92 lines
2.6 KiB
Python
Executable File
92 lines
2.6 KiB
Python
Executable File
#!/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')
|