ZDoom-style saves

git-svn-id: https://svn.eduke32.com/eduke32@6569 1a8010ca-5511-0410-912e-c29ae57300e0
This commit is contained in:
hendricks266 2017-12-18 11:24:53 +00:00
parent 29a0fffd5b
commit d9b0479343
15 changed files with 443 additions and 247 deletions

View file

@ -775,18 +775,6 @@ void G_CheckCommandLine(int32_t argc, char const * const * argv)
if (*c)
G_AddCon(c);
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
ud.warp_on = 2 + (*c) - '0';
break;
case 'z':
c++;
g_scriptDebug = Batoi(c);

View file

@ -204,7 +204,7 @@ void G_OpenDemoWrite(void)
if (g_demo_filePtr == NULL)
return;
i=sv_saveandmakesnapshot(g_demo_filePtr, -1, demorec_diffs_cvar, demorec_diffcompress_cvar,
i=sv_saveandmakesnapshot(g_demo_filePtr, nullptr, -1, demorec_diffs_cvar, demorec_diffcompress_cvar,
demorec_synccompress_cvar|(demorec_seeds_cvar<<1));
if (i)
{

View file

@ -4842,7 +4842,7 @@ FAKE_F3:
g_doQuickSave = 0;
if (g_lastSaveSlot == -1)
if (!g_lastusersave.isValid())
goto FAKE_F2;
KB_FlushKeyboardQueue();
@ -4857,16 +4857,19 @@ FAKE_F3:
G_DrawRooms(myconnectindex,65536);
g_screenCapture = 0;
if (g_lastSaveSlot >= 0)
if (g_lastusersave.isValid())
{
savebrief_t & sv = g_lastusersave;
// dirty hack... char 127 in last position indicates an auto-filled name
if (ud.savegame[g_lastSaveSlot][MAXSAVEGAMENAME] == 127)
if (sv.name[MAXSAVEGAMENAME] == 127)
{
Bstrncpy(&ud.savegame[g_lastSaveSlot][0], g_mapInfo[ud.volume_number * MAXLEVELS + ud.level_number].name, MAXSAVEGAMENAME);
ud.savegame[g_lastSaveSlot][MAXSAVEGAMENAME] = 127;
strncpy(sv.name, g_mapInfo[ud.volume_number * MAXLEVELS + ud.level_number].name, MAXSAVEGAMENAME);
sv.name[MAXSAVEGAMENAME] = 127;
}
G_SavePlayerMaybeMulti(g_lastSaveSlot);
g_quickload = &sv;
G_SavePlayerMaybeMulti(sv);
}
}
@ -4898,14 +4901,14 @@ FAKE_F3:
g_doQuickSave = 0;
if (g_lastSaveSlot == -1)
if (g_quickload == nullptr || !g_quickload->isValid())
goto FAKE_F3;
else if (g_lastSaveSlot >= 0)
else if (g_quickload->isValid())
{
KB_FlushKeyboardQueue();
KB_ClearKeysDown();
S_PauseSounds(1);
G_LoadPlayerMaybeMulti(g_lastSaveSlot);
G_LoadPlayerMaybeMulti(*g_quickload);
}
}
@ -5922,8 +5925,6 @@ static void G_Startup(void)
// initprintf("Loading palette/lookups...\n");
G_LoadLookups();
ReadSaveGameHeaders();
screenpeek = myconnectindex;
Bfflush(NULL);
@ -6532,7 +6533,10 @@ int app_main(int argc, char const * const * argv)
Menu_Init();
}
if (ud.warp_on > 1 && (!g_netServer && ud.multimode < 2))
#if 0
// previously, passing -0 through -9 on the command line would load the save in that slot #
// this code should be reusable for a new parameter that takes a filename, if desired
if (/* havesavename */ && (!g_netServer && ud.multimode < 2))
{
clearview(0L);
//g_player[myconnectindex].ps->palette = palette;
@ -6542,9 +6546,10 @@ int app_main(int argc, char const * const * argv)
menutext_center(105,"Loading saved game...");
nextpage();
if (G_LoadPlayer(ud.warp_on-2))
ud.warp_on = 0;
if (G_LoadPlayer(/* savefile */))
/* havesavename = false; */
}
#endif
FX_StopAllSounds();
S_ClearSoundLocks();
@ -6721,21 +6726,20 @@ MAIN_LOOP_RESTART:
}
// handle CON_SAVE and CON_SAVENN
if (g_requestedSaveSlot != -1)
if (g_saveRequested)
{
g_lastSaveSlot = g_requestedSaveSlot;
OSD_Printf("Saving to slot %d\n", g_requestedSaveSlot);
KB_FlushKeyboardQueue();
g_screenCapture = 1;
G_DrawRooms(myconnectindex, 65536);
g_screenCapture = 0;
G_SavePlayerMaybeMulti(g_requestedSaveSlot);
G_SavePlayerMaybeMulti(g_lastautosave);
g_quickload = &g_lastautosave;
g_requestedSaveSlot = -1;
OSD_Printf("Saved: %s\n", g_lastautosave.path);
g_saveRequested = false;
}
G_DoCheats();

View file

@ -132,7 +132,6 @@ extern camera_t g_camera;
#define MAXRIDECULE 10
#define MAXRIDECULELENGTH 40
#define MAXSAVEGAMES 10
#define MAXSAVEGAMENAMESTRUCT 32
#define MAXSAVEGAMENAME (MAXSAVEGAMENAMESTRUCT-1)
#define MAXPWLOCKOUT 128
@ -239,7 +238,6 @@ typedef struct {
char god,warp_on,cashman,eog,showallmap;
char show_help,scrollmode,noclip;
char ridecule[MAXRIDECULE][MAXRIDECULELENGTH];
char savegame[MAXSAVEGAMES][MAXSAVEGAMENAMESTRUCT];
char pwlockout[MAXPWLOCKOUT],rtsname[MAXRTSNAME];
char display_bonus_screen;
char show_level_text;
@ -481,6 +479,10 @@ enum
(((g_modDir[0] != '/') ? Bsnprintf(buf, size, "%s/" basename, g_modDir, ##__VA_ARGS__) \
: Bsnprintf(buf, size, basename, ##__VA_ARGS__)) >= ((int32_t)size) - 1\
)
#define G_ModDirSnprintfLite(buf, size, basename) \
\
((g_modDir[0] != '/') ? Bsnprintf(buf, size, "%s/%s", g_modDir, basename) \
: Bsnprintf(buf, size, basename))
static inline void G_NewGame_EnterLevel(void)
{

View file

@ -1078,7 +1078,7 @@ static int32_t VM_ResetPlayer(int const playerNum, int32_t vmFlags, int32_t cons
//AddLog("resetplayer");
if (!g_netServer && ud.multimode < 2 && !(resetFlags & 2))
{
if (g_lastSaveSlot >= 0 && ud.recstat != 2 && !(resetFlags & 1))
if (g_quickload && g_quickload->isValid() && ud.recstat != 2 && !(resetFlags & 1))
{
Menu_Open(playerNum);
KB_ClearKeyDown(sc_Space);
@ -3463,20 +3463,29 @@ nullquote:
{
int32_t const requestedSlot = *insptr++;
if ((unsigned)requestedSlot >= MAXSAVEGAMES)
if ((unsigned)requestedSlot >= 10)
continue;
g_requestedSaveSlot = requestedSlot;
// check if we need to make a new file
if (strcmp(g_lastautosave.path, g_lastusersave.path) == 0 ||
requestedSlot != g_lastAutoSaveArbitraryID)
{
g_lastautosave.reset();
}
if (tw == CON_SAVE || ud.savegame[requestedSlot][0] == 0)
g_lastAutoSaveArbitraryID = requestedSlot;
if (tw == CON_SAVE || g_lastautosave.name[0] == 0)
{
time_t timeStruct = time(NULL);
struct tm *pTime = localtime(&timeStruct);
strftime(ud.savegame[requestedSlot], sizeof(ud.savegame[requestedSlot]),
strftime(g_lastautosave.name, sizeof(g_lastautosave.name),
"%d %b %Y %I:%M%p", pTime);
}
g_saveRequested = true;
continue;
}

View file

@ -59,7 +59,6 @@ int32_t __fastcall VM_GetUserdef(int32_t labelNum)
case USERDEFS_CLIPPING: labelNum = ud.noclip; break;
// case USERDEFS_USER_NAME: labelNum= ud.user_name[MAXPLAYERS][32]; break;
// case USERDEFS_RIDECULE: labelNum= ud.ridecule; break;
// case USERDEFS_SAVEGAME: labelNum= ud.savegame; break;
// case USERDEFS_PWLOCKOUT: labelNum= ud.pwlockout; break;
// case USERDEFS_RTSNAME: labelNum= ud.rtsname; break;
case USERDEFS_OVERHEAD_ON: labelNum = ud.overhead_on; break;
@ -198,7 +197,6 @@ void __fastcall VM_SetUserdef(int32_t const labelNum, int32_t const iSet)
case USERDEFS_CLIPPING: ud.noclip = iSet; break;
// case USERDEFS_USER_NAME: ud.user_name[MAXPLAYERS][32] = lValue; break;
// case USERDEFS_RIDECULE: ud.ridecule = lValue; break;
// case USERDEFS_SAVEGAME: ud.savegame = lValue; break;
// case USERDEFS_PWLOCKOUT: ud.pwlockout = lValue; break;
// case USERDEFS_RTSNAME: ud.rtsname = lValue; break;
case USERDEFS_OVERHEAD_ON: ud.overhead_on = iSet; break;

View file

@ -22,6 +22,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "duke3d.h"
#include "menus.h"
#include "savegame.h"
#define gamevars_c_
@ -1552,7 +1553,7 @@ static void Gv_AddSystemVars(void)
Gv_NewVar("NUMSECTORS",(intptr_t)&numsectors, GAMEVAR_SYSTEM | GAMEVAR_INT16PTR | GAMEVAR_READONLY);
Gv_NewVar("Numsprites",(intptr_t)&Numsprites, GAMEVAR_SYSTEM | GAMEVAR_INT32PTR | GAMEVAR_READONLY);
Gv_NewVar("lastsavepos",(intptr_t)&g_lastSaveSlot, GAMEVAR_SYSTEM | GAMEVAR_INT32PTR);
Gv_NewVar("lastsavepos",(intptr_t)&g_lastAutoSaveArbitraryID, GAMEVAR_SYSTEM | GAMEVAR_INT32PTR);
# ifdef USE_OPENGL
Gv_NewVar("rendmode",(intptr_t)&rendmode, GAMEVAR_READONLY | GAMEVAR_INT32PTR | GAMEVAR_SYSTEM);
@ -1709,7 +1710,7 @@ void Gv_RefreshPointers(void)
aGameVars[Gv_GetVarIndex("NUMSECTORS")].global = (intptr_t)&numsectors;
aGameVars[Gv_GetVarIndex("Numsprites")].global = (intptr_t)&Numsprites;
aGameVars[Gv_GetVarIndex("lastsavepos")].global = (intptr_t)&g_lastSaveSlot;
aGameVars[Gv_GetVarIndex("lastsavepos")].global = (intptr_t)&g_lastAutoSaveArbitraryID;
# ifdef USE_OPENGL
aGameVars[Gv_GetVarIndex("rendmode")].global = (intptr_t)&rendmode;
# endif

View file

@ -94,8 +94,6 @@ int32_t g_actorRespawnTime = 768;
int32_t g_bouncemineRadius = 2500;
int32_t g_deleteQueueSize = 64;
int32_t g_itemRespawnTime = 768;
int32_t g_lastSaveSlot = -1;
int32_t g_requestedSaveSlot = -1;
int32_t g_morterRadius = 2500;
int32_t g_numFreezeBounces = 3;

View file

@ -174,8 +174,6 @@ extern int32_t g_bouncemineRadius;
extern int32_t g_deleteQueueSize;
extern int32_t g_gametypeCnt;
extern int32_t g_itemRespawnTime;
extern int32_t g_lastSaveSlot;
extern int32_t g_requestedSaveSlot;
extern int32_t g_morterRadius;
extern int32_t g_numFreezeBounces;
extern int32_t g_pipebombRadius;

View file

@ -490,7 +490,6 @@ enum
// game.h
MAXRIDECULE = 10,
MAXRIDECULELENGTH = 40,
MAXSAVEGAMES = 10,
MAXSAVEGAMENAME = 22,
MAXPWLOCKOUT = 128,
MAXRTSNAME = 128,
@ -589,7 +588,6 @@ typedef struct {
char god,warp_on,cashman,eog,showallmap;
char show_help,scrollmode,noclip;
char ridecule[MAXRIDECULE][MAXRIDECULELENGTH];
char savegame[MAXSAVEGAMES][MAXSAVEGAMENAME];
char pwlockout[MAXPWLOCKOUT],rtsname[MAXRTSNAME];
char display_bonus_screen;
char show_level_text;

View file

@ -219,7 +219,7 @@ static MenuMenuFormat_t MMF_MouseJoySetupBtns = { { 76<<16,
static MenuMenuFormat_t MMF_FuncList = { { 100<<16, 51<<16, }, 152<<16 };
static MenuMenuFormat_t MMF_ColorCorrect = { { MENU_MARGIN_REGULAR<<16, 86<<16, }, 190<<16 };
static MenuMenuFormat_t MMF_BigSliders = { { MENU_MARGIN_WIDE<<16, 37<<16, }, 190<<16 };
static MenuMenuFormat_t MMF_LoadSave = { { 223<<16, 48<<16, }, 320<<16 };
static MenuMenuFormat_t MMF_LoadSave = { { 200<<16, 49<<16, }, 145<<16 };
static MenuMenuFormat_t MMF_NetSetup = { { 36<<16, 38<<16, }, 190<<16 };
static MenuMenuFormat_t MMF_FileSelectLeft = { { 40<<16, 45<<16, }, 162<<16 };
static MenuMenuFormat_t MMF_FileSelectRight = { { 164<<16, 45<<16, }, 162<<16 };
@ -242,7 +242,7 @@ static MenuEntryFormat_t MEF_VideoSetup_Apply = { 4<<16, 16<<16, 168<<16 };
static MenuEntryFormat_t MEF_FuncList = { 3<<16, 0, 100<<16 };
static MenuEntryFormat_t MEF_ColorCorrect = { 2<<16, 0, -(240<<16) };
static MenuEntryFormat_t MEF_BigSliders = { 2<<16, 0, 170<<16 };
static MenuEntryFormat_t MEF_LoadSave = { 7<<16, -1, 78<<16 };
static MenuEntryFormat_t MEF_LoadSave = { 2<<16, -1, 78<<16 };
static MenuEntryFormat_t MEF_NetSetup = { 4<<16, 0, 112<<16 };
static MenuEntryFormat_t MEF_NetSetup_Confirm = { 4<<16, 16<<16, 112<<16 };
@ -1110,14 +1110,18 @@ static MenuEntry_t *MEL_SCREENSETUP[] = {
// Save and load will be filled in before every viewing of the save/load screen.
static MenuLink_t MEO_LOAD = { MENU_LOADVERIFY, MA_None, };
static MenuEntry_t ME_LOAD_TEMPLATE = MAKE_MENUENTRY( NULL, &MF_MinifontSave, &MEF_LoadSave, &MEO_LOAD, Link );
static MenuEntry_t ME_LOAD[MAXSAVEGAMES];
static MenuEntry_t *MEL_LOAD[MAXSAVEGAMES];
static MenuEntry_t ME_LOAD_EMPTY = MAKE_MENUENTRY( NULL, &MF_MinifontSave, &MEF_LoadSave, nullptr, Dummy );
static MenuEntry_t *ME_LOAD;
static MenuEntry_t **MEL_LOAD;
static char const s_NewSaveGame[] = "(New Save Game)";
static MenuString_t MEO_SAVE_TEMPLATE = MAKE_MENUSTRING( NULL, &MF_MinifontSave, MAXSAVEGAMENAME, 0 );
static MenuString_t MEO_SAVE[MAXSAVEGAMES];
static MenuString_t MEO_SAVE_NEW = MAKE_MENUSTRING( NULL, &MF_MinifontSave, MAXSAVEGAMENAME, 0 );
static MenuString_t *MEO_SAVE;
static MenuEntry_t ME_SAVE_TEMPLATE = MAKE_MENUENTRY( NULL, &MF_MinifontSave, &MEF_LoadSave, &MEO_SAVE_TEMPLATE, String );
static MenuEntry_t ME_SAVE[MAXSAVEGAMES];
static MenuEntry_t *MEL_SAVE[MAXSAVEGAMES];
static MenuEntry_t ME_SAVE_NEW = MAKE_MENUENTRY( s_NewSaveGame, &MF_MinifontSave, &MEF_LoadSave, &MEO_SAVE_NEW, String );
static MenuEntry_t *ME_SAVE;
static MenuEntry_t **MEL_SAVE;
static int32_t soundrate, soundvoices;
static MenuOption_t MEO_SOUND = MAKE_MENUOPTION( &MF_Redfont, &MEOS_OffOn, &ud.config.SoundToggle );
@ -1221,7 +1225,7 @@ static MenuEntry_t *MEL_PLAYER[] = {
};
static MenuString_t MEO_MACROS_TEMPLATE = MAKE_MENUSTRING( NULL, &MF_Bluefont, MAXRIDECULELENGTH, 0 );
static MenuString_t MEO_MACROS[MAXSAVEGAMES];
static MenuString_t MEO_MACROS[10];
static MenuEntry_t ME_MACROS_TEMPLATE = MAKE_MENUENTRY( NULL, &MF_Bluefont, &MEF_Macros, &MEO_MACROS_TEMPLATE, String );
static MenuEntry_t ME_MACROS[MAXRIDECULE];
static MenuEntry_t *MEL_MACROS[MAXRIDECULE];
@ -1299,6 +1303,7 @@ static MenuEntry_t *MEL_NETJOIN[] = {
#define NoTitle NULL
#define MAKE_MENUMENU(Title, Format, Entries) { Title, Format, Entries, ARRAY_SIZE(Entries), 0, 0, 0 }
#define MAKE_MENUMENU_CUSTOMSIZE(Title, Format, Entries) { Title, Format, Entries, 0, 0, 0, 0 }
static MenuMenu_t M_MAIN = MAKE_MENUMENU( NoTitle, &MMF_Top_Main, MEL_MAIN );
static MenuMenu_t M_MAIN_INGAME = MAKE_MENUMENU( NoTitle, &MMF_Top_Main, MEL_MAIN_INGAME );
@ -1334,8 +1339,8 @@ static MenuMenu_t M_RENDERERSETUP_POLYMER = MAKE_MENUMENU("Polymer Setup", &MMF_
static MenuMenu_t M_COLCORR = MAKE_MENUMENU( "Color Correction", &MMF_ColorCorrect, MEL_COLCORR );
static MenuMenu_t M_SCREENSETUP = MAKE_MENUMENU( "HUD Setup", &MMF_BigOptions, MEL_SCREENSETUP );
static MenuMenu_t M_DISPLAYSETUP = MAKE_MENUMENU( "Display Setup", &MMF_BigOptions, MEL_DISPLAYSETUP );
static MenuMenu_t M_LOAD = MAKE_MENUMENU( s_LoadGame, &MMF_LoadSave, MEL_LOAD );
static MenuMenu_t M_SAVE = MAKE_MENUMENU( s_SaveGame, &MMF_LoadSave, MEL_SAVE );
static MenuMenu_t M_LOAD = MAKE_MENUMENU_CUSTOMSIZE( s_LoadGame, &MMF_LoadSave, MEL_LOAD );
static MenuMenu_t M_SAVE = MAKE_MENUMENU_CUSTOMSIZE( s_SaveGame, &MMF_LoadSave, MEL_SAVE );
static MenuMenu_t M_SOUND = MAKE_MENUMENU( "Sound Setup", &MMF_BigOptions, MEL_SOUND );
static MenuMenu_t M_ADVSOUND = MAKE_MENUMENU( "Advanced Sound", &MMF_BigOptions, MEL_ADVSOUND );
static MenuMenu_t M_NETWORK = MAKE_MENUMENU( "Network Game", &MMF_Top_Joystick_Network, MEL_NETWORK );
@ -1490,6 +1495,29 @@ static void MenuEntry_LookDisabledOnCondition(MenuEntry_t * const entry, const i
entry->flags &= ~MEF_LookDisabled;
}
static int32_t M_RunMenu_Menu(Menu_t *cm, MenuMenu_t *menu, MenuEntry_t *currentry, int32_t state, const vec2_t origin, bool actually_draw = true);
static void Menu_EntryFocus(/*MenuEntry_t *entry*/);
static MenuEntry_t *Menu_AdjustForCurrentEntryAssignment(MenuMenu_t *menu)
{
MenuEntry_t *currentry = menu->entrylist[menu->currentEntry];
Menu_EntryFocus(/*currentry*/);
if (currentry->ybottom - menu->scrollPos > klabs(menu->format->bottomcutoff))
menu->scrollPos = currentry->ybottom - klabs(menu->format->bottomcutoff);
else if (currentry->ytop - menu->scrollPos < menu->format->pos.y)
menu->scrollPos = currentry->ytop - menu->format->pos.y;
return currentry;
}
static MenuEntry_t *Menu_AdjustForCurrentEntryAssignmentBlind(MenuMenu_t *menu)
{
M_RunMenu_Menu(nullptr, menu, nullptr, 0, { 0, 0 }, false);
return Menu_AdjustForCurrentEntryAssignment(menu);
}
/*
This function prepares data after ART and CON have been processed.
It also initializes some data in loops rather than statically at compile time.
@ -1606,6 +1634,7 @@ void Menu_Init(void)
MEOSN_NetSkills[g_skillCnt] = MenuSkillNone;
MMF_Top_Skill.pos.y = (58 + (4-g_skillCnt)*6)<<16;
M_SKILL.currentEntry = 1;
Menu_AdjustForCurrentEntryAssignmentBlind(&M_SKILL);
// prepare multiplayer gametypes
k = -1;
@ -1620,20 +1649,6 @@ void Menu_Init(void)
if (NAM_WW2GI)
ME_NETOPTIONS_MONSTERS.name = "Enemies";
// prepare savegames
for (i = 0; i < MAXSAVEGAMES; ++i)
{
MEL_LOAD[i] = &ME_LOAD[i];
MEL_SAVE[i] = &ME_SAVE[i];
ME_LOAD[i] = ME_LOAD_TEMPLATE;
ME_SAVE[i] = ME_SAVE_TEMPLATE;
ME_SAVE[i].entry = &MEO_SAVE[i];
MEO_SAVE[i] = MEO_SAVE_TEMPLATE;
ME_LOAD[i].name = ud.savegame[i];
MEO_SAVE[i].variable = ud.savegame[i];
}
// prepare text chat macros
for (i = 0; i < MAXRIDECULE; ++i)
{
@ -1791,6 +1806,7 @@ void Menu_Init(void)
static void Menu_Run(Menu_t *cm, vec2_t origin);
static void Menu_BlackRectangle(int32_t x, int32_t y, int32_t width, int32_t height, int32_t orientation);
/*
At present, no true difference is planned between Menu_Pre() and Menu_PreDraw().
@ -2113,24 +2129,36 @@ static void Menu_PreDraw(MenuID_t cm, MenuEntry_t *entry, const vec2_t origin)
#ifndef EDUKE32_ANDROID_MENU
"\n(Y/N)"
#endif
, ud.savegame[g_lastSaveSlot]);
, g_quickload->name);
mgametextcenter(origin.x, origin.y + (90<<16), tempbuf);
break;
case MENU_LOAD:
{
#if 0
for (i = 0; i <= 108; i += 12)
rotatesprite_fs(origin.x + ((160+64+91-64)<<16), origin.y + ((i+56)<<16), 65536L,0,TEXTBOX,24,0,10);
#endif
Menu_BlackRectangle(origin.x + (198<<16), origin.y + (47<<16), 102<<16, 100<<16, 1|32);
rotatesprite_fs(origin.x + (22<<16), origin.y + (97<<16), 65536L,0,WINDOWBORDER2,24,0,10);
rotatesprite_fs(origin.x + (180<<16), origin.y + (97<<16), 65536L,1024,WINDOWBORDER2,24,0,10);
rotatesprite_fs(origin.x + (99<<16), origin.y + (50<<16), 65536L,512,WINDOWBORDER1,24,0,10);
rotatesprite_fs(origin.x + (103<<16), origin.y + (144<<16), 65536L,1024+512,WINDOWBORDER1,24,0,10);
if (ud.savegame[M_LOAD.currentEntry][0])
if (M_LOAD.currentEntry >= (int32_t)g_nummenusaves)
{
mmenutext(origin.x + (72<<16), origin.y + (100<<16), "Empty");
break;
}
menusave_t & msv = g_menusaves[M_LOAD.currentEntry];
if (msv.brief.isValid())
{
rotatesprite_fs(origin.x + (101<<16), origin.y + (97<<16), 65536>>1,512,TILE_LOADSHOT,-32,0,4+10+64);
if (g_oldverSavegame[M_LOAD.currentEntry])
if (msv.isOldVer)
{
mmenutext(origin.x + (53<<16), origin.y + (70<<16), "Previous");
mmenutext(origin.x + (58<<16), origin.y + (90<<16), "Version");
@ -2160,13 +2188,16 @@ static void Menu_PreDraw(MenuID_t cm, MenuEntry_t *entry, const vec2_t origin)
if (savehead.volnum == 0 && savehead.levnum == 7)
mgametextcenter(origin.x, origin.y + (180<<16), savehead.boardfn);
}
else
mmenutext(origin.x + (69<<16), origin.y + (70<<16), "Empty");
break;
}
case MENU_SAVE:
{
#if 0
for (i = 0; i <= 108; i += 12)
rotatesprite_fs(origin.x + ((160+64+91-64)<<16), origin.y + ((i+56)<<16), 65536L,0,TEXTBOX,24,0,10);
#endif
Menu_BlackRectangle(origin.x + (198<<16), origin.y + (47<<16), 102<<16, 100<<16, 1|32);
rotatesprite_fs(origin.x + (22<<16), origin.y + (97<<16), 65536L,0,WINDOWBORDER2,24,0,10);
rotatesprite_fs(origin.x + (180<<16), origin.y + (97<<16), 65536L,1024,WINDOWBORDER2,24,0,10);
@ -2174,31 +2205,36 @@ static void Menu_PreDraw(MenuID_t cm, MenuEntry_t *entry, const vec2_t origin)
rotatesprite_fs(origin.x + (103<<16), origin.y + (144<<16), 65536L,1024+512,WINDOWBORDER1,24,0,10);
j = 0;
for (i = 0; i < 10; ++i)
if (((MenuString_t*)M_SAVE.entrylist[i]->entry)->editfield)
for (size_t k = 0; k < g_nummenusaves+1; ++k)
if (((MenuString_t*)M_SAVE.entrylist[k]->entry)->editfield)
j |= 1;
if (j)
rotatesprite_fs(origin.x + (101<<16), origin.y + (97<<16), 65536L>>1,512,TILE_SAVESHOT,-32,0,4+10+64);
else if (ud.savegame[M_SAVE.currentEntry][0])
rotatesprite_fs(origin.x + (101<<16), origin.y + (97<<16), 65536L>>1,512,TILE_LOADSHOT,-32,0,4+10+64);
else
mmenutext(origin.x + (69<<16), origin.y + (70<<16), "Empty");
if (ud.savegame[M_SAVE.currentEntry][0] && g_oldverSavegame[M_SAVE.currentEntry])
else if (0 < M_SAVE.currentEntry && M_SAVE.currentEntry <= (int32_t)g_nummenusaves)
{
mmenutext(origin.x + (53<<16), origin.y + (70<<16), "Previous");
mmenutext(origin.x + (58<<16), origin.y + (90<<16), "Version");
if (g_menusaves[M_SAVE.currentEntry-1].brief.isValid())
{
rotatesprite_fs(origin.x + (101<<16), origin.y + (97<<16), 65536L>>1,512,TILE_LOADSHOT,-32,0,4+10+64);
if (g_menusaves[M_SAVE.currentEntry-1].isOldVer)
{
mmenutext(origin.x + (53<<16), origin.y + (70<<16), "Previous");
mmenutext(origin.x + (58<<16), origin.y + (90<<16), "Version");
#ifndef EDUKE32_SIMPLE_MENU
Bsprintf(tempbuf,"Saved: %d.%d.%d %d-bit", savehead.majorver, savehead.minorver,
savehead.bytever, 8*savehead.ptrsize);
mgametext(origin.x + (31<<16), origin.y + (104<<16), tempbuf);
Bsprintf(tempbuf,"Our: %d.%d.%d %d-bit", SV_MAJOR_VER, SV_MINOR_VER, BYTEVERSION,
(int32_t)(8*sizeof(intptr_t)));
mgametext(origin.x + ((31+16)<<16), origin.y + (114<<16), tempbuf);
Bsprintf(tempbuf,"Saved: %d.%d.%d %d-bit", savehead.majorver, savehead.minorver,
savehead.bytever, 8*savehead.ptrsize);
mgametext(origin.x + (31<<16), origin.y + (104<<16), tempbuf);
Bsprintf(tempbuf,"Our: %d.%d.%d %d-bit", SV_MAJOR_VER, SV_MINOR_VER, BYTEVERSION,
(int32_t)(8*sizeof(intptr_t)));
mgametext(origin.x + ((31+16)<<16), origin.y + (114<<16), tempbuf);
#endif
}
}
}
else
mmenutext(origin.x + (82<<16), origin.y + (100<<16), "New");
if (ud.multimode > 1)
{
@ -2211,6 +2247,7 @@ static void Menu_PreDraw(MenuID_t cm, MenuEntry_t *entry, const vec2_t origin)
if (ud.volume_number == 0 && ud.level_number == 7)
mgametextcenter(origin.x, origin.y + (180<<16), currentboardfilename);
break;
}
#ifdef EDUKE32_ANDROID_MENU
case MENU_SKILL:
@ -2223,8 +2260,10 @@ static void Menu_PreDraw(MenuID_t cm, MenuEntry_t *entry, const vec2_t origin)
#endif
case MENU_LOADVERIFY:
{
fade_screen_black(1);
if (g_oldverSavegame[M_LOAD.currentEntry])
menusave_t & msv = g_menusaves[M_LOAD.currentEntry];
if (msv.isOldVer)
{
Bsprintf(tempbuf, "Start new game:\n%s / %s"
#ifndef EDUKE32_ANDROID_MENU
@ -2239,10 +2278,11 @@ static void Menu_PreDraw(MenuID_t cm, MenuEntry_t *entry, const vec2_t origin)
#ifndef EDUKE32_ANDROID_MENU
"\n(Y/N)"
#endif
, ud.savegame[M_LOAD.currentEntry]);
, msv.brief.name);
mgametextcenter(origin.x, origin.y + (90<<16), tempbuf);
}
break;
}
case MENU_SAVEVERIFY:
fade_screen_black(1);
@ -2619,10 +2659,21 @@ static void Menu_EntryFocus(/*MenuEntry_t *entry*/)
switch (g_currentMenu)
{
case MENU_LOAD:
G_LoadSaveHeaderNew(M_LOAD.currentEntry, &savehead);
if (M_LOAD.currentEntry < (int32_t)g_nummenusaves)
{
savebrief_t & sv = g_menusaves[M_LOAD.currentEntry].brief;
if (sv.isValid())
G_LoadSaveHeaderNew(sv.path, &savehead);
}
break;
case MENU_SAVE:
G_LoadSaveHeaderNew(M_SAVE.currentEntry, &savehead);
if (0 < M_SAVE.currentEntry && M_SAVE.currentEntry <= (int32_t)g_nummenusaves)
{
savebrief_t & sv = g_menusaves[M_SAVE.currentEntry-1].brief;
if (sv.isValid())
G_LoadSaveHeaderNew(sv.path, &savehead);
}
break;
default:
@ -3131,10 +3182,19 @@ static void Menu_EntryStringActivate(/*MenuEntry_t *entry*/)
switch (g_currentMenu)
{
case MENU_SAVE:
if (!save_xxh)
save_xxh = XXH32((uint8_t *)&ud.savegame[M_SAVE.currentEntry][0], MAXSAVEGAMENAME, 0xDEADBEEF);
if (ud.savegame[M_SAVE.currentEntry][0])
Menu_Change(MENU_SAVEVERIFY);
if (M_SAVE.currentEntry > 0)
{
savebrief_t & sv = g_menusaves[M_SAVE.currentEntry-1].brief;
if (!save_xxh)
save_xxh = XXH32((uint8_t *)sv.name, MAXSAVEGAMENAME, 0xDEADBEEF);
if (sv.isValid())
Menu_Change(MENU_SAVEVERIFY);
}
else
{
ME_SAVE_NEW.name = nullptr;
save_xxh = 0;
}
break;
default:
@ -3142,41 +3202,44 @@ static void Menu_EntryStringActivate(/*MenuEntry_t *entry*/)
}
}
static int32_t Menu_EntryStringSubmit(MenuEntry_t *entry, char *input)
static int32_t Menu_EntryStringSubmit(/*MenuEntry_t *entry, */char *input)
{
MenuString_t *object = (MenuString_t*)entry->entry;
int32_t returnvar = 0;
switch (g_currentMenu)
{
case MENU_SAVE:
{
savebrief_t & sv = g_lastusersave = M_SAVE.currentEntry == 0 ? savebrief_t{input} : g_menusaves[M_SAVE.currentEntry-1].brief;
// dirty hack... char 127 in last position indicates an auto-filled name
#ifdef __ANDROID__
if (1)
#else
if (input[0] == 0 || (ud.savegame[M_SAVE.currentEntry][MAXSAVEGAMENAME] == 127 &&
Bstrncmp(&ud.savegame[M_SAVE.currentEntry][0], input, MAXSAVEGAMENAME) == 0 &&
save_xxh == XXH32((uint8_t *)&ud.savegame[M_SAVE.currentEntry][0], MAXSAVEGAMENAME, 0xDEADBEEF)))
if (input[0] == 0 || (sv.name[MAXSAVEGAMENAME] == 127 &&
strncmp(sv.name, input, MAXSAVEGAMENAME) == 0 &&
save_xxh == XXH32((uint8_t *)sv.name, MAXSAVEGAMENAME, 0xDEADBEEF)))
#endif
{
Bstrncpy(&ud.savegame[M_SAVE.currentEntry][0], g_mapInfo[ud.volume_number * MAXLEVELS + ud.level_number].name, MAXSAVEGAMENAME);
ud.savegame[M_SAVE.currentEntry][MAXSAVEGAMENAME] = 127;
strncpy(sv.name, g_mapInfo[ud.volume_number * MAXLEVELS + ud.level_number].name, MAXSAVEGAMENAME);
sv.name[MAXSAVEGAMENAME] = 127;
returnvar = -1;
}
else
{
ud.savegame[M_SAVE.currentEntry][MAXSAVEGAMENAME] = 0;
Bstrncpy(object->variable, input, object->bufsize);
strncpy(sv.name, input, MAXSAVEGAMENAME);
sv.name[MAXSAVEGAMENAME] = 0;
}
G_SavePlayerMaybeMulti(M_SAVE.currentEntry);
G_SavePlayerMaybeMulti(sv);
g_lastSaveSlot = M_SAVE.currentEntry;
g_quickload = &sv;
g_player[myconnectindex].ps->gm = MODE_GAME;
Menu_Change(MENU_CLOSE);
save_xxh = 0;
break;
}
default:
break;
@ -3191,7 +3254,7 @@ static void Menu_EntryStringCancel(/*MenuEntry_t *entry*/)
{
case MENU_SAVE:
save_xxh = 0;
ReadSaveGameHeaders();
ME_SAVE_NEW.name = s_NewSaveGame;
break;
default:
@ -3226,7 +3289,7 @@ static void Menu_Verify(int32_t input)
FX_StopAllSounds();
S_ClearSoundLocks();
G_LoadPlayerMaybeMulti(g_lastSaveSlot);
G_LoadPlayerMaybeMulti(*g_quickload);
}
else
{
@ -3243,14 +3306,26 @@ static void Menu_Verify(int32_t input)
case MENU_LOADVERIFY:
if (input)
{
g_lastSaveSlot = M_LOAD.currentEntry;
savebrief_t & sv = g_menusaves[M_LOAD.currentEntry].brief;
if (strcmp(sv.path, g_lastusersave.path) != 0)
{
g_freshload = sv;
g_lastusersave.reset();
g_lastautosave.reset();
g_quickload = &g_freshload;
}
else
{
g_quickload = &g_lastusersave;
}
KB_FlushKeyboardQueue();
KB_ClearKeysDown();
Menu_Change(MENU_CLOSE);
G_LoadPlayerMaybeMulti(g_lastSaveSlot);
G_LoadPlayerMaybeMulti(sv);
}
break;
@ -3258,7 +3333,6 @@ static void Menu_Verify(int32_t input)
if (!input)
{
save_xxh = 0;
ReadSaveGameHeaders();
((MenuString_t*)M_SAVE.entrylist[M_SAVE.currentEntry]->entry)->editfield = NULL;
}
@ -3629,6 +3703,7 @@ static void Menu_MaybeSetSelectionToChild(Menu_t * m, MenuID_t id)
if (link->linkID == id)
{
menu->currentEntry = i;
Menu_AdjustForCurrentEntryAssignmentBlind(menu);
break;
}
}
@ -3636,6 +3711,43 @@ static void Menu_MaybeSetSelectionToChild(Menu_t * m, MenuID_t id)
}
}
static void Menu_ReadSaveGameHeaders()
{
ReadSaveGameHeaders();
size_t const numloaditems = max(g_nummenusaves, 1), numsaveitems = g_nummenusaves+1;
ME_LOAD = (MenuEntry_t *)realloc(ME_LOAD, g_nummenusaves * sizeof(MenuEntry_t));
MEL_LOAD = (MenuEntry_t **)realloc(MEL_LOAD, numloaditems * sizeof(MenuEntry_t *));
MEO_SAVE = (MenuString_t *)realloc(MEO_SAVE, g_nummenusaves * sizeof(MenuString_t));
ME_SAVE = (MenuEntry_t *)realloc(ME_SAVE, g_nummenusaves * sizeof(MenuEntry_t));
MEL_SAVE = (MenuEntry_t **)realloc(MEL_SAVE, numsaveitems * sizeof(MenuEntry_t *));
MEL_SAVE[0] = &ME_SAVE_NEW;
ME_SAVE_NEW.name = s_NewSaveGame;
for (size_t i = 0; i < g_nummenusaves; ++i)
{
MEL_LOAD[i] = &ME_LOAD[i];
MEL_SAVE[i+1] = &ME_SAVE[i];
ME_LOAD[i] = ME_LOAD_TEMPLATE;
ME_SAVE[i] = ME_SAVE_TEMPLATE;
ME_SAVE[i].entry = &MEO_SAVE[i];
MEO_SAVE[i] = MEO_SAVE_TEMPLATE;
ME_LOAD[i].name = g_menusaves[i].brief.name;
MEO_SAVE[i].variable = g_menusaves[i].brief.name;
}
if (g_nummenusaves == 0)
MEL_LOAD[0] = &ME_LOAD_EMPTY;
M_LOAD.entrylist = MEL_LOAD;
M_LOAD.numEntries = numloaditems;
M_SAVE.entrylist = MEL_SAVE;
M_SAVE.numEntries = numsaveitems;
// lexicographical sorting?
}
static void Menu_AboutToStartDisplaying(Menu_t * m)
{
switch (m->menuID)
@ -3653,22 +3765,47 @@ static void Menu_AboutToStartDisplaying(Menu_t * m)
case MENU_LOAD:
if (KXDWN)
M_LOAD.title = (g_player[myconnectindex].ps->gm & MODE_GAME) ? s_LoadGame : s_Continue;
for (size_t i = 0; i < MAXSAVEGAMES; ++i)
{
MenuEntry_DisableOnCondition(&ME_LOAD[i], !ud.savegame[i][0] /*|| g_oldverSavegame[i]*/);
MenuEntry_LookDisabledOnCondition(&ME_LOAD[i], g_oldverSavegame[i]);
}
if (g_lastSaveSlot >= 0 && g_previousMenu != MENU_LOADVERIFY)
M_LOAD.currentEntry = g_lastSaveSlot;
Menu_ReadSaveGameHeaders();
for (size_t i = 0; i < g_nummenusaves; ++i)
MenuEntry_LookDisabledOnCondition(&ME_LOAD[i], g_menusaves[i].isOldVer);
if (g_quickload && g_quickload->isValid())
{
for (size_t i = 0; i < g_nummenusaves; ++i)
{
if (strcmp(g_menusaves[i].brief.path, g_quickload->path) == 0)
{
M_LOAD.currentEntry = i;
Menu_AdjustForCurrentEntryAssignmentBlind(&M_LOAD);
break;
}
}
}
break;
case MENU_SAVE:
if (g_lastSaveSlot >= 0 && g_previousMenu != MENU_SAVEVERIFY)
M_SAVE.currentEntry = g_lastSaveSlot;
if (g_previousMenu == MENU_SAVEVERIFY)
break;
for (size_t i = 0; i < MAXSAVEGAMES; ++i)
MenuEntry_LookDisabledOnCondition(&ME_SAVE[i], g_oldverSavegame[i]);
Menu_ReadSaveGameHeaders();
if (g_lastusersave.isValid())
{
for (size_t i = 0; i < g_nummenusaves; ++i)
{
if (strcmp(g_menusaves[i].brief.path, g_lastusersave.path) == 0)
{
M_SAVE.currentEntry = i+1;
Menu_AdjustForCurrentEntryAssignmentBlind(&M_SAVE);
break;
}
}
}
for (size_t i = 0; i < g_nummenusaves; ++i)
MenuEntry_LookDisabledOnCondition(&ME_SAVE[i], g_menusaves[i].isOldVer);
if (g_player[myconnectindex].ps->gm&MODE_GAME)
{
@ -4162,13 +4299,13 @@ static void Menu_RunInput_EntryRangeDouble_MovementArbitrary(/*MenuEntry_t *entr
static void Menu_RunInput_EntryRangeDouble_Movement(/*MenuEntry_t *entry, */MenuRangeDouble_t *object, MenuMovement_t direction);
#endif
static void Menu_RunInput_EntryString_Activate(MenuEntry_t *entry);
static void Menu_RunInput_EntryString_Submit(MenuEntry_t *entry, MenuString_t *object);
static void Menu_RunInput_EntryString_Submit(/*MenuEntry_t *entry, */MenuString_t *object);
static void Menu_RunInput_EntryString_Cancel(/*MenuEntry_t *entry, */MenuString_t *object);
static void Menu_RunInput_FileSelect_MovementVerify(MenuFileSelect_t *object);
static void Menu_RunInput_FileSelect_Movement(MenuFileSelect_t *object, MenuMovement_t direction);
static void Menu_RunInput_FileSelect_Select(MenuFileSelect_t *object);
static int32_t M_RunMenu_Menu(Menu_t *cm, MenuMenu_t *menu, MenuEntry_t *currentry, int32_t state, const vec2_t origin)
static int32_t M_RunMenu_Menu(Menu_t *cm, MenuMenu_t *menu, MenuEntry_t *currentry, int32_t state, const vec2_t origin, bool actually_draw)
{
int32_t totalHeight = 0;
@ -4245,7 +4382,9 @@ static int32_t M_RunMenu_Menu(Menu_t *cm, MenuMenu_t *menu, MenuEntry_t *current
if (entry->format->width == 0)
status |= MT_XCenter;
const int32_t dodraw = entry->type != Spacer && 0 <= y - menu->scrollPos + entry->font->get_yline() && y - menu->scrollPos <= klabs(menu->format->bottomcutoff) - menu->format->pos.y;
bool const dodraw = entry->type != Spacer && actually_draw &&
0 <= y - menu->scrollPos + entry->font->get_yline() &&
y - menu->scrollPos <= klabs(menu->format->bottomcutoff) - menu->format->pos.y;
int32_t const height = entry->getHeight(); // max(textsize.y, entry->font->get_yline()); // bluefont Q ruins this
status |= MT_YCenter;
@ -4763,7 +4902,7 @@ static int32_t M_RunMenu_Menu(Menu_t *cm, MenuMenu_t *menu, MenuEntry_t *current
{
if (entry == currentry && object->editfield != NULL)
{
Menu_RunInput_EntryString_Submit(entry, object);
Menu_RunInput_EntryString_Submit(/*entry, */object);
S_PlaySound(PISTOL_BODYHIT);
@ -4797,7 +4936,8 @@ static int32_t M_RunMenu_Menu(Menu_t *cm, MenuMenu_t *menu, MenuEntry_t *current
}
// draw indicators if applicable
Menu_RunScrollbar(cm, menu->format, y_upper + totalHeight, &menu->scrollPos, 320<<16, origin);
if (actually_draw)
Menu_RunScrollbar(cm, menu->format, y_upper + totalHeight, &menu->scrollPos, 320<<16, origin);
}
return totalHeight;
@ -4847,7 +4987,8 @@ static void Menu_RunOptionList(Menu_t *cm, MenuEntry_t *entry, MenuOption_t *obj
if (object->options->entryFormat->width == 0)
status |= MT_XCenter;
const int32_t dodraw = 0 <= y - object->options->scrollPos + object->options->font->get_yline() && y - object->options->scrollPos <= object->options->menuFormat->bottomcutoff - object->options->menuFormat->pos.y;
bool const dodraw = 0 <= y - object->options->scrollPos + object->options->font->get_yline() &&
y - object->options->scrollPos <= object->options->menuFormat->bottomcutoff - object->options->menuFormat->pos.y;
int32_t const height = object->options->font->get_yline(); // max(textsize.y, object->options->font->get_yline());
status |= MT_YCenter;
@ -5263,20 +5404,14 @@ or else this function will recurse infinitely.
*/
static MenuEntry_t *Menu_RunInput_Menu_MovementVerify(MenuMenu_t *menu)
{
MenuEntry_t *currentry = menu->entrylist[menu->currentEntry];
Menu_EntryFocus(/*currentry*/);
if (currentry->ybottom - menu->scrollPos > klabs(menu->format->bottomcutoff))
menu->scrollPos = currentry->ybottom - klabs(menu->format->bottomcutoff);
else if (currentry->ytop - menu->scrollPos < menu->format->pos.y)
menu->scrollPos = currentry->ytop - menu->format->pos.y;
return currentry;
return Menu_AdjustForCurrentEntryAssignment(menu);
}
static MenuEntry_t *Menu_RunInput_Menu_Movement(MenuMenu_t *menu, MenuMovement_t direction)
{
if (menu->numEntries == 1)
return menu->entrylist[menu->currentEntry];
switch (direction)
{
case MM_End:
@ -5631,7 +5766,10 @@ static void Menu_RunInput_EntryString_Activate(MenuEntry_t *entry)
{
MenuString_t *object = (MenuString_t*)entry->entry;
Bstrncpy(typebuf, object->variable, TYPEBUFSIZE);
if (object->variable)
strncpy(typebuf, object->variable, TYPEBUFSIZE);
else
typebuf[0] = '\0';
object->editfield = typebuf;
// this limitation is an arbitrary implementation detail
@ -5642,10 +5780,13 @@ static void Menu_RunInput_EntryString_Activate(MenuEntry_t *entry)
WithSDL2_StartTextInput();
}
static void Menu_RunInput_EntryString_Submit(MenuEntry_t *entry, MenuString_t *object)
static void Menu_RunInput_EntryString_Submit(/*MenuEntry_t *entry, */MenuString_t *object)
{
if (!Menu_EntryStringSubmit(entry, object->editfield))
Bstrncpy(object->variable, object->editfield, object->bufsize);
if (!Menu_EntryStringSubmit(/*entry, */object->editfield))
{
if (object->variable)
strncpy(object->variable, object->editfield, object->bufsize);
}
object->editfield = NULL;
WithSDL2_StopTextInput();
@ -6249,7 +6390,7 @@ static void Menu_RunInput(Menu_t *cm)
}
else if (hitstate == 1)
{
Menu_RunInput_EntryString_Submit(currentry, object);
Menu_RunInput_EntryString_Submit(/*currentry, */object);
S_PlaySound(PISTOL_BODYHIT);
}

View file

@ -447,7 +447,6 @@ extern MenuAnimation_t m_animation;
extern MenuID_t g_currentMenu;
extern Menu_t *m_currentMenu;
extern int32_t g_lastSaveSlot;
extern int32_t g_quitDeadline;
extern int32_t voting;
int Menu_Change(int32_t cm);

View file

@ -24,6 +24,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "anim.h"
#include "menus.h"
#include "demo.h"
#include "savegame.h"
#ifdef LUNATIC
# include "lunatic_game.h"
@ -1413,7 +1414,10 @@ end_vol4a:
ud.from_bonus = 0;
ud.last_level = -1;
g_lastSaveSlot = -1;
g_lastAutoSaveArbitraryID = -1;
g_lastautosave.reset();
g_lastusersave.reset();
g_quickload = nullptr;
#ifdef EDUKE32_TOUCH_DEVICES
pPlayer->zoom = 360;

View file

@ -30,9 +30,9 @@ static int32_t g_savedOK;
const char *g_failedVarname;
#endif
extern char *bitptr;
static OutputFileCounter savecounter;
uint8_t g_oldverSavegame[MAXSAVEGAMES];
extern char *bitptr;
#define BITPTR_POINTER 1
@ -138,64 +138,88 @@ void G_ResetInterpolations(void)
G_SetInterpolation(g_animatePtr[i]);
}
void ReadSaveGameHeaders(void)
{
char fn[16];
int32_t fil, i;
savebrief_t g_lastautosave, g_lastusersave, g_freshload;
int32_t g_lastAutoSaveArbitraryID = -1;
bool g_saveRequested;
savebrief_t * g_quickload;
menusave_t * g_menusaves;
size_t g_nummenusaves;
static void ReadSaveGameHeaders_CACHE1D(CACHE1D_FIND_REC *f)
{
savehead_t h;
EDUKE32_STATIC_ASSERT(sizeof(h.savename) == sizeof(ud.savegame[0]));
Bstrcpy(fn, "save0.esv");
for (i=0; i<MAXSAVEGAMES; i++)
for (; f != nullptr; f = f->next)
{
int32_t k;
fn[4] = i + '0';
fil = kopen4loadfrommod(fn, 0);
char const * fn = f->name;
int32_t fil = kopen4loadfrommod(fn, 0);
if (fil == -1)
{
Bmemset(ud.savegame[i], 0, sizeof(ud.savegame[i]));
continue;
}
k = sv_loadheader(fil, i, &h);
menusave_t & msv = g_menusaves[g_nummenusaves];
int32_t k = sv_loadheader(fil, 0, &h);
if (k)
{
// old version, signal to menu code
if (k > 0)
g_oldverSavegame[i] = 1;
msv.isOldVer = 1;
else
continue;
// else h.savename is all zeros (fatal failure, like wrong header
// magic or too short header)
}
else
msv.isOldVer = 0;
if (k >= 0)
Bmemcpy(ud.savegame[i], h.savename, sizeof(ud.savegame[i]));
if (k >= 0 && h.savename[0] != '\0')
{
memcpy(msv.brief.name, h.savename, ARRAY_SIZE(msv.brief.name));
strncpy(msv.brief.path, fn, ARRAY_SIZE(msv.brief.path));
++g_nummenusaves;
}
kclose(fil);
}
}
int32_t G_LoadSaveHeaderNew(int32_t spot, savehead_t *saveh)
static size_t countcache1dfind(CACHE1D_FIND_REC *f)
{
char fn[16];
int32_t fil, screenshotofs, i;
size_t x = 0;
for (; f != nullptr; f = f->next)
++x;
return x;
}
Bassert(spot < MAXSAVEGAMES);
void ReadSaveGameHeaders(void)
{
static char const DefaultPath[] = "/", SavePattern[] = "*.esv";
Bstrcpy(fn, "save0.esv");
fn[4] = spot + '0';
CACHE1D_FIND_REC *findfiles_default = klistpath(DefaultPath, SavePattern, CACHE1D_FIND_FILE);
fil = kopen4loadfrommod(fn, 0);
// potentially overallocating but programmatically simple
size_t const numfiles = countcache1dfind(findfiles_default);
g_menusaves = (menusave_t *)Xrealloc(g_menusaves, sizeof(menusave_t) * numfiles);
g_nummenusaves = 0;
ReadSaveGameHeaders_CACHE1D(findfiles_default);
klistfree(findfiles_default);
}
int32_t G_LoadSaveHeaderNew(char const *fn, savehead_t *saveh)
{
int32_t fil = kopen4loadfrommod(fn, 0);
if (fil == -1)
return -1;
i = sv_loadheader(fil, spot, saveh);
int32_t i = sv_loadheader(fil, 0, saveh);
if (i < 0)
goto corrupt;
int32_t screenshotofs;
if (kread(fil, &screenshotofs, 4) != 4)
goto corrupt;
@ -208,7 +232,7 @@ int32_t G_LoadSaveHeaderNew(int32_t spot, savehead_t *saveh)
{
if (kdfread((char *)waloff[TILE_LOADSHOT], 320, 200, fil) != 200)
{
OSD_Printf("G_LoadSaveHeaderNew(%d): failed reading screenshot\n", spot);
OSD_Printf("G_LoadSaveHeaderNew(): failed reading screenshot \"%s\"\n", fn);
goto corrupt;
}
}
@ -230,25 +254,17 @@ corrupt:
static void sv_postudload();
// XXX: keyboard input 'blocked' after load fail? (at least ESC?)
int32_t G_LoadPlayer(int32_t spot)
int32_t G_LoadPlayer(savebrief_t & sv)
{
char fn[16];
int32_t fil, i;
savehead_t h;
Bassert(spot < MAXSAVEGAMES);
Bstrcpy(fn, "save0.esv");
fn[4] = spot + '0';
fil = kopen4loadfrommod(fn, 0);
int32_t fil = kopen4loadfrommod(sv.path, 0);
if (fil == -1)
return -1;
ready2send = 0;
i = sv_loadheader(fil, spot, &h);
int32_t i = sv_loadheader(fil, 0, &h);
if ((i < 0) || h.numplayers != ud.multimode)
{
if (i == -4 || i == -3 || i == 1)
@ -283,12 +299,6 @@ int32_t G_LoadPlayer(int32_t spot)
FX_StopAllSounds();
S_ClearSoundLocks();
if (spot >= 0 && numplayers==1)
{
Bmemcpy(ud.savegame[spot], h.savename, sizeof(ud.savegame[0]));
ud.savegame[spot][sizeof(ud.savegame[0])-1] = 0;
}
// non-"m_" fields will be loaded from svgm_udnetw
ud.m_volume_number = h.volnum;
ud.m_level_number = h.levnum;
@ -321,12 +331,12 @@ int32_t G_LoadPlayer(int32_t spot)
else
{
// read the rest...
i = sv_loadsnapshot(fil, spot, &h);
i = sv_loadsnapshot(fil, 0, &h);
if (i)
{
// in theory, we could load into an initial dump first and trivially
// recover if things go wrong...
Bsprintf(tempbuf, "Loading save game file \"%s\" failed (code %d), cannot recover.", fn, i);
Bsprintf(tempbuf, "Loading save game file \"%s\" failed (code %d), cannot recover.", sv.path, i);
G_GameExit(tempbuf);
}
}
@ -395,44 +405,52 @@ static void G_SavePalette(void)
}
#endif
int32_t G_SavePlayer(int32_t spot)
int32_t G_SavePlayer(savebrief_t & sv)
{
char fn[16];
FILE *fil;
#ifdef __ANDROID__
G_SavePalette();
#endif
Bassert(spot < MAXSAVEGAMES);
G_SaveTimers();
Bstrcpy(fn, "save0.esv");
fn[4] = spot + '0';
// Bstrcpy(mpfn, "edukA_00.esv");
Net_WaitForServer();
ready2send = 0;
char temp[BMAX_PATH];
errno = 0;
FILE *fil;
if (sv.isValid())
{
char temp[BMAX_PATH];
if (G_ModDirSnprintf(temp, sizeof(temp), "%s", fn))
if (G_ModDirSnprintf(temp, sizeof(temp), "%s", sv.path))
{
OSD_Printf("G_SavePlayer: file name \"%s\" too long\n", fn);
return -1;
OSD_Printf("G_SavePlayer: file name \"%s\" too long\n", sv.path);
goto saveproblem;
}
errno = 0;
fil = fopen(temp, "wb");
if (!fil)
}
else
{
static char const SaveName[] = "save0000.esv";
size_t len = G_ModDirSnprintfLite(temp, sizeof(temp), SaveName);
if (len >= sizeof(temp)-1)
{
OSD_Printf("G_SavePlayer: failed opening \"%s\" for writing: %s\n",
temp, strerror(errno));
return -1;
OSD_Printf("G_SavePlayer: could not form automatic save path\n");
goto saveproblem;
}
char * zeros = temp + (len-8);
fil = savecounter.opennextfile(temp, zeros);
savecounter.count++;
// don't copy the mod dir into sv.path
strcpy(sv.path, temp + (len-(ARRAY_SIZE(SaveName)-1)));
}
if (!fil)
{
OSD_Printf("G_SavePlayer: failed opening \"%s\" for writing: %s\n",
temp, strerror(errno));
goto saveproblem;
}
#ifdef POLYMER
@ -443,7 +461,7 @@ int32_t G_SavePlayer(int32_t spot)
VM_OnEvent(EVENT_SAVEGAME, g_player[screenpeek].ps->i, screenpeek);
// SAVE!
sv_saveandmakesnapshot(fil, spot, 0, 0, 0);
sv_saveandmakesnapshot(fil, sv.name, 0, 0, 0, 0);
fclose(fil);
@ -467,41 +485,46 @@ int32_t G_SavePlayer(int32_t spot)
VM_OnEvent(EVENT_POSTSAVEGAME, g_player[screenpeek].ps->i, screenpeek);
return 0;
saveproblem:
ready2send = 1;
Net_WaitForServer();
G_RestoreTimers();
ototalclock = totalclock;
return -1;
}
void G_LoadPlayerMaybeMulti(int32_t slot)
void G_LoadPlayerMaybeMulti(savebrief_t & sv)
{
if (g_netServer || ud.multimode > 1)
{
Bstrcpy(apStrings[QUOTE_RESERVED4], "Multiplayer Loading Not Yet Supported");
P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps);
// G_LoadPlayer(-1-g_lastSaveSlot);
// g_player[myconnectindex].ps->gm = MODE_GAME;
}
else
{
int32_t c = G_LoadPlayer(slot);
int32_t c = G_LoadPlayer(sv);
if (c == 0)
g_player[myconnectindex].ps->gm = MODE_GAME;
}
}
void G_SavePlayerMaybeMulti(int32_t slot)
void G_SavePlayerMaybeMulti(savebrief_t & sv)
{
Bassert(slot >= 0);
CONFIG_WriteSetup(2);
if (g_netServer || ud.multimode > 1)
{
Bstrcpy(apStrings[QUOTE_RESERVED4], "Multiplayer Saving Not Yet Supported");
P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps);
// G_SavePlayer(-1-slot);
}
else
{
G_SavePlayer(slot);
G_SavePlayer(sv);
}
}
@ -1283,7 +1306,7 @@ static void SV_AllocSnap(int32_t allocinit)
}
// make snapshot only if spot < 0 (demo)
int32_t sv_saveandmakesnapshot(FILE *fil, int8_t spot, int8_t recdiffsp, int8_t diffcompress, int8_t synccompress)
int32_t sv_saveandmakesnapshot(FILE *fil, char const *name, int8_t spot, int8_t recdiffsp, int8_t diffcompress, int8_t synccompress)
{
savehead_t h;
@ -1338,10 +1361,10 @@ int32_t sv_saveandmakesnapshot(FILE *fil, int8_t spot, int8_t recdiffsp, int8_t
}
}
if ((unsigned)spot < MAXSAVEGAMES)
if (spot >= 0)
{
// savegame
Bstrncpyz(h.savename, ud.savegame[spot], sizeof(h.savename));
Bstrncpyz(h.savename, name, sizeof(h.savename));
#ifdef __ANDROID__
Bstrncpyz(h.volname, g_volumeNames[ud.volume_number], sizeof(h.volname));
Bstrncpyz(h.skillname, g_skillNames[ud.player_skill], sizeof(h.skillname));
@ -1414,8 +1437,6 @@ int32_t sv_saveandmakesnapshot(FILE *fil, int8_t spot, int8_t recdiffsp, int8_t
}
}
g_oldverSavegame[spot] = 0;
return 0;
}

View file

@ -61,19 +61,55 @@ typedef struct
} savehead_t;
#pragma pack(pop)
struct savebrief_t
{
savebrief_t() = default;
savebrief_t(char const *n)
{
strncpy(name, n, MAXSAVEGAMENAME);
}
char name[MAXSAVEGAMENAMESTRUCT];
char path[BMAX_PATH];
void reset()
{
name[0] = '\0';
path[0] = '\0';
}
bool isValid() const
{
return path[0] != '\0';
}
};
struct menusave_t
{
savebrief_t brief;
uint8_t isOldVer = 0;
};
extern savebrief_t g_lastautosave, g_lastusersave, g_freshload;
extern int32_t g_lastAutoSaveArbitraryID;
extern bool g_saveRequested;
extern savebrief_t * g_quickload;
extern menusave_t * g_menusaves;
extern size_t g_nummenusaves;
int32_t sv_updatestate(int32_t frominit);
int32_t sv_readdiff(int32_t fil);
uint32_t sv_writediff(FILE *fil);
int32_t sv_loadheader(int32_t fil, int32_t spot, savehead_t *h);
int32_t sv_loadsnapshot(int32_t fil, int32_t spot, savehead_t *h);
int32_t sv_saveandmakesnapshot(FILE *fil, int8_t spot, int8_t recdiffsp, int8_t diffcompress, int8_t synccompress);
int32_t sv_saveandmakesnapshot(FILE *fil, char const *name, int8_t spot, int8_t recdiffsp, int8_t diffcompress, int8_t synccompress);
void sv_freemem();
int32_t G_SavePlayer(int32_t spot);
int32_t G_LoadPlayer(int32_t spot);
int32_t G_LoadSaveHeaderNew(int32_t spot, savehead_t *saveh);
int32_t G_SavePlayer(savebrief_t & sv);
int32_t G_LoadPlayer(savebrief_t & sv);
int32_t G_LoadSaveHeaderNew(char const *fn, savehead_t *saveh);
void ReadSaveGameHeaders(void);
void G_SavePlayerMaybeMulti(int32_t slot);
void G_LoadPlayerMaybeMulti(int32_t slot);
void G_SavePlayerMaybeMulti(savebrief_t & sv);
void G_LoadPlayerMaybeMulti(savebrief_t & sv);
#ifdef YAX_ENABLE
extern void sv_postyaxload(void);
@ -82,7 +118,6 @@ extern void sv_postyaxload(void);
// XXX: The 'bitptr' decl really belongs into gamedef.h, but we don't want to
// pull all of it in savegame.c?
extern char *bitptr;
extern uint8_t g_oldverSavegame[MAXSAVEGAMES];
enum
{