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