1 /*  Sarien - A Sierra AGI resource interpreter engine
2  *  Copyright (C) 1999-2001 Stuart George and Claudio Matsuoka
3  *
4  *  $Id: savegame.c,v 1.46 2001/11/02 14:28:41 cmatsuoka 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 /*
12  * Savegame support by Vasyl Tsvirkunov <vasyl@pacbell.net>
13  */
14 
15 #ifndef PALMOS
16 
17 #include <stdio.h>
18 #include <string.h>
19 #ifndef __MPW__
20 #ifndef __DICE__
21 #include <sys/stat.h>
22 #endif
23 #include <sys/types.h>
24 #endif
25 #ifdef WIN32
26 #include <direct.h>
27 #endif
28 
29 #include "sarien.h"
30 #include "agi.h"
31 #include "graphics.h"
32 #include "sprite.h"
33 #include "text.h"
34 #include "savegame.h"
35 
36 #if defined(__DICE__) || defined(WIN32)
37 #  define MKDIR(a,b) mkdir(a)
38 #else
39 #  define MKDIR(a,b) mkdir(a,b)
40 #endif
41 
42 /* Image stack support */
43 struct image_stack_element
44 {
45 	UINT8 type;
46 	UINT8 pad;
47 	SINT16 parm1;
48 	SINT16 parm2;
49 	SINT16 parm3;
50 	SINT16 parm4;
51 	SINT16 parm5;
52 	SINT16 parm6;
53 	SINT16 parm7;
54 	struct image_stack_element* next;
55 };
56 
57 struct image_stack_element* image_stack = NULL;
58 
clear_image_stack(void)59 void clear_image_stack(void)
60 {
61 	struct image_stack_element* ptr = image_stack;
62 	struct image_stack_element* doomed;
63 	while(ptr)
64 	{
65 		doomed = ptr;
66 		ptr = ptr->next;
67 		free(doomed);
68 	}
69 	image_stack = NULL;
70 }
71 
record_image_stack_call(UINT8 type,SINT16 p1,SINT16 p2,SINT16 p3,SINT16 p4,SINT16 p5,SINT16 p6,SINT16 p7)72 void record_image_stack_call(UINT8 type, SINT16 p1, SINT16 p2, SINT16 p3, SINT16 p4, SINT16 p5, SINT16 p6, SINT16 p7)
73 {
74 	struct image_stack_element* pnew;
75 	struct image_stack_element* ptr;
76 
77 	pnew = (struct image_stack_element*)malloc(sizeof(struct image_stack_element));
78 	pnew->type = type;
79 	pnew->parm1 = p1;
80 	pnew->parm2 = p2;
81 	pnew->parm3 = p3;
82 	pnew->parm4 = p4;
83 	pnew->parm5 = p5;
84 	pnew->parm6 = p6;
85 	pnew->parm7 = p7;
86 	pnew->next = NULL;
87 
88 	if(image_stack)
89 	{
90 		ptr = image_stack;
91 		while(ptr->next)
92 			ptr = ptr->next;
93 		ptr->next = pnew;
94 	}
95 	else
96 		image_stack = pnew;
97 }
98 
replay_image_stack_call(UINT8 type,SINT16 p1,SINT16 p2,SINT16 p3,SINT16 p4,SINT16 p5,SINT16 p6,SINT16 p7)99 void replay_image_stack_call(UINT8 type, SINT16 p1, SINT16 p2, SINT16 p3, SINT16 p4, SINT16 p5, SINT16 p6, SINT16 p7)
100 {
101 	switch(type)
102 	{
103 	case ADD_PIC:
104 		agi_load_resource(rPICTURE, p1);
105 		decode_picture(p1, p2);
106 		break;
107 	case ADD_VIEW:
108 		agi_load_resource(rVIEW, p1);
109 		add_to_pic(p1, p2, p3, p4, p5, p6, p7);
110 		break;
111 	}
112 }
113 
114 /* */
115 
116 static char* strSig = "Sarien:";
117 
write_uint8(FILE * f,SINT8 val)118 void write_uint8(FILE* f, SINT8 val)
119 {
120 	fwrite(&val, 1, 1, f);
121 }
122 
write_sint16(FILE * f,SINT16 val)123 void write_sint16(FILE* f, SINT16 val)
124 {
125 	static UINT8 buf[2];
126 	buf[0] = (UINT8)((val>>8)&255);
127 	buf[1] = (UINT8)(val&255);
128 	fwrite(buf, 1, 2, f);
129 }
130 
write_uint16(FILE * f,UINT16 val)131 void write_uint16(FILE* f, UINT16 val)
132 {
133 	static UINT8 buf[2];
134 	buf[0] = (UINT8)((val>>8)&255);
135 	buf[1] = (UINT8)(val&255);
136 	fwrite(buf, 1, 2, f);
137 }
138 
read_uint8(FILE * f)139 UINT8 read_uint8(FILE* f)
140 {
141 	static UINT8 buf[1];
142 	fread(buf, 1, 1, f);
143 	return buf[0];
144 }
145 
read_uint16(FILE * f)146 UINT16 read_uint16(FILE* f)
147 {
148 	static UINT8 buf[2];
149 	fread(buf, 1, 2, f);
150 	return (buf[0]<<8)|buf[1];
151 }
152 
read_sint16(FILE * f)153 SINT16 read_sint16(FILE* f)
154 {
155 	static UINT8 buf[2];
156 	fread(buf, 1, 2, f);
157 	return (SINT16)((buf[0]<<8)|buf[1]);
158 }
159 
160 
write_string(FILE * f,char * s)161 void write_string(FILE* f, char* s)
162 {
163 	write_sint16(f, (SINT16)strlen(s));
164 	fwrite(s, 1, strlen(s), f);
165 }
166 
read_string(FILE * f,char * s)167 void read_string(FILE* f, char* s)
168 {
169 	SINT16 size = read_sint16(f);
170 	fread(s, 1, size, f);
171 	s[size] = (char)0;
172 }
173 
write_bytes(FILE * f,char * s,SINT16 size)174 void write_bytes(FILE* f, char* s, SINT16 size)
175 {
176 	fwrite(s, 1, size, f);
177 }
178 
read_bytes(FILE * f,char * s,SINT16 size)179 void read_bytes(FILE* f, char* s, SINT16 size)
180 {
181 	fread(s, 1, size, f);
182 }
183 
save_game(char * s,char * d)184 int save_game(char* s, char* d)
185 {
186 	SINT16 i;
187 	struct image_stack_element* ptr = image_stack;
188 	FILE* f=fopen(s, "wb");
189 
190 	if(!f)
191 		return err_BadFileOpen;
192 
193 	write_bytes(f, strSig, 8);
194 	write_string(f, d);
195 
196 	write_sint16(f, (SINT16)game.state);
197 	/* game.name */
198 	write_string(f, game.id);
199 	/* game.crc */
200 
201 	for(i=0; i<MAX_FLAGS; i++)
202 		write_uint8(f, game.flags[i]);
203 	for(i=0; i<MAX_VARS; i++)
204 		write_uint8(f, game.vars[i]);
205 
206 	write_sint16(f, (SINT16)game.horizon);
207 	write_sint16(f, (SINT16)game.line_status);
208 	write_sint16(f, (SINT16)game.line_user_input);
209 	write_sint16(f, (SINT16)game.line_min_print);
210 	/* game.cursor_pos */
211 	/* game.input_buffer */
212 	/* game.echo_buffer */
213 	/* game.keypress */
214 	write_sint16(f, (SINT16)game.input_mode);
215 	write_sint16(f, (SINT16)game.lognum);
216 
217 	write_sint16(f, (SINT16)game.player_control);
218 	write_sint16(f, (SINT16)game.quit_prog_now);
219 	write_sint16(f, (SINT16)game.status_line);
220 	write_sint16(f, (SINT16)game.clock_enabled);
221 	write_sint16(f, (SINT16)game.exit_all_logics);
222 	write_sint16(f, (SINT16)game.picture_shown);
223 	write_sint16(f, (SINT16)game.has_prompt);
224 	write_sint16(f, (SINT16)game.game_flags);
225 
226 	write_sint16(f, (SINT16)game.alt_pri);
227 	for(i=0; i<_HEIGHT; i++)
228 		write_uint8(f, game.pri_table[i]);
229 
230 	/* game.msg_box_ticks */
231 	/* game.block */
232 	/* game.window */
233 	/* game.has_window */
234 
235 	write_sint16(f, (SINT16)game.gfx_mode);
236 	write_uint8(f, game.cursor_char);
237 	write_sint16(f, (SINT16)game.color_fg);
238 	write_sint16(f, (SINT16)game.color_bg);
239 
240 	/* game.hires (#ifdef USE_HIRES) */
241 	/* game.sbuf */
242 
243 	/* game.ego_words */
244 	/* game.num_ego_words */
245 
246 	write_sint16(f, (SINT16)game.num_objects);
247 	for(i=0; i<(SINT16)game.num_objects; i++)
248 		write_sint16(f, (SINT16)object_get_location(i));
249 
250 	/* game.ev_keyp */
251 	/* game.ev_scan */
252 	for(i=0; i<MAX_WORDS1; i++)
253 		write_string(f, game.strings[i]);
254 
255 	/* record info about loaded resources */
256 	for(i=0; i<MAX_DIRS; i++)
257 	{
258 		write_uint8(f, game.dir_logic[i].flags);
259 		write_sint16(f, (SINT16)game.logics[i].sIP);
260 		write_sint16(f, (SINT16)game.logics[i].cIP);
261 	}
262 	for(i=0; i<MAX_DIRS; i++)
263 		write_uint8(f, game.dir_pic[i].flags);
264 	for(i=0; i<MAX_DIRS; i++)
265 		write_uint8(f, game.dir_view[i].flags);
266 	for(i=0; i<MAX_DIRS; i++)
267 		write_uint8(f, game.dir_sound[i].flags);
268 
269 	/* game.pictures */
270 	/* game.logics */
271 	/* game.views */
272 	/* game.sounds */
273 
274 	for(i=0; i<MAX_VIEWTABLE; i++)
275 	{
276 		struct vt_entry* v = &game.view_table[i];
277 
278 		write_uint8(f, v->step_time);
279 		write_uint8(f, v->step_time_count);
280 		write_uint8(f, v->entry);
281 		write_sint16(f, v->x_pos);
282 		write_sint16(f, v->y_pos);
283 		write_uint8(f, v->current_view);
284 
285 		/* v->view_data */
286 
287 		write_uint8(f, v->current_loop);
288 		write_uint8(f, v->num_loops);
289 
290 		/* v->loop_data */
291 
292 		write_uint8(f, v->current_cel);
293 		write_uint8(f, v->num_cels);
294 
295 		/* v->cel_data */
296 		/* v->cel_data_2 */
297 
298 		write_sint16(f, v->x_pos2);
299 		write_sint16(f, v->y_pos2);
300 
301 		/* v->s */
302 
303 		write_sint16(f, v->x_size);
304 		write_sint16(f, v->y_size);
305 		write_uint8(f, v->step_size);
306 		write_uint8(f, v->cycle_time);
307 		write_uint8(f, v->cycle_time_count);
308 		write_uint8(f, v->direction);
309 
310 		write_uint8(f, v->motion);
311 		write_uint8(f, v->cycle);
312 		write_uint8(f, v->priority);
313 
314 		write_uint16(f, v->flags);
315 
316 		write_uint8(f, v->parm1);
317 		write_uint8(f, v->parm2);
318 		write_uint8(f, v->parm3);
319 		write_uint8(f, v->parm4);
320 	}
321 
322 /* Save image stack */
323 	while(ptr)
324 	{
325 		write_uint8(f, ptr->type);
326 		write_sint16(f, ptr->parm1);
327 		write_sint16(f, ptr->parm2);
328 		write_sint16(f, ptr->parm3);
329 		write_sint16(f, ptr->parm4);
330 		write_sint16(f, ptr->parm5);
331 		write_sint16(f, ptr->parm6);
332 		write_sint16(f, ptr->parm7);
333 		ptr = ptr->next;
334 	}
335 	write_uint8(f, 0);
336 
337 	fclose(f);
338 
339 	return err_OK;
340 }
341 
load_game(char * s)342 int load_game(char* s)
343 {
344 	int i;
345 	UINT8 t;
346 	SINT16 parm[7];
347 	char sig[8];
348 	char id[8];
349 	char description[256];
350 	FILE* f=fopen(s, "rb");
351 
352 	if(!f)
353 		return err_BadFileOpen;
354 
355 	read_bytes(f, sig, 8);
356 	if(strncmp(sig, strSig, 8))
357 	{
358 		fclose(f);
359 		return err_BadFileOpen;
360 	}
361 
362 	read_string(f, description);
363 
364 	game.state = read_sint16(f);
365 	/* game.name - not saved */
366 	read_string(f, id);
367 	if(strcmp(id, game.id))
368 	{
369 		fclose(f);
370 		return err_BadFileOpen;
371 	}
372 	/* game.crc - not saved */
373 
374 	for(i=0; i<MAX_FLAGS; i++)
375 		game.flags[i] = read_uint8(f);
376 	for(i=0; i<MAX_VARS; i++)
377 		game.vars[i] = read_uint8(f);
378 
379 	game.horizon = read_sint16(f);
380 	game.line_status = read_sint16(f);
381 	game.line_user_input = read_sint16(f);
382 	game.line_min_print = read_sint16(f);
383 
384 	/* These are never saved */
385 	game.cursor_pos = 0;
386 	game.input_buffer[0] = 0;
387 	game.echo_buffer[0] = 0;
388 	game.keypress = 0;
389 
390 	game.input_mode = read_sint16(f);
391 	game.lognum = read_sint16(f);
392 
393 	game.player_control = read_sint16(f);
394 	game.quit_prog_now = read_sint16(f);
395 	game.status_line = read_sint16(f);
396 	game.clock_enabled = read_sint16(f);
397 	game.exit_all_logics = read_sint16(f);
398 	game.picture_shown = read_sint16(f);
399 	game.has_prompt = read_sint16(f);
400 	game.game_flags = read_sint16(f);
401 
402 	game.alt_pri = read_sint16(f);
403 	for(i=0; i<_HEIGHT; i++)
404 		game.pri_table[i] = read_uint8(f);
405 
406 	if(game.has_window)
407 		close_window();
408 	game.msg_box_ticks = 0;
409 	game.block.active = FALSE;
410 	/* game.window - fixed by close_window() */
411 	/* game.has_window - fixed by close_window() */
412 
413 	game.gfx_mode = read_sint16(f);
414 	game.cursor_char = read_uint8(f);
415 	game.color_fg = read_sint16(f);
416 	game.color_bg = read_sint16(f);
417 
418 	/* game.hires (#ifdef USE_HIRES) - rebuilt from image stack */
419 	/* game.sbuf - rebuilt from image stack */
420 
421 	/* game.ego_words - fixed by clean_input */
422 	/* game.num_ego_words - fixed by clean_input */
423 
424 	game.num_objects = read_sint16(f);
425 	for(i=0; i<(SINT16)game.num_objects; i++)
426 		object_set_location(i, read_sint16(f));
427 
428 	/* Those are not serialized */
429 	for (i = 0; i < MAX_DIRS; i++) {
430 		game.ev_scan[i].occured = FALSE;
431 		game.ev_keyp[i].occured = FALSE;
432 	}
433 
434 	for(i=0; i<MAX_WORDS1; i++)
435 		read_string(f, game.strings[i]);
436 
437 	for(i=0; i<MAX_DIRS; i++)
438 	{
439 		if(read_uint8(f) & RES_LOADED)
440 			agi_load_resource(rLOGIC, i);
441 		else if(!(game.dir_logic[i].flags & RES_CACHED))
442 			agi_unload_resource(rLOGIC, i);
443 		game.logics[i].sIP = read_sint16(f);
444 		game.logics[i].cIP = read_sint16(f);
445 	}
446 	for(i=0; i<MAX_DIRS; i++)
447 	{
448 		if(read_uint8(f) & RES_LOADED)
449 			agi_load_resource(rPICTURE, i);
450 		else if(!(game.dir_pic[i].flags & RES_CACHED))
451 			agi_unload_resource(rPICTURE, i);
452 	}
453 	for(i=0; i<MAX_DIRS; i++)
454 	{
455 		if(read_uint8(f) & RES_LOADED)
456 			agi_load_resource(rVIEW, i);
457 		else if(!(game.dir_view[i].flags & RES_CACHED))
458 			agi_unload_resource(rVIEW, i);
459 	}
460 	for(i=0; i<MAX_DIRS; i++)
461 	{
462 		if(read_uint8(f) & RES_LOADED)
463 			agi_load_resource(rSOUND, i);
464 		else if(!(game.dir_sound[i].flags & RES_CACHED))
465 			agi_unload_resource(rSOUND, i);
466 	}
467 
468 	/* game.pictures - loaded above */
469 	/* game.logics - loaded above */
470 	/* game.views - loaded above */
471 	/* game.sounds - loaded above */
472 
473 	for(i=0; i<MAX_VIEWTABLE; i++)
474 	{
475 		struct vt_entry* v = &game.view_table[i];
476 
477 		v->step_time = read_uint8(f);
478 		v->step_time_count = read_uint8(f);
479 		v->entry = read_uint8(f);
480 		v->x_pos = read_sint16(f);
481 		v->y_pos = read_sint16(f);
482 		v->current_view = read_uint8(f);
483 
484 		/* v->view_data - fixed below  */
485 
486 		v->current_loop = read_uint8(f);
487 		v->num_loops = read_uint8(f);
488 
489 		/* v->loop_data - fixed below  */
490 
491 		v->current_cel = read_uint8(f);
492 		v->num_cels = read_uint8(f);
493 
494 		/* v->cel_data - fixed below  */
495 		/* v->cel_data_2 - fixed below  */
496 
497 		v->x_pos2 = read_sint16(f);
498 		v->y_pos2 = read_sint16(f);
499 
500 		/* v->s - fixed below */
501 
502 		v->x_size = read_sint16(f);
503 		v->y_size = read_sint16(f);
504 		v->step_size = read_uint8(f);
505 		v->cycle_time = read_uint8(f);
506 		v->cycle_time_count = read_uint8(f);
507 		v->direction = read_uint8(f);
508 
509 		v->motion = read_uint8(f);
510 		v->cycle = read_uint8(f);
511 		v->priority = read_uint8(f);
512 
513 		v->flags = read_uint16(f);
514 
515 		v->parm1 = read_uint8(f);
516 		v->parm2 = read_uint8(f);
517 		v->parm3 = read_uint8(f);
518 		v->parm4 = read_uint8(f);
519 	}
520 
521 /* Fix some pointers in viewtable */
522 	for(i=0; i<MAX_VIEWTABLE; i++)
523 	{
524 		struct vt_entry* v = &game.view_table[i];
525 
526 		if(game.dir_view[v->current_view].offset == _EMPTY)
527 			continue;
528 
529 		if(!(game.dir_view[v->current_view].flags & RES_LOADED))
530 			agi_load_resource(rVIEW, v->current_view);
531 
532 		set_view(v, v->current_view); /* Fix v->view_data */
533 		set_loop(v, v->current_loop); /* Fix v->loop_data */
534 		set_cel(v, v->current_cel);   /* Fix v->cel_data */
535 		v->cel_data_2 = v->cel_data;
536 		v->s = NULL; /* not sure if it is used... */
537 	}
538 
539 	erase_both();
540 
541 /* Clear input line */
542 	clear_screen(0);
543 	write_status();
544 
545 /* Recreate background from saved image stack */
546 	clear_image_stack();
547 	while((t = read_uint8(f)) != 0)
548 	{
549 		for(i=0; i<7; i++)
550 			parm[i] = read_sint16(f);
551 		replay_image_stack_call(t, parm[0], parm[1], parm[2], parm[3], parm[4], parm[5], parm[6]);
552 	}
553 
554 	if(ferror(f) || feof(f))
555 	{
556 		fclose(f);
557 		return err_BadFileOpen;
558 	}
559 
560 	fclose(f);
561 
562 	setflag(F_restore_just_ran, TRUE);
563 
564 	game.has_prompt = 0; /* force input line repaint if necessary*/
565 	clean_input();
566 
567 	erase_both();
568 	blit_both();
569 	commit_both();
570 	show_pic();
571 	do_update();
572 
573 	return err_OK;
574 }
575 
savegame_dialog()576 int savegame_dialog ()
577 {
578 	char home[MAX_PATH], path[MAX_PATH];
579 	char *desc;
580 	int slot = 0;
581 
582 	if (get_app_dir (home, MAX_PATH) < 0) {
583 		message_box ("Couldn't save game.");
584 		return err_BadFileOpen;
585 	}
586 
587 	message_box ("Multi-slot savegames are under development and"
588 		"will be available in future versions of Sarien.");
589  	desc = "Save game test";
590 
591 	/* DATADIR conflicts with ObjIdl.h in win32 SDK,
592 		renamed to DATA_DIR */
593 	sprintf (path, "%s/" DATA_DIR "/", home);
594 
595 	MKDIR (path, 0755);
596 	sprintf (path, "%s/" DATA_DIR "/%s/", home, game.id);
597 	MKDIR (path, 0711);
598 
599 	sprintf (path, "%s/" DATA_DIR "/%s/%08d.sav",
600 		home, game.id, slot);
601 	_D (_D_WARN "file is [%s]", path);
602 
603 	save_game(path, desc);
604 
605 	message_box ("Game saved.");
606 
607 	return err_OK;
608 }
609 
610 
loadgame_dialog()611 int loadgame_dialog ()
612 {
613 	char home[MAX_PATH], path[MAX_PATH];
614 	int slot = 0;
615 	int rc;
616 
617 	if (get_app_dir (home, MAX_PATH) < 0) {
618 		message_box ("Error loading game.");
619 		return err_BadFileOpen;
620 	}
621 
622 	sprintf (path, "%s/" DATA_DIR "/", home);
623 	MKDIR (path, 0755);
624 	sprintf (path, "%s/" DATA_DIR "/%s/", home, game.id);
625 	MKDIR (path, 0711);
626 
627 	sprintf (path, "%s/" DATA_DIR "/%s/%08d.sav",
628 		home, game.id, slot);
629 
630 	stop_sound();
631 	if((rc = load_game (path)) == err_OK)
632 		message_box ("Game loaded.");
633 	else
634 		message_box ("Error loading game.");
635 
636 	return rc;
637 }
638 
639 #endif /* PALMOS */
640 
641 /* end: savegame.c */
642 
643