Add -d and -q
This commit is contained in:
parent
d229015710
commit
0fe0b1d05a
6 changed files with 96 additions and 68 deletions
127
libray/iso.py
127
libray/iso.py
|
|
@ -33,22 +33,49 @@ except ImportError:
|
|||
|
||||
|
||||
class ISO:
|
||||
"""Class for handling PS3 .iso files
|
||||
"""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
|
||||
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 read_regions(self, input_iso, filename):
|
||||
"""List with info dict (start, end, whether it's encrypted) for every region.
|
||||
|
||||
Basically, every other (odd numbered) region is encrypted.
|
||||
"""
|
||||
regions = []
|
||||
|
||||
encrypted = False
|
||||
for _ in range(0, self.number_of_regions):
|
||||
|
||||
regions.append({
|
||||
'start': core.to_int(input_iso.read(self.NUM_INFO_BYTES)) * core.SECTOR,
|
||||
'end': core.to_int(input_iso.read(self.NUM_INFO_BYTES)) * core.SECTOR,
|
||||
'enc': encrypted
|
||||
})
|
||||
|
||||
input_iso.seek(input_iso.tell() - self.NUM_INFO_BYTES)
|
||||
|
||||
encrypted = not encrypted
|
||||
|
||||
# Last region might not actually be 2048 bytes, so we'll just cheat
|
||||
regions[-1]['end'] = self.size
|
||||
|
||||
return regions
|
||||
|
||||
|
||||
def __init__(self, args):
|
||||
"""ISO constructor using args from argparse"""
|
||||
"""ISO constructor using args from argparse."""
|
||||
|
||||
self.size = core.size(args.iso)
|
||||
|
||||
|
|
@ -56,38 +83,48 @@ class ISO:
|
|||
core.error('looks like ISO file/mount is empty?')
|
||||
|
||||
with open(args.iso, 'rb') as input_iso:
|
||||
self.number_of_regions = core.to_int(input_iso.read(self.NUM_INFO_BYTES))
|
||||
unused_bytes = input_iso.read(self.NUM_INFO_BYTES) # Yeah, I don't know either.
|
||||
# Get number of regions (times two as the number represents both encrypted and decrypted regions )
|
||||
self.number_of_regions = core.to_int(input_iso.read(self.NUM_INFO_BYTES)) * 2
|
||||
|
||||
# Skip unused bytes
|
||||
input_iso.seek(input_iso.tell() + self.NUM_INFO_BYTES)
|
||||
|
||||
self.regions = self.read_regions(input_iso, args.iso)
|
||||
|
||||
input_iso.seek(core.SECTOR)
|
||||
playstation = input_iso.read(16)
|
||||
# Seek to the start of region 2, '+ 16' skips a section containing some 'playstation'
|
||||
input_iso.seek(core.SECTOR + 16)
|
||||
|
||||
self.game_id = input_iso.read(16).decode('utf8').strip()
|
||||
|
||||
if args.verbose:
|
||||
if args.verbose and not args.quiet:
|
||||
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)
|
||||
|
||||
if not args.decryption_key:
|
||||
if not args.ird:
|
||||
if not args.quiet:
|
||||
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 or error in IRD. 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 or error in IRD. Expected filesize larger than %.2f GiB, actual size is %.2f GiB' % (self.regions[-1]['start'] / 1024**3, self.size / 1024**3 ) )
|
||||
|
||||
self.disc_key = cipher.encrypt(self.ird.data1)
|
||||
else:
|
||||
self.disc_key = cipher.encrypt(core.to_bytes(args.decryption_key))
|
||||
|
||||
|
||||
def decrypt(self, args):
|
||||
"""Decrypt self using args from argparse"""
|
||||
"""Decrypt self using args from argparse."""
|
||||
|
||||
print('Decrypting with disc key: %s' % self.disc_key.hex())
|
||||
if not args.quiet:
|
||||
print('Decrypting with disc key: %s' % self.disc_key.hex())
|
||||
|
||||
with open(args.iso, 'rb') as input_iso:
|
||||
|
||||
|
|
@ -100,18 +137,19 @@ class ISO:
|
|||
|
||||
pbar = tqdm(total= (self.size // 2048) - 4 )
|
||||
|
||||
for i, region in enumerate(self.regions):
|
||||
for region in self.regions:
|
||||
input_iso.seek(region['start'])
|
||||
|
||||
# Unencrypted region, just copy it
|
||||
if not region['enc']:
|
||||
while input_iso.tell() < region['end']:
|
||||
data = input_iso.read(core.SECTOR)
|
||||
if not data:
|
||||
if not data and not args.quiet:
|
||||
core.warning('Trying to read past the end of the file')
|
||||
break
|
||||
pbar.update(1)
|
||||
output_iso.write(data)
|
||||
continue
|
||||
# Encrypted region, decrypt then write
|
||||
else:
|
||||
while input_iso.tell() < region['end']:
|
||||
num = input_iso.tell() // 2048
|
||||
|
|
@ -121,43 +159,26 @@ class ISO:
|
|||
num >>= 8
|
||||
|
||||
data = input_iso.read(core.SECTOR)
|
||||
if not data:
|
||||
if not data and not args.quiet:
|
||||
core.warning('Trying to read past the end of the file')
|
||||
break
|
||||
pbar.update(1)
|
||||
|
||||
cipher = AES.new(self.disc_key, AES.MODE_CBC, bytes(iv))
|
||||
decrypted = cipher.decrypt(data)
|
||||
|
||||
output_iso.write(decrypted)
|
||||
|
||||
pbar.close()
|
||||
pbar.update(1)
|
||||
|
||||
|
||||
def read_regions(self, input_iso, filename):
|
||||
"""List with information (start, end, whether it's encrypted) for every region"""
|
||||
regions = []
|
||||
|
||||
encrypted = False
|
||||
for i in range(0, self.number_of_regions*2):
|
||||
regions.append({
|
||||
'start': core.to_int(input_iso.read(self.NUM_INFO_BYTES))*core.SECTOR,
|
||||
'end': core.to_int(input_iso.read(self.NUM_INFO_BYTES))*core.SECTOR,
|
||||
'enc': encrypted
|
||||
})
|
||||
input_iso.seek(input_iso.tell() - self.NUM_INFO_BYTES)
|
||||
encrypted = not encrypted
|
||||
regions[-1]['end'] = self.size
|
||||
|
||||
return regions
|
||||
pbar.close()
|
||||
|
||||
|
||||
def print_info(self):
|
||||
# TODO: This could probably have been a __str__? Who cares?
|
||||
"""Print some info about the ISO"""
|
||||
"""Print some info about the ISO."""
|
||||
|
||||
print('Info from ISO:')
|
||||
print('Regions: %s (%s)' % (self.number_of_regions, self.number_of_regions*2) )
|
||||
print('Regions: %s' % self.number_of_regions)
|
||||
for i, region in enumerate(self.regions):
|
||||
print(i, region)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue