1 //Copyright Paul Reiche, Fred Ford. 1992-2002
2 
3 /*
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  */
18 
19 #include <assert.h>
20 
21 #include "build.h"
22 #include "libs/declib.h"
23 #include "encount.h"
24 #include "starmap.h"
25 #include "libs/file.h"
26 #include "globdata.h"
27 #include "options.h"
28 #include "save.h"
29 #include "setup.h"
30 #include "state.h"
31 #include "grpinfo.h"
32 
33 #include "libs/tasklib.h"
34 #include "libs/log.h"
35 #include "libs/misc.h"
36 
37 //#define DEBUG_LOAD
38 
39 // XXX: these should handle endian conversions later
40 static inline COUNT
cread_8(DECODE_REF fh,BYTE * v)41 cread_8 (DECODE_REF fh, BYTE *v)
42 {
43 	BYTE t;
44 	if (!v) /* read value ignored */
45 		v = &t;
46 	return cread (v, 1, 1, fh);
47 }
48 
49 static inline COUNT
cread_16(DECODE_REF fh,UWORD * v)50 cread_16 (DECODE_REF fh, UWORD *v)
51 {
52 	UWORD t;
53 	if (!v) /* read value ignored */
54 		v = &t;
55 	return cread (v, 2, 1, fh);
56 }
57 
58 static inline COUNT
cread_16s(DECODE_REF fh,SWORD * v)59 cread_16s (DECODE_REF fh, SWORD *v)
60 {
61 	UWORD t;
62 	COUNT ret;
63 	// value was converted to unsigned when saved
64 	ret = cread_16 (fh, &t);
65 	// unsigned to signed conversion
66 	if (v)
67 		*v = t;
68 	return ret;
69 }
70 
71 static inline COUNT
cread_32(DECODE_REF fh,DWORD * v)72 cread_32 (DECODE_REF fh, DWORD *v)
73 {
74 	DWORD t;
75 	if (!v) /* read value ignored */
76 		v = &t;
77 	return cread (v, 4, 1, fh);
78 }
79 
80 static inline COUNT
cread_32s(DECODE_REF fh,SDWORD * v)81 cread_32s (DECODE_REF fh, SDWORD *v)
82 {
83 	DWORD t;
84 	COUNT ret;
85 	// value was converted to unsigned when saved
86 	ret = cread_32 (fh, &t);
87 	// unsigned to signed conversion
88 	if (v)
89 		*v = t;
90 	return ret;
91 }
92 
93 static inline COUNT
cread_ptr(DECODE_REF fh)94 cread_ptr (DECODE_REF fh)
95 {
96 	DWORD t;
97 	return cread_32 (fh, &t); /* ptrs are useless in saves */
98 }
99 
100 static inline COUNT
cread_a8(DECODE_REF fh,BYTE * ar,COUNT count)101 cread_a8 (DECODE_REF fh, BYTE *ar, COUNT count)
102 {
103 	assert (ar != NULL);
104 	return cread (ar, 1, count, fh) == count;
105 }
106 
107 static inline size_t
read_8(void * fp,BYTE * v)108 read_8 (void *fp, BYTE *v)
109 {
110 	BYTE t;
111 	if (!v) /* read value ignored */
112 		v = &t;
113 	return ReadResFile (v, 1, 1, fp);
114 }
115 
116 static inline size_t
read_16(void * fp,UWORD * v)117 read_16 (void *fp, UWORD *v)
118 {
119 	UWORD t;
120 	if (!v) /* read value ignored */
121 		v = &t;
122 	return ReadResFile (v, 2, 1, fp);
123 }
124 
125 static inline size_t
read_32(void * fp,DWORD * v)126 read_32 (void *fp, DWORD *v)
127 {
128 	DWORD t;
129 	if (!v) /* read value ignored */
130 		v = &t;
131 	return ReadResFile (v, 4, 1, fp);
132 }
133 
134 static inline size_t
read_32s(void * fp,SDWORD * v)135 read_32s (void *fp, SDWORD *v)
136 {
137 	DWORD t;
138 	COUNT ret;
139 	// value was converted to unsigned when saved
140 	ret = read_32 (fp, &t);
141 	// unsigned to signed conversion
142 	if (v)
143 		*v = t;
144 	return ret;
145 }
146 
147 static inline size_t
read_ptr(void * fp)148 read_ptr (void *fp)
149 {
150 	DWORD t;
151 	return read_32 (fp, &t); /* ptrs are useless in saves */
152 }
153 
154 static inline size_t
read_a8(void * fp,BYTE * ar,COUNT count)155 read_a8 (void *fp, BYTE *ar, COUNT count)
156 {
157 	assert (ar != NULL);
158 	return ReadResFile (ar, 1, count, fp) == count;
159 }
160 
161 static inline size_t
read_str(void * fp,char * str,COUNT count)162 read_str (void *fp, char *str, COUNT count)
163 {
164 	// no type conversion needed for strings
165 	return read_a8 (fp, (BYTE *)str, count);
166 }
167 
168 static inline size_t
read_a16(void * fp,UWORD * ar,COUNT count)169 read_a16 (void *fp, UWORD *ar, COUNT count)
170 {
171 	assert (ar != NULL);
172 
173 	for ( ; count > 0; --count, ++ar)
174 	{
175 		if (read_16 (fp, ar) != 1)
176 			return 0;
177 	}
178 	return 1;
179 }
180 
181 typedef struct struct_GAMESTATE_TRANSPOSE {
182 	int start, end, target;
183 } GAMESTATE_TRANSPOSE;
184 
185 #define LEGACY_GAMESTATE_SIZE 155
186 
187 /* The *_GRPOFFS* states are no longer intermingled with the rest of
188  * the state. We need to shuffle all the rest of the state data
189  * down. */
190 static GAMESTATE_TRANSPOSE transpose[] = {
191 	{   0,   51,   0 },
192 	{ 404,  450,  52 },
193 	{ 483,  878,  99 },
194 	{ 911,  930, 495 },
195 	{ 963, 1237, 515 },
196 	{  -1,   -1,  -1 } };
197 
198 static DWORD old_defgrp_offsets[] = { 0, 52, 84, 116, 148, 180, 212, 244,
199 				      276, 308, 340, 372, 451, 879, 931 };
200 
201 static DWORD new_defgrp_offsets[] = {
202 	0,
203 	SHOFIXTI_GRPOFFS0,
204 	ZOQFOT_GRPOFFS0,
205 	MELNORME0_GRPOFFS0,
206 	MELNORME1_GRPOFFS0,
207 	MELNORME2_GRPOFFS0,
208 	MELNORME3_GRPOFFS0,
209 	MELNORME4_GRPOFFS0,
210 	MELNORME5_GRPOFFS0,
211 	MELNORME6_GRPOFFS0,
212 	MELNORME7_GRPOFFS0,
213 	MELNORME8_GRPOFFS0,
214 	URQUAN_PROBE_GRPOFFS0,
215 	COLONY_GRPOFFS0,
216 	SAMATRA_GRPOFFS0
217 };
218 
219 static void
InterpretLegacyGameState(BYTE * result,BYTE * legacy)220 InterpretLegacyGameState (BYTE *result, BYTE *legacy)
221 {
222 	int i;
223 	DWORD grpoffs[NUM_DEFGRPS];
224 	GAMESTATE_TRANSPOSE *t = &transpose[0];
225 	grpoffs[0] = 0;
226 	for (i = 1; i < NUM_DEFGRPS; ++i)
227 	{
228 		grpoffs[i] = getGameState32 (legacy, old_defgrp_offsets[i]);
229 	}
230 	while (t->start >= 0)
231 	{
232 		copyGameState (result, t->target, legacy, t->start, t->end);
233 		++t;
234 	}
235 	for (i = 1; i < NUM_DEFGRPS; ++i)
236 	{
237 		setGameState32 (result, new_defgrp_offsets[i], grpoffs[i]);
238 	}
239 }
240 
241 static void
LoadEmptyQueue(DECODE_REF fh)242 LoadEmptyQueue (DECODE_REF fh)
243 {
244 	COUNT num_links;
245 
246 	cread_16 (fh, &num_links);
247 	if (num_links)
248 	{
249 		log_add (log_Error, "LoadEmptyQueue(): BUG: the queue is not empty!");
250 #ifdef DEBUG
251 		explode ();
252 #endif
253 	}
254 }
255 
256 static void
LoadShipQueue(DECODE_REF fh,QUEUE * pQueue)257 LoadShipQueue (DECODE_REF fh, QUEUE *pQueue)
258 {
259 	COUNT num_links;
260 
261 	cread_16 (fh, &num_links);
262 
263 	while (num_links--)
264 	{
265 		HSHIPFRAG hStarShip;
266 		SHIP_FRAGMENT *FragPtr;
267 		COUNT Index;
268 		BYTE tmpb;
269 
270 		cread_16 (fh, &Index);
271 
272 		hStarShip = CloneShipFragment (Index, pQueue, 0);
273 		FragPtr = LockShipFrag (pQueue, hStarShip);
274 
275 		// Read SHIP_FRAGMENT elements
276 		cread_16 (fh, NULL); /* unused: was which_side */
277 		cread_8  (fh, &FragPtr->captains_name_index);
278 		cread_8  (fh, NULL); /* padding */
279 		cread_16 (fh, NULL); /* unused: was ship_flags */
280 		cread_8  (fh, &FragPtr->race_id);
281 		cread_8  (fh, &FragPtr->index);
282 		// XXX: reading crew as BYTE to maintain savegame compatibility
283 		cread_8  (fh, &tmpb);
284 		FragPtr->crew_level = tmpb;
285 		cread_8  (fh, &tmpb);
286 		FragPtr->max_crew = tmpb;
287 		cread_8  (fh, &FragPtr->energy_level);
288 		cread_8  (fh, &FragPtr->max_energy);
289 		cread_16 (fh, NULL); /* unused; was loc.x */
290 		cread_16 (fh, NULL); /* unused; was loc.y */
291 
292 		UnlockShipFrag (pQueue, hStarShip);
293 	}
294 }
295 
296 static void
LoadRaceQueue(DECODE_REF fh,QUEUE * pQueue)297 LoadRaceQueue (DECODE_REF fh, QUEUE *pQueue)
298 {
299 	COUNT num_links;
300 
301 	cread_16 (fh, &num_links);
302 
303 	while (num_links--)
304 	{
305 		HFLEETINFO hStarShip;
306 		FLEET_INFO *FleetPtr;
307 		COUNT Index;
308 		BYTE tmpb;
309 
310 		cread_16 (fh, &Index);
311 
312 		hStarShip = GetStarShipFromIndex (pQueue, Index);
313 		FleetPtr = LockFleetInfo (pQueue, hStarShip);
314 
315 		// Read FLEET_INFO elements
316 		cread_16 (fh, &FleetPtr->allied_state);
317 		cread_8  (fh, &FleetPtr->days_left);
318 		cread_8  (fh, &FleetPtr->growth_fract);
319 		cread_8  (fh, &tmpb);
320 		FleetPtr->crew_level = tmpb;
321 		cread_8  (fh, &tmpb);
322 		FleetPtr->max_crew = tmpb;
323 		cread_8  (fh, &FleetPtr->growth);
324 		cread_8  (fh, &FleetPtr->max_energy);
325 		cread_16s(fh, &FleetPtr->loc.x);
326 		cread_16s(fh, &FleetPtr->loc.y);
327 
328 		cread_16 (fh, &FleetPtr->actual_strength);
329 		cread_16 (fh, &FleetPtr->known_strength);
330 		cread_16s(fh, &FleetPtr->known_loc.x);
331 		cread_16s(fh, &FleetPtr->known_loc.y);
332 		cread_8  (fh, &FleetPtr->growth_err_term);
333 		cread_8  (fh, &FleetPtr->func_index);
334 		cread_16s(fh, &FleetPtr->dest_loc.x);
335 		cread_16s(fh, &FleetPtr->dest_loc.y);
336 		cread_16 (fh, NULL); /* alignment padding */
337 
338 		UnlockFleetInfo (pQueue, hStarShip);
339 	}
340 }
341 
342 static void
LoadGroupQueue(DECODE_REF fh,QUEUE * pQueue)343 LoadGroupQueue (DECODE_REF fh, QUEUE *pQueue)
344 {
345 	COUNT num_links;
346 
347 	cread_16 (fh, &num_links);
348 
349 	while (num_links--)
350 	{
351 		HIPGROUP hGroup;
352 		IP_GROUP *GroupPtr;
353 		BYTE tmpb;
354 
355 		cread_16 (fh, NULL); /* unused; was race_id */
356 
357 		hGroup = BuildGroup (pQueue, 0);
358 		GroupPtr = LockIpGroup (pQueue, hGroup);
359 
360 		cread_16 (fh, NULL); /* unused; was which_side */
361 		cread_8  (fh, NULL); /* unused; was captains_name_index */
362 		cread_8  (fh, NULL); /* padding; for savegame compat */
363 		cread_16 (fh, &GroupPtr->group_counter);
364 		cread_8  (fh, &GroupPtr->race_id);
365 		cread_8  (fh, &tmpb); /* was var2 */
366 		GroupPtr->sys_loc = LONIBBLE (tmpb);
367 		GroupPtr->task = HINIBBLE (tmpb);
368 		cread_8  (fh, &GroupPtr->in_system); /* was crew_level */
369 		cread_8  (fh, NULL); /* unused; was max_crew */
370 		cread_8  (fh, &tmpb); /* was energy_level */
371 		GroupPtr->dest_loc = LONIBBLE (tmpb);
372 		GroupPtr->orbit_pos = HINIBBLE (tmpb);
373 		cread_8  (fh, &GroupPtr->group_id); /* was max_energy */
374 		cread_16s(fh, &GroupPtr->loc.x);
375 		cread_16s(fh, &GroupPtr->loc.y);
376 
377 		UnlockIpGroup (pQueue, hGroup);
378 	}
379 }
380 
381 static void
LoadEncounter(ENCOUNTER * EncounterPtr,DECODE_REF fh)382 LoadEncounter (ENCOUNTER *EncounterPtr, DECODE_REF fh)
383 {
384 	COUNT i;
385 	BYTE tmpb;
386 
387 	cread_ptr (fh); /* useless ptr; HENCOUNTER pred */
388 	EncounterPtr->pred = 0;
389 	cread_ptr (fh); /* useless ptr; HENCOUNTER succ */
390 	EncounterPtr->succ = 0;
391 	cread_ptr (fh); /* useless ptr; HELEMENT hElement */
392 	EncounterPtr->hElement = 0;
393 	cread_16s (fh, &EncounterPtr->transition_state);
394 	cread_16s (fh, &EncounterPtr->origin.x);
395 	cread_16s (fh, &EncounterPtr->origin.y);
396 	cread_16  (fh, &EncounterPtr->radius);
397 	// former STAR_DESC fields
398 	cread_16s (fh, &EncounterPtr->loc_pt.x);
399 	cread_16s (fh, &EncounterPtr->loc_pt.y);
400 	cread_8   (fh, &EncounterPtr->race_id);
401 	cread_8   (fh, &tmpb);
402 	EncounterPtr->num_ships = tmpb & ENCOUNTER_SHIPS_MASK;
403 	EncounterPtr->flags = tmpb & ENCOUNTER_FLAGS_MASK;
404 	cread_16  (fh, NULL); /* alignment padding */
405 
406 	// Load each entry in the BRIEF_SHIP_INFO array
407 	for (i = 0; i < MAX_HYPER_SHIPS; i++)
408 	{
409 		BRIEF_SHIP_INFO *ShipInfo = &EncounterPtr->ShipList[i];
410 
411 		cread_16  (fh, NULL); /* useless; was SHIP_INFO.ship_flags */
412 		cread_8   (fh, &ShipInfo->race_id);
413 		cread_8   (fh, NULL); /* useless; was SHIP_INFO.var2 */
414 		// XXX: reading crew as BYTE to maintain savegame compatibility
415 		cread_8   (fh, &tmpb);
416 		ShipInfo->crew_level = tmpb;
417 		cread_8   (fh, &tmpb);
418 		ShipInfo->max_crew = tmpb;
419 		cread_8   (fh, NULL); /* useless; was SHIP_INFO.energy_level */
420 		cread_8   (fh, &ShipInfo->max_energy);
421 		cread_16  (fh, NULL); /* useless; was SHIP_INFO.loc.x */
422 		cread_16  (fh, NULL); /* useless; was SHIP_INFO.loc.y */
423 		cread_32  (fh, NULL); /* useless val; STRING race_strings */
424 		cread_ptr (fh); /* useless ptr; FRAME icons */
425 		cread_ptr (fh); /* useless ptr; FRAME melee_icon */
426 	}
427 
428 	// Load the stuff after the BRIEF_SHIP_INFO array
429 	cread_32s (fh, &EncounterPtr->log_x);
430 	cread_32s (fh, &EncounterPtr->log_y);
431 }
432 
433 static void
LoadEvent(EVENT * EventPtr,DECODE_REF fh)434 LoadEvent (EVENT *EventPtr, DECODE_REF fh)
435 {
436 	cread_ptr (fh); /* useless ptr; HEVENT pred */
437 	EventPtr->pred = 0;
438 	cread_ptr (fh); /* useless ptr; HEVENT succ */
439 	EventPtr->succ = 0;
440 	cread_8   (fh, &EventPtr->day_index);
441 	cread_8   (fh, &EventPtr->month_index);
442 	cread_16  (fh, &EventPtr->year_index);
443 	cread_8   (fh, &EventPtr->func_index);
444 	cread_8   (fh, NULL); /* padding */
445 	cread_16  (fh, NULL); /* padding */
446 }
447 
448 static void
DummyLoadQueue(QUEUE * QueuePtr,DECODE_REF fh)449 DummyLoadQueue (QUEUE *QueuePtr, DECODE_REF fh)
450 {
451 	/* QUEUE should never actually be loaded since it contains
452 	 * purely internal representation and the lists
453 	 * involved are actually loaded separately */
454 	(void)QueuePtr; /* silence compiler */
455 
456 	/* QUEUE format with QUEUE_TABLE defined -- UQM default */
457 	cread_ptr (fh); /* HLINK head */
458 	cread_ptr (fh); /* HLINK tail */
459 	cread_ptr (fh); /* BYTE* pq_tab */
460 	cread_ptr (fh); /* HLINK free_list */
461 	cread_16  (fh, NULL); /* MEM_HANDLE hq_tab */
462 	cread_16  (fh, NULL); /* COUNT object_size */
463 	cread_8   (fh, NULL); /* BYTE num_objects */
464 
465 	cread_8   (fh, NULL); /* padding */
466 	cread_16  (fh, NULL); /* padding */
467 }
468 
469 static void
LoadClockState(CLOCK_STATE * ClockPtr,DECODE_REF fh)470 LoadClockState (CLOCK_STATE *ClockPtr, DECODE_REF fh)
471 {
472 	cread_8   (fh, &ClockPtr->day_index);
473 	cread_8   (fh, &ClockPtr->month_index);
474 	cread_16  (fh, &ClockPtr->year_index);
475 	cread_16s (fh, &ClockPtr->tick_count);
476 	cread_16s (fh, &ClockPtr->day_in_ticks);
477 	cread_ptr (fh); /* not loading ptr; Semaphore clock_sem */
478 	cread_ptr (fh); /* not loading ptr; Task clock_task */
479 	cread_32  (fh, NULL); /* not loading; DWORD TimeCounter */
480 
481 	DummyLoadQueue (&ClockPtr->event_q, fh);
482 }
483 
484 static void
LoadGameState(GAME_STATE * GSPtr,DECODE_REF fh)485 LoadGameState (GAME_STATE *GSPtr, DECODE_REF fh)
486 {
487 	BYTE dummy8, oldstate[LEGACY_GAMESTATE_SIZE];
488 
489 	cread_8   (fh, &dummy8); /* obsolete */
490 	cread_8   (fh, &GSPtr->glob_flags);
491 	cread_8   (fh, &GSPtr->CrewCost);
492 	cread_8   (fh, &GSPtr->FuelCost);
493 	cread_a8  (fh, GSPtr->ModuleCost, NUM_MODULES);
494 	cread_a8  (fh, GSPtr->ElementWorth, NUM_ELEMENT_CATEGORIES);
495 	cread_ptr (fh); /* not loading ptr; PRIMITIVE *DisplayArray */
496 	cread_16  (fh, &GSPtr->CurrentActivity);
497 
498 	cread_16  (fh, NULL); /* CLOCK_STATE alignment padding */
499 	LoadClockState (&GSPtr->GameClock, fh);
500 
501 	cread_16s (fh, &GSPtr->autopilot.x);
502 	cread_16s (fh, &GSPtr->autopilot.y);
503 	cread_16s (fh, &GSPtr->ip_location.x);
504 	cread_16s (fh, &GSPtr->ip_location.y);
505 	/* STAMP ShipStamp */
506 	cread_16s (fh, &GSPtr->ShipStamp.origin.x);
507 	cread_16s (fh, &GSPtr->ShipStamp.origin.y);
508 	cread_16  (fh, &GSPtr->ShipFacing);
509 	cread_8   (fh, &GSPtr->ip_planet);
510 	cread_8   (fh, &GSPtr->in_orbit);
511 
512 	/* VELOCITY_DESC velocity */
513 	cread_16  (fh, &GSPtr->velocity.TravelAngle);
514 	cread_16s (fh, &GSPtr->velocity.vector.width);
515 	cread_16s (fh, &GSPtr->velocity.vector.height);
516 	cread_16s (fh, &GSPtr->velocity.fract.width);
517 	cread_16s (fh, &GSPtr->velocity.fract.height);
518 	cread_16s (fh, &GSPtr->velocity.error.width);
519 	cread_16s (fh, &GSPtr->velocity.error.height);
520 	cread_16s (fh, &GSPtr->velocity.incr.width);
521 	cread_16s (fh, &GSPtr->velocity.incr.height);
522 	cread_16  (fh, NULL); /* VELOCITY_DESC padding */
523 
524 	cread_32  (fh, &GSPtr->BattleGroupRef);
525 
526 	DummyLoadQueue (&GSPtr->avail_race_q, fh);
527 	DummyLoadQueue (&GSPtr->npc_built_ship_q, fh);
528 	// Not loading ip_group_q, was not there originally
529 	DummyLoadQueue (&GSPtr->encounter_q, fh);
530 	DummyLoadQueue (&GSPtr->built_ship_q, fh);
531 
532 	cread_a8  (fh, oldstate, LEGACY_GAMESTATE_SIZE);
533 	InterpretLegacyGameState (GSPtr->GameState, oldstate);
534 
535 	cread_8  (fh, NULL); /* GAME_STATE alignment padding */
536 }
537 
538 static BOOLEAN
LoadSisState(SIS_STATE * SSPtr,void * fp)539 LoadSisState (SIS_STATE *SSPtr, void *fp)
540 {
541 	if (
542 			read_32s (fp, &SSPtr->log_x) != 1 ||
543 			read_32s (fp, &SSPtr->log_y) != 1 ||
544 			read_32  (fp, &SSPtr->ResUnits) != 1 ||
545 			read_32  (fp, &SSPtr->FuelOnBoard) != 1 ||
546 			read_16  (fp, &SSPtr->CrewEnlisted) != 1 ||
547 			read_16  (fp, &SSPtr->TotalElementMass) != 1 ||
548 			read_16  (fp, &SSPtr->TotalBioMass) != 1 ||
549 			read_a8  (fp, SSPtr->ModuleSlots, NUM_MODULE_SLOTS) != 1 ||
550 			read_a8  (fp, SSPtr->DriveSlots, NUM_DRIVE_SLOTS) != 1 ||
551 			read_a8  (fp, SSPtr->JetSlots, NUM_JET_SLOTS) != 1 ||
552 			read_8   (fp, &SSPtr->NumLanders) != 1 ||
553 			read_a16 (fp, SSPtr->ElementAmounts, NUM_ELEMENT_CATEGORIES) != 1 ||
554 
555 			read_str (fp, SSPtr->ShipName, SIS_NAME_SIZE) != 1 ||
556 			read_str (fp, SSPtr->CommanderName, SIS_NAME_SIZE) != 1 ||
557 			read_str (fp, SSPtr->PlanetName, SIS_NAME_SIZE) != 1 ||
558 
559 			read_16  (fp, NULL) != 1 /* padding */
560 		)
561 		return FALSE;
562 	else
563 		return TRUE;
564 }
565 
566 static BOOLEAN
LoadSummary(SUMMARY_DESC * SummPtr,void * fp)567 LoadSummary (SUMMARY_DESC *SummPtr, void *fp)
568 {
569 	if (!LoadSisState (&SummPtr->SS, fp))
570 		return FALSE;
571 
572 	if (
573 			read_8  (fp, &SummPtr->Activity) != 1 ||
574 			read_8  (fp, &SummPtr->Flags) != 1 ||
575 			read_8  (fp, &SummPtr->day_index) != 1 ||
576 			read_8  (fp, &SummPtr->month_index) != 1 ||
577 			read_16 (fp, &SummPtr->year_index) != 1 ||
578 			read_8  (fp, &SummPtr->MCreditLo) != 1 ||
579 			read_8  (fp, &SummPtr->MCreditHi) != 1 ||
580 			read_8  (fp, &SummPtr->NumShips) != 1 ||
581 			read_8  (fp, &SummPtr->NumDevices) != 1 ||
582 			read_a8 (fp, SummPtr->ShipList, MAX_BUILT_SHIPS) != 1 ||
583 			read_a8 (fp, SummPtr->DeviceList, MAX_EXCLUSIVE_DEVICES) != 1 ||
584 
585 			read_16  (fp, NULL) != 1 /* padding */
586 		)
587 		return FALSE;
588 	else
589 		return TRUE;
590 }
591 
592 static void
LoadStarDesc(STAR_DESC * SDPtr,DECODE_REF fh)593 LoadStarDesc (STAR_DESC *SDPtr, DECODE_REF fh)
594 {
595 	cread_16s(fh, &SDPtr->star_pt.x);
596 	cread_16s(fh, &SDPtr->star_pt.y);
597 	cread_8  (fh, &SDPtr->Type);
598 	cread_8  (fh, &SDPtr->Index);
599 	cread_8  (fh, &SDPtr->Prefix);
600 	cread_8  (fh, &SDPtr->Postfix);
601 }
602 
603 BOOLEAN
LoadLegacyGame(COUNT which_game,SUMMARY_DESC * SummPtr)604 LoadLegacyGame (COUNT which_game, SUMMARY_DESC *SummPtr)
605 {
606 	uio_Stream *in_fp;
607 	char file[PATH_MAX];
608 	char buf[256];
609 	SUMMARY_DESC loc_sd;
610 	GAME_STATE_FILE *fp;
611 	DECODE_REF fh;
612 	COUNT num_links;
613 	STAR_DESC SD;
614 	ACTIVITY Activity;
615 
616 	sprintf (file, "starcon2.%02u", which_game);
617 	in_fp = res_OpenResFile (saveDir, file, "rb");
618 	if (!in_fp)
619 		return FALSE;
620 
621 	loc_sd.SaveName[0] = '\0';
622 	if (!LoadSummary (&loc_sd, in_fp))
623 	{
624 		log_add (log_Error, "Warning: Savegame is corrupt");
625 		res_CloseResFile (in_fp);
626 		return FALSE;
627 	}
628 
629 	if (!SummPtr)
630 	{
631 		SummPtr = &loc_sd;
632 	}
633 	else
634 	{	// only need summary for displaying to user
635 		memcpy (SummPtr, &loc_sd, sizeof (*SummPtr));
636 		res_CloseResFile (in_fp);
637 		return TRUE;
638 	}
639 
640 	// Crude check for big-endian/little-endian incompatibilities.
641 	// year_index is suitable as it's a multi-byte value within
642 	// a specific recognisable range.
643 	if (SummPtr->year_index < START_YEAR ||
644 			SummPtr->year_index >= START_YEAR +
645 			YEARS_TO_KOHRAH_VICTORY + 1 /* Utwig intervention */ +
646 			1 /* time to destroy all races, plenty */ +
647 			25 /* for cheaters */)
648 	{
649 		log_add (log_Error, "Warning: Savegame corrupt or from "
650 				"an incompatible platform.");
651 		res_CloseResFile (in_fp);
652 		return FALSE;
653 	}
654 
655 	GlobData.SIS_state = SummPtr->SS;
656 
657 	if ((fh = copen (in_fp, FILE_STREAM, STREAM_READ)) == 0)
658 	{
659 		res_CloseResFile (in_fp);
660 		return FALSE;
661 	}
662 
663 	ReinitQueue (&GLOBAL (GameClock.event_q));
664 	ReinitQueue (&GLOBAL (encounter_q));
665 	ReinitQueue (&GLOBAL (ip_group_q));
666 	ReinitQueue (&GLOBAL (npc_built_ship_q));
667 	ReinitQueue (&GLOBAL (built_ship_q));
668 
669 	memset (&GLOBAL (GameState[0]), 0, sizeof (GLOBAL (GameState)));
670 	Activity = GLOBAL (CurrentActivity);
671 	LoadGameState (&GlobData.Game_state, fh);
672 	NextActivity = GLOBAL (CurrentActivity);
673 	GLOBAL (CurrentActivity) = Activity;
674 
675 	LoadRaceQueue (fh, &GLOBAL (avail_race_q));
676 	// START_INTERPLANETARY is only set when saving from Homeworld
677 	//   encounter screen. When the game is loaded, the
678 	//   GenerateOrbitalFunction for the current star system will
679 	//   create the encounter anew and populate the npc queue.
680 	if (!(NextActivity & START_INTERPLANETARY))
681 	{
682 		if (NextActivity & START_ENCOUNTER)
683 			LoadShipQueue (fh, &GLOBAL (npc_built_ship_q));
684 		else if (LOBYTE (NextActivity) == IN_INTERPLANETARY)
685 			// XXX: Technically, this queue does not need to be
686 			//   saved/loaded at all. IP groups will be reloaded
687 			//   from group state files. But the original code did,
688 			//   and so will we until we can prove we do not need to.
689 			LoadGroupQueue (fh, &GLOBAL (ip_group_q));
690 		else
691 			// XXX: The empty queue read is only needed to maintain
692 			//   the savegame compatibility
693 			LoadEmptyQueue (fh);
694 	}
695 	LoadShipQueue (fh, &GLOBAL (built_ship_q));
696 
697 	// Load the game events (compressed)
698 	cread_16 (fh, &num_links);
699 	{
700 #ifdef DEBUG_LOAD
701 		log_add (log_Debug, "EVENTS:");
702 #endif /* DEBUG_LOAD */
703 		while (num_links--)
704 		{
705 			HEVENT hEvent;
706 			EVENT *EventPtr;
707 
708 			hEvent = AllocEvent ();
709 			LockEvent (hEvent, &EventPtr);
710 
711 			LoadEvent (EventPtr, fh);
712 
713 #ifdef DEBUG_LOAD
714 		log_add (log_Debug, "\t%u/%u/%u -- %u",
715 				EventPtr->month_index,
716 				EventPtr->day_index,
717 				EventPtr->year_index,
718 				EventPtr->func_index);
719 #endif /* DEBUG_LOAD */
720 			UnlockEvent (hEvent);
721 			PutEvent (hEvent);
722 		}
723 	}
724 
725 	// Load the encounters (black globes in HS/QS (compressed))
726 	cread_16 (fh, &num_links);
727 	{
728 		while (num_links--)
729 		{
730 			HENCOUNTER hEncounter;
731 			ENCOUNTER *EncounterPtr;
732 
733 			hEncounter = AllocEncounter ();
734 			LockEncounter (hEncounter, &EncounterPtr);
735 
736 			LoadEncounter (EncounterPtr, fh);
737 
738 			UnlockEncounter (hEncounter);
739 			PutEncounter (hEncounter);
740 		}
741 	}
742 
743 	// Copy the star info file from the compressed stream
744 	fp = OpenStateFile (STARINFO_FILE, "wb");
745 	if (fp)
746 	{
747 		DWORD flen;
748 
749 		cread_32 (fh, &flen);
750 		while (flen)
751 		{
752 			COUNT num_bytes;
753 
754 			num_bytes = flen >= sizeof (buf) ? sizeof (buf) : (COUNT)flen;
755 			cread (buf, num_bytes, 1, fh);
756 			WriteStateFile (buf, num_bytes, 1, fp);
757 
758 			flen -= num_bytes;
759 		}
760 		CloseStateFile (fp);
761 	}
762 
763 	// Copy the defined groupinfo file from the compressed stream
764 	fp = OpenStateFile (DEFGRPINFO_FILE, "wb");
765 	if (fp)
766 	{
767 		DWORD flen;
768 
769 		cread_32 (fh, &flen);
770 		while (flen)
771 		{
772 			COUNT num_bytes;
773 
774 			num_bytes = flen >= sizeof (buf) ? sizeof (buf) : (COUNT)flen;
775 			cread (buf, num_bytes, 1, fh);
776 			WriteStateFile (buf, num_bytes, 1, fp);
777 
778 			flen -= num_bytes;
779 		}
780 		CloseStateFile (fp);
781 	}
782 
783 	// Copy the random groupinfo file from the compressed stream
784 	fp = OpenStateFile (RANDGRPINFO_FILE, "wb");
785 	if (fp)
786 	{
787 		DWORD flen;
788 
789 		cread_32 (fh, &flen);
790 		while (flen)
791 		{
792 			COUNT num_bytes;
793 
794 			num_bytes = flen >= sizeof (buf) ? sizeof (buf) : (COUNT)flen;
795 			cread (buf, num_bytes, 1, fh);
796 			WriteStateFile (buf, num_bytes, 1, fp);
797 
798 			flen -= num_bytes;
799 		}
800 		CloseStateFile (fp);
801 	}
802 
803 	LoadStarDesc (&SD, fh);
804 
805 	cclose (fh);
806 	res_CloseResFile (in_fp);
807 
808 	EncounterGroup = 0;
809 	EncounterRace = -1;
810 
811 	ReinitQueue (&race_q[0]);
812 	ReinitQueue (&race_q[1]);
813 	CurStarDescPtr = FindStar (NULL, &SD.star_pt, 0, 0);
814 	if (!(NextActivity & START_ENCOUNTER)
815 			&& LOBYTE (NextActivity) == IN_INTERPLANETARY)
816 		NextActivity |= START_INTERPLANETARY;
817 
818 	return TRUE;
819 }
820 
821 
822