def compile_src(creator_name: str,
                src_dir: str,
                build_dir: str,
                mods_dir: str,
                mod_name: str = "Untitled") -> None:
    """
    Packages your mod into a proper mod file. It creates 2 mod files, a full mod file which contains all the files
    in the source folder unchanged along with the compiled python versions next to uncompiled ones and a slim mod-file
    which contains only the compiled versions.

    Modified from andrew's code.
    https://sims4studio.com/thread/15145/started-python-scripting

    :param creator_name: The creators name
    :param src_dir: Source dir for the mod files
    :param build_dir: Place to put the mod files
    :param mods_dir: Place to an extra copy of the slim mod file for testing
    :param mod_name: Name to call the mod
    :return: Nothing
    """

    # Prepend creator name to mod name
    mod_name = creator_name + '_' + mod_name
    mods_sub_dir = os.path.join(mods_dir, mod_name)

    # Create ts4script paths
    ts4script_full_build_path = os.path.join(build_dir,
                                             mod_name + '.ts4script')
    ts4script_mod_path = os.path.join(mods_sub_dir, mod_name + '.ts4script')

    print("Clearing out old builds...")

    # Delete and re-create build and sub-folder in Mods
    is_devmode = symlink_exists_win("", mods_dir, mod_name)
    symlink_remove_win("", mods_dir, mod_name)

    if is_devmode:
        print("Exiting Dev Mode...")

    remove_dir(build_dir)

    ensure_path_created(build_dir)
    ensure_path_created(mods_sub_dir)

    print("Re-building mod...")

    # Compile the mod
    zf = PyZipFile(ts4script_full_build_path,
                   mode='w',
                   compression=ZIP_STORED,
                   allowZip64=True,
                   optimize=2)
    compile_full(src_dir, zf)
    zf.close()

    # Copy it over to the mods folder
    shutil.copyfile(ts4script_full_build_path, ts4script_mod_path)

    print("----------")
    print("Complete")
def debug_install_mod(mod_src: str, mods_dir: str, mod_name: str,
                      mod_folder_name: str) -> None:
    """
    Compiles and installs the mod which adds a cheat code so the user can setup the debugger in-game

    :param mod_src: Path to the script which does this
    :param mods_dir: Path to the users mod folder
    :param mod_name: Name of the mod
    :param mod_folder_name: Name of mod Subfolder
    :return: Nothing
    """

    print("Compiling and installing the cheatcode mod...")

    # Get destination file path
    mods_sub_dir = os.path.join(mods_dir, mod_folder_name)
    mod_path = os.path.join(mods_sub_dir, mod_name + '.ts4script')

    ensure_path_created(mods_sub_dir)

    # Get compiled path and compile mod
    mod_src_pyc = replace_extension(mod_src, "pyc")
    py_compile.compile(mod_src, mod_src_pyc)

    # Create mod at destination and add compiled file to it
    zf = PyZipFile(mod_path,
                   mode='w',
                   compression=ZIP_STORED,
                   allowZip64=True,
                   optimize=2)
    zf.write(mod_src_pyc, mod_name + ".pyc")
    zf.close()
Beispiel #3
0
def symlink_create_win(creator_name: str,
                       src_dir: str,
                       mods_dir: str,
                       mod_name: str = "Untitled") -> None:
    """
    Creates a symlink, it first wipes out the mod that may be there. When entering devmode, you don't compile anymore,
    so any compiled code needs to be removed.

    :param creator_name: Creator Name
    :param src_dir: Path to the source folder in this project
    :param mods_dir: Path to the Mods Folder
    :param mod_name: Name of Mod
    :return: Nothing
    """

    # Build paths
    scripts_path = get_scripts_path(creator_name, mods_dir, mod_name)
    mod_folder_path = str(Path(scripts_path).parent)

    # Safely remove folder with symlink
    symlink_remove_win(creator_name, mods_dir, mod_name)

    # Re-create folder
    ensure_path_created(mod_folder_path)

    # Create Scripts Folder as a Directory Junction
    exec_cmd("mklink", '/J ' + '"' + scripts_path + '" ' '"' + src_dir + '"')

    print("")
    print(
        "Dev Mode is activated, you no longer have to compile after each change, run devmode.reload [path.of.module]"
    )
    print(
        "to reload individual files while the game is running. To exit dev mode, simply run 'compile.py' which will"
    )
    print("return things to normal.")
    print(
        "It's recomended to test a compiled version before final release after working in Dev Mode"
    )
    print("")
Beispiel #4
0
from Utility.helpers_decompile import decompile_pre, decompile_zips, decompile_print_totals
from Utility.helpers_path import ensure_path_created, remove_dir
from settings import gameplay_folder_data, gameplay_folder_game, projects_python_path

if os.path.exists(projects_python_path):
    print("This will wipe out the old decompilation at: " +
          projects_python_path)
    answer = input("Are you sure you want to do this? [y/n]: ")
    if answer is not "y":
        sys.exit("Program aborted by user")

print("Emptying prior decompilation...")
remove_dir(projects_python_path)

# Make sure the python folder exists
ensure_path_created(projects_python_path)

# Do a pre-setup
decompile_pre()

# Decompile all zips to the python projects folder
print("")
print("Beginning decompilation")
print("THIS WILL SERIOUSLY TAKE A VERY LONG TIME!!! " +
      "Additionally many files will not decompile properly which is normal.")
print("")

decompile_zips(gameplay_folder_data, projects_python_path)
decompile_zips(gameplay_folder_game, projects_python_path)

# Print final statistics
def decompile_dir(src_dir: str, dest_dir: str, filename: str) -> None:
    """
    Decompiles a directory of compiled python files to a different directory
    Modified from andrew's code.
    https://sims4studio.com/thread/15145/started-python-scripting

    :param src_dir: Path of dir to decompile
    :param dest_dir: Path of dir to send decompiled files to
    :param filename: Original filename of what's being decompiled (For progress output purposes)
    :return: Nothing
    """

    # Gain R/W to global counts and timing
    global total_suc_count
    global total_fail_count
    global total_count
    global total_minutes

    # Begin clock
    time_start = get_time()

    print("Decompiling " + filename)

    # Local counts for this one task
    col_count = 0
    suc_count = 0
    fail_count = 0
    count = 0

    # Go through each compiled python file in the folder
    for root, dirs, files in os.walk(src_dir):
        for filename in fnmatch.filter(files, python_compiled_ext):

            # Get details about the source file
            src_file_path = str(os.path.join(root, filename))
            src_file_rel_path = get_rel_path(src_file_path, src_dir)

            # Create destination file path
            dest_file_path = replace_extension(dest_dir + os.path.sep + src_file_rel_path, "py")

            # And ensures the folders exist so there's no error
            # Make sure to strip off the file name at the end
            ensure_path_created(str(Path(dest_file_path).parent))

            # Decompile it to destination
            success = exec_package("uncompyle6",
                                   "-o " + '"' + dest_file_path + '"' + " " +
                                   '"' + src_file_path + '"')

            # Print progress
            # Prints a single dot on the same line which gives a nice clean progress report
            # Tally number of files and successful / failed files
            if success:
                print(".", end="")
                suc_count += 1
                total_suc_count += 1
            else:
                print("x", end="")
                fail_count += 1
                total_fail_count += 1

            count += 1
            total_count += 1

            # Insert a new progress line every 80 characters
            col_count += 1
            if col_count >= 80:
                col_count = 0
                print("")

    time_end = get_time()
    elapsed_minutes = get_minutes(time_end, time_start)
    total_minutes += elapsed_minutes

    # Print a newline and then a compact completion message giving successful, failed, and total count stats and timing
    print("")
    print("")
    print("Completed")
    print("S: " + str(suc_count) + " [" + str(round((suc_count/count) * 100, 2)) + "%], ", end="")
    print("F: " + str(fail_count) + " [" + str(round((fail_count/count) * 100, 2)) + "%], ", end="")
    print("T: " + str(count) + ", ", end="")
    print(get_time_str(elapsed_minutes))
    print("")
Beispiel #6
0
#    distributed under the License is distributed on an "AS IS" BASIS,
#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#    See the License for the specific language governing permissions and
#    limitations under the License.

# This installs, updates, and removes package files in the projects folder to match the assets folder
import fnmatch

import os
import shutil
from settings import assets_path, mods_folder, creator_name, project_name, build_path
from Utility.helpers_path import ensure_path_created, remove_file

mod_name_folder_path = mods_folder + os.sep + creator_name + "_" + project_name

ensure_path_created(mod_name_folder_path)
file_list_failed = []


def remove_tl_packages(path: str) -> int:
    count = 0

    # Remove existing package files
    for root, dirs, files in os.walk(path):
        for filename in fnmatch.filter(files, "*.package"):
            remove_file(root + os.sep + filename)
            count += 1

        # Only cover the top-level folder
        break
    return count
def debug_install_egg(egg_path: str, mods_dir, dest_name: str,
                      mod_folder_name: str) -> None:
    """
    Copies the debug egg provided by Pycharm Pro which adds the capability to make debugging happen inside of
    PyCharm Pro. A bit of work goes into this so it'll be much slower.

    :param egg_path: Path to the debug egg
    :param mods_dir: Path to the mods folder
    :param dest_name: Name of the mod
    :param mod_folder_name: Name of mod Subfolder
    :return:
    """

    print("Re-packaging and installing the debugging capability mod...")
    # Get egg filename and path
    filename = Path(egg_path).name
    mods_sub_dir = os.path.join(mods_dir, mod_folder_name)
    mod_path = os.path.join(mods_sub_dir, dest_name + ".ts4script")

    ensure_path_created(mods_sub_dir)

    # Get python ctypes folder
    sys_ctypes_folder = os.path.join(get_sys_folder(), "Lib", "ctypes")

    # Create temp directory
    tmp_dir = tempfile.TemporaryDirectory()
    tmp_egg = tmp_dir.name + os.sep + filename

    # Remove old mod in mods folder there, if it exists
    remove_file(mod_path)

    # Copy egg to temp path
    shutil.copyfile(egg_path, tmp_egg)

    # Extract egg
    # This step is a bit redundant but I need to copy over everything but one folder into the zip file and I don't
    # know how to do that in python so I copy over the zip, extract it, copy in the whole folder, delete the one
    # sub-folder, then re-zip everything up. It's a pain but it's what I know hwo to do now and Google's not much help
    zip = PyZipFile(tmp_egg)
    zip.extractall(tmp_dir.name)
    zip.close()

    # Remove archive
    remove_file(tmp_egg)

    # Copy ctype folder to extracted archive
    shutil.copytree(sys_ctypes_folder, tmp_dir.name + os.sep + "ctypes")

    # Remove that one folder
    remove_dir(tmp_dir.name + os.sep + "ctypes" + os.sep + "__pycache__")

    # Grab a handle on the egg
    zf = PyZipFile(mod_path,
                   mode='w',
                   compression=ZIP_STORED,
                   allowZip64=True,
                   optimize=2)

    # Add all the files in the tmp directory to the zip file
    for folder, subs, files in os.walk(tmp_dir.name):
        for file in files:
            archive_path = get_rel_path(folder + os.sep + file, tmp_dir.name)
            zf.write(folder + os.sep + file, archive_path)

    zf.close()

    # There's a temporary directory bug that causes auto-cleanup to sometimes fail
    # We're preventing crash messages from flooding the screen to keep things tidy
    try:
        tmp_dir.cleanup()
    except:
        pass