// Port a l'API de callbacks de SDL3: el runtime posseïx el main loop i ens // crida a SDL_AppInit/SDL_AppIterate/SDL_AppEvent/SDL_AppQuit. Imprescindible // per al port a emscripten on no podem tindre un bucle while propi al hilo // principal. Funciona igual en build natiu (Linux/macOS/Windows) perquè // SDL3 embolcalla el seu propi main loop darrere d'aquestes callbacks. #define SDL_MAIN_USE_CALLBACKS #include #include #include #include #include "core/audio/audio.hpp" #include "core/input/key_config.hpp" #include "core/jail/jdraw8.hpp" #include "core/jail/jfile.hpp" #include "core/jail/jgame.hpp" #include "core/locale/locale.hpp" #include "core/rendering/menu.hpp" #include "core/rendering/overlay.hpp" #include "core/rendering/screen.hpp" #include "core/resources/resource_cache.hpp" #include "core/resources/resource_helper.hpp" #include "core/resources/resource_list.hpp" #include "core/system/director.hpp" #include "game/options.hpp" SDL_AppResult SDL_AppInit(void** /*appstate*/, int /*argc*/, char* /*argv*/[]) { srand(unsigned(time(nullptr))); // Crea la carpeta de configuració i carrega les opcions file_setconfigfolder("jailgames/aee"); // Ruta absoluta a data/ basada en la ubicació de l'executable. // 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) + "resources.pack"; } else { resource_pack_path = "resources.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(); // KeyConfig: defaults des de data/input/keys.yaml + overrides de l'usuari KeyConfig::init("input/keys.yaml", std::string(file_getconfigfolder()) + "keys.yaml"); #ifndef NDEBUG // debug.yaml: estat inicial de gameplay per a tests ràpids, // només en builds de debug. Options::setDebugFile(std::string(file_getconfigfolder()) + "debug.yaml"); Options::loadDebugFromFile(); #endif #ifdef __EMSCRIPTEN__ // MEMFS no persistix entre recàrregues: força valors sensats per a web. Options::window.fullscreen = false; Options::window.zoom = 3; Options::video.aspect_ratio_4_3 = true; Options::video.scaling_mode = Options::ScalingMode::INTEGER; Options::video.texture_filter = Options::TextureFilter::LINEAR; #endif // Carrega textos (idioma per defecte: valencià) Locale::load("locale/ca.yaml"); // Carrega presets de shaders Options::setPostFXFile(std::string(file_getconfigfolder()) + "postfx.yaml"); Options::loadPostFXFromFile(); Options::setCrtPiFile(std::string(file_getconfigfolder()) + "crtpi.yaml"); Options::loadCrtPiFromFile(); JG_Init(); Screen::init(); JD8_Init(); Audio::init(); // crida internament JA_Init i aplica Options::audio Overlay::init(); Menu::init(); // Manifest d'assets (data/config/assets.yaml) + Cache. La precarga // real es fa al BootLoaderScene, que el Director arrenca automàticament // mentre `Resource::Cache::isLoadDone()` siga fals. Resource::List::init("config/assets.yaml"); Resource::Cache::init(); Resource::Cache::get()->beginLoad(); Director::init(); Director::get()->setup(); return SDL_APP_CONTINUE; } SDL_AppResult SDL_AppIterate(void* /*appstate*/) { // Una iteració del bucle del Director. Abans els events es drenaven // amb SDL_PollEvent dins d'aquesta funció; ara SDL ens els lliura // d'un en un via SDL_AppEvent, així que iterate() no els toca. if (!Director::get()->iterate()) { return SDL_APP_SUCCESS; } return SDL_APP_CONTINUE; } SDL_AppResult SDL_AppEvent(void* /*appstate*/, SDL_Event* event) { if (!event) return SDL_APP_CONTINUE; Director::get()->handleEvent(*event); if (Director::get()->isQuitRequested()) { return SDL_APP_SUCCESS; } return SDL_APP_CONTINUE; } void SDL_AppQuit(void* /*appstate*/, SDL_AppResult /*result*/) { // Neteja en ordre invers al de SDL_AppInit. Director::get()->teardown(); Options::saveToFile(); KeyConfig::saveOverrides(); #ifndef NDEBUG Options::saveDebugToFile(); #endif Director::destroy(); KeyConfig::destroy(); Menu::destroy(); Overlay::destroy(); Resource::Cache::destroy(); Resource::List::destroy(); Audio::destroy(); // el destructor del singleton crida JA_Quit JD8_Quit(); Screen::destroy(); JG_Finalize(); ResourceHelper::shutdownResourceSystem(); }