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.
This commit is contained in:
Michael Martin
2013-09-25 01:15:30 -07:00
parent 0851e23c8e
commit 28fb037258
5 changed files with 268 additions and 55 deletions
+196 -12
View File
@@ -34,6 +34,7 @@
#include "sounds.h" #include "sounds.h"
#include "util.h" #include "util.h"
#include "libs/graphics/gfx_common.h" #include "libs/graphics/gfx_common.h"
#include "libs/log.h"
#include <ctype.h> #include <ctype.h>
@@ -352,10 +353,178 @@ NameCaptainOrShip (bool nameCaptain)
DrawNameString (nameCaptain, buf, 0, DDSHS_NORMAL); 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) if (namingCB)
namingCB (); namingCB ();
SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT); SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
HFree (gIndex);
SetFlashRect (NULL);
if (tes.Success)
return (TRUE);
else
return (FALSE);
} }
void void
@@ -825,9 +994,10 @@ DrawGameSelection (PICK_GAME_STATE *pickState, COUNT selSlot)
{ {
DateToString (buf2, sizeof buf2, desc->month_index, DateToString (buf2, sizeof buf2, desc->month_index,
desc->day_index, desc->year_index); desc->day_index, desc->year_index);
snprintf (buf, sizeof buf, "%s %s", if (desc->SaveName[0])
GAME_STRING (SAVEGAME_STRING_BASE + 4), buf2); snprintf (buf, sizeof buf, "%s: %s", buf2, desc->SaveName);
// "Saved Game - Date:" else
snprintf (buf, sizeof buf, "%s", buf2);
} }
font_DrawText (&t); font_DrawText (&t);
} }
@@ -936,22 +1106,35 @@ DoPickGame (MENU_STATE *pMS)
} }
static BOOLEAN 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; SUMMARY_DESC *desc = pickState->summary + gameIndex;
UNICODE nameBuf[SAVE_NAME_SIZE];
STAMP saveStamp; STAMP saveStamp;
BOOLEAN success; BOOLEAN success;
saveStamp.frame = NULL; saveStamp.frame = NULL;
// TODO: fix ConfirmSaveLoad() interface so it does not rely on
// MsgStamp != NULL parameter.
ConfirmSaveLoad (pickState->saving ? &saveStamp : NULL);
if (pickState->saving) 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 else
{
success = FALSE;
*canceled_by_user = TRUE;
}
}
else
{
ConfirmSaveLoad (pickState->saving ? &saveStamp : NULL);
success = LoadGame (gameIndex, NULL); success = LoadGame (gameIndex, NULL);
}
// TODO: the same should be done for both save and load if we also // TODO: the same should be done for both save and load if we also
// display a load problem message // display a load problem message
@@ -1032,6 +1215,8 @@ PickGame (BOOLEAN saving, BOOLEAN fromMainMenu)
// Save/load retry loop // Save/load retry loop
while (1) while (1)
{ {
BOOLEAN canceled_by_user = FALSE;
pickState.success = FALSE; pickState.success = FALSE;
DoInput (&MenuState, TRUE); DoInput (&MenuState, TRUE);
if (!pickState.success) if (!pickState.success)
@@ -1039,11 +1224,11 @@ PickGame (BOOLEAN saving, BOOLEAN fromMainMenu)
lastUsedSlot = MenuState.CurState; lastUsedSlot = MenuState.CurState;
if (SaveLoadGame (&pickState, MenuState.CurState)) if (SaveLoadGame (&pickState, MenuState.CurState, &canceled_by_user))
break; // all good break; // all good
// something broke // something broke
if (saving) if (saving && !canceled_by_user)
SaveProblem (); SaveProblem ();
// TODO: Shouldn't we have a Problem() equivalent for Load too? // TODO: Shouldn't we have a Problem() equivalent for Load too?
@@ -1154,4 +1339,3 @@ GameOptions (void)
return !(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)); return !(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD));
} }
+29 -8
View File
@@ -480,10 +480,10 @@ LoadGameState (GAME_STATE *GSPtr, DECODE_REF fh)
} }
static BOOLEAN static BOOLEAN
LoadSisState (SIS_STATE *SSPtr, void *fp) LoadSisState (SIS_STATE *SSPtr, void *fp, SDWORD first)
{ {
SSPtr->log_x = first;
if ( if (
read_32s (fp, &SSPtr->log_x) != 1 ||
read_32s (fp, &SSPtr->log_y) != 1 || read_32s (fp, &SSPtr->log_y) != 1 ||
read_32 (fp, &SSPtr->ResUnits) != 1 || read_32 (fp, &SSPtr->ResUnits) != 1 ||
read_32 (fp, &SSPtr->FuelOnBoard) != 1 || read_32 (fp, &SSPtr->FuelOnBoard) != 1 ||
@@ -510,7 +510,26 @@ LoadSisState (SIS_STATE *SSPtr, void *fp)
static BOOLEAN static BOOLEAN
LoadSummary (SUMMARY_DESC *SummPtr, void *fp) 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; return FALSE;
if ( if (
@@ -524,11 +543,15 @@ LoadSummary (SUMMARY_DESC *SummPtr, void *fp)
read_8 (fp, &SummPtr->NumShips) != 1 || read_8 (fp, &SummPtr->NumShips) != 1 ||
read_8 (fp, &SummPtr->NumDevices) != 1 || read_8 (fp, &SummPtr->NumDevices) != 1 ||
read_a8 (fp, SummPtr->ShipList, MAX_BUILT_SHIPS) != 1 || read_a8 (fp, SummPtr->ShipList, MAX_BUILT_SHIPS) != 1 ||
read_a8 (fp, SummPtr->DeviceList, MAX_EXCLUSIVE_DEVICES) != 1 || read_a8 (fp, SummPtr->DeviceList, MAX_EXCLUSIVE_DEVICES) != 1
read_16 (fp, NULL) != 1 /* padding */
) )
return FALSE; 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 else
return TRUE; return TRUE;
} }
@@ -760,5 +783,3 @@ LoadGame (COUNT which_game, SUMMARY_DESC *SummPtr)
return TRUE; return TRUE;
} }
+9 -4
View File
@@ -443,6 +443,8 @@ SaveSisState (const SIS_STATE *SSPtr, void *fp)
static BOOLEAN static BOOLEAN
SaveSummary (const SUMMARY_DESC *SummPtr, void *fp) SaveSummary (const SUMMARY_DESC *SummPtr, void *fp)
{ {
if (write_32 (fp, SAVE_MAGIC) != 1)
return FALSE;
if (!SaveSisState (&SummPtr->SS, fp)) if (!SaveSisState (&SummPtr->SS, fp))
return FALSE; return FALSE;
@@ -458,6 +460,7 @@ SaveSummary (const SUMMARY_DESC *SummPtr, void *fp)
write_8 (fp, SummPtr->NumDevices) != 1 || write_8 (fp, SummPtr->NumDevices) != 1 ||
write_a8 (fp, SummPtr->ShipList, MAX_BUILT_SHIPS) != 1 || write_a8 (fp, SummPtr->ShipList, MAX_BUILT_SHIPS) != 1 ||
write_a8 (fp, SummPtr->DeviceList, MAX_EXCLUSIVE_DEVICES) != 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_16 (fp, 0) != 1 /* padding */
) )
@@ -478,8 +481,9 @@ SaveStarDesc (const STAR_DESC *SDPtr, DECODE_REF fh)
} }
static void static void
PrepareSummary (SUMMARY_DESC *SummPtr) PrepareSummary (SUMMARY_DESC *SummPtr, const char *name)
{ {
int i;
SummPtr->SS = GlobData.SIS_state; SummPtr->SS = GlobData.SIS_state;
SummPtr->Activity = LOBYTE (GLOBAL (CurrentActivity)); SummPtr->Activity = LOBYTE (GLOBAL (CurrentActivity));
@@ -535,6 +539,8 @@ PrepareSummary (SUMMARY_DESC *SummPtr)
SummPtr->day_index = GLOBAL (GameClock.day_index); SummPtr->day_index = GLOBAL (GameClock.day_index);
SummPtr->month_index = GLOBAL (GameClock.month_index); SummPtr->month_index = GLOBAL (GameClock.month_index);
SummPtr->year_index = GLOBAL (GameClock.year_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 static void
@@ -635,7 +641,7 @@ SaveFlagshipState (void)
// This function first writes to a memory file, and then writes the whole // This function first writes to a memory file, and then writes the whole
// lot to the actual save file at once. // lot to the actual save file at once.
BOOLEAN BOOLEAN
SaveGame (COUNT which_game, SUMMARY_DESC *SummPtr) SaveGame (COUNT which_game, SUMMARY_DESC *SummPtr, const char *name)
{ {
BOOLEAN success, made_room; BOOLEAN success, made_room;
void *out_fp, *h; void *out_fp, *h;
@@ -833,7 +839,7 @@ RetrySave:
flen + sizeof (*SummPtr)); flen + sizeof (*SummPtr));
if (flen && (out_fp = res_OpenResFile (saveDir, file, "wb"))) if (flen && (out_fp = res_OpenResFile (saveDir, file, "wb")))
{ {
PrepareSummary (SummPtr); PrepareSummary (SummPtr, name);
success = SaveSummary (SummPtr, out_fp); success = SaveSummary (SummPtr, out_fp);
// Then write the rest of the data. // Then write the rest of the data.
@@ -858,4 +864,3 @@ RetrySave:
return (success); return (success);
} }
+1 -1
View File
@@ -26,7 +26,7 @@ extern "C" {
#endif #endif
extern void SaveProblem (void); 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) #if defined(__cplusplus)
} }
+3
View File
@@ -160,6 +160,8 @@ typedef struct
// is only used for displaying savegame summaries. There is also // is only used for displaying savegame summaries. There is also
// room for only 16 devices on screen. // room for only 16 devices on screen.
#define MAX_EXCLUSIVE_DEVICES 16 #define MAX_EXCLUSIVE_DEVICES 16
#define SAVE_MAGIC 0x01534d55
#define SAVE_NAME_SIZE 24
typedef struct typedef struct
{ {
@@ -172,6 +174,7 @@ typedef struct
BYTE NumShips, NumDevices; BYTE NumShips, NumDevices;
BYTE ShipList[MAX_BUILT_SHIPS]; BYTE ShipList[MAX_BUILT_SHIPS];
BYTE DeviceList[MAX_EXCLUSIVE_DEVICES]; BYTE DeviceList[MAX_EXCLUSIVE_DEVICES];
UNICODE SaveName[SAVE_NAME_SIZE];
} SUMMARY_DESC; } SUMMARY_DESC;
#define OVERRIDE_LANDER_FLAGS (1 << 7) #define OVERRIDE_LANDER_FLAGS (1 << 7)