CREATE and FUCK your own AI GIRLFRIEND TRY FOR FREE
x

Tool Ren'Py UnRen for MacOS and Linux v0.8.2

5.00 star(s) 1 Vote

dikau

Member
Dec 16, 2019
315
282
Totally my bad, I just misread your post. For some reason I read "shipped with the game" as "shipped with MacOS." No idea why. I'm just dumb like that sometimes.

However, I have discovered something strange. Where you save a game matters a lot more than I previously thought. When I ran Unren on u4ia today I got this error
Python:
 > Could not find platform independent libraries <prefix>
  > Python path configuration:
  >   PYTHONHOME = (not set)
  >   PYTHONPATH = (not set)
  >   program name = '/Users/xxxxxxx/Games/u4ia.app/Contents/MacOS/python'
  >   isolated = 0
  >   environment = 1
  >   user site = 1
  >   import site = 1
  >   sys._base_executable = '/Users/xxxxxxx/Games/u4ia.app/Contents/MacOS/python'
  >   sys.base_prefix = '/home/tom/ab/renpy-build/tmp/install.mac-arm64'
  >   sys.base_exec_prefix = '/home/tom/ab/renpy-build/tmp/install.mac-arm64'
  >   sys.platlibdir = 'lib'
  >   sys.executable = '/Users/xxxxxxx/Games/u4ia.app/Contents/MacOS/python'
  >   sys.prefix = '/home/tom/ab/renpy-build/tmp/install.mac-arm64'
  >   sys.exec_prefix = '/home/tom/ab/renpy-build/tmp/install.mac-arm64'
  >   sys.path = [
  >     '/home/tom/ab/renpy-build/tmp/install.mac-arm64/lib/python39.zip',
  >     '/home/tom/ab/renpy-build/tmp/install.mac-arm64/lib/python3.9',
  >     '/home/tom/ab/renpy-build/tmp/install.mac-arm64/lib/lib-dynload',
  >   ]
  > Fatal Python error: init_fs_encoding: failed to get the Python codec of the filesystem encoding
  > Python runtime state: core initialized
  > ModuleNotFoundError: No module named 'encodings'
  >
  > Current thread 0x00000001f4275e00 (most recent call first):
  > <no Python frame>
However, based on some discussion in this thread for 0x52's Universal Renpy Mod, I tried moving the game to my desktop and then trying Unren. It worked like a charm. Weird.
Yes, that can happen due to macOS . Basically, it checks if downloaded apps are from trusted sources to keep us safe from potential sketchy software. Some folders are considered "pretty safe" for running apps.

If you disable that restriction, you can run any apps in any folder, which includes running Renpy's own Python in this case. Removing GateKeeper for single app is pretty trivial, and it's handled in the UnRen script using xattr

So if you're facing this issue, it might be because you don't have the privilege to "trick" GateKeeper i.e. you need sudo, or there's another protective app running, or you've tweaked the script (?).
 
Last edited:

toobad0912

Newbie
May 1, 2021
80
30
Yes, that can happen due to macOS . Basically, it checks if downloaded apps are from trusted sources to keep us safe from potential sketchy software. Some folders are considered "pretty safe" for running apps.

If you disable that restriction, you can run any apps in any folder, which includes running Renpy's own Python in this case. Removing GateKeeper for single app is pretty trivial, and it's handled in the UnRen script using xattr

So if you're facing this issue, it might be because you don't have the privilege to "trick" GateKeeper i.e. you need sudo, or there's another protective app running, or you've tweaked the script (?).
Definitely haven't tweaked the script. Wouldn't even know how to if I wanted to lol. I just moved my games a few levels up in the hierarchy and things seem to be working fine now! Thanks though.
 

Welwigo

Newbie
Apr 12, 2021
77
114
I've encountered quite the bizarre error. Using Mint 20.1, so it's the Ubuntu Focal 20.04 base. Either using an alias or just using good old python2.7 -m ./UnRen.command runs the tools... But then it gives me the error of
File "./UnRen.command", line 61
quicksavekey="K_F5"
^
IndentationError: unexpected indent


And if I fix that, then it gives me the error of
File "./UnRen.command", line 69
ARG1=$1
^
SyntaxError: invalid syntax


It seems something's have a terribly uncouth interaction. Either that or it just fuckin' hates my Python 2.7 install.
 

andy2020

Newbie
Aug 24, 2019
15
6
I'm getting errors.

UnRen for Mac and Linux v0.8.2
by goobdoob @ www.f95zone.to
based on the original version by jimmy5 @ www.f95zone.to
and UnRen.bat by sam @ www.f95zone.to
Python is installed, detected
Error - cannot find rpatool or unrpyc in UnRen v0.8.2 Tools. Exiting

The files rpatool and unrpyc.py are both in the tools folder.

I even copied them into the directory that I run the UnRen.command from - same error.

I commented out the check for those files though so that I could enable console, but then when I attempt to run the game:

./Princess\ Trainer\ GE.sh
The label stress_up is defined twice, at
File "15_messages.rpyc", line 49 and
File "15_messages.rpyc", line 164.
 
Last edited:

dikau

Member
Dec 16, 2019
315
282
I've read the entire thread and now I'm a little confused: is this the last version?
does it support python 3?
It's another version of UnRen from this thread. It uses the Python that comes with Ren'Py games, rather than your system-installed Python. Yes, it supports Ren'Py 8 with its Python 3.
 

kaxfool

Newbie
May 24, 2017
15
14
I had trouble getting UnRen working. (Ubuntu 20.04) Same issue as few others here:
Code:
$ ./UnRen.command
UnRen for Mac and Linux v0.8.2
by goobdoob @ www.f95zone.to
based on the original version by jimmy5 @ www.f95zone.to
and UnRen.bat by sam @ www.f95zone.to
Python is installed, detected
Error - cannot find rpatool or unrpyc in UnRen v0.8.2 Tools. Exiting
However, my fix differs a bit from the previous ones so worth commenting.
I had both Python2 and Python3 installed. However, it seems the script failed because there was no python version detected with command "python".
So I installed "python-is-python3" package. Now the script can detect something at "python" and decide it's unsupported, and goes for "python2" which works. I suppose alias python=python2 would also work. But this is what I did because it's the quickest way to make the script work permanently.
Fucking finally. Thanks for this. Had the same issue as you but UnRen.command works now. New error though:

ValueError: unsupported pickle protocol: 5

Edit: Fixed by downloading and replacing rpatool with the rpatool.py from https://f95zone.to/threads/rpa-extractor-for-mac.175350/.

Renamed rpatool.py to rpatool before replacing. Still didnt work by running ./UnRen.command so ran the following directly:

python rpatool -x /PATH/game/archive.rpa

and it worked like a charm (y)
 
Last edited:

BadmanBaxter

Well-Known Member
May 28, 2019
1,380
551
View attachment 141160

UnRen is a script that makes it easy to do interesting things to Ren'Py games. This version, for MacOS and Linux, is based off of by @Sam . UnRen.command is a bash shell script that calls python commands to do some of the work.

The original version was created by @jimmy5 , but he's passed ownership to me.

The current version is 0.8.2, available here.

Features:
  • Extract RPA packages, using .
  • Decompile rpyc files, using .
  • Enable Console and Developer Menu
  • Enable Quick Save and Quick Load
  • Force enable skipping of unseen content
  • Force enable rollback (scroll wheel)
  • Open game directory

Installation:

Download and unzip the script. Place it where you like. There are 2 ways to run it:
  1. Double click on UnRen.command. This will open a Terminal window that will allow you to drag (or type the path to) the app you want to work on.
  2. Run UnRen.command from the command line. You can provide the path to the app on the command line, or type it in when UnRen asks for it.
  3. UnRen runs in bash. Other shells may or may not work. Do NOT run UnRen with "sh UnRen.command" if your sh is not bash. Instead, if you run it from the command line, run it with a full path, or "./UnRen.command".

Known Issues:
  • None
You don't have permission to view the spoiler content. Log in or register now.
You don't have permission to view the spoiler content. Log in or register now.
You don't have permission to view the spoiler content. Log in or register now.
You don't have permission to view the spoiler content. Log in or register now.
You don't have permission to view the spoiler content. Log in or register now.
Trying to use this on my chromebook on linux so that I can access the renpy console in pokemon academy life. I have downloaded and unzipped and put it in the game folder of pokemon academy life however aside from double clicking to open the command part I understand none of what I have to do or type in to get it to work anyone here know what I should do?
 

SteelyDan14

Formerly Known as GeekBone
Modder
Donor
Jan 13, 2018
1,342
5,814
Trying to use this on my chromebook on linux so that I can access the renpy console in pokemon academy life. I have downloaded and unzipped and put it in the game folder of pokemon academy life however aside from double clicking to open the command part I understand none of what I have to do or type in to get it to work anyone here know what I should do?
It works differently from the win version and does not have to be in the same directory. Just run it in a terminal and then drag the folder with the game into the terminal, hit enter and your menu will appear.
 

SteelyDan14

Formerly Known as GeekBone
Modder
Donor
Jan 13, 2018
1,342
5,814
Trying to use this on my chromebook on linux so that I can access the renpy console in pokemon academy life. I have downloaded and unzipped and put it in the game folder of pokemon academy life however aside from double clicking to open the command part I understand none of what I have to do or type in to get it to work anyone here know what I should do?
The other option is to just unlock the console yourself. Open the RENPY folder and then the COMMON folder and look for a file called 00console.rpy. Open it with a text editor and look for this line:
Code:
# Configuration and style initalization.
init -1500 python:

    # If true, the console is enabled despite config.developer being False.
    config.console = False
Change False to True, save and reload your game.
 

Mr_SmithXoXo

Member
Nov 11, 2020
130
190
Fucking finally. Thanks for this. Had the same issue as you but UnRen.command works now. New error though:

ValueError: unsupported pickle protocol: 5

Edit: Fixed by downloading and replacing rpatool with the rpatool.py from https://f95zone.to/threads/rpa-extractor-for-mac.175350/.

Renamed rpatool.py to rpatool before replacing. Still didnt work by running ./UnRen.command so ran the following directly:

python rpatool -x /PATH/game/archive.rpa

and it worked like a charm (y)
Can you give me a detailed step-by-step guide? Nothing seems to work for me.
 

Saeious

New Member
Apr 22, 2024
7
12
Fucking finally. Thanks for this. Had the same issue as you but UnRen.command works now. New error though:

ValueError: unsupported pickle protocol: 5

Edit: Fixed by downloading and replacing rpatool with the rpatool.py from https://f95zone.to/threads/rpa-extractor-for-mac.175350/.

Renamed rpatool.py to rpatool before replacing. Still didnt work by running ./UnRen.command so ran the following directly:

python rpatool -x /PATH/game/archive.rpa

and it worked like a charm (y)
This had what I needed in it, thanks!
 
  • Like
Reactions: kaxfool

harem.king

Engaged Member
Aug 16, 2023
3,372
5,731
I'm getting errors.

UnRen for Mac and Linux v0.8.2
by goobdoob @ www.f95zone.to
based on the original version by jimmy5 @ www.f95zone.to
and UnRen.bat by sam @ www.f95zone.to
Python is installed, detected
Error - cannot find rpatool or unrpyc in UnRen v0.8.2 Tools. Exiting

The files rpatool and unrpyc.py are both in the tools folder.

I even copied them into the directory that I run the UnRen.command from - same error.

I commented out the check for those files though so that I could enable console, but then when I attempt to run the game:

./Princess\ Trainer\ GE.sh
The label stress_up is defined twice, at
File "15_messages.rpyc", line 49 and
File "15_messages.rpyc", line 164.
Dev is assuming that linux and mac use the same syntax.
the zip file is full of DSStore files that show that the dev is using a mac.
there are various issues in the script file that make it incompatible with linux.

for example
Code:
rpatool_raw=`dirname "$0"`"/UnRen Tools/rpatool"
unrpyc_raw=`dirname "$0"`"/UnRen Tools/unrpyc.py"
in linux using the current folder's path is denoted with ./
in mac it is denoted with /
which is why it cannot find those assorted files.

also the first line
Code:
#! /bin/bash
need to remove the space between the ! and / for linux.

I was going to try and fix it myself but then I found someone already made a working linux version.
here
https://f95zone.to/threads/unren-ba...ole-developer-menu-enabler.3083/post-13551798
 
Last edited:

MaxRichard

Member
Oct 7, 2023
326
781
For others who might find this useful in the future, I've updated the rpatool file to run with python3 instead of python2 (thanks, ChatGPT!). Terribly untested, provided as is with no warranty. If it breaks your new gaming rig in half, it's your fault, not mine!

Python:
#!/usr/bin/env python3

import sys
import os
import codecs
import pickle
import errno
import random

# def _unmangle(data):
#     return data.encode('latin1')
if sys.version_info[0] >= 3:
    def _unmangle(data):
        return data
elif sys.version_info[0] == 2:
    def _unmangle(data):
        return data.encode('latin1')

def _unpickle(data):
    # Specify latin1 encoding to prevent raw byte values from causing an ASCII decode error.
    return pickle.loads(data, encoding='latin1')

class RenPyArchive:
    file = None
    handle = None

    files = {}
    indexes = {}

    version = None
    padlength = 0
    key = None
    verbose = False

    RPA2_MAGIC = 'RPA-2.0 '
    RPA3_MAGIC = 'RPA-3.0 '
    RPA3_2_MAGIC = 'RPA-3.2 '

    # For backward compatibility, otherwise Python3-packed archives won't be read by Python2
    PICKLE_PROTOCOL = 2

    def __init__(self, file=None, version=3, padlength=0, key=0xDEADBEEF, verbose=False):
        self.padlength = padlength
        self.key = key
        self.verbose = verbose

        if file is not None:
            self.load(file)
        else:
            self.version = version

    def __del__(self):
        if self.handle is not None:
            self.handle.close()

    # Determine archive version.
    def get_version(self):
        self.handle.seek(0)
        magic = self.handle.readline().decode('utf-8')

        if magic.startswith(self.RPA3_2_MAGIC):
            return 3.2
        elif magic.startswith(self.RPA3_MAGIC):
            return 3
        elif magic.startswith(self.RPA2_MAGIC):
            return 2
        elif self.file.endswith('.rpi'):
            return 1

        raise ValueError('the given file is not a valid Ren\'Py archive, or an unsupported version')

    # Extract file indexes from opened archive.
    def extract_indexes(self):
        self.handle.seek(0)
        indexes = None

        if self.version in [2, 3, 3.2]:
            # Fetch metadata.
            metadata = self.handle.readline()
            vals = metadata.split()
            offset = int(vals[1], 16)
            if self.version == 3:
                self.key = 0
                for subkey in vals[2:]:
                    self.key ^= int(subkey, 16)
            elif self.version == 3.2:
                self.key = 0
                for subkey in vals[3:]:
                    self.key ^= int(subkey, 16)

            # Load in indexes.
            self.handle.seek(offset)
            contents = codecs.decode(self.handle.read(), 'zlib')
            indexes = _unpickle(contents)

            # Deobfuscate indexes.
            if self.version in [3, 3.2]:
                obfuscated_indexes = indexes
                indexes = {}
                for i in obfuscated_indexes.keys():
                    if len(obfuscated_indexes[i][0]) == 2:
                        indexes[i] = [ (offset ^ self.key, length ^ self.key) for offset, length in obfuscated_indexes[i] ]
                    else:
                        indexes[i] = [ (offset ^ self.key, length ^ self.key, prefix) for offset, length, prefix in obfuscated_indexes[i] ]
        else:
            indexes = pickle.loads(codecs.decode(self.handle.read(), 'zlib'))

        return indexes

    # Generate pseudorandom padding (for whatever reason).
    def generate_padding(self):
        length = random.randint(1, self.padlength)
        padding = bytearray()
        while length > 0:
            padding.append(random.randint(1, 255))
            length -= 1
        return bytes(padding)

    # Converts a filename to archive format.
    def convert_filename(self, filename):
        (drive, filename) = os.path.splitdrive(os.path.normpath(filename).replace(os.sep, '/'))
        return filename

    # Debug (verbose) messages.
    def verbose_print(self, message):
        if self.verbose:
            print(message)

    # List files in archive and current internal storage.
    def list(self):
        return list(self.indexes.keys()) + list(self.files.keys())

    # Check if a file exists in the archive.
    def has_file(self, filename):
        return filename in self.indexes.keys() or filename in self.files.keys()

    # Read file from archive or internal storage.
    def read(self, filename):
        filename = self.convert_filename(filename)

        # Check if the file exists in our indexes.
        if filename not in self.files and filename not in self.indexes:
            raise IOError(errno.ENOENT, 'the requested file {0} does not exist in the given Ren\'Py archive'.format(
                filename))

        # If it's in our opened archive index, and our archive handle isn't valid, something is obviously wrong.
        if filename not in self.files and filename in self.indexes and self.handle is None:
            raise IOError(errno.ENOENT, 'the requested file {0} does not exist in the given Ren\'Py archive'.format(
                filename))

        # Check our simplified internal indexes first, in case someone wants to read a file they added before without saving, for some unholy reason.
        if filename in self.files:
            self.verbose_print('Reading file {0} from internal storage...'.format(filename))
            return self.files[filename]
        # We need to read the file from our open archive.
        else:
            # Read offset and length, seek to the offset and read the file contents.
            if len(self.indexes[filename][0]) == 3:
                (offset, length, prefix) = self.indexes[filename][0]
            else:
                (offset, length) = self.indexes[filename][0]
                prefix = ''

            self.verbose_print('Reading file {0} from data file {1}... (offset = {2}, length = {3} bytes)'.format(
                filename, self.file, offset, length))
            self.handle.seek(offset)
            return _unmangle(prefix) + self.handle.read(length - len(prefix))

    # Modify a file in archive or internal storage.
    def change(self, filename, contents):
        # Our 'change' is basically removing the file from our indexes first, and then re-adding it.
        self.remove(filename)
        self.add(filename, contents)

    # Add a file to the internal storage.
    def add(self, filename, contents):
        filename = self.convert_filename(filename)
        if filename in self.files or filename in self.indexes:
            raise ValueError('file {0} already exists in archive'.format(filename))

        self.verbose_print('Adding file {0} to archive... (length = {1} bytes)'.format(
            filename, len(contents)))
        self.files[filename] = contents

    # Remove a file from archive or internal storage.
    def remove(self, filename):
        if filename in self.files:
            self.verbose_print('Removing file {0} from internal storage...'.format(filename))
            del self.files[filename]
        elif filename in self.indexes:
            self.verbose_print('Removing file {0} from archive indexes...'.format(filename))
            del self.indexes[filename]
        else:
            raise IOError(errno.ENOENT, 'the requested file {0} does not exist in this archive'.format(filename))

    # Load archive.
    def load(self, filename):
        if self.handle is not None:
            self.handle.close()
        self.file = filename
        self.files = {}
        self.handle = open(self.file, 'rb')
        self.version = self.get_version()
        self.indexes = self.extract_indexes()

    # Save current state into a new file, merging archive and internal storage, rebuilding indexes, and optionally saving in another format version.
    def save(self, filename=None):
        if filename is None:
            filename = self.file
        if filename is None:
            raise ValueError('no target file found for saving archive')
        if self.version != 2 and self.version != 3:
            raise ValueError('saving is only supported for version 2 and 3 archives')

        self.verbose_print('Rebuilding archive index...')
        # Fill our own files structure with the files added or changed in this session.
        files = self.files.copy()
        # First, read files from the current archive into our files structure.
        for file in list(self.indexes.keys()):
            content = self.read(file)
            # Remove from indexes array once read, add to our own array.
            del self.indexes[file]
            files[file] = content

        # Predict header length, we'll write that one last.
        if self.version == 3:
            offset = 34
        elif self.version == 2:
            offset = 25
        archive = open(filename, 'wb')
        archive.seek(offset)

        # Build our own indexes while writing files to the archive.
        indexes = {}
        self.verbose_print('Writing files to archive file...')
        for file, content in files.items():
            # Generate random padding, for whatever reason.
            if self.padlength > 0:
                padding = self.generate_padding()
                archive.write(padding)
                offset += len(padding)

            archive.write(content)
            # Update index.
            if self.version == 3:
                indexes[file] = [ (offset ^ self.key, len(content) ^ self.key) ]
            elif self.version == 2:
                indexes[file] = [ (offset, len(content)) ]
            offset += len(content)

        # Write the indexes.
        self.verbose_print('Writing archive index to archive file...')
        archive.write(codecs.encode(pickle.dumps(indexes, self.PICKLE_PROTOCOL), 'zlib'))
        # Now write the header.
        self.verbose_print('Writing header to archive file... (version = RPAv{0})'.format(self.version))
        archive.seek(0)
        if self.version == 3:
            archive.write('{}{:016x} {:08x}\n'.format(self.RPA3_MAGIC, offset, self.key).encode('utf-8'))
        else:
            archive.write('{}{:016x}\n'.format(self.RPA2_MAGIC, offset).encode('utf-8'))
        # We're done, close it.
        archive.close()

        # Reload the file in our inner database.
        self.load(filename)

if __name__ == "__main__":
    import argparse

    parser = argparse.ArgumentParser(
        description='A tool for working with Ren\'Py archive files.',
        epilog='The FILE argument can optionally be in ARCHIVE=REAL format, mapping a file in the archive file system to a file on your real file system. An example of this: rpatool -x test.rpa script.rpyc=/home/foo/test.rpyc',
        add_help=False)

    parser.add_argument('archive', metavar='ARCHIVE', help='The Ren\'py archive file to operate on.')
    parser.add_argument('files', metavar='FILE', nargs='*', help='Zero or more files to operate on.')

    parser.add_argument('-l', '--list', action='store_true', help='List files in archive ARCHIVE.')
    parser.add_argument('-x', '--extract', action='store_true', help='Extract FILEs from ARCHIVE.')
    parser.add_argument('-c', '--create', action='store_true', help='Create ARCHIVE from FILEs.')
    parser.add_argument('-d', '--delete', action='store_true', help='Delete FILEs from ARCHIVE.')
    parser.add_argument('-a', '--append', action='store_true', help='Append FILEs to ARCHIVE.')

    parser.add_argument('-2', '--two', action='store_true', help='Use the RPAv2 format for creating/appending to archives.')
    parser.add_argument('-3', '--three', action='store_true', help='Use the RPAv3 format for creating/appending to archives (default).')

    parser.add_argument('-k', '--key', metavar='KEY', help='The obfuscation key used for creating RPAv3 archives, in hexadecimal (default: 0xDEADBEEF).')
    parser.add_argument('-p', '--padding', metavar='COUNT', help='The maximum number of bytes of padding to add between files (default: 0).')
    parser.add_argument('-o', '--outfile', help='An alternative output archive file when appending to or deleting from archives, or output directory when extracting.')

    parser.add_argument('-h', '--help', action='help', help='Print this help and exit.')
    parser.add_argument('-v', '--verbose', action='store_true', help='Be a bit more verbose while performing operations.')
    parser.add_argument('-V', '--version', action='version', version='rpatool v0.8', help='Show version information.')
    arguments = parser.parse_args()

    # Determine RPA version.
    if arguments.two:
        version = 2
    else:
        version = 3

    # Determine RPAv3 key.
    if 'key' in arguments and arguments.key is not None:
        key = int(arguments.key, 16)
    else:
        key = 0xDEADBEEF

    # Determine padding bytes.
    if 'padding' in arguments and arguments.padding is not None:
        padding = int(arguments.padding)
    else:
        padding = 0

    # Determine output file/directory and input archive
    if arguments.create:
        archive_file = None
        output = arguments.archive
    else:
        archive_file = arguments.archive
        if arguments.outfile is not None:
            output = arguments.outfile
        else:
            # Default output directory for extraction is the current directory.
            if arguments.extract:
                output = '.'
            else:
                output = arguments.archive

    try:
        archive = RenPyArchive(archive_file, padlength=padding, key=key, version=version, verbose=arguments.verbose)
    except IOError as e:
        print('Could not open archive file {0} for reading: {1}'.format(archive_file, e), file=sys.stderr)
        sys.exit(1)

    if arguments.create or arguments.append:
        # We need this separate function to recursively process directories.
        def add_file(filename):
            # If the archive path differs from the actual file path, as given in the argument,
            # extract the archive path and actual file path.
            if '=' in filename:
                outfile, filename = filename.split('=', 1)
            else:
                outfile = filename

            if os.path.isdir(filename):
                for file in os.listdir(filename):
                    # We need to do this in order to maintain a possible ARCHIVE=REAL mapping between directories.
                    add_file(outfile + '/' + file + '=' + filename + '/' + file)
            else:
                try:
                    with open(filename, 'rb') as file_obj:
                        archive.add(outfile, file_obj.read())
                except Exception as e:
                    print('Could not add file {0} to archive: {1}'.format(filename, e), file=sys.stderr)

        # Iterate over the given files to add to archive.
        for filename in arguments.files:
            add_file(filename)

        # Set version for saving, and save.
        archive.version = version
        try:
            archive.save(output)
        except Exception as e:
            print('Could not save archive file: {0}'.format(e), file=sys.stderr)
    elif arguments.delete:
        # Iterate over the given files to delete from the archive.
        for filename in arguments.files:
            try:
                archive.remove(filename)
            except Exception as e:
                print('Could not delete file {0} from archive: {1}'.format(filename, e), file=sys.stderr)

        # Set version for saving, and save.
        archive.version = version
        try:
            archive.save(output)
        except Exception as e:
            print('Could not save archive file: {0}'.format(e), file=sys.stderr)
    elif arguments.extract:
        # Either extract the given files, or all files if no files are given.
        if len(arguments.files) > 0:
            files_to_extract = arguments.files
        else:
            files_to_extract = archive.list()

        # Create output directory if not present.
        if not os.path.exists(output):
            os.makedirs(output)

        # Iterate over files to extract.
        for filename in files_to_extract:
            if '=' in filename:
                outfile, filename = filename.split('=', 1)
            else:
                outfile = filename

            try:
                contents = archive.read(filename)

                # Create output directory for file if not present.
                full_output_path = os.path.join(output, outfile)
                os.makedirs(os.path.dirname(full_output_path), exist_ok=True)

                with open(full_output_path, 'wb') as file_obj:
                    file_obj.write(contents)
            except Exception as e:
                print('Could not extract file {0} from archive: {1}'.format(filename, e), file=sys.stderr)
    elif arguments.list:
        # Print the sorted file list.
        file_list = archive.list()
        file_list.sort()
        for file in file_list:
            print(file)
    else:
        print('No operation given :(')
        print('Use {0} --help for usage details.'.format(sys.argv[0]))
 
  • Like
Reactions: syh653

Madeddy

Active Member
Dec 17, 2017
831
483
Its beyond me what the goal for this is, but if you have fun with it - fair enough. Anyway, i feel the need to clear some misunderstandings with this, so users don't run full speed in a wall and wonder then what happened:
I've updated the rpatool file to run with python3 instead of python2
  1. This statement is nonsense.
    A user invokes a interpreter in a certain version and the code(app) to be run must then be able to be executed in the chosen version.
  2. rpatool got 2017 Python 3 support can be run in both versions since.
Greets

p.s.: God and devils! I sound like the smart-ass of the week. :eek:
 
  • Like
Reactions: RoughlySpecific

MaxRichard

Member
Oct 7, 2023
326
781
Its beyond me what the goal for this is, but if you have fun with it - fair enough. Anyway, i feel the need to clear some misunderstandings with this, so users don't run full speed in a wall and wonder then what happened:

  1. This statement is nonsense.
    A user invokes a interpreter in a certain version and the code(app) to be run must then be able to be executed in the chosen version.
  2. rpatool got 2017 Python 3 support can be run in both versions since.
Greets

p.s.: God and devils! I sound like the smart-ass of the week. :eek:
Sorry, was in a hurry and not precise with words. I updated the rpatool file to work as expected with python3 instead of python2, since it previously did not work correctly for me. I was having issues both with `.encoding` calls and with Pickle unloading an unknown format (5, IIRC?).

It's been over a decade since I've had to port Python 2 code to Python 3 so I just had ChatGPT do it for me and only hand modified a couple things myself.
 
  • Like
Reactions: RoughlySpecific

Madeddy

Active Member
Dec 17, 2017
831
483
... work as expected with python3 ... since it previously did not work correctly for me. I was having issues both with `.encoding` calls and with Pickle unloading an unknown format (5, IIRC?).
Yeah, happens. The reason for this lies not with Ren'Py but with the respective games/devs because the did some manipulations or simple fuck ups.

Info for the curious:
- Current Ren'Py versions up to the last ones (v7.8 and v8.3) restrict the pickle protocol to version 2. If you find anything higher its irregular. Ren'Py drops now Py2 support and the next RenPy (v8.4 ?) will be Py3-only with the pickle prot. open to the highest (5 currently).
- Encoding can be the same but also possible its something wrong in the users Python install or system settings. :unsure:

Greets
 
  • Like
Reactions: MaxRichard

citrone

Member
Jul 17, 2023
259
522
this thread is kind of a mess, how very unix like
so whats the most recent and functional version?
ty for your work
 
5.00 star(s) 1 Vote