212 lines
6.0 KiB
C++
212 lines
6.0 KiB
C++
#include "save.h"
|
|
#include <SDL3/SDL.h>
|
|
#include <cstdio>
|
|
#include <type_traits>
|
|
|
|
#ifdef SDL_PLATFORM_EMSCRIPTEN
|
|
#include <emscripten.h>
|
|
#endif
|
|
|
|
constexpr const char* KEY_DISCOVERED = "discovered";
|
|
constexpr const char* KEY_ITEM = "item";
|
|
constexpr const char* SETTINGS_FILE = "settings.txt";
|
|
|
|
static void value_read(const char* text, float& value)
|
|
{
|
|
value = (float)SDL_atof(text);
|
|
}
|
|
|
|
static void value_read(const char* text, int& value)
|
|
{
|
|
value = SDL_atoi(text);
|
|
}
|
|
|
|
static void value_read(const char* text, bool& value)
|
|
{
|
|
value = SDL_atoi(text);
|
|
}
|
|
|
|
static void value_read(const char* text, Measurement& value)
|
|
{
|
|
value = (Measurement)SDL_atoi(text);
|
|
}
|
|
|
|
static void value_read(const char* text, std::string& value)
|
|
{
|
|
value = text;
|
|
}
|
|
|
|
static void value_read(const char* text, Vec2& value)
|
|
{
|
|
SDL_sscanf(text, "%f %f", &value.x, &value.y);
|
|
}
|
|
|
|
static void value_read(const char* text, Color& value)
|
|
{
|
|
SDL_sscanf(text, "%f %f %f", &value.r, &value.g, &value.b);
|
|
}
|
|
|
|
static std::string value_write(float value)
|
|
{
|
|
return std::to_string(value);
|
|
}
|
|
|
|
static std::string value_write(int value)
|
|
{
|
|
return std::to_string(value);
|
|
}
|
|
|
|
static std::string value_write(const std::string& value)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
static std::string value_write(Vec2 value)
|
|
{
|
|
return value_write(value.x) + " " + value_write(value.y);
|
|
}
|
|
|
|
static std::string value_write(Color value)
|
|
{
|
|
return value_write(value.r) + " " + value_write(value.g) + " " + value_write(value.b);
|
|
}
|
|
|
|
// Calls line with each key and value of a key=value text file. Returns whether it was there.
|
|
template <class Line> static bool lines_read(const std::string& path, Line&& line)
|
|
{
|
|
char* text = (char*)SDL_LoadFile(path.c_str(), nullptr);
|
|
if (!text)
|
|
return false;
|
|
char* rest = nullptr;
|
|
for (char* entry = SDL_strtok_r(text, "\r\n", &rest); entry; entry = SDL_strtok_r(nullptr, "\r\n", &rest))
|
|
{
|
|
char* value = SDL_strchr(entry, '=');
|
|
if (!value)
|
|
continue;
|
|
*value++ = '\0';
|
|
line(std::string(entry), value);
|
|
}
|
|
SDL_free(text);
|
|
return true;
|
|
}
|
|
|
|
static void file_write(const std::string& path, const std::string& text)
|
|
{
|
|
SDL_CreateDirectory(path.substr(0, path.find_last_of('/')).c_str());
|
|
if (!SDL_SaveFile(path.c_str(), text.data(), text.size()))
|
|
SDL_Log("Failed to save %s: %s", path.c_str(), SDL_GetError());
|
|
#ifdef SDL_PLATFORM_EMSCRIPTEN
|
|
EM_ASM(FS.syncfs(false, () => {}));
|
|
#endif
|
|
}
|
|
|
|
// Reads every field a visitable struct names from a key=value file, and writes them back.
|
|
template <class Fields> static bool fields_read(const std::string& path, Fields& fields)
|
|
{
|
|
return lines_read(path, [&](const std::string& key, const char* value) {
|
|
fields.visit([&](const char* name, auto& field) {
|
|
if (key == name)
|
|
value_read(value, field);
|
|
});
|
|
});
|
|
}
|
|
|
|
template <class Fields> static std::string fields_write(Fields& fields)
|
|
{
|
|
std::string text;
|
|
fields.visit([&](const char* name, auto& field) {
|
|
using Type = std::decay_t<decltype(field)>;
|
|
if constexpr (std::is_same_v<Type, bool> || std::is_enum_v<Type>)
|
|
text += std::string(name) + "=" + value_write((int)field) + "\n";
|
|
else
|
|
text += std::string(name) + "=" + value_write(field) + "\n";
|
|
});
|
|
return text;
|
|
}
|
|
|
|
template <class Visitor> void Settings::visit(Visitor&& visitor)
|
|
{
|
|
visitor("character_color", isCharacterColor);
|
|
visitor("color", color);
|
|
visitor("console", isConsole);
|
|
visitor("height", height);
|
|
visitor("last_character", lastCharacter);
|
|
visitor("measurement", measurement);
|
|
visitor("mute", isMuted);
|
|
visitor("volume", volume);
|
|
visitor("width", width);
|
|
visitor("x", x);
|
|
visitor("y", y);
|
|
}
|
|
|
|
void Settings::load()
|
|
{
|
|
fields_read(pref_path_get(SETTINGS_FILE), *this);
|
|
}
|
|
|
|
void Settings::save()
|
|
{
|
|
file_write(pref_path_get(SETTINGS_FILE), fields_write(*this));
|
|
}
|
|
|
|
// Puts back the defaults of what the settings menu shows.
|
|
void Settings::reset()
|
|
{
|
|
Settings defaults;
|
|
color = defaults.color;
|
|
measurement = defaults.measurement;
|
|
volume = defaults.volume;
|
|
isCharacterColor = defaults.isCharacterColor;
|
|
isMuted = defaults.isMuted;
|
|
}
|
|
|
|
template <class Visitor> void Progress::visit(Visitor&& visitor)
|
|
{
|
|
visitor("alternate", isAlternate);
|
|
visitor("calories", calories);
|
|
visitor("capacity", capacity);
|
|
visitor("digesting", isDigesting);
|
|
visitor("digestion_progress", digestionProgress);
|
|
visitor("digestion_rate", digestionRate);
|
|
visitor("digestion_timer", digestionTimer);
|
|
visitor("pan", pan);
|
|
visitor("playtime", playtime);
|
|
visitor("postgame", isPostgame);
|
|
visitor("spawn_delay", spawnDelay);
|
|
visitor("total_calories", totalCalories);
|
|
visitor("total_consumed", totalConsumed);
|
|
visitor("total_spawned", totalSpawned);
|
|
visitor("weight", weight);
|
|
visitor("zoom", zoom);
|
|
}
|
|
|
|
// Items are kept one per line as their position, velocity, rotation and then name.
|
|
void Progress::load(const std::string& path)
|
|
{
|
|
*this = {};
|
|
isValid = fields_read(path, *this);
|
|
lines_read(path, [&](const std::string& key, const char* value) {
|
|
if (key == KEY_DISCOVERED)
|
|
discovered.emplace_back(value);
|
|
SavedItem item;
|
|
int length = 0;
|
|
if (key == KEY_ITEM && std::sscanf(value, "%f %f %f %f %f %n", &item.position.x, &item.position.y,
|
|
&item.velocity.x, &item.velocity.y, &item.rotation, &length) == 5)
|
|
{
|
|
item.name = value + length;
|
|
items.push_back(item);
|
|
}
|
|
});
|
|
}
|
|
|
|
void Progress::save(const std::string& path)
|
|
{
|
|
std::string text = fields_write(*this);
|
|
for (const std::string& name : discovered)
|
|
text += std::string(KEY_DISCOVERED) + "=" + name + "\n";
|
|
for (const SavedItem& item : items)
|
|
text += std::string(KEY_ITEM) + "=" + value_write(item.position) + " " + value_write(item.velocity) + " " +
|
|
value_write(item.rotation) + " " + item.name + "\n";
|
|
file_write(path, text);
|
|
}
|