Skip to content

Commit

Permalink
Python 3 and continue on error option.
Browse files Browse the repository at this point in the history
  • Loading branch information
Lattyware committed Oct 4, 2017
1 parent 734e47c commit bdec8e1
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 22 deletions.
4 changes: 2 additions & 2 deletions README
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Unrpa is a script to extract files from archives created for the Ren'Py Visual Novel Engine (http://www.renpy.org/).

You will need Python 2.x in order to run it (either install through your package manager or see
https://www.python.org/downloads/ and look for the latest release with a version beginning with 2).
You will need Python 3.x in order to run it (either install through your package manager or see
https://www.python.org/downloads/).

Options:
--version show program's version number and exit
Expand Down
59 changes: 39 additions & 20 deletions unrpa
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/usr/bin/env python2
#!/usr/bin/env python3

"""
unrpa is a tool to extract files from Ren'Py archives (.rpa).
Expand All @@ -21,12 +21,14 @@ import os
import optparse
import sys
import pickle
import zlib
import traceback


class UnRPA:
NAME = "unrpa"

def __init__(self, filename, verbosity=1, path=None, mkdir=False, version=None):
def __init__(self, filename, verbosity=1, path=None, mkdir=False, version=None, continue_on_error=False):
self.verbose = verbosity
if path:
self.path = os.path.abspath(path)
Expand All @@ -35,6 +37,7 @@ class UnRPA:
self.mkdir = mkdir
self.version = version
self.archive = filename
self.continue_on_error = continue_on_error

def log(self, verbosity, message):
if self.verbose > verbosity:
Expand All @@ -52,17 +55,28 @@ class UnRPA:

index = self.get_index()
total_files = len(index)
for file_number, (item, data) in enumerate(index.iteritems()):
self.make_directory_structure(os.path.join(self.path, os.path.split(item)[0]))
raw_file = self.extract_file(item, data, file_number, total_files)
with open(os.path.join(self.path, item.encode('UTF-8')), "wb") as f:
f.write(raw_file)
for file_number, (item, data) in enumerate(index.items()):
try:
item_path = item.decode("utf-8")
self.make_directory_structure(os.path.join(self.path, os.path.split(item_path)[0]))
raw_file = self.extract_file(item_path, data, file_number, total_files)
with open(os.path.join(self.path, item_path), "wb") as f:
f.write(raw_file)
except BaseException as e:
if self.continue_on_error:
traceback.print_exc()
self.log(0,
"error extracting (see above), but --continue-on-error was used, so we will keep going.")
else:
raise Exception("There was an error while trying to extract a file. See the nested exception for "
"more. If you wish to try and extract as much from the archive as possible, please "
"use the --continue-on-error flag.") from e

def list_files(self):
self.log(1, "listing files:")
paths = self.get_index().keys()
for path in sorted(paths):
print(path.encode('utf-8'))
print(path)

def extract_file(self, name, data, file_number, total_files):
self.log(1, "[{:04.2%}] {:>3}".format(file_number / float(total_files), name))
Expand Down Expand Up @@ -94,12 +108,12 @@ class UnRPA:
offset = int(parts[1], 16)
key = int(parts[2], 16)
f.seek(offset)
index = pickle.loads(f.read().decode("zlib"))
index = pickle.loads(zlib.decompress(f.read()), encoding="bytes")
if self.version == 3:
index = self.deobfuscate_index(index, key)

if "/" != os.sep:
return {item.replace("/", os.sep): data for item, data in index.iteritems()}
return {item.replace("/", os.sep): data for item, data in index.items()}
else:
return index

Expand All @@ -108,38 +122,42 @@ class UnRPA:
if ext == ".rpa":
with open(self.archive, "rb") as f:
line = f.readline()
if line.startswith("RPA-3.0 "):
if line.startswith(b"RPA-3.0 "):
return 3
if line.startswith("RPA-2.0 "):
if line.startswith(b"RPA-2.0 "):
return 2
else:
return None
elif ext == ".rpi":
return 1

def deobfuscate_index(self, index, key):
return {k: self.deobfuscate_entry(key, v) for k, v in index.iteritems()}
return {k: self.deobfuscate_entry(key, v) for k, v in index.items()}

def deobfuscate_entry(self, key, entry):
if len(entry[0]) == 2:
return [(offset ^ key, dlen ^ key, '') for offset, dlen in entry]
return [(offset ^ key, dlen ^ key, "") for offset, dlen in entry]
else:
return [(offset ^ key, dlen ^ key, start) for offset, dlen, start in entry]


if __name__ == "__main__":
parser = optparse.OptionParser(usage="usage: %prog [options] pathname", version="%prog 1.1")

parser.add_option("-v", "--verbose", action="count", dest="verbose", help="explain what is being done [default]")
parser.add_option("-s", "--silent", action="store_const", const=0, dest="verbose", default=1, help="make no output")
parser.add_option("-v", "--verbose", action="count", dest="verbose",
help="explain what is being done [default].")
parser.add_option("-s", "--silent", action="store_const", const=0, dest="verbose", default=1,
help="no output.")
parser.add_option("-l", "--list", action="store_true", dest="list", default=False,
help="only list contents, do not extract")
help="only list contents, do not extract.")
parser.add_option("-p", "--path", action="store", type="string", dest="path", default=None,
help="will extract to the given path")
help="will extract to the given path.")
parser.add_option("-m", "--mkdir", action="store_true", dest="mkdir", default=False,
help="will make any non-existent directories in extraction path")
help="will make any non-existent directories in extraction path.")
parser.add_option("-f", "--force", action="store", type="int", dest="version", default=None,
help="forces an archive version. May result in failure.")
parser.add_option("--continue-on-error", action="store_true", dest="continue_on_error", default=False,
help="try to continue extraction when something goes wrong.")

(options, args) = parser.parse_args()

Expand All @@ -159,7 +177,8 @@ if __name__ == "__main__":

filename = args[0]

extractor = UnRPA(filename, options.verbose, options.path, options.mkdir, options.version)
extractor = UnRPA(filename, options.verbose, options.path, options.mkdir, options.version,
options.continue_on_error)
if options.list:
extractor.list_files()
else:
Expand Down

0 comments on commit bdec8e1

Please sign in to comment.