diff options
Diffstat (limited to 'src')
106 files changed, 2707 insertions, 1051 deletions
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index c4bad8cb0..869da5e83 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt @@ -2,13 +2,16 @@ set(SRCS audio_core.cpp codec.cpp hle/dsp.cpp + hle/filter.cpp hle/pipe.cpp ) set(HEADERS audio_core.h codec.h + hle/common.h hle/dsp.h + hle/filter.h hle/pipe.h sink.h ) diff --git a/src/audio_core/hle/common.h b/src/audio_core/hle/common.h new file mode 100644 index 000000000..37d441eb2 --- /dev/null +++ b/src/audio_core/hle/common.h @@ -0,0 +1,35 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <algorithm> +#include <array> + +#include "audio_core/audio_core.h" + +#include "common/common_types.h" + +namespace DSP { +namespace HLE { + +/// The final output to the speakers is stereo. Preprocessing output in Source is also stereo. +using StereoFrame16 = std::array<std::array<s16, 2>, AudioCore::samples_per_frame>; + +/// The DSP is quadraphonic internally. +using QuadFrame32 = std::array<std::array<s32, 4>, AudioCore::samples_per_frame>; + +/** + * This performs the filter operation defined by FilterT::ProcessSample on the frame in-place. + * FilterT::ProcessSample is called sequentially on the samples. + */ +template<typename FrameT, typename FilterT> +void FilterFrame(FrameT& frame, FilterT& filter) { + std::transform(frame.begin(), frame.end(), frame.begin(), [&filter](const typename FrameT::value_type& sample) { + return filter.ProcessSample(sample); + }); +} + +} // namespace HLE +} // namespace DSP diff --git a/src/audio_core/hle/dsp.h b/src/audio_core/hle/dsp.h index 376436c29..c15ef0b7a 100644 --- a/src/audio_core/hle/dsp.h +++ b/src/audio_core/hle/dsp.h @@ -126,8 +126,11 @@ struct SourceConfiguration { union { u32_le dirty_raw; + BitField<0, 1, u32_le> format_dirty; + BitField<1, 1, u32_le> mono_or_stereo_dirty; BitField<2, 1, u32_le> adpcm_coefficients_dirty; BitField<3, 1, u32_le> partial_embedded_buffer_dirty; ///< Tends to be set when a looped buffer is queued. + BitField<4, 1, u32_le> partial_reset_flag; BitField<16, 1, u32_le> enable_dirty; BitField<17, 1, u32_le> interpolation_dirty; @@ -143,8 +146,7 @@ struct SourceConfiguration { BitField<27, 1, u32_le> gain_2_dirty; BitField<28, 1, u32_le> sync_dirty; BitField<29, 1, u32_le> reset_flag; - - BitField<31, 1, u32_le> embedded_buffer_dirty; + BitField<30, 1, u32_le> embedded_buffer_dirty; }; // Gain control @@ -175,7 +177,8 @@ struct SourceConfiguration { /** * This is the simplest normalized first-order digital recursive filter. * The transfer function of this filter is: - * H(z) = b0 / (1 + a1 z^-1) + * H(z) = b0 / (1 - a1 z^-1) + * Note the feedbackward coefficient is negated. * Values are signed fixed point with 15 fractional bits. */ struct SimpleFilter { @@ -192,11 +195,11 @@ struct SourceConfiguration { * Values are signed fixed point with 14 fractional bits. */ struct BiquadFilter { - s16_le b0; - s16_le b1; - s16_le b2; - s16_le a1; s16_le a2; + s16_le a1; + s16_le b2; + s16_le b1; + s16_le b0; }; union { diff --git a/src/audio_core/hle/filter.cpp b/src/audio_core/hle/filter.cpp new file mode 100644 index 000000000..2c65ef026 --- /dev/null +++ b/src/audio_core/hle/filter.cpp @@ -0,0 +1,115 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <array> +#include <cstddef> + +#include "audio_core/hle/common.h" +#include "audio_core/hle/dsp.h" +#include "audio_core/hle/filter.h" + +#include "common/common_types.h" +#include "common/math_util.h" + +namespace DSP { +namespace HLE { + +void SourceFilters::Reset() { + Enable(false, false); +} + +void SourceFilters::Enable(bool simple, bool biquad) { + simple_filter_enabled = simple; + biquad_filter_enabled = biquad; + + if (!simple) + simple_filter.Reset(); + if (!biquad) + biquad_filter.Reset(); +} + +void SourceFilters::Configure(SourceConfiguration::Configuration::SimpleFilter config) { + simple_filter.Configure(config); +} + +void SourceFilters::Configure(SourceConfiguration::Configuration::BiquadFilter config) { + biquad_filter.Configure(config); +} + +void SourceFilters::ProcessFrame(StereoFrame16& frame) { + if (!simple_filter_enabled && !biquad_filter_enabled) + return; + + if (simple_filter_enabled) { + FilterFrame(frame, simple_filter); + } + + if (biquad_filter_enabled) { + FilterFrame(frame, biquad_filter); + } +} + +// SimpleFilter + +void SourceFilters::SimpleFilter::Reset() { + y1.fill(0); + // Configure as passthrough. + a1 = 0; + b0 = 1 << 15; +} + +void SourceFilters::SimpleFilter::Configure(SourceConfiguration::Configuration::SimpleFilter config) { + a1 = config.a1; + b0 = config.b0; +} + +std::array<s16, 2> SourceFilters::SimpleFilter::ProcessSample(const std::array<s16, 2>& x0) { + std::array<s16, 2> y0; + for (size_t i = 0; i < 2; i++) { + const s32 tmp = (b0 * x0[i] + a1 * y1[i]) >> 15; + y0[i] = MathUtil::Clamp(tmp, -32768, 32767); + } + + y1 = y0; + + return y0; +} + +// BiquadFilter + +void SourceFilters::BiquadFilter::Reset() { + x1.fill(0); + x2.fill(0); + y1.fill(0); + y2.fill(0); + // Configure as passthrough. + a1 = a2 = b1 = b2 = 0; + b0 = 1 << 14; +} + +void SourceFilters::BiquadFilter::Configure(SourceConfiguration::Configuration::BiquadFilter config) { + a1 = config.a1; + a2 = config.a2; + b0 = config.b0; + b1 = config.b1; + b2 = config.b2; +} + +std::array<s16, 2> SourceFilters::BiquadFilter::ProcessSample(const std::array<s16, 2>& x0) { + std::array<s16, 2> y0; + for (size_t i = 0; i < 2; i++) { + const s32 tmp = (b0 * x0[i] + b1 * x1[i] + b2 * x2[i] + a1 * y1[i] + a2 * y2[i]) >> 14; + y0[i] = MathUtil::Clamp(tmp, -32768, 32767); + } + + x2 = x1; + x1 = x0; + y2 = y1; + y1 = y0; + + return y0; +} + +} // namespace HLE +} // namespace DSP diff --git a/src/audio_core/hle/filter.h b/src/audio_core/hle/filter.h new file mode 100644 index 000000000..75738f600 --- /dev/null +++ b/src/audio_core/hle/filter.h @@ -0,0 +1,112 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> + +#include "audio_core/hle/common.h" +#include "audio_core/hle/dsp.h" + +#include "common/common_types.h" + +namespace DSP { +namespace HLE { + +/// Preprocessing filters. There is an independent set of filters for each Source. +class SourceFilters final { + SourceFilters() { Reset(); } + + /// Reset internal state. + void Reset(); + + /** + * Enable/Disable filters + * See also: SourceConfiguration::Configuration::simple_filter_enabled, + * SourceConfiguration::Configuration::biquad_filter_enabled. + * @param simple If true, enables the simple filter. If false, disables it. + * @param simple If true, enables the biquad filter. If false, disables it. + */ + void Enable(bool simple, bool biquad); + + /** + * Configure simple filter. + * @param config Configuration from DSP shared memory. + */ + void Configure(SourceConfiguration::Configuration::SimpleFilter config); + + /** + * Configure biquad filter. + * @param config Configuration from DSP shared memory. + */ + void Configure(SourceConfiguration::Configuration::BiquadFilter config); + + /** + * Processes a frame in-place. + * @param frame Audio samples to process. Modified in-place. + */ + void ProcessFrame(StereoFrame16& frame); + +private: + bool simple_filter_enabled; + bool biquad_filter_enabled; + + struct SimpleFilter { + SimpleFilter() { Reset(); } + + /// Resets internal state. + void Reset(); + + /** + * Configures this filter with application settings. + * @param config Configuration from DSP shared memory. + */ + void Configure(SourceConfiguration::Configuration::SimpleFilter config); + + /** + * Processes a single stereo PCM16 sample. + * @param x0 Input sample + * @return Output sample + */ + std::array<s16, 2> ProcessSample(const std::array<s16, 2>& x0); + + private: + // Configuration + s32 a1, b0; + // Internal state + std::array<s16, 2> y1; + } simple_filter; + + struct BiquadFilter { + BiquadFilter() { Reset(); } + + /// Resets internal state. + void Reset(); + + /** + * Configures this filter with application settings. + * @param config Configuration from DSP shared memory. + */ + void Configure(SourceConfiguration::Configuration::BiquadFilter config); + + /** + * Processes a single stereo PCM16 sample. + * @param x0 Input sample + * @return Output sample + */ + std::array<s16, 2> ProcessSample(const std::array<s16, 2>& x0); + + private: + // Configuration + s32 a1, a2, b0, b1, b2; + // Internal state + std::array<s16, 2> x1; + std::array<s16, 2> x2; + std::array<s16, 2> y1; + std::array<s16, 2> y2; + } biquad_filter; +}; + +} // namespace HLE +} // namespace DSP diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp index 40e40f192..d6ad13f69 100644 --- a/src/citra/citra.cpp +++ b/src/citra/citra.cpp @@ -5,6 +5,7 @@ #include <string> #include <thread> #include <iostream> +#include <memory> // This needs to be included before getopt.h because the latter #defines symbols used by it #include "common/microprofile.h" @@ -19,7 +20,6 @@ #include "common/logging/log.h" #include "common/logging/backend.h" #include "common/logging/filter.h" -#include "common/make_unique.h" #include "common/scope_exit.h" #include "core/settings.h" @@ -36,25 +36,43 @@ static void PrintHelp() { - std::cout << "Usage: citra <filename>" << std::endl; + std::cout << "Usage: citra [options] <filename>" << std::endl; + std::cout << "--help, -h Display this information" << std::endl; + std::cout << "--gdbport, -g number Enable gdb stub on port number" << 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; std::string boot_filename; + static struct option long_options[] = { { "help", no_argument, 0, 'h' }, + { "gdbport", required_argument, 0, 'g' }, { 0, 0, 0, 0 } }; while (optind < argc) { - char arg = getopt_long(argc, argv, ":h", long_options, &option_index); + char arg = getopt_long(argc, argv, ":hg:", long_options, &option_index); if (arg != -1) { switch (arg) { case 'h': PrintHelp(); return 0; + 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; } } else { boot_filename = argv[optind]; @@ -73,16 +91,14 @@ int main(int argc, char **argv) { return -1; } - Config config; log_filter.ParseFilterString(Settings::values.log_filter); - GDBStub::ToggleServer(Settings::values.use_gdbstub); - GDBStub::SetServerPort(static_cast<u32>(Settings::values.gdbstub_port)); - - std::unique_ptr<EmuWindow_SDL2> emu_window = Common::make_unique<EmuWindow_SDL2>(); + // Apply the command line arguments + Settings::values.gdbstub_port = gdb_port; + Settings::values.use_gdbstub = use_gdbstub; + Settings::Apply(); - VideoCore::g_hw_renderer_enabled = Settings::values.use_hw_renderer; - VideoCore::g_shader_jit_enabled = Settings::values.use_shader_jit; + std::unique_ptr<EmuWindow_SDL2> emu_window = std::make_unique<EmuWindow_SDL2>(); System::Init(emu_window.get()); SCOPE_EXIT({ System::Shutdown(); }); diff --git a/src/citra/config.cpp b/src/citra/config.cpp index 9034b188e..6b6617352 100644 --- a/src/citra/config.cpp +++ b/src/citra/config.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <memory> + #include <inih/cpp/INIReader.h> #include <SDL.h> @@ -10,7 +12,6 @@ #include "common/file_util.h" #include "common/logging/log.h" -#include "common/make_unique.h" #include "core/settings.h" @@ -19,7 +20,7 @@ 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 = Common::make_unique<INIReader>(sdl2_config_loc); + sdl2_config = std::make_unique<INIReader>(sdl2_config_loc); Reload(); } @@ -31,7 +32,7 @@ bool Config::LoadINI(const std::string& default_contents, bool retry) { LOG_WARNING(Config, "Failed to load %s. Creating file from defaults...", location); FileUtil::CreateFullPath(location); FileUtil::WriteStringToFile(true, default_contents, location); - sdl2_config = Common::make_unique<INIReader>(location); // Reopen file + sdl2_config = std::make_unique<INIReader>(location); // Reopen file return LoadINI(default_contents, false); } diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index 9b3eb2cd6..6660d9879 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt @@ -17,12 +17,16 @@ set(SRCS debugger/profiler.cpp debugger/ramview.cpp debugger/registers.cpp - game_list.cpp util/spinbox.cpp util/util.cpp bootmanager.cpp + configure_debug.cpp + configure_dialog.cpp + configure_general.cpp + game_list.cpp hotkeys.cpp main.cpp + ui_settings.cpp citra-qt.rc Info.plist ) @@ -44,12 +48,16 @@ set(HEADERS debugger/profiler.h debugger/ramview.h debugger/registers.h - game_list.h util/spinbox.h util/util.h bootmanager.h + configure_debug.h + configure_dialog.h + configure_general.h + game_list.h hotkeys.h main.h + ui_settings.h version.h ) @@ -59,6 +67,9 @@ set(UIS debugger/disassembler.ui debugger/profiler.ui debugger/registers.ui + configure.ui + configure_debug.ui + configure_general.ui hotkeys.ui main.ui ) diff --git a/src/citra_qt/config.cpp b/src/citra_qt/config.cpp index d1a19ade9..e363be38a 100644 --- a/src/citra_qt/config.cpp +++ b/src/citra_qt/config.cpp @@ -7,16 +7,16 @@ #include <QStringList> #include "citra_qt/config.h" +#include "citra_qt/ui_settings.h" #include "common/file_util.h" #include "core/settings.h" Config::Config() { - // TODO: Don't hardcode the path; let the frontend decide where to put the config files. qt_config_loc = FileUtil::GetUserPath(D_CONFIG_IDX) + "qt-config.ini"; FileUtil::CreateFullPath(qt_config_loc); - qt_config = new QSettings(QString::fromLocal8Bit(qt_config_loc.c_str()), QSettings::IniFormat); + qt_config = new QSettings(QString::fromStdString(qt_config_loc), QSettings::IniFormat); Reload(); } @@ -67,6 +67,51 @@ void Config::ReadValues() { Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool(); Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt(); qt_config->endGroup(); + + qt_config->beginGroup("UI"); + + qt_config->beginGroup("UILayout"); + UISettings::values.geometry = qt_config->value("geometry").toByteArray(); + UISettings::values.state = qt_config->value("state").toByteArray(); + UISettings::values.renderwindow_geometry = qt_config->value("geometryRenderWindow").toByteArray(); + UISettings::values.gamelist_header_state = qt_config->value("gameListHeaderState").toByteArray(); + UISettings::values.microprofile_geometry = qt_config->value("microProfileDialogGeometry").toByteArray(); + UISettings::values.microprofile_visible = qt_config->value("microProfileDialogVisible", false).toBool(); + qt_config->endGroup(); + + qt_config->beginGroup("Paths"); + UISettings::values.roms_path = qt_config->value("romsPath").toString(); + UISettings::values.symbols_path = qt_config->value("symbolsPath").toString(); + UISettings::values.gamedir = qt_config->value("gameListRootDir", ".").toString(); + UISettings::values.gamedir_deepscan = qt_config->value("gameListDeepScan", false).toBool(); + UISettings::values.recent_files = qt_config->value("recentFiles").toStringList(); + qt_config->endGroup(); + + qt_config->beginGroup("Shortcuts"); + QStringList groups = qt_config->childGroups(); + for (auto group : groups) { + qt_config->beginGroup(group); + + QStringList hotkeys = qt_config->childGroups(); + for (auto hotkey : hotkeys) { + qt_config->beginGroup(hotkey); + UISettings::values.shortcuts.emplace_back( + UISettings::Shortcut(group + "/" + hotkey, + UISettings::ContextualShortcut(qt_config->value("KeySeq").toString(), + qt_config->value("Context").toInt()))); + qt_config->endGroup(); + } + + qt_config->endGroup(); + } + qt_config->endGroup(); + + UISettings::values.single_window_mode = qt_config->value("singleWindowMode", true).toBool(); + UISettings::values.display_titlebar = qt_config->value("displayTitleBars", true).toBool(); + UISettings::values.confirm_before_closing = qt_config->value("confirmClose",true).toBool(); + UISettings::values.first_start = qt_config->value("firstStart", true).toBool(); + + qt_config->endGroup(); } void Config::SaveValues() { @@ -107,10 +152,44 @@ void Config::SaveValues() { qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub); qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port); qt_config->endGroup(); + + qt_config->beginGroup("UI"); + + qt_config->beginGroup("UILayout"); + qt_config->setValue("geometry", UISettings::values.geometry); + qt_config->setValue("state", UISettings::values.state); + qt_config->setValue("geometryRenderWindow", UISettings::values.renderwindow_geometry); + qt_config->setValue("gameListHeaderState", UISettings::values.gamelist_header_state); + qt_config->setValue("microProfileDialogGeometry", UISettings::values.microprofile_geometry); + qt_config->setValue("microProfileDialogVisible", UISettings::values.microprofile_visible); + qt_config->endGroup(); + + qt_config->beginGroup("Paths"); + qt_config->setValue("romsPath", UISettings::values.roms_path); + qt_config->setValue("symbolsPath", UISettings::values.symbols_path); + qt_config->setValue("gameListRootDir", UISettings::values.gamedir); + qt_config->setValue("gameListDeepScan", UISettings::values.gamedir_deepscan); + qt_config->setValue("recentFiles", UISettings::values.recent_files); + qt_config->endGroup(); + + qt_config->beginGroup("Shortcuts"); + for (auto shortcut : UISettings::values.shortcuts ) { + qt_config->setValue(shortcut.first + "/KeySeq", shortcut.second.first); + qt_config->setValue(shortcut.first + "/Context", shortcut.second.second); + } + qt_config->endGroup(); + + qt_config->setValue("singleWindowMode", UISettings::values.single_window_mode); + qt_config->setValue("displayTitleBars", UISettings::values.display_titlebar); + qt_config->setValue("confirmClose", UISettings::values.confirm_before_closing); + qt_config->setValue("firstStart", UISettings::values.first_start); + + qt_config->endGroup(); } void Config::Reload() { ReadValues(); + Settings::Apply(); } void Config::Save() { diff --git a/src/citra_qt/configure.ui b/src/citra_qt/configure.ui new file mode 100644 index 000000000..6ae056ff9 --- /dev/null +++ b/src/citra_qt/configure.ui @@ -0,0 +1,97 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ConfigureDialog</class> + <widget class="QDialog" name="ConfigureDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>441</width> + <height>501</height> + </rect> + </property> + <property name="windowTitle"> + <string>Citra Configuration</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QTabWidget" name="tabWidget"> + <property name="currentIndex"> + <number>0</number> + </property> + <widget class="ConfigureGeneral" name="generalTab"> + <attribute name="title"> + <string>General</string> + </attribute> + </widget> + <widget class="QWidget" name="inputTab"> + <attribute name="title"> + <string>Input</string> + </attribute> + </widget> + <widget class="ConfigureDebug" name="debugTab"> + <attribute name="title"> + <string>Debug</string> + </attribute> + </widget> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>ConfigureGeneral</class> + <extends>QWidget</extends> + <header>configure_general.h</header> + <container>1</container> + </customwidget> + <customwidget> + <class>ConfigureDebug</class> + <extends>QWidget</extends> + <header>configure_debug.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>ConfigureDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>220</x> + <y>380</y> + </hint> + <hint type="destinationlabel"> + <x>220</x> + <y>200</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>ConfigureDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>220</x> + <y>380</y> + </hint> + <hint type="destinationlabel"> + <x>220</x> + <y>200</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/citra_qt/configure_debug.cpp b/src/citra_qt/configure_debug.cpp new file mode 100644 index 000000000..dc3d7b906 --- /dev/null +++ b/src/citra_qt/configure_debug.cpp @@ -0,0 +1,31 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "citra_qt/configure_debug.h" +#include "ui_configure_debug.h" + +#include "core/settings.h" + +ConfigureDebug::ConfigureDebug(QWidget *parent) : + QWidget(parent), + ui(new Ui::ConfigureDebug) +{ + ui->setupUi(this); + this->setConfiguration(); +} + +ConfigureDebug::~ConfigureDebug() { +} + +void ConfigureDebug::setConfiguration() { + ui->toogle_gdbstub->setChecked(Settings::values.use_gdbstub); + ui->gdbport_spinbox->setEnabled(Settings::values.use_gdbstub); + ui->gdbport_spinbox->setValue(Settings::values.gdbstub_port); +} + +void ConfigureDebug::applyConfiguration() { + Settings::values.use_gdbstub = ui->toogle_gdbstub->isChecked(); + Settings::values.gdbstub_port = ui->gdbport_spinbox->value(); + Settings::Apply(); +} diff --git a/src/citra_qt/configure_debug.h b/src/citra_qt/configure_debug.h new file mode 100644 index 000000000..ab58ebbdc --- /dev/null +++ b/src/citra_qt/configure_debug.h @@ -0,0 +1,29 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <QWidget> + +namespace Ui { +class ConfigureDebug; +} + +class ConfigureDebug : public QWidget +{ + Q_OBJECT + +public: + explicit ConfigureDebug(QWidget *parent = nullptr); + ~ConfigureDebug(); + + void applyConfiguration(); + +private: + void setConfiguration(); + +private: + std::unique_ptr<Ui::ConfigureDebug> ui; +}; diff --git a/src/citra_qt/configure_debug.ui b/src/citra_qt/configure_debug.ui new file mode 100644 index 000000000..3ba7f44da --- /dev/null +++ b/src/citra_qt/configure_debug.ui @@ -0,0 +1,102 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ConfigureDebug</class> + <widget class="QWidget" name="ConfigureDebug"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string>GDB</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <widget class="QCheckBox" name="toogle_gdbstub"> + <property name="text"> + <string>Enable GDB Stub</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Port:</string> + </property> + </widget> + </item> + <item> + <widget class="QSpinBox" name="gdbport_spinbox"> + <property name="maximum"> + <number>65536</number> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + </layout> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>toogle_gdbstub</sender> + <signal>toggled(bool)</signal> + <receiver>gdbport_spinbox</receiver> + <slot>setEnabled(bool)</slot> + <hints> + <hint type="sourcelabel"> + <x>84</x> + <y>157</y> + </hint> + <hint type="destinationlabel"> + <x>342</x> + <y>158</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/citra_qt/configure_dialog.cpp b/src/citra_qt/configure_dialog.cpp new file mode 100644 index 000000000..87c26c715 --- /dev/null +++ b/src/citra_qt/configure_dialog.cpp @@ -0,0 +1,29 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "citra_qt/config.h" +#include "citra_qt/configure_dialog.h" +#include "ui_configure.h" + + +#include "core/settings.h" + +ConfigureDialog::ConfigureDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::ConfigureDialog) +{ + ui->setupUi(this); + this->setConfiguration(); +} + +ConfigureDialog::~ConfigureDialog() { +} + +void ConfigureDialog::setConfiguration() { +} + +void ConfigureDialog::applyConfiguration() { + ui->generalTab->applyConfiguration(); + ui->debugTab->applyConfiguration(); +} diff --git a/src/citra_qt/configure_dialog.h b/src/citra_qt/configure_dialog.h new file mode 100644 index 000000000..89020eeb4 --- /dev/null +++ b/src/citra_qt/configure_dialog.h @@ -0,0 +1,29 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <QDialog> + +namespace Ui { +class ConfigureDialog; +} + +class ConfigureDialog : public QDialog +{ + Q_OBJECT + +public: + explicit ConfigureDialog(QWidget *parent = nullptr); + ~ConfigureDialog(); + + void applyConfiguration(); + +private: + void setConfiguration(); + +private: + std::unique_ptr<Ui::ConfigureDialog> ui; +}; diff --git a/src/citra_qt/configure_general.cpp b/src/citra_qt/configure_general.cpp new file mode 100644 index 000000000..a27d0d26c --- /dev/null +++ b/src/citra_qt/configure_general.cpp @@ -0,0 +1,37 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "citra_qt/configure_general.h" +#include "citra_qt/ui_settings.h" +#include "ui_configure_general.h" + +#include "core/settings.h" + +ConfigureGeneral::ConfigureGeneral(QWidget *parent) : + QWidget(parent), + ui(new Ui::ConfigureGeneral) +{ + ui->setupUi(this); + this->setConfiguration(); +} + +ConfigureGeneral::~ConfigureGeneral() { +} + +void ConfigureGeneral::setConfiguration() { + ui->toogle_deepscan->setChecked(UISettings::values.gamedir_deepscan); + ui->toogle_check_exit->setChecked(UISettings::values.confirm_before_closing); + ui->region_combobox->setCurrentIndex(Settings::values.region_value); + ui->toogle_hw_renderer->setChecked(Settings::values.use_hw_renderer); + ui->toogle_shader_jit->setChecked(Settings::values.use_shader_jit); +} + +void ConfigureGeneral::applyConfiguration() { + UISettings::values.gamedir_deepscan = ui->toogle_deepscan->isChecked(); + UISettings::values.confirm_before_closing = ui->toogle_check_exit->isChecked(); + Settings::values.region_value = ui->region_combobox->currentIndex(); + Settings::values.use_hw_renderer = ui->toogle_hw_renderer->isChecked(); + Settings::values.use_shader_jit = ui->toogle_shader_jit->isChecked(); + Settings::Apply(); +} diff --git a/src/citra_qt/configure_general.h b/src/citra_qt/configure_general.h new file mode 100644 index 000000000..a6c68e62d --- /dev/null +++ b/src/citra_qt/configure_general.h @@ -0,0 +1,29 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <QWidget> + +namespace Ui { +class ConfigureGeneral; +} + +class ConfigureGeneral : public QWidget +{ + Q_OBJECT + +public: + explicit ConfigureGeneral(QWidget *parent = nullptr); + ~ConfigureGeneral(); + + void applyConfiguration(); + +private: + void setConfiguration(); + +private: + std::unique_ptr<Ui::ConfigureGeneral> ui; +}; diff --git a/src/citra_qt/configure_general.ui b/src/citra_qt/configure_general.ui new file mode 100644 index 000000000..47184c5c6 --- /dev/null +++ b/src/citra_qt/configure_general.ui @@ -0,0 +1,166 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ConfigureGeneral</class> + <widget class="QWidget" name="ConfigureGeneral"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>300</width> + <height>377</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string>General</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QCheckBox" name="toogle_deepscan"> + <property name="text"> + <string>Recursive scan for game folder</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="toogle_check_exit"> + <property name="text"> + <string>Confirm exit while emulation is running</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_4"> + <property name="title"> + <string>Emulation</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_5"> + <item> + <layout class="QVBoxLayout" name="verticalLayout_6"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_6"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Region:</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="region_combobox"> + <item> + <property name="text"> + <string notr="true">JPN</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">USA</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">EUR</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">AUS</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">CHN</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">KOR</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">TWN</string> + </property> + </item> + </widget> + </item> + </layout> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_2"> + <property name="title"> + <string>Performance</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QCheckBox" name="toogle_hw_renderer"> + <property name="text"> + <string>Enable hardware renderer</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="toogle_shader_jit"> + <property name="text"> + <string>Enable shader JIT</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_3"> + <property name="title"> + <string>Hotkeys</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_4"> + <item> + <layout class="QVBoxLayout" name="verticalLayout_4"> + <item> + <widget class="GHotkeysDialog" name="widget" native="true"/> + </item> + </layout> + </item> + </layout> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>GHotkeysDialog</class> + <extends>QWidget</extends> + <header>hotkeys.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> diff --git a/src/citra_qt/game_list.cpp b/src/citra_qt/game_list.cpp index a0b216b0a..d14532102 100644 --- a/src/citra_qt/game_list.cpp +++ b/src/citra_qt/game_list.cpp @@ -8,6 +8,7 @@ #include "game_list.h" #include "game_list_p.h" +#include "ui_settings.h" #include "core/loader/loader.h" @@ -66,7 +67,7 @@ void GameList::ValidateEntry(const QModelIndex& item) if (file_path.isEmpty()) return; - std::string std_file_path(file_path.toLocal8Bit()); + std::string std_file_path(file_path.toStdString()); if (!FileUtil::Exists(std_file_path) || FileUtil::IsDirectory(std_file_path)) return; emit GameChosen(file_path); @@ -100,19 +101,15 @@ void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) current_worker = std::move(worker); } -void GameList::SaveInterfaceLayout(QSettings& settings) +void GameList::SaveInterfaceLayout() { - settings.beginGroup("UILayout"); - settings.setValue("gameListHeaderState", tree_view->header()->saveState()); - settings.endGroup(); + UISettings::values.gamelist_header_state = tree_view->header()->saveState(); } -void GameList::LoadInterfaceLayout(QSettings& settings) +void GameList::LoadInterfaceLayout() { auto header = tree_view->header(); - settings.beginGroup("UILayout"); - header->restoreState(settings.value("gameListHeaderState").toByteArray()); - settings.endGroup(); + header->restoreState(UISettings::values.gamelist_header_state); item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder()); } @@ -148,7 +145,7 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, bool d emit EntryReady({ new GameListItem(QString::fromStdString(Loader::GetFileTypeString(filetype))), - new GameListItemPath(QString::fromLocal8Bit(physical_name.c_str())), + new GameListItemPath(QString::fromStdString(physical_name)), new GameListItemSize(FileUtil::GetSize(physical_name)), }); } diff --git a/src/citra_qt/game_list.h b/src/citra_qt/game_list.h index 0950d9622..48febdc60 100644 --- a/src/citra_qt/game_list.h +++ b/src/citra_qt/game_list.h @@ -31,8 +31,8 @@ public: void PopulateAsync(const QString& dir_path, bool deep_scan); - void SaveInterfaceLayout(QSettings& settings); - void LoadInterfaceLayout(QSettings& settings); + void SaveInterfaceLayout(); + void LoadInterfaceLayout(); public slots: void AddEntry(QList<QStandardItem*> entry_items); diff --git a/src/citra_qt/hotkeys.cpp b/src/citra_qt/hotkeys.cpp index ed6b12fc4..41f95c63d 100644 --- a/src/citra_qt/hotkeys.cpp +++ b/src/citra_qt/hotkeys.cpp @@ -4,11 +4,12 @@ #include <map> +#include <QtGlobal> #include <QKeySequence> -#include <QSettings> #include <QShortcut> #include "citra_qt/hotkeys.h" +#include "citra_qt/ui_settings.h" struct Hotkey { @@ -24,54 +25,39 @@ typedef std::map<QString, HotkeyMap> HotkeyGroupMap; HotkeyGroupMap hotkey_groups; -void SaveHotkeys(QSettings& settings) +void SaveHotkeys() { - settings.beginGroup("Shortcuts"); - + UISettings::values.shortcuts.clear(); for (auto group : hotkey_groups) { - settings.beginGroup(group.first); for (auto hotkey : group.second) { - settings.beginGroup(hotkey.first); - settings.setValue(QString("KeySeq"), hotkey.second.keyseq.toString()); - settings.setValue(QString("Context"), hotkey.second.context); - settings.endGroup(); + UISettings::values.shortcuts.emplace_back( + UISettings::Shortcut(group.first + "/" + hotkey.first, + UISettings::ContextualShortcut(hotkey.second.keyseq.toString(), + hotkey.second.context))); } - settings.endGroup(); } - settings.endGroup(); } -void LoadHotkeys(QSettings& settings) +void LoadHotkeys() { - settings.beginGroup("Shortcuts"); - // Make sure NOT to use a reference here because it would become invalid once we call beginGroup() - QStringList groups = settings.childGroups(); - for (auto group : groups) + for (auto shortcut : UISettings::values.shortcuts) { - settings.beginGroup(group); + QStringList cat = shortcut.first.split("/"); + Q_ASSERT(cat.size() >= 2); - QStringList hotkeys = settings.childGroups(); - for (auto hotkey : hotkeys) + // RegisterHotkey assigns default keybindings, so use old values as default parameters + Hotkey& hk = hotkey_groups[cat[0]][cat[1]]; + if (!shortcut.second.first.isEmpty()) { - settings.beginGroup(hotkey); - - // RegisterHotkey assigns default keybindings, so use old values as default parameters - Hotkey& hk = hotkey_groups[group][hotkey]; - hk.keyseq = QKeySequence::fromString(settings.value("KeySeq", hk.keyseq.toString()).toString()); - hk.context = (Qt::ShortcutContext)settings.value("Context", hk.context).toInt(); - if (hk.shortcut) - hk.shortcut->setKey(hk.keyseq); - - settings.endGroup(); + hk.keyseq = QKeySequence::fromString(shortcut.second.first); + hk.context = (Qt::ShortcutContext)shortcut.second.second; } - - settings.endGroup(); + if (hk.shortcut) + hk.shortcut->setKey(hk.keyseq); } - - settings.endGroup(); } void RegisterHotkey(const QString& group, const QString& action, const QKeySequence& default_keyseq, Qt::ShortcutContext default_context) @@ -94,7 +80,7 @@ QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widge } -GHotkeysDialog::GHotkeysDialog(QWidget* parent): QDialog(parent) +GHotkeysDialog::GHotkeysDialog(QWidget* parent): QWidget(parent) { ui.setupUi(this); diff --git a/src/citra_qt/hotkeys.h b/src/citra_qt/hotkeys.h index 2fe635882..38aa5f012 100644 --- a/src/citra_qt/hotkeys.h +++ b/src/citra_qt/hotkeys.h @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#pragma once + #include "ui_hotkeys.h" class QDialog; @@ -33,16 +35,16 @@ QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widge * * @note Each hotkey group will be stored a settings group; For each hotkey inside that group, a settings group will be created to store the key sequence and the hotkey context. */ -void SaveHotkeys(QSettings& settings); +void SaveHotkeys(); /** * Loads hotkeys from the settings file. * * @note Yet unregistered hotkeys which are present in the settings will automatically be registered. */ -void LoadHotkeys(QSettings& settings); +void LoadHotkeys(); -class GHotkeysDialog : public QDialog +class GHotkeysDialog : public QWidget { Q_OBJECT diff --git a/src/citra_qt/hotkeys.ui b/src/citra_qt/hotkeys.ui index 38a9a14d1..050fe064e 100644 --- a/src/citra_qt/hotkeys.ui +++ b/src/citra_qt/hotkeys.ui @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>hotkeys</class> - <widget class="QDialog" name="hotkeys"> + <widget class="QWidget" name="hotkeys"> <property name="geometry"> <rect> <x>0</x> @@ -39,51 +39,8 @@ </column> </widget> </item> - <item> - <widget class="QDialogButtonBox" name="buttonBox"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="standardButtons"> - <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Reset</set> - </property> - </widget> - </item> </layout> </widget> <resources/> - <connections> - <connection> - <sender>buttonBox</sender> - <signal>accepted()</signal> - <receiver>hotkeys</receiver> - <slot>accept()</slot> - <hints> - <hint type="sourcelabel"> - <x>248</x> - <y>254</y> - </hint> - <hint type="destinationlabel"> - <x>157</x> - <y>274</y> - </hint> - </hints> - </connection> - <connection> - <sender>buttonBox</sender> - <signal>rejected()</signal> - <receiver>hotkeys</receiver> - <slot>reject()</slot> - <hints> - <hint type="sourcelabel"> - <x>316</x> - <y>260</y> - </hint> - <hint type="destinationlabel"> - <x>286</x> - <y>274</y> - </hint> - </hints> - </connection> - </connections> + <connections/> </ui> diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 32cceaf7e..2ca1e51f6 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include <clocale> +#include <memory> #include <thread> #include <QDesktopWidget> @@ -13,9 +14,11 @@ #include "citra_qt/bootmanager.h" #include "citra_qt/config.h" +#include "citra_qt/configure_dialog.h" #include "citra_qt/game_list.h" #include "citra_qt/hotkeys.h" #include "citra_qt/main.h" +#include "citra_qt/ui_settings.h" // Debugger #include "citra_qt/debugger/callstack.h" @@ -30,7 +33,6 @@ #include "citra_qt/debugger/ramview.h" #include "citra_qt/debugger/registers.h" -#include "common/make_unique.h" #include "common/microprofile.h" #include "common/platform.h" #include "common/scm_rev.h" @@ -50,12 +52,10 @@ #include "video_core/video_core.h" -GMainWindow::GMainWindow() : emu_thread(nullptr) +GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { Pica::g_debug_context = Pica::DebugContext::Construct(); - Config config; - ui.setupUi(this); statusBar()->hide(); @@ -133,33 +133,18 @@ GMainWindow::GMainWindow() : emu_thread(nullptr) setGeometry(x, y, w, h); // Restore UI state - QSettings settings; - - settings.beginGroup("UILayout"); - restoreGeometry(settings.value("geometry").toByteArray()); - restoreState(settings.value("state").toByteArray()); - render_window->restoreGeometry(settings.value("geometryRenderWindow").toByteArray()); - microProfileDialog->restoreGeometry(settings.value("microProfileDialogGeometry").toByteArray()); - microProfileDialog->setVisible(settings.value("microProfileDialogVisible").toBool()); - settings.endGroup(); - - game_list->LoadInterfaceLayout(settings); - - ui.action_Use_Gdbstub->setChecked(Settings::values.use_gdbstub); - SetGdbstubEnabled(ui.action_Use_Gdbstub->isChecked()); - - GDBStub::SetServerPort(static_cast<u32>(Settings::values.gdbstub_port)); - - ui.action_Use_Hardware_Renderer->setChecked(Settings::values.use_hw_renderer); - SetHardwareRendererEnabled(ui.action_Use_Hardware_Renderer->isChecked()); + restoreGeometry(UISettings::values.geometry); + restoreState(UISettings::values.state); + render_window->restoreGeometry(UISettings::values.renderwindow_geometry); + microProfileDialog->restoreGeometry(UISettings::values.microprofile_geometry); + microProfileDialog->setVisible(UISettings::values.microprofile_visible); - ui.action_Use_Shader_JIT->setChecked(Settings::values.use_shader_jit); - SetShaderJITEnabled(ui.action_Use_Shader_JIT->isChecked()); + game_list->LoadInterfaceLayout(); - ui.action_Single_Window_Mode->setChecked(settings.value("singleWindowMode", true).toBool()); + ui.action_Single_Window_Mode->setChecked(UISettings::values.single_window_mode); ToggleWindowMode(); - ui.actionDisplay_widget_title_bars->setChecked(settings.value("displayTitleBars", true).toBool()); + ui.actionDisplay_widget_title_bars->setChecked(UISettings::values.display_titlebar); OnDisplayTitleBars(ui.actionDisplay_widget_title_bars->isChecked()); // Prepare actions for recent files @@ -172,21 +157,16 @@ GMainWindow::GMainWindow() : emu_thread(nullptr) } UpdateRecentFiles(); - confirm_before_closing = settings.value("confirmClose", true).toBool(); - // Setup connections - connect(game_list, SIGNAL(GameChosen(QString)), this, SLOT(OnGameListLoadFile(QString))); - connect(ui.action_Load_File, SIGNAL(triggered()), this, SLOT(OnMenuLoadFile())); + connect(game_list, SIGNAL(GameChosen(QString)), this, SLOT(OnGameListLoadFile(QString)), Qt::DirectConnection); + connect(ui.action_Configure, SIGNAL(triggered()), this, SLOT(OnConfigure())); + connect(ui.action_Load_File, SIGNAL(triggered()), this, SLOT(OnMenuLoadFile()),Qt::DirectConnection); connect(ui.action_Load_Symbol_Map, SIGNAL(triggered()), this, SLOT(OnMenuLoadSymbolMap())); connect(ui.action_Select_Game_List_Root, SIGNAL(triggered()), this, SLOT(OnMenuSelectGameListRoot())); connect(ui.action_Start, SIGNAL(triggered()), this, SLOT(OnStartGame())); connect(ui.action_Pause, SIGNAL(triggered()), this, SLOT(OnPauseGame())); connect(ui.action_Stop, SIGNAL(triggered()), this, SLOT(OnStopGame())); - connect(ui.action_Use_Hardware_Renderer, SIGNAL(triggered(bool)), this, SLOT(SetHardwareRendererEnabled(bool))); - connect(ui.action_Use_Shader_JIT, SIGNAL(triggered(bool)), this, SLOT(SetShaderJITEnabled(bool))); - connect(ui.action_Use_Gdbstub, SIGNAL(triggered(bool)), this, SLOT(SetGdbstubEnabled(bool))); connect(ui.action_Single_Window_Mode, SIGNAL(triggered(bool)), this, SLOT(ToggleWindowMode())); - connect(ui.action_Hotkeys, SIGNAL(triggered()), this, SLOT(OnOpenHotkeysDialog())); connect(this, SIGNAL(EmulationStarting(EmuThread*)), disasmWidget, SLOT(OnEmulationStarting(EmuThread*))); connect(this, SIGNAL(EmulationStopping()), disasmWidget, SLOT(OnEmulationStopping())); @@ -201,7 +181,7 @@ GMainWindow::GMainWindow() : emu_thread(nullptr) // Setup hotkeys RegisterHotkey("Main Window", "Load File", QKeySequence::Open); RegisterHotkey("Main Window", "Start Emulation"); - LoadHotkeys(settings); + LoadHotkeys(); connect(GetHotkey("Main Window", "Load File", this), SIGNAL(activated()), this, SLOT(OnMenuLoadFile())); connect(GetHotkey("Main Window", "Start Emulation", this), SIGNAL(activated()), this, SLOT(OnStartGame())); @@ -211,7 +191,7 @@ GMainWindow::GMainWindow() : emu_thread(nullptr) show(); - game_list->PopulateAsync(settings.value("gameListRootDir", ".").toString(), settings.value("gameListDeepScan", false).toBool()); + game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); QStringList args = QApplication::arguments(); if (args.length() >= 2) { @@ -319,7 +299,7 @@ void GMainWindow::BootGame(const std::string& filename) { return; // Create and start the emulation thread - emu_thread = Common::make_unique<EmuThread>(render_window); + emu_thread = std::make_unique<EmuThread>(render_window); emit EmulationStarting(emu_thread.get()); render_window->moveContext(); emu_thread->start(); @@ -375,32 +355,24 @@ void GMainWindow::ShutdownGame() { emulation_running = false; } -void GMainWindow::StoreRecentFile(const std::string& filename) -{ - QSettings settings; - QStringList recent_files = settings.value("recentFiles").toStringList(); - recent_files.prepend(QString::fromStdString(filename)); - recent_files.removeDuplicates(); - while (recent_files.size() > max_recent_files_item) { - recent_files.removeLast(); +void GMainWindow::StoreRecentFile(const std::string& filename) { + UISettings::values.recent_files.prepend(QString::fromStdString(filename)); + UISettings::values.recent_files.removeDuplicates(); + while (UISettings::values.recent_files.size() > max_recent_files_item) { + UISettings::values.recent_files.removeLast(); } - settings.setValue("recentFiles", recent_files); - UpdateRecentFiles(); } void GMainWindow::UpdateRecentFiles() { - QSettings settings; - QStringList recent_files = settings.value("recentFiles").toStringList(); - - unsigned int num_recent_files = std::min(recent_files.size(), static_cast<int>(max_recent_files_item)); + unsigned int num_recent_files = std::min(UISettings::values.recent_files.size(), static_cast<int>(max_recent_files_item)); for (unsigned int i = 0; i < num_recent_files; i++) { - QString text = QString("&%1. %2").arg(i + 1).arg(QFileInfo(recent_files[i]).fileName()); + QString text = QString("&%1. %2").arg(i + 1).arg(QFileInfo(UISettings::values.recent_files[i]).fileName()); actions_recent_files[i]->setText(text); - actions_recent_files[i]->setData(recent_files[i]); - actions_recent_files[i]->setToolTip(recent_files[i]); + actions_recent_files[i]->setData(UISettings::values.recent_files[i]); + actions_recent_files[i]->setToolTip(UISettings::values.recent_files[i]); actions_recent_files[i]->setVisible(true); } @@ -417,40 +389,32 @@ void GMainWindow::UpdateRecentFiles() { } void GMainWindow::OnGameListLoadFile(QString game_path) { - BootGame(game_path.toLocal8Bit().data()); + BootGame(game_path.toStdString()); } void GMainWindow::OnMenuLoadFile() { - QSettings settings; - QString rom_path = settings.value("romsPath", QString()).toString(); - - QString filename = QFileDialog::getOpenFileName(this, tr("Load File"), rom_path, tr("3DS executable (*.3ds *.3dsx *.elf *.axf *.cci *.cxi)")); + QString filename = QFileDialog::getOpenFileName(this, tr("Load File"), UISettings::values.roms_path, tr("3DS executable (*.3ds *.3dsx *.elf *.axf *.cci *.cxi)")); if (!filename.isEmpty()) { - settings.setValue("romsPath", QFileInfo(filename).path()); + UISettings::values.roms_path = QFileInfo(filename).path(); - BootGame(filename.toLocal8Bit().data()); + BootGame(filename.toStdString()); } } void GMainWindow::OnMenuLoadSymbolMap() { - QSettings settings; - QString symbol_path = settings.value("symbolsPath", QString()).toString(); - - QString filename = QFileDialog::getOpenFileName(this, tr("Load Symbol Map"), symbol_path, tr("Symbol map (*)")); + QString filename = QFileDialog::getOpenFileName(this, tr("Load Symbol Map"), UISettings::values.symbols_path, tr("Symbol map (*)")); if (!filename.isEmpty()) { - settings.setValue("symbolsPath", QFileInfo(filename).path()); + UISettings::values.symbols_path = QFileInfo(filename).path(); - LoadSymbolMap(filename.toLocal8Bit().data()); + LoadSymbolMap(filename.toStdString()); } } void GMainWindow::OnMenuSelectGameListRoot() { - QSettings settings; - QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory")); if (!dir_path.isEmpty()) { - settings.setValue("gameListRootDir", dir_path); - game_list->PopulateAsync(dir_path, settings.value("gameListDeepScan").toBool()); + UISettings::values.gamedir = dir_path; + game_list->PopulateAsync(dir_path, UISettings::values.gamedir_deepscan); } } @@ -461,15 +425,12 @@ void GMainWindow::OnMenuRecentFile() { QString filename = action->data().toString(); QFileInfo file_info(filename); if (file_info.exists()) { - BootGame(filename.toLocal8Bit().data()); + BootGame(filename.toStdString()); } else { // Display an error message and remove the file from the list. QMessageBox::information(this, tr("File not found"), tr("File \"%1\" not found").arg(filename)); - QSettings settings; - QStringList recent_files = settings.value("recentFiles").toStringList(); - recent_files.removeOne(filename); - settings.setValue("recentFiles", recent_files); + UISettings::values.recent_files.removeOne(filename); UpdateRecentFiles(); } } @@ -496,31 +457,6 @@ void GMainWindow::OnStopGame() { ShutdownGame(); } -void GMainWindow::OnOpenHotkeysDialog() { - GHotkeysDialog dialog(this); - dialog.exec(); -} - -void GMainWindow::SetHardwareRendererEnabled(bool enabled) { - VideoCore::g_hw_renderer_enabled = enabled; - - Config config; - Settings::values.use_hw_renderer = enabled; - config.Save(); -} - -void GMainWindow::SetGdbstubEnabled(bool enabled) { - GDBStub::ToggleServer(enabled); -} - -void GMainWindow::SetShaderJITEnabled(bool enabled) { - VideoCore::g_shader_jit_enabled = enabled; - - Config config; - Settings::values.use_shader_jit = enabled; - config.Save(); -} - void GMainWindow::ToggleWindowMode() { if (ui.action_Single_Window_Mode->isChecked()) { // Render in the main window... @@ -547,11 +483,17 @@ void GMainWindow::ToggleWindowMode() { } void GMainWindow::OnConfigure() { - //GControllerConfigDialog* dialog = new GControllerConfigDialog(controller_ports, this); + ConfigureDialog configureDialog(this); + auto result = configureDialog.exec(); + if (result == QDialog::Accepted) + { + configureDialog.applyConfiguration(); + config->Save(); + } } bool GMainWindow::ConfirmClose() { - if (emu_thread == nullptr || !confirm_before_closing) + if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) return true; auto answer = QMessageBox::question(this, tr("Citra"), @@ -566,23 +508,18 @@ void GMainWindow::closeEvent(QCloseEvent* event) { return; } - // Save window layout - QSettings settings(QSettings::IniFormat, QSettings::UserScope, "Citra team", "Citra"); + UISettings::values.geometry = saveGeometry(); + UISettings::values.state = saveState(); + UISettings::values.renderwindow_geometry = render_window->saveGeometry(); + UISettings::values.microprofile_geometry = microProfileDialog->saveGeometry(); + UISettings::values.microprofile_visible = microProfileDialog->isVisible(); - settings.beginGroup("UILayout"); - settings.setValue("geometry", saveGeometry()); - settings.setValue("state", saveState()); - settings.setValue("geometryRenderWindow", render_window->saveGeometry()); - settings.setValue("microProfileDialogGeometry", microProfileDialog->saveGeometry()); - settings.setValue("microProfileDialogVisible", microProfileDialog->isVisible()); - settings.endGroup(); + UISettings::values.single_window_mode = ui.action_Single_Window_Mode->isChecked(); + UISettings::values.display_titlebar = ui.actionDisplay_widget_title_bars->isChecked(); + UISettings::values.first_start = false; - settings.setValue("singleWindowMode", ui.action_Single_Window_Mode->isChecked()); - settings.setValue("displayTitleBars", ui.actionDisplay_widget_title_bars->isChecked()); - settings.setValue("firstStart", false); - settings.setValue("confirmClose", confirm_before_closing); - game_list->SaveInterfaceLayout(settings); - SaveHotkeys(settings); + game_list->SaveInterfaceLayout(); + SaveHotkeys(); // Shutdown session if the emu thread is active... if (emu_thread != nullptr) @@ -607,7 +544,6 @@ int main(int argc, char* argv[]) { }); // Init settings params - QSettings::setDefaultFormat(QSettings::IniFormat); QCoreApplication::setOrganizationName("Citra team"); QCoreApplication::setApplicationName("Citra"); diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h index 6e4e56689..477db5c5c 100644 --- a/src/citra_qt/main.h +++ b/src/citra_qt/main.h @@ -10,6 +10,7 @@ #include "ui_main.h" +class Config; class GameList; class GImageInfo; class GRenderWindow; @@ -104,12 +105,8 @@ private slots: /// Called whenever a user selects the "File->Select Game List Root" menu item void OnMenuSelectGameListRoot(); void OnMenuRecentFile(); - void OnOpenHotkeysDialog(); void OnConfigure(); void OnDisplayTitleBars(bool); - void SetHardwareRendererEnabled(bool); - void SetGdbstubEnabled(bool); - void SetShaderJITEnabled(bool); void ToggleWindowMode(); private: @@ -118,6 +115,8 @@ private: GRenderWindow* render_window; GameList* game_list; + std::unique_ptr<Config> config; + // Whether emulation is currently running in Citra. bool emulation_running = false; std::unique_ptr<EmuThread> emu_thread; @@ -131,7 +130,6 @@ private: GPUCommandListWidget* graphicsCommandsWidget; QAction* actions_recent_files[max_recent_files_item]; - bool confirm_before_closing; }; #endif // _CITRA_QT_MAIN_HXX_ diff --git a/src/citra_qt/main.ui b/src/citra_qt/main.ui index 1e8a07cfb..441e0b81e 100644 --- a/src/citra_qt/main.ui +++ b/src/citra_qt/main.ui @@ -45,7 +45,7 @@ <x>0</x> <y>0</y> <width>1081</width> - <height>22</height> + <height>19</height> </rect> </property> <widget class="QMenu" name="menu_File"> @@ -73,9 +73,6 @@ <addaction name="action_Pause"/> <addaction name="action_Stop"/> <addaction name="separator"/> - <addaction name="action_Use_Hardware_Renderer"/> - <addaction name="action_Use_Shader_JIT"/> - <addaction name="action_Use_Gdbstub"/> <addaction name="action_Configure"/> </widget> <widget class="QMenu" name="menu_View"> @@ -84,7 +81,6 @@ </property> <addaction name="action_Single_Window_Mode"/> <addaction name="actionDisplay_widget_title_bars"/> - <addaction name="action_Hotkeys"/> </widget> <widget class="QMenu" name="menu_Help"> <property name="title"> @@ -150,35 +146,6 @@ <string>Single Window Mode</string> </property> </action> - <action name="action_Hotkeys"> - <property name="text"> - <string>Configure &Hotkeys ...</string> - </property> - </action> - <action name="action_Use_Hardware_Renderer"> - <property name="checkable"> - <bool>true</bool> - </property> - <property name="text"> - <string>Use Hardware Renderer</string> - </property> - </action> - <action name="action_Use_Shader_JIT"> - <property name="checkable"> - <bool>true</bool> - </property> - <property name="text"> - <string>Use Shader JIT</string> - </property> - </action> - <action name="action_Use_Gdbstub"> - <property name="checkable"> - <bool>true</bool> - </property> - <property name="text"> - <string>Use Gdbstub</string> - </property> - </action> <action name="action_Configure"> <property name="text"> <string>Configure ...</string> @@ -220,22 +187,6 @@ </hints> </connection> <connection> - <sender>action_Configure</sender> - <signal>triggered()</signal> - <receiver>MainWindow</receiver> - <slot>OnConfigure()</slot> - <hints> - <hint type="sourcelabel"> - <x>-1</x> - <y>-1</y> - </hint> - <hint type="destinationlabel"> - <x>540</x> - <y>364</y> - </hint> - </hints> - </connection> - <connection> <sender>actionDisplay_widget_title_bars</sender> <signal>triggered(bool)</signal> <receiver>MainWindow</receiver> diff --git a/src/citra_qt/ui_settings.cpp b/src/citra_qt/ui_settings.cpp new file mode 100644 index 000000000..5f2215899 --- /dev/null +++ b/src/citra_qt/ui_settings.cpp @@ -0,0 +1,11 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "ui_settings.h" + +namespace UISettings { + +Values values = {}; + +} diff --git a/src/citra_qt/ui_settings.h b/src/citra_qt/ui_settings.h new file mode 100644 index 000000000..62db4a73e --- /dev/null +++ b/src/citra_qt/ui_settings.h @@ -0,0 +1,47 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <QByteArray> +#include <QStringList> +#include <QString> + +#include <vector> + +namespace UISettings { + +using ContextualShortcut = std::pair<QString, int> ; +using Shortcut = std::pair<QString, ContextualShortcut>; + +struct Values { + QByteArray geometry; + QByteArray state; + + QByteArray renderwindow_geometry; + + QByteArray gamelist_header_state; + + QByteArray microprofile_geometry; + bool microprofile_visible; + + bool single_window_mode; + bool display_titlebar; + + bool confirm_before_closing; + bool first_start; + + QString roms_path; + QString symbols_path; + QString gamedir; + bool gamedir_deepscan; + QStringList recent_files; + + // Shortcut name <Shortcut, context> + std::vector<Shortcut> shortcuts; +}; + +extern Values values; + +} diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 1c9be718f..c839ce173 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -42,7 +42,6 @@ set(HEADERS logging/filter.h logging/log.h logging/backend.h - make_unique.h math_util.h memory_util.h microprofile.h diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index c3061479a..53700c865 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp @@ -85,7 +85,7 @@ bool Exists(const std::string &filename) StripTailDirSlashes(copy); #ifdef _WIN32 - int result = _tstat64(Common::UTF8ToTStr(copy).c_str(), &file_info); + int result = _wstat64(Common::UTF8ToUTF16W(copy).c_str(), &file_info); #else int result = stat64(copy.c_str(), &file_info); #endif @@ -102,7 +102,7 @@ bool IsDirectory(const std::string &filename) StripTailDirSlashes(copy); #ifdef _WIN32 - int result = _tstat64(Common::UTF8ToTStr(copy).c_str(), &file_info); + int result = _wstat64(Common::UTF8ToUTF16W(copy).c_str(), &file_info); #else int result = stat64(copy.c_str(), &file_info); #endif @@ -138,7 +138,7 @@ bool Delete(const std::string &filename) } #ifdef _WIN32 - if (!DeleteFile(Common::UTF8ToTStr(filename).c_str())) + if (!DeleteFileW(Common::UTF8ToUTF16W(filename).c_str())) { LOG_ERROR(Common_Filesystem, "DeleteFile failed on %s: %s", filename.c_str(), GetLastErrorMsg()); @@ -160,7 +160,7 @@ bool CreateDir(const std::string &path) { LOG_TRACE(Common_Filesystem, "directory %s", path.c_str()); #ifdef _WIN32 - if (::CreateDirectory(Common::UTF8ToTStr(path).c_str(), nullptr)) + if (::CreateDirectoryW(Common::UTF8ToUTF16W(path).c_str(), nullptr)) return true; DWORD error = GetLastError(); if (error == ERROR_ALREADY_EXISTS) @@ -241,7 +241,7 @@ bool DeleteDir(const std::string &filename) } #ifdef _WIN32 - if (::RemoveDirectory(Common::UTF8ToTStr(filename).c_str())) + if (::RemoveDirectoryW(Common::UTF8ToUTF16W(filename).c_str())) return true; #else if (rmdir(filename.c_str()) == 0) @@ -257,8 +257,13 @@ bool Rename(const std::string &srcFilename, const std::string &destFilename) { LOG_TRACE(Common_Filesystem, "%s --> %s", srcFilename.c_str(), destFilename.c_str()); +#ifdef _WIN32 + if (_wrename(Common::UTF8ToUTF16W(srcFilename).c_str(), Common::UTF8ToUTF16W(destFilename).c_str()) == 0) + return true; +#else if (rename(srcFilename.c_str(), destFilename.c_str()) == 0) return true; +#endif LOG_ERROR(Common_Filesystem, "failed %s --> %s: %s", srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); return false; @@ -270,7 +275,7 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename) LOG_TRACE(Common_Filesystem, "%s --> %s", srcFilename.c_str(), destFilename.c_str()); #ifdef _WIN32 - if (CopyFile(Common::UTF8ToTStr(srcFilename).c_str(), Common::UTF8ToTStr(destFilename).c_str(), FALSE)) + if (CopyFileW(Common::UTF8ToUTF16W(srcFilename).c_str(), Common::UTF8ToUTF16W(destFilename).c_str(), FALSE)) return true; LOG_ERROR(Common_Filesystem, "failed %s --> %s: %s", @@ -358,7 +363,7 @@ u64 GetSize(const std::string &filename) struct stat64 buf; #ifdef _WIN32 - if (_tstat64(Common::UTF8ToTStr(filename).c_str(), &buf) == 0) + if (_wstat64(Common::UTF8ToUTF16W(filename).c_str(), &buf) == 0) #else if (stat64(filename.c_str(), &buf) == 0) #endif @@ -432,16 +437,16 @@ bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directo #ifdef _WIN32 // Find the first file in the directory. - WIN32_FIND_DATA ffd; + WIN32_FIND_DATAW ffd; - HANDLE handle_find = FindFirstFile(Common::UTF8ToTStr(directory + "\\*").c_str(), &ffd); + HANDLE handle_find = FindFirstFileW(Common::UTF8ToUTF16W(directory + "\\*").c_str(), &ffd); if (handle_find == INVALID_HANDLE_VALUE) { FindClose(handle_find); return false; } // windows loop do { - const std::string virtual_name(Common::TStrToUTF8(ffd.cFileName)); + const std::string virtual_name(Common::UTF16ToUTF8(ffd.cFileName)); #else struct dirent dirent, *result = nullptr; @@ -465,7 +470,7 @@ bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directo found_entries += ret_entries; #ifdef _WIN32 - } while (FindNextFile(handle_find, &ffd) != 0); + } while (FindNextFileW(handle_find, &ffd) != 0); FindClose(handle_find); #else } @@ -572,15 +577,23 @@ void CopyDir(const std::string &source_path, const std::string &dest_path) // Returns the current directory std::string GetCurrentDir() { - char *dir; // Get the current working directory (getcwd uses malloc) +#ifdef _WIN32 + wchar_t *dir; + if (!(dir = _wgetcwd(nullptr, 0))) { +#else + char *dir; if (!(dir = getcwd(nullptr, 0))) { - +#endif LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: %s", GetLastErrorMsg()); return nullptr; } +#ifdef _WIN32 + std::string strDir = Common::UTF16ToUTF8(dir); +#else std::string strDir = dir; +#endif free(dir); return strDir; } @@ -588,7 +601,11 @@ std::string GetCurrentDir() // Sets the current directory to the given directory bool SetCurrentDir(const std::string &directory) { +#ifdef _WIN32 + return _wchdir(Common::UTF8ToUTF16W(directory).c_str()) == 0; +#else return chdir(directory.c_str()) == 0; +#endif } #if defined(__APPLE__) @@ -613,9 +630,9 @@ std::string& GetExeDirectory() static std::string exe_path; if (exe_path.empty()) { - TCHAR tchar_exe_path[2048]; - GetModuleFileName(nullptr, tchar_exe_path, 2048); - exe_path = Common::TStrToUTF8(tchar_exe_path); + wchar_t wchar_exe_path[2048]; + GetModuleFileNameW(nullptr, wchar_exe_path, 2048); + exe_path = Common::UTF16ToUTF8(wchar_exe_path); exe_path = exe_path.substr(0, exe_path.find_last_of('\\')); } return exe_path; @@ -807,13 +824,12 @@ size_t WriteStringToFile(bool text_file, const std::string &str, const char *fil size_t ReadFileToString(bool text_file, const char *filename, std::string &str) { - FileUtil::IOFile file(filename, text_file ? "r" : "rb"); - auto const f = file.GetHandle(); + IOFile file(filename, text_file ? "r" : "rb"); - if (!f) + if (!file) return false; - str.resize(static_cast<u32>(GetSize(f))); + str.resize(static_cast<u32>(file.GetSize())); return file.ReadArray(&str[0], str.size()); } @@ -860,15 +876,10 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam } IOFile::IOFile() - : m_file(nullptr), m_good(true) -{} - -IOFile::IOFile(std::FILE* file) - : m_file(file), m_good(true) -{} +{ +} IOFile::IOFile(const std::string& filename, const char openmode[]) - : m_file(nullptr), m_good(true) { Open(filename, openmode); } @@ -879,7 +890,6 @@ IOFile::~IOFile() } IOFile::IOFile(IOFile&& other) - : m_file(nullptr), m_good(true) { Swap(other); } @@ -900,7 +910,7 @@ bool IOFile::Open(const std::string& filename, const char openmode[]) { Close(); #ifdef _WIN32 - _tfopen_s(&m_file, Common::UTF8ToTStr(filename).c_str(), Common::UTF8ToTStr(openmode).c_str()); + _wfopen_s(&m_file, Common::UTF8ToUTF16W(filename).c_str(), Common::UTF8ToUTF16W(openmode).c_str()); #else m_file = fopen(filename.c_str(), openmode); #endif @@ -918,26 +928,12 @@ bool IOFile::Close() return m_good; } -std::FILE* IOFile::ReleaseHandle() -{ - std::FILE* const ret = m_file; - m_file = nullptr; - return ret; -} - -void IOFile::SetHandle(std::FILE* file) -{ - Close(); - Clear(); - m_file = file; -} - -u64 IOFile::GetSize() +u64 IOFile::GetSize() const { if (IsOpen()) return FileUtil::GetSize(m_file); - else - return 0; + + return 0; } bool IOFile::Seek(s64 off, int origin) @@ -948,12 +944,12 @@ bool IOFile::Seek(s64 off, int origin) return m_good; } -u64 IOFile::Tell() +u64 IOFile::Tell() const { if (IsOpen()) return ftello(m_file); - else - return -1; + + return -1; } bool IOFile::Flush() diff --git a/src/common/file_util.h b/src/common/file_util.h index a85121aa6..b54a9fb72 100644 --- a/src/common/file_util.h +++ b/src/common/file_util.h @@ -14,6 +14,10 @@ #include "common/common_types.h" +#ifdef _MSC_VER +#include "common/string_util.h" +#endif + // User directory indices for GetUserPath enum { D_USER_IDX, @@ -172,7 +176,6 @@ class IOFile : public NonCopyable { public: IOFile(); - IOFile(std::FILE* file); IOFile(const std::string& filename, const char openmode[]); ~IOFile(); @@ -188,6 +191,9 @@ public: template <typename T> size_t ReadArray(T* data, size_t length) { + static_assert(std::is_standard_layout<T>(), "Given array does not consist of standard layout objects"); + static_assert(std::is_trivially_copyable<T>(), "Given array does not consist of trivially copyable objects"); + if (!IsOpen()) { m_good = false; return -1; @@ -203,9 +209,8 @@ public: template <typename T> size_t WriteArray(const T* data, size_t length) { - static_assert(std::is_standard_layout<T>::value, "Given array does not consist of standard layout objects"); - // TODO: gcc 4.8 does not support is_trivially_copyable, but we really should check for it here. - //static_assert(std::is_trivially_copyable<T>::value, "Given array does not consist of trivially copyable objects"); + static_assert(std::is_standard_layout<T>(), "Given array does not consist of standard layout objects"); + static_assert(std::is_trivially_copyable<T>(), "Given array does not consist of trivially copyable objects"); if (!IsOpen()) { m_good = false; @@ -235,32 +240,24 @@ public: return WriteArray(&object, 1); } - bool IsOpen() { return nullptr != m_file; } + bool IsOpen() const { return nullptr != m_file; } // m_good is set to false when a read, write or other function fails - bool IsGood() { return m_good; } - operator void*() { return m_good ? m_file : nullptr; } - - std::FILE* ReleaseHandle(); - - std::FILE* GetHandle() { return m_file; } - - void SetHandle(std::FILE* file); + bool IsGood() const { return m_good; } + explicit operator bool() const { return IsGood(); } bool Seek(s64 off, int origin); - u64 Tell(); - u64 GetSize(); + u64 Tell() const; + u64 GetSize() const; bool Resize(u64 size); bool Flush(); // clear error state void Clear() { m_good = true; std::clearerr(m_file); } - std::FILE* m_file; - bool m_good; private: - IOFile(IOFile&); - IOFile& operator=(IOFile& other); + std::FILE* m_file = nullptr; + bool m_good = true; }; } // namespace diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 4c86151ab..3d39f94d5 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -34,6 +34,7 @@ namespace Log { SUB(Kernel, SVC) \ CLS(Service) \ SUB(Service, SRV) \ + SUB(Service, FRD) \ SUB(Service, FS) \ SUB(Service, ERR) \ SUB(Service, APT) \ @@ -46,8 +47,10 @@ namespace Log { SUB(Service, NIM) \ SUB(Service, NWM) \ SUB(Service, CAM) \ + SUB(Service, CECD) \ SUB(Service, CFG) \ SUB(Service, DSP) \ + SUB(Service, DLP) \ SUB(Service, HID) \ SUB(Service, SOC) \ SUB(Service, IR) \ diff --git a/src/common/logging/log.h b/src/common/logging/log.h index e4c39c308..521362317 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h @@ -49,6 +49,7 @@ enum class Class : ClassType { Service, ///< HLE implementation of system services. Each major service /// should have its own subclass. Service_SRV, ///< The SRV (Service Directory) implementation + Service_FRD, ///< The FRD (Friends) service Service_FS, ///< The FS (Filesystem) service implementation Service_ERR, ///< The ERR (Error) port implementation Service_APT, ///< The APT (Applets) service @@ -61,8 +62,10 @@ enum class Class : ClassType { Service_NIM, ///< The NIM (Network interface manager) service Service_NWM, ///< The NWM (Network wlan manager) service Service_CAM, ///< The CAM (Camera) service + Service_CECD, ///< The CECD (StreetPass) service Service_CFG, ///< The CFG (Configuration) service Service_DSP, ///< The DSP (DSP control) service + Service_DLP, ///< The DLP (Download Play) service Service_HID, ///< The HID (Human interface device) service Service_SOC, ///< The SOC (Socket) service Service_IR, ///< The IR service diff --git a/src/common/make_unique.h b/src/common/make_unique.h deleted file mode 100644 index f6e7f017c..000000000 --- a/src/common/make_unique.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <algorithm> -#include <memory> - -namespace Common { - -template <typename T, typename... Args> -std::unique_ptr<T> make_unique(Args&&... args) { - return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); -} - -} // namespace diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp index 6d6fc591f..f0aa072db 100644 --- a/src/common/string_util.cpp +++ b/src/common/string_util.cpp @@ -320,27 +320,27 @@ std::u16string UTF8ToUTF16(const std::string& input) #endif } -static std::string UTF16ToUTF8(const std::wstring& input) +static std::wstring CPToUTF16(u32 code_page, const std::string& input) { - auto const size = WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast<int>(input.size()), nullptr, 0, nullptr, nullptr); + auto const size = MultiByteToWideChar(code_page, 0, input.data(), static_cast<int>(input.size()), nullptr, 0); - std::string output; + std::wstring output; output.resize(size); - if (size == 0 || size != WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast<int>(input.size()), &output[0], static_cast<int>(output.size()), nullptr, nullptr)) + if (size == 0 || size != MultiByteToWideChar(code_page, 0, input.data(), static_cast<int>(input.size()), &output[0], static_cast<int>(output.size()))) output.clear(); return output; } -static std::wstring CPToUTF16(u32 code_page, const std::string& input) +std::string UTF16ToUTF8(const std::wstring& input) { - auto const size = MultiByteToWideChar(code_page, 0, input.data(), static_cast<int>(input.size()), nullptr, 0); + auto const size = WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast<int>(input.size()), nullptr, 0, nullptr, nullptr); - std::wstring output; + std::string output; output.resize(size); - if (size == 0 || size != MultiByteToWideChar(code_page, 0, input.data(), static_cast<int>(input.size()), &output[0], static_cast<int>(output.size()))) + if (size == 0 || size != WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast<int>(input.size()), &output[0], static_cast<int>(output.size()), nullptr, nullptr)) output.clear(); return output; diff --git a/src/common/string_util.h b/src/common/string_util.h index c5c474c6f..89d9f133e 100644 --- a/src/common/string_util.h +++ b/src/common/string_util.h @@ -95,7 +95,7 @@ std::string CP1252ToUTF8(const std::string& str); std::string SHIFTJISToUTF8(const std::string& str); #ifdef _WIN32 - +std::string UTF16ToUTF8(const std::wstring& input); std::wstring UTF8ToUTF16W(const std::string& str); #ifdef _UNICODE diff --git a/src/common/thread.h b/src/common/thread.h index 8255ee6d3..bbfa8befa 100644 --- a/src/common/thread.h +++ b/src/common/thread.h @@ -30,8 +30,7 @@ # endif #endif -namespace Common -{ +namespace Common { int CurrentThreadId(); @@ -43,55 +42,55 @@ public: Event() : is_set(false) {} void Set() { - std::lock_guard<std::mutex> lk(m_mutex); + std::lock_guard<std::mutex> lk(mutex); if (!is_set) { is_set = true; - m_condvar.notify_one(); + condvar.notify_one(); } } void Wait() { - std::unique_lock<std::mutex> lk(m_mutex); - m_condvar.wait(lk, [&]{ return is_set; }); + std::unique_lock<std::mutex> lk(mutex); + condvar.wait(lk, [&]{ return is_set; }); is_set = false; } void Reset() { - std::unique_lock<std::mutex> lk(m_mutex); + std::unique_lock<std::mutex> lk(mutex); // no other action required, since wait loops on the predicate and any lingering signal will get cleared on the first iteration is_set = false; } private: bool is_set; - std::condition_variable m_condvar; - std::mutex m_mutex; + std::condition_variable condvar; + std::mutex mutex; }; class Barrier { public: - Barrier(size_t count) : m_count(count), m_waiting(0) {} + explicit Barrier(size_t count_) : count(count_), waiting(0), generation(0) {} /// Blocks until all "count" threads have called Sync() void Sync() { - std::unique_lock<std::mutex> lk(m_mutex); + std::unique_lock<std::mutex> lk(mutex); + const size_t current_generation = generation; - // TODO: broken when next round of Sync()s - // is entered before all waiting threads return from the notify_all - - if (++m_waiting == m_count) { - m_waiting = 0; - m_condvar.notify_all(); + if (++waiting == count) { + generation++; + waiting = 0; + condvar.notify_all(); } else { - m_condvar.wait(lk, [&]{ return m_waiting == 0; }); + condvar.wait(lk, [this, current_generation]{ return current_generation != generation; }); } } private: - std::condition_variable m_condvar; - std::mutex m_mutex; - const size_t m_count; - size_t m_waiting; + std::condition_variable condvar; + std::mutex mutex; + const size_t count; + size_t waiting; + size_t generation; // Incremented once each time the barrier is used }; void SleepCurrentThread(int ms); @@ -100,8 +99,7 @@ void SwitchCurrentThread(); // On Linux, this is equal to sleep 1ms // Use this function during a spin-wait to make the current thread // relax while another thread is working. This may be more efficient // than using events because event functions use kernel calls. -inline void YieldCPU() -{ +inline void YieldCPU() { std::this_thread::yield(); } diff --git a/src/common/x64/emitter.cpp b/src/common/x64/emitter.cpp index 1dcf2416c..5662f7f86 100644 --- a/src/common/x64/emitter.cpp +++ b/src/common/x64/emitter.cpp @@ -455,6 +455,18 @@ void XEmitter::CALL(const void* fnptr) Write32(u32(distance)); } +FixupBranch XEmitter::CALL() +{ + FixupBranch branch; + branch.type = 1; + branch.ptr = code + 5; + + Write8(0xE8); + Write32(0); + + return branch; +} + FixupBranch XEmitter::J(bool force5bytes) { FixupBranch branch; @@ -531,6 +543,22 @@ void XEmitter::SetJumpTarget(const FixupBranch& branch) } } +void XEmitter::SetJumpTarget(const FixupBranch& branch, const u8* target) +{ + if (branch.type == 0) + { + s64 distance = (s64)(target - branch.ptr); + ASSERT_MSG(distance >= -0x80 && distance < 0x80, "Jump target too far away, needs force5Bytes = true"); + branch.ptr[-1] = (u8)(s8)distance; + } + else if (branch.type == 1) + { + s64 distance = (s64)(target - branch.ptr); + ASSERT_MSG(distance >= -0x80000000LL && distance < 0x80000000LL, "Jump target too far away, needs indirect register"); + ((s32*)branch.ptr)[-1] = (s32)distance; + } +} + //Single byte opcodes //There is no PUSHAD/POPAD in 64-bit mode. void XEmitter::INT3() {Write8(0xCC);} diff --git a/src/common/x64/emitter.h b/src/common/x64/emitter.h index 7c6548fb5..a33724146 100644 --- a/src/common/x64/emitter.h +++ b/src/common/x64/emitter.h @@ -425,12 +425,14 @@ public: #undef CALL #endif void CALL(const void* fnptr); + FixupBranch CALL(); void CALLptr(OpArg arg); FixupBranch J_CC(CCFlags conditionCode, bool force5bytes = false); void J_CC(CCFlags conditionCode, const u8* addr, bool force5Bytes = false); void SetJumpTarget(const FixupBranch& branch); + void SetJumpTarget(const FixupBranch& branch, const u8* target); void SETcc(CCFlags flag, OpArg dest); // Note: CMOV brings small if any benefit on current cpus. diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 3473e2f5b..a8d891689 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -68,6 +68,7 @@ set(SRCS hle/service/cfg/cfg_s.cpp hle/service/cfg/cfg_u.cpp hle/service/csnd_snd.cpp + hle/service/dlp_srvr.cpp hle/service/dsp_dsp.cpp hle/service/err_f.cpp hle/service/frd/frd.cpp @@ -200,6 +201,7 @@ set(HEADERS hle/service/cfg/cfg_s.h hle/service/cfg/cfg_u.h hle/service/csnd_snd.h + hle/service/dlp_srvr.h hle/service/dsp_dsp.h hle/service/err_f.h hle/service/frd/frd.h diff --git a/src/core/arm/dyncom/arm_dyncom.cpp b/src/core/arm/dyncom/arm_dyncom.cpp index f3be2c857..a3581132c 100644 --- a/src/core/arm/dyncom/arm_dyncom.cpp +++ b/src/core/arm/dyncom/arm_dyncom.cpp @@ -3,8 +3,7 @@ // Refer to the license.txt file included. #include <cstring> - -#include "common/make_unique.h" +#include <memory> #include "core/arm/skyeye_common/armstate.h" #include "core/arm/skyeye_common/armsupp.h" @@ -18,7 +17,7 @@ #include "core/core_timing.h" ARM_DynCom::ARM_DynCom(PrivilegeMode initial_mode) { - state = Common::make_unique<ARMul_State>(initial_mode); + state = std::make_unique<ARMul_State>(initial_mode); } ARM_DynCom::~ARM_DynCom() { @@ -94,7 +93,7 @@ void ARM_DynCom::ResetContext(Core::ThreadContext& context, u32 stack_top, u32 e context.cpu_registers[0] = arg; context.pc = entry_point; context.sp = stack_top; - context.cpsr = 0x1F; // Usermode + context.cpsr = 0x1F | ((entry_point & 1) << 5); // Usermode and THUMB mode } void ARM_DynCom::SaveContext(Core::ThreadContext& ctx) { diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp index 5f8826034..647784208 100644 --- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp +++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp @@ -36,7 +36,8 @@ enum { CALL = (1 << 4), RET = (1 << 5), END_OF_PAGE = (1 << 6), - THUMB = (1 << 7) + THUMB = (1 << 7), + SINGLE_STEP = (1 << 8) }; #define RM BITS(sht_oper, 0, 3) @@ -3466,7 +3467,35 @@ enum { MICROPROFILE_DEFINE(DynCom_Decode, "DynCom", "Decode", MP_RGB(255, 64, 64)); -static int InterpreterTranslate(ARMul_State* cpu, int& bb_start, u32 addr) { +static unsigned int InterpreterTranslateInstruction(const ARMul_State* cpu, const u32 phys_addr, ARM_INST_PTR& inst_base) { + unsigned int inst_size = 4; + unsigned int inst = Memory::Read32(phys_addr & 0xFFFFFFFC); + + // If we are in Thumb mode, we'll translate one Thumb instruction to the corresponding ARM instruction + if (cpu->TFlag) { + u32 arm_inst; + ThumbDecodeStatus state = DecodeThumbInstruction(inst, phys_addr, &arm_inst, &inst_size, &inst_base); + + // We have translated the Thumb branch instruction in the Thumb decoder + if (state == ThumbDecodeStatus::BRANCH) { + return inst_size; + } + inst = arm_inst; + } + + int idx; + if (DecodeARMInstruction(inst, &idx) == ARMDecodeStatus::FAILURE) { + std::string disasm = ARM_Disasm::Disassemble(phys_addr, inst); + LOG_ERROR(Core_ARM11, "Decode failure.\tPC : [0x%x]\tInstruction : %s [%x]", phys_addr, disasm.c_str(), inst); + LOG_ERROR(Core_ARM11, "cpsr=0x%x, cpu->TFlag=%d, r15=0x%x", cpu->Cpsr, cpu->TFlag, cpu->Reg[15]); + CITRA_IGNORE_EXIT(-1); + } + inst_base = arm_instruction_trans[idx](inst, idx); + + return inst_size; +} + +static int InterpreterTranslateBlock(ARMul_State* cpu, int& bb_start, u32 addr) { Common::Profiling::ScopeTimer timer_decode(profile_decode); MICROPROFILE_SCOPE(DynCom_Decode); @@ -3475,8 +3504,6 @@ static int InterpreterTranslate(ARMul_State* cpu, int& bb_start, u32 addr) { // Go on next, until terminal instruction // Save start addr of basicblock in CreamCache ARM_INST_PTR inst_base = nullptr; - unsigned int inst, inst_size = 4; - int idx; int ret = NON_BRANCH; int size = 0; // instruction size of basic block bb_start = top; @@ -3485,30 +3512,10 @@ static int InterpreterTranslate(ARMul_State* cpu, int& bb_start, u32 addr) { u32 pc_start = cpu->Reg[15]; while (ret == NON_BRANCH) { - inst = Memory::Read32(phys_addr & 0xFFFFFFFC); + unsigned int inst_size = InterpreterTranslateInstruction(cpu, phys_addr, inst_base); size++; - // If we are in Thumb mode, we'll translate one Thumb instruction to the corresponding ARM instruction - if (cpu->TFlag) { - u32 arm_inst; - ThumbDecodeStatus state = DecodeThumbInstruction(inst, phys_addr, &arm_inst, &inst_size, &inst_base); - - // We have translated the Thumb branch instruction in the Thumb decoder - if (state == ThumbDecodeStatus::BRANCH) { - goto translated; - } - inst = arm_inst; - } - - if (DecodeARMInstruction(inst, &idx) == ARMDecodeStatus::FAILURE) { - std::string disasm = ARM_Disasm::Disassemble(phys_addr, inst); - LOG_ERROR(Core_ARM11, "Decode failure.\tPC : [0x%x]\tInstruction : %s [%x]", phys_addr, disasm.c_str(), inst); - LOG_ERROR(Core_ARM11, "cpsr=0x%x, cpu->TFlag=%d, r15=0x%x", cpu->Cpsr, cpu->TFlag, cpu->Reg[15]); - CITRA_IGNORE_EXIT(-1); - } - inst_base = arm_instruction_trans[idx](inst, idx); -translated: phys_addr += inst_size; if ((phys_addr & 0xfff) == 0) { @@ -3522,6 +3529,27 @@ translated: return KEEP_GOING; } +static int InterpreterTranslateSingle(ARMul_State* cpu, int& bb_start, u32 addr) { + Common::Profiling::ScopeTimer timer_decode(profile_decode); + MICROPROFILE_SCOPE(DynCom_Decode); + + ARM_INST_PTR inst_base = nullptr; + bb_start = top; + + u32 phys_addr = addr; + u32 pc_start = cpu->Reg[15]; + + InterpreterTranslateInstruction(cpu, phys_addr, inst_base); + + if (inst_base->br == NON_BRANCH) { + inst_base->br = SINGLE_STEP; + } + + cpu->instruction_cache[pc_start] = bb_start; + + return KEEP_GOING; +} + static int clz(unsigned int x) { int n; if (x == 0) return (32); @@ -3871,8 +3899,11 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { auto itr = cpu->instruction_cache.find(cpu->Reg[15]); if (itr != cpu->instruction_cache.end()) { ptr = itr->second; + } else if (cpu->NumInstrsToExecute != 1) { + if (InterpreterTranslateBlock(cpu, ptr, cpu->Reg[15]) == FETCH_EXCEPTION) + goto END; } else { - if (InterpreterTranslate(cpu, ptr, cpu->Reg[15]) == FETCH_EXCEPTION) + if (InterpreterTranslateSingle(cpu, ptr, cpu->Reg[15]) == FETCH_EXCEPTION) goto END; } @@ -3924,9 +3955,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { add_inst* const inst_cream = (add_inst*)inst_base->component; - u32 rn_val = RN; - if (inst_cream->Rn == 15) - rn_val += 2 * cpu->GetInstructionSize(); + u32 rn_val = CHECK_READ_REG15_WA(cpu, inst_cream->Rn); bool carry; bool overflow; @@ -4051,11 +4080,12 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { if ((inst_base->cond == ConditionCode::AL) || CondPassed(cpu, inst_base->cond)) { unsigned int inst = inst_cream->inst; if (BITS(inst, 20, 27) == 0x12 && BITS(inst, 4, 7) == 0x3) { + const u32 jump_address = cpu->Reg[inst_cream->val.Rm]; cpu->Reg[14] = (cpu->Reg[15] + cpu->GetInstructionSize()); if(cpu->TFlag) cpu->Reg[14] |= 0x1; - cpu->Reg[15] = cpu->Reg[inst_cream->val.Rm] & 0xfffffffe; - cpu->TFlag = cpu->Reg[inst_cream->val.Rm] & 0x1; + cpu->Reg[15] = jump_address & 0xfffffffe; + cpu->TFlag = jump_address & 0x1; } else { cpu->Reg[14] = (cpu->Reg[15] + cpu->GetInstructionSize()); cpu->TFlag = 0x1; @@ -6136,9 +6166,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { sub_inst* const inst_cream = (sub_inst*)inst_base->component; - u32 rn_val = RN; - if (inst_cream->Rn == 15) - rn_val += 2 * cpu->GetInstructionSize(); + u32 rn_val = CHECK_READ_REG15_WA(cpu, inst_cream->Rn); bool carry; bool overflow; diff --git a/src/core/core.cpp b/src/core/core.cpp index 84d6c392e..3bb843aab 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -4,7 +4,6 @@ #include <memory> -#include "common/make_unique.h" #include "common/logging/log.h" #include "core/core.h" @@ -74,8 +73,8 @@ void Stop() { /// Initialize the core void Init() { - g_sys_core = Common::make_unique<ARM_DynCom>(USER32MODE); - g_app_core = Common::make_unique<ARM_DynCom>(USER32MODE); + g_sys_core = std::make_unique<ARM_DynCom>(USER32MODE); + g_app_core = std::make_unique<ARM_DynCom>(USER32MODE); LOG_DEBUG(Core, "Initialized OK"); } diff --git a/src/core/file_sys/archive_extsavedata.cpp b/src/core/file_sys/archive_extsavedata.cpp index 961264fe5..1d9eaefcb 100644 --- a/src/core/file_sys/archive_extsavedata.cpp +++ b/src/core/file_sys/archive_extsavedata.cpp @@ -3,12 +3,12 @@ // Refer to the license.txt file included. #include <algorithm> +#include <memory> #include <vector> #include "common/common_types.h" #include "common/file_util.h" #include "common/logging/log.h" -#include "common/make_unique.h" #include "common/string_util.h" #include "core/file_sys/archive_extsavedata.h" @@ -84,7 +84,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_ExtSaveData::Open(cons ErrorSummary::InvalidState, ErrorLevel::Status); } } - auto archive = Common::make_unique<DiskArchive>(fullpath); + auto archive = std::make_unique<DiskArchive>(fullpath); return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); } diff --git a/src/core/file_sys/archive_romfs.cpp b/src/core/file_sys/archive_romfs.cpp index a9a29ebde..38828b546 100644 --- a/src/core/file_sys/archive_romfs.cpp +++ b/src/core/file_sys/archive_romfs.cpp @@ -7,7 +7,6 @@ #include "common/common_types.h" #include "common/logging/log.h" -#include "common/make_unique.h" #include "core/file_sys/archive_romfs.h" #include "core/file_sys/ivfc_archive.h" @@ -25,7 +24,7 @@ ArchiveFactory_RomFS::ArchiveFactory_RomFS(Loader::AppLoader& app_loader) { } ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_RomFS::Open(const Path& path) { - auto archive = Common::make_unique<IVFCArchive>(romfs_file, data_offset, data_size); + auto archive = std::make_unique<IVFCArchive>(romfs_file, data_offset, data_size); return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); } diff --git a/src/core/file_sys/archive_savedata.cpp b/src/core/file_sys/archive_savedata.cpp index fe020d21c..fd5711e14 100644 --- a/src/core/file_sys/archive_savedata.cpp +++ b/src/core/file_sys/archive_savedata.cpp @@ -3,11 +3,11 @@ // Refer to the license.txt file included. #include <algorithm> +#include <memory> #include "common/common_types.h" #include "common/file_util.h" #include "common/logging/log.h" -#include "common/make_unique.h" #include "common/string_util.h" #include "core/file_sys/archive_savedata.h" @@ -53,7 +53,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveData::Open(const P ErrorSummary::InvalidState, ErrorLevel::Status); } - auto archive = Common::make_unique<DiskArchive>(std::move(concrete_mount_point)); + auto archive = std::make_unique<DiskArchive>(std::move(concrete_mount_point)); return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); } diff --git a/src/core/file_sys/archive_savedatacheck.cpp b/src/core/file_sys/archive_savedatacheck.cpp index 3db11c500..9f65e5455 100644 --- a/src/core/file_sys/archive_savedatacheck.cpp +++ b/src/core/file_sys/archive_savedatacheck.cpp @@ -3,12 +3,12 @@ // Refer to the license.txt file included. #include <algorithm> +#include <memory> #include <vector> #include "common/common_types.h" #include "common/file_util.h" #include "common/logging/log.h" -#include "common/make_unique.h" #include "common/string_util.h" #include "core/file_sys/archive_savedatacheck.h" @@ -44,7 +44,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveDataCheck::Open(co } auto size = file->GetSize(); - auto archive = Common::make_unique<IVFCArchive>(file, 0, size); + auto archive = std::make_unique<IVFCArchive>(file, 0, size); return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); } diff --git a/src/core/file_sys/archive_sdmc.cpp b/src/core/file_sys/archive_sdmc.cpp index 657221cbf..9b218af58 100644 --- a/src/core/file_sys/archive_sdmc.cpp +++ b/src/core/file_sys/archive_sdmc.cpp @@ -3,10 +3,10 @@ // Refer to the license.txt file included. #include <algorithm> +#include <memory> #include "common/file_util.h" #include "common/logging/log.h" -#include "common/make_unique.h" #include "core/file_sys/archive_sdmc.h" #include "core/file_sys/disk_archive.h" @@ -36,7 +36,7 @@ bool ArchiveFactory_SDMC::Initialize() { } ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SDMC::Open(const Path& path) { - auto archive = Common::make_unique<DiskArchive>(sdmc_directory); + auto archive = std::make_unique<DiskArchive>(sdmc_directory); return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); } diff --git a/src/core/file_sys/archive_systemsavedata.cpp b/src/core/file_sys/archive_systemsavedata.cpp index e1780de2f..1bcc228a1 100644 --- a/src/core/file_sys/archive_systemsavedata.cpp +++ b/src/core/file_sys/archive_systemsavedata.cpp @@ -3,11 +3,11 @@ // Refer to the license.txt file included. #include <algorithm> +#include <memory> #include <vector> #include "common/common_types.h" #include "common/file_util.h" -#include "common/make_unique.h" #include "common/string_util.h" #include "core/file_sys/archive_systemsavedata.h" @@ -59,7 +59,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SystemSaveData::Open(c return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS, ErrorSummary::InvalidState, ErrorLevel::Status); } - auto archive = Common::make_unique<DiskArchive>(fullpath); + auto archive = std::make_unique<DiskArchive>(fullpath); return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); } diff --git a/src/core/file_sys/disk_archive.cpp b/src/core/file_sys/disk_archive.cpp index 8e4ea01c5..489cc96fb 100644 --- a/src/core/file_sys/disk_archive.cpp +++ b/src/core/file_sys/disk_archive.cpp @@ -4,11 +4,11 @@ #include <algorithm> #include <cstdio> +#include <memory> #include "common/common_types.h" #include "common/file_util.h" #include "common/logging/log.h" -#include "common/make_unique.h" #include "core/file_sys/disk_archive.h" @@ -19,7 +19,7 @@ namespace FileSys { ResultVal<std::unique_ptr<FileBackend>> DiskArchive::OpenFile(const Path& path, const Mode mode) const { LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex); - auto file = Common::make_unique<DiskFile>(*this, path, mode); + auto file = std::make_unique<DiskFile>(*this, path, mode); ResultCode result = file->Open(); if (result.IsError()) return result; @@ -83,7 +83,7 @@ bool DiskArchive::RenameDirectory(const Path& src_path, const Path& dest_path) c std::unique_ptr<DirectoryBackend> DiskArchive::OpenDirectory(const Path& path) const { LOG_DEBUG(Service_FS, "called path=%s", path.DebugStr().c_str()); - auto directory = Common::make_unique<DiskDirectory>(*this, path); + auto directory = std::make_unique<DiskDirectory>(*this, path); if (!directory->Open()) return nullptr; return std::move(directory); @@ -132,7 +132,7 @@ ResultCode DiskFile::Open() { // Open the file in binary mode, to avoid problems with CR/LF on Windows systems mode_string += "b"; - file = Common::make_unique<FileUtil::IOFile>(path, mode_string.c_str()); + file = std::make_unique<FileUtil::IOFile>(path, mode_string.c_str()); if (file->IsOpen()) return RESULT_SUCCESS; return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, ErrorSummary::NotFound, ErrorLevel::Status); diff --git a/src/core/file_sys/ivfc_archive.cpp b/src/core/file_sys/ivfc_archive.cpp index a8e9a72ef..c61791ef7 100644 --- a/src/core/file_sys/ivfc_archive.cpp +++ b/src/core/file_sys/ivfc_archive.cpp @@ -7,7 +7,6 @@ #include "common/common_types.h" #include "common/logging/log.h" -#include "common/make_unique.h" #include "core/file_sys/ivfc_archive.h" @@ -21,7 +20,7 @@ std::string IVFCArchive::GetName() const { } ResultVal<std::unique_ptr<FileBackend>> IVFCArchive::OpenFile(const Path& path, const Mode mode) const { - return MakeResult<std::unique_ptr<FileBackend>>(Common::make_unique<IVFCFile>(romfs_file, data_offset, data_size)); + return MakeResult<std::unique_ptr<FileBackend>>(std::make_unique<IVFCFile>(romfs_file, data_offset, data_size)); } ResultCode IVFCArchive::DeleteFile(const Path& path) const { @@ -58,7 +57,7 @@ bool IVFCArchive::RenameDirectory(const Path& src_path, const Path& dest_path) c } std::unique_ptr<DirectoryBackend> IVFCArchive::OpenDirectory(const Path& path) const { - return Common::make_unique<IVFCDirectory>(); + return std::make_unique<IVFCDirectory>(); } u64 IVFCArchive::GetFreeBytes() const { diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index 3a2445241..c1a7ec5bf 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp @@ -60,6 +60,59 @@ const u32 R15_REGISTER = 15; const u32 CPSR_REGISTER = 25; const u32 FPSCR_REGISTER = 58; +// For sample XML files see the GDB source /gdb/features +// GDB also wants the l character at the start +// This XML defines what the registers are for this specific ARM device +static const char* target_xml = +R"(l<?xml version="1.0"?> +<!DOCTYPE target SYSTEM "gdb-target.dtd"> +<target version="1.0"> + <feature name="org.gnu.gdb.arm.core"> + <reg name="r0" bitsize="32"/> + <reg name="r1" bitsize="32"/> + <reg name="r2" bitsize="32"/> + <reg name="r3" bitsize="32"/> + <reg name="r4" bitsize="32"/> + <reg name="r5" bitsize="32"/> + <reg name="r6" bitsize="32"/> + <reg name="r7" bitsize="32"/> + <reg name="r8" bitsize="32"/> + <reg name="r9" bitsize="32"/> + <reg name="r10" bitsize="32"/> + <reg name="r11" bitsize="32"/> + <reg name="r12" bitsize="32"/> + <reg name="sp" bitsize="32" type="data_ptr"/> + <reg name="lr" bitsize="32"/> + <reg name="pc" bitsize="32" type="code_ptr"/> + + <!-- The CPSR is register 25, rather than register 16, because + the FPA registers historically were placed between the PC + and the CPSR in the "g" packet. --> + + <reg name="cpsr" bitsize="32" regnum="25"/> + </feature> + <feature name="org.gnu.gdb.arm.vfp"> + <reg name="d0" bitsize="64" type="float"/> + <reg name="d1" bitsize="64" type="float"/> + <reg name="d2" bitsize="64" type="float"/> + <reg name="d3" bitsize="64" type="float"/> + <reg name="d4" bitsize="64" type="float"/> + <reg name="d5" bitsize="64" type="float"/> + <reg name="d6" bitsize="64" type="float"/> + <reg name="d7" bitsize="64" type="float"/> + <reg name="d8" bitsize="64" type="float"/> + <reg name="d9" bitsize="64" type="float"/> + <reg name="d10" bitsize="64" type="float"/> + <reg name="d11" bitsize="64" type="float"/> + <reg name="d12" bitsize="64" type="float"/> + <reg name="d13" bitsize="64" type="float"/> + <reg name="d14" bitsize="64" type="float"/> + <reg name="d15" bitsize="64" type="float"/> + <reg name="fpscr" bitsize="32" type="int" group="float"/> + </feature> +</target> +)"; + namespace GDBStub { static int gdbserver_socket = -1; @@ -211,7 +264,7 @@ static u8 ReadByte() { } /// Calculate the checksum of the current command buffer. -static u8 CalculateChecksum(u8 *buffer, u32 length) { +static u8 CalculateChecksum(u8* buffer, u32 length) { return static_cast<u8>(std::accumulate(buffer, buffer + length, 0, std::plus<u8>())); } @@ -353,8 +406,15 @@ static void SendReply(const char* reply) { static void HandleQuery() { LOG_DEBUG(Debug_GDBStub, "gdb: query '%s'\n", command_buffer + 1); - if (!strcmp(reinterpret_cast<const char*>(command_buffer + 1), "TStatus")) { + const char* query = reinterpret_cast<const char*>(command_buffer + 1); + + if (strcmp(query, "TStatus") == 0 ) { SendReply("T0"); + } else if (strncmp(query, "Supported:", strlen("Supported:")) == 0) { + // PacketSize needs to be large enough for target xml + SendReply("PacketSize=800;qXfer:features:read+"); + } else if (strncmp(query, "Xfer:features:read:target.xml:", strlen("Xfer:features:read:target.xml:")) == 0) { + SendReply(target_xml); } else { SendReply(""); } @@ -491,29 +551,25 @@ static void ReadRegisters() { memset(buffer, 0, sizeof(buffer)); u8* bufptr = buffer; - for (int i = 0, reg = 0; reg <= FPSCR_REGISTER; i++, reg++) { - if (reg <= R15_REGISTER) { - IntToGdbHex(bufptr + i * CHAR_BIT, Core::g_app_core->GetReg(reg)); - } else if (reg == CPSR_REGISTER) { - IntToGdbHex(bufptr + i * CHAR_BIT, Core::g_app_core->GetCPSR()); - } else if (reg == CPSR_REGISTER - 1) { - // Dummy FPA register, ignore - IntToGdbHex(bufptr + i * CHAR_BIT, 0); - } else if (reg < CPSR_REGISTER) { - // Dummy FPA registers, ignore - IntToGdbHex(bufptr + i * CHAR_BIT, 0); - IntToGdbHex(bufptr + (i + 1) * CHAR_BIT, 0); - IntToGdbHex(bufptr + (i + 2) * CHAR_BIT, 0); - i += 2; - } else if (reg > CPSR_REGISTER && reg < FPSCR_REGISTER) { - IntToGdbHex(bufptr + i * CHAR_BIT, Core::g_app_core->GetVFPReg(reg - CPSR_REGISTER - 1)); - IntToGdbHex(bufptr + (i + 1) * CHAR_BIT, 0); - i++; - } else if (reg == FPSCR_REGISTER) { - IntToGdbHex(bufptr + i * CHAR_BIT, Core::g_app_core->GetVFPSystemReg(VFP_FPSCR)); - } + + for (int reg = 0; reg <= R15_REGISTER; reg++) { + IntToGdbHex(bufptr + reg * CHAR_BIT, Core::g_app_core->GetReg(reg)); } + bufptr += (16 * CHAR_BIT); + + IntToGdbHex(bufptr, Core::g_app_core->GetCPSR()); + + bufptr += CHAR_BIT; + + for (int reg = 0; reg <= 31; reg++) { + IntToGdbHex(bufptr + reg * CHAR_BIT, Core::g_app_core->GetVFPReg(reg)); + } + + bufptr += (32 * CHAR_BIT); + + IntToGdbHex(bufptr, Core::g_app_core->GetVFPSystemReg(VFP_FPSCR)); + SendReply(reinterpret_cast<char*>(buffer)); } @@ -885,6 +941,12 @@ void Init(u16 port) { LOG_ERROR(Debug_GDBStub, "Failed to create gdb socket"); } + // Set socket to SO_REUSEADDR so it can always bind on the same port + int reuse_enabled = 1; + if (setsockopt(tmpsock, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse_enabled, sizeof(reuse_enabled)) < 0) { + LOG_ERROR(Debug_GDBStub, "Failed to set gdb socket option"); + } + const sockaddr* server_addr = reinterpret_cast<const sockaddr*>(&saddr_server); socklen_t server_addrlen = sizeof(saddr_server); if (bind(tmpsock, server_addr, server_addrlen) < 0) { diff --git a/src/core/hle/config_mem.cpp b/src/core/hle/config_mem.cpp index b1a72dc0c..ccd73cfcb 100644 --- a/src/core/hle/config_mem.cpp +++ b/src/core/hle/config_mem.cpp @@ -3,13 +3,6 @@ // Refer to the license.txt file included. #include <cstring> - -#include "common/assert.h" -#include "common/common_types.h" -#include "common/common_funcs.h" - -#include "core/core.h" -#include "core/memory.h" #include "core/hle/config_mem.h" //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/hle/hle.cpp b/src/core/hle/hle.cpp index 331b1b22a..e545de3b5 100644 --- a/src/core/hle/hle.cpp +++ b/src/core/hle/hle.cpp @@ -8,8 +8,6 @@ #include "core/arm/arm_interface.h" #include "core/core.h" #include "core/hle/hle.h" -#include "core/hle/config_mem.h" -#include "core/hle/shared_page.h" #include "core/hle/service/service.h" //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 24b266eae..0546f6e16 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -2,10 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <memory> + #include "common/assert.h" #include "common/common_funcs.h" #include "common/logging/log.h" -#include "common/make_unique.h" #include "core/hle/kernel/memory.h" #include "core/hle/kernel/process.h" diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 0cb76ba1c..2d22652d9 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -24,6 +24,7 @@ enum class ErrorDescription : u32 { FS_InvalidOpenFlags = 230, FS_NotAFile = 250, FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive + OutofRangeOrMisalignedAddress = 513, // TODO(purpasmart): Check if this name fits its actual usage FS_InvalidPath = 702, InvalidSection = 1000, TooLarge = 1001, diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 06be9940e..9591522e5 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cinttypes> + #include "common/logging/log.h" #include "core/hle/service/service.h" @@ -9,30 +11,119 @@ #include "core/hle/service/am/am_app.h" #include "core/hle/service/am/am_net.h" #include "core/hle/service/am/am_sys.h" +#include "core/hle/service/am/am_u.h" namespace Service { namespace AM { -void TitleIDListGetTotal(Service::Interface* self) { +static std::array<u32, 3> am_content_count = { 0, 0, 0 }; +static std::array<u32, 3> am_titles_count = { 0, 0, 0 }; +static std::array<u32, 3> am_titles_list_count = { 0, 0, 0 }; +static u32 am_ticket_count = 0; +static u32 am_ticket_list_count = 0; + +void GetTitleCount(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 media_type = cmd_buff[1] & 0xFF; cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = 0; + cmd_buff[2] = am_titles_count[media_type]; + LOG_WARNING(Service_AM, "(STUBBED) media_type=%u, title_count=0x%08x", media_type, am_titles_count[media_type]); +} + +void FindContentInfos(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u32 media_type = cmd_buff[1] & 0xFF; + u64 title_id = (static_cast<u64>(cmd_buff[3]) << 32) | cmd_buff[2]; + u32 content_ids_pointer = cmd_buff[6]; + u32 content_info_pointer = cmd_buff[8]; + + am_content_count[media_type] = cmd_buff[4]; - LOG_WARNING(Service_AM, "(STUBBED) media_type %u", media_type); + cmd_buff[1] = RESULT_SUCCESS.raw; + LOG_WARNING(Service_AM, "(STUBBED) media_type=%u, title_id=0x%016lx, content_cound=%u, content_ids_pointer=0x%08x, content_info_pointer=0x%08x", + media_type, title_id, am_content_count[media_type], content_ids_pointer, content_info_pointer); } -void GetTitleIDList(Service::Interface* self) { +void ListContentInfos(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 num_titles = cmd_buff[1]; + u32 media_type = cmd_buff[2] & 0xFF; - u32 addr = cmd_buff[4]; + u64 title_id = (static_cast<u64>(cmd_buff[4]) << 32) | cmd_buff[3]; + u32 start_index = cmd_buff[5]; + u32 content_info_pointer = cmd_buff[7]; + + am_content_count[media_type] = cmd_buff[1]; cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = 0; + cmd_buff[2] = am_content_count[media_type]; + LOG_WARNING(Service_AM, "(STUBBED) media_type=%u, content_count=%u, title_id=0x%016" PRIx64 ", start_index=0x%08x, content_info_pointer=0x%08X", + media_type, am_content_count[media_type], title_id, start_index, content_info_pointer); +} + +void DeleteContents(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u32 media_type = cmd_buff[1] & 0xFF; + u64 title_id = (static_cast<u64>(cmd_buff[3]) << 32) | cmd_buff[2]; + u32 content_ids_pointer = cmd_buff[6]; - LOG_WARNING(Service_AM, "(STUBBED) Requested %u titles from media type %u. Address=0x%08X", num_titles, media_type, addr); + am_content_count[media_type] = cmd_buff[4]; + + cmd_buff[1] = RESULT_SUCCESS.raw; + LOG_WARNING(Service_AM, "(STUBBED) media_type=%u, title_id=0x%016" PRIx64 ", content_count=%u, content_ids_pointer=0x%08x", + media_type, title_id, am_content_count[media_type], content_ids_pointer); +} + +void GetTitleList(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u32 media_type = cmd_buff[2] & 0xFF; + u32 title_ids_output_pointer = cmd_buff[4]; + + am_titles_list_count[media_type] = cmd_buff[1]; + + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = am_titles_list_count[media_type]; + LOG_WARNING(Service_AM, "(STUBBED) media_type=%u, titles_list_count=0x%08X, title_ids_output_pointer=0x%08X", + media_type, am_titles_list_count[media_type], title_ids_output_pointer); +} + +void GetTitleInfo(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u32 media_type = cmd_buff[1] & 0xFF; + u32 title_id_list_pointer = cmd_buff[4]; + u32 title_list_pointer = cmd_buff[6]; + + am_titles_count[media_type] = cmd_buff[2]; + + cmd_buff[1] = RESULT_SUCCESS.raw; + LOG_WARNING(Service_AM, "(STUBBED) media_type=%u, total_titles=0x%08X, title_id_list_pointer=0x%08X, title_list_pointer=0x%08X", + media_type, am_titles_count[media_type], title_id_list_pointer, title_list_pointer); +} + +void GetDataTitleInfos(Service::Interface* self) { + GetTitleInfo(self); + + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + +void ListDataTitleTicketInfos(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u64 title_id = (static_cast<u64>(cmd_buff[3]) << 32) | cmd_buff[2]; + u32 start_index = cmd_buff[4]; + u32 ticket_info_pointer = cmd_buff[6]; + + am_ticket_count = cmd_buff[1]; + + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = am_ticket_count; + LOG_WARNING(Service_AM, "(STUBBED) ticket_count=0x%08X, title_id=0x%016" PRIx64 ", start_index=0x%08X, ticket_info_pointer=0x%08X", + am_ticket_count, title_id, start_index, ticket_info_pointer); } void GetNumContentInfos(Service::Interface* self) { @@ -40,16 +131,47 @@ void GetNumContentInfos(Service::Interface* self) { cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = 1; // Number of content infos plus one - LOG_WARNING(Service_AM, "(STUBBED) called"); } +void DeleteTicket(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u64 title_id = (static_cast<u64>(cmd_buff[2]) << 32) | cmd_buff[1]; + + cmd_buff[1] = RESULT_SUCCESS.raw; + LOG_WARNING(Service_AM, "(STUBBED) called title_id=0x%016" PRIx64 "",title_id); +} + +void GetTicketCount(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = am_ticket_count; + LOG_WARNING(Service_AM, "(STUBBED) called ticket_count=0x%08x",am_ticket_count); +} + +void GetTicketList(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u32 num_of_skip = cmd_buff[2]; + u32 ticket_list_pointer = cmd_buff[4]; + + am_ticket_list_count = cmd_buff[1]; + + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = am_ticket_list_count; + LOG_WARNING(Service_AM, "(STUBBED) ticket_list_count=0x%08x, num_of_skip=0x%08x, ticket_list_pointer=0x%08x", + am_ticket_list_count, num_of_skip, ticket_list_pointer); +} + void Init() { using namespace Kernel; AddService(new AM_APP_Interface); AddService(new AM_NET_Interface); AddService(new AM_SYS_Interface); + AddService(new AM_U_Interface); } void Shutdown() { diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 15e63bc7b..5676cdd5f 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -11,7 +11,7 @@ class Interface; namespace AM { /** - * AM::TitleIDListGetTotal service function + * AM::GetTitleCount service function * Gets the number of installed titles in the requested media type * Inputs: * 0 : Command header (0x00010040) @@ -20,36 +20,140 @@ namespace AM { * 1 : Result, 0 on success, otherwise error code * 2 : The number of titles in the requested media type */ -void TitleIDListGetTotal(Service::Interface* self); +void GetTitleCount(Service::Interface* self); /** - * AM::GetTitleIDList service function + * AM::FindContentInfos service function + * Inputs: + * 1 : MediaType + * 2-3 : u64, Title ID + * 4 : Content count + * 6 : Content IDs pointer + * 8 : Content Infos pointer + * Outputs: + * 1 : Result, 0 on success, otherwise error code + */ +void FindContentInfos(Service::Interface* self); + +/** + * AM::ListContentInfos service function + * Inputs: + * 1 : Content count + * 2 : MediaType + * 3-4 : u64, Title ID + * 5 : Start Index + * 7 : Content Infos pointer + * Outputs: + * 1 : Result, 0 on success, otherwise error code + * 2 : Number of content infos returned + */ +void ListContentInfos(Service::Interface* self); + +/** + * AM::DeleteContents service function + * Inputs: + * 1 : MediaType + * 2-3 : u64, Title ID + * 4 : Content count + * 6 : Content IDs pointer + * Outputs: + * 1 : Result, 0 on success, otherwise error code + */ +void DeleteContents(Service::Interface* self); + +/** + * AM::GetTitleList service function * Loads information about the desired number of titles from the desired media type into an array * Inputs: - * 0 : Command header (0x00020082) - * 1 : The maximum number of titles to load + * 1 : Title count * 2 : Media type to load the titles from - * 3 : Descriptor of the output buffer pointer - * 4 : Address of the output buffer + * 4 : Title IDs output pointer * Outputs: * 1 : Result, 0 on success, otherwise error code * 2 : The number of titles loaded from the requested media type */ -void GetTitleIDList(Service::Interface* self); +void GetTitleList(Service::Interface* self); + +/** + * AM::GetTitleInfo service function + * Inputs: + * 1 : u8 Mediatype + * 2 : Total titles + * 4 : TitleIDList pointer + * 6 : TitleList pointer + * Outputs: + * 1 : Result, 0 on success, otherwise error code + */ +void GetTitleInfo(Service::Interface* self); + +/** + * AM::GetDataTitleInfos service function + * Wrapper for AM::GetTitleInfo + * Inputs: + * 1 : u8 Mediatype + * 2 : Total titles + * 4 : TitleIDList pointer + * 6 : TitleList pointer + * Outputs: + * 1 : Result, 0 on success, otherwise error code + */ +void GetDataTitleInfos(Service::Interface* self); + +/** + * AM::ListDataTitleTicketInfos service function + * Inputs: + * 1 : Ticket count + * 2-3 : u64, Title ID + * 4 : Start Index? + * 5 : (TicketCount * 24) << 8 | 0x4 + * 6 : Ticket Infos pointer + * Outputs: + * 1 : Result, 0 on success, otherwise error code + * 2 : Number of ticket infos returned + */ +void ListDataTitleTicketInfos(Service::Interface* self); /** * AM::GetNumContentInfos service function * Inputs: * 0 : Command header (0x100100C0) - * 1 : Unknown - * 2 : Unknown - * 3 : Unknown + * 1 : MediaType + * 2-3 : u64, Title ID * Outputs: * 1 : Result, 0 on success, otherwise error code * 2 : Number of content infos plus one */ void GetNumContentInfos(Service::Interface* self); +/** + * AM::DeleteTicket service function + * Inputs: + * 1-2 : u64, Title ID + * Outputs: + * 1 : Result, 0 on success, otherwise error code + */ +void DeleteTicket(Service::Interface* self); + +/** + * AM::GetTicketCount service function + * Outputs: + * 1 : Result, 0 on success, otherwise error code + * 2 : Total titles + */ +void GetTicketCount(Service::Interface* self); + +/** + * AM::GetTicketList service function + * Inputs: + * 1 : Number of TicketList + * 2 : Number to skip + * 4 : TicketList pointer + * Outputs: + * 1 : Result, 0 on success, otherwise error code + * 2 : Total TicketList + */ +void GetTicketList(Service::Interface* self); + /// Initialize AM service void Init(); diff --git a/src/core/hle/service/am/am_app.cpp b/src/core/hle/service/am/am_app.cpp index 16c76a1eb..d27b3defd 100644 --- a/src/core/hle/service/am/am_app.cpp +++ b/src/core/hle/service/am/am_app.cpp @@ -9,14 +9,14 @@ namespace Service { namespace AM { const Interface::FunctionInfo FunctionTable[] = { - {0x100100C0, GetNumContentInfos, "GetNumContentInfos"}, - {0x10020104, nullptr, "FindContentInfos"}, - {0x10030142, nullptr, "ListContentInfos"}, - {0x10040102, nullptr, "DeleteContents"}, - {0x10050084, nullptr, "GetDataTitleInfos"}, - {0x10070102, nullptr, "ListDataTitleTicketInfos"}, - {0x100900C0, nullptr, "IsDataTitleInUse"}, - {0x100A0000, nullptr, "IsExternalTitleDatabaseInitialized"}, + {0x100100C0, GetNumContentInfos, "GetNumContentInfos"}, + {0x10020104, FindContentInfos, "FindContentInfos"}, + {0x10030142, ListContentInfos, "ListContentInfos"}, + {0x10040102, DeleteContents, "DeleteContents"}, + {0x10050084, GetDataTitleInfos, "GetDataTitleInfos"}, + {0x10070102, ListDataTitleTicketInfos, "ListDataTitleTicketInfos"}, + {0x100900C0, nullptr, "IsDataTitleInUse"}, + {0x100A0000, nullptr, "IsExternalTitleDatabaseInitialized"}, }; AM_APP_Interface::AM_APP_Interface() { diff --git a/src/core/hle/service/am/am_net.cpp b/src/core/hle/service/am/am_net.cpp index 065e04118..e75755245 100644 --- a/src/core/hle/service/am/am_net.cpp +++ b/src/core/hle/service/am/am_net.cpp @@ -9,16 +9,20 @@ namespace Service { namespace AM { const Interface::FunctionInfo FunctionTable[] = { - {0x00010040, TitleIDListGetTotal, "TitleIDListGetTotal"}, - {0x00020082, GetTitleIDList, "GetTitleIDList"}, - {0x00030084, nullptr, "ListTitles"}, + {0x00010040, GetTitleCount, "GetTitleCount"}, + {0x00020082, GetTitleList, "GetTitleList"}, + {0x00030084, GetTitleInfo, "GetTitleInfo"}, {0x000400C0, nullptr, "DeleteApplicationTitle"}, {0x000500C0, nullptr, "GetTitleProductCode"}, - {0x00080000, nullptr, "TitleIDListGetTotal3"}, - {0x00090082, nullptr, "GetTitleIDList3"}, + {0x000600C0, nullptr, "GetTitleExtDataId"}, + {0x00070080, DeleteTicket, "DeleteTicket"}, + {0x00080000, GetTicketCount, "GetTicketCount"}, + {0x00090082, GetTicketList, "GetTicketList"}, {0x000A0000, nullptr, "GetDeviceID"}, - {0x000D0084, nullptr, "ListTitles2"}, - {0x00140040, nullptr, "FinishInstallToMedia"}, + {0x000D0084, nullptr, "GetPendingTitleInfo"}, + {0x000E00C0, nullptr, "DeletePendingTitle"}, + {0x00140040, nullptr, "FinalizePendingTitles"}, + {0x00150040, nullptr, "DeleteAllPendingTitles"}, {0x00180080, nullptr, "InitializeTitleDatabase"}, {0x00190040, nullptr, "ReloadDBS"}, {0x001A00C0, nullptr, "GetDSiWareExportSize"}, diff --git a/src/core/hle/service/am/am_sys.cpp b/src/core/hle/service/am/am_sys.cpp index e38812297..8bad5e1c9 100644 --- a/src/core/hle/service/am/am_sys.cpp +++ b/src/core/hle/service/am/am_sys.cpp @@ -9,23 +9,27 @@ namespace Service { namespace AM { const Interface::FunctionInfo FunctionTable[] = { - {0x00010040, TitleIDListGetTotal, "TitleIDListGetTotal"}, - {0x00020082, GetTitleIDList, "GetTitleIDList"}, - {0x00030084, nullptr, "ListTitles"}, + {0x00010040, GetTitleCount, "GetTitleCount"}, + {0x00020082, GetTitleList, "GetTitleList"}, + {0x00030084, GetTitleInfo, "GetTitleInfo"}, {0x000400C0, nullptr, "DeleteApplicationTitle"}, {0x000500C0, nullptr, "GetTitleProductCode"}, - {0x00080000, nullptr, "TitleIDListGetTotal3"}, - {0x00090082, nullptr, "GetTitleIDList3"}, + {0x000600C0, nullptr, "GetTitleExtDataId"}, + {0x00070080, DeleteTicket, "DeleteTicket"}, + {0x00080000, GetTicketCount, "GetTicketCount"}, + {0x00090082, GetTicketList, "GetTicketList"}, {0x000A0000, nullptr, "GetDeviceID"}, - {0x000D0084, nullptr, "ListTitles2"}, - {0x00140040, nullptr, "FinishInstallToMedia"}, + {0x000D0084, nullptr, "GetPendingTitleInfo"}, + {0x000E00C0, nullptr, "DeletePendingTitle"}, + {0x00140040, nullptr, "FinalizePendingTitles"}, + {0x00150040, nullptr, "DeleteAllPendingTitles"}, {0x00180080, nullptr, "InitializeTitleDatabase"}, {0x00190040, nullptr, "ReloadDBS"}, {0x001A00C0, nullptr, "GetDSiWareExportSize"}, {0x001B0144, nullptr, "ExportDSiWare"}, {0x001C0084, nullptr, "ImportDSiWare"}, - {0x00230080, nullptr, "TitleIDListGetTotal2"}, - {0x002400C2, nullptr, "GetTitleIDList2"} + {0x00230080, nullptr, "GetPendingTitleCount"}, + {0x002400C2, nullptr, "GetPendingTitleList"} }; AM_SYS_Interface::AM_SYS_Interface() { diff --git a/src/core/hle/service/am/am_u.cpp b/src/core/hle/service/am/am_u.cpp index c0392b754..d583dd9e6 100644 --- a/src/core/hle/service/am/am_u.cpp +++ b/src/core/hle/service/am/am_u.cpp @@ -9,16 +9,20 @@ namespace Service { namespace AM { const Interface::FunctionInfo FunctionTable[] = { - {0x00010040, TitleIDListGetTotal, "TitleIDListGetTotal"}, - {0x00020082, GetTitleIDList, "GetTitleIDList"}, - {0x00030084, nullptr, "ListTitles"}, + {0x00010040, GetTitleCount, "GetTitleCount"}, + {0x00020082, GetTitleList, "GetTitleList"}, + {0x00030084, GetTitleInfo, "GetTitleInfo"}, {0x000400C0, nullptr, "DeleteApplicationTitle"}, {0x000500C0, nullptr, "GetTitleProductCode"}, - {0x00080000, nullptr, "TitleIDListGetTotal3"}, - {0x00090082, nullptr, "GetTitleIDList3"}, + {0x000600C0, nullptr, "GetTitleExtDataId"}, + {0x00070080, DeleteTicket, "DeleteTicket"}, + {0x00080000, GetTicketCount, "GetTicketCount"}, + {0x00090082, GetTicketList, "GetTicketList"}, {0x000A0000, nullptr, "GetDeviceID"}, - {0x000D0084, nullptr, "ListTitles2"}, - {0x00140040, nullptr, "FinishInstallToMedia"}, + {0x000D0084, nullptr, "GetPendingTitleInfo"}, + {0x000E00C0, nullptr, "DeletePendingTitle"}, + {0x00140040, nullptr, "FinalizePendingTitles"}, + {0x00150040, nullptr, "DeleteAllPendingTitles"}, {0x00180080, nullptr, "InitializeTitleDatabase"}, {0x00190040, nullptr, "ReloadDBS"}, {0x001A00C0, nullptr, "GetDSiWareExportSize"}, diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index a49365287..6d72e8188 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -397,6 +397,23 @@ void GetAppletInfo(Service::Interface* self) { LOG_WARNING(Service_APT, "(stubbed) called appid=%u", app_id); } +void GetStartupArgument(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 parameter_size = cmd_buff[1]; + StartupArgumentType startup_argument_type = static_cast<StartupArgumentType>(cmd_buff[2]); + + if (parameter_size >= 0x300) { + LOG_ERROR(Service_APT, "Parameter size is outside the valid range (capped to 0x300): parameter_size=0x%08x", parameter_size); + return; + } + + LOG_WARNING(Service_APT,"(stubbed) called startup_argument_type=%u , parameter_size=0x%08x , parameter_value=0x%08x", + startup_argument_type, parameter_size, Memory::Read32(cmd_buff[41])); + + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = (parameter_size > 0) ? 1 : 0; +} + void Init() { AddService(new APT_A_Interface); AddService(new APT_S_Interface); diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h index 47a97c1a1..668b4a66f 100644 --- a/src/core/hle/service/apt/apt.h +++ b/src/core/hle/service/apt/apt.h @@ -67,6 +67,12 @@ enum class AppletId : u32 { Ed2 = 0x402, }; +enum class StartupArgumentType : u32 { + OtherApp = 0, + Restart = 1, + OtherMedia = 2, +}; + /// Send a parameter to the currently-running application, which will read it via ReceiveParameter void SendParameter(const MessageParameter& parameter); @@ -344,6 +350,17 @@ void PreloadLibraryApplet(Service::Interface* self); */ void StartLibraryApplet(Service::Interface* self); +/** + * APT::GetStartupArgument service function + * Inputs: + * 1 : Parameter Size (capped to 0x300) + * 2 : StartupArgumentType + * Outputs: + * 0 : Return header + * 1 : u8, Exists (0 = does not exist, 1 = exists) + */ +void GetStartupArgument(Service::Interface* self); + /// Initialize the APT service void Init(); diff --git a/src/core/hle/service/apt/apt_a.cpp b/src/core/hle/service/apt/apt_a.cpp index 0c6a77305..9ff47701a 100644 --- a/src/core/hle/service/apt/apt_a.cpp +++ b/src/core/hle/service/apt/apt_a.cpp @@ -13,9 +13,10 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00020080, Initialize, "Initialize?"}, {0x00030040, Enable, "Enable?"}, {0x00040040, nullptr, "Finalize?"}, - {0x00050040, nullptr, "GetAppletManInfo?"}, - {0x00060040, nullptr, "GetAppletInfo?"}, + {0x00050040, GetAppletManInfo, "GetAppletManInfo"}, + {0x00060040, GetAppletInfo, "GetAppletInfo"}, {0x00090040, IsRegistered, "IsRegistered"}, + {0x000B0040, InquireNotification, "InquireNotification"}, {0x000C0104, SendParameter, "SendParameter"}, {0x000D0080, ReceiveParameter, "ReceiveParameter"}, {0x000E0080, GlanceParameter, "GlanceParameter"}, @@ -24,9 +25,13 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"}, {0x001E0084, StartLibraryApplet, "StartLibraryApplet"}, {0x003B0040, nullptr, "CancelLibraryApplet?"}, + {0x003E0080, nullptr, "ReplySleepQuery"}, {0x00430040, NotifyToWait, "NotifyToWait?"}, {0x00440000, GetSharedFont, "GetSharedFont?"}, {0x004B00C2, AppletUtility, "AppletUtility?"}, + {0x004F0080, SetAppCpuTimeLimit, "SetAppCpuTimeLimit"}, + {0x00500040, GetAppCpuTimeLimit, "GetAppCpuTimeLimit"}, + {0x00510080, GetStartupArgument, "GetStartupArgument"}, {0x00550040, nullptr, "WriteInputToNsState?"}, }; diff --git a/src/core/hle/service/apt/apt_s.cpp b/src/core/hle/service/apt/apt_s.cpp index 7f6e81a63..ca54e593c 100644 --- a/src/core/hle/service/apt/apt_s.cpp +++ b/src/core/hle/service/apt/apt_s.cpp @@ -13,8 +13,8 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00020080, Initialize, "Initialize"}, {0x00030040, Enable, "Enable"}, {0x00040040, nullptr, "Finalize"}, - {0x00050040, nullptr, "GetAppletManInfo"}, - {0x00060040, nullptr, "GetAppletInfo"}, + {0x00050040, GetAppletManInfo, "GetAppletManInfo"}, + {0x00060040, GetAppletInfo, "GetAppletInfo"}, {0x00070000, nullptr, "GetLastSignaledAppletId"}, {0x00080000, nullptr, "CountRegisteredApplet"}, {0x00090040, nullptr, "IsRegistered"}, @@ -87,9 +87,9 @@ const Interface::FunctionInfo FunctionTable[] = { {0x004C0000, nullptr, "SetFatalErrDispMode"}, {0x004D0080, nullptr, "GetAppletProgramInfo"}, {0x004E0000, nullptr, "HardwareResetAsync"}, - {0x004F0080, nullptr, "SetApplicationCpuTimeLimit"}, - {0x00500040, nullptr, "GetApplicationCpuTimeLimit"}, - {0x00510080, nullptr, "GetStartupArgument"}, + {0x004F0080, SetAppCpuTimeLimit, "SetAppCpuTimeLimit"}, + {0x00500040, GetAppCpuTimeLimit, "GetAppCpuTimeLimit"}, + {0x00510080, GetStartupArgument, "GetStartupArgument"}, {0x00520104, nullptr, "Wrap1"}, {0x00530104, nullptr, "Unwrap1"}, {0x00580002, nullptr, "GetProgramID"}, diff --git a/src/core/hle/service/apt/apt_u.cpp b/src/core/hle/service/apt/apt_u.cpp index b13b51549..0e85c6d08 100644 --- a/src/core/hle/service/apt/apt_u.cpp +++ b/src/core/hle/service/apt/apt_u.cpp @@ -89,7 +89,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x004E0000, nullptr, "HardwareResetAsync"}, {0x004F0080, SetAppCpuTimeLimit, "SetAppCpuTimeLimit"}, {0x00500040, GetAppCpuTimeLimit, "GetAppCpuTimeLimit"}, - {0x00510080, nullptr, "GetStartupArgument"}, + {0x00510080, GetStartupArgument, "GetStartupArgument"}, {0x00520104, nullptr, "Wrap1"}, {0x00530104, nullptr, "Unwrap1"}, {0x00580002, nullptr, "GetProgramID"}, diff --git a/src/core/hle/service/cecd/cecd.cpp b/src/core/hle/service/cecd/cecd.cpp index 6d79ce9b4..50c03495e 100644 --- a/src/core/hle/service/cecd/cecd.cpp +++ b/src/core/hle/service/cecd/cecd.cpp @@ -4,6 +4,7 @@ #include "common/logging/log.h" +#include "core/hle/kernel/event.h" #include "core/hle/service/service.h" #include "core/hle/service/cecd/cecd.h" #include "core/hle/service/cecd/cecd_s.h" @@ -12,14 +13,47 @@ namespace Service { namespace CECD { -void Init() { - using namespace Kernel; +static Kernel::SharedPtr<Kernel::Event> cecinfo_event; +static Kernel::SharedPtr<Kernel::Event> change_state_event; + +void GetCecStateAbbreviated(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[2] = static_cast<u32>(CecStateAbbreviated::CEC_STATE_ABBREV_IDLE); + + LOG_WARNING(Service_CECD, "(STUBBED) called"); +} + +void GetCecInfoEventHandle(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[3] = Kernel::g_handle_table.Create(cecinfo_event).MoveFrom(); // Event handle + + LOG_WARNING(Service_CECD, "(STUBBED) called"); +} + +void GetChangeStateEventHandle(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[3] = Kernel::g_handle_table.Create(change_state_event).MoveFrom(); // Event handle + + LOG_WARNING(Service_CECD, "(STUBBED) called"); +} + +void Init() { AddService(new CECD_S_Interface); AddService(new CECD_U_Interface); + + cecinfo_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "CECD_U::cecinfo_event"); + change_state_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "CECD_U::change_state_event"); } void Shutdown() { + cecinfo_event = nullptr; + change_state_event = nullptr; } } // namespace CECD diff --git a/src/core/hle/service/cecd/cecd.h b/src/core/hle/service/cecd/cecd.h index 9e158521b..435611363 100644 --- a/src/core/hle/service/cecd/cecd.h +++ b/src/core/hle/service/cecd/cecd.h @@ -5,8 +5,49 @@ #pragma once namespace Service { + +class Interface; + namespace CECD { +enum class CecStateAbbreviated { + CEC_STATE_ABBREV_IDLE = 1, ///< Corresponds to CEC_STATE_IDLE + CEC_STATE_ABBREV_NOT_LOCAL = 2, ///< Corresponds to CEC_STATEs *FINISH*, *POST, and OVER_BOSS + CEC_STATE_ABBREV_SCANNING = 3, ///< Corresponds to CEC_STATE_SCANNING + CEC_STATE_ABBREV_WLREADY = 4, ///< Corresponds to CEC_STATE_WIRELESS_READY when some unknown bool is true + CEC_STATE_ABBREV_OTHER = 5, ///< Corresponds to CEC_STATEs besides *FINISH*, *POST, and OVER_BOSS and those listed here +}; + +/** + * GetCecStateAbbreviated service function + * Inputs: + * 0: 0x000E0000 + * Outputs: + * 1: ResultCode + * 2: CecStateAbbreviated + */ +void GetCecStateAbbreviated(Service::Interface* self); + +/** + * GetCecInfoEventHandle service function + * Inputs: + * 0: 0x000F0000 + * Outputs: + * 1: ResultCode + * 3: Event Handle + */ +void GetCecInfoEventHandle(Service::Interface* self); + +/** + * GetChangeStateEventHandle service function + * Inputs: + * 0: 0x00100000 + * Outputs: + * 1: ResultCode + * 3: Event Handle + */ +void GetChangeStateEventHandle(Service::Interface* self); + /// Initialize CECD service(s) void Init(); diff --git a/src/core/hle/service/cecd/cecd_u.cpp b/src/core/hle/service/cecd/cecd_u.cpp index 9b720a738..be6d4d8f6 100644 --- a/src/core/hle/service/cecd/cecd_u.cpp +++ b/src/core/hle/service/cecd/cecd_u.cpp @@ -2,13 +2,17 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "core/hle/service/cecd/cecd.h" #include "core/hle/service/cecd/cecd_u.h" namespace Service { namespace CECD { static const Interface::FunctionInfo FunctionTable[] = { - { 0x00120104, nullptr, "ReadSavedData" }, + {0x000E0000, GetCecStateAbbreviated, "GetCecStateAbbreviated"}, + {0x000F0000, GetCecInfoEventHandle, "GetCecInfoEventHandle"}, + {0x00100000, GetChangeStateEventHandle, "GetChangeStateEventHandle"}, + {0x00120104, nullptr, "ReadSavedData"}, }; CECD_U_Interface::CECD_U_Interface() { diff --git a/src/core/hle/service/cfg/cfg_i.cpp b/src/core/hle/service/cfg/cfg_i.cpp index 0559a07b2..b18060f6d 100644 --- a/src/core/hle/service/cfg/cfg_i.cpp +++ b/src/core/hle/service/cfg/cfg_i.cpp @@ -9,6 +9,18 @@ namespace Service { namespace CFG { const Interface::FunctionInfo FunctionTable[] = { + // cfg common + {0x00010082, GetConfigInfoBlk2, "GetConfigInfoBlk2"}, + {0x00020000, SecureInfoGetRegion, "SecureInfoGetRegion"}, + {0x00030040, GenHashConsoleUnique, "GenHashConsoleUnique"}, + {0x00040000, GetRegionCanadaUSA, "GetRegionCanadaUSA"}, + {0x00050000, GetSystemModel, "GetSystemModel"}, + {0x00060000, GetModelNintendo2DS, "GetModelNintendo2DS"}, + {0x00070040, nullptr, "WriteToFirstByteCfgSavegame"}, + {0x00080080, nullptr, "GoThroughTable"}, + {0x00090040, GetCountryCodeString, "GetCountryCodeString"}, + {0x000A0040, GetCountryCodeID, "GetCountryCodeID"}, + // cfg:i {0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"}, {0x04020082, nullptr, "SetConfigInfoBlk4"}, {0x04030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"}, diff --git a/src/core/hle/service/cfg/cfg_s.cpp b/src/core/hle/service/cfg/cfg_s.cpp index b03d290e5..e001f7687 100644 --- a/src/core/hle/service/cfg/cfg_s.cpp +++ b/src/core/hle/service/cfg/cfg_s.cpp @@ -9,10 +9,18 @@ namespace Service { namespace CFG { const Interface::FunctionInfo FunctionTable[] = { + // cfg common {0x00010082, GetConfigInfoBlk2, "GetConfigInfoBlk2"}, {0x00020000, SecureInfoGetRegion, "SecureInfoGetRegion"}, {0x00030040, GenHashConsoleUnique, "GenHashConsoleUnique"}, + {0x00040000, GetRegionCanadaUSA, "GetRegionCanadaUSA"}, {0x00050000, GetSystemModel, "GetSystemModel"}, + {0x00060000, GetModelNintendo2DS, "GetModelNintendo2DS"}, + {0x00070040, nullptr, "WriteToFirstByteCfgSavegame"}, + {0x00080080, nullptr, "GoThroughTable"}, + {0x00090040, GetCountryCodeString, "GetCountryCodeString"}, + {0x000A0040, GetCountryCodeID, "GetCountryCodeID"}, + // cfg:s {0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"}, {0x04020082, nullptr, "SetConfigInfoBlk4"}, {0x04030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"}, diff --git a/src/core/hle/service/cfg/cfg_u.cpp b/src/core/hle/service/cfg/cfg_u.cpp index 89ae96c9e..606f7b2eb 100644 --- a/src/core/hle/service/cfg/cfg_u.cpp +++ b/src/core/hle/service/cfg/cfg_u.cpp @@ -9,6 +9,7 @@ namespace Service { namespace CFG { const Interface::FunctionInfo FunctionTable[] = { + // cfg common {0x00010082, GetConfigInfoBlk2, "GetConfigInfoBlk2"}, {0x00020000, SecureInfoGetRegion, "SecureInfoGetRegion"}, {0x00030040, GenHashConsoleUnique, "GenHashConsoleUnique"}, diff --git a/src/core/hle/service/dlp_srvr.cpp b/src/core/hle/service/dlp_srvr.cpp new file mode 100644 index 000000000..1f30188da --- /dev/null +++ b/src/core/hle/service/dlp_srvr.cpp @@ -0,0 +1,36 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/logging/log.h" +#include "core/hle/hle.h" +#include "core/hle/service/dlp_srvr.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace DLP_SRVR + +namespace DLP_SRVR { + +static void unk_0x000E0040(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = 0; + + LOG_WARNING(Service_DLP, "(STUBBED) called"); +} + +const Interface::FunctionInfo FunctionTable[] = { + {0x00010183, nullptr, "Initialize"}, + {0x00020000, nullptr, "Finalize"}, + {0x000E0040, unk_0x000E0040, "unk_0x000E0040"}, +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Interface class + +Interface::Interface() { + Register(FunctionTable); +} + +} // namespace diff --git a/src/core/hle/service/dlp_srvr.h b/src/core/hle/service/dlp_srvr.h new file mode 100644 index 000000000..d65d00814 --- /dev/null +++ b/src/core/hle/service/dlp_srvr.h @@ -0,0 +1,23 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace DLP_SRVR + +namespace DLP_SRVR { + +class Interface : public Service::Interface { +public: + Interface(); + + std::string GetPortName() const override { + return "dlp:SRVR"; + } +}; + +} // namespace diff --git a/src/core/hle/service/frd/frd.cpp b/src/core/hle/service/frd/frd.cpp index c13ffd9d2..15d604bb6 100644 --- a/src/core/hle/service/frd/frd.cpp +++ b/src/core/hle/service/frd/frd.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/string_util.h" + #include "core/hle/service/service.h" #include "core/hle/service/frd/frd.h" #include "core/hle/service/frd/frd_a.h" @@ -10,6 +12,95 @@ namespace Service { namespace FRD { +static FriendKey my_friend_key = {0, 0, 0ull}; +static MyPresence my_presence = {}; + +void GetMyPresence(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u32 shifted_out_size = cmd_buff[64]; + u32 my_presence_addr = cmd_buff[65]; + + ASSERT(shifted_out_size == ((sizeof(MyPresence) << 14) | 2)); + + Memory::WriteBlock(my_presence_addr, reinterpret_cast<const u8*>(&my_presence), sizeof(MyPresence)); + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + + LOG_WARNING(Service_FRD, "(STUBBED) called"); +} + +void GetFriendKeyList(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u32 unknown = cmd_buff[1]; + u32 frd_count = cmd_buff[2]; + u32 frd_key_addr = cmd_buff[65]; + + FriendKey zero_key = {}; + for (u32 i = 0; i < frd_count; ++i) { + Memory::WriteBlock(frd_key_addr + i * sizeof(FriendKey), + reinterpret_cast<const u8*>(&zero_key), sizeof(FriendKey)); + } + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[2] = 0; // 0 friends + LOG_WARNING(Service_FRD, "(STUBBED) called, unknown=%d, frd_count=%d, frd_key_addr=0x%08X", + unknown, frd_count, frd_key_addr); +} + +void GetFriendProfile(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u32 count = cmd_buff[1]; + u32 frd_key_addr = cmd_buff[3]; + u32 profiles_addr = cmd_buff[65]; + + Profile zero_profile = {}; + for (u32 i = 0; i < count; ++i) { + Memory::WriteBlock(profiles_addr + i * sizeof(Profile), + reinterpret_cast<const u8*>(&zero_profile), sizeof(Profile)); + } + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_FRD, "(STUBBED) called, count=%d, frd_key_addr=0x%08X, profiles_addr=0x%08X", + count, frd_key_addr, profiles_addr); +} + +void GetFriendAttributeFlags(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u32 count = cmd_buff[1]; + u32 frd_key_addr = cmd_buff[3]; + u32 attr_flags_addr = cmd_buff[65]; + + for (u32 i = 0; i < count; ++i) { + //TODO:(mailwl) figure out AttributeFlag size and zero all buffer. Assume 1 byte + Memory::Write8(attr_flags_addr + i, 0); + } + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_FRD, "(STUBBED) called, count=%d, frd_key_addr=0x%08X, attr_flags_addr=0x%08X", + count, frd_key_addr, attr_flags_addr); +} + +void GetMyFriendKey(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + Memory::WriteBlock(cmd_buff[2], reinterpret_cast<const u8*>(&my_friend_key), sizeof(FriendKey)); + LOG_WARNING(Service_FRD, "(STUBBED) called"); +} + +void GetMyScreenName(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + // TODO: (mailwl) get the name from config + Common::UTF8ToUTF16("Citra").copy(reinterpret_cast<char16_t*>(&cmd_buff[2]), 11); + LOG_WARNING(Service_FRD, "(STUBBED) called"); +} + void Init() { using namespace Kernel; diff --git a/src/core/hle/service/frd/frd.h b/src/core/hle/service/frd/frd.h index f9f88b444..c8283a7f3 100644 --- a/src/core/hle/service/frd/frd.h +++ b/src/core/hle/service/frd/frd.h @@ -4,9 +4,97 @@ #pragma once +#include "common/common_types.h" + namespace Service { + +class Interface; + namespace FRD { +struct FriendKey { + u32 friend_id; + u32 unknown; + u64 friend_code; +}; + +struct MyPresence { + u8 unknown[0x12C]; +}; + +struct Profile { + u8 region; + u8 country; + u8 area; + u8 language; + u32 unknown; +}; + +/** + * FRD::GetMyPresence service function + * Inputs: + * 64 : sizeof (MyPresence) << 14 | 2 + * 65 : Address of MyPresence structure + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +void GetMyPresence(Service::Interface* self); + +/** + * FRD::GetFriendKeyList service function + * Inputs: + * 1 : Unknown + * 2 : Max friends count + * 65 : Address of FriendKey List + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : FriendKey count filled + */ +void GetFriendKeyList(Service::Interface* self); + +/** + * FRD::GetFriendProfile service function + * Inputs: + * 1 : Friends count + * 2 : Friends count << 18 | 2 + * 3 : Address of FriendKey List + * 64 : (count * sizeof (Profile)) << 10 | 2 + * 65 : Address of Profiles List + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +void GetFriendProfile(Service::Interface* self); + +/** + * FRD::GetFriendAttributeFlags service function + * Inputs: + * 1 : Friends count + * 2 : Friends count << 18 | 2 + * 3 : Address of FriendKey List + * 65 : Address of AttributeFlags + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +void GetFriendAttributeFlags(Service::Interface* self); + +/** + * FRD::GetMyFriendKey service function + * Inputs: + * none + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2-5 : FriendKey + */ +void GetMyFriendKey(Service::Interface* self); + +/** + * FRD::GetMyScreenName service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : UTF16 encoded name (max 11 symbols) + */ +void GetMyScreenName(Service::Interface* self); + /// Initialize FRD service(s) void Init(); diff --git a/src/core/hle/service/frd/frd_u.cpp b/src/core/hle/service/frd/frd_u.cpp index 2c6885377..db8666416 100644 --- a/src/core/hle/service/frd/frd_u.cpp +++ b/src/core/hle/service/frd/frd_u.cpp @@ -2,65 +2,66 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "core/hle/service/frd/frd.h" #include "core/hle/service/frd/frd_u.h" namespace Service { namespace FRD { const Interface::FunctionInfo FunctionTable[] = { - {0x00010000, nullptr, "HasLoggedIn"}, - {0x00020000, nullptr, "IsOnline"}, - {0x00030000, nullptr, "Login"}, - {0x00040000, nullptr, "Logout"}, - {0x00050000, nullptr, "GetMyFriendKey"}, - {0x00060000, nullptr, "GetMyPreference"}, - {0x00070000, nullptr, "GetMyProfile"}, - {0x00080000, nullptr, "GetMyPresence"}, - {0x00090000, nullptr, "GetMyScreenName"}, - {0x000A0000, nullptr, "GetMyMii"}, - {0x000B0000, nullptr, "GetMyLocalAccountId"}, - {0x000C0000, nullptr, "GetMyPlayingGame"}, - {0x000D0000, nullptr, "GetMyFavoriteGame"}, - {0x000E0000, nullptr, "GetMyNcPrincipalId"}, - {0x000F0000, nullptr, "GetMyComment"}, - {0x00100040, nullptr, "GetMyPassword"}, - {0x00110080, nullptr, "GetFriendKeyList"}, - {0x00120042, nullptr, "GetFriendPresence"}, - {0x00130142, nullptr, "GetFriendScreenName"}, - {0x00140044, nullptr, "GetFriendMii"}, - {0x00150042, nullptr, "GetFriendProfile"}, - {0x00160042, nullptr, "GetFriendRelationship"}, - {0x00170042, nullptr, "GetFriendAttributeFlags"}, - {0x00180044, nullptr, "GetFriendPlayingGame"}, - {0x00190042, nullptr, "GetFriendFavoriteGame"}, - {0x001A00C4, nullptr, "GetFriendInfo"}, - {0x001B0080, nullptr, "IsIncludedInFriendList"}, - {0x001C0042, nullptr, "UnscrambleLocalFriendCode"}, - {0x001D0002, nullptr, "UpdateGameModeDescription"}, - {0x001E02C2, nullptr, "UpdateGameMode"}, - {0x001F0042, nullptr, "SendInvitation"}, - {0x00200002, nullptr, "AttachToEventNotification"}, - {0x00210040, nullptr, "SetNotificationMask"}, - {0x00220040, nullptr, "GetEventNotification"}, - {0x00230000, nullptr, "GetLastResponseResult"}, - {0x00240040, nullptr, "PrincipalIdToFriendCode"}, - {0x00250080, nullptr, "FriendCodeToPrincipalId"}, - {0x00260080, nullptr, "IsValidFriendCode"}, - {0x00270040, nullptr, "ResultToErrorCode"}, - {0x00280244, nullptr, "RequestGameAuthentication"}, - {0x00290000, nullptr, "GetGameAuthenticationData"}, - {0x002A0204, nullptr, "RequestServiceLocator"}, - {0x002B0000, nullptr, "GetServiceLocatorData"}, - {0x002C0002, nullptr, "DetectNatProperties"}, - {0x002D0000, nullptr, "GetNatProperties"}, - {0x002E0000, nullptr, "GetServerTimeInterval"}, - {0x002F0040, nullptr, "AllowHalfAwake"}, - {0x00300000, nullptr, "GetServerTypes"}, - {0x00310082, nullptr, "GetFriendComment"}, - {0x00320042, nullptr, "SetClientSdkVersion"}, - {0x00330000, nullptr, "GetMyApproachContext"}, - {0x00340046, nullptr, "AddFriendWithApproach"}, - {0x00350082, nullptr, "DecryptApproachContext"}, + {0x00010000, nullptr, "HasLoggedIn"}, + {0x00020000, nullptr, "IsOnline"}, + {0x00030000, nullptr, "Login"}, + {0x00040000, nullptr, "Logout"}, + {0x00050000, GetMyFriendKey, "GetMyFriendKey"}, + {0x00060000, nullptr, "GetMyPreference"}, + {0x00070000, nullptr, "GetMyProfile"}, + {0x00080000, GetMyPresence, "GetMyPresence"}, + {0x00090000, GetMyScreenName, "GetMyScreenName"}, + {0x000A0000, nullptr, "GetMyMii"}, + {0x000B0000, nullptr, "GetMyLocalAccountId"}, + {0x000C0000, nullptr, "GetMyPlayingGame"}, + {0x000D0000, nullptr, "GetMyFavoriteGame"}, + {0x000E0000, nullptr, "GetMyNcPrincipalId"}, + {0x000F0000, nullptr, "GetMyComment"}, + {0x00100040, nullptr, "GetMyPassword"}, + {0x00110080, GetFriendKeyList, "GetFriendKeyList"}, + {0x00120042, nullptr, "GetFriendPresence"}, + {0x00130142, nullptr, "GetFriendScreenName"}, + {0x00140044, nullptr, "GetFriendMii"}, + {0x00150042, GetFriendProfile, "GetFriendProfile"}, + {0x00160042, nullptr, "GetFriendRelationship"}, + {0x00170042, GetFriendAttributeFlags, "GetFriendAttributeFlags"}, + {0x00180044, nullptr, "GetFriendPlayingGame"}, + {0x00190042, nullptr, "GetFriendFavoriteGame"}, + {0x001A00C4, nullptr, "GetFriendInfo"}, + {0x001B0080, nullptr, "IsIncludedInFriendList"}, + {0x001C0042, nullptr, "UnscrambleLocalFriendCode"}, + {0x001D0002, nullptr, "UpdateGameModeDescription"}, + {0x001E02C2, nullptr, "UpdateGameMode"}, + {0x001F0042, nullptr, "SendInvitation"}, + {0x00200002, nullptr, "AttachToEventNotification"}, + {0x00210040, nullptr, "SetNotificationMask"}, + {0x00220040, nullptr, "GetEventNotification"}, + {0x00230000, nullptr, "GetLastResponseResult"}, + {0x00240040, nullptr, "PrincipalIdToFriendCode"}, + {0x00250080, nullptr, "FriendCodeToPrincipalId"}, + {0x00260080, nullptr, "IsValidFriendCode"}, + {0x00270040, nullptr, "ResultToErrorCode"}, + {0x00280244, nullptr, "RequestGameAuthentication"}, + {0x00290000, nullptr, "GetGameAuthenticationData"}, + {0x002A0204, nullptr, "RequestServiceLocator"}, + {0x002B0000, nullptr, "GetServiceLocatorData"}, + {0x002C0002, nullptr, "DetectNatProperties"}, + {0x002D0000, nullptr, "GetNatProperties"}, + {0x002E0000, nullptr, "GetServerTimeInterval"}, + {0x002F0040, nullptr, "AllowHalfAwake"}, + {0x00300000, nullptr, "GetServerTypes"}, + {0x00310082, nullptr, "GetFriendComment"}, + {0x00320042, nullptr, "SetClientSdkVersion"}, + {0x00330000, nullptr, "GetMyApproachContext"}, + {0x00340046, nullptr, "AddFriendWithApproach"}, + {0x00350082, nullptr, "DecryptApproachContext"}, }; FRD_U_Interface::FRD_U_Interface() { diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp index 590697e76..e9588cb72 100644 --- a/src/core/hle/service/fs/archive.cpp +++ b/src/core/hle/service/fs/archive.cpp @@ -15,7 +15,6 @@ #include "common/common_types.h" #include "common/file_util.h" #include "common/logging/log.h" -#include "common/make_unique.h" #include "core/file_sys/archive_backend.h" #include "core/file_sys/archive_extsavedata.h" @@ -521,23 +520,23 @@ void ArchiveInit() { std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX); std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX); - auto sdmc_factory = Common::make_unique<FileSys::ArchiveFactory_SDMC>(sdmc_directory); + auto sdmc_factory = std::make_unique<FileSys::ArchiveFactory_SDMC>(sdmc_directory); if (sdmc_factory->Initialize()) RegisterArchiveType(std::move(sdmc_factory), ArchiveIdCode::SDMC); else LOG_ERROR(Service_FS, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str()); // Create the SaveData archive - auto savedata_factory = Common::make_unique<FileSys::ArchiveFactory_SaveData>(sdmc_directory); + auto savedata_factory = std::make_unique<FileSys::ArchiveFactory_SaveData>(sdmc_directory); RegisterArchiveType(std::move(savedata_factory), ArchiveIdCode::SaveData); - auto extsavedata_factory = Common::make_unique<FileSys::ArchiveFactory_ExtSaveData>(sdmc_directory, false); + auto extsavedata_factory = std::make_unique<FileSys::ArchiveFactory_ExtSaveData>(sdmc_directory, false); if (extsavedata_factory->Initialize()) RegisterArchiveType(std::move(extsavedata_factory), ArchiveIdCode::ExtSaveData); else LOG_ERROR(Service_FS, "Can't instantiate ExtSaveData archive with path %s", extsavedata_factory->GetMountPoint().c_str()); - auto sharedextsavedata_factory = Common::make_unique<FileSys::ArchiveFactory_ExtSaveData>(nand_directory, true); + auto sharedextsavedata_factory = std::make_unique<FileSys::ArchiveFactory_ExtSaveData>(nand_directory, true); if (sharedextsavedata_factory->Initialize()) RegisterArchiveType(std::move(sharedextsavedata_factory), ArchiveIdCode::SharedExtSaveData); else @@ -545,10 +544,10 @@ void ArchiveInit() { sharedextsavedata_factory->GetMountPoint().c_str()); // Create the SaveDataCheck archive, basically a small variation of the RomFS archive - auto savedatacheck_factory = Common::make_unique<FileSys::ArchiveFactory_SaveDataCheck>(nand_directory); + auto savedatacheck_factory = std::make_unique<FileSys::ArchiveFactory_SaveDataCheck>(nand_directory); RegisterArchiveType(std::move(savedatacheck_factory), ArchiveIdCode::SaveDataCheck); - auto systemsavedata_factory = Common::make_unique<FileSys::ArchiveFactory_SystemSaveData>(nand_directory); + auto systemsavedata_factory = std::make_unique<FileSys::ArchiveFactory_SystemSaveData>(nand_directory); RegisterArchiveType(std::move(systemsavedata_factory), ArchiveIdCode::SystemSaveData); } diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp index 2ace2cade..0c655395e 100644 --- a/src/core/hle/service/gsp_gpu.cpp +++ b/src/core/hle/service/gsp_gpu.cpp @@ -31,6 +31,13 @@ const static u32 REGS_BEGIN = 0x1EB00000; namespace GSP_GPU { +const ResultCode ERR_GSP_REGS_OUTOFRANGE_OR_MISALIGNED(ErrorDescription::OutofRangeOrMisalignedAddress, ErrorModule::GX, + ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E02A01 +const ResultCode ERR_GSP_REGS_MISALIGNED(ErrorDescription::MisalignedSize, ErrorModule::GX, + ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E02BF2 +const ResultCode ERR_GSP_REGS_INVALID_SIZE(ErrorDescription::InvalidSize, ErrorModule::GX, + ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E02BEC + /// Event triggered when GSP interrupt has been signalled Kernel::SharedPtr<Kernel::Event> g_interrupt_event; /// GSP shared memoryings @@ -59,47 +66,87 @@ static inline InterruptRelayQueue* GetInterruptRelayQueue(u32 thread_id) { } /** - * Checks if the parameters in a register write call are valid and logs in the case that - * they are not - * @param base_address The first address in the sequence of registers that will be written - * @param size_in_bytes The number of registers that will be written - * @return true if the parameters are valid, false otherwise + * Writes sequential GSP GPU hardware registers using an array of source data + * + * @param base_address The address of the first register in the sequence + * @param size_in_bytes The number of registers to update (size of data) + * @param data A pointer to the source data + * @return RESULT_SUCCESS if the parameters are valid, error code otherwise */ -static bool CheckWriteParameters(u32 base_address, u32 size_in_bytes) { - // TODO: Return proper error codes - if (base_address + size_in_bytes >= 0x420000) { - LOG_ERROR(Service_GSP, "Write address out of range! (address=0x%08x, size=0x%08x)", +static ResultCode WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) { + // This magic number is verified to be done by the gsp module + const u32 max_size_in_bytes = 0x80; + + if (base_address & 3 || base_address >= 0x420000) { + LOG_ERROR(Service_GSP, "Write address was out of range or misaligned! (address=0x%08x, size=0x%08x)", base_address, size_in_bytes); - return false; - } + return ERR_GSP_REGS_OUTOFRANGE_OR_MISALIGNED; + } else if (size_in_bytes <= max_size_in_bytes) { + if (size_in_bytes & 3) { + LOG_ERROR(Service_GSP, "Misaligned size 0x%08x", size_in_bytes); + return ERR_GSP_REGS_MISALIGNED; + } else { + while (size_in_bytes > 0) { + HW::Write<u32>(base_address + REGS_BEGIN, *data); + + size_in_bytes -= 4; + ++data; + base_address += 4; + } + return RESULT_SUCCESS; + } - // size should be word-aligned - if ((size_in_bytes % 4) != 0) { - LOG_ERROR(Service_GSP, "Invalid size 0x%08x", size_in_bytes); - return false; + } else { + LOG_ERROR(Service_GSP, "Out of range size 0x%08x", size_in_bytes); + return ERR_GSP_REGS_INVALID_SIZE; } - - return true; } /** - * Writes sequential GSP GPU hardware registers using an array of source data + * Updates sequential GSP GPU hardware registers using parallel arrays of source data and masks. + * For each register, the value is updated only where the mask is high * * @param base_address The address of the first register in the sequence * @param size_in_bytes The number of registers to update (size of data) - * @param data A pointer to the source data + * @param data A pointer to the source data to use for updates + * @param masks A pointer to the masks + * @return RESULT_SUCCESS if the parameters are valid, error code otherwise */ -static void WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) { - // TODO: Return proper error codes - if (!CheckWriteParameters(base_address, size_in_bytes)) - return; +static ResultCode WriteHWRegsWithMask(u32 base_address, u32 size_in_bytes, const u32* data, const u32* masks) { + // This magic number is verified to be done by the gsp module + const u32 max_size_in_bytes = 0x80; - while (size_in_bytes > 0) { - HW::Write<u32>(base_address + REGS_BEGIN, *data); + if (base_address & 3 || base_address >= 0x420000) { + LOG_ERROR(Service_GSP, "Write address was out of range or misaligned! (address=0x%08x, size=0x%08x)", + base_address, size_in_bytes); + return ERR_GSP_REGS_OUTOFRANGE_OR_MISALIGNED; + } else if (size_in_bytes <= max_size_in_bytes) { + if (size_in_bytes & 3) { + LOG_ERROR(Service_GSP, "Misaligned size 0x%08x", size_in_bytes); + return ERR_GSP_REGS_MISALIGNED; + } else { + while (size_in_bytes > 0) { + const u32 reg_address = base_address + REGS_BEGIN; + + u32 reg_value; + HW::Read<u32>(reg_value, reg_address); + + // Update the current value of the register only for set mask bits + reg_value = (reg_value & ~*masks) | (*data | *masks); + + HW::Write<u32>(reg_address, reg_value); + + size_in_bytes -= 4; + ++data; + ++masks; + base_address += 4; + } + return RESULT_SUCCESS; + } - size_in_bytes -= 4; - ++data; - base_address += 4; + } else { + LOG_ERROR(Service_GSP, "Out of range size 0x%08x", size_in_bytes); + return ERR_GSP_REGS_INVALID_SIZE; } } @@ -120,39 +167,7 @@ static void WriteHWRegs(Service::Interface* self) { u32* src = (u32*)Memory::GetPointer(cmd_buff[4]); - WriteHWRegs(reg_addr, size, src); -} - -/** - * Updates sequential GSP GPU hardware registers using parallel arrays of source data and masks. - * For each register, the value is updated only where the mask is high - * - * @param base_address The address of the first register in the sequence - * @param size_in_bytes The number of registers to update (size of data) - * @param data A pointer to the source data to use for updates - * @param masks A pointer to the masks - */ -static void WriteHWRegsWithMask(u32 base_address, u32 size_in_bytes, const u32* data, const u32* masks) { - // TODO: Return proper error codes - if (!CheckWriteParameters(base_address, size_in_bytes)) - return; - - while (size_in_bytes > 0) { - const u32 reg_address = base_address + REGS_BEGIN; - - u32 reg_value; - HW::Read<u32>(reg_value, reg_address); - - // Update the current value of the register only for set mask bits - reg_value = (reg_value & ~*masks) | (*data | *masks); - - HW::Write<u32>(reg_address, reg_value); - - size_in_bytes -= 4; - ++data; - ++masks; - base_address += 4; - } + cmd_buff[1] = WriteHWRegs(reg_addr, size, src).raw; } /** @@ -174,7 +189,7 @@ static void WriteHWRegsWithMask(Service::Interface* self) { u32* src_data = (u32*)Memory::GetPointer(cmd_buff[4]); u32* mask_data = (u32*)Memory::GetPointer(cmd_buff[6]); - WriteHWRegsWithMask(reg_addr, size, src_data, mask_data); + cmd_buff[1] = WriteHWRegsWithMask(reg_addr, size, src_data, mask_data).raw; } /// Read a GSP GPU hardware register @@ -206,27 +221,27 @@ static void ReadHWRegs(Service::Interface* self) { } } -void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) { +ResultCode SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) { u32 base_address = 0x400000; PAddr phys_address_left = Memory::VirtualToPhysicalAddress(info.address_left); PAddr phys_address_right = Memory::VirtualToPhysicalAddress(info.address_right); if (info.active_fb == 0) { - WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_left1)), 4, - &phys_address_left); - WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_right1)), 4, - &phys_address_right); + WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_left1)), + 4, &phys_address_left); + WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_right1)), + 4, &phys_address_right); } else { - WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_left2)), 4, - &phys_address_left); - WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_right2)), 4, - &phys_address_right); + WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_left2)), + 4, &phys_address_left); + WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_right2)), + 4, &phys_address_right); } - WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].stride)), 4, - &info.stride); - WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].color_format)), 4, - &info.format); - WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].active_fb)), 4, - &info.shown_fb); + WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].stride)), + 4, &info.stride); + WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].color_format)), + 4, &info.format); + WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].active_fb)), + 4, &info.shown_fb); if (Pica::g_debug_context) Pica::g_debug_context->OnEvent(Pica::DebugContext::Event::BufferSwapped, nullptr); @@ -234,6 +249,8 @@ void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) { if (screen_id == 0) { MicroProfileFlip(); } + + return RESULT_SUCCESS; } /** @@ -251,9 +268,8 @@ static void SetBufferSwap(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 screen_id = cmd_buff[1]; FrameBufferInfo* fb_info = (FrameBufferInfo*)&cmd_buff[2]; - SetBufferSwap(screen_id, *fb_info); - cmd_buff[1] = 0; // No error + cmd_buff[1] = SetBufferSwap(screen_id, *fb_info).raw; } /** @@ -286,6 +302,22 @@ static void FlushDataCache(Service::Interface* self) { } /** + * GSP_GPU::SetAxiConfigQoSMode service function + * Inputs: + * 1 : Mode, unused in emulator + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void SetAxiConfigQoSMode(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 mode = cmd_buff[1]; + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + + LOG_WARNING(Service_GSP, "(STUBBED) called mode=0x%08X", mode); +} + +/** * GSP_GPU::RegisterInterruptRelayQueue service function * Inputs: * 1 : "Flags" field, purpose is unknown @@ -302,6 +334,12 @@ static void RegisterInterruptRelayQueue(Service::Interface* self) { g_interrupt_event = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[3]); ASSERT_MSG((g_interrupt_event != nullptr), "handle is not valid!"); + g_interrupt_event->name = "GSP_GPU::interrupt_event"; + + using Kernel::MemoryPermission; + g_shared_memory = Kernel::SharedMemory::Create(0x1000, MemoryPermission::ReadWrite, + MemoryPermission::ReadWrite, "GSPSharedMem"); + Handle shmem_handle = Kernel::g_handle_table.Create(g_shared_memory).MoveFrom(); // This specific code is required for a successful initialization, rather than 0 @@ -314,6 +352,22 @@ static void RegisterInterruptRelayQueue(Service::Interface* self) { } /** + * GSP_GPU::UnregisterInterruptRelayQueue service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void UnregisterInterruptRelayQueue(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + g_shared_memory = nullptr; + g_interrupt_event = nullptr; + + cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_WARNING(Service_GSP, "called"); +} + +/** * Signals that the specified interrupt type has occurred to userland code * @param interrupt_id ID of interrupt that is being signalled * @todo This should probably take a thread_id parameter and only signal this thread? @@ -591,11 +645,11 @@ const Interface::FunctionInfo FunctionTable[] = { {0x000D0140, nullptr, "SetDisplayTransfer"}, {0x000E0180, nullptr, "SetTextureCopy"}, {0x000F0200, nullptr, "SetMemoryFill"}, - {0x00100040, nullptr, "SetAxiConfigQoSMode"}, + {0x00100040, SetAxiConfigQoSMode, "SetAxiConfigQoSMode"}, {0x00110040, nullptr, "SetPerfLogMode"}, {0x00120000, nullptr, "GetPerfLog"}, {0x00130042, RegisterInterruptRelayQueue, "RegisterInterruptRelayQueue"}, - {0x00140000, nullptr, "UnregisterInterruptRelayQueue"}, + {0x00140000, UnregisterInterruptRelayQueue, "UnregisterInterruptRelayQueue"}, {0x00150002, nullptr, "TryAcquireRight"}, {0x00160042, nullptr, "AcquireRight"}, {0x00170000, nullptr, "ReleaseRight"}, @@ -616,10 +670,7 @@ Interface::Interface() { Register(FunctionTable); g_interrupt_event = nullptr; - - using Kernel::MemoryPermission; - g_shared_memory = Kernel::SharedMemory::Create(0x1000, MemoryPermission::ReadWrite, - MemoryPermission::ReadWrite, "GSPSharedMem"); + g_shared_memory = nullptr; g_thread_id = 0; } diff --git a/src/core/hle/service/gsp_gpu.h b/src/core/hle/service/gsp_gpu.h index 0e2f7a21e..55a993bb8 100644 --- a/src/core/hle/service/gsp_gpu.h +++ b/src/core/hle/service/gsp_gpu.h @@ -194,7 +194,7 @@ public: */ void SignalInterrupt(InterruptId interrupt_id); -void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info); +ResultCode SetBufferSwap(u32 screen_id, const FrameBufferInfo& info); /** * Retrieves the framebuffer info stored in the GSP shared memory for the diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 35b648409..0fe3a4d7a 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -9,6 +9,7 @@ #include "core/hle/service/ac_u.h" #include "core/hle/service/act_u.h" #include "core/hle/service/csnd_snd.h" +#include "core/hle/service/dlp_srvr.h" #include "core/hle/service/dsp_dsp.h" #include "core/hle/service/err_f.h" #include "core/hle/service/gsp_gpu.h" @@ -70,9 +71,8 @@ ResultVal<bool> Interface::SyncRequest() { // TODO(bunnei): Hack - ignore error cmd_buff[1] = 0; return MakeResult<bool>(false); - } else { - LOG_TRACE(Service, "%s", MakeFunctionString(itr->second.name, GetPortName().c_str(), cmd_buff).c_str()); } + LOG_TRACE(Service, "%s", MakeFunctionString(itr->second.name, GetPortName().c_str(), cmd_buff).c_str()); itr->second.func(this); @@ -121,6 +121,7 @@ void Init() { AddService(new AC_U::Interface); AddService(new ACT_U::Interface); AddService(new CSND_SND::Interface); + AddService(new DLP_SRVR::Interface); AddService(new DSP_DSP::Interface); AddService(new GSP_GPU::Interface); AddService(new GSP_LCD::Interface); diff --git a/src/core/hle/shared_page.cpp b/src/core/hle/shared_page.cpp index 50c5bc01b..2a1caeaac 100644 --- a/src/core/hle/shared_page.cpp +++ b/src/core/hle/shared_page.cpp @@ -16,6 +16,9 @@ void Init() { std::memset(&shared_page, 0, sizeof(shared_page)); shared_page.running_hw = 0x1; // product + + // Some games wait until this value becomes 0x1, before asking running_hw + shared_page.unknown_value = 0x1; } } // namespace diff --git a/src/core/hle/shared_page.h b/src/core/hle/shared_page.h index 379bb7b63..35a07c685 100644 --- a/src/core/hle/shared_page.h +++ b/src/core/hle/shared_page.h @@ -39,12 +39,14 @@ struct SharedPageDef { DateTime date_time_0; // 20 DateTime date_time_1; // 40 u8 wifi_macaddr[6]; // 60 - u8 wifi_unknown1; // 66 + u8 wifi_link_level; // 66 u8 wifi_unknown2; // 67 INSERT_PADDING_BYTES(0x80 - 0x68); // 68 float_le sliderstate_3d; // 80 u8 ledstate_3d; // 84 - INSERT_PADDING_BYTES(0xA0 - 0x85); // 85 + INSERT_PADDING_BYTES(1); // 85 + u8 unknown_value; // 86 + INSERT_PADDING_BYTES(0xA0 - 0x87); // 87 u64_le menu_title_id; // A0 u64_le active_menu_title_id; // A8 INSERT_PADDING_BYTES(0x1000 - 0xB0); // B0 diff --git a/src/core/hw/y2r.cpp b/src/core/hw/y2r.cpp index 48c45564f..083391e83 100644 --- a/src/core/hw/y2r.cpp +++ b/src/core/hw/y2r.cpp @@ -261,7 +261,7 @@ void PerformConversion(ConversionConfiguration& cvt) { ASSERT(cvt.block_alignment != BlockAlignment::Block8x8 || cvt.input_lines % 8 == 0); // Tiles per row size_t num_tiles = cvt.input_line_width / 8; - ASSERT(num_tiles < MAX_TILES); + ASSERT(num_tiles <= MAX_TILES); // Buffer used as a CDMA source/target. std::unique_ptr<u8[]> data_buffer(new u8[cvt.input_line_width * 8 * 4]); diff --git a/src/core/loader/3dsx.cpp b/src/core/loader/3dsx.cpp index 8eed6a50a..5fb3b9e2b 100644 --- a/src/core/loader/3dsx.cpp +++ b/src/core/loader/3dsx.cpp @@ -10,13 +10,9 @@ #include "core/file_sys/archive_romfs.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/resource_limit.h" -#include "core/hle/service/fs/archive.h" -#include "core/loader/elf.h" -#include "core/loader/ncch.h" +#include "core/loader/3dsx.h" #include "core/memory.h" -#include "3dsx.h" - namespace Loader { /* diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index b1907cd55..886501c41 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp @@ -6,7 +6,6 @@ #include <string> #include "common/logging/log.h" -#include "common/make_unique.h" #include "common/string_util.h" #include "core/file_sys/archive_romfs.h" @@ -120,7 +119,7 @@ ResultStatus LoadFile(const std::string& filename) { AppLoader_THREEDSX app_loader(std::move(file), filename_filename, filename); // Load application and RomFS if (ResultStatus::Success == app_loader.Load()) { - Service::FS::RegisterArchiveType(Common::make_unique<FileSys::ArchiveFactory_RomFS>(app_loader), Service::FS::ArchiveIdCode::RomFS); + Service::FS::RegisterArchiveType(std::make_unique<FileSys::ArchiveFactory_RomFS>(app_loader), Service::FS::ArchiveIdCode::RomFS); return ResultStatus::Success; } break; @@ -139,7 +138,7 @@ ResultStatus LoadFile(const std::string& filename) { // Load application and RomFS ResultStatus result = app_loader.Load(); if (ResultStatus::Success == result) { - Service::FS::RegisterArchiveType(Common::make_unique<FileSys::ArchiveFactory_RomFS>(app_loader), Service::FS::ArchiveIdCode::RomFS); + Service::FS::RegisterArchiveType(std::make_unique<FileSys::ArchiveFactory_RomFS>(app_loader), Service::FS::ArchiveIdCode::RomFS); } return result; } diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index 93f21bec2..a4b47ef8c 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -7,7 +7,6 @@ #include <memory> #include "common/logging/log.h" -#include "common/make_unique.h" #include "common/string_util.h" #include "common/swap.h" @@ -175,7 +174,7 @@ ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>& return ResultStatus::Error; LOG_DEBUG(Loader, "%d sections:", kMaxSections); - // Iterate through the ExeFs archive until we find the .code file... + // Iterate through the ExeFs archive until we find a section with the specified name... for (unsigned section_number = 0; section_number < kMaxSections; section_number++) { const auto& section = exefs_header.section[section_number]; @@ -187,7 +186,7 @@ ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>& s64 section_offset = (section.offset + exefs_offset + sizeof(ExeFs_Header) + ncch_offset); file.Seek(section_offset, SEEK_SET); - if (is_compressed) { + if (strcmp(section.name, ".code") == 0 && is_compressed) { // Section is compressed, read compressed .code section... std::unique_ptr<u8[]> temp_buffer; try { diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 8a14f75aa..1aa26fbd2 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -4,8 +4,22 @@ #include "settings.h" +#include "core/gdbstub/gdbstub.h" + +#include "video_core/video_core.h" + namespace Settings { Values values = {}; +void Apply() { + + GDBStub::SetServerPort(static_cast<u32>(values.gdbstub_port)); + GDBStub::ToggleServer(values.use_gdbstub); + + VideoCore::g_hw_renderer_enabled = values.use_hw_renderer; + VideoCore::g_shader_jit_enabled = values.use_shader_jit; + } + +} // namespace diff --git a/src/core/settings.h b/src/core/settings.h index 97ddcdff9..4933a516d 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -67,4 +67,6 @@ struct Values { u16 gdbstub_port; } extern values; +void Apply(); + } diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp index 028b59348..3abe79c09 100644 --- a/src/video_core/command_processor.cpp +++ b/src/video_core/command_processor.cpp @@ -140,7 +140,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) { immediate_attribute_id = 0; Shader::UnitState<false> shader_unit; - Shader::Setup(shader_unit); + Shader::Setup(); if (g_debug_context) g_debug_context->OnEvent(DebugContext::Event::VertexLoaded, static_cast<void*>(&immediate_input)); @@ -249,10 +249,6 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) { const u16* index_address_16 = reinterpret_cast<const u16*>(index_address_8); bool index_u16 = index_info.format != 0; -#if PICA_DUMP_GEOMETRY - DebugUtils::GeometryDumper geometry_dumper; - PrimitiveAssembler<DebugUtils::GeometryDumper::Vertex> dumping_primitive_assembler(regs.triangle_topology.Value()); -#endif PrimitiveAssembler<Shader::OutputVertex>& primitive_assembler = g_state.primitive_assembler; if (g_debug_context) { @@ -304,7 +300,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) { vertex_cache_ids.fill(-1); Shader::UnitState<false> shader_unit; - Shader::Setup(shader_unit); + Shader::Setup(); for (unsigned int index = 0; index < regs.num_vertices; ++index) { @@ -388,17 +384,6 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) { if (g_debug_context) g_debug_context->OnEvent(DebugContext::Event::VertexLoaded, (void*)&input); -#if PICA_DUMP_GEOMETRY - // NOTE: When dumping geometry, we simply assume that the first input attribute - // corresponds to the position for now. - DebugUtils::GeometryDumper::Vertex dumped_vertex = { - input.attr[0][0].ToFloat32(), input.attr[0][1].ToFloat32(), input.attr[0][2].ToFloat32() - }; - using namespace std::placeholders; - dumping_primitive_assembler.SubmitVertex(dumped_vertex, - std::bind(&DebugUtils::GeometryDumper::AddTriangle, - &geometry_dumper, _1, _2, _3)); -#endif // Send to vertex shader output = Shader::Run(shader_unit, input, attribute_config.GetNumTotalAttributes()); @@ -424,10 +409,6 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) { range.second, range.first); } -#if PICA_DUMP_GEOMETRY - geometry_dumper.Dump(); -#endif - break; } diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp index bac6d69c7..c3a9c9598 100644 --- a/src/video_core/debug_utils/debug_utils.cpp +++ b/src/video_core/debug_utils/debug_utils.cpp @@ -85,35 +85,6 @@ std::shared_ptr<DebugContext> g_debug_context; // TODO: Get rid of this global namespace DebugUtils { -void GeometryDumper::AddTriangle(Vertex& v0, Vertex& v1, Vertex& v2) { - vertices.push_back(v0); - vertices.push_back(v1); - vertices.push_back(v2); - - int num_vertices = (int)vertices.size(); - faces.push_back({{ num_vertices-3, num_vertices-2, num_vertices-1 }}); -} - -void GeometryDumper::Dump() { - static int index = 0; - std::string filename = std::string("geometry_dump") + std::to_string(++index) + ".obj"; - - std::ofstream file(filename); - - for (const auto& vertex : vertices) { - file << "v " << vertex.pos[0] - << " " << vertex.pos[1] - << " " << vertex.pos[2] << std::endl; - } - - for (const Face& face : faces) { - file << "f " << 1+face.index[0] - << " " << 1+face.index[1] - << " " << 1+face.index[2] << std::endl; - } -} - - void DumpShader(const std::string& filename, const Regs::ShaderConfig& config, const Shader::ShaderSetup& setup, const Regs::VSOutputAttributes* output_attributes) { struct StuffToWrite { @@ -315,7 +286,7 @@ void StartPicaTracing() } std::lock_guard<std::mutex> lock(pica_trace_mutex); - pica_trace = std::unique_ptr<PicaTrace>(new PicaTrace); + pica_trace = std::make_unique<PicaTrace>(); is_pica_tracing = true; } @@ -615,6 +586,21 @@ TextureInfo TextureInfo::FromPicaRegister(const Regs::TextureConfig& config, return info; } +#ifdef HAVE_PNG +// Adapter functions to libpng to write/flush to File::IOFile instances. +static void WriteIOFile(png_structp png_ptr, png_bytep data, png_size_t length) { + auto* fp = static_cast<FileUtil::IOFile*>(png_get_io_ptr(png_ptr)); + if (!fp->WriteBytes(data, length)) + png_error(png_ptr, "Failed to write to output PNG file."); +} + +static void FlushIOFile(png_structp png_ptr) { + auto* fp = static_cast<FileUtil::IOFile*>(png_get_io_ptr(png_ptr)); + if (!fp->Flush()) + png_error(png_ptr, "Failed to flush to output PNG file."); +} +#endif + void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) { #ifndef HAVE_PNG return; @@ -658,7 +644,7 @@ void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) { goto finalise; } - png_init_io(png_ptr, fp.GetHandle()); + png_set_write_fn(png_ptr, static_cast<void*>(&fp), WriteIOFile, FlushIOFile); // Write header (8 bit color depth) png_set_IHDR(png_ptr, info_ptr, texture_config.width, texture_config.height, diff --git a/src/video_core/debug_utils/debug_utils.h b/src/video_core/debug_utils/debug_utils.h index 795160a32..7df941619 100644 --- a/src/video_core/debug_utils/debug_utils.h +++ b/src/video_core/debug_utils/debug_utils.h @@ -158,30 +158,9 @@ extern std::shared_ptr<DebugContext> g_debug_context; // TODO: Get rid of this g namespace DebugUtils { -#define PICA_DUMP_GEOMETRY 0 #define PICA_DUMP_TEXTURES 0 #define PICA_LOG_TEV 0 -// Simple utility class for dumping geometry data to an OBJ file -class GeometryDumper { -public: - struct Vertex { - std::array<float,3> pos; - }; - - void AddTriangle(Vertex& v0, Vertex& v1, Vertex& v2); - - void Dump(); - -private: - struct Face { - int index[3]; - }; - - std::vector<Vertex> vertices; - std::vector<Face> faces; -}; - void DumpShader(const std::string& filename, const Regs::ShaderConfig& config, const Shader::ShaderSetup& setup, const Regs::VSOutputAttributes* output_attributes); diff --git a/src/video_core/pica.h b/src/video_core/pica.h index 16f9e4006..4552ff81c 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h @@ -578,7 +578,17 @@ struct Regs { } struct { - INSERT_PADDING_WORDS(0x6); + INSERT_PADDING_WORDS(0x3); + + union { + BitField<0, 4, u32> allow_color_write; // 0 = disable, else enable + }; + + INSERT_PADDING_WORDS(0x1); + + union { + BitField<0, 2, u32> allow_depth_stencil_write; // 0 = disable, else enable + }; DepthFormat depth_format; // TODO: Should be a BitField! BitField<16, 3, ColorFormat> color_format; diff --git a/src/video_core/primitive_assembly.cpp b/src/video_core/primitive_assembly.cpp index 0061690f1..ff3e2b862 100644 --- a/src/video_core/primitive_assembly.cpp +++ b/src/video_core/primitive_assembly.cpp @@ -68,7 +68,5 @@ void PrimitiveAssembler<VertexType>::Reconfigure(Regs::TriangleTopology topology // explicitly instantiate use cases template struct PrimitiveAssembler<Shader::OutputVertex>; -template -struct PrimitiveAssembler<DebugUtils::GeometryDumper::Vertex>; } // namespace diff --git a/src/video_core/rasterizer.cpp b/src/video_core/rasterizer.cpp index fd02aa652..0434ad05a 100644 --- a/src/video_core/rasterizer.cpp +++ b/src/video_core/rasterizer.cpp @@ -809,7 +809,8 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, auto UpdateStencil = [stencil_test, x, y, &old_stencil](Pica::Regs::StencilAction action) { u8 new_stencil = PerformStencilAction(action, old_stencil, stencil_test.reference_value); - SetStencil(x >> 4, y >> 4, (new_stencil & stencil_test.write_mask) | (old_stencil & ~stencil_test.write_mask)); + if (g_state.regs.framebuffer.allow_depth_stencil_write != 0) + SetStencil(x >> 4, y >> 4, (new_stencil & stencil_test.write_mask) | (old_stencil & ~stencil_test.write_mask)); }; if (stencil_action_enable) { @@ -909,7 +910,7 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, } } - if (output_merger.depth_write_enable) + if (regs.framebuffer.allow_depth_stencil_write != 0 && output_merger.depth_write_enable) SetDepth(x >> 4, y >> 4, z); // The stencil depth_pass action is executed even if depth testing is disabled @@ -922,92 +923,72 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, if (output_merger.alphablend_enable) { auto params = output_merger.alpha_blending; - auto LookupFactorRGB = [&](Regs::BlendFactor factor) -> Math::Vec3<u8> { + auto LookupFactor = [&](unsigned channel, Regs::BlendFactor factor) -> u8 { + DEBUG_ASSERT(channel < 4); + + const Math::Vec4<u8> blend_const = { + static_cast<u8>(output_merger.blend_const.r), + static_cast<u8>(output_merger.blend_const.g), + static_cast<u8>(output_merger.blend_const.b), + static_cast<u8>(output_merger.blend_const.a) + }; + switch (factor) { - case Regs::BlendFactor::Zero : - return Math::Vec3<u8>(0, 0, 0); + case Regs::BlendFactor::Zero: + return 0; - case Regs::BlendFactor::One : - return Math::Vec3<u8>(255, 255, 255); + case Regs::BlendFactor::One: + return 255; case Regs::BlendFactor::SourceColor: - return combiner_output.rgb(); + return combiner_output[channel]; case Regs::BlendFactor::OneMinusSourceColor: - return Math::Vec3<u8>(255 - combiner_output.r(), 255 - combiner_output.g(), 255 - combiner_output.b()); + return 255 - combiner_output[channel]; case Regs::BlendFactor::DestColor: - return dest.rgb(); + return dest[channel]; case Regs::BlendFactor::OneMinusDestColor: - return Math::Vec3<u8>(255 - dest.r(), 255 - dest.g(), 255 - dest.b()); + return 255 - dest[channel]; case Regs::BlendFactor::SourceAlpha: - return Math::Vec3<u8>(combiner_output.a(), combiner_output.a(), combiner_output.a()); + return combiner_output.a(); case Regs::BlendFactor::OneMinusSourceAlpha: - return Math::Vec3<u8>(255 - combiner_output.a(), 255 - combiner_output.a(), 255 - combiner_output.a()); + return 255 - combiner_output.a(); case Regs::BlendFactor::DestAlpha: - return Math::Vec3<u8>(dest.a(), dest.a(), dest.a()); + return dest.a(); case Regs::BlendFactor::OneMinusDestAlpha: - return Math::Vec3<u8>(255 - dest.a(), 255 - dest.a(), 255 - dest.a()); + return 255 - dest.a(); case Regs::BlendFactor::ConstantColor: - return Math::Vec3<u8>(output_merger.blend_const.r, output_merger.blend_const.g, output_merger.blend_const.b); + return blend_const[channel]; case Regs::BlendFactor::OneMinusConstantColor: - return Math::Vec3<u8>(255 - output_merger.blend_const.r, 255 - output_merger.blend_const.g, 255 - output_merger.blend_const.b); + return 255 - blend_const[channel]; case Regs::BlendFactor::ConstantAlpha: - return Math::Vec3<u8>(output_merger.blend_const.a, output_merger.blend_const.a, output_merger.blend_const.a); + return blend_const.a(); case Regs::BlendFactor::OneMinusConstantAlpha: - return Math::Vec3<u8>(255 - output_merger.blend_const.a, 255 - output_merger.blend_const.a, 255 - output_merger.blend_const.a); - - default: - LOG_CRITICAL(HW_GPU, "Unknown color blend factor %x", factor); - UNIMPLEMENTED(); - break; - } - - return {}; - }; - - auto LookupFactorA = [&](Regs::BlendFactor factor) -> u8 { - switch (factor) { - case Regs::BlendFactor::Zero: - return 0; - - case Regs::BlendFactor::One: - return 255; - - case Regs::BlendFactor::SourceAlpha: - return combiner_output.a(); - - case Regs::BlendFactor::OneMinusSourceAlpha: - return 255 - combiner_output.a(); + return 255 - blend_const.a(); - case Regs::BlendFactor::DestAlpha: - return dest.a(); - - case Regs::BlendFactor::OneMinusDestAlpha: - return 255 - dest.a(); - - case Regs::BlendFactor::ConstantAlpha: - return output_merger.blend_const.a; - - case Regs::BlendFactor::OneMinusConstantAlpha: - return 255 - output_merger.blend_const.a; + case Regs::BlendFactor::SourceAlphaSaturate: + // Returns 1.0 for the alpha channel + if (channel == 3) + return 255; + return std::min(combiner_output.a(), static_cast<u8>(255 - dest.a())); default: - LOG_CRITICAL(HW_GPU, "Unknown alpha blend factor %x", factor); + LOG_CRITICAL(HW_GPU, "Unknown blend factor %x", factor); UNIMPLEMENTED(); break; } - return {}; + return combiner_output[channel]; }; static auto EvaluateBlendEquation = [](const Math::Vec4<u8>& src, const Math::Vec4<u8>& srcfactor, @@ -1059,10 +1040,15 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, MathUtil::Clamp(result.a(), 0, 255)); }; - auto srcfactor = Math::MakeVec(LookupFactorRGB(params.factor_source_rgb), - LookupFactorA(params.factor_source_a)); - auto dstfactor = Math::MakeVec(LookupFactorRGB(params.factor_dest_rgb), - LookupFactorA(params.factor_dest_a)); + auto srcfactor = Math::MakeVec(LookupFactor(0, params.factor_source_rgb), + LookupFactor(1, params.factor_source_rgb), + LookupFactor(2, params.factor_source_rgb), + LookupFactor(3, params.factor_source_a)); + + auto dstfactor = Math::MakeVec(LookupFactor(0, params.factor_dest_rgb), + LookupFactor(1, params.factor_dest_rgb), + LookupFactor(2, params.factor_dest_rgb), + LookupFactor(3, params.factor_dest_a)); blend_output = EvaluateBlendEquation(combiner_output, srcfactor, dest, dstfactor, params.blend_equation_rgb); blend_output.a() = EvaluateBlendEquation(combiner_output, srcfactor, dest, dstfactor, params.blend_equation_a).a(); @@ -1133,7 +1119,8 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, output_merger.alpha_enable ? blend_output.a() : dest.a() }; - DrawPixel(x >> 4, y >> 4, result); + if (regs.framebuffer.allow_color_write != 0) + DrawPixel(x >> 4, y >> 4, result); } } } diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp index 6467ff723..101f84eb9 100644 --- a/src/video_core/renderer_base.cpp +++ b/src/video_core/renderer_base.cpp @@ -4,8 +4,6 @@ #include <memory> -#include "common/make_unique.h" - #include "core/settings.h" #include "video_core/renderer_base.h" @@ -19,9 +17,9 @@ void RendererBase::RefreshRasterizerSetting() { opengl_rasterizer_active = hw_renderer_enabled; if (hw_renderer_enabled) { - rasterizer = Common::make_unique<RasterizerOpenGL>(); + rasterizer = std::make_unique<RasterizerOpenGL>(); } else { - rasterizer = Common::make_unique<VideoCore::SWRasterizer>(); + rasterizer = std::make_unique<VideoCore::SWRasterizer>(); } rasterizer->InitObjects(); rasterizer->Reset(); diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 1fadcf5ae..6ca9f45e2 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -9,7 +9,6 @@ #include "common/color.h" #include "common/file_util.h" -#include "common/make_unique.h" #include "common/math_util.h" #include "common/microprofile.h" #include "common/profiler.h" @@ -140,8 +139,9 @@ void RasterizerOpenGL::InitObjects() { } state.Apply(); - ASSERT_MSG(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE, - "OpenGL rasterizer framebuffer setup failed, status %X", glCheckFramebufferStatus(GL_FRAMEBUFFER)); + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + ASSERT_MSG(status == GL_FRAMEBUFFER_COMPLETE, + "OpenGL rasterizer framebuffer setup failed, status %X", status); } void RasterizerOpenGL::Reset() { @@ -153,6 +153,9 @@ void RasterizerOpenGL::Reset() { SyncLogicOp(); SyncStencilTest(); SyncDepthTest(); + SyncColorWriteMask(); + SyncStencilWriteMask(); + SyncDepthWriteMask(); SetShader(); @@ -268,15 +271,36 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) { state.draw.shader_dirty = true; break; - // Stencil test + // Sync GL stencil test + stencil write mask + // (Pica stencil test function register also contains a stencil write mask) case PICA_REG_INDEX(output_merger.stencil_test.raw_func): + SyncStencilTest(); + SyncStencilWriteMask(); + break; case PICA_REG_INDEX(output_merger.stencil_test.raw_op): + case PICA_REG_INDEX(framebuffer.depth_format): SyncStencilTest(); break; - // Depth test + // Sync GL depth test + depth and color write mask + // (Pica depth test function register also contains a depth and color write mask) case PICA_REG_INDEX(output_merger.depth_test_enable): SyncDepthTest(); + SyncDepthWriteMask(); + SyncColorWriteMask(); + break; + + // Sync GL depth and stencil write mask + // (This is a dedicated combined depth / stencil write-enable register) + case PICA_REG_INDEX(framebuffer.allow_depth_stencil_write): + SyncDepthWriteMask(); + SyncStencilWriteMask(); + break; + + // Sync GL color write mask + // (This is a dedicated color write-enable register) + case PICA_REG_INDEX(framebuffer.allow_color_write): + SyncColorWriteMask(); break; // Logic op @@ -677,7 +701,7 @@ void RasterizerOpenGL::ReconfigureDepthTexture(DepthTextureInfo& texture, Pica:: void RasterizerOpenGL::SetShader() { PicaShaderConfig config = PicaShaderConfig::CurrentConfig(); - std::unique_ptr<PicaShader> shader = Common::make_unique<PicaShader>(); + std::unique_ptr<PicaShader> shader = std::make_unique<PicaShader>(); // Find (or generate) the GLSL shader for the current TEV state auto cached_shader = shader_cache.find(config); @@ -808,6 +832,10 @@ void RasterizerOpenGL::SyncFramebuffer() { ReloadDepthBuffer(); } + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + ASSERT_MSG(status == GL_FRAMEBUFFER_COMPLETE, + "OpenGL rasterizer framebuffer setup failed, status %X", status); } void RasterizerOpenGL::SyncCullMode() { @@ -876,13 +904,39 @@ void RasterizerOpenGL::SyncLogicOp() { state.logic_op = PicaToGL::LogicOp(Pica::g_state.regs.output_merger.logic_op); } +void RasterizerOpenGL::SyncColorWriteMask() { + const auto& regs = Pica::g_state.regs; + + auto IsColorWriteEnabled = [&](u32 value) { + return (regs.framebuffer.allow_color_write != 0 && value != 0) ? GL_TRUE : GL_FALSE; + }; + + state.color_mask.red_enabled = IsColorWriteEnabled(regs.output_merger.red_enable); + state.color_mask.green_enabled = IsColorWriteEnabled(regs.output_merger.green_enable); + state.color_mask.blue_enabled = IsColorWriteEnabled(regs.output_merger.blue_enable); + state.color_mask.alpha_enabled = IsColorWriteEnabled(regs.output_merger.alpha_enable); +} + +void RasterizerOpenGL::SyncStencilWriteMask() { + const auto& regs = Pica::g_state.regs; + state.stencil.write_mask = (regs.framebuffer.allow_depth_stencil_write != 0) + ? static_cast<GLuint>(regs.output_merger.stencil_test.write_mask) + : 0; +} + +void RasterizerOpenGL::SyncDepthWriteMask() { + const auto& regs = Pica::g_state.regs; + state.depth.write_mask = (regs.framebuffer.allow_depth_stencil_write != 0 && regs.output_merger.depth_write_enable) + ? GL_TRUE + : GL_FALSE; +} + void RasterizerOpenGL::SyncStencilTest() { const auto& regs = Pica::g_state.regs; state.stencil.test_enabled = regs.output_merger.stencil_test.enable && regs.framebuffer.depth_format == Pica::Regs::DepthFormat::D24S8; state.stencil.test_func = PicaToGL::CompareFunc(regs.output_merger.stencil_test.func); state.stencil.test_ref = regs.output_merger.stencil_test.reference_value; state.stencil.test_mask = regs.output_merger.stencil_test.input_mask; - state.stencil.write_mask = regs.output_merger.stencil_test.write_mask; state.stencil.action_stencil_fail = PicaToGL::StencilOp(regs.output_merger.stencil_test.action_stencil_fail); state.stencil.action_depth_fail = PicaToGL::StencilOp(regs.output_merger.stencil_test.action_depth_fail); state.stencil.action_depth_pass = PicaToGL::StencilOp(regs.output_merger.stencil_test.action_depth_pass); @@ -894,11 +948,6 @@ void RasterizerOpenGL::SyncDepthTest() { regs.output_merger.depth_write_enable == 1; state.depth.test_func = regs.output_merger.depth_test_enable == 1 ? PicaToGL::CompareFunc(regs.output_merger.depth_test_func) : GL_ALWAYS; - state.color_mask.red_enabled = regs.output_merger.red_enable; - state.color_mask.green_enabled = regs.output_merger.green_enable; - state.color_mask.blue_enabled = regs.output_merger.blue_enable; - state.color_mask.alpha_enabled = regs.output_merger.alpha_enable; - state.depth.write_mask = regs.output_merger.depth_write_enable ? GL_TRUE : GL_FALSE; } void RasterizerOpenGL::SyncCombinerColor() { diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index fc85aa3ff..390349a0c 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -344,6 +344,15 @@ private: /// Syncs the logic op states to match the PICA register void SyncLogicOp(); + /// Syncs the color write mask to match the PICA register state + void SyncColorWriteMask(); + + /// Syncs the stencil write mask to match the PICA register state + void SyncStencilWriteMask(); + + /// Syncs the depth write mask to match the PICA register state + void SyncDepthWriteMask(); + /// Syncs the stencil test states to match the PICA register void SyncStencilTest(); diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index a9ad46fe0..1323c12e4 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -2,8 +2,9 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <memory> + #include "common/hash.h" -#include "common/make_unique.h" #include "common/math_util.h" #include "common/microprofile.h" #include "common/vector_math.h" @@ -29,7 +30,7 @@ void RasterizerCacheOpenGL::LoadAndBindTexture(OpenGLState &state, unsigned text } else { MICROPROFILE_SCOPE(OpenGL_TextureUpload); - std::unique_ptr<CachedTexture> new_texture = Common::make_unique<CachedTexture>(); + std::unique_ptr<CachedTexture> new_texture = std::make_unique<CachedTexture>(); new_texture->texture.Create(); state.texture_units[texture_unit].texture_2d = new_texture->texture.handle; diff --git a/src/video_core/renderer_opengl/pica_to_gl.h b/src/video_core/renderer_opengl/pica_to_gl.h index 3d6c4e9e5..fd3617d77 100644 --- a/src/video_core/renderer_opengl/pica_to_gl.h +++ b/src/video_core/renderer_opengl/pica_to_gl.h @@ -22,7 +22,7 @@ inline GLenum TextureFilterMode(Pica::Regs::TextureConfig::TextureFilter mode) { }; // Range check table for input - if (mode >= ARRAY_SIZE(filter_mode_table)) { + if (static_cast<size_t>(mode) >= ARRAY_SIZE(filter_mode_table)) { LOG_CRITICAL(Render_OpenGL, "Unknown texture filtering mode %d", mode); UNREACHABLE(); @@ -51,7 +51,7 @@ inline GLenum WrapMode(Pica::Regs::TextureConfig::WrapMode mode) { }; // Range check table for input - if (mode >= ARRAY_SIZE(wrap_mode_table)) { + if (static_cast<size_t>(mode) >= ARRAY_SIZE(wrap_mode_table)) { LOG_CRITICAL(Render_OpenGL, "Unknown texture wrap mode %d", mode); UNREACHABLE(); @@ -91,7 +91,7 @@ inline GLenum BlendFunc(Pica::Regs::BlendFactor factor) { }; // Range check table for input - if ((unsigned)factor >= ARRAY_SIZE(blend_func_table)) { + if (static_cast<size_t>(factor) >= ARRAY_SIZE(blend_func_table)) { LOG_CRITICAL(Render_OpenGL, "Unknown blend factor %d", factor); UNREACHABLE(); @@ -122,7 +122,7 @@ inline GLenum LogicOp(Pica::Regs::LogicOp op) { }; // Range check table for input - if ((unsigned)op >= ARRAY_SIZE(logic_op_table)) { + if (static_cast<size_t>(op) >= ARRAY_SIZE(logic_op_table)) { LOG_CRITICAL(Render_OpenGL, "Unknown logic op %d", op); UNREACHABLE(); @@ -145,7 +145,7 @@ inline GLenum CompareFunc(Pica::Regs::CompareFunc func) { }; // Range check table for input - if ((unsigned)func >= ARRAY_SIZE(compare_func_table)) { + if (static_cast<size_t>(func) >= ARRAY_SIZE(compare_func_table)) { LOG_CRITICAL(Render_OpenGL, "Unknown compare function %d", func); UNREACHABLE(); @@ -168,7 +168,7 @@ inline GLenum StencilOp(Pica::Regs::StencilAction action) { }; // Range check table for input - if ((unsigned)action >= ARRAY_SIZE(stencil_op_table)) { + if (static_cast<size_t>(action) >= ARRAY_SIZE(stencil_op_table)) { LOG_CRITICAL(Render_OpenGL, "Unknown stencil op %d", action); UNREACHABLE(); diff --git a/src/video_core/shader/shader.cpp b/src/video_core/shader/shader.cpp index eb1db0778..75301accd 100644 --- a/src/video_core/shader/shader.cpp +++ b/src/video_core/shader/shader.cpp @@ -8,7 +8,6 @@ #include <boost/range/algorithm/fill.hpp> #include "common/hash.h" -#include "common/make_unique.h" #include "common/microprofile.h" #include "common/profiler.h" @@ -29,36 +28,24 @@ namespace Pica { namespace Shader { #ifdef ARCHITECTURE_x86_64 -static std::unordered_map<u64, CompiledShader*> shader_map; -static JitCompiler jit; -static CompiledShader* jit_shader; - -static void ClearCache() { - shader_map.clear(); - jit.Clear(); - LOG_INFO(HW_GPU, "Shader JIT cache cleared"); -} +static std::unordered_map<u64, std::unique_ptr<JitShader>> shader_map; +static const JitShader* jit_shader; #endif // ARCHITECTURE_x86_64 -void Setup(UnitState<false>& state) { +void Setup() { #ifdef ARCHITECTURE_x86_64 if (VideoCore::g_shader_jit_enabled) { u64 cache_key = (Common::ComputeHash64(&g_state.vs.program_code, sizeof(g_state.vs.program_code)) ^ - Common::ComputeHash64(&g_state.vs.swizzle_data, sizeof(g_state.vs.swizzle_data)) ^ - g_state.regs.vs.main_offset); + Common::ComputeHash64(&g_state.vs.swizzle_data, sizeof(g_state.vs.swizzle_data))); auto iter = shader_map.find(cache_key); if (iter != shader_map.end()) { - jit_shader = iter->second; + jit_shader = iter->second.get(); } else { - // Check if remaining JIT code space is enough for at least one more (massive) shader - if (jit.GetSpaceLeft() < jit_shader_size) { - // If not, clear the cache of all previously compiled shaders - ClearCache(); - } - - jit_shader = jit.Compile(); - shader_map.emplace(cache_key, jit_shader); + auto shader = std::make_unique<JitShader>(); + shader->Compile(); + jit_shader = shader.get(); + shader_map[cache_key] = std::move(shader); } } #endif // ARCHITECTURE_x86_64 @@ -66,7 +53,7 @@ void Setup(UnitState<false>& state) { void Shutdown() { #ifdef ARCHITECTURE_x86_64 - ClearCache(); + shader_map.clear(); #endif // ARCHITECTURE_x86_64 } @@ -110,7 +97,7 @@ OutputVertex Run(UnitState<false>& state, const InputVertex& input, int num_attr #ifdef ARCHITECTURE_x86_64 if (VideoCore::g_shader_jit_enabled) - jit_shader(&state.registers); + jit_shader->Run(&state.registers, g_state.regs.vs.main_offset); else RunInterpreter(state); #else diff --git a/src/video_core/shader/shader.h b/src/video_core/shader/shader.h index 7af8f1fa1..9c5bd97bd 100644 --- a/src/video_core/shader/shader.h +++ b/src/video_core/shader/shader.h @@ -339,9 +339,8 @@ struct UnitState { /** * Performs any shader unit setup that only needs to happen once per shader (as opposed to once per * vertex, which would happen within the `Run` function). - * @param state Shader unit state, must be setup per shader and per shader unit */ -void Setup(UnitState<false>& state); +void Setup(); /// Performs any cleanup when the emulator is shutdown void Shutdown(); diff --git a/src/video_core/shader/shader_jit_x64.cpp b/src/video_core/shader/shader_jit_x64.cpp index dffe051ef..b47d3beda 100644 --- a/src/video_core/shader/shader_jit_x64.cpp +++ b/src/video_core/shader/shader_jit_x64.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> #include <smmintrin.h> #include "common/x64/abi.h" @@ -19,73 +20,73 @@ namespace Shader { using namespace Gen; -typedef void (JitCompiler::*JitFunction)(Instruction instr); +typedef void (JitShader::*JitFunction)(Instruction instr); const JitFunction instr_table[64] = { - &JitCompiler::Compile_ADD, // add - &JitCompiler::Compile_DP3, // dp3 - &JitCompiler::Compile_DP4, // dp4 - &JitCompiler::Compile_DPH, // dph + &JitShader::Compile_ADD, // add + &JitShader::Compile_DP3, // dp3 + &JitShader::Compile_DP4, // dp4 + &JitShader::Compile_DPH, // dph nullptr, // unknown - &JitCompiler::Compile_EX2, // ex2 - &JitCompiler::Compile_LG2, // lg2 + &JitShader::Compile_EX2, // ex2 + &JitShader::Compile_LG2, // lg2 nullptr, // unknown - &JitCompiler::Compile_MUL, // mul - &JitCompiler::Compile_SGE, // sge - &JitCompiler::Compile_SLT, // slt - &JitCompiler::Compile_FLR, // flr - &JitCompiler::Compile_MAX, // max - &JitCompiler::Compile_MIN, // min - &JitCompiler::Compile_RCP, // rcp - &JitCompiler::Compile_RSQ, // rsq + &JitShader::Compile_MUL, // mul + &JitShader::Compile_SGE, // sge + &JitShader::Compile_SLT, // slt + &JitShader::Compile_FLR, // flr + &JitShader::Compile_MAX, // max + &JitShader::Compile_MIN, // min + &JitShader::Compile_RCP, // rcp + &JitShader::Compile_RSQ, // rsq nullptr, // unknown nullptr, // unknown - &JitCompiler::Compile_MOVA, // mova - &JitCompiler::Compile_MOV, // mov + &JitShader::Compile_MOVA, // mova + &JitShader::Compile_MOV, // mov nullptr, // unknown nullptr, // unknown nullptr, // unknown nullptr, // unknown - &JitCompiler::Compile_DPH, // dphi + &JitShader::Compile_DPH, // dphi nullptr, // unknown - &JitCompiler::Compile_SGE, // sgei - &JitCompiler::Compile_SLT, // slti + &JitShader::Compile_SGE, // sgei + &JitShader::Compile_SLT, // slti nullptr, // unknown nullptr, // unknown nullptr, // unknown nullptr, // unknown nullptr, // unknown - &JitCompiler::Compile_NOP, // nop - &JitCompiler::Compile_END, // end + &JitShader::Compile_NOP, // nop + &JitShader::Compile_END, // end nullptr, // break - &JitCompiler::Compile_CALL, // call - &JitCompiler::Compile_CALLC, // callc - &JitCompiler::Compile_CALLU, // callu - &JitCompiler::Compile_IF, // ifu - &JitCompiler::Compile_IF, // ifc - &JitCompiler::Compile_LOOP, // loop + &JitShader::Compile_CALL, // call + &JitShader::Compile_CALLC, // callc + &JitShader::Compile_CALLU, // callu + &JitShader::Compile_IF, // ifu + &JitShader::Compile_IF, // ifc + &JitShader::Compile_LOOP, // loop nullptr, // emit nullptr, // sete - &JitCompiler::Compile_JMP, // jmpc - &JitCompiler::Compile_JMP, // jmpu - &JitCompiler::Compile_CMP, // cmp - &JitCompiler::Compile_CMP, // cmp - &JitCompiler::Compile_MAD, // madi - &JitCompiler::Compile_MAD, // madi - &JitCompiler::Compile_MAD, // madi - &JitCompiler::Compile_MAD, // madi - &JitCompiler::Compile_MAD, // madi - &JitCompiler::Compile_MAD, // madi - &JitCompiler::Compile_MAD, // madi - &JitCompiler::Compile_MAD, // madi - &JitCompiler::Compile_MAD, // mad - &JitCompiler::Compile_MAD, // mad - &JitCompiler::Compile_MAD, // mad - &JitCompiler::Compile_MAD, // mad - &JitCompiler::Compile_MAD, // mad - &JitCompiler::Compile_MAD, // mad - &JitCompiler::Compile_MAD, // mad - &JitCompiler::Compile_MAD, // mad + &JitShader::Compile_JMP, // jmpc + &JitShader::Compile_JMP, // jmpu + &JitShader::Compile_CMP, // cmp + &JitShader::Compile_CMP, // cmp + &JitShader::Compile_MAD, // madi + &JitShader::Compile_MAD, // madi + &JitShader::Compile_MAD, // madi + &JitShader::Compile_MAD, // madi + &JitShader::Compile_MAD, // madi + &JitShader::Compile_MAD, // madi + &JitShader::Compile_MAD, // madi + &JitShader::Compile_MAD, // madi + &JitShader::Compile_MAD, // mad + &JitShader::Compile_MAD, // mad + &JitShader::Compile_MAD, // mad + &JitShader::Compile_MAD, // mad + &JitShader::Compile_MAD, // mad + &JitShader::Compile_MAD, // mad + &JitShader::Compile_MAD, // mad + &JitShader::Compile_MAD, // mad }; // The following is used to alias some commonly used registers. Generally, RAX-RDX and XMM0-XMM3 can @@ -138,13 +139,32 @@ static const u8 NO_SRC_REG_SWIZZLE = 0x1b; static const u8 NO_DEST_REG_MASK = 0xf; /** + * Get the vertex shader instruction for a given offset in the current shader program + * @param offset Offset in the current shader program of the instruction + * @return Instruction at the specified offset + */ +static Instruction GetVertexShaderInstruction(size_t offset) { + return { g_state.vs.program_code[offset] }; +} + +static void LogCritical(const char* msg) { + LOG_CRITICAL(HW_GPU, msg); +} + +void JitShader::Compile_Assert(bool condition, const char* msg) { + if (!condition) { + ABI_CallFunctionP(reinterpret_cast<const void*>(LogCritical), const_cast<char*>(msg)); + } +} + +/** * Loads and swizzles a source register into the specified XMM register. * @param instr VS instruction, used for determining how to load the source register * @param src_num Number indicating which source register to load (1 = src1, 2 = src2, 3 = src3) * @param src_reg SourceRegister object corresponding to the source register to load * @param dest Destination XMM register to store the loaded, swizzled source register */ -void JitCompiler::Compile_SwizzleSrc(Instruction instr, unsigned src_num, SourceRegister src_reg, X64Reg dest) { +void JitShader::Compile_SwizzleSrc(Instruction instr, unsigned src_num, SourceRegister src_reg, X64Reg dest) { X64Reg src_ptr; size_t src_offset; @@ -216,7 +236,7 @@ void JitCompiler::Compile_SwizzleSrc(Instruction instr, unsigned src_num, Source } } -void JitCompiler::Compile_DestEnable(Instruction instr,X64Reg src) { +void JitShader::Compile_DestEnable(Instruction instr,X64Reg src) { DestRegister dest; unsigned operand_desc_id; if (instr.opcode.Value().EffectiveOpCode() == OpCode::Id::MAD || @@ -263,7 +283,7 @@ void JitCompiler::Compile_DestEnable(Instruction instr,X64Reg src) { } } -void JitCompiler::Compile_SanitizedMul(Gen::X64Reg src1, Gen::X64Reg src2, Gen::X64Reg scratch) { +void JitShader::Compile_SanitizedMul(Gen::X64Reg src1, Gen::X64Reg src2, Gen::X64Reg scratch) { MOVAPS(scratch, R(src1)); CMPPS(scratch, R(src2), CMP_ORD); @@ -276,7 +296,7 @@ void JitCompiler::Compile_SanitizedMul(Gen::X64Reg src1, Gen::X64Reg src2, Gen:: ANDPS(src1, R(scratch)); } -void JitCompiler::Compile_EvaluateCondition(Instruction instr) { +void JitShader::Compile_EvaluateCondition(Instruction instr) { // Note: NXOR is used below to check for equality switch (instr.flow_control.op) { case Instruction::FlowControlType::Or: @@ -307,23 +327,23 @@ void JitCompiler::Compile_EvaluateCondition(Instruction instr) { } } -void JitCompiler::Compile_UniformCondition(Instruction instr) { +void JitShader::Compile_UniformCondition(Instruction instr) { int offset = offsetof(decltype(g_state.vs.uniforms), b) + (instr.flow_control.bool_uniform_id * sizeof(bool)); CMP(sizeof(bool) * 8, MDisp(UNIFORMS, offset), Imm8(0)); } -BitSet32 JitCompiler::PersistentCallerSavedRegs() { +BitSet32 JitShader::PersistentCallerSavedRegs() { return persistent_regs & ABI_ALL_CALLER_SAVED; } -void JitCompiler::Compile_ADD(Instruction instr) { +void JitShader::Compile_ADD(Instruction instr) { Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1); Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2); ADDPS(SRC1, R(SRC2)); Compile_DestEnable(instr, SRC1); } -void JitCompiler::Compile_DP3(Instruction instr) { +void JitShader::Compile_DP3(Instruction instr) { Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1); Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2); @@ -342,7 +362,7 @@ void JitCompiler::Compile_DP3(Instruction instr) { Compile_DestEnable(instr, SRC1); } -void JitCompiler::Compile_DP4(Instruction instr) { +void JitShader::Compile_DP4(Instruction instr) { Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1); Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2); @@ -359,7 +379,7 @@ void JitCompiler::Compile_DP4(Instruction instr) { Compile_DestEnable(instr, SRC1); } -void JitCompiler::Compile_DPH(Instruction instr) { +void JitShader::Compile_DPH(Instruction instr) { if (instr.opcode.Value().EffectiveOpCode() == OpCode::Id::DPHI) { Compile_SwizzleSrc(instr, 1, instr.common.src1i, SRC1); Compile_SwizzleSrc(instr, 2, instr.common.src2i, SRC2); @@ -391,7 +411,7 @@ void JitCompiler::Compile_DPH(Instruction instr) { Compile_DestEnable(instr, SRC1); } -void JitCompiler::Compile_EX2(Instruction instr) { +void JitShader::Compile_EX2(Instruction instr) { Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1); MOVSS(XMM0, R(SRC1)); @@ -404,7 +424,7 @@ void JitCompiler::Compile_EX2(Instruction instr) { Compile_DestEnable(instr, SRC1); } -void JitCompiler::Compile_LG2(Instruction instr) { +void JitShader::Compile_LG2(Instruction instr) { Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1); MOVSS(XMM0, R(SRC1)); @@ -417,14 +437,14 @@ void JitCompiler::Compile_LG2(Instruction instr) { Compile_DestEnable(instr, SRC1); } -void JitCompiler::Compile_MUL(Instruction instr) { +void JitShader::Compile_MUL(Instruction instr) { Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1); Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2); Compile_SanitizedMul(SRC1, SRC2, SCRATCH); Compile_DestEnable(instr, SRC1); } -void JitCompiler::Compile_SGE(Instruction instr) { +void JitShader::Compile_SGE(Instruction instr) { if (instr.opcode.Value().EffectiveOpCode() == OpCode::Id::SGEI) { Compile_SwizzleSrc(instr, 1, instr.common.src1i, SRC1); Compile_SwizzleSrc(instr, 2, instr.common.src2i, SRC2); @@ -439,7 +459,7 @@ void JitCompiler::Compile_SGE(Instruction instr) { Compile_DestEnable(instr, SRC2); } -void JitCompiler::Compile_SLT(Instruction instr) { +void JitShader::Compile_SLT(Instruction instr) { if (instr.opcode.Value().EffectiveOpCode() == OpCode::Id::SLTI) { Compile_SwizzleSrc(instr, 1, instr.common.src1i, SRC1); Compile_SwizzleSrc(instr, 2, instr.common.src2i, SRC2); @@ -454,7 +474,7 @@ void JitCompiler::Compile_SLT(Instruction instr) { Compile_DestEnable(instr, SRC1); } -void JitCompiler::Compile_FLR(Instruction instr) { +void JitShader::Compile_FLR(Instruction instr) { Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1); if (Common::GetCPUCaps().sse4_1) { @@ -467,7 +487,7 @@ void JitCompiler::Compile_FLR(Instruction instr) { Compile_DestEnable(instr, SRC1); } -void JitCompiler::Compile_MAX(Instruction instr) { +void JitShader::Compile_MAX(Instruction instr) { Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1); Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2); // SSE semantics match PICA200 ones: In case of NaN, SRC2 is returned. @@ -475,7 +495,7 @@ void JitCompiler::Compile_MAX(Instruction instr) { Compile_DestEnable(instr, SRC1); } -void JitCompiler::Compile_MIN(Instruction instr) { +void JitShader::Compile_MIN(Instruction instr) { Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1); Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2); // SSE semantics match PICA200 ones: In case of NaN, SRC2 is returned. @@ -483,7 +503,7 @@ void JitCompiler::Compile_MIN(Instruction instr) { Compile_DestEnable(instr, SRC1); } -void JitCompiler::Compile_MOVA(Instruction instr) { +void JitShader::Compile_MOVA(Instruction instr) { SwizzlePattern swiz = { g_state.vs.swizzle_data[instr.common.operand_desc_id] }; if (!swiz.DestComponentEnabled(0) && !swiz.DestComponentEnabled(1)) { @@ -528,12 +548,12 @@ void JitCompiler::Compile_MOVA(Instruction instr) { } } -void JitCompiler::Compile_MOV(Instruction instr) { +void JitShader::Compile_MOV(Instruction instr) { Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1); Compile_DestEnable(instr, SRC1); } -void JitCompiler::Compile_RCP(Instruction instr) { +void JitShader::Compile_RCP(Instruction instr) { Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1); // TODO(bunnei): RCPSS is a pretty rough approximation, this might cause problems if Pica @@ -544,7 +564,7 @@ void JitCompiler::Compile_RCP(Instruction instr) { Compile_DestEnable(instr, SRC1); } -void JitCompiler::Compile_RSQ(Instruction instr) { +void JitShader::Compile_RSQ(Instruction instr) { Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1); // TODO(bunnei): RSQRTSS is a pretty rough approximation, this might cause problems if Pica @@ -555,36 +575,41 @@ void JitCompiler::Compile_RSQ(Instruction instr) { Compile_DestEnable(instr, SRC1); } -void JitCompiler::Compile_NOP(Instruction instr) { +void JitShader::Compile_NOP(Instruction instr) { } -void JitCompiler::Compile_END(Instruction instr) { +void JitShader::Compile_END(Instruction instr) { ABI_PopRegistersAndAdjustStack(ABI_ALL_CALLEE_SAVED, 8); RET(); } -void JitCompiler::Compile_CALL(Instruction instr) { - unsigned offset = instr.flow_control.dest_offset; - while (offset < (instr.flow_control.dest_offset + instr.flow_control.num_instructions)) { - Compile_NextInstr(&offset); - } +void JitShader::Compile_CALL(Instruction instr) { + // Push offset of the return + PUSH(64, Imm32(instr.flow_control.dest_offset + instr.flow_control.num_instructions)); + + // Call the subroutine + FixupBranch b = CALL(); + fixup_branches.push_back({ b, instr.flow_control.dest_offset }); + + // Skip over the return offset that's on the stack + ADD(64, R(RSP), Imm32(8)); } -void JitCompiler::Compile_CALLC(Instruction instr) { +void JitShader::Compile_CALLC(Instruction instr) { Compile_EvaluateCondition(instr); FixupBranch b = J_CC(CC_Z, true); Compile_CALL(instr); SetJumpTarget(b); } -void JitCompiler::Compile_CALLU(Instruction instr) { +void JitShader::Compile_CALLU(Instruction instr) { Compile_UniformCondition(instr); FixupBranch b = J_CC(CC_Z, true); Compile_CALL(instr); SetJumpTarget(b); } -void JitCompiler::Compile_CMP(Instruction instr) { +void JitShader::Compile_CMP(Instruction instr) { using Op = Instruction::Common::CompareOpType::Op; Op op_x = instr.common.compare_op.x; Op op_y = instr.common.compare_op.y; @@ -627,7 +652,7 @@ void JitCompiler::Compile_CMP(Instruction instr) { SHR(64, R(COND1), Imm8(63)); } -void JitCompiler::Compile_MAD(Instruction instr) { +void JitShader::Compile_MAD(Instruction instr) { Compile_SwizzleSrc(instr, 1, instr.mad.src1, SRC1); if (instr.opcode.Value().EffectiveOpCode() == OpCode::Id::MADI) { @@ -644,9 +669,8 @@ void JitCompiler::Compile_MAD(Instruction instr) { Compile_DestEnable(instr, SRC1); } -void JitCompiler::Compile_IF(Instruction instr) { - ASSERT_MSG(instr.flow_control.dest_offset > *offset_ptr, "Backwards if-statements (%d -> %d) not supported", - *offset_ptr, instr.flow_control.dest_offset.Value()); +void JitShader::Compile_IF(Instruction instr) { + Compile_Assert(instr.flow_control.dest_offset >= program_counter, "Backwards if-statements not supported"); // Evaluate the "IF" condition if (instr.opcode.Value() == OpCode::Id::IFU) { @@ -676,10 +700,9 @@ void JitCompiler::Compile_IF(Instruction instr) { SetJumpTarget(b2); } -void JitCompiler::Compile_LOOP(Instruction instr) { - ASSERT_MSG(instr.flow_control.dest_offset > *offset_ptr, "Backwards loops (%d -> %d) not supported", - *offset_ptr, instr.flow_control.dest_offset.Value()); - ASSERT_MSG(!looping, "Nested loops not supported"); +void JitShader::Compile_LOOP(Instruction instr) { + Compile_Assert(instr.flow_control.dest_offset >= program_counter, "Backwards loops not supported"); + Compile_Assert(!looping, "Nested loops not supported"); looping = true; @@ -705,10 +728,7 @@ void JitCompiler::Compile_LOOP(Instruction instr) { looping = false; } -void JitCompiler::Compile_JMP(Instruction instr) { - ASSERT_MSG(instr.flow_control.dest_offset > *offset_ptr, "Backwards jumps (%d -> %d) not supported", - *offset_ptr, instr.flow_control.dest_offset.Value()); - +void JitShader::Compile_JMP(Instruction instr) { if (instr.opcode.Value() == OpCode::Id::JMPC) Compile_EvaluateCondition(instr); else if (instr.opcode.Value() == OpCode::Id::JMPU) @@ -718,30 +738,38 @@ void JitCompiler::Compile_JMP(Instruction instr) { bool inverted_condition = (instr.opcode.Value() == OpCode::Id::JMPU) && (instr.flow_control.num_instructions & 1); + FixupBranch b = J_CC(inverted_condition ? CC_Z : CC_NZ, true); + fixup_branches.push_back({ b, instr.flow_control.dest_offset }); +} - Compile_Block(instr.flow_control.dest_offset); +void JitShader::Compile_Block(unsigned end) { + while (program_counter < end) { + Compile_NextInstr(); + } +} + +void JitShader::Compile_Return() { + // Peek return offset on the stack and check if we're at that offset + MOV(64, R(RAX), MDisp(RSP, 8)); + CMP(32, R(RAX), Imm32(program_counter)); + // If so, jump back to before CALL + FixupBranch b = J_CC(CC_NZ, true); + RET(); SetJumpTarget(b); } -void JitCompiler::Compile_Block(unsigned end) { - // Save current offset pointer - unsigned* prev_offset_ptr = offset_ptr; - unsigned offset = *prev_offset_ptr; +void JitShader::Compile_NextInstr() { + if (std::binary_search(return_offsets.begin(), return_offsets.end(), program_counter)) { + Compile_Return(); + } - while (offset < end) - Compile_NextInstr(&offset); + ASSERT_MSG(code_ptr[program_counter] == nullptr, "Tried to compile already compiled shader location!"); + code_ptr[program_counter] = GetCodePtr(); - // Restore current offset pointer - offset_ptr = prev_offset_ptr; - *offset_ptr = offset; -} + Instruction instr = GetVertexShaderInstruction(program_counter++); -void JitCompiler::Compile_NextInstr(unsigned* offset) { - offset_ptr = offset; - - Instruction instr = *(Instruction*)&g_state.vs.program_code[(*offset_ptr)++]; OpCode::Id opcode = instr.opcode.Value(); auto instr_func = instr_table[static_cast<unsigned>(opcode)]; @@ -755,9 +783,35 @@ void JitCompiler::Compile_NextInstr(unsigned* offset) { } } -CompiledShader* JitCompiler::Compile() { - const u8* start = GetCodePtr(); - unsigned offset = g_state.regs.vs.main_offset; +void JitShader::FindReturnOffsets() { + return_offsets.clear(); + + for (size_t offset = 0; offset < g_state.vs.program_code.size(); ++offset) { + Instruction instr = GetVertexShaderInstruction(offset); + + switch (instr.opcode.Value()) { + case OpCode::Id::CALL: + case OpCode::Id::CALLC: + case OpCode::Id::CALLU: + return_offsets.push_back(instr.flow_control.dest_offset + instr.flow_control.num_instructions); + break; + } + } + + // Sort for efficient binary search later + std::sort(return_offsets.begin(), return_offsets.end()); +} + +void JitShader::Compile() { + // Reset flow control state + program = (CompiledShader*)GetCodePtr(); + program_counter = 0; + looping = false; + code_ptr.fill(nullptr); + fixup_branches.clear(); + + // Find all `CALL` instructions and identify return locations + FindReturnOffsets(); // The stack pointer is 8 modulo 16 at the entry of a procedure ABI_PushRegistersAndAdjustStack(ABI_ALL_CALLEE_SAVED, 8); @@ -780,21 +834,31 @@ CompiledShader* JitCompiler::Compile() { MOV(PTRBITS, R(RAX), ImmPtr(&neg)); MOVAPS(NEGBIT, MatR(RAX)); - looping = false; + // Jump to start of the shader program + JMPptr(R(ABI_PARAM2)); + + // Compile entire program + Compile_Block(static_cast<unsigned>(g_state.vs.program_code.size())); - while (offset < g_state.vs.program_code.size()) { - Compile_NextInstr(&offset); + // Set the target for any incomplete branches now that the entire shader program has been emitted + for (const auto& branch : fixup_branches) { + SetJumpTarget(branch.first, code_ptr[branch.second]); } - return (CompiledShader*)start; -} + // Free memory that's no longer needed + return_offsets.clear(); + return_offsets.shrink_to_fit(); + fixup_branches.clear(); + fixup_branches.shrink_to_fit(); + + uintptr_t size = reinterpret_cast<uintptr_t>(GetCodePtr()) - reinterpret_cast<uintptr_t>(program); + ASSERT_MSG(size <= MAX_SHADER_SIZE, "Compiled a shader that exceeds the allocated size!"); -JitCompiler::JitCompiler() { - AllocCodeSpace(jit_cache_size); + LOG_DEBUG(HW_GPU, "Compiled shader size=%d", size); } -void JitCompiler::Clear() { - ClearCodeSpace(); +JitShader::JitShader() { + AllocCodeSpace(MAX_SHADER_SIZE); } } // namespace Shader diff --git a/src/video_core/shader/shader_jit_x64.h b/src/video_core/shader/shader_jit_x64.h index 5357c964b..cd6280ade 100644 --- a/src/video_core/shader/shader_jit_x64.h +++ b/src/video_core/shader/shader_jit_x64.h @@ -4,6 +4,9 @@ #pragma once +#include <utility> +#include <vector> + #include <nihstro/shader_bytecode.h> #include "common/x64/emitter.h" @@ -19,24 +22,22 @@ namespace Pica { namespace Shader { -/// Memory needed to be available to compile the next shader (otherwise, clear the cache) -constexpr size_t jit_shader_size = 1024 * 512; -/// Memory allocated for the JIT code space cache -constexpr size_t jit_cache_size = 1024 * 1024 * 8; - -using CompiledShader = void(void* registers); +/// Memory allocated for each compiled shader (64Kb) +constexpr size_t MAX_SHADER_SIZE = 1024 * 64; /** * This class implements the shader JIT compiler. It recompiles a Pica shader program into x86_64 * code that can be executed on the host machine directly. */ -class JitCompiler : public Gen::XCodeBlock { +class JitShader : public Gen::XCodeBlock { public: - JitCompiler(); + JitShader(); - CompiledShader* Compile(); + void Run(void* registers, unsigned offset) const { + program(registers, code_ptr[offset]); + } - void Clear(); + void Compile(); void Compile_ADD(Instruction instr); void Compile_DP3(Instruction instr); @@ -66,8 +67,9 @@ public: void Compile_MAD(Instruction instr); private: + void Compile_Block(unsigned end); - void Compile_NextInstr(unsigned* offset); + void Compile_NextInstr(); void Compile_SwizzleSrc(Instruction instr, unsigned src_num, SourceRegister src_reg, Gen::X64Reg dest); void Compile_DestEnable(Instruction instr, Gen::X64Reg dest); @@ -81,13 +83,39 @@ private: void Compile_EvaluateCondition(Instruction instr); void Compile_UniformCondition(Instruction instr); + /** + * Emits the code to conditionally return from a subroutine envoked by the `CALL` instruction. + */ + void Compile_Return(); + BitSet32 PersistentCallerSavedRegs(); - /// Pointer to the variable that stores the current Pica code offset. Used to handle nested code blocks. - unsigned* offset_ptr = nullptr; + /** + * Assertion evaluated at compile-time, but only triggered if executed at runtime. + * @param msg Message to be logged if the assertion fails. + */ + void Compile_Assert(bool condition, const char* msg); + + /** + * Analyzes the entire shader program for `CALL` instructions before emitting any code, + * identifying the locations where a return needs to be inserted. + */ + void FindReturnOffsets(); + + /// Mapping of Pica VS instructions to pointers in the emitted code + std::array<const u8*, 1024> code_ptr; + + /// Offsets in code where a return needs to be inserted + std::vector<unsigned> return_offsets; + + unsigned program_counter = 0; ///< Offset of the next instruction to decode + bool looping = false; ///< True if compiling a loop, used to check for nested loops + + /// Branches that need to be fixed up once the entire shader program is compiled + std::vector<std::pair<Gen::FixupBranch, unsigned>> fixup_branches; - /// Set to true if currently in a loop, used to check for the existence of nested loops - bool looping = false; + using CompiledShader = void(void* registers, const u8* start_addr); + CompiledShader* program = nullptr; }; } // Shader diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index ee5e50df1..256899c89 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp @@ -5,7 +5,6 @@ #include <memory> #include "common/emu_window.h" -#include "common/make_unique.h" #include "common/logging/log.h" #include "core/core.h" @@ -32,7 +31,7 @@ bool Init(EmuWindow* emu_window) { Pica::Init(); g_emu_window = emu_window; - g_renderer = Common::make_unique<RendererOpenGL>(); + g_renderer = std::make_unique<RendererOpenGL>(); g_renderer->SetWindow(g_emu_window); if (g_renderer->Init()) { LOG_DEBUG(Render, "initialized OK"); |