#include "session.hpp" #include "os.hpp" #include "processes/pxi_fs.hpp" // Required to check if gamecard has an update partition #include #include #include #include #include #include #include #include std::unique_ptr LoadGameCard(spdlog::logger& logger, Settings::Settings& settings) { // TODO: Move gamecard initialization below setup so that we can gracefully display gamecard loading errors logger.info("Loading gamecard image"); auto gamecard = Meta::invoke([&]() { auto&& visitor = [](auto&& val) -> std::unique_ptr { if constexpr (std::is_same_v, Settings::InitialApplicationTag::HostFile>) { try { if (Loader::GameCardFrom3DSX::IsLoadableFile(val.filename)) return std::unique_ptr { new Loader::GameCardFrom3DSX(val.filename) }; else if (Loader::GameCardFromCXI::IsLoadableFile(val.filename)) return std::unique_ptr { new Loader::GameCardFromCXI(val.filename) }; else if (Loader::GameCardFromCCI::IsLoadableFile(val.filename)) return std::unique_ptr { new Loader::GameCardFromCCI(val.filename) }; } catch (std::ios_base::failure& err) { throw std::runtime_error(fmt::format("Could not load game file \"{}\"", val.filename)); } } else if constexpr (std::is_same_v, Settings::InitialApplicationTag::FileDescriptor>) { if (Loader::GameCardFrom3DSX::IsLoadableFile(val.fd)) return std::unique_ptr { new Loader::GameCardFrom3DSX(val.fd) }; else if (Loader::GameCardFromCXI::IsLoadableFile(val.fd)) return std::unique_ptr { new Loader::GameCardFromCXI(val.fd) }; else if (Loader::GameCardFromCCI::IsLoadableFile(val.fd)) return std::unique_ptr { new Loader::GameCardFromCCI(val.fd) }; } else if constexpr (std::is_same_v, std::monostate>) { return nullptr; } throw std::runtime_error("Unrecognized input file format. Input must be decrypted and in 3DSX, CCI, or CXI format."); }; return std::visit(visitor, settings.get()); }); if (settings.get() && gamecard) { try { // Attempt to open update partition required to boot gamecards from Home Menu. // If this throws, the partition does not exist (i.e. this is not a CCI file). // TODO: For CXI files, provide a dummy update partition gamecard->GetPartitionFromId(Loader::NCSDPartitionId::UpdateData); } catch (...) { logger.error("Can't use non-CCI/3DS file when booting home menu. Either redump as CCI or launch this title directly\n"); // TODO: Throw exception instead std::exit(1); } } return gamecard; } EmuSession::EmuSession( LogManager& log_manager, Settings::Settings& settings, VulkanDeviceManager& vulkan_device_manager, EmuDisplay::EmuDisplay& display, const KeyDatabase& keydb, std::unique_ptr gamecard) : setup(std::make_shared(log_manager, keydb, std::move(gamecard), profiler, debug_server)), pica { log_manager.RegisterLogger("Pica"), debug_server, settings, setup->mem, profiler, vulkan_device_manager.physical_device, *vulkan_device_manager.device, vulkan_device_manager.graphics_queue_index, vulkan_device_manager.graphics_queue } { setup->cpus[0].cpu.cpsr.mode = ARM::InternalProcessorMode::User; std::unique_ptr os_module; std::tie(setup->os, os_module) = HLE::OS::OS::Create(settings, *setup, log_manager, profiler, pica, display); for (auto& cpu : setup->cpus) cpu.os = setup->os.get(); setup->os->Initialize(); pica.InjectDependency(*setup->os.get()); pica.InjectDependency(setup->mem); setup->mem.InjectDependency(pica); setup->mem.InjectDependency(input); // TODO: Check if we can enable this on Android, too network_console = std::make_unique(12347); console_thread = std::thread { [os_module=std::move(os_module), &console=*network_console]() mutable { console.RegisterModule("os", std::move(os_module)); console.Run(); } }; #if ENABLE_PISTACHE std::thread debug_server_thread([&]() { try { debug_server.Run(3001); } catch (...) { // QMetaObject::invokeMethod(&window, "onEmulatorException", Qt::BlockingQueuedConnection, Q_ARG(std::exception_ptr, std::current_exception())); //// emuthread_exception = std::current_exception(); // while(true); } }); debug_server_thread.detach(); #endif } EmuSession::~EmuSession() { // Stop NetworkConsole first so that the console thread can return network_console->Stop(); console_thread.join(); // TODO: Shut down debug_server } void EmuSession::Run() { setup->os->Run(setup); }