#include "emulator.h" #include "common/abi.h" #include "common/assert.h" #include "common/commonSubsystem.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 "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 namespace Emulator { static void PrintSystemInfo() { Common::SystemInfo info = Common::GetSystemInfo(); LOGF("ProcessorName = %s\n", info.ProcessorName.c_str()); } static void KytyClose() { auto* rt = Common::Singleton::Instance(); rt->Clear(); LOGF("done!\n"); Common::SubsystemsListSingleton::Instance()->ShutdownAll(); } 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) { EXIT_IF(!Common::Thread::IsMainThread()); auto* slist = Common::SubsystemsList::Instance(); auto* audio = Libs::Audio::AudioSubsystem::Instance(); auto* config = Config::ConfigSubsystem::Instance(); auto* controller = Libs::Controller::ControllerSubsystem::Instance(); auto* core = Common::CommonSubsystem::Instance(); auto* file_system = Libs::LibKernel::FileSystem::FileSystemSubsystem::Instance(); auto* graphics = Libs::Graphics::GraphicsSubsystem::Instance(); auto* log = Log::LogSubsystem::Instance(); auto* memory = Libs::LibKernel::Memory::MemorySubsystem::Instance(); auto* network = Libs::Network::NetworkSubsystem::Instance(); auto* profiler = Profiler::ProfilerSubsystem::Instance(); auto* pthread = Libs::LibKernel::PthreadSubsystem::Instance(); auto* timer = Loader::Timer::TimerSubsystem::Instance(); slist->Add(config, {core}); slist->InitAll(true); Config::Load(cfg); slist->Add(log, {core, config}); slist->InitAll(true); 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); } } slist->Add(audio, {core, log, pthread, memory}); slist->Add(controller, {core, log, config}); slist->Add(file_system, {core, log, pthread}); slist->Add(graphics, {core, log, pthread, memory, config, profiler, controller}); slist->Add(memory, {core, log}); slist->Add(network, {core, log, pthread}); slist->Add(profiler, {core, config}); slist->Add(pthread, {core, log, timer}); slist->Add(timer, {core, log}); slist->InitAll(true); } 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"; Init(options.config, param_json); ClearDebugTextureFolder(); PrintSystemInfo(); int ok = atexit(KytyClose); 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