diff options
Diffstat (limited to 'src/yuzu_cmd')
| -rw-r--r-- | src/yuzu_cmd/CMakeLists.txt | 5 | ||||
| -rw-r--r-- | src/yuzu_cmd/config.cpp | 35 | ||||
| -rw-r--r-- | src/yuzu_cmd/config.h | 16 | ||||
| -rw-r--r-- | src/yuzu_cmd/default_ini.h | 46 | ||||
| -rw-r--r-- | src/yuzu_cmd/emu_window/emu_window_sdl2.cpp | 30 | ||||
| -rw-r--r-- | src/yuzu_cmd/emu_window/emu_window_sdl2.h | 8 | ||||
| -rw-r--r-- | src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp | 10 | ||||
| -rw-r--r-- | src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h | 7 | ||||
| -rw-r--r-- | src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp | 11 | ||||
| -rw-r--r-- | src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h | 7 | ||||
| -rw-r--r-- | src/yuzu_cmd/yuzu.cpp | 203 | ||||
| -rw-r--r-- | src/yuzu_cmd/yuzu.rc | 3 | 
12 files changed, 292 insertions, 89 deletions
| diff --git a/src/yuzu_cmd/CMakeLists.txt b/src/yuzu_cmd/CMakeLists.txt index 74fc24972..7d8ca3d8a 100644 --- a/src/yuzu_cmd/CMakeLists.txt +++ b/src/yuzu_cmd/CMakeLists.txt @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: 2018 yuzu Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later +  set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules)  # Credits to Samantas5855 and others for this function. @@ -45,7 +48,7 @@ if (YUZU_USE_EXTERNAL_SDL2)  endif()  if(UNIX AND NOT APPLE) -    install(TARGETS yuzu-cmd RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") +    install(TARGETS yuzu-cmd)  endif()  if (MSVC) diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index ff616da70..66dd0dc15 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp @@ -1,8 +1,8 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2014 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later  #include <memory> +#include <optional>  #include <sstream>  // Ignore -Wimplicit-fallthrough due to https://github.com/libsdl-org/SDL/issues/4307 @@ -20,7 +20,6 @@  #include "common/fs/fs.h"  #include "common/fs/path_util.h"  #include "common/logging/log.h" -#include "common/param_package.h"  #include "common/settings.h"  #include "core/hle/service/acc/profile_manager.h"  #include "input_common/main.h" @@ -29,11 +28,12 @@  namespace FS = Common::FS; -Config::Config() { -    // TODO: Don't hardcode the path; let the frontend decide where to put the config files. -    sdl2_config_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "sdl2-config.ini"; -    sdl2_config = std::make_unique<INIReader>(FS::PathToUTF8String(sdl2_config_loc)); +const std::filesystem::path default_config_path = +    FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "sdl2-config.ini"; +Config::Config(std::optional<std::filesystem::path> config_path) +    : sdl2_config_loc{config_path.value_or(default_config_path)}, +      sdl2_config{std::make_unique<INIReader>(FS::PathToUTF8String(sdl2_config_loc))} {      Reload();  } @@ -89,17 +89,17 @@ static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs>  }};  template <> -void Config::ReadSetting(const std::string& group, Settings::BasicSetting<std::string>& setting) { +void Config::ReadSetting(const std::string& group, Settings::Setting<std::string>& setting) {      setting = sdl2_config->Get(group, setting.GetLabel(), setting.GetDefault());  }  template <> -void Config::ReadSetting(const std::string& group, Settings::BasicSetting<bool>& setting) { +void Config::ReadSetting(const std::string& group, Settings::Setting<bool>& setting) {      setting = sdl2_config->GetBoolean(group, setting.GetLabel(), setting.GetDefault());  } -template <typename Type> -void Config::ReadSetting(const std::string& group, Settings::BasicSetting<Type>& setting) { +template <typename Type, bool ranged> +void Config::ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting) {      setting = static_cast<Type>(sdl2_config->GetInteger(group, setting.GetLabel(),                                                          static_cast<long>(setting.GetDefault())));  } @@ -266,6 +266,7 @@ void Config::ReadValues() {      // Core      ReadSetting("Core", Settings::values.use_multi_core); +    ReadSetting("Core", Settings::values.use_extended_memory_layout);      // Cpu      ReadSetting("Cpu", Settings::values.cpu_accuracy); @@ -279,11 +280,14 @@ void Config::ReadValues() {      ReadSetting("Cpu", Settings::values.cpuopt_misc_ir);      ReadSetting("Cpu", Settings::values.cpuopt_reduce_misalign_checks);      ReadSetting("Cpu", Settings::values.cpuopt_fastmem); +    ReadSetting("Cpu", Settings::values.cpuopt_fastmem_exclusives); +    ReadSetting("Cpu", Settings::values.cpuopt_recompile_exclusives);      ReadSetting("Cpu", Settings::values.cpuopt_unsafe_unfuse_fma);      ReadSetting("Cpu", Settings::values.cpuopt_unsafe_reduce_fp_error);      ReadSetting("Cpu", Settings::values.cpuopt_unsafe_ignore_standard_fpcr);      ReadSetting("Cpu", Settings::values.cpuopt_unsafe_inaccurate_nan);      ReadSetting("Cpu", Settings::values.cpuopt_unsafe_fastmem_check); +    ReadSetting("Cpu", Settings::values.cpuopt_unsafe_ignore_global_monitor);      // Renderer      ReadSetting("Renderer", Settings::values.renderer_backend); @@ -305,13 +309,12 @@ void Config::ReadValues() {      ReadSetting("Renderer", Settings::values.gpu_accuracy);      ReadSetting("Renderer", Settings::values.use_asynchronous_gpu_emulation);      ReadSetting("Renderer", Settings::values.use_vsync); -    ReadSetting("Renderer", Settings::values.fps_cap); -    ReadSetting("Renderer", Settings::values.disable_fps_limit);      ReadSetting("Renderer", Settings::values.shader_backend);      ReadSetting("Renderer", Settings::values.use_asynchronous_shaders);      ReadSetting("Renderer", Settings::values.nvdec_emulation);      ReadSetting("Renderer", Settings::values.accelerate_astc);      ReadSetting("Renderer", Settings::values.use_fast_gpu_time); +    ReadSetting("Renderer", Settings::values.use_pessimistic_flushes);      ReadSetting("Renderer", Settings::values.bg_red);      ReadSetting("Renderer", Settings::values.bg_green); @@ -319,7 +322,7 @@ void Config::ReadValues() {      // Audio      ReadSetting("Audio", Settings::values.sink_id); -    ReadSetting("Audio", Settings::values.audio_device_id); +    ReadSetting("Audio", Settings::values.audio_output_device_id);      ReadSetting("Audio", Settings::values.volume);      // Miscellaneous @@ -339,6 +342,8 @@ void Config::ReadValues() {      ReadSetting("Debugging", Settings::values.use_debug_asserts);      ReadSetting("Debugging", Settings::values.use_auto_stub);      ReadSetting("Debugging", Settings::values.disable_macro_jit); +    ReadSetting("Debugging", Settings::values.use_gdbstub); +    ReadSetting("Debugging", Settings::values.gdbstub_port);      const auto title_list = sdl2_config->Get("AddOns", "title_ids", "");      std::stringstream ss(title_list); diff --git a/src/yuzu_cmd/config.h b/src/yuzu_cmd/config.h index 1ee932be2..021438b17 100644 --- a/src/yuzu_cmd/config.h +++ b/src/yuzu_cmd/config.h @@ -1,11 +1,11 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2014 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later  #pragma once  #include <filesystem>  #include <memory> +#include <optional>  #include <string>  #include "common/settings.h" @@ -13,25 +13,25 @@  class INIReader;  class Config { -    std::unique_ptr<INIReader> sdl2_config;      std::filesystem::path sdl2_config_loc; +    std::unique_ptr<INIReader> sdl2_config;      bool LoadINI(const std::string& default_contents = "", bool retry = true);      void ReadValues();  public: -    Config(); +    explicit Config(std::optional<std::filesystem::path> config_path);      ~Config();      void Reload();  private:      /** -     * Applies a value read from the sdl2_config to a BasicSetting. +     * Applies a value read from the sdl2_config to a Setting.       *       * @param group The name of the INI group       * @param setting The yuzu setting to modify       */ -    template <typename Type> -    void ReadSetting(const std::string& group, Settings::BasicSetting<Type>& setting); +    template <typename Type, bool ranged> +    void ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting);  }; diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index 6d613bf7a..d214771b0 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h @@ -1,6 +1,5 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2014 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later  #pragma once @@ -124,7 +123,11 @@ keyboard_enabled =  [Core]  # Whether to use multi-core for CPU emulation  # 0: Disabled, 1 (default): Enabled -use_multi_core= +use_multi_core = + +# Enable extended guest system memory layout (6GB DRAM) +# 0 (default): Disabled, 1: Enabled +use_extended_memory_layout =  [Cpu]  # Adjusts various optimizations. @@ -174,6 +177,14 @@ cpuopt_reduce_misalign_checks =  # 0: Disabled, 1 (default): Enabled  cpuopt_fastmem = +# Enable Host MMU Emulation for exclusive memory instructions (faster guest memory access) +# 0: Disabled, 1 (default): Enabled +cpuopt_fastmem_exclusives = + +# Enable fallback on failure of fastmem of exclusive memory instructions (faster guest memory access) +# 0: Disabled, 1 (default): Enabled +cpuopt_recompile_exclusives = +  # Enable unfuse FMA (improve performance on CPUs without FMA)  # Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.  # 0: Disabled, 1 (default): Enabled @@ -199,9 +210,14 @@ cpuopt_unsafe_inaccurate_nan =  # 0: Disabled, 1 (default): Enabled  cpuopt_unsafe_fastmem_check = +# Enable faster exclusive instructions +# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. +# 0: Disabled, 1 (default): Enabled +cpuopt_unsafe_ignore_global_monitor = +  [Renderer]  # Which backend API to use. -# 0 (default): OpenGL, 1: Vulkan +# 0: OpenGL, 1 (default): Vulkan  backend =  # Enable graphics API debugging mode. @@ -303,6 +319,10 @@ use_asynchronous_gpu_emulation =  # 0: Off, 1 (default): On  use_fast_gpu_time = +# Force unmodified buffers to be flushed, which can cost performance. +# 0: Off (default), 1: On +use_pessimistic_flushes = +  # Whether to use garbage collection or not for GPU caches.  # 0 (default): Off, 1: On  use_caches_gc = @@ -313,10 +333,6 @@ bg_red =  bg_blue =  bg_green = -# Caps the unlocked framerate to a multiple of the title's target FPS. -# 1 - 1000: Target FPS multiple cap. 1000 (default) -fps_cap = -  [Audio]  # Which audio output engine to use.  # auto (default): Auto-select @@ -325,12 +341,6 @@ fps_cap =  # null: No audio output  output_engine = -# Whether or not to enable the audio-stretching post-processing effect. -# This effect adjusts audio speed to match emulation speed and helps prevent audio stutter, -# at the cost of increasing audio latency. -# 0: No, 1 (default): Yes -enable_audio_stretching = -  # Which audio device to use.  # auto (default): Auto-select  output_device = @@ -423,9 +433,11 @@ use_debug_asserts =  use_auto_stub =  # Enables/Disables the macro JIT compiler  disable_macro_jit=false -# Presents guest frames as they become available. Experimental. +# Determines whether to enable the GDB stub and wait for the debugger to attach before running.  # false: Disabled (default), true: Enabled -disable_fps_limit=false +use_gdbstub=false +# The port to use for the GDB server, if it is enabled. +gdbstub_port=6543  [WebService]  # Whether or not to enable telemetry diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp index 57f807826..4ac72c2f6 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp @@ -1,6 +1,5 @@ -// Copyright 2016 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2016 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later  #include <SDL.h> @@ -93,7 +92,7 @@ void EmuWindow_SDL2::OnFingerMotion(float x, float y, std::size_t id) {  }  void EmuWindow_SDL2::OnFingerUp() { -    input_subsystem->GetTouchScreen()->TouchReleased(0); +    input_subsystem->GetTouchScreen()->ReleaseAllTouch();  }  void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) { @@ -123,14 +122,15 @@ void EmuWindow_SDL2::ShowCursor(bool show_cursor) {  }  void EmuWindow_SDL2::Fullscreen() { +    SDL_DisplayMode display_mode;      switch (Settings::values.fullscreen_mode.GetValue()) {      case Settings::FullscreenMode::Exclusive: -        // Set window size to render size before entering fullscreen -- SDL does not resize to -        // display dimensions in this mode. -        // TODO: Multiply the window size by resolution_factor (for both docked modes) -        if (Settings::values.use_docked_mode) { -            SDL_SetWindowSize(render_window, Layout::ScreenDocked::Width, -                              Layout::ScreenDocked::Height); +        // Set window size to render size before entering fullscreen -- SDL2 does not resize window +        // to display dimensions automatically in this mode. +        if (SDL_GetDesktopDisplayMode(0, &display_mode) == 0) { +            SDL_SetWindowSize(render_window, display_mode.w, display_mode.h); +        } else { +            LOG_ERROR(Frontend, "SDL_GetDesktopDisplayMode failed: {}", SDL_GetError());          }          if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN) == 0) { @@ -161,7 +161,15 @@ void EmuWindow_SDL2::WaitEvent() {      SDL_Event event;      if (!SDL_WaitEvent(&event)) { -        LOG_CRITICAL(Frontend, "SDL_WaitEvent failed: {}", SDL_GetError()); +        const char* error = SDL_GetError(); +        if (!error || strcmp(error, "") == 0) { +            // https://github.com/libsdl-org/SDL/issues/5780 +            // Sometimes SDL will return without actually having hit an error condition; +            // just ignore it in this case. +            return; +        } + +        LOG_CRITICAL(Frontend, "SDL_WaitEvent failed: {}", error);          exit(1);      } diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h index 0af002693..90bb0b415 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h @@ -1,10 +1,8 @@ -// Copyright 2016 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2016 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later  #pragma once -#include <memory>  #include <utility>  #include "core/frontend/emu_window.h" @@ -21,7 +19,7 @@ enum class MouseButton;  class EmuWindow_SDL2 : public Core::Frontend::EmuWindow {  public: -    explicit EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem, Core::System& system_); +    explicit EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem_, Core::System& system_);      ~EmuWindow_SDL2();      /// Whether the window is still open, and a close request hasn't yet been sent diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp index 70db865ec..9b660c13c 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later  #include <algorithm>  #include <cstdlib> @@ -11,7 +10,6 @@  #include <fmt/format.h>  #include <glad/glad.h> -#include "common/assert.h"  #include "common/logging/log.h"  #include "common/scm_rev.h"  #include "common/settings.h" @@ -75,9 +73,9 @@ bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() {      return unsupported_ext.empty();  } -EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem, +EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem_,                                       Core::System& system_, bool fullscreen) -    : EmuWindow_SDL2{input_subsystem, system_} { +    : EmuWindow_SDL2{input_subsystem_, system_} {      SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);      SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 6);      SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h index d7f2c83d8..39346e704 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later  #pragma once @@ -18,7 +17,7 @@ class InputSubsystem;  class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 {  public: -    explicit EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem, Core::System& system_, +    explicit EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem_, Core::System& system_,                                 bool fullscreen);      ~EmuWindow_SDL2_GL(); diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp index de40b76bf..65455c86e 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later  #include <cstdlib>  #include <memory> @@ -8,10 +7,8 @@  #include <fmt/format.h> -#include "common/assert.h"  #include "common/logging/log.h"  #include "common/scm_rev.h" -#include "common/settings.h"  #include "video_core/renderer_vulkan/renderer_vulkan.h"  #include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h" @@ -24,9 +21,9 @@  #include <SDL.h>  #include <SDL_syswm.h> -EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem, +EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem_,                                       Core::System& system_, bool fullscreen) -    : EmuWindow_SDL2{input_subsystem, system_} { +    : EmuWindow_SDL2{input_subsystem_, system_} {      const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name,                                                   Common::g_scm_branch, Common::g_scm_desc);      render_window = diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h index 3ea521b2a..e39ad754d 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later  #pragma once @@ -19,7 +18,7 @@ class InputSubsystem;  class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 {  public: -    explicit EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem, Core::System& system, +    explicit EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem_, Core::System& system,                                 bool fullscreen);      ~EmuWindow_SDL2_VK() override; diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index b44ea0cc4..3a0f33cba 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -1,21 +1,17 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2014 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later  #include <chrono>  #include <iostream>  #include <memory> +#include <regex>  #include <string>  #include <thread>  #include <fmt/ostream.h>  #include "common/detached_tasks.h" -#include "common/fs/fs.h" -#include "common/fs/fs_paths.h" -#include "common/fs/path_util.h"  #include "common/logging/backend.h" -#include "common/logging/filter.h"  #include "common/logging/log.h"  #include "common/microprofile.h"  #include "common/nvidia_flags.h" @@ -25,6 +21,7 @@  #include "common/string_util.h"  #include "common/telemetry.h"  #include "core/core.h" +#include "core/cpu_manager.h"  #include "core/crypto/key_manager.h"  #include "core/file_sys/registered_cache.h"  #include "core/file_sys/vfs_real.h" @@ -32,6 +29,7 @@  #include "core/loader/loader.h"  #include "core/telemetry_session.h"  #include "input_common/main.h" +#include "network/network.h"  #include "video_core/renderer_base.h"  #include "yuzu_cmd/config.h"  #include "yuzu_cmd/emu_window/emu_window_sdl2.h" @@ -63,22 +61,122 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;  static void PrintHelp(const char* argv0) {      std::cout << "Usage: " << argv0                << " [options] <filename>\n" +                 "-m, --multiplayer=nick:password@address:port" +                 " Nickname, password, address and port for multiplayer\n"                   "-f, --fullscreen      Start in fullscreen mode\n"                   "-h, --help            Display this help and exit\n"                   "-v, --version         Output version information and exit\n" -                 "-p, --program         Pass following string as arguments to executable\n"; +                 "-p, --program         Pass following string as arguments to executable\n" +                 "-c, --config          Load the specified configuration file\n";  }  static void PrintVersion() {      std::cout << "yuzu " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl;  } +static void OnStateChanged(const Network::RoomMember::State& state) { +    switch (state) { +    case Network::RoomMember::State::Idle: +        LOG_DEBUG(Network, "Network is idle"); +        break; +    case Network::RoomMember::State::Joining: +        LOG_DEBUG(Network, "Connection sequence to room started"); +        break; +    case Network::RoomMember::State::Joined: +        LOG_DEBUG(Network, "Successfully joined to the room"); +        break; +    case Network::RoomMember::State::Moderator: +        LOG_DEBUG(Network, "Successfully joined the room as a moderator"); +        break; +    default: +        break; +    } +} + +static void OnNetworkError(const Network::RoomMember::Error& error) { +    switch (error) { +    case Network::RoomMember::Error::LostConnection: +        LOG_DEBUG(Network, "Lost connection to the room"); +        break; +    case Network::RoomMember::Error::CouldNotConnect: +        LOG_ERROR(Network, "Error: Could not connect"); +        exit(1); +        break; +    case Network::RoomMember::Error::NameCollision: +        LOG_ERROR( +            Network, +            "You tried to use the same nickname as another user that is connected to the Room"); +        exit(1); +        break; +    case Network::RoomMember::Error::IpCollision: +        LOG_ERROR(Network, "You tried to use the same fake IP-Address as another user that is " +                           "connected to the Room"); +        exit(1); +        break; +    case Network::RoomMember::Error::WrongPassword: +        LOG_ERROR(Network, "Room replied with: Wrong password"); +        exit(1); +        break; +    case Network::RoomMember::Error::WrongVersion: +        LOG_ERROR(Network, +                  "You are using a different version than the room you are trying to connect to"); +        exit(1); +        break; +    case Network::RoomMember::Error::RoomIsFull: +        LOG_ERROR(Network, "The room is full"); +        exit(1); +        break; +    case Network::RoomMember::Error::HostKicked: +        LOG_ERROR(Network, "You have been kicked by the host"); +        break; +    case Network::RoomMember::Error::HostBanned: +        LOG_ERROR(Network, "You have been banned by the host"); +        break; +    case Network::RoomMember::Error::UnknownError: +        LOG_ERROR(Network, "UnknownError"); +        break; +    case Network::RoomMember::Error::PermissionDenied: +        LOG_ERROR(Network, "PermissionDenied"); +        break; +    case Network::RoomMember::Error::NoSuchUser: +        LOG_ERROR(Network, "NoSuchUser"); +        break; +    } +} + +static void OnMessageReceived(const Network::ChatEntry& msg) { +    std::cout << std::endl << msg.nickname << ": " << msg.message << std::endl << std::endl; +} + +static void OnStatusMessageReceived(const Network::StatusMessageEntry& msg) { +    std::string message; +    switch (msg.type) { +    case Network::IdMemberJoin: +        message = fmt::format("{} has joined", msg.nickname); +        break; +    case Network::IdMemberLeave: +        message = fmt::format("{} has left", msg.nickname); +        break; +    case Network::IdMemberKicked: +        message = fmt::format("{} has been kicked", msg.nickname); +        break; +    case Network::IdMemberBanned: +        message = fmt::format("{} has been banned", msg.nickname); +        break; +    case Network::IdAddressUnbanned: +        message = fmt::format("{} has been unbanned", msg.nickname); +        break; +    } +    if (!message.empty()) +        std::cout << std::endl << "* " << message << std::endl << std::endl; +} +  /// Application entry point  int main(int argc, char** argv) {      Common::Log::Initialize();      Common::Log::SetColorConsoleBackendEnabled(true); +    Common::Log::Start();      Common::DetachedTasks detached_tasks; -    Config config;      int option_index = 0;  #ifdef _WIN32 @@ -91,21 +189,64 @@ int main(int argc, char** argv) {      }  #endif      std::string filepath; +    std::optional<std::string> config_path; +    std::string program_args; +    bool use_multiplayer = false;      bool fullscreen = false; +    std::string nickname{}; +    std::string password{}; +    std::string address{}; +    u16 port = Network::DefaultRoomPort;      static struct option long_options[] = { +        // clang-format off +        {"multiplayer", required_argument, 0, 'm'},          {"fullscreen", no_argument, 0, 'f'},          {"help", no_argument, 0, 'h'},          {"version", no_argument, 0, 'v'},          {"program", optional_argument, 0, 'p'}, +        {"config", required_argument, 0, 'c'},          {0, 0, 0, 0}, +        // clang-format on      };      while (optind < argc) { -        int arg = getopt_long(argc, argv, "g:fhvp::", long_options, &option_index); +        int arg = getopt_long(argc, argv, "g:fhvp::c:", long_options, &option_index);          if (arg != -1) {              switch (static_cast<char>(arg)) { +            case 'm': { +                use_multiplayer = true; +                const std::string str_arg(optarg); +                // regex to check if the format is nickname:password@ip:port +                // with optional :password +                const std::regex re("^([^:]+)(?::(.+))?@([^:]+)(?::([0-9]+))?$"); +                if (!std::regex_match(str_arg, re)) { +                    std::cout << "Wrong format for option --multiplayer\n"; +                    PrintHelp(argv[0]); +                    return 0; +                } + +                std::smatch match; +                std::regex_search(str_arg, match, re); +                ASSERT(match.size() == 5); +                nickname = match[1]; +                password = match[2]; +                address = match[3]; +                if (!match[4].str().empty()) +                    port = std::stoi(match[4]); +                std::regex nickname_re("^[a-zA-Z0-9._\\- ]+$"); +                if (!std::regex_match(nickname, nickname_re)) { +                    std::cout +                        << "Nickname is not valid. Must be 4 to 20 alphanumeric characters.\n"; +                    return 0; +                } +                if (address.empty()) { +                    std::cout << "Address to room must not be empty.\n"; +                    return 0; +                } +                break; +            }              case 'f':                  fullscreen = true;                  LOG_INFO(Frontend, "Starting in fullscreen mode..."); @@ -117,9 +258,12 @@ int main(int argc, char** argv) {                  PrintVersion();                  return 0;              case 'p': -                Settings::values.program_args = argv[optind]; +                program_args = argv[optind];                  ++optind;                  break; +            case 'c': +                config_path = optarg; +                break;              }          } else {  #ifdef _WIN32 @@ -131,6 +275,18 @@ int main(int argc, char** argv) {          }      } +    Config config{config_path}; + +    // apply the log_filter setting +    // the logger was initialized before and doesn't pick up the filter on its own +    Common::Log::Filter filter; +    filter.ParseFilterString(Settings::values.log_filter.GetValue()); +    Common::Log::SetGlobalFilter(filter); + +    if (!program_args.empty()) { +        Settings::values.program_args = program_args; +    } +  #ifdef _WIN32      LocalFree(argv_w);  #endif @@ -197,8 +353,24 @@ int main(int argc, char** argv) {      system.TelemetrySession().AddField(Common::Telemetry::FieldType::App, "Frontend", "SDL"); +    if (use_multiplayer) { +        if (auto member = system.GetRoomNetwork().GetRoomMember().lock()) { +            member->BindOnChatMessageRecieved(OnMessageReceived); +            member->BindOnStatusMessageReceived(OnStatusMessageReceived); +            member->BindOnStateChanged(OnStateChanged); +            member->BindOnError(OnNetworkError); +            LOG_DEBUG(Network, "Start connection to {}:{} with nickname {}", address, port, +                      nickname); +            member->Join(nickname, address.c_str(), port, 0, Network::NoPreferredIP, password); +        } else { +            LOG_ERROR(Network, "Could not access RoomMember"); +            return 0; +        } +    } +      // Core is loaded, start the GPU (makes the GPU contexts current to this thread)      system.GPU().Start(); +    system.GetCpuManager().OnGpuReady();      if (Settings::values.use_disk_shader_cache.GetValue()) {          system.Renderer().ReadRasterizer()->LoadDiskResources( @@ -206,10 +378,19 @@ int main(int argc, char** argv) {              [](VideoCore::LoadCallbackStage, size_t value, size_t total) {});      } +    system.RegisterExitCallback([&] { +        // Just exit right away. +        exit(0); +    }); +      void(system.Run()); +    if (system.DebuggerEnabled()) { +        system.InitializeDebugger(); +    }      while (emu_window->IsOpen()) {          emu_window->WaitEvent();      } +    system.DetachDebugger();      void(system.Pause());      system.Shutdown(); diff --git a/src/yuzu_cmd/yuzu.rc b/src/yuzu_cmd/yuzu.rc index 0cde75e2f..e230cf680 100644 --- a/src/yuzu_cmd/yuzu.rc +++ b/src/yuzu_cmd/yuzu.rc @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later +  #include "winresrc.h"  /////////////////////////////////////////////////////////////////////////////  // | 
