chore(release): v0.21.0 — empaquetat de recursos (AE26) i neteja de fonts
- Nou format de pack AE26 v1 (XOR + djb2), eina `pack_resources`, regeneració automàtica de `build/resources.pack` a cada build. - `Resource::Helper` amb init/shutdown, fallback a disc només en Debug (apunta al source tree); Release exigeix pack present. - Callers de YAML migrats a `loadFile`+deserialize des de buffer. - Arrel neta: el pack viu a `build/`, eliminat `sync_data`. - Placeholder font -> `ari-w9500-condensed-display`; esborrades variants bold/condensed no usades a `data/fonts/`.
This commit is contained in:
1 parent
aeed0e01c2
commit
4da05b3d76
24 files changed
+574
-625
No files matched your search
@@ -13,17 +13,18 @@ auto KeyConfig::get() -> KeyConfig& {
|
||||
}
|
||||
|
||||
void KeyConfig::load(const std::string& file_path) {
|
||||
const auto FULL = Resource::Helper::resolve(file_path);
|
||||
std::ifstream file(FULL);
|
||||
if (!file.is_open()) {
|
||||
std::cerr << "KeyConfig: no s'ha pogut obrir " << FULL << '\n';
|
||||
auto bytes = Resource::Helper::loadFile(file_path);
|
||||
if (bytes.empty()) {
|
||||
std::cerr << "KeyConfig: no s'ha pogut obrir " << file_path << '\n';
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
auto yaml = fkyaml::node::deserialize(file);
|
||||
const auto* begin = reinterpret_cast<const char*>(bytes.data());
|
||||
const auto* end = begin + bytes.size();
|
||||
auto yaml = fkyaml::node::deserialize(begin, end);
|
||||
if (!yaml.contains("keys") || !yaml["keys"].is_sequence()) {
|
||||
std::cerr << "KeyConfig: falta la secció 'keys' a " << FULL << '\n';
|
||||
std::cerr << "KeyConfig: falta la secció 'keys' a " << file_path << '\n';
|
||||
return;
|
||||
}
|
||||
bindings_.clear();
|
||||
@@ -38,9 +39,9 @@ void KeyConfig::load(const std::string& file_path) {
|
||||
}
|
||||
bindings_[ID] = SC;
|
||||
}
|
||||
std::cout << "KeyConfig: " << bindings_.size() << " bindings des de " << FULL << '\n';
|
||||
std::cout << "KeyConfig: " << bindings_.size() << " bindings des de " << file_path << '\n';
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "KeyConfig: error parsejant " << FULL << ": " << e.what() << '\n';
|
||||
std::cerr << "KeyConfig: error parsejant " << file_path << ": " << e.what() << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,20 +34,21 @@ auto Locale::get() -> Locale& {
|
||||
}
|
||||
|
||||
void Locale::load(const std::string& file_path) {
|
||||
const auto FULL = Resource::Helper::resolve(file_path);
|
||||
std::ifstream file(FULL);
|
||||
if (!file.is_open()) {
|
||||
std::cerr << "Locale: no s'ha pogut obrir " << FULL << '\n';
|
||||
auto bytes = Resource::Helper::loadFile(file_path);
|
||||
if (bytes.empty()) {
|
||||
std::cerr << "Locale: no s'ha pogut obrir " << file_path << '\n';
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
auto yaml = fkyaml::node::deserialize(file);
|
||||
const auto* begin = reinterpret_cast<const char*>(bytes.data());
|
||||
const auto* end = begin + bytes.size();
|
||||
auto yaml = fkyaml::node::deserialize(begin, end);
|
||||
strings_.clear();
|
||||
flatten(yaml, "", strings_);
|
||||
std::cout << "Locale: " << strings_.size() << " traduccions des de " << FULL << '\n';
|
||||
std::cout << "Locale: " << strings_.size() << " traduccions des de " << file_path << '\n';
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "Locale: error parsejant " << FULL << ": " << e.what() << '\n';
|
||||
std::cerr << "Locale: error parsejant " << file_path << ": " << e.what() << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,15 +3,65 @@
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "core/resources/resource_pack.hpp"
|
||||
|
||||
namespace Resource::Helper {
|
||||
|
||||
namespace {
|
||||
|
||||
std::string g_data_root;
|
||||
Pack g_pack;
|
||||
bool g_pack_loaded = false;
|
||||
bool g_fallback_enabled = true;
|
||||
|
||||
auto readFromDisk(const std::string& filepath) -> std::vector<std::uint8_t> {
|
||||
const auto FULL = resolve(filepath);
|
||||
std::ifstream file(FULL, std::ios::binary | std::ios::ate);
|
||||
if (!file) {
|
||||
std::cerr << "Resource::Helper: cannot open " << FULL << '\n';
|
||||
return {};
|
||||
}
|
||||
std::streamsize size = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
std::vector<std::uint8_t> data(static_cast<std::size_t>(size));
|
||||
if (!file.read(reinterpret_cast<char*>(data.data()), size)) {
|
||||
std::cerr << "Resource::Helper: failed to read " << FULL << '\n';
|
||||
return {};
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
auto initializeResourceSystem(const std::string& pack_file, bool enable_fallback) -> bool {
|
||||
g_fallback_enabled = enable_fallback;
|
||||
g_pack_loaded = g_pack.loadPack(pack_file);
|
||||
|
||||
if (g_pack_loaded) {
|
||||
std::cout << "Resource::Helper: pack loaded (" << g_pack.getResourceCount()
|
||||
<< " entries) from " << pack_file << '\n';
|
||||
} else if (enable_fallback) {
|
||||
std::cout << "Resource::Helper: no pack at " << pack_file
|
||||
<< " — using filesystem fallback\n";
|
||||
} else {
|
||||
std::cerr << "Resource::Helper: FATAL — no pack at " << pack_file
|
||||
<< " and fallback disabled\n";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void shutdownResourceSystem() {
|
||||
g_pack.clear();
|
||||
g_pack_loaded = false;
|
||||
}
|
||||
|
||||
auto hasPack() -> bool {
|
||||
return g_pack_loaded;
|
||||
}
|
||||
|
||||
void setDataRoot(const std::string& absolute_path) {
|
||||
g_data_root = absolute_path;
|
||||
if (!g_data_root.empty() && g_data_root.back() != '/') {
|
||||
@@ -26,20 +76,17 @@ namespace Resource::Helper {
|
||||
}
|
||||
|
||||
auto loadFile(const std::string& filepath) -> std::vector<std::uint8_t> {
|
||||
const auto FULL = resolve(filepath);
|
||||
std::ifstream file(FULL, std::ios::binary | std::ios::ate);
|
||||
if (!file) {
|
||||
std::cerr << "Resource::Helper: cannot open " << FULL << '\n';
|
||||
return {};
|
||||
if (g_pack_loaded) {
|
||||
// El pack està indexat relatiu a `data/` (la carpeta que
|
||||
// s'empaqueta), però els callers passen "data/foo.yaml" amb
|
||||
// prefix. Fem el strip ací perquè no hagin de tenir-ho en compte.
|
||||
constexpr std::string_view PREFIX = "data/";
|
||||
std::string key = filepath;
|
||||
if (key.starts_with(PREFIX)) { key.erase(0, PREFIX.size()); }
|
||||
if (g_pack.hasResource(key)) { return g_pack.getResource(key); }
|
||||
}
|
||||
std::streamsize size = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
std::vector<std::uint8_t> data(static_cast<std::size_t>(size));
|
||||
if (!file.read(reinterpret_cast<char*>(data.data()), size)) {
|
||||
std::cerr << "Resource::Helper: failed to read " << FULL << '\n';
|
||||
return {};
|
||||
}
|
||||
return data;
|
||||
if (g_fallback_enabled) { return readFromDisk(filepath); }
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace Resource::Helper
|
||||
@@ -6,23 +6,33 @@
|
||||
|
||||
namespace Resource::Helper {
|
||||
|
||||
// Estableix el prefix absolut des d'on es resolen totes les rutes
|
||||
// relatives passades a `loadFile`/`resolve`. Pensat per a cridar-se una
|
||||
// sola vegada al primer pas del Director amb `SDL_GetBasePath()`: així el
|
||||
// binari troba `data/` sense dependre del CWD des d'on s'ha llançat (és
|
||||
// obligatori per a .app de macOS i útil a Linux per no haver d'estar
|
||||
// dins de `build/`).
|
||||
// Inicialitza el sistema de recursos. Carrega el pack des de `pack_file`
|
||||
// i decideix si `loadFile` pot caure a disc quan un recurs no és al pack.
|
||||
// En Release el pack és obligatori: passar `enable_fallback=false` fa
|
||||
// que `initializeResourceSystem` retorne false si no s'ha pogut carregar.
|
||||
auto initializeResourceSystem(const std::string& pack_file, bool enable_fallback) -> bool;
|
||||
|
||||
// Allibera el pack carregat a memòria.
|
||||
void shutdownResourceSystem();
|
||||
|
||||
// True si el sistema es va inicialitzar amb un pack vàlid.
|
||||
[[nodiscard]] auto hasPack() -> bool;
|
||||
|
||||
// Estableix el prefix des d'on es resolen les rutes quan el fallback
|
||||
// llig de disc. En Debug apunta al source tree (`${CMAKE_SOURCE_DIR}/`)
|
||||
// perquè editar YAML/GIF dins `data/` es vegi reflectit sense re-packejar.
|
||||
// En Release el fallback està desactivat i aquest camí no es fa servir.
|
||||
void setDataRoot(const std::string& absolute_path);
|
||||
|
||||
// Resol una ruta relativa contra el data root. Si la ruta ja és absoluta
|
||||
// (comença per '/') la retorna sense modificar — així els fitxers de
|
||||
// config (system_folder_ + "config.yaml") segueixen funcionant sense
|
||||
// canvis al call site.
|
||||
// Resol una ruta relativa contra el data root. Si ja és absoluta
|
||||
// (comença per '/') la retorna sense modificar. S'usa per a rutes que
|
||||
// NO passen pel pack (p.ex. fitxers de config dins system_folder_).
|
||||
[[nodiscard]] auto resolve(const std::string& filepath) -> std::string;
|
||||
|
||||
// Carga un fitxer del filesystem (a aquesta fase encara no hi ha pack).
|
||||
// Les rutes relatives es resolen via `resolve()`; les absolutes s'obrin
|
||||
// directament.
|
||||
// Carrega un recurs pel seu camí relatiu (p.ex. "fonts/hud.gif",
|
||||
// "input/keys.yaml"). Prova primer el pack; si no hi és i el fallback
|
||||
// està actiu, cau a disc (data root + ruta). Retorna vector buit si
|
||||
// no el troba.
|
||||
auto loadFile(const std::string& filepath) -> std::vector<std::uint8_t>;
|
||||
|
||||
} // namespace Resource::Helper
|
||||
@@ -0,0 +1,209 @@
|
||||
#include "core/resources/resource_pack.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
namespace Resource {
|
||||
|
||||
const std::string Pack::DEFAULT_ENCRYPT_KEY = "AEE_2026_PACK_KEY";
|
||||
|
||||
namespace {
|
||||
constexpr const char* MAGIC = "AE26";
|
||||
constexpr std::uint32_t VERSION = 1;
|
||||
} // namespace
|
||||
|
||||
auto Pack::calculateChecksum(const std::vector<std::uint8_t>& data) -> std::uint32_t {
|
||||
// djb2: seed 0x12345678. Compatible amb aee/projecte_2026 per si mai
|
||||
// reutilitzem eines de diagnòstic entre projectes.
|
||||
std::uint32_t checksum = 0x12345678;
|
||||
for (unsigned char b : data) {
|
||||
// cppcheck-suppress useStlAlgorithm
|
||||
checksum = ((checksum << 5) + checksum) + b;
|
||||
}
|
||||
return checksum;
|
||||
}
|
||||
|
||||
void Pack::xorInPlace(std::vector<std::uint8_t>& data, const std::string& key) {
|
||||
if (key.empty()) { return; }
|
||||
for (std::size_t i = 0; i < data.size(); ++i) {
|
||||
data[i] ^= static_cast<std::uint8_t>(key[i % key.length()]);
|
||||
}
|
||||
}
|
||||
|
||||
auto Pack::loadPack(const std::string& pack_file) -> bool {
|
||||
std::ifstream file(pack_file, std::ios::binary);
|
||||
if (!file) { return false; }
|
||||
|
||||
std::array<char, 4> header{};
|
||||
file.read(header.data(), 4);
|
||||
if (std::string(header.data(), 4) != MAGIC) {
|
||||
std::cerr << "Resource::Pack: invalid pack file format (bad magic): " << pack_file << '\n';
|
||||
return false;
|
||||
}
|
||||
|
||||
std::uint32_t version = 0;
|
||||
file.read(reinterpret_cast<char*>(&version), sizeof(version));
|
||||
if (version != VERSION) {
|
||||
std::cerr << "Resource::Pack: unsupported pack version: " << version << '\n';
|
||||
return false;
|
||||
}
|
||||
|
||||
std::uint32_t resource_count = 0;
|
||||
file.read(reinterpret_cast<char*>(&resource_count), sizeof(resource_count));
|
||||
|
||||
resources_.clear();
|
||||
resources_.reserve(resource_count);
|
||||
|
||||
for (std::uint32_t i = 0; i < resource_count; ++i) {
|
||||
std::uint32_t filename_length = 0;
|
||||
file.read(reinterpret_cast<char*>(&filename_length), sizeof(filename_length));
|
||||
|
||||
std::string filename(filename_length, '\0');
|
||||
file.read(filename.data(), filename_length);
|
||||
|
||||
PackEntry 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;
|
||||
}
|
||||
|
||||
std::uint64_t data_size = 0;
|
||||
file.read(reinterpret_cast<char*>(&data_size), sizeof(data_size));
|
||||
|
||||
data_.resize(data_size);
|
||||
file.read(reinterpret_cast<char*>(data_.data()), static_cast<std::streamsize>(data_size));
|
||||
|
||||
xorInPlace(data_, DEFAULT_ENCRYPT_KEY);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto Pack::savePack(const std::string& pack_file) const -> bool {
|
||||
std::ofstream file(pack_file, std::ios::binary);
|
||||
if (!file) {
|
||||
std::cerr << "Resource::Pack: could not create pack file: " << pack_file << '\n';
|
||||
return false;
|
||||
}
|
||||
|
||||
file.write(MAGIC, 4);
|
||||
|
||||
std::uint32_t version = VERSION;
|
||||
file.write(reinterpret_cast<const char*>(&version), sizeof(version));
|
||||
|
||||
auto resource_count = static_cast<std::uint32_t>(resources_.size());
|
||||
file.write(reinterpret_cast<const char*>(&resource_count), sizeof(resource_count));
|
||||
|
||||
for (const auto& [filename, entry] : resources_) {
|
||||
auto filename_length = static_cast<std::uint32_t>(filename.length());
|
||||
file.write(reinterpret_cast<const char*>(&filename_length), sizeof(filename_length));
|
||||
file.write(filename.c_str(), filename_length);
|
||||
file.write(reinterpret_cast<const char*>(&entry.offset), sizeof(entry.offset));
|
||||
file.write(reinterpret_cast<const char*>(&entry.size), sizeof(entry.size));
|
||||
file.write(reinterpret_cast<const char*>(&entry.checksum), sizeof(entry.checksum));
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t> encrypted = data_;
|
||||
xorInPlace(encrypted, DEFAULT_ENCRYPT_KEY);
|
||||
|
||||
std::uint64_t data_size = encrypted.size();
|
||||
file.write(reinterpret_cast<const char*>(&data_size), sizeof(data_size));
|
||||
file.write(reinterpret_cast<const char*>(encrypted.data()), static_cast<std::streamsize>(data_size));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto Pack::addFile(const std::string& filename, const std::string& filepath) -> bool {
|
||||
std::ifstream file(filepath, std::ios::binary | std::ios::ate);
|
||||
if (!file) {
|
||||
std::cerr << "Resource::Pack: could not open file: " << filepath << '\n';
|
||||
return false;
|
||||
}
|
||||
|
||||
std::streamsize file_size = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
std::vector<std::uint8_t> file_data(file_size);
|
||||
if (!file.read(reinterpret_cast<char*>(file_data.data()), file_size)) {
|
||||
std::cerr << "Resource::Pack: could not read file: " << filepath << '\n';
|
||||
return false;
|
||||
}
|
||||
|
||||
PackEntry entry;
|
||||
entry.filename = filename;
|
||||
entry.offset = data_.size();
|
||||
entry.size = file_data.size();
|
||||
entry.checksum = calculateChecksum(file_data);
|
||||
|
||||
data_.insert(data_.end(), file_data.begin(), file_data.end());
|
||||
resources_[filename] = entry;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto Pack::addDirectory(const std::string& directory) -> bool {
|
||||
if (!std::filesystem::exists(directory)) {
|
||||
std::cerr << "Resource::Pack: directory does not exist: " << directory << '\n';
|
||||
return false;
|
||||
}
|
||||
|
||||
// cppcheck-suppress useStlAlgorithm
|
||||
for (const auto& entry : std::filesystem::recursive_directory_iterator(directory)) {
|
||||
if (!entry.is_regular_file()) { continue; }
|
||||
|
||||
std::string filepath = entry.path().string();
|
||||
std::string filename = std::filesystem::relative(entry.path(), directory).string();
|
||||
std::ranges::replace(filename, '\\', '/');
|
||||
|
||||
if (!addFile(filename, filepath)) { return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
auto Pack::getResource(const std::string& filename) const -> std::vector<std::uint8_t> {
|
||||
auto it = resources_.find(filename);
|
||||
if (it == resources_.end()) { return {}; }
|
||||
|
||||
const PackEntry& entry = it->second;
|
||||
if (entry.offset + entry.size > data_.size()) {
|
||||
std::cerr << "Resource::Pack: invalid resource data: " << filename << '\n';
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t> result(
|
||||
data_.begin() + static_cast<std::ptrdiff_t>(entry.offset),
|
||||
data_.begin() + static_cast<std::ptrdiff_t>(entry.offset + entry.size));
|
||||
|
||||
std::uint32_t checksum = calculateChecksum(result);
|
||||
if (checksum != entry.checksum) {
|
||||
std::cerr << "Resource::Pack: checksum mismatch for: " << filename << '\n';
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
auto Pack::hasResource(const std::string& filename) const -> bool {
|
||||
return resources_.contains(filename);
|
||||
}
|
||||
|
||||
void Pack::clear() {
|
||||
resources_.clear();
|
||||
data_.clear();
|
||||
}
|
||||
|
||||
auto Pack::getResourceCount() const -> std::size_t {
|
||||
return resources_.size();
|
||||
}
|
||||
|
||||
auto Pack::getResourceList() const -> std::vector<std::string> {
|
||||
std::vector<std::string> result;
|
||||
result.reserve(resources_.size());
|
||||
for (const auto& [filename, entry] : resources_) {
|
||||
result.push_back(filename);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace Resource
|
||||
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace Resource {
|
||||
|
||||
// Entrada d'un recurs dins el pack.
|
||||
struct PackEntry {
|
||||
std::string filename;
|
||||
std::uint64_t offset{0};
|
||||
std::uint64_t size{0};
|
||||
std::uint32_t checksum{0};
|
||||
};
|
||||
|
||||
// Pack binari de recursos carregat a memòria. Format:
|
||||
// Header: "AE26" (4 bytes) + version uint32 + resource_count uint32
|
||||
// Index: per cada recurs -> filename_len uint32 + filename
|
||||
// + offset uint64 + size uint64 + checksum uint32
|
||||
// Payload: data_size uint64 + bytes xifrats amb XOR (DEFAULT_ENCRYPT_KEY)
|
||||
// La classe té dos rols: (a) loader en temps d'execució — carregar un
|
||||
// .pack del disc i obtindre els bytes d'un recurs per nom; (b) builder
|
||||
// usat per l'eina `pack_resources` per a empaquetar `data/`.
|
||||
class Pack {
|
||||
public:
|
||||
Pack() = default;
|
||||
~Pack() = default;
|
||||
|
||||
Pack(const Pack&) = delete;
|
||||
auto operator=(const Pack&) -> Pack& = delete;
|
||||
Pack(Pack&&) = default;
|
||||
auto operator=(Pack&&) -> Pack& = default;
|
||||
|
||||
// I/O del fitxer. `loadPack` retorna false silenciosament si el
|
||||
// fitxer no existeix (el caller decideix si cal fallback).
|
||||
auto loadPack(const std::string& pack_file) -> bool;
|
||||
auto savePack(const std::string& pack_file) const -> bool;
|
||||
|
||||
// Builders usats per l'eina `pack_resources`.
|
||||
auto addFile(const std::string& filename, const std::string& filepath) -> bool;
|
||||
auto addDirectory(const std::string& directory) -> bool;
|
||||
|
||||
[[nodiscard]] auto getResource(const std::string& filename) const -> std::vector<std::uint8_t>;
|
||||
[[nodiscard]] auto hasResource(const std::string& filename) const -> bool;
|
||||
|
||||
void clear();
|
||||
[[nodiscard]] auto getResourceCount() const -> std::size_t;
|
||||
[[nodiscard]] auto getResourceList() const -> std::vector<std::string>;
|
||||
|
||||
static const std::string DEFAULT_ENCRYPT_KEY;
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, PackEntry> resources_;
|
||||
std::vector<std::uint8_t> data_;
|
||||
|
||||
static auto calculateChecksum(const std::vector<std::uint8_t>& data) -> std::uint32_t;
|
||||
static void xorInPlace(std::vector<std::uint8_t>& data, const std::string& key);
|
||||
};
|
||||
|
||||
} // namespace Resource
|
||||
@@ -40,13 +40,26 @@ Director::Director() {
|
||||
std::fprintf(stderr, "SDL_Init failed: %s\n", SDL_GetError());
|
||||
}
|
||||
|
||||
// Rutes dels recursos relatives a l'executable, no al CWD. SDL_GetBasePath
|
||||
// retorna la carpeta del binari al desplegament portable i Contents/
|
||||
// Resources/ dins d'un .app a macOS. Ha de fer-se abans de qualsevol
|
||||
// loader que passi per Resource::Helper::resolve (Locale, KeyConfig,
|
||||
// Resource::Cache, PaletteLoader, etc.).
|
||||
// Sistema de recursos: el pack viu al costat del binari (build/), el
|
||||
// regenera CMake a cada build. En Debug habilitem el fallback a disc
|
||||
// apuntant al source tree perquè editar YAML/GIF dins `data/` es vegi
|
||||
// sense re-packejar; en Release el pack és obligatori.
|
||||
std::string pack_file = "resources.pack";
|
||||
if (const char* base = SDL_GetBasePath()) {
|
||||
pack_file = std::string(base) + "resources.pack";
|
||||
#ifndef _DEBUG
|
||||
Resource::Helper::setDataRoot(base);
|
||||
#endif
|
||||
}
|
||||
#ifdef _DEBUG
|
||||
Resource::Helper::setDataRoot(AEE_SOURCE_DIR);
|
||||
constexpr bool ENABLE_FALLBACK = true;
|
||||
#else
|
||||
constexpr bool ENABLE_FALLBACK = false;
|
||||
#endif
|
||||
if (!Resource::Helper::initializeResourceSystem(pack_file, ENABLE_FALLBACK)) {
|
||||
std::fprintf(stderr, "Director: sistema de recursos no disponible; avortem\n");
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
// Prepara la carpeta de sistema (~/.config/jailgames/aee_2026/) i carrega
|
||||
@@ -207,6 +220,7 @@ Director::~Director() {
|
||||
// desfà les llibreries abans que corren els destructors estàtics.
|
||||
current_scene_.reset();
|
||||
Resource::Cache::get().clearAudio();
|
||||
Resource::Helper::shutdownResourceSystem();
|
||||
GamepadManager::get().shutdown();
|
||||
Audio::destroy();
|
||||
Screen::get().shutdown();
|
||||
|
||||
Reference in new issue
Block a user