diff --git a/CMakeLists.txt b/CMakeLists.txt index 594c6bd..e577564 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -110,7 +110,10 @@ add_executable(pack_resources EXCLUDE_FROM_ALL tools/pack_resources/pack_resources.cpp source/core/resources/resource_pack.cpp ) -target_include_directories(pack_resources PRIVATE "${CMAKE_SOURCE_DIR}/source") +target_include_directories(pack_resources PRIVATE + "${CMAKE_SOURCE_DIR}/source" + "${CMAKE_BINARY_DIR}" +) target_compile_options(pack_resources PRIVATE -Wall -Wextra -Wpedantic) # --- REGENERACIÓ AUTOMÀTICA DE build/resources.pack --- diff --git a/source/core/resources/resource_pack.cpp b/source/core/resources/resource_pack.cpp index c697692..8183cf3 100644 --- a/source/core/resources/resource_pack.cpp +++ b/source/core/resources/resource_pack.cpp @@ -10,300 +10,287 @@ namespace Resource { -// Calcular checksum CRC32 simplificat -auto Pack::calculateChecksum(const std::vector& data) -> uint32_t { - uint32_t checksum = 0x12345678; - for (unsigned char byte : data) { - checksum = ((checksum << 5) + checksum) + byte; - } - return checksum; -} - -// Encriptació XOR (simètrica) -void Pack::encryptData(std::vector& data, const std::string& key) { - if (key.empty()) { - return; - } - for (size_t i = 0; i < data.size(); ++i) { - data[i] ^= key[i % key.length()]; - } -} - -void Pack::decryptData(std::vector& data, const std::string& key) { - // XOR es simètric - encryptData(data, key); -} - -// Llegir file complet a memòria -auto Pack::readFile(const std::string& filepath) -> std::vector { - std::ifstream file(filepath, std::ios::binary | std::ios::ate); - if (!file) { - std::cerr << "[ResourcePack] Error: no es pot obrir " << filepath << '\n'; - return {}; + // Calcular checksum CRC32 simplificat + auto Pack::calculateChecksum(const std::vector& data) -> uint32_t { + uint32_t checksum = 0x12345678; + for (unsigned char byte : data) { + checksum = ((checksum << 5) + checksum) + byte; + } + return checksum; } - std::streamsize file_size = file.tellg(); - file.seekg(0, std::ios::beg); - - std::vector data(file_size); - if (!file.read(reinterpret_cast(data.data()), file_size)) { - std::cerr << "[ResourcePack] Error: no es pot llegir " << filepath << '\n'; - return {}; + // Encriptació XOR (simètrica) + void Pack::encryptData(std::vector& data, const std::string& key) { + if (key.empty()) { + return; + } + for (size_t i = 0; i < data.size(); ++i) { + data[i] ^= key[i % key.length()]; + } } - return data; -} - -// Añadir un file individual al paquet -auto Pack::addFile(const std::string& filepath, const std::string& pack_name) -> bool { - auto file_data = readFile(filepath); - if (file_data.empty()) { - return false; + void Pack::decryptData(std::vector& data, const std::string& key) { + // XOR es simètric + encryptData(data, key); } - ResourceEntry entry{ - .filename = pack_name, - .offset = data_.size(), - .size = file_data.size(), - .checksum = calculateChecksum(file_data)}; - - // Añadir dades al bloc de dades - data_.insert(data_.end(), file_data.begin(), file_data.end()); - - resources_[pack_name] = entry; - - std::cout << "[ResourcePack] Añadido: " << pack_name << " (" << file_data.size() - << " bytes)\n"; - return true; -} - -// Añadir todos los archivos de un directori recursivament -auto Pack::addDirectory(const std::string& dir_path, - const std::string& base_path) -> bool { - namespace fs = std::filesystem; - - if (!fs::exists(dir_path) || !fs::is_directory(dir_path)) { - std::cerr << "[ResourcePack] Error: directori no trobat: " << dir_path << '\n'; - return false; - } - - std::string current_base = base_path.empty() ? "" : base_path + "/"; - - for (const auto& entry : fs::recursive_directory_iterator(dir_path)) { - if (!entry.is_regular_file()) { - continue; + // Llegir file complet a memòria + auto Pack::readFile(const std::string& filepath) -> std::vector { + std::ifstream file(filepath, std::ios::binary | std::ios::ate); + if (!file) { + std::cerr << "[ResourcePack] Error: no es pot obrir " << filepath << '\n'; + return {}; } - std::string full_path = entry.path().string(); - std::string relative_path = entry.path().lexically_relative(dir_path).string(); + std::streamsize file_size = file.tellg(); + file.seekg(0, std::ios::beg); - // Convertir barres invertides a normals (Windows) - std::ranges::replace(relative_path, '\\', '/'); - - // Saltar archivos de desenvolupament - if (relative_path.find(".world") != std::string::npos || - relative_path.find(".tsx") != std::string::npos || - relative_path.find(".DS_Store") != std::string::npos || - relative_path.find(".git") != std::string::npos) { - std::cout << "[ResourcePack] Saltant: " << relative_path << '\n'; - continue; + std::vector data(file_size); + if (!file.read(reinterpret_cast(data.data()), file_size)) { + std::cerr << "[ResourcePack] Error: no es pot llegir " << filepath << '\n'; + return {}; } - std::string pack_name = current_base + relative_path; - addFile(full_path, pack_name); + return data; } - return true; -} + // Añadir un file individual al paquet + auto Pack::addFile(const std::string& filepath, const std::string& pack_name) -> bool { + auto file_data = readFile(filepath); + if (file_data.empty()) { + return false; + } -// Guardar paquet a disc -auto Pack::savePack(const std::string& pack_file) -> bool { - std::ofstream file(pack_file, std::ios::binary); - if (!file) { - std::cerr << "[ResourcePack] Error: no es pot crear " << pack_file << '\n'; - return false; + ResourceEntry entry{ + .filename = pack_name, + .offset = data_.size(), + .size = file_data.size(), + .checksum = calculateChecksum(file_data)}; + + // Añadir dades al bloc de dades + data_.insert(data_.end(), file_data.begin(), file_data.end()); + + resources_[pack_name] = entry; + + return true; } - // Escriure capçalera - file.write(MAGIC_HEADER, 4); - file.write(reinterpret_cast(&VERSION), sizeof(VERSION)); + // Añadir todos los archivos de un directori recursivament + auto Pack::addDirectory(const std::string& dir_path, + const std::string& base_path) -> bool { + namespace fs = std::filesystem; - // Escriure nombre de recursos - auto resource_count = static_cast(resources_.size()); - file.write(reinterpret_cast(&resource_count), sizeof(resource_count)); + if (!fs::exists(dir_path) || !fs::is_directory(dir_path)) { + std::cerr << "[ResourcePack] Error: directori no trobat: " << dir_path << '\n'; + return false; + } - // Escriure metadades de recursos - for (const auto& [name, entry] : resources_) { - // Nom del file - auto name_len = static_cast(entry.filename.length()); - file.write(reinterpret_cast(&name_len), sizeof(name_len)); - file.write(entry.filename.c_str(), name_len); + std::string current_base = base_path.empty() ? "" : base_path + "/"; - // Offset, mida, checksum - file.write(reinterpret_cast(&entry.offset), sizeof(entry.offset)); - file.write(reinterpret_cast(&entry.size), sizeof(entry.size)); - file.write(reinterpret_cast(&entry.checksum), sizeof(entry.checksum)); + for (const auto& entry : fs::recursive_directory_iterator(dir_path)) { + if (!entry.is_regular_file()) { + continue; + } + + std::string full_path = entry.path().string(); + std::string relative_path = entry.path().lexically_relative(dir_path).string(); + + // Convertir barres invertides a normals (Windows) + std::ranges::replace(relative_path, '\\', '/'); + + // Saltar archivos de desenvolupament + if (relative_path.find(".world") != std::string::npos || + relative_path.find(".tsx") != std::string::npos || + relative_path.find(".DS_Store") != std::string::npos || + relative_path.find(".git") != std::string::npos) { + continue; + } + + std::string pack_name = current_base + relative_path; + addFile(full_path, pack_name); + } + + return true; } - // Encriptar dades - std::vector encrypted_data = data_; - encryptData(encrypted_data, DEFAULT_ENCRYPT_KEY); + // Guardar paquet a disc + auto Pack::savePack(const std::string& pack_file) -> bool { + std::ofstream file(pack_file, std::ios::binary); + if (!file) { + std::cerr << "[ResourcePack] Error: no es pot crear " << pack_file << '\n'; + return false; + } - // Escriure mida de dades y dades encriptades - auto data_size = static_cast(encrypted_data.size()); - file.write(reinterpret_cast(&data_size), sizeof(data_size)); - file.write(reinterpret_cast(encrypted_data.data()), encrypted_data.size()); + // Escriure capçalera + file.write(MAGIC_HEADER, 4); + file.write(reinterpret_cast(&VERSION), sizeof(VERSION)); - std::cout << "[ResourcePack] Guardat: " << pack_file << " (" << resources_.size() - << " recursos, " << data_size << " bytes)\n"; + // Escriure nombre de recursos + auto resource_count = static_cast(resources_.size()); + file.write(reinterpret_cast(&resource_count), sizeof(resource_count)); - return true; -} + // Escriure metadades de recursos + for (const auto& [name, entry] : resources_) { + // Nom del file + auto name_len = static_cast(entry.filename.length()); + file.write(reinterpret_cast(&name_len), sizeof(name_len)); + file.write(entry.filename.c_str(), name_len); -// Carregar paquet desde disc -auto Pack::loadPack(const std::string& pack_file) -> bool { - std::ifstream file(pack_file, std::ios::binary); - if (!file) { - std::cerr << "[ResourcePack] Error: no es pot obrir " << pack_file << '\n'; - return false; + // Offset, mida, checksum + file.write(reinterpret_cast(&entry.offset), sizeof(entry.offset)); + file.write(reinterpret_cast(&entry.size), sizeof(entry.size)); + file.write(reinterpret_cast(&entry.checksum), sizeof(entry.checksum)); + } + + // Encriptar dades + std::vector encrypted_data = data_; + encryptData(encrypted_data, DEFAULT_ENCRYPT_KEY); + + // Escriure mida de dades y dades encriptades + auto data_size = static_cast(encrypted_data.size()); + file.write(reinterpret_cast(&data_size), sizeof(data_size)); + file.write(reinterpret_cast(encrypted_data.data()), encrypted_data.size()); + + return true; } - // Llegir capçalera - char magic[4]; - file.read(magic, 4); - if (std::string(magic, 4) != MAGIC_HEADER) { - std::cerr << "[ResourcePack] Error: capçalera invàlida (esperava " << MAGIC_HEADER - << ")\n"; - return false; + // Carregar paquet desde disc + auto Pack::loadPack(const std::string& pack_file) -> bool { + std::ifstream file(pack_file, std::ios::binary); + if (!file) { + std::cerr << "[ResourcePack] Error: no es pot obrir " << pack_file << '\n'; + return false; + } + + // Llegir capçalera + char magic[4]; + file.read(magic, 4); + if (std::string(magic, 4) != MAGIC_HEADER) { + std::cerr << "[ResourcePack] Error: capçalera invàlida (esperava " << MAGIC_HEADER + << ")\n"; + return false; + } + + uint32_t version; + file.read(reinterpret_cast(&version), sizeof(version)); + if (version != VERSION) { + std::cerr << "[ResourcePack] Error: versión incompatible (esperava " << VERSION + << ", trobat " << version << ")\n"; + return false; + } + + // Llegir nombre de recursos + uint32_t resource_count; + file.read(reinterpret_cast(&resource_count), sizeof(resource_count)); + + // Llegir metadades de recursos + resources_.clear(); + for (uint32_t i = 0; i < resource_count; ++i) { + // Nom del file + uint32_t name_len; + file.read(reinterpret_cast(&name_len), sizeof(name_len)); + + std::string filename(name_len, '\0'); + file.read(filename.data(), name_len); + + // Offset, mida, checksum + ResourceEntry entry; + entry.filename = filename; + file.read(reinterpret_cast(&entry.offset), sizeof(entry.offset)); + file.read(reinterpret_cast(&entry.size), sizeof(entry.size)); + file.read(reinterpret_cast(&entry.checksum), sizeof(entry.checksum)); + + resources_[filename] = entry; + } + + // Llegir dades encriptades + uint64_t data_size; + file.read(reinterpret_cast(&data_size), sizeof(data_size)); + + data_.resize(data_size); + file.read(reinterpret_cast(data_.data()), data_size); + + // Desencriptar + decryptData(data_, DEFAULT_ENCRYPT_KEY); + + return true; } - uint32_t version; - file.read(reinterpret_cast(&version), sizeof(version)); - if (version != VERSION) { - std::cerr << "[ResourcePack] Error: versión incompatible (esperava " << VERSION - << ", trobat " << version << ")\n"; - return false; - } + // Obtenir un recurs del paquet + auto Pack::getResource(const std::string& filename) -> std::vector { + auto it = resources_.find(filename); + if (it == resources_.end()) { + std::cerr << "[ResourcePack] Error: recurs no trobat: " << filename << '\n'; + return {}; + } - // Llegir nombre de recursos - uint32_t resource_count; - file.read(reinterpret_cast(&resource_count), sizeof(resource_count)); + const auto& entry = it->second; - // Llegir metadades de recursos - resources_.clear(); - for (uint32_t i = 0; i < resource_count; ++i) { - // Nom del file - uint32_t name_len; - file.read(reinterpret_cast(&name_len), sizeof(name_len)); - - std::string filename(name_len, '\0'); - file.read(filename.data(), name_len); - - // Offset, mida, checksum - ResourceEntry entry; - entry.filename = filename; - file.read(reinterpret_cast(&entry.offset), sizeof(entry.offset)); - file.read(reinterpret_cast(&entry.size), sizeof(entry.size)); - file.read(reinterpret_cast(&entry.checksum), sizeof(entry.checksum)); - - resources_[filename] = entry; - } - - // Llegir dades encriptades - uint64_t data_size; - file.read(reinterpret_cast(&data_size), sizeof(data_size)); - - data_.resize(data_size); - file.read(reinterpret_cast(data_.data()), data_size); - - // Desencriptar - decryptData(data_, DEFAULT_ENCRYPT_KEY); - - std::cout << "[ResourcePack] Carregat: " << pack_file << " (" << resources_.size() - << " recursos)\n"; - - return true; -} - -// Obtenir un recurs del paquet -auto Pack::getResource(const std::string& filename) -> std::vector { - auto it = resources_.find(filename); - if (it == resources_.end()) { - std::cerr << "[ResourcePack] Error: recurs no trobat: " << filename << '\n'; - return {}; - } - - const auto& entry = it->second; - - // Extreure dades - if (entry.offset + entry.size > data_.size()) { - std::cerr << "[ResourcePack] Error: offset/mida invàlid per " << filename << '\n'; - return {}; - } - - std::vector resource_data(data_.begin() + entry.offset, - data_.begin() + entry.offset + entry.size); - - // Verificar checksum - uint32_t computed_checksum = calculateChecksum(resource_data); - if (computed_checksum != entry.checksum) { - std::cerr << "[ResourcePack] ADVERTÈNCIA: checksum invàlid per " << filename - << " (esperat " << entry.checksum << ", calculat " << computed_checksum - << ")\n"; - // No falla, pero adverteix - } - - return resource_data; -} - -// Comprovar si existeix un recurs -auto Pack::hasResource(const std::string& filename) const -> bool { - return resources_.contains(filename); -} - -// Obtenir list de todos los recursos -auto Pack::getResourceList() const -> std::vector { - std::vector list; - list.reserve(resources_.size()); - - for (const auto& [name, entry] : resources_) { - list.push_back(name); - } - - std::ranges::sort(list); - return list; -} - -// Validar integritat del paquet -auto Pack::validatePack() const -> bool { - bool valid = true; - - for (const auto& [name, entry] : resources_) { - // Verificar offset i mida + // Extreure dades if (entry.offset + entry.size > data_.size()) { - std::cerr << "[ResourcePack] Error de validació: " << name - << " té offset/mida invàlid\n"; - valid = false; - continue; + std::cerr << "[ResourcePack] Error: offset/mida invàlid per " << filename << '\n'; + return {}; } - // Extreure i verificar checksum std::vector resource_data(data_.begin() + entry.offset, data_.begin() + entry.offset + entry.size); + // Verificar checksum uint32_t computed_checksum = calculateChecksum(resource_data); if (computed_checksum != entry.checksum) { - std::cerr << "[ResourcePack] Error de validació: " << name - << " té checksum invàlid\n"; - valid = false; + std::cerr << "[ResourcePack] ADVERTÈNCIA: checksum invàlid per " << filename + << " (esperat " << entry.checksum << ", calculat " << computed_checksum + << ")\n"; + // No falla, pero adverteix } + + return resource_data; } - if (valid) { - std::cout << "[ResourcePack] Validació OK (" << resources_.size() << " recursos)\n"; + // Comprovar si existeix un recurs + auto Pack::hasResource(const std::string& filename) const -> bool { + return resources_.contains(filename); } - return valid; -} + // Obtenir list de todos los recursos + auto Pack::getResourceList() const -> std::vector { + std::vector list; + list.reserve(resources_.size()); + + for (const auto& [name, entry] : resources_) { + list.push_back(name); + } + + std::ranges::sort(list); + return list; + } + + // Validar integritat del paquet + auto Pack::validatePack() const -> bool { + bool valid = true; + + for (const auto& [name, entry] : resources_) { + // Verificar offset i mida + if (entry.offset + entry.size > data_.size()) { + std::cerr << "[ResourcePack] Error de validació: " << name + << " té offset/mida invàlid\n"; + valid = false; + continue; + } + + // Extreure i verificar checksum + std::vector resource_data(data_.begin() + entry.offset, + data_.begin() + entry.offset + entry.size); + + uint32_t computed_checksum = calculateChecksum(resource_data); + if (computed_checksum != entry.checksum) { + std::cerr << "[ResourcePack] Error de validació: " << name + << " té checksum invàlid\n"; + valid = false; + } + } + + return valid; + } } // namespace Resource diff --git a/tools/pack_resources/pack_resources.cpp b/tools/pack_resources/pack_resources.cpp index 8051ea9..7f425c8 100644 --- a/tools/pack_resources/pack_resources.cpp +++ b/tools/pack_resources/pack_resources.cpp @@ -1,92 +1,99 @@ // pack_resources.cpp - Utilitat per crear paquets de recursos // © 2026 JailDesigner -#include "../../source/core/resources/resource_pack.hpp" - #include #include +#include -void print_usage(const char* program_name) { - std::cout << "Ús: " << program_name << " [opcions] [directori_entrada] [fitxer_sortida]\n"; - std::cout << "\nOpcions:\n"; - std::cout << " --list Llistar contingut d'un paquet\n"; - std::cout << "\nExemples:\n"; - std::cout << " " << program_name << " data resources.pack\n"; - std::cout << " " << program_name << " --list resources.pack\n"; - std::cout << "\nSi no s'especifiquen arguments, empaqueta 'data/' a 'resources.pack'\n"; -} +#include "core/resources/resource_pack.hpp" +#include "project.h" + +namespace { + + void showHelp() { + std::cout << Project::LONG_NAME << " - Resource Packer\n"; + std::cout << "==============================================\n"; + std::cout << "Usage: pack_resources [options] [input_dir] [output_file]\n\n"; + std::cout << "Options:\n"; + std::cout << " --help Show this help message\n"; + std::cout << " --list List contents of an existing pack file\n\n"; + std::cout << "Arguments:\n"; + std::cout << " input_dir Directory to pack (default: data)\n"; + std::cout << " output_file Pack file name (default: resources.pack)\n"; + } + + void listPackContents(const std::string& pack_file) { + Resource::Pack pack; + if (!pack.loadPack(pack_file)) { + std::cerr << "Error: cannot open pack file: " << pack_file << '\n'; + return; + } + auto resources = pack.getResourceList(); + std::cout << "Pack file: " << pack_file << '\n'; + std::cout << "Resources: " << resources.size() << '\n'; + for (const auto& r : resources) { std::cout << " " << r << '\n'; } + } + +} // namespace int main(int argc, char* argv[]) { - std::string input_dir = "data"; + std::string data_dir = "data"; std::string output_file = "resources.pack"; + bool list_mode = false; + bool data_dir_set = false; - // Processar arguments - if (argc == 2 && std::string(argv[1]) == "--help") { - print_usage(argv[0]); + for (int i = 1; i < argc; ++i) { + std::string arg = argv[i]; + if (arg == "--help" || arg == "-h") { + showHelp(); + return 0; + } + if (arg == "--list") { + list_mode = true; + if (i + 1 < argc) { output_file = argv[++i]; } + continue; + } + if (!arg.empty() && arg[0] != '-') { + if (!data_dir_set) { + data_dir = arg; + data_dir_set = true; + } else { + output_file = arg; + } + } + } + + if (list_mode) { + listPackContents(output_file); return 0; } - // Mode --list - if (argc == 3 && std::string(argv[1]) == "--list") { - Resource::Pack pack; - if (!pack.loadPack(argv[2])) { - std::cerr << "ERROR: No es pot carregar " << argv[2] << "\n"; - return 1; - } + std::cout << Project::LONG_NAME << " - Resource Packer\n"; + std::cout << "==============================================\n"; + std::cout << "Input directory: " << data_dir << '\n'; + std::cout << "Output file: " << output_file << '\n'; - std::cout << "Contingut de " << argv[2] << ":\n"; - auto resources = pack.getResourceList(); - std::cout << "Total: " << resources.size() << " recursos\n\n"; - - for (const auto& name : resources) { - std::cout << " " << name << "\n"; - } - - return 0; - } - - // Mode empaquetar - if (argc >= 3) { - input_dir = argv[1]; - output_file = argv[2]; - } - - // Verificar que existeix el directori - if (!std::filesystem::exists(input_dir)) { - std::cerr << "ERROR: Directori no trobat: " << input_dir << "\n"; + if (!std::filesystem::exists(data_dir)) { + std::cerr << "Error: input directory does not exist: " << data_dir << '\n'; return 1; } - if (!std::filesystem::is_directory(input_dir)) { - std::cerr << "ERROR: " << input_dir << " no és un directori\n"; - return 1; - } - - // Crear paquet - std::cout << "Creant paquet de recursos...\n"; - std::cout << " Entrada: " << input_dir << "\n"; - std::cout << " Sortida: " << output_file << "\n\n"; - Resource::Pack pack; - if (!pack.addDirectory(input_dir)) { - std::cerr << "ERROR: No s'ha pogut afegir el directori\n"; + std::cout << "Scanning and packing resources...\n"; + if (!pack.addDirectory(data_dir)) { + std::cerr << "Error: failed to add directory to pack\n"; return 1; } + std::cout << "Found " << pack.getResourceList().size() << " resources\n"; + std::cout << "Saving pack file...\n"; if (!pack.savePack(output_file)) { - std::cerr << "ERROR: No s'ha pogut guardar el paquet\n"; + std::cerr << "Error: failed to save pack file\n"; return 1; } - // Resum - auto resources = pack.getResourceList(); - std::cout << "\n"; - std::cout << "✓ Paquet creat amb èxit!\n"; - std::cout << " Recursos: " << resources.size() << "\n"; - - // Mostrar mida del fitxer - auto file_size = std::filesystem::file_size(output_file); - std::cout << " Mida: " << (file_size / 1024) << " KB\n"; - + auto file_size = std::filesystem::file_size(std::filesystem::path(output_file)); + std::cout << "Pack file created: " << output_file << " (" + << (static_cast(file_size) / 1024.0 / 1024.0) << " MB)\n"; return 0; }