Files
vibe3_physics/source/resource_pack.cpp
Sergio Valor a9d7b66e83 Refactorizar estilo del proyecto: .h → .hpp, #pragma once, includes desde raíz
Modernizar convenciones de código C++ aplicando las siguientes directivas:

## Cambios principales

**1. Renombrar headers (.h → .hpp)**
- 36 archivos renombrados a extensión .hpp (estándar C++)
- Mantenidos como .h: stb_image.h, stb_image_resize2.h (librerías C externas)

**2. Modernizar include guards (#ifndef → #pragma once)**
- resource_manager.hpp: #ifndef RESOURCE_MANAGER_H → #pragma once
- resource_pack.hpp: #ifndef RESOURCE_PACK_H → #pragma once
- spatial_grid.hpp: #ifndef SPATIAL_GRID_H → #pragma once

**3. Sistema de includes desde raíz del proyecto**
- CMakeLists.txt: añadido include_directories(${CMAKE_SOURCE_DIR}/source)
- Eliminadas rutas relativas (../) en todos los includes
- Includes ahora usan rutas absolutas desde source/

**Antes:**
```cpp
#include "../defines.h"
#include "../text/textrenderer.h"
```

**Ahora:**
```cpp
#include "defines.hpp"
#include "text/textrenderer.hpp"
```

## Archivos afectados

- 1 archivo CMakeLists.txt modificado
- 36 archivos renombrados (.h → .hpp)
- 32 archivos .cpp actualizados (includes)
- 36 archivos .hpp actualizados (includes + guards)
- 1 archivo tools/ actualizado

**Total: 70 archivos modificados**

## Verificación

 Proyecto compila sin errores
 Todas las rutas de includes correctas
 Include guards modernizados
 Librerías externas C mantienen extensión .h

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-23 13:49:58 +02:00

273 lines
8.8 KiB
C++

#include "resource_pack.hpp"
#include <algorithm>
#include <cstring>
#include <filesystem>
#include <iostream>
namespace fs = std::filesystem;
// Clave XOR para ofuscación simple (puede cambiarse)
constexpr uint8_t XOR_KEY = 0x5A;
ResourcePack::ResourcePack() : isLoaded_(false) {}
ResourcePack::~ResourcePack() {
clear();
}
// ============================================================================
// EMPAQUETADO (herramienta pack_resources)
// ============================================================================
bool ResourcePack::addDirectory(const std::string& dirPath, const std::string& prefix) {
if (!fs::exists(dirPath) || !fs::is_directory(dirPath)) {
std::cerr << "Error: Directorio no existe: " << dirPath << std::endl;
return false;
}
for (const auto& entry : fs::recursive_directory_iterator(dirPath)) {
if (entry.is_regular_file()) {
// Construir ruta relativa desde data/ (ej: "data/ball.png" → "ball.png")
std::string relativePath = fs::relative(entry.path(), dirPath).string();
std::string fullPath = prefix.empty() ? relativePath : prefix + "/" + relativePath;
fullPath = normalizePath(fullPath);
// Leer archivo completo
std::ifstream file(entry.path(), std::ios::binary);
if (!file) {
std::cerr << "Error: No se pudo abrir: " << entry.path() << std::endl;
continue;
}
file.seekg(0, std::ios::end);
size_t fileSize = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<unsigned char> buffer(fileSize);
file.read(reinterpret_cast<char*>(buffer.data()), fileSize);
file.close();
// Crear entrada de recurso
ResourceEntry resource;
resource.path = fullPath;
resource.offset = 0; // Se calculará al guardar
resource.size = static_cast<uint32_t>(fileSize);
resource.checksum = calculateChecksum(buffer.data(), fileSize);
resources_[fullPath] = resource;
std::cout << " Añadido: " << fullPath << " (" << fileSize << " bytes)" << std::endl;
}
}
return !resources_.empty();
}
bool ResourcePack::savePack(const std::string& packFilePath) {
std::ofstream packFile(packFilePath, std::ios::binary);
if (!packFile) {
std::cerr << "Error: No se pudo crear pack: " << packFilePath << std::endl;
return false;
}
// 1. Escribir header
PackHeader header;
std::memcpy(header.magic, "VBE3", 4);
header.version = 1;
header.fileCount = static_cast<uint32_t>(resources_.size());
packFile.write(reinterpret_cast<const char*>(&header), sizeof(PackHeader));
// 2. Calcular offsets (después del header + índice)
uint32_t currentOffset = sizeof(PackHeader);
// Calcular tamaño del índice (cada entrada: uint32_t pathLen + path + 3*uint32_t)
for (const auto& [path, entry] : resources_) {
currentOffset += sizeof(uint32_t); // pathLen
currentOffset += static_cast<uint32_t>(path.size()); // path
currentOffset += sizeof(uint32_t) * 3; // offset, size, checksum
}
// 3. Escribir índice
for (auto& [path, entry] : resources_) {
entry.offset = currentOffset;
uint32_t pathLen = static_cast<uint32_t>(path.size());
packFile.write(reinterpret_cast<const char*>(&pathLen), sizeof(uint32_t));
packFile.write(path.c_str(), pathLen);
packFile.write(reinterpret_cast<const char*>(&entry.offset), sizeof(uint32_t));
packFile.write(reinterpret_cast<const char*>(&entry.size), sizeof(uint32_t));
packFile.write(reinterpret_cast<const char*>(&entry.checksum), sizeof(uint32_t));
currentOffset += entry.size;
}
// 4. Escribir datos de archivos (sin encriptar en pack, se encripta al cargar)
for (const auto& [path, entry] : resources_) {
// Encontrar archivo original en disco
fs::path originalPath = fs::current_path() / "data" / path;
std::ifstream file(originalPath, std::ios::binary);
if (!file) {
std::cerr << "Error: No se pudo re-leer: " << originalPath << std::endl;
continue;
}
std::vector<unsigned char> buffer(entry.size);
file.read(reinterpret_cast<char*>(buffer.data()), entry.size);
file.close();
packFile.write(reinterpret_cast<const char*>(buffer.data()), entry.size);
}
packFile.close();
return true;
}
// ============================================================================
// DESEMPAQUETADO (juego)
// ============================================================================
bool ResourcePack::loadPack(const std::string& packFilePath) {
clear();
packFile_.open(packFilePath, std::ios::binary);
if (!packFile_) {
return false;
}
// 1. Leer header
PackHeader header;
packFile_.read(reinterpret_cast<char*>(&header), sizeof(PackHeader));
if (std::memcmp(header.magic, "VBE3", 4) != 0) {
std::cerr << "Error: Pack inválido (magic incorrecto)" << std::endl;
packFile_.close();
return false;
}
if (header.version != 1) {
std::cerr << "Error: Versión de pack no soportada: " << header.version << std::endl;
packFile_.close();
return false;
}
// 2. Leer índice
for (uint32_t i = 0; i < header.fileCount; i++) {
ResourceEntry entry;
uint32_t pathLen;
packFile_.read(reinterpret_cast<char*>(&pathLen), sizeof(uint32_t));
std::vector<char> pathBuffer(pathLen + 1, '\0');
packFile_.read(pathBuffer.data(), pathLen);
entry.path = std::string(pathBuffer.data());
packFile_.read(reinterpret_cast<char*>(&entry.offset), sizeof(uint32_t));
packFile_.read(reinterpret_cast<char*>(&entry.size), sizeof(uint32_t));
packFile_.read(reinterpret_cast<char*>(&entry.checksum), sizeof(uint32_t));
resources_[entry.path] = entry;
}
isLoaded_ = true;
return true;
}
ResourcePack::ResourceData ResourcePack::loadResource(const std::string& resourcePath) {
ResourceData result = {nullptr, 0};
if (!isLoaded_) {
return result;
}
std::string normalizedPath = normalizePath(resourcePath);
auto it = resources_.find(normalizedPath);
if (it == resources_.end()) {
return result;
}
const ResourceEntry& entry = it->second;
// Leer datos desde el pack
packFile_.seekg(entry.offset);
result.data = new unsigned char[entry.size];
result.size = entry.size;
packFile_.read(reinterpret_cast<char*>(result.data), entry.size);
// Verificar checksum
uint32_t checksum = calculateChecksum(result.data, entry.size);
if (checksum != entry.checksum) {
std::cerr << "Warning: Checksum incorrecto para: " << resourcePath << std::endl;
}
return result;
}
// ============================================================================
// UTILIDADES
// ============================================================================
std::vector<std::string> ResourcePack::getResourceList() const {
std::vector<std::string> list;
for (const auto& [path, entry] : resources_) {
list.push_back(path);
}
return list;
}
size_t ResourcePack::getResourceCount() const {
return resources_.size();
}
void ResourcePack::clear() {
resources_.clear();
if (packFile_.is_open()) {
packFile_.close();
}
isLoaded_ = false;
}
// ============================================================================
// FUNCIONES AUXILIARES
// ============================================================================
void ResourcePack::encryptData(unsigned char* data, size_t size) {
for (size_t i = 0; i < size; i++) {
data[i] ^= XOR_KEY;
}
}
void ResourcePack::decryptData(unsigned char* data, size_t size) {
// XOR es simétrico
encryptData(data, size);
}
uint32_t ResourcePack::calculateChecksum(const unsigned char* data, size_t size) {
uint32_t checksum = 0;
for (size_t i = 0; i < size; i++) {
checksum ^= static_cast<uint32_t>(data[i]);
checksum = (checksum << 1) | (checksum >> 31); // Rotate left
}
return checksum;
}
std::string ResourcePack::normalizePath(const std::string& path) {
std::string normalized = path;
// Reemplazar \ por /
std::replace(normalized.begin(), normalized.end(), '\\', '/');
// Buscar "data/" en cualquier parte del path y extraer lo que viene después
size_t data_pos = normalized.find("data/");
if (data_pos != std::string::npos) {
normalized = normalized.substr(data_pos + 5); // +5 para saltar "data/"
}
// Eliminar ./ del inicio si quedó
if (normalized.substr(0, 2) == "./") {
normalized = normalized.substr(2);
}
return normalized;
}