bool YamlPath::get(YAML::Node root, YAML::Node& out) { size_t beginToken = 0, endToken = 0, pathSize = codedPath.size(); auto delimiter = MAP_DELIM; // First token must be a map key. while (endToken < pathSize) { if (!root.IsDefined()) { return false; // A node before the end of the path was mising, quit! } beginToken = endToken; endToken = pathSize; endToken = std::min(endToken, codedPath.find(SEQ_DELIM, beginToken)); endToken = std::min(endToken, codedPath.find(MAP_DELIM, beginToken)); if (delimiter == SEQ_DELIM) { int index = std::stoi(&codedPath[beginToken]); if (root.IsSequence()) { root.reset(root[index]); } else { return false; } } else if (delimiter == MAP_DELIM) { auto key = codedPath.substr(beginToken, endToken - beginToken); if (root.IsMap()) { root.reset(root[key]); } else { return false; } } else { return false; // Path is malformed, return null node. } delimiter = codedPath[endToken]; // Get next character as the delimiter. ++endToken; // Move past the delimiter. } // Success! Assign the result. out.reset(root); return true; }
YAML::Node YamlPath::get(YAML::Node node) { size_t beginToken = 0, endToken = 0, pathSize = codedPath.size(); auto delimiter = MAP_DELIM; // First token must be a map key. while (endToken < pathSize) { beginToken = endToken; endToken = pathSize; endToken = std::min(endToken, codedPath.find(SEQ_DELIM, beginToken)); endToken = std::min(endToken, codedPath.find(MAP_DELIM, beginToken)); if (delimiter == SEQ_DELIM) { int index = std::stoi(&codedPath[beginToken]); node.reset(node[index]); } else if (delimiter == MAP_DELIM) { auto key = codedPath.substr(beginToken, endToken - beginToken); node.reset(node[key]); } else { return Node(); // Path is malformed, return null node. } delimiter = codedPath[endToken]; // Get next character as the delimiter. ++endToken; // Move past the delimiter. if (endToken < pathSize && !node) { return Node(); // A node in the path was missing, return null node. } } return node; }
TEST ResetNode() { YAML::Node node = YAML::Load("[1, 2, 3]"); YAML_ASSERT(!node.IsNull()); YAML::Node other = node; node.reset(); YAML_ASSERT(node.IsNull()); YAML_ASSERT(!other.IsNull()); node.reset(other); YAML_ASSERT(!node.IsNull()); YAML_ASSERT(other == node); return true; }
virtual Node* getNode(const char* key) { std::string path = key; YAML::Node result; for(size_t i = 0; i < entries.size(); i++) { YAML::Node node = entries[i]; size_t pos = 0; while(pos < path.size()) { std::string subpath; size_t end = path.find('.', pos); if(end == std::string::npos) { subpath = path.substr(pos); pos = path.size(); } else { subpath = path.substr(pos, end - pos); pos = end + 1; } if(!node.IsNull()) { node.reset(node[subpath]); } } if(!node.IsNull() && node.IsDefined()) { return new NodeImpl(node); } } return NULL; }
T get(const std::string& path, const T& fallback = T()) { for(size_t i = 0; i < entries.size(); i++) { YAML::Node node = entries[i]; size_t pos = 0; while(pos < path.size()) { std::string subpath; size_t end = path.find('.', pos); if(end == std::string::npos) { subpath = path.substr(pos); pos = path.size(); } else { subpath = path.substr(pos, end - pos); pos = end + 1; } if(!node.IsNull()) { node.reset(node[subpath]); } } if(!node.IsNull() && node.IsDefined()) { return node.as<T>(); } } return fallback; }
SettingsDialog::SettingsDialog(wxWindow* parent) : wxDialog(parent, wxID_ANY, "Settings", wxDefaultPosition) { // Load default config loaded = YAML::Load(g_cfg_defaults); // Incrementally load config.yml const fs::file config(fs::get_config_dir() + "/config.yml", fs::read + fs::write + fs::create); loaded += YAML::Load(config.to_string()); std::vector<std::unique_ptr<cfg_adapter>> pads; static const u32 width = 458; static const u32 height = 400; // Settings panels wxNotebook* nb_config = new wxNotebook(this, wxID_ANY, wxPoint(6, 6), wxSize(width, height)); wxPanel* p_system = new wxPanel(nb_config, wxID_ANY); wxPanel* p_core = new wxPanel(nb_config, wxID_ANY); wxPanel* p_graphics = new wxPanel(nb_config, wxID_ANY); wxPanel* p_audio = new wxPanel(nb_config, wxID_ANY); wxPanel* p_io = new wxPanel(nb_config, wxID_ANY); wxPanel* p_misc = new wxPanel(nb_config, wxID_ANY); wxPanel* p_networking = new wxPanel(nb_config, wxID_ANY); nb_config->AddPage(p_core, "Core"); nb_config->AddPage(p_graphics, "Graphics"); nb_config->AddPage(p_audio, "Audio"); nb_config->AddPage(p_io, "Input / Output"); nb_config->AddPage(p_misc, "Misc"); nb_config->AddPage(p_networking, "Networking"); nb_config->AddPage(p_system, "System"); wxBoxSizer* s_subpanel_core = new wxBoxSizer(wxHORIZONTAL); wxBoxSizer* s_subpanel_core1 = new wxBoxSizer(wxVERTICAL); wxBoxSizer* s_subpanel_core2 = new wxBoxSizer(wxVERTICAL); wxBoxSizer* s_subpanel_graphics = new wxBoxSizer(wxHORIZONTAL); wxBoxSizer* s_subpanel_graphics1 = new wxBoxSizer(wxVERTICAL); wxBoxSizer* s_subpanel_graphics2 = new wxBoxSizer(wxVERTICAL); wxBoxSizer* s_subpanel_audio = new wxBoxSizer(wxVERTICAL); wxBoxSizer* s_subpanel_io = new wxBoxSizer(wxHORIZONTAL); wxBoxSizer* s_subpanel_io1 = new wxBoxSizer(wxVERTICAL); wxBoxSizer* s_subpanel_io2 = new wxBoxSizer(wxVERTICAL); wxBoxSizer* s_subpanel_system = new wxBoxSizer(wxVERTICAL); wxBoxSizer* s_subpanel_misc = new wxBoxSizer(wxVERTICAL); wxBoxSizer* s_subpanel_networking = new wxBoxSizer(wxVERTICAL); // Core wxStaticBoxSizer* s_round_core_lle = new wxStaticBoxSizer(wxVERTICAL, p_core, "Load libraries"); chbox_list_core_lle = new wxCheckListBox(p_core, wxID_ANY, wxDefaultPosition, wxDefaultSize, {}, wxLB_EXTENDED); chbox_list_core_lle->Bind(wxEVT_CHECKLISTBOX, &SettingsDialog::OnModuleListItemToggled, this); wxTextCtrl* s_module_search_box = new wxTextCtrl(p_core, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, {}); s_module_search_box->Bind(wxEVT_TEXT, &SettingsDialog::OnSearchBoxTextChanged, this); // Graphics wxStaticBoxSizer* s_round_gs_render = new wxStaticBoxSizer(wxVERTICAL, p_graphics, "Rendering API"); wxStaticBoxSizer* s_round_gs_d3d_adapter = new wxStaticBoxSizer(wxVERTICAL, p_graphics, "D3D Adapter"); wxStaticBoxSizer* s_round_gs_res = new wxStaticBoxSizer(wxVERTICAL, p_graphics, "Resolution"); wxStaticBoxSizer* s_round_gs_aspect = new wxStaticBoxSizer(wxVERTICAL, p_graphics, "Aspect ratio"); wxStaticBoxSizer* s_round_gs_frame_limit = new wxStaticBoxSizer(wxVERTICAL, p_graphics, "Frame limit"); // Input / Output wxStaticBoxSizer* s_round_io_pad_handler = new wxStaticBoxSizer(wxVERTICAL, p_io, "Controller handler"); wxStaticBoxSizer* s_round_io_keyboard_handler = new wxStaticBoxSizer(wxVERTICAL, p_io, "Keyboard handler"); wxStaticBoxSizer* s_round_io_mouse_handler = new wxStaticBoxSizer(wxVERTICAL, p_io, "Mouse handler"); wxStaticBoxSizer* s_round_io_camera = new wxStaticBoxSizer(wxVERTICAL, p_io, "Camera"); wxStaticBoxSizer* s_round_io_camera_type = new wxStaticBoxSizer(wxVERTICAL, p_io, "Camera type"); // Audio wxStaticBoxSizer* s_round_audio_out = new wxStaticBoxSizer(wxVERTICAL, p_audio, "Audio out"); // Networking wxStaticBoxSizer* s_round_net_status = new wxStaticBoxSizer(wxVERTICAL, p_networking, "Connection status"); // System wxStaticBoxSizer* s_round_sys_lang = new wxStaticBoxSizer(wxVERTICAL, p_system, "Language"); wxRadioBox* rbox_ppu_decoder; wxRadioBox* rbox_spu_decoder; wxComboBox* cbox_gs_render = new wxComboBox(p_graphics, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(150, -1), 0, NULL, wxCB_READONLY); wxComboBox* cbox_gs_d3d_adapter = new wxComboBox(p_graphics, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(150, -1), 0, NULL, wxCB_READONLY); wxComboBox* cbox_gs_resolution = new wxComboBox(p_graphics, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(150, -1), 0, NULL, wxCB_READONLY); wxComboBox* cbox_gs_aspect = new wxComboBox(p_graphics, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(150, -1), 0, NULL, wxCB_READONLY); wxComboBox* cbox_gs_frame_limit = new wxComboBox(p_graphics, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(150, -1), 0, NULL, wxCB_READONLY); wxComboBox* cbox_pad_handler = new wxComboBox(p_io, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(150, -1), 0, NULL, wxCB_READONLY);; wxComboBox* cbox_keyboard_handler = new wxComboBox(p_io, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(150, -1), 0, NULL, wxCB_READONLY); wxComboBox* cbox_mouse_handler = new wxComboBox(p_io, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(150, -1), 0, NULL, wxCB_READONLY); wxComboBox* cbox_camera = new wxComboBox(p_io, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(150, -1), 0, NULL, wxCB_READONLY); wxComboBox* cbox_camera_type = new wxComboBox(p_io, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(150, -1), 0, NULL, wxCB_READONLY); wxComboBox* cbox_audio_out = new wxComboBox(p_audio, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(150, -1), 0, NULL, wxCB_READONLY); wxComboBox* cbox_net_status = new wxComboBox(p_networking, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY); wxComboBox* cbox_sys_lang = new wxComboBox(p_system, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY); wxCheckBox* chbox_core_hook_stfunc = new wxCheckBox(p_core, wxID_ANY, "Hook static functions"); wxCheckBox* chbox_core_load_liblv2 = new wxCheckBox(p_core, wxID_ANY, "Load liblv2.sprx"); wxCheckBox* chbox_vfs_enable_host_root = new wxCheckBox(p_system, wxID_ANY, "Enable /host_root/"); wxCheckBox* chbox_gs_log_prog = new wxCheckBox(p_graphics, wxID_ANY, "Log shader programs"); wxCheckBox* chbox_gs_dump_depth = new wxCheckBox(p_graphics, wxID_ANY, "Write depth buffer"); wxCheckBox* chbox_gs_dump_color = new wxCheckBox(p_graphics, wxID_ANY, "Write color buffers"); wxCheckBox* chbox_gs_read_color = new wxCheckBox(p_graphics, wxID_ANY, "Read color buffers"); wxCheckBox* chbox_gs_read_depth = new wxCheckBox(p_graphics, wxID_ANY, "Read depth buffer"); wxCheckBox* chbox_gs_vsync = new wxCheckBox(p_graphics, wxID_ANY, "VSync"); wxCheckBox* chbox_gs_debug_output = new wxCheckBox(p_graphics, wxID_ANY, "Debug output"); wxCheckBox* chbox_gs_overlay = new wxCheckBox(p_graphics, wxID_ANY, "Debug overlay"); wxCheckBox* chbox_gs_gl_legacy_buffers = new wxCheckBox(p_graphics, wxID_ANY, "Use legacy OpenGL buffers"); wxCheckBox* chbox_audio_dump = new wxCheckBox(p_audio, wxID_ANY, "Dump to file"); wxCheckBox* chbox_audio_conv = new wxCheckBox(p_audio, wxID_ANY, "Convert to 16-bit"); wxCheckBox* chbox_hle_exitonstop = new wxCheckBox(p_misc, wxID_ANY, "Exit RPCS3 when process finishes"); wxCheckBox* chbox_hle_always_start = new wxCheckBox(p_misc, wxID_ANY, "Always start after boot"); wxCheckBox* chbox_dbg_ap_systemcall = new wxCheckBox(p_misc, wxID_ANY, "Automatically pause at system call"); wxCheckBox* chbox_dbg_ap_functioncall = new wxCheckBox(p_misc, wxID_ANY, "Automatically pause at function call"); { // Sort string vector alphabetically static const auto sort_string_vector = [](std::vector<std::string>& vec) { std::sort(vec.begin(), vec.end(), [](const std::string &str1, const std::string &str2) { return str1 < str2; }); }; auto&& data = loaded["Core"]["Load libraries"].as<std::vector<std::string>, std::initializer_list<std::string>>({}); sort_string_vector(data); // List selected modules first for (const auto& unk : data) { lle_module_list.insert(lle_module_list.end(), std::pair<std::string, bool>(unk, true)); chbox_list_core_lle->Check(chbox_list_core_lle->Append(unk)); } const std::string& lle_dir = Emu.GetLibDir(); // TODO std::unordered_set<std::string> set(data.begin(), data.end()); std::vector<std::string> lle_module_list_unselected; for (const auto& prxf : fs::dir(lle_dir)) { // List found unselected modules if (!prxf.is_directory && ppu_prx_object(fs::file(lle_dir + prxf.name)) == elf_error::ok && !set.count(prxf.name)) { lle_module_list_unselected.push_back(prxf.name); } } sort_string_vector(lle_module_list_unselected); for (const auto& prxf : lle_module_list_unselected) { lle_module_list.insert(lle_module_list.end(), std::pair<std::string, bool>(prxf, false)); chbox_list_core_lle->Check(chbox_list_core_lle->Append(prxf), false); } lle_module_list_unselected.clear(); } radiobox_pad_helper ppu_decoder_modes({ "Core", "PPU decoder" }); rbox_ppu_decoder = new wxRadioBox(p_core, wxID_ANY, "PPU decoder", wxDefaultPosition, wxSize(-1, -1), ppu_decoder_modes, 1); pads.emplace_back(std::make_unique<radiobox_pad>(std::move(ppu_decoder_modes), rbox_ppu_decoder)); radiobox_pad_helper spu_decoder_modes({ "Core", "SPU decoder" }); rbox_spu_decoder = new wxRadioBox(p_core, wxID_ANY, "SPU decoder", wxDefaultPosition, wxSize(-1, -1), spu_decoder_modes, 1); pads.emplace_back(std::make_unique<radiobox_pad>(std::move(spu_decoder_modes), rbox_spu_decoder)); rbox_spu_decoder->Enable(3, false); // TODO pads.emplace_back(std::make_unique<checkbox_pad>(cfg_location{ "Core", "Hook static functions" }, chbox_core_hook_stfunc)); pads.emplace_back(std::make_unique<checkbox_pad>(cfg_location{ "Core", "Load liblv2.sprx only" }, chbox_core_load_liblv2)); pads.emplace_back(std::make_unique<checkbox_pad>(cfg_location{ "VFS", "Enable /host_root/" }, chbox_vfs_enable_host_root)); pads.emplace_back(std::make_unique<combobox_pad>(cfg_location{ "Video", "Rendering API" }, cbox_gs_render)); pads.emplace_back(std::make_unique<combobox_pad>(cfg_location{ "Video", "Resolution" }, cbox_gs_resolution)); pads.emplace_back(std::make_unique<combobox_pad>(cfg_location{ "Video", "Aspect ratio" }, cbox_gs_aspect)); pads.emplace_back(std::make_unique<combobox_pad>(cfg_location{ "Video", "Frame limit" }, cbox_gs_frame_limit)); pads.emplace_back(std::make_unique<checkbox_pad>(cfg_location{ "Video", "Log shader programs" }, chbox_gs_log_prog)); pads.emplace_back(std::make_unique<checkbox_pad>(cfg_location{ "Video", "Write depth buffer" }, chbox_gs_dump_depth)); pads.emplace_back(std::make_unique<checkbox_pad>(cfg_location{ "Video", "Write color buffers" }, chbox_gs_dump_color)); pads.emplace_back(std::make_unique<checkbox_pad>(cfg_location{ "Video", "Read color buffers" }, chbox_gs_read_color)); pads.emplace_back(std::make_unique<checkbox_pad>(cfg_location{ "Video", "Read depth buffer" }, chbox_gs_read_depth)); pads.emplace_back(std::make_unique<checkbox_pad>(cfg_location{ "Video", "VSync" }, chbox_gs_vsync)); pads.emplace_back(std::make_unique<checkbox_pad>(cfg_location{ "Video", "Debug output" }, chbox_gs_debug_output)); pads.emplace_back(std::make_unique<checkbox_pad>(cfg_location{ "Video", "Debug overlay" }, chbox_gs_overlay)); pads.emplace_back(std::make_unique<checkbox_pad>(cfg_location{ "Video", "Use legacy OpenGL buffers (Debug)" }, chbox_gs_gl_legacy_buffers)); pads.emplace_back(std::make_unique<combobox_pad>(cfg_location{ "Audio", "Renderer" }, cbox_audio_out)); pads.emplace_back(std::make_unique<checkbox_pad>(cfg_location{ "Audio", "Dump to file" }, chbox_audio_dump)); pads.emplace_back(std::make_unique<checkbox_pad>(cfg_location{ "Audio", "Convert to 16-bit" }, chbox_audio_conv)); pads.emplace_back(std::make_unique<combobox_pad>(cfg_location{ "Input/Output", "Controller" }, cbox_pad_handler)); pads.emplace_back(std::make_unique<combobox_pad>(cfg_location{ "Input/Output", "Keyboard" }, cbox_keyboard_handler)); pads.emplace_back(std::make_unique<combobox_pad>(cfg_location{ "Input/Output", "Mouse" }, cbox_mouse_handler)); pads.emplace_back(std::make_unique<combobox_pad>(cfg_location{ "Input/Output", "Camera" }, cbox_camera)); pads.emplace_back(std::make_unique<combobox_pad>(cfg_location{ "Input/Output", "Camera type" }, cbox_camera_type)); pads.emplace_back(std::make_unique<combobox_pad>(cfg_location{ "Net", "Connection status" }, cbox_net_status)); pads.emplace_back(std::make_unique<combobox_pad>(cfg_location{ "System", "Language" }, cbox_sys_lang)); pads.emplace_back(std::make_unique<checkbox_pad>(cfg_location{ "Miscellaneous", "Exit RPCS3 when process finishes" }, chbox_hle_exitonstop)); pads.emplace_back(std::make_unique<checkbox_pad>(cfg_location{ "Miscellaneous", "Always start after boot" }, chbox_hle_always_start)); pads.emplace_back(std::make_unique<checkbox_pad>(cfg_location{ "Miscellaneous", "Automatically pause at system call" }, chbox_dbg_ap_systemcall)); pads.emplace_back(std::make_unique<checkbox_pad>(cfg_location{ "Miscellaneous", "Automatically pause at function call" }, chbox_dbg_ap_functioncall)); #ifdef _MSC_VER Microsoft::WRL::ComPtr<IDXGIFactory4> dxgi_factory; if (SUCCEEDED(CreateDXGIFactory(IID_PPV_ARGS(&dxgi_factory)))) { Microsoft::WRL::ComPtr<IDXGIAdapter> adapter; for (UINT id = 0; dxgi_factory->EnumAdapters(id, adapter.ReleaseAndGetAddressOf()) != DXGI_ERROR_NOT_FOUND; id++) { DXGI_ADAPTER_DESC desc; adapter->GetDesc(&desc); cbox_gs_d3d_adapter->Append(desc.Description); } pads.emplace_back(std::make_unique<combobox_pad>(cfg_location{ "Video", "D3D12", "Adapter" }, cbox_gs_d3d_adapter)); } else #endif { cbox_gs_d3d_adapter->Enable(false); } // Core s_round_core_lle->Add(chbox_list_core_lle, wxSizerFlags().Border(wxALL, 5).Expand()); s_round_core_lle->Add(s_module_search_box, wxSizerFlags().Border(wxALL, 5).Expand()); // Rendering s_round_gs_render->Add(cbox_gs_render, wxSizerFlags().Border(wxALL, 5).Expand()); s_round_gs_d3d_adapter->Add(cbox_gs_d3d_adapter, wxSizerFlags().Border(wxALL, 5).Expand()); s_round_gs_res->Add(cbox_gs_resolution, wxSizerFlags().Border(wxALL, 5).Expand()); s_round_gs_aspect->Add(cbox_gs_aspect, wxSizerFlags().Border(wxALL, 5).Expand()); s_round_gs_frame_limit->Add(cbox_gs_frame_limit, wxSizerFlags().Border(wxALL, 5).Expand()); // Input/Output s_round_io_pad_handler->Add(cbox_pad_handler, wxSizerFlags().Border(wxALL, 5).Expand()); s_round_io_keyboard_handler->Add(cbox_keyboard_handler, wxSizerFlags().Border(wxALL, 5).Expand()); s_round_io_mouse_handler->Add(cbox_mouse_handler, wxSizerFlags().Border(wxALL, 5).Expand()); s_round_io_camera->Add(cbox_camera, wxSizerFlags().Border(wxALL, 5).Expand()); s_round_io_camera_type->Add(cbox_camera_type, wxSizerFlags().Border(wxALL, 5).Expand()); s_round_audio_out->Add(cbox_audio_out, wxSizerFlags().Border(wxALL, 5).Expand()); // Networking s_round_net_status->Add(cbox_net_status, wxSizerFlags().Border(wxALL, 5).Expand()); // System s_round_sys_lang->Add(cbox_sys_lang, wxSizerFlags().Border(wxALL, 5).Expand()); // Core s_subpanel_core1->Add(rbox_ppu_decoder, wxSizerFlags().Border(wxALL, 5).Expand()); s_subpanel_core1->Add(rbox_spu_decoder, wxSizerFlags().Border(wxALL, 5).Expand()); s_subpanel_core1->Add(chbox_core_hook_stfunc, wxSizerFlags().Border(wxALL, 5).Expand()); s_subpanel_core1->Add(chbox_core_load_liblv2, wxSizerFlags().Border(wxALL, 5).Expand()); s_subpanel_core2->Add(s_round_core_lle, wxSizerFlags().Border(wxALL, 5).Expand()); s_subpanel_core->Add(s_subpanel_core1); s_subpanel_core->Add(s_subpanel_core2); // Graphics s_subpanel_graphics1->Add(s_round_gs_render, wxSizerFlags().Border(wxALL, 5).Expand()); s_subpanel_graphics1->Add(s_round_gs_res, wxSizerFlags().Border(wxALL, 5).Expand()); s_subpanel_graphics1->Add(s_round_gs_d3d_adapter, wxSizerFlags().Border(wxALL, 5).Expand()); s_subpanel_graphics1->Add(chbox_gs_dump_color, wxSizerFlags().Border(wxALL, 5).Expand()); s_subpanel_graphics1->Add(chbox_gs_read_color, wxSizerFlags().Border(wxALL, 5).Expand()); s_subpanel_graphics1->Add(chbox_gs_dump_depth, wxSizerFlags().Border(wxALL, 5).Expand()); s_subpanel_graphics1->Add(chbox_gs_read_depth, wxSizerFlags().Border(wxALL, 5).Expand()); s_subpanel_graphics1->Add(chbox_gs_gl_legacy_buffers, wxSizerFlags().Border(wxALL, 5).Expand()); s_subpanel_graphics2->Add(s_round_gs_aspect, wxSizerFlags().Border(wxALL, 5).Expand()); s_subpanel_graphics2->Add(s_round_gs_frame_limit, wxSizerFlags().Border(wxALL, 5).Expand()); s_subpanel_graphics2->AddSpacer(68); s_subpanel_graphics2->Add(chbox_gs_debug_output, wxSizerFlags().Border(wxALL, 5).Expand()); s_subpanel_graphics2->Add(chbox_gs_overlay, wxSizerFlags().Border(wxALL, 5).Expand()); s_subpanel_graphics2->Add(chbox_gs_log_prog, wxSizerFlags().Border(wxALL, 5).Expand()); s_subpanel_graphics2->Add(chbox_gs_vsync, wxSizerFlags().Border(wxALL, 5).Expand()); s_subpanel_graphics->Add(s_subpanel_graphics1); s_subpanel_graphics->Add(s_subpanel_graphics2); // Input - Output s_subpanel_io1->Add(s_round_io_pad_handler, wxSizerFlags().Border(wxALL, 5).Expand()); s_subpanel_io1->Add(s_round_io_keyboard_handler, wxSizerFlags().Border(wxALL, 5).Expand()); s_subpanel_io1->Add(s_round_io_mouse_handler, wxSizerFlags().Border(wxALL, 5).Expand()); s_subpanel_io2->Add(s_round_io_camera, wxSizerFlags().Border(wxALL, 5).Expand()); s_subpanel_io2->Add(s_round_io_camera_type, wxSizerFlags().Border(wxALL, 5).Expand()); s_subpanel_io->Add(s_subpanel_io1); s_subpanel_io->Add(s_subpanel_io2); // Audio s_subpanel_audio->Add(s_round_audio_out, wxSizerFlags().Border(wxALL, 5).Expand()); s_subpanel_audio->Add(chbox_audio_dump, wxSizerFlags().Border(wxALL, 5).Expand()); s_subpanel_audio->Add(chbox_audio_conv, wxSizerFlags().Border(wxALL, 5).Expand()); // Miscellaneous s_subpanel_misc->Add(chbox_hle_exitonstop, wxSizerFlags().Border(wxALL, 5).Expand()); s_subpanel_misc->Add(chbox_hle_always_start, wxSizerFlags().Border(wxALL, 5).Expand()); // Auto Pause s_subpanel_misc->Add(chbox_dbg_ap_systemcall, wxSizerFlags().Border(wxALL, 5).Expand()); s_subpanel_misc->Add(chbox_dbg_ap_functioncall, wxSizerFlags().Border(wxALL, 5).Expand()); // Networking s_subpanel_networking->Add(s_round_net_status, wxSizerFlags().Border(wxALL, 5).Expand()); // System s_subpanel_system->Add(chbox_vfs_enable_host_root, wxSizerFlags().Border(wxALL, 5).Expand()); s_subpanel_system->Add(s_round_sys_lang, wxSizerFlags().Border(wxALL, 5).Expand()); // Buttons wxBoxSizer* s_b_panel(new wxBoxSizer(wxHORIZONTAL)); s_b_panel->Add(new wxButton(this, wxID_OK), wxSizerFlags().Border(wxALL, 5).Bottom()); s_b_panel->Add(new wxButton(this, wxID_CANCEL), wxSizerFlags().Border(wxALL, 5).Bottom()); // Resize panels SetSizerAndFit(s_subpanel_core, false); SetSizerAndFit(s_subpanel_graphics, false); SetSizerAndFit(s_subpanel_io, false); SetSizerAndFit(s_subpanel_audio, false); SetSizerAndFit(s_subpanel_misc, false); SetSizerAndFit(s_subpanel_networking, false); SetSizerAndFit(s_subpanel_system, false); SetSizerAndFit(s_b_panel, false); SetSize(width + 26, height + 80); if (ShowModal() == wxID_OK) { std::set<std::string> lle_selected; for (auto& i : lle_module_list) { if (i.second) // selected { lle_selected.emplace(i.first); } } saved.reset(); saved["Core"]["Load libraries"] = std::vector<std::string>(lle_selected.begin(), lle_selected.end()); for (auto& pad : pads) { pad->save(); } loaded += saved; YAML::Emitter out; emit(out, loaded); // Save config config.seek(0); config.trunc(0); config.write(out.c_str(), out.size()); } }
void Emulator::Load(bool add_only) { Stop(); try { Init(); // Load game list (maps ABCD12345 IDs to /dev_bdvd/ locations) YAML::Node games = YAML::Load(fs::file{fs::get_config_dir() + "/games.yml", fs::read + fs::create}.to_string()); if (!games.IsMap()) { games.reset(); } LOG_NOTICE(LOADER, "Path: %s", m_path); const std::string elf_dir = fs::get_parent_dir(m_path); // Load PARAM.SFO (TODO) const auto _psf = psf::load_object([&] { if (fs::file sfov{elf_dir + "/sce_sys/param.sfo"}) { return sfov; } else { return fs::file(elf_dir + "/../PARAM.SFO"); } }()); m_title = psf::get_string(_psf, "TITLE", m_path); m_title_id = psf::get_string(_psf, "TITLE_ID"); const auto _cat = psf::get_string(_psf, "CATEGORY"); LOG_NOTICE(LOADER, "Title: %s", GetTitle()); LOG_NOTICE(LOADER, "Serial: %s", GetTitleID()); // Initialize data/cache directory m_cache_path = fs::get_data_dir(m_title_id, m_path); LOG_NOTICE(LOADER, "Cache: %s", GetCachePath()); // Load custom config-0 if (fs::file cfg_file{m_cache_path + "/config.yml"}) { LOG_NOTICE(LOADER, "Applying custom config: %s/config.yml", m_cache_path); g_cfg.from_string(cfg_file.to_string()); } // Load custom config-1 if (fs::file cfg_file{fs::get_config_dir() + "data/" + m_title_id + "/config.yml"}) { LOG_NOTICE(LOADER, "Applying custom config: data/%s/config.yml", m_title_id); g_cfg.from_string(cfg_file.to_string()); } // Load custom config-2 if (fs::file cfg_file{m_path + ".yml"}) { LOG_NOTICE(LOADER, "Applying custom config: %s.yml", m_path); g_cfg.from_string(cfg_file.to_string()); } LOG_NOTICE(LOADER, "Used configuration:\n%s\n", g_cfg.to_string()); // Load patches from different locations fxm::check_unlocked<patch_engine>()->append(fs::get_config_dir() + "data/" + m_title_id + "/patch.yml"); fxm::check_unlocked<patch_engine>()->append(m_cache_path + "/patch.yml"); // Mount all devices const std::string emu_dir_ = g_cfg.vfs.emulator_dir; const std::string emu_dir = emu_dir_.empty() ? fs::get_config_dir() : emu_dir_; const std::string home_dir = g_cfg.vfs.app_home; std::string bdvd_dir = g_cfg.vfs.dev_bdvd; vfs::mount("dev_hdd0", fmt::replace_all(g_cfg.vfs.dev_hdd0, "$(EmulatorDir)", emu_dir)); vfs::mount("dev_hdd1", fmt::replace_all(g_cfg.vfs.dev_hdd1, "$(EmulatorDir)", emu_dir)); vfs::mount("dev_flash", fmt::replace_all(g_cfg.vfs.dev_flash, "$(EmulatorDir)", emu_dir)); vfs::mount("dev_usb", fmt::replace_all(g_cfg.vfs.dev_usb000, "$(EmulatorDir)", emu_dir)); vfs::mount("dev_usb000", fmt::replace_all(g_cfg.vfs.dev_usb000, "$(EmulatorDir)", emu_dir)); vfs::mount("app_home", home_dir.empty() ? elf_dir + '/' : fmt::replace_all(home_dir, "$(EmulatorDir)", emu_dir)); // Detect boot location const std::string hdd0_game = vfs::get("/dev_hdd0/game/"); const std::string hdd0_disc = vfs::get("/dev_hdd0/disc/"); if (_cat == "DG" && m_path.find(hdd0_game + m_title_id + '/') != -1) { // Booting disc game from wrong location LOG_ERROR(LOADER, "Disc game found at invalid location: /dev_hdd0/game/%s/", m_title_id); // Move and retry from correct location if (fs::rename(hdd0_game + m_title_id, hdd0_disc + m_title_id)) { LOG_SUCCESS(LOADER, "Disc game moved to special location: /dev_hdd0/disc/%s/", m_title_id); return SetPath(hdd0_disc + m_path.substr(hdd0_game.size())), Load(); } else { LOG_ERROR(LOADER, "Failed to move disc game to /dev_hdd0/disc/%s/ (%s)", m_title_id, fs::g_tls_error); return; } } // Booting disc game if (_cat == "DG" && bdvd_dir.empty()) { // Mount /dev_bdvd/ if necessary if (auto pos = elf_dir.rfind("/PS3_GAME") + 1) { bdvd_dir = elf_dir.substr(0, pos); } } // Booting patch data if (_cat == "GD" && bdvd_dir.empty()) { // Load /dev_bdvd/ from game list if available if (auto node = games[m_title_id]) { bdvd_dir = node.Scalar(); } else { LOG_FATAL(LOADER, "Disc directory not found. Try to run the game from the actual game disc directory."); } } // Check /dev_bdvd/ if (!bdvd_dir.empty() && fs::is_dir(bdvd_dir)) { fs::file sfb_file; vfs::mount("dev_bdvd", bdvd_dir); LOG_NOTICE(LOADER, "Disc: %s", vfs::get("/dev_bdvd")); if (!sfb_file.open(vfs::get("/dev_bdvd/PS3_DISC.SFB")) || sfb_file.size() < 4 || sfb_file.read<u32>() != ".SFB"_u32) { LOG_ERROR(LOADER, "Invalid disc directory for the disc game %s", m_title_id); return; } const std::string bdvd_title_id = psf::get_string(psf::load_object(fs::file{vfs::get("/dev_bdvd/PS3_GAME/PARAM.SFO")}), "TITLE_ID"); if (bdvd_title_id != m_title_id) { LOG_ERROR(LOADER, "Unexpected disc directory for the disc game %s (found %s)", m_title_id, bdvd_title_id); return; } // Store /dev_bdvd/ location games[m_title_id] = bdvd_dir; YAML::Emitter out; out << games; fs::file(fs::get_config_dir() + "/games.yml", fs::rewrite).write(out.c_str(), out.size()); } else if (_cat == "DG" || _cat == "GD") { LOG_ERROR(LOADER, "Failed to mount disc directory for the disc game %s", m_title_id); return; } if (add_only) { LOG_NOTICE(LOADER, "Finished to add data to games.yml by boot for: %s", m_path); return; } // Check game updates const std::string hdd0_boot = hdd0_game + m_title_id + "/USRDIR/EBOOT.BIN"; if (_cat == "DG" && fs::is_file(hdd0_boot)) { // Booting game update LOG_SUCCESS(LOADER, "Updates found at /dev_hdd0/game/%s/!", m_title_id); return SetPath(hdd0_boot), Load(); } // Mount /host_root/ if necessary if (g_cfg.vfs.host_root) { vfs::mount("host_root", {}); } // Open SELF or ELF fs::file elf_file(m_path); if (!elf_file) { LOG_ERROR(LOADER, "Failed to open executable: %s", m_path); return; } // Check SELF header if (elf_file.size() >= 4 && elf_file.read<u32>() == "SCE\0"_u32) { const std::string decrypted_path = m_cache_path + "boot.elf"; fs::stat_t encrypted_stat = elf_file.stat(); fs::stat_t decrypted_stat; // Check modification time and try to load decrypted ELF if (fs::stat(decrypted_path, decrypted_stat) && decrypted_stat.mtime == encrypted_stat.mtime) { elf_file.open(decrypted_path); } else { // Decrypt SELF elf_file = decrypt_self(std::move(elf_file)); if (fs::file elf_out{decrypted_path, fs::rewrite}) { elf_out.write(elf_file.to_vector<u8>()); elf_out.close(); fs::utime(decrypted_path, encrypted_stat.atime, encrypted_stat.mtime); } else { LOG_ERROR(LOADER, "Failed to create boot.elf"); } } } ppu_exec_object ppu_exec; ppu_prx_object ppu_prx; spu_exec_object spu_exec; arm_exec_object arm_exec; if (!elf_file) { LOG_ERROR(LOADER, "Failed to decrypt SELF: %s", m_path); return; } else if (ppu_exec.open(elf_file) == elf_error::ok) { // PS3 executable g_system = system_type::ps3; m_state = system_state::ready; GetCallbacks().on_ready(); vm::ps3::init(); if (m_elf_path.empty()) { if (m_path.find(hdd0_game) != -1) { m_elf_path = "/dev_hdd0/game/" + m_path.substr(hdd0_game.size()); } else if (!bdvd_dir.empty() && fs::is_dir(bdvd_dir)) { //Disc games are on /dev_bdvd/ size_t pos = m_path.rfind("PS3_GAME"); m_elf_path = "/dev_bdvd/" + m_path.substr(pos); } else { //For homebrew m_elf_path = "/host_root/" + m_path; } LOG_NOTICE(LOADER, "Elf path: %s", m_elf_path); } ppu_load_exec(ppu_exec); fxm::import<GSRender>(Emu.GetCallbacks().get_gs_render); // TODO: must be created in appropriate sys_rsx syscall } else if (ppu_prx.open(elf_file) == elf_error::ok) { // PPU PRX (experimental) g_system = system_type::ps3; m_state = system_state::ready; GetCallbacks().on_ready(); vm::ps3::init(); ppu_load_prx(ppu_prx, m_path); } else if (spu_exec.open(elf_file) == elf_error::ok) { // SPU executable (experimental) g_system = system_type::ps3; m_state = system_state::ready; GetCallbacks().on_ready(); vm::ps3::init(); spu_load_exec(spu_exec); } else if (arm_exec.open(elf_file) == elf_error::ok) { // ARMv7 executable g_system = system_type::psv; m_state = system_state::ready; GetCallbacks().on_ready(); vm::psv::init(); if (m_elf_path.empty()) { m_elf_path = "host_root:" + m_path; LOG_NOTICE(LOADER, "Elf path: %s", m_elf_path); } arm_load_exec(arm_exec); } else { LOG_ERROR(LOADER, "Invalid or unsupported file format: %s", m_path); LOG_WARNING(LOADER, "** ppu_exec -> %s", ppu_exec.get_error()); LOG_WARNING(LOADER, "** ppu_prx -> %s", ppu_prx.get_error()); LOG_WARNING(LOADER, "** spu_exec -> %s", spu_exec.get_error()); LOG_WARNING(LOADER, "** arm_exec -> %s", arm_exec.get_error()); return; } if (g_cfg.misc.autostart && IsReady()) { Run(); } else if (IsPaused()) { m_state = system_state::ready; GetCallbacks().on_ready(); } } catch (const std::exception& e) { LOG_FATAL(LOADER, "%s thrown: %s", typeid(e).name(), e.what()); Stop(); } }