70 lines
2.1 KiB
Python
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)')
|