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 "encount.h"
23 #include "starmap.h"
24 #include "libs/file.h"
25 #include "globdata.h"
26 #include "options.h"
27 #include "save.h"
28 #include "setup.h"
29 #include "state.h"
30 #include "grpintrn.h"
31 
32 #include "libs/tasklib.h"
33 #include "libs/log.h"
34 #include "libs/misc.h"
35 
36 //#define DEBUG_LOAD
37 
38 ACTIVITY NextActivity;
39 
40 static inline size_t
read_8(void * fp,BYTE * v)41 read_8 (void *fp, BYTE *v)
42 {
43 	BYTE t;
44 	if (!v) /* read value ignored */
45 		v = &t;
46 	return ReadResFile (v, 1, 1, fp);
47 }
48 
49 static inline size_t
read_16(void * fp,UWORD * v)50 read_16 (void *fp, UWORD *v)
51 {
52 	UWORD t = 0;
53 	int shift, i;
54 	for (i = 0, shift = 0; i < 2; ++i, shift += 8)
55 	{
56 		BYTE b;
57 		if (read_8 (fp, &b) != 1)
58 			return 0;
59 		t |= ((UWORD)b) << shift;
60 	}
61 
62 	if (v)
63 		*v = t;
64 
65 	return 1;
66 }
67 
68 static inline size_t
read_16s(void * fp,SWORD * v)69 read_16s (void *fp, SWORD *v)
70 {
71 	return read_16 (fp, (UWORD *) v);
72 }
73 
74 static inline size_t
read_32(void * fp,DWORD * v)75 read_32 (void *fp, DWORD *v)
76 {
77 	DWORD t = 0;
78 	int shift, i;
79 	for (i = 0, shift = 0; i < 4; ++i, shift += 8)
80 	{
81 		BYTE b;
82 		if (read_8 (fp, &b) != 1)
83 			return 0;
84 		t |= ((DWORD)b) << shift;
85 	}
86 
87 	if (v)
88 		*v = t;
89 
90 	return 1;
91 }
92 
93 static inline size_t
read_32s(void * fp,SDWORD * v)94 read_32s (void *fp, SDWORD *v)
95 {
96 	return read_32 (fp, (DWORD *) v);
97 }
98 
99 static inline size_t
read_a8(void * fp,BYTE * ar,COUNT count)100 read_a8 (void *fp, BYTE *ar, COUNT count)
101 {
102 	assert (ar != NULL);
103 	return ReadResFile (ar, 1, count, fp) == count;
104 }
105 
106 static inline size_t
read_a8s(void * fp,char * ar,COUNT count)107 read_a8s (void *fp, char *ar, COUNT count)
108 {
109 	return read_a8(fp, (BYTE *) ar, count);
110 }
111 
112 static inline size_t
skip_8(void * fp,COUNT count)113 skip_8 (void *fp, COUNT count)
114 {
115 	int i;
116 	for (i = 0; i < count; ++i)
117 	{
118 		if (read_8(fp, NULL) != 1)
119 			return 0;
120 	}
121 	return 1;
122 }
123 
124 static inline size_t
read_str(void * fp,char * str,COUNT count)125 read_str (void *fp, char *str, COUNT count)
126 {
127 	// no type conversion needed for strings
128 	return read_a8 (fp, (BYTE *)str, count);
129 }
130 
131 static inline size_t
read_a16(void * fp,UWORD * ar,COUNT count)132 read_a16 (void *fp, UWORD *ar, COUNT count)
133 {
134 	assert (ar != NULL);
135 
136 	for ( ; count > 0; --count, ++ar)
137 	{
138 		if (read_16 (fp, ar) != 1)
139 			return 0;
140 	}
141 	return 1;
142 }
143 
144 static void
LoadShipQueue(void * fh,QUEUE * pQueue,DWORD size)145 LoadShipQueue (void *fh, QUEUE *pQueue, DWORD size)
146 {
147 	COUNT num_links = size / 11;
148 
149 	while (num_links--)
150 	{
151 		HSHIPFRAG hStarShip;
152 		SHIP_FRAGMENT *FragPtr;
153 		COUNT Index;
154 
155 		read_16 (fh, &Index);
156 
157 		hStarShip = CloneShipFragment (Index, pQueue, 0);
158 		FragPtr = LockShipFrag (pQueue, hStarShip);
159 
160 		// Read SHIP_FRAGMENT elements
161 		read_8  (fh, &FragPtr->captains_name_index);
162 		read_8  (fh, &FragPtr->race_id);
163 		read_8  (fh, &FragPtr->index);
164 		read_16 (fh, &FragPtr->crew_level);
165 		read_16 (fh, &FragPtr->max_crew);
166 		read_8  (fh, &FragPtr->energy_level);
167 		read_8  (fh, &FragPtr->max_energy);
168 
169 		UnlockShipFrag (pQueue, hStarShip);
170 	}
171 }
172 
173 static void
LoadRaceQueue(void * fh,QUEUE * pQueue,DWORD size)174 LoadRaceQueue (void *fh, QUEUE *pQueue, DWORD size)
175 {
176 	COUNT num_links = size / 30;
177 
178 	while (num_links--)
179 	{
180 		HFLEETINFO hStarShip;
181 		FLEET_INFO *FleetPtr;
182 		COUNT Index;
183 
184 		read_16 (fh, &Index);
185 
186 		hStarShip = GetStarShipFromIndex (pQueue, Index);
187 		FleetPtr = LockFleetInfo (pQueue, hStarShip);
188 
189 		// Read FLEET_INFO elements
190 		read_16 (fh, &FleetPtr->allied_state);
191 		read_8  (fh, &FleetPtr->days_left);
192 		read_8  (fh, &FleetPtr->growth_fract);
193 		read_16 (fh, &FleetPtr->crew_level);
194 		read_16 (fh, &FleetPtr->max_crew);
195 		read_8  (fh, &FleetPtr->growth);
196 		read_8  (fh, &FleetPtr->max_energy);
197 		read_16s(fh, &FleetPtr->loc.x);
198 		read_16s(fh, &FleetPtr->loc.y);
199 
200 		read_16 (fh, &FleetPtr->actual_strength);
201 		read_16 (fh, &FleetPtr->known_strength);
202 		read_16s(fh, &FleetPtr->known_loc.x);
203 		read_16s(fh, &FleetPtr->known_loc.y);
204 		read_8  (fh, &FleetPtr->growth_err_term);
205 		read_8  (fh, &FleetPtr->func_index);
206 		read_16s(fh, &FleetPtr->dest_loc.x);
207 		read_16s(fh, &FleetPtr->dest_loc.y);
208 
209 		UnlockFleetInfo (pQueue, hStarShip);
210 	}
211 }
212 
213 static void
LoadGroupQueue(void * fh,QUEUE * pQueue,DWORD size)214 LoadGroupQueue (void *fh, QUEUE *pQueue, DWORD size)
215 {
216 	COUNT num_links = size / 13;
217 
218 	while (num_links--)
219 	{
220 		HIPGROUP hGroup;
221 		IP_GROUP *GroupPtr;
222 
223 		hGroup = BuildGroup (pQueue, 0);
224 		GroupPtr = LockIpGroup (pQueue, hGroup);
225 
226 		read_16 (fh, &GroupPtr->group_counter);
227 		read_8  (fh, &GroupPtr->race_id);
228 		read_8  (fh, &GroupPtr->sys_loc);
229 		read_8  (fh, &GroupPtr->task);
230 		read_8  (fh, &GroupPtr->in_system); /* was crew_level */
231 		read_8  (fh, &GroupPtr->dest_loc);
232 		read_8  (fh, &GroupPtr->orbit_pos);
233 		read_8  (fh, &GroupPtr->group_id); /* was max_energy */
234 		read_16s(fh, &GroupPtr->loc.x);
235 		read_16s(fh, &GroupPtr->loc.y);
236 
237 		UnlockIpGroup (pQueue, hGroup);
238 	}
239 }
240 
241 static void
LoadEncounter(ENCOUNTER * EncounterPtr,void * fh)242 LoadEncounter (ENCOUNTER *EncounterPtr, void *fh)
243 {
244 	COUNT i;
245 
246 	EncounterPtr->pred = 0;
247 	EncounterPtr->succ = 0;
248 	EncounterPtr->hElement = 0;
249 	read_16s (fh, &EncounterPtr->transition_state);
250 	read_16s (fh, &EncounterPtr->origin.x);
251 	read_16s (fh, &EncounterPtr->origin.y);
252 	read_16  (fh, &EncounterPtr->radius);
253 	// former STAR_DESC fields
254 	read_16s (fh, &EncounterPtr->loc_pt.x);
255 	read_16s (fh, &EncounterPtr->loc_pt.y);
256 	read_8   (fh, &EncounterPtr->race_id);
257 	read_8   (fh, &EncounterPtr->num_ships);
258 	read_8   (fh, &EncounterPtr->flags);
259 
260 	// Load each entry in the BRIEF_SHIP_INFO array
261 	for (i = 0; i < MAX_HYPER_SHIPS; i++)
262 	{
263 		BRIEF_SHIP_INFO *ShipInfo = &EncounterPtr->ShipList[i];
264 
265 		read_8   (fh, &ShipInfo->race_id);
266 		read_16  (fh, &ShipInfo->crew_level);
267 		read_16  (fh, &ShipInfo->max_crew);
268 		read_8   (fh, &ShipInfo->max_energy);
269 	}
270 
271 	// Load the stuff after the BRIEF_SHIP_INFO array
272 	read_32s (fh, &EncounterPtr->log_x);
273 	read_32s (fh, &EncounterPtr->log_y);
274 }
275 
276 static void
LoadEvent(EVENT * EventPtr,void * fh)277 LoadEvent (EVENT *EventPtr, void *fh)
278 {
279 	EventPtr->pred = 0;
280 	EventPtr->succ = 0;
281 	read_8   (fh, &EventPtr->day_index);
282 	read_8   (fh, &EventPtr->month_index);
283 	read_16  (fh, &EventPtr->year_index);
284 	read_8   (fh, &EventPtr->func_index);
285 }
286 
287 static void
LoadClockState(CLOCK_STATE * ClockPtr,void * fh)288 LoadClockState (CLOCK_STATE *ClockPtr, void *fh)
289 {
290 	read_8   (fh, &ClockPtr->day_index);
291 	read_8   (fh, &ClockPtr->month_index);
292 	read_16  (fh, &ClockPtr->year_index);
293 	read_16s (fh, &ClockPtr->tick_count);
294 	read_16s (fh, &ClockPtr->day_in_ticks);
295 }
296 
297 static BOOLEAN
LoadGameState(GAME_STATE * GSPtr,void * fh)298 LoadGameState (GAME_STATE *GSPtr, void *fh)
299 {
300 	DWORD magic;
301 	read_32 (fh, &magic);
302 	if (magic != GLOBAL_STATE_TAG)
303 	{
304 		return FALSE;
305 	}
306 	read_32 (fh, &magic);
307 	if (magic != 75)
308 	{
309 		/* Chunk is the wrong size. */
310 		return FALSE;
311 	}
312 	read_8   (fh, &GSPtr->glob_flags);
313 	read_8   (fh, &GSPtr->CrewCost);
314 	read_8   (fh, &GSPtr->FuelCost);
315 	read_a8  (fh, GSPtr->ModuleCost, NUM_MODULES);
316 	read_a8  (fh, GSPtr->ElementWorth, NUM_ELEMENT_CATEGORIES);
317 	read_16  (fh, &GSPtr->CurrentActivity);
318 
319 	LoadClockState (&GSPtr->GameClock, fh);
320 
321 	read_16s (fh, &GSPtr->autopilot.x);
322 	read_16s (fh, &GSPtr->autopilot.y);
323 	read_16s (fh, &GSPtr->ip_location.x);
324 	read_16s (fh, &GSPtr->ip_location.y);
325 	/* STAMP ShipStamp */
326 	read_16s (fh, &GSPtr->ShipStamp.origin.x);
327 	read_16s (fh, &GSPtr->ShipStamp.origin.y);
328 	read_16  (fh, &GSPtr->ShipFacing);
329 	read_8   (fh, &GSPtr->ip_planet);
330 	read_8   (fh, &GSPtr->in_orbit);
331 
332 	/* VELOCITY_DESC velocity */
333 	read_16  (fh, &GSPtr->velocity.TravelAngle);
334 	read_16s (fh, &GSPtr->velocity.vector.width);
335 	read_16s (fh, &GSPtr->velocity.vector.height);
336 	read_16s (fh, &GSPtr->velocity.fract.width);
337 	read_16s (fh, &GSPtr->velocity.fract.height);
338 	read_16s (fh, &GSPtr->velocity.error.width);
339 	read_16s (fh, &GSPtr->velocity.error.height);
340 	read_16s (fh, &GSPtr->velocity.incr.width);
341 	read_16s (fh, &GSPtr->velocity.incr.height);
342 
343 	read_32 (fh, &magic);
344 	if (magic != GAME_STATE_TAG)
345 	{
346 		return FALSE;
347 	}
348 	memset (GSPtr->GameState, 0, sizeof (GSPtr->GameState));
349 	read_32 (fh, &magic);
350 	if (magic > sizeof (GSPtr->GameState))
351 	{
352 		read_a8 (fh, GSPtr->GameState, sizeof (GSPtr->GameState));
353 		skip_8  (fh, magic - sizeof (GSPtr->GameState));
354 	}
355 	else
356 	{
357 		read_a8 (fh, GSPtr->GameState, magic);
358 	}
359 	return TRUE;
360 }
361 
362 static BOOLEAN
LoadSisState(SIS_STATE * SSPtr,void * fp)363 LoadSisState (SIS_STATE *SSPtr, void *fp)
364 {
365 	if (
366 			read_32s (fp, &SSPtr->log_x) != 1 ||
367 			read_32s (fp, &SSPtr->log_y) != 1 ||
368 			read_32  (fp, &SSPtr->ResUnits) != 1 ||
369 			read_32  (fp, &SSPtr->FuelOnBoard) != 1 ||
370 			read_16  (fp, &SSPtr->CrewEnlisted) != 1 ||
371 			read_16  (fp, &SSPtr->TotalElementMass) != 1 ||
372 			read_16  (fp, &SSPtr->TotalBioMass) != 1 ||
373 			read_a8  (fp, SSPtr->ModuleSlots, NUM_MODULE_SLOTS) != 1 ||
374 			read_a8  (fp, SSPtr->DriveSlots, NUM_DRIVE_SLOTS) != 1 ||
375 			read_a8  (fp, SSPtr->JetSlots, NUM_JET_SLOTS) != 1 ||
376 			read_8   (fp, &SSPtr->NumLanders) != 1 ||
377 			read_a16 (fp, SSPtr->ElementAmounts, NUM_ELEMENT_CATEGORIES) != 1 ||
378 
379 			read_str (fp, SSPtr->ShipName, SIS_NAME_SIZE) != 1 ||
380 			read_str (fp, SSPtr->CommanderName, SIS_NAME_SIZE) != 1 ||
381 			read_str (fp, SSPtr->PlanetName, SIS_NAME_SIZE) != 1
382 		)
383 		return FALSE;
384 	return TRUE;
385 }
386 
387 static BOOLEAN
LoadSummary(SUMMARY_DESC * SummPtr,void * fp)388 LoadSummary (SUMMARY_DESC *SummPtr, void *fp)
389 {
390 	DWORD magic;
391 	DWORD nameSize = 0;
392 	if (!read_32 (fp, &magic))
393 		return FALSE;
394 	if (magic == SAVEFILE_TAG)
395 	{
396 		if (read_32 (fp, &magic) != 1 || magic != SUMMARY_TAG)
397 			return FALSE;
398 		if (read_32 (fp, &magic) != 1 || magic < 160)
399 			return FALSE;
400 		nameSize = magic - 160;
401 	}
402 	else
403 	{
404 		return FALSE;
405 	}
406 
407 	if (!LoadSisState (&SummPtr->SS, fp))
408 		return FALSE;
409 
410 	if (		read_8  (fp, &SummPtr->Activity) != 1 ||
411 			read_8  (fp, &SummPtr->Flags) != 1 ||
412 			read_8  (fp, &SummPtr->day_index) != 1 ||
413 			read_8  (fp, &SummPtr->month_index) != 1 ||
414 			read_16 (fp, &SummPtr->year_index) != 1 ||
415 			read_8  (fp, &SummPtr->MCreditLo) != 1 ||
416 			read_8  (fp, &SummPtr->MCreditHi) != 1 ||
417 			read_8  (fp, &SummPtr->NumShips) != 1 ||
418 			read_8  (fp, &SummPtr->NumDevices) != 1 ||
419 			read_a8 (fp, SummPtr->ShipList, MAX_BUILT_SHIPS) != 1 ||
420 			read_a8 (fp, SummPtr->DeviceList, MAX_EXCLUSIVE_DEVICES) != 1
421 		)
422 		return FALSE;
423 
424 	if (nameSize < SAVE_NAME_SIZE)
425 	{
426 		if (read_a8s (fp, SummPtr->SaveName, nameSize) != 1)
427 			return FALSE;
428 		SummPtr->SaveName[nameSize] = 0;
429 	}
430 	else
431 	{
432 		DWORD remaining = nameSize - SAVE_NAME_SIZE + 1;
433 		if (read_a8s (fp, SummPtr->SaveName, SAVE_NAME_SIZE-1) != 1)
434 			return FALSE;
435 		SummPtr->SaveName[SAVE_NAME_SIZE-1] = 0;
436 		if (skip_8 (fp, remaining) != 1)
437 			return FALSE;
438 	}
439 	return TRUE;
440 }
441 
442 static void
LoadStarDesc(STAR_DESC * SDPtr,void * fh)443 LoadStarDesc (STAR_DESC *SDPtr, void *fh)
444 {
445 	read_16s(fh, &SDPtr->star_pt.x);
446 	read_16s(fh, &SDPtr->star_pt.y);
447 	read_8  (fh, &SDPtr->Type);
448 	read_8  (fh, &SDPtr->Index);
449 	read_8  (fh, &SDPtr->Prefix);
450 	read_8  (fh, &SDPtr->Postfix);
451 }
452 
453 static void
LoadScanInfo(uio_Stream * fh,DWORD flen)454 LoadScanInfo (uio_Stream *fh, DWORD flen)
455 {
456 	GAME_STATE_FILE *fp = OpenStateFile (STARINFO_FILE, "wb");
457 	if (fp)
458 	{
459 		while (flen)
460 		{
461 			DWORD val;
462 			read_32 (fh, &val);
463 			swrite_32 (fp, val);
464 			flen -= 4;
465 		}
466 		CloseStateFile (fp);
467 	}
468 }
469 
470 static void
LoadGroupList(uio_Stream * fh,DWORD chunksize)471 LoadGroupList (uio_Stream *fh, DWORD chunksize)
472 {
473 	GAME_STATE_FILE *fp = OpenStateFile (RANDGRPINFO_FILE, "rb");
474 	if (fp)
475 	{
476 		GROUP_HEADER h;
477 		BYTE LastEnc, NumGroups;
478 		int i;
479 		ReadGroupHeader (fp, &h);
480 		/* There's only supposed to be one of these, so group 0 should be
481 		 * zero here whenever we're here. We add the group list to the
482 		 * end here. */
483 		h.GroupOffset[0] = LengthStateFile (fp);
484 		SeekStateFile (fp, 0, SEEK_SET);
485 		WriteGroupHeader (fp, &h);
486 		SeekStateFile (fp, h.GroupOffset[0], SEEK_SET);
487 		read_8 (fh, &LastEnc);
488 		NumGroups = (chunksize - 1) / 14;
489 		swrite_8 (fp, LastEnc);
490 		swrite_8 (fp, NumGroups);
491 		for (i = 0; i < NumGroups; ++i)
492 		{
493 			BYTE race_outer;
494 			IP_GROUP ip;
495 			read_8   (fh, &race_outer);
496 			read_16  (fh, &ip.group_counter);
497 			read_8   (fh, &ip.race_id);
498 			read_8   (fh, &ip.sys_loc);
499 			read_8   (fh, &ip.task);
500 			read_8   (fh, &ip.in_system);
501 			read_8   (fh, &ip.dest_loc);
502 			read_8   (fh, &ip.orbit_pos);
503 			read_8   (fh, &ip.group_id);
504 			read_16s (fh, &ip.loc.x);
505 			read_16s (fh, &ip.loc.y);
506 
507 			swrite_8 (fp, race_outer);
508 			WriteIpGroup (fp, &ip);
509 		}
510 		CloseStateFile (fp);
511 	}
512 }
513 
514 static void
LoadBattleGroup(uio_Stream * fh,DWORD chunksize)515 LoadBattleGroup (uio_Stream *fh, DWORD chunksize)
516 {
517 	GAME_STATE_FILE *fp;
518 	GROUP_HEADER h;
519 	DWORD encounter, offset;
520 	BYTE current;
521 	int i;
522 
523 	read_32 (fh, &encounter);
524 	read_8 (fh, &current);
525 	chunksize -= 5;
526 	if (encounter)
527 	{
528 		/* This is a defined group, so it's new */
529 		fp = OpenStateFile (DEFGRPINFO_FILE, "rb");
530 		offset = LengthStateFile (fp);
531 		memset (&h, 0, sizeof (GROUP_HEADER));
532 	}
533 	else
534 	{
535 		/* This is the random group. Load in what was there,
536 		 * as we might have already seen the Group List. */
537 		fp = OpenStateFile (RANDGRPINFO_FILE, "rb");
538 		current = FALSE;
539 		offset = 0;
540 		ReadGroupHeader (fp, &h);
541 	}
542 	if (!fp)
543 	{
544 		skip_8 (fh, chunksize);
545 		return;
546 	}
547 	read_16 (fh, &h.star_index);
548 	read_8  (fh, &h.day_index);
549 	read_8  (fh, &h.month_index);
550 	read_16 (fh, &h.year_index);
551 	read_8  (fh, &h.NumGroups);
552 	chunksize -= 7;
553 	/* Write out the half-finished state file so that we can use
554 	 * the file size to compute group offsets */
555 	SeekStateFile (fp, offset, SEEK_SET);
556 	WriteGroupHeader (fp, &h);
557 	for (i = 1; i <= h.NumGroups; ++i)
558 	{
559 		int j;
560 		BYTE icon, NumShips;
561 		read_8 (fh, &icon);
562 		read_8 (fh, &NumShips);
563 		chunksize -= 2;
564 		h.GroupOffset[i] = LengthStateFile (fp);
565 		SeekStateFile (fp, h.GroupOffset[i], SEEK_SET);
566 		swrite_8 (fp, icon);
567 		swrite_8 (fp, NumShips);
568 		for (j = 0; j < NumShips; ++j)
569 		{
570 			BYTE race_outer;
571 			SHIP_FRAGMENT sf;
572 			read_8  (fh, &race_outer);
573                         read_8  (fh, &sf.captains_name_index);
574                         read_8  (fh, &sf.race_id);
575                         read_8  (fh, &sf.index);
576                         read_16 (fh, &sf.crew_level);
577                         read_16 (fh, &sf.max_crew);
578                         read_8  (fh, &sf.energy_level);
579                         read_8  (fh, &sf.max_energy);
580 			chunksize -= 10;
581 
582 			swrite_8 (fp, race_outer);
583 			WriteShipFragment (fp, &sf);
584 		}
585 	}
586 	/* Now that the GroupOffset array is properly initialized,
587 	 * write the header back out. */
588 	SeekStateFile (fp, offset, SEEK_SET);
589 	WriteGroupHeader (fp, &h);
590 	CloseStateFile (fp);
591 	/* And update the gamestate accordingly, if we're a defined group. */
592 	if (encounter)
593 	{
594 		SET_GAME_STATE_32 (SHOFIXTI_GRPOFFS0 + (encounter - 1) * 32, offset);
595 		if (current)
596 		{
597 			GLOBAL (BattleGroupRef) = offset;
598 		}
599 	}
600 	/* Consistency check. */
601 	if (chunksize)
602 	{
603 		log_add (log_Warning, "BattleGroup chunk mis-sized!");
604 	}
605 }
606 
607 BOOLEAN
LoadGame(COUNT which_game,SUMMARY_DESC * SummPtr)608 LoadGame (COUNT which_game, SUMMARY_DESC *SummPtr)
609 {
610 	uio_Stream *in_fp;
611 	char file[PATH_MAX];
612 	SUMMARY_DESC loc_sd;
613 	COUNT num_links;
614 	STAR_DESC SD;
615 	ACTIVITY Activity;
616 	DWORD chunk, chunkSize;
617 	BOOLEAN first_group_spec = TRUE;
618 
619 	sprintf (file, "uqmsave.%02u", which_game);
620 	in_fp = res_OpenResFile (saveDir, file, "rb");
621 	if (!in_fp)
622 		return LoadLegacyGame (which_game, SummPtr);
623 
624 	if (!LoadSummary (&loc_sd, in_fp))
625 	{
626 		res_CloseResFile (in_fp);
627 		return LoadLegacyGame (which_game, SummPtr);
628 	}
629 
630 	if (!SummPtr)
631 	{
632 		SummPtr = &loc_sd;
633 	}
634 	else
635 	{	// only need summary for displaying to user
636 		memcpy (SummPtr, &loc_sd, sizeof (*SummPtr));
637 		res_CloseResFile (in_fp);
638 		return TRUE;
639 	}
640 
641 	GlobData.SIS_state = SummPtr->SS;
642 
643 	ReinitQueue (&GLOBAL (GameClock.event_q));
644 	ReinitQueue (&GLOBAL (encounter_q));
645 	ReinitQueue (&GLOBAL (ip_group_q));
646 	ReinitQueue (&GLOBAL (npc_built_ship_q));
647 	ReinitQueue (&GLOBAL (built_ship_q));
648 
649 	memset (&GLOBAL (GameState[0]), 0, sizeof (GLOBAL (GameState)));
650 	Activity = GLOBAL (CurrentActivity);
651 	if (!LoadGameState (&GlobData.Game_state, in_fp))
652 	{
653 		res_CloseResFile (in_fp);
654 		return FALSE;
655 	}
656 	NextActivity = GLOBAL (CurrentActivity);
657 	GLOBAL (CurrentActivity) = Activity;
658 
659 	chunk = 0;
660 	while (TRUE)
661 	{
662 		if (read_32(in_fp, &chunk) != 1)
663 		{
664 			break;
665 		}
666 		if (read_32(in_fp, &chunkSize) != 1)
667 		{
668 			res_CloseResFile (in_fp);
669 			return FALSE;
670 		}
671 		switch (chunk)
672 		{
673 		case RACE_Q_TAG:
674 			LoadRaceQueue (in_fp, &GLOBAL (avail_race_q), chunkSize);
675 			break;
676 		case IP_GRP_Q_TAG:
677 			LoadGroupQueue (in_fp, &GLOBAL (ip_group_q), chunkSize);
678 			break;
679 		case ENCOUNTERS_TAG:
680 			num_links = chunkSize / 65;
681 			while (num_links--)
682 			{
683 				HENCOUNTER hEncounter;
684 				ENCOUNTER *EncounterPtr;
685 
686 				hEncounter = AllocEncounter ();
687 				LockEncounter (hEncounter, &EncounterPtr);
688 
689 				LoadEncounter (EncounterPtr, in_fp);
690 
691 				UnlockEncounter (hEncounter);
692 				PutEncounter (hEncounter);
693 			}
694 			break;
695 		case EVENTS_TAG:
696 			num_links = chunkSize / 5;
697 #ifdef DEBUG_LOAD
698 			log_add (log_Debug, "EVENTS:");
699 #endif /* DEBUG_LOAD */
700 			while (num_links--)
701 			{
702 				HEVENT hEvent;
703 				EVENT *EventPtr;
704 
705 				hEvent = AllocEvent ();
706 				LockEvent (hEvent, &EventPtr);
707 
708 				LoadEvent (EventPtr, in_fp);
709 
710 #ifdef DEBUG_LOAD
711 				log_add (log_Debug, "\t%u/%u/%u -- %u",
712 						 EventPtr->month_index,
713 						 EventPtr->day_index,
714 						 EventPtr->year_index,
715 						 EventPtr->func_index);
716 #endif /* DEBUG_LOAD */
717 				UnlockEvent (hEvent);
718 				PutEvent (hEvent);
719 			}
720 			break;
721 		case STAR_TAG:
722 			LoadStarDesc (&SD, in_fp);
723 			break;
724 		case NPC_SHIP_Q_TAG:
725 			LoadShipQueue (in_fp, &GLOBAL (npc_built_ship_q), chunkSize);
726 			break;
727 		case SHIP_Q_TAG:
728 			LoadShipQueue (in_fp, &GLOBAL (built_ship_q), chunkSize);
729 			break;
730 		case SCAN_TAG:
731 			LoadScanInfo (in_fp, chunkSize);
732 			break;
733 		case GROUP_LIST_TAG:
734 			if (first_group_spec)
735 			{
736 				InitGroupInfo (TRUE);
737 				GLOBAL (BattleGroupRef) = 0;
738 				first_group_spec = FALSE;
739 			}
740 			LoadGroupList (in_fp, chunkSize);
741 			break;
742 		case BATTLE_GROUP_TAG:
743 			if (first_group_spec)
744 			{
745 				InitGroupInfo (TRUE);
746 				GLOBAL (BattleGroupRef) = 0;
747 				first_group_spec = FALSE;
748 			}
749 			LoadBattleGroup (in_fp, chunkSize);
750 			break;
751 		default:
752 			log_add (log_Debug, "Skipping chunk of tag %08X (size %u)", chunk, chunkSize);
753 			if (skip_8(in_fp, chunkSize) != 1)
754 			{
755 				res_CloseResFile (in_fp);
756 				return FALSE;
757 			}
758 			break;
759 		}
760 	}
761 	res_CloseResFile (in_fp);
762 
763 	EncounterGroup = 0;
764 	EncounterRace = -1;
765 
766 	ReinitQueue (&race_q[0]);
767 	ReinitQueue (&race_q[1]);
768 	CurStarDescPtr = FindStar (NULL, &SD.star_pt, 0, 0);
769 	if (!(NextActivity & START_ENCOUNTER)
770 			&& LOBYTE (NextActivity) == IN_INTERPLANETARY)
771 		NextActivity |= START_INTERPLANETARY;
772 
773 	return TRUE;
774 }
775