diff options
Diffstat (limited to 'src')
106 files changed, 2707 insertions, 1051 deletions
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index c4bad8cb0..869da5e83 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt | |||
| @@ -2,13 +2,16 @@ set(SRCS | |||
| 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 | ||
| 8 | set(HEADERS | 9 | set(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 | |||
| 14 | namespace DSP { | ||
| 15 | namespace HLE { | ||
| 16 | |||
| 17 | /// The final output to the speakers is stereo. Preprocessing output in Source is also stereo. | ||
| 18 | using StereoFrame16 = std::array<std::array<s16, 2>, AudioCore::samples_per_frame>; | ||
| 19 | |||
| 20 | /// The DSP is quadraphonic internally. | ||
| 21 | using 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 | */ | ||
| 27 | template<typename FrameT, typename FilterT> | ||
| 28 | void 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 | |||
| 15 | namespace DSP { | ||
| 16 | namespace HLE { | ||
| 17 | |||
| 18 | void SourceFilters::Reset() { | ||
| 19 | Enable(false, false); | ||
| 20 | } | ||
| 21 | |||
| 22 | void 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 | |||
| 32 | void SourceFilters::Configure(SourceConfiguration::Configuration::SimpleFilter config) { | ||
| 33 | simple_filter.Configure(config); | ||
| 34 | } | ||
| 35 | |||
| 36 | void SourceFilters::Configure(SourceConfiguration::Configuration::BiquadFilter config) { | ||
| 37 | biquad_filter.Configure(config); | ||
| 38 | } | ||
| 39 | |||
| 40 | void 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 | |||
| 55 | void SourceFilters::SimpleFilter::Reset() { | ||
| 56 | y1.fill(0); | ||
| 57 | // Configure as passthrough. | ||
| 58 | a1 = 0; | ||
| 59 | b0 = 1 << 15; | ||
| 60 | } | ||
| 61 | |||
| 62 | void SourceFilters::SimpleFilter::Configure(SourceConfiguration::Configuration::SimpleFilter config) { | ||
| 63 | a1 = config.a1; | ||
| 64 | b0 = config.b0; | ||
| 65 | } | ||
| 66 | |||
| 67 | std::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 | |||
| 81 | void 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 | |||
| 91 | void 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 | |||
| 99 | std::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 | |||
| 14 | namespace DSP { | ||
| 15 | namespace HLE { | ||
| 16 | |||
| 17 | /// Preprocessing filters. There is an independent set of filters for each Source. | ||
| 18 | class 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 | |||
| 51 | private: | ||
| 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 | ||
| 37 | static void PrintHelp() | 37 | static 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 |
| 43 | int main(int argc, char **argv) { | 45 | int 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 @@ | |||
| 19 | Config::Config() { | 20 | Config::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 | ||
| 14 | Config::Config() { | 15 | Config::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 | ||
| 72 | void Config::SaveValues() { | 117 | void 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 | ||
| 112 | void Config::Reload() { | 190 | void Config::Reload() { |
| 113 | ReadValues(); | 191 | ReadValues(); |
| 192 | Settings::Apply(); | ||
| 114 | } | 193 | } |
| 115 | 194 | ||
| 116 | void Config::Save() { | 195 | void Config::Save() { |
diff --git a/src/citra_qt/configure.ui b/src/citra_qt/configure.ui new file mode 100644 index 000000000..6ae056ff9 --- /dev/null +++ b/src/citra_qt/configure.ui | |||
| @@ -0,0 +1,97 @@ | |||
| 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 | |||
| 10 | ConfigureDebug::ConfigureDebug(QWidget *parent) : | ||
| 11 | QWidget(parent), | ||
| 12 | ui(new Ui::ConfigureDebug) | ||
| 13 | { | ||
| 14 | ui->setupUi(this); | ||
| 15 | this->setConfiguration(); | ||
| 16 | } | ||
| 17 | |||
| 18 | ConfigureDebug::~ConfigureDebug() { | ||
| 19 | } | ||
| 20 | |||
| 21 | void 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 | |||
| 27 | void 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 | |||
| 10 | namespace Ui { | ||
| 11 | class ConfigureDebug; | ||
| 12 | } | ||
| 13 | |||
| 14 | class ConfigureDebug : public QWidget | ||
| 15 | { | ||
| 16 | Q_OBJECT | ||
| 17 | |||
| 18 | public: | ||
| 19 | explicit ConfigureDebug(QWidget *parent = nullptr); | ||
| 20 | ~ConfigureDebug(); | ||
| 21 | |||
| 22 | void applyConfiguration(); | ||
| 23 | |||
| 24 | private: | ||
| 25 | void setConfiguration(); | ||
| 26 | |||
| 27 | private: | ||
| 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 | |||
| 12 | ConfigureDialog::ConfigureDialog(QWidget *parent) : | ||
| 13 | QDialog(parent), | ||
| 14 | ui(new Ui::ConfigureDialog) | ||
| 15 | { | ||
| 16 | ui->setupUi(this); | ||
| 17 | this->setConfiguration(); | ||
| 18 | } | ||
| 19 | |||
| 20 | ConfigureDialog::~ConfigureDialog() { | ||
| 21 | } | ||
| 22 | |||
| 23 | void ConfigureDialog::setConfiguration() { | ||
| 24 | } | ||
| 25 | |||
| 26 | void 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 | |||
| 10 | namespace Ui { | ||
| 11 | class ConfigureDialog; | ||
| 12 | } | ||
| 13 | |||
| 14 | class ConfigureDialog : public QDialog | ||
| 15 | { | ||
| 16 | Q_OBJECT | ||
| 17 | |||
| 18 | public: | ||
| 19 | explicit ConfigureDialog(QWidget *parent = nullptr); | ||
| 20 | ~ConfigureDialog(); | ||
| 21 | |||
| 22 | void applyConfiguration(); | ||
| 23 | |||
| 24 | private: | ||
| 25 | void setConfiguration(); | ||
| 26 | |||
| 27 | private: | ||
| 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 | |||
| 11 | ConfigureGeneral::ConfigureGeneral(QWidget *parent) : | ||
| 12 | QWidget(parent), | ||
| 13 | ui(new Ui::ConfigureGeneral) | ||
| 14 | { | ||
| 15 | ui->setupUi(this); | ||
| 16 | this->setConfiguration(); | ||
| 17 | } | ||
| 18 | |||
| 19 | ConfigureGeneral::~ConfigureGeneral() { | ||
| 20 | } | ||
| 21 | |||
| 22 | void 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 | |||
| 30 | void 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 | |||
| 10 | namespace Ui { | ||
| 11 | class ConfigureGeneral; | ||
| 12 | } | ||
| 13 | |||
| 14 | class ConfigureGeneral : public QWidget | ||
| 15 | { | ||
| 16 | Q_OBJECT | ||
| 17 | |||
| 18 | public: | ||
| 19 | explicit ConfigureGeneral(QWidget *parent = nullptr); | ||
| 20 | ~ConfigureGeneral(); | ||
| 21 | |||
| 22 | void applyConfiguration(); | ||
| 23 | |||
| 24 | private: | ||
| 25 | void setConfiguration(); | ||
| 26 | |||
| 27 | private: | ||
| 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 | ||
| 103 | void GameList::SaveInterfaceLayout(QSettings& settings) | 104 | void 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 | ||
| 110 | void GameList::LoadInterfaceLayout(QSettings& settings) | 109 | void 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 | ||
| 37 | public slots: | 37 | public 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 | ||
| 13 | struct Hotkey | 14 | struct Hotkey |
| 14 | { | 15 | { |
| @@ -24,54 +25,39 @@ typedef std::map<QString, HotkeyMap> HotkeyGroupMap; | |||
| 24 | 25 | ||
| 25 | HotkeyGroupMap hotkey_groups; | 26 | HotkeyGroupMap hotkey_groups; |
| 26 | 27 | ||
| 27 | void SaveHotkeys(QSettings& settings) | 28 | void 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 | ||
| 46 | void LoadHotkeys(QSettings& settings) | 43 | void 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 | ||
| 77 | void RegisterHotkey(const QString& group, const QString& action, const QKeySequence& default_keyseq, Qt::ShortcutContext default_context) | 63 | void RegisterHotkey(const QString& group, const QString& action, const QKeySequence& default_keyseq, Qt::ShortcutContext default_context) |
| @@ -94,7 +80,7 @@ QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widge | |||
| 94 | } | 80 | } |
| 95 | 81 | ||
| 96 | 82 | ||
| 97 | GHotkeysDialog::GHotkeysDialog(QWidget* parent): QDialog(parent) | 83 | GHotkeysDialog::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 | ||
| 7 | class QDialog; | 9 | class 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 | */ |
| 36 | void SaveHotkeys(QSettings& settings); | 38 | void 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 | */ |
| 43 | void LoadHotkeys(QSettings& settings); | 45 | void LoadHotkeys(); |
| 44 | 46 | ||
| 45 | class GHotkeysDialog : public QDialog | 47 | class 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 | ||
| 53 | GMainWindow::GMainWindow() : emu_thread(nullptr) | 55 | GMainWindow::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 | ||
| 378 | void GMainWindow::StoreRecentFile(const std::string& filename) | 358 | void 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 | ||
| 393 | void GMainWindow::UpdateRecentFiles() { | 368 | void 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 | ||
| 419 | void GMainWindow::OnGameListLoadFile(QString game_path) { | 391 | void GMainWindow::OnGameListLoadFile(QString game_path) { |
| 420 | BootGame(game_path.toLocal8Bit().data()); | 392 | BootGame(game_path.toStdString()); |
| 421 | } | 393 | } |
| 422 | 394 | ||
| 423 | void GMainWindow::OnMenuLoadFile() { | 395 | void 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 | ||
| 435 | void GMainWindow::OnMenuLoadSymbolMap() { | 404 | void 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 | ||
| 447 | void GMainWindow::OnMenuSelectGameListRoot() { | 413 | void 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 | ||
| 499 | void GMainWindow::OnOpenHotkeysDialog() { | ||
| 500 | GHotkeysDialog dialog(this); | ||
| 501 | dialog.exec(); | ||
| 502 | } | ||
| 503 | |||
| 504 | void 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 | |||
| 512 | void GMainWindow::SetGdbstubEnabled(bool enabled) { | ||
| 513 | GDBStub::ToggleServer(enabled); | ||
| 514 | } | ||
| 515 | |||
| 516 | void 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 | |||
| 524 | void GMainWindow::ToggleWindowMode() { | 460 | void 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 | ||
| 549 | void GMainWindow::OnConfigure() { | 485 | void 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 | ||
| 553 | bool GMainWindow::ConfirmClose() { | 495 | bool 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 | ||
| 13 | class Config; | ||
| 13 | class GameList; | 14 | class GameList; |
| 14 | class GImageInfo; | 15 | class GImageInfo; |
| 15 | class GRenderWindow; | 16 | class 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 | ||
| 115 | private: | 112 | private: |
| @@ -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 &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 | |||
| 7 | namespace UISettings { | ||
| 8 | |||
| 9 | Values 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 | |||
| 13 | namespace UISettings { | ||
| 14 | |||
| 15 | using ContextualShortcut = std::pair<QString, int> ; | ||
| 16 | using Shortcut = std::pair<QString, ContextualShortcut>; | ||
| 17 | |||
| 18 | struct 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 | |||
| 45 | extern 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 |
| 573 | std::string GetCurrentDir() | 578 | std::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 |
| 589 | bool SetCurrentDir(const std::string &directory) | 602 | bool 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 | ||
| 808 | size_t ReadFileToString(bool text_file, const char *filename, std::string &str) | 825 | size_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 | ||
| 862 | IOFile::IOFile() | 878 | IOFile::IOFile() |
| 863 | : m_file(nullptr), m_good(true) | 879 | { |
| 864 | {} | 880 | } |
| 865 | |||
| 866 | IOFile::IOFile(std::FILE* file) | ||
| 867 | : m_file(file), m_good(true) | ||
| 868 | {} | ||
| 869 | 881 | ||
| 870 | IOFile::IOFile(const std::string& filename, const char openmode[]) | 882 | IOFile::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 | ||
| 881 | IOFile::IOFile(IOFile&& other) | 892 | IOFile::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 | ||
| 921 | std::FILE* IOFile::ReleaseHandle() | 931 | u64 IOFile::GetSize() const |
| 922 | { | ||
| 923 | std::FILE* const ret = m_file; | ||
| 924 | m_file = nullptr; | ||
| 925 | return ret; | ||
| 926 | } | ||
| 927 | |||
| 928 | void IOFile::SetHandle(std::FILE* file) | ||
| 929 | { | ||
| 930 | Close(); | ||
| 931 | Clear(); | ||
| 932 | m_file = file; | ||
| 933 | } | ||
| 934 | |||
| 935 | u64 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 | ||
| 943 | bool IOFile::Seek(s64 off, int origin) | 939 | bool 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 | ||
| 951 | u64 IOFile::Tell() | 947 | u64 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 | ||
| 959 | bool IOFile::Flush() | 955 | bool IOFile::Flush() |
diff --git a/src/common/file_util.h b/src/common/file_util.h index a85121aa6..b54a9fb72 100644 --- a/src/common/file_util.h +++ b/src/common/file_util.h | |||
| @@ -14,6 +14,10 @@ | |||
| 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 |
| 18 | enum { | 22 | enum { |
| 19 | D_USER_IDX, | 23 | D_USER_IDX, |
| @@ -172,7 +176,6 @@ class IOFile : public NonCopyable | |||
| 172 | { | 176 | { |
| 173 | public: | 177 | public: |
| 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; | ||
| 261 | private: | 258 | private: |
| 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 | |||
| 10 | namespace Common { | ||
| 11 | |||
| 12 | template <typename T, typename... Args> | ||
| 13 | std::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 | ||
| 323 | static std::string UTF16ToUTF8(const std::wstring& input) | 323 | static 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 | ||
| 336 | static std::wstring CPToUTF16(u32 code_page, const std::string& input) | 336 | std::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); | |||
| 95 | std::string SHIFTJISToUTF8(const std::string& str); | 95 | std::string SHIFTJISToUTF8(const std::string& str); |
| 96 | 96 | ||
| 97 | #ifdef _WIN32 | 97 | #ifdef _WIN32 |
| 98 | 98 | std::string UTF16ToUTF8(const std::wstring& input); | |
| 99 | std::wstring UTF8ToUTF16W(const std::string& str); | 99 | std::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 | ||
| 33 | namespace Common | 33 | namespace Common { |
| 34 | { | ||
| 35 | 34 | ||
| 36 | int CurrentThreadId(); | 35 | int 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 | ||
| 65 | private: | 64 | private: |
| 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 | ||
| 71 | class Barrier { | 70 | class Barrier { |
| 72 | public: | 71 | public: |
| 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 | ||
| 90 | private: | 88 | private: |
| 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 | ||
| 97 | void SleepCurrentThread(int ms); | 96 | void 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. |
| 103 | inline void YieldCPU() | 102 | inline 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 | ||
| 458 | FixupBranch 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 | |||
| 458 | FixupBranch XEmitter::J(bool force5bytes) | 470 | FixupBranch 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 | ||
| 546 | void 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. |
| 536 | void XEmitter::INT3() {Write8(0xCC);} | 564 | void XEmitter::INT3() {Write8(0xCC);} |
diff --git a/src/common/x64/emitter.h b/src/common/x64/emitter.h index 7c6548fb5..a33724146 100644 --- a/src/common/x64/emitter.h +++ b/src/common/x64/emitter.h | |||
| @@ -425,12 +425,14 @@ public: | |||
| 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 | ||
| 20 | ARM_DynCom::ARM_DynCom(PrivilegeMode initial_mode) { | 19 | ARM_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 | ||
| 24 | ARM_DynCom::~ARM_DynCom() { | 23 | ARM_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 | ||
| 100 | void ARM_DynCom::SaveContext(Core::ThreadContext& ctx) { | 99 | void ARM_DynCom::SaveContext(Core::ThreadContext& ctx) { |
diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp index 5f8826034..647784208 100644 --- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp +++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp | |||
| @@ -36,7 +36,8 @@ enum { | |||
| 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 | ||
| 3467 | MICROPROFILE_DEFINE(DynCom_Decode, "DynCom", "Decode", MP_RGB(255, 64, 64)); | 3468 | MICROPROFILE_DEFINE(DynCom_Decode, "DynCom", "Decode", MP_RGB(255, 64, 64)); |
| 3468 | 3469 | ||
| 3469 | static int InterpreterTranslate(ARMul_State* cpu, int& bb_start, u32 addr) { | 3470 | static 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 | |||
| 3498 | static 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 | ||
| 3511 | translated: | ||
| 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 | ||
| 3532 | static 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 | |||
| 3525 | static int clz(unsigned int x) { | 3553 | static 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 |
| 76 | void Init() { | 75 | void 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 | ||
| 27 | ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_RomFS::Open(const Path& path) { | 26 | ResultVal<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 | ||
| 38 | ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SDMC::Open(const Path& path) { | 38 | ResultVal<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 | ||
| 20 | ResultVal<std::unique_ptr<FileBackend>> DiskArchive::OpenFile(const Path& path, const Mode mode) const { | 20 | ResultVal<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 | ||
| 84 | std::unique_ptr<DirectoryBackend> DiskArchive::OpenDirectory(const Path& path) const { | 84 | std::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 | ||
| 23 | ResultVal<std::unique_ptr<FileBackend>> IVFCArchive::OpenFile(const Path& path, const Mode mode) const { | 22 | ResultVal<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 | ||
| 27 | ResultCode IVFCArchive::DeleteFile(const Path& path) const { | 26 | ResultCode 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 | ||
| 60 | std::unique_ptr<DirectoryBackend> IVFCArchive::OpenDirectory(const Path& path) const { | 59 | std::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 | ||
| 64 | u64 IVFCArchive::GetFreeBytes() const { | 63 | u64 IVFCArchive::GetFreeBytes() const { |
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index 3a2445241..c1a7ec5bf 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp | |||
| @@ -60,6 +60,59 @@ const u32 R15_REGISTER = 15; | |||
| 60 | const u32 CPSR_REGISTER = 25; | 60 | const u32 CPSR_REGISTER = 25; |
| 61 | const u32 FPSCR_REGISTER = 58; | 61 | const 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 | ||
| 66 | static const char* target_xml = | ||
| 67 | R"(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 | |||
| 63 | namespace GDBStub { | 116 | namespace GDBStub { |
| 64 | 117 | ||
| 65 | static int gdbserver_socket = -1; | 118 | static 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. |
| 214 | static u8 CalculateChecksum(u8 *buffer, u32 length) { | 267 | static 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) { | |||
| 353 | static void HandleQuery() { | 406 | static 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 | ||
| 13 | namespace Service { | 16 | namespace Service { |
| 14 | namespace AM { | 17 | namespace AM { |
| 15 | 18 | ||
| 16 | void TitleIDListGetTotal(Service::Interface* self) { | 19 | static std::array<u32, 3> am_content_count = { 0, 0, 0 }; |
| 20 | static std::array<u32, 3> am_titles_count = { 0, 0, 0 }; | ||
| 21 | static std::array<u32, 3> am_titles_list_count = { 0, 0, 0 }; | ||
| 22 | static u32 am_ticket_count = 0; | ||
| 23 | static u32 am_ticket_list_count = 0; | ||
| 24 | |||
| 25 | void 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 | |||
| 35 | void 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 | ||
| 26 | void GetTitleIDList(Service::Interface* self) { | 50 | void 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 | |||
| 66 | void 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 | |||
| 80 | void 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 | |||
| 94 | void 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 | |||
| 108 | void GetDataTitleInfos(Service::Interface* self) { | ||
| 109 | GetTitleInfo(self); | ||
| 110 | |||
| 111 | LOG_WARNING(Service_AM, "(STUBBED) called"); | ||
| 112 | } | ||
| 113 | |||
| 114 | void 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 | ||
| 38 | void GetNumContentInfos(Service::Interface* self) { | 129 | void 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 | ||
| 137 | void 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 | |||
| 146 | void 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 | |||
| 154 | void 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 | |||
| 47 | void Init() { | 168 | void 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 | ||
| 55 | void Shutdown() { | 177 | void Shutdown() { |
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 15e63bc7b..5676cdd5f 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h | |||
| @@ -11,7 +11,7 @@ class Interface; | |||
| 11 | namespace AM { | 11 | namespace 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 | */ |
| 23 | void TitleIDListGetTotal(Service::Interface* self); | 23 | void 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 | */ | ||
| 36 | void 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 | */ | ||
| 50 | void 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 | */ | ||
| 62 | void 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 | */ |
| 38 | void GetTitleIDList(Service::Interface* self); | 75 | void 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 | */ | ||
| 87 | void 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 | */ | ||
| 100 | void 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 | */ | ||
| 114 | void 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 | */ |
| 51 | void GetNumContentInfos(Service::Interface* self); | 126 | void 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 | */ | ||
| 135 | void 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 | */ | ||
| 143 | void 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 | */ | ||
| 155 | void GetTicketList(Service::Interface* self); | ||
| 156 | |||
| 53 | /// Initialize AM service | 157 | /// Initialize AM service |
| 54 | void Init(); | 158 | void 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 { | |||
| 9 | namespace AM { | 9 | namespace AM { |
| 10 | 10 | ||
| 11 | const Interface::FunctionInfo FunctionTable[] = { | 11 | const 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 | ||
| 22 | AM_APP_Interface::AM_APP_Interface() { | 22 | AM_APP_Interface::AM_APP_Interface() { |
diff --git a/src/core/hle/service/am/am_net.cpp b/src/core/hle/service/am/am_net.cpp index 065e04118..e75755245 100644 --- a/src/core/hle/service/am/am_net.cpp +++ b/src/core/hle/service/am/am_net.cpp | |||
| @@ -9,16 +9,20 @@ namespace Service { | |||
| 9 | namespace AM { | 9 | namespace AM { |
| 10 | 10 | ||
| 11 | const Interface::FunctionInfo FunctionTable[] = { | 11 | const 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 { | |||
| 9 | namespace AM { | 9 | namespace AM { |
| 10 | 10 | ||
| 11 | const Interface::FunctionInfo FunctionTable[] = { | 11 | const 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 | ||
| 31 | AM_SYS_Interface::AM_SYS_Interface() { | 35 | AM_SYS_Interface::AM_SYS_Interface() { |
diff --git a/src/core/hle/service/am/am_u.cpp b/src/core/hle/service/am/am_u.cpp index c0392b754..d583dd9e6 100644 --- a/src/core/hle/service/am/am_u.cpp +++ b/src/core/hle/service/am/am_u.cpp | |||
| @@ -9,16 +9,20 @@ namespace Service { | |||
| 9 | namespace AM { | 9 | namespace AM { |
| 10 | 10 | ||
| 11 | const Interface::FunctionInfo FunctionTable[] = { | 11 | const 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 | ||
| 400 | void 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 | |||
| 400 | void Init() { | 417 | void 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 | ||
| 70 | enum 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 |
| 71 | void SendParameter(const MessageParameter& parameter); | 77 | void SendParameter(const MessageParameter& parameter); |
| 72 | 78 | ||
| @@ -344,6 +350,17 @@ void PreloadLibraryApplet(Service::Interface* self); | |||
| 344 | */ | 350 | */ |
| 345 | void StartLibraryApplet(Service::Interface* self); | 351 | void 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 | */ | ||
| 362 | void GetStartupArgument(Service::Interface* self); | ||
| 363 | |||
| 347 | /// Initialize the APT service | 364 | /// Initialize the APT service |
| 348 | void Init(); | 365 | void 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 @@ | |||
| 12 | namespace Service { | 13 | namespace Service { |
| 13 | namespace CECD { | 14 | namespace CECD { |
| 14 | 15 | ||
| 15 | void Init() { | 16 | static Kernel::SharedPtr<Kernel::Event> cecinfo_event; |
| 16 | using namespace Kernel; | 17 | static Kernel::SharedPtr<Kernel::Event> change_state_event; |
| 18 | |||
| 19 | void 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 | |||
| 28 | void 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 | |||
| 37 | void 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 | |||
| 46 | void 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 | ||
| 22 | void Shutdown() { | 54 | void 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 | ||
| 7 | namespace Service { | 7 | namespace Service { |
| 8 | |||
| 9 | class Interface; | ||
| 10 | |||
| 8 | namespace CECD { | 11 | namespace CECD { |
| 9 | 12 | ||
| 13 | enum 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 | */ | ||
| 29 | void 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 | */ | ||
| 39 | void 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 | */ | ||
| 49 | void GetChangeStateEventHandle(Service::Interface* self); | ||
| 50 | |||
| 10 | /// Initialize CECD service(s) | 51 | /// Initialize CECD service(s) |
| 11 | void Init(); | 52 | void 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 | ||
| 7 | namespace Service { | 8 | namespace Service { |
| 8 | namespace CECD { | 9 | namespace CECD { |
| 9 | 10 | ||
| 10 | static const Interface::FunctionInfo FunctionTable[] = { | 11 | static 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 | ||
| 14 | CECD_U_Interface::CECD_U_Interface() { | 18 | CECD_U_Interface::CECD_U_Interface() { |
diff --git a/src/core/hle/service/cfg/cfg_i.cpp b/src/core/hle/service/cfg/cfg_i.cpp index 0559a07b2..b18060f6d 100644 --- a/src/core/hle/service/cfg/cfg_i.cpp +++ b/src/core/hle/service/cfg/cfg_i.cpp | |||
| @@ -9,6 +9,18 @@ namespace Service { | |||
| 9 | namespace CFG { | 9 | namespace CFG { |
| 10 | 10 | ||
| 11 | const Interface::FunctionInfo FunctionTable[] = { | 11 | const 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 { | |||
| 9 | namespace CFG { | 9 | namespace CFG { |
| 10 | 10 | ||
| 11 | const Interface::FunctionInfo FunctionTable[] = { | 11 | const 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 { | |||
| 9 | namespace CFG { | 9 | namespace CFG { |
| 10 | 10 | ||
| 11 | const Interface::FunctionInfo FunctionTable[] = { | 11 | const 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 | |||
| 12 | namespace DLP_SRVR { | ||
| 13 | |||
| 14 | static 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 | |||
| 23 | const Interface::FunctionInfo FunctionTable[] = { | ||
| 24 | {0x00010183, nullptr, "Initialize"}, | ||
| 25 | {0x00020000, nullptr, "Finalize"}, | ||
| 26 | {0x000E0040, unk_0x000E0040, "unk_0x000E0040"}, | ||
| 27 | }; | ||
| 28 | |||
| 29 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 30 | // Interface class | ||
| 31 | |||
| 32 | Interface::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 | |||
| 12 | namespace DLP_SRVR { | ||
| 13 | |||
| 14 | class Interface : public Service::Interface { | ||
| 15 | public: | ||
| 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 @@ | |||
| 10 | namespace Service { | 12 | namespace Service { |
| 11 | namespace FRD { | 13 | namespace FRD { |
| 12 | 14 | ||
| 15 | static FriendKey my_friend_key = {0, 0, 0ull}; | ||
| 16 | static MyPresence my_presence = {}; | ||
| 17 | |||
| 18 | void 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 | |||
| 33 | void 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 | |||
| 52 | void 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 | |||
| 70 | void 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 | |||
| 87 | void 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 | |||
| 95 | void 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 | |||
| 13 | void Init() { | 104 | void 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 | |||
| 7 | namespace Service { | 9 | namespace Service { |
| 10 | |||
| 11 | class Interface; | ||
| 12 | |||
| 8 | namespace FRD { | 13 | namespace FRD { |
| 9 | 14 | ||
| 15 | struct FriendKey { | ||
| 16 | u32 friend_id; | ||
| 17 | u32 unknown; | ||
| 18 | u64 friend_code; | ||
| 19 | }; | ||
| 20 | |||
| 21 | struct MyPresence { | ||
| 22 | u8 unknown[0x12C]; | ||
| 23 | }; | ||
| 24 | |||
| 25 | struct 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 | */ | ||
| 41 | void 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 | */ | ||
| 53 | void 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 | */ | ||
| 66 | void 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 | */ | ||
| 78 | void 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 | */ | ||
| 88 | void 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 | */ | ||
| 96 | void GetMyScreenName(Service::Interface* self); | ||
| 97 | |||
| 10 | /// Initialize FRD service(s) | 98 | /// Initialize FRD service(s) |
| 11 | void Init(); | 99 | void 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 | ||
| 7 | namespace Service { | 8 | namespace Service { |
| 8 | namespace FRD { | 9 | namespace FRD { |
| 9 | 10 | ||
| 10 | const Interface::FunctionInfo FunctionTable[] = { | 11 | const 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 | ||
| 66 | FRD_U_Interface::FRD_U_Interface() { | 67 | FRD_U_Interface::FRD_U_Interface() { |
diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp index 590697e76..e9588cb72 100644 --- a/src/core/hle/service/fs/archive.cpp +++ b/src/core/hle/service/fs/archive.cpp | |||
| @@ -15,7 +15,6 @@ | |||
| 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 | ||
| 32 | namespace GSP_GPU { | 32 | namespace GSP_GPU { |
| 33 | 33 | ||
| 34 | const ResultCode ERR_GSP_REGS_OUTOFRANGE_OR_MISALIGNED(ErrorDescription::OutofRangeOrMisalignedAddress, ErrorModule::GX, | ||
| 35 | ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E02A01 | ||
| 36 | const ResultCode ERR_GSP_REGS_MISALIGNED(ErrorDescription::MisalignedSize, ErrorModule::GX, | ||
| 37 | ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E02BF2 | ||
| 38 | const 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 |
| 35 | Kernel::SharedPtr<Kernel::Event> g_interrupt_event; | 42 | Kernel::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 | */ |
| 68 | static bool CheckWriteParameters(u32 base_address, u32 size_in_bytes) { | 76 | static 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 | */ |
| 92 | static void WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) { | 115 | static 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 | */ | ||
| 135 | static 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 | ||
| 209 | void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) { | 224 | ResultCode 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 | */ | ||
| 311 | static 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 | */ | ||
| 359 | static 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 | */ |
| 195 | void SignalInterrupt(InterruptId interrupt_id); | 195 | void SignalInterrupt(InterruptId interrupt_id); |
| 196 | 196 | ||
| 197 | void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info); | 197 | ResultCode 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 | |||
| 20 | namespace Loader { | 16 | namespace 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 | |||
| 7 | namespace Settings { | 11 | namespace Settings { |
| 8 | 12 | ||
| 9 | Values values = {}; | 13 | Values values = {}; |
| 10 | 14 | ||
| 15 | void 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 | ||
| 70 | void 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 | ||
| 86 | namespace DebugUtils { | 86 | namespace DebugUtils { |
| 87 | 87 | ||
| 88 | void 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 | |||
| 97 | void 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 | |||
| 117 | void DumpShader(const std::string& filename, const Regs::ShaderConfig& config, const Shader::ShaderSetup& setup, const Regs::VSOutputAttributes* output_attributes) | 88 | void 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. | ||
| 591 | static 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 | |||
| 597 | static 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 | |||
| 618 | void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) { | 604 | void 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 | ||
| 159 | namespace DebugUtils { | 159 | namespace 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 | ||
| 166 | class GeometryDumper { | ||
| 167 | public: | ||
| 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 | |||
| 176 | private: | ||
| 177 | struct Face { | ||
| 178 | int index[3]; | ||
| 179 | }; | ||
| 180 | |||
| 181 | std::vector<Vertex> vertices; | ||
| 182 | std::vector<Face> faces; | ||
| 183 | }; | ||
| 184 | |||
| 185 | void DumpShader(const std::string& filename, const Regs::ShaderConfig& config, | 164 | void 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 |
| 69 | template | 69 | template |
| 70 | struct PrimitiveAssembler<Shader::OutputVertex>; | 70 | struct PrimitiveAssembler<Shader::OutputVertex>; |
| 71 | template | ||
| 72 | struct 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 | ||
| 147 | void RasterizerOpenGL::Reset() { | 147 | void 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 | ||
| 678 | void RasterizerOpenGL::SetShader() { | 702 | void 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 | ||
| 813 | void RasterizerOpenGL::SyncCullMode() { | 841 | void 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 | ||
| 907 | void 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 | |||
| 920 | void 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 | |||
| 927 | void 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 | |||
| 879 | void RasterizerOpenGL::SyncStencilTest() { | 934 | void 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 | ||
| 904 | void RasterizerOpenGL::SyncCombinerColor() { | 953 | void RasterizerOpenGL::SyncCombinerColor() { |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index fc85aa3ff..390349a0c 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h | |||
| @@ -344,6 +344,15 @@ private: | |||
| 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 { | |||
| 29 | namespace Shader { | 28 | namespace Shader { |
| 30 | 29 | ||
| 31 | #ifdef ARCHITECTURE_x86_64 | 30 | #ifdef ARCHITECTURE_x86_64 |
| 32 | static std::unordered_map<u64, CompiledShader*> shader_map; | 31 | static std::unordered_map<u64, std::unique_ptr<JitShader>> shader_map; |
| 33 | static JitCompiler jit; | 32 | static const JitShader* jit_shader; |
| 34 | static CompiledShader* jit_shader; | ||
| 35 | |||
| 36 | static 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 | ||
| 43 | void Setup(UnitState<false>& state) { | 35 | void 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 | ||
| 67 | void Shutdown() { | 54 | void 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 | */ |
| 344 | void Setup(UnitState<false>& state); | 343 | void Setup(); |
| 345 | 344 | ||
| 346 | /// Performs any cleanup when the emulator is shutdown | 345 | /// Performs any cleanup when the emulator is shutdown |
| 347 | void Shutdown(); | 346 | void Shutdown(); |
diff --git a/src/video_core/shader/shader_jit_x64.cpp b/src/video_core/shader/shader_jit_x64.cpp index dffe051ef..b47d3beda 100644 --- a/src/video_core/shader/shader_jit_x64.cpp +++ b/src/video_core/shader/shader_jit_x64.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 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 | ||
| 20 | using namespace Gen; | 21 | using namespace Gen; |
| 21 | 22 | ||
| 22 | typedef void (JitCompiler::*JitFunction)(Instruction instr); | 23 | typedef void (JitShader::*JitFunction)(Instruction instr); |
| 23 | 24 | ||
| 24 | const JitFunction instr_table[64] = { | 25 | const 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; | |||
| 138 | static const u8 NO_DEST_REG_MASK = 0xf; | 139 | static 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 | */ | ||
| 146 | static Instruction GetVertexShaderInstruction(size_t offset) { | ||
| 147 | return { g_state.vs.program_code[offset] }; | ||
| 148 | } | ||
| 149 | |||
| 150 | static void LogCritical(const char* msg) { | ||
| 151 | LOG_CRITICAL(HW_GPU, msg); | ||
| 152 | } | ||
| 153 | |||
| 154 | void 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 | */ |
| 147 | void JitCompiler::Compile_SwizzleSrc(Instruction instr, unsigned src_num, SourceRegister src_reg, X64Reg dest) { | 167 | void 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 | ||
| 219 | void JitCompiler::Compile_DestEnable(Instruction instr,X64Reg src) { | 239 | void 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 | ||
| 266 | void JitCompiler::Compile_SanitizedMul(Gen::X64Reg src1, Gen::X64Reg src2, Gen::X64Reg scratch) { | 286 | void 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 | ||
| 279 | void JitCompiler::Compile_EvaluateCondition(Instruction instr) { | 299 | void 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 | ||
| 310 | void JitCompiler::Compile_UniformCondition(Instruction instr) { | 330 | void 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 | ||
| 315 | BitSet32 JitCompiler::PersistentCallerSavedRegs() { | 335 | BitSet32 JitShader::PersistentCallerSavedRegs() { |
| 316 | return persistent_regs & ABI_ALL_CALLER_SAVED; | 336 | return persistent_regs & ABI_ALL_CALLER_SAVED; |
| 317 | } | 337 | } |
| 318 | 338 | ||
| 319 | void JitCompiler::Compile_ADD(Instruction instr) { | 339 | void 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 | ||
| 326 | void JitCompiler::Compile_DP3(Instruction instr) { | 346 | void 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 | ||
| 345 | void JitCompiler::Compile_DP4(Instruction instr) { | 365 | void 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 | ||
| 362 | void JitCompiler::Compile_DPH(Instruction instr) { | 382 | void 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 | ||
| 394 | void JitCompiler::Compile_EX2(Instruction instr) { | 414 | void 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 | ||
| 407 | void JitCompiler::Compile_LG2(Instruction instr) { | 427 | void 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 | ||
| 420 | void JitCompiler::Compile_MUL(Instruction instr) { | 440 | void 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 | ||
| 427 | void JitCompiler::Compile_SGE(Instruction instr) { | 447 | void 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 | ||
| 442 | void JitCompiler::Compile_SLT(Instruction instr) { | 462 | void 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 | ||
| 457 | void JitCompiler::Compile_FLR(Instruction instr) { | 477 | void 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 | ||
| 470 | void JitCompiler::Compile_MAX(Instruction instr) { | 490 | void 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 | ||
| 478 | void JitCompiler::Compile_MIN(Instruction instr) { | 498 | void 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 | ||
| 486 | void JitCompiler::Compile_MOVA(Instruction instr) { | 506 | void 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 | ||
| 531 | void JitCompiler::Compile_MOV(Instruction instr) { | 551 | void 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 | ||
| 536 | void JitCompiler::Compile_RCP(Instruction instr) { | 556 | void 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 | ||
| 547 | void JitCompiler::Compile_RSQ(Instruction instr) { | 567 | void 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 | ||
| 558 | void JitCompiler::Compile_NOP(Instruction instr) { | 578 | void JitShader::Compile_NOP(Instruction instr) { |
| 559 | } | 579 | } |
| 560 | 580 | ||
| 561 | void JitCompiler::Compile_END(Instruction instr) { | 581 | void 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 | ||
| 566 | void JitCompiler::Compile_CALL(Instruction instr) { | 586 | void 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 | ||
| 573 | void JitCompiler::Compile_CALLC(Instruction instr) { | 598 | void 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 | ||
| 580 | void JitCompiler::Compile_CALLU(Instruction instr) { | 605 | void 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 | ||
| 587 | void JitCompiler::Compile_CMP(Instruction instr) { | 612 | void 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 | ||
| 630 | void JitCompiler::Compile_MAD(Instruction instr) { | 655 | void 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 | ||
| 647 | void JitCompiler::Compile_IF(Instruction instr) { | 672 | void 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 | ||
| 679 | void JitCompiler::Compile_LOOP(Instruction instr) { | 703 | void 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 | ||
| 708 | void JitCompiler::Compile_JMP(Instruction instr) { | 731 | void 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); | 746 | void JitShader::Compile_Block(unsigned end) { |
| 747 | while (program_counter < end) { | ||
| 748 | Compile_NextInstr(); | ||
| 749 | } | ||
| 750 | } | ||
| 751 | |||
| 752 | void 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 | ||
| 728 | void JitCompiler::Compile_Block(unsigned end) { | 763 | void 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 | ||
| 741 | void 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 | ||
| 758 | CompiledShader* JitCompiler::Compile() { | 786 | void 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 | |||
| 805 | void 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 | ||
| 792 | JitCompiler::JitCompiler() { | 857 | LOG_DEBUG(HW_GPU, "Compiled shader size=%d", size); |
| 793 | AllocCodeSpace(jit_cache_size); | ||
| 794 | } | 858 | } |
| 795 | 859 | ||
| 796 | void JitCompiler::Clear() { | 860 | JitShader::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 | ||
| 20 | namespace Shader { | 23 | namespace 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) |
| 23 | constexpr size_t jit_shader_size = 1024 * 512; | 26 | constexpr size_t MAX_SHADER_SIZE = 1024 * 64; |
| 24 | /// Memory allocated for the JIT code space cache | ||
| 25 | constexpr size_t jit_cache_size = 1024 * 1024 * 8; | ||
| 26 | |||
| 27 | using 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 | */ |
| 33 | class JitCompiler : public Gen::XCodeBlock { | 32 | class JitShader : public Gen::XCodeBlock { |
| 34 | public: | 33 | public: |
| 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 | ||
| 68 | private: | 69 | private: |
| 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"); |