#include "core/system/director.hpp" #include #include // Para mkdir, stat, S_IRWXU #include // Para getuid #include // Para errno, EEXIST, EACCES, ENAMETOO... #include // Para printf, perror #include // Para exit, EXIT_FAILURE, srand #include // Para basic_ostream, operator<<, cout #include // Para make_unique, unique_ptr #include // Para operator+, allocator, char_traits #include "core/audio/audio.hpp" // Para Audio #include "core/input/input.hpp" // Para Input, InputAction #include "core/rendering/screen.hpp" // Para Screen #include "core/resources/resource_cache.hpp" // Para Resource #include "core/resources/resource_helper.hpp" // Para ResourceHelper #include "core/resources/resource_list.hpp" // Para Asset, AssetType #include "core/resources/resource_loader.hpp" // Para ResourceLoader #include "game/options.hpp" // Para Options, options, OptionsVideo #include "game/scene_manager.hpp" // Para SceneManager #include "game/scenes/game.hpp" // Para Game, GameMode #include "game/scenes/logo.hpp" // Para Logo #include "game/scenes/title.hpp" // Para Title #include "game/ui/notifier.hpp" // Para Notifier #include "project.h" #include "utils/defines.hpp" // Para WINDOW_CAPTION #ifdef _DEBUG #include "core/system/debug.hpp" // Para Debug #endif #ifndef _WIN32 #include #endif // Constructor Director::Director(std::vector const& args) { std::cout << "Game start" << '\n'; // Crea e inicializa las opciones del programa Options::init(); // Comprueba los parametros del programa executable_path_ = getPath(checkProgramArguments(args)); // Crea la carpeta del sistema donde guardar datos createSystemFolder("jailgames"); createSystemFolder(std::string("jailgames/") + Project::NAME); // Determinar el prefijo de ruta según la plataforma #ifdef MACOS_BUNDLE const std::string PREFIX = "/../Resources"; #else const std::string PREFIX; #endif // Preparar ruta al pack (en macOS bundle está en Contents/Resources/) std::string pack_path = executable_path_ + PREFIX + "/resources.pack"; #ifdef RELEASE_BUILD // ============================================================ // RELEASE BUILD: Pack-first architecture // ============================================================ std::cout << "\n** RELEASE MODE: Pack-first initialization\n"; // 1. Initialize resource pack system (required, no fallback) std::cout << "Initializing resource pack: " << pack_path << '\n'; if (!Resource::Helper::initializeResourceSystem(pack_path, false)) { std::cerr << "ERROR: Failed to load resources.pack (required in release builds)\n"; exit(EXIT_FAILURE); } // 2. Validate pack integrity std::cout << "Validating pack integrity..." << '\n'; if (!Resource::Loader::get().validatePack()) { std::cerr << "ERROR: Pack validation failed\n"; exit(EXIT_FAILURE); } // 3. Load assets.yaml from pack std::cout << "Loading assets configuration from pack..." << '\n'; std::string assets_config = Resource::Loader::get().loadAssetsConfig(); if (assets_config.empty()) { std::cerr << "ERROR: Failed to load assets.yaml from pack\n"; exit(EXIT_FAILURE); } // 4. Initialize Asset system with config from pack // NOTE: In release, don't use executable_path or PREFIX - paths in pack are relative // Pass empty string to avoid issues when running from different directories Resource::List::init(""); // Empty executable_path in release Resource::List::get()->loadFromString(assets_config, "", system_folder_); // Empty PREFIX for pack std::cout << "Asset system initialized from pack\n"; #else // ============================================================ // DEVELOPMENT BUILD: Filesystem-first architecture // ============================================================ std::cout << "\n** DEVELOPMENT MODE: Filesystem-first initialization\n"; // 1. Initialize Asset system from filesystem Resource::List::init(executable_path_); // 2. Load asset configuration from disk // Note: Asset verification happens during Resource::Cache::load() setFileList(); // 3. Initialize resource pack system (optional, with fallback) std::cout << "Initializing resource pack (development mode): " << pack_path << '\n'; Resource::Helper::initializeResourceSystem(pack_path, true); #endif // Configura la ruta y carga las opciones desde un fichero Options::setConfigFile(Resource::List::get()->get("config.yaml")); Options::loadFromFile(); // Inicializa JailAudio Audio::init(); // Crea los objetos Screen::init(); // Initialize resources (works for both release and development) Resource::Cache::init(); Notifier::init("", "8bithud"); Screen::get()->setNotificationsEnabled(true); // Special handling for gamecontrollerdb.txt - SDL needs filesystem path #ifdef RELEASE_BUILD // In release, construct the path manually (not from Asset which has empty executable_path) std::string gamecontroller_db = executable_path_ + PREFIX + "/gamecontrollerdb.txt"; Input::init(gamecontroller_db); #else // In development, use Asset as normal Input::init(Resource::List::get()->get("gamecontrollerdb.txt")); // Carga configuración de controles #endif // Aplica las teclas y botones del gamepad configurados desde Options Input::get()->applyKeyboardBindingsFromOptions(); Input::get()->applyGamepadBindingsFromOptions(); #ifdef _DEBUG Debug::init(); #endif std::cout << "\n"; // Fin de inicialización de sistemas } Director::~Director() { // Guarda las opciones a un fichero Options::saveToFile(); // Destruye los singletones #ifdef _DEBUG Debug::destroy(); #endif Input::destroy(); Notifier::destroy(); Resource::Cache::destroy(); Resource::Helper::shutdownResourceSystem(); // Shutdown resource pack system Audio::destroy(); Screen::destroy(); Resource::List::destroy(); SDL_Quit(); std::cout << "\nBye!" << '\n'; } // Comprueba los parametros del programa auto Director::checkProgramArguments(std::vector const& args) -> std::string { // Iterar sobre los argumentos del programa (saltando args[0] que es el ejecutable) for (std::size_t i = 1; i < args.size(); ++i) { const std::string& argument = args[i]; if (argument == "--console") { Options::console = true; } else if (argument == "--infiniteLives") { Options::cheats.infinite_lives = Options::Cheat::State::ENABLED; } else if (argument == "--invincible") { Options::cheats.invincible = Options::Cheat::State::ENABLED; } } return args[0]; } // Crea la carpeta del sistema donde guardar datos void Director::createSystemFolder(const std::string& folder) { #ifdef _WIN32 system_folder_ = std::string(getenv("APPDATA")) + "/" + folder; #elif __APPLE__ struct passwd* pw = getpwuid(getuid()); const char* homedir = pw->pw_dir; system_folder_ = std::string(homedir) + "/Library/Application Support" + "/" + folder; #elif __linux__ struct passwd* pw = getpwuid(getuid()); const char* homedir = pw->pw_dir; system_folder_ = std::string(homedir) + "/.config/" + folder; { // Intenta crear ".config", per si no existeix std::string config_base_folder = std::string(homedir) + "/.config"; int ret = mkdir(config_base_folder.c_str(), S_IRWXU); if (ret == -1 && errno != EEXIST) { printf("ERROR CREATING CONFIG BASE FOLDER."); exit(EXIT_FAILURE); } } #endif struct stat st = {.st_dev = 0}; if (stat(system_folder_.c_str(), &st) == -1) { errno = 0; #ifdef _WIN32 int ret = mkdir(system_folder_.c_str()); #else int ret = mkdir(system_folder_.c_str(), S_IRWXU); #endif if (ret == -1) { switch (errno) { case EACCES: printf("the parent directory does not allow write"); exit(EXIT_FAILURE); case EEXIST: printf("pathname already exists"); exit(EXIT_FAILURE); case ENAMETOOLONG: printf("pathname is too long"); exit(EXIT_FAILURE); default: perror("mkdir"); exit(EXIT_FAILURE); } } } } // Carga la configuración de assets desde assets.yaml void Director::setFileList() { // Determinar el prefijo de ruta según la plataforma #ifdef MACOS_BUNDLE const std::string PREFIX = "/../Resources"; #else const std::string PREFIX; #endif // Construir ruta al archivo de configuración de assets std::string config_path = executable_path_ + PREFIX + "/config/assets.yaml"; // Cargar todos los assets desde el archivo de configuración // La verificación de existencia de archivos se realiza durante Resource::Cache::load() Resource::List::get()->loadFromFile(config_path, PREFIX, system_folder_); } // Ejecuta la seccion de juego con el logo void Director::runLogo() { auto logo = std::make_unique(); logo->run(); } // Ejecuta la seccion de juego con el titulo y los menus void Director::runTitle() { auto title = std::make_unique(); title->run(); } // Ejecuta la seccion de juego donde se juega void Director::runGame() { Audio::get()->stopMusic(); auto game = std::make_unique<Game>(Game::Mode::GAME); game->run(); } auto Director::run() -> int { // Bucle principal while (SceneManager::current != SceneManager::Scene::QUIT) { switch (SceneManager::current) { case SceneManager::Scene::LOGO: runLogo(); break; case SceneManager::Scene::TITLE: runTitle(); break; case SceneManager::Scene::GAME: runGame(); break; default: break; } } return 0; }