agsprite: fix UB
[rofl0r-agsutils.git] / DataFile.c
blob8cc2fa48d75344d597ecc760eb1e84527d49a4c6
1 #define _GNU_SOURCE
2 #include <stdlib.h>
3 #include <string.h>
4 #include <assert.h>
5 #include "DataFile.h"
6 #include "Script.h"
8 void ADF_init(ADF* a, char* dir) {
9 memset(a, 0, sizeof(*a));
10 a->f = &a->f_b;
11 a->dir = dir;
14 void ADF_close(ADF* a) {
15 AF_close(a->f);
18 ASI *ADF_get_global_script(ADF* a) {
19 return &a->globalscript;
22 ASI *ADF_get_dialog_script(ADF* a) {
23 return &a->dialogscript;
26 ASI *ADF_get_script(ADF* a, size_t index) {
27 if(index >= a->scriptcount) return 0;
28 return &a->scripts[index];
31 size_t ADF_get_scriptcount(ADF* a) {
32 return a->scriptcount;
35 typedef enum interaction_type {
36 it_char = 0,
37 it_inventory,
38 it_max
39 } interaction_type;
41 static int ADF_read_interaction(ADF *a, interaction_type t) {
42 /* deserialize_interaction_scripts */
43 static const size_t iter_start[it_max] = {
44 [it_char] = 0,
45 [it_inventory] = 1,
47 size_t *countmap[it_max] = {
48 [it_char] = &a->game.charactercount,
49 [it_inventory] = &a->game.inventorycount,
50 }, l = *countmap[t], i = iter_start[t];
51 char buf[200];
52 for(; i < l; i++) {
53 size_t j = 0, evcnt = AF_read_uint(a->f);
54 for(; j < evcnt; j++) {
55 if(!AF_read_string(a->f, buf, 200)) return 0; /* function names of interaction scripts */
58 return 1;
61 static int deserialize_command_list(ADF *a) {
62 size_t l = AF_read_uint(a->f);
63 AF_read_uint(a->f); /*timesrun*/
64 size_t i;
65 char childs[1024];
66 assert(l < sizeof(childs));
67 for(i = 0; i < l; i++) {
68 AF_read_uint(a->f); // unused
69 AF_read_uint(a->f); // type
70 size_t j = 0;
71 for(; j < 5; j++) {
72 char buf[4];
73 if(1 != AF_read(a->f, buf, 1)) // valType
74 return 0;
75 if(3 != AF_read(a->f, buf, 3)) // padding
76 return 0;
77 AF_read_uint(a->f); // val
78 AF_read_uint(a->f); // extra
80 childs[i] = !!AF_read_uint(a->f); // children
81 AF_read_uint(a->f); // parent
83 for(i = 0; i < l; i++) {
84 if(childs[i]) deserialize_command_list(a);
86 return 1;
89 static int ADF_read_interaction2x(ADF *a, interaction_type t) {
90 /* deserialize_new_interaction */
91 size_t *countmap[it_max] = {
92 [it_char] = &a->game.charactercount,
93 [it_inventory] = &a->game.inventorycount,
94 }, l = *countmap[t], i = 0;
95 for(; i < l; i++) {
96 int response[32];
97 if(AF_read_uint(a->f) != 1) continue;
98 size_t evcnt = AF_read_uint(a->f);
99 assert(evcnt <= 30);
100 AF_read_junk(a->f, evcnt * sizeof(int)); /*event types */
101 size_t j = 0;
102 for(; j < evcnt; j++)
103 response[j] = AF_read_int(a->f);
104 for(j = 0; j < evcnt; j++)
105 if(response[j]) {
106 if(!deserialize_command_list(a))
107 return 0;
110 return 1;
113 static int ADF_read_gamebase(ADF *a) {
114 /* acroom.h: 2881. void ReadFile() */
115 size_t l;
116 unsigned x;
117 char game_name[52];
118 l = 50 /* game name */ + 2 /*padding*/;
119 if(!AF_read(a->f, game_name, l)) return 0;
120 /* 100 options */
121 if(!AF_read_junk(a->f, 100*4)) return 0;
122 l = 256; /* 256 "paluses", unsigned char*/
123 if(!AF_read_junk(a->f, l)) return 0;
124 l = 256 * 4; /*sizeof color, read into defpal*/
125 if(!AF_read_junk(a->f, l)) return 0;
126 a->game.viewcount = AF_read_uint(a->f);
127 a->game.charactercount = AF_read_uint(a->f);
128 AF_read_uint(a->f); /* character of player*/
129 AF_read_uint(a->f); /* totalscore*/
130 a->game.inventorycount = AF_read_ushort(a->f);
131 AF_read_ushort(a->f); /* padding */
132 a->game.dialogcount = AF_read_uint(a->f);
133 AF_read_uint(a->f); /* numdlgmessage*/
134 a->game.fontcount = AF_read_uint(a->f);
135 AF_read_uint(a->f); /* color depth*/
136 AF_read_uint(a->f); /* target_win */
137 AF_read_uint(a->f);/* dialog_bullet */
138 AF_read_ushort(a->f);/* hotdot */
139 AF_read_ushort(a->f);/* hotdotouter */
140 AF_read_uint(a->f);/* uniqueid */
141 AF_read_uint(a->f);/* numgui */
142 a->game.cursorcount = AF_read_uint(a->f);
143 x = AF_read_uint(a->f);/* default_resolution */
144 if(a->version >= 43 /* 3.3.0 */ && x == 8 /* custom resolution */) {
145 /* 2 ints with the custom width and height */
146 if(!AF_read_junk(a->f, 8)) return 0;
148 AF_read_uint(a->f);/* default_lipsync_frame */
149 AF_read_uint(a->f);/* invhotdotsprite */
150 l = 4 * 17; /* reserved */
151 if(!AF_read_junk(a->f, l)) return 0;
152 l = 500 * 4 /* 500 global message numbers */;
153 if(!AF_read_junk(a->f, l)) return 0;
154 a->game.hasdict = !!AF_read_int(a->f);/* dict */
155 AF_read_uint(a->f);/* globalscript */
156 AF_read_uint(a->f);/* chars */
157 AF_read_uint(a->f);/* compiled_script */
158 return 1;
161 static int ADF_read_dictionary(ADF *a) {
162 /* acroom.h:1547 */
163 size_t i,l = AF_read_uint(a->f);
164 for(i = 0; i < l; i++) {
165 /* length of encrypted string */
166 size_t e = AF_read_uint(a->f);
167 if(!AF_read_junk(a->f, e)) return 0;
168 AF_read_short(a->f); /* wordnum */
170 return 1;
174 static int ADF_read_view(ADF *a) {
175 (void) a;
176 return 1;
179 static int ADF_read_view2x(ADF *a) {
180 (void) a;
181 return 1;
184 int ADF_open(ADF* a) {
185 char fnbuf[512];
186 size_t l = strlen(a->dir), i;
187 if(l >= sizeof(fnbuf) - 20) return 0;
188 memcpy(fnbuf, a->dir, l);
189 char* p = fnbuf + l;
190 *p = '/'; p++;
191 memcpy(p, "game28.dta", sizeof("game28.dta"));
192 if(!AF_open(a->f, fnbuf)) {
193 memcpy(p, "ac2game.dta", sizeof("ac2game.dta"));
194 if(!AF_open(a->f, fnbuf)) return 0;
197 if(30 != AF_read(a->f, fnbuf, 30)) {
198 err_close:
199 AF_close(a->f);
200 return 0;
202 if(memcmp("Adventure Creator Game File v2", fnbuf, 30)) goto err_close;
203 a->version = AF_read_int(a->f);
204 /* here comes some version string with minor, major - we dont need it */
205 /* FIXME? newer ags does this only with version >= 12 */
206 /* 4 bytes containing the length of the string, followed by the string */
207 l = AF_read_uint(a->f);
208 if(l > 20) goto err_close;
209 if(l != (size_t) AF_read(a->f, fnbuf, l)) goto err_close;
211 /* main_game_file.cpp:OpenMainGameFileBase */
212 if(a->version >= 48 /* kGameVersion_341 */) {
213 /* latest ags now has placed an int here: number of required capabilities */
214 l = AF_read_uint(a->f);
215 /* followed by l int/string pairs */
216 for(i=0; i<l; i++) {
217 size_t len = AF_read_uint(a->f);
218 if(!AF_read_junk(a->f, len)) goto err_close;
221 if(!ADF_read_gamebase(a)) goto err_close;
222 if(a->version > 32) {
223 l = 40 /* guid */ + 20 /*savegame extension*/ + 50 /*savegame dir*/;
224 if(l != (size_t) AF_read(a->f, fnbuf, l)) goto err_close;
226 if(a->version < 50 /* 3.5.0 */) {
227 l = a->game.fontcount * 2; /* fontflags and fontoutline arrays [each 1b/font] */
228 if(!AF_read_junk(a->f, l)) goto err_close;
229 if(a->version >= 48) {
230 /* version == 3.4.1 have YOffset and versions >= 3.4.1.2 < 3.5.0 have YOffeset and LineSpacing ints */
231 l = a->game.fontcount * 4;
232 if(a->version == 49) l*=2;
233 if(!AF_read_junk(a->f, l)) goto err_close;
235 } else {
236 /* 3.5.0 has 5 ints per font, see gamesetupstruct.cpp */
237 l = a->game.fontcount * 4 * 5;
238 if(!AF_read_junk(a->f, l)) goto err_close;
240 // number of sprites
241 l = (a->version < 24) ? 6000 : AF_read_uint(a->f);
242 if(!AF_read_junk(a->f, l)) goto err_close;
243 l = 68 * a->game.inventorycount; /* sizeof(InventoryItemInfo) */
244 if(!AF_read_junk(a->f, l)) goto err_close;
246 l = 24 /* sizeof(MouseCursor) */ * a->game.cursorcount;
247 if(!AF_read_junk(a->f, l)) goto err_close;
248 if(a->version > 32) {
249 if(!ADF_read_interaction(a, it_char)) goto err_close;
250 if(!ADF_read_interaction(a, it_inventory)) goto err_close;
251 } else {
252 if(!ADF_read_interaction2x(a, it_char)) goto err_close;
253 if(!ADF_read_interaction2x(a, it_inventory)) goto err_close;
254 a->globalvarcount = AF_read_uint(a->f);
255 l = 28 /* sizeof(InteractionVariable)*/ * a->globalvarcount;
256 if(!AF_read_junk(a->f, l)) goto err_close;
258 if(a->game.hasdict)
259 if(!ADF_read_dictionary(a)) goto err_close;
260 a->scriptstart = AF_get_pos(a->f);
261 if(!ASI_read_script(a->f, &a->globalscript)) goto err_close;
262 if(a->version > 37)
263 if(!ASI_read_script(a->f, &a->dialogscript)) goto err_close;
264 if(a->version >= 31) {
265 a->scriptcount = AF_read_uint(a->f);
266 for(l = 0; l < a->scriptcount; l++)
267 if(!ASI_read_script(a->f, &a->scripts[l])) goto err_close;
269 a->scriptend = AF_get_pos(a->f);
270 /* at this point we have everything we need */
271 return 1;
273 if(a->version > 31) {
274 for(l = 0; l < a->game.viewcount; l++)
275 if(!ADF_read_view(a)) goto err_close;
276 } else {
277 for(l = 0; l < a->game.viewcount; l++)
278 if(!ADF_read_view2x(a)) goto err_close;
280 if(a->version <= 19) {
281 /* skip junk */
282 l = AF_read_uint(a->f) * 0x204;
283 if(!AF_read_junk(a->f, l)) goto err_close;
285 /* ... we are around line 11977 in ac.cpp at this point*/
286 return 1;