refactor: file_getfilebuffer → file_readfile (std::vector<char>) — elimina 3 leaks (paleta + música gameplay + música cinemàtica)
This commit is contained in:
@@ -58,7 +58,7 @@ The scenes layer itself lives in [source/scenes/](source/scenes/): `scene.hpp` (
|
||||
- Collision detection, scoring, lives, level progression
|
||||
- Visible animation cadence (once translated to ms, must look identical)
|
||||
- Difficulty curves and cinematic timings
|
||||
- Cheat codes (`reviu`, `alone`, `obert`) — currently broken, should be restored
|
||||
- Cheat codes (`reviu`, `alone`, `obert`)
|
||||
- Original palettes, fades, music cues
|
||||
|
||||
**Free to change** (internal representation):
|
||||
@@ -235,10 +235,10 @@ JD8_Flip produces ABGR byte order: `0xFF000000 + R + (G<<8) + (B<<16)`. SDL text
|
||||
### Known Issues & Technical Debt
|
||||
|
||||
1. **gif.h cannot be included twice**: Functions are not `static` or `inline`, causing multiple definition errors. Text class uses `extern` forward declarations as workaround
|
||||
2. **Cheats are broken (`reviu`, `alone`, `obert`)**: `JI_CheatActivated` in [jinput.cpp:46](source/core/jail/jinput.cpp#L46) compares `SDL_Scancode` values (e.g. `SDL_SCANCODE_R`=21) against ASCII chars (`'r'`=114). They never match. Regression from SDL3 migration. **Now fixable** — scheduled for Phase 1 of modernization since jail is no longer off-limits.
|
||||
2. ~~**Cheats are broken (`reviu`, `alone`, `obert`)**~~: Fixed. `JI_moveCheats` tradueix `SDL_Scancode` → ASCII via `scancode_to_ascii` abans de ficar-los al buffer ([jinput.cpp:32-37, 55-61](source/core/jail/jinput.cpp#L32-L61)), i `JI_CheatActivated` compara ASCII amb ASCII.
|
||||
3. **No sound effects in game**: Game code never calls `JA_PlaySound*`/`JA_LoadSound` — only music via `JA_PlayMusic`/`JA_FadeOutMusic`. The SONS and VOL SONS menu items control volume of an empty channel pool. Infrastructure ready for future SFX.
|
||||
4. **Raw `malloc`/`free` in gameplay structs**: `Sprite`/`Entitat` use `malloc` for `Frame[]` and `Animacio[]`; `jfile.cpp` uses a global `scratch[255]` buffer (UB under concurrent calls); `jail_audio.cpp` mixes `new`/`malloc`/`SDL_malloc`. Scheduled for Phase 1 (RAII pass).
|
||||
5. **Blocking loops in cinematics and fades**: `ModuleSequence::doIntro()` has 15+ `while(!JG_ShouldUpdate())` spin-waits; `JD8_FadeOut`/`JD8_FadeToPal` run 32 internal iterations calling `JD8_Flip`. Incompatible with `SDL_AppIterate`. Scheduled for Phase 2 (state-machine refactor).
|
||||
4. ~~**Raw `malloc`/`free` in gameplay structs**~~: Majoritàriament arreglat. `Sprite`/`Entitat` usen `std::vector<Frame>` i `std::vector<Animacio>` ([sprite.hpp](source/game/sprite.hpp)). `jfile.cpp` ja no té el global `scratch[255]` (substituït per `thread_local std::string`). L'API `file_getfilebuffer` (que tornava raw `char*` amb `malloc`) s'ha substituït per `file_readfile` que retorna `std::vector<char>` RAII — elimina els leaks silenciosos que hi havia a `JD8_LoadPalette`, `ModuleGame::Go()` i `scenes::playMusic`. Queda `jail_audio.hpp` barrejant `new`/`malloc`/`SDL_malloc` de forma pairada i correcta (no leak), pendent de polir amb `std::unique_ptr` quan toque.
|
||||
5. ~~**Blocking loops in cinematics and fades**~~: Fixed. La migració completa de `ModuleSequence::do*()` a la capa `scenes::` (Steps 1–9) ha eliminat tots els `while(!JG_ShouldUpdate())` i `wait_frame_or_skip()`. Les cinemàtiques ara són tick-based amb acumuladors ms. `JD8_FadeOut`/`JD8_FadeToPal` encara tenen el seu bucle intern de 32 passos (usat per a transicions fora d'escena com al final de `ModuleGame`); el wrapper tick-based `scenes::PaletteFade` el consumeix un pas per tick quan es crida des d'una escena.
|
||||
6. ~~**`SDL_AddTimer` in audio**~~: Fixed in Phase 3. `jail_audio` is now a header-only `inline` module (single `.cpp` stub only hosts the `stb_vorbis` implementation to avoid multiple definitions). Music uses true streaming via `stb_vorbis_open_memory` + `JA_PumpMusic` with a 0.5s low-water-mark instead of decoding the whole OGG into RAM. Mixing/fade update runs manually via `JA_Update()` called once per frame from `Director::run()`. Ported from the `jaildoctors_dilemma` codebase.
|
||||
7. ~~**Game thread + `publishFrame` mutex/cv**~~: Fixed in Phase 4+5. Replaced by a cooperative `GameFiber` (ucontext on POSIX, Fibers API on Windows). `JD8_Flip()` calls `GameFiber::yield()`, Director calls `GameFiber::resume()` once per frame. Zero threads, zero mutexes. Emscripten fiber backend still pending for Phase 7.
|
||||
|
||||
|
||||
@@ -67,7 +67,6 @@ set(APP_SOURCES
|
||||
source/game/mapa.cpp
|
||||
source/game/marcador.cpp
|
||||
source/game/modulegame.cpp
|
||||
source/game/modulesequence.cpp
|
||||
source/game/momia.cpp
|
||||
source/game/prota.cpp
|
||||
source/game/sprite.cpp
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -45,9 +45,9 @@ int ModuleGame::Go() {
|
||||
const char* music = info::ctx.num_piramide == 3 ? "00000008.ogg" : (info::ctx.num_piramide == 2 ? "00000007.ogg" : (info::ctx.num_piramide == 6 ? "00000002.ogg" : "00000006.ogg"));
|
||||
const char* current_music = JA_GetMusicFilename();
|
||||
if ((JA_GetMusicState() != JA_MUSIC_PLAYING) || !(strcmp(music, current_music) == 0)) {
|
||||
int size;
|
||||
char* buffer = file_getfilebuffer(music, size);
|
||||
JA_PlayMusic(JA_LoadMusic((Uint8*)buffer, size, music));
|
||||
auto buffer = file_readfile(music);
|
||||
JA_PlayMusic(JA_LoadMusic(reinterpret_cast<Uint8*>(buffer.data()),
|
||||
static_cast<Uint32>(buffer.size()), music));
|
||||
}
|
||||
|
||||
JD8_FadeToPal(JD8_LoadPalette(info::ctx.pepe_activat ? "frames2.gif" : "frames.gif"));
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
#include "game/modulesequence.hpp"
|
||||
|
||||
#include "core/jail/jdraw8.hpp"
|
||||
#include "core/jail/jgame.hpp"
|
||||
|
||||
ModuleSequence::ModuleSequence() {
|
||||
}
|
||||
|
||||
ModuleSequence::~ModuleSequence(void) {
|
||||
}
|
||||
|
||||
int ModuleSequence::Go() {
|
||||
if (info::ctx.num_piramide == 6 && info::ctx.diners < 200) info::ctx.num_piramide = 7;
|
||||
|
||||
// Totes les branques del vell switch han estat migrades a scenes:
|
||||
// case 0 (Menú) → scenes::MenuScene
|
||||
// case 1 i 7 (Slides) → scenes::SlidesScene
|
||||
// case 2..5 (Banner) → scenes::BannerScene
|
||||
// case 6 (Secreta) → scenes::SecretaScene
|
||||
// case 8 (Credits) → scenes::CreditsScene
|
||||
// case 100 (Mort) → scenes::MortScene
|
||||
// case 255 (Intro) → scenes::IntroScene / scenes::IntroNewLogoScene
|
||||
// (amb scenes::IntroSpritesScene com a sub-escena)
|
||||
// El gameFiberEntry les dispatcha via SceneRegistry abans de caure a
|
||||
// aquest Go() — així que en la pràctica ja no s'arriba ací. El codi
|
||||
// que queda sota (JD8_FadeOut + transicions de num_piramide) serà
|
||||
// eliminat al Step 10 del pla, junt amb ModuleSequence::Go() sencer.
|
||||
|
||||
JD8_FadeOut();
|
||||
|
||||
if (JG_Quitting()) {
|
||||
return -1;
|
||||
} else {
|
||||
if (info::ctx.num_piramide == 255) {
|
||||
info::ctx.num_piramide = 0;
|
||||
return 1;
|
||||
} else if (info::ctx.num_piramide == 0) {
|
||||
info::ctx.num_piramide = 1;
|
||||
return 1;
|
||||
} else if (info::ctx.num_piramide == 7) {
|
||||
info::ctx.num_piramide = 8;
|
||||
return 1;
|
||||
} else if (info::ctx.num_piramide == 8) {
|
||||
info::ctx.num_piramide = 255;
|
||||
return 1;
|
||||
} else if (info::ctx.num_piramide == 100) {
|
||||
info::ctx.num_piramide = 0;
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include "game/info.hpp"
|
||||
|
||||
class ModuleSequence {
|
||||
public:
|
||||
ModuleSequence();
|
||||
~ModuleSequence(void);
|
||||
|
||||
int Go();
|
||||
|
||||
// Totes les doX() han sigut migrades a escenes dins source/scenes/.
|
||||
// El Step 10 del pla eliminarà ModuleSequence sencer, inclòs Go(),
|
||||
// quan el gameFiberEntry deixe de cridar-lo com a fallback.
|
||||
};
|
||||
@@ -9,13 +9,13 @@ namespace scenes {
|
||||
|
||||
void playMusic(const char* filename, int loop) {
|
||||
if (!filename) return;
|
||||
int size = 0;
|
||||
char* buffer = file_getfilebuffer(filename, size);
|
||||
if (!buffer) return;
|
||||
// JA_LoadMusic fa una còpia del OGG comprimit (SDL_malloc), així que
|
||||
// el `buffer` original es queda huérfano. Leak conegut heredat del
|
||||
// codi original — es tractarà quan jfile tinga una API std::vector.
|
||||
JA_PlayMusic(JA_LoadMusic(reinterpret_cast<Uint8*>(buffer), size, filename), loop);
|
||||
auto buffer = file_readfile(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()),
|
||||
static_cast<Uint32>(buffer.size()), filename),
|
||||
loop);
|
||||
}
|
||||
|
||||
} // namespace scenes
|
||||
|
||||
Reference in New Issue
Block a user