diff --git a/.gitignore b/.gitignore index a04272c..258a3b4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ *.iso *.ird *.gz +ird/ # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/README.md b/README.md index 0fe6d4a..6cf811e 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,6 @@ extracting, repackaging, and encrypting PS3 ISOs. A hackable, crossplatform, alternative to ISOTools and ISO-Rebuilder. - [see also](http://www.psdevwiki.com/ps3/Bluray_disc#Encryption) ([archive.fo](https://archive.fo/hN1E6)) [7bit encoded int / RLE / CLP](https://github.com/Microsoft/referencesource/blob/master/mscorlib/system/io/binaryreader.cs#L582-L600) @@ -17,16 +16,8 @@ A hackable, crossplatform, alternative to ISOTools and ISO-Rebuilder. clp = compressed length prefix -## Todo +## Possibly todo -- Automatically download .ird file if not given - Docstrings - Extract ISO (currently doable with `7z x output.iso` -- Test .irds with version < 9 -- Custom command to backup all irds available - -## Advanced - -Figure out the SCSI commands to get data1, if at all possible. - diff --git a/libray/core.py b/libray/core.py index 6067943..14f38ae 100644 --- a/libray/core.py +++ b/libray/core.py @@ -21,8 +21,6 @@ import os import sys import shutil -import requests -from bs4 import BeautifulSoup try: @@ -33,8 +31,6 @@ except ImportError: # Magic numbers / Constant variables SECTOR = 2048 -ALL_IRD_NET_LOC = 'http://jonnysp.bplaced.net/data.php' -GET_IRD_NET_LOC = 'http://jonnysp.bplaced.net/ird/' # Utility functions @@ -80,35 +76,6 @@ def error(msg): def warning(msg): print('WARNING: %s. Continuing regardless' % msg) - -def download_ird(ird_name): - ird_link = GET_IRD_NET_LOC + ird_name - r = requests.get(ird_link, stream=True) - - with open(ird_name, 'wb') as ird_file: - r.raw.decode_content = True - shutil.copyfileobj(r.raw, ird_file) - - -def ird_by_game_id(game_id): - gameid = game_id.replace('-','') - r = requests.get(ALL_IRD_NET_LOC, headers = {'User-Agent': 'Anonymous (You)' }, timeout=5) - soup = BeautifulSoup(r.text, "html.parser") - - ird_name = False - for elem in soup.find_all("a"): - url = elem.get('href').split('/')[-1].replace('\\"','') - if gameid in url: - ird_name = url - - if not ird_name: - error("Unable to download IRD, couldn't find link") - - download_ird(ird_name) - - return(ird_name) - - # Main functions diff --git a/libray/ird.py b/libray/ird.py deleted file mode 100644 index e357222..0000000 --- a/libray/ird.py +++ /dev/null @@ -1,137 +0,0 @@ -# -*- coding: utf8 -*- - -# libray - Libre Blu-Ray PS3 ISO Tool -# Copyright (C) 2018 Nichlas Severinsen -# -# This file is part of libray. -# -# libray 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. -# -# libray 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 libray. If not, see . - - -import os -import sys -import zlib -import shutil - -try: - from libray import core -except ImportError: - import core - - -class IRD: - - - ORDER = 'little' - TEMP_FILE = 'ird' - MAGIC_STRING = b"3IRD" - - - def __init__(self, args): - - self.uncompress(args.ird) # TODO: Try/Except - - self.size = core.filesize(self.TEMP_FILE) - with open(self.TEMP_FILE, 'rb') as input_ird: - if input_ird.read(4) != self.MAGIC_STRING: - core.error("Either not an IRD file, corruped IRD file, or unknown IRD format") - - self.version = core.to_int(input_ird.read(1), self.ORDER) - self.game_id = input_ird.read(9) - name_length = core.read_seven_bit_encoded_int(input_ird, self.ORDER) - self.game_name = input_ird.read(name_length).decode('utf8') - self.update_version = input_ird.read(4) - self.game_version = input_ird.read(5) - self.app_version = input_ird.read(5) - - if self.version == 7: - self.identifier = input_ird.read(4) - - header_length = (core.to_int(input_ird.read(4), self.ORDER)) - self.header = input_ird.read(header_length) - footer_length = (core.to_int(input_ird.read(4), self.ORDER)) - self.footer = input_ird.read(footer_length) - - self.region_count = core.to_int(input_ird.read(1), self.ORDER) - self.region_hashes = [] - for i in range(0, self.region_count): - self.region_hashes.append(input_ird.read(16)) - - self.file_count = core.to_int(input_ird.read(4), self.ORDER) - self.file_hashes = [] - for i in range(0, self.file_count): - key = core.to_int(input_ird.read(8), self.ORDER) - val = input_ird.read(16) - self.file_hashes.append({'key': key, 'val': val}) - - if self.version >= 9: - self.pic = input_ird.read(115) - - unused_bytes = input_ird.read(4) # Yeah, I don't know either. - - self.data1 = input_ird.read(16) - self.data2 = input_ird.read(16) - - if self.version < 9: - self.pic = input_ird.read(115) - - if self.version < 7: - self.uid = core.to_int(input_ird.read(4), self.ORDER) - - if args.verbose: - self.print_info() - - 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 = False - with open(filename, 'rb') as input_ird: - if input_ird.read(4) != self.MAGIC_STRING: - uncompress = True - - if uncompress: - with open(filename, 'rb') as gzfile: - with open(self.TEMP_FILE, 'wb') as tmpfile: - tmpfile.write(zlib.decompress(gzfile.read(), zlib.MAX_WBITS|16)) - else: - shutil.copyfile(filename, self.TEMP_FILE) - - - - def print_info(self): - print('Info from IRD:') - print('Version: %s' % self.version) - print('Game ID: %s' % self.game_id) - print('Game Name: %s' % self.game_name) - print('Update Version: %s' % self.update_version) - print('Game Version: %s' % self.game_version) - print('App Version: %s' % self.app_version) - print('Region Count: %s' % self.region_count) - print('File Count: %s' % self.file_count) - print('Data1: %s' % self.data1.hex()) - print('Data2: %s' % self.data2.hex()) - - diff --git a/libray/iso.py b/libray/iso.py index 29f3837..6bcb01c 100644 --- a/libray/iso.py +++ b/libray/iso.py @@ -25,10 +25,8 @@ from Crypto.Cipher import AES try: from libray import core - from libray import ird except ImportError: import core - import ird class ISO: @@ -45,6 +43,10 @@ class ISO: self.regions = self.read_regions(input_iso, args.iso) + input_iso.seek(3968) + self.data1 = input_iso.read(16) + + input_iso.seek(core.SECTOR) playstation = input_iso.read(16) self.game_id = input_iso.read(16).decode('utf8').strip() @@ -52,20 +54,11 @@ class ISO: if args.verbose: self.print_info() - if not args.ird: - core.warning('No IRD file specified, downloading required file') - args.ird = core.ird_by_game_id(self.game_id) # Download ird - - self.ird = ird.IRD(args) - - if self.ird.region_count != len(self.regions)-1: - core.error('Corrupt ISO. Expected %s regions, found %s regions' % (self.ird.region_count, len(self.regions)-1)) - if self.regions[-1]['start'] > self.size: core.error('Corrupt ISO. Expected filesize larger than %.2f GiB, actual size is %.2f GiB' % (self.regions[-1]['start'] / 1024**3, self.size / 1024**3 ) ) cipher = AES.new(core.ISO_SECRET, AES.MODE_CBC, core.ISO_IV) - self.disc_key = cipher.encrypt(self.ird.data1) + self.disc_key = cipher.encrypt(self.data1) def decrypt(self, args): diff --git a/libray/libray.py b/libray/libray.py index 89cc4cb..5665ae3 100755 --- a/libray/libray.py +++ b/libray/libray.py @@ -19,7 +19,7 @@ # You should have received a copy of the GNU General Public License # along with libray. If not, see . - +import sys import argparse try: @@ -34,11 +34,13 @@ if __name__ == '__main__': parser = argparse.ArgumentParser(description='A Libre (FLOSS) Python application for unencrypting, extracting, repackaging, and encrypting PS3 ISOs') parser.add_argument('-v', '--verbose', help="Increase verbosity", action='count') parser.add_argument('-o', '--output', dest='output', type=str, help="Output filename", default='output.iso') - parser.add_argument('-k', '--ird', dest='ird', type=str, help="Path to .ird file", default="") required = parser.add_argument_group('required arguments') required.add_argument('-i', '--iso', dest='iso', type=str, help="Path to .iso file", required=True) args = parser.parse_args() - core.decrypt(args) + if args.iso: + core.decrypt(args) + sys.exit() + print("Not enough arguments given. See --help") \ No newline at end of file