Some cleaning up, changelog
This commit is contained in:
parent
cee1c05820
commit
81a43952a8
6 changed files with 77 additions and 25 deletions
17
CHANGELOG.md
Normal file
17
CHANGELOG.md
Normal file
|
|
@ -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.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
10
README.md
10
README.md
|
|
@ -7,6 +7,8 @@ extracting, repackaging, and encrypting PS3 ISOs.
|
||||||
|
|
||||||
A hackable, crossplatform, alternative to ISOTools and ISO-Rebuilder.
|
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
|
## How to install
|
||||||
|
|
||||||
1. Clone this repository ```git clone https://notabug.org/necklace/libray```
|
1. Clone this repository ```git clone https://notabug.org/necklace/libray```
|
||||||
|
|
@ -85,15 +87,9 @@ clp = compressed length prefix
|
||||||
|
|
||||||
## 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`
|
||||||
|
- Repackage (unextract) and reencrypt iso?
|
||||||
- Test .irds with version < 9
|
- Test .irds with version < 9
|
||||||
- Custom command to backup all irds available
|
- Custom command to backup all irds available
|
||||||
- pypi
|
- pypi
|
||||||
|
|
||||||
## Advanced
|
|
||||||
|
|
||||||
Figure out the SCSI commands to get data1, if at all possible.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,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 os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import shutil
|
import shutil
|
||||||
|
|
@ -38,12 +39,15 @@ GET_IRD_NET_LOC = 'http://jonnysp.bplaced.net/ird/'
|
||||||
|
|
||||||
# Utility functions
|
# Utility functions
|
||||||
|
|
||||||
def to_int(data, order='big'):
|
|
||||||
|
def to_int(data, byteorder='big'):
|
||||||
|
"""Convert bytes to integer"""
|
||||||
if isinstance(data, bytes):
|
if isinstance(data, bytes):
|
||||||
return int.from_bytes(data, order)
|
return int.from_bytes(data, byteorder)
|
||||||
|
|
||||||
|
|
||||||
def to_bytes(data):
|
def to_bytes(data):
|
||||||
|
"""Convert a string of HEX to bytes"""
|
||||||
if isinstance(data, str):
|
if isinstance(data, str):
|
||||||
return bytes(bytearray.fromhex(data))
|
return bytes(bytearray.fromhex(data))
|
||||||
|
|
||||||
|
|
@ -53,12 +57,13 @@ ISO_IV = to_bytes("69474772af6fdab342743aefaa186287")
|
||||||
|
|
||||||
|
|
||||||
def filesize(filename):
|
def filesize(filename):
|
||||||
|
"""Get size of a file in bytes from os.stat"""
|
||||||
return os.stat(filename).st_size
|
return os.stat(filename).st_size
|
||||||
|
|
||||||
|
|
||||||
def read_seven_bit_encoded_int(fileobj, order):
|
def read_seven_bit_encoded_int(fileobj, order):
|
||||||
# Read out an Int32 7 bits at a time. The high bit
|
"""Read an Int32, 7 bits at a time."""
|
||||||
# of the byte when on means to continue reading more bytes
|
# The highest bit of the byte when on, means to continue reading more bytes.
|
||||||
count = 0
|
count = 0
|
||||||
shift = 0
|
shift = 0
|
||||||
byte = -1
|
byte = -1
|
||||||
|
|
@ -73,15 +78,18 @@ def read_seven_bit_encoded_int(fileobj, order):
|
||||||
|
|
||||||
|
|
||||||
def error(msg):
|
def error(msg):
|
||||||
|
"""Print fatal error message and terminate"""
|
||||||
print('ERROR: %s' % msg)
|
print('ERROR: %s' % msg)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
def warning(msg):
|
def warning(msg):
|
||||||
|
"""Print a warning message"""
|
||||||
print('WARNING: %s. Continuing regardless' % msg)
|
print('WARNING: %s. Continuing regardless' % msg)
|
||||||
|
|
||||||
|
|
||||||
def download_ird(ird_name):
|
def download_ird(ird_name):
|
||||||
|
"""Download an .ird from GET_IRD_NET_LOC"""
|
||||||
ird_link = GET_IRD_NET_LOC + ird_name
|
ird_link = GET_IRD_NET_LOC + ird_name
|
||||||
r = requests.get(ird_link, stream=True)
|
r = requests.get(ird_link, stream=True)
|
||||||
|
|
||||||
|
|
@ -91,6 +99,7 @@ def download_ird(ird_name):
|
||||||
|
|
||||||
|
|
||||||
def ird_by_game_id(game_id):
|
def ird_by_game_id(game_id):
|
||||||
|
"""Using a game_id, download the responding .ird from ALL_IRD_NET_LOC"""
|
||||||
gameid = game_id.replace('-','')
|
gameid = game_id.replace('-','')
|
||||||
r = requests.get(ALL_IRD_NET_LOC, headers = {'User-Agent': 'Anonymous (You)' }, timeout=5)
|
r = requests.get(ALL_IRD_NET_LOC, headers = {'User-Agent': 'Anonymous (You)' }, timeout=5)
|
||||||
soup = BeautifulSoup(r.text, "html.parser")
|
soup = BeautifulSoup(r.text, "html.parser")
|
||||||
|
|
@ -113,6 +122,10 @@ def ird_by_game_id(game_id):
|
||||||
|
|
||||||
|
|
||||||
def decrypt(args):
|
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)
|
input_iso = iso.ISO(args)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import sys
|
||||||
import zlib
|
import zlib
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from libray import core
|
from libray import core
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|
@ -31,7 +32,19 @@ except ImportError:
|
||||||
|
|
||||||
|
|
||||||
class IRD:
|
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'
|
ORDER = 'little'
|
||||||
TEMP_FILE = 'ird'
|
TEMP_FILE = 'ird'
|
||||||
|
|
@ -39,8 +52,9 @@ class IRD:
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, args):
|
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)
|
self.size = core.filesize(self.TEMP_FILE)
|
||||||
with open(self.TEMP_FILE, 'rb') as input_ird:
|
with open(self.TEMP_FILE, 'rb') as input_ird:
|
||||||
|
|
@ -95,18 +109,9 @@ class IRD:
|
||||||
os.remove(self.TEMP_FILE)
|
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):
|
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
|
uncompress = False
|
||||||
with open(filename, 'rb') as input_ird:
|
with open(filename, 'rb') as input_ird:
|
||||||
if input_ird.read(4) != self.MAGIC_STRING:
|
if input_ird.read(4) != self.MAGIC_STRING:
|
||||||
|
|
@ -120,8 +125,10 @@ class IRD:
|
||||||
shutil.copyfile(filename, self.TEMP_FILE)
|
shutil.copyfile(filename, self.TEMP_FILE)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def print_info(self):
|
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('Info from IRD:')
|
||||||
print('Version: %s' % self.version)
|
print('Version: %s' % self.version)
|
||||||
print('Game ID: %s' % self.game_id)
|
print('Game ID: %s' % self.game_id)
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import sys
|
||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
from Crypto.Cipher import AES
|
from Crypto.Cipher import AES
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from libray import core
|
from libray import core
|
||||||
from libray import ird
|
from libray import ird
|
||||||
|
|
@ -32,12 +33,23 @@ except ImportError:
|
||||||
|
|
||||||
|
|
||||||
class ISO:
|
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
|
NUM_INFO_BYTES = 4
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, args):
|
def __init__(self, args):
|
||||||
|
"""ISO constructor using args from argparse"""
|
||||||
|
|
||||||
with open(args.iso, 'rb') as input_iso:
|
with open(args.iso, 'rb') as input_iso:
|
||||||
self.size = core.filesize(args.iso)
|
self.size = core.filesize(args.iso)
|
||||||
self.number_of_regions = core.to_int(input_iso.read(self.NUM_INFO_BYTES))
|
self.number_of_regions = core.to_int(input_iso.read(self.NUM_INFO_BYTES))
|
||||||
|
|
@ -69,6 +81,7 @@ class ISO:
|
||||||
|
|
||||||
|
|
||||||
def decrypt(self, args):
|
def decrypt(self, args):
|
||||||
|
"""Decrypt self using args from argparse"""
|
||||||
|
|
||||||
print('Decrypting with disc key: %s' % self.disc_key.hex())
|
print('Decrypting with disc key: %s' % self.disc_key.hex())
|
||||||
|
|
||||||
|
|
@ -110,7 +123,9 @@ class ISO:
|
||||||
|
|
||||||
pbar.close()
|
pbar.close()
|
||||||
|
|
||||||
|
|
||||||
def read_regions(self, input_iso, filename):
|
def read_regions(self, input_iso, filename):
|
||||||
|
"""List with information (start, end, whether it's encrypted) for every region"""
|
||||||
regions = []
|
regions = []
|
||||||
|
|
||||||
encrypted = False
|
encrypted = False
|
||||||
|
|
@ -128,6 +143,9 @@ class ISO:
|
||||||
|
|
||||||
|
|
||||||
def print_info(self):
|
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('Info from ISO:')
|
||||||
print('Regions: %s (%s)' % (self.number_of_regions, self.number_of_regions*2) )
|
print('Regions: %s (%s)' % (self.number_of_regions, self.number_of_regions*2) )
|
||||||
for i, region in enumerate(self.regions):
|
for i, region in enumerate(self.regions):
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from libray import core
|
from libray import core
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue