You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
What did you try to open the archive with unrpa, and how did it fail?
Extracting files from C:\Temp\DSCS-0.1.1-win\game\dscs.rpa.
[0.00%] modules\0005_core\keymap.rpyc
There was an error while trying to extract a file from the archive.
If you wish to try and extract as much from the archive as possible, please use --continue-on-error.
Error Detail: Traceback (most recent call last):
File "C:\Users\omega\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\LocalCache\local-packages\Python37\site-packages\unrpa_init_.py", line 134, in extract_files
version.postprocess(file_view, output_file)
File "C:\Users\omega\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\LocalCache\local-packages\Python37\site-packages\unrpa\versions\version.py", line 24, in postprocess
for segment in iter(source.read1, b""):
File "C:\Users\omega\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\LocalCache\local-packages\Python37\site-packages\unrpa\view.py", line 20, in read1
return self.base_read(lambda source: source.read1, amount)
File "C:\Users\omega\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\LocalCache\local-packages\Python37\site-packages\unrpa\view.py", line 34, in base_read
return self.base_read(method, amount)
File "C:\Users\omega\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\LocalCache\local-packages\Python37\site-packages\unrpa\view.py", line 37, in base_read
raise Exception("End of archive reached before the file should end.")
Exception: End of archive reached before the file should end.
Files needed to add support
from __future__ import division, absolute_import, with_statement, print_function, unicode_literals
from renpy.compat import *
import renpy, os.path, sys, types, threading, zlib, re, io, unicodedata
from renpy.compat.pickle import loads
from renpy.webloader import DownloadNeeded
(b'').encode(b'utf-8')
def get_path(fn):
fn = os.path.join(renpy.config.gamedir, fn)
dn = os.path.dirname(fn)
try:
if not os.path.exists(dn):
os.makedirs(dn)
except:
pass
return fn
if renpy.android:
import android.apk
expansion = os.environ.get(b'ANDROID_EXPANSION', None)
if expansion is not None:
print(b'Using expansion file', expansion)
apks = [
android.apk.APK(apk=expansion, prefix=b'assets/x-game/'),
android.apk.APK(apk=expansion, prefix=b'assets/x-renpy/x-common/')]
game_apks = [
apks[0]]
else:
print(b'Not using expansion file.')
apks = [
android.apk.APK(prefix=b'assets/x-game/'),
android.apk.APK(prefix=b'assets/x-renpy/x-common/')]
game_apks = [
apks[0]]
else:
apks = []
game_apks = []
archives = []
old_config_archives = None
lower_map = {}
archive_handlers = []
class RPAv3ArchiveHandler(object):
@staticmethod
def get_supported_extensions():
return [b'.rpa']
@staticmethod
def get_supported_headers():
return [b'RPA-3.0 ']
@staticmethod
def read_index(infile):
l = infile.read(40)
offset = int(l[8:24], 16)
key = int(l[25:33], 16)
infile.seek(offset)
index = loads(zlib.decompress(infile.read()))
for k in index.keys():
if len(index[k][0]) == 2:
index[k] = [ (offset ^ key ^ 3735929054, dlen ^ key ^ 3735929054) for offset, dlen in index[k] ]
else:
index[k] = [ (offset ^ key ^ 3735929054, dlen ^ key ^ 3735929054, start) for offset, dlen, start in index[k] ]
return index
archive_handlers.append(RPAv3ArchiveHandler)
class RPAv2ArchiveHandler(object):
@staticmethod
def get_supported_extensions():
return [b'.rpa']
@staticmethod
def get_supported_headers():
return [b'RPA-2.0 ']
@staticmethod
def read_index(infile):
l = infile.read(24)
offset = int(l[8:], 16)
infile.seek(offset)
index = loads(zlib.decompress(infile.read()))
return index
archive_handlers.append(RPAv2ArchiveHandler)
class RPAv1ArchiveHandler(object):
@staticmethod
def get_supported_extensions():
return [b'.rpi']
@staticmethod
def get_supported_headers():
return [b'x\x9c']
@staticmethod
def read_index(infile):
return loads(zlib.decompress(infile.read()))
archive_handlers.append(RPAv1ArchiveHandler)
def index_archives():
global archives
global old_config_archives
if old_config_archives == renpy.config.archives:
return
else:
old_config_archives = renpy.config.archives[:]
lower_map.clear()
cleardirfiles()
archives = []
max_header_length = 0
for handler in archive_handlers:
for header in handler.get_supported_headers():
header_len = len(header)
if header_len > max_header_length:
max_header_length = header_len
archive_extensions = []
for handler in archive_handlers:
for ext in handler.get_supported_extensions():
if ext not in archive_extensions:
archive_extensions.append(ext)
for prefix in renpy.config.archives:
for ext in archive_extensions:
fn = None
f = None
try:
fn = transfn(prefix + ext)
f = open(fn, b'rb')
except:
continue
with f:
file_header = f.read(max_header_length)
for handler in archive_handlers:
try:
archive_handled = False
for header in handler.get_supported_headers():
if file_header.startswith(header):
f.seek(0, 0)
index = handler.read_index(f)
archives.append((prefix + ext, index))
archive_handled = True
break
if archive_handled == True:
break
except:
raise
for dir, fn in listdirfiles():
lower_map[unicodedata.normalize(b'NFC', fn.lower())] = fn
for fn in remote_files:
lower_map[unicodedata.normalize(b'NFC', fn.lower())] = fn
return
def walkdir(dir):
rv = []
if not os.path.exists(dir) and not renpy.config.developer:
return rv
for i in os.listdir(dir):
if i[0] == b'.':
continue
try:
i = renpy.exports.fsdecode(i)
except:
continue
if os.path.isdir(dir + b'/' + i):
for fn in walkdir(dir + b'/' + i):
rv.append(i + b'/' + fn)
else:
rv.append(i)
return rv
game_files = []
common_files = []
loadable_cache = {}
remote_files = {}
def cleardirfiles():
global common_files
global game_files
game_files = []
common_files = []
scandirfiles_callbacks = []
def scandirfiles():
seen = set()
def add(dn, fn, files, seen):
fn = unicode(fn)
if fn in seen:
return
if fn.startswith(b'cache/'):
return
if fn.startswith(b'saves/'):
return
files.append((dn, fn))
seen.add(fn)
loadable_cache[unicodedata.normalize(b'NFC', fn.lower())] = True
for i in scandirfiles_callbacks:
i(add, seen)
def scandirfiles_from_apk(add, seen):
for apk in apks:
if apk not in game_apks:
files = common_files
else:
files = game_files
for f in apk.list():
f = (b'/').join(i[2:] for i in f.split(b'/'))
add(None, f, files, seen)
return
if renpy.android:
scandirfiles_callbacks.append(scandirfiles_from_apk)
def scandirfiles_from_remote_file(add, seen):
index_filename = os.path.join(renpy.config.gamedir, b'renpyweb_remote_files.txt')
if os.path.exists(index_filename):
files = game_files
with open(index_filename, b'rb') as (remote_index):
while True:
f = remote_index.readline()
metadata = remote_index.readline()
if f == b'' or metadata == b'':
break
f = f.rstrip(b'\r\n')
metadata = metadata.rstrip(b'\r\n')
entry_type, entry_size = metadata.split(b' ')
if entry_type == b'image':
entry_size = [ int(i) for i in entry_size.split(b',') ]
add(b'/game', f, files, seen)
remote_files[f] = {b'type': entry_type, b'size': entry_size}
if renpy.emscripten or os.environ.get(b'RENPY_SIMULATE_DOWNLOAD', False):
scandirfiles_callbacks.append(scandirfiles_from_remote_file)
def scandirfiles_from_filesystem(add, seen):
for i in renpy.config.searchpath:
if renpy.config.commondir and i == renpy.config.commondir:
files = common_files
else:
files = game_files
i = os.path.join(renpy.config.basedir, i)
for j in walkdir(i):
add(i, j, files, seen)
scandirfiles_callbacks.append(scandirfiles_from_filesystem)
def scandirfiles_from_archives(add, seen):
files = game_files
for _prefix, index in archives:
for j in index:
add(None, j, files, seen)
return
scandirfiles_callbacks.append(scandirfiles_from_archives)
def listdirfiles(common=True):
if not game_files and not common_files:
scandirfiles()
if common:
return game_files + common_files
else:
return list(game_files)
class SubFile(object):
def __init__(self, fn, base, length, start):
self.fn = fn
self.f = None
self.base = base
self.offset = 0
self.length = length
self.start = start
if not self.start:
self.name = fn
else:
self.name = None
return
def open(self):
self.f = open(self.fn, b'rb')
self.f.seek(self.base)
def __enter__(self):
return self
def __exit__(self, _type, value, tb):
self.close()
return False
def read(self, length=None):
if self.f is None:
self.open()
maxlength = self.length - self.offset
if length is not None:
length = min(length, maxlength)
else:
length = maxlength
rv1 = self.start[self.offset:self.offset + length]
length -= len(rv1)
self.offset += len(rv1)
if length:
rv2 = self.f.read(length)
self.offset += len(rv2)
else:
rv2 = b''
return rv1 + rv2
def readline(self, length=None):
if self.f is None:
self.open()
maxlength = self.length - self.offset
if length is not None:
length = min(length, maxlength)
else:
length = maxlength
if self.offset < len(self.start):
rv = b''
while length:
c = self.read(1)
rv += c
if c == b'\n':
break
length -= 1
return rv
rv = self.f.readline(length)
self.offset += len(rv)
return rv
def readlines(self, length=None):
rv = []
while True:
l = self.readline(length)
if not l:
break
if length is not None:
length -= len(l)
if l < 0:
break
rv.append(l)
return rv
def xreadlines(self):
return self
def __iter__(self):
return self
def __next__(self):
rv = self.readline()
if not rv:
raise StopIteration()
return rv
next = __next__
def flush(self):
pass
def seek(self, offset, whence=0):
if self.f is None:
self.open()
if whence == 0:
offset = offset
elif whence == 1:
offset = self.offset + offset
elif whence == 2:
offset = self.length + offset
if offset > self.length:
offset = self.length
self.offset = offset
offset = offset - len(self.start)
if offset < 0:
offset = 0
self.f.seek(offset + self.base)
return
def tell(self):
return self.offset
def close(self):
if self.f is not None:
self.f.close()
self.f = None
return
def write(self, s):
raise Exception(b'Write not supported by SubFile')
open_file = open
if b'RENPY_FORCE_SUBFILE' in os.environ:
def open_file(name, mode):
f = open(name, mode)
f.seek(0, 2)
length = f.tell()
f.seek(0, 0)
return SubFile(f, 0, length, b'')
file_open_callbacks = []
def load_core(name):
name = lower_map.get(unicodedata.normalize(b'NFC', name.lower()), name)
for i in file_open_callbacks:
rv = i(name)
if rv is not None:
return rv
return
def load_from_file_open_callback(name):
if renpy.config.file_open_callback:
return renpy.config.file_open_callback(name)
else:
return
file_open_callbacks.append(load_from_file_open_callback)
def load_from_filesystem(name):
if not renpy.config.force_archives:
try:
fn = transfn(name)
return open_file(fn, b'rb')
except:
pass
return
file_open_callbacks.append(load_from_filesystem)
def load_from_apk(name):
for apk in apks:
prefixed_name = (b'/').join(b'x-' + i for i in name.split(b'/'))
try:
return apk.open(prefixed_name)
except IOError:
pass
return
if renpy.android:
file_open_callbacks.append(load_from_apk)
def load_from_archive(name):
for prefix, index in archives:
if name not in index:
continue
afn = transfn(prefix)
data = []
if len(index[name]) == 1:
t = index[name][0]
if len(t) == 2:
offset, dlen = t
start = b''
else:
offset, dlen, start = t
rv = SubFile(afn, offset, dlen, start)
else:
with open(afn, b'rb') as (f):
for offset, dlen in index[name]:
f.seek(offset)
data.append(f.read(dlen))
rv = io.BytesIO((b'').join(data))
return rv
return
file_open_callbacks.append(load_from_archive)
def load_from_remote_file(name):
if name in remote_files:
raise DownloadNeeded(relpath=name, rtype=remote_files[name][b'type'], size=remote_files[name][b'size'])
return
if renpy.emscripten or os.environ.get(b'RENPY_SIMULATE_DOWNLOAD', False):
file_open_callbacks.append(load_from_remote_file)
def check_name(name):
if renpy.config.reject_backslash and b'\\' in name:
raise Exception(b"Backslash in filename, use '/' instead: %r" % name)
if renpy.config.reject_relative:
split = name.split(b'/')
if b'.' in split or b'..' in split:
raise Exception(b"Filenames may not contain relative directories like '.' and '..': %r" % name)
def get_prefixes(tl=True):
rv = []
if tl:
language = renpy.game.preferences.language
else:
language = None
for prefix in renpy.config.search_prefixes:
if language is not None:
rv.append(renpy.config.tl_directory + b'/' + language + b'/' + prefix)
rv.append(prefix)
return rv
def load(name, tl=True):
if renpy.display.predict.predicting:
if threading.current_thread().name == b'MainThread':
if not (renpy.emscripten or os.environ.get(b'RENPY_SIMULATE_DOWNLOAD', False)):
raise Exception((b'Refusing to open {} while predicting.').format(name))
if renpy.config.reject_backslash and b'\\' in name:
raise Exception(b"Backslash in filename, use '/' instead: %r" % name)
name = re.sub(b'/+', b'/', name).lstrip(b'/')
for p in get_prefixes(tl):
rv = load_core(p + name)
if rv is not None:
return rv
raise IOError(b"Couldn't find file '%s'." % name)
return
def loadable_core(name):
name = lower_map.get(unicodedata.normalize(b'NFC', name.lower()), name)
if name in loadable_cache:
return loadable_cache[name]
try:
transfn(name)
loadable_cache[name] = True
return True
except:
pass
for apk in apks:
prefixed_name = (b'/').join(b'x-' + i for i in name.split(b'/'))
if prefixed_name in apk.info:
loadable_cache[name] = True
return True
for _prefix, index in archives:
if name in index:
loadable_cache[name] = True
return True
if name in remote_files:
loadable_cache[name] = True
return name
loadable_cache[name] = False
return False
def loadable(name):
name = name.lstrip(b'/')
if renpy.config.loadable_callback is not None and renpy.config.loadable_callback(name):
return True
else:
for p in get_prefixes():
if loadable_core(p + name):
return True
return False
def transfn(name):
name = name.lstrip(b'/')
if renpy.config.reject_backslash and b'\\' in name:
raise Exception(b"Backslash in filename, use '/' instead: %r" % name)
name = lower_map.get(unicodedata.normalize(b'NFC', name.lower()), name)
if isinstance(name, bytes):
name = name.decode(b'utf-8')
for d in renpy.config.searchpath:
fn = os.path.join(renpy.config.basedir, d, name)
add_auto(fn)
if os.path.isfile(fn):
return fn
raise Exception(b"Couldn't find file '%s'." % name)
hash_cache = dict()
def get_hash(name):
rv = hash_cache.get(name, None)
if rv is not None:
return rv
else:
rv = 0
try:
f = load(name)
while True:
data = f.read(1048576)
if not data:
break
rv = zlib.adler32(data, rv)
except:
pass
hash_cache[name] = rv
return rv
class RenpyImporter(object):
def __init__(self, prefix=b''):
self.prefix = prefix
def translate(self, fullname, prefix=None):
if prefix is None:
prefix = self.prefix
try:
if not isinstance(fullname, str):
fullname = fullname.decode(b'utf-8')
fn = prefix + fullname.replace(b'.', b'/')
except:
return
if loadable(fn + b'.py'):
return fn + b'.py'
else:
if loadable(fn + b'/__init__.py'):
return fn + b'/__init__.py'
return
def find_module(self, fullname, path=None):
if path is not None:
for i in path:
if self.translate(fullname, i):
return RenpyImporter(i)
if self.translate(fullname):
return self
else:
return
def load_module(self, fullname):
filename = self.translate(fullname, self.prefix)
pyname = pystr(fullname)
mod = sys.modules.setdefault(pyname, types.ModuleType(pyname))
mod.__name__ = pyname
mod.__file__ = filename
mod.__loader__ = self
if filename.endswith(b'__init__.py'):
mod.__path__ = [
filename[:-len(b'__init__.py')]]
for encoding in [b'utf-8', b'latin-1']:
try:
source = load(filename).read().decode(encoding)
if source and source[0] == b'\ufeff':
source = source[1:]
source = source.encode(b'raw_unicode_escape')
source = source.replace(b'\r', b'')
code = compile(source, filename, b'exec', renpy.python.old_compile_flags, 1)
break
except:
if encoding == b'latin-1':
raise
exec code in mod.__dict__
return sys.modules[fullname]
def get_data(self, filename):
return load(filename).read()
meta_backup = []
def add_python_directory(path):
if path and not path.endswith(b'/'):
path = path + b'/'
sys.meta_path.insert(0, RenpyImporter(path))
def init_importer():
meta_backup[:] = sys.meta_path
add_python_directory(b'python-packages/')
add_python_directory(b'')
def quit_importer():
sys.meta_path[:] = meta_backup
needs_autoreload = set()
auto_mtimes = {}
auto_thread = None
auto_quit_flag = True
auto_lock = threading.Condition()
auto_blacklisted = renpy.object.Sentinel(b'auto_blacklisted')
def auto_mtime(fn):
try:
return os.path.getmtime(fn)
except:
return
return
def add_auto(fn, force=False):
fn = fn.replace(b'\\', b'/')
if not renpy.autoreload:
return
if fn in auto_mtimes and not force:
return
for e in renpy.config.autoreload_blacklist:
if fn.endswith(e):
with auto_lock:
auto_mtimes[fn] = auto_blacklisted
return
mtime = auto_mtime(fn)
with auto_lock:
auto_mtimes[fn] = mtime
def auto_thread_function():
global auto_quit_flag
global needs_autoreload
while True:
with auto_lock:
auto_lock.wait(1.5)
if auto_quit_flag:
return
items = list(auto_mtimes.items())
for fn, mtime in items:
if mtime is auto_blacklisted:
continue
if auto_mtime(fn) != mtime:
with auto_lock:
if auto_mtime(fn) != auto_mtimes[fn]:
needs_autoreload.add(fn)
def check_autoreload():
while needs_autoreload:
fn = next(iter(needs_autoreload))
mtime = auto_mtime(fn)
with auto_lock:
needs_autoreload.discard(fn)
auto_mtimes[fn] = mtime
if not renpy.autoreload:
return
for regex, func in renpy.config.autoreload_functions:
if re.search(regex, fn, re.I):
fn = os.path.relpath(fn, renpy.config.gamedir).replace(b'\\', b'/')
func(fn)
break
else:
renpy.exports.reload_script()
def auto_init():
global auto_quit_flag
global auto_thread
global needs_autoreload
needs_autoreload = set()
if not renpy.autoreload:
return
auto_quit_flag = False
auto_thread = threading.Thread(target=auto_thread_function)
auto_thread.daemon = True
auto_thread.start()
def auto_quit():
global auto_quit_flag
if auto_thread is None:
return
else:
auto_quit_flag = True
with auto_lock:
auto_lock.notify_all()
auto_thread.join()
return
Additional context
It seems a random number is added to the key and offset to determine the index in the archive extractor.
NSFW link to game
The text was updated successfully, but these errors were encountered:
What did you try to open the archive with unrpa, and how did it fail?
Extracting files from C:\Temp\DSCS-0.1.1-win\game\dscs.rpa.
[0.00%] modules\0005_core\keymap.rpyc
There was an error while trying to extract a file from the archive.
If you wish to try and extract as much from the archive as possible, please use --continue-on-error.
Error Detail: Traceback (most recent call last):
File "C:\Users\omega\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\LocalCache\local-packages\Python37\site-packages\unrpa_init_.py", line 134, in extract_files
version.postprocess(file_view, output_file)
File "C:\Users\omega\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\LocalCache\local-packages\Python37\site-packages\unrpa\versions\version.py", line 24, in postprocess
for segment in iter(source.read1, b""):
File "C:\Users\omega\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\LocalCache\local-packages\Python37\site-packages\unrpa\view.py", line 20, in read1
return self.base_read(lambda source: source.read1, amount)
File "C:\Users\omega\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\LocalCache\local-packages\Python37\site-packages\unrpa\view.py", line 34, in base_read
return self.base_read(method, amount)
File "C:\Users\omega\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\LocalCache\local-packages\Python37\site-packages\unrpa\view.py", line 37, in base_read
raise Exception("End of archive reached before the file should end.")
Exception: End of archive reached before the file should end.
Files needed to add support
Additional context
It seems a random number is added to the key and offset to determine the index in the archive extractor.
NSFW link to game
The text was updated successfully, but these errors were encountered: