diff --git a/segafs/makesegafs.py b/segafs/makesegafs.py index debdd90..fd9785f 100644 --- a/segafs/makesegafs.py +++ b/segafs/makesegafs.py @@ -9,7 +9,16 @@ import zlib from Crypto.Cipher import AES, PKCS1_OAEP from Crypto.Hash import HMAC, SHA1 from Crypto.PublicKey import RSA -from construct import Bytes, Const, Int16ul, Int32ul, Int64ul, Int8ul, Struct +from construct import ( + Bytes, + Const, + IfThenElse, + Int16ul, + Int32ul, + Int64ul, + Int8ul, + Struct, +) # ---- Configuration ENCRYPTION_KEY = bytes.fromhex("") @@ -28,11 +37,7 @@ BOOTID = { "second": 43, "milli": 0, }, - "game_version": { - "release": 0, - "minor": 30, - "major": 1, - }, + "game_version": b"A041", "block_size": 0x40000, "header_block_count": 8, "unk1": 0, @@ -57,7 +62,8 @@ BOOTID = { "minor": 54, "major": 80, }, - "strings": b"\x00" * 0x27AC, # Depending on the app/opt/pack, this might have some text in it. + "strings": b"\x00" + * 0x27AC, # Depending on the app/opt/pack, this might have some text in it. } # ---- @@ -65,7 +71,9 @@ BOOTID = { # The BootID (app/opt/pack header) encryption key and IV. BTKEY = bytes.fromhex("09ca5efd30c9aaef3804d0a7e3fa7120") BTIV = bytes.fromhex("b155c22c2e7f0491fa7f0fdc217aff90") -SIGKEY = bytes.fromhex("e1bdcb2d5e9ed3b5de234364dfa4d126849edff769fc6c28fba5f43bc482bd7479d676afce8188e1d3a6852f4ebce45cde46bd15e8ee5fe84d197f945a54518f") +SIGKEY = bytes.fromhex( + "e1bdcb2d5e9ed3b5de234364dfa4d126849edff769fc6c28fba5f43bc482bd7479d676afce8188e1d3a6852f4ebce45cde46bd15e8ee5fe84d197f945a54518f" +) HEADER_META_PUBKEY = RSA.import_key("""-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsRMLnJuczNpfoqPpHQ3o 5XNkjKXO6P3ToV/45Az5dNaHVL7uEu9vPI7a2KYFQnNYgD3UUHFahfTcljzLOkcH @@ -81,7 +89,7 @@ uwIDAQAB # ---- Constants. Don't edit. EXFAT_HEADER = bytes.fromhex("EB769045584641542020200000000000") OPTION_IV = bytes.fromhex("C063BF6F562D084D7963C987F5281761") -# ---- +# ---- # ---- Script Timestamp = Struct( @@ -107,7 +115,7 @@ BootID = Struct( "sequence_number" / Int16ul, "game_id" / Bytes(4), "game_timestamp" / Timestamp, - "game_version" / Version, + "game_version" / IfThenElse(lambda ctx: ctx.type == 0x0201, Bytes(4), Version), "block_count" / Int64ul, "block_size" / Int64ul, "header_block_count" / Int64ul, @@ -120,6 +128,7 @@ BootID = Struct( "strings" / Bytes(0x27AC), ) + def get_page_iv(iv: bytes, offset: int): return bytes(x ^ (offset >> (8 * (i % 8))) & 0xFF for (i, x) in enumerate(iv)) @@ -134,11 +143,27 @@ print(f"Generated IV: {iv.hex()}") filesize = os.stat(INPUT_FILE).st_size BOOTID["block_count"] = ceil(filesize / BOOTID["block_size"]) + 8 -header_meta = struct.pack(" 0: page_iv = get_page_iv(iv, total_written) cipher = AES.new(ENCRYPTION_KEY, AES.MODE_CBC, page_iv) @@ -192,7 +217,7 @@ with open(INPUT_FILE, "rb") as fin, open(OUTPUT_FILE, "w+b") as fout: block_crc32 = zlib.crc32(encrypted, block_crc32) null_byte_count -= 4096 - + block_crc32s.append(block_crc32) block_crc32 = 0 @@ -207,8 +232,10 @@ with open(INPUT_FILE, "rb") as fin, open(OUTPUT_FILE, "w+b") as fout: # Skip the HMAC signature and the first CRC32, which we're trying to calculate. _ = fout.seek(0x204, os.SEEK_CUR) - block_0_crc32 = zlib.crc32(fout.read(BOOTID["block_size"] - 0x2800 - 0x204), block_0_crc32) - + block_0_crc32 = zlib.crc32( + fout.read(BOOTID["block_size"] - 0x2800 - 0x204), block_0_crc32 + ) + _ = fout.seek(0x2A00) _ = fout.write(struct.pack("