Release 0.0.6
This commit is contained in:
parent
5e9bd4f257
commit
f3e8132a37
8 changed files with 300 additions and 35 deletions
|
|
@ -22,6 +22,7 @@
|
|||
import os
|
||||
import sys
|
||||
import stat
|
||||
import zlib
|
||||
import shutil
|
||||
import requests
|
||||
from bs4 import BeautifulSoup
|
||||
|
|
@ -89,13 +90,22 @@ def read_seven_bit_encoded_int(fileobj, order):
|
|||
|
||||
def error(msg):
|
||||
"""Print fatal error message and terminate"""
|
||||
print('ERROR: %s' % msg)
|
||||
print('[ERROR] %s' % msg)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def warning(msg):
|
||||
"""Print a warning message"""
|
||||
print('WARNING: %s. Continuing regardless.' % msg)
|
||||
def warning(msg, args):
|
||||
"""Print a warning message. Warning messages can be silenced with --quiet"""
|
||||
|
||||
if not args.quiet:
|
||||
print('[WARNING] %s. Continuing regardless.' % msg)
|
||||
|
||||
|
||||
def vprint(msg, args):
|
||||
"""Vprint, verbose print, can be silenced with --quiet"""
|
||||
|
||||
if not args.quiet:
|
||||
print('[*] ' + msg)
|
||||
|
||||
|
||||
def download_ird(ird_name):
|
||||
|
|
@ -136,6 +146,22 @@ def ird_by_game_id(game_id):
|
|||
return(ird_name)
|
||||
|
||||
|
||||
def crc32(filename):
|
||||
"""Calculate crc32 for file"""
|
||||
|
||||
with open(filename, 'rb') as infile:
|
||||
|
||||
crc32 = 0
|
||||
|
||||
while True:
|
||||
data = infile.read(65536)
|
||||
if not data:
|
||||
break
|
||||
crc32 = zlib.crc32(data, crc32)
|
||||
|
||||
return "%08X" % (crc32 & 0xFFFFFFFF)
|
||||
|
||||
|
||||
# Main functions
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@
|
|||
|
||||
|
||||
import sys
|
||||
import sqlite3
|
||||
import pkg_resources
|
||||
from tqdm import tqdm
|
||||
from Crypto.Cipher import AES
|
||||
|
||||
|
|
@ -96,35 +98,67 @@ class ISO:
|
|||
|
||||
self.game_id = input_iso.read(16).decode('utf8').strip()
|
||||
|
||||
if args.verbose and not args.quiet:
|
||||
self.print_info()
|
||||
|
||||
cipher = AES.new(core.ISO_SECRET, AES.MODE_CBC, core.ISO_IV)
|
||||
|
||||
if not args.decryption_key:
|
||||
if not args.ird:
|
||||
if not args.quiet:
|
||||
core.warning('No IRD file specified, finding required file')
|
||||
args.ird = core.ird_by_game_id(self.game_id) # Download ird
|
||||
# No key or .ird specified. Let's first check if keys.db is packaged with this release
|
||||
|
||||
self.ird = ird.IRD(args)
|
||||
redump = False
|
||||
|
||||
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))
|
||||
core.vprint('Checking for bundled redump keys', args)
|
||||
|
||||
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 ) )
|
||||
try:
|
||||
db = sqlite3.connect(pkg_resources.resource_filename(__name__, 'data/keys.db'))
|
||||
c = db.cursor()
|
||||
|
||||
self.disc_key = cipher.encrypt(self.ird.data1)
|
||||
core.vprint('Calculating crc32', args)
|
||||
|
||||
crc32 = core.crc32(args.iso)
|
||||
|
||||
keys = c.execute('SELECT * FROM games WHERE crc32=?', [crc32.lower()]).fetchall()
|
||||
|
||||
if keys:
|
||||
|
||||
self.disc_key = keys[0][-1]
|
||||
|
||||
core.vprint('.ISO identified as "%s"' % keys[0][0], args)
|
||||
|
||||
redump = True
|
||||
|
||||
else:
|
||||
raise ValueError
|
||||
|
||||
except:
|
||||
core.vprint('No keys found', args)
|
||||
|
||||
if not redump:
|
||||
|
||||
# Fallback to checking if an .ird exists
|
||||
|
||||
core.warning('No IRD file specified, finding required file', args)
|
||||
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 = core.to_bytes(args.decryption_key)
|
||||
|
||||
if args.verbose and not args.quiet:
|
||||
self.print_info()
|
||||
|
||||
|
||||
def decrypt(self, args):
|
||||
"""Decrypt self using args from argparse."""
|
||||
|
||||
if not args.quiet:
|
||||
print('Decrypting with disc key: %s' % self.disc_key.hex())
|
||||
core.vprint('Decrypting with disc key: %s' % self.disc_key.hex(), args)
|
||||
|
||||
with open(args.iso, 'rb') as input_iso:
|
||||
|
||||
|
|
@ -145,8 +179,8 @@ class ISO:
|
|||
if not region['enc']:
|
||||
while input_iso.tell() < region['end']:
|
||||
data = input_iso.read(core.SECTOR)
|
||||
if not data and not args.quiet:
|
||||
core.warning('Trying to read past the end of the file')
|
||||
if not data:
|
||||
core.warning('Trying to read past the end of the file', args)
|
||||
break
|
||||
output_iso.write(data)
|
||||
|
||||
|
|
@ -163,8 +197,8 @@ class ISO:
|
|||
num >>= 8
|
||||
|
||||
data = input_iso.read(core.SECTOR)
|
||||
if not data and not args.quiet:
|
||||
core.warning('Trying to read past the end of the file')
|
||||
if not data:
|
||||
core.warning('Trying to read past the end of the file', args)
|
||||
break
|
||||
|
||||
cipher = AES.new(self.disc_key, AES.MODE_CBC, bytes(iv))
|
||||
|
|
@ -177,14 +211,14 @@ class ISO:
|
|||
|
||||
if not args.quiet:
|
||||
pbar.close()
|
||||
print('Decryption complete.')
|
||||
|
||||
core.vprint('Decryption complete!', args)
|
||||
|
||||
|
||||
def encrypt(self, args):
|
||||
"""Encrypt self using args from argparse."""
|
||||
|
||||
if not args.quiet:
|
||||
print('Re-encrypting with disc key: %s' % self.disc_key.hex())
|
||||
core.vprint('Re-encrypting with disc key: %s' % self.disc_key.hex(), args)
|
||||
|
||||
with open(args.iso, 'rb') as input_iso:
|
||||
|
||||
|
|
@ -205,8 +239,8 @@ class ISO:
|
|||
if not region['enc']:
|
||||
while input_iso.tell() < region['end']:
|
||||
data = input_iso.read(core.SECTOR)
|
||||
if not data and not args.quiet:
|
||||
core.warning('Trying to read past the end of the file')
|
||||
if not data:
|
||||
core.warning('Trying to read past the end of the file', args)
|
||||
break
|
||||
output_iso.write(data)
|
||||
|
||||
|
|
@ -223,8 +257,8 @@ class ISO:
|
|||
num >>= 8
|
||||
|
||||
data = input_iso.read(core.SECTOR)
|
||||
if not data and not args.quiet:
|
||||
core.warning('Trying to read past the end of the file')
|
||||
if not data:
|
||||
core.warning('Trying to read past the end of the file', args)
|
||||
break
|
||||
|
||||
cipher = AES.new(self.disc_key, AES.MODE_CBC, bytes(iv))
|
||||
|
|
@ -235,6 +269,11 @@ class ISO:
|
|||
if not args.quiet:
|
||||
pbar.update(1)
|
||||
|
||||
if not args.quiet:
|
||||
pbar.close()
|
||||
|
||||
core.vprint('Re-encryption complete!', args)
|
||||
|
||||
|
||||
def print_info(self):
|
||||
# TODO: This could probably have been a __str__? Who cares?
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue