1
0
Fork 0
mirror of https://github.com/TerryCavanagh/VVVVVV.git synced 2024-12-23 01:59:43 +01:00

Move all settings to settings.vvv

The game previously did this dumb thing where it lumped in all its
settings with its file that tracked your records and unlocks,
`unlock.vvv`. It wasn't really an issue, until 2.3 came along and added
a few settings, suddenly making a problem where 2.3 settings would be
reset by chance if you decided to touch 2.2.

The solution to this is to move all settings to a new file,
`settings.vvv`. However, for compatibility with 2.2, settings will still
be written to `unlock.vvv`.

The game will prioritize reading from `settings.vvv` instead of
`unlock.vvv`, so if there's a setting that's missing from `unlock.vvv`,
no worries there. But if `settings.vvv` is missing, then it'll read
settings from `unlock.vvv`. As well, if `unlock.vvv` is missing, then
`settings.vvv` will be read from instead (I explicitly tested for this,
and found that I had to write special code to handle this case,
otherwise the game would overwrite the existing `settings.vvv` before
reading from it; kids, make sure to always test your code!).

Closes #373 fully.
This commit is contained in:
Misa 2020-11-03 23:11:21 -08:00 committed by Ethan Lee
parent 9e1ba97f63
commit 5aa8b99113
6 changed files with 144 additions and 27 deletions

View file

@ -4436,6 +4436,14 @@ void Game::deletestats()
} }
} }
void Game::deletesettings()
{
if (!FILESYSTEM_delete("saves/settings.vvv"))
{
puts("Error deleting saves/settings.vvv");
}
}
void Game::unlocknum( int t ) void Game::unlocknum( int t )
{ {
#if !defined(MAKEANDPLAY) #if !defined(MAKEANDPLAY)
@ -4471,7 +4479,10 @@ void Game::loadstats(int *width, int *height, bool *vsync)
tinyxml2::XMLDocument doc; tinyxml2::XMLDocument doc;
if (!FILESYSTEM_loadTiXml2Document("saves/unlock.vvv", doc)) if (!FILESYSTEM_loadTiXml2Document("saves/unlock.vvv", doc))
{ {
savestats(); // Save unlock.vvv only. Maybe we have a settings.vvv laying around too,
// and we don't want to overwrite that!
savestats(true);
printf("No Stats found. Assuming a new player\n"); printf("No Stats found. Assuming a new player\n");
} }
@ -4493,7 +4504,9 @@ void Game::loadstats(int *width, int *height, bool *vsync)
hRoot=tinyxml2::XMLHandle(pElem); hRoot=tinyxml2::XMLHandle(pElem);
} }
for( pElem = hRoot.FirstChildElement( "Data" ).FirstChild().ToElement(); pElem; pElem=pElem->NextSiblingElement()) tinyxml2::XMLElement* dataNode = hRoot.FirstChildElement("Data").FirstChild().ToElement();
for( pElem = dataNode; pElem; pElem=pElem->NextSiblingElement())
{ {
std::string pKey(pElem->Value()); std::string pKey(pElem->Value());
const char* pText = pElem->GetText() ; const char* pText = pElem->GetText() ;
@ -4526,6 +4539,35 @@ void Game::loadstats(int *width, int *height, bool *vsync)
stat_trinkets = help.Int(pText); stat_trinkets = help.Int(pText);
} }
if (pKey == "swnbestrank")
{
swnbestrank = help.Int(pText);
}
if (pKey == "swnrecord")
{
swnrecord = help.Int(pText);
}
}
deserializesettings(dataNode, width, height, vsync);
}
void Game::deserializesettings(tinyxml2::XMLElement* dataNode, int* width, int* height, bool* vsync)
{
// Don't duplicate controller buttons!
controllerButton_flip.clear();
controllerButton_map.clear();
controllerButton_esc.clear();
controllerButton_restart.clear();
for (tinyxml2::XMLElement* pElem = dataNode;
pElem != NULL;
pElem = pElem->NextSiblingElement())
{
std::string pKey(pElem->Value());
const char* pText = pElem->GetText();
if (pKey == "fullscreen") if (pKey == "fullscreen")
{ {
fullscreen = help.Int(pText); fullscreen = help.Int(pText);
@ -4595,16 +4637,6 @@ void Game::loadstats(int *width, int *height, bool *vsync)
} }
if (pKey == "swnbestrank")
{
swnbestrank = help.Int(pText);
}
if (pKey == "swnrecord")
{
swnrecord = help.Int(pText);
}
if (pKey == "advanced_smoothing") if (pKey == "advanced_smoothing")
{ {
fullScreenEffect_badSignal = help.Int(pText); fullScreenEffect_badSignal = help.Int(pText);
@ -4734,7 +4766,7 @@ void Game::loadstats(int *width, int *height, bool *vsync)
} }
} }
void Game::savestats() void Game::savestats(const bool stats_only /*= true*/)
{ {
tinyxml2::XMLDocument doc; tinyxml2::XMLDocument doc;
bool already_exists = FILESYSTEM_loadTiXml2Document("saves/unlock.vvv", doc); bool already_exists = FILESYSTEM_loadTiXml2Document("saves/unlock.vvv", doc);
@ -4804,6 +4836,24 @@ void Game::savestats()
xml::update_tag(dataNode, "stat_trinkets", stat_trinkets); xml::update_tag(dataNode, "stat_trinkets", stat_trinkets);
xml::update_tag(dataNode, "swnbestrank", swnbestrank);
xml::update_tag(dataNode, "swnrecord", swnrecord);
serializesettings(dataNode);
FILESYSTEM_saveTiXml2Document("saves/unlock.vvv", doc);
if (!stats_only)
{
savesettings();
}
}
void Game::serializesettings(tinyxml2::XMLElement* dataNode)
{
tinyxml2::XMLDocument& doc = xml::get_document(dataNode);
xml::update_tag(dataNode, "fullscreen", fullscreen); xml::update_tag(dataNode, "fullscreen", fullscreen);
xml::update_tag(dataNode, "stretch", stretchMode); xml::update_tag(dataNode, "stretch", stretchMode);
@ -4834,10 +4884,6 @@ void Game::savestats()
xml::update_tag(dataNode, "slowdown", slowdown); xml::update_tag(dataNode, "slowdown", slowdown);
xml::update_tag(dataNode, "swnbestrank", swnbestrank);
xml::update_tag(dataNode, "swnrecord", swnrecord);
xml::update_tag(dataNode, "advanced_smoothing", fullScreenEffect_badSignal); xml::update_tag(dataNode, "advanced_smoothing", fullScreenEffect_badSignal);
@ -4927,8 +4973,58 @@ void Game::savestats()
} }
xml::update_tag(dataNode, "controllerSensitivity", controllerSensitivity); xml::update_tag(dataNode, "controllerSensitivity", controllerSensitivity);
}
FILESYSTEM_saveTiXml2Document("saves/unlock.vvv", doc); void Game::loadsettings(int* width, int* height, bool* vsync)
{
tinyxml2::XMLDocument doc;
if (!FILESYSTEM_loadTiXml2Document("saves/settings.vvv", doc))
{
savesettings();
puts("No settings.vvv found");
}
tinyxml2::XMLHandle hDoc(&doc);
tinyxml2::XMLElement* pElem;
tinyxml2::XMLHandle hRoot(NULL);
{
pElem = hDoc.FirstChildElement().ToElement();
// should always have a valid root but handle gracefully if it doesn't
if (!pElem)
{
}
;
// save this for later
hRoot = tinyxml2::XMLHandle(pElem);
}
tinyxml2::XMLElement* dataNode = hRoot.FirstChildElement("Data").FirstChild().ToElement();
deserializesettings(dataNode, width, height, vsync);
}
void Game::savesettings()
{
tinyxml2::XMLDocument doc;
bool already_exists = FILESYSTEM_loadTiXml2Document("saves/settings.vvv", doc);
if (!already_exists)
{
puts("No settings.vvv found. Creating new file");
}
xml::update_declaration(doc);
tinyxml2::XMLElement* root = xml::update_element(doc, "Settings");
xml::update_comment(root, " Settings (duplicated from unlock.vvv) ");
tinyxml2::XMLElement* dataNode = xml::update_element(root, "Data");
serializesettings(dataNode);
FILESYSTEM_saveTiXml2Document("saves/settings.vvv", doc);
} }
void Game::customstart() void Game::customstart()

View file

@ -6,7 +6,11 @@
#include <vector> #include <vector>
// Forward decl without including all of <tinyxml2.h> // Forward decl without including all of <tinyxml2.h>
namespace tinyxml2 { class XMLDocument; } namespace tinyxml2
{
class XMLDocument;
class XMLElement;
}
struct MenuOption struct MenuOption
{ {
@ -126,10 +130,20 @@ public:
void loadstats(int *width, int *height, bool *vsync); void loadstats(int *width, int *height, bool *vsync);
void savestats(); void savestats(const bool stats_only = false);
void deletestats(); void deletestats();
void deserializesettings(tinyxml2::XMLElement* dataNode, int* width, int* height, bool* vsync);
void serializesettings(tinyxml2::XMLElement* dataNode);
void loadsettings(int* width, int* height, bool* vsync);
void savesettings();
void deletesettings();
void deletequick(); void deletequick();
bool savetele(); bool savetele();

View file

@ -1280,6 +1280,7 @@ void menuactionpress()
game.deletequick(); game.deletequick();
game.deletetele(); game.deletetele();
game.deletestats(); game.deletestats();
game.deletesettings();
game.flashlight = 5; game.flashlight = 5;
game.screenshake = 15; game.screenshake = 15;
game.createmenu(Menu::mainmenu); game.createmenu(Menu::mainmenu);

View file

@ -4,17 +4,16 @@
namespace xml namespace xml
{ {
// Helper functions for these utils (not exported) // Just get the document, because TinyXML-2 is annoying.
// Useful outside of this file.
static inline tinyxml2::XMLDocument& get_document(tinyxml2::XMLNode* parent) // TODO: But XML handling should really be put in a separate file (maybe this
// one renamed?) instead of lumped in with Game.cpp, and when that happens
// maybe this will be unexported again?
tinyxml2::XMLDocument& get_document(tinyxml2::XMLNode* parent)
{ {
return *(parent->GetDocument()); return *(parent->GetDocument());
} }
// EXPORTED FUNCTIONS
// Create a new element if it doesn't exist. Returns the element. // Create a new element if it doesn't exist. Returns the element.
tinyxml2::XMLElement* update_element(tinyxml2::XMLNode* parent, const char* name) tinyxml2::XMLElement* update_element(tinyxml2::XMLNode* parent, const char* name)
{ {

View file

@ -11,6 +11,8 @@ namespace tinyxml2
namespace xml namespace xml
{ {
tinyxml2::XMLDocument& get_document(tinyxml2::XMLNode* parent);
tinyxml2::XMLElement* update_element(tinyxml2::XMLNode* parent, const char* name); tinyxml2::XMLElement* update_element(tinyxml2::XMLNode* parent, const char* name);
// Same thing as above, but takes &parent instead of *parent // Same thing as above, but takes &parent instead of *parent
tinyxml2::XMLElement* update_element(tinyxml2::XMLNode& parent, const char* name); tinyxml2::XMLElement* update_element(tinyxml2::XMLNode& parent, const char* name);

View file

@ -217,7 +217,12 @@ int main(int argc, char *argv[])
int width = 320; int width = 320;
int height = 240; int height = 240;
bool vsync = false; bool vsync = false;
// Prioritize unlock.vvv first (2.2 and below),
// but settings have been migrated to settings.vvv (2.3 and up)
game.loadstats(&width, &height, &vsync); game.loadstats(&width, &height, &vsync);
game.loadsettings(&width, &height, &vsync);
gameScreen.init( gameScreen.init(
width, width,
height, height,