// resource_pack.cpp - Implementació del sistema de empaquetament // © 2026 JailDesigner #include "resource_pack.hpp" #include #include #include #include namespace Resource { // Calcular checksum CRC32 simplificat auto Pack::calculateChecksum(const std::vector& data) const -> 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 {}; } 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 {}; } 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; } 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; } 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) { std::cout << "[ResourcePack] Saltant: " << relative_path << '\n'; continue; } std::string pack_name = current_base + relative_path; addFile(full_path, pack_name); } return true; } // 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 capçalera file.write(MAGIC_HEADER, 4); file.write(reinterpret_cast(&VERSION), sizeof(VERSION)); // Escriure nombre de recursos auto resource_count = static_cast(resources_.size()); file.write(reinterpret_cast(&resource_count), sizeof(resource_count)); // 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); // 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()); std::cout << "[ResourcePack] Guardat: " << pack_file << " (" << resources_.size() << " recursos, " << data_size << " bytes)\n"; return true; } // 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); 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 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; } } if (valid) { std::cout << "[ResourcePack] Validació OK (" << resources_.size() << " recursos)\n"; } return valid; } } // namespace Resource