summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitmodules3
-rwxr-xr-x.travis-build.sh9
-rw-r--r--CMakeLists.txt2
-rw-r--r--appveyor.yml9
m---------externals/catch0
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/audio_core/CMakeLists.txt2
-rw-r--r--src/audio_core/hle/dsp.cpp59
-rw-r--r--src/audio_core/hle/dsp.h2
-rw-r--r--src/audio_core/hle/mixers.cpp201
-rw-r--r--src/audio_core/hle/mixers.h63
-rw-r--r--src/citra/citra.cpp8
-rw-r--r--src/citra/config.cpp5
-rw-r--r--src/citra_qt/CMakeLists.txt12
-rw-r--r--src/citra_qt/config.cpp6
-rw-r--r--src/citra_qt/config/controller_config.cpp95
-rw-r--r--src/citra_qt/config/controller_config.h56
-rw-r--r--src/citra_qt/config/controller_config.ui308
-rw-r--r--src/citra_qt/config/controller_config_util.cpp125
-rw-r--r--src/citra_qt/config/controller_config_util.h82
-rw-r--r--src/citra_qt/configure.ui11
-rw-r--r--src/citra_qt/configure_audio.cpp44
-rw-r--r--src/citra_qt/configure_audio.h27
-rw-r--r--src/citra_qt/configure_audio.ui48
-rw-r--r--src/citra_qt/configure_dialog.cpp1
-rw-r--r--src/citra_qt/debugger/callstack.cpp5
-rw-r--r--src/citra_qt/debugger/graphics_cmdlists.cpp125
-rw-r--r--src/citra_qt/debugger/graphics_cmdlists.h22
-rw-r--r--src/citra_qt/debugger/graphics_framebuffer.cpp356
-rw-r--r--src/citra_qt/debugger/graphics_framebuffer.h76
-rw-r--r--src/citra_qt/debugger/graphics_surface.cpp736
-rw-r--r--src/citra_qt/debugger/graphics_surface.h120
-rw-r--r--src/citra_qt/debugger/profiler.cpp16
-rw-r--r--src/citra_qt/game_list.cpp35
-rw-r--r--src/citra_qt/game_list_p.h55
-rw-r--r--src/citra_qt/main.cpp29
-rw-r--r--src/citra_qt/main.h1
-rw-r--r--src/common/common_funcs.h30
-rw-r--r--src/common/file_util.cpp48
-rw-r--r--src/common/file_util.h12
-rw-r--r--src/core/CMakeLists.txt14
-rw-r--r--src/core/arm/arm_interface.h1
-rw-r--r--src/core/arm/dyncom/arm_dyncom_dec.cpp4
-rw-r--r--src/core/arm/dyncom/arm_dyncom_interpreter.cpp12
-rw-r--r--src/core/arm/skyeye_common/vfp/vfp_helper.h20
-rw-r--r--src/core/arm/skyeye_common/vfp/vfpdouble.cpp142
-rw-r--r--src/core/arm/skyeye_common/vfp/vfpsingle.cpp142
-rw-r--r--src/core/file_sys/archive_backend.cpp12
-rw-r--r--src/core/gdbstub/gdbstub.cpp2
-rw-r--r--src/core/hle/applets/mii_selector.cpp21
-rw-r--r--src/core/hle/applets/mii_selector.h4
-rw-r--r--src/core/hle/applets/swkbd.cpp17
-rw-r--r--src/core/hle/kernel/thread.cpp4
-rw-r--r--src/core/hle/kernel/vm_manager.cpp1
-rw-r--r--src/core/hle/result.h1
-rw-r--r--src/core/hle/service/act_a.cpp26
-rw-r--r--src/core/hle/service/act_a.h (renamed from src/core/hle/service/dlp_srvr.h)6
-rw-r--r--src/core/hle/service/act_u.cpp3
-rw-r--r--src/core/hle/service/apt/apt.cpp76
-rw-r--r--src/core/hle/service/apt/apt.h50
-rw-r--r--src/core/hle/service/apt/apt_a.cpp6
-rw-r--r--src/core/hle/service/apt/apt_s.cpp8
-rw-r--r--src/core/hle/service/apt/apt_u.cpp8
-rw-r--r--src/core/hle/service/cfg/cfg.cpp58
-rw-r--r--src/core/hle/service/dlp/dlp.cpp24
-rw-r--r--src/core/hle/service/dlp/dlp.h15
-rw-r--r--src/core/hle/service/dlp/dlp_clnt.cpp20
-rw-r--r--src/core/hle/service/dlp/dlp_clnt.h22
-rw-r--r--src/core/hle/service/dlp/dlp_fkcl.cpp20
-rw-r--r--src/core/hle/service/dlp/dlp_fkcl.h22
-rw-r--r--src/core/hle/service/dlp/dlp_srvr.cpp (renamed from src/core/hle/service/dlp_srvr.cpp)22
-rw-r--r--src/core/hle/service/dlp/dlp_srvr.h22
-rw-r--r--src/core/hle/service/dsp_dsp.cpp21
-rw-r--r--src/core/hle/service/frd/frd.cpp10
-rw-r--r--src/core/hle/service/fs/archive.cpp22
-rw-r--r--src/core/hle/service/gsp_gpu.cpp119
-rw-r--r--src/core/hle/service/ptm/ptm.cpp16
-rw-r--r--src/core/hle/service/ptm/ptm.h8
-rw-r--r--src/core/hle/service/ptm/ptm_sysm.cpp4
-rw-r--r--src/core/hle/service/service.cpp7
-rw-r--r--src/core/hle/service/soc_u.cpp100
-rw-r--r--src/core/hle/service/ssl_c.cpp7
-rw-r--r--src/core/hle/svc.cpp8
-rw-r--r--src/core/loader/3dsx.cpp3
-rw-r--r--src/core/loader/3dsx.h8
-rw-r--r--src/core/loader/elf.h8
-rw-r--r--src/core/loader/loader.cpp60
-rw-r--r--src/core/loader/loader.h69
-rw-r--r--src/core/loader/ncch.cpp9
-rw-r--r--src/core/loader/ncch.h8
-rw-r--r--src/core/loader/smdh.cpp54
-rw-r--r--src/core/loader/smdh.h82
-rw-r--r--src/core/memory.cpp236
-rw-r--r--src/core/memory.h8
-rw-r--r--src/core/memory_setup.h2
-rw-r--r--src/core/mmio.h6
-rw-r--r--src/core/settings.h3
-rw-r--r--src/tests/CMakeLists.txt16
-rw-r--r--src/tests/tests.cpp9
-rw-r--r--src/video_core/command_processor.cpp24
-rw-r--r--src/video_core/debug_utils/debug_utils.cpp199
-rw-r--r--src/video_core/debug_utils/debug_utils.h6
-rw-r--r--src/video_core/pica.h40
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp121
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h53
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.cpp6
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp7
-rw-r--r--src/video_core/renderer_opengl/gl_state.h2
-rw-r--r--src/video_core/renderer_opengl/pica_to_gl.h20
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp2
-rw-r--r--src/video_core/shader/shader.cpp112
-rw-r--r--src/video_core/shader/shader.h40
-rw-r--r--src/video_core/shader/shader_interpreter.cpp12
-rw-r--r--src/video_core/shader/shader_interpreter.h2
-rw-r--r--src/video_core/shader/shader_jit_x64.cpp32
-rw-r--r--src/video_core/shader/shader_jit_x64.h6
-rw-r--r--src/video_core/vertex_loader.cpp8
-rw-r--r--src/video_core/vertex_loader.h23
118 files changed, 3193 insertions, 2075 deletions
diff --git a/.gitmodules b/.gitmodules
index db0905b3d..1f0b80768 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -10,3 +10,6 @@
[submodule "soundtouch"]
path = externals/soundtouch
url = https://github.com/citra-emu/ext-soundtouch.git
+[submodule "catch"]
+ path = externals/catch
+ url = https://github.com/philsquared/Catch.git
diff --git a/.travis-build.sh b/.travis-build.sh
index e06a4299b..511df04ac 100755
--- a/.travis-build.sh
+++ b/.travis-build.sh
@@ -18,9 +18,16 @@ if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then
mkdir build && cd build
cmake -DCITRA_FORCE_QT4=ON ..
make -j4
+
+ ctest -VV -C Release
elif [ "$TRAVIS_OS_NAME" = "osx" ]; then
+ set -o pipefail
+
export Qt5_DIR=$(brew --prefix)/opt/qt5
+
mkdir build && cd build
cmake .. -GXcode
- xcodebuild -configuration Release | xcpretty -c && exit ${PIPESTATUS[0]}
+ xcodebuild -configuration Release | xcpretty -c
+
+ ctest -VV -C Release
fi
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8f2898973..f7b0af115 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -255,6 +255,8 @@ endif()
add_subdirectory(externals/soundtouch)
+enable_testing()
+
add_subdirectory(src)
# Install freedesktop.org metadata files, following those specifications:
diff --git a/appveyor.yml b/appveyor.yml
index 1edd7041f..fa4134384 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -22,7 +22,14 @@ before_build:
- cmake -G "Visual Studio 14 2015 Win64" -DCITRA_USE_BUNDLED_QT=1 -DCITRA_USE_BUNDLED_SDL2=1 ..
- cd ..
-after_build:
+build:
+ project: build/citra.sln
+ parallel: true
+
+test_script:
+ - cd build && ctest -VV -C Release && cd ..
+
+on_success:
# copying the needed QT Dlls is now done post build. See the CMakeLists.txt file in the citra-qt folder
- ps: >
if (!"$env:APPVEYOR_PULL_REQUEST_TITLE" -and ("$env:APPVEYOR_REPO_BRANCH" -eq "master"))
diff --git a/externals/catch b/externals/catch
new file mode 160000
+Subproject c984fc3ecde60b59efa2203e82261acac8ac850
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index de4fe716a..1e1245160 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -5,6 +5,7 @@ add_subdirectory(common)
add_subdirectory(core)
add_subdirectory(video_core)
add_subdirectory(audio_core)
+add_subdirectory(tests)
if (ENABLE_SDL2)
add_subdirectory(citra)
endif()
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index eba0a5697..a72a907ef 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -3,6 +3,7 @@ set(SRCS
codec.cpp
hle/dsp.cpp
hle/filter.cpp
+ hle/mixers.cpp
hle/pipe.cpp
hle/source.cpp
interpolate.cpp
@@ -16,6 +17,7 @@ set(HEADERS
hle/common.h
hle/dsp.h
hle/filter.h
+ hle/mixers.h
hle/pipe.h
hle/source.h
interpolate.h
diff --git a/src/audio_core/hle/dsp.cpp b/src/audio_core/hle/dsp.cpp
index 5113ad8ca..0640e1eff 100644
--- a/src/audio_core/hle/dsp.cpp
+++ b/src/audio_core/hle/dsp.cpp
@@ -6,6 +6,7 @@
#include <memory>
#include "audio_core/hle/dsp.h"
+#include "audio_core/hle/mixers.h"
#include "audio_core/hle/pipe.h"
#include "audio_core/hle/source.h"
#include "audio_core/sink.h"
@@ -14,6 +15,8 @@
namespace DSP {
namespace HLE {
+// Region management
+
std::array<SharedMemory, 2> g_regions;
static size_t CurrentRegionIndex() {
@@ -41,16 +44,57 @@ static SharedMemory& WriteRegion() {
return g_regions[1 - CurrentRegionIndex()];
}
+// Audio processing and mixing
+
static std::array<Source, num_sources> sources = {
Source(0), Source(1), Source(2), Source(3), Source(4), Source(5),
Source(6), Source(7), Source(8), Source(9), Source(10), Source(11),
Source(12), Source(13), Source(14), Source(15), Source(16), Source(17),
Source(18), Source(19), Source(20), Source(21), Source(22), Source(23)
};
+static Mixers mixers;
+
+static StereoFrame16 GenerateCurrentFrame() {
+ SharedMemory& read = ReadRegion();
+ SharedMemory& write = WriteRegion();
+
+ std::array<QuadFrame32, 3> intermediate_mixes = {};
+
+ // Generate intermediate mixes
+ for (size_t i = 0; i < num_sources; i++) {
+ write.source_statuses.status[i] = sources[i].Tick(read.source_configurations.config[i], read.adpcm_coefficients.coeff[i]);
+ for (size_t mix = 0; mix < 3; mix++) {
+ sources[i].MixInto(intermediate_mixes[mix], mix);
+ }
+ }
+
+ // Generate final mix
+ write.dsp_status = mixers.Tick(read.dsp_configuration, read.intermediate_mix_samples, write.intermediate_mix_samples, intermediate_mixes);
+
+ StereoFrame16 output_frame = mixers.GetOutput();
+
+ // Write current output frame to the shared memory region
+ for (size_t samplei = 0; samplei < output_frame.size(); samplei++) {
+ for (size_t channeli = 0; channeli < output_frame[0].size(); channeli++) {
+ write.final_samples.pcm16[samplei][channeli] = s16_le(output_frame[samplei][channeli]);
+ }
+ }
+
+ return output_frame;
+}
+
+// Audio output
static std::unique_ptr<AudioCore::Sink> sink;
static AudioCore::TimeStretcher time_stretcher;
+static void OutputCurrentFrame(const StereoFrame16& frame) {
+ time_stretcher.AddSamples(&frame[0][0], frame.size());
+ sink->EnqueueSamples(time_stretcher.Process(sink->SamplesInQueue()));
+}
+
+// Public Interface
+
void Init() {
DSP::HLE::ResetPipes();
@@ -58,6 +102,8 @@ void Init() {
source.Reset();
}
+ mixers.Reset();
+
time_stretcher.Reset();
if (sink) {
time_stretcher.SetOutputSampleRate(sink->GetNativeSampleRate());
@@ -75,17 +121,12 @@ void Shutdown() {
}
bool Tick() {
- SharedMemory& read = ReadRegion();
- SharedMemory& write = WriteRegion();
+ StereoFrame16 current_frame = {};
- std::array<QuadFrame32, 3> intermediate_mixes = {};
+ // TODO: Check dsp::DSP semaphore (which indicates emulated application has finished writing to shared memory region)
+ current_frame = GenerateCurrentFrame();
- for (size_t i = 0; i < num_sources; i++) {
- write.source_statuses.status[i] = sources[i].Tick(read.source_configurations.config[i], read.adpcm_coefficients.coeff[i]);
- for (size_t mix = 0; mix < 3; mix++) {
- sources[i].MixInto(intermediate_mixes[mix], mix);
- }
- }
+ OutputCurrentFrame(current_frame);
return true;
}
diff --git a/src/audio_core/hle/dsp.h b/src/audio_core/hle/dsp.h
index f6e53f68f..9275cd7de 100644
--- a/src/audio_core/hle/dsp.h
+++ b/src/audio_core/hle/dsp.h
@@ -428,7 +428,7 @@ ASSERT_DSP_STRUCT(DspStatus, 32);
/// Final mixed output in PCM16 stereo format, what you hear out of the speakers.
/// When the application writes to this region it has no effect.
struct FinalMixSamples {
- s16_le pcm16[2 * samples_per_frame];
+ s16_le pcm16[samples_per_frame][2];
};
ASSERT_DSP_STRUCT(FinalMixSamples, 640);
diff --git a/src/audio_core/hle/mixers.cpp b/src/audio_core/hle/mixers.cpp
new file mode 100644
index 000000000..18335f7f0
--- /dev/null
+++ b/src/audio_core/hle/mixers.cpp
@@ -0,0 +1,201 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <cstddef>
+
+#include "audio_core/hle/common.h"
+#include "audio_core/hle/dsp.h"
+#include "audio_core/hle/mixers.h"
+
+#include "common/assert.h"
+#include "common/logging/log.h"
+#include "common/math_util.h"
+
+namespace DSP {
+namespace HLE {
+
+void Mixers::Reset() {
+ current_frame.fill({});
+ state = {};
+}
+
+DspStatus Mixers::Tick(DspConfiguration& config,
+ const IntermediateMixSamples& read_samples,
+ IntermediateMixSamples& write_samples,
+ const std::array<QuadFrame32, 3>& input)
+{
+ ParseConfig(config);
+
+ AuxReturn(read_samples);
+ AuxSend(write_samples, input);
+
+ MixCurrentFrame();
+
+ return GetCurrentStatus();
+}
+
+void Mixers::ParseConfig(DspConfiguration& config) {
+ if (!config.dirty_raw) {
+ return;
+ }
+
+ if (config.mixer1_enabled_dirty) {
+ config.mixer1_enabled_dirty.Assign(0);
+ state.mixer1_enabled = config.mixer1_enabled != 0;
+ LOG_TRACE(Audio_DSP, "mixers mixer1_enabled = %hu", config.mixer1_enabled);
+ }
+
+ if (config.mixer2_enabled_dirty) {
+ config.mixer2_enabled_dirty.Assign(0);
+ state.mixer2_enabled = config.mixer2_enabled != 0;
+ LOG_TRACE(Audio_DSP, "mixers mixer2_enabled = %hu", config.mixer2_enabled);
+ }
+
+ if (config.volume_0_dirty) {
+ config.volume_0_dirty.Assign(0);
+ state.intermediate_mixer_volume[0] = config.volume[0];
+ LOG_TRACE(Audio_DSP, "mixers volume[0] = %f", config.volume[0]);
+ }
+
+ if (config.volume_1_dirty) {
+ config.volume_1_dirty.Assign(0);
+ state.intermediate_mixer_volume[1] = config.volume[1];
+ LOG_TRACE(Audio_DSP, "mixers volume[1] = %f", config.volume[1]);
+ }
+
+ if (config.volume_2_dirty) {
+ config.volume_2_dirty.Assign(0);
+ state.intermediate_mixer_volume[2] = config.volume[2];
+ LOG_TRACE(Audio_DSP, "mixers volume[2] = %f", config.volume[2]);
+ }
+
+ if (config.output_format_dirty) {
+ config.output_format_dirty.Assign(0);
+ state.output_format = config.output_format;
+ LOG_TRACE(Audio_DSP, "mixers output_format = %zu", static_cast<size_t>(config.output_format));
+ }
+
+ if (config.headphones_connected_dirty) {
+ config.headphones_connected_dirty.Assign(0);
+ // Do nothing.
+ // (Note: Whether headphones are connected does affect coefficients used for surround sound.)
+ LOG_TRACE(Audio_DSP, "mixers headphones_connected=%hu", config.headphones_connected);
+ }
+
+ if (config.dirty_raw) {
+ LOG_DEBUG(Audio_DSP, "mixers remaining_dirty=%x", config.dirty_raw);
+ }
+
+ config.dirty_raw = 0;
+}
+
+static s16 ClampToS16(s32 value) {
+ return static_cast<s16>(MathUtil::Clamp(value, -32768, 32767));
+}
+
+static std::array<s16, 2> AddAndClampToS16(const std::array<s16, 2>& a, const std::array<s16, 2>& b) {
+ return {
+ ClampToS16(static_cast<s32>(a[0]) + static_cast<s32>(b[0])),
+ ClampToS16(static_cast<s32>(a[1]) + static_cast<s32>(b[1]))
+ };
+}
+
+void Mixers::DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& samples) {
+ // TODO(merry): Limiter. (Currently we're performing final mixing assuming a disabled limiter.)
+
+ switch (state.output_format) {
+ case OutputFormat::Mono:
+ std::transform(current_frame.begin(), current_frame.end(), samples.begin(), current_frame.begin(),
+ [gain](const std::array<s16, 2>& accumulator, const std::array<s32, 4>& sample) -> std::array<s16, 2> {
+ // Downmix to mono
+ s16 mono = ClampToS16(static_cast<s32>((gain * sample[0] + gain * sample[1] + gain * sample[2] + gain * sample[3]) / 2));
+ // Mix into current frame
+ return AddAndClampToS16(accumulator, { mono, mono });
+ });
+ return;
+
+ case OutputFormat::Surround:
+ // TODO(merry): Implement surround sound.
+ // fallthrough
+
+ case OutputFormat::Stereo:
+ std::transform(current_frame.begin(), current_frame.end(), samples.begin(), current_frame.begin(),
+ [gain](const std::array<s16, 2>& accumulator, const std::array<s32, 4>& sample) -> std::array<s16, 2> {
+ // Downmix to stereo
+ s16 left = ClampToS16(static_cast<s32>(gain * sample[0] + gain * sample[2]));
+ s16 right = ClampToS16(static_cast<s32>(gain * sample[1] + gain * sample[3]));
+ // Mix into current frame
+ return AddAndClampToS16(accumulator, { left, right });
+ });
+ return;
+ }
+
+ UNREACHABLE_MSG("Invalid output_format %zu", static_cast<size_t>(state.output_format));
+}
+
+void Mixers::AuxReturn(const IntermediateMixSamples& read_samples) {
+ // NOTE: read_samples.mix{1,2}.pcm32 annoyingly have their dimensions in reverse order to QuadFrame32.
+
+ if (state.mixer1_enabled) {
+ for (size_t sample = 0; sample < samples_per_frame; sample++) {
+ for (size_t channel = 0; channel < 4; channel++) {
+ state.intermediate_mix_buffer[1][sample][channel] = read_samples.mix1.pcm32[channel][sample];
+ }
+ }
+ }
+
+ if (state.mixer2_enabled) {
+ for (size_t sample = 0; sample < samples_per_frame; sample++) {
+ for (size_t channel = 0; channel < 4; channel++) {
+ state.intermediate_mix_buffer[2][sample][channel] = read_samples.mix2.pcm32[channel][sample];
+ }
+ }
+ }
+}
+
+void Mixers::AuxSend(IntermediateMixSamples& write_samples, const std::array<QuadFrame32, 3>& input) {
+ // NOTE: read_samples.mix{1,2}.pcm32 annoyingly have their dimensions in reverse order to QuadFrame32.
+
+ state.intermediate_mix_buffer[0] = input[0];
+
+ if (state.mixer1_enabled) {
+ for (size_t sample = 0; sample < samples_per_frame; sample++) {
+ for (size_t channel = 0; channel < 4; channel++) {
+ write_samples.mix1.pcm32[channel][sample] = input[1][sample][channel];
+ }
+ }
+ } else {
+ state.intermediate_mix_buffer[1] = input[1];
+ }
+
+ if (state.mixer2_enabled) {
+ for (size_t sample = 0; sample < samples_per_frame; sample++) {
+ for (size_t channel = 0; channel < 4; channel++) {
+ write_samples.mix2.pcm32[channel][sample] = input[2][sample][channel];
+ }
+ }
+ } else {
+ state.intermediate_mix_buffer[2] = input[2];
+ }
+}
+
+void Mixers::MixCurrentFrame() {
+ current_frame.fill({});
+
+ for (size_t mix = 0; mix < 3; mix++) {
+ DownmixAndMixIntoCurrentFrame(state.intermediate_mixer_volume[mix], state.intermediate_mix_buffer[mix]);
+ }
+
+ // TODO(merry): Compressor. (We currently assume a disabled compressor.)
+}
+
+DspStatus Mixers::GetCurrentStatus() const {
+ DspStatus status;
+ status.unknown = 0;
+ status.dropped_frames = 0;
+ return status;
+}
+
+} // namespace HLE
+} // namespace DSP
diff --git a/src/audio_core/hle/mixers.h b/src/audio_core/hle/mixers.h
new file mode 100644
index 000000000..b52952eb5
--- /dev/null
+++ b/src/audio_core/hle/mixers.h
@@ -0,0 +1,63 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+
+#include "audio_core/hle/common.h"
+#include "audio_core/hle/dsp.h"
+
+namespace DSP {
+namespace HLE {
+
+class Mixers final {
+public:
+ Mixers() {
+ Reset();
+ }
+
+ void Reset();
+
+ DspStatus Tick(DspConfiguration& config,
+ const IntermediateMixSamples& read_samples,
+ IntermediateMixSamples& write_samples,
+ const std::array<QuadFrame32, 3>& input);
+
+ StereoFrame16 GetOutput() const {
+ return current_frame;
+ }
+
+private:
+ StereoFrame16 current_frame = {};
+
+ using OutputFormat = DspConfiguration::OutputFormat;
+
+ struct {
+ std::array<float, 3> intermediate_mixer_volume = {};
+
+ bool mixer1_enabled = false;
+ bool mixer2_enabled = false;
+ std::array<QuadFrame32, 3> intermediate_mix_buffer = {};
+
+ OutputFormat output_format = OutputFormat::Stereo;
+
+ } state;
+
+ /// INTERNAL: Update our internal state based on the current config.
+ void ParseConfig(DspConfiguration& config);
+ /// INTERNAL: Read samples from shared memory that have been modified by the ARM11.
+ void AuxReturn(const IntermediateMixSamples& read_samples);
+ /// INTERNAL: Write samples to shared memory for the ARM11 to modify.
+ void AuxSend(IntermediateMixSamples& write_samples, const std::array<QuadFrame32, 3>& input);
+ /// INTERNAL: Mix current_frame.
+ void MixCurrentFrame();
+ /// INTERNAL: Downmix from quadraphonic to stereo based on status.output_format and accumulate into current_frame.
+ void DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& samples);
+ /// INTERNAL: Generate DspStatus based on internal state.
+ DspStatus GetCurrentStatus() const;
+};
+
+} // namespace HLE
+} // namespace DSP
diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp
index b4501eb2e..e01216734 100644
--- a/src/citra/citra.cpp
+++ b/src/citra/citra.cpp
@@ -114,7 +114,13 @@ int main(int argc, char **argv) {
System::Init(emu_window.get());
SCOPE_EXIT({ System::Shutdown(); });
- Loader::ResultStatus load_result = Loader::LoadFile(boot_filename);
+ std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(boot_filename);
+ if (!loader) {
+ LOG_CRITICAL(Frontend, "Failed to obtain loader for %s!", boot_filename.c_str());
+ return -1;
+ }
+
+ Loader::ResultStatus load_result = loader->Load();
if (Loader::ResultStatus::Success != load_result) {
LOG_CRITICAL(Frontend, "Failed to load ROM (Error %i)!", load_result);
return -1;
diff --git a/src/citra/config.cpp b/src/citra/config.cpp
index fb8dd9ba3..22cb51ea8 100644
--- a/src/citra/config.cpp
+++ b/src/citra/config.cpp
@@ -82,8 +82,9 @@ void Config::ReadValues() {
// Data Storage
Settings::values.use_virtual_sd = sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true);
- // System Region
- Settings::values.region_value = sdl2_config->GetInteger("System Region", "region_value", 1);
+ // System
+ Settings::values.is_new_3ds = sdl2_config->GetBoolean("System", "is_new_3ds", false);
+ Settings::values.region_value = sdl2_config->GetInteger("System", "region_value", 1);
// Miscellaneous
Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Info");
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt
index 3f0099200..43a766053 100644
--- a/src/citra_qt/CMakeLists.txt
+++ b/src/citra_qt/CMakeLists.txt
@@ -2,8 +2,6 @@ set(CMAKE_AUTOMOC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(SRCS
- config/controller_config.cpp
- config/controller_config_util.cpp
config.cpp
debugger/callstack.cpp
debugger/disassembler.cpp
@@ -11,7 +9,7 @@ set(SRCS
debugger/graphics_breakpoint_observer.cpp
debugger/graphics_breakpoints.cpp
debugger/graphics_cmdlists.cpp
- debugger/graphics_framebuffer.cpp
+ debugger/graphics_surface.cpp
debugger/graphics_tracing.cpp
debugger/graphics_vertex_shader.cpp
debugger/profiler.cpp
@@ -20,6 +18,7 @@ set(SRCS
util/spinbox.cpp
util/util.cpp
bootmanager.cpp
+ configure_audio.cpp
configure_debug.cpp
configure_dialog.cpp
configure_general.cpp
@@ -32,8 +31,6 @@ set(SRCS
)
set(HEADERS
- config/controller_config.h
- config/controller_config_util.h
config.h
debugger/callstack.h
debugger/disassembler.h
@@ -42,7 +39,7 @@ set(HEADERS
debugger/graphics_breakpoints.h
debugger/graphics_breakpoints_p.h
debugger/graphics_cmdlists.h
- debugger/graphics_framebuffer.h
+ debugger/graphics_surface.h
debugger/graphics_tracing.h
debugger/graphics_vertex_shader.h
debugger/profiler.h
@@ -51,6 +48,7 @@ set(HEADERS
util/spinbox.h
util/util.h
bootmanager.h
+ configure_audio.h
configure_debug.h
configure_dialog.h
configure_general.h
@@ -63,12 +61,12 @@ set(HEADERS
)
set(UIS
- config/controller_config.ui
debugger/callstack.ui
debugger/disassembler.ui
debugger/profiler.ui
debugger/registers.ui
configure.ui
+ configure_audio.ui
configure_debug.ui
configure_general.ui
hotkeys.ui
diff --git a/src/citra_qt/config.cpp b/src/citra_qt/config.cpp
index 539fafb6f..ba7edaff9 100644
--- a/src/citra_qt/config.cpp
+++ b/src/citra_qt/config.cpp
@@ -65,7 +65,8 @@ void Config::ReadValues() {
Settings::values.use_virtual_sd = qt_config->value("use_virtual_sd", true).toBool();
qt_config->endGroup();
- qt_config->beginGroup("System Region");
+ qt_config->beginGroup("System");
+ Settings::values.is_new_3ds = qt_config->value("is_new_3ds", false).toBool();
Settings::values.region_value = qt_config->value("region_value", 1).toInt();
qt_config->endGroup();
@@ -156,7 +157,8 @@ void Config::SaveValues() {
qt_config->setValue("use_virtual_sd", Settings::values.use_virtual_sd);
qt_config->endGroup();
- qt_config->beginGroup("System Region");
+ qt_config->beginGroup("System");
+ qt_config->setValue("is_new_3ds", Settings::values.is_new_3ds);
qt_config->setValue("region_value", Settings::values.region_value);
qt_config->endGroup();
diff --git a/src/citra_qt/config/controller_config.cpp b/src/citra_qt/config/controller_config.cpp
deleted file mode 100644
index 512879f1b..000000000
--- a/src/citra_qt/config/controller_config.cpp
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <QDialogButtonBox>
-
-#include "controller_config.h"
-#include "controller_config_util.h"
-
-/* TODO(bunnei): ImplementMe
-
-using common::Config;
-
-GControllerConfig::GControllerConfig(common::Config::ControllerPort* initial_config, QWidget* parent) : QWidget(parent)
-{
- ui.setupUi(this);
- ((QGridLayout*)ui.mainStickTab->layout())->addWidget(new GStickConfig(Config::ANALOG_LEFT, Config::ANALOG_RIGHT, Config::ANALOG_UP, Config::ANALOG_DOWN, this, this), 1, 1);
- ((QGridLayout*)ui.cStickTab->layout())->addWidget(new GStickConfig(Config::C_LEFT, Config::C_RIGHT, Config::C_UP, Config::C_DOWN, this, this), 1, 1);
- ((QGridLayout*)ui.dPadTab->layout())->addWidget(new GStickConfig(Config::DPAD_LEFT, Config::DPAD_RIGHT, Config::DPAD_UP, Config::DPAD_DOWN, this, this), 1, 1);
-
- // TODO: Arrange these more compactly?
- QVBoxLayout* layout = (QVBoxLayout*)ui.buttonsTab->layout();
- layout->addWidget(new GButtonConfigGroup("A Button", Config::BUTTON_A, this, ui.buttonsTab));
- layout->addWidget(new GButtonConfigGroup("B Button", Config::BUTTON_B, this, ui.buttonsTab));
- layout->addWidget(new GButtonConfigGroup("X Button", Config::BUTTON_X, this, ui.buttonsTab));
- layout->addWidget(new GButtonConfigGroup("Y Button", Config::BUTTON_Y, this, ui.buttonsTab));
- layout->addWidget(new GButtonConfigGroup("Z Button", Config::BUTTON_Z, this, ui.buttonsTab));
- layout->addWidget(new GButtonConfigGroup("L Trigger", Config::TRIGGER_L, this, ui.buttonsTab));
- layout->addWidget(new GButtonConfigGroup("R Trigger", Config::TRIGGER_R, this, ui.buttonsTab));
- layout->addWidget(new GButtonConfigGroup("Start Button", Config::BUTTON_START, this, ui.buttonsTab));
-
- memcpy(config, initial_config, sizeof(config));
-
- emit ActivePortChanged(config[0]);
-}
-
-void GControllerConfig::OnKeyConfigChanged(common::Config::Control id, int key, const QString& name)
-{
- if (InputSourceJoypad())
- {
- config[GetActiveController()].pads.key_code[id] = key;
- }
- else
- {
- config[GetActiveController()].keys.key_code[id] = key;
- }
- emit ActivePortChanged(config[GetActiveController()]);
-}
-
-int GControllerConfig::GetActiveController()
-{
- return ui.activeControllerCB->currentIndex();
-}
-
-bool GControllerConfig::InputSourceJoypad()
-{
- return ui.inputSourceCB->currentIndex() == 1;
-}
-
-GControllerConfigDialog::GControllerConfigDialog(common::Config::ControllerPort* controller_ports, QWidget* parent) : QDialog(parent), config_ptr(controller_ports)
-{
- setWindowTitle(tr("Input configuration"));
-
- QVBoxLayout* layout = new QVBoxLayout(this);
- config_widget = new GControllerConfig(controller_ports, this);
- layout->addWidget(config_widget);
-
- QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
- layout->addWidget(buttons);
-
- connect(buttons, SIGNAL(rejected()), this, SLOT(reject()));
- connect(buttons, SIGNAL(accepted()), this, SLOT(accept()));
-
- connect(this, SIGNAL(accepted()), this, SLOT(EnableChanges()));
-
- layout->setSizeConstraint(QLayout::SetFixedSize);
- setLayout(layout);
- setModal(true);
- show();
-}
-
-void GControllerConfigDialog::EnableChanges()
-{
- for (unsigned int i = 0; i < 4; ++i)
- {
- memcpy(&config_ptr[i], &config_widget->GetControllerConfig(i), sizeof(common::Config::ControllerPort));
-
- if (common::g_config) {
- // Apply changes if running a game
- memcpy(&common::g_config->controller_ports(i), &config_widget->GetControllerConfig(i), sizeof(common::Config::ControllerPort));
- }
- }
-}
-
-*/
diff --git a/src/citra_qt/config/controller_config.h b/src/citra_qt/config/controller_config.h
deleted file mode 100644
index 451593de1..000000000
--- a/src/citra_qt/config/controller_config.h
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#ifndef _CONTROLLER_CONFIG_HXX_
-#define _CONTROLLER_CONFIG_HXX_
-
-#include <QDialog>
-
-//#include "ui_controller_config.h"
-
-/* TODO(bunnei): ImplementMe
-
-#include "config.h"
-
-class GControllerConfig : public QWidget
-{
- Q_OBJECT
-
-public:
- GControllerConfig(common::Config::ControllerPort* initial_config, QWidget* parent = NULL);
-
- const common::Config::ControllerPort& GetControllerConfig(int index) const { return config[index]; }
-
-signals:
- void ActivePortChanged(const common::Config::ControllerPort&);
-
-public slots:
- void OnKeyConfigChanged(common::Config::Control id, int key, const QString& name);
-
-private:
- int GetActiveController();
- bool InputSourceJoypad();
-
- Ui::ControllerConfig ui;
- common::Config::ControllerPort config[4];
-};
-
-class GControllerConfigDialog : public QDialog
-{
- Q_OBJECT
-
-public:
- GControllerConfigDialog(common::Config::ControllerPort* controller_ports, QWidget* parent = NULL);
-
-public slots:
- void EnableChanges();
-
-private:
- GControllerConfig* config_widget;
- common::Config::ControllerPort* config_ptr;
-};
-
-*/
-
-#endif // _CONTROLLER_CONFIG_HXX_
diff --git a/src/citra_qt/config/controller_config.ui b/src/citra_qt/config/controller_config.ui
deleted file mode 100644
index 9f650047b..000000000
--- a/src/citra_qt/config/controller_config.ui
+++ /dev/null
@@ -1,308 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>ControllerConfig</class>
- <widget class="QWidget" name="ControllerConfig">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>503</width>
- <height>293</height>
- </rect>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="windowTitle">
- <string>Controller Configuration</string>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout">
- <property name="sizeConstraint">
- <enum>QLayout::SetFixedSize</enum>
- </property>
- <item>
- <layout class="QGridLayout" name="gridLayout">
- <item row="1" column="1">
- <widget class="QComboBox" name="activeControllerCB">
- <item>
- <property name="text">
- <string>Controller 1</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Controller 2</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Controller 3</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Controller 4</string>
- </property>
- </item>
- </widget>
- </item>
- <item row="0" column="2">
- <spacer name="horizontalSpacer">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="1" column="2">
- <widget class="QCheckBox" name="checkBox">
- <property name="text">
- <string>Enabled</string>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QComboBox" name="inputSourceCB">
- <item>
- <property name="text">
- <string>Keyboard</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Joypad</string>
- </property>
- </item>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QLabel" name="label_2">
- <property name="text">
- <string>Active Controller:</string>
- </property>
- </widget>
- </item>
- <item row="0" column="0">
- <widget class="QLabel" name="label">
- <property name="text">
- <string>Input Source:</string>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QTabWidget" name="tabWidget">
- <property name="currentIndex">
- <number>0</number>
- </property>
- <widget class="QWidget" name="mainStickTab">
- <attribute name="title">
- <string>Main Stick</string>
- </attribute>
- <layout class="QGridLayout" name="gridLayout_3">
- <item row="2" column="2">
- <spacer name="verticalSpacer_2">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="0" column="2">
- <spacer name="verticalSpacer_3">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="1" column="0">
- <spacer name="horizontalSpacer_4">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="1" column="4">
- <spacer name="horizontalSpacer_3">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- </widget>
- <widget class="QWidget" name="cStickTab">
- <attribute name="title">
- <string>C-Stick</string>
- </attribute>
- <layout class="QGridLayout" name="gridLayout_4">
- <item row="1" column="0">
- <spacer name="horizontalSpacer_6">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>0</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="0" column="1">
- <spacer name="verticalSpacer_5">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="2" column="1">
- <spacer name="verticalSpacer_4">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="1" column="2">
- <spacer name="horizontalSpacer_5">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- </widget>
- <widget class="QWidget" name="dPadTab">
- <attribute name="title">
- <string>D-Pad</string>
- </attribute>
- <layout class="QGridLayout" name="gridLayout_5">
- <item row="1" column="2">
- <spacer name="horizontalSpacer_7">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="0" column="1">
- <spacer name="verticalSpacer_7">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="2" column="1">
- <spacer name="verticalSpacer_6">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="1" column="0">
- <spacer name="horizontalSpacer_8">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- </widget>
- <widget class="QWidget" name="buttonsTab">
- <attribute name="title">
- <string>Buttons</string>
- </attribute>
- <layout class="QVBoxLayout" name="verticalLayout_2"/>
- </widget>
- </widget>
- </item>
- </layout>
- </widget>
- <resources/>
- <connections/>
- <slots>
- <signal>ControlsChanged()</signal>
- <signal>MainStickCleared()</signal>
- <signal>CStickCleared()</signal>
- <signal>DPadCleared()</signal>
- <signal>ButtonsCleared()</signal>
- <slot>OnControlsChanged()</slot>
- <slot>OnMainStickCleared()</slot>
- <slot>OnCStickCleared()</slot>
- <slot>OnDPadCleared()</slot>
- <slot>OnButtonsCleared()</slot>
- </slots>
-</ui>
diff --git a/src/citra_qt/config/controller_config_util.cpp b/src/citra_qt/config/controller_config_util.cpp
deleted file mode 100644
index d68b119df..000000000
--- a/src/citra_qt/config/controller_config_util.cpp
+++ /dev/null
@@ -1,125 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <QPushButton>
-#include <QStyle>
-#include <QGridLayout>
-#include <QKeyEvent>
-#include <QHBoxLayout>
-#include <QLabel>
-
-#include "controller_config_util.h"
-
-/* TODO(bunnei): ImplementMe
-GStickConfig::GStickConfig(common::Config::Control leftid, common::Config::Control rightid, common::Config::Control upid, common::Config::Control downid, QObject* change_receiver, QWidget* parent) : QWidget(parent)
-{
- left = new GKeyConfigButton(leftid, style()->standardIcon(QStyle::SP_ArrowLeft), QString(), change_receiver, this);
- right = new GKeyConfigButton(rightid, style()->standardIcon(QStyle::SP_ArrowRight), QString(), change_receiver, this);
- up = new GKeyConfigButton(upid, style()->standardIcon(QStyle::SP_ArrowUp), QString(), change_receiver, this);
- down = new GKeyConfigButton(downid, style()->standardIcon(QStyle::SP_ArrowDown), QString(), change_receiver, this);
- clear = new QPushButton(tr("Clear"), this);
-
- QGridLayout* layout = new QGridLayout(this);
- layout->addWidget(left, 1, 0);
- layout->addWidget(right, 1, 2);
- layout->addWidget(up, 0, 1);
- layout->addWidget(down, 2, 1);
- layout->addWidget(clear, 1, 1);
-
- setLayout(layout);
-}
-
-GKeyConfigButton::GKeyConfigButton(common::Config::Control id, const QIcon& icon, const QString& text, QObject* change_receiver, QWidget* parent) : QPushButton(icon, text, parent), id(id), inputGrabbed(false)
-{
- connect(this, SIGNAL(clicked()), this, SLOT(OnClicked()));
- connect(this, SIGNAL(KeyAssigned(common::Config::Control, int, const QString&)), change_receiver, SLOT(OnKeyConfigChanged(common::Config::Control, int, const QString&)));
- connect(change_receiver, SIGNAL(ActivePortChanged(const common::Config::ControllerPort&)), this, SLOT(OnActivePortChanged(const common::Config::ControllerPort&)));
-}
-
-GKeyConfigButton::GKeyConfigButton(common::Config::Control id, const QString& text, QObject* change_receiver, QWidget* parent) : QPushButton(text, parent), id(id), inputGrabbed(false)
-{
- connect(this, SIGNAL(clicked()), this, SLOT(OnClicked()));
- connect(this, SIGNAL(KeyAssigned(common::Config::Control, int, const QString&)), change_receiver, SLOT(OnKeyConfigChanged(common::Config::Control, int, const QString&)));
- connect(change_receiver, SIGNAL(ActivePortChanged(const common::Config::ControllerPort&)), this, SLOT(OnActivePortChanged(const common::Config::ControllerPort&)));
-}
-
-void GKeyConfigButton::OnActivePortChanged(const common::Config::ControllerPort& config)
-{
- // TODO: Doesn't use joypad struct if that's the input source...
- QString text = QKeySequence(config.keys.key_code[id]).toString(); // has a nicer format
- if (config.keys.key_code[id] == Qt::Key_Shift) text = tr("Shift");
- else if (config.keys.key_code[id] == Qt::Key_Control) text = tr("Control");
- else if (config.keys.key_code[id] == Qt::Key_Alt) text = tr("Alt");
- else if (config.keys.key_code[id] == Qt::Key_Meta) text = tr("Meta");
- setText(text);
-}
-
-void GKeyConfigButton::OnClicked()
-{
- grabKeyboard();
- grabMouse();
- inputGrabbed = true;
-
- old_text = text();
- setText(tr("Input..."));
-}
-
-void GKeyConfigButton::keyPressEvent(QKeyEvent* event)
-{
- if (inputGrabbed)
- {
- releaseKeyboard();
- releaseMouse();
- setText(QString());
-
- // TODO: Doesn't capture "return" key
- // TODO: This doesn't quite work well, yet... find a better way
- QString text = QKeySequence(event->key()).toString(); // has a nicer format than event->text()
- int key = event->key();
- if (event->modifiers() == Qt::ShiftModifier) { text = tr("Shift"); key = Qt::Key_Shift; }
- else if (event->modifiers() == Qt::ControlModifier) { text = tr("Ctrl"); key = Qt::Key_Control; }
- else if (event->modifiers() == Qt::AltModifier) { text = tr("Alt"); key = Qt::Key_Alt; }
- else if (event->modifiers() == Qt::MetaModifier) { text = tr("Meta"); key = Qt::Key_Meta; }
-
- setText(old_text);
- emit KeyAssigned(id, key, text);
-
- inputGrabbed = false;
-
- // TODO: Keys like "return" cause another keyPressEvent to be generated after this one...
- }
-
- QPushButton::keyPressEvent(event); // TODO: Necessary?
-}
-
-void GKeyConfigButton::mousePressEvent(QMouseEvent* event)
-{
- // Abort key assignment
- if (inputGrabbed)
- {
- releaseKeyboard();
- releaseMouse();
- setText(old_text);
- inputGrabbed = false;
- }
-
- QAbstractButton::mousePressEvent(event);
-}
-
-GButtonConfigGroup::GButtonConfigGroup(const QString& name, common::Config::Control id, QObject* change_receiver, QWidget* parent) : QWidget(parent), id(id)
-{
- QHBoxLayout* layout = new QHBoxLayout(this);
-
- QPushButton* clear_button = new QPushButton(tr("Clear"));
-
- layout->addWidget(new QLabel(name, this));
- layout->addWidget(config_button = new GKeyConfigButton(id, QString(), change_receiver, this));
- layout->addWidget(clear_button);
-
- // TODO: connect config_button, clear_button
-
- setLayout(layout);
-}
-
-*/
diff --git a/src/citra_qt/config/controller_config_util.h b/src/citra_qt/config/controller_config_util.h
deleted file mode 100644
index 15e025b57..000000000
--- a/src/citra_qt/config/controller_config_util.h
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#ifndef _CONTROLLER_CONFIG_UTIL_HXX_
-#define _CONTROLLER_CONFIG_UTIL_HXX_
-
-#include <QWidget>
-#include <QPushButton>
-
-/* TODO(bunnei): ImplementMe
-
-#include "config.h"
-
-class GStickConfig : public QWidget
-{
- Q_OBJECT
-
-public:
- // change_receiver needs to have a OnKeyConfigChanged(common::Config::Control, int, const QString&) slot!
- GStickConfig(common::Config::Control leftid, common::Config::Control rightid, common::Config::Control upid, common::Config::Control downid, QObject* change_receiver, QWidget* parent = NULL);
-
-signals:
- void LeftChanged();
- void RightChanged();
- void UpChanged();
- void DownChanged();
-
-private:
- QPushButton* left;
- QPushButton* right;
- QPushButton* up;
- QPushButton* down;
-
- QPushButton* clear;
-};
-
-class GKeyConfigButton : public QPushButton
-{
- Q_OBJECT
-
-public:
- // TODO: change_receiver also needs to have an ActivePortChanged(const common::Config::ControllerPort&) signal
- // change_receiver needs to have a OnKeyConfigChanged(common::Config::Control, int, const QString&) slot!
- GKeyConfigButton(common::Config::Control id, const QIcon& icon, const QString& text, QObject* change_receiver, QWidget* parent);
- GKeyConfigButton(common::Config::Control id, const QString& text, QObject* change_receiver, QWidget* parent);
-
-signals:
- void KeyAssigned(common::Config::Control id, int key, const QString& text);
-
-private slots:
- void OnActivePortChanged(const common::Config::ControllerPort& config);
-
- void OnClicked();
-
- void keyPressEvent(QKeyEvent* event); // TODO: bGrabbed?
- void mousePressEvent(QMouseEvent* event);
-
-private:
- common::Config::Control id;
- bool inputGrabbed;
-
- QString old_text;
-};
-
-class GButtonConfigGroup : public QWidget
-{
- Q_OBJECT
-
-public:
- // change_receiver needs to have a OnKeyConfigChanged(common::Config::Control, int, const QString&) slot!
- GButtonConfigGroup(const QString& name, common::Config::Control id, QObject* change_receiver, QWidget* parent = NULL);
-
-private:
- GKeyConfigButton* config_button;
-
- common::Config::Control id;
-};
-
-*/
-
-#endif // _CONTROLLER_CONFIG_HXX_
diff --git a/src/citra_qt/configure.ui b/src/citra_qt/configure.ui
index 6ae056ff9..e1624bbef 100644
--- a/src/citra_qt/configure.ui
+++ b/src/citra_qt/configure.ui
@@ -29,6 +29,11 @@
<string>Input</string>
</attribute>
</widget>
+ <widget class="ConfigureAudio" name="audioTab">
+ <attribute name="title">
+ <string>Audio</string>
+ </attribute>
+ </widget>
<widget class="ConfigureDebug" name="debugTab">
<attribute name="title">
<string>Debug</string>
@@ -53,6 +58,12 @@
<container>1</container>
</customwidget>
<customwidget>
+ <class>ConfigureAudio</class>
+ <extends>QWidget</extends>
+ <header>configure_audio.h</header>
+ <container>1</container>
+ </customwidget>
+ <customwidget>
<class>ConfigureDebug</class>
<extends>QWidget</extends>
<header>configure_debug.h</header>
diff --git a/src/citra_qt/configure_audio.cpp b/src/citra_qt/configure_audio.cpp
new file mode 100644
index 000000000..cedfa2f2a
--- /dev/null
+++ b/src/citra_qt/configure_audio.cpp
@@ -0,0 +1,44 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "audio_core/sink_details.h"
+
+#include "citra_qt/configure_audio.h"
+#include "ui_configure_audio.h"
+
+#include "core/settings.h"
+
+ConfigureAudio::ConfigureAudio(QWidget* parent) :
+ QWidget(parent),
+ ui(std::make_unique<Ui::ConfigureAudio>())
+{
+ ui->setupUi(this);
+
+ ui->output_sink_combo_box->clear();
+ ui->output_sink_combo_box->addItem("auto");
+ for (const auto& sink_detail : AudioCore::g_sink_details) {
+ ui->output_sink_combo_box->addItem(sink_detail.id);
+ }
+
+ this->setConfiguration();
+}
+
+ConfigureAudio::~ConfigureAudio() {
+}
+
+void ConfigureAudio::setConfiguration() {
+ int new_sink_index = 0;
+ for (int index = 0; index < ui->output_sink_combo_box->count(); index++) {
+ if (ui->output_sink_combo_box->itemText(index).toStdString() == Settings::values.sink_id) {
+ new_sink_index = index;
+ break;
+ }
+ }
+ ui->output_sink_combo_box->setCurrentIndex(new_sink_index);
+}
+
+void ConfigureAudio::applyConfiguration() {
+ Settings::values.sink_id = ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex()).toStdString();
+ Settings::Apply();
+}
diff --git a/src/citra_qt/configure_audio.h b/src/citra_qt/configure_audio.h
new file mode 100644
index 000000000..51df2e27b
--- /dev/null
+++ b/src/citra_qt/configure_audio.h
@@ -0,0 +1,27 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <QWidget>
+
+namespace Ui {
+class ConfigureAudio;
+}
+
+class ConfigureAudio : public QWidget {
+ Q_OBJECT
+
+public:
+ explicit ConfigureAudio(QWidget* parent = nullptr);
+ ~ConfigureAudio();
+
+ void applyConfiguration();
+
+private:
+ void setConfiguration();
+
+ std::unique_ptr<Ui::ConfigureAudio> ui;
+};
diff --git a/src/citra_qt/configure_audio.ui b/src/citra_qt/configure_audio.ui
new file mode 100644
index 000000000..d7f6946ca
--- /dev/null
+++ b/src/citra_qt/configure_audio.ui
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<ui version="4.0">
+ <class>ConfigureAudio</class>
+ <widget class="QWidget" name="ConfigureAudio">
+ <layout class="QVBoxLayout">
+ <item>
+ <widget class="QGroupBox">
+ <property name="title">
+ <string>Audio</string>
+ </property>
+ <layout class="QVBoxLayout">
+ <item>
+ <layout class="QHBoxLayout">
+ <item>
+ <widget class="QLabel">
+ <property name="text">
+ <string>Output Engine:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="output_sink_combo_box">
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <resources />
+ <connections />
+</ui>
diff --git a/src/citra_qt/configure_dialog.cpp b/src/citra_qt/configure_dialog.cpp
index 87c26c715..2f0317fe0 100644
--- a/src/citra_qt/configure_dialog.cpp
+++ b/src/citra_qt/configure_dialog.cpp
@@ -25,5 +25,6 @@ void ConfigureDialog::setConfiguration() {
void ConfigureDialog::applyConfiguration() {
ui->generalTab->applyConfiguration();
+ ui->audioTab->applyConfiguration();
ui->debugTab->applyConfiguration();
}
diff --git a/src/citra_qt/debugger/callstack.cpp b/src/citra_qt/debugger/callstack.cpp
index 793944639..1a3077495 100644
--- a/src/citra_qt/debugger/callstack.cpp
+++ b/src/citra_qt/debugger/callstack.cpp
@@ -37,10 +37,13 @@ void CallstackWidget::OnDebugModeEntered()
int counter = 0;
for (u32 addr = 0x10000000; addr >= sp; addr -= 4)
{
+ if (!Memory::IsValidVirtualAddress(addr))
+ break;
+
const u32 ret_addr = Memory::Read32(addr);
const u32 call_addr = ret_addr - 4; //get call address???
- if (Memory::GetPointer(call_addr) == nullptr)
+ if (!Memory::IsValidVirtualAddress(call_addr))
break;
/* TODO (mattvail) clean me, move to debugger interface */
diff --git a/src/citra_qt/debugger/graphics_cmdlists.cpp b/src/citra_qt/debugger/graphics_cmdlists.cpp
index 5186d2b44..3e0a0a145 100644
--- a/src/citra_qt/debugger/graphics_cmdlists.cpp
+++ b/src/citra_qt/debugger/graphics_cmdlists.cpp
@@ -50,123 +50,6 @@ public:
}
};
-TextureInfoDockWidget::TextureInfoDockWidget(const Pica::DebugUtils::TextureInfo& info, QWidget* parent)
- : QDockWidget(tr("Texture 0x%1").arg(info.physical_address, 8, 16, QLatin1Char('0'))),
- info(info) {
-
- QWidget* main_widget = new QWidget;
-
- QLabel* image_widget = new QLabel;
-
- connect(this, SIGNAL(UpdatePixmap(const QPixmap&)), image_widget, SLOT(setPixmap(const QPixmap&)));
-
- CSpinBox* phys_address_spinbox = new CSpinBox;
- phys_address_spinbox->SetBase(16);
- phys_address_spinbox->SetRange(0, 0xFFFFFFFF);
- phys_address_spinbox->SetPrefix("0x");
- phys_address_spinbox->SetValue(info.physical_address);
- connect(phys_address_spinbox, SIGNAL(ValueChanged(qint64)), this, SLOT(OnAddressChanged(qint64)));
-
- QComboBox* format_choice = new QComboBox;
- format_choice->addItem(tr("RGBA8"));
- format_choice->addItem(tr("RGB8"));
- format_choice->addItem(tr("RGB5A1"));
- format_choice->addItem(tr("RGB565"));
- format_choice->addItem(tr("RGBA4"));
- format_choice->addItem(tr("IA8"));
- format_choice->addItem(tr("RG8"));
- format_choice->addItem(tr("I8"));
- format_choice->addItem(tr("A8"));
- format_choice->addItem(tr("IA4"));
- format_choice->addItem(tr("I4"));
- format_choice->addItem(tr("A4"));
- format_choice->addItem(tr("ETC1"));
- format_choice->addItem(tr("ETC1A4"));
- format_choice->setCurrentIndex(static_cast<int>(info.format));
- connect(format_choice, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFormatChanged(int)));
-
- QSpinBox* width_spinbox = new QSpinBox;
- width_spinbox->setMaximum(65535);
- width_spinbox->setValue(info.width);
- connect(width_spinbox, SIGNAL(valueChanged(int)), this, SLOT(OnWidthChanged(int)));
-
- QSpinBox* height_spinbox = new QSpinBox;
- height_spinbox->setMaximum(65535);
- height_spinbox->setValue(info.height);
- connect(height_spinbox, SIGNAL(valueChanged(int)), this, SLOT(OnHeightChanged(int)));
-
- QSpinBox* stride_spinbox = new QSpinBox;
- stride_spinbox->setMaximum(65535 * 4);
- stride_spinbox->setValue(info.stride);
- connect(stride_spinbox, SIGNAL(valueChanged(int)), this, SLOT(OnStrideChanged(int)));
-
- QVBoxLayout* main_layout = new QVBoxLayout;
- main_layout->addWidget(image_widget);
-
- {
- QHBoxLayout* sub_layout = new QHBoxLayout;
- sub_layout->addWidget(new QLabel(tr("Source Address:")));
- sub_layout->addWidget(phys_address_spinbox);
- main_layout->addLayout(sub_layout);
- }
-
- {
- QHBoxLayout* sub_layout = new QHBoxLayout;
- sub_layout->addWidget(new QLabel(tr("Format")));
- sub_layout->addWidget(format_choice);
- main_layout->addLayout(sub_layout);
- }
-
- {
- QHBoxLayout* sub_layout = new QHBoxLayout;
- sub_layout->addWidget(new QLabel(tr("Width:")));
- sub_layout->addWidget(width_spinbox);
- sub_layout->addStretch();
- sub_layout->addWidget(new QLabel(tr("Height:")));
- sub_layout->addWidget(height_spinbox);
- sub_layout->addStretch();
- sub_layout->addWidget(new QLabel(tr("Stride:")));
- sub_layout->addWidget(stride_spinbox);
- main_layout->addLayout(sub_layout);
- }
-
- main_widget->setLayout(main_layout);
-
- emit UpdatePixmap(ReloadPixmap());
-
- setWidget(main_widget);
-}
-
-void TextureInfoDockWidget::OnAddressChanged(qint64 value) {
- info.physical_address = value;
- emit UpdatePixmap(ReloadPixmap());
-}
-
-void TextureInfoDockWidget::OnFormatChanged(int value) {
- info.format = static_cast<Pica::Regs::TextureFormat>(value);
- emit UpdatePixmap(ReloadPixmap());
-}
-
-void TextureInfoDockWidget::OnWidthChanged(int value) {
- info.width = value;
- emit UpdatePixmap(ReloadPixmap());
-}
-
-void TextureInfoDockWidget::OnHeightChanged(int value) {
- info.height = value;
- emit UpdatePixmap(ReloadPixmap());
-}
-
-void TextureInfoDockWidget::OnStrideChanged(int value) {
- info.stride = value;
- emit UpdatePixmap(ReloadPixmap());
-}
-
-QPixmap TextureInfoDockWidget::ReloadPixmap() const {
- u8* src = Memory::GetPhysicalPointer(info.physical_address);
- return QPixmap::fromImage(LoadTexture(src, info));
-}
-
GPUCommandListModel::GPUCommandListModel(QObject* parent) : QAbstractListModel(parent) {
}
@@ -249,16 +132,16 @@ void GPUCommandListWidget::OnCommandDoubleClicked(const QModelIndex& index) {
index = 0;
} else if (COMMAND_IN_RANGE(command_id, texture1)) {
index = 1;
- } else {
+ } else if (COMMAND_IN_RANGE(command_id, texture2)) {
index = 2;
+ } else {
+ UNREACHABLE_MSG("Unknown texture command");
}
auto config = Pica::g_state.regs.GetTextures()[index].config;
auto format = Pica::g_state.regs.GetTextures()[index].format;
auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(config, format);
- // TODO: Instead, emit a signal here to be caught by the main window widget.
- auto main_window = static_cast<QMainWindow*>(parent());
- main_window->tabifyDockWidget(this, new TextureInfoDockWidget(info, main_window));
+ // TODO: Open a surface debugger
}
}
diff --git a/src/citra_qt/debugger/graphics_cmdlists.h b/src/citra_qt/debugger/graphics_cmdlists.h
index 586cc7239..8a2a294b9 100644
--- a/src/citra_qt/debugger/graphics_cmdlists.h
+++ b/src/citra_qt/debugger/graphics_cmdlists.h
@@ -61,25 +61,3 @@ private:
QWidget* command_info_widget;
QPushButton* toggle_tracing;
};
-
-class TextureInfoDockWidget : public QDockWidget {
- Q_OBJECT
-
-public:
- TextureInfoDockWidget(const Pica::DebugUtils::TextureInfo& info, QWidget* parent = nullptr);
-
-signals:
- void UpdatePixmap(const QPixmap& pixmap);
-
-private slots:
- void OnAddressChanged(qint64 value);
- void OnFormatChanged(int value);
- void OnWidthChanged(int value);
- void OnHeightChanged(int value);
- void OnStrideChanged(int value);
-
-private:
- QPixmap ReloadPixmap() const;
-
- Pica::DebugUtils::TextureInfo info;
-};
diff --git a/src/citra_qt/debugger/graphics_framebuffer.cpp b/src/citra_qt/debugger/graphics_framebuffer.cpp
deleted file mode 100644
index 68cff78b2..000000000
--- a/src/citra_qt/debugger/graphics_framebuffer.cpp
+++ /dev/null
@@ -1,356 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <QBoxLayout>
-#include <QComboBox>
-#include <QDebug>
-#include <QLabel>
-#include <QPushButton>
-#include <QSpinBox>
-
-#include "citra_qt/debugger/graphics_framebuffer.h"
-#include "citra_qt/util/spinbox.h"
-
-#include "common/color.h"
-
-#include "core/memory.h"
-#include "core/hw/gpu.h"
-
-#include "video_core/pica.h"
-#include "video_core/pica_state.h"
-#include "video_core/utils.h"
-
-GraphicsFramebufferWidget::GraphicsFramebufferWidget(std::shared_ptr<Pica::DebugContext> debug_context,
- QWidget* parent)
- : BreakPointObserverDock(debug_context, tr("Pica Framebuffer"), parent),
- framebuffer_source(Source::PicaTarget)
-{
- setObjectName("PicaFramebuffer");
-
- framebuffer_source_list = new QComboBox;
- framebuffer_source_list->addItem(tr("Active Render Target"));
- framebuffer_source_list->addItem(tr("Active Depth Buffer"));
- framebuffer_source_list->addItem(tr("Custom"));
- framebuffer_source_list->setCurrentIndex(static_cast<int>(framebuffer_source));
-
- framebuffer_address_control = new CSpinBox;
- framebuffer_address_control->SetBase(16);
- framebuffer_address_control->SetRange(0, 0xFFFFFFFF);
- framebuffer_address_control->SetPrefix("0x");
-
- framebuffer_width_control = new QSpinBox;
- framebuffer_width_control->setMinimum(1);
- framebuffer_width_control->setMaximum(std::numeric_limits<int>::max()); // TODO: Find actual maximum
-
- framebuffer_height_control = new QSpinBox;
- framebuffer_height_control->setMinimum(1);
- framebuffer_height_control->setMaximum(std::numeric_limits<int>::max()); // TODO: Find actual maximum
-
- framebuffer_format_control = new QComboBox;
- framebuffer_format_control->addItem(tr("RGBA8"));
- framebuffer_format_control->addItem(tr("RGB8"));
- framebuffer_format_control->addItem(tr("RGB5A1"));
- framebuffer_format_control->addItem(tr("RGB565"));
- framebuffer_format_control->addItem(tr("RGBA4"));
- framebuffer_format_control->addItem(tr("D16"));
- framebuffer_format_control->addItem(tr("D24"));
- framebuffer_format_control->addItem(tr("D24X8"));
- framebuffer_format_control->addItem(tr("X24S8"));
- framebuffer_format_control->addItem(tr("(unknown)"));
-
- // TODO: This QLabel should shrink the image to the available space rather than just expanding...
- framebuffer_picture_label = new QLabel;
-
- auto enlarge_button = new QPushButton(tr("Enlarge"));
-
- // Connections
- connect(this, SIGNAL(Update()), this, SLOT(OnUpdate()));
- connect(framebuffer_source_list, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFramebufferSourceChanged(int)));
- connect(framebuffer_address_control, SIGNAL(ValueChanged(qint64)), this, SLOT(OnFramebufferAddressChanged(qint64)));
- connect(framebuffer_width_control, SIGNAL(valueChanged(int)), this, SLOT(OnFramebufferWidthChanged(int)));
- connect(framebuffer_height_control, SIGNAL(valueChanged(int)), this, SLOT(OnFramebufferHeightChanged(int)));
- connect(framebuffer_format_control, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFramebufferFormatChanged(int)));
-
- auto main_widget = new QWidget;
- auto main_layout = new QVBoxLayout;
- {
- auto sub_layout = new QHBoxLayout;
- sub_layout->addWidget(new QLabel(tr("Source:")));
- sub_layout->addWidget(framebuffer_source_list);
- main_layout->addLayout(sub_layout);
- }
- {
- auto sub_layout = new QHBoxLayout;
- sub_layout->addWidget(new QLabel(tr("Virtual Address:")));
- sub_layout->addWidget(framebuffer_address_control);
- main_layout->addLayout(sub_layout);
- }
- {
- auto sub_layout = new QHBoxLayout;
- sub_layout->addWidget(new QLabel(tr("Width:")));
- sub_layout->addWidget(framebuffer_width_control);
- main_layout->addLayout(sub_layout);
- }
- {
- auto sub_layout = new QHBoxLayout;
- sub_layout->addWidget(new QLabel(tr("Height:")));
- sub_layout->addWidget(framebuffer_height_control);
- main_layout->addLayout(sub_layout);
- }
- {
- auto sub_layout = new QHBoxLayout;
- sub_layout->addWidget(new QLabel(tr("Format:")));
- sub_layout->addWidget(framebuffer_format_control);
- main_layout->addLayout(sub_layout);
- }
- main_layout->addWidget(framebuffer_picture_label);
- main_layout->addWidget(enlarge_button);
- main_widget->setLayout(main_layout);
- setWidget(main_widget);
-
- // Load current data - TODO: Make sure this works when emulation is not running
- if (debug_context && debug_context->at_breakpoint)
- emit Update();
- widget()->setEnabled(false); // TODO: Only enable if currently at breakpoint
-}
-
-void GraphicsFramebufferWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data)
-{
- emit Update();
- widget()->setEnabled(true);
-}
-
-void GraphicsFramebufferWidget::OnResumed()
-{
- widget()->setEnabled(false);
-}
-
-void GraphicsFramebufferWidget::OnFramebufferSourceChanged(int new_value)
-{
- framebuffer_source = static_cast<Source>(new_value);
- emit Update();
-}
-
-void GraphicsFramebufferWidget::OnFramebufferAddressChanged(qint64 new_value)
-{
- if (framebuffer_address != new_value) {
- framebuffer_address = static_cast<unsigned>(new_value);
-
- framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
- emit Update();
- }
-}
-
-void GraphicsFramebufferWidget::OnFramebufferWidthChanged(int new_value)
-{
- if (framebuffer_width != static_cast<unsigned>(new_value)) {
- framebuffer_width = static_cast<unsigned>(new_value);
-
- framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
- emit Update();
- }
-}
-
-void GraphicsFramebufferWidget::OnFramebufferHeightChanged(int new_value)
-{
- if (framebuffer_height != static_cast<unsigned>(new_value)) {
- framebuffer_height = static_cast<unsigned>(new_value);
-
- framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
- emit Update();
- }
-}
-
-void GraphicsFramebufferWidget::OnFramebufferFormatChanged(int new_value)
-{
- if (framebuffer_format != static_cast<Format>(new_value)) {
- framebuffer_format = static_cast<Format>(new_value);
-
- framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
- emit Update();
- }
-}
-
-void GraphicsFramebufferWidget::OnUpdate()
-{
- QPixmap pixmap;
-
- switch (framebuffer_source) {
- case Source::PicaTarget:
- {
- // TODO: Store a reference to the registers in the debug context instead of accessing them directly...
-
- const auto& framebuffer = Pica::g_state.regs.framebuffer;
-
- framebuffer_address = framebuffer.GetColorBufferPhysicalAddress();
- framebuffer_width = framebuffer.GetWidth();
- framebuffer_height = framebuffer.GetHeight();
-
- switch (framebuffer.color_format) {
- case Pica::Regs::ColorFormat::RGBA8:
- framebuffer_format = Format::RGBA8;
- break;
-
- case Pica::Regs::ColorFormat::RGB8:
- framebuffer_format = Format::RGB8;
- break;
-
- case Pica::Regs::ColorFormat::RGB5A1:
- framebuffer_format = Format::RGB5A1;
- break;
-
- case Pica::Regs::ColorFormat::RGB565:
- framebuffer_format = Format::RGB565;
- break;
-
- case Pica::Regs::ColorFormat::RGBA4:
- framebuffer_format = Format::RGBA4;
- break;
-
- default:
- framebuffer_format = Format::Unknown;
- break;
- }
-
- break;
- }
-
- case Source::DepthBuffer:
- {
- const auto& framebuffer = Pica::g_state.regs.framebuffer;
-
- framebuffer_address = framebuffer.GetDepthBufferPhysicalAddress();
- framebuffer_width = framebuffer.GetWidth();
- framebuffer_height = framebuffer.GetHeight();
-
- switch (framebuffer.depth_format) {
- case Pica::Regs::DepthFormat::D16:
- framebuffer_format = Format::D16;
- break;
-
- case Pica::Regs::DepthFormat::D24:
- framebuffer_format = Format::D24;
- break;
-
- case Pica::Regs::DepthFormat::D24S8:
- framebuffer_format = Format::D24X8;
- break;
-
- default:
- framebuffer_format = Format::Unknown;
- break;
- }
-
- break;
- }
-
- case Source::Custom:
- {
- // Keep user-specified values
- break;
- }
-
- default:
- qDebug() << "Unknown framebuffer source " << static_cast<int>(framebuffer_source);
- break;
- }
-
- // TODO: Implement a good way to visualize alpha components!
- // TODO: Unify this decoding code with the texture decoder
- u32 bytes_per_pixel = GraphicsFramebufferWidget::BytesPerPixel(framebuffer_format);
-
- QImage decoded_image(framebuffer_width, framebuffer_height, QImage::Format_ARGB32);
- u8* buffer = Memory::GetPhysicalPointer(framebuffer_address);
-
- for (unsigned int y = 0; y < framebuffer_height; ++y) {
- for (unsigned int x = 0; x < framebuffer_width; ++x) {
- const u32 coarse_y = y & ~7;
- u32 offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * framebuffer_width * bytes_per_pixel;
- const u8* pixel = buffer + offset;
- Math::Vec4<u8> color = { 0, 0, 0, 0 };
-
- switch (framebuffer_format) {
- case Format::RGBA8:
- color = Color::DecodeRGBA8(pixel);
- break;
- case Format::RGB8:
- color = Color::DecodeRGB8(pixel);
- break;
- case Format::RGB5A1:
- color = Color::DecodeRGB5A1(pixel);
- break;
- case Format::RGB565:
- color = Color::DecodeRGB565(pixel);
- break;
- case Format::RGBA4:
- color = Color::DecodeRGBA4(pixel);
- break;
- case Format::D16:
- {
- u32 data = Color::DecodeD16(pixel);
- color.r() = data & 0xFF;
- color.g() = (data >> 8) & 0xFF;
- break;
- }
- case Format::D24:
- {
- u32 data = Color::DecodeD24(pixel);
- color.r() = data & 0xFF;
- color.g() = (data >> 8) & 0xFF;
- color.b() = (data >> 16) & 0xFF;
- break;
- }
- case Format::D24X8:
- {
- Math::Vec2<u32> data = Color::DecodeD24S8(pixel);
- color.r() = data.x & 0xFF;
- color.g() = (data.x >> 8) & 0xFF;
- color.b() = (data.x >> 16) & 0xFF;
- break;
- }
- case Format::X24S8:
- {
- Math::Vec2<u32> data = Color::DecodeD24S8(pixel);
- color.r() = color.g() = color.b() = data.y;
- break;
- }
- default:
- qDebug() << "Unknown fb color format " << static_cast<int>(framebuffer_format);
- break;
- }
-
- decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), 255));
- }
- }
- pixmap = QPixmap::fromImage(decoded_image);
-
- framebuffer_address_control->SetValue(framebuffer_address);
- framebuffer_width_control->setValue(framebuffer_width);
- framebuffer_height_control->setValue(framebuffer_height);
- framebuffer_format_control->setCurrentIndex(static_cast<int>(framebuffer_format));
- framebuffer_picture_label->setPixmap(pixmap);
-}
-
-u32 GraphicsFramebufferWidget::BytesPerPixel(GraphicsFramebufferWidget::Format format) {
- switch (format) {
- case Format::RGBA8:
- case Format::D24X8:
- case Format::X24S8:
- return 4;
- case Format::RGB8:
- case Format::D24:
- return 3;
- case Format::RGB5A1:
- case Format::RGB565:
- case Format::RGBA4:
- case Format::D16:
- return 2;
- default:
- UNREACHABLE_MSG("GraphicsFramebufferWidget::BytesPerPixel: this "
- "should not be reached as this function should "
- "be given a format which is in "
- "GraphicsFramebufferWidget::Format. Instead got %i",
- static_cast<int>(format));
- }
-}
diff --git a/src/citra_qt/debugger/graphics_framebuffer.h b/src/citra_qt/debugger/graphics_framebuffer.h
deleted file mode 100644
index 5cd96f2e9..000000000
--- a/src/citra_qt/debugger/graphics_framebuffer.h
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include "citra_qt/debugger/graphics_breakpoint_observer.h"
-
-class QComboBox;
-class QLabel;
-class QSpinBox;
-
-class CSpinBox;
-
-class GraphicsFramebufferWidget : public BreakPointObserverDock {
- Q_OBJECT
-
- using Event = Pica::DebugContext::Event;
-
- enum class Source {
- PicaTarget = 0,
- DepthBuffer = 1,
- Custom = 2,
-
- // TODO: Add GPU framebuffer sources!
- };
-
- enum class Format {
- RGBA8 = 0,
- RGB8 = 1,
- RGB5A1 = 2,
- RGB565 = 3,
- RGBA4 = 4,
- D16 = 5,
- D24 = 6,
- D24X8 = 7,
- X24S8 = 8,
- Unknown = 9
- };
-
- static u32 BytesPerPixel(Format format);
-
-public:
- GraphicsFramebufferWidget(std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent = nullptr);
-
-public slots:
- void OnFramebufferSourceChanged(int new_value);
- void OnFramebufferAddressChanged(qint64 new_value);
- void OnFramebufferWidthChanged(int new_value);
- void OnFramebufferHeightChanged(int new_value);
- void OnFramebufferFormatChanged(int new_value);
- void OnUpdate();
-
-private slots:
- void OnBreakPointHit(Pica::DebugContext::Event event, void* data) override;
- void OnResumed() override;
-
-signals:
- void Update();
-
-private:
-
- QComboBox* framebuffer_source_list;
- CSpinBox* framebuffer_address_control;
- QSpinBox* framebuffer_width_control;
- QSpinBox* framebuffer_height_control;
- QComboBox* framebuffer_format_control;
-
- QLabel* framebuffer_picture_label;
-
- Source framebuffer_source;
- unsigned framebuffer_address;
- unsigned framebuffer_width;
- unsigned framebuffer_height;
- Format framebuffer_format;
-};
diff --git a/src/citra_qt/debugger/graphics_surface.cpp b/src/citra_qt/debugger/graphics_surface.cpp
new file mode 100644
index 000000000..ac2d6f89b
--- /dev/null
+++ b/src/citra_qt/debugger/graphics_surface.cpp
@@ -0,0 +1,736 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <QBoxLayout>
+#include <QComboBox>
+#include <QDebug>
+#include <QFileDialog>
+#include <QLabel>
+#include <QMouseEvent>
+#include <QPushButton>
+#include <QScrollArea>
+#include <QSpinBox>
+
+#include "citra_qt/debugger/graphics_surface.h"
+#include "citra_qt/util/spinbox.h"
+
+#include "common/color.h"
+
+#include "core/memory.h"
+#include "core/hw/gpu.h"
+
+#include "video_core/pica.h"
+#include "video_core/pica_state.h"
+#include "video_core/utils.h"
+
+SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_) : QLabel(parent), surface_widget(surface_widget_) {}
+SurfacePicture::~SurfacePicture() {}
+
+void SurfacePicture::mousePressEvent(QMouseEvent* event)
+{
+ // Only do something while the left mouse button is held down
+ if (!(event->buttons() & Qt::LeftButton))
+ return;
+
+ if (pixmap() == nullptr)
+ return;
+
+ if (surface_widget)
+ surface_widget->Pick(event->x() * pixmap()->width() / width(),
+ event->y() * pixmap()->height() / height());
+}
+
+void SurfacePicture::mouseMoveEvent(QMouseEvent* event)
+{
+ // We also want to handle the event if the user moves the mouse while holding down the LMB
+ mousePressEvent(event);
+}
+
+
+GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext> debug_context,
+ QWidget* parent)
+ : BreakPointObserverDock(debug_context, tr("Pica Surface Viewer"), parent),
+ surface_source(Source::ColorBuffer)
+{
+ setObjectName("PicaSurface");
+
+ surface_source_list = new QComboBox;
+ surface_source_list->addItem(tr("Color Buffer"));
+ surface_source_list->addItem(tr("Depth Buffer"));
+ surface_source_list->addItem(tr("Stencil Buffer"));
+ surface_source_list->addItem(tr("Texture 0"));
+ surface_source_list->addItem(tr("Texture 1"));
+ surface_source_list->addItem(tr("Texture 2"));
+ surface_source_list->addItem(tr("Custom"));
+ surface_source_list->setCurrentIndex(static_cast<int>(surface_source));
+
+ surface_address_control = new CSpinBox;
+ surface_address_control->SetBase(16);
+ surface_address_control->SetRange(0, 0xFFFFFFFF);
+ surface_address_control->SetPrefix("0x");
+
+ unsigned max_dimension = 16384; // TODO: Find actual maximum
+
+ surface_width_control = new QSpinBox;
+ surface_width_control->setRange(0, max_dimension);
+
+ surface_height_control = new QSpinBox;
+ surface_height_control->setRange(0, max_dimension);
+
+ surface_picker_x_control = new QSpinBox;
+ surface_picker_x_control->setRange(0, max_dimension - 1);
+
+ surface_picker_y_control = new QSpinBox;
+ surface_picker_y_control->setRange(0, max_dimension - 1);
+
+ surface_format_control = new QComboBox;
+
+ // Color formats sorted by Pica texture format index
+ surface_format_control->addItem(tr("RGBA8"));
+ surface_format_control->addItem(tr("RGB8"));
+ surface_format_control->addItem(tr("RGB5A1"));
+ surface_format_control->addItem(tr("RGB565"));
+ surface_format_control->addItem(tr("RGBA4"));
+ surface_format_control->addItem(tr("IA8"));
+ surface_format_control->addItem(tr("RG8"));
+ surface_format_control->addItem(tr("I8"));
+ surface_format_control->addItem(tr("A8"));
+ surface_format_control->addItem(tr("IA4"));
+ surface_format_control->addItem(tr("I4"));
+ surface_format_control->addItem(tr("A4"));
+ surface_format_control->addItem(tr("ETC1"));
+ surface_format_control->addItem(tr("ETC1A4"));
+ surface_format_control->addItem(tr("D16"));
+ surface_format_control->addItem(tr("D24"));
+ surface_format_control->addItem(tr("D24X8"));
+ surface_format_control->addItem(tr("X24S8"));
+ surface_format_control->addItem(tr("Unknown"));
+
+ surface_info_label = new QLabel();
+ surface_info_label->setWordWrap(true);
+
+ surface_picture_label = new SurfacePicture(0, this);
+ surface_picture_label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ surface_picture_label->setAlignment(Qt::AlignLeft | Qt::AlignTop);
+ surface_picture_label->setScaledContents(false);
+
+ auto scroll_area = new QScrollArea();
+ scroll_area->setBackgroundRole(QPalette::Dark);
+ scroll_area->setWidgetResizable(false);
+ scroll_area->setWidget(surface_picture_label);
+
+ save_surface = new QPushButton(QIcon::fromTheme("document-save"), tr("Save"));
+
+ // Connections
+ connect(this, SIGNAL(Update()), this, SLOT(OnUpdate()));
+ connect(surface_source_list, SIGNAL(currentIndexChanged(int)), this, SLOT(OnSurfaceSourceChanged(int)));
+ connect(surface_address_control, SIGNAL(ValueChanged(qint64)), this, SLOT(OnSurfaceAddressChanged(qint64)));
+ connect(surface_width_control, SIGNAL(valueChanged(int)), this, SLOT(OnSurfaceWidthChanged(int)));
+ connect(surface_height_control, SIGNAL(valueChanged(int)), this, SLOT(OnSurfaceHeightChanged(int)));
+ connect(surface_format_control, SIGNAL(currentIndexChanged(int)), this, SLOT(OnSurfaceFormatChanged(int)));
+ connect(surface_picker_x_control, SIGNAL(valueChanged(int)), this, SLOT(OnSurfacePickerXChanged(int)));
+ connect(surface_picker_y_control, SIGNAL(valueChanged(int)), this, SLOT(OnSurfacePickerYChanged(int)));
+ connect(save_surface, SIGNAL(clicked()), this, SLOT(SaveSurface()));
+
+ auto main_widget = new QWidget;
+ auto main_layout = new QVBoxLayout;
+ {
+ auto sub_layout = new QHBoxLayout;
+ sub_layout->addWidget(new QLabel(tr("Source:")));
+ sub_layout->addWidget(surface_source_list);
+ main_layout->addLayout(sub_layout);
+ }
+ {
+ auto sub_layout = new QHBoxLayout;
+ sub_layout->addWidget(new QLabel(tr("Physical Address:")));
+ sub_layout->addWidget(surface_address_control);
+ main_layout->addLayout(sub_layout);
+ }
+ {
+ auto sub_layout = new QHBoxLayout;
+ sub_layout->addWidget(new QLabel(tr("Width:")));
+ sub_layout->addWidget(surface_width_control);
+ main_layout->addLayout(sub_layout);
+ }
+ {
+ auto sub_layout = new QHBoxLayout;
+ sub_layout->addWidget(new QLabel(tr("Height:")));
+ sub_layout->addWidget(surface_height_control);
+ main_layout->addLayout(sub_layout);
+ }
+ {
+ auto sub_layout = new QHBoxLayout;
+ sub_layout->addWidget(new QLabel(tr("Format:")));
+ sub_layout->addWidget(surface_format_control);
+ main_layout->addLayout(sub_layout);
+ }
+ main_layout->addWidget(scroll_area);
+
+ auto info_layout = new QHBoxLayout;
+ {
+ auto xy_layout = new QVBoxLayout;
+ {
+ {
+ auto sub_layout = new QHBoxLayout;
+ sub_layout->addWidget(new QLabel(tr("X:")));
+ sub_layout->addWidget(surface_picker_x_control);
+ xy_layout->addLayout(sub_layout);
+ }
+ {
+ auto sub_layout = new QHBoxLayout;
+ sub_layout->addWidget(new QLabel(tr("Y:")));
+ sub_layout->addWidget(surface_picker_y_control);
+ xy_layout->addLayout(sub_layout);
+ }
+ }
+ info_layout->addLayout(xy_layout);
+ surface_info_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
+ info_layout->addWidget(surface_info_label);
+ }
+ main_layout->addLayout(info_layout);
+
+ main_layout->addWidget(save_surface);
+ main_widget->setLayout(main_layout);
+ setWidget(main_widget);
+
+ // Load current data - TODO: Make sure this works when emulation is not running
+ if (debug_context && debug_context->at_breakpoint) {
+ emit Update();
+ widget()->setEnabled(debug_context->at_breakpoint);
+ } else {
+ widget()->setEnabled(false);
+ }
+}
+
+void GraphicsSurfaceWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data)
+{
+ emit Update();
+ widget()->setEnabled(true);
+}
+
+void GraphicsSurfaceWidget::OnResumed()
+{
+ widget()->setEnabled(false);
+}
+
+void GraphicsSurfaceWidget::OnSurfaceSourceChanged(int new_value)
+{
+ surface_source = static_cast<Source>(new_value);
+ emit Update();
+}
+
+void GraphicsSurfaceWidget::OnSurfaceAddressChanged(qint64 new_value)
+{
+ if (surface_address != new_value) {
+ surface_address = static_cast<unsigned>(new_value);
+
+ surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
+ emit Update();
+ }
+}
+
+void GraphicsSurfaceWidget::OnSurfaceWidthChanged(int new_value)
+{
+ if (surface_width != static_cast<unsigned>(new_value)) {
+ surface_width = static_cast<unsigned>(new_value);
+
+ surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
+ emit Update();
+ }
+}
+
+void GraphicsSurfaceWidget::OnSurfaceHeightChanged(int new_value)
+{
+ if (surface_height != static_cast<unsigned>(new_value)) {
+ surface_height = static_cast<unsigned>(new_value);
+
+ surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
+ emit Update();
+ }
+}
+
+void GraphicsSurfaceWidget::OnSurfaceFormatChanged(int new_value)
+{
+ if (surface_format != static_cast<Format>(new_value)) {
+ surface_format = static_cast<Format>(new_value);
+
+ surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
+ emit Update();
+ }
+}
+
+void GraphicsSurfaceWidget::OnSurfacePickerXChanged(int new_value)
+{
+ if (surface_picker_x != new_value) {
+ surface_picker_x = new_value;
+ Pick(surface_picker_x, surface_picker_y);
+ }
+}
+
+void GraphicsSurfaceWidget::OnSurfacePickerYChanged(int new_value)
+{
+ if (surface_picker_y != new_value) {
+ surface_picker_y = new_value;
+ Pick(surface_picker_x, surface_picker_y);
+ }
+}
+
+void GraphicsSurfaceWidget::Pick(int x, int y)
+{
+ surface_picker_x_control->setValue(x);
+ surface_picker_y_control->setValue(y);
+
+ if (x < 0 || x >= surface_width || y < 0 || y >= surface_height) {
+ surface_info_label->setText(tr("Pixel out of bounds"));
+ surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
+ return;
+ }
+
+ u8* buffer = Memory::GetPhysicalPointer(surface_address);
+ if (buffer == nullptr) {
+ surface_info_label->setText(tr("(unable to access pixel data)"));
+ surface_info_label->setAlignment(Qt::AlignCenter);
+ return;
+ }
+
+ unsigned nibbles_per_pixel = GraphicsSurfaceWidget::NibblesPerPixel(surface_format);
+ unsigned stride = nibbles_per_pixel * surface_width / 2;
+
+ unsigned bytes_per_pixel;
+ bool nibble_mode = (nibbles_per_pixel == 1);
+ if (nibble_mode) {
+ // As nibbles are contained in a byte we still need to access one byte per nibble
+ bytes_per_pixel = 1;
+ } else {
+ bytes_per_pixel = nibbles_per_pixel / 2;
+ }
+
+ const u32 coarse_y = y & ~7;
+ u32 offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride;
+ const u8* pixel = buffer + (nibble_mode ? (offset / 2) : offset);
+
+ auto GetText = [offset](Format format, const u8* pixel) {
+ switch (format) {
+ case Format::RGBA8:
+ {
+ auto value = Color::DecodeRGBA8(pixel) / 255.0f;
+ return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4")
+ .arg(QString::number(value.r(), 'f', 2))
+ .arg(QString::number(value.g(), 'f', 2))
+ .arg(QString::number(value.b(), 'f', 2))
+ .arg(QString::number(value.a(), 'f', 2));
+ }
+ case Format::RGB8:
+ {
+ auto value = Color::DecodeRGB8(pixel) / 255.0f;
+ return QString("Red: %1, Green: %2, Blue: %3")
+ .arg(QString::number(value.r(), 'f', 2))
+ .arg(QString::number(value.g(), 'f', 2))
+ .arg(QString::number(value.b(), 'f', 2));
+ }
+ case Format::RGB5A1:
+ {
+ auto value = Color::DecodeRGB5A1(pixel) / 255.0f;
+ return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4")
+ .arg(QString::number(value.r(), 'f', 2))
+ .arg(QString::number(value.g(), 'f', 2))
+ .arg(QString::number(value.b(), 'f', 2))
+ .arg(QString::number(value.a(), 'f', 2));
+ }
+ case Format::RGB565:
+ {
+ auto value = Color::DecodeRGB565(pixel) / 255.0f;
+ return QString("Red: %1, Green: %2, Blue: %3")
+ .arg(QString::number(value.r(), 'f', 2))
+ .arg(QString::number(value.g(), 'f', 2))
+ .arg(QString::number(value.b(), 'f', 2));
+ }
+ case Format::RGBA4:
+ {
+ auto value = Color::DecodeRGBA4(pixel) / 255.0f;
+ return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4")
+ .arg(QString::number(value.r(), 'f', 2))
+ .arg(QString::number(value.g(), 'f', 2))
+ .arg(QString::number(value.b(), 'f', 2))
+ .arg(QString::number(value.a(), 'f', 2));
+ }
+ case Format::IA8:
+ return QString("Index: %1, Alpha: %2")
+ .arg(pixel[0])
+ .arg(pixel[1]);
+ case Format::RG8: {
+ auto value = Color::DecodeRG8(pixel) / 255.0f;
+ return QString("Red: %1, Green: %2")
+ .arg(QString::number(value.r(), 'f', 2))
+ .arg(QString::number(value.g(), 'f', 2));
+ }
+ case Format::I8:
+ return QString("Index: %1").arg(*pixel);
+ case Format::A8:
+ return QString("Alpha: %1").arg(QString::number(*pixel / 255.0f, 'f', 2));
+ case Format::IA4:
+ return QString("Index: %1, Alpha: %2")
+ .arg(*pixel & 0xF)
+ .arg((*pixel & 0xF0) >> 4);
+ case Format::I4:
+ {
+ u8 i = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF;
+ return QString("Index: %1").arg(i);
+ }
+ case Format::A4:
+ {
+ u8 a = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF;
+ return QString("Alpha: %1").arg(QString::number(a / 15.0f, 'f', 2));
+ }
+ case Format::ETC1:
+ case Format::ETC1A4:
+ // TODO: Display block information or channel values?
+ return QString("Compressed data");
+ case Format::D16:
+ {
+ auto value = Color::DecodeD16(pixel);
+ return QString("Depth: %1").arg(QString::number(value / (float)0xFFFF, 'f', 4));
+ }
+ case Format::D24:
+ {
+ auto value = Color::DecodeD24(pixel);
+ return QString("Depth: %1").arg(QString::number(value / (float)0xFFFFFF, 'f', 4));
+ }
+ case Format::D24X8:
+ case Format::X24S8:
+ {
+ auto values = Color::DecodeD24S8(pixel);
+ return QString("Depth: %1, Stencil: %2").arg(QString::number(values[0] / (float)0xFFFFFF, 'f', 4)).arg(values[1]);
+ }
+ case Format::Unknown:
+ return QString("Unknown format");
+ default:
+ return QString("Unhandled format");
+ }
+ return QString("");
+ };
+
+ QString nibbles = "";
+ for (unsigned i = 0; i < nibbles_per_pixel; i++) {
+ unsigned nibble_index = i;
+ if (nibble_mode) {
+ nibble_index += (offset % 2) ? 0 : 1;
+ }
+ u8 byte = pixel[nibble_index / 2];
+ u8 nibble = (byte >> ((nibble_index % 2) ? 0 : 4)) & 0xF;
+ nibbles.append(QString::number(nibble, 16).toUpper());
+ }
+
+ surface_info_label->setText(QString("Raw: 0x%3\n(%4)").arg(nibbles).arg(GetText(surface_format, pixel)));
+ surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
+}
+
+void GraphicsSurfaceWidget::OnUpdate()
+{
+ QPixmap pixmap;
+
+ switch (surface_source) {
+ case Source::ColorBuffer:
+ {
+ // TODO: Store a reference to the registers in the debug context instead of accessing them directly...
+
+ const auto& framebuffer = Pica::g_state.regs.framebuffer;
+
+ surface_address = framebuffer.GetColorBufferPhysicalAddress();
+ surface_width = framebuffer.GetWidth();
+ surface_height = framebuffer.GetHeight();
+
+ switch (framebuffer.color_format) {
+ case Pica::Regs::ColorFormat::RGBA8:
+ surface_format = Format::RGBA8;
+ break;
+
+ case Pica::Regs::ColorFormat::RGB8:
+ surface_format = Format::RGB8;
+ break;
+
+ case Pica::Regs::ColorFormat::RGB5A1:
+ surface_format = Format::RGB5A1;
+ break;
+
+ case Pica::Regs::ColorFormat::RGB565:
+ surface_format = Format::RGB565;
+ break;
+
+ case Pica::Regs::ColorFormat::RGBA4:
+ surface_format = Format::RGBA4;
+ break;
+
+ default:
+ surface_format = Format::Unknown;
+ break;
+ }
+
+ break;
+ }
+
+ case Source::DepthBuffer:
+ {
+ const auto& framebuffer = Pica::g_state.regs.framebuffer;
+
+ surface_address = framebuffer.GetDepthBufferPhysicalAddress();
+ surface_width = framebuffer.GetWidth();
+ surface_height = framebuffer.GetHeight();
+
+ switch (framebuffer.depth_format) {
+ case Pica::Regs::DepthFormat::D16:
+ surface_format = Format::D16;
+ break;
+
+ case Pica::Regs::DepthFormat::D24:
+ surface_format = Format::D24;
+ break;
+
+ case Pica::Regs::DepthFormat::D24S8:
+ surface_format = Format::D24X8;
+ break;
+
+ default:
+ surface_format = Format::Unknown;
+ break;
+ }
+
+ break;
+ }
+
+ case Source::StencilBuffer:
+ {
+ const auto& framebuffer = Pica::g_state.regs.framebuffer;
+
+ surface_address = framebuffer.GetDepthBufferPhysicalAddress();
+ surface_width = framebuffer.GetWidth();
+ surface_height = framebuffer.GetHeight();
+
+ switch (framebuffer.depth_format) {
+ case Pica::Regs::DepthFormat::D24S8:
+ surface_format = Format::X24S8;
+ break;
+
+ default:
+ surface_format = Format::Unknown;
+ break;
+ }
+
+ break;
+ }
+
+ case Source::Texture0:
+ case Source::Texture1:
+ case Source::Texture2:
+ {
+ unsigned texture_index;
+ if (surface_source == Source::Texture0) texture_index = 0;
+ else if (surface_source == Source::Texture1) texture_index = 1;
+ else if (surface_source == Source::Texture2) texture_index = 2;
+ else {
+ qDebug() << "Unknown texture source " << static_cast<int>(surface_source);
+ break;
+ }
+
+ const auto texture = Pica::g_state.regs.GetTextures()[texture_index];
+ auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(texture.config, texture.format);
+
+ surface_address = info.physical_address;
+ surface_width = info.width;
+ surface_height = info.height;
+ surface_format = static_cast<Format>(info.format);
+
+ if (surface_format > Format::MaxTextureFormat) {
+ qDebug() << "Unknown texture format " << static_cast<int>(info.format);
+ }
+ break;
+ }
+
+ case Source::Custom:
+ {
+ // Keep user-specified values
+ break;
+ }
+
+ default:
+ qDebug() << "Unknown surface source " << static_cast<int>(surface_source);
+ break;
+ }
+
+ surface_address_control->SetValue(surface_address);
+ surface_width_control->setValue(surface_width);
+ surface_height_control->setValue(surface_height);
+ surface_format_control->setCurrentIndex(static_cast<int>(surface_format));
+
+ // TODO: Implement a good way to visualize alpha components!
+
+ QImage decoded_image(surface_width, surface_height, QImage::Format_ARGB32);
+ u8* buffer = Memory::GetPhysicalPointer(surface_address);
+
+ if (buffer == nullptr) {
+ surface_picture_label->hide();
+ surface_info_label->setText(tr("(invalid surface address)"));
+ surface_info_label->setAlignment(Qt::AlignCenter);
+ surface_picker_x_control->setEnabled(false);
+ surface_picker_y_control->setEnabled(false);
+ save_surface->setEnabled(false);
+ return;
+ }
+
+ if (surface_format == Format::Unknown) {
+ surface_picture_label->hide();
+ surface_info_label->setText(tr("(unknown surface format)"));
+ surface_info_label->setAlignment(Qt::AlignCenter);
+ surface_picker_x_control->setEnabled(false);
+ surface_picker_y_control->setEnabled(false);
+ save_surface->setEnabled(false);
+ return;
+ }
+
+ surface_picture_label->show();
+
+ unsigned nibbles_per_pixel = GraphicsSurfaceWidget::NibblesPerPixel(surface_format);
+ unsigned stride = nibbles_per_pixel * surface_width / 2;
+
+ // We handle depth formats here because DebugUtils only supports TextureFormats
+ if (surface_format <= Format::MaxTextureFormat) {
+
+ // Generate a virtual texture
+ Pica::DebugUtils::TextureInfo info;
+ info.physical_address = surface_address;
+ info.width = surface_width;
+ info.height = surface_height;
+ info.format = static_cast<Pica::Regs::TextureFormat>(surface_format);
+ info.stride = stride;
+
+ for (unsigned int y = 0; y < surface_height; ++y) {
+ for (unsigned int x = 0; x < surface_width; ++x) {
+ Math::Vec4<u8> color = Pica::DebugUtils::LookupTexture(buffer, x, y, info, true);
+ decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), color.a()));
+ }
+ }
+
+ } else {
+
+ ASSERT_MSG(nibbles_per_pixel >= 2, "Depth decoder only supports formats with at least one byte per pixel");
+ unsigned bytes_per_pixel = nibbles_per_pixel / 2;
+
+ for (unsigned int y = 0; y < surface_height; ++y) {
+ for (unsigned int x = 0; x < surface_width; ++x) {
+ const u32 coarse_y = y & ~7;
+ u32 offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride;
+ const u8* pixel = buffer + offset;
+ Math::Vec4<u8> color = { 0, 0, 0, 0 };
+
+ switch(surface_format) {
+ case Format::D16:
+ {
+ u32 data = Color::DecodeD16(pixel);
+ color.r() = data & 0xFF;
+ color.g() = (data >> 8) & 0xFF;
+ break;
+ }
+ case Format::D24:
+ {
+ u32 data = Color::DecodeD24(pixel);
+ color.r() = data & 0xFF;
+ color.g() = (data >> 8) & 0xFF;
+ color.b() = (data >> 16) & 0xFF;
+ break;
+ }
+ case Format::D24X8:
+ {
+ Math::Vec2<u32> data = Color::DecodeD24S8(pixel);
+ color.r() = data.x & 0xFF;
+ color.g() = (data.x >> 8) & 0xFF;
+ color.b() = (data.x >> 16) & 0xFF;
+ break;
+ }
+ case Format::X24S8:
+ {
+ Math::Vec2<u32> data = Color::DecodeD24S8(pixel);
+ color.r() = color.g() = color.b() = data.y;
+ break;
+ }
+ default:
+ qDebug() << "Unknown surface format " << static_cast<int>(surface_format);
+ break;
+ }
+
+ decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), 255));
+ }
+ }
+
+ }
+
+ pixmap = QPixmap::fromImage(decoded_image);
+ surface_picture_label->setPixmap(pixmap);
+ surface_picture_label->resize(pixmap.size());
+
+ // Update the info with pixel data
+ surface_picker_x_control->setEnabled(true);
+ surface_picker_y_control->setEnabled(true);
+ Pick(surface_picker_x, surface_picker_y);
+
+ // Enable saving the converted pixmap to file
+ save_surface->setEnabled(true);
+}
+
+void GraphicsSurfaceWidget::SaveSurface() {
+ QString png_filter = tr("Portable Network Graphic (*.png)");
+ QString bin_filter = tr("Binary data (*.bin)");
+
+ QString selectedFilter;
+ QString filename = QFileDialog::getSaveFileName(this, tr("Save Surface"), QString("texture-0x%1.png").arg(QString::number(surface_address, 16)),
+ QString("%1;;%2").arg(png_filter, bin_filter), &selectedFilter);
+
+ if (filename.isEmpty()) {
+ // If the user canceled the dialog, don't save anything.
+ return;
+ }
+
+ if (selectedFilter == png_filter) {
+ const QPixmap* pixmap = surface_picture_label->pixmap();
+ ASSERT_MSG(pixmap != nullptr, "No pixmap set");
+
+ QFile file(filename);
+ file.open(QIODevice::WriteOnly);
+ if (pixmap)
+ pixmap->save(&file, "PNG");
+ } else if (selectedFilter == bin_filter) {
+ const u8* buffer = Memory::GetPhysicalPointer(surface_address);
+ ASSERT_MSG(buffer != nullptr, "Memory not accessible");
+
+ QFile file(filename);
+ file.open(QIODevice::WriteOnly);
+ int size = surface_width * surface_height * NibblesPerPixel(surface_format) / 2;
+ QByteArray data(reinterpret_cast<const char*>(buffer), size);
+ file.write(data);
+ } else {
+ UNREACHABLE_MSG("Unhandled filter selected");
+ }
+}
+
+unsigned int GraphicsSurfaceWidget::NibblesPerPixel(GraphicsSurfaceWidget::Format format) {
+ if (format <= Format::MaxTextureFormat) {
+ return Pica::Regs::NibblesPerPixel(static_cast<Pica::Regs::TextureFormat>(format));
+ }
+
+ switch (format) {
+ case Format::D24X8:
+ case Format::X24S8:
+ return 4 * 2;
+ case Format::D24:
+ return 3 * 2;
+ case Format::D16:
+ return 2 * 2;
+ default:
+ UNREACHABLE_MSG("GraphicsSurfaceWidget::BytesPerPixel: this "
+ "should not be reached as this function should "
+ "be given a format which is in "
+ "GraphicsSurfaceWidget::Format. Instead got %i",
+ static_cast<int>(format));
+ return 0;
+ }
+}
diff --git a/src/citra_qt/debugger/graphics_surface.h b/src/citra_qt/debugger/graphics_surface.h
new file mode 100644
index 000000000..7c7f50e38
--- /dev/null
+++ b/src/citra_qt/debugger/graphics_surface.h
@@ -0,0 +1,120 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "citra_qt/debugger/graphics_breakpoint_observer.h"
+
+#include <QLabel>
+#include <QPushButton>
+
+class QComboBox;
+class QSpinBox;
+class CSpinBox;
+
+class GraphicsSurfaceWidget;
+
+class SurfacePicture : public QLabel
+{
+ Q_OBJECT
+
+public:
+ SurfacePicture(QWidget* parent = 0, GraphicsSurfaceWidget* surface_widget = nullptr);
+ ~SurfacePicture();
+
+protected slots:
+ virtual void mouseMoveEvent(QMouseEvent* event);
+ virtual void mousePressEvent(QMouseEvent* event);
+
+private:
+ GraphicsSurfaceWidget* surface_widget;
+
+};
+
+class GraphicsSurfaceWidget : public BreakPointObserverDock {
+ Q_OBJECT
+
+ using Event = Pica::DebugContext::Event;
+
+ enum class Source {
+ ColorBuffer = 0,
+ DepthBuffer = 1,
+ StencilBuffer = 2,
+ Texture0 = 3,
+ Texture1 = 4,
+ Texture2 = 5,
+ Custom = 6,
+ };
+
+ enum class Format {
+ // These must match the TextureFormat type!
+ RGBA8 = 0,
+ RGB8 = 1,
+ RGB5A1 = 2,
+ RGB565 = 3,
+ RGBA4 = 4,
+ IA8 = 5,
+ RG8 = 6, ///< @note Also called HILO8 in 3DBrew.
+ I8 = 7,
+ A8 = 8,
+ IA4 = 9,
+ I4 = 10,
+ A4 = 11,
+ ETC1 = 12, // compressed
+ ETC1A4 = 13,
+ MaxTextureFormat = 13,
+ D16 = 14,
+ D24 = 15,
+ D24X8 = 16,
+ X24S8 = 17,
+ Unknown = 18,
+ };
+
+ static unsigned int NibblesPerPixel(Format format);
+
+public:
+ GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent = nullptr);
+ void Pick(int x, int y);
+
+public slots:
+ void OnSurfaceSourceChanged(int new_value);
+ void OnSurfaceAddressChanged(qint64 new_value);
+ void OnSurfaceWidthChanged(int new_value);
+ void OnSurfaceHeightChanged(int new_value);
+ void OnSurfaceFormatChanged(int new_value);
+ void OnSurfacePickerXChanged(int new_value);
+ void OnSurfacePickerYChanged(int new_value);
+ void OnUpdate();
+
+private slots:
+ void OnBreakPointHit(Pica::DebugContext::Event event, void* data) override;
+ void OnResumed() override;
+
+ void SaveSurface();
+
+signals:
+ void Update();
+
+private:
+
+ QComboBox* surface_source_list;
+ CSpinBox* surface_address_control;
+ QSpinBox* surface_width_control;
+ QSpinBox* surface_height_control;
+ QComboBox* surface_format_control;
+
+ SurfacePicture* surface_picture_label;
+ QSpinBox* surface_picker_x_control;
+ QSpinBox* surface_picker_y_control;
+ QLabel* surface_info_label;
+ QPushButton* save_surface;
+
+ Source surface_source;
+ unsigned surface_address;
+ unsigned surface_width;
+ unsigned surface_height;
+ Format surface_format;
+ int surface_picker_x = 0;
+ int surface_picker_y = 0;
+};
diff --git a/src/citra_qt/debugger/profiler.cpp b/src/citra_qt/debugger/profiler.cpp
index 7bb010f77..585ac049a 100644
--- a/src/citra_qt/debugger/profiler.cpp
+++ b/src/citra_qt/debugger/profiler.cpp
@@ -151,6 +151,8 @@ private:
/// This timer is used to redraw the widget's contents continuously. To save resources, it only
/// runs while the widget is visible.
QTimer update_timer;
+ /// Scale the coordinate system appropriately when physical DPI != logical DPI.
+ qreal x_scale, y_scale;
};
#endif
@@ -220,11 +222,17 @@ MicroProfileWidget::MicroProfileWidget(QWidget* parent) : QWidget(parent) {
MicroProfileInitUI();
connect(&update_timer, SIGNAL(timeout()), SLOT(update()));
+
+ QPainter painter(this);
+ x_scale = qreal(painter.device()->physicalDpiX()) / qreal(painter.device()->logicalDpiX());
+ y_scale = qreal(painter.device()->physicalDpiY()) / qreal(painter.device()->logicalDpiY());
}
void MicroProfileWidget::paintEvent(QPaintEvent* ev) {
QPainter painter(this);
+ painter.scale(x_scale, y_scale);
+
painter.setBackground(Qt::black);
painter.eraseRect(rect());
@@ -248,24 +256,24 @@ void MicroProfileWidget::hideEvent(QHideEvent* ev) {
}
void MicroProfileWidget::mouseMoveEvent(QMouseEvent* ev) {
- MicroProfileMousePosition(ev->x(), ev->y(), 0);
+ MicroProfileMousePosition(ev->x() / x_scale, ev->y() / y_scale, 0);
ev->accept();
}
void MicroProfileWidget::mousePressEvent(QMouseEvent* ev) {
- MicroProfileMousePosition(ev->x(), ev->y(), 0);
+ MicroProfileMousePosition(ev->x() / x_scale, ev->y() / y_scale, 0);
MicroProfileMouseButton(ev->buttons() & Qt::LeftButton, ev->buttons() & Qt::RightButton);
ev->accept();
}
void MicroProfileWidget::mouseReleaseEvent(QMouseEvent* ev) {
- MicroProfileMousePosition(ev->x(), ev->y(), 0);
+ MicroProfileMousePosition(ev->x() / x_scale, ev->y() / y_scale, 0);
MicroProfileMouseButton(ev->buttons() & Qt::LeftButton, ev->buttons() & Qt::RightButton);
ev->accept();
}
void MicroProfileWidget::wheelEvent(QWheelEvent* ev) {
- MicroProfileMousePosition(ev->x(), ev->y(), ev->delta() / 120);
+ MicroProfileMousePosition(ev->x() / x_scale, ev->y() / y_scale, ev->delta() / 120);
ev->accept();
}
diff --git a/src/citra_qt/game_list.cpp b/src/citra_qt/game_list.cpp
index d4ac9c96e..15484fae3 100644
--- a/src/citra_qt/game_list.cpp
+++ b/src/citra_qt/game_list.cpp
@@ -118,46 +118,33 @@ void GameList::LoadInterfaceLayout()
item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder());
}
-void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, bool deep_scan)
+void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion)
{
const auto callback = [&](unsigned* num_entries_out,
const std::string& directory,
- const std::string& virtual_name) -> bool {
+ const std::string& virtual_name,
+ unsigned int recursion) -> bool {
std::string physical_name = directory + DIR_SEP + virtual_name;
if (stop_processing)
return false; // Breaks the callback loop.
- if (deep_scan && FileUtil::IsDirectory(physical_name)) {
- AddFstEntriesToGameList(physical_name, true);
- } else {
- std::string filename_filename, filename_extension;
- Common::SplitPath(physical_name, nullptr, &filename_filename, &filename_extension);
-
- Loader::FileType guessed_filetype = Loader::GuessFromExtension(filename_extension);
- if (guessed_filetype == Loader::FileType::Unknown)
- return true;
- Loader::FileType filetype = Loader::IdentifyFile(physical_name);
- if (filetype == Loader::FileType::Unknown) {
- LOG_WARNING(Frontend, "File %s is of indeterminate type and is possibly corrupted.", physical_name.c_str());
+ if (!FileUtil::IsDirectory(physical_name)) {
+ std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(physical_name);
+ if (!loader)
return true;
- }
- if (guessed_filetype != filetype) {
- LOG_WARNING(Frontend, "Filetype and extension of file %s do not match.", physical_name.c_str());
- }
std::vector<u8> smdh;
- std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(FileUtil::IOFile(physical_name, "rb"), filetype, filename_filename, physical_name);
-
- if (loader)
- loader->ReadIcon(smdh);
+ loader->ReadIcon(smdh);
emit EntryReady({
new GameListItemPath(QString::fromStdString(physical_name), smdh),
- new GameListItem(QString::fromStdString(Loader::GetFileTypeString(filetype))),
+ new GameListItem(QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
new GameListItemSize(FileUtil::GetSize(physical_name)),
});
+ } else if (recursion > 0) {
+ AddFstEntriesToGameList(physical_name, recursion - 1);
}
return true;
@@ -169,7 +156,7 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, bool d
void GameListWorker::run()
{
stop_processing = false;
- AddFstEntriesToGameList(dir_path.toStdString(), deep_scan);
+ AddFstEntriesToGameList(dir_path.toStdString(), deep_scan ? 256 : 0);
emit Finished();
}
diff --git a/src/citra_qt/game_list_p.h b/src/citra_qt/game_list_p.h
index 284f5da81..353b2d1e2 100644
--- a/src/citra_qt/game_list_p.h
+++ b/src/citra_qt/game_list_p.h
@@ -15,52 +15,21 @@
#include "common/string_util.h"
#include "common/color.h"
-#include "core/loader/loader.h"
+#include "core/loader/smdh.h"
#include "video_core/utils.h"
/**
- * Tests if data is a valid SMDH by its length and magic number.
- * @param smdh_data data buffer to test
- * @return bool test result
- */
-static bool IsValidSMDH(const std::vector<u8>& smdh_data) {
- if (smdh_data.size() < sizeof(Loader::SMDH))
- return false;
-
- u32 magic;
- memcpy(&magic, smdh_data.data(), 4);
-
- return Loader::MakeMagic('S', 'M', 'D', 'H') == magic;
-}
-
-/**
* Gets game icon from SMDH
* @param sdmh SMDH data
* @param large If true, returns large icon (48x48), otherwise returns small icon (24x24)
* @return QPixmap game icon
*/
-static QPixmap GetIconFromSMDH(const Loader::SMDH& smdh, bool large) {
- u32 size;
- const u8* icon_data;
-
- if (large) {
- size = 48;
- icon_data = smdh.large_icon.data();
- } else {
- size = 24;
- icon_data = smdh.small_icon.data();
- }
-
- QImage icon(size, size, QImage::Format::Format_RGB888);
- for (u32 x = 0; x < size; ++x) {
- for (u32 y = 0; y < size; ++y) {
- u32 coarse_y = y & ~7;
- auto v = Color::DecodeRGB565(
- icon_data + VideoCore::GetMortonOffset(x, y, 2) + coarse_y * size * 2);
- icon.setPixel(x, y, qRgb(v.r(), v.g(), v.b()));
- }
- }
+static QPixmap GetQPixmapFromSMDH(const Loader::SMDH& smdh, bool large) {
+ std::vector<u16> icon_data = smdh.GetIcon(large);
+ const uchar* data = reinterpret_cast<const uchar*>(icon_data.data());
+ int size = large ? 48 : 24;
+ QImage icon(data, size, size, QImage::Format::Format_RGB16);
return QPixmap::fromImage(icon);
}
@@ -82,8 +51,8 @@ static QPixmap GetDefaultIcon(bool large) {
* @param language title language
* @return QString short title
*/
-static QString GetShortTitleFromSMDH(const Loader::SMDH& smdh, Loader::SMDH::TitleLanguage language) {
- return QString::fromUtf16(smdh.titles[static_cast<int>(language)].short_title.data());
+static QString GetQStringShortTitleFromSMDH(const Loader::SMDH& smdh, Loader::SMDH::TitleLanguage language) {
+ return QString::fromUtf16(smdh.GetShortTitle(language).data());
}
class GameListItem : public QStandardItem {
@@ -112,7 +81,7 @@ public:
{
setData(game_path, FullPathRole);
- if (!IsValidSMDH(smdh_data)) {
+ if (!Loader::IsValidSMDH(smdh_data)) {
// SMDH is not valid, set a default icon
setData(GetDefaultIcon(true), Qt::DecorationRole);
return;
@@ -122,10 +91,10 @@ public:
memcpy(&smdh, smdh_data.data(), sizeof(Loader::SMDH));
// Get icon from SMDH
- setData(GetIconFromSMDH(smdh, true), Qt::DecorationRole);
+ setData(GetQPixmapFromSMDH(smdh, true), Qt::DecorationRole);
// Get title form SMDH
- setData(GetShortTitleFromSMDH(smdh, Loader::SMDH::TitleLanguage::English), TitleRole);
+ setData(GetQStringShortTitleFromSMDH(smdh, Loader::SMDH::TitleLanguage::English), TitleRole);
}
QVariant data(int role) const override {
@@ -212,5 +181,5 @@ private:
bool deep_scan;
std::atomic_bool stop_processing;
- void AddFstEntriesToGameList(const std::string& dir_path, bool deep_scan);
+ void AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion = 0);
};
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index a85c94a4b..0ed1ffa5a 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -29,7 +29,7 @@
#include "citra_qt/debugger/graphics.h"
#include "citra_qt/debugger/graphics_breakpoints.h"
#include "citra_qt/debugger/graphics_cmdlists.h"
-#include "citra_qt/debugger/graphics_framebuffer.h"
+#include "citra_qt/debugger/graphics_surface.h"
#include "citra_qt/debugger/graphics_tracing.h"
#include "citra_qt/debugger/graphics_vertex_shader.h"
#include "citra_qt/debugger/profiler.h"
@@ -101,10 +101,6 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr)
addDockWidget(Qt::RightDockWidgetArea, graphicsBreakpointsWidget);
graphicsBreakpointsWidget->hide();
- auto graphicsFramebufferWidget = new GraphicsFramebufferWidget(Pica::g_debug_context, this);
- addDockWidget(Qt::RightDockWidgetArea, graphicsFramebufferWidget);
- graphicsFramebufferWidget->hide();
-
auto graphicsVertexShaderWidget = new GraphicsVertexShaderWidget(Pica::g_debug_context, this);
addDockWidget(Qt::RightDockWidgetArea, graphicsVertexShaderWidget);
graphicsVertexShaderWidget->hide();
@@ -113,7 +109,12 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr)
addDockWidget(Qt::RightDockWidgetArea, graphicsTracingWidget);
graphicsTracingWidget->hide();
+ auto graphicsSurfaceViewerAction = new QAction(tr("Create Pica surface viewer"), this);
+ connect(graphicsSurfaceViewerAction, SIGNAL(triggered()), this, SLOT(OnCreateGraphicsSurfaceViewer()));
+
QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging"));
+ debug_menu->addAction(graphicsSurfaceViewerAction);
+ debug_menu->addSeparator();
debug_menu->addAction(profilerWidget->toggleViewAction());
#if MICROPROFILE_ENABLED
debug_menu->addAction(microProfileDialog->toggleViewAction());
@@ -124,7 +125,6 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr)
debug_menu->addAction(graphicsWidget->toggleViewAction());
debug_menu->addAction(graphicsCommandsWidget->toggleViewAction());
debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction());
- debug_menu->addAction(graphicsFramebufferWidget->toggleViewAction());
debug_menu->addAction(graphicsVertexShaderWidget->toggleViewAction());
debug_menu->addAction(graphicsTracingWidget->toggleViewAction());
@@ -272,7 +272,15 @@ bool GMainWindow::InitializeSystem() {
}
bool GMainWindow::LoadROM(const std::string& filename) {
- Loader::ResultStatus result = Loader::LoadFile(filename);
+ std::unique_ptr<Loader::AppLoader> app_loader = Loader::GetLoader(filename);
+ if (!app_loader) {
+ LOG_CRITICAL(Frontend, "Failed to obtain loader for %s!", filename.c_str());
+ QMessageBox::critical(this, tr("Error while loading ROM!"),
+ tr("The ROM format is not supported."));
+ return false;
+ }
+
+ Loader::ResultStatus result = app_loader->Load();
if (Loader::ResultStatus::Success != result) {
LOG_CRITICAL(Frontend, "Failed to load ROM!");
System::Shutdown();
@@ -509,6 +517,13 @@ void GMainWindow::OnConfigure() {
}
}
+void GMainWindow::OnCreateGraphicsSurfaceViewer() {
+ auto graphicsSurfaceViewerWidget = new GraphicsSurfaceWidget(Pica::g_debug_context, this);
+ addDockWidget(Qt::RightDockWidgetArea, graphicsSurfaceViewerWidget);
+ // TODO: Maybe graphicsSurfaceViewerWidget->setFloating(true);
+ graphicsSurfaceViewerWidget->show();
+}
+
bool GMainWindow::ConfirmClose() {
if (emu_thread == nullptr || !UISettings::values.confirm_before_closing)
return true;
diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h
index 477db5c5c..b836b13fb 100644
--- a/src/citra_qt/main.h
+++ b/src/citra_qt/main.h
@@ -108,6 +108,7 @@ private slots:
void OnConfigure();
void OnDisplayTitleBars(bool);
void ToggleWindowMode();
+ void OnCreateGraphicsSurfaceViewer();
private:
Ui::MainWindow ui;
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h
index ab3515683..4633897ce 100644
--- a/src/common/common_funcs.h
+++ b/src/common/common_funcs.h
@@ -72,18 +72,24 @@ inline u64 _rotr64(u64 x, unsigned int shift){
}
#else // _MSC_VER
- #if (_MSC_VER < 1900)
- // Function Cross-Compatibility
- #define snprintf _snprintf
- #endif
-
- // Locale Cross-Compatibility
- #define locale_t _locale_t
-
- extern "C" {
- __declspec(dllimport) void __stdcall DebugBreak(void);
- }
- #define Crash() {DebugBreak();}
+
+#if (_MSC_VER < 1900)
+ // Function Cross-Compatibility
+ #define snprintf _snprintf
+#endif
+
+// Locale Cross-Compatibility
+#define locale_t _locale_t
+
+extern "C" {
+ __declspec(dllimport) void __stdcall DebugBreak(void);
+}
+#define Crash() {DebugBreak();}
+
+// cstdlib provides these on MSVC
+#define rotr _rotr
+#define rotl _rotl
+
#endif // _MSC_VER ndef
// Generic function to get last error message.
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index 6e2867658..17af7c385 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -434,7 +434,7 @@ bool CreateEmptyFile(const std::string &filename)
}
-bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directory, DirectoryEntryCallable callback)
+bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directory, DirectoryEntryCallable callback, unsigned int recursion)
{
LOG_TRACE(Common_Filesystem, "directory %s", directory.c_str());
@@ -472,7 +472,7 @@ bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directo
continue;
unsigned ret_entries = 0;
- if (!callback(&ret_entries, directory, virtual_name)) {
+ if (!callback(&ret_entries, directory, virtual_name, recursion)) {
callback_error = true;
break;
}
@@ -486,30 +486,34 @@ bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directo
closedir(dirp);
#endif
- if (!callback_error) {
- // num_entries_out is allowed to be specified nullptr, in which case we shouldn't try to set it
- if (num_entries_out != nullptr)
- *num_entries_out = found_entries;
- return true;
- } else {
+ if (callback_error)
return false;
- }
+
+ // num_entries_out is allowed to be specified nullptr, in which case we shouldn't try to set it
+ if (num_entries_out != nullptr)
+ *num_entries_out = found_entries;
+ return true;
}
-unsigned ScanDirectoryTree(const std::string &directory, FSTEntry& parent_entry)
+unsigned ScanDirectoryTree(const std::string &directory, FSTEntry& parent_entry, unsigned int recursion)
{
const auto callback = [&parent_entry](unsigned* num_entries_out,
const std::string& directory,
- const std::string& virtual_name) -> bool {
+ const std::string& virtual_name,
+ unsigned int recursion) -> bool {
FSTEntry entry;
entry.virtualName = virtual_name;
entry.physicalName = directory + DIR_SEP + virtual_name;
if (IsDirectory(entry.physicalName)) {
entry.isDirectory = true;
- // is a directory, lets go inside
- entry.size = ScanDirectoryTree(entry.physicalName, entry);
- *num_entries_out += (int)entry.size;
+ // is a directory, lets go inside if we didn't recurse to often
+ if (recursion > 0) {
+ entry.size = ScanDirectoryTree(entry.physicalName, entry, recursion - 1);
+ *num_entries_out += (int)entry.size;
+ } else {
+ entry.size = 0;
+ }
} else { // is a file
entry.isDirectory = false;
entry.size = GetSize(entry.physicalName);
@@ -522,23 +526,27 @@ unsigned ScanDirectoryTree(const std::string &directory, FSTEntry& parent_entry)
};
unsigned num_entries;
- return ForeachDirectoryEntry(&num_entries, directory, callback) ? num_entries : 0;
+ return ForeachDirectoryEntry(&num_entries, directory, callback, recursion) ? num_entries : 0;
}
-bool DeleteDirRecursively(const std::string &directory)
+bool DeleteDirRecursively(const std::string &directory, unsigned int recursion)
{
const static auto callback = [](unsigned* num_entries_out,
const std::string& directory,
- const std::string& virtual_name) -> bool {
+ const std::string& virtual_name,
+ unsigned int recursion) -> bool {
std::string new_path = directory + DIR_SEP_CHR + virtual_name;
- if (IsDirectory(new_path))
- return DeleteDirRecursively(new_path);
+ if (IsDirectory(new_path)) {
+ if (recursion == 0)
+ return false;
+ return DeleteDirRecursively(new_path, recursion - 1);
+ }
return Delete(new_path);
};
- if (!ForeachDirectoryEntry(nullptr, directory, callback))
+ if (!ForeachDirectoryEntry(nullptr, directory, callback, recursion))
return false;
// Delete the outermost directory
diff --git a/src/common/file_util.h b/src/common/file_util.h
index c6a8694ce..32ae2dc57 100644
--- a/src/common/file_util.h
+++ b/src/common/file_util.h
@@ -105,11 +105,13 @@ bool CreateEmptyFile(const std::string &filename);
* @param num_entries_out to be assigned by the callable with the number of iterated directory entries, never null
* @param directory the path to the enclosing directory
* @param virtual_name the entry name, without any preceding directory info
+ * @param recursion Number of children directory to read before giving up
* @return whether handling the entry succeeded
*/
using DirectoryEntryCallable = std::function<bool(unsigned* num_entries_out,
const std::string& directory,
- const std::string& virtual_name)>;
+ const std::string& virtual_name,
+ unsigned int recursion)>;
/**
* Scans a directory, calling the callback for each file/directory contained within.
@@ -117,20 +119,22 @@ using DirectoryEntryCallable = std::function<bool(unsigned* num_entries_out,
* @param num_entries_out assigned by the function with the number of iterated directory entries, can be null
* @param directory the directory to scan
* @param callback The callback which will be called for each entry
+ * @param recursion Number of children directories to read before giving up
* @return whether scanning the directory succeeded
*/
-bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directory, DirectoryEntryCallable callback);
+bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directory, DirectoryEntryCallable callback, unsigned int recursion = 0);
/**
* Scans the directory tree, storing the results.
* @param directory the parent directory to start scanning from
* @param parent_entry FSTEntry where the filesystem tree results will be stored.
+ * @param recursion Number of children directories to read before giving up.
* @return the total number of files/directories found
*/
-unsigned ScanDirectoryTree(const std::string &directory, FSTEntry& parent_entry);
+unsigned ScanDirectoryTree(const std::string &directory, FSTEntry& parent_entry, unsigned int recursion = 0);
// deletes the given directory and anything under it. Returns true on success.
-bool DeleteDirRecursively(const std::string &directory);
+bool DeleteDirRecursively(const std::string &directory, unsigned int recursion = 256);
// Returns the current directory
std::string GetCurrentDir();
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index f6a7566bf..e9b04098b 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -42,6 +42,7 @@ set(SRCS
hle/kernel/timer.cpp
hle/kernel/vm_manager.cpp
hle/service/ac_u.cpp
+ hle/service/act_a.cpp
hle/service/act_u.cpp
hle/service/am/am.cpp
hle/service/am/am_app.cpp
@@ -69,7 +70,10 @@ set(SRCS
hle/service/cfg/cfg_s.cpp
hle/service/cfg/cfg_u.cpp
hle/service/csnd_snd.cpp
- hle/service/dlp_srvr.cpp
+ hle/service/dlp/dlp.cpp
+ hle/service/dlp/dlp_clnt.cpp
+ hle/service/dlp/dlp_fkcl.cpp
+ hle/service/dlp/dlp_srvr.cpp
hle/service/dsp_dsp.cpp
hle/service/err_f.cpp
hle/service/frd/frd.cpp
@@ -120,6 +124,7 @@ set(SRCS
loader/elf.cpp
loader/loader.cpp
loader/ncch.cpp
+ loader/smdh.cpp
tracer/recorder.cpp
memory.cpp
settings.cpp
@@ -176,6 +181,7 @@ set(HEADERS
hle/kernel/vm_manager.h
hle/result.h
hle/service/ac_u.h
+ hle/service/act_a.h
hle/service/act_u.h
hle/service/am/am.h
hle/service/am/am_app.h
@@ -203,7 +209,10 @@ set(HEADERS
hle/service/cfg/cfg_s.h
hle/service/cfg/cfg_u.h
hle/service/csnd_snd.h
- hle/service/dlp_srvr.h
+ hle/service/dlp/dlp.h
+ hle/service/dlp/dlp_clnt.h
+ hle/service/dlp/dlp_fkcl.h
+ hle/service/dlp/dlp_srvr.h
hle/service/dsp_dsp.h
hle/service/err_f.h
hle/service/frd/frd.h
@@ -254,6 +263,7 @@ set(HEADERS
loader/elf.h
loader/loader.h
loader/ncch.h
+ loader/smdh.h
tracer/recorder.h
tracer/citrace.h
memory.h
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index 533067d4f..d8abe5aeb 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -6,6 +6,7 @@
#include "common/common_types.h"
#include "core/arm/skyeye_common/arm_regformat.h"
+#include "core/arm/skyeye_common/vfp/asm_vfp.h"
namespace Core {
struct ThreadContext;
diff --git a/src/core/arm/dyncom/arm_dyncom_dec.cpp b/src/core/arm/dyncom/arm_dyncom_dec.cpp
index 8cd6755cb..247d379e3 100644
--- a/src/core/arm/dyncom/arm_dyncom_dec.cpp
+++ b/src/core/arm/dyncom/arm_dyncom_dec.cpp
@@ -422,6 +422,10 @@ ARMDecodeStatus DecodeARMInstruction(u32 instr, s32* idx) {
n = arm_instruction[i].attribute_value;
base = 0;
+ // 3DS has no VFP3 support
+ if (arm_instruction[i].version == ARMVFP3)
+ continue;
+
while (n) {
if (arm_instruction[i].content[base + 1] == 31 && arm_instruction[i].content[base] == 0) {
// clrex
diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
index 8d4b26815..cfc67287f 100644
--- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
+++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
@@ -5527,28 +5527,32 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
// SMUAD and SMLAD
if (BIT(op2, 1) == 0) {
- RD = (product1 + product2);
+ u32 rd_val = (product1 + product2);
if (inst_cream->Ra != 15) {
- RD += cpu->Reg[inst_cream->Ra];
+ rd_val += cpu->Reg[inst_cream->Ra];
if (ARMul_AddOverflowQ(product1 + product2, cpu->Reg[inst_cream->Ra]))
cpu->Cpsr |= (1 << 27);
}
+ RD = rd_val;
+
if (ARMul_AddOverflowQ(product1, product2))
cpu->Cpsr |= (1 << 27);
}
// SMUSD and SMLSD
else {
- RD = (product1 - product2);
+ u32 rd_val = (product1 - product2);
if (inst_cream->Ra != 15) {
- RD += cpu->Reg[inst_cream->Ra];
+ rd_val += cpu->Reg[inst_cream->Ra];
if (ARMul_AddOverflowQ(product1 - product2, cpu->Reg[inst_cream->Ra]))
cpu->Cpsr |= (1 << 27);
}
+
+ RD = rd_val;
}
}
diff --git a/src/core/arm/skyeye_common/vfp/vfp_helper.h b/src/core/arm/skyeye_common/vfp/vfp_helper.h
index 210972917..68714800c 100644
--- a/src/core/arm/skyeye_common/vfp/vfp_helper.h
+++ b/src/core/arm/skyeye_common/vfp/vfp_helper.h
@@ -271,8 +271,9 @@ inline int vfp_single_type(const vfp_single* s)
// Unpack a single-precision float. Note that this returns the magnitude
// of the single-precision float mantissa with the 1. if necessary,
// aligned to bit 30.
-inline void vfp_single_unpack(vfp_single* s, s32 val, u32* fpscr)
+inline u32 vfp_single_unpack(vfp_single* s, s32 val, u32 fpscr)
{
+ u32 exceptions = 0;
s->sign = vfp_single_packed_sign(val) >> 16,
s->exponent = vfp_single_packed_exponent(val);
@@ -283,12 +284,13 @@ inline void vfp_single_unpack(vfp_single* s, s32 val, u32* fpscr)
// If flush-to-zero mode is enabled, turn the denormal into zero.
// On a VFPv2 architecture, the sign of the zero is always positive.
- if ((*fpscr & FPSCR_FLUSH_TO_ZERO) != 0 && (vfp_single_type(s) & VFP_DENORMAL) != 0) {
+ if ((fpscr & FPSCR_FLUSH_TO_ZERO) != 0 && (vfp_single_type(s) & VFP_DENORMAL) != 0) {
s->sign = 0;
s->exponent = 0;
s->significand = 0;
- *fpscr |= FPSCR_IDC;
+ exceptions |= FPSCR_IDC;
}
+ return exceptions;
}
// Re-pack a single-precision float. This assumes that the float is
@@ -302,7 +304,7 @@ inline s32 vfp_single_pack(const vfp_single* s)
}
-u32 vfp_single_normaliseround(ARMul_State* state, int sd, vfp_single* vs, u32 fpscr, u32 exceptions, const char* func);
+u32 vfp_single_normaliseround(ARMul_State* state, int sd, vfp_single* vs, u32 fpscr, const char* func);
// Double-precision
struct vfp_double {
@@ -357,8 +359,9 @@ inline int vfp_double_type(const vfp_double* s)
// Unpack a double-precision float. Note that this returns the magnitude
// of the double-precision float mantissa with the 1. if necessary,
// aligned to bit 62.
-inline void vfp_double_unpack(vfp_double* s, s64 val, u32* fpscr)
+inline u32 vfp_double_unpack(vfp_double* s, s64 val, u32 fpscr)
{
+ u32 exceptions = 0;
s->sign = vfp_double_packed_sign(val) >> 48;
s->exponent = vfp_double_packed_exponent(val);
@@ -369,12 +372,13 @@ inline void vfp_double_unpack(vfp_double* s, s64 val, u32* fpscr)
// If flush-to-zero mode is enabled, turn the denormal into zero.
// On a VFPv2 architecture, the sign of the zero is always positive.
- if ((*fpscr & FPSCR_FLUSH_TO_ZERO) != 0 && (vfp_double_type(s) & VFP_DENORMAL) != 0) {
+ if ((fpscr & FPSCR_FLUSH_TO_ZERO) != 0 && (vfp_double_type(s) & VFP_DENORMAL) != 0) {
s->sign = 0;
s->exponent = 0;
s->significand = 0;
- *fpscr |= FPSCR_IDC;
+ exceptions |= FPSCR_IDC;
}
+ return exceptions;
}
// Re-pack a double-precision float. This assumes that the float is
@@ -447,4 +451,4 @@ inline u32 fls(u32 x)
u32 vfp_double_multiply(vfp_double* vdd, vfp_double* vdn, vfp_double* vdm, u32 fpscr);
u32 vfp_double_add(vfp_double* vdd, vfp_double* vdn, vfp_double *vdm, u32 fpscr);
-u32 vfp_double_normaliseround(ARMul_State* state, int dd, vfp_double* vd, u32 fpscr, u32 exceptions, const char* func);
+u32 vfp_double_normaliseround(ARMul_State* state, int dd, vfp_double* vd, u32 fpscr, const char* func);
diff --git a/src/core/arm/skyeye_common/vfp/vfpdouble.cpp b/src/core/arm/skyeye_common/vfp/vfpdouble.cpp
index 45914d479..1d5641810 100644
--- a/src/core/arm/skyeye_common/vfp/vfpdouble.cpp
+++ b/src/core/arm/skyeye_common/vfp/vfpdouble.cpp
@@ -85,11 +85,12 @@ static void vfp_double_normalise_denormal(struct vfp_double *vd)
vfp_double_dump("normalise_denormal: out", vd);
}
-u32 vfp_double_normaliseround(ARMul_State* state, int dd, struct vfp_double *vd, u32 fpscr, u32 exceptions, const char *func)
+u32 vfp_double_normaliseround(ARMul_State* state, int dd, struct vfp_double *vd, u32 fpscr, const char *func)
{
u64 significand, incr;
int exponent, shift, underflow;
u32 rmode;
+ u32 exceptions = 0;
vfp_double_dump("pack: in", vd);
@@ -291,8 +292,9 @@ static u32 vfp_double_fsqrt(ARMul_State* state, int dd, int unused, int dm, u32
LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
vfp_double vdm, vdd, *vdp;
int ret, tm;
+ u32 exceptions = 0;
- vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr);
+ exceptions |= vfp_double_unpack(&vdm, vfp_get_double(state, dm), fpscr);
tm = vfp_double_type(&vdm);
if (tm & (VFP_NAN|VFP_INFINITY)) {
@@ -369,7 +371,8 @@ sqrt_invalid:
}
vdd.significand = vfp_shiftright64jamming(vdd.significand, 1);
- return vfp_double_normaliseround(state, dd, &vdd, fpscr, 0, "fsqrt");
+ exceptions |= vfp_double_normaliseround(state, dd, &vdd, fpscr, "fsqrt");
+ return exceptions;
}
/*
@@ -475,7 +478,7 @@ static u32 vfp_double_fcvts(ARMul_State* state, int sd, int unused, int dm, u32
u32 exceptions = 0;
LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
- vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr);
+ exceptions |= vfp_double_unpack(&vdm, vfp_get_double(state, dm), fpscr);
tm = vfp_double_type(&vdm);
@@ -504,7 +507,8 @@ static u32 vfp_double_fcvts(ARMul_State* state, int sd, int unused, int dm, u32
else
vsd.exponent = vdm.exponent - (1023 - 127);
- return vfp_single_normaliseround(state, sd, &vsd, fpscr, exceptions, "fcvts");
+ exceptions |= vfp_single_normaliseround(state, sd, &vsd, fpscr, "fcvts");
+ return exceptions;
pack_nan:
vfp_put_float(state, vfp_single_pack(&vsd), sd);
@@ -514,6 +518,7 @@ pack_nan:
static u32 vfp_double_fuito(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
{
struct vfp_double vdm;
+ u32 exceptions = 0;
u32 m = vfp_get_float(state, dm);
LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
@@ -521,12 +526,14 @@ static u32 vfp_double_fuito(ARMul_State* state, int dd, int unused, int dm, u32
vdm.exponent = 1023 + 63 - 1;
vdm.significand = (u64)m;
- return vfp_double_normaliseround(state, dd, &vdm, fpscr, 0, "fuito");
+ exceptions |= vfp_double_normaliseround(state, dd, &vdm, fpscr, "fuito");
+ return exceptions;
}
static u32 vfp_double_fsito(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
{
struct vfp_double vdm;
+ u32 exceptions = 0;
u32 m = vfp_get_float(state, dm);
LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
@@ -534,7 +541,8 @@ static u32 vfp_double_fsito(ARMul_State* state, int dd, int unused, int dm, u32
vdm.exponent = 1023 + 63 - 1;
vdm.significand = vdm.sign ? (~m + 1) : m;
- return vfp_double_normaliseround(state, dd, &vdm, fpscr, 0, "fsito");
+ exceptions |= vfp_double_normaliseround(state, dd, &vdm, fpscr, "fsito");
+ return exceptions;
}
static u32 vfp_double_ftoui(ARMul_State* state, int sd, int unused, int dm, u32 fpscr)
@@ -545,7 +553,7 @@ static u32 vfp_double_ftoui(ARMul_State* state, int sd, int unused, int dm, u32
int tm;
LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
- vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr);
+ exceptions |= vfp_double_unpack(&vdm, vfp_get_double(state, dm), fpscr);
/*
* Do we have a denormalised number?
@@ -560,7 +568,7 @@ static u32 vfp_double_ftoui(ARMul_State* state, int sd, int unused, int dm, u32
if (vdm.exponent >= 1023 + 32) {
d = vdm.sign ? 0 : 0xffffffff;
exceptions = FPSCR_IOC;
- } else if (vdm.exponent >= 1023 - 1) {
+ } else if (vdm.exponent >= 1023) {
int shift = 1023 + 63 - vdm.exponent;
u64 rem, incr = 0;
@@ -595,12 +603,20 @@ static u32 vfp_double_ftoui(ARMul_State* state, int sd, int unused, int dm, u32
} else {
d = 0;
if (vdm.exponent | vdm.significand) {
- exceptions |= FPSCR_IXC;
- if (rmode == FPSCR_ROUND_PLUSINF && vdm.sign == 0)
+ if (rmode == FPSCR_ROUND_NEAREST) {
+ if (vdm.exponent >= 1022) {
+ d = vdm.sign ? 0 : 1;
+ exceptions |= vdm.sign ? FPSCR_IOC : FPSCR_IXC;
+ } else {
+ exceptions |= FPSCR_IXC;
+ }
+ } else if (rmode == FPSCR_ROUND_PLUSINF && vdm.sign == 0) {
d = 1;
- else if (rmode == FPSCR_ROUND_MINUSINF && vdm.sign) {
- d = 0;
- exceptions |= FPSCR_IOC;
+ exceptions |= FPSCR_IXC;
+ } else if (rmode == FPSCR_ROUND_MINUSINF) {
+ exceptions |= vdm.sign ? FPSCR_IOC : FPSCR_IXC;
+ } else {
+ exceptions |= FPSCR_IXC;
}
}
}
@@ -615,7 +631,7 @@ static u32 vfp_double_ftoui(ARMul_State* state, int sd, int unused, int dm, u32
static u32 vfp_double_ftouiz(ARMul_State* state, int sd, int unused, int dm, u32 fpscr)
{
LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
- return vfp_double_ftoui(state, sd, unused, dm, FPSCR_ROUND_TOZERO);
+ return vfp_double_ftoui(state, sd, unused, dm, (fpscr & ~FPSCR_RMODE_MASK) | FPSCR_ROUND_TOZERO);
}
static u32 vfp_double_ftosi(ARMul_State* state, int sd, int unused, int dm, u32 fpscr)
@@ -626,7 +642,7 @@ static u32 vfp_double_ftosi(ARMul_State* state, int sd, int unused, int dm, u32
int tm;
LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
- vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr);
+ exceptions |= vfp_double_unpack(&vdm, vfp_get_double(state, dm), fpscr);
vfp_double_dump("VDM", &vdm);
/*
@@ -639,12 +655,12 @@ static u32 vfp_double_ftosi(ARMul_State* state, int sd, int unused, int dm, u32
if (tm & VFP_NAN) {
d = 0;
exceptions |= FPSCR_IOC;
- } else if (vdm.exponent >= 1023 + 32) {
+ } else if (vdm.exponent >= 1023 + 31) {
d = 0x7fffffff;
if (vdm.sign)
d = ~d;
exceptions |= FPSCR_IOC;
- } else if (vdm.exponent >= 1023 - 1) {
+ } else if (vdm.exponent >= 1023) {
int shift = 1023 + 63 - vdm.exponent; /* 58 */
u64 rem, incr = 0;
@@ -675,10 +691,17 @@ static u32 vfp_double_ftosi(ARMul_State* state, int sd, int unused, int dm, u32
d = 0;
if (vdm.exponent | vdm.significand) {
exceptions |= FPSCR_IXC;
- if (rmode == FPSCR_ROUND_PLUSINF && vdm.sign == 0)
+ if (rmode == FPSCR_ROUND_NEAREST) {
+ if (vdm.exponent >= 1022) {
+ d = vdm.sign ? 0xffffffff : 1;
+ } else {
+ d = 0;
+ }
+ } else if (rmode == FPSCR_ROUND_PLUSINF && vdm.sign == 0) {
d = 1;
- else if (rmode == FPSCR_ROUND_MINUSINF && vdm.sign)
- d = -1;
+ } else if (rmode == FPSCR_ROUND_MINUSINF && vdm.sign) {
+ d = 0xffffffff;
+ }
}
}
@@ -692,7 +715,7 @@ static u32 vfp_double_ftosi(ARMul_State* state, int sd, int unused, int dm, u32
static u32 vfp_double_ftosiz(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
{
LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
- return vfp_double_ftosi(state, dd, unused, dm, FPSCR_ROUND_TOZERO);
+ return vfp_double_ftosi(state, dd, unused, dm, (fpscr & ~FPSCR_RMODE_MASK) | FPSCR_ROUND_TOZERO);
}
static struct op fops_ext[] = {
@@ -892,21 +915,21 @@ static u32
vfp_double_multiply_accumulate(ARMul_State* state, int dd, int dn, int dm, u32 fpscr, u32 negate, const char *func)
{
struct vfp_double vdd, vdp, vdn, vdm;
- u32 exceptions;
+ u32 exceptions = 0;
- vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr);
+ exceptions |= vfp_double_unpack(&vdn, vfp_get_double(state, dn), fpscr);
if (vdn.exponent == 0 && vdn.significand)
vfp_double_normalise_denormal(&vdn);
- vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr);
+ exceptions |= vfp_double_unpack(&vdm, vfp_get_double(state, dm), fpscr);
if (vdm.exponent == 0 && vdm.significand)
vfp_double_normalise_denormal(&vdm);
- exceptions = vfp_double_multiply(&vdp, &vdn, &vdm, fpscr);
+ exceptions |= vfp_double_multiply(&vdp, &vdn, &vdm, fpscr);
if (negate & NEG_MULTIPLY)
vdp.sign = vfp_sign_negate(vdp.sign);
- vfp_double_unpack(&vdn, vfp_get_double(state, dd), &fpscr);
+ exceptions |= vfp_double_unpack(&vdn, vfp_get_double(state, dd), fpscr);
if (vdn.exponent == 0 && vdn.significand != 0)
vfp_double_normalise_denormal(&vdn);
@@ -915,7 +938,8 @@ vfp_double_multiply_accumulate(ARMul_State* state, int dd, int dn, int dm, u32 f
exceptions |= vfp_double_add(&vdd, &vdn, &vdp, fpscr);
- return vfp_double_normaliseround(state, dd, &vdd, fpscr, exceptions, func);
+ exceptions |= vfp_double_normaliseround(state, dd, &vdd, fpscr, func);
+ return exceptions;
}
/*
@@ -964,19 +988,21 @@ static u32 vfp_double_fnmsc(ARMul_State* state, int dd, int dn, int dm, u32 fpsc
static u32 vfp_double_fmul(ARMul_State* state, int dd, int dn, int dm, u32 fpscr)
{
struct vfp_double vdd, vdn, vdm;
- u32 exceptions;
+ u32 exceptions = 0;
LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
- vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr);
+ exceptions |= vfp_double_unpack(&vdn, vfp_get_double(state, dn), fpscr);
if (vdn.exponent == 0 && vdn.significand)
vfp_double_normalise_denormal(&vdn);
- vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr);
+ exceptions |= vfp_double_unpack(&vdm, vfp_get_double(state, dm), fpscr);
if (vdm.exponent == 0 && vdm.significand)
vfp_double_normalise_denormal(&vdm);
- exceptions = vfp_double_multiply(&vdd, &vdn, &vdm, fpscr);
- return vfp_double_normaliseround(state, dd, &vdd, fpscr, exceptions, "fmul");
+ exceptions |= vfp_double_multiply(&vdd, &vdn, &vdm, fpscr);
+
+ exceptions |= vfp_double_normaliseround(state, dd, &vdd, fpscr, "fmul");
+ return exceptions;
}
/*
@@ -985,21 +1011,22 @@ static u32 vfp_double_fmul(ARMul_State* state, int dd, int dn, int dm, u32 fpscr
static u32 vfp_double_fnmul(ARMul_State* state, int dd, int dn, int dm, u32 fpscr)
{
struct vfp_double vdd, vdn, vdm;
- u32 exceptions;
+ u32 exceptions = 0;
LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
- vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr);
+ exceptions |= vfp_double_unpack(&vdn, vfp_get_double(state, dn), fpscr);
if (vdn.exponent == 0 && vdn.significand)
vfp_double_normalise_denormal(&vdn);
- vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr);
+ exceptions |= vfp_double_unpack(&vdm, vfp_get_double(state, dm), fpscr);
if (vdm.exponent == 0 && vdm.significand)
vfp_double_normalise_denormal(&vdm);
- exceptions = vfp_double_multiply(&vdd, &vdn, &vdm, fpscr);
+ exceptions |= vfp_double_multiply(&vdd, &vdn, &vdm, fpscr);
vdd.sign = vfp_sign_negate(vdd.sign);
- return vfp_double_normaliseround(state, dd, &vdd, fpscr, exceptions, "fnmul");
+ exceptions |= vfp_double_normaliseround(state, dd, &vdd, fpscr, "fnmul");
+ return exceptions;
}
/*
@@ -1008,20 +1035,21 @@ static u32 vfp_double_fnmul(ARMul_State* state, int dd, int dn, int dm, u32 fpsc
static u32 vfp_double_fadd(ARMul_State* state, int dd, int dn, int dm, u32 fpscr)
{
struct vfp_double vdd, vdn, vdm;
- u32 exceptions;
+ u32 exceptions = 0;
LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
- vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr);
+ exceptions |= vfp_double_unpack(&vdn, vfp_get_double(state, dn), fpscr);
if (vdn.exponent == 0 && vdn.significand)
vfp_double_normalise_denormal(&vdn);
- vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr);
+ exceptions |= vfp_double_unpack(&vdm, vfp_get_double(state, dm), fpscr);
if (vdm.exponent == 0 && vdm.significand)
vfp_double_normalise_denormal(&vdm);
- exceptions = vfp_double_add(&vdd, &vdn, &vdm, fpscr);
+ exceptions |= vfp_double_add(&vdd, &vdn, &vdm, fpscr);
- return vfp_double_normaliseround(state, dd, &vdd, fpscr, exceptions, "fadd");
+ exceptions |= vfp_double_normaliseround(state, dd, &vdd, fpscr, "fadd");
+ return exceptions;
}
/*
@@ -1030,14 +1058,14 @@ static u32 vfp_double_fadd(ARMul_State* state, int dd, int dn, int dm, u32 fpscr
static u32 vfp_double_fsub(ARMul_State* state, int dd, int dn, int dm, u32 fpscr)
{
struct vfp_double vdd, vdn, vdm;
- u32 exceptions;
+ u32 exceptions = 0;
LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
- vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr);
+ exceptions |= vfp_double_unpack(&vdn, vfp_get_double(state, dn), fpscr);
if (vdn.exponent == 0 && vdn.significand)
vfp_double_normalise_denormal(&vdn);
- vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr);
+ exceptions |= vfp_double_unpack(&vdm, vfp_get_double(state, dm), fpscr);
if (vdm.exponent == 0 && vdm.significand)
vfp_double_normalise_denormal(&vdm);
@@ -1046,9 +1074,10 @@ static u32 vfp_double_fsub(ARMul_State* state, int dd, int dn, int dm, u32 fpscr
*/
vdm.sign = vfp_sign_negate(vdm.sign);
- exceptions = vfp_double_add(&vdd, &vdn, &vdm, fpscr);
+ exceptions |= vfp_double_add(&vdd, &vdn, &vdm, fpscr);
- return vfp_double_normaliseround(state, dd, &vdd, fpscr, exceptions, "fsub");
+ exceptions |= vfp_double_normaliseround(state, dd, &vdd, fpscr, "fsub");
+ return exceptions;
}
/*
@@ -1061,8 +1090,8 @@ static u32 vfp_double_fdiv(ARMul_State* state, int dd, int dn, int dm, u32 fpscr
int tm, tn;
LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
- vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr);
- vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr);
+ exceptions |= vfp_double_unpack(&vdn, vfp_get_double(state, dn), fpscr);
+ exceptions |= vfp_double_unpack(&vdm, vfp_get_double(state, dm), fpscr);
vdd.sign = vdn.sign ^ vdm.sign;
@@ -1131,16 +1160,18 @@ static u32 vfp_double_fdiv(ARMul_State* state, int dd, int dn, int dm, u32 fpscr
}
vdd.significand |= (reml != 0);
}
- return vfp_double_normaliseround(state, dd, &vdd, fpscr, 0, "fdiv");
+
+ exceptions |= vfp_double_normaliseround(state, dd, &vdd, fpscr, "fdiv");
+ return exceptions;
vdn_nan:
- exceptions = vfp_propagate_nan(&vdd, &vdn, &vdm, fpscr);
+ exceptions |= vfp_propagate_nan(&vdd, &vdn, &vdm, fpscr);
pack:
vfp_put_double(state, vfp_double_pack(&vdd), dd);
return exceptions;
vdm_nan:
- exceptions = vfp_propagate_nan(&vdd, &vdm, &vdn, fpscr);
+ exceptions |= vfp_propagate_nan(&vdd, &vdm, &vdn, fpscr);
goto pack;
zero:
@@ -1149,7 +1180,7 @@ zero:
goto pack;
divzero:
- exceptions = FPSCR_DZC;
+ exceptions |= FPSCR_DZC;
infinity:
vdd.exponent = 2047;
vdd.significand = 0;
@@ -1157,7 +1188,8 @@ infinity:
invalid:
vfp_put_double(state, vfp_double_pack(&vfp_double_default_qnan), dd);
- return FPSCR_IOC;
+ exceptions |= FPSCR_IOC;
+ return exceptions;
}
static struct op fops[] = {
diff --git a/src/core/arm/skyeye_common/vfp/vfpsingle.cpp b/src/core/arm/skyeye_common/vfp/vfpsingle.cpp
index e47ad2760..60264f9b3 100644
--- a/src/core/arm/skyeye_common/vfp/vfpsingle.cpp
+++ b/src/core/arm/skyeye_common/vfp/vfpsingle.cpp
@@ -89,10 +89,11 @@ static void vfp_single_normalise_denormal(struct vfp_single *vs)
}
-u32 vfp_single_normaliseround(ARMul_State* state, int sd, struct vfp_single *vs, u32 fpscr, u32 exceptions, const char *func)
+u32 vfp_single_normaliseround(ARMul_State* state, int sd, struct vfp_single *vs, u32 fpscr, const char *func)
{
u32 significand, incr, rmode;
int exponent, shift, underflow;
+ u32 exceptions = 0;
vfp_single_dump("pack: in", vs);
@@ -334,8 +335,9 @@ static u32 vfp_single_fsqrt(ARMul_State* state, int sd, int unused, s32 m, u32 f
{
struct vfp_single vsm, vsd, *vsp;
int ret, tm;
+ u32 exceptions = 0;
- vfp_single_unpack(&vsm, m, &fpscr);
+ exceptions |= vfp_single_unpack(&vsm, m, fpscr);
tm = vfp_single_type(&vsm);
if (tm & (VFP_NAN|VFP_INFINITY)) {
vsp = &vsd;
@@ -408,7 +410,8 @@ sqrt_invalid:
}
vsd.significand = vfp_shiftright32jamming(vsd.significand, 1);
- return vfp_single_normaliseround(state, sd, &vsd, fpscr, 0, "fsqrt");
+ exceptions |= vfp_single_normaliseround(state, sd, &vsd, fpscr, "fsqrt");
+ return exceptions;
}
/*
@@ -503,7 +506,7 @@ static u32 vfp_single_fcvtd(ARMul_State* state, int dd, int unused, s32 m, u32 f
int tm;
u32 exceptions = 0;
- vfp_single_unpack(&vsm, m, &fpscr);
+ exceptions |= vfp_single_unpack(&vsm, m, fpscr);
tm = vfp_single_type(&vsm);
@@ -511,7 +514,7 @@ static u32 vfp_single_fcvtd(ARMul_State* state, int dd, int unused, s32 m, u32 f
* If we have a signalling NaN, signal invalid operation.
*/
if (tm == VFP_SNAN)
- exceptions = FPSCR_IOC;
+ exceptions |= FPSCR_IOC;
if (tm & VFP_DENORMAL)
vfp_single_normalise_denormal(&vsm);
@@ -532,7 +535,8 @@ static u32 vfp_single_fcvtd(ARMul_State* state, int dd, int unused, s32 m, u32 f
else
vdd.exponent = vsm.exponent + (1023 - 127);
- return vfp_double_normaliseround(state, dd, &vdd, fpscr, exceptions, "fcvtd");
+ exceptions |= vfp_double_normaliseround(state, dd, &vdd, fpscr, "fcvtd");
+ return exceptions;
pack_nan:
vfp_put_double(state, vfp_double_pack(&vdd), dd);
@@ -542,23 +546,27 @@ pack_nan:
static u32 vfp_single_fuito(ARMul_State* state, int sd, int unused, s32 m, u32 fpscr)
{
struct vfp_single vs;
+ u32 exceptions = 0;
vs.sign = 0;
vs.exponent = 127 + 31 - 1;
vs.significand = (u32)m;
- return vfp_single_normaliseround(state, sd, &vs, fpscr, 0, "fuito");
+ exceptions |= vfp_single_normaliseround(state, sd, &vs, fpscr, "fuito");
+ return exceptions;
}
static u32 vfp_single_fsito(ARMul_State* state, int sd, int unused, s32 m, u32 fpscr)
{
struct vfp_single vs;
+ u32 exceptions = 0;
vs.sign = (m & 0x80000000) >> 16;
vs.exponent = 127 + 31 - 1;
vs.significand = vs.sign ? -m : m;
- return vfp_single_normaliseround(state, sd, &vs, fpscr, 0, "fsito");
+ exceptions |= vfp_single_normaliseround(state, sd, &vs, fpscr, "fsito");
+ return exceptions;
}
static u32 vfp_single_ftoui(ARMul_State* state, int sd, int unused, s32 m, u32 fpscr)
@@ -568,7 +576,7 @@ static u32 vfp_single_ftoui(ARMul_State* state, int sd, int unused, s32 m, u32 f
int rmode = fpscr & FPSCR_RMODE_MASK;
int tm;
- vfp_single_unpack(&vsm, m, &fpscr);
+ exceptions |= vfp_single_unpack(&vsm, m, fpscr);
vfp_single_dump("VSM", &vsm);
/*
@@ -583,7 +591,7 @@ static u32 vfp_single_ftoui(ARMul_State* state, int sd, int unused, s32 m, u32 f
if (vsm.exponent >= 127 + 32) {
d = vsm.sign ? 0 : 0xffffffff;
- exceptions = FPSCR_IOC;
+ exceptions |= FPSCR_IOC;
} else if (vsm.exponent >= 127) {
int shift = 127 + 31 - vsm.exponent;
u32 rem, incr = 0;
@@ -592,7 +600,11 @@ static u32 vfp_single_ftoui(ARMul_State* state, int sd, int unused, s32 m, u32 f
* 2^0 <= m < 2^32-2^8
*/
d = (vsm.significand << 1) >> shift;
- rem = vsm.significand << (33 - shift);
+ if (shift > 0) {
+ rem = (vsm.significand << 1) << (32 - shift);
+ } else {
+ rem = 0;
+ }
if (rmode == FPSCR_ROUND_NEAREST) {
incr = 0x80000000;
@@ -619,12 +631,20 @@ static u32 vfp_single_ftoui(ARMul_State* state, int sd, int unused, s32 m, u32 f
} else {
d = 0;
if (vsm.exponent | vsm.significand) {
- exceptions |= FPSCR_IXC;
- if (rmode == FPSCR_ROUND_PLUSINF && vsm.sign == 0)
+ if (rmode == FPSCR_ROUND_NEAREST) {
+ if (vsm.exponent >= 126) {
+ d = vsm.sign ? 0 : 1;
+ exceptions |= vsm.sign ? FPSCR_IOC : FPSCR_IXC;
+ } else {
+ exceptions |= FPSCR_IXC;
+ }
+ } else if (rmode == FPSCR_ROUND_PLUSINF && vsm.sign == 0) {
d = 1;
- else if (rmode == FPSCR_ROUND_MINUSINF && vsm.sign) {
- d = 0;
- exceptions |= FPSCR_IOC;
+ exceptions |= FPSCR_IXC;
+ } else if (rmode == FPSCR_ROUND_MINUSINF) {
+ exceptions |= vsm.sign ? FPSCR_IOC : FPSCR_IXC;
+ } else {
+ exceptions |= FPSCR_IXC;
}
}
}
@@ -638,7 +658,7 @@ static u32 vfp_single_ftoui(ARMul_State* state, int sd, int unused, s32 m, u32 f
static u32 vfp_single_ftouiz(ARMul_State* state, int sd, int unused, s32 m, u32 fpscr)
{
- return vfp_single_ftoui(state, sd, unused, m, FPSCR_ROUND_TOZERO);
+ return vfp_single_ftoui(state, sd, unused, m, (fpscr & ~FPSCR_RMODE_MASK) | FPSCR_ROUND_TOZERO);
}
static u32 vfp_single_ftosi(ARMul_State* state, int sd, int unused, s32 m, u32 fpscr)
@@ -648,7 +668,7 @@ static u32 vfp_single_ftosi(ARMul_State* state, int sd, int unused, s32 m, u32 f
int rmode = fpscr & FPSCR_RMODE_MASK;
int tm;
- vfp_single_unpack(&vsm, m, &fpscr);
+ exceptions |= vfp_single_unpack(&vsm, m, fpscr);
vfp_single_dump("VSM", &vsm);
/*
@@ -661,7 +681,7 @@ static u32 vfp_single_ftosi(ARMul_State* state, int sd, int unused, s32 m, u32 f
if (tm & VFP_NAN) {
d = 0;
exceptions |= FPSCR_IOC;
- } else if (vsm.exponent >= 127 + 32) {
+ } else if (vsm.exponent >= 127 + 31) {
/*
* m >= 2^31-2^7: invalid
*/
@@ -675,7 +695,7 @@ static u32 vfp_single_ftosi(ARMul_State* state, int sd, int unused, s32 m, u32 f
/* 2^0 <= m <= 2^31-2^7 */
d = (vsm.significand << 1) >> shift;
- rem = vsm.significand << (33 - shift);
+ rem = (vsm.significand << 1) << (32 - shift);
if (rmode == FPSCR_ROUND_NEAREST) {
incr = 0x80000000;
@@ -701,10 +721,14 @@ static u32 vfp_single_ftosi(ARMul_State* state, int sd, int unused, s32 m, u32 f
d = 0;
if (vsm.exponent | vsm.significand) {
exceptions |= FPSCR_IXC;
- if (rmode == FPSCR_ROUND_PLUSINF && vsm.sign == 0)
+ if (rmode == FPSCR_ROUND_NEAREST) {
+ if (vsm.exponent >= 126)
+ d = vsm.sign ? 0xffffffff : 1;
+ } else if (rmode == FPSCR_ROUND_PLUSINF && vsm.sign == 0) {
d = 1;
- else if (rmode == FPSCR_ROUND_MINUSINF && vsm.sign)
- d = -1;
+ } else if (rmode == FPSCR_ROUND_MINUSINF && vsm.sign) {
+ d = 0xffffffff;
+ }
}
}
@@ -717,7 +741,7 @@ static u32 vfp_single_ftosi(ARMul_State* state, int sd, int unused, s32 m, u32 f
static u32 vfp_single_ftosiz(ARMul_State* state, int sd, int unused, s32 m, u32 fpscr)
{
- return vfp_single_ftosi(state, sd, unused, m, FPSCR_ROUND_TOZERO);
+ return vfp_single_ftosi(state, sd, unused, m, (fpscr & ~FPSCR_RMODE_MASK) | FPSCR_ROUND_TOZERO);
}
static struct op fops_ext[] = {
@@ -774,7 +798,7 @@ vfp_single_fadd_nonnumber(struct vfp_single *vsd, struct vfp_single *vsn,
/*
* different signs -> invalid
*/
- exceptions = FPSCR_IOC;
+ exceptions |= FPSCR_IOC;
vsp = &vfp_single_default_qnan;
} else {
/*
@@ -921,27 +945,27 @@ static u32
vfp_single_multiply_accumulate(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr, u32 negate, const char *func)
{
vfp_single vsd, vsp, vsn, vsm;
- u32 exceptions;
+ u32 exceptions = 0;
s32 v;
v = vfp_get_float(state, sn);
LOG_TRACE(Core_ARM11, "s%u = %08x", sn, v);
- vfp_single_unpack(&vsn, v, &fpscr);
+ exceptions |= vfp_single_unpack(&vsn, v, fpscr);
if (vsn.exponent == 0 && vsn.significand)
vfp_single_normalise_denormal(&vsn);
- vfp_single_unpack(&vsm, m, &fpscr);
+ exceptions |= vfp_single_unpack(&vsm, m, fpscr);
if (vsm.exponent == 0 && vsm.significand)
vfp_single_normalise_denormal(&vsm);
- exceptions = vfp_single_multiply(&vsp, &vsn, &vsm, fpscr);
+ exceptions |= vfp_single_multiply(&vsp, &vsn, &vsm, fpscr);
if (negate & NEG_MULTIPLY)
vsp.sign = vfp_sign_negate(vsp.sign);
v = vfp_get_float(state, sd);
LOG_TRACE(Core_ARM11, "s%u = %08x", sd, v);
- vfp_single_unpack(&vsn, v, &fpscr);
+ exceptions |= vfp_single_unpack(&vsn, v, fpscr);
if (vsn.exponent == 0 && vsn.significand != 0)
vfp_single_normalise_denormal(&vsn);
@@ -950,7 +974,8 @@ vfp_single_multiply_accumulate(ARMul_State* state, int sd, int sn, s32 m, u32 fp
exceptions |= vfp_single_add(&vsd, &vsn, &vsp, fpscr);
- return vfp_single_normaliseround(state, sd, &vsd, fpscr, exceptions, func);
+ exceptions |= vfp_single_normaliseround(state, sd, &vsd, fpscr, func);
+ return exceptions;
}
/*
@@ -962,8 +987,10 @@ vfp_single_multiply_accumulate(ARMul_State* state, int sd, int sn, s32 m, u32 fp
*/
static u32 vfp_single_fmac(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
{
+ u32 exceptions = 0;
LOG_TRACE(Core_ARM11, "s%u = %08x", sn, sd);
- return vfp_single_multiply_accumulate(state, sd, sn, m, fpscr, 0, "fmac");
+ exceptions |= vfp_single_multiply_accumulate(state, sd, sn, m, fpscr, 0, "fmac");
+ return exceptions;
}
/*
@@ -1000,21 +1027,23 @@ static u32 vfp_single_fnmsc(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr
static u32 vfp_single_fmul(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
{
struct vfp_single vsd, vsn, vsm;
- u32 exceptions;
+ u32 exceptions = 0;
s32 n = vfp_get_float(state, sn);
LOG_TRACE(Core_ARM11, "s%u = %08x", sn, n);
- vfp_single_unpack(&vsn, n, &fpscr);
+ exceptions |= vfp_single_unpack(&vsn, n, fpscr);
if (vsn.exponent == 0 && vsn.significand)
vfp_single_normalise_denormal(&vsn);
- vfp_single_unpack(&vsm, m, &fpscr);
+ exceptions |= vfp_single_unpack(&vsm, m, fpscr);
if (vsm.exponent == 0 && vsm.significand)
vfp_single_normalise_denormal(&vsm);
- exceptions = vfp_single_multiply(&vsd, &vsn, &vsm, fpscr);
- return vfp_single_normaliseround(state, sd, &vsd, fpscr, exceptions, "fmul");
+ exceptions |= vfp_single_multiply(&vsd, &vsn, &vsm, fpscr);
+
+ exceptions |= vfp_single_normaliseround(state, sd, &vsd, fpscr, "fmul");
+ return exceptions;
}
/*
@@ -1023,22 +1052,24 @@ static u32 vfp_single_fmul(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
static u32 vfp_single_fnmul(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
{
struct vfp_single vsd, vsn, vsm;
- u32 exceptions;
+ u32 exceptions = 0;
s32 n = vfp_get_float(state, sn);
LOG_TRACE(Core_ARM11, "s%u = %08x", sn, n);
- vfp_single_unpack(&vsn, n, &fpscr);
+ exceptions |= vfp_single_unpack(&vsn, n, fpscr);
if (vsn.exponent == 0 && vsn.significand)
vfp_single_normalise_denormal(&vsn);
- vfp_single_unpack(&vsm, m, &fpscr);
+ exceptions |= vfp_single_unpack(&vsm, m, fpscr);
if (vsm.exponent == 0 && vsm.significand)
vfp_single_normalise_denormal(&vsm);
- exceptions = vfp_single_multiply(&vsd, &vsn, &vsm, fpscr);
+ exceptions |= vfp_single_multiply(&vsd, &vsn, &vsm, fpscr);
vsd.sign = vfp_sign_negate(vsd.sign);
- return vfp_single_normaliseround(state, sd, &vsd, fpscr, exceptions, "fnmul");
+
+ exceptions |= vfp_single_normaliseround(state, sd, &vsd, fpscr, "fnmul");
+ return exceptions;
}
/*
@@ -1047,7 +1078,7 @@ static u32 vfp_single_fnmul(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr
static u32 vfp_single_fadd(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
{
struct vfp_single vsd, vsn, vsm;
- u32 exceptions;
+ u32 exceptions = 0;
s32 n = vfp_get_float(state, sn);
LOG_TRACE(Core_ARM11, "s%u = %08x", sn, n);
@@ -1055,17 +1086,18 @@ static u32 vfp_single_fadd(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
/*
* Unpack and normalise denormals.
*/
- vfp_single_unpack(&vsn, n, &fpscr);
+ exceptions |= vfp_single_unpack(&vsn, n, fpscr);
if (vsn.exponent == 0 && vsn.significand)
vfp_single_normalise_denormal(&vsn);
- vfp_single_unpack(&vsm, m, &fpscr);
+ exceptions |= vfp_single_unpack(&vsm, m, fpscr);
if (vsm.exponent == 0 && vsm.significand)
vfp_single_normalise_denormal(&vsm);
- exceptions = vfp_single_add(&vsd, &vsn, &vsm, fpscr);
+ exceptions |= vfp_single_add(&vsd, &vsn, &vsm, fpscr);
- return vfp_single_normaliseround(state, sd, &vsd, fpscr, exceptions, "fadd");
+ exceptions |= vfp_single_normaliseround(state, sd, &vsd, fpscr, "fadd");
+ return exceptions;
}
/*
@@ -1095,8 +1127,8 @@ static u32 vfp_single_fdiv(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
LOG_TRACE(Core_ARM11, "s%u = %08x", sn, n);
- vfp_single_unpack(&vsn, n, &fpscr);
- vfp_single_unpack(&vsm, m, &fpscr);
+ exceptions |= vfp_single_unpack(&vsn, n, fpscr);
+ exceptions |= vfp_single_unpack(&vsm, m, fpscr);
vsd.sign = vsn.sign ^ vsm.sign;
@@ -1162,16 +1194,17 @@ static u32 vfp_single_fdiv(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
if ((vsd.significand & 0x3f) == 0)
vsd.significand |= ((u64)vsm.significand * vsd.significand != (u64)vsn.significand << 32);
- return vfp_single_normaliseround(state, sd, &vsd, fpscr, 0, "fdiv");
+ exceptions |= vfp_single_normaliseround(state, sd, &vsd, fpscr, "fdiv");
+ return exceptions;
vsn_nan:
- exceptions = vfp_propagate_nan(&vsd, &vsn, &vsm, fpscr);
+ exceptions |= vfp_propagate_nan(&vsd, &vsn, &vsm, fpscr);
pack:
vfp_put_float(state, vfp_single_pack(&vsd), sd);
return exceptions;
vsm_nan:
- exceptions = vfp_propagate_nan(&vsd, &vsm, &vsn, fpscr);
+ exceptions |= vfp_propagate_nan(&vsd, &vsm, &vsn, fpscr);
goto pack;
zero:
@@ -1180,7 +1213,7 @@ zero:
goto pack;
divzero:
- exceptions = FPSCR_DZC;
+ exceptions |= FPSCR_DZC;
infinity:
vsd.exponent = 255;
vsd.significand = 0;
@@ -1188,7 +1221,8 @@ infinity:
invalid:
vfp_put_float(state, vfp_single_pack(&vfp_single_default_qnan), sd);
- return FPSCR_IOC;
+ exceptions |= FPSCR_IOC;
+ return exceptions;
}
static struct op fops[] = {
diff --git a/src/core/file_sys/archive_backend.cpp b/src/core/file_sys/archive_backend.cpp
index 97adf0e12..cc0aa7022 100644
--- a/src/core/file_sys/archive_backend.cpp
+++ b/src/core/file_sys/archive_backend.cpp
@@ -19,22 +19,22 @@ Path::Path(LowPathType type, u32 size, u32 pointer) : type(type) {
switch (type) {
case Binary:
{
- u8* data = Memory::GetPointer(pointer);
- binary = std::vector<u8>(data, data + size);
+ binary.resize(size);
+ Memory::ReadBlock(pointer, binary.data(), binary.size());
break;
}
case Char:
{
- const char* data = reinterpret_cast<const char*>(Memory::GetPointer(pointer));
- string = std::string(data, size - 1); // Data is always null-terminated.
+ string.resize(size - 1); // Data is always null-terminated.
+ Memory::ReadBlock(pointer, &string[0], string.size());
break;
}
case Wchar:
{
- const char16_t* data = reinterpret_cast<const char16_t*>(Memory::GetPointer(pointer));
- u16str = std::u16string(data, size/2 - 1); // Data is always null-terminated.
+ u16str.resize(size / 2 - 1); // Data is always null-terminated.
+ Memory::ReadBlock(pointer, &u16str[0], u16str.size() * sizeof(char16_t));
break;
}
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
index 820b19e1a..28d403158 100644
--- a/src/core/gdbstub/gdbstub.cpp
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -646,7 +646,7 @@ static void ReadMemory() {
u8* data = Memory::GetPointer(addr);
if (!data) {
- return SendReply("E0");
+ return SendReply("E00");
}
MemToGdbHex(reply, data, len);
diff --git a/src/core/hle/applets/mii_selector.cpp b/src/core/hle/applets/mii_selector.cpp
index bf39eca22..77f01d208 100644
--- a/src/core/hle/applets/mii_selector.cpp
+++ b/src/core/hle/applets/mii_selector.cpp
@@ -32,9 +32,9 @@ ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& p
// The LibAppJustStarted message contains a buffer with the size of the framebuffer shared memory.
// Create the SharedMemory that will hold the framebuffer data
Service::APT::CaptureBufferInfo capture_info;
- ASSERT(sizeof(capture_info) == parameter.buffer_size);
+ ASSERT(sizeof(capture_info) == parameter.buffer.size());
- memcpy(&capture_info, parameter.data, sizeof(capture_info));
+ memcpy(&capture_info, parameter.buffer.data(), sizeof(capture_info));
using Kernel::MemoryPermission;
// Allocate a heap block of the required size for this applet.
@@ -47,8 +47,7 @@ ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& p
// Send the response message with the newly created SharedMemory
Service::APT::MessageParameter result;
result.signal = static_cast<u32>(Service::APT::SignalType::LibAppFinished);
- result.data = nullptr;
- result.buffer_size = 0;
+ result.buffer.clear();
result.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
result.sender_id = static_cast<u32>(id);
result.object = framebuffer_memory;
@@ -63,15 +62,17 @@ ResultCode MiiSelector::StartImpl(const Service::APT::AppletStartupParameter& pa
// TODO(Subv): Set the expected fields in the response buffer before resending it to the application.
// TODO(Subv): Reverse the parameter format for the Mii Selector
- if(parameter.buffer_size >= sizeof(u32)) {
- // TODO: defaults return no error, but garbage in other unknown fields
- memset(parameter.data, 0, sizeof(u32));
- }
+ memcpy(&config, parameter.buffer.data(), parameter.buffer.size());
+
+ // TODO(Subv): Find more about this structure, result code 0 is enough to let most games continue.
+ MiiResult result;
+ memset(&result, 0, sizeof(result));
+ result.result_code = 0;
// Let the application know that we're closing
Service::APT::MessageParameter message;
- message.buffer_size = parameter.buffer_size;
- message.data = parameter.data;
+ message.buffer.resize(sizeof(MiiResult));
+ std::memcpy(message.buffer.data(), &result, message.buffer.size());
message.signal = static_cast<u32>(Service::APT::SignalType::LibAppClosed);
message.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
message.sender_id = static_cast<u32>(id);
diff --git a/src/core/hle/applets/mii_selector.h b/src/core/hle/applets/mii_selector.h
index be6b04642..24e8e721d 100644
--- a/src/core/hle/applets/mii_selector.h
+++ b/src/core/hle/applets/mii_selector.h
@@ -24,7 +24,7 @@ struct MiiConfig {
u8 unk_004;
INSERT_PADDING_BYTES(3);
u16 unk_008;
- INSERT_PADDING_BYTES(0x8C - 0xA);
+ INSERT_PADDING_BYTES(0x82);
u8 unk_08C;
INSERT_PADDING_BYTES(3);
u16 unk_090;
@@ -75,6 +75,8 @@ public:
/// Whether this applet is currently running instead of the host application or not.
bool started;
+
+ MiiConfig config;
};
}
diff --git a/src/core/hle/applets/swkbd.cpp b/src/core/hle/applets/swkbd.cpp
index 90c6adc65..d87bf3d57 100644
--- a/src/core/hle/applets/swkbd.cpp
+++ b/src/core/hle/applets/swkbd.cpp
@@ -35,9 +35,9 @@ ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter con
// The LibAppJustStarted message contains a buffer with the size of the framebuffer shared memory.
// Create the SharedMemory that will hold the framebuffer data
Service::APT::CaptureBufferInfo capture_info;
- ASSERT(sizeof(capture_info) == parameter.buffer_size);
+ ASSERT(sizeof(capture_info) == parameter.buffer.size());
- memcpy(&capture_info, parameter.data, sizeof(capture_info));
+ memcpy(&capture_info, parameter.buffer.data(), sizeof(capture_info));
using Kernel::MemoryPermission;
// Allocate a heap block of the required size for this applet.
@@ -50,8 +50,7 @@ ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter con
// Send the response message with the newly created SharedMemory
Service::APT::MessageParameter result;
result.signal = static_cast<u32>(Service::APT::SignalType::LibAppFinished);
- result.data = nullptr;
- result.buffer_size = 0;
+ result.buffer.clear();
result.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
result.sender_id = static_cast<u32>(id);
result.object = framebuffer_memory;
@@ -61,9 +60,9 @@ ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter con
}
ResultCode SoftwareKeyboard::StartImpl(Service::APT::AppletStartupParameter const& parameter) {
- ASSERT_MSG(parameter.buffer_size == sizeof(config), "The size of the parameter (SoftwareKeyboardConfig) is wrong");
+ ASSERT_MSG(parameter.buffer.size() == sizeof(config), "The size of the parameter (SoftwareKeyboardConfig) is wrong");
- memcpy(&config, parameter.data, parameter.buffer_size);
+ memcpy(&config, parameter.buffer.data(), parameter.buffer.size());
text_memory = boost::static_pointer_cast<Kernel::SharedMemory, Kernel::Object>(parameter.object);
// TODO(Subv): Verify if this is the correct behavior
@@ -99,7 +98,7 @@ void SoftwareKeyboard::DrawScreenKeyboard() {
auto info = bottom_screen->framebuffer_info[bottom_screen->index];
// TODO(Subv): Draw the HLE keyboard, for now just zero-fill the framebuffer
- memset(Memory::GetPointer(info.address_left), 0, info.stride * 320);
+ Memory::ZeroBlock(info.address_left, info.stride * 320);
GSP_GPU::SetBufferSwap(1, info);
}
@@ -107,8 +106,8 @@ void SoftwareKeyboard::DrawScreenKeyboard() {
void SoftwareKeyboard::Finalize() {
// Let the application know that we're closing
Service::APT::MessageParameter message;
- message.buffer_size = sizeof(SoftwareKeyboardConfig);
- message.data = reinterpret_cast<u8*>(&config);
+ message.buffer.resize(sizeof(SoftwareKeyboardConfig));
+ std::memcpy(message.buffer.data(), &config, message.buffer.size());
message.signal = static_cast<u32>(Service::APT::SignalType::LibAppClosed);
message.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
message.sender_id = static_cast<u32>(id);
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 68f026918..3f6bec5fa 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -403,7 +403,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
priority = new_priority;
}
- if (!Memory::GetPointer(entry_point)) {
+ if (!Memory::IsValidVirtualAddress(entry_point)) {
LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name.c_str(), entry_point);
// TODO: Verify error
return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
@@ -526,6 +526,8 @@ SharedPtr<Thread> SetupMainThread(u32 entry_point, s32 priority) {
SharedPtr<Thread> thread = thread_res.MoveFrom();
+ thread->context.fpscr = FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO | FPSCR_ROUND_TOZERO | FPSCR_IXC; // 0x03C00010
+
// Run new "main" thread
SwitchContext(thread.get());
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index 1e289f38a..066146cff 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -7,6 +7,7 @@
#include "common/assert.h"
#include "core/hle/kernel/vm_manager.h"
+#include "core/memory.h"
#include "core/memory_setup.h"
#include "core/mmio.h"
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index bfb3327ce..57dedcb22 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -26,6 +26,7 @@ enum class ErrorDescription : u32 {
FS_NotAFile = 250,
FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive
OutofRangeOrMisalignedAddress = 513, // TODO(purpasmart): Check if this name fits its actual usage
+ GPU_FirstInitialization = 519,
FS_InvalidPath = 702,
InvalidSection = 1000,
TooLarge = 1001,
diff --git a/src/core/hle/service/act_a.cpp b/src/core/hle/service/act_a.cpp
new file mode 100644
index 000000000..3a775fa90
--- /dev/null
+++ b/src/core/hle/service/act_a.cpp
@@ -0,0 +1,26 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/act_a.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace ACT_A
+
+namespace ACT_A {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x041300C2, nullptr, "UpdateMiiImage"},
+ {0x041B0142, nullptr, "AgreeEula"},
+ {0x04210042, nullptr, "UploadMii"},
+ {0x04230082, nullptr, "ValidateMailAddress"},
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable);
+}
+
+} // namespace
diff --git a/src/core/hle/service/dlp_srvr.h b/src/core/hle/service/act_a.h
index d65d00814..765cae644 100644
--- a/src/core/hle/service/dlp_srvr.h
+++ b/src/core/hle/service/act_a.h
@@ -7,16 +7,16 @@
#include "core/hle/service/service.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
-// Namespace DLP_SRVR
+// Namespace ACT_A
-namespace DLP_SRVR {
+namespace ACT_A {
class Interface : public Service::Interface {
public:
Interface();
std::string GetPortName() const override {
- return "dlp:SRVR";
+ return "act:a";
}
};
diff --git a/src/core/hle/service/act_u.cpp b/src/core/hle/service/act_u.cpp
index b23d17fba..05de4d002 100644
--- a/src/core/hle/service/act_u.cpp
+++ b/src/core/hle/service/act_u.cpp
@@ -10,7 +10,10 @@
namespace ACT_U {
const Interface::FunctionInfo FunctionTable[] = {
+ {0x00010084, nullptr, "Initialize"},
+ {0x00020040, nullptr, "GetErrorCode"},
{0x000600C2, nullptr, "GetAccountDataBlock"},
+ {0x000D0040, nullptr, "GenerateUuid"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp
index 73fce6079..1e54a53dd 100644
--- a/src/core/hle/service/apt/apt.cpp
+++ b/src/core/hle/service/apt/apt.cpp
@@ -14,6 +14,7 @@
#include "core/hle/service/apt/apt_u.h"
#include "core/hle/service/apt/bcfnt/bcfnt.h"
#include "core/hle/service/fs/archive.h"
+#include "core/hle/service/ptm/ptm.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/mutex.h"
@@ -33,6 +34,9 @@ static Kernel::SharedPtr<Kernel::Event> parameter_event; ///< APT parameter even
static u32 cpu_percent; ///< CPU time available to the running application
+// APT::CheckNew3DSApp will check this unknown_ns_state_field to determine processing mode
+static u8 unknown_ns_state_field;
+
/// Parameter data to be returned in the next call to Glance/ReceiveParameter
static MessageParameter next_parameter;
@@ -176,12 +180,12 @@ void SendParameter(Service::Interface* self) {
}
MessageParameter param;
- param.buffer_size = buffer_size;
param.destination_id = dst_app_id;
param.sender_id = src_app_id;
param.object = Kernel::g_handle_table.GetGeneric(handle);
param.signal = signal_type;
- param.data = Memory::GetPointer(buffer);
+ param.buffer.resize(buffer_size);
+ Memory::ReadBlock(buffer, param.buffer.data(), param.buffer.size());
cmd_buff[1] = dest_applet->ReceiveParameter(param).raw;
@@ -199,16 +203,15 @@ void ReceiveParameter(Service::Interface* self) {
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
cmd_buff[2] = next_parameter.sender_id;
cmd_buff[3] = next_parameter.signal; // Signal type
- cmd_buff[4] = next_parameter.buffer_size; // Parameter buffer size
+ cmd_buff[4] = next_parameter.buffer.size(); // Parameter buffer size
cmd_buff[5] = 0x10;
cmd_buff[6] = 0;
if (next_parameter.object != nullptr)
cmd_buff[6] = Kernel::g_handle_table.Create(next_parameter.object).MoveFrom();
- cmd_buff[7] = (next_parameter.buffer_size << 14) | 2;
+ cmd_buff[7] = (next_parameter.buffer.size() << 14) | 2;
cmd_buff[8] = buffer;
- if (next_parameter.data)
- memcpy(Memory::GetPointer(buffer), next_parameter.data, std::min(buffer_size, next_parameter.buffer_size));
+ Memory::WriteBlock(buffer, next_parameter.buffer.data(), next_parameter.buffer.size());
LOG_WARNING(Service_APT, "called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size);
}
@@ -222,16 +225,15 @@ void GlanceParameter(Service::Interface* self) {
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
cmd_buff[2] = next_parameter.sender_id;
cmd_buff[3] = next_parameter.signal; // Signal type
- cmd_buff[4] = next_parameter.buffer_size; // Parameter buffer size
+ cmd_buff[4] = next_parameter.buffer.size(); // Parameter buffer size
cmd_buff[5] = 0x10;
cmd_buff[6] = 0;
if (next_parameter.object != nullptr)
cmd_buff[6] = Kernel::g_handle_table.Create(next_parameter.object).MoveFrom();
- cmd_buff[7] = (next_parameter.buffer_size << 14) | 2;
+ cmd_buff[7] = (next_parameter.buffer.size() << 14) | 2;
cmd_buff[8] = buffer;
- if (next_parameter.data)
- memcpy(Memory::GetPointer(buffer), next_parameter.data, std::min(buffer_size, next_parameter.buffer_size));
+ Memory::WriteBlock(buffer, next_parameter.buffer.data(), std::min(static_cast<size_t>(buffer_size), next_parameter.buffer.size()));
LOG_WARNING(Service_APT, "called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size);
}
@@ -258,6 +260,10 @@ void PrepareToStartApplication(Service::Interface* self) {
u32 title_info4 = cmd_buff[4];
u32 flags = cmd_buff[5];
+ if (flags & 0x00000100) {
+ unknown_ns_state_field = 1;
+ }
+
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
LOG_WARNING(Service_APT, "(STUBBED) called title_info1=0x%08X, title_info2=0x%08X, title_info3=0x%08X,"
@@ -365,14 +371,36 @@ void StartLibraryApplet(Service::Interface* self) {
return;
}
+ size_t buffer_size = cmd_buff[2];
+ VAddr buffer_addr = cmd_buff[6];
+
AppletStartupParameter parameter;
- parameter.buffer_size = cmd_buff[2];
parameter.object = Kernel::g_handle_table.GetGeneric(cmd_buff[4]);
- parameter.data = Memory::GetPointer(cmd_buff[6]);
+ parameter.buffer.resize(buffer_size);
+ Memory::ReadBlock(buffer_addr, parameter.buffer.data(), parameter.buffer.size());
cmd_buff[1] = applet->Start(parameter).raw;
}
+void SetNSStateField(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ unknown_ns_state_field = cmd_buff[1];
+
+ cmd_buff[0] = IPC::MakeHeader(0x55, 1, 0);
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ LOG_WARNING(Service_APT, "(STUBBED) unknown_ns_state_field=%u", unknown_ns_state_field);
+}
+
+void GetNSStateField(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ cmd_buff[0] = IPC::MakeHeader(0x56, 2, 0);
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ cmd_buff[8] = unknown_ns_state_field;
+ LOG_WARNING(Service_APT, "(STUBBED) unknown_ns_state_field=%u", unknown_ns_state_field);
+}
+
void GetAppletInfo(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
auto app_id = static_cast<AppletId>(cmd_buff[1]);
@@ -408,6 +436,29 @@ void GetStartupArgument(Service::Interface* self) {
cmd_buff[2] = (parameter_size > 0) ? 1 : 0;
}
+void CheckNew3DSApp(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ if (unknown_ns_state_field) {
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ cmd_buff[2] = 0;
+ } else {
+ PTM::CheckNew3DS(self);
+ }
+
+ cmd_buff[0] = IPC::MakeHeader(0x101, 2, 0);
+ LOG_WARNING(Service_APT, "(STUBBED) called");
+}
+
+void CheckNew3DS(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ PTM::CheckNew3DS(self);
+
+ cmd_buff[0] = IPC::MakeHeader(0x102, 2, 0);
+ LOG_WARNING(Service_APT, "(STUBBED) called");
+}
+
void Init() {
AddService(new APT_A_Interface);
AddService(new APT_S_Interface);
@@ -441,6 +492,7 @@ void Init() {
lock = Kernel::Mutex::Create(false, "APT_U:Lock");
cpu_percent = 0;
+ unknown_ns_state_field = 0;
// TODO(bunnei): Check if these are created in Initialize or on APT process startup.
notification_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT_U:Notification");
diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h
index 1a1034fcc..76b3a3807 100644
--- a/src/core/hle/service/apt/apt.h
+++ b/src/core/hle/service/apt/apt.h
@@ -20,16 +20,14 @@ struct MessageParameter {
u32 sender_id = 0;
u32 destination_id = 0;
u32 signal = 0;
- u32 buffer_size = 0;
Kernel::SharedPtr<Kernel::Object> object = nullptr;
- u8* data = nullptr;
+ std::vector<u8> buffer;
};
/// Holds information about the parameters used in StartLibraryApplet
struct AppletStartupParameter {
- u32 buffer_size = 0;
Kernel::SharedPtr<Kernel::Object> object = nullptr;
- u8* data = nullptr;
+ std::vector<u8> buffer;
};
/// Used by the application to pass information about the current framebuffer to applets.
@@ -376,6 +374,50 @@ void StartLibraryApplet(Service::Interface* self);
*/
void GetStartupArgument(Service::Interface* self);
+/**
+ * APT::SetNSStateField service function
+ * Inputs:
+ * 1 : u8 NS state field
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * Note:
+ * This writes the input u8 to a NS state field.
+ */
+void SetNSStateField(Service::Interface* self);
+
+/**
+ * APT::GetNSStateField service function
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 8 : u8 NS state field
+ * Note:
+ * This returns a u8 NS state field(which can be set by cmd 0x00550040), at cmdreply+8.
+ */
+void GetNSStateField(Service::Interface* self);
+
+/**
+ * APT::CheckNew3DSApp service function
+ * Outputs:
+ * 1: Result code, 0 on success, otherwise error code
+ * 2: u8 output: 0 = Old3DS, 1 = New3DS.
+ * Note:
+ * This uses PTMSYSM:CheckNew3DS.
+ * When a certain NS state field is non-zero, the output value is zero,
+ * Otherwise the output is from PTMSYSM:CheckNew3DS.
+ * Normally this NS state field is zero, however this state field is set to 1
+ * when APT:PrepareToStartApplication is used with flags bit8 is set.
+ */
+void CheckNew3DSApp(Service::Interface* self);
+
+/**
+ * Wrapper for PTMSYSM:CheckNew3DS
+ * APT::CheckNew3DS service function
+ * Outputs:
+ * 1: Result code, 0 on success, otherwise error code
+ * 2: u8 output: 0 = Old3DS, 1 = New3DS.
+ */
+void CheckNew3DS(Service::Interface* self);
+
/// Initialize the APT service
void Init();
diff --git a/src/core/hle/service/apt/apt_a.cpp b/src/core/hle/service/apt/apt_a.cpp
index 9ff47701a..223c0a8bd 100644
--- a/src/core/hle/service/apt/apt_a.cpp
+++ b/src/core/hle/service/apt/apt_a.cpp
@@ -21,6 +21,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x000D0080, ReceiveParameter, "ReceiveParameter"},
{0x000E0080, GlanceParameter, "GlanceParameter"},
{0x000F0100, CancelParameter, "CancelParameter"},
+ {0x00150140, PrepareToStartApplication, "PrepareToStartApplication"},
{0x00160040, PreloadLibraryApplet, "PreloadLibraryApplet"},
{0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"},
{0x001E0084, StartLibraryApplet, "StartLibraryApplet"},
@@ -32,7 +33,10 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x004F0080, SetAppCpuTimeLimit, "SetAppCpuTimeLimit"},
{0x00500040, GetAppCpuTimeLimit, "GetAppCpuTimeLimit"},
{0x00510080, GetStartupArgument, "GetStartupArgument"},
- {0x00550040, nullptr, "WriteInputToNsState?"},
+ {0x00550040, SetNSStateField, "SetNSStateField?"},
+ {0x00560000, GetNSStateField, "GetNSStateField?"},
+ {0x01010000, CheckNew3DSApp, "CheckNew3DSApp"},
+ {0x01020000, CheckNew3DS, "CheckNew3DS"}
};
APT_A_Interface::APT_A_Interface() {
diff --git a/src/core/hle/service/apt/apt_s.cpp b/src/core/hle/service/apt/apt_s.cpp
index ca54e593c..f5c52fa3d 100644
--- a/src/core/hle/service/apt/apt_s.cpp
+++ b/src/core/hle/service/apt/apt_s.cpp
@@ -29,7 +29,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00120040, nullptr, "SetHomeMenuAppletIdForDebug"},
{0x00130000, nullptr, "GetPreparationState"},
{0x00140040, nullptr, "SetPreparationState"},
- {0x00150140, nullptr, "PrepareToStartApplication"},
+ {0x00150140, PrepareToStartApplication, "PrepareToStartApplication"},
{0x00160040, PreloadLibraryApplet, "PreloadLibraryApplet"},
{0x00170040, nullptr, "FinishPreloadingLibraryApplet"},
{0x00180040, PrepareToStartLibraryApplet,"PrepareToStartLibraryApplet"},
@@ -92,9 +92,11 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00510080, GetStartupArgument, "GetStartupArgument"},
{0x00520104, nullptr, "Wrap1"},
{0x00530104, nullptr, "Unwrap1"},
+ {0x00550040, SetNSStateField, "SetNSStateField?" },
+ {0x00560000, GetNSStateField, "GetNSStateField?" },
{0x00580002, nullptr, "GetProgramID"},
- {0x01010000, nullptr, "CheckNew3DSApp"},
- {0x01020000, nullptr, "CheckNew3DS"}
+ {0x01010000, CheckNew3DSApp, "CheckNew3DSApp"},
+ {0x01020000, CheckNew3DS, "CheckNew3DS"}
};
APT_S_Interface::APT_S_Interface() {
diff --git a/src/core/hle/service/apt/apt_u.cpp b/src/core/hle/service/apt/apt_u.cpp
index 0e85c6d08..0e60bd34f 100644
--- a/src/core/hle/service/apt/apt_u.cpp
+++ b/src/core/hle/service/apt/apt_u.cpp
@@ -29,7 +29,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00120040, nullptr, "SetHomeMenuAppletIdForDebug"},
{0x00130000, nullptr, "GetPreparationState"},
{0x00140040, nullptr, "SetPreparationState"},
- {0x00150140, nullptr, "PrepareToStartApplication"},
+ {0x00150140, PrepareToStartApplication, "PrepareToStartApplication"},
{0x00160040, PreloadLibraryApplet, "PreloadLibraryApplet"},
{0x00170040, nullptr, "FinishPreloadingLibraryApplet"},
{0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"},
@@ -92,9 +92,11 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00510080, GetStartupArgument, "GetStartupArgument"},
{0x00520104, nullptr, "Wrap1"},
{0x00530104, nullptr, "Unwrap1"},
+ {0x00550040, SetNSStateField, "SetNSStateField?"},
+ {0x00560000, GetNSStateField, "GetNSStateField?"},
{0x00580002, nullptr, "GetProgramID"},
- {0x01010000, nullptr, "CheckNew3DSApp"},
- {0x01020000, nullptr, "CheckNew3DS"}
+ {0x01010000, CheckNew3DSApp, "CheckNew3DSApp"},
+ {0x01020000, CheckNew3DS, "CheckNew3DS"}
};
APT_U_Interface::APT_U_Interface() {
diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp
index b9322c55d..e067db645 100644
--- a/src/core/hle/service/cfg/cfg.cpp
+++ b/src/core/hle/service/cfg/cfg.cpp
@@ -47,6 +47,12 @@ struct UsernameBlock {
};
static_assert(sizeof(UsernameBlock) == 0x1C, "UsernameBlock must be exactly 0x1C bytes");
+struct BirthdayBlock {
+ u8 month; ///< The month of the birthday
+ u8 day; ///< The day of the birthday
+};
+static_assert(sizeof(BirthdayBlock) == 2, "BirthdayBlock must be exactly 2 bytes");
+
struct ConsoleModelInfo {
u8 model; ///< The console model (3DS, 2DS, etc)
u8 unknown[3]; ///< Unknown data
@@ -65,9 +71,8 @@ static const u64 CFG_SAVE_ID = 0x00010017;
static const u64 CONSOLE_UNIQUE_ID = 0xDEADC0DE;
static const ConsoleModelInfo CONSOLE_MODEL = { NINTENDO_3DS_XL, { 0, 0, 0 } };
static const u8 CONSOLE_LANGUAGE = LANGUAGE_EN;
-static const char CONSOLE_USERNAME[0x14] = "CITRA";
-/// This will be initialized in Init, and will be used when creating the block
-static UsernameBlock CONSOLE_USERNAME_BLOCK;
+static const UsernameBlock CONSOLE_USERNAME_BLOCK = { u"CITRA", 0, 0 };
+static const BirthdayBlock PROFILE_BIRTHDAY = { 3, 25 }; // March 25th, 2014
/// TODO(Subv): Find out what this actually is
static const u8 SOUND_OUTPUT_MODE = 2;
static const u8 UNITED_STATES_COUNTRY_ID = 49;
@@ -191,28 +196,32 @@ void GetConfigInfoBlk2(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
u32 size = cmd_buff[1];
u32 block_id = cmd_buff[2];
- u8* data_pointer = Memory::GetPointer(cmd_buff[4]);
+ VAddr data_pointer = cmd_buff[4];
- if (data_pointer == nullptr) {
+ if (!Memory::IsValidVirtualAddress(data_pointer)) {
cmd_buff[1] = -1; // TODO(Subv): Find the right error code
return;
}
- cmd_buff[1] = Service::CFG::GetConfigInfoBlock(block_id, size, 0x2, data_pointer).raw;
+ std::vector<u8> data(size);
+ cmd_buff[1] = Service::CFG::GetConfigInfoBlock(block_id, size, 0x2, data.data()).raw;
+ Memory::WriteBlock(data_pointer, data.data(), data.size());
}
void GetConfigInfoBlk8(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
u32 size = cmd_buff[1];
u32 block_id = cmd_buff[2];
- u8* data_pointer = Memory::GetPointer(cmd_buff[4]);
+ VAddr data_pointer = cmd_buff[4];
- if (data_pointer == nullptr) {
+ if (!Memory::IsValidVirtualAddress(data_pointer)) {
cmd_buff[1] = -1; // TODO(Subv): Find the right error code
return;
}
- cmd_buff[1] = Service::CFG::GetConfigInfoBlock(block_id, size, 0x8, data_pointer).raw;
+ std::vector<u8> data(size);
+ cmd_buff[1] = Service::CFG::GetConfigInfoBlock(block_id, size, 0x8, data.data()).raw;
+ Memory::WriteBlock(data_pointer, data.data(), data.size());
}
void UpdateConfigNANDSavegame(Service::Interface* self) {
@@ -329,32 +338,22 @@ ResultCode FormatConfig() {
res = CreateConfigInfoBlk(0x00050005, sizeof(STEREO_CAMERA_SETTINGS), 0xE, STEREO_CAMERA_SETTINGS.data());
if (!res.IsSuccess()) return res;
+
res = CreateConfigInfoBlk(0x00070001, sizeof(SOUND_OUTPUT_MODE), 0xE, &SOUND_OUTPUT_MODE);
if (!res.IsSuccess()) return res;
+
res = CreateConfigInfoBlk(0x00090001, sizeof(CONSOLE_UNIQUE_ID), 0xE, &CONSOLE_UNIQUE_ID);
if (!res.IsSuccess()) return res;
- res = CreateConfigInfoBlk(0x000A0000, sizeof(CONSOLE_USERNAME_BLOCK), 0xE, &CONSOLE_USERNAME_BLOCK);
- if (!res.IsSuccess()) return res;
- // 0x000A0000 - Profile username
- struct {
- u16_le username[10];
- u8 unused[4];
- u32_le wordfilter_version; // Unused by Citra
- } profile_username = {};
-
- std::u16string username_string = Common::UTF8ToUTF16("Citra");
- std::copy(username_string.cbegin(), username_string.cend(), profile_username.username);
- res = CreateConfigInfoBlk(0x000A0000, sizeof(profile_username), 0xE, &profile_username);
+ res = CreateConfigInfoBlk(0x000A0000, sizeof(CONSOLE_USERNAME_BLOCK), 0xE, &CONSOLE_USERNAME_BLOCK);
if (!res.IsSuccess()) return res;
- // 0x000A0001 - Profile birthday
- const u8 profile_birthday[2] = {3, 25}; // March 25th, 2014
- res = CreateConfigInfoBlk(0x000A0001, sizeof(profile_birthday), 0xE, profile_birthday);
+ res = CreateConfigInfoBlk(0x000A0001, sizeof(PROFILE_BIRTHDAY), 0xE, &PROFILE_BIRTHDAY);
if (!res.IsSuccess()) return res;
res = CreateConfigInfoBlk(0x000A0002, sizeof(CONSOLE_LANGUAGE), 0xE, &CONSOLE_LANGUAGE);
if (!res.IsSuccess()) return res;
+
res = CreateConfigInfoBlk(0x000B0000, sizeof(COUNTRY_INFO), 0xE, &COUNTRY_INFO);
if (!res.IsSuccess()) return res;
@@ -435,17 +434,6 @@ void Init() {
return;
}
- // Initialize the Username block
- // TODO(Subv): Initialize this directly in the variable when MSVC supports char16_t string literals
- memset(&CONSOLE_USERNAME_BLOCK, 0, sizeof(CONSOLE_USERNAME_BLOCK));
- CONSOLE_USERNAME_BLOCK.ng_word = 0;
- CONSOLE_USERNAME_BLOCK.zero = 0;
-
- // Copy string to buffer and pad with zeros at the end
- auto size = Common::UTF8ToUTF16(CONSOLE_USERNAME).copy(CONSOLE_USERNAME_BLOCK.username, 0x14);
- std::fill(std::begin(CONSOLE_USERNAME_BLOCK.username) + size,
- std::end(CONSOLE_USERNAME_BLOCK.username), 0);
-
FormatConfig();
}
diff --git a/src/core/hle/service/dlp/dlp.cpp b/src/core/hle/service/dlp/dlp.cpp
new file mode 100644
index 000000000..7c8db794b
--- /dev/null
+++ b/src/core/hle/service/dlp/dlp.cpp
@@ -0,0 +1,24 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/service.h"
+#include "core/hle/service/dlp/dlp.h"
+#include "core/hle/service/dlp/dlp_clnt.h"
+#include "core/hle/service/dlp/dlp_fkcl.h"
+#include "core/hle/service/dlp/dlp_srvr.h"
+
+namespace Service {
+namespace DLP {
+
+void Init() {
+ AddService(new DLP_CLNT_Interface);
+ AddService(new DLP_FKCL_Interface);
+ AddService(new DLP_SRVR_Interface);
+}
+
+void Shutdown() {
+}
+
+} // namespace DLP
+} // namespace Service
diff --git a/src/core/hle/service/dlp/dlp.h b/src/core/hle/service/dlp/dlp.h
new file mode 100644
index 000000000..ec2fe46e8
--- /dev/null
+++ b/src/core/hle/service/dlp/dlp.h
@@ -0,0 +1,15 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+namespace Service {
+namespace DLP {
+
+/// Initializes the DLP services.
+void Init();
+
+/// Shuts down the DLP services.
+void Shutdown();
+
+} // namespace DLP
+} // namespace Service
diff --git a/src/core/hle/service/dlp/dlp_clnt.cpp b/src/core/hle/service/dlp/dlp_clnt.cpp
new file mode 100644
index 000000000..0b31d47df
--- /dev/null
+++ b/src/core/hle/service/dlp/dlp_clnt.cpp
@@ -0,0 +1,20 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/dlp/dlp_clnt.h"
+
+namespace Service {
+namespace DLP {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x000100C3, nullptr, "Initialize"},
+ {0x00110000, nullptr, "GetWirelessRebootPassphrase"},
+};
+
+DLP_CLNT_Interface::DLP_CLNT_Interface() {
+ Register(FunctionTable);
+}
+
+} // namespace DLP
+} // namespace Service
diff --git a/src/core/hle/service/dlp/dlp_clnt.h b/src/core/hle/service/dlp/dlp_clnt.h
new file mode 100644
index 000000000..067f11e37
--- /dev/null
+++ b/src/core/hle/service/dlp/dlp_clnt.h
@@ -0,0 +1,22 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Service {
+namespace DLP {
+
+class DLP_CLNT_Interface final : public Interface {
+public:
+ DLP_CLNT_Interface();
+
+ std::string GetPortName() const override {
+ return "dlp:CLNT";
+ }
+};
+
+} // namespace DLP
+} // namespace Service
diff --git a/src/core/hle/service/dlp/dlp_fkcl.cpp b/src/core/hle/service/dlp/dlp_fkcl.cpp
new file mode 100644
index 000000000..a845260e5
--- /dev/null
+++ b/src/core/hle/service/dlp/dlp_fkcl.cpp
@@ -0,0 +1,20 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/dlp/dlp_fkcl.h"
+
+namespace Service {
+namespace DLP {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x00010083, nullptr, "Initialize"},
+ {0x000F0000, nullptr, "GetWirelessRebootPassphrase"},
+};
+
+DLP_FKCL_Interface::DLP_FKCL_Interface() {
+ Register(FunctionTable);
+}
+
+} // namespace DLP
+} // namespace Service
diff --git a/src/core/hle/service/dlp/dlp_fkcl.h b/src/core/hle/service/dlp/dlp_fkcl.h
new file mode 100644
index 000000000..e4837a167
--- /dev/null
+++ b/src/core/hle/service/dlp/dlp_fkcl.h
@@ -0,0 +1,22 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Service {
+namespace DLP {
+
+class DLP_FKCL_Interface final : public Interface {
+public:
+ DLP_FKCL_Interface();
+
+ std::string GetPortName() const override {
+ return "dlp:FKCL";
+ }
+};
+
+} // namespace DLP
+} // namespace Service
diff --git a/src/core/hle/service/dlp_srvr.cpp b/src/core/hle/service/dlp/dlp_srvr.cpp
index 1f30188da..da9b30f56 100644
--- a/src/core/hle/service/dlp_srvr.cpp
+++ b/src/core/hle/service/dlp/dlp_srvr.cpp
@@ -2,16 +2,15 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "common/common_types.h"
#include "common/logging/log.h"
-#include "core/hle/hle.h"
-#include "core/hle/service/dlp_srvr.h"
+#include "core/hle/result.h"
+#include "core/hle/service/dlp/dlp_srvr.h"
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Namespace DLP_SRVR
+namespace Service {
+namespace DLP {
-namespace DLP_SRVR {
-
-static void unk_0x000E0040(Service::Interface* self) {
+static void unk_0x000E0040(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
cmd_buff[1] = RESULT_SUCCESS.raw;
@@ -23,14 +22,13 @@ static void unk_0x000E0040(Service::Interface* self) {
const Interface::FunctionInfo FunctionTable[] = {
{0x00010183, nullptr, "Initialize"},
{0x00020000, nullptr, "Finalize"},
+ {0x000800C0, nullptr, "SendWirelessRebootPassphrase"},
{0x000E0040, unk_0x000E0040, "unk_0x000E0040"},
};
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Interface class
-
-Interface::Interface() {
+DLP_SRVR_Interface::DLP_SRVR_Interface() {
Register(FunctionTable);
}
-} // namespace
+} // namespace DLP
+} // namespace Service
diff --git a/src/core/hle/service/dlp/dlp_srvr.h b/src/core/hle/service/dlp/dlp_srvr.h
new file mode 100644
index 000000000..19fe17840
--- /dev/null
+++ b/src/core/hle/service/dlp/dlp_srvr.h
@@ -0,0 +1,22 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Service {
+namespace DLP {
+
+class DLP_SRVR_Interface final : public Interface {
+public:
+ DLP_SRVR_Interface();
+
+ std::string GetPortName() const override {
+ return "dlp:SRVR";
+ }
+};
+
+} // namespace DLP
+} // namespace Service
diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp
index 274fc751a..c8aadd9db 100644
--- a/src/core/hle/service/dsp_dsp.cpp
+++ b/src/core/hle/service/dsp_dsp.cpp
@@ -140,12 +140,15 @@ static void LoadComponent(Service::Interface* self) {
// TODO(bunnei): Implement real DSP firmware loading
- ASSERT(Memory::GetPointer(buffer) != nullptr);
- ASSERT(size > 0x37C);
+ ASSERT(Memory::IsValidVirtualAddress(buffer));
+
+ std::vector<u8> component_data(size);
+ Memory::ReadBlock(buffer, component_data.data(), component_data.size());
- LOG_INFO(Service_DSP, "Firmware hash: %#" PRIx64, Common::ComputeHash64(Memory::GetPointer(buffer), size));
+ LOG_INFO(Service_DSP, "Firmware hash: %#" PRIx64, Common::ComputeHash64(component_data.data(), component_data.size()));
// Some versions of the firmware have the location of DSP structures listed here.
- LOG_INFO(Service_DSP, "Structures hash: %#" PRIx64, Common::ComputeHash64(Memory::GetPointer(buffer) + 0x340, 60));
+ ASSERT(size > 0x37C);
+ LOG_INFO(Service_DSP, "Structures hash: %#" PRIx64, Common::ComputeHash64(component_data.data() + 0x340, 60));
LOG_WARNING(Service_DSP, "(STUBBED) called size=0x%X, prog_mask=0x%08X, data_mask=0x%08X, buffer=0x%08X",
size, prog_mask, data_mask, buffer);
@@ -285,7 +288,7 @@ static void WriteProcessPipe(Service::Interface* self) {
return;
}
- ASSERT_MSG(Memory::GetPointer(buffer) != nullptr, "Invalid Buffer: pipe=%u, size=0x%X, buffer=0x%08X", pipe_index, size, buffer);
+ ASSERT_MSG(Memory::IsValidVirtualAddress(buffer), "Invalid Buffer: pipe=%u, size=0x%X, buffer=0x%08X", pipe, size, buffer);
std::vector<u8> message(size);
for (u32 i = 0; i < size; i++) {
@@ -324,7 +327,7 @@ static void ReadPipeIfPossible(Service::Interface* self) {
DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(pipe_index);
- ASSERT_MSG(Memory::GetPointer(addr) != nullptr, "Invalid addr: pipe=%u, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe_index, unknown, size, addr);
+ ASSERT_MSG(Memory::IsValidVirtualAddress(addr), "Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe, unknown, size, addr);
cmd_buff[0] = IPC::MakeHeader(0x10, 1, 2);
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
@@ -364,7 +367,7 @@ static void ReadPipe(Service::Interface* self) {
DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(pipe_index);
- ASSERT_MSG(Memory::GetPointer(addr) != nullptr, "Invalid addr: pipe=%u, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe_index, unknown, size, addr);
+ ASSERT_MSG(Memory::IsValidVirtualAddress(addr), "Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe, unknown, size, addr);
if (DSP::HLE::GetPipeReadableSize(pipe) >= size) {
std::vector<u8> response = DSP::HLE::PipeRead(pipe, size);
@@ -440,9 +443,9 @@ static void GetHeadphoneStatus(Service::Interface* self) {
cmd_buff[0] = IPC::MakeHeader(0x1F, 2, 0);
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
- cmd_buff[2] = 0; // Not using headphones?
+ cmd_buff[2] = 0; // Not using headphones
- LOG_WARNING(Service_DSP, "(STUBBED) called");
+ LOG_DEBUG(Service_DSP, "called");
}
/**
diff --git a/src/core/hle/service/frd/frd.cpp b/src/core/hle/service/frd/frd.cpp
index 15d604bb6..29d144365 100644
--- a/src/core/hle/service/frd/frd.cpp
+++ b/src/core/hle/service/frd/frd.cpp
@@ -23,7 +23,7 @@ void GetMyPresence(Service::Interface* self) {
ASSERT(shifted_out_size == ((sizeof(MyPresence) << 14) | 2));
- Memory::WriteBlock(my_presence_addr, reinterpret_cast<const u8*>(&my_presence), sizeof(MyPresence));
+ Memory::WriteBlock(my_presence_addr, &my_presence, sizeof(MyPresence));
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
@@ -39,8 +39,7 @@ void GetFriendKeyList(Service::Interface* self) {
FriendKey zero_key = {};
for (u32 i = 0; i < frd_count; ++i) {
- Memory::WriteBlock(frd_key_addr + i * sizeof(FriendKey),
- reinterpret_cast<const u8*>(&zero_key), sizeof(FriendKey));
+ Memory::WriteBlock(frd_key_addr + i * sizeof(FriendKey), &zero_key, sizeof(FriendKey));
}
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
@@ -58,8 +57,7 @@ void GetFriendProfile(Service::Interface* self) {
Profile zero_profile = {};
for (u32 i = 0; i < count; ++i) {
- Memory::WriteBlock(profiles_addr + i * sizeof(Profile),
- reinterpret_cast<const u8*>(&zero_profile), sizeof(Profile));
+ Memory::WriteBlock(profiles_addr + i * sizeof(Profile), &zero_profile, sizeof(Profile));
}
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
@@ -88,7 +86,7 @@ void GetMyFriendKey(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
- Memory::WriteBlock(cmd_buff[2], reinterpret_cast<const u8*>(&my_friend_key), sizeof(FriendKey));
+ Memory::WriteBlock(cmd_buff[2], &my_friend_key, sizeof(FriendKey));
LOG_WARNING(Service_FRD, "(STUBBED) called");
}
diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp
index cc51ede0c..81b9abe4c 100644
--- a/src/core/hle/service/fs/archive.cpp
+++ b/src/core/hle/service/fs/archive.cpp
@@ -108,13 +108,14 @@ ResultVal<bool> File::SyncRequest() {
offset, length, backend->GetSize());
}
- ResultVal<size_t> read = backend->Read(offset, length, Memory::GetPointer(address));
+ std::vector<u8> data(length);
+ ResultVal<size_t> read = backend->Read(offset, data.size(), data.data());
if (read.Failed()) {
cmd_buff[1] = read.Code().raw;
return read.Code();
}
+ Memory::WriteBlock(address, data.data(), *read);
cmd_buff[2] = static_cast<u32>(*read);
- Memory::RasterizerFlushAndInvalidateRegion(Memory::VirtualToPhysicalAddress(address), length);
break;
}
@@ -128,7 +129,9 @@ ResultVal<bool> File::SyncRequest() {
LOG_TRACE(Service_FS, "Write %s %s: offset=0x%llx length=%d address=0x%x, flush=0x%x",
GetTypeName().c_str(), GetName().c_str(), offset, length, address, flush);
- ResultVal<size_t> written = backend->Write(offset, length, flush != 0, Memory::GetPointer(address));
+ std::vector<u8> data(length);
+ Memory::ReadBlock(address, data.data(), data.size());
+ ResultVal<size_t> written = backend->Write(offset, data.size(), flush != 0, data.data());
if (written.Failed()) {
cmd_buff[1] = written.Code().raw;
return written.Code();
@@ -216,12 +219,14 @@ ResultVal<bool> Directory::SyncRequest() {
{
u32 count = cmd_buff[1];
u32 address = cmd_buff[3];
- auto entries = reinterpret_cast<FileSys::Entry*>(Memory::GetPointer(address));
+ std::vector<FileSys::Entry> entries(count);
LOG_TRACE(Service_FS, "Read %s %s: count=%d",
GetTypeName().c_str(), GetName().c_str(), count);
// Number of entries actually read
- cmd_buff[2] = backend->Read(count, entries);
+ u32 read = backend->Read(entries.size(), entries.data());
+ cmd_buff[2] = read;
+ Memory::WriteBlock(address, entries.data(), read * sizeof(FileSys::Entry));
break;
}
@@ -456,11 +461,12 @@ ResultCode CreateExtSaveData(MediaType media_type, u32 high, u32 low, VAddr icon
if (result.IsError())
return result;
- u8* smdh_icon = Memory::GetPointer(icon_buffer);
- if (!smdh_icon)
+ if (!Memory::IsValidVirtualAddress(icon_buffer))
return ResultCode(-1); // TODO(Subv): Find the right error code
- ext_savedata->WriteIcon(path, smdh_icon, icon_size);
+ std::vector<u8> smdh_icon(icon_size);
+ Memory::ReadBlock(icon_buffer, smdh_icon.data(), smdh_icon.size());
+ ext_savedata->WriteIcon(path, smdh_icon.data(), smdh_icon.size());
return RESULT_SUCCESS;
}
diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp
index 8ded9b09b..ec565f46d 100644
--- a/src/core/hle/service/gsp_gpu.cpp
+++ b/src/core/hle/service/gsp_gpu.cpp
@@ -44,7 +44,7 @@ Kernel::SharedPtr<Kernel::SharedMemory> g_shared_memory;
u32 g_thread_id = 0;
static bool gpu_right_acquired = false;
-
+static bool first_initialization = true;
/// Gets a pointer to a thread command buffer in GSP shared memory
static inline u8* GetCommandBuffer(u32 thread_id) {
return g_shared_memory->GetPointer(0x800 + (thread_id * sizeof(CommandBuffer)));
@@ -66,14 +66,26 @@ static inline InterruptRelayQueue* GetInterruptRelayQueue(u32 thread_id) {
}
/**
+ * Writes a single GSP GPU hardware registers with a single u32 value
+ * (For internal use.)
+ *
+ * @param base_address The address of the register in question
+ * @param data Data to be written
+ */
+static void WriteSingleHWReg(u32 base_address, u32 data) {
+ DEBUG_ASSERT_MSG((base_address & 3) == 0 && base_address < 0x420000, "Write address out of range or misaligned");
+ HW::Write<u32>(base_address + REGS_BEGIN, data);
+}
+
+/**
* Writes sequential GSP GPU hardware registers using an array of source data
*
* @param base_address The address of the first register in the sequence
* @param size_in_bytes The number of registers to update (size of data)
- * @param data A pointer to the source data
+ * @param data_vaddr A pointer to the source data
* @return RESULT_SUCCESS if the parameters are valid, error code otherwise
*/
-static ResultCode WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) {
+static ResultCode WriteHWRegs(u32 base_address, u32 size_in_bytes, VAddr data_vaddr) {
// This magic number is verified to be done by the gsp module
const u32 max_size_in_bytes = 0x80;
@@ -87,10 +99,10 @@ static ResultCode WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* da
return ERR_GSP_REGS_MISALIGNED;
} else {
while (size_in_bytes > 0) {
- HW::Write<u32>(base_address + REGS_BEGIN, *data);
+ WriteSingleHWReg(base_address, Memory::Read32(data_vaddr));
size_in_bytes -= 4;
- ++data;
+ data_vaddr += 4;
base_address += 4;
}
return RESULT_SUCCESS;
@@ -112,7 +124,7 @@ static ResultCode WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* da
* @param masks A pointer to the masks
* @return RESULT_SUCCESS if the parameters are valid, error code otherwise
*/
-static ResultCode WriteHWRegsWithMask(u32 base_address, u32 size_in_bytes, const u32* data, const u32* masks) {
+static ResultCode WriteHWRegsWithMask(u32 base_address, u32 size_in_bytes, VAddr data_vaddr, VAddr masks_vaddr) {
// This magic number is verified to be done by the gsp module
const u32 max_size_in_bytes = 0x80;
@@ -131,14 +143,17 @@ static ResultCode WriteHWRegsWithMask(u32 base_address, u32 size_in_bytes, const
u32 reg_value;
HW::Read<u32>(reg_value, reg_address);
+ u32 data = Memory::Read32(data_vaddr);
+ u32 mask = Memory::Read32(masks_vaddr);
+
// Update the current value of the register only for set mask bits
- reg_value = (reg_value & ~*masks) | (*data | *masks);
+ reg_value = (reg_value & ~mask) | (data | mask);
- HW::Write<u32>(reg_address, reg_value);
+ WriteSingleHWReg(base_address, reg_value);
size_in_bytes -= 4;
- ++data;
- ++masks;
+ data_vaddr += 4;
+ masks_vaddr += 4;
base_address += 4;
}
return RESULT_SUCCESS;
@@ -164,8 +179,7 @@ static void WriteHWRegs(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
u32 reg_addr = cmd_buff[1];
u32 size = cmd_buff[2];
-
- u32* src = (u32*)Memory::GetPointer(cmd_buff[4]);
+ VAddr src = cmd_buff[4];
cmd_buff[1] = WriteHWRegs(reg_addr, size, src).raw;
}
@@ -186,8 +200,8 @@ static void WriteHWRegsWithMask(Service::Interface* self) {
u32 reg_addr = cmd_buff[1];
u32 size = cmd_buff[2];
- u32* src_data = (u32*)Memory::GetPointer(cmd_buff[4]);
- u32* mask_data = (u32*)Memory::GetPointer(cmd_buff[6]);
+ VAddr src_data = cmd_buff[4];
+ VAddr mask_data = cmd_buff[6];
cmd_buff[1] = WriteHWRegsWithMask(reg_addr, size, src_data, mask_data).raw;
}
@@ -210,13 +224,16 @@ static void ReadHWRegs(Service::Interface* self) {
return;
}
- u32* dst = (u32*)Memory::GetPointer(cmd_buff[0x41]);
+ VAddr dst_vaddr = cmd_buff[0x41];
while (size > 0) {
- HW::Read<u32>(*dst, reg_addr + REGS_BEGIN);
+ u32 value;
+ HW::Read<u32>(value, reg_addr + REGS_BEGIN);
+
+ Memory::Write32(dst_vaddr, value);
size -= 4;
- ++dst;
+ dst_vaddr += 4;
reg_addr += 4;
}
}
@@ -226,22 +243,22 @@ ResultCode SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) {
PAddr phys_address_left = Memory::VirtualToPhysicalAddress(info.address_left);
PAddr phys_address_right = Memory::VirtualToPhysicalAddress(info.address_right);
if (info.active_fb == 0) {
- WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_left1)),
- 4, &phys_address_left);
- WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_right1)),
- 4, &phys_address_right);
+ WriteSingleHWReg(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_left1)),
+ phys_address_left);
+ WriteSingleHWReg(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_right1)),
+ phys_address_right);
} else {
- WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_left2)),
- 4, &phys_address_left);
- WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_right2)),
- 4, &phys_address_right);
+ WriteSingleHWReg(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_left2)),
+ phys_address_left);
+ WriteSingleHWReg(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_right2)),
+ phys_address_right);
}
- WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].stride)),
- 4, &info.stride);
- WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].color_format)),
- 4, &info.format);
- WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].active_fb)),
- 4, &info.shown_fb);
+ WriteSingleHWReg(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].stride)),
+ info.stride);
+ WriteSingleHWReg(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].color_format)),
+ info.format);
+ WriteSingleHWReg(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].active_fb)),
+ info.shown_fb);
if (Pica::g_debug_context)
Pica::g_debug_context->OnEvent(Pica::DebugContext::Event::BufferSwapped, nullptr);
@@ -330,24 +347,25 @@ static void RegisterInterruptRelayQueue(Service::Interface* self) {
u32 flags = cmd_buff[1];
g_interrupt_event = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[3]);
+ // TODO(mailwl): return right error code instead assert
ASSERT_MSG((g_interrupt_event != nullptr), "handle is not valid!");
g_interrupt_event->name = "GSP_GPU::interrupt_event";
- using Kernel::MemoryPermission;
- g_shared_memory = Kernel::SharedMemory::Create(nullptr, 0x1000,
- MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
- 0, Kernel::MemoryRegion::BASE, "GSP:SharedMemory");
-
- Handle shmem_handle = Kernel::g_handle_table.Create(g_shared_memory).MoveFrom();
-
- // This specific code is required for a successful initialization, rather than 0
- cmd_buff[1] = ResultCode((ErrorDescription)519, ErrorModule::GX,
- ErrorSummary::Success, ErrorLevel::Success).raw;
+ if (first_initialization) {
+ // This specific code is required for a successful initialization, rather than 0
+ first_initialization = false;
+ cmd_buff[1] = ResultCode(ErrorDescription::GPU_FirstInitialization, ErrorModule::GX,
+ ErrorSummary::Success, ErrorLevel::Success).raw;
+ } else {
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ }
cmd_buff[2] = g_thread_id++; // Thread ID
- cmd_buff[4] = shmem_handle; // GSP shared memory
+ cmd_buff[4] = Kernel::g_handle_table.Create(g_shared_memory).MoveFrom(); // GSP shared memory
g_interrupt_event->Signal(); // TODO(bunnei): Is this correct?
+
+ LOG_WARNING(Service_GSP, "called, flags=0x%08X", flags);
}
/**
@@ -358,12 +376,12 @@ static void RegisterInterruptRelayQueue(Service::Interface* self) {
static void UnregisterInterruptRelayQueue(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
- g_shared_memory = nullptr;
+ g_thread_id = 0;
g_interrupt_event = nullptr;
cmd_buff[1] = RESULT_SUCCESS.raw;
- LOG_WARNING(Service_GSP, "called");
+ LOG_WARNING(Service_GSP, "(STUBBED) called");
}
/**
@@ -432,9 +450,9 @@ static void ExecuteCommand(const Command& command, u32 thread_id) {
Memory::RasterizerFlushAndInvalidateRegion(Memory::VirtualToPhysicalAddress(command.dma_request.dest_address),
command.dma_request.size);
- memcpy(Memory::GetPointer(command.dma_request.dest_address),
- Memory::GetPointer(command.dma_request.source_address),
- command.dma_request.size);
+ // TODO(Subv): These memory accesses should not go through the application's memory mapping.
+ // They should go through the GSP module's memory mapping.
+ Memory::CopyBlock(command.dma_request.dest_address, command.dma_request.source_address, command.dma_request.size);
SignalInterrupt(InterruptId::DMA);
break;
}
@@ -701,10 +719,15 @@ Interface::Interface() {
Register(FunctionTable);
g_interrupt_event = nullptr;
- g_shared_memory = nullptr;
+
+ using Kernel::MemoryPermission;
+ g_shared_memory = Kernel::SharedMemory::Create(nullptr, 0x1000,
+ MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
+ 0, Kernel::MemoryRegion::BASE, "GSP:SharedMemory");
g_thread_id = 0;
gpu_right_acquired = false;
+ first_initialization = true;
}
Interface::~Interface() {
diff --git a/src/core/hle/service/ptm/ptm.cpp b/src/core/hle/service/ptm/ptm.cpp
index 94f494690..e2c17d93b 100644
--- a/src/core/hle/service/ptm/ptm.cpp
+++ b/src/core/hle/service/ptm/ptm.cpp
@@ -3,7 +3,7 @@
// Refer to the license.txt file included.
#include "common/logging/log.h"
-
+#include "core/settings.h"
#include "core/file_sys/file_backend.h"
#include "core/hle/service/fs/archive.h"
#include "core/hle/service/ptm/ptm.h"
@@ -89,6 +89,20 @@ void IsLegacyPowerOff(Service::Interface* self) {
LOG_WARNING(Service_PTM, "(STUBBED) called");
}
+void CheckNew3DS(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ const bool is_new_3ds = Settings::values.is_new_3ds;
+
+ if (is_new_3ds) {
+ LOG_CRITICAL(Service_PTM, "The option 'is_new_3ds' is enabled as part of the 'System' settings. Citra does not fully support New 3DS emulation yet!");
+ }
+
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ cmd_buff[2] = is_new_3ds ? 1 : 0;
+
+ LOG_WARNING(Service_PTM, "(STUBBED) called isNew3DS = 0x%08x", static_cast<u32>(is_new_3ds));
+}
+
void Init() {
AddService(new PTM_Play_Interface);
AddService(new PTM_Sysm_Interface);
diff --git a/src/core/hle/service/ptm/ptm.h b/src/core/hle/service/ptm/ptm.h
index 4cf7383d1..7ef8877c7 100644
--- a/src/core/hle/service/ptm/ptm.h
+++ b/src/core/hle/service/ptm/ptm.h
@@ -88,6 +88,14 @@ void GetTotalStepCount(Interface* self);
*/
void IsLegacyPowerOff(Interface* self);
+/**
+ * PTM::CheckNew3DS service function
+ * Outputs:
+ * 1: Result code, 0 on success, otherwise error code
+ * 2: u8 output: 0 = Old3DS, 1 = New3DS.
+ */
+void CheckNew3DS(Interface* self);
+
/// Initialize the PTM service
void Init();
diff --git a/src/core/hle/service/ptm/ptm_sysm.cpp b/src/core/hle/service/ptm/ptm_sysm.cpp
index fe76dd108..cc4ef1101 100644
--- a/src/core/hle/service/ptm/ptm_sysm.cpp
+++ b/src/core/hle/service/ptm/ptm_sysm.cpp
@@ -18,7 +18,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x040700C0, nullptr, "ShutdownAsync"},
{0x04080000, nullptr, "Awake"},
{0x04090080, nullptr, "RebootAsync"},
- {0x040A0000, nullptr, "CheckNew3DS"},
+ {0x040A0000, CheckNew3DS, "CheckNew3DS"},
{0x08010640, nullptr, "SetInfoLEDPattern"},
{0x08020040, nullptr, "SetInfoLEDPatternHeader"},
{0x08030000, nullptr, "GetInfoLEDStatus"},
@@ -35,7 +35,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x080E0140, nullptr, "NotifyPlayEvent"},
{0x080F0000, IsLegacyPowerOff, "IsLegacyPowerOff"},
{0x08100000, nullptr, "ClearLegacyPowerOff"},
- {0x08110000, nullptr, "GetShellStatus"},
+ {0x08110000, GetShellState, "GetShellState"},
{0x08120000, nullptr, "IsShutdownByBatteryEmpty"},
{0x08130000, nullptr, "FormatSavedata"},
{0x08140000, nullptr, "GetLegacyJumpProhibitedFlag"},
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 0fe3a4d7a..395880843 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -7,9 +7,9 @@
#include "core/hle/service/service.h"
#include "core/hle/service/ac_u.h"
+#include "core/hle/service/act_a.h"
#include "core/hle/service/act_u.h"
#include "core/hle/service/csnd_snd.h"
-#include "core/hle/service/dlp_srvr.h"
#include "core/hle/service/dsp_dsp.h"
#include "core/hle/service/err_f.h"
#include "core/hle/service/gsp_gpu.h"
@@ -30,6 +30,7 @@
#include "core/hle/service/boss/boss.h"
#include "core/hle/service/cam/cam.h"
#include "core/hle/service/cecd/cecd.h"
+#include "core/hle/service/dlp/dlp.h"
#include "core/hle/service/frd/frd.h"
#include "core/hle/service/fs/archive.h"
#include "core/hle/service/cfg/cfg.h"
@@ -110,6 +111,7 @@ void Init() {
Service::CAM::Init();
Service::CECD::Init();
Service::CFG::Init();
+ Service::DLP::Init();
Service::FRD::Init();
Service::HID::Init();
Service::IR::Init();
@@ -119,9 +121,9 @@ void Init() {
Service::PTM::Init();
AddService(new AC_U::Interface);
+ AddService(new ACT_A::Interface);
AddService(new ACT_U::Interface);
AddService(new CSND_SND::Interface);
- AddService(new DLP_SRVR::Interface);
AddService(new DSP_DSP::Interface);
AddService(new GSP_GPU::Interface);
AddService(new GSP_LCD::Interface);
@@ -148,6 +150,7 @@ void Shutdown() {
Service::IR::Shutdown();
Service::HID::Shutdown();
Service::FRD::Shutdown();
+ Service::DLP::Shutdown();
Service::CFG::Shutdown();
Service::CECD::Shutdown();
Service::CAM::Shutdown();
diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp
index d3e5d4bca..9b285567b 100644
--- a/src/core/hle/service/soc_u.cpp
+++ b/src/core/hle/service/soc_u.cpp
@@ -373,14 +373,18 @@ static void Bind(Service::Interface* self) {
u32* cmd_buffer = Kernel::GetCommandBuffer();
u32 socket_handle = cmd_buffer[1];
u32 len = cmd_buffer[2];
- CTRSockAddr* ctr_sock_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[6]));
- if (ctr_sock_addr == nullptr) {
+ // Virtual address of the sock_addr structure
+ VAddr sock_addr_addr = cmd_buffer[6];
+ if (!Memory::IsValidVirtualAddress(sock_addr_addr)) {
cmd_buffer[1] = -1; // TODO(Subv): Correct code
return;
}
- sockaddr sock_addr = CTRSockAddr::ToPlatform(*ctr_sock_addr);
+ CTRSockAddr ctr_sock_addr;
+ Memory::ReadBlock(sock_addr_addr, reinterpret_cast<u8*>(&ctr_sock_addr), sizeof(CTRSockAddr));
+
+ sockaddr sock_addr = CTRSockAddr::ToPlatform(ctr_sock_addr);
int res = ::bind(socket_handle, &sock_addr, std::max<u32>(sizeof(sock_addr), len));
@@ -496,7 +500,7 @@ static void Accept(Service::Interface* self) {
result = TranslateError(GET_ERRNO);
} else {
CTRSockAddr ctr_addr = CTRSockAddr::FromPlatform(addr);
- Memory::WriteBlock(cmd_buffer[0x104 >> 2], (const u8*)&ctr_addr, max_addr_len);
+ Memory::WriteBlock(cmd_buffer[0x104 >> 2], &ctr_addr, sizeof(ctr_addr));
}
cmd_buffer[0] = IPC::MakeHeader(4, 2, 2);
@@ -547,20 +551,31 @@ static void SendTo(Service::Interface* self) {
u32 flags = cmd_buffer[3];
u32 addr_len = cmd_buffer[4];
- u8* input_buff = Memory::GetPointer(cmd_buffer[8]);
- CTRSockAddr* ctr_dest_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[10]));
+ VAddr input_buff_address = cmd_buffer[8];
+ if (!Memory::IsValidVirtualAddress(input_buff_address)) {
+ cmd_buffer[1] = -1; // TODO(Subv): Find the right error code
+ return;
+ }
- if (ctr_dest_addr == nullptr) {
+ // Memory address of the dest_addr structure
+ VAddr dest_addr_addr = cmd_buffer[10];
+ if (!Memory::IsValidVirtualAddress(dest_addr_addr)) {
cmd_buffer[1] = -1; // TODO(Subv): Find the right error code
return;
}
+ std::vector<u8> input_buff(len);
+ Memory::ReadBlock(input_buff_address, input_buff.data(), input_buff.size());
+
+ CTRSockAddr ctr_dest_addr;
+ Memory::ReadBlock(dest_addr_addr, &ctr_dest_addr, sizeof(ctr_dest_addr));
+
int ret = -1;
if (addr_len > 0) {
- sockaddr dest_addr = CTRSockAddr::ToPlatform(*ctr_dest_addr);
- ret = ::sendto(socket_handle, (const char*)input_buff, len, flags, &dest_addr, sizeof(dest_addr));
+ sockaddr dest_addr = CTRSockAddr::ToPlatform(ctr_dest_addr);
+ ret = ::sendto(socket_handle, reinterpret_cast<const char*>(input_buff.data()), len, flags, &dest_addr, sizeof(dest_addr));
} else {
- ret = ::sendto(socket_handle, (const char*)input_buff, len, flags, nullptr, 0);
+ ret = ::sendto(socket_handle, reinterpret_cast<const char*>(input_buff.data()), len, flags, nullptr, 0);
}
int result = 0;
@@ -591,14 +606,24 @@ static void RecvFrom(Service::Interface* self) {
std::memcpy(&buffer_parameters, &cmd_buffer[64], sizeof(buffer_parameters));
- u8* output_buff = Memory::GetPointer(buffer_parameters.output_buffer_addr);
+ if (!Memory::IsValidVirtualAddress(buffer_parameters.output_buffer_addr)) {
+ cmd_buffer[1] = -1; // TODO(Subv): Find the right error code
+ return;
+ }
+
+ if (!Memory::IsValidVirtualAddress(buffer_parameters.output_src_address_buffer)) {
+ cmd_buffer[1] = -1; // TODO(Subv): Find the right error code
+ return;
+ }
+
+ std::vector<u8> output_buff(len);
sockaddr src_addr;
socklen_t src_addr_len = sizeof(src_addr);
- int ret = ::recvfrom(socket_handle, (char*)output_buff, len, flags, &src_addr, &src_addr_len);
+ int ret = ::recvfrom(socket_handle, reinterpret_cast<char*>(output_buff.data()), len, flags, &src_addr, &src_addr_len);
if (ret >= 0 && buffer_parameters.output_src_address_buffer != 0 && src_addr_len > 0) {
- CTRSockAddr* ctr_src_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(buffer_parameters.output_src_address_buffer));
- *ctr_src_addr = CTRSockAddr::FromPlatform(src_addr);
+ CTRSockAddr ctr_src_addr = CTRSockAddr::FromPlatform(src_addr);
+ Memory::WriteBlock(buffer_parameters.output_src_address_buffer, &ctr_src_addr, sizeof(ctr_src_addr));
}
int result = 0;
@@ -606,6 +631,9 @@ static void RecvFrom(Service::Interface* self) {
if (ret == SOCKET_ERROR_VALUE) {
result = TranslateError(GET_ERRNO);
total_received = 0;
+ } else {
+ // Write only the data we received to avoid overwriting parts of the buffer with zeros
+ Memory::WriteBlock(buffer_parameters.output_buffer_addr, output_buff.data(), total_received);
}
cmd_buffer[1] = result;
@@ -617,18 +645,28 @@ static void Poll(Service::Interface* self) {
u32* cmd_buffer = Kernel::GetCommandBuffer();
u32 nfds = cmd_buffer[1];
int timeout = cmd_buffer[2];
- CTRPollFD* input_fds = reinterpret_cast<CTRPollFD*>(Memory::GetPointer(cmd_buffer[6]));
- CTRPollFD* output_fds = reinterpret_cast<CTRPollFD*>(Memory::GetPointer(cmd_buffer[0x104 >> 2]));
+
+ VAddr input_fds_addr = cmd_buffer[6];
+ VAddr output_fds_addr = cmd_buffer[0x104 >> 2];
+ if (!Memory::IsValidVirtualAddress(input_fds_addr) || !Memory::IsValidVirtualAddress(output_fds_addr)) {
+ cmd_buffer[1] = -1; // TODO(Subv): Find correct error code.
+ return;
+ }
+
+ std::vector<CTRPollFD> ctr_fds(nfds);
+ Memory::ReadBlock(input_fds_addr, ctr_fds.data(), nfds * sizeof(CTRPollFD));
// The 3ds_pollfd and the pollfd structures may be different (Windows/Linux have different sizes)
// so we have to copy the data
std::vector<pollfd> platform_pollfd(nfds);
- std::transform(input_fds, input_fds + nfds, platform_pollfd.begin(), CTRPollFD::ToPlatform);
+ std::transform(ctr_fds.begin(), ctr_fds.end(), platform_pollfd.begin(), CTRPollFD::ToPlatform);
const int ret = ::poll(platform_pollfd.data(), nfds, timeout);
// Now update the output pollfd structure
- std::transform(platform_pollfd.begin(), platform_pollfd.end(), output_fds, CTRPollFD::FromPlatform);
+ std::transform(platform_pollfd.begin(), platform_pollfd.end(), ctr_fds.begin(), CTRPollFD::FromPlatform);
+
+ Memory::WriteBlock(output_fds_addr, ctr_fds.data(), nfds * sizeof(CTRPollFD));
int result = 0;
if (ret == SOCKET_ERROR_VALUE)
@@ -643,14 +681,16 @@ static void GetSockName(Service::Interface* self) {
u32 socket_handle = cmd_buffer[1];
socklen_t ctr_len = cmd_buffer[2];
- CTRSockAddr* ctr_dest_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[0x104 >> 2]));
+ // Memory address of the ctr_dest_addr structure
+ VAddr ctr_dest_addr_addr = cmd_buffer[0x104 >> 2];
sockaddr dest_addr;
socklen_t dest_addr_len = sizeof(dest_addr);
int ret = ::getsockname(socket_handle, &dest_addr, &dest_addr_len);
- if (ctr_dest_addr != nullptr) {
- *ctr_dest_addr = CTRSockAddr::FromPlatform(dest_addr);
+ if (ctr_dest_addr_addr != 0 && Memory::IsValidVirtualAddress(ctr_dest_addr_addr)) {
+ CTRSockAddr ctr_dest_addr = CTRSockAddr::FromPlatform(dest_addr);
+ Memory::WriteBlock(ctr_dest_addr_addr, &ctr_dest_addr, sizeof(ctr_dest_addr));
} else {
cmd_buffer[1] = -1; // TODO(Subv): Verify error
return;
@@ -682,14 +722,16 @@ static void GetPeerName(Service::Interface* self) {
u32 socket_handle = cmd_buffer[1];
socklen_t len = cmd_buffer[2];
- CTRSockAddr* ctr_dest_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[0x104 >> 2]));
+ // Memory address of the ctr_dest_addr structure
+ VAddr ctr_dest_addr_addr = cmd_buffer[0x104 >> 2];
sockaddr dest_addr;
socklen_t dest_addr_len = sizeof(dest_addr);
int ret = ::getpeername(socket_handle, &dest_addr, &dest_addr_len);
- if (ctr_dest_addr != nullptr) {
- *ctr_dest_addr = CTRSockAddr::FromPlatform(dest_addr);
+ if (ctr_dest_addr_addr != 0 && Memory::IsValidVirtualAddress(ctr_dest_addr_addr)) {
+ CTRSockAddr ctr_dest_addr = CTRSockAddr::FromPlatform(dest_addr);
+ Memory::WriteBlock(ctr_dest_addr_addr, &ctr_dest_addr, sizeof(ctr_dest_addr));
} else {
cmd_buffer[1] = -1;
return;
@@ -711,13 +753,17 @@ static void Connect(Service::Interface* self) {
u32 socket_handle = cmd_buffer[1];
socklen_t len = cmd_buffer[2];
- CTRSockAddr* ctr_input_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[6]));
- if (ctr_input_addr == nullptr) {
+ // Memory address of the ctr_input_addr structure
+ VAddr ctr_input_addr_addr = cmd_buffer[6];
+ if (!Memory::IsValidVirtualAddress(ctr_input_addr_addr)) {
cmd_buffer[1] = -1; // TODO(Subv): Verify error
return;
}
- sockaddr input_addr = CTRSockAddr::ToPlatform(*ctr_input_addr);
+ CTRSockAddr ctr_input_addr;
+ Memory::ReadBlock(ctr_input_addr_addr, &ctr_input_addr, sizeof(ctr_input_addr));
+
+ sockaddr input_addr = CTRSockAddr::ToPlatform(ctr_input_addr);
int ret = ::connect(socket_handle, &input_addr, sizeof(input_addr));
int result = 0;
if (ret != 0)
diff --git a/src/core/hle/service/ssl_c.cpp b/src/core/hle/service/ssl_c.cpp
index 14a4e98ec..a8aff1abf 100644
--- a/src/core/hle/service/ssl_c.cpp
+++ b/src/core/hle/service/ssl_c.cpp
@@ -31,7 +31,6 @@ static void GenerateRandomData(Service::Interface* self) {
u32 size = cmd_buff[1];
VAddr address = cmd_buff[3];
- u8* output_buff = Memory::GetPointer(address);
// Fill the output buffer with random data.
u32 data = 0;
@@ -44,13 +43,13 @@ static void GenerateRandomData(Service::Interface* self) {
if (size > 4) {
// Use up the entire 4 bytes of the random data for as long as possible
- *(u32*)(output_buff + i) = data;
+ Memory::Write32(address + i, data);
i += 4;
} else if (size == 2) {
- *(u16*)(output_buff + i) = (u16)(data & 0xffff);
+ Memory::Write16(address + i, static_cast<u16>(data & 0xffff));
i += 2;
} else {
- *(u8*)(output_buff + i) = (u8)(data & 0xff);
+ Memory::Write8(address + i, static_cast<u8>(data & 0xff));
i++;
}
}
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index 3a53126c1..0ce72de87 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -6,6 +6,7 @@
#include "common/logging/log.h"
#include "common/microprofile.h"
+#include "common/scope_exit.h"
#include "common/string_util.h"
#include "common/symbols.h"
@@ -326,9 +327,9 @@ static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_cou
}
}
- HLE::Reschedule(__func__);
+ SCOPE_EXIT({HLE::Reschedule("WaitSynchronizationN");}); // Reschedule after putting the threads to sleep.
- // If thread should wait, then set its state to waiting and then reschedule...
+ // If thread should wait, then set its state to waiting
if (wait_thread) {
// Actually wait the current thread on each object if we decided to wait...
@@ -502,6 +503,9 @@ static ResultCode CreateThread(Handle* out_handle, s32 priority, u32 entry_point
CASCADE_RESULT(SharedPtr<Thread> thread, Kernel::Thread::Create(
name, entry_point, priority, arg, processor_id, stack_top));
+
+ thread->context.fpscr = FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO | FPSCR_ROUND_TOZERO; // 0x03C00000
+
CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(thread)));
LOG_TRACE(Kernel_SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, "
diff --git a/src/core/loader/3dsx.cpp b/src/core/loader/3dsx.cpp
index 98e7ab48f..a16411e14 100644
--- a/src/core/loader/3dsx.cpp
+++ b/src/core/loader/3dsx.cpp
@@ -10,6 +10,7 @@
#include "core/file_sys/archive_romfs.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
+#include "core/hle/service/fs/archive.h"
#include "core/loader/3dsx.h"
#include "core/memory.h"
@@ -263,6 +264,8 @@ ResultStatus AppLoader_THREEDSX::Load() {
Kernel::g_current_process->Run(48, Kernel::DEFAULT_STACK_SIZE);
+ Service::FS::RegisterArchiveType(std::make_unique<FileSys::ArchiveFactory_RomFS>(*this), Service::FS::ArchiveIdCode::RomFS);
+
is_loaded = true;
return ResultStatus::Success;
}
diff --git a/src/core/loader/3dsx.h b/src/core/loader/3dsx.h
index 3ee686703..90b20c61c 100644
--- a/src/core/loader/3dsx.h
+++ b/src/core/loader/3dsx.h
@@ -28,6 +28,14 @@ public:
static FileType IdentifyType(FileUtil::IOFile& file);
/**
+ * Returns the type of this file
+ * @return FileType corresponding to the loaded file
+ */
+ FileType GetFileType() override {
+ return IdentifyType(file);
+ }
+
+ /**
* Load the bootable file
* @return ResultStatus result of function
*/
diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h
index c6a5ebe99..cb3724f9d 100644
--- a/src/core/loader/elf.h
+++ b/src/core/loader/elf.h
@@ -28,6 +28,14 @@ public:
static FileType IdentifyType(FileUtil::IOFile& file);
/**
+ * Returns the type of this file
+ * @return FileType corresponding to the loaded file
+ */
+ FileType GetFileType() override {
+ return IdentifyType(file);
+ }
+
+ /**
* Load the bootable file
* @return ResultStatus result of function
*/
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index af3f62248..9719d30d5 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -8,9 +8,7 @@
#include "common/logging/log.h"
#include "common/string_util.h"
-#include "core/file_sys/archive_romfs.h"
#include "core/hle/kernel/process.h"
-#include "core/hle/service/fs/archive.h"
#include "core/loader/3dsx.h"
#include "core/loader/elf.h"
#include "core/loader/ncch.h"
@@ -67,6 +65,9 @@ FileType GuessFromExtension(const std::string& extension_) {
if (extension == ".3dsx")
return FileType::THREEDSX;
+ if (extension == ".cia")
+ return FileType::CIA;
+
return FileType::Unknown;
}
@@ -90,7 +91,15 @@ const char* GetFileTypeString(FileType type) {
return "unknown";
}
-std::unique_ptr<AppLoader> GetLoader(FileUtil::IOFile&& file, FileType type,
+/**
+ * Get a loader for a file with a specific type
+ * @param file The file to load
+ * @param type The type of the file
+ * @param filename the file name (without path)
+ * @param filepath the file full path (with name)
+ * @return std::unique_ptr<AppLoader> a pointer to a loader object; nullptr for unsupported type
+ */
+static std::unique_ptr<AppLoader> GetFileLoader(FileUtil::IOFile&& file, FileType type,
const std::string& filename, const std::string& filepath) {
switch (type) {
@@ -108,15 +117,15 @@ std::unique_ptr<AppLoader> GetLoader(FileUtil::IOFile&& file, FileType type,
return std::make_unique<AppLoader_NCCH>(std::move(file), filepath);
default:
- return std::unique_ptr<AppLoader>();
+ return nullptr;
}
}
-ResultStatus LoadFile(const std::string& filename) {
+std::unique_ptr<AppLoader> GetLoader(const std::string& filename) {
FileUtil::IOFile file(filename, "rb");
if (!file.IsOpen()) {
LOG_ERROR(Loader, "Failed to load file %s", filename.c_str());
- return ResultStatus::Error;
+ return nullptr;
}
std::string filename_filename, filename_extension;
@@ -133,44 +142,7 @@ ResultStatus LoadFile(const std::string& filename) {
LOG_INFO(Loader, "Loading file %s as %s...", filename.c_str(), GetFileTypeString(type));
- std::unique_ptr<AppLoader> app_loader = GetLoader(std::move(file), type, filename_filename, filename);
-
- switch (type) {
-
- // 3DSX file format...
- // or NCCH/NCSD container formats...
- case FileType::THREEDSX:
- case FileType::CXI:
- case FileType::CCI:
- {
- // Load application and RomFS
- ResultStatus result = app_loader->Load();
- if (ResultStatus::Success == result) {
- Service::FS::RegisterArchiveType(std::make_unique<FileSys::ArchiveFactory_RomFS>(*app_loader), Service::FS::ArchiveIdCode::RomFS);
- return ResultStatus::Success;
- }
- return result;
- }
-
- // Standard ELF file format...
- case FileType::ELF:
- return app_loader->Load();
-
- // CIA file format...
- case FileType::CIA:
- return ResultStatus::ErrorNotImplemented;
-
- // Error occurred durring IdentifyFile...
- case FileType::Error:
-
- // IdentifyFile could know identify file type...
- case FileType::Unknown:
- {
- LOG_CRITICAL(Loader, "File %s is of unknown type.", filename.c_str());
- return ResultStatus::ErrorInvalidFormat;
- }
- }
- return ResultStatus::Error;
+ return GetFileLoader(std::move(file), type, filename_filename, filename);
}
} // namespace Loader
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 9d3e9ed3b..77d87afe1 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -10,10 +10,8 @@
#include <string>
#include <vector>
-#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/file_util.h"
-#include "common/swap.h"
namespace Kernel {
struct AddressMapping;
@@ -80,51 +78,6 @@ constexpr u32 MakeMagic(char a, char b, char c, char d) {
return a | b << 8 | c << 16 | d << 24;
}
-/// SMDH data structure that contains titles, icons etc. See https://www.3dbrew.org/wiki/SMDH
-struct SMDH {
- u32_le magic;
- u16_le version;
- INSERT_PADDING_BYTES(2);
-
- struct Title {
- std::array<u16, 0x40> short_title;
- std::array<u16, 0x80> long_title;
- std::array<u16, 0x40> publisher;
- };
- std::array<Title, 16> titles;
-
- std::array<u8, 16> ratings;
- u32_le region_lockout;
- u32_le match_maker_id;
- u64_le match_maker_bit_id;
- u32_le flags;
- u16_le eula_version;
- INSERT_PADDING_BYTES(2);
- float_le banner_animation_frame;
- u32_le cec_id;
- INSERT_PADDING_BYTES(8);
-
- std::array<u8, 0x480> small_icon;
- std::array<u8, 0x1200> large_icon;
-
- /// indicates the language used for each title entry
- enum class TitleLanguage {
- Japanese = 0,
- English = 1,
- French = 2,
- German = 3,
- Italian = 4,
- Spanish = 5,
- SimplifiedChinese = 6,
- Korean= 7,
- Dutch = 8,
- Portuguese = 9,
- Russian = 10,
- TraditionalChinese = 11
- };
-};
-static_assert(sizeof(SMDH) == 0x36C0, "SMDH structure size is wrong");
-
/// Interface for loading an application
class AppLoader : NonCopyable {
public:
@@ -132,6 +85,12 @@ public:
virtual ~AppLoader() { }
/**
+ * Returns the type of this file
+ * @return FileType corresponding to the loaded file
+ */
+ virtual FileType GetFileType() = 0;
+
+ /**
* Load the application
* @return ResultStatus result of function
*/
@@ -197,20 +156,10 @@ protected:
extern const std::initializer_list<Kernel::AddressMapping> default_address_mappings;
/**
- * Get a loader for a file with a specific type
- * @param file The file to load
- * @param type The type of the file
- * @param filename the file name (without path)
- * @param filepath the file full path (with name)
- * @return std::unique_ptr<AppLoader> a pointer to a loader object; nullptr for unsupported type
- */
-std::unique_ptr<AppLoader> GetLoader(FileUtil::IOFile&& file, FileType type, const std::string& filename, const std::string& filepath);
-
-/**
- * Identifies and loads a bootable file
+ * Identifies a bootable file and return a suitable loader
* @param filename String filename of bootable file
- * @return ResultStatus result of function
+ * @return best loader for this file
*/
-ResultStatus LoadFile(const std::string& filename);
+std::unique_ptr<AppLoader> GetLoader(const std::string& filename);
} // namespace
diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp
index 7391bdb26..fca091ff9 100644
--- a/src/core/loader/ncch.cpp
+++ b/src/core/loader/ncch.cpp
@@ -10,8 +10,10 @@
#include "common/string_util.h"
#include "common/swap.h"
+#include "core/file_sys/archive_romfs.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
+#include "core/hle/service/fs/archive.h"
#include "core/loader/ncch.h"
#include "core/memory.h"
@@ -303,7 +305,12 @@ ResultStatus AppLoader_NCCH::Load() {
is_loaded = true; // Set state to loaded
- return LoadExec(); // Load the executable into memory for booting
+ result = LoadExec(); // Load the executable into memory for booting
+ if (ResultStatus::Success != result)
+ return result;
+
+ Service::FS::RegisterArchiveType(std::make_unique<FileSys::ArchiveFactory_RomFS>(*this), Service::FS::ArchiveIdCode::RomFS);
+ return ResultStatus::Success;
}
ResultStatus AppLoader_NCCH::ReadCode(std::vector<u8>& buffer) {
diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h
index fd852c3de..75609ee57 100644
--- a/src/core/loader/ncch.h
+++ b/src/core/loader/ncch.h
@@ -174,6 +174,14 @@ public:
static FileType IdentifyType(FileUtil::IOFile& file);
/**
+ * Returns the type of this file
+ * @return FileType corresponding to the loaded file
+ */
+ FileType GetFileType() override {
+ return IdentifyType(file);
+ }
+
+ /**
* Load the application
* @return ResultStatus result of function
*/
diff --git a/src/core/loader/smdh.cpp b/src/core/loader/smdh.cpp
new file mode 100644
index 000000000..2d014054a
--- /dev/null
+++ b/src/core/loader/smdh.cpp
@@ -0,0 +1,54 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <cstring>
+#include <vector>
+
+#include "common/common_types.h"
+
+#include "core/loader/loader.h"
+#include "core/loader/smdh.h"
+
+#include "video_core/utils.h"
+
+namespace Loader {
+
+bool IsValidSMDH(const std::vector<u8>& smdh_data) {
+ if (smdh_data.size() < sizeof(Loader::SMDH))
+ return false;
+
+ u32 magic;
+ memcpy(&magic, smdh_data.data(), sizeof(u32));
+
+ return Loader::MakeMagic('S', 'M', 'D', 'H') == magic;
+}
+
+std::vector<u16> SMDH::GetIcon(bool large) const {
+ u32 size;
+ const u8* icon_data;
+
+ if (large) {
+ size = 48;
+ icon_data = large_icon.data();
+ } else {
+ size = 24;
+ icon_data = small_icon.data();
+ }
+
+ std::vector<u16> icon(size * size);
+ for (u32 x = 0; x < size; ++x) {
+ for (u32 y = 0; y < size; ++y) {
+ u32 coarse_y = y & ~7;
+ const u8* pixel = icon_data + VideoCore::GetMortonOffset(x, y, 2) + coarse_y * size * 2;
+ icon[x + size * y] = (pixel[1] << 8) + pixel[0];
+ }
+ }
+ return icon;
+}
+
+std::array<u16, 0x40> SMDH::GetShortTitle(Loader::SMDH::TitleLanguage language) const {
+ return titles[static_cast<int>(language)].short_title;
+}
+
+} // namespace
diff --git a/src/core/loader/smdh.h b/src/core/loader/smdh.h
new file mode 100644
index 000000000..2011abda2
--- /dev/null
+++ b/src/core/loader/smdh.h
@@ -0,0 +1,82 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <vector>
+
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "common/swap.h"
+
+namespace Loader {
+
+/**
+ * Tests if data is a valid SMDH by its length and magic number.
+ * @param smdh_data data buffer to test
+ * @return bool test result
+ */
+bool IsValidSMDH(const std::vector<u8>& smdh_data);
+
+/// SMDH data structure that contains titles, icons etc. See https://www.3dbrew.org/wiki/SMDH
+struct SMDH {
+ u32_le magic;
+ u16_le version;
+ INSERT_PADDING_BYTES(2);
+
+ struct Title {
+ std::array<u16, 0x40> short_title;
+ std::array<u16, 0x80> long_title;
+ std::array<u16, 0x40> publisher;
+ };
+ std::array<Title, 16> titles;
+
+ std::array<u8, 16> ratings;
+ u32_le region_lockout;
+ u32_le match_maker_id;
+ u64_le match_maker_bit_id;
+ u32_le flags;
+ u16_le eula_version;
+ INSERT_PADDING_BYTES(2);
+ float_le banner_animation_frame;
+ u32_le cec_id;
+ INSERT_PADDING_BYTES(8);
+
+ std::array<u8, 0x480> small_icon;
+ std::array<u8, 0x1200> large_icon;
+
+ /// indicates the language used for each title entry
+ enum class TitleLanguage {
+ Japanese = 0,
+ English = 1,
+ French = 2,
+ German = 3,
+ Italian = 4,
+ Spanish = 5,
+ SimplifiedChinese = 6,
+ Korean= 7,
+ Dutch = 8,
+ Portuguese = 9,
+ Russian = 10,
+ TraditionalChinese = 11
+ };
+
+ /**
+ * Gets game icon from SMDH
+ * @param large If true, returns large icon (48x48), otherwise returns small icon (24x24)
+ * @return vector of RGB565 data
+ */
+ std::vector<u16> GetIcon(bool large) const;
+
+ /**
+ * Gets the short game title from SMDH
+ * @param language title language
+ * @return UTF-16 array of the short title
+ */
+ std::array<u16, 0x40> GetShortTitle(Loader::SMDH::TitleLanguage language) const;
+};
+static_assert(sizeof(SMDH) == 0x36C0, "SMDH structure size is wrong");
+
+} // namespace
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index ee9b69f81..8c9e5d46d 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -246,6 +246,26 @@ void Write(const VAddr vaddr, const T data) {
}
}
+bool IsValidVirtualAddress(const VAddr vaddr) {
+ const u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
+ if (page_pointer)
+ return true;
+
+ if (current_page_table->attributes[vaddr >> PAGE_BITS] != PageType::Special)
+ return false;
+
+ MMIORegionPointer mmio_region = GetMMIOHandler(vaddr);
+ if (mmio_region) {
+ return mmio_region->IsValidAddress(vaddr);
+ }
+
+ return false;
+}
+
+bool IsValidPhysicalAddress(const PAddr paddr) {
+ return IsValidVirtualAddress(PhysicalToVirtualAddress(paddr));
+}
+
u8* GetPointer(const VAddr vaddr) {
u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
if (page_pointer) {
@@ -261,6 +281,7 @@ u8* GetPointer(const VAddr vaddr) {
}
u8* GetPhysicalPointer(PAddr address) {
+ // TODO(Subv): This call should not go through the application's memory mapping.
return GetPointer(PhysicalToVirtualAddress(address));
}
@@ -343,6 +364,59 @@ u64 Read64(const VAddr addr) {
return Read<u64_le>(addr);
}
+void ReadBlock(const VAddr src_addr, void* dest_buffer, const size_t size) {
+ size_t remaining_size = size;
+ size_t page_index = src_addr >> PAGE_BITS;
+ size_t page_offset = src_addr & PAGE_MASK;
+
+ while (remaining_size > 0) {
+ const size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size);
+ const VAddr current_vaddr = (page_index << PAGE_BITS) + page_offset;
+
+ switch (current_page_table->attributes[page_index]) {
+ case PageType::Unmapped: {
+ LOG_ERROR(HW_Memory, "unmapped ReadBlock @ 0x%08X (start address = 0x%08X, size = %zu)", current_vaddr, src_addr, size);
+ std::memset(dest_buffer, 0, copy_amount);
+ break;
+ }
+ case PageType::Memory: {
+ DEBUG_ASSERT(current_page_table->pointers[page_index]);
+
+ const u8* src_ptr = current_page_table->pointers[page_index] + page_offset;
+ std::memcpy(dest_buffer, src_ptr, copy_amount);
+ break;
+ }
+ case PageType::Special: {
+ DEBUG_ASSERT(GetMMIOHandler(current_vaddr));
+
+ GetMMIOHandler(current_vaddr)->ReadBlock(current_vaddr, dest_buffer, copy_amount);
+ break;
+ }
+ case PageType::RasterizerCachedMemory: {
+ RasterizerFlushRegion(VirtualToPhysicalAddress(current_vaddr), copy_amount);
+
+ std::memcpy(dest_buffer, GetPointerFromVMA(current_vaddr), copy_amount);
+ break;
+ }
+ case PageType::RasterizerCachedSpecial: {
+ DEBUG_ASSERT(GetMMIOHandler(current_vaddr));
+
+ RasterizerFlushRegion(VirtualToPhysicalAddress(current_vaddr), copy_amount);
+
+ GetMMIOHandler(current_vaddr)->ReadBlock(current_vaddr, dest_buffer, copy_amount);
+ break;
+ }
+ default:
+ UNREACHABLE();
+ }
+
+ page_index++;
+ page_offset = 0;
+ dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
+ remaining_size -= copy_amount;
+ }
+}
+
void Write8(const VAddr addr, const u8 data) {
Write<u8>(addr, data);
}
@@ -359,9 +433,165 @@ void Write64(const VAddr addr, const u64 data) {
Write<u64_le>(addr, data);
}
-void WriteBlock(const VAddr addr, const u8* data, const size_t size) {
- for (u32 offset = 0; offset < size; offset++) {
- Write8(addr + offset, data[offset]);
+void WriteBlock(const VAddr dest_addr, const void* src_buffer, const size_t size) {
+ size_t remaining_size = size;
+ size_t page_index = dest_addr >> PAGE_BITS;
+ size_t page_offset = dest_addr & PAGE_MASK;
+
+ while (remaining_size > 0) {
+ const size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size);
+ const VAddr current_vaddr = (page_index << PAGE_BITS) + page_offset;
+
+ switch (current_page_table->attributes[page_index]) {
+ case PageType::Unmapped: {
+ LOG_ERROR(HW_Memory, "unmapped WriteBlock @ 0x%08X (start address = 0x%08X, size = %zu)", current_vaddr, dest_addr, size);
+ break;
+ }
+ case PageType::Memory: {
+ DEBUG_ASSERT(current_page_table->pointers[page_index]);
+
+ u8* dest_ptr = current_page_table->pointers[page_index] + page_offset;
+ std::memcpy(dest_ptr, src_buffer, copy_amount);
+ break;
+ }
+ case PageType::Special: {
+ DEBUG_ASSERT(GetMMIOHandler(current_vaddr));
+
+ GetMMIOHandler(current_vaddr)->WriteBlock(current_vaddr, src_buffer, copy_amount);
+ break;
+ }
+ case PageType::RasterizerCachedMemory: {
+ RasterizerFlushAndInvalidateRegion(VirtualToPhysicalAddress(current_vaddr), copy_amount);
+
+ std::memcpy(GetPointerFromVMA(current_vaddr), src_buffer, copy_amount);
+ break;
+ }
+ case PageType::RasterizerCachedSpecial: {
+ DEBUG_ASSERT(GetMMIOHandler(current_vaddr));
+
+ RasterizerFlushAndInvalidateRegion(VirtualToPhysicalAddress(current_vaddr), copy_amount);
+
+ GetMMIOHandler(current_vaddr)->WriteBlock(current_vaddr, src_buffer, copy_amount);
+ break;
+ }
+ default:
+ UNREACHABLE();
+ }
+
+ page_index++;
+ page_offset = 0;
+ src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
+ remaining_size -= copy_amount;
+ }
+}
+
+void ZeroBlock(const VAddr dest_addr, const size_t size) {
+ size_t remaining_size = size;
+ size_t page_index = dest_addr >> PAGE_BITS;
+ size_t page_offset = dest_addr & PAGE_MASK;
+
+ static const std::array<u8, PAGE_SIZE> zeros = {};
+
+ while (remaining_size > 0) {
+ const size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size);
+ const VAddr current_vaddr = (page_index << PAGE_BITS) + page_offset;
+
+ switch (current_page_table->attributes[page_index]) {
+ case PageType::Unmapped: {
+ LOG_ERROR(HW_Memory, "unmapped ZeroBlock @ 0x%08X (start address = 0x%08X, size = %zu)", current_vaddr, dest_addr, size);
+ break;
+ }
+ case PageType::Memory: {
+ DEBUG_ASSERT(current_page_table->pointers[page_index]);
+
+ u8* dest_ptr = current_page_table->pointers[page_index] + page_offset;
+ std::memset(dest_ptr, 0, copy_amount);
+ break;
+ }
+ case PageType::Special: {
+ DEBUG_ASSERT(GetMMIOHandler(current_vaddr));
+
+ GetMMIOHandler(current_vaddr)->WriteBlock(current_vaddr, zeros.data(), copy_amount);
+ break;
+ }
+ case PageType::RasterizerCachedMemory: {
+ RasterizerFlushAndInvalidateRegion(VirtualToPhysicalAddress(current_vaddr), copy_amount);
+
+ std::memset(GetPointerFromVMA(current_vaddr), 0, copy_amount);
+ break;
+ }
+ case PageType::RasterizerCachedSpecial: {
+ DEBUG_ASSERT(GetMMIOHandler(current_vaddr));
+
+ RasterizerFlushAndInvalidateRegion(VirtualToPhysicalAddress(current_vaddr), copy_amount);
+
+ GetMMIOHandler(current_vaddr)->WriteBlock(current_vaddr, zeros.data(), copy_amount);
+ break;
+ }
+ default:
+ UNREACHABLE();
+ }
+
+ page_index++;
+ page_offset = 0;
+ remaining_size -= copy_amount;
+ }
+}
+
+void CopyBlock(VAddr dest_addr, VAddr src_addr, const size_t size) {
+ size_t remaining_size = size;
+ size_t page_index = src_addr >> PAGE_BITS;
+ size_t page_offset = src_addr & PAGE_MASK;
+
+ while (remaining_size > 0) {
+ const size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size);
+ const VAddr current_vaddr = (page_index << PAGE_BITS) + page_offset;
+
+ switch (current_page_table->attributes[page_index]) {
+ case PageType::Unmapped: {
+ LOG_ERROR(HW_Memory, "unmapped CopyBlock @ 0x%08X (start address = 0x%08X, size = %zu)", current_vaddr, src_addr, size);
+ ZeroBlock(dest_addr, copy_amount);
+ break;
+ }
+ case PageType::Memory: {
+ DEBUG_ASSERT(current_page_table->pointers[page_index]);
+ const u8* src_ptr = current_page_table->pointers[page_index] + page_offset;
+ WriteBlock(dest_addr, src_ptr, copy_amount);
+ break;
+ }
+ case PageType::Special: {
+ DEBUG_ASSERT(GetMMIOHandler(current_vaddr));
+
+ std::vector<u8> buffer(copy_amount);
+ GetMMIOHandler(current_vaddr)->ReadBlock(current_vaddr, buffer.data(), buffer.size());
+ WriteBlock(dest_addr, buffer.data(), buffer.size());
+ break;
+ }
+ case PageType::RasterizerCachedMemory: {
+ RasterizerFlushRegion(VirtualToPhysicalAddress(current_vaddr), copy_amount);
+
+ WriteBlock(dest_addr, GetPointerFromVMA(current_vaddr), copy_amount);
+ break;
+ }
+ case PageType::RasterizerCachedSpecial: {
+ DEBUG_ASSERT(GetMMIOHandler(current_vaddr));
+
+ RasterizerFlushRegion(VirtualToPhysicalAddress(current_vaddr), copy_amount);
+
+ std::vector<u8> buffer(copy_amount);
+ GetMMIOHandler(current_vaddr)->ReadBlock(current_vaddr, buffer.data(), buffer.size());
+ WriteBlock(dest_addr, buffer.data(), buffer.size());
+ break;
+ }
+ default:
+ UNREACHABLE();
+ }
+
+ page_index++;
+ page_offset = 0;
+ dest_addr += copy_amount;
+ src_addr += copy_amount;
+ remaining_size -= copy_amount;
}
}
diff --git a/src/core/memory.h b/src/core/memory.h
index 126d60471..ae5588dee 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -110,6 +110,9 @@ enum : VAddr {
NEW_LINEAR_HEAP_VADDR_END = NEW_LINEAR_HEAP_VADDR + NEW_LINEAR_HEAP_SIZE,
};
+bool IsValidVirtualAddress(const VAddr addr);
+bool IsValidPhysicalAddress(const PAddr addr);
+
u8 Read8(VAddr addr);
u16 Read16(VAddr addr);
u32 Read32(VAddr addr);
@@ -120,7 +123,10 @@ void Write16(VAddr addr, u16 data);
void Write32(VAddr addr, u32 data);
void Write64(VAddr addr, u64 data);
-void WriteBlock(VAddr addr, const u8* data, size_t size);
+void ReadBlock(const VAddr src_addr, void* dest_buffer, size_t size);
+void WriteBlock(const VAddr dest_addr, const void* src_buffer, size_t size);
+void ZeroBlock(const VAddr dest_addr, const size_t size);
+void CopyBlock(VAddr dest_addr, VAddr src_addr, size_t size);
u8* GetPointer(VAddr virtual_address);
diff --git a/src/core/memory_setup.h b/src/core/memory_setup.h
index 05f70a1fe..ee8ea7857 100644
--- a/src/core/memory_setup.h
+++ b/src/core/memory_setup.h
@@ -6,7 +6,7 @@
#include "common/common_types.h"
-#include "core/memory.h"
+#include "core/mmio.h"
namespace Memory {
diff --git a/src/core/mmio.h b/src/core/mmio.h
index 06b555e98..d76f005d8 100644
--- a/src/core/mmio.h
+++ b/src/core/mmio.h
@@ -18,15 +18,21 @@ class MMIORegion {
public:
virtual ~MMIORegion() = default;
+ virtual bool IsValidAddress(VAddr addr) = 0;
+
virtual u8 Read8(VAddr addr) = 0;
virtual u16 Read16(VAddr addr) = 0;
virtual u32 Read32(VAddr addr) = 0;
virtual u64 Read64(VAddr addr) = 0;
+ virtual bool ReadBlock(VAddr src_addr, void* dest_buffer, size_t size) = 0;
+
virtual void Write8(VAddr addr, u8 data) = 0;
virtual void Write16(VAddr addr, u16 data) = 0;
virtual void Write32(VAddr addr, u32 data) = 0;
virtual void Write64(VAddr addr, u64 data) = 0;
+
+ virtual bool WriteBlock(VAddr dest_addr, const void* src_buffer, size_t size) = 0;
};
using MMIORegionPointer = std::shared_ptr<MMIORegion>;
diff --git a/src/core/settings.h b/src/core/settings.h
index d6f8f2ff3..f95e62390 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -52,6 +52,9 @@ static const std::array<Values, NUM_INPUTS> All = {{
struct Values {
+ // CheckNew3DS
+ bool is_new_3ds;
+
// Controls
std::array<int, NativeInput::NUM_INPUTS> input_mappings;
float pad_circle_modifier_scale;
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
new file mode 100644
index 000000000..457c55571
--- /dev/null
+++ b/src/tests/CMakeLists.txt
@@ -0,0 +1,16 @@
+set(SRCS
+ tests.cpp
+ )
+
+set(HEADERS
+ )
+
+create_directory_groups(${SRCS} ${HEADERS})
+
+include_directories(../../externals/catch/single_include/)
+
+add_executable(tests ${SRCS} ${HEADERS})
+target_link_libraries(tests core video_core audio_core common)
+target_link_libraries(tests ${PLATFORM_LIBRARIES})
+
+add_test(NAME tests COMMAND $<TARGET_FILE:tests>)
diff --git a/src/tests/tests.cpp b/src/tests/tests.cpp
new file mode 100644
index 000000000..73978676f
--- /dev/null
+++ b/src/tests/tests.cpp
@@ -0,0 +1,9 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#define CATCH_CONFIG_MAIN
+#include <catch.hpp>
+
+// Catch provides the main function since we've given it the
+// CATCH_CONFIG_MAIN preprocessor directive.
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp
index ad0da796e..19e03adf4 100644
--- a/src/video_core/command_processor.cpp
+++ b/src/video_core/command_processor.cpp
@@ -149,7 +149,8 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
// Send to vertex shader
if (g_debug_context)
g_debug_context->OnEvent(DebugContext::Event::VertexShaderInvocation, static_cast<void*>(&immediate_input));
- Shader::OutputVertex output = g_state.vs.Run(shader_unit, immediate_input, regs.vs.num_input_attributes+1);
+ g_state.vs.Run(shader_unit, immediate_input, regs.vs.num_input_attributes+1);
+ Shader::OutputVertex output_vertex = shader_unit.output_registers.ToVertex(regs.vs);
// Send to renderer
using Pica::Shader::OutputVertex;
@@ -157,7 +158,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
VideoCore::g_renderer->Rasterizer()->AddTriangle(v0, v1, v2);
};
- g_state.primitive_assembler.SubmitVertex(output, AddTriangle);
+ g_state.primitive_assembler.SubmitVertex(output_vertex, AddTriangle);
}
}
}
@@ -199,9 +200,8 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
// Processes information about internal vertex attributes to figure out how a vertex is loaded.
// Later, these can be compiled and cached.
- VertexLoader loader;
const u32 base_address = regs.vertex_attributes.GetPhysicalBaseAddress();
- loader.Setup(regs);
+ VertexLoader loader(regs);
// Load vertices
bool is_indexed = (id == PICA_REG_INDEX(trigger_draw_indexed));
@@ -231,7 +231,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
// The size has been tuned for optimal balance between hit-rate and the cost of lookup
const size_t VERTEX_CACHE_SIZE = 32;
std::array<u16, VERTEX_CACHE_SIZE> vertex_cache_ids;
- std::array<Shader::OutputVertex, VERTEX_CACHE_SIZE> vertex_cache;
+ std::array<Shader::OutputRegisters, VERTEX_CACHE_SIZE> vertex_cache;
unsigned int vertex_cache_pos = 0;
vertex_cache_ids.fill(-1);
@@ -249,7 +249,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
ASSERT(vertex != -1);
bool vertex_cache_hit = false;
- Shader::OutputVertex output;
+ Shader::OutputRegisters output_registers;
if (is_indexed) {
if (g_debug_context && Pica::g_debug_context->recorder) {
@@ -259,7 +259,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
for (unsigned int i = 0; i < VERTEX_CACHE_SIZE; ++i) {
if (vertex == vertex_cache_ids[i]) {
- output = vertex_cache[i];
+ output_registers = vertex_cache[i];
vertex_cache_hit = true;
break;
}
@@ -274,15 +274,19 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
// Send to vertex shader
if (g_debug_context)
g_debug_context->OnEvent(DebugContext::Event::VertexShaderInvocation, (void*)&input);
- output = g_state.vs.Run(shader_unit, input, loader.GetNumTotalAttributes());
+ g_state.vs.Run(shader_unit, input, loader.GetNumTotalAttributes());
+ output_registers = shader_unit.output_registers;
if (is_indexed) {
- vertex_cache[vertex_cache_pos] = output;
+ vertex_cache[vertex_cache_pos] = output_registers;
vertex_cache_ids[vertex_cache_pos] = vertex;
vertex_cache_pos = (vertex_cache_pos + 1) % VERTEX_CACHE_SIZE;
}
}
+ // Retreive vertex from register data
+ Shader::OutputVertex output_vertex = output_registers.ToVertex(regs.vs);
+
// Send to renderer
using Pica::Shader::OutputVertex;
auto AddTriangle = [](
@@ -290,7 +294,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
VideoCore::g_renderer->Rasterizer()->AddTriangle(v0, v1, v2);
};
- primitive_assembler.SubmitVertex(output, AddTriangle);
+ primitive_assembler.SubmitVertex(output_vertex, AddTriangle);
}
for (auto& range : memory_accesses.ranges) {
diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp
index 2f645b441..871368323 100644
--- a/src/video_core/debug_utils/debug_utils.cpp
+++ b/src/video_core/debug_utils/debug_utils.cpp
@@ -696,106 +696,125 @@ finalise:
#endif
}
-void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig,6>& stages)
-{
+static std::string ReplacePattern(const std::string& input, const std::string& pattern, const std::string& replacement) {
+ size_t start = input.find(pattern);
+ if (start == std::string::npos)
+ return input;
+
+ std::string ret = input;
+ ret.replace(start, pattern.length(), replacement);
+ return ret;
+}
+
+static std::string GetTevStageConfigSourceString(const Pica::Regs::TevStageConfig::Source& source) {
using Source = Pica::Regs::TevStageConfig::Source;
+ static const std::map<Source, std::string> source_map = {
+ { Source::PrimaryColor, "PrimaryColor" },
+ { Source::PrimaryFragmentColor, "PrimaryFragmentColor" },
+ { Source::SecondaryFragmentColor, "SecondaryFragmentColor" },
+ { Source::Texture0, "Texture0" },
+ { Source::Texture1, "Texture1" },
+ { Source::Texture2, "Texture2" },
+ { Source::Texture3, "Texture3" },
+ { Source::PreviousBuffer, "PreviousBuffer" },
+ { Source::Constant, "Constant" },
+ { Source::Previous, "Previous" },
+ };
+
+ const auto src_it = source_map.find(source);
+ if (src_it == source_map.end())
+ return "Unknown";
+
+ return src_it->second;
+}
+
+static std::string GetTevStageConfigColorSourceString(const Pica::Regs::TevStageConfig::Source& source, const Pica::Regs::TevStageConfig::ColorModifier modifier) {
using ColorModifier = Pica::Regs::TevStageConfig::ColorModifier;
+ static const std::map<ColorModifier, std::string> color_modifier_map = {
+ { ColorModifier::SourceColor, "%source.rgb" },
+ { ColorModifier::OneMinusSourceColor, "(1.0 - %source.rgb)" },
+ { ColorModifier::SourceAlpha, "%source.aaa" },
+ { ColorModifier::OneMinusSourceAlpha, "(1.0 - %source.aaa)" },
+ { ColorModifier::SourceRed, "%source.rrr" },
+ { ColorModifier::OneMinusSourceRed, "(1.0 - %source.rrr)" },
+ { ColorModifier::SourceGreen, "%source.ggg" },
+ { ColorModifier::OneMinusSourceGreen, "(1.0 - %source.ggg)" },
+ { ColorModifier::SourceBlue, "%source.bbb" },
+ { ColorModifier::OneMinusSourceBlue, "(1.0 - %source.bbb)" },
+ };
+
+ auto src_str = GetTevStageConfigSourceString(source);
+ auto modifier_it = color_modifier_map.find(modifier);
+ std::string modifier_str = "%source.????";
+ if (modifier_it != color_modifier_map.end())
+ modifier_str = modifier_it->second;
+
+ return ReplacePattern(modifier_str, "%source", src_str);
+}
+
+static std::string GetTevStageConfigAlphaSourceString(const Pica::Regs::TevStageConfig::Source& source, const Pica::Regs::TevStageConfig::AlphaModifier modifier) {
using AlphaModifier = Pica::Regs::TevStageConfig::AlphaModifier;
+ static const std::map<AlphaModifier, std::string> alpha_modifier_map = {
+ { AlphaModifier::SourceAlpha, "%source.a" },
+ { AlphaModifier::OneMinusSourceAlpha, "(1.0 - %source.a)" },
+ { AlphaModifier::SourceRed, "%source.r" },
+ { AlphaModifier::OneMinusSourceRed, "(1.0 - %source.r)" },
+ { AlphaModifier::SourceGreen, "%source.g" },
+ { AlphaModifier::OneMinusSourceGreen, "(1.0 - %source.g)" },
+ { AlphaModifier::SourceBlue, "%source.b" },
+ { AlphaModifier::OneMinusSourceBlue, "(1.0 - %source.b)" },
+ };
+
+ auto src_str = GetTevStageConfigSourceString(source);
+ auto modifier_it = alpha_modifier_map.find(modifier);
+ std::string modifier_str = "%source.????";
+ if (modifier_it != alpha_modifier_map.end())
+ modifier_str = modifier_it->second;
+
+ return ReplacePattern(modifier_str, "%source", src_str);
+}
+
+static std::string GetTevStageConfigOperationString(const Pica::Regs::TevStageConfig::Operation& operation) {
using Operation = Pica::Regs::TevStageConfig::Operation;
+ static const std::map<Operation, std::string> combiner_map = {
+ { Operation::Replace, "%source1" },
+ { Operation::Modulate, "(%source1 * %source2)" },
+ { Operation::Add, "(%source1 + %source2)" },
+ { Operation::AddSigned, "(%source1 + %source2) - 0.5" },
+ { Operation::Lerp, "lerp(%source1, %source2, %source3)" },
+ { Operation::Subtract, "(%source1 - %source2)" },
+ { Operation::Dot3_RGB, "dot(%source1, %source2)" },
+ { Operation::MultiplyThenAdd, "((%source1 * %source2) + %source3)" },
+ { Operation::AddThenMultiply, "((%source1 + %source2) * %source3)" },
+ };
- std::string stage_info = "Tev setup:\n";
- for (size_t index = 0; index < stages.size(); ++index) {
- const auto& tev_stage = stages[index];
+ const auto op_it = combiner_map.find(operation);
+ if (op_it == combiner_map.end())
+ return "Unknown op (%source1, %source2, %source3)";
- static const std::map<Source, std::string> source_map = {
- { Source::PrimaryColor, "PrimaryColor" },
- { Source::Texture0, "Texture0" },
- { Source::Texture1, "Texture1" },
- { Source::Texture2, "Texture2" },
- { Source::Constant, "Constant" },
- { Source::Previous, "Previous" },
- };
+ return op_it->second;
+}
- static const std::map<ColorModifier, std::string> color_modifier_map = {
- { ColorModifier::SourceColor, { "%source.rgb" } },
- { ColorModifier::SourceAlpha, { "%source.aaa" } },
- };
- static const std::map<AlphaModifier, std::string> alpha_modifier_map = {
- { AlphaModifier::SourceAlpha, "%source.a" },
- { AlphaModifier::OneMinusSourceAlpha, "(255 - %source.a)" },
- };
+std::string GetTevStageConfigColorCombinerString(const Pica::Regs::TevStageConfig& tev_stage) {
+ auto op_str = GetTevStageConfigOperationString(tev_stage.color_op);
+ op_str = ReplacePattern(op_str, "%source1", GetTevStageConfigColorSourceString(tev_stage.color_source1, tev_stage.color_modifier1));
+ op_str = ReplacePattern(op_str, "%source2", GetTevStageConfigColorSourceString(tev_stage.color_source2, tev_stage.color_modifier2));
+ return ReplacePattern(op_str, "%source3", GetTevStageConfigColorSourceString(tev_stage.color_source3, tev_stage.color_modifier3));
+}
- static const std::map<Operation, std::string> combiner_map = {
- { Operation::Replace, "%source1" },
- { Operation::Modulate, "(%source1 * %source2) / 255" },
- { Operation::Add, "(%source1 + %source2)" },
- { Operation::Lerp, "lerp(%source1, %source2, %source3)" },
- };
+std::string GetTevStageConfigAlphaCombinerString(const Pica::Regs::TevStageConfig& tev_stage) {
+ auto op_str = GetTevStageConfigOperationString(tev_stage.alpha_op);
+ op_str = ReplacePattern(op_str, "%source1", GetTevStageConfigAlphaSourceString(tev_stage.alpha_source1, tev_stage.alpha_modifier1));
+ op_str = ReplacePattern(op_str, "%source2", GetTevStageConfigAlphaSourceString(tev_stage.alpha_source2, tev_stage.alpha_modifier2));
+ return ReplacePattern(op_str, "%source3", GetTevStageConfigAlphaSourceString(tev_stage.alpha_source3, tev_stage.alpha_modifier3));
+}
- static auto ReplacePattern =
- [](const std::string& input, const std::string& pattern, const std::string& replacement) -> std::string {
- size_t start = input.find(pattern);
- if (start == std::string::npos)
- return input;
-
- std::string ret = input;
- ret.replace(start, pattern.length(), replacement);
- return ret;
- };
- static auto GetColorSourceStr =
- [](const Source& src, const ColorModifier& modifier) {
- auto src_it = source_map.find(src);
- std::string src_str = "Unknown";
- if (src_it != source_map.end())
- src_str = src_it->second;
-
- auto modifier_it = color_modifier_map.find(modifier);
- std::string modifier_str = "%source.????";
- if (modifier_it != color_modifier_map.end())
- modifier_str = modifier_it->second;
-
- return ReplacePattern(modifier_str, "%source", src_str);
- };
- static auto GetColorCombinerStr =
- [](const Regs::TevStageConfig& tev_stage) {
- auto op_it = combiner_map.find(tev_stage.color_op);
- std::string op_str = "Unknown op (%source1, %source2, %source3)";
- if (op_it != combiner_map.end())
- op_str = op_it->second;
-
- op_str = ReplacePattern(op_str, "%source1", GetColorSourceStr(tev_stage.color_source1, tev_stage.color_modifier1));
- op_str = ReplacePattern(op_str, "%source2", GetColorSourceStr(tev_stage.color_source2, tev_stage.color_modifier2));
- return ReplacePattern(op_str, "%source3", GetColorSourceStr(tev_stage.color_source3, tev_stage.color_modifier3));
- };
- static auto GetAlphaSourceStr =
- [](const Source& src, const AlphaModifier& modifier) {
- auto src_it = source_map.find(src);
- std::string src_str = "Unknown";
- if (src_it != source_map.end())
- src_str = src_it->second;
-
- auto modifier_it = alpha_modifier_map.find(modifier);
- std::string modifier_str = "%source.????";
- if (modifier_it != alpha_modifier_map.end())
- modifier_str = modifier_it->second;
-
- return ReplacePattern(modifier_str, "%source", src_str);
- };
- static auto GetAlphaCombinerStr =
- [](const Regs::TevStageConfig& tev_stage) {
- auto op_it = combiner_map.find(tev_stage.alpha_op);
- std::string op_str = "Unknown op (%source1, %source2, %source3)";
- if (op_it != combiner_map.end())
- op_str = op_it->second;
-
- op_str = ReplacePattern(op_str, "%source1", GetAlphaSourceStr(tev_stage.alpha_source1, tev_stage.alpha_modifier1));
- op_str = ReplacePattern(op_str, "%source2", GetAlphaSourceStr(tev_stage.alpha_source2, tev_stage.alpha_modifier2));
- return ReplacePattern(op_str, "%source3", GetAlphaSourceStr(tev_stage.alpha_source3, tev_stage.alpha_modifier3));
- };
-
- stage_info += "Stage " + std::to_string(index) + ": " + GetColorCombinerStr(tev_stage) + " " + GetAlphaCombinerStr(tev_stage) + "\n";
+void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig, 6>& stages) {
+ std::string stage_info = "Tev setup:\n";
+ for (size_t index = 0; index < stages.size(); ++index) {
+ const auto& tev_stage = stages[index];
+ stage_info += "Stage " + std::to_string(index) + ": " + GetTevStageConfigColorCombinerString(tev_stage) + " " + GetTevStageConfigAlphaCombinerString(tev_stage) + "\n";
}
-
LOG_TRACE(HW_GPU, "%s", stage_info.c_str());
}
diff --git a/src/video_core/debug_utils/debug_utils.h b/src/video_core/debug_utils/debug_utils.h
index f628292a4..92e9734ae 100644
--- a/src/video_core/debug_utils/debug_utils.h
+++ b/src/video_core/debug_utils/debug_utils.h
@@ -224,7 +224,11 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int s, int t, const Texture
void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data);
-void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig,6>& stages);
+std::string GetTevStageConfigColorCombinerString(const Pica::Regs::TevStageConfig& tev_stage);
+std::string GetTevStageConfigAlphaCombinerString(const Pica::Regs::TevStageConfig& tev_stage);
+
+/// Dumps the Tev stage config to log at trace level
+void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig, 6>& stages);
/**
* Used in the vertex loader to merge access records. TODO: Investigate if actually useful.
diff --git a/src/video_core/pica.h b/src/video_core/pica.h
index 86c0a0096..544ea037f 100644
--- a/src/video_core/pica.h
+++ b/src/video_core/pica.h
@@ -787,23 +787,21 @@ struct Regs {
LightColor diffuse; // material.diffuse * light.diffuse
LightColor ambient; // material.ambient * light.ambient
- struct {
- // Encoded as 16-bit floating point
- union {
- BitField< 0, 16, u32> x;
- BitField<16, 16, u32> y;
- };
- union {
- BitField< 0, 16, u32> z;
- };
+ // Encoded as 16-bit floating point
+ union {
+ BitField< 0, 16, u32> x;
+ BitField<16, 16, u32> y;
+ };
+ union {
+ BitField< 0, 16, u32> z;
+ };
- INSERT_PADDING_WORDS(0x3);
+ INSERT_PADDING_WORDS(0x3);
- union {
- BitField<0, 1, u32> directional;
- BitField<1, 1, u32> two_sided_diffuse; // When disabled, clamp dot-product to 0
- };
- };
+ union {
+ BitField<0, 1, u32> directional;
+ BitField<1, 1, u32> two_sided_diffuse; // When disabled, clamp dot-product to 0
+ } config;
BitField<0, 20, u32> dist_atten_bias;
BitField<0, 20, u32> dist_atten_scale;
@@ -824,7 +822,7 @@ struct Regs {
BitField<27, 1, u32> clamp_highlights;
BitField<28, 2, LightingBumpMode> bump_mode;
BitField<30, 1, u32> disable_bump_renorm;
- };
+ } config0;
union {
BitField<16, 1, u32> disable_lut_d0;
@@ -845,13 +843,13 @@ struct Regs {
BitField<29, 1, u32> disable_dist_atten_light_5;
BitField<30, 1, u32> disable_dist_atten_light_6;
BitField<31, 1, u32> disable_dist_atten_light_7;
- };
+ } config1;
bool IsDistAttenDisabled(unsigned index) const {
- const unsigned disable[] = { disable_dist_atten_light_0, disable_dist_atten_light_1,
- disable_dist_atten_light_2, disable_dist_atten_light_3,
- disable_dist_atten_light_4, disable_dist_atten_light_5,
- disable_dist_atten_light_6, disable_dist_atten_light_7 };
+ const unsigned disable[] = { config1.disable_dist_atten_light_0, config1.disable_dist_atten_light_1,
+ config1.disable_dist_atten_light_2, config1.disable_dist_atten_light_3,
+ config1.disable_dist_atten_light_4, config1.disable_dist_atten_light_5,
+ config1.disable_dist_atten_light_6, config1.disable_dist_atten_light_7 };
return disable[index] != 0;
}
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index ed2e2f3ae..931c34a37 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -104,7 +104,6 @@ RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) {
// Sync fixed function OpenGL state
SyncCullMode();
- SyncDepthModifiers();
SyncBlendEnabled();
SyncBlendFuncs();
SyncBlendColor();
@@ -259,8 +258,10 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) {
// Depth modifiers
case PICA_REG_INDEX(viewport_depth_range):
+ SyncDepthScale();
+ break;
case PICA_REG_INDEX(viewport_depth_near_plane):
- SyncDepthModifiers();
+ SyncDepthOffset();
break;
// Depth buffering
@@ -379,6 +380,17 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) {
SyncCombinerColor();
break;
+ // Fragment lighting switches
+ case PICA_REG_INDEX(lighting.disable):
+ case PICA_REG_INDEX(lighting.num_lights):
+ case PICA_REG_INDEX(lighting.config0):
+ case PICA_REG_INDEX(lighting.config1):
+ case PICA_REG_INDEX(lighting.abs_lut_input):
+ case PICA_REG_INDEX(lighting.lut_input):
+ case PICA_REG_INDEX(lighting.lut_scale):
+ case PICA_REG_INDEX(lighting.light_enable):
+ break;
+
// Fragment lighting specular 0 color
case PICA_REG_INDEX_WORKAROUND(lighting.light[0].specular_0, 0x140 + 0 * 0x10):
SyncLightSpecular0(0);
@@ -517,6 +529,70 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) {
SyncLightPosition(7);
break;
+ // Fragment lighting light source config
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[0].config, 0x149 + 0 * 0x10):
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[1].config, 0x149 + 1 * 0x10):
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[2].config, 0x149 + 2 * 0x10):
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[3].config, 0x149 + 3 * 0x10):
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[4].config, 0x149 + 4 * 0x10):
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[5].config, 0x149 + 5 * 0x10):
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[6].config, 0x149 + 6 * 0x10):
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[7].config, 0x149 + 7 * 0x10):
+ shader_dirty = true;
+ break;
+
+ // Fragment lighting distance attenuation bias
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[0].dist_atten_bias, 0x014A + 0 * 0x10):
+ SyncLightDistanceAttenuationBias(0);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[1].dist_atten_bias, 0x014A + 1 * 0x10):
+ SyncLightDistanceAttenuationBias(1);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[2].dist_atten_bias, 0x014A + 2 * 0x10):
+ SyncLightDistanceAttenuationBias(2);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[3].dist_atten_bias, 0x014A + 3 * 0x10):
+ SyncLightDistanceAttenuationBias(3);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[4].dist_atten_bias, 0x014A + 4 * 0x10):
+ SyncLightDistanceAttenuationBias(4);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[5].dist_atten_bias, 0x014A + 5 * 0x10):
+ SyncLightDistanceAttenuationBias(5);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[6].dist_atten_bias, 0x014A + 6 * 0x10):
+ SyncLightDistanceAttenuationBias(6);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[7].dist_atten_bias, 0x014A + 7 * 0x10):
+ SyncLightDistanceAttenuationBias(7);
+ break;
+
+ // Fragment lighting distance attenuation scale
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[0].dist_atten_scale, 0x014B + 0 * 0x10):
+ SyncLightDistanceAttenuationScale(0);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[1].dist_atten_scale, 0x014B + 1 * 0x10):
+ SyncLightDistanceAttenuationScale(1);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[2].dist_atten_scale, 0x014B + 2 * 0x10):
+ SyncLightDistanceAttenuationScale(2);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[3].dist_atten_scale, 0x014B + 3 * 0x10):
+ SyncLightDistanceAttenuationScale(3);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[4].dist_atten_scale, 0x014B + 4 * 0x10):
+ SyncLightDistanceAttenuationScale(4);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[5].dist_atten_scale, 0x014B + 5 * 0x10):
+ SyncLightDistanceAttenuationScale(5);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[6].dist_atten_scale, 0x014B + 6 * 0x10):
+ SyncLightDistanceAttenuationScale(6);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[7].dist_atten_scale, 0x014B + 7 * 0x10):
+ SyncLightDistanceAttenuationScale(7);
+ break;
+
// Fragment lighting global ambient color (emission + ambient * ambient)
case PICA_REG_INDEX_WORKAROUND(lighting.global_ambient, 0x1c0):
SyncGlobalAmbient();
@@ -880,6 +956,8 @@ void RasterizerOpenGL::SetShader() {
glUniformBlockBinding(current_shader->shader.handle, block_index, 0);
// Update uniforms
+ SyncDepthScale();
+ SyncDepthOffset();
SyncAlphaTest();
SyncCombinerColor();
auto& tev_stages = Pica::g_state.regs.GetTevStages();
@@ -893,6 +971,8 @@ void RasterizerOpenGL::SetShader() {
SyncLightDiffuse(light_index);
SyncLightAmbient(light_index);
SyncLightPosition(light_index);
+ SyncLightDistanceAttenuationBias(light_index);
+ SyncLightDistanceAttenuationScale(light_index);
}
}
}
@@ -922,13 +1002,20 @@ void RasterizerOpenGL::SyncCullMode() {
}
}
-void RasterizerOpenGL::SyncDepthModifiers() {
+void RasterizerOpenGL::SyncDepthScale() {
float depth_scale = Pica::float24::FromRaw(Pica::g_state.regs.viewport_depth_range).ToFloat32();
- float depth_offset = Pica::float24::FromRaw(Pica::g_state.regs.viewport_depth_near_plane).ToFloat32();
+ if (depth_scale != uniform_block_data.data.depth_scale) {
+ uniform_block_data.data.depth_scale = depth_scale;
+ uniform_block_data.dirty = true;
+ }
+}
- uniform_block_data.data.depth_scale = depth_scale;
- uniform_block_data.data.depth_offset = depth_offset;
- uniform_block_data.dirty = true;
+void RasterizerOpenGL::SyncDepthOffset() {
+ float depth_offset = Pica::float24::FromRaw(Pica::g_state.regs.viewport_depth_near_plane).ToFloat32();
+ if (depth_offset != uniform_block_data.data.depth_offset) {
+ uniform_block_data.data.depth_offset = depth_offset;
+ uniform_block_data.dirty = true;
+ }
}
void RasterizerOpenGL::SyncBlendEnabled() {
@@ -937,6 +1024,8 @@ void RasterizerOpenGL::SyncBlendEnabled() {
void RasterizerOpenGL::SyncBlendFuncs() {
const auto& regs = Pica::g_state.regs;
+ state.blend.rgb_equation = PicaToGL::BlendEquation(regs.output_merger.alpha_blending.blend_equation_rgb);
+ state.blend.a_equation = PicaToGL::BlendEquation(regs.output_merger.alpha_blending.blend_equation_a);
state.blend.src_rgb_func = PicaToGL::BlendFunc(regs.output_merger.alpha_blending.factor_source_rgb);
state.blend.dst_rgb_func = PicaToGL::BlendFunc(regs.output_merger.alpha_blending.factor_dest_rgb);
state.blend.src_a_func = PicaToGL::BlendFunc(regs.output_merger.alpha_blending.factor_source_a);
@@ -1093,3 +1182,21 @@ void RasterizerOpenGL::SyncLightPosition(int light_index) {
uniform_block_data.dirty = true;
}
}
+
+void RasterizerOpenGL::SyncLightDistanceAttenuationBias(int light_index) {
+ GLfloat dist_atten_bias = Pica::float20::FromRaw(Pica::g_state.regs.lighting.light[light_index].dist_atten_bias).ToFloat32();
+
+ if (dist_atten_bias != uniform_block_data.data.light_src[light_index].dist_atten_bias) {
+ uniform_block_data.data.light_src[light_index].dist_atten_bias = dist_atten_bias;
+ uniform_block_data.dirty = true;
+ }
+}
+
+void RasterizerOpenGL::SyncLightDistanceAttenuationScale(int light_index) {
+ GLfloat dist_atten_scale = Pica::float20::FromRaw(Pica::g_state.regs.lighting.light[light_index].dist_atten_scale).ToFloat32();
+
+ if (dist_atten_scale != uniform_block_data.data.light_src[light_index].dist_atten_scale) {
+ uniform_block_data.data.light_src[light_index].dist_atten_scale = dist_atten_scale;
+ uniform_block_data.dirty = true;
+ }
+}
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index eed00011a..bb7f20161 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -89,49 +89,47 @@ union PicaShaderConfig {
unsigned num = regs.lighting.light_enable.GetNum(light_index);
const auto& light = regs.lighting.light[num];
state.lighting.light[light_index].num = num;
- state.lighting.light[light_index].directional = light.directional != 0;
- state.lighting.light[light_index].two_sided_diffuse = light.two_sided_diffuse != 0;
+ state.lighting.light[light_index].directional = light.config.directional != 0;
+ state.lighting.light[light_index].two_sided_diffuse = light.config.two_sided_diffuse != 0;
state.lighting.light[light_index].dist_atten_enable = !regs.lighting.IsDistAttenDisabled(num);
- state.lighting.light[light_index].dist_atten_bias = Pica::float20::FromRaw(light.dist_atten_bias).ToFloat32();
- state.lighting.light[light_index].dist_atten_scale = Pica::float20::FromRaw(light.dist_atten_scale).ToFloat32();
}
- state.lighting.lut_d0.enable = regs.lighting.disable_lut_d0 == 0;
+ state.lighting.lut_d0.enable = regs.lighting.config1.disable_lut_d0 == 0;
state.lighting.lut_d0.abs_input = regs.lighting.abs_lut_input.disable_d0 == 0;
state.lighting.lut_d0.type = regs.lighting.lut_input.d0.Value();
state.lighting.lut_d0.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.d0);
- state.lighting.lut_d1.enable = regs.lighting.disable_lut_d1 == 0;
+ state.lighting.lut_d1.enable = regs.lighting.config1.disable_lut_d1 == 0;
state.lighting.lut_d1.abs_input = regs.lighting.abs_lut_input.disable_d1 == 0;
state.lighting.lut_d1.type = regs.lighting.lut_input.d1.Value();
state.lighting.lut_d1.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.d1);
- state.lighting.lut_fr.enable = regs.lighting.disable_lut_fr == 0;
+ state.lighting.lut_fr.enable = regs.lighting.config1.disable_lut_fr == 0;
state.lighting.lut_fr.abs_input = regs.lighting.abs_lut_input.disable_fr == 0;
state.lighting.lut_fr.type = regs.lighting.lut_input.fr.Value();
state.lighting.lut_fr.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.fr);
- state.lighting.lut_rr.enable = regs.lighting.disable_lut_rr == 0;
+ state.lighting.lut_rr.enable = regs.lighting.config1.disable_lut_rr == 0;
state.lighting.lut_rr.abs_input = regs.lighting.abs_lut_input.disable_rr == 0;
state.lighting.lut_rr.type = regs.lighting.lut_input.rr.Value();
state.lighting.lut_rr.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rr);
- state.lighting.lut_rg.enable = regs.lighting.disable_lut_rg == 0;
+ state.lighting.lut_rg.enable = regs.lighting.config1.disable_lut_rg == 0;
state.lighting.lut_rg.abs_input = regs.lighting.abs_lut_input.disable_rg == 0;
state.lighting.lut_rg.type = regs.lighting.lut_input.rg.Value();
state.lighting.lut_rg.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rg);
- state.lighting.lut_rb.enable = regs.lighting.disable_lut_rb == 0;
+ state.lighting.lut_rb.enable = regs.lighting.config1.disable_lut_rb == 0;
state.lighting.lut_rb.abs_input = regs.lighting.abs_lut_input.disable_rb == 0;
state.lighting.lut_rb.type = regs.lighting.lut_input.rb.Value();
state.lighting.lut_rb.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rb);
- state.lighting.config = regs.lighting.config;
- state.lighting.fresnel_selector = regs.lighting.fresnel_selector;
- state.lighting.bump_mode = regs.lighting.bump_mode;
- state.lighting.bump_selector = regs.lighting.bump_selector;
- state.lighting.bump_renorm = regs.lighting.disable_bump_renorm == 0;
- state.lighting.clamp_highlights = regs.lighting.clamp_highlights != 0;
+ state.lighting.config = regs.lighting.config0.config;
+ state.lighting.fresnel_selector = regs.lighting.config0.fresnel_selector;
+ state.lighting.bump_mode = regs.lighting.config0.bump_mode;
+ state.lighting.bump_selector = regs.lighting.config0.bump_selector;
+ state.lighting.bump_renorm = regs.lighting.config0.disable_bump_renorm == 0;
+ state.lighting.clamp_highlights = regs.lighting.config0.clamp_highlights != 0;
return res;
}
@@ -184,8 +182,6 @@ union PicaShaderConfig {
bool directional;
bool two_sided_diffuse;
bool dist_atten_enable;
- GLfloat dist_atten_scale;
- GLfloat dist_atten_bias;
} light[8];
bool enable;
@@ -316,6 +312,8 @@ private:
alignas(16) GLvec3 diffuse;
alignas(16) GLvec3 ambient;
alignas(16) GLvec3 position;
+ GLfloat dist_atten_bias;
+ GLfloat dist_atten_scale;
};
/// Uniform structure for the Uniform Buffer Object, all members must be 16-byte aligned
@@ -330,7 +328,7 @@ private:
LightSrc light_src[8];
};
- static_assert(sizeof(UniformData) == 0x310, "The size of the UniformData structure has changed, update the structure in the shader");
+ static_assert(sizeof(UniformData) == 0x390, "The size of the UniformData structure has changed, update the structure in the shader");
static_assert(sizeof(UniformData) < 16384, "UniformData structure must be less than 16kb as per the OpenGL spec");
/// Sets the OpenGL shader in accordance with the current PICA register state
@@ -339,8 +337,11 @@ private:
/// Syncs the cull mode to match the PICA register
void SyncCullMode();
- /// Syncs the depth scale and offset to match the PICA registers
- void SyncDepthModifiers();
+ /// Syncs the depth scale to match the PICA register
+ void SyncDepthScale();
+
+ /// Syncs the depth offset to match the PICA register
+ void SyncDepthOffset();
/// Syncs the blend enabled status to match the PICA register
void SyncBlendEnabled();
@@ -399,6 +400,12 @@ private:
/// Syncs the specified light's position to match the PICA register
void SyncLightPosition(int light_index);
+ /// Syncs the specified light's distance attenuation bias to match the PICA register
+ void SyncLightDistanceAttenuationBias(int light_index);
+
+ /// Syncs the specified light's distance attenuation scale to match the PICA register
+ void SyncLightDistanceAttenuationScale(int light_index);
+
OpenGLState state;
RasterizerCacheOpenGL res_cache;
@@ -413,7 +420,7 @@ private:
UniformData data;
bool lut_dirty[6];
bool dirty;
- } uniform_block_data;
+ } uniform_block_data = {};
std::array<SamplerInfo, 3> texture_samplers;
OGLVertexArray vertex_array;
@@ -422,5 +429,5 @@ private:
OGLFramebuffer framebuffer;
std::array<OGLTexture, 6> lighting_luts;
- std::array<std::array<GLvec4, 256>, 6> lighting_lut_data;
+ std::array<std::array<GLvec4, 256>, 6> lighting_lut_data{};
};
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp
index 71d60e69c..8332e722d 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp
@@ -439,9 +439,7 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
// If enabled, compute distance attenuation value
std::string dist_atten = "1.0";
if (light_config.dist_atten_enable) {
- std::string scale = std::to_string(light_config.dist_atten_scale);
- std::string bias = std::to_string(light_config.dist_atten_bias);
- std::string index = "(" + scale + " * length(-view - " + light_src + ".position) + " + bias + ")";
+ std::string index = "(" + light_src + ".dist_atten_scale * length(-view - " + light_src + ".position) + " + light_src + ".dist_atten_bias)";
index = "((clamp(" + index + ", 0.0, FLOAT_255)))";
const unsigned lut_num = ((unsigned)Regs::LightingSampler::DistanceAttenuation + light_config.num);
dist_atten = GetLutValue((Regs::LightingSampler)lut_num, index);
@@ -549,6 +547,8 @@ struct LightSrc {
vec3 diffuse;
vec3 ambient;
vec3 position;
+ float dist_atten_bias;
+ float dist_atten_scale;
};
layout (std140) uniform shader_data {
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index 02cd9f417..fa141fc9a 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -36,6 +36,8 @@ OpenGLState::OpenGLState() {
stencil.action_stencil_fail = GL_KEEP;
blend.enabled = false;
+ blend.rgb_equation = GL_FUNC_ADD;
+ blend.a_equation = GL_FUNC_ADD;
blend.src_rgb_func = GL_ONE;
blend.dst_rgb_func = GL_ZERO;
blend.src_a_func = GL_ONE;
@@ -165,6 +167,11 @@ void OpenGLState::Apply() const {
blend.src_a_func, blend.dst_a_func);
}
+ if (blend.rgb_equation != cur_state.blend.rgb_equation ||
+ blend.a_equation != cur_state.blend.a_equation) {
+ glBlendEquationSeparate(blend.rgb_equation, blend.a_equation);
+ }
+
if (logic_op != cur_state.logic_op) {
glLogicOp(logic_op);
}
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index 24f20e47c..228727054 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -40,6 +40,8 @@ public:
struct {
bool enabled; // GL_BLEND
+ GLenum rgb_equation; // GL_BLEND_EQUATION_RGB
+ GLenum a_equation; // GL_BLEND_EQUATION_ALPHA
GLenum src_rgb_func; // GL_BLEND_SRC_RGB
GLenum dst_rgb_func; // GL_BLEND_DST_RGB
GLenum src_a_func; // GL_BLEND_SRC_ALPHA
diff --git a/src/video_core/renderer_opengl/pica_to_gl.h b/src/video_core/renderer_opengl/pica_to_gl.h
index 976d1f364..6dc2758c5 100644
--- a/src/video_core/renderer_opengl/pica_to_gl.h
+++ b/src/video_core/renderer_opengl/pica_to_gl.h
@@ -78,6 +78,26 @@ inline GLenum WrapMode(Pica::Regs::TextureConfig::WrapMode mode) {
return gl_mode;
}
+inline GLenum BlendEquation(Pica::Regs::BlendEquation equation) {
+ static const GLenum blend_equation_table[] = {
+ GL_FUNC_ADD, // BlendEquation::Add
+ GL_FUNC_SUBTRACT, // BlendEquation::Subtract
+ GL_FUNC_REVERSE_SUBTRACT, // BlendEquation::ReverseSubtract
+ GL_MIN, // BlendEquation::Min
+ GL_MAX, // BlendEquation::Max
+ };
+
+ // Range check table for input
+ if (static_cast<size_t>(equation) >= ARRAY_SIZE(blend_equation_table)) {
+ LOG_CRITICAL(Render_OpenGL, "Unknown blend equation %d", equation);
+ UNREACHABLE();
+
+ return GL_FUNC_ADD;
+ }
+
+ return blend_equation_table[(unsigned)equation];
+}
+
inline GLenum BlendFunc(Pica::Regs::BlendFactor factor) {
static const GLenum blend_func_table[] = {
GL_ZERO, // BlendFactor::Zero
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 8f424a435..8410e0a64 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -450,7 +450,7 @@ static const char* GetType(GLenum type) {
#undef RET
}
-static void DebugHandler(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length,
+static void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length,
const GLchar* message, const void* user_param) {
Log::Level level;
switch (severity) {
diff --git a/src/video_core/shader/shader.cpp b/src/video_core/shader/shader.cpp
index e93a9d92a..f565e2c91 100644
--- a/src/video_core/shader/shader.cpp
+++ b/src/video_core/shader/shader.cpp
@@ -30,6 +30,58 @@ namespace Pica {
namespace Shader {
+OutputVertex OutputRegisters::ToVertex(const Regs::ShaderConfig& config) {
+ // Setup output data
+ OutputVertex ret;
+ // TODO(neobrain): Under some circumstances, up to 16 attributes may be output. We need to
+ // figure out what those circumstances are and enable the remaining outputs then.
+ unsigned index = 0;
+ for (unsigned i = 0; i < 7; ++i) {
+
+ if (index >= g_state.regs.vs_output_total)
+ break;
+
+ if ((config.output_mask & (1 << i)) == 0)
+ continue;
+
+ const auto& output_register_map = g_state.regs.vs_output_attributes[index];
+
+ u32 semantics[4] = {
+ output_register_map.map_x, output_register_map.map_y,
+ output_register_map.map_z, output_register_map.map_w
+ };
+
+ for (unsigned comp = 0; comp < 4; ++comp) {
+ float24* out = ((float24*)&ret) + semantics[comp];
+ if (semantics[comp] != Regs::VSOutputAttributes::INVALID) {
+ *out = value[i][comp];
+ } else {
+ // Zero output so that attributes which aren't output won't have denormals in them,
+ // which would slow us down later.
+ memset(out, 0, sizeof(*out));
+ }
+ }
+
+ index++;
+ }
+
+ // The hardware takes the absolute and saturates vertex colors like this, *before* doing interpolation
+ for (unsigned i = 0; i < 4; ++i) {
+ ret.color[i] = float24::FromFloat32(
+ std::fmin(std::fabs(ret.color[i].ToFloat32()), 1.0f));
+ }
+
+ LOG_TRACE(HW_GPU, "Output vertex: pos(%.2f, %.2f, %.2f, %.2f), quat(%.2f, %.2f, %.2f, %.2f), "
+ "col(%.2f, %.2f, %.2f, %.2f), tc0(%.2f, %.2f), view(%.2f, %.2f, %.2f)",
+ ret.pos.x.ToFloat32(), ret.pos.y.ToFloat32(), ret.pos.z.ToFloat32(), ret.pos.w.ToFloat32(),
+ ret.quat.x.ToFloat32(), ret.quat.y.ToFloat32(), ret.quat.z.ToFloat32(), ret.quat.w.ToFloat32(),
+ ret.color.x.ToFloat32(), ret.color.y.ToFloat32(), ret.color.z.ToFloat32(), ret.color.w.ToFloat32(),
+ ret.tc0.u().ToFloat32(), ret.tc0.v().ToFloat32(),
+ ret.view.x.ToFloat32(), ret.view.y.ToFloat32(), ret.view.z.ToFloat32());
+
+ return ret;
+}
+
#ifdef ARCHITECTURE_x86_64
static std::unordered_map<u64, std::unique_ptr<JitShader>> shader_map;
static const JitShader* jit_shader;
@@ -62,8 +114,9 @@ void ShaderSetup::Setup() {
MICROPROFILE_DEFINE(GPU_Shader, "GPU", "Shader", MP_RGB(50, 50, 240));
-OutputVertex ShaderSetup::Run(UnitState<false>& state, const InputVertex& input, int num_attributes) {
+void ShaderSetup::Run(UnitState<false>& state, const InputVertex& input, int num_attributes) {
auto& config = g_state.regs.vs;
+ auto& setup = g_state.vs;
MICROPROFILE_SCOPE(GPU_Shader);
@@ -81,62 +134,13 @@ OutputVertex ShaderSetup::Run(UnitState<false>& state, const InputVertex& input,
#ifdef ARCHITECTURE_x86_64
if (VideoCore::g_shader_jit_enabled)
- jit_shader->Run(&state.registers, g_state.regs.vs.main_offset);
+ jit_shader->Run(setup, state, config.main_offset);
else
- RunInterpreter(state);
+ RunInterpreter(setup, state, config.main_offset);
#else
- RunInterpreter(state);
+ RunInterpreter(setup, state, config.main_offset);
#endif // ARCHITECTURE_x86_64
- // Setup output data
- OutputVertex ret;
- // TODO(neobrain): Under some circumstances, up to 16 attributes may be output. We need to
- // figure out what those circumstances are and enable the remaining outputs then.
- unsigned index = 0;
- for (unsigned i = 0; i < 7; ++i) {
-
- if (index >= g_state.regs.vs_output_total)
- break;
-
- if ((g_state.regs.vs.output_mask & (1 << i)) == 0)
- continue;
-
- const auto& output_register_map = g_state.regs.vs_output_attributes[index]; // TODO: Don't hardcode VS here
-
- u32 semantics[4] = {
- output_register_map.map_x, output_register_map.map_y,
- output_register_map.map_z, output_register_map.map_w
- };
-
- for (unsigned comp = 0; comp < 4; ++comp) {
- float24* out = ((float24*)&ret) + semantics[comp];
- if (semantics[comp] != Regs::VSOutputAttributes::INVALID) {
- *out = state.registers.output[i][comp];
- } else {
- // Zero output so that attributes which aren't output won't have denormals in them,
- // which would slow us down later.
- memset(out, 0, sizeof(*out));
- }
- }
-
- index++;
- }
-
- // The hardware takes the absolute and saturates vertex colors like this, *before* doing interpolation
- for (unsigned i = 0; i < 4; ++i) {
- ret.color[i] = float24::FromFloat32(
- std::fmin(std::fabs(ret.color[i].ToFloat32()), 1.0f));
- }
-
- LOG_TRACE(HW_GPU, "Output vertex: pos(%.2f, %.2f, %.2f, %.2f), quat(%.2f, %.2f, %.2f, %.2f), "
- "col(%.2f, %.2f, %.2f, %.2f), tc0(%.2f, %.2f), view(%.2f, %.2f, %.2f)",
- ret.pos.x.ToFloat32(), ret.pos.y.ToFloat32(), ret.pos.z.ToFloat32(), ret.pos.w.ToFloat32(),
- ret.quat.x.ToFloat32(), ret.quat.y.ToFloat32(), ret.quat.z.ToFloat32(), ret.quat.w.ToFloat32(),
- ret.color.x.ToFloat32(), ret.color.y.ToFloat32(), ret.color.z.ToFloat32(), ret.color.w.ToFloat32(),
- ret.tc0.u().ToFloat32(), ret.tc0.v().ToFloat32(),
- ret.view.x.ToFloat32(), ret.view.y.ToFloat32(), ret.view.z.ToFloat32());
-
- return ret;
}
DebugData<true> ShaderSetup::ProduceDebugInfo(const InputVertex& input, int num_attributes, const Regs::ShaderConfig& config, const ShaderSetup& setup) {
@@ -156,7 +160,7 @@ DebugData<true> ShaderSetup::ProduceDebugInfo(const InputVertex& input, int num_
state.conditional_code[0] = false;
state.conditional_code[1] = false;
- RunInterpreter(state);
+ RunInterpreter(setup, state, config.main_offset);
return state.debug;
}
diff --git a/src/video_core/shader/shader.h b/src/video_core/shader/shader.h
index 983e4a967..fee16df62 100644
--- a/src/video_core/shader/shader.h
+++ b/src/video_core/shader/shader.h
@@ -84,6 +84,15 @@ struct OutputVertex {
static_assert(std::is_pod<OutputVertex>::value, "Structure is not POD");
static_assert(sizeof(OutputVertex) == 32 * sizeof(float), "OutputVertex has invalid size");
+struct OutputRegisters {
+ OutputRegisters() = default;
+
+ alignas(16) Math::Vec4<float24> value[16];
+
+ OutputVertex ToVertex(const Regs::ShaderConfig& config);
+};
+static_assert(std::is_pod<OutputRegisters>::value, "Structure is not POD");
+
// Helper structure used to keep track of data useful for inspection of shader emulation
template<bool full_debugging>
struct DebugData;
@@ -267,11 +276,12 @@ struct UnitState {
// The registers are accessed by the shader JIT using SSE instructions, and are therefore
// required to be 16-byte aligned.
alignas(16) Math::Vec4<float24> input[16];
- alignas(16) Math::Vec4<float24> output[16];
alignas(16) Math::Vec4<float24> temporary[16];
} registers;
static_assert(std::is_pod<Registers>::value, "Structure is not POD");
+ OutputRegisters output_registers;
+
bool conditional_code[2];
// Two Address registers and one loop counter
@@ -283,10 +293,10 @@ struct UnitState {
static size_t InputOffset(const SourceRegister& reg) {
switch (reg.GetRegisterType()) {
case RegisterType::Input:
- return offsetof(UnitState::Registers, input) + reg.GetIndex()*sizeof(Math::Vec4<float24>);
+ return offsetof(UnitState, registers.input) + reg.GetIndex()*sizeof(Math::Vec4<float24>);
case RegisterType::Temporary:
- return offsetof(UnitState::Registers, temporary) + reg.GetIndex()*sizeof(Math::Vec4<float24>);
+ return offsetof(UnitState, registers.temporary) + reg.GetIndex()*sizeof(Math::Vec4<float24>);
default:
UNREACHABLE();
@@ -297,10 +307,10 @@ struct UnitState {
static size_t OutputOffset(const DestRegister& reg) {
switch (reg.GetRegisterType()) {
case RegisterType::Output:
- return offsetof(UnitState::Registers, output) + reg.GetIndex()*sizeof(Math::Vec4<float24>);
+ return offsetof(UnitState, output_registers.value) + reg.GetIndex()*sizeof(Math::Vec4<float24>);
case RegisterType::Temporary:
- return offsetof(UnitState::Registers, temporary) + reg.GetIndex()*sizeof(Math::Vec4<float24>);
+ return offsetof(UnitState, registers.temporary) + reg.GetIndex()*sizeof(Math::Vec4<float24>);
default:
UNREACHABLE();
@@ -323,6 +333,23 @@ struct ShaderSetup {
std::array<Math::Vec4<u8>, 4> i;
} uniforms;
+ static size_t UniformOffset(RegisterType type, unsigned index) {
+ switch (type) {
+ case RegisterType::FloatUniform:
+ return offsetof(ShaderSetup, uniforms.f) + index*sizeof(Math::Vec4<float24>);
+
+ case RegisterType::BoolUniform:
+ return offsetof(ShaderSetup, uniforms.b) + index*sizeof(bool);
+
+ case RegisterType::IntUniform:
+ return offsetof(ShaderSetup, uniforms.i) + index*sizeof(Math::Vec4<u8>);
+
+ default:
+ UNREACHABLE();
+ return 0;
+ }
+ }
+
std::array<u32, 1024> program_code;
std::array<u32, 1024> swizzle_data;
@@ -337,9 +364,8 @@ struct ShaderSetup {
* @param state Shader unit state, must be setup per shader and per shader unit
* @param input Input vertex into the shader
* @param num_attributes The number of vertex shader attributes
- * @return The output vertex, after having been processed by the vertex shader
*/
- OutputVertex Run(UnitState<false>& state, const InputVertex& input, int num_attributes);
+ void Run(UnitState<false>& state, const InputVertex& input, int num_attributes);
/**
* Produce debug information based on the given shader and input vertex
diff --git a/src/video_core/shader/shader_interpreter.cpp b/src/video_core/shader/shader_interpreter.cpp
index 3a827d11f..b1eadc071 100644
--- a/src/video_core/shader/shader_interpreter.cpp
+++ b/src/video_core/shader/shader_interpreter.cpp
@@ -41,11 +41,11 @@ struct CallStackElement {
};
template<bool Debug>
-void RunInterpreter(UnitState<Debug>& state) {
+void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned offset) {
// TODO: Is there a maximal size for this?
boost::container::static_vector<CallStackElement, 16> call_stack;
- u32 program_counter = g_state.regs.vs.main_offset;
+ u32 program_counter = offset;
const auto& uniforms = g_state.vs.uniforms;
const auto& swizzle_data = g_state.vs.swizzle_data;
@@ -144,7 +144,7 @@ void RunInterpreter(UnitState<Debug>& state) {
src2[3] = src2[3] * float24::FromFloat32(-1);
}
- float24* dest = (instr.common.dest.Value() < 0x10) ? &state.registers.output[instr.common.dest.Value().GetIndex()][0]
+ float24* dest = (instr.common.dest.Value() < 0x10) ? &state.output_registers.value[instr.common.dest.Value().GetIndex()][0]
: (instr.common.dest.Value() < 0x20) ? &state.registers.temporary[instr.common.dest.Value().GetIndex()][0]
: dummy_vec4_float24;
@@ -483,7 +483,7 @@ void RunInterpreter(UnitState<Debug>& state) {
src3[3] = src3[3] * float24::FromFloat32(-1);
}
- float24* dest = (instr.mad.dest.Value() < 0x10) ? &state.registers.output[instr.mad.dest.Value().GetIndex()][0]
+ float24* dest = (instr.mad.dest.Value() < 0x10) ? &state.output_registers.value[instr.mad.dest.Value().GetIndex()][0]
: (instr.mad.dest.Value() < 0x20) ? &state.registers.temporary[instr.mad.dest.Value().GetIndex()][0]
: dummy_vec4_float24;
@@ -647,8 +647,8 @@ void RunInterpreter(UnitState<Debug>& state) {
}
// Explicit instantiation
-template void RunInterpreter(UnitState<false>& state);
-template void RunInterpreter(UnitState<true>& state);
+template void RunInterpreter(const ShaderSetup& setup, UnitState<false>& state, unsigned offset);
+template void RunInterpreter(const ShaderSetup& setup, UnitState<true>& state, unsigned offset);
} // namespace
diff --git a/src/video_core/shader/shader_interpreter.h b/src/video_core/shader/shader_interpreter.h
index 6048cdf3a..bb3ce1c6e 100644
--- a/src/video_core/shader/shader_interpreter.h
+++ b/src/video_core/shader/shader_interpreter.h
@@ -11,7 +11,7 @@ namespace Shader {
template <bool Debug> struct UnitState;
template<bool Debug>
-void RunInterpreter(UnitState<Debug>& state);
+void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned offset);
} // namespace
diff --git a/src/video_core/shader/shader_jit_x64.cpp b/src/video_core/shader/shader_jit_x64.cpp
index 99f6c51eb..43e7e6b4c 100644
--- a/src/video_core/shader/shader_jit_x64.cpp
+++ b/src/video_core/shader/shader_jit_x64.cpp
@@ -102,7 +102,7 @@ const JitFunction instr_table[64] = {
// purposes, as documented below:
/// Pointer to the uniform memory
-static const X64Reg UNIFORMS = R9;
+static const X64Reg SETUP = R9;
/// The two 32-bit VS address offset registers set by the MOVA instruction
static const X64Reg ADDROFFS_REG_0 = R10;
static const X64Reg ADDROFFS_REG_1 = R11;
@@ -117,7 +117,7 @@ static const X64Reg COND0 = R13;
/// Result of the previous CMP instruction for the Y-component comparison
static const X64Reg COND1 = R14;
/// Pointer to the UnitState instance for the current VS unit
-static const X64Reg REGISTERS = R15;
+static const X64Reg STATE = R15;
/// SIMD scratch register
static const X64Reg SCRATCH = XMM0;
/// Loaded with the first swizzled source register, otherwise can be used as a scratch register
@@ -136,7 +136,7 @@ static const X64Reg NEGBIT = XMM15;
// State registers that must not be modified by external functions calls
// Scratch registers, e.g., SRC1 and SCRATCH, have to be saved on the side if needed
static const BitSet32 persistent_regs = {
- UNIFORMS, REGISTERS, // Pointers to register blocks
+ SETUP, STATE, // Pointers to register blocks
ADDROFFS_REG_0, ADDROFFS_REG_1, LOOPCOUNT_REG, COND0, COND1, // Cached registers
ONE+16, NEGBIT+16, // Constants
};
@@ -177,10 +177,10 @@ void JitShader::Compile_SwizzleSrc(Instruction instr, unsigned src_num, SourceRe
size_t src_offset;
if (src_reg.GetRegisterType() == RegisterType::FloatUniform) {
- src_ptr = UNIFORMS;
- src_offset = src_reg.GetIndex() * sizeof(float24) * 4;
+ src_ptr = SETUP;
+ src_offset = ShaderSetup::UniformOffset(RegisterType::FloatUniform, src_reg.GetIndex());
} else {
- src_ptr = REGISTERS;
+ src_ptr = STATE;
src_offset = UnitState<false>::InputOffset(src_reg);
}
@@ -264,11 +264,11 @@ void JitShader::Compile_DestEnable(Instruction instr,X64Reg src) {
// If all components are enabled, write the result to the destination register
if (swiz.dest_mask == NO_DEST_REG_MASK) {
// Store dest back to memory
- MOVAPS(MDisp(REGISTERS, dest_offset_disp), src);
+ MOVAPS(MDisp(STATE, dest_offset_disp), src);
} else {
// Not all components are enabled, so mask the result when storing to the destination register...
- MOVAPS(SCRATCH, MDisp(REGISTERS, dest_offset_disp));
+ MOVAPS(SCRATCH, MDisp(STATE, dest_offset_disp));
if (Common::GetCPUCaps().sse4_1) {
u8 mask = ((swiz.dest_mask & 1) << 3) | ((swiz.dest_mask & 8) >> 3) | ((swiz.dest_mask & 2) << 1) | ((swiz.dest_mask & 4) >> 1);
@@ -287,7 +287,7 @@ void JitShader::Compile_DestEnable(Instruction instr,X64Reg src) {
}
// Store dest back to memory
- MOVAPS(MDisp(REGISTERS, dest_offset_disp), SCRATCH);
+ MOVAPS(MDisp(STATE, dest_offset_disp), SCRATCH);
}
}
@@ -336,8 +336,8 @@ void JitShader::Compile_EvaluateCondition(Instruction instr) {
}
void JitShader::Compile_UniformCondition(Instruction instr) {
- int offset = offsetof(decltype(g_state.vs.uniforms), b) + (instr.flow_control.bool_uniform_id * sizeof(bool));
- CMP(sizeof(bool) * 8, MDisp(UNIFORMS, offset), Imm8(0));
+ int offset = ShaderSetup::UniformOffset(RegisterType::BoolUniform, instr.flow_control.bool_uniform_id);
+ CMP(sizeof(bool) * 8, MDisp(SETUP, offset), Imm8(0));
}
BitSet32 JitShader::PersistentCallerSavedRegs() {
@@ -714,8 +714,8 @@ void JitShader::Compile_LOOP(Instruction instr) {
looping = true;
- int offset = offsetof(decltype(g_state.vs.uniforms), i) + (instr.flow_control.int_uniform_id * sizeof(Math::Vec4<u8>));
- MOV(32, R(LOOPCOUNT), MDisp(UNIFORMS, offset));
+ int offset = ShaderSetup::UniformOffset(RegisterType::IntUniform, instr.flow_control.int_uniform_id);
+ MOV(32, R(LOOPCOUNT), MDisp(SETUP, offset));
MOV(32, R(LOOPCOUNT_REG), R(LOOPCOUNT));
SHR(32, R(LOOPCOUNT_REG), Imm8(8));
AND(32, R(LOOPCOUNT_REG), Imm32(0xff)); // Y-component is the start
@@ -826,8 +826,8 @@ void JitShader::Compile() {
// The stack pointer is 8 modulo 16 at the entry of a procedure
ABI_PushRegistersAndAdjustStack(ABI_ALL_CALLEE_SAVED, 8);
- MOV(PTRBITS, R(REGISTERS), R(ABI_PARAM1));
- MOV(PTRBITS, R(UNIFORMS), ImmPtr(&g_state.vs.uniforms));
+ MOV(PTRBITS, R(SETUP), R(ABI_PARAM1));
+ MOV(PTRBITS, R(STATE), R(ABI_PARAM2));
// Zero address/loop registers
XOR(64, R(ADDROFFS_REG_0), R(ADDROFFS_REG_0));
@@ -845,7 +845,7 @@ void JitShader::Compile() {
MOVAPS(NEGBIT, MatR(RAX));
// Jump to start of the shader program
- JMPptr(R(ABI_PARAM2));
+ JMPptr(R(ABI_PARAM3));
// Compile entire program
Compile_Block(static_cast<unsigned>(g_state.vs.program_code.size()));
diff --git a/src/video_core/shader/shader_jit_x64.h b/src/video_core/shader/shader_jit_x64.h
index 30aa7ff30..5468459d4 100644
--- a/src/video_core/shader/shader_jit_x64.h
+++ b/src/video_core/shader/shader_jit_x64.h
@@ -36,8 +36,8 @@ class JitShader : public Gen::XCodeBlock {
public:
JitShader();
- void Run(void* registers, unsigned offset) const {
- program(registers, code_ptr[offset]);
+ void Run(const ShaderSetup& setup, UnitState<false>& state, unsigned offset) const {
+ program(&setup, &state, code_ptr[offset]);
}
void Compile();
@@ -117,7 +117,7 @@ private:
/// Branches that need to be fixed up once the entire shader program is compiled
std::vector<std::pair<Gen::FixupBranch, unsigned>> fixup_branches;
- using CompiledShader = void(void* registers, const u8* start_addr);
+ using CompiledShader = void(const void* setup, void* state, const u8* start_addr);
CompiledShader* program = nullptr;
};
diff --git a/src/video_core/vertex_loader.cpp b/src/video_core/vertex_loader.cpp
index 83896814f..e40f0f1ee 100644
--- a/src/video_core/vertex_loader.cpp
+++ b/src/video_core/vertex_loader.cpp
@@ -2,8 +2,8 @@
#include <boost/range/algorithm/fill.hpp>
-#include "common/assert.h"
#include "common/alignment.h"
+#include "common/assert.h"
#include "common/bit_field.h"
#include "common/common_types.h"
#include "common/logging/log.h"
@@ -21,6 +21,8 @@
namespace Pica {
void VertexLoader::Setup(const Pica::Regs& regs) {
+ ASSERT_MSG(!is_setup, "VertexLoader is not intended to be setup more than once.");
+
const auto& attribute_config = regs.vertex_attributes;
num_total_attributes = attribute_config.GetNumTotalAttributes();
@@ -60,9 +62,13 @@ void VertexLoader::Setup(const Pica::Regs& regs) {
}
}
}
+
+ is_setup = true;
}
void VertexLoader::LoadVertex(u32 base_address, int index, int vertex, Shader::InputVertex& input, DebugUtils::MemoryAccessTracker& memory_accesses) {
+ ASSERT_MSG(is_setup, "A VertexLoader needs to be setup before loading vertices.");
+
for (int i = 0; i < num_total_attributes; ++i) {
if (vertex_attribute_elements[i] != 0) {
// Load per-vertex data from the loader arrays
diff --git a/src/video_core/vertex_loader.h b/src/video_core/vertex_loader.h
index becf5a403..ac162c254 100644
--- a/src/video_core/vertex_loader.h
+++ b/src/video_core/vertex_loader.h
@@ -1,7 +1,8 @@
#pragma once
-#include "common/common_types.h"
+#include <array>
+#include "common/common_types.h"
#include "video_core/pica.h"
namespace Pica {
@@ -11,23 +12,29 @@ class MemoryAccessTracker;
}
namespace Shader {
-class InputVertex;
+struct InputVertex;
}
class VertexLoader {
public:
+ VertexLoader() = default;
+ explicit VertexLoader(const Pica::Regs& regs) {
+ Setup(regs);
+ }
+
void Setup(const Pica::Regs& regs);
void LoadVertex(u32 base_address, int index, int vertex, Shader::InputVertex& input, DebugUtils::MemoryAccessTracker& memory_accesses);
int GetNumTotalAttributes() const { return num_total_attributes; }
private:
- u32 vertex_attribute_sources[16];
- u32 vertex_attribute_strides[16] = {};
- Regs::VertexAttributeFormat vertex_attribute_formats[16] = {};
- u32 vertex_attribute_elements[16] = {};
- bool vertex_attribute_is_default[16];
- int num_total_attributes;
+ std::array<u32, 16> vertex_attribute_sources;
+ std::array<u32, 16> vertex_attribute_strides{};
+ std::array<Regs::VertexAttributeFormat, 16> vertex_attribute_formats;
+ std::array<u32, 16> vertex_attribute_elements{};
+ std::array<bool, 16> vertex_attribute_is_default;
+ int num_total_attributes = 0;
+ bool is_setup = false;
};
} // namespace Pica