achievement_data: added checksum and format verification

This commit is contained in:
Hyper 2024-12-01 03:20:51 +00:00
parent 6151be142e
commit f1448b12e2
2 changed files with 117 additions and 16 deletions

View file

@ -3,10 +3,29 @@
#include <ui/achievement_overlay.h>
#include <user/config.h>
#define NUM_RECORDS sizeof(Data.Records) / sizeof(Record)
time_t AchievementData::GetTimestamp(uint16_t id)
{
for (int i = 0; i < NUM_RECORDS; i++)
{
if (!Data.Records[i].ID)
break;
if (Data.Records[i].ID == id)
return Data.Records[i].Timestamp;
}
return 0;
}
bool AchievementData::IsUnlocked(uint16_t id)
{
for (int i = 0; i < sizeof(Data.Records) / sizeof(Record); i++)
for (int i = 0; i < NUM_RECORDS; i++)
{
if (!Data.Records[i].ID)
break;
if (Data.Records[i].ID == id)
return true;
}
@ -19,7 +38,7 @@ void AchievementData::Unlock(uint16_t id)
if (IsUnlocked(id))
return;
for (int i = 0; i < sizeof(Data.Records) / sizeof(Record); i++)
for (int i = 0; i < NUM_RECORDS; i++)
{
if (Data.Records[i].ID == 0)
{
@ -33,15 +52,39 @@ void AchievementData::Unlock(uint16_t id)
AchievementOverlay::Open(id);
}
time_t AchievementData::GetTimestamp(uint16_t id)
uint32_t AchievementData::CalculateChecksum()
{
for (int i = 0; i < sizeof(Data.Records) / sizeof(Record); i++)
auto result = 0;
for (int i = 0; i < NUM_RECORDS; i++)
{
if (Data.Records[i].ID == id)
return Data.Records[i].Timestamp;
auto& record = Data.Records[i];
for (size_t j = 0; j < sizeof(Record); j++)
result ^= ((uint8_t*)(&record))[j];
}
return 0;
return result;
}
bool AchievementData::VerifySignature()
{
char sig[4] = ACH_SIGNATURE;
return Data.Signature[0] == sig[0] &&
Data.Signature[1] == sig[1] &&
Data.Signature[2] == sig[2] &&
Data.Signature[3] == sig[3];
}
bool AchievementData::VerifyVersion()
{
return Data.Version == Version ACH_VERSION;
}
bool AchievementData::VerifyChecksum()
{
return Data.Checksum == CalculateChecksum();
}
void AchievementData::Load()
@ -55,11 +98,48 @@ void AchievementData::Load()
if (!file)
{
printf("[*] Failed to parse achievement data.\n");
printf("[*] ERROR: failed to read achievement data.\n");
return;
}
file.read((char*)&Data.Signature, sizeof(Data.Signature));
if (!VerifySignature())
{
printf("[*] ERROR: invalid achievement data signature.\n");
char sig[4] = ACH_SIGNATURE;
Data.Signature[0] = sig[0];
Data.Signature[1] = sig[1];
Data.Signature[2] = sig[2];
Data.Signature[3] = sig[3];
file.close();
return;
}
file.read((char*)&Data.Version, sizeof(Data.Version));
if (!VerifyVersion())
{
printf("[*] ERROR: unsupported achievement data version.\n");
Data.Version = ACH_VERSION;
file.close();
return;
}
file.seekg(0);
file.read((char*)&Data, sizeof(Data));
// TODO: display error message to user before wiping data?
if (!VerifyChecksum())
{
printf("[*] ERROR: achievement data checksum mismatch.\n");
memset(&Data.Records, 0, sizeof(Data.Records));
}
file.close();
}
@ -69,10 +149,12 @@ void AchievementData::Save()
if (!file)
{
printf("[*] Failed to write achievement data.\n");
printf("[*] ERROR: failed to write achievement data.\n");
return;
}
Data.Checksum = CalculateChecksum();
file.write((const char*)&Data, sizeof(Data));
file.close();
}

View file

@ -1,10 +1,9 @@
#pragma once
#include <cstdint>
#include <user/paths.h>
#define ACH_SIGNATURE 'ACH '
#define ACH_VERSION 0x01000000
#define ACH_SIGNATURE { 'A', 'C', 'H', ' ' }
#define ACH_VERSION { 1, 0, 0 }
class AchievementData
{
@ -18,12 +17,28 @@ public:
};
#pragma pack(pop)
struct Version
{
uint8_t Major;
uint8_t Minor;
uint8_t Revision;
uint8_t Reserved;
bool operator==(const Version& other) const
{
return Major == other.Major &&
Minor == other.Minor &&
Revision == other.Revision;
}
};
class Data
{
public:
uint32_t Signature{};
uint32_t Version{};
uint32_t Reserved[2];
char Signature[4];
Version Version{};
uint32_t Checksum;
uint32_t Reserved;
Record Records[50];
};
@ -34,9 +49,13 @@ public:
return GetSavePath() / "ACH-DATA";
}
static time_t GetTimestamp(uint16_t id);
static bool IsUnlocked(uint16_t id);
static void Unlock(uint16_t id);
static time_t GetTimestamp(uint16_t id);
static uint32_t CalculateChecksum();
static bool VerifySignature();
static bool VerifyVersion();
static bool VerifyChecksum();
static void Load();
static void Save();
};