- update of load/save menu to latest GZDoom code.

Input in save menu working.
This commit is contained in:
Christoph Oelckers 2019-11-30 00:49:50 +01:00
parent 94b4044d95
commit e266044391
12 changed files with 1109 additions and 861 deletions

View file

@ -830,6 +830,7 @@ set (PCH_SOURCES
common/menu/imagescroller.cpp
common/menu/joystickmenu.cpp
common/menu/listmenu.cpp
common/menu/savegamemanager.cpp
common/menu/loadsavemenu.cpp
common/menu/menu.cpp
common/menu/menudef.cpp

View file

@ -901,3 +901,4 @@ void ScaleWithAspect(int& w, int& h, int Width, int Height)
h = static_cast<int>(y);
}

View file

@ -48,6 +48,7 @@ void DrawTexture(F2DDrawer *drawer, FTexture* img, double x, double y, int tags_
void DrawChar (F2DDrawer* drawer, FFont *font, int normalcolor, double x, double y, int character, int tag_first, ...);
void DrawText(F2DDrawer* drawer, FFont *font, int normalcolor, double x, double y, const char *string, int tag_first, ...);
void DrawText(F2DDrawer* drawer, FFont *font, int normalcolor, double x, double y, const char32_t *string, int tag_first, ...);
void DrawFrame(F2DDrawer* twod, PalEntry color, int left, int top, int width, int height, int thickness);
EXTERN_CVAR(Int, con_scaletext) // Scale notify text at high resolutions?
EXTERN_CVAR(Int, con_scale)

View file

@ -246,3 +246,25 @@ void DrawText(F2DDrawer* drawer, FFont *font, int normalcolor, double x, double
DrawTextCommon(drawer, font, normalcolor, x, y, string, parms);
}
//==========================================================================
//
// V_DrawFrame
//
// Draw a frame around the specified area using the view border
// frame graphics. The border is drawn outside the area, not in it.
//
//==========================================================================
void DrawFrame(F2DDrawer* twod, PalEntry color, int left, int top, int width, int height, int thickness)
{
// Sanity check for incomplete gameinfo
int offset = thickness == -1 ? screen->GetHeight() / 400 : thickness;
int right = left + width;
int bottom = top + height;
// Draw top and bottom sides.
twod->AddColorOnlyQuad(left, top - offset, width, offset, color);
twod->AddColorOnlyQuad(left - offset, top - offset, offset, height + 2 * offset, color);
twod->AddColorOnlyQuad(left, bottom, width, offset, color);
twod->AddColorOnlyQuad(right, top - offset, offset, height + 2 * offset, color);
}

View file

@ -684,7 +684,7 @@ int FFont::StringWidth(const uint8_t *string) const
}
else
{
w += NewSmallFont->CharWidth(chr) + GlobalKerning;
w += CharWidth(chr) + GlobalKerning;
}
}

File diff suppressed because it is too large Load diff

View file

@ -569,6 +569,7 @@ bool M_SetMenu(FName menu, int param, FName caller)
*/
}
Printf("Attempting to open menu of unknown type '%s'\n", menu.GetChars());
M_ClearMenus();
return false;
}
@ -974,7 +975,7 @@ CCMD(opensavemenu)
{
gi->MenuOpened();
M_StartControlPanel(false);
M_SetMenu(NAME_SaveMenu);
M_SetMenu(NAME_SaveGameMenu);
}
}
@ -982,5 +983,5 @@ CCMD(openloadmenu)
{
gi->MenuOpened();
M_StartControlPanel(false);
M_SetMenu(NAME_LoadMenu);
M_SetMenu(NAME_LoadGameMenu);
}

View file

@ -138,18 +138,6 @@ enum ENativeFontValues
};
extern FGameStartup GameStartupInfo;
struct FSaveGameNode
{
FString Title;
FString Filename;
bool bOldVersion;
bool bMissingWads;
bool bNoDelete;
FSaveGameNode() { bNoDelete = false; }
};
extern EMenuState menuactive;
@ -682,29 +670,31 @@ public:
class DTextEnterMenu : public DMenu
{
using Super = DMenu;
TArray<char> mEnterString;
FString* mOutString;
unsigned int mEnterSize;
unsigned int mEnterPos;
int mSizeMode; // 1: size is length in chars. 2: also check string width
bool mInputGridOkay;
FString mEnterString;
int mEnterSize;
int mEnterPos;
bool mInputGridOkay;
int InputGridX;
int InputGridY;
// [TP]
int CursorSize;
bool AllowColors;
FFont *displayFont;
void AppendChar(int ch);
public:
// [TP] Added allowcolors
DTextEnterMenu(DMenu *parent, FString &textbuffer, int sizemode, bool showgrid, bool allowcolors = false);
DTextEnterMenu(DMenu *parent, FFont *dpf, FString textbuffer, int maxlen, bool showgrid, bool allowcolors = false);
void Drawer ();
bool MenuEvent (int mkey, bool fromcontroller);
bool Responder(event_t *ev);
bool TranslateKeyboardEvents();
bool MouseEvent(int type, int x, int y);
const char* GetText() { return mEnterString.GetChars(); }
};
@ -757,4 +747,56 @@ template<class Menu> struct TMenuClassDescriptor : public MenuClassDescriptor
}
};
struct FSaveGameNode
{
FString SaveTitle;
FString Filename;
bool bOldVersion = false;
bool bMissingWads = false;
bool bNoDelete = false;
};
struct FSavegameManager
{
private:
TArray<FSaveGameNode*> SaveGames;
FSaveGameNode NewSaveNode;
int LastSaved = -1;
int LastAccessed = -1;
TArray<char> SavePicData;
FTexture *SavePic = nullptr;
public:
int WindowSize = 0;
FString SaveCommentString;
FSaveGameNode *quickSaveSlot = nullptr;
~FSavegameManager();
private:
int InsertSaveNode(FSaveGameNode *node);
public:
void NotifyNewSave(const FString &file, const FString &title, bool okForQuicksave, bool forceQuicksave);
void ClearSaveGames();
void ReadSaveStrings();
void UnloadSaveData();
int RemoveSaveSlot(int index);
void LoadSavegame(int Selected);
void DoSave(int Selected, const char *savegamestring);
unsigned ExtractSaveData(int index);
void ClearSaveStuff();
bool DrawSavePic(int x, int y, int w, int h);
void DrawSaveComment(FFont *font, int cr, int x, int y, int scalefactor);
void SetFileInfo(int Selected);
unsigned SavegameCount();
FSaveGameNode *GetSavegame(int i);
void InsertNewSaveNode();
bool RemoveNewSaveNode();
};
extern FSavegameManager savegameManager;
#endif

View file

@ -47,8 +47,6 @@
#include "optionmenuitems.h"
void ClearSaveGames();
// Menu-relevant content that gets filled in by scripts. This will get processed after the game has loaded.
FString gSkillNames[MAXSKILLS];
FString gVolumeNames[MAXVOLUMES];
@ -94,7 +92,6 @@ static void DeinitMenus()
OptionValues.Clear();
DMenu::CurrentMenu = NULL;
DefaultListMenuSettings.mItems.Clear();
ClearSaveGames();
}
static FTexture* GetMenuTexture(const char* const name)

View file

@ -63,16 +63,13 @@ CVAR(Bool, m_showinputgrid, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
//=============================================================================
// [TP] Added allowcolors
DTextEnterMenu::DTextEnterMenu(DMenu *parent, FString &textbuffer, int sizemode, bool showgrid, bool allowcolors)
DTextEnterMenu::DTextEnterMenu(DMenu *parent, FFont *dpf, FString textbuffer, int maxlen, bool showgrid, bool allowcolors)
: DMenu(parent)
{
mOutString = &textbuffer;
mEnterSize = 32; // this needs to calculate the size based on screen space (or scroll)
mEnterString.Resize(mEnterSize + 1);
mEnterPos = (unsigned)textbuffer.Len();
mSizeMode = sizemode;
mInputGridOkay = showgrid || m_showinputgrid;
if (mEnterPos > 0)
mEnterString = textbuffer;
mEnterPos = maxlen;
mInputGridOkay = (showgrid && (m_showinputgrid == 0)) || (m_showinputgrid >= 1);
if (mEnterString.Len() > 0)
{
InputGridX = INPUTGRID_WIDTH - 1;
InputGridY = INPUTGRID_HEIGHT - 1;
@ -84,6 +81,8 @@ DTextEnterMenu::DTextEnterMenu(DMenu *parent, FString &textbuffer, int sizemode,
InputGridY = 0;
}
AllowColors = allowcolors; // [TP]
displayFont = dpf;
CursorSize = displayFont->StringWidth(displayFont->GetCursor());
}
//=============================================================================
@ -111,12 +110,7 @@ bool DTextEnterMenu::Responder(event_t *ev)
if (ev->subtype == EV_GUI_Char)
{
mInputGridOkay = false;
if (mEnterPos < mEnterSize &&
(mSizeMode == 2/*entering player name*/ || (size_t)SmallFont->StringWidth(mEnterString) < (mEnterSize-1)*8))
{
mEnterString[mEnterPos] = (char)ev->data1;
mEnterString[++mEnterPos] = 0;
}
AppendChar(ev->data1);
return true;
}
char ch = (char)ev->data1;
@ -124,8 +118,7 @@ bool DTextEnterMenu::Responder(event_t *ev)
{
if (mEnterPos > 0)
{
mEnterPos--;
mEnterString[mEnterPos] = 0;
mEnterString.DeleteLastCharacter();
}
}
else if (ev->subtype == EV_GUI_KeyDown)
@ -133,17 +126,21 @@ bool DTextEnterMenu::Responder(event_t *ev)
if (ch == GK_ESCAPE)
{
DMenu *parent = mParentMenu;
Close();
parent->MenuEvent(MKEY_Abort, false);
Close();
return true;
}
else if (ch == '\r')
{
if (mEnterString[0])
if (mEnterString.Len() > 0)
{
// [TP] If we allow color codes, colorize the string now.
//if (AllowColors)
//mEnterString = mEnterString.Filter();
DMenu *parent = mParentMenu;
Close();
parent->MenuEvent(MKEY_Input, false);
Close();
return true;
}
}
@ -164,8 +161,8 @@ bool DTextEnterMenu::Responder(event_t *ev)
bool DTextEnterMenu::MouseEvent(int type, int x, int y)
{
const int cell_width = 18 * CleanXfac;
const int cell_height = 12 * CleanYfac;
const int cell_width = 18 * CleanXfac_1;
const int cell_height = 16 * CleanYfac_1;
const int screen_y = screen->GetHeight() - INPUTGRID_HEIGHT * cell_height;
const int screen_x = (screen->GetWidth() - INPUTGRID_WIDTH * cell_width) / 2;
@ -177,11 +174,11 @@ bool DTextEnterMenu::MouseEvent(int type, int x, int y)
{
if (MenuEvent(MKEY_Enter, true))
{
//S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE);
//M_MenuSound(CursorSound);
if (m_use_mouse == 2) InputGridX = InputGridY = -1;
return true;
}
}
return true;
}
else
{
@ -190,8 +187,21 @@ bool DTextEnterMenu::MouseEvent(int type, int x, int y)
return Super::MouseEvent(type, x, y);
}
//=============================================================================
//
//
//
//=============================================================================
void DTextEnterMenu::AppendChar(int ch)
{
FStringf newstring("%s%c%c", mEnterString.GetChars(), ch, displayFont->GetCursor());
if (mEnterSize < 0 || displayFont->StringWidth(newstring) < mEnterSize)
{
mEnterString.AppendCharacter(ch);
}
}
//=============================================================================
//
//
@ -237,9 +247,9 @@ bool DTextEnterMenu::MenuEvent (int key, bool fromcontroller)
return true;
case MKEY_Clear:
if (mEnterPos > 0)
{
mEnterString[--mEnterPos] = 0;
if (mEnterString.Len() > 0)
{
mEnterString.DeleteLastCharacter();
}
return true;
@ -250,26 +260,24 @@ bool DTextEnterMenu::MenuEvent (int key, bool fromcontroller)
ch = InputGridChars[InputGridX + InputGridY * INPUTGRID_WIDTH];
if (ch == 0) // end
{
if (mEnterString[0] != '\0')
if (mEnterString.Len() > 0)
{
DMenu *parent = mParentMenu;
Close();
parent->MenuEvent(MKEY_Input, false);
Close();
return true;
}
}
else if (ch == '\b') // bs
{
if (mEnterPos > 0)
if (mEnterString.Len() > 0)
{
mEnterString[--mEnterPos] = 0;
mEnterString.DeleteLastCharacter();
}
}
else if (mEnterPos < mEnterSize &&
(mSizeMode == 2/*entering player name*/ || (size_t)SmallFont->StringWidth(mEnterString) < (mEnterSize-1)*8))
else
{
mEnterString[mEnterPos] = ch;
mEnterString[++mEnterPos] = 0;
AppendChar(ch);
}
}
return true;
@ -294,7 +302,7 @@ void DTextEnterMenu::Drawer ()
{
const int cell_width = 18 * CleanXfac;
const int cell_height = 12 * CleanYfac;
const int top_padding = cell_height / 2 - SmallFont->GetHeight() * CleanYfac / 2;
const int top_padding = cell_height / 2 - displayFont->GetHeight() * CleanYfac / 2;
// Darken the background behind the character grid.
// Unless we frame it with a border, I think it looks better to extend the
@ -302,7 +310,7 @@ void DTextEnterMenu::Drawer ()
twod.AddColorOnlyQuad(0 /*screen->GetWidth()/2 - 13 * cell_width / 2*/,
screen->GetHeight() - INPUTGRID_HEIGHT * cell_height,
screen->GetWidth() /*13 * cell_width*/,
INPUTGRID_HEIGHT * cell_height, 0xff000000);
INPUTGRID_HEIGHT * cell_height, 0xc8000000);
if (InputGridX >= 0 && InputGridY >= 0)
{
@ -321,42 +329,42 @@ void DTextEnterMenu::Drawer ()
int width;
const int xx = x * cell_width - INPUTGRID_WIDTH * cell_width / 2 + screen->GetWidth() / 2;
const int ch = InputGridChars[y * INPUTGRID_WIDTH + x];
FTexture *pic = SmallFont->GetChar(ch, CR_DARKGRAY, &width);
FTexture *pic = displayFont->GetChar(ch, CR_DARKGRAY, &width);
EColorRange color;
int remap;
// The highlighted character is yellow; the rest are dark gray.
color = (x == InputGridX && y == InputGridY) ? CR_YELLOW : CR_DARKGRAY;
remap = SmallFont->GetColorTranslation(color);
remap = displayFont->GetColorTranslation(color);
if (pic != NULL)
{
// Draw a normal character.
DrawTexture(&twod, pic, xx + cell_width/2 - width*CleanXfac/2, yy + top_padding,
DrawTexture(&twod, pic, xx + cell_width/2 - width*CleanXfac_1/2, yy + top_padding,
DTA_TranslationIndex, remap,
DTA_CleanNoMove, true,
DTA_CleanNoMove_1, true,
TAG_DONE);
}
else if (ch == ' ')
{
// Draw the space as a box outline. We also draw it 50% wider than it really is.
const int x1 = xx + cell_width/2 - width * CleanXfac * 3 / 4;
const int x2 = x1 + width * 3 * CleanXfac / 2;
const int x1 = xx + cell_width/2 - width * CleanXfac_1 * 3 / 4;
const int x2 = x1 + width * 3 * CleanXfac_1 / 2;
const int y1 = yy + top_padding;
const int y2 = y1 + SmallFont->GetHeight() * CleanYfac;
const int y2 = y1 + displayFont->GetHeight() * CleanYfac_1;
auto palcolor = PalEntry(255, 160, 160, 160);
twod.AddColorOnlyQuad(x1, y1, x2, y1+CleanYfac, palcolor); // top
twod.AddColorOnlyQuad(x1, y2, x2, y2+CleanYfac, palcolor); // bottom
twod.AddColorOnlyQuad(x1, y1+CleanYfac, x1+CleanXfac, y2, palcolor); // left
twod.AddColorOnlyQuad(x2-CleanXfac, y1+CleanYfac, x2, y2, palcolor); // right
twod.AddColorOnlyQuad(x1, y1, x2 - x1, CleanYfac_1, palcolor); // top
twod.AddColorOnlyQuad(x1, y2, x2 - x1, CleanYfac_1, palcolor); // bottom
twod.AddColorOnlyQuad(x1, y1+CleanYfac_1, CleanXfac_1, y2 - y1, palcolor); // left
twod.AddColorOnlyQuad(x2-CleanXfac_1, y1+CleanYfac_1, CleanXfac_1, CleanYfac_1, palcolor); // right
}
else if (ch == '\b' || ch == 0)
{
// Draw the backspace and end "characters".
const char *const str = ch == '\b' ? "BS" : "ED";
DrawText(&twod, SmallFont, color,
xx + cell_width/2 - SmallFont->StringWidth(str)*CleanXfac/2,
yy + top_padding, str, DTA_CleanNoMove, true, TAG_DONE);
DrawText(&twod, NewSmallFont, color,
xx + cell_width/2 - displayFont->StringWidth(str)*CleanXfac_1/2,
yy + top_padding, str, DTA_CleanNoMove_1, true, TAG_DONE);
}
}
}

View file

@ -0,0 +1,507 @@
/*
** loadsavemenu.cpp
** The load game and save game menus
**
**---------------------------------------------------------------------------
** Copyright 2001-2010 Randy Heit
** Copyright 2010 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "menu/menu.h"
#include "version.h"
#include "m_png.h"
#include "filesystem.h"
#include "v_text.h"
#include "d_event.h"
#include "gstrings.h"
#include "d_gui.h"
#include "v_draw.h"
#include "files.h"
#include "resourcefile.h"
#include "sjson.h"
#include "cmdlib.h"
#include "files.h"
#include "savegamehelp.h"
#include "i_specialpaths.h"
#include "../../platform/win32/i_findfile.h" // This is a temporary direct path. Needs to be fixed when stuff gets cleaned up.
FSavegameManager savegameManager;
//=============================================================================
//
// Save data maintenance
//
//=============================================================================
void FSavegameManager::ClearSaveGames()
{
for (unsigned i = 0; i<SaveGames.Size(); i++)
{
if (!SaveGames[i]->bNoDelete)
delete SaveGames[i];
}
SaveGames.Clear();
}
FSavegameManager::~FSavegameManager()
{
ClearSaveGames();
}
//=============================================================================
//
// Save data maintenance
//
//=============================================================================
int FSavegameManager::RemoveSaveSlot(int index)
{
int listindex = SaveGames[0]->bNoDelete ? index - 1 : index;
if (listindex < 0) return index;
remove(SaveGames[index]->Filename.GetChars());
UnloadSaveData();
FSaveGameNode *file = SaveGames[index];
if (quickSaveSlot == SaveGames[index])
{
quickSaveSlot = nullptr;
}
if (!file->bNoDelete) delete file;
if (LastSaved == listindex) LastSaved = -1;
else if (LastSaved > listindex) LastSaved--;
if (LastAccessed == listindex) LastAccessed = -1;
else if (LastAccessed > listindex) LastAccessed--;
SaveGames.Delete(index);
if ((unsigned)index >= SaveGames.Size()) index--;
ExtractSaveData(index);
return index;
}
//=============================================================================
//
//
//
//=============================================================================
int FSavegameManager::InsertSaveNode(FSaveGameNode *node)
{
if (SaveGames.Size() == 0)
{
return SaveGames.Push(node);
}
if (node->bOldVersion)
{ // Add node at bottom of list
return SaveGames.Push(node);
}
else
{ // Add node at top of list
unsigned int i;
for (i = 0; i < SaveGames.Size(); i++)
{
if (SaveGames[i]->bOldVersion || node->SaveTitle.CompareNoCase(SaveGames[i]->SaveTitle) <= 0)
{
break;
}
}
SaveGames.Insert(i, node);
return i;
}
}
//=============================================================================
//
// M_ReadSaveStrings
//
// Find savegames and read their titles
//
//=============================================================================
void FSavegameManager::ReadSaveStrings()
{
if (SaveGames.Size() == 0)
{
void *filefirst;
findstate_t c_file;
FString filter;
LastSaved = LastAccessed = -1;
quickSaveSlot = nullptr;
filter = G_BuildSaveName("*");
filefirst = I_FindFirst(filter.GetChars(), &c_file);
if (filefirst != ((void *)(-1)))
{
do
{
// I_FindName only returns the file's name and not its full path
FString filepath = G_BuildSaveName(I_FindName(&c_file));
FResourceFile *savegame = FResourceFile::OpenResourceFile(filepath, true, true);
if (savegame != nullptr)
{
FResourceLump *info = savegame->FindLump("info.json");
if (info == nullptr)
{
// savegame info not found. This is not a savegame so leave it alone.
delete savegame;
continue;
}
auto fr = info->NewReader();
FString title;
int check = G_ValidateSavegame(fr, &title);
delete savegame;
if (check != 0)
{
FSaveGameNode *node = new FSaveGameNode;
node->Filename = filepath;
node->bOldVersion = true;
node->bMissingWads = false;
node->SaveTitle = title;
InsertSaveNode(node);
}
}
} while (I_FindNext (filefirst, &c_file) == 0);
I_FindClose (filefirst);
}
}
}
//=============================================================================
//
//
//
//=============================================================================
void FSavegameManager::NotifyNewSave(const FString &file, const FString &title, bool okForQuicksave, bool forceQuicksave)
{
FSaveGameNode *node;
if (file.IsEmpty())
return;
ReadSaveStrings();
// See if the file is already in our list
for (unsigned i = 0; i<SaveGames.Size(); i++)
{
FSaveGameNode *node = SaveGames[i];
#ifdef __unix__
if (node->Filename.Compare(file) == 0)
#else
if (node->Filename.CompareNoCase(file) == 0)
#endif
{
node->SaveTitle = title;
node->bOldVersion = false;
node->bMissingWads = false;
if (okForQuicksave)
{
if (quickSaveSlot == nullptr || quickSaveSlot == (FSaveGameNode*)1 || forceQuicksave) quickSaveSlot = node;
LastAccessed = LastSaved = i;
}
return;
}
}
node = new FSaveGameNode;
node->SaveTitle = title;
node->Filename = file;
node->bOldVersion = false;
node->bMissingWads = false;
int index = InsertSaveNode(node);
if (okForQuicksave)
{
if (quickSaveSlot == nullptr || quickSaveSlot == (FSaveGameNode*)1 || forceQuicksave) quickSaveSlot = node;
LastAccessed = LastSaved = index;
}
else
{
LastAccessed = ++LastSaved;
}
}
//=============================================================================
//
// Loads the savegame
//
//=============================================================================
void FSavegameManager::LoadSavegame(int Selected)
{
//G_LoadGame(SaveGames[Selected]->Filename.GetChars(), true);
if (quickSaveSlot == (FSaveGameNode*)1)
{
quickSaveSlot = SaveGames[Selected];
}
M_ClearMenus();
LastAccessed = Selected;
}
//=============================================================================
//
//
//
//=============================================================================
void FSavegameManager::DoSave(int Selected, const char *savegamestring)
{
if (Selected != 0)
{
auto node = SaveGames[Selected];
//G_SaveGame(node->Filename.GetChars(), savegamestring);
}
else
{
// Find an unused filename and save as that
FString filename;
int i;
for (i = 0;; ++i)
{
filename = G_BuildSaveName(FStringf("save%04d", i));
if (!FileExists(filename))
{
break;
}
}
//G_SaveGame(filename, savegamestring);
}
M_ClearMenus();
}
//=============================================================================
//
//
//
//=============================================================================
unsigned FSavegameManager::ExtractSaveData(int index)
{
FResourceFile *resf;
FSaveGameNode *node;
if (index == -1)
{
if (SaveGames.Size() > 0 && SaveGames[0]->bNoDelete)
{
index = LastSaved + 1;
}
else
{
index = LastAccessed < 0? 0 : LastAccessed;
}
}
UnloadSaveData();
if ((unsigned)index < SaveGames.Size() &&
(node = SaveGames[index]) &&
!node->Filename.IsEmpty() &&
!node->bOldVersion &&
(resf = FResourceFile::OpenResourceFile(node->Filename.GetChars(), true)) != nullptr)
{
FResourceLump *info = resf->FindLump("info.json");
if (info == nullptr)
{
// this should not happen because the file has already been verified.
return index;
}
auto fr = info->NewReader();
auto data = fr.ReadPadded(1);
sjson_context* ctx = sjson_create_context(0, 0, NULL);
if (ctx)
{
sjson_node* root = sjson_decode(ctx, (const char*)data.Data());
FString comment = sjson_get_string(root, "Creation Time", "");
FString pcomment = sjson_get_string(root, "Comment", "");
if (comment.Len() > 0) comment += "\n";
comment += pcomment;
SaveCommentString = comment;
// Extract pic (todo: let the renderer write a proper PNG file instead of a raw canvas dunp of the software renderer - and make it work for all games.)
FResourceLump *pic = resf->FindLump("savepic.png");
if (pic != nullptr)
{
FileReader picreader;
picreader.OpenMemoryArray([=](TArray<uint8_t> &array)
{
auto cache = pic->Lock();
array.Resize(pic->LumpSize);
memcpy(&array[0], cache, pic->LumpSize);
pic->Unlock();
return true;
});
PNGHandle *png = M_VerifyPNG(picreader);
if (png != nullptr)
{
SavePic = nullptr; // not yet implemented: PNGTexture_CreateFromFile(png, node->Filename);
delete png;
if (SavePic && SavePic->GetWidth() == 1 && SavePic->GetHeight() == 1)
{
delete SavePic;
SavePic = nullptr;
SavePicData.Clear();
}
}
}
}
delete resf;
}
return index;
}
//=============================================================================
//
//
//
//=============================================================================
void FSavegameManager::UnloadSaveData()
{
if (SavePic != nullptr)
{
delete SavePic;
}
SaveCommentString = "";
SavePic = nullptr;
SavePicData.Clear();
}
//=============================================================================
//
//
//
//=============================================================================
void FSavegameManager::ClearSaveStuff()
{
UnloadSaveData();
if (quickSaveSlot == (FSaveGameNode*)1)
{
quickSaveSlot = nullptr;
}
}
//=============================================================================
//
//
//
//=============================================================================
bool FSavegameManager::DrawSavePic(int x, int y, int w, int h)
{
if (SavePic == nullptr) return false;
DrawTexture(&twod, SavePic, x, y, DTA_DestWidth, w, DTA_DestHeight, h, DTA_Masked, false, TAG_DONE);
return true;
}
//=============================================================================
//
//
//
//=============================================================================
void FSavegameManager::SetFileInfo(int Selected)
{
if (!SaveGames[Selected]->Filename.IsEmpty())
{
SaveCommentString.Format("File on disk:\n%s", SaveGames[Selected]->Filename.GetChars());
}
}
//=============================================================================
//
//
//
//=============================================================================
unsigned FSavegameManager::SavegameCount()
{
return SaveGames.Size();
}
//=============================================================================
//
//
//
//=============================================================================
FSaveGameNode *FSavegameManager::GetSavegame(int i)
{
return SaveGames[i];
}
//=============================================================================
//
//
//
//=============================================================================
void FSavegameManager::InsertNewSaveNode()
{
NewSaveNode.SaveTitle = GStrings["NEWSAVE"];
NewSaveNode.bNoDelete = true;
SaveGames.Insert(0, &NewSaveNode);
}
//=============================================================================
//
//
//
//=============================================================================
bool FSavegameManager::RemoveNewSaveNode()
{
if (SaveGames[0] == &NewSaveNode)
{
SaveGames.Delete(0);
return true;
}
return false;
}

View file

@ -20,8 +20,8 @@ xx(MainMenu)
xx(IngameMenu)
xx(HelpMenu)
xx(CreditsMenu)
xx(SaveMenu)
xx(LoadMenu)
xx(SaveGameMenu)
xx(LoadGameMenu)
xx(SoundMenu)
xx(ConfirmPlayerReset)
xx(EpisodeMenu)