Minor stylistic fixes and README updates.
This commit is contained in:
parent
412886cb77
commit
a50e17589b
2 changed files with 27 additions and 25 deletions
|
@ -6,7 +6,7 @@ Currently, only writing to RPAv2/RPAv3 archives is supported.
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
-----
|
-----
|
||||||
rpatool [-l] [-x] [-c] [-d] [-a] [-o OUTFILE] [-2] [-3] [-k KEY]
|
rpatool [-l|-x|-c|-d] [-a] [-o OUTFILE] [-2] [-3] [-k KEY]
|
||||||
[-p COUNT] [-h] [-v] [-V]
|
[-p COUNT] [-h] [-v] [-V]
|
||||||
ARCHIVE [FILE [FILE ...]]
|
ARCHIVE [FILE [FILE ...]]
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ Usage
|
||||||
ARCHIVE The Ren'py archive file to operate on
|
ARCHIVE The Ren'py archive file to operate on
|
||||||
FILE Zero or more files to operate on
|
FILE Zero or more files to operate on
|
||||||
|
|
||||||
action arguments:
|
actions:
|
||||||
-l, --list List files in archive ARCHIVE
|
-l, --list List files in archive ARCHIVE
|
||||||
-x, --extract Extract FILEs from ARCHIVE
|
-x, --extract Extract FILEs from ARCHIVE
|
||||||
-c, --create Creative ARCHIVE from FILEs
|
-c, --create Creative ARCHIVE from FILEs
|
||||||
|
|
48
rpatool
48
rpatool
|
@ -26,25 +26,27 @@ class RenPyArchive:
|
||||||
self.key = key
|
self.key = key
|
||||||
self.verbose = verbose
|
self.verbose = verbose
|
||||||
|
|
||||||
if file != None:
|
if file is not None:
|
||||||
self.load(file)
|
self.load(file)
|
||||||
else:
|
else:
|
||||||
self.version = version
|
self.version = version
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
if self.handle != None:
|
if self.handle is not None:
|
||||||
self.handle.close()
|
self.handle.close()
|
||||||
|
|
||||||
# Determine archive version.
|
# Determine archive version.
|
||||||
def get_version(self):
|
def get_version(self):
|
||||||
self.handle.seek(0)
|
self.handle.seek(0)
|
||||||
magic = self.handle.readline().decode('utf-8')
|
magic = self.handle.readline().decode('utf-8')
|
||||||
|
|
||||||
if magic.startswith(self.RPA3_MAGIC):
|
if magic.startswith(self.RPA3_MAGIC):
|
||||||
return 3
|
return 3
|
||||||
elif magic.startswith(self.RPA2_MAGIC):
|
elif magic.startswith(self.RPA2_MAGIC):
|
||||||
return 2
|
return 2
|
||||||
elif self.file.endswith('.rpi'):
|
elif self.file.endswith('.rpi'):
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
raise ValueError('the given file is not a valid Ren\'Py archive, or an unsupported version')
|
raise ValueError('the given file is not a valid Ren\'Py archive, or an unsupported version')
|
||||||
|
|
||||||
# Extract file indexes from opened archive.
|
# Extract file indexes from opened archive.
|
||||||
|
@ -53,17 +55,17 @@ class RenPyArchive:
|
||||||
indexes = None
|
indexes = None
|
||||||
|
|
||||||
if self.version == 2 or self.version == 3:
|
if self.version == 2 or self.version == 3:
|
||||||
# Fetch metadata
|
# Fetch metadata.
|
||||||
metadata = self.handle.readline()
|
metadata = self.handle.readline()
|
||||||
offset = int(metadata[8:24], 16)
|
offset = int(metadata[8:24], 16)
|
||||||
if self.version == 3:
|
if self.version == 3:
|
||||||
self.key = int(metadata[25:33], 16)
|
self.key = int(metadata[25:33], 16)
|
||||||
|
|
||||||
# Load in indexes
|
# Load in indexes.
|
||||||
self.handle.seek(offset)
|
self.handle.seek(offset)
|
||||||
indexes = pickle.loads(self.handle.read().decode('zlib'))
|
indexes = pickle.loads(self.handle.read().decode('zlib'))
|
||||||
|
|
||||||
# Deobfuscate indexes
|
# Deobfuscate indexes.
|
||||||
if self.version == 3:
|
if self.version == 3:
|
||||||
obfuscated_indexes = indexes
|
obfuscated_indexes = indexes
|
||||||
indexes = {}
|
indexes = {}
|
||||||
|
@ -111,12 +113,12 @@ class RenPyArchive:
|
||||||
def read(self, filename):
|
def read(self, filename):
|
||||||
filename = self.convert_filename(filename)
|
filename = self.convert_filename(filename)
|
||||||
|
|
||||||
# Check if the file exists in our indexes
|
# Check if the file exists in our indexes.
|
||||||
if not filename in self.files and not filename in self.indexes:
|
if not filename in self.files and not filename in self.indexes:
|
||||||
raise IOError(errno.ENOENT, 'the requested file {0} does not exist in the given Ren\'Py archive'.format(filename))
|
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 it's in our opened archive index, and our archive handle isn't valid, something is obviously wrong.
|
||||||
if not filename in self.files and filename in self.indexes and self.handle == None:
|
if not filename 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))
|
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.
|
# Check our simplified internal indexes first, in case someone wants to read a file they added before without saving, for some unholy reason.
|
||||||
|
@ -138,7 +140,7 @@ class RenPyArchive:
|
||||||
|
|
||||||
# Modify a file in archive or internal storage.
|
# Modify a file in archive or internal storage.
|
||||||
def change(self, filename, contents):
|
def change(self, filename, contents):
|
||||||
# Our 'change' is basically removing the file from our indexes first, and then re-add it.
|
# Our 'change' is basically removing the file from our indexes first, and then re-adding it.
|
||||||
self.remove(filename)
|
self.remove(filename)
|
||||||
self.add(filename, contents)
|
self.add(filename, contents)
|
||||||
|
|
||||||
|
@ -165,7 +167,7 @@ class RenPyArchive:
|
||||||
|
|
||||||
# Load archive.
|
# Load archive.
|
||||||
def load(self, filename):
|
def load(self, filename):
|
||||||
if self.handle != None:
|
if self.handle is not None:
|
||||||
self.handle.close()
|
self.handle.close()
|
||||||
self.file = filename
|
self.file = filename
|
||||||
self.files = {}
|
self.files = {}
|
||||||
|
@ -175,20 +177,20 @@ class RenPyArchive:
|
||||||
|
|
||||||
# Save current state into a new file, merging archive and internal storage, rebuilding indexes, and optionally saving in another format version.
|
# 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):
|
def save(self, filename = None):
|
||||||
if filename == None:
|
if filename is None:
|
||||||
filename = self.file
|
filename = self.file
|
||||||
if filename == None:
|
if filename is None:
|
||||||
raise ValueError('no target file found for saving archive')
|
raise ValueError('no target file found for saving archive')
|
||||||
if self.version != 2 and self.version != 3:
|
if self.version != 2 and self.version != 3:
|
||||||
raise ValueError('saving is only supported for version 2 and 3 archives')
|
raise ValueError('saving is only supported for version 2 and 3 archives')
|
||||||
|
|
||||||
self.verbose_print('Rebuilding archive index...')
|
self.verbose_print('Rebuilding archive index...')
|
||||||
# Fill our own files structure with the files added or changed in this session
|
# Fill our own files structure with the files added or changed in this session.
|
||||||
files = self.files
|
files = self.files
|
||||||
# First, read files from the current archive into our files structure
|
# First, read files from the current archive into our files structure.
|
||||||
for file in self.indexes.keys():
|
for file in self.indexes.keys():
|
||||||
content = self.read(file)
|
content = self.read(file)
|
||||||
# Remove from indexes array once read, add to our own array
|
# Remove from indexes array once read, add to our own array.
|
||||||
del self.indexes[file]
|
del self.indexes[file]
|
||||||
files[file] = content
|
files[file] = content
|
||||||
|
|
||||||
|
@ -232,7 +234,7 @@ class RenPyArchive:
|
||||||
# We're done, close it.
|
# We're done, close it.
|
||||||
archive.close()
|
archive.close()
|
||||||
|
|
||||||
# Reload the file in our inner database
|
# Reload the file in our inner database.
|
||||||
self.load(filename)
|
self.load(filename)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
@ -264,20 +266,20 @@ if __name__ == "__main__":
|
||||||
parser.add_argument('-V', '--version', action='version', version='rpatool v0.8', help='Show version information.')
|
parser.add_argument('-V', '--version', action='version', version='rpatool v0.8', help='Show version information.')
|
||||||
arguments = parser.parse_args()
|
arguments = parser.parse_args()
|
||||||
|
|
||||||
# Determine RPA version
|
# Determine RPA version.
|
||||||
if arguments.two:
|
if arguments.two:
|
||||||
version = 2
|
version = 2
|
||||||
else:
|
else:
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
# Determine RPAv3 key
|
# Determine RPAv3 key.
|
||||||
if 'key' in arguments and arguments.key != None:
|
if 'key' in arguments and arguments.key is not None:
|
||||||
key = int(arguments.key, 16)
|
key = int(arguments.key, 16)
|
||||||
else:
|
else:
|
||||||
key = 0xDEADBEEF
|
key = 0xDEADBEEF
|
||||||
|
|
||||||
# Determine padding bytes
|
# Determine padding bytes.
|
||||||
if 'padding' in arguments and arguments.padding != None:
|
if 'padding' in arguments and arguments.padding is not None:
|
||||||
padding = int(arguments.padding)
|
padding = int(arguments.padding)
|
||||||
else:
|
else:
|
||||||
padding = 0
|
padding = 0
|
||||||
|
@ -288,7 +290,7 @@ if __name__ == "__main__":
|
||||||
output = arguments.archive
|
output = arguments.archive
|
||||||
else:
|
else:
|
||||||
archive = arguments.archive
|
archive = arguments.archive
|
||||||
if 'outfile' in arguments and arguments.outfile != None:
|
if 'outfile' in arguments and arguments.outfile is not None:
|
||||||
output = arguments.outfile
|
output = arguments.outfile
|
||||||
else:
|
else:
|
||||||
# Default output directory for extraction is the current directory.
|
# Default output directory for extraction is the current directory.
|
||||||
|
@ -298,7 +300,7 @@ if __name__ == "__main__":
|
||||||
output = arguments.archive
|
output = arguments.archive
|
||||||
|
|
||||||
# Normalize files.
|
# Normalize files.
|
||||||
if len(arguments.files) > 0 and type(arguments.files[0]) == list:
|
if len(arguments.files) > 0 and isinstance(arguments.files[0], list):
|
||||||
arguments.files = arguments.files[0]
|
arguments.files = arguments.files[0]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue