misc/tg_dir_xtract.py

92 lines
2.6 KiB
Python
Raw Normal View History

2023-11-09 18:18:05 -05:00
#!/usr/bin/env python3
# A very very POC .DIR extractor
# I probably won't continue using python
# this is just a test because I hate quickbms dearly
import ctypes
import sys
import os
from pathlib import Path, PurePath
class CoolStructure(ctypes.Structure):
def __repr__(self) -> str:
values = ", ".join(f"{name}={value}" for name, value in self._asdict().items())
return f"<{self.__class__.__name__}: {values}>"
def _asdict(self) -> dict:
return {field[0]: getattr(self, field[0]) for field in self._fields_}
# DIR structures. Note that I'm not using the Python `struct` module for brevity.
class DirHeader(CoolStructure):
_fields_ = [
('magic', ctypes.c_char * 4), # "DIRF"
('pad', ctypes.c_uint32),
('version', ctypes.c_char * 4), # version (2.2d)
('fileCount', ctypes.c_uint32), # count of files
('pad_2', ctypes.c_char * 0x30),
]
def valid(self) -> bool:
return self.magic == b'DIRF'
def version(self) -> str:
return self.version.decode('ascii')
class DirTocEntry(CoolStructure):
_fields_ = [
('blockOffset', ctypes.c_uint32), # offset in 0x800 "blocks"
('size', ctypes.c_uint32), # size in bytes
('_fileName', ctypes.c_char * 0x38) # filename
]
def fileName(self) -> str:
return self._fileName.decode('ascii')
def byteOffset(self) -> int:
return self.blockOffset * 0x800
def getData(self, file) -> bytes:
ret = bytearray(self.size)
lastOffset = file.tell()
file.seek(self.byteOffset(), os.SEEK_SET)
file.readinto(ret)
file.seek(lastOffset, os.SEEK_SET)
return ret
# """""main loop"""""
def makePath(filename):
return Path(os.getcwd()) / Path(sys.argv[1]).stem / filename
def makeFolderPath():
(Path(os.getcwd()) / Path(sys.argv[1]).stem).mkdir(exist_ok=True)
with open(sys.argv[1], "rb") as file:
header = DirHeader()
file.readinto(header)
if header.valid():
makeFolderPath()
toc = []
for i in range(0, header.fileCount): # hacky and slow probably but whatever
entry = DirTocEntry()
file.readinto(entry)
toc.append(entry)
# read the TOC out
print(f'going to extract {header.fileCount} files')
for item in toc:
name = item.fileName()
data = item.getData(file)
path = makePath(name)
with open(str(path), "wb") as outFile:
outFile.write(data)
print(f' wrote \'{str(path)}\' size {item.size} bytes')