early-access version 1255

This commit is contained in:
pineappleEA
2020-12-28 15:15:37 +00:00
parent 84b39492d1
commit 78b48028e1
6254 changed files with 1868140 additions and 0 deletions

36
src/yuzu_cmd/CMakeLists.txt Executable file
View File

@@ -0,0 +1,36 @@
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules)
add_executable(yuzu-cmd
config.cpp
config.h
default_ini.h
emu_window/emu_window_sdl2.cpp
emu_window/emu_window_sdl2.h
emu_window/emu_window_sdl2_gl.cpp
emu_window/emu_window_sdl2_gl.h
emu_window/emu_window_sdl2_vk.cpp
emu_window/emu_window_sdl2_vk.h
resource.h
yuzu.cpp
yuzu.rc
)
create_target_directory_groups(yuzu-cmd)
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)
target_include_directories(yuzu-cmd PRIVATE ../../externals/Vulkan-Headers/include)
if(UNIX AND NOT APPLE)
install(TARGETS yuzu-cmd RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
endif()
if (MSVC)
include(CopyYuzuSDLDeps)
copy_yuzu_SDL_deps(yuzu-cmd)
endif()

475
src/yuzu_cmd/config.cpp Executable file
View File

@@ -0,0 +1,475 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <memory>
#include <sstream>
#include <SDL.h>
#include <inih/cpp/INIReader.h>
#include "common/file_util.h"
#include "common/logging/log.h"
#include "common/param_package.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/settings.h"
#include "input_common/main.h"
#include "input_common/udp/client.h"
#include "yuzu_cmd/config.h"
#include "yuzu_cmd/default_ini.h"
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::GetUserPath(FS::UserPath::ConfigDir) + "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 std::string& location = this->sdl2_config_loc;
if (sdl2_config->ParseError() < 0) {
if (retry) {
LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...", location);
FS::CreateFullPath(location);
FS::WriteStringToFile(true, location, default_contents);
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 {}", 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,
},
}};
static const std::array<int, Settings::NativeMouseButton::NumMouseButtons> default_mouse_buttons = {
SDL_SCANCODE_LEFTBRACKET, SDL_SCANCODE_RIGHTBRACKET, SDL_SCANCODE_APOSTROPHE,
SDL_SCANCODE_MINUS, SDL_SCANCODE_EQUALS,
};
static const std::array<int, 0x8A> keyboard_keys = {
0,
0,
0,
0,
SDL_SCANCODE_A,
SDL_SCANCODE_B,
SDL_SCANCODE_C,
SDL_SCANCODE_D,
SDL_SCANCODE_E,
SDL_SCANCODE_F,
SDL_SCANCODE_G,
SDL_SCANCODE_H,
SDL_SCANCODE_I,
SDL_SCANCODE_J,
SDL_SCANCODE_K,
SDL_SCANCODE_L,
SDL_SCANCODE_M,
SDL_SCANCODE_N,
SDL_SCANCODE_O,
SDL_SCANCODE_P,
SDL_SCANCODE_Q,
SDL_SCANCODE_R,
SDL_SCANCODE_S,
SDL_SCANCODE_T,
SDL_SCANCODE_U,
SDL_SCANCODE_V,
SDL_SCANCODE_W,
SDL_SCANCODE_X,
SDL_SCANCODE_Y,
SDL_SCANCODE_Z,
SDL_SCANCODE_1,
SDL_SCANCODE_2,
SDL_SCANCODE_3,
SDL_SCANCODE_4,
SDL_SCANCODE_5,
SDL_SCANCODE_6,
SDL_SCANCODE_7,
SDL_SCANCODE_8,
SDL_SCANCODE_9,
SDL_SCANCODE_0,
SDL_SCANCODE_RETURN,
SDL_SCANCODE_ESCAPE,
SDL_SCANCODE_BACKSPACE,
SDL_SCANCODE_TAB,
SDL_SCANCODE_SPACE,
SDL_SCANCODE_MINUS,
SDL_SCANCODE_EQUALS,
SDL_SCANCODE_LEFTBRACKET,
SDL_SCANCODE_RIGHTBRACKET,
SDL_SCANCODE_BACKSLASH,
0,
SDL_SCANCODE_SEMICOLON,
SDL_SCANCODE_APOSTROPHE,
SDL_SCANCODE_GRAVE,
SDL_SCANCODE_COMMA,
SDL_SCANCODE_PERIOD,
SDL_SCANCODE_SLASH,
SDL_SCANCODE_CAPSLOCK,
SDL_SCANCODE_F1,
SDL_SCANCODE_F2,
SDL_SCANCODE_F3,
SDL_SCANCODE_F4,
SDL_SCANCODE_F5,
SDL_SCANCODE_F6,
SDL_SCANCODE_F7,
SDL_SCANCODE_F8,
SDL_SCANCODE_F9,
SDL_SCANCODE_F10,
SDL_SCANCODE_F11,
SDL_SCANCODE_F12,
0,
SDL_SCANCODE_SCROLLLOCK,
SDL_SCANCODE_PAUSE,
SDL_SCANCODE_INSERT,
SDL_SCANCODE_HOME,
SDL_SCANCODE_PAGEUP,
SDL_SCANCODE_DELETE,
SDL_SCANCODE_END,
SDL_SCANCODE_PAGEDOWN,
SDL_SCANCODE_RIGHT,
SDL_SCANCODE_LEFT,
SDL_SCANCODE_DOWN,
SDL_SCANCODE_UP,
SDL_SCANCODE_NUMLOCKCLEAR,
SDL_SCANCODE_KP_DIVIDE,
SDL_SCANCODE_KP_MULTIPLY,
SDL_SCANCODE_KP_MINUS,
SDL_SCANCODE_KP_PLUS,
SDL_SCANCODE_KP_ENTER,
SDL_SCANCODE_KP_1,
SDL_SCANCODE_KP_2,
SDL_SCANCODE_KP_3,
SDL_SCANCODE_KP_4,
SDL_SCANCODE_KP_5,
SDL_SCANCODE_KP_6,
SDL_SCANCODE_KP_7,
SDL_SCANCODE_KP_8,
SDL_SCANCODE_KP_9,
SDL_SCANCODE_KP_0,
SDL_SCANCODE_KP_PERIOD,
0,
0,
SDL_SCANCODE_POWER,
SDL_SCANCODE_KP_EQUALS,
SDL_SCANCODE_F13,
SDL_SCANCODE_F14,
SDL_SCANCODE_F15,
SDL_SCANCODE_F16,
SDL_SCANCODE_F17,
SDL_SCANCODE_F18,
SDL_SCANCODE_F19,
SDL_SCANCODE_F20,
SDL_SCANCODE_F21,
SDL_SCANCODE_F22,
SDL_SCANCODE_F23,
SDL_SCANCODE_F24,
0,
SDL_SCANCODE_HELP,
SDL_SCANCODE_MENU,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
SDL_SCANCODE_KP_COMMA,
SDL_SCANCODE_KP_LEFTPAREN,
SDL_SCANCODE_KP_RIGHTPAREN,
0,
0,
0,
0,
};
static const std::array<int, 8> keyboard_mods{
SDL_SCANCODE_LCTRL, SDL_SCANCODE_LSHIFT, SDL_SCANCODE_LALT, SDL_SCANCODE_LGUI,
SDL_SCANCODE_RCTRL, SDL_SCANCODE_RSHIFT, SDL_SCANCODE_RALT, SDL_SCANCODE_RGUI,
};
void Config::ReadValues() {
// Controls
for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
const auto group = fmt::format("ControlsP{}", p);
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
Settings::values.players.GetValue()[p].buttons[i] =
sdl2_config->Get(group, Settings::NativeButton::mapping[i], default_param);
if (Settings::values.players.GetValue()[p].buttons[i].empty())
Settings::values.players.GetValue()[p].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.players.GetValue()[p].analogs[i] =
sdl2_config->Get(group, Settings::NativeAnalog::mapping[i], default_param);
if (Settings::values.players.GetValue()[p].analogs[i].empty())
Settings::values.players.GetValue()[p].analogs[i] = default_param;
}
}
Settings::values.mouse_enabled =
sdl2_config->GetBoolean("ControlsGeneral", "mouse_enabled", false);
for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
std::string default_param = InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]);
Settings::values.mouse_buttons[i] = sdl2_config->Get(
"ControlsGeneral", std::string("mouse_") + Settings::NativeMouseButton::mapping[i],
default_param);
if (Settings::values.mouse_buttons[i].empty())
Settings::values.mouse_buttons[i] = default_param;
}
Settings::values.motion_device = sdl2_config->Get(
"ControlsGeneral", "motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01");
Settings::values.keyboard_enabled =
sdl2_config->GetBoolean("ControlsGeneral", "keyboard_enabled", false);
Settings::values.debug_pad_enabled =
sdl2_config->GetBoolean("ControlsGeneral", "debug_pad_enabled", false);
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
Settings::values.debug_pad_buttons[i] = sdl2_config->Get(
"ControlsGeneral", std::string("debug_pad_") + Settings::NativeButton::mapping[i],
default_param);
if (Settings::values.debug_pad_buttons[i].empty())
Settings::values.debug_pad_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.debug_pad_analogs[i] = sdl2_config->Get(
"ControlsGeneral", std::string("debug_pad_") + Settings::NativeAnalog::mapping[i],
default_param);
if (Settings::values.debug_pad_analogs[i].empty())
Settings::values.debug_pad_analogs[i] = default_param;
}
Settings::values.vibration_enabled.SetValue(
sdl2_config->GetBoolean("ControlsGeneral", "vibration_enabled", true));
Settings::values.enable_accurate_vibrations.SetValue(
sdl2_config->GetBoolean("ControlsGeneral", "enable_accurate_vibrations", false));
Settings::values.motion_enabled.SetValue(
sdl2_config->GetBoolean("ControlsGeneral", "motion_enabled", true));
Settings::values.touchscreen.enabled =
sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true);
Settings::values.touchscreen.device =
sdl2_config->Get("ControlsGeneral", "touch_device", "engine:emu_window");
Settings::values.touchscreen.finger =
sdl2_config->GetInteger("ControlsGeneral", "touch_finger", 0);
Settings::values.touchscreen.rotation_angle =
sdl2_config->GetInteger("ControlsGeneral", "touch_angle", 0);
Settings::values.touchscreen.diameter_x =
sdl2_config->GetInteger("ControlsGeneral", "touch_diameter_x", 15);
Settings::values.touchscreen.diameter_y =
sdl2_config->GetInteger("ControlsGeneral", "touch_diameter_y", 15);
Settings::values.udp_input_servers =
sdl2_config->Get("Controls", "udp_input_address", InputCommon::CemuhookUDP::DEFAULT_SRV);
std::transform(keyboard_keys.begin(), keyboard_keys.end(),
Settings::values.keyboard_keys.begin(), InputCommon::GenerateKeyboardParam);
std::transform(keyboard_mods.begin(), keyboard_mods.end(),
Settings::values.keyboard_keys.begin() +
Settings::NativeKeyboard::LeftControlKey,
InputCommon::GenerateKeyboardParam);
std::transform(keyboard_mods.begin(), keyboard_mods.end(),
Settings::values.keyboard_mods.begin(), InputCommon::GenerateKeyboardParam);
// Data Storage
Settings::values.use_virtual_sd =
sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true);
FS::GetUserPath(
FS::UserPath::NANDDir,
sdl2_config->Get("Data Storage", "nand_directory", FS::GetUserPath(FS::UserPath::NANDDir)));
FS::GetUserPath(
FS::UserPath::SDMCDir,
sdl2_config->Get("Data Storage", "sdmc_directory", FS::GetUserPath(FS::UserPath::SDMCDir)));
FS::GetUserPath(
FS::UserPath::LoadDir,
sdl2_config->Get("Data Storage", "load_directory", FS::GetUserPath(FS::UserPath::LoadDir)));
FS::GetUserPath(
FS::UserPath::DumpDir,
sdl2_config->Get("Data Storage", "dump_directory", FS::GetUserPath(FS::UserPath::DumpDir)));
FS::GetUserPath(FS::UserPath::CacheDir,
sdl2_config->Get("Data Storage", "cache_directory",
FS::GetUserPath(FS::UserPath::CacheDir)));
Settings::values.gamecard_inserted =
sdl2_config->GetBoolean("Data Storage", "gamecard_inserted", false);
Settings::values.gamecard_current_game =
sdl2_config->GetBoolean("Data Storage", "gamecard_current_game", false);
Settings::values.gamecard_path = sdl2_config->Get("Data Storage", "gamecard_path", "");
// System
Settings::values.use_docked_mode.SetValue(
sdl2_config->GetBoolean("System", "use_docked_mode", false));
Settings::values.current_user = std::clamp<int>(
sdl2_config->GetInteger("System", "current_user", 0), 0, Service::Account::MAX_USERS - 1);
const auto rng_seed_enabled = sdl2_config->GetBoolean("System", "rng_seed_enabled", false);
if (rng_seed_enabled) {
Settings::values.rng_seed.SetValue(sdl2_config->GetInteger("System", "rng_seed", 0));
} else {
Settings::values.rng_seed.SetValue(std::nullopt);
}
const auto custom_rtc_enabled = sdl2_config->GetBoolean("System", "custom_rtc_enabled", false);
if (custom_rtc_enabled) {
Settings::values.custom_rtc.SetValue(
std::chrono::seconds(sdl2_config->GetInteger("System", "custom_rtc", 0)));
} else {
Settings::values.custom_rtc.SetValue(std::nullopt);
}
Settings::values.language_index.SetValue(
sdl2_config->GetInteger("System", "language_index", 1));
Settings::values.time_zone_index.SetValue(
sdl2_config->GetInteger("System", "time_zone_index", 0));
// Core
Settings::values.use_multi_core.SetValue(
sdl2_config->GetBoolean("Core", "use_multi_core", true));
// Renderer
const int renderer_backend = sdl2_config->GetInteger(
"Renderer", "backend", static_cast<int>(Settings::RendererBackend::OpenGL));
Settings::values.renderer_backend.SetValue(
static_cast<Settings::RendererBackend>(renderer_backend));
Settings::values.renderer_debug = sdl2_config->GetBoolean("Renderer", "debug", false);
Settings::values.vulkan_device.SetValue(
sdl2_config->GetInteger("Renderer", "vulkan_device", 0));
Settings::values.aspect_ratio.SetValue(
static_cast<int>(sdl2_config->GetInteger("Renderer", "aspect_ratio", 0)));
Settings::values.max_anisotropy.SetValue(
static_cast<int>(sdl2_config->GetInteger("Renderer", "max_anisotropy", 0)));
Settings::values.use_frame_limit.SetValue(
sdl2_config->GetBoolean("Renderer", "use_frame_limit", true));
Settings::values.frame_limit.SetValue(
static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100)));
Settings::values.use_disk_shader_cache.SetValue(
sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", false));
const int gpu_accuracy_level = sdl2_config->GetInteger("Renderer", "gpu_accuracy", 0);
Settings::values.gpu_accuracy.SetValue(static_cast<Settings::GPUAccuracy>(gpu_accuracy_level));
Settings::values.use_asynchronous_gpu_emulation.SetValue(
sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", true));
Settings::values.use_vsync.SetValue(
static_cast<u16>(sdl2_config->GetInteger("Renderer", "use_vsync", 1)));
Settings::values.use_assembly_shaders.SetValue(
sdl2_config->GetBoolean("Renderer", "use_assembly_shaders", true));
Settings::values.use_asynchronous_shaders.SetValue(
sdl2_config->GetBoolean("Renderer", "use_asynchronous_shaders", false));
Settings::values.use_asynchronous_shaders.SetValue(
sdl2_config->GetBoolean("Renderer", "use_asynchronous_shaders", false));
Settings::values.use_fast_gpu_time.SetValue(
sdl2_config->GetBoolean("Renderer", "use_fast_gpu_time", true));
Settings::values.bg_red.SetValue(
static_cast<float>(sdl2_config->GetReal("Renderer", "bg_red", 0.0)));
Settings::values.bg_green.SetValue(
static_cast<float>(sdl2_config->GetReal("Renderer", "bg_green", 0.0)));
Settings::values.bg_blue.SetValue(
static_cast<float>(sdl2_config->GetReal("Renderer", "bg_blue", 0.0)));
// Audio
Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto");
Settings::values.enable_audio_stretching.SetValue(
sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true));
Settings::values.audio_device_id = sdl2_config->Get("Audio", "output_device", "auto");
Settings::values.volume.SetValue(
static_cast<float>(sdl2_config->GetReal("Audio", "volume", 1)));
// Miscellaneous
Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace");
Settings::values.use_dev_keys = sdl2_config->GetBoolean("Miscellaneous", "use_dev_keys", false);
// Debugging
Settings::values.record_frame_times =
sdl2_config->GetBoolean("Debugging", "record_frame_times", false);
Settings::values.program_args = sdl2_config->Get("Debugging", "program_args", "");
Settings::values.dump_exefs = sdl2_config->GetBoolean("Debugging", "dump_exefs", false);
Settings::values.dump_nso = sdl2_config->GetBoolean("Debugging", "dump_nso", false);
Settings::values.reporting_services =
sdl2_config->GetBoolean("Debugging", "reporting_services", false);
Settings::values.quest_flag = sdl2_config->GetBoolean("Debugging", "quest_flag", false);
Settings::values.disable_macro_jit =
sdl2_config->GetBoolean("Debugging", "disable_macro_jit", false);
const auto title_list = sdl2_config->Get("AddOns", "title_ids", "");
std::stringstream ss(title_list);
std::string line;
while (std::getline(ss, line, '|')) {
const auto title_id = std::stoul(line, nullptr, 16);
const auto disabled_list = sdl2_config->Get("AddOns", "disabled_" + line, "");
std::stringstream inner_ss(disabled_list);
std::string inner_line;
std::vector<std::string> out;
while (std::getline(inner_ss, inner_line, '|')) {
out.push_back(inner_line);
}
Settings::values.disabled_addons.insert_or_assign(title_id, out);
}
// Web Service
Settings::values.enable_telemetry =
sdl2_config->GetBoolean("WebService", "enable_telemetry", true);
Settings::values.web_api_url =
sdl2_config->Get("WebService", "web_api_url", "https://api.yuzu-emu.org");
Settings::values.yuzu_username = sdl2_config->Get("WebService", "yuzu_username", "");
Settings::values.yuzu_token = sdl2_config->Get("WebService", "yuzu_token", "");
// Services
Settings::values.bcat_backend = sdl2_config->Get("Services", "bcat_backend", "null");
Settings::values.bcat_boxcat_local =
sdl2_config->GetBoolean("Services", "bcat_boxcat_local", false);
}
void Config::Reload() {
LoadINI(DefaultINI::sdl2_config_file);
ReadValues();
}

24
src/yuzu_cmd/config.h Executable file
View File

@@ -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();
};

354
src/yuzu_cmd/default_ini.h Executable file
View File

@@ -0,0 +1,354 @@
// 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 Switch 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_lstick=
button_rstick=
button_l=
button_r=
button_zl=
button_zr=
button_plus=
button_minus=
button_dleft=
button_dup=
button_dright=
button_ddown=
button_lstick_left=
button_lstick_up=
button_lstick_right=
button_lstick_down=
button_sl=
button_sr=
button_home=
button_screenshot=
# 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)
lstick=
rstick=
# Whether to enable or disable vibration
# 0: Disabled, 1 (default): Enabled
vibration_enabled=
# Whether to enable or disable accurate vibrations
# 0 (default): Disabled, 1: Enabled
enable_accurate_vibrations=
# 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)
# - "cemuhookudp" reads motion input from a udp server that uses cemuhook's udp protocol
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
# - "cemuhookudp" reads touch input from a udp server that uses cemuhook's udp protocol
# - "min_x", "min_y", "max_x", "max_y": defines the udp device's touch screen coordinate system
touch_device=
# Most desktop operating systems do not expose a way to poll the motion state of the controllers
# so as a way around it, cemuhook created a udp client/server protocol to broadcast the data directly
# from a controller device to the client program. Citra has a client that can connect and read
# from any cemuhook compatible motion program.
# IPv4 address of the udp input server (Default "127.0.0.1")
udp_input_address=127.0.0.1
# Port of the udp input server. (Default 26760)
udp_input_port=
# The pad to request data on. Should be between 0 (Pad 1) and 3 (Pad 4). (Default 0)
udp_pad_index=
[Core]
# Whether to use multi-core for CPU emulation
# 0: Disabled, 1 (default): Enabled
use_multi_core=
[Cpu]
# Enable inline page tables optimization (faster guest memory access)
# 0: Disabled, 1 (default): Enabled
cpuopt_page_tables =
# Enable block linking CPU optimization (reduce block dispatcher use during predictable jumps)
# 0: Disabled, 1 (default): Enabled
cpuopt_block_linking =
# Enable return stack buffer CPU optimization (reduce block dispatcher use during predictable returns)
# 0: Disabled, 1 (default): Enabled
cpuopt_return_stack_buffer =
# Enable fast dispatcher CPU optimization (use a two-tiered dispatcher architecture)
# 0: Disabled, 1 (default): Enabled
cpuopt_fast_dispatcher =
# Enable context elimination CPU Optimization (reduce host memory use for guest context)
# 0: Disabled, 1 (default): Enabled
cpuopt_context_elimination =
# Enable constant propagation CPU optimization (basic IR optimization)
# 0: Disabled, 1 (default): Enabled
cpuopt_const_prop =
# Enable miscellaneous CPU optimizations (basic IR optimization)
# 0: Disabled, 1 (default): Enabled
cpuopt_misc_ir =
# Enable reduction of memory misalignment checks (reduce memory fallbacks for misaligned access)
# 0: Disabled, 1 (default): Enabled
cpuopt_reduce_misalign_checks =
[Renderer]
# Which backend API to use.
# 0 (default): OpenGL, 1: Vulkan
backend =
# Enable graphics API debugging mode.
# 0 (default): Disabled, 1: Enabled
debug =
# Which Vulkan physical device to use (defaults to 0)
vulkan_device =
# 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 =
# Aspect ratio
# 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Stretch to Window
aspect_ratio =
# Anisotropic filtering
# 0: Default, 1: 2x, 2: 4x, 3: 8x, 4: 16x
max_anisotropy =
# Whether to enable V-Sync (caps the framerate at 60FPS) or not.
# 0 (default): Off, 1: On
use_vsync =
# Whether to use OpenGL assembly shaders or not. NV_gpu_program5 is required.
# 0: Off, 1 (default): On
use_assembly_shaders =
# Whether to allow asynchronous shader building.
# 0 (default): Off, 1: On
use_asynchronous_shaders =
# Turns on the frame limiter, which will limit frames output to the target game speed
# 0: Off, 1: On (default)
use_frame_limit =
# Limits the speed of the game to run no faster than this value as a percentage of target speed
# 1 - 9999: Speed limit as a percentage of target game speed. 100 (default)
frame_limit =
# Whether to use disk based shader cache
# 0 (default): Off, 1 : On
use_disk_shader_cache =
# Which gpu accuracy level to use
# 0 (Normal), 1 (High), 2 (Extreme)
gpu_accuracy =
# Whether to use asynchronous GPU emulation
# 0 : Off (slow), 1 (default): On (fast)
use_asynchronous_gpu_emulation =
# Forces VSync on the display thread. Usually doesn't impact performance, but on some drivers it can
# so only turn this off if you notice a speed difference.
# 0: Off, 1 (default): 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 =
# 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, cubeb: Cubeb audio engine (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 =
# Output volume.
# 1.0 (default): 100%, 0.0; mute
volume =
[Data Storage]
# Whether to create a virtual SD card.
# 1 (default): Yes, 0: No
use_virtual_sd =
# Whether or not to enable gamecard emulation
# 1: Yes, 0 (default): No
gamecard_inserted =
# Whether or not the gamecard should be emulated as the current game
# If 'gamecard_inserted' is 0 this setting is irrelevant
# 1: Yes, 0 (default): No
gamecard_current_game =
# Path to an XCI file to use as the gamecard
# If 'gamecard_inserted' is 0 this setting is irrelevant
# If 'gamecard_current_game' is 1 this setting is irrelevant
gamecard_path =
[System]
# Whether the system is docked
# 1: Yes, 0 (default): No
use_docked_mode =
# Allow the use of NFC in games
# 1 (default): Yes, 0 : No
enable_nfc =
# Sets the seed for the RNG generator built into the switch
# rng_seed will be ignored and randomly generated if rng_seed_enabled is false
rng_seed_enabled =
rng_seed =
# Sets the current time (in seconds since 12:00 AM Jan 1, 1970) that will be used by the time service
# This will auto-increment, with the time set being the time the game is started
# This override will only occur if custom_rtc_enabled is true, otherwise the current time is used
custom_rtc_enabled =
custom_rtc =
# Sets the account username, max length is 32 characters
# yuzu (default)
username = yuzu
# Sets the systems language index
# 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese,
# 7: Korean, 8: Dutch, 9: Portuguese, 10: Russian, 11: Taiwanese, 12: British English, 13: Canadian French,
# 14: Latin American Spanish, 15: Simplified Chinese, 16: Traditional Chinese
language_index =
# The system region that yuzu will use during emulation
# -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan
region_value =
# The system time zone that yuzu will use during emulation
# 0: Auto-select (default), 1: Default (system archive value), Others: Index for specified time zone
time_zone_index =
[Miscellaneous]
# A filter which removes logs below a certain logging level.
# Examples: *:Debug Kernel.SVC:Trace Service.*:Critical
log_filter = *:Trace
[Debugging]
# Record frame time data, can be found in the log directory. Boolean value
record_frame_times =
# Determines whether or not yuzu will dump the ExeFS of all games it attempts to load while loading them
dump_exefs=false
# Determines whether or not yuzu will dump all NSOs it attempts to load while loading them
dump_nso=false
# Determines whether or not yuzu will report to the game that the emulated console is in Kiosk Mode
# false: Retail/Normal Mode (default), true: Kiosk Mode
quest_flag =
# Enables/Disables the macro JIT compiler
disable_macro_jit=false
[WebService]
# Whether or not to enable telemetry
# 0: No, 1 (default): Yes
enable_telemetry =
# URL for Web API
web_api_url = https://api.yuzu-emu.org
# Username and token for yuzu Web Service
# See https://profile.yuzu-emu.org/ for more info
yuzu_username =
yuzu_token =
[Services]
# The name of the backend to use for BCAT
# If this is set to 'boxcat' boxcat will be used, otherwise a null implementation will be used
bcat_backend =
[AddOns]
# Used to disable add-ons
# List of title IDs of games that will have add-ons disabled (separated by '|'):
title_ids =
# 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 '|')
# e.x. disabled_0100000000010000 = Update|DLC <- disables Updates and DLC on Super Mario Odyssey
)";
}

View File

@@ -0,0 +1,198 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <SDL.h>
#include "common/logging/log.h"
#include "common/scm_rev.h"
#include "core/core.h"
#include "core/perf_stats.h"
#include "input_common/keyboard.h"
#include "input_common/main.h"
#include "input_common/mouse/mouse_input.h"
#include "input_common/sdl/sdl.h"
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
EmuWindow_SDL2::EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem_)
: input_subsystem{input_subsystem_} {
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) {
LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting...");
exit(1);
}
input_subsystem->Initialize();
SDL_SetMainReady();
}
EmuWindow_SDL2::~EmuWindow_SDL2() {
input_subsystem->Shutdown();
SDL_Quit();
}
void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0));
input_subsystem->GetMouse()->MouseMove(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) {
input_subsystem->GetMouse()->PressButton(x, y, button);
} else {
input_subsystem->GetMouse()->ReleaseButton(button);
}
}
}
std::pair<unsigned, unsigned> EmuWindow_SDL2::TouchToPixelPos(float touch_x, float touch_y) const {
int w, h;
SDL_GetWindowSize(render_window, &w, &h);
touch_x *= w;
touch_y *= h;
return {static_cast<unsigned>(std::max(std::round(touch_x), 0.0f)),
static_cast<unsigned>(std::max(std::round(touch_y), 0.0f))};
}
void EmuWindow_SDL2::OnFingerDown(float x, float y) {
// TODO(NeatNit): keep track of multitouch using the fingerID and a dictionary of some kind
// This isn't critical because the best we can do when we have that is to average them, like the
// 3DS does
const auto [px, py] = TouchToPixelPos(x, y);
TouchPressed(px, py);
}
void EmuWindow_SDL2::OnFingerMotion(float x, float y) {
const auto [px, py] = TouchToPixelPos(x, y);
TouchMoved(px, py);
}
void EmuWindow_SDL2::OnFingerUp() {
TouchReleased();
}
void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {
if (state == SDL_PRESSED) {
input_subsystem->GetKeyboard()->PressKey(key);
} else if (state == SDL_RELEASED) {
input_subsystem->GetKeyboard()->ReleaseKey(key);
}
}
bool EmuWindow_SDL2::IsOpen() const {
return is_open;
}
bool EmuWindow_SDL2::IsShown() const {
return is_shown;
}
void EmuWindow_SDL2::OnResize() {
int width, height;
SDL_GetWindowSize(render_window, &width, &height);
UpdateCurrentFramebufferLayout(width, height);
}
void EmuWindow_SDL2::Fullscreen() {
if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN) == 0) {
return;
}
LOG_ERROR(Frontend, "Fullscreening failed: {}", SDL_GetError());
// Try a different fullscreening method
LOG_INFO(Frontend, "Attempting to use borderless fullscreen...");
if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN_DESKTOP) == 0) {
return;
}
LOG_ERROR(Frontend, "Borderless fullscreening failed: {}", SDL_GetError());
// Fallback algorithm: Maximise window.
// Works on all systems (unless something is seriously wrong), so no fallback for this one.
LOG_INFO(Frontend, "Falling back on a maximised window...");
SDL_MaximizeWindow(render_window);
}
void EmuWindow_SDL2::WaitEvent() {
// Called on main thread
SDL_Event event;
if (!SDL_WaitEvent(&event)) {
LOG_CRITICAL(Frontend, "SDL_WaitEvent failed: {}", SDL_GetError());
exit(1);
}
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:
OnResize();
break;
case SDL_WINDOWEVENT_MINIMIZED:
case SDL_WINDOWEVENT_EXPOSED:
is_shown = event.window.event == SDL_WINDOWEVENT_EXPOSED;
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:
// ignore if it came from touch
if (event.button.which != SDL_TOUCH_MOUSEID)
OnMouseMotion(event.motion.x, event.motion.y);
break;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
// ignore if it came from touch
if (event.button.which != SDL_TOUCH_MOUSEID) {
OnMouseButton(event.button.button, event.button.state, event.button.x, event.button.y);
}
break;
case SDL_FINGERDOWN:
OnFingerDown(event.tfinger.x, event.tfinger.y);
break;
case SDL_FINGERMOTION:
OnFingerMotion(event.tfinger.x, event.tfinger.y);
break;
case SDL_FINGERUP:
OnFingerUp();
break;
case SDL_QUIT:
is_open = false;
break;
default:
break;
}
const u32 current_time = SDL_GetTicks();
if (current_time > last_time + 2000) {
const auto results = Core::System::GetInstance().GetAndResetPerfStats();
const auto title =
fmt::format("yuzu {} | {}-{} | FPS: {:.0f} ({:.0f}%)", Common::g_build_fullname,
Common::g_scm_branch, Common::g_scm_desc, results.game_fps,
results.emulation_speed * 100.0);
SDL_SetWindowTitle(render_window, title.c_str());
last_time = current_time;
}
}
void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned> minimal_size) {
SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second);
}

View File

@@ -0,0 +1,80 @@
// 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;
namespace Core {
class System;
}
namespace InputCommon {
class InputSubsystem;
}
class EmuWindow_SDL2 : public Core::Frontend::EmuWindow {
public:
explicit EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem);
~EmuWindow_SDL2();
/// Whether the window is still open, and a close request hasn't yet been sent
bool IsOpen() const;
/// Returns if window is shown (not minimized)
bool IsShown() const override;
/// Wait for the next event on the main thread.
void WaitEvent();
protected:
/// Called by WaitEvent when a key is pressed or released.
void OnKeyEvent(int key, u8 state);
/// Called by WaitEvent when the mouse moves.
void OnMouseMotion(s32 x, s32 y);
/// Called by WaitEvent when a mouse button is pressed or released
void OnMouseButton(u32 button, u8 state, s32 x, s32 y);
/// Translates pixel position (0..1) to pixel positions
std::pair<unsigned, unsigned> TouchToPixelPos(float touch_x, float touch_y) const;
/// Called by WaitEvent when a finger starts touching the touchscreen
void OnFingerDown(float x, float y);
/// Called by WaitEvent when a finger moves while touching the touchscreen
void OnFingerMotion(float x, float y);
/// Called by WaitEvent when a finger stops touching the touchscreen
void OnFingerUp();
/// Called by WaitEvent when any event that may cause the window to be resized occurs
void OnResize();
/// Called when user passes the fullscreen parameter flag
void Fullscreen();
/// Called when a configuration change affects the minimal size of the window
void OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned> minimal_size) override;
/// Is the window still open?
bool is_open = true;
/// Is the window being shown?
bool is_shown = true;
/// Internal SDL2 render window
SDL_Window* render_window{};
/// Keeps track of how often to update the title bar during gameplay
u32 last_time = 0;
/// Input subsystem to use with this window.
InputCommon::InputSubsystem* input_subsystem;
};

View File

@@ -0,0 +1,163 @@
// Copyright 2019 yuzu 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 <fmt/format.h>
#include <glad/glad.h>
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/scm_rev.h"
#include "common/string_util.h"
#include "core/core.h"
#include "core/settings.h"
#include "input_common/keyboard.h"
#include "input_common/main.h"
#include "video_core/renderer_base.h"
#include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h"
class SDLGLContext : public Core::Frontend::GraphicsContext {
public:
explicit SDLGLContext() {
// create a hidden window to make the shared context against
window = SDL_CreateWindow(NULL, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 0, 0,
SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL);
context = SDL_GL_CreateContext(window);
}
~SDLGLContext() {
DoneCurrent();
SDL_GL_DeleteContext(context);
SDL_DestroyWindow(window);
}
void MakeCurrent() override {
if (is_current) {
return;
}
is_current = SDL_GL_MakeCurrent(window, context) == 0;
}
void DoneCurrent() override {
if (!is_current) {
return;
}
SDL_GL_MakeCurrent(window, nullptr);
is_current = false;
}
private:
SDL_Window* window;
SDL_GLContext context;
bool is_current = false;
};
bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() {
std::vector<std::string_view> unsupported_ext;
if (!GLAD_GL_ARB_buffer_storage)
unsupported_ext.push_back("ARB_buffer_storage");
if (!GLAD_GL_ARB_direct_state_access)
unsupported_ext.push_back("ARB_direct_state_access");
if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev)
unsupported_ext.push_back("ARB_vertex_type_10f_11f_11f_rev");
if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge)
unsupported_ext.push_back("ARB_texture_mirror_clamp_to_edge");
if (!GLAD_GL_ARB_multi_bind)
unsupported_ext.push_back("ARB_multi_bind");
if (!GLAD_GL_ARB_clip_control)
unsupported_ext.push_back("ARB_clip_control");
// Extensions required to support some texture formats.
if (!GLAD_GL_EXT_texture_compression_s3tc)
unsupported_ext.push_back("EXT_texture_compression_s3tc");
if (!GLAD_GL_ARB_texture_compression_rgtc)
unsupported_ext.push_back("ARB_texture_compression_rgtc");
if (!GLAD_GL_ARB_depth_buffer_float)
unsupported_ext.push_back("ARB_depth_buffer_float");
for (const auto& extension : unsupported_ext)
LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", extension);
return unsupported_ext.empty();
}
EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem, bool fullscreen)
: EmuWindow_SDL2{input_subsystem} {
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
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);
SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1);
if (Settings::values.renderer_debug) {
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
}
SDL_GL_SetSwapInterval(0);
std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_fullname,
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! {}", SDL_GetError());
exit(1);
}
dummy_window = SDL_CreateWindow(NULL, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 0, 0,
SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL);
if (fullscreen) {
Fullscreen();
}
window_context = SDL_GL_CreateContext(render_window);
core_context = CreateSharedContext();
if (window_context == nullptr) {
LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context: {}", SDL_GetError());
exit(1);
}
if (core_context == nullptr) {
LOG_CRITICAL(Frontend, "Failed to create shared SDL2 GL context: {}", SDL_GetError());
exit(1);
}
if (!gladLoadGLLoader(static_cast<GLADloadproc>(SDL_GL_GetProcAddress))) {
LOG_CRITICAL(Frontend, "Failed to initialize GL functions! {}", SDL_GetError());
exit(1);
}
if (!SupportsRequiredGLExtensions()) {
LOG_CRITICAL(Frontend, "GPU does not support all required OpenGL extensions! Exiting...");
exit(1);
}
OnResize();
OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
SDL_PumpEvents();
LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch,
Common::g_scm_desc);
Settings::LogSettings();
}
EmuWindow_SDL2_GL::~EmuWindow_SDL2_GL() {
core_context.reset();
SDL_GL_DeleteContext(window_context);
}
std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateSharedContext() const {
return std::make_unique<SDLGLContext>();
}

View File

@@ -0,0 +1,36 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include "core/frontend/emu_window.h"
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
namespace InputCommon {
class InputSubsystem;
}
class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 {
public:
explicit EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem, bool fullscreen);
~EmuWindow_SDL2_GL();
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
private:
/// Fake hidden window for the core context
SDL_Window* dummy_window{};
/// Whether the GPU and driver supports the OpenGL extension required
bool SupportsRequiredGLExtensions();
using SDL_GLContext = void*;
/// The OpenGL context associated with the window
SDL_GLContext window_context;
/// The OpenGL context associated with the core
std::unique_ptr<Core::Frontend::GraphicsContext> core_context;
};

View File

@@ -0,0 +1,75 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstdlib>
#include <memory>
#include <string>
#include <fmt/format.h>
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/scm_rev.h"
#include "core/settings.h"
#include "video_core/renderer_vulkan/renderer_vulkan.h"
#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h"
// Include these late to avoid polluting everything with Xlib macros
#include <SDL.h>
#include <SDL_syswm.h>
EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem)
: EmuWindow_SDL2{input_subsystem} {
const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name,
Common::g_scm_branch, Common::g_scm_desc);
render_window =
SDL_CreateWindow(window_title.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height,
SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
SDL_SysWMinfo wm;
SDL_VERSION(&wm.version);
if (SDL_GetWindowWMInfo(render_window, &wm) == SDL_FALSE) {
LOG_CRITICAL(Frontend, "Failed to get information from the window manager");
std::exit(EXIT_FAILURE);
}
switch (wm.subsystem) {
#ifdef SDL_VIDEO_DRIVER_WINDOWS
case SDL_SYSWM_TYPE::SDL_SYSWM_WINDOWS:
window_info.type = Core::Frontend::WindowSystemType::Windows;
window_info.render_surface = reinterpret_cast<void*>(wm.info.win.window);
break;
#endif
#ifdef SDL_VIDEO_DRIVER_X11
case SDL_SYSWM_TYPE::SDL_SYSWM_X11:
window_info.type = Core::Frontend::WindowSystemType::X11;
window_info.display_connection = wm.info.x11.display;
window_info.render_surface = reinterpret_cast<void*>(wm.info.x11.window);
break;
#endif
#ifdef SDL_VIDEO_DRIVER_WAYLAND
case SDL_SYSWM_TYPE::SDL_SYSWM_WAYLAND:
window_info.type = Core::Frontend::WindowSystemType::Wayland;
window_info.display_connection = wm.info.wl.display;
window_info.render_surface = wm.info.wl.surface;
break;
#endif
default:
LOG_CRITICAL(Frontend, "Window manager subsystem not implemented");
std::exit(EXIT_FAILURE);
}
OnResize();
OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
SDL_PumpEvents();
LOG_INFO(Frontend, "yuzu Version: {} | {}-{} (Vulkan)", Common::g_build_name,
Common::g_scm_branch, Common::g_scm_desc);
}
EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() = default;
std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_VK::CreateSharedContext() const {
return std::make_unique<DummyContext>();
}

View File

@@ -0,0 +1,28 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include "core/frontend/emu_window.h"
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
namespace Core {
class System;
}
namespace InputCommon {
class InputSubsystem;
}
class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 {
public:
explicit EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem);
~EmuWindow_SDL2_VK() override;
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
};
class DummyContext : public Core::Frontend::GraphicsContext {};

16
src/yuzu_cmd/resource.h Executable file
View File

@@ -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

230
src/yuzu_cmd/yuzu.cpp Executable file
View File

@@ -0,0 +1,230 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <chrono>
#include <iostream>
#include <memory>
#include <string>
#include <thread>
#include <fmt/ostream.h>
#include "common/common_paths.h"
#include "common/detached_tasks.h"
#include "common/file_util.h"
#include "common/logging/backend.h"
#include "common/logging/filter.h"
#include "common/logging/log.h"
#include "common/microprofile.h"
#include "common/scm_rev.h"
#include "common/scope_exit.h"
#include "common/string_util.h"
#include "common/telemetry.h"
#include "core/core.h"
#include "core/crypto/key_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/vfs_real.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h"
#include "core/settings.h"
#include "core/telemetry_session.h"
#include "input_common/main.h"
#include "video_core/renderer_base.h"
#include "yuzu_cmd/config.h"
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
#include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h"
#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h"
#ifdef _WIN32
// windows.h needs to be included before shellapi.h
#include <windows.h>
#include <shellapi.h>
#endif
#undef _UNICODE
#include <getopt.h>
#ifndef _MSC_VER
#include <unistd.h>
#endif
#ifdef _WIN32
extern "C" {
// tells Nvidia and AMD drivers to use the dedicated GPU by default on laptops with switchable
// graphics
__declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001;
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
}
#endif
static void PrintHelp(const char* argv0) {
std::cout << "Usage: " << argv0
<< " [options] <filename>\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";
}
static void PrintVersion() {
std::cout << "yuzu " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl;
}
static void InitializeLogging() {
Log::Filter log_filter(Log::Level::Debug);
log_filter.ParseFilterString(Settings::values.log_filter);
Log::SetGlobalFilter(log_filter);
Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
const std::string& log_dir = Common::FS::GetUserPath(Common::FS::UserPath::LogDir);
Common::FS::CreateFullPath(log_dir);
Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE));
#ifdef _WIN32
Log::AddBackend(std::make_unique<Log::DebuggerBackend>());
#endif
}
/// Application entry point
int main(int argc, char** argv) {
Common::DetachedTasks detached_tasks;
Config config;
int option_index = 0;
InitializeLogging();
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;
bool fullscreen = false;
static struct option long_options[] = {
{"fullscreen", no_argument, 0, 'f'},
{"help", no_argument, 0, 'h'},
{"version", no_argument, 0, 'v'},
{"program", optional_argument, 0, 'p'},
{0, 0, 0, 0},
};
while (optind < argc) {
int arg = getopt_long(argc, argv, "g:fhvp::", long_options, &option_index);
if (arg != -1) {
switch (static_cast<char>(arg)) {
case 'f':
fullscreen = true;
LOG_INFO(Frontend, "Starting in fullscreen mode...");
break;
case 'h':
PrintHelp(argv[0]);
return 0;
case 'v':
PrintVersion();
return 0;
case 'p':
Settings::values.program_args = argv[optind];
++optind;
break;
}
} else {
#ifdef _WIN32
filepath = Common::UTF16ToUTF8(argv_w[optind]);
#else
filepath = argv[optind];
#endif
optind++;
}
}
#ifdef _WIN32
LocalFree(argv_w);
#endif
MicroProfileOnThreadCreate("EmuThread");
SCOPE_EXIT({ MicroProfileShutdown(); });
if (filepath.empty()) {
LOG_CRITICAL(Frontend, "Failed to load ROM: No ROM specified");
return -1;
}
auto& system{Core::System::GetInstance()};
InputCommon::InputSubsystem input_subsystem;
// Apply the command line arguments
Settings::Apply(system);
std::unique_ptr<EmuWindow_SDL2> emu_window;
switch (Settings::values.renderer_backend.GetValue()) {
case Settings::RendererBackend::OpenGL:
emu_window = std::make_unique<EmuWindow_SDL2_GL>(&input_subsystem, fullscreen);
break;
case Settings::RendererBackend::Vulkan:
emu_window = std::make_unique<EmuWindow_SDL2_VK>(&input_subsystem);
break;
}
system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
system.GetFileSystemController().CreateFactories(*system.GetFilesystem());
const Core::System::ResultStatus load_result{system.Load(*emu_window, filepath)};
switch (load_result) {
case Core::System::ResultStatus::ErrorGetLoader:
LOG_CRITICAL(Frontend, "Failed to obtain loader for {}!", filepath);
return -1;
case Core::System::ResultStatus::ErrorLoader:
LOG_CRITICAL(Frontend, "Failed to load ROM!");
return -1;
case Core::System::ResultStatus::ErrorNotInitialized:
LOG_CRITICAL(Frontend, "CPUCore not initialized");
return -1;
case Core::System::ResultStatus::ErrorVideoCore:
LOG_CRITICAL(Frontend, "Failed to initialize VideoCore!");
return -1;
case Core::System::ResultStatus::Success:
break; // Expected case
default:
if (static_cast<u32>(load_result) >
static_cast<u32>(Core::System::ResultStatus::ErrorLoader)) {
const u16 loader_id = static_cast<u16>(Core::System::ResultStatus::ErrorLoader);
const u16 error_id = static_cast<u16>(load_result) - loader_id;
LOG_CRITICAL(Frontend,
"While attempting to load the ROM requested, an error occured. Please "
"refer to the yuzu wiki for more information or the yuzu discord for "
"additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}",
loader_id, error_id, static_cast<Loader::ResultStatus>(error_id));
}
}
system.TelemetrySession().AddField(Common::Telemetry::FieldType::App, "Frontend", "SDL");
// Core is loaded, start the GPU (makes the GPU contexts current to this thread)
system.GPU().Start();
system.Renderer().Rasterizer().LoadDiskResources(
system.CurrentProcess()->GetTitleID(), false,
[](VideoCore::LoadCallbackStage, size_t value, size_t total) {});
void(system.Run());
while (emu_window->IsOpen()) {
emu_window->WaitEvent();
}
void(system.Pause());
system.Shutdown();
detached_tasks.WaitForAllTasks();
return 0;
}

17
src/yuzu_cmd/yuzu.rc Executable file
View File

@@ -0,0 +1,17 @@
#include "winresrc.h"
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
YUZU_ICON ICON "../../dist/yuzu.ico"
/////////////////////////////////////////////////////////////////////////////
//
// RT_MANIFEST
//
0 RT_MANIFEST "../../dist/yuzu.manifest"