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