acabat amb resource.pack

This commit is contained in:
2026-04-16 16:21:44 +02:00
parent b2d5f5af61
commit 4244bcaea3
11 changed files with 55 additions and 167 deletions

View File

@@ -228,7 +228,7 @@ JD8_Flip produces ABGR byte order: `0xFF000000 + R + (G<<8) + (B<<16)`. SDL text
| `~/.config/jailgames/aee/postfx.yaml` | PostFX shader presets (6 defaults: CRT, NTSC, CURVED, SCANLINES, SUBTLE, CRT LIVE) |
| `~/.config/jailgames/aee/crtpi.yaml` | CRT-Pi shader presets (4 defaults: DEFAULT, CURVED, SHARP, MINIMAL) |
### Resource Pack (`source/core/resources/`) — **en construcció**
### Resource Pack (`source/core/resources/`)
Sistema d'empaquetat d'assets a l'estil `coffee_crisis_arcade_edition`. Genera un sol fitxer binari opac `resource.pack` que substitueix la carpeta `data/` als releases natius.
@@ -249,22 +249,13 @@ Checksum: djb2-like amb seed `0x12345678`. Càrrega full-to-RAM (sense mmap).
- `make pack` compila l'eina (target `pack_resources` a `EXCLUDE_FROM_ALL` de [CMakeLists.txt](CMakeLists.txt)) i genera `resource.pack` a la rel. 33 entrades ≈ 4 MB.
- `./build/pack_resources --list resource.pack` inspecciona el pack.
**Estat actual (Fase 1 completada, 2026-04-16)**:
- Classes + eina compilen i funcionen. El pack es genera correctament i el `--list` mostra les 33 entrades (totes les GIFs, OGGs, font `.fnt`+`.gif`, `locale/ca.yaml`, `shaders/*`).
- **Encara no està cablejat al joc**: cap callsite usa `ResourceHelper::loadFile`. Tots segueixen cridant `file_readfile`. El joc funciona exactament igual que abans.
- Nou getter `file_getresourcefolder()` afegit a [jfile.hpp](source/core/jail/jfile.hpp) perquè ResourceHelper puga construir el path del fallback.
**Pendent (Fases 2-6 del pla [.claude/plans/declarative-popping-breeze.md](/home/sergio/.claude/plans/declarative-popping-breeze.md))**:
1. **Fase 2** — Afegir `ResourceHelper::initializeResourceSystem(pack_path, enable_fallback=true)` a [main.cpp](source/main.cpp) just després de `file_setresourcefolder`, i `shutdownResourceSystem()` a `SDL_AppQuit`.
2. **Fase 3** — Migrar callsites `file_readfile``ResourceHelper::loadFile` (tipus canvia `std::vector<char>``std::vector<uint8_t>`):
- [locale.cpp:30](source/core/locale/locale.cpp#L30)
- [text.cpp:65, 129](source/core/rendering/text.cpp) (`.fnt` + bitmap)
- [scene_utils.cpp:12](source/scenes/scene_utils.cpp) (música escenes)
- [modulegame.cpp:52](source/game/modulegame.cpp) (música gameplay)
- [jdraw8.cpp:47, 65](source/core/jail/jdraw8.cpp) (`JD8_LoadSurface`, `JD8_LoadPalette`)
3. **Fase 4** — Eliminar scaffold `.jrf` de [jfile.cpp](source/core/jail/jfile.cpp): `file_setresourcefilename`, `file_setsource`, `SOURCE_FILE`/`SOURCE_FOLDER`, `dictionary_loaded()`, `file_getfilepointer()`, `file_readfile()`. Mantenir només config-folder + `file_setresourcefolder` + `file_getresourcefolder`.
4. **Fase 5** — Als targets release de [Makefile](Makefile) (`_linux_release`/`_windows_release`/`_macos_release`): afegir dependència `pack` i canviar `cp -r data``cp resource.pack`. WASM intacte (segueix usant `--preload-file data@/data`).
5. **Fase 6** — A [main.cpp](source/main.cpp): `enable_fallback = false` només per a `NDEBUG && !__EMSCRIPTEN__` (pack obligatori a Release natiu; Debug i WASM mantenen fallback).
**Estat actual (Fases 1-6 completades, 2026-04-16)**:
- `ResourcePack` + `ResourceHelper` + eina `pack_resources` compilen i funcionen. El pack genera 33 entrades ≈ 4 MB.
- Cablejat al joc via `ResourceHelper::initializeResourceSystem` a [main.cpp](source/main.cpp) (amb `return SDL_APP_FAILURE` si falla), i `shutdownResourceSystem` a `SDL_AppQuit`.
- Tots els callsites de recursos usen `ResourceHelper::loadFile` (`std::vector<uint8_t>`): [locale.cpp](source/core/locale/locale.cpp), [text.cpp](source/core/rendering/text.cpp), [scene_utils.cpp](source/scenes/scene_utils.cpp), [modulegame.cpp](source/game/modulegame.cpp), [jdraw8.cpp](source/core/jail/jdraw8.cpp).
- Scaffold `.jrf` eliminat de [jfile.cpp](source/core/jail/jfile.cpp): `file_setresourcefilename`, `file_setsource`, `SOURCE_FILE`/`SOURCE_FOLDER`, `dictionary_loaded`, `file_getfilepointer`, `file_readfile`. Només queden config-folder i resource-folder getters/setters.
- Targets release a [Makefile](Makefile) (`_linux_release`/`_windows_release`/`_macos_release`) depenen de `pack` i copien `resource.pack` en lloc de `data/`. WASM intacte (`--preload-file data@/data`).
- `enable_fallback = false` a Release natiu (`NDEBUG && !__EMSCRIPTEN__`): el pack és obligatori. Debug i WASM mantenen el fallback actiu.
### External Libraries (`source/external/`)
@@ -317,7 +308,6 @@ Checksum: djb2-like amb seed `0x12345678`. Càrrega full-to-RAM (sense mmap).
- **FPS counter jitter**: time segment width changes per frame (100 Hz centi updates) causes ~1-2 px horizontal jitter in centered layout. Could lock to max-width or use monospace digits.
- **Notification messages partially hardcoded**: overlay/global_inputs/director now use Locale, but the window title (`Texts::WINDOW_TITLE`) and some game-layer strings remain hardcoded.
- **jail_audio `JA_Sound_t` RAII**: `JA_Music_t` ja està net (vector + string), però `JA_Sound_t` encara usa `Uint8*` via `SDL_LoadWAV` out-param. Petit polish per a completar la coherència RAII.
- **Resource Pack — Fases 2-6**: la classe `ResourcePack`, `ResourceHelper` i l'eina `pack_resources` ja estan fetes (Fase 1). Queda cablejar-ho al joc: init a `main.cpp`, migrar 5 callsites de `file_readfile` a `ResourceHelper::loadFile`, eliminar l'scaffold `.jrf` de `jfile`, integrar `resource.pack` als bundles release, i flip `enable_fallback=false` per a Release natiu. Detall complet a la secció *Resource Pack* i al pla [.claude/plans/declarative-popping-breeze.md](/home/sergio/.claude/plans/declarative-popping-breeze.md).
### Previously Fixed (kept for reference)

View File

@@ -99,7 +99,7 @@ endif
# ==============================================================================
# COMPILACIÓN PARA WINDOWS (RELEASE)
# ==============================================================================
_windows_release:
_windows_release: pack
@echo off
@echo Creando release para Windows - Version: $(VERSION)
@@ -112,8 +112,8 @@ _windows_release:
@powershell -Command "if (Test-Path '$(RELEASE_FOLDER)') {Remove-Item '$(RELEASE_FOLDER)' -Recurse -Force}"
@powershell -Command "if (-not (Test-Path '$(RELEASE_FOLDER)')) {New-Item '$(RELEASE_FOLDER)' -ItemType Directory}"
# Copia ficheros
@powershell -Command "Copy-Item -Path 'data' -Destination '$(RELEASE_FOLDER)' -Recurse"
# Copia ficheros (resource.pack substitueix la carpeta data/)
@powershell -Command "Copy-Item 'resource.pack' -Destination '$(RELEASE_FOLDER)'"
@powershell -Command "Copy-Item 'LICENSE' -Destination '$(RELEASE_FOLDER)'"
@powershell -Command "Copy-Item 'README.md' -Destination '$(RELEASE_FOLDER)'"
@powershell -Command "Copy-Item 'gamecontrollerdb.txt' -Destination '$(RELEASE_FOLDER)'"
@@ -132,7 +132,7 @@ _windows_release:
# ==============================================================================
# COMPILACIÓN PARA MACOS (RELEASE)
# ==============================================================================
_macos_release:
_macos_release: pack
@echo "Creando release para macOS - Version: $(VERSION)"
# Verificar e instalar create-dmg si es necesario
@@ -154,8 +154,8 @@ _macos_release:
$(MKDIR) "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/MacOS"
$(MKDIR) "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Resources"
# Copia carpetas y ficheros
cp -R data "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Resources"
# Copia carpetas y ficheros (resource.pack substitueix la carpeta data/)
cp resource.pack "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Resources"
cp gamecontrollerdb.txt "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Resources"
cp -R release/macos/frameworks/SDL3.xcframework/macos-arm64_x86_64/SDL3.framework "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Frameworks"
cp release/icons/*.icns "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Resources"
@@ -252,7 +252,7 @@ wasm:
# ==============================================================================
# COMPILACIÓN PARA LINUX (RELEASE)
# ==============================================================================
_linux_release:
_linux_release: pack
@echo "Creando release para Linux - Version: $(VERSION)"
# Compila con cmake
@@ -263,8 +263,8 @@ _linux_release:
$(RMDIR) "$(RELEASE_FOLDER)"
$(MKDIR) "$(RELEASE_FOLDER)"
# Copia ficheros
cp -r data "$(RELEASE_FOLDER)"
# Copia ficheros (resource.pack substitueix la carpeta data/)
cp resource.pack "$(RELEASE_FOLDER)"
cp LICENSE "$(RELEASE_FOLDER)"
cp README.md "$(RELEASE_FOLDER)"
cp gamecontrollerdb.txt "$(RELEASE_FOLDER)"

Binary file not shown.

View File

@@ -2,7 +2,7 @@
#include <fstream>
#include "core/jail/jfile.hpp"
#include "core/resources/resource_helper.hpp"
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-but-set-variable"
@@ -44,10 +44,10 @@ JD8_Surface JD8_NewSurface() {
}
JD8_Surface JD8_LoadSurface(const char* file) {
auto buffer = file_readfile(file);
auto buffer = ResourceHelper::loadFile(file);
unsigned short w, h;
Uint8* pixels = LoadGif(reinterpret_cast<unsigned char*>(buffer.data()), &w, &h);
Uint8* pixels = LoadGif(buffer.data(), &w, &h);
if (pixels == NULL) {
printf("Unable to load bitmap: %s\n", SDL_GetError());
@@ -62,8 +62,8 @@ JD8_Surface JD8_LoadSurface(const char* file) {
}
JD8_Palette JD8_LoadPalette(const char* file) {
auto buffer = file_readfile(file);
return (JD8_Palette)LoadPalette(reinterpret_cast<unsigned char*>(buffer.data()));
auto buffer = ResourceHelper::loadFile(file);
return (JD8_Palette)LoadPalette(buffer.data());
}
void JD8_SetScreenPalette(JD8_Palette palette) {

View File

@@ -1,15 +1,10 @@
#include "core/jail/jfile.hpp"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
@@ -19,58 +14,14 @@
namespace {
constexpr const char* DEFAULT_FILENAME = "data.jf2";
constexpr const char* DEFAULT_FOLDER = "data/";
struct file_entry {
std::string path;
uint32_t size;
uint32_t offset;
};
struct keyvalue {
std::string key;
std::string value;
};
std::vector<file_entry> toc;
std::vector<keyvalue> config;
std::string resource_filename;
std::string resource_folder;
std::string config_folder;
int file_source = SOURCE_FILE;
bool dictionary_loaded() {
if (resource_filename.empty()) resource_filename = DEFAULT_FILENAME;
std::ifstream fi(resource_filename, std::ios::binary);
if (!fi.is_open()) return false;
char header[4];
fi.read(header, 4);
uint32_t num_files, toc_offset;
fi.read(reinterpret_cast<char*>(&num_files), 4);
fi.read(reinterpret_cast<char*>(&toc_offset), 4);
fi.seekg(toc_offset);
for (uint32_t i = 0; i < num_files; ++i) {
uint32_t file_offset, file_size;
fi.read(reinterpret_cast<char*>(&file_offset), 4);
fi.read(reinterpret_cast<char*>(&file_size), 4);
uint8_t path_size;
fi.read(reinterpret_cast<char*>(&path_size), 1);
char file_name[256];
fi.read(file_name, path_size);
file_name[path_size] = 0;
toc.push_back({std::string(file_name), file_size, file_offset});
}
return true;
}
std::string filename_with_folder(const char* filename) {
return resource_folder + filename;
}
void load_config_values() {
config.clear();
@@ -97,10 +48,6 @@ void save_config_values() {
} // namespace
void file_setresourcefilename(const char* str) {
resource_filename = str;
}
void file_setresourcefolder(const char* str) {
resource_folder = str;
}
@@ -109,58 +56,6 @@ const char* file_getresourcefolder() {
return resource_folder.c_str();
}
void file_setsource(const int src) {
file_source = src % 2;
if (src == SOURCE_FOLDER && resource_folder.empty()) file_setresourcefolder(DEFAULT_FOLDER);
}
FILE* file_getfilepointer(const char* resourcename, int& filesize, const bool binary) {
if (file_source == SOURCE_FILE && toc.empty()) {
if (!dictionary_loaded()) file_setsource(SOURCE_FOLDER);
}
FILE* f = nullptr;
if (file_source == SOURCE_FILE) {
const std::string name(resourcename);
size_t count = 0;
for (; count < toc.size(); ++count) {
if (toc[count].path == name) break;
}
if (count == toc.size()) {
perror("El recurs no s'ha trobat en l'arxiu de recursos");
exit(1);
}
filesize = static_cast<int>(toc[count].size);
f = fopen(resource_filename.c_str(), binary ? "rb" : "r");
if (!f) {
perror("No s'ha pogut obrir l'arxiu de recursos");
exit(1);
}
fseek(f, toc[count].offset, SEEK_SET);
} else {
const std::string full = filename_with_folder(resourcename);
f = fopen(full.c_str(), binary ? "rb" : "r");
if (!f) return nullptr;
fseek(f, 0, SEEK_END);
filesize = static_cast<int>(ftell(f));
fseek(f, 0, SEEK_SET);
}
return f;
}
std::vector<char> file_readfile(const char* resourcename) {
int filesize = 0;
FILE* f = file_getfilepointer(resourcename, filesize, true);
if (!f) return {};
std::vector<char> buffer(filesize);
fread(buffer.data(), filesize, 1, f);
fclose(f);
return buffer;
}
// Crea la carpeta del sistema on guardar les dades.
// Accepta rutes amb subdirectoris (ex: "jailgames/aee") i crea tota la jerarquia.
void file_setconfigfolder(const char* foldername) {

View File

@@ -1,26 +1,10 @@
#pragma once
#include <stdio.h>
#include <vector>
#define SOURCE_FILE 0
#define SOURCE_FOLDER 1
void file_setconfigfolder(const char* foldername);
const char* file_getconfigfolder();
void file_setresourcefilename(const char* str);
void file_setresourcefolder(const char* str);
const char* file_getresourcefolder();
void file_setsource(const int src);
FILE* file_getfilepointer(const char* resourcename, int& filesize, const bool binary = false);
// Llig tot el contingut d'un recurs (fitxer solt o entrada del .jrf).
// Retorna un vector buit si el recurs no existeix. El vector es destrueix
// automàticament en eixir d'àmbit — no fa falta cap free() manual. Mida =
// bytes llegits (el buffer no està null-terminated).
std::vector<char> file_readfile(const char* resourcename);
const char* file_getconfigvalue(const char* key);
void file_setconfigvalue(const char* key, const char* value);

View File

@@ -4,7 +4,7 @@
#include <string>
#include <unordered_map>
#include "core/jail/jfile.hpp"
#include "core/resources/resource_helper.hpp"
#include "external/fkyaml_node.hpp"
namespace Locale {
@@ -27,12 +27,12 @@ namespace Locale {
}
bool load(const char* filename) {
auto buffer = file_readfile(filename);
auto buffer = ResourceHelper::loadFile(filename);
if (buffer.empty()) {
std::cerr << "Locale: unable to load " << filename << '\n';
return false;
}
std::string content(buffer.data(), buffer.size());
std::string content(reinterpret_cast<const char*>(buffer.data()), buffer.size());
try {
auto yaml = fkyaml::node::deserialize(content);

View File

@@ -8,7 +8,7 @@
#include <sstream>
#include <string>
#include "core/jail/jfile.hpp"
#include "core/resources/resource_helper.hpp"
// Forward declarations de gif.h (inclòs des de jdraw8.cpp, no es pot incloure dos vegades)
struct rgb;
@@ -62,13 +62,13 @@ auto Text::nextCodepoint(const char*& ptr) -> uint32_t {
// --- Càrrega de font ---
void Text::loadFont(const char* fnt_file) {
auto buffer = file_readfile(fnt_file);
auto buffer = ResourceHelper::loadFile(fnt_file);
if (buffer.empty()) {
std::cerr << "Text: unable to load font file: " << fnt_file << '\n';
return;
}
std::istringstream stream(std::string(buffer.data(), buffer.size()));
std::istringstream stream(std::string(reinterpret_cast<const char*>(buffer.data()), buffer.size()));
std::string line;
int glyph_index = 0;
@@ -126,14 +126,14 @@ void Text::loadFont(const char* fnt_file) {
}
void Text::loadBitmap(const char* gif_file) {
auto buffer = file_readfile(gif_file);
auto buffer = ResourceHelper::loadFile(gif_file);
if (buffer.empty()) {
std::cerr << "Text: unable to load bitmap: " << gif_file << '\n';
return;
}
// Extrau dimensions del header GIF (bytes 6-7 = width, 8-9 = height, little-endian)
auto* raw = reinterpret_cast<unsigned char*>(buffer.data());
auto* raw = buffer.data();
int w = raw[6] | (raw[7] << 8);
int h = raw[8] | (raw[9] << 8);

View File

@@ -2,9 +2,9 @@
#include "core/jail/jail_audio.hpp"
#include "core/jail/jdraw8.hpp"
#include "core/jail/jfile.hpp"
#include "core/jail/jgame.hpp"
#include "core/jail/jinput.hpp"
#include "core/resources/resource_helper.hpp"
ModuleGame::ModuleGame() {
this->gfx = JD8_LoadSurface(info::ctx.pepe_activat ? "frames2.gif" : "frames.gif");
@@ -49,8 +49,8 @@ void ModuleGame::onEnter() {
const char* current_music = JA_GetMusicFilename();
if ((JA_GetMusicState() != JA_MUSIC_PLAYING) || !current_music ||
strcmp(music, current_music) != 0) {
auto buffer = file_readfile(music);
JA_PlayMusic(JA_LoadMusic(reinterpret_cast<Uint8*>(buffer.data()),
auto buffer = ResourceHelper::loadFile(music);
JA_PlayMusic(JA_LoadMusic(buffer.data(),
static_cast<Uint32>(buffer.size()), music));
}

View File

@@ -19,6 +19,7 @@
#include "core/rendering/menu.hpp"
#include "core/rendering/overlay.hpp"
#include "core/rendering/screen.hpp"
#include "core/resources/resource_helper.hpp"
#include "core/system/director.hpp"
#include "game/options.hpp"
@@ -32,10 +33,27 @@ SDL_AppResult SDL_AppInit(void** /*appstate*/, int /*argc*/, char* /*argv*/[]) {
// SDL_GetBasePath() detecta automàticament si estem dins d'un .app bundle
// (retorna Contents/Resources/) o en un executable normal (carpeta del binari).
const char* base_path = SDL_GetBasePath();
std::string resource_pack_path;
if (base_path) {
const std::string data_path = std::string(base_path) + "data/";
file_setresourcefolder(data_path.c_str());
resource_pack_path = std::string(base_path) + "resource.pack";
} else {
resource_pack_path = "resource.pack";
}
// Sistema de recursos: prova el pack i cau a fitxers solts dins data/.
// Release natiu exigix el pack (sense fallback); Debug i WASM mantenen
// el fallback actiu per a desenvolupament i per al build amb MEMFS.
#if defined(NDEBUG) && !defined(__EMSCRIPTEN__)
const bool enable_fallback = false;
#else
const bool enable_fallback = true;
#endif
if (!ResourceHelper::initializeResourceSystem(resource_pack_path, enable_fallback)) {
return SDL_APP_FAILURE;
}
Options::setConfigFile(std::string(file_getconfigfolder()) + "config.yaml");
Options::loadFromFile();
@@ -102,4 +120,5 @@ void SDL_AppQuit(void* /*appstate*/, SDL_AppResult /*result*/) {
JD8_Quit();
Screen::destroy();
JG_Finalize();
ResourceHelper::shutdownResourceSystem();
}

View File

@@ -3,17 +3,17 @@
#include <SDL3/SDL.h>
#include "core/jail/jail_audio.hpp"
#include "core/jail/jfile.hpp"
#include "core/resources/resource_helper.hpp"
namespace scenes {
void playMusic(const char* filename, int loop) {
if (!filename) return;
auto buffer = file_readfile(filename);
auto buffer = ResourceHelper::loadFile(filename);
if (buffer.empty()) return;
// JA_LoadMusic fa una còpia interna del OGG comprimit (via SDL_malloc)
// per a stb_vorbis. El `buffer` local es destruirà en sortir d'àmbit.
JA_PlayMusic(JA_LoadMusic(reinterpret_cast<Uint8*>(buffer.data()),
JA_PlayMusic(JA_LoadMusic(buffer.data(),
static_cast<Uint32>(buffer.size()), filename),
loop);
}