#include "asset.h" #include #include #include #include #include #include "utils.h" // Singleton Asset *Asset::instance = nullptr; void Asset::init(const std::string &executable_path) { Asset::instance = new Asset(executable_path); } void Asset::destroy() { delete Asset::instance; } auto Asset::get() -> Asset * { return Asset::instance; } // Añade un elemento al mapa (función auxiliar) void Asset::addToMap(const std::string &file_path, Type type, bool required, bool absolute) { std::string full_path = absolute ? file_path : executable_path_ + file_path; std::string filename = getFileName(full_path); // Verificar si ya existe el archivo if (file_list_.find(filename) != file_list_.end()) { SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Warning: Asset '%s' already exists, overwriting", filename.c_str()); } file_list_.emplace(filename, Item{std::move(full_path), type, required}); } // Añade un elemento a la lista void Asset::add(const std::string &file_path, Type type, bool required, bool absolute) { addToMap(file_path, type, required, absolute); } // Carga recursos desde un archivo de configuración con soporte para variables // Carga recursos desde un archivo de configuración con soporte para variables void Asset::loadFromFile(const std::string &config_file_path, const std::string &prefix, const std::string &system_folder) { std::ifstream file(config_file_path); if (!file.is_open()) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Cannot open config file: %s", config_file_path.c_str()); return; } std::string line; int line_number = 0; while (std::getline(file, line)) { ++line_number; // Limpiar espacios en blanco al principio y final line.erase(0, line.find_first_not_of(" \t\r")); line.erase(line.find_last_not_of(" \t\r") + 1); // DEBUG: mostrar línea leída (opcional, puedes comentar esta línea) // SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Line %d: '%s'", line_number, line.c_str()); // Ignorar líneas vacías y comentarios if (line.empty() || line[0] == '#' || line[0] == ';') { continue; } // Dividir la línea por el separador '|' std::vector parts; std::istringstream iss(line); std::string part; while (std::getline(iss, part, '|')) { parts.push_back(part); } // Verificar que tenemos al menos tipo y ruta if (parts.size() < 2) { SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Warning: Malformed line %d in config file (insufficient fields)", line_number); continue; } try { std::string type_str = parts[0]; std::string path = parts[1]; // Valores por defecto bool required = true; bool absolute = false; // Si hay opciones en el tercer campo, parsearlas if (parts.size() >= 3) { parseOptions(parts[2], required, absolute); } // Reemplazar variables en la ruta path = replaceVariables(path, prefix, system_folder); // Parsear el tipo de asset Type type = parseAssetType(type_str); // Añadir al mapa addToMap(path, type, required, absolute); } catch (const std::exception &e) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error parsing line %d in config file: %s", line_number, e.what()); } } SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Loaded %d assets from config file", static_cast(file_list_.size())); file.close(); } // Devuelve la ruta completa a un fichero (búsqueda O(1)) auto Asset::get(const std::string &filename) const -> std::string { auto it = file_list_.find(filename); if (it != file_list_.end()) { return it->second.file; } SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Warning: file %s not found", filename.c_str()); return ""; } // Verifica si un recurso existe auto Asset::exists(const std::string &filename) const -> bool { return file_list_.find(filename) != file_list_.end(); } // Comprueba que existen todos los elementos auto Asset::check() const -> bool { bool success = true; SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n** CHECKING FILES"); // Agrupar por tipo para mostrar organizado std::unordered_map> by_type; for (const auto &[filename, item] : file_list_) { if (item.required) { by_type[item.type].push_back(&item); } } // Verificar por tipo for (int type = 0; type < static_cast(Type::SIZE); ++type) { Type asset_type = static_cast(type); if (by_type.find(asset_type) != by_type.end()) { SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> %s FILES", getTypeName(asset_type).c_str()); bool type_success = true; for (const auto *item : by_type[asset_type]) { if (!checkFile(item->file)) { success = false; type_success = false; } } if (type_success) { SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, " All files are OK."); } } } // Resultado if (success) { SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n** CHECKING FILES COMPLETED.\n"); } else { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "\n** CHECKING FILES FAILED.\n"); } return success; } // Comprueba que existe un fichero auto Asset::checkFile(const std::string &path) -> bool { std::ifstream file(path); bool success = file.good(); file.close(); if (!success) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Checking file: %s [ ERROR ]", getFileName(path).c_str()); } return success; } // Parsea string a Type auto Asset::parseAssetType(const std::string &type_str) -> Type { if (type_str == "BITMAP") { return Type::BITMAP; } if (type_str == "MUSIC") { return Type::MUSIC; } if (type_str == "SOUND") { return Type::SOUND; } if (type_str == "FONT") { return Type::FONT; } if (type_str == "LANG") { return Type::LANG; } if (type_str == "DATA") { return Type::DATA; } if (type_str == "DEMODATA") { return Type::DEMODATA; } if (type_str == "ANIMATION") { return Type::ANIMATION; } if (type_str == "PALETTE") { return Type::PALETTE; } throw std::runtime_error("Unknown asset type: " + type_str); } // Devuelve el nombre del tipo de recurso auto Asset::getTypeName(Type type) -> std::string { switch (type) { case Type::BITMAP: return "BITMAP"; case Type::MUSIC: return "MUSIC"; case Type::SOUND: return "SOUND"; case Type::FONT: return "FONT"; case Type::LANG: return "LANG"; case Type::DATA: return "DATA"; case Type::DEMODATA: return "DEMODATA"; case Type::ANIMATION: return "ANIMATION"; case Type::PALETTE: return "PALETTE"; default: return "ERROR"; } } // Devuelve la lista de recursos de un tipo auto Asset::getListByType(Type type) const -> std::vector { std::vector list; for (const auto &[filename, item] : file_list_) { if (item.type == type) { list.push_back(item.file); } } return list; } // Reemplaza variables en las rutas auto Asset::replaceVariables(const std::string &path, const std::string &prefix, const std::string &system_folder) -> std::string { std::string result = path; // Reemplazar ${PREFIX} size_t pos = 0; while ((pos = result.find("${PREFIX}", pos)) != std::string::npos) { result.replace(pos, 9, prefix); // 9 = longitud de "${PREFIX}" pos += prefix.length(); } // Reemplazar ${SYSTEM_FOLDER} pos = 0; while ((pos = result.find("${SYSTEM_FOLDER}", pos)) != std::string::npos) { result.replace(pos, 16, system_folder); // 16 = longitud de "${SYSTEM_FOLDER}" pos += system_folder.length(); } return result; } // Parsea las opciones de una línea de configuración auto Asset::parseOptions(const std::string &options, bool &required, bool &absolute) -> void { if (options.empty()) { return; } std::istringstream iss(options); std::string option; while (std::getline(iss, option, ',')) { // Eliminar espacios option.erase(0, option.find_first_not_of(" \t")); option.erase(option.find_last_not_of(" \t") + 1); if (option == "optional") { required = false; } else if (option == "absolute") { absolute = true; } } }