mirror of
https://github.com/etienerodri/PRSA---Rom-Manager-.git
synced 2026-05-07 13:39:21 -05:00
150 lines
5.9 KiB
Python
150 lines
5.9 KiB
Python
from typing import Dict, List, Tuple, Optional
|
|
from load.lz10util import decompress_lz10
|
|
from load.narcutil import parse_narc
|
|
|
|
def detect_graphics_magic(data: bytes) -> Tuple[bool, str]:
|
|
if not data or len(data) < 4:
|
|
return (False, '')
|
|
magic = data[:4]
|
|
graphics_magics = [(b'RGCN', 'RGCN'), (b'NCGR', 'NCGR'), (b'NCBR', 'NCBR'), (b'NCER', 'NCER'), (b'RNAN', 'RNAN')]
|
|
for magic_bytes, name in graphics_magics:
|
|
if magic == magic_bytes:
|
|
return (True, name)
|
|
magic_reversed = magic[::-1]
|
|
for magic_bytes, name in graphics_magics:
|
|
if magic_reversed == magic_bytes:
|
|
return (True, f'{name}_REVERSED')
|
|
for magic_bytes, name in graphics_magics:
|
|
if magic[:3] == magic_bytes[:3] or magic[1:4] == magic_bytes[1:4]:
|
|
return (True, f'{name}_PARTIAL')
|
|
return (False, '')
|
|
|
|
def detect_palette_magic(data: bytes) -> Tuple[bool, str]:
|
|
if not data or len(data) < 4:
|
|
return (False, '')
|
|
magic = data[:4]
|
|
palette_magics = [(b'RLCN', 'RLCN'), (b'NCLR', 'NCLR'), (b'RTFN', 'RTFN')]
|
|
for magic_bytes, name in palette_magics:
|
|
if magic == magic_bytes:
|
|
return (True, name)
|
|
magic_reversed = magic[::-1]
|
|
for magic_bytes, name in palette_magics:
|
|
if magic_reversed == magic_bytes:
|
|
return (True, f'{name}_REVERSED')
|
|
for magic_bytes, name in palette_magics:
|
|
if magic[:3] == magic_bytes[:3] or magic[1:4] == magic_bytes[1:4]:
|
|
return (True, f'{name}_PARTIAL')
|
|
return (False, '')
|
|
|
|
def try_parse_as_graphics(data: bytes) -> Optional[bytes]:
|
|
if not data or len(data) < 32:
|
|
return None
|
|
is_gfx, fmt = detect_graphics_magic(data)
|
|
if is_gfx:
|
|
return data
|
|
for offset in range(0, min(len(data) - 4, 64)):
|
|
section_magic = data[offset:offset + 4]
|
|
if section_magic in [b'RAHC', b'CHAR', b'CRAH', b'RAHC'[::-1]]:
|
|
return data
|
|
return None
|
|
|
|
def try_parse_as_palette(data: bytes) -> Optional[bytes]:
|
|
if not data or len(data) < 32:
|
|
return None
|
|
is_pal, fmt = detect_palette_magic(data)
|
|
if is_pal:
|
|
return data
|
|
for offset in range(0, min(len(data) - 4, 64)):
|
|
section_magic = data[offset:offset + 4]
|
|
if section_magic in [b'TTLP', b'PLTT', b'PLTL', b'TTLP'[::-1]]:
|
|
return data
|
|
return None
|
|
|
|
def classify_tileset_data(inner_files: List[bytes]) -> Tuple[Optional[bytes], Optional[bytes]]:
|
|
rgcn = None
|
|
rlcn = None
|
|
for bf in inner_files:
|
|
if not bf or len(bf) < 4:
|
|
continue
|
|
if not rgcn:
|
|
graphics_data = try_parse_as_graphics(bf)
|
|
if graphics_data:
|
|
rgcn = graphics_data
|
|
continue
|
|
if not rlcn:
|
|
palette_data = try_parse_as_palette(bf)
|
|
if palette_data:
|
|
rlcn = palette_data
|
|
continue
|
|
if rgcn and rlcn:
|
|
break
|
|
if not rgcn or not rlcn:
|
|
for bf in inner_files:
|
|
if not bf:
|
|
continue
|
|
if not rgcn and len(bf) >= 1024:
|
|
rgcn = bf
|
|
elif not rlcn and len(bf) < 1024:
|
|
rlcn = bf
|
|
return (rgcn, rlcn)
|
|
|
|
def parse_tex_map(tex_path: str) -> Dict:
|
|
try:
|
|
with open(tex_path, 'rb') as f:
|
|
raw = f.read()
|
|
dec = decompress_lz10(raw)
|
|
if dec[:4] in [b'TEX\x00', b'TEX.', b'TEX\xff', b'TEX ', b'\x00XET']:
|
|
dec = dec[4:]
|
|
elif dec[:3] == b'TEX':
|
|
dec = dec[4:]
|
|
try:
|
|
outer_files = parse_narc(dec)
|
|
except ValueError as e:
|
|
print(f'Warning: TEX not a valid NARC ({e}), treating as single tileset')
|
|
outer_files = [dec]
|
|
tilesets = []
|
|
for i, ts_blob in enumerate(outer_files):
|
|
if not ts_blob:
|
|
tilesets.append({'index': i, 'RGCN': None, 'RLCN': None, 'NCGR': None, 'NCLR': None, 'error': 'Empty tileset data'})
|
|
continue
|
|
inner_files = []
|
|
if ts_blob[:4] == b'NARC':
|
|
try:
|
|
inner_files = parse_narc(ts_blob)
|
|
except ValueError:
|
|
inner_files = [ts_blob]
|
|
elif len(ts_blob) > 8:
|
|
parts = []
|
|
current_start = 0
|
|
for offset in range(4, len(ts_blob) - 4):
|
|
magic = ts_blob[offset:offset + 4]
|
|
is_gfx, _ = detect_graphics_magic(magic)
|
|
is_pal, _ = detect_palette_magic(magic)
|
|
if is_gfx or is_pal:
|
|
if current_start < offset:
|
|
parts.append(ts_blob[current_start:offset])
|
|
current_start = offset
|
|
if current_start < len(ts_blob):
|
|
parts.append(ts_blob[current_start:])
|
|
if len(parts) > 1:
|
|
inner_files = parts
|
|
else:
|
|
inner_files = [ts_blob]
|
|
else:
|
|
inner_files = [ts_blob]
|
|
rgcn, rlcn = classify_tileset_data(inner_files)
|
|
tileset_entry = {'index': i, 'RGCN': rgcn, 'RLCN': rlcn, 'NCGR': rgcn, 'NCLR': rlcn}
|
|
if not rgcn and (not rlcn):
|
|
tileset_entry['error'] = 'No valid graphics or palette data found'
|
|
elif not rgcn:
|
|
tileset_entry['warning'] = 'Graphics data (RGCN) missing'
|
|
elif not rlcn:
|
|
tileset_entry['warning'] = 'Palette data (RLCN) missing'
|
|
tilesets.append(tileset_entry)
|
|
return {'tilesets': tilesets, 'tileset_count': len(tilesets)}
|
|
except Exception as e:
|
|
print(f'ERROR parsing TEX file: {e}')
|
|
import traceback
|
|
traceback.print_exc()
|
|
return {'tilesets': [], 'tileset_count': 0, 'error': str(e)}
|