stcm.md (5792B)
1 STCM 2 ==== 3 4 STCM files store bytecode for a VM used by the Re;Births. 5 6 The general structure of an STCM file is: 7 8 * Header 9 * Padding until 0x50, `GLOBAL_DATA` string, padding until 0x70. 10 * Data structures 11 * Padding until dividable by 16, `CODE_START_\0` 12 * Instructions 13 * `EXPORT_DATA\0` 14 * Export entries 15 * `COLLECTION_LINK\0` 16 * Collection link header, entries until EOF 17 18 But generally the engine do not care about it, it only follows the offsets. The 19 strings and extra padding can be removed without breaking anything. There are 20 usually a lot of dead code and unused data in the STCM files. 21 22 Header 23 ------ 24 25 ```c++ 26 struct Header 27 { 28 char magic[0x20]; 29 uint32_t export_offset; 30 uint32_t export_count; 31 uint32_t field_28; // ?? 32 uint32_t collection_link_offset; 33 }; 34 sizeof(Header) == 0x30 35 ``` 36 37 `magic` starts with `STCM2L` (where `L` likely means little-endian), after a 38 null terminated describing the build date (?) of the generator follows (ignored 39 by the game). (Like`STCM2L Apr 22 2013 19:39:01` in R;B3). 40 41 There are `export_count` count `ExportEntry` at `export_offset` and a 42 `CollectionLinkHeader` at `collection_link_offset`. 43 44 45 Export entry 46 ------------ 47 48 ```c++ 49 struct ExportEntry 50 { 51 uint32_t type; // 0 = CODE, 1 = DATA 52 char name[0x20]; 53 uint32_t offset; 54 }; 55 sizeof(ExportEntry) == 0x28 56 ``` 57 58 If `type == 0`, `offset` is an offset to an `InstructionHeader`, if `type == 1`, 59 it should be an offset to `Data` (not really used by the game). `name` is a null 60 terminated string. 61 62 Data 63 ---- 64 65 ```c++ 66 struct Data 67 { 68 uint32_t type; // 0 or 1 ?? 69 uint32_t offset_unit; 70 uint32_t field_8; 71 uint32_t length; 72 }; 73 sizeof(Data) = 0x10 74 ``` 75 76 `length` bytes of data follow the structure. `offset_unit` is `length/4` for 77 string data, 1 otherwise? 78 79 Strings are stored as null terminated strings, the length is padded to a 80 multiple of 4 (so a string of length 4 will have a terminating zero byte and 3 81 padding zero bytes at the end). 82 83 84 Instruction 85 ----------- 86 87 ```c++ 88 struct InstructionHeader 89 { 90 uint32_t is_call; // 0 or 1 91 uint32_t opcode_offset; 92 uint32_t param_count; // < 16 93 uint32_t size; 94 }; 95 sizeof(InstructionHeader) == 0x10; 96 ``` 97 98 If `is_call` is true, `opcode_offset` contains an offset to the called 99 instruction. If `is_call` is false, `opcode_offset` is simply an opcode number 100 in the VM, not an offset. 101 102 `param_count` of `Parameter` structure follows the header, followed by `size` 103 bytes of arbitrary payload (usually `Data` structures described above). After 104 the payload usually comes the next instruction. The following opcodes are known 105 to jump unconditionally, thus in this case the engine won't try to parse and 106 execute the next instruction: 0, 6. 107 108 ```c++ 109 struct Parameter 110 { 111 uint32_t param_0; 112 uint32_t param_4; 113 uint32_t param_8; 114 }; 115 sizeof(Parameter) == 0x0c 116 117 uint32_t TypeTag(uint32_t x) { return x >> 30; } 118 uint32_t Value(uint32_t x) { return x & 0x3fffffff; } 119 ``` 120 121 The meaning of the members depend on the value of `TypeTag(param_0)` (i.e. the 122 upper two bits of `param_0`. 123 124 ### `param_0` 125 126 ```c++ 127 enum Type 128 { 129 MEM_OFFSET = 0, 130 IMMEDIATE = 1, 131 INDIRECT = 2, 132 SPECIAL = 3, 133 }; 134 ``` 135 136 If `TypeTag(param_0) == MEM_OFFSET`, `Value(param_0)` (the lower 30 bits of 137 `param_0`) contains an offset to a `Data` structure, `param_4` and `param_8` 138 parsed normally. 139 140 If `TypeTag(param_0) == INDIRECT`, `Value() < 256` (and not a file offset). 141 `param_4` must be `0x40000000`. `param_8` parsed normally. 142 143 If `TypeTag(param_0) == SPECIAL`, then there are other subcases: 144 145 ```c++ 146 enum TypeSpecial 147 { 148 READ_STACK_MIN = 0xffffff00, // range MIN..MAX 149 READ_STACK_MAX = 0xffffff0f, 150 READ_4AC_MIN = 0xffffff20, // range MIN..MAX 151 READ_4AC_MAX = 0xffffff27, 152 INSTR_PTR0 = 0xffffff40, 153 INSTR_PTR1 = 0xffffff41, 154 COLL_LINK = 0xffffff42, 155 }; 156 ``` 157 158 If `(param_0 >= READ_STACK_MIN && param_0 <= READ_STACK_MAX) || (param_0 >= 159 READ_4AC_MIN && param_0 <= READ_4AC_MAX)`, then `param_4` and `param_8` must be 160 `0x40000000`. 161 162 If `param_0 == INSTR_PTR0 || param_0 == INSTR_PTR1`, then `param_4` contains an 163 offset to an another instruction, `param_8` must be `0x40000000`. 164 165 If `patam_0 == COLL_LINK`, `param_4` contains an offset to a `CollectionLink` 166 structure, `param_8` must be 8. 167 168 ### `param_4` and `param_8` 169 170 The following only applies when these parameters are "parsed normally". 171 `param_n` refers to either `param_4` or `param_8`. 172 173 If `TypeTag(param_n) == MEM_OFFSET`, `Value(param_n)` contains an offset to ???. 174 175 If `TypeTag(param_n) == IMMEDIATE || TypeTag(param_n) == INDIRECT`, 176 `Value(param_n)` contains a value. 177 178 If `TypeTag(param_n) == SPECIAL`, then `(param_n >= READ_STACK_MIN && param_n <= 179 READ_STACK_MAX) || (param_n >= READ_4AC_MIN && param_n <= READ_4AC_MAX)`. 180 181 Collection link 182 --------------- 183 184 ```c++ 185 struct CollectionLinkHeader 186 { 187 uint32_t field_00; 188 uint32_t offset; 189 uint32_t count; 190 uint32_t field_0c; 191 uint32_t field_10; 192 uint32_t field_14; 193 uint32_t field_18; 194 uint32_t field_1c; 195 uint32_t field_20; 196 uint32_t field_24; 197 uint32_t field_28; 198 uint32_t field_2c; 199 uint32_t field_30; 200 uint32_t field_34; 201 uint32_t field_38; 202 uint32_t field_3c; 203 }; 204 sizeof(CollectionLinkHeader) == 0x40); 205 ``` 206 207 The `field_*` members are always zero in the files I encountered... There are 208 `count` `CollectionLinkEntry` structures at `offset`. 209 210 211 ```c++ 212 struct CollectionLinkEntry 213 { 214 uint32_t name_0; 215 uint32_t name_1; 216 uint32_t field_08; 217 uint32_t field_0c; 218 uint32_t field_10; 219 uint32_t field_14; 220 uint32_t field_18; 221 uint32_t field_1c; 222 }; 223 sizeof(CollectionLinkEntry) == 0x20; 224 ``` 225 226 The `field_*` members are always zero in the files I encountered... `name_0` and 227 `name_1` are offsets to null terminated strings (they're generally after the 228 last entry).