refactor: file_getfilebuffer → file_readfile (std::vector<char>) — elimina 3 leaks (paleta + música gameplay + música cinemàtica)

This commit is contained in:
2026-04-16 09:43:27 +02:00
parent d343e719ca
commit b3ff620c81
12 changed files with 83 additions and 172 deletions

View File

@@ -45,13 +45,10 @@ JD8_Surface JD8_NewSurface() {
}
JD8_Surface JD8_LoadSurface(const char* file) {
int filesize = 0;
char* buffer = file_getfilebuffer(file, filesize);
auto buffer = file_readfile(file);
unsigned short w, h;
Uint8* pixels = LoadGif((unsigned char*)buffer, &w, &h);
free(buffer);
Uint8* pixels = LoadGif(reinterpret_cast<unsigned char*>(buffer.data()), &w, &h);
if (pixels == NULL) {
printf("Unable to load bitmap: %s\n", SDL_GetError());
@@ -66,13 +63,8 @@ JD8_Surface JD8_LoadSurface(const char* file) {
}
JD8_Palette JD8_LoadPalette(const char* file) {
int filesize = 0;
char* buffer = NULL;
buffer = file_getfilebuffer(file, filesize);
JD8_Palette palette = (JD8_Palette)LoadPalette((unsigned char*)buffer);
return palette;
auto buffer = file_readfile(file);
return (JD8_Palette)LoadPalette(reinterpret_cast<unsigned char*>(buffer.data()));
}
void JD8_SetScreenPalette(JD8_Palette palette) {

View File

@@ -147,12 +147,12 @@ FILE* file_getfilepointer(const char* resourcename, int& filesize, const bool bi
return f;
}
char* file_getfilebuffer(const char* resourcename, int& filesize, const bool zero_terminate) {
std::vector<char> file_readfile(const char* resourcename) {
int filesize = 0;
FILE* f = file_getfilepointer(resourcename, filesize, true);
if (!f) return nullptr;
char* buffer = static_cast<char*>(malloc(zero_terminate ? filesize + 1 : filesize));
fread(buffer, filesize, 1, f);
if (zero_terminate) buffer[filesize] = 0;
if (!f) return {};
std::vector<char> buffer(filesize);
fread(buffer.data(), filesize, 1, f);
fclose(f);
return buffer;
}

View File

@@ -1,6 +1,8 @@
#pragma once
#include <stdio.h>
#include <vector>
#define SOURCE_FILE 0
#define SOURCE_FOLDER 1
@@ -12,7 +14,12 @@ void file_setresourcefolder(const char* str);
void file_setsource(const int src);
FILE* file_getfilepointer(const char* resourcename, int& filesize, const bool binary = false);
char* file_getfilebuffer(const char* resourcename, int& filesize, const bool zero_terminate = 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

@@ -27,14 +27,12 @@ namespace Locale {
}
bool load(const char* filename) {
int size = 0;
char* buffer = file_getfilebuffer(filename, size, true);
if (!buffer || size <= 0) {
auto buffer = file_readfile(filename);
if (buffer.empty()) {
std::cerr << "Locale: unable to load " << filename << '\n';
return false;
}
std::string content(buffer, size);
free(buffer);
std::string content(buffer.data(), buffer.size());
try {
auto yaml = fkyaml::node::deserialize(content);

View File

@@ -62,15 +62,13 @@ auto Text::nextCodepoint(const char*& ptr) -> uint32_t {
// --- Càrrega de font ---
void Text::loadFont(const char* fnt_file) {
int filesize = 0;
char* buffer = file_getfilebuffer(fnt_file, filesize, true);
if (!buffer) {
auto buffer = file_readfile(fnt_file);
if (buffer.empty()) {
std::cerr << "Text: unable to load font file: " << fnt_file << '\n';
return;
}
std::istringstream stream(std::string(buffer, filesize));
free(buffer);
std::istringstream stream(std::string(buffer.data(), buffer.size()));
std::string line;
int glyph_index = 0;
@@ -128,15 +126,14 @@ void Text::loadFont(const char* fnt_file) {
}
void Text::loadBitmap(const char* gif_file) {
int filesize = 0;
char* buffer = file_getfilebuffer(gif_file, filesize);
if (!buffer) {
auto buffer = file_readfile(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);
auto* raw = reinterpret_cast<unsigned char*>(buffer.data());
int w = raw[6] | (raw[7] << 8);
int h = raw[8] | (raw[9] << 8);
@@ -144,7 +141,6 @@ void Text::loadBitmap(const char* gif_file) {
Uint8* pixels = LoadGif(raw, &gw, &gh);
if (!pixels) {
std::cerr << "Text: unable to decode GIF: " << gif_file << '\n';
free(buffer);
return;
}
@@ -152,7 +148,6 @@ void Text::loadBitmap(const char* gif_file) {
bitmap_height_ = h;
bitmap_ = pixels;
free(buffer);
std::cout << "Text: bitmap loaded " << w << "x" << h << '\n';
}

View File

@@ -18,7 +18,6 @@
#include "core/system/fiber.hpp"
#include "game/info.hpp"
#include "game/modulegame.hpp"
#include "game/modulesequence.hpp"
#include "game/options.hpp"
#include "scenes/banner_scene.hpp"
#include "scenes/credits_scene.hpp"
@@ -38,10 +37,11 @@ Director* Director::instance_ = nullptr;
namespace {
// Entry del fiber del joc. Executa la màquina d'estats que alterna entre
// ModuleSequence (state=1) i ModuleGame (state=0) fins que el joc demana
// eixir. Quan el joc crida JD8_Flip() des de dins d'aquest fiber, el
// control torna automàticament al Director.
// Entry del fiber del joc. Alterna entre la capa `scenes::` (cinemàtiques
// i menús, triats per `info::ctx.num_piramide` via el SceneRegistry) i
// ModuleGame (gameplay) fins que el joc demana eixir. Quan el codi de
// gameplay o una escena crida JD8_Flip(), el control torna automàticament
// al Director via `GameFiber::yield()`.
void gameFiberEntry() {
info::ctx.num_habitacio = Options::game.habitacio_inicial;
info::ctx.num_piramide = Options::game.piramide_inicial;
@@ -60,51 +60,41 @@ void gameFiberEntry() {
int gameState = 1;
while (gameState != -1 && !JG_Quitting()) {
// Mode "Scene nova": si el state actual és de seqüència i el
// registry té una escena migrada per al num_piramide, l'executem
// amb un mini-loop tick-based. El codi de la Scene no toca
// fibers; el JD8_Flip() entre ticks és el que cedeix al Director.
if (gameState == 1) {
// Replica del redirect que el `ModuleSequence::Go()` vell feia
// al principi: si el jugador arriba a la piràmide Secreta (6)
// sense prou diners, salta directament als slides de fracàs (7).
// Cal fer-ho ací perquè el SceneRegistry consulta num_piramide
// abans del fallback legacy; mentres doSecreta no estiga migrada
// també continuarà al Go() vell amb num_piramide ja corregida.
if (info::ctx.num_piramide == 6 && info::ctx.diners < 200) {
info::ctx.num_piramide = 7;
}
if (auto scene = scenes::SceneRegistry::instance().tryCreate(info::ctx.num_piramide)) {
scene->onEnter();
Uint32 last = SDL_GetTicks();
while (!scene->done() && !JG_Quitting()) {
JI_Update(); // refresca key_pressed/any_key per a les escenes
const Uint32 now = SDL_GetTicks();
scene->tick(static_cast<int>(now - last));
last = now;
JD8_Flip(); // presenta i cedix al Director
}
gameState = scene->nextState();
continue;
}
if (gameState == 0) {
// Gameplay pur (ModuleGame encara és cooperatiu-clàssic: conté
// el seu while intern i crida JD8_Flip manualment). Fora
// d'abast de la migració scenes:: — es tractarà en una fase
// posterior quan el fiber es puga eliminar.
auto* mg = new ModuleGame();
gameState = mg->Go();
delete mg;
continue;
}
// Fallback al codi legacy (encara no migrat a Scene).
switch (gameState) {
case 0: {
auto* moduleGame = new ModuleGame();
gameState = moduleGame->Go();
delete moduleGame;
break;
}
case 1: {
auto* moduleSequence = new ModuleSequence();
gameState = moduleSequence->Go();
delete moduleSequence;
break;
}
// gameState == 1: dispatch a la capa scenes::.
// El vell `ModuleSequence::Go()` feia aquest redirect al principi:
// si el jugador arriba a la Secreta (6) sense prou diners, salta
// als slides de fracàs (7) abans de buscar l'escena al registry.
if (info::ctx.num_piramide == 6 && info::ctx.diners < 200) {
info::ctx.num_piramide = 7;
}
auto scene = scenes::SceneRegistry::instance().tryCreate(info::ctx.num_piramide);
if (!scene) {
// State no registrat — indica un bug del dispatcher o del
// registre d'escenes. Eixim ordenadament en lloc de cremar CPU.
break;
}
scene->onEnter();
Uint32 last = SDL_GetTicks();
while (!scene->done() && !JG_Quitting()) {
JI_Update(); // refresca key_pressed/any_key per a les escenes
const Uint32 now = SDL_GetTicks();
scene->tick(static_cast<int>(now - last));
last = now;
JD8_Flip(); // presenta i cedix al Director
}
gameState = scene->nextState();
}
}
@@ -114,10 +104,10 @@ void Director::init() {
instance_ = new Director();
Gamepad::init();
// Registre d'escenes migrades. Cada entrada = una funció del vell
// ModuleSequence reescrita com a `scenes::*Scene`. Mentre vagen
// caient, el fallback al switch legacy de gameFiberEntry deixa de
// rebre aquests states.
// Registre d'escenes. Cada entrada = un state_key (`num_piramide`)
// amb una factory de `scenes::Scene`. El gameFiberEntry consulta
// aquest registry per a tots els states de seqüència; si una clau
// no apareix ací, el fiber eixirà del loop.
auto& registry = scenes::SceneRegistry::instance();
registry.registerScene(0, [] { return std::make_unique<scenes::MenuScene>(); });
registry.registerScene(100, [] { return std::make_unique<scenes::MortScene>(); });
@@ -136,8 +126,8 @@ void Director::init() {
registry.registerScene(8, [] { return std::make_unique<scenes::CreditsScene>(); });
// State 255 (intro): dues variants segons `Options::game.use_new_logo`.
// La factory tria a runtime — així es pot togglar des del menú sense
// re-registrar. Les dues escenes acaben delegant a doIntroSprites
// legacy fins al Step 9 de la migració.
// re-registrar. Les dues escenes construeixen una IntroSpritesScene
// com a sub-escena per a la part d'animacions de sprites.
registry.registerScene(255, []() -> std::unique_ptr<scenes::Scene> {
if (Options::game.use_new_logo) {
return std::make_unique<scenes::IntroNewLogoScene>();