Files
bigmak94andClaude Opus 5.5 bd9bded792 GalaxyQuest: Super Mario Galaxy in VR on Meta Quest 3
A native port built on the Petari decompilation (previous commit): an
HLE platform layer (OS, DVD, GX, audio, input, NAND) under the game's
own code, an OpenXR presentation (a diorama around the player, a giant
screen, the HUD and settings as compositor layers, SpaceWarp, dynamic
resolution), Touch Plus controls, the tools that convert your own game
files (tools/cook, which also takes the few pieces of data the game's
executable holds from your sys/main.dol), and the port's changes to the
decompilation (`git diff petari-base -- decomp`).  README.md explains
installing and building, docs/TECHNICAL.md how it works.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 01:59:38 +02:00

96 lines
3.0 KiB
Python

#!/usr/bin/env python3
"""Sanity-checks cooked (little-endian) data: re-walks every RARC container
and the block chains / headers of the main formats using little-endian reads."""
import collections
import os
import struct
import sys
def le32(b, o):
return struct.unpack_from('<I', b, o)[0]
def le16(b, o):
return struct.unpack_from('<H', b, o)[0]
def rarc_files(b):
hdr = le32(b, 8)
data_start = hdr + le32(b, 0xC)
ndirs, dir_off, nfiles, file_off, _, str_off = (le32(b, hdr + 4 * i) for i in range(6))
str_off += hdr
def cstr(o):
return bytes(b[str_off + o:b.index(b'\0', str_off + o)]).decode('shift_jis', 'replace')
dirs = [(cstr(le32(b, hdr + dir_off + 16 * i + 4)), le16(b, hdr + dir_off + 16 * i + 0xA), le32(b, hdr + dir_off + 16 * i + 0xC))
for i in range(ndirs)]
out = []
def walk(di, prefix):
name, count, first = dirs[di]
for e in range(first, first + count):
f = hdr + file_off + 0x14 * e
word = le32(b, f + 4)
flags, name_off = word >> 24, word & 0xFFFFFF
ename = cstr(name_off)
if flags & 2:
if ename not in ('.', '..'):
walk(le32(b, f + 8), prefix + ename + '/')
else:
off, size = data_start + le32(b, f + 8), le32(b, f + 0xC)
if off + size > len(b):
raise ValueError('file %s outside archive' % ename)
out.append((prefix + ename, flags, b[off:off + size]))
walk(0, dirs[0][0] + '/')
return out
def check_blob(path, b, problems):
m = b[:4]
if m in (b'2D3J', b'1D3J'): # swapped magic reads as 'J3D2'/'J3D1'
nblocks = le32(b, 0xC)
o = 0x20
for _ in range(nblocks):
size = le32(b, o + 4)
if size < 8 or o + size > len(b) + 0x20:
problems.append('%s: bad J3D block chain at %#x' % (path, o))
return
o += size
elif path.endswith('.kcl'):
pos, nrm, prism, octree = struct.unpack_from('<4I', b, 0)
if not (pos == 0x38 and pos <= nrm <= prism + 0x10 <= octree <= len(b)):
problems.append('%s: bad KCL header' % path)
def main(root):
problems = []
counts = collections.Counter()
for dp, _, names in os.walk(root):
for n in names:
if not n.endswith(('.arc', '.szs')):
continue
p = os.path.join(dp, n)
b = open(p, 'rb').read()
if b[:4] != b'CRAR':
counts['non-rarc'] += 1
continue
try:
for path, flags, blob in rarc_files(b):
counts['files'] += 1
check_blob(path, blob, problems)
except Exception as e:
problems.append('%s: %r' % (p, e))
counts['archives'] += 1
print(dict(counts))
for pr in problems[:50]:
print('PROBLEM', pr)
print('%d problems' % len(problems))
return 1 if problems else 0
if __name__ == '__main__':
sys.exit(main(sys.argv[1]))