Merge branch 'better-saves'

This commit is contained in:
Michael Martin
2013-10-19 17:12:38 -07:00
21 changed files with 2397 additions and 1208 deletions
+1 -1
View File
@@ -1831,7 +1831,7 @@ Empty Slot
#(Saved Game - Date:)
Saved Game - Date:
Saved Game
#(Sound) -- OPTION_STRING_BASE
+149
View File
@@ -0,0 +1,149 @@
SAVEFILE FORMAT
---------------
This document represents a work in progress. The save format described
here will evolve before finalization for 0.8.
The old save file format used a custom compressor and was very finicky
about padding and alignment for machines that were not even in use
anymore. It was also extremely fragile and hard for modders to extend.
The new save format seeks to alleviate these problems.
GENERAL FORMAT
--------------
All multibyte values are little-endian. There are no alignment
restrictions inherent in the format.
The save file begins with a 32-bit identifier and version number (a
"magic number") that identifies it as a particular version of an UQM
save file. If the format changes in a way that older versions of UQM
cannot read it, either the identifier or the version number should
change.
The vanilla UQM system has a save number of 0x01534d55, which, if
interpreted as a byte stream is UMS (Ur-quan Masters Save) and a
binary 1 (version 1). Vanilla UQM reserves the tags "UMSx" for all x
for use to evolve the core save format. Modders are encouraged, but
not required, to also use the fourth bit as a version number.
Following the 32-bit file identifier comes a series of chunks. All
chunks have the same general format: a 32-bit tag, similar to that of
the file as a whole, followed by a 32-bit integer specifying the size
of the chunk, and then that many bytes of data.
Bytes in a chunk tag should all be in the range 0x20-0x7E -- that is,
they should be printable ASCII characters. Chunks are traditionally
referred to by their tag names.
Chunks whose tag has a least significant byte in the range 0x41-0x5a,
inclusive---that is to say, whose names start with a capital
letter---are mandatory. If you extend the set of mandatory chunks, you
must increase the version of the file. Tags that do not start with a
capital letter may be ignored by other versions of UQM that otherwise
understand that data version.
(Why would you want to have ignorable bits of save file? Such chunks
may contain helpful but ancillary information. For instance, at the
time of this writing, there is an outstanding bug that life forms are
un-stunned if you save and load in orbit. One could add a new chunk
that tracks the stunned status of life forms on the planet you're in
orbit around, and just revert to the old behavior if it's not there or
if you're running on a version without that fix. Such an ancillary
data chunk would have a tag like "stun" or "biot".)
Note also that despite being "mandatory", it is not the case that all
chunks will be present in all save files. Different data is saved out
depending on the situation you saved in.
Unless otherwise specified, chunks may be stored in any order in the
save file.
CHUNK INVENTORY
---------------
- "Summ": Summary. This chunk must come first. This chunk
carries the flagship configuration information and some overview
information that is displayed on the savegame view screen. It is of
variable length, because the last element of this chunk is the name
of the save as chosen by the user.
- "GlSt": Global State. This chunk must come second, after
Summ. Represents most of the data in the global state structure in
globdata.h. BattleGroupRef is excised from this; its value is
computed later on.
- "GmSt": Game State. This chunk must come third, after GlSt. This is
the gigantic bitfield that the GET_GAME_STATE macros modify. It is
variably-sized; excess bytes in this array will be ignored, and if
there are insufficient bytes in the save file, the remaining bits
will be initialized to zero. Modders setting extra event flags may
be able to import a legacy game into a sensible state by choosing
their defaults judiciously.
- "Evts": Events. An array of values describing scripted future
events.
- "Enct": Encounters. Details of battle groups that are pursuing you
through HyperSpace.
- "RacQ": Available Race Queue. Which species are active in the game,
where they are, etc. Corresponds to avail_race_q.
- "IGpQ": Interplanetary Group Queue. Battlegroup information for
ships in your current star system, if you're in a current star
system. It is currently unclear whether or not this ever needs to
exist, but in keeping with legacy logic, it will appear whenever you
are in a star system but not in the middle of an encounter.
- "NpcQ": NPC Queue. Battlegroup information for ships you are in the
middle of encountering. This should only appear if your loaded
activity is "IN_ENCOUNTER" and loading the game will trigger the red
alert.
- "ShpQ": Ship Queue. Battlegroup information for your flagship's
escort fleet.
- "Star": Star Description. Basic indexing information to indicate
which star system you are in.
- "Scan": Scanner Masks. This is a semi-structured tree of DWORDs that
represents which planetary resources have been captured or
removed. It is a format roughly similar to the old star info
statefile format (see doc/devel/statefile) but little-endianness is
enforced, making this chunk endian-safe where the old statefile dump
was not.
- "BtGp": Battle Group. Defines the relevant information for all ships
in a given star system. This includes an "encounter ID" - randomly
generated fleets have an encounter ID of zero, and ones built by the
plot have an 32-bit identifier. Vanilla UQM reserves the first 32
encounter IDs for itself, and uses 15 of them (random encounter,
Ur-Quan Probe, Shofixti Survivor, Zoq-Fot-Pik Emissary, Unzervalt
Guardian, nine Melnorme Traders, and the final boss). This also
includes the expiration date for random encounters and which system
they are relevant to. Much of this information was originally stored
in randgrp.dat, but it has echoes in defgrp.dat as well. The data
here is a ragged 2D array of a slight extension of the SHIP_FRAGMENT
structure. There is one BtGp chunk per defined group. (Since one of
these is defined at game start, and the random encounter structure
has values that mean 'no encounter present', there should always be
at least two of these chunks in any save.)
- "Grps": Active Battle Groups. These are IP_GROUP structures to
supplement the SHIP_FRAGMENTs specified in BtGp chunks. They give
more detailed information about the precise location and disposition
of each ship in the system you are either in or most recently left.
THINGS LEFT TO DO
-----------------
The last 448 bits in GmSt probably shouldn't exist. However, we should
not remove them from the source base (and thus the save file) until
after other pending commits have been merged. When this happens, we
can break out those bits into an array of DWORDs instead. The loading
code basically ignores those GmSt bits by overwriting them while
loading later chunks, and GmSt is an expandable array in the first
place, so removing those final bits from the Game State array should
be compatible in both directions.
+2 -1
View File
@@ -4,6 +4,7 @@ uqm_CFILES="battle.c battlecontrols.c border.c build.c cleanup.c clock.c
cyborg.c demo.c displist.c dummy.c encount.c flash.c fmv.c galaxy.c
gameev.c gameinp.c gameopt.c gendef.c getchar.c globdata.c gravity.c
cons_res.c grpinfo.c hyper.c init.c intel.c intro.c ipdisp.c load.c
load_legacy.c
loadship.c master.c menu.c misc.c oscill.c outfit.c pickship.c
plandata.c process.c restart.c save.c settings.c setup.c setupmenu.c
ship.c shipstat.c shipyard.c sis.c sounds.c starbase.c starcon.c
@@ -14,7 +15,7 @@ uqm_HFILES="battlecontrols.h battle.h build.h clock.h cnctdlg.h coderes.h
corecode.h credits.h demo.h displist.h dummy.h element.h encount.h
flash.h fmv.h gameev.h gameopt.h gamestr.h gendef.h globdata.h
grpinfo.h hyper.h ifontres.h igfxres.h ikey_con.h imusicre.h init.h
intel.h ipdisp.h isndres.h istrtab.h load.h master.h menustat.h
intel.h ipdisp.h isndres.h istrtab.h master.h menustat.h
nameref.h oscill.h pickship.h process.h races.h resinst.h respkg.h
restart.h save.h settings.h setup.h setupmenu.h shipcont.h ship.h
sis.h sounds.h starbase.h starcon.h state.h status.h tactrans.h
+1 -1
View File
@@ -31,8 +31,8 @@
#include "endian_uqm.h"
#include "gamestr.h"
#include "options.h"
#include "load.h"
#include "oscill.h"
#include "save.h"
#include "settings.h"
#include "setup.h"
#include "sounds.h"
+219 -30
View File
@@ -26,7 +26,6 @@
#include "sis.h"
#include "units.h"
#include "gamestr.h"
#include "load.h"
#include "options.h"
#include "save.h"
#include "settings.h"
@@ -253,7 +252,7 @@ DrawNameString (bool nameCaptain, UNICODE *Str, COUNT CursorPos,
// disallow the change
return (FALSE);
}
PreUpdateFlashRect ();
SetContextForeGroundColor (BackGround);
@@ -264,7 +263,7 @@ DrawNameString (bool nameCaptain, UNICODE *Str, COUNT CursorPos,
text_r.corner.x += *pchar_deltas++;
if (CursorPos < lf.CharCount) /* end of line */
--text_r.corner.x;
if (state & DDSHS_BLOCKCUR)
{ // Use block cursor for keyboardless systems
if (CursorPos == lf.CharCount)
@@ -284,7 +283,7 @@ DrawNameString (bool nameCaptain, UNICODE *Str, COUNT CursorPos,
{ // Insertion point cursor
text_r.extent.width = 1;
}
text_r.corner.y = r.corner.y;
text_r.extent.height = r.extent.height;
SetContextForeGroundColor (BLACK_COLOR);
@@ -292,7 +291,7 @@ DrawNameString (bool nameCaptain, UNICODE *Str, COUNT CursorPos,
SetContextForeGroundColor (ForeGround);
font_DrawText (&lf);
PostUpdateFlashRect ();
}
@@ -347,15 +346,190 @@ NameCaptainOrShip (bool nameCaptain)
utf8StringCopy (Setting, tes.MaxSize, buf);
else
utf8StringCopy (buf, sizeof (buf), Setting);
SetFlashRect (SFR_MENU_3DO);
DrawNameString (nameCaptain, buf, 0, DDSHS_NORMAL);
if (namingCB)
namingCB ();
}
static BOOLEAN
DrawSaveNameString (UNICODE *Str, COUNT CursorPos, COUNT state, COUNT gameIndex)
{
RECT r;
TEXT lf;
Color BackGround, ForeGround;
FONT Font;
UNICODE fullStr[256], dateStr[80];
DateToString (dateStr, sizeof dateStr, GLOBAL(GameClock.month_index),
GLOBAL(GameClock.day_index), GLOBAL(GameClock.year_index));
strncat (dateStr, ": ", sizeof(dateStr) - strlen(dateStr) -1);
snprintf (fullStr, sizeof fullStr, "%s%s", dateStr, Str);
SetContextForeGroundColor (BUILD_COLOR (MAKE_RGB15 (0x1B, 0x00, 0x1B), 0x33));
r.extent.width = 15;
if (MAX_SAVED_GAMES > 99)
r.extent.width += 5;
r.extent.height = 11;
r.corner.x = 8;
r.corner.y = (160 + ((gameIndex % SAVES_PER_PAGE) * 13));
DrawRectangle (&r);
r.extent.width = (204 - SAFE_X);
r.corner.x = (30 + SAFE_X);
DrawRectangle (&r);
Font = TinyFont;
lf.baseline.x = r.corner.x + 3;
lf.baseline.y = r.corner.y + 8;
BackGround = BUILD_COLOR (MAKE_RGB15 (0x1B, 0x00, 0x1B), 0x33);
ForeGround = BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x14), 0x01);
lf.align = ALIGN_LEFT;
SetContextFont (Font);
lf.pStr = fullStr;
lf.CharCount = (COUNT)~0;
if (!(state & DDSHS_EDIT))
{
TEXT t;
SetContextForeGroundColor (BLACK_COLOR);
DrawFilledRectangle (&r);
t.baseline.x = r.corner.x + 3;
t.baseline.y = r.corner.y + 8;
t.align = ALIGN_LEFT;
t.pStr = Str;
t.CharCount = (COUNT)~0;
SetContextForeGroundColor (CAPTAIN_NAME_TEXT_COLOR);
font_DrawText (&lf);
}
else
{ // editing state
COUNT i, FullCursorPos;
RECT text_r;
BYTE char_deltas[256];
BYTE *pchar_deltas;
TextRect (&lf, &text_r, char_deltas);
if ((text_r.extent.width + 2) >= r.extent.width)
{ // the text does not fit the input box size and so
// will not fit when displayed later
// disallow the change
return (FALSE);
}
PreUpdateFlashRect ();
SetContextForeGroundColor (BackGround);
DrawFilledRectangle (&r);
pchar_deltas = char_deltas;
FullCursorPos = CursorPos + strlen(dateStr) - 1;
for (i = FullCursorPos; i > 0; --i)
text_r.corner.x += *pchar_deltas++;
if (FullCursorPos < lf.CharCount) /* end of line */
--text_r.corner.x;
if (state & DDSHS_BLOCKCUR)
{ // Use block cursor for keyboardless systems
if (FullCursorPos == lf.CharCount)
{ // cursor at end-line -- use insertion point
text_r.extent.width = 1;
}
else if (FullCursorPos + 1 == lf.CharCount)
{ // extra pixel for last char margin
text_r.extent.width = (SIZE)*pchar_deltas + 2;
}
else
{ // normal mid-line char
text_r.extent.width = (SIZE)*pchar_deltas + 1;
}
}
else
{ // Insertion point cursor
text_r.extent.width = 1;
}
text_r.corner.y = r.corner.y;
text_r.extent.height = r.extent.height;
SetContextForeGroundColor (BLACK_COLOR);
DrawFilledRectangle (&text_r);
SetContextForeGroundColor (ForeGround);
font_DrawText (&lf);
PostUpdateFlashRect ();
}
return (TRUE);
}
static BOOLEAN
OnSaveNameChange (TEXTENTRY_STATE *pTES)
{
COUNT hl = DDSHS_EDIT;
COUNT *gameIndex = pTES->CbParam;
if (pTES->JoystickMode)
hl |= DDSHS_BLOCKCUR;
return DrawSaveNameString (pTES->BaseStr, pTES->CursorPos, hl, *gameIndex);
}
static BOOLEAN
NameSaveGame (COUNT gameIndex, UNICODE *buf)
{
TEXTENTRY_STATE tes;
COUNT CursPos = strlen(buf);
COUNT *gIndex = HMalloc (sizeof (COUNT));
RECT r;
*gIndex = gameIndex;
DrawSaveNameString (buf, CursPos, DDSHS_EDIT, gameIndex);
tes.MaxSize = SAVE_NAME_SIZE;
// text entry setup
tes.Initialized = FALSE;
tes.BaseStr = buf;
tes.CursorPos = CursPos;
tes.CbParam = gIndex;
tes.ChangeCallback = OnSaveNameChange;
tes.FrameCallback = 0;
r.extent.width = (204 - SAFE_X);
r.extent.height = 11;
r.corner.x = (30 + SAFE_X);
r.corner.y = (160 + ((gameIndex % SAVES_PER_PAGE) * 13));
SetFlashRect (&r);
if (!DoTextEntry (&tes))
buf[0] = 0;
SetFlashRect(NULL);
DrawSaveNameString (buf, CursPos, DDSHS_NORMAL, gameIndex);
if (namingCB)
namingCB ();
SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
HFree (gIndex);
SetFlashRect (NULL);
if (tes.Success)
return (TRUE);
else
return (FALSE);
}
void
@@ -611,7 +785,7 @@ DrawSavegameSummary (PICK_GAME_STATE *pickState, COUNT gameIndex)
UninitQueue (&GLOBAL (built_ship_q));
SetContextClipRect (&OldRect);
SetContext (SpaceContext);
// draw devices
s.origin.y = 13;
@@ -680,7 +854,7 @@ DrawSavegameSummary (PICK_GAME_STATE *pickState, COUNT gameIndex)
SetContextForeGroundColor (
BUILD_COLOR (MAKE_RGB15 (0x10, 0x00, 0x10), 0x01));
font_DrawText (&t);
// print the location
t.baseline.x = 6;
t.baseline.y = 139 + 6;
@@ -697,7 +871,7 @@ DrawSavegameSummary (PICK_GAME_STATE *pickState, COUNT gameIndex)
{
BYTE QuasiState;
STAR_DESC *SDPtr;
QuasiState = GET_GAME_STATE (ARILOU_SPACE_SIDE);
SET_GAME_STATE (ARILOU_SPACE_SIDE, 0);
SDPtr = FindStar (NULL, &starPt, 1, 1);
@@ -770,11 +944,11 @@ DrawGameSelection (PICK_GAME_STATE *pickState, COUNT selSlot)
COUNT curSlot;
UNICODE buf[256];
UNICODE buf2[80];
BatchGraphics ();
SetContextFont (TinyFont);
// Erase the selection menu
r.extent.width = 240;
r.extent.height = 65;
@@ -825,9 +999,7 @@ DrawGameSelection (PICK_GAME_STATE *pickState, COUNT selSlot)
{
DateToString (buf2, sizeof buf2, desc->month_index,
desc->day_index, desc->year_index);
snprintf (buf, sizeof buf, "%s %s",
GAME_STRING (SAVEGAME_STRING_BASE + 4), buf2);
// "Saved Game - Date:"
snprintf (buf, sizeof buf, "%s: %s", buf2, desc->SaveName[0] ? desc->SaveName : GAME_STRING (SAVEGAME_STRING_BASE + 4));
}
font_DrawText (&t);
}
@@ -928,7 +1100,7 @@ DoPickGame (MENU_STATE *pMS)
SetContext (SpaceContext);
RedrawPickDisplay (pickState, pMS->CurState);
}
SleepThreadUntil (TimeIn + ONE_SECOND / 30);
}
@@ -936,22 +1108,38 @@ DoPickGame (MENU_STATE *pMS)
}
static BOOLEAN
SaveLoadGame (PICK_GAME_STATE *pickState, COUNT gameIndex)
SaveLoadGame (PICK_GAME_STATE *pickState, COUNT gameIndex, BOOLEAN *canceled_by_user)
{
SUMMARY_DESC *desc = pickState->summary + gameIndex;
UNICODE nameBuf[256];
STAMP saveStamp;
BOOLEAN success;
saveStamp.frame = NULL;
// TODO: fix ConfirmSaveLoad() interface so it does not rely on
// MsgStamp != NULL parameter.
ConfirmSaveLoad (pickState->saving ? &saveStamp : NULL);
if (pickState->saving)
success = SaveGame (gameIndex, desc);
{
// Initialize the save name with whatever name is there already
// SAVE_NAME_SIZE is less than 256, so this is safe.
strncpy(nameBuf, desc->SaveName, SAVE_NAME_SIZE);
nameBuf[SAVE_NAME_SIZE] = 0;
if (NameSaveGame (gameIndex, nameBuf))
{
PlayMenuSound (MENU_SOUND_SUCCESS);
ConfirmSaveLoad (pickState->saving ? &saveStamp : NULL);
success = SaveGame (gameIndex, desc, nameBuf);
}
else
{
success = FALSE;
*canceled_by_user = TRUE;
}
}
else
{
ConfirmSaveLoad (pickState->saving ? &saveStamp : NULL);
success = LoadGame (gameIndex, NULL);
}
// TODO: the same should be done for both save and load if we also
// display a load problem message
@@ -1005,12 +1193,12 @@ PickGame (BOOLEAN saving, BOOLEAN fromMainMenu)
// draw the current savegame and fade in
SetTransitionSource (NULL);
BatchGraphics ();
SetContextBackGroundColor (BLACK_COLOR);
ClearDrawable ();
RedrawPickDisplay (&pickState, MenuState.CurState);
DrawSaveLoad (&pickState);
if (fromMainMenu)
{
UnbatchGraphics ();
@@ -1028,10 +1216,12 @@ PickGame (BOOLEAN saving, BOOLEAN fromMainMenu)
SetMenuSounds (MENU_SOUND_ARROWS | MENU_SOUND_PAGEUP | MENU_SOUND_PAGEDOWN,
0);
MenuState.InputFunc = DoPickGame;
// Save/load retry loop
while (1)
{
BOOLEAN canceled_by_user = FALSE;
pickState.success = FALSE;
DoInput (&MenuState, TRUE);
if (!pickState.success)
@@ -1039,11 +1229,11 @@ PickGame (BOOLEAN saving, BOOLEAN fromMainMenu)
lastUsedSlot = MenuState.CurState;
if (SaveLoadGame (&pickState, MenuState.CurState))
if (SaveLoadGame (&pickState, MenuState.CurState, &canceled_by_user))
break; // all good
// something broke
if (saving)
if (saving && !canceled_by_user)
SaveProblem ();
// TODO: Shouldn't we have a Problem() equivalent for Load too?
@@ -1058,7 +1248,7 @@ PickGame (BOOLEAN saving, BOOLEAN fromMainMenu)
{ // Load succeeded, signal up the chain
GLOBAL (CurrentActivity) |= CHECK_LOAD;
}
if (!(GLOBAL (CurrentActivity) & CHECK_ABORT) &&
(saving || (!pickState.success && !fromMainMenu)))
{ // Restore previous screen
@@ -1145,7 +1335,7 @@ GameOptions (void)
DrawMenuStateStrings (PM_SAVE_GAME, MenuState.CurState);
SetFlashRect (SFR_MENU_3DO);
SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
MenuState.InputFunc = DoGameOptions;
DoInput (&MenuState, TRUE);
@@ -1154,4 +1344,3 @@ GameOptions (void)
return !(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD));
}
+29 -13
View File
@@ -48,31 +48,31 @@ GLOBDATA GlobData;
BYTE
getGameState (int startBit, int endBit)
getGameState (BYTE *state, int startBit, int endBit)
{
return (BYTE) (((startBit >> 3) == (endBit >> 3)
? (GLOBAL (GameState[startBit >> 3]) >> (startBit & 7))
: ((GLOBAL (GameState[startBit >> 3]) >> (startBit & 7))
| (GLOBAL (GameState[endBit >> 3])
? (state[startBit >> 3] >> (startBit & 7))
: ((state[startBit >> 3] >> (startBit & 7))
| (state[endBit >> 3]
<< (endBit - startBit - (endBit & 7)))))
& ((1 << (endBit - startBit + 1)) - 1));
}
void
setGameState (int startBit, int endBit, BYTE val
setGameState (BYTE *state, int startBit, int endBit, BYTE val
#ifdef STATE_DEBUG
, const char *name
#endif
)
{
GLOBAL (GameState[startBit >> 3]) =
(GLOBAL (GameState[startBit >> 3])
state[startBit >> 3] =
(state[startBit >> 3]
& (BYTE) ~(((1 << (endBit - startBit + 1)) - 1) << (startBit & 7)))
| (BYTE)((val) << (startBit & 7));
if ((startBit >> 3) < (endBit >> 3)) {
GLOBAL (GameState[endBit >> 3]) =
(GLOBAL (GameState[endBit >> 3])
state[endBit >> 3] =
(state[endBit >> 3]
& (BYTE)~((1 << ((endBit & 7) + 1)) - 1))
| (BYTE)((val) >> (endBit - startBit - (endBit & 7)));
}
@@ -82,21 +82,21 @@ setGameState (int startBit, int endBit, BYTE val
}
DWORD
getGameState32 (int startBit)
getGameState32 (BYTE *state, int startBit)
{
DWORD v;
int shift;
for (v = 0, shift = 0; shift < 32; shift += 8, startBit += 8)
{
v |= getGameState (startBit, startBit + 7) << shift;
v |= getGameState (state, startBit, startBit + 7) << shift;
}
return v;
}
void
setGameState32 (int startBit, DWORD val
setGameState32 (BYTE *state, int startBit, DWORD val
#ifdef STATE_DEBUG
, const char *name
#endif
@@ -107,7 +107,7 @@ setGameState32 (int startBit, DWORD val
for (i = 0; i < 4; ++i, v >>= 8, startBit += 8)
{
setGameState (startBit, startBit + 7, v & 0xff
setGameState (state, startBit, startBit + 7, v & 0xff
#ifdef STATE_DEBUG
, "(ignored)"
#endif
@@ -119,6 +119,22 @@ setGameState32 (int startBit, DWORD val
#endif
}
void
copyGameState (BYTE *dest, DWORD target, BYTE *src, DWORD begin, DWORD end)
{
while (begin < end)
{
BYTE b;
DWORD delta = 7;
if (begin + delta > end)
delta = end - begin;
b = getGameState (src, begin, begin + delta);
setGameState (dest, target, target + delta, b);
begin += 8;
target += 8;
}
}
static void
CreateRadar (void)
{
+118 -91
View File
@@ -255,61 +255,6 @@ START_GAME_STATE
ADD_GAME_STATE (LANDER_SHIELDS, 4)
ADD_GAME_STATE (SHOFIXTI_GRPOFFS0, 8)
ADD_GAME_STATE (SHOFIXTI_GRPOFFS1, 8)
ADD_GAME_STATE (SHOFIXTI_GRPOFFS2, 8)
ADD_GAME_STATE (SHOFIXTI_GRPOFFS3, 8)
ADD_GAME_STATE (ZOQFOT_GRPOFFS0, 8)
ADD_GAME_STATE (ZOQFOT_GRPOFFS1, 8)
ADD_GAME_STATE (ZOQFOT_GRPOFFS2, 8)
ADD_GAME_STATE (ZOQFOT_GRPOFFS3, 8)
ADD_GAME_STATE (MELNORME0_GRPOFFS0, 8)
ADD_GAME_STATE (MELNORME0_GRPOFFS1, 8)
ADD_GAME_STATE (MELNORME0_GRPOFFS2, 8)
ADD_GAME_STATE (MELNORME0_GRPOFFS3, 8)
ADD_GAME_STATE (MELNORME1_GRPOFFS0, 8)
ADD_GAME_STATE (MELNORME1_GRPOFFS1, 8)
ADD_GAME_STATE (MELNORME1_GRPOFFS2, 8)
ADD_GAME_STATE (MELNORME1_GRPOFFS3, 8)
ADD_GAME_STATE (MELNORME2_GRPOFFS0, 8)
ADD_GAME_STATE (MELNORME2_GRPOFFS1, 8)
ADD_GAME_STATE (MELNORME2_GRPOFFS2, 8)
ADD_GAME_STATE (MELNORME2_GRPOFFS3, 8)
ADD_GAME_STATE (MELNORME3_GRPOFFS0, 8)
ADD_GAME_STATE (MELNORME3_GRPOFFS1, 8)
ADD_GAME_STATE (MELNORME3_GRPOFFS2, 8)
ADD_GAME_STATE (MELNORME3_GRPOFFS3, 8)
ADD_GAME_STATE (MELNORME4_GRPOFFS0, 8)
ADD_GAME_STATE (MELNORME4_GRPOFFS1, 8)
ADD_GAME_STATE (MELNORME4_GRPOFFS2, 8)
ADD_GAME_STATE (MELNORME4_GRPOFFS3, 8)
ADD_GAME_STATE (MELNORME5_GRPOFFS0, 8)
ADD_GAME_STATE (MELNORME5_GRPOFFS1, 8)
ADD_GAME_STATE (MELNORME5_GRPOFFS2, 8)
ADD_GAME_STATE (MELNORME5_GRPOFFS3, 8)
ADD_GAME_STATE (MELNORME6_GRPOFFS0, 8)
ADD_GAME_STATE (MELNORME6_GRPOFFS1, 8)
ADD_GAME_STATE (MELNORME6_GRPOFFS2, 8)
ADD_GAME_STATE (MELNORME6_GRPOFFS3, 8)
ADD_GAME_STATE (MELNORME7_GRPOFFS0, 8)
ADD_GAME_STATE (MELNORME7_GRPOFFS1, 8)
ADD_GAME_STATE (MELNORME7_GRPOFFS2, 8)
ADD_GAME_STATE (MELNORME7_GRPOFFS3, 8)
ADD_GAME_STATE (MELNORME8_GRPOFFS0, 8)
ADD_GAME_STATE (MELNORME8_GRPOFFS1, 8)
ADD_GAME_STATE (MELNORME8_GRPOFFS2, 8)
ADD_GAME_STATE (MELNORME8_GRPOFFS3, 8)
ADD_GAME_STATE (MET_MELNORME, 1)
ADD_GAME_STATE (MELNORME_RESCUE_REFUSED, 1)
ADD_GAME_STATE (MELNORME_RESCUE_COUNT, 3)
@@ -329,10 +274,6 @@ START_GAME_STATE
ADD_GAME_STATE (MELNORME_PISSED_COUNT, 2)
ADD_GAME_STATE (MELNORME_HATE_COUNT, 2)
ADD_GAME_STATE (URQUAN_PROBE_GRPOFFS0, 8)
ADD_GAME_STATE (URQUAN_PROBE_GRPOFFS1, 8)
ADD_GAME_STATE (URQUAN_PROBE_GRPOFFS2, 8)
ADD_GAME_STATE (URQUAN_PROBE_GRPOFFS3, 8)
ADD_GAME_STATE (PROBE_MESSAGE_DELIVERED, 1)
ADD_GAME_STATE (PROBE_ILWRATH_ENCOUNTER, 1)
@@ -647,11 +588,6 @@ START_GAME_STATE
ADD_GAME_STATE (URQUAN_PROTECTING_SAMATRA, 1)
ADD_GAME_STATE (COLONY_GRPOFFS0, 8)
ADD_GAME_STATE (COLONY_GRPOFFS1, 8)
ADD_GAME_STATE (COLONY_GRPOFFS2, 8)
ADD_GAME_STATE (COLONY_GRPOFFS3, 8)
#define THRADDASH_BODY_THRESHOLD 25
ADD_GAME_STATE (THRADDASH_BODY_COUNT, 5)
@@ -671,11 +607,6 @@ START_GAME_STATE
ADD_GAME_STATE (ILWRATH_WORSHIP, 2)
ADD_GAME_STATE (ILWRATH_FIGHT_THRADDASH, 1)
ADD_GAME_STATE (SAMATRA_GRPOFFS0, 8)
ADD_GAME_STATE (SAMATRA_GRPOFFS1, 8)
ADD_GAME_STATE (SAMATRA_GRPOFFS2, 8)
ADD_GAME_STATE (SAMATRA_GRPOFFS3, 8)
ADD_GAME_STATE (READY_TO_CONFUSE_URQUAN, 1)
ADD_GAME_STATE (URQUAN_HYPNO_VISITS, 1)
ADD_GAME_STATE (MENTIONED_PET_COMPULSION, 1)
@@ -877,6 +808,79 @@ START_GAME_STATE
ADD_GAME_STATE (ORZ_STACK0, 1)
ADD_GAME_STATE (ORZ_STACK1, 1)
/* These state bits are actually offsets into defgrp.dat. They really
* shouldn't be part of the serialized Game State array! --MCM */
ADD_GAME_STATE (SHOFIXTI_GRPOFFS0, 8)
ADD_GAME_STATE (SHOFIXTI_GRPOFFS1, 8)
ADD_GAME_STATE (SHOFIXTI_GRPOFFS2, 8)
ADD_GAME_STATE (SHOFIXTI_GRPOFFS3, 8)
ADD_GAME_STATE (ZOQFOT_GRPOFFS0, 8)
ADD_GAME_STATE (ZOQFOT_GRPOFFS1, 8)
ADD_GAME_STATE (ZOQFOT_GRPOFFS2, 8)
ADD_GAME_STATE (ZOQFOT_GRPOFFS3, 8)
ADD_GAME_STATE (MELNORME0_GRPOFFS0, 8)
ADD_GAME_STATE (MELNORME0_GRPOFFS1, 8)
ADD_GAME_STATE (MELNORME0_GRPOFFS2, 8)
ADD_GAME_STATE (MELNORME0_GRPOFFS3, 8)
ADD_GAME_STATE (MELNORME1_GRPOFFS0, 8)
ADD_GAME_STATE (MELNORME1_GRPOFFS1, 8)
ADD_GAME_STATE (MELNORME1_GRPOFFS2, 8)
ADD_GAME_STATE (MELNORME1_GRPOFFS3, 8)
ADD_GAME_STATE (MELNORME2_GRPOFFS0, 8)
ADD_GAME_STATE (MELNORME2_GRPOFFS1, 8)
ADD_GAME_STATE (MELNORME2_GRPOFFS2, 8)
ADD_GAME_STATE (MELNORME2_GRPOFFS3, 8)
ADD_GAME_STATE (MELNORME3_GRPOFFS0, 8)
ADD_GAME_STATE (MELNORME3_GRPOFFS1, 8)
ADD_GAME_STATE (MELNORME3_GRPOFFS2, 8)
ADD_GAME_STATE (MELNORME3_GRPOFFS3, 8)
ADD_GAME_STATE (MELNORME4_GRPOFFS0, 8)
ADD_GAME_STATE (MELNORME4_GRPOFFS1, 8)
ADD_GAME_STATE (MELNORME4_GRPOFFS2, 8)
ADD_GAME_STATE (MELNORME4_GRPOFFS3, 8)
ADD_GAME_STATE (MELNORME5_GRPOFFS0, 8)
ADD_GAME_STATE (MELNORME5_GRPOFFS1, 8)
ADD_GAME_STATE (MELNORME5_GRPOFFS2, 8)
ADD_GAME_STATE (MELNORME5_GRPOFFS3, 8)
ADD_GAME_STATE (MELNORME6_GRPOFFS0, 8)
ADD_GAME_STATE (MELNORME6_GRPOFFS1, 8)
ADD_GAME_STATE (MELNORME6_GRPOFFS2, 8)
ADD_GAME_STATE (MELNORME6_GRPOFFS3, 8)
ADD_GAME_STATE (MELNORME7_GRPOFFS0, 8)
ADD_GAME_STATE (MELNORME7_GRPOFFS1, 8)
ADD_GAME_STATE (MELNORME7_GRPOFFS2, 8)
ADD_GAME_STATE (MELNORME7_GRPOFFS3, 8)
ADD_GAME_STATE (MELNORME8_GRPOFFS0, 8)
ADD_GAME_STATE (MELNORME8_GRPOFFS1, 8)
ADD_GAME_STATE (MELNORME8_GRPOFFS2, 8)
ADD_GAME_STATE (MELNORME8_GRPOFFS3, 8)
ADD_GAME_STATE (URQUAN_PROBE_GRPOFFS0, 8)
ADD_GAME_STATE (URQUAN_PROBE_GRPOFFS1, 8)
ADD_GAME_STATE (URQUAN_PROBE_GRPOFFS2, 8)
ADD_GAME_STATE (URQUAN_PROBE_GRPOFFS3, 8)
ADD_GAME_STATE (COLONY_GRPOFFS0, 8)
ADD_GAME_STATE (COLONY_GRPOFFS1, 8)
ADD_GAME_STATE (COLONY_GRPOFFS2, 8)
ADD_GAME_STATE (COLONY_GRPOFFS3, 8)
ADD_GAME_STATE (SAMATRA_GRPOFFS0, 8)
ADD_GAME_STATE (SAMATRA_GRPOFFS1, 8)
ADD_GAME_STATE (SAMATRA_GRPOFFS2, 8)
ADD_GAME_STATE (SAMATRA_GRPOFFS3, 8)
END_GAME_STATE
// Values for GAME_STATE.glob_flags:
@@ -970,38 +974,61 @@ extern GLOBDATA GlobData;
#define MAX_ENCOUNTERS 16
#define MAX_BATTLE_GROUPS 32
/* DEFGRP enumeration. These identify scripted TrueSpace encounters
* more consistently than offsets into the DEFGRPINFO_FILE state
* file. */
enum {
DEFGRP_NONE,
DEFGRP_SHOFIXTI,
DEFGRP_ZOQFOT,
DEFGRP_MELNORME0,
DEFGRP_MELNORME1,
DEFGRP_MELNORME2,
DEFGRP_MELNORME3,
DEFGRP_MELNORME4,
DEFGRP_MELNORME5,
DEFGRP_MELNORME6,
DEFGRP_MELNORME7,
DEFGRP_MELNORME8,
DEFGRP_URQUAN_PROBE,
DEFGRP_COLONY,
DEFGRP_SAMATRA,
NUM_DEFGRPS
};
//#define STATE_DEBUG
extern BYTE getGameState (int startBit, int endBit);
extern void setGameState (int startBit, int endBit, BYTE val
extern BYTE getGameState (BYTE *state, int startBit, int endBit);
extern void setGameState (BYTE *state, int startBit, int endBit, BYTE val
#ifdef STATE_DEBUG
, const char *name
#endif
);
extern void copyGameState (BYTE *dest, DWORD target, BYTE *src, DWORD begin, DWORD end);
#define GET_GAME_STATE(SName) getGameState (GLOBAL(GameState), (SName), (END_##SName))
#ifdef STATE_DEBUG
# define SET_GAME_STATE(SName, val) \
setGameState (GLOBAL(GameState), (SName), (END_##SName), (val), #SName)
#else
# define SET_GAME_STATE(SName, val) \
setGameState (GLOBAL(GameState), (SName), (END_##SName), (val))
#endif
extern DWORD getGameState32 (BYTE *state, int startBit);
extern void setGameState32 (BYTE *state, int startBit, DWORD val
#ifdef STATE_DEBUG
, const char *name
#endif
);
#define GET_GAME_STATE(SName) getGameState ((SName), (END_##SName))
#ifdef STATE_DEBUG
# define SET_GAME_STATE(SName, val) \
setGameState ((SName), (END_##SName), (val), #SName)
#else
# define SET_GAME_STATE(SName, val) \
setGameState ((SName), (END_##SName), (val))
#endif
extern DWORD getGameState32 (int startBit);
extern void setGameState32 (int startBit, DWORD val
#ifdef STATE_DEBUG
, const char *name
#endif
);
#define GET_GAME_STATE_32(SName) getGameState32 ((SName))
#define GET_GAME_STATE_32(SName) getGameState32 (GLOBAL(GameState), (SName))
#ifdef STATE_DEBUG
# define SET_GAME_STATE_32(SName, val) \
setGameState32 ((SName), (val), #SName)
setGameState32 (GLOBAL(GameState), (SName), (val), #SName)
#else
# define SET_GAME_STATE_32(SName, val) \
setGameState32 ((SName), (val))
setGameState32 (GLOBAL(GameState), (SName), (val))
#endif
+7 -42
View File
@@ -23,7 +23,7 @@
#include "globdata.h"
#include "intel.h"
#include "state.h"
#include "grpinfo.h"
#include "grpintrn.h"
#include "libs/mathlib.h"
#include "libs/log.h"
@@ -35,42 +35,7 @@
static BYTE LastEncGroup;
// Last encountered group, saved into state files
//#define DEBUG_GROUPS
// A group header describes battle groups present in a star system. There is
// at most 1 group header per system.
// 'Random' group info file (RANDGRPINFO_FILE) always contains only one
// group header record, which describes the last-visited star system,
// (which may be the current system). Thus the randomly generated groups
// are valid for 7 days (set in PutGroupInfo) after the player leaves
// the system, or until the player enters another star system.
typedef struct
{
BYTE NumGroups;
BYTE day_index, month_index;
COUNT star_index, year_index;
// day_index, month_index, year_index specify when
// random groups expire (if you were to leave the system
// by going to HSpace and stay there till such time)
// star_index is the index of a star this group header
// applies to; ~0 means uninited
DWORD GroupOffset[NUM_SAVED_BATTLE_GROUPS + 1];
// Absolute offsets of group definitions in a state file
// Group 0 is a list of groups present in solarsys
// (RANDGRPINFO_FILE only)
// Groups 1..max are definitions of actual battle groups
// containing ship makeup and status
// Each group has the following format:
// 1 byte, RaceType (LastEncGroup in Group 0)
// 1 byte, NumShips (NumGroups in Group 0)
// Ships follow:
// 1 byte, RaceType
// 16 bytes, part of SHIP_FRAGMENT struct
} GROUP_HEADER;
static void
void
ReadGroupHeader (GAME_STATE_FILE *fp, GROUP_HEADER *pGH)
{
sread_8 (fp, &pGH->NumGroups);
@@ -82,7 +47,7 @@ ReadGroupHeader (GAME_STATE_FILE *fp, GROUP_HEADER *pGH)
sread_a32 (fp, pGH->GroupOffset, NUM_SAVED_BATTLE_GROUPS + 1);
}
static void
void
WriteGroupHeader (GAME_STATE_FILE *fp, const GROUP_HEADER *pGH)
{
swrite_8 (fp, pGH->NumGroups);
@@ -94,7 +59,7 @@ WriteGroupHeader (GAME_STATE_FILE *fp, const GROUP_HEADER *pGH)
swrite_a32 (fp, pGH->GroupOffset, NUM_SAVED_BATTLE_GROUPS + 1);
}
static void
void
ReadShipFragment (GAME_STATE_FILE *fp, SHIP_FRAGMENT *FragPtr)
{
BYTE tmpb;
@@ -116,7 +81,7 @@ ReadShipFragment (GAME_STATE_FILE *fp, SHIP_FRAGMENT *FragPtr)
sread_16 (fp, NULL); /* unused; was loc.y */
}
static void
void
WriteShipFragment (GAME_STATE_FILE *fp, const SHIP_FRAGMENT *FragPtr)
{
swrite_16 (fp, 0); /* unused: was which_side */
@@ -134,7 +99,7 @@ WriteShipFragment (GAME_STATE_FILE *fp, const SHIP_FRAGMENT *FragPtr)
swrite_16 (fp, 0); /* unused; was loc.y */
}
static void
void
ReadIpGroup (GAME_STATE_FILE *fp, IP_GROUP *GroupPtr)
{
BYTE tmpb;
@@ -157,7 +122,7 @@ ReadIpGroup (GAME_STATE_FILE *fp, IP_GROUP *GroupPtr)
sread_16s(fp, &GroupPtr->loc.y);
}
static void
void
WriteIpGroup (GAME_STATE_FILE *fp, const IP_GROUP *GroupPtr)
{
swrite_16 (fp, 0); /* unused; was which_side */
+56
View File
@@ -0,0 +1,56 @@
#ifndef _GRPINTRN_H
#define _GRPINTRN_H
// For IPGROUP
#include "grpinfo.h"
// For SHIP_FRAGMENT
#include "races.h"
// For GAME_STATE_FILE
#include "state.h"
//#define DEBUG_GROUPS
// A group header describes battle groups present in a star system. There is
// at most 1 group header per system.
// 'Random' group info file (RANDGRPINFO_FILE) always contains only one
// group header record, which describes the last-visited star system,
// (which may be the current system). Thus the randomly generated groups
// are valid for 7 days (set in PutGroupInfo) after the player leaves
// the system, or until the player enters another star system.
typedef struct
{
BYTE NumGroups;
BYTE day_index, month_index;
COUNT star_index, year_index;
// day_index, month_index, year_index specify when
// random groups expire (if you were to leave the system
// by going to HSpace and stay there till such time)
// star_index is the index of a star this group header
// applies to; ~0 means uninited
DWORD GroupOffset[NUM_SAVED_BATTLE_GROUPS + 1];
// Absolute offsets of group definitions in a state file
// Group 0 is a list of groups present in solarsys
// (RANDGRPINFO_FILE only)
// Groups 1..max are definitions of actual battle groups
// containing ship makeup and status
// Each group has the following format:
// 1 byte, RaceType (LastEncGroup in Group 0)
// 1 byte, NumShips (NumGroups in Group 0)
// Ships follow:
// 1 byte, RaceType
// 16 bytes, part of SHIP_FRAGMENT struct
// (part of IP_GROUP struct in Group 0)
} GROUP_HEADER;
void ReadGroupHeader (GAME_STATE_FILE *fp, GROUP_HEADER *pGH);
void WriteGroupHeader (GAME_STATE_FILE *fp, const GROUP_HEADER *pGH);
void ReadShipFragment (GAME_STATE_FILE *fp, SHIP_FRAGMENT *FragPtr);
void WriteShipFragment (GAME_STATE_FILE *fp, const SHIP_FRAGMENT *FragPtr);
void ReadIpGroup (GAME_STATE_FILE *fp, IP_GROUP *GroupPtr);
void WriteIpGroup (GAME_STATE_FILE *fp, const IP_GROUP *GroupPtr);
#endif
+475 -471
View File
File diff suppressed because it is too large Load Diff
-39
View File
@@ -1,39 +0,0 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _LOAD_H
#define _LOAD_H
#include "sis.h"
// for SUMMARY_DESC
#include "libs/compiler.h"
#include "globdata.h"
#if defined(__cplusplus)
extern "C" {
#endif
extern ACTIVITY NextActivity;
extern BOOLEAN LoadGame (COUNT which_game, SUMMARY_DESC *summary_desc);
#if defined(__cplusplus)
}
#endif
#endif /* _LOAD_H */
+821
View File
@@ -0,0 +1,821 @@
//Copyright Paul Reiche, Fred Ford. 1992-2002
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <assert.h>
#include "build.h"
#include "libs/declib.h"
#include "encount.h"
#include "starmap.h"
#include "libs/file.h"
#include "globdata.h"
#include "options.h"
#include "save.h"
#include "setup.h"
#include "state.h"
#include "grpinfo.h"
#include "libs/tasklib.h"
#include "libs/log.h"
#include "libs/misc.h"
//#define DEBUG_LOAD
// XXX: these should handle endian conversions later
static inline COUNT
cread_8 (DECODE_REF fh, BYTE *v)
{
BYTE t;
if (!v) /* read value ignored */
v = &t;
return cread (v, 1, 1, fh);
}
static inline COUNT
cread_16 (DECODE_REF fh, UWORD *v)
{
UWORD t;
if (!v) /* read value ignored */
v = &t;
return cread (v, 2, 1, fh);
}
static inline COUNT
cread_16s (DECODE_REF fh, SWORD *v)
{
UWORD t;
COUNT ret;
// value was converted to unsigned when saved
ret = cread_16 (fh, &t);
// unsigned to signed conversion
if (v)
*v = t;
return ret;
}
static inline COUNT
cread_32 (DECODE_REF fh, DWORD *v)
{
DWORD t;
if (!v) /* read value ignored */
v = &t;
return cread (v, 4, 1, fh);
}
static inline COUNT
cread_32s (DECODE_REF fh, SDWORD *v)
{
DWORD t;
COUNT ret;
// value was converted to unsigned when saved
ret = cread_32 (fh, &t);
// unsigned to signed conversion
if (v)
*v = t;
return ret;
}
static inline COUNT
cread_ptr (DECODE_REF fh)
{
DWORD t;
return cread_32 (fh, &t); /* ptrs are useless in saves */
}
static inline COUNT
cread_a8 (DECODE_REF fh, BYTE *ar, COUNT count)
{
assert (ar != NULL);
return cread (ar, 1, count, fh) == count;
}
static inline size_t
read_8 (void *fp, BYTE *v)
{
BYTE t;
if (!v) /* read value ignored */
v = &t;
return ReadResFile (v, 1, 1, fp);
}
static inline size_t
read_16 (void *fp, UWORD *v)
{
UWORD t;
if (!v) /* read value ignored */
v = &t;
return ReadResFile (v, 2, 1, fp);
}
static inline size_t
read_32 (void *fp, DWORD *v)
{
DWORD t;
if (!v) /* read value ignored */
v = &t;
return ReadResFile (v, 4, 1, fp);
}
static inline size_t
read_32s (void *fp, SDWORD *v)
{
DWORD t;
COUNT ret;
// value was converted to unsigned when saved
ret = read_32 (fp, &t);
// unsigned to signed conversion
if (v)
*v = t;
return ret;
}
static inline size_t
read_ptr (void *fp)
{
DWORD t;
return read_32 (fp, &t); /* ptrs are useless in saves */
}
static inline size_t
read_a8 (void *fp, BYTE *ar, COUNT count)
{
assert (ar != NULL);
return ReadResFile (ar, 1, count, fp) == count;
}
static inline size_t
read_str (void *fp, char *str, COUNT count)
{
// no type conversion needed for strings
return read_a8 (fp, (BYTE *)str, count);
}
static inline size_t
read_a16 (void *fp, UWORD *ar, COUNT count)
{
assert (ar != NULL);
for ( ; count > 0; --count, ++ar)
{
if (read_16 (fp, ar) != 1)
return 0;
}
return 1;
}
typedef struct struct_GAMESTATE_TRANSPOSE {
int start, end, target;
} GAMESTATE_TRANSPOSE;
#define LEGACY_GAMESTATE_SIZE 155
/* The *_GRPOFFS* states are no longer intermingled with the rest of
* the state. We need to shuffle all the rest of the state data
* down. */
static GAMESTATE_TRANSPOSE transpose[] = {
{ 0, 51, 0 },
{ 404, 450, 52 },
{ 483, 878, 99 },
{ 911, 930, 495 },
{ 963, 1237, 515 },
{ -1, -1, -1 } };
static DWORD old_defgrp_offsets[] = { 0, 52, 84, 116, 148, 180, 212, 244,
276, 308, 340, 372, 451, 879, 931 };
static DWORD new_defgrp_offsets[] = {
0,
SHOFIXTI_GRPOFFS0,
ZOQFOT_GRPOFFS0,
MELNORME0_GRPOFFS0,
MELNORME1_GRPOFFS0,
MELNORME2_GRPOFFS0,
MELNORME3_GRPOFFS0,
MELNORME4_GRPOFFS0,
MELNORME5_GRPOFFS0,
MELNORME6_GRPOFFS0,
MELNORME7_GRPOFFS0,
MELNORME8_GRPOFFS0,
URQUAN_PROBE_GRPOFFS0,
COLONY_GRPOFFS0,
SAMATRA_GRPOFFS0
};
static void
InterpretLegacyGameState (BYTE *result, BYTE *legacy)
{
int i;
DWORD grpoffs[NUM_DEFGRPS];
GAMESTATE_TRANSPOSE *t = &transpose[0];
grpoffs[0] = 0;
for (i = 1; i < NUM_DEFGRPS; ++i)
{
grpoffs[i] = getGameState32 (legacy, old_defgrp_offsets[i]);
}
while (t->start >= 0)
{
copyGameState (result, t->target, legacy, t->start, t->end);
++t;
}
for (i = 1; i < NUM_DEFGRPS; ++i)
{
setGameState32 (result, new_defgrp_offsets[i], grpoffs[i]);
}
}
static void
LoadEmptyQueue (DECODE_REF fh)
{
COUNT num_links;
cread_16 (fh, &num_links);
if (num_links)
{
log_add (log_Error, "LoadEmptyQueue(): BUG: the queue is not empty!");
#ifdef DEBUG
explode ();
#endif
}
}
static void
LoadShipQueue (DECODE_REF fh, QUEUE *pQueue)
{
COUNT num_links;
cread_16 (fh, &num_links);
while (num_links--)
{
HSHIPFRAG hStarShip;
SHIP_FRAGMENT *FragPtr;
COUNT Index;
BYTE tmpb;
cread_16 (fh, &Index);
hStarShip = CloneShipFragment (Index, pQueue, 0);
FragPtr = LockShipFrag (pQueue, hStarShip);
// Read SHIP_FRAGMENT elements
cread_16 (fh, NULL); /* unused: was which_side */
cread_8 (fh, &FragPtr->captains_name_index);
cread_8 (fh, NULL); /* padding */
cread_16 (fh, NULL); /* unused: was ship_flags */
cread_8 (fh, &FragPtr->race_id);
cread_8 (fh, &FragPtr->index);
// XXX: reading crew as BYTE to maintain savegame compatibility
cread_8 (fh, &tmpb);
FragPtr->crew_level = tmpb;
cread_8 (fh, &tmpb);
FragPtr->max_crew = tmpb;
cread_8 (fh, &FragPtr->energy_level);
cread_8 (fh, &FragPtr->max_energy);
cread_16 (fh, NULL); /* unused; was loc.x */
cread_16 (fh, NULL); /* unused; was loc.y */
UnlockShipFrag (pQueue, hStarShip);
}
}
static void
LoadRaceQueue (DECODE_REF fh, QUEUE *pQueue)
{
COUNT num_links;
cread_16 (fh, &num_links);
while (num_links--)
{
HFLEETINFO hStarShip;
FLEET_INFO *FleetPtr;
COUNT Index;
BYTE tmpb;
cread_16 (fh, &Index);
hStarShip = GetStarShipFromIndex (pQueue, Index);
FleetPtr = LockFleetInfo (pQueue, hStarShip);
// Read FLEET_INFO elements
cread_16 (fh, &FleetPtr->allied_state);
cread_8 (fh, &FleetPtr->days_left);
cread_8 (fh, &FleetPtr->growth_fract);
cread_8 (fh, &tmpb);
FleetPtr->crew_level = tmpb;
cread_8 (fh, &tmpb);
FleetPtr->max_crew = tmpb;
cread_8 (fh, &FleetPtr->growth);
cread_8 (fh, &FleetPtr->max_energy);
cread_16s(fh, &FleetPtr->loc.x);
cread_16s(fh, &FleetPtr->loc.y);
cread_16 (fh, &FleetPtr->actual_strength);
cread_16 (fh, &FleetPtr->known_strength);
cread_16s(fh, &FleetPtr->known_loc.x);
cread_16s(fh, &FleetPtr->known_loc.y);
cread_8 (fh, &FleetPtr->growth_err_term);
cread_8 (fh, &FleetPtr->func_index);
cread_16s(fh, &FleetPtr->dest_loc.x);
cread_16s(fh, &FleetPtr->dest_loc.y);
cread_16 (fh, NULL); /* alignment padding */
UnlockFleetInfo (pQueue, hStarShip);
}
}
static void
LoadGroupQueue (DECODE_REF fh, QUEUE *pQueue)
{
COUNT num_links;
cread_16 (fh, &num_links);
while (num_links--)
{
HIPGROUP hGroup;
IP_GROUP *GroupPtr;
BYTE tmpb;
cread_16 (fh, NULL); /* unused; was race_id */
hGroup = BuildGroup (pQueue, 0);
GroupPtr = LockIpGroup (pQueue, hGroup);
cread_16 (fh, NULL); /* unused; was which_side */
cread_8 (fh, NULL); /* unused; was captains_name_index */
cread_8 (fh, NULL); /* padding; for savegame compat */
cread_16 (fh, &GroupPtr->group_counter);
cread_8 (fh, &GroupPtr->race_id);
cread_8 (fh, &tmpb); /* was var2 */
GroupPtr->sys_loc = LONIBBLE (tmpb);
GroupPtr->task = HINIBBLE (tmpb);
cread_8 (fh, &GroupPtr->in_system); /* was crew_level */
cread_8 (fh, NULL); /* unused; was max_crew */
cread_8 (fh, &tmpb); /* was energy_level */
GroupPtr->dest_loc = LONIBBLE (tmpb);
GroupPtr->orbit_pos = HINIBBLE (tmpb);
cread_8 (fh, &GroupPtr->group_id); /* was max_energy */
cread_16s(fh, &GroupPtr->loc.x);
cread_16s(fh, &GroupPtr->loc.y);
UnlockIpGroup (pQueue, hGroup);
}
}
static void
LoadEncounter (ENCOUNTER *EncounterPtr, DECODE_REF fh)
{
COUNT i;
BYTE tmpb;
cread_ptr (fh); /* useless ptr; HENCOUNTER pred */
EncounterPtr->pred = 0;
cread_ptr (fh); /* useless ptr; HENCOUNTER succ */
EncounterPtr->succ = 0;
cread_ptr (fh); /* useless ptr; HELEMENT hElement */
EncounterPtr->hElement = 0;
cread_16s (fh, &EncounterPtr->transition_state);
cread_16s (fh, &EncounterPtr->origin.x);
cread_16s (fh, &EncounterPtr->origin.y);
cread_16 (fh, &EncounterPtr->radius);
// former STAR_DESC fields
cread_16s (fh, &EncounterPtr->loc_pt.x);
cread_16s (fh, &EncounterPtr->loc_pt.y);
cread_8 (fh, &EncounterPtr->race_id);
cread_8 (fh, &tmpb);
EncounterPtr->num_ships = tmpb & ENCOUNTER_SHIPS_MASK;
EncounterPtr->flags = tmpb & ENCOUNTER_FLAGS_MASK;
cread_16 (fh, NULL); /* alignment padding */
// Load each entry in the BRIEF_SHIP_INFO array
for (i = 0; i < MAX_HYPER_SHIPS; i++)
{
BRIEF_SHIP_INFO *ShipInfo = &EncounterPtr->ShipList[i];
cread_16 (fh, NULL); /* useless; was SHIP_INFO.ship_flags */
cread_8 (fh, &ShipInfo->race_id);
cread_8 (fh, NULL); /* useless; was SHIP_INFO.var2 */
// XXX: reading crew as BYTE to maintain savegame compatibility
cread_8 (fh, &tmpb);
ShipInfo->crew_level = tmpb;
cread_8 (fh, &tmpb);
ShipInfo->max_crew = tmpb;
cread_8 (fh, NULL); /* useless; was SHIP_INFO.energy_level */
cread_8 (fh, &ShipInfo->max_energy);
cread_16 (fh, NULL); /* useless; was SHIP_INFO.loc.x */
cread_16 (fh, NULL); /* useless; was SHIP_INFO.loc.y */
cread_32 (fh, NULL); /* useless val; STRING race_strings */
cread_ptr (fh); /* useless ptr; FRAME icons */
cread_ptr (fh); /* useless ptr; FRAME melee_icon */
}
// Load the stuff after the BRIEF_SHIP_INFO array
cread_32s (fh, &EncounterPtr->log_x);
cread_32s (fh, &EncounterPtr->log_y);
}
static void
LoadEvent (EVENT *EventPtr, DECODE_REF fh)
{
cread_ptr (fh); /* useless ptr; HEVENT pred */
EventPtr->pred = 0;
cread_ptr (fh); /* useless ptr; HEVENT succ */
EventPtr->succ = 0;
cread_8 (fh, &EventPtr->day_index);
cread_8 (fh, &EventPtr->month_index);
cread_16 (fh, &EventPtr->year_index);
cread_8 (fh, &EventPtr->func_index);
cread_8 (fh, NULL); /* padding */
cread_16 (fh, NULL); /* padding */
}
static void
DummyLoadQueue (QUEUE *QueuePtr, DECODE_REF fh)
{
/* QUEUE should never actually be loaded since it contains
* purely internal representation and the lists
* involved are actually loaded separately */
(void)QueuePtr; /* silence compiler */
/* QUEUE format with QUEUE_TABLE defined -- UQM default */
cread_ptr (fh); /* HLINK head */
cread_ptr (fh); /* HLINK tail */
cread_ptr (fh); /* BYTE* pq_tab */
cread_ptr (fh); /* HLINK free_list */
cread_16 (fh, NULL); /* MEM_HANDLE hq_tab */
cread_16 (fh, NULL); /* COUNT object_size */
cread_8 (fh, NULL); /* BYTE num_objects */
cread_8 (fh, NULL); /* padding */
cread_16 (fh, NULL); /* padding */
}
static void
LoadClockState (CLOCK_STATE *ClockPtr, DECODE_REF fh)
{
cread_8 (fh, &ClockPtr->day_index);
cread_8 (fh, &ClockPtr->month_index);
cread_16 (fh, &ClockPtr->year_index);
cread_16s (fh, &ClockPtr->tick_count);
cread_16s (fh, &ClockPtr->day_in_ticks);
cread_ptr (fh); /* not loading ptr; Semaphore clock_sem */
cread_ptr (fh); /* not loading ptr; Task clock_task */
cread_32 (fh, NULL); /* not loading; DWORD TimeCounter */
DummyLoadQueue (&ClockPtr->event_q, fh);
}
static void
LoadGameState (GAME_STATE *GSPtr, DECODE_REF fh)
{
BYTE dummy8, oldstate[LEGACY_GAMESTATE_SIZE];
cread_8 (fh, &dummy8); /* obsolete */
cread_8 (fh, &GSPtr->glob_flags);
cread_8 (fh, &GSPtr->CrewCost);
cread_8 (fh, &GSPtr->FuelCost);
cread_a8 (fh, GSPtr->ModuleCost, NUM_MODULES);
cread_a8 (fh, GSPtr->ElementWorth, NUM_ELEMENT_CATEGORIES);
cread_ptr (fh); /* not loading ptr; PRIMITIVE *DisplayArray */
cread_16 (fh, &GSPtr->CurrentActivity);
cread_16 (fh, NULL); /* CLOCK_STATE alignment padding */
LoadClockState (&GSPtr->GameClock, fh);
cread_16s (fh, &GSPtr->autopilot.x);
cread_16s (fh, &GSPtr->autopilot.y);
cread_16s (fh, &GSPtr->ip_location.x);
cread_16s (fh, &GSPtr->ip_location.y);
/* STAMP ShipStamp */
cread_16s (fh, &GSPtr->ShipStamp.origin.x);
cread_16s (fh, &GSPtr->ShipStamp.origin.y);
cread_16 (fh, &GSPtr->ShipFacing);
cread_8 (fh, &GSPtr->ip_planet);
cread_8 (fh, &GSPtr->in_orbit);
/* VELOCITY_DESC velocity */
cread_16 (fh, &GSPtr->velocity.TravelAngle);
cread_16s (fh, &GSPtr->velocity.vector.width);
cread_16s (fh, &GSPtr->velocity.vector.height);
cread_16s (fh, &GSPtr->velocity.fract.width);
cread_16s (fh, &GSPtr->velocity.fract.height);
cread_16s (fh, &GSPtr->velocity.error.width);
cread_16s (fh, &GSPtr->velocity.error.height);
cread_16s (fh, &GSPtr->velocity.incr.width);
cread_16s (fh, &GSPtr->velocity.incr.height);
cread_16 (fh, NULL); /* VELOCITY_DESC padding */
cread_32 (fh, &GSPtr->BattleGroupRef);
DummyLoadQueue (&GSPtr->avail_race_q, fh);
DummyLoadQueue (&GSPtr->npc_built_ship_q, fh);
// Not loading ip_group_q, was not there originally
DummyLoadQueue (&GSPtr->encounter_q, fh);
DummyLoadQueue (&GSPtr->built_ship_q, fh);
cread_a8 (fh, oldstate, LEGACY_GAMESTATE_SIZE);
InterpretLegacyGameState (GSPtr->GameState, oldstate);
cread_8 (fh, NULL); /* GAME_STATE alignment padding */
}
static BOOLEAN
LoadSisState (SIS_STATE *SSPtr, void *fp)
{
if (
read_32s (fp, &SSPtr->log_x) != 1 ||
read_32s (fp, &SSPtr->log_y) != 1 ||
read_32 (fp, &SSPtr->ResUnits) != 1 ||
read_32 (fp, &SSPtr->FuelOnBoard) != 1 ||
read_16 (fp, &SSPtr->CrewEnlisted) != 1 ||
read_16 (fp, &SSPtr->TotalElementMass) != 1 ||
read_16 (fp, &SSPtr->TotalBioMass) != 1 ||
read_a8 (fp, SSPtr->ModuleSlots, NUM_MODULE_SLOTS) != 1 ||
read_a8 (fp, SSPtr->DriveSlots, NUM_DRIVE_SLOTS) != 1 ||
read_a8 (fp, SSPtr->JetSlots, NUM_JET_SLOTS) != 1 ||
read_8 (fp, &SSPtr->NumLanders) != 1 ||
read_a16 (fp, SSPtr->ElementAmounts, NUM_ELEMENT_CATEGORIES) != 1 ||
read_str (fp, SSPtr->ShipName, SIS_NAME_SIZE) != 1 ||
read_str (fp, SSPtr->CommanderName, SIS_NAME_SIZE) != 1 ||
read_str (fp, SSPtr->PlanetName, SIS_NAME_SIZE) != 1 ||
read_16 (fp, NULL) != 1 /* padding */
)
return FALSE;
else
return TRUE;
}
static BOOLEAN
LoadSummary (SUMMARY_DESC *SummPtr, void *fp)
{
if (!LoadSisState (&SummPtr->SS, fp))
return FALSE;
if (
read_8 (fp, &SummPtr->Activity) != 1 ||
read_8 (fp, &SummPtr->Flags) != 1 ||
read_8 (fp, &SummPtr->day_index) != 1 ||
read_8 (fp, &SummPtr->month_index) != 1 ||
read_16 (fp, &SummPtr->year_index) != 1 ||
read_8 (fp, &SummPtr->MCreditLo) != 1 ||
read_8 (fp, &SummPtr->MCreditHi) != 1 ||
read_8 (fp, &SummPtr->NumShips) != 1 ||
read_8 (fp, &SummPtr->NumDevices) != 1 ||
read_a8 (fp, SummPtr->ShipList, MAX_BUILT_SHIPS) != 1 ||
read_a8 (fp, SummPtr->DeviceList, MAX_EXCLUSIVE_DEVICES) != 1 ||
read_16 (fp, NULL) != 1 /* padding */
)
return FALSE;
else
return TRUE;
}
static void
LoadStarDesc (STAR_DESC *SDPtr, DECODE_REF fh)
{
cread_16s(fh, &SDPtr->star_pt.x);
cread_16s(fh, &SDPtr->star_pt.y);
cread_8 (fh, &SDPtr->Type);
cread_8 (fh, &SDPtr->Index);
cread_8 (fh, &SDPtr->Prefix);
cread_8 (fh, &SDPtr->Postfix);
}
BOOLEAN
LoadLegacyGame (COUNT which_game, SUMMARY_DESC *SummPtr)
{
uio_Stream *in_fp;
char file[PATH_MAX];
char buf[256];
SUMMARY_DESC loc_sd;
GAME_STATE_FILE *fp;
DECODE_REF fh;
COUNT num_links;
STAR_DESC SD;
ACTIVITY Activity;
sprintf (file, "starcon2.%02u", which_game);
in_fp = res_OpenResFile (saveDir, file, "rb");
if (!in_fp)
return FALSE;
loc_sd.SaveName[0] = '\0';
if (!LoadSummary (&loc_sd, in_fp))
{
log_add (log_Error, "Warning: Savegame is corrupt");
res_CloseResFile (in_fp);
return FALSE;
}
if (!SummPtr)
{
SummPtr = &loc_sd;
}
else
{ // only need summary for displaying to user
memcpy (SummPtr, &loc_sd, sizeof (*SummPtr));
res_CloseResFile (in_fp);
return TRUE;
}
// Crude check for big-endian/little-endian incompatibilities.
// year_index is suitable as it's a multi-byte value within
// a specific recognisable range.
if (SummPtr->year_index < START_YEAR ||
SummPtr->year_index >= START_YEAR +
YEARS_TO_KOHRAH_VICTORY + 1 /* Utwig intervention */ +
1 /* time to destroy all races, plenty */ +
25 /* for cheaters */)
{
log_add (log_Error, "Warning: Savegame corrupt or from "
"an incompatible platform.");
res_CloseResFile (in_fp);
return FALSE;
}
GlobData.SIS_state = SummPtr->SS;
if ((fh = copen (in_fp, FILE_STREAM, STREAM_READ)) == 0)
{
res_CloseResFile (in_fp);
return FALSE;
}
ReinitQueue (&GLOBAL (GameClock.event_q));
ReinitQueue (&GLOBAL (encounter_q));
ReinitQueue (&GLOBAL (ip_group_q));
ReinitQueue (&GLOBAL (npc_built_ship_q));
ReinitQueue (&GLOBAL (built_ship_q));
memset (&GLOBAL (GameState[0]), 0, sizeof (GLOBAL (GameState)));
Activity = GLOBAL (CurrentActivity);
LoadGameState (&GlobData.Game_state, fh);
NextActivity = GLOBAL (CurrentActivity);
GLOBAL (CurrentActivity) = Activity;
LoadRaceQueue (fh, &GLOBAL (avail_race_q));
// START_INTERPLANETARY is only set when saving from Homeworld
// encounter screen. When the game is loaded, the
// GenerateOrbitalFunction for the current star system will
// create the encounter anew and populate the npc queue.
if (!(NextActivity & START_INTERPLANETARY))
{
if (NextActivity & START_ENCOUNTER)
LoadShipQueue (fh, &GLOBAL (npc_built_ship_q));
else if (LOBYTE (NextActivity) == IN_INTERPLANETARY)
// XXX: Technically, this queue does not need to be
// saved/loaded at all. IP groups will be reloaded
// from group state files. But the original code did,
// and so will we until we can prove we do not need to.
LoadGroupQueue (fh, &GLOBAL (ip_group_q));
else
// XXX: The empty queue read is only needed to maintain
// the savegame compatibility
LoadEmptyQueue (fh);
}
LoadShipQueue (fh, &GLOBAL (built_ship_q));
// Load the game events (compressed)
cread_16 (fh, &num_links);
{
#ifdef DEBUG_LOAD
log_add (log_Debug, "EVENTS:");
#endif /* DEBUG_LOAD */
while (num_links--)
{
HEVENT hEvent;
EVENT *EventPtr;
hEvent = AllocEvent ();
LockEvent (hEvent, &EventPtr);
LoadEvent (EventPtr, fh);
#ifdef DEBUG_LOAD
log_add (log_Debug, "\t%u/%u/%u -- %u",
EventPtr->month_index,
EventPtr->day_index,
EventPtr->year_index,
EventPtr->func_index);
#endif /* DEBUG_LOAD */
UnlockEvent (hEvent);
PutEvent (hEvent);
}
}
// Load the encounters (black globes in HS/QS (compressed))
cread_16 (fh, &num_links);
{
while (num_links--)
{
HENCOUNTER hEncounter;
ENCOUNTER *EncounterPtr;
hEncounter = AllocEncounter ();
LockEncounter (hEncounter, &EncounterPtr);
LoadEncounter (EncounterPtr, fh);
UnlockEncounter (hEncounter);
PutEncounter (hEncounter);
}
}
// Copy the star info file from the compressed stream
fp = OpenStateFile (STARINFO_FILE, "wb");
if (fp)
{
DWORD flen;
cread_32 (fh, &flen);
while (flen)
{
COUNT num_bytes;
num_bytes = flen >= sizeof (buf) ? sizeof (buf) : (COUNT)flen;
cread (buf, num_bytes, 1, fh);
WriteStateFile (buf, num_bytes, 1, fp);
flen -= num_bytes;
}
CloseStateFile (fp);
}
// Copy the defined groupinfo file from the compressed stream
fp = OpenStateFile (DEFGRPINFO_FILE, "wb");
if (fp)
{
DWORD flen;
cread_32 (fh, &flen);
while (flen)
{
COUNT num_bytes;
num_bytes = flen >= sizeof (buf) ? sizeof (buf) : (COUNT)flen;
cread (buf, num_bytes, 1, fh);
WriteStateFile (buf, num_bytes, 1, fp);
flen -= num_bytes;
}
CloseStateFile (fp);
}
// Copy the random groupinfo file from the compressed stream
fp = OpenStateFile (RANDGRPINFO_FILE, "wb");
if (fp)
{
DWORD flen;
cread_32 (fh, &flen);
while (flen)
{
COUNT num_bytes;
num_bytes = flen >= sizeof (buf) ? sizeof (buf) : (COUNT)flen;
cread (buf, num_bytes, 1, fh);
WriteStateFile (buf, num_bytes, 1, fp);
flen -= num_bytes;
}
CloseStateFile (fp);
}
LoadStarDesc (&SD, fh);
cclose (fh);
res_CloseResFile (in_fp);
EncounterGroup = 0;
EncounterRace = -1;
ReinitQueue (&race_q[0]);
ReinitQueue (&race_q[1]);
CurStarDescPtr = FindStar (NULL, &SD.star_pt, 0, 0);
if (!(NextActivity & START_ENCOUNTER)
&& LOBYTE (NextActivity) == IN_INTERPLANETARY)
NextActivity |= START_INTERPLANETARY;
return TRUE;
}
+1 -1
View File
@@ -23,9 +23,9 @@
#include "../encount.h"
#include "../gamestr.h"
#include "../controls.h"
#include "../save.h"
#include "../settings.h"
#include "../shipcont.h"
#include "../load.h"
#include "../setup.h"
#include "../state.h"
#include "../sis.h"
+1 -1
View File
@@ -30,13 +30,13 @@
#include "../nameref.h"
#include "../resinst.h"
#include "../settings.h"
#include "../load.h"
#include "../util.h"
#include "../process.h"
#include "../setup.h"
#include "../sounds.h"
#include "../state.h"
#include "../sis.h"
#include "../save.h"
#include "options.h"
#include "libs/graphics/gfx_common.h"
#include "libs/graphics/drawable.h"
+1 -1
View File
@@ -37,11 +37,11 @@
#include "../ipdisp.h"
#include "../grpinfo.h"
#include "../process.h"
#include "../load.h"
#include "../setup.h"
#include "../sounds.h"
#include "../state.h"
#include "../uqmdebug.h"
#include "../save.h"
#include "options.h"
#include "libs/graphics/gfx_common.h"
#include "libs/mathlib.h"
+1 -1
View File
@@ -30,8 +30,8 @@
#include "supermelee/melee.h"
#include "resinst.h"
#include "nameref.h"
#include "save.h"
#include "settings.h"
#include "load.h"
#include "setup.h"
#include "sounds.h"
#include "setupmenu.h"
+466 -489
View File
File diff suppressed because it is too large Load Diff
+46 -3
View File
@@ -17,16 +17,59 @@
#ifndef _SAVE_H
#define _SAVE_H
#include "sis.h"
// for SUMMARY_DESC
#include "sis.h" // SUMMARY_DESC includes SIS_STATE in it
#include "globdata.h"
#include "libs/compiler.h"
#if defined(__cplusplus)
extern "C" {
#endif
// XXX: Theoretically, a player can have 17 devices on board without
// cheating. We only provide
// room for 16 below, which is not really a problem since this
// is only used for displaying savegame summaries. There is also
// room for only 16 devices on screen.
#define MAX_EXCLUSIVE_DEVICES 16
#define SAVE_NAME_SIZE 64
// The savefile tag numbers.
#define SAVEFILE_TAG 0x01534d55 // "UMS\x01": UQM Save version 1
#define SUMMARY_TAG 0x6d6d7553 // "Summ": Summary. Must be first!
#define GLOBAL_STATE_TAG 0x74536c47 // "GlSt": Global State. Must be 2nd!
#define GAME_STATE_TAG 0x74536d47 // "GmSt": Game State Bits. Must be 3rd!
#define EVENTS_TAG 0x73747645 // "Evts": Events
#define ENCOUNTERS_TAG 0x74636e45 // "Enct": Encounters
#define RACE_Q_TAG 0x51636152 // "RacQ": avail_race_q
#define IP_GRP_Q_TAG 0x51704749 // "IGpQ": ip_group_q
#define NPC_SHIP_Q_TAG 0x5163704e // "NpcQ": npc_built_ship_q
#define SHIP_Q_TAG 0x51706853 // "ShpQ": built_ship_q
#define STAR_TAG 0x72617453 // "Star": STAR_DESC
#define SCAN_TAG 0x6e616353 // "Scan": Scan Masks (stuff picked up)
#define BATTLE_GROUP_TAG 0x70477442 // "BtGp": Battle Group definition
#define GROUP_LIST_TAG 0x73707247 // "Grps": Group List
typedef struct
{
SIS_STATE SS;
BYTE Activity;
BYTE Flags;
BYTE day_index, month_index;
COUNT year_index;
BYTE MCreditLo, MCreditHi;
BYTE NumShips, NumDevices;
BYTE ShipList[MAX_BUILT_SHIPS];
BYTE DeviceList[MAX_EXCLUSIVE_DEVICES];
UNICODE SaveName[SAVE_NAME_SIZE];
} SUMMARY_DESC;
extern ACTIVITY NextActivity;
extern BOOLEAN LoadGame (COUNT which_game, SUMMARY_DESC *summary_desc);
extern BOOLEAN LoadLegacyGame (COUNT which_game, SUMMARY_DESC *summary_desc);
extern void SaveProblem (void);
extern BOOLEAN SaveGame (COUNT which_game, SUMMARY_DESC *summary_desc);
extern BOOLEAN SaveGame (COUNT which_game, SUMMARY_DESC *summary_desc, const char *name);
#if defined(__cplusplus)
}
+1 -21
View File
@@ -76,7 +76,7 @@ enum
CANNON_WEAPON,
TRACKING_SYSTEM,
ANTIMISSILE_DEFENSE,
NUM_PURCHASE_MODULES,
BOMB_MODULE_0 = NUM_PURCHASE_MODULES,
@@ -154,26 +154,6 @@ typedef struct
UNICODE PlanetName[SIS_NAME_SIZE];
} SIS_STATE;
// XXX: Theoretically, a player can have 17 devices on board without
// cheating. We only provide
// room for 16 below, which is not really a problem since this
// is only used for displaying savegame summaries. There is also
// room for only 16 devices on screen.
#define MAX_EXCLUSIVE_DEVICES 16
typedef struct
{
SIS_STATE SS;
BYTE Activity;
BYTE Flags;
BYTE day_index, month_index;
COUNT year_index;
BYTE MCreditLo, MCreditHi;
BYTE NumShips, NumDevices;
BYTE ShipList[MAX_BUILT_SHIPS];
BYTE DeviceList[MAX_EXCLUSIVE_DEVICES];
} SUMMARY_DESC;
#define OVERRIDE_LANDER_FLAGS (1 << 7)
#define AFTER_BOMB_INSTALLED (1 << 7)
+1 -1
View File
@@ -22,7 +22,7 @@
#include "starmap.h"
#include "comm.h"
#include "gamestr.h"
#include "load.h"
#include "save.h"
#include "starbase.h"
#include "sis.h"
#include "resinst.h"
+1 -1
View File
@@ -24,10 +24,10 @@
#include "gameev.h"
#include "types.h"
#include "globdata.h"
#include "load.h"
#include "resinst.h"
#include "restart.h"
#include "starbase.h"
#include "save.h"
#include "setup.h"
#include "master.h"
#include "controls.h"