# WH40K: Chaos Gate Re-implementation of the WH40K binary. **You must have a copy of the original game data to use this project** WH40K.exe is the existing game engine WH40K_TD.exe is the map editor. Allows things to be saved as .MAP or as .SMF ("Super Macro File") This README is a grab-bag of miscellaneous information about file formats at present. This is being migrated to the [Formats overview](doc/formats/index.md) as a precursor to making this file useful! No game yet, nothing even close. I'm in the very early stages of trying to understand the various file formats. Until then, you can play WH40L: Chaos Gate in a WinXP VM, disconnected from the internet. It doesn't need 3D rendering! "Mission Setup" includes information about available squad types From EquipDef.cpp Dumo: CEquipment we learn the following object types: 0. DELETED 1. WEAPON 2. GRENADE 3. MEDIPACK 4. SCANNER 5. GENESEED 6. CLIP 7. DOOR KEY 8. DOOR KEY 9. DOOR KEY 10. DOOR KEY And we learn they can be "on".... 0. CHARACTER 1. VEHICLE 2. CANISTER Hypothesis: Idx/WarHammer.idx points objects into bitmap data in Anim/WarHammer.ani We can use WH40K_TD.exe and investigate reads of .idx followed by reads of .ani to test this. Obj/*.obj and Assign/*.asn files seem to have some identity WH40K_TD.exe opens files in this order: 1. Data/USEng.dta 1. WH40K_TD.exe (?) 1. Cursor/Cursors.cur 1. pread64(fd, 23, 0) = 23 1. _llseek(fd, 0, [0], SEEK_CUR) = 0 1. _llseek(fd, 0, [0], SEEK_CUR) = 0 1. _llseek(fd, 40666, [40666], SEEK_SET) = 0 1. _llseek(fd, 0, [0], SEEK_SET) = 0 1. close(fd) = 0 1. read(fd, "\x26\x00\x00\x00\x20\x00\x00\x00\x30\x01\x00\x00\x50\x01\x00\x00\x8a\x9d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 32) = 32 1. (...) 1. _llseek(15, 39868, [39868], SEEK_SET) = 0 1. read(15, "\x27\x00\x1f\x00\x22\x00\x24\x00\x00\x00\x00\x00\x06\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x03\x81\x1f\x80\x1a\x81\x1f\x80\x03\x00\x80\x02\x83\x1f\x32\x1f\x80\x18\x83\x1f\x32\x1f\x80\x02\x00\x80\x01\x85\x1f\x32\x37\x39\x1f\x80\x16\x85\x1f\x32\x37"..., 798) = 798 1. (some statting of Idx/WarHammer.idx, no reading that I saw) 1. Anim/WarHammer.ani 1. read(15, "\x7e\xdf\x02\x00\x20\x00\x00\x00\xf0\xfb\x16\x00\x10\xfc\x16\x00\xa5\x4a\xc3\x18\x00\x00\x00\x00\x00\x00\x00\x00\x28\x92\x52\x00", 32) = 32 1. (some clones of child procs, I didn't follow them) 1. Sounds/wh40k.ds 1. pread64(31, "\xff\xff\xff\xff\x08\x60\x06\x00\x61\x6d\x62\x5f\x30\x31\x5f\x6a\x75\x6e\x67\x6c\x65\x2e\x77", 23, 0) = 23 1. read(31, "\xff\xff\xff\xff\x08\x60\x06\x00\x61\x6d\x62\x5f\x30\x31\x5f\x6a\x75\x6e\x67\x6c\x65\x2e\x77\x61\x76\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"..., 417792) = 417792 1. read(31, "\xe5\x03\x00\x00\x00\x58\x02\x00\x05\x10\x00\x50\xfe\x5e\xff\x00\x72\x03\x3d\x89\x12\x30\x00\x1f\x9e\xe2\x10\xf4\x32\xdd\xef\xf1\x42\xff\x0f\xc0\x12\x9c\xf2\x70\xeb\x01\x41\x04\x20\xad\xf1\x20\x11\x60\xce\xf3\x20\xc1\x42\xfa\x01\x32\x22\x0c\xcd\x00\x0b\xfe"..., 4096) = 4096 1. Data/Sounds.dat 1. pread64(34, "#**********************", 23, 0) = 23 1. ... 1. Pic/wh40k.pcx 1. read(34, "\x0a\x05\x01\x08\x00\x00\x00\x00\x7f\x02\xdf\x01\x48\x00\x48\x00\x00\x00\x00\x80\x00\x00\x00\x80\x00\x80\x80\x00\x00\x00\x80\x80\x00\x80\x00\x80\x80\xc0\xc0\xc0\xc0\xdc\xc0\xa6\xca\xf0\xff\xff\xff\xf0\xf0\xf0\xdd\xdd\xdd\xcb\xcb\xcb\xbb\xbb\xbb\xb2\xb2\xb2"..., 168509) = 168509 1. Sets/* 1. (lots of statting these) 1. Data/Randchar.dat 1. pread64(34, "#**********************", 23, 0) = 23 1. read(34, "#***************************************************************"..., 4096) = 4096 1. ... 1. Data/WeapDef.dat 1. pread64(35, "#**********************", 23, 0) = 23 1. read(35, "#***************************************************************"..., 4096) = 4096 1. ... 1. Data/SpellDef.dat 1. pread64(35, "#**********************", 23, 0) = 23 1. read(35, "#***************************************************************"..., 4096) = 4096 1. ... 1. Data/AniObDef.dat 1. pread64(35, "# ******** ANIMATED OBJ", 23, 0) = 23 1. read(35, "# ******** ANIMATED OBJECT DEFINITIONS **************\r\n#\t\t0 : **"..., 4096) = 4096 1. ... 1. Data/VehicDef.dat 1. pread64(35, "# ******** VEHICLE DEFI", 23, 0) = 23 1. read(35, "# ******** VEHICLE DEFINITIONS **************\r\n#\t\t0 : *** VEHICL"..., 4096) = 4096 1. ... 1. Data/StdWeap.dat 1. pread64(35, "# ******** SQUAD STANDA", 23, 0) = 23 1. read(35, "# ******** SQUAD STANDARD WEAPONS **************\r\n#\t\t0 : *** SQU"..., 4096) = 4096 1. ... 1. Data/Ultnames.dat 1. Data/Chanames.dat 1. Data/keymap.dta 1. Filters/wh40k.flt 1. _llseek(35, 0, [0], SEEK_SET) = 0 1. read(35, "\x01\x00\x00\x00", 4) = 4 1. _llseek(35, 4, [4], SEEK_SET) = 0 1. read(35, "\x4d\x79\x46\x69\x6c\x74\x65\x72\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"..., 72) = 72 1. _llseek(35, 1444, [1444], SEEK_SET) = 0 1. read(35, "\x1f\x1e\x7f\x77\x1e\x1e\x1e\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1f\x1f\x1f\x4f\x4f\x1d\x1d\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1f\x1f\x1f\x1f\x1f\xaf\xaf\xaf\x97\x97\x97\x97\x1e\x1e\x1e\x1e\x1e\x1e\x1f\x1f\x1f"..., 327680) = 327680 1. Misc/occlusio.lis 1. pread64(35, "62 # Number of Absol", 23, 0) = 23 1. read(35, "62 # Number of Absolute Deltas.\r\n # These Deltas are off"..., 4096) = 982 1. read(35, "", 3114) = 0 1. Data/GDestroy.dat 1. (stat Obj/destroy.obj) 1. Data/minimap.dat 1. Misc/occlusio.list 1. Obj/specials.obj 1. Obj/Man_Shadow.obj 1. Sets/map01.set 1. Data/Defs.dat 1. Assign/jungtil.asn 1. pread64(36, "# jungle floor\r\n# jungt", 23, 0) = 23 1. read(36, "# jungle floor\r\n# jungtil.obj/.asn\r\n# /--> d:\\warflics\\mission"..., 4096) = 408 1. read(36, "", 3688) = 0 1. Obj/jungtil.obj 1. read(36, "\x12\x00\x00\x00\x20\x00\x00\x00\x90\x00\x00\x00\xb0\x00\x00\x00\x88\x17\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 32) = 32 1. _llseek(36, 32, [32], SEEK_SET) = 0 1. read(36, "\x00\x00\x00\x00\xcf\x01\x00\x00\xcf\x01\x00\x00\x72\x11\x00\x00\x41\x13\x00\x00\x55\x11\x00\x00\x96\x24\x00\x00\x74\x11\x00\x00\x0a\x36\x00\x00\x76\x11\x00\x00\x80\x47\x00\x00\x6d\x11\x00\x00\xed\x58\x00\x00\x78\x11\x00\x00\x65\x6a\x00\x00\x70\x11\x00\x00"..., 144) = 144 1. _llseek(36, 176, [176], SEEK_SET) = 0 1. read(36, "\xd1\x00\x42\x01\x80\x00\x3f\x00\x00\x00\x00\x00\xb7\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x3e\x04\x1f\x80\x3e\x00\x80\x3c\x08\x1f\x80\x3c\x00\x80\x3a\x0c\x1f\x80\x3a\x00\x80\x38\x10\x1f\x80\x38\x00\x80\x36\x14\x1f\x80\x36\x00\x80\x34\x18\x1f\x80"..., 463) = 463 1. _llseek(36, 639, [639], SEEK_SET) = 0 1. read(36, "\xd1\x00\x42\x01\x80\x00\x3f\x00\x00\x00\x00\x00\x5a\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x3e\x84\x6d\x6c\x6e\x1e\x80\x3e\x00\x80\x3c\x88\xbf\x76\x6e\x6d\x6e\x76\x76\x6e\x80\x3c\x00\x80\x3a\x84\xbf\x76\x6e\x76\x04\x6d\x84\x6e\x76\x7d\x97\x80\x3a"..., 4466) = 4466 1. _llseek(36, 5105, [5105], SEEK_SET) = 0 1. read(36, "\xd1\x00\x42\x01\x80\x00\x3f\x00\x00\x00\x00\x00\x3d\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x3e\x84\x6d\x6c\x6e\x1e\x80\x3e\x00\x80\x3c\x88\xbf\x76\x6e\x6d\x6e\x76\x97\x6d\x80\x3c\x00\x80\x3a\x8c\xbf\x1e\x6c\x6d\x6d\x6e\x6d\x76\x6e\x97\x1c\x76\x80"..., 4437) = 4437 1. _llseek(36, 9542, [9542], SEEK_SET) = 0 1. read(36, "\xd1\x00\x42\x01\x80\x00\x3f\x00\x00\x00\x00\x00\x5c\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x3e\x84\x6d\x6c\x76\x7e\x80\x3e\x00\x80\x3c\x88\xbf\x76\x6d\x6e\x6d\x6e\x76\x97\x80\x3c\x00\x80\x3a\x83\x6d\x1e\x6c\x03\x6d\x86\x6e\x96\x76\x6d\x1c\x97\x80"..., 4468) = 4468 1. _llseek(36, 14010, [14010], SEEK_SET) = 0 1. read(36, "\xd1\x00\x42\x01\x80\x00\x3f\x00\x00\x00\x00\x00\x5e\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x3e\x84\x6d\x6c\x6e\x1e\x80\x3e\x00\x80\x3c\x88\xbf\x6e\x97\x6e\x76\x67\x97\x76\x80\x3c\x00\x80\x3a\x8c\x7d\xbf\x1e\x7f\x6e\x6e\x1e\x7d\x96\x6e\x7f\x97\x80"..., 4470) = 4470 1. _llseek(36, 18480, [18480], SEEK_SET) = 0 1. read(36, "\xd1\x00\x42\x01\x80\x00\x3f\x00\x00\x00\x00\x00\x55\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x3e\x84\x65\x6e\x1e\x7c\x80\x3e\x00\x80\x3c\x88\x66\x6e\x6b\x8f\x1c\x67\x6d\x6c\x80\x3c\x00\x80\x3a\x8c\x75\x1c\x6e\x6e\xad\x8f\x1c\x8f\x6d\x1c\x7d\x1c\x80"..., 4461) = 4461 1. _llseek(36, 22941, [22941], SEEK_SET) = 0 1. read(36, "\xd1\x00\x42\x01\x80\x00\x3f\x00\x00\x00\x00\x00\x60\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x3e\x84\x6d\x6c\x6e\x1e\x80\x3e\x00\x80\x3c\x88\xbf\x75\x76\x6e\x6d\x6e\x97\x97\x80\x3c\x00\x80\x3a\x85\x6b\x6b\x6e\x96\x76\x04\x6d\x83\x6e\x6b\x97\x80\x3a"..., 4472) = 4472 1. _llseek(36, 27413, [27413], SEEK_SET) = 0 1. read(36, "\xd1\x00\x42\x01\x80\x00\x3f\x00\x00\x00\x00\x00\x58\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x3e\x84\x6d\x76\x76\x1e\x80\x3e\x00\x80\x3c\x88\x8f\x6e\x6d\x6e\x96\x6e\x76\x67\x80\x3c\x00\x80\x3a\x87\x6b\x1a\x8f\x75\x96\x1c\x76\x03\x6e\x82\x6d\x6e\x80"..., 4464) = 4464 1. _llseek(36, 31877, [31877], SEEK_SET) = 0 1. read(36, "\xd1\x00\x42\x01\x80\x00\x3f\x00\x00\x00\x00\x00\x65\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x3e\x84\x6c\x6e\x97\x1c\x80\x3e\x00\x80\x3c\x88\x6d\x6e\x76\x6e\x76\x6d\x97\x1c\x80\x3c\x00\x80\x3a\x8c\x6d\x6d\x96\xad\x76\x96\x6e\x76\x6e\x6e\x97\x6e\x80"..., 4477) = 4477 1. _llseek(36, 36354, [36354], SEEK_SET) = 0 1. read(36, "\xd1\x00\x42\x01\x80\x00\x3f\x00\x00\x00\x00\x00\x61\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x3e\x84\x6d\x6c\x6e\x1e\x80\x3e\x00\x80\x3c\x88\x75\x67\x6e\x76\x6d\x6e\x97\x1c\x80\x3c\x00\x80\x3a\x8c\x8f\x76\xad\x97\x6e\x6d\x6e\xbe\x6e\x76\x6e\x7d\x80"..., 4473) = 4473 1. _llseek(36, 40827, [40827], SEEK_SET) = 0 1. read(36, "\xd1\x00\x42\x01\x80\x00\x3f\x00\x00\x00\x00\x00\x60\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x3e\x84\x6d\x6c\x6e\x1e\x80\x3e\x00\x80\x3c\x88\xbf\x76\x6e\x6d\x6d\x6e\x97\x1c\x80\x3c\x00\x80\x3a\x82\x76\x97\x03\x6d\x87\xbe\x6e\x76\x96\x6e\x1e\x6d\x80"..., 4472) = 4472 1. _llseek(36, 45299, [45299], SEEK_SET) = 0 1. read(36, "\xd1\x00\x42\x01\x80\x00\x3f\x00\x00\x00\x00\x00\x3d\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x3e\x84\x6d\x6c\x6e\x1e\x80\x3e\x00\x80\x3c\x88\xbf\x76\x6e\x6d\x6e\x76\x97\x6d\x80\x3c\x00\x80\x3a\x8c\xbf\x1e\x6c\x6d\x6d\x6e\x6d\x76\x6e\x97\x1c\x76\x80"..., 4437) = 4437 1. _llseek(36, 49736, [49736], SEEK_SET) = 0 1. read(36, "\xd1\x00\x42\x01\x80\x00\x3f\x00\x00\x00\x00\x00\xb7\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x3e\x04\x1f\x80\x3e\x00\x80\x3c\x08\x1f\x80\x3c\x00\x80\x3a\x0c\x1f\x80\x3a\x00\x80\x38\x10\x1f\x80\x38\x00\x80\x36\x14\x1f\x80\x36\x00\x80\x34\x18\x1f\x80"..., 463) = 463 1. _llseek(36, 50199, [50199], SEEK_SET) = 0 1. read(36, "\xd1\x00\x42\x01\x80\x00\x3f\x00\x00\x00\x00\x00\x83\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x3e\x84\x6d\x6c\x6e\x1e\x80\x3e\x00\x80\x3c\x88\xbf\x76\x6e\x6d\x6e\x76\x76\x6e\x80\x3c\x00\x80\x3a\x84\xbf\x76\x6e\x76\x04\x6d\x84\x6e\x76\x7d\x97\x80\x3a"..., 4251) = 4251 1. _llseek(36, 54450, [54450], SEEK_SET) = 0 1. read(36, "\xd1\x00\x42\x01\x80\x00\x3f\x00\x00\x00\x00\x00\xe6\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x3e\x84\x6d\x6c\x6e\x1e\x80\x3e\x00\x80\x3c\x88\xbf\x76\x6e\x6d\x6e\x76\x76\x6e\x80\x3c\x00\x80\x3a\x84\xbf\x76\x6e\x76\x04\x6d\x84\x6e\x76\x7d\x97\x80\x3a"..., 4350) = 4350 1. _llseek(36, 58800, [58800], SEEK_SET) = 0 1. read(36, "\xd1\x00\x42\x01\x80\x00\x3f\x00\x00\x00\x00\x00\x9e\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x3e\x84\x6d\x6c\x6e\x1e\x80\x3e\x00\x80\x3c\x88\xbf\x76\x6e\x6d\x6e\x76\x76\x6e\x80\x3c\x00\x80\x3a\x84\xbf\x76\x6e\x76\x04\x6d\x84\x6e\x76\x7d\x97\x80\x3a"..., 4278) = 4278 1. _llseek(36, 63078, [63078], SEEK_SET) = 0 1. read(36, "\xd1\x00\x42\x01\x80\x00\x3f\x00\x00\x00\x00\x00\xd1\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x3e\x84\x6d\x6c\x6e\x1e\x80\x3e\x00\x80\x3c\x88\xbf\x76\x6e\x6d\x6e\x76\x76\x6e\x80\x3c\x00\x80\x3a\x84\xbf\x76\x6e\x76\x04\x6d\x84\x6e\x76\x7d\x97\x80\x3a"..., 4329) = 4329 1. _llseek(36, 67407, [67407], SEEK_SET) = 0 1. read(36, "\xd1\x00\x42\x01\x80\x00\x3f\x00\x00\x00\x00\x00\xd1\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x3e\x84\x6d\x6c\x6e\x1e\x80\x3e\x00\x80\x3c\x88\xbf\x76\x6e\x6d\x6e\x76\x76\x6e\x80\x3c\x00\x80\x3a\x84\xbf\x76\x6e\x76\x04\x6d\x84\x6e\x76\x7d\x97\x80\x3a"..., 4329) = 4329 1. Assign/h_jung4.asn 1. Obj/h_jung4.obj 1. (more assign + obj pairs) 1. Data/Cycle.cyc jungtil.asn references 18 frames (0-17) and a `jungtil.flc`, which is an AutoDesk format We read jungtil.obj in about that number of blocks. Presumably each is a frame in some format or another, but seemingly *not* FLC. Here's our list of reads: | Offset | Size | Gap | Purpose | |--------|------|-----|---------| | 0 | 32 | 0 | Main header | 32 | 144 | 0 | Frame headers | 176 | 463 | 0 | Frame 1 | 639 | 4466 | 0 | ... | 5105 | 4437 | 0 | | 9542 | 4468 | 0 | | 14010 | 4470 | 0 | | 18480 | 4461 | 0 | | 22941 | 4472 | 0 | | 27413 | 4464 | 0 | | 31877 | 4477 | 0 | | 36354 | 4473 | 0 | | 40827 | 4472 | 0 | | 45299 | 4437 | 0 | | 49736 | 463 | 0 | | 50199 | 4251 | 0 | | 54450 | 4350 | 0 | | 58800 | 4278 | 0 | | 63078 | 4329 | 0 | | 67407 | 4329 | 0 | Frame 18 20 reads in total. The following chunks are identical: ``` * 176 and 49736 (all 463-byte frames) * 63078 and 67407 (all 4329-byte frames) ``` Here's the hex of the first 32 bytes: ``` 0 1 2 3 4 5 6 7 8 9 a b c d e f 0x0000 12 00 00 00 20 00 00 00 90 00 00 00 B0 00 00 00 0x0010 88 17 01 00 00 00 00 00 00 00 00 00 00 00 00 00 ``` * Offset 0 = 0x12 = 18 = the total number of frames ? * Offset 4 = 0x20 = 32 = the size of the this block ? * Offset 8 = 0x90 = 144 = the size of the next block ? * Offset c = 0xB0 = 176 = the offset of the third read, or the size of the first two blocks combined ? Need to unpack a few more objs to check what these numbers do. They could all be 32-bit LE integers too. 0x10-0x13 as a 32-bit LE is 71560. Total file size (71736) - 32 - 144 = 71560 = size of all frames combined Obj/j_tree02.obj doesn't seem to adhere to these rules at all... Then the next 144 bytes: ``` 0 1 2 3 4 5 6 7 0x0020 00 00 00 00 CF 01 00 00 = 0, 463 0x0028 CF 01 00 00 72 11 00 00 = 463, 4466 0x0030 41 13 00 00 55 11 00 00 = 4929, 4437 0x0038 96 24 00 00 74 11 00 00 = ... 0x0040 0A 36 00 00 76 11 00 00 0x0048 80 47 00 00 6D 11 00 00 0x0050 ED 58 00 00 78 11 00 00 0x0058 65 6A 00 00 70 11 00 00 0x0060 D5 7B 00 00 7D 11 00 00 0c0068 52 8D 00 00 79 11 00 00 0x0070 CB 9E 00 00 78 11 00 00 0x0078 43 B0 00 00 55 11 00 00 0x0080 98 C1 00 00 CF 01 00 00 0x0088 67 C3 00 00 9B 10 00 00 0x0090 02 D4 00 00 FE 10 00 00 0x0098 00 E5 00 00 B6 10 00 00 0x00A0 B6 F5 00 00 E9 10 00 00 0x00A8 9F 06 01 00 E9 10 00 00 ``` 8 bytes per frame (8*18=144). First four bytes in each line are the position relative to the start of the first frame (i.e., minus the size of the first two blocks). Last four bytes in each line are the total size of the frame, header + data. Then there are 18 blocks of varying size. These must be the frames. First 32 bytes of all 18 blocks has a lot of similarities: ``` 0 1 2 3 4 5 6 7 8 9 a b c d e f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f (0c,0d) (18,19) (1a,1b) (1c,1d) (1e,1f) 00176.0463 D1 00 42 01 80 00 3F 00 00 00 00 00 B7 01 00 00 00 00 00 00 00 00 00 00 80 3E 04 1F 80 3E 00 80 439 16000 7940 16000 32768 00639.4466 D1 00 42 01 80 00 3F 00 00 00 00 00 5A 11 00 00 00 00 00 00 00 00 00 00 80 3E 84 6D 6C 6E 1E 80 4442 16000 28036 28268 32798 05105.4437 D1 00 42 01 80 00 3F 00 00 00 00 00 3D 11 00 00 00 00 00 00 00 00 00 00 80 3E 84 6D 6C 6E 1E 80 4413 16000 28036 28268 32798 09542.4468 D1 00 42 01 80 00 3F 00 00 00 00 00 5C 11 00 00 00 00 00 00 00 00 00 00 80 3E 84 6D 6C 76 7E 80 4444 16000 28036 30316 32894 14010.4470 D1 00 42 01 80 00 3F 00 00 00 00 00 5E 11 00 00 00 00 00 00 00 00 00 00 80 3E 84 6D 6C 6E 1E 80 4446 16000 28036 28268 32798 18480.4461 D1 00 42 01 80 00 3F 00 00 00 00 00 55 11 00 00 00 00 00 00 00 00 00 00 80 3E 84 65 6E 1E 7C 80 4437 16000 25988 7790 32892 22941.4472 D1 00 42 01 80 00 3F 00 00 00 00 00 60 11 00 00 00 00 00 00 00 00 00 00 80 3E 84 6D 6C 6E 1E 80 4448 16000 28036 28268 32798 27413.4464 D1 00 42 01 80 00 3F 00 00 00 00 00 58 11 00 00 00 00 00 00 00 00 00 00 80 3E 84 6D 76 76 1E 80 ... 16000 28036 30326 32798 31877.4477 D1 00 42 01 80 00 3F 00 00 00 00 00 65 11 00 00 00 00 00 00 00 00 00 00 80 3E 84 6C 6E 97 1C 80 16000 27780 38766 32796 36354.4473 D1 00 42 01 80 00 3F 00 00 00 00 00 61 11 00 00 00 00 00 00 00 00 00 00 80 3E 84 6D 6C 6E 1E 80 16000 28036 28268 32798 40827.4472 D1 00 42 01 80 00 3F 00 00 00 00 00 60 11 00 00 00 00 00 00 00 00 00 00 80 3E 84 6D 6C 6E 1E 80 16000 28036 28268 32798 45299.4437 D1 00 42 01 80 00 3F 00 00 00 00 00 3D 11 00 00 00 00 00 00 00 00 00 00 80 3E 84 6D 6C 6E 1E 80 16000 28036 28268 32798 49736.0463 D1 00 42 01 80 00 3F 00 00 00 00 00 B7 01 00 00 00 00 00 00 00 00 00 00 80 3E 04 1F 80 3E 00 80 16000 7940 16000 32768 50199.4251 D1 00 42 01 80 00 3F 00 00 00 00 00 83 10 00 00 00 00 00 00 00 00 00 00 80 3E 84 6D 6C 6E 1E 80 16000 28036 28268 32798 54450.4350 D1 00 42 01 80 00 3F 00 00 00 00 00 E6 10 00 00 00 00 00 00 00 00 00 00 80 3E 84 6D 6C 6E 1E 80 16000 28036 28268 32798 58800.4278 D1 00 42 01 80 00 3F 00 00 00 00 00 9E 10 00 00 00 00 00 00 00 00 00 00 80 3E 84 6D 6C 6E 1E 80 16000 28036 28268 32798 63078.4329 D1 00 42 01 80 00 3F 00 00 00 00 00 D1 10 00 00 00 00 00 00 00 00 00 00 80 3E 84 6D 6C 6E 1E 80 16000 28036 28268 32798 67407.4329 D1 00 42 01 80 00 3F 00 00 00 00 00 D1 10 00 00 00 00 00 00 00 00 00 00 80 3E 84 6D 6C 6E 1E 80 16000 28036 28268 32798 IDENTICAL IDENTICAL IDENTICAL LE SIZE? IDENTICAL IDENTICAL V 21102801 4128896 0 vv [209, 322] [128, 63] [0,0] ``` Remainder of each block is highly variable, so probably pixel data, and (some of?) these 32 bytes are header data bytes 4-7 could be dimension? Tiles are 64x64 pixels (I think?), displayed in an isometric fashion, so I am wrong or it's something else. bytes 12 & 13 as LE 16-bit (or 12-15 as LE 32-bit) is always 24 bytes under full block size. Perhaps we have 24 bytes of header and that's the header-exclusive pixel data size? Last 8 bits would then actually be the start of the image, but there's a lot of similarity between them... It smells a bit like a DIB: | Offset | Size | Example | Purpose | | 0 | 2 | D1 00 (209) | ? | 2 | 2 | 41 01 (321) | ? | 4 | 2 | 80 00 (128) | Width? | | 6 | 2 | 3F 00 (63) | Height? | 8 | 4 | 00 00 00 00 | Could be pitch / stride? | 12 | 4 | D1 10 00 00 | Size of pixel data | 16 | 4 | 00 00 00 00 | ? | 20 | 4 | 00 00 00 00 | ? Now, how to convert the pixel data to an image?!?! Average bits per pixel seems to be less-than-1, e.g. for crates.obj: ``` 2018/03/16 21:21:37 CFrame 0: w=76 h=57 sz=3008 w*h=4332 bpp=0.6943674976915974 2018/03/16 21:21:37 CFrame 1: w=76 h=57 sz=2988 w*h=4332 bpp=0.6897506925207756 2018/03/16 21:21:37 CFrame 2: w=108 h=73 sz=5185 w*h=7884 bpp=0.6576610857432775 2018/03/16 21:21:37 CFrame 3: w=107 h=73 sz=5208 w*h=7811 bpp=0.6667520163871463 2018/03/16 21:21:37 CFrame 4: w=109 h=95 sz=7639 w*h=10355 bpp=0.7377112506035731 2018/03/16 21:21:37 CFrame 5: w=109 h=95 sz=7620 w*h=10355 bpp=0.7358763882182521 2018/03/16 21:21:37 CFrame 6: w=76 h=57 sz=2996 w*h=4332 bpp=0.6915974145891043 2018/03/16 21:21:37 CFrame 7: w=76 h=57 sz=2964 w*h=4332 bpp=0.6842105263157895 2018/03/16 21:21:37 CFrame 8: w=108 h=73 sz=5148 w*h=7884 bpp=0.6529680365296804 2018/03/16 21:21:37 CFrame 9: w=107 h=73 sz=5153 w*h=7811 bpp=0.659710664447574 2018/03/16 21:21:37 CFrame 10: w=109 h=95 sz=7423 w*h=10355 bpp=0.7168517624336069 2018/03/16 21:21:37 CFrame 11: w=109 h=95 sz=7487 w*h=10355 bpp=0.7230323515210043 ``` So some form of compression? RLE? How does WH40K_TD.EXE draw these?