DataFile: add API to access spriteflags count/offset
[rofl0r-agsutils.git] / DataFile.c
blobc2a2c0c55804b8eb047d337801387087be65ea66
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_close(ADF* a) {
9 AF_close(a->f);
12 ASI *ADF_get_global_script(ADF* a) {
13 return &a->globalscript;
16 ASI *ADF_get_dialog_script(ADF* a) {
17 return &a->dialogscript;
20 ASI *ADF_get_script(ADF* a, size_t index) {
21 if(index >= a->scriptcount) return 0;
22 return &a->scripts[index];
25 size_t ADF_get_scriptcount(ADF* a) {
26 return a->scriptcount;
29 typedef enum interaction_type {
30 it_char = 0,
31 it_inventory,
32 it_max
33 } interaction_type;
35 static int ADF_read_interaction(ADF *a, interaction_type t) {
36 /* deserialize_interaction_scripts */
37 static const size_t iter_start[it_max] = {
38 [it_char] = 0,
39 [it_inventory] = 1,
41 size_t *countmap[it_max] = {
42 [it_char] = &a->game.charactercount,
43 [it_inventory] = &a->game.inventorycount,
44 }, l = *countmap[t], i = iter_start[t];
45 char buf[200];
46 for(; i < l; i++) {
47 size_t j = 0, evcnt = AF_read_uint(a->f);
48 for(; j < evcnt; j++) {
49 if(!AF_read_string(a->f, buf, 200)) return 0; /* function names of interaction scripts */
52 return 1;
55 static int deserialize_command_list(ADF *a) {
56 size_t l = AF_read_uint(a->f);
57 AF_read_uint(a->f); /*timesrun*/
58 size_t i;
59 char childs[1024];
60 assert(l < sizeof(childs));
61 for(i = 0; i < l; i++) {
62 AF_read_uint(a->f); // unused
63 AF_read_uint(a->f); // type
64 size_t j = 0;
65 for(; j < 5; j++) {
66 char buf[4];
67 if(1 != AF_read(a->f, buf, 1)) // valType
68 return 0;
69 if(3 != AF_read(a->f, buf, 3)) // padding
70 return 0;
71 AF_read_uint(a->f); // val
72 AF_read_uint(a->f); // extra
74 childs[i] = !!AF_read_uint(a->f); // children
75 AF_read_uint(a->f); // parent
77 for(i = 0; i < l; i++) {
78 if(childs[i]) deserialize_command_list(a);
80 return 1;
83 static int ADF_read_interaction2x(ADF *a, interaction_type t) {
84 /* deserialize_new_interaction */
85 size_t *countmap[it_max] = {
86 [it_char] = &a->game.charactercount,
87 [it_inventory] = &a->game.inventorycount,
88 }, l = *countmap[t], i = 0;
89 for(; i < l; i++) {
90 int response[32];
91 if(AF_read_uint(a->f) != 1) continue;
92 size_t evcnt = AF_read_uint(a->f);
93 assert(evcnt <= 30);
94 AF_read_junk(a->f, evcnt * sizeof(int)); /*event types */
95 size_t j = 0;
96 for(; j < evcnt; j++)
97 response[j] = AF_read_int(a->f);
98 for(j = 0; j < evcnt; j++)
99 if(response[j]) {
100 if(!deserialize_command_list(a))
101 return 0;
104 return 1;
107 static int ADF_read_gamebase(ADF *a) {
108 /* acroom.h: 2881. void ReadFile() */
109 size_t l;
110 unsigned x;
111 char game_name[52];
112 l = 50 /* game name */ + 2 /*padding*/;
113 if(!AF_read(a->f, game_name, l)) return 0;
114 /* 100 options */
115 if(!AF_read_junk(a->f, 100*4)) return 0;
116 l = 256; /* 256 "paluses", unsigned char*/
117 if(!AF_read_junk(a->f, l)) return 0;
118 l = 256 * 4; /*sizeof color, read into defpal*/
119 if(!AF_read_junk(a->f, l)) return 0;
120 a->game.viewcount = AF_read_uint(a->f);
121 a->game.charactercount = AF_read_uint(a->f);
122 AF_read_uint(a->f); /* character of player*/
123 AF_read_uint(a->f); /* totalscore*/
124 a->game.inventorycount = AF_read_ushort(a->f);
125 AF_read_ushort(a->f); /* padding */
126 a->game.dialogcount = AF_read_uint(a->f);
127 AF_read_uint(a->f); /* numdlgmessage*/
128 a->game.fontcount = AF_read_uint(a->f);
129 AF_read_uint(a->f); /* color depth*/
130 AF_read_uint(a->f); /* target_win */
131 AF_read_uint(a->f);/* dialog_bullet */
132 AF_read_ushort(a->f);/* hotdot */
133 AF_read_ushort(a->f);/* hotdotouter */
134 AF_read_uint(a->f);/* uniqueid */
135 AF_read_uint(a->f);/* numgui */
136 a->game.cursorcount = AF_read_uint(a->f);
137 x = AF_read_uint(a->f);/* default_resolution */
138 if(a->version >= 43 /* 3.3.0 */ && x == 8 /* custom resolution */) {
139 /* 2 ints with the custom width and height */
140 if(!AF_read_junk(a->f, 8)) return 0;
142 AF_read_uint(a->f);/* default_lipsync_frame */
143 AF_read_uint(a->f);/* invhotdotsprite */
144 l = 4 * 17; /* reserved */
145 if(!AF_read_junk(a->f, l)) return 0;
146 l = 500 * 4 /* 500 global message numbers */;
147 if(!AF_read_junk(a->f, l)) return 0;
148 a->game.hasdict = !!AF_read_int(a->f);/* dict */
149 AF_read_uint(a->f);/* globalscript */
150 AF_read_uint(a->f);/* chars */
151 AF_read_uint(a->f);/* compiled_script */
152 return 1;
155 static int ADF_read_dictionary(ADF *a) {
156 /* acroom.h:1547 */
157 size_t i,l = AF_read_uint(a->f);
158 for(i = 0; i < l; i++) {
159 /* length of encrypted string */
160 size_t e = AF_read_uint(a->f);
161 if(!AF_read_junk(a->f, e)) return 0;
162 AF_read_short(a->f); /* wordnum */
164 return 1;
168 static int ADF_read_view(ADF *a) {
169 (void) a;
170 return 1;
173 static int ADF_read_view2x(ADF *a) {
174 (void) a;
175 return 1;
178 int ADF_find_datafile(const char *dir, char *fnbuf, size_t flen)
180 size_t l = strlen(dir);
181 if(l >= flen - 20) return 0;
182 memcpy(fnbuf, dir, l);
183 char* p = fnbuf + l;
184 *p = '/'; p++;
185 memcpy(p, "game28.dta", sizeof("game28.dta"));
186 AF f;
187 if(!AF_open(&f, fnbuf)) {
188 memcpy(p, "ac2game.dta", sizeof("ac2game.dta"));
189 if(!AF_open(&f, fnbuf)) return 0;
191 AF_close(&f);
192 return 1;
195 int ADF_open(ADF* a, const char *filename) {
196 char fnbuf[512];
197 size_t l, i;
199 memset(a, 0, sizeof(*a));
200 a->f = &a->f_b;
202 if(!AF_open(a->f, filename)) return 0;
204 if(30 != AF_read(a->f, fnbuf, 30)) {
205 err_close:
206 AF_close(a->f);
207 return 0;
209 if(memcmp("Adventure Creator Game File v2", fnbuf, 30)) goto err_close;
210 a->version = AF_read_int(a->f);
211 /* here comes some version string with minor, major - we dont need it */
212 /* FIXME? newer ags does this only with version >= 12 */
213 /* 4 bytes containing the length of the string, followed by the string */
214 l = AF_read_uint(a->f);
215 if(l > 20) goto err_close;
216 if(l != (size_t) AF_read(a->f, fnbuf, l)) goto err_close;
218 /* main_game_file.cpp:OpenMainGameFileBase */
219 if(a->version >= 48 /* kGameVersion_341 */) {
220 /* latest ags now has placed an int here: number of required capabilities */
221 l = AF_read_uint(a->f);
222 /* followed by l int/string pairs */
223 for(i=0; i<l; i++) {
224 size_t len = AF_read_uint(a->f);
225 if(!AF_read_junk(a->f, len)) goto err_close;
228 if(!ADF_read_gamebase(a)) goto err_close;
229 if(a->version > 32) {
230 l = 40 /* guid */ + 20 /*savegame extension*/ + 50 /*savegame dir*/;
231 if(l != (size_t) AF_read(a->f, fnbuf, l)) goto err_close;
233 if(a->version < 50 /* 3.5.0 */) {
234 l = a->game.fontcount * 2; /* fontflags and fontoutline arrays [each 1b/font] */
235 if(!AF_read_junk(a->f, l)) goto err_close;
236 if(a->version >= 48) {
237 /* version == 3.4.1 have YOffset and versions >= 3.4.1.2 < 3.5.0 have YOffeset and LineSpacing ints */
238 l = a->game.fontcount * 4;
239 if(a->version == 49) l*=2;
240 if(!AF_read_junk(a->f, l)) goto err_close;
242 } else {
243 /* 3.5.0 has 5 ints per font, see gamesetupstruct.cpp */
244 l = a->game.fontcount * 4 * 5;
245 if(!AF_read_junk(a->f, l)) goto err_close;
247 // number of sprites
248 l = a->numsprites = (a->version < 24) ? 6000 : AF_read_uint(a->f);
249 a->spriteflagsstart = AF_get_pos(a->f);
250 // array of spriteflags (1 char each, max: MAX_SPRITES==30000)
251 if(!AF_read_junk(a->f, l)) goto err_close;
252 l = 68 * a->game.inventorycount; /* sizeof(InventoryItemInfo) */
253 if(!AF_read_junk(a->f, l)) goto err_close;
255 l = 24 /* sizeof(MouseCursor) */ * a->game.cursorcount;
256 if(!AF_read_junk(a->f, l)) goto err_close;
257 if(a->version > 32) {
258 if(!ADF_read_interaction(a, it_char)) goto err_close;
259 if(!ADF_read_interaction(a, it_inventory)) goto err_close;
260 } else {
261 if(!ADF_read_interaction2x(a, it_char)) goto err_close;
262 if(!ADF_read_interaction2x(a, it_inventory)) goto err_close;
263 a->globalvarcount = AF_read_uint(a->f);
264 l = 28 /* sizeof(InteractionVariable)*/ * a->globalvarcount;
265 if(!AF_read_junk(a->f, l)) goto err_close;
267 if(a->game.hasdict)
268 if(!ADF_read_dictionary(a)) goto err_close;
269 a->scriptstart = AF_get_pos(a->f);
270 if(!ASI_read_script(a->f, &a->globalscript)) goto err_close;
271 if(a->version > 37)
272 if(!ASI_read_script(a->f, &a->dialogscript)) goto err_close;
273 if(a->version >= 31) {
274 a->scriptcount = AF_read_uint(a->f);
275 for(l = 0; l < a->scriptcount; l++)
276 if(!ASI_read_script(a->f, &a->scripts[l])) goto err_close;
278 a->scriptend = AF_get_pos(a->f);
279 /* at this point we have everything we need */
280 return 1;
282 if(a->version > 31) {
283 for(l = 0; l < a->game.viewcount; l++)
284 if(!ADF_read_view(a)) goto err_close;
285 } else {
286 for(l = 0; l < a->game.viewcount; l++)
287 if(!ADF_read_view2x(a)) goto err_close;
289 if(a->version <= 19) {
290 /* skip junk */
291 l = AF_read_uint(a->f) * 0x204;
292 if(!AF_read_junk(a->f, l)) goto err_close;
294 /* ... we are around line 11977 in ac.cpp at this point*/
295 return 1;