Daniel Seiller
8d92f25b8c
- Started implementing new parser for chunked data - Started documenting data formats - Started dissector for network protocol - Added AI-Graph renderer (converts .pth files to python data you can import into Blender) - Added Script to convert savefile to JSON - Added (old) parser for chunked data format - Added basic parser for LFVF data section (Vertex Data) - Added script to analyze and filter read trace generated with frida script - Added various Frida scripts
117 lines
3.2 KiB
Python
117 lines
3.2 KiB
Python
from construct import *
|
|
import binascii
|
|
import os
|
|
|
|
Chunked = LazyBound(lambda: struct)
|
|
|
|
|
|
class CustomError(SymmetricAdapter):
|
|
def __init__(self, msg):
|
|
super(SymmetricAdapter, self).__init__(Pass)
|
|
self._message = msg
|
|
|
|
def _decode(self, obj, context, path):
|
|
# print("Error",path)
|
|
# print(str(context))
|
|
msg = "Invalid ID: " + repr(context.id)
|
|
raise ValidationError(message=msg, path=this.path)
|
|
|
|
|
|
RGB = NamedTuple("RGB", "R G B", Int8ul[3])
|
|
|
|
RGBA = NamedTuple("RGBA", "R G B A", Int8ul[4])
|
|
|
|
|
|
def make_chain(*sizes):
|
|
"utility function to make sequence of byte arrays"
|
|
return Sequence(*[Bytes(s) for s in sizes])
|
|
|
|
|
|
child_nodes = "children" / Struct("num" / Int32ul, "nodes" / Chunked[this.num])
|
|
|
|
subchunks = {
|
|
b"SM3\0": Struct(
|
|
"unk" / Bytes(4),
|
|
"timestamp" / Timestamp(Int32ul, 1, 1970),
|
|
child_nodes,
|
|
"scene" / Chunked,
|
|
),
|
|
b"SCN\0": Struct(
|
|
"version" / Int32ul,
|
|
"m3d_name" / PascalString(Int32ul, "utf8"),
|
|
"name" / PascalString(Int32ul, "utf8"),
|
|
child_nodes,
|
|
),
|
|
b"INI\0": Struct(
|
|
"data"
|
|
/ PrefixedArray(Int32ul, PrefixedArray(Int32ul, PascalString(Int32ul, "utf8"))),
|
|
"colors?" / Sequence(Int8ul, Int8ul, Int8ul, Int8ul, Float32l)[2],
|
|
"unk_data" / Bytes(0x18),
|
|
"unk_float" / Float32l,
|
|
"unk_int" / Int32ul,
|
|
child_nodes,
|
|
),
|
|
b"EMI\0": Struct(
|
|
"version"/Int32ul,
|
|
"num_materials"/Int32ul,
|
|
"num_unk"/Int32ul,
|
|
"materials"/Chunked
|
|
),
|
|
|
|
b"MAT\0": Struct(
|
|
"tris"/Int32ul,
|
|
"name"/PascalString(Int32ul,"utf8"),
|
|
"idx"/Bytes(this.tris*4*4)
|
|
),
|
|
|
|
None: Bytes(lambda ctx:ctx.size),
|
|
}
|
|
|
|
struct = Struct(
|
|
"id" / Bytes(4),
|
|
"size" / Int32ul,
|
|
"data" / Switch(this.id, subchunks, default=subchunks[None]),
|
|
)
|
|
|
|
|
|
def io_peek(fh, n):
|
|
p = fh.tell()
|
|
ret = fh.read(n)
|
|
fh.seek(p)
|
|
return ret
|
|
|
|
|
|
basedir = r"D:/Games/Deep Silver/Scrapland/extracted/Data.packed"
|
|
|
|
files = [
|
|
r"Models/Elements/AnilloEstructuraA/AnilloEstructuraA.SM3",
|
|
r"models/elements/antenaa/antenaa.lod1.sm3",
|
|
r"models/elements/abshield/anm/loop.cm3",
|
|
r"levels/fake/map/map3d.amc",
|
|
r"levels/shipedit/map/map3d.dum",
|
|
r"levels/menu/map/map3d.emi",
|
|
r"Models/Skies/Menu/Sky.SM3",
|
|
r"Levels/Menu/Map/Map3D.SM3",
|
|
r"Models/Elements/AnilloEstructuraD/AnilloEstructuraD.LOD1.SM3",
|
|
r"levels/menu/map/map3d.amc",
|
|
r"levels/menu/map/map3d.dum",
|
|
r"levels/menu/map/scenecamera/anm/loop.cm3",
|
|
r"models/chars/boss/boss.sm3",
|
|
r"models/chars/boss/anm/boss_walk.cm3",
|
|
]
|
|
for file in files:
|
|
file = os.path.join(basedir, file).replace("/","\\")
|
|
print()
|
|
print("#" * 3, file)
|
|
with open(file, "rb") as infile:
|
|
try:
|
|
data = struct.parse_stream(infile)
|
|
# assert infile.read()==b"","leftover data"
|
|
except Exception as ex:
|
|
print("Error:", ex)
|
|
data = None
|
|
if data:
|
|
print(data)
|
|
print("OFFSET:", hex(infile.tell()))
|
|
print("NEXT:", io_peek(infile, 16))
|
|
print("NEXT:", binascii.hexlify(io_peek(infile, 16)))
|