From 28fb037258129fad1cd202d00488c284ca6d1cb6 Mon Sep 17 00:00:00 2001 From: Michael Martin Date: Wed, 25 Sep 2013 01:15:30 -0700 Subject: [PATCH 01/22] First draft of namable savegames. This code is loosely adapted from JMS's code in UQM-HD, but very little remains beyond the text input loop internals. Fully backwards compatible, but this format shouldn't survive to the final merge. --- sc2/src/uqm/gameopt.c | 242 +++++++++++++++++++++++++++++++++++++----- sc2/src/uqm/load.c | 45 +++++--- sc2/src/uqm/save.c | 29 ++--- sc2/src/uqm/save.h | 2 +- sc2/src/uqm/sis.h | 5 +- 5 files changed, 268 insertions(+), 55 deletions(-) diff --git a/sc2/src/uqm/gameopt.c b/sc2/src/uqm/gameopt.c index 726f0b2ce..80070e7bf 100644 --- a/sc2/src/uqm/gameopt.c +++ b/sc2/src/uqm/gameopt.c @@ -34,6 +34,7 @@ #include "sounds.h" #include "util.h" #include "libs/graphics/gfx_common.h" +#include "libs/log.h" #include @@ -253,7 +254,7 @@ DrawNameString (bool nameCaptain, UNICODE *Str, COUNT CursorPos, // disallow the change return (FALSE); } - + PreUpdateFlashRect (); SetContextForeGroundColor (BackGround); @@ -264,7 +265,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 +285,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 +293,7 @@ DrawNameString (bool nameCaptain, UNICODE *Str, COUNT CursorPos, SetContextForeGroundColor (ForeGround); font_DrawText (&lf); - + PostUpdateFlashRect (); } @@ -347,15 +348,183 @@ 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; + + 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 = Str; + lf.CharCount = (COUNT)~0; + + if (!(state & DDSHS_EDIT)) + { + //RECT r; + 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 (&t); + } + else + { // editing state + COUNT i; + RECT text_r; + BYTE char_deltas[SAVE_NAME_SIZE]; + 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; + + for (i = CursorPos; i > 0; --i) + 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) + { // cursor at end-line -- use insertion point + text_r.extent.width = 1; + } + else if (CursorPos + 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 = 0; + 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 +780,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 +849,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 +866,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 +939,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 +994,10 @@ 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:" + if (desc->SaveName[0]) + snprintf (buf, sizeof buf, "%s: %s", buf2, desc->SaveName); + else + snprintf (buf, sizeof buf, "%s", buf2); } font_DrawText (&t); } @@ -928,7 +1098,7 @@ DoPickGame (MENU_STATE *pMS) SetContext (SpaceContext); RedrawPickDisplay (pickState, pMS->CurState); } - + SleepThreadUntil (TimeIn + ONE_SECOND / 30); } @@ -936,22 +1106,35 @@ 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[SAVE_NAME_SIZE]; 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); + { + nameBuf[0] = 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 +1188,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 +1211,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 +1224,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 +1243,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 +1330,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 +1339,3 @@ GameOptions (void) return !(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)); } - diff --git a/sc2/src/uqm/load.c b/sc2/src/uqm/load.c index 98924f173..c7a58c6d0 100644 --- a/sc2/src/uqm/load.c +++ b/sc2/src/uqm/load.c @@ -368,7 +368,7 @@ LoadEncounter (ENCOUNTER *EncounterPtr, DECODE_REF fh) 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); @@ -405,7 +405,7 @@ DummyLoadQueue (QUEUE *QueuePtr, DECODE_REF fh) 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 */ } @@ -438,7 +438,7 @@ LoadGameState (GAME_STATE *GSPtr, DECODE_REF fh) 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); @@ -466,7 +466,7 @@ LoadGameState (GAME_STATE *GSPtr, DECODE_REF fh) 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 @@ -480,10 +480,10 @@ LoadGameState (GAME_STATE *GSPtr, DECODE_REF fh) } static BOOLEAN -LoadSisState (SIS_STATE *SSPtr, void *fp) +LoadSisState (SIS_STATE *SSPtr, void *fp, SDWORD first) { + SSPtr->log_x = first; 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 || @@ -510,7 +510,26 @@ LoadSisState (SIS_STATE *SSPtr, void *fp) static BOOLEAN LoadSummary (SUMMARY_DESC *SummPtr, void *fp) { - if (!LoadSisState (&SummPtr->SS, fp)) + SDWORD magic; + BOOLEAN legacy; + SummPtr->SaveName[0] = 0; + if (!read_32s (fp, &magic)) + return FALSE; + if (magic == SAVE_MAGIC) + { + legacy = FALSE; + // Read in the real first value for LoadSisState + if (read_32 (fp, &magic) != 1) + return FALSE; + } + else + { + // Otherwise, we're legacy and the "magic" number was + // really LoadSisState's first value + legacy = TRUE; + } + + if (!LoadSisState (&SummPtr->SS, fp, magic)) return FALSE; if ( @@ -524,11 +543,15 @@ LoadSummary (SUMMARY_DESC *SummPtr, void *fp) 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 */ + read_a8 (fp, SummPtr->DeviceList, MAX_EXCLUSIVE_DEVICES) != 1 ) return FALSE; + if (!legacy && (read_a8 (fp, SummPtr->SaveName, SAVE_NAME_SIZE) != 1)) + return FALSE; + // Don't trust the savefile to properly null-terminate! + SummPtr->SaveName[SAVE_NAME_SIZE-1] = 0; + if (read_16 (fp, NULL) != 1) /* padding */ + return FALSE; else return TRUE; } @@ -760,5 +783,3 @@ LoadGame (COUNT which_game, SUMMARY_DESC *SummPtr) return TRUE; } - - diff --git a/sc2/src/uqm/save.c b/sc2/src/uqm/save.c index c49fe505e..95be06eda 100644 --- a/sc2/src/uqm/save.c +++ b/sc2/src/uqm/save.c @@ -305,7 +305,7 @@ SaveEncounter (const ENCOUNTER *EncounterPtr, DECODE_REF fh) cwrite_ptr (fh); /* useless ptr; FRAME icons */ cwrite_ptr (fh); /* useless ptr; FRAME melee_icon */ } - + // Save the stuff after the BRIEF_SHIP_INFO array cwrite_32 (fh, EncounterPtr->log_x); cwrite_32 (fh, EncounterPtr->log_y); @@ -340,7 +340,7 @@ DummySaveQueue (const QUEUE *QueuePtr, DECODE_REF fh) cwrite_16 (fh, 0); /* MEM_HANDLE hq_tab */ cwrite_16 (fh, 0); /* COUNT object_size */ cwrite_8 (fh, 0); /* BYTE num_objects */ - + cwrite_8 (fh, 0); /* padding */ cwrite_16 (fh, 0); /* padding */ } @@ -371,7 +371,7 @@ SaveGameState (const GAME_STATE *GSPtr, DECODE_REF fh) cwrite_a8 (fh, GSPtr->ElementWorth, NUM_ELEMENT_CATEGORIES); cwrite_ptr (fh); /* useless ptr; PRIMITIVE *DisplayArray */ cwrite_16 (fh, GSPtr->CurrentActivity); - + cwrite_16 (fh, 0); /* CLOCK_STATE alignment padding */ SaveClockState (&GSPtr->GameClock, fh); @@ -399,7 +399,7 @@ SaveGameState (const GAME_STATE *GSPtr, DECODE_REF fh) cwrite_16 (fh, 0); /* VELOCITY_DESC padding */ cwrite_32 (fh, GSPtr->BattleGroupRef); - + DummySaveQueue (&GSPtr->avail_race_q, fh); DummySaveQueue (&GSPtr->npc_built_ship_q, fh); // Not saving ip_group_q, was not there originally @@ -443,6 +443,8 @@ SaveSisState (const SIS_STATE *SSPtr, void *fp) static BOOLEAN SaveSummary (const SUMMARY_DESC *SummPtr, void *fp) { + if (write_32 (fp, SAVE_MAGIC) != 1) + return FALSE; if (!SaveSisState (&SummPtr->SS, fp)) return FALSE; @@ -458,6 +460,7 @@ SaveSummary (const SUMMARY_DESC *SummPtr, void *fp) write_8 (fp, SummPtr->NumDevices) != 1 || write_a8 (fp, SummPtr->ShipList, MAX_BUILT_SHIPS) != 1 || write_a8 (fp, SummPtr->DeviceList, MAX_EXCLUSIVE_DEVICES) != 1 || + write_a8 (fp, SummPtr->SaveName, SAVE_NAME_SIZE) != 1 || write_16 (fp, 0) != 1 /* padding */ ) @@ -478,8 +481,9 @@ SaveStarDesc (const STAR_DESC *SDPtr, DECODE_REF fh) } static void -PrepareSummary (SUMMARY_DESC *SummPtr) +PrepareSummary (SUMMARY_DESC *SummPtr, const char *name) { + int i; SummPtr->SS = GlobData.SIS_state; SummPtr->Activity = LOBYTE (GLOBAL (CurrentActivity)); @@ -535,6 +539,8 @@ PrepareSummary (SUMMARY_DESC *SummPtr) SummPtr->day_index = GLOBAL (GameClock.day_index); SummPtr->month_index = GLOBAL (GameClock.month_index); SummPtr->year_index = GLOBAL (GameClock.year_index); + SummPtr->SaveName[SAVE_NAME_SIZE-1] = 0; + strncpy (SummPtr->SaveName, name, SAVE_NAME_SIZE-1); } static void @@ -549,7 +555,7 @@ SaveProblemMessage (STAMP *MsgStamp) // TODO: This should probably just use DoPopupWindow() ppStr[0] = GAME_STRING (SAVEGAME_STRING_BASE + 2); - + SetContextFont (StarConFont); t.baseline.x = t.baseline.y = 0; @@ -579,7 +585,7 @@ SaveProblemMessage (STAMP *MsgStamp) r.extent.height += 8; *MsgStamp = SaveContextFrame (&r); - + BatchGraphics (); DrawStarConBox (&r, 2, BUILD_COLOR (MAKE_RGB15 (0x10, 0x10, 0x10), 0x19), @@ -605,7 +611,7 @@ SaveProblem (void) { STAMP s; CONTEXT OldContext; - + OldContext = SetContext (SpaceContext); SaveProblemMessage (&s); FlushGraphics (); @@ -635,7 +641,7 @@ SaveFlagshipState (void) // This function first writes to a memory file, and then writes the whole // lot to the actual save file at once. BOOLEAN -SaveGame (COUNT which_game, SUMMARY_DESC *SummPtr) +SaveGame (COUNT which_game, SUMMARY_DESC *SummPtr, const char *name) { BOOLEAN success, made_room; void *out_fp, *h; @@ -833,7 +839,7 @@ RetrySave: flen + sizeof (*SummPtr)); if (flen && (out_fp = res_OpenResFile (saveDir, file, "wb"))) { - PrepareSummary (SummPtr); + PrepareSummary (SummPtr, name); success = SaveSummary (SummPtr, out_fp); // Then write the rest of the data. @@ -846,7 +852,7 @@ RetrySave: } else success = FALSE; - + if (!success) DeleteResFile (saveDir, file); } @@ -858,4 +864,3 @@ RetrySave: return (success); } - diff --git a/sc2/src/uqm/save.h b/sc2/src/uqm/save.h index bb7084b18..7a3c3c2ac 100644 --- a/sc2/src/uqm/save.h +++ b/sc2/src/uqm/save.h @@ -26,7 +26,7 @@ extern "C" { #endif 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) } diff --git a/sc2/src/uqm/sis.h b/sc2/src/uqm/sis.h index 06dea4265..723081887 100644 --- a/sc2/src/uqm/sis.h +++ b/sc2/src/uqm/sis.h @@ -76,7 +76,7 @@ enum CANNON_WEAPON, TRACKING_SYSTEM, ANTIMISSILE_DEFENSE, - + NUM_PURCHASE_MODULES, BOMB_MODULE_0 = NUM_PURCHASE_MODULES, @@ -160,6 +160,8 @@ typedef struct // is only used for displaying savegame summaries. There is also // room for only 16 devices on screen. #define MAX_EXCLUSIVE_DEVICES 16 +#define SAVE_MAGIC 0x01534d55 +#define SAVE_NAME_SIZE 24 typedef struct { @@ -172,6 +174,7 @@ typedef struct BYTE NumShips, NumDevices; BYTE ShipList[MAX_BUILT_SHIPS]; BYTE DeviceList[MAX_EXCLUSIVE_DEVICES]; + UNICODE SaveName[SAVE_NAME_SIZE]; } SUMMARY_DESC; #define OVERRIDE_LANDER_FLAGS (1 << 7) From 2fc4f01ec4f1550ac974ee7472bc6cfc1d46aa29 Mon Sep 17 00:00:00 2001 From: Michael Martin Date: Wed, 25 Sep 2013 17:41:43 -0700 Subject: [PATCH 02/22] Normalize legacy/modern save display. Also clean up some detritus from the initial implementation. --- sc2/content/base/gamestrings.txt | 2 +- sc2/src/uqm/gameopt.c | 6 +----- sc2/src/uqm/save.c | 1 - 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/sc2/content/base/gamestrings.txt b/sc2/content/base/gamestrings.txt index adc10e55a..fe0a504e3 100644 --- a/sc2/content/base/gamestrings.txt +++ b/sc2/content/base/gamestrings.txt @@ -1831,7 +1831,7 @@ Empty Slot #(Saved Game - Date:) -Saved Game - Date: +Saved Game #(Sound) -- OPTION_STRING_BASE diff --git a/sc2/src/uqm/gameopt.c b/sc2/src/uqm/gameopt.c index 80070e7bf..296d9d3b9 100644 --- a/sc2/src/uqm/gameopt.c +++ b/sc2/src/uqm/gameopt.c @@ -34,7 +34,6 @@ #include "sounds.h" #include "util.h" #include "libs/graphics/gfx_common.h" -#include "libs/log.h" #include @@ -994,10 +993,7 @@ DrawGameSelection (PICK_GAME_STATE *pickState, COUNT selSlot) { DateToString (buf2, sizeof buf2, desc->month_index, desc->day_index, desc->year_index); - if (desc->SaveName[0]) - snprintf (buf, sizeof buf, "%s: %s", buf2, desc->SaveName); - else - snprintf (buf, sizeof buf, "%s", buf2); + snprintf (buf, sizeof buf, "%s: %s", buf2, desc->SaveName[0] ? desc->SaveName : GAME_STRING (SAVEGAME_STRING_BASE + 4)); } font_DrawText (&t); } diff --git a/sc2/src/uqm/save.c b/sc2/src/uqm/save.c index 95be06eda..9c3e389d8 100644 --- a/sc2/src/uqm/save.c +++ b/sc2/src/uqm/save.c @@ -483,7 +483,6 @@ SaveStarDesc (const STAR_DESC *SDPtr, DECODE_REF fh) static void PrepareSummary (SUMMARY_DESC *SummPtr, const char *name) { - int i; SummPtr->SS = GlobData.SIS_state; SummPtr->Activity = LOBYTE (GLOBAL (CurrentActivity)); From 27087d0ea83134865f09733a626973191f5c1750 Mon Sep 17 00:00:00 2001 From: Michael Martin Date: Wed, 25 Sep 2013 20:32:15 -0700 Subject: [PATCH 03/22] Refactor savegame information Coalesce it out of three header files into one (save.h). This also includes references to NextActivity, which may be the wrong place to do it. Time will tell. --- sc2/src/uqm/comm.c | 2 +- sc2/src/uqm/gameopt.c | 1 - sc2/src/uqm/load.c | 4 +--- sc2/src/uqm/load.h | 39 ---------------------------------- sc2/src/uqm/planets/devices.c | 2 +- sc2/src/uqm/planets/scan.c | 2 +- sc2/src/uqm/planets/solarsys.c | 2 +- sc2/src/uqm/restart.c | 2 +- sc2/src/uqm/save.h | 31 +++++++++++++++++++++++++-- sc2/src/uqm/sis.h | 23 -------------------- sc2/src/uqm/starbase.c | 2 +- sc2/src/uqm/starcon.c | 2 +- 12 files changed, 37 insertions(+), 75 deletions(-) delete mode 100644 sc2/src/uqm/load.h diff --git a/sc2/src/uqm/comm.c b/sc2/src/uqm/comm.c index 55c07df11..6336de8a9 100644 --- a/sc2/src/uqm/comm.c +++ b/sc2/src/uqm/comm.c @@ -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" diff --git a/sc2/src/uqm/gameopt.c b/sc2/src/uqm/gameopt.c index 296d9d3b9..18bc0ab8b 100644 --- a/sc2/src/uqm/gameopt.c +++ b/sc2/src/uqm/gameopt.c @@ -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" diff --git a/sc2/src/uqm/load.c b/sc2/src/uqm/load.c index c7a58c6d0..e115273b6 100644 --- a/sc2/src/uqm/load.c +++ b/sc2/src/uqm/load.c @@ -18,16 +18,14 @@ #include -#include "load.h" - #include "build.h" #include "libs/declib.h" #include "encount.h" #include "starmap.h" #include "libs/file.h" #include "globdata.h" -#include "load.h" #include "options.h" +#include "save.h" #include "setup.h" #include "state.h" #include "grpinfo.h" diff --git a/sc2/src/uqm/load.h b/sc2/src/uqm/load.h deleted file mode 100644 index 175e38c80..000000000 --- a/sc2/src/uqm/load.h +++ /dev/null @@ -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 */ - diff --git a/sc2/src/uqm/planets/devices.c b/sc2/src/uqm/planets/devices.c index 99b0ac379..e781d5b1c 100644 --- a/sc2/src/uqm/planets/devices.c +++ b/sc2/src/uqm/planets/devices.c @@ -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" diff --git a/sc2/src/uqm/planets/scan.c b/sc2/src/uqm/planets/scan.c index ed063337b..3d5d9fd7b 100644 --- a/sc2/src/uqm/planets/scan.c +++ b/sc2/src/uqm/planets/scan.c @@ -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" diff --git a/sc2/src/uqm/planets/solarsys.c b/sc2/src/uqm/planets/solarsys.c index 677b9dfdb..11bd4c0bf 100644 --- a/sc2/src/uqm/planets/solarsys.c +++ b/sc2/src/uqm/planets/solarsys.c @@ -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" diff --git a/sc2/src/uqm/restart.c b/sc2/src/uqm/restart.c index b877a2edb..f52e753b4 100644 --- a/sc2/src/uqm/restart.c +++ b/sc2/src/uqm/restart.c @@ -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" diff --git a/sc2/src/uqm/save.h b/sc2/src/uqm/save.h index 7a3c3c2ac..6f37f01ff 100644 --- a/sc2/src/uqm/save.h +++ b/sc2/src/uqm/save.h @@ -17,14 +17,41 @@ #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_MAGIC 0x01534d55 +#define SAVE_NAME_SIZE 24 + +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 void SaveProblem (void); extern BOOLEAN SaveGame (COUNT which_game, SUMMARY_DESC *summary_desc, const char *name); diff --git a/sc2/src/uqm/sis.h b/sc2/src/uqm/sis.h index 723081887..ee07a81db 100644 --- a/sc2/src/uqm/sis.h +++ b/sc2/src/uqm/sis.h @@ -154,29 +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 -#define SAVE_MAGIC 0x01534d55 -#define SAVE_NAME_SIZE 24 - -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; - #define OVERRIDE_LANDER_FLAGS (1 << 7) #define AFTER_BOMB_INSTALLED (1 << 7) diff --git a/sc2/src/uqm/starbase.c b/sc2/src/uqm/starbase.c index be12f06c7..f1192085c 100644 --- a/sc2/src/uqm/starbase.c +++ b/sc2/src/uqm/starbase.c @@ -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" diff --git a/sc2/src/uqm/starcon.c b/sc2/src/uqm/starcon.c index cf9d6724f..5903e6860 100644 --- a/sc2/src/uqm/starcon.c +++ b/sc2/src/uqm/starcon.c @@ -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" From e4657ea3d1b1724ab434c2ebb53fdd76494f710b Mon Sep 17 00:00:00 2001 From: Michael Martin Date: Wed, 25 Sep 2013 22:31:42 -0700 Subject: [PATCH 04/22] Improved save file format inspired by IFF. Savefile is built of chunks that include their length. The only two right now are Summ (the summary, must be first) and OmnZ (the omnibus compressed data). Padding that was originally used for alignment purposes has been removed from the format, unless is it is part of the compressed data. --- sc2/src/uqm/load.c | 99 ++++++++++++++++++++++++++++++++++++++-------- sc2/src/uqm/save.c | 20 ++++++---- sc2/src/uqm/save.h | 6 ++- 3 files changed, 98 insertions(+), 27 deletions(-) diff --git a/sc2/src/uqm/load.c b/sc2/src/uqm/load.c index e115273b6..b4da899ec 100644 --- a/sc2/src/uqm/load.c +++ b/sc2/src/uqm/load.c @@ -160,6 +160,18 @@ read_a8 (void *fp, BYTE *ar, COUNT count) return ReadResFile (ar, 1, count, fp) == count; } +static inline size_t +skip_8 (void *fp, COUNT count) +{ + int i; + for (i = 0; i < count; ++i) + { + if (read_8(fp, NULL) != 1) + return 0; + } + return 1; +} + static inline size_t read_str (void *fp, char *str, COUNT count) { @@ -478,7 +490,7 @@ LoadGameState (GAME_STATE *GSPtr, DECODE_REF fh) } static BOOLEAN -LoadSisState (SIS_STATE *SSPtr, void *fp, SDWORD first) +LoadSisState (SIS_STATE *SSPtr, void *fp, SDWORD first, BOOLEAN legacy) { SSPtr->log_x = first; if ( @@ -496,26 +508,31 @@ LoadSisState (SIS_STATE *SSPtr, void *fp, SDWORD first) 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 */ + read_str (fp, SSPtr->PlanetName, SIS_NAME_SIZE) != 1 ) return FALSE; - else - return TRUE; + if (legacy && (read_16 (fp, NULL) != 1)) + return FALSE; + return TRUE; } static BOOLEAN -LoadSummary (SUMMARY_DESC *SummPtr, void *fp) +LoadSummary (SUMMARY_DESC *SummPtr, void *fp, BOOLEAN *legacy_ptr) { SDWORD magic; + DWORD nameSize = 0; BOOLEAN legacy; - SummPtr->SaveName[0] = 0; if (!read_32s (fp, &magic)) return FALSE; if (magic == SAVE_MAGIC) { legacy = FALSE; + *legacy_ptr = FALSE; + if (read_32 (fp, &magic) != 1 || magic != SUMMARY_MAGIC) + return FALSE; + if (read_32 (fp, &magic) != 1 || magic < 161) + return FALSE; + nameSize = magic - 160; // Read in the real first value for LoadSisState if (read_32 (fp, &magic) != 1) return FALSE; @@ -525,9 +542,10 @@ LoadSummary (SUMMARY_DESC *SummPtr, void *fp) // Otherwise, we're legacy and the "magic" number was // really LoadSisState's first value legacy = TRUE; + *legacy_ptr = TRUE; } - if (!LoadSisState (&SummPtr->SS, fp, magic)) + if (!LoadSisState (&SummPtr->SS, fp, magic, legacy)) return FALSE; if ( @@ -544,14 +562,32 @@ LoadSummary (SUMMARY_DESC *SummPtr, void *fp) read_a8 (fp, SummPtr->DeviceList, MAX_EXCLUSIVE_DEVICES) != 1 ) return FALSE; - if (!legacy && (read_a8 (fp, SummPtr->SaveName, SAVE_NAME_SIZE) != 1)) - return FALSE; - // Don't trust the savefile to properly null-terminate! - SummPtr->SaveName[SAVE_NAME_SIZE-1] = 0; - if (read_16 (fp, NULL) != 1) /* padding */ - return FALSE; + + if (!legacy) + { + if (nameSize < SAVE_NAME_SIZE) + { + if (read_a8 (fp, SummPtr->SaveName, nameSize) != 1) + return FALSE; + SummPtr->SaveName[nameSize] = 0; + } + else + { + DWORD remaining = nameSize - SAVE_NAME_SIZE + 1; + if (read_a8 (fp, SummPtr->SaveName, SAVE_NAME_SIZE-1) != 1) + return FALSE; + SummPtr->SaveName[SAVE_NAME_SIZE-1] = 0; + if (skip_8 (fp, remaining) != 1) + return FALSE; + } + } else - return TRUE; + { + SummPtr->SaveName[0] = 0; + if (read_16 (fp, NULL) != 1) /* padding */ + return FALSE; + } + return TRUE; } static void @@ -577,13 +613,15 @@ LoadGame (COUNT which_game, SUMMARY_DESC *SummPtr) COUNT num_links; STAR_DESC SD; ACTIVITY Activity; + BOOLEAN legacy; + DWORD chunk, chunkSize; sprintf (file, "starcon2.%02u", which_game); in_fp = res_OpenResFile (saveDir, file, "rb"); if (!in_fp) return FALSE; - if (!LoadSummary (&loc_sd, in_fp)) + if (!LoadSummary (&loc_sd, in_fp, &legacy)) { log_add (log_Error, "Warning: Savegame is corrupt"); res_CloseResFile (in_fp); @@ -618,6 +656,33 @@ LoadGame (COUNT which_game, SUMMARY_DESC *SummPtr) GlobData.SIS_state = SummPtr->SS; + if (!legacy) + { + chunk = 0; + while (chunk != OMNIZIP_MAGIC) + { + if (read_32(in_fp, &chunk) != 1) + { + res_CloseResFile (in_fp); + return FALSE; + } + if (read_32(in_fp, &chunkSize) != 1) + { + res_CloseResFile (in_fp); + return FALSE; + } + if (chunk == OMNIZIP_MAGIC) + break; + + log_add (log_Debug, "Skipping chunk of tag %08X (size %u)", chunk, chunkSize); + if (skip_8(in_fp, chunkSize) != 1) + return FALSE; + } + /* Fortunately for us, the OmnZ chunk is internally + * self-sizing, so we can ignore the chunk size as long as we + * aren't skipping it. */ + } + if ((fh = copen (in_fp, FILE_STREAM, STREAM_READ)) == 0) { res_CloseResFile (in_fp); diff --git a/sc2/src/uqm/save.c b/sc2/src/uqm/save.c index 9c3e389d8..509515c15 100644 --- a/sc2/src/uqm/save.c +++ b/sc2/src/uqm/save.c @@ -431,9 +431,7 @@ SaveSisState (const SIS_STATE *SSPtr, void *fp) write_str (fp, SSPtr->ShipName, SIS_NAME_SIZE) != 1 || write_str (fp, SSPtr->CommanderName, SIS_NAME_SIZE) != 1 || - write_str (fp, SSPtr->PlanetName, SIS_NAME_SIZE) != 1 || - - write_16 (fp, 0) != 1 /* padding */ + write_str (fp, SSPtr->PlanetName, SIS_NAME_SIZE) != 1 ) return FALSE; else @@ -443,7 +441,11 @@ SaveSisState (const SIS_STATE *SSPtr, void *fp) static BOOLEAN SaveSummary (const SUMMARY_DESC *SummPtr, void *fp) { - if (write_32 (fp, SAVE_MAGIC) != 1) + if ( + write_32 (fp, SAVE_MAGIC) != 1 || + write_32 (fp, SUMMARY_MAGIC) != 1 || + write_32 (fp, 161 + strlen(SummPtr->SaveName)) != 1 + ) return FALSE; if (!SaveSisState (&SummPtr->SS, fp)) return FALSE; @@ -460,9 +462,7 @@ SaveSummary (const SUMMARY_DESC *SummPtr, void *fp) write_8 (fp, SummPtr->NumDevices) != 1 || write_a8 (fp, SummPtr->ShipList, MAX_BUILT_SHIPS) != 1 || write_a8 (fp, SummPtr->DeviceList, MAX_EXCLUSIVE_DEVICES) != 1 || - write_a8 (fp, SummPtr->SaveName, SAVE_NAME_SIZE) != 1 || - - write_16 (fp, 0) != 1 /* padding */ + write_a8 (fp, SummPtr->SaveName, strlen(SummPtr->SaveName)+1) != 1 ) return FALSE; else @@ -835,13 +835,17 @@ RetrySave: // Write the memory file to the actual savegame file. sprintf (file, "starcon2.%02u", which_game); log_add (log_Debug, "'%s' is %u bytes long", file, - flen + sizeof (*SummPtr)); + flen + 181 + strlen(SummPtr->SaveName)); if (flen && (out_fp = res_OpenResFile (saveDir, file, "wb"))) { PrepareSummary (SummPtr, name); success = SaveSummary (SummPtr, out_fp); // Then write the rest of the data. + if (success && write_32 (out_fp, OMNIZIP_MAGIC) != 1) + success = FALSE; + if (success && write_32 (out_fp, flen) != 1) + success = FALSE; if (success && WriteResFile (h, flen, 1, out_fp) != 1) success = FALSE; diff --git a/sc2/src/uqm/save.h b/sc2/src/uqm/save.h index 6f37f01ff..f06c4a051 100644 --- a/sc2/src/uqm/save.h +++ b/sc2/src/uqm/save.h @@ -31,8 +31,10 @@ extern "C" { // is only used for displaying savegame summaries. There is also // room for only 16 devices on screen. #define MAX_EXCLUSIVE_DEVICES 16 -#define SAVE_MAGIC 0x01534d55 -#define SAVE_NAME_SIZE 24 +#define SAVE_MAGIC 0x01534d55 // "UMS\x01": UQM Save version 1 +#define SUMMARY_MAGIC 0x6d6d7553 // "Summ": Summary. Must be first! +#define OMNIZIP_MAGIC 0x5a6e6d4f // "OmnZ": All data compressed. +#define SAVE_NAME_SIZE 64 typedef struct { From 78d6312a1e9cd91fee1aa1fb961d4c050821f6ad Mon Sep 17 00:00:00 2001 From: Michael Martin Date: Sun, 6 Oct 2013 17:24:20 -0700 Subject: [PATCH 05/22] Make the UI for named saves acceptable. --- sc2/src/uqm/gameopt.c | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/sc2/src/uqm/gameopt.c b/sc2/src/uqm/gameopt.c index 18bc0ab8b..b604dd538 100644 --- a/sc2/src/uqm/gameopt.c +++ b/sc2/src/uqm/gameopt.c @@ -362,6 +362,12 @@ DrawSaveNameString (UNICODE *Str, COUNT CursorPos, COUNT state, COUNT gameIndex) 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; @@ -386,12 +392,11 @@ DrawSaveNameString (UNICODE *Str, COUNT CursorPos, COUNT state, COUNT gameIndex) lf.align = ALIGN_LEFT; SetContextFont (Font); - lf.pStr = Str; + lf.pStr = fullStr; lf.CharCount = (COUNT)~0; if (!(state & DDSHS_EDIT)) { - //RECT r; TEXT t; SetContextForeGroundColor (BLACK_COLOR); @@ -403,13 +408,13 @@ DrawSaveNameString (UNICODE *Str, COUNT CursorPos, COUNT state, COUNT gameIndex) t.pStr = Str; t.CharCount = (COUNT)~0; SetContextForeGroundColor (CAPTAIN_NAME_TEXT_COLOR); - font_DrawText (&t); + font_DrawText (&lf); } else { // editing state - COUNT i; + COUNT i, FullCursorPos; RECT text_r; - BYTE char_deltas[SAVE_NAME_SIZE]; + BYTE char_deltas[256]; BYTE *pchar_deltas; TextRect (&lf, &text_r, char_deltas); @@ -427,18 +432,20 @@ DrawSaveNameString (UNICODE *Str, COUNT CursorPos, COUNT state, COUNT gameIndex) pchar_deltas = char_deltas; - for (i = CursorPos; i > 0; --i) + FullCursorPos = CursorPos + strlen(dateStr) - 1; + for (i = FullCursorPos; i > 0; --i) text_r.corner.x += *pchar_deltas++; - if (CursorPos < lf.CharCount) /* end of line */ + + if (FullCursorPos < lf.CharCount) /* end of line */ --text_r.corner.x; if (state & DDSHS_BLOCKCUR) { // Use block cursor for keyboardless systems - if (CursorPos == lf.CharCount) + if (FullCursorPos == lf.CharCount) { // cursor at end-line -- use insertion point text_r.extent.width = 1; } - else if (CursorPos + 1 == lf.CharCount) + else if (FullCursorPos + 1 == lf.CharCount) { // extra pixel for last char margin text_r.extent.width = (SIZE)*pchar_deltas + 2; } @@ -481,7 +488,7 @@ static BOOLEAN NameSaveGame (COUNT gameIndex, UNICODE *buf) { TEXTENTRY_STATE tes; - COUNT CursPos = 0; + COUNT CursPos = strlen(buf); COUNT *gIndex = HMalloc (sizeof (COUNT)); RECT r; *gIndex = gameIndex; @@ -1104,7 +1111,7 @@ static BOOLEAN SaveLoadGame (PICK_GAME_STATE *pickState, COUNT gameIndex, BOOLEAN *canceled_by_user) { SUMMARY_DESC *desc = pickState->summary + gameIndex; - UNICODE nameBuf[SAVE_NAME_SIZE]; + UNICODE nameBuf[256]; STAMP saveStamp; BOOLEAN success; @@ -1112,7 +1119,10 @@ SaveLoadGame (PICK_GAME_STATE *pickState, COUNT gameIndex, BOOLEAN *canceled_by_ if (pickState->saving) { - nameBuf[0] = 0; + // 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); From 51fa5a7ae50f8b1f51cb403004f8167f0f034247 Mon Sep 17 00:00:00 2001 From: Michael Martin Date: Sun, 6 Oct 2013 23:28:31 -0700 Subject: [PATCH 06/22] Save format refactoring: Uncompressed save format Legacy files are handled by load_legacy.c, but this now should be the only part of the code that still uses the custom decompression library. These save formats are very much interim and held for testing purposes! Do not use these commits for actual play, or your saves will be lost. --- sc2/src/uqm/Makeinfo | 3 +- sc2/src/uqm/load.c | 526 +++++++++++--------------- sc2/src/uqm/load_legacy.c | 761 ++++++++++++++++++++++++++++++++++++++ sc2/src/uqm/save.c | 92 +++-- sc2/src/uqm/save.h | 3 +- 5 files changed, 1034 insertions(+), 351 deletions(-) create mode 100644 sc2/src/uqm/load_legacy.c diff --git a/sc2/src/uqm/Makeinfo b/sc2/src/uqm/Makeinfo index 5f0145617..4b224fac0 100644 --- a/sc2/src/uqm/Makeinfo +++ b/sc2/src/uqm/Makeinfo @@ -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 diff --git a/sc2/src/uqm/load.c b/sc2/src/uqm/load.c index b4da899ec..081ce9526 100644 --- a/sc2/src/uqm/load.c +++ b/sc2/src/uqm/load.c @@ -19,7 +19,6 @@ #include #include "build.h" -#include "libs/declib.h" #include "encount.h" #include "starmap.h" #include "libs/file.h" @@ -38,74 +37,6 @@ ACTIVITY NextActivity; -// 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) { @@ -124,6 +55,15 @@ read_16 (void *fp, UWORD *v) return ReadResFile (v, 2, 1, fp); } +static inline size_t +read_16s (void *fp, SWORD *v) +{ + SWORD t; + if (!v) /* read value ignored */ + v = &t; + return ReadResFile (v, 2, 1, fp); +} + static inline size_t read_32 (void *fp, DWORD *v) { @@ -193,11 +133,11 @@ read_a16 (void *fp, UWORD *ar, COUNT count) } static void -LoadEmptyQueue (DECODE_REF fh) +LoadEmptyQueue (void *fh) { COUNT num_links; - cread_16 (fh, &num_links); + read_16 (fh, &num_links); if (num_links) { log_add (log_Error, "LoadEmptyQueue(): BUG: the queue is not empty!"); @@ -208,11 +148,11 @@ LoadEmptyQueue (DECODE_REF fh) } static void -LoadShipQueue (DECODE_REF fh, QUEUE *pQueue) +LoadShipQueue (void *fh, QUEUE *pQueue) { COUNT num_links; - cread_16 (fh, &num_links); + read_16 (fh, &num_links); while (num_links--) { @@ -221,38 +161,38 @@ LoadShipQueue (DECODE_REF fh, QUEUE *pQueue) COUNT Index; BYTE tmpb; - cread_16 (fh, &Index); + read_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); + read_16 (fh, NULL); /* unused: was which_side */ + read_8 (fh, &FragPtr->captains_name_index); + read_8 (fh, NULL); /* padding */ + read_16 (fh, NULL); /* unused: was ship_flags */ + read_8 (fh, &FragPtr->race_id); + read_8 (fh, &FragPtr->index); // XXX: reading crew as BYTE to maintain savegame compatibility - cread_8 (fh, &tmpb); + read_8 (fh, &tmpb); FragPtr->crew_level = tmpb; - cread_8 (fh, &tmpb); + read_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 */ + read_8 (fh, &FragPtr->energy_level); + read_8 (fh, &FragPtr->max_energy); + read_16 (fh, NULL); /* unused; was loc.x */ + read_16 (fh, NULL); /* unused; was loc.y */ UnlockShipFrag (pQueue, hStarShip); } } static void -LoadRaceQueue (DECODE_REF fh, QUEUE *pQueue) +LoadRaceQueue (void *fh, QUEUE *pQueue) { COUNT num_links; - cread_16 (fh, &num_links); + read_16 (fh, &num_links); while (num_links--) { @@ -261,44 +201,44 @@ LoadRaceQueue (DECODE_REF fh, QUEUE *pQueue) COUNT Index; BYTE tmpb; - cread_16 (fh, &Index); + read_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); + read_16 (fh, &FleetPtr->allied_state); + read_8 (fh, &FleetPtr->days_left); + read_8 (fh, &FleetPtr->growth_fract); + read_8 (fh, &tmpb); FleetPtr->crew_level = tmpb; - cread_8 (fh, &tmpb); + read_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); + read_8 (fh, &FleetPtr->growth); + read_8 (fh, &FleetPtr->max_energy); + read_16s(fh, &FleetPtr->loc.x); + read_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 */ + read_16 (fh, &FleetPtr->actual_strength); + read_16 (fh, &FleetPtr->known_strength); + read_16s(fh, &FleetPtr->known_loc.x); + read_16s(fh, &FleetPtr->known_loc.y); + read_8 (fh, &FleetPtr->growth_err_term); + read_8 (fh, &FleetPtr->func_index); + read_16s(fh, &FleetPtr->dest_loc.x); + read_16s(fh, &FleetPtr->dest_loc.y); + read_16 (fh, NULL); /* alignment padding */ UnlockFleetInfo (pQueue, hStarShip); } } static void -LoadGroupQueue (DECODE_REF fh, QUEUE *pQueue) +LoadGroupQueue (void *fh, QUEUE *pQueue) { COUNT num_links; - cread_16 (fh, &num_links); + read_16 (fh, &num_links); while (num_links--) { @@ -306,101 +246,101 @@ LoadGroupQueue (DECODE_REF fh, QUEUE *pQueue) IP_GROUP *GroupPtr; BYTE tmpb; - cread_16 (fh, NULL); /* unused; was race_id */ + read_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 */ + read_16 (fh, NULL); /* unused; was which_side */ + read_8 (fh, NULL); /* unused; was captains_name_index */ + read_8 (fh, NULL); /* padding; for savegame compat */ + read_16 (fh, &GroupPtr->group_counter); + read_8 (fh, &GroupPtr->race_id); + read_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 */ + read_8 (fh, &GroupPtr->in_system); /* was crew_level */ + read_8 (fh, NULL); /* unused; was max_crew */ + read_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); + read_8 (fh, &GroupPtr->group_id); /* was max_energy */ + read_16s(fh, &GroupPtr->loc.x); + read_16s(fh, &GroupPtr->loc.y); UnlockIpGroup (pQueue, hGroup); } } static void -LoadEncounter (ENCOUNTER *EncounterPtr, DECODE_REF fh) +LoadEncounter (ENCOUNTER *EncounterPtr, void *fh) { COUNT i; BYTE tmpb; - cread_ptr (fh); /* useless ptr; HENCOUNTER pred */ + read_ptr (fh); /* useless ptr; HENCOUNTER pred */ EncounterPtr->pred = 0; - cread_ptr (fh); /* useless ptr; HENCOUNTER succ */ + read_ptr (fh); /* useless ptr; HENCOUNTER succ */ EncounterPtr->succ = 0; - cread_ptr (fh); /* useless ptr; HELEMENT hElement */ + read_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); + read_16s (fh, &EncounterPtr->transition_state); + read_16s (fh, &EncounterPtr->origin.x); + read_16s (fh, &EncounterPtr->origin.y); + read_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); + read_16s (fh, &EncounterPtr->loc_pt.x); + read_16s (fh, &EncounterPtr->loc_pt.y); + read_8 (fh, &EncounterPtr->race_id); + read_8 (fh, &tmpb); EncounterPtr->num_ships = tmpb & ENCOUNTER_SHIPS_MASK; EncounterPtr->flags = tmpb & ENCOUNTER_FLAGS_MASK; - cread_16 (fh, NULL); /* alignment padding */ + read_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 */ + read_16 (fh, NULL); /* useless; was SHIP_INFO.ship_flags */ + read_8 (fh, &ShipInfo->race_id); + read_8 (fh, NULL); /* useless; was SHIP_INFO.var2 */ // XXX: reading crew as BYTE to maintain savegame compatibility - cread_8 (fh, &tmpb); + read_8 (fh, &tmpb); ShipInfo->crew_level = tmpb; - cread_8 (fh, &tmpb); + read_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 */ + read_8 (fh, NULL); /* useless; was SHIP_INFO.energy_level */ + read_8 (fh, &ShipInfo->max_energy); + read_16 (fh, NULL); /* useless; was SHIP_INFO.loc.x */ + read_16 (fh, NULL); /* useless; was SHIP_INFO.loc.y */ + read_32 (fh, NULL); /* useless val; STRING race_strings */ + read_ptr (fh); /* useless ptr; FRAME icons */ + read_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); + read_32s (fh, &EncounterPtr->log_x); + read_32s (fh, &EncounterPtr->log_y); } static void -LoadEvent (EVENT *EventPtr, DECODE_REF fh) +LoadEvent (EVENT *EventPtr, void *fh) { - cread_ptr (fh); /* useless ptr; HEVENT pred */ + read_ptr (fh); /* useless ptr; HEVENT pred */ EventPtr->pred = 0; - cread_ptr (fh); /* useless ptr; HEVENT succ */ + read_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 */ + read_8 (fh, &EventPtr->day_index); + read_8 (fh, &EventPtr->month_index); + read_16 (fh, &EventPtr->year_index); + read_8 (fh, &EventPtr->func_index); + read_8 (fh, NULL); /* padding */ + read_16 (fh, NULL); /* padding */ } static void -DummyLoadQueue (QUEUE *QueuePtr, DECODE_REF fh) +DummyLoadQueue (QUEUE *QueuePtr, void *fh) { /* QUEUE should never actually be loaded since it contains * purely internal representation and the lists @@ -408,74 +348,74 @@ DummyLoadQueue (QUEUE *QueuePtr, DECODE_REF fh) (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 */ + read_ptr (fh); /* HLINK head */ + read_ptr (fh); /* HLINK tail */ + read_ptr (fh); /* BYTE* pq_tab */ + read_ptr (fh); /* HLINK free_list */ + read_16 (fh, NULL); /* MEM_HANDLE hq_tab */ + read_16 (fh, NULL); /* COUNT object_size */ + read_8 (fh, NULL); /* BYTE num_objects */ - cread_8 (fh, NULL); /* padding */ - cread_16 (fh, NULL); /* padding */ + read_8 (fh, NULL); /* padding */ + read_16 (fh, NULL); /* padding */ } static void -LoadClockState (CLOCK_STATE *ClockPtr, DECODE_REF fh) +LoadClockState (CLOCK_STATE *ClockPtr, void *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 */ + read_8 (fh, &ClockPtr->day_index); + read_8 (fh, &ClockPtr->month_index); + read_16 (fh, &ClockPtr->year_index); + read_16s (fh, &ClockPtr->tick_count); + read_16s (fh, &ClockPtr->day_in_ticks); + read_ptr (fh); /* not loading ptr; Semaphore clock_sem */ + read_ptr (fh); /* not loading ptr; Task clock_task */ + read_32 (fh, NULL); /* not loading; DWORD TimeCounter */ DummyLoadQueue (&ClockPtr->event_q, fh); } static void -LoadGameState (GAME_STATE *GSPtr, DECODE_REF fh) +LoadGameState (GAME_STATE *GSPtr, void *fh) { BYTE dummy8; - 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); + read_8 (fh, &dummy8); /* obsolete */ + read_8 (fh, &GSPtr->glob_flags); + read_8 (fh, &GSPtr->CrewCost); + read_8 (fh, &GSPtr->FuelCost); + read_a8 (fh, GSPtr->ModuleCost, NUM_MODULES); + read_a8 (fh, GSPtr->ElementWorth, NUM_ELEMENT_CATEGORIES); + read_ptr (fh); /* not loading ptr; PRIMITIVE *DisplayArray */ + read_16 (fh, &GSPtr->CurrentActivity); - cread_16 (fh, NULL); /* CLOCK_STATE alignment padding */ + read_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); + read_16s (fh, &GSPtr->autopilot.x); + read_16s (fh, &GSPtr->autopilot.y); + read_16s (fh, &GSPtr->ip_location.x); + read_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); + read_16s (fh, &GSPtr->ShipStamp.origin.x); + read_16s (fh, &GSPtr->ShipStamp.origin.y); + read_16 (fh, &GSPtr->ShipFacing); + read_8 (fh, &GSPtr->ip_planet); + read_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 */ + read_16 (fh, &GSPtr->velocity.TravelAngle); + read_16s (fh, &GSPtr->velocity.vector.width); + read_16s (fh, &GSPtr->velocity.vector.height); + read_16s (fh, &GSPtr->velocity.fract.width); + read_16s (fh, &GSPtr->velocity.fract.height); + read_16s (fh, &GSPtr->velocity.error.width); + read_16s (fh, &GSPtr->velocity.error.height); + read_16s (fh, &GSPtr->velocity.incr.width); + read_16s (fh, &GSPtr->velocity.incr.height); + read_16 (fh, NULL); /* VELOCITY_DESC padding */ - cread_32 (fh, &GSPtr->BattleGroupRef); + read_32 (fh, &GSPtr->BattleGroupRef); DummyLoadQueue (&GSPtr->avail_race_q, fh); DummyLoadQueue (&GSPtr->npc_built_ship_q, fh); @@ -483,17 +423,17 @@ LoadGameState (GAME_STATE *GSPtr, DECODE_REF fh) DummyLoadQueue (&GSPtr->encounter_q, fh); DummyLoadQueue (&GSPtr->built_ship_q, fh); - cread_a8 (fh, GSPtr->GameState, sizeof (GSPtr->GameState)); + read_a8 (fh, GSPtr->GameState, sizeof (GSPtr->GameState)); assert (sizeof (GSPtr->GameState) % 4 == 3); - cread_8 (fh, NULL); /* GAME_STATE alignment padding */ + read_8 (fh, NULL); /* GAME_STATE alignment padding */ } static BOOLEAN -LoadSisState (SIS_STATE *SSPtr, void *fp, SDWORD first, BOOLEAN legacy) +LoadSisState (SIS_STATE *SSPtr, void *fp) { - SSPtr->log_x = first; 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 || @@ -511,45 +451,33 @@ LoadSisState (SIS_STATE *SSPtr, void *fp, SDWORD first, BOOLEAN legacy) read_str (fp, SSPtr->PlanetName, SIS_NAME_SIZE) != 1 ) return FALSE; - if (legacy && (read_16 (fp, NULL) != 1)) - return FALSE; return TRUE; } static BOOLEAN -LoadSummary (SUMMARY_DESC *SummPtr, void *fp, BOOLEAN *legacy_ptr) +LoadSummary (SUMMARY_DESC *SummPtr, void *fp) { SDWORD magic; DWORD nameSize = 0; - BOOLEAN legacy; if (!read_32s (fp, &magic)) return FALSE; if (magic == SAVE_MAGIC) { - legacy = FALSE; - *legacy_ptr = FALSE; if (read_32 (fp, &magic) != 1 || magic != SUMMARY_MAGIC) return FALSE; if (read_32 (fp, &magic) != 1 || magic < 161) return FALSE; nameSize = magic - 160; - // Read in the real first value for LoadSisState - if (read_32 (fp, &magic) != 1) - return FALSE; } else { - // Otherwise, we're legacy and the "magic" number was - // really LoadSisState's first value - legacy = TRUE; - *legacy_ptr = TRUE; + return FALSE; } - if (!LoadSisState (&SummPtr->SS, fp, magic, legacy)) + if (!LoadSisState (&SummPtr->SS, fp)) return FALSE; - if ( - read_8 (fp, &SummPtr->Activity) != 1 || + 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 || @@ -563,42 +491,33 @@ LoadSummary (SUMMARY_DESC *SummPtr, void *fp, BOOLEAN *legacy_ptr) ) return FALSE; - if (!legacy) + if (nameSize < SAVE_NAME_SIZE) { - if (nameSize < SAVE_NAME_SIZE) - { - if (read_a8 (fp, SummPtr->SaveName, nameSize) != 1) - return FALSE; - SummPtr->SaveName[nameSize] = 0; - } - else - { - DWORD remaining = nameSize - SAVE_NAME_SIZE + 1; - if (read_a8 (fp, SummPtr->SaveName, SAVE_NAME_SIZE-1) != 1) - return FALSE; - SummPtr->SaveName[SAVE_NAME_SIZE-1] = 0; - if (skip_8 (fp, remaining) != 1) - return FALSE; - } + if (read_a8 (fp, SummPtr->SaveName, nameSize) != 1) + return FALSE; + SummPtr->SaveName[nameSize] = 0; } else { - SummPtr->SaveName[0] = 0; - if (read_16 (fp, NULL) != 1) /* padding */ + DWORD remaining = nameSize - SAVE_NAME_SIZE + 1; + if (read_a8 (fp, SummPtr->SaveName, SAVE_NAME_SIZE-1) != 1) + return FALSE; + SummPtr->SaveName[SAVE_NAME_SIZE-1] = 0; + if (skip_8 (fp, remaining) != 1) return FALSE; } return TRUE; } static void -LoadStarDesc (STAR_DESC *SDPtr, DECODE_REF fh) +LoadStarDesc (STAR_DESC *SDPtr, void *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); + read_16s(fh, &SDPtr->star_pt.x); + read_16s(fh, &SDPtr->star_pt.y); + read_8 (fh, &SDPtr->Type); + read_8 (fh, &SDPtr->Index); + read_8 (fh, &SDPtr->Prefix); + read_8 (fh, &SDPtr->Postfix); } BOOLEAN @@ -609,23 +528,20 @@ LoadGame (COUNT which_game, SUMMARY_DESC *SummPtr) char buf[256]; SUMMARY_DESC loc_sd; GAME_STATE_FILE *fp; - DECODE_REF fh; COUNT num_links; STAR_DESC SD; ACTIVITY Activity; - BOOLEAN legacy; DWORD chunk, chunkSize; - sprintf (file, "starcon2.%02u", which_game); + sprintf (file, "uqmsave.%02u", which_game); in_fp = res_OpenResFile (saveDir, file, "rb"); if (!in_fp) - return FALSE; + return LoadLegacyGame (which_game, SummPtr); - if (!LoadSummary (&loc_sd, in_fp, &legacy)) + if (!LoadSummary (&loc_sd, in_fp)) { - log_add (log_Error, "Warning: Savegame is corrupt"); res_CloseResFile (in_fp); - return FALSE; + return LoadLegacyGame (which_game, SummPtr); } if (!SummPtr) @@ -639,54 +555,27 @@ LoadGame (COUNT which_game, SUMMARY_DESC *SummPtr) 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 (!legacy) + chunk = 0; + while (chunk != OMNIBUS_MAGIC) { - chunk = 0; - while (chunk != OMNIZIP_MAGIC) + if (read_32(in_fp, &chunk) != 1) { - if (read_32(in_fp, &chunk) != 1) - { - res_CloseResFile (in_fp); - return FALSE; - } - if (read_32(in_fp, &chunkSize) != 1) - { - res_CloseResFile (in_fp); - return FALSE; - } - if (chunk == OMNIZIP_MAGIC) - break; - - log_add (log_Debug, "Skipping chunk of tag %08X (size %u)", chunk, chunkSize); - if (skip_8(in_fp, chunkSize) != 1) - return FALSE; + res_CloseResFile (in_fp); + return FALSE; } - /* Fortunately for us, the OmnZ chunk is internally - * self-sizing, so we can ignore the chunk size as long as we - * aren't skipping it. */ - } + if (read_32(in_fp, &chunkSize) != 1) + { + res_CloseResFile (in_fp); + return FALSE; + } + if (chunk == OMNIBUS_MAGIC) + break; - if ((fh = copen (in_fp, FILE_STREAM, STREAM_READ)) == 0) - { - res_CloseResFile (in_fp); - return FALSE; + log_add (log_Debug, "Skipping chunk of tag %08X (size %u)", chunk, chunkSize); + if (skip_8(in_fp, chunkSize) != 1) + return FALSE; } ReinitQueue (&GLOBAL (GameClock.event_q)); @@ -697,11 +586,11 @@ LoadGame (COUNT which_game, SUMMARY_DESC *SummPtr) memset (&GLOBAL (GameState[0]), 0, sizeof (GLOBAL (GameState))); Activity = GLOBAL (CurrentActivity); - LoadGameState (&GlobData.Game_state, fh); + LoadGameState (&GlobData.Game_state, in_fp); NextActivity = GLOBAL (CurrentActivity); GLOBAL (CurrentActivity) = Activity; - LoadRaceQueue (fh, &GLOBAL (avail_race_q)); + LoadRaceQueue (in_fp, &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 @@ -709,22 +598,22 @@ LoadGame (COUNT which_game, SUMMARY_DESC *SummPtr) if (!(NextActivity & START_INTERPLANETARY)) { if (NextActivity & START_ENCOUNTER) - LoadShipQueue (fh, &GLOBAL (npc_built_ship_q)); + LoadShipQueue (in_fp, &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)); + LoadGroupQueue (in_fp, &GLOBAL (ip_group_q)); else // XXX: The empty queue read is only needed to maintain // the savegame compatibility - LoadEmptyQueue (fh); + LoadEmptyQueue (in_fp); } - LoadShipQueue (fh, &GLOBAL (built_ship_q)); + LoadShipQueue (in_fp, &GLOBAL (built_ship_q)); // Load the game events (compressed) - cread_16 (fh, &num_links); + read_16 (in_fp, &num_links); { #ifdef DEBUG_LOAD log_add (log_Debug, "EVENTS:"); @@ -737,7 +626,7 @@ LoadGame (COUNT which_game, SUMMARY_DESC *SummPtr) hEvent = AllocEvent (); LockEvent (hEvent, &EventPtr); - LoadEvent (EventPtr, fh); + LoadEvent (EventPtr, in_fp); #ifdef DEBUG_LOAD log_add (log_Debug, "\t%u/%u/%u -- %u", @@ -752,7 +641,7 @@ LoadGame (COUNT which_game, SUMMARY_DESC *SummPtr) } // Load the encounters (black globes in HS/QS (compressed)) - cread_16 (fh, &num_links); + read_16 (in_fp, &num_links); { while (num_links--) { @@ -762,7 +651,7 @@ LoadGame (COUNT which_game, SUMMARY_DESC *SummPtr) hEncounter = AllocEncounter (); LockEncounter (hEncounter, &EncounterPtr); - LoadEncounter (EncounterPtr, fh); + LoadEncounter (EncounterPtr, in_fp); UnlockEncounter (hEncounter); PutEncounter (hEncounter); @@ -775,13 +664,13 @@ LoadGame (COUNT which_game, SUMMARY_DESC *SummPtr) { DWORD flen; - cread_32 (fh, &flen); + read_32 (in_fp, &flen); while (flen) { COUNT num_bytes; num_bytes = flen >= sizeof (buf) ? sizeof (buf) : (COUNT)flen; - cread (buf, num_bytes, 1, fh); + read_a8 (in_fp, buf, num_bytes); WriteStateFile (buf, num_bytes, 1, fp); flen -= num_bytes; @@ -795,13 +684,13 @@ LoadGame (COUNT which_game, SUMMARY_DESC *SummPtr) { DWORD flen; - cread_32 (fh, &flen); + read_32 (in_fp, &flen); while (flen) { COUNT num_bytes; num_bytes = flen >= sizeof (buf) ? sizeof (buf) : (COUNT)flen; - cread (buf, num_bytes, 1, fh); + read_a8 (in_fp, buf, num_bytes); WriteStateFile (buf, num_bytes, 1, fp); flen -= num_bytes; @@ -815,13 +704,13 @@ LoadGame (COUNT which_game, SUMMARY_DESC *SummPtr) { DWORD flen; - cread_32 (fh, &flen); + read_32 (in_fp, &flen); while (flen) { COUNT num_bytes; num_bytes = flen >= sizeof (buf) ? sizeof (buf) : (COUNT)flen; - cread (buf, num_bytes, 1, fh); + read_a8 (in_fp, buf, num_bytes); WriteStateFile (buf, num_bytes, 1, fp); flen -= num_bytes; @@ -829,9 +718,8 @@ LoadGame (COUNT which_game, SUMMARY_DESC *SummPtr) CloseStateFile (fp); } - LoadStarDesc (&SD, fh); + LoadStarDesc (&SD, in_fp); - cclose (fh); res_CloseResFile (in_fp); EncounterGroup = 0; diff --git a/sc2/src/uqm/load_legacy.c b/sc2/src/uqm/load_legacy.c new file mode 100644 index 000000000..3c7a7e702 --- /dev/null +++ b/sc2/src/uqm/load_legacy.c @@ -0,0 +1,761 @@ +//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 + +#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; +} + +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; + + 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, GSPtr->GameState, sizeof (GSPtr->GameState)); + + assert (sizeof (GSPtr->GameState) % 4 == 3); + 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; +} + + diff --git a/sc2/src/uqm/save.c b/sc2/src/uqm/save.c index 509515c15..bfaa140f5 100644 --- a/sc2/src/uqm/save.c +++ b/sc2/src/uqm/save.c @@ -41,38 +41,63 @@ #include "libs/inplib.h" #include "libs/log.h" #include "libs/memlib.h" -#include "libs/declib.h" +typedef struct savebuf_struct +{ + char *buf; + size_t bufsize; + int index, error; +} SAVEBUF; // XXX: these should handle endian conversions later static inline COUNT -cwrite_8 (DECODE_REF fh, BYTE v) +cwrite_8 (SAVEBUF *fh, BYTE v) { - return cwrite (&v, 1, 1, fh); + if (!fh) + return 0; + if (!fh->error && fh->index < fh->bufsize) + { + fh->buf[fh->index++] = v; + return 1; + } + fh->error = 1; + return 0; } static inline COUNT -cwrite_16 (DECODE_REF fh, UWORD v) +cwrite_16 (SAVEBUF *fh, UWORD v) { - return cwrite (&v, 2, 1, fh); + if (cwrite_8 (fh, (BYTE)(v & 0xFF)) && + cwrite_8 (fh, (BYTE)((v >> 8) & 0xFF))) + return 1; + return 0; } static inline COUNT -cwrite_32 (DECODE_REF fh, DWORD v) +cwrite_32 (SAVEBUF *fh, DWORD v) { - return cwrite (&v, 4, 1, fh); + if (cwrite_8 (fh, (BYTE)(v & 0xFF)) && + cwrite_8 (fh, (BYTE)((v >> 8) & 0xFF)) && + cwrite_8 (fh, (BYTE)((v >> 16) & 0xFF)) && + cwrite_8 (fh, (BYTE)((v >> 24) & 0xFF))) + return 1; + return 0; } static inline COUNT -cwrite_ptr (DECODE_REF fh) +cwrite_ptr (SAVEBUF *fh) { return cwrite_32 (fh, 0); /* ptrs are useless in saves */ } static inline COUNT -cwrite_a8 (DECODE_REF fh, const BYTE *ar, COUNT count) +cwrite_a8 (SAVEBUF *fh, const BYTE *ar, COUNT count) { - return cwrite (ar, 1, count, fh) == count; + int i; + for (i = 0; i < count; ++i) + if (!cwrite_8 (fh, ar[i])) + return 0; + return 1; } static inline size_t @@ -124,7 +149,7 @@ write_a16 (void *fp, const UWORD *ar, COUNT count) } static void -SaveEmptyQueue (DECODE_REF fh) +SaveEmptyQueue (SAVEBUF *fh) { COUNT num_links = 0; @@ -133,7 +158,7 @@ SaveEmptyQueue (DECODE_REF fh) } static void -SaveShipQueue (DECODE_REF fh, QUEUE *pQueue) +SaveShipQueue (SAVEBUF *fh, QUEUE *pQueue) { COUNT num_links; HSHIPFRAG hStarShip; @@ -178,7 +203,7 @@ SaveShipQueue (DECODE_REF fh, QUEUE *pQueue) } static void -SaveRaceQueue (DECODE_REF fh, QUEUE *pQueue) +SaveRaceQueue (SAVEBUF *fh, QUEUE *pQueue) { COUNT num_links; HFLEETINFO hFleet; @@ -228,7 +253,7 @@ SaveRaceQueue (DECODE_REF fh, QUEUE *pQueue) } static void -SaveGroupQueue (DECODE_REF fh, QUEUE *pQueue) +SaveGroupQueue (SAVEBUF *fh, QUEUE *pQueue) { HIPGROUP hGroup, hNextGroup; @@ -266,7 +291,7 @@ SaveGroupQueue (DECODE_REF fh, QUEUE *pQueue) } static void -SaveEncounter (const ENCOUNTER *EncounterPtr, DECODE_REF fh) +SaveEncounter (const ENCOUNTER *EncounterPtr, SAVEBUF *fh) { COUNT i; @@ -312,7 +337,7 @@ SaveEncounter (const ENCOUNTER *EncounterPtr, DECODE_REF fh) } static void -SaveEvent (const EVENT *EventPtr, DECODE_REF fh) +SaveEvent (const EVENT *EventPtr, SAVEBUF *fh) { cwrite_ptr (fh); /* useless ptr; HEVENT pred */ cwrite_ptr (fh); /* useless ptr; HEVENT succ */ @@ -325,7 +350,7 @@ SaveEvent (const EVENT *EventPtr, DECODE_REF fh) } static void -DummySaveQueue (const QUEUE *QueuePtr, DECODE_REF fh) +DummySaveQueue (const QUEUE *QueuePtr, SAVEBUF *fh) { /* QUEUE should never actually be saved since it contains * purely internal representation and the lists @@ -346,7 +371,7 @@ DummySaveQueue (const QUEUE *QueuePtr, DECODE_REF fh) } static void -SaveClockState (const CLOCK_STATE *ClockPtr, DECODE_REF fh) +SaveClockState (const CLOCK_STATE *ClockPtr, SAVEBUF *fh) { cwrite_8 (fh, ClockPtr->day_index); cwrite_8 (fh, ClockPtr->month_index); @@ -361,7 +386,7 @@ SaveClockState (const CLOCK_STATE *ClockPtr, DECODE_REF fh) } static void -SaveGameState (const GAME_STATE *GSPtr, DECODE_REF fh) +SaveGameState (const GAME_STATE *GSPtr, SAVEBUF *fh) { cwrite_8 (fh, 0); /* obsolete; BYTE cur_state */ cwrite_8 (fh, GSPtr->glob_flags); @@ -470,7 +495,7 @@ SaveSummary (const SUMMARY_DESC *SummPtr, void *fp) } static void -SaveStarDesc (const STAR_DESC *SDPtr, DECODE_REF fh) +SaveStarDesc (const STAR_DESC *SDPtr, SAVEBUF *fh) { cwrite_16 (fh, SDPtr->star_pt.x); cwrite_16 (fh, SDPtr->star_pt.y); @@ -644,14 +669,14 @@ SaveGame (COUNT which_game, SUMMARY_DESC *SummPtr, const char *name) { BOOLEAN success, made_room; void *out_fp, *h; - DECODE_REF fh; + SAVEBUF fh_backing, *fh; success = TRUE; made_room = FALSE; + fh = &fh_backing; RetrySave: - h = HMalloc (10 * 1024); - if (h == 0 - || (fh = copen (h, MEMORY_STREAM, STREAM_WRITE)) == 0) + h = HMalloc (128 * 1024); + if (h == 0) { if (success) { @@ -677,6 +702,10 @@ RetrySave: STAR_DESC SD; char buf[256], file[PATH_MAX]; + fh->buf = h; + fh->bufsize = 128 * 1024; + fh->index = 0; + fh->error = 0; success = TRUE; if (CurStarDescPtr) SD = *CurStarDescPtr; @@ -773,6 +802,7 @@ RetrySave: { flen = LengthStateFile (fp); // Write the uncompressed size. + printf ("Star Info: %u bytes\n", flen); cwrite_32 (fh, flen); while (flen) { @@ -780,7 +810,7 @@ RetrySave: num_bytes = flen >= sizeof (buf) ? sizeof (buf) : (COUNT)flen; ReadStateFile (buf, num_bytes, 1, fp); - cwrite (buf, num_bytes, 1, fh); + cwrite_a8 (fh, buf, num_bytes); flen -= num_bytes; } @@ -793,6 +823,7 @@ RetrySave: { flen = LengthStateFile (fp); // Write the uncompressed size. + printf ("Defined Group Info: %u bytes\n", flen); cwrite_32 (fh, flen); while (flen) { @@ -800,7 +831,7 @@ RetrySave: num_bytes = flen >= sizeof (buf) ? sizeof (buf) : (COUNT)flen; ReadStateFile (buf, num_bytes, 1, fp); - cwrite (buf, num_bytes, 1, fh); + cwrite_a8 (fh, buf, num_bytes); flen -= num_bytes; } @@ -813,6 +844,7 @@ RetrySave: { flen = LengthStateFile (fp); // Write the uncompressed size. + printf ("Random Group Info: %u bytes\n", flen); cwrite_32 (fh, flen); while (flen) { @@ -820,7 +852,7 @@ RetrySave: num_bytes = flen >= sizeof (buf) ? sizeof (buf) : (COUNT)flen; ReadStateFile (buf, num_bytes, 1, fp); - cwrite (buf, num_bytes, 1, fh); + cwrite_a8 (fh, buf, num_bytes); flen -= num_bytes; } @@ -830,10 +862,10 @@ RetrySave: // Write the current star desc into the memory file (compressed). SaveStarDesc (&SD, fh); - flen = cclose (fh); + flen = fh->index; // Write the memory file to the actual savegame file. - sprintf (file, "starcon2.%02u", which_game); + sprintf (file, "uqmsave.%02u", which_game); log_add (log_Debug, "'%s' is %u bytes long", file, flen + 181 + strlen(SummPtr->SaveName)); if (flen && (out_fp = res_OpenResFile (saveDir, file, "wb"))) @@ -842,7 +874,7 @@ RetrySave: success = SaveSummary (SummPtr, out_fp); // Then write the rest of the data. - if (success && write_32 (out_fp, OMNIZIP_MAGIC) != 1) + if (success && write_32 (out_fp, OMNIBUS_MAGIC) != 1) success = FALSE; if (success && write_32 (out_fp, flen) != 1) success = FALSE; diff --git a/sc2/src/uqm/save.h b/sc2/src/uqm/save.h index f06c4a051..62602eb90 100644 --- a/sc2/src/uqm/save.h +++ b/sc2/src/uqm/save.h @@ -33,7 +33,7 @@ extern "C" { #define MAX_EXCLUSIVE_DEVICES 16 #define SAVE_MAGIC 0x01534d55 // "UMS\x01": UQM Save version 1 #define SUMMARY_MAGIC 0x6d6d7553 // "Summ": Summary. Must be first! -#define OMNIZIP_MAGIC 0x5a6e6d4f // "OmnZ": All data compressed. +#define OMNIBUS_MAGIC 0x696e6d4f // "Omni": All data, uncompressed #define SAVE_NAME_SIZE 64 typedef struct @@ -53,6 +53,7 @@ typedef struct 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, const char *name); From 876daf0827f1ad8b8f2b5229cab7e278a821e315 Mon Sep 17 00:00:00 2001 From: Michael Martin Date: Mon, 7 Oct 2013 00:39:06 -0700 Subject: [PATCH 07/22] Remove padding and unused data from savegame format. --- sc2/src/uqm/load.c | 95 +++------------------------------- sc2/src/uqm/save.c | 126 ++++----------------------------------------- 2 files changed, 17 insertions(+), 204 deletions(-) diff --git a/sc2/src/uqm/load.c b/sc2/src/uqm/load.c index 081ce9526..d7788c8e0 100644 --- a/sc2/src/uqm/load.c +++ b/sc2/src/uqm/load.c @@ -86,13 +86,6 @@ read_32s (void *fp, SDWORD *v) 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) { @@ -167,21 +160,13 @@ LoadShipQueue (void *fh, QUEUE *pQueue) FragPtr = LockShipFrag (pQueue, hStarShip); // Read SHIP_FRAGMENT elements - read_16 (fh, NULL); /* unused: was which_side */ read_8 (fh, &FragPtr->captains_name_index); - read_8 (fh, NULL); /* padding */ - read_16 (fh, NULL); /* unused: was ship_flags */ read_8 (fh, &FragPtr->race_id); read_8 (fh, &FragPtr->index); - // XXX: reading crew as BYTE to maintain savegame compatibility - read_8 (fh, &tmpb); - FragPtr->crew_level = tmpb; - read_8 (fh, &tmpb); - FragPtr->max_crew = tmpb; + read_16 (fh, &FragPtr->crew_level); + read_16 (fh, &FragPtr->max_crew); read_8 (fh, &FragPtr->energy_level); read_8 (fh, &FragPtr->max_energy); - read_16 (fh, NULL); /* unused; was loc.x */ - read_16 (fh, NULL); /* unused; was loc.y */ UnlockShipFrag (pQueue, hStarShip); } @@ -227,7 +212,6 @@ LoadRaceQueue (void *fh, QUEUE *pQueue) read_8 (fh, &FleetPtr->func_index); read_16s(fh, &FleetPtr->dest_loc.x); read_16s(fh, &FleetPtr->dest_loc.y); - read_16 (fh, NULL); /* alignment padding */ UnlockFleetInfo (pQueue, hStarShip); } @@ -246,21 +230,15 @@ LoadGroupQueue (void *fh, QUEUE *pQueue) IP_GROUP *GroupPtr; BYTE tmpb; - read_16 (fh, NULL); /* unused; was race_id */ - hGroup = BuildGroup (pQueue, 0); GroupPtr = LockIpGroup (pQueue, hGroup); - read_16 (fh, NULL); /* unused; was which_side */ - read_8 (fh, NULL); /* unused; was captains_name_index */ - read_8 (fh, NULL); /* padding; for savegame compat */ read_16 (fh, &GroupPtr->group_counter); read_8 (fh, &GroupPtr->race_id); read_8 (fh, &tmpb); /* was var2 */ GroupPtr->sys_loc = LONIBBLE (tmpb); GroupPtr->task = HINIBBLE (tmpb); read_8 (fh, &GroupPtr->in_system); /* was crew_level */ - read_8 (fh, NULL); /* unused; was max_crew */ read_8 (fh, &tmpb); /* was energy_level */ GroupPtr->dest_loc = LONIBBLE (tmpb); GroupPtr->orbit_pos = HINIBBLE (tmpb); @@ -278,11 +256,8 @@ LoadEncounter (ENCOUNTER *EncounterPtr, void *fh) COUNT i; BYTE tmpb; - read_ptr (fh); /* useless ptr; HENCOUNTER pred */ EncounterPtr->pred = 0; - read_ptr (fh); /* useless ptr; HENCOUNTER succ */ EncounterPtr->succ = 0; - read_ptr (fh); /* useless ptr; HELEMENT hElement */ EncounterPtr->hElement = 0; read_16s (fh, &EncounterPtr->transition_state); read_16s (fh, &EncounterPtr->origin.x); @@ -292,31 +267,18 @@ LoadEncounter (ENCOUNTER *EncounterPtr, void *fh) read_16s (fh, &EncounterPtr->loc_pt.x); read_16s (fh, &EncounterPtr->loc_pt.y); read_8 (fh, &EncounterPtr->race_id); - read_8 (fh, &tmpb); - EncounterPtr->num_ships = tmpb & ENCOUNTER_SHIPS_MASK; - EncounterPtr->flags = tmpb & ENCOUNTER_FLAGS_MASK; - read_16 (fh, NULL); /* alignment padding */ + read_8 (fh, &EncounterPtr->num_ships); + read_8 (fh, &EncounterPtr->flags); // Load each entry in the BRIEF_SHIP_INFO array for (i = 0; i < MAX_HYPER_SHIPS; i++) { BRIEF_SHIP_INFO *ShipInfo = &EncounterPtr->ShipList[i]; - read_16 (fh, NULL); /* useless; was SHIP_INFO.ship_flags */ read_8 (fh, &ShipInfo->race_id); - read_8 (fh, NULL); /* useless; was SHIP_INFO.var2 */ - // XXX: reading crew as BYTE to maintain savegame compatibility - read_8 (fh, &tmpb); - ShipInfo->crew_level = tmpb; - read_8 (fh, &tmpb); - ShipInfo->max_crew = tmpb; - read_8 (fh, NULL); /* useless; was SHIP_INFO.energy_level */ + read_16 (fh, &ShipInfo->crew_level); + read_16 (fh, &ShipInfo->max_crew); read_8 (fh, &ShipInfo->max_energy); - read_16 (fh, NULL); /* useless; was SHIP_INFO.loc.x */ - read_16 (fh, NULL); /* useless; was SHIP_INFO.loc.y */ - read_32 (fh, NULL); /* useless val; STRING race_strings */ - read_ptr (fh); /* useless ptr; FRAME icons */ - read_ptr (fh); /* useless ptr; FRAME melee_icon */ } // Load the stuff after the BRIEF_SHIP_INFO array @@ -327,37 +289,12 @@ LoadEncounter (ENCOUNTER *EncounterPtr, void *fh) static void LoadEvent (EVENT *EventPtr, void *fh) { - read_ptr (fh); /* useless ptr; HEVENT pred */ EventPtr->pred = 0; - read_ptr (fh); /* useless ptr; HEVENT succ */ EventPtr->succ = 0; read_8 (fh, &EventPtr->day_index); read_8 (fh, &EventPtr->month_index); read_16 (fh, &EventPtr->year_index); read_8 (fh, &EventPtr->func_index); - read_8 (fh, NULL); /* padding */ - read_16 (fh, NULL); /* padding */ -} - -static void -DummyLoadQueue (QUEUE *QueuePtr, void *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 */ - read_ptr (fh); /* HLINK head */ - read_ptr (fh); /* HLINK tail */ - read_ptr (fh); /* BYTE* pq_tab */ - read_ptr (fh); /* HLINK free_list */ - read_16 (fh, NULL); /* MEM_HANDLE hq_tab */ - read_16 (fh, NULL); /* COUNT object_size */ - read_8 (fh, NULL); /* BYTE num_objects */ - - read_8 (fh, NULL); /* padding */ - read_16 (fh, NULL); /* padding */ } static void @@ -368,28 +305,18 @@ LoadClockState (CLOCK_STATE *ClockPtr, void *fh) read_16 (fh, &ClockPtr->year_index); read_16s (fh, &ClockPtr->tick_count); read_16s (fh, &ClockPtr->day_in_ticks); - read_ptr (fh); /* not loading ptr; Semaphore clock_sem */ - read_ptr (fh); /* not loading ptr; Task clock_task */ - read_32 (fh, NULL); /* not loading; DWORD TimeCounter */ - - DummyLoadQueue (&ClockPtr->event_q, fh); } static void LoadGameState (GAME_STATE *GSPtr, void *fh) { - BYTE dummy8; - - read_8 (fh, &dummy8); /* obsolete */ read_8 (fh, &GSPtr->glob_flags); read_8 (fh, &GSPtr->CrewCost); read_8 (fh, &GSPtr->FuelCost); read_a8 (fh, GSPtr->ModuleCost, NUM_MODULES); read_a8 (fh, GSPtr->ElementWorth, NUM_ELEMENT_CATEGORIES); - read_ptr (fh); /* not loading ptr; PRIMITIVE *DisplayArray */ read_16 (fh, &GSPtr->CurrentActivity); - read_16 (fh, NULL); /* CLOCK_STATE alignment padding */ LoadClockState (&GSPtr->GameClock, fh); read_16s (fh, &GSPtr->autopilot.x); @@ -413,20 +340,10 @@ LoadGameState (GAME_STATE *GSPtr, void *fh) read_16s (fh, &GSPtr->velocity.error.height); read_16s (fh, &GSPtr->velocity.incr.width); read_16s (fh, &GSPtr->velocity.incr.height); - read_16 (fh, NULL); /* VELOCITY_DESC padding */ read_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); - read_a8 (fh, GSPtr->GameState, sizeof (GSPtr->GameState)); - - assert (sizeof (GSPtr->GameState) % 4 == 3); - read_8 (fh, NULL); /* GAME_STATE alignment padding */ } static BOOLEAN diff --git a/sc2/src/uqm/save.c b/sc2/src/uqm/save.c index bfaa140f5..8b2791a17 100644 --- a/sc2/src/uqm/save.c +++ b/sc2/src/uqm/save.c @@ -84,12 +84,6 @@ cwrite_32 (SAVEBUF *fh, DWORD v) return 0; } -static inline COUNT -cwrite_ptr (SAVEBUF *fh) -{ - return cwrite_32 (fh, 0); /* ptrs are useless in saves */ -} - static inline COUNT cwrite_a8 (SAVEBUF *fh, const BYTE *ar, COUNT count) { @@ -118,12 +112,6 @@ write_32 (void *fp, DWORD v) return WriteResFile (&v, 4, 1, fp); } -static inline size_t -write_ptr (void *fp) -{ - return write_32 (fp, 0); /* ptrs are useless in saves */ -} - static inline size_t write_a8 (void *fp, const BYTE *ar, COUNT count) { @@ -183,19 +171,13 @@ SaveShipQueue (SAVEBUF *fh, QUEUE *pQueue) cwrite_16 (fh, Index); // Write SHIP_FRAGMENT elements - cwrite_16 (fh, 0); /* unused; was which_side */ cwrite_8 (fh, FragPtr->captains_name_index); - cwrite_8 (fh, 0); /* padding */ - cwrite_16 (fh, 0); /* unused: was ship_flags */ cwrite_8 (fh, FragPtr->race_id); cwrite_8 (fh, FragPtr->index); - // XXX: writing crew as BYTE to maintain savegame compatibility - cwrite_8 (fh, FragPtr->crew_level); - cwrite_8 (fh, FragPtr->max_crew); + cwrite_16 (fh, FragPtr->crew_level); + cwrite_16 (fh, FragPtr->max_crew); cwrite_8 (fh, FragPtr->energy_level); cwrite_8 (fh, FragPtr->max_energy); - cwrite_16 (fh, 0); /* unused; was loc.x */ - cwrite_16 (fh, 0); /* unused; was loc.y */ UnlockShipFrag (pQueue, hStarShip); hStarShip = hNextShip; @@ -245,7 +227,6 @@ SaveRaceQueue (SAVEBUF *fh, QUEUE *pQueue) cwrite_8 (fh, FleetPtr->func_index); cwrite_16 (fh, FleetPtr->dest_loc.x); cwrite_16 (fh, FleetPtr->dest_loc.y); - cwrite_16 (fh, 0); /* alignment padding */ UnlockFleetInfo (pQueue, hFleet); hFleet = hNextFleet; @@ -267,18 +248,12 @@ SaveGroupQueue (SAVEBUF *fh, QUEUE *pQueue) GroupPtr = LockIpGroup (pQueue, hGroup); hNextGroup = _GetSuccLink (GroupPtr); - cwrite_16 (fh, GroupPtr->race_id); /* unused; for old versions */ - - cwrite_16 (fh, 0); /* unused; was which_side */ - cwrite_8 (fh, 0); /* unused; was captains_name_index */ - cwrite_8 (fh, 0); /* padding; for savegame compat */ cwrite_16 (fh, GroupPtr->group_counter); cwrite_8 (fh, GroupPtr->race_id); assert (GroupPtr->sys_loc < 0x10 && GroupPtr->task < 0x10); cwrite_8 (fh, MAKE_BYTE (GroupPtr->sys_loc, GroupPtr->task)); /* was var2 */ cwrite_8 (fh, GroupPtr->in_system); /* was crew_level */ - cwrite_8 (fh, 0); /* unused; was max_crew */ assert (GroupPtr->dest_loc < 0x10 && GroupPtr->orbit_pos < 0x10); cwrite_8 (fh, MAKE_BYTE (GroupPtr->dest_loc, GroupPtr->orbit_pos)); /* was energy_level */ @@ -295,9 +270,6 @@ SaveEncounter (const ENCOUNTER *EncounterPtr, SAVEBUF *fh) { COUNT i; - cwrite_ptr (fh); /* useless ptr; HENCOUNTER pred */ - cwrite_ptr (fh); /* useless ptr; HENCOUNTER succ */ - cwrite_ptr (fh); /* useless ptr; HELEMENT hElement */ cwrite_16 (fh, EncounterPtr->transition_state); cwrite_16 (fh, EncounterPtr->origin.x); cwrite_16 (fh, EncounterPtr->origin.y); @@ -306,29 +278,18 @@ SaveEncounter (const ENCOUNTER *EncounterPtr, SAVEBUF *fh) cwrite_16 (fh, EncounterPtr->loc_pt.x); cwrite_16 (fh, EncounterPtr->loc_pt.y); cwrite_8 (fh, EncounterPtr->race_id); - // XXX: writing combined fields to maintain savegame compatibility - cwrite_8 (fh, (EncounterPtr->num_ships & ENCOUNTER_SHIPS_MASK) - | (EncounterPtr->flags & ENCOUNTER_FLAGS_MASK)); - cwrite_16 (fh, 0); /* alignment padding */ + cwrite_8 (fh, EncounterPtr->num_ships); + cwrite_8 (fh, EncounterPtr->flags); // Save each entry in the BRIEF_SHIP_INFO array for (i = 0; i < MAX_HYPER_SHIPS; i++) { const BRIEF_SHIP_INFO *ShipInfo = &EncounterPtr->ShipList[i]; - cwrite_16 (fh, 0); /* useless; was SHIP_INFO.ship_flags */ cwrite_8 (fh, ShipInfo->race_id); - cwrite_8 (fh, 0); /* useless; was SHIP_INFO.var2 */ - // XXX: writing crew as BYTE to maintain savegame compatibility - cwrite_8 (fh, ShipInfo->crew_level); - cwrite_8 (fh, ShipInfo->max_crew); - cwrite_8 (fh, 0); /* useless; was SHIP_INFO.energy_level */ + cwrite_16 (fh, ShipInfo->crew_level); + cwrite_16 (fh, ShipInfo->max_crew); cwrite_8 (fh, ShipInfo->max_energy); - cwrite_16 (fh, 0); /* useless; was SHIP_INFO.loc.x */ - cwrite_16 (fh, 0); /* useless; was SHIP_INFO.loc.y */ - cwrite_32 (fh, 0); /* useless val; STRING race_strings */ - cwrite_ptr (fh); /* useless ptr; FRAME icons */ - cwrite_ptr (fh); /* useless ptr; FRAME melee_icon */ } // Save the stuff after the BRIEF_SHIP_INFO array @@ -339,35 +300,10 @@ SaveEncounter (const ENCOUNTER *EncounterPtr, SAVEBUF *fh) static void SaveEvent (const EVENT *EventPtr, SAVEBUF *fh) { - cwrite_ptr (fh); /* useless ptr; HEVENT pred */ - cwrite_ptr (fh); /* useless ptr; HEVENT succ */ cwrite_8 (fh, EventPtr->day_index); cwrite_8 (fh, EventPtr->month_index); cwrite_16 (fh, EventPtr->year_index); cwrite_8 (fh, EventPtr->func_index); - cwrite_8 (fh, 0); /* padding */ - cwrite_16 (fh, 0); /* padding */ -} - -static void -DummySaveQueue (const QUEUE *QueuePtr, SAVEBUF *fh) -{ - /* QUEUE should never actually be saved since it contains - * purely internal representation and the lists - * involved are actually saved separately */ - (void)QueuePtr; /* silence compiler */ - - /* QUEUE format with QUEUE_TABLE defined -- UQM default */ - cwrite_ptr (fh); /* HLINK head */ - cwrite_ptr (fh); /* HLINK tail */ - cwrite_ptr (fh); /* BYTE* pq_tab */ - cwrite_ptr (fh); /* HLINK free_list */ - cwrite_16 (fh, 0); /* MEM_HANDLE hq_tab */ - cwrite_16 (fh, 0); /* COUNT object_size */ - cwrite_8 (fh, 0); /* BYTE num_objects */ - - cwrite_8 (fh, 0); /* padding */ - cwrite_16 (fh, 0); /* padding */ } static void @@ -378,26 +314,18 @@ SaveClockState (const CLOCK_STATE *ClockPtr, SAVEBUF *fh) cwrite_16 (fh, ClockPtr->year_index); cwrite_16 (fh, ClockPtr->tick_count); cwrite_16 (fh, ClockPtr->day_in_ticks); - cwrite_ptr (fh); /* useless ptr; Semaphore clock_sem */ - cwrite_ptr (fh); /* useless ptr; Task clock_task */ - cwrite_32 (fh, 0); /* useless value; DWORD TimeCounter */ - - DummySaveQueue (&ClockPtr->event_q, fh); } static void SaveGameState (const GAME_STATE *GSPtr, SAVEBUF *fh) { - cwrite_8 (fh, 0); /* obsolete; BYTE cur_state */ cwrite_8 (fh, GSPtr->glob_flags); cwrite_8 (fh, GSPtr->CrewCost); cwrite_8 (fh, GSPtr->FuelCost); cwrite_a8 (fh, GSPtr->ModuleCost, NUM_MODULES); cwrite_a8 (fh, GSPtr->ElementWorth, NUM_ELEMENT_CATEGORIES); - cwrite_ptr (fh); /* useless ptr; PRIMITIVE *DisplayArray */ cwrite_16 (fh, GSPtr->CurrentActivity); - cwrite_16 (fh, 0); /* CLOCK_STATE alignment padding */ SaveClockState (&GSPtr->GameClock, fh); cwrite_16 (fh, GSPtr->autopilot.x); @@ -421,20 +349,10 @@ SaveGameState (const GAME_STATE *GSPtr, SAVEBUF *fh) cwrite_16 (fh, GSPtr->velocity.error.height); cwrite_16 (fh, GSPtr->velocity.incr.width); cwrite_16 (fh, GSPtr->velocity.incr.height); - cwrite_16 (fh, 0); /* VELOCITY_DESC padding */ cwrite_32 (fh, GSPtr->BattleGroupRef); - DummySaveQueue (&GSPtr->avail_race_q, fh); - DummySaveQueue (&GSPtr->npc_built_ship_q, fh); - // Not saving ip_group_q, was not there originally - DummySaveQueue (&GSPtr->encounter_q, fh); - DummySaveQueue (&GSPtr->built_ship_q, fh); - cwrite_a8 (fh, GSPtr->GameState, sizeof (GSPtr->GameState)); - - assert (sizeof (GSPtr->GameState) % 4 == 3); - cwrite_8 (fh, 0); /* GAME_STATE alignment padding */ } static BOOLEAN @@ -469,7 +387,7 @@ SaveSummary (const SUMMARY_DESC *SummPtr, void *fp) if ( write_32 (fp, SAVE_MAGIC) != 1 || write_32 (fp, SUMMARY_MAGIC) != 1 || - write_32 (fp, 161 + strlen(SummPtr->SaveName)) != 1 + write_32 (fp, 160 + strlen(SummPtr->SaveName)) != 1 ) return FALSE; if (!SaveSisState (&SummPtr->SS, fp)) @@ -487,7 +405,7 @@ SaveSummary (const SUMMARY_DESC *SummPtr, void *fp) write_8 (fp, SummPtr->NumDevices) != 1 || write_a8 (fp, SummPtr->ShipList, MAX_BUILT_SHIPS) != 1 || write_a8 (fp, SummPtr->DeviceList, MAX_EXCLUSIVE_DEVICES) != 1 || - write_a8 (fp, SummPtr->SaveName, strlen(SummPtr->SaveName)+1) != 1 + write_a8 (fp, SummPtr->SaveName, strlen(SummPtr->SaveName)) != 1 ) return FALSE; else @@ -667,31 +585,16 @@ SaveFlagshipState (void) BOOLEAN SaveGame (COUNT which_game, SUMMARY_DESC *SummPtr, const char *name) { - BOOLEAN success, made_room; + BOOLEAN success; void *out_fp, *h; SAVEBUF fh_backing, *fh; success = TRUE; - made_room = FALSE; fh = &fh_backing; -RetrySave: h = HMalloc (128 * 1024); if (h == 0) { - if (success) - { - success = FALSE; - made_room = TRUE; - HFree (h); - - FreeSC2Data (); - log_add (log_Debug, "Insufficient room for save buffers" - " -- RETRYING"); - goto RetrySave; - } - else - log_add (log_Debug, "Insufficient room for save buffers" - " -- GIVING UP!"); + log_add (log_Debug, "Insufficient room for save buffers"); } else { @@ -742,7 +645,6 @@ RetrySave: // from group state files. But the original code did, // and so will we until we can prove we do not need to. SaveGroupQueue (fh, &GLOBAL (ip_group_q)); - //SaveEmptyQueue (fh); else // XXX: empty queue write-out is only needed to maintain // the savegame compatibility @@ -802,7 +704,6 @@ RetrySave: { flen = LengthStateFile (fp); // Write the uncompressed size. - printf ("Star Info: %u bytes\n", flen); cwrite_32 (fh, flen); while (flen) { @@ -823,7 +724,6 @@ RetrySave: { flen = LengthStateFile (fp); // Write the uncompressed size. - printf ("Defined Group Info: %u bytes\n", flen); cwrite_32 (fh, flen); while (flen) { @@ -844,7 +744,6 @@ RetrySave: { flen = LengthStateFile (fp); // Write the uncompressed size. - printf ("Random Group Info: %u bytes\n", flen); cwrite_32 (fh, flen); while (flen) { @@ -867,7 +766,7 @@ RetrySave: // Write the memory file to the actual savegame file. sprintf (file, "uqmsave.%02u", which_game); log_add (log_Debug, "'%s' is %u bytes long", file, - flen + 181 + strlen(SummPtr->SaveName)); + flen + 180 + strlen(SummPtr->SaveName)); if (flen && (out_fp = res_OpenResFile (saveDir, file, "wb"))) { PrepareSummary (SummPtr, name); @@ -894,8 +793,5 @@ RetrySave: HFree (h); - if (made_room) - LoadSC2Data (); - return (success); } From eca23397c3ecfa62b02e67d0bd2ac39d778d224c Mon Sep 17 00:00:00 2001 From: Michael Martin Date: Wed, 9 Oct 2013 00:34:35 -0700 Subject: [PATCH 08/22] Output bytes now track field sizes. This removes the clumsy local variable pointer stuff. --- sc2/src/uqm/load.c | 30 +++++++++++------------------- sc2/src/uqm/save.c | 20 +++++++++----------- sc2/src/uqm/save.h | 8 +++++--- 3 files changed, 25 insertions(+), 33 deletions(-) diff --git a/sc2/src/uqm/load.c b/sc2/src/uqm/load.c index d7788c8e0..14234ee3c 100644 --- a/sc2/src/uqm/load.c +++ b/sc2/src/uqm/load.c @@ -152,7 +152,6 @@ LoadShipQueue (void *fh, QUEUE *pQueue) HSHIPFRAG hStarShip; SHIP_FRAGMENT *FragPtr; COUNT Index; - BYTE tmpb; read_16 (fh, &Index); @@ -184,7 +183,6 @@ LoadRaceQueue (void *fh, QUEUE *pQueue) HFLEETINFO hStarShip; FLEET_INFO *FleetPtr; COUNT Index; - BYTE tmpb; read_16 (fh, &Index); @@ -195,10 +193,8 @@ LoadRaceQueue (void *fh, QUEUE *pQueue) read_16 (fh, &FleetPtr->allied_state); read_8 (fh, &FleetPtr->days_left); read_8 (fh, &FleetPtr->growth_fract); - read_8 (fh, &tmpb); - FleetPtr->crew_level = tmpb; - read_8 (fh, &tmpb); - FleetPtr->max_crew = tmpb; + read_16 (fh, &FleetPtr->crew_level); + read_16 (fh, &FleetPtr->max_crew); read_8 (fh, &FleetPtr->growth); read_8 (fh, &FleetPtr->max_energy); read_16s(fh, &FleetPtr->loc.x); @@ -228,20 +224,17 @@ LoadGroupQueue (void *fh, QUEUE *pQueue) { HIPGROUP hGroup; IP_GROUP *GroupPtr; - BYTE tmpb; hGroup = BuildGroup (pQueue, 0); GroupPtr = LockIpGroup (pQueue, hGroup); read_16 (fh, &GroupPtr->group_counter); read_8 (fh, &GroupPtr->race_id); - read_8 (fh, &tmpb); /* was var2 */ - GroupPtr->sys_loc = LONIBBLE (tmpb); - GroupPtr->task = HINIBBLE (tmpb); + read_8 (fh, &GroupPtr->sys_loc); + read_8 (fh, &GroupPtr->task); read_8 (fh, &GroupPtr->in_system); /* was crew_level */ - read_8 (fh, &tmpb); /* was energy_level */ - GroupPtr->dest_loc = LONIBBLE (tmpb); - GroupPtr->orbit_pos = HINIBBLE (tmpb); + read_8 (fh, &GroupPtr->dest_loc); + read_8 (fh, &GroupPtr->orbit_pos); read_8 (fh, &GroupPtr->group_id); /* was max_energy */ read_16s(fh, &GroupPtr->loc.x); read_16s(fh, &GroupPtr->loc.y); @@ -254,7 +247,6 @@ static void LoadEncounter (ENCOUNTER *EncounterPtr, void *fh) { COUNT i; - BYTE tmpb; EncounterPtr->pred = 0; EncounterPtr->succ = 0; @@ -378,11 +370,11 @@ LoadSummary (SUMMARY_DESC *SummPtr, void *fp) DWORD nameSize = 0; if (!read_32s (fp, &magic)) return FALSE; - if (magic == SAVE_MAGIC) + if (magic == SAVEFILE_TAG) { - if (read_32 (fp, &magic) != 1 || magic != SUMMARY_MAGIC) + if (read_32 (fp, &magic) != 1 || magic != SUMMARY_TAG) return FALSE; - if (read_32 (fp, &magic) != 1 || magic < 161) + if (read_32 (fp, &magic) != 1 || magic < 160) return FALSE; nameSize = magic - 160; } @@ -475,7 +467,7 @@ LoadGame (COUNT which_game, SUMMARY_DESC *SummPtr) GlobData.SIS_state = SummPtr->SS; chunk = 0; - while (chunk != OMNIBUS_MAGIC) + while (chunk != OMNIBUS_TAG) { if (read_32(in_fp, &chunk) != 1) { @@ -487,7 +479,7 @@ LoadGame (COUNT which_game, SUMMARY_DESC *SummPtr) res_CloseResFile (in_fp); return FALSE; } - if (chunk == OMNIBUS_MAGIC) + if (chunk == OMNIBUS_TAG) break; log_add (log_Debug, "Skipping chunk of tag %08X (size %u)", chunk, chunkSize); diff --git a/sc2/src/uqm/save.c b/sc2/src/uqm/save.c index 8b2791a17..27505e4cb 100644 --- a/sc2/src/uqm/save.c +++ b/sc2/src/uqm/save.c @@ -212,8 +212,8 @@ SaveRaceQueue (SAVEBUF *fh, QUEUE *pQueue) cwrite_16 (fh, FleetPtr->allied_state); cwrite_8 (fh, FleetPtr->days_left); cwrite_8 (fh, FleetPtr->growth_fract); - cwrite_8 (fh, FleetPtr->crew_level); - cwrite_8 (fh, FleetPtr->max_crew); + cwrite_16 (fh, FleetPtr->crew_level); + cwrite_16 (fh, FleetPtr->max_crew); cwrite_8 (fh, FleetPtr->growth); cwrite_8 (fh, FleetPtr->max_energy); cwrite_16 (fh, FleetPtr->loc.x); @@ -250,13 +250,11 @@ SaveGroupQueue (SAVEBUF *fh, QUEUE *pQueue) cwrite_16 (fh, GroupPtr->group_counter); cwrite_8 (fh, GroupPtr->race_id); - assert (GroupPtr->sys_loc < 0x10 && GroupPtr->task < 0x10); - cwrite_8 (fh, MAKE_BYTE (GroupPtr->sys_loc, GroupPtr->task)); - /* was var2 */ + cwrite_8 (fh, GroupPtr->sys_loc); + cwrite_8 (fh, GroupPtr->task); cwrite_8 (fh, GroupPtr->in_system); /* was crew_level */ - assert (GroupPtr->dest_loc < 0x10 && GroupPtr->orbit_pos < 0x10); - cwrite_8 (fh, MAKE_BYTE (GroupPtr->dest_loc, GroupPtr->orbit_pos)); - /* was energy_level */ + cwrite_8 (fh, GroupPtr->dest_loc); + cwrite_8 (fh, GroupPtr->orbit_pos); cwrite_8 (fh, GroupPtr->group_id); /* was max_energy */ cwrite_16 (fh, GroupPtr->loc.x); cwrite_16 (fh, GroupPtr->loc.y); @@ -385,8 +383,8 @@ static BOOLEAN SaveSummary (const SUMMARY_DESC *SummPtr, void *fp) { if ( - write_32 (fp, SAVE_MAGIC) != 1 || - write_32 (fp, SUMMARY_MAGIC) != 1 || + write_32 (fp, SAVEFILE_TAG) != 1 || + write_32 (fp, SUMMARY_TAG) != 1 || write_32 (fp, 160 + strlen(SummPtr->SaveName)) != 1 ) return FALSE; @@ -773,7 +771,7 @@ SaveGame (COUNT which_game, SUMMARY_DESC *SummPtr, const char *name) success = SaveSummary (SummPtr, out_fp); // Then write the rest of the data. - if (success && write_32 (out_fp, OMNIBUS_MAGIC) != 1) + if (success && write_32 (out_fp, OMNIBUS_TAG) != 1) success = FALSE; if (success && write_32 (out_fp, flen) != 1) success = FALSE; diff --git a/sc2/src/uqm/save.h b/sc2/src/uqm/save.h index 62602eb90..d96669c63 100644 --- a/sc2/src/uqm/save.h +++ b/sc2/src/uqm/save.h @@ -31,11 +31,13 @@ extern "C" { // is only used for displaying savegame summaries. There is also // room for only 16 devices on screen. #define MAX_EXCLUSIVE_DEVICES 16 -#define SAVE_MAGIC 0x01534d55 // "UMS\x01": UQM Save version 1 -#define SUMMARY_MAGIC 0x6d6d7553 // "Summ": Summary. Must be first! -#define OMNIBUS_MAGIC 0x696e6d4f // "Omni": All data, uncompressed #define SAVE_NAME_SIZE 64 +// The savefile tag numbers. +#define SAVEFILE_TAG 0x01534d55 // "UMS\x01": UQM Save version 1 +#define OMNIBUS_TAG 0x696e6d4f // "Omni": All data, uncompressed +#define SUMMARY_TAG 0x6d6d7553 // "Summ": Summary. Must be first! + typedef struct { SIS_STATE SS; From 34dab83ff60ce0400060a90439468e962446d1bf Mon Sep 17 00:00:00 2001 From: Serge van den Boom Date: Wed, 9 Oct 2013 18:42:04 +0200 Subject: [PATCH 09/22] Warn the reader that this file is outdated, with a referral to doc/devel/resources. --- tools/resmap/README.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/resmap/README.txt b/tools/resmap/README.txt index 29b79ffd3..2ffa19688 100644 --- a/tools/resmap/README.txt +++ b/tools/resmap/README.txt @@ -3,6 +3,11 @@ game, as well as both integer and string names for each one. Running gen_resfiles.py will cause it to regenerate uqm.rmp, all the .ls2 files, and the i*.h source headers that produce the #defines. +XXX The information below is out of date. See the section 'Updating +the core resources' in sc2/doc/devel/resources for an up-to-date +description of the contents of the resources.csv file. + + The .csv file itself is a comma-separated-values list with nine fields: From 7138aeca64840a107843265aaeb83717b57a5d89 Mon Sep 17 00:00:00 2001 From: Michael Martin Date: Sun, 13 Oct 2013 05:07:04 -0700 Subject: [PATCH 10/22] Major revision to save format This accomplishes what I set out to do, but doesn't actually solve the problem well enough given what I learned while doing it. Documentation forthcoming in future commit. --- sc2/src/uqm/load.c | 317 +++++++++----------- sc2/src/uqm/save.c | 733 ++++++++++++++++++++------------------------- sc2/src/uqm/save.h | 13 +- 3 files changed, 474 insertions(+), 589 deletions(-) diff --git a/sc2/src/uqm/load.c b/sc2/src/uqm/load.c index 14234ee3c..4a4953c62 100644 --- a/sc2/src/uqm/load.c +++ b/sc2/src/uqm/load.c @@ -126,26 +126,9 @@ read_a16 (void *fp, UWORD *ar, COUNT count) } static void -LoadEmptyQueue (void *fh) +LoadShipQueue (void *fh, QUEUE *pQueue, DWORD size) { - COUNT num_links; - - read_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 (void *fh, QUEUE *pQueue) -{ - COUNT num_links; - - read_16 (fh, &num_links); + COUNT num_links = size / 11; while (num_links--) { @@ -172,11 +155,9 @@ LoadShipQueue (void *fh, QUEUE *pQueue) } static void -LoadRaceQueue (void *fh, QUEUE *pQueue) +LoadRaceQueue (void *fh, QUEUE *pQueue, DWORD size) { - COUNT num_links; - - read_16 (fh, &num_links); + COUNT num_links = size / 30; while (num_links--) { @@ -214,11 +195,9 @@ LoadRaceQueue (void *fh, QUEUE *pQueue) } static void -LoadGroupQueue (void *fh, QUEUE *pQueue) +LoadGroupQueue (void *fh, QUEUE *pQueue, DWORD size) { - COUNT num_links; - - read_16 (fh, &num_links); + COUNT num_links = size / 13; while (num_links--) { @@ -299,9 +278,21 @@ LoadClockState (CLOCK_STATE *ClockPtr, void *fh) read_16s (fh, &ClockPtr->day_in_ticks); } -static void +static BOOLEAN LoadGameState (GAME_STATE *GSPtr, void *fh) { + DWORD magic; + read_32 (fh, &magic); + if (magic != GLOBAL_STATE_TAG) + { + return FALSE; + } + read_32 (fh, &magic); + if (magic != 79) + { + /* Chunk is the wrong size. */ + return FALSE; + } read_8 (fh, &GSPtr->glob_flags); read_8 (fh, &GSPtr->CrewCost); read_8 (fh, &GSPtr->FuelCost); @@ -335,7 +326,23 @@ LoadGameState (GAME_STATE *GSPtr, void *fh) read_32 (fh, &GSPtr->BattleGroupRef); - read_a8 (fh, GSPtr->GameState, sizeof (GSPtr->GameState)); + read_32 (fh, &magic); + if (magic != GAME_STATE_TAG) + { + return FALSE; + } + memset (GSPtr->GameState, 0, sizeof (GSPtr->GameState)); + read_32 (fh, &magic); + if (magic > sizeof (GSPtr->GameState)) + { + read_a8 (fh, GSPtr->GameState, sizeof (GSPtr->GameState)); + skip_8 (fh, magic - sizeof (GSPtr->GameState)); + } + else + { + read_a8 (fh, GSPtr->GameState, magic); + } + return TRUE; } static BOOLEAN @@ -429,14 +436,33 @@ LoadStarDesc (STAR_DESC *SDPtr, void *fh) read_8 (fh, &SDPtr->Postfix); } +static void +LoadStateFile (int file_type, void *fh, DWORD flen) +{ + GAME_STATE_FILE *fp = OpenStateFile (file_type, "wb"); + char buf[256]; + if (fp) + { + while (flen) + { + COUNT num_bytes; + + num_bytes = flen >= sizeof (buf) ? sizeof (buf) : (COUNT)flen; + read_a8 (fh, buf, num_bytes); + WriteStateFile (buf, num_bytes, 1, fp); + + flen -= num_bytes; + } + CloseStateFile (fp); + } +} + BOOLEAN LoadGame (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; COUNT num_links; STAR_DESC SD; ACTIVITY Activity; @@ -466,27 +492,6 @@ LoadGame (COUNT which_game, SUMMARY_DESC *SummPtr) GlobData.SIS_state = SummPtr->SS; - chunk = 0; - while (chunk != OMNIBUS_TAG) - { - if (read_32(in_fp, &chunk) != 1) - { - res_CloseResFile (in_fp); - return FALSE; - } - if (read_32(in_fp, &chunkSize) != 1) - { - res_CloseResFile (in_fp); - return FALSE; - } - if (chunk == OMNIBUS_TAG) - break; - - log_add (log_Debug, "Skipping chunk of tag %08X (size %u)", chunk, chunkSize); - if (skip_8(in_fp, chunkSize) != 1) - return FALSE; - } - ReinitQueue (&GLOBAL (GameClock.event_q)); ReinitQueue (&GLOBAL (encounter_q)); ReinitQueue (&GLOBAL (ip_group_q)); @@ -495,140 +500,104 @@ LoadGame (COUNT which_game, SUMMARY_DESC *SummPtr) memset (&GLOBAL (GameState[0]), 0, sizeof (GLOBAL (GameState))); Activity = GLOBAL (CurrentActivity); - LoadGameState (&GlobData.Game_state, in_fp); + if (!LoadGameState (&GlobData.Game_state, in_fp)) + { + res_CloseResFile (in_fp); + return FALSE; + } NextActivity = GLOBAL (CurrentActivity); GLOBAL (CurrentActivity) = Activity; - LoadRaceQueue (in_fp, &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)) + chunk = 0; + while (TRUE) { - if (NextActivity & START_ENCOUNTER) - LoadShipQueue (in_fp, &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 (in_fp, &GLOBAL (ip_group_q)); - else - // XXX: The empty queue read is only needed to maintain - // the savegame compatibility - LoadEmptyQueue (in_fp); - } - LoadShipQueue (in_fp, &GLOBAL (built_ship_q)); - - // Load the game events (compressed) - read_16 (in_fp, &num_links); - { -#ifdef DEBUG_LOAD - log_add (log_Debug, "EVENTS:"); -#endif /* DEBUG_LOAD */ - while (num_links--) + if (read_32(in_fp, &chunk) != 1) { - HEVENT hEvent; - EVENT *EventPtr; + break; + } + if (read_32(in_fp, &chunkSize) != 1) + { + res_CloseResFile (in_fp); + return FALSE; + } + switch (chunk) + { + case RACE_Q_TAG: + LoadRaceQueue (in_fp, &GLOBAL (avail_race_q), chunkSize); + break; + case IP_GRP_Q_TAG: + LoadGroupQueue (in_fp, &GLOBAL (ip_group_q), chunkSize); + break; + case ENCOUNTERS_TAG: + num_links = chunkSize / 65; + while (num_links--) + { + HENCOUNTER hEncounter; + ENCOUNTER *EncounterPtr; - hEvent = AllocEvent (); - LockEvent (hEvent, &EventPtr); + hEncounter = AllocEncounter (); + LockEncounter (hEncounter, &EncounterPtr); - LoadEvent (EventPtr, in_fp); + LoadEncounter (EncounterPtr, in_fp); + + UnlockEncounter (hEncounter); + PutEncounter (hEncounter); + } + break; + case EVENTS_TAG: + num_links = chunkSize / 5; +#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, in_fp); #ifdef DEBUG_LOAD - log_add (log_Debug, "\t%u/%u/%u -- %u", - EventPtr->month_index, - EventPtr->day_index, - EventPtr->year_index, - EventPtr->func_index); + 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); + UnlockEvent (hEvent); + PutEvent (hEvent); + } + break; + case STAR_TAG: + LoadStarDesc (&SD, in_fp); + break; + case NPC_SHIP_Q_TAG: + LoadShipQueue (in_fp, &GLOBAL (npc_built_ship_q), chunkSize); + break; + case SHIP_Q_TAG: + LoadShipQueue (in_fp, &GLOBAL (built_ship_q), chunkSize); + break; + case STAR_SF_TAG: + LoadStateFile (STARINFO_FILE, in_fp, chunkSize); + break; + case DEFGRP_SF_TAG: + LoadStateFile (DEFGRPINFO_FILE, in_fp, chunkSize); + break; + case RANDGRP_SF_TAG: + LoadStateFile (RANDGRPINFO_FILE, in_fp, chunkSize); + break; + default: + log_add (log_Debug, "Skipping chunk of tag %08X (size %u)", chunk, chunkSize); + if (skip_8(in_fp, chunkSize) != 1) + { + res_CloseResFile (in_fp); + return FALSE; + } + break; } } - - // Load the encounters (black globes in HS/QS (compressed)) - read_16 (in_fp, &num_links); - { - while (num_links--) - { - HENCOUNTER hEncounter; - ENCOUNTER *EncounterPtr; - - hEncounter = AllocEncounter (); - LockEncounter (hEncounter, &EncounterPtr); - - LoadEncounter (EncounterPtr, in_fp); - - UnlockEncounter (hEncounter); - PutEncounter (hEncounter); - } - } - - // Copy the star info file from the compressed stream - fp = OpenStateFile (STARINFO_FILE, "wb"); - if (fp) - { - DWORD flen; - - read_32 (in_fp, &flen); - while (flen) - { - COUNT num_bytes; - - num_bytes = flen >= sizeof (buf) ? sizeof (buf) : (COUNT)flen; - read_a8 (in_fp, buf, num_bytes); - 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; - - read_32 (in_fp, &flen); - while (flen) - { - COUNT num_bytes; - - num_bytes = flen >= sizeof (buf) ? sizeof (buf) : (COUNT)flen; - read_a8 (in_fp, buf, num_bytes); - 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; - - read_32 (in_fp, &flen); - while (flen) - { - COUNT num_bytes; - - num_bytes = flen >= sizeof (buf) ? sizeof (buf) : (COUNT)flen; - read_a8 (in_fp, buf, num_bytes); - WriteStateFile (buf, num_bytes, 1, fp); - - flen -= num_bytes; - } - CloseStateFile (fp); - } - - LoadStarDesc (&SD, in_fp); - res_CloseResFile (in_fp); EncounterGroup = 0; diff --git a/sc2/src/uqm/save.c b/sc2/src/uqm/save.c index 27505e4cb..51fc56f9c 100644 --- a/sc2/src/uqm/save.c +++ b/sc2/src/uqm/save.c @@ -42,118 +42,78 @@ #include "libs/log.h" #include "libs/memlib.h" -typedef struct savebuf_struct -{ - char *buf; - size_t bufsize; - int index, error; -} SAVEBUF; +// Status boolean. If for some insane reason you need to +// save games in different threads, you'll need to +// protect your calls to SaveGame with a mutex. + +// It's arguably over-paranoid to check for error on +// every single write, but this preserves the older +// behavior. + +static BOOLEAN io_ok = TRUE; // XXX: these should handle endian conversions later -static inline COUNT -cwrite_8 (SAVEBUF *fh, BYTE v) -{ - if (!fh) - return 0; - if (!fh->error && fh->index < fh->bufsize) - { - fh->buf[fh->index++] = v; - return 1; - } - fh->error = 1; - return 0; -} - -static inline COUNT -cwrite_16 (SAVEBUF *fh, UWORD v) -{ - if (cwrite_8 (fh, (BYTE)(v & 0xFF)) && - cwrite_8 (fh, (BYTE)((v >> 8) & 0xFF))) - return 1; - return 0; -} - -static inline COUNT -cwrite_32 (SAVEBUF *fh, DWORD v) -{ - if (cwrite_8 (fh, (BYTE)(v & 0xFF)) && - cwrite_8 (fh, (BYTE)((v >> 8) & 0xFF)) && - cwrite_8 (fh, (BYTE)((v >> 16) & 0xFF)) && - cwrite_8 (fh, (BYTE)((v >> 24) & 0xFF))) - return 1; - return 0; -} - -static inline COUNT -cwrite_a8 (SAVEBUF *fh, const BYTE *ar, COUNT count) -{ - int i; - for (i = 0; i < count; ++i) - if (!cwrite_8 (fh, ar[i])) - return 0; - return 1; -} - -static inline size_t +static inline void write_8 (void *fp, BYTE v) { - return WriteResFile (&v, 1, 1, fp); + if (io_ok) + if (WriteResFile (&v, 1, 1, fp) != 1) + io_ok = FALSE; } -static inline size_t +static inline void write_16 (void *fp, UWORD v) { - return WriteResFile (&v, 2, 1, fp); + if (io_ok) + if (WriteResFile (&v, 2, 1, fp) != 1) + io_ok = FALSE; } -static inline size_t +static inline void write_32 (void *fp, DWORD v) { - return WriteResFile (&v, 4, 1, fp); + if (io_ok) + if (WriteResFile (&v, 4, 1, fp) != 1) + io_ok = FALSE; } -static inline size_t +static inline void write_a8 (void *fp, const BYTE *ar, COUNT count) { - return WriteResFile (ar, 1, count, fp) == count; + if (io_ok) + if (WriteResFile (ar, 1, count, fp) != count) + io_ok = FALSE; } -static inline size_t +static inline void write_str (void *fp, const char *str, COUNT count) { // no type conversion needed for strings - return write_a8 (fp, (const BYTE *)str, count); + write_a8 (fp, (const BYTE *)str, count); } -static inline size_t +static inline void write_a16 (void *fp, const UWORD *ar, COUNT count) { for ( ; count > 0; --count, ++ar) { - if (write_16 (fp, *ar) != 1) - return 0; + if (!io_ok) + break; + write_16 (fp, *ar); } - return 1; } static void -SaveEmptyQueue (SAVEBUF *fh) -{ - COUNT num_links = 0; - - // Write the number of entries in the queue. - cwrite_16 (fh, num_links); -} - -static void -SaveShipQueue (SAVEBUF *fh, QUEUE *pQueue) +SaveShipQueue (uio_Stream *fh, QUEUE *pQueue, DWORD tag) { COUNT num_links; HSHIPFRAG hStarShip; - // Write the number of entries in the queue. num_links = CountLinks (pQueue); - cwrite_16 (fh, num_links); + if (num_links == 0) + return; + write_32 (fh, tag); + write_32 (fh, num_links * 11); // Size of chunk: each entry is 11 bytes long. hStarShip = GetHeadLink (pQueue); while (num_links--) @@ -168,16 +128,16 @@ SaveShipQueue (SAVEBUF *fh, QUEUE *pQueue) Index = FragPtr->race_id; // Write the number identifying this ship type. // See races.h; look for the enum containing NUM_AVAILABLE_RACES. - cwrite_16 (fh, Index); + write_16 (fh, Index); // Write SHIP_FRAGMENT elements - cwrite_8 (fh, FragPtr->captains_name_index); - cwrite_8 (fh, FragPtr->race_id); - cwrite_8 (fh, FragPtr->index); - cwrite_16 (fh, FragPtr->crew_level); - cwrite_16 (fh, FragPtr->max_crew); - cwrite_8 (fh, FragPtr->energy_level); - cwrite_8 (fh, FragPtr->max_energy); + write_8 (fh, FragPtr->captains_name_index); + write_8 (fh, FragPtr->race_id); + write_8 (fh, FragPtr->index); + write_16 (fh, FragPtr->crew_level); + write_16 (fh, FragPtr->max_crew); + write_8 (fh, FragPtr->energy_level); + write_8 (fh, FragPtr->max_energy); UnlockShipFrag (pQueue, hStarShip); hStarShip = hNextShip; @@ -185,14 +145,17 @@ SaveShipQueue (SAVEBUF *fh, QUEUE *pQueue) } static void -SaveRaceQueue (SAVEBUF *fh, QUEUE *pQueue) +SaveRaceQueue (uio_Stream *fh, QUEUE *pQueue) { COUNT num_links; HFLEETINFO hFleet; - // Write the number of entries in the queue. num_links = CountLinks (pQueue); - cwrite_16 (fh, num_links); + if (num_links == 0) + return; + write_32 (fh, RACE_Q_TAG); + // Write chunk size: 30 bytes per entry + write_32 (fh, num_links * 30); hFleet = GetHeadLink (pQueue); while (num_links--) @@ -206,27 +169,27 @@ SaveRaceQueue (SAVEBUF *fh, QUEUE *pQueue) Index = GetIndexFromStarShip (pQueue, hFleet); // The index is the position in the queue. - cwrite_16 (fh, Index); + write_16 (fh, Index); // Write FLEET_INFO elements - cwrite_16 (fh, FleetPtr->allied_state); - cwrite_8 (fh, FleetPtr->days_left); - cwrite_8 (fh, FleetPtr->growth_fract); - cwrite_16 (fh, FleetPtr->crew_level); - cwrite_16 (fh, FleetPtr->max_crew); - cwrite_8 (fh, FleetPtr->growth); - cwrite_8 (fh, FleetPtr->max_energy); - cwrite_16 (fh, FleetPtr->loc.x); - cwrite_16 (fh, FleetPtr->loc.y); + write_16 (fh, FleetPtr->allied_state); + write_8 (fh, FleetPtr->days_left); + write_8 (fh, FleetPtr->growth_fract); + write_16 (fh, FleetPtr->crew_level); + write_16 (fh, FleetPtr->max_crew); + write_8 (fh, FleetPtr->growth); + write_8 (fh, FleetPtr->max_energy); + write_16 (fh, FleetPtr->loc.x); + write_16 (fh, FleetPtr->loc.y); - cwrite_16 (fh, FleetPtr->actual_strength); - cwrite_16 (fh, FleetPtr->known_strength); - cwrite_16 (fh, FleetPtr->known_loc.x); - cwrite_16 (fh, FleetPtr->known_loc.y); - cwrite_8 (fh, FleetPtr->growth_err_term); - cwrite_8 (fh, FleetPtr->func_index); - cwrite_16 (fh, FleetPtr->dest_loc.x); - cwrite_16 (fh, FleetPtr->dest_loc.y); + write_16 (fh, FleetPtr->actual_strength); + write_16 (fh, FleetPtr->known_strength); + write_16 (fh, FleetPtr->known_loc.x); + write_16 (fh, FleetPtr->known_loc.y); + write_8 (fh, FleetPtr->growth_err_term); + write_8 (fh, FleetPtr->func_index); + write_16 (fh, FleetPtr->dest_loc.x); + write_16 (fh, FleetPtr->dest_loc.y); UnlockFleetInfo (pQueue, hFleet); hFleet = hNextFleet; @@ -234,12 +197,16 @@ SaveRaceQueue (SAVEBUF *fh, QUEUE *pQueue) } static void -SaveGroupQueue (SAVEBUF *fh, QUEUE *pQueue) +SaveGroupQueue (uio_Stream *fh, QUEUE *pQueue) { HIPGROUP hGroup, hNextGroup; + COUNT num_links; - // Write the number of entries in the queue. - cwrite_16 (fh, CountLinks (pQueue)); + num_links = CountLinks (pQueue); + if (num_links == 0) + return; + write_32 (fh, IP_GRP_Q_TAG); + write_32 (fh, num_links * 13); // 13 bytes per element right now for (hGroup = GetHeadLink (pQueue); hGroup; hGroup = hNextGroup) { @@ -248,177 +215,220 @@ SaveGroupQueue (SAVEBUF *fh, QUEUE *pQueue) GroupPtr = LockIpGroup (pQueue, hGroup); hNextGroup = _GetSuccLink (GroupPtr); - cwrite_16 (fh, GroupPtr->group_counter); - cwrite_8 (fh, GroupPtr->race_id); - cwrite_8 (fh, GroupPtr->sys_loc); - cwrite_8 (fh, GroupPtr->task); - cwrite_8 (fh, GroupPtr->in_system); /* was crew_level */ - cwrite_8 (fh, GroupPtr->dest_loc); - cwrite_8 (fh, GroupPtr->orbit_pos); - cwrite_8 (fh, GroupPtr->group_id); /* was max_energy */ - cwrite_16 (fh, GroupPtr->loc.x); - cwrite_16 (fh, GroupPtr->loc.y); + write_16 (fh, GroupPtr->group_counter); + write_8 (fh, GroupPtr->race_id); + write_8 (fh, GroupPtr->sys_loc); + write_8 (fh, GroupPtr->task); + write_8 (fh, GroupPtr->in_system); /* was crew_level */ + write_8 (fh, GroupPtr->dest_loc); + write_8 (fh, GroupPtr->orbit_pos); + write_8 (fh, GroupPtr->group_id); /* was max_energy */ + write_16 (fh, GroupPtr->loc.x); + write_16 (fh, GroupPtr->loc.y); UnlockIpGroup (pQueue, hGroup); } } static void -SaveEncounter (const ENCOUNTER *EncounterPtr, SAVEBUF *fh) +SaveEncounters (uio_Stream *fh) { - COUNT i; + COUNT num_links; + HENCOUNTER hEncounter; + num_links = CountLinks (&GLOBAL (encounter_q)); + if (num_links == 0) + return; + write_32 (fh, ENCOUNTERS_TAG); + write_32 (fh, 65 * num_links); - cwrite_16 (fh, EncounterPtr->transition_state); - cwrite_16 (fh, EncounterPtr->origin.x); - cwrite_16 (fh, EncounterPtr->origin.y); - cwrite_16 (fh, EncounterPtr->radius); - // former STAR_DESC fields - cwrite_16 (fh, EncounterPtr->loc_pt.x); - cwrite_16 (fh, EncounterPtr->loc_pt.y); - cwrite_8 (fh, EncounterPtr->race_id); - cwrite_8 (fh, EncounterPtr->num_ships); - cwrite_8 (fh, EncounterPtr->flags); - - // Save each entry in the BRIEF_SHIP_INFO array - for (i = 0; i < MAX_HYPER_SHIPS; i++) + hEncounter = GetHeadLink (&GLOBAL (encounter_q)); + while (num_links--) { - const BRIEF_SHIP_INFO *ShipInfo = &EncounterPtr->ShipList[i]; + HENCOUNTER hNextEncounter; + ENCOUNTER *EncounterPtr; + COUNT i; - cwrite_8 (fh, ShipInfo->race_id); - cwrite_16 (fh, ShipInfo->crew_level); - cwrite_16 (fh, ShipInfo->max_crew); - cwrite_8 (fh, ShipInfo->max_energy); + LockEncounter (hEncounter, &EncounterPtr); + hNextEncounter = GetSuccEncounter (EncounterPtr); + + write_16 (fh, EncounterPtr->transition_state); + write_16 (fh, EncounterPtr->origin.x); + write_16 (fh, EncounterPtr->origin.y); + write_16 (fh, EncounterPtr->radius); + // former STAR_DESC fields + write_16 (fh, EncounterPtr->loc_pt.x); + write_16 (fh, EncounterPtr->loc_pt.y); + write_8 (fh, EncounterPtr->race_id); + write_8 (fh, EncounterPtr->num_ships); + write_8 (fh, EncounterPtr->flags); + + // Save each entry in the BRIEF_SHIP_INFO array + for (i = 0; i < MAX_HYPER_SHIPS; i++) + { + const BRIEF_SHIP_INFO *ShipInfo = &EncounterPtr->ShipList[i]; + + write_8 (fh, ShipInfo->race_id); + write_16 (fh, ShipInfo->crew_level); + write_16 (fh, ShipInfo->max_crew); + write_8 (fh, ShipInfo->max_energy); + } + + // Save the stuff after the BRIEF_SHIP_INFO array + write_32 (fh, EncounterPtr->log_x); + write_32 (fh, EncounterPtr->log_y); + + UnlockEncounter (hEncounter); + hEncounter = hNextEncounter; } - - // Save the stuff after the BRIEF_SHIP_INFO array - cwrite_32 (fh, EncounterPtr->log_x); - cwrite_32 (fh, EncounterPtr->log_y); } static void -SaveEvent (const EVENT *EventPtr, SAVEBUF *fh) +SaveEvents (uio_Stream *fh) { - cwrite_8 (fh, EventPtr->day_index); - cwrite_8 (fh, EventPtr->month_index); - cwrite_16 (fh, EventPtr->year_index); - cwrite_8 (fh, EventPtr->func_index); + COUNT num_links; + HEVENT hEvent; + num_links = CountLinks (&GLOBAL (GameClock.event_q)); + if (num_links == 0) + return; + write_32 (fh, EVENTS_TAG); + write_32 (fh, num_links * 5); /* Event chunks are five bytes each */ + + hEvent = GetHeadLink (&GLOBAL (GameClock.event_q)); + while (num_links--) + { + HEVENT hNextEvent; + EVENT *EventPtr; + + LockEvent (hEvent, &EventPtr); + hNextEvent = GetSuccEvent (EventPtr); + + write_8 (fh, EventPtr->day_index); + write_8 (fh, EventPtr->month_index); + write_16 (fh, EventPtr->year_index); + write_8 (fh, EventPtr->func_index); + + UnlockEvent (hEvent); + hEvent = hNextEvent; + } } +/* The clock state is folded in with the game state chunk. */ static void -SaveClockState (const CLOCK_STATE *ClockPtr, SAVEBUF *fh) +SaveClockState (const CLOCK_STATE *ClockPtr, uio_Stream *fh) { - cwrite_8 (fh, ClockPtr->day_index); - cwrite_8 (fh, ClockPtr->month_index); - cwrite_16 (fh, ClockPtr->year_index); - cwrite_16 (fh, ClockPtr->tick_count); - cwrite_16 (fh, ClockPtr->day_in_ticks); + write_8 (fh, ClockPtr->day_index); + write_8 (fh, ClockPtr->month_index); + write_16 (fh, ClockPtr->year_index); + write_16 (fh, ClockPtr->tick_count); + write_16 (fh, ClockPtr->day_in_ticks); } +/* Save out the game state chunks. There are two of these; the Global + * State chunk is fixed size, but the Game State tag can be extended + * by modders. */ static void -SaveGameState (const GAME_STATE *GSPtr, SAVEBUF *fh) +SaveGameState (const GAME_STATE *GSPtr, uio_Stream *fh) { - cwrite_8 (fh, GSPtr->glob_flags); - cwrite_8 (fh, GSPtr->CrewCost); - cwrite_8 (fh, GSPtr->FuelCost); - cwrite_a8 (fh, GSPtr->ModuleCost, NUM_MODULES); - cwrite_a8 (fh, GSPtr->ElementWorth, NUM_ELEMENT_CATEGORIES); - cwrite_16 (fh, GSPtr->CurrentActivity); + write_32 (fh, GLOBAL_STATE_TAG); + write_32 (fh, 79); + write_8 (fh, GSPtr->glob_flags); + write_8 (fh, GSPtr->CrewCost); + write_8 (fh, GSPtr->FuelCost); + write_a8 (fh, GSPtr->ModuleCost, NUM_MODULES); + write_a8 (fh, GSPtr->ElementWorth, NUM_ELEMENT_CATEGORIES); + write_16 (fh, GSPtr->CurrentActivity); SaveClockState (&GSPtr->GameClock, fh); - cwrite_16 (fh, GSPtr->autopilot.x); - cwrite_16 (fh, GSPtr->autopilot.y); - cwrite_16 (fh, GSPtr->ip_location.x); - cwrite_16 (fh, GSPtr->ip_location.y); + write_16 (fh, GSPtr->autopilot.x); + write_16 (fh, GSPtr->autopilot.y); + write_16 (fh, GSPtr->ip_location.x); + write_16 (fh, GSPtr->ip_location.y); /* STAMP ShipStamp */ - cwrite_16 (fh, GSPtr->ShipStamp.origin.x); - cwrite_16 (fh, GSPtr->ShipStamp.origin.y); - cwrite_16 (fh, GSPtr->ShipFacing); - cwrite_8 (fh, GSPtr->ip_planet); - cwrite_8 (fh, GSPtr->in_orbit); + write_16 (fh, GSPtr->ShipStamp.origin.x); + write_16 (fh, GSPtr->ShipStamp.origin.y); + write_16 (fh, GSPtr->ShipFacing); + write_8 (fh, GSPtr->ip_planet); + write_8 (fh, GSPtr->in_orbit); /* VELOCITY_DESC velocity */ - cwrite_16 (fh, GSPtr->velocity.TravelAngle); - cwrite_16 (fh, GSPtr->velocity.vector.width); - cwrite_16 (fh, GSPtr->velocity.vector.height); - cwrite_16 (fh, GSPtr->velocity.fract.width); - cwrite_16 (fh, GSPtr->velocity.fract.height); - cwrite_16 (fh, GSPtr->velocity.error.width); - cwrite_16 (fh, GSPtr->velocity.error.height); - cwrite_16 (fh, GSPtr->velocity.incr.width); - cwrite_16 (fh, GSPtr->velocity.incr.height); + write_16 (fh, GSPtr->velocity.TravelAngle); + write_16 (fh, GSPtr->velocity.vector.width); + write_16 (fh, GSPtr->velocity.vector.height); + write_16 (fh, GSPtr->velocity.fract.width); + write_16 (fh, GSPtr->velocity.fract.height); + write_16 (fh, GSPtr->velocity.error.width); + write_16 (fh, GSPtr->velocity.error.height); + write_16 (fh, GSPtr->velocity.incr.width); + write_16 (fh, GSPtr->velocity.incr.height); - cwrite_32 (fh, GSPtr->BattleGroupRef); + write_32 (fh, GSPtr->BattleGroupRef); - cwrite_a8 (fh, GSPtr->GameState, sizeof (GSPtr->GameState)); + /* The Game state bits. Vanilla UQM uses 155 bytes here. */ + write_32 (fh, GAME_STATE_TAG); + write_32 (fh, sizeof (GSPtr->GameState)); + write_a8 (fh, GSPtr->GameState, sizeof (GSPtr->GameState)); } -static BOOLEAN +/* This is folded into the Summary chunk */ +static void SaveSisState (const SIS_STATE *SSPtr, void *fp) { - if ( - write_32 (fp, SSPtr->log_x) != 1 || - write_32 (fp, SSPtr->log_y) != 1 || - write_32 (fp, SSPtr->ResUnits) != 1 || - write_32 (fp, SSPtr->FuelOnBoard) != 1 || - write_16 (fp, SSPtr->CrewEnlisted) != 1 || - write_16 (fp, SSPtr->TotalElementMass) != 1 || - write_16 (fp, SSPtr->TotalBioMass) != 1 || - write_a8 (fp, SSPtr->ModuleSlots, NUM_MODULE_SLOTS) != 1 || - write_a8 (fp, SSPtr->DriveSlots, NUM_DRIVE_SLOTS) != 1 || - write_a8 (fp, SSPtr->JetSlots, NUM_JET_SLOTS) != 1 || - write_8 (fp, SSPtr->NumLanders) != 1 || - write_a16 (fp, SSPtr->ElementAmounts, NUM_ELEMENT_CATEGORIES) != 1 || + write_32 (fp, SSPtr->log_x); + write_32 (fp, SSPtr->log_y); + write_32 (fp, SSPtr->ResUnits); + write_32 (fp, SSPtr->FuelOnBoard); + write_16 (fp, SSPtr->CrewEnlisted); + write_16 (fp, SSPtr->TotalElementMass); + write_16 (fp, SSPtr->TotalBioMass); + write_a8 (fp, SSPtr->ModuleSlots, NUM_MODULE_SLOTS); + write_a8 (fp, SSPtr->DriveSlots, NUM_DRIVE_SLOTS); + write_a8 (fp, SSPtr->JetSlots, NUM_JET_SLOTS); + write_8 (fp, SSPtr->NumLanders); + write_a16 (fp, SSPtr->ElementAmounts, NUM_ELEMENT_CATEGORIES); - write_str (fp, SSPtr->ShipName, SIS_NAME_SIZE) != 1 || - write_str (fp, SSPtr->CommanderName, SIS_NAME_SIZE) != 1 || - write_str (fp, SSPtr->PlanetName, SIS_NAME_SIZE) != 1 - ) - return FALSE; - else - return TRUE; + write_str (fp, SSPtr->ShipName, SIS_NAME_SIZE); + write_str (fp, SSPtr->CommanderName, SIS_NAME_SIZE); + write_str (fp, SSPtr->PlanetName, SIS_NAME_SIZE); } +/* Write out the Summary Chunk. This is variable length because of the + savegame name */ static BOOLEAN SaveSummary (const SUMMARY_DESC *SummPtr, void *fp) { - if ( - write_32 (fp, SAVEFILE_TAG) != 1 || - write_32 (fp, SUMMARY_TAG) != 1 || - write_32 (fp, 160 + strlen(SummPtr->SaveName)) != 1 - ) - return FALSE; - if (!SaveSisState (&SummPtr->SS, fp)) - return FALSE; + write_32 (fp, SUMMARY_TAG); + write_32 (fp, 160 + strlen(SummPtr->SaveName)); + SaveSisState (&SummPtr->SS, fp); - if ( - write_8 (fp, SummPtr->Activity) != 1 || - write_8 (fp, SummPtr->Flags) != 1 || - write_8 (fp, SummPtr->day_index) != 1 || - write_8 (fp, SummPtr->month_index) != 1 || - write_16 (fp, SummPtr->year_index) != 1 || - write_8 (fp, SummPtr->MCreditLo) != 1 || - write_8 (fp, SummPtr->MCreditHi) != 1 || - write_8 (fp, SummPtr->NumShips) != 1 || - write_8 (fp, SummPtr->NumDevices) != 1 || - write_a8 (fp, SummPtr->ShipList, MAX_BUILT_SHIPS) != 1 || - write_a8 (fp, SummPtr->DeviceList, MAX_EXCLUSIVE_DEVICES) != 1 || - write_a8 (fp, SummPtr->SaveName, strlen(SummPtr->SaveName)) != 1 - ) - return FALSE; - else - return TRUE; + write_8 (fp, SummPtr->Activity); + write_8 (fp, SummPtr->Flags); + write_8 (fp, SummPtr->day_index); + write_8 (fp, SummPtr->month_index); + write_16 (fp, SummPtr->year_index); + write_8 (fp, SummPtr->MCreditLo); + write_8 (fp, SummPtr->MCreditHi); + write_8 (fp, SummPtr->NumShips); + write_8 (fp, SummPtr->NumDevices); + write_a8 (fp, SummPtr->ShipList, MAX_BUILT_SHIPS); + write_a8 (fp, SummPtr->DeviceList, MAX_EXCLUSIVE_DEVICES); + write_a8 (fp, SummPtr->SaveName, strlen(SummPtr->SaveName)); } +/* Save the Star Description chunk. This is not to be confused with + * the Star *Info* chunk, which records which planetary features you + * have exploited with your lander */ static void -SaveStarDesc (const STAR_DESC *SDPtr, SAVEBUF *fh) +SaveStarDesc (const STAR_DESC *SDPtr, uio_Stream *fh) { - cwrite_16 (fh, SDPtr->star_pt.x); - cwrite_16 (fh, SDPtr->star_pt.y); - cwrite_8 (fh, SDPtr->Type); - cwrite_8 (fh, SDPtr->Index); - cwrite_8 (fh, SDPtr->Prefix); - cwrite_8 (fh, SDPtr->Postfix); + write_32 (fh, STAR_TAG); + write_32 (fh, 8); + write_16 (fh, SDPtr->star_pt.x); + write_16 (fh, SDPtr->star_pt.y); + write_8 (fh, SDPtr->Type); + write_8 (fh, SDPtr->Index); + write_8 (fh, SDPtr->Prefix); + write_8 (fh, SDPtr->Postfix); } static void @@ -578,57 +588,71 @@ SaveFlagshipState (void) } } +static void +SaveStateFile (DWORD statefileId, DWORD tag, uio_Stream *fh) +{ + GAME_STATE_FILE *fp; + char buf[256]; + fp = OpenStateFile (statefileId, "rb"); + if (fp) + { + DWORD flen = LengthStateFile (fp); + write_32 (fh, tag); + write_32 (fh, flen); + while (flen) + { + COUNT num_bytes; + + num_bytes = flen >= sizeof (buf) ? sizeof (buf) : (COUNT)flen; + ReadStateFile (buf, num_bytes, 1, fp); + write_a8 (fh, buf, num_bytes); + + flen -= num_bytes; + } + CloseStateFile (fp); + } +} + // This function first writes to a memory file, and then writes the whole // lot to the actual save file at once. BOOLEAN SaveGame (COUNT which_game, SUMMARY_DESC *SummPtr, const char *name) { - BOOLEAN success; - void *out_fp, *h; - SAVEBUF fh_backing, *fh; - - success = TRUE; - fh = &fh_backing; - h = HMalloc (128 * 1024); - if (h == 0) - { - log_add (log_Debug, "Insufficient room for save buffers"); - } + uio_Stream *out_fp; + POINT pt; + STAR_DESC SD; + char file[PATH_MAX]; + if (CurStarDescPtr) + SD = *CurStarDescPtr; else + memset (&SD, 0, sizeof (SD)); + + // XXX: Backup: SaveFlagshipState() overwrites ip_location + pt = GLOBAL (ip_location); + SaveFlagshipState (); + if (LOBYTE (GLOBAL (CurrentActivity)) == IN_INTERPLANETARY + && !(GLOBAL (CurrentActivity) + & (START_ENCOUNTER | START_INTERPLANETARY))) + PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP); + + // Write the memory file to the actual savegame file. + sprintf (file, "uqmsave.%02u", which_game); + if ((out_fp = res_OpenResFile (saveDir, file, "wb"))) { - GAME_STATE_FILE *fp; - DWORD flen; - COUNT num_links; - POINT pt; - STAR_DESC SD; - char buf[256], file[PATH_MAX]; + io_ok = TRUE; + write_32 (out_fp, SAVEFILE_TAG); - fh->buf = h; - fh->bufsize = 128 * 1024; - fh->index = 0; - fh->error = 0; - success = TRUE; - if (CurStarDescPtr) - SD = *CurStarDescPtr; - else - memset (&SD, 0, sizeof (SD)); + PrepareSummary (SummPtr, name); + SaveSummary (SummPtr, out_fp); - // XXX: Backup: SaveFlagshipState() overwrites ip_location - pt = GLOBAL (ip_location); - SaveFlagshipState (); - if (LOBYTE (GLOBAL (CurrentActivity)) == IN_INTERPLANETARY - && !(GLOBAL (CurrentActivity) - & (START_ENCOUNTER | START_INTERPLANETARY))) - PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP); - - SaveGameState (&GlobData.Game_state, fh); + SaveGameState (&GlobData.Game_state, out_fp); // XXX: Restore GLOBAL (ip_location) = pt; // Only relevant when loading a game and must be cleaned GLOBAL (in_orbit) = 0; - SaveRaceQueue (fh, &GLOBAL (avail_race_q)); + SaveRaceQueue (out_fp, &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 @@ -636,160 +660,41 @@ SaveGame (COUNT which_game, SUMMARY_DESC *SummPtr, const char *name) if (!(GLOBAL (CurrentActivity) & START_INTERPLANETARY)) { if (GLOBAL (CurrentActivity) & START_ENCOUNTER) - SaveShipQueue (fh, &GLOBAL (npc_built_ship_q)); + SaveShipQueue (out_fp, &GLOBAL (npc_built_ship_q), NPC_SHIP_Q_TAG); else if (LOBYTE (GLOBAL (CurrentActivity)) == 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. - SaveGroupQueue (fh, &GLOBAL (ip_group_q)); - else - // XXX: empty queue write-out is only needed to maintain - // the savegame compatibility - SaveEmptyQueue (fh); + SaveGroupQueue (out_fp, &GLOBAL (ip_group_q)); } - SaveShipQueue (fh, &GLOBAL (built_ship_q)); + SaveShipQueue (out_fp, &GLOBAL (built_ship_q), SHIP_Q_TAG); - // Save the number of game events (compressed). - num_links = CountLinks (&GLOBAL (GameClock.event_q)); - cwrite_16 (fh, num_links); - // Save the game events themselves (compressed): + // Save the game event chunk + SaveEvents (out_fp); + + // Save the encounter chunk (black globes in HS/QS) + SaveEncounters (out_fp); + + // Save out the state file chunks. + SaveStateFile (STARINFO_FILE, STAR_SF_TAG, out_fp); + SaveStateFile (DEFGRPINFO_FILE, DEFGRP_SF_TAG, out_fp); + SaveStateFile (RANDGRPINFO_FILE, RANDGRP_SF_TAG, out_fp); + + // Save out the Star Descriptor + SaveStarDesc (&SD, out_fp); + + res_CloseResFile (out_fp); + if (!io_ok) { - HEVENT hEvent; - - hEvent = GetHeadLink (&GLOBAL (GameClock.event_q)); - while (num_links--) - { - HEVENT hNextEvent; - EVENT *EventPtr; - - LockEvent (hEvent, &EventPtr); - hNextEvent = GetSuccEvent (EventPtr); - - SaveEvent (EventPtr, fh); - - UnlockEvent (hEvent); - hEvent = hNextEvent; - } + DeleteResFile(saveDir, file); + return FALSE; } - - // Save the number of encounters (black globes in HS/QS (compressed)) - num_links = CountLinks (&GLOBAL (encounter_q)); - // Save the encounters themselves (compressed): - cwrite_16 (fh, num_links); - { - HENCOUNTER hEncounter; - - hEncounter = GetHeadLink (&GLOBAL (encounter_q)); - while (num_links--) - { - HENCOUNTER hNextEncounter; - ENCOUNTER *EncounterPtr; - - LockEncounter (hEncounter, &EncounterPtr); - hNextEncounter = GetSuccEncounter (EncounterPtr); - - SaveEncounter (EncounterPtr, fh); - - UnlockEncounter (hEncounter); - hEncounter = hNextEncounter; - } - } - - // Copy the star info file to the memory file (compressed). - fp = OpenStateFile (STARINFO_FILE, "rb"); - if (fp) - { - flen = LengthStateFile (fp); - // Write the uncompressed size. - cwrite_32 (fh, flen); - while (flen) - { - COUNT num_bytes; - - num_bytes = flen >= sizeof (buf) ? sizeof (buf) : (COUNT)flen; - ReadStateFile (buf, num_bytes, 1, fp); - cwrite_a8 (fh, buf, num_bytes); - - flen -= num_bytes; - } - CloseStateFile (fp); - } - - // Copy the defined groupinfo file into the memory file (compressed) - fp = OpenStateFile (DEFGRPINFO_FILE, "rb"); - if (fp) - { - flen = LengthStateFile (fp); - // Write the uncompressed size. - cwrite_32 (fh, flen); - while (flen) - { - COUNT num_bytes; - - num_bytes = flen >= sizeof (buf) ? sizeof (buf) : (COUNT)flen; - ReadStateFile (buf, num_bytes, 1, fp); - cwrite_a8 (fh, buf, num_bytes); - - flen -= num_bytes; - } - CloseStateFile (fp); - } - - // Copy the random groupinfo file into the memory file (compressed) - fp = OpenStateFile (RANDGRPINFO_FILE, "rb"); - if (fp) - { - flen = LengthStateFile (fp); - // Write the uncompressed size. - cwrite_32 (fh, flen); - while (flen) - { - COUNT num_bytes; - - num_bytes = flen >= sizeof (buf) ? sizeof (buf) : (COUNT)flen; - ReadStateFile (buf, num_bytes, 1, fp); - cwrite_a8 (fh, buf, num_bytes); - - flen -= num_bytes; - } - CloseStateFile (fp); - } - - // Write the current star desc into the memory file (compressed). - SaveStarDesc (&SD, fh); - - flen = fh->index; - - // Write the memory file to the actual savegame file. - sprintf (file, "uqmsave.%02u", which_game); - log_add (log_Debug, "'%s' is %u bytes long", file, - flen + 180 + strlen(SummPtr->SaveName)); - if (flen && (out_fp = res_OpenResFile (saveDir, file, "wb"))) - { - PrepareSummary (SummPtr, name); - - success = SaveSummary (SummPtr, out_fp); - // Then write the rest of the data. - if (success && write_32 (out_fp, OMNIBUS_TAG) != 1) - success = FALSE; - if (success && write_32 (out_fp, flen) != 1) - success = FALSE; - if (success && WriteResFile (h, flen, 1, out_fp) != 1) - success = FALSE; - - if (res_CloseResFile ((uio_Stream *)out_fp) == 0) - success = FALSE; - - } - else - success = FALSE; - - if (!success) - DeleteResFile (saveDir, file); + } + else + { + return FALSE; } - HFree (h); - - return (success); + return TRUE; } diff --git a/sc2/src/uqm/save.h b/sc2/src/uqm/save.h index d96669c63..3d0ac1329 100644 --- a/sc2/src/uqm/save.h +++ b/sc2/src/uqm/save.h @@ -35,8 +35,19 @@ extern "C" { // The savefile tag numbers. #define SAVEFILE_TAG 0x01534d55 // "UMS\x01": UQM Save version 1 -#define OMNIBUS_TAG 0x696e6d4f // "Omni": All data, uncompressed #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 STAR_SF_TAG 0x46534953 // "SISF": Star Info State File +#define DEFGRP_SF_TAG 0x46534744 // "DGSF": Defined Group State File +#define RANDGRP_SF_TAG 0x46534752 // "RGSF": Random Group State File typedef struct { From 634ec1ae7d377f0ac5a3202c8d1d288852a7fc9d Mon Sep 17 00:00:00 2001 From: Michael Martin Date: Sun, 13 Oct 2013 05:44:09 -0700 Subject: [PATCH 11/22] Document the new savefile format. --- sc2/doc/devel/savefile | 145 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 sc2/doc/devel/savefile diff --git a/sc2/doc/devel/savefile b/sc2/doc/devel/savefile new file mode 100644 index 000000000..4a2c3acd3 --- /dev/null +++ b/sc2/doc/devel/savefile @@ -0,0 +1,145 @@ + 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. + +- "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. + +- "SISF": Star Info State File. An index into which planetside + resources you have investigated and collected. See + doc/devel/statefiles for more details. + +- "DGSF": Defined Group State File. Battlegroup information for + space-based encounters dictated by the plot. See + doc/devel/statefiles for more details. + +- "RGSF": Random Group State File. Battlegroup information for random + encounters in interplanetary space. See doc/devel/statefiles for + more details. + +THINGS LEFT TO DO BEFORE MERGING +-------------------------------- + +- Actually enforce little-endianness of everything that hits the disk. + - This is a simple matter of improving the read_* and write_* + functions in load.c and save.c. + +- There are 448 bits in GmSt that are actually indices into DGSF. They + shouldn't be there. In fact, they shouldn't even be in the + GAME_STATE array either. They should be an array of DWORDs living + independently in the global state + - First, we should juggle the order of the state bits so that + they're all at the end. That way we can ultimately just truncate + GmSt and everything will be fine. We can do that without + complicating legacy loads much. + +- The State Files are (except maybe for Star Info) a horrible mess and + we should not be replicating them in the save file. We should + instead be regenerating them from more structured forms. + - This is complicated by the fact that there are references to the + _GSF chunks in both GlSt (BattleGroupRef) and GmSt + (*_GRPOFFSET*). I have a good handle on what needs to be done to + GmSt, but the significance of the GlSt pointer still requires + research. From e7d30008a2133523ea510213c4420d263b3ed48d Mon Sep 17 00:00:00 2001 From: Michael Martin Date: Sun, 13 Oct 2013 16:36:41 -0700 Subject: [PATCH 12/22] Endian safety in all savegame chunks except statefiles. --- sc2/src/uqm/load.c | 50 +++++++++++++++++++++++++++------------------- sc2/src/uqm/save.c | 12 +++++------ 2 files changed, 36 insertions(+), 26 deletions(-) diff --git a/sc2/src/uqm/load.c b/sc2/src/uqm/load.c index 4a4953c62..48f0d4312 100644 --- a/sc2/src/uqm/load.c +++ b/sc2/src/uqm/load.c @@ -49,41 +49,51 @@ read_8 (void *fp, BYTE *v) 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); + UWORD t = 0; + int shift, i; + for (i = 0, shift = 0; i < 2; ++i, shift += 8) + { + BYTE b; + if (read_8 (fp, &b) != 1) + return 0; + t |= ((UWORD)b) << shift; + } + + if (v) + *v = t; + + return 1; } static inline size_t read_16s (void *fp, SWORD *v) { - SWORD t; - if (!v) /* read value ignored */ - v = &t; - return ReadResFile (v, 2, 1, fp); + return read_16 (fp, v); } 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); + DWORD t = 0; + int shift, i; + for (i = 0, shift = 0; i < 4; ++i, shift += 8) + { + BYTE b; + if (read_8 (fp, &b) != 1) + return 0; + t |= ((DWORD)b) << shift; + } + + if (v) + *v = t; + + return 1; } 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; + return read_32 (fp, v); } static inline size_t diff --git a/sc2/src/uqm/save.c b/sc2/src/uqm/save.c index 51fc56f9c..1345be27b 100644 --- a/sc2/src/uqm/save.c +++ b/sc2/src/uqm/save.c @@ -64,17 +64,17 @@ write_8 (void *fp, BYTE v) static inline void write_16 (void *fp, UWORD v) { - if (io_ok) - if (WriteResFile (&v, 2, 1, fp) != 1) - io_ok = FALSE; + write_8 (fp, (BYTE)( v & 0xff)); + write_8 (fp, (BYTE)((v >> 8) & 0xff)); } static inline void write_32 (void *fp, DWORD v) { - if (io_ok) - if (WriteResFile (&v, 4, 1, fp) != 1) - io_ok = FALSE; + write_8 (fp, (BYTE)( v & 0xff)); + write_8 (fp, (BYTE)((v >> 8) & 0xff)); + write_8 (fp, (BYTE)((v >> 16) & 0xff)); + write_8 (fp, (BYTE)((v >> 24) & 0xff)); } static inline void From 40dcd65afa6b4b1f96f8e1e6ecfc99fd27366971 Mon Sep 17 00:00:00 2001 From: Michael Martin Date: Sun, 13 Oct 2013 18:49:54 -0700 Subject: [PATCH 13/22] Generalize the getGameState functions. --- sc2/src/uqm/globdata.c | 26 +++++++++++++------------- sc2/src/uqm/globdata.h | 20 ++++++++++---------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/sc2/src/uqm/globdata.c b/sc2/src/uqm/globdata.c index 20e39a470..a387313c1 100644 --- a/sc2/src/uqm/globdata.c +++ b/sc2/src/uqm/globdata.c @@ -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 diff --git a/sc2/src/uqm/globdata.h b/sc2/src/uqm/globdata.h index 031b57df7..60b528b83 100644 --- a/sc2/src/uqm/globdata.h +++ b/sc2/src/uqm/globdata.h @@ -972,36 +972,36 @@ extern GLOBDATA GlobData; //#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 ); -#define GET_GAME_STATE(SName) getGameState ((SName), (END_##SName)) +#define GET_GAME_STATE(SName) getGameState (GLOBAL(GameState), (SName), (END_##SName)) #ifdef STATE_DEBUG # define SET_GAME_STATE(SName, val) \ - setGameState ((SName), (END_##SName), (val), #SName) + setGameState (GLOBAL(GameState), (SName), (END_##SName), (val), #SName) #else # define SET_GAME_STATE(SName, val) \ - setGameState ((SName), (END_##SName), (val)) + setGameState (GLOBAL(GameState), (SName), (END_##SName), (val)) #endif -extern DWORD getGameState32 (int startBit); -extern void setGameState32 (int startBit, DWORD val +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_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 From 60a9ca61cfd58c9fa5623be476586464c937c58c Mon Sep 17 00:00:00 2001 From: Michael Martin Date: Sun, 13 Oct 2013 23:19:13 -0700 Subject: [PATCH 14/22] Reorganize the GameState array. This is preparation work so that when the state files are removed from the savegame format (or possibly removed entirely) it will not be a savegame compatibility-breaking change. Legacy support has been modified so that the GameState bits are reordered as part of load, so loading legacy saves still works. --- sc2/src/uqm/globdata.c | 16 ++++ sc2/src/uqm/globdata.h | 167 ++++++++++++++++++++++---------------- sc2/src/uqm/load_legacy.c | 66 ++++++++++++++- 3 files changed, 176 insertions(+), 73 deletions(-) diff --git a/sc2/src/uqm/globdata.c b/sc2/src/uqm/globdata.c index a387313c1..ff9edc2fb 100644 --- a/sc2/src/uqm/globdata.c +++ b/sc2/src/uqm/globdata.c @@ -119,6 +119,22 @@ setGameState32 (BYTE *state, 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) { diff --git a/sc2/src/uqm/globdata.h b/sc2/src/uqm/globdata.h index 60b528b83..952b5fcc9 100644 --- a/sc2/src/uqm/globdata.h +++ b/sc2/src/uqm/globdata.h @@ -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,14 +974,37 @@ 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 (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 diff --git a/sc2/src/uqm/load_legacy.c b/sc2/src/uqm/load_legacy.c index 3c7a7e702..6470a52f1 100644 --- a/sc2/src/uqm/load_legacy.c +++ b/sc2/src/uqm/load_legacy.c @@ -178,6 +178,66 @@ read_a16 (void *fp, UWORD *ar, COUNT count) 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) { @@ -424,7 +484,7 @@ LoadClockState (CLOCK_STATE *ClockPtr, DECODE_REF fh) static void LoadGameState (GAME_STATE *GSPtr, DECODE_REF fh) { - BYTE dummy8; + BYTE dummy8, oldstate[LEGACY_GAMESTATE_SIZE]; cread_8 (fh, &dummy8); /* obsolete */ cread_8 (fh, &GSPtr->glob_flags); @@ -469,9 +529,9 @@ LoadGameState (GAME_STATE *GSPtr, DECODE_REF fh) DummyLoadQueue (&GSPtr->encounter_q, fh); DummyLoadQueue (&GSPtr->built_ship_q, fh); - cread_a8 (fh, GSPtr->GameState, sizeof (GSPtr->GameState)); + cread_a8 (fh, oldstate, LEGACY_GAMESTATE_SIZE); + InterpretLegacyGameState (GSPtr->GameState, oldstate); - assert (sizeof (GSPtr->GameState) % 4 == 3); cread_8 (fh, NULL); /* GAME_STATE alignment padding */ } From 9f3a70985ef5d4a797945df646b74451b17f7f93 Mon Sep 17 00:00:00 2001 From: Michael Martin Date: Sun, 13 Oct 2013 23:22:20 -0700 Subject: [PATCH 15/22] Update savefile spec based on new findings. --- sc2/doc/devel/savefile | 47 ++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/sc2/doc/devel/savefile b/sc2/doc/devel/savefile index 4a2c3acd3..8b7b4a577 100644 --- a/sc2/doc/devel/savefile +++ b/sc2/doc/devel/savefile @@ -10,8 +10,8 @@ anymore. It was also extremely fragile and hard for modders to extend. The new save format seeks to alleviate these problems. -GENERAL FORMAT --------------- + GENERAL FORMAT + -------------- All multibyte values are little-endian. There are no alignment restrictions inherent in the format. @@ -119,27 +119,44 @@ save file. encounters in interplanetary space. See doc/devel/statefiles for more details. -THINGS LEFT TO DO BEFORE MERGING --------------------------------- +- "Scan": Scanner Masks. NOT YET IMPLEMENTED; WILL REPLACE SISF. It + might just be a rename with better endianness enforcement, though; + the SISF dump is pretty well-structured as it is. + +- "DGrp": Defined Group. NOT YET IMPLEMENTED; WILL REPLACE DGSF. Will + carry an enumeration id to indicate which of the defgrps it is + (that's currently intrinsic to the game state bit array) and will + also have a bit indicating whether or not it is the "current" + defgrp. If no defgrp is current, BattleGroupRef is 0; otherwise, + BattleGroupRef is the defgrp file offset that corresponds to the + current system. There will be one DGrp chunk for each system with an + active defgrp record. + +- NOT YET DESIGNED: RGSF also needs to be replaced with one or more + chunk types. This will likely be similar to the DGrp chunks, but + there are stronger constraints on repetition here. + + THINGS LEFT TO DO BEFORE MERGING + -------------------------------- - Actually enforce little-endianness of everything that hits the disk. - - This is a simple matter of improving the read_* and write_* - functions in load.c and save.c. + - Everything that isn't a State File now respects this. + - State files are going to require more work (see below). - There are 448 bits in GmSt that are actually indices into DGSF. They shouldn't be there. In fact, they shouldn't even be in the GAME_STATE array either. They should be an array of DWORDs living independently in the global state - - First, we should juggle the order of the state bits so that - they're all at the end. That way we can ultimately just truncate - GmSt and everything will be fine. We can do that without - complicating legacy loads much. + - In preparation for this, the GRPOFFS state has been moved to the + end of the state array. We'll be able to eradicate it once the + state files are no longer part of the save. - The State Files are (except maybe for Star Info) a horrible mess and we should not be replicating them in the save file. We should instead be regenerating them from more structured forms. - - This is complicated by the fact that there are references to the - _GSF chunks in both GlSt (BattleGroupRef) and GmSt - (*_GRPOFFSET*). I have a good handle on what needs to be done to - GmSt, but the significance of the GlSt pointer still requires - research. + - Once we do this we can start computing new values for the GRPOFFS + state and the BattleGroupRef based on the new chunks. We can then + remove the GRPOFFS bits from the GmSt chunk and the BattleGroupRef + from the GlSt chunk. From there we can see our way to eradicating + the state file abstraction entirely, but that will be quite a bit + more work. From fe0a9a11267a9ba0c959d2a16daeeca54dabf840 Mon Sep 17 00:00:00 2001 From: Michael Martin Date: Fri, 18 Oct 2013 22:38:39 -0700 Subject: [PATCH 16/22] Expose some of the statefile structure to load and save. --- sc2/src/uqm/grpinfo.c | 49 ++++++------------------------------ sc2/src/uqm/grpintrn.h | 56 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 42 deletions(-) create mode 100644 sc2/src/uqm/grpintrn.h diff --git a/sc2/src/uqm/grpinfo.c b/sc2/src/uqm/grpinfo.c index dc7be0f4c..43f1b22b6 100644 --- a/sc2/src/uqm/grpinfo.c +++ b/sc2/src/uqm/grpinfo.c @@ -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 */ diff --git a/sc2/src/uqm/grpintrn.h b/sc2/src/uqm/grpintrn.h new file mode 100644 index 000000000..d2136a507 --- /dev/null +++ b/sc2/src/uqm/grpintrn.h @@ -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 From c30fd90cb444ca87278414e81139796d4074052b Mon Sep 17 00:00:00 2001 From: Michael Martin Date: Sat, 19 Oct 2013 01:29:02 -0700 Subject: [PATCH 17/22] Intermediate testing save format. Rationalized statefile chunks are now written out alongside the original statefile chunks. The loader still relies on the latter for now. This will make correctness cross-checking a bit easier. --- sc2/src/uqm/save.c | 147 ++++++++++++++++++++++++++++++++++++++++++++- sc2/src/uqm/save.h | 3 + 2 files changed, 148 insertions(+), 2 deletions(-) diff --git a/sc2/src/uqm/save.c b/sc2/src/uqm/save.c index 1345be27b..d05a10c98 100644 --- a/sc2/src/uqm/save.c +++ b/sc2/src/uqm/save.c @@ -32,7 +32,7 @@ #include "shipcont.h" #include "setup.h" #include "state.h" -#include "grpinfo.h" +#include "grpintrn.h" #include "util.h" #include "hyper.h" // for SaveSisHyperState() @@ -613,6 +613,146 @@ SaveStateFile (DWORD statefileId, DWORD tag, uio_Stream *fh) } } +static void +SaveStarInfo (uio_Stream *fh) +{ + GAME_STATE_FILE *fp; + fp = OpenStateFile (STARINFO_FILE, "rb"); + if (fp) + { + DWORD flen = LengthStateFile (fp); + if (flen % 4) + { + log_add (log_Warning, "Unexpected Star Info length! Expected an integral number of DWORDS.\n"); + } + else + { + write_32 (fh, SCAN_TAG); + write_32 (fh, flen); + while (flen) + { + DWORD val; + sread_32 (fp, &val); + write_32 (fh, val); + flen -= 4; + } + } + CloseStateFile (fp); + } +} + +static void +SaveBattleGroup (GAME_STATE_FILE *fp, DWORD encounter_id, DWORD grpoffs, uio_Stream *fh) +{ + GROUP_HEADER h; + DWORD size = 12; + int i; + SeekStateFile (fp, grpoffs, SEEK_SET); + ReadGroupHeader (fp, &h); + for (i = 1; i <= h.NumGroups; ++i) + { + BYTE NumShips; + SeekStateFile (fp, h.GroupOffset[i], SEEK_SET); + sread_8 (fp, NULL); + sread_8 (fp, &NumShips); + size += 2 + 10 * NumShips; + } + write_32 (fh, BATTLE_GROUP_TAG); + write_32 (fh, size); + write_32 (fh, encounter_id); + write_8 (fh, (grpoffs && (GLOBAL (BattleGroupRef) == grpoffs)) ? 1 : 0); // current + write_16 (fh, h.star_index); + write_8 (fh, h.day_index); + write_8 (fh, h.month_index); + write_16 (fh, h.year_index); + write_8 (fh, h.NumGroups); + for (i = 1; i <= h.NumGroups; ++i) + { + int j; + BYTE b; + SeekStateFile (fp, h.GroupOffset[i], SEEK_SET); + sread_8 (fp, &b); // Group race icon + write_8 (fh, b); + sread_8 (fp, &b); // NumShips + write_8 (fh, b); + for (j = 0; j < b; ++j) + { + BYTE race_outer; + SHIP_FRAGMENT sf; + sread_8 (fp, &race_outer); + ReadShipFragment (fp, &sf); + write_8 (fh, race_outer); + write_8 (fh, sf.captains_name_index); + write_8 (fh, sf.race_id); + write_8 (fh, sf.index); + write_16 (fh, sf.crew_level); + write_16 (fh, sf.max_crew); + write_8 (fh, sf.energy_level); + write_8 (fh, sf.max_energy); + } + } +} + +static void +SaveGroups (uio_Stream *fh) +{ + GAME_STATE_FILE *fp; + fp = OpenStateFile (RANDGRPINFO_FILE, "rb"); + if (fp && LengthStateFile (fp) > 0) + { + GROUP_HEADER h; + BYTE lastenc, count; + int i; + ReadGroupHeader (fp, &h); + /* Group List */ + SeekStateFile (fp, h.GroupOffset[0], SEEK_SET); + sread_8 (fp, &lastenc); + sread_8 (fp, &count); + write_32 (fh, GROUP_LIST_TAG); + write_32 (fh, 2 + 14 * count); // Chunk size + write_8 (fh, lastenc); + write_8 (fh, count); + for (i = 0; i < count; ++i) + { + BYTE race_outer; + IP_GROUP ip; + sread_8 (fp, &race_outer); + ReadIpGroup (fp, &ip); + + write_8 (fh, race_outer); + write_16 (fh, ip.group_counter); + write_8 (fh, ip.race_id); + write_8 (fh, ip.sys_loc); + write_8 (fh, ip.task); + write_8 (fh, ip.in_system); + write_8 (fh, ip.dest_loc); + write_8 (fh, ip.orbit_pos); + write_8 (fh, ip.group_id); + write_16 (fh, ip.loc.x); + write_16 (fh, ip.loc.y); + } + SaveBattleGroup (fp, 0, 0, fh); + CloseStateFile (fp); + } + fp = OpenStateFile (DEFGRPINFO_FILE, "rb"); + if (fp && LengthStateFile (fp) > 0) + { + int state_index = SHOFIXTI_GRPOFFS0; + int encounter_index = 1; + while (state_index < NUM_GAME_STATE_BITS) + { + DWORD grpoffs = GET_GAME_STATE_32 (state_index); + if (grpoffs) + { + SaveBattleGroup (fp, encounter_index, grpoffs, fh); + } + ++encounter_index; + state_index += 32; + } + CloseStateFile (fp); + } +} + // This function first writes to a memory file, and then writes the whole // lot to the actual save file at once. BOOLEAN @@ -680,7 +820,10 @@ SaveGame (COUNT which_game, SUMMARY_DESC *SummPtr, const char *name) SaveStateFile (STARINFO_FILE, STAR_SF_TAG, out_fp); SaveStateFile (DEFGRPINFO_FILE, DEFGRP_SF_TAG, out_fp); SaveStateFile (RANDGRPINFO_FILE, RANDGRP_SF_TAG, out_fp); - + + SaveStarInfo (out_fp); + SaveGroups (out_fp); + // Save out the Star Descriptor SaveStarDesc (&SD, out_fp); diff --git a/sc2/src/uqm/save.h b/sc2/src/uqm/save.h index 3d0ac1329..c917ca8d8 100644 --- a/sc2/src/uqm/save.h +++ b/sc2/src/uqm/save.h @@ -45,6 +45,9 @@ extern "C" { #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 #define STAR_SF_TAG 0x46534953 // "SISF": Star Info State File #define DEFGRP_SF_TAG 0x46534744 // "DGSF": Defined Group State File #define RANDGRP_SF_TAG 0x46534752 // "RGSF": Random Group State File From f271e8270cc21ca574165a0a3769af722994eee5 Mon Sep 17 00:00:00 2001 From: Michael Martin Date: Sat, 19 Oct 2013 15:52:19 -0700 Subject: [PATCH 18/22] Tentative loading logic for the new statefile format. --- sc2/src/uqm/load.c | 178 ++++++++++++++++++++++++++++++++++++++++++++- sc2/src/uqm/save.c | 3 +- 2 files changed, 178 insertions(+), 3 deletions(-) diff --git a/sc2/src/uqm/load.c b/sc2/src/uqm/load.c index 48f0d4312..96075f8a8 100644 --- a/sc2/src/uqm/load.c +++ b/sc2/src/uqm/load.c @@ -27,7 +27,7 @@ #include "save.h" #include "setup.h" #include "state.h" -#include "grpinfo.h" +#include "grpintrn.h" #include "libs/tasklib.h" #include "libs/log.h" @@ -467,6 +467,160 @@ LoadStateFile (int file_type, void *fh, DWORD flen) } } +static void +LoadScanInfo (uio_Stream *fh, DWORD flen) +{ + GAME_STATE_FILE *fp = OpenStateFile (STARINFO_FILE, "wb"); + if (fp) + { + while (flen) + { + DWORD val; + read_32 (fh, &val); + swrite_32 (fp, val); + flen -= 4; + } + CloseStateFile (fp); + } +} + +static void +LoadGroupList (uio_Stream *fh, DWORD chunksize) +{ + GAME_STATE_FILE *fp = OpenStateFile (RANDGRPINFO_FILE, "rb"); + if (fp) + { + GROUP_HEADER h; + BYTE LastEnc, NumGroups; + int i; + ReadGroupHeader (fp, &h); + /* There's only supposed to be one of these, so group 0 should be + * zero here whenever we're here. We add the group list to the + * end here. */ + h.GroupOffset[0] = LengthStateFile (fp); + SeekStateFile (fp, 0, SEEK_SET); + WriteGroupHeader (fp, &h); + SeekStateFile (fp, h.GroupOffset[0], SEEK_SET); + read_8 (fh, &LastEnc); + NumGroups = (chunksize - 1) / 14; + swrite_8 (fp, LastEnc); + swrite_8 (fp, NumGroups); + for (i = 0; i < NumGroups; ++i) + { + BYTE race_outer; + IP_GROUP ip; + read_8 (fh, &race_outer); + read_16 (fh, &ip.group_counter); + read_8 (fh, &ip.race_id); + read_8 (fh, &ip.sys_loc); + read_8 (fh, &ip.task); + read_8 (fh, &ip.in_system); + read_8 (fh, &ip.dest_loc); + read_8 (fh, &ip.orbit_pos); + read_8 (fh, &ip.group_id); + read_16 (fh, &ip.loc.x); + read_16 (fh, &ip.loc.y); + + swrite_8 (fp, race_outer); + WriteIpGroup (fp, &ip); + } + CloseStateFile (fp); + } +} + +static void +LoadBattleGroup (uio_Stream *fh, DWORD chunksize) +{ + GAME_STATE_FILE *fp; + GROUP_HEADER h; + DWORD encounter, offset; + BYTE current; + int i; + + read_32 (fh, &encounter); + read_8 (fh, ¤t); + chunksize -= 5; + if (encounter) + { + /* This is a defined group, so it's new */ + fp = OpenStateFile (DEFGRPINFO_FILE, "rb"); + offset = LengthStateFile (fp); + memset (&h, 0, sizeof (GROUP_HEADER)); + } + else + { + /* This is the random group. Load in what was there, + * as we might have already seen the Group List. */ + fp = OpenStateFile (RANDGRPINFO_FILE, "rb"); + current = FALSE; + offset = 0; + ReadGroupHeader (fp, &h); + } + if (!fp) + { + skip_8 (fh, chunksize); + return; + } + read_16 (fh, &h.star_index); + read_8 (fh, &h.day_index); + read_8 (fh, &h.month_index); + read_16 (fh, &h.year_index); + read_8 (fh, &h.NumGroups); + chunksize -= 7; + /* Write out the half-finished state file so that we can use + * the file size to compute group offsets */ + SeekStateFile (fp, offset, SEEK_SET); + WriteGroupHeader (fp, &h); + for (i = 1; i <= h.NumGroups; ++i) + { + int j; + BYTE icon, NumShips; + read_8 (fh, &icon); + read_8 (fh, &NumShips); + chunksize -= 2; + h.GroupOffset[i] = LengthStateFile (fp); + SeekStateFile (fp, h.GroupOffset[i], SEEK_SET); + swrite_8 (fp, icon); + swrite_8 (fp, NumShips); + for (j = 0; j < NumShips; ++j) + { + BYTE race_outer; + SHIP_FRAGMENT sf; + read_8 (fh, &race_outer); + read_8 (fh, &sf.captains_name_index); + read_8 (fh, &sf.race_id); + read_8 (fh, &sf.index); + read_16 (fh, &sf.crew_level); + read_16 (fh, &sf.max_crew); + read_8 (fh, &sf.energy_level); + read_8 (fh, &sf.max_energy); + chunksize -= 10; + + swrite_8 (fp, race_outer); + WriteShipFragment (fp, &sf); + } + } + /* Now that the GroupOffset array is properly initialized, + * write the header back out. */ + SeekStateFile (fp, offset, SEEK_SET); + WriteGroupHeader (fp, &h); + CloseStateFile (fp); + /* And update the gamestate accordingly, if we're a defined group. */ + if (encounter) + { + SET_GAME_STATE_32 (SHOFIXTI_GRPOFFS0 + (encounter - 1) * 32, offset); + if (current) + { + GLOBAL (BattleGroupRef) = offset; + } + } + /* Consistency check. */ + if (chunksize) + { + log_add (log_Warning, "BattleGroup chunk mis-sized!"); + } +} + BOOLEAN LoadGame (COUNT which_game, SUMMARY_DESC *SummPtr) { @@ -477,6 +631,7 @@ LoadGame (COUNT which_game, SUMMARY_DESC *SummPtr) STAR_DESC SD; ACTIVITY Activity; DWORD chunk, chunkSize; + BOOLEAN first_group_spec = TRUE; sprintf (file, "uqmsave.%02u", which_game); in_fp = res_OpenResFile (saveDir, file, "rb"); @@ -589,6 +744,27 @@ LoadGame (COUNT which_game, SUMMARY_DESC *SummPtr) case SHIP_Q_TAG: LoadShipQueue (in_fp, &GLOBAL (built_ship_q), chunkSize); break; + case SCAN_TAG: + LoadScanInfo (in_fp, chunkSize); + break; + case GROUP_LIST_TAG: + if (first_group_spec) + { + InitGroupInfo (TRUE); + GLOBAL (BattleGroupRef) = 0; + first_group_spec = FALSE; + } + LoadGroupList (in_fp, chunkSize); + break; + case BATTLE_GROUP_TAG: + if (first_group_spec) + { + InitGroupInfo (TRUE); + GLOBAL (BattleGroupRef) = 0; + first_group_spec = FALSE; + } + LoadBattleGroup (in_fp, chunkSize); + break; case STAR_SF_TAG: LoadStateFile (STARINFO_FILE, in_fp, chunkSize); break; diff --git a/sc2/src/uqm/save.c b/sc2/src/uqm/save.c index d05a10c98..6976ddac0 100644 --- a/sc2/src/uqm/save.c +++ b/sc2/src/uqm/save.c @@ -709,9 +709,8 @@ SaveGroups (uio_Stream *fh) sread_8 (fp, &lastenc); sread_8 (fp, &count); write_32 (fh, GROUP_LIST_TAG); - write_32 (fh, 2 + 14 * count); // Chunk size + write_32 (fh, 1 + 14 * count); // Chunk size write_8 (fh, lastenc); - write_8 (fh, count); for (i = 0; i < count; ++i) { BYTE race_outer; From f66cbbcd89888540858137c6885d4e1284d5b95a Mon Sep 17 00:00:00 2001 From: Michael Martin Date: Sat, 19 Oct 2013 16:59:36 -0700 Subject: [PATCH 19/22] Finalize the work on the new save file format. Remove all references to state files from the format. Some still leaks in in the GmSt chunk, but those are ignored or overwritten by later loading phases. --- sc2/doc/devel/savefile | 99 ++++++++++++++++++------------------------ sc2/src/uqm/load.c | 34 +-------------- sc2/src/uqm/save.c | 14 +++--- sc2/src/uqm/save.h | 3 -- 4 files changed, 49 insertions(+), 101 deletions(-) diff --git a/sc2/doc/devel/savefile b/sc2/doc/devel/savefile index 8b7b4a577..306c1ab10 100644 --- a/sc2/doc/devel/savefile +++ b/sc2/doc/devel/savefile @@ -1,5 +1,5 @@ - SAVEFILE FORMAT - --------------- + SAVEFILE FORMAT + --------------- This document represents a work in progress. The save format described here will evolve before finalization for 0.8. @@ -10,8 +10,8 @@ anymore. It was also extremely fragile and hard for modders to extend. The new save format seeks to alleviate these problems. - GENERAL FORMAT - -------------- + GENERAL FORMAT + -------------- All multibyte values are little-endian. There are no alignment restrictions inherent in the format. @@ -60,8 +60,8 @@ depending on the situation you saved in. Unless otherwise specified, chunks may be stored in any order in the save file. - CHUNK INVENTORY - --------------- + CHUNK INVENTORY + --------------- - "Summ": Summary. This chunk must come first. This chunk carries the flagship configuration information and some overview @@ -71,7 +71,8 @@ save file. - "GlSt": Global State. This chunk must come second, after Summ. Represents most of the data in the global state structure in - globdata.h. + 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 @@ -107,56 +108,42 @@ save file. - "Star": Star Description. Basic indexing information to indicate which star system you are in. -- "SISF": Star Info State File. An index into which planetside - resources you have investigated and collected. See - doc/devel/statefiles for more details. +- "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. -- "DGSF": Defined Group State File. Battlegroup information for - space-based encounters dictated by the plot. See - doc/devel/statefiles for more details. +- "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.) -- "RGSF": Random Group State File. Battlegroup information for random - encounters in interplanetary space. See doc/devel/statefiles for - more details. +- "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. -- "Scan": Scanner Masks. NOT YET IMPLEMENTED; WILL REPLACE SISF. It - might just be a rename with better endianness enforcement, though; - the SISF dump is pretty well-structured as it is. + THINGS LEFT TO DO + ----------------- -- "DGrp": Defined Group. NOT YET IMPLEMENTED; WILL REPLACE DGSF. Will - carry an enumeration id to indicate which of the defgrps it is - (that's currently intrinsic to the game state bit array) and will - also have a bit indicating whether or not it is the "current" - defgrp. If no defgrp is current, BattleGroupRef is 0; otherwise, - BattleGroupRef is the defgrp file offset that corresponds to the - current system. There will be one DGrp chunk for each system with an - active defgrp record. - -- NOT YET DESIGNED: RGSF also needs to be replaced with one or more - chunk types. This will likely be similar to the DGrp chunks, but - there are stronger constraints on repetition here. - - THINGS LEFT TO DO BEFORE MERGING - -------------------------------- - -- Actually enforce little-endianness of everything that hits the disk. - - Everything that isn't a State File now respects this. - - State files are going to require more work (see below). - -- There are 448 bits in GmSt that are actually indices into DGSF. They - shouldn't be there. In fact, they shouldn't even be in the - GAME_STATE array either. They should be an array of DWORDs living - independently in the global state - - In preparation for this, the GRPOFFS state has been moved to the - end of the state array. We'll be able to eradicate it once the - state files are no longer part of the save. - -- The State Files are (except maybe for Star Info) a horrible mess and - we should not be replicating them in the save file. We should - instead be regenerating them from more structured forms. - - Once we do this we can start computing new values for the GRPOFFS - state and the BattleGroupRef based on the new chunks. We can then - remove the GRPOFFS bits from the GmSt chunk and the BattleGroupRef - from the GlSt chunk. From there we can see our way to eradicating - the state file abstraction entirely, but that will be quite a bit - more work. +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. diff --git a/sc2/src/uqm/load.c b/sc2/src/uqm/load.c index 96075f8a8..9d434c4b2 100644 --- a/sc2/src/uqm/load.c +++ b/sc2/src/uqm/load.c @@ -298,7 +298,7 @@ LoadGameState (GAME_STATE *GSPtr, void *fh) return FALSE; } read_32 (fh, &magic); - if (magic != 79) + if (magic != 75) { /* Chunk is the wrong size. */ return FALSE; @@ -334,8 +334,6 @@ LoadGameState (GAME_STATE *GSPtr, void *fh) read_16s (fh, &GSPtr->velocity.incr.width); read_16s (fh, &GSPtr->velocity.incr.height); - read_32 (fh, &GSPtr->BattleGroupRef); - read_32 (fh, &magic); if (magic != GAME_STATE_TAG) { @@ -446,27 +444,6 @@ LoadStarDesc (STAR_DESC *SDPtr, void *fh) read_8 (fh, &SDPtr->Postfix); } -static void -LoadStateFile (int file_type, void *fh, DWORD flen) -{ - GAME_STATE_FILE *fp = OpenStateFile (file_type, "wb"); - char buf[256]; - if (fp) - { - while (flen) - { - COUNT num_bytes; - - num_bytes = flen >= sizeof (buf) ? sizeof (buf) : (COUNT)flen; - read_a8 (fh, buf, num_bytes); - WriteStateFile (buf, num_bytes, 1, fp); - - flen -= num_bytes; - } - CloseStateFile (fp); - } -} - static void LoadScanInfo (uio_Stream *fh, DWORD flen) { @@ -765,15 +742,6 @@ LoadGame (COUNT which_game, SUMMARY_DESC *SummPtr) } LoadBattleGroup (in_fp, chunkSize); break; - case STAR_SF_TAG: - LoadStateFile (STARINFO_FILE, in_fp, chunkSize); - break; - case DEFGRP_SF_TAG: - LoadStateFile (DEFGRPINFO_FILE, in_fp, chunkSize); - break; - case RANDGRP_SF_TAG: - LoadStateFile (RANDGRPINFO_FILE, in_fp, chunkSize); - break; default: log_add (log_Debug, "Skipping chunk of tag %08X (size %u)", chunk, chunkSize); if (skip_8(in_fp, chunkSize) != 1) diff --git a/sc2/src/uqm/save.c b/sc2/src/uqm/save.c index 6976ddac0..102b0309a 100644 --- a/sc2/src/uqm/save.c +++ b/sc2/src/uqm/save.c @@ -330,7 +330,7 @@ static void SaveGameState (const GAME_STATE *GSPtr, uio_Stream *fh) { write_32 (fh, GLOBAL_STATE_TAG); - write_32 (fh, 79); + write_32 (fh, 75); write_8 (fh, GSPtr->glob_flags); write_8 (fh, GSPtr->CrewCost); write_8 (fh, GSPtr->FuelCost); @@ -362,9 +362,9 @@ SaveGameState (const GAME_STATE *GSPtr, uio_Stream *fh) write_16 (fh, GSPtr->velocity.incr.width); write_16 (fh, GSPtr->velocity.incr.height); - write_32 (fh, GSPtr->BattleGroupRef); - - /* The Game state bits. Vanilla UQM uses 155 bytes here. */ + /* The Game state bits. Vanilla UQM uses 155 bytes here at + * present. Only the first 99 bytes are significant, though; + * the rest will be overwritten by the BtGp chunks. */ write_32 (fh, GAME_STATE_TAG); write_32 (fh, sizeof (GSPtr->GameState)); write_a8 (fh, GSPtr->GameState, sizeof (GSPtr->GameState)); @@ -815,11 +815,7 @@ SaveGame (COUNT which_game, SUMMARY_DESC *SummPtr, const char *name) // Save the encounter chunk (black globes in HS/QS) SaveEncounters (out_fp); - // Save out the state file chunks. - SaveStateFile (STARINFO_FILE, STAR_SF_TAG, out_fp); - SaveStateFile (DEFGRPINFO_FILE, DEFGRP_SF_TAG, out_fp); - SaveStateFile (RANDGRPINFO_FILE, RANDGRP_SF_TAG, out_fp); - + // Save out the data that used to be in state files SaveStarInfo (out_fp); SaveGroups (out_fp); diff --git a/sc2/src/uqm/save.h b/sc2/src/uqm/save.h index c917ca8d8..c6b4fc972 100644 --- a/sc2/src/uqm/save.h +++ b/sc2/src/uqm/save.h @@ -48,9 +48,6 @@ extern "C" { #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 -#define STAR_SF_TAG 0x46534953 // "SISF": Star Info State File -#define DEFGRP_SF_TAG 0x46534744 // "DGSF": Defined Group State File -#define RANDGRP_SF_TAG 0x46534752 // "RGSF": Random Group State File typedef struct { From f9d27fbbe6b0824dce8566c0897b9d5bced44087 Mon Sep 17 00:00:00 2001 From: Serge van den Boom Date: Wed, 23 Oct 2013 21:51:13 +0200 Subject: [PATCH 20/22] Use internal libmikmod by default. --- sc2/build/unix/build.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sc2/build/unix/build.config b/sc2/build/unix/build.config index 1b8fd702d..c1c6ec3c9 100644 --- a/sc2/build/unix/build.config +++ b/sc2/build/unix/build.config @@ -280,7 +280,7 @@ uqm_prepare_config() USE_INTERNAL_MIKMOD="" use_library libmikmod } - CHOICE_mikmod_DEFAULT=external + CHOICE_mikmod_DEFAULT=internal CHOICE_joystick_OPTIONS="enabled disabled" CHOICE_joystick_TITLE="Joystick support" From 573755dcb53b71f53aedc34044ab655f9fceb11c Mon Sep 17 00:00:00 2001 From: Serge van den Boom Date: Sat, 2 Nov 2013 00:12:02 +0100 Subject: [PATCH 21/22] Alternative for Subversion '$Date$'. --- sc2/.gitattributes | 1 + sc2/content/version | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 sc2/.gitattributes diff --git a/sc2/.gitattributes b/sc2/.gitattributes new file mode 100644 index 000000000..79ab785b5 --- /dev/null +++ b/sc2/.gitattributes @@ -0,0 +1 @@ +content/version export-subst diff --git a/sc2/content/version b/sc2/content/version index 5a41a146b..d75b5b1d8 100644 --- a/sc2/content/version +++ b/sc2/content/version @@ -1,3 +1,3 @@ 0.7.0 -$Date$ +$Format:%ad$ From 7326ff096d0fc60086f2a6885fdc05aa707dd9fd Mon Sep 17 00:00:00 2001 From: Serge van den Boom Date: Sat, 2 Nov 2013 21:59:56 +0100 Subject: [PATCH 22/22] Grouping ship properties together at the top of the ship .c files, from oldlaptop. --- sc2/ChangeLog | 2 + sc2/src/uqm/ships/androsyn/androsyn.c | 47 ++++++------- sc2/src/uqm/ships/arilou/arilou.c | 19 +++--- sc2/src/uqm/ships/blackurq/blackurq.c | 64 +++++++++--------- sc2/src/uqm/ships/chenjesu/chenjesu.c | 67 +++++++++--------- sc2/src/uqm/ships/chmmr/chmmr.c | 39 ++++++----- sc2/src/uqm/ships/druuge/druuge.c | 29 ++++---- sc2/src/uqm/ships/human/human.c | 38 ++++++----- sc2/src/uqm/ships/ilwrath/ilwrath.c | 30 +++++---- sc2/src/uqm/ships/lastbat/lastbat.c | 96 +++++++++++++------------- sc2/src/uqm/ships/melnorme/melnorme.c | 43 ++++++------ sc2/src/uqm/ships/mmrnmhrm/mmrnmhrm.c | 48 +++++++------ sc2/src/uqm/ships/mycon/mycon.c | 27 ++++---- sc2/src/uqm/ships/orz/orz.c | 55 ++++++++------- sc2/src/uqm/ships/pkunk/pkunk.c | 37 +++++----- sc2/src/uqm/ships/shofixti/shofixti.c | 31 ++++++--- sc2/src/uqm/ships/sis_ship/sis_ship.c | 97 +++++++++++++++++++-------- sc2/src/uqm/ships/slylandr/slylandr.c | 27 +++++--- sc2/src/uqm/ships/spathi/spathi.c | 40 ++++++----- sc2/src/uqm/ships/supox/supox.c | 27 +++++--- sc2/src/uqm/ships/syreen/syreen.c | 29 ++++---- sc2/src/uqm/ships/thradd/thradd.c | 60 ++++++++++------- sc2/src/uqm/ships/umgah/umgah.c | 41 +++++------ sc2/src/uqm/ships/urquan/urquan.c | 57 ++++++++-------- sc2/src/uqm/ships/utwig/utwig.c | 35 +++++----- sc2/src/uqm/ships/vux/vux.c | 49 ++++++++------ sc2/src/uqm/ships/yehat/yehat.c | 29 ++++---- sc2/src/uqm/ships/zoqfot/zoqfot.c | 41 ++++++----- 28 files changed, 675 insertions(+), 529 deletions(-) diff --git a/sc2/ChangeLog b/sc2/ChangeLog index d2aa75e14..7e08d244e 100644 --- a/sc2/ChangeLog +++ b/sc2/ChangeLog @@ -1,4 +1,6 @@ Changes towards version 0.8: +- Grouping ship properties together at the top of the ship .c files, from + oldlaptop. - Some added documentation for UIO. - Added a StringHashTable type. - A few superficial fixes to charhashtable. diff --git a/sc2/src/uqm/ships/androsyn/androsyn.c b/sc2/src/uqm/ships/androsyn/androsyn.c index 2625b3f80..1139d8da0 100644 --- a/sc2/src/uqm/ships/androsyn/androsyn.c +++ b/sc2/src/uqm/ships/androsyn/androsyn.c @@ -22,22 +22,38 @@ #include "libs/mathlib.h" - +// Core characteristics #define MAX_CREW 20 #define MAX_ENERGY 24 #define ENERGY_REGENERATION 1 -#define WEAPON_ENERGY_COST 3 -#define SPECIAL_ENERGY_COST 2 #define ENERGY_WAIT 8 #define MAX_THRUST 24 #define THRUST_INCREMENT 3 #define TURN_WAIT 4 #define THRUST_WAIT 0 -#define WEAPON_WAIT 0 -#define SPECIAL_WAIT 0 - #define SHIP_MASS 6 +// Bubbles +#define WEAPON_ENERGY_COST 3 +#define WEAPON_WAIT 0 +#define ANDROSYNTH_OFFSET 14 +#define MISSILE_OFFSET 3 +#define MISSILE_SPEED DISPLAY_TO_WORLD (8) +#define MISSILE_LIFE 200 +#define MISSILE_HITS 3 +#define MISSILE_DAMAGE 2 +#define TRACK_WAIT 2 + +// Blazer +#define SPECIAL_ENERGY_COST 2 +#define BLAZER_DEGENERATION (-1) +#define SPECIAL_WAIT 0 +#define BLAZER_OFFSET 10 +#define BLAZER_THRUST 60 +#define BLAZER_TURN_WAIT 1 +#define BLAZER_DAMAGE 3 +#define BLAZER_MASS 1 + static RACE_DESC androsynth_desc = { { /* SHIP_INFO */ @@ -149,15 +165,10 @@ SetCustomShipData (RACE_DESC *pRaceDesc, const CustomShipData_t *data) } } - -#define BLAZER_DAMAGE 3 -#define BLAZER_MASS 1 - static void blazer_collision (ELEMENT *ElementPtr0, POINT *pPt0, ELEMENT *ElementPtr1, POINT *pPt1) { -#define BLAZER_OFFSET 10 BYTE old_offs; COUNT old_crew_level; COUNT old_life; @@ -177,8 +188,6 @@ blazer_collision (ELEMENT *ElementPtr0, POINT *pPt0, collision (ElementPtr0, pPt0, ElementPtr1, pPt1); } -#define MISSILE_SPEED DISPLAY_TO_WORLD (8) - static void bubble_preprocess (ELEMENT *ElementPtr) { @@ -214,23 +223,15 @@ bubble_preprocess (ELEMENT *ElementPtr) facing -= (COUNT)TFB_Random () & (ANGLE_TO_FACING (HALF_CIRCLE) - 1); SetVelocityVector (&ElementPtr->velocity, MISSILE_SPEED, facing); - -#define TRACK_WAIT 2 turn_wait = TRACK_WAIT; } ElementPtr->turn_wait = MAKE_BYTE (turn_wait, thrust_wait); } -#define MISSILE_DAMAGE 2 -#define MISSILE_LIFE 200 - static COUNT initialize_bubble (ELEMENT *ShipPtr, HELEMENT BubbleArray[]) { -#define ANDROSYNTH_OFFSET 14 -#define MISSILE_OFFSET 3 -#define MISSILE_HITS 3 STARSHIP *StarShipPtr; MISSILE_BLOCK MissileBlock; @@ -364,8 +365,6 @@ androsynth_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern, } } -#define BLAZER_TURN_WAIT 1 - static void androsynth_postprocess (ELEMENT *ElementPtr) { @@ -375,7 +374,6 @@ androsynth_postprocess (ELEMENT *ElementPtr) /* take care of blazer effect */ if (ElementPtr->next.image.farray == StarShipPtr->RaceDescPtr->ship_data.special) { -#define BLAZER_DEGENERATION (-1) if ((StarShipPtr->cur_status_flags & SPECIAL) || StarShipPtr->RaceDescPtr->ship_info.energy_level == 0) { @@ -484,7 +482,6 @@ androsynth_preprocess (ELEMENT *ElementPtr) --ElementPtr->thrust_wait; else { -#define BLAZER_THRUST 60 COUNT facing; facing = StarShipPtr->ShipFacing; diff --git a/sc2/src/uqm/ships/arilou/arilou.c b/sc2/src/uqm/ships/arilou/arilou.c index 5b32bcd3b..78340a37a 100644 --- a/sc2/src/uqm/ships/arilou/arilou.c +++ b/sc2/src/uqm/ships/arilou/arilou.c @@ -22,24 +22,28 @@ #include "libs/mathlib.h" - +// Core characteristics #define MAX_CREW 6 #define MAX_ENERGY 20 #define ENERGY_REGENERATION 1 -#define WEAPON_ENERGY_COST 2 -#define SPECIAL_ENERGY_COST 3 #define ENERGY_WAIT 6 #define MAX_THRUST /* DISPLAY_TO_WORLD (10) */ 40 #define THRUST_INCREMENT MAX_THRUST -#define TURN_WAIT 0 #define THRUST_WAIT 0 -#define WEAPON_WAIT 1 -#define SPECIAL_WAIT 2 - +#define TURN_WAIT 0 #define SHIP_MASS 1 + +// Tracking Laser +#define WEAPON_ENERGY_COST 2 +#define WEAPON_WAIT 1 #define ARILOU_OFFSET 9 #define LASER_RANGE DISPLAY_TO_WORLD (100 + ARILOU_OFFSET) +// Teleporter +#define SPECIAL_ENERGY_COST 3 +#define SPECIAL_WAIT 2 +#define HYPER_LIFE 5 + static RACE_DESC arilou_desc = { { /* SHIP_INFO */ @@ -209,7 +213,6 @@ arilou_preprocess (ELEMENT *ElementPtr) && DeltaEnergy (ElementPtr, -SPECIAL_ENERGY_COST)) { /* Special key is pressed; start teleport */ -#define HYPER_LIFE 5 ZeroVelocityComponents (&ElementPtr->velocity); StarShipPtr->cur_status_flags &= ~(SHIP_AT_MAX_SPEED | LEFT | RIGHT | THRUST | WEAPON); diff --git a/sc2/src/uqm/ships/blackurq/blackurq.c b/sc2/src/uqm/ships/blackurq/blackurq.c index c05591d58..286191dd0 100644 --- a/sc2/src/uqm/ships/blackurq/blackurq.c +++ b/sc2/src/uqm/ships/blackurq/blackurq.c @@ -22,25 +22,48 @@ #include "uqm/globdata.h" - +// Core characteristics #define MAX_CREW MAX_CREW_SIZE #define MAX_ENERGY MAX_ENERGY_SIZE #define ENERGY_REGENERATION 1 -#define WEAPON_ENERGY_COST 6 -#define SPECIAL_ENERGY_COST (MAX_ENERGY_SIZE / 2) #define ENERGY_WAIT 4 #define MAX_THRUST 30 #define THRUST_INCREMENT 6 #define TURN_WAIT 4 #define THRUST_WAIT 6 -#define WEAPON_WAIT 6 -#define SPECIAL_WAIT 9 - #define SHIP_MASS 10 + +// Buzzsaw +#define WEAPON_ENERGY_COST 6 +#define WEAPON_WAIT 6 +#define MISSILE_OFFSET 9 +#define KOHR_AH_OFFSET 28 #define MISSILE_SPEED 64 -#define MISSILE_LIFE 64 /* actually, it's as long as you - * hold the button down. - */ +#define MISSILE_LIFE 64 + /* actually, it's as long as you hold the button down.*/ +#define MISSILE_HITS 10 +#define MISSILE_DAMAGE 4 +#define SAW_RATE 0 +#define MAX_SAWS 8 +#define ACTIVATE_RANGE 224 + /* Originally SPACE_WIDTH - the distance within which + * stationary sawblades will home */ +#define TRACK_WAIT 4 +#define FRAGMENT_SPEED MISSILE_SPEED +#define FRAGMENT_LIFE 10 +#define FRAGMENT_RANGE (FRAGMENT_LIFE * FRAGMENT_SPEED) + +// F.R.I.E.D. +#define SPECIAL_ENERGY_COST (MAX_ENERGY_SIZE / 2) +#define SPECIAL_WAIT 9 +#define GAS_OFFSET 2 +#define GAS_SPEED 16 +#define GAS_RATE 2 /* Controls animation of the gas cloud decay - the decay + * animation advances one frame every GAS_RATE frames. */ +#define GAS_HITS 100 +#define GAS_DAMAGE 3 +#define GAS_ALT_DAMAGE 50 +#define NUM_GAS_CLOUDS 16 static RACE_DESC black_urquan_desc = { @@ -113,9 +136,6 @@ static RACE_DESC black_urquan_desc = 0, /* CodeRef */ }; -#define SAW_RATE 0 -#define MAX_SAWS 8 - static void spin_preprocess (ELEMENT *ElementPtr) { @@ -154,8 +174,6 @@ spin_preprocess (ELEMENT *ElementPtr) UnlockElement (StarShipPtr->hShip); } -#define TRACK_WAIT 4 - static void buzztrack_preprocess (ELEMENT *ElementPtr) { @@ -172,7 +190,6 @@ buzztrack_preprocess (ELEMENT *ElementPtr) } else { -#define ACTIVATE_RANGE 224 /* Originally SPACE_WIDTH */ SIZE delta_x, delta_y; ELEMENT *eptr; @@ -311,10 +328,6 @@ buzzsaw_postprocess (ELEMENT *ElementPtr) static COUNT initialize_buzzsaw (ELEMENT *ShipPtr, HELEMENT SawArray[]) { -#define MISSILE_HITS 10 -#define MISSILE_DAMAGE 4 -#define MISSILE_OFFSET 9 -#define KOHR_AH_OFFSET 28 STARSHIP *StarShipPtr; MISSILE_BLOCK MissileBlock; @@ -379,9 +392,6 @@ black_urquan_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern, lpEvalDesc = &ObjectsOfConcern[ENEMY_SHIP_INDEX]; if (lpEvalDesc->ObjectPtr) { -#define FRAGMENT_LIFE 10 -#define FRAGMENT_SPEED MISSILE_SPEED -#define FRAGMENT_RANGE (FRAGMENT_LIFE * FRAGMENT_SPEED) HELEMENT h, hNext; ELEMENT *BuzzSawPtr; @@ -433,8 +443,6 @@ black_urquan_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern, } } -#define GAS_RATE 2 - static void gas_cloud_preprocess (ELEMENT *ElementPtr) { @@ -450,8 +458,6 @@ gas_cloud_preprocess (ELEMENT *ElementPtr) } } -#define GAS_DAMAGE 3 - static void gas_cloud_collision (ELEMENT *ElementPtr0, POINT *pPt0, ELEMENT *ElementPtr1, POINT *pPt1) @@ -459,7 +465,7 @@ gas_cloud_collision (ELEMENT *ElementPtr0, POINT *pPt0, if (ElementPtr1->state_flags & PLAYER_SHIP) ElementPtr0->mass_points = GAS_DAMAGE; else - ElementPtr0->mass_points = 50; + ElementPtr0->mass_points = GAS_ALT_DAMAGE; weapon_collision (ElementPtr0, pPt0, ElementPtr1, pPt1); } @@ -467,10 +473,6 @@ gas_cloud_collision (ELEMENT *ElementPtr0, POINT *pPt0, static void spawn_gas_cloud (ELEMENT *ElementPtr) { -#define GAS_SPEED 16 -#define GAS_HITS 100 -#define GAS_OFFSET 2 -#define NUM_GAS_CLOUDS 16 SIZE dx, dy; STARSHIP *StarShipPtr; MISSILE_BLOCK MissileBlock; diff --git a/sc2/src/uqm/ships/chenjesu/chenjesu.c b/sc2/src/uqm/ships/chenjesu/chenjesu.c index 6d548ba33..85a101485 100644 --- a/sc2/src/uqm/ships/chenjesu/chenjesu.c +++ b/sc2/src/uqm/ships/chenjesu/chenjesu.c @@ -23,27 +23,48 @@ #include "uqm/globdata.h" #include "libs/mathlib.h" - +// Core characteristics #define MAX_CREW 36 #define MAX_ENERGY 30 #define ENERGY_REGENERATION 1 -#define WEAPON_ENERGY_COST 5 -#define SPECIAL_ENERGY_COST MAX_ENERGY #define ENERGY_WAIT 4 #define MAX_THRUST /* DISPLAY_TO_WORLD (7) */ 27 #define THRUST_INCREMENT /* DISPLAY_TO_WORLD (2) */ 3 -#define TURN_WAIT 6 #define THRUST_WAIT 4 -#define WEAPON_WAIT 0 -#define SPECIAL_WAIT 0 - -#define MAX_DOGGIES 4 - +#define TURN_WAIT 6 #define SHIP_MASS 10 + +// Photon Shard +#define WEAPON_ENERGY_COST 5 +#define WEAPON_WAIT 0 +#define CHENJESU_OFFSET 16 +#define MISSILE_OFFSET 0 #define MISSILE_SPEED DISPLAY_TO_WORLD (16) -#define MISSILE_LIFE 90 /* actually, it's as long as you - * hold the button down. - */ +#define MISSILE_LIFE 90 + /* actually, it's as long as you hold the button down. */ +#define MISSILE_HITS 10 +#define MISSILE_DAMAGE 6 +#define NUM_SPARKLES 8 + +// Shrapnel +#define FRAGMENT_OFFSET 2 +#define NUM_FRAGMENTS 8 +#define FRAGMENT_LIFE 10 +#define FRAGMENT_SPEED MISSILE_SPEED +#define FRAGMENT_RANGE (FRAGMENT_LIFE * FRAGMENT_SPEED) + /* This bit is for the cyborg only. */ +#define FRAGMENT_HITS 1 +#define FRAGMENT_DAMAGE 2 + +// DOGI +#define SPECIAL_ENERGY_COST MAX_ENERGY +#define SPECIAL_WAIT 0 +#define DOGGY_OFFSET 18 +#define DOGGY_SPEED DISPLAY_TO_WORLD (8) +#define ENERGY_DRAIN 10 +#define MAX_DOGGIES 4 +#define DOGGY_HITS 3 +#define DOGGY_MASS 4 static RACE_DESC chenjesu_desc = { @@ -116,17 +137,9 @@ static RACE_DESC chenjesu_desc = 0, /* CodeRef */ }; -#define FRAGMENT_LIFE 10 -#define FRAGMENT_SPEED MISSILE_SPEED -#define FRAGMENT_RANGE (FRAGMENT_LIFE * FRAGMENT_SPEED) - static void crystal_postprocess (ELEMENT *ElementPtr) { -#define FRAGMENT_HITS 1 -#define FRAGMENT_DAMAGE 2 -#define FRAGMENT_OFFSET 2 -#define NUM_FRAGMENTS 8 STARSHIP *StarShipPtr; MISSILE_BLOCK MissileBlock; @@ -212,7 +225,6 @@ crystal_collision (ELEMENT *ElementPtr0, POINT *pPt0, { ELEMENT *BlastElementPtr; -#define NUM_SPARKLES 8 LockElement (hBlastElement, &BlastElementPtr); BlastElementPtr->current.location = ElementPtr1->current.location; @@ -231,9 +243,6 @@ crystal_collision (ELEMENT *ElementPtr0, POINT *pPt0, } } -#define DOGGY_OFFSET 18 -#define DOGGY_SPEED DISPLAY_TO_WORLD (8) - static void doggy_preprocess (ELEMENT *ElementPtr) { @@ -314,7 +323,6 @@ static void doggy_collision (ELEMENT *ElementPtr0, POINT *pPt0, ELEMENT *ElementPtr1, POINT *pPt1) { -#define ENERGY_DRAIN 10 collision (ElementPtr0, pPt0, ElementPtr1, pPt1); if ((ElementPtr1->state_flags & PLAYER_SHIP) && !elementsOfSamePlayer (ElementPtr0, ElementPtr1)) @@ -335,8 +343,6 @@ doggy_collision (ELEMENT *ElementPtr0, POINT *pPt0, ElementPtr0->thrust_wait += COLLISION_THRUST_WAIT << 1; } -#define CHENJESU_OFFSET 16 - static void spawn_doggy (ELEMENT *ElementPtr) { @@ -352,8 +358,8 @@ spawn_doggy (ELEMENT *ElementPtr) PutElement (hDoggyElement); LockElement (hDoggyElement, &DoggyElementPtr); - DoggyElementPtr->hit_points = 3; - DoggyElementPtr->mass_points = 4; + DoggyElementPtr->hit_points = DOGGY_HITS; + DoggyElementPtr->mass_points = DOGGY_MASS; DoggyElementPtr->thrust_wait = 0; DoggyElementPtr->playerNr = ElementPtr->playerNr; DoggyElementPtr->state_flags = APPEARING; @@ -499,9 +505,6 @@ chenjesu_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern, static COUNT initialize_crystal (ELEMENT *ShipPtr, HELEMENT CrystalArray[]) { -#define MISSILE_HITS 10 -#define MISSILE_DAMAGE 6 -#define MISSILE_OFFSET 0 STARSHIP *StarShipPtr; MISSILE_BLOCK MissileBlock; diff --git a/sc2/src/uqm/ships/chmmr/chmmr.c b/sc2/src/uqm/ships/chmmr/chmmr.c index d5a616443..8f6abf0dc 100644 --- a/sc2/src/uqm/ships/chmmr/chmmr.c +++ b/sc2/src/uqm/ships/chmmr/chmmr.c @@ -24,23 +24,36 @@ #include "uqm/globdata.h" #include "libs/mathlib.h" - +// Core characteristics #define MAX_CREW MAX_CREW_SIZE #define MAX_ENERGY MAX_ENERGY_SIZE #define ENERGY_REGENERATION 1 -#define WEAPON_ENERGY_COST 2 -#define SPECIAL_ENERGY_COST 1 #define ENERGY_WAIT 1 #define MAX_THRUST 35 #define THRUST_INCREMENT 7 -#define TURN_WAIT 3 #define THRUST_WAIT 5 -#define WEAPON_WAIT 0 -#define SPECIAL_WAIT 0 - +#define TURN_WAIT 3 #define SHIP_MASS 10 + +// Laser +#define WEAPON_ENERGY_COST 2 +#define WEAPON_WAIT 0 #define CHMMR_OFFSET 18 #define LASER_RANGE DISPLAY_TO_WORLD (150) +#define NUM_CYCLES 4 + +// Tractor Beam +#define SPECIAL_ENERGY_COST 1 +#define SPECIAL_WAIT 0 +#define NUM_SHADOWS 5 + +// Satellites +#define NUM_SATELLITES 3 +#define SATELLITE_OFFSET DISPLAY_TO_WORLD (64) +#define SATELLITE_HITPOINTS 10 +#define SATELLITE_MASS 10 +#define DEFENSE_RANGE (UWORD)64 +#define DEFENSE_WAIT 2 static RACE_DESC chmmr_desc = { @@ -202,7 +215,6 @@ laser_death (ELEMENT *ElementPtr) static COUNT initialize_megawatt_laser (ELEMENT *ShipPtr, HELEMENT LaserArray[]) { -#define NUM_CYCLES 4 RECT r; STARSHIP *StarShipPtr; LASER_BLOCK LaserBlock; @@ -337,8 +349,6 @@ chmmr_postprocess (ELEMENT *ElementPtr) facing = 0; if (TrackShip (ElementPtr, &facing) >= 0) { -#define NUM_SHADOWS 5 - ELEMENT *ShipElementPtr; LockElement (ElementPtr->hTarget, &ShipElementPtr); @@ -444,8 +454,6 @@ chmmr_postprocess (ELEMENT *ElementPtr) StarShipPtr->special_counter = 0; } -#define SATELLITE_OFFSET DISPLAY_TO_WORLD (64) - static void satellite_preprocess (ELEMENT *ElementPtr) { @@ -502,8 +510,6 @@ satellite_preprocess (ELEMENT *ElementPtr) static void spawn_point_defense (ELEMENT *ElementPtr) { -#define DEFENSE_RANGE (UWORD)64 -#define DEFENSE_WAIT 2 BYTE weakest; UWORD best_dist; STARSHIP *StarShipPtr; @@ -674,7 +680,6 @@ satellite_death (ELEMENT *ElementPtr) static void spawn_satellites (ELEMENT *ElementPtr) { -#define NUM_SATELLITES 3 COUNT i; STARSHIP *StarShipPtr; @@ -697,8 +702,8 @@ spawn_satellites (ELEMENT *ElementPtr) SattPtr->state_flags = IGNORE_SIMILAR | APPEARING | FINITE_LIFE; SattPtr->life_span = NORMAL_LIFE + 1; - SattPtr->hit_points = 10; - SattPtr->mass_points = 10; + SattPtr->hit_points = SATELLITE_HITPOINTS; + SattPtr->mass_points = SATELLITE_MASS; angle = (i * FULL_CIRCLE + (NUM_SATELLITES >> 1)) / NUM_SATELLITES; diff --git a/sc2/src/uqm/ships/druuge/druuge.c b/sc2/src/uqm/ships/druuge/druuge.c index ff75fc76d..6ba2591ce 100644 --- a/sc2/src/uqm/ships/druuge/druuge.c +++ b/sc2/src/uqm/ships/druuge/druuge.c @@ -20,23 +20,33 @@ #include "druuge.h" #include "resinst.h" +// Core characteristics #define MAX_CREW 14 #define MAX_ENERGY 32 #define ENERGY_REGENERATION 1 -#define WEAPON_ENERGY_COST 4 -#define SPECIAL_ENERGY_COST 16 #define ENERGY_WAIT 50 #define MAX_THRUST 20 #define THRUST_INCREMENT 2 -#define TURN_WAIT 4 #define THRUST_WAIT 1 -#define WEAPON_WAIT 10 -#define SPECIAL_WAIT 30 - +#define TURN_WAIT 4 #define SHIP_MASS 5 + +// Mass Driver +#define WEAPON_ENERGY_COST 4 +#define WEAPON_WAIT 10 +#define DRUUGE_OFFSET 24 +#define MISSILE_OFFSET 6 #define MISSILE_SPEED DISPLAY_TO_WORLD (30) #define MISSILE_LIFE 20 #define MISSILE_RANGE (MISSILE_SPEED * MISSILE_LIFE) +#define MISSILE_HITS 4 +#define MISSILE_DAMAGE 6 +#define RECOIL_VELOCITY WORLD_TO_VELOCITY (DISPLAY_TO_WORLD (6)) +#define MAX_RECOIL_VELOCITY (RECOIL_VELOCITY * 4) + +// Furnace +#define SPECIAL_ENERGY_COST 16 +#define SPECIAL_WAIT 30 static RACE_DESC druuge_desc = { @@ -109,9 +119,6 @@ static RACE_DESC druuge_desc = 0, /* CodeRef */ }; -#define RECOIL_VELOCITY WORLD_TO_VELOCITY (DISPLAY_TO_WORLD (6)) -#define MAX_RECOIL_VELOCITY (RECOIL_VELOCITY * 4) - static void cannon_collision (ELEMENT *ElementPtr0, POINT *pPt0, ELEMENT *ElementPtr1, POINT *pPt1) @@ -153,10 +160,6 @@ cannon_collision (ELEMENT *ElementPtr0, POINT *pPt0, static COUNT initialize_cannon (ELEMENT *ShipPtr, HELEMENT CannonArray[]) { -#define DRUUGE_OFFSET 24 -#define MISSILE_OFFSET 6 -#define MISSILE_HITS 4 -#define MISSILE_DAMAGE 6 STARSHIP *StarShipPtr; MISSILE_BLOCK MissileBlock; diff --git a/sc2/src/uqm/ships/human/human.c b/sc2/src/uqm/ships/human/human.c index de751b960..354486dde 100644 --- a/sc2/src/uqm/ships/human/human.c +++ b/sc2/src/uqm/ships/human/human.c @@ -23,24 +23,36 @@ #include "uqm/colors.h" #include "uqm/globdata.h" - +// Core characteristics #define MAX_CREW 18 #define MAX_ENERGY 18 #define ENERGY_REGENERATION 1 -#define WEAPON_ENERGY_COST 9 -#define SPECIAL_ENERGY_COST 4 #define ENERGY_WAIT 8 #define MAX_THRUST /* DISPLAY_TO_WORLD (6) */ 24 #define THRUST_INCREMENT /* DISPLAY_TO_WORLD (2) */ 3 -#define TURN_WAIT 1 #define THRUST_WAIT 4 -#define WEAPON_WAIT 10 -#define SPECIAL_WAIT 9 - +#define TURN_WAIT 1 #define SHIP_MASS 6 -#define MISSILE_LIFE 60 + +// Nuke +#define WEAPON_ENERGY_COST 9 +#define WEAPON_WAIT 10 +#define HUMAN_OFFSET 42 +#define NUKE_OFFSET 8 #define MIN_MISSILE_SPEED DISPLAY_TO_WORLD (10) #define MAX_MISSILE_SPEED DISPLAY_TO_WORLD (20) +#define MISSILE_SPEED (MAX_THRUST >= MIN_MISSILE_SPEED ? \ + MAX_THRUST : MIN_MISSILE_SPEED) +#define THRUST_SCALE DISPLAY_TO_WORLD (1) +#define MISSILE_LIFE 60 +#define MISSILE_HITS 1 +#define MISSILE_DAMAGE 4 +#define TRACK_WAIT 3 + +// Point-Defense Laser +#define SPECIAL_ENERGY_COST 4 +#define SPECIAL_WAIT 9 +#define LASER_RANGE (UWORD)100 static RACE_DESC human_desc = { @@ -113,10 +125,6 @@ static RACE_DESC human_desc = 0, /* CodeRef */ }; -#define MISSILE_SPEED (MAX_THRUST >= MIN_MISSILE_SPEED ? \ - MAX_THRUST : MIN_MISSILE_SPEED) -#define TRACK_WAIT 3 - static void nuke_preprocess (ELEMENT *ElementPtr) { @@ -141,7 +149,6 @@ nuke_preprocess (ELEMENT *ElementPtr) { SIZE speed; -#define THRUST_SCALE DISPLAY_TO_WORLD (1) if ((speed = MISSILE_SPEED + ((MISSILE_LIFE - ElementPtr->life_span) * THRUST_SCALE)) > MAX_MISSILE_SPEED) @@ -196,7 +203,6 @@ spawn_point_defense (ELEMENT *ElementPtr) if (ObjectPtr != ShipPtr && CollidingElement (ObjectPtr) && !OBJECT_CLOAKED (ObjectPtr)) { -#define LASER_RANGE (UWORD)100 SIZE delta_x, delta_y; delta_x = ObjectPtr->next.location.x - @@ -265,10 +271,6 @@ spawn_point_defense (ELEMENT *ElementPtr) static COUNT initialize_nuke (ELEMENT *ShipPtr, HELEMENT NukeArray[]) { -#define HUMAN_OFFSET 42 -#define MISSILE_DAMAGE 4 -#define MISSILE_HITS 1 -#define NUKE_OFFSET 8 STARSHIP *StarShipPtr; MISSILE_BLOCK MissileBlock; diff --git a/sc2/src/uqm/ships/ilwrath/ilwrath.c b/sc2/src/uqm/ships/ilwrath/ilwrath.c index e4975ff2c..394708134 100644 --- a/sc2/src/uqm/ships/ilwrath/ilwrath.c +++ b/sc2/src/uqm/ships/ilwrath/ilwrath.c @@ -24,21 +24,33 @@ #include "uqm/globdata.h" +// Core characteristics #define MAX_CREW 22 #define MAX_ENERGY 16 #define ENERGY_REGENERATION 4 -#define WEAPON_ENERGY_COST 1 -#define SPECIAL_ENERGY_COST 3 #define ENERGY_WAIT 4 #define MAX_THRUST 25 #define THRUST_INCREMENT 5 -#define TURN_WAIT 2 #define THRUST_WAIT 0 -#define WEAPON_WAIT 0 -#define SPECIAL_WAIT 13 - +#define TURN_WAIT 2 #define SHIP_MASS 7 +#define LOOK_AHEAD 4 + /* Controls how much the auto-turn will attempt to "lead" + * its target. */ + +// Hellfire Spout +#define WEAPON_ENERGY_COST 1 +#define WEAPON_WAIT 0 #define MISSILE_LIFE 8 +#define ILWRATH_OFFSET 29 +#define MISSILE_SPEED MAX_THRUST +#define MISSILE_HITS 1 +#define MISSILE_DAMAGE 1 +#define MISSILE_OFFSET 0 + +// Cloaking Device +#define SPECIAL_ENERGY_COST 3 +#define SPECIAL_WAIT 13 static RACE_DESC ilwrath_desc = { @@ -178,11 +190,6 @@ ilwrath_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern, static COUNT initialize_flame (ELEMENT *ShipPtr, HELEMENT FlameArray[]) { -#define ILWRATH_OFFSET 29 -#define MISSILE_SPEED MAX_THRUST -#define MISSILE_HITS 1 -#define MISSILE_DAMAGE 1 -#define MISSILE_OFFSET 0 STARSHIP *StarShipPtr; MISSILE_BLOCK MissileBlock; @@ -278,7 +285,6 @@ ilwrath_preprocess (ELEMENT *ElementPtr) facing = StarShipPtr->ShipFacing; if (TrackShip (ElementPtr, &facing) >= 0) { -#define LOOK_AHEAD 4 ELEMENT *eptr; SIZE dx0, dy0, dx1, dy1; VELOCITY_DESC v; diff --git a/sc2/src/uqm/ships/lastbat/lastbat.c b/sc2/src/uqm/ships/lastbat/lastbat.c index 568b2aae1..9d44742c6 100644 --- a/sc2/src/uqm/ships/lastbat/lastbat.c +++ b/sc2/src/uqm/ships/lastbat/lastbat.c @@ -27,25 +27,58 @@ #include "libs/mathlib.h" #include "libs/timelib.h" - #define num_generators characteristics.max_thrust +// Core characteristics #define MAX_CREW 1 #define MAX_ENERGY MAX_ENERGY_SIZE #define ENERGY_REGENERATION 1 -#define WEAPON_ENERGY_COST 2 -#define SPECIAL_ENERGY_COST 3 #define ENERGY_WAIT 6 #define MAX_THRUST 0 #define THRUST_INCREMENT 0 #define TURN_WAIT 0 #define THRUST_WAIT 0 -#define WEAPON_WAIT ((ONE_SECOND / BATTLE_FRAME_RATE) * 10) -#define SPECIAL_WAIT ((ONE_SECOND / BATTLE_FRAME_RATE) * 3) - #define SHIP_MASS (MAX_SHIP_MASS * 10) -#define SAMATRA_OFFSET 9 -#define MISSILE_SPEED DISPLAY_TO_WORLD (8) +#define TURRET_WAIT 0 /* Controls animation of the Sa-Matra's central + * 'furnace', a new frame is displayed once every + * TURRET_WAIT frames. */ + +// Yellow comet +#define WEAPON_WAIT ((ONE_SECOND / BATTLE_FRAME_RATE) * 10) +#define COMET_DAMAGE 2 +#define COMET_OFFSET 0 +#define COMET_HITS 12 +#define COMET_SPEED DISPLAY_TO_WORLD (12) +#define COMET_LIFE 2 +#define COMET_TURN_WAIT 3 +#define MAX_COMETS 3 +#define WEAPON_ENERGY_COST 2 + /* Used for samatra_desc.weapon_energy_cost, but the value isn't + * actually used. */ + +// Green sentinel +#define SPECIAL_WAIT ((ONE_SECOND / BATTLE_FRAME_RATE) * 3) +#define SENTINEL_SPEED DISPLAY_TO_WORLD (8) +#define SENTINEL_LIFE 2 +#define SENTINEL_OFFSET 0 +#define SENTINEL_HITS 10 +#define SENTINEL_DAMAGE 1 +#define TRACK_WAIT 1 +#define ANIMATION_WAIT 1 +#define RECOIL_VELOCITY WORLD_TO_VELOCITY (DISPLAY_TO_WORLD (10)) +#define MAX_RECOIL_VELOCITY (RECOIL_VELOCITY * 4) +#define MAX_SENTINELS 4 +#define SPECIAL_ENERGY_COST 3 + /* Used for samatra_desc.special_energy_cost, but the value isn't + * actually used. */ + +// Blue force field +#define GATE_DAMAGE 1 +#define GATE_HITS 100 + +// Red generators +#define GENERATOR_HITS 15 +#define MAX_GENERATORS 8 static RACE_DESC samatra_desc = { @@ -153,8 +186,6 @@ comet_preprocess (ELEMENT *ElementPtr) ElementPtr->state_flags |= CHANGING; } -#define COMET_DAMAGE 2 - static void comet_collision (ELEMENT *ElementPtr0, POINT *pPt0, ELEMENT *ElementPtr1, POINT *pPt1) @@ -199,10 +230,6 @@ comet_collision (ELEMENT *ElementPtr0, POINT *pPt0, static HELEMENT spawn_comet (ELEMENT *ElementPtr) { -#define COMET_OFFSET 0 -#define COMET_HITS 12 -#define COMET_SPEED DISPLAY_TO_WORLD (12) -#define COMET_LIFE 2 MISSILE_BLOCK MissileBlock; HELEMENT hComet; STARSHIP *StarShipPtr; @@ -258,12 +285,11 @@ spawn_comet (ELEMENT *ElementPtr) --CometPtr->turn_wait; else { -#define COMET_WAIT 3 facing = NORMALIZE_FACING (facing); if (TrackShip (CometPtr, &facing) > 0) SetVelocityVector (&CometPtr->velocity, COMET_SPEED, facing); - CometPtr->turn_wait = COMET_WAIT; + CometPtr->turn_wait = COMET_TURN_WAIT; } } UnlockElement (hComet); @@ -279,7 +305,6 @@ turret_preprocess (ELEMENT *ElementPtr) --ElementPtr->turn_wait; else { -#define TURRET_WAIT 0 ElementPtr->next.image.frame = SetAbsFrameIndex (ElementPtr->current.image.frame, (GetFrameIndex (ElementPtr->current.image.frame) % 10) + 1); @@ -289,9 +314,6 @@ turret_preprocess (ELEMENT *ElementPtr) } } -#define GATE_DAMAGE 1 -#define GATE_HITS 100 - static void gate_collision (ELEMENT *ElementPtr0, POINT *pPt0, ELEMENT *ElementPtr1, POINT *pPt1) @@ -413,8 +435,6 @@ generator_death (ELEMENT *ElementPtr) } } -#define GENERATOR_HITS 15 - static void generator_preprocess (ELEMENT *ElementPtr) { @@ -443,8 +463,6 @@ generator_collision (ELEMENT *ElementPtr0, POINT *pPt0, (void) pPt1; /* Satisfying compiler (unused parameter) */ } -#define TRACK_WAIT 1 - static void sentinel_preprocess (ELEMENT *ElementPtr) { @@ -458,7 +476,6 @@ sentinel_preprocess (ELEMENT *ElementPtr) --ElementPtr->thrust_wait; else { -#define ANIMATION_WAIT 1 ElementPtr->next.image.frame = SetAbsFrameIndex (ElementPtr->current.image.frame, (GetFrameIndex (ElementPtr->current.image.frame) + 1) % 6); @@ -483,7 +500,7 @@ sentinel_preprocess (ELEMENT *ElementPtr) ElementPtr->state_flags &= ~NONSOLID; facing = (COUNT)TFB_Random (); SetVelocityVector (&ElementPtr->velocity, - MISSILE_SPEED, facing); + SENTINEL_SPEED, facing); } facing = NORMALIZE_FACING (facing); if (ElementPtr->hTarget == 0) @@ -577,16 +594,13 @@ sentinel_preprocess (ELEMENT *ElementPtr) } SetVelocityVector (&ElementPtr->velocity, - MISSILE_SPEED, facing); + SENTINEL_SPEED, facing); } ElementPtr->turn_wait = TRACK_WAIT; } } -#define RECOIL_VELOCITY WORLD_TO_VELOCITY (DISPLAY_TO_WORLD (10)) -#define MAX_RECOIL_VELOCITY (RECOIL_VELOCITY * 4) - static void sentinel_collision (ELEMENT *ElementPtr0, POINT *pPt0, ELEMENT *ElementPtr1, POINT *pPt1) @@ -604,8 +618,8 @@ sentinel_collision (ELEMENT *ElementPtr0, POINT *pPt0, angle = ARCTAN (pPt0->x - pPt1->x, pPt0->y - pPt1->y); SetVelocityComponents (&ElementPtr0->velocity, - COSINE (angle, WORLD_TO_VELOCITY (MISSILE_SPEED)), - SINE (angle, WORLD_TO_VELOCITY (MISSILE_SPEED))); + COSINE (angle, WORLD_TO_VELOCITY (SENTINEL_SPEED)), + SINE (angle, WORLD_TO_VELOCITY (SENTINEL_SPEED))); ElementPtr0->turn_wait = TRACK_WAIT; ElementPtr0->state_flags |= COLLISION | DEFY_PHYSICS; } @@ -688,9 +702,6 @@ samatra_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern, ship_intelligence (ShipPtr, ObjectsOfConcern, ConcernCounter); } -#define MAX_COMETS 3 -#define MAX_SENTINELS 4 - static void samatra_postprocess (ELEMENT *ElementPtr) { @@ -709,10 +720,6 @@ samatra_postprocess (ELEMENT *ElementPtr) if (StarShipPtr->special_counter == 0 && StarShipPtr->RaceDescPtr->characteristics.special_wait < MAX_SENTINELS) { -#define MISSILE_LIFE 2 -#define MISSILE_OFFSET 0 -#define MISSILE_HITS 10 -#define MISSILE_DAMAGE 1 MISSILE_BLOCK MissileBlock; HELEMENT hSentinel; @@ -724,12 +731,12 @@ samatra_postprocess (ELEMENT *ElementPtr) MissileBlock.sender = ElementPtr->playerNr; MissileBlock.flags = 0; MissileBlock.pixoffs = 0; - MissileBlock.speed = MISSILE_SPEED; - MissileBlock.hit_points = MISSILE_HITS; - MissileBlock.damage = MISSILE_DAMAGE; - MissileBlock.life = MISSILE_LIFE; + MissileBlock.speed = SENTINEL_SPEED; + MissileBlock.hit_points = SENTINEL_HITS; + MissileBlock.damage = SENTINEL_DAMAGE; + MissileBlock.life = SENTINEL_LIFE; MissileBlock.preprocess_func = sentinel_preprocess; - MissileBlock.blast_offs = MISSILE_OFFSET; + MissileBlock.blast_offs = SENTINEL_OFFSET; hSentinel = initialize_missile (&MissileBlock); if (hSentinel) @@ -765,7 +772,6 @@ samatra_preprocess (ELEMENT *ElementPtr) } else { -#define MAX_GENERATORS 8 POINT offs[] = { {-127-9, -53+18}, diff --git a/sc2/src/uqm/ships/melnorme/melnorme.c b/sc2/src/uqm/ships/melnorme/melnorme.c index 404722592..8e5ab2b8d 100644 --- a/sc2/src/uqm/ships/melnorme/melnorme.c +++ b/sc2/src/uqm/ships/melnorme/melnorme.c @@ -23,23 +23,38 @@ #include "uqm/globdata.h" #include "libs/mathlib.h" - +// Core characteristics #define MAX_CREW 20 #define MAX_ENERGY MAX_ENERGY_SIZE #define ENERGY_REGENERATION 1 -#define WEAPON_ENERGY_COST 5 -#define SPECIAL_ENERGY_COST 20 #define ENERGY_WAIT 4 #define MAX_THRUST 36 #define THRUST_INCREMENT 6 -#define TURN_WAIT 4 #define THRUST_WAIT 4 -#define WEAPON_WAIT 1 -#define SPECIAL_WAIT 20 - +#define TURN_WAIT 4 #define SHIP_MASS 7 + +// Blaster Pulse +#define WEAPON_ENERGY_COST 5 +#define WEAPON_WAIT 1 +#define MELNORME_OFFSET 24 +#define LEVEL_COUNTER 72 +#define MAX_PUMP 4 #define PUMPUP_SPEED DISPLAY_TO_WORLD (45) #define PUMPUP_LIFE 10 +#define PUMPUP_DAMAGE 2 +#define MIN_PUMPITUDE_ANIMS 3 +#define NUM_PUMP_ANIMS 5 +#define REVERSE_DIR (BYTE)(1 << 7) + +// Confusion Pulse +#define SPECIAL_ENERGY_COST 20 +#define SPECIAL_WAIT 20 +#define CMISSILE_SPEED DISPLAY_TO_WORLD (30) +#define CMISSILE_LIFE 20 +#define CMISSILE_HITS 200 +#define CMISSILE_DAMAGE 0 +#define CMISSILE_OFFSET 4 static RACE_DESC melnorme_desc = { @@ -112,9 +127,6 @@ static RACE_DESC melnorme_desc = 0, /* CodeRef */ }; -#define NUM_PUMP_ANIMS 5 -#define REVERSE_DIR (BYTE)(1 << 7) - static void pump_up_preprocess (ELEMENT *ElementPtr) { @@ -146,11 +158,6 @@ pump_up_preprocess (ELEMENT *ElementPtr) static COUNT initialize_pump_up (ELEMENT *ShipPtr, HELEMENT PumpUpArray[]); -#define MELNORME_OFFSET 24 -#define LEVEL_COUNTER 72 -#define MAX_PUMP 4 -#define PUMPUP_DAMAGE 2 - static void pump_up_postprocess (ELEMENT *ElementPtr) { @@ -282,7 +289,6 @@ pump_up_collision (ELEMENT *ElementPtr0, POINT *pPt0, { ELEMENT *BlastElementPtr; -#define MIN_PUMPITUDE_ANIMS 3 LockElement (hBlastElement, &BlastElementPtr); BlastElementPtr->life_span = @@ -474,11 +480,6 @@ confusion_collision (ELEMENT *ElementPtr0, POINT *pPt0, static COUNT initialize_confusion (ELEMENT *ShipPtr, HELEMENT ConfusionArray[]) { -#define CMISSILE_SPEED DISPLAY_TO_WORLD (30) -#define CMISSILE_HITS 200 -#define CMISSILE_DAMAGE 0 -#define CMISSILE_LIFE 20 -#define CMISSILE_OFFSET 4 STARSHIP *StarShipPtr; MISSILE_BLOCK ConfusionBlock; diff --git a/sc2/src/uqm/ships/mmrnmhrm/mmrnmhrm.c b/sc2/src/uqm/ships/mmrnmhrm/mmrnmhrm.c index bc5ed0d60..e8f8348e1 100644 --- a/sc2/src/uqm/ships/mmrnmhrm/mmrnmhrm.c +++ b/sc2/src/uqm/ships/mmrnmhrm/mmrnmhrm.c @@ -20,35 +20,51 @@ #include "mmrnmhrm.h" #include "resinst.h" - +// Core characteristics #define MAX_CREW 20 #define MAX_ENERGY 10 +#define SHIP_MASS 3 + +// X-Wing characteristics #define ENERGY_REGENERATION 2 -#define WEAPON_ENERGY_COST 1 -#define SPECIAL_ENERGY_COST MAX_ENERGY #define ENERGY_WAIT 6 #define MAX_THRUST 20 #define THRUST_INCREMENT 5 -#define TURN_WAIT 2 #define THRUST_WAIT 1 -#define WEAPON_WAIT 0 -#define SPECIAL_WAIT 0 +#define TURN_WAIT 2 +// Y-Wing characteristics #define YWING_ENERGY_REGENERATION 1 -#define YWING_WEAPON_ENERGY_COST 1 #define YWING_SPECIAL_ENERGY_COST MAX_ENERGY #define YWING_ENERGY_WAIT 6 #define YWING_MAX_THRUST 50 #define YWING_THRUST_INCREMENT 10 -#define YWING_TURN_WAIT 14 #define YWING_THRUST_WAIT 0 -#define YWING_WEAPON_WAIT 20 -#define YWING_SPECIAL_WAIT 0 +#define YWING_TURN_WAIT 14 -#define SHIP_MASS 3 +// X-Wing Lasers #define MMRNMHRM_OFFSET 16 +#define WEAPON_ENERGY_COST 1 +#define WEAPON_WAIT 0 +#define CENTER_OFFS DISPLAY_TO_WORLD (4) +#define WING_OFFS DISPLAY_TO_WORLD (10) #define LASER_RANGE DISPLAY_TO_WORLD (125 + MMRNMHRM_OFFSET) +// Y-Wing Missiles +#define YWING_WEAPON_ENERGY_COST 1 +#define YWING_WEAPON_WAIT 20 +#define LAUNCH_OFFS DISPLAY_TO_WORLD (4) +#define MISSILE_OFFSET 0 +#define MISSILE_SPEED DISPLAY_TO_WORLD (20) +#define MISSILE_LIFE 40 +#define MISSILE_HITS 1 +#define MISSILE_DAMAGE 1 +#define TRACK_WAIT 5 + +// Transform +#define SPECIAL_ENERGY_COST MAX_ENERGY +#define SPECIAL_WAIT 0 +#define YWING_SPECIAL_WAIT 0 static RACE_DESC mmrnmhrm_desc = { @@ -121,9 +137,6 @@ static RACE_DESC mmrnmhrm_desc = 0, /* CodeRef */ }; -#define MISSILE_SPEED DISPLAY_TO_WORLD (20) -#define TRACK_WAIT 5 - // Private per-instance ship data typedef CHARACTERISTIC_STUFF MMRNMHRM_DATA; @@ -288,7 +301,6 @@ twin_laser_collision (ELEMENT *ElementPtr0, POINT *pPt0, static COUNT initialize_dual_weapons (ELEMENT *ShipPtr, HELEMENT WeaponArray[]) { -#define CENTER_OFFS DISPLAY_TO_WORLD (4) COORD cx, cy; COUNT facing, angle; SIZE offs_x, offs_y; @@ -302,7 +314,6 @@ initialize_dual_weapons (ELEMENT *ShipPtr, HELEMENT WeaponArray[]) if (ShipPtr->next.image.farray == StarShipPtr->RaceDescPtr->ship_data.ship) { -#define WING_OFFS DISPLAY_TO_WORLD (10) COORD ex, ey; LASER_BLOCK LaserBlock; ELEMENT *LaserPtr; @@ -342,11 +353,6 @@ initialize_dual_weapons (ELEMENT *ShipPtr, HELEMENT WeaponArray[]) } else { -#define MISSILE_HITS 1 -#define MISSILE_DAMAGE 1 -#define MISSILE_OFFSET 0 -#define MISSILE_LIFE 40 -#define LAUNCH_OFFS DISPLAY_TO_WORLD (4) MISSILE_BLOCK TorpBlock; ELEMENT *TorpPtr; diff --git a/sc2/src/uqm/ships/mycon/mycon.c b/sc2/src/uqm/ships/mycon/mycon.c index dcad5667a..8c99fbef5 100644 --- a/sc2/src/uqm/ships/mycon/mycon.c +++ b/sc2/src/uqm/ships/mycon/mycon.c @@ -20,26 +20,34 @@ #include "mycon.h" #include "resinst.h" +// Core characteristics #define MAX_CREW 20 #define MAX_ENERGY 40 #define ENERGY_REGENERATION 1 -#define WEAPON_ENERGY_COST 20 -#define SPECIAL_ENERGY_COST MAX_ENERGY #define ENERGY_WAIT 4 #define MAX_THRUST /* DISPLAY_TO_WORLD (7) */ 27 #define THRUST_INCREMENT /* DISPLAY_TO_WORLD (2) */ 9 -#define TURN_WAIT 6 #define THRUST_WAIT 6 -#define WEAPON_WAIT 5 -#define SPECIAL_WAIT 0 - +#define TURN_WAIT 6 #define SHIP_MASS 7 +// Plasmoid +#define WEAPON_ENERGY_COST 20 +#define WEAPON_WAIT 5 +#define MYCON_OFFSET 24 +#define MISSILE_OFFSET 0 #define NUM_PLASMAS 11 #define NUM_GLOBALLS 8 #define PLASMA_DURATION 13 #define MISSILE_LIFE (NUM_PLASMAS * PLASMA_DURATION) #define MISSILE_SPEED DISPLAY_TO_WORLD (8) +#define MISSILE_DAMAGE 10 +#define TRACK_WAIT 1 + +// Regenerate +#define SPECIAL_ENERGY_COST MAX_ENERGY +#define SPECIAL_WAIT 0 +#define REGENERATION_AMOUNT 4 static RACE_DESC mycon_desc = { @@ -112,10 +120,6 @@ static RACE_DESC mycon_desc = 0, /* CodeRef */ }; -#define MISSILE_DAMAGE 10 - -#define TRACK_WAIT 1 - static void plasma_preprocess (ELEMENT *ElementPtr) { @@ -297,8 +301,6 @@ mycon_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern, static COUNT initialize_plasma (ELEMENT *ShipPtr, HELEMENT PlasmaArray[]) { -#define MYCON_OFFSET 24 -#define MISSILE_OFFSET 0 STARSHIP *StarShipPtr; MISSILE_BLOCK MissileBlock; @@ -343,7 +345,6 @@ mycon_postprocess (ELEMENT *ElementPtr) && ElementPtr->crew_level != StarShipPtr->RaceDescPtr->ship_info.max_crew && DeltaEnergy (ElementPtr, -SPECIAL_ENERGY_COST)) { -#define REGENERATION_AMOUNT 4 SIZE add_crew; ProcessSound (SetAbsSoundIndex ( diff --git a/sc2/src/uqm/ships/orz/orz.c b/sc2/src/uqm/ships/orz/orz.c index 78bb39c16..1a95fec25 100644 --- a/sc2/src/uqm/ships/orz/orz.c +++ b/sc2/src/uqm/ships/orz/orz.c @@ -24,24 +24,42 @@ #include "uqm/globdata.h" #include "libs/mathlib.h" - +// Core characteristics #define MAX_CREW 16 #define MAX_ENERGY 20 #define ENERGY_REGENERATION 1 -#define WEAPON_ENERGY_COST (MAX_ENERGY / 3) -#define SPECIAL_ENERGY_COST 0 #define ENERGY_WAIT 6 #define MAX_THRUST 35 #define THRUST_INCREMENT 5 -#define TURN_WAIT 1 #define THRUST_WAIT 0 -#define WEAPON_WAIT 4 -#define SPECIAL_WAIT 12 - +#define TURN_WAIT 1 #define SHIP_MASS 4 + +// Howitzer +#define WEAPON_ENERGY_COST (MAX_ENERGY / 3) +#define WEAPON_WAIT 4 #define ORZ_OFFSET 9 #define MISSILE_SPEED DISPLAY_TO_WORLD (30) #define MISSILE_LIFE 12 +#define MISSILE_HITS 2 +#define MISSILE_DAMAGE 3 +#define MISSILE_OFFSET 1 + +// Marine +#define SPECIAL_ENERGY_COST 0 +#define SPECIAL_WAIT 12 +#define MARINE_MAX_THRUST 32 +#define MARINE_THRUST_INCREMENT 8 +#define MARINE_HIT_POINTS 3 +#define MARINE_MASS_POINTS 1 +#define MAX_MARINES 8 +#define MARINE_WAIT 12 +#define ION_LIFE 1 +#define START_ION_COLOR BUILD_COLOR (MAKE_RGB15 (0x1F, 0x15, 0x00), 0x7A) + +// Rotating Turret +#define TURRET_OFFSET 14 +#define TURRET_WAIT 3 static RACE_DESC orz_desc = { @@ -122,15 +140,9 @@ howitzer_collision (ELEMENT *ElementPtr0, POINT *pPt0, weapon_collision (ElementPtr0, pPt0, ElementPtr1, pPt1); } -#define TURRET_OFFSET 14 -#define TURRET_WAIT 3 - static COUNT initialize_turret_missile (ELEMENT *ShipPtr, HELEMENT MissileArray[]) { -#define MISSILE_HITS 2 -#define MISSILE_DAMAGE 3 -#define MISSILE_OFFSET 1 ELEMENT *TurretPtr; STARSHIP *StarShipPtr; MISSILE_BLOCK MissileBlock; @@ -180,8 +192,6 @@ initialize_turret_missile (ELEMENT *ShipPtr, HELEMENT MissileArray[]) return (1); } -#define MAX_MARINES 8 - static BYTE count_marines (STARSHIP *StarShipPtr, BOOLEAN FindSpot) { @@ -310,8 +320,6 @@ orz_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern, UnlockElement (GetSuccElement (ShipPtr)); } -#define START_ION_COLOR BUILD_COLOR (MAKE_RGB15 (0x1F, 0x15, 0x00), 0x7A) - static void ion_preprocess (ELEMENT *ElementPtr) { @@ -352,8 +360,6 @@ ion_preprocess (ELEMENT *ElementPtr) static void marine_preprocess (ELEMENT *ElementPtr); -#define MARINE_WAIT 12 - void intruder_preprocess (ELEMENT *ElementPtr) { @@ -475,7 +481,6 @@ spawn_marine_ion_trail (ELEMENT *ElementPtr, STARSHIP *StarShipPtr, hIonElement = AllocElement (); if (hIonElement) { -#define ION_LIFE 1 COUNT angle; ELEMENT *IonElementPtr; @@ -698,8 +703,10 @@ marine_preprocess (ELEMENT *ElementPtr) // XXX: thrust_wait is abused to store marine speed and // gravity well flags StarShipPtr->cur_status_flags = ElementPtr->thrust_wait << 6; - StarShipPtr->RaceDescPtr->characteristics.thrust_increment = 8; - StarShipPtr->RaceDescPtr->characteristics.max_thrust = 32; + StarShipPtr->RaceDescPtr->characteristics.thrust_increment = + MARINE_THRUST_INCREMENT; + StarShipPtr->RaceDescPtr->characteristics.max_thrust = + MARINE_MAX_THRUST; thrust_status = inertial_thrust (ElementPtr); @@ -965,8 +972,8 @@ turret_postprocess (ELEMENT *ElementPtr) SpaceMarinePtr->state_flags = IGNORE_SIMILAR | APPEARING | CREW_OBJECT; SpaceMarinePtr->life_span = NORMAL_LIFE; - SpaceMarinePtr->hit_points = 3; - SpaceMarinePtr->mass_points = 1; + SpaceMarinePtr->hit_points = MARINE_HIT_POINTS; + SpaceMarinePtr->mass_points = MARINE_MASS_POINTS; facing = FACING_TO_ANGLE (StarShipPtr->ShipFacing); SpaceMarinePtr->current.location.x = diff --git a/sc2/src/uqm/ships/pkunk/pkunk.c b/sc2/src/uqm/ships/pkunk/pkunk.c index 6e8902e07..76f8413f8 100644 --- a/sc2/src/uqm/ships/pkunk/pkunk.c +++ b/sc2/src/uqm/ships/pkunk/pkunk.c @@ -24,23 +24,36 @@ #include "uqm/tactrans.h" #include "libs/mathlib.h" - +// Core characteristics #define MAX_CREW 8 #define MAX_ENERGY 12 #define ENERGY_REGENERATION 0 -#define WEAPON_ENERGY_COST 1 -#define SPECIAL_ENERGY_COST 2 #define ENERGY_WAIT 0 #define MAX_THRUST 64 #define THRUST_INCREMENT 16 -#define TURN_WAIT 0 #define THRUST_WAIT 0 -#define WEAPON_WAIT 0 -#define SPECIAL_WAIT 16 - +#define TURN_WAIT 0 #define SHIP_MASS 1 + +// Triple Miniguns +#define WEAPON_ENERGY_COST 1 +#define WEAPON_WAIT 0 +#define PKUNK_OFFSET 15 +#define MISSILE_OFFSET 1 #define MISSILE_SPEED DISPLAY_TO_WORLD (24) #define MISSILE_LIFE 5 +#define MISSILE_HITS 1 +#define MISSILE_DAMAGE 1 + +// Taunt +#define SPECIAL_ENERGY_COST 2 +#define SPECIAL_WAIT 16 + +// Respawn +#define PHOENIX_LIFE 12 +#define START_PHOENIX_COLOR BUILD_COLOR (MAKE_RGB15 (0x1F, 0x15, 0x00), 0x7A) +#define TRANSITION_LIFE 1 +#define TRANSITION_SPEED DISPLAY_TO_WORLD (20) static RACE_DESC pkunk_desc = { @@ -173,10 +186,6 @@ animate (ELEMENT *ElementPtr) static COUNT initialize_bug_missile (ELEMENT *ShipPtr, HELEMENT MissileArray[]) { -#define PKUNK_OFFSET 15 -#define MISSILE_HITS 1 -#define MISSILE_DAMAGE 1 -#define MISSILE_OFFSET 1 COUNT i; STARSHIP *StarShipPtr; MISSILE_BLOCK MissileBlock; @@ -363,9 +372,6 @@ intercept_pkunk_death (ELEMENT *ElementPtr) UnlockElement (StarShipPtr->hShip); } -#define START_PHOENIX_COLOR BUILD_COLOR (MAKE_RGB15 (0x1F, 0x15, 0x00), 0x7A) -#define TRANSITION_LIFE 1 - static void spawn_phoenix_trail (ELEMENT *ElementPtr) { @@ -399,8 +405,6 @@ spawn_phoenix_trail (ELEMENT *ElementPtr) } // else, the element disappears. } -#define PHOENIX_LIFE 12 - static void phoenix_transition (ELEMENT *ElementPtr) { @@ -417,7 +421,6 @@ phoenix_transition (ELEMENT *ElementPtr) } else if ((hShipImage = AllocElement ())) { -#define TRANSITION_SPEED DISPLAY_TO_WORLD (20) COUNT angle; PutElement (hShipImage); diff --git a/sc2/src/uqm/ships/shofixti/shofixti.c b/sc2/src/uqm/ships/shofixti/shofixti.c index 454a29c7a..03de57e77 100644 --- a/sc2/src/uqm/ships/shofixti/shofixti.c +++ b/sc2/src/uqm/ships/shofixti/shofixti.c @@ -24,12 +24,10 @@ #include "uqm/tactrans.h" #include "libs/mathlib.h" - +// Core characteristics #define MAX_CREW 6 #define MAX_ENERGY 4 #define ENERGY_REGENERATION 1 -#define WEAPON_ENERGY_COST 1 -#define SPECIAL_ENERGY_COST 0 #define ENERGY_WAIT 9 #define MAX_THRUST 35 #define THRUST_INCREMENT 5 @@ -37,10 +35,24 @@ #define THRUST_WAIT 0 #define WEAPON_WAIT 3 #define SPECIAL_WAIT 0 - #define SHIP_MASS 1 + +// Dart Gun +#define WEAPON_ENERGY_COST 1 +#define SHOFIXTI_OFFSET 15 +#define MISSILE_OFFSET 1 #define MISSILE_SPEED DISPLAY_TO_WORLD (24) #define MISSILE_LIFE 10 +#define MISSILE_HITS 1 +#define MISSILE_DAMAGE 1 + +// Glory Device +#define SPECIAL_ENERGY_COST 0 +#define DESTRUCT_RANGE 180 +#define MAX_DESTRUCTION (DESTRUCT_RANGE / 10) + +// Full game: Tanaka/Katana's damaged ships +#define NUM_LIMPETS 3 static RACE_DESC shofixti_desc = { @@ -116,10 +128,7 @@ static RACE_DESC shofixti_desc = static COUNT initialize_standard_missile (ELEMENT *ShipPtr, HELEMENT MissileArray[]) { -#define SHOFIXTI_OFFSET 15 -#define MISSILE_HITS 1 -#define MISSILE_DAMAGE 1 -#define MISSILE_OFFSET 1 + STARSHIP *StarShipPtr; MISSILE_BLOCK MissileBlock; @@ -266,7 +275,6 @@ self_destruct_kill_objects (ELEMENT *ElementPtr) continue; } -#define DESTRUCT_RANGE 180 delta_x = ObjPtr->next.location.x - ElementPtr->next.location.x; if (delta_x < 0) delta_x = -delta_x; @@ -279,7 +287,6 @@ self_destruct_kill_objects (ELEMENT *ElementPtr) if (delta_x <= DESTRUCT_RANGE && delta_y <= DESTRUCT_RANGE && dist <= DESTRUCT_RANGE * DESTRUCT_RANGE) { -#define MAX_DESTRUCTION (DESTRUCT_RANGE / 10) int destruction = 1 + MAX_DESTRUCTION * (DESTRUCT_RANGE - square_root (dist)) / DESTRUCT_RANGE; @@ -462,7 +469,6 @@ init_shofixti (void) && !GET_GAME_STATE (SHOFIXTI_RECRUITED)) { // Tanaka/Katana flies in a damaged ship. -#define NUM_LIMPETS 3 COUNT i; new_shofixti_desc.ship_data.ship_rsc[0] = OLDSHOF_BIG_MASK_PMAP_ANIM; @@ -484,7 +490,10 @@ init_shofixti (void) --new_shofixti_desc.characteristics.turn_wait; if (++new_shofixti_desc.characteristics.thrust_wait == 0) --new_shofixti_desc.characteristics.thrust_wait; + +/* This should be the same as MIN_THRUST_INCREMENT in vux.c */ #define MIN_THRUST_INCREMENT DISPLAY_TO_WORLD (1) + if (new_shofixti_desc.characteristics.thrust_increment <= MIN_THRUST_INCREMENT) { diff --git a/sc2/src/uqm/ships/sis_ship/sis_ship.c b/sc2/src/uqm/ships/sis_ship/sis_ship.c index 0553ef3e5..d5898277a 100644 --- a/sc2/src/uqm/ships/sis_ship/sis_ship.c +++ b/sc2/src/uqm/ships/sis_ship/sis_ship.c @@ -26,27 +26,73 @@ #include "uqm/hyper.h" #include "libs/mathlib.h" - -#define MAX_TRACKING 3 -#define MAX_DEFENSE 8 +/* Core characteristics. + * All of these are changed at init time by some module, except for + * MAX_ENERGY, THRUST_INCREMENT, and SHIP_MASS. */ #define MAX_CREW MAX_CREW_SIZE + /* This value gets thrown out - actual max crew is determined by the + * number of crew pods. The minimum value is 1 (just the Captain). */ + #define MAX_ENERGY MAX_ENERGY_SIZE #define ENERGY_REGENERATION 1 -#define WEAPON_ENERGY_COST 1 -#define SPECIAL_ENERGY_COST 0 -#define ENERGY_WAIT 10 -#define MAX_THRUST 10 -#define THRUST_INCREMENT 4 -#define TURN_WAIT 17 -#define THRUST_WAIT 6 -#define WEAPON_WAIT 6 -#define SPECIAL_WAIT 9 + /* Shiva furnaces increase this by 1 each. */ +#define SHIVA_ENERGY_REGEN_INC 1 +#define ENERGY_WAIT 10 + /* Dynamos decrease this by 2 each, to a minimum of 4. */ +#define MIN_ENERGY_WAIT 4 +#define DYNAMO_UNIT_ENERGY_WAIT_DEC 2 + +#define MAX_THRUST 10 + /* Thrusters increase this and decrease THRUST_WAIT based on + * THRUST_INCREMENT, see InitDriveSlots near the bottom of this file + * for details. */ +#define THRUST_INCREMENT 4 +#define THRUST_WAIT 6 +#define TURN_WAIT 17 + /* Turning jets decrease by 2 each */ #define SHIP_MASS MAX_SHIP_MASS + +/* Primary weapon - energy cost and damage change at init time based on + * the number and type of weapon modules installed. */ + +#define BLASTER_DAMAGE 2 + /* This is the damage value for the basic ion bolt guns. Fusion + * blasters and hellbore cannons end up doing (BLASTER_DAMAGE * 2) + * and (BLASTER_DAMAGE * 3) damage, respectively, but this depends + * on enum values. */ + +#define BLASTER_HITS 2 /* Hitpoints for ion bolt guns, see BLASTER_DAMAGE */ + +#define WEAPON_ENERGY_COST 1 + /* This value gets thrown out and reset in an ugly manner based on + * the enum that is used for module IDs. Bigger gun = higher value. + */ +#define WEAPON_WAIT 6 #define BLASTER_SPEED DISPLAY_TO_WORLD (24) #define BLASTER_LIFE 12 + /* This value is greatly increased, based in part on the enum used + * for module IDs (bigger gun == longer life). See the first half of + * InitWeaponSlots */ +#define MAX_TRACKING 3 +#define TRACKER_ENERGY_COST 3 +#define BLASTER_OFFSET 8 +#define SIS_VERT_OFFSET 28 + /* Used for foward, spread, and rear slots */ +#define SIS_HORZ_OFFSET 20 + /* Used for side slot */ + + +/* Secondary weapon */ +#define SPECIAL_ENERGY_COST 0 + /* Increased by 1 for each point defense module */ +#define ANTIMISSILE_ENERGY_INC 1 +#define SPECIAL_WAIT 9 +#define LASER_RANGE (UWORD)100 +#define MAX_DEFENSE 8 + static RACE_DESC sis_desc = { @@ -404,7 +450,6 @@ spawn_point_defense (ELEMENT *ElementPtr) if (ObjectPtr != ShipPtr && CollidingElement (ObjectPtr) && !OBJECT_CLOAKED (ObjectPtr)) { -#define LASER_RANGE (UWORD)100 SIZE delta_x, delta_y; delta_x = ObjectPtr->next.location.x - @@ -507,8 +552,6 @@ sis_battle_postprocess (ELEMENT *ElementPtr) } } -#define BLASTER_DAMAGE 2 - static void blaster_collision (ELEMENT *ElementPtr0, POINT *pPt0, ELEMENT *ElementPtr1, POINT *pPt1) @@ -588,10 +631,6 @@ blaster_preprocess (ELEMENT *ElementPtr) static COUNT initialize_blasters (ELEMENT *ShipPtr, HELEMENT BlasterArray[]) { -#define SIS_VERT_OFFSET 28 -#define SIS_HORZ_OFFSET 20 -#define BLASTER_HITS 2 -#define BLASTER_OFFSET 8 BYTE nt; COUNT i; STARSHIP *StarShipPtr; @@ -707,10 +746,6 @@ sis_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern, static void InitWeaponSlots (RACE_DESC *RaceDescPtr, const BYTE *ModuleSlots) { -#define SIS_VERT_OFFSET 28 -#define SIS_HORZ_OFFSET 20 -#define BLASTER_HITS 2 -#define BLASTER_OFFSET 8 COUNT i; SIS_DATA *SisData = GetCustomShipData (RaceDescPtr); MISSILE_BLOCK *lpMB = SisData->MissileBlock; @@ -810,22 +845,26 @@ InitModuleSlots (RACE_DESC *RaceDescPtr, const BYTE *ModuleSlots) ++num_trackers; break; case ANTIMISSILE_DEFENSE: - ++RaceDescPtr->characteristics.special_energy_cost; + RaceDescPtr->characteristics.special_energy_cost += + ANTIMISSILE_ENERGY_INC; break; case SHIVA_FURNACE: - ++RaceDescPtr->characteristics.energy_regeneration; + RaceDescPtr->characteristics.energy_regeneration += + SHIVA_ENERGY_REGEN_INC; break; case DYNAMO_UNIT: - RaceDescPtr->characteristics.energy_wait -= 2; - if (RaceDescPtr->characteristics.energy_wait < 4) - RaceDescPtr->characteristics.energy_wait = 4; + RaceDescPtr->characteristics.energy_wait -= + DYNAMO_UNIT_ENERGY_WAIT_DEC; + if (RaceDescPtr->characteristics.energy_wait < MIN_ENERGY_WAIT) + RaceDescPtr->characteristics.energy_wait = MIN_ENERGY_WAIT; break; } } if (num_trackers > MAX_TRACKING) num_trackers = MAX_TRACKING; - RaceDescPtr->characteristics.weapon_energy_cost += num_trackers * 3; + RaceDescPtr->characteristics.weapon_energy_cost += + num_trackers * TRACKER_ENERGY_COST; SisData->num_trackers = num_trackers; if (RaceDescPtr->characteristics.special_energy_cost) { diff --git a/sc2/src/uqm/ships/slylandr/slylandr.c b/sc2/src/uqm/ships/slylandr/slylandr.c index 729700876..8c4ca95a2 100644 --- a/sc2/src/uqm/ships/slylandr/slylandr.c +++ b/sc2/src/uqm/ships/slylandr/slylandr.c @@ -23,22 +23,30 @@ #include "uqm/globdata.h" #include "libs/mathlib.h" - +// Core characteristics #define MAX_CREW 12 #define MAX_ENERGY 20 #define ENERGY_REGENERATION 0 -#define WEAPON_ENERGY_COST 2 -#define SPECIAL_ENERGY_COST 0 #define ENERGY_WAIT 10 #define MAX_THRUST 60 #define THRUST_INCREMENT MAX_THRUST -#define TURN_WAIT 0 #define THRUST_WAIT 0 -#define WEAPON_WAIT 17 -#define SPECIAL_WAIT 20 - +#define TURN_WAIT 0 #define SHIP_MASS 1 + +// Lightning weapon +#define WEAPON_ENERGY_COST 2 +#define WEAPON_WAIT 17 #define SLYLANDRO_OFFSET 9 +#define LASER_LENGTH 32 + /* Total length of lighting bolts. Actual range is usually less than + * this, since the lightning rarely is straight. */ + +// Harvester +#define SPECIAL_ENERGY_COST 0 +#define SPECIAL_WAIT 20 +#define HARVEST_RANGE (208 * 3 / 8) + /* Was originally (SPACE_HEIGHT * 3 / 8) */ static RACE_DESC slylandro_desc = { @@ -238,9 +246,8 @@ initialize_lightning (ELEMENT *ElementPtr, HELEMENT LaserArray[]) angle += LOWORD (rand_val) & (QUADRANT - 1); else angle -= LOWORD (rand_val) & (QUADRANT - 1); -#define LASER_RANGE 32 delta = WORLD_TO_VELOCITY ( - DISPLAY_TO_WORLD ((HIWORD (rand_val) & (LASER_RANGE - 1)) + 4) + DISPLAY_TO_WORLD ((HIWORD (rand_val) & (LASER_LENGTH - 1)) + 4) ); SetVelocityComponents (&LaserPtr->velocity, COSINE (angle, delta), SINE (angle, delta)); @@ -313,8 +320,6 @@ harvest_space_junk (ELEMENT *ElementPtr) && !GRAVITY_MASS (ObjPtr->mass_points) && CollisionPossible (ObjPtr, ElementPtr)) { -//HARVEST_RANGE was originally (SPACE_HEIGHT * 3 / 8) -#define HARVEST_RANGE (208 * 3 / 8) SIZE dx, dy; if ((dx = ObjPtr->next.location.x diff --git a/sc2/src/uqm/ships/spathi/spathi.c b/sc2/src/uqm/ships/spathi/spathi.c index f1f7243cf..aa4bd00ed 100644 --- a/sc2/src/uqm/ships/spathi/spathi.c +++ b/sc2/src/uqm/ships/spathi/spathi.c @@ -20,23 +20,39 @@ #include "spathi.h" #include "resinst.h" +// Core characteristics #define MAX_CREW 30 #define MAX_ENERGY 10 #define ENERGY_REGENERATION 1 -#define WEAPON_ENERGY_COST 2 -#define SPECIAL_ENERGY_COST 3 #define ENERGY_WAIT 10 #define MAX_THRUST 48 #define THRUST_INCREMENT 12 -#define TURN_WAIT 1 #define THRUST_WAIT 1 -#define WEAPON_WAIT 0 -#define SPECIAL_WAIT 7 - +#define TURN_WAIT 1 #define SHIP_MASS 5 + +// Forward gun +#define WEAPON_ENERGY_COST 2 +#define WEAPON_WAIT 0 +#define SPATHI_FORWARD_OFFSET 16 #define MISSILE_SPEED DISPLAY_TO_WORLD (30) #define MISSILE_LIFE 10 +#define MISSILE_HITS 1 +#define MISSILE_DAMAGE 1 +#define MISSILE_OFFSET 1 #define MISSILE_RANGE (MISSILE_SPEED * MISSILE_LIFE) + /* This is for the cyborg only. */ + +// B.U.T.T. +#define SPECIAL_ENERGY_COST 3 +#define SPECIAL_WAIT 7 +#define SPATHI_REAR_OFFSET 20 +#define DISCRIMINATOR_SPEED DISPLAY_TO_WORLD (8) +#define DISCRIMINATOR_LIFE 30 +#define DISCRIMINATOR_HITS 1 +#define DISCRIMINATOR_DAMAGE 2 +#define DISCRIMINATOR_OFFSET 4 +#define TRACK_WAIT 1 static RACE_DESC spathi_desc = { @@ -109,9 +125,6 @@ static RACE_DESC spathi_desc = 0, /* CodeRef */ }; -#define DISCRIMINATOR_SPEED DISPLAY_TO_WORLD (8) -#define TRACK_WAIT 1 - static void butt_missile_preprocess (ELEMENT *ElementPtr) { @@ -140,11 +153,6 @@ butt_missile_preprocess (ELEMENT *ElementPtr) static void spawn_butt_missile (ELEMENT *ShipPtr) { -#define SPATHI_REAR_OFFSET 20 -#define DISCRIMINATOR_LIFE 30 -#define DISCRIMINATOR_HITS 1 -#define DISCRIMINATOR_DAMAGE 2 -#define DISCRIMINATOR_OFFSET 4 HELEMENT ButtMissile; STARSHIP *StarShipPtr; MISSILE_BLOCK ButtMissileBlock; @@ -239,10 +247,6 @@ spathi_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern, static COUNT initialize_standard_missile (ELEMENT *ShipPtr, HELEMENT MissileArray[]) { -#define SPATHI_FORWARD_OFFSET 16 -#define MISSILE_HITS 1 -#define MISSILE_DAMAGE 1 -#define MISSILE_OFFSET 1 STARSHIP *StarShipPtr; MISSILE_BLOCK MissileBlock; diff --git a/sc2/src/uqm/ships/supox/supox.c b/sc2/src/uqm/ships/supox/supox.c index b9dd4656a..854c5b37c 100644 --- a/sc2/src/uqm/ships/supox/supox.c +++ b/sc2/src/uqm/ships/supox/supox.c @@ -22,23 +22,32 @@ #include "libs/mathlib.h" - +// Core characteristics #define MAX_CREW 12 #define MAX_ENERGY 16 #define ENERGY_REGENERATION 1 -#define WEAPON_ENERGY_COST 1 -#define SPECIAL_ENERGY_COST 1 #define ENERGY_WAIT 4 #define MAX_THRUST 40 #define THRUST_INCREMENT 8 -#define TURN_WAIT 1 #define THRUST_WAIT 0 -#define WEAPON_WAIT 2 -#define SPECIAL_WAIT 0 - +#define TURN_WAIT 1 #define SHIP_MASS 4 + +// Gob launcher +#define WEAPON_ENERGY_COST 1 +#define WEAPON_WAIT 2 +#define SUPOX_OFFSET 23 +#define MISSILE_OFFSET 2 #define MISSILE_SPEED DISPLAY_TO_WORLD (30) #define MISSILE_LIFE 10 +#define MISSILE_HITS 1 +#define MISSILE_DAMAGE 1 + +// Lateral/reverse thrust +#define SPECIAL_ENERGY_COST 1 + /* Unused - uncomment below to enable. */ +#define SPECIAL_WAIT 0 + /* Unused except to initialize supox_desc.special_wait */ static RACE_DESC supox_desc = { @@ -179,10 +188,6 @@ supox_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern, static COUNT initialize_horn (ELEMENT *ShipPtr, HELEMENT HornArray[]) { -#define MISSILE_HITS 1 -#define MISSILE_DAMAGE 1 -#define MISSILE_OFFSET 2 -#define SUPOX_OFFSET 23 STARSHIP *StarShipPtr; MISSILE_BLOCK MissileBlock; diff --git a/sc2/src/uqm/ships/syreen/syreen.c b/sc2/src/uqm/ships/syreen/syreen.c index fbcf8eb07..c65ecd204 100644 --- a/sc2/src/uqm/ships/syreen/syreen.c +++ b/sc2/src/uqm/ships/syreen/syreen.c @@ -22,24 +22,33 @@ #include "libs/mathlib.h" - +// Core characteristics #define SYREEN_MAX_CREW_SIZE MAX_CREW_SIZE #define MAX_CREW 12 #define MAX_ENERGY 16 #define ENERGY_REGENERATION 1 -#define WEAPON_ENERGY_COST 1 -#define SPECIAL_ENERGY_COST 5 #define ENERGY_WAIT 6 #define MAX_THRUST /* DISPLAY_TO_WORLD (8) */ 36 #define THRUST_INCREMENT /* DISPLAY_TO_WORLD (2) */ 9 -#define TURN_WAIT 1 #define THRUST_WAIT 1 -#define WEAPON_WAIT 8 -#define SPECIAL_WAIT 20 - +#define TURN_WAIT 1 #define SHIP_MASS 2 + +// Particle Beam Stiletto +#define WEAPON_ENERGY_COST 1 +#define WEAPON_WAIT 8 +#define SYREEN_OFFSET 30 #define MISSILE_SPEED DISPLAY_TO_WORLD (30) #define MISSILE_LIFE 10 +#define MISSILE_HITS 1 +#define MISSILE_DAMAGE 2 +#define MISSILE_OFFSET 3 + +// Syreen song +#define SPECIAL_ENERGY_COST 5 +#define SPECIAL_WAIT 20 +#define ABANDONER_RANGE 208 /* originally SPACE_HEIGHT */ +#define MAX_ABANDONERS 8 static RACE_DESC syreen_desc = { @@ -115,10 +124,6 @@ static RACE_DESC syreen_desc = static COUNT initialize_dagger (ELEMENT *ShipPtr, HELEMENT DaggerArray[]) { -#define SYREEN_OFFSET 30 -#define MISSILE_HITS 1 -#define MISSILE_DAMAGE 2 -#define MISSILE_OFFSET 3 STARSHIP *StarShipPtr; MISSILE_BLOCK MissileBlock; @@ -194,13 +199,11 @@ spawn_crew (ELEMENT *ElementPtr) dx = WORLD_TO_DISPLAY (dx); dy = WORLD_TO_DISPLAY (dy); -#define ABANDONER_RANGE 208 /* originally SPACE_HEIGHT */ if (dx <= ABANDONER_RANGE && dy <= ABANDONER_RANGE && (d_squared = (DWORD)((UWORD)dx * (UWORD)dx) + (DWORD)((UWORD)dy * (UWORD)dy)) <= (DWORD)((UWORD)ABANDONER_RANGE * (UWORD)ABANDONER_RANGE)) { -#define MAX_ABANDONERS 8 COUNT crew_loss; crew_loss = ((MAX_ABANDONERS diff --git a/sc2/src/uqm/ships/thradd/thradd.c b/sc2/src/uqm/ships/thradd/thradd.c index 3e3c3d707..0d7a8e24a 100644 --- a/sc2/src/uqm/ships/thradd/thradd.c +++ b/sc2/src/uqm/ships/thradd/thradd.c @@ -22,24 +22,42 @@ #include "uqm/globdata.h" - +// Core characteristics #define MAX_CREW 8 #define MAX_ENERGY 24 #define ENERGY_REGENERATION 1 -#define WEAPON_ENERGY_COST 2 -#define SPECIAL_ENERGY_COST 1 #define ENERGY_WAIT 6 #define MAX_THRUST 28 #define THRUST_INCREMENT 7 -#define TURN_WAIT 1 #define THRUST_WAIT 0 -#define WEAPON_WAIT 12 -#define SPECIAL_WAIT 0 - +#define TURN_WAIT 1 #define SHIP_MASS 7 -#define THRADDASH_OFFSET 9 + +// Ion Blasters +#define WEAPON_ENERGY_COST 2 +#define WEAPON_WAIT 12 #define MISSILE_SPEED DISPLAY_TO_WORLD (30) #define MISSILE_LIFE 15 +#define MISSILE_OFFSET 3 +#define THRADDASH_OFFSET 9 +#define MISSILE_HITS 2 +#define MISSILE_DAMAGE 1 + +// Afterburner +#define SPECIAL_ENERGY_COST 1 +#define SPECIAL_WAIT 0 +#define SPECIAL_THRUST_INCREMENT 12 +#define SPECIAL_MAX_THRUST 72 +#define NAPALM_LIFE 48 +#define NAPALM_OFFSET 0 +#define NAPALM_HITS 1 +#define NAPALM_DAMAGE 2 +#define NAPALM_DECAY_RATE 5 + /* Controls the speed of the afterburner "decay" animation; it will + * decay one step (one animation frame) per NAPALM_DECAY_RATE + * frames. */ +#define NUM_NAPALM_FADES 6 + static RACE_DESC thraddash_desc = { @@ -202,8 +220,6 @@ thraddash_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern, } } -#define NAPALM_WAIT 1 - static void flame_napalm_preprocess (ELEMENT *ElementPtr) { @@ -220,24 +236,26 @@ flame_napalm_preprocess (ELEMENT *ElementPtr) InitIntersectEndPoint (ElementPtr); InitIntersectFrame (ElementPtr); } + /* turn_wait is abused here to store the speed of the decay animation */ else if (ElementPtr->turn_wait > 0) --ElementPtr->turn_wait; else { -#define NUM_NAPALM_FADES 6 - if (ElementPtr->life_span <= NUM_NAPALM_FADES * (NAPALM_WAIT + 1) + if (ElementPtr->life_span <= NUM_NAPALM_FADES * (NAPALM_DECAY_RATE + 1) || GetFrameIndex ( ElementPtr->current.image.frame ) != NUM_NAPALM_FADES) ElementPtr->next.image.frame = DecFrameIndex (ElementPtr->current.image.frame); - else if (ElementPtr->life_span > NUM_NAPALM_FADES * (NAPALM_WAIT + 1)) + else if (ElementPtr->life_span > NUM_NAPALM_FADES * (NAPALM_DECAY_RATE + 1)) ElementPtr->next.image.frame = SetAbsFrameIndex ( ElementPtr->current.image.frame, GetFrameCount (ElementPtr->current.image.frame) - 1 ); - ElementPtr->turn_wait = NAPALM_WAIT; + /* turn_wait is abused here to store the speed of the decay + * animation. */ + ElementPtr->turn_wait = NAPALM_DECAY_RATE; ElementPtr->state_flags |= CHANGING; } } @@ -245,9 +263,6 @@ flame_napalm_preprocess (ELEMENT *ElementPtr) static COUNT initialize_horn (ELEMENT *ShipPtr, HELEMENT HornArray[]) { -#define MISSILE_HITS 2 -#define MISSILE_DAMAGE 1 -#define MISSILE_OFFSET 3 STARSHIP *StarShipPtr; MISSILE_BLOCK MissileBlock; @@ -284,8 +299,6 @@ thraddash_preprocess (ELEMENT *ElementPtr) } else if (DeltaEnergy (ElementPtr, -SPECIAL_ENERGY_COST)) { -#define SPECIAL_THRUST_INCREMENT 12 -#define SPECIAL_MAX_THRUST 72 COUNT max_thrust, thrust_increment; STATUS_FLAGS thrust_status; HELEMENT hTrailElement; @@ -317,10 +330,6 @@ thraddash_preprocess (ELEMENT *ElementPtr) StarShipPtr->RaceDescPtr->characteristics.max_thrust = max_thrust; { -#define NAPALM_HITS 1 -#define NAPALM_DAMAGE 2 -#define NAPALM_LIFE 48 -#define NAPALM_OFFSET 0 MISSILE_BLOCK MissileBlock; MissileBlock.cx = ElementPtr->next.location.x; @@ -348,7 +357,10 @@ thraddash_preprocess (ELEMENT *ElementPtr) LockElement (hTrailElement, &TrailElementPtr); SetElementStarShip (TrailElementPtr, StarShipPtr); TrailElementPtr->hTarget = 0; - TrailElementPtr->turn_wait = NAPALM_WAIT; + + /* turn_wait is abused here to store the speed of the decay + * animation */ + TrailElementPtr->turn_wait = NAPALM_DECAY_RATE; TrailElementPtr->state_flags |= NONSOLID; SetPrimType ( diff --git a/sc2/src/uqm/ships/umgah/umgah.c b/sc2/src/uqm/ships/umgah/umgah.c index 054cdfa9a..370ad4036 100644 --- a/sc2/src/uqm/ships/umgah/umgah.c +++ b/sc2/src/uqm/ships/umgah/umgah.c @@ -22,22 +22,31 @@ #include "libs/mathlib.h" - +// Core characteristics #define MAX_CREW 10 #define MAX_ENERGY 30 #define ENERGY_REGENERATION MAX_ENERGY -#define WEAPON_ENERGY_COST 0 -#define SPECIAL_ENERGY_COST 1 #define ENERGY_WAIT 150 #define MAX_THRUST /* DISPLAY_TO_WORLD (5) */ 18 #define THRUST_INCREMENT /* DISPLAY_TO_WORLD (2) */ 6 -#define TURN_WAIT 4 #define THRUST_WAIT 3 -#define WEAPON_WAIT 0 -#define SPECIAL_WAIT 2 - +#define TURN_WAIT 4 #define SHIP_MASS 1 +// Antimatter cone +#define WEAPON_ENERGY_COST 0 +#define WEAPON_WAIT 0 +#define UMGAH_OFFSET 0 +#define CONE_OFFSET 0 +#define CONE_SPEED 0 +#define CONE_HITS 100 +#define CONE_DAMAGE 1 +#define CONE_LIFE 1 + +// Retropropulsion +#define SPECIAL_ENERGY_COST 1 +#define SPECIAL_WAIT 2 +#define JUMP_DIST DISPLAY_TO_WORLD (40) static RACE_DESC umgah_desc = { @@ -179,8 +188,6 @@ cone_collision (ELEMENT *ElementPtr0, POINT *pPt0, } } -#define JUMP_DIST DISPLAY_TO_WORLD (40) - static void umgah_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern, COUNT ConcernCounter) @@ -299,12 +306,6 @@ umgah_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern, static COUNT initialize_cone (ELEMENT *ShipPtr, HELEMENT ConeArray[]) { -#define UMGAH_OFFSET 0 -#define MISSILE_SPEED 0 -#define MISSILE_HITS 100 -#define MISSILE_DAMAGE 1 -#define MISSILE_LIFE 1 -#define MISSILE_OFFSET 0 STARSHIP *StarShipPtr; UMGAH_DATA* UmgahData; MISSILE_BLOCK MissileBlock; @@ -317,12 +318,12 @@ initialize_cone (ELEMENT *ShipPtr, HELEMENT ConeArray[]) MissileBlock.sender = ShipPtr->playerNr; MissileBlock.flags = IGNORE_SIMILAR; MissileBlock.pixoffs = UMGAH_OFFSET; - MissileBlock.speed = MISSILE_SPEED; - MissileBlock.hit_points = MISSILE_HITS; - MissileBlock.damage = MISSILE_DAMAGE; - MissileBlock.life = MISSILE_LIFE; + MissileBlock.speed = CONE_SPEED; + MissileBlock.hit_points = CONE_HITS; + MissileBlock.damage = CONE_DAMAGE; + MissileBlock.life = CONE_LIFE; MissileBlock.preprocess_func = cone_preprocess; - MissileBlock.blast_offs = MISSILE_OFFSET; + MissileBlock.blast_offs = CONE_OFFSET; // This func is called every frame while the player is holding down WEAPON // Don't reset the cone FRAME to the first image every time diff --git a/sc2/src/uqm/ships/urquan/urquan.c b/sc2/src/uqm/ships/urquan/urquan.c index ec31087d3..9df87d015 100644 --- a/sc2/src/uqm/ships/urquan/urquan.c +++ b/sc2/src/uqm/ships/urquan/urquan.c @@ -24,23 +24,39 @@ #include - +// Core characteristics #define MAX_CREW MAX_CREW_SIZE #define MAX_ENERGY MAX_ENERGY_SIZE #define ENERGY_REGENERATION 1 -#define WEAPON_ENERGY_COST 6 -#define SPECIAL_ENERGY_COST 8 #define ENERGY_WAIT 4 #define MAX_THRUST 30 #define THRUST_INCREMENT 6 -#define TURN_WAIT 4 #define THRUST_WAIT 6 -#define WEAPON_WAIT 6 -#define SPECIAL_WAIT 9 - +#define TURN_WAIT 4 #define SHIP_MASS 10 + +// Fusion blast +#define WEAPON_ENERGY_COST 6 +#define WEAPON_WAIT 6 #define MISSILE_SPEED DISPLAY_TO_WORLD (20) #define MISSILE_LIFE 20 +#define MISSILE_HITS 10 +#define MISSILE_DAMAGE 6 +#define MISSILE_OFFSET 8 +#define URQUAN_OFFSET 32 + +// Fighters +#define SPECIAL_ENERGY_COST 8 +#define SPECIAL_WAIT 9 +#define FIGHTER_OFFSET 4 +#define FIGHTER_SPEED DISPLAY_TO_WORLD (8) +#define ONE_WAY_FLIGHT 125 +#define TRACK_THRESHOLD 6 +#define FIGHTER_LIFE (ONE_WAY_FLIGHT + ONE_WAY_FLIGHT + 150) +#define FIGHTER_HITS 1 +#define FIGHTER_MASS 0 +#define FIGHTER_WEAPON_WAIT 8 +#define FIGHTER_LASER_RANGE DISPLAY_TO_WORLD (40 + FIGHTER_OFFSET) static RACE_DESC urquan_desc = { @@ -116,10 +132,6 @@ static RACE_DESC urquan_desc = static COUNT initialize_fusion (ELEMENT *ShipPtr, HELEMENT FusionArray[]) { -#define MISSILE_HITS 10 -#define MISSILE_DAMAGE 6 -#define MISSILE_OFFSET 8 -#define URQUAN_OFFSET 32 STARSHIP *StarShipPtr; MISSILE_BLOCK MissileBlock; @@ -142,15 +154,6 @@ initialize_fusion (ELEMENT *ShipPtr, HELEMENT FusionArray[]) return (1); } -#define TRACK_THRESHOLD 6 -#define FIGHTER_SPEED DISPLAY_TO_WORLD (8) -#define ONE_WAY_FLIGHT 125 -#define FIGHTER_LIFE (ONE_WAY_FLIGHT + ONE_WAY_FLIGHT + 150) - -#define FIGHTER_WEAPON_WAIT 8 -#define FIGHTER_OFFSET 4 -#define LASER_RANGE DISPLAY_TO_WORLD (40 + FIGHTER_OFFSET) - static void fighter_postprocess (ELEMENT *ElementPtr) { @@ -162,8 +165,8 @@ fighter_postprocess (ELEMENT *ElementPtr) LaserBlock.cx = ElementPtr->next.location.x; LaserBlock.cy = ElementPtr->next.location.y; LaserBlock.face = ElementPtr->thrust_wait; - LaserBlock.ex = COSINE (FACING_TO_ANGLE (LaserBlock.face), LASER_RANGE); - LaserBlock.ey = SINE (FACING_TO_ANGLE (LaserBlock.face), LASER_RANGE); + LaserBlock.ex = COSINE (FACING_TO_ANGLE (LaserBlock.face), FIGHTER_LASER_RANGE); + LaserBlock.ey = SINE (FACING_TO_ANGLE (LaserBlock.face), FIGHTER_LASER_RANGE); LaserBlock.sender = ElementPtr->playerNr; LaserBlock.flags = IGNORE_SIMILAR; LaserBlock.pixoffs = FIGHTER_OFFSET; @@ -256,10 +259,10 @@ fighter_preprocess (ELEMENT *ElementPtr) delta_y = WRAP_DELTA_Y (delta_y); if (ElementPtr->thrust_wait == 0 - && abs (delta_x) < LASER_RANGE * 3 / 4 - && abs (delta_y) < LASER_RANGE * 3 / 4 + && abs (delta_x) < FIGHTER_LASER_RANGE * 3 / 4 + && abs (delta_y) < FIGHTER_LASER_RANGE * 3 / 4 && delta_x * delta_x + delta_y * delta_y < - (LASER_RANGE * 3 / 4) * (LASER_RANGE * 3 / 4)) + (FIGHTER_LASER_RANGE * 3 / 4) * (FIGHTER_LASER_RANGE * 3 / 4)) { ElementPtr->thrust_wait = (BYTE)NORMALIZE_FACING ( @@ -414,8 +417,8 @@ spawn_fighters (ELEMENT *ElementPtr) PutElement (hFighterElement); LockElement (hFighterElement, &FighterElementPtr); - FighterElementPtr->hit_points = 1; - FighterElementPtr->mass_points = 0; + FighterElementPtr->hit_points = FIGHTER_HITS; + FighterElementPtr->mass_points = FIGHTER_MASS; FighterElementPtr->thrust_wait = TRACK_THRESHOLD + 1; FighterElementPtr->playerNr = ElementPtr->playerNr; FighterElementPtr->state_flags = APPEARING | FINITE_LIFE diff --git a/sc2/src/uqm/ships/utwig/utwig.c b/sc2/src/uqm/ships/utwig/utwig.c index af4cc588f..cb5f2fdcb 100644 --- a/sc2/src/uqm/ships/utwig/utwig.c +++ b/sc2/src/uqm/ships/utwig/utwig.c @@ -23,24 +23,36 @@ #include "uqm/globdata.h" #include "libs/mathlib.h" - +// Core characteristics #define MAX_CREW 20 #define MAX_ENERGY 20 #define ENERGY_REGENERATION 0 -#define WEAPON_ENERGY_COST 0 -#define SPECIAL_ENERGY_COST 1 #define ENERGY_WAIT 255 #define MAX_THRUST 36 #define THRUST_INCREMENT 6 -#define TURN_WAIT 1 #define THRUST_WAIT 6 -#define WEAPON_WAIT 7 -#define SPECIAL_WAIT 12 - +#define TURN_WAIT 1 #define SHIP_MASS 8 + +// Weapon +#define WEAPON_ENERGY_COST 0 +#define WEAPON_WAIT 7 #define UTWIG_OFFSET 9 #define MISSILE_SPEED DISPLAY_TO_WORLD (30) #define MISSILE_LIFE 10 +#define MISSILE_HITS 1 +#define MISSILE_DAMAGE 1 +#define MISSILE_OFFSET 1 +#define LAUNCH_XOFFS0 DISPLAY_TO_WORLD (5) +#define LAUNCH_YOFFS0 -DISPLAY_TO_WORLD (18) +#define LAUNCH_XOFFS1 DISPLAY_TO_WORLD (13) +#define LAUNCH_YOFFS1 -DISPLAY_TO_WORLD (9) +#define LAUNCH_XOFFS2 DISPLAY_TO_WORLD (17) +#define LAUNCH_YOFFS2 -DISPLAY_TO_WORLD (4) + +// Shield +#define SPECIAL_ENERGY_COST 1 +#define SPECIAL_WAIT 12 static RACE_DESC utwig_desc = { @@ -116,15 +128,6 @@ static RACE_DESC utwig_desc = static COUNT initialize_lance (ELEMENT *ShipPtr, HELEMENT WeaponArray[]) { -#define LAUNCH_XOFFS0 DISPLAY_TO_WORLD (5) -#define LAUNCH_YOFFS0 -DISPLAY_TO_WORLD (18) -#define LAUNCH_XOFFS1 DISPLAY_TO_WORLD (13) -#define LAUNCH_YOFFS1 -DISPLAY_TO_WORLD (9) -#define LAUNCH_XOFFS2 DISPLAY_TO_WORLD (17) -#define LAUNCH_YOFFS2 -DISPLAY_TO_WORLD (4) -#define MISSILE_HITS 1 -#define MISSILE_DAMAGE 1 -#define MISSILE_OFFSET 1 COUNT i; STARSHIP *StarShipPtr; MISSILE_BLOCK MissileBlock; diff --git a/sc2/src/uqm/ships/vux/vux.c b/sc2/src/uqm/ships/vux/vux.c index a6975924e..83e6c470e 100644 --- a/sc2/src/uqm/ships/vux/vux.c +++ b/sc2/src/uqm/ships/vux/vux.c @@ -23,26 +23,44 @@ #include "uqm/globdata.h" #include "libs/mathlib.h" - +// Core characteristics #define MAX_CREW 20 #define MAX_ENERGY 40 #define ENERGY_REGENERATION 1 -#define WEAPON_ENERGY_COST 1 -#define SPECIAL_ENERGY_COST 2 #define ENERGY_WAIT 8 #define MAX_THRUST /* DISPLAY_TO_WORLD (5) */ 21 #define THRUST_INCREMENT /* DISPLAY_TO_WORLD (2) */ 7 -#define TURN_WAIT 6 #define THRUST_WAIT 4 -#define WEAPON_WAIT 0 -#define SPECIAL_WAIT 7 - +#define TURN_WAIT 6 #define SHIP_MASS 6 -#define WARP_OFFSET 46 /* How far outside of laser-range ship can warp in */ + +// Laser +#define WEAPON_ENERGY_COST 1 +#define WEAPON_WAIT 0 #define VUX_OFFSET 12 #define LASER_BASE 150 #define LASER_RANGE DISPLAY_TO_WORLD (LASER_BASE + VUX_OFFSET) +// Limpet +#define SPECIAL_ENERGY_COST 2 +#define SPECIAL_WAIT 7 +#define LIMPET_SPEED 25 +#define LIMPET_OFFSET 8 +#define LIMPET_LIFE 80 +#define LIMPET_HITS 1 +#define LIMPET_DAMAGE 0 +#define MIN_THRUST_INCREMENT DISPLAY_TO_WORLD (1) + +// Aggressive Entry +#define WARP_OFFSET 46 + /* How far outside of the laser range can the ship warp in. */ +#define MAXX_ENTRY_DIST DISPLAY_TO_WORLD ((LASER_BASE + VUX_OFFSET + WARP_OFFSET) << 1) +#define MAXY_ENTRY_DIST DISPLAY_TO_WORLD ((LASER_BASE + VUX_OFFSET + WARP_OFFSET) << 1) + /* Originally, the warp distance was: + * DISPLAY_TO_WORLD (SPACE_HEIGHT << 1) + * where SPACE_HEIGHT = SCREEN_HEIGHT - (SAFE_Y * 2) + * But in reality this should be relative to the laser-range. */ + static RACE_DESC vux_desc = { { /* SHIP_INFO */ @@ -114,7 +132,6 @@ static RACE_DESC vux_desc = 0, /* CodeRef */ }; -#define LIMPET_SPEED 25 static void limpet_preprocess (ELEMENT *ElementPtr) @@ -128,8 +145,7 @@ limpet_preprocess (ELEMENT *ElementPtr) if ((delta_facing = TrackShip (ElementPtr, &facing)) > 0) { facing = orig_facing + delta_facing; - SetVelocityVector (&ElementPtr->velocity, - LIMPET_SPEED, facing); + SetVelocityVector (&ElementPtr->velocity, LIMPET_SPEED, facing); } ElementPtr->next.image.frame = IncFrameIndex (ElementPtr->next.image.frame); @@ -154,7 +170,6 @@ limpet_collision (ELEMENT *ElementPtr0, POINT *pPt0, --RDPtr->characteristics.turn_wait; if (++RDPtr->characteristics.thrust_wait == 0) --RDPtr->characteristics.thrust_wait; -#define MIN_THRUST_INCREMENT DISPLAY_TO_WORLD (1) if (RDPtr->characteristics.thrust_increment <= MIN_THRUST_INCREMENT) { RDPtr->characteristics.max_thrust = @@ -193,10 +208,6 @@ limpet_collision (ELEMENT *ElementPtr0, POINT *pPt0, static void spawn_limpets (ELEMENT *ElementPtr) { -#define LIMPET_OFFSET 8 -#define LIMPET_LIFE 80 -#define LIMPET_HITS 1 -#define LIMPET_DAMAGE 0 HELEMENT Limpet; STARSHIP *StarShipPtr; MISSILE_BLOCK MissileBlock; @@ -327,12 +338,6 @@ vux_preprocess (ELEMENT *ElementPtr) do { - // Originally, the warp distance was: - // DISPLAY_TO_WORLD (SPACE_HEIGHT << 1) - // where SPACE_HEIGHT = SCREEN_HEIGHT - (SAFE_Y * 2) - // But in reality this should be relative to the laser-range -#define MAXX_ENTRY_DIST DISPLAY_TO_WORLD ((LASER_BASE + VUX_OFFSET + WARP_OFFSET) << 1) -#define MAXY_ENTRY_DIST DISPLAY_TO_WORLD ((LASER_BASE + VUX_OFFSET + WARP_OFFSET) << 1) SIZE dx, dy; ElementPtr->current.location.x = diff --git a/sc2/src/uqm/ships/yehat/yehat.c b/sc2/src/uqm/ships/yehat/yehat.c index 7ebe109ca..f3d0fb899 100644 --- a/sc2/src/uqm/ships/yehat/yehat.c +++ b/sc2/src/uqm/ships/yehat/yehat.c @@ -22,23 +22,32 @@ #include "libs/mathlib.h" - +// Core characteristics #define MAX_CREW 20 #define MAX_ENERGY 10 #define ENERGY_REGENERATION 2 -#define WEAPON_ENERGY_COST 1 -#define SPECIAL_ENERGY_COST 3 #define ENERGY_WAIT 6 #define MAX_THRUST 30 #define THRUST_INCREMENT 6 -#define TURN_WAIT 2 #define THRUST_WAIT 2 -#define WEAPON_WAIT 0 -#define SPECIAL_WAIT 2 - +#define TURN_WAIT 2 #define SHIP_MASS 3 + +// Twin Pulse Cannon +#define WEAPON_ENERGY_COST 1 +#define WEAPON_WAIT 0 +#define YEHAT_OFFSET 16 +#define LAUNCH_OFFS DISPLAY_TO_WORLD (8) #define MISSILE_SPEED DISPLAY_TO_WORLD (20) #define MISSILE_LIFE 10 +#define MISSILE_HITS 1 +#define MISSILE_DAMAGE 1 +#define MISSILE_OFFSET 1 + +// Force Shield +#define SPECIAL_ENERGY_COST 3 +#define SPECIAL_WAIT 2 +#define SHIELD_LIFE 10 static RACE_DESC yehat_desc = { @@ -114,11 +123,6 @@ static RACE_DESC yehat_desc = static COUNT initialize_standard_missiles (ELEMENT *ShipPtr, HELEMENT MissileArray[]) { -#define YEHAT_OFFSET 16 -#define LAUNCH_OFFS DISPLAY_TO_WORLD (8) -#define MISSILE_HITS 1 -#define MISSILE_DAMAGE 1 -#define MISSILE_OFFSET 1 SIZE offs_x, offs_y; STARSHIP *StarShipPtr; MISSILE_BLOCK MissileBlock; @@ -334,7 +338,6 @@ yehat_preprocess (ELEMENT *ElementPtr) DeltaEnergy (ElementPtr, -SPECIAL_ENERGY_COST); /* so text will flash */ else { -#define SHIELD_LIFE 10 ElementPtr->life_span = SHIELD_LIFE + NORMAL_LIFE; ElementPtr->next.image.farray = StarShipPtr->RaceDescPtr->ship_data.special; diff --git a/sc2/src/uqm/ships/zoqfot/zoqfot.c b/sc2/src/uqm/ships/zoqfot/zoqfot.c index d6842745a..15a602481 100644 --- a/sc2/src/uqm/ships/zoqfot/zoqfot.c +++ b/sc2/src/uqm/ships/zoqfot/zoqfot.c @@ -22,24 +22,39 @@ #include "libs/mathlib.h" - +// Core characteristics #define MAX_CREW 10 #define MAX_ENERGY 10 #define ENERGY_REGENERATION 1 -#define WEAPON_ENERGY_COST 1 -#define SPECIAL_ENERGY_COST (MAX_ENERGY * 3 / 4) #define ENERGY_WAIT 4 #define MAX_THRUST 40 #define THRUST_INCREMENT 10 -#define TURN_WAIT 1 #define THRUST_WAIT 0 -#define WEAPON_WAIT 0 -#define SPECIAL_WAIT 6 - +#define TURN_WAIT 1 #define SHIP_MASS 5 + +// Main weapon +#define WEAPON_ENERGY_COST 1 +#define WEAPON_WAIT 0 +#define ZOQFOTPIK_OFFSET 13 +#define MISSILE_OFFSET 0 #define MISSILE_SPEED DISPLAY_TO_WORLD (10) + /* Used by the cyborg only. */ #define MISSILE_LIFE 10 #define MISSILE_RANGE (MISSILE_SPEED * MISSILE_LIFE) +#define MISSILE_DAMAGE 1 +#define MISSILE_HITS 1 +#define SPIT_WAIT 2 + /* Controls the main weapon color change animation's speed. + * The animation advances one frame every SPIT_WAIT frames. */ + +// Tongue +#define SPECIAL_ENERGY_COST (MAX_ENERGY * 3 / 4) +#define SPECIAL_WAIT 6 +#define TONGUE_SPEED 0 +#define TONGUE_HITS 1 +#define TONGUE_DAMAGE 12 +#define TONGUE_OFFSET 4 static RACE_DESC zoqfotpik_desc = { @@ -112,12 +127,10 @@ static RACE_DESC zoqfotpik_desc = 0, /* CodeRef */ }; -#define ZOQFOTPIK_OFFSET 13 -#define SPIT_WAIT 2 - static void spit_preprocess (ELEMENT *ElementPtr) { + /* turn_wait is abused here to control the animation speed. */ if (ElementPtr->turn_wait > 0) --ElementPtr->turn_wait; else @@ -136,6 +149,7 @@ spit_preprocess (ELEMENT *ElementPtr) (SIZE)COSINE (angle, speed), (SIZE)SINE (angle, speed)); + /* turn_wait is abused here to control the animation speed. */ ElementPtr->turn_wait = SPIT_WAIT; ElementPtr->state_flags |= CHANGING; } @@ -144,9 +158,6 @@ spit_preprocess (ELEMENT *ElementPtr) static COUNT initialize_spit (ELEMENT *ShipPtr, HELEMENT SpitArray[]) { -#define MISSILE_DAMAGE 1 -#define MISSILE_HITS 1 -#define MISSILE_OFFSET 0 STARSHIP *StarShipPtr; MISSILE_BLOCK MissileBlock; @@ -199,10 +210,6 @@ tongue_collision (ELEMENT *ElementPtr0, POINT *pPt0, static void spawn_tongue (ELEMENT *ElementPtr) { -#define TONGUE_SPEED 0 -#define TONGUE_HITS 1 -#define TONGUE_DAMAGE 12 -#define TONGUE_OFFSET 4 STARSHIP *StarShipPtr; MISSILE_BLOCK TongueBlock; HELEMENT Tongue;