summaryrefslogtreecommitdiffstats
path: root/src/yuzu_cmd
diff options
context:
space:
mode:
Diffstat (limited to 'src/yuzu_cmd')
-rw-r--r--src/yuzu_cmd/CMakeLists.txt35
-rw-r--r--src/yuzu_cmd/citra.cpp175
-rw-r--r--src/yuzu_cmd/citra.rc17
-rw-r--r--src/yuzu_cmd/config.cpp160
-rw-r--r--src/yuzu_cmd/config.h24
-rw-r--r--src/yuzu_cmd/default_ini.h179
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp177
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.h59
-rw-r--r--src/yuzu_cmd/resource.h16
9 files changed, 842 insertions, 0 deletions
diff --git a/src/yuzu_cmd/CMakeLists.txt b/src/yuzu_cmd/CMakeLists.txt
new file mode 100644
index 000000000..c6c527eb6
--- /dev/null
+++ b/src/yuzu_cmd/CMakeLists.txt
@@ -0,0 +1,35 @@
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules)
+
+set(SRCS
+ emu_window/emu_window_sdl2.cpp
+ citra.cpp
+ config.cpp
+ citra.rc
+ )
+set(HEADERS
+ emu_window/emu_window_sdl2.h
+ config.h
+ default_ini.h
+ resource.h
+ )
+
+create_directory_groups(${SRCS} ${HEADERS})
+
+add_executable(yuzu-cmd ${SRCS} ${HEADERS})
+target_link_libraries(yuzu-cmd PRIVATE common core input_common)
+target_link_libraries(yuzu-cmd PRIVATE inih glad)
+if (MSVC)
+ target_link_libraries(yuzu-cmd PRIVATE getopt)
+endif()
+target_link_libraries(yuzu-cmd PRIVATE ${PLATFORM_LIBRARIES} SDL2 Threads::Threads)
+
+if(UNIX AND NOT APPLE)
+ install(TARGETS yuzu-cmd RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
+endif()
+
+if (MSVC)
+ include(CopyCitraSDLDeps)
+ include(CopyYuzuUnicornDeps)
+ copy_citra_SDL_deps(yuzu-cmd)
+ copy_yuzu_unicorn_deps(yuzu-cmd)
+endif()
diff --git a/src/yuzu_cmd/citra.cpp b/src/yuzu_cmd/citra.cpp
new file mode 100644
index 000000000..e524c5535
--- /dev/null
+++ b/src/yuzu_cmd/citra.cpp
@@ -0,0 +1,175 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <iostream>
+#include <memory>
+#include <string>
+#include <thread>
+
+// This needs to be included before getopt.h because the latter #defines symbols used by it
+#include "common/microprofile.h"
+
+#ifdef _MSC_VER
+#include <getopt.h>
+#else
+#include <getopt.h>
+#include <unistd.h>
+#endif
+
+#ifdef _WIN32
+// windows.h needs to be included before shellapi.h
+#include <windows.h>
+
+#include <shellapi.h>
+#endif
+
+#include "citra/config.h"
+#include "citra/emu_window/emu_window_sdl2.h"
+#include "common/logging/backend.h"
+#include "common/logging/filter.h"
+#include "common/logging/log.h"
+#include "common/scm_rev.h"
+#include "common/scope_exit.h"
+#include "common/string_util.h"
+#include "core/core.h"
+#include "core/gdbstub/gdbstub.h"
+#include "core/loader/loader.h"
+#include "core/settings.h"
+
+static void PrintHelp(const char* argv0) {
+ std::cout << "Usage: " << argv0
+ << " [options] <filename>\n"
+ "-g, --gdbport=NUMBER Enable gdb stub on port NUMBER\n"
+ "-h, --help Display this help and exit\n"
+ "-v, --version Output version information and exit\n";
+}
+
+static void PrintVersion() {
+ std::cout << "Citra " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl;
+}
+
+/// Application entry point
+int main(int argc, char** argv) {
+ Config config;
+ int option_index = 0;
+ bool use_gdbstub = Settings::values.use_gdbstub;
+ u32 gdb_port = static_cast<u32>(Settings::values.gdbstub_port);
+ char* endarg;
+#ifdef _WIN32
+ int argc_w;
+ auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w);
+
+ if (argv_w == nullptr) {
+ LOG_CRITICAL(Frontend, "Failed to get command line arguments");
+ return -1;
+ }
+#endif
+ std::string filepath;
+
+ static struct option long_options[] = {
+ {"gdbport", required_argument, 0, 'g'},
+ {"help", no_argument, 0, 'h'},
+ {"version", no_argument, 0, 'v'},
+ {0, 0, 0, 0},
+ };
+
+ while (optind < argc) {
+ char arg = getopt_long(argc, argv, "g:hv", long_options, &option_index);
+ if (arg != -1) {
+ switch (arg) {
+ case 'g':
+ errno = 0;
+ gdb_port = strtoul(optarg, &endarg, 0);
+ use_gdbstub = true;
+ if (endarg == optarg)
+ errno = EINVAL;
+ if (errno != 0) {
+ perror("--gdbport");
+ exit(1);
+ }
+ break;
+ case 'h':
+ PrintHelp(argv[0]);
+ return 0;
+ case 'v':
+ PrintVersion();
+ return 0;
+ }
+ } else {
+#ifdef _WIN32
+ filepath = Common::UTF16ToUTF8(argv_w[optind]);
+#else
+ filepath = argv[optind];
+#endif
+ optind++;
+ }
+ }
+
+#ifdef _WIN32
+ LocalFree(argv_w);
+#endif
+
+ Log::Filter log_filter(Log::Level::Debug);
+ Log::SetFilter(&log_filter);
+
+ MicroProfileOnThreadCreate("EmuThread");
+ SCOPE_EXIT({ MicroProfileShutdown(); });
+
+ if (filepath.empty()) {
+ LOG_CRITICAL(Frontend, "Failed to load ROM: No ROM specified");
+ return -1;
+ }
+
+ log_filter.ParseFilterString(Settings::values.log_filter);
+
+ // Apply the command line arguments
+ Settings::values.gdbstub_port = gdb_port;
+ Settings::values.use_gdbstub = use_gdbstub;
+ Settings::Apply();
+
+ std::unique_ptr<EmuWindow_SDL2> emu_window{std::make_unique<EmuWindow_SDL2>()};
+
+ Core::System& system{Core::System::GetInstance()};
+
+ SCOPE_EXIT({ system.Shutdown(); });
+
+ const Core::System::ResultStatus load_result{system.Load(emu_window.get(), filepath)};
+
+ switch (load_result) {
+ case Core::System::ResultStatus::ErrorGetLoader:
+ LOG_CRITICAL(Frontend, "Failed to obtain loader for %s!", filepath.c_str());
+ return -1;
+ case Core::System::ResultStatus::ErrorLoader:
+ LOG_CRITICAL(Frontend, "Failed to load ROM!");
+ return -1;
+ case Core::System::ResultStatus::ErrorLoader_ErrorEncrypted:
+ LOG_CRITICAL(Frontend, "The game that you are trying to load must be decrypted before "
+ "being used with Citra. \n\n For more information on dumping and "
+ "decrypting games, please refer to: "
+ "https://citra-emu.org/wiki/dumping-game-cartridges/");
+ return -1;
+ case Core::System::ResultStatus::ErrorLoader_ErrorInvalidFormat:
+ LOG_CRITICAL(Frontend, "Error while loading ROM: The ROM format is not supported.");
+ return -1;
+ case Core::System::ResultStatus::ErrorNotInitialized:
+ LOG_CRITICAL(Frontend, "CPUCore not initialized");
+ return -1;
+ case Core::System::ResultStatus::ErrorSystemMode:
+ LOG_CRITICAL(Frontend, "Failed to determine system mode!");
+ return -1;
+ case Core::System::ResultStatus::ErrorVideoCore:
+ LOG_CRITICAL(Frontend, "VideoCore not initialized");
+ return -1;
+ case Core::System::ResultStatus::Success:
+ break; // Expected case
+ }
+
+ Core::Telemetry().AddField(Telemetry::FieldType::App, "Frontend", "SDL");
+
+ while (emu_window->IsOpen()) {
+ system.RunLoop();
+ }
+
+ return 0;
+}
diff --git a/src/yuzu_cmd/citra.rc b/src/yuzu_cmd/citra.rc
new file mode 100644
index 000000000..c490ef302
--- /dev/null
+++ b/src/yuzu_cmd/citra.rc
@@ -0,0 +1,17 @@
+#include "winresrc.h"
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+CITRA_ICON ICON "../../dist/citra.ico"
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// RT_MANIFEST
+//
+
+1 RT_MANIFEST "../../dist/citra.manifest"
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
new file mode 100644
index 000000000..94d1a9f1c
--- /dev/null
+++ b/src/yuzu_cmd/config.cpp
@@ -0,0 +1,160 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <memory>
+#include <SDL.h>
+#include <inih/cpp/INIReader.h>
+#include "citra/config.h"
+#include "citra/default_ini.h"
+#include "common/file_util.h"
+#include "common/logging/log.h"
+#include "common/param_package.h"
+#include "core/settings.h"
+#include "input_common/main.h"
+
+Config::Config() {
+ // TODO: Don't hardcode the path; let the frontend decide where to put the config files.
+ sdl2_config_loc = FileUtil::GetUserPath(D_CONFIG_IDX) + "sdl2-config.ini";
+ sdl2_config = std::make_unique<INIReader>(sdl2_config_loc);
+
+ Reload();
+}
+
+Config::~Config() = default;
+
+bool Config::LoadINI(const std::string& default_contents, bool retry) {
+ const char* location = this->sdl2_config_loc.c_str();
+ if (sdl2_config->ParseError() < 0) {
+ if (retry) {
+ LOG_WARNING(Config, "Failed to load %s. Creating file from defaults...", location);
+ FileUtil::CreateFullPath(location);
+ FileUtil::WriteStringToFile(true, default_contents, location);
+ sdl2_config = std::make_unique<INIReader>(location); // Reopen file
+
+ return LoadINI(default_contents, false);
+ }
+ LOG_ERROR(Config, "Failed.");
+ return false;
+ }
+ LOG_INFO(Config, "Successfully loaded %s", location);
+ return true;
+}
+
+static const std::array<int, Settings::NativeButton::NumButtons> default_buttons = {
+ SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_T,
+ SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H, SDL_SCANCODE_Q, SDL_SCANCODE_W,
+ SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_B,
+};
+
+static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs{{
+ {
+ SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT, SDL_SCANCODE_D,
+ },
+ {
+ SDL_SCANCODE_I, SDL_SCANCODE_K, SDL_SCANCODE_J, SDL_SCANCODE_L, SDL_SCANCODE_D,
+ },
+}};
+
+void Config::ReadValues() {
+ // Controls
+ for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
+ std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
+ Settings::values.buttons[i] =
+ sdl2_config->Get("Controls", Settings::NativeButton::mapping[i], default_param);
+ if (Settings::values.buttons[i].empty())
+ Settings::values.buttons[i] = default_param;
+ }
+
+ for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
+ std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
+ default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
+ default_analogs[i][3], default_analogs[i][4], 0.5f);
+ Settings::values.analogs[i] =
+ sdl2_config->Get("Controls", Settings::NativeAnalog::mapping[i], default_param);
+ if (Settings::values.analogs[i].empty())
+ Settings::values.analogs[i] = default_param;
+ }
+
+ Settings::values.motion_device = sdl2_config->Get(
+ "Controls", "motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01");
+ Settings::values.touch_device =
+ sdl2_config->Get("Controls", "touch_device", "engine:emu_window");
+
+ // Core
+ Settings::values.cpu_core =
+ static_cast<Settings::CpuCore>(sdl2_config->GetInteger("Core", "cpu_core", 0));
+
+ // Renderer
+ Settings::values.use_hw_renderer = sdl2_config->GetBoolean("Renderer", "use_hw_renderer", true);
+ Settings::values.use_shader_jit = sdl2_config->GetBoolean("Renderer", "use_shader_jit", true);
+ Settings::values.resolution_factor =
+ (float)sdl2_config->GetReal("Renderer", "resolution_factor", 1.0);
+ Settings::values.use_vsync = sdl2_config->GetBoolean("Renderer", "use_vsync", false);
+ Settings::values.toggle_framelimit =
+ sdl2_config->GetBoolean("Renderer", "toggle_framelimit", true);
+
+ Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 0.0);
+ Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 0.0);
+ Settings::values.bg_blue = (float)sdl2_config->GetReal("Renderer", "bg_blue", 0.0);
+
+ // Layout
+ Settings::values.layout_option =
+ static_cast<Settings::LayoutOption>(sdl2_config->GetInteger("Layout", "layout_option", 0));
+ Settings::values.swap_screen = sdl2_config->GetBoolean("Layout", "swap_screen", false);
+ Settings::values.custom_layout = sdl2_config->GetBoolean("Layout", "custom_layout", false);
+ Settings::values.custom_top_left =
+ static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_top_left", 0));
+ Settings::values.custom_top_top =
+ static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_top_top", 0));
+ Settings::values.custom_top_right =
+ static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_top_right", 400));
+ Settings::values.custom_top_bottom =
+ static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_top_bottom", 240));
+ Settings::values.custom_bottom_left =
+ static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_bottom_left", 40));
+ Settings::values.custom_bottom_top =
+ static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_bottom_top", 240));
+ Settings::values.custom_bottom_right =
+ static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_bottom_right", 360));
+ Settings::values.custom_bottom_bottom =
+ static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_bottom_bottom", 480));
+
+ // Audio
+ Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto");
+ Settings::values.enable_audio_stretching =
+ sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true);
+ Settings::values.audio_device_id = sdl2_config->Get("Audio", "output_device", "auto");
+
+ // Data Storage
+ Settings::values.use_virtual_sd =
+ sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true);
+
+ // System
+ Settings::values.is_new_3ds = sdl2_config->GetBoolean("System", "is_new_3ds", false);
+ Settings::values.region_value =
+ sdl2_config->GetInteger("System", "region_value", Settings::REGION_VALUE_AUTO_SELECT);
+
+ // Miscellaneous
+ Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Info");
+
+ // Debugging
+ Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false);
+ Settings::values.gdbstub_port =
+ static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689));
+
+ // Web Service
+ Settings::values.enable_telemetry =
+ sdl2_config->GetBoolean("WebService", "enable_telemetry", true);
+ Settings::values.telemetry_endpoint_url = sdl2_config->Get(
+ "WebService", "telemetry_endpoint_url", "https://services.citra-emu.org/api/telemetry");
+ Settings::values.verify_endpoint_url = sdl2_config->Get(
+ "WebService", "verify_endpoint_url", "https://services.citra-emu.org/api/profile");
+ Settings::values.citra_username = sdl2_config->Get("WebService", "citra_username", "");
+ Settings::values.citra_token = sdl2_config->Get("WebService", "citra_token", "");
+}
+
+void Config::Reload() {
+ LoadINI(DefaultINI::sdl2_config_file);
+ ReadValues();
+}
diff --git a/src/yuzu_cmd/config.h b/src/yuzu_cmd/config.h
new file mode 100644
index 000000000..abc90f642
--- /dev/null
+++ b/src/yuzu_cmd/config.h
@@ -0,0 +1,24 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <string>
+
+class INIReader;
+
+class Config {
+ std::unique_ptr<INIReader> sdl2_config;
+ std::string sdl2_config_loc;
+
+ bool LoadINI(const std::string& default_contents = "", bool retry = true);
+ void ReadValues();
+
+public:
+ Config();
+ ~Config();
+
+ void Reload();
+};
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
new file mode 100644
index 000000000..b7b8abe1e
--- /dev/null
+++ b/src/yuzu_cmd/default_ini.h
@@ -0,0 +1,179 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+namespace DefaultINI {
+
+const char* sdl2_config_file = R"(
+[Controls]
+# The input devices and parameters for each 3DS native input
+# It should be in the format of "engine:[engine_name],[param1]:[value1],[param2]:[value2]..."
+# Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values
+
+# for button input, the following devices are available:
+# - "keyboard" (default) for keyboard input. Required parameters:
+# - "code": the code of the key to bind
+# - "sdl" for joystick input using SDL. Required parameters:
+# - "joystick": the index of the joystick to bind
+# - "button"(optional): the index of the button to bind
+# - "hat"(optional): the index of the hat to bind as direction buttons
+# - "axis"(optional): the index of the axis to bind
+# - "direction"(only used for hat): the direction name of the hat to bind. Can be "up", "down", "left" or "right"
+# - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is
+# triggered if the axis value crosses
+# - "direction"(only used for axis): "+" means the button is triggered when the axis value
+# is greater than the threshold; "-" means the button is triggered when the axis value
+# is smaller than the threshold
+button_a=
+button_b=
+button_x=
+button_y=
+button_up=
+button_down=
+button_left=
+button_right=
+button_l=
+button_r=
+button_start=
+button_select=
+button_zl=
+button_zr=
+button_home=
+
+# for analog input, the following devices are available:
+# - "analog_from_button" (default) for emulating analog input from direction buttons. Required parameters:
+# - "up", "down", "left", "right": sub-devices for each direction.
+# Should be in the format as a button input devices using escape characters, for example, "engine$0keyboard$1code$00"
+# - "modifier": sub-devices as a modifier.
+# - "modifier_scale": a float number representing the applied modifier scale to the analog input.
+# Must be in range of 0.0-1.0. Defaults to 0.5
+# - "sdl" for joystick input using SDL. Required parameters:
+# - "joystick": the index of the joystick to bind
+# - "axis_x": the index of the axis to bind as x-axis (default to 0)
+# - "axis_y": the index of the axis to bind as y-axis (default to 1)
+circle_pad=
+c_stick=
+
+# for motion input, the following devices are available:
+# - "motion_emu" (default) for emulating motion input from mouse input. Required parameters:
+# - "update_period": update period in milliseconds (default to 100)
+# - "sensitivity": the coefficient converting mouse movement to tilting angle (default to 0.01)
+motion_device=
+
+# for touch input, the following devices are available:
+# - "emu_window" (default) for emulating touch input from mouse input to the emulation window. No parameters required
+touch_device=
+
+[Core]
+# Which CPU core to use for CPU emulation
+# 0 (default): Unicorn (slow), 1: Dynarmic (faster)
+cpu_core =
+
+[Renderer]
+# Whether to use software or hardware rendering.
+# 0: Software, 1 (default): Hardware
+use_hw_renderer =
+
+# Whether to use the Just-In-Time (JIT) compiler for shader emulation
+# 0: Interpreter (slow), 1 (default): JIT (fast)
+use_shader_jit =
+
+# Resolution scale factor
+# 0: Auto (scales resolution to window size), 1: Native 3DS screen resolution, Otherwise a scale
+# factor for the 3DS resolution
+resolution_factor =
+
+# Whether to enable V-Sync (caps the framerate at 60FPS) or not.
+# 0 (default): Off, 1: On
+use_vsync =
+
+# The clear color for the renderer. What shows up on the sides of the bottom screen.
+# Must be in range of 0.0-1.0. Defaults to 1.0 for all.
+bg_red =
+bg_blue =
+bg_green =
+
+[Layout]
+# Layout for the screen inside the render window.
+# 0 (default): Default Top Bottom Screen, 1: Single Screen Only, 2: Large Screen Small Screen
+layout_option =
+
+# Toggle custom layout (using the settings below) on or off.
+# 0 (default): Off , 1: On
+custom_layout =
+
+# Screen placement when using Custom layout option
+# 0x, 0y is the top left corner of the render window.
+custom_top_left =
+custom_top_top =
+custom_top_right =
+custom_top_bottom =
+custom_bottom_left =
+custom_bottom_top =
+custom_bottom_right =
+custom_bottom_bottom =
+
+#Whether to toggle frame limiter on or off.
+# 0: Off , 1 (default): On
+toggle_framelimit =
+
+# Swaps the prominent screen with the other screen.
+# For example, if Single Screen is chosen, setting this to 1 will display the bottom screen instead of the top screen.
+# 0 (default): Top Screen is prominent, 1: Bottom Screen is prominent
+swap_screen =
+
+[Audio]
+# Which audio output engine to use.
+# auto (default): Auto-select, null: No audio output, sdl2: SDL2 (if available)
+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 =
+
+[Data Storage]
+# Whether to create a virtual SD card.
+# 1 (default): Yes, 0: No
+use_virtual_sd =
+
+[System]
+# The system model that Citra will try to emulate
+# 0: Old 3DS (default), 1: New 3DS
+is_new_3ds =
+
+# The system region that Citra will use during emulation
+# -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan
+region_value =
+
+[Miscellaneous]
+# A filter which removes logs below a certain logging level.
+# Examples: *:Debug Kernel.SVC:Trace Service.*:Critical
+log_filter = *:Info
+
+[Debugging]
+# Port for listening to GDB connections.
+use_gdbstub=false
+gdbstub_port=24689
+
+[WebService]
+# Whether or not to enable telemetry
+# 0: No, 1 (default): Yes
+enable_telemetry =
+# Endpoint URL for submitting telemetry data
+telemetry_endpoint_url = https://services.citra-emu.org/api/telemetry
+# Endpoint URL to verify the username and token
+verify_endpoint_url = https://services.citra-emu.org/api/profile
+# Username and token for Citra Web Service
+# See https://services.citra-emu.org/ for more info
+citra_username =
+citra_token =
+)";
+}
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
new file mode 100644
index 000000000..e65b04e4b
--- /dev/null
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -0,0 +1,177 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <cstdlib>
+#include <string>
+#define SDL_MAIN_HANDLED
+#include <SDL.h>
+#include <glad/glad.h>
+#include "citra/emu_window/emu_window_sdl2.h"
+#include "common/logging/log.h"
+#include "common/scm_rev.h"
+#include "common/string_util.h"
+#include "core/settings.h"
+#include "input_common/keyboard.h"
+#include "input_common/main.h"
+#include "input_common/motion_emu.h"
+#include "network/network.h"
+
+void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
+ TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0));
+ InputCommon::GetMotionEmu()->Tilt(x, y);
+}
+
+void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
+ if (button == SDL_BUTTON_LEFT) {
+ if (state == SDL_PRESSED) {
+ TouchPressed((unsigned)std::max(x, 0), (unsigned)std::max(y, 0));
+ } else {
+ TouchReleased();
+ }
+ } else if (button == SDL_BUTTON_RIGHT) {
+ if (state == SDL_PRESSED) {
+ InputCommon::GetMotionEmu()->BeginTilt(x, y);
+ } else {
+ InputCommon::GetMotionEmu()->EndTilt();
+ }
+ }
+}
+
+void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {
+ if (state == SDL_PRESSED) {
+ InputCommon::GetKeyboard()->PressKey(key);
+ } else if (state == SDL_RELEASED) {
+ InputCommon::GetKeyboard()->ReleaseKey(key);
+ }
+}
+
+bool EmuWindow_SDL2::IsOpen() const {
+ return is_open;
+}
+
+void EmuWindow_SDL2::OnResize() {
+ int width, height;
+ SDL_GetWindowSize(render_window, &width, &height);
+ UpdateCurrentFramebufferLayout(width, height);
+}
+
+EmuWindow_SDL2::EmuWindow_SDL2() {
+ InputCommon::Init();
+ Network::Init();
+
+ SDL_SetMainReady();
+
+ // Initialize the window
+ if (SDL_Init(SDL_INIT_VIDEO) < 0) {
+ LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting...");
+ exit(1);
+ }
+
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
+ SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
+ SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0);
+
+ std::string window_title = Common::StringFromFormat("yuzu %s| %s-%s ", Common::g_build_name,
+ Common::g_scm_branch, Common::g_scm_desc);
+ render_window =
+ SDL_CreateWindow(window_title.c_str(),
+ SDL_WINDOWPOS_UNDEFINED, // x position
+ SDL_WINDOWPOS_UNDEFINED, // y position
+ Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height,
+ SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
+
+ if (render_window == nullptr) {
+ LOG_CRITICAL(Frontend, "Failed to create SDL2 window! Exiting...");
+ exit(1);
+ }
+
+ gl_context = SDL_GL_CreateContext(render_window);
+
+ if (gl_context == nullptr) {
+ LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context! Exiting...");
+ exit(1);
+ }
+
+ if (!gladLoadGLLoader(static_cast<GLADloadproc>(SDL_GL_GetProcAddress))) {
+ LOG_CRITICAL(Frontend, "Failed to initialize GL functions! Exiting...");
+ exit(1);
+ }
+
+ OnResize();
+ OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
+ SDL_PumpEvents();
+ SDL_GL_SetSwapInterval(Settings::values.use_vsync);
+
+ DoneCurrent();
+}
+
+EmuWindow_SDL2::~EmuWindow_SDL2() {
+ SDL_GL_DeleteContext(gl_context);
+ SDL_Quit();
+
+ Network::Shutdown();
+ InputCommon::Shutdown();
+}
+
+void EmuWindow_SDL2::SwapBuffers() {
+ SDL_GL_SwapWindow(render_window);
+}
+
+void EmuWindow_SDL2::PollEvents() {
+ SDL_Event event;
+
+ // SDL_PollEvent returns 0 when there are no more events in the event queue
+ while (SDL_PollEvent(&event)) {
+ switch (event.type) {
+ case SDL_WINDOWEVENT:
+ switch (event.window.event) {
+ case SDL_WINDOWEVENT_SIZE_CHANGED:
+ case SDL_WINDOWEVENT_RESIZED:
+ case SDL_WINDOWEVENT_MAXIMIZED:
+ case SDL_WINDOWEVENT_RESTORED:
+ case SDL_WINDOWEVENT_MINIMIZED:
+ OnResize();
+ break;
+ case SDL_WINDOWEVENT_CLOSE:
+ is_open = false;
+ break;
+ }
+ break;
+ case SDL_KEYDOWN:
+ case SDL_KEYUP:
+ OnKeyEvent(static_cast<int>(event.key.keysym.scancode), event.key.state);
+ break;
+ case SDL_MOUSEMOTION:
+ OnMouseMotion(event.motion.x, event.motion.y);
+ break;
+ case SDL_MOUSEBUTTONDOWN:
+ case SDL_MOUSEBUTTONUP:
+ OnMouseButton(event.button.button, event.button.state, event.button.x, event.button.y);
+ break;
+ case SDL_QUIT:
+ is_open = false;
+ break;
+ }
+ }
+}
+
+void EmuWindow_SDL2::MakeCurrent() {
+ SDL_GL_MakeCurrent(render_window, gl_context);
+}
+
+void EmuWindow_SDL2::DoneCurrent() {
+ SDL_GL_MakeCurrent(render_window, nullptr);
+}
+
+void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest(
+ const std::pair<unsigned, unsigned>& minimal_size) {
+
+ SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second);
+}
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
new file mode 100644
index 000000000..3664d2fbe
--- /dev/null
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
@@ -0,0 +1,59 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <utility>
+#include "core/frontend/emu_window.h"
+
+struct SDL_Window;
+
+class EmuWindow_SDL2 : public EmuWindow {
+public:
+ EmuWindow_SDL2();
+ ~EmuWindow_SDL2();
+
+ /// Swap buffers to display the next frame
+ void SwapBuffers() override;
+
+ /// Polls window events
+ void PollEvents() override;
+
+ /// Makes the graphics context current for the caller thread
+ void MakeCurrent() override;
+
+ /// Releases the GL context from the caller thread
+ void DoneCurrent() override;
+
+ /// Whether the window is still open, and a close request hasn't yet been sent
+ bool IsOpen() const;
+
+private:
+ /// Called by PollEvents when a key is pressed or released.
+ void OnKeyEvent(int key, u8 state);
+
+ /// Called by PollEvents when the mouse moves.
+ void OnMouseMotion(s32 x, s32 y);
+
+ /// Called by PollEvents when a mouse button is pressed or released
+ void OnMouseButton(u32 button, u8 state, s32 x, s32 y);
+
+ /// Called by PollEvents when any event that may cause the window to be resized occurs
+ void OnResize();
+
+ /// Called when a configuration change affects the minimal size of the window
+ void OnMinimalClientAreaChangeRequest(
+ const std::pair<unsigned, unsigned>& minimal_size) override;
+
+ /// Is the window still open?
+ bool is_open = true;
+
+ /// Internal SDL2 render window
+ SDL_Window* render_window;
+
+ using SDL_GLContext = void*;
+ /// The OpenGL context associated with the window
+ SDL_GLContext gl_context;
+};
diff --git a/src/yuzu_cmd/resource.h b/src/yuzu_cmd/resource.h
new file mode 100644
index 000000000..df8e459e4
--- /dev/null
+++ b/src/yuzu_cmd/resource.h
@@ -0,0 +1,16 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by pcafe.rc
+//
+#define IDI_ICON3 103
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 105
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif