diff options
| author | 2019-06-21 11:42:38 -0400 | |
|---|---|---|
| committer | 2019-06-21 11:42:38 -0400 | |
| commit | 100ed88e15e9ece96506795aa8a079a55cdbad57 (patch) | |
| tree | 9b961f52134c9a7f8bee354828b8bf6e5dc9205b /src | |
| parent | Merge pull request #2596 from FernandoS27/revert-2590 (diff) | |
| parent | yuzutest: Add minor comments (diff) | |
| download | yuzu-100ed88e15e9ece96506795aa8a079a55cdbad57.tar.gz yuzu-100ed88e15e9ece96506795aa8a079a55cdbad57.tar.xz yuzu-100ed88e15e9ece96506795aa8a079a55cdbad57.zip | |
Merge pull request #2291 from DarkLordZach/homebrew-testing
yuzu_tester: Add and implement testing utility for homebrew
Diffstat (limited to 'src')
| -rw-r--r-- | src/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/yuzu_tester/CMakeLists.txt | 34 | ||||
| -rw-r--r-- | src/yuzu_tester/config.cpp | 184 | ||||
| -rw-r--r-- | src/yuzu_tester/config.h | 24 | ||||
| -rw-r--r-- | src/yuzu_tester/default_ini.h | 150 | ||||
| -rw-r--r-- | src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp | 122 | ||||
| -rw-r--r-- | src/yuzu_tester/emu_window/emu_window_sdl2_hide.h | 41 | ||||
| -rw-r--r-- | src/yuzu_tester/resource.h | 16 | ||||
| -rw-r--r-- | src/yuzu_tester/service/yuzutest.cpp | 112 | ||||
| -rw-r--r-- | src/yuzu_tester/service/yuzutest.h | 25 | ||||
| -rw-r--r-- | src/yuzu_tester/yuzu.cpp | 267 | ||||
| -rw-r--r-- | src/yuzu_tester/yuzu.rc | 17 |
12 files changed, 993 insertions, 0 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 04018233f..f18239edb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt | |||
| @@ -88,6 +88,7 @@ add_subdirectory(tests) | |||
| 88 | 88 | ||
| 89 | if (ENABLE_SDL2) | 89 | if (ENABLE_SDL2) |
| 90 | add_subdirectory(yuzu_cmd) | 90 | add_subdirectory(yuzu_cmd) |
| 91 | add_subdirectory(yuzu_tester) | ||
| 91 | endif() | 92 | endif() |
| 92 | 93 | ||
| 93 | if (ENABLE_QT) | 94 | if (ENABLE_QT) |
diff --git a/src/yuzu_tester/CMakeLists.txt b/src/yuzu_tester/CMakeLists.txt new file mode 100644 index 000000000..06c2ee011 --- /dev/null +++ b/src/yuzu_tester/CMakeLists.txt | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules) | ||
| 2 | |||
| 3 | add_executable(yuzu-tester | ||
| 4 | config.cpp | ||
| 5 | config.h | ||
| 6 | default_ini.h | ||
| 7 | emu_window/emu_window_sdl2_hide.cpp | ||
| 8 | emu_window/emu_window_sdl2_hide.h | ||
| 9 | resource.h | ||
| 10 | service/yuzutest.cpp | ||
| 11 | service/yuzutest.h | ||
| 12 | yuzu.cpp | ||
| 13 | yuzu.rc | ||
| 14 | ) | ||
| 15 | |||
| 16 | create_target_directory_groups(yuzu-tester) | ||
| 17 | |||
| 18 | target_link_libraries(yuzu-tester PRIVATE common core input_common) | ||
| 19 | target_link_libraries(yuzu-tester PRIVATE inih glad) | ||
| 20 | if (MSVC) | ||
| 21 | target_link_libraries(yuzu-tester PRIVATE getopt) | ||
| 22 | endif() | ||
| 23 | target_link_libraries(yuzu-tester PRIVATE ${PLATFORM_LIBRARIES} SDL2 Threads::Threads) | ||
| 24 | |||
| 25 | if(UNIX AND NOT APPLE) | ||
| 26 | install(TARGETS yuzu-tester RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") | ||
| 27 | endif() | ||
| 28 | |||
| 29 | if (MSVC) | ||
| 30 | include(CopyYuzuSDLDeps) | ||
| 31 | include(CopyYuzuUnicornDeps) | ||
| 32 | copy_yuzu_SDL_deps(yuzu-tester) | ||
| 33 | copy_yuzu_unicorn_deps(yuzu-tester) | ||
| 34 | endif() | ||
diff --git a/src/yuzu_tester/config.cpp b/src/yuzu_tester/config.cpp new file mode 100644 index 000000000..d7e0d408d --- /dev/null +++ b/src/yuzu_tester/config.cpp | |||
| @@ -0,0 +1,184 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <memory> | ||
| 6 | #include <sstream> | ||
| 7 | #include <SDL.h> | ||
| 8 | #include <inih/cpp/INIReader.h> | ||
| 9 | #include "common/file_util.h" | ||
| 10 | #include "common/logging/log.h" | ||
| 11 | #include "common/param_package.h" | ||
| 12 | #include "core/hle/service/acc/profile_manager.h" | ||
| 13 | #include "core/settings.h" | ||
| 14 | #include "input_common/main.h" | ||
| 15 | #include "yuzu_tester/config.h" | ||
| 16 | #include "yuzu_tester/default_ini.h" | ||
| 17 | |||
| 18 | Config::Config() { | ||
| 19 | // TODO: Don't hardcode the path; let the frontend decide where to put the config files. | ||
| 20 | sdl2_config_loc = | ||
| 21 | FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "sdl2-tester-config.ini"; | ||
| 22 | sdl2_config = std::make_unique<INIReader>(sdl2_config_loc); | ||
| 23 | |||
| 24 | Reload(); | ||
| 25 | } | ||
| 26 | |||
| 27 | Config::~Config() = default; | ||
| 28 | |||
| 29 | bool Config::LoadINI(const std::string& default_contents, bool retry) { | ||
| 30 | const char* location = this->sdl2_config_loc.c_str(); | ||
| 31 | if (sdl2_config->ParseError() < 0) { | ||
| 32 | if (retry) { | ||
| 33 | LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...", location); | ||
| 34 | FileUtil::CreateFullPath(location); | ||
| 35 | FileUtil::WriteStringToFile(true, default_contents, location); | ||
| 36 | sdl2_config = std::make_unique<INIReader>(location); // Reopen file | ||
| 37 | |||
| 38 | return LoadINI(default_contents, false); | ||
| 39 | } | ||
| 40 | LOG_ERROR(Config, "Failed."); | ||
| 41 | return false; | ||
| 42 | } | ||
| 43 | LOG_INFO(Config, "Successfully loaded {}", location); | ||
| 44 | return true; | ||
| 45 | } | ||
| 46 | |||
| 47 | void Config::ReadValues() { | ||
| 48 | // Controls | ||
| 49 | for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { | ||
| 50 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { | ||
| 51 | Settings::values.players[p].buttons[i] = ""; | ||
| 52 | } | ||
| 53 | |||
| 54 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { | ||
| 55 | Settings::values.players[p].analogs[i] = ""; | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | Settings::values.mouse_enabled = false; | ||
| 60 | for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) { | ||
| 61 | Settings::values.mouse_buttons[i] = ""; | ||
| 62 | } | ||
| 63 | |||
| 64 | Settings::values.motion_device = ""; | ||
| 65 | |||
| 66 | Settings::values.keyboard_enabled = false; | ||
| 67 | |||
| 68 | Settings::values.debug_pad_enabled = false; | ||
| 69 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { | ||
| 70 | Settings::values.debug_pad_buttons[i] = ""; | ||
| 71 | } | ||
| 72 | |||
| 73 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { | ||
| 74 | Settings::values.debug_pad_analogs[i] = ""; | ||
| 75 | } | ||
| 76 | |||
| 77 | Settings::values.touchscreen.enabled = ""; | ||
| 78 | Settings::values.touchscreen.device = ""; | ||
| 79 | Settings::values.touchscreen.finger = 0; | ||
| 80 | Settings::values.touchscreen.rotation_angle = 0; | ||
| 81 | Settings::values.touchscreen.diameter_x = 15; | ||
| 82 | Settings::values.touchscreen.diameter_y = 15; | ||
| 83 | |||
| 84 | // Data Storage | ||
| 85 | Settings::values.use_virtual_sd = | ||
| 86 | sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true); | ||
| 87 | FileUtil::GetUserPath(FileUtil::UserPath::NANDDir, | ||
| 88 | sdl2_config->Get("Data Storage", "nand_directory", | ||
| 89 | FileUtil::GetUserPath(FileUtil::UserPath::NANDDir))); | ||
| 90 | FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir, | ||
| 91 | sdl2_config->Get("Data Storage", "sdmc_directory", | ||
| 92 | FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))); | ||
| 93 | |||
| 94 | // System | ||
| 95 | Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false); | ||
| 96 | const auto size = sdl2_config->GetInteger("System", "users_size", 0); | ||
| 97 | |||
| 98 | Settings::values.current_user = std::clamp<int>( | ||
| 99 | sdl2_config->GetInteger("System", "current_user", 0), 0, Service::Account::MAX_USERS - 1); | ||
| 100 | |||
| 101 | const auto rng_seed_enabled = sdl2_config->GetBoolean("System", "rng_seed_enabled", false); | ||
| 102 | if (rng_seed_enabled) { | ||
| 103 | Settings::values.rng_seed = sdl2_config->GetInteger("System", "rng_seed", 0); | ||
| 104 | } else { | ||
| 105 | Settings::values.rng_seed = std::nullopt; | ||
| 106 | } | ||
| 107 | |||
| 108 | const auto custom_rtc_enabled = sdl2_config->GetBoolean("System", "custom_rtc_enabled", false); | ||
| 109 | if (custom_rtc_enabled) { | ||
| 110 | Settings::values.custom_rtc = | ||
| 111 | std::chrono::seconds(sdl2_config->GetInteger("System", "custom_rtc", 0)); | ||
| 112 | } else { | ||
| 113 | Settings::values.custom_rtc = std::nullopt; | ||
| 114 | } | ||
| 115 | |||
| 116 | // Core | ||
| 117 | Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true); | ||
| 118 | Settings::values.use_multi_core = sdl2_config->GetBoolean("Core", "use_multi_core", false); | ||
| 119 | |||
| 120 | // Renderer | ||
| 121 | Settings::values.resolution_factor = | ||
| 122 | static_cast<float>(sdl2_config->GetReal("Renderer", "resolution_factor", 1.0)); | ||
| 123 | Settings::values.use_frame_limit = false; | ||
| 124 | Settings::values.frame_limit = 100; | ||
| 125 | Settings::values.use_disk_shader_cache = | ||
| 126 | sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", false); | ||
| 127 | Settings::values.use_accurate_gpu_emulation = | ||
| 128 | sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false); | ||
| 129 | Settings::values.use_asynchronous_gpu_emulation = | ||
| 130 | sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", false); | ||
| 131 | |||
| 132 | Settings::values.bg_red = static_cast<float>(sdl2_config->GetReal("Renderer", "bg_red", 0.0)); | ||
| 133 | Settings::values.bg_green = | ||
| 134 | static_cast<float>(sdl2_config->GetReal("Renderer", "bg_green", 0.0)); | ||
| 135 | Settings::values.bg_blue = static_cast<float>(sdl2_config->GetReal("Renderer", "bg_blue", 0.0)); | ||
| 136 | |||
| 137 | // Audio | ||
| 138 | Settings::values.sink_id = "null"; | ||
| 139 | Settings::values.enable_audio_stretching = false; | ||
| 140 | Settings::values.audio_device_id = "auto"; | ||
| 141 | Settings::values.volume = 0; | ||
| 142 | |||
| 143 | Settings::values.language_index = sdl2_config->GetInteger("System", "language_index", 1); | ||
| 144 | |||
| 145 | // Miscellaneous | ||
| 146 | Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace"); | ||
| 147 | Settings::values.use_dev_keys = sdl2_config->GetBoolean("Miscellaneous", "use_dev_keys", false); | ||
| 148 | |||
| 149 | // Debugging | ||
| 150 | Settings::values.use_gdbstub = false; | ||
| 151 | Settings::values.program_args = ""; | ||
| 152 | Settings::values.dump_exefs = sdl2_config->GetBoolean("Debugging", "dump_exefs", false); | ||
| 153 | Settings::values.dump_nso = sdl2_config->GetBoolean("Debugging", "dump_nso", false); | ||
| 154 | |||
| 155 | const auto title_list = sdl2_config->Get("AddOns", "title_ids", ""); | ||
| 156 | std::stringstream ss(title_list); | ||
| 157 | std::string line; | ||
| 158 | while (std::getline(ss, line, '|')) { | ||
| 159 | const auto title_id = std::stoul(line, nullptr, 16); | ||
| 160 | const auto disabled_list = sdl2_config->Get("AddOns", "disabled_" + line, ""); | ||
| 161 | |||
| 162 | std::stringstream inner_ss(disabled_list); | ||
| 163 | std::string inner_line; | ||
| 164 | std::vector<std::string> out; | ||
| 165 | while (std::getline(inner_ss, inner_line, '|')) { | ||
| 166 | out.push_back(inner_line); | ||
| 167 | } | ||
| 168 | |||
| 169 | Settings::values.disabled_addons.insert_or_assign(title_id, out); | ||
| 170 | } | ||
| 171 | |||
| 172 | // Web Service | ||
| 173 | Settings::values.enable_telemetry = | ||
| 174 | sdl2_config->GetBoolean("WebService", "enable_telemetry", true); | ||
| 175 | Settings::values.web_api_url = | ||
| 176 | sdl2_config->Get("WebService", "web_api_url", "https://api.yuzu-emu.org"); | ||
| 177 | Settings::values.yuzu_username = sdl2_config->Get("WebService", "yuzu_username", ""); | ||
| 178 | Settings::values.yuzu_token = sdl2_config->Get("WebService", "yuzu_token", ""); | ||
| 179 | } | ||
| 180 | |||
| 181 | void Config::Reload() { | ||
| 182 | LoadINI(DefaultINI::sdl2_config_file); | ||
| 183 | ReadValues(); | ||
| 184 | } | ||
diff --git a/src/yuzu_tester/config.h b/src/yuzu_tester/config.h new file mode 100644 index 000000000..3b68e5bc9 --- /dev/null +++ b/src/yuzu_tester/config.h | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include <string> | ||
| 9 | |||
| 10 | class INIReader; | ||
| 11 | |||
| 12 | class Config { | ||
| 13 | std::unique_ptr<INIReader> sdl2_config; | ||
| 14 | std::string sdl2_config_loc; | ||
| 15 | |||
| 16 | bool LoadINI(const std::string& default_contents = "", bool retry = true); | ||
| 17 | void ReadValues(); | ||
| 18 | |||
| 19 | public: | ||
| 20 | Config(); | ||
| 21 | ~Config(); | ||
| 22 | |||
| 23 | void Reload(); | ||
| 24 | }; | ||
diff --git a/src/yuzu_tester/default_ini.h b/src/yuzu_tester/default_ini.h new file mode 100644 index 000000000..46a9960cd --- /dev/null +++ b/src/yuzu_tester/default_ini.h | |||
| @@ -0,0 +1,150 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | namespace DefaultINI { | ||
| 8 | |||
| 9 | const char* sdl2_config_file = R"( | ||
| 10 | [Core] | ||
| 11 | # Whether to use the Just-In-Time (JIT) compiler for CPU emulation | ||
| 12 | # 0: Interpreter (slow), 1 (default): JIT (fast) | ||
| 13 | use_cpu_jit = | ||
| 14 | |||
| 15 | # Whether to use multi-core for CPU emulation | ||
| 16 | # 0 (default): Disabled, 1: Enabled | ||
| 17 | use_multi_core= | ||
| 18 | |||
| 19 | [Renderer] | ||
| 20 | # Whether to use software or hardware rendering. | ||
| 21 | # 0: Software, 1 (default): Hardware | ||
| 22 | use_hw_renderer = | ||
| 23 | |||
| 24 | # Whether to use the Just-In-Time (JIT) compiler for shader emulation | ||
| 25 | # 0: Interpreter (slow), 1 (default): JIT (fast) | ||
| 26 | use_shader_jit = | ||
| 27 | |||
| 28 | # Resolution scale factor | ||
| 29 | # 0: Auto (scales resolution to window size), 1: Native Switch screen resolution, Otherwise a scale | ||
| 30 | # factor for the Switch resolution | ||
| 31 | resolution_factor = | ||
| 32 | |||
| 33 | # Whether to enable V-Sync (caps the framerate at 60FPS) or not. | ||
| 34 | # 0 (default): Off, 1: On | ||
| 35 | use_vsync = | ||
| 36 | |||
| 37 | # Whether to use disk based shader cache | ||
| 38 | # 0 (default): Off, 1 : On | ||
| 39 | use_disk_shader_cache = | ||
| 40 | |||
| 41 | # Whether to use accurate GPU emulation | ||
| 42 | # 0 (default): Off (fast), 1 : On (slow) | ||
| 43 | use_accurate_gpu_emulation = | ||
| 44 | |||
| 45 | # Whether to use asynchronous GPU emulation | ||
| 46 | # 0 : Off (slow), 1 (default): On (fast) | ||
| 47 | use_asynchronous_gpu_emulation = | ||
| 48 | |||
| 49 | # The clear color for the renderer. What shows up on the sides of the bottom screen. | ||
| 50 | # Must be in range of 0.0-1.0. Defaults to 1.0 for all. | ||
| 51 | bg_red = | ||
| 52 | bg_blue = | ||
| 53 | bg_green = | ||
| 54 | |||
| 55 | [Layout] | ||
| 56 | # Layout for the screen inside the render window. | ||
| 57 | # 0 (default): Default Top Bottom Screen, 1: Single Screen Only, 2: Large Screen Small Screen | ||
| 58 | layout_option = | ||
| 59 | |||
| 60 | # Toggle custom layout (using the settings below) on or off. | ||
| 61 | # 0 (default): Off, 1: On | ||
| 62 | custom_layout = | ||
| 63 | |||
| 64 | # Screen placement when using Custom layout option | ||
| 65 | # 0x, 0y is the top left corner of the render window. | ||
| 66 | custom_top_left = | ||
| 67 | custom_top_top = | ||
| 68 | custom_top_right = | ||
| 69 | custom_top_bottom = | ||
| 70 | custom_bottom_left = | ||
| 71 | custom_bottom_top = | ||
| 72 | custom_bottom_right = | ||
| 73 | custom_bottom_bottom = | ||
| 74 | |||
| 75 | # Swaps the prominent screen with the other screen. | ||
| 76 | # For example, if Single Screen is chosen, setting this to 1 will display the bottom screen instead of the top screen. | ||
| 77 | # 0 (default): Top Screen is prominent, 1: Bottom Screen is prominent | ||
| 78 | swap_screen = | ||
| 79 | |||
| 80 | [Data Storage] | ||
| 81 | # Whether to create a virtual SD card. | ||
| 82 | # 1 (default): Yes, 0: No | ||
| 83 | use_virtual_sd = | ||
| 84 | |||
| 85 | [System] | ||
| 86 | # Whether the system is docked | ||
| 87 | # 1: Yes, 0 (default): No | ||
| 88 | use_docked_mode = | ||
| 89 | |||
| 90 | # Allow the use of NFC in games | ||
| 91 | # 1 (default): Yes, 0 : No | ||
| 92 | enable_nfc = | ||
| 93 | |||
| 94 | # Sets the seed for the RNG generator built into the switch | ||
| 95 | # rng_seed will be ignored and randomly generated if rng_seed_enabled is false | ||
| 96 | rng_seed_enabled = | ||
| 97 | rng_seed = | ||
| 98 | |||
| 99 | # Sets the current time (in seconds since 12:00 AM Jan 1, 1970) that will be used by the time service | ||
| 100 | # This will auto-increment, with the time set being the time the game is started | ||
| 101 | # This override will only occur if custom_rtc_enabled is true, otherwise the current time is used | ||
| 102 | custom_rtc_enabled = | ||
| 103 | custom_rtc = | ||
| 104 | |||
| 105 | # Sets the account username, max length is 32 characters | ||
| 106 | # yuzu (default) | ||
| 107 | username = yuzu | ||
| 108 | |||
| 109 | # Sets the systems language index | ||
| 110 | # 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese, | ||
| 111 | # 7: Korean, 8: Dutch, 9: Portuguese, 10: Russian, 11: Taiwanese, 12: British English, 13: Canadian French, | ||
| 112 | # 14: Latin American Spanish, 15: Simplified Chinese, 16: Traditional Chinese | ||
| 113 | language_index = | ||
| 114 | |||
| 115 | # The system region that yuzu will use during emulation | ||
| 116 | # -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan | ||
| 117 | region_value = | ||
| 118 | |||
| 119 | [Miscellaneous] | ||
| 120 | # A filter which removes logs below a certain logging level. | ||
| 121 | # Examples: *:Debug Kernel.SVC:Trace Service.*:Critical | ||
| 122 | log_filter = *:Trace | ||
| 123 | |||
| 124 | [Debugging] | ||
| 125 | # Arguments to be passed to argv/argc in the emulated program. It is preferable to use the testing service datastring | ||
| 126 | program_args= | ||
| 127 | # Determines whether or not yuzu will dump the ExeFS of all games it attempts to load while loading them | ||
| 128 | dump_exefs=false | ||
| 129 | # Determines whether or not yuzu will dump all NSOs it attempts to load while loading them | ||
| 130 | dump_nso=false | ||
| 131 | |||
| 132 | [WebService] | ||
| 133 | # Whether or not to enable telemetry | ||
| 134 | # 0: No, 1 (default): Yes | ||
| 135 | enable_telemetry = | ||
| 136 | # URL for Web API | ||
| 137 | web_api_url = https://api.yuzu-emu.org | ||
| 138 | # Username and token for yuzu Web Service | ||
| 139 | # See https://profile.yuzu-emu.org/ for more info | ||
| 140 | yuzu_username = | ||
| 141 | yuzu_token = | ||
| 142 | |||
| 143 | [AddOns] | ||
| 144 | # Used to disable add-ons | ||
| 145 | # List of title IDs of games that will have add-ons disabled (separated by '|'): | ||
| 146 | title_ids = | ||
| 147 | # For each title ID, have a key/value pair called `disabled_<title_id>` equal to the names of the add-ons to disable (sep. by '|') | ||
| 148 | # e.x. disabled_0100000000010000 = Update|DLC <- disables Updates and DLC on Super Mario Odyssey | ||
| 149 | )"; | ||
| 150 | } | ||
diff --git a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp new file mode 100644 index 000000000..e7fe8decf --- /dev/null +++ b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp | |||
| @@ -0,0 +1,122 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <cstdlib> | ||
| 7 | #include <string> | ||
| 8 | #define SDL_MAIN_HANDLED | ||
| 9 | #include <SDL.h> | ||
| 10 | #include <fmt/format.h> | ||
| 11 | #include <glad/glad.h> | ||
| 12 | #include "common/logging/log.h" | ||
| 13 | #include "common/scm_rev.h" | ||
| 14 | #include "core/settings.h" | ||
| 15 | #include "input_common/main.h" | ||
| 16 | #include "yuzu_tester/emu_window/emu_window_sdl2_hide.h" | ||
| 17 | |||
| 18 | bool EmuWindow_SDL2_Hide::SupportsRequiredGLExtensions() { | ||
| 19 | std::vector<std::string> unsupported_ext; | ||
| 20 | |||
| 21 | if (!GLAD_GL_ARB_direct_state_access) | ||
| 22 | unsupported_ext.push_back("ARB_direct_state_access"); | ||
| 23 | if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev) | ||
| 24 | unsupported_ext.push_back("ARB_vertex_type_10f_11f_11f_rev"); | ||
| 25 | if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge) | ||
| 26 | unsupported_ext.push_back("ARB_texture_mirror_clamp_to_edge"); | ||
| 27 | if (!GLAD_GL_ARB_multi_bind) | ||
| 28 | unsupported_ext.push_back("ARB_multi_bind"); | ||
| 29 | |||
| 30 | // Extensions required to support some texture formats. | ||
| 31 | if (!GLAD_GL_EXT_texture_compression_s3tc) | ||
| 32 | unsupported_ext.push_back("EXT_texture_compression_s3tc"); | ||
| 33 | if (!GLAD_GL_ARB_texture_compression_rgtc) | ||
| 34 | unsupported_ext.push_back("ARB_texture_compression_rgtc"); | ||
| 35 | if (!GLAD_GL_ARB_depth_buffer_float) | ||
| 36 | unsupported_ext.push_back("ARB_depth_buffer_float"); | ||
| 37 | |||
| 38 | for (const std::string& ext : unsupported_ext) | ||
| 39 | LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext); | ||
| 40 | |||
| 41 | return unsupported_ext.empty(); | ||
| 42 | } | ||
| 43 | |||
| 44 | EmuWindow_SDL2_Hide::EmuWindow_SDL2_Hide() { | ||
| 45 | // Initialize the window | ||
| 46 | if (SDL_Init(SDL_INIT_VIDEO) < 0) { | ||
| 47 | LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting..."); | ||
| 48 | exit(1); | ||
| 49 | } | ||
| 50 | |||
| 51 | InputCommon::Init(); | ||
| 52 | |||
| 53 | SDL_SetMainReady(); | ||
| 54 | |||
| 55 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); | ||
| 56 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); | ||
| 57 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); | ||
| 58 | SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); | ||
| 59 | SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); | ||
| 60 | SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); | ||
| 61 | SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); | ||
| 62 | SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); | ||
| 63 | |||
| 64 | std::string window_title = fmt::format("yuzu-tester {} | {}-{}", Common::g_build_fullname, | ||
| 65 | Common::g_scm_branch, Common::g_scm_desc); | ||
| 66 | render_window = SDL_CreateWindow(window_title.c_str(), | ||
| 67 | SDL_WINDOWPOS_UNDEFINED, // x position | ||
| 68 | SDL_WINDOWPOS_UNDEFINED, // y position | ||
| 69 | Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height, | ||
| 70 | SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | | ||
| 71 | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN); | ||
| 72 | |||
| 73 | if (render_window == nullptr) { | ||
| 74 | LOG_CRITICAL(Frontend, "Failed to create SDL2 window! {}", SDL_GetError()); | ||
| 75 | exit(1); | ||
| 76 | } | ||
| 77 | |||
| 78 | gl_context = SDL_GL_CreateContext(render_window); | ||
| 79 | |||
| 80 | if (gl_context == nullptr) { | ||
| 81 | LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context! {}", SDL_GetError()); | ||
| 82 | exit(1); | ||
| 83 | } | ||
| 84 | |||
| 85 | if (!gladLoadGLLoader(static_cast<GLADloadproc>(SDL_GL_GetProcAddress))) { | ||
| 86 | LOG_CRITICAL(Frontend, "Failed to initialize GL functions! {}", SDL_GetError()); | ||
| 87 | exit(1); | ||
| 88 | } | ||
| 89 | |||
| 90 | if (!SupportsRequiredGLExtensions()) { | ||
| 91 | LOG_CRITICAL(Frontend, "GPU does not support all required OpenGL extensions! Exiting..."); | ||
| 92 | exit(1); | ||
| 93 | } | ||
| 94 | |||
| 95 | SDL_PumpEvents(); | ||
| 96 | SDL_GL_SetSwapInterval(false); | ||
| 97 | LOG_INFO(Frontend, "yuzu-tester Version: {} | {}-{}", Common::g_build_fullname, | ||
| 98 | Common::g_scm_branch, Common::g_scm_desc); | ||
| 99 | Settings::LogSettings(); | ||
| 100 | |||
| 101 | DoneCurrent(); | ||
| 102 | } | ||
| 103 | |||
| 104 | EmuWindow_SDL2_Hide::~EmuWindow_SDL2_Hide() { | ||
| 105 | InputCommon::Shutdown(); | ||
| 106 | SDL_GL_DeleteContext(gl_context); | ||
| 107 | SDL_Quit(); | ||
| 108 | } | ||
| 109 | |||
| 110 | void EmuWindow_SDL2_Hide::SwapBuffers() { | ||
| 111 | SDL_GL_SwapWindow(render_window); | ||
| 112 | } | ||
| 113 | |||
| 114 | void EmuWindow_SDL2_Hide::PollEvents() {} | ||
| 115 | |||
| 116 | void EmuWindow_SDL2_Hide::MakeCurrent() { | ||
| 117 | SDL_GL_MakeCurrent(render_window, gl_context); | ||
| 118 | } | ||
| 119 | |||
| 120 | void EmuWindow_SDL2_Hide::DoneCurrent() { | ||
| 121 | SDL_GL_MakeCurrent(render_window, nullptr); | ||
| 122 | } | ||
diff --git a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h new file mode 100644 index 000000000..1a8953c75 --- /dev/null +++ b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/frontend/emu_window.h" | ||
| 8 | |||
| 9 | struct SDL_Window; | ||
| 10 | |||
| 11 | class EmuWindow_SDL2_Hide : public Core::Frontend::EmuWindow { | ||
| 12 | public: | ||
| 13 | explicit EmuWindow_SDL2_Hide(); | ||
| 14 | ~EmuWindow_SDL2_Hide(); | ||
| 15 | |||
| 16 | /// Swap buffers to display the next frame | ||
| 17 | void SwapBuffers() override; | ||
| 18 | |||
| 19 | /// Polls window events | ||
| 20 | void PollEvents() override; | ||
| 21 | |||
| 22 | /// Makes the graphics context current for the caller thread | ||
| 23 | void MakeCurrent() override; | ||
| 24 | |||
| 25 | /// Releases the GL context from the caller thread | ||
| 26 | void DoneCurrent() override; | ||
| 27 | |||
| 28 | /// Whether the window is still open, and a close request hasn't yet been sent | ||
| 29 | bool IsOpen() const; | ||
| 30 | |||
| 31 | private: | ||
| 32 | /// Whether the GPU and driver supports the OpenGL extension required | ||
| 33 | bool SupportsRequiredGLExtensions(); | ||
| 34 | |||
| 35 | /// Internal SDL2 render window | ||
| 36 | SDL_Window* render_window; | ||
| 37 | |||
| 38 | using SDL_GLContext = void*; | ||
| 39 | /// The OpenGL context associated with the window | ||
| 40 | SDL_GLContext gl_context; | ||
| 41 | }; | ||
diff --git a/src/yuzu_tester/resource.h b/src/yuzu_tester/resource.h new file mode 100644 index 000000000..df8e459e4 --- /dev/null +++ b/src/yuzu_tester/resource.h | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | //{{NO_DEPENDENCIES}} | ||
| 2 | // Microsoft Visual C++ generated include file. | ||
| 3 | // Used by pcafe.rc | ||
| 4 | // | ||
| 5 | #define IDI_ICON3 103 | ||
| 6 | |||
| 7 | // Next default values for new objects | ||
| 8 | // | ||
| 9 | #ifdef APSTUDIO_INVOKED | ||
| 10 | #ifndef APSTUDIO_READONLY_SYMBOLS | ||
| 11 | #define _APS_NEXT_RESOURCE_VALUE 105 | ||
| 12 | #define _APS_NEXT_COMMAND_VALUE 40001 | ||
| 13 | #define _APS_NEXT_CONTROL_VALUE 1001 | ||
| 14 | #define _APS_NEXT_SYMED_VALUE 101 | ||
| 15 | #endif | ||
| 16 | #endif | ||
diff --git a/src/yuzu_tester/service/yuzutest.cpp b/src/yuzu_tester/service/yuzutest.cpp new file mode 100644 index 000000000..85d3f436b --- /dev/null +++ b/src/yuzu_tester/service/yuzutest.cpp | |||
| @@ -0,0 +1,112 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <memory> | ||
| 6 | #include "common/string_util.h" | ||
| 7 | #include "core/hle/ipc_helpers.h" | ||
| 8 | #include "core/hle/service/service.h" | ||
| 9 | #include "core/hle/service/sm/sm.h" | ||
| 10 | #include "yuzu_tester/service/yuzutest.h" | ||
| 11 | |||
| 12 | namespace Service::Yuzu { | ||
| 13 | |||
| 14 | constexpr u64 SERVICE_VERSION = 0x00000002; | ||
| 15 | |||
| 16 | class YuzuTest final : public ServiceFramework<YuzuTest> { | ||
| 17 | public: | ||
| 18 | explicit YuzuTest(std::string data, | ||
| 19 | std::function<void(std::vector<TestResult>)> finish_callback) | ||
| 20 | : ServiceFramework{"yuzutest"}, data(std::move(data)), | ||
| 21 | finish_callback(std::move(finish_callback)) { | ||
| 22 | static const FunctionInfo functions[] = { | ||
| 23 | {0, &YuzuTest::Initialize, "Initialize"}, | ||
| 24 | {1, &YuzuTest::GetServiceVersion, "GetServiceVersion"}, | ||
| 25 | {2, &YuzuTest::GetData, "GetData"}, | ||
| 26 | {10, &YuzuTest::StartIndividual, "StartIndividual"}, | ||
| 27 | {20, &YuzuTest::FinishIndividual, "FinishIndividual"}, | ||
| 28 | {100, &YuzuTest::ExitProgram, "ExitProgram"}, | ||
| 29 | }; | ||
| 30 | |||
| 31 | RegisterHandlers(functions); | ||
| 32 | } | ||
| 33 | |||
| 34 | private: | ||
| 35 | void Initialize(Kernel::HLERequestContext& ctx) { | ||
| 36 | LOG_DEBUG(Frontend, "called"); | ||
| 37 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 38 | rb.Push(RESULT_SUCCESS); | ||
| 39 | } | ||
| 40 | |||
| 41 | void GetServiceVersion(Kernel::HLERequestContext& ctx) { | ||
| 42 | LOG_DEBUG(Frontend, "called"); | ||
| 43 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 44 | rb.Push(RESULT_SUCCESS); | ||
| 45 | rb.Push(SERVICE_VERSION); | ||
| 46 | } | ||
| 47 | |||
| 48 | void GetData(Kernel::HLERequestContext& ctx) { | ||
| 49 | LOG_DEBUG(Frontend, "called"); | ||
| 50 | const auto size = ctx.GetWriteBufferSize(); | ||
| 51 | const auto write_size = std::min(size, data.size()); | ||
| 52 | ctx.WriteBuffer(data.data(), write_size); | ||
| 53 | |||
| 54 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 55 | rb.Push(RESULT_SUCCESS); | ||
| 56 | rb.Push<u32>(write_size); | ||
| 57 | } | ||
| 58 | |||
| 59 | void StartIndividual(Kernel::HLERequestContext& ctx) { | ||
| 60 | const auto name_raw = ctx.ReadBuffer(); | ||
| 61 | |||
| 62 | const auto name = Common::StringFromFixedZeroTerminatedBuffer( | ||
| 63 | reinterpret_cast<const char*>(name_raw.data()), name_raw.size()); | ||
| 64 | |||
| 65 | LOG_DEBUG(Frontend, "called, name={}", name); | ||
| 66 | |||
| 67 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 68 | rb.Push(RESULT_SUCCESS); | ||
| 69 | } | ||
| 70 | |||
| 71 | void FinishIndividual(Kernel::HLERequestContext& ctx) { | ||
| 72 | IPC::RequestParser rp{ctx}; | ||
| 73 | |||
| 74 | const auto code = rp.PopRaw<u32>(); | ||
| 75 | |||
| 76 | const auto result_data_raw = ctx.ReadBuffer(); | ||
| 77 | const auto test_name_raw = ctx.ReadBuffer(1); | ||
| 78 | |||
| 79 | const auto data = Common::StringFromFixedZeroTerminatedBuffer( | ||
| 80 | reinterpret_cast<const char*>(result_data_raw.data()), result_data_raw.size()); | ||
| 81 | const auto test_name = Common::StringFromFixedZeroTerminatedBuffer( | ||
| 82 | reinterpret_cast<const char*>(test_name_raw.data()), test_name_raw.size()); | ||
| 83 | |||
| 84 | LOG_INFO(Frontend, "called, result_code={:08X}, data={}, name={}", code, data, test_name); | ||
| 85 | |||
| 86 | results.push_back({code, data, test_name}); | ||
| 87 | |||
| 88 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 89 | rb.Push(RESULT_SUCCESS); | ||
| 90 | } | ||
| 91 | |||
| 92 | void ExitProgram(Kernel::HLERequestContext& ctx) { | ||
| 93 | LOG_DEBUG(Frontend, "called"); | ||
| 94 | |||
| 95 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 96 | rb.Push(RESULT_SUCCESS); | ||
| 97 | |||
| 98 | finish_callback(std::move(results)); | ||
| 99 | } | ||
| 100 | |||
| 101 | std::string data; | ||
| 102 | |||
| 103 | std::vector<TestResult> results; | ||
| 104 | std::function<void(std::vector<TestResult>)> finish_callback; | ||
| 105 | }; | ||
| 106 | |||
| 107 | void InstallInterfaces(SM::ServiceManager& sm, std::string data, | ||
| 108 | std::function<void(std::vector<TestResult>)> finish_callback) { | ||
| 109 | std::make_shared<YuzuTest>(data, finish_callback)->InstallAsService(sm); | ||
| 110 | } | ||
| 111 | |||
| 112 | } // namespace Service::Yuzu | ||
diff --git a/src/yuzu_tester/service/yuzutest.h b/src/yuzu_tester/service/yuzutest.h new file mode 100644 index 000000000..eca129c8c --- /dev/null +++ b/src/yuzu_tester/service/yuzutest.h | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <functional> | ||
| 8 | #include <string> | ||
| 9 | |||
| 10 | namespace Service::SM { | ||
| 11 | class ServiceManager; | ||
| 12 | } | ||
| 13 | |||
| 14 | namespace Service::Yuzu { | ||
| 15 | |||
| 16 | struct TestResult { | ||
| 17 | u32 code; | ||
| 18 | std::string data; | ||
| 19 | std::string name; | ||
| 20 | }; | ||
| 21 | |||
| 22 | void InstallInterfaces(SM::ServiceManager& sm, std::string data, | ||
| 23 | std::function<void(std::vector<TestResult>)> finish_callback); | ||
| 24 | |||
| 25 | } // namespace Service::Yuzu | ||
diff --git a/src/yuzu_tester/yuzu.cpp b/src/yuzu_tester/yuzu.cpp new file mode 100644 index 000000000..b589c3de3 --- /dev/null +++ b/src/yuzu_tester/yuzu.cpp | |||
| @@ -0,0 +1,267 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <iostream> | ||
| 6 | #include <memory> | ||
| 7 | #include <string> | ||
| 8 | #include <thread> | ||
| 9 | |||
| 10 | #include <fmt/ostream.h> | ||
| 11 | |||
| 12 | #include "common/common_paths.h" | ||
| 13 | #include "common/detached_tasks.h" | ||
| 14 | #include "common/file_util.h" | ||
| 15 | #include "common/logging/backend.h" | ||
| 16 | #include "common/logging/filter.h" | ||
| 17 | #include "common/logging/log.h" | ||
| 18 | #include "common/microprofile.h" | ||
| 19 | #include "common/scm_rev.h" | ||
| 20 | #include "common/scope_exit.h" | ||
| 21 | #include "common/string_util.h" | ||
| 22 | #include "common/telemetry.h" | ||
| 23 | #include "core/core.h" | ||
| 24 | #include "core/crypto/key_manager.h" | ||
| 25 | #include "core/file_sys/vfs_real.h" | ||
| 26 | #include "core/hle/service/filesystem/filesystem.h" | ||
| 27 | #include "core/loader/loader.h" | ||
| 28 | #include "core/settings.h" | ||
| 29 | #include "core/telemetry_session.h" | ||
| 30 | #include "video_core/renderer_base.h" | ||
| 31 | #include "yuzu_tester/config.h" | ||
| 32 | #include "yuzu_tester/emu_window/emu_window_sdl2_hide.h" | ||
| 33 | #include "yuzu_tester/service/yuzutest.h" | ||
| 34 | |||
| 35 | #ifdef _WIN32 | ||
| 36 | // windows.h needs to be included before shellapi.h | ||
| 37 | #include <windows.h> | ||
| 38 | |||
| 39 | #include <shellapi.h> | ||
| 40 | #endif | ||
| 41 | |||
| 42 | #undef _UNICODE | ||
| 43 | #include <getopt.h> | ||
| 44 | #ifndef _MSC_VER | ||
| 45 | #include <unistd.h> | ||
| 46 | #endif | ||
| 47 | |||
| 48 | #ifdef _WIN32 | ||
| 49 | extern "C" { | ||
| 50 | // tells Nvidia and AMD drivers to use the dedicated GPU by default on laptops with switchable | ||
| 51 | // graphics | ||
| 52 | __declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001; | ||
| 53 | __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; | ||
| 54 | } | ||
| 55 | #endif | ||
| 56 | |||
| 57 | static void PrintHelp(const char* argv0) { | ||
| 58 | std::cout << "Usage: " << argv0 | ||
| 59 | << " [options] <filename>\n" | ||
| 60 | "-h, --help Display this help and exit\n" | ||
| 61 | "-v, --version Output version information and exit\n" | ||
| 62 | "-d, --datastring Pass following string as data to test service command #2\n" | ||
| 63 | "-l, --log Log to console in addition to file (will log to file only " | ||
| 64 | "by default)\n"; | ||
| 65 | } | ||
| 66 | |||
| 67 | static void PrintVersion() { | ||
| 68 | std::cout << "yuzu [Test Utility] " << Common::g_scm_branch << " " << Common::g_scm_desc | ||
| 69 | << std::endl; | ||
| 70 | } | ||
| 71 | |||
| 72 | static void InitializeLogging(bool console) { | ||
| 73 | Log::Filter log_filter(Log::Level::Debug); | ||
| 74 | log_filter.ParseFilterString(Settings::values.log_filter); | ||
| 75 | Log::SetGlobalFilter(log_filter); | ||
| 76 | |||
| 77 | if (console) | ||
| 78 | Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>()); | ||
| 79 | |||
| 80 | const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir); | ||
| 81 | FileUtil::CreateFullPath(log_dir); | ||
| 82 | Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE)); | ||
| 83 | #ifdef _WIN32 | ||
| 84 | Log::AddBackend(std::make_unique<Log::DebuggerBackend>()); | ||
| 85 | #endif | ||
| 86 | } | ||
| 87 | |||
| 88 | /// Application entry point | ||
| 89 | int main(int argc, char** argv) { | ||
| 90 | Common::DetachedTasks detached_tasks; | ||
| 91 | Config config; | ||
| 92 | |||
| 93 | int option_index = 0; | ||
| 94 | |||
| 95 | char* endarg; | ||
| 96 | #ifdef _WIN32 | ||
| 97 | int argc_w; | ||
| 98 | auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w); | ||
| 99 | |||
| 100 | if (argv_w == nullptr) { | ||
| 101 | std::cout << "Failed to get command line arguments" << std::endl; | ||
| 102 | return -1; | ||
| 103 | } | ||
| 104 | #endif | ||
| 105 | std::string filepath; | ||
| 106 | |||
| 107 | static struct option long_options[] = { | ||
| 108 | {"help", no_argument, 0, 'h'}, | ||
| 109 | {"version", no_argument, 0, 'v'}, | ||
| 110 | {"datastring", optional_argument, 0, 'd'}, | ||
| 111 | {"log", no_argument, 0, 'l'}, | ||
| 112 | {0, 0, 0, 0}, | ||
| 113 | }; | ||
| 114 | |||
| 115 | bool console_log = false; | ||
| 116 | std::string datastring; | ||
| 117 | |||
| 118 | while (optind < argc) { | ||
| 119 | int arg = getopt_long(argc, argv, "hvdl::", long_options, &option_index); | ||
| 120 | if (arg != -1) { | ||
| 121 | switch (static_cast<char>(arg)) { | ||
| 122 | case 'h': | ||
| 123 | PrintHelp(argv[0]); | ||
| 124 | return 0; | ||
| 125 | case 'v': | ||
| 126 | PrintVersion(); | ||
| 127 | return 0; | ||
| 128 | case 'd': | ||
| 129 | datastring = argv[optind]; | ||
| 130 | ++optind; | ||
| 131 | break; | ||
| 132 | case 'l': | ||
| 133 | console_log = true; | ||
| 134 | break; | ||
| 135 | } | ||
| 136 | } else { | ||
| 137 | #ifdef _WIN32 | ||
| 138 | filepath = Common::UTF16ToUTF8(argv_w[optind]); | ||
| 139 | #else | ||
| 140 | filepath = argv[optind]; | ||
| 141 | #endif | ||
| 142 | optind++; | ||
| 143 | } | ||
| 144 | } | ||
| 145 | |||
| 146 | InitializeLogging(console_log); | ||
| 147 | |||
| 148 | #ifdef _WIN32 | ||
| 149 | LocalFree(argv_w); | ||
| 150 | #endif | ||
| 151 | |||
| 152 | MicroProfileOnThreadCreate("EmuThread"); | ||
| 153 | SCOPE_EXIT({ MicroProfileShutdown(); }); | ||
| 154 | |||
| 155 | if (filepath.empty()) { | ||
| 156 | LOG_CRITICAL(Frontend, "Failed to load application: No application specified"); | ||
| 157 | std::cout << "Failed to load application: No application specified" << std::endl; | ||
| 158 | PrintHelp(argv[0]); | ||
| 159 | return -1; | ||
| 160 | } | ||
| 161 | |||
| 162 | Settings::values.use_gdbstub = false; | ||
| 163 | Settings::Apply(); | ||
| 164 | |||
| 165 | std::unique_ptr<EmuWindow_SDL2_Hide> emu_window{std::make_unique<EmuWindow_SDL2_Hide>()}; | ||
| 166 | |||
| 167 | if (!Settings::values.use_multi_core) { | ||
| 168 | // Single core mode must acquire OpenGL context for entire emulation session | ||
| 169 | emu_window->MakeCurrent(); | ||
| 170 | } | ||
| 171 | |||
| 172 | bool finished = false; | ||
| 173 | int return_value = 0; | ||
| 174 | const auto callback = [&finished, | ||
| 175 | &return_value](std::vector<Service::Yuzu::TestResult> results) { | ||
| 176 | finished = true; | ||
| 177 | return_value = 0; | ||
| 178 | |||
| 179 | // Find the minimum length needed to fully enclose all test names (and the header field) in | ||
| 180 | // the fmt::format column by first finding the maximum size of any test name and comparing | ||
| 181 | // that to 9, the string length of 'Test Name' | ||
| 182 | const auto needed_length_name = | ||
| 183 | std::max<u64>(std::max_element(results.begin(), results.end(), | ||
| 184 | [](const auto& lhs, const auto& rhs) { | ||
| 185 | return lhs.name.size() < rhs.name.size(); | ||
| 186 | }) | ||
| 187 | ->name.size(), | ||
| 188 | 9ull); | ||
| 189 | |||
| 190 | std::size_t passed = 0; | ||
| 191 | std::size_t failed = 0; | ||
| 192 | |||
| 193 | std::cout << fmt::format("Result [Res Code] | {:<{}} | Extra Data", "Test Name", | ||
| 194 | needed_length_name) | ||
| 195 | << std::endl; | ||
| 196 | |||
| 197 | for (const auto& res : results) { | ||
| 198 | const auto main_res = res.code == 0 ? "PASSED" : "FAILED"; | ||
| 199 | if (res.code == 0) | ||
| 200 | ++passed; | ||
| 201 | else | ||
| 202 | ++failed; | ||
| 203 | std::cout << fmt::format("{} [{:08X}] | {:<{}} | {}", main_res, res.code, res.name, | ||
| 204 | needed_length_name, res.data) | ||
| 205 | << std::endl; | ||
| 206 | } | ||
| 207 | |||
| 208 | std::cout << std::endl | ||
| 209 | << fmt::format("{:4d} Passed | {:4d} Failed | {:4d} Total | {:2.2f} Passed Ratio", | ||
| 210 | passed, failed, passed + failed, | ||
| 211 | static_cast<float>(passed) / (passed + failed)) | ||
| 212 | << std::endl | ||
| 213 | << (failed == 0 ? "PASSED" : "FAILED") << std::endl; | ||
| 214 | |||
| 215 | if (failed > 0) | ||
| 216 | return_value = -1; | ||
| 217 | }; | ||
| 218 | |||
| 219 | Core::System& system{Core::System::GetInstance()}; | ||
| 220 | system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); | ||
| 221 | Service::FileSystem::CreateFactories(*system.GetFilesystem()); | ||
| 222 | |||
| 223 | SCOPE_EXIT({ system.Shutdown(); }); | ||
| 224 | |||
| 225 | const Core::System::ResultStatus load_result{system.Load(*emu_window, filepath)}; | ||
| 226 | |||
| 227 | switch (load_result) { | ||
| 228 | case Core::System::ResultStatus::ErrorGetLoader: | ||
| 229 | LOG_CRITICAL(Frontend, "Failed to obtain loader for %s!", filepath.c_str()); | ||
| 230 | return -1; | ||
| 231 | case Core::System::ResultStatus::ErrorLoader: | ||
| 232 | LOG_CRITICAL(Frontend, "Failed to load ROM!"); | ||
| 233 | return -1; | ||
| 234 | case Core::System::ResultStatus::ErrorNotInitialized: | ||
| 235 | LOG_CRITICAL(Frontend, "CPUCore not initialized"); | ||
| 236 | return -1; | ||
| 237 | case Core::System::ResultStatus::ErrorVideoCore: | ||
| 238 | LOG_CRITICAL(Frontend, "Failed to initialize VideoCore!"); | ||
| 239 | return -1; | ||
| 240 | case Core::System::ResultStatus::Success: | ||
| 241 | break; // Expected case | ||
| 242 | default: | ||
| 243 | if (static_cast<u32>(load_result) > | ||
| 244 | static_cast<u32>(Core::System::ResultStatus::ErrorLoader)) { | ||
| 245 | const u16 loader_id = static_cast<u16>(Core::System::ResultStatus::ErrorLoader); | ||
| 246 | const u16 error_id = static_cast<u16>(load_result) - loader_id; | ||
| 247 | LOG_CRITICAL(Frontend, | ||
| 248 | "While attempting to load the ROM requested, an error occured. Please " | ||
| 249 | "refer to the yuzu wiki for more information or the yuzu discord for " | ||
| 250 | "additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}", | ||
| 251 | loader_id, error_id, static_cast<Loader::ResultStatus>(error_id)); | ||
| 252 | } | ||
| 253 | } | ||
| 254 | |||
| 255 | Service::Yuzu::InstallInterfaces(system.ServiceManager(), datastring, callback); | ||
| 256 | |||
| 257 | system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDLHideTester"); | ||
| 258 | |||
| 259 | system.Renderer().Rasterizer().LoadDiskResources(); | ||
| 260 | |||
| 261 | while (!finished) { | ||
| 262 | system.RunLoop(); | ||
| 263 | } | ||
| 264 | |||
| 265 | detached_tasks.WaitForAllTasks(); | ||
| 266 | return return_value; | ||
| 267 | } | ||
diff --git a/src/yuzu_tester/yuzu.rc b/src/yuzu_tester/yuzu.rc new file mode 100644 index 000000000..7de8ef3d9 --- /dev/null +++ b/src/yuzu_tester/yuzu.rc | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | #include "winresrc.h" | ||
| 2 | ///////////////////////////////////////////////////////////////////////////// | ||
| 3 | // | ||
| 4 | // Icon | ||
| 5 | // | ||
| 6 | |||
| 7 | // Icon with lowest ID value placed first to ensure application icon | ||
| 8 | // remains consistent on all systems. | ||
| 9 | YUZU_ICON ICON "../../dist/yuzu.ico" | ||
| 10 | |||
| 11 | |||
| 12 | ///////////////////////////////////////////////////////////////////////////// | ||
| 13 | // | ||
| 14 | // RT_MANIFEST | ||
| 15 | // | ||
| 16 | |||
| 17 | 1 RT_MANIFEST "../../dist/yuzu.manifest" | ||