1 /* Sarien - A Sierra AGI resource interpreter engine
2 * Copyright (C) 1999-2001 Stuart George and Claudio Matsuoka
3 *
4 * $Id: agi_v3.c,v 1.36 2001/09/11 06:14:51 jpenner Exp $
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; see docs/COPYING for further details.
9 */
10
11 #ifndef PALMOS
12
13 #include <stdio.h>
14 #include <string.h>
15 #include "sarien.h"
16 #include "agi.h"
17 #include "lzw.h"
18
19 static int agi_v3_init (void);
20 static int agi_v3_deinit (void);
21 static int agi_v3_detect_game (char *);
22 static int agi_v3_load_resource (int, int);
23 static int agi_v3_unload_resource (int, int);
24 static int agi_v3_load_objects(char *);
25 static int agi_v3_load_words(char *);
26
27 struct agi_loader agi_v3 = {
28 3,
29 0,
30 agi_v3_init,
31 agi_v3_deinit,
32 agi_v3_detect_game,
33 agi_v3_load_resource,
34 agi_v3_unload_resource,
35 agi_v3_load_objects,
36 agi_v3_load_words
37 };
38
39
agi_v3_detect_game(char * gn)40 int agi_v3_detect_game (char *gn)
41 {
42 int ec = err_Unk;
43 char x[MAX_PATH], *xname, *path;
44 int l;
45
46 _D ("(\"%s\")", gn);
47 strncpy (game.dir, gn, MAX_PATH);
48
49 strcpy (x, "*vol.0");
50 path = fixpath (GAMEDIR, x);
51
52 _D (_D_WARN "path = %s", path);
53 if (file_isthere (path)) {
54 _D(_D_WARN "getting xname for path = %s", path);
55 xname = file_name (path);
56 /* remove the DIR from xname */
57 l = strlen (xname);
58 if (l >= 5)
59 l -= 5;
60 xname[l] = 0;
61 strncpy (game.name, xname, 8);
62 _D (_D_WARN "game.name = %s", game.name);
63
64 agi_v3.int_version = 0x3149; /* setup for 3.002.149 */
65 ec = v3id_game();
66 } else {
67 _D (_D_CRIT "not found");
68 ec = err_InvalidAGIFile;
69 }
70
71 return ec;
72 }
73
74
agi_v3_load_dir(struct agi_dir * agid,FILE * fp,UINT32 offs,UINT32 len)75 static int agi_v3_load_dir (struct agi_dir *agid, FILE *fp, UINT32 offs, UINT32 len)
76 {
77 int ec = err_OK;
78 UINT8 *mem;
79 unsigned int i;
80
81 fseek (fp, offs, SEEK_SET);
82 if ((mem = malloc (len + 32)) != NULL) {
83 fread(mem, 1, len, fp);
84
85 /* set all directory resources to gone */
86 for(i = 0; i < MAX_DIRS; i++) {
87 agid[i].volume = 0xff;
88 agid[i].offset = _EMPTY;
89 }
90
91 /* build directory entries */
92 for(i = 0; i < len; i += 3) {
93 agid[i / 3].volume = hilo_getbyte (mem + i) >> 4;
94 agid[i / 3].offset = hilo_getpword (mem+i) & (UINT32)_EMPTY;
95 }
96
97 free(mem);
98 } else {
99 ec = err_NotEnoughMemory;
100 }
101
102 return ec;
103 }
104
105
106 struct agi3vol {
107 UINT32 sddr;
108 UINT32 len;
109 };
110
agi_v3_init(void)111 int agi_v3_init (void)
112 {
113 int ec = err_OK;
114 struct agi3vol agi_vol3[4];
115 int i;
116 UINT16 xd[4];
117 FILE *fp;
118 char *path;
119
120 path = fixpath (GAMEDIR, DIR_);
121
122 if ((fp = fopen(path, "rb")) == NULL) {
123 printf ("Failed to open \"%s\"\n", path);
124 return err_BadFileOpen;
125 }
126 /* build offset table for v3 directory format */
127 fread (&xd, 1, 8, fp);
128 fseek (fp, 0, SEEK_END);
129
130 for(i = 0; i < 4; i++)
131 agi_vol3[i].sddr = lohi_getword((UINT8 *)&xd[i]);
132
133 agi_vol3[0].len = agi_vol3[1].sddr - agi_vol3[0].sddr;
134 agi_vol3[1].len = agi_vol3[2].sddr - agi_vol3[1].sddr;
135 agi_vol3[2].len = agi_vol3[3].sddr - agi_vol3[2].sddr;
136 agi_vol3[3].len = ftell(fp) - agi_vol3[3].sddr;
137
138 if (agi_vol3[3].len > 256 * 3)
139 agi_vol3[3].len = 256 * 3;
140
141 fseek(fp, 0, SEEK_SET);
142
143 /* read in directory files */
144 ec = agi_v3_load_dir (game.dir_logic, fp, agi_vol3[0].sddr,
145 agi_vol3[0].len);
146
147 if(ec == err_OK) {
148 ec = agi_v3_load_dir (game.dir_pic, fp, agi_vol3[1].sddr,
149 agi_vol3[1].len);
150 }
151
152 if(ec == err_OK) {
153 ec = agi_v3_load_dir (game.dir_view, fp, agi_vol3[2].sddr,
154 agi_vol3[2].len);
155 }
156
157 if(ec == err_OK) {
158 ec = agi_v3_load_dir (game.dir_sound, fp, agi_vol3[3].sddr,
159 agi_vol3[3].len);
160 }
161
162 return ec;
163 }
164
165
agi_v3_deinit()166 int agi_v3_deinit ()
167 {
168 int ec=err_OK;
169
170 #if 0
171 /* unload words */
172 agi_v3_unload_words();
173
174 /* unload objects */
175 agi_v3_unload_objects();
176 #endif
177
178 return ec;
179 }
180
181
agi_v3_unload_resource(int t,int n)182 int agi_v3_unload_resource (int t, int n)
183 {
184 switch (t) {
185 case rLOGIC:
186 unload_logic (n);
187 break;
188 case rPICTURE:
189 unload_picture (n);
190 break;
191 case rVIEW:
192 unload_view (n);
193 break;
194 case rSOUND:
195 unload_sound(n);
196 break;
197 }
198
199 return err_OK;
200 }
201
202
203 /*
204 * This function does noting but load a raw resource into memory,
205 * if further decoding is required, it must be done by another
206 * routine.
207 *
208 * NULL is returned if unsucsessful.
209 */
210
agi_v3_load_vol_res(struct agi_dir * agid)211 UINT8* agi_v3_load_vol_res (struct agi_dir *agid)
212 {
213 char x[MAX_PATH], *path;
214 UINT8 *data = NULL, *comp_buffer;
215 FILE *fp;
216
217 _D ("(%p)", agid);
218 sprintf (x, "vol.%i", agid->volume);
219 path = fixpath (GAMEDIR, x);
220
221 if (agid->offset != _EMPTY && (fp = fopen((char*)path, "rb")) != NULL) {
222 fseek (fp, agid->offset, SEEK_SET);
223 fread (&x, 1, 7, fp);
224
225 if (hilo_getword((UINT8 *)x) != 0x1234) {
226 #if 0
227 /* FIXME */
228 deinit_video_mode();
229 #endif
230 printf("ACK! BAD RESOURCE!!!\n");
231 exit(0);
232 }
233
234 agid->len = lohi_getword((UINT8 *)x + 3);/* uncompressed size */
235 agid->clen = lohi_getword((UINT8 *)x + 5);/* compressed len */
236
237 comp_buffer = calloc (1, agid->clen + 32);
238 fread (comp_buffer, 1, agid->clen, fp);
239
240 if (x[2] & 0x80 || agid->len == agid->clen) {
241 /* do not decompress */
242 data = comp_buffer;
243
244 #if 0
245 /* CM: added to avoid problems in
246 * convert_v2_v3_pic() when clen > len
247 * e.g. Sierra demo 4, first picture
248 * (Tue Mar 16 13:13:43 EST 1999)
249 */
250 agid->len = agid->clen;
251
252 /* Now removed to fix Gold Rush! in demo4 */
253 #endif
254 } else {
255 /* it is compressed */
256 data = calloc (1, agid->len + 32);
257 LZW_expand (comp_buffer, data, agid->len);
258 free (comp_buffer);
259 agid->flags |= RES_COMPRESSED;
260 }
261
262 fclose(fp);
263 } else {
264 /* we have a bad volume resource */
265 /* set that resource to NA */
266 agid->offset = _EMPTY;
267 }
268
269 return data;
270 }
271
272
273 /*
274 * Loads a resource into memory, a raw resource is loaded in
275 * with above routine, then further decoded here.
276 */
277
agi_v3_load_resource(int t,int n)278 int agi_v3_load_resource (int t, int n)
279 {
280 int ec = err_OK;
281 UINT8 *data = NULL;
282
283 if (n > MAX_DIRS)
284 return err_BadResource;
285
286 switch (t) {
287 case rLOGIC:
288 /* load resource into memory, decrypt messages at the end
289 * and build the message list (if logic is in memory)
290 */
291 if (~game.dir_logic[n].flags & RES_LOADED) {
292 /* if logic is already in memory, unload it */
293 agi_v3.unload_resource (rLOGIC, n);
294
295 /* load raw resource into data */
296 data = agi_v3_load_vol_res (&game.dir_logic[n]);
297 game.logics[n].data=data;
298
299 /* uncompressed logic files need to be decrypted */
300 if (data != NULL) {
301 /* resloaded flag gets set by decode logic */
302 /* needed to build string table */
303 ec = decode_logic(n);
304 game.logics[n].sIP=2;
305 } else {
306 ec=err_BadResource;
307 }
308
309 /*logics[n].sIP=2;*/ /* saved IP = 2 */
310 /*logics[n].cIP=2;*/ /* current IP = 2 */
311
312 game.logics[n].cIP = game.logics[n].sIP;
313 }
314
315 /* if logic was cached, we get here */
316 /* reset code pointers incase it was cached */
317
318 game.logics[n].cIP = game.logics[n].sIP;
319 break;
320 case rPICTURE:
321 /* if picture is currently NOT loaded *OR* cacheing is off,
322 * unload the resource (caching==off) and reload it
323 */
324 if (~game.dir_pic[n].flags & RES_LOADED) {
325 agi_v3.unload_resource (rPICTURE, n);
326 data = agi_v3_load_vol_res (&game.dir_pic[n]);
327 if (data != NULL) {
328 data = convert_v3_pic (data,
329 game.dir_pic[n].len);
330 game.pictures[n].rdata = data;
331 game.dir_pic[n].flags |= RES_LOADED;
332 } else {
333 ec=err_BadResource;
334 }
335 }
336 break;
337 case rSOUND:
338 if (game.dir_sound[n].flags & RES_LOADED)
339 break;
340
341 if ((data = agi_v3_load_vol_res (&game.dir_sound[n])) != NULL) {
342 game.sounds[n].rdata = data;
343 game.dir_sound[n].flags |= RES_LOADED;
344 decode_sound (n);
345 } else {
346 ec = err_BadResource;
347 }
348 break;
349 case rVIEW:
350 /* Load a VIEW resource into memory...
351 * Since VIEWS alter the view table ALL the time can we
352 * cache the view? or must we reload it all the time?
353 */
354 /* load a raw view from a VOL file into data */
355 if (game.dir_view[n].flags & RES_LOADED)
356 break;
357
358 agi_v3.unload_resource (rVIEW, n);
359 if ((data = agi_v3_load_vol_res (&game.dir_view[n])) != NULL) {
360 game.views[n].rdata = data;
361 game.dir_view[n].flags |= RES_LOADED;
362 ec = decode_view(n);
363 } else {
364 ec = err_BadResource;
365 }
366 break;
367 default:
368 ec = err_BadResource;
369 break;
370 }
371
372 return ec;
373 }
374
agi_v3_load_objects(char * fname)375 static int agi_v3_load_objects(char *fname)
376 {
377 return load_objects(fname);
378 }
379
agi_v3_load_words(char * fname)380 static int agi_v3_load_words(char *fname)
381 {
382 return load_words(fname);
383 }
384
385 #endif /* PALMOS */
386