Merge branch 'save-corruption' into 'master'

Save corruption fixes

See merge request KartKrew/Kart!2314
This commit is contained in:
Eidolon 2024-04-30 20:49:02 +00:00
commit f4e023a38e
2 changed files with 35 additions and 21 deletions

View file

@ -13,6 +13,7 @@
#include <algorithm> #include <algorithm>
#include <cstdint> #include <cstdint>
#include <cstddef> #include <cstddef>
#include <exception>
#include <filesystem> #include <filesystem>
#include <fmt/format.h> #include <fmt/format.h>
@ -286,7 +287,8 @@ void srb2::save_ng_gamedata()
std::string gamedataname_s {gamedatafilename}; std::string gamedataname_s {gamedatafilename};
fs::path savepath {fmt::format("{}/{}", srb2home, gamedataname_s)}; fs::path savepath {fmt::format("{}/{}", srb2home, gamedataname_s)};
fs::path tmpsavepath {fmt::format("{}/{}.tmp", srb2home, gamedataname_s)}; int random_number = rand();
fs::path tmpsavepath {fmt::format("{}/{}_{}.tmp", srb2home, gamedataname_s, random_number)};
json ngdata_json = ng; json ngdata_json = ng;
@ -294,20 +296,17 @@ void srb2::save_ng_gamedata()
{ {
std::string tmpsavepathstring = tmpsavepath.string(); std::string tmpsavepathstring = tmpsavepath.string();
srb2::io::FileStream file {tmpsavepathstring, srb2::io::FileStreamMode::kWrite}; srb2::io::FileStream file {tmpsavepathstring, srb2::io::FileStreamMode::kWrite};
srb2::io::BufferedOutputStream<srb2::io::FileStream> bos {std::move(file)};
// The header is necessary to validate during loading. // The header is necessary to validate during loading.
srb2::io::write(static_cast<uint32_t>(GD_VERSION_MAJOR), bos); // major srb2::io::write(static_cast<uint32_t>(GD_VERSION_MAJOR), file); // major
srb2::io::write(static_cast<uint8_t>(GD_VERSION_MINOR), bos); // minor/flags srb2::io::write(static_cast<uint8_t>(GD_VERSION_MINOR), file); // minor/flags
srb2::io::write(static_cast<uint8_t>(gamedata->evercrashed), bos); // dirty (crash recovery) srb2::io::write(static_cast<uint8_t>(gamedata->evercrashed), file); // dirty (crash recovery)
std::vector<uint8_t> ubjson = json::to_ubjson(ng); std::vector<uint8_t> ubjson = json::to_ubjson(ng);
srb2::io::write_exact(bos, tcb::as_bytes(tcb::make_span(ubjson))); srb2::io::write_exact(file, tcb::as_bytes(tcb::make_span(ubjson)));
bos.flush();
file = bos.stream();
file.close(); file.close();
} }
catch (const srb2::io::FileStreamException& ex) catch (const std::exception& ex)
{ {
CONS_Alert(CONS_ERROR, "NG Gamedata save failed: %s\n", ex.what()); CONS_Alert(CONS_ERROR, "NG Gamedata save failed: %s\n", ex.what());
} }
@ -434,6 +433,13 @@ void srb2::load_ng_gamedata()
json parsed = json::from_ubjson(remainder_as_u8); json parsed = json::from_ubjson(remainder_as_u8);
js = parsed.template get<GamedataJson>(); js = parsed.template get<GamedataJson>();
} }
catch (const std::exception& ex)
{
const char* gdfolder = G_GameDataFolder();
const char* what = ex.what();
I_Error("Game data is corrupt.\nDelete %s (maybe in %s) and try again.\n\nException: %s", gamedatafilename, gdfolder, what);
return;
}
catch (...) catch (...)
{ {
const char* gdfolder = G_GameDataFolder(); const char* gdfolder = G_GameDataFolder();

View file

@ -12,6 +12,7 @@
/// \brief implements methods for profiles etc. /// \brief implements methods for profiles etc.
#include <algorithm> #include <algorithm>
#include <exception>
#include <fmt/format.h> #include <fmt/format.h>
@ -315,26 +316,28 @@ void PR_SaveProfiles(void)
std::vector<uint8_t> ubjson = json::to_ubjson(ng); std::vector<uint8_t> ubjson = json::to_ubjson(ng);
std::string realpath = fmt::format("{}/{}", srb2home, PROFILESFILE); std::string realpath = fmt::format("{}/{}", srb2home, PROFILESFILE);
std::string tmppath = fmt::format("{}.tmp", realpath); int random_number = rand();
std::string tmppath = fmt::format("{}_{}.tmp", realpath, random_number);
try try
{ {
io::FileStream file {tmppath, io::FileStreamMode::kWrite}; io::FileStream file {tmppath, io::FileStreamMode::kWrite};
io::BufferedOutputStream<io::FileStream> bos {std::move(file)};
io::write(static_cast<uint32_t>(0x52494E47), bos, io::Endian::kBE); // "RING" io::write(static_cast<uint32_t>(0x52494E47), file, io::Endian::kBE); // "RING"
io::write(static_cast<uint32_t>(0x5052464C), bos, io::Endian::kBE); // "PRFL" io::write(static_cast<uint32_t>(0x5052464C), file, io::Endian::kBE); // "PRFL"
io::write(static_cast<uint8_t>(0), bos); // reserved1 io::write(static_cast<uint8_t>(0), file); // reserved1
io::write(static_cast<uint8_t>(0), bos); // reserved2 io::write(static_cast<uint8_t>(0), file); // reserved2
io::write(static_cast<uint8_t>(0), bos); // reserved3 io::write(static_cast<uint8_t>(0), file); // reserved3
io::write(static_cast<uint8_t>(0), bos); // reserved4 io::write(static_cast<uint8_t>(0), file); // reserved4
io::write_exact(bos, tcb::as_bytes(tcb::make_span(ubjson))); io::write_exact(file, tcb::as_bytes(tcb::make_span(ubjson)));
bos.flush();
file = bos.stream();
file.close(); file.close();
fs::rename(tmppath, realpath); fs::rename(tmppath, realpath);
} }
catch (const std::exception& ex)
{
I_Error("Couldn't save profiles. Are you out of Disk space / playing in a protected folder?\n\nException: %s", ex.what());
}
catch (...) catch (...)
{ {
I_Error("Couldn't save profiles. Are you out of Disk space / playing in a protected folder?"); I_Error("Couldn't save profiles. Are you out of Disk space / playing in a protected folder?");
@ -398,9 +401,14 @@ void PR_LoadProfiles(void)
json parsed = json::from_ubjson(remainder_as_u8); json parsed = json::from_ubjson(remainder_as_u8);
js = parsed.template get<ProfilesJson>(); js = parsed.template get<ProfilesJson>();
} }
catch (const std::exception& ex)
{
I_Error("Profiles file is corrupt.\n\nException: %s", ex.what());
return;
}
catch (...) catch (...)
{ {
I_Error("Profiles file is corrupt"); I_Error("Profiles file is corrupt.");
return; return;
} }