Files
GalScripts/pack_softpal_text_dat.py

70 lines
2.1 KiB
Python

import struct
from os.path import splitext
import json
import sys
def rotate_right(val, rshift):
return ((val >> rshift) | ((val << (8 - rshift)) & 0xFF)) & 0xFF
def encrypt_bytes(data: bytes) -> bytes:
# Matches inverse of decrypt in unpack_softpal_text_dat.py
CONST = 0x084DF873 ^ 0xFF987DEE
b = bytearray(data)
pos = 0x10
shift = 4
while pos + 4 <= len(b):
val = struct.unpack('<I', b[pos:pos+4])[0]
val ^= CONST
packed = bytearray(struct.pack('<I', val))
packed[0] = rotate_right(packed[0], shift)
b[pos:pos+4] = packed
pos += 4
shift = (shift + 1) % 8
return bytes(b)
def pack_table(table: dict, encoding: str = 'cp932', encrypt: bool = True) -> bytes:
# header: 12 bytes (unused by unpack)
header = b"\x00" * 12
items = sorted(((int(k), v) for k, v in table.items()), key=lambda x: x[0])
count = len(items)
out = bytearray()
out += header
out += struct.pack('<I', count)
# After this, the first u32 at offset 16 will be the first key
# We'll write entries immediately after (so first key sits at offset 16)
for key, s in items:
out += struct.pack('<I', key)
if isinstance(s, str):
encoded = s.encode(encoding)
else:
encoded = bytes(s)
out += encoded + b'\x00'
if encrypt:
out = encrypt_bytes(bytes(out))
return bytes(out)
if __name__ == '__main__':
if len(sys.argv) < 2:
print("Usage: python pack_softpal_text_dat.py <input.json> [<output.dat>] [<encoding>] [--no-encrypt]")
sys.exit(1)
infile = sys.argv[1]
outfile = sys.argv[2] if len(sys.argv) > 2 else splitext(infile)[0] + '.dat'
encoding = sys.argv[3] if len(sys.argv) > 3 and not sys.argv[3].startswith('--') else 'cp932'
encrypt = True
if '--no-encrypt' in sys.argv:
encrypt = False
with open(infile, 'r', encoding='utf-8') as f:
table = json.load(f)
data = pack_table(table, encoding=encoding, encrypt=encrypt)
with open(outfile, 'wb') as f:
f.write(data)
print(f'Wrote {outfile} ({len(data)} bytes)')