diff --git a/include/dusk/main.h b/include/dusk/main.h index 0a2be8734d..a16291a8a7 100644 --- a/include/dusk/main.h +++ b/include/dusk/main.h @@ -12,6 +12,18 @@ extern bool RestartRequested; extern std::filesystem::path ConfigPath; extern std::filesystem::path CachePath; +extern uint8_t SaveRequested; +struct StageRequest { + std::string stage; + bool set; + s8 room; + s16 point; + s8 layer; +}; +extern StageRequest StageRequested; + + + #if defined(__ANDROID__) || (defined(TARGET_OS_IOS) && TARGET_OS_IOS) || \ (defined(TARGET_OS_TV) && TARGET_OS_TV) inline constexpr bool SupportsProcessRestart = false; diff --git a/src/d/d_s_logo.cpp b/src/d/d_s_logo.cpp index 570dfac577..d338775182 100644 --- a/src/d/d_s_logo.cpp +++ b/src/d/d_s_logo.cpp @@ -23,8 +23,12 @@ #include "m_Do/m_Do_main.h" #include "JSystem/JUtility/JUTConsole.h" +#ifdef TARGET_PC #include "dusk/logging.h" #include "dusk/version.hpp" +#include "dusk/main.h" +#include "m_Do/m_Do_MemCard.h" +#endif #if !PLATFORM_GCN #include @@ -757,7 +761,63 @@ void dScnLogo_c::nextSceneChange() { if (!mDoRst::isReset()) { if (!isOpeningCut()) { - dComIfG_changeOpeningScene(this, fpcNm_OPENING_SCENE_e); +#ifdef TARGET_PC + // If we are requesting a save from the command line, load it here and set the scene to play instead of loading the LOGO SCENE + if (dusk::SaveRequested >= 1 && dusk::SaveRequested <= 3) { + u8 buf[SAVEDATA_SIZE * 3]; + mDoMemCd_Load(); + uint8_t status; + do { + status = mDoMemCd_LoadSync(buf, sizeof(buf), 0); + // Wait until the card is loaded + } while (status == 0); + + if (status == 1) { + dComIfGs_setCardToMemory(buf, dusk::SaveRequested - 1); + } else { + dComIfGs_init(); + } + + dComIfGs_setNoFile(dusk::SaveRequested); + dComIfGs_setDataNum(dusk::SaveRequested-1); + + dComIfGs_gameStart(); + + fopScnM_ChangeReq(this, fpcNm_PLAY_SCENE_e, 0, 30); + + dKy_clear_game_init(); + dComIfGs_resetDan(); + dComIfGs_setRestartRoomParam(0); + + DuskLog.info("Loaded Save From Slot {}",dusk::SaveRequested); + dusk::SaveRequested = 0xff; + } else if (dusk::SaveRequested == 0xff) { + // This indicates that the save has loaded, but we are waiting for the scene + // manager to change to play + } else if (dusk::StageRequested.set) { + // Do nothing if we need to request a stage to load later in the function + } else{ +#endif + dComIfG_changeOpeningScene(this, fpcNm_OPENING_SCENE_e); +#ifdef TARGET_PC + } + + if (dusk::StageRequested.set) { + // If we aren't loading a save, initialize a blank save file and request the correct scene to load + if (dusk::SaveRequested == 0) { + dComIfGs_init(); + + fopScnM_ChangeReq(this, fpcNm_PLAY_SCENE_e, 0, 30); + dusk::SaveRequested = 0xff; //Skip requesting the scene from above + } + + // Use both to force-set start stage + dComIfGp_setNextStage(dusk::StageRequested.stage.c_str(), dusk::StageRequested.point, dusk::StageRequested.room, dusk::StageRequested.layer); + g_dComIfG_gameInfo.play.mNextStage.getStartStage()->set(dusk::StageRequested.stage.c_str(), dusk::StageRequested.room, dusk::StageRequested.point, dusk::StageRequested.layer); + + dusk::StageRequested.set = false; // Setting the stage should only happen once + } +#endif } else { #if DEBUG fopScnM_ChangeReq(this, fpcNm_MENU_SCENE_e, 0, 30); diff --git a/src/m_Do/m_Do_main.cpp b/src/m_Do/m_Do_main.cpp index ce029035b1..881135fc51 100644 --- a/src/m_Do/m_Do_main.cpp +++ b/src/m_Do/m_Do_main.cpp @@ -42,6 +42,7 @@ #include "m_Do/m_Do_ext2.h" #include "SSystem/SComponent/c_counter.h" #include +#include #include #include @@ -125,6 +126,8 @@ bool dusk::IsRunning = true; bool dusk::IsShuttingDown = false; bool dusk::IsGameLaunched = false; bool dusk::RestartRequested = false; +uint8_t dusk::SaveRequested = 0; +dusk::StageRequest dusk::StageRequested = {"",false}; std::filesystem::path dusk::ConfigPath; std::filesystem::path dusk::CachePath; #endif @@ -502,8 +505,6 @@ int game_main(int argc, char* argv[]) { } mainCalled = true; - dusk::registerSettings(); - cxxopts::ParseResult parsed_arg_options; try { @@ -516,7 +517,10 @@ int game_main(int argc, char* argv[]) { ("dvd", "Path to DVD image file", cxxopts::value()) ("mods", "Path to mods directory", cxxopts::value()) ("backend", "Graphics API backend to use (auto, d3d12, d3d11, metal, vulkan, null)", cxxopts::value()) - ("cvar", "Override configuration variables without modifying config", cxxopts::value>()); + ("cvar", "Override configuration variables without modifying config", cxxopts::value>()) + ("develop", "Enable the game's developer mode and OSReport for debugging", cxxopts::value()->default_value("false")->implicit_value("true")) + ("load-save", "Skip the opening and load a save from slot 1-3", cxxopts::value()->default_value("0")) + ("stage", "Upon launching, load a stage, room, spawn point, and layer. When using --load-save, it uses the specified save on the loaded stage. Format (STAGE,ROOM,POINT,LAYER). Example: (STAGE) or (STAGE,0,0,-1)", cxxopts::value()); arg_options.parse_positional({"dvd"}); arg_options.positional_help(""); @@ -529,11 +533,52 @@ int game_main(int argc, char* argv[]) { printf("%s", (arg_options.help() + "\n").c_str()); exit(0); } + + if (parsed_arg_options.count("stage")) { + std::stringstream ss(parsed_arg_options["stage"].as()); + std::string token; + + std::getline(ss,token,','); + std::string stageName = token; + s8 room = 0; + s16 point = 0; + s8 layer = -1; + if (std::getline(ss,token,',')) { + room = std::stoi(token); + if (std::getline(ss,token,',')) { + point = std::stoi(token); + if (std::getline(ss,token,',')) { + layer = std::stoi(token); + } + } + } + + dusk::StageRequested = {stageName,true, room,point,layer}; + } } catch (const cxxopts::exceptions::exception& e) { fprintf(stderr, "Argument Error: %s\n", e.what()); exit(1); } + catch (const std::invalid_argument& e) { + // Handle parsing std::stoi when loading a stage + fprintf(stderr, "Fatal: Invalid Argument When Parsing Stage\n"); + exit(1); + } + catch (const std::out_of_range& e) { + // Handle parsing std::stoi when loading a stage + fprintf(stderr, "Fatal: Argument Out of Range In Parsing Stage\n"); + exit(1); + } + + if (parsed_arg_options.contains("load-save")){ + uint8_t slot = parsed_arg_options["load-save"].as(); + if (slot >= 1 && slot <= 3) { + dusk::SaveRequested = slot; + } + } + + dusk::registerSettings(); const auto startupLogLevel = static_cast(parsed_arg_options["log-level"].as()); @@ -542,6 +587,12 @@ int game_main(int argc, char* argv[]) { dusk::CachePath = dataPaths.cachePath; dusk::InitializeFileLogging(dusk::CachePath, startupLogLevel); + // Development Mode + if (parsed_arg_options.count("develop")) { + mDoMain::developmentMode = parsed_arg_options["develop"].as(); // Enable Dev Mode for Debugging + dusk::OSReportReallyForceEnable = parsed_arg_options["develop"].as(); // Print OSReport to console + } + log_build_info(); dusk::config::load_from_user_preferences(); @@ -682,7 +733,7 @@ int game_main(int argc, char* argv[]) { saveConfigBeforePrelaunch = true; } - std::string dvd_path; + std::string dvd_path = dusk::getSettings().backend.isoPath; bool dvd_opened = false; if (parsed_arg_options.count("dvd")) { dvd_path = parsed_arg_options["dvd"].as(); @@ -705,6 +756,22 @@ int game_main(int argc, char* argv[]) { } } + bool skipPreLaunchUI = dusk::getSettings().backend.skipPreLaunchUI.getValue(); + + // If we can't load right into the game, stop requesting to load a stage or save + if (forcePreLaunchUI || dvd_path.empty()) { + if (dusk::StageRequested.set) { + DuskLog.warn("Cannot load stage {} because no iso path is set, opening prelaunch UI",dusk::StageRequested.stage); + dusk::StageRequested = {}; + } + if (dusk::SaveRequested) { + DuskLog.warn("Cannot load save {} because no iso path is set, opening prelaunch UI",dusk::SaveRequested); + dusk::SaveRequested = 0; + } + }else if (dusk::StageRequested.set || dusk::SaveRequested) { + skipPreLaunchUI = true; + } + dusk::iso::log_verification_state( dusk::getSettings().backend.isoPath.getValue(), dusk::getSettings().backend.isoVerification.getValue()); @@ -713,7 +780,7 @@ int game_main(int argc, char* argv[]) { if (dusk::getSettings().backend.isoPath.getValue().empty()) { forcePreLaunchUI = true; } - if (forcePreLaunchUI && dusk::getSettings().backend.skipPreLaunchUI.getValue()) { + if (forcePreLaunchUI && skipPreLaunchUI) { DuskLog.warn("Prelaunch UI was disabled with no usable DVD image, enabling prelaunch UI"); dusk::getSettings().backend.skipPreLaunchUI.setValue(false); saveConfigBeforePrelaunch = true; @@ -722,7 +789,7 @@ int game_main(int argc, char* argv[]) { dusk::config::save(); } - if (!dusk::getSettings().backend.skipPreLaunchUI) { + if (!skipPreLaunchUI) { dusk::ui::push_document(std::make_unique(), true); // pre game launch ui main loop @@ -741,7 +808,6 @@ int game_main(int argc, char* argv[]) { } dvd_path = dusk::getSettings().backend.isoPath; - if (dvd_path.empty()) { DuskLog.fatal("No DVD image specified, unable to boot!"); } @@ -784,8 +850,6 @@ int game_main(int argc, char* argv[]) { // Global Context Init dComIfG_ct(); - // Development Mode - // mDoMain::developmentMode = 1; // Force Dev Mode for Debugging mDoDvdThd::SyncWidthSound = false; // Mod search directories, highest priority first: user dir (--mods replaces it), then