diff --git a/.gitignore b/.gitignore
index 37bf246..33a1a67 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,19 +1,14 @@
-# Custom
*.iso
*.ird
-*.txt
-ird
-region_*
-*.bin
-*.BIN
-*.ini
-*.INI
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
+# C extensions
+*.so
+
# Distribution / packaging
.Python
build/
@@ -33,7 +28,80 @@ wheels/
*.egg
MANIFEST
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+.hypothesis/
+.pytest_cache/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# pyenv
+.python-version
+
+# celery beat schedule file
+celerybeat-schedule
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
\ No newline at end of file
diff --git a/LibRay-PS3/core.py b/LibRay-PS3/core.py
deleted file mode 100644
index 18a0fc1..0000000
--- a/LibRay-PS3/core.py
+++ /dev/null
@@ -1,139 +0,0 @@
-# -*- coding: utf8 -*-
-
-# LibRay-PS3 - Libre Blu-Ray PS3 ISO Tool
-# Copyright (C) 2018 Nichlas Severinsen
-#
-# This program 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.
-#
-# This program 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 this program. If not, see .
-
-import os
-import sys
-import gzip
-
-ORDER = 'big'
-IRD_ORDER = 'little'
-SECTOR = 2048
-
-def bytes_to_int(byte):
- return int.from_bytes(byte, ORDER)
-
-def ird_bytes_to_int(byte):
- return int.from_bytes(byte, IRD_ORDER)
-
-def read_seven_bit_encoded_int(fileobj):
- # Read out an Int32 7 bits at a time. The high bit
- # of the byte when on means to continue reading more bytes
- count = 0
- shift = 0
- byte = -1
- while (byte & 0x80) != 0 or byte == -1:
- # Check for a corrupted stream. Read a max of 5 bytes.
- if shift == (5 * 7):
- raise ValueError
- byte = ird_bytes_to_int(fileobj.read(1))
- count |= (byte & 0x7F) << shift
- shift += 7
- print(byte, count, shift)
- return count
-
-
-class IRD:
-
- def is_compressed(self, fileobj):
- fileobj.seek(0)
- return fileobj.read(4) != b"3IRD"
-
- def uncompress(self, filename):
- with gzip.open(filename, 'rb') as gzfile:
- with open('ird', 'wb') as outfile:
- outfile.write(gzfile.read())
-
- def __init__(self, filename):
-
- with open(filename, 'rb') as fileobj:
- if self.is_compressed(fileobj):
- self.uncompress(filename)
-
- self.size = os.stat('ird').st_size
- with open('ird', 'rb') as ird:
- self.magic_string = ird.read(4)
- self.version = ird_bytes_to_int(ird.read(1))
- self.game_id = ird.read(9)
- length = read_seven_bit_encoded_int(ird)
- self.game_name = ird.read(length)
- self.update_version = ird.read(4)
- self.game_version = ird.read(5)
- self.app_version = ird.read(5)
- if self.version == 7:
- self.identifier = ird.read(4)
- if self.version < 9:
- back = ird.tell()
- length = read_seven_bit_encoded_int(ird)
- if length:
- self.header = ird.read(length)
- else:
- ird.seek(back)
- back = ird.tell()
- length = read_seven_bit_encoded_int(ird)
- if length:
- self.footer = ird.read(length)
- else:
- ird.seek(back)
- self.region_count = ird_bytes_to_int(ird.read(1))
- self.region_hash = []
- for i in range(0, self.region_count):
- self.region_hash.append(ird.read(16))
- self.file_count = ird_bytes_to_int(ird.read(4))
- print(vars(self) )
-
-
- return
- sys.exit()
- back = ird.tell()
- prefix = bytes_to_int(ird.read(1))
- length = prefix >> 1
- if prefix & 0b00000001:
- ird.seek(back)
-
-
-
-
- self.header = ird.read(length)
- back = ird.tell()
- prefix = bytes_to_int(ird.read(1))
- length = prefix >> 1
- ird.seek(back)
- self.footer = ird.read(length)
- self.region_count
-
- print(self.game_name, self.update_version, self.game_version, self.region_count)
-
- ird.seek(self.size - (2 + 115 + 16 + 16))
- if self.version >= 9:
- self.pic = ird.read(115)
- self.data_one = ird.read(16)
- print(self.data_one.hex())
- self.data_two = ird.read(16)
- if self.version < 9:
- self.pic = ird.read(115)
- self.uid = ird.read(2)
-
- print(self.pic)
- print(self.uid)
- print(self.game_id)
-
- if filename != 'ird':
- os.remove('ird')
-
-
-
diff --git a/LibRay-PS3/libray-ps3.py b/LibRay-PS3/libray-ps3.py
deleted file mode 100644
index 53bf52a..0000000
--- a/LibRay-PS3/libray-ps3.py
+++ /dev/null
@@ -1,141 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf8 -*-
-
-# LibRay-PS3 - Libre Blu-Ray PS3 ISO Tool
-# Copyright (C) 2018 Nichlas Severinsen
-#
-# This program 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.
-#
-# This program 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 this program. If not, see .
-
-
-import os
-import sys
-import core
-import base64
-import struct
-import shutil
-import binascii
-from Crypto.Cipher import AES
-
-def bytes_to_int(byte):
- return int.from_bytes(byte, core.ORDER)
-
-def int_to_hexstr(integer):
- return '{:02x}'.format(integer)
-
-def int_to_bytes(integer):
- return bytes(bytearray.fromhex(int_to_hexstr(integer)))
-
-def hexstr_to_bytes(hexstr):
- return bytes(bytearray.fromhex(hexstr))
-
-def bprint(byte):
- byteint = bytes_to_int(byte)
- print(byte, '\t->', byteint, '\t->', byteint*8, '\t->', byteint*2048 )
-
-def decode(self, text):
- '''
- Remove the PKCS#7 padding from a text string
- '''
- nl = len(text)
- val = int(binascii.hexlify(text[-1]), 16)
- if val > self.k:
- raise ValueError('Input is not padded or padding is corrupt')
-
- l = nl - val
- return text[:l]
-
-
-BS = 32
-pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
-unpad = lambda s : s[:-ord(s[len(s)-1:])]
-
-if __name__ == '__main__':
-
- #core.IRD('BCAS20001-CA107E13820801F29488EBEB7D82A2C4.ird')
- #core.IRD('BLES00048-1AA29AD85F7770BDCED0B7030067D59A.ird')
- #sys.exit()
-
- bprint(b'\x00\x00\x00\x00')
- bprint(b'\x00\x00\x0c\xbf')
- bprint(b'\x00\x00\x00q\xc2')
- bprint(b'\x00\x00s\xc2\x7f')
- bprint(b'\x00\x00s\xc2\x80')
-
- data = hexstr_to_bytes("11089487d46ec9c1ec71205c2a6e8adc") # bles00048
- #data = hexstr_to_bytes("18c871628e0c3bbbd20b8a4cfb40b750") # bles000681
- key = hexstr_to_bytes("380bcf0b53455b3c7817ab4fa3ba90ed")
- iv = hexstr_to_bytes("69474772af6fdab342743aefaa186287")
-
- cipher = AES.new(key, AES.MODE_CBC, iv)
- disc_key = cipher.encrypt(data)
- #print(disc_key)
- #print(disc_key.hex())
- #disc_key = hexstr_to_bytes("01AD4F9DFED22E37998BDDC57E135935")
- #print(disc_key.hex())
- #print(unpad(disc_key.hex()))
- #disc_key = hexstr_to_bytes("DCD55A55B033905C58E7FE2A7F969F27")
-
- regions = [
- {'start': 0, 'end': 6682624, 'enc': False},
- {'start': 6682624, 'end': 59641856, 'enc': True},
- {'start': 59641856, 'end': 15537010688, 'enc': False},
- {'start': 15537010688, 'end': 15537012736, 'enc': True }
- # There's also a last sector between 15537010688 and 15537012736, but seems like it's not used
- ]
-
- files = []
- test = hexstr_to_bytes("533570a1")
- with open(sys.argv[1], 'rb') as iso:
- for i, region in enumerate(regions):
- files.append('region_' + str(i))
- with open('region_' + str(i), 'wb') as output:
- iso.seek(region['start'])
-
- if not region['enc']:
- while iso.tell() < region['end']:
- data = iso.read(core.SECTOR)
- output.write(data)
- continue
- else:
- while iso.tell() < region['end']:
- num = iso.tell() // 2048
- backupnum = num
- iv = bytearray([0 for i in range(0,16)])
- for j in range(0,16):
- iv[16 - j - 1] = (num & 0xFF)
- num >>= 8
-
- data = iso.read(core.SECTOR)
-
- cipher = AES.new(disc_key, AES.MODE_CBC, bytes(iv))
- decrypted = cipher.decrypt(data)
-
- if test in decrypted:
- print('nyees')
- print(backupnum)
- print(iv.hex())
- print(data.hex())
- print(decrypted.hex())
-
- output.write(decrypted)
- print(iso.tell())
-
-
- with open('output.iso', 'wb') as iso:
- for f in files:
- with open(f, 'rb') as fd:
- shutil.copyfileobj(fd, iso, 1024*1024*10)
-
- sys.exit()
-
\ No newline at end of file
diff --git a/README.md b/README.md
index efd3ad5..0fe6d4a 100644
--- a/README.md
+++ b/README.md
@@ -1,16 +1,32 @@
-# LibRay-PS3
+# LibRay
LibRay: A portmanteau of Libre and Blu-Ray
-LibRay-PS3 aims to be a Libre (FLOSS) Python application for unencrypting,
+LibRay aims to be a Libre (FLOSS) Python application for unencrypting,
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)
clp = compressed length prefix
+
+
+## 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/__init__.py b/libray/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/libray/core.py b/libray/core.py
new file mode 100644
index 0000000..4e08801
--- /dev/null
+++ b/libray/core.py
@@ -0,0 +1,82 @@
+# -*- 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
+
+try:
+ from libray import iso
+except ImportError:
+ import iso
+
+# Magic numbers / Constant variables
+
+SECTOR = 2048
+
+# Utility functions
+
+def to_int(data, order='big'):
+ if isinstance(data, bytes):
+ return int.from_bytes(data, order)
+
+
+def to_bytes(data):
+ if isinstance(data, str):
+ return bytes(bytearray.fromhex(data))
+
+
+ISO_SECRET = to_bytes("380bcf0b53455b3c7817ab4fa3ba90ed")
+ISO_IV = to_bytes("69474772af6fdab342743aefaa186287")
+
+
+def filesize(filename):
+ return os.stat(filename).st_size
+
+
+def read_seven_bit_encoded_int(fileobj, order):
+ # Read out an Int32 7 bits at a time. The high bit
+ # of the byte when on means to continue reading more bytes
+ count = 0
+ shift = 0
+ byte = -1
+ while (byte & 0x80) != 0 or byte == -1:
+ # Check for a corrupted stream. Read a max of 5 bytes.
+ if shift == (5 * 7):
+ raise ValueError
+ byte = to_int(fileobj.read(1), order)
+ count |= (byte & 0x7F) << shift
+ shift += 7
+ return count
+
+
+def error(msg):
+ print('ERROR: %s' % msg)
+ sys.exit(1)
+
+# Main functions
+
+def decrypt(args):
+
+ input_iso = iso.ISO(args)
+
+ input_iso.decrypt(args)
+
+
+
diff --git a/libray/ird.py b/libray/ird.py
new file mode 100644
index 0000000..b64103c
--- /dev/null
+++ b/libray/ird.py
@@ -0,0 +1,136 @@
+# -*- 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 gzip
+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 gzip.open(filename, 'rb') as gzfile:
+ with open(self.TEMP_FILE, 'wb') as tmpfile:
+ tmpfile.write(gzfile.read())
+ 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
new file mode 100644
index 0000000..fdd8a53
--- /dev/null
+++ b/libray/iso.py
@@ -0,0 +1,106 @@
+# -*- 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 .
+
+from Crypto.Cipher import AES
+
+try:
+ from libray import core
+ from libray import ird
+except ImportError:
+ import core
+ import ird
+
+
+class ISO:
+
+
+ NUM_INFO_BYTES = 4
+
+
+ def __init__(self, args):
+ 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.
+
+ self.regions = self.read_regions(input_iso, args.iso)
+
+ if args.verbose:
+ self.print_info()
+
+ self.ird = ird.IRD(args)
+
+ cipher = AES.new(core.ISO_SECRET, AES.MODE_CBC, core.ISO_IV)
+ self.disc_key = cipher.encrypt(self.ird.data1)
+
+
+ def decrypt(self, args):
+
+ print('Decrypting with disc key: %s' % self.disc_key.hex())
+
+ with open(args.iso, 'rb') as input_iso:
+ with open(args.output, 'wb') as output_iso:
+ for i, region in enumerate(self.regions):
+ input_iso.seek(region['start'])
+
+ if not region['enc']:
+ while input_iso.tell() < region['end']:
+ data = input_iso.read(core.SECTOR)
+ output_iso.write(data)
+ continue
+ else:
+ while input_iso.tell() < region['end']:
+ num = input_iso.tell() // 2048
+ iv = bytearray([0 for i in range(0,16)])
+ for j in range(0,16):
+ iv[16 - j - 1] = (num & 0xFF)
+ num >>= 8
+
+ data = input_iso.read(core.SECTOR)
+
+ cipher = AES.new(self.disc_key, AES.MODE_CBC, bytes(iv))
+ decrypted = cipher.decrypt(data)
+
+ output_iso.write(decrypted)
+
+
+ def read_regions(self, input_iso, filename):
+ 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'] = core.filesize(filename)
+
+ return regions
+
+
+ def print_info(self):
+ print('Info from ISO:')
+ print('Regions: %s (%s)' % (self.number_of_regions, self.number_of_regions*2) )
+ for i, region in enumerate(self.regions):
+ print(i, region)
+
+
\ No newline at end of file
diff --git a/libray/libray b/libray/libray
new file mode 120000
index 0000000..211fc97
--- /dev/null
+++ b/libray/libray
@@ -0,0 +1 @@
+libray.py
\ No newline at end of file
diff --git a/libray/libray.py b/libray/libray.py
new file mode 100755
index 0000000..f0b55b2
--- /dev/null
+++ b/libray/libray.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python3
+# -*- 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 argparse
+
+try:
+ from libray import core
+except ImportError:
+ import core
+
+
+if __name__ == '__main__':
+
+ # Parse command line arguments with argpase
+ 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')
+ 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('-k', '--ird', dest='ird', type=str, help="Path to .ird file", required=True)
+ args = parser.parse_args()
+
+ core.decrypt(args)
+
+
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index 904545b..d033bb0 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1 +1 @@
-pycrypto
+pycrypto==2.6.1
\ No newline at end of file
diff --git a/setup.py b/setup.py
new file mode 100755
index 0000000..2b72a64
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,16 @@
+#!/usr/bin/env python3
+# -*- coding: utf8 -*-
+
+from setuptools import setup
+
+setup(
+ name="libray",
+ version="0.0.1",
+ description='A Libre (FLOSS) Python application for unencrypting, extracting, repackaging, and encrypting PS3 ISOs',
+ author="Nichlas Severinsen",
+ author_email="ns@nsz.no",
+ url="https://notabug.org/necklace/libray",
+ packages=['libray'],
+ scripts=['libray/libray'],
+ install_requires=['pycrypto==2.6.1'],
+)
\ No newline at end of file