summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/CMakeLists.txt13
-rw-r--r--src/audio_core/hle/common.h2
-rw-r--r--src/audio_core/hle/dsp.cpp24
-rw-r--r--src/audio_core/hle/dsp.h14
-rw-r--r--src/audio_core/hle/filter.h1
-rw-r--r--src/audio_core/hle/pipe.cpp9
-rw-r--r--src/audio_core/hle/pipe.h12
-rw-r--r--src/audio_core/hle/source.cpp320
-rw-r--r--src/audio_core/hle/source.h144
-rw-r--r--src/audio_core/sdl2_sink.cpp126
-rw-r--r--src/audio_core/sdl2_sink.h30
-rw-r--r--src/audio_core/sink.h2
-rw-r--r--src/audio_core/sink_details.cpp7
-rw-r--r--src/citra/config.cpp2
-rw-r--r--src/citra/default_ini.h2
-rw-r--r--src/citra/emu_window/emu_window_sdl2.cpp7
-rw-r--r--src/citra_qt/CMakeLists.txt1
-rw-r--r--src/citra_qt/debugger/graphics_breakpoints.cpp2
-rw-r--r--src/citra_qt/debugger/graphics_vertex_shader.cpp6
-rw-r--r--src/citra_qt/game_list.cpp16
-rw-r--r--src/citra_qt/game_list.h2
-rw-r--r--src/citra_qt/game_list_p.h106
-rw-r--r--src/citra_qt/main.cpp11
-rw-r--r--src/citra_qt/util/util.cpp2
-rw-r--r--src/common/logging/backend.cpp1
-rw-r--r--src/common/logging/log.h3
-rw-r--r--src/common/swap.h68
-rw-r--r--src/core/arm/dyncom/arm_dyncom.cpp2
-rw-r--r--src/core/core.cpp2
-rw-r--r--src/core/gdbstub/gdbstub.cpp20
-rw-r--r--src/core/hle/applets/mii_selector.cpp24
-rw-r--r--src/core/hle/applets/mii_selector.h50
-rw-r--r--src/core/hle/applets/swkbd.cpp20
-rw-r--r--src/core/hle/applets/swkbd.h7
-rw-r--r--src/core/hle/hle.cpp20
-rw-r--r--src/core/hle/hle.h4
-rw-r--r--src/core/hle/kernel/process.h2
-rw-r--r--src/core/hle/kernel/thread.cpp3
-rw-r--r--src/core/hle/service/apt/apt.h15
-rw-r--r--src/core/hle/service/dsp_dsp.cpp4
-rw-r--r--src/core/hle/svc.cpp5
-rw-r--r--src/core/hw/gpu.cpp4
-rw-r--r--src/core/loader/3dsx.cpp33
-rw-r--r--src/core/loader/3dsx.h9
-rw-r--r--src/core/loader/loader.cpp53
-rw-r--r--src/core/loader/loader.h57
-rw-r--r--src/core/loader/ncch.cpp25
-rw-r--r--src/core/loader/ncch.h7
-rw-r--r--src/core/tracer/recorder.cpp24
-rw-r--r--src/video_core/command_processor.cpp10
-rw-r--r--src/video_core/debug_utils/debug_utils.cpp9
-rw-r--r--src/video_core/debug_utils/debug_utils.h2
-rw-r--r--src/video_core/pica_state.h2
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp2
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp8
55 files changed, 1167 insertions, 189 deletions
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index 5a2747e78..13b5e400e 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -4,6 +4,7 @@ set(SRCS
4 hle/dsp.cpp 4 hle/dsp.cpp
5 hle/filter.cpp 5 hle/filter.cpp
6 hle/pipe.cpp 6 hle/pipe.cpp
7 hle/source.cpp
7 interpolate.cpp 8 interpolate.cpp
8 sink_details.cpp 9 sink_details.cpp
9 ) 10 )
@@ -15,6 +16,7 @@ set(HEADERS
15 hle/dsp.h 16 hle/dsp.h
16 hle/filter.h 17 hle/filter.h
17 hle/pipe.h 18 hle/pipe.h
19 hle/source.h
18 interpolate.h 20 interpolate.h
19 null_sink.h 21 null_sink.h
20 sink.h 22 sink.h
@@ -23,7 +25,18 @@ set(HEADERS
23 25
24include_directories(../../externals/soundtouch/include) 26include_directories(../../externals/soundtouch/include)
25 27
28if(SDL2_FOUND)
29 set(SRCS ${SRCS} sdl2_sink.cpp)
30 set(HEADERS ${HEADERS} sdl2_sink.h)
31 include_directories(${SDL2_INCLUDE_DIR})
32endif()
33
26create_directory_groups(${SRCS} ${HEADERS}) 34create_directory_groups(${SRCS} ${HEADERS})
27 35
28add_library(audio_core STATIC ${SRCS} ${HEADERS}) 36add_library(audio_core STATIC ${SRCS} ${HEADERS})
29target_link_libraries(audio_core SoundTouch) 37target_link_libraries(audio_core SoundTouch)
38
39if(SDL2_FOUND)
40 target_link_libraries(audio_core ${SDL2_LIBRARY})
41 set_property(TARGET audio_core APPEND PROPERTY COMPILE_DEFINITIONS HAVE_SDL2)
42endif()
diff --git a/src/audio_core/hle/common.h b/src/audio_core/hle/common.h
index 7910f42ae..596b67eaf 100644
--- a/src/audio_core/hle/common.h
+++ b/src/audio_core/hle/common.h
@@ -27,7 +27,7 @@ using QuadFrame32 = std::array<std::array<s32, 4>, samples_per_frame>;
27 */ 27 */
28template<typename FrameT, typename FilterT> 28template<typename FrameT, typename FilterT>
29void FilterFrame(FrameT& frame, FilterT& filter) { 29void FilterFrame(FrameT& frame, FilterT& filter) {
30 std::transform(frame.begin(), frame.end(), frame.begin(), [&filter](const typename FrameT::value_type& sample) { 30 std::transform(frame.begin(), frame.end(), frame.begin(), [&filter](const auto& sample) {
31 return filter.ProcessSample(sample); 31 return filter.ProcessSample(sample);
32 }); 32 });
33} 33}
diff --git a/src/audio_core/hle/dsp.cpp b/src/audio_core/hle/dsp.cpp
index 4d44bd2d9..0cdbdb06a 100644
--- a/src/audio_core/hle/dsp.cpp
+++ b/src/audio_core/hle/dsp.cpp
@@ -2,10 +2,12 @@
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 <array>
5#include <memory> 6#include <memory>
6 7
7#include "audio_core/hle/dsp.h" 8#include "audio_core/hle/dsp.h"
8#include "audio_core/hle/pipe.h" 9#include "audio_core/hle/pipe.h"
10#include "audio_core/hle/source.h"
9#include "audio_core/sink.h" 11#include "audio_core/sink.h"
10 12
11namespace DSP { 13namespace DSP {
@@ -38,16 +40,38 @@ static SharedMemory& WriteRegion() {
38 return g_regions[1 - CurrentRegionIndex()]; 40 return g_regions[1 - CurrentRegionIndex()];
39} 41}
40 42
43static std::array<Source, num_sources> sources = {
44 Source(0), Source(1), Source(2), Source(3), Source(4), Source(5),
45 Source(6), Source(7), Source(8), Source(9), Source(10), Source(11),
46 Source(12), Source(13), Source(14), Source(15), Source(16), Source(17),
47 Source(18), Source(19), Source(20), Source(21), Source(22), Source(23)
48};
49
41static std::unique_ptr<AudioCore::Sink> sink; 50static std::unique_ptr<AudioCore::Sink> sink;
42 51
43void Init() { 52void Init() {
44 DSP::HLE::ResetPipes(); 53 DSP::HLE::ResetPipes();
54 for (auto& source : sources) {
55 source.Reset();
56 }
45} 57}
46 58
47void Shutdown() { 59void Shutdown() {
48} 60}
49 61
50bool Tick() { 62bool Tick() {
63 SharedMemory& read = ReadRegion();
64 SharedMemory& write = WriteRegion();
65
66 std::array<QuadFrame32, 3> intermediate_mixes = {};
67
68 for (size_t i = 0; i < num_sources; i++) {
69 write.source_statuses.status[i] = sources[i].Tick(read.source_configurations.config[i], read.adpcm_coefficients.coeff[i]);
70 for (size_t mix = 0; mix < 3; mix++) {
71 sources[i].MixInto(intermediate_mixes[mix], mix);
72 }
73 }
74
51 return true; 75 return true;
52} 76}
53 77
diff --git a/src/audio_core/hle/dsp.h b/src/audio_core/hle/dsp.h
index 4f2410c27..f6e53f68f 100644
--- a/src/audio_core/hle/dsp.h
+++ b/src/audio_core/hle/dsp.h
@@ -33,13 +33,9 @@ namespace HLE {
33// double-buffer. The frame counter is located as the very last u16 of each region and is incremented 33// double-buffer. The frame counter is located as the very last u16 of each region and is incremented
34// each audio tick. 34// each audio tick.
35 35
36struct SharedMemory;
37
38constexpr VAddr region0_base = 0x1FF50000; 36constexpr VAddr region0_base = 0x1FF50000;
39constexpr VAddr region1_base = 0x1FF70000; 37constexpr VAddr region1_base = 0x1FF70000;
40 38
41extern std::array<SharedMemory, 2> g_regions;
42
43/** 39/**
44 * The DSP is native 16-bit. The DSP also appears to be big-endian. When reading 32-bit numbers from 40 * The DSP is native 16-bit. The DSP also appears to be big-endian. When reading 32-bit numbers from
45 * its memory regions, the higher and lower 16-bit halves are swapped compared to the little-endian 41 * its memory regions, the higher and lower 16-bit halves are swapped compared to the little-endian
@@ -169,9 +165,9 @@ struct SourceConfiguration {
169 float_le rate_multiplier; 165 float_le rate_multiplier;
170 166
171 enum class InterpolationMode : u8 { 167 enum class InterpolationMode : u8 {
172 None = 0, 168 Polyphase = 0,
173 Linear = 1, 169 Linear = 1,
174 Polyphase = 2 170 None = 2
175 }; 171 };
176 172
177 InterpolationMode interpolation_mode; 173 InterpolationMode interpolation_mode;
@@ -318,10 +314,10 @@ ASSERT_DSP_STRUCT(SourceConfiguration::Configuration::Buffer, 20);
318struct SourceStatus { 314struct SourceStatus {
319 struct Status { 315 struct Status {
320 u8 is_enabled; ///< Is this channel enabled? (Doesn't have to be playing anything.) 316 u8 is_enabled; ///< Is this channel enabled? (Doesn't have to be playing anything.)
321 u8 previous_buffer_id_dirty; ///< Non-zero when previous_buffer_id changes 317 u8 current_buffer_id_dirty; ///< Non-zero when current_buffer_id changes
322 u16_le sync; ///< Is set by the DSP to the value of SourceConfiguration::sync 318 u16_le sync; ///< Is set by the DSP to the value of SourceConfiguration::sync
323 u32_dsp buffer_position; ///< Number of samples into the current buffer 319 u32_dsp buffer_position; ///< Number of samples into the current buffer
324 u16_le previous_buffer_id; ///< Updated when a buffer finishes playing 320 u16_le current_buffer_id; ///< Updated when a buffer finishes playing
325 INSERT_PADDING_DSPWORDS(1); 321 INSERT_PADDING_DSPWORDS(1);
326 }; 322 };
327 323
@@ -507,6 +503,8 @@ struct SharedMemory {
507}; 503};
508ASSERT_DSP_STRUCT(SharedMemory, 0x8000); 504ASSERT_DSP_STRUCT(SharedMemory, 0x8000);
509 505
506extern std::array<SharedMemory, 2> g_regions;
507
510// Structures must have an offset that is a multiple of two. 508// Structures must have an offset that is a multiple of two.
511static_assert(offsetof(SharedMemory, frame_counter) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); 509static_assert(offsetof(SharedMemory, frame_counter) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
512static_assert(offsetof(SharedMemory, source_configurations) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); 510static_assert(offsetof(SharedMemory, source_configurations) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
diff --git a/src/audio_core/hle/filter.h b/src/audio_core/hle/filter.h
index 75738f600..43d2035cd 100644
--- a/src/audio_core/hle/filter.h
+++ b/src/audio_core/hle/filter.h
@@ -16,6 +16,7 @@ namespace HLE {
16 16
17/// Preprocessing filters. There is an independent set of filters for each Source. 17/// Preprocessing filters. There is an independent set of filters for each Source.
18class SourceFilters final { 18class SourceFilters final {
19public:
19 SourceFilters() { Reset(); } 20 SourceFilters() { Reset(); }
20 21
21 /// Reset internal state. 22 /// Reset internal state.
diff --git a/src/audio_core/hle/pipe.cpp b/src/audio_core/hle/pipe.cpp
index 03280780f..44dff1345 100644
--- a/src/audio_core/hle/pipe.cpp
+++ b/src/audio_core/hle/pipe.cpp
@@ -36,12 +36,17 @@ std::vector<u8> PipeRead(DspPipe pipe_number, u32 length) {
36 return {}; 36 return {};
37 } 37 }
38 38
39 if (length > UINT16_MAX) { // Can only read at most UINT16_MAX from the pipe
40 LOG_ERROR(Audio_DSP, "length of %u greater than max of %u", length, UINT16_MAX);
41 return {};
42 }
43
39 std::vector<u8>& data = pipe_data[pipe_index]; 44 std::vector<u8>& data = pipe_data[pipe_index];
40 45
41 if (length > data.size()) { 46 if (length > data.size()) {
42 LOG_WARNING(Audio_DSP, "pipe_number = %zu is out of data, application requested read of %u but %zu remain", 47 LOG_WARNING(Audio_DSP, "pipe_number = %zu is out of data, application requested read of %u but %zu remain",
43 pipe_index, length, data.size()); 48 pipe_index, length, data.size());
44 length = data.size(); 49 length = static_cast<u32>(data.size());
45 } 50 }
46 51
47 if (length == 0) 52 if (length == 0)
@@ -94,7 +99,7 @@ static void AudioPipeWriteStructAddresses() {
94 }; 99 };
95 100
96 // Begin with a u16 denoting the number of structs. 101 // Begin with a u16 denoting the number of structs.
97 WriteU16(DspPipe::Audio, struct_addresses.size()); 102 WriteU16(DspPipe::Audio, static_cast<u16>(struct_addresses.size()));
98 // Then write the struct addresses. 103 // Then write the struct addresses.
99 for (u16 addr : struct_addresses) { 104 for (u16 addr : struct_addresses) {
100 WriteU16(DspPipe::Audio, addr); 105 WriteU16(DspPipe::Audio, addr);
diff --git a/src/audio_core/hle/pipe.h b/src/audio_core/hle/pipe.h
index 64d97f8ba..b714c0496 100644
--- a/src/audio_core/hle/pipe.h
+++ b/src/audio_core/hle/pipe.h
@@ -24,10 +24,14 @@ enum class DspPipe {
24constexpr size_t NUM_DSP_PIPE = 8; 24constexpr size_t NUM_DSP_PIPE = 8;
25 25
26/** 26/**
27 * Read a DSP pipe. 27 * Reads `length` bytes from the DSP pipe identified with `pipe_number`.
28 * @param pipe_number The Pipe ID 28 * @note Can read up to the maximum value of a u16 in bytes (65,535).
29 * @param length How much data to request. 29 * @note IF an error is encoutered with either an invalid `pipe_number` or `length` value, an empty vector will be returned.
30 * @return The data read from the pipe. The size of this vector can be less than the length requested. 30 * @note IF `length` is set to 0, an empty vector will be returned.
31 * @note IF `length` is greater than the amount of data available, this function will only read the available amount.
32 * @param pipe_number a `DspPipe`
33 * @param length the number of bytes to read. The max is 65,535 (max of u16).
34 * @returns a vector of bytes from the specified pipe. On error, will be empty.
31 */ 35 */
32std::vector<u8> PipeRead(DspPipe pipe_number, u32 length); 36std::vector<u8> PipeRead(DspPipe pipe_number, u32 length);
33 37
diff --git a/src/audio_core/hle/source.cpp b/src/audio_core/hle/source.cpp
new file mode 100644
index 000000000..30552fe26
--- /dev/null
+++ b/src/audio_core/hle/source.cpp
@@ -0,0 +1,320 @@
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 <algorithm>
6#include <array>
7
8#include "audio_core/codec.h"
9#include "audio_core/hle/common.h"
10#include "audio_core/hle/source.h"
11#include "audio_core/interpolate.h"
12
13#include "common/assert.h"
14#include "common/logging/log.h"
15
16#include "core/memory.h"
17
18namespace DSP {
19namespace HLE {
20
21SourceStatus::Status Source::Tick(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]) {
22 ParseConfig(config, adpcm_coeffs);
23
24 if (state.enabled) {
25 GenerateFrame();
26 }
27
28 return GetCurrentStatus();
29}
30
31void Source::MixInto(QuadFrame32& dest, size_t intermediate_mix_id) const {
32 if (!state.enabled)
33 return;
34
35 const std::array<float, 4>& gains = state.gain.at(intermediate_mix_id);
36 for (size_t samplei = 0; samplei < samples_per_frame; samplei++) {
37 // Conversion from stereo (current_frame) to quadraphonic (dest) occurs here.
38 dest[samplei][0] += static_cast<s32>(gains[0] * current_frame[samplei][0]);
39 dest[samplei][1] += static_cast<s32>(gains[1] * current_frame[samplei][1]);
40 dest[samplei][2] += static_cast<s32>(gains[2] * current_frame[samplei][0]);
41 dest[samplei][3] += static_cast<s32>(gains[3] * current_frame[samplei][1]);
42 }
43}
44
45void Source::Reset() {
46 current_frame.fill({});
47 state = {};
48}
49
50void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]) {
51 if (!config.dirty_raw) {
52 return;
53 }
54
55 if (config.reset_flag) {
56 config.reset_flag.Assign(0);
57 Reset();
58 LOG_TRACE(Audio_DSP, "source_id=%zu reset", source_id);
59 }
60
61 if (config.partial_reset_flag) {
62 config.partial_reset_flag.Assign(0);
63 state.input_queue = std::priority_queue<Buffer, std::vector<Buffer>, BufferOrder>{};
64 LOG_TRACE(Audio_DSP, "source_id=%zu partial_reset", source_id);
65 }
66
67 if (config.enable_dirty) {
68 config.enable_dirty.Assign(0);
69 state.enabled = config.enable != 0;
70 LOG_TRACE(Audio_DSP, "source_id=%zu enable=%d", source_id, state.enabled);
71 }
72
73 if (config.sync_dirty) {
74 config.sync_dirty.Assign(0);
75 state.sync = config.sync;
76 LOG_TRACE(Audio_DSP, "source_id=%zu sync=%u", source_id, state.sync);
77 }
78
79 if (config.rate_multiplier_dirty) {
80 config.rate_multiplier_dirty.Assign(0);
81 state.rate_multiplier = config.rate_multiplier;
82 LOG_TRACE(Audio_DSP, "source_id=%zu rate=%f", source_id, state.rate_multiplier);
83
84 if (state.rate_multiplier <= 0) {
85 LOG_ERROR(Audio_DSP, "Was given an invalid rate multiplier: source_id=%zu rate=%f", source_id, state.rate_multiplier);
86 state.rate_multiplier = 1.0f;
87 // Note: Actual firmware starts producing garbage if this occurs.
88 }
89 }
90
91 if (config.adpcm_coefficients_dirty) {
92 config.adpcm_coefficients_dirty.Assign(0);
93 std::transform(adpcm_coeffs, adpcm_coeffs + state.adpcm_coeffs.size(), state.adpcm_coeffs.begin(),
94 [](const auto& coeff) { return static_cast<s16>(coeff); });
95 LOG_TRACE(Audio_DSP, "source_id=%zu adpcm update", source_id);
96 }
97
98 if (config.gain_0_dirty) {
99 config.gain_0_dirty.Assign(0);
100 std::transform(config.gain[0], config.gain[0] + state.gain[0].size(), state.gain[0].begin(),
101 [](const auto& coeff) { return static_cast<float>(coeff); });
102 LOG_TRACE(Audio_DSP, "source_id=%zu gain 0 update", source_id);
103 }
104
105 if (config.gain_1_dirty) {
106 config.gain_1_dirty.Assign(0);
107 std::transform(config.gain[1], config.gain[1] + state.gain[1].size(), state.gain[1].begin(),
108 [](const auto& coeff) { return static_cast<float>(coeff); });
109 LOG_TRACE(Audio_DSP, "source_id=%zu gain 1 update", source_id);
110 }
111
112 if (config.gain_2_dirty) {
113 config.gain_2_dirty.Assign(0);
114 std::transform(config.gain[2], config.gain[2] + state.gain[2].size(), state.gain[2].begin(),
115 [](const auto& coeff) { return static_cast<float>(coeff); });
116 LOG_TRACE(Audio_DSP, "source_id=%zu gain 2 update", source_id);
117 }
118
119 if (config.filters_enabled_dirty) {
120 config.filters_enabled_dirty.Assign(0);
121 state.filters.Enable(config.simple_filter_enabled.ToBool(), config.biquad_filter_enabled.ToBool());
122 LOG_TRACE(Audio_DSP, "source_id=%zu enable_simple=%hu enable_biquad=%hu",
123 source_id, config.simple_filter_enabled.Value(), config.biquad_filter_enabled.Value());
124 }
125
126 if (config.simple_filter_dirty) {
127 config.simple_filter_dirty.Assign(0);
128 state.filters.Configure(config.simple_filter);
129 LOG_TRACE(Audio_DSP, "source_id=%zu simple filter update", source_id);
130 }
131
132 if (config.biquad_filter_dirty) {
133 config.biquad_filter_dirty.Assign(0);
134 state.filters.Configure(config.biquad_filter);
135 LOG_TRACE(Audio_DSP, "source_id=%zu biquad filter update", source_id);
136 }
137
138 if (config.interpolation_dirty) {
139 config.interpolation_dirty.Assign(0);
140 state.interpolation_mode = config.interpolation_mode;
141 LOG_TRACE(Audio_DSP, "source_id=%zu interpolation_mode=%zu", source_id, static_cast<size_t>(state.interpolation_mode));
142 }
143
144 if (config.format_dirty || config.embedded_buffer_dirty) {
145 config.format_dirty.Assign(0);
146 state.format = config.format;
147 LOG_TRACE(Audio_DSP, "source_id=%zu format=%zu", source_id, static_cast<size_t>(state.format));
148 }
149
150 if (config.mono_or_stereo_dirty || config.embedded_buffer_dirty) {
151 config.mono_or_stereo_dirty.Assign(0);
152 state.mono_or_stereo = config.mono_or_stereo;
153 LOG_TRACE(Audio_DSP, "source_id=%zu mono_or_stereo=%zu", source_id, static_cast<size_t>(state.mono_or_stereo));
154 }
155
156 if (config.embedded_buffer_dirty) {
157 config.embedded_buffer_dirty.Assign(0);
158 state.input_queue.emplace(Buffer{
159 config.physical_address,
160 config.length,
161 static_cast<u8>(config.adpcm_ps),
162 { config.adpcm_yn[0], config.adpcm_yn[1] },
163 config.adpcm_dirty.ToBool(),
164 config.is_looping.ToBool(),
165 config.buffer_id,
166 state.mono_or_stereo,
167 state.format,
168 false
169 });
170 LOG_TRACE(Audio_DSP, "enqueuing embedded addr=0x%08x len=%u id=%hu", config.physical_address, config.length, config.buffer_id);
171 }
172
173 if (config.buffer_queue_dirty) {
174 config.buffer_queue_dirty.Assign(0);
175 for (size_t i = 0; i < 4; i++) {
176 if (config.buffers_dirty & (1 << i)) {
177 const auto& b = config.buffers[i];
178 state.input_queue.emplace(Buffer{
179 b.physical_address,
180 b.length,
181 static_cast<u8>(b.adpcm_ps),
182 { b.adpcm_yn[0], b.adpcm_yn[1] },
183 b.adpcm_dirty != 0,
184 b.is_looping != 0,
185 b.buffer_id,
186 state.mono_or_stereo,
187 state.format,
188 true
189 });
190 LOG_TRACE(Audio_DSP, "enqueuing queued %zu addr=0x%08x len=%u id=%hu", i, b.physical_address, b.length, b.buffer_id);
191 }
192 }
193 config.buffers_dirty = 0;
194 }
195
196 if (config.dirty_raw) {
197 LOG_DEBUG(Audio_DSP, "source_id=%zu remaining_dirty=%x", source_id, config.dirty_raw);
198 }
199
200 config.dirty_raw = 0;
201}
202
203void Source::GenerateFrame() {
204 current_frame.fill({});
205
206 if (state.current_buffer.empty() && !DequeueBuffer()) {
207 state.enabled = false;
208 state.buffer_update = true;
209 state.current_buffer_id = 0;
210 return;
211 }
212
213 size_t frame_position = 0;
214
215 state.current_sample_number = state.next_sample_number;
216 while (frame_position < current_frame.size()) {
217 if (state.current_buffer.empty() && !DequeueBuffer()) {
218 break;
219 }
220
221 const size_t size_to_copy = std::min(state.current_buffer.size(), current_frame.size() - frame_position);
222
223 std::copy(state.current_buffer.begin(), state.current_buffer.begin() + size_to_copy, current_frame.begin() + frame_position);
224 state.current_buffer.erase(state.current_buffer.begin(), state.current_buffer.begin() + size_to_copy);
225
226 frame_position += size_to_copy;
227 state.next_sample_number += static_cast<u32>(size_to_copy);
228 }
229
230 state.filters.ProcessFrame(current_frame);
231}
232
233
234bool Source::DequeueBuffer() {
235 ASSERT_MSG(state.current_buffer.empty(), "Shouldn't dequeue; we still have data in current_buffer");
236
237 if (state.input_queue.empty())
238 return false;
239
240 const Buffer buf = state.input_queue.top();
241 state.input_queue.pop();
242
243 if (buf.adpcm_dirty) {
244 state.adpcm_state.yn1 = buf.adpcm_yn[0];
245 state.adpcm_state.yn2 = buf.adpcm_yn[1];
246 }
247
248 if (buf.is_looping) {
249 LOG_ERROR(Audio_DSP, "Looped buffers are unimplemented at the moment");
250 }
251
252 const u8* const memory = Memory::GetPhysicalPointer(buf.physical_address);
253 if (memory) {
254 const unsigned num_channels = buf.mono_or_stereo == MonoOrStereo::Stereo ? 2 : 1;
255 switch (buf.format) {
256 case Format::PCM8:
257 state.current_buffer = Codec::DecodePCM8(num_channels, memory, buf.length);
258 break;
259 case Format::PCM16:
260 state.current_buffer = Codec::DecodePCM16(num_channels, memory, buf.length);
261 break;
262 case Format::ADPCM:
263 DEBUG_ASSERT(num_channels == 1);
264 state.current_buffer = Codec::DecodeADPCM(memory, buf.length, state.adpcm_coeffs, state.adpcm_state);
265 break;
266 default:
267 UNIMPLEMENTED();
268 break;
269 }
270 } else {
271 LOG_WARNING(Audio_DSP, "source_id=%zu buffer_id=%hu length=%u: Invalid physical address 0x%08X",
272 source_id, buf.buffer_id, buf.length, buf.physical_address);
273 state.current_buffer.clear();
274 return true;
275 }
276
277 switch (state.interpolation_mode) {
278 case InterpolationMode::None:
279 state.current_buffer = AudioInterp::None(state.interp_state, state.current_buffer, state.rate_multiplier);
280 break;
281 case InterpolationMode::Linear:
282 state.current_buffer = AudioInterp::Linear(state.interp_state, state.current_buffer, state.rate_multiplier);
283 break;
284 case InterpolationMode::Polyphase:
285 // TODO(merry): Implement polyphase interpolation
286 state.current_buffer = AudioInterp::Linear(state.interp_state, state.current_buffer, state.rate_multiplier);
287 break;
288 default:
289 UNIMPLEMENTED();
290 break;
291 }
292
293 state.current_sample_number = 0;
294 state.next_sample_number = 0;
295 state.current_buffer_id = buf.buffer_id;
296 state.buffer_update = buf.from_queue;
297
298 LOG_TRACE(Audio_DSP, "source_id=%zu buffer_id=%hu from_queue=%s current_buffer.size()=%zu",
299 source_id, buf.buffer_id, buf.from_queue ? "true" : "false", state.current_buffer.size());
300 return true;
301}
302
303SourceStatus::Status Source::GetCurrentStatus() {
304 SourceStatus::Status ret;
305
306 // Applications depend on the correct emulation of
307 // current_buffer_id_dirty and current_buffer_id to synchronise
308 // audio with video.
309 ret.is_enabled = state.enabled;
310 ret.current_buffer_id_dirty = state.buffer_update ? 1 : 0;
311 state.buffer_update = false;
312 ret.current_buffer_id = state.current_buffer_id;
313 ret.buffer_position = state.current_sample_number;
314 ret.sync = state.sync;
315
316 return ret;
317}
318
319} // namespace HLE
320} // namespace DSP
diff --git a/src/audio_core/hle/source.h b/src/audio_core/hle/source.h
new file mode 100644
index 000000000..7ee08d424
--- /dev/null
+++ b/src/audio_core/hle/source.h
@@ -0,0 +1,144 @@
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#include <queue>
9#include <vector>
10
11#include "audio_core/codec.h"
12#include "audio_core/hle/common.h"
13#include "audio_core/hle/dsp.h"
14#include "audio_core/hle/filter.h"
15#include "audio_core/interpolate.h"
16
17#include "common/common_types.h"
18
19namespace DSP {
20namespace HLE {
21
22/**
23 * This module performs:
24 * - Buffer management
25 * - Decoding of buffers
26 * - Buffer resampling and interpolation
27 * - Per-source filtering (SimpleFilter, BiquadFilter)
28 * - Per-source gain
29 * - Other per-source processing
30 */
31class Source final {
32public:
33 explicit Source(size_t source_id_) : source_id(source_id_) {
34 Reset();
35 }
36
37 /// Resets internal state.
38 void Reset();
39
40 /**
41 * This is called once every audio frame. This performs per-source processing every frame.
42 * @param config The new configuration we've got for this Source from the application.
43 * @param adpcm_coeffs ADPCM coefficients to use if config tells us to use them (may contain invalid values otherwise).
44 * @return The current status of this Source. This is given back to the emulated application via SharedMemory.
45 */
46 SourceStatus::Status Tick(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]);
47
48 /**
49 * Mix this source's output into dest, using the gains for the `intermediate_mix_id`-th intermediate mixer.
50 * @param dest The QuadFrame32 to mix into.
51 * @param intermediate_mix_id The id of the intermediate mix whose gains we are using.
52 */
53 void MixInto(QuadFrame32& dest, size_t intermediate_mix_id) const;
54
55private:
56 const size_t source_id;
57 StereoFrame16 current_frame;
58
59 using Format = SourceConfiguration::Configuration::Format;
60 using InterpolationMode = SourceConfiguration::Configuration::InterpolationMode;
61 using MonoOrStereo = SourceConfiguration::Configuration::MonoOrStereo;
62
63 /// Internal representation of a buffer for our buffer queue
64 struct Buffer {
65 PAddr physical_address;
66 u32 length;
67 u8 adpcm_ps;
68 std::array<u16, 2> adpcm_yn;
69 bool adpcm_dirty;
70 bool is_looping;
71 u16 buffer_id;
72
73 MonoOrStereo mono_or_stereo;
74 Format format;
75
76 bool from_queue;
77 };
78
79 struct BufferOrder {
80 bool operator() (const Buffer& a, const Buffer& b) const {
81 // Lower buffer_id comes first.
82 return a.buffer_id > b.buffer_id;
83 }
84 };
85
86 struct {
87
88 // State variables
89
90 bool enabled = false;
91 u16 sync = 0;
92
93 // Mixing
94
95 std::array<std::array<float, 4>, 3> gain = {};
96
97 // Buffer queue
98
99 std::priority_queue<Buffer, std::vector<Buffer>, BufferOrder> input_queue;
100 MonoOrStereo mono_or_stereo = MonoOrStereo::Mono;
101 Format format = Format::ADPCM;
102
103 // Current buffer
104
105 u32 current_sample_number = 0;
106 u32 next_sample_number = 0;
107 std::vector<std::array<s16, 2>> current_buffer;
108
109 // buffer_id state
110
111 bool buffer_update = false;
112 u32 current_buffer_id = 0;
113
114 // Decoding state
115
116 std::array<s16, 16> adpcm_coeffs = {};
117 Codec::ADPCMState adpcm_state = {};
118
119 // Resampling state
120
121 float rate_multiplier = 1.0;
122 InterpolationMode interpolation_mode = InterpolationMode::Polyphase;
123 AudioInterp::State interp_state = {};
124
125 // Filter state
126
127 SourceFilters filters;
128
129 } state;
130
131 // Internal functions
132
133 /// INTERNAL: Update our internal state based on the current config.
134 void ParseConfig(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]);
135 /// INTERNAL: Generate the current audio output for this frame based on our internal state.
136 void GenerateFrame();
137 /// INTERNAL: Dequeues a buffer and does preprocessing on it (decoding, resampling). Puts it into current_buffer.
138 bool DequeueBuffer();
139 /// INTERNAL: Generates a SourceStatus::Status based on our internal state.
140 SourceStatus::Status GetCurrentStatus();
141};
142
143} // namespace HLE
144} // namespace DSP
diff --git a/src/audio_core/sdl2_sink.cpp b/src/audio_core/sdl2_sink.cpp
new file mode 100644
index 000000000..dc75c04ee
--- /dev/null
+++ b/src/audio_core/sdl2_sink.cpp
@@ -0,0 +1,126 @@
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 <list>
6#include <vector>
7
8#include <SDL.h>
9
10#include "audio_core/audio_core.h"
11#include "audio_core/sdl2_sink.h"
12
13#include "common/assert.h"
14#include "common/logging/log.h"
15#include <numeric>
16
17namespace AudioCore {
18
19struct SDL2Sink::Impl {
20 unsigned int sample_rate = 0;
21
22 SDL_AudioDeviceID audio_device_id = 0;
23
24 std::list<std::vector<s16>> queue;
25
26 static void Callback(void* impl_, u8* buffer, int buffer_size_in_bytes);
27};
28
29SDL2Sink::SDL2Sink() : impl(std::make_unique<Impl>()) {
30 if (SDL_Init(SDL_INIT_AUDIO) < 0) {
31 LOG_CRITICAL(Audio_Sink, "SDL_Init(SDL_INIT_AUDIO) failed");
32 impl->audio_device_id = 0;
33 return;
34 }
35
36 SDL_AudioSpec desired_audiospec;
37 SDL_zero(desired_audiospec);
38 desired_audiospec.format = AUDIO_S16;
39 desired_audiospec.channels = 2;
40 desired_audiospec.freq = native_sample_rate;
41 desired_audiospec.samples = 1024;
42 desired_audiospec.userdata = impl.get();
43 desired_audiospec.callback = &Impl::Callback;
44
45 SDL_AudioSpec obtained_audiospec;
46 SDL_zero(obtained_audiospec);
47
48 impl->audio_device_id = SDL_OpenAudioDevice(nullptr, false, &desired_audiospec, &obtained_audiospec, 0);
49 if (impl->audio_device_id <= 0) {
50 LOG_CRITICAL(Audio_Sink, "SDL_OpenAudioDevice failed");
51 return;
52 }
53
54 impl->sample_rate = obtained_audiospec.freq;
55
56 // SDL2 audio devices start out paused, unpause it:
57 SDL_PauseAudioDevice(impl->audio_device_id, 0);
58}
59
60SDL2Sink::~SDL2Sink() {
61 if (impl->audio_device_id <= 0)
62 return;
63
64 SDL_CloseAudioDevice(impl->audio_device_id);
65}
66
67unsigned int SDL2Sink::GetNativeSampleRate() const {
68 if (impl->audio_device_id <= 0)
69 return native_sample_rate;
70
71 return impl->sample_rate;
72}
73
74void SDL2Sink::EnqueueSamples(const std::vector<s16>& samples) {
75 if (impl->audio_device_id <= 0)
76 return;
77
78 ASSERT_MSG(samples.size() % 2 == 0, "Samples must be in interleaved stereo PCM16 format (size must be a multiple of two)");
79
80 SDL_LockAudioDevice(impl->audio_device_id);
81 impl->queue.emplace_back(samples);
82 SDL_UnlockAudioDevice(impl->audio_device_id);
83}
84
85size_t SDL2Sink::SamplesInQueue() const {
86 if (impl->audio_device_id <= 0)
87 return 0;
88
89 SDL_LockAudioDevice(impl->audio_device_id);
90
91 size_t total_size = std::accumulate(impl->queue.begin(), impl->queue.end(), static_cast<size_t>(0),
92 [](size_t sum, const auto& buffer) {
93 // Division by two because each stereo sample is made of two s16.
94 return sum + buffer.size() / 2;
95 });
96
97 SDL_UnlockAudioDevice(impl->audio_device_id);
98
99 return total_size;
100}
101
102void SDL2Sink::Impl::Callback(void* impl_, u8* buffer, int buffer_size_in_bytes) {
103 Impl* impl = reinterpret_cast<Impl*>(impl_);
104
105 size_t remaining_size = static_cast<size_t>(buffer_size_in_bytes) / sizeof(s16); // Keep track of size in 16-bit increments.
106
107 while (remaining_size > 0 && !impl->queue.empty()) {
108 if (impl->queue.front().size() <= remaining_size) {
109 memcpy(buffer, impl->queue.front().data(), impl->queue.front().size() * sizeof(s16));
110 buffer += impl->queue.front().size() * sizeof(s16);
111 remaining_size -= impl->queue.front().size();
112 impl->queue.pop_front();
113 } else {
114 memcpy(buffer, impl->queue.front().data(), remaining_size * sizeof(s16));
115 buffer += remaining_size * sizeof(s16);
116 impl->queue.front().erase(impl->queue.front().begin(), impl->queue.front().begin() + remaining_size);
117 remaining_size = 0;
118 }
119 }
120
121 if (remaining_size > 0) {
122 memset(buffer, 0, remaining_size * sizeof(s16));
123 }
124}
125
126} // namespace AudioCore
diff --git a/src/audio_core/sdl2_sink.h b/src/audio_core/sdl2_sink.h
new file mode 100644
index 000000000..0f296b673
--- /dev/null
+++ b/src/audio_core/sdl2_sink.h
@@ -0,0 +1,30 @@
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 <cstddef>
8#include <memory>
9
10#include "audio_core/sink.h"
11
12namespace AudioCore {
13
14class SDL2Sink final : public Sink {
15public:
16 SDL2Sink();
17 ~SDL2Sink() override;
18
19 unsigned int GetNativeSampleRate() const override;
20
21 void EnqueueSamples(const std::vector<s16>& samples) override;
22
23 size_t SamplesInQueue() const override;
24
25private:
26 struct Impl;
27 std::unique_ptr<Impl> impl;
28};
29
30} // namespace AudioCore
diff --git a/src/audio_core/sink.h b/src/audio_core/sink.h
index cad21a85e..1c881c3d2 100644
--- a/src/audio_core/sink.h
+++ b/src/audio_core/sink.h
@@ -19,7 +19,7 @@ public:
19 virtual ~Sink() = default; 19 virtual ~Sink() = default;
20 20
21 /// The native rate of this sink. The sink expects to be fed samples that respect this. (Units: samples/sec) 21 /// The native rate of this sink. The sink expects to be fed samples that respect this. (Units: samples/sec)
22 virtual unsigned GetNativeSampleRate() const = 0; 22 virtual unsigned int GetNativeSampleRate() const = 0;
23 23
24 /** 24 /**
25 * Feed stereo samples to sink. 25 * Feed stereo samples to sink.
diff --git a/src/audio_core/sink_details.cpp b/src/audio_core/sink_details.cpp
index d2cc74103..ba5e83d17 100644
--- a/src/audio_core/sink_details.cpp
+++ b/src/audio_core/sink_details.cpp
@@ -8,10 +8,17 @@
8#include "audio_core/null_sink.h" 8#include "audio_core/null_sink.h"
9#include "audio_core/sink_details.h" 9#include "audio_core/sink_details.h"
10 10
11#ifdef HAVE_SDL2
12#include "audio_core/sdl2_sink.h"
13#endif
14
11namespace AudioCore { 15namespace AudioCore {
12 16
13// g_sink_details is ordered in terms of desirability, with the best choice at the top. 17// g_sink_details is ordered in terms of desirability, with the best choice at the top.
14const std::vector<SinkDetails> g_sink_details = { 18const std::vector<SinkDetails> g_sink_details = {
19#ifdef HAVE_SDL2
20 { "sdl2", []() { return std::make_unique<SDL2Sink>(); } },
21#endif
15 { "null", []() { return std::make_unique<NullSink>(); } }, 22 { "null", []() { return std::make_unique<NullSink>(); } },
16}; 23};
17 24
diff --git a/src/citra/config.cpp b/src/citra/config.cpp
index 0d17c80bf..c5cb4fb38 100644
--- a/src/citra/config.cpp
+++ b/src/citra/config.cpp
@@ -85,7 +85,7 @@ void Config::ReadValues() {
85 85
86 // Debugging 86 // Debugging
87 Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false); 87 Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false);
88 Settings::values.gdbstub_port = sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689); 88 Settings::values.gdbstub_port = static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689));
89} 89}
90 90
91void Config::Reload() { 91void Config::Reload() {
diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h
index 0e6171736..49126356f 100644
--- a/src/citra/default_ini.h
+++ b/src/citra/default_ini.h
@@ -58,7 +58,7 @@ bg_green =
58 58
59[Audio] 59[Audio]
60# Which audio output engine to use. 60# Which audio output engine to use.
61# auto (default): Auto-select, null: No audio output 61# auto (default): Auto-select, null: No audio output, sdl2: SDL2 (if available)
62output_engine = 62output_engine =
63 63
64[Data Storage] 64[Data Storage]
diff --git a/src/citra/emu_window/emu_window_sdl2.cpp b/src/citra/emu_window/emu_window_sdl2.cpp
index 924189f4c..12cdd9d95 100644
--- a/src/citra/emu_window/emu_window_sdl2.cpp
+++ b/src/citra/emu_window/emu_window_sdl2.cpp
@@ -9,6 +9,8 @@
9#define SDL_MAIN_HANDLED 9#define SDL_MAIN_HANDLED
10#include <SDL.h> 10#include <SDL.h>
11 11
12#include <glad/glad.h>
13
12#include "common/key_map.h" 14#include "common/key_map.h"
13#include "common/logging/log.h" 15#include "common/logging/log.h"
14#include "common/scm_rev.h" 16#include "common/scm_rev.h"
@@ -98,6 +100,11 @@ EmuWindow_SDL2::EmuWindow_SDL2() {
98 exit(1); 100 exit(1);
99 } 101 }
100 102
103 if (!gladLoadGLLoader(static_cast<GLADloadproc>(SDL_GL_GetProcAddress))) {
104 LOG_CRITICAL(Frontend, "Failed to initialize GL functions! Exiting...");
105 exit(1);
106 }
107
101 OnResize(); 108 OnResize();
102 OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); 109 OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
103 SDL_PumpEvents(); 110 SDL_PumpEvents();
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt
index cc9e0c624..3f0099200 100644
--- a/src/citra_qt/CMakeLists.txt
+++ b/src/citra_qt/CMakeLists.txt
@@ -55,6 +55,7 @@ set(HEADERS
55 configure_dialog.h 55 configure_dialog.h
56 configure_general.h 56 configure_general.h
57 game_list.h 57 game_list.h
58 game_list_p.h
58 hotkeys.h 59 hotkeys.h
59 main.h 60 main.h
60 ui_settings.h 61 ui_settings.h
diff --git a/src/citra_qt/debugger/graphics_breakpoints.cpp b/src/citra_qt/debugger/graphics_breakpoints.cpp
index c8510128a..fe66918a8 100644
--- a/src/citra_qt/debugger/graphics_breakpoints.cpp
+++ b/src/citra_qt/debugger/graphics_breakpoints.cpp
@@ -44,7 +44,7 @@ QVariant BreakPointModel::data(const QModelIndex& index, int role) const
44 { Pica::DebugContext::Event::PicaCommandProcessed, tr("Pica command processed") }, 44 { Pica::DebugContext::Event::PicaCommandProcessed, tr("Pica command processed") },
45 { Pica::DebugContext::Event::IncomingPrimitiveBatch, tr("Incoming primitive batch") }, 45 { Pica::DebugContext::Event::IncomingPrimitiveBatch, tr("Incoming primitive batch") },
46 { Pica::DebugContext::Event::FinishedPrimitiveBatch, tr("Finished primitive batch") }, 46 { Pica::DebugContext::Event::FinishedPrimitiveBatch, tr("Finished primitive batch") },
47 { Pica::DebugContext::Event::VertexLoaded, tr("Vertex loaded") }, 47 { Pica::DebugContext::Event::VertexShaderInvocation, tr("Vertex shader invocation") },
48 { Pica::DebugContext::Event::IncomingDisplayTransfer, tr("Incoming display transfer") }, 48 { Pica::DebugContext::Event::IncomingDisplayTransfer, tr("Incoming display transfer") },
49 { Pica::DebugContext::Event::GSPCommandProcessed, tr("GSP command processed") }, 49 { Pica::DebugContext::Event::GSPCommandProcessed, tr("GSP command processed") },
50 { Pica::DebugContext::Event::BufferSwapped, tr("Buffers swapped") } 50 { Pica::DebugContext::Event::BufferSwapped, tr("Buffers swapped") }
diff --git a/src/citra_qt/debugger/graphics_vertex_shader.cpp b/src/citra_qt/debugger/graphics_vertex_shader.cpp
index d648d4640..854f6ff16 100644
--- a/src/citra_qt/debugger/graphics_vertex_shader.cpp
+++ b/src/citra_qt/debugger/graphics_vertex_shader.cpp
@@ -365,7 +365,7 @@ GraphicsVertexShaderWidget::GraphicsVertexShaderWidget(std::shared_ptr< Pica::De
365 input_data[i]->setValidator(new QDoubleValidator(input_data[i])); 365 input_data[i]->setValidator(new QDoubleValidator(input_data[i]));
366 } 366 }
367 367
368 breakpoint_warning = new QLabel(tr("(data only available at VertexLoaded breakpoints)")); 368 breakpoint_warning = new QLabel(tr("(data only available at vertex shader invocation breakpoints)"));
369 369
370 // TODO: Add some button for jumping to the shader entry point 370 // TODO: Add some button for jumping to the shader entry point
371 371
@@ -454,7 +454,7 @@ GraphicsVertexShaderWidget::GraphicsVertexShaderWidget(std::shared_ptr< Pica::De
454 454
455void GraphicsVertexShaderWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) { 455void GraphicsVertexShaderWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) {
456 auto input = static_cast<Pica::Shader::InputVertex*>(data); 456 auto input = static_cast<Pica::Shader::InputVertex*>(data);
457 if (event == Pica::DebugContext::Event::VertexLoaded) { 457 if (event == Pica::DebugContext::Event::VertexShaderInvocation) {
458 Reload(true, data); 458 Reload(true, data);
459 } else { 459 } else {
460 // No vertex data is retrievable => invalidate currently stored vertex data 460 // No vertex data is retrievable => invalidate currently stored vertex data
@@ -515,7 +515,7 @@ void GraphicsVertexShaderWidget::Reload(bool replace_vertex_data, void* vertex_d
515 } 515 }
516 516
517 // Initialize debug info text for current cycle count 517 // Initialize debug info text for current cycle count
518 cycle_index->setMaximum(debug_data.records.size() - 1); 518 cycle_index->setMaximum(static_cast<int>(debug_data.records.size() - 1));
519 OnCycleIndexChanged(cycle_index->value()); 519 OnCycleIndexChanged(cycle_index->value());
520 520
521 model->endResetModel(); 521 model->endResetModel();
diff --git a/src/citra_qt/game_list.cpp b/src/citra_qt/game_list.cpp
index d14532102..d4ac9c96e 100644
--- a/src/citra_qt/game_list.cpp
+++ b/src/citra_qt/game_list.cpp
@@ -34,8 +34,8 @@ GameList::GameList(QWidget* parent)
34 tree_view->setUniformRowHeights(true); 34 tree_view->setUniformRowHeights(true);
35 35
36 item_model->insertColumns(0, COLUMN_COUNT); 36 item_model->insertColumns(0, COLUMN_COUNT);
37 item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, "File type");
38 item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, "Name"); 37 item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, "Name");
38 item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, "File type");
39 item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "Size"); 39 item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "Size");
40 40
41 connect(tree_view, SIGNAL(activated(const QModelIndex&)), this, SLOT(ValidateEntry(const QModelIndex&))); 41 connect(tree_view, SIGNAL(activated(const QModelIndex&)), this, SLOT(ValidateEntry(const QModelIndex&)));
@@ -109,7 +109,11 @@ void GameList::SaveInterfaceLayout()
109void GameList::LoadInterfaceLayout() 109void GameList::LoadInterfaceLayout()
110{ 110{
111 auto header = tree_view->header(); 111 auto header = tree_view->header();
112 header->restoreState(UISettings::values.gamelist_header_state); 112 if (!header->restoreState(UISettings::values.gamelist_header_state)) {
113 // We are using the name column to display icons and titles
114 // so make it as large as possible as default.
115 header->resizeSection(COLUMN_NAME, header->width());
116 }
113 117
114 item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder()); 118 item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder());
115} 119}
@@ -143,9 +147,15 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, bool d
143 LOG_WARNING(Frontend, "Filetype and extension of file %s do not match.", physical_name.c_str()); 147 LOG_WARNING(Frontend, "Filetype and extension of file %s do not match.", physical_name.c_str());
144 } 148 }
145 149
150 std::vector<u8> smdh;
151 std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(FileUtil::IOFile(physical_name, "rb"), filetype, filename_filename, physical_name);
152
153 if (loader)
154 loader->ReadIcon(smdh);
155
146 emit EntryReady({ 156 emit EntryReady({
157 new GameListItemPath(QString::fromStdString(physical_name), smdh),
147 new GameListItem(QString::fromStdString(Loader::GetFileTypeString(filetype))), 158 new GameListItem(QString::fromStdString(Loader::GetFileTypeString(filetype))),
148 new GameListItemPath(QString::fromStdString(physical_name)),
149 new GameListItemSize(FileUtil::GetSize(physical_name)), 159 new GameListItemSize(FileUtil::GetSize(physical_name)),
150 }); 160 });
151 } 161 }
diff --git a/src/citra_qt/game_list.h b/src/citra_qt/game_list.h
index 48febdc60..198674f04 100644
--- a/src/citra_qt/game_list.h
+++ b/src/citra_qt/game_list.h
@@ -20,8 +20,8 @@ class GameList : public QWidget {
20 20
21public: 21public:
22 enum { 22 enum {
23 COLUMN_FILE_TYPE,
24 COLUMN_NAME, 23 COLUMN_NAME,
24 COLUMN_FILE_TYPE,
25 COLUMN_SIZE, 25 COLUMN_SIZE,
26 COLUMN_COUNT, // Number of columns 26 COLUMN_COUNT, // Number of columns
27 }; 27 };
diff --git a/src/citra_qt/game_list_p.h b/src/citra_qt/game_list_p.h
index 820012bce..284f5da81 100644
--- a/src/citra_qt/game_list_p.h
+++ b/src/citra_qt/game_list_p.h
@@ -6,13 +6,85 @@
6 6
7#include <atomic> 7#include <atomic>
8 8
9#include <QImage>
9#include <QRunnable> 10#include <QRunnable>
10#include <QStandardItem> 11#include <QStandardItem>
11#include <QString> 12#include <QString>
12 13
13#include "citra_qt/util/util.h" 14#include "citra_qt/util/util.h"
14#include "common/string_util.h" 15#include "common/string_util.h"
16#include "common/color.h"
15 17
18#include "core/loader/loader.h"
19
20#include "video_core/utils.h"
21
22/**
23 * Tests if data is a valid SMDH by its length and magic number.
24 * @param smdh_data data buffer to test
25 * @return bool test result
26 */
27static bool IsValidSMDH(const std::vector<u8>& smdh_data) {
28 if (smdh_data.size() < sizeof(Loader::SMDH))
29 return false;
30
31 u32 magic;
32 memcpy(&magic, smdh_data.data(), 4);
33
34 return Loader::MakeMagic('S', 'M', 'D', 'H') == magic;
35}
36
37/**
38 * Gets game icon from SMDH
39 * @param sdmh SMDH data
40 * @param large If true, returns large icon (48x48), otherwise returns small icon (24x24)
41 * @return QPixmap game icon
42 */
43static QPixmap GetIconFromSMDH(const Loader::SMDH& smdh, bool large) {
44 u32 size;
45 const u8* icon_data;
46
47 if (large) {
48 size = 48;
49 icon_data = smdh.large_icon.data();
50 } else {
51 size = 24;
52 icon_data = smdh.small_icon.data();
53 }
54
55 QImage icon(size, size, QImage::Format::Format_RGB888);
56 for (u32 x = 0; x < size; ++x) {
57 for (u32 y = 0; y < size; ++y) {
58 u32 coarse_y = y & ~7;
59 auto v = Color::DecodeRGB565(
60 icon_data + VideoCore::GetMortonOffset(x, y, 2) + coarse_y * size * 2);
61 icon.setPixel(x, y, qRgb(v.r(), v.g(), v.b()));
62 }
63 }
64 return QPixmap::fromImage(icon);
65}
66
67/**
68 * Gets the default icon (for games without valid SMDH)
69 * @param large If true, returns large icon (48x48), otherwise returns small icon (24x24)
70 * @return QPixmap default icon
71 */
72static QPixmap GetDefaultIcon(bool large) {
73 int size = large ? 48 : 24;
74 QPixmap icon(size, size);
75 icon.fill(Qt::transparent);
76 return icon;
77}
78
79/**
80 * Gets the short game title fromn SMDH
81 * @param sdmh SMDH data
82 * @param language title language
83 * @return QString short title
84 */
85static QString GetShortTitleFromSMDH(const Loader::SMDH& smdh, Loader::SMDH::TitleLanguage language) {
86 return QString::fromUtf16(smdh.titles[static_cast<int>(language)].short_title.data());
87}
16 88
17class GameListItem : public QStandardItem { 89class GameListItem : public QStandardItem {
18 90
@@ -27,29 +99,43 @@ public:
27 * A specialization of GameListItem for path values. 99 * A specialization of GameListItem for path values.
28 * This class ensures that for every full path value it holds, a correct string representation 100 * This class ensures that for every full path value it holds, a correct string representation
29 * of just the filename (with no extension) will be displayed to the user. 101 * of just the filename (with no extension) will be displayed to the user.
102 * If this class recieves valid SMDH data, it will also display game icons and titles.
30 */ 103 */
31class GameListItemPath : public GameListItem { 104class GameListItemPath : public GameListItem {
32 105
33public: 106public:
34 static const int FullPathRole = Qt::UserRole + 1; 107 static const int FullPathRole = Qt::UserRole + 1;
108 static const int TitleRole = Qt::UserRole + 2;
35 109
36 GameListItemPath(): GameListItem() {} 110 GameListItemPath(): GameListItem() {}
37 GameListItemPath(const QString& game_path): GameListItem() 111 GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data): GameListItem()
38 { 112 {
39 setData(game_path, FullPathRole); 113 setData(game_path, FullPathRole);
114
115 if (!IsValidSMDH(smdh_data)) {
116 // SMDH is not valid, set a default icon
117 setData(GetDefaultIcon(true), Qt::DecorationRole);
118 return;
119 }
120
121 Loader::SMDH smdh;
122 memcpy(&smdh, smdh_data.data(), sizeof(Loader::SMDH));
123
124 // Get icon from SMDH
125 setData(GetIconFromSMDH(smdh, true), Qt::DecorationRole);
126
127 // Get title form SMDH
128 setData(GetShortTitleFromSMDH(smdh, Loader::SMDH::TitleLanguage::English), TitleRole);
40 } 129 }
41 130
42 void setData(const QVariant& value, int role) override 131 QVariant data(int role) const override {
43 { 132 if (role == Qt::DisplayRole) {
44 // By specializing setData for FullPathRole, we can ensure that the two string
45 // representations of the data are always accurate and in the correct format.
46 if (role == FullPathRole) {
47 std::string filename; 133 std::string filename;
48 Common::SplitPath(value.toString().toStdString(), nullptr, &filename, nullptr); 134 Common::SplitPath(data(FullPathRole).toString().toStdString(), nullptr, &filename, nullptr);
49 GameListItem::setData(QString::fromStdString(filename), Qt::DisplayRole); 135 QString title = data(TitleRole).toString();
50 GameListItem::setData(value, FullPathRole); 136 return QString::fromStdString(filename) + (title.isEmpty() ? "" : "\n " + title);
51 } else { 137 } else {
52 GameListItem::setData(value, role); 138 return GameListItem::data(role);
53 } 139 }
54 } 140 }
55}; 141};
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index f1ab29755..a85c94a4b 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -6,6 +6,9 @@
6#include <memory> 6#include <memory>
7#include <thread> 7#include <thread>
8 8
9#include <glad/glad.h>
10
11#define QT_NO_OPENGL
9#include <QDesktopWidget> 12#include <QDesktopWidget>
10#include <QtGui> 13#include <QtGui>
11#include <QFileDialog> 14#include <QFileDialog>
@@ -240,6 +243,14 @@ bool GMainWindow::InitializeSystem() {
240 if (emu_thread != nullptr) 243 if (emu_thread != nullptr)
241 ShutdownGame(); 244 ShutdownGame();
242 245
246 render_window->MakeCurrent();
247 if (!gladLoadGL()) {
248 QMessageBox::critical(this, tr("Error while starting Citra!"),
249 tr("Failed to initialize the video core!\n\n"
250 "Please ensure that your GPU supports OpenGL 3.3 and that you have the latest graphics driver."));
251 return false;
252 }
253
243 // Initialize the core emulation 254 // Initialize the core emulation
244 System::Result system_result = System::Init(render_window); 255 System::Result system_result = System::Init(render_window);
245 if (System::Result::Success != system_result) { 256 if (System::Result::Success != system_result) {
diff --git a/src/citra_qt/util/util.cpp b/src/citra_qt/util/util.cpp
index 8734a8efd..2f9beb5cc 100644
--- a/src/citra_qt/util/util.cpp
+++ b/src/citra_qt/util/util.cpp
@@ -19,7 +19,7 @@ QString ReadableByteSize(qulonglong size) {
19 static const std::array<const char*, 6> units = { "B", "KiB", "MiB", "GiB", "TiB", "PiB" }; 19 static const std::array<const char*, 6> units = { "B", "KiB", "MiB", "GiB", "TiB", "PiB" };
20 if (size == 0) 20 if (size == 0)
21 return "0"; 21 return "0";
22 int digit_groups = std::min<int>((int)(std::log10(size) / std::log10(1024)), units.size()); 22 int digit_groups = std::min<int>(static_cast<int>(std::log10(size) / std::log10(1024)), static_cast<int>(units.size()));
23 return QString("%L1 %2").arg(size / std::pow(1024, digit_groups), 0, 'f', 1) 23 return QString("%L1 %2").arg(size / std::pow(1024, digit_groups), 0, 'f', 1)
24 .arg(units[digit_groups]); 24 .arg(units[digit_groups]);
25} 25}
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 3d39f94d5..d7008fc66 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -65,6 +65,7 @@ namespace Log {
65 SUB(Render, OpenGL) \ 65 SUB(Render, OpenGL) \
66 CLS(Audio) \ 66 CLS(Audio) \
67 SUB(Audio, DSP) \ 67 SUB(Audio, DSP) \
68 SUB(Audio, Sink) \
68 CLS(Loader) 69 CLS(Loader)
69 70
70// GetClassName is a macro defined by Windows.h, grrr... 71// GetClassName is a macro defined by Windows.h, grrr...
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index 521362317..c6910b1c7 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -78,8 +78,9 @@ enum class Class : ClassType {
78 Render, ///< Emulator video output and hardware acceleration 78 Render, ///< Emulator video output and hardware acceleration
79 Render_Software, ///< Software renderer backend 79 Render_Software, ///< Software renderer backend
80 Render_OpenGL, ///< OpenGL backend 80 Render_OpenGL, ///< OpenGL backend
81 Audio, ///< Emulator audio output 81 Audio, ///< Audio emulation
82 Audio_DSP, ///< The HLE implementation of the DSP 82 Audio_DSP, ///< The HLE implementation of the DSP
83 Audio_Sink, ///< Emulator audio output backend
83 Loader, ///< ROM loader 84 Loader, ///< ROM loader
84 85
85 Count ///< Total number of logging classes 86 Count ///< Total number of logging classes
diff --git a/src/common/swap.h b/src/common/swap.h
index a7c37bc44..1749bd7a4 100644
--- a/src/common/swap.h
+++ b/src/common/swap.h
@@ -25,6 +25,8 @@
25 #include <sys/endian.h> 25 #include <sys/endian.h>
26#endif 26#endif
27 27
28#include <cstring>
29
28#include "common/common_types.h" 30#include "common/common_types.h"
29 31
30// GCC 4.6+ 32// GCC 4.6+
@@ -58,9 +60,6 @@
58 60
59namespace Common { 61namespace Common {
60 62
61inline u8 swap8(u8 _data) {return _data;}
62inline u32 swap24(const u8* _data) {return (_data[0] << 16) | (_data[1] << 8) | _data[2];}
63
64#ifdef _MSC_VER 63#ifdef _MSC_VER
65inline u16 swap16(u16 _data) {return _byteswap_ushort(_data);} 64inline u16 swap16(u16 _data) {return _byteswap_ushort(_data);}
66inline u32 swap32(u32 _data) {return _byteswap_ulong (_data);} 65inline u32 swap32(u32 _data) {return _byteswap_ulong (_data);}
@@ -92,52 +91,29 @@ inline u64 swap64(u64 data) {return ((u64)swap32(data) << 32) | swap32(data >> 3
92#endif 91#endif
93 92
94inline float swapf(float f) { 93inline float swapf(float f) {
95 union { 94 static_assert(sizeof(u32) == sizeof(float),
96 float f; 95 "float must be the same size as uint32_t.");
97 unsigned int u32;
98 } dat1, dat2;
99
100 dat1.f = f;
101 dat2.u32 = swap32(dat1.u32);
102 96
103 return dat2.f; 97 u32 value;
104} 98 std::memcpy(&value, &f, sizeof(u32));
105
106inline double swapd(double f) {
107 union {
108 double f;
109 unsigned long long u64;
110 } dat1, dat2;
111 99
112 dat1.f = f; 100 value = swap32(value);
113 dat2.u64 = swap64(dat1.u64); 101 std::memcpy(&f, &value, sizeof(u32));
114 102
115 return dat2.f; 103 return f;
116} 104}
117 105
118inline u16 swap16(const u8* _pData) {return swap16(*(const u16*)_pData);} 106inline double swapd(double f) {
119inline u32 swap32(const u8* _pData) {return swap32(*(const u32*)_pData);} 107 static_assert(sizeof(u64) == sizeof(double),
120inline u64 swap64(const u8* _pData) {return swap64(*(const u64*)_pData);} 108 "double must be the same size as uint64_t.");
121
122template <int count>
123void swap(u8*);
124 109
125template <> 110 u64 value;
126inline void swap<1>(u8* data) { } 111 std::memcpy(&value, &f, sizeof(u64));
127 112
128template <> 113 value = swap64(value);
129inline void swap<2>(u8* data) { 114 std::memcpy(&f, &value, sizeof(u64));
130 *reinterpret_cast<u16*>(data) = swap16(data);
131}
132
133template <>
134inline void swap<4>(u8* data) {
135 *reinterpret_cast<u32*>(data) = swap32(data);
136}
137 115
138template <> 116 return f;
139inline void swap<8>(u8* data) {
140 *reinterpret_cast<u64*>(data) = swap64(data);
141} 117}
142 118
143} // Namespace Common 119} // Namespace Common
@@ -534,35 +510,35 @@ bool operator==(const S &p, const swap_struct_t<T, F> v) {
534template <typename T> 510template <typename T>
535struct swap_64_t { 511struct swap_64_t {
536 static T swap(T x) { 512 static T swap(T x) {
537 return (T)Common::swap64(*(u64 *)&x); 513 return static_cast<T>(Common::swap64(x));
538 } 514 }
539}; 515};
540 516
541template <typename T> 517template <typename T>
542struct swap_32_t { 518struct swap_32_t {
543 static T swap(T x) { 519 static T swap(T x) {
544 return (T)Common::swap32(*(u32 *)&x); 520 return static_cast<T>(Common::swap32(x));
545 } 521 }
546}; 522};
547 523
548template <typename T> 524template <typename T>
549struct swap_16_t { 525struct swap_16_t {
550 static T swap(T x) { 526 static T swap(T x) {
551 return (T)Common::swap16(*(u16 *)&x); 527 return static_cast<T>(Common::swap16(x));
552 } 528 }
553}; 529};
554 530
555template <typename T> 531template <typename T>
556struct swap_float_t { 532struct swap_float_t {
557 static T swap(T x) { 533 static T swap(T x) {
558 return (T)Common::swapf(*(float *)&x); 534 return static_cast<T>(Common::swapf(x));
559 } 535 }
560}; 536};
561 537
562template <typename T> 538template <typename T>
563struct swap_double_t { 539struct swap_double_t {
564 static T swap(T x) { 540 static T swap(T x) {
565 return (T)Common::swapd(*(double *)&x); 541 return static_cast<T>(Common::swapd(x));
566 } 542 }
567}; 543};
568 544
diff --git a/src/core/arm/dyncom/arm_dyncom.cpp b/src/core/arm/dyncom/arm_dyncom.cpp
index a3581132c..13492a08b 100644
--- a/src/core/arm/dyncom/arm_dyncom.cpp
+++ b/src/core/arm/dyncom/arm_dyncom.cpp
@@ -93,7 +93,7 @@ void ARM_DynCom::ResetContext(Core::ThreadContext& context, u32 stack_top, u32 e
93 context.cpu_registers[0] = arg; 93 context.cpu_registers[0] = arg;
94 context.pc = entry_point; 94 context.pc = entry_point;
95 context.sp = stack_top; 95 context.sp = stack_top;
96 context.cpsr = 0x1F | ((entry_point & 1) << 5); // Usermode and THUMB mode 96 context.cpsr = USER32MODE | ((entry_point & 1) << 5); // Usermode and THUMB mode
97} 97}
98 98
99void ARM_DynCom::SaveContext(Core::ThreadContext& ctx) { 99void ARM_DynCom::SaveContext(Core::ThreadContext& ctx) {
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 3bb843aab..cabab744a 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -51,7 +51,7 @@ void RunLoop(int tight_loop) {
51 } 51 }
52 52
53 HW::Update(); 53 HW::Update();
54 if (HLE::g_reschedule) { 54 if (HLE::IsReschedulePending()) {
55 Kernel::Reschedule(); 55 Kernel::Reschedule();
56 } 56 }
57} 57}
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
index ae0c116ef..1360ee845 100644
--- a/src/core/gdbstub/gdbstub.cpp
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -374,7 +374,7 @@ static void SendReply(const char* reply) {
374 374
375 memset(command_buffer, 0, sizeof(command_buffer)); 375 memset(command_buffer, 0, sizeof(command_buffer));
376 376
377 command_length = strlen(reply); 377 command_length = static_cast<u32>(strlen(reply));
378 if (command_length + 4 > sizeof(command_buffer)) { 378 if (command_length + 4 > sizeof(command_buffer)) {
379 LOG_ERROR(Debug_GDBStub, "command_buffer overflow in SendReply"); 379 LOG_ERROR(Debug_GDBStub, "command_buffer overflow in SendReply");
380 return; 380 return;
@@ -515,7 +515,7 @@ static bool IsDataAvailable() {
515 return false; 515 return false;
516 } 516 }
517 517
518 return FD_ISSET(gdbserver_socket, &fd_socket); 518 return FD_ISSET(gdbserver_socket, &fd_socket) != 0;
519} 519}
520 520
521/// Send requested register to gdb client. 521/// Send requested register to gdb client.
@@ -633,10 +633,10 @@ static void ReadMemory() {
633 633
634 auto start_offset = command_buffer+1; 634 auto start_offset = command_buffer+1;
635 auto addr_pos = std::find(start_offset, command_buffer+command_length, ','); 635 auto addr_pos = std::find(start_offset, command_buffer+command_length, ',');
636 PAddr addr = HexToInt(start_offset, addr_pos - start_offset); 636 PAddr addr = HexToInt(start_offset, static_cast<u32>(addr_pos - start_offset));
637 637
638 start_offset = addr_pos+1; 638 start_offset = addr_pos+1;
639 u32 len = HexToInt(start_offset, (command_buffer + command_length) - start_offset); 639 u32 len = HexToInt(start_offset, static_cast<u32>((command_buffer + command_length) - start_offset));
640 640
641 LOG_DEBUG(Debug_GDBStub, "gdb: addr: %08x len: %08x\n", addr, len); 641 LOG_DEBUG(Debug_GDBStub, "gdb: addr: %08x len: %08x\n", addr, len);
642 642
@@ -658,11 +658,11 @@ static void ReadMemory() {
658static void WriteMemory() { 658static void WriteMemory() {
659 auto start_offset = command_buffer+1; 659 auto start_offset = command_buffer+1;
660 auto addr_pos = std::find(start_offset, command_buffer+command_length, ','); 660 auto addr_pos = std::find(start_offset, command_buffer+command_length, ',');
661 PAddr addr = HexToInt(start_offset, addr_pos - start_offset); 661 PAddr addr = HexToInt(start_offset, static_cast<u32>(addr_pos - start_offset));
662 662
663 start_offset = addr_pos+1; 663 start_offset = addr_pos+1;
664 auto len_pos = std::find(start_offset, command_buffer+command_length, ':'); 664 auto len_pos = std::find(start_offset, command_buffer+command_length, ':');
665 u32 len = HexToInt(start_offset, len_pos - start_offset); 665 u32 len = HexToInt(start_offset, static_cast<u32>(len_pos - start_offset));
666 666
667 u8* dst = Memory::GetPointer(addr); 667 u8* dst = Memory::GetPointer(addr);
668 if (!dst) { 668 if (!dst) {
@@ -752,10 +752,10 @@ static void AddBreakpoint() {
752 752
753 auto start_offset = command_buffer+3; 753 auto start_offset = command_buffer+3;
754 auto addr_pos = std::find(start_offset, command_buffer+command_length, ','); 754 auto addr_pos = std::find(start_offset, command_buffer+command_length, ',');
755 PAddr addr = HexToInt(start_offset, addr_pos - start_offset); 755 PAddr addr = HexToInt(start_offset, static_cast<u32>(addr_pos - start_offset));
756 756
757 start_offset = addr_pos+1; 757 start_offset = addr_pos+1;
758 u32 len = HexToInt(start_offset, (command_buffer + command_length) - start_offset); 758 u32 len = HexToInt(start_offset, static_cast<u32>((command_buffer + command_length) - start_offset));
759 759
760 if (type == BreakpointType::Access) { 760 if (type == BreakpointType::Access) {
761 // Access is made up of Read and Write types, so add both breakpoints 761 // Access is made up of Read and Write types, so add both breakpoints
@@ -800,10 +800,10 @@ static void RemoveBreakpoint() {
800 800
801 auto start_offset = command_buffer+3; 801 auto start_offset = command_buffer+3;
802 auto addr_pos = std::find(start_offset, command_buffer+command_length, ','); 802 auto addr_pos = std::find(start_offset, command_buffer+command_length, ',');
803 PAddr addr = HexToInt(start_offset, addr_pos - start_offset); 803 PAddr addr = HexToInt(start_offset, static_cast<u32>(addr_pos - start_offset));
804 804
805 start_offset = addr_pos+1; 805 start_offset = addr_pos+1;
806 u32 len = HexToInt(start_offset, (command_buffer + command_length) - start_offset); 806 u32 len = HexToInt(start_offset, static_cast<u32>((command_buffer + command_length) - start_offset));
807 807
808 if (type == BreakpointType::Access) { 808 if (type == BreakpointType::Access) {
809 // Access is made up of Read and Write types, so add both breakpoints 809 // Access is made up of Read and Write types, so add both breakpoints
diff --git a/src/core/hle/applets/mii_selector.cpp b/src/core/hle/applets/mii_selector.cpp
index 708d2f630..b4456ca90 100644
--- a/src/core/hle/applets/mii_selector.cpp
+++ b/src/core/hle/applets/mii_selector.cpp
@@ -21,13 +21,6 @@
21namespace HLE { 21namespace HLE {
22namespace Applets { 22namespace Applets {
23 23
24MiiSelector::MiiSelector(Service::APT::AppletId id) : Applet(id), started(false) {
25 // Create the SharedMemory that will hold the framebuffer data
26 // TODO(Subv): What size should we use here?
27 using Kernel::MemoryPermission;
28 framebuffer_memory = Kernel::SharedMemory::Create(0x1000, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, "MiiSelector Memory");
29}
30
31ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& parameter) { 24ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& parameter) {
32 if (parameter.signal != static_cast<u32>(Service::APT::SignalType::LibAppJustStarted)) { 25 if (parameter.signal != static_cast<u32>(Service::APT::SignalType::LibAppJustStarted)) {
33 LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal); 26 LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal);
@@ -36,8 +29,18 @@ ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& p
36 return ResultCode(-1); 29 return ResultCode(-1);
37 } 30 }
38 31
32 // The LibAppJustStarted message contains a buffer with the size of the framebuffer shared memory.
33 // Create the SharedMemory that will hold the framebuffer data
34 Service::APT::CaptureBufferInfo capture_info;
35 ASSERT(sizeof(capture_info) == parameter.buffer_size);
36
37 memcpy(&capture_info, parameter.data, sizeof(capture_info));
38 using Kernel::MemoryPermission;
39 framebuffer_memory = Kernel::SharedMemory::Create(capture_info.size, MemoryPermission::ReadWrite,
40 MemoryPermission::ReadWrite, "MiiSelector Memory");
41
42 // Send the response message with the newly created SharedMemory
39 Service::APT::MessageParameter result; 43 Service::APT::MessageParameter result;
40 // The buffer passed in parameter contains the data returned by GSPGPU::ImportDisplayCaptureInfo
41 result.signal = static_cast<u32>(Service::APT::SignalType::LibAppFinished); 44 result.signal = static_cast<u32>(Service::APT::SignalType::LibAppFinished);
42 result.data = nullptr; 45 result.data = nullptr;
43 result.buffer_size = 0; 46 result.buffer_size = 0;
@@ -55,6 +58,11 @@ ResultCode MiiSelector::StartImpl(const Service::APT::AppletStartupParameter& pa
55 // TODO(Subv): Set the expected fields in the response buffer before resending it to the application. 58 // TODO(Subv): Set the expected fields in the response buffer before resending it to the application.
56 // TODO(Subv): Reverse the parameter format for the Mii Selector 59 // TODO(Subv): Reverse the parameter format for the Mii Selector
57 60
61 if(parameter.buffer_size >= sizeof(u32)) {
62 // TODO: defaults return no error, but garbage in other unknown fields
63 memset(parameter.data, 0, sizeof(u32));
64 }
65
58 // Let the application know that we're closing 66 // Let the application know that we're closing
59 Service::APT::MessageParameter message; 67 Service::APT::MessageParameter message;
60 message.buffer_size = parameter.buffer_size; 68 message.buffer_size = parameter.buffer_size;
diff --git a/src/core/hle/applets/mii_selector.h b/src/core/hle/applets/mii_selector.h
index 6a3e7c8eb..be6b04642 100644
--- a/src/core/hle/applets/mii_selector.h
+++ b/src/core/hle/applets/mii_selector.h
@@ -16,17 +16,61 @@
16namespace HLE { 16namespace HLE {
17namespace Applets { 17namespace Applets {
18 18
19struct MiiConfig {
20 u8 unk_000;
21 u8 unk_001;
22 u8 unk_002;
23 u8 unk_003;
24 u8 unk_004;
25 INSERT_PADDING_BYTES(3);
26 u16 unk_008;
27 INSERT_PADDING_BYTES(0x8C - 0xA);
28 u8 unk_08C;
29 INSERT_PADDING_BYTES(3);
30 u16 unk_090;
31 INSERT_PADDING_BYTES(2);
32 u32 unk_094;
33 u16 unk_098;
34 u8 unk_09A[0x64];
35 u8 unk_0FE;
36 u8 unk_0FF;
37 u32 unk_100;
38};
39
40static_assert(sizeof(MiiConfig) == 0x104, "MiiConfig structure has incorrect size");
41#define ASSERT_REG_POSITION(field_name, position) static_assert(offsetof(MiiConfig, field_name) == position, "Field "#field_name" has invalid position")
42ASSERT_REG_POSITION(unk_008, 0x08);
43ASSERT_REG_POSITION(unk_08C, 0x8C);
44ASSERT_REG_POSITION(unk_090, 0x90);
45ASSERT_REG_POSITION(unk_094, 0x94);
46ASSERT_REG_POSITION(unk_0FE, 0xFE);
47#undef ASSERT_REG_POSITION
48
49struct MiiResult {
50 u32 result_code;
51 u8 unk_04;
52 INSERT_PADDING_BYTES(7);
53 u8 unk_0C[0x60];
54 u8 unk_6C[0x16];
55 INSERT_PADDING_BYTES(2);
56};
57static_assert(sizeof(MiiResult) == 0x84, "MiiResult structure has incorrect size");
58#define ASSERT_REG_POSITION(field_name, position) static_assert(offsetof(MiiResult, field_name) == position, "Field "#field_name" has invalid position")
59ASSERT_REG_POSITION(unk_0C, 0x0C);
60ASSERT_REG_POSITION(unk_6C, 0x6C);
61#undef ASSERT_REG_POSITION
62
19class MiiSelector final : public Applet { 63class MiiSelector final : public Applet {
20public: 64public:
21 MiiSelector(Service::APT::AppletId id); 65 MiiSelector(Service::APT::AppletId id) : Applet(id), started(false) { }
22 66
23 ResultCode ReceiveParameter(const Service::APT::MessageParameter& parameter) override; 67 ResultCode ReceiveParameter(const Service::APT::MessageParameter& parameter) override;
24 ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) override; 68 ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) override;
25 void Update() override; 69 void Update() override;
26 bool IsRunning() const override { return started; } 70 bool IsRunning() const override { return started; }
27 71
28 /// TODO(Subv): Find out what this is actually used for. 72 /// This SharedMemory will be created when we receive the LibAppJustStarted message.
29 /// It is believed that the application stores the current screen image here. 73 /// It holds the framebuffer info retrieved by the application with GSPGPU::ImportDisplayCaptureInfo
30 Kernel::SharedPtr<Kernel::SharedMemory> framebuffer_memory; 74 Kernel::SharedPtr<Kernel::SharedMemory> framebuffer_memory;
31 75
32 /// Whether this applet is currently running instead of the host application or not. 76 /// Whether this applet is currently running instead of the host application or not.
diff --git a/src/core/hle/applets/swkbd.cpp b/src/core/hle/applets/swkbd.cpp
index 1db6b5a17..87238aa1c 100644
--- a/src/core/hle/applets/swkbd.cpp
+++ b/src/core/hle/applets/swkbd.cpp
@@ -24,13 +24,6 @@
24namespace HLE { 24namespace HLE {
25namespace Applets { 25namespace Applets {
26 26
27SoftwareKeyboard::SoftwareKeyboard(Service::APT::AppletId id) : Applet(id), started(false) {
28 // Create the SharedMemory that will hold the framebuffer data
29 // TODO(Subv): What size should we use here?
30 using Kernel::MemoryPermission;
31 framebuffer_memory = Kernel::SharedMemory::Create(0x1000, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, "SoftwareKeyboard Memory");
32}
33
34ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter const& parameter) { 27ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter const& parameter) {
35 if (parameter.signal != static_cast<u32>(Service::APT::SignalType::LibAppJustStarted)) { 28 if (parameter.signal != static_cast<u32>(Service::APT::SignalType::LibAppJustStarted)) {
36 LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal); 29 LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal);
@@ -39,8 +32,19 @@ ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter con
39 return ResultCode(-1); 32 return ResultCode(-1);
40 } 33 }
41 34
35 // The LibAppJustStarted message contains a buffer with the size of the framebuffer shared memory.
36 // Create the SharedMemory that will hold the framebuffer data
37 Service::APT::CaptureBufferInfo capture_info;
38 ASSERT(sizeof(capture_info) == parameter.buffer_size);
39
40 memcpy(&capture_info, parameter.data, sizeof(capture_info));
41
42 using Kernel::MemoryPermission;
43 framebuffer_memory = Kernel::SharedMemory::Create(capture_info.size, MemoryPermission::ReadWrite,
44 MemoryPermission::ReadWrite, "SoftwareKeyboard Memory");
45
46 // Send the response message with the newly created SharedMemory
42 Service::APT::MessageParameter result; 47 Service::APT::MessageParameter result;
43 // The buffer passed in parameter contains the data returned by GSPGPU::ImportDisplayCaptureInfo
44 result.signal = static_cast<u32>(Service::APT::SignalType::LibAppFinished); 48 result.signal = static_cast<u32>(Service::APT::SignalType::LibAppFinished);
45 result.data = nullptr; 49 result.data = nullptr;
46 result.buffer_size = 0; 50 result.buffer_size = 0;
diff --git a/src/core/hle/applets/swkbd.h b/src/core/hle/applets/swkbd.h
index cb95b8d90..cf26a8fb7 100644
--- a/src/core/hle/applets/swkbd.h
+++ b/src/core/hle/applets/swkbd.h
@@ -53,8 +53,7 @@ static_assert(sizeof(SoftwareKeyboardConfig) == 0x400, "Software Keyboard Config
53 53
54class SoftwareKeyboard final : public Applet { 54class SoftwareKeyboard final : public Applet {
55public: 55public:
56 SoftwareKeyboard(Service::APT::AppletId id); 56 SoftwareKeyboard(Service::APT::AppletId id) : Applet(id), started(false) { }
57 ~SoftwareKeyboard() {}
58 57
59 ResultCode ReceiveParameter(const Service::APT::MessageParameter& parameter) override; 58 ResultCode ReceiveParameter(const Service::APT::MessageParameter& parameter) override;
60 ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) override; 59 ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) override;
@@ -72,8 +71,8 @@ public:
72 */ 71 */
73 void Finalize(); 72 void Finalize();
74 73
75 /// TODO(Subv): Find out what this is actually used for. 74 /// This SharedMemory will be created when we receive the LibAppJustStarted message.
76 /// It is believed that the application stores the current screen image here. 75 /// It holds the framebuffer info retrieved by the application with GSPGPU::ImportDisplayCaptureInfo
77 Kernel::SharedPtr<Kernel::SharedMemory> framebuffer_memory; 76 Kernel::SharedPtr<Kernel::SharedMemory> framebuffer_memory;
78 77
79 /// SharedMemory where the output text will be stored 78 /// SharedMemory where the output text will be stored
diff --git a/src/core/hle/hle.cpp b/src/core/hle/hle.cpp
index e545de3b5..5c5373517 100644
--- a/src/core/hle/hle.cpp
+++ b/src/core/hle/hle.cpp
@@ -12,9 +12,13 @@
12 12
13//////////////////////////////////////////////////////////////////////////////////////////////////// 13////////////////////////////////////////////////////////////////////////////////////////////////////
14 14
15namespace HLE { 15namespace {
16
17bool reschedule; ///< If true, immediately reschedules the CPU to a new thread
16 18
17bool g_reschedule; ///< If true, immediately reschedules the CPU to a new thread 19}
20
21namespace HLE {
18 22
19void Reschedule(const char *reason) { 23void Reschedule(const char *reason) {
20 DEBUG_ASSERT_MSG(reason != nullptr && strlen(reason) < 256, "Reschedule: Invalid or too long reason."); 24 DEBUG_ASSERT_MSG(reason != nullptr && strlen(reason) < 256, "Reschedule: Invalid or too long reason.");
@@ -27,13 +31,21 @@ void Reschedule(const char *reason) {
27 31
28 Core::g_app_core->PrepareReschedule(); 32 Core::g_app_core->PrepareReschedule();
29 33
30 g_reschedule = true; 34 reschedule = true;
35}
36
37bool IsReschedulePending() {
38 return reschedule;
39}
40
41void DoneRescheduling() {
42 reschedule = false;
31} 43}
32 44
33void Init() { 45void Init() {
34 Service::Init(); 46 Service::Init();
35 47
36 g_reschedule = false; 48 reschedule = false;
37 49
38 LOG_DEBUG(Kernel, "initialized OK"); 50 LOG_DEBUG(Kernel, "initialized OK");
39} 51}
diff --git a/src/core/hle/hle.h b/src/core/hle/hle.h
index e0b97797c..69ac0ade6 100644
--- a/src/core/hle/hle.h
+++ b/src/core/hle/hle.h
@@ -13,9 +13,9 @@ const Handle INVALID_HANDLE = 0;
13 13
14namespace HLE { 14namespace HLE {
15 15
16extern bool g_reschedule; ///< If true, immediately reschedules the CPU to a new thread
17
18void Reschedule(const char *reason); 16void Reschedule(const char *reason);
17bool IsReschedulePending();
18void DoneRescheduling();
19 19
20void Init(); 20void Init();
21void Shutdown(); 21void Shutdown();
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index 6d2ca96a2..a06afef2b 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -107,6 +107,8 @@ public:
107 ProcessFlags flags; 107 ProcessFlags flags;
108 /// Kernel compatibility version for this process 108 /// Kernel compatibility version for this process
109 u16 kernel_version = 0; 109 u16 kernel_version = 0;
110 /// The default CPU for this process, threads are scheduled on this cpu by default.
111 u8 ideal_processor = 0;
110 112
111 /// The id of this process 113 /// The id of this process
112 u32 process_id = next_process_id++; 114 u32 process_id = next_process_id++;
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index bf32f653d..6dc95d0f1 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -483,7 +483,8 @@ void Reschedule() {
483 483
484 Thread* cur = GetCurrentThread(); 484 Thread* cur = GetCurrentThread();
485 Thread* next = PopNextReadyThread(); 485 Thread* next = PopNextReadyThread();
486 HLE::g_reschedule = false; 486
487 HLE::DoneRescheduling();
487 488
488 // Don't bother switching to the same thread 489 // Don't bother switching to the same thread
489 if (next == cur) 490 if (next == cur)
diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h
index 668b4a66f..1a1034fcc 100644
--- a/src/core/hle/service/apt/apt.h
+++ b/src/core/hle/service/apt/apt.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "common/swap.h"
8 9
9#include "core/hle/kernel/kernel.h" 10#include "core/hle/kernel/kernel.h"
10 11
@@ -31,6 +32,20 @@ struct AppletStartupParameter {
31 u8* data = nullptr; 32 u8* data = nullptr;
32}; 33};
33 34
35/// Used by the application to pass information about the current framebuffer to applets.
36struct CaptureBufferInfo {
37 u32_le size;
38 u8 is_3d;
39 INSERT_PADDING_BYTES(0x3); // Padding for alignment
40 u32_le top_screen_left_offset;
41 u32_le top_screen_right_offset;
42 u32_le top_screen_format;
43 u32_le bottom_screen_left_offset;
44 u32_le bottom_screen_right_offset;
45 u32_le bottom_screen_format;
46};
47static_assert(sizeof(CaptureBufferInfo) == 0x20, "CaptureBufferInfo struct has incorrect size");
48
34/// Signals used by APT functions 49/// Signals used by APT functions
35enum class SignalType : u32 { 50enum class SignalType : u32 {
36 None = 0x0, 51 None = 0x0,
diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp
index 995bee3f9..274fc751a 100644
--- a/src/core/hle/service/dsp_dsp.cpp
+++ b/src/core/hle/service/dsp_dsp.cpp
@@ -288,7 +288,7 @@ static void WriteProcessPipe(Service::Interface* self) {
288 ASSERT_MSG(Memory::GetPointer(buffer) != nullptr, "Invalid Buffer: pipe=%u, size=0x%X, buffer=0x%08X", pipe_index, size, buffer); 288 ASSERT_MSG(Memory::GetPointer(buffer) != nullptr, "Invalid Buffer: pipe=%u, size=0x%X, buffer=0x%08X", pipe_index, size, buffer);
289 289
290 std::vector<u8> message(size); 290 std::vector<u8> message(size);
291 for (size_t i = 0; i < size; i++) { 291 for (u32 i = 0; i < size; i++) {
292 message[i] = Memory::Read8(buffer + i); 292 message[i] = Memory::Read8(buffer + i);
293 } 293 }
294 294
@@ -403,7 +403,7 @@ static void GetPipeReadableSize(Service::Interface* self) {
403 403
404 cmd_buff[0] = IPC::MakeHeader(0xF, 2, 0); 404 cmd_buff[0] = IPC::MakeHeader(0xF, 2, 0);
405 cmd_buff[1] = RESULT_SUCCESS.raw; // No error 405 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
406 cmd_buff[2] = DSP::HLE::GetPipeReadableSize(pipe); 406 cmd_buff[2] = static_cast<u32>(DSP::HLE::GetPipeReadableSize(pipe));
407 407
408 LOG_DEBUG(Service_DSP, "pipe=%u, unknown=0x%08X, return cmd_buff[2]=0x%08X", pipe_index, unknown, cmd_buff[2]); 408 LOG_DEBUG(Service_DSP, "pipe=%u, unknown=0x%08X, return cmd_buff[2]=0x%08X", pipe_index, unknown, cmd_buff[2]);
409} 409}
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index fb2aecbf2..60c8747f3 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -496,6 +496,11 @@ static ResultCode CreateThread(Handle* out_handle, s32 priority, u32 entry_point
496 break; 496 break;
497 } 497 }
498 498
499 if (processor_id == THREADPROCESSORID_1 || processor_id == THREADPROCESSORID_ALL ||
500 (processor_id == THREADPROCESSORID_DEFAULT && Kernel::g_current_process->ideal_processor == THREADPROCESSORID_1)) {
501 LOG_WARNING(Kernel_SVC, "Newly created thread is allowed to be run in the SysCore, unimplemented.");
502 }
503
499 CASCADE_RESULT(SharedPtr<Thread> thread, Kernel::Thread::Create( 504 CASCADE_RESULT(SharedPtr<Thread> thread, Kernel::Thread::Create(
500 name, entry_point, priority, arg, processor_id, stack_top)); 505 name, entry_point, priority, arg, processor_id, stack_top));
501 CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(thread))); 506 CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(thread)));
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp
index 2fe856293..a4dfb7e43 100644
--- a/src/core/hw/gpu.cpp
+++ b/src/core/hw/gpu.cpp
@@ -188,10 +188,10 @@ inline void Write(u32 addr, const T data) {
188 u32 output_gap = config.texture_copy.output_gap * 16; 188 u32 output_gap = config.texture_copy.output_gap * 16;
189 189
190 size_t contiguous_input_size = config.texture_copy.size / input_width * (input_width + input_gap); 190 size_t contiguous_input_size = config.texture_copy.size / input_width * (input_width + input_gap);
191 Memory::RasterizerFlushRegion(config.GetPhysicalInputAddress(), contiguous_input_size); 191 Memory::RasterizerFlushRegion(config.GetPhysicalInputAddress(), static_cast<u32>(contiguous_input_size));
192 192
193 size_t contiguous_output_size = config.texture_copy.size / output_width * (output_width + output_gap); 193 size_t contiguous_output_size = config.texture_copy.size / output_width * (output_width + output_gap);
194 Memory::RasterizerFlushAndInvalidateRegion(config.GetPhysicalOutputAddress(), contiguous_output_size); 194 Memory::RasterizerFlushAndInvalidateRegion(config.GetPhysicalOutputAddress(), static_cast<u32>(contiguous_output_size));
195 195
196 u32 remaining_size = config.texture_copy.size; 196 u32 remaining_size = config.texture_copy.size;
197 u32 remaining_input = input_width; 197 u32 remaining_input = input_width;
diff --git a/src/core/loader/3dsx.cpp b/src/core/loader/3dsx.cpp
index 5fb3b9e2b..98e7ab48f 100644
--- a/src/core/loader/3dsx.cpp
+++ b/src/core/loader/3dsx.cpp
@@ -178,11 +178,11 @@ static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr, Shared
178 for (unsigned current_inprogress = 0; current_inprogress < remaining && pos < end_pos; current_inprogress++) { 178 for (unsigned current_inprogress = 0; current_inprogress < remaining && pos < end_pos; current_inprogress++) {
179 const auto& table = reloc_table[current_inprogress]; 179 const auto& table = reloc_table[current_inprogress];
180 LOG_TRACE(Loader, "(t=%d,skip=%u,patch=%u)", current_segment_reloc_table, 180 LOG_TRACE(Loader, "(t=%d,skip=%u,patch=%u)", current_segment_reloc_table,
181 (u32)table.skip, (u32)table.patch); 181 static_cast<u32>(table.skip), static_cast<u32>(table.patch));
182 pos += table.skip; 182 pos += table.skip;
183 s32 num_patches = table.patch; 183 s32 num_patches = table.patch;
184 while (0 < num_patches && pos < end_pos) { 184 while (0 < num_patches && pos < end_pos) {
185 u32 in_addr = (u8*)pos - program_image.data(); 185 u32 in_addr = static_cast<u32>(reinterpret_cast<u8*>(pos) - program_image.data());
186 u32 addr = TranslateAddr(*pos, &loadinfo, offsets); 186 u32 addr = TranslateAddr(*pos, &loadinfo, offsets);
187 LOG_TRACE(Loader, "Patching %08X <-- rel(%08X,%d) (%08X)", 187 LOG_TRACE(Loader, "Patching %08X <-- rel(%08X,%d) (%08X)",
188 base_addr + in_addr, addr, current_segment_reloc_table, *pos); 188 base_addr + in_addr, addr, current_segment_reloc_table, *pos);
@@ -284,7 +284,7 @@ ResultStatus AppLoader_THREEDSX::ReadRomFS(std::shared_ptr<FileUtil::IOFile>& ro
284 // Check if the 3DSX has a RomFS... 284 // Check if the 3DSX has a RomFS...
285 if (hdr.fs_offset != 0) { 285 if (hdr.fs_offset != 0) {
286 u32 romfs_offset = hdr.fs_offset; 286 u32 romfs_offset = hdr.fs_offset;
287 u32 romfs_size = file.GetSize() - hdr.fs_offset; 287 u32 romfs_size = static_cast<u32>(file.GetSize()) - hdr.fs_offset;
288 288
289 LOG_DEBUG(Loader, "RomFS offset: 0x%08X", romfs_offset); 289 LOG_DEBUG(Loader, "RomFS offset: 0x%08X", romfs_offset);
290 LOG_DEBUG(Loader, "RomFS size: 0x%08X", romfs_size); 290 LOG_DEBUG(Loader, "RomFS size: 0x%08X", romfs_size);
@@ -303,4 +303,31 @@ ResultStatus AppLoader_THREEDSX::ReadRomFS(std::shared_ptr<FileUtil::IOFile>& ro
303 return ResultStatus::ErrorNotUsed; 303 return ResultStatus::ErrorNotUsed;
304} 304}
305 305
306ResultStatus AppLoader_THREEDSX::ReadIcon(std::vector<u8>& buffer) {
307 if (!file.IsOpen())
308 return ResultStatus::Error;
309
310 // Reset read pointer in case this file has been read before.
311 file.Seek(0, SEEK_SET);
312
313 THREEDSX_Header hdr;
314 if (file.ReadBytes(&hdr, sizeof(THREEDSX_Header)) != sizeof(THREEDSX_Header))
315 return ResultStatus::Error;
316
317 if (hdr.header_size != sizeof(THREEDSX_Header))
318 return ResultStatus::Error;
319
320 // Check if the 3DSX has a SMDH...
321 if (hdr.smdh_offset != 0) {
322 file.Seek(hdr.smdh_offset, SEEK_SET);
323 buffer.resize(hdr.smdh_size);
324
325 if (file.ReadBytes(&buffer[0], hdr.smdh_size) != hdr.smdh_size)
326 return ResultStatus::Error;
327
328 return ResultStatus::Success;
329 }
330 return ResultStatus::ErrorNotUsed;
331}
332
306} // namespace Loader 333} // namespace Loader
diff --git a/src/core/loader/3dsx.h b/src/core/loader/3dsx.h
index 365ddb7a5..3ee686703 100644
--- a/src/core/loader/3dsx.h
+++ b/src/core/loader/3dsx.h
@@ -17,7 +17,7 @@ namespace Loader {
17/// Loads an 3DSX file 17/// Loads an 3DSX file
18class AppLoader_THREEDSX final : public AppLoader { 18class AppLoader_THREEDSX final : public AppLoader {
19public: 19public:
20 AppLoader_THREEDSX(FileUtil::IOFile&& file, std::string filename, const std::string& filepath) 20 AppLoader_THREEDSX(FileUtil::IOFile&& file, const std::string& filename, const std::string& filepath)
21 : AppLoader(std::move(file)), filename(std::move(filename)), filepath(filepath) {} 21 : AppLoader(std::move(file)), filename(std::move(filename)), filepath(filepath) {}
22 22
23 /** 23 /**
@@ -34,6 +34,13 @@ public:
34 ResultStatus Load() override; 34 ResultStatus Load() override;
35 35
36 /** 36 /**
37 * Get the icon (typically icon section) of the application
38 * @param buffer Reference to buffer to store data
39 * @return ResultStatus result of function
40 */
41 ResultStatus ReadIcon(std::vector<u8>& buffer) override;
42
43 /**
37 * Get the RomFS of the application 44 * Get the RomFS of the application
38 * @param romfs_file Reference to buffer to store data 45 * @param romfs_file Reference to buffer to store data
39 * @param offset Offset in the file to the RomFS 46 * @param offset Offset in the file to the RomFS
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index 886501c41..af3f62248 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -90,6 +90,28 @@ const char* GetFileTypeString(FileType type) {
90 return "unknown"; 90 return "unknown";
91} 91}
92 92
93std::unique_ptr<AppLoader> GetLoader(FileUtil::IOFile&& file, FileType type,
94 const std::string& filename, const std::string& filepath) {
95 switch (type) {
96
97 // 3DSX file format.
98 case FileType::THREEDSX:
99 return std::make_unique<AppLoader_THREEDSX>(std::move(file), filename, filepath);
100
101 // Standard ELF file format.
102 case FileType::ELF:
103 return std::make_unique<AppLoader_ELF>(std::move(file), filename);
104
105 // NCCH/NCSD container formats.
106 case FileType::CXI:
107 case FileType::CCI:
108 return std::make_unique<AppLoader_NCCH>(std::move(file), filepath);
109
110 default:
111 return std::unique_ptr<AppLoader>();
112 }
113}
114
93ResultStatus LoadFile(const std::string& filename) { 115ResultStatus LoadFile(const std::string& filename) {
94 FileUtil::IOFile file(filename, "rb"); 116 FileUtil::IOFile file(filename, "rb");
95 if (!file.IsOpen()) { 117 if (!file.IsOpen()) {
@@ -111,38 +133,29 @@ ResultStatus LoadFile(const std::string& filename) {
111 133
112 LOG_INFO(Loader, "Loading file %s as %s...", filename.c_str(), GetFileTypeString(type)); 134 LOG_INFO(Loader, "Loading file %s as %s...", filename.c_str(), GetFileTypeString(type));
113 135
136 std::unique_ptr<AppLoader> app_loader = GetLoader(std::move(file), type, filename_filename, filename);
137
114 switch (type) { 138 switch (type) {
115 139
116 //3DSX file format... 140 // 3DSX file format...
141 // or NCCH/NCSD container formats...
117 case FileType::THREEDSX: 142 case FileType::THREEDSX:
118 {
119 AppLoader_THREEDSX app_loader(std::move(file), filename_filename, filename);
120 // Load application and RomFS
121 if (ResultStatus::Success == app_loader.Load()) {
122 Service::FS::RegisterArchiveType(std::make_unique<FileSys::ArchiveFactory_RomFS>(app_loader), Service::FS::ArchiveIdCode::RomFS);
123 return ResultStatus::Success;
124 }
125 break;
126 }
127
128 // Standard ELF file format...
129 case FileType::ELF:
130 return AppLoader_ELF(std::move(file), filename_filename).Load();
131
132 // NCCH/NCSD container formats...
133 case FileType::CXI: 143 case FileType::CXI:
134 case FileType::CCI: 144 case FileType::CCI:
135 { 145 {
136 AppLoader_NCCH app_loader(std::move(file), filename);
137
138 // Load application and RomFS 146 // Load application and RomFS
139 ResultStatus result = app_loader.Load(); 147 ResultStatus result = app_loader->Load();
140 if (ResultStatus::Success == result) { 148 if (ResultStatus::Success == result) {
141 Service::FS::RegisterArchiveType(std::make_unique<FileSys::ArchiveFactory_RomFS>(app_loader), Service::FS::ArchiveIdCode::RomFS); 149 Service::FS::RegisterArchiveType(std::make_unique<FileSys::ArchiveFactory_RomFS>(*app_loader), Service::FS::ArchiveIdCode::RomFS);
150 return ResultStatus::Success;
142 } 151 }
143 return result; 152 return result;
144 } 153 }
145 154
155 // Standard ELF file format...
156 case FileType::ELF:
157 return app_loader->Load();
158
146 // CIA file format... 159 // CIA file format...
147 case FileType::CIA: 160 case FileType::CIA:
148 return ResultStatus::ErrorNotImplemented; 161 return ResultStatus::ErrorNotImplemented;
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 84a4ce5fc..9d3e9ed3b 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -10,8 +10,10 @@
10#include <string> 10#include <string>
11#include <vector> 11#include <vector>
12 12
13#include "common/common_funcs.h"
13#include "common/common_types.h" 14#include "common/common_types.h"
14#include "common/file_util.h" 15#include "common/file_util.h"
16#include "common/swap.h"
15 17
16namespace Kernel { 18namespace Kernel {
17struct AddressMapping; 19struct AddressMapping;
@@ -78,6 +80,51 @@ constexpr u32 MakeMagic(char a, char b, char c, char d) {
78 return a | b << 8 | c << 16 | d << 24; 80 return a | b << 8 | c << 16 | d << 24;
79} 81}
80 82
83/// SMDH data structure that contains titles, icons etc. See https://www.3dbrew.org/wiki/SMDH
84struct SMDH {
85 u32_le magic;
86 u16_le version;
87 INSERT_PADDING_BYTES(2);
88
89 struct Title {
90 std::array<u16, 0x40> short_title;
91 std::array<u16, 0x80> long_title;
92 std::array<u16, 0x40> publisher;
93 };
94 std::array<Title, 16> titles;
95
96 std::array<u8, 16> ratings;
97 u32_le region_lockout;
98 u32_le match_maker_id;
99 u64_le match_maker_bit_id;
100 u32_le flags;
101 u16_le eula_version;
102 INSERT_PADDING_BYTES(2);
103 float_le banner_animation_frame;
104 u32_le cec_id;
105 INSERT_PADDING_BYTES(8);
106
107 std::array<u8, 0x480> small_icon;
108 std::array<u8, 0x1200> large_icon;
109
110 /// indicates the language used for each title entry
111 enum class TitleLanguage {
112 Japanese = 0,
113 English = 1,
114 French = 2,
115 German = 3,
116 Italian = 4,
117 Spanish = 5,
118 SimplifiedChinese = 6,
119 Korean= 7,
120 Dutch = 8,
121 Portuguese = 9,
122 Russian = 10,
123 TraditionalChinese = 11
124 };
125};
126static_assert(sizeof(SMDH) == 0x36C0, "SMDH structure size is wrong");
127
81/// Interface for loading an application 128/// Interface for loading an application
82class AppLoader : NonCopyable { 129class AppLoader : NonCopyable {
83public: 130public:
@@ -150,6 +197,16 @@ protected:
150extern const std::initializer_list<Kernel::AddressMapping> default_address_mappings; 197extern const std::initializer_list<Kernel::AddressMapping> default_address_mappings;
151 198
152/** 199/**
200 * Get a loader for a file with a specific type
201 * @param file The file to load
202 * @param type The type of the file
203 * @param filename the file name (without path)
204 * @param filepath the file full path (with name)
205 * @return std::unique_ptr<AppLoader> a pointer to a loader object; nullptr for unsupported type
206 */
207std::unique_ptr<AppLoader> GetLoader(FileUtil::IOFile&& file, FileType type, const std::string& filename, const std::string& filepath);
208
209/**
153 * Identifies and loads a bootable file 210 * Identifies and loads a bootable file
154 * @param filename String filename of bootable file 211 * @param filename String filename of bootable file
155 * @return ResultStatus result of function 212 * @return ResultStatus result of function
diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp
index 066e91a9e..7391bdb26 100644
--- a/src/core/loader/ncch.cpp
+++ b/src/core/loader/ncch.cpp
@@ -156,6 +156,9 @@ ResultStatus AppLoader_NCCH::LoadExec() {
156 Kernel::g_current_process->resource_limit = Kernel::ResourceLimit::GetForCategory( 156 Kernel::g_current_process->resource_limit = Kernel::ResourceLimit::GetForCategory(
157 static_cast<Kernel::ResourceLimitCategory>(exheader_header.arm11_system_local_caps.resource_limit_category)); 157 static_cast<Kernel::ResourceLimitCategory>(exheader_header.arm11_system_local_caps.resource_limit_category));
158 158
159 // Set the default CPU core for this process
160 Kernel::g_current_process->ideal_processor = exheader_header.arm11_system_local_caps.ideal_processor;
161
159 // Copy data while converting endianess 162 // Copy data while converting endianess
160 std::array<u32, ARRAY_SIZE(exheader_header.arm11_kernel_caps.descriptors)> kernel_caps; 163 std::array<u32, ARRAY_SIZE(exheader_header.arm11_kernel_caps.descriptors)> kernel_caps;
161 std::copy_n(exheader_header.arm11_kernel_caps.descriptors, kernel_caps.size(), begin(kernel_caps)); 164 std::copy_n(exheader_header.arm11_kernel_caps.descriptors, kernel_caps.size(), begin(kernel_caps));
@@ -173,6 +176,10 @@ ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>&
173 if (!file.IsOpen()) 176 if (!file.IsOpen())
174 return ResultStatus::Error; 177 return ResultStatus::Error;
175 178
179 ResultStatus result = LoadExeFS();
180 if (result != ResultStatus::Success)
181 return result;
182
176 LOG_DEBUG(Loader, "%d sections:", kMaxSections); 183 LOG_DEBUG(Loader, "%d sections:", kMaxSections);
177 // Iterate through the ExeFs archive until we find a section with the specified name... 184 // Iterate through the ExeFs archive until we find a section with the specified name...
178 for (unsigned section_number = 0; section_number < kMaxSections; section_number++) { 185 for (unsigned section_number = 0; section_number < kMaxSections; section_number++) {
@@ -215,9 +222,9 @@ ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>&
215 return ResultStatus::ErrorNotUsed; 222 return ResultStatus::ErrorNotUsed;
216} 223}
217 224
218ResultStatus AppLoader_NCCH::Load() { 225ResultStatus AppLoader_NCCH::LoadExeFS() {
219 if (is_loaded) 226 if (is_exefs_loaded)
220 return ResultStatus::ErrorAlreadyLoaded; 227 return ResultStatus::Success;
221 228
222 if (!file.IsOpen()) 229 if (!file.IsOpen())
223 return ResultStatus::Error; 230 return ResultStatus::Error;
@@ -282,6 +289,18 @@ ResultStatus AppLoader_NCCH::Load() {
282 if (file.ReadBytes(&exefs_header, sizeof(ExeFs_Header)) != sizeof(ExeFs_Header)) 289 if (file.ReadBytes(&exefs_header, sizeof(ExeFs_Header)) != sizeof(ExeFs_Header))
283 return ResultStatus::Error; 290 return ResultStatus::Error;
284 291
292 is_exefs_loaded = true;
293 return ResultStatus::Success;
294}
295
296ResultStatus AppLoader_NCCH::Load() {
297 if (is_loaded)
298 return ResultStatus::ErrorAlreadyLoaded;
299
300 ResultStatus result = LoadExeFS();
301 if (result != ResultStatus::Success)
302 return result;
303
285 is_loaded = true; // Set state to loaded 304 is_loaded = true; // Set state to loaded
286 305
287 return LoadExec(); // Load the executable into memory for booting 306 return LoadExec(); // Load the executable into memory for booting
diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h
index ca6772a78..fd852c3de 100644
--- a/src/core/loader/ncch.h
+++ b/src/core/loader/ncch.h
@@ -232,6 +232,13 @@ private:
232 */ 232 */
233 ResultStatus LoadExec(); 233 ResultStatus LoadExec();
234 234
235 /**
236 * Ensure ExeFS is loaded and ready for reading sections
237 * @return ResultStatus result of function
238 */
239 ResultStatus LoadExeFS();
240
241 bool is_exefs_loaded = false;
235 bool is_compressed = false; 242 bool is_compressed = false;
236 243
237 u32 entry_point = 0; 244 u32 entry_point = 0;
diff --git a/src/core/tracer/recorder.cpp b/src/core/tracer/recorder.cpp
index c6dc35c83..7abaacf70 100644
--- a/src/core/tracer/recorder.cpp
+++ b/src/core/tracer/recorder.cpp
@@ -26,17 +26,17 @@ void Recorder::Finish(const std::string& filename) {
26 // Calculate file offsets 26 // Calculate file offsets
27 auto& initial = header.initial_state_offsets; 27 auto& initial = header.initial_state_offsets;
28 28
29 initial.gpu_registers_size = initial_state.gpu_registers.size(); 29 initial.gpu_registers_size = static_cast<u32>(initial_state.gpu_registers.size());
30 initial.lcd_registers_size = initial_state.lcd_registers.size(); 30 initial.lcd_registers_size = static_cast<u32>(initial_state.lcd_registers.size());
31 initial.pica_registers_size = initial_state.pica_registers.size(); 31 initial.pica_registers_size = static_cast<u32>(initial_state.pica_registers.size());
32 initial.default_attributes_size = initial_state.default_attributes.size(); 32 initial.default_attributes_size = static_cast<u32>(initial_state.default_attributes.size());
33 initial.vs_program_binary_size = initial_state.vs_program_binary.size(); 33 initial.vs_program_binary_size = static_cast<u32>(initial_state.vs_program_binary.size());
34 initial.vs_swizzle_data_size = initial_state.vs_swizzle_data.size(); 34 initial.vs_swizzle_data_size = static_cast<u32>(initial_state.vs_swizzle_data.size());
35 initial.vs_float_uniforms_size = initial_state.vs_float_uniforms.size(); 35 initial.vs_float_uniforms_size = static_cast<u32>(initial_state.vs_float_uniforms.size());
36 initial.gs_program_binary_size = initial_state.gs_program_binary.size(); 36 initial.gs_program_binary_size = static_cast<u32>(initial_state.gs_program_binary.size());
37 initial.gs_swizzle_data_size = initial_state.gs_swizzle_data.size(); 37 initial.gs_swizzle_data_size = static_cast<u32>(initial_state.gs_swizzle_data.size());
38 initial.gs_float_uniforms_size = initial_state.gs_float_uniforms.size(); 38 initial.gs_float_uniforms_size = static_cast<u32>(initial_state.gs_float_uniforms.size());
39 header.stream_size = stream.size(); 39 header.stream_size = static_cast<u32>(stream.size());
40 40
41 initial.gpu_registers = sizeof(header); 41 initial.gpu_registers = sizeof(header);
42 initial.lcd_registers = initial.gpu_registers + initial.gpu_registers_size * sizeof(u32); 42 initial.lcd_registers = initial.gpu_registers + initial.gpu_registers_size * sizeof(u32);
@@ -68,7 +68,7 @@ void Recorder::Finish(const std::string& filename) {
68 DEBUG_ASSERT(stream_element.extra_data.size() == 0); 68 DEBUG_ASSERT(stream_element.extra_data.size() == 0);
69 break; 69 break;
70 } 70 }
71 header.stream_offset += stream_element.extra_data.size(); 71 header.stream_offset += static_cast<u32>(stream_element.extra_data.size());
72 } 72 }
73 73
74 try { 74 try {
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp
index be1a936b2..dd1379503 100644
--- a/src/video_core/command_processor.cpp
+++ b/src/video_core/command_processor.cpp
@@ -146,10 +146,9 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
146 Shader::UnitState<false> shader_unit; 146 Shader::UnitState<false> shader_unit;
147 Shader::Setup(); 147 Shader::Setup();
148 148
149 if (g_debug_context)
150 g_debug_context->OnEvent(DebugContext::Event::VertexLoaded, static_cast<void*>(&immediate_input));
151
152 // Send to vertex shader 149 // Send to vertex shader
150 if (g_debug_context)
151 g_debug_context->OnEvent(DebugContext::Event::VertexShaderInvocation, static_cast<void*>(&immediate_input));
153 Shader::OutputVertex output = Shader::Run(shader_unit, immediate_input, regs.vs.num_input_attributes+1); 152 Shader::OutputVertex output = Shader::Run(shader_unit, immediate_input, regs.vs.num_input_attributes+1);
154 153
155 // Send to renderer 154 // Send to renderer
@@ -272,10 +271,9 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
272 Shader::InputVertex input; 271 Shader::InputVertex input;
273 loader.LoadVertex(base_address, index, vertex, input, memory_accesses); 272 loader.LoadVertex(base_address, index, vertex, input, memory_accesses);
274 273
275 if (g_debug_context)
276 g_debug_context->OnEvent(DebugContext::Event::VertexLoaded, (void*)&input);
277
278 // Send to vertex shader 274 // Send to vertex shader
275 if (g_debug_context)
276 g_debug_context->OnEvent(DebugContext::Event::VertexShaderInvocation, (void*)&input);
279 output = Shader::Run(shader_unit, input, loader.GetNumTotalAttributes()); 277 output = Shader::Run(shader_unit, input, loader.GetNumTotalAttributes());
280 278
281 if (is_indexed) { 279 if (is_indexed) {
diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp
index fb20f81dd..2f645b441 100644
--- a/src/video_core/debug_utils/debug_utils.cpp
+++ b/src/video_core/debug_utils/debug_utils.cpp
@@ -208,11 +208,12 @@ void DumpShader(const std::string& filename, const Regs::ShaderConfig& config, c
208 208
209 // TODO: Reduce the amount of binary code written to relevant portions 209 // TODO: Reduce the amount of binary code written to relevant portions
210 dvlp.binary_offset = write_offset - dvlp_offset; 210 dvlp.binary_offset = write_offset - dvlp_offset;
211 dvlp.binary_size_words = setup.program_code.size(); 211 dvlp.binary_size_words = static_cast<uint32_t>(setup.program_code.size());
212 QueueForWriting(reinterpret_cast<const u8*>(setup.program_code.data()), setup.program_code.size() * sizeof(u32)); 212 QueueForWriting(reinterpret_cast<const u8*>(setup.program_code.data()),
213 static_cast<u32>(setup.program_code.size()) * sizeof(u32));
213 214
214 dvlp.swizzle_info_offset = write_offset - dvlp_offset; 215 dvlp.swizzle_info_offset = write_offset - dvlp_offset;
215 dvlp.swizzle_info_num_entries = setup.swizzle_data.size(); 216 dvlp.swizzle_info_num_entries = static_cast<uint32_t>(setup.swizzle_data.size());
216 u32 dummy = 0; 217 u32 dummy = 0;
217 for (unsigned int i = 0; i < setup.swizzle_data.size(); ++i) { 218 for (unsigned int i = 0; i < setup.swizzle_data.size(); ++i) {
218 QueueForWriting(reinterpret_cast<const u8*>(&setup.swizzle_data[i]), sizeof(setup.swizzle_data[i])); 219 QueueForWriting(reinterpret_cast<const u8*>(&setup.swizzle_data[i]), sizeof(setup.swizzle_data[i]));
@@ -264,7 +265,7 @@ void DumpShader(const std::string& filename, const Regs::ShaderConfig& config, c
264 constant_table.emplace_back(constant); 265 constant_table.emplace_back(constant);
265 } 266 }
266 dvle.constant_table_offset = write_offset - dvlb.dvle_offset; 267 dvle.constant_table_offset = write_offset - dvlb.dvle_offset;
267 dvle.constant_table_size = constant_table.size(); 268 dvle.constant_table_size = static_cast<uint32_t>(constant_table.size());
268 for (const auto& constant : constant_table) { 269 for (const auto& constant : constant_table) {
269 QueueForWriting(reinterpret_cast<const u8*>(&constant), sizeof(constant)); 270 QueueForWriting(reinterpret_cast<const u8*>(&constant), sizeof(constant));
270 } 271 }
diff --git a/src/video_core/debug_utils/debug_utils.h b/src/video_core/debug_utils/debug_utils.h
index be2d0301a..f628292a4 100644
--- a/src/video_core/debug_utils/debug_utils.h
+++ b/src/video_core/debug_utils/debug_utils.h
@@ -40,7 +40,7 @@ public:
40 PicaCommandProcessed, 40 PicaCommandProcessed,
41 IncomingPrimitiveBatch, 41 IncomingPrimitiveBatch,
42 FinishedPrimitiveBatch, 42 FinishedPrimitiveBatch,
43 VertexLoaded, 43 VertexShaderInvocation,
44 IncomingDisplayTransfer, 44 IncomingDisplayTransfer,
45 GSPCommandProcessed, 45 GSPCommandProcessed,
46 BufferSwapped, 46 BufferSwapped,
diff --git a/src/video_core/pica_state.h b/src/video_core/pica_state.h
index bbecad850..1059c6ae4 100644
--- a/src/video_core/pica_state.h
+++ b/src/video_core/pica_state.h
@@ -56,7 +56,7 @@ struct State {
56 // Used to buffer partial vertices for immediate-mode rendering. 56 // Used to buffer partial vertices for immediate-mode rendering.
57 Shader::InputVertex input_vertex; 57 Shader::InputVertex input_vertex;
58 // Index of the next attribute to be loaded into `input_vertex`. 58 // Index of the next attribute to be loaded into `input_vertex`.
59 int current_attribute = 0; 59 u32 current_attribute = 0;
60 } immediate; 60 } immediate;
61 61
62 // This is constructed with a dummy triangle topology 62 // This is constructed with a dummy triangle topology
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 519d81aeb..0b471dfd2 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -93,7 +93,7 @@ RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) {
93 state.Apply(); 93 state.Apply();
94 94
95 for (size_t i = 0; i < lighting_luts.size(); ++i) { 95 for (size_t i = 0; i < lighting_luts.size(); ++i) {
96 glActiveTexture(GL_TEXTURE3 + i); 96 glActiveTexture(static_cast<GLenum>(GL_TEXTURE3 + i));
97 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA32F, 256, 0, GL_RGBA, GL_FLOAT, nullptr); 97 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA32F, 256, 0, GL_RGBA, GL_FLOAT, nullptr);
98 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 98 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
99 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 99 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 0e9a0be8b..8f424a435 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -192,7 +192,7 @@ void RendererOpenGL::LoadFBToScreenInfo(const GPU::Regs::FramebufferConfig& fram
192 // only allows rows to have a memory alignement of 4. 192 // only allows rows to have a memory alignement of 4.
193 ASSERT(pixel_stride % 4 == 0); 193 ASSERT(pixel_stride % 4 == 0);
194 194
195 if (!Rasterizer()->AccelerateDisplay(framebuffer, framebuffer_addr, pixel_stride, screen_info)) { 195 if (!Rasterizer()->AccelerateDisplay(framebuffer, framebuffer_addr, static_cast<u32>(pixel_stride), screen_info)) {
196 // Reset the screen info's display texture to its own permanent texture 196 // Reset the screen info's display texture to its own permanent texture
197 screen_info.display_texture = screen_info.texture.resource.handle; 197 screen_info.display_texture = screen_info.texture.resource.handle;
198 screen_info.display_texcoords = MathUtil::Rectangle<float>(0.f, 0.f, 1.f, 1.f); 198 screen_info.display_texcoords = MathUtil::Rectangle<float>(0.f, 0.f, 1.f, 1.f);
@@ -473,12 +473,6 @@ static void DebugHandler(GLenum source, GLenum type, GLuint id, GLenum severity,
473bool RendererOpenGL::Init() { 473bool RendererOpenGL::Init() {
474 render_window->MakeCurrent(); 474 render_window->MakeCurrent();
475 475
476 // TODO: Make frontends initialize this, so they can use gladLoadGLLoader with their own loaders
477 if (!gladLoadGL()) {
478 LOG_CRITICAL(Render_OpenGL, "Failed to initialize GL functions! Exiting...");
479 exit(-1);
480 }
481
482 if (GLAD_GL_KHR_debug) { 476 if (GLAD_GL_KHR_debug) {
483 glEnable(GL_DEBUG_OUTPUT); 477 glEnable(GL_DEBUG_OUTPUT);
484 glDebugMessageCallback(DebugHandler, nullptr); 478 glDebugMessageCallback(DebugHandler, nullptr);