Revert "Rewrote to not use .ird, turn's out they're not needed"

This reverts commit f8b35fbe83.
This commit is contained in:
Nichlas Severinsen 2018-07-09 07:40:41 +02:00
parent f8b35fbe83
commit e45367a875
6 changed files with 195 additions and 12 deletions

1
.gitignore vendored
View file

@ -1,7 +1,6 @@
*.iso *.iso
*.ird *.ird
*.gz *.gz
ird/
# Byte-compiled / optimized / DLL files # Byte-compiled / optimized / DLL files
__pycache__/ __pycache__/

View file

@ -9,6 +9,7 @@ extracting, repackaging, and encrypting PS3 ISOs.
A hackable, crossplatform, alternative to ISOTools and ISO-Rebuilder. 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)) [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) [7bit encoded int / RLE / CLP](https://github.com/Microsoft/referencesource/blob/master/mscorlib/system/io/binaryreader.cs#L582-L600)
@ -16,8 +17,16 @@ A hackable, crossplatform, alternative to ISOTools and ISO-Rebuilder.
clp = compressed length prefix clp = compressed length prefix
## Possibly todo ## Todo
- Automatically download .ird file if not given
- Docstrings - Docstrings
- Extract ISO (currently doable with `7z x output.iso` - 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.

View file

@ -21,6 +21,8 @@
import os import os
import sys import sys
import shutil import shutil
import requests
from bs4 import BeautifulSoup
try: try:
@ -31,6 +33,8 @@ except ImportError:
# Magic numbers / Constant variables # Magic numbers / Constant variables
SECTOR = 2048 SECTOR = 2048
ALL_IRD_NET_LOC = 'http://jonnysp.bplaced.net/data.php'
GET_IRD_NET_LOC = 'http://jonnysp.bplaced.net/ird/'
# Utility functions # Utility functions
@ -76,6 +80,35 @@ def error(msg):
def warning(msg): def warning(msg):
print('WARNING: %s. Continuing regardless' % 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 # Main functions

137
libray/ird.py Normal file
View file

@ -0,0 +1,137 @@
# -*- 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 <https://www.gnu.org/licenses/>.
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())

View file

@ -25,8 +25,10 @@ from Crypto.Cipher import AES
try: try:
from libray import core from libray import core
from libray import ird
except ImportError: except ImportError:
import core import core
import ird
class ISO: class ISO:
@ -43,10 +45,6 @@ class ISO:
self.regions = self.read_regions(input_iso, args.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) input_iso.seek(core.SECTOR)
playstation = input_iso.read(16) playstation = input_iso.read(16)
self.game_id = input_iso.read(16).decode('utf8').strip() self.game_id = input_iso.read(16).decode('utf8').strip()
@ -54,11 +52,20 @@ class ISO:
if args.verbose: if args.verbose:
self.print_info() 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: 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 ) ) 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) cipher = AES.new(core.ISO_SECRET, AES.MODE_CBC, core.ISO_IV)
self.disc_key = cipher.encrypt(self.data1) self.disc_key = cipher.encrypt(self.ird.data1)
def decrypt(self, args): def decrypt(self, args):

View file

@ -19,7 +19,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with libray. If not, see <https://www.gnu.org/licenses/>. # along with libray. If not, see <https://www.gnu.org/licenses/>.
import sys
import argparse import argparse
try: try:
@ -34,13 +34,11 @@ if __name__ == '__main__':
parser = argparse.ArgumentParser(description='A Libre (FLOSS) Python application for unencrypting, extracting, repackaging, and encrypting PS3 ISOs') 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('-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('-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 = parser.add_argument_group('required arguments')
required.add_argument('-i', '--iso', dest='iso', type=str, help="Path to .iso file", required=True) required.add_argument('-i', '--iso', dest='iso', type=str, help="Path to .iso file", required=True)
args = parser.parse_args() args = parser.parse_args()
if args.iso: core.decrypt(args)
core.decrypt(args)
sys.exit()
print("Not enough arguments given. See --help")