diff --git a/source/duke3d/src/menus.cpp b/source/duke3d/src/menus.cpp index 2857a31f5..9f253f629 100644 --- a/source/duke3d/src/menus.cpp +++ b/source/duke3d/src/menus.cpp @@ -2348,14 +2348,27 @@ static void Menu_PreDraw(MenuID_t cm, MenuEntry_t *entry, const vec2_t origin) { videoFadeToBlack(1); menusave_t & msv = g_menusaves[M_LOAD.currentEntry]; - if (msv.isOldVer) + if (msv.isOldVer && msv.brief.isExt) { + Bsprintf(tempbuf, "Resume game from sequence point:\n\"%s\"" +#ifndef EDUKE32_ANDROID_MENU + "\n(Y/N)" +#endif + , msv.brief.name); + mgametextcenter(origin.x, origin.y + (90<<16), tempbuf); + } + else if (msv.isOldVer) + { +#if 1 + mgametextcenter(origin.x, origin.y + (90<<16), "You're not supposed to be here."); +#else Bsprintf(tempbuf, "Start new game:\n%s / %s" #ifndef EDUKE32_ANDROID_MENU "\n(Y/N)" #endif , g_mapInfo[(ud.volume_number*MAXLEVELS) + ud.level_number].name, g_skillNames[ud.player_skill-1]); mgametextcenter(origin.x, origin.y + (90<<16), tempbuf); +#endif } else { @@ -2691,7 +2704,11 @@ static void Menu_LoadReadHeaders() Menu_ReadSaveGameHeaders(); for (int i = 0; i < g_nummenusaves; ++i) - MenuEntry_DisableOnCondition(&ME_LOAD[i], g_menusaves[i].isOldVer); + { + menusave_t const & msv = g_menusaves[i]; + // MenuEntry_LookDisabledOnCondition(&ME_LOAD[i], msv.isOldVer && msv.brief.isExt); + MenuEntry_DisableOnCondition(&ME_LOAD[i], msv.isOldVer && !msv.brief.isExt); + } } static void Menu_SaveReadHeaders() @@ -2699,7 +2716,10 @@ static void Menu_SaveReadHeaders() Menu_ReadSaveGameHeaders(); for (int i = 0; i < g_nummenusaves; ++i) - MenuEntry_LookDisabledOnCondition(&ME_SAVE[i], g_menusaves[i].isOldVer); + { + menusave_t const & msv = g_menusaves[i]; + MenuEntry_LookDisabledOnCondition(&ME_SAVE[i], msv.isOldVer && !msv.brief.isExt); + } } static void Menu_PreInput(MenuEntry_t *entry) @@ -3472,7 +3492,8 @@ static void Menu_Verify(int32_t input) case MENU_LOADVERIFY: if (input) { - savebrief_t & sv = g_menusaves[M_LOAD.currentEntry].brief; + menusave_t & msv = g_menusaves[M_LOAD.currentEntry]; + savebrief_t & sv = msv.brief; if (strcmp(sv.path, g_lastusersave.path) != 0) { @@ -3489,9 +3510,10 @@ static void Menu_Verify(int32_t input) KB_FlushKeyboardQueue(); KB_ClearKeysDown(); - Menu_Change(MENU_CLOSE); - - G_LoadPlayerMaybeMulti(sv); + if (G_LoadPlayerMaybeMulti(sv)) + Menu_Change(MENU_PREVIOUS); + else + Menu_Change(MENU_CLOSE); } break; diff --git a/source/duke3d/src/player.cpp b/source/duke3d/src/player.cpp index 638cd819c..5a88488d2 100644 --- a/source/duke3d/src/player.cpp +++ b/source/duke3d/src/player.cpp @@ -5640,7 +5640,7 @@ HORIZONLY:; #define SJSON_IMPLEMENT #include "sjson.h" -int portableBackupSave(const char *path) +int portableBackupSave(const char * path) { if (!FURY) return 0; @@ -5652,14 +5652,14 @@ int portableBackupSave(const char *path) return 1; } - sjson_context* ctx = sjson_create_context(0, 0, NULL); + sjson_context * ctx = sjson_create_context(0, 0, NULL); if (!ctx) { buildprint("Could not create sjson_context\n"); return 1; } - sjson_node* root = sjson_mkobject(ctx); + sjson_node * root = sjson_mkobject(ctx); // sjson_put_string(ctx, root, "map", currentboardfilename); sjson_put_int(ctx, root, "volume", ud.last_stateless_volume); @@ -5686,20 +5686,9 @@ int portableBackupSave(const char *path) for (int w = 0; w < MAX_WEAPONS; ++w) sjson_append_element(gotweapon, sjson_mkbool(ctx, !!(ps->gotweapon & (1<ammo_amount[w]; - sjson_put_ints(ctx, player, "ammo_amount", ammo_amount, MAX_WEAPONS); - - int max_ammo_amount[MAX_WEAPONS]; - for (int w = 0; w < MAX_WEAPONS; ++w) - max_ammo_amount[w] = ps->max_ammo_amount[w]; - sjson_put_ints(ctx, player, "max_ammo_amount", max_ammo_amount, MAX_WEAPONS); - - int inv_amount[GET_MAX]; - for (int i = 0; i < GET_MAX; ++i) - inv_amount[i] = ps->inv_amount[i]; - sjson_put_ints(ctx, player, "inv_amount", inv_amount, GET_MAX); + sjson_put_int16s(ctx, player, "ammo_amount", ps->ammo_amount, MAX_WEAPONS); + sjson_put_int16s(ctx, player, "max_ammo_amount", ps->max_ammo_amount, MAX_WEAPONS); + sjson_put_int16s(ctx, player, "inv_amount", ps->inv_amount, GET_MAX); sjson_put_int(ctx, player, "max_shield_amount", ps->max_shield_amount); @@ -5751,7 +5740,7 @@ int portableBackupSave(const char *path) return 1; } - char* encoded = sjson_stringify(ctx, root, " "); + char * encoded = sjson_stringify(ctx, root, " "); buildvfs_FILE fil = buildvfs_fopen_write(fn); if (!fil) @@ -5763,6 +5752,7 @@ int portableBackupSave(const char *path) buildvfs_fwrite(encoded, strlen(encoded), 1, fil); buildvfs_fclose(fil); + sjson_free_string(ctx, encoded); sjson_destroy_context(ctx); return 0; diff --git a/source/duke3d/src/player.h b/source/duke3d/src/player.h index 6daa519e1..a850f04ee 100644 --- a/source/duke3d/src/player.h +++ b/source/duke3d/src/player.h @@ -426,4 +426,6 @@ static inline int P_Get(int32_t spriteNum) { return P_GetP((uspriteptr_t)&sprite } #endif +extern int portableBackupSave(const char *); + #endif diff --git a/source/duke3d/src/savegame.cpp b/source/duke3d/src/savegame.cpp index 2ab200130..055576db9 100644 --- a/source/duke3d/src/savegame.cpp +++ b/source/duke3d/src/savegame.cpp @@ -160,11 +160,27 @@ static void ReadSaveGameHeaders_CACHE1D(CACHE1D_FIND_REC *f) menusave_t & msv = g_internalsaves[g_numinternalsaves]; + msv.brief.isExt = 0; + int32_t k = sv_loadheader(fil, 0, &h); if (k) { if (k < 0) msv.isUnreadable = 1; + else + { + if (FURY) + { + char extfn[BMAX_PATH]; + snprintf(extfn, ARRAY_SIZE(extfn), "%s.ext", fn); + buildvfs_kfd fil = kopen4loadfrommod(extfn, 0); + if (fil != buildvfs_kfd_invalid) + { + msv.brief.isExt = 1; + kclose(fil); + } + } + } msv.isOldVer = 1; } else @@ -330,9 +346,223 @@ static void sv_postudload(); // hack static int different_user_map; +#include "sjson.h" + // XXX: keyboard input 'blocked' after load fail? (at least ESC?) int32_t G_LoadPlayer(savebrief_t & sv) { + if (sv.isExt) + { + char extfn[BMAX_PATH]; + snprintf(extfn, ARRAY_SIZE(extfn), "%s.ext", sv.path); + buildvfs_kfd fil = kopen4loadfrommod(extfn, 0); + if (fil == buildvfs_kfd_invalid) + { + return -1; + } + + int32_t len = kfilelength(fil); + auto text = (char *)Xmalloc(len+1); + text[len] = '\0'; + + if (kread_and_test(fil, text, len)) + { + kclose(fil); + Xfree(text); + return -1; + } + + kclose(fil); + + + sjson_context * ctx = sjson_create_context(0, 0, NULL); + sjson_node * root = sjson_decode(ctx, text); + + Xfree(text); + + int volume = sjson_get_int(root, "volume", -1); + int level = sjson_get_int(root, "level", -1); + int skill = sjson_get_int(root, "skill", -1); + + if (volume == -1 || level == -1 || skill == -1) + { + sjson_destroy_context(ctx); + return -1; + } + + sjson_node * players = sjson_find_member(root, "players"); + + int numplayers = sjson_child_count(players); + + if (numplayers != ud.multimode) + { + P_DoQuote(QUOTE_SAVE_BAD_PLAYERS, g_player[myconnectindex].ps); + + sjson_destroy_context(ctx); + return 1; + } + + { + // CODEDUP from non-isExt branch, with simplifying assumptions + + VM_OnEvent(EVENT_PRELOADGAME, g_player[screenpeek].ps->i, screenpeek); + + ud.multimode = numplayers; + + Net_WaitForServer(); + + FX_StopAllSounds(); + S_ClearSoundLocks(); + + ud.m_volume_number = volume; + ud.m_level_number = level; + ud.m_player_skill = skill; + + boardfilename[0] = '\0'; + + int const mapIdx = volume*MAXLEVELS + level; + + if (boardfilename[0]) + Bstrcpy(currentboardfilename, boardfilename); + else if (g_mapInfo[mapIdx].filename) + Bstrcpy(currentboardfilename, g_mapInfo[mapIdx].filename); + + if (currentboardfilename[0]) + { + artSetupMapArt(currentboardfilename); + append_ext_UNSAFE(currentboardfilename, ".mhk"); + engineLoadMHK(currentboardfilename); + } + + currentboardfilename[0] = '\0'; + + // G_NewGame_EnterLevel(); + } + + { + // CODEDUP from G_NewGame + + auto & p0 = *g_player[0].ps; + + ready2send = 0; + + ud.from_bonus = 0; + ud.last_level = -1; + ud.level_number = level; + ud.player_skill = skill; + ud.secretlevel = 0; + ud.skill_voice = -1; + ud.volume_number = volume; + + g_lastAutoSaveArbitraryID = -1; + +#ifdef EDUKE32_TOUCH_DEVICES + p0.zoom = 360; +#else + p0.zoom = 768; +#endif + p0.gm = 0; + + Menu_Close(0); + +#if !defined LUNATIC + Gv_ResetVars(); + Gv_InitWeaponPointers(); + Gv_RefreshPointers(); +#endif + Gv_ResetSystemDefaults(); + + for (int i=0; i < (MAXVOLUMES*MAXLEVELS); i++) + G_FreeMapState(i); + + if (ud.m_coop != 1) + p0.last_weapon = -1; + + display_mirror = 0; + } + + int p = 0; + for (sjson_node * player = sjson_first_child(players); player != nullptr; player = player->next) + { + playerdata_t * playerData = &g_player[p]; + DukePlayer_t * ps = playerData->ps; + auto pSprite = &sprite[ps->i]; + + pSprite->extra = sjson_get_int(player, "extra", -1); + ps->max_player_health = sjson_get_int(player, "max_player_health", -1); + + sjson_node * gotweapon = sjson_find_member(player, "gotweapon"); + int w_end = min(MAX_WEAPONS, sjson_child_count(gotweapon)); + ps->gotweapon = 0; + for (int w = 0; w < w_end; ++w) + { + sjson_node * ele = sjson_find_element(gotweapon, w); + if (ele->tag == SJSON_BOOL && ele->bool_) + ps->gotweapon |= 1<ammo_amount, MAX_WEAPONS, player, "ammo_amount"); + /* bool flag_max_ammo_amount = */ sjson_get_int16s(ps->max_ammo_amount, MAX_WEAPONS, player, "max_ammo_amount"); + /* bool flag_inv_amount = */ sjson_get_int16s(ps->inv_amount, GET_MAX, player, "inv_amount"); + + ps->max_shield_amount = sjson_get_int(player, "max_shield_amount", -1); + + ps->curr_weapon = sjson_get_int(player, "curr_weapon", -1); + ps->subweapon = sjson_get_int(player, "subweapon", -1); + ps->inven_icon = sjson_get_int(player, "inven_icon", -1); + + sjson_node * vars = sjson_find_member(player, "vars"); + + for (int j=0; ji, p); + } + + ++p; + } + + { + sjson_node * vars = sjson_find_member(root, "vars"); + + for (int j=0; ji, screenpeek); + + return 0; + } + buildvfs_kfd const fil = kopen4loadfrommod(sv.path, 0); if (fil == buildvfs_kfd_invalid) @@ -540,6 +770,8 @@ int32_t G_SavePlayer(savebrief_t & sv, bool isAutoSave) goto saveproblem; } + sv.isExt = 0; + // temporary hack ud.user_map = G_HaveUserMap(); @@ -550,7 +782,6 @@ int32_t G_SavePlayer(savebrief_t & sv, bool isAutoSave) VM_OnEvent(EVENT_SAVEGAME, g_player[screenpeek].ps->i, screenpeek); - extern int portableBackupSave(const char *); portableBackupSave(sv.path); // SAVE! diff --git a/source/duke3d/src/savegame.h b/source/duke3d/src/savegame.h index 760f1b895..9bf42b04c 100644 --- a/source/duke3d/src/savegame.h +++ b/source/duke3d/src/savegame.h @@ -83,11 +83,13 @@ struct savebrief_t char name[MAXSAVEGAMENAMESTRUCT]; char path[BMAX_PATH]; + uint8_t isExt = 0; void reset() { name[0] = '\0'; path[0] = '\0'; + isExt = 0; } bool isValid() const { @@ -103,10 +105,10 @@ struct menusave_t uint8_t isAutoSave = 0; void clear() { - brief.reset(); - isOldVer = 0; - isUnreadable = 0; - isAutoSave = 0; + brief.reset(); + isOldVer = 0; + isUnreadable = 0; + isAutoSave = 0; } };