savestates: save the build name to be displayed when there's a version mismatch (#6493)

* savestates: add a build_name field to the header

* savestates: display build name on save/load menu

* savestates: add zero member to header just in case of UB from an older save state

* savestates: add legacy hash lookup

* savestate_data: update hash database
This commit is contained in:
Vitor K 2023-08-11 00:55:22 -03:00 committed by GitHub
parent af78268dd5
commit eb8d2941c9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 1470 additions and 11 deletions

View file

@ -1422,10 +1422,17 @@ void GMainWindow::UpdateSaveStates() {
actions_save_state[i]->setText(tr("Slot %1").arg(i + 1));
}
for (const auto& savestate : savestates) {
const auto text = tr("Slot %1 - %2")
.arg(savestate.slot)
.arg(QDateTime::fromSecsSinceEpoch(savestate.time)
.toString(QStringLiteral("yyyy-MM-dd hh:mm:ss")));
const bool display_name =
savestate.status == Core::SaveStateInfo::ValidationStatus::RevisionDismatch &&
!savestate.build_name.empty();
const auto text =
tr("Slot %1 - %2 %3")
.arg(savestate.slot)
.arg(QDateTime::fromSecsSinceEpoch(savestate.time)
.toString(QStringLiteral("yyyy-MM-dd hh:mm:ss")))
.arg(display_name ? QString::fromStdString(savestate.build_name) : QLatin1String())
.trimmed();
actions_load_state[savestate.slot - 1]->setEnabled(true);
actions_load_state[savestate.slot - 1]->setText(text);
actions_save_state[savestate.slot - 1]->setText(text);

View file

@ -463,6 +463,7 @@ add_library(citra_core STATIC
precompiled_headers.h
savestate.cpp
savestate.h
savestate_data.h
system_titles.cpp
system_titles.h
telemetry_session.cpp

View file

@ -15,18 +15,21 @@
#include "core/core.h"
#include "core/movie.h"
#include "core/savestate.h"
#include "core/savestate_data.h"
#include "network/network.h"
namespace Core {
#pragma pack(push, 1)
struct CSTHeader {
std::array<u8, 4> filetype; /// Unique Identifier to check the file type (always "CST"0x1B)
u64_le program_id; /// ID of the ROM being executed. Also called title_id
std::array<u8, 20> revision; /// Git hash of the revision this savestate was created with
u64_le time; /// The time when this save state was created
std::array<u8, 4> filetype; /// Unique Identifier to check the file type (always "CST"0x1B)
u64_le program_id; /// ID of the ROM being executed. Also called title_id
std::array<u8, 20> revision; /// Git hash of the revision this savestate was created with
u64_le time; /// The time when this save state was created
std::array<u8, 20> build_name; /// The build name (Canary/Nightly) with the version number
u32_le zero = 0; /// Should be zero, just in case.
std::array<u8, 216> reserved{}; /// Make heading 256 bytes so it has consistent size
std::array<u8, 192> reserved{}; /// Make heading 256 bytes so it has consistent size
};
static_assert(sizeof(CSTHeader) == 256, "CSTHeader should be 256 bytes");
#pragma pack(pop)
@ -58,11 +61,26 @@ static bool ValidateSaveState(const CSTHeader& header, SaveStateInfo& info, u64
return false;
}
const std::string revision = fmt::format("{:02x}", fmt::join(header.revision, ""));
const std::string build_name =
header.zero == 0 ? reinterpret_cast<const char*>(header.build_name.data()) : "";
if (revision == Common::g_scm_rev) {
info.status = SaveStateInfo::ValidationStatus::OK;
} else {
LOG_WARNING(Core, "Save state file {} created from a different revision {}", path,
revision);
if (!build_name.empty()) {
info.build_name = build_name;
} else if (hash_to_version.find(revision) != hash_to_version.end()) {
info.build_name = hash_to_version.at(revision);
}
if (info.build_name.empty()) {
LOG_WARNING(Core, "Save state file {} created from a different revision {}", path,
revision);
} else {
LOG_WARNING(Core,
"Save state file {} created from a different build {} with revision {}",
path, info.build_name, revision);
}
info.status = SaveStateInfo::ValidationStatus::RevisionDismatch;
}
return true;
@ -134,6 +152,10 @@ void System::SaveState(u32 slot) const {
header.time = std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch())
.count();
const std::string build_fullname = Common::g_build_fullname;
std::memset(header.build_name.data(), 0, sizeof(header.build_name));
std::memcpy(header.build_name.data(), build_fullname.c_str(),
std::min(build_fullname.length(), sizeof(header.build_name) - 1));
if (file.WriteBytes(&header, sizeof(header)) != sizeof(header) ||
file.WriteBytes(buffer.data(), buffer.size()) != buffer.size()) {

View file

@ -4,6 +4,7 @@
#pragma once
#include <string>
#include <vector>
#include "common/common_types.h"
@ -16,6 +17,7 @@ struct SaveStateInfo {
OK,
RevisionDismatch,
} status;
std::string build_name;
};
constexpr u32 SaveStateSlotCount = 10; // Maximum count of savestate slots

1427
src/core/savestate_data.h Normal file

File diff suppressed because it is too large Load diff