feat(pack): alinear sortida i build amb projectes germans

This commit is contained in:
2026-05-25 07:50:29 +02:00
parent 99b18d208d
commit 2d073b6055
3 changed files with 314 additions and 317 deletions
+4 -1
View File
@@ -110,7 +110,10 @@ add_executable(pack_resources EXCLUDE_FROM_ALL
tools/pack_resources/pack_resources.cpp tools/pack_resources/pack_resources.cpp
source/core/resources/resource_pack.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) target_compile_options(pack_resources PRIVATE -Wall -Wextra -Wpedantic)
# --- REGENERACIÓ AUTOMÀTICA DE build/resources.pack --- # --- REGENERACIÓ AUTOMÀTICA DE build/resources.pack ---
+238 -251
View File
@@ -10,300 +10,287 @@
namespace Resource { namespace Resource {
// Calcular checksum CRC32 simplificat // Calcular checksum CRC32 simplificat
auto Pack::calculateChecksum(const std::vector<uint8_t>& data) -> uint32_t { auto Pack::calculateChecksum(const std::vector<uint8_t>& data) -> uint32_t {
uint32_t checksum = 0x12345678; uint32_t checksum = 0x12345678;
for (unsigned char byte : data) { for (unsigned char byte : data) {
checksum = ((checksum << 5) + checksum) + byte; checksum = ((checksum << 5) + checksum) + byte;
} }
return checksum; return checksum;
}
// Encriptació XOR (simètrica)
void Pack::encryptData(std::vector<uint8_t>& 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<uint8_t>& 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<uint8_t> {
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(); // Encriptació XOR (simètrica)
file.seekg(0, std::ios::beg); void Pack::encryptData(std::vector<uint8_t>& data, const std::string& key) {
if (key.empty()) {
std::vector<uint8_t> data(file_size); return;
if (!file.read(reinterpret_cast<char*>(data.data()), file_size)) { }
std::cerr << "[ResourcePack] Error: no es pot llegir " << filepath << '\n'; for (size_t i = 0; i < data.size(); ++i) {
return {}; data[i] ^= key[i % key.length()];
}
} }
return data; void Pack::decryptData(std::vector<uint8_t>& data, const std::string& key) {
} // XOR es simètric
encryptData(data, key);
// 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{ // Llegir file complet a memòria
.filename = pack_name, auto Pack::readFile(const std::string& filepath) -> std::vector<uint8_t> {
.offset = data_.size(), std::ifstream file(filepath, std::ios::binary | std::ios::ate);
.size = file_data.size(), if (!file) {
.checksum = calculateChecksum(file_data)}; std::cerr << "[ResourcePack] Error: no es pot obrir " << filepath << '\n';
return {};
// 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::streamsize file_size = file.tellg();
std::string relative_path = entry.path().lexically_relative(dir_path).string(); file.seekg(0, std::ios::beg);
// Convertir barres invertides a normals (Windows) std::vector<uint8_t> data(file_size);
std::ranges::replace(relative_path, '\\', '/'); if (!file.read(reinterpret_cast<char*>(data.data()), file_size)) {
std::cerr << "[ResourcePack] Error: no es pot llegir " << filepath << '\n';
// Saltar archivos de desenvolupament return {};
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; return data;
addFile(full_path, pack_name);
} }
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 ResourceEntry entry{
auto Pack::savePack(const std::string& pack_file) -> bool { .filename = pack_name,
std::ofstream file(pack_file, std::ios::binary); .offset = data_.size(),
if (!file) { .size = file_data.size(),
std::cerr << "[ResourcePack] Error: no es pot crear " << pack_file << '\n'; .checksum = calculateChecksum(file_data)};
return false;
// 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 // Añadir todos los archivos de un directori recursivament
file.write(MAGIC_HEADER, 4); auto Pack::addDirectory(const std::string& dir_path,
file.write(reinterpret_cast<const char*>(&VERSION), sizeof(VERSION)); const std::string& base_path) -> bool {
namespace fs = std::filesystem;
// Escriure nombre de recursos if (!fs::exists(dir_path) || !fs::is_directory(dir_path)) {
auto resource_count = static_cast<uint32_t>(resources_.size()); std::cerr << "[ResourcePack] Error: directori no trobat: " << dir_path << '\n';
file.write(reinterpret_cast<const char*>(&resource_count), sizeof(resource_count)); return false;
}
// Escriure metadades de recursos std::string current_base = base_path.empty() ? "" : base_path + "/";
for (const auto& [name, entry] : resources_) {
// Nom del file
auto name_len = static_cast<uint32_t>(entry.filename.length());
file.write(reinterpret_cast<const char*>(&name_len), sizeof(name_len));
file.write(entry.filename.c_str(), name_len);
// Offset, mida, checksum for (const auto& entry : fs::recursive_directory_iterator(dir_path)) {
file.write(reinterpret_cast<const char*>(&entry.offset), sizeof(entry.offset)); if (!entry.is_regular_file()) {
file.write(reinterpret_cast<const char*>(&entry.size), sizeof(entry.size)); continue;
file.write(reinterpret_cast<const char*>(&entry.checksum), sizeof(entry.checksum)); }
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 // Guardar paquet a disc
std::vector<uint8_t> encrypted_data = data_; auto Pack::savePack(const std::string& pack_file) -> bool {
encryptData(encrypted_data, DEFAULT_ENCRYPT_KEY); 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 // Escriure capçalera
auto data_size = static_cast<uint64_t>(encrypted_data.size()); file.write(MAGIC_HEADER, 4);
file.write(reinterpret_cast<const char*>(&data_size), sizeof(data_size)); file.write(reinterpret_cast<const char*>(&VERSION), sizeof(VERSION));
file.write(reinterpret_cast<const char*>(encrypted_data.data()), encrypted_data.size());
std::cout << "[ResourcePack] Guardat: " << pack_file << " (" << resources_.size() // Escriure nombre de recursos
<< " recursos, " << data_size << " bytes)\n"; auto resource_count = static_cast<uint32_t>(resources_.size());
file.write(reinterpret_cast<const char*>(&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<uint32_t>(entry.filename.length());
file.write(reinterpret_cast<const char*>(&name_len), sizeof(name_len));
file.write(entry.filename.c_str(), name_len);
// Carregar paquet desde disc // Offset, mida, checksum
auto Pack::loadPack(const std::string& pack_file) -> bool { file.write(reinterpret_cast<const char*>(&entry.offset), sizeof(entry.offset));
std::ifstream file(pack_file, std::ios::binary); file.write(reinterpret_cast<const char*>(&entry.size), sizeof(entry.size));
if (!file) { file.write(reinterpret_cast<const char*>(&entry.checksum), sizeof(entry.checksum));
std::cerr << "[ResourcePack] Error: no es pot obrir " << pack_file << '\n'; }
return false;
// Encriptar dades
std::vector<uint8_t> encrypted_data = data_;
encryptData(encrypted_data, DEFAULT_ENCRYPT_KEY);
// Escriure mida de dades y dades encriptades
auto data_size = static_cast<uint64_t>(encrypted_data.size());
file.write(reinterpret_cast<const char*>(&data_size), sizeof(data_size));
file.write(reinterpret_cast<const char*>(encrypted_data.data()), encrypted_data.size());
return true;
} }
// Llegir capçalera // Carregar paquet desde disc
char magic[4]; auto Pack::loadPack(const std::string& pack_file) -> bool {
file.read(magic, 4); std::ifstream file(pack_file, std::ios::binary);
if (std::string(magic, 4) != MAGIC_HEADER) { if (!file) {
std::cerr << "[ResourcePack] Error: capçalera invàlida (esperava " << MAGIC_HEADER std::cerr << "[ResourcePack] Error: no es pot obrir " << pack_file << '\n';
<< ")\n"; return false;
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<char*>(&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<char*>(&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<char*>(&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<char*>(&entry.offset), sizeof(entry.offset));
file.read(reinterpret_cast<char*>(&entry.size), sizeof(entry.size));
file.read(reinterpret_cast<char*>(&entry.checksum), sizeof(entry.checksum));
resources_[filename] = entry;
}
// Llegir dades encriptades
uint64_t data_size;
file.read(reinterpret_cast<char*>(&data_size), sizeof(data_size));
data_.resize(data_size);
file.read(reinterpret_cast<char*>(data_.data()), data_size);
// Desencriptar
decryptData(data_, DEFAULT_ENCRYPT_KEY);
return true;
} }
uint32_t version; // Obtenir un recurs del paquet
file.read(reinterpret_cast<char*>(&version), sizeof(version)); auto Pack::getResource(const std::string& filename) -> std::vector<uint8_t> {
if (version != VERSION) { auto it = resources_.find(filename);
std::cerr << "[ResourcePack] Error: versión incompatible (esperava " << VERSION if (it == resources_.end()) {
<< ", trobat " << version << ")\n"; std::cerr << "[ResourcePack] Error: recurs no trobat: " << filename << '\n';
return false; return {};
} }
// Llegir nombre de recursos const auto& entry = it->second;
uint32_t resource_count;
file.read(reinterpret_cast<char*>(&resource_count), sizeof(resource_count));
// Llegir metadades de recursos // Extreure dades
resources_.clear();
for (uint32_t i = 0; i < resource_count; ++i) {
// Nom del file
uint32_t name_len;
file.read(reinterpret_cast<char*>(&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<char*>(&entry.offset), sizeof(entry.offset));
file.read(reinterpret_cast<char*>(&entry.size), sizeof(entry.size));
file.read(reinterpret_cast<char*>(&entry.checksum), sizeof(entry.checksum));
resources_[filename] = entry;
}
// Llegir dades encriptades
uint64_t data_size;
file.read(reinterpret_cast<char*>(&data_size), sizeof(data_size));
data_.resize(data_size);
file.read(reinterpret_cast<char*>(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<uint8_t> {
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<uint8_t> 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::string> {
std::vector<std::string> 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()) { if (entry.offset + entry.size > data_.size()) {
std::cerr << "[ResourcePack] Error de validació: " << name std::cerr << "[ResourcePack] Error: offset/mida invàlid per " << filename << '\n';
<< " té offset/mida invàlid\n"; return {};
valid = false;
continue;
} }
// Extreure i verificar checksum
std::vector<uint8_t> resource_data(data_.begin() + entry.offset, std::vector<uint8_t> resource_data(data_.begin() + entry.offset,
data_.begin() + entry.offset + entry.size); data_.begin() + entry.offset + entry.size);
// Verificar checksum
uint32_t computed_checksum = calculateChecksum(resource_data); uint32_t computed_checksum = calculateChecksum(resource_data);
if (computed_checksum != entry.checksum) { if (computed_checksum != entry.checksum) {
std::cerr << "[ResourcePack] Error de validació: " << name std::cerr << "[ResourcePack] ADVERTÈNCIA: checksum invàlid per " << filename
<< " té checksum invàlid\n"; << " (esperat " << entry.checksum << ", calculat " << computed_checksum
valid = false; << ")\n";
// No falla, pero adverteix
} }
return resource_data;
} }
if (valid) { // Comprovar si existeix un recurs
std::cout << "[ResourcePack] Validació OK (" << resources_.size() << " recursos)\n"; 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::string> {
std::vector<std::string> 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<uint8_t> 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 } // namespace Resource
+72 -65
View File
@@ -1,92 +1,99 @@
// pack_resources.cpp - Utilitat per crear paquets de recursos // pack_resources.cpp - Utilitat per crear paquets de recursos
// © 2026 JailDesigner // © 2026 JailDesigner
#include "../../source/core/resources/resource_pack.hpp"
#include <filesystem> #include <filesystem>
#include <iostream> #include <iostream>
#include <string>
void print_usage(const char* program_name) { #include "core/resources/resource_pack.hpp"
std::cout << "Ús: " << program_name << " [opcions] [directori_entrada] [fitxer_sortida]\n"; #include "project.h"
std::cout << "\nOpcions:\n";
std::cout << " --list <fitxer> Llistar contingut d'un paquet\n"; namespace {
std::cout << "\nExemples:\n";
std::cout << " " << program_name << " data resources.pack\n"; void showHelp() {
std::cout << " " << program_name << " --list resources.pack\n"; std::cout << Project::LONG_NAME << " - Resource Packer\n";
std::cout << "\nSi no s'especifiquen arguments, empaqueta 'data/' a 'resources.pack'\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[]) { int main(int argc, char* argv[]) {
std::string input_dir = "data"; std::string data_dir = "data";
std::string output_file = "resources.pack"; std::string output_file = "resources.pack";
bool list_mode = false;
bool data_dir_set = false;
// Processar arguments for (int i = 1; i < argc; ++i) {
if (argc == 2 && std::string(argv[1]) == "--help") { std::string arg = argv[i];
print_usage(argv[0]); 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; return 0;
} }
// Mode --list std::cout << Project::LONG_NAME << " - Resource Packer\n";
if (argc == 3 && std::string(argv[1]) == "--list") { std::cout << "==============================================\n";
Resource::Pack pack; std::cout << "Input directory: " << data_dir << '\n';
if (!pack.loadPack(argv[2])) { std::cout << "Output file: " << output_file << '\n';
std::cerr << "ERROR: No es pot carregar " << argv[2] << "\n";
return 1;
}
std::cout << "Contingut de " << argv[2] << ":\n"; if (!std::filesystem::exists(data_dir)) {
auto resources = pack.getResourceList(); std::cerr << "Error: input directory does not exist: " << data_dir << '\n';
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";
return 1; 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; Resource::Pack pack;
if (!pack.addDirectory(input_dir)) { std::cout << "Scanning and packing resources...\n";
std::cerr << "ERROR: No s'ha pogut afegir el directori\n"; if (!pack.addDirectory(data_dir)) {
std::cerr << "Error: failed to add directory to pack\n";
return 1; return 1;
} }
std::cout << "Found " << pack.getResourceList().size() << " resources\n";
std::cout << "Saving pack file...\n";
if (!pack.savePack(output_file)) { 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; return 1;
} }
// Resum auto file_size = std::filesystem::file_size(std::filesystem::path(output_file));
auto resources = pack.getResourceList(); std::cout << "Pack file created: " << output_file << " ("
std::cout << "\n"; << (static_cast<double>(file_size) / 1024.0 / 1024.0) << " MB)\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";
return 0; return 0;
} }