#include "emulator.h" #include "common/abi.h" #include "common/assert.h" #include "common/emulatorConfig.h" #include "common/file.h" #include "common/logging/log.h" #include "common/profiler.h" #include "common/singleton.h" #include "common/stringUtils.h" #include "common/subsystems.h" #include "common/systemInfo.h" #include "common/threads.h" #include "graphics/presentation/window.h" #include "kernel/fileSystem.h" #include "kernel/memory.h" #include "kernel/pthread.h" #include "kytyGitVersion.h" #include "libs/agc.h" #include "libs/audio.h" #include "libs/controller.h" #include "libs/libs.h" #include "libs/network.h" #include "loader/runtimeLinker.h" #include "loader/systemContent.h" #include "loader/timer.h" #include #include #include namespace Emulator { static void PrintSystemInfo() { const Common::SystemInfo info = Common::GetSystemInfo(); #if defined(__APPLE__) static constexpr auto platform_name = "macOS"; #elif KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS static constexpr auto platform_name = "Windows"; #elif KYTY_PLATFORM == KYTY_PLATFORM_LINUX static constexpr auto platform_name = "Linux"; #else static constexpr auto platform_name = "Unknown"; #endif LOGF("Build\n" " version: %s\n\n" "Host\n" " os: %s\n" " cpu: %s\n" " threads: %u\n\n", KYTY_BUILD_LABEL, platform_name, info.ProcessorName.c_str(), std::thread::hardware_concurrency()); } static void KytyClose() { auto* rt = Common::Singleton::Instance(); rt->Clear(); LOGF("done!\n"); Common::Subsystems::EmergencyShutdownActive(); } static void MountOrCreateDir(const std::filesystem::path& dir, const std::string& point) { if (!Common::File::IsDirectoryExisting(dir)) { Common::File::CreateDirectories(dir); } EXIT_NOT_IMPLEMENTED(!Common::File::IsDirectoryExisting(dir)); Libs::LibKernel::FileSystem::Mount(dir, point); auto dir_text = Common::PathToString(dir); LOGF("Mounted %s -> %s\n", point.c_str(), dir_text.c_str()); } static void MountSandboxDirs() { std::string title_id; if (!Loader::SystemContentParamSfoGetString("TITLE_ID", &title_id) || title_id.empty()) { title_id = "UNKNOWN"; } MountOrCreateDir("_DownloadData/" + title_id, "/download0"); MountOrCreateDir("_TempData/" + title_id, "/temp0"); MountOrCreateDir("_TempData/" + title_id, "/temp"); } static bool ClearDirectoryContents(const std::filesystem::path& dir) { bool ok = true; for (const auto& entry: Common::File::GetDirEntries(dir)) { if (entry.name == "." || entry.name == "..") { continue; } auto path = dir / entry.name; if (entry.is_file) { Common::File::RemoveReadonly(path); ok = Common::File::DeleteFile(path) && ok; } else { ok = ClearDirectoryContents(path) && ok; ok = Common::File::DeleteDirectory(path) && ok; } } return ok; } static void ClearDebugTextureFolder() { const std::string debug_texture_folder = "_Textures"; if (!Common::File::IsDirectoryExisting(debug_texture_folder)) { Common::File::CreateDirectories(debug_texture_folder); return; } if (!ClearDirectoryContents(debug_texture_folder)) { LOGF_COLOR(Log::Color::BrightYellow, "TextureDump: failed to completely clear %s\n", debug_texture_folder.c_str()); } } static void Init(const Config::ConfigOptions& cfg, const std::filesystem::path& param_json, Common::Subsystems& subsystems) { EXIT_IF(!Common::Thread::IsMainThread()); subsystems.Initialize(); Config::Load(cfg); subsystems.Initialize(); if (Common::File::IsFileExisting(param_json)) { Loader::SystemContentLoadParamSfo(param_json); if (const auto flexible_memory_size = Loader::SystemContentGetFlexibleMemorySize(); flexible_memory_size != 0) { Libs::LibKernel::Memory::SetFlexibleMemorySize(flexible_memory_size); } } // Initialization order is explicit; destruction is automatic and reversed. subsystems.Initialize(); subsystems.Initialize(); subsystems.Initialize(); subsystems.Initialize(); subsystems.Initialize(); subsystems.Initialize(); subsystems.Initialize(); subsystems.Initialize(); subsystems.Initialize(); } static void LoadElf(const std::filesystem::path& elf, bool dbg_print_reloc = false, const std::filesystem::path& save_name = {}) { auto* rt = Common::Singleton::Instance(); auto* program = rt->LoadProgram( Libs::LibKernel::FileSystem::GetRealFilename(Common::PathToGenericString(elf))); if (dbg_print_reloc) { program->dbg_print_reloc = true; } if (!save_name.empty()) { rt->SaveProgram(program, Libs::LibKernel::FileSystem::GetRealFilename( Common::PathToGenericString(save_name))); } } static void Execute(const std::filesystem::path& game_patch) { auto patch_path = game_patch; Common::Thread guest_thread( [](void* param) { auto* rt = Common::Singleton::Instance(); rt->Execute(*static_cast(param)); }, &patch_path); Libs::Graphics::WindowRun(); std::quick_exit(0); } void Run(const RunOptions& options) { if (options.app0_dir.empty()) { EXIT("app0 directory is required\n"); } if (options.elf.empty()) { EXIT("ELF is required\n"); } const auto param_json = options.app0_dir / "sce_sys" / "param.json"; Common::Subsystems subsystems(true); Init(options.config, param_json, subsystems); ClearDebugTextureFolder(); PrintSystemInfo(); int ok = atexit(KytyClose); EXIT_NOT_IMPLEMENTED(ok != 0); // Guest threads are still running, so skip KytyClose() and only flush emergency state. ok = at_quick_exit(Common::Subsystems::EmergencyShutdownActive); EXIT_NOT_IMPLEMENTED(ok != 0); Libs::LibKernel::FileSystem::Mount(options.app0_dir, "/app0"); Libs::LibKernel::FileSystem::Mount(options.app0_dir, "/hostapp"); MountSandboxDirs(); auto* rt = Common::Singleton::Instance(); Libs::InitAll(rt->Symbols()); LoadElf(options.elf); Execute(options.game_patch); } } // namespace Emulator