diff --git a/source/manage_hiscore_table.cpp b/source/manage_hiscore_table.cpp index 92b7a41..834db19 100644 --- a/source/manage_hiscore_table.cpp +++ b/source/manage_hiscore_table.cpp @@ -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 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 name_buffer(name_size + 1); + if (SDL_ReadIO(file, name_buffer.data(), name_size) != static_cast(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(entry.score); + + // Checksum del nombre + for (char c : entry.name) { + checksum = ((checksum << 5) + checksum) + static_cast(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(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); diff --git a/source/manage_hiscore_table.hpp b/source/manage_hiscore_table.hpp index 5ee9566..ff63cf1 100644 --- a/source/manage_hiscore_table.hpp +++ b/source/manage_hiscore_table.hpp @@ -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 }; \ No newline at end of file