From 81a43952a8a79b4e2ff8356b3729edec0b3ec1e3 Mon Sep 17 00:00:00 2001 From: Nichlas Severinsen Date: Fri, 7 Jun 2019 09:00:03 +0200 Subject: [PATCH] Some cleaning up, changelog --- CHANGELOG.md | 17 +++++++++++++++++ README.md | 10 +++------- libray/core.py | 21 +++++++++++++++++---- libray/ird.py | 33 ++++++++++++++++++++------------- libray/iso.py | 20 +++++++++++++++++++- libray/libray.py | 1 + 6 files changed, 77 insertions(+), 25 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..36fa5d6 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,17 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). + +## [0.0.1] - 2019-05-16 +### Added +- Manually loading .ird files with `-k` (for 'key') +- Automatic downloading of .ird files when not given (currently only from jonnysp.bplaced.net) +- Successful decryption of a PS3 .iso with .ird when path to encrypted .iso is given with `-i` +- Manually specify output .iso file with `-o` (default is 'output.iso') +- Verbosity with `-v` +- Helptext with `-h` (automatically generated by argparse) +- Other relevant things: README, COPYING, etc. + + + diff --git a/README.md b/README.md index e0c0734..b247db4 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,8 @@ extracting, repackaging, and encrypting PS3 ISOs. A hackable, crossplatform, alternative to ISOTools and ISO-Rebuilder. +**Note: this is still a very beta project, report any bug you see!** + ## How to install 1. Clone this repository ```git clone https://notabug.org/necklace/libray``` @@ -85,15 +87,9 @@ clp = compressed length prefix ## Todo -- Automatically download .ird file if not given - Docstrings - Extract ISO (currently doable with `7z x output.iso` +- Repackage (unextract) and reencrypt iso? - Test .irds with version < 9 - Custom command to backup all irds available - pypi - -## Advanced - -Figure out the SCSI commands to get data1, if at all possible. - - diff --git a/libray/core.py b/libray/core.py index df62f4b..9d34a5e 100644 --- a/libray/core.py +++ b/libray/core.py @@ -18,6 +18,7 @@ # You should have received a copy of the GNU General Public License # along with libray. If not, see . + import os import sys import shutil @@ -38,12 +39,15 @@ GET_IRD_NET_LOC = 'http://jonnysp.bplaced.net/ird/' # Utility functions -def to_int(data, order='big'): + +def to_int(data, byteorder='big'): + """Convert bytes to integer""" if isinstance(data, bytes): - return int.from_bytes(data, order) + return int.from_bytes(data, byteorder) def to_bytes(data): + """Convert a string of HEX to bytes""" if isinstance(data, str): return bytes(bytearray.fromhex(data)) @@ -53,12 +57,13 @@ ISO_IV = to_bytes("69474772af6fdab342743aefaa186287") def filesize(filename): + """Get size of a file in bytes from os.stat""" return os.stat(filename).st_size def read_seven_bit_encoded_int(fileobj, order): - # Read out an Int32 7 bits at a time. The high bit - # of the byte when on means to continue reading more bytes + """Read an Int32, 7 bits at a time.""" + # The highest bit of the byte when on, means to continue reading more bytes. count = 0 shift = 0 byte = -1 @@ -73,15 +78,18 @@ def read_seven_bit_encoded_int(fileobj, order): def error(msg): + """Print fatal error message and terminate""" print('ERROR: %s' % msg) sys.exit(1) def warning(msg): + """Print a warning message""" print('WARNING: %s. Continuing regardless' % msg) def download_ird(ird_name): + """Download an .ird from GET_IRD_NET_LOC""" ird_link = GET_IRD_NET_LOC + ird_name r = requests.get(ird_link, stream=True) @@ -91,6 +99,7 @@ def download_ird(ird_name): def ird_by_game_id(game_id): + """Using a game_id, download the responding .ird from ALL_IRD_NET_LOC""" gameid = game_id.replace('-','') r = requests.get(ALL_IRD_NET_LOC, headers = {'User-Agent': 'Anonymous (You)' }, timeout=5) soup = BeautifulSoup(r.text, "html.parser") @@ -113,6 +122,10 @@ def ird_by_game_id(game_id): def decrypt(args): + """Try to decrypt a given .iso using relevant .ird using args from argparse + + If no .ird is given this will try to automatically download an .ird file with the encryption/decryption key for the given game .iso + """ input_iso = iso.ISO(args) diff --git a/libray/ird.py b/libray/ird.py index 028edea..c14226d 100644 --- a/libray/ird.py +++ b/libray/ird.py @@ -24,6 +24,7 @@ import sys import zlib import shutil + try: from libray import core except ImportError: @@ -31,7 +32,19 @@ except ImportError: class IRD: + """Class for handling .ird files + Attributes: + version: IRD version number + game_id: PS3 game identifier + game_name: Name of PS3 game + update_version: PS3 firmware update version + game_version: PS3 game version + app_version: PS3 app version + region_count: How many encrypted regions are in the .iso + file_count: How many files are supposed to be in the .iso + data1: Encryption key + """ ORDER = 'little' TEMP_FILE = 'ird' @@ -39,8 +52,9 @@ class IRD: def __init__(self, args): + """IRD constructor using args from argparse""" - self.uncompress(args.ird) # TODO: Try/Except + self.uncompress(args.ird) # TODO: Try/Except? self.size = core.filesize(self.TEMP_FILE) with open(self.TEMP_FILE, 'rb') as input_ird: @@ -95,18 +109,9 @@ class IRD: os.remove(self.TEMP_FILE) - def get_if_exists(self, input_ird): - starting_address = input_ird.tell() - length = core.read_seven_bit_encoded_int(input_ird, self.ORDER) - print(length) - if length: - return input_ird.read(length) - - input_ird.seek(starting_address) - return None - - def uncompress(self, filename): + """Uncompress IRD. Assumes given .ird file is not compressed, but then tries to decompress it with zlib/gzfile if it was not uncompressed""" + uncompress = False with open(filename, 'rb') as input_ird: if input_ird.read(4) != self.MAGIC_STRING: @@ -120,8 +125,10 @@ class IRD: shutil.copyfile(filename, self.TEMP_FILE) - def print_info(self): + # TODO: This could probably have been a __str__? Who cares? + """Print some info about the IRD""" + print('Info from IRD:') print('Version: %s' % self.version) print('Game ID: %s' % self.game_id) diff --git a/libray/iso.py b/libray/iso.py index d0c18e6..630aab4 100644 --- a/libray/iso.py +++ b/libray/iso.py @@ -23,6 +23,7 @@ import sys from tqdm import tqdm from Crypto.Cipher import AES + try: from libray import core from libray import ird @@ -32,12 +33,23 @@ except ImportError: class ISO: - + """Class for handling PS3 .iso files + + Attributes: + size: Size of .iso in bytes + number_of_regions: Number of regions in the .iso + regions: List with info of every region + game_id: PS3 game id + ird: IRD object (see ird.py) + disc_key: data1 from .ird, encrypted + """ NUM_INFO_BYTES = 4 def __init__(self, args): + """ISO constructor using args from argparse""" + with open(args.iso, 'rb') as input_iso: self.size = core.filesize(args.iso) self.number_of_regions = core.to_int(input_iso.read(self.NUM_INFO_BYTES)) @@ -69,6 +81,7 @@ class ISO: def decrypt(self, args): + """Decrypt self using args from argparse""" print('Decrypting with disc key: %s' % self.disc_key.hex()) @@ -110,7 +123,9 @@ class ISO: pbar.close() + def read_regions(self, input_iso, filename): + """List with information (start, end, whether it's encrypted) for every region""" regions = [] encrypted = False @@ -128,6 +143,9 @@ class ISO: def print_info(self): + # TODO: This could probably have been a __str__? Who cares? + """Print some info about the ISO""" + print('Info from ISO:') print('Regions: %s (%s)' % (self.number_of_regions, self.number_of_regions*2) ) for i, region in enumerate(self.regions): diff --git a/libray/libray.py b/libray/libray.py index 21caeda..bf4ecf8 100755 --- a/libray/libray.py +++ b/libray/libray.py @@ -22,6 +22,7 @@ import argparse + try: from libray import core except ImportError: