summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-x.travis-build.sh4
-rwxr-xr-x.travis-deps.sh4
-rw-r--r--.travis.yml4
-rw-r--r--CMakeLists.txt2
-rw-r--r--src/audio_core/CMakeLists.txt3
-rw-r--r--src/audio_core/hle/common.h35
-rw-r--r--src/audio_core/hle/dsp.h17
-rw-r--r--src/audio_core/hle/filter.cpp115
-rw-r--r--src/audio_core/hle/filter.h112
-rw-r--r--src/citra/citra.cpp36
-rw-r--r--src/citra/config.cpp7
-rw-r--r--src/citra_qt/CMakeLists.txt15
-rw-r--r--src/citra_qt/config.cpp83
-rw-r--r--src/citra_qt/configure.ui97
-rw-r--r--src/citra_qt/configure_debug.cpp31
-rw-r--r--src/citra_qt/configure_debug.h29
-rw-r--r--src/citra_qt/configure_debug.ui102
-rw-r--r--src/citra_qt/configure_dialog.cpp29
-rw-r--r--src/citra_qt/configure_dialog.h29
-rw-r--r--src/citra_qt/configure_general.cpp37
-rw-r--r--src/citra_qt/configure_general.h29
-rw-r--r--src/citra_qt/configure_general.ui166
-rw-r--r--src/citra_qt/game_list.cpp17
-rw-r--r--src/citra_qt/game_list.h4
-rw-r--r--src/citra_qt/hotkeys.cpp54
-rw-r--r--src/citra_qt/hotkeys.h8
-rw-r--r--src/citra_qt/hotkeys.ui47
-rw-r--r--src/citra_qt/main.cpp176
-rw-r--r--src/citra_qt/main.h8
-rw-r--r--src/citra_qt/main.ui51
-rw-r--r--src/citra_qt/ui_settings.cpp11
-rw-r--r--src/citra_qt/ui_settings.h47
-rw-r--r--src/common/CMakeLists.txt1
-rw-r--r--src/common/file_util.cpp94
-rw-r--r--src/common/file_util.h35
-rw-r--r--src/common/logging/backend.cpp3
-rw-r--r--src/common/logging/log.h3
-rw-r--r--src/common/make_unique.h17
-rw-r--r--src/common/string_util.cpp16
-rw-r--r--src/common/string_util.h2
-rw-r--r--src/common/thread.h46
-rw-r--r--src/common/x64/emitter.cpp28
-rw-r--r--src/common/x64/emitter.h2
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/arm/dyncom/arm_dyncom.cpp7
-rw-r--r--src/core/arm/dyncom/arm_dyncom_interpreter.cpp96
-rw-r--r--src/core/core.cpp5
-rw-r--r--src/core/file_sys/archive_extsavedata.cpp4
-rw-r--r--src/core/file_sys/archive_romfs.cpp3
-rw-r--r--src/core/file_sys/archive_savedata.cpp4
-rw-r--r--src/core/file_sys/archive_savedatacheck.cpp4
-rw-r--r--src/core/file_sys/archive_sdmc.cpp4
-rw-r--r--src/core/file_sys/archive_systemsavedata.cpp4
-rw-r--r--src/core/file_sys/disk_archive.cpp8
-rw-r--r--src/core/file_sys/ivfc_archive.cpp5
-rw-r--r--src/core/gdbstub/gdbstub.cpp108
-rw-r--r--src/core/hle/config_mem.cpp7
-rw-r--r--src/core/hle/hle.cpp2
-rw-r--r--src/core/hle/kernel/process.cpp3
-rw-r--r--src/core/hle/result.h1
-rw-r--r--src/core/hle/service/am/am.cpp140
-rw-r--r--src/core/hle/service/am/am.h126
-rw-r--r--src/core/hle/service/am/am_app.cpp16
-rw-r--r--src/core/hle/service/am/am_net.cpp18
-rw-r--r--src/core/hle/service/am/am_sys.cpp22
-rw-r--r--src/core/hle/service/am/am_u.cpp18
-rw-r--r--src/core/hle/service/apt/apt.cpp17
-rw-r--r--src/core/hle/service/apt/apt.h17
-rw-r--r--src/core/hle/service/apt/apt_a.cpp9
-rw-r--r--src/core/hle/service/apt/apt_s.cpp10
-rw-r--r--src/core/hle/service/apt/apt_u.cpp2
-rw-r--r--src/core/hle/service/cecd/cecd.cpp38
-rw-r--r--src/core/hle/service/cecd/cecd.h41
-rw-r--r--src/core/hle/service/cecd/cecd_u.cpp6
-rw-r--r--src/core/hle/service/cfg/cfg_i.cpp12
-rw-r--r--src/core/hle/service/cfg/cfg_s.cpp8
-rw-r--r--src/core/hle/service/cfg/cfg_u.cpp1
-rw-r--r--src/core/hle/service/dlp_srvr.cpp36
-rw-r--r--src/core/hle/service/dlp_srvr.h23
-rw-r--r--src/core/hle/service/frd/frd.cpp91
-rw-r--r--src/core/hle/service/frd/frd.h88
-rw-r--r--src/core/hle/service/frd/frd_u.cpp107
-rw-r--r--src/core/hle/service/fs/archive.cpp13
-rw-r--r--src/core/hle/service/gsp_gpu.cpp221
-rw-r--r--src/core/hle/service/gsp_gpu.h2
-rw-r--r--src/core/hle/service/service.cpp5
-rw-r--r--src/core/hle/shared_page.cpp3
-rw-r--r--src/core/hle/shared_page.h6
-rw-r--r--src/core/hw/y2r.cpp2
-rw-r--r--src/core/loader/3dsx.cpp6
-rw-r--r--src/core/loader/loader.cpp5
-rw-r--r--src/core/loader/ncch.cpp5
-rw-r--r--src/core/settings.cpp14
-rw-r--r--src/core/settings.h2
-rw-r--r--src/video_core/command_processor.cpp23
-rw-r--r--src/video_core/debug_utils/debug_utils.cpp48
-rw-r--r--src/video_core/debug_utils/debug_utils.h21
-rw-r--r--src/video_core/pica.h12
-rw-r--r--src/video_core/primitive_assembly.cpp2
-rw-r--r--src/video_core/rasterizer.cpp107
-rw-r--r--src/video_core/renderer_base.cpp6
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp73
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h9
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp5
-rw-r--r--src/video_core/renderer_opengl/pica_to_gl.h12
-rw-r--r--src/video_core/shader/shader.cpp35
-rw-r--r--src/video_core/shader/shader.h3
-rw-r--r--src/video_core/shader/shader_jit_x64.cpp302
-rw-r--r--src/video_core/shader/shader_jit_x64.h58
-rw-r--r--src/video_core/video_core.cpp3
110 files changed, 2714 insertions, 1058 deletions
diff --git a/.travis-build.sh b/.travis-build.sh
index 22a3a9fd6..e06a4299b 100755
--- a/.travis-build.sh
+++ b/.travis-build.sh
@@ -11,8 +11,8 @@ fi
11 11
12#if OS is linux or is not set 12#if OS is linux or is not set
13if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then 13if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then
14 export CC=gcc-4.9 14 export CC=gcc-5
15 export CXX=g++-4.9 15 export CXX=g++-5
16 export PKG_CONFIG_PATH=$HOME/.local/lib/pkgconfig:$PKG_CONFIG_PATH 16 export PKG_CONFIG_PATH=$HOME/.local/lib/pkgconfig:$PKG_CONFIG_PATH
17 17
18 mkdir build && cd build 18 mkdir build && cd build
diff --git a/.travis-deps.sh b/.travis-deps.sh
index eb99ead4f..c7bb7e785 100755
--- a/.travis-deps.sh
+++ b/.travis-deps.sh
@@ -5,8 +5,8 @@ set -x
5 5
6#if OS is linux or is not set 6#if OS is linux or is not set
7if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then 7if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then
8 export CC=gcc-4.9 8 export CC=gcc-5
9 export CXX=g++-4.9 9 export CXX=g++-5
10 mkdir -p $HOME/.local 10 mkdir -p $HOME/.local
11 11
12 curl -L http://www.cmake.org/files/v2.8/cmake-2.8.11-Linux-i386.tar.gz \ 12 curl -L http://www.cmake.org/files/v2.8/cmake-2.8.11-Linux-i386.tar.gz \
diff --git a/.travis.yml b/.travis.yml
index 2e875cccf..8d86baece 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -15,8 +15,8 @@ addons:
15 sources: 15 sources:
16 - ubuntu-toolchain-r-test 16 - ubuntu-toolchain-r-test
17 packages: 17 packages:
18 - gcc-4.9 18 - gcc-5
19 - g++-4.9 19 - g++-5
20 - xorg-dev 20 - xorg-dev
21 - lib32stdc++6 # For CMake 21 - lib32stdc++6 # For CMake
22 - lftp # To upload builds 22 - lftp # To upload builds
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d6a4a915a..3a0a161e7 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -65,7 +65,7 @@ endif()
65message(STATUS "Target architecture: ${ARCHITECTURE}") 65message(STATUS "Target architecture: ${ARCHITECTURE}")
66 66
67if (NOT MSVC) 67if (NOT MSVC)
68 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wno-attributes -pthread") 68 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++1y -Wno-attributes -pthread")
69 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread") 69 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread")
70 70
71 if (ARCHITECTURE_x86_64) 71 if (ARCHITECTURE_x86_64)
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
2 audio_core.cpp 2 audio_core.cpp
3 codec.cpp 3 codec.cpp
4 hle/dsp.cpp 4 hle/dsp.cpp
5 hle/filter.cpp
5 hle/pipe.cpp 6 hle/pipe.cpp
6 ) 7 )
7 8
8set(HEADERS 9set(HEADERS
9 audio_core.h 10 audio_core.h
10 codec.h 11 codec.h
12 hle/common.h
11 hle/dsp.h 13 hle/dsp.h
14 hle/filter.h
12 hle/pipe.h 15 hle/pipe.h
13 sink.h 16 sink.h
14 ) 17 )
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 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <algorithm>
8#include <array>
9
10#include "audio_core/audio_core.h"
11
12#include "common/common_types.h"
13
14namespace DSP {
15namespace HLE {
16
17/// The final output to the speakers is stereo. Preprocessing output in Source is also stereo.
18using StereoFrame16 = std::array<std::array<s16, 2>, AudioCore::samples_per_frame>;
19
20/// The DSP is quadraphonic internally.
21using QuadFrame32 = std::array<std::array<s32, 4>, AudioCore::samples_per_frame>;
22
23/**
24 * This performs the filter operation defined by FilterT::ProcessSample on the frame in-place.
25 * FilterT::ProcessSample is called sequentially on the samples.
26 */
27template<typename FrameT, typename FilterT>
28void FilterFrame(FrameT& frame, FilterT& filter) {
29 std::transform(frame.begin(), frame.end(), frame.begin(), [&filter](const typename FrameT::value_type& sample) {
30 return filter.ProcessSample(sample);
31 });
32}
33
34} // namespace HLE
35} // 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 {
126 union { 126 union {
127 u32_le dirty_raw; 127 u32_le dirty_raw;
128 128
129 BitField<0, 1, u32_le> format_dirty;
130 BitField<1, 1, u32_le> mono_or_stereo_dirty;
129 BitField<2, 1, u32_le> adpcm_coefficients_dirty; 131 BitField<2, 1, u32_le> adpcm_coefficients_dirty;
130 BitField<3, 1, u32_le> partial_embedded_buffer_dirty; ///< Tends to be set when a looped buffer is queued. 132 BitField<3, 1, u32_le> partial_embedded_buffer_dirty; ///< Tends to be set when a looped buffer is queued.
133 BitField<4, 1, u32_le> partial_reset_flag;
131 134
132 BitField<16, 1, u32_le> enable_dirty; 135 BitField<16, 1, u32_le> enable_dirty;
133 BitField<17, 1, u32_le> interpolation_dirty; 136 BitField<17, 1, u32_le> interpolation_dirty;
@@ -143,8 +146,7 @@ struct SourceConfiguration {
143 BitField<27, 1, u32_le> gain_2_dirty; 146 BitField<27, 1, u32_le> gain_2_dirty;
144 BitField<28, 1, u32_le> sync_dirty; 147 BitField<28, 1, u32_le> sync_dirty;
145 BitField<29, 1, u32_le> reset_flag; 148 BitField<29, 1, u32_le> reset_flag;
146 149 BitField<30, 1, u32_le> embedded_buffer_dirty;
147 BitField<31, 1, u32_le> embedded_buffer_dirty;
148 }; 150 };
149 151
150 // Gain control 152 // Gain control
@@ -175,7 +177,8 @@ struct SourceConfiguration {
175 /** 177 /**
176 * This is the simplest normalized first-order digital recursive filter. 178 * This is the simplest normalized first-order digital recursive filter.
177 * The transfer function of this filter is: 179 * The transfer function of this filter is:
178 * H(z) = b0 / (1 + a1 z^-1) 180 * H(z) = b0 / (1 - a1 z^-1)
181 * Note the feedbackward coefficient is negated.
179 * Values are signed fixed point with 15 fractional bits. 182 * Values are signed fixed point with 15 fractional bits.
180 */ 183 */
181 struct SimpleFilter { 184 struct SimpleFilter {
@@ -192,11 +195,11 @@ struct SourceConfiguration {
192 * Values are signed fixed point with 14 fractional bits. 195 * Values are signed fixed point with 14 fractional bits.
193 */ 196 */
194 struct BiquadFilter { 197 struct BiquadFilter {
195 s16_le b0;
196 s16_le b1;
197 s16_le b2;
198 s16_le a1;
199 s16_le a2; 198 s16_le a2;
199 s16_le a1;
200 s16_le b2;
201 s16_le b1;
202 s16_le b0;
200 }; 203 };
201 204
202 union { 205 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 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <array>
6#include <cstddef>
7
8#include "audio_core/hle/common.h"
9#include "audio_core/hle/dsp.h"
10#include "audio_core/hle/filter.h"
11
12#include "common/common_types.h"
13#include "common/math_util.h"
14
15namespace DSP {
16namespace HLE {
17
18void SourceFilters::Reset() {
19 Enable(false, false);
20}
21
22void SourceFilters::Enable(bool simple, bool biquad) {
23 simple_filter_enabled = simple;
24 biquad_filter_enabled = biquad;
25
26 if (!simple)
27 simple_filter.Reset();
28 if (!biquad)
29 biquad_filter.Reset();
30}
31
32void SourceFilters::Configure(SourceConfiguration::Configuration::SimpleFilter config) {
33 simple_filter.Configure(config);
34}
35
36void SourceFilters::Configure(SourceConfiguration::Configuration::BiquadFilter config) {
37 biquad_filter.Configure(config);
38}
39
40void SourceFilters::ProcessFrame(StereoFrame16& frame) {
41 if (!simple_filter_enabled && !biquad_filter_enabled)
42 return;
43
44 if (simple_filter_enabled) {
45 FilterFrame(frame, simple_filter);
46 }
47
48 if (biquad_filter_enabled) {
49 FilterFrame(frame, biquad_filter);
50 }
51}
52
53// SimpleFilter
54
55void SourceFilters::SimpleFilter::Reset() {
56 y1.fill(0);
57 // Configure as passthrough.
58 a1 = 0;
59 b0 = 1 << 15;
60}
61
62void SourceFilters::SimpleFilter::Configure(SourceConfiguration::Configuration::SimpleFilter config) {
63 a1 = config.a1;
64 b0 = config.b0;
65}
66
67std::array<s16, 2> SourceFilters::SimpleFilter::ProcessSample(const std::array<s16, 2>& x0) {
68 std::array<s16, 2> y0;
69 for (size_t i = 0; i < 2; i++) {
70 const s32 tmp = (b0 * x0[i] + a1 * y1[i]) >> 15;
71 y0[i] = MathUtil::Clamp(tmp, -32768, 32767);
72 }
73
74 y1 = y0;
75
76 return y0;
77}
78
79// BiquadFilter
80
81void SourceFilters::BiquadFilter::Reset() {
82 x1.fill(0);
83 x2.fill(0);
84 y1.fill(0);
85 y2.fill(0);
86 // Configure as passthrough.
87 a1 = a2 = b1 = b2 = 0;
88 b0 = 1 << 14;
89}
90
91void SourceFilters::BiquadFilter::Configure(SourceConfiguration::Configuration::BiquadFilter config) {
92 a1 = config.a1;
93 a2 = config.a2;
94 b0 = config.b0;
95 b1 = config.b1;
96 b2 = config.b2;
97}
98
99std::array<s16, 2> SourceFilters::BiquadFilter::ProcessSample(const std::array<s16, 2>& x0) {
100 std::array<s16, 2> y0;
101 for (size_t i = 0; i < 2; i++) {
102 const s32 tmp = (b0 * x0[i] + b1 * x1[i] + b2 * x2[i] + a1 * y1[i] + a2 * y2[i]) >> 14;
103 y0[i] = MathUtil::Clamp(tmp, -32768, 32767);
104 }
105
106 x2 = x1;
107 x1 = x0;
108 y2 = y1;
109 y1 = y0;
110
111 return y0;
112}
113
114} // namespace HLE
115} // 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 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8
9#include "audio_core/hle/common.h"
10#include "audio_core/hle/dsp.h"
11
12#include "common/common_types.h"
13
14namespace DSP {
15namespace HLE {
16
17/// Preprocessing filters. There is an independent set of filters for each Source.
18class SourceFilters final {
19 SourceFilters() { Reset(); }
20
21 /// Reset internal state.
22 void Reset();
23
24 /**
25 * Enable/Disable filters
26 * See also: SourceConfiguration::Configuration::simple_filter_enabled,
27 * SourceConfiguration::Configuration::biquad_filter_enabled.
28 * @param simple If true, enables the simple filter. If false, disables it.
29 * @param simple If true, enables the biquad filter. If false, disables it.
30 */
31 void Enable(bool simple, bool biquad);
32
33 /**
34 * Configure simple filter.
35 * @param config Configuration from DSP shared memory.
36 */
37 void Configure(SourceConfiguration::Configuration::SimpleFilter config);
38
39 /**
40 * Configure biquad filter.
41 * @param config Configuration from DSP shared memory.
42 */
43 void Configure(SourceConfiguration::Configuration::BiquadFilter config);
44
45 /**
46 * Processes a frame in-place.
47 * @param frame Audio samples to process. Modified in-place.
48 */
49 void ProcessFrame(StereoFrame16& frame);
50
51private:
52 bool simple_filter_enabled;
53 bool biquad_filter_enabled;
54
55 struct SimpleFilter {
56 SimpleFilter() { Reset(); }
57
58 /// Resets internal state.
59 void Reset();
60
61 /**
62 * Configures this filter with application settings.
63 * @param config Configuration from DSP shared memory.
64 */
65 void Configure(SourceConfiguration::Configuration::SimpleFilter config);
66
67 /**
68 * Processes a single stereo PCM16 sample.
69 * @param x0 Input sample
70 * @return Output sample
71 */
72 std::array<s16, 2> ProcessSample(const std::array<s16, 2>& x0);
73
74 private:
75 // Configuration
76 s32 a1, b0;
77 // Internal state
78 std::array<s16, 2> y1;
79 } simple_filter;
80
81 struct BiquadFilter {
82 BiquadFilter() { Reset(); }
83
84 /// Resets internal state.
85 void Reset();
86
87 /**
88 * Configures this filter with application settings.
89 * @param config Configuration from DSP shared memory.
90 */
91 void Configure(SourceConfiguration::Configuration::BiquadFilter config);
92
93 /**
94 * Processes a single stereo PCM16 sample.
95 * @param x0 Input sample
96 * @return Output sample
97 */
98 std::array<s16, 2> ProcessSample(const std::array<s16, 2>& x0);
99
100 private:
101 // Configuration
102 s32 a1, a2, b0, b1, b2;
103 // Internal state
104 std::array<s16, 2> x1;
105 std::array<s16, 2> x2;
106 std::array<s16, 2> y1;
107 std::array<s16, 2> y2;
108 } biquad_filter;
109};
110
111} // namespace HLE
112} // 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 @@
5#include <string> 5#include <string>
6#include <thread> 6#include <thread>
7#include <iostream> 7#include <iostream>
8#include <memory>
8 9
9// This needs to be included before getopt.h because the latter #defines symbols used by it 10// This needs to be included before getopt.h because the latter #defines symbols used by it
10#include "common/microprofile.h" 11#include "common/microprofile.h"
@@ -19,7 +20,6 @@
19#include "common/logging/log.h" 20#include "common/logging/log.h"
20#include "common/logging/backend.h" 21#include "common/logging/backend.h"
21#include "common/logging/filter.h" 22#include "common/logging/filter.h"
22#include "common/make_unique.h"
23#include "common/scope_exit.h" 23#include "common/scope_exit.h"
24 24
25#include "core/settings.h" 25#include "core/settings.h"
@@ -36,25 +36,43 @@
36 36
37static void PrintHelp() 37static void PrintHelp()
38{ 38{
39 std::cout << "Usage: citra <filename>" << std::endl; 39 std::cout << "Usage: citra [options] <filename>" << std::endl;
40 std::cout << "--help, -h Display this information" << std::endl;
41 std::cout << "--gdbport, -g number Enable gdb stub on port number" << std::endl;
40} 42}
41 43
42/// Application entry point 44/// Application entry point
43int main(int argc, char **argv) { 45int main(int argc, char **argv) {
46 Config config;
44 int option_index = 0; 47 int option_index = 0;
48 bool use_gdbstub = Settings::values.use_gdbstub;
49 u32 gdb_port = static_cast<u32>(Settings::values.gdbstub_port);
50 char *endarg;
45 std::string boot_filename; 51 std::string boot_filename;
52
46 static struct option long_options[] = { 53 static struct option long_options[] = {
47 { "help", no_argument, 0, 'h' }, 54 { "help", no_argument, 0, 'h' },
55 { "gdbport", required_argument, 0, 'g' },
48 { 0, 0, 0, 0 } 56 { 0, 0, 0, 0 }
49 }; 57 };
50 58
51 while (optind < argc) { 59 while (optind < argc) {
52 char arg = getopt_long(argc, argv, ":h", long_options, &option_index); 60 char arg = getopt_long(argc, argv, ":hg:", long_options, &option_index);
53 if (arg != -1) { 61 if (arg != -1) {
54 switch (arg) { 62 switch (arg) {
55 case 'h': 63 case 'h':
56 PrintHelp(); 64 PrintHelp();
57 return 0; 65 return 0;
66 case 'g':
67 errno = 0;
68 gdb_port = strtoul(optarg, &endarg, 0);
69 use_gdbstub = true;
70 if (endarg == optarg) errno = EINVAL;
71 if (errno != 0) {
72 perror("--gdbport");
73 exit(1);
74 }
75 break;
58 } 76 }
59 } else { 77 } else {
60 boot_filename = argv[optind]; 78 boot_filename = argv[optind];
@@ -73,16 +91,14 @@ int main(int argc, char **argv) {
73 return -1; 91 return -1;
74 } 92 }
75 93
76 Config config;
77 log_filter.ParseFilterString(Settings::values.log_filter); 94 log_filter.ParseFilterString(Settings::values.log_filter);
78 95
79 GDBStub::ToggleServer(Settings::values.use_gdbstub); 96 // Apply the command line arguments
80 GDBStub::SetServerPort(static_cast<u32>(Settings::values.gdbstub_port)); 97 Settings::values.gdbstub_port = gdb_port;
81 98 Settings::values.use_gdbstub = use_gdbstub;
82 std::unique_ptr<EmuWindow_SDL2> emu_window = Common::make_unique<EmuWindow_SDL2>(); 99 Settings::Apply();
83 100
84 VideoCore::g_hw_renderer_enabled = Settings::values.use_hw_renderer; 101 std::unique_ptr<EmuWindow_SDL2> emu_window = std::make_unique<EmuWindow_SDL2>();
85 VideoCore::g_shader_jit_enabled = Settings::values.use_shader_jit;
86 102
87 System::Init(emu_window.get()); 103 System::Init(emu_window.get());
88 SCOPE_EXIT({ System::Shutdown(); }); 104 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 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <memory>
6
5#include <inih/cpp/INIReader.h> 7#include <inih/cpp/INIReader.h>
6 8
7#include <SDL.h> 9#include <SDL.h>
@@ -10,7 +12,6 @@
10 12
11#include "common/file_util.h" 13#include "common/file_util.h"
12#include "common/logging/log.h" 14#include "common/logging/log.h"
13#include "common/make_unique.h"
14 15
15#include "core/settings.h" 16#include "core/settings.h"
16 17
@@ -19,7 +20,7 @@
19Config::Config() { 20Config::Config() {
20 // TODO: Don't hardcode the path; let the frontend decide where to put the config files. 21 // TODO: Don't hardcode the path; let the frontend decide where to put the config files.
21 sdl2_config_loc = FileUtil::GetUserPath(D_CONFIG_IDX) + "sdl2-config.ini"; 22 sdl2_config_loc = FileUtil::GetUserPath(D_CONFIG_IDX) + "sdl2-config.ini";
22 sdl2_config = Common::make_unique<INIReader>(sdl2_config_loc); 23 sdl2_config = std::make_unique<INIReader>(sdl2_config_loc);
23 24
24 Reload(); 25 Reload();
25} 26}
@@ -31,7 +32,7 @@ bool Config::LoadINI(const std::string& default_contents, bool retry) {
31 LOG_WARNING(Config, "Failed to load %s. Creating file from defaults...", location); 32 LOG_WARNING(Config, "Failed to load %s. Creating file from defaults...", location);
32 FileUtil::CreateFullPath(location); 33 FileUtil::CreateFullPath(location);
33 FileUtil::WriteStringToFile(true, default_contents, location); 34 FileUtil::WriteStringToFile(true, default_contents, location);
34 sdl2_config = Common::make_unique<INIReader>(location); // Reopen file 35 sdl2_config = std::make_unique<INIReader>(location); // Reopen file
35 36
36 return LoadINI(default_contents, false); 37 return LoadINI(default_contents, false);
37 } 38 }
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
17 debugger/profiler.cpp 17 debugger/profiler.cpp
18 debugger/ramview.cpp 18 debugger/ramview.cpp
19 debugger/registers.cpp 19 debugger/registers.cpp
20 game_list.cpp
21 util/spinbox.cpp 20 util/spinbox.cpp
22 util/util.cpp 21 util/util.cpp
23 bootmanager.cpp 22 bootmanager.cpp
23 configure_debug.cpp
24 configure_dialog.cpp
25 configure_general.cpp
26 game_list.cpp
24 hotkeys.cpp 27 hotkeys.cpp
25 main.cpp 28 main.cpp
29 ui_settings.cpp
26 citra-qt.rc 30 citra-qt.rc
27 Info.plist 31 Info.plist
28 ) 32 )
@@ -44,12 +48,16 @@ set(HEADERS
44 debugger/profiler.h 48 debugger/profiler.h
45 debugger/ramview.h 49 debugger/ramview.h
46 debugger/registers.h 50 debugger/registers.h
47 game_list.h
48 util/spinbox.h 51 util/spinbox.h
49 util/util.h 52 util/util.h
50 bootmanager.h 53 bootmanager.h
54 configure_debug.h
55 configure_dialog.h
56 configure_general.h
57 game_list.h
51 hotkeys.h 58 hotkeys.h
52 main.h 59 main.h
60 ui_settings.h
53 version.h 61 version.h
54 ) 62 )
55 63
@@ -59,6 +67,9 @@ set(UIS
59 debugger/disassembler.ui 67 debugger/disassembler.ui
60 debugger/profiler.ui 68 debugger/profiler.ui
61 debugger/registers.ui 69 debugger/registers.ui
70 configure.ui
71 configure_debug.ui
72 configure_general.ui
62 hotkeys.ui 73 hotkeys.ui
63 main.ui 74 main.ui
64 ) 75 )
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 @@
7#include <QStringList> 7#include <QStringList>
8 8
9#include "citra_qt/config.h" 9#include "citra_qt/config.h"
10#include "citra_qt/ui_settings.h"
10 11
11#include "common/file_util.h" 12#include "common/file_util.h"
12#include "core/settings.h" 13#include "core/settings.h"
13 14
14Config::Config() { 15Config::Config() {
15
16 // TODO: Don't hardcode the path; let the frontend decide where to put the config files. 16 // TODO: Don't hardcode the path; let the frontend decide where to put the config files.
17 qt_config_loc = FileUtil::GetUserPath(D_CONFIG_IDX) + "qt-config.ini"; 17 qt_config_loc = FileUtil::GetUserPath(D_CONFIG_IDX) + "qt-config.ini";
18 FileUtil::CreateFullPath(qt_config_loc); 18 FileUtil::CreateFullPath(qt_config_loc);
19 qt_config = new QSettings(QString::fromLocal8Bit(qt_config_loc.c_str()), QSettings::IniFormat); 19 qt_config = new QSettings(QString::fromStdString(qt_config_loc), QSettings::IniFormat);
20 20
21 Reload(); 21 Reload();
22} 22}
@@ -67,6 +67,51 @@ void Config::ReadValues() {
67 Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool(); 67 Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool();
68 Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt(); 68 Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt();
69 qt_config->endGroup(); 69 qt_config->endGroup();
70
71 qt_config->beginGroup("UI");
72
73 qt_config->beginGroup("UILayout");
74 UISettings::values.geometry = qt_config->value("geometry").toByteArray();
75 UISettings::values.state = qt_config->value("state").toByteArray();
76 UISettings::values.renderwindow_geometry = qt_config->value("geometryRenderWindow").toByteArray();
77 UISettings::values.gamelist_header_state = qt_config->value("gameListHeaderState").toByteArray();
78 UISettings::values.microprofile_geometry = qt_config->value("microProfileDialogGeometry").toByteArray();
79 UISettings::values.microprofile_visible = qt_config->value("microProfileDialogVisible", false).toBool();
80 qt_config->endGroup();
81
82 qt_config->beginGroup("Paths");
83 UISettings::values.roms_path = qt_config->value("romsPath").toString();
84 UISettings::values.symbols_path = qt_config->value("symbolsPath").toString();
85 UISettings::values.gamedir = qt_config->value("gameListRootDir", ".").toString();
86 UISettings::values.gamedir_deepscan = qt_config->value("gameListDeepScan", false).toBool();
87 UISettings::values.recent_files = qt_config->value("recentFiles").toStringList();
88 qt_config->endGroup();
89
90 qt_config->beginGroup("Shortcuts");
91 QStringList groups = qt_config->childGroups();
92 for (auto group : groups) {
93 qt_config->beginGroup(group);
94
95 QStringList hotkeys = qt_config->childGroups();
96 for (auto hotkey : hotkeys) {
97 qt_config->beginGroup(hotkey);
98 UISettings::values.shortcuts.emplace_back(
99 UISettings::Shortcut(group + "/" + hotkey,
100 UISettings::ContextualShortcut(qt_config->value("KeySeq").toString(),
101 qt_config->value("Context").toInt())));
102 qt_config->endGroup();
103 }
104
105 qt_config->endGroup();
106 }
107 qt_config->endGroup();
108
109 UISettings::values.single_window_mode = qt_config->value("singleWindowMode", true).toBool();
110 UISettings::values.display_titlebar = qt_config->value("displayTitleBars", true).toBool();
111 UISettings::values.confirm_before_closing = qt_config->value("confirmClose",true).toBool();
112 UISettings::values.first_start = qt_config->value("firstStart", true).toBool();
113
114 qt_config->endGroup();
70} 115}
71 116
72void Config::SaveValues() { 117void Config::SaveValues() {
@@ -107,10 +152,44 @@ void Config::SaveValues() {
107 qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub); 152 qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub);
108 qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port); 153 qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port);
109 qt_config->endGroup(); 154 qt_config->endGroup();
155
156 qt_config->beginGroup("UI");
157
158 qt_config->beginGroup("UILayout");
159 qt_config->setValue("geometry", UISettings::values.geometry);
160 qt_config->setValue("state", UISettings::values.state);
161 qt_config->setValue("geometryRenderWindow", UISettings::values.renderwindow_geometry);
162 qt_config->setValue("gameListHeaderState", UISettings::values.gamelist_header_state);
163 qt_config->setValue("microProfileDialogGeometry", UISettings::values.microprofile_geometry);
164 qt_config->setValue("microProfileDialogVisible", UISettings::values.microprofile_visible);
165 qt_config->endGroup();
166
167 qt_config->beginGroup("Paths");
168 qt_config->setValue("romsPath", UISettings::values.roms_path);
169 qt_config->setValue("symbolsPath", UISettings::values.symbols_path);
170 qt_config->setValue("gameListRootDir", UISettings::values.gamedir);
171 qt_config->setValue("gameListDeepScan", UISettings::values.gamedir_deepscan);
172 qt_config->setValue("recentFiles", UISettings::values.recent_files);
173 qt_config->endGroup();
174
175 qt_config->beginGroup("Shortcuts");
176 for (auto shortcut : UISettings::values.shortcuts ) {
177 qt_config->setValue(shortcut.first + "/KeySeq", shortcut.second.first);
178 qt_config->setValue(shortcut.first + "/Context", shortcut.second.second);
179 }
180 qt_config->endGroup();
181
182 qt_config->setValue("singleWindowMode", UISettings::values.single_window_mode);
183 qt_config->setValue("displayTitleBars", UISettings::values.display_titlebar);
184 qt_config->setValue("confirmClose", UISettings::values.confirm_before_closing);
185 qt_config->setValue("firstStart", UISettings::values.first_start);
186
187 qt_config->endGroup();
110} 188}
111 189
112void Config::Reload() { 190void Config::Reload() {
113 ReadValues(); 191 ReadValues();
192 Settings::Apply();
114} 193}
115 194
116void Config::Save() { 195void 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 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureDialog</class>
4 <widget class="QDialog" name="ConfigureDialog">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>441</width>
10 <height>501</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Citra Configuration</string>
15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout">
17 <item>
18 <widget class="QTabWidget" name="tabWidget">
19 <property name="currentIndex">
20 <number>0</number>
21 </property>
22 <widget class="ConfigureGeneral" name="generalTab">
23 <attribute name="title">
24 <string>General</string>
25 </attribute>
26 </widget>
27 <widget class="QWidget" name="inputTab">
28 <attribute name="title">
29 <string>Input</string>
30 </attribute>
31 </widget>
32 <widget class="ConfigureDebug" name="debugTab">
33 <attribute name="title">
34 <string>Debug</string>
35 </attribute>
36 </widget>
37 </widget>
38 </item>
39 <item>
40 <widget class="QDialogButtonBox" name="buttonBox">
41 <property name="standardButtons">
42 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
43 </property>
44 </widget>
45 </item>
46 </layout>
47 </widget>
48 <customwidgets>
49 <customwidget>
50 <class>ConfigureGeneral</class>
51 <extends>QWidget</extends>
52 <header>configure_general.h</header>
53 <container>1</container>
54 </customwidget>
55 <customwidget>
56 <class>ConfigureDebug</class>
57 <extends>QWidget</extends>
58 <header>configure_debug.h</header>
59 <container>1</container>
60 </customwidget>
61 </customwidgets>
62 <resources/>
63 <connections>
64 <connection>
65 <sender>buttonBox</sender>
66 <signal>accepted()</signal>
67 <receiver>ConfigureDialog</receiver>
68 <slot>accept()</slot>
69 <hints>
70 <hint type="sourcelabel">
71 <x>220</x>
72 <y>380</y>
73 </hint>
74 <hint type="destinationlabel">
75 <x>220</x>
76 <y>200</y>
77 </hint>
78 </hints>
79 </connection>
80 <connection>
81 <sender>buttonBox</sender>
82 <signal>rejected()</signal>
83 <receiver>ConfigureDialog</receiver>
84 <slot>reject()</slot>
85 <hints>
86 <hint type="sourcelabel">
87 <x>220</x>
88 <y>380</y>
89 </hint>
90 <hint type="destinationlabel">
91 <x>220</x>
92 <y>200</y>
93 </hint>
94 </hints>
95 </connection>
96 </connections>
97</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 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "citra_qt/configure_debug.h"
6#include "ui_configure_debug.h"
7
8#include "core/settings.h"
9
10ConfigureDebug::ConfigureDebug(QWidget *parent) :
11 QWidget(parent),
12 ui(new Ui::ConfigureDebug)
13{
14 ui->setupUi(this);
15 this->setConfiguration();
16}
17
18ConfigureDebug::~ConfigureDebug() {
19}
20
21void ConfigureDebug::setConfiguration() {
22 ui->toogle_gdbstub->setChecked(Settings::values.use_gdbstub);
23 ui->gdbport_spinbox->setEnabled(Settings::values.use_gdbstub);
24 ui->gdbport_spinbox->setValue(Settings::values.gdbstub_port);
25}
26
27void ConfigureDebug::applyConfiguration() {
28 Settings::values.use_gdbstub = ui->toogle_gdbstub->isChecked();
29 Settings::values.gdbstub_port = ui->gdbport_spinbox->value();
30 Settings::Apply();
31}
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 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <QWidget>
9
10namespace Ui {
11class ConfigureDebug;
12}
13
14class ConfigureDebug : public QWidget
15{
16 Q_OBJECT
17
18public:
19 explicit ConfigureDebug(QWidget *parent = nullptr);
20 ~ConfigureDebug();
21
22 void applyConfiguration();
23
24private:
25 void setConfiguration();
26
27private:
28 std::unique_ptr<Ui::ConfigureDebug> ui;
29};
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 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureDebug</class>
4 <widget class="QWidget" name="ConfigureDebug">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>400</width>
10 <height>300</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Form</string>
15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout">
17 <item>
18 <layout class="QVBoxLayout" name="verticalLayout_3">
19 <item>
20 <widget class="QGroupBox" name="groupBox">
21 <property name="title">
22 <string>GDB</string>
23 </property>
24 <layout class="QVBoxLayout" name="verticalLayout_2">
25 <item>
26 <layout class="QHBoxLayout" name="horizontalLayout_3">
27 <item>
28 <widget class="QCheckBox" name="toogle_gdbstub">
29 <property name="text">
30 <string>Enable GDB Stub</string>
31 </property>
32 </widget>
33 </item>
34 <item>
35 <spacer name="horizontalSpacer">
36 <property name="orientation">
37 <enum>Qt::Horizontal</enum>
38 </property>
39 <property name="sizeHint" stdset="0">
40 <size>
41 <width>40</width>
42 <height>20</height>
43 </size>
44 </property>
45 </spacer>
46 </item>
47 <item>
48 <widget class="QLabel" name="label">
49 <property name="text">
50 <string>Port:</string>
51 </property>
52 </widget>
53 </item>
54 <item>
55 <widget class="QSpinBox" name="gdbport_spinbox">
56 <property name="maximum">
57 <number>65536</number>
58 </property>
59 </widget>
60 </item>
61 </layout>
62 </item>
63 </layout>
64 </widget>
65 </item>
66 </layout>
67 </item>
68 <item>
69 <spacer name="verticalSpacer">
70 <property name="orientation">
71 <enum>Qt::Vertical</enum>
72 </property>
73 <property name="sizeHint" stdset="0">
74 <size>
75 <width>20</width>
76 <height>40</height>
77 </size>
78 </property>
79 </spacer>
80 </item>
81 </layout>
82 </widget>
83 <resources/>
84 <connections>
85 <connection>
86 <sender>toogle_gdbstub</sender>
87 <signal>toggled(bool)</signal>
88 <receiver>gdbport_spinbox</receiver>
89 <slot>setEnabled(bool)</slot>
90 <hints>
91 <hint type="sourcelabel">
92 <x>84</x>
93 <y>157</y>
94 </hint>
95 <hint type="destinationlabel">
96 <x>342</x>
97 <y>158</y>
98 </hint>
99 </hints>
100 </connection>
101 </connections>
102</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 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "citra_qt/config.h"
6#include "citra_qt/configure_dialog.h"
7#include "ui_configure.h"
8
9
10#include "core/settings.h"
11
12ConfigureDialog::ConfigureDialog(QWidget *parent) :
13 QDialog(parent),
14 ui(new Ui::ConfigureDialog)
15{
16 ui->setupUi(this);
17 this->setConfiguration();
18}
19
20ConfigureDialog::~ConfigureDialog() {
21}
22
23void ConfigureDialog::setConfiguration() {
24}
25
26void ConfigureDialog::applyConfiguration() {
27 ui->generalTab->applyConfiguration();
28 ui->debugTab->applyConfiguration();
29}
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 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <QDialog>
9
10namespace Ui {
11class ConfigureDialog;
12}
13
14class ConfigureDialog : public QDialog
15{
16 Q_OBJECT
17
18public:
19 explicit ConfigureDialog(QWidget *parent = nullptr);
20 ~ConfigureDialog();
21
22 void applyConfiguration();
23
24private:
25 void setConfiguration();
26
27private:
28 std::unique_ptr<Ui::ConfigureDialog> ui;
29};
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 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "citra_qt/configure_general.h"
6#include "citra_qt/ui_settings.h"
7#include "ui_configure_general.h"
8
9#include "core/settings.h"
10
11ConfigureGeneral::ConfigureGeneral(QWidget *parent) :
12 QWidget(parent),
13 ui(new Ui::ConfigureGeneral)
14{
15 ui->setupUi(this);
16 this->setConfiguration();
17}
18
19ConfigureGeneral::~ConfigureGeneral() {
20}
21
22void ConfigureGeneral::setConfiguration() {
23 ui->toogle_deepscan->setChecked(UISettings::values.gamedir_deepscan);
24 ui->toogle_check_exit->setChecked(UISettings::values.confirm_before_closing);
25 ui->region_combobox->setCurrentIndex(Settings::values.region_value);
26 ui->toogle_hw_renderer->setChecked(Settings::values.use_hw_renderer);
27 ui->toogle_shader_jit->setChecked(Settings::values.use_shader_jit);
28}
29
30void ConfigureGeneral::applyConfiguration() {
31 UISettings::values.gamedir_deepscan = ui->toogle_deepscan->isChecked();
32 UISettings::values.confirm_before_closing = ui->toogle_check_exit->isChecked();
33 Settings::values.region_value = ui->region_combobox->currentIndex();
34 Settings::values.use_hw_renderer = ui->toogle_hw_renderer->isChecked();
35 Settings::values.use_shader_jit = ui->toogle_shader_jit->isChecked();
36 Settings::Apply();
37}
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 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <QWidget>
9
10namespace Ui {
11class ConfigureGeneral;
12}
13
14class ConfigureGeneral : public QWidget
15{
16 Q_OBJECT
17
18public:
19 explicit ConfigureGeneral(QWidget *parent = nullptr);
20 ~ConfigureGeneral();
21
22 void applyConfiguration();
23
24private:
25 void setConfiguration();
26
27private:
28 std::unique_ptr<Ui::ConfigureGeneral> ui;
29};
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 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureGeneral</class>
4 <widget class="QWidget" name="ConfigureGeneral">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>300</width>
10 <height>377</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Form</string>
15 </property>
16 <layout class="QHBoxLayout" name="horizontalLayout">
17 <item>
18 <layout class="QVBoxLayout" name="verticalLayout">
19 <item>
20 <widget class="QGroupBox" name="groupBox">
21 <property name="title">
22 <string>General</string>
23 </property>
24 <layout class="QHBoxLayout" name="horizontalLayout_3">
25 <item>
26 <layout class="QVBoxLayout" name="verticalLayout_2">
27 <item>
28 <widget class="QCheckBox" name="toogle_deepscan">
29 <property name="text">
30 <string>Recursive scan for game folder</string>
31 </property>
32 </widget>
33 </item>
34 <item>
35 <widget class="QCheckBox" name="toogle_check_exit">
36 <property name="text">
37 <string>Confirm exit while emulation is running</string>
38 </property>
39 </widget>
40 </item>
41 </layout>
42 </item>
43 </layout>
44 </widget>
45 </item>
46 <item>
47 <widget class="QGroupBox" name="groupBox_4">
48 <property name="title">
49 <string>Emulation</string>
50 </property>
51 <layout class="QHBoxLayout" name="horizontalLayout_5">
52 <item>
53 <layout class="QVBoxLayout" name="verticalLayout_6">
54 <item>
55 <layout class="QHBoxLayout" name="horizontalLayout_6">
56 <item>
57 <widget class="QLabel" name="label">
58 <property name="text">
59 <string>Region:</string>
60 </property>
61 </widget>
62 </item>
63 <item>
64 <widget class="QComboBox" name="region_combobox">
65 <item>
66 <property name="text">
67 <string notr="true">JPN</string>
68 </property>
69 </item>
70 <item>
71 <property name="text">
72 <string notr="true">USA</string>
73 </property>
74 </item>
75 <item>
76 <property name="text">
77 <string notr="true">EUR</string>
78 </property>
79 </item>
80 <item>
81 <property name="text">
82 <string notr="true">AUS</string>
83 </property>
84 </item>
85 <item>
86 <property name="text">
87 <string notr="true">CHN</string>
88 </property>
89 </item>
90 <item>
91 <property name="text">
92 <string notr="true">KOR</string>
93 </property>
94 </item>
95 <item>
96 <property name="text">
97 <string notr="true">TWN</string>
98 </property>
99 </item>
100 </widget>
101 </item>
102 </layout>
103 </item>
104 </layout>
105 </item>
106 </layout>
107 </widget>
108 </item>
109 <item>
110 <widget class="QGroupBox" name="groupBox_2">
111 <property name="title">
112 <string>Performance</string>
113 </property>
114 <layout class="QHBoxLayout" name="horizontalLayout_2">
115 <item>
116 <layout class="QVBoxLayout" name="verticalLayout_3">
117 <item>
118 <widget class="QCheckBox" name="toogle_hw_renderer">
119 <property name="text">
120 <string>Enable hardware renderer</string>
121 </property>
122 </widget>
123 </item>
124 <item>
125 <widget class="QCheckBox" name="toogle_shader_jit">
126 <property name="text">
127 <string>Enable shader JIT</string>
128 </property>
129 </widget>
130 </item>
131 </layout>
132 </item>
133 </layout>
134 </widget>
135 </item>
136 <item>
137 <widget class="QGroupBox" name="groupBox_3">
138 <property name="title">
139 <string>Hotkeys</string>
140 </property>
141 <layout class="QHBoxLayout" name="horizontalLayout_4">
142 <item>
143 <layout class="QVBoxLayout" name="verticalLayout_4">
144 <item>
145 <widget class="GHotkeysDialog" name="widget" native="true"/>
146 </item>
147 </layout>
148 </item>
149 </layout>
150 </widget>
151 </item>
152 </layout>
153 </item>
154 </layout>
155 </widget>
156 <customwidgets>
157 <customwidget>
158 <class>GHotkeysDialog</class>
159 <extends>QWidget</extends>
160 <header>hotkeys.h</header>
161 <container>1</container>
162 </customwidget>
163 </customwidgets>
164 <resources/>
165 <connections/>
166</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 @@
8 8
9#include "game_list.h" 9#include "game_list.h"
10#include "game_list_p.h" 10#include "game_list_p.h"
11#include "ui_settings.h"
11 12
12#include "core/loader/loader.h" 13#include "core/loader/loader.h"
13 14
@@ -66,7 +67,7 @@ void GameList::ValidateEntry(const QModelIndex& item)
66 67
67 if (file_path.isEmpty()) 68 if (file_path.isEmpty())
68 return; 69 return;
69 std::string std_file_path(file_path.toLocal8Bit()); 70 std::string std_file_path(file_path.toStdString());
70 if (!FileUtil::Exists(std_file_path) || FileUtil::IsDirectory(std_file_path)) 71 if (!FileUtil::Exists(std_file_path) || FileUtil::IsDirectory(std_file_path))
71 return; 72 return;
72 emit GameChosen(file_path); 73 emit GameChosen(file_path);
@@ -100,19 +101,15 @@ void GameList::PopulateAsync(const QString& dir_path, bool deep_scan)
100 current_worker = std::move(worker); 101 current_worker = std::move(worker);
101} 102}
102 103
103void GameList::SaveInterfaceLayout(QSettings& settings) 104void GameList::SaveInterfaceLayout()
104{ 105{
105 settings.beginGroup("UILayout"); 106 UISettings::values.gamelist_header_state = tree_view->header()->saveState();
106 settings.setValue("gameListHeaderState", tree_view->header()->saveState());
107 settings.endGroup();
108} 107}
109 108
110void GameList::LoadInterfaceLayout(QSettings& settings) 109void GameList::LoadInterfaceLayout()
111{ 110{
112 auto header = tree_view->header(); 111 auto header = tree_view->header();
113 settings.beginGroup("UILayout"); 112 header->restoreState(UISettings::values.gamelist_header_state);
114 header->restoreState(settings.value("gameListHeaderState").toByteArray());
115 settings.endGroup();
116 113
117 item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder()); 114 item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder());
118} 115}
@@ -148,7 +145,7 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, bool d
148 145
149 emit EntryReady({ 146 emit EntryReady({
150 new GameListItem(QString::fromStdString(Loader::GetFileTypeString(filetype))), 147 new GameListItem(QString::fromStdString(Loader::GetFileTypeString(filetype))),
151 new GameListItemPath(QString::fromLocal8Bit(physical_name.c_str())), 148 new GameListItemPath(QString::fromStdString(physical_name)),
152 new GameListItemSize(FileUtil::GetSize(physical_name)), 149 new GameListItemSize(FileUtil::GetSize(physical_name)),
153 }); 150 });
154 } 151 }
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:
31 31
32 void PopulateAsync(const QString& dir_path, bool deep_scan); 32 void PopulateAsync(const QString& dir_path, bool deep_scan);
33 33
34 void SaveInterfaceLayout(QSettings& settings); 34 void SaveInterfaceLayout();
35 void LoadInterfaceLayout(QSettings& settings); 35 void LoadInterfaceLayout();
36 36
37public slots: 37public slots:
38 void AddEntry(QList<QStandardItem*> entry_items); 38 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 @@
4 4
5#include <map> 5#include <map>
6 6
7#include <QtGlobal>
7#include <QKeySequence> 8#include <QKeySequence>
8#include <QSettings>
9#include <QShortcut> 9#include <QShortcut>
10 10
11#include "citra_qt/hotkeys.h" 11#include "citra_qt/hotkeys.h"
12#include "citra_qt/ui_settings.h"
12 13
13struct Hotkey 14struct Hotkey
14{ 15{
@@ -24,54 +25,39 @@ typedef std::map<QString, HotkeyMap> HotkeyGroupMap;
24 25
25HotkeyGroupMap hotkey_groups; 26HotkeyGroupMap hotkey_groups;
26 27
27void SaveHotkeys(QSettings& settings) 28void SaveHotkeys()
28{ 29{
29 settings.beginGroup("Shortcuts"); 30 UISettings::values.shortcuts.clear();
30
31 for (auto group : hotkey_groups) 31 for (auto group : hotkey_groups)
32 { 32 {
33 settings.beginGroup(group.first);
34 for (auto hotkey : group.second) 33 for (auto hotkey : group.second)
35 { 34 {
36 settings.beginGroup(hotkey.first); 35 UISettings::values.shortcuts.emplace_back(
37 settings.setValue(QString("KeySeq"), hotkey.second.keyseq.toString()); 36 UISettings::Shortcut(group.first + "/" + hotkey.first,
38 settings.setValue(QString("Context"), hotkey.second.context); 37 UISettings::ContextualShortcut(hotkey.second.keyseq.toString(),
39 settings.endGroup(); 38 hotkey.second.context)));
40 } 39 }
41 settings.endGroup();
42 } 40 }
43 settings.endGroup();
44} 41}
45 42
46void LoadHotkeys(QSettings& settings) 43void LoadHotkeys()
47{ 44{
48 settings.beginGroup("Shortcuts");
49
50 // Make sure NOT to use a reference here because it would become invalid once we call beginGroup() 45 // Make sure NOT to use a reference here because it would become invalid once we call beginGroup()
51 QStringList groups = settings.childGroups(); 46 for (auto shortcut : UISettings::values.shortcuts)
52 for (auto group : groups)
53 { 47 {
54 settings.beginGroup(group); 48 QStringList cat = shortcut.first.split("/");
49 Q_ASSERT(cat.size() >= 2);
55 50
56 QStringList hotkeys = settings.childGroups(); 51 // RegisterHotkey assigns default keybindings, so use old values as default parameters
57 for (auto hotkey : hotkeys) 52 Hotkey& hk = hotkey_groups[cat[0]][cat[1]];
53 if (!shortcut.second.first.isEmpty())
58 { 54 {
59 settings.beginGroup(hotkey); 55 hk.keyseq = QKeySequence::fromString(shortcut.second.first);
60 56 hk.context = (Qt::ShortcutContext)shortcut.second.second;
61 // RegisterHotkey assigns default keybindings, so use old values as default parameters
62 Hotkey& hk = hotkey_groups[group][hotkey];
63 hk.keyseq = QKeySequence::fromString(settings.value("KeySeq", hk.keyseq.toString()).toString());
64 hk.context = (Qt::ShortcutContext)settings.value("Context", hk.context).toInt();
65 if (hk.shortcut)
66 hk.shortcut->setKey(hk.keyseq);
67
68 settings.endGroup();
69 } 57 }
70 58 if (hk.shortcut)
71 settings.endGroup(); 59 hk.shortcut->setKey(hk.keyseq);
72 } 60 }
73
74 settings.endGroup();
75} 61}
76 62
77void RegisterHotkey(const QString& group, const QString& action, const QKeySequence& default_keyseq, Qt::ShortcutContext default_context) 63void 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
94} 80}
95 81
96 82
97GHotkeysDialog::GHotkeysDialog(QWidget* parent): QDialog(parent) 83GHotkeysDialog::GHotkeysDialog(QWidget* parent): QWidget(parent)
98{ 84{
99 ui.setupUi(this); 85 ui.setupUi(this);
100 86
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 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once
6
5#include "ui_hotkeys.h" 7#include "ui_hotkeys.h"
6 8
7class QDialog; 9class QDialog;
@@ -33,16 +35,16 @@ QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widge
33 * 35 *
34 * @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. 36 * @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.
35 */ 37 */
36void SaveHotkeys(QSettings& settings); 38void SaveHotkeys();
37 39
38/** 40/**
39 * Loads hotkeys from the settings file. 41 * Loads hotkeys from the settings file.
40 * 42 *
41 * @note Yet unregistered hotkeys which are present in the settings will automatically be registered. 43 * @note Yet unregistered hotkeys which are present in the settings will automatically be registered.
42 */ 44 */
43void LoadHotkeys(QSettings& settings); 45void LoadHotkeys();
44 46
45class GHotkeysDialog : public QDialog 47class GHotkeysDialog : public QWidget
46{ 48{
47 Q_OBJECT 49 Q_OBJECT
48 50
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 @@
1<?xml version="1.0" encoding="UTF-8"?> 1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0"> 2<ui version="4.0">
3 <class>hotkeys</class> 3 <class>hotkeys</class>
4 <widget class="QDialog" name="hotkeys"> 4 <widget class="QWidget" name="hotkeys">
5 <property name="geometry"> 5 <property name="geometry">
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
@@ -39,51 +39,8 @@
39 </column> 39 </column>
40 </widget> 40 </widget>
41 </item> 41 </item>
42 <item>
43 <widget class="QDialogButtonBox" name="buttonBox">
44 <property name="orientation">
45 <enum>Qt::Horizontal</enum>
46 </property>
47 <property name="standardButtons">
48 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Reset</set>
49 </property>
50 </widget>
51 </item>
52 </layout> 42 </layout>
53 </widget> 43 </widget>
54 <resources/> 44 <resources/>
55 <connections> 45 <connections/>
56 <connection>
57 <sender>buttonBox</sender>
58 <signal>accepted()</signal>
59 <receiver>hotkeys</receiver>
60 <slot>accept()</slot>
61 <hints>
62 <hint type="sourcelabel">
63 <x>248</x>
64 <y>254</y>
65 </hint>
66 <hint type="destinationlabel">
67 <x>157</x>
68 <y>274</y>
69 </hint>
70 </hints>
71 </connection>
72 <connection>
73 <sender>buttonBox</sender>
74 <signal>rejected()</signal>
75 <receiver>hotkeys</receiver>
76 <slot>reject()</slot>
77 <hints>
78 <hint type="sourcelabel">
79 <x>316</x>
80 <y>260</y>
81 </hint>
82 <hint type="destinationlabel">
83 <x>286</x>
84 <y>274</y>
85 </hint>
86 </hints>
87 </connection>
88 </connections>
89</ui> 46</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 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <clocale> 5#include <clocale>
6#include <memory>
6#include <thread> 7#include <thread>
7 8
8#include <QDesktopWidget> 9#include <QDesktopWidget>
@@ -13,9 +14,11 @@
13 14
14#include "citra_qt/bootmanager.h" 15#include "citra_qt/bootmanager.h"
15#include "citra_qt/config.h" 16#include "citra_qt/config.h"
17#include "citra_qt/configure_dialog.h"
16#include "citra_qt/game_list.h" 18#include "citra_qt/game_list.h"
17#include "citra_qt/hotkeys.h" 19#include "citra_qt/hotkeys.h"
18#include "citra_qt/main.h" 20#include "citra_qt/main.h"
21#include "citra_qt/ui_settings.h"
19 22
20// Debugger 23// Debugger
21#include "citra_qt/debugger/callstack.h" 24#include "citra_qt/debugger/callstack.h"
@@ -30,7 +33,6 @@
30#include "citra_qt/debugger/ramview.h" 33#include "citra_qt/debugger/ramview.h"
31#include "citra_qt/debugger/registers.h" 34#include "citra_qt/debugger/registers.h"
32 35
33#include "common/make_unique.h"
34#include "common/microprofile.h" 36#include "common/microprofile.h"
35#include "common/platform.h" 37#include "common/platform.h"
36#include "common/scm_rev.h" 38#include "common/scm_rev.h"
@@ -50,12 +52,10 @@
50 52
51#include "video_core/video_core.h" 53#include "video_core/video_core.h"
52 54
53GMainWindow::GMainWindow() : emu_thread(nullptr) 55GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr)
54{ 56{
55 Pica::g_debug_context = Pica::DebugContext::Construct(); 57 Pica::g_debug_context = Pica::DebugContext::Construct();
56 58
57 Config config;
58
59 ui.setupUi(this); 59 ui.setupUi(this);
60 statusBar()->hide(); 60 statusBar()->hide();
61 61
@@ -133,33 +133,18 @@ GMainWindow::GMainWindow() : emu_thread(nullptr)
133 setGeometry(x, y, w, h); 133 setGeometry(x, y, w, h);
134 134
135 // Restore UI state 135 // Restore UI state
136 QSettings settings; 136 restoreGeometry(UISettings::values.geometry);
137 137 restoreState(UISettings::values.state);
138 settings.beginGroup("UILayout"); 138 render_window->restoreGeometry(UISettings::values.renderwindow_geometry);
139 restoreGeometry(settings.value("geometry").toByteArray()); 139 microProfileDialog->restoreGeometry(UISettings::values.microprofile_geometry);
140 restoreState(settings.value("state").toByteArray()); 140 microProfileDialog->setVisible(UISettings::values.microprofile_visible);
141 render_window->restoreGeometry(settings.value("geometryRenderWindow").toByteArray());
142 microProfileDialog->restoreGeometry(settings.value("microProfileDialogGeometry").toByteArray());
143 microProfileDialog->setVisible(settings.value("microProfileDialogVisible").toBool());
144 settings.endGroup();
145
146 game_list->LoadInterfaceLayout(settings);
147
148 ui.action_Use_Gdbstub->setChecked(Settings::values.use_gdbstub);
149 SetGdbstubEnabled(ui.action_Use_Gdbstub->isChecked());
150
151 GDBStub::SetServerPort(static_cast<u32>(Settings::values.gdbstub_port));
152
153 ui.action_Use_Hardware_Renderer->setChecked(Settings::values.use_hw_renderer);
154 SetHardwareRendererEnabled(ui.action_Use_Hardware_Renderer->isChecked());
155 141
156 ui.action_Use_Shader_JIT->setChecked(Settings::values.use_shader_jit); 142 game_list->LoadInterfaceLayout();
157 SetShaderJITEnabled(ui.action_Use_Shader_JIT->isChecked());
158 143
159 ui.action_Single_Window_Mode->setChecked(settings.value("singleWindowMode", true).toBool()); 144 ui.action_Single_Window_Mode->setChecked(UISettings::values.single_window_mode);
160 ToggleWindowMode(); 145 ToggleWindowMode();
161 146
162 ui.actionDisplay_widget_title_bars->setChecked(settings.value("displayTitleBars", true).toBool()); 147 ui.actionDisplay_widget_title_bars->setChecked(UISettings::values.display_titlebar);
163 OnDisplayTitleBars(ui.actionDisplay_widget_title_bars->isChecked()); 148 OnDisplayTitleBars(ui.actionDisplay_widget_title_bars->isChecked());
164 149
165 // Prepare actions for recent files 150 // Prepare actions for recent files
@@ -172,21 +157,16 @@ GMainWindow::GMainWindow() : emu_thread(nullptr)
172 } 157 }
173 UpdateRecentFiles(); 158 UpdateRecentFiles();
174 159
175 confirm_before_closing = settings.value("confirmClose", true).toBool();
176
177 // Setup connections 160 // Setup connections
178 connect(game_list, SIGNAL(GameChosen(QString)), this, SLOT(OnGameListLoadFile(QString))); 161 connect(game_list, SIGNAL(GameChosen(QString)), this, SLOT(OnGameListLoadFile(QString)), Qt::DirectConnection);
179 connect(ui.action_Load_File, SIGNAL(triggered()), this, SLOT(OnMenuLoadFile())); 162 connect(ui.action_Configure, SIGNAL(triggered()), this, SLOT(OnConfigure()));
163 connect(ui.action_Load_File, SIGNAL(triggered()), this, SLOT(OnMenuLoadFile()),Qt::DirectConnection);
180 connect(ui.action_Load_Symbol_Map, SIGNAL(triggered()), this, SLOT(OnMenuLoadSymbolMap())); 164 connect(ui.action_Load_Symbol_Map, SIGNAL(triggered()), this, SLOT(OnMenuLoadSymbolMap()));
181 connect(ui.action_Select_Game_List_Root, SIGNAL(triggered()), this, SLOT(OnMenuSelectGameListRoot())); 165 connect(ui.action_Select_Game_List_Root, SIGNAL(triggered()), this, SLOT(OnMenuSelectGameListRoot()));
182 connect(ui.action_Start, SIGNAL(triggered()), this, SLOT(OnStartGame())); 166 connect(ui.action_Start, SIGNAL(triggered()), this, SLOT(OnStartGame()));
183 connect(ui.action_Pause, SIGNAL(triggered()), this, SLOT(OnPauseGame())); 167 connect(ui.action_Pause, SIGNAL(triggered()), this, SLOT(OnPauseGame()));
184 connect(ui.action_Stop, SIGNAL(triggered()), this, SLOT(OnStopGame())); 168 connect(ui.action_Stop, SIGNAL(triggered()), this, SLOT(OnStopGame()));
185 connect(ui.action_Use_Hardware_Renderer, SIGNAL(triggered(bool)), this, SLOT(SetHardwareRendererEnabled(bool)));
186 connect(ui.action_Use_Shader_JIT, SIGNAL(triggered(bool)), this, SLOT(SetShaderJITEnabled(bool)));
187 connect(ui.action_Use_Gdbstub, SIGNAL(triggered(bool)), this, SLOT(SetGdbstubEnabled(bool)));
188 connect(ui.action_Single_Window_Mode, SIGNAL(triggered(bool)), this, SLOT(ToggleWindowMode())); 169 connect(ui.action_Single_Window_Mode, SIGNAL(triggered(bool)), this, SLOT(ToggleWindowMode()));
189 connect(ui.action_Hotkeys, SIGNAL(triggered()), this, SLOT(OnOpenHotkeysDialog()));
190 170
191 connect(this, SIGNAL(EmulationStarting(EmuThread*)), disasmWidget, SLOT(OnEmulationStarting(EmuThread*))); 171 connect(this, SIGNAL(EmulationStarting(EmuThread*)), disasmWidget, SLOT(OnEmulationStarting(EmuThread*)));
192 connect(this, SIGNAL(EmulationStopping()), disasmWidget, SLOT(OnEmulationStopping())); 172 connect(this, SIGNAL(EmulationStopping()), disasmWidget, SLOT(OnEmulationStopping()));
@@ -201,7 +181,7 @@ GMainWindow::GMainWindow() : emu_thread(nullptr)
201 // Setup hotkeys 181 // Setup hotkeys
202 RegisterHotkey("Main Window", "Load File", QKeySequence::Open); 182 RegisterHotkey("Main Window", "Load File", QKeySequence::Open);
203 RegisterHotkey("Main Window", "Start Emulation"); 183 RegisterHotkey("Main Window", "Start Emulation");
204 LoadHotkeys(settings); 184 LoadHotkeys();
205 185
206 connect(GetHotkey("Main Window", "Load File", this), SIGNAL(activated()), this, SLOT(OnMenuLoadFile())); 186 connect(GetHotkey("Main Window", "Load File", this), SIGNAL(activated()), this, SLOT(OnMenuLoadFile()));
207 connect(GetHotkey("Main Window", "Start Emulation", this), SIGNAL(activated()), this, SLOT(OnStartGame())); 187 connect(GetHotkey("Main Window", "Start Emulation", this), SIGNAL(activated()), this, SLOT(OnStartGame()));
@@ -211,7 +191,7 @@ GMainWindow::GMainWindow() : emu_thread(nullptr)
211 191
212 show(); 192 show();
213 193
214 game_list->PopulateAsync(settings.value("gameListRootDir", ".").toString(), settings.value("gameListDeepScan", false).toBool()); 194 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
215 195
216 QStringList args = QApplication::arguments(); 196 QStringList args = QApplication::arguments();
217 if (args.length() >= 2) { 197 if (args.length() >= 2) {
@@ -319,7 +299,7 @@ void GMainWindow::BootGame(const std::string& filename) {
319 return; 299 return;
320 300
321 // Create and start the emulation thread 301 // Create and start the emulation thread
322 emu_thread = Common::make_unique<EmuThread>(render_window); 302 emu_thread = std::make_unique<EmuThread>(render_window);
323 emit EmulationStarting(emu_thread.get()); 303 emit EmulationStarting(emu_thread.get());
324 render_window->moveContext(); 304 render_window->moveContext();
325 emu_thread->start(); 305 emu_thread->start();
@@ -375,32 +355,24 @@ void GMainWindow::ShutdownGame() {
375 emulation_running = false; 355 emulation_running = false;
376} 356}
377 357
378void GMainWindow::StoreRecentFile(const std::string& filename) 358void GMainWindow::StoreRecentFile(const std::string& filename) {
379{ 359 UISettings::values.recent_files.prepend(QString::fromStdString(filename));
380 QSettings settings; 360 UISettings::values.recent_files.removeDuplicates();
381 QStringList recent_files = settings.value("recentFiles").toStringList(); 361 while (UISettings::values.recent_files.size() > max_recent_files_item) {
382 recent_files.prepend(QString::fromStdString(filename)); 362 UISettings::values.recent_files.removeLast();
383 recent_files.removeDuplicates();
384 while (recent_files.size() > max_recent_files_item) {
385 recent_files.removeLast();
386 } 363 }
387 364
388 settings.setValue("recentFiles", recent_files);
389
390 UpdateRecentFiles(); 365 UpdateRecentFiles();
391} 366}
392 367
393void GMainWindow::UpdateRecentFiles() { 368void GMainWindow::UpdateRecentFiles() {
394 QSettings settings; 369 unsigned int num_recent_files = std::min(UISettings::values.recent_files.size(), static_cast<int>(max_recent_files_item));
395 QStringList recent_files = settings.value("recentFiles").toStringList();
396
397 unsigned int num_recent_files = std::min(recent_files.size(), static_cast<int>(max_recent_files_item));
398 370
399 for (unsigned int i = 0; i < num_recent_files; i++) { 371 for (unsigned int i = 0; i < num_recent_files; i++) {
400 QString text = QString("&%1. %2").arg(i + 1).arg(QFileInfo(recent_files[i]).fileName()); 372 QString text = QString("&%1. %2").arg(i + 1).arg(QFileInfo(UISettings::values.recent_files[i]).fileName());
401 actions_recent_files[i]->setText(text); 373 actions_recent_files[i]->setText(text);
402 actions_recent_files[i]->setData(recent_files[i]); 374 actions_recent_files[i]->setData(UISettings::values.recent_files[i]);
403 actions_recent_files[i]->setToolTip(recent_files[i]); 375 actions_recent_files[i]->setToolTip(UISettings::values.recent_files[i]);
404 actions_recent_files[i]->setVisible(true); 376 actions_recent_files[i]->setVisible(true);
405 } 377 }
406 378
@@ -417,40 +389,32 @@ void GMainWindow::UpdateRecentFiles() {
417} 389}
418 390
419void GMainWindow::OnGameListLoadFile(QString game_path) { 391void GMainWindow::OnGameListLoadFile(QString game_path) {
420 BootGame(game_path.toLocal8Bit().data()); 392 BootGame(game_path.toStdString());
421} 393}
422 394
423void GMainWindow::OnMenuLoadFile() { 395void GMainWindow::OnMenuLoadFile() {
424 QSettings settings; 396 QString filename = QFileDialog::getOpenFileName(this, tr("Load File"), UISettings::values.roms_path, tr("3DS executable (*.3ds *.3dsx *.elf *.axf *.cci *.cxi)"));
425 QString rom_path = settings.value("romsPath", QString()).toString();
426
427 QString filename = QFileDialog::getOpenFileName(this, tr("Load File"), rom_path, tr("3DS executable (*.3ds *.3dsx *.elf *.axf *.cci *.cxi)"));
428 if (!filename.isEmpty()) { 397 if (!filename.isEmpty()) {
429 settings.setValue("romsPath", QFileInfo(filename).path()); 398 UISettings::values.roms_path = QFileInfo(filename).path();
430 399
431 BootGame(filename.toLocal8Bit().data()); 400 BootGame(filename.toStdString());
432 } 401 }
433} 402}
434 403
435void GMainWindow::OnMenuLoadSymbolMap() { 404void GMainWindow::OnMenuLoadSymbolMap() {
436 QSettings settings; 405 QString filename = QFileDialog::getOpenFileName(this, tr("Load Symbol Map"), UISettings::values.symbols_path, tr("Symbol map (*)"));
437 QString symbol_path = settings.value("symbolsPath", QString()).toString();
438
439 QString filename = QFileDialog::getOpenFileName(this, tr("Load Symbol Map"), symbol_path, tr("Symbol map (*)"));
440 if (!filename.isEmpty()) { 406 if (!filename.isEmpty()) {
441 settings.setValue("symbolsPath", QFileInfo(filename).path()); 407 UISettings::values.symbols_path = QFileInfo(filename).path();
442 408
443 LoadSymbolMap(filename.toLocal8Bit().data()); 409 LoadSymbolMap(filename.toStdString());
444 } 410 }
445} 411}
446 412
447void GMainWindow::OnMenuSelectGameListRoot() { 413void GMainWindow::OnMenuSelectGameListRoot() {
448 QSettings settings;
449
450 QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory")); 414 QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory"));
451 if (!dir_path.isEmpty()) { 415 if (!dir_path.isEmpty()) {
452 settings.setValue("gameListRootDir", dir_path); 416 UISettings::values.gamedir = dir_path;
453 game_list->PopulateAsync(dir_path, settings.value("gameListDeepScan").toBool()); 417 game_list->PopulateAsync(dir_path, UISettings::values.gamedir_deepscan);
454 } 418 }
455} 419}
456 420
@@ -461,15 +425,12 @@ void GMainWindow::OnMenuRecentFile() {
461 QString filename = action->data().toString(); 425 QString filename = action->data().toString();
462 QFileInfo file_info(filename); 426 QFileInfo file_info(filename);
463 if (file_info.exists()) { 427 if (file_info.exists()) {
464 BootGame(filename.toLocal8Bit().data()); 428 BootGame(filename.toStdString());
465 } else { 429 } else {
466 // Display an error message and remove the file from the list. 430 // Display an error message and remove the file from the list.
467 QMessageBox::information(this, tr("File not found"), tr("File \"%1\" not found").arg(filename)); 431 QMessageBox::information(this, tr("File not found"), tr("File \"%1\" not found").arg(filename));
468 432
469 QSettings settings; 433 UISettings::values.recent_files.removeOne(filename);
470 QStringList recent_files = settings.value("recentFiles").toStringList();
471 recent_files.removeOne(filename);
472 settings.setValue("recentFiles", recent_files);
473 UpdateRecentFiles(); 434 UpdateRecentFiles();
474 } 435 }
475} 436}
@@ -496,31 +457,6 @@ void GMainWindow::OnStopGame() {
496 ShutdownGame(); 457 ShutdownGame();
497} 458}
498 459
499void GMainWindow::OnOpenHotkeysDialog() {
500 GHotkeysDialog dialog(this);
501 dialog.exec();
502}
503
504void GMainWindow::SetHardwareRendererEnabled(bool enabled) {
505 VideoCore::g_hw_renderer_enabled = enabled;
506
507 Config config;
508 Settings::values.use_hw_renderer = enabled;
509 config.Save();
510}
511
512void GMainWindow::SetGdbstubEnabled(bool enabled) {
513 GDBStub::ToggleServer(enabled);
514}
515
516void GMainWindow::SetShaderJITEnabled(bool enabled) {
517 VideoCore::g_shader_jit_enabled = enabled;
518
519 Config config;
520 Settings::values.use_shader_jit = enabled;
521 config.Save();
522}
523
524void GMainWindow::ToggleWindowMode() { 460void GMainWindow::ToggleWindowMode() {
525 if (ui.action_Single_Window_Mode->isChecked()) { 461 if (ui.action_Single_Window_Mode->isChecked()) {
526 // Render in the main window... 462 // Render in the main window...
@@ -547,11 +483,17 @@ void GMainWindow::ToggleWindowMode() {
547} 483}
548 484
549void GMainWindow::OnConfigure() { 485void GMainWindow::OnConfigure() {
550 //GControllerConfigDialog* dialog = new GControllerConfigDialog(controller_ports, this); 486 ConfigureDialog configureDialog(this);
487 auto result = configureDialog.exec();
488 if (result == QDialog::Accepted)
489 {
490 configureDialog.applyConfiguration();
491 config->Save();
492 }
551} 493}
552 494
553bool GMainWindow::ConfirmClose() { 495bool GMainWindow::ConfirmClose() {
554 if (emu_thread == nullptr || !confirm_before_closing) 496 if (emu_thread == nullptr || !UISettings::values.confirm_before_closing)
555 return true; 497 return true;
556 498
557 auto answer = QMessageBox::question(this, tr("Citra"), 499 auto answer = QMessageBox::question(this, tr("Citra"),
@@ -566,23 +508,18 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
566 return; 508 return;
567 } 509 }
568 510
569 // Save window layout 511 UISettings::values.geometry = saveGeometry();
570 QSettings settings(QSettings::IniFormat, QSettings::UserScope, "Citra team", "Citra"); 512 UISettings::values.state = saveState();
513 UISettings::values.renderwindow_geometry = render_window->saveGeometry();
514 UISettings::values.microprofile_geometry = microProfileDialog->saveGeometry();
515 UISettings::values.microprofile_visible = microProfileDialog->isVisible();
571 516
572 settings.beginGroup("UILayout"); 517 UISettings::values.single_window_mode = ui.action_Single_Window_Mode->isChecked();
573 settings.setValue("geometry", saveGeometry()); 518 UISettings::values.display_titlebar = ui.actionDisplay_widget_title_bars->isChecked();
574 settings.setValue("state", saveState()); 519 UISettings::values.first_start = false;
575 settings.setValue("geometryRenderWindow", render_window->saveGeometry());
576 settings.setValue("microProfileDialogGeometry", microProfileDialog->saveGeometry());
577 settings.setValue("microProfileDialogVisible", microProfileDialog->isVisible());
578 settings.endGroup();
579 520
580 settings.setValue("singleWindowMode", ui.action_Single_Window_Mode->isChecked()); 521 game_list->SaveInterfaceLayout();
581 settings.setValue("displayTitleBars", ui.actionDisplay_widget_title_bars->isChecked()); 522 SaveHotkeys();
582 settings.setValue("firstStart", false);
583 settings.setValue("confirmClose", confirm_before_closing);
584 game_list->SaveInterfaceLayout(settings);
585 SaveHotkeys(settings);
586 523
587 // Shutdown session if the emu thread is active... 524 // Shutdown session if the emu thread is active...
588 if (emu_thread != nullptr) 525 if (emu_thread != nullptr)
@@ -607,7 +544,6 @@ int main(int argc, char* argv[]) {
607 }); 544 });
608 545
609 // Init settings params 546 // Init settings params
610 QSettings::setDefaultFormat(QSettings::IniFormat);
611 QCoreApplication::setOrganizationName("Citra team"); 547 QCoreApplication::setOrganizationName("Citra team");
612 QCoreApplication::setApplicationName("Citra"); 548 QCoreApplication::setApplicationName("Citra");
613 549
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 @@
10 10
11#include "ui_main.h" 11#include "ui_main.h"
12 12
13class Config;
13class GameList; 14class GameList;
14class GImageInfo; 15class GImageInfo;
15class GRenderWindow; 16class GRenderWindow;
@@ -104,12 +105,8 @@ private slots:
104 /// Called whenever a user selects the "File->Select Game List Root" menu item 105 /// Called whenever a user selects the "File->Select Game List Root" menu item
105 void OnMenuSelectGameListRoot(); 106 void OnMenuSelectGameListRoot();
106 void OnMenuRecentFile(); 107 void OnMenuRecentFile();
107 void OnOpenHotkeysDialog();
108 void OnConfigure(); 108 void OnConfigure();
109 void OnDisplayTitleBars(bool); 109 void OnDisplayTitleBars(bool);
110 void SetHardwareRendererEnabled(bool);
111 void SetGdbstubEnabled(bool);
112 void SetShaderJITEnabled(bool);
113 void ToggleWindowMode(); 110 void ToggleWindowMode();
114 111
115private: 112private:
@@ -118,6 +115,8 @@ private:
118 GRenderWindow* render_window; 115 GRenderWindow* render_window;
119 GameList* game_list; 116 GameList* game_list;
120 117
118 std::unique_ptr<Config> config;
119
121 // Whether emulation is currently running in Citra. 120 // Whether emulation is currently running in Citra.
122 bool emulation_running = false; 121 bool emulation_running = false;
123 std::unique_ptr<EmuThread> emu_thread; 122 std::unique_ptr<EmuThread> emu_thread;
@@ -131,7 +130,6 @@ private:
131 GPUCommandListWidget* graphicsCommandsWidget; 130 GPUCommandListWidget* graphicsCommandsWidget;
132 131
133 QAction* actions_recent_files[max_recent_files_item]; 132 QAction* actions_recent_files[max_recent_files_item];
134 bool confirm_before_closing;
135}; 133};
136 134
137#endif // _CITRA_QT_MAIN_HXX_ 135#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 @@
45 <x>0</x> 45 <x>0</x>
46 <y>0</y> 46 <y>0</y>
47 <width>1081</width> 47 <width>1081</width>
48 <height>22</height> 48 <height>19</height>
49 </rect> 49 </rect>
50 </property> 50 </property>
51 <widget class="QMenu" name="menu_File"> 51 <widget class="QMenu" name="menu_File">
@@ -73,9 +73,6 @@
73 <addaction name="action_Pause"/> 73 <addaction name="action_Pause"/>
74 <addaction name="action_Stop"/> 74 <addaction name="action_Stop"/>
75 <addaction name="separator"/> 75 <addaction name="separator"/>
76 <addaction name="action_Use_Hardware_Renderer"/>
77 <addaction name="action_Use_Shader_JIT"/>
78 <addaction name="action_Use_Gdbstub"/>
79 <addaction name="action_Configure"/> 76 <addaction name="action_Configure"/>
80 </widget> 77 </widget>
81 <widget class="QMenu" name="menu_View"> 78 <widget class="QMenu" name="menu_View">
@@ -84,7 +81,6 @@
84 </property> 81 </property>
85 <addaction name="action_Single_Window_Mode"/> 82 <addaction name="action_Single_Window_Mode"/>
86 <addaction name="actionDisplay_widget_title_bars"/> 83 <addaction name="actionDisplay_widget_title_bars"/>
87 <addaction name="action_Hotkeys"/>
88 </widget> 84 </widget>
89 <widget class="QMenu" name="menu_Help"> 85 <widget class="QMenu" name="menu_Help">
90 <property name="title"> 86 <property name="title">
@@ -150,35 +146,6 @@
150 <string>Single Window Mode</string> 146 <string>Single Window Mode</string>
151 </property> 147 </property>
152 </action> 148 </action>
153 <action name="action_Hotkeys">
154 <property name="text">
155 <string>Configure &amp;Hotkeys ...</string>
156 </property>
157 </action>
158 <action name="action_Use_Hardware_Renderer">
159 <property name="checkable">
160 <bool>true</bool>
161 </property>
162 <property name="text">
163 <string>Use Hardware Renderer</string>
164 </property>
165 </action>
166 <action name="action_Use_Shader_JIT">
167 <property name="checkable">
168 <bool>true</bool>
169 </property>
170 <property name="text">
171 <string>Use Shader JIT</string>
172 </property>
173 </action>
174 <action name="action_Use_Gdbstub">
175 <property name="checkable">
176 <bool>true</bool>
177 </property>
178 <property name="text">
179 <string>Use Gdbstub</string>
180 </property>
181 </action>
182 <action name="action_Configure"> 149 <action name="action_Configure">
183 <property name="text"> 150 <property name="text">
184 <string>Configure ...</string> 151 <string>Configure ...</string>
@@ -220,22 +187,6 @@
220 </hints> 187 </hints>
221 </connection> 188 </connection>
222 <connection> 189 <connection>
223 <sender>action_Configure</sender>
224 <signal>triggered()</signal>
225 <receiver>MainWindow</receiver>
226 <slot>OnConfigure()</slot>
227 <hints>
228 <hint type="sourcelabel">
229 <x>-1</x>
230 <y>-1</y>
231 </hint>
232 <hint type="destinationlabel">
233 <x>540</x>
234 <y>364</y>
235 </hint>
236 </hints>
237 </connection>
238 <connection>
239 <sender>actionDisplay_widget_title_bars</sender> 190 <sender>actionDisplay_widget_title_bars</sender>
240 <signal>triggered(bool)</signal> 191 <signal>triggered(bool)</signal>
241 <receiver>MainWindow</receiver> 192 <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 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "ui_settings.h"
6
7namespace UISettings {
8
9Values values = {};
10
11}
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 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <QByteArray>
8#include <QStringList>
9#include <QString>
10
11#include <vector>
12
13namespace UISettings {
14
15using ContextualShortcut = std::pair<QString, int> ;
16using Shortcut = std::pair<QString, ContextualShortcut>;
17
18struct Values {
19 QByteArray geometry;
20 QByteArray state;
21
22 QByteArray renderwindow_geometry;
23
24 QByteArray gamelist_header_state;
25
26 QByteArray microprofile_geometry;
27 bool microprofile_visible;
28
29 bool single_window_mode;
30 bool display_titlebar;
31
32 bool confirm_before_closing;
33 bool first_start;
34
35 QString roms_path;
36 QString symbols_path;
37 QString gamedir;
38 bool gamedir_deepscan;
39 QStringList recent_files;
40
41 // Shortcut name <Shortcut, context>
42 std::vector<Shortcut> shortcuts;
43};
44
45extern Values values;
46
47}
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
42 logging/filter.h 42 logging/filter.h
43 logging/log.h 43 logging/log.h
44 logging/backend.h 44 logging/backend.h
45 make_unique.h
46 math_util.h 45 math_util.h
47 memory_util.h 46 memory_util.h
48 microprofile.h 47 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)
85 StripTailDirSlashes(copy); 85 StripTailDirSlashes(copy);
86 86
87#ifdef _WIN32 87#ifdef _WIN32
88 int result = _tstat64(Common::UTF8ToTStr(copy).c_str(), &file_info); 88 int result = _wstat64(Common::UTF8ToUTF16W(copy).c_str(), &file_info);
89#else 89#else
90 int result = stat64(copy.c_str(), &file_info); 90 int result = stat64(copy.c_str(), &file_info);
91#endif 91#endif
@@ -102,7 +102,7 @@ bool IsDirectory(const std::string &filename)
102 StripTailDirSlashes(copy); 102 StripTailDirSlashes(copy);
103 103
104#ifdef _WIN32 104#ifdef _WIN32
105 int result = _tstat64(Common::UTF8ToTStr(copy).c_str(), &file_info); 105 int result = _wstat64(Common::UTF8ToUTF16W(copy).c_str(), &file_info);
106#else 106#else
107 int result = stat64(copy.c_str(), &file_info); 107 int result = stat64(copy.c_str(), &file_info);
108#endif 108#endif
@@ -138,7 +138,7 @@ bool Delete(const std::string &filename)
138 } 138 }
139 139
140#ifdef _WIN32 140#ifdef _WIN32
141 if (!DeleteFile(Common::UTF8ToTStr(filename).c_str())) 141 if (!DeleteFileW(Common::UTF8ToUTF16W(filename).c_str()))
142 { 142 {
143 LOG_ERROR(Common_Filesystem, "DeleteFile failed on %s: %s", 143 LOG_ERROR(Common_Filesystem, "DeleteFile failed on %s: %s",
144 filename.c_str(), GetLastErrorMsg()); 144 filename.c_str(), GetLastErrorMsg());
@@ -160,7 +160,7 @@ bool CreateDir(const std::string &path)
160{ 160{
161 LOG_TRACE(Common_Filesystem, "directory %s", path.c_str()); 161 LOG_TRACE(Common_Filesystem, "directory %s", path.c_str());
162#ifdef _WIN32 162#ifdef _WIN32
163 if (::CreateDirectory(Common::UTF8ToTStr(path).c_str(), nullptr)) 163 if (::CreateDirectoryW(Common::UTF8ToUTF16W(path).c_str(), nullptr))
164 return true; 164 return true;
165 DWORD error = GetLastError(); 165 DWORD error = GetLastError();
166 if (error == ERROR_ALREADY_EXISTS) 166 if (error == ERROR_ALREADY_EXISTS)
@@ -241,7 +241,7 @@ bool DeleteDir(const std::string &filename)
241 } 241 }
242 242
243#ifdef _WIN32 243#ifdef _WIN32
244 if (::RemoveDirectory(Common::UTF8ToTStr(filename).c_str())) 244 if (::RemoveDirectoryW(Common::UTF8ToUTF16W(filename).c_str()))
245 return true; 245 return true;
246#else 246#else
247 if (rmdir(filename.c_str()) == 0) 247 if (rmdir(filename.c_str()) == 0)
@@ -257,8 +257,13 @@ bool Rename(const std::string &srcFilename, const std::string &destFilename)
257{ 257{
258 LOG_TRACE(Common_Filesystem, "%s --> %s", 258 LOG_TRACE(Common_Filesystem, "%s --> %s",
259 srcFilename.c_str(), destFilename.c_str()); 259 srcFilename.c_str(), destFilename.c_str());
260#ifdef _WIN32
261 if (_wrename(Common::UTF8ToUTF16W(srcFilename).c_str(), Common::UTF8ToUTF16W(destFilename).c_str()) == 0)
262 return true;
263#else
260 if (rename(srcFilename.c_str(), destFilename.c_str()) == 0) 264 if (rename(srcFilename.c_str(), destFilename.c_str()) == 0)
261 return true; 265 return true;
266#endif
262 LOG_ERROR(Common_Filesystem, "failed %s --> %s: %s", 267 LOG_ERROR(Common_Filesystem, "failed %s --> %s: %s",
263 srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); 268 srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
264 return false; 269 return false;
@@ -270,7 +275,7 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename)
270 LOG_TRACE(Common_Filesystem, "%s --> %s", 275 LOG_TRACE(Common_Filesystem, "%s --> %s",
271 srcFilename.c_str(), destFilename.c_str()); 276 srcFilename.c_str(), destFilename.c_str());
272#ifdef _WIN32 277#ifdef _WIN32
273 if (CopyFile(Common::UTF8ToTStr(srcFilename).c_str(), Common::UTF8ToTStr(destFilename).c_str(), FALSE)) 278 if (CopyFileW(Common::UTF8ToUTF16W(srcFilename).c_str(), Common::UTF8ToUTF16W(destFilename).c_str(), FALSE))
274 return true; 279 return true;
275 280
276 LOG_ERROR(Common_Filesystem, "failed %s --> %s: %s", 281 LOG_ERROR(Common_Filesystem, "failed %s --> %s: %s",
@@ -358,7 +363,7 @@ u64 GetSize(const std::string &filename)
358 363
359 struct stat64 buf; 364 struct stat64 buf;
360#ifdef _WIN32 365#ifdef _WIN32
361 if (_tstat64(Common::UTF8ToTStr(filename).c_str(), &buf) == 0) 366 if (_wstat64(Common::UTF8ToUTF16W(filename).c_str(), &buf) == 0)
362#else 367#else
363 if (stat64(filename.c_str(), &buf) == 0) 368 if (stat64(filename.c_str(), &buf) == 0)
364#endif 369#endif
@@ -432,16 +437,16 @@ bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directo
432 437
433#ifdef _WIN32 438#ifdef _WIN32
434 // Find the first file in the directory. 439 // Find the first file in the directory.
435 WIN32_FIND_DATA ffd; 440 WIN32_FIND_DATAW ffd;
436 441
437 HANDLE handle_find = FindFirstFile(Common::UTF8ToTStr(directory + "\\*").c_str(), &ffd); 442 HANDLE handle_find = FindFirstFileW(Common::UTF8ToUTF16W(directory + "\\*").c_str(), &ffd);
438 if (handle_find == INVALID_HANDLE_VALUE) { 443 if (handle_find == INVALID_HANDLE_VALUE) {
439 FindClose(handle_find); 444 FindClose(handle_find);
440 return false; 445 return false;
441 } 446 }
442 // windows loop 447 // windows loop
443 do { 448 do {
444 const std::string virtual_name(Common::TStrToUTF8(ffd.cFileName)); 449 const std::string virtual_name(Common::UTF16ToUTF8(ffd.cFileName));
445#else 450#else
446 struct dirent dirent, *result = nullptr; 451 struct dirent dirent, *result = nullptr;
447 452
@@ -465,7 +470,7 @@ bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directo
465 found_entries += ret_entries; 470 found_entries += ret_entries;
466 471
467#ifdef _WIN32 472#ifdef _WIN32
468 } while (FindNextFile(handle_find, &ffd) != 0); 473 } while (FindNextFileW(handle_find, &ffd) != 0);
469 FindClose(handle_find); 474 FindClose(handle_find);
470#else 475#else
471 } 476 }
@@ -572,15 +577,23 @@ void CopyDir(const std::string &source_path, const std::string &dest_path)
572// Returns the current directory 577// Returns the current directory
573std::string GetCurrentDir() 578std::string GetCurrentDir()
574{ 579{
575 char *dir;
576 // Get the current working directory (getcwd uses malloc) 580 // Get the current working directory (getcwd uses malloc)
581#ifdef _WIN32
582 wchar_t *dir;
583 if (!(dir = _wgetcwd(nullptr, 0))) {
584#else
585 char *dir;
577 if (!(dir = getcwd(nullptr, 0))) { 586 if (!(dir = getcwd(nullptr, 0))) {
578 587#endif
579 LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: %s", 588 LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: %s",
580 GetLastErrorMsg()); 589 GetLastErrorMsg());
581 return nullptr; 590 return nullptr;
582 } 591 }
592#ifdef _WIN32
593 std::string strDir = Common::UTF16ToUTF8(dir);
594#else
583 std::string strDir = dir; 595 std::string strDir = dir;
596#endif
584 free(dir); 597 free(dir);
585 return strDir; 598 return strDir;
586} 599}
@@ -588,7 +601,11 @@ std::string GetCurrentDir()
588// Sets the current directory to the given directory 601// Sets the current directory to the given directory
589bool SetCurrentDir(const std::string &directory) 602bool SetCurrentDir(const std::string &directory)
590{ 603{
604#ifdef _WIN32
605 return _wchdir(Common::UTF8ToUTF16W(directory).c_str()) == 0;
606#else
591 return chdir(directory.c_str()) == 0; 607 return chdir(directory.c_str()) == 0;
608#endif
592} 609}
593 610
594#if defined(__APPLE__) 611#if defined(__APPLE__)
@@ -613,9 +630,9 @@ std::string& GetExeDirectory()
613 static std::string exe_path; 630 static std::string exe_path;
614 if (exe_path.empty()) 631 if (exe_path.empty())
615 { 632 {
616 TCHAR tchar_exe_path[2048]; 633 wchar_t wchar_exe_path[2048];
617 GetModuleFileName(nullptr, tchar_exe_path, 2048); 634 GetModuleFileNameW(nullptr, wchar_exe_path, 2048);
618 exe_path = Common::TStrToUTF8(tchar_exe_path); 635 exe_path = Common::UTF16ToUTF8(wchar_exe_path);
619 exe_path = exe_path.substr(0, exe_path.find_last_of('\\')); 636 exe_path = exe_path.substr(0, exe_path.find_last_of('\\'));
620 } 637 }
621 return exe_path; 638 return exe_path;
@@ -807,13 +824,12 @@ size_t WriteStringToFile(bool text_file, const std::string &str, const char *fil
807 824
808size_t ReadFileToString(bool text_file, const char *filename, std::string &str) 825size_t ReadFileToString(bool text_file, const char *filename, std::string &str)
809{ 826{
810 FileUtil::IOFile file(filename, text_file ? "r" : "rb"); 827 IOFile file(filename, text_file ? "r" : "rb");
811 auto const f = file.GetHandle();
812 828
813 if (!f) 829 if (!file)
814 return false; 830 return false;
815 831
816 str.resize(static_cast<u32>(GetSize(f))); 832 str.resize(static_cast<u32>(file.GetSize()));
817 return file.ReadArray(&str[0], str.size()); 833 return file.ReadArray(&str[0], str.size());
818} 834}
819 835
@@ -860,15 +876,10 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
860} 876}
861 877
862IOFile::IOFile() 878IOFile::IOFile()
863 : m_file(nullptr), m_good(true) 879{
864{} 880}
865
866IOFile::IOFile(std::FILE* file)
867 : m_file(file), m_good(true)
868{}
869 881
870IOFile::IOFile(const std::string& filename, const char openmode[]) 882IOFile::IOFile(const std::string& filename, const char openmode[])
871 : m_file(nullptr), m_good(true)
872{ 883{
873 Open(filename, openmode); 884 Open(filename, openmode);
874} 885}
@@ -879,7 +890,6 @@ IOFile::~IOFile()
879} 890}
880 891
881IOFile::IOFile(IOFile&& other) 892IOFile::IOFile(IOFile&& other)
882 : m_file(nullptr), m_good(true)
883{ 893{
884 Swap(other); 894 Swap(other);
885} 895}
@@ -900,7 +910,7 @@ bool IOFile::Open(const std::string& filename, const char openmode[])
900{ 910{
901 Close(); 911 Close();
902#ifdef _WIN32 912#ifdef _WIN32
903 _tfopen_s(&m_file, Common::UTF8ToTStr(filename).c_str(), Common::UTF8ToTStr(openmode).c_str()); 913 _wfopen_s(&m_file, Common::UTF8ToUTF16W(filename).c_str(), Common::UTF8ToUTF16W(openmode).c_str());
904#else 914#else
905 m_file = fopen(filename.c_str(), openmode); 915 m_file = fopen(filename.c_str(), openmode);
906#endif 916#endif
@@ -918,26 +928,12 @@ bool IOFile::Close()
918 return m_good; 928 return m_good;
919} 929}
920 930
921std::FILE* IOFile::ReleaseHandle() 931u64 IOFile::GetSize() const
922{
923 std::FILE* const ret = m_file;
924 m_file = nullptr;
925 return ret;
926}
927
928void IOFile::SetHandle(std::FILE* file)
929{
930 Close();
931 Clear();
932 m_file = file;
933}
934
935u64 IOFile::GetSize()
936{ 932{
937 if (IsOpen()) 933 if (IsOpen())
938 return FileUtil::GetSize(m_file); 934 return FileUtil::GetSize(m_file);
939 else 935
940 return 0; 936 return 0;
941} 937}
942 938
943bool IOFile::Seek(s64 off, int origin) 939bool IOFile::Seek(s64 off, int origin)
@@ -948,12 +944,12 @@ bool IOFile::Seek(s64 off, int origin)
948 return m_good; 944 return m_good;
949} 945}
950 946
951u64 IOFile::Tell() 947u64 IOFile::Tell() const
952{ 948{
953 if (IsOpen()) 949 if (IsOpen())
954 return ftello(m_file); 950 return ftello(m_file);
955 else 951
956 return -1; 952 return -1;
957} 953}
958 954
959bool IOFile::Flush() 955bool 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 @@
14 14
15#include "common/common_types.h" 15#include "common/common_types.h"
16 16
17#ifdef _MSC_VER
18#include "common/string_util.h"
19#endif
20
17// User directory indices for GetUserPath 21// User directory indices for GetUserPath
18enum { 22enum {
19 D_USER_IDX, 23 D_USER_IDX,
@@ -172,7 +176,6 @@ class IOFile : public NonCopyable
172{ 176{
173public: 177public:
174 IOFile(); 178 IOFile();
175 IOFile(std::FILE* file);
176 IOFile(const std::string& filename, const char openmode[]); 179 IOFile(const std::string& filename, const char openmode[]);
177 180
178 ~IOFile(); 181 ~IOFile();
@@ -188,6 +191,9 @@ public:
188 template <typename T> 191 template <typename T>
189 size_t ReadArray(T* data, size_t length) 192 size_t ReadArray(T* data, size_t length)
190 { 193 {
194 static_assert(std::is_standard_layout<T>(), "Given array does not consist of standard layout objects");
195 static_assert(std::is_trivially_copyable<T>(), "Given array does not consist of trivially copyable objects");
196
191 if (!IsOpen()) { 197 if (!IsOpen()) {
192 m_good = false; 198 m_good = false;
193 return -1; 199 return -1;
@@ -203,9 +209,8 @@ public:
203 template <typename T> 209 template <typename T>
204 size_t WriteArray(const T* data, size_t length) 210 size_t WriteArray(const T* data, size_t length)
205 { 211 {
206 static_assert(std::is_standard_layout<T>::value, "Given array does not consist of standard layout objects"); 212 static_assert(std::is_standard_layout<T>(), "Given array does not consist of standard layout objects");
207 // TODO: gcc 4.8 does not support is_trivially_copyable, but we really should check for it here. 213 static_assert(std::is_trivially_copyable<T>(), "Given array does not consist of trivially copyable objects");
208 //static_assert(std::is_trivially_copyable<T>::value, "Given array does not consist of trivially copyable objects");
209 214
210 if (!IsOpen()) { 215 if (!IsOpen()) {
211 m_good = false; 216 m_good = false;
@@ -235,32 +240,24 @@ public:
235 return WriteArray(&object, 1); 240 return WriteArray(&object, 1);
236 } 241 }
237 242
238 bool IsOpen() { return nullptr != m_file; } 243 bool IsOpen() const { return nullptr != m_file; }
239 244
240 // m_good is set to false when a read, write or other function fails 245 // m_good is set to false when a read, write or other function fails
241 bool IsGood() { return m_good; } 246 bool IsGood() const { return m_good; }
242 operator void*() { return m_good ? m_file : nullptr; } 247 explicit operator bool() const { return IsGood(); }
243
244 std::FILE* ReleaseHandle();
245
246 std::FILE* GetHandle() { return m_file; }
247
248 void SetHandle(std::FILE* file);
249 248
250 bool Seek(s64 off, int origin); 249 bool Seek(s64 off, int origin);
251 u64 Tell(); 250 u64 Tell() const;
252 u64 GetSize(); 251 u64 GetSize() const;
253 bool Resize(u64 size); 252 bool Resize(u64 size);
254 bool Flush(); 253 bool Flush();
255 254
256 // clear error state 255 // clear error state
257 void Clear() { m_good = true; std::clearerr(m_file); } 256 void Clear() { m_good = true; std::clearerr(m_file); }
258 257
259 std::FILE* m_file;
260 bool m_good;
261private: 258private:
262 IOFile(IOFile&); 259 std::FILE* m_file = nullptr;
263 IOFile& operator=(IOFile& other); 260 bool m_good = true;
264}; 261};
265 262
266} // namespace 263} // 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 {
34 SUB(Kernel, SVC) \ 34 SUB(Kernel, SVC) \
35 CLS(Service) \ 35 CLS(Service) \
36 SUB(Service, SRV) \ 36 SUB(Service, SRV) \
37 SUB(Service, FRD) \
37 SUB(Service, FS) \ 38 SUB(Service, FS) \
38 SUB(Service, ERR) \ 39 SUB(Service, ERR) \
39 SUB(Service, APT) \ 40 SUB(Service, APT) \
@@ -46,8 +47,10 @@ namespace Log {
46 SUB(Service, NIM) \ 47 SUB(Service, NIM) \
47 SUB(Service, NWM) \ 48 SUB(Service, NWM) \
48 SUB(Service, CAM) \ 49 SUB(Service, CAM) \
50 SUB(Service, CECD) \
49 SUB(Service, CFG) \ 51 SUB(Service, CFG) \
50 SUB(Service, DSP) \ 52 SUB(Service, DSP) \
53 SUB(Service, DLP) \
51 SUB(Service, HID) \ 54 SUB(Service, HID) \
52 SUB(Service, SOC) \ 55 SUB(Service, SOC) \
53 SUB(Service, IR) \ 56 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 {
49 Service, ///< HLE implementation of system services. Each major service 49 Service, ///< HLE implementation of system services. Each major service
50 /// should have its own subclass. 50 /// should have its own subclass.
51 Service_SRV, ///< The SRV (Service Directory) implementation 51 Service_SRV, ///< The SRV (Service Directory) implementation
52 Service_FRD, ///< The FRD (Friends) service
52 Service_FS, ///< The FS (Filesystem) service implementation 53 Service_FS, ///< The FS (Filesystem) service implementation
53 Service_ERR, ///< The ERR (Error) port implementation 54 Service_ERR, ///< The ERR (Error) port implementation
54 Service_APT, ///< The APT (Applets) service 55 Service_APT, ///< The APT (Applets) service
@@ -61,8 +62,10 @@ enum class Class : ClassType {
61 Service_NIM, ///< The NIM (Network interface manager) service 62 Service_NIM, ///< The NIM (Network interface manager) service
62 Service_NWM, ///< The NWM (Network wlan manager) service 63 Service_NWM, ///< The NWM (Network wlan manager) service
63 Service_CAM, ///< The CAM (Camera) service 64 Service_CAM, ///< The CAM (Camera) service
65 Service_CECD, ///< The CECD (StreetPass) service
64 Service_CFG, ///< The CFG (Configuration) service 66 Service_CFG, ///< The CFG (Configuration) service
65 Service_DSP, ///< The DSP (DSP control) service 67 Service_DSP, ///< The DSP (DSP control) service
68 Service_DLP, ///< The DLP (Download Play) service
66 Service_HID, ///< The HID (Human interface device) service 69 Service_HID, ///< The HID (Human interface device) service
67 Service_SOC, ///< The SOC (Socket) service 70 Service_SOC, ///< The SOC (Socket) service
68 Service_IR, ///< The IR service 71 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 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <algorithm>
8#include <memory>
9
10namespace Common {
11
12template <typename T, typename... Args>
13std::unique_ptr<T> make_unique(Args&&... args) {
14 return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
15}
16
17} // 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)
320#endif 320#endif
321} 321}
322 322
323static std::string UTF16ToUTF8(const std::wstring& input) 323static std::wstring CPToUTF16(u32 code_page, const std::string& input)
324{ 324{
325 auto const size = WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast<int>(input.size()), nullptr, 0, nullptr, nullptr); 325 auto const size = MultiByteToWideChar(code_page, 0, input.data(), static_cast<int>(input.size()), nullptr, 0);
326 326
327 std::string output; 327 std::wstring output;
328 output.resize(size); 328 output.resize(size);
329 329
330 if (size == 0 || size != WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast<int>(input.size()), &output[0], static_cast<int>(output.size()), nullptr, nullptr)) 330 if (size == 0 || size != MultiByteToWideChar(code_page, 0, input.data(), static_cast<int>(input.size()), &output[0], static_cast<int>(output.size())))
331 output.clear(); 331 output.clear();
332 332
333 return output; 333 return output;
334} 334}
335 335
336static std::wstring CPToUTF16(u32 code_page, const std::string& input) 336std::string UTF16ToUTF8(const std::wstring& input)
337{ 337{
338 auto const size = MultiByteToWideChar(code_page, 0, input.data(), static_cast<int>(input.size()), nullptr, 0); 338 auto const size = WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast<int>(input.size()), nullptr, 0, nullptr, nullptr);
339 339
340 std::wstring output; 340 std::string output;
341 output.resize(size); 341 output.resize(size);
342 342
343 if (size == 0 || size != MultiByteToWideChar(code_page, 0, input.data(), static_cast<int>(input.size()), &output[0], static_cast<int>(output.size()))) 343 if (size == 0 || size != WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast<int>(input.size()), &output[0], static_cast<int>(output.size()), nullptr, nullptr))
344 output.clear(); 344 output.clear();
345 345
346 return output; 346 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);
95std::string SHIFTJISToUTF8(const std::string& str); 95std::string SHIFTJISToUTF8(const std::string& str);
96 96
97#ifdef _WIN32 97#ifdef _WIN32
98 98std::string UTF16ToUTF8(const std::wstring& input);
99std::wstring UTF8ToUTF16W(const std::string& str); 99std::wstring UTF8ToUTF16W(const std::string& str);
100 100
101#ifdef _UNICODE 101#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 @@
30# endif 30# endif
31#endif 31#endif
32 32
33namespace Common 33namespace Common {
34{
35 34
36int CurrentThreadId(); 35int CurrentThreadId();
37 36
@@ -43,55 +42,55 @@ public:
43 Event() : is_set(false) {} 42 Event() : is_set(false) {}
44 43
45 void Set() { 44 void Set() {
46 std::lock_guard<std::mutex> lk(m_mutex); 45 std::lock_guard<std::mutex> lk(mutex);
47 if (!is_set) { 46 if (!is_set) {
48 is_set = true; 47 is_set = true;
49 m_condvar.notify_one(); 48 condvar.notify_one();
50 } 49 }
51 } 50 }
52 51
53 void Wait() { 52 void Wait() {
54 std::unique_lock<std::mutex> lk(m_mutex); 53 std::unique_lock<std::mutex> lk(mutex);
55 m_condvar.wait(lk, [&]{ return is_set; }); 54 condvar.wait(lk, [&]{ return is_set; });
56 is_set = false; 55 is_set = false;
57 } 56 }
58 57
59 void Reset() { 58 void Reset() {
60 std::unique_lock<std::mutex> lk(m_mutex); 59 std::unique_lock<std::mutex> lk(mutex);
61 // no other action required, since wait loops on the predicate and any lingering signal will get cleared on the first iteration 60 // no other action required, since wait loops on the predicate and any lingering signal will get cleared on the first iteration
62 is_set = false; 61 is_set = false;
63 } 62 }
64 63
65private: 64private:
66 bool is_set; 65 bool is_set;
67 std::condition_variable m_condvar; 66 std::condition_variable condvar;
68 std::mutex m_mutex; 67 std::mutex mutex;
69}; 68};
70 69
71class Barrier { 70class Barrier {
72public: 71public:
73 Barrier(size_t count) : m_count(count), m_waiting(0) {} 72 explicit Barrier(size_t count_) : count(count_), waiting(0), generation(0) {}
74 73
75 /// Blocks until all "count" threads have called Sync() 74 /// Blocks until all "count" threads have called Sync()
76 void Sync() { 75 void Sync() {
77 std::unique_lock<std::mutex> lk(m_mutex); 76 std::unique_lock<std::mutex> lk(mutex);
77 const size_t current_generation = generation;
78 78
79 // TODO: broken when next round of Sync()s 79 if (++waiting == count) {
80 // is entered before all waiting threads return from the notify_all 80 generation++;
81 81 waiting = 0;
82 if (++m_waiting == m_count) { 82 condvar.notify_all();
83 m_waiting = 0;
84 m_condvar.notify_all();
85 } else { 83 } else {
86 m_condvar.wait(lk, [&]{ return m_waiting == 0; }); 84 condvar.wait(lk, [this, current_generation]{ return current_generation != generation; });
87 } 85 }
88 } 86 }
89 87
90private: 88private:
91 std::condition_variable m_condvar; 89 std::condition_variable condvar;
92 std::mutex m_mutex; 90 std::mutex mutex;
93 const size_t m_count; 91 const size_t count;
94 size_t m_waiting; 92 size_t waiting;
93 size_t generation; // Incremented once each time the barrier is used
95}; 94};
96 95
97void SleepCurrentThread(int ms); 96void SleepCurrentThread(int ms);
@@ -100,8 +99,7 @@ void SwitchCurrentThread(); // On Linux, this is equal to sleep 1ms
100// Use this function during a spin-wait to make the current thread 99// Use this function during a spin-wait to make the current thread
101// relax while another thread is working. This may be more efficient 100// relax while another thread is working. This may be more efficient
102// than using events because event functions use kernel calls. 101// than using events because event functions use kernel calls.
103inline void YieldCPU() 102inline void YieldCPU() {
104{
105 std::this_thread::yield(); 103 std::this_thread::yield();
106} 104}
107 105
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)
455 Write32(u32(distance)); 455 Write32(u32(distance));
456} 456}
457 457
458FixupBranch XEmitter::CALL()
459{
460 FixupBranch branch;
461 branch.type = 1;
462 branch.ptr = code + 5;
463
464 Write8(0xE8);
465 Write32(0);
466
467 return branch;
468}
469
458FixupBranch XEmitter::J(bool force5bytes) 470FixupBranch XEmitter::J(bool force5bytes)
459{ 471{
460 FixupBranch branch; 472 FixupBranch branch;
@@ -531,6 +543,22 @@ void XEmitter::SetJumpTarget(const FixupBranch& branch)
531 } 543 }
532} 544}
533 545
546void XEmitter::SetJumpTarget(const FixupBranch& branch, const u8* target)
547{
548 if (branch.type == 0)
549 {
550 s64 distance = (s64)(target - branch.ptr);
551 ASSERT_MSG(distance >= -0x80 && distance < 0x80, "Jump target too far away, needs force5Bytes = true");
552 branch.ptr[-1] = (u8)(s8)distance;
553 }
554 else if (branch.type == 1)
555 {
556 s64 distance = (s64)(target - branch.ptr);
557 ASSERT_MSG(distance >= -0x80000000LL && distance < 0x80000000LL, "Jump target too far away, needs indirect register");
558 ((s32*)branch.ptr)[-1] = (s32)distance;
559 }
560}
561
534//Single byte opcodes 562//Single byte opcodes
535//There is no PUSHAD/POPAD in 64-bit mode. 563//There is no PUSHAD/POPAD in 64-bit mode.
536void XEmitter::INT3() {Write8(0xCC);} 564void 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:
425#undef CALL 425#undef CALL
426#endif 426#endif
427 void CALL(const void* fnptr); 427 void CALL(const void* fnptr);
428 FixupBranch CALL();
428 void CALLptr(OpArg arg); 429 void CALLptr(OpArg arg);
429 430
430 FixupBranch J_CC(CCFlags conditionCode, bool force5bytes = false); 431 FixupBranch J_CC(CCFlags conditionCode, bool force5bytes = false);
431 void J_CC(CCFlags conditionCode, const u8* addr, bool force5Bytes = false); 432 void J_CC(CCFlags conditionCode, const u8* addr, bool force5Bytes = false);
432 433
433 void SetJumpTarget(const FixupBranch& branch); 434 void SetJumpTarget(const FixupBranch& branch);
435 void SetJumpTarget(const FixupBranch& branch, const u8* target);
434 436
435 void SETcc(CCFlags flag, OpArg dest); 437 void SETcc(CCFlags flag, OpArg dest);
436 // Note: CMOV brings small if any benefit on current cpus. 438 // 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
68 hle/service/cfg/cfg_s.cpp 68 hle/service/cfg/cfg_s.cpp
69 hle/service/cfg/cfg_u.cpp 69 hle/service/cfg/cfg_u.cpp
70 hle/service/csnd_snd.cpp 70 hle/service/csnd_snd.cpp
71 hle/service/dlp_srvr.cpp
71 hle/service/dsp_dsp.cpp 72 hle/service/dsp_dsp.cpp
72 hle/service/err_f.cpp 73 hle/service/err_f.cpp
73 hle/service/frd/frd.cpp 74 hle/service/frd/frd.cpp
@@ -200,6 +201,7 @@ set(HEADERS
200 hle/service/cfg/cfg_s.h 201 hle/service/cfg/cfg_s.h
201 hle/service/cfg/cfg_u.h 202 hle/service/cfg/cfg_u.h
202 hle/service/csnd_snd.h 203 hle/service/csnd_snd.h
204 hle/service/dlp_srvr.h
203 hle/service/dsp_dsp.h 205 hle/service/dsp_dsp.h
204 hle/service/err_f.h 206 hle/service/err_f.h
205 hle/service/frd/frd.h 207 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 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cstring> 5#include <cstring>
6 6#include <memory>
7#include "common/make_unique.h"
8 7
9#include "core/arm/skyeye_common/armstate.h" 8#include "core/arm/skyeye_common/armstate.h"
10#include "core/arm/skyeye_common/armsupp.h" 9#include "core/arm/skyeye_common/armsupp.h"
@@ -18,7 +17,7 @@
18#include "core/core_timing.h" 17#include "core/core_timing.h"
19 18
20ARM_DynCom::ARM_DynCom(PrivilegeMode initial_mode) { 19ARM_DynCom::ARM_DynCom(PrivilegeMode initial_mode) {
21 state = Common::make_unique<ARMul_State>(initial_mode); 20 state = std::make_unique<ARMul_State>(initial_mode);
22} 21}
23 22
24ARM_DynCom::~ARM_DynCom() { 23ARM_DynCom::~ARM_DynCom() {
@@ -94,7 +93,7 @@ void ARM_DynCom::ResetContext(Core::ThreadContext& context, u32 stack_top, u32 e
94 context.cpu_registers[0] = arg; 93 context.cpu_registers[0] = arg;
95 context.pc = entry_point; 94 context.pc = entry_point;
96 context.sp = stack_top; 95 context.sp = stack_top;
97 context.cpsr = 0x1F; // Usermode 96 context.cpsr = 0x1F | ((entry_point & 1) << 5); // Usermode and THUMB mode
98} 97}
99 98
100void ARM_DynCom::SaveContext(Core::ThreadContext& ctx) { 99void 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 {
36 CALL = (1 << 4), 36 CALL = (1 << 4),
37 RET = (1 << 5), 37 RET = (1 << 5),
38 END_OF_PAGE = (1 << 6), 38 END_OF_PAGE = (1 << 6),
39 THUMB = (1 << 7) 39 THUMB = (1 << 7),
40 SINGLE_STEP = (1 << 8)
40}; 41};
41 42
42#define RM BITS(sht_oper, 0, 3) 43#define RM BITS(sht_oper, 0, 3)
@@ -3466,7 +3467,35 @@ enum {
3466 3467
3467MICROPROFILE_DEFINE(DynCom_Decode, "DynCom", "Decode", MP_RGB(255, 64, 64)); 3468MICROPROFILE_DEFINE(DynCom_Decode, "DynCom", "Decode", MP_RGB(255, 64, 64));
3468 3469
3469static int InterpreterTranslate(ARMul_State* cpu, int& bb_start, u32 addr) { 3470static unsigned int InterpreterTranslateInstruction(const ARMul_State* cpu, const u32 phys_addr, ARM_INST_PTR& inst_base) {
3471 unsigned int inst_size = 4;
3472 unsigned int inst = Memory::Read32(phys_addr & 0xFFFFFFFC);
3473
3474 // If we are in Thumb mode, we'll translate one Thumb instruction to the corresponding ARM instruction
3475 if (cpu->TFlag) {
3476 u32 arm_inst;
3477 ThumbDecodeStatus state = DecodeThumbInstruction(inst, phys_addr, &arm_inst, &inst_size, &inst_base);
3478
3479 // We have translated the Thumb branch instruction in the Thumb decoder
3480 if (state == ThumbDecodeStatus::BRANCH) {
3481 return inst_size;
3482 }
3483 inst = arm_inst;
3484 }
3485
3486 int idx;
3487 if (DecodeARMInstruction(inst, &idx) == ARMDecodeStatus::FAILURE) {
3488 std::string disasm = ARM_Disasm::Disassemble(phys_addr, inst);
3489 LOG_ERROR(Core_ARM11, "Decode failure.\tPC : [0x%x]\tInstruction : %s [%x]", phys_addr, disasm.c_str(), inst);
3490 LOG_ERROR(Core_ARM11, "cpsr=0x%x, cpu->TFlag=%d, r15=0x%x", cpu->Cpsr, cpu->TFlag, cpu->Reg[15]);
3491 CITRA_IGNORE_EXIT(-1);
3492 }
3493 inst_base = arm_instruction_trans[idx](inst, idx);
3494
3495 return inst_size;
3496}
3497
3498static int InterpreterTranslateBlock(ARMul_State* cpu, int& bb_start, u32 addr) {
3470 Common::Profiling::ScopeTimer timer_decode(profile_decode); 3499 Common::Profiling::ScopeTimer timer_decode(profile_decode);
3471 MICROPROFILE_SCOPE(DynCom_Decode); 3500 MICROPROFILE_SCOPE(DynCom_Decode);
3472 3501
@@ -3475,8 +3504,6 @@ static int InterpreterTranslate(ARMul_State* cpu, int& bb_start, u32 addr) {
3475 // Go on next, until terminal instruction 3504 // Go on next, until terminal instruction
3476 // Save start addr of basicblock in CreamCache 3505 // Save start addr of basicblock in CreamCache
3477 ARM_INST_PTR inst_base = nullptr; 3506 ARM_INST_PTR inst_base = nullptr;
3478 unsigned int inst, inst_size = 4;
3479 int idx;
3480 int ret = NON_BRANCH; 3507 int ret = NON_BRANCH;
3481 int size = 0; // instruction size of basic block 3508 int size = 0; // instruction size of basic block
3482 bb_start = top; 3509 bb_start = top;
@@ -3485,30 +3512,10 @@ static int InterpreterTranslate(ARMul_State* cpu, int& bb_start, u32 addr) {
3485 u32 pc_start = cpu->Reg[15]; 3512 u32 pc_start = cpu->Reg[15];
3486 3513
3487 while (ret == NON_BRANCH) { 3514 while (ret == NON_BRANCH) {
3488 inst = Memory::Read32(phys_addr & 0xFFFFFFFC); 3515 unsigned int inst_size = InterpreterTranslateInstruction(cpu, phys_addr, inst_base);
3489 3516
3490 size++; 3517 size++;
3491 // If we are in Thumb mode, we'll translate one Thumb instruction to the corresponding ARM instruction
3492 if (cpu->TFlag) {
3493 u32 arm_inst;
3494 ThumbDecodeStatus state = DecodeThumbInstruction(inst, phys_addr, &arm_inst, &inst_size, &inst_base);
3495
3496 // We have translated the Thumb branch instruction in the Thumb decoder
3497 if (state == ThumbDecodeStatus::BRANCH) {
3498 goto translated;
3499 }
3500 inst = arm_inst;
3501 }
3502
3503 if (DecodeARMInstruction(inst, &idx) == ARMDecodeStatus::FAILURE) {
3504 std::string disasm = ARM_Disasm::Disassemble(phys_addr, inst);
3505 LOG_ERROR(Core_ARM11, "Decode failure.\tPC : [0x%x]\tInstruction : %s [%x]", phys_addr, disasm.c_str(), inst);
3506 LOG_ERROR(Core_ARM11, "cpsr=0x%x, cpu->TFlag=%d, r15=0x%x", cpu->Cpsr, cpu->TFlag, cpu->Reg[15]);
3507 CITRA_IGNORE_EXIT(-1);
3508 }
3509 inst_base = arm_instruction_trans[idx](inst, idx);
3510 3518
3511translated:
3512 phys_addr += inst_size; 3519 phys_addr += inst_size;
3513 3520
3514 if ((phys_addr & 0xfff) == 0) { 3521 if ((phys_addr & 0xfff) == 0) {
@@ -3522,6 +3529,27 @@ translated:
3522 return KEEP_GOING; 3529 return KEEP_GOING;
3523} 3530}
3524 3531
3532static int InterpreterTranslateSingle(ARMul_State* cpu, int& bb_start, u32 addr) {
3533 Common::Profiling::ScopeTimer timer_decode(profile_decode);
3534 MICROPROFILE_SCOPE(DynCom_Decode);
3535
3536 ARM_INST_PTR inst_base = nullptr;
3537 bb_start = top;
3538
3539 u32 phys_addr = addr;
3540 u32 pc_start = cpu->Reg[15];
3541
3542 InterpreterTranslateInstruction(cpu, phys_addr, inst_base);
3543
3544 if (inst_base->br == NON_BRANCH) {
3545 inst_base->br = SINGLE_STEP;
3546 }
3547
3548 cpu->instruction_cache[pc_start] = bb_start;
3549
3550 return KEEP_GOING;
3551}
3552
3525static int clz(unsigned int x) { 3553static int clz(unsigned int x) {
3526 int n; 3554 int n;
3527 if (x == 0) return (32); 3555 if (x == 0) return (32);
@@ -3871,8 +3899,11 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
3871 auto itr = cpu->instruction_cache.find(cpu->Reg[15]); 3899 auto itr = cpu->instruction_cache.find(cpu->Reg[15]);
3872 if (itr != cpu->instruction_cache.end()) { 3900 if (itr != cpu->instruction_cache.end()) {
3873 ptr = itr->second; 3901 ptr = itr->second;
3902 } else if (cpu->NumInstrsToExecute != 1) {
3903 if (InterpreterTranslateBlock(cpu, ptr, cpu->Reg[15]) == FETCH_EXCEPTION)
3904 goto END;
3874 } else { 3905 } else {
3875 if (InterpreterTranslate(cpu, ptr, cpu->Reg[15]) == FETCH_EXCEPTION) 3906 if (InterpreterTranslateSingle(cpu, ptr, cpu->Reg[15]) == FETCH_EXCEPTION)
3876 goto END; 3907 goto END;
3877 } 3908 }
3878 3909
@@ -3924,9 +3955,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
3924 if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { 3955 if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
3925 add_inst* const inst_cream = (add_inst*)inst_base->component; 3956 add_inst* const inst_cream = (add_inst*)inst_base->component;
3926 3957
3927 u32 rn_val = RN; 3958 u32 rn_val = CHECK_READ_REG15_WA(cpu, inst_cream->Rn);
3928 if (inst_cream->Rn == 15)
3929 rn_val += 2 * cpu->GetInstructionSize();
3930 3959
3931 bool carry; 3960 bool carry;
3932 bool overflow; 3961 bool overflow;
@@ -4051,11 +4080,12 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
4051 if ((inst_base->cond == ConditionCode::AL) || CondPassed(cpu, inst_base->cond)) { 4080 if ((inst_base->cond == ConditionCode::AL) || CondPassed(cpu, inst_base->cond)) {
4052 unsigned int inst = inst_cream->inst; 4081 unsigned int inst = inst_cream->inst;
4053 if (BITS(inst, 20, 27) == 0x12 && BITS(inst, 4, 7) == 0x3) { 4082 if (BITS(inst, 20, 27) == 0x12 && BITS(inst, 4, 7) == 0x3) {
4083 const u32 jump_address = cpu->Reg[inst_cream->val.Rm];
4054 cpu->Reg[14] = (cpu->Reg[15] + cpu->GetInstructionSize()); 4084 cpu->Reg[14] = (cpu->Reg[15] + cpu->GetInstructionSize());
4055 if(cpu->TFlag) 4085 if(cpu->TFlag)
4056 cpu->Reg[14] |= 0x1; 4086 cpu->Reg[14] |= 0x1;
4057 cpu->Reg[15] = cpu->Reg[inst_cream->val.Rm] & 0xfffffffe; 4087 cpu->Reg[15] = jump_address & 0xfffffffe;
4058 cpu->TFlag = cpu->Reg[inst_cream->val.Rm] & 0x1; 4088 cpu->TFlag = jump_address & 0x1;
4059 } else { 4089 } else {
4060 cpu->Reg[14] = (cpu->Reg[15] + cpu->GetInstructionSize()); 4090 cpu->Reg[14] = (cpu->Reg[15] + cpu->GetInstructionSize());
4061 cpu->TFlag = 0x1; 4091 cpu->TFlag = 0x1;
@@ -6136,9 +6166,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
6136 if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { 6166 if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
6137 sub_inst* const inst_cream = (sub_inst*)inst_base->component; 6167 sub_inst* const inst_cream = (sub_inst*)inst_base->component;
6138 6168
6139 u32 rn_val = RN; 6169 u32 rn_val = CHECK_READ_REG15_WA(cpu, inst_cream->Rn);
6140 if (inst_cream->Rn == 15)
6141 rn_val += 2 * cpu->GetInstructionSize();
6142 6170
6143 bool carry; 6171 bool carry;
6144 bool overflow; 6172 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 @@
4 4
5#include <memory> 5#include <memory>
6 6
7#include "common/make_unique.h"
8#include "common/logging/log.h" 7#include "common/logging/log.h"
9 8
10#include "core/core.h" 9#include "core/core.h"
@@ -74,8 +73,8 @@ void Stop() {
74 73
75/// Initialize the core 74/// Initialize the core
76void Init() { 75void Init() {
77 g_sys_core = Common::make_unique<ARM_DynCom>(USER32MODE); 76 g_sys_core = std::make_unique<ARM_DynCom>(USER32MODE);
78 g_app_core = Common::make_unique<ARM_DynCom>(USER32MODE); 77 g_app_core = std::make_unique<ARM_DynCom>(USER32MODE);
79 78
80 LOG_DEBUG(Core, "Initialized OK"); 79 LOG_DEBUG(Core, "Initialized OK");
81} 80}
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 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <memory>
6#include <vector> 7#include <vector>
7 8
8#include "common/common_types.h" 9#include "common/common_types.h"
9#include "common/file_util.h" 10#include "common/file_util.h"
10#include "common/logging/log.h" 11#include "common/logging/log.h"
11#include "common/make_unique.h"
12#include "common/string_util.h" 12#include "common/string_util.h"
13 13
14#include "core/file_sys/archive_extsavedata.h" 14#include "core/file_sys/archive_extsavedata.h"
@@ -84,7 +84,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_ExtSaveData::Open(cons
84 ErrorSummary::InvalidState, ErrorLevel::Status); 84 ErrorSummary::InvalidState, ErrorLevel::Status);
85 } 85 }
86 } 86 }
87 auto archive = Common::make_unique<DiskArchive>(fullpath); 87 auto archive = std::make_unique<DiskArchive>(fullpath);
88 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); 88 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
89} 89}
90 90
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 @@
7 7
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "common/make_unique.h"
11 10
12#include "core/file_sys/archive_romfs.h" 11#include "core/file_sys/archive_romfs.h"
13#include "core/file_sys/ivfc_archive.h" 12#include "core/file_sys/ivfc_archive.h"
@@ -25,7 +24,7 @@ ArchiveFactory_RomFS::ArchiveFactory_RomFS(Loader::AppLoader& app_loader) {
25} 24}
26 25
27ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_RomFS::Open(const Path& path) { 26ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_RomFS::Open(const Path& path) {
28 auto archive = Common::make_unique<IVFCArchive>(romfs_file, data_offset, data_size); 27 auto archive = std::make_unique<IVFCArchive>(romfs_file, data_offset, data_size);
29 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); 28 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
30} 29}
31 30
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 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <memory>
6 7
7#include "common/common_types.h" 8#include "common/common_types.h"
8#include "common/file_util.h" 9#include "common/file_util.h"
9#include "common/logging/log.h" 10#include "common/logging/log.h"
10#include "common/make_unique.h"
11#include "common/string_util.h" 11#include "common/string_util.h"
12 12
13#include "core/file_sys/archive_savedata.h" 13#include "core/file_sys/archive_savedata.h"
@@ -53,7 +53,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveData::Open(const P
53 ErrorSummary::InvalidState, ErrorLevel::Status); 53 ErrorSummary::InvalidState, ErrorLevel::Status);
54 } 54 }
55 55
56 auto archive = Common::make_unique<DiskArchive>(std::move(concrete_mount_point)); 56 auto archive = std::make_unique<DiskArchive>(std::move(concrete_mount_point));
57 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); 57 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
58} 58}
59 59
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 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <memory>
6#include <vector> 7#include <vector>
7 8
8#include "common/common_types.h" 9#include "common/common_types.h"
9#include "common/file_util.h" 10#include "common/file_util.h"
10#include "common/logging/log.h" 11#include "common/logging/log.h"
11#include "common/make_unique.h"
12#include "common/string_util.h" 12#include "common/string_util.h"
13 13
14#include "core/file_sys/archive_savedatacheck.h" 14#include "core/file_sys/archive_savedatacheck.h"
@@ -44,7 +44,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveDataCheck::Open(co
44 } 44 }
45 auto size = file->GetSize(); 45 auto size = file->GetSize();
46 46
47 auto archive = Common::make_unique<IVFCArchive>(file, 0, size); 47 auto archive = std::make_unique<IVFCArchive>(file, 0, size);
48 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); 48 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
49} 49}
50 50
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 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <memory>
6 7
7#include "common/file_util.h" 8#include "common/file_util.h"
8#include "common/logging/log.h" 9#include "common/logging/log.h"
9#include "common/make_unique.h"
10 10
11#include "core/file_sys/archive_sdmc.h" 11#include "core/file_sys/archive_sdmc.h"
12#include "core/file_sys/disk_archive.h" 12#include "core/file_sys/disk_archive.h"
@@ -36,7 +36,7 @@ bool ArchiveFactory_SDMC::Initialize() {
36} 36}
37 37
38ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SDMC::Open(const Path& path) { 38ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SDMC::Open(const Path& path) {
39 auto archive = Common::make_unique<DiskArchive>(sdmc_directory); 39 auto archive = std::make_unique<DiskArchive>(sdmc_directory);
40 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); 40 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
41} 41}
42 42
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 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <memory>
6#include <vector> 7#include <vector>
7 8
8#include "common/common_types.h" 9#include "common/common_types.h"
9#include "common/file_util.h" 10#include "common/file_util.h"
10#include "common/make_unique.h"
11#include "common/string_util.h" 11#include "common/string_util.h"
12 12
13#include "core/file_sys/archive_systemsavedata.h" 13#include "core/file_sys/archive_systemsavedata.h"
@@ -59,7 +59,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SystemSaveData::Open(c
59 return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS, 59 return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS,
60 ErrorSummary::InvalidState, ErrorLevel::Status); 60 ErrorSummary::InvalidState, ErrorLevel::Status);
61 } 61 }
62 auto archive = Common::make_unique<DiskArchive>(fullpath); 62 auto archive = std::make_unique<DiskArchive>(fullpath);
63 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); 63 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
64} 64}
65 65
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 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <cstdio> 6#include <cstdio>
7#include <memory>
7 8
8#include "common/common_types.h" 9#include "common/common_types.h"
9#include "common/file_util.h" 10#include "common/file_util.h"
10#include "common/logging/log.h" 11#include "common/logging/log.h"
11#include "common/make_unique.h"
12 12
13#include "core/file_sys/disk_archive.h" 13#include "core/file_sys/disk_archive.h"
14 14
@@ -19,7 +19,7 @@ namespace FileSys {
19 19
20ResultVal<std::unique_ptr<FileBackend>> DiskArchive::OpenFile(const Path& path, const Mode mode) const { 20ResultVal<std::unique_ptr<FileBackend>> DiskArchive::OpenFile(const Path& path, const Mode mode) const {
21 LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex); 21 LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex);
22 auto file = Common::make_unique<DiskFile>(*this, path, mode); 22 auto file = std::make_unique<DiskFile>(*this, path, mode);
23 ResultCode result = file->Open(); 23 ResultCode result = file->Open();
24 if (result.IsError()) 24 if (result.IsError())
25 return result; 25 return result;
@@ -83,7 +83,7 @@ bool DiskArchive::RenameDirectory(const Path& src_path, const Path& dest_path) c
83 83
84std::unique_ptr<DirectoryBackend> DiskArchive::OpenDirectory(const Path& path) const { 84std::unique_ptr<DirectoryBackend> DiskArchive::OpenDirectory(const Path& path) const {
85 LOG_DEBUG(Service_FS, "called path=%s", path.DebugStr().c_str()); 85 LOG_DEBUG(Service_FS, "called path=%s", path.DebugStr().c_str());
86 auto directory = Common::make_unique<DiskDirectory>(*this, path); 86 auto directory = std::make_unique<DiskDirectory>(*this, path);
87 if (!directory->Open()) 87 if (!directory->Open())
88 return nullptr; 88 return nullptr;
89 return std::move(directory); 89 return std::move(directory);
@@ -132,7 +132,7 @@ ResultCode DiskFile::Open() {
132 // Open the file in binary mode, to avoid problems with CR/LF on Windows systems 132 // Open the file in binary mode, to avoid problems with CR/LF on Windows systems
133 mode_string += "b"; 133 mode_string += "b";
134 134
135 file = Common::make_unique<FileUtil::IOFile>(path, mode_string.c_str()); 135 file = std::make_unique<FileUtil::IOFile>(path, mode_string.c_str());
136 if (file->IsOpen()) 136 if (file->IsOpen())
137 return RESULT_SUCCESS; 137 return RESULT_SUCCESS;
138 return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, ErrorSummary::NotFound, ErrorLevel::Status); 138 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 @@
7 7
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "common/make_unique.h"
11 10
12#include "core/file_sys/ivfc_archive.h" 11#include "core/file_sys/ivfc_archive.h"
13 12
@@ -21,7 +20,7 @@ std::string IVFCArchive::GetName() const {
21} 20}
22 21
23ResultVal<std::unique_ptr<FileBackend>> IVFCArchive::OpenFile(const Path& path, const Mode mode) const { 22ResultVal<std::unique_ptr<FileBackend>> IVFCArchive::OpenFile(const Path& path, const Mode mode) const {
24 return MakeResult<std::unique_ptr<FileBackend>>(Common::make_unique<IVFCFile>(romfs_file, data_offset, data_size)); 23 return MakeResult<std::unique_ptr<FileBackend>>(std::make_unique<IVFCFile>(romfs_file, data_offset, data_size));
25} 24}
26 25
27ResultCode IVFCArchive::DeleteFile(const Path& path) const { 26ResultCode IVFCArchive::DeleteFile(const Path& path) const {
@@ -58,7 +57,7 @@ bool IVFCArchive::RenameDirectory(const Path& src_path, const Path& dest_path) c
58} 57}
59 58
60std::unique_ptr<DirectoryBackend> IVFCArchive::OpenDirectory(const Path& path) const { 59std::unique_ptr<DirectoryBackend> IVFCArchive::OpenDirectory(const Path& path) const {
61 return Common::make_unique<IVFCDirectory>(); 60 return std::make_unique<IVFCDirectory>();
62} 61}
63 62
64u64 IVFCArchive::GetFreeBytes() const { 63u64 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;
60const u32 CPSR_REGISTER = 25; 60const u32 CPSR_REGISTER = 25;
61const u32 FPSCR_REGISTER = 58; 61const u32 FPSCR_REGISTER = 58;
62 62
63// For sample XML files see the GDB source /gdb/features
64// GDB also wants the l character at the start
65// This XML defines what the registers are for this specific ARM device
66static const char* target_xml =
67R"(l<?xml version="1.0"?>
68<!DOCTYPE target SYSTEM "gdb-target.dtd">
69<target version="1.0">
70 <feature name="org.gnu.gdb.arm.core">
71 <reg name="r0" bitsize="32"/>
72 <reg name="r1" bitsize="32"/>
73 <reg name="r2" bitsize="32"/>
74 <reg name="r3" bitsize="32"/>
75 <reg name="r4" bitsize="32"/>
76 <reg name="r5" bitsize="32"/>
77 <reg name="r6" bitsize="32"/>
78 <reg name="r7" bitsize="32"/>
79 <reg name="r8" bitsize="32"/>
80 <reg name="r9" bitsize="32"/>
81 <reg name="r10" bitsize="32"/>
82 <reg name="r11" bitsize="32"/>
83 <reg name="r12" bitsize="32"/>
84 <reg name="sp" bitsize="32" type="data_ptr"/>
85 <reg name="lr" bitsize="32"/>
86 <reg name="pc" bitsize="32" type="code_ptr"/>
87
88 <!-- The CPSR is register 25, rather than register 16, because
89 the FPA registers historically were placed between the PC
90 and the CPSR in the "g" packet. -->
91
92 <reg name="cpsr" bitsize="32" regnum="25"/>
93 </feature>
94 <feature name="org.gnu.gdb.arm.vfp">
95 <reg name="d0" bitsize="64" type="float"/>
96 <reg name="d1" bitsize="64" type="float"/>
97 <reg name="d2" bitsize="64" type="float"/>
98 <reg name="d3" bitsize="64" type="float"/>
99 <reg name="d4" bitsize="64" type="float"/>
100 <reg name="d5" bitsize="64" type="float"/>
101 <reg name="d6" bitsize="64" type="float"/>
102 <reg name="d7" bitsize="64" type="float"/>
103 <reg name="d8" bitsize="64" type="float"/>
104 <reg name="d9" bitsize="64" type="float"/>
105 <reg name="d10" bitsize="64" type="float"/>
106 <reg name="d11" bitsize="64" type="float"/>
107 <reg name="d12" bitsize="64" type="float"/>
108 <reg name="d13" bitsize="64" type="float"/>
109 <reg name="d14" bitsize="64" type="float"/>
110 <reg name="d15" bitsize="64" type="float"/>
111 <reg name="fpscr" bitsize="32" type="int" group="float"/>
112 </feature>
113</target>
114)";
115
63namespace GDBStub { 116namespace GDBStub {
64 117
65static int gdbserver_socket = -1; 118static int gdbserver_socket = -1;
@@ -211,7 +264,7 @@ static u8 ReadByte() {
211} 264}
212 265
213/// Calculate the checksum of the current command buffer. 266/// Calculate the checksum of the current command buffer.
214static u8 CalculateChecksum(u8 *buffer, u32 length) { 267static u8 CalculateChecksum(u8* buffer, u32 length) {
215 return static_cast<u8>(std::accumulate(buffer, buffer + length, 0, std::plus<u8>())); 268 return static_cast<u8>(std::accumulate(buffer, buffer + length, 0, std::plus<u8>()));
216} 269}
217 270
@@ -353,8 +406,15 @@ static void SendReply(const char* reply) {
353static void HandleQuery() { 406static void HandleQuery() {
354 LOG_DEBUG(Debug_GDBStub, "gdb: query '%s'\n", command_buffer + 1); 407 LOG_DEBUG(Debug_GDBStub, "gdb: query '%s'\n", command_buffer + 1);
355 408
356 if (!strcmp(reinterpret_cast<const char*>(command_buffer + 1), "TStatus")) { 409 const char* query = reinterpret_cast<const char*>(command_buffer + 1);
410
411 if (strcmp(query, "TStatus") == 0 ) {
357 SendReply("T0"); 412 SendReply("T0");
413 } else if (strncmp(query, "Supported:", strlen("Supported:")) == 0) {
414 // PacketSize needs to be large enough for target xml
415 SendReply("PacketSize=800;qXfer:features:read+");
416 } else if (strncmp(query, "Xfer:features:read:target.xml:", strlen("Xfer:features:read:target.xml:")) == 0) {
417 SendReply(target_xml);
358 } else { 418 } else {
359 SendReply(""); 419 SendReply("");
360 } 420 }
@@ -491,29 +551,25 @@ static void ReadRegisters() {
491 memset(buffer, 0, sizeof(buffer)); 551 memset(buffer, 0, sizeof(buffer));
492 552
493 u8* bufptr = buffer; 553 u8* bufptr = buffer;
494 for (int i = 0, reg = 0; reg <= FPSCR_REGISTER; i++, reg++) { 554
495 if (reg <= R15_REGISTER) { 555 for (int reg = 0; reg <= R15_REGISTER; reg++) {
496 IntToGdbHex(bufptr + i * CHAR_BIT, Core::g_app_core->GetReg(reg)); 556 IntToGdbHex(bufptr + reg * CHAR_BIT, Core::g_app_core->GetReg(reg));
497 } else if (reg == CPSR_REGISTER) {
498 IntToGdbHex(bufptr + i * CHAR_BIT, Core::g_app_core->GetCPSR());
499 } else if (reg == CPSR_REGISTER - 1) {
500 // Dummy FPA register, ignore
501 IntToGdbHex(bufptr + i * CHAR_BIT, 0);
502 } else if (reg < CPSR_REGISTER) {
503 // Dummy FPA registers, ignore
504 IntToGdbHex(bufptr + i * CHAR_BIT, 0);
505 IntToGdbHex(bufptr + (i + 1) * CHAR_BIT, 0);
506 IntToGdbHex(bufptr + (i + 2) * CHAR_BIT, 0);
507 i += 2;
508 } else if (reg > CPSR_REGISTER && reg < FPSCR_REGISTER) {
509 IntToGdbHex(bufptr + i * CHAR_BIT, Core::g_app_core->GetVFPReg(reg - CPSR_REGISTER - 1));
510 IntToGdbHex(bufptr + (i + 1) * CHAR_BIT, 0);
511 i++;
512 } else if (reg == FPSCR_REGISTER) {
513 IntToGdbHex(bufptr + i * CHAR_BIT, Core::g_app_core->GetVFPSystemReg(VFP_FPSCR));
514 }
515 } 557 }
516 558
559 bufptr += (16 * CHAR_BIT);
560
561 IntToGdbHex(bufptr, Core::g_app_core->GetCPSR());
562
563 bufptr += CHAR_BIT;
564
565 for (int reg = 0; reg <= 31; reg++) {
566 IntToGdbHex(bufptr + reg * CHAR_BIT, Core::g_app_core->GetVFPReg(reg));
567 }
568
569 bufptr += (32 * CHAR_BIT);
570
571 IntToGdbHex(bufptr, Core::g_app_core->GetVFPSystemReg(VFP_FPSCR));
572
517 SendReply(reinterpret_cast<char*>(buffer)); 573 SendReply(reinterpret_cast<char*>(buffer));
518} 574}
519 575
@@ -885,6 +941,12 @@ void Init(u16 port) {
885 LOG_ERROR(Debug_GDBStub, "Failed to create gdb socket"); 941 LOG_ERROR(Debug_GDBStub, "Failed to create gdb socket");
886 } 942 }
887 943
944 // Set socket to SO_REUSEADDR so it can always bind on the same port
945 int reuse_enabled = 1;
946 if (setsockopt(tmpsock, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse_enabled, sizeof(reuse_enabled)) < 0) {
947 LOG_ERROR(Debug_GDBStub, "Failed to set gdb socket option");
948 }
949
888 const sockaddr* server_addr = reinterpret_cast<const sockaddr*>(&saddr_server); 950 const sockaddr* server_addr = reinterpret_cast<const sockaddr*>(&saddr_server);
889 socklen_t server_addrlen = sizeof(saddr_server); 951 socklen_t server_addrlen = sizeof(saddr_server);
890 if (bind(tmpsock, server_addr, server_addrlen) < 0) { 952 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 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cstring> 5#include <cstring>
6
7#include "common/assert.h"
8#include "common/common_types.h"
9#include "common/common_funcs.h"
10
11#include "core/core.h"
12#include "core/memory.h"
13#include "core/hle/config_mem.h" 6#include "core/hle/config_mem.h"
14 7
15//////////////////////////////////////////////////////////////////////////////////////////////////// 8////////////////////////////////////////////////////////////////////////////////////////////////////
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 @@
8#include "core/arm/arm_interface.h" 8#include "core/arm/arm_interface.h"
9#include "core/core.h" 9#include "core/core.h"
10#include "core/hle/hle.h" 10#include "core/hle/hle.h"
11#include "core/hle/config_mem.h"
12#include "core/hle/shared_page.h"
13#include "core/hle/service/service.h" 11#include "core/hle/service/service.h"
14 12
15//////////////////////////////////////////////////////////////////////////////////////////////////// 13////////////////////////////////////////////////////////////////////////////////////////////////////
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 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <memory>
6
5#include "common/assert.h" 7#include "common/assert.h"
6#include "common/common_funcs.h" 8#include "common/common_funcs.h"
7#include "common/logging/log.h" 9#include "common/logging/log.h"
8#include "common/make_unique.h"
9 10
10#include "core/hle/kernel/memory.h" 11#include "core/hle/kernel/memory.h"
11#include "core/hle/kernel/process.h" 12#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 {
24 FS_InvalidOpenFlags = 230, 24 FS_InvalidOpenFlags = 230,
25 FS_NotAFile = 250, 25 FS_NotAFile = 250,
26 FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive 26 FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive
27 OutofRangeOrMisalignedAddress = 513, // TODO(purpasmart): Check if this name fits its actual usage
27 FS_InvalidPath = 702, 28 FS_InvalidPath = 702,
28 InvalidSection = 1000, 29 InvalidSection = 1000,
29 TooLarge = 1001, 30 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 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cinttypes>
6
5#include "common/logging/log.h" 7#include "common/logging/log.h"
6 8
7#include "core/hle/service/service.h" 9#include "core/hle/service/service.h"
@@ -9,30 +11,119 @@
9#include "core/hle/service/am/am_app.h" 11#include "core/hle/service/am/am_app.h"
10#include "core/hle/service/am/am_net.h" 12#include "core/hle/service/am/am_net.h"
11#include "core/hle/service/am/am_sys.h" 13#include "core/hle/service/am/am_sys.h"
14#include "core/hle/service/am/am_u.h"
12 15
13namespace Service { 16namespace Service {
14namespace AM { 17namespace AM {
15 18
16void TitleIDListGetTotal(Service::Interface* self) { 19static std::array<u32, 3> am_content_count = { 0, 0, 0 };
20static std::array<u32, 3> am_titles_count = { 0, 0, 0 };
21static std::array<u32, 3> am_titles_list_count = { 0, 0, 0 };
22static u32 am_ticket_count = 0;
23static u32 am_ticket_list_count = 0;
24
25void GetTitleCount(Service::Interface* self) {
17 u32* cmd_buff = Kernel::GetCommandBuffer(); 26 u32* cmd_buff = Kernel::GetCommandBuffer();
27
18 u32 media_type = cmd_buff[1] & 0xFF; 28 u32 media_type = cmd_buff[1] & 0xFF;
19 29
20 cmd_buff[1] = RESULT_SUCCESS.raw; 30 cmd_buff[1] = RESULT_SUCCESS.raw;
21 cmd_buff[2] = 0; 31 cmd_buff[2] = am_titles_count[media_type];
32 LOG_WARNING(Service_AM, "(STUBBED) media_type=%u, title_count=0x%08x", media_type, am_titles_count[media_type]);
33}
34
35void FindContentInfos(Service::Interface* self) {
36 u32* cmd_buff = Kernel::GetCommandBuffer();
37
38 u32 media_type = cmd_buff[1] & 0xFF;
39 u64 title_id = (static_cast<u64>(cmd_buff[3]) << 32) | cmd_buff[2];
40 u32 content_ids_pointer = cmd_buff[6];
41 u32 content_info_pointer = cmd_buff[8];
42
43 am_content_count[media_type] = cmd_buff[4];
22 44
23 LOG_WARNING(Service_AM, "(STUBBED) media_type %u", media_type); 45 cmd_buff[1] = RESULT_SUCCESS.raw;
46 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",
47 media_type, title_id, am_content_count[media_type], content_ids_pointer, content_info_pointer);
24} 48}
25 49
26void GetTitleIDList(Service::Interface* self) { 50void ListContentInfos(Service::Interface* self) {
27 u32* cmd_buff = Kernel::GetCommandBuffer(); 51 u32* cmd_buff = Kernel::GetCommandBuffer();
28 u32 num_titles = cmd_buff[1]; 52
29 u32 media_type = cmd_buff[2] & 0xFF; 53 u32 media_type = cmd_buff[2] & 0xFF;
30 u32 addr = cmd_buff[4]; 54 u64 title_id = (static_cast<u64>(cmd_buff[4]) << 32) | cmd_buff[3];
55 u32 start_index = cmd_buff[5];
56 u32 content_info_pointer = cmd_buff[7];
57
58 am_content_count[media_type] = cmd_buff[1];
31 59
32 cmd_buff[1] = RESULT_SUCCESS.raw; 60 cmd_buff[1] = RESULT_SUCCESS.raw;
33 cmd_buff[2] = 0; 61 cmd_buff[2] = am_content_count[media_type];
62 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",
63 media_type, am_content_count[media_type], title_id, start_index, content_info_pointer);
64}
65
66void DeleteContents(Service::Interface* self) {
67 u32* cmd_buff = Kernel::GetCommandBuffer();
68
69 u32 media_type = cmd_buff[1] & 0xFF;
70 u64 title_id = (static_cast<u64>(cmd_buff[3]) << 32) | cmd_buff[2];
71 u32 content_ids_pointer = cmd_buff[6];
34 72
35 LOG_WARNING(Service_AM, "(STUBBED) Requested %u titles from media type %u. Address=0x%08X", num_titles, media_type, addr); 73 am_content_count[media_type] = cmd_buff[4];
74
75 cmd_buff[1] = RESULT_SUCCESS.raw;
76 LOG_WARNING(Service_AM, "(STUBBED) media_type=%u, title_id=0x%016" PRIx64 ", content_count=%u, content_ids_pointer=0x%08x",
77 media_type, title_id, am_content_count[media_type], content_ids_pointer);
78}
79
80void GetTitleList(Service::Interface* self) {
81 u32* cmd_buff = Kernel::GetCommandBuffer();
82
83 u32 media_type = cmd_buff[2] & 0xFF;
84 u32 title_ids_output_pointer = cmd_buff[4];
85
86 am_titles_list_count[media_type] = cmd_buff[1];
87
88 cmd_buff[1] = RESULT_SUCCESS.raw;
89 cmd_buff[2] = am_titles_list_count[media_type];
90 LOG_WARNING(Service_AM, "(STUBBED) media_type=%u, titles_list_count=0x%08X, title_ids_output_pointer=0x%08X",
91 media_type, am_titles_list_count[media_type], title_ids_output_pointer);
92}
93
94void GetTitleInfo(Service::Interface* self) {
95 u32* cmd_buff = Kernel::GetCommandBuffer();
96
97 u32 media_type = cmd_buff[1] & 0xFF;
98 u32 title_id_list_pointer = cmd_buff[4];
99 u32 title_list_pointer = cmd_buff[6];
100
101 am_titles_count[media_type] = cmd_buff[2];
102
103 cmd_buff[1] = RESULT_SUCCESS.raw;
104 LOG_WARNING(Service_AM, "(STUBBED) media_type=%u, total_titles=0x%08X, title_id_list_pointer=0x%08X, title_list_pointer=0x%08X",
105 media_type, am_titles_count[media_type], title_id_list_pointer, title_list_pointer);
106}
107
108void GetDataTitleInfos(Service::Interface* self) {
109 GetTitleInfo(self);
110
111 LOG_WARNING(Service_AM, "(STUBBED) called");
112}
113
114void ListDataTitleTicketInfos(Service::Interface* self) {
115 u32* cmd_buff = Kernel::GetCommandBuffer();
116
117 u64 title_id = (static_cast<u64>(cmd_buff[3]) << 32) | cmd_buff[2];
118 u32 start_index = cmd_buff[4];
119 u32 ticket_info_pointer = cmd_buff[6];
120
121 am_ticket_count = cmd_buff[1];
122
123 cmd_buff[1] = RESULT_SUCCESS.raw;
124 cmd_buff[2] = am_ticket_count;
125 LOG_WARNING(Service_AM, "(STUBBED) ticket_count=0x%08X, title_id=0x%016" PRIx64 ", start_index=0x%08X, ticket_info_pointer=0x%08X",
126 am_ticket_count, title_id, start_index, ticket_info_pointer);
36} 127}
37 128
38void GetNumContentInfos(Service::Interface* self) { 129void GetNumContentInfos(Service::Interface* self) {
@@ -40,16 +131,47 @@ void GetNumContentInfos(Service::Interface* self) {
40 131
41 cmd_buff[1] = RESULT_SUCCESS.raw; 132 cmd_buff[1] = RESULT_SUCCESS.raw;
42 cmd_buff[2] = 1; // Number of content infos plus one 133 cmd_buff[2] = 1; // Number of content infos plus one
43
44 LOG_WARNING(Service_AM, "(STUBBED) called"); 134 LOG_WARNING(Service_AM, "(STUBBED) called");
45} 135}
46 136
137void DeleteTicket(Service::Interface* self) {
138 u32* cmd_buff = Kernel::GetCommandBuffer();
139
140 u64 title_id = (static_cast<u64>(cmd_buff[2]) << 32) | cmd_buff[1];
141
142 cmd_buff[1] = RESULT_SUCCESS.raw;
143 LOG_WARNING(Service_AM, "(STUBBED) called title_id=0x%016" PRIx64 "",title_id);
144}
145
146void GetTicketCount(Service::Interface* self) {
147 u32* cmd_buff = Kernel::GetCommandBuffer();
148
149 cmd_buff[1] = RESULT_SUCCESS.raw;
150 cmd_buff[2] = am_ticket_count;
151 LOG_WARNING(Service_AM, "(STUBBED) called ticket_count=0x%08x",am_ticket_count);
152}
153
154void GetTicketList(Service::Interface* self) {
155 u32* cmd_buff = Kernel::GetCommandBuffer();
156
157 u32 num_of_skip = cmd_buff[2];
158 u32 ticket_list_pointer = cmd_buff[4];
159
160 am_ticket_list_count = cmd_buff[1];
161
162 cmd_buff[1] = RESULT_SUCCESS.raw;
163 cmd_buff[2] = am_ticket_list_count;
164 LOG_WARNING(Service_AM, "(STUBBED) ticket_list_count=0x%08x, num_of_skip=0x%08x, ticket_list_pointer=0x%08x",
165 am_ticket_list_count, num_of_skip, ticket_list_pointer);
166}
167
47void Init() { 168void Init() {
48 using namespace Kernel; 169 using namespace Kernel;
49 170
50 AddService(new AM_APP_Interface); 171 AddService(new AM_APP_Interface);
51 AddService(new AM_NET_Interface); 172 AddService(new AM_NET_Interface);
52 AddService(new AM_SYS_Interface); 173 AddService(new AM_SYS_Interface);
174 AddService(new AM_U_Interface);
53} 175}
54 176
55void Shutdown() { 177void 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;
11namespace AM { 11namespace AM {
12 12
13/** 13/**
14 * AM::TitleIDListGetTotal service function 14 * AM::GetTitleCount service function
15 * Gets the number of installed titles in the requested media type 15 * Gets the number of installed titles in the requested media type
16 * Inputs: 16 * Inputs:
17 * 0 : Command header (0x00010040) 17 * 0 : Command header (0x00010040)
@@ -20,36 +20,140 @@ namespace AM {
20 * 1 : Result, 0 on success, otherwise error code 20 * 1 : Result, 0 on success, otherwise error code
21 * 2 : The number of titles in the requested media type 21 * 2 : The number of titles in the requested media type
22 */ 22 */
23void TitleIDListGetTotal(Service::Interface* self); 23void GetTitleCount(Service::Interface* self);
24 24
25/** 25/**
26 * AM::GetTitleIDList service function 26 * AM::FindContentInfos service function
27 * Inputs:
28 * 1 : MediaType
29 * 2-3 : u64, Title ID
30 * 4 : Content count
31 * 6 : Content IDs pointer
32 * 8 : Content Infos pointer
33 * Outputs:
34 * 1 : Result, 0 on success, otherwise error code
35 */
36void FindContentInfos(Service::Interface* self);
37
38/**
39 * AM::ListContentInfos service function
40 * Inputs:
41 * 1 : Content count
42 * 2 : MediaType
43 * 3-4 : u64, Title ID
44 * 5 : Start Index
45 * 7 : Content Infos pointer
46 * Outputs:
47 * 1 : Result, 0 on success, otherwise error code
48 * 2 : Number of content infos returned
49 */
50void ListContentInfos(Service::Interface* self);
51
52/**
53 * AM::DeleteContents service function
54 * Inputs:
55 * 1 : MediaType
56 * 2-3 : u64, Title ID
57 * 4 : Content count
58 * 6 : Content IDs pointer
59 * Outputs:
60 * 1 : Result, 0 on success, otherwise error code
61 */
62void DeleteContents(Service::Interface* self);
63
64/**
65 * AM::GetTitleList service function
27 * Loads information about the desired number of titles from the desired media type into an array 66 * Loads information about the desired number of titles from the desired media type into an array
28 * Inputs: 67 * Inputs:
29 * 0 : Command header (0x00020082) 68 * 1 : Title count
30 * 1 : The maximum number of titles to load
31 * 2 : Media type to load the titles from 69 * 2 : Media type to load the titles from
32 * 3 : Descriptor of the output buffer pointer 70 * 4 : Title IDs output pointer
33 * 4 : Address of the output buffer
34 * Outputs: 71 * Outputs:
35 * 1 : Result, 0 on success, otherwise error code 72 * 1 : Result, 0 on success, otherwise error code
36 * 2 : The number of titles loaded from the requested media type 73 * 2 : The number of titles loaded from the requested media type
37 */ 74 */
38void GetTitleIDList(Service::Interface* self); 75void GetTitleList(Service::Interface* self);
76
77/**
78 * AM::GetTitleInfo service function
79 * Inputs:
80 * 1 : u8 Mediatype
81 * 2 : Total titles
82 * 4 : TitleIDList pointer
83 * 6 : TitleList pointer
84 * Outputs:
85 * 1 : Result, 0 on success, otherwise error code
86 */
87void GetTitleInfo(Service::Interface* self);
88
89/**
90 * AM::GetDataTitleInfos service function
91 * Wrapper for AM::GetTitleInfo
92 * Inputs:
93 * 1 : u8 Mediatype
94 * 2 : Total titles
95 * 4 : TitleIDList pointer
96 * 6 : TitleList pointer
97 * Outputs:
98 * 1 : Result, 0 on success, otherwise error code
99 */
100void GetDataTitleInfos(Service::Interface* self);
101
102/**
103 * AM::ListDataTitleTicketInfos service function
104 * Inputs:
105 * 1 : Ticket count
106 * 2-3 : u64, Title ID
107 * 4 : Start Index?
108 * 5 : (TicketCount * 24) << 8 | 0x4
109 * 6 : Ticket Infos pointer
110 * Outputs:
111 * 1 : Result, 0 on success, otherwise error code
112 * 2 : Number of ticket infos returned
113 */
114void ListDataTitleTicketInfos(Service::Interface* self);
39 115
40/** 116/**
41 * AM::GetNumContentInfos service function 117 * AM::GetNumContentInfos service function
42 * Inputs: 118 * Inputs:
43 * 0 : Command header (0x100100C0) 119 * 0 : Command header (0x100100C0)
44 * 1 : Unknown 120 * 1 : MediaType
45 * 2 : Unknown 121 * 2-3 : u64, Title ID
46 * 3 : Unknown
47 * Outputs: 122 * Outputs:
48 * 1 : Result, 0 on success, otherwise error code 123 * 1 : Result, 0 on success, otherwise error code
49 * 2 : Number of content infos plus one 124 * 2 : Number of content infos plus one
50 */ 125 */
51void GetNumContentInfos(Service::Interface* self); 126void GetNumContentInfos(Service::Interface* self);
52 127
128/**
129 * AM::DeleteTicket service function
130 * Inputs:
131 * 1-2 : u64, Title ID
132 * Outputs:
133 * 1 : Result, 0 on success, otherwise error code
134 */
135void DeleteTicket(Service::Interface* self);
136
137/**
138 * AM::GetTicketCount service function
139 * Outputs:
140 * 1 : Result, 0 on success, otherwise error code
141 * 2 : Total titles
142 */
143void GetTicketCount(Service::Interface* self);
144
145/**
146 * AM::GetTicketList service function
147 * Inputs:
148 * 1 : Number of TicketList
149 * 2 : Number to skip
150 * 4 : TicketList pointer
151 * Outputs:
152 * 1 : Result, 0 on success, otherwise error code
153 * 2 : Total TicketList
154 */
155void GetTicketList(Service::Interface* self);
156
53/// Initialize AM service 157/// Initialize AM service
54void Init(); 158void Init();
55 159
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 {
9namespace AM { 9namespace AM {
10 10
11const Interface::FunctionInfo FunctionTable[] = { 11const Interface::FunctionInfo FunctionTable[] = {
12 {0x100100C0, GetNumContentInfos, "GetNumContentInfos"}, 12 {0x100100C0, GetNumContentInfos, "GetNumContentInfos"},
13 {0x10020104, nullptr, "FindContentInfos"}, 13 {0x10020104, FindContentInfos, "FindContentInfos"},
14 {0x10030142, nullptr, "ListContentInfos"}, 14 {0x10030142, ListContentInfos, "ListContentInfos"},
15 {0x10040102, nullptr, "DeleteContents"}, 15 {0x10040102, DeleteContents, "DeleteContents"},
16 {0x10050084, nullptr, "GetDataTitleInfos"}, 16 {0x10050084, GetDataTitleInfos, "GetDataTitleInfos"},
17 {0x10070102, nullptr, "ListDataTitleTicketInfos"}, 17 {0x10070102, ListDataTitleTicketInfos, "ListDataTitleTicketInfos"},
18 {0x100900C0, nullptr, "IsDataTitleInUse"}, 18 {0x100900C0, nullptr, "IsDataTitleInUse"},
19 {0x100A0000, nullptr, "IsExternalTitleDatabaseInitialized"}, 19 {0x100A0000, nullptr, "IsExternalTitleDatabaseInitialized"},
20}; 20};
21 21
22AM_APP_Interface::AM_APP_Interface() { 22AM_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 {
9namespace AM { 9namespace AM {
10 10
11const Interface::FunctionInfo FunctionTable[] = { 11const Interface::FunctionInfo FunctionTable[] = {
12 {0x00010040, TitleIDListGetTotal, "TitleIDListGetTotal"}, 12 {0x00010040, GetTitleCount, "GetTitleCount"},
13 {0x00020082, GetTitleIDList, "GetTitleIDList"}, 13 {0x00020082, GetTitleList, "GetTitleList"},
14 {0x00030084, nullptr, "ListTitles"}, 14 {0x00030084, GetTitleInfo, "GetTitleInfo"},
15 {0x000400C0, nullptr, "DeleteApplicationTitle"}, 15 {0x000400C0, nullptr, "DeleteApplicationTitle"},
16 {0x000500C0, nullptr, "GetTitleProductCode"}, 16 {0x000500C0, nullptr, "GetTitleProductCode"},
17 {0x00080000, nullptr, "TitleIDListGetTotal3"}, 17 {0x000600C0, nullptr, "GetTitleExtDataId"},
18 {0x00090082, nullptr, "GetTitleIDList3"}, 18 {0x00070080, DeleteTicket, "DeleteTicket"},
19 {0x00080000, GetTicketCount, "GetTicketCount"},
20 {0x00090082, GetTicketList, "GetTicketList"},
19 {0x000A0000, nullptr, "GetDeviceID"}, 21 {0x000A0000, nullptr, "GetDeviceID"},
20 {0x000D0084, nullptr, "ListTitles2"}, 22 {0x000D0084, nullptr, "GetPendingTitleInfo"},
21 {0x00140040, nullptr, "FinishInstallToMedia"}, 23 {0x000E00C0, nullptr, "DeletePendingTitle"},
24 {0x00140040, nullptr, "FinalizePendingTitles"},
25 {0x00150040, nullptr, "DeleteAllPendingTitles"},
22 {0x00180080, nullptr, "InitializeTitleDatabase"}, 26 {0x00180080, nullptr, "InitializeTitleDatabase"},
23 {0x00190040, nullptr, "ReloadDBS"}, 27 {0x00190040, nullptr, "ReloadDBS"},
24 {0x001A00C0, nullptr, "GetDSiWareExportSize"}, 28 {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 {
9namespace AM { 9namespace AM {
10 10
11const Interface::FunctionInfo FunctionTable[] = { 11const Interface::FunctionInfo FunctionTable[] = {
12 {0x00010040, TitleIDListGetTotal, "TitleIDListGetTotal"}, 12 {0x00010040, GetTitleCount, "GetTitleCount"},
13 {0x00020082, GetTitleIDList, "GetTitleIDList"}, 13 {0x00020082, GetTitleList, "GetTitleList"},
14 {0x00030084, nullptr, "ListTitles"}, 14 {0x00030084, GetTitleInfo, "GetTitleInfo"},
15 {0x000400C0, nullptr, "DeleteApplicationTitle"}, 15 {0x000400C0, nullptr, "DeleteApplicationTitle"},
16 {0x000500C0, nullptr, "GetTitleProductCode"}, 16 {0x000500C0, nullptr, "GetTitleProductCode"},
17 {0x00080000, nullptr, "TitleIDListGetTotal3"}, 17 {0x000600C0, nullptr, "GetTitleExtDataId"},
18 {0x00090082, nullptr, "GetTitleIDList3"}, 18 {0x00070080, DeleteTicket, "DeleteTicket"},
19 {0x00080000, GetTicketCount, "GetTicketCount"},
20 {0x00090082, GetTicketList, "GetTicketList"},
19 {0x000A0000, nullptr, "GetDeviceID"}, 21 {0x000A0000, nullptr, "GetDeviceID"},
20 {0x000D0084, nullptr, "ListTitles2"}, 22 {0x000D0084, nullptr, "GetPendingTitleInfo"},
21 {0x00140040, nullptr, "FinishInstallToMedia"}, 23 {0x000E00C0, nullptr, "DeletePendingTitle"},
24 {0x00140040, nullptr, "FinalizePendingTitles"},
25 {0x00150040, nullptr, "DeleteAllPendingTitles"},
22 {0x00180080, nullptr, "InitializeTitleDatabase"}, 26 {0x00180080, nullptr, "InitializeTitleDatabase"},
23 {0x00190040, nullptr, "ReloadDBS"}, 27 {0x00190040, nullptr, "ReloadDBS"},
24 {0x001A00C0, nullptr, "GetDSiWareExportSize"}, 28 {0x001A00C0, nullptr, "GetDSiWareExportSize"},
25 {0x001B0144, nullptr, "ExportDSiWare"}, 29 {0x001B0144, nullptr, "ExportDSiWare"},
26 {0x001C0084, nullptr, "ImportDSiWare"}, 30 {0x001C0084, nullptr, "ImportDSiWare"},
27 {0x00230080, nullptr, "TitleIDListGetTotal2"}, 31 {0x00230080, nullptr, "GetPendingTitleCount"},
28 {0x002400C2, nullptr, "GetTitleIDList2"} 32 {0x002400C2, nullptr, "GetPendingTitleList"}
29}; 33};
30 34
31AM_SYS_Interface::AM_SYS_Interface() { 35AM_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 {
9namespace AM { 9namespace AM {
10 10
11const Interface::FunctionInfo FunctionTable[] = { 11const Interface::FunctionInfo FunctionTable[] = {
12 {0x00010040, TitleIDListGetTotal, "TitleIDListGetTotal"}, 12 {0x00010040, GetTitleCount, "GetTitleCount"},
13 {0x00020082, GetTitleIDList, "GetTitleIDList"}, 13 {0x00020082, GetTitleList, "GetTitleList"},
14 {0x00030084, nullptr, "ListTitles"}, 14 {0x00030084, GetTitleInfo, "GetTitleInfo"},
15 {0x000400C0, nullptr, "DeleteApplicationTitle"}, 15 {0x000400C0, nullptr, "DeleteApplicationTitle"},
16 {0x000500C0, nullptr, "GetTitleProductCode"}, 16 {0x000500C0, nullptr, "GetTitleProductCode"},
17 {0x00080000, nullptr, "TitleIDListGetTotal3"}, 17 {0x000600C0, nullptr, "GetTitleExtDataId"},
18 {0x00090082, nullptr, "GetTitleIDList3"}, 18 {0x00070080, DeleteTicket, "DeleteTicket"},
19 {0x00080000, GetTicketCount, "GetTicketCount"},
20 {0x00090082, GetTicketList, "GetTicketList"},
19 {0x000A0000, nullptr, "GetDeviceID"}, 21 {0x000A0000, nullptr, "GetDeviceID"},
20 {0x000D0084, nullptr, "ListTitles2"}, 22 {0x000D0084, nullptr, "GetPendingTitleInfo"},
21 {0x00140040, nullptr, "FinishInstallToMedia"}, 23 {0x000E00C0, nullptr, "DeletePendingTitle"},
24 {0x00140040, nullptr, "FinalizePendingTitles"},
25 {0x00150040, nullptr, "DeleteAllPendingTitles"},
22 {0x00180080, nullptr, "InitializeTitleDatabase"}, 26 {0x00180080, nullptr, "InitializeTitleDatabase"},
23 {0x00190040, nullptr, "ReloadDBS"}, 27 {0x00190040, nullptr, "ReloadDBS"},
24 {0x001A00C0, nullptr, "GetDSiWareExportSize"}, 28 {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) {
397 LOG_WARNING(Service_APT, "(stubbed) called appid=%u", app_id); 397 LOG_WARNING(Service_APT, "(stubbed) called appid=%u", app_id);
398} 398}
399 399
400void GetStartupArgument(Service::Interface* self) {
401 u32* cmd_buff = Kernel::GetCommandBuffer();
402 u32 parameter_size = cmd_buff[1];
403 StartupArgumentType startup_argument_type = static_cast<StartupArgumentType>(cmd_buff[2]);
404
405 if (parameter_size >= 0x300) {
406 LOG_ERROR(Service_APT, "Parameter size is outside the valid range (capped to 0x300): parameter_size=0x%08x", parameter_size);
407 return;
408 }
409
410 LOG_WARNING(Service_APT,"(stubbed) called startup_argument_type=%u , parameter_size=0x%08x , parameter_value=0x%08x",
411 startup_argument_type, parameter_size, Memory::Read32(cmd_buff[41]));
412
413 cmd_buff[1] = RESULT_SUCCESS.raw;
414 cmd_buff[2] = (parameter_size > 0) ? 1 : 0;
415}
416
400void Init() { 417void Init() {
401 AddService(new APT_A_Interface); 418 AddService(new APT_A_Interface);
402 AddService(new APT_S_Interface); 419 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 {
67 Ed2 = 0x402, 67 Ed2 = 0x402,
68}; 68};
69 69
70enum class StartupArgumentType : u32 {
71 OtherApp = 0,
72 Restart = 1,
73 OtherMedia = 2,
74};
75
70/// Send a parameter to the currently-running application, which will read it via ReceiveParameter 76/// Send a parameter to the currently-running application, which will read it via ReceiveParameter
71void SendParameter(const MessageParameter& parameter); 77void SendParameter(const MessageParameter& parameter);
72 78
@@ -344,6 +350,17 @@ void PreloadLibraryApplet(Service::Interface* self);
344 */ 350 */
345void StartLibraryApplet(Service::Interface* self); 351void StartLibraryApplet(Service::Interface* self);
346 352
353/**
354 * APT::GetStartupArgument service function
355 * Inputs:
356 * 1 : Parameter Size (capped to 0x300)
357 * 2 : StartupArgumentType
358 * Outputs:
359 * 0 : Return header
360 * 1 : u8, Exists (0 = does not exist, 1 = exists)
361 */
362void GetStartupArgument(Service::Interface* self);
363
347/// Initialize the APT service 364/// Initialize the APT service
348void Init(); 365void Init();
349 366
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[] = {
13 {0x00020080, Initialize, "Initialize?"}, 13 {0x00020080, Initialize, "Initialize?"},
14 {0x00030040, Enable, "Enable?"}, 14 {0x00030040, Enable, "Enable?"},
15 {0x00040040, nullptr, "Finalize?"}, 15 {0x00040040, nullptr, "Finalize?"},
16 {0x00050040, nullptr, "GetAppletManInfo?"}, 16 {0x00050040, GetAppletManInfo, "GetAppletManInfo"},
17 {0x00060040, nullptr, "GetAppletInfo?"}, 17 {0x00060040, GetAppletInfo, "GetAppletInfo"},
18 {0x00090040, IsRegistered, "IsRegistered"}, 18 {0x00090040, IsRegistered, "IsRegistered"},
19 {0x000B0040, InquireNotification, "InquireNotification"},
19 {0x000C0104, SendParameter, "SendParameter"}, 20 {0x000C0104, SendParameter, "SendParameter"},
20 {0x000D0080, ReceiveParameter, "ReceiveParameter"}, 21 {0x000D0080, ReceiveParameter, "ReceiveParameter"},
21 {0x000E0080, GlanceParameter, "GlanceParameter"}, 22 {0x000E0080, GlanceParameter, "GlanceParameter"},
@@ -24,9 +25,13 @@ const Interface::FunctionInfo FunctionTable[] = {
24 {0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"}, 25 {0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"},
25 {0x001E0084, StartLibraryApplet, "StartLibraryApplet"}, 26 {0x001E0084, StartLibraryApplet, "StartLibraryApplet"},
26 {0x003B0040, nullptr, "CancelLibraryApplet?"}, 27 {0x003B0040, nullptr, "CancelLibraryApplet?"},
28 {0x003E0080, nullptr, "ReplySleepQuery"},
27 {0x00430040, NotifyToWait, "NotifyToWait?"}, 29 {0x00430040, NotifyToWait, "NotifyToWait?"},
28 {0x00440000, GetSharedFont, "GetSharedFont?"}, 30 {0x00440000, GetSharedFont, "GetSharedFont?"},
29 {0x004B00C2, AppletUtility, "AppletUtility?"}, 31 {0x004B00C2, AppletUtility, "AppletUtility?"},
32 {0x004F0080, SetAppCpuTimeLimit, "SetAppCpuTimeLimit"},
33 {0x00500040, GetAppCpuTimeLimit, "GetAppCpuTimeLimit"},
34 {0x00510080, GetStartupArgument, "GetStartupArgument"},
30 {0x00550040, nullptr, "WriteInputToNsState?"}, 35 {0x00550040, nullptr, "WriteInputToNsState?"},
31}; 36};
32 37
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[] = {
13 {0x00020080, Initialize, "Initialize"}, 13 {0x00020080, Initialize, "Initialize"},
14 {0x00030040, Enable, "Enable"}, 14 {0x00030040, Enable, "Enable"},
15 {0x00040040, nullptr, "Finalize"}, 15 {0x00040040, nullptr, "Finalize"},
16 {0x00050040, nullptr, "GetAppletManInfo"}, 16 {0x00050040, GetAppletManInfo, "GetAppletManInfo"},
17 {0x00060040, nullptr, "GetAppletInfo"}, 17 {0x00060040, GetAppletInfo, "GetAppletInfo"},
18 {0x00070000, nullptr, "GetLastSignaledAppletId"}, 18 {0x00070000, nullptr, "GetLastSignaledAppletId"},
19 {0x00080000, nullptr, "CountRegisteredApplet"}, 19 {0x00080000, nullptr, "CountRegisteredApplet"},
20 {0x00090040, nullptr, "IsRegistered"}, 20 {0x00090040, nullptr, "IsRegistered"},
@@ -87,9 +87,9 @@ const Interface::FunctionInfo FunctionTable[] = {
87 {0x004C0000, nullptr, "SetFatalErrDispMode"}, 87 {0x004C0000, nullptr, "SetFatalErrDispMode"},
88 {0x004D0080, nullptr, "GetAppletProgramInfo"}, 88 {0x004D0080, nullptr, "GetAppletProgramInfo"},
89 {0x004E0000, nullptr, "HardwareResetAsync"}, 89 {0x004E0000, nullptr, "HardwareResetAsync"},
90 {0x004F0080, nullptr, "SetApplicationCpuTimeLimit"}, 90 {0x004F0080, SetAppCpuTimeLimit, "SetAppCpuTimeLimit"},
91 {0x00500040, nullptr, "GetApplicationCpuTimeLimit"}, 91 {0x00500040, GetAppCpuTimeLimit, "GetAppCpuTimeLimit"},
92 {0x00510080, nullptr, "GetStartupArgument"}, 92 {0x00510080, GetStartupArgument, "GetStartupArgument"},
93 {0x00520104, nullptr, "Wrap1"}, 93 {0x00520104, nullptr, "Wrap1"},
94 {0x00530104, nullptr, "Unwrap1"}, 94 {0x00530104, nullptr, "Unwrap1"},
95 {0x00580002, nullptr, "GetProgramID"}, 95 {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[] = {
89 {0x004E0000, nullptr, "HardwareResetAsync"}, 89 {0x004E0000, nullptr, "HardwareResetAsync"},
90 {0x004F0080, SetAppCpuTimeLimit, "SetAppCpuTimeLimit"}, 90 {0x004F0080, SetAppCpuTimeLimit, "SetAppCpuTimeLimit"},
91 {0x00500040, GetAppCpuTimeLimit, "GetAppCpuTimeLimit"}, 91 {0x00500040, GetAppCpuTimeLimit, "GetAppCpuTimeLimit"},
92 {0x00510080, nullptr, "GetStartupArgument"}, 92 {0x00510080, GetStartupArgument, "GetStartupArgument"},
93 {0x00520104, nullptr, "Wrap1"}, 93 {0x00520104, nullptr, "Wrap1"},
94 {0x00530104, nullptr, "Unwrap1"}, 94 {0x00530104, nullptr, "Unwrap1"},
95 {0x00580002, nullptr, "GetProgramID"}, 95 {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 @@
4 4
5#include "common/logging/log.h" 5#include "common/logging/log.h"
6 6
7#include "core/hle/kernel/event.h"
7#include "core/hle/service/service.h" 8#include "core/hle/service/service.h"
8#include "core/hle/service/cecd/cecd.h" 9#include "core/hle/service/cecd/cecd.h"
9#include "core/hle/service/cecd/cecd_s.h" 10#include "core/hle/service/cecd/cecd_s.h"
@@ -12,14 +13,47 @@
12namespace Service { 13namespace Service {
13namespace CECD { 14namespace CECD {
14 15
15void Init() { 16static Kernel::SharedPtr<Kernel::Event> cecinfo_event;
16 using namespace Kernel; 17static Kernel::SharedPtr<Kernel::Event> change_state_event;
18
19void GetCecStateAbbreviated(Service::Interface* self) {
20 u32* cmd_buff = Kernel::GetCommandBuffer();
21
22 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
23 cmd_buff[2] = static_cast<u32>(CecStateAbbreviated::CEC_STATE_ABBREV_IDLE);
24
25 LOG_WARNING(Service_CECD, "(STUBBED) called");
26}
27
28void GetCecInfoEventHandle(Service::Interface* self) {
29 u32* cmd_buff = Kernel::GetCommandBuffer();
17 30
31 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
32 cmd_buff[3] = Kernel::g_handle_table.Create(cecinfo_event).MoveFrom(); // Event handle
33
34 LOG_WARNING(Service_CECD, "(STUBBED) called");
35}
36
37void GetChangeStateEventHandle(Service::Interface* self) {
38 u32* cmd_buff = Kernel::GetCommandBuffer();
39
40 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
41 cmd_buff[3] = Kernel::g_handle_table.Create(change_state_event).MoveFrom(); // Event handle
42
43 LOG_WARNING(Service_CECD, "(STUBBED) called");
44}
45
46void Init() {
18 AddService(new CECD_S_Interface); 47 AddService(new CECD_S_Interface);
19 AddService(new CECD_U_Interface); 48 AddService(new CECD_U_Interface);
49
50 cecinfo_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "CECD_U::cecinfo_event");
51 change_state_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "CECD_U::change_state_event");
20} 52}
21 53
22void Shutdown() { 54void Shutdown() {
55 cecinfo_event = nullptr;
56 change_state_event = nullptr;
23} 57}
24 58
25} // namespace CECD 59} // 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 @@
5#pragma once 5#pragma once
6 6
7namespace Service { 7namespace Service {
8
9class Interface;
10
8namespace CECD { 11namespace CECD {
9 12
13enum class CecStateAbbreviated {
14 CEC_STATE_ABBREV_IDLE = 1, ///< Corresponds to CEC_STATE_IDLE
15 CEC_STATE_ABBREV_NOT_LOCAL = 2, ///< Corresponds to CEC_STATEs *FINISH*, *POST, and OVER_BOSS
16 CEC_STATE_ABBREV_SCANNING = 3, ///< Corresponds to CEC_STATE_SCANNING
17 CEC_STATE_ABBREV_WLREADY = 4, ///< Corresponds to CEC_STATE_WIRELESS_READY when some unknown bool is true
18 CEC_STATE_ABBREV_OTHER = 5, ///< Corresponds to CEC_STATEs besides *FINISH*, *POST, and OVER_BOSS and those listed here
19};
20
21/**
22 * GetCecStateAbbreviated service function
23 * Inputs:
24 * 0: 0x000E0000
25 * Outputs:
26 * 1: ResultCode
27 * 2: CecStateAbbreviated
28 */
29void GetCecStateAbbreviated(Service::Interface* self);
30
31/**
32 * GetCecInfoEventHandle service function
33 * Inputs:
34 * 0: 0x000F0000
35 * Outputs:
36 * 1: ResultCode
37 * 3: Event Handle
38 */
39void GetCecInfoEventHandle(Service::Interface* self);
40
41/**
42 * GetChangeStateEventHandle service function
43 * Inputs:
44 * 0: 0x00100000
45 * Outputs:
46 * 1: ResultCode
47 * 3: Event Handle
48 */
49void GetChangeStateEventHandle(Service::Interface* self);
50
10/// Initialize CECD service(s) 51/// Initialize CECD service(s)
11void Init(); 52void Init();
12 53
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 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "core/hle/service/cecd/cecd.h"
5#include "core/hle/service/cecd/cecd_u.h" 6#include "core/hle/service/cecd/cecd_u.h"
6 7
7namespace Service { 8namespace Service {
8namespace CECD { 9namespace CECD {
9 10
10static const Interface::FunctionInfo FunctionTable[] = { 11static const Interface::FunctionInfo FunctionTable[] = {
11 { 0x00120104, nullptr, "ReadSavedData" }, 12 {0x000E0000, GetCecStateAbbreviated, "GetCecStateAbbreviated"},
13 {0x000F0000, GetCecInfoEventHandle, "GetCecInfoEventHandle"},
14 {0x00100000, GetChangeStateEventHandle, "GetChangeStateEventHandle"},
15 {0x00120104, nullptr, "ReadSavedData"},
12}; 16};
13 17
14CECD_U_Interface::CECD_U_Interface() { 18CECD_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 {
9namespace CFG { 9namespace CFG {
10 10
11const Interface::FunctionInfo FunctionTable[] = { 11const Interface::FunctionInfo FunctionTable[] = {
12 // cfg common
13 {0x00010082, GetConfigInfoBlk2, "GetConfigInfoBlk2"},
14 {0x00020000, SecureInfoGetRegion, "SecureInfoGetRegion"},
15 {0x00030040, GenHashConsoleUnique, "GenHashConsoleUnique"},
16 {0x00040000, GetRegionCanadaUSA, "GetRegionCanadaUSA"},
17 {0x00050000, GetSystemModel, "GetSystemModel"},
18 {0x00060000, GetModelNintendo2DS, "GetModelNintendo2DS"},
19 {0x00070040, nullptr, "WriteToFirstByteCfgSavegame"},
20 {0x00080080, nullptr, "GoThroughTable"},
21 {0x00090040, GetCountryCodeString, "GetCountryCodeString"},
22 {0x000A0040, GetCountryCodeID, "GetCountryCodeID"},
23 // cfg:i
12 {0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"}, 24 {0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"},
13 {0x04020082, nullptr, "SetConfigInfoBlk4"}, 25 {0x04020082, nullptr, "SetConfigInfoBlk4"},
14 {0x04030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"}, 26 {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 {
9namespace CFG { 9namespace CFG {
10 10
11const Interface::FunctionInfo FunctionTable[] = { 11const Interface::FunctionInfo FunctionTable[] = {
12 // cfg common
12 {0x00010082, GetConfigInfoBlk2, "GetConfigInfoBlk2"}, 13 {0x00010082, GetConfigInfoBlk2, "GetConfigInfoBlk2"},
13 {0x00020000, SecureInfoGetRegion, "SecureInfoGetRegion"}, 14 {0x00020000, SecureInfoGetRegion, "SecureInfoGetRegion"},
14 {0x00030040, GenHashConsoleUnique, "GenHashConsoleUnique"}, 15 {0x00030040, GenHashConsoleUnique, "GenHashConsoleUnique"},
16 {0x00040000, GetRegionCanadaUSA, "GetRegionCanadaUSA"},
15 {0x00050000, GetSystemModel, "GetSystemModel"}, 17 {0x00050000, GetSystemModel, "GetSystemModel"},
18 {0x00060000, GetModelNintendo2DS, "GetModelNintendo2DS"},
19 {0x00070040, nullptr, "WriteToFirstByteCfgSavegame"},
20 {0x00080080, nullptr, "GoThroughTable"},
21 {0x00090040, GetCountryCodeString, "GetCountryCodeString"},
22 {0x000A0040, GetCountryCodeID, "GetCountryCodeID"},
23 // cfg:s
16 {0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"}, 24 {0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"},
17 {0x04020082, nullptr, "SetConfigInfoBlk4"}, 25 {0x04020082, nullptr, "SetConfigInfoBlk4"},
18 {0x04030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"}, 26 {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 {
9namespace CFG { 9namespace CFG {
10 10
11const Interface::FunctionInfo FunctionTable[] = { 11const Interface::FunctionInfo FunctionTable[] = {
12 // cfg common
12 {0x00010082, GetConfigInfoBlk2, "GetConfigInfoBlk2"}, 13 {0x00010082, GetConfigInfoBlk2, "GetConfigInfoBlk2"},
13 {0x00020000, SecureInfoGetRegion, "SecureInfoGetRegion"}, 14 {0x00020000, SecureInfoGetRegion, "SecureInfoGetRegion"},
14 {0x00030040, GenHashConsoleUnique, "GenHashConsoleUnique"}, 15 {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 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/logging/log.h"
6#include "core/hle/hle.h"
7#include "core/hle/service/dlp_srvr.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace DLP_SRVR
11
12namespace DLP_SRVR {
13
14static void unk_0x000E0040(Service::Interface* self) {
15 u32* cmd_buff = Kernel::GetCommandBuffer();
16
17 cmd_buff[1] = RESULT_SUCCESS.raw;
18 cmd_buff[2] = 0;
19
20 LOG_WARNING(Service_DLP, "(STUBBED) called");
21}
22
23const Interface::FunctionInfo FunctionTable[] = {
24 {0x00010183, nullptr, "Initialize"},
25 {0x00020000, nullptr, "Finalize"},
26 {0x000E0040, unk_0x000E0040, "unk_0x000E0040"},
27};
28
29////////////////////////////////////////////////////////////////////////////////////////////////////
30// Interface class
31
32Interface::Interface() {
33 Register(FunctionTable);
34}
35
36} // 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 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/service.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace DLP_SRVR
11
12namespace DLP_SRVR {
13
14class Interface : public Service::Interface {
15public:
16 Interface();
17
18 std::string GetPortName() const override {
19 return "dlp:SRVR";
20 }
21};
22
23} // 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 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/string_util.h"
6
5#include "core/hle/service/service.h" 7#include "core/hle/service/service.h"
6#include "core/hle/service/frd/frd.h" 8#include "core/hle/service/frd/frd.h"
7#include "core/hle/service/frd/frd_a.h" 9#include "core/hle/service/frd/frd_a.h"
@@ -10,6 +12,95 @@
10namespace Service { 12namespace Service {
11namespace FRD { 13namespace FRD {
12 14
15static FriendKey my_friend_key = {0, 0, 0ull};
16static MyPresence my_presence = {};
17
18void GetMyPresence(Service::Interface* self) {
19 u32* cmd_buff = Kernel::GetCommandBuffer();
20
21 u32 shifted_out_size = cmd_buff[64];
22 u32 my_presence_addr = cmd_buff[65];
23
24 ASSERT(shifted_out_size == ((sizeof(MyPresence) << 14) | 2));
25
26 Memory::WriteBlock(my_presence_addr, reinterpret_cast<const u8*>(&my_presence), sizeof(MyPresence));
27
28 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
29
30 LOG_WARNING(Service_FRD, "(STUBBED) called");
31}
32
33void GetFriendKeyList(Service::Interface* self) {
34 u32* cmd_buff = Kernel::GetCommandBuffer();
35
36 u32 unknown = cmd_buff[1];
37 u32 frd_count = cmd_buff[2];
38 u32 frd_key_addr = cmd_buff[65];
39
40 FriendKey zero_key = {};
41 for (u32 i = 0; i < frd_count; ++i) {
42 Memory::WriteBlock(frd_key_addr + i * sizeof(FriendKey),
43 reinterpret_cast<const u8*>(&zero_key), sizeof(FriendKey));
44 }
45
46 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
47 cmd_buff[2] = 0; // 0 friends
48 LOG_WARNING(Service_FRD, "(STUBBED) called, unknown=%d, frd_count=%d, frd_key_addr=0x%08X",
49 unknown, frd_count, frd_key_addr);
50}
51
52void GetFriendProfile(Service::Interface* self) {
53 u32* cmd_buff = Kernel::GetCommandBuffer();
54
55 u32 count = cmd_buff[1];
56 u32 frd_key_addr = cmd_buff[3];
57 u32 profiles_addr = cmd_buff[65];
58
59 Profile zero_profile = {};
60 for (u32 i = 0; i < count; ++i) {
61 Memory::WriteBlock(profiles_addr + i * sizeof(Profile),
62 reinterpret_cast<const u8*>(&zero_profile), sizeof(Profile));
63 }
64
65 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
66 LOG_WARNING(Service_FRD, "(STUBBED) called, count=%d, frd_key_addr=0x%08X, profiles_addr=0x%08X",
67 count, frd_key_addr, profiles_addr);
68}
69
70void GetFriendAttributeFlags(Service::Interface* self) {
71 u32* cmd_buff = Kernel::GetCommandBuffer();
72
73 u32 count = cmd_buff[1];
74 u32 frd_key_addr = cmd_buff[3];
75 u32 attr_flags_addr = cmd_buff[65];
76
77 for (u32 i = 0; i < count; ++i) {
78 //TODO:(mailwl) figure out AttributeFlag size and zero all buffer. Assume 1 byte
79 Memory::Write8(attr_flags_addr + i, 0);
80 }
81
82 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
83 LOG_WARNING(Service_FRD, "(STUBBED) called, count=%d, frd_key_addr=0x%08X, attr_flags_addr=0x%08X",
84 count, frd_key_addr, attr_flags_addr);
85}
86
87void GetMyFriendKey(Service::Interface* self) {
88 u32* cmd_buff = Kernel::GetCommandBuffer();
89
90 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
91 Memory::WriteBlock(cmd_buff[2], reinterpret_cast<const u8*>(&my_friend_key), sizeof(FriendKey));
92 LOG_WARNING(Service_FRD, "(STUBBED) called");
93}
94
95void GetMyScreenName(Service::Interface* self) {
96 u32* cmd_buff = Kernel::GetCommandBuffer();
97
98 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
99 // TODO: (mailwl) get the name from config
100 Common::UTF8ToUTF16("Citra").copy(reinterpret_cast<char16_t*>(&cmd_buff[2]), 11);
101 LOG_WARNING(Service_FRD, "(STUBBED) called");
102}
103
13void Init() { 104void Init() {
14 using namespace Kernel; 105 using namespace Kernel;
15 106
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 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "common/common_types.h"
8
7namespace Service { 9namespace Service {
10
11class Interface;
12
8namespace FRD { 13namespace FRD {
9 14
15struct FriendKey {
16 u32 friend_id;
17 u32 unknown;
18 u64 friend_code;
19};
20
21struct MyPresence {
22 u8 unknown[0x12C];
23};
24
25struct Profile {
26 u8 region;
27 u8 country;
28 u8 area;
29 u8 language;
30 u32 unknown;
31};
32
33/**
34 * FRD::GetMyPresence service function
35 * Inputs:
36 * 64 : sizeof (MyPresence) << 14 | 2
37 * 65 : Address of MyPresence structure
38 * Outputs:
39 * 1 : Result of function, 0 on success, otherwise error code
40 */
41void GetMyPresence(Service::Interface* self);
42
43/**
44 * FRD::GetFriendKeyList service function
45 * Inputs:
46 * 1 : Unknown
47 * 2 : Max friends count
48 * 65 : Address of FriendKey List
49 * Outputs:
50 * 1 : Result of function, 0 on success, otherwise error code
51 * 2 : FriendKey count filled
52 */
53void GetFriendKeyList(Service::Interface* self);
54
55/**
56 * FRD::GetFriendProfile service function
57 * Inputs:
58 * 1 : Friends count
59 * 2 : Friends count << 18 | 2
60 * 3 : Address of FriendKey List
61 * 64 : (count * sizeof (Profile)) << 10 | 2
62 * 65 : Address of Profiles List
63 * Outputs:
64 * 1 : Result of function, 0 on success, otherwise error code
65 */
66void GetFriendProfile(Service::Interface* self);
67
68/**
69 * FRD::GetFriendAttributeFlags service function
70 * Inputs:
71 * 1 : Friends count
72 * 2 : Friends count << 18 | 2
73 * 3 : Address of FriendKey List
74 * 65 : Address of AttributeFlags
75 * Outputs:
76 * 1 : Result of function, 0 on success, otherwise error code
77 */
78void GetFriendAttributeFlags(Service::Interface* self);
79
80/**
81 * FRD::GetMyFriendKey service function
82 * Inputs:
83 * none
84 * Outputs:
85 * 1 : Result of function, 0 on success, otherwise error code
86 * 2-5 : FriendKey
87 */
88void GetMyFriendKey(Service::Interface* self);
89
90/**
91 * FRD::GetMyScreenName service function
92 * Outputs:
93 * 1 : Result of function, 0 on success, otherwise error code
94 * 2 : UTF16 encoded name (max 11 symbols)
95 */
96void GetMyScreenName(Service::Interface* self);
97
10/// Initialize FRD service(s) 98/// Initialize FRD service(s)
11void Init(); 99void Init();
12 100
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 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "core/hle/service/frd/frd.h"
5#include "core/hle/service/frd/frd_u.h" 6#include "core/hle/service/frd/frd_u.h"
6 7
7namespace Service { 8namespace Service {
8namespace FRD { 9namespace FRD {
9 10
10const Interface::FunctionInfo FunctionTable[] = { 11const Interface::FunctionInfo FunctionTable[] = {
11 {0x00010000, nullptr, "HasLoggedIn"}, 12 {0x00010000, nullptr, "HasLoggedIn"},
12 {0x00020000, nullptr, "IsOnline"}, 13 {0x00020000, nullptr, "IsOnline"},
13 {0x00030000, nullptr, "Login"}, 14 {0x00030000, nullptr, "Login"},
14 {0x00040000, nullptr, "Logout"}, 15 {0x00040000, nullptr, "Logout"},
15 {0x00050000, nullptr, "GetMyFriendKey"}, 16 {0x00050000, GetMyFriendKey, "GetMyFriendKey"},
16 {0x00060000, nullptr, "GetMyPreference"}, 17 {0x00060000, nullptr, "GetMyPreference"},
17 {0x00070000, nullptr, "GetMyProfile"}, 18 {0x00070000, nullptr, "GetMyProfile"},
18 {0x00080000, nullptr, "GetMyPresence"}, 19 {0x00080000, GetMyPresence, "GetMyPresence"},
19 {0x00090000, nullptr, "GetMyScreenName"}, 20 {0x00090000, GetMyScreenName, "GetMyScreenName"},
20 {0x000A0000, nullptr, "GetMyMii"}, 21 {0x000A0000, nullptr, "GetMyMii"},
21 {0x000B0000, nullptr, "GetMyLocalAccountId"}, 22 {0x000B0000, nullptr, "GetMyLocalAccountId"},
22 {0x000C0000, nullptr, "GetMyPlayingGame"}, 23 {0x000C0000, nullptr, "GetMyPlayingGame"},
23 {0x000D0000, nullptr, "GetMyFavoriteGame"}, 24 {0x000D0000, nullptr, "GetMyFavoriteGame"},
24 {0x000E0000, nullptr, "GetMyNcPrincipalId"}, 25 {0x000E0000, nullptr, "GetMyNcPrincipalId"},
25 {0x000F0000, nullptr, "GetMyComment"}, 26 {0x000F0000, nullptr, "GetMyComment"},
26 {0x00100040, nullptr, "GetMyPassword"}, 27 {0x00100040, nullptr, "GetMyPassword"},
27 {0x00110080, nullptr, "GetFriendKeyList"}, 28 {0x00110080, GetFriendKeyList, "GetFriendKeyList"},
28 {0x00120042, nullptr, "GetFriendPresence"}, 29 {0x00120042, nullptr, "GetFriendPresence"},
29 {0x00130142, nullptr, "GetFriendScreenName"}, 30 {0x00130142, nullptr, "GetFriendScreenName"},
30 {0x00140044, nullptr, "GetFriendMii"}, 31 {0x00140044, nullptr, "GetFriendMii"},
31 {0x00150042, nullptr, "GetFriendProfile"}, 32 {0x00150042, GetFriendProfile, "GetFriendProfile"},
32 {0x00160042, nullptr, "GetFriendRelationship"}, 33 {0x00160042, nullptr, "GetFriendRelationship"},
33 {0x00170042, nullptr, "GetFriendAttributeFlags"}, 34 {0x00170042, GetFriendAttributeFlags, "GetFriendAttributeFlags"},
34 {0x00180044, nullptr, "GetFriendPlayingGame"}, 35 {0x00180044, nullptr, "GetFriendPlayingGame"},
35 {0x00190042, nullptr, "GetFriendFavoriteGame"}, 36 {0x00190042, nullptr, "GetFriendFavoriteGame"},
36 {0x001A00C4, nullptr, "GetFriendInfo"}, 37 {0x001A00C4, nullptr, "GetFriendInfo"},
37 {0x001B0080, nullptr, "IsIncludedInFriendList"}, 38 {0x001B0080, nullptr, "IsIncludedInFriendList"},
38 {0x001C0042, nullptr, "UnscrambleLocalFriendCode"}, 39 {0x001C0042, nullptr, "UnscrambleLocalFriendCode"},
39 {0x001D0002, nullptr, "UpdateGameModeDescription"}, 40 {0x001D0002, nullptr, "UpdateGameModeDescription"},
40 {0x001E02C2, nullptr, "UpdateGameMode"}, 41 {0x001E02C2, nullptr, "UpdateGameMode"},
41 {0x001F0042, nullptr, "SendInvitation"}, 42 {0x001F0042, nullptr, "SendInvitation"},
42 {0x00200002, nullptr, "AttachToEventNotification"}, 43 {0x00200002, nullptr, "AttachToEventNotification"},
43 {0x00210040, nullptr, "SetNotificationMask"}, 44 {0x00210040, nullptr, "SetNotificationMask"},
44 {0x00220040, nullptr, "GetEventNotification"}, 45 {0x00220040, nullptr, "GetEventNotification"},
45 {0x00230000, nullptr, "GetLastResponseResult"}, 46 {0x00230000, nullptr, "GetLastResponseResult"},
46 {0x00240040, nullptr, "PrincipalIdToFriendCode"}, 47 {0x00240040, nullptr, "PrincipalIdToFriendCode"},
47 {0x00250080, nullptr, "FriendCodeToPrincipalId"}, 48 {0x00250080, nullptr, "FriendCodeToPrincipalId"},
48 {0x00260080, nullptr, "IsValidFriendCode"}, 49 {0x00260080, nullptr, "IsValidFriendCode"},
49 {0x00270040, nullptr, "ResultToErrorCode"}, 50 {0x00270040, nullptr, "ResultToErrorCode"},
50 {0x00280244, nullptr, "RequestGameAuthentication"}, 51 {0x00280244, nullptr, "RequestGameAuthentication"},
51 {0x00290000, nullptr, "GetGameAuthenticationData"}, 52 {0x00290000, nullptr, "GetGameAuthenticationData"},
52 {0x002A0204, nullptr, "RequestServiceLocator"}, 53 {0x002A0204, nullptr, "RequestServiceLocator"},
53 {0x002B0000, nullptr, "GetServiceLocatorData"}, 54 {0x002B0000, nullptr, "GetServiceLocatorData"},
54 {0x002C0002, nullptr, "DetectNatProperties"}, 55 {0x002C0002, nullptr, "DetectNatProperties"},
55 {0x002D0000, nullptr, "GetNatProperties"}, 56 {0x002D0000, nullptr, "GetNatProperties"},
56 {0x002E0000, nullptr, "GetServerTimeInterval"}, 57 {0x002E0000, nullptr, "GetServerTimeInterval"},
57 {0x002F0040, nullptr, "AllowHalfAwake"}, 58 {0x002F0040, nullptr, "AllowHalfAwake"},
58 {0x00300000, nullptr, "GetServerTypes"}, 59 {0x00300000, nullptr, "GetServerTypes"},
59 {0x00310082, nullptr, "GetFriendComment"}, 60 {0x00310082, nullptr, "GetFriendComment"},
60 {0x00320042, nullptr, "SetClientSdkVersion"}, 61 {0x00320042, nullptr, "SetClientSdkVersion"},
61 {0x00330000, nullptr, "GetMyApproachContext"}, 62 {0x00330000, nullptr, "GetMyApproachContext"},
62 {0x00340046, nullptr, "AddFriendWithApproach"}, 63 {0x00340046, nullptr, "AddFriendWithApproach"},
63 {0x00350082, nullptr, "DecryptApproachContext"}, 64 {0x00350082, nullptr, "DecryptApproachContext"},
64}; 65};
65 66
66FRD_U_Interface::FRD_U_Interface() { 67FRD_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 @@
15#include "common/common_types.h" 15#include "common/common_types.h"
16#include "common/file_util.h" 16#include "common/file_util.h"
17#include "common/logging/log.h" 17#include "common/logging/log.h"
18#include "common/make_unique.h"
19 18
20#include "core/file_sys/archive_backend.h" 19#include "core/file_sys/archive_backend.h"
21#include "core/file_sys/archive_extsavedata.h" 20#include "core/file_sys/archive_extsavedata.h"
@@ -521,23 +520,23 @@ void ArchiveInit() {
521 520
522 std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX); 521 std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX);
523 std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX); 522 std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX);
524 auto sdmc_factory = Common::make_unique<FileSys::ArchiveFactory_SDMC>(sdmc_directory); 523 auto sdmc_factory = std::make_unique<FileSys::ArchiveFactory_SDMC>(sdmc_directory);
525 if (sdmc_factory->Initialize()) 524 if (sdmc_factory->Initialize())
526 RegisterArchiveType(std::move(sdmc_factory), ArchiveIdCode::SDMC); 525 RegisterArchiveType(std::move(sdmc_factory), ArchiveIdCode::SDMC);
527 else 526 else
528 LOG_ERROR(Service_FS, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str()); 527 LOG_ERROR(Service_FS, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str());
529 528
530 // Create the SaveData archive 529 // Create the SaveData archive
531 auto savedata_factory = Common::make_unique<FileSys::ArchiveFactory_SaveData>(sdmc_directory); 530 auto savedata_factory = std::make_unique<FileSys::ArchiveFactory_SaveData>(sdmc_directory);
532 RegisterArchiveType(std::move(savedata_factory), ArchiveIdCode::SaveData); 531 RegisterArchiveType(std::move(savedata_factory), ArchiveIdCode::SaveData);
533 532
534 auto extsavedata_factory = Common::make_unique<FileSys::ArchiveFactory_ExtSaveData>(sdmc_directory, false); 533 auto extsavedata_factory = std::make_unique<FileSys::ArchiveFactory_ExtSaveData>(sdmc_directory, false);
535 if (extsavedata_factory->Initialize()) 534 if (extsavedata_factory->Initialize())
536 RegisterArchiveType(std::move(extsavedata_factory), ArchiveIdCode::ExtSaveData); 535 RegisterArchiveType(std::move(extsavedata_factory), ArchiveIdCode::ExtSaveData);
537 else 536 else
538 LOG_ERROR(Service_FS, "Can't instantiate ExtSaveData archive with path %s", extsavedata_factory->GetMountPoint().c_str()); 537 LOG_ERROR(Service_FS, "Can't instantiate ExtSaveData archive with path %s", extsavedata_factory->GetMountPoint().c_str());
539 538
540 auto sharedextsavedata_factory = Common::make_unique<FileSys::ArchiveFactory_ExtSaveData>(nand_directory, true); 539 auto sharedextsavedata_factory = std::make_unique<FileSys::ArchiveFactory_ExtSaveData>(nand_directory, true);
541 if (sharedextsavedata_factory->Initialize()) 540 if (sharedextsavedata_factory->Initialize())
542 RegisterArchiveType(std::move(sharedextsavedata_factory), ArchiveIdCode::SharedExtSaveData); 541 RegisterArchiveType(std::move(sharedextsavedata_factory), ArchiveIdCode::SharedExtSaveData);
543 else 542 else
@@ -545,10 +544,10 @@ void ArchiveInit() {
545 sharedextsavedata_factory->GetMountPoint().c_str()); 544 sharedextsavedata_factory->GetMountPoint().c_str());
546 545
547 // Create the SaveDataCheck archive, basically a small variation of the RomFS archive 546 // Create the SaveDataCheck archive, basically a small variation of the RomFS archive
548 auto savedatacheck_factory = Common::make_unique<FileSys::ArchiveFactory_SaveDataCheck>(nand_directory); 547 auto savedatacheck_factory = std::make_unique<FileSys::ArchiveFactory_SaveDataCheck>(nand_directory);
549 RegisterArchiveType(std::move(savedatacheck_factory), ArchiveIdCode::SaveDataCheck); 548 RegisterArchiveType(std::move(savedatacheck_factory), ArchiveIdCode::SaveDataCheck);
550 549
551 auto systemsavedata_factory = Common::make_unique<FileSys::ArchiveFactory_SystemSaveData>(nand_directory); 550 auto systemsavedata_factory = std::make_unique<FileSys::ArchiveFactory_SystemSaveData>(nand_directory);
552 RegisterArchiveType(std::move(systemsavedata_factory), ArchiveIdCode::SystemSaveData); 551 RegisterArchiveType(std::move(systemsavedata_factory), ArchiveIdCode::SystemSaveData);
553} 552}
554 553
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;
31 31
32namespace GSP_GPU { 32namespace GSP_GPU {
33 33
34const ResultCode ERR_GSP_REGS_OUTOFRANGE_OR_MISALIGNED(ErrorDescription::OutofRangeOrMisalignedAddress, ErrorModule::GX,
35 ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E02A01
36const ResultCode ERR_GSP_REGS_MISALIGNED(ErrorDescription::MisalignedSize, ErrorModule::GX,
37 ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E02BF2
38const ResultCode ERR_GSP_REGS_INVALID_SIZE(ErrorDescription::InvalidSize, ErrorModule::GX,
39 ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E02BEC
40
34/// Event triggered when GSP interrupt has been signalled 41/// Event triggered when GSP interrupt has been signalled
35Kernel::SharedPtr<Kernel::Event> g_interrupt_event; 42Kernel::SharedPtr<Kernel::Event> g_interrupt_event;
36/// GSP shared memoryings 43/// GSP shared memoryings
@@ -59,47 +66,87 @@ static inline InterruptRelayQueue* GetInterruptRelayQueue(u32 thread_id) {
59} 66}
60 67
61/** 68/**
62 * Checks if the parameters in a register write call are valid and logs in the case that 69 * Writes sequential GSP GPU hardware registers using an array of source data
63 * they are not 70 *
64 * @param base_address The first address in the sequence of registers that will be written 71 * @param base_address The address of the first register in the sequence
65 * @param size_in_bytes The number of registers that will be written 72 * @param size_in_bytes The number of registers to update (size of data)
66 * @return true if the parameters are valid, false otherwise 73 * @param data A pointer to the source data
74 * @return RESULT_SUCCESS if the parameters are valid, error code otherwise
67 */ 75 */
68static bool CheckWriteParameters(u32 base_address, u32 size_in_bytes) { 76static ResultCode WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) {
69 // TODO: Return proper error codes 77 // This magic number is verified to be done by the gsp module
70 if (base_address + size_in_bytes >= 0x420000) { 78 const u32 max_size_in_bytes = 0x80;
71 LOG_ERROR(Service_GSP, "Write address out of range! (address=0x%08x, size=0x%08x)", 79
80 if (base_address & 3 || base_address >= 0x420000) {
81 LOG_ERROR(Service_GSP, "Write address was out of range or misaligned! (address=0x%08x, size=0x%08x)",
72 base_address, size_in_bytes); 82 base_address, size_in_bytes);
73 return false; 83 return ERR_GSP_REGS_OUTOFRANGE_OR_MISALIGNED;
74 } 84 } else if (size_in_bytes <= max_size_in_bytes) {
85 if (size_in_bytes & 3) {
86 LOG_ERROR(Service_GSP, "Misaligned size 0x%08x", size_in_bytes);
87 return ERR_GSP_REGS_MISALIGNED;
88 } else {
89 while (size_in_bytes > 0) {
90 HW::Write<u32>(base_address + REGS_BEGIN, *data);
91
92 size_in_bytes -= 4;
93 ++data;
94 base_address += 4;
95 }
96 return RESULT_SUCCESS;
97 }
75 98
76 // size should be word-aligned 99 } else {
77 if ((size_in_bytes % 4) != 0) { 100 LOG_ERROR(Service_GSP, "Out of range size 0x%08x", size_in_bytes);
78 LOG_ERROR(Service_GSP, "Invalid size 0x%08x", size_in_bytes); 101 return ERR_GSP_REGS_INVALID_SIZE;
79 return false;
80 } 102 }
81
82 return true;
83} 103}
84 104
85/** 105/**
86 * Writes sequential GSP GPU hardware registers using an array of source data 106 * Updates sequential GSP GPU hardware registers using parallel arrays of source data and masks.
107 * For each register, the value is updated only where the mask is high
87 * 108 *
88 * @param base_address The address of the first register in the sequence 109 * @param base_address The address of the first register in the sequence
89 * @param size_in_bytes The number of registers to update (size of data) 110 * @param size_in_bytes The number of registers to update (size of data)
90 * @param data A pointer to the source data 111 * @param data A pointer to the source data to use for updates
112 * @param masks A pointer to the masks
113 * @return RESULT_SUCCESS if the parameters are valid, error code otherwise
91 */ 114 */
92static void WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) { 115static ResultCode WriteHWRegsWithMask(u32 base_address, u32 size_in_bytes, const u32* data, const u32* masks) {
93 // TODO: Return proper error codes 116 // This magic number is verified to be done by the gsp module
94 if (!CheckWriteParameters(base_address, size_in_bytes)) 117 const u32 max_size_in_bytes = 0x80;
95 return;
96 118
97 while (size_in_bytes > 0) { 119 if (base_address & 3 || base_address >= 0x420000) {
98 HW::Write<u32>(base_address + REGS_BEGIN, *data); 120 LOG_ERROR(Service_GSP, "Write address was out of range or misaligned! (address=0x%08x, size=0x%08x)",
121 base_address, size_in_bytes);
122 return ERR_GSP_REGS_OUTOFRANGE_OR_MISALIGNED;
123 } else if (size_in_bytes <= max_size_in_bytes) {
124 if (size_in_bytes & 3) {
125 LOG_ERROR(Service_GSP, "Misaligned size 0x%08x", size_in_bytes);
126 return ERR_GSP_REGS_MISALIGNED;
127 } else {
128 while (size_in_bytes > 0) {
129 const u32 reg_address = base_address + REGS_BEGIN;
130
131 u32 reg_value;
132 HW::Read<u32>(reg_value, reg_address);
133
134 // Update the current value of the register only for set mask bits
135 reg_value = (reg_value & ~*masks) | (*data | *masks);
136
137 HW::Write<u32>(reg_address, reg_value);
138
139 size_in_bytes -= 4;
140 ++data;
141 ++masks;
142 base_address += 4;
143 }
144 return RESULT_SUCCESS;
145 }
99 146
100 size_in_bytes -= 4; 147 } else {
101 ++data; 148 LOG_ERROR(Service_GSP, "Out of range size 0x%08x", size_in_bytes);
102 base_address += 4; 149 return ERR_GSP_REGS_INVALID_SIZE;
103 } 150 }
104} 151}
105 152
@@ -120,39 +167,7 @@ static void WriteHWRegs(Service::Interface* self) {
120 167
121 u32* src = (u32*)Memory::GetPointer(cmd_buff[4]); 168 u32* src = (u32*)Memory::GetPointer(cmd_buff[4]);
122 169
123 WriteHWRegs(reg_addr, size, src); 170 cmd_buff[1] = WriteHWRegs(reg_addr, size, src).raw;
124}
125
126/**
127 * Updates sequential GSP GPU hardware registers using parallel arrays of source data and masks.
128 * For each register, the value is updated only where the mask is high
129 *
130 * @param base_address The address of the first register in the sequence
131 * @param size_in_bytes The number of registers to update (size of data)
132 * @param data A pointer to the source data to use for updates
133 * @param masks A pointer to the masks
134 */
135static void WriteHWRegsWithMask(u32 base_address, u32 size_in_bytes, const u32* data, const u32* masks) {
136 // TODO: Return proper error codes
137 if (!CheckWriteParameters(base_address, size_in_bytes))
138 return;
139
140 while (size_in_bytes > 0) {
141 const u32 reg_address = base_address + REGS_BEGIN;
142
143 u32 reg_value;
144 HW::Read<u32>(reg_value, reg_address);
145
146 // Update the current value of the register only for set mask bits
147 reg_value = (reg_value & ~*masks) | (*data | *masks);
148
149 HW::Write<u32>(reg_address, reg_value);
150
151 size_in_bytes -= 4;
152 ++data;
153 ++masks;
154 base_address += 4;
155 }
156} 171}
157 172
158/** 173/**
@@ -174,7 +189,7 @@ static void WriteHWRegsWithMask(Service::Interface* self) {
174 u32* src_data = (u32*)Memory::GetPointer(cmd_buff[4]); 189 u32* src_data = (u32*)Memory::GetPointer(cmd_buff[4]);
175 u32* mask_data = (u32*)Memory::GetPointer(cmd_buff[6]); 190 u32* mask_data = (u32*)Memory::GetPointer(cmd_buff[6]);
176 191
177 WriteHWRegsWithMask(reg_addr, size, src_data, mask_data); 192 cmd_buff[1] = WriteHWRegsWithMask(reg_addr, size, src_data, mask_data).raw;
178} 193}
179 194
180/// Read a GSP GPU hardware register 195/// Read a GSP GPU hardware register
@@ -206,27 +221,27 @@ static void ReadHWRegs(Service::Interface* self) {
206 } 221 }
207} 222}
208 223
209void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) { 224ResultCode SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) {
210 u32 base_address = 0x400000; 225 u32 base_address = 0x400000;
211 PAddr phys_address_left = Memory::VirtualToPhysicalAddress(info.address_left); 226 PAddr phys_address_left = Memory::VirtualToPhysicalAddress(info.address_left);
212 PAddr phys_address_right = Memory::VirtualToPhysicalAddress(info.address_right); 227 PAddr phys_address_right = Memory::VirtualToPhysicalAddress(info.address_right);
213 if (info.active_fb == 0) { 228 if (info.active_fb == 0) {
214 WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_left1)), 4, 229 WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_left1)),
215 &phys_address_left); 230 4, &phys_address_left);
216 WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_right1)), 4, 231 WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_right1)),
217 &phys_address_right); 232 4, &phys_address_right);
218 } else { 233 } else {
219 WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_left2)), 4, 234 WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_left2)),
220 &phys_address_left); 235 4, &phys_address_left);
221 WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_right2)), 4, 236 WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_right2)),
222 &phys_address_right); 237 4, &phys_address_right);
223 } 238 }
224 WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].stride)), 4, 239 WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].stride)),
225 &info.stride); 240 4, &info.stride);
226 WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].color_format)), 4, 241 WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].color_format)),
227 &info.format); 242 4, &info.format);
228 WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].active_fb)), 4, 243 WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].active_fb)),
229 &info.shown_fb); 244 4, &info.shown_fb);
230 245
231 if (Pica::g_debug_context) 246 if (Pica::g_debug_context)
232 Pica::g_debug_context->OnEvent(Pica::DebugContext::Event::BufferSwapped, nullptr); 247 Pica::g_debug_context->OnEvent(Pica::DebugContext::Event::BufferSwapped, nullptr);
@@ -234,6 +249,8 @@ void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) {
234 if (screen_id == 0) { 249 if (screen_id == 0) {
235 MicroProfileFlip(); 250 MicroProfileFlip();
236 } 251 }
252
253 return RESULT_SUCCESS;
237} 254}
238 255
239/** 256/**
@@ -251,9 +268,8 @@ static void SetBufferSwap(Service::Interface* self) {
251 u32* cmd_buff = Kernel::GetCommandBuffer(); 268 u32* cmd_buff = Kernel::GetCommandBuffer();
252 u32 screen_id = cmd_buff[1]; 269 u32 screen_id = cmd_buff[1];
253 FrameBufferInfo* fb_info = (FrameBufferInfo*)&cmd_buff[2]; 270 FrameBufferInfo* fb_info = (FrameBufferInfo*)&cmd_buff[2];
254 SetBufferSwap(screen_id, *fb_info);
255 271
256 cmd_buff[1] = 0; // No error 272 cmd_buff[1] = SetBufferSwap(screen_id, *fb_info).raw;
257} 273}
258 274
259/** 275/**
@@ -286,6 +302,22 @@ static void FlushDataCache(Service::Interface* self) {
286} 302}
287 303
288/** 304/**
305 * GSP_GPU::SetAxiConfigQoSMode service function
306 * Inputs:
307 * 1 : Mode, unused in emulator
308 * Outputs:
309 * 1 : Result of function, 0 on success, otherwise error code
310 */
311static void SetAxiConfigQoSMode(Service::Interface* self) {
312 u32* cmd_buff = Kernel::GetCommandBuffer();
313 u32 mode = cmd_buff[1];
314
315 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
316
317 LOG_WARNING(Service_GSP, "(STUBBED) called mode=0x%08X", mode);
318}
319
320/**
289 * GSP_GPU::RegisterInterruptRelayQueue service function 321 * GSP_GPU::RegisterInterruptRelayQueue service function
290 * Inputs: 322 * Inputs:
291 * 1 : "Flags" field, purpose is unknown 323 * 1 : "Flags" field, purpose is unknown
@@ -302,6 +334,12 @@ static void RegisterInterruptRelayQueue(Service::Interface* self) {
302 g_interrupt_event = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[3]); 334 g_interrupt_event = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[3]);
303 ASSERT_MSG((g_interrupt_event != nullptr), "handle is not valid!"); 335 ASSERT_MSG((g_interrupt_event != nullptr), "handle is not valid!");
304 336
337 g_interrupt_event->name = "GSP_GPU::interrupt_event";
338
339 using Kernel::MemoryPermission;
340 g_shared_memory = Kernel::SharedMemory::Create(0x1000, MemoryPermission::ReadWrite,
341 MemoryPermission::ReadWrite, "GSPSharedMem");
342
305 Handle shmem_handle = Kernel::g_handle_table.Create(g_shared_memory).MoveFrom(); 343 Handle shmem_handle = Kernel::g_handle_table.Create(g_shared_memory).MoveFrom();
306 344
307 // This specific code is required for a successful initialization, rather than 0 345 // This specific code is required for a successful initialization, rather than 0
@@ -314,6 +352,22 @@ static void RegisterInterruptRelayQueue(Service::Interface* self) {
314} 352}
315 353
316/** 354/**
355 * GSP_GPU::UnregisterInterruptRelayQueue service function
356 * Outputs:
357 * 1 : Result of function, 0 on success, otherwise error code
358 */
359static void UnregisterInterruptRelayQueue(Service::Interface* self) {
360 u32* cmd_buff = Kernel::GetCommandBuffer();
361
362 g_shared_memory = nullptr;
363 g_interrupt_event = nullptr;
364
365 cmd_buff[1] = RESULT_SUCCESS.raw;
366
367 LOG_WARNING(Service_GSP, "called");
368}
369
370/**
317 * Signals that the specified interrupt type has occurred to userland code 371 * Signals that the specified interrupt type has occurred to userland code
318 * @param interrupt_id ID of interrupt that is being signalled 372 * @param interrupt_id ID of interrupt that is being signalled
319 * @todo This should probably take a thread_id parameter and only signal this thread? 373 * @todo This should probably take a thread_id parameter and only signal this thread?
@@ -591,11 +645,11 @@ const Interface::FunctionInfo FunctionTable[] = {
591 {0x000D0140, nullptr, "SetDisplayTransfer"}, 645 {0x000D0140, nullptr, "SetDisplayTransfer"},
592 {0x000E0180, nullptr, "SetTextureCopy"}, 646 {0x000E0180, nullptr, "SetTextureCopy"},
593 {0x000F0200, nullptr, "SetMemoryFill"}, 647 {0x000F0200, nullptr, "SetMemoryFill"},
594 {0x00100040, nullptr, "SetAxiConfigQoSMode"}, 648 {0x00100040, SetAxiConfigQoSMode, "SetAxiConfigQoSMode"},
595 {0x00110040, nullptr, "SetPerfLogMode"}, 649 {0x00110040, nullptr, "SetPerfLogMode"},
596 {0x00120000, nullptr, "GetPerfLog"}, 650 {0x00120000, nullptr, "GetPerfLog"},
597 {0x00130042, RegisterInterruptRelayQueue, "RegisterInterruptRelayQueue"}, 651 {0x00130042, RegisterInterruptRelayQueue, "RegisterInterruptRelayQueue"},
598 {0x00140000, nullptr, "UnregisterInterruptRelayQueue"}, 652 {0x00140000, UnregisterInterruptRelayQueue, "UnregisterInterruptRelayQueue"},
599 {0x00150002, nullptr, "TryAcquireRight"}, 653 {0x00150002, nullptr, "TryAcquireRight"},
600 {0x00160042, nullptr, "AcquireRight"}, 654 {0x00160042, nullptr, "AcquireRight"},
601 {0x00170000, nullptr, "ReleaseRight"}, 655 {0x00170000, nullptr, "ReleaseRight"},
@@ -616,10 +670,7 @@ Interface::Interface() {
616 Register(FunctionTable); 670 Register(FunctionTable);
617 671
618 g_interrupt_event = nullptr; 672 g_interrupt_event = nullptr;
619 673 g_shared_memory = nullptr;
620 using Kernel::MemoryPermission;
621 g_shared_memory = Kernel::SharedMemory::Create(0x1000, MemoryPermission::ReadWrite,
622 MemoryPermission::ReadWrite, "GSPSharedMem");
623 674
624 g_thread_id = 0; 675 g_thread_id = 0;
625} 676}
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:
194 */ 194 */
195void SignalInterrupt(InterruptId interrupt_id); 195void SignalInterrupt(InterruptId interrupt_id);
196 196
197void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info); 197ResultCode SetBufferSwap(u32 screen_id, const FrameBufferInfo& info);
198 198
199/** 199/**
200 * Retrieves the framebuffer info stored in the GSP shared memory for the 200 * 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 @@
9#include "core/hle/service/ac_u.h" 9#include "core/hle/service/ac_u.h"
10#include "core/hle/service/act_u.h" 10#include "core/hle/service/act_u.h"
11#include "core/hle/service/csnd_snd.h" 11#include "core/hle/service/csnd_snd.h"
12#include "core/hle/service/dlp_srvr.h"
12#include "core/hle/service/dsp_dsp.h" 13#include "core/hle/service/dsp_dsp.h"
13#include "core/hle/service/err_f.h" 14#include "core/hle/service/err_f.h"
14#include "core/hle/service/gsp_gpu.h" 15#include "core/hle/service/gsp_gpu.h"
@@ -70,9 +71,8 @@ ResultVal<bool> Interface::SyncRequest() {
70 // TODO(bunnei): Hack - ignore error 71 // TODO(bunnei): Hack - ignore error
71 cmd_buff[1] = 0; 72 cmd_buff[1] = 0;
72 return MakeResult<bool>(false); 73 return MakeResult<bool>(false);
73 } else {
74 LOG_TRACE(Service, "%s", MakeFunctionString(itr->second.name, GetPortName().c_str(), cmd_buff).c_str());
75 } 74 }
75 LOG_TRACE(Service, "%s", MakeFunctionString(itr->second.name, GetPortName().c_str(), cmd_buff).c_str());
76 76
77 itr->second.func(this); 77 itr->second.func(this);
78 78
@@ -121,6 +121,7 @@ void Init() {
121 AddService(new AC_U::Interface); 121 AddService(new AC_U::Interface);
122 AddService(new ACT_U::Interface); 122 AddService(new ACT_U::Interface);
123 AddService(new CSND_SND::Interface); 123 AddService(new CSND_SND::Interface);
124 AddService(new DLP_SRVR::Interface);
124 AddService(new DSP_DSP::Interface); 125 AddService(new DSP_DSP::Interface);
125 AddService(new GSP_GPU::Interface); 126 AddService(new GSP_GPU::Interface);
126 AddService(new GSP_LCD::Interface); 127 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() {
16 std::memset(&shared_page, 0, sizeof(shared_page)); 16 std::memset(&shared_page, 0, sizeof(shared_page));
17 17
18 shared_page.running_hw = 0x1; // product 18 shared_page.running_hw = 0x1; // product
19
20 // Some games wait until this value becomes 0x1, before asking running_hw
21 shared_page.unknown_value = 0x1;
19} 22}
20 23
21} // namespace 24} // 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 {
39 DateTime date_time_0; // 20 39 DateTime date_time_0; // 20
40 DateTime date_time_1; // 40 40 DateTime date_time_1; // 40
41 u8 wifi_macaddr[6]; // 60 41 u8 wifi_macaddr[6]; // 60
42 u8 wifi_unknown1; // 66 42 u8 wifi_link_level; // 66
43 u8 wifi_unknown2; // 67 43 u8 wifi_unknown2; // 67
44 INSERT_PADDING_BYTES(0x80 - 0x68); // 68 44 INSERT_PADDING_BYTES(0x80 - 0x68); // 68
45 float_le sliderstate_3d; // 80 45 float_le sliderstate_3d; // 80
46 u8 ledstate_3d; // 84 46 u8 ledstate_3d; // 84
47 INSERT_PADDING_BYTES(0xA0 - 0x85); // 85 47 INSERT_PADDING_BYTES(1); // 85
48 u8 unknown_value; // 86
49 INSERT_PADDING_BYTES(0xA0 - 0x87); // 87
48 u64_le menu_title_id; // A0 50 u64_le menu_title_id; // A0
49 u64_le active_menu_title_id; // A8 51 u64_le active_menu_title_id; // A8
50 INSERT_PADDING_BYTES(0x1000 - 0xB0); // B0 52 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) {
261 ASSERT(cvt.block_alignment != BlockAlignment::Block8x8 || cvt.input_lines % 8 == 0); 261 ASSERT(cvt.block_alignment != BlockAlignment::Block8x8 || cvt.input_lines % 8 == 0);
262 // Tiles per row 262 // Tiles per row
263 size_t num_tiles = cvt.input_line_width / 8; 263 size_t num_tiles = cvt.input_line_width / 8;
264 ASSERT(num_tiles < MAX_TILES); 264 ASSERT(num_tiles <= MAX_TILES);
265 265
266 // Buffer used as a CDMA source/target. 266 // Buffer used as a CDMA source/target.
267 std::unique_ptr<u8[]> data_buffer(new u8[cvt.input_line_width * 8 * 4]); 267 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 @@
10#include "core/file_sys/archive_romfs.h" 10#include "core/file_sys/archive_romfs.h"
11#include "core/hle/kernel/process.h" 11#include "core/hle/kernel/process.h"
12#include "core/hle/kernel/resource_limit.h" 12#include "core/hle/kernel/resource_limit.h"
13#include "core/hle/service/fs/archive.h" 13#include "core/loader/3dsx.h"
14#include "core/loader/elf.h"
15#include "core/loader/ncch.h"
16#include "core/memory.h" 14#include "core/memory.h"
17 15
18#include "3dsx.h"
19
20namespace Loader { 16namespace Loader {
21 17
22/* 18/*
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 @@
6#include <string> 6#include <string>
7 7
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "common/make_unique.h"
10#include "common/string_util.h" 9#include "common/string_util.h"
11 10
12#include "core/file_sys/archive_romfs.h" 11#include "core/file_sys/archive_romfs.h"
@@ -120,7 +119,7 @@ ResultStatus LoadFile(const std::string& filename) {
120 AppLoader_THREEDSX app_loader(std::move(file), filename_filename, filename); 119 AppLoader_THREEDSX app_loader(std::move(file), filename_filename, filename);
121 // Load application and RomFS 120 // Load application and RomFS
122 if (ResultStatus::Success == app_loader.Load()) { 121 if (ResultStatus::Success == app_loader.Load()) {
123 Service::FS::RegisterArchiveType(Common::make_unique<FileSys::ArchiveFactory_RomFS>(app_loader), Service::FS::ArchiveIdCode::RomFS); 122 Service::FS::RegisterArchiveType(std::make_unique<FileSys::ArchiveFactory_RomFS>(app_loader), Service::FS::ArchiveIdCode::RomFS);
124 return ResultStatus::Success; 123 return ResultStatus::Success;
125 } 124 }
126 break; 125 break;
@@ -139,7 +138,7 @@ ResultStatus LoadFile(const std::string& filename) {
139 // Load application and RomFS 138 // Load application and RomFS
140 ResultStatus result = app_loader.Load(); 139 ResultStatus result = app_loader.Load();
141 if (ResultStatus::Success == result) { 140 if (ResultStatus::Success == result) {
142 Service::FS::RegisterArchiveType(Common::make_unique<FileSys::ArchiveFactory_RomFS>(app_loader), Service::FS::ArchiveIdCode::RomFS); 141 Service::FS::RegisterArchiveType(std::make_unique<FileSys::ArchiveFactory_RomFS>(app_loader), Service::FS::ArchiveIdCode::RomFS);
143 } 142 }
144 return result; 143 return result;
145 } 144 }
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 @@
7#include <memory> 7#include <memory>
8 8
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "common/make_unique.h"
11#include "common/string_util.h" 10#include "common/string_util.h"
12#include "common/swap.h" 11#include "common/swap.h"
13 12
@@ -175,7 +174,7 @@ ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>&
175 return ResultStatus::Error; 174 return ResultStatus::Error;
176 175
177 LOG_DEBUG(Loader, "%d sections:", kMaxSections); 176 LOG_DEBUG(Loader, "%d sections:", kMaxSections);
178 // Iterate through the ExeFs archive until we find the .code file... 177 // Iterate through the ExeFs archive until we find a section with the specified name...
179 for (unsigned section_number = 0; section_number < kMaxSections; section_number++) { 178 for (unsigned section_number = 0; section_number < kMaxSections; section_number++) {
180 const auto& section = exefs_header.section[section_number]; 179 const auto& section = exefs_header.section[section_number];
181 180
@@ -187,7 +186,7 @@ ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>&
187 s64 section_offset = (section.offset + exefs_offset + sizeof(ExeFs_Header) + ncch_offset); 186 s64 section_offset = (section.offset + exefs_offset + sizeof(ExeFs_Header) + ncch_offset);
188 file.Seek(section_offset, SEEK_SET); 187 file.Seek(section_offset, SEEK_SET);
189 188
190 if (is_compressed) { 189 if (strcmp(section.name, ".code") == 0 && is_compressed) {
191 // Section is compressed, read compressed .code section... 190 // Section is compressed, read compressed .code section...
192 std::unique_ptr<u8[]> temp_buffer; 191 std::unique_ptr<u8[]> temp_buffer;
193 try { 192 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 @@
4 4
5#include "settings.h" 5#include "settings.h"
6 6
7#include "core/gdbstub/gdbstub.h"
8
9#include "video_core/video_core.h"
10
7namespace Settings { 11namespace Settings {
8 12
9Values values = {}; 13Values values = {};
10 14
15void Apply() {
16
17 GDBStub::SetServerPort(static_cast<u32>(values.gdbstub_port));
18 GDBStub::ToggleServer(values.use_gdbstub);
19
20 VideoCore::g_hw_renderer_enabled = values.use_hw_renderer;
21 VideoCore::g_shader_jit_enabled = values.use_shader_jit;
22
11} 23}
24
25} // 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 {
67 u16 gdbstub_port; 67 u16 gdbstub_port;
68} extern values; 68} extern values;
69 69
70void Apply();
71
70} 72}
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) {
140 immediate_attribute_id = 0; 140 immediate_attribute_id = 0;
141 141
142 Shader::UnitState<false> shader_unit; 142 Shader::UnitState<false> shader_unit;
143 Shader::Setup(shader_unit); 143 Shader::Setup();
144 144
145 if (g_debug_context) 145 if (g_debug_context)
146 g_debug_context->OnEvent(DebugContext::Event::VertexLoaded, static_cast<void*>(&immediate_input)); 146 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) {
249 const u16* index_address_16 = reinterpret_cast<const u16*>(index_address_8); 249 const u16* index_address_16 = reinterpret_cast<const u16*>(index_address_8);
250 bool index_u16 = index_info.format != 0; 250 bool index_u16 = index_info.format != 0;
251 251
252#if PICA_DUMP_GEOMETRY
253 DebugUtils::GeometryDumper geometry_dumper;
254 PrimitiveAssembler<DebugUtils::GeometryDumper::Vertex> dumping_primitive_assembler(regs.triangle_topology.Value());
255#endif
256 PrimitiveAssembler<Shader::OutputVertex>& primitive_assembler = g_state.primitive_assembler; 252 PrimitiveAssembler<Shader::OutputVertex>& primitive_assembler = g_state.primitive_assembler;
257 253
258 if (g_debug_context) { 254 if (g_debug_context) {
@@ -304,7 +300,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
304 vertex_cache_ids.fill(-1); 300 vertex_cache_ids.fill(-1);
305 301
306 Shader::UnitState<false> shader_unit; 302 Shader::UnitState<false> shader_unit;
307 Shader::Setup(shader_unit); 303 Shader::Setup();
308 304
309 for (unsigned int index = 0; index < regs.num_vertices; ++index) 305 for (unsigned int index = 0; index < regs.num_vertices; ++index)
310 { 306 {
@@ -388,17 +384,6 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
388 if (g_debug_context) 384 if (g_debug_context)
389 g_debug_context->OnEvent(DebugContext::Event::VertexLoaded, (void*)&input); 385 g_debug_context->OnEvent(DebugContext::Event::VertexLoaded, (void*)&input);
390 386
391#if PICA_DUMP_GEOMETRY
392 // NOTE: When dumping geometry, we simply assume that the first input attribute
393 // corresponds to the position for now.
394 DebugUtils::GeometryDumper::Vertex dumped_vertex = {
395 input.attr[0][0].ToFloat32(), input.attr[0][1].ToFloat32(), input.attr[0][2].ToFloat32()
396 };
397 using namespace std::placeholders;
398 dumping_primitive_assembler.SubmitVertex(dumped_vertex,
399 std::bind(&DebugUtils::GeometryDumper::AddTriangle,
400 &geometry_dumper, _1, _2, _3));
401#endif
402 // Send to vertex shader 387 // Send to vertex shader
403 output = Shader::Run(shader_unit, input, attribute_config.GetNumTotalAttributes()); 388 output = Shader::Run(shader_unit, input, attribute_config.GetNumTotalAttributes());
404 389
@@ -424,10 +409,6 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
424 range.second, range.first); 409 range.second, range.first);
425 } 410 }
426 411
427#if PICA_DUMP_GEOMETRY
428 geometry_dumper.Dump();
429#endif
430
431 break; 412 break;
432 } 413 }
433 414
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
85 85
86namespace DebugUtils { 86namespace DebugUtils {
87 87
88void GeometryDumper::AddTriangle(Vertex& v0, Vertex& v1, Vertex& v2) {
89 vertices.push_back(v0);
90 vertices.push_back(v1);
91 vertices.push_back(v2);
92
93 int num_vertices = (int)vertices.size();
94 faces.push_back({{ num_vertices-3, num_vertices-2, num_vertices-1 }});
95}
96
97void GeometryDumper::Dump() {
98 static int index = 0;
99 std::string filename = std::string("geometry_dump") + std::to_string(++index) + ".obj";
100
101 std::ofstream file(filename);
102
103 for (const auto& vertex : vertices) {
104 file << "v " << vertex.pos[0]
105 << " " << vertex.pos[1]
106 << " " << vertex.pos[2] << std::endl;
107 }
108
109 for (const Face& face : faces) {
110 file << "f " << 1+face.index[0]
111 << " " << 1+face.index[1]
112 << " " << 1+face.index[2] << std::endl;
113 }
114}
115
116
117void DumpShader(const std::string& filename, const Regs::ShaderConfig& config, const Shader::ShaderSetup& setup, const Regs::VSOutputAttributes* output_attributes) 88void DumpShader(const std::string& filename, const Regs::ShaderConfig& config, const Shader::ShaderSetup& setup, const Regs::VSOutputAttributes* output_attributes)
118{ 89{
119 struct StuffToWrite { 90 struct StuffToWrite {
@@ -315,7 +286,7 @@ void StartPicaTracing()
315 } 286 }
316 287
317 std::lock_guard<std::mutex> lock(pica_trace_mutex); 288 std::lock_guard<std::mutex> lock(pica_trace_mutex);
318 pica_trace = std::unique_ptr<PicaTrace>(new PicaTrace); 289 pica_trace = std::make_unique<PicaTrace>();
319 290
320 is_pica_tracing = true; 291 is_pica_tracing = true;
321} 292}
@@ -615,6 +586,21 @@ TextureInfo TextureInfo::FromPicaRegister(const Regs::TextureConfig& config,
615 return info; 586 return info;
616} 587}
617 588
589#ifdef HAVE_PNG
590// Adapter functions to libpng to write/flush to File::IOFile instances.
591static void WriteIOFile(png_structp png_ptr, png_bytep data, png_size_t length) {
592 auto* fp = static_cast<FileUtil::IOFile*>(png_get_io_ptr(png_ptr));
593 if (!fp->WriteBytes(data, length))
594 png_error(png_ptr, "Failed to write to output PNG file.");
595}
596
597static void FlushIOFile(png_structp png_ptr) {
598 auto* fp = static_cast<FileUtil::IOFile*>(png_get_io_ptr(png_ptr));
599 if (!fp->Flush())
600 png_error(png_ptr, "Failed to flush to output PNG file.");
601}
602#endif
603
618void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) { 604void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) {
619#ifndef HAVE_PNG 605#ifndef HAVE_PNG
620 return; 606 return;
@@ -658,7 +644,7 @@ void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) {
658 goto finalise; 644 goto finalise;
659 } 645 }
660 646
661 png_init_io(png_ptr, fp.GetHandle()); 647 png_set_write_fn(png_ptr, static_cast<void*>(&fp), WriteIOFile, FlushIOFile);
662 648
663 // Write header (8 bit color depth) 649 // Write header (8 bit color depth)
664 png_set_IHDR(png_ptr, info_ptr, texture_config.width, texture_config.height, 650 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
158 158
159namespace DebugUtils { 159namespace DebugUtils {
160 160
161#define PICA_DUMP_GEOMETRY 0
162#define PICA_DUMP_TEXTURES 0 161#define PICA_DUMP_TEXTURES 0
163#define PICA_LOG_TEV 0 162#define PICA_LOG_TEV 0
164 163
165// Simple utility class for dumping geometry data to an OBJ file
166class GeometryDumper {
167public:
168 struct Vertex {
169 std::array<float,3> pos;
170 };
171
172 void AddTriangle(Vertex& v0, Vertex& v1, Vertex& v2);
173
174 void Dump();
175
176private:
177 struct Face {
178 int index[3];
179 };
180
181 std::vector<Vertex> vertices;
182 std::vector<Face> faces;
183};
184
185void DumpShader(const std::string& filename, const Regs::ShaderConfig& config, 164void DumpShader(const std::string& filename, const Regs::ShaderConfig& config,
186 const Shader::ShaderSetup& setup, const Regs::VSOutputAttributes* output_attributes); 165 const Shader::ShaderSetup& setup, const Regs::VSOutputAttributes* output_attributes);
187 166
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 {
578 } 578 }
579 579
580 struct { 580 struct {
581 INSERT_PADDING_WORDS(0x6); 581 INSERT_PADDING_WORDS(0x3);
582
583 union {
584 BitField<0, 4, u32> allow_color_write; // 0 = disable, else enable
585 };
586
587 INSERT_PADDING_WORDS(0x1);
588
589 union {
590 BitField<0, 2, u32> allow_depth_stencil_write; // 0 = disable, else enable
591 };
582 592
583 DepthFormat depth_format; // TODO: Should be a BitField! 593 DepthFormat depth_format; // TODO: Should be a BitField!
584 BitField<16, 3, ColorFormat> color_format; 594 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
68// explicitly instantiate use cases 68// explicitly instantiate use cases
69template 69template
70struct PrimitiveAssembler<Shader::OutputVertex>; 70struct PrimitiveAssembler<Shader::OutputVertex>;
71template
72struct PrimitiveAssembler<DebugUtils::GeometryDumper::Vertex>;
73 71
74} // namespace 72} // 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,
809 809
810 auto UpdateStencil = [stencil_test, x, y, &old_stencil](Pica::Regs::StencilAction action) { 810 auto UpdateStencil = [stencil_test, x, y, &old_stencil](Pica::Regs::StencilAction action) {
811 u8 new_stencil = PerformStencilAction(action, old_stencil, stencil_test.reference_value); 811 u8 new_stencil = PerformStencilAction(action, old_stencil, stencil_test.reference_value);
812 SetStencil(x >> 4, y >> 4, (new_stencil & stencil_test.write_mask) | (old_stencil & ~stencil_test.write_mask)); 812 if (g_state.regs.framebuffer.allow_depth_stencil_write != 0)
813 SetStencil(x >> 4, y >> 4, (new_stencil & stencil_test.write_mask) | (old_stencil & ~stencil_test.write_mask));
813 }; 814 };
814 815
815 if (stencil_action_enable) { 816 if (stencil_action_enable) {
@@ -909,7 +910,7 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0,
909 } 910 }
910 } 911 }
911 912
912 if (output_merger.depth_write_enable) 913 if (regs.framebuffer.allow_depth_stencil_write != 0 && output_merger.depth_write_enable)
913 SetDepth(x >> 4, y >> 4, z); 914 SetDepth(x >> 4, y >> 4, z);
914 915
915 // The stencil depth_pass action is executed even if depth testing is disabled 916 // The stencil depth_pass action is executed even if depth testing is disabled
@@ -922,92 +923,72 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0,
922 if (output_merger.alphablend_enable) { 923 if (output_merger.alphablend_enable) {
923 auto params = output_merger.alpha_blending; 924 auto params = output_merger.alpha_blending;
924 925
925 auto LookupFactorRGB = [&](Regs::BlendFactor factor) -> Math::Vec3<u8> { 926 auto LookupFactor = [&](unsigned channel, Regs::BlendFactor factor) -> u8 {
927 DEBUG_ASSERT(channel < 4);
928
929 const Math::Vec4<u8> blend_const = {
930 static_cast<u8>(output_merger.blend_const.r),
931 static_cast<u8>(output_merger.blend_const.g),
932 static_cast<u8>(output_merger.blend_const.b),
933 static_cast<u8>(output_merger.blend_const.a)
934 };
935
926 switch (factor) { 936 switch (factor) {
927 case Regs::BlendFactor::Zero : 937 case Regs::BlendFactor::Zero:
928 return Math::Vec3<u8>(0, 0, 0); 938 return 0;
929 939
930 case Regs::BlendFactor::One : 940 case Regs::BlendFactor::One:
931 return Math::Vec3<u8>(255, 255, 255); 941 return 255;
932 942
933 case Regs::BlendFactor::SourceColor: 943 case Regs::BlendFactor::SourceColor:
934 return combiner_output.rgb(); 944 return combiner_output[channel];
935 945
936 case Regs::BlendFactor::OneMinusSourceColor: 946 case Regs::BlendFactor::OneMinusSourceColor:
937 return Math::Vec3<u8>(255 - combiner_output.r(), 255 - combiner_output.g(), 255 - combiner_output.b()); 947 return 255 - combiner_output[channel];
938 948
939 case Regs::BlendFactor::DestColor: 949 case Regs::BlendFactor::DestColor:
940 return dest.rgb(); 950 return dest[channel];
941 951
942 case Regs::BlendFactor::OneMinusDestColor: 952 case Regs::BlendFactor::OneMinusDestColor:
943 return Math::Vec3<u8>(255 - dest.r(), 255 - dest.g(), 255 - dest.b()); 953 return 255 - dest[channel];
944 954
945 case Regs::BlendFactor::SourceAlpha: 955 case Regs::BlendFactor::SourceAlpha:
946 return Math::Vec3<u8>(combiner_output.a(), combiner_output.a(), combiner_output.a()); 956 return combiner_output.a();
947 957
948 case Regs::BlendFactor::OneMinusSourceAlpha: 958 case Regs::BlendFactor::OneMinusSourceAlpha:
949 return Math::Vec3<u8>(255 - combiner_output.a(), 255 - combiner_output.a(), 255 - combiner_output.a()); 959 return 255 - combiner_output.a();
950 960
951 case Regs::BlendFactor::DestAlpha: 961 case Regs::BlendFactor::DestAlpha:
952 return Math::Vec3<u8>(dest.a(), dest.a(), dest.a()); 962 return dest.a();
953 963
954 case Regs::BlendFactor::OneMinusDestAlpha: 964 case Regs::BlendFactor::OneMinusDestAlpha:
955 return Math::Vec3<u8>(255 - dest.a(), 255 - dest.a(), 255 - dest.a()); 965 return 255 - dest.a();
956 966
957 case Regs::BlendFactor::ConstantColor: 967 case Regs::BlendFactor::ConstantColor:
958 return Math::Vec3<u8>(output_merger.blend_const.r, output_merger.blend_const.g, output_merger.blend_const.b); 968 return blend_const[channel];
959 969
960 case Regs::BlendFactor::OneMinusConstantColor: 970 case Regs::BlendFactor::OneMinusConstantColor:
961 return Math::Vec3<u8>(255 - output_merger.blend_const.r, 255 - output_merger.blend_const.g, 255 - output_merger.blend_const.b); 971 return 255 - blend_const[channel];
962 972
963 case Regs::BlendFactor::ConstantAlpha: 973 case Regs::BlendFactor::ConstantAlpha:
964 return Math::Vec3<u8>(output_merger.blend_const.a, output_merger.blend_const.a, output_merger.blend_const.a); 974 return blend_const.a();
965 975
966 case Regs::BlendFactor::OneMinusConstantAlpha: 976 case Regs::BlendFactor::OneMinusConstantAlpha:
967 return Math::Vec3<u8>(255 - output_merger.blend_const.a, 255 - output_merger.blend_const.a, 255 - output_merger.blend_const.a); 977 return 255 - blend_const.a();
968
969 default:
970 LOG_CRITICAL(HW_GPU, "Unknown color blend factor %x", factor);
971 UNIMPLEMENTED();
972 break;
973 }
974
975 return {};
976 };
977
978 auto LookupFactorA = [&](Regs::BlendFactor factor) -> u8 {
979 switch (factor) {
980 case Regs::BlendFactor::Zero:
981 return 0;
982
983 case Regs::BlendFactor::One:
984 return 255;
985
986 case Regs::BlendFactor::SourceAlpha:
987 return combiner_output.a();
988
989 case Regs::BlendFactor::OneMinusSourceAlpha:
990 return 255 - combiner_output.a();
991 978
992 case Regs::BlendFactor::DestAlpha: 979 case Regs::BlendFactor::SourceAlphaSaturate:
993 return dest.a(); 980 // Returns 1.0 for the alpha channel
994 981 if (channel == 3)
995 case Regs::BlendFactor::OneMinusDestAlpha: 982 return 255;
996 return 255 - dest.a(); 983 return std::min(combiner_output.a(), static_cast<u8>(255 - dest.a()));
997
998 case Regs::BlendFactor::ConstantAlpha:
999 return output_merger.blend_const.a;
1000
1001 case Regs::BlendFactor::OneMinusConstantAlpha:
1002 return 255 - output_merger.blend_const.a;
1003 984
1004 default: 985 default:
1005 LOG_CRITICAL(HW_GPU, "Unknown alpha blend factor %x", factor); 986 LOG_CRITICAL(HW_GPU, "Unknown blend factor %x", factor);
1006 UNIMPLEMENTED(); 987 UNIMPLEMENTED();
1007 break; 988 break;
1008 } 989 }
1009 990
1010 return {}; 991 return combiner_output[channel];
1011 }; 992 };
1012 993
1013 static auto EvaluateBlendEquation = [](const Math::Vec4<u8>& src, const Math::Vec4<u8>& srcfactor, 994 static auto EvaluateBlendEquation = [](const Math::Vec4<u8>& src, const Math::Vec4<u8>& srcfactor,
@@ -1059,10 +1040,15 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0,
1059 MathUtil::Clamp(result.a(), 0, 255)); 1040 MathUtil::Clamp(result.a(), 0, 255));
1060 }; 1041 };
1061 1042
1062 auto srcfactor = Math::MakeVec(LookupFactorRGB(params.factor_source_rgb), 1043 auto srcfactor = Math::MakeVec(LookupFactor(0, params.factor_source_rgb),
1063 LookupFactorA(params.factor_source_a)); 1044 LookupFactor(1, params.factor_source_rgb),
1064 auto dstfactor = Math::MakeVec(LookupFactorRGB(params.factor_dest_rgb), 1045 LookupFactor(2, params.factor_source_rgb),
1065 LookupFactorA(params.factor_dest_a)); 1046 LookupFactor(3, params.factor_source_a));
1047
1048 auto dstfactor = Math::MakeVec(LookupFactor(0, params.factor_dest_rgb),
1049 LookupFactor(1, params.factor_dest_rgb),
1050 LookupFactor(2, params.factor_dest_rgb),
1051 LookupFactor(3, params.factor_dest_a));
1066 1052
1067 blend_output = EvaluateBlendEquation(combiner_output, srcfactor, dest, dstfactor, params.blend_equation_rgb); 1053 blend_output = EvaluateBlendEquation(combiner_output, srcfactor, dest, dstfactor, params.blend_equation_rgb);
1068 blend_output.a() = EvaluateBlendEquation(combiner_output, srcfactor, dest, dstfactor, params.blend_equation_a).a(); 1054 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,
1133 output_merger.alpha_enable ? blend_output.a() : dest.a() 1119 output_merger.alpha_enable ? blend_output.a() : dest.a()
1134 }; 1120 };
1135 1121
1136 DrawPixel(x >> 4, y >> 4, result); 1122 if (regs.framebuffer.allow_color_write != 0)
1123 DrawPixel(x >> 4, y >> 4, result);
1137 } 1124 }
1138 } 1125 }
1139} 1126}
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 @@
4 4
5#include <memory> 5#include <memory>
6 6
7#include "common/make_unique.h"
8
9#include "core/settings.h" 7#include "core/settings.h"
10 8
11#include "video_core/renderer_base.h" 9#include "video_core/renderer_base.h"
@@ -19,9 +17,9 @@ void RendererBase::RefreshRasterizerSetting() {
19 opengl_rasterizer_active = hw_renderer_enabled; 17 opengl_rasterizer_active = hw_renderer_enabled;
20 18
21 if (hw_renderer_enabled) { 19 if (hw_renderer_enabled) {
22 rasterizer = Common::make_unique<RasterizerOpenGL>(); 20 rasterizer = std::make_unique<RasterizerOpenGL>();
23 } else { 21 } else {
24 rasterizer = Common::make_unique<VideoCore::SWRasterizer>(); 22 rasterizer = std::make_unique<VideoCore::SWRasterizer>();
25 } 23 }
26 rasterizer->InitObjects(); 24 rasterizer->InitObjects();
27 rasterizer->Reset(); 25 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 @@
9 9
10#include "common/color.h" 10#include "common/color.h"
11#include "common/file_util.h" 11#include "common/file_util.h"
12#include "common/make_unique.h"
13#include "common/math_util.h" 12#include "common/math_util.h"
14#include "common/microprofile.h" 13#include "common/microprofile.h"
15#include "common/profiler.h" 14#include "common/profiler.h"
@@ -140,8 +139,9 @@ void RasterizerOpenGL::InitObjects() {
140 } 139 }
141 state.Apply(); 140 state.Apply();
142 141
143 ASSERT_MSG(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE, 142 GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
144 "OpenGL rasterizer framebuffer setup failed, status %X", glCheckFramebufferStatus(GL_FRAMEBUFFER)); 143 ASSERT_MSG(status == GL_FRAMEBUFFER_COMPLETE,
144 "OpenGL rasterizer framebuffer setup failed, status %X", status);
145} 145}
146 146
147void RasterizerOpenGL::Reset() { 147void RasterizerOpenGL::Reset() {
@@ -153,6 +153,9 @@ void RasterizerOpenGL::Reset() {
153 SyncLogicOp(); 153 SyncLogicOp();
154 SyncStencilTest(); 154 SyncStencilTest();
155 SyncDepthTest(); 155 SyncDepthTest();
156 SyncColorWriteMask();
157 SyncStencilWriteMask();
158 SyncDepthWriteMask();
156 159
157 SetShader(); 160 SetShader();
158 161
@@ -268,15 +271,36 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) {
268 state.draw.shader_dirty = true; 271 state.draw.shader_dirty = true;
269 break; 272 break;
270 273
271 // Stencil test 274 // Sync GL stencil test + stencil write mask
275 // (Pica stencil test function register also contains a stencil write mask)
272 case PICA_REG_INDEX(output_merger.stencil_test.raw_func): 276 case PICA_REG_INDEX(output_merger.stencil_test.raw_func):
277 SyncStencilTest();
278 SyncStencilWriteMask();
279 break;
273 case PICA_REG_INDEX(output_merger.stencil_test.raw_op): 280 case PICA_REG_INDEX(output_merger.stencil_test.raw_op):
281 case PICA_REG_INDEX(framebuffer.depth_format):
274 SyncStencilTest(); 282 SyncStencilTest();
275 break; 283 break;
276 284
277 // Depth test 285 // Sync GL depth test + depth and color write mask
286 // (Pica depth test function register also contains a depth and color write mask)
278 case PICA_REG_INDEX(output_merger.depth_test_enable): 287 case PICA_REG_INDEX(output_merger.depth_test_enable):
279 SyncDepthTest(); 288 SyncDepthTest();
289 SyncDepthWriteMask();
290 SyncColorWriteMask();
291 break;
292
293 // Sync GL depth and stencil write mask
294 // (This is a dedicated combined depth / stencil write-enable register)
295 case PICA_REG_INDEX(framebuffer.allow_depth_stencil_write):
296 SyncDepthWriteMask();
297 SyncStencilWriteMask();
298 break;
299
300 // Sync GL color write mask
301 // (This is a dedicated color write-enable register)
302 case PICA_REG_INDEX(framebuffer.allow_color_write):
303 SyncColorWriteMask();
280 break; 304 break;
281 305
282 // Logic op 306 // Logic op
@@ -677,7 +701,7 @@ void RasterizerOpenGL::ReconfigureDepthTexture(DepthTextureInfo& texture, Pica::
677 701
678void RasterizerOpenGL::SetShader() { 702void RasterizerOpenGL::SetShader() {
679 PicaShaderConfig config = PicaShaderConfig::CurrentConfig(); 703 PicaShaderConfig config = PicaShaderConfig::CurrentConfig();
680 std::unique_ptr<PicaShader> shader = Common::make_unique<PicaShader>(); 704 std::unique_ptr<PicaShader> shader = std::make_unique<PicaShader>();
681 705
682 // Find (or generate) the GLSL shader for the current TEV state 706 // Find (or generate) the GLSL shader for the current TEV state
683 auto cached_shader = shader_cache.find(config); 707 auto cached_shader = shader_cache.find(config);
@@ -808,6 +832,10 @@ void RasterizerOpenGL::SyncFramebuffer() {
808 832
809 ReloadDepthBuffer(); 833 ReloadDepthBuffer();
810 } 834 }
835
836 GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
837 ASSERT_MSG(status == GL_FRAMEBUFFER_COMPLETE,
838 "OpenGL rasterizer framebuffer setup failed, status %X", status);
811} 839}
812 840
813void RasterizerOpenGL::SyncCullMode() { 841void RasterizerOpenGL::SyncCullMode() {
@@ -876,13 +904,39 @@ void RasterizerOpenGL::SyncLogicOp() {
876 state.logic_op = PicaToGL::LogicOp(Pica::g_state.regs.output_merger.logic_op); 904 state.logic_op = PicaToGL::LogicOp(Pica::g_state.regs.output_merger.logic_op);
877} 905}
878 906
907void RasterizerOpenGL::SyncColorWriteMask() {
908 const auto& regs = Pica::g_state.regs;
909
910 auto IsColorWriteEnabled = [&](u32 value) {
911 return (regs.framebuffer.allow_color_write != 0 && value != 0) ? GL_TRUE : GL_FALSE;
912 };
913
914 state.color_mask.red_enabled = IsColorWriteEnabled(regs.output_merger.red_enable);
915 state.color_mask.green_enabled = IsColorWriteEnabled(regs.output_merger.green_enable);
916 state.color_mask.blue_enabled = IsColorWriteEnabled(regs.output_merger.blue_enable);
917 state.color_mask.alpha_enabled = IsColorWriteEnabled(regs.output_merger.alpha_enable);
918}
919
920void RasterizerOpenGL::SyncStencilWriteMask() {
921 const auto& regs = Pica::g_state.regs;
922 state.stencil.write_mask = (regs.framebuffer.allow_depth_stencil_write != 0)
923 ? static_cast<GLuint>(regs.output_merger.stencil_test.write_mask)
924 : 0;
925}
926
927void RasterizerOpenGL::SyncDepthWriteMask() {
928 const auto& regs = Pica::g_state.regs;
929 state.depth.write_mask = (regs.framebuffer.allow_depth_stencil_write != 0 && regs.output_merger.depth_write_enable)
930 ? GL_TRUE
931 : GL_FALSE;
932}
933
879void RasterizerOpenGL::SyncStencilTest() { 934void RasterizerOpenGL::SyncStencilTest() {
880 const auto& regs = Pica::g_state.regs; 935 const auto& regs = Pica::g_state.regs;
881 state.stencil.test_enabled = regs.output_merger.stencil_test.enable && regs.framebuffer.depth_format == Pica::Regs::DepthFormat::D24S8; 936 state.stencil.test_enabled = regs.output_merger.stencil_test.enable && regs.framebuffer.depth_format == Pica::Regs::DepthFormat::D24S8;
882 state.stencil.test_func = PicaToGL::CompareFunc(regs.output_merger.stencil_test.func); 937 state.stencil.test_func = PicaToGL::CompareFunc(regs.output_merger.stencil_test.func);
883 state.stencil.test_ref = regs.output_merger.stencil_test.reference_value; 938 state.stencil.test_ref = regs.output_merger.stencil_test.reference_value;
884 state.stencil.test_mask = regs.output_merger.stencil_test.input_mask; 939 state.stencil.test_mask = regs.output_merger.stencil_test.input_mask;
885 state.stencil.write_mask = regs.output_merger.stencil_test.write_mask;
886 state.stencil.action_stencil_fail = PicaToGL::StencilOp(regs.output_merger.stencil_test.action_stencil_fail); 940 state.stencil.action_stencil_fail = PicaToGL::StencilOp(regs.output_merger.stencil_test.action_stencil_fail);
887 state.stencil.action_depth_fail = PicaToGL::StencilOp(regs.output_merger.stencil_test.action_depth_fail); 941 state.stencil.action_depth_fail = PicaToGL::StencilOp(regs.output_merger.stencil_test.action_depth_fail);
888 state.stencil.action_depth_pass = PicaToGL::StencilOp(regs.output_merger.stencil_test.action_depth_pass); 942 state.stencil.action_depth_pass = PicaToGL::StencilOp(regs.output_merger.stencil_test.action_depth_pass);
@@ -894,11 +948,6 @@ void RasterizerOpenGL::SyncDepthTest() {
894 regs.output_merger.depth_write_enable == 1; 948 regs.output_merger.depth_write_enable == 1;
895 state.depth.test_func = regs.output_merger.depth_test_enable == 1 ? 949 state.depth.test_func = regs.output_merger.depth_test_enable == 1 ?
896 PicaToGL::CompareFunc(regs.output_merger.depth_test_func) : GL_ALWAYS; 950 PicaToGL::CompareFunc(regs.output_merger.depth_test_func) : GL_ALWAYS;
897 state.color_mask.red_enabled = regs.output_merger.red_enable;
898 state.color_mask.green_enabled = regs.output_merger.green_enable;
899 state.color_mask.blue_enabled = regs.output_merger.blue_enable;
900 state.color_mask.alpha_enabled = regs.output_merger.alpha_enable;
901 state.depth.write_mask = regs.output_merger.depth_write_enable ? GL_TRUE : GL_FALSE;
902} 951}
903 952
904void RasterizerOpenGL::SyncCombinerColor() { 953void 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:
344 /// Syncs the logic op states to match the PICA register 344 /// Syncs the logic op states to match the PICA register
345 void SyncLogicOp(); 345 void SyncLogicOp();
346 346
347 /// Syncs the color write mask to match the PICA register state
348 void SyncColorWriteMask();
349
350 /// Syncs the stencil write mask to match the PICA register state
351 void SyncStencilWriteMask();
352
353 /// Syncs the depth write mask to match the PICA register state
354 void SyncDepthWriteMask();
355
347 /// Syncs the stencil test states to match the PICA register 356 /// Syncs the stencil test states to match the PICA register
348 void SyncStencilTest(); 357 void SyncStencilTest();
349 358
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 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <memory>
6
5#include "common/hash.h" 7#include "common/hash.h"
6#include "common/make_unique.h"
7#include "common/math_util.h" 8#include "common/math_util.h"
8#include "common/microprofile.h" 9#include "common/microprofile.h"
9#include "common/vector_math.h" 10#include "common/vector_math.h"
@@ -29,7 +30,7 @@ void RasterizerCacheOpenGL::LoadAndBindTexture(OpenGLState &state, unsigned text
29 } else { 30 } else {
30 MICROPROFILE_SCOPE(OpenGL_TextureUpload); 31 MICROPROFILE_SCOPE(OpenGL_TextureUpload);
31 32
32 std::unique_ptr<CachedTexture> new_texture = Common::make_unique<CachedTexture>(); 33 std::unique_ptr<CachedTexture> new_texture = std::make_unique<CachedTexture>();
33 34
34 new_texture->texture.Create(); 35 new_texture->texture.Create();
35 state.texture_units[texture_unit].texture_2d = new_texture->texture.handle; 36 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) {
22 }; 22 };
23 23
24 // Range check table for input 24 // Range check table for input
25 if (mode >= ARRAY_SIZE(filter_mode_table)) { 25 if (static_cast<size_t>(mode) >= ARRAY_SIZE(filter_mode_table)) {
26 LOG_CRITICAL(Render_OpenGL, "Unknown texture filtering mode %d", mode); 26 LOG_CRITICAL(Render_OpenGL, "Unknown texture filtering mode %d", mode);
27 UNREACHABLE(); 27 UNREACHABLE();
28 28
@@ -51,7 +51,7 @@ inline GLenum WrapMode(Pica::Regs::TextureConfig::WrapMode mode) {
51 }; 51 };
52 52
53 // Range check table for input 53 // Range check table for input
54 if (mode >= ARRAY_SIZE(wrap_mode_table)) { 54 if (static_cast<size_t>(mode) >= ARRAY_SIZE(wrap_mode_table)) {
55 LOG_CRITICAL(Render_OpenGL, "Unknown texture wrap mode %d", mode); 55 LOG_CRITICAL(Render_OpenGL, "Unknown texture wrap mode %d", mode);
56 UNREACHABLE(); 56 UNREACHABLE();
57 57
@@ -91,7 +91,7 @@ inline GLenum BlendFunc(Pica::Regs::BlendFactor factor) {
91 }; 91 };
92 92
93 // Range check table for input 93 // Range check table for input
94 if ((unsigned)factor >= ARRAY_SIZE(blend_func_table)) { 94 if (static_cast<size_t>(factor) >= ARRAY_SIZE(blend_func_table)) {
95 LOG_CRITICAL(Render_OpenGL, "Unknown blend factor %d", factor); 95 LOG_CRITICAL(Render_OpenGL, "Unknown blend factor %d", factor);
96 UNREACHABLE(); 96 UNREACHABLE();
97 97
@@ -122,7 +122,7 @@ inline GLenum LogicOp(Pica::Regs::LogicOp op) {
122 }; 122 };
123 123
124 // Range check table for input 124 // Range check table for input
125 if ((unsigned)op >= ARRAY_SIZE(logic_op_table)) { 125 if (static_cast<size_t>(op) >= ARRAY_SIZE(logic_op_table)) {
126 LOG_CRITICAL(Render_OpenGL, "Unknown logic op %d", op); 126 LOG_CRITICAL(Render_OpenGL, "Unknown logic op %d", op);
127 UNREACHABLE(); 127 UNREACHABLE();
128 128
@@ -145,7 +145,7 @@ inline GLenum CompareFunc(Pica::Regs::CompareFunc func) {
145 }; 145 };
146 146
147 // Range check table for input 147 // Range check table for input
148 if ((unsigned)func >= ARRAY_SIZE(compare_func_table)) { 148 if (static_cast<size_t>(func) >= ARRAY_SIZE(compare_func_table)) {
149 LOG_CRITICAL(Render_OpenGL, "Unknown compare function %d", func); 149 LOG_CRITICAL(Render_OpenGL, "Unknown compare function %d", func);
150 UNREACHABLE(); 150 UNREACHABLE();
151 151
@@ -168,7 +168,7 @@ inline GLenum StencilOp(Pica::Regs::StencilAction action) {
168 }; 168 };
169 169
170 // Range check table for input 170 // Range check table for input
171 if ((unsigned)action >= ARRAY_SIZE(stencil_op_table)) { 171 if (static_cast<size_t>(action) >= ARRAY_SIZE(stencil_op_table)) {
172 LOG_CRITICAL(Render_OpenGL, "Unknown stencil op %d", action); 172 LOG_CRITICAL(Render_OpenGL, "Unknown stencil op %d", action);
173 UNREACHABLE(); 173 UNREACHABLE();
174 174
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 @@
8#include <boost/range/algorithm/fill.hpp> 8#include <boost/range/algorithm/fill.hpp>
9 9
10#include "common/hash.h" 10#include "common/hash.h"
11#include "common/make_unique.h"
12#include "common/microprofile.h" 11#include "common/microprofile.h"
13#include "common/profiler.h" 12#include "common/profiler.h"
14 13
@@ -29,36 +28,24 @@ namespace Pica {
29namespace Shader { 28namespace Shader {
30 29
31#ifdef ARCHITECTURE_x86_64 30#ifdef ARCHITECTURE_x86_64
32static std::unordered_map<u64, CompiledShader*> shader_map; 31static std::unordered_map<u64, std::unique_ptr<JitShader>> shader_map;
33static JitCompiler jit; 32static const JitShader* jit_shader;
34static CompiledShader* jit_shader;
35
36static void ClearCache() {
37 shader_map.clear();
38 jit.Clear();
39 LOG_INFO(HW_GPU, "Shader JIT cache cleared");
40}
41#endif // ARCHITECTURE_x86_64 33#endif // ARCHITECTURE_x86_64
42 34
43void Setup(UnitState<false>& state) { 35void Setup() {
44#ifdef ARCHITECTURE_x86_64 36#ifdef ARCHITECTURE_x86_64
45 if (VideoCore::g_shader_jit_enabled) { 37 if (VideoCore::g_shader_jit_enabled) {
46 u64 cache_key = (Common::ComputeHash64(&g_state.vs.program_code, sizeof(g_state.vs.program_code)) ^ 38 u64 cache_key = (Common::ComputeHash64(&g_state.vs.program_code, sizeof(g_state.vs.program_code)) ^
47 Common::ComputeHash64(&g_state.vs.swizzle_data, sizeof(g_state.vs.swizzle_data)) ^ 39 Common::ComputeHash64(&g_state.vs.swizzle_data, sizeof(g_state.vs.swizzle_data)));
48 g_state.regs.vs.main_offset);
49 40
50 auto iter = shader_map.find(cache_key); 41 auto iter = shader_map.find(cache_key);
51 if (iter != shader_map.end()) { 42 if (iter != shader_map.end()) {
52 jit_shader = iter->second; 43 jit_shader = iter->second.get();
53 } else { 44 } else {
54 // Check if remaining JIT code space is enough for at least one more (massive) shader 45 auto shader = std::make_unique<JitShader>();
55 if (jit.GetSpaceLeft() < jit_shader_size) { 46 shader->Compile();
56 // If not, clear the cache of all previously compiled shaders 47 jit_shader = shader.get();
57 ClearCache(); 48 shader_map[cache_key] = std::move(shader);
58 }
59
60 jit_shader = jit.Compile();
61 shader_map.emplace(cache_key, jit_shader);
62 } 49 }
63 } 50 }
64#endif // ARCHITECTURE_x86_64 51#endif // ARCHITECTURE_x86_64
@@ -66,7 +53,7 @@ void Setup(UnitState<false>& state) {
66 53
67void Shutdown() { 54void Shutdown() {
68#ifdef ARCHITECTURE_x86_64 55#ifdef ARCHITECTURE_x86_64
69 ClearCache(); 56 shader_map.clear();
70#endif // ARCHITECTURE_x86_64 57#endif // ARCHITECTURE_x86_64
71} 58}
72 59
@@ -110,7 +97,7 @@ OutputVertex Run(UnitState<false>& state, const InputVertex& input, int num_attr
110 97
111#ifdef ARCHITECTURE_x86_64 98#ifdef ARCHITECTURE_x86_64
112 if (VideoCore::g_shader_jit_enabled) 99 if (VideoCore::g_shader_jit_enabled)
113 jit_shader(&state.registers); 100 jit_shader->Run(&state.registers, g_state.regs.vs.main_offset);
114 else 101 else
115 RunInterpreter(state); 102 RunInterpreter(state);
116#else 103#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 {
339/** 339/**
340 * Performs any shader unit setup that only needs to happen once per shader (as opposed to once per 340 * Performs any shader unit setup that only needs to happen once per shader (as opposed to once per
341 * vertex, which would happen within the `Run` function). 341 * vertex, which would happen within the `Run` function).
342 * @param state Shader unit state, must be setup per shader and per shader unit
343 */ 342 */
344void Setup(UnitState<false>& state); 343void Setup();
345 344
346/// Performs any cleanup when the emulator is shutdown 345/// Performs any cleanup when the emulator is shutdown
347void Shutdown(); 346void 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 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
5#include <smmintrin.h> 6#include <smmintrin.h>
6 7
7#include "common/x64/abi.h" 8#include "common/x64/abi.h"
@@ -19,73 +20,73 @@ namespace Shader {
19 20
20using namespace Gen; 21using namespace Gen;
21 22
22typedef void (JitCompiler::*JitFunction)(Instruction instr); 23typedef void (JitShader::*JitFunction)(Instruction instr);
23 24
24const JitFunction instr_table[64] = { 25const JitFunction instr_table[64] = {
25 &JitCompiler::Compile_ADD, // add 26 &JitShader::Compile_ADD, // add
26 &JitCompiler::Compile_DP3, // dp3 27 &JitShader::Compile_DP3, // dp3
27 &JitCompiler::Compile_DP4, // dp4 28 &JitShader::Compile_DP4, // dp4
28 &JitCompiler::Compile_DPH, // dph 29 &JitShader::Compile_DPH, // dph
29 nullptr, // unknown 30 nullptr, // unknown
30 &JitCompiler::Compile_EX2, // ex2 31 &JitShader::Compile_EX2, // ex2
31 &JitCompiler::Compile_LG2, // lg2 32 &JitShader::Compile_LG2, // lg2
32 nullptr, // unknown 33 nullptr, // unknown
33 &JitCompiler::Compile_MUL, // mul 34 &JitShader::Compile_MUL, // mul
34 &JitCompiler::Compile_SGE, // sge 35 &JitShader::Compile_SGE, // sge
35 &JitCompiler::Compile_SLT, // slt 36 &JitShader::Compile_SLT, // slt
36 &JitCompiler::Compile_FLR, // flr 37 &JitShader::Compile_FLR, // flr
37 &JitCompiler::Compile_MAX, // max 38 &JitShader::Compile_MAX, // max
38 &JitCompiler::Compile_MIN, // min 39 &JitShader::Compile_MIN, // min
39 &JitCompiler::Compile_RCP, // rcp 40 &JitShader::Compile_RCP, // rcp
40 &JitCompiler::Compile_RSQ, // rsq 41 &JitShader::Compile_RSQ, // rsq
41 nullptr, // unknown 42 nullptr, // unknown
42 nullptr, // unknown 43 nullptr, // unknown
43 &JitCompiler::Compile_MOVA, // mova 44 &JitShader::Compile_MOVA, // mova
44 &JitCompiler::Compile_MOV, // mov 45 &JitShader::Compile_MOV, // mov
45 nullptr, // unknown 46 nullptr, // unknown
46 nullptr, // unknown 47 nullptr, // unknown
47 nullptr, // unknown 48 nullptr, // unknown
48 nullptr, // unknown 49 nullptr, // unknown
49 &JitCompiler::Compile_DPH, // dphi 50 &JitShader::Compile_DPH, // dphi
50 nullptr, // unknown 51 nullptr, // unknown
51 &JitCompiler::Compile_SGE, // sgei 52 &JitShader::Compile_SGE, // sgei
52 &JitCompiler::Compile_SLT, // slti 53 &JitShader::Compile_SLT, // slti
53 nullptr, // unknown 54 nullptr, // unknown
54 nullptr, // unknown 55 nullptr, // unknown
55 nullptr, // unknown 56 nullptr, // unknown
56 nullptr, // unknown 57 nullptr, // unknown
57 nullptr, // unknown 58 nullptr, // unknown
58 &JitCompiler::Compile_NOP, // nop 59 &JitShader::Compile_NOP, // nop
59 &JitCompiler::Compile_END, // end 60 &JitShader::Compile_END, // end
60 nullptr, // break 61 nullptr, // break
61 &JitCompiler::Compile_CALL, // call 62 &JitShader::Compile_CALL, // call
62 &JitCompiler::Compile_CALLC, // callc 63 &JitShader::Compile_CALLC, // callc
63 &JitCompiler::Compile_CALLU, // callu 64 &JitShader::Compile_CALLU, // callu
64 &JitCompiler::Compile_IF, // ifu 65 &JitShader::Compile_IF, // ifu
65 &JitCompiler::Compile_IF, // ifc 66 &JitShader::Compile_IF, // ifc
66 &JitCompiler::Compile_LOOP, // loop 67 &JitShader::Compile_LOOP, // loop
67 nullptr, // emit 68 nullptr, // emit
68 nullptr, // sete 69 nullptr, // sete
69 &JitCompiler::Compile_JMP, // jmpc 70 &JitShader::Compile_JMP, // jmpc
70 &JitCompiler::Compile_JMP, // jmpu 71 &JitShader::Compile_JMP, // jmpu
71 &JitCompiler::Compile_CMP, // cmp 72 &JitShader::Compile_CMP, // cmp
72 &JitCompiler::Compile_CMP, // cmp 73 &JitShader::Compile_CMP, // cmp
73 &JitCompiler::Compile_MAD, // madi 74 &JitShader::Compile_MAD, // madi
74 &JitCompiler::Compile_MAD, // madi 75 &JitShader::Compile_MAD, // madi
75 &JitCompiler::Compile_MAD, // madi 76 &JitShader::Compile_MAD, // madi
76 &JitCompiler::Compile_MAD, // madi 77 &JitShader::Compile_MAD, // madi
77 &JitCompiler::Compile_MAD, // madi 78 &JitShader::Compile_MAD, // madi
78 &JitCompiler::Compile_MAD, // madi 79 &JitShader::Compile_MAD, // madi
79 &JitCompiler::Compile_MAD, // madi 80 &JitShader::Compile_MAD, // madi
80 &JitCompiler::Compile_MAD, // madi 81 &JitShader::Compile_MAD, // madi
81 &JitCompiler::Compile_MAD, // mad 82 &JitShader::Compile_MAD, // mad
82 &JitCompiler::Compile_MAD, // mad 83 &JitShader::Compile_MAD, // mad
83 &JitCompiler::Compile_MAD, // mad 84 &JitShader::Compile_MAD, // mad
84 &JitCompiler::Compile_MAD, // mad 85 &JitShader::Compile_MAD, // mad
85 &JitCompiler::Compile_MAD, // mad 86 &JitShader::Compile_MAD, // mad
86 &JitCompiler::Compile_MAD, // mad 87 &JitShader::Compile_MAD, // mad
87 &JitCompiler::Compile_MAD, // mad 88 &JitShader::Compile_MAD, // mad
88 &JitCompiler::Compile_MAD, // mad 89 &JitShader::Compile_MAD, // mad
89}; 90};
90 91
91// The following is used to alias some commonly used registers. Generally, RAX-RDX and XMM0-XMM3 can 92// 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;
138static const u8 NO_DEST_REG_MASK = 0xf; 139static const u8 NO_DEST_REG_MASK = 0xf;
139 140
140/** 141/**
142 * Get the vertex shader instruction for a given offset in the current shader program
143 * @param offset Offset in the current shader program of the instruction
144 * @return Instruction at the specified offset
145 */
146static Instruction GetVertexShaderInstruction(size_t offset) {
147 return { g_state.vs.program_code[offset] };
148}
149
150static void LogCritical(const char* msg) {
151 LOG_CRITICAL(HW_GPU, msg);
152}
153
154void JitShader::Compile_Assert(bool condition, const char* msg) {
155 if (!condition) {
156 ABI_CallFunctionP(reinterpret_cast<const void*>(LogCritical), const_cast<char*>(msg));
157 }
158}
159
160/**
141 * Loads and swizzles a source register into the specified XMM register. 161 * Loads and swizzles a source register into the specified XMM register.
142 * @param instr VS instruction, used for determining how to load the source register 162 * @param instr VS instruction, used for determining how to load the source register
143 * @param src_num Number indicating which source register to load (1 = src1, 2 = src2, 3 = src3) 163 * @param src_num Number indicating which source register to load (1 = src1, 2 = src2, 3 = src3)
144 * @param src_reg SourceRegister object corresponding to the source register to load 164 * @param src_reg SourceRegister object corresponding to the source register to load
145 * @param dest Destination XMM register to store the loaded, swizzled source register 165 * @param dest Destination XMM register to store the loaded, swizzled source register
146 */ 166 */
147void JitCompiler::Compile_SwizzleSrc(Instruction instr, unsigned src_num, SourceRegister src_reg, X64Reg dest) { 167void JitShader::Compile_SwizzleSrc(Instruction instr, unsigned src_num, SourceRegister src_reg, X64Reg dest) {
148 X64Reg src_ptr; 168 X64Reg src_ptr;
149 size_t src_offset; 169 size_t src_offset;
150 170
@@ -216,7 +236,7 @@ void JitCompiler::Compile_SwizzleSrc(Instruction instr, unsigned src_num, Source
216 } 236 }
217} 237}
218 238
219void JitCompiler::Compile_DestEnable(Instruction instr,X64Reg src) { 239void JitShader::Compile_DestEnable(Instruction instr,X64Reg src) {
220 DestRegister dest; 240 DestRegister dest;
221 unsigned operand_desc_id; 241 unsigned operand_desc_id;
222 if (instr.opcode.Value().EffectiveOpCode() == OpCode::Id::MAD || 242 if (instr.opcode.Value().EffectiveOpCode() == OpCode::Id::MAD ||
@@ -263,7 +283,7 @@ void JitCompiler::Compile_DestEnable(Instruction instr,X64Reg src) {
263 } 283 }
264} 284}
265 285
266void JitCompiler::Compile_SanitizedMul(Gen::X64Reg src1, Gen::X64Reg src2, Gen::X64Reg scratch) { 286void JitShader::Compile_SanitizedMul(Gen::X64Reg src1, Gen::X64Reg src2, Gen::X64Reg scratch) {
267 MOVAPS(scratch, R(src1)); 287 MOVAPS(scratch, R(src1));
268 CMPPS(scratch, R(src2), CMP_ORD); 288 CMPPS(scratch, R(src2), CMP_ORD);
269 289
@@ -276,7 +296,7 @@ void JitCompiler::Compile_SanitizedMul(Gen::X64Reg src1, Gen::X64Reg src2, Gen::
276 ANDPS(src1, R(scratch)); 296 ANDPS(src1, R(scratch));
277} 297}
278 298
279void JitCompiler::Compile_EvaluateCondition(Instruction instr) { 299void JitShader::Compile_EvaluateCondition(Instruction instr) {
280 // Note: NXOR is used below to check for equality 300 // Note: NXOR is used below to check for equality
281 switch (instr.flow_control.op) { 301 switch (instr.flow_control.op) {
282 case Instruction::FlowControlType::Or: 302 case Instruction::FlowControlType::Or:
@@ -307,23 +327,23 @@ void JitCompiler::Compile_EvaluateCondition(Instruction instr) {
307 } 327 }
308} 328}
309 329
310void JitCompiler::Compile_UniformCondition(Instruction instr) { 330void JitShader::Compile_UniformCondition(Instruction instr) {
311 int offset = offsetof(decltype(g_state.vs.uniforms), b) + (instr.flow_control.bool_uniform_id * sizeof(bool)); 331 int offset = offsetof(decltype(g_state.vs.uniforms), b) + (instr.flow_control.bool_uniform_id * sizeof(bool));
312 CMP(sizeof(bool) * 8, MDisp(UNIFORMS, offset), Imm8(0)); 332 CMP(sizeof(bool) * 8, MDisp(UNIFORMS, offset), Imm8(0));
313} 333}
314 334
315BitSet32 JitCompiler::PersistentCallerSavedRegs() { 335BitSet32 JitShader::PersistentCallerSavedRegs() {
316 return persistent_regs & ABI_ALL_CALLER_SAVED; 336 return persistent_regs & ABI_ALL_CALLER_SAVED;
317} 337}
318 338
319void JitCompiler::Compile_ADD(Instruction instr) { 339void JitShader::Compile_ADD(Instruction instr) {
320 Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1); 340 Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
321 Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2); 341 Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2);
322 ADDPS(SRC1, R(SRC2)); 342 ADDPS(SRC1, R(SRC2));
323 Compile_DestEnable(instr, SRC1); 343 Compile_DestEnable(instr, SRC1);
324} 344}
325 345
326void JitCompiler::Compile_DP3(Instruction instr) { 346void JitShader::Compile_DP3(Instruction instr) {
327 Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1); 347 Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
328 Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2); 348 Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2);
329 349
@@ -342,7 +362,7 @@ void JitCompiler::Compile_DP3(Instruction instr) {
342 Compile_DestEnable(instr, SRC1); 362 Compile_DestEnable(instr, SRC1);
343} 363}
344 364
345void JitCompiler::Compile_DP4(Instruction instr) { 365void JitShader::Compile_DP4(Instruction instr) {
346 Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1); 366 Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
347 Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2); 367 Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2);
348 368
@@ -359,7 +379,7 @@ void JitCompiler::Compile_DP4(Instruction instr) {
359 Compile_DestEnable(instr, SRC1); 379 Compile_DestEnable(instr, SRC1);
360} 380}
361 381
362void JitCompiler::Compile_DPH(Instruction instr) { 382void JitShader::Compile_DPH(Instruction instr) {
363 if (instr.opcode.Value().EffectiveOpCode() == OpCode::Id::DPHI) { 383 if (instr.opcode.Value().EffectiveOpCode() == OpCode::Id::DPHI) {
364 Compile_SwizzleSrc(instr, 1, instr.common.src1i, SRC1); 384 Compile_SwizzleSrc(instr, 1, instr.common.src1i, SRC1);
365 Compile_SwizzleSrc(instr, 2, instr.common.src2i, SRC2); 385 Compile_SwizzleSrc(instr, 2, instr.common.src2i, SRC2);
@@ -391,7 +411,7 @@ void JitCompiler::Compile_DPH(Instruction instr) {
391 Compile_DestEnable(instr, SRC1); 411 Compile_DestEnable(instr, SRC1);
392} 412}
393 413
394void JitCompiler::Compile_EX2(Instruction instr) { 414void JitShader::Compile_EX2(Instruction instr) {
395 Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1); 415 Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
396 MOVSS(XMM0, R(SRC1)); 416 MOVSS(XMM0, R(SRC1));
397 417
@@ -404,7 +424,7 @@ void JitCompiler::Compile_EX2(Instruction instr) {
404 Compile_DestEnable(instr, SRC1); 424 Compile_DestEnable(instr, SRC1);
405} 425}
406 426
407void JitCompiler::Compile_LG2(Instruction instr) { 427void JitShader::Compile_LG2(Instruction instr) {
408 Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1); 428 Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
409 MOVSS(XMM0, R(SRC1)); 429 MOVSS(XMM0, R(SRC1));
410 430
@@ -417,14 +437,14 @@ void JitCompiler::Compile_LG2(Instruction instr) {
417 Compile_DestEnable(instr, SRC1); 437 Compile_DestEnable(instr, SRC1);
418} 438}
419 439
420void JitCompiler::Compile_MUL(Instruction instr) { 440void JitShader::Compile_MUL(Instruction instr) {
421 Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1); 441 Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
422 Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2); 442 Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2);
423 Compile_SanitizedMul(SRC1, SRC2, SCRATCH); 443 Compile_SanitizedMul(SRC1, SRC2, SCRATCH);
424 Compile_DestEnable(instr, SRC1); 444 Compile_DestEnable(instr, SRC1);
425} 445}
426 446
427void JitCompiler::Compile_SGE(Instruction instr) { 447void JitShader::Compile_SGE(Instruction instr) {
428 if (instr.opcode.Value().EffectiveOpCode() == OpCode::Id::SGEI) { 448 if (instr.opcode.Value().EffectiveOpCode() == OpCode::Id::SGEI) {
429 Compile_SwizzleSrc(instr, 1, instr.common.src1i, SRC1); 449 Compile_SwizzleSrc(instr, 1, instr.common.src1i, SRC1);
430 Compile_SwizzleSrc(instr, 2, instr.common.src2i, SRC2); 450 Compile_SwizzleSrc(instr, 2, instr.common.src2i, SRC2);
@@ -439,7 +459,7 @@ void JitCompiler::Compile_SGE(Instruction instr) {
439 Compile_DestEnable(instr, SRC2); 459 Compile_DestEnable(instr, SRC2);
440} 460}
441 461
442void JitCompiler::Compile_SLT(Instruction instr) { 462void JitShader::Compile_SLT(Instruction instr) {
443 if (instr.opcode.Value().EffectiveOpCode() == OpCode::Id::SLTI) { 463 if (instr.opcode.Value().EffectiveOpCode() == OpCode::Id::SLTI) {
444 Compile_SwizzleSrc(instr, 1, instr.common.src1i, SRC1); 464 Compile_SwizzleSrc(instr, 1, instr.common.src1i, SRC1);
445 Compile_SwizzleSrc(instr, 2, instr.common.src2i, SRC2); 465 Compile_SwizzleSrc(instr, 2, instr.common.src2i, SRC2);
@@ -454,7 +474,7 @@ void JitCompiler::Compile_SLT(Instruction instr) {
454 Compile_DestEnable(instr, SRC1); 474 Compile_DestEnable(instr, SRC1);
455} 475}
456 476
457void JitCompiler::Compile_FLR(Instruction instr) { 477void JitShader::Compile_FLR(Instruction instr) {
458 Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1); 478 Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
459 479
460 if (Common::GetCPUCaps().sse4_1) { 480 if (Common::GetCPUCaps().sse4_1) {
@@ -467,7 +487,7 @@ void JitCompiler::Compile_FLR(Instruction instr) {
467 Compile_DestEnable(instr, SRC1); 487 Compile_DestEnable(instr, SRC1);
468} 488}
469 489
470void JitCompiler::Compile_MAX(Instruction instr) { 490void JitShader::Compile_MAX(Instruction instr) {
471 Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1); 491 Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
472 Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2); 492 Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2);
473 // SSE semantics match PICA200 ones: In case of NaN, SRC2 is returned. 493 // SSE semantics match PICA200 ones: In case of NaN, SRC2 is returned.
@@ -475,7 +495,7 @@ void JitCompiler::Compile_MAX(Instruction instr) {
475 Compile_DestEnable(instr, SRC1); 495 Compile_DestEnable(instr, SRC1);
476} 496}
477 497
478void JitCompiler::Compile_MIN(Instruction instr) { 498void JitShader::Compile_MIN(Instruction instr) {
479 Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1); 499 Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
480 Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2); 500 Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2);
481 // SSE semantics match PICA200 ones: In case of NaN, SRC2 is returned. 501 // SSE semantics match PICA200 ones: In case of NaN, SRC2 is returned.
@@ -483,7 +503,7 @@ void JitCompiler::Compile_MIN(Instruction instr) {
483 Compile_DestEnable(instr, SRC1); 503 Compile_DestEnable(instr, SRC1);
484} 504}
485 505
486void JitCompiler::Compile_MOVA(Instruction instr) { 506void JitShader::Compile_MOVA(Instruction instr) {
487 SwizzlePattern swiz = { g_state.vs.swizzle_data[instr.common.operand_desc_id] }; 507 SwizzlePattern swiz = { g_state.vs.swizzle_data[instr.common.operand_desc_id] };
488 508
489 if (!swiz.DestComponentEnabled(0) && !swiz.DestComponentEnabled(1)) { 509 if (!swiz.DestComponentEnabled(0) && !swiz.DestComponentEnabled(1)) {
@@ -528,12 +548,12 @@ void JitCompiler::Compile_MOVA(Instruction instr) {
528 } 548 }
529} 549}
530 550
531void JitCompiler::Compile_MOV(Instruction instr) { 551void JitShader::Compile_MOV(Instruction instr) {
532 Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1); 552 Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
533 Compile_DestEnable(instr, SRC1); 553 Compile_DestEnable(instr, SRC1);
534} 554}
535 555
536void JitCompiler::Compile_RCP(Instruction instr) { 556void JitShader::Compile_RCP(Instruction instr) {
537 Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1); 557 Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
538 558
539 // TODO(bunnei): RCPSS is a pretty rough approximation, this might cause problems if Pica 559 // TODO(bunnei): RCPSS is a pretty rough approximation, this might cause problems if Pica
@@ -544,7 +564,7 @@ void JitCompiler::Compile_RCP(Instruction instr) {
544 Compile_DestEnable(instr, SRC1); 564 Compile_DestEnable(instr, SRC1);
545} 565}
546 566
547void JitCompiler::Compile_RSQ(Instruction instr) { 567void JitShader::Compile_RSQ(Instruction instr) {
548 Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1); 568 Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
549 569
550 // TODO(bunnei): RSQRTSS is a pretty rough approximation, this might cause problems if Pica 570 // TODO(bunnei): RSQRTSS is a pretty rough approximation, this might cause problems if Pica
@@ -555,36 +575,41 @@ void JitCompiler::Compile_RSQ(Instruction instr) {
555 Compile_DestEnable(instr, SRC1); 575 Compile_DestEnable(instr, SRC1);
556} 576}
557 577
558void JitCompiler::Compile_NOP(Instruction instr) { 578void JitShader::Compile_NOP(Instruction instr) {
559} 579}
560 580
561void JitCompiler::Compile_END(Instruction instr) { 581void JitShader::Compile_END(Instruction instr) {
562 ABI_PopRegistersAndAdjustStack(ABI_ALL_CALLEE_SAVED, 8); 582 ABI_PopRegistersAndAdjustStack(ABI_ALL_CALLEE_SAVED, 8);
563 RET(); 583 RET();
564} 584}
565 585
566void JitCompiler::Compile_CALL(Instruction instr) { 586void JitShader::Compile_CALL(Instruction instr) {
567 unsigned offset = instr.flow_control.dest_offset; 587 // Push offset of the return
568 while (offset < (instr.flow_control.dest_offset + instr.flow_control.num_instructions)) { 588 PUSH(64, Imm32(instr.flow_control.dest_offset + instr.flow_control.num_instructions));
569 Compile_NextInstr(&offset); 589
570 } 590 // Call the subroutine
591 FixupBranch b = CALL();
592 fixup_branches.push_back({ b, instr.flow_control.dest_offset });
593
594 // Skip over the return offset that's on the stack
595 ADD(64, R(RSP), Imm32(8));
571} 596}
572 597
573void JitCompiler::Compile_CALLC(Instruction instr) { 598void JitShader::Compile_CALLC(Instruction instr) {
574 Compile_EvaluateCondition(instr); 599 Compile_EvaluateCondition(instr);
575 FixupBranch b = J_CC(CC_Z, true); 600 FixupBranch b = J_CC(CC_Z, true);
576 Compile_CALL(instr); 601 Compile_CALL(instr);
577 SetJumpTarget(b); 602 SetJumpTarget(b);
578} 603}
579 604
580void JitCompiler::Compile_CALLU(Instruction instr) { 605void JitShader::Compile_CALLU(Instruction instr) {
581 Compile_UniformCondition(instr); 606 Compile_UniformCondition(instr);
582 FixupBranch b = J_CC(CC_Z, true); 607 FixupBranch b = J_CC(CC_Z, true);
583 Compile_CALL(instr); 608 Compile_CALL(instr);
584 SetJumpTarget(b); 609 SetJumpTarget(b);
585} 610}
586 611
587void JitCompiler::Compile_CMP(Instruction instr) { 612void JitShader::Compile_CMP(Instruction instr) {
588 using Op = Instruction::Common::CompareOpType::Op; 613 using Op = Instruction::Common::CompareOpType::Op;
589 Op op_x = instr.common.compare_op.x; 614 Op op_x = instr.common.compare_op.x;
590 Op op_y = instr.common.compare_op.y; 615 Op op_y = instr.common.compare_op.y;
@@ -627,7 +652,7 @@ void JitCompiler::Compile_CMP(Instruction instr) {
627 SHR(64, R(COND1), Imm8(63)); 652 SHR(64, R(COND1), Imm8(63));
628} 653}
629 654
630void JitCompiler::Compile_MAD(Instruction instr) { 655void JitShader::Compile_MAD(Instruction instr) {
631 Compile_SwizzleSrc(instr, 1, instr.mad.src1, SRC1); 656 Compile_SwizzleSrc(instr, 1, instr.mad.src1, SRC1);
632 657
633 if (instr.opcode.Value().EffectiveOpCode() == OpCode::Id::MADI) { 658 if (instr.opcode.Value().EffectiveOpCode() == OpCode::Id::MADI) {
@@ -644,9 +669,8 @@ void JitCompiler::Compile_MAD(Instruction instr) {
644 Compile_DestEnable(instr, SRC1); 669 Compile_DestEnable(instr, SRC1);
645} 670}
646 671
647void JitCompiler::Compile_IF(Instruction instr) { 672void JitShader::Compile_IF(Instruction instr) {
648 ASSERT_MSG(instr.flow_control.dest_offset > *offset_ptr, "Backwards if-statements (%d -> %d) not supported", 673 Compile_Assert(instr.flow_control.dest_offset >= program_counter, "Backwards if-statements not supported");
649 *offset_ptr, instr.flow_control.dest_offset.Value());
650 674
651 // Evaluate the "IF" condition 675 // Evaluate the "IF" condition
652 if (instr.opcode.Value() == OpCode::Id::IFU) { 676 if (instr.opcode.Value() == OpCode::Id::IFU) {
@@ -676,10 +700,9 @@ void JitCompiler::Compile_IF(Instruction instr) {
676 SetJumpTarget(b2); 700 SetJumpTarget(b2);
677} 701}
678 702
679void JitCompiler::Compile_LOOP(Instruction instr) { 703void JitShader::Compile_LOOP(Instruction instr) {
680 ASSERT_MSG(instr.flow_control.dest_offset > *offset_ptr, "Backwards loops (%d -> %d) not supported", 704 Compile_Assert(instr.flow_control.dest_offset >= program_counter, "Backwards loops not supported");
681 *offset_ptr, instr.flow_control.dest_offset.Value()); 705 Compile_Assert(!looping, "Nested loops not supported");
682 ASSERT_MSG(!looping, "Nested loops not supported");
683 706
684 looping = true; 707 looping = true;
685 708
@@ -705,10 +728,7 @@ void JitCompiler::Compile_LOOP(Instruction instr) {
705 looping = false; 728 looping = false;
706} 729}
707 730
708void JitCompiler::Compile_JMP(Instruction instr) { 731void JitShader::Compile_JMP(Instruction instr) {
709 ASSERT_MSG(instr.flow_control.dest_offset > *offset_ptr, "Backwards jumps (%d -> %d) not supported",
710 *offset_ptr, instr.flow_control.dest_offset.Value());
711
712 if (instr.opcode.Value() == OpCode::Id::JMPC) 732 if (instr.opcode.Value() == OpCode::Id::JMPC)
713 Compile_EvaluateCondition(instr); 733 Compile_EvaluateCondition(instr);
714 else if (instr.opcode.Value() == OpCode::Id::JMPU) 734 else if (instr.opcode.Value() == OpCode::Id::JMPU)
@@ -718,30 +738,38 @@ void JitCompiler::Compile_JMP(Instruction instr) {
718 738
719 bool inverted_condition = (instr.opcode.Value() == OpCode::Id::JMPU) && 739 bool inverted_condition = (instr.opcode.Value() == OpCode::Id::JMPU) &&
720 (instr.flow_control.num_instructions & 1); 740 (instr.flow_control.num_instructions & 1);
741
721 FixupBranch b = J_CC(inverted_condition ? CC_Z : CC_NZ, true); 742 FixupBranch b = J_CC(inverted_condition ? CC_Z : CC_NZ, true);
743 fixup_branches.push_back({ b, instr.flow_control.dest_offset });
744}
722 745
723 Compile_Block(instr.flow_control.dest_offset); 746void JitShader::Compile_Block(unsigned end) {
747 while (program_counter < end) {
748 Compile_NextInstr();
749 }
750}
751
752void JitShader::Compile_Return() {
753 // Peek return offset on the stack and check if we're at that offset
754 MOV(64, R(RAX), MDisp(RSP, 8));
755 CMP(32, R(RAX), Imm32(program_counter));
724 756
757 // If so, jump back to before CALL
758 FixupBranch b = J_CC(CC_NZ, true);
759 RET();
725 SetJumpTarget(b); 760 SetJumpTarget(b);
726} 761}
727 762
728void JitCompiler::Compile_Block(unsigned end) { 763void JitShader::Compile_NextInstr() {
729 // Save current offset pointer 764 if (std::binary_search(return_offsets.begin(), return_offsets.end(), program_counter)) {
730 unsigned* prev_offset_ptr = offset_ptr; 765 Compile_Return();
731 unsigned offset = *prev_offset_ptr; 766 }
732 767
733 while (offset < end) 768 ASSERT_MSG(code_ptr[program_counter] == nullptr, "Tried to compile already compiled shader location!");
734 Compile_NextInstr(&offset); 769 code_ptr[program_counter] = GetCodePtr();
735 770
736 // Restore current offset pointer 771 Instruction instr = GetVertexShaderInstruction(program_counter++);
737 offset_ptr = prev_offset_ptr;
738 *offset_ptr = offset;
739}
740 772
741void JitCompiler::Compile_NextInstr(unsigned* offset) {
742 offset_ptr = offset;
743
744 Instruction instr = *(Instruction*)&g_state.vs.program_code[(*offset_ptr)++];
745 OpCode::Id opcode = instr.opcode.Value(); 773 OpCode::Id opcode = instr.opcode.Value();
746 auto instr_func = instr_table[static_cast<unsigned>(opcode)]; 774 auto instr_func = instr_table[static_cast<unsigned>(opcode)];
747 775
@@ -755,9 +783,35 @@ void JitCompiler::Compile_NextInstr(unsigned* offset) {
755 } 783 }
756} 784}
757 785
758CompiledShader* JitCompiler::Compile() { 786void JitShader::FindReturnOffsets() {
759 const u8* start = GetCodePtr(); 787 return_offsets.clear();
760 unsigned offset = g_state.regs.vs.main_offset; 788
789 for (size_t offset = 0; offset < g_state.vs.program_code.size(); ++offset) {
790 Instruction instr = GetVertexShaderInstruction(offset);
791
792 switch (instr.opcode.Value()) {
793 case OpCode::Id::CALL:
794 case OpCode::Id::CALLC:
795 case OpCode::Id::CALLU:
796 return_offsets.push_back(instr.flow_control.dest_offset + instr.flow_control.num_instructions);
797 break;
798 }
799 }
800
801 // Sort for efficient binary search later
802 std::sort(return_offsets.begin(), return_offsets.end());
803}
804
805void JitShader::Compile() {
806 // Reset flow control state
807 program = (CompiledShader*)GetCodePtr();
808 program_counter = 0;
809 looping = false;
810 code_ptr.fill(nullptr);
811 fixup_branches.clear();
812
813 // Find all `CALL` instructions and identify return locations
814 FindReturnOffsets();
761 815
762 // The stack pointer is 8 modulo 16 at the entry of a procedure 816 // The stack pointer is 8 modulo 16 at the entry of a procedure
763 ABI_PushRegistersAndAdjustStack(ABI_ALL_CALLEE_SAVED, 8); 817 ABI_PushRegistersAndAdjustStack(ABI_ALL_CALLEE_SAVED, 8);
@@ -780,21 +834,31 @@ CompiledShader* JitCompiler::Compile() {
780 MOV(PTRBITS, R(RAX), ImmPtr(&neg)); 834 MOV(PTRBITS, R(RAX), ImmPtr(&neg));
781 MOVAPS(NEGBIT, MatR(RAX)); 835 MOVAPS(NEGBIT, MatR(RAX));
782 836
783 looping = false; 837 // Jump to start of the shader program
838 JMPptr(R(ABI_PARAM2));
839
840 // Compile entire program
841 Compile_Block(static_cast<unsigned>(g_state.vs.program_code.size()));
784 842
785 while (offset < g_state.vs.program_code.size()) { 843 // Set the target for any incomplete branches now that the entire shader program has been emitted
786 Compile_NextInstr(&offset); 844 for (const auto& branch : fixup_branches) {
845 SetJumpTarget(branch.first, code_ptr[branch.second]);
787 } 846 }
788 847
789 return (CompiledShader*)start; 848 // Free memory that's no longer needed
790} 849 return_offsets.clear();
850 return_offsets.shrink_to_fit();
851 fixup_branches.clear();
852 fixup_branches.shrink_to_fit();
853
854 uintptr_t size = reinterpret_cast<uintptr_t>(GetCodePtr()) - reinterpret_cast<uintptr_t>(program);
855 ASSERT_MSG(size <= MAX_SHADER_SIZE, "Compiled a shader that exceeds the allocated size!");
791 856
792JitCompiler::JitCompiler() { 857 LOG_DEBUG(HW_GPU, "Compiled shader size=%d", size);
793 AllocCodeSpace(jit_cache_size);
794} 858}
795 859
796void JitCompiler::Clear() { 860JitShader::JitShader() {
797 ClearCodeSpace(); 861 AllocCodeSpace(MAX_SHADER_SIZE);
798} 862}
799 863
800} // namespace Shader 864} // 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 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <utility>
8#include <vector>
9
7#include <nihstro/shader_bytecode.h> 10#include <nihstro/shader_bytecode.h>
8 11
9#include "common/x64/emitter.h" 12#include "common/x64/emitter.h"
@@ -19,24 +22,22 @@ namespace Pica {
19 22
20namespace Shader { 23namespace Shader {
21 24
22/// Memory needed to be available to compile the next shader (otherwise, clear the cache) 25/// Memory allocated for each compiled shader (64Kb)
23constexpr size_t jit_shader_size = 1024 * 512; 26constexpr size_t MAX_SHADER_SIZE = 1024 * 64;
24/// Memory allocated for the JIT code space cache
25constexpr size_t jit_cache_size = 1024 * 1024 * 8;
26
27using CompiledShader = void(void* registers);
28 27
29/** 28/**
30 * This class implements the shader JIT compiler. It recompiles a Pica shader program into x86_64 29 * This class implements the shader JIT compiler. It recompiles a Pica shader program into x86_64
31 * code that can be executed on the host machine directly. 30 * code that can be executed on the host machine directly.
32 */ 31 */
33class JitCompiler : public Gen::XCodeBlock { 32class JitShader : public Gen::XCodeBlock {
34public: 33public:
35 JitCompiler(); 34 JitShader();
36 35
37 CompiledShader* Compile(); 36 void Run(void* registers, unsigned offset) const {
37 program(registers, code_ptr[offset]);
38 }
38 39
39 void Clear(); 40 void Compile();
40 41
41 void Compile_ADD(Instruction instr); 42 void Compile_ADD(Instruction instr);
42 void Compile_DP3(Instruction instr); 43 void Compile_DP3(Instruction instr);
@@ -66,8 +67,9 @@ public:
66 void Compile_MAD(Instruction instr); 67 void Compile_MAD(Instruction instr);
67 68
68private: 69private:
70
69 void Compile_Block(unsigned end); 71 void Compile_Block(unsigned end);
70 void Compile_NextInstr(unsigned* offset); 72 void Compile_NextInstr();
71 73
72 void Compile_SwizzleSrc(Instruction instr, unsigned src_num, SourceRegister src_reg, Gen::X64Reg dest); 74 void Compile_SwizzleSrc(Instruction instr, unsigned src_num, SourceRegister src_reg, Gen::X64Reg dest);
73 void Compile_DestEnable(Instruction instr, Gen::X64Reg dest); 75 void Compile_DestEnable(Instruction instr, Gen::X64Reg dest);
@@ -81,13 +83,39 @@ private:
81 void Compile_EvaluateCondition(Instruction instr); 83 void Compile_EvaluateCondition(Instruction instr);
82 void Compile_UniformCondition(Instruction instr); 84 void Compile_UniformCondition(Instruction instr);
83 85
86 /**
87 * Emits the code to conditionally return from a subroutine envoked by the `CALL` instruction.
88 */
89 void Compile_Return();
90
84 BitSet32 PersistentCallerSavedRegs(); 91 BitSet32 PersistentCallerSavedRegs();
85 92
86 /// Pointer to the variable that stores the current Pica code offset. Used to handle nested code blocks. 93 /**
87 unsigned* offset_ptr = nullptr; 94 * Assertion evaluated at compile-time, but only triggered if executed at runtime.
95 * @param msg Message to be logged if the assertion fails.
96 */
97 void Compile_Assert(bool condition, const char* msg);
98
99 /**
100 * Analyzes the entire shader program for `CALL` instructions before emitting any code,
101 * identifying the locations where a return needs to be inserted.
102 */
103 void FindReturnOffsets();
104
105 /// Mapping of Pica VS instructions to pointers in the emitted code
106 std::array<const u8*, 1024> code_ptr;
107
108 /// Offsets in code where a return needs to be inserted
109 std::vector<unsigned> return_offsets;
110
111 unsigned program_counter = 0; ///< Offset of the next instruction to decode
112 bool looping = false; ///< True if compiling a loop, used to check for nested loops
113
114 /// Branches that need to be fixed up once the entire shader program is compiled
115 std::vector<std::pair<Gen::FixupBranch, unsigned>> fixup_branches;
88 116
89 /// Set to true if currently in a loop, used to check for the existence of nested loops 117 using CompiledShader = void(void* registers, const u8* start_addr);
90 bool looping = false; 118 CompiledShader* program = nullptr;
91}; 119};
92 120
93} // Shader 121} // 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 @@
5#include <memory> 5#include <memory>
6 6
7#include "common/emu_window.h" 7#include "common/emu_window.h"
8#include "common/make_unique.h"
9#include "common/logging/log.h" 8#include "common/logging/log.h"
10 9
11#include "core/core.h" 10#include "core/core.h"
@@ -32,7 +31,7 @@ bool Init(EmuWindow* emu_window) {
32 Pica::Init(); 31 Pica::Init();
33 32
34 g_emu_window = emu_window; 33 g_emu_window = emu_window;
35 g_renderer = Common::make_unique<RendererOpenGL>(); 34 g_renderer = std::make_unique<RendererOpenGL>();
36 g_renderer->SetWindow(g_emu_window); 35 g_renderer->SetWindow(g_emu_window);
37 if (g_renderer->Init()) { 36 if (g_renderer->Init()) {
38 LOG_DEBUG(Render, "initialized OK"); 37 LOG_DEBUG(Render, "initialized OK");