1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
//! Everything related to parsing and building of GameMaker data files.
//!
//! GameMaker is a Game Engine created by YoYoGames.
//! You will see the abbreviations "GM" and "YY" a lot.
//!
//! GameMaker has its own programming language called "GML".
//! This language is (usually\*) compiled to GML bytecode which
//! is then run in a VM; making it portable to every platform.
//!
//! # Distinction between data file and runner
//!
//! A compiled GameMaker game consists of two main components:
//! * The data file
//! * The runner
//!
//! The data file (typically named `data.xxx` or `game.xxx`)
//! contains all the game's static resources in a binary format.
//! Common file extensions include `.win`, `.unx`, `.droid`, and `.ios`
//! for their respective platforms,
//! though YoYoGames sometimes uses `.win` for console targets as well.
//!
//! This data file stores virtually all game content, including:
//! * Textures and sprite data
//! * Audio files
//! * Rooms
//! * Game objects
//! * GML bytecode (unless YYC)
//! * Other game assets and configuration
//!
//! **This library (LibGM) provides tools to parse and modify these GameMaker
//! data files.**
//!
//! The runner is the executable program that loads and runs the data file. It
//! handles:
//! * Input processing (keyboard, mouse, controllers)
//! * Video and audio output
//! * Game state management
//! * Real-time game execution
//!
//! The runner is a reusable\* executable that can run any
//! GameMaker game built for the same version.
//!
//! The data file contains the unique content and logic that defines your
//! specific game. When launched, the runner loads the data file and executes
//! the game contained within it.
//!
//! # VM vs YYC
//! You may have noticed some stars regarding bytecode while reading.
//! This is because YoYoGames has created `YYC` ([YoYo Compiler](https://manual.gamemaker.io/monthly/en/Settings/YoYo_Compiler.htm)).
//! When exporting their game, game developers can choose between regular VM
//! output or YYC output.
//!
//! In VM mode, the GML source code is compiled to their own bytecode that
//! then gets executed by the runner's special GML Virtual Machine.
//! The VM bytecode is stored in the data file and is therefore
//! able to be parsed, decompiled and modified by LibGM.
//!
//! In YYC, the GML source code is converted to C code
//! and then compiled by GCC (TODO: not sure).
//! The main point is:
//! The data file doesn't store anything code related for YYC.
//! Instead, all the game's code is compiled to machine code in the specialized
//! runner. In this case, the runner does not execute bytecode in a VM but
//! instead executes compiled machine code directly embedded in its executable
//! file. Therefore, YYC runners are not portable to other games.
//!
//!
//! ## Data file format
//! As I already said, the GameMaker data file format is binary.
//! (It only contains text for chunk names and in the `STRG` chunk.)
//!
//! The data file consists of "chunks", although the name "chunk" may be a bit
//! misleading, as these chunks do not have a fixed size.
//! > Note: These are the same chunks you see in the console
//! > output in GameMaker Studio when building your game.
//!
//! Every chunk has a 4 character name that indicates what it stores.
//! (For Example: `SPRT` stores sprites, `ROOM` stores rooms, `OBJT` stores game
//! objects, etc.) These chunk names are hardcoded (the game dev does not choose
//! them). New ones are only added when YoYoGames adds a major feature like
//! particles.
//!
//! Every chunk only exists once. There is only one `ROOM` chunk
//! that stores **all** data related to GameMaker rooms.
//!
//! The order of these chunks does not matter.
//! > TODO: Possible exception: `GEN8` needs to be first, at least for
//! > `UndertaleModLib`?
//!
//! Now, onto the actual structure of a data file.
//! Every data file begins with the 4 letters `FORM`.
//! > Note: On Big endian targets, it is `MROF` instead, as chunk names are
//! > reversed there.
//! > Do not worry about this though, big endian is very legacy
//! > and is not relevant for modern target platforms.
//!
//! After the 4 byte `FORM` string, you are met with the (remaining) data
//! length. This equivalent to the size of the data file minus 8 (the FORM
//! header is excluded). This data length is specified as a 32-bit integer
//! (`u32`).
//!
//! Then, you will see the first chunk.
//! > Note: Some GameMaker datamining software (like UndertaleModLib) also see
//! > FORM as a chunk.
//! > I do not do this, as this is the only time chunks have any sort of
//! > hierarchy
//! > and it would unnecessarily make things more complicated.
//!
//! Chunks are structured similar to FORM:
//! * Chunk name [4 bytes]
//! * Chunk length (excluding this header) [4 bytes]
//! * Chunk data [n bytes]
//! * Potentially chunk padding (nullbytes), depending on the platform and
//! version.
//!
//! Most chunks are of "pointer list type"; UndertaleModLib calls them
//! `UndertaleListChunk`. To understand what that means, we first need to look
//! at list structures in data files.
//!
//! # Lists
//!
//! There are two common list types:
//! * Simple lists
//! * Pointer lists
//!
//! Simple lists have the following structure:
//! * Element count [4 bytes]
//! * Element #1 [n bytes]
//! * Element #2 [n bytes]
//! * ...
//! * Element #count-1 [n bytes]
//! * Element #count [n bytes]
//!
//! In other words, they store their element count, followed by the element
//! data. Simple indeed. (TODO: are the element sizes always the same?)
//!
//! Pointer lists have the following structure:
//! * Element count [4 bytes]
//! * Pointer to element #1 [4 bytes]
//! * Pointer to element #2 [4 bytes]
//! * ...
//! * Pointer to element #count-1 [4 bytes]
//! * Pointer to element #count [4 bytes]
//! * Element #1 [n bytes]
//! * Element #2 [n bytes]
//! * ...
//! * Element #count-1 [n bytes]
//! * Element #count [n bytes]
//!
//! In other words, they store their element count, a list of 32-bit pointers to
//! the corresponding element data and then their actual elements' data.
//!
//! A pointer is just a `u32` number. It specifies the absolute position where
//! something is located in the data file.
//! > Note: Null/Zeroed pointers are always invalid, as they would point to the
//! > FORM string.
//! > YoYoGames has recently implemented a system to remove unused assets,
//! > which nulls out pointers that point to unused elements.
//! > This is a pain in the ass for datamining libraries like mine,
//! > and LibGM still support them yet.
//!
//! *A bit of technical insight to pointer lists:*
//! You might be thinking how pointer lists store redundant data
//! and take up more space than simple lists.
//! This is true! They are indeed redundant.
//! However, some GameMaker elements may have a varying data length,
//! which makes simple pointer arithmetic impossible in simple lists.
//! The runner would have to parse all elements
//! sequentially just to get to the one it actually wants to read.
//! Depending on how those elements are typically used, this can be very
//! inefficient, which is why YoYoGames includes pointers to the element in
//! pointer lists.
//!
//!
//! # Chunk types
//! Now that you know what pointer lists are, I can explain different "chunk
//! types". Most chunks store a collection of elements and are therefore a giant
//! pointer list. For example: `ROOM` stores multiple rooms, `FONT` stores
//! multiple fonts, etc. There are a few exceptions, though.
//!
//! The `FEAT` chunk ("feature flags") consists of only a simple
//! list of string references (which are pointers anyway).
//!
//! The `GEN8` and `OPTN` contain general info and options regarding the data
//! file / game. They are called `UndertaleSingleChunk` in UndertaleModLib.
//! They do not contain a list. Instead, they are the
//! element itself and contain the fields directly.
pub use GMData;
pub use ParsingOptions;
pub use parse_bytes;
pub use parse_file;
pub use GMRef;
pub use build_bytes;
pub use build_file;
pub use GMVersion;
pub use GMVersionReq;