Skip to content

Instantly share code, notes, and snippets.

@Wh1terat
Created January 5, 2023 15:11
Show Gist options
  • Save Wh1terat/72af90a73e2eb7a9e0522c3992859067 to your computer and use it in GitHub Desktop.
Save Wh1terat/72af90a73e2eb7a9e0522c3992859067 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
import argparse
from pathlib import Path
from struct import pack, unpack, iter_unpack
__title__ = "Xanavi RCL (Un)packer"
__version__ = "0.1"
__author__ = "Gareth Bryan"
__license__ = "MIT"
def main(args):
if args.action == 'p':
rcl_pack(args.indir, args.outfile)
elif args.action == 'u':
rcl_unpack(args.infile, args.outdir)
def rcl_pack(indir, outfile):
table_len = sum(16 for _ in indir.glob('*.png'))
header = bytearray(0x118)
table = bytearray()
data = bytearray()
header[0x00:0x20] = b'Xanavi Informatics Corporation'.ljust(0x20, b'\x00')
header[0x20:0x50] = b'20080722-125925 08IT_OPENING_M3H'.ljust(0x30, b'\x00')
header[0x50:0x70] = b'M3H'.ljust(0x20, b'\x00')
header[0x70:0x74] = b'\xFF\xFF\x00\x01' # Type?
header[0x74:0x78] = b'\xFF\xFF\xFF\xFF'
header[0x7C:0x80] = b'\xFF\xFF\xFF\xFF'
header[0x100:0x104] = pack('>I', 0x108)
header[0x104:0x108] = pack('>I', 0x10C)
header[0x108:0x10C] = pack('>I', 12)
header[0x10C:0x110] = pack('>I', 8)
header[0x110:0x114] = pack('>I', 0x114)
# Write file data start
header[0x114:0x118] = pack('>I', table_len + 4)
# Loop through PNGs
for p in sorted(indir.glob('*.png')):
offset = len(header) + table_len + len(data)
# Add file table entry
table.extend(
pack(
">4I",
offset, offset, p.stat().st_size, p.stat().st_size
)
)
with p.open('rb') as f:
# Add png to data bytearray
data.extend(f.read())
# Align file to 4 bytes
if len(data) % 4:
padding = b'\x00' * (4 - (len(data) % 4))
data.extend(padding)
# Set filesize in header
header[0x78:0x7C] = pack('>I', len(header)+len(table)+len(data))
# Write output
outfile.write(header)
outfile.write(table)
outfile.write(data)
def rcl_unpack(infile, outdir):
infile = infile.read()
outdir.mkdir()
table_start, table_len = unpack('>2I', infile[0x110:0x118])
for i, entry in enumerate(iter_unpack('>I4xI4x', infile[table_start+4:table_start+table_len])):
offset, size = entry
png = f'{outdir}/{i:03d}.png'
#print(png, entry)
with open(png, 'wb') as f:
f.write(infile[offset:offset+size])
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="{} v{}".format(__title__, __version__)
)
subparsers = parser.add_subparsers(title="Actions", dest="action", required=True)
act_pack = subparsers.add_parser("p", help="Pack")
act_pack.add_argument(
"indir",
help="Input dir",
type=Path,
nargs="?",
)
act_pack.add_argument(
"outfile",
help="Output RCL file",
type=argparse.FileType("wb"),
nargs="?",
)
act_unpack = subparsers.add_parser("u", help="Unpack")
act_unpack.add_argument(
"infile",
help="Input RCL file",
type=argparse.FileType("rb"),
nargs="?",
)
act_unpack.add_argument(
"outdir",
help="Output dir",
type=Path,
nargs="?",
)
args = parser.parse_args()
main(args)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment