Rpa extractor скачать


Декомпиляция rpyc- и rpa-файлов. Утилиты от Vendor’а, Lolbot’а и скрипт от Eliont’а. - Ren'Py - Коллективный блог - Anivisual.net

В этой статье я расскажу о трёх способах декомпиляции rpyc- и rpa-файлов. Эти методы просты и не требуют каких-то дополнительных программных средств, но, к сожалению, они не универсальны и не всесильны. Соответственно, если они не смогли справиться с имеющимися в вашем распоряжении архивами, то не стоит унывать, есть и другие утилиты и скрипты (например, unrpec или rpatool), просто они могут быть более сложными в применении и требовать установки дополнительных программ.

Метод 1. Утилита от Vendor’а (тексты)

Эта утилита подойдёт тем, кто хочет перевести небольшую одноязычную новеллу и не хочет разбираться со скриптами.

1. Качаем vendor_utils.zip, распаковываем во временную папку (например, \Katawa Shoujo\_temp) 2. Туда же копируем интересующие файлы. Например, script-a1-monday-RU.rpyc 3. Запускаем GetText.bat, получаем два файла: script-a1-monday-RU.TXT и script-a1-monday-RU.BIN 4. Глумимся над полученным файлом с расширением TXT (в BIN не лезем!!!) 5. Запускаем InsertText.bat, получаем новый вариант файла script-a1-monday-RU.rpyc 6. Перемещаем (это важно, если временную папку вы создали вложенной в главную папку игры, иначе игра будет ругаться на дубликаты, т.к. она найдёт ваш забытый во временной папке rpyc-файл и попытается его подключить к проекту) новый script-a1-monday-RU.rpyc обратно. 7. Запускаем игру…

Ссылку на первоисточник, к сожалению, найти не смог :(

Метод 2. Утилита от lolbot’а (скрипты)

Эта утилита позволяет получить исходники всех скриптов, включая системные, и подойдёт для любого уровня проекта, лишь бы справилась…

1. Качаем lolbot_decompile.zip, распаковываем в папку с игрой (в результате файл decompile.rpy из архива должен оказаться в папке \game, например \Katawa Shoujo\game) 2. Запускаем игру и некоторое время ждём, пока игра не начнётся. Всё, из игры можно выходить. Наигрались :) 3. Если первые два пункта выполнены без ошибок, то в корневой папке игры (например, \Katawa Shoujo) увидите огромное количество файлов с длинными названиями, например _Users_delta_Documents_ks svn_Katawa Shoujo Act 1_game_RU_ui_strings-RU.rpy.txt. Это – исходный код во всей красе. Он менее читабелен, чем полученный в методе 1, зато делать с ним можно ну всё что угодно. В том числе править имена персонажей, меню и прочее. Файл decompile.rpy из папки \game можно удалять, иначе при каждом запуске игра будет генерить эти многочисленные файлы. 4. После внесения изменений вспоминаем, что движок RenPy сам генерит этот файл rpyc из rpy. Итак, в папку игры копируем уже исправленный файл (например, _Users_delta_Documents_ks svn_Katawa Shoujo Act 1_game_RU_ui_strings-RU.rpy.txt) и приводим его название в нормальный вид (например, ui_strings-RU.rpy) 5. Запускаем игру… 6. После запуска получаем свежесгенерённый rpyc-файл, а rpy-файл можно стирать

Очевидно, что описанные выше операции можно проделывать и над другими файлами, например над script-a1-monday-RU.rpyc.

Ссылка: https://github.com/lolbot-iichan/decompile.rpy

Метод 3. Скрипт от Eliont’а (графика, звук, шрифты и т.п.)

Для небольшой новеллы:

1. В папке \game (например, \Sugars Delight\game) переименовываем файлы script.rpyc и (если есть) script.rpy в script.rpyc_ и script.rpy_, соответственно. 2. В папке \game (например, \Sugars Delight\game) создаём файл script.rpy со следующим содержимым:

Код

init python:   image_files = [   fn   for dir, fn in renpy.loader.listdirfiles()   if not fn.lower().endswith(".rpy") and not fn.lower().endswith(".rpyc") and not fn.lower().endswith(".rpa") and not fn.lower().endswith(".rpyb")   if not fn[0] == "_"   ]

  def unarchive(original_filename, new_filename):   import os   import os.path

  new_filename = config.basedir + "/" + new_filename   dirname = os.path.dirname(new_filename)

  if not os.path.exists(dirname):   os.makedirs(dirname)

  orig = renpy.file(original_filename)   new = file(new_filename, "wb")   new.write(orig.read())   new.close()   orig.close()

label start:   python:   for img in image_files:   unarchive(img, "extracted/"+img)   return

3. Запускаем игру, жмём «Начать» и ждём, пока снова не появится меню (т.е. игра не запустится), после чего выходим из игры. 4. В новой папке \extracted (например, \Sugars Delight\extracted) получаем всё, кроме самих скриптов, т.е. видео, музыку, аудио, шрифты. 5. Удаляем наш script.rpy, переименовываем описанные в п.1 файлы script.rpyc_ и script.rpy_ обратно в script.rpyc и script.rpy.

Для новеллы побольше:

0. Перво-наперво нам понадобятся исходники скриптов, которые можно получить методом 2. 1. Находим файл, содержащий метку label start:. Меняем расширение оригинальному файлу (например, ui_labels.rpyc переименовываем в ui_labels.rpyc_) 2. Копируем полученный по 2-му методу в эту папку одноимённый файл (например, ui_labels.rpy), в котором удаляем блок, начинающийся с label start:. 3. В папке \game (например, \Katawa Shoujo\game) создаём файл script.rpy со следующим содержимым:

Код

init python:   image_files = [   fn   for dir, fn in renpy.loader.listdirfiles()   if not fn.lower().endswith(".rpy") and not fn.lower().endswith(".rpyc") and not fn.lower().endswith(".rpa") and not fn.lower().endswith(".rpyb")   if not fn[0] == "_"   ]

  def unarchive(original_filename, new_filename):   import os   import os.path

  new_filename = config.basedir + "/" + new_filename   dirname = os.path.dirname(new_filename)

  if not os.path.exists(dirname):   os.makedirs(dirname)

  orig = renpy.file(original_filename)   new = file(new_filename, "wb")   new.write(orig.read())   new.close()   orig.close()

label start:   python:   for img in image_files:   unarchive(img, "extracted/"+img)   return

(код идентичен приведённому выше) 4. Запускаем игру, жмём «Начать» и ждём, пока снова не появится меню (т.е. игра не запустится), после чего выходим из игры. 5. В новой папке \extracted (например, \Katawa Shoujo\extracted) получаем всё, кроме самих скриптов, т.е. видео, музыку, аудио, шрифты. 6. Удаляем изменённые файлы, в которых мы удаляли блок (например, ui_labels.rpy, ui_labels.rpyc), а также и сам script.rpy, меняем расширение файла оригинальным файлам (например, ui_labels.rpyc_ обратно в ui_labels.rpyc).

Ссылка: http://honyaku-subs.ru/forums/viewtopic.php?f=20&t=144#p1726

Материал от пользователя сайта.

anivisual.net

Ren’Py .rpa Extractor – epw14

Was on Steam, came across a Russian Visual Novel, skimmed the comments and found someone wishing for a rip of the music. As it was a free-to-play game, I downloaded it, quickly wrote an extractor in vb.NET [I know: should have been C++] for the Ren’Py .rpa archive (a brute-force affair, nothing elegant), and that was that.

Tried to contact said fellow to deliver the goods via Steam. Could I contact him? Nope. Not unless I wanted to be his bwestest fwiend in the whole, wide world, where we could Fwacebook and Twitter each other to our hearts’ content. No, Steam. Just, no.

(… sigh …)

The upside is: the extractor was easy to write, and I proved my life philosophy correct: an anti-social bastard is a happy bastard.

Note: this extractor is just a brute-force search. This is SLOW (1.3 gb takes around half an hour on a non-SSD drive / it’s just going byte-to-byte w/o any jumps). Could make it faster via chunk-buffering | filetype size detection+jumps, but that’s for another time.

The extractor recognises:

  • JPG
  • PNG
  • MP3
  • OGG
  • RPC2
  • RPY
  • TXT
  • WAV
  • TTF
  • TTC
  • OTF
  • WEBM
  • MKV

Anything else it comes across has the extension of UNK.

Update 1.01:

Fixed stupid command-line argument issue with code.

Tested against 2 non-commercial games, and the ‘Fault Milestone One’ commercial game, and it extracts all the data (FMO graphic came from the archive). You’ll inevitably come across .UNK files as I think Ren’Py can store just about anything in its archives, but a quick hex-check will see you good with the UNKs.

Rambling: From the demo of FMO, you’ll extract around 1,653 files [1.2 GB], and there’s some pretty stuff to be had in this archive. The music is full-on professional. Art style is nice (though why, to establish a character is psychotic, do we need animals being butchered by this fruitloop? [yes, I know this is one of the three requirements for a serial killer / a right proper nutta]). All in all, a very polished game and worth a check on Steam.

Signature: 0x5250412D332E (it doesn’t check, just searches for the ‘Made with Ren’Py.‘ asset tag and extracts as is found)

-epw, 20160109-10

MediaFire Link (6.18 kb)

Like this:

Like Loading...

epw14.wordpress.com

Shizmob/rpatool: A tool to work with Ren'Py archives.

This is a simple tool allowing you to create, modify and extract Ren'Py Archive (.rpa/.rpi) files. Currently, only writing to RPAv2/RPAv3 archives is supported.

Usage

rpatool [-l|-x|-c|-d|-a] [-o OUTFILE] [-2] [-3] [-k KEY] [-p COUNT] [-h] [-v] [-V] ARCHIVE [FILE [FILE ...]] positional arguments: ARCHIVE The Ren'py archive file to operate on FILE Zero or more files to operate on actions: -l, --list List files in archive ARCHIVE -x, --extract Extract FILEs from ARCHIVE -c, --create Creative ARCHIVE from FILEs -d, --delete Delete FILEs from ARCHIVE -a, --append Append FILEs to ARCHIVE optional arguments: -o OUTFILE, --outfile OUTFILE An alternative output archive file when appending to or deleting from archives, or output directory when extracting. -2, --two Use the RPAv2 format for creating/appending to archives -3, --three Use the RPAv3 format for creating/appending to archives (default) -k KEY, --key KEY The obfuscation key used for creating RPAv3 archives (default: 0xDEADBEEF) -p COUNT, --padding COUNT The maximum number of bytes of padding to add between files (default: 0) -h, --help Print this help and exit -v, --verbose Be a bit more verbose while performing operations -V, --version Show version information 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 is: rpatool -x test.rpa script.rpyc=/home/foo/test.rpyc

Examples

rpatool -x foo.rpa

Will extract every file from foo.rpainto the current directory, making subdirectories when necessary.

rpatool -o output -x foo.rpa script.rpyc ui.png

Will extract the files script.rpyc and ui.png from foo.rpa into the directory output.

rpatool -c bar.rpa test.jpg script.rpy sprites

Will create the archive bar.rpa, containing the files test.jpg, script.rpy and the directory sprites.

rpatool -p 25 -k 12345 -c bar.rpa movies=C:\projects\vn\movies

Will create the archive bar.rpa with the obfuscation key 0x12345 and maximum padding of 25, taking files from C:\projects\vn\movies and placing them in the archive folder movies.

rpatool -l baz.rpa

Will list all files in the archive baz.rpa.

rpatool -v -a foo.rpa sprites=sprites_new

Will add all files from the directory sprites_new to the directory sprites in the archive, giving more information about what it's doing.

rpatool -o bar_new.rpa -d bar.rpa foo.jpg

Will remove the file foo.jpg from the archive bar.rpa, storing the result archive in bar_new.rpa.

API

rpatool can also be included in any other project (following the license conditions, of course) to provide the RenPyArchive class. A small overview:

RenPyArchive([file = None], [version = 3], [padlength = 0], [key = 0xDEADBEEF], [verbose = False])

The constructor, which will optionally load an archive file.

file: the archive file to open. If None, no archive will be attempted to open.

version: the archive format version used to save the archive when RenPyArchive.save([file]) is called. Default: 3

padlength: the maximum number of bytes of padding to put between files when saving. Default: 0

key: the obfuscation key used when saving RPAv3 archives. Default: 0xDEADBEEF

verbose: print info on what we are doing to the command line. Default: False

RenPyArchive.load(filename)

Loads an archive file from filename. Will raise an IOError if the file can't be accessed, or a ValueError if the file is not detected as a Ren'Py archive.

RenPyArchive.save([filename])

Save the archive to filename. Will raise ValueError if the filename isn't given with filename, nor previously defined, or an IOError if it couldn't save the file.

RenPyArchive.list()

Give a list of all filenames currently in the archive.

RenPyArchive.has_file(filename)

Returns True if filename is found in the archive, False otherwhise.

RenPyArchive.add(filename, content)

Add a file to the archive with file filename and contents content. Will raise a ValueError if the filename already exists in the archive.

RenPyArchive.change(filename, content)

Change the contents of a current file in the archive. Will raise an IOError if the file isn't known in the archive.

RenPyArchive.remove(filename)

Remove filename from the archive. Will raise an IOError if the filename isn't known in the archive.

RenPyArchive.read(filename)

Read and return the content of file filename in the archive. Will raise an IOError if the filename isn't known in the archive.

License

rpatool is licensed under the WTFPL. See the LICENSE file for more details.

Disclaimer

This tool is intended for use with files on which the authors allowed modification of and/or extraction from ONLY and the unpermitted use on files where such consent was not given is highly discouraged, and most likely a license violation as well. Support requests for help with dealing with such files will not be answered.

Credits

Credits for the creation of the Ren'Py archive format and the reference code in Ren'Py go to renpytom.

github.com

rpatool/rpatool at master · Shizmob/rpatool · GitHub

#!/usr/bin/env python from __future__ import print_function import sys import os import codecs import pickle import errno import random if sys.version_info[0] >= 3: def _unicode(text): return text def _printable(text): return text 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') elif sys.version_info[0] == 2: def _unicode(text): if isinstance(text, unicode): return text return text.decode('utf-8') def _printable(text): return text.encode('utf-8') def _unmangle(data): return data def _unpickle(data): return pickle.loads(data) 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 ' # 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_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 == 2 or self.version == 3: # 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) # Load in indexes. self.handle.seek(offset) contents = codecs.decode(self.handle.read(), 'zlib') indexes = _unpickle(contents) # Deobfuscate indexes. if self.version == 3: 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 = '' while length > 0: padding += chr(random.randint(1, 255)) length -= 1 return 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): filename = _unicode(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(_unicode(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( _printable(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( _printable(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(_printable(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( _printable(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): filename = _unicode(filename) # 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(_unicode(filename)) if filename in self.files or filename in self.indexes: raise ValueError('file {0} already exists in archive'.format(_printable(filename))) self.verbose_print('Adding file {0} to archive... (length = {1} bytes)'.format( _printable(filename), len(contents))) self.files[filename] = contents # Remove a file from archive or internal storage. def remove(self, filename): filename = _unicode(filename) if filename in self.files: self.verbose_print('Removing file {0} from internal storage...'.format(_printable(filename))) del self.files[filename] elif filename in self.indexes: self.verbose_print('Removing file {0} from archive indexes...'.format(_printable(filename))) del self.indexes[filename] else: raise IOError(errno.ENOENT, 'the requested file {0} does not exist in this archive'.format(_printable(filename))) # Load archive. def load(self, filename): filename = _unicode(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): filename = _unicode(filename) 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 # 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. offset = 0 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(codecs.encode('{}{:016x} {:08x}\n'.format(self.RPA3_MAGIC, offset, self.key))) else: archive.write(codecs.encode('{}{:016x}\n'.format(self.RPA2_MAGIC, offset))) # 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='*', action='append', 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='Creative 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 = None output = _unicode(arguments.archive) else: archive = _unicode(arguments.archive) if 'outfile' in arguments and arguments.outfile is not None: output = _unicode(arguments.outfile) else: # Default output directory for extraction is the current directory. if arguments.extract: output = '.' else: output = _unicode(arguments.archive) # Normalize files. if len(arguments.files) > 0 and isinstance(arguments.files[0], list): arguments.files = arguments.files[0] try: archive = RenPyArchive(archive, 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, e), file=sys.stderr) sys.exit(1) if arguments.create or arguments.append: # We need this seperate 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 filename.find('=') != -1: (outfile, filename) = filename.split('=', 2) 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 + os.sep + file + '=' + filename + os.sep + file) else: try: with open(filename, 'rb') as file: archive.add(outfile, file.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(_unicode(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 = arguments.files else: files = 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: if filename.find('=') != -1: (outfile, filename) = filename.split('=', 2) else: outfile = filename try: contents = archive.read(filename) # Create output directory for file if not present. if not os.path.exists(os.path.dirname(os.path.join(output, outfile))): os.makedirs(os.path.dirname(os.path.join(output, outfile))) with open(os.path.join(output, outfile), 'wb') as file: file.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. list = archive.list() list.sort() for file in list: print(file) else: print('No operation given :(') print('Use {0} --help for usage details.'.format(sys.argv[0]))

github.com

Rpa windows extractor Free Download for Windows

10  Max Programming, LLC  361  Shareware

Extract e-mail addresses from all kinds of text sources like your local files.

35  Computer Application Studio  5,382  Shareware

Extract audio from DVDs, Blu-ray discs, video files, and DVD folders easily.

 Newprosoft  307  Shareware

Web Content Extractor allows you to extract data from websites.

5  WebExtractor System  1,230  Shareware

Extracts useful contact information from websites.

177  Jared Breland  29,129  Freeware

A program designed to decompress and extract files from any type of archive.

 RBH Solutions Pvt. Ltd.  Commercial

RPA 2004 is state-of-the-art solution designed for accurate protocol analysis.

1  123HiddenSender com  43  Shareware

The 1st Fax Extractor is a professional targeted fax number and extractor tool.

 SmartLine Inc.  5  Freeware

Do you have a self-extractor archive created by WinZipSelf-Extractor which is when you trying to run....

1  1st-soft.com  2  Shareware

1st Audio Splitter Extractor is an mp3/wav splitter and mp3 extractor in one...

 Web Data Miner  2

Web Link Extractor is a powerful link extractor utility.

2  Alfred Zolo and ZoloPages Inc.  56  Freeware

Is a Name extractor, Address extractor for any web page with deployed data.

1  KnowleSys Software, Inc.  5  Shareware

Web Link Extractor is a powerful link extractor utility. It extracts Link Text and Link URL from t....

 TechnoCom  Shareware

Email Extractor v2.0 is a very fast Email Addresses Extractor tool for Extracting Email Addresses of....

 SharewarePile.com  1  Freeware

SharewarePile Audio Converter Extractor Max is an audio track extractor tool.

 Ahmad Software Technologies  39  Shareware

Cute Web Phone Number Extractor Advance is a telephone number extractor.

softwaretopic.informer.com

unrpa/unrpa at master · Lattyware/unrpa · GitHub

#!/usr/bin/env python3 """ unrpa is a tool to extract files from Ren'Py archives (.rpa). This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. """ 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, continue_on_error=False): self.verbose = verbosity if path: self.path = os.path.abspath(path) else: self.path = os.getcwd() 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: print("{}: {}".format(UnRPA.NAME, message)) def exit(self, message): sys.exit("{}: error: {}".format(UnRPA.NAME, message)) def extract_files(self): self.log(0, "extracting files.") if self.mkdir: self.make_directory_structure(self.path) if not os.path.isdir(self.path): self.exit("path doesn't exist, if you want to create it, use -m.") index = self.get_index() total_files = len(index) for file_number, (path, data) in enumerate(index.items()): try: self.make_directory_structure(os.path.join(self.path, os.path.split(path)[0])) raw_file = self.extract_file(path, data, file_number, total_files) with open(os.path.join(self.path, 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) def extract_file(self, name, data, file_number, total_files): self.log(1, "[{:04.2%}] {:>3}".format(file_number / float(total_files), name)) offset, dlen, start = data[0] with open(self.archive, "rb") as f: f.seek(offset) raw_file = start + f.read(dlen - len(start)) return raw_file def make_directory_structure(self, name): self.log(2, "creating directory structure: {}".format(name)) if not os.path.exists(name): os.makedirs(name) def get_index(self): if not self.version: self.version = self.detect_version() if not self.version: self.exit("file doesn't look like an archive, if you are sure it is, use -f.") with open(self.archive, "rb") as f: offset = 0 if self.version == 2: offset = int(f.readline()[8:], 16) elif self.version == 3: line = f.readline() parts = line.split() offset = int(parts[1], 16) key = int(parts[2], 16) f.seek(offset) index = pickle.loads(zlib.decompress(f.read()), encoding="bytes") if self.version == 3: index = self.deobfuscate_index(index, key) return {self.ensure_str_path(path).replace("/", os.sep): data for path, data in index.items()} def ensure_str_path(self, key): try: return key.decode("utf-8") except AttributeError: return key def detect_version(self): ext = os.path.splitext(self.archive)[1].lower() if ext == ".rpa": with open(self.archive, "rb") as f: line = f.readline() if line.startswith(b"RPA-3.0 "): return 3 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.items()} def deobfuscate_entry(self, key, entry): if len(entry[0]) == 2: 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="no output.") parser.add_option("-l", "--list", action="store_true", dest="list", default=False, 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.") parser.add_option("-m", "--mkdir", action="store_true", dest="mkdir", default=False, 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() if not len(args) == 1: if options.verbose: parser.print_help() parser.error("incorrect number of arguments.") if options.list and options.path: parser.error("option -p: only valid when extracting.") if options.mkdir and not options.path: parser.error("option -m: only valid when --path (-p) is set.") if options.list and options.verbose == 0: parser.error("option -l: can't be silent while listing data.") filename = args[0] extractor = UnRPA(filename, options.verbose, options.path, options.mkdir, options.version, options.continue_on_error) if options.list: extractor.list_files() else: extractor.extract_files()

github.com

decompile.rpy/depack.rpy at master · lolbot-iichan/decompile.rpy · GitHub

decompile.rpy/depack.rpy at master · lolbot-iichan/decompile.rpy · GitHub Permalink
# RenPy archive unpacker 1.1
# Decompiles PRA archives from RenPy runtime.
# Compatible with games using old versions of RenPy 6.x
# =============
# HOW TO USE IT
# =============
# 1. Put this file to your /game/ dir.
# 2. Run the game.
# 3. See /unpacked/ dir.
init 65535 python:
import os
import shutil
import __builtin__
_LB_GAME_DIR = os.path.join(config.basedir, "game")
_LB_OUTPUT_DIR = os.path.join(config.basedir, "unpacked", "game")
if hasattr(renpy,"list_files"):
_LB_list_files = renpy.list_files
else:
# for RenPy before 6.11.0
_LB_list_files = lambda: [fn for dir, fn in renpy.loader.listdirfiles() if dir != renpy.config.commondir]
# for removing invisible "archived" folder
renpy.loader.walkdir = (lambda f: lambda dir: f(dir) if os.path.exists(dir) else [])(renpy.loader.walkdir)
if hasattr(renpy,"file"):
_LB_file = renpy.file
else:
# for RenPy before 6.3.0
_LB_file = renpy.loader.load
for fname in _LB_list_files():
old_path = os.path.join(_LB_GAME_DIR, fname)
new_path = os.path.join(_LB_OUTPUT_DIR, fname)
if not os.path.exists(old_path) and not os.path.exists(new_path):
dirname = os.path.dirname(new_path)
if not os.path.exists(dirname):
os.makedirs(dirname)
new = __builtin__.open(new_path, "wb")
orig = _LB_file(fname)
shutil.copyfileobj(orig, new)
orig.close()
new.close()
Jump to Line You can't perform that action at this time. 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.

github.com


Смотрите также