fix: detecta fitxers de puntuació corruptes
This commit is contained in:
@@ -101,57 +101,175 @@ void ManageHiScoreTable::sort() {
|
||||
|
||||
// Carga la tabla desde un fichero
|
||||
auto ManageHiScoreTable::loadFromFile(const std::string& file_path) -> bool {
|
||||
clear();
|
||||
auto success = true;
|
||||
auto* file = SDL_IOFromFile(file_path.c_str(), "rb");
|
||||
|
||||
if (file != nullptr) {
|
||||
table_.clear(); // Limpia la tabla actual
|
||||
if (file == nullptr) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Unable to load %s file! %s", getFileName(file_path).c_str(), SDL_GetError());
|
||||
clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Lee el número de entradas en la tabla
|
||||
int table_size = 0;
|
||||
SDL_ReadIO(file, &table_size, sizeof(int));
|
||||
Table temp_table; // Tabla temporal para no corromper la actual si hay errores
|
||||
bool success = true;
|
||||
|
||||
// Lee los datos de cada entrada
|
||||
for (int i = 0; i < table_size; ++i) {
|
||||
HiScoreEntry entry;
|
||||
// Validación 1: Verificar magic number "CCAE"
|
||||
char magic[4];
|
||||
if (SDL_ReadIO(file, magic, 4) != 4 || magic[0] != 'C' || magic[1] != 'C' || magic[2] != 'A' || magic[3] != 'E') {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Invalid magic number in %s - file may be corrupted or old format", getFileName(file_path).c_str());
|
||||
SDL_CloseIO(file);
|
||||
clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Lee la puntuación
|
||||
SDL_ReadIO(file, &entry.score, sizeof(int));
|
||||
// Validación 2: Verificar versión del formato
|
||||
int version = 0;
|
||||
if (SDL_ReadIO(file, &version, sizeof(int)) != sizeof(int)) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Cannot read version in %s", getFileName(file_path).c_str());
|
||||
SDL_CloseIO(file);
|
||||
clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Lee el tamaño del nombre y luego el nombre
|
||||
int name_size = 0;
|
||||
SDL_ReadIO(file, &name_size, sizeof(int));
|
||||
if (version != FILE_VERSION) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Unsupported file version %d in %s (expected %d)", version, getFileName(file_path).c_str(), FILE_VERSION);
|
||||
SDL_CloseIO(file);
|
||||
clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<char> name_buffer(name_size + 1);
|
||||
SDL_ReadIO(file, name_buffer.data(), name_size);
|
||||
name_buffer[name_size] = '\0'; // Asegurar el fin de la cadena
|
||||
entry.name = std::string(name_buffer.data());
|
||||
// Validación 3: Leer y validar tamaño de la tabla
|
||||
int table_size = 0;
|
||||
if (SDL_ReadIO(file, &table_size, sizeof(int)) != sizeof(int)) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Cannot read table size in %s", getFileName(file_path).c_str());
|
||||
SDL_CloseIO(file);
|
||||
clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Lee el valor de one_credit_complete
|
||||
int occ_value = 0;
|
||||
SDL_ReadIO(file, &occ_value, sizeof(int));
|
||||
entry.one_credit_complete = (occ_value != 0);
|
||||
if (table_size < 0 || table_size > MAX_TABLE_SIZE) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Invalid table size %d in %s (expected 0-%d)", table_size, getFileName(file_path).c_str(), MAX_TABLE_SIZE);
|
||||
SDL_CloseIO(file);
|
||||
clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Añade la entrada a la tabla
|
||||
table_.push_back(entry);
|
||||
// Leer cada entrada con validaciones
|
||||
for (int i = 0; i < table_size; ++i) {
|
||||
HiScoreEntry entry;
|
||||
|
||||
// Validación 4: Leer y validar puntuación
|
||||
if (SDL_ReadIO(file, &entry.score, sizeof(int)) != sizeof(int)) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Cannot read score for entry %d in %s", i, getFileName(file_path).c_str());
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\nReading file: %s", getFileName(file_path).c_str());
|
||||
SDL_CloseIO(file);
|
||||
} else {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Unable to load %s file! %s", getFileName(file_path).c_str(), SDL_GetError());
|
||||
success = false;
|
||||
if (entry.score < 0 || entry.score > MAX_SCORE) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Invalid score %d for entry %d in %s", entry.score, i, getFileName(file_path).c_str());
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Validación 5: Leer y validar tamaño del nombre
|
||||
int name_size = 0;
|
||||
if (SDL_ReadIO(file, &name_size, sizeof(int)) != sizeof(int)) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Cannot read name size for entry %d in %s", i, getFileName(file_path).c_str());
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (name_size < 0 || name_size > MAX_NAME_SIZE) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Invalid name size %d for entry %d in %s", name_size, i, getFileName(file_path).c_str());
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Leer el nombre
|
||||
std::vector<char> name_buffer(name_size + 1);
|
||||
if (SDL_ReadIO(file, name_buffer.data(), name_size) != static_cast<size_t>(name_size)) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Cannot read name for entry %d in %s", i, getFileName(file_path).c_str());
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
name_buffer[name_size] = '\0';
|
||||
entry.name = std::string(name_buffer.data());
|
||||
|
||||
// Validación 6: Leer one_credit_complete
|
||||
int occ_value = 0;
|
||||
if (SDL_ReadIO(file, &occ_value, sizeof(int)) != sizeof(int)) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Cannot read one_credit_complete for entry %d in %s", i, getFileName(file_path).c_str());
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
entry.one_credit_complete = (occ_value != 0);
|
||||
|
||||
temp_table.push_back(entry);
|
||||
}
|
||||
|
||||
// Validación 7: Verificar checksum
|
||||
if (success) {
|
||||
unsigned int stored_checksum = 0;
|
||||
if (SDL_ReadIO(file, &stored_checksum, sizeof(unsigned int)) != sizeof(unsigned int)) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Cannot read checksum in %s", getFileName(file_path).c_str());
|
||||
success = false;
|
||||
} else {
|
||||
unsigned int calculated_checksum = calculateChecksum(temp_table);
|
||||
if (stored_checksum != calculated_checksum) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Checksum mismatch in %s (stored: 0x%08X, calculated: 0x%08X) - file is corrupted",
|
||||
getFileName(file_path).c_str(), stored_checksum, calculated_checksum);
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SDL_CloseIO(file);
|
||||
|
||||
// Si todo fue bien, actualizar la tabla; si no, usar valores por defecto
|
||||
if (success) {
|
||||
table_ = std::move(temp_table);
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\nReading file: %s (loaded %d entries successfully)", getFileName(file_path).c_str(), table_size);
|
||||
} else {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "File %s is corrupted - loading default values", getFileName(file_path).c_str());
|
||||
clear();
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
// Calcula checksum de la tabla
|
||||
auto ManageHiScoreTable::calculateChecksum(const Table& table) -> unsigned int {
|
||||
unsigned int checksum = 0x12345678; // Magic seed
|
||||
|
||||
for (const auto& entry : table) {
|
||||
// Checksum del score
|
||||
checksum = ((checksum << 5) + checksum) + static_cast<unsigned int>(entry.score);
|
||||
|
||||
// Checksum del nombre
|
||||
for (char c : entry.name) {
|
||||
checksum = ((checksum << 5) + checksum) + static_cast<unsigned int>(c);
|
||||
}
|
||||
|
||||
// Checksum de one_credit_complete
|
||||
checksum = ((checksum << 5) + checksum) + (entry.one_credit_complete ? 1u : 0u);
|
||||
}
|
||||
|
||||
return checksum;
|
||||
}
|
||||
|
||||
// Guarda la tabla en un fichero
|
||||
auto ManageHiScoreTable::saveToFile(const std::string& file_path) -> bool {
|
||||
auto success = true;
|
||||
auto* file = SDL_IOFromFile(file_path.c_str(), "w+b");
|
||||
|
||||
if (file != nullptr) {
|
||||
// Escribe magic number "CCAE"
|
||||
const char magic[4] = {'C', 'C', 'A', 'E'};
|
||||
SDL_WriteIO(file, magic, 4);
|
||||
|
||||
// Escribe versión del formato
|
||||
int version = FILE_VERSION;
|
||||
SDL_WriteIO(file, &version, sizeof(int));
|
||||
|
||||
// Guarda el número de entradas en la tabla
|
||||
int table_size = static_cast<int>(table_.size());
|
||||
SDL_WriteIO(file, &table_size, sizeof(int));
|
||||
@@ -173,6 +291,10 @@ auto ManageHiScoreTable::saveToFile(const std::string& file_path) -> bool {
|
||||
SDL_WriteIO(file, &occ_value, sizeof(int));
|
||||
}
|
||||
|
||||
// Calcula y escribe el checksum
|
||||
unsigned int checksum = calculateChecksum(table_);
|
||||
SDL_WriteIO(file, &checksum, sizeof(unsigned int));
|
||||
|
||||
// SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Writing file: %s", getFileName(file_path).c_str());
|
||||
Logger::info("Writing file: " + getFileName(file_path));
|
||||
SDL_CloseIO(file);
|
||||
|
||||
@@ -24,6 +24,10 @@ class ManageHiScoreTable {
|
||||
public:
|
||||
// --- Constantes ---
|
||||
static constexpr int NO_ENTRY = -1;
|
||||
static constexpr int FILE_VERSION = 1;
|
||||
static constexpr int MAX_TABLE_SIZE = 100;
|
||||
static constexpr int MAX_NAME_SIZE = 50;
|
||||
static constexpr int MAX_SCORE = 999999999;
|
||||
|
||||
// --- Constructor y destructor ---
|
||||
explicit ManageHiScoreTable(Table& table) // Constructor con referencia a tabla
|
||||
@@ -41,5 +45,6 @@ class ManageHiScoreTable {
|
||||
Table& table_; // Referencia a la tabla con los records
|
||||
|
||||
// --- Métodos privados ---
|
||||
void sort(); // Ordena la tabla
|
||||
void sort(); // Ordena la tabla
|
||||
static auto calculateChecksum(const Table& table) -> unsigned int; // Calcula checksum de la tabla
|
||||
};
|
||||
Reference in New Issue
Block a user