Merge pull request #2086 from linkmauve/clang-format

Add clang-format as part of our {commit,travis}-time checks
This commit is contained in:
Yuri Kunde Schlesner 2016-09-21 11:29:48 -07:00 committed by GitHub
commit d5d2ca8058
401 changed files with 19654 additions and 18552 deletions

View File

@ -1,4 +1,4 @@
#!/bin/sh #!/bin/bash
set -e set -e
set -x set -x
@ -9,6 +9,39 @@ if grep -nr '\s$' src *.yml *.txt *.md Doxyfile .gitignore .gitmodules .travis*
exit 1 exit 1
fi fi
# Only run clang-format on Linux because we don't have 4.0 on OS X images
if [ "$TRAVIS_OS_NAME" = "linux" ]; then
# Default clang-format points to default 3.5 version one
CLANG_FORMAT=clang-format-4.0
$CLANG_FORMAT --version
if [ "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then
# Get list of every file modified in this pull request
files_to_lint="$(git diff --name-only --diff-filter=ACMRTUXB $TRAVIS_COMMIT_RANGE | grep '^src/[^.]*[.]\(cpp\|h\)$')"
else
# Check everything for branch pushes
files_to_lint="$(find src/ -name '*.cpp' -or -name '*.h')"
fi
# Turn off tracing for this because it's too verbose
set +x
for f in $files_to_lint; do
d=$(diff -u "$f" <($CLANG_FORMAT "$f"))
if ! [ -z "$d" ]; then
echo "!!! $f not compliant to coding style, here is the fix:"
echo "$d"
fail=1
fi
done
set -x
if [ "$fail" = 1 ]; then
exit 1
fi
fi
#if OS is linux or is not set #if OS is linux or is not set
if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then
export CC=gcc-6 export CC=gcc-6

View File

@ -17,6 +17,7 @@ addons:
apt: apt:
sources: sources:
- ubuntu-toolchain-r-test - ubuntu-toolchain-r-test
- llvm-toolchain-precise
packages: packages:
- gcc-6 - gcc-6
- g++-6 - g++-6
@ -25,6 +26,7 @@ addons:
- xorg-dev - xorg-dev
- lib32stdc++6 # For CMake - lib32stdc++6 # For CMake
- lftp # To upload builds - lftp # To upload builds
- clang-format-4.0
cache: cache:
directories: directories:

View File

@ -1,4 +1,4 @@
#!/bin/sh #!/bin/bash
# Enforce citra's whitespace policy # Enforce citra's whitespace policy
git config --local core.whitespace tab-in-indent,trailing-space git config --local core.whitespace tab-in-indent,trailing-space
@ -7,7 +7,7 @@ paths_to_check="src/ CMakeLists.txt"
# If there are whitespace errors, print the offending file names and fail. # If there are whitespace errors, print the offending file names and fail.
if ! git diff --cached --check -- $paths_to_check ; then if ! git diff --cached --check -- $paths_to_check ; then
cat<<END; cat<<END
Error: This commit would contain trailing spaces or tabs, which is against this repo's policy. Error: This commit would contain trailing spaces or tabs, which is against this repo's policy.
Please correct those issues before commiting. (Use 'git diff --check' for more details) Please correct those issues before commiting. (Use 'git diff --check' for more details)
@ -18,9 +18,26 @@ fi
# Check for tabs, since tab-in-indent catches only those at the beginning of a line # Check for tabs, since tab-in-indent catches only those at the beginning of a line
if git diff --cached -- $paths_to_check | egrep '^\+.* '; then if git diff --cached -- $paths_to_check | egrep '^\+.* '; then
cat<<END; cat<<END
Error: This commit would contain a tab, which is against this repo's policy. Error: This commit would contain a tab, which is against this repo's policy.
If you know what you are doing, you can try 'git commit --no-verify' to bypass the check. If you know what you are doing, you can try 'git commit --no-verify' to bypass the check.
END END
exit 1 exit 1
fi fi
for f in $(git diff --name-only --diff-filter=ACMRTUXB --cached); do
if ! echo "$f" | egrep -q "[.](cpp|h)$"; then
continue
fi
if ! echo "$f" | egrep -q "^src/"; then
continue
fi
d=$(diff -u "$f" <(clang-format "$f"))
if ! [ -z "$d" ]; then
echo "!!! $f not compliant to coding style, here is the fix:"
echo "$d"
fail=1
fi
done
exit "$fail"

88
src/.clang-format Normal file
View File

@ -0,0 +1,88 @@
---
Language: Cpp
# BasedOnStyle: LLVM
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlinesLeft: false
AlignOperands: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: true
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
ColumnLimit: 100
CommentPragmas: '^ IWYU pragma:'
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
IncludeCategories:
- Regex: '^\<[^Q][^/.>]*\>'
Priority: -2
- Regex: '^\<'
Priority: -1
- Regex: '^\"'
Priority: 0
IndentCaseLabels: false
IndentWidth: 4
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: true
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 150
PointerAlignment: Left
ReflowComments: true
SortIncludes: true
SpaceAfterCStyleCast: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
TabWidth: 4
UseTab: Never
...

View File

@ -4,14 +4,12 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include "audio_core/audio_core.h" #include "audio_core/audio_core.h"
#include "audio_core/hle/dsp.h" #include "audio_core/hle/dsp.h"
#include "audio_core/hle/pipe.h" #include "audio_core/hle/pipe.h"
#include "audio_core/null_sink.h" #include "audio_core/null_sink.h"
#include "audio_core/sink.h" #include "audio_core/sink.h"
#include "audio_core/sink_details.h" #include "audio_core/sink_details.h"
#include "core/core_timing.h" #include "core/core_timing.h"
#include "core/hle/kernel/vm_manager.h" #include "core/hle/kernel/vm_manager.h"
#include "core/hle/service/dsp_dsp.h" #include "core/hle/service/dsp_dsp.h"
@ -42,10 +40,18 @@ void Init() {
} }
void AddAddressSpace(Kernel::VMManager& address_space) { void AddAddressSpace(Kernel::VMManager& address_space) {
auto r0_vma = address_space.MapBackingMemory(DSP::HLE::region0_base, reinterpret_cast<u8*>(&DSP::HLE::g_regions[0]), sizeof(DSP::HLE::SharedMemory), Kernel::MemoryState::IO).MoveFrom(); auto r0_vma = address_space
.MapBackingMemory(DSP::HLE::region0_base,
reinterpret_cast<u8*>(&DSP::HLE::g_regions[0]),
sizeof(DSP::HLE::SharedMemory), Kernel::MemoryState::IO)
.MoveFrom();
address_space.Reprotect(r0_vma, Kernel::VMAPermission::ReadWrite); address_space.Reprotect(r0_vma, Kernel::VMAPermission::ReadWrite);
auto r1_vma = address_space.MapBackingMemory(DSP::HLE::region1_base, reinterpret_cast<u8*>(&DSP::HLE::g_regions[1]), sizeof(DSP::HLE::SharedMemory), Kernel::MemoryState::IO).MoveFrom(); auto r1_vma = address_space
.MapBackingMemory(DSP::HLE::region1_base,
reinterpret_cast<u8*>(&DSP::HLE::g_regions[1]),
sizeof(DSP::HLE::SharedMemory), Kernel::MemoryState::IO)
.MoveFrom();
address_space.Reprotect(r1_vma, Kernel::VMAPermission::ReadWrite); address_space.Reprotect(r1_vma, Kernel::VMAPermission::ReadWrite);
} }
@ -58,9 +64,9 @@ void SelectSink(std::string sink_id) {
return; return;
} }
auto iter = std::find_if(g_sink_details.begin(), g_sink_details.end(), [sink_id](const auto& sink_detail) { auto iter =
return sink_detail.id == sink_id; std::find_if(g_sink_details.begin(), g_sink_details.end(),
}); [sink_id](const auto& sink_detail) { return sink_detail.id == sink_id; });
if (iter == g_sink_details.end()) { if (iter == g_sink_details.end()) {
LOG_ERROR(Audio, "AudioCore::SelectSink given invalid sink_id"); LOG_ERROR(Audio, "AudioCore::SelectSink given invalid sink_id");

View File

@ -6,31 +6,32 @@
#include <cstddef> #include <cstddef>
#include <cstring> #include <cstring>
#include <vector> #include <vector>
#include "audio_core/codec.h" #include "audio_core/codec.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/math_util.h" #include "common/math_util.h"
namespace Codec { namespace Codec {
StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count, const std::array<s16, 16>& adpcm_coeff, ADPCMState& state) { StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count,
const std::array<s16, 16>& adpcm_coeff, ADPCMState& state) {
// GC-ADPCM with scale factor and variable coefficients. // GC-ADPCM with scale factor and variable coefficients.
// Frames are 8 bytes long containing 14 samples each. // Frames are 8 bytes long containing 14 samples each.
// Samples are 4 bits (one nibble) long. // Samples are 4 bits (one nibble) long.
constexpr size_t FRAME_LEN = 8; constexpr size_t FRAME_LEN = 8;
constexpr size_t SAMPLES_PER_FRAME = 14; constexpr size_t SAMPLES_PER_FRAME = 14;
constexpr std::array<int, 16> SIGNED_NIBBLES {{ 0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1 }}; constexpr std::array<int, 16> SIGNED_NIBBLES = {
{0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1}};
const size_t ret_size = sample_count % 2 == 0 ? sample_count : sample_count + 1; // Ensure multiple of two. const size_t ret_size =
sample_count % 2 == 0 ? sample_count : sample_count + 1; // Ensure multiple of two.
StereoBuffer16 ret(ret_size); StereoBuffer16 ret(ret_size);
int yn1 = state.yn1, int yn1 = state.yn1, yn2 = state.yn2;
yn2 = state.yn2;
const size_t NUM_FRAMES = (sample_count + (SAMPLES_PER_FRAME - 1)) / SAMPLES_PER_FRAME; // Round up. const size_t NUM_FRAMES =
(sample_count + (SAMPLES_PER_FRAME - 1)) / SAMPLES_PER_FRAME; // Round up.
for (size_t framei = 0; framei < NUM_FRAMES; framei++) { for (size_t framei = 0; framei < NUM_FRAMES; framei++) {
const int frame_header = data[framei * FRAME_LEN]; const int frame_header = data[framei * FRAME_LEN];
const int scale = 1 << (frame_header & 0xF); const int scale = 1 << (frame_header & 0xF);
@ -43,7 +44,8 @@ StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count, cons
// Decodes an audio sample. One nibble produces one sample. // Decodes an audio sample. One nibble produces one sample.
const auto decode_sample = [&](const int nibble) -> s16 { const auto decode_sample = [&](const int nibble) -> s16 {
const int xn = nibble * scale; const int xn = nibble * scale;
// We first transform everything into 11 bit fixed point, perform the second order digital filter, then transform back. // We first transform everything into 11 bit fixed point, perform the second order
// digital filter, then transform back.
// 0x400 == 0.5 in 11 bit fixed point. // 0x400 == 0.5 in 11 bit fixed point.
// Filter: y[n] = x[n] + 0.5 + c1 * y[n-1] + c2 * y[n-2] // Filter: y[n] = x[n] + 0.5 + c1 * y[n-1] + c2 * y[n-2]
int val = ((xn << 11) + 0x400 + coef1 * yn1 + coef2 * yn2) >> 11; int val = ((xn << 11) + 0x400 + coef1 * yn1 + coef2 * yn2) >> 11;
@ -82,7 +84,8 @@ static s16 SignExtendS8(u8 x) {
return static_cast<s16>(static_cast<s8>(x)); return static_cast<s16>(static_cast<s8>(x));
} }
StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data, const size_t sample_count) { StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data,
const size_t sample_count) {
ASSERT(num_channels == 1 || num_channels == 2); ASSERT(num_channels == 1 || num_channels == 2);
StereoBuffer16 ret(sample_count); StereoBuffer16 ret(sample_count);
@ -101,7 +104,8 @@ StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data, con
return ret; return ret;
} }
StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data, const size_t sample_count) { StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data,
const size_t sample_count) {
ASSERT(num_channels == 1 || num_channels == 2); ASSERT(num_channels == 1 || num_channels == 2);
StereoBuffer16 ret(sample_count); StereoBuffer16 ret(sample_count);
@ -118,5 +122,4 @@ StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data, co
return ret; return ret;
} }
}; };

View File

@ -6,7 +6,6 @@
#include <array> #include <array>
#include <vector> #include <vector>
#include "common/common_types.h" #include "common/common_types.h"
namespace Codec { namespace Codec {
@ -29,7 +28,8 @@ struct ADPCMState {
* @param state ADPCM state, this is updated with new state * @param state ADPCM state, this is updated with new state
* @return Decoded stereo signed PCM16 data, sample_count in length * @return Decoded stereo signed PCM16 data, sample_count in length
*/ */
StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count, const std::array<s16, 16>& adpcm_coeff, ADPCMState& state); StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count,
const std::array<s16, 16>& adpcm_coeff, ADPCMState& state);
/** /**
* @param num_channels Number of channels * @param num_channels Number of channels
@ -37,7 +37,8 @@ StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count, cons
* @param sample_count Length of buffer in terms of number of samples * @param sample_count Length of buffer in terms of number of samples
* @return Decoded stereo signed PCM16 data, sample_count in length * @return Decoded stereo signed PCM16 data, sample_count in length
*/ */
StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data, const size_t sample_count); StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data,
const size_t sample_count);
/** /**
* @param num_channels Number of channels * @param num_channels Number of channels
@ -45,6 +46,6 @@ StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data, con
* @param sample_count Length of buffer in terms of number of samples * @param sample_count Length of buffer in terms of number of samples
* @return Decoded stereo signed PCM16 data, sample_count in length * @return Decoded stereo signed PCM16 data, sample_count in length
*/ */
StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data, const size_t sample_count); StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data,
const size_t sample_count);
}; };

View File

@ -6,7 +6,6 @@
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include "common/common_types.h" #include "common/common_types.h"
namespace DSP { namespace DSP {
@ -25,11 +24,10 @@ using QuadFrame32 = std::array<std::array<s32, 4>, samples_per_frame>;
* This performs the filter operation defined by FilterT::ProcessSample on the frame in-place. * This performs the filter operation defined by FilterT::ProcessSample on the frame in-place.
* FilterT::ProcessSample is called sequentially on the samples. * FilterT::ProcessSample is called sequentially on the samples.
*/ */
template<typename FrameT, typename FilterT> template <typename FrameT, typename FilterT>
void FilterFrame(FrameT& frame, FilterT& filter) { void FilterFrame(FrameT& frame, FilterT& filter) {
std::transform(frame.begin(), frame.end(), frame.begin(), [&filter](const auto& sample) { std::transform(frame.begin(), frame.end(), frame.begin(),
return filter.ProcessSample(sample); [&filter](const auto& sample) { return filter.ProcessSample(sample); });
});
} }
} // namespace HLE } // namespace HLE

View File

@ -4,7 +4,6 @@
#include <array> #include <array>
#include <memory> #include <memory>
#include "audio_core/hle/dsp.h" #include "audio_core/hle/dsp.h"
#include "audio_core/hle/mixers.h" #include "audio_core/hle/mixers.h"
#include "audio_core/hle/pipe.h" #include "audio_core/hle/pipe.h"
@ -47,10 +46,9 @@ static SharedMemory& WriteRegion() {
// Audio processing and mixing // Audio processing and mixing
static std::array<Source, num_sources> sources = { static std::array<Source, num_sources> sources = {
Source(0), Source(1), Source(2), Source(3), Source(4), Source(5), Source(0), Source(1), Source(2), Source(3), Source(4), Source(5), Source(6), Source(7),
Source(6), Source(7), Source(8), Source(9), Source(10), Source(11), Source(8), Source(9), Source(10), Source(11), Source(12), Source(13), Source(14), Source(15),
Source(12), Source(13), Source(14), Source(15), Source(16), Source(17), Source(16), Source(17), Source(18), Source(19), Source(20), Source(21), Source(22), Source(23),
Source(18), Source(19), Source(20), Source(21), Source(22), Source(23)
}; };
static Mixers mixers; static Mixers mixers;
@ -62,14 +60,16 @@ static StereoFrame16 GenerateCurrentFrame() {
// Generate intermediate mixes // Generate intermediate mixes
for (size_t i = 0; i < num_sources; i++) { 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]); 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++) { for (size_t mix = 0; mix < 3; mix++) {
sources[i].MixInto(intermediate_mixes[mix], mix); sources[i].MixInto(intermediate_mixes[mix], mix);
} }
} }
// Generate final mix // Generate final mix
write.dsp_status = mixers.Tick(read.dsp_configuration, read.intermediate_mix_samples, write.intermediate_mix_samples, intermediate_mixes); write.dsp_status = mixers.Tick(read.dsp_configuration, read.intermediate_mix_samples,
write.intermediate_mix_samples, intermediate_mixes);
StereoFrame16 output_frame = mixers.GetOutput(); StereoFrame16 output_frame = mixers.GetOutput();
@ -152,7 +152,8 @@ void Shutdown() {
bool Tick() { bool Tick() {
StereoFrame16 current_frame = {}; StereoFrame16 current_frame = {};
// TODO: Check dsp::DSP semaphore (which indicates emulated application has finished writing to shared memory region) // TODO: Check dsp::DSP semaphore (which indicates emulated application has finished writing to
// shared memory region)
current_frame = GenerateCurrentFrame(); current_frame = GenerateCurrentFrame();
OutputCurrentFrame(current_frame); OutputCurrentFrame(current_frame);

View File

@ -8,9 +8,7 @@
#include <cstddef> #include <cstddef>
#include <memory> #include <memory>
#include <type_traits> #include <type_traits>
#include "audio_core/hle/common.h" #include "audio_core/hle/common.h"
#include "common/bit_field.h" #include "common/bit_field.h"
#include "common/common_funcs.h" #include "common/common_funcs.h"
#include "common/common_types.h" #include "common/common_types.h"
@ -23,15 +21,15 @@ class Sink;
namespace DSP { namespace DSP {
namespace HLE { namespace HLE {
// The application-accessible region of DSP memory consists of two parts. // The application-accessible region of DSP memory consists of two parts. Both are marked as IO and
// Both are marked as IO and have Read/Write permissions. // have Read/Write permissions.
// //
// First Region: 0x1FF50000 (Size: 0x8000) // First Region: 0x1FF50000 (Size: 0x8000)
// Second Region: 0x1FF70000 (Size: 0x8000) // Second Region: 0x1FF70000 (Size: 0x8000)
// //
// The DSP reads from each region alternately based on the frame counter for each region much like a // The DSP reads from each region alternately based on the frame counter for each region much like a
// double-buffer. The frame counter is located as the very last u16 of each region and is incremented // double-buffer. The frame counter is located as the very last u16 of each region and is
// each audio tick. // incremented each audio tick.
constexpr VAddr region0_base = 0x1FF50000; constexpr VAddr region0_base = 0x1FF50000;
constexpr VAddr region1_base = 0x1FF70000; constexpr VAddr region1_base = 0x1FF70000;
@ -56,6 +54,7 @@ struct u32_dsp {
void operator=(u32 new_value) { void operator=(u32 new_value) {
storage = Convert(new_value); storage = Convert(new_value);
} }
private: private:
static constexpr u32 Convert(u32 value) { static constexpr u32 Convert(u32 value) {
return (value << 16) | (value >> 16); return (value << 16) | (value >> 16);
@ -89,13 +88,13 @@ static_assert(std::is_trivially_copyable<u32_dsp>::value, "u32_dsp isn't trivial
// #: This refers to the order in which they appear in the DspPipe::Audio DSP pipe. // #: This refers to the order in which they appear in the DspPipe::Audio DSP pipe.
// See also: DSP::HLE::PipeRead. // See also: DSP::HLE::PipeRead.
// //
// Note that the above addresses do vary slightly between audio firmwares observed; the addresses are // Note that the above addresses do vary slightly between audio firmwares observed; the addresses
// not fixed in stone. The addresses above are only an examplar; they're what this implementation // are not fixed in stone. The addresses above are only an examplar; they're what this
// does and provides to applications. // implementation does and provides to applications.
// //
// Application requests the DSP service to convert DSP addresses into ARM11 virtual addresses using the // Application requests the DSP service to convert DSP addresses into ARM11 virtual addresses using
// ConvertProcessAddressFromDspDram service call. Applications seem to derive the addresses for the // the ConvertProcessAddressFromDspDram service call. Applications seem to derive the addresses for
// second region via: // the second region via:
// second_region_dsp_addr = first_region_dsp_addr | 0x10000 // second_region_dsp_addr = first_region_dsp_addr | 0x10000
// //
// Applications maintain most of its own audio state, the memory region is used mainly for // Applications maintain most of its own audio state, the memory region is used mainly for
@ -110,13 +109,16 @@ static_assert(std::is_trivially_copyable<u32_dsp>::value, "u32_dsp isn't trivial
// GCC versions < 5.0 do not implement std::is_trivially_copyable. // GCC versions < 5.0 do not implement std::is_trivially_copyable.
// Excluding MSVC because it has weird behaviour for std::is_trivially_copyable. // Excluding MSVC because it has weird behaviour for std::is_trivially_copyable.
#if (__GNUC__ >= 5) || defined(__clang__) #if (__GNUC__ >= 5) || defined(__clang__)
#define ASSERT_DSP_STRUCT(name, size) \ #define ASSERT_DSP_STRUCT(name, size) \
static_assert(std::is_standard_layout<name>::value, "DSP structure " #name " doesn't use standard layout"); \ static_assert(std::is_standard_layout<name>::value, \
static_assert(std::is_trivially_copyable<name>::value, "DSP structure " #name " isn't trivially copyable"); \ "DSP structure " #name " doesn't use standard layout"); \
static_assert(std::is_trivially_copyable<name>::value, \
"DSP structure " #name " isn't trivially copyable"); \
static_assert(sizeof(name) == (size), "Unexpected struct size for DSP structure " #name) static_assert(sizeof(name) == (size), "Unexpected struct size for DSP structure " #name)
#else #else
#define ASSERT_DSP_STRUCT(name, size) \ #define ASSERT_DSP_STRUCT(name, size) \
static_assert(std::is_standard_layout<name>::value, "DSP structure " #name " doesn't use standard layout"); \ static_assert(std::is_standard_layout<name>::value, \
"DSP structure " #name " doesn't use standard layout"); \
static_assert(sizeof(name) == (size), "Unexpected struct size for DSP structure " #name) static_assert(sizeof(name) == (size), "Unexpected struct size for DSP structure " #name)
#endif #endif
@ -130,7 +132,8 @@ struct SourceConfiguration {
BitField<0, 1, u32_le> format_dirty; BitField<0, 1, u32_le> format_dirty;
BitField<1, 1, u32_le> mono_or_stereo_dirty; BitField<1, 1, u32_le> mono_or_stereo_dirty;
BitField<2, 1, u32_le> adpcm_coefficients_dirty; BitField<2, 1, u32_le> adpcm_coefficients_dirty;
BitField<3, 1, u32_le> partial_embedded_buffer_dirty; ///< Tends to be set when a looped buffer is queued. /// Tends to be set when a looped buffer is queued.
BitField<3, 1, u32_le> partial_embedded_buffer_dirty;
BitField<4, 1, u32_le> partial_reset_flag; BitField<4, 1, u32_le> partial_reset_flag;
BitField<16, 1, u32_le> enable_dirty; BitField<16, 1, u32_le> enable_dirty;
@ -138,7 +141,8 @@ struct SourceConfiguration {
BitField<18, 1, u32_le> rate_multiplier_dirty; BitField<18, 1, u32_le> rate_multiplier_dirty;
BitField<19, 1, u32_le> buffer_queue_dirty; BitField<19, 1, u32_le> buffer_queue_dirty;
BitField<20, 1, u32_le> loop_related_dirty; BitField<20, 1, u32_le> loop_related_dirty;
BitField<21, 1, u32_le> play_position_dirty; ///< Tends to also be set when embedded buffer is updated. /// Tends to also be set when embedded buffer is updated.
BitField<21, 1, u32_le> play_position_dirty;
BitField<22, 1, u32_le> filters_enabled_dirty; BitField<22, 1, u32_le> filters_enabled_dirty;
BitField<23, 1, u32_le> simple_filter_dirty; BitField<23, 1, u32_le> simple_filter_dirty;
BitField<24, 1, u32_le> biquad_filter_dirty; BitField<24, 1, u32_le> biquad_filter_dirty;
@ -153,9 +157,9 @@ struct SourceConfiguration {
// Gain control // Gain control
/** /**
* Gain is between 0.0-1.0. This determines how much will this source appear on * Gain is between 0.0-1.0. This determines how much will this source appear on each of the
* each of the 12 channels that feed into the intermediate mixers. * 12 channels that feed into the intermediate mixers. Each of the three intermediate mixers
* Each of the three intermediate mixers is fed two left and two right channels. * is fed two left and two right channels.
*/ */
float_le gain[3][4]; float_le gain[3][4];
@ -167,7 +171,7 @@ struct SourceConfiguration {
enum class InterpolationMode : u8 { enum class InterpolationMode : u8 {
Polyphase = 0, Polyphase = 0,
Linear = 1, Linear = 1,
None = 2 None = 2,
}; };
InterpolationMode interpolation_mode; InterpolationMode interpolation_mode;
@ -191,8 +195,8 @@ struct SourceConfiguration {
* This is a normalised biquad filter (second-order). * This is a normalised biquad filter (second-order).
* The transfer function of this filter is: * The transfer function of this filter is:
* H(z) = (b0 + b1 z^-1 + b2 z^-2) / (1 - a1 z^-1 - a2 z^-2) * H(z) = (b0 + b1 z^-1 + b2 z^-2) / (1 - a1 z^-1 - a2 z^-2)
* Nintendo chose to negate the feedbackward coefficients. This differs from standard notation * Nintendo chose to negate the feedbackward coefficients. This differs from standard
* as in: https://ccrma.stanford.edu/~jos/filters/Direct_Form_I.html * notation as in: https://ccrma.stanford.edu/~jos/filters/Direct_Form_I.html
* Values are signed fixed point with 14 fractional bits. * Values are signed fixed point with 14 fractional bits.
*/ */
struct BiquadFilter { struct BiquadFilter {
@ -239,8 +243,9 @@ struct SourceConfiguration {
/// Is a looping buffer. /// Is a looping buffer.
u8 is_looping; u8 is_looping;
/// This value is shown in SourceStatus::previous_buffer_id when this buffer has finished. /// This value is shown in SourceStatus::previous_buffer_id when this buffer has
/// This allows the emulated application to tell what buffer is currently playing /// finished. This allows the emulated application to tell what buffer is currently
/// playing.
u16_le buffer_id; u16_le buffer_id;
INSERT_PADDING_DSPWORDS(1); INSERT_PADDING_DSPWORDS(1);
@ -270,13 +275,13 @@ struct SourceConfiguration {
enum class MonoOrStereo : u16_le { enum class MonoOrStereo : u16_le {
Mono = 1, Mono = 1,
Stereo = 2 Stereo = 2,
}; };
enum class Format : u16_le { enum class Format : u16_le {
PCM8 = 0, PCM8 = 0,
PCM16 = 1, PCM16 = 1,
ADPCM = 2 ADPCM = 2,
}; };
union { union {
@ -302,7 +307,8 @@ struct SourceConfiguration {
BitField<1, 1, u16_le> is_looping; ///< Is this a looping buffer? BitField<1, 1, u16_le> is_looping; ///< Is this a looping buffer?
}; };
/// Buffer id of embedded buffer (used as a buffer id in SourceStatus to reference this buffer). /// Buffer id of embedded buffer (used as a buffer id in SourceStatus to reference this
/// buffer).
u16_le buffer_id; u16_le buffer_id;
}; };
@ -347,7 +353,8 @@ struct DspConfiguration {
BitField<28, 1, u32_le> headphones_connected_dirty; BitField<28, 1, u32_le> headphones_connected_dirty;
}; };
/// The DSP has three intermediate audio mixers. This controls the volume level (0.0-1.0) for each at the final mixer /// The DSP has three intermediate audio mixers. This controls the volume level (0.0-1.0) for
/// each at the final mixer.
float_le volume[3]; float_le volume[3];
INSERT_PADDING_DSPWORDS(3); INSERT_PADDING_DSPWORDS(3);
@ -355,7 +362,7 @@ struct DspConfiguration {
enum class OutputFormat : u16_le { enum class OutputFormat : u16_le {
Mono = 0, Mono = 0,
Stereo = 1, Stereo = 1,
Surround = 2 Surround = 2,
}; };
OutputFormat output_format; OutputFormat output_format;
@ -388,8 +395,10 @@ struct DspConfiguration {
u16_le enable; u16_le enable;
INSERT_PADDING_DSPWORDS(1); INSERT_PADDING_DSPWORDS(1);
u16_le outputs; u16_le outputs;
u32_dsp work_buffer_address; ///< The application allocates a block of memory for the DSP to use as a work buffer. /// The application allocates a block of memory for the DSP to use as a work buffer.
u16_le frame_count; ///< Frames to delay by u32_dsp work_buffer_address;
/// Frames to delay by
u16_le frame_count;
// Coefficients // Coefficients
s16_le g; ///< Fixed point with 7 fractional bits s16_le g; ///< Fixed point with 7 fractional bits
@ -506,21 +515,36 @@ ASSERT_DSP_STRUCT(SharedMemory, 0x8000);
extern std::array<SharedMemory, 2> g_regions; extern std::array<SharedMemory, 2> g_regions;
// Structures must have an offset that is a multiple of two. // Structures must have an offset that is a multiple of two.
static_assert(offsetof(SharedMemory, frame_counter) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); static_assert(offsetof(SharedMemory, frame_counter) % 2 == 0,
static_assert(offsetof(SharedMemory, source_configurations) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, source_statuses) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); static_assert(offsetof(SharedMemory, source_configurations) % 2 == 0,
static_assert(offsetof(SharedMemory, adpcm_coefficients) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, dsp_configuration) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); static_assert(offsetof(SharedMemory, source_statuses) % 2 == 0,
static_assert(offsetof(SharedMemory, dsp_status) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, final_samples) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); static_assert(offsetof(SharedMemory, adpcm_coefficients) % 2 == 0,
static_assert(offsetof(SharedMemory, intermediate_mix_samples) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, compressor) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); static_assert(offsetof(SharedMemory, dsp_configuration) % 2 == 0,
static_assert(offsetof(SharedMemory, dsp_debug) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, unknown10) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); static_assert(offsetof(SharedMemory, dsp_status) % 2 == 0,
static_assert(offsetof(SharedMemory, unknown11) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, unknown12) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); static_assert(offsetof(SharedMemory, final_samples) % 2 == 0,
static_assert(offsetof(SharedMemory, unknown13) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, unknown14) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); static_assert(offsetof(SharedMemory, intermediate_mix_samples) % 2 == 0,
"Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, compressor) % 2 == 0,
"Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, dsp_debug) % 2 == 0,
"Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, unknown10) % 2 == 0,
"Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, unknown11) % 2 == 0,
"Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, unknown12) % 2 == 0,
"Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, unknown13) % 2 == 0,
"Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, unknown14) % 2 == 0,
"Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
#undef INSERT_PADDING_DSPWORDS #undef INSERT_PADDING_DSPWORDS
#undef ASSERT_DSP_STRUCT #undef ASSERT_DSP_STRUCT

View File

@ -4,11 +4,9 @@
#include <array> #include <array>
#include <cstddef> #include <cstddef>
#include "audio_core/hle/common.h" #include "audio_core/hle/common.h"
#include "audio_core/hle/dsp.h" #include "audio_core/hle/dsp.h"
#include "audio_core/hle/filter.h" #include "audio_core/hle/filter.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/math_util.h" #include "common/math_util.h"
@ -59,7 +57,9 @@ void SourceFilters::SimpleFilter::Reset() {
b0 = 1 << 15; b0 = 1 << 15;
} }
void SourceFilters::SimpleFilter::Configure(SourceConfiguration::Configuration::SimpleFilter config) { void SourceFilters::SimpleFilter::Configure(
SourceConfiguration::Configuration::SimpleFilter config) {
a1 = config.a1; a1 = config.a1;
b0 = config.b0; b0 = config.b0;
} }
@ -88,7 +88,9 @@ void SourceFilters::BiquadFilter::Reset() {
b0 = 1 << 14; b0 = 1 << 14;
} }
void SourceFilters::BiquadFilter::Configure(SourceConfiguration::Configuration::BiquadFilter config) { void SourceFilters::BiquadFilter::Configure(
SourceConfiguration::Configuration::BiquadFilter config) {
a1 = config.a1; a1 = config.a1;
a2 = config.a2; a2 = config.a2;
b0 = config.b0; b0 = config.b0;

View File

@ -5,10 +5,8 @@
#pragma once #pragma once
#include <array> #include <array>
#include "audio_core/hle/common.h" #include "audio_core/hle/common.h"
#include "audio_core/hle/dsp.h" #include "audio_core/hle/dsp.h"
#include "common/common_types.h" #include "common/common_types.h"
namespace DSP { namespace DSP {
@ -17,7 +15,9 @@ namespace HLE {
/// Preprocessing filters. There is an independent set of filters for each Source. /// Preprocessing filters. There is an independent set of filters for each Source.
class SourceFilters final { class SourceFilters final {
public: public:
SourceFilters() { Reset(); } SourceFilters() {
Reset();
}
/// Reset internal state. /// Reset internal state.
void Reset(); void Reset();
@ -54,7 +54,9 @@ private:
bool biquad_filter_enabled; bool biquad_filter_enabled;
struct SimpleFilter { struct SimpleFilter {
SimpleFilter() { Reset(); } SimpleFilter() {
Reset();
}
/// Resets internal state. /// Resets internal state.
void Reset(); void Reset();
@ -80,7 +82,9 @@ private:
} simple_filter; } simple_filter;
struct BiquadFilter { struct BiquadFilter {
BiquadFilter() { Reset(); } BiquadFilter() {
Reset();
}
/// Resets internal state. /// Resets internal state.
void Reset(); void Reset();

View File

@ -7,7 +7,6 @@
#include "audio_core/hle/common.h" #include "audio_core/hle/common.h"
#include "audio_core/hle/dsp.h" #include "audio_core/hle/dsp.h"
#include "audio_core/hle/mixers.h" #include "audio_core/hle/mixers.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/math_util.h" #include "common/math_util.h"
@ -20,11 +19,9 @@ void Mixers::Reset() {
state = {}; state = {};
} }
DspStatus Mixers::Tick(DspConfiguration& config, DspStatus Mixers::Tick(DspConfiguration& config, const IntermediateMixSamples& read_samples,
const IntermediateMixSamples& read_samples,
IntermediateMixSamples& write_samples, IntermediateMixSamples& write_samples,
const std::array<QuadFrame32, 3>& input) const std::array<QuadFrame32, 3>& input) {
{
ParseConfig(config); ParseConfig(config);
AuxReturn(read_samples); AuxReturn(read_samples);
@ -73,13 +70,14 @@ void Mixers::ParseConfig(DspConfiguration& config) {
if (config.output_format_dirty) { if (config.output_format_dirty) {
config.output_format_dirty.Assign(0); config.output_format_dirty.Assign(0);
state.output_format = config.output_format; state.output_format = config.output_format;
LOG_TRACE(Audio_DSP, "mixers output_format = %zu", static_cast<size_t>(config.output_format)); LOG_TRACE(Audio_DSP, "mixers output_format = %zu",
static_cast<size_t>(config.output_format));
} }
if (config.headphones_connected_dirty) { if (config.headphones_connected_dirty) {
config.headphones_connected_dirty.Assign(0); config.headphones_connected_dirty.Assign(0);
// Do nothing. // Do nothing. (Note: Whether headphones are connected does affect coefficients used for
// (Note: Whether headphones are connected does affect coefficients used for surround sound.) // surround sound.)
LOG_TRACE(Audio_DSP, "mixers headphones_connected=%hu", config.headphones_connected); LOG_TRACE(Audio_DSP, "mixers headphones_connected=%hu", config.headphones_connected);
} }
@ -94,11 +92,10 @@ static s16 ClampToS16(s32 value) {
return static_cast<s16>(MathUtil::Clamp(value, -32768, 32767)); 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) { static std::array<s16, 2> AddAndClampToS16(const std::array<s16, 2>& a,
return { const std::array<s16, 2>& b) {
ClampToS16(static_cast<s32>(a[0]) + static_cast<s32>(b[0])), return {ClampToS16(static_cast<s32>(a[0]) + static_cast<s32>(b[0])),
ClampToS16(static_cast<s32>(a[1]) + static_cast<s32>(b[1])) ClampToS16(static_cast<s32>(a[1]) + static_cast<s32>(b[1]))};
};
} }
void Mixers::DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& samples) { void Mixers::DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& samples) {
@ -106,12 +103,16 @@ void Mixers::DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& sample
switch (state.output_format) { switch (state.output_format) {
case OutputFormat::Mono: case OutputFormat::Mono:
std::transform(current_frame.begin(), current_frame.end(), samples.begin(), current_frame.begin(), std::transform(
[gain](const std::array<s16, 2>& accumulator, const std::array<s32, 4>& sample) -> std::array<s16, 2> { 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 // Downmix to mono
s16 mono = ClampToS16(static_cast<s32>((gain * sample[0] + gain * sample[1] + gain * sample[2] + gain * sample[3]) / 2)); s16 mono = ClampToS16(static_cast<s32>(
(gain * sample[0] + gain * sample[1] + gain * sample[2] + gain * sample[3]) /
2));
// Mix into current frame // Mix into current frame
return AddAndClampToS16(accumulator, { mono, mono }); return AddAndClampToS16(accumulator, {mono, mono});
}); });
return; return;
@ -120,13 +121,15 @@ void Mixers::DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& sample
// fallthrough // fallthrough
case OutputFormat::Stereo: case OutputFormat::Stereo:
std::transform(current_frame.begin(), current_frame.end(), samples.begin(), current_frame.begin(), std::transform(
[gain](const std::array<s16, 2>& accumulator, const std::array<s32, 4>& sample) -> std::array<s16, 2> { 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 // Downmix to stereo
s16 left = ClampToS16(static_cast<s32>(gain * sample[0] + gain * sample[2])); s16 left = ClampToS16(static_cast<s32>(gain * sample[0] + gain * sample[2]));
s16 right = ClampToS16(static_cast<s32>(gain * sample[1] + gain * sample[3])); s16 right = ClampToS16(static_cast<s32>(gain * sample[1] + gain * sample[3]));
// Mix into current frame // Mix into current frame
return AddAndClampToS16(accumulator, { left, right }); return AddAndClampToS16(accumulator, {left, right});
}); });
return; return;
} }
@ -135,12 +138,14 @@ void Mixers::DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& sample
} }
void Mixers::AuxReturn(const IntermediateMixSamples& read_samples) { void Mixers::AuxReturn(const IntermediateMixSamples& read_samples) {
// NOTE: read_samples.mix{1,2}.pcm32 annoyingly have their dimensions in reverse order to QuadFrame32. // NOTE: read_samples.mix{1,2}.pcm32 annoyingly have their dimensions in reverse order to
// QuadFrame32.
if (state.mixer1_enabled) { if (state.mixer1_enabled) {
for (size_t sample = 0; sample < samples_per_frame; sample++) { for (size_t sample = 0; sample < samples_per_frame; sample++) {
for (size_t channel = 0; channel < 4; channel++) { for (size_t channel = 0; channel < 4; channel++) {
state.intermediate_mix_buffer[1][sample][channel] = read_samples.mix1.pcm32[channel][sample]; state.intermediate_mix_buffer[1][sample][channel] =
read_samples.mix1.pcm32[channel][sample];
} }
} }
} }
@ -148,14 +153,17 @@ void Mixers::AuxReturn(const IntermediateMixSamples& read_samples) {
if (state.mixer2_enabled) { if (state.mixer2_enabled) {
for (size_t sample = 0; sample < samples_per_frame; sample++) { for (size_t sample = 0; sample < samples_per_frame; sample++) {
for (size_t channel = 0; channel < 4; channel++) { for (size_t channel = 0; channel < 4; channel++) {
state.intermediate_mix_buffer[2][sample][channel] = read_samples.mix2.pcm32[channel][sample]; 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) { void Mixers::AuxSend(IntermediateMixSamples& write_samples,
// NOTE: read_samples.mix{1,2}.pcm32 annoyingly have their dimensions in reverse order to QuadFrame32. 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]; state.intermediate_mix_buffer[0] = input[0];
@ -184,7 +192,8 @@ void Mixers::MixCurrentFrame() {
current_frame.fill({}); current_frame.fill({});
for (size_t mix = 0; mix < 3; mix++) { for (size_t mix = 0; mix < 3; mix++) {
DownmixAndMixIntoCurrentFrame(state.intermediate_mixer_volume[mix], state.intermediate_mix_buffer[mix]); DownmixAndMixIntoCurrentFrame(state.intermediate_mixer_volume[mix],
state.intermediate_mix_buffer[mix]);
} }
// TODO(merry): Compressor. (We currently assume a disabled compressor.) // TODO(merry): Compressor. (We currently assume a disabled compressor.)

View File

@ -5,7 +5,6 @@
#pragma once #pragma once
#include <array> #include <array>
#include "audio_core/hle/common.h" #include "audio_core/hle/common.h"
#include "audio_core/hle/dsp.h" #include "audio_core/hle/dsp.h"
@ -20,10 +19,8 @@ public:
void Reset(); void Reset();
DspStatus Tick(DspConfiguration& config, DspStatus Tick(DspConfiguration& config, const IntermediateMixSamples& read_samples,
const IntermediateMixSamples& read_samples, IntermediateMixSamples& write_samples, const std::array<QuadFrame32, 3>& input);
IntermediateMixSamples& write_samples,
const std::array<QuadFrame32, 3>& input);
StereoFrame16 GetOutput() const { StereoFrame16 GetOutput() const {
return current_frame; return current_frame;
@ -53,7 +50,8 @@ private:
void AuxSend(IntermediateMixSamples& write_samples, const std::array<QuadFrame32, 3>& input); void AuxSend(IntermediateMixSamples& write_samples, const std::array<QuadFrame32, 3>& input);
/// INTERNAL: Mix current_frame. /// INTERNAL: Mix current_frame.
void MixCurrentFrame(); void MixCurrentFrame();
/// INTERNAL: Downmix from quadraphonic to stereo based on status.output_format and accumulate into current_frame. /// INTERNAL: Downmix from quadraphonic to stereo based on status.output_format and accumulate
/// into current_frame.
void DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& samples); void DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& samples);
/// INTERNAL: Generate DspStatus based on internal state. /// INTERNAL: Generate DspStatus based on internal state.
DspStatus GetCurrentStatus() const; DspStatus GetCurrentStatus() const;

View File

@ -4,14 +4,11 @@
#include <array> #include <array>
#include <vector> #include <vector>
#include "audio_core/hle/dsp.h" #include "audio_core/hle/dsp.h"
#include "audio_core/hle/pipe.h" #include "audio_core/hle/pipe.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/hle/service/dsp_dsp.h" #include "core/hle/service/dsp_dsp.h"
namespace DSP { namespace DSP {
@ -44,7 +41,9 @@ std::vector<u8> PipeRead(DspPipe pipe_number, u32 length) {
std::vector<u8>& data = pipe_data[pipe_index]; std::vector<u8>& data = pipe_data[pipe_index];
if (length > data.size()) { if (length > data.size()) {
LOG_WARNING(Audio_DSP, "pipe_number = %zu is out of data, application requested read of %u but %zu remain", LOG_WARNING(
Audio_DSP,
"pipe_number = %zu is out of data, application requested read of %u but %zu remain",
pipe_index, length, data.size()); pipe_index, length, data.size());
length = static_cast<u32>(data.size()); length = static_cast<u32>(data.size());
} }
@ -95,7 +94,7 @@ static void AudioPipeWriteStructAddresses() {
0x8000 + offsetof(SharedMemory, unknown11) / 2, 0x8000 + offsetof(SharedMemory, unknown11) / 2,
0x8000 + offsetof(SharedMemory, unknown12) / 2, 0x8000 + offsetof(SharedMemory, unknown12) / 2,
0x8000 + offsetof(SharedMemory, unknown13) / 2, 0x8000 + offsetof(SharedMemory, unknown13) / 2,
0x8000 + offsetof(SharedMemory, unknown14) / 2 0x8000 + offsetof(SharedMemory, unknown14) / 2,
}; };
// Begin with a u16 denoting the number of structs. // Begin with a u16 denoting the number of structs.
@ -112,7 +111,8 @@ void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) {
switch (pipe_number) { switch (pipe_number) {
case DspPipe::Audio: { case DspPipe::Audio: {
if (buffer.size() != 4) { if (buffer.size() != 4) {
LOG_ERROR(Audio_DSP, "DspPipe::Audio: Unexpected buffer length %zu was written", buffer.size()); LOG_ERROR(Audio_DSP, "DspPipe::Audio: Unexpected buffer length %zu was written",
buffer.size());
return; return;
} }
@ -120,7 +120,7 @@ void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) {
Initalize = 0, Initalize = 0,
Shutdown = 1, Shutdown = 1,
Wakeup = 2, Wakeup = 2,
Sleep = 3 Sleep = 3,
}; };
// The difference between Initialize and Wakeup is that Input state is maintained // The difference between Initialize and Wakeup is that Input state is maintained
@ -152,7 +152,9 @@ void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) {
dsp_state = DspState::Sleeping; dsp_state = DspState::Sleeping;
break; break;
default: default:
LOG_ERROR(Audio_DSP, "Application has requested unknown state transition of DSP hardware %hhu", buffer[0]); LOG_ERROR(Audio_DSP,
"Application has requested unknown state transition of DSP hardware %hhu",
buffer[0]);
dsp_state = DspState::Off; dsp_state = DspState::Off;
break; break;
} }
@ -160,7 +162,8 @@ void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) {
return; return;
} }
default: default:
LOG_CRITICAL(Audio_DSP, "pipe_number = %zu unimplemented", static_cast<size_t>(pipe_number)); LOG_CRITICAL(Audio_DSP, "pipe_number = %zu unimplemented",
static_cast<size_t>(pipe_number));
UNIMPLEMENTED(); UNIMPLEMENTED();
return; return;
} }

View File

@ -6,7 +6,6 @@
#include <cstddef> #include <cstddef>
#include <vector> #include <vector>
#include "common/common_types.h" #include "common/common_types.h"
namespace DSP { namespace DSP {
@ -19,16 +18,18 @@ enum class DspPipe {
Debug = 0, Debug = 0,
Dma = 1, Dma = 1,
Audio = 2, Audio = 2,
Binary = 3 Binary = 3,
}; };
constexpr size_t NUM_DSP_PIPE = 8; constexpr size_t NUM_DSP_PIPE = 8;
/** /**
* Reads `length` bytes from the DSP pipe identified with `pipe_number`. * Reads `length` bytes from the DSP pipe identified with `pipe_number`.
* @note Can read up to the maximum value of a u16 in bytes (65,535). * @note Can read up to the maximum value of a u16 in bytes (65,535).
* @note IF an error is encoutered with either an invalid `pipe_number` or `length` value, an empty vector will be returned. * @note IF an error is encoutered with either an invalid `pipe_number` or `length` value, an empty
* vector will be returned.
* @note IF `length` is set to 0, an empty vector will be returned. * @note IF `length` is set to 0, an empty vector will be returned.
* @note IF `length` is greater than the amount of data available, this function will only read the available amount. * @note IF `length` is greater than the amount of data available, this function will only read the
* available amount.
* @param pipe_number a `DspPipe` * @param pipe_number a `DspPipe`
* @param length the number of bytes to read. The max is 65,535 (max of u16). * @param length the number of bytes to read. The max is 65,535 (max of u16).
* @returns a vector of bytes from the specified pipe. On error, will be empty. * @returns a vector of bytes from the specified pipe. On error, will be empty.
@ -52,8 +53,9 @@ void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer);
enum class DspState { enum class DspState {
Off, Off,
On, On,
Sleeping Sleeping,
}; };
/// Get the state of the DSP /// Get the state of the DSP
DspState GetDspState(); DspState GetDspState();

View File

@ -4,21 +4,19 @@
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include "audio_core/codec.h" #include "audio_core/codec.h"
#include "audio_core/hle/common.h" #include "audio_core/hle/common.h"
#include "audio_core/hle/source.h" #include "audio_core/hle/source.h"
#include "audio_core/interpolate.h" #include "audio_core/interpolate.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/memory.h" #include "core/memory.h"
namespace DSP { namespace DSP {
namespace HLE { namespace HLE {
SourceStatus::Status Source::Tick(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]) { SourceStatus::Status Source::Tick(SourceConfiguration::Configuration& config,
const s16_le (&adpcm_coeffs)[16]) {
ParseConfig(config, adpcm_coeffs); ParseConfig(config, adpcm_coeffs);
if (state.enabled) { if (state.enabled) {
@ -47,7 +45,8 @@ void Source::Reset() {
state = {}; state = {};
} }
void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]) { void Source::ParseConfig(SourceConfiguration::Configuration& config,
const s16_le (&adpcm_coeffs)[16]) {
if (!config.dirty_raw) { if (!config.dirty_raw) {
return; return;
} }
@ -82,7 +81,8 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_l
LOG_TRACE(Audio_DSP, "source_id=%zu rate=%f", source_id, state.rate_multiplier); LOG_TRACE(Audio_DSP, "source_id=%zu rate=%f", source_id, state.rate_multiplier);
if (state.rate_multiplier <= 0) { if (state.rate_multiplier <= 0) {
LOG_ERROR(Audio_DSP, "Was given an invalid rate multiplier: source_id=%zu rate=%f", source_id, state.rate_multiplier); LOG_ERROR(Audio_DSP, "Was given an invalid rate multiplier: source_id=%zu rate=%f",
source_id, state.rate_multiplier);
state.rate_multiplier = 1.0f; state.rate_multiplier = 1.0f;
// Note: Actual firmware starts producing garbage if this occurs. // Note: Actual firmware starts producing garbage if this occurs.
} }
@ -90,7 +90,8 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_l
if (config.adpcm_coefficients_dirty) { if (config.adpcm_coefficients_dirty) {
config.adpcm_coefficients_dirty.Assign(0); config.adpcm_coefficients_dirty.Assign(0);
std::transform(adpcm_coeffs, adpcm_coeffs + state.adpcm_coeffs.size(), state.adpcm_coeffs.begin(), std::transform(adpcm_coeffs, adpcm_coeffs + state.adpcm_coeffs.size(),
state.adpcm_coeffs.begin(),
[](const auto& coeff) { return static_cast<s16>(coeff); }); [](const auto& coeff) { return static_cast<s16>(coeff); });
LOG_TRACE(Audio_DSP, "source_id=%zu adpcm update", source_id); LOG_TRACE(Audio_DSP, "source_id=%zu adpcm update", source_id);
} }
@ -118,9 +119,10 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_l
if (config.filters_enabled_dirty) { if (config.filters_enabled_dirty) {
config.filters_enabled_dirty.Assign(0); config.filters_enabled_dirty.Assign(0);
state.filters.Enable(config.simple_filter_enabled.ToBool(), config.biquad_filter_enabled.ToBool()); state.filters.Enable(config.simple_filter_enabled.ToBool(),
LOG_TRACE(Audio_DSP, "source_id=%zu enable_simple=%hu enable_biquad=%hu", config.biquad_filter_enabled.ToBool());
source_id, config.simple_filter_enabled.Value(), config.biquad_filter_enabled.Value()); LOG_TRACE(Audio_DSP, "source_id=%zu enable_simple=%hu enable_biquad=%hu", source_id,
config.simple_filter_enabled.Value(), config.biquad_filter_enabled.Value());
} }
if (config.simple_filter_dirty) { if (config.simple_filter_dirty) {
@ -138,19 +140,22 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_l
if (config.interpolation_dirty) { if (config.interpolation_dirty) {
config.interpolation_dirty.Assign(0); config.interpolation_dirty.Assign(0);
state.interpolation_mode = config.interpolation_mode; state.interpolation_mode = config.interpolation_mode;
LOG_TRACE(Audio_DSP, "source_id=%zu interpolation_mode=%zu", source_id, static_cast<size_t>(state.interpolation_mode)); LOG_TRACE(Audio_DSP, "source_id=%zu interpolation_mode=%zu", source_id,
static_cast<size_t>(state.interpolation_mode));
} }
if (config.format_dirty || config.embedded_buffer_dirty) { if (config.format_dirty || config.embedded_buffer_dirty) {
config.format_dirty.Assign(0); config.format_dirty.Assign(0);
state.format = config.format; state.format = config.format;
LOG_TRACE(Audio_DSP, "source_id=%zu format=%zu", source_id, static_cast<size_t>(state.format)); LOG_TRACE(Audio_DSP, "source_id=%zu format=%zu", source_id,
static_cast<size_t>(state.format));
} }
if (config.mono_or_stereo_dirty || config.embedded_buffer_dirty) { if (config.mono_or_stereo_dirty || config.embedded_buffer_dirty) {
config.mono_or_stereo_dirty.Assign(0); config.mono_or_stereo_dirty.Assign(0);
state.mono_or_stereo = config.mono_or_stereo; state.mono_or_stereo = config.mono_or_stereo;
LOG_TRACE(Audio_DSP, "source_id=%zu mono_or_stereo=%zu", source_id, static_cast<size_t>(state.mono_or_stereo)); LOG_TRACE(Audio_DSP, "source_id=%zu mono_or_stereo=%zu", source_id,
static_cast<size_t>(state.mono_or_stereo));
} }
if (config.embedded_buffer_dirty) { if (config.embedded_buffer_dirty) {
@ -159,15 +164,16 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_l
config.physical_address, config.physical_address,
config.length, config.length,
static_cast<u8>(config.adpcm_ps), static_cast<u8>(config.adpcm_ps),
{ config.adpcm_yn[0], config.adpcm_yn[1] }, {config.adpcm_yn[0], config.adpcm_yn[1]},
config.adpcm_dirty.ToBool(), config.adpcm_dirty.ToBool(),
config.is_looping.ToBool(), config.is_looping.ToBool(),
config.buffer_id, config.buffer_id,
state.mono_or_stereo, state.mono_or_stereo,
state.format, state.format,
false false,
}); });
LOG_TRACE(Audio_DSP, "enqueuing embedded addr=0x%08x len=%u id=%hu", config.physical_address, config.length, config.buffer_id); LOG_TRACE(Audio_DSP, "enqueuing embedded addr=0x%08x len=%u id=%hu",
config.physical_address, config.length, config.buffer_id);
} }
if (config.buffer_queue_dirty) { if (config.buffer_queue_dirty) {
@ -179,15 +185,16 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_l
b.physical_address, b.physical_address,
b.length, b.length,
static_cast<u8>(b.adpcm_ps), static_cast<u8>(b.adpcm_ps),
{ b.adpcm_yn[0], b.adpcm_yn[1] }, {b.adpcm_yn[0], b.adpcm_yn[1]},
b.adpcm_dirty != 0, b.adpcm_dirty != 0,
b.is_looping != 0, b.is_looping != 0,
b.buffer_id, b.buffer_id,
state.mono_or_stereo, state.mono_or_stereo,
state.format, state.format,
true true,
}); });
LOG_TRACE(Audio_DSP, "enqueuing queued %zu addr=0x%08x len=%u id=%hu", i, b.physical_address, b.length, b.buffer_id); LOG_TRACE(Audio_DSP, "enqueuing queued %zu addr=0x%08x len=%u id=%hu", i,
b.physical_address, b.length, b.buffer_id);
} }
} }
config.buffers_dirty = 0; config.buffers_dirty = 0;
@ -218,10 +225,13 @@ void Source::GenerateFrame() {
break; break;
} }
const size_t size_to_copy = std::min(state.current_buffer.size(), current_frame.size() - frame_position); const size_t size_to_copy =
std::min(state.current_buffer.size(), current_frame.size() - frame_position);
std::copy(state.current_buffer.begin(), state.current_buffer.begin() + size_to_copy, current_frame.begin() + frame_position); std::copy(state.current_buffer.begin(), state.current_buffer.begin() + size_to_copy,
state.current_buffer.erase(state.current_buffer.begin(), state.current_buffer.begin() + size_to_copy); current_frame.begin() + frame_position);
state.current_buffer.erase(state.current_buffer.begin(),
state.current_buffer.begin() + size_to_copy);
frame_position += size_to_copy; frame_position += size_to_copy;
state.next_sample_number += static_cast<u32>(size_to_copy); state.next_sample_number += static_cast<u32>(size_to_copy);
@ -230,9 +240,9 @@ void Source::GenerateFrame() {
state.filters.ProcessFrame(current_frame); state.filters.ProcessFrame(current_frame);
} }
bool Source::DequeueBuffer() { bool Source::DequeueBuffer() {
ASSERT_MSG(state.current_buffer.empty(), "Shouldn't dequeue; we still have data in current_buffer"); ASSERT_MSG(state.current_buffer.empty(),
"Shouldn't dequeue; we still have data in current_buffer");
if (state.input_queue.empty()) if (state.input_queue.empty())
return false; return false;
@ -261,14 +271,16 @@ bool Source::DequeueBuffer() {
break; break;
case Format::ADPCM: case Format::ADPCM:
DEBUG_ASSERT(num_channels == 1); DEBUG_ASSERT(num_channels == 1);
state.current_buffer = Codec::DecodeADPCM(memory, buf.length, state.adpcm_coeffs, state.adpcm_state); state.current_buffer =
Codec::DecodeADPCM(memory, buf.length, state.adpcm_coeffs, state.adpcm_state);
break; break;
default: default:
UNIMPLEMENTED(); UNIMPLEMENTED();
break; break;
} }
} else { } else {
LOG_WARNING(Audio_DSP, "source_id=%zu buffer_id=%hu length=%u: Invalid physical address 0x%08X", LOG_WARNING(Audio_DSP,
"source_id=%zu buffer_id=%hu length=%u: Invalid physical address 0x%08X",
source_id, buf.buffer_id, buf.length, buf.physical_address); source_id, buf.buffer_id, buf.length, buf.physical_address);
state.current_buffer.clear(); state.current_buffer.clear();
return true; return true;
@ -276,14 +288,17 @@ bool Source::DequeueBuffer() {
switch (state.interpolation_mode) { switch (state.interpolation_mode) {
case InterpolationMode::None: case InterpolationMode::None:
state.current_buffer = AudioInterp::None(state.interp_state, state.current_buffer, state.rate_multiplier); state.current_buffer =
AudioInterp::None(state.interp_state, state.current_buffer, state.rate_multiplier);
break; break;
case InterpolationMode::Linear: case InterpolationMode::Linear:
state.current_buffer = AudioInterp::Linear(state.interp_state, state.current_buffer, state.rate_multiplier); state.current_buffer =
AudioInterp::Linear(state.interp_state, state.current_buffer, state.rate_multiplier);
break; break;
case InterpolationMode::Polyphase: case InterpolationMode::Polyphase:
// TODO(merry): Implement polyphase interpolation // TODO(merry): Implement polyphase interpolation
state.current_buffer = AudioInterp::Linear(state.interp_state, state.current_buffer, state.rate_multiplier); state.current_buffer =
AudioInterp::Linear(state.interp_state, state.current_buffer, state.rate_multiplier);
break; break;
default: default:
UNIMPLEMENTED(); UNIMPLEMENTED();
@ -296,7 +311,8 @@ bool Source::DequeueBuffer() {
state.buffer_update = buf.from_queue; state.buffer_update = buf.from_queue;
LOG_TRACE(Audio_DSP, "source_id=%zu buffer_id=%hu from_queue=%s current_buffer.size()=%zu", LOG_TRACE(Audio_DSP, "source_id=%zu buffer_id=%hu from_queue=%s current_buffer.size()=%zu",
source_id, buf.buffer_id, buf.from_queue ? "true" : "false", state.current_buffer.size()); source_id, buf.buffer_id, buf.from_queue ? "true" : "false",
state.current_buffer.size());
return true; return true;
} }

View File

@ -7,13 +7,11 @@
#include <array> #include <array>
#include <queue> #include <queue>
#include <vector> #include <vector>
#include "audio_core/codec.h" #include "audio_core/codec.h"
#include "audio_core/hle/common.h" #include "audio_core/hle/common.h"
#include "audio_core/hle/dsp.h" #include "audio_core/hle/dsp.h"
#include "audio_core/hle/filter.h" #include "audio_core/hle/filter.h"
#include "audio_core/interpolate.h" #include "audio_core/interpolate.h"
#include "common/common_types.h" #include "common/common_types.h"
namespace DSP { namespace DSP {
@ -40,13 +38,17 @@ public:
/** /**
* This is called once every audio frame. This performs per-source processing every frame. * This is called once every audio frame. This performs per-source processing every frame.
* @param config The new configuration we've got for this Source from the application. * @param config The new configuration we've got for this Source from the application.
* @param adpcm_coeffs ADPCM coefficients to use if config tells us to use them (may contain invalid values otherwise). * @param adpcm_coeffs ADPCM coefficients to use if config tells us to use them (may contain
* @return The current status of this Source. This is given back to the emulated application via SharedMemory. * invalid values otherwise).
* @return The current status of this Source. This is given back to the emulated application via
* SharedMemory.
*/ */
SourceStatus::Status Tick(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]); SourceStatus::Status Tick(SourceConfiguration::Configuration& config,
const s16_le (&adpcm_coeffs)[16]);
/** /**
* Mix this source's output into dest, using the gains for the `intermediate_mix_id`-th intermediate mixer. * Mix this source's output into dest, using the gains for the `intermediate_mix_id`-th
* intermediate mixer.
* @param dest The QuadFrame32 to mix into. * @param dest The QuadFrame32 to mix into.
* @param intermediate_mix_id The id of the intermediate mix whose gains we are using. * @param intermediate_mix_id The id of the intermediate mix whose gains we are using.
*/ */
@ -77,7 +79,7 @@ private:
}; };
struct BufferOrder { struct BufferOrder {
bool operator() (const Buffer& a, const Buffer& b) const { bool operator()(const Buffer& a, const Buffer& b) const {
// Lower buffer_id comes first. // Lower buffer_id comes first.
return a.buffer_id > b.buffer_id; return a.buffer_id > b.buffer_id;
} }
@ -134,7 +136,8 @@ private:
void ParseConfig(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]); void ParseConfig(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]);
/// INTERNAL: Generate the current audio output for this frame based on our internal state. /// INTERNAL: Generate the current audio output for this frame based on our internal state.
void GenerateFrame(); void GenerateFrame();
/// INTERNAL: Dequeues a buffer and does preprocessing on it (decoding, resampling). Puts it into current_buffer. /// INTERNAL: Dequeues a buffer and does preprocessing on it (decoding, resampling). Puts it
/// into current_buffer.
bool DequeueBuffer(); bool DequeueBuffer();
/// INTERNAL: Generates a SourceStatus::Status based on our internal state. /// INTERNAL: Generates a SourceStatus::Status based on our internal state.
SourceStatus::Status GetCurrentStatus(); SourceStatus::Status GetCurrentStatus();

View File

@ -3,7 +3,6 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include "audio_core/interpolate.h" #include "audio_core/interpolate.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/math_util.h" #include "common/math_util.h"
@ -17,7 +16,8 @@ constexpr u64 scale_mask = scale_factor - 1;
/// Here we step over the input in steps of rate_multiplier, until we consume all of the input. /// Here we step over the input in steps of rate_multiplier, until we consume all of the input.
/// Three adjacent samples are passed to fn each step. /// Three adjacent samples are passed to fn each step.
template <typename Function> template <typename Function>
static StereoBuffer16 StepOverSamples(State& state, const StereoBuffer16& input, float rate_multiplier, Function fn) { static StereoBuffer16 StepOverSamples(State& state, const StereoBuffer16& input,
float rate_multiplier, Function fn) {
ASSERT(rate_multiplier > 0); ASSERT(rate_multiplier > 0);
if (input.size() < 2) if (input.size() < 2)
@ -63,21 +63,22 @@ static StereoBuffer16 StepOverSamples(State& state, const StereoBuffer16& input,
} }
StereoBuffer16 None(State& state, const StereoBuffer16& input, float rate_multiplier) { StereoBuffer16 None(State& state, const StereoBuffer16& input, float rate_multiplier) {
return StepOverSamples(state, input, rate_multiplier, [](u64 fraction, const auto& x0, const auto& x1, const auto& x2) { return StepOverSamples(
return x0; state, input, rate_multiplier,
}); [](u64 fraction, const auto& x0, const auto& x1, const auto& x2) { return x0; });
} }
StereoBuffer16 Linear(State& state, const StereoBuffer16& input, float rate_multiplier) { StereoBuffer16 Linear(State& state, const StereoBuffer16& input, float rate_multiplier) {
// Note on accuracy: Some values that this produces are +/- 1 from the actual firmware. // Note on accuracy: Some values that this produces are +/- 1 from the actual firmware.
return StepOverSamples(state, input, rate_multiplier, [](u64 fraction, const auto& x0, const auto& x1, const auto& x2) { return StepOverSamples(state, input, rate_multiplier,
[](u64 fraction, const auto& x0, const auto& x1, const auto& x2) {
// This is a saturated subtraction. (Verified by black-box fuzzing.) // This is a saturated subtraction. (Verified by black-box fuzzing.)
s64 delta0 = MathUtil::Clamp<s64>(x1[0] - x0[0], -32768, 32767); s64 delta0 = MathUtil::Clamp<s64>(x1[0] - x0[0], -32768, 32767);
s64 delta1 = MathUtil::Clamp<s64>(x1[1] - x0[1], -32768, 32767); s64 delta1 = MathUtil::Clamp<s64>(x1[1] - x0[1], -32768, 32767);
return std::array<s16, 2> { return std::array<s16, 2>{
static_cast<s16>(x0[0] + fraction * delta0 / scale_factor), static_cast<s16>(x0[0] + fraction * delta0 / scale_factor),
static_cast<s16>(x0[1] + fraction * delta1 / scale_factor) static_cast<s16>(x0[1] + fraction * delta1 / scale_factor),
}; };
}); });
} }

View File

@ -6,7 +6,6 @@
#include <array> #include <array>
#include <vector> #include <vector>
#include "common/common_types.h" #include "common/common_types.h"
namespace AudioInterp { namespace AudioInterp {
@ -24,7 +23,8 @@ struct State {
* No interpolation. This is equivalent to a zero-order hold. There is a two-sample predelay. * No interpolation. This is equivalent to a zero-order hold. There is a two-sample predelay.
* @param input Input buffer. * @param input Input buffer.
* @param rate_multiplier Stretch factor. Must be a positive non-zero value. * @param rate_multiplier Stretch factor. Must be a positive non-zero value.
* rate_multiplier > 1.0 performs decimation and rate_multipler < 1.0 performs upsampling. * rate_multiplier > 1.0 performs decimation and rate_multipler < 1.0
* performs upsampling.
* @return The resampled audio buffer. * @return The resampled audio buffer.
*/ */
StereoBuffer16 None(State& state, const StereoBuffer16& input, float rate_multiplier); StereoBuffer16 None(State& state, const StereoBuffer16& input, float rate_multiplier);
@ -33,7 +33,8 @@ StereoBuffer16 None(State& state, const StereoBuffer16& input, float rate_multip
* Linear interpolation. This is equivalent to a first-order hold. There is a two-sample predelay. * Linear interpolation. This is equivalent to a first-order hold. There is a two-sample predelay.
* @param input Input buffer. * @param input Input buffer.
* @param rate_multiplier Stretch factor. Must be a positive non-zero value. * @param rate_multiplier Stretch factor. Must be a positive non-zero value.
* rate_multiplier > 1.0 performs decimation and rate_multipler < 1.0 performs upsampling. * rate_multiplier > 1.0 performs decimation and rate_multipler < 1.0
* performs upsampling.
* @return The resampled audio buffer. * @return The resampled audio buffer.
*/ */
StereoBuffer16 Linear(State& state, const StereoBuffer16& input, float rate_multiplier); StereoBuffer16 Linear(State& state, const StereoBuffer16& input, float rate_multiplier);

View File

@ -5,7 +5,6 @@
#pragma once #pragma once
#include <cstddef> #include <cstddef>
#include "audio_core/audio_core.h" #include "audio_core/audio_core.h"
#include "audio_core/sink.h" #include "audio_core/sink.h"

View File

@ -3,16 +3,13 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <list> #include <list>
#include <numeric>
#include <vector> #include <vector>
#include <SDL.h> #include <SDL.h>
#include "audio_core/audio_core.h" #include "audio_core/audio_core.h"
#include "audio_core/sdl2_sink.h" #include "audio_core/sdl2_sink.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include <numeric>
namespace AudioCore { namespace AudioCore {
@ -45,7 +42,8 @@ SDL2Sink::SDL2Sink() : impl(std::make_unique<Impl>()) {
SDL_AudioSpec obtained_audiospec; SDL_AudioSpec obtained_audiospec;
SDL_zero(obtained_audiospec); SDL_zero(obtained_audiospec);
impl->audio_device_id = SDL_OpenAudioDevice(nullptr, false, &desired_audiospec, &obtained_audiospec, 0); impl->audio_device_id =
SDL_OpenAudioDevice(nullptr, false, &desired_audiospec, &obtained_audiospec, 0);
if (impl->audio_device_id <= 0) { if (impl->audio_device_id <= 0) {
LOG_CRITICAL(Audio_Sink, "SDL_OpenAudioDevice failed"); LOG_CRITICAL(Audio_Sink, "SDL_OpenAudioDevice failed");
return; return;
@ -86,9 +84,10 @@ size_t SDL2Sink::SamplesInQueue() const {
SDL_LockAudioDevice(impl->audio_device_id); SDL_LockAudioDevice(impl->audio_device_id);
size_t total_size = std::accumulate(impl->queue.begin(), impl->queue.end(), static_cast<size_t>(0), size_t total_size = std::accumulate(impl->queue.begin(), impl->queue.end(),
[](size_t sum, const auto& buffer) { static_cast<size_t>(0), [](size_t sum, const auto& buffer) {
// Division by two because each stereo sample is made of two s16. // Division by two because each stereo sample is made of
// two s16.
return sum + buffer.size() / 2; return sum + buffer.size() / 2;
}); });
@ -100,7 +99,8 @@ size_t SDL2Sink::SamplesInQueue() const {
void SDL2Sink::Impl::Callback(void* impl_, u8* buffer, int buffer_size_in_bytes) { void SDL2Sink::Impl::Callback(void* impl_, u8* buffer, int buffer_size_in_bytes) {
Impl* impl = reinterpret_cast<Impl*>(impl_); Impl* impl = reinterpret_cast<Impl*>(impl_);
size_t remaining_size = static_cast<size_t>(buffer_size_in_bytes) / sizeof(s16); // Keep track of size in 16-bit increments. size_t remaining_size = static_cast<size_t>(buffer_size_in_bytes) /
sizeof(s16); // Keep track of size in 16-bit increments.
while (remaining_size > 0 && !impl->queue.empty()) { while (remaining_size > 0 && !impl->queue.empty()) {
if (impl->queue.front().size() <= remaining_size) { if (impl->queue.front().size() <= remaining_size) {
@ -111,7 +111,8 @@ void SDL2Sink::Impl::Callback(void* impl_, u8* buffer, int buffer_size_in_bytes)
} else { } else {
memcpy(buffer, impl->queue.front().data(), remaining_size * sizeof(s16)); memcpy(buffer, impl->queue.front().data(), remaining_size * sizeof(s16));
buffer += remaining_size * sizeof(s16); buffer += remaining_size * sizeof(s16);
impl->queue.front().erase(impl->queue.front().begin(), impl->queue.front().begin() + remaining_size); impl->queue.front().erase(impl->queue.front().begin(),
impl->queue.front().begin() + remaining_size);
remaining_size = 0; remaining_size = 0;
} }
} }

View File

@ -6,7 +6,6 @@
#include <cstddef> #include <cstddef>
#include <memory> #include <memory>
#include "audio_core/sink.h" #include "audio_core/sink.h"
namespace AudioCore { namespace AudioCore {

View File

@ -5,20 +5,21 @@
#pragma once #pragma once
#include <vector> #include <vector>
#include "common/common_types.h" #include "common/common_types.h"
namespace AudioCore { namespace AudioCore {
/** /**
* This class is an interface for an audio sink. An audio sink accepts samples in stereo signed PCM16 format to be output. * This class is an interface for an audio sink. An audio sink accepts samples in stereo signed
* Sinks *do not* handle resampling and expect the correct sample rate. They are dumb outputs. * PCM16 format to be output. Sinks *do not* handle resampling and expect the correct sample rate.
* They are dumb outputs.
*/ */
class Sink { class Sink {
public: public:
virtual ~Sink() = default; virtual ~Sink() = default;
/// The native rate of this sink. The sink expects to be fed samples that respect this. (Units: samples/sec) /// The native rate of this sink. The sink expects to be fed samples that respect this. (Units:
/// samples/sec)
virtual unsigned int GetNativeSampleRate() const = 0; virtual unsigned int GetNativeSampleRate() const = 0;
/** /**

View File

@ -4,10 +4,8 @@
#include <memory> #include <memory>
#include <vector> #include <vector>
#include "audio_core/null_sink.h" #include "audio_core/null_sink.h"
#include "audio_core/sink_details.h" #include "audio_core/sink_details.h"
#ifdef HAVE_SDL2 #ifdef HAVE_SDL2
#include "audio_core/sdl2_sink.h" #include "audio_core/sdl2_sink.h"
#endif #endif
@ -17,9 +15,9 @@ namespace AudioCore {
// g_sink_details is ordered in terms of desirability, with the best choice at the top. // g_sink_details is ordered in terms of desirability, with the best choice at the top.
const std::vector<SinkDetails> g_sink_details = { const std::vector<SinkDetails> g_sink_details = {
#ifdef HAVE_SDL2 #ifdef HAVE_SDL2
{ "sdl2", []() { return std::make_unique<SDL2Sink>(); } }, {"sdl2", []() { return std::make_unique<SDL2Sink>(); }},
#endif #endif
{ "null", []() { return std::make_unique<NullSink>(); } }, {"null", []() { return std::make_unique<NullSink>(); }},
}; };
} // namespace AudioCore } // namespace AudioCore

View File

@ -5,12 +5,9 @@
#include <chrono> #include <chrono>
#include <cmath> #include <cmath>
#include <vector> #include <vector>
#include <SoundTouch.h> #include <SoundTouch.h>
#include "audio_core/audio_core.h" #include "audio_core/audio_core.h"
#include "audio_core/time_stretch.h" #include "audio_core/time_stretch.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/math_util.h" #include "common/math_util.h"
@ -48,7 +45,8 @@ std::vector<s16> TimeStretcher::Process(size_t samples_in_queue) {
double ratio = CalculateCurrentRatio(); double ratio = CalculateCurrentRatio();
ratio = CorrectForUnderAndOverflow(ratio, samples_in_queue); ratio = CorrectForUnderAndOverflow(ratio, samples_in_queue);
impl->smoothed_ratio = (1.0 - SMOOTHING_FACTOR) * impl->smoothed_ratio + SMOOTHING_FACTOR * ratio; impl->smoothed_ratio =
(1.0 - SMOOTHING_FACTOR) * impl->smoothed_ratio + SMOOTHING_FACTOR * ratio;
impl->smoothed_ratio = ClampRatio(impl->smoothed_ratio); impl->smoothed_ratio = ClampRatio(impl->smoothed_ratio);
// SoundTouch's tempo definition the inverse of our ratio definition. // SoundTouch's tempo definition the inverse of our ratio definition.
@ -100,7 +98,8 @@ double TimeStretcher::CalculateCurrentRatio() {
const steady_clock::time_point now = steady_clock::now(); const steady_clock::time_point now = steady_clock::now();
const std::chrono::duration<double> duration = now - impl->frame_timer; const std::chrono::duration<double> duration = now - impl->frame_timer;
const double expected_time = static_cast<double>(impl->samples_queued) / static_cast<double>(native_sample_rate); const double expected_time =
static_cast<double>(impl->samples_queued) / static_cast<double>(native_sample_rate);
const double actual_time = duration.count(); const double actual_time = duration.count();
double ratio; double ratio;

View File

@ -5,7 +5,6 @@
#include <cstddef> #include <cstddef>
#include <memory> #include <memory>
#include <vector> #include <vector>
#include "common/common_types.h" #include "common/common_types.h"
namespace AudioCore { namespace AudioCore {
@ -37,7 +36,8 @@ public:
/** /**
* Does audio stretching and produces the time-stretched samples. * Does audio stretching and produces the time-stretched samples.
* Timer calculations use sample_delay to determine how much of a margin we have. * Timer calculations use sample_delay to determine how much of a margin we have.
* @param sample_delay How many samples are buffered downstream of this module and haven't been played yet. * @param sample_delay How many samples are buffered downstream of this module and haven't been
* played yet.
* @return Samples to play in interleaved stereo PCM16 format. * @return Samples to play in interleaved stereo PCM16 format.
*/ */
std::vector<s16> Process(size_t sample_delay); std::vector<s16> Process(size_t sample_delay);
@ -48,7 +48,8 @@ private:
/// INTERNAL: ratio = wallclock time / emulated time /// INTERNAL: ratio = wallclock time / emulated time
double CalculateCurrentRatio(); double CalculateCurrentRatio();
/// INTERNAL: If we have too many or too few samples downstream, nudge ratio in the appropriate direction. /// INTERNAL: If we have too many or too few samples downstream, nudge ratio in the appropriate
/// direction.
double CorrectForUnderAndOverflow(double ratio, size_t sample_delay) const; double CorrectForUnderAndOverflow(double ratio, size_t sample_delay) const;
/// INTERNAL: Gets the time-stretched samples from SoundTouch. /// INTERNAL: Gets the time-stretched samples from SoundTouch.
std::vector<s16> GetSamples(); std::vector<s16> GetSamples();

View File

@ -2,10 +2,10 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <string>
#include <thread>
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include <string>
#include <thread>
// This needs to be included before getopt.h because the latter #defines symbols used by it // This needs to be included before getopt.h because the latter #defines symbols used by it
#include "common/microprofile.h" #include "common/microprofile.h"
@ -13,53 +13,48 @@
#ifdef _MSC_VER #ifdef _MSC_VER
#include <getopt.h> #include <getopt.h>
#else #else
#include <unistd.h>
#include <getopt.h> #include <getopt.h>
#include <unistd.h>
#endif #endif
#ifdef _WIN32 #ifdef _WIN32
#include <Windows.h> #include <Windows.h>
#endif #endif
#include "common/logging/log.h" #include "citra/config.h"
#include "citra/emu_window/emu_window_sdl2.h"
#include "common/logging/backend.h" #include "common/logging/backend.h"
#include "common/logging/filter.h" #include "common/logging/filter.h"
#include "common/logging/log.h"
#include "common/scm_rev.h" #include "common/scm_rev.h"
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "common/string_util.h" #include "common/string_util.h"
#include "core/settings.h"
#include "core/system.h"
#include "core/core.h" #include "core/core.h"
#include "core/gdbstub/gdbstub.h" #include "core/gdbstub/gdbstub.h"
#include "core/loader/loader.h" #include "core/loader/loader.h"
#include "core/settings.h"
#include "citra/config.h" #include "core/system.h"
#include "citra/emu_window/emu_window_sdl2.h"
#include "video_core/video_core.h" #include "video_core/video_core.h"
static void PrintHelp(const char* argv0) {
static void PrintHelp(const char *argv0) std::cout << "Usage: " << argv0
{ << " [options] <filename>\n"
std::cout << "Usage: " << argv0 << " [options] <filename>\n"
"-g, --gdbport=NUMBER Enable gdb stub on port NUMBER\n" "-g, --gdbport=NUMBER Enable gdb stub on port NUMBER\n"
"-h, --help Display this help and exit\n" "-h, --help Display this help and exit\n"
"-v, --version Output version information and exit\n"; "-v, --version Output version information and exit\n";
} }
static void PrintVersion() static void PrintVersion() {
{
std::cout << "Citra " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl; std::cout << "Citra " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl;
} }
/// Application entry point /// Application entry point
int main(int argc, char **argv) { int main(int argc, char** argv) {
Config config; Config config;
int option_index = 0; int option_index = 0;
bool use_gdbstub = Settings::values.use_gdbstub; bool use_gdbstub = Settings::values.use_gdbstub;
u32 gdb_port = static_cast<u32>(Settings::values.gdbstub_port); u32 gdb_port = static_cast<u32>(Settings::values.gdbstub_port);
char *endarg; char* endarg;
#ifdef _WIN32 #ifdef _WIN32
int argc_w; int argc_w;
auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w); auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w);
@ -72,10 +67,10 @@ int main(int argc, char **argv) {
std::string boot_filename; std::string boot_filename;
static struct option long_options[] = { static struct option long_options[] = {
{ "gdbport", required_argument, 0, 'g' }, {"gdbport", required_argument, 0, 'g'},
{ "help", no_argument, 0, 'h' }, {"help", no_argument, 0, 'h'},
{ "version", no_argument, 0, 'v' }, {"version", no_argument, 0, 'v'},
{ 0, 0, 0, 0 } {0, 0, 0, 0},
}; };
while (optind < argc) { while (optind < argc) {
@ -86,7 +81,8 @@ int main(int argc, char **argv) {
errno = 0; errno = 0;
gdb_port = strtoul(optarg, &endarg, 0); gdb_port = strtoul(optarg, &endarg, 0);
use_gdbstub = true; use_gdbstub = true;
if (endarg == optarg) errno = EINVAL; if (endarg == optarg)
errno = EINVAL;
if (errno != 0) { if (errno != 0) {
perror("--gdbport"); perror("--gdbport");
exit(1); exit(1);

View File

@ -3,19 +3,13 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <memory> #include <memory>
#include <inih/cpp/INIReader.h>
#include <SDL.h> #include <SDL.h>
#include <inih/cpp/INIReader.h>
#include "citra/default_ini.h" #include "citra/default_ini.h"
#include "common/file_util.h" #include "common/file_util.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/settings.h"
#include "config.h" #include "config.h"
#include "core/settings.h"
Config::Config() { Config::Config() {
// TODO: Don't hardcode the path; let the frontend decide where to put the config files. // TODO: Don't hardcode the path; let the frontend decide where to put the config files.
@ -45,15 +39,13 @@ bool Config::LoadINI(const std::string& default_contents, bool retry) {
static const std::array<int, Settings::NativeInput::NUM_INPUTS> defaults = { static const std::array<int, Settings::NativeInput::NUM_INPUTS> defaults = {
// directly mapped keys // directly mapped keys
SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_Q, SDL_SCANCODE_W,
SDL_SCANCODE_Q, SDL_SCANCODE_W, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_B, SDL_SCANCODE_T,
SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_B, SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H, SDL_SCANCODE_I, SDL_SCANCODE_K, SDL_SCANCODE_J,
SDL_SCANCODE_T, SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H, SDL_SCANCODE_L,
SDL_SCANCODE_I, SDL_SCANCODE_K, SDL_SCANCODE_J, SDL_SCANCODE_L,
// indirectly mapped keys // indirectly mapped keys
SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT, SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT, SDL_SCANCODE_D,
SDL_SCANCODE_D,
}; };
void Config::ReadValues() { void Config::ReadValues() {
@ -62,7 +54,8 @@ void Config::ReadValues() {
Settings::values.input_mappings[Settings::NativeInput::All[i]] = Settings::values.input_mappings[Settings::NativeInput::All[i]] =
sdl2_config->GetInteger("Controls", Settings::NativeInput::Mapping[i], defaults[i]); sdl2_config->GetInteger("Controls", Settings::NativeInput::Mapping[i], defaults[i]);
} }
Settings::values.pad_circle_modifier_scale = (float)sdl2_config->GetReal("Controls", "pad_circle_modifier_scale", 0.5); Settings::values.pad_circle_modifier_scale =
(float)sdl2_config->GetReal("Controls", "pad_circle_modifier_scale", 0.5);
// Core // Core
Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true); Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true);
@ -71,7 +64,8 @@ void Config::ReadValues() {
// Renderer // Renderer
Settings::values.use_hw_renderer = sdl2_config->GetBoolean("Renderer", "use_hw_renderer", true); Settings::values.use_hw_renderer = sdl2_config->GetBoolean("Renderer", "use_hw_renderer", true);
Settings::values.use_shader_jit = sdl2_config->GetBoolean("Renderer", "use_shader_jit", true); Settings::values.use_shader_jit = sdl2_config->GetBoolean("Renderer", "use_shader_jit", true);
Settings::values.use_scaled_resolution = sdl2_config->GetBoolean("Renderer", "use_scaled_resolution", false); Settings::values.use_scaled_resolution =
sdl2_config->GetBoolean("Renderer", "use_scaled_resolution", false);
Settings::values.use_vsync = sdl2_config->GetBoolean("Renderer", "use_vsync", false); Settings::values.use_vsync = sdl2_config->GetBoolean("Renderer", "use_vsync", false);
Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 1.0); Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 1.0);
@ -80,10 +74,12 @@ void Config::ReadValues() {
// Audio // Audio
Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto"); Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto");
Settings::values.enable_audio_stretching = sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true); Settings::values.enable_audio_stretching =
sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true);
// Data Storage // Data Storage
Settings::values.use_virtual_sd = sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true); Settings::values.use_virtual_sd =
sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true);
// System // System
Settings::values.is_new_3ds = sdl2_config->GetBoolean("System", "is_new_3ds", false); Settings::values.is_new_3ds = sdl2_config->GetBoolean("System", "is_new_3ds", false);
@ -94,7 +90,8 @@ void Config::ReadValues() {
// Debugging // Debugging
Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false); Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false);
Settings::values.gdbstub_port = static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689)); Settings::values.gdbstub_port =
static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689));
} }
void Config::Reload() { void Config::Reload() {

View File

@ -6,15 +6,15 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include <inih/cpp/INIReader.h> #include <inih/cpp/INIReader.h>
class Config { class Config {
std::unique_ptr<INIReader> sdl2_config; std::unique_ptr<INIReader> sdl2_config;
std::string sdl2_config_loc; std::string sdl2_config_loc;
bool LoadINI(const std::string& default_contents="", bool retry=true); bool LoadINI(const std::string& default_contents = "", bool retry = true);
void ReadValues(); void ReadValues();
public: public:
Config(); Config();

View File

@ -104,5 +104,4 @@ log_filter = *:Info
use_gdbstub=false use_gdbstub=false
gdbstub_port=24689 gdbstub_port=24689
)"; )";
} }

View File

@ -5,22 +5,16 @@
#include <algorithm> #include <algorithm>
#include <cstdlib> #include <cstdlib>
#include <string> #include <string>
#define SDL_MAIN_HANDLED #define SDL_MAIN_HANDLED
#include <SDL.h> #include <SDL.h>
#include <glad/glad.h> #include <glad/glad.h>
#include "citra/emu_window/emu_window_sdl2.h"
#include "common/key_map.h" #include "common/key_map.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/scm_rev.h" #include "common/scm_rev.h"
#include "common/string_util.h" #include "common/string_util.h"
#include "core/settings.h"
#include "core/hle/service/hid/hid.h" #include "core/hle/service/hid/hid.h"
#include "core/settings.h"
#include "citra/emu_window/emu_window_sdl2.h"
#include "video_core/video_core.h" #include "video_core/video_core.h"
void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
@ -40,9 +34,9 @@ void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) { void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {
if (state == SDL_PRESSED) { if (state == SDL_PRESSED) {
KeyMap::PressKey(*this, { key, keyboard_id }); KeyMap::PressKey(*this, {key, keyboard_id});
} else if (state == SDL_RELEASED) { } else if (state == SDL_RELEASED) {
KeyMap::ReleaseKey(*this, { key, keyboard_id }); KeyMap::ReleaseKey(*this, {key, keyboard_id});
} }
} }
@ -55,7 +49,8 @@ void EmuWindow_SDL2::OnResize() {
SDL_GetWindowSize(render_window, &width, &height); SDL_GetWindowSize(render_window, &width, &height);
NotifyFramebufferLayoutChanged(EmuWindow::FramebufferLayout::DefaultScreenLayout(width, height)); NotifyFramebufferLayoutChanged(
EmuWindow::FramebufferLayout::DefaultScreenLayout(width, height));
} }
EmuWindow_SDL2::EmuWindow_SDL2() { EmuWindow_SDL2::EmuWindow_SDL2() {
@ -80,12 +75,13 @@ EmuWindow_SDL2::EmuWindow_SDL2() {
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0);
std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc); std::string window_title =
render_window = SDL_CreateWindow(window_title.c_str(), Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc);
render_window = SDL_CreateWindow(
window_title.c_str(),
SDL_WINDOWPOS_UNDEFINED, // x position SDL_WINDOWPOS_UNDEFINED, // x position
SDL_WINDOWPOS_UNDEFINED, // y position SDL_WINDOWPOS_UNDEFINED, // y position
VideoCore::kScreenTopWidth, VideoCore::kScreenTopWidth, VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight,
VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight,
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
if (render_window == nullptr) { if (render_window == nullptr) {
@ -171,10 +167,14 @@ void EmuWindow_SDL2::DoneCurrent() {
void EmuWindow_SDL2::ReloadSetKeymaps() { void EmuWindow_SDL2::ReloadSetKeymaps() {
KeyMap::ClearKeyMapping(keyboard_id); KeyMap::ClearKeyMapping(keyboard_id);
for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
KeyMap::SetKeyMapping({ Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id }, KeyMap::mapping_targets[i]); KeyMap::SetKeyMapping(
{Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id},
KeyMap::mapping_targets[i]);
} }
} }
void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest(const std::pair<unsigned, unsigned>& minimal_size) { void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest(
const std::pair<unsigned, unsigned>& minimal_size) {
SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second); SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second);
} }

View File

@ -5,7 +5,6 @@
#pragma once #pragma once
#include <utility> #include <utility>
#include "common/emu_window.h" #include "common/emu_window.h"
struct SDL_Window; struct SDL_Window;
@ -47,7 +46,8 @@ private:
void OnResize(); void OnResize();
/// Called when a configuration change affects the minimal size of the window /// Called when a configuration change affects the minimal size of the window
void OnMinimalClientAreaChangeRequest(const std::pair<unsigned, unsigned>& minimal_size) override; void OnMinimalClientAreaChangeRequest(
const std::pair<unsigned, unsigned>& minimal_size) override;
/// Is the window still open? /// Is the window still open?
bool is_open = true; bool is_open = true;
@ -55,7 +55,7 @@ private:
/// Internal SDL2 render window /// Internal SDL2 render window
SDL_Window* render_window; SDL_Window* render_window;
using SDL_GLContext = void *; using SDL_GLContext = void*;
/// The OpenGL context associated with the window /// The OpenGL context associated with the window
SDL_GLContext gl_context; SDL_GLContext gl_context;

View File

@ -9,16 +9,13 @@
#endif #endif
#include "citra_qt/bootmanager.h" #include "citra_qt/bootmanager.h"
#include "common/key_map.h" #include "common/key_map.h"
#include "common/microprofile.h" #include "common/microprofile.h"
#include "common/scm_rev.h" #include "common/scm_rev.h"
#include "common/string_util.h" #include "common/string_util.h"
#include "core/core.h" #include "core/core.h"
#include "core/settings.h" #include "core/settings.h"
#include "core/system.h" #include "core/system.h"
#include "video_core/debug_utils/debug_utils.h" #include "video_core/debug_utils/debug_utils.h"
#include "video_core/video_core.h" #include "video_core/video_core.h"
@ -27,9 +24,8 @@
#define APP_TITLE APP_NAME " " APP_VERSION #define APP_TITLE APP_NAME " " APP_VERSION
#define COPYRIGHT "Copyright (C) 2013-2014 Citra Team" #define COPYRIGHT "Copyright (C) 2013-2014 Citra Team"
EmuThread::EmuThread(GRenderWindow* render_window) : EmuThread::EmuThread(GRenderWindow* render_window)
exec_step(false), running(false), stop_run(false), render_window(render_window) { : exec_step(false), running(false), stop_run(false), render_window(render_window) {}
}
void EmuThread::run() { void EmuThread::run() {
render_window->MakeCurrent(); render_window->MakeCurrent();
@ -64,7 +60,7 @@ void EmuThread::run() {
was_active = false; was_active = false;
} else { } else {
std::unique_lock<std::mutex> lock(running_mutex); std::unique_lock<std::mutex> lock(running_mutex);
running_cv.wait(lock, [this]{ return IsRunning() || exec_step || stop_run; }); running_cv.wait(lock, [this] { return IsRunning() || exec_step || stop_run; });
} }
} }
@ -78,14 +74,13 @@ void EmuThread::run() {
render_window->moveContext(); render_window->moveContext();
} }
// This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL context. // This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL
// context.
// The corresponding functionality is handled in EmuThread instead // The corresponding functionality is handled in EmuThread instead
class GGLWidgetInternal : public QGLWidget class GGLWidgetInternal : public QGLWidget {
{
public: public:
GGLWidgetInternal(QGLFormat fmt, GRenderWindow* parent) GGLWidgetInternal(QGLFormat fmt, GRenderWindow* parent)
: QGLWidget(fmt, parent), parent(parent) { : QGLWidget(fmt, parent), parent(parent) {}
}
void paintEvent(QPaintEvent* ev) override { void paintEvent(QPaintEvent* ev) override {
if (do_painting) { if (do_painting) {
@ -98,37 +93,43 @@ public:
parent->OnFramebufferSizeChanged(); parent->OnFramebufferSizeChanged();
} }
void DisablePainting() { do_painting = false; } void DisablePainting() {
void EnablePainting() { do_painting = true; } do_painting = false;
}
void EnablePainting() {
do_painting = true;
}
private: private:
GRenderWindow* parent; GRenderWindow* parent;
bool do_painting; bool do_painting;
}; };
GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) : GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
QWidget(parent), keyboard_id(0), emu_thread(emu_thread), child(nullptr) { : QWidget(parent), keyboard_id(0), emu_thread(emu_thread), child(nullptr) {
std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc); std::string window_title =
Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc);
setWindowTitle(QString::fromStdString(window_title)); setWindowTitle(QString::fromStdString(window_title));
keyboard_id = KeyMap::NewDeviceId(); keyboard_id = KeyMap::NewDeviceId();
ReloadSetKeymaps(); ReloadSetKeymaps();
} }
void GRenderWindow::moveContext() void GRenderWindow::moveContext() {
{
DoneCurrent(); DoneCurrent();
// We need to move GL context to the swapping thread in Qt5 // We need to move GL context to the swapping thread in Qt5
#if QT_VERSION > QT_VERSION_CHECK(5, 0, 0) #if QT_VERSION > QT_VERSION_CHECK(5, 0, 0)
// If the thread started running, move the GL Context to the new thread. Otherwise, move it back. // If the thread started running, move the GL Context to the new thread. Otherwise, move it
auto thread = (QThread::currentThread() == qApp->thread() && emu_thread != nullptr) ? emu_thread : qApp->thread(); // back.
auto thread = (QThread::currentThread() == qApp->thread() && emu_thread != nullptr)
? emu_thread
: qApp->thread();
child->context()->moveToThread(thread); child->context()->moveToThread(thread);
#endif #endif
} }
void GRenderWindow::SwapBuffers() void GRenderWindow::SwapBuffers() {
{
#if !defined(QT_NO_DEBUG) #if !defined(QT_NO_DEBUG)
// Qt debug runtime prints a bogus warning on the console if you haven't called makeCurrent // Qt debug runtime prints a bogus warning on the console if you haven't called makeCurrent
// since the last time you called swapBuffers. This presumably means something if you're using // since the last time you called swapBuffers. This presumably means something if you're using
@ -139,54 +140,48 @@ void GRenderWindow::SwapBuffers()
child->swapBuffers(); child->swapBuffers();
} }
void GRenderWindow::MakeCurrent() void GRenderWindow::MakeCurrent() {
{
child->makeCurrent(); child->makeCurrent();
} }
void GRenderWindow::DoneCurrent() void GRenderWindow::DoneCurrent() {
{
child->doneCurrent(); child->doneCurrent();
} }
void GRenderWindow::PollEvents() { void GRenderWindow::PollEvents() {}
}
// On Qt 5.0+, this correctly gets the size of the framebuffer (pixels). // On Qt 5.0+, this correctly gets the size of the framebuffer (pixels).
// //
// Older versions get the window size (density independent pixels), // Older versions get the window size (density independent pixels),
// and hence, do not support DPI scaling ("retina" displays). // and hence, do not support DPI scaling ("retina" displays).
// The result will be a viewport that is smaller than the extent of the window. // The result will be a viewport that is smaller than the extent of the window.
void GRenderWindow::OnFramebufferSizeChanged() void GRenderWindow::OnFramebufferSizeChanged() {
{ // Screen changes potentially incur a change in screen DPI, hence we should update the
// Screen changes potentially incur a change in screen DPI, hence we should update the framebuffer size // framebuffer size
qreal pixelRatio = windowPixelRatio(); qreal pixelRatio = windowPixelRatio();
unsigned width = child->QPaintDevice::width() * pixelRatio; unsigned width = child->QPaintDevice::width() * pixelRatio;
unsigned height = child->QPaintDevice::height() * pixelRatio; unsigned height = child->QPaintDevice::height() * pixelRatio;
NotifyFramebufferLayoutChanged(EmuWindow::FramebufferLayout::DefaultScreenLayout(width, height)); NotifyFramebufferLayoutChanged(
EmuWindow::FramebufferLayout::DefaultScreenLayout(width, height));
} }
void GRenderWindow::BackupGeometry() void GRenderWindow::BackupGeometry() {
{
geometry = ((QGLWidget*)this)->saveGeometry(); geometry = ((QGLWidget*)this)->saveGeometry();
} }
void GRenderWindow::RestoreGeometry() void GRenderWindow::RestoreGeometry() {
{
// We don't want to back up the geometry here (obviously) // We don't want to back up the geometry here (obviously)
QWidget::restoreGeometry(geometry); QWidget::restoreGeometry(geometry);
} }
void GRenderWindow::restoreGeometry(const QByteArray& geometry) void GRenderWindow::restoreGeometry(const QByteArray& geometry) {
{
// Make sure users of this class don't need to deal with backing up the geometry themselves // Make sure users of this class don't need to deal with backing up the geometry themselves
QWidget::restoreGeometry(geometry); QWidget::restoreGeometry(geometry);
BackupGeometry(); BackupGeometry();
} }
QByteArray GRenderWindow::saveGeometry() QByteArray GRenderWindow::saveGeometry() {
{
// If we are a top-level widget, store the current geometry // If we are a top-level widget, store the current geometry
// otherwise, store the last backup // otherwise, store the last backup
if (parent() == nullptr) if (parent() == nullptr)
@ -195,8 +190,7 @@ QByteArray GRenderWindow::saveGeometry()
return geometry; return geometry;
} }
qreal GRenderWindow::windowPixelRatio() qreal GRenderWindow::windowPixelRatio() {
{
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
// windowHandle() might not be accessible until the window is displayed to screen. // windowHandle() might not be accessible until the window is displayed to screen.
return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f; return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f;
@ -210,20 +204,16 @@ void GRenderWindow::closeEvent(QCloseEvent* event) {
QWidget::closeEvent(event); QWidget::closeEvent(event);
} }
void GRenderWindow::keyPressEvent(QKeyEvent* event) void GRenderWindow::keyPressEvent(QKeyEvent* event) {
{ KeyMap::PressKey(*this, {event->key(), keyboard_id});
KeyMap::PressKey(*this, { event->key(), keyboard_id });
} }
void GRenderWindow::keyReleaseEvent(QKeyEvent* event) void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
{ KeyMap::ReleaseKey(*this, {event->key(), keyboard_id});
KeyMap::ReleaseKey(*this, { event->key(), keyboard_id });
} }
void GRenderWindow::mousePressEvent(QMouseEvent *event) void GRenderWindow::mousePressEvent(QMouseEvent* event) {
{ if (event->button() == Qt::LeftButton) {
if (event->button() == Qt::LeftButton)
{
auto pos = event->pos(); auto pos = event->pos();
qreal pixelRatio = windowPixelRatio(); qreal pixelRatio = windowPixelRatio();
this->TouchPressed(static_cast<unsigned>(pos.x() * pixelRatio), this->TouchPressed(static_cast<unsigned>(pos.x() * pixelRatio),
@ -231,30 +221,28 @@ void GRenderWindow::mousePressEvent(QMouseEvent *event)
} }
} }
void GRenderWindow::mouseMoveEvent(QMouseEvent *event) void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
{
auto pos = event->pos(); auto pos = event->pos();
qreal pixelRatio = windowPixelRatio(); qreal pixelRatio = windowPixelRatio();
this->TouchMoved(std::max(static_cast<unsigned>(pos.x() * pixelRatio), 0u), this->TouchMoved(std::max(static_cast<unsigned>(pos.x() * pixelRatio), 0u),
std::max(static_cast<unsigned>(pos.y() * pixelRatio), 0u)); std::max(static_cast<unsigned>(pos.y() * pixelRatio), 0u));
} }
void GRenderWindow::mouseReleaseEvent(QMouseEvent *event) void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
{
if (event->button() == Qt::LeftButton) if (event->button() == Qt::LeftButton)
this->TouchReleased(); this->TouchReleased();
} }
void GRenderWindow::ReloadSetKeymaps() void GRenderWindow::ReloadSetKeymaps() {
{
KeyMap::ClearKeyMapping(keyboard_id); KeyMap::ClearKeyMapping(keyboard_id);
for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
KeyMap::SetKeyMapping({ Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id }, KeyMap::mapping_targets[i]); KeyMap::SetKeyMapping(
{Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id},
KeyMap::mapping_targets[i]);
} }
} }
void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height) void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height) {
{
NotifyClientAreaSizeChanged(std::make_pair(width, height)); NotifyClientAreaSizeChanged(std::make_pair(width, height));
} }
@ -267,7 +255,8 @@ void GRenderWindow::InitRenderTarget() {
delete layout(); delete layout();
} }
// TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, WA_DontShowOnScreen, WA_DeleteOnClose // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
// WA_DontShowOnScreen, WA_DeleteOnClose
QGLFormat fmt; QGLFormat fmt;
fmt.setVersion(3, 3); fmt.setVersion(3, 3);
fmt.setProfile(QGLFormat::CoreProfile); fmt.setProfile(QGLFormat::CoreProfile);
@ -279,7 +268,8 @@ void GRenderWindow::InitRenderTarget() {
child = new GGLWidgetInternal(fmt, this); child = new GGLWidgetInternal(fmt, this);
QBoxLayout* layout = new QHBoxLayout(this); QBoxLayout* layout = new QHBoxLayout(this);
resize(VideoCore::kScreenTopWidth, VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight); resize(VideoCore::kScreenTopWidth,
VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight);
layout->addWidget(child); layout->addWidget(child);
layout->setMargin(0); layout->setMargin(0);
setLayout(layout); setLayout(layout);
@ -292,7 +282,8 @@ void GRenderWindow::InitRenderTarget() {
BackupGeometry(); BackupGeometry();
} }
void GRenderWindow::OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) { void GRenderWindow::OnMinimalClientAreaChangeRequest(
const std::pair<unsigned, unsigned>& minimal_size) {
setMinimumSize(minimal_size.first, minimal_size.second); setMinimumSize(minimal_size.first, minimal_size.second);
} }
@ -306,11 +297,12 @@ void GRenderWindow::OnEmulationStopping() {
child->EnablePainting(); child->EnablePainting();
} }
void GRenderWindow::showEvent(QShowEvent * event) { void GRenderWindow::showEvent(QShowEvent* event) {
QWidget::showEvent(event); QWidget::showEvent(event);
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
// windowHandle() is not initialized until the Window is shown, so we connect it here. // windowHandle() is not initialized until the Window is shown, so we connect it here.
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) connect(this->windowHandle(), SIGNAL(screenChanged(QScreen*)), this,
connect(this->windowHandle(), SIGNAL(screenChanged(QScreen*)), this, SLOT(OnFramebufferSizeChanged()), Qt::UniqueConnection); SLOT(OnFramebufferSizeChanged()), Qt::UniqueConnection);
#endif #endif
} }

View File

@ -5,10 +5,8 @@
#include <atomic> #include <atomic>
#include <condition_variable> #include <condition_variable>
#include <mutex> #include <mutex>
#include <QGLWidget> #include <QGLWidget>
#include <QThread> #include <QThread>
#include "common/emu_window.h" #include "common/emu_window.h"
#include "common/thread.h" #include "common/thread.h"
@ -19,8 +17,7 @@ class GGLWidgetInternal;
class GMainWindow; class GMainWindow;
class GRenderWindow; class GRenderWindow;
class EmuThread : public QThread class EmuThread : public QThread {
{
Q_OBJECT Q_OBJECT
public: public:
@ -58,7 +55,9 @@ public:
* @return True if the emulation thread is running, otherwise false * @return True if the emulation thread is running, otherwise false
* @note This function is thread-safe * @note This function is thread-safe
*/ */
bool IsRunning() { return running; } bool IsRunning() {
return running;
}
/** /**
* Requests for the emulation thread to stop running * Requests for the emulation thread to stop running
@ -81,20 +80,23 @@ signals:
/** /**
* Emitted when the CPU has halted execution * Emitted when the CPU has halted execution
* *
* @warning When connecting to this signal from other threads, make sure to specify either Qt::QueuedConnection (invoke slot within the destination object's message thread) or even Qt::BlockingQueuedConnection (additionally block source thread until slot returns) * @warning When connecting to this signal from other threads, make sure to specify either
* Qt::QueuedConnection (invoke slot within the destination object's message thread) or even
* Qt::BlockingQueuedConnection (additionally block source thread until slot returns)
*/ */
void DebugModeEntered(); void DebugModeEntered();
/** /**
* Emitted right before the CPU continues execution * Emitted right before the CPU continues execution
* *
* @warning When connecting to this signal from other threads, make sure to specify either Qt::QueuedConnection (invoke slot within the destination object's message thread) or even Qt::BlockingQueuedConnection (additionally block source thread until slot returns) * @warning When connecting to this signal from other threads, make sure to specify either
* Qt::QueuedConnection (invoke slot within the destination object's message thread) or even
* Qt::BlockingQueuedConnection (additionally block source thread until slot returns)
*/ */
void DebugModeLeft(); void DebugModeLeft();
}; };
class GRenderWindow : public QWidget, public EmuWindow class GRenderWindow : public QWidget, public EmuWindow {
{
Q_OBJECT Q_OBJECT
public: public:
@ -118,9 +120,9 @@ public:
void keyPressEvent(QKeyEvent* event) override; void keyPressEvent(QKeyEvent* event) override;
void keyReleaseEvent(QKeyEvent* event) override; void keyReleaseEvent(QKeyEvent* event) override;
void mousePressEvent(QMouseEvent *event) override; void mousePressEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent* event) override;
void ReloadSetKeymaps() override; void ReloadSetKeymaps() override;
@ -140,7 +142,8 @@ signals:
void Closed(); void Closed();
private: private:
void OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) override; void OnMinimalClientAreaChangeRequest(
const std::pair<unsigned, unsigned>& minimal_size) override;
GGLWidgetInternal* child; GGLWidgetInternal* child;

View File

@ -3,10 +3,8 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <QSettings> #include <QSettings>
#include "citra_qt/config.h" #include "citra_qt/config.h"
#include "citra_qt/ui_settings.h" #include "citra_qt/ui_settings.h"
#include "common/file_util.h" #include "common/file_util.h"
Config::Config() { Config::Config() {
@ -20,24 +18,23 @@ Config::Config() {
const std::array<QVariant, Settings::NativeInput::NUM_INPUTS> Config::defaults = { const std::array<QVariant, Settings::NativeInput::NUM_INPUTS> Config::defaults = {
// directly mapped keys // directly mapped keys
Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_Q, Qt::Key_W, Qt::Key_1, Qt::Key_2,
Qt::Key_Q, Qt::Key_W, Qt::Key_1, Qt::Key_2, Qt::Key_M, Qt::Key_N, Qt::Key_B, Qt::Key_T, Qt::Key_G, Qt::Key_F, Qt::Key_H, Qt::Key_I,
Qt::Key_M, Qt::Key_N, Qt::Key_B, Qt::Key_K, Qt::Key_J, Qt::Key_L,
Qt::Key_T, Qt::Key_G, Qt::Key_F, Qt::Key_H,
Qt::Key_I, Qt::Key_K, Qt::Key_J, Qt::Key_L,
// indirectly mapped keys // indirectly mapped keys
Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right, Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right, Qt::Key_D,
Qt::Key_D,
}; };
void Config::ReadValues() { void Config::ReadValues() {
qt_config->beginGroup("Controls"); qt_config->beginGroup("Controls");
for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
Settings::values.input_mappings[Settings::NativeInput::All[i]] = Settings::values.input_mappings[Settings::NativeInput::All[i]] =
qt_config->value(QString::fromStdString(Settings::NativeInput::Mapping[i]), defaults[i]).toInt(); qt_config->value(QString::fromStdString(Settings::NativeInput::Mapping[i]), defaults[i])
.toInt();
} }
Settings::values.pad_circle_modifier_scale = qt_config->value("pad_circle_modifier_scale", 0.5).toFloat(); Settings::values.pad_circle_modifier_scale =
qt_config->value("pad_circle_modifier_scale", 0.5).toFloat();
qt_config->endGroup(); qt_config->endGroup();
qt_config->beginGroup("Core"); qt_config->beginGroup("Core");
@ -48,7 +45,8 @@ void Config::ReadValues() {
qt_config->beginGroup("Renderer"); qt_config->beginGroup("Renderer");
Settings::values.use_hw_renderer = qt_config->value("use_hw_renderer", true).toBool(); Settings::values.use_hw_renderer = qt_config->value("use_hw_renderer", true).toBool();
Settings::values.use_shader_jit = qt_config->value("use_shader_jit", true).toBool(); Settings::values.use_shader_jit = qt_config->value("use_shader_jit", true).toBool();
Settings::values.use_scaled_resolution = qt_config->value("use_scaled_resolution", false).toBool(); Settings::values.use_scaled_resolution =
qt_config->value("use_scaled_resolution", false).toBool();
Settings::values.use_vsync = qt_config->value("use_vsync", false).toBool(); Settings::values.use_vsync = qt_config->value("use_vsync", false).toBool();
Settings::values.bg_red = qt_config->value("bg_red", 1.0).toFloat(); Settings::values.bg_red = qt_config->value("bg_red", 1.0).toFloat();
@ -58,7 +56,8 @@ void Config::ReadValues() {
qt_config->beginGroup("Audio"); qt_config->beginGroup("Audio");
Settings::values.sink_id = qt_config->value("output_engine", "auto").toString().toStdString(); Settings::values.sink_id = qt_config->value("output_engine", "auto").toString().toStdString();
Settings::values.enable_audio_stretching = qt_config->value("enable_audio_stretching", true).toBool(); Settings::values.enable_audio_stretching =
qt_config->value("enable_audio_stretching", true).toBool();
qt_config->endGroup(); qt_config->endGroup();
qt_config->beginGroup("Data Storage"); qt_config->beginGroup("Data Storage");
@ -84,10 +83,14 @@ void Config::ReadValues() {
qt_config->beginGroup("UILayout"); qt_config->beginGroup("UILayout");
UISettings::values.geometry = qt_config->value("geometry").toByteArray(); UISettings::values.geometry = qt_config->value("geometry").toByteArray();
UISettings::values.state = qt_config->value("state").toByteArray(); UISettings::values.state = qt_config->value("state").toByteArray();
UISettings::values.renderwindow_geometry = qt_config->value("geometryRenderWindow").toByteArray(); UISettings::values.renderwindow_geometry =
UISettings::values.gamelist_header_state = qt_config->value("gameListHeaderState").toByteArray(); qt_config->value("geometryRenderWindow").toByteArray();
UISettings::values.microprofile_geometry = qt_config->value("microProfileDialogGeometry").toByteArray(); UISettings::values.gamelist_header_state =
UISettings::values.microprofile_visible = qt_config->value("microProfileDialogVisible", false).toBool(); qt_config->value("gameListHeaderState").toByteArray();
UISettings::values.microprofile_geometry =
qt_config->value("microProfileDialogGeometry").toByteArray();
UISettings::values.microprofile_visible =
qt_config->value("microProfileDialogVisible", false).toBool();
qt_config->endGroup(); qt_config->endGroup();
qt_config->beginGroup("Paths"); qt_config->beginGroup("Paths");
@ -106,8 +109,8 @@ void Config::ReadValues() {
QStringList hotkeys = qt_config->childGroups(); QStringList hotkeys = qt_config->childGroups();
for (auto hotkey : hotkeys) { for (auto hotkey : hotkeys) {
qt_config->beginGroup(hotkey); qt_config->beginGroup(hotkey);
UISettings::values.shortcuts.emplace_back( UISettings::values.shortcuts.emplace_back(UISettings::Shortcut(
UISettings::Shortcut(group + "/" + hotkey, group + "/" + hotkey,
UISettings::ContextualShortcut(qt_config->value("KeySeq").toString(), UISettings::ContextualShortcut(qt_config->value("KeySeq").toString(),
qt_config->value("Context").toInt()))); qt_config->value("Context").toInt())));
qt_config->endGroup(); qt_config->endGroup();
@ -119,7 +122,7 @@ void Config::ReadValues() {
UISettings::values.single_window_mode = qt_config->value("singleWindowMode", true).toBool(); UISettings::values.single_window_mode = qt_config->value("singleWindowMode", true).toBool();
UISettings::values.display_titlebar = qt_config->value("displayTitleBars", true).toBool(); UISettings::values.display_titlebar = qt_config->value("displayTitleBars", true).toBool();
UISettings::values.confirm_before_closing = qt_config->value("confirmClose",true).toBool(); UISettings::values.confirm_before_closing = qt_config->value("confirmClose", true).toBool();
UISettings::values.first_start = qt_config->value("firstStart", true).toBool(); UISettings::values.first_start = qt_config->value("firstStart", true).toBool();
qt_config->endGroup(); qt_config->endGroup();
@ -131,7 +134,8 @@ void Config::SaveValues() {
qt_config->setValue(QString::fromStdString(Settings::NativeInput::Mapping[i]), qt_config->setValue(QString::fromStdString(Settings::NativeInput::Mapping[i]),
Settings::values.input_mappings[Settings::NativeInput::All[i]]); Settings::values.input_mappings[Settings::NativeInput::All[i]]);
} }
qt_config->setValue("pad_circle_modifier_scale", (double)Settings::values.pad_circle_modifier_scale); qt_config->setValue("pad_circle_modifier_scale",
(double)Settings::values.pad_circle_modifier_scale);
qt_config->endGroup(); qt_config->endGroup();
qt_config->beginGroup("Core"); qt_config->beginGroup("Core");

View File

@ -6,7 +6,6 @@
#include <string> #include <string>
#include <QVariant> #include <QVariant>
#include "core/settings.h" #include "core/settings.h"
class QSettings; class QSettings;
@ -17,6 +16,7 @@ class Config {
void ReadValues(); void ReadValues();
void SaveValues(); void SaveValues();
public: public:
Config(); Config();
~Config(); ~Config();

View File

@ -3,16 +3,12 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include "audio_core/sink_details.h" #include "audio_core/sink_details.h"
#include "citra_qt/configure_audio.h" #include "citra_qt/configure_audio.h"
#include "core/settings.h"
#include "ui_configure_audio.h" #include "ui_configure_audio.h"
#include "core/settings.h" ConfigureAudio::ConfigureAudio(QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureAudio>()) {
ConfigureAudio::ConfigureAudio(QWidget* parent) :
QWidget(parent),
ui(std::make_unique<Ui::ConfigureAudio>())
{
ui->setupUi(this); ui->setupUi(this);
ui->output_sink_combo_box->clear(); ui->output_sink_combo_box->clear();
@ -24,8 +20,7 @@ ConfigureAudio::ConfigureAudio(QWidget* parent) :
this->setConfiguration(); this->setConfiguration();
} }
ConfigureAudio::~ConfigureAudio() { ConfigureAudio::~ConfigureAudio() {}
}
void ConfigureAudio::setConfiguration() { void ConfigureAudio::setConfiguration() {
int new_sink_index = 0; int new_sink_index = 0;
@ -41,7 +36,9 @@ void ConfigureAudio::setConfiguration() {
} }
void ConfigureAudio::applyConfiguration() { void ConfigureAudio::applyConfiguration() {
Settings::values.sink_id = ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex()).toStdString(); Settings::values.sink_id =
ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex())
.toStdString();
Settings::values.enable_audio_stretching = ui->toggle_audio_stretching->isChecked(); Settings::values.enable_audio_stretching = ui->toggle_audio_stretching->isChecked();
Settings::Apply(); Settings::Apply();
} }

View File

@ -3,20 +3,15 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include "citra_qt/configure_debug.h" #include "citra_qt/configure_debug.h"
#include "core/settings.h"
#include "ui_configure_debug.h" #include "ui_configure_debug.h"
#include "core/settings.h" ConfigureDebug::ConfigureDebug(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureDebug) {
ConfigureDebug::ConfigureDebug(QWidget *parent) :
QWidget(parent),
ui(new Ui::ConfigureDebug)
{
ui->setupUi(this); ui->setupUi(this);
this->setConfiguration(); this->setConfiguration();
} }
ConfigureDebug::~ConfigureDebug() { ConfigureDebug::~ConfigureDebug() {}
}
void ConfigureDebug::setConfiguration() { void ConfigureDebug::setConfiguration() {
ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub); ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub);

View File

@ -11,12 +11,11 @@ namespace Ui {
class ConfigureDebug; class ConfigureDebug;
} }
class ConfigureDebug : public QWidget class ConfigureDebug : public QWidget {
{
Q_OBJECT Q_OBJECT
public: public:
explicit ConfigureDebug(QWidget *parent = nullptr); explicit ConfigureDebug(QWidget* parent = nullptr);
~ConfigureDebug(); ~ConfigureDebug();
void applyConfiguration(); void applyConfiguration();

View File

@ -4,24 +4,17 @@
#include "citra_qt/config.h" #include "citra_qt/config.h"
#include "citra_qt/configure_dialog.h" #include "citra_qt/configure_dialog.h"
#include "core/settings.h"
#include "ui_configure.h" #include "ui_configure.h"
ConfigureDialog::ConfigureDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ConfigureDialog) {
#include "core/settings.h"
ConfigureDialog::ConfigureDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::ConfigureDialog)
{
ui->setupUi(this); ui->setupUi(this);
this->setConfiguration(); this->setConfiguration();
} }
ConfigureDialog::~ConfigureDialog() { ConfigureDialog::~ConfigureDialog() {}
}
void ConfigureDialog::setConfiguration() { void ConfigureDialog::setConfiguration() {}
}
void ConfigureDialog::applyConfiguration() { void ConfigureDialog::applyConfiguration() {
ui->generalTab->applyConfiguration(); ui->generalTab->applyConfiguration();

View File

@ -11,12 +11,11 @@ namespace Ui {
class ConfigureDialog; class ConfigureDialog;
} }
class ConfigureDialog : public QDialog class ConfigureDialog : public QDialog {
{
Q_OBJECT Q_OBJECT
public: public:
explicit ConfigureDialog(QWidget *parent); explicit ConfigureDialog(QWidget* parent);
~ConfigureDialog(); ~ConfigureDialog();
void applyConfiguration(); void applyConfiguration();

View File

@ -4,23 +4,20 @@
#include "citra_qt/configure_general.h" #include "citra_qt/configure_general.h"
#include "citra_qt/ui_settings.h" #include "citra_qt/ui_settings.h"
#include "ui_configure_general.h"
#include "core/settings.h" #include "core/settings.h"
#include "core/system.h" #include "core/system.h"
#include "ui_configure_general.h"
ConfigureGeneral::ConfigureGeneral(QWidget* parent)
: QWidget(parent), ui(new Ui::ConfigureGeneral) {
ConfigureGeneral::ConfigureGeneral(QWidget *parent) :
QWidget(parent),
ui(new Ui::ConfigureGeneral)
{
ui->setupUi(this); ui->setupUi(this);
this->setConfiguration(); this->setConfiguration();
ui->toggle_cpu_jit->setEnabled(!System::IsPoweredOn()); ui->toggle_cpu_jit->setEnabled(!System::IsPoweredOn());
} }
ConfigureGeneral::~ConfigureGeneral() { ConfigureGeneral::~ConfigureGeneral() {}
}
void ConfigureGeneral::setConfiguration() { void ConfigureGeneral::setConfiguration() {
ui->toggle_deepscan->setChecked(UISettings::values.gamedir_deepscan); ui->toggle_deepscan->setChecked(UISettings::values.gamedir_deepscan);

View File

@ -11,12 +11,11 @@ namespace Ui {
class ConfigureGeneral; class ConfigureGeneral;
} }
class ConfigureGeneral : public QWidget class ConfigureGeneral : public QWidget {
{
Q_OBJECT Q_OBJECT
public: public:
explicit ConfigureGeneral(QWidget *parent = nullptr); explicit ConfigureGeneral(QWidget* parent = nullptr);
~ConfigureGeneral(); ~ConfigureGeneral();
void applyConfiguration(); void applyConfiguration();

View File

@ -3,23 +3,20 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include "citra_qt/configure_graphics.h" #include "citra_qt/configure_graphics.h"
#include "ui_configure_graphics.h"
#include "core/settings.h" #include "core/settings.h"
#include "core/system.h" #include "core/system.h"
#include "ui_configure_graphics.h"
ConfigureGraphics::ConfigureGraphics(QWidget* parent)
: QWidget(parent), ui(new Ui::ConfigureGraphics) {
ConfigureGraphics::ConfigureGraphics(QWidget *parent) :
QWidget(parent),
ui(new Ui::ConfigureGraphics)
{
ui->setupUi(this); ui->setupUi(this);
this->setConfiguration(); this->setConfiguration();
ui->toggle_vsync->setEnabled(!System::IsPoweredOn()); ui->toggle_vsync->setEnabled(!System::IsPoweredOn());
} }
ConfigureGraphics::~ConfigureGraphics() { ConfigureGraphics::~ConfigureGraphics() {}
}
void ConfigureGraphics::setConfiguration() { void ConfigureGraphics::setConfiguration() {
ui->toggle_hw_renderer->setChecked(Settings::values.use_hw_renderer); ui->toggle_hw_renderer->setChecked(Settings::values.use_hw_renderer);

View File

@ -11,12 +11,11 @@ namespace Ui {
class ConfigureGraphics; class ConfigureGraphics;
} }
class ConfigureGraphics : public QWidget class ConfigureGraphics : public QWidget {
{
Q_OBJECT Q_OBJECT
public: public:
explicit ConfigureGraphics(QWidget *parent = nullptr); explicit ConfigureGraphics(QWidget* parent = nullptr);
~ConfigureGraphics(); ~ConfigureGraphics();
void applyConfiguration(); void applyConfiguration();

View File

@ -5,38 +5,39 @@
#include <memory> #include <memory>
#include <utility> #include <utility>
#include <QTimer> #include <QTimer>
#include "citra_qt/configure_input.h" #include "citra_qt/configure_input.h"
ConfigureInput::ConfigureInput(QWidget* parent) : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()) { ConfigureInput::ConfigureInput(QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()) {
ui->setupUi(this); ui->setupUi(this);
// Initialize mapping of input enum to UI button. // Initialize mapping of input enum to UI button.
input_mapping = { input_mapping = {
{ std::make_pair(Settings::NativeInput::Values::A, ui->buttonA) }, {Settings::NativeInput::Values::A, ui->buttonA},
{ std::make_pair(Settings::NativeInput::Values::B, ui->buttonB) }, {Settings::NativeInput::Values::B, ui->buttonB},
{ std::make_pair(Settings::NativeInput::Values::X, ui->buttonX) }, {Settings::NativeInput::Values::X, ui->buttonX},
{ std::make_pair(Settings::NativeInput::Values::Y, ui->buttonY) }, {Settings::NativeInput::Values::Y, ui->buttonY},
{ std::make_pair(Settings::NativeInput::Values::L, ui->buttonL) }, {Settings::NativeInput::Values::L, ui->buttonL},
{ std::make_pair(Settings::NativeInput::Values::R, ui->buttonR) }, {Settings::NativeInput::Values::R, ui->buttonR},
{ std::make_pair(Settings::NativeInput::Values::ZL, ui->buttonZL) }, {Settings::NativeInput::Values::ZL, ui->buttonZL},
{ std::make_pair(Settings::NativeInput::Values::ZR, ui->buttonZR) }, {Settings::NativeInput::Values::ZR, ui->buttonZR},
{ std::make_pair(Settings::NativeInput::Values::START, ui->buttonStart) }, {Settings::NativeInput::Values::START, ui->buttonStart},
{ std::make_pair(Settings::NativeInput::Values::SELECT, ui->buttonSelect) }, {Settings::NativeInput::Values::SELECT, ui->buttonSelect},
{ std::make_pair(Settings::NativeInput::Values::HOME, ui->buttonHome) }, {Settings::NativeInput::Values::HOME, ui->buttonHome},
{ std::make_pair(Settings::NativeInput::Values::DUP, ui->buttonDpadUp) }, {Settings::NativeInput::Values::DUP, ui->buttonDpadUp},
{ std::make_pair(Settings::NativeInput::Values::DDOWN, ui->buttonDpadDown) }, {Settings::NativeInput::Values::DDOWN, ui->buttonDpadDown},
{ std::make_pair(Settings::NativeInput::Values::DLEFT, ui->buttonDpadLeft) }, {Settings::NativeInput::Values::DLEFT, ui->buttonDpadLeft},
{ std::make_pair(Settings::NativeInput::Values::DRIGHT, ui->buttonDpadRight) }, {Settings::NativeInput::Values::DRIGHT, ui->buttonDpadRight},
{ std::make_pair(Settings::NativeInput::Values::CUP, ui->buttonCStickUp) }, {Settings::NativeInput::Values::CUP, ui->buttonCStickUp},
{ std::make_pair(Settings::NativeInput::Values::CDOWN, ui->buttonCStickDown) }, {Settings::NativeInput::Values::CDOWN, ui->buttonCStickDown},
{ std::make_pair(Settings::NativeInput::Values::CLEFT, ui->buttonCStickLeft) }, {Settings::NativeInput::Values::CLEFT, ui->buttonCStickLeft},
{ std::make_pair(Settings::NativeInput::Values::CRIGHT, ui->buttonCStickRight) }, {Settings::NativeInput::Values::CRIGHT, ui->buttonCStickRight},
{ std::make_pair(Settings::NativeInput::Values::CIRCLE_UP, ui->buttonCircleUp) }, {Settings::NativeInput::Values::CIRCLE_UP, ui->buttonCircleUp},
{ std::make_pair(Settings::NativeInput::Values::CIRCLE_DOWN, ui->buttonCircleDown) }, {Settings::NativeInput::Values::CIRCLE_DOWN, ui->buttonCircleDown},
{ std::make_pair(Settings::NativeInput::Values::CIRCLE_LEFT, ui->buttonCircleLeft) }, {Settings::NativeInput::Values::CIRCLE_LEFT, ui->buttonCircleLeft},
{ std::make_pair(Settings::NativeInput::Values::CIRCLE_RIGHT, ui->buttonCircleRight) }, {Settings::NativeInput::Values::CIRCLE_RIGHT, ui->buttonCircleRight},
{ std::make_pair(Settings::NativeInput::Values::CIRCLE_MODIFIER, ui->buttonCircleMod) }, {Settings::NativeInput::Values::CIRCLE_MODIFIER, ui->buttonCircleMod},
}; };
// Attach handle click method to each button click. // Attach handle click method to each button click.
@ -47,7 +48,10 @@ ConfigureInput::ConfigureInput(QWidget* parent) : QWidget(parent), ui(std::make_
setFocusPolicy(Qt::ClickFocus); setFocusPolicy(Qt::ClickFocus);
timer = new QTimer(this); timer = new QTimer(this);
timer->setSingleShot(true); timer->setSingleShot(true);
connect(timer, &QTimer::timeout, this, [&]() { key_pressed = Qt::Key_Escape; setKey(); }); connect(timer, &QTimer::timeout, this, [&]() {
key_pressed = Qt::Key_Escape;
setKey();
});
this->setConfiguration(); this->setConfiguration();
} }
@ -59,7 +63,7 @@ void ConfigureInput::handleClick() {
grabKeyboard(); grabKeyboard();
grabMouse(); grabMouse();
changing_button = sender; changing_button = sender;
timer->start(5000); //Cancel after 5 seconds timer->start(5000); // Cancel after 5 seconds
} }
void ConfigureInput::applyConfiguration() { void ConfigureInput::applyConfiguration() {

View File

@ -4,9 +4,9 @@
#pragma once #pragma once
#include <QWidget> #include <memory>
#include <QKeyEvent> #include <QKeyEvent>
#include <QWidget>
#include "citra_qt/config.h" #include "citra_qt/config.h"
#include "core/settings.h" #include "core/settings.h"
#include "ui_configure_input.h" #include "ui_configure_input.h"
@ -16,7 +16,7 @@ class QString;
class QTimer; class QTimer;
namespace Ui { namespace Ui {
class ConfigureInput; class ConfigureInput;
} }
class ConfigureInput : public QWidget { class ConfigureInput : public QWidget {
@ -39,7 +39,8 @@ private:
/// Load configuration settings into button text /// Load configuration settings into button text
void setConfiguration(); void setConfiguration();
/// Check all inputs for duplicate keys. Clears out any other button with the same value as this button's new value. /// Check all inputs for duplicate keys. Clears out any other button with the same value as this
/// button's new value.
void removeDuplicates(const QString& newValue); void removeDuplicates(const QString& newValue);
/// Handle key press event for input tab when a button is 'waiting'. /// Handle key press event for input tab when a button is 'waiting'.

View File

@ -4,27 +4,24 @@
#include "citra_qt/configure_system.h" #include "citra_qt/configure_system.h"
#include "citra_qt/ui_settings.h" #include "citra_qt/ui_settings.h"
#include "core/hle/service/cfg/cfg.h"
#include "core/hle/service/fs/archive.h"
#include "core/system.h"
#include "ui_configure_system.h" #include "ui_configure_system.h"
#include "core/hle/service/fs/archive.h"
#include "core/hle/service/cfg/cfg.h"
#include "core/system.h"
static const std::array<int, 12> days_in_month = {{ static const std::array<int, 12> days_in_month = {{
31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
}}; }};
ConfigureSystem::ConfigureSystem(QWidget *parent) : ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureSystem) {
QWidget(parent),
ui(new Ui::ConfigureSystem) {
ui->setupUi(this); ui->setupUi(this);
connect(ui->combo_birthmonth, SIGNAL(currentIndexChanged(int)), SLOT(updateBirthdayComboBox(int))); connect(ui->combo_birthmonth, SIGNAL(currentIndexChanged(int)),
SLOT(updateBirthdayComboBox(int)));
this->setConfiguration(); this->setConfiguration();
} }
ConfigureSystem::~ConfigureSystem() { ConfigureSystem::~ConfigureSystem() {}
}
void ConfigureSystem::setConfiguration() { void ConfigureSystem::setConfiguration() {
enabled = !System::IsPoweredOn(); enabled = !System::IsPoweredOn();
@ -54,13 +51,17 @@ void ConfigureSystem::setConfiguration() {
void ConfigureSystem::ReadSystemSettings() { void ConfigureSystem::ReadSystemSettings() {
// set username // set username
username = Service::CFG::GetUsername(); username = Service::CFG::GetUsername();
// ui->edit_username->setText(QString::fromStdU16String(username)); // TODO(wwylele): Use this when we move to Qt 5.5 // TODO(wwylele): Use this when we move to Qt 5.5
ui->edit_username->setText(QString::fromUtf16(reinterpret_cast<const ushort*>(username.data()))); // ui->edit_username->setText(QString::fromStdU16String(username));
ui->edit_username->setText(
QString::fromUtf16(reinterpret_cast<const ushort*>(username.data())));
// set birthday // set birthday
std::tie(birthmonth, birthday) = Service::CFG::GetBirthday(); std::tie(birthmonth, birthday) = Service::CFG::GetBirthday();
ui->combo_birthmonth->setCurrentIndex(birthmonth - 1); ui->combo_birthmonth->setCurrentIndex(birthmonth - 1);
updateBirthdayComboBox(birthmonth - 1); // explicitly update it because the signal from setCurrentIndex is not reliable updateBirthdayComboBox(
birthmonth -
1); // explicitly update it because the signal from setCurrentIndex is not reliable
ui->combo_birthday->setCurrentIndex(birthday - 1); ui->combo_birthday->setCurrentIndex(birthday - 1);
// set system language // set system language
@ -79,8 +80,10 @@ void ConfigureSystem::applyConfiguration() {
bool modified = false; bool modified = false;
// apply username // apply username
// std::u16string new_username = ui->edit_username->text().toStdU16String(); // TODO(wwylele): Use this when we move to Qt 5.5 // TODO(wwylele): Use this when we move to Qt 5.5
std::u16string new_username(reinterpret_cast<const char16_t*>(ui->edit_username->text().utf16())); // std::u16string new_username = ui->edit_username->text().toStdU16String();
std::u16string new_username(
reinterpret_cast<const char16_t*>(ui->edit_username->text().utf16()));
if (new_username != username) { if (new_username != username) {
Service::CFG::SetUsername(new_username); Service::CFG::SetUsername(new_username);
modified = true; modified = true;

View File

@ -11,12 +11,11 @@ namespace Ui {
class ConfigureSystem; class ConfigureSystem;
} }
class ConfigureSystem : public QWidget class ConfigureSystem : public QWidget {
{
Q_OBJECT Q_OBJECT
public: public:
explicit ConfigureSystem(QWidget *parent = nullptr); explicit ConfigureSystem(QWidget* parent = nullptr);
~ConfigureSystem(); ~ConfigureSystem();
void applyConfiguration(); void applyConfiguration();

View File

@ -3,19 +3,15 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <QStandardItemModel> #include <QStandardItemModel>
#include "citra_qt/debugger/callstack.h" #include "citra_qt/debugger/callstack.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/symbols.h" #include "common/symbols.h"
#include "core/core.h"
#include "core/memory.h"
#include "core/arm/arm_interface.h" #include "core/arm/arm_interface.h"
#include "core/arm/disassembler/arm_disasm.h" #include "core/arm/disassembler/arm_disasm.h"
#include "core/core.h"
#include "core/memory.h"
CallstackWidget::CallstackWidget(QWidget* parent): QDockWidget(parent) CallstackWidget::CallstackWidget(QWidget* parent) : QDockWidget(parent) {
{
ui.setupUi(this); ui.setupUi(this);
callstack_model = new QStandardItemModel(this); callstack_model = new QStandardItemModel(this);
@ -27,29 +23,26 @@ CallstackWidget::CallstackWidget(QWidget* parent): QDockWidget(parent)
ui.treeView->setModel(callstack_model); ui.treeView->setModel(callstack_model);
} }
void CallstackWidget::OnDebugModeEntered() void CallstackWidget::OnDebugModeEntered() {
{
// Stack pointer // Stack pointer
const u32 sp = Core::g_app_core->GetReg(13); const u32 sp = Core::g_app_core->GetReg(13);
Clear(); Clear();
int counter = 0; int counter = 0;
for (u32 addr = 0x10000000; addr >= sp; addr -= 4) for (u32 addr = 0x10000000; addr >= sp; addr -= 4) {
{
if (!Memory::IsValidVirtualAddress(addr)) if (!Memory::IsValidVirtualAddress(addr))
break; break;
const u32 ret_addr = Memory::Read32(addr); const u32 ret_addr = Memory::Read32(addr);
const u32 call_addr = ret_addr - 4; //get call address??? const u32 call_addr = ret_addr - 4; // get call address???
if (!Memory::IsValidVirtualAddress(call_addr)) if (!Memory::IsValidVirtualAddress(call_addr))
break; break;
/* TODO (mattvail) clean me, move to debugger interface */ /* TODO (mattvail) clean me, move to debugger interface */
u32 insn = Memory::Read32(call_addr); u32 insn = Memory::Read32(call_addr);
if (ARM_Disasm::Decode(insn) == OP_BL) if (ARM_Disasm::Decode(insn) == OP_BL) {
{
std::string name; std::string name;
// ripped from disasm // ripped from disasm
u8 cond = (insn >> 28) & 0xf; u8 cond = (insn >> 28) & 0xf;
@ -63,12 +56,18 @@ void CallstackWidget::OnDebugModeEntered()
i_offset += 8; i_offset += 8;
const u32 func_addr = call_addr + i_offset; const u32 func_addr = call_addr + i_offset;
callstack_model->setItem(counter, 0, new QStandardItem(QString("0x%1").arg(addr, 8, 16, QLatin1Char('0')))); callstack_model->setItem(
callstack_model->setItem(counter, 1, new QStandardItem(QString("0x%1").arg(ret_addr, 8, 16, QLatin1Char('0')))); counter, 0, new QStandardItem(QString("0x%1").arg(addr, 8, 16, QLatin1Char('0'))));
callstack_model->setItem(counter, 2, new QStandardItem(QString("0x%1").arg(call_addr, 8, 16, QLatin1Char('0')))); callstack_model->setItem(counter, 1, new QStandardItem(QString("0x%1").arg(
ret_addr, 8, 16, QLatin1Char('0'))));
callstack_model->setItem(counter, 2, new QStandardItem(QString("0x%1").arg(
call_addr, 8, 16, QLatin1Char('0'))));
name = Symbols::HasSymbol(func_addr) ? Symbols::GetSymbol(func_addr).name : "unknown"; name = Symbols::HasSymbol(func_addr) ? Symbols::GetSymbol(func_addr).name : "unknown";
callstack_model->setItem(counter, 3, new QStandardItem(QString("%1_%2").arg(QString::fromStdString(name)) callstack_model->setItem(
counter, 3, new QStandardItem(
QString("%1_%2")
.arg(QString::fromStdString(name))
.arg(QString("0x%1").arg(func_addr, 8, 16, QLatin1Char('0'))))); .arg(QString("0x%1").arg(func_addr, 8, 16, QLatin1Char('0')))));
counter++; counter++;
@ -76,13 +75,9 @@ void CallstackWidget::OnDebugModeEntered()
} }
} }
void CallstackWidget::OnDebugModeLeft() void CallstackWidget::OnDebugModeLeft() {}
{
} void CallstackWidget::Clear() {
void CallstackWidget::Clear()
{
for (int row = 0; row < callstack_model->rowCount(); row++) { for (int row = 0; row < callstack_model->rowCount(); row++) {
for (int column = 0; column < callstack_model->columnCount(); column++) { for (int column = 0; column < callstack_model->columnCount(); column++) {
callstack_model->setItem(row, column, new QStandardItem()); callstack_model->setItem(row, column, new QStandardItem());

View File

@ -7,8 +7,7 @@
class QStandardItemModel; class QStandardItemModel;
class CallstackWidget : public QDockWidget class CallstackWidget : public QDockWidget {
{
Q_OBJECT Q_OBJECT
public: public:

View File

@ -3,23 +3,20 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <QShortcut> #include <QShortcut>
#include "citra_qt/bootmanager.h" #include "citra_qt/bootmanager.h"
#include "citra_qt/hotkeys.h"
#include "citra_qt/debugger/disassembler.h" #include "citra_qt/debugger/disassembler.h"
#include "citra_qt/hotkeys.h"
#include "citra_qt/util/util.h" #include "citra_qt/util/util.h"
#include "common/break_points.h" #include "common/break_points.h"
#include "common/symbols.h" #include "common/symbols.h"
#include "core/core.h"
#include "core/memory.h"
#include "core/arm/arm_interface.h" #include "core/arm/arm_interface.h"
#include "core/arm/disassembler/arm_disasm.h" #include "core/arm/disassembler/arm_disasm.h"
#include "core/core.h"
#include "core/memory.h"
DisassemblerModel::DisassemblerModel(QObject* parent) : DisassemblerModel::DisassemblerModel(QObject* parent)
QAbstractListModel(parent), base_address(0), code_size(0), program_counter(0), selection(QModelIndex()) { : QAbstractListModel(parent), base_address(0), code_size(0), program_counter(0),
} selection(QModelIndex()) {}
int DisassemblerModel::columnCount(const QModelIndex& parent) const { int DisassemblerModel::columnCount(const QModelIndex& parent) const {
return 3; return 3;
@ -31,8 +28,7 @@ int DisassemblerModel::rowCount(const QModelIndex& parent) const {
QVariant DisassemblerModel::data(const QModelIndex& index, int role) const { QVariant DisassemblerModel::data(const QModelIndex& index, int role) const {
switch (role) { switch (role) {
case Qt::DisplayRole: case Qt::DisplayRole: {
{
u32 address = base_address + index.row() * 4; u32 address = base_address + index.row() * 4;
u32 instr = Memory::Read32(address); u32 instr = Memory::Read32(address);
std::string disassembly = ARM_Disasm::Disassemble(address, instr); std::string disassembly = ARM_Disasm::Disassemble(address, instr);
@ -42,9 +38,10 @@ QVariant DisassemblerModel::data(const QModelIndex& index, int role) const {
} else if (index.column() == 1) { } else if (index.column() == 1) {
return QString::fromStdString(disassembly); return QString::fromStdString(disassembly);
} else if (index.column() == 2) { } else if (index.column() == 2) {
if(Symbols::HasSymbol(address)) { if (Symbols::HasSymbol(address)) {
TSymbol symbol = Symbols::GetSymbol(address); TSymbol symbol = Symbols::GetSymbol(address);
return QString("%1 - Size:%2").arg(QString::fromStdString(symbol.name)) return QString("%1 - Size:%2")
.arg(QString::fromStdString(symbol.name))
.arg(symbol.size / 4); // divide by 4 to get instruction count .arg(symbol.size / 4); // divide by 4 to get instruction count
} else if (ARM_Disasm::Decode(instr) == OP_BL) { } else if (ARM_Disasm::Decode(instr) == OP_BL) {
u32 offset = instr & 0xFFFFFF; u32 offset = instr & 0xFFFFFF;
@ -65,8 +62,7 @@ QVariant DisassemblerModel::data(const QModelIndex& index, int role) const {
break; break;
} }
case Qt::BackgroundRole: case Qt::BackgroundRole: {
{
unsigned int address = base_address + 4 * index.row(); unsigned int address = base_address + 4 * index.row();
if (breakpoints.IsAddressBreakPoint(address)) if (breakpoints.IsAddressBreakPoint(address))
@ -77,8 +73,7 @@ QVariant DisassemblerModel::data(const QModelIndex& index, int role) const {
break; break;
} }
case Qt::FontRole: case Qt::FontRole: {
{
if (index.column() == 0 || index.column() == 1) { // 2 is the symbols column if (index.column() == 0 || index.column() == 1) { // 2 is the symbols column
return GetMonospaceFont(); return GetMonospaceFont();
} }
@ -103,7 +98,7 @@ const BreakPoints& DisassemblerModel::GetBreakPoints() const {
void DisassemblerModel::ParseFromAddress(unsigned int address) { void DisassemblerModel::ParseFromAddress(unsigned int address) {
// NOTE: A too large value causes lagging when scrolling the disassembly // NOTE: A too large value causes lagging when scrolling the disassembly
const unsigned int chunk_size = 1000*500; const unsigned int chunk_size = 1000 * 500;
// If we haven't loaded anything yet, initialize base address to the parameter address // If we haven't loaded anything yet, initialize base address to the parameter address
if (code_size == 0) if (code_size == 0)
@ -165,23 +160,26 @@ void DisassemblerModel::SetNextInstruction(unsigned int address) {
emit dataChanged(prev_index, prev_index); emit dataChanged(prev_index, prev_index);
} }
DisassemblerWidget::DisassemblerWidget(QWidget* parent, EmuThread* emu_thread) : DisassemblerWidget::DisassemblerWidget(QWidget* parent, EmuThread* emu_thread)
QDockWidget(parent), base_addr(0), emu_thread(emu_thread) { : QDockWidget(parent), base_addr(0), emu_thread(emu_thread) {
disasm_ui.setupUi(this); disasm_ui.setupUi(this);
RegisterHotkey("Disassembler", "Start/Stop", QKeySequence(Qt::Key_F5), Qt::ApplicationShortcut); RegisterHotkey("Disassembler", "Start/Stop", QKeySequence(Qt::Key_F5), Qt::ApplicationShortcut);
RegisterHotkey("Disassembler", "Step", QKeySequence(Qt::Key_F10), Qt::ApplicationShortcut); RegisterHotkey("Disassembler", "Step", QKeySequence(Qt::Key_F10), Qt::ApplicationShortcut);
RegisterHotkey("Disassembler", "Step into", QKeySequence(Qt::Key_F11), Qt::ApplicationShortcut); RegisterHotkey("Disassembler", "Step into", QKeySequence(Qt::Key_F11), Qt::ApplicationShortcut);
RegisterHotkey("Disassembler", "Set Breakpoint", QKeySequence(Qt::Key_F9), Qt::ApplicationShortcut); RegisterHotkey("Disassembler", "Set Breakpoint", QKeySequence(Qt::Key_F9),
Qt::ApplicationShortcut);
connect(disasm_ui.button_step, SIGNAL(clicked()), this, SLOT(OnStep())); connect(disasm_ui.button_step, SIGNAL(clicked()), this, SLOT(OnStep()));
connect(disasm_ui.button_pause, SIGNAL(clicked()), this, SLOT(OnPause())); connect(disasm_ui.button_pause, SIGNAL(clicked()), this, SLOT(OnPause()));
connect(disasm_ui.button_continue, SIGNAL(clicked()), this, SLOT(OnContinue())); connect(disasm_ui.button_continue, SIGNAL(clicked()), this, SLOT(OnContinue()));
connect(GetHotkey("Disassembler", "Start/Stop", this), SIGNAL(activated()), this, SLOT(OnToggleStartStop())); connect(GetHotkey("Disassembler", "Start/Stop", this), SIGNAL(activated()), this,
SLOT(OnToggleStartStop()));
connect(GetHotkey("Disassembler", "Step", this), SIGNAL(activated()), this, SLOT(OnStep())); connect(GetHotkey("Disassembler", "Step", this), SIGNAL(activated()), this, SLOT(OnStep()));
connect(GetHotkey("Disassembler", "Step into", this), SIGNAL(activated()), this, SLOT(OnStepInto())); connect(GetHotkey("Disassembler", "Step into", this), SIGNAL(activated()), this,
SLOT(OnStepInto()));
setEnabled(false); setEnabled(false);
} }
@ -195,7 +193,8 @@ void DisassemblerWidget::Init() {
QModelIndex model_index = model->IndexFromAbsoluteAddress(Core::g_app_core->GetPC()); QModelIndex model_index = model->IndexFromAbsoluteAddress(Core::g_app_core->GetPC());
disasm_ui.treeView->scrollTo(model_index); disasm_ui.treeView->scrollTo(model_index);
disasm_ui.treeView->selectionModel()->setCurrentIndex(model_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); disasm_ui.treeView->selectionModel()->setCurrentIndex(
model_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
} }
void DisassemblerWidget::OnContinue() { void DisassemblerWidget::OnContinue() {
@ -234,11 +233,11 @@ void DisassemblerWidget::OnDebugModeEntered() {
QModelIndex model_index = model->IndexFromAbsoluteAddress(next_instr); QModelIndex model_index = model->IndexFromAbsoluteAddress(next_instr);
disasm_ui.treeView->scrollTo(model_index); disasm_ui.treeView->scrollTo(model_index);
disasm_ui.treeView->selectionModel()->setCurrentIndex(model_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); disasm_ui.treeView->selectionModel()->setCurrentIndex(
model_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
} }
void DisassemblerWidget::OnDebugModeLeft() { void DisassemblerWidget::OnDebugModeLeft() {}
}
int DisassemblerWidget::SelectedRow() { int DisassemblerWidget::SelectedRow() {
QModelIndex index = disasm_ui.treeView->selectionModel()->currentIndex(); QModelIndex index = disasm_ui.treeView->selectionModel()->currentIndex();
@ -254,10 +253,12 @@ void DisassemblerWidget::OnEmulationStarting(EmuThread* emu_thread) {
model = new DisassemblerModel(this); model = new DisassemblerModel(this);
disasm_ui.treeView->setModel(model); disasm_ui.treeView->setModel(model);
connect(disasm_ui.treeView->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), connect(disasm_ui.treeView->selectionModel(),
model, SLOT(OnSelectionChanged(const QModelIndex&))); SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), model,
SLOT(OnSelectionChanged(const QModelIndex&)));
connect(disasm_ui.button_breakpoint, SIGNAL(clicked()), model, SLOT(OnSetOrUnsetBreakpoint())); connect(disasm_ui.button_breakpoint, SIGNAL(clicked()), model, SLOT(OnSetOrUnsetBreakpoint()));
connect(GetHotkey("Disassembler", "Set Breakpoint", this), SIGNAL(activated()), model, SLOT(OnSetOrUnsetBreakpoint())); connect(GetHotkey("Disassembler", "Set Breakpoint", this), SIGNAL(activated()), model,
SLOT(OnSetOrUnsetBreakpoint()));
Init(); Init();
setEnabled(true); setEnabled(true);

View File

@ -6,17 +6,14 @@
#include <QAbstractListModel> #include <QAbstractListModel>
#include <QDockWidget> #include <QDockWidget>
#include "ui_disassembler.h"
#include "common/break_points.h" #include "common/break_points.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "ui_disassembler.h"
class QAction; class QAction;
class EmuThread; class EmuThread;
class DisassemblerModel : public QAbstractListModel class DisassemblerModel : public QAbstractListModel {
{
Q_OBJECT Q_OBJECT
public: public:
@ -46,8 +43,7 @@ private:
mutable BreakPoints breakpoints; mutable BreakPoints breakpoints;
}; };
class DisassemblerWidget : public QDockWidget class DisassemblerWidget : public QDockWidget {
{
Q_OBJECT Q_OBJECT
public: public:

View File

@ -3,41 +3,38 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <QListView> #include <QListView>
#include "citra_qt/debugger/graphics.h" #include "citra_qt/debugger/graphics.h"
#include "citra_qt/util/util.h" #include "citra_qt/util/util.h"
extern GraphicsDebugger g_debugger; extern GraphicsDebugger g_debugger;
GPUCommandStreamItemModel::GPUCommandStreamItemModel(QObject* parent) : QAbstractListModel(parent), command_count(0) GPUCommandStreamItemModel::GPUCommandStreamItemModel(QObject* parent)
{ : QAbstractListModel(parent), command_count(0) {
connect(this, SIGNAL(GXCommandFinished(int)), this, SLOT(OnGXCommandFinishedInternal(int))); connect(this, SIGNAL(GXCommandFinished(int)), this, SLOT(OnGXCommandFinishedInternal(int)));
} }
int GPUCommandStreamItemModel::rowCount(const QModelIndex& parent) const int GPUCommandStreamItemModel::rowCount(const QModelIndex& parent) const {
{
return command_count; return command_count;
} }
QVariant GPUCommandStreamItemModel::data(const QModelIndex& index, int role) const QVariant GPUCommandStreamItemModel::data(const QModelIndex& index, int role) const {
{
if (!index.isValid()) if (!index.isValid())
return QVariant(); return QVariant();
int command_index = index.row(); int command_index = index.row();
const GSP_GPU::Command& command = GetDebugger()->ReadGXCommandHistory(command_index); const GSP_GPU::Command& command = GetDebugger()->ReadGXCommandHistory(command_index);
if (role == Qt::DisplayRole) if (role == Qt::DisplayRole) {
{
std::map<GSP_GPU::CommandId, const char*> command_names = { std::map<GSP_GPU::CommandId, const char*> command_names = {
{ GSP_GPU::CommandId::REQUEST_DMA, "REQUEST_DMA" }, {GSP_GPU::CommandId::REQUEST_DMA, "REQUEST_DMA"},
{ GSP_GPU::CommandId::SUBMIT_GPU_CMDLIST, "SUBMIT_GPU_CMDLIST" }, {GSP_GPU::CommandId::SUBMIT_GPU_CMDLIST, "SUBMIT_GPU_CMDLIST"},
{ GSP_GPU::CommandId::SET_MEMORY_FILL, "SET_MEMORY_FILL" }, {GSP_GPU::CommandId::SET_MEMORY_FILL, "SET_MEMORY_FILL"},
{ GSP_GPU::CommandId::SET_DISPLAY_TRANSFER, "SET_DISPLAY_TRANSFER" }, {GSP_GPU::CommandId::SET_DISPLAY_TRANSFER, "SET_DISPLAY_TRANSFER"},
{ GSP_GPU::CommandId::SET_TEXTURE_COPY, "SET_TEXTURE_COPY" }, {GSP_GPU::CommandId::SET_TEXTURE_COPY, "SET_TEXTURE_COPY"},
{ GSP_GPU::CommandId::CACHE_FLUSH, "CACHE_FLUSH" }, {GSP_GPU::CommandId::CACHE_FLUSH, "CACHE_FLUSH"},
}; };
const u32* command_data = reinterpret_cast<const u32*>(&command); const u32* command_data = reinterpret_cast<const u32*>(&command);
QString str = QString("%1 %2 %3 %4 %5 %6 %7 %8 %9").arg(command_names[command.id]) QString str = QString("%1 %2 %3 %4 %5 %6 %7 %8 %9")
.arg(command_names[command.id])
.arg(command_data[0], 8, 16, QLatin1Char('0')) .arg(command_data[0], 8, 16, QLatin1Char('0'))
.arg(command_data[1], 8, 16, QLatin1Char('0')) .arg(command_data[1], 8, 16, QLatin1Char('0'))
.arg(command_data[2], 8, 16, QLatin1Char('0')) .arg(command_data[2], 8, 16, QLatin1Char('0'))
@ -47,31 +44,26 @@ QVariant GPUCommandStreamItemModel::data(const QModelIndex& index, int role) con
.arg(command_data[6], 8, 16, QLatin1Char('0')) .arg(command_data[6], 8, 16, QLatin1Char('0'))
.arg(command_data[7], 8, 16, QLatin1Char('0')); .arg(command_data[7], 8, 16, QLatin1Char('0'));
return QVariant(str); return QVariant(str);
} } else {
else
{
return QVariant(); return QVariant();
} }
} }
void GPUCommandStreamItemModel::GXCommandProcessed(int total_command_count) void GPUCommandStreamItemModel::GXCommandProcessed(int total_command_count) {
{
emit GXCommandFinished(total_command_count); emit GXCommandFinished(total_command_count);
} }
void GPUCommandStreamItemModel::OnGXCommandFinishedInternal(int total_command_count) void GPUCommandStreamItemModel::OnGXCommandFinishedInternal(int total_command_count) {
{
if (total_command_count == 0) if (total_command_count == 0)
return; return;
int prev_command_count = command_count; int prev_command_count = command_count;
command_count = total_command_count; command_count = total_command_count;
emit dataChanged(index(prev_command_count,0), index(total_command_count-1,0)); emit dataChanged(index(prev_command_count, 0), index(total_command_count - 1, 0));
} }
GPUCommandStreamWidget::GPUCommandStreamWidget(QWidget* parent)
GPUCommandStreamWidget::GPUCommandStreamWidget(QWidget* parent) : QDockWidget(tr("Graphics Debugger"), parent) : QDockWidget(tr("Graphics Debugger"), parent) {
{
setObjectName("GraphicsDebugger"); setObjectName("GraphicsDebugger");
GPUCommandStreamItemModel* command_model = new GPUCommandStreamItemModel(this); GPUCommandStreamItemModel* command_model = new GPUCommandStreamItemModel(this);

View File

@ -6,11 +6,10 @@
#include <QAbstractListModel> #include <QAbstractListModel>
#include <QDockWidget> #include <QDockWidget>
#include "video_core/gpu_debugger.h" #include "video_core/gpu_debugger.h"
class GPUCommandStreamItemModel : public QAbstractListModel, public GraphicsDebugger::DebuggerObserver class GPUCommandStreamItemModel : public QAbstractListModel,
{ public GraphicsDebugger::DebuggerObserver {
Q_OBJECT Q_OBJECT
public: public:
@ -32,8 +31,7 @@ private:
int command_count; int command_count;
}; };
class GPUCommandStreamWidget : public QDockWidget class GPUCommandStreamWidget : public QDockWidget {
{
Q_OBJECT Q_OBJECT
public: public:

View File

@ -3,30 +3,25 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <QMetaType> #include <QMetaType>
#include "citra_qt/debugger/graphics_breakpoint_observer.h" #include "citra_qt/debugger/graphics_breakpoint_observer.h"
BreakPointObserverDock::BreakPointObserverDock(std::shared_ptr<Pica::DebugContext> debug_context, BreakPointObserverDock::BreakPointObserverDock(std::shared_ptr<Pica::DebugContext> debug_context,
const QString& title, QWidget* parent) const QString& title, QWidget* parent)
: QDockWidget(title, parent), BreakPointObserver(debug_context) : QDockWidget(title, parent), BreakPointObserver(debug_context) {
{
qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event"); qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event");
connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed())); connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed()));
// NOTE: This signal is emitted from a non-GUI thread, but connect() takes // NOTE: This signal is emitted from a non-GUI thread, but connect() takes
// care of delaying its handling to the GUI thread. // care of delaying its handling to the GUI thread.
connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)), connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event, void*)), this,
this, SLOT(OnBreakPointHit(Pica::DebugContext::Event,void*)), SLOT(OnBreakPointHit(Pica::DebugContext::Event, void*)), Qt::BlockingQueuedConnection);
Qt::BlockingQueuedConnection);
} }
void BreakPointObserverDock::OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) void BreakPointObserverDock::OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) {
{
emit BreakPointHit(event, data); emit BreakPointHit(event, data);
} }
void BreakPointObserverDock::OnPicaResume() void BreakPointObserverDock::OnPicaResume() {
{
emit Resumed(); emit Resumed();
} }

View File

@ -5,7 +5,6 @@
#pragma once #pragma once
#include <QDockWidget> #include <QDockWidget>
#include "video_core/debug_utils/debug_utils.h" #include "video_core/debug_utils/debug_utils.h"
/** /**
@ -13,7 +12,8 @@
* This is because the Pica breakpoint callbacks are called from a non-GUI thread, while * This is because the Pica breakpoint callbacks are called from a non-GUI thread, while
* the widget usually wants to perform reactions in the GUI thread. * the widget usually wants to perform reactions in the GUI thread.
*/ */
class BreakPointObserverDock : public QDockWidget, protected Pica::DebugContext::BreakPointObserver { class BreakPointObserverDock : public QDockWidget,
protected Pica::DebugContext::BreakPointObserver {
Q_OBJECT Q_OBJECT
public: public:

View File

@ -7,47 +7,39 @@
#include <QPushButton> #include <QPushButton>
#include <QTreeView> #include <QTreeView>
#include <QVBoxLayout> #include <QVBoxLayout>
#include "citra_qt/debugger/graphics_breakpoints.h" #include "citra_qt/debugger/graphics_breakpoints.h"
#include "citra_qt/debugger/graphics_breakpoints_p.h" #include "citra_qt/debugger/graphics_breakpoints_p.h"
#include "common/assert.h" #include "common/assert.h"
BreakPointModel::BreakPointModel(std::shared_ptr<Pica::DebugContext> debug_context, QObject* parent) BreakPointModel::BreakPointModel(std::shared_ptr<Pica::DebugContext> debug_context, QObject* parent)
: QAbstractListModel(parent), context_weak(debug_context), : QAbstractListModel(parent), context_weak(debug_context),
at_breakpoint(debug_context->at_breakpoint), at_breakpoint(debug_context->at_breakpoint),
active_breakpoint(debug_context->active_breakpoint) active_breakpoint(debug_context->active_breakpoint) {}
{
} int BreakPointModel::columnCount(const QModelIndex& parent) const {
int BreakPointModel::columnCount(const QModelIndex& parent) const
{
return 1; return 1;
} }
int BreakPointModel::rowCount(const QModelIndex& parent) const int BreakPointModel::rowCount(const QModelIndex& parent) const {
{
return static_cast<int>(Pica::DebugContext::Event::NumEvents); return static_cast<int>(Pica::DebugContext::Event::NumEvents);
} }
QVariant BreakPointModel::data(const QModelIndex& index, int role) const QVariant BreakPointModel::data(const QModelIndex& index, int role) const {
{
const auto event = static_cast<Pica::DebugContext::Event>(index.row()); const auto event = static_cast<Pica::DebugContext::Event>(index.row());
switch (role) { switch (role) {
case Qt::DisplayRole: case Qt::DisplayRole: {
{
if (index.column() == 0) { if (index.column() == 0) {
static const std::map<Pica::DebugContext::Event, QString> map = { static const std::map<Pica::DebugContext::Event, QString> map = {
{ Pica::DebugContext::Event::PicaCommandLoaded, tr("Pica command loaded") }, {Pica::DebugContext::Event::PicaCommandLoaded, tr("Pica command loaded")},
{ Pica::DebugContext::Event::PicaCommandProcessed, tr("Pica command processed") }, {Pica::DebugContext::Event::PicaCommandProcessed, tr("Pica command processed")},
{ Pica::DebugContext::Event::IncomingPrimitiveBatch, tr("Incoming primitive batch") }, {Pica::DebugContext::Event::IncomingPrimitiveBatch, tr("Incoming primitive batch")},
{ Pica::DebugContext::Event::FinishedPrimitiveBatch, tr("Finished primitive batch") }, {Pica::DebugContext::Event::FinishedPrimitiveBatch, tr("Finished primitive batch")},
{ Pica::DebugContext::Event::VertexShaderInvocation, tr("Vertex shader invocation") }, {Pica::DebugContext::Event::VertexShaderInvocation, tr("Vertex shader invocation")},
{ Pica::DebugContext::Event::IncomingDisplayTransfer, tr("Incoming display transfer") }, {Pica::DebugContext::Event::IncomingDisplayTransfer,
{ Pica::DebugContext::Event::GSPCommandProcessed, tr("GSP command processed") }, tr("Incoming display transfer")},
{ Pica::DebugContext::Event::BufferSwapped, tr("Buffers swapped") } {Pica::DebugContext::Event::GSPCommandProcessed, tr("GSP command processed")},
{Pica::DebugContext::Event::BufferSwapped, tr("Buffers swapped")},
}; };
DEBUG_ASSERT(map.size() == static_cast<size_t>(Pica::DebugContext::Event::NumEvents)); DEBUG_ASSERT(map.size() == static_cast<size_t>(Pica::DebugContext::Event::NumEvents));
@ -57,23 +49,20 @@ QVariant BreakPointModel::data(const QModelIndex& index, int role) const
break; break;
} }
case Qt::CheckStateRole: case Qt::CheckStateRole: {
{
if (index.column() == 0) if (index.column() == 0)
return data(index, Role_IsEnabled).toBool() ? Qt::Checked : Qt::Unchecked; return data(index, Role_IsEnabled).toBool() ? Qt::Checked : Qt::Unchecked;
break; break;
} }
case Qt::BackgroundRole: case Qt::BackgroundRole: {
{
if (at_breakpoint && index.row() == static_cast<int>(active_breakpoint)) { if (at_breakpoint && index.row() == static_cast<int>(active_breakpoint)) {
return QBrush(QColor(0xE0, 0xE0, 0x10)); return QBrush(QColor(0xE0, 0xE0, 0x10));
} }
break; break;
} }
case Role_IsEnabled: case Role_IsEnabled: {
{
auto context = context_weak.lock(); auto context = context_weak.lock();
return context && context->breakpoints[(int)event].enabled; return context && context->breakpoints[(int)event].enabled;
} }
@ -84,8 +73,7 @@ QVariant BreakPointModel::data(const QModelIndex& index, int role) const
return QVariant(); return QVariant();
} }
Qt::ItemFlags BreakPointModel::flags(const QModelIndex &index) const Qt::ItemFlags BreakPointModel::flags(const QModelIndex& index) const {
{
if (!index.isValid()) if (!index.isValid())
return 0; return 0;
@ -95,14 +83,11 @@ Qt::ItemFlags BreakPointModel::flags(const QModelIndex &index) const
return flags; return flags;
} }
bool BreakPointModel::setData(const QModelIndex& index, const QVariant& value, int role) {
bool BreakPointModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
const auto event = static_cast<Pica::DebugContext::Event>(index.row()); const auto event = static_cast<Pica::DebugContext::Event>(index.row());
switch (role) { switch (role) {
case Qt::CheckStateRole: case Qt::CheckStateRole: {
{
if (index.column() != 0) if (index.column() != 0)
return false; return false;
@ -120,9 +105,7 @@ bool BreakPointModel::setData(const QModelIndex& index, const QVariant& value, i
return false; return false;
} }
void BreakPointModel::OnBreakPointHit(Pica::DebugContext::Event event) {
void BreakPointModel::OnBreakPointHit(Pica::DebugContext::Event event)
{
auto context = context_weak.lock(); auto context = context_weak.lock();
if (!context) if (!context)
return; return;
@ -133,8 +116,7 @@ void BreakPointModel::OnBreakPointHit(Pica::DebugContext::Event event)
createIndex(static_cast<int>(event), 0)); createIndex(static_cast<int>(event), 0));
} }
void BreakPointModel::OnResumed() void BreakPointModel::OnResumed() {
{
auto context = context_weak.lock(); auto context = context_weak.lock();
if (!context) if (!context)
return; return;
@ -145,12 +127,10 @@ void BreakPointModel::OnResumed()
active_breakpoint = context->active_breakpoint; active_breakpoint = context->active_breakpoint;
} }
GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(
GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(std::shared_ptr<Pica::DebugContext> debug_context, std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent)
QWidget* parent)
: QDockWidget(tr("Pica Breakpoints"), parent), : QDockWidget(tr("Pica Breakpoints"), parent),
Pica::DebugContext::BreakPointObserver(debug_context) Pica::DebugContext::BreakPointObserver(debug_context) {
{
setObjectName("PicaBreakPointsWidget"); setObjectName("PicaBreakPointsWidget");
status_text = new QLabel(tr("Emulation running")); status_text = new QLabel(tr("Emulation running"));
@ -165,23 +145,21 @@ GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(std::shared_ptr<Pica::Debug
qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event"); qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event");
connect(breakpoint_list, SIGNAL(doubleClicked(const QModelIndex&)), connect(breakpoint_list, SIGNAL(doubleClicked(const QModelIndex&)), this,
this, SLOT(OnItemDoubleClicked(const QModelIndex&))); SLOT(OnItemDoubleClicked(const QModelIndex&)));
connect(resume_button, SIGNAL(clicked()), this, SLOT(OnResumeRequested())); connect(resume_button, SIGNAL(clicked()), this, SLOT(OnResumeRequested()));
connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)), connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event, void*)), this,
this, SLOT(OnBreakPointHit(Pica::DebugContext::Event,void*)), SLOT(OnBreakPointHit(Pica::DebugContext::Event, void*)), Qt::BlockingQueuedConnection);
Qt::BlockingQueuedConnection);
connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed())); connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed()));
connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)), connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event, void*)), breakpoint_model,
breakpoint_model, SLOT(OnBreakPointHit(Pica::DebugContext::Event)), SLOT(OnBreakPointHit(Pica::DebugContext::Event)), Qt::BlockingQueuedConnection);
Qt::BlockingQueuedConnection);
connect(this, SIGNAL(Resumed()), breakpoint_model, SLOT(OnResumed())); connect(this, SIGNAL(Resumed()), breakpoint_model, SLOT(OnResumed()));
connect(this, SIGNAL(BreakPointsChanged(const QModelIndex&,const QModelIndex&)), connect(this, SIGNAL(BreakPointsChanged(const QModelIndex&, const QModelIndex&)),
breakpoint_model, SIGNAL(dataChanged(const QModelIndex&,const QModelIndex&))); breakpoint_model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)));
QWidget* main_widget = new QWidget; QWidget* main_widget = new QWidget;
auto main_layout = new QVBoxLayout; auto main_layout = new QVBoxLayout;
@ -197,38 +175,32 @@ GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(std::shared_ptr<Pica::Debug
setWidget(main_widget); setWidget(main_widget);
} }
void GraphicsBreakPointsWidget::OnPicaBreakPointHit(Event event, void* data) void GraphicsBreakPointsWidget::OnPicaBreakPointHit(Event event, void* data) {
{
// Process in GUI thread // Process in GUI thread
emit BreakPointHit(event, data); emit BreakPointHit(event, data);
} }
void GraphicsBreakPointsWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) void GraphicsBreakPointsWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) {
{
status_text->setText(tr("Emulation halted at breakpoint")); status_text->setText(tr("Emulation halted at breakpoint"));
resume_button->setEnabled(true); resume_button->setEnabled(true);
} }
void GraphicsBreakPointsWidget::OnPicaResume() void GraphicsBreakPointsWidget::OnPicaResume() {
{
// Process in GUI thread // Process in GUI thread
emit Resumed(); emit Resumed();
} }
void GraphicsBreakPointsWidget::OnResumed() void GraphicsBreakPointsWidget::OnResumed() {
{
status_text->setText(tr("Emulation running")); status_text->setText(tr("Emulation running"));
resume_button->setEnabled(false); resume_button->setEnabled(false);
} }
void GraphicsBreakPointsWidget::OnResumeRequested() void GraphicsBreakPointsWidget::OnResumeRequested() {
{
if (auto context = context_weak.lock()) if (auto context = context_weak.lock())
context->Resume(); context->Resume();
} }
void GraphicsBreakPointsWidget::OnItemDoubleClicked(const QModelIndex& index) void GraphicsBreakPointsWidget::OnItemDoubleClicked(const QModelIndex& index) {
{
if (!index.isValid()) if (!index.isValid())
return; return;

View File

@ -5,9 +5,7 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include <QDockWidget> #include <QDockWidget>
#include "video_core/debug_utils/debug_utils.h" #include "video_core/debug_utils/debug_utils.h"
class QLabel; class QLabel;

View File

@ -5,9 +5,7 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include <QAbstractListModel> #include <QAbstractListModel>
#include "video_core/debug_utils/debug_utils.h" #include "video_core/debug_utils/debug_utils.h"
class BreakPointModel : public QAbstractListModel { class BreakPointModel : public QAbstractListModel {
@ -23,7 +21,7 @@ public:
int columnCount(const QModelIndex& parent = QModelIndex()) const override; int columnCount(const QModelIndex& parent = QModelIndex()) const override;
int rowCount(const QModelIndex& parent = QModelIndex()) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override; Qt::ItemFlags flags(const QModelIndex& index) const override;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;

View File

@ -13,16 +13,13 @@
#include <QSpinBox> #include <QSpinBox>
#include <QTreeView> #include <QTreeView>
#include <QVBoxLayout> #include <QVBoxLayout>
#include "citra_qt/debugger/graphics_cmdlists.h" #include "citra_qt/debugger/graphics_cmdlists.h"
#include "citra_qt/util/spinbox.h" #include "citra_qt/util/spinbox.h"
#include "citra_qt/util/util.h" #include "citra_qt/util/util.h"
#include "common/vector_math.h" #include "common/vector_math.h"
#include "video_core/debug_utils/debug_utils.h"
#include "video_core/pica.h" #include "video_core/pica.h"
#include "video_core/pica_state.h" #include "video_core/pica_state.h"
#include "video_core/debug_utils/debug_utils.h"
QImage LoadTexture(u8* src, const Pica::DebugUtils::TextureInfo& info) { QImage LoadTexture(u8* src, const Pica::DebugUtils::TextureInfo& info) {
QImage decoded_image(info.width, info.height, QImage::Format_ARGB32); QImage decoded_image(info.width, info.height, QImage::Format_ARGB32);
@ -38,7 +35,8 @@ QImage LoadTexture(u8* src, const Pica::DebugUtils::TextureInfo& info) {
class TextureInfoWidget : public QWidget { class TextureInfoWidget : public QWidget {
public: public:
TextureInfoWidget(u8* src, const Pica::DebugUtils::TextureInfo& info, QWidget* parent = nullptr) : QWidget(parent) { TextureInfoWidget(u8* src, const Pica::DebugUtils::TextureInfo& info, QWidget* parent = nullptr)
: QWidget(parent) {
QLabel* image_widget = new QLabel; QLabel* image_widget = new QLabel;
QPixmap image_pixmap = QPixmap::fromImage(LoadTexture(src, info)); QPixmap image_pixmap = QPixmap::fromImage(LoadTexture(src, info));
image_pixmap = image_pixmap.scaled(200, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation); image_pixmap = image_pixmap.scaled(200, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation);
@ -50,9 +48,7 @@ public:
} }
}; };
GPUCommandListModel::GPUCommandListModel(QObject* parent) : QAbstractListModel(parent) { GPUCommandListModel::GPUCommandListModel(QObject* parent) : QAbstractListModel(parent) {}
}
int GPUCommandListModel::rowCount(const QModelIndex& parent) const { int GPUCommandListModel::rowCount(const QModelIndex& parent) const {
return static_cast<int>(pica_trace.writes.size()); return static_cast<int>(pica_trace.writes.size());
@ -70,7 +66,7 @@ QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const {
if (role == Qt::DisplayRole) { if (role == Qt::DisplayRole) {
QString content; QString content;
switch ( index.column() ) { switch (index.column()) {
case 0: case 0:
return QString::fromLatin1(Pica::Regs::GetCommandName(write.cmd_id).c_str()); return QString::fromLatin1(Pica::Regs::GetCommandName(write.cmd_id).c_str());
case 1: case 1:
@ -88,9 +84,8 @@ QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const {
} }
QVariant GPUCommandListModel::headerData(int section, Qt::Orientation orientation, int role) const { QVariant GPUCommandListModel::headerData(int section, Qt::Orientation orientation, int role) const {
switch(role) { switch (role) {
case Qt::DisplayRole: case Qt::DisplayRole: {
{
switch (section) { switch (section) {
case 0: case 0:
return tr("Command Name"); return tr("Command Name");
@ -122,9 +117,9 @@ void GPUCommandListModel::OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace&
cmd_id < PICA_REG_INDEX(reg_name) + sizeof(decltype(Pica::g_state.regs.reg_name)) / 4) cmd_id < PICA_REG_INDEX(reg_name) + sizeof(decltype(Pica::g_state.regs.reg_name)) / 4)
void GPUCommandListWidget::OnCommandDoubleClicked(const QModelIndex& index) { void GPUCommandListWidget::OnCommandDoubleClicked(const QModelIndex& index) {
const unsigned int command_id = list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toUInt(); const unsigned int command_id =
if (COMMAND_IN_RANGE(command_id, texture0) || list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toUInt();
COMMAND_IN_RANGE(command_id, texture1) || if (COMMAND_IN_RANGE(command_id, texture0) || COMMAND_IN_RANGE(command_id, texture1) ||
COMMAND_IN_RANGE(command_id, texture2)) { COMMAND_IN_RANGE(command_id, texture2)) {
unsigned index; unsigned index;
@ -148,9 +143,9 @@ void GPUCommandListWidget::OnCommandDoubleClicked(const QModelIndex& index) {
void GPUCommandListWidget::SetCommandInfo(const QModelIndex& index) { void GPUCommandListWidget::SetCommandInfo(const QModelIndex& index) {
QWidget* new_info_widget = nullptr; QWidget* new_info_widget = nullptr;
const unsigned int command_id = list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toUInt(); const unsigned int command_id =
if (COMMAND_IN_RANGE(command_id, texture0) || list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toUInt();
COMMAND_IN_RANGE(command_id, texture1) || if (COMMAND_IN_RANGE(command_id, texture0) || COMMAND_IN_RANGE(command_id, texture1) ||
COMMAND_IN_RANGE(command_id, texture2)) { COMMAND_IN_RANGE(command_id, texture2)) {
unsigned index; unsigned index;
@ -179,7 +174,8 @@ void GPUCommandListWidget::SetCommandInfo(const QModelIndex& index) {
} }
#undef COMMAND_IN_RANGE #undef COMMAND_IN_RANGE
GPUCommandListWidget::GPUCommandListWidget(QWidget* parent) : QDockWidget(tr("Pica Command List"), parent) { GPUCommandListWidget::GPUCommandListWidget(QWidget* parent)
: QDockWidget(tr("Pica Command List"), parent) {
setObjectName("Pica Command List"); setObjectName("Pica Command List");
GPUCommandListModel* model = new GPUCommandListModel(this); GPUCommandListModel* model = new GPUCommandListModel(this);
@ -191,23 +187,24 @@ GPUCommandListWidget::GPUCommandListWidget(QWidget* parent) : QDockWidget(tr("Pi
list_widget->setRootIsDecorated(false); list_widget->setRootIsDecorated(false);
list_widget->setUniformRowHeights(true); list_widget->setUniformRowHeights(true);
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
list_widget->header()->setSectionResizeMode(QHeaderView::ResizeToContents); list_widget->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
#else #else
list_widget->header()->setResizeMode(QHeaderView::ResizeToContents); list_widget->header()->setResizeMode(QHeaderView::ResizeToContents);
#endif #endif
connect(list_widget->selectionModel(), SIGNAL(currentChanged(const QModelIndex&,const QModelIndex&)), connect(list_widget->selectionModel(),
this, SLOT(SetCommandInfo(const QModelIndex&))); SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), this,
connect(list_widget, SIGNAL(doubleClicked(const QModelIndex&)), SLOT(SetCommandInfo(const QModelIndex&)));
this, SLOT(OnCommandDoubleClicked(const QModelIndex&))); connect(list_widget, SIGNAL(doubleClicked(const QModelIndex&)), this,
SLOT(OnCommandDoubleClicked(const QModelIndex&)));
toggle_tracing = new QPushButton(tr("Start Tracing")); toggle_tracing = new QPushButton(tr("Start Tracing"));
QPushButton* copy_all = new QPushButton(tr("Copy All")); QPushButton* copy_all = new QPushButton(tr("Copy All"));
connect(toggle_tracing, SIGNAL(clicked()), this, SLOT(OnToggleTracing())); connect(toggle_tracing, SIGNAL(clicked()), this, SLOT(OnToggleTracing()));
connect(this, SIGNAL(TracingFinished(const Pica::DebugUtils::PicaTrace&)), connect(this, SIGNAL(TracingFinished(const Pica::DebugUtils::PicaTrace&)), model,
model, SLOT(OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace&))); SLOT(OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace&)));
connect(copy_all, SIGNAL(clicked()), this, SLOT(CopyAllToClipboard())); connect(copy_all, SIGNAL(clicked()), this, SLOT(CopyAllToClipboard()));

View File

@ -6,15 +6,13 @@
#include <QAbstractListModel> #include <QAbstractListModel>
#include <QDockWidget> #include <QDockWidget>
#include "video_core/gpu_debugger.h"
#include "video_core/debug_utils/debug_utils.h" #include "video_core/debug_utils/debug_utils.h"
#include "video_core/gpu_debugger.h"
class QPushButton; class QPushButton;
class QTreeView; class QTreeView;
class GPUCommandListModel : public QAbstractListModel class GPUCommandListModel : public QAbstractListModel {
{
Q_OBJECT Q_OBJECT
public: public:
@ -27,7 +25,8 @@ public:
int columnCount(const QModelIndex& parent = QModelIndex()) const override; int columnCount(const QModelIndex& parent = QModelIndex()) const override;
int rowCount(const QModelIndex& parent = QModelIndex()) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
public slots: public slots:
void OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& trace); void OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& trace);
@ -36,8 +35,7 @@ private:
Pica::DebugUtils::PicaTrace pica_trace; Pica::DebugUtils::PicaTrace pica_trace;
}; };
class GPUCommandListWidget : public QDockWidget class GPUCommandListWidget : public QDockWidget {
{
Q_OBJECT Q_OBJECT
public: public:

View File

@ -11,24 +11,20 @@
#include <QPushButton> #include <QPushButton>
#include <QScrollArea> #include <QScrollArea>
#include <QSpinBox> #include <QSpinBox>
#include "citra_qt/debugger/graphics_surface.h" #include "citra_qt/debugger/graphics_surface.h"
#include "citra_qt/util/spinbox.h" #include "citra_qt/util/spinbox.h"
#include "common/color.h" #include "common/color.h"
#include "core/memory.h"
#include "core/hw/gpu.h" #include "core/hw/gpu.h"
#include "core/memory.h"
#include "video_core/pica.h" #include "video_core/pica.h"
#include "video_core/pica_state.h" #include "video_core/pica_state.h"
#include "video_core/utils.h" #include "video_core/utils.h"
SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_) : QLabel(parent), surface_widget(surface_widget_) {} SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_)
: QLabel(parent), surface_widget(surface_widget_) {}
SurfacePicture::~SurfacePicture() {} SurfacePicture::~SurfacePicture() {}
void SurfacePicture::mousePressEvent(QMouseEvent* event) void SurfacePicture::mousePressEvent(QMouseEvent* event) {
{
// Only do something while the left mouse button is held down // Only do something while the left mouse button is held down
if (!(event->buttons() & Qt::LeftButton)) if (!(event->buttons() & Qt::LeftButton))
return; return;
@ -41,18 +37,15 @@ void SurfacePicture::mousePressEvent(QMouseEvent* event)
event->y() * pixmap()->height() / height()); event->y() * pixmap()->height() / height());
} }
void SurfacePicture::mouseMoveEvent(QMouseEvent* event) void SurfacePicture::mouseMoveEvent(QMouseEvent* event) {
{
// We also want to handle the event if the user moves the mouse while holding down the LMB // We also want to handle the event if the user moves the mouse while holding down the LMB
mousePressEvent(event); mousePressEvent(event);
} }
GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext> debug_context, GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext> debug_context,
QWidget* parent) QWidget* parent)
: BreakPointObserverDock(debug_context, tr("Pica Surface Viewer"), parent), : BreakPointObserverDock(debug_context, tr("Pica Surface Viewer"), parent),
surface_source(Source::ColorBuffer) surface_source(Source::ColorBuffer) {
{
setObjectName("PicaSurface"); setObjectName("PicaSurface");
surface_source_list = new QComboBox; surface_source_list = new QComboBox;
@ -124,13 +117,20 @@ GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext>
// Connections // Connections
connect(this, SIGNAL(Update()), this, SLOT(OnUpdate())); connect(this, SIGNAL(Update()), this, SLOT(OnUpdate()));
connect(surface_source_list, SIGNAL(currentIndexChanged(int)), this, SLOT(OnSurfaceSourceChanged(int))); connect(surface_source_list, SIGNAL(currentIndexChanged(int)), this,
connect(surface_address_control, SIGNAL(ValueChanged(qint64)), this, SLOT(OnSurfaceAddressChanged(qint64))); SLOT(OnSurfaceSourceChanged(int)));
connect(surface_width_control, SIGNAL(valueChanged(int)), this, SLOT(OnSurfaceWidthChanged(int))); connect(surface_address_control, SIGNAL(ValueChanged(qint64)), this,
connect(surface_height_control, SIGNAL(valueChanged(int)), this, SLOT(OnSurfaceHeightChanged(int))); SLOT(OnSurfaceAddressChanged(qint64)));
connect(surface_format_control, SIGNAL(currentIndexChanged(int)), this, SLOT(OnSurfaceFormatChanged(int))); connect(surface_width_control, SIGNAL(valueChanged(int)), this,
connect(surface_picker_x_control, SIGNAL(valueChanged(int)), this, SLOT(OnSurfacePickerXChanged(int))); SLOT(OnSurfaceWidthChanged(int)));
connect(surface_picker_y_control, SIGNAL(valueChanged(int)), this, SLOT(OnSurfacePickerYChanged(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())); connect(save_surface, SIGNAL(clicked()), this, SLOT(SaveSurface()));
auto main_widget = new QWidget; auto main_widget = new QWidget;
@ -203,25 +203,21 @@ GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext>
} }
} }
void GraphicsSurfaceWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) void GraphicsSurfaceWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) {
{
emit Update(); emit Update();
widget()->setEnabled(true); widget()->setEnabled(true);
} }
void GraphicsSurfaceWidget::OnResumed() void GraphicsSurfaceWidget::OnResumed() {
{
widget()->setEnabled(false); widget()->setEnabled(false);
} }
void GraphicsSurfaceWidget::OnSurfaceSourceChanged(int new_value) void GraphicsSurfaceWidget::OnSurfaceSourceChanged(int new_value) {
{
surface_source = static_cast<Source>(new_value); surface_source = static_cast<Source>(new_value);
emit Update(); emit Update();
} }
void GraphicsSurfaceWidget::OnSurfaceAddressChanged(qint64 new_value) void GraphicsSurfaceWidget::OnSurfaceAddressChanged(qint64 new_value) {
{
if (surface_address != new_value) { if (surface_address != new_value) {
surface_address = static_cast<unsigned>(new_value); surface_address = static_cast<unsigned>(new_value);
@ -230,8 +226,7 @@ void GraphicsSurfaceWidget::OnSurfaceAddressChanged(qint64 new_value)
} }
} }
void GraphicsSurfaceWidget::OnSurfaceWidthChanged(int new_value) void GraphicsSurfaceWidget::OnSurfaceWidthChanged(int new_value) {
{
if (surface_width != static_cast<unsigned>(new_value)) { if (surface_width != static_cast<unsigned>(new_value)) {
surface_width = static_cast<unsigned>(new_value); surface_width = static_cast<unsigned>(new_value);
@ -240,8 +235,7 @@ void GraphicsSurfaceWidget::OnSurfaceWidthChanged(int new_value)
} }
} }
void GraphicsSurfaceWidget::OnSurfaceHeightChanged(int new_value) void GraphicsSurfaceWidget::OnSurfaceHeightChanged(int new_value) {
{
if (surface_height != static_cast<unsigned>(new_value)) { if (surface_height != static_cast<unsigned>(new_value)) {
surface_height = static_cast<unsigned>(new_value); surface_height = static_cast<unsigned>(new_value);
@ -250,8 +244,7 @@ void GraphicsSurfaceWidget::OnSurfaceHeightChanged(int new_value)
} }
} }
void GraphicsSurfaceWidget::OnSurfaceFormatChanged(int new_value) void GraphicsSurfaceWidget::OnSurfaceFormatChanged(int new_value) {
{
if (surface_format != static_cast<Format>(new_value)) { if (surface_format != static_cast<Format>(new_value)) {
surface_format = static_cast<Format>(new_value); surface_format = static_cast<Format>(new_value);
@ -260,24 +253,21 @@ void GraphicsSurfaceWidget::OnSurfaceFormatChanged(int new_value)
} }
} }
void GraphicsSurfaceWidget::OnSurfacePickerXChanged(int new_value) void GraphicsSurfaceWidget::OnSurfacePickerXChanged(int new_value) {
{
if (surface_picker_x != new_value) { if (surface_picker_x != new_value) {
surface_picker_x = new_value; surface_picker_x = new_value;
Pick(surface_picker_x, surface_picker_y); Pick(surface_picker_x, surface_picker_y);
} }
} }
void GraphicsSurfaceWidget::OnSurfacePickerYChanged(int new_value) void GraphicsSurfaceWidget::OnSurfacePickerYChanged(int new_value) {
{
if (surface_picker_y != new_value) { if (surface_picker_y != new_value) {
surface_picker_y = new_value; surface_picker_y = new_value;
Pick(surface_picker_x, surface_picker_y); Pick(surface_picker_x, surface_picker_y);
} }
} }
void GraphicsSurfaceWidget::Pick(int x, int y) void GraphicsSurfaceWidget::Pick(int x, int y) {
{
surface_picker_x_control->setValue(x); surface_picker_x_control->setValue(x);
surface_picker_y_control->setValue(y); surface_picker_y_control->setValue(y);
@ -312,8 +302,7 @@ void GraphicsSurfaceWidget::Pick(int x, int y)
auto GetText = [offset](Format format, const u8* pixel) { auto GetText = [offset](Format format, const u8* pixel) {
switch (format) { switch (format) {
case Format::RGBA8: case Format::RGBA8: {
{
auto value = Color::DecodeRGBA8(pixel) / 255.0f; auto value = Color::DecodeRGBA8(pixel) / 255.0f;
return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4") return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4")
.arg(QString::number(value.r(), 'f', 2)) .arg(QString::number(value.r(), 'f', 2))
@ -321,16 +310,14 @@ void GraphicsSurfaceWidget::Pick(int x, int y)
.arg(QString::number(value.b(), 'f', 2)) .arg(QString::number(value.b(), 'f', 2))
.arg(QString::number(value.a(), 'f', 2)); .arg(QString::number(value.a(), 'f', 2));
} }
case Format::RGB8: case Format::RGB8: {
{
auto value = Color::DecodeRGB8(pixel) / 255.0f; auto value = Color::DecodeRGB8(pixel) / 255.0f;
return QString("Red: %1, Green: %2, Blue: %3") return QString("Red: %1, Green: %2, Blue: %3")
.arg(QString::number(value.r(), 'f', 2)) .arg(QString::number(value.r(), 'f', 2))
.arg(QString::number(value.g(), 'f', 2)) .arg(QString::number(value.g(), 'f', 2))
.arg(QString::number(value.b(), 'f', 2)); .arg(QString::number(value.b(), 'f', 2));
} }
case Format::RGB5A1: case Format::RGB5A1: {
{
auto value = Color::DecodeRGB5A1(pixel) / 255.0f; auto value = Color::DecodeRGB5A1(pixel) / 255.0f;
return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4") return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4")
.arg(QString::number(value.r(), 'f', 2)) .arg(QString::number(value.r(), 'f', 2))
@ -338,16 +325,14 @@ void GraphicsSurfaceWidget::Pick(int x, int y)
.arg(QString::number(value.b(), 'f', 2)) .arg(QString::number(value.b(), 'f', 2))
.arg(QString::number(value.a(), 'f', 2)); .arg(QString::number(value.a(), 'f', 2));
} }
case Format::RGB565: case Format::RGB565: {
{
auto value = Color::DecodeRGB565(pixel) / 255.0f; auto value = Color::DecodeRGB565(pixel) / 255.0f;
return QString("Red: %1, Green: %2, Blue: %3") return QString("Red: %1, Green: %2, Blue: %3")
.arg(QString::number(value.r(), 'f', 2)) .arg(QString::number(value.r(), 'f', 2))
.arg(QString::number(value.g(), 'f', 2)) .arg(QString::number(value.g(), 'f', 2))
.arg(QString::number(value.b(), 'f', 2)); .arg(QString::number(value.b(), 'f', 2));
} }
case Format::RGBA4: case Format::RGBA4: {
{
auto value = Color::DecodeRGBA4(pixel) / 255.0f; auto value = Color::DecodeRGBA4(pixel) / 255.0f;
return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4") return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4")
.arg(QString::number(value.r(), 'f', 2)) .arg(QString::number(value.r(), 'f', 2))
@ -356,9 +341,7 @@ void GraphicsSurfaceWidget::Pick(int x, int y)
.arg(QString::number(value.a(), 'f', 2)); .arg(QString::number(value.a(), 'f', 2));
} }
case Format::IA8: case Format::IA8:
return QString("Index: %1, Alpha: %2") return QString("Index: %1, Alpha: %2").arg(pixel[0]).arg(pixel[1]);
.arg(pixel[0])
.arg(pixel[1]);
case Format::RG8: { case Format::RG8: {
auto value = Color::DecodeRG8(pixel) / 255.0f; auto value = Color::DecodeRG8(pixel) / 255.0f;
return QString("Red: %1, Green: %2") return QString("Red: %1, Green: %2")
@ -370,16 +353,12 @@ void GraphicsSurfaceWidget::Pick(int x, int y)
case Format::A8: case Format::A8:
return QString("Alpha: %1").arg(QString::number(*pixel / 255.0f, 'f', 2)); return QString("Alpha: %1").arg(QString::number(*pixel / 255.0f, 'f', 2));
case Format::IA4: case Format::IA4:
return QString("Index: %1, Alpha: %2") return QString("Index: %1, Alpha: %2").arg(*pixel & 0xF).arg((*pixel & 0xF0) >> 4);
.arg(*pixel & 0xF) case Format::I4: {
.arg((*pixel & 0xF0) >> 4);
case Format::I4:
{
u8 i = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF; u8 i = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF;
return QString("Index: %1").arg(i); return QString("Index: %1").arg(i);
} }
case Format::A4: case Format::A4: {
{
u8 a = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF; u8 a = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF;
return QString("Alpha: %1").arg(QString::number(a / 15.0f, 'f', 2)); return QString("Alpha: %1").arg(QString::number(a / 15.0f, 'f', 2));
} }
@ -387,21 +366,20 @@ void GraphicsSurfaceWidget::Pick(int x, int y)
case Format::ETC1A4: case Format::ETC1A4:
// TODO: Display block information or channel values? // TODO: Display block information or channel values?
return QString("Compressed data"); return QString("Compressed data");
case Format::D16: case Format::D16: {
{
auto value = Color::DecodeD16(pixel); auto value = Color::DecodeD16(pixel);
return QString("Depth: %1").arg(QString::number(value / (float)0xFFFF, 'f', 4)); return QString("Depth: %1").arg(QString::number(value / (float)0xFFFF, 'f', 4));
} }
case Format::D24: case Format::D24: {
{
auto value = Color::DecodeD24(pixel); auto value = Color::DecodeD24(pixel);
return QString("Depth: %1").arg(QString::number(value / (float)0xFFFFFF, 'f', 4)); return QString("Depth: %1").arg(QString::number(value / (float)0xFFFFFF, 'f', 4));
} }
case Format::D24X8: case Format::D24X8:
case Format::X24S8: case Format::X24S8: {
{
auto values = Color::DecodeD24S8(pixel); auto values = Color::DecodeD24S8(pixel);
return QString("Depth: %1, Stencil: %2").arg(QString::number(values[0] / (float)0xFFFFFF, 'f', 4)).arg(values[1]); return QString("Depth: %1, Stencil: %2")
.arg(QString::number(values[0] / (float)0xFFFFFF, 'f', 4))
.arg(values[1]);
} }
case Format::Unknown: case Format::Unknown:
return QString("Unknown format"); return QString("Unknown format");
@ -422,18 +400,18 @@ void GraphicsSurfaceWidget::Pick(int x, int y)
nibbles.append(QString::number(nibble, 16).toUpper()); 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->setText(
QString("Raw: 0x%3\n(%4)").arg(nibbles).arg(GetText(surface_format, pixel)));
surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
} }
void GraphicsSurfaceWidget::OnUpdate() void GraphicsSurfaceWidget::OnUpdate() {
{
QPixmap pixmap; QPixmap pixmap;
switch (surface_source) { switch (surface_source) {
case Source::ColorBuffer: case Source::ColorBuffer: {
{ // TODO: Store a reference to the registers in the debug context instead of accessing them
// TODO: Store a reference to the registers in the debug context instead of accessing them directly... // directly...
const auto& framebuffer = Pica::g_state.regs.framebuffer; const auto& framebuffer = Pica::g_state.regs.framebuffer;
@ -470,8 +448,7 @@ void GraphicsSurfaceWidget::OnUpdate()
break; break;
} }
case Source::DepthBuffer: case Source::DepthBuffer: {
{
const auto& framebuffer = Pica::g_state.regs.framebuffer; const auto& framebuffer = Pica::g_state.regs.framebuffer;
surface_address = framebuffer.GetDepthBufferPhysicalAddress(); surface_address = framebuffer.GetDepthBufferPhysicalAddress();
@ -499,8 +476,7 @@ void GraphicsSurfaceWidget::OnUpdate()
break; break;
} }
case Source::StencilBuffer: case Source::StencilBuffer: {
{
const auto& framebuffer = Pica::g_state.regs.framebuffer; const auto& framebuffer = Pica::g_state.regs.framebuffer;
surface_address = framebuffer.GetDepthBufferPhysicalAddress(); surface_address = framebuffer.GetDepthBufferPhysicalAddress();
@ -522,12 +498,14 @@ void GraphicsSurfaceWidget::OnUpdate()
case Source::Texture0: case Source::Texture0:
case Source::Texture1: case Source::Texture1:
case Source::Texture2: case Source::Texture2: {
{
unsigned texture_index; unsigned texture_index;
if (surface_source == Source::Texture0) texture_index = 0; if (surface_source == Source::Texture0)
else if (surface_source == Source::Texture1) texture_index = 1; texture_index = 0;
else if (surface_source == Source::Texture2) texture_index = 2; else if (surface_source == Source::Texture1)
texture_index = 1;
else if (surface_source == Source::Texture2)
texture_index = 2;
else { else {
qDebug() << "Unknown texture source " << static_cast<int>(surface_source); qDebug() << "Unknown texture source " << static_cast<int>(surface_source);
break; break;
@ -547,8 +525,7 @@ void GraphicsSurfaceWidget::OnUpdate()
break; break;
} }
case Source::Custom: case Source::Custom: {
{
// Keep user-specified values // Keep user-specified values
break; break;
} }
@ -613,7 +590,8 @@ void GraphicsSurfaceWidget::OnUpdate()
} else { } else {
ASSERT_MSG(nibbles_per_pixel >= 2, "Depth decoder only supports formats with at least one byte per pixel"); 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; unsigned bytes_per_pixel = nibbles_per_pixel / 2;
for (unsigned int y = 0; y < surface_height; ++y) { for (unsigned int y = 0; y < surface_height; ++y) {
@ -621,34 +599,30 @@ void GraphicsSurfaceWidget::OnUpdate()
const u32 coarse_y = y & ~7; const u32 coarse_y = y & ~7;
u32 offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride; u32 offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride;
const u8* pixel = buffer + offset; const u8* pixel = buffer + offset;
Math::Vec4<u8> color = { 0, 0, 0, 0 }; Math::Vec4<u8> color = {0, 0, 0, 0};
switch(surface_format) { switch (surface_format) {
case Format::D16: case Format::D16: {
{
u32 data = Color::DecodeD16(pixel); u32 data = Color::DecodeD16(pixel);
color.r() = data & 0xFF; color.r() = data & 0xFF;
color.g() = (data >> 8) & 0xFF; color.g() = (data >> 8) & 0xFF;
break; break;
} }
case Format::D24: case Format::D24: {
{
u32 data = Color::DecodeD24(pixel); u32 data = Color::DecodeD24(pixel);
color.r() = data & 0xFF; color.r() = data & 0xFF;
color.g() = (data >> 8) & 0xFF; color.g() = (data >> 8) & 0xFF;
color.b() = (data >> 16) & 0xFF; color.b() = (data >> 16) & 0xFF;
break; break;
} }
case Format::D24X8: case Format::D24X8: {
{
Math::Vec2<u32> data = Color::DecodeD24S8(pixel); Math::Vec2<u32> data = Color::DecodeD24S8(pixel);
color.r() = data.x & 0xFF; color.r() = data.x & 0xFF;
color.g() = (data.x >> 8) & 0xFF; color.g() = (data.x >> 8) & 0xFF;
color.b() = (data.x >> 16) & 0xFF; color.b() = (data.x >> 16) & 0xFF;
break; break;
} }
case Format::X24S8: case Format::X24S8: {
{
Math::Vec2<u32> data = Color::DecodeD24S8(pixel); Math::Vec2<u32> data = Color::DecodeD24S8(pixel);
color.r() = color.g() = color.b() = data.y; color.r() = color.g() = color.b() = data.y;
break; break;
@ -661,7 +635,6 @@ void GraphicsSurfaceWidget::OnUpdate()
decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), 255)); decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), 255));
} }
} }
} }
pixmap = QPixmap::fromImage(decoded_image); pixmap = QPixmap::fromImage(decoded_image);
@ -682,7 +655,9 @@ void GraphicsSurfaceWidget::SaveSurface() {
QString bin_filter = tr("Binary data (*.bin)"); QString bin_filter = tr("Binary data (*.bin)");
QString selectedFilter; QString selectedFilter;
QString filename = QFileDialog::getSaveFileName(this, tr("Save Surface"), QString("texture-0x%1.png").arg(QString::number(surface_address, 16)), 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); QString("%1;;%2").arg(png_filter, bin_filter), &selectedFilter);
if (filename.isEmpty()) { if (filename.isEmpty()) {
@ -726,9 +701,8 @@ unsigned int GraphicsSurfaceWidget::NibblesPerPixel(GraphicsSurfaceWidget::Forma
case Format::D16: case Format::D16:
return 2 * 2; return 2 * 2;
default: default:
UNREACHABLE_MSG("GraphicsSurfaceWidget::BytesPerPixel: this " UNREACHABLE_MSG("GraphicsSurfaceWidget::BytesPerPixel: this should not be reached as this "
"should not be reached as this function should " "function should be given a format which is in "
"be given a format which is in "
"GraphicsSurfaceWidget::Format. Instead got %i", "GraphicsSurfaceWidget::Format. Instead got %i",
static_cast<int>(format)); static_cast<int>(format));
return 0; return 0;

View File

@ -4,10 +4,9 @@
#pragma once #pragma once
#include "citra_qt/debugger/graphics_breakpoint_observer.h"
#include <QLabel> #include <QLabel>
#include <QPushButton> #include <QPushButton>
#include "citra_qt/debugger/graphics_breakpoint_observer.h"
class QComboBox; class QComboBox;
class QSpinBox; class QSpinBox;
@ -15,8 +14,7 @@ class CSpinBox;
class GraphicsSurfaceWidget; class GraphicsSurfaceWidget;
class SurfacePicture : public QLabel class SurfacePicture : public QLabel {
{
Q_OBJECT Q_OBJECT
public: public:
@ -29,7 +27,6 @@ protected slots:
private: private:
GraphicsSurfaceWidget* surface_widget; GraphicsSurfaceWidget* surface_widget;
}; };
class GraphicsSurfaceWidget : public BreakPointObserverDock { class GraphicsSurfaceWidget : public BreakPointObserverDock {
@ -74,7 +71,8 @@ class GraphicsSurfaceWidget : public BreakPointObserverDock {
static unsigned int NibblesPerPixel(Format format); static unsigned int NibblesPerPixel(Format format);
public: public:
GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent = nullptr); GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext> debug_context,
QWidget* parent = nullptr);
void Pick(int x, int y); void Pick(int x, int y);
public slots: public slots:
@ -97,7 +95,6 @@ signals:
void Update(); void Update();
private: private:
QComboBox* surface_source_list; QComboBox* surface_source_list;
CSpinBox* surface_address_control; CSpinBox* surface_address_control;
QSpinBox* surface_width_control; QSpinBox* surface_width_control;

View File

@ -6,25 +6,18 @@
#include <array> #include <array>
#include <iterator> #include <iterator>
#include <memory> #include <memory>
#include <boost/range/algorithm/copy.hpp>
#include <QBoxLayout> #include <QBoxLayout>
#include <QComboBox> #include <QComboBox>
#include <QFileDialog> #include <QFileDialog>
#include <QMessageBox> #include <QMessageBox>
#include <QPushButton> #include <QPushButton>
#include <boost/range/algorithm/copy.hpp>
#include "citra_qt/debugger/graphics_tracing.h" #include "citra_qt/debugger/graphics_tracing.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "core/hw/gpu.h" #include "core/hw/gpu.h"
#include "core/hw/lcd.h" #include "core/hw/lcd.h"
#include "core/tracer/recorder.h" #include "core/tracer/recorder.h"
#include "nihstro/float24.h" #include "nihstro/float24.h"
#include "video_core/pica.h" #include "video_core/pica.h"
#include "video_core/pica_state.h" #include "video_core/pica_state.h"
@ -35,12 +28,16 @@ GraphicsTracingWidget::GraphicsTracingWidget(std::shared_ptr<Pica::DebugContext>
setObjectName("CiTracing"); setObjectName("CiTracing");
QPushButton* start_recording = new QPushButton(tr("Start Recording")); QPushButton* start_recording = new QPushButton(tr("Start Recording"));
QPushButton* stop_recording = new QPushButton(QIcon::fromTheme("document-save"), tr("Stop and Save")); QPushButton* stop_recording =
new QPushButton(QIcon::fromTheme("document-save"), tr("Stop and Save"));
QPushButton* abort_recording = new QPushButton(tr("Abort Recording")); QPushButton* abort_recording = new QPushButton(tr("Abort Recording"));
connect(this, SIGNAL(SetStartTracingButtonEnabled(bool)), start_recording, SLOT(setVisible(bool))); connect(this, SIGNAL(SetStartTracingButtonEnabled(bool)), start_recording,
connect(this, SIGNAL(SetStopTracingButtonEnabled(bool)), stop_recording, SLOT(setVisible(bool))); SLOT(setVisible(bool)));
connect(this, SIGNAL(SetAbortTracingButtonEnabled(bool)), abort_recording, SLOT(setVisible(bool))); connect(this, SIGNAL(SetStopTracingButtonEnabled(bool)), stop_recording,
SLOT(setVisible(bool)));
connect(this, SIGNAL(SetAbortTracingButtonEnabled(bool)), abort_recording,
SLOT(setVisible(bool)));
connect(start_recording, SIGNAL(clicked()), this, SLOT(StartRecording())); connect(start_recording, SIGNAL(clicked()), this, SLOT(StartRecording()));
connect(stop_recording, SIGNAL(clicked()), this, SLOT(StopRecording())); connect(stop_recording, SIGNAL(clicked()), this, SLOT(StopRecording()));
connect(abort_recording, SIGNAL(clicked()), this, SLOT(AbortRecording())); connect(abort_recording, SIGNAL(clicked()), this, SLOT(AbortRecording()));
@ -74,26 +71,31 @@ void GraphicsTracingWidget::StartRecording() {
std::array<u32, 4 * 16> default_attributes; std::array<u32, 4 * 16> default_attributes;
for (unsigned i = 0; i < 16; ++i) { for (unsigned i = 0; i < 16; ++i) {
for (unsigned comp = 0; comp < 3; ++comp) { for (unsigned comp = 0; comp < 3; ++comp) {
default_attributes[4 * i + comp] = nihstro::to_float24(Pica::g_state.vs_default_attributes[i][comp].ToFloat32()); default_attributes[4 * i + comp] =
nihstro::to_float24(Pica::g_state.vs_default_attributes[i][comp].ToFloat32());
} }
} }
std::array<u32, 4 * 96> vs_float_uniforms; std::array<u32, 4 * 96> vs_float_uniforms;
for (unsigned i = 0; i < 96; ++i) for (unsigned i = 0; i < 96; ++i)
for (unsigned comp = 0; comp < 3; ++comp) for (unsigned comp = 0; comp < 3; ++comp)
vs_float_uniforms[4 * i + comp] = nihstro::to_float24(Pica::g_state.vs.uniforms.f[i][comp].ToFloat32()); vs_float_uniforms[4 * i + comp] =
nihstro::to_float24(Pica::g_state.vs.uniforms.f[i][comp].ToFloat32());
CiTrace::Recorder::InitialState state; CiTrace::Recorder::InitialState state;
std::copy_n((u32*)&GPU::g_regs, sizeof(GPU::g_regs) / sizeof(u32), std::back_inserter(state.gpu_registers)); std::copy_n((u32*)&GPU::g_regs, sizeof(GPU::g_regs) / sizeof(u32),
std::copy_n((u32*)&LCD::g_regs, sizeof(LCD::g_regs) / sizeof(u32), std::back_inserter(state.lcd_registers)); std::back_inserter(state.gpu_registers));
std::copy_n((u32*)&Pica::g_state.regs, sizeof(Pica::g_state.regs) / sizeof(u32), std::back_inserter(state.pica_registers)); std::copy_n((u32*)&LCD::g_regs, sizeof(LCD::g_regs) / sizeof(u32),
std::back_inserter(state.lcd_registers));
std::copy_n((u32*)&Pica::g_state.regs, sizeof(Pica::g_state.regs) / sizeof(u32),
std::back_inserter(state.pica_registers));
boost::copy(default_attributes, std::back_inserter(state.default_attributes)); boost::copy(default_attributes, std::back_inserter(state.default_attributes));
boost::copy(shader_binary, std::back_inserter(state.vs_program_binary)); boost::copy(shader_binary, std::back_inserter(state.vs_program_binary));
boost::copy(swizzle_data, std::back_inserter(state.vs_swizzle_data)); boost::copy(swizzle_data, std::back_inserter(state.vs_swizzle_data));
boost::copy(vs_float_uniforms, std::back_inserter(state.vs_float_uniforms)); boost::copy(vs_float_uniforms, std::back_inserter(state.vs_float_uniforms));
//boost::copy(TODO: Not implemented, std::back_inserter(state.gs_program_binary)); // boost::copy(TODO: Not implemented, std::back_inserter(state.gs_program_binary));
//boost::copy(TODO: Not implemented, std::back_inserter(state.gs_swizzle_data)); // boost::copy(TODO: Not implemented, std::back_inserter(state.gs_swizzle_data));
//boost::copy(TODO: Not implemented, std::back_inserter(state.gs_float_uniforms)); // boost::copy(TODO: Not implemented, std::back_inserter(state.gs_float_uniforms));
auto recorder = new CiTrace::Recorder(state); auto recorder = new CiTrace::Recorder(state);
context->recorder = std::shared_ptr<CiTrace::Recorder>(recorder); context->recorder = std::shared_ptr<CiTrace::Recorder>(recorder);
@ -156,10 +158,11 @@ void GraphicsTracingWidget::OnEmulationStopping() {
if (!context) if (!context)
return; return;
if (context->recorder) { if (context->recorder) {
auto reply = QMessageBox::question(this, tr("CiTracing still active"), auto reply =
tr("A CiTrace is still being recorded. Do you want to save it? If not, all recorded data will be discarded."), QMessageBox::question(this, tr("CiTracing still active"),
tr("A CiTrace is still being recorded. Do you want to save it? "
"If not, all recorded data will be discarded."),
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
if (reply == QMessageBox::Yes) { if (reply == QMessageBox::Yes) {

View File

@ -12,7 +12,8 @@ class GraphicsTracingWidget : public BreakPointObserverDock {
Q_OBJECT Q_OBJECT
public: public:
GraphicsTracingWidget(std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent = nullptr); GraphicsTracingWidget(std::shared_ptr<Pica::DebugContext> debug_context,
QWidget* parent = nullptr);
private slots: private slots:
void StartRecording(); void StartRecording();

View File

@ -4,7 +4,6 @@
#include <iomanip> #include <iomanip>
#include <sstream> #include <sstream>
#include <QBoxLayout> #include <QBoxLayout>
#include <QFileDialog> #include <QFileDialog>
#include <QFormLayout> #include <QFormLayout>
@ -15,10 +14,8 @@
#include <QSignalMapper> #include <QSignalMapper>
#include <QSpinBox> #include <QSpinBox>
#include <QTreeView> #include <QTreeView>
#include "citra_qt/debugger/graphics_vertex_shader.h" #include "citra_qt/debugger/graphics_vertex_shader.h"
#include "citra_qt/util/util.h" #include "citra_qt/util/util.h"
#include "video_core/pica.h" #include "video_core/pica.h"
#include "video_core/pica_state.h" #include "video_core/pica_state.h"
#include "video_core/shader/shader.h" #include "video_core/shader/shader.h"
@ -28,9 +25,8 @@ using nihstro::Instruction;
using nihstro::SourceRegister; using nihstro::SourceRegister;
using nihstro::SwizzlePattern; using nihstro::SwizzlePattern;
GraphicsVertexShaderModel::GraphicsVertexShaderModel(GraphicsVertexShaderWidget* parent): QAbstractTableModel(parent), par(parent) { GraphicsVertexShaderModel::GraphicsVertexShaderModel(GraphicsVertexShaderWidget* parent)
: QAbstractTableModel(parent), par(parent) {}
}
int GraphicsVertexShaderModel::columnCount(const QModelIndex& parent) const { int GraphicsVertexShaderModel::columnCount(const QModelIndex& parent) const {
return 3; return 3;
@ -40,10 +36,10 @@ int GraphicsVertexShaderModel::rowCount(const QModelIndex& parent) const {
return static_cast<int>(par->info.code.size()); return static_cast<int>(par->info.code.size());
} }
QVariant GraphicsVertexShaderModel::headerData(int section, Qt::Orientation orientation, int role) const { QVariant GraphicsVertexShaderModel::headerData(int section, Qt::Orientation orientation,
switch(role) { int role) const {
case Qt::DisplayRole: switch (role) {
{ case Qt::DisplayRole: {
if (section == 0) { if (section == 0) {
return tr("Offset"); return tr("Offset");
} else if (section == 1) { } else if (section == 1) {
@ -69,8 +65,8 @@ static std::string SelectorToString(u32 selector) {
} }
// e.g. "-c92[a0.x].xyzw" // e.g. "-c92[a0.x].xyzw"
static void print_input(std::ostringstream& output, const SourceRegister& input, static void print_input(std::ostringstream& output, const SourceRegister& input, bool negate,
bool negate, const std::string& swizzle_mask, bool align = true, const std::string& swizzle_mask, bool align = true,
const std::string& address_register_name = std::string()) { const std::string& address_register_name = std::string()) {
if (align) if (align)
output << std::setw(4) << std::right; output << std::setw(4) << std::right;
@ -83,20 +79,18 @@ static void print_input(std::ostringstream& output, const SourceRegister& input,
QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) const { QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) const {
switch (role) { switch (role) {
case Qt::DisplayRole: case Qt::DisplayRole: {
{
switch (index.column()) { switch (index.column()) {
case 0: case 0:
if (par->info.HasLabel(index.row())) if (par->info.HasLabel(index.row()))
return QString::fromStdString(par->info.GetLabel(index.row())); return QString::fromStdString(par->info.GetLabel(index.row()));
return QString("%1").arg(4*index.row(), 4, 16, QLatin1Char('0')); return QString("%1").arg(4 * index.row(), 4, 16, QLatin1Char('0'));
case 1: case 1:
return QString("%1").arg(par->info.code[index.row()].hex, 8, 16, QLatin1Char('0')); return QString("%1").arg(par->info.code[index.row()].hex, 8, 16, QLatin1Char('0'));
case 2: case 2: {
{
std::ostringstream output; std::ostringstream output;
output.flags(std::ios::uppercase); output.flags(std::ios::uppercase);
@ -117,8 +111,9 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
const Instruction instr = par->info.code[index.row()]; const Instruction instr = par->info.code[index.row()];
const OpCode opcode = instr.opcode; const OpCode opcode = instr.opcode;
const OpCode::Info opcode_info = opcode.GetInfo(); const OpCode::Info opcode_info = opcode.GetInfo();
const u32 operand_desc_id = opcode_info.type == OpCode::Type::MultiplyAdd ? const u32 operand_desc_id = opcode_info.type == OpCode::Type::MultiplyAdd
instr.mad.operand_desc_id.Value() : instr.common.operand_desc_id.Value(); ? instr.mad.operand_desc_id.Value()
: instr.common.operand_desc_id.Value();
const SwizzlePattern swizzle = par->info.swizzle_info[operand_desc_id].pattern; const SwizzlePattern swizzle = par->info.swizzle_info[operand_desc_id].pattern;
// longest known instruction name: "setemit " // longest known instruction name: "setemit "
@ -136,15 +131,14 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
break; break;
case OpCode::Type::Arithmetic: case OpCode::Type::Arithmetic:
case OpCode::Type::MultiplyAdd: case OpCode::Type::MultiplyAdd: {
{
// Use custom code for special instructions // Use custom code for special instructions
switch (opcode.EffectiveOpCode()) { switch (opcode.EffectiveOpCode()) {
case OpCode::Id::CMP: case OpCode::Id::CMP: {
{
AlignToColumn(kOpcodeColumnWidth); AlignToColumn(kOpcodeColumnWidth);
// NOTE: CMP always writes both cc components, so we do not consider the dest mask here. // NOTE: CMP always writes both cc components, so we do not consider the dest
// mask here.
output << " cc.xy"; output << " cc.xy";
AlignToColumn(kOutputColumnWidth); AlignToColumn(kOutputColumnWidth);
@ -152,22 +146,29 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
SourceRegister src2 = instr.common.GetSrc2(false); SourceRegister src2 = instr.common.GetSrc2(false);
output << ' '; output << ' ';
print_input(output, src1, swizzle.negate_src1, swizzle.SelectorToString(false).substr(0,1), false, instr.common.AddressRegisterName()); print_input(output, src1, swizzle.negate_src1,
output << ' ' << instr.common.compare_op.ToString(instr.common.compare_op.x) << ' '; swizzle.SelectorToString(false).substr(0, 1), false,
print_input(output, src2, swizzle.negate_src2, swizzle.SelectorToString(true).substr(0,1), false); instr.common.AddressRegisterName());
output << ' ' << instr.common.compare_op.ToString(instr.common.compare_op.x)
<< ' ';
print_input(output, src2, swizzle.negate_src2,
swizzle.SelectorToString(true).substr(0, 1), false);
output << ", "; output << ", ";
print_input(output, src1, swizzle.negate_src1, swizzle.SelectorToString(false).substr(1,1), false, instr.common.AddressRegisterName()); print_input(output, src1, swizzle.negate_src1,
output << ' ' << instr.common.compare_op.ToString(instr.common.compare_op.y) << ' '; swizzle.SelectorToString(false).substr(1, 1), false,
print_input(output, src2, swizzle.negate_src2, swizzle.SelectorToString(true).substr(1,1), false); instr.common.AddressRegisterName());
output << ' ' << instr.common.compare_op.ToString(instr.common.compare_op.y)
<< ' ';
print_input(output, src2, swizzle.negate_src2,
swizzle.SelectorToString(true).substr(1, 1), false);
break; break;
} }
case OpCode::Id::MAD: case OpCode::Id::MAD:
case OpCode::Id::MADI: case OpCode::Id::MADI: {
{
AlignToColumn(kOpcodeColumnWidth); AlignToColumn(kOpcodeColumnWidth);
bool src_is_inverted = 0 != (opcode_info.subtype & OpCode::Info::SrcInversed); bool src_is_inverted = 0 != (opcode_info.subtype & OpCode::Info::SrcInversed);
@ -175,34 +176,42 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
SourceRegister src2 = instr.mad.GetSrc2(src_is_inverted); SourceRegister src2 = instr.mad.GetSrc2(src_is_inverted);
SourceRegister src3 = instr.mad.GetSrc3(src_is_inverted); SourceRegister src3 = instr.mad.GetSrc3(src_is_inverted);
output << std::setw(3) << std::right << instr.mad.dest.Value().GetName() << '.' << swizzle.DestMaskToString(); output << std::setw(3) << std::right << instr.mad.dest.Value().GetName() << '.'
<< swizzle.DestMaskToString();
AlignToColumn(kOutputColumnWidth); AlignToColumn(kOutputColumnWidth);
print_input(output, src1, swizzle.negate_src1, SelectorToString(swizzle.src1_selector)); print_input(output, src1, swizzle.negate_src1,
SelectorToString(swizzle.src1_selector));
AlignToColumn(kInputOperandColumnWidth); AlignToColumn(kInputOperandColumnWidth);
if (src_is_inverted) { if (src_is_inverted) {
print_input(output, src2, swizzle.negate_src2, SelectorToString(swizzle.src2_selector)); print_input(output, src2, swizzle.negate_src2,
SelectorToString(swizzle.src2_selector));
} else { } else {
print_input(output, src2, swizzle.negate_src2, SelectorToString(swizzle.src2_selector), true, instr.mad.AddressRegisterName()); print_input(output, src2, swizzle.negate_src2,
SelectorToString(swizzle.src2_selector), true,
instr.mad.AddressRegisterName());
} }
AlignToColumn(kInputOperandColumnWidth); AlignToColumn(kInputOperandColumnWidth);
if (src_is_inverted) { if (src_is_inverted) {
print_input(output, src3, swizzle.negate_src3, SelectorToString(swizzle.src3_selector), true, instr.mad.AddressRegisterName()); print_input(output, src3, swizzle.negate_src3,
SelectorToString(swizzle.src3_selector), true,
instr.mad.AddressRegisterName());
} else { } else {
print_input(output, src3, swizzle.negate_src3, SelectorToString(swizzle.src3_selector)); print_input(output, src3, swizzle.negate_src3,
SelectorToString(swizzle.src3_selector));
} }
AlignToColumn(kInputOperandColumnWidth); AlignToColumn(kInputOperandColumnWidth);
break; break;
} }
default: default: {
{
AlignToColumn(kOpcodeColumnWidth); AlignToColumn(kOpcodeColumnWidth);
bool src_is_inverted = 0 != (opcode_info.subtype & OpCode::Info::SrcInversed); bool src_is_inverted = 0 != (opcode_info.subtype & OpCode::Info::SrcInversed);
if (opcode_info.subtype & OpCode::Info::Dest) { if (opcode_info.subtype & OpCode::Info::Dest) {
// e.g. "r12.xy__" // e.g. "r12.xy__"
output << std::setw(3) << std::right << instr.common.dest.Value().GetName() << '.' << swizzle.DestMaskToString(); output << std::setw(3) << std::right << instr.common.dest.Value().GetName()
<< '.' << swizzle.DestMaskToString();
} else if (opcode_info.subtype == OpCode::Info::MOVA) { } else if (opcode_info.subtype == OpCode::Info::MOVA) {
output << " a0." << swizzle.DestMaskToString(); output << " a0." << swizzle.DestMaskToString();
} }
@ -210,14 +219,18 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
if (opcode_info.subtype & OpCode::Info::Src1) { if (opcode_info.subtype & OpCode::Info::Src1) {
SourceRegister src1 = instr.common.GetSrc1(src_is_inverted); SourceRegister src1 = instr.common.GetSrc1(src_is_inverted);
print_input(output, src1, swizzle.negate_src1, swizzle.SelectorToString(false), true, instr.common.AddressRegisterName()); print_input(output, src1, swizzle.negate_src1,
swizzle.SelectorToString(false), true,
instr.common.AddressRegisterName());
AlignToColumn(kInputOperandColumnWidth); AlignToColumn(kInputOperandColumnWidth);
} }
// TODO: In some cases, the Address Register is used as an index for SRC2 instead of SRC1 // TODO: In some cases, the Address Register is used as an index for SRC2
// instead of SRC1
if (opcode_info.subtype & OpCode::Info::Src2) { if (opcode_info.subtype & OpCode::Info::Src2) {
SourceRegister src2 = instr.common.GetSrc2(src_is_inverted); SourceRegister src2 = instr.common.GetSrc2(src_is_inverted);
print_input(output, src2, swizzle.negate_src2, swizzle.SelectorToString(true)); print_input(output, src2, swizzle.negate_src2,
swizzle.SelectorToString(true));
AlignToColumn(kInputOperandColumnWidth); AlignToColumn(kInputOperandColumnWidth);
} }
break; break;
@ -228,8 +241,7 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
} }
case OpCode::Type::Conditional: case OpCode::Type::Conditional:
case OpCode::Type::UniformFlowControl: case OpCode::Type::UniformFlowControl: {
{
output << ' '; output << ' ';
switch (opcode.EffectiveOpCode()) { switch (opcode.EffectiveOpCode()) {
@ -242,7 +254,8 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
output << '('; output << '(';
if (instr.flow_control.op != instr.flow_control.JustY) { if (instr.flow_control.op != instr.flow_control.JustY) {
if (instr.flow_control.refx) output << '!'; if (instr.flow_control.refx)
output << '!';
output << "cc.x"; output << "cc.x";
} }
@ -253,7 +266,8 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
} }
if (instr.flow_control.op != instr.flow_control.JustX) { if (instr.flow_control.op != instr.flow_control.JustX) {
if (instr.flow_control.refy) output << '!'; if (instr.flow_control.refy)
output << '!';
output << "cc.y"; output << "cc.y";
} }
@ -266,17 +280,23 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
u32 target_addr_else = instr.flow_control.dest_offset; u32 target_addr_else = instr.flow_control.dest_offset;
if (opcode_info.subtype & OpCode::Info::HasAlternative) { if (opcode_info.subtype & OpCode::Info::HasAlternative) {
output << "else jump to 0x" << std::setw(4) << std::right << std::setfill('0') << std::hex << (4 * instr.flow_control.dest_offset); output << "else jump to 0x" << std::setw(4) << std::right
<< std::setfill('0') << std::hex
<< (4 * instr.flow_control.dest_offset);
} else if (opcode_info.subtype & OpCode::Info::HasExplicitDest) { } else if (opcode_info.subtype & OpCode::Info::HasExplicitDest) {
output << "jump to 0x" << std::setw(4) << std::right << std::setfill('0') << std::hex << (4 * instr.flow_control.dest_offset); output << "jump to 0x" << std::setw(4) << std::right << std::setfill('0')
<< std::hex << (4 * instr.flow_control.dest_offset);
} else { } else {
// TODO: Handle other cases // TODO: Handle other cases
output << "(unknown destination)"; output << "(unknown destination)";
} }
if (opcode_info.subtype & OpCode::Info::HasFinishPoint) { if (opcode_info.subtype & OpCode::Info::HasFinishPoint) {
output << " (return on 0x" << std::setw(4) << std::right << std::setfill('0') << std::hex output << " (return on 0x" << std::setw(4) << std::right
<< (4 * instr.flow_control.dest_offset + 4 * instr.flow_control.num_instructions) << ')'; << std::setfill('0') << std::hex
<< (4 * instr.flow_control.dest_offset +
4 * instr.flow_control.num_instructions)
<< ')';
} }
break; break;
@ -300,8 +320,7 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
case Qt::FontRole: case Qt::FontRole:
return GetMonospaceFont(); return GetMonospaceFont();
case Qt::BackgroundRole: case Qt::BackgroundRole: {
{
// Highlight current instruction // Highlight current instruction
int current_record_index = par->cycle_index->value(); int current_record_index = par->cycle_index->value();
if (current_record_index < static_cast<int>(par->debug_data.records.size())) { if (current_record_index < static_cast<int>(par->debug_data.records.size())) {
@ -319,10 +338,8 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
return QBrush(QColor(192, 192, 192)); return QBrush(QColor(192, 192, 192));
} }
// TODO: Draw arrows for each "reachable" instruction to visualize control flow // TODO: Draw arrows for each "reachable" instruction to visualize control flow
default: default:
break; break;
} }
@ -331,8 +348,8 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
} }
void GraphicsVertexShaderWidget::DumpShader() { void GraphicsVertexShaderWidget::DumpShader() {
QString filename = QFileDialog::getSaveFileName(this, tr("Save Shader Dump"), "shader_dump.shbin", QString filename = QFileDialog::getSaveFileName(
tr("Shader Binary (*.shbin)")); this, tr("Save Shader Dump"), "shader_dump.shbin", tr("Shader Binary (*.shbin)"));
if (filename.isEmpty()) { if (filename.isEmpty()) {
// If the user canceled the dialog, don't dump anything. // If the user canceled the dialog, don't dump anything.
@ -342,11 +359,12 @@ void GraphicsVertexShaderWidget::DumpShader() {
auto& setup = Pica::g_state.vs; auto& setup = Pica::g_state.vs;
auto& config = Pica::g_state.regs.vs; auto& config = Pica::g_state.regs.vs;
Pica::DebugUtils::DumpShader(filename.toStdString(), config, setup, Pica::g_state.regs.vs_output_attributes); Pica::DebugUtils::DumpShader(filename.toStdString(), config, setup,
Pica::g_state.regs.vs_output_attributes);
} }
GraphicsVertexShaderWidget::GraphicsVertexShaderWidget(std::shared_ptr< Pica::DebugContext > debug_context, GraphicsVertexShaderWidget::GraphicsVertexShaderWidget(
QWidget* parent) std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent)
: BreakPointObserverDock(debug_context, "Pica Vertex Shader", parent) { : BreakPointObserverDock(debug_context, "Pica Vertex Shader", parent) {
setObjectName("PicaVertexShader"); setObjectName("PicaVertexShader");
@ -365,7 +383,8 @@ GraphicsVertexShaderWidget::GraphicsVertexShaderWidget(std::shared_ptr< Pica::De
input_data[i]->setValidator(new QDoubleValidator(input_data[i])); input_data[i]->setValidator(new QDoubleValidator(input_data[i]));
} }
breakpoint_warning = new QLabel(tr("(data only available at vertex shader invocation breakpoints)")); breakpoint_warning =
new QLabel(tr("(data only available at vertex shader invocation breakpoints)"));
// TODO: Add some button for jumping to the shader entry point // TODO: Add some button for jumping to the shader entry point
@ -442,7 +461,8 @@ GraphicsVertexShaderWidget::GraphicsVertexShaderWidget(std::shared_ptr< Pica::De
// Set a minimum height so that the size of this label doesn't cause the rest of the bottom // Set a minimum height so that the size of this label doesn't cause the rest of the bottom
// part of the UI to keep jumping up and down when cycling through instructions. // part of the UI to keep jumping up and down when cycling through instructions.
instruction_description->setMinimumHeight(instruction_description->fontMetrics().lineSpacing() * 6); instruction_description->setMinimumHeight(instruction_description->fontMetrics().lineSpacing() *
6);
instruction_description->setAlignment(Qt::AlignLeft | Qt::AlignTop); instruction_description->setAlignment(Qt::AlignLeft | Qt::AlignTop);
main_layout->addWidget(instruction_description); main_layout->addWidget(instruction_description);
@ -471,7 +491,8 @@ void GraphicsVertexShaderWidget::Reload(bool replace_vertex_data, void* vertex_d
memcpy(&input_vertex, vertex_data, sizeof(input_vertex)); memcpy(&input_vertex, vertex_data, sizeof(input_vertex));
for (unsigned attr = 0; attr < 16; ++attr) { for (unsigned attr = 0; attr < 16; ++attr) {
for (unsigned comp = 0; comp < 4; ++comp) { for (unsigned comp = 0; comp < 4; ++comp) {
input_data[4 * attr + comp]->setText(QString("%1").arg(input_vertex.attr[attr][comp].ToFloat32())); input_data[4 * attr + comp]->setText(
QString("%1").arg(input_vertex.attr[attr][comp].ToFloat32()));
} }
} }
breakpoint_warning->hide(); breakpoint_warning->hide();
@ -498,10 +519,11 @@ void GraphicsVertexShaderWidget::Reload(bool replace_vertex_data, void* vertex_d
info.swizzle_info.push_back({pattern}); info.swizzle_info.push_back({pattern});
u32 entry_point = Pica::g_state.regs.vs.main_offset; u32 entry_point = Pica::g_state.regs.vs.main_offset;
info.labels.insert({ entry_point, "main" }); info.labels.insert({entry_point, "main"});
// Generate debug information // Generate debug information
debug_data = Pica::g_state.vs.ProduceDebugInfo(input_vertex, num_attributes, shader_config, shader_setup); debug_data = Pica::g_state.vs.ProduceDebugInfo(input_vertex, num_attributes, shader_config,
shader_setup);
// Reload widget state // Reload widget state
for (int attr = 0; attr < num_attributes; ++attr) { for (int attr = 0; attr < num_attributes; ++attr) {
@ -537,29 +559,60 @@ void GraphicsVertexShaderWidget::OnCycleIndexChanged(int index) {
auto& record = debug_data.records[index]; auto& record = debug_data.records[index];
if (record.mask & Pica::Shader::DebugDataRecord::SRC1) if (record.mask & Pica::Shader::DebugDataRecord::SRC1)
text += tr("SRC1: %1, %2, %3, %4\n").arg(record.src1.x.ToFloat32()).arg(record.src1.y.ToFloat32()).arg(record.src1.z.ToFloat32()).arg(record.src1.w.ToFloat32()); text += tr("SRC1: %1, %2, %3, %4\n")
.arg(record.src1.x.ToFloat32())
.arg(record.src1.y.ToFloat32())
.arg(record.src1.z.ToFloat32())
.arg(record.src1.w.ToFloat32());
if (record.mask & Pica::Shader::DebugDataRecord::SRC2) if (record.mask & Pica::Shader::DebugDataRecord::SRC2)
text += tr("SRC2: %1, %2, %3, %4\n").arg(record.src2.x.ToFloat32()).arg(record.src2.y.ToFloat32()).arg(record.src2.z.ToFloat32()).arg(record.src2.w.ToFloat32()); text += tr("SRC2: %1, %2, %3, %4\n")
.arg(record.src2.x.ToFloat32())
.arg(record.src2.y.ToFloat32())
.arg(record.src2.z.ToFloat32())
.arg(record.src2.w.ToFloat32());
if (record.mask & Pica::Shader::DebugDataRecord::SRC3) if (record.mask & Pica::Shader::DebugDataRecord::SRC3)
text += tr("SRC3: %1, %2, %3, %4\n").arg(record.src3.x.ToFloat32()).arg(record.src3.y.ToFloat32()).arg(record.src3.z.ToFloat32()).arg(record.src3.w.ToFloat32()); text += tr("SRC3: %1, %2, %3, %4\n")
.arg(record.src3.x.ToFloat32())
.arg(record.src3.y.ToFloat32())
.arg(record.src3.z.ToFloat32())
.arg(record.src3.w.ToFloat32());
if (record.mask & Pica::Shader::DebugDataRecord::DEST_IN) if (record.mask & Pica::Shader::DebugDataRecord::DEST_IN)
text += tr("DEST_IN: %1, %2, %3, %4\n").arg(record.dest_in.x.ToFloat32()).arg(record.dest_in.y.ToFloat32()).arg(record.dest_in.z.ToFloat32()).arg(record.dest_in.w.ToFloat32()); text += tr("DEST_IN: %1, %2, %3, %4\n")
.arg(record.dest_in.x.ToFloat32())
.arg(record.dest_in.y.ToFloat32())
.arg(record.dest_in.z.ToFloat32())
.arg(record.dest_in.w.ToFloat32());
if (record.mask & Pica::Shader::DebugDataRecord::DEST_OUT) if (record.mask & Pica::Shader::DebugDataRecord::DEST_OUT)
text += tr("DEST_OUT: %1, %2, %3, %4\n").arg(record.dest_out.x.ToFloat32()).arg(record.dest_out.y.ToFloat32()).arg(record.dest_out.z.ToFloat32()).arg(record.dest_out.w.ToFloat32()); text += tr("DEST_OUT: %1, %2, %3, %4\n")
.arg(record.dest_out.x.ToFloat32())
.arg(record.dest_out.y.ToFloat32())
.arg(record.dest_out.z.ToFloat32())
.arg(record.dest_out.w.ToFloat32());
if (record.mask & Pica::Shader::DebugDataRecord::ADDR_REG_OUT) if (record.mask & Pica::Shader::DebugDataRecord::ADDR_REG_OUT)
text += tr("Addres Registers: %1, %2\n").arg(record.address_registers[0]).arg(record.address_registers[1]); text += tr("Addres Registers: %1, %2\n")
.arg(record.address_registers[0])
.arg(record.address_registers[1]);
if (record.mask & Pica::Shader::DebugDataRecord::CMP_RESULT) if (record.mask & Pica::Shader::DebugDataRecord::CMP_RESULT)
text += tr("Compare Result: %1, %2\n").arg(record.conditional_code[0] ? "true" : "false").arg(record.conditional_code[1] ? "true" : "false"); text += tr("Compare Result: %1, %2\n")
.arg(record.conditional_code[0] ? "true" : "false")
.arg(record.conditional_code[1] ? "true" : "false");
if (record.mask & Pica::Shader::DebugDataRecord::COND_BOOL_IN) if (record.mask & Pica::Shader::DebugDataRecord::COND_BOOL_IN)
text += tr("Static Condition: %1\n").arg(record.cond_bool ? "true" : "false"); text += tr("Static Condition: %1\n").arg(record.cond_bool ? "true" : "false");
if (record.mask & Pica::Shader::DebugDataRecord::COND_CMP_IN) if (record.mask & Pica::Shader::DebugDataRecord::COND_CMP_IN)
text += tr("Dynamic Conditions: %1, %2\n").arg(record.cond_cmp[0] ? "true" : "false").arg(record.cond_cmp[1] ? "true" : "false"); text += tr("Dynamic Conditions: %1, %2\n")
.arg(record.cond_cmp[0] ? "true" : "false")
.arg(record.cond_cmp[1] ? "true" : "false");
if (record.mask & Pica::Shader::DebugDataRecord::LOOP_INT_IN) if (record.mask & Pica::Shader::DebugDataRecord::LOOP_INT_IN)
text += tr("Loop Parameters: %1 (repeats), %2 (initializer), %3 (increment), %4\n").arg(record.loop_int.x).arg(record.loop_int.y).arg(record.loop_int.z).arg(record.loop_int.w); text += tr("Loop Parameters: %1 (repeats), %2 (initializer), %3 (increment), %4\n")
.arg(record.loop_int.x)
.arg(record.loop_int.y)
.arg(record.loop_int.z)
.arg(record.loop_int.w);
text += tr("Instruction offset: 0x%1").arg(4 * record.instruction_offset, 4, 16, QLatin1Char('0')); text +=
tr("Instruction offset: 0x%1").arg(4 * record.instruction_offset, 4, 16, QLatin1Char('0'));
if (record.mask & Pica::Shader::DebugDataRecord::NEXT_INSTR) { if (record.mask & Pica::Shader::DebugDataRecord::NEXT_INSTR) {
text += tr(" -> 0x%2").arg(4 * record.next_instruction, 4, 16, QLatin1Char('0')); text += tr(" -> 0x%2").arg(4 * record.next_instruction, 4, 16, QLatin1Char('0'));
} else { } else {
@ -570,6 +623,7 @@ void GraphicsVertexShaderWidget::OnCycleIndexChanged(int index) {
// Emit model update notification and scroll to current instruction // Emit model update notification and scroll to current instruction
QModelIndex instr_index = model->index(record.instruction_offset, 0); QModelIndex instr_index = model->index(record.instruction_offset, 0);
emit model->dataChanged(instr_index, model->index(record.instruction_offset, model->columnCount())); emit model->dataChanged(instr_index,
model->index(record.instruction_offset, model->columnCount()));
binary_list->scrollTo(instr_index, QAbstractItemView::EnsureVisible); binary_list->scrollTo(instr_index, QAbstractItemView::EnsureVisible);
} }

View File

@ -5,11 +5,9 @@
#pragma once #pragma once
#include <QAbstractTableModel> #include <QAbstractTableModel>
#include <QTreeView>
#include "citra_qt/debugger/graphics_breakpoint_observer.h" #include "citra_qt/debugger/graphics_breakpoint_observer.h"
#include "nihstro/parser_shbin.h" #include "nihstro/parser_shbin.h"
#include "video_core/shader/shader.h" #include "video_core/shader/shader.h"
class QLabel; class QLabel;
@ -26,7 +24,8 @@ public:
int columnCount(const QModelIndex& parent = QModelIndex()) const override; int columnCount(const QModelIndex& parent = QModelIndex()) const override;
int rowCount(const QModelIndex& parent = QModelIndex()) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
private: private:
GraphicsVertexShaderWidget* par; GraphicsVertexShaderWidget* par;
@ -56,7 +55,9 @@ private slots:
/** /**
* Reload widget based on the current PICA200 state * Reload widget based on the current PICA200 state
* @param replace_vertex_data If true, invalidate all current vertex data * @param replace_vertex_data If true, invalidate all current vertex data
* @param vertex_data New vertex data to use, as passed to OnBreakPointHit. May be nullptr to specify that no valid vertex data can be retrieved currently. Only used if replace_vertex_data is true. * @param vertex_data New vertex data to use, as passed to OnBreakPointHit. May be nullptr to
* specify that no valid vertex data can be retrieved currently. Only used if
* replace_vertex_data is true.
*/ */
void Reload(bool replace_vertex_data = false, void* vertex_data = nullptr); void Reload(bool replace_vertex_data = false, void* vertex_data = nullptr);
@ -66,9 +67,12 @@ private:
GraphicsVertexShaderModel* model; GraphicsVertexShaderModel* model;
/// TODO: Move these into a single struct /// TODO: Move these into a single struct
std::array<QLineEdit*, 4*16> input_data; // A text box for each of the 4 components of up to 16 vertex attributes std::array<QLineEdit*, 4 * 16>
std::array<QWidget*, 16> input_data_container; // QWidget containing the QLayout containing each vertex attribute input_data; // A text box for each of the 4 components of up to 16 vertex attributes
std::array<QLabel*, 16> input_data_mapping; // A QLabel denoting the shader input attribute which the vertex attribute maps to std::array<QWidget*, 16>
input_data_container; // QWidget containing the QLayout containing each vertex attribute
std::array<QLabel*, 16> input_data_mapping; // A QLabel denoting the shader input attribute
// which the vertex attribute maps to
// Text to be shown when input vertex data is not retrievable // Text to be shown when input vertex data is not retrievable
QLabel* breakpoint_warning; QLabel* breakpoint_warning;

View File

@ -5,10 +5,8 @@
#include <QMouseEvent> #include <QMouseEvent>
#include <QPainter> #include <QPainter>
#include <QString> #include <QString>
#include "citra_qt/debugger/profiler.h" #include "citra_qt/debugger/profiler.h"
#include "citra_qt/util/util.h" #include "citra_qt/util/util.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/microprofile.h" #include "common/microprofile.h"
#include "common/profiler_reporting.h" #include "common/profiler_reporting.h"
@ -22,57 +20,58 @@
using namespace Common::Profiling; using namespace Common::Profiling;
static QVariant GetDataForColumn(int col, const AggregatedDuration& duration) static QVariant GetDataForColumn(int col, const AggregatedDuration& duration) {
{
static auto duration_to_float = [](Duration dur) -> float { static auto duration_to_float = [](Duration dur) -> float {
using FloatMs = std::chrono::duration<float, std::chrono::milliseconds::period>; using FloatMs = std::chrono::duration<float, std::chrono::milliseconds::period>;
return std::chrono::duration_cast<FloatMs>(dur).count(); return std::chrono::duration_cast<FloatMs>(dur).count();
}; };
switch (col) { switch (col) {
case 1: return duration_to_float(duration.avg); case 1:
case 2: return duration_to_float(duration.min); return duration_to_float(duration.avg);
case 3: return duration_to_float(duration.max); case 2:
default: return QVariant(); return duration_to_float(duration.min);
case 3:
return duration_to_float(duration.max);
default:
return QVariant();
} }
} }
ProfilerModel::ProfilerModel(QObject* parent) : QAbstractItemModel(parent) ProfilerModel::ProfilerModel(QObject* parent) : QAbstractItemModel(parent) {
{
updateProfilingInfo(); updateProfilingInfo();
} }
QVariant ProfilerModel::headerData(int section, Qt::Orientation orientation, int role) const QVariant ProfilerModel::headerData(int section, Qt::Orientation orientation, int role) const {
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
switch (section) { switch (section) {
case 0: return tr("Category"); case 0:
case 1: return tr("Avg"); return tr("Category");
case 2: return tr("Min"); case 1:
case 3: return tr("Max"); return tr("Avg");
case 2:
return tr("Min");
case 3:
return tr("Max");
} }
} }
return QVariant(); return QVariant();
} }
QModelIndex ProfilerModel::index(int row, int column, const QModelIndex& parent) const QModelIndex ProfilerModel::index(int row, int column, const QModelIndex& parent) const {
{
return createIndex(row, column); return createIndex(row, column);
} }
QModelIndex ProfilerModel::parent(const QModelIndex& child) const QModelIndex ProfilerModel::parent(const QModelIndex& child) const {
{
return QModelIndex(); return QModelIndex();
} }
int ProfilerModel::columnCount(const QModelIndex& parent) const int ProfilerModel::columnCount(const QModelIndex& parent) const {
{
return 4; return 4;
} }
int ProfilerModel::rowCount(const QModelIndex& parent) const int ProfilerModel::rowCount(const QModelIndex& parent) const {
{
if (parent.isValid()) { if (parent.isValid()) {
return 0; return 0;
} else { } else {
@ -80,8 +79,7 @@ int ProfilerModel::rowCount(const QModelIndex& parent) const
} }
} }
QVariant ProfilerModel::data(const QModelIndex& index, int role) const QVariant ProfilerModel::data(const QModelIndex& index, int role) const {
{
if (role == Qt::DisplayRole) { if (role == Qt::DisplayRole) {
if (index.row() == 0) { if (index.row() == 0) {
if (index.column() == 0) { if (index.column() == 0) {
@ -101,14 +99,12 @@ QVariant ProfilerModel::data(const QModelIndex& index, int role) const
return QVariant(); return QVariant();
} }
void ProfilerModel::updateProfilingInfo() void ProfilerModel::updateProfilingInfo() {
{
results = GetTimingResultsAggregator()->GetAggregatedResults(); results = GetTimingResultsAggregator()->GetAggregatedResults();
emit dataChanged(createIndex(0, 1), createIndex(rowCount() - 1, 3)); emit dataChanged(createIndex(0, 1), createIndex(rowCount() - 1, 3));
} }
ProfilerWidget::ProfilerWidget(QWidget* parent) : QDockWidget(parent) ProfilerWidget::ProfilerWidget(QWidget* parent) : QDockWidget(parent) {
{
ui.setupUi(this); ui.setupUi(this);
model = new ProfilerModel(this); model = new ProfilerModel(this);
@ -118,8 +114,7 @@ ProfilerWidget::ProfilerWidget(QWidget* parent) : QDockWidget(parent)
connect(&update_timer, SIGNAL(timeout()), model, SLOT(updateProfilingInfo())); connect(&update_timer, SIGNAL(timeout()), model, SLOT(updateProfilingInfo()));
} }
void ProfilerWidget::setProfilingInfoUpdateEnabled(bool enable) void ProfilerWidget::setProfilingInfoUpdateEnabled(bool enable) {
{
if (enable) { if (enable) {
update_timer.start(100); update_timer.start(100);
model->updateProfilingInfo(); model->updateProfilingInfo();
@ -157,9 +152,7 @@ private:
#endif #endif
MicroProfileDialog::MicroProfileDialog(QWidget* parent) MicroProfileDialog::MicroProfileDialog(QWidget* parent) : QWidget(parent, Qt::Dialog) {
: QWidget(parent, Qt::Dialog)
{
setObjectName("MicroProfile"); setObjectName("MicroProfile");
setWindowTitle(tr("MicroProfile")); setWindowTitle(tr("MicroProfile"));
resize(1000, 600); resize(1000, 600);
@ -175,7 +168,8 @@ MicroProfileDialog::MicroProfileDialog(QWidget* parent)
layout->addWidget(widget); layout->addWidget(widget);
setLayout(layout); setLayout(layout);
// Configure focus so that widget is focusable and the dialog automatically forwards focus to it. // Configure focus so that widget is focusable and the dialog automatically forwards focus to
// it.
setFocusProxy(widget); setFocusProxy(widget);
widget->setFocusPolicy(Qt::StrongFocus); widget->setFocusPolicy(Qt::StrongFocus);
widget->setFocus(); widget->setFocus();
@ -207,7 +201,6 @@ void MicroProfileDialog::hideEvent(QHideEvent* ev) {
QWidget::hideEvent(ev); QWidget::hideEvent(ev);
} }
#if MICROPROFILE_ENABLED #if MICROPROFILE_ENABLED
/// There's no way to pass a user pointer to MicroProfile, so this variable is used to make the /// There's no way to pass a user pointer to MicroProfile, so this variable is used to make the
@ -308,7 +301,8 @@ void MicroProfileDrawText(int x, int y, u32 hex_color, const char* text, u32 tex
} }
} }
void MicroProfileDrawBox(int left, int top, int right, int bottom, u32 hex_color, MicroProfileBoxType type) { void MicroProfileDrawBox(int left, int top, int right, int bottom, u32 hex_color,
MicroProfileBoxType type) {
QColor color = QColor::fromRgba(hex_color); QColor color = QColor::fromRgba(hex_color);
QBrush brush = color; QBrush brush = color;
if (type == MicroProfileBoxTypeBar) { if (type == MicroProfileBoxTypeBar) {
@ -326,7 +320,7 @@ void MicroProfileDrawLine2D(u32 vertices_length, float* vertices, u32 hex_color)
static std::vector<QPointF> point_buf; static std::vector<QPointF> point_buf;
for (u32 i = 0; i < vertices_length; ++i) { for (u32 i = 0; i < vertices_length; ++i) {
point_buf.emplace_back(vertices[i*2 + 0], vertices[i*2 + 1]); point_buf.emplace_back(vertices[i * 2 + 0], vertices[i * 2 + 1]);
} }
// hex_color does not include an alpha, so it must be assumed to be 255 // hex_color does not include an alpha, so it must be assumed to be 255

View File

@ -7,21 +7,20 @@
#include <QAbstractItemModel> #include <QAbstractItemModel>
#include <QDockWidget> #include <QDockWidget>
#include <QTimer> #include <QTimer>
#include "ui_profiler.h"
#include "common/microprofile.h" #include "common/microprofile.h"
#include "common/profiler_reporting.h" #include "common/profiler_reporting.h"
#include "ui_profiler.h"
class ProfilerModel : public QAbstractItemModel class ProfilerModel : public QAbstractItemModel {
{
Q_OBJECT Q_OBJECT
public: public:
ProfilerModel(QObject* parent); ProfilerModel(QObject* parent);
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation,
QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; int role = Qt::DisplayRole) const override;
QModelIndex index(int row, int column,
const QModelIndex& parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex& child) const override; QModelIndex parent(const QModelIndex& child) const override;
int columnCount(const QModelIndex& parent = QModelIndex()) const override; int columnCount(const QModelIndex& parent = QModelIndex()) const override;
int rowCount(const QModelIndex& parent = QModelIndex()) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override;
@ -34,8 +33,7 @@ private:
Common::Profiling::AggregatedFrameResult results; Common::Profiling::AggregatedFrameResult results;
}; };
class ProfilerWidget : public QDockWidget class ProfilerWidget : public QDockWidget {
{
Q_OBJECT Q_OBJECT
public: public:
@ -51,7 +49,6 @@ private:
QTimer update_timer; QTimer update_timer;
}; };
class MicroProfileDialog : public QWidget { class MicroProfileDialog : public QWidget {
Q_OBJECT Q_OBJECT

View File

@ -4,12 +4,9 @@
#include "citra_qt/debugger/ramview.h" #include "citra_qt/debugger/ramview.h"
GRamView::GRamView(QWidget* parent) : QHexEdit(parent) GRamView::GRamView(QWidget* parent) : QHexEdit(parent) {}
{
}
void GRamView::OnCPUStepped() void GRamView::OnCPUStepped() {
{
// TODO: QHexEdit doesn't show vertical scroll bars for > 10MB data streams... // TODO: QHexEdit doesn't show vertical scroll bars for > 10MB data streams...
//setData(QByteArray((const char*)Mem_RAM,sizeof(Mem_RAM)/8)); // setData(QByteArray((const char*)Mem_RAM,sizeof(Mem_RAM)/8));
} }

View File

@ -4,8 +4,7 @@
#include "qhexedit.h" #include "qhexedit.h"
class GRamView : public QHexEdit class GRamView : public QHexEdit {
{
Q_OBJECT Q_OBJECT
public: public:

View File

@ -3,12 +3,10 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <QTreeWidgetItem> #include <QTreeWidgetItem>
#include "citra_qt/debugger/registers.h" #include "citra_qt/debugger/registers.h"
#include "citra_qt/util/util.h" #include "citra_qt/util/util.h"
#include "core/core.h"
#include "core/arm/arm_interface.h" #include "core/arm/arm_interface.h"
#include "core/core.h"
RegistersWidget::RegistersWidget(QWidget* parent) : QDockWidget(parent) { RegistersWidget::RegistersWidget(QWidget* parent) : QDockWidget(parent) {
cpu_regs_ui.setupUi(this); cpu_regs_ui.setupUi(this);
@ -16,7 +14,8 @@ RegistersWidget::RegistersWidget(QWidget* parent) : QDockWidget(parent) {
tree = cpu_regs_ui.treeWidget; tree = cpu_regs_ui.treeWidget;
tree->addTopLevelItem(core_registers = new QTreeWidgetItem(QStringList(tr("Registers")))); tree->addTopLevelItem(core_registers = new QTreeWidgetItem(QStringList(tr("Registers"))));
tree->addTopLevelItem(vfp_registers = new QTreeWidgetItem(QStringList(tr("VFP Registers")))); tree->addTopLevelItem(vfp_registers = new QTreeWidgetItem(QStringList(tr("VFP Registers"))));
tree->addTopLevelItem(vfp_system_registers = new QTreeWidgetItem(QStringList(tr("VFP System Registers")))); tree->addTopLevelItem(vfp_system_registers =
new QTreeWidgetItem(QStringList(tr("VFP System Registers"))));
tree->addTopLevelItem(cpsr = new QTreeWidgetItem(QStringList("CPSR"))); tree->addTopLevelItem(cpsr = new QTreeWidgetItem(QStringList("CPSR")));
for (int i = 0; i < 16; ++i) { for (int i = 0; i < 16; ++i) {
@ -63,17 +62,18 @@ void RegistersWidget::OnDebugModeEntered() {
return; return;
for (int i = 0; i < core_registers->childCount(); ++i) for (int i = 0; i < core_registers->childCount(); ++i)
core_registers->child(i)->setText(1, QString("0x%1").arg(Core::g_app_core->GetReg(i), 8, 16, QLatin1Char('0'))); core_registers->child(i)->setText(
1, QString("0x%1").arg(Core::g_app_core->GetReg(i), 8, 16, QLatin1Char('0')));
for (int i = 0; i < vfp_registers->childCount(); ++i) for (int i = 0; i < vfp_registers->childCount(); ++i)
vfp_registers->child(i)->setText(1, QString("0x%1").arg(Core::g_app_core->GetVFPReg(i), 8, 16, QLatin1Char('0'))); vfp_registers->child(i)->setText(
1, QString("0x%1").arg(Core::g_app_core->GetVFPReg(i), 8, 16, QLatin1Char('0')));
UpdateCPSRValues(); UpdateCPSRValues();
UpdateVFPSystemRegisterValues(); UpdateVFPSystemRegisterValues();
} }
void RegistersWidget::OnDebugModeLeft() { void RegistersWidget::OnDebugModeLeft() {}
}
void RegistersWidget::OnEmulationStarting(EmuThread* emu_thread) { void RegistersWidget::OnEmulationStarting(EmuThread* emu_thread) {
setEnabled(true); setEnabled(true);
@ -130,14 +130,17 @@ void RegistersWidget::UpdateCPSRValues() {
const u32 cpsr_val = Core::g_app_core->GetCPSR(); const u32 cpsr_val = Core::g_app_core->GetCPSR();
cpsr->setText(1, QString("0x%1").arg(cpsr_val, 8, 16, QLatin1Char('0'))); cpsr->setText(1, QString("0x%1").arg(cpsr_val, 8, 16, QLatin1Char('0')));
cpsr->child(0)->setText(1, QString("b%1").arg(cpsr_val & 0x1F, 5, 2, QLatin1Char('0'))); // M - Mode cpsr->child(0)->setText(
1, QString("b%1").arg(cpsr_val & 0x1F, 5, 2, QLatin1Char('0'))); // M - Mode
cpsr->child(1)->setText(1, QString::number((cpsr_val >> 5) & 1)); // T - State cpsr->child(1)->setText(1, QString::number((cpsr_val >> 5) & 1)); // T - State
cpsr->child(2)->setText(1, QString::number((cpsr_val >> 6) & 1)); // F - FIQ disable cpsr->child(2)->setText(1, QString::number((cpsr_val >> 6) & 1)); // F - FIQ disable
cpsr->child(3)->setText(1, QString::number((cpsr_val >> 7) & 1)); // I - IRQ disable cpsr->child(3)->setText(1, QString::number((cpsr_val >> 7) & 1)); // I - IRQ disable
cpsr->child(4)->setText(1, QString::number((cpsr_val >> 8) & 1)); // A - Imprecise abort cpsr->child(4)->setText(1, QString::number((cpsr_val >> 8) & 1)); // A - Imprecise abort
cpsr->child(5)->setText(1, QString::number((cpsr_val >> 9) & 1)); // E - Data endianess cpsr->child(5)->setText(1, QString::number((cpsr_val >> 9) & 1)); // E - Data endianess
cpsr->child(6)->setText(1, QString::number((cpsr_val >> 10) & 0x3F)); // IT - If-Then state (DNM) cpsr->child(6)->setText(1,
cpsr->child(7)->setText(1, QString::number((cpsr_val >> 16) & 0xF)); // GE - Greater-than-or-Equal QString::number((cpsr_val >> 10) & 0x3F)); // IT - If-Then state (DNM)
cpsr->child(7)->setText(1,
QString::number((cpsr_val >> 16) & 0xF)); // GE - Greater-than-or-Equal
cpsr->child(8)->setText(1, QString::number((cpsr_val >> 20) & 0xF)); // DNM - Do not modify cpsr->child(8)->setText(1, QString::number((cpsr_val >> 20) & 0xF)); // DNM - Do not modify
cpsr->child(9)->setText(1, QString::number((cpsr_val >> 24) & 1)); // J - Jazelle cpsr->child(9)->setText(1, QString::number((cpsr_val >> 24) & 1)); // J - Jazelle
cpsr->child(10)->setText(1, QString::number((cpsr_val >> 27) & 1)); // Q - Saturation cpsr->child(10)->setText(1, QString::number((cpsr_val >> 27) & 1)); // Q - Saturation
@ -228,6 +231,8 @@ void RegistersWidget::UpdateVFPSystemRegisterValues() {
fpexc->child(6)->setText(1, QString::number((fpexc_val >> 30) & 1)); fpexc->child(6)->setText(1, QString::number((fpexc_val >> 30) & 1));
fpexc->child(7)->setText(1, QString::number((fpexc_val >> 31) & 1)); fpexc->child(7)->setText(1, QString::number((fpexc_val >> 31) & 1));
vfp_system_registers->child(2)->setText(1, QString("0x%1").arg(fpinst_val, 8, 16, QLatin1Char('0'))); vfp_system_registers->child(2)->setText(
vfp_system_registers->child(3)->setText(1, QString("0x%1").arg(fpinst2_val, 8, 16, QLatin1Char('0'))); 1, QString("0x%1").arg(fpinst_val, 8, 16, QLatin1Char('0')));
vfp_system_registers->child(3)->setText(
1, QString("0x%1").arg(fpinst2_val, 8, 16, QLatin1Char('0')));
} }

View File

@ -2,16 +2,14 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include "ui_registers.h"
#include <QDockWidget> #include <QDockWidget>
#include "ui_registers.h"
class QTreeWidget; class QTreeWidget;
class QTreeWidgetItem; class QTreeWidgetItem;
class EmuThread; class EmuThread;
class RegistersWidget : public QDockWidget class RegistersWidget : public QDockWidget {
{
Q_OBJECT Q_OBJECT
public: public:

View File

@ -5,19 +5,15 @@
#include <QHeaderView> #include <QHeaderView>
#include <QThreadPool> #include <QThreadPool>
#include <QVBoxLayout> #include <QVBoxLayout>
#include "common/common_paths.h"
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/loader/loader.h"
#include "game_list.h" #include "game_list.h"
#include "game_list_p.h" #include "game_list_p.h"
#include "ui_settings.h" #include "ui_settings.h"
#include "core/loader/loader.h" GameList::GameList(QWidget* parent) {
#include "common/common_paths.h"
#include "common/logging/log.h"
#include "common/string_util.h"
GameList::GameList(QWidget* parent)
{
QVBoxLayout* layout = new QVBoxLayout; QVBoxLayout* layout = new QVBoxLayout;
tree_view = new QTreeView; tree_view = new QTreeView;
@ -38,9 +34,11 @@ GameList::GameList(QWidget* parent)
item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, "File type"); item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, "File type");
item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "Size"); item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "Size");
connect(tree_view, SIGNAL(activated(const QModelIndex&)), this, SLOT(ValidateEntry(const QModelIndex&))); connect(tree_view, SIGNAL(activated(const QModelIndex&)), this,
SLOT(ValidateEntry(const QModelIndex&)));
// We must register all custom types with the Qt Automoc system so that we are able to use it with // We must register all custom types with the Qt Automoc system so that we are able to use it
// with
// signals/slots. In this case, QList falls under the umbrells of custom types. // signals/slots. In this case, QList falls under the umbrells of custom types.
qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>"); qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>");
@ -48,18 +46,15 @@ GameList::GameList(QWidget* parent)
setLayout(layout); setLayout(layout);
} }
GameList::~GameList() GameList::~GameList() {
{
emit ShouldCancelWorker(); emit ShouldCancelWorker();
} }
void GameList::AddEntry(QList<QStandardItem*> entry_items) void GameList::AddEntry(QList<QStandardItem*> entry_items) {
{
item_model->invisibleRootItem()->appendRow(entry_items); item_model->invisibleRootItem()->appendRow(entry_items);
} }
void GameList::ValidateEntry(const QModelIndex& item) void GameList::ValidateEntry(const QModelIndex& item) {
{
// We don't care about the individual QStandardItem that was selected, but its row. // We don't care about the individual QStandardItem that was selected, but its row.
int row = item_model->itemFromIndex(item)->row(); int row = item_model->itemFromIndex(item)->row();
QStandardItem* child_file = item_model->invisibleRootItem()->child(row, COLUMN_NAME); QStandardItem* child_file = item_model->invisibleRootItem()->child(row, COLUMN_NAME);
@ -73,14 +68,13 @@ void GameList::ValidateEntry(const QModelIndex& item)
emit GameChosen(file_path); emit GameChosen(file_path);
} }
void GameList::DonePopulating() void GameList::DonePopulating() {
{
tree_view->setEnabled(true); tree_view->setEnabled(true);
} }
void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) {
{ if (!FileUtil::Exists(dir_path.toStdString()) ||
if (!FileUtil::Exists(dir_path.toStdString()) || !FileUtil::IsDirectory(dir_path.toStdString())) { !FileUtil::IsDirectory(dir_path.toStdString())) {
LOG_ERROR(Frontend, "Could not find game list folder at %s", dir_path.toLocal8Bit().data()); LOG_ERROR(Frontend, "Could not find game list folder at %s", dir_path.toLocal8Bit().data());
return; return;
} }
@ -92,22 +86,22 @@ void GameList::PopulateAsync(const QString& dir_path, bool deep_scan)
emit ShouldCancelWorker(); emit ShouldCancelWorker();
GameListWorker* worker = new GameListWorker(dir_path, deep_scan); GameListWorker* worker = new GameListWorker(dir_path, deep_scan);
connect(worker, SIGNAL(EntryReady(QList<QStandardItem*>)), this, SLOT(AddEntry(QList<QStandardItem*>)), Qt::QueuedConnection); connect(worker, SIGNAL(EntryReady(QList<QStandardItem*>)), this,
SLOT(AddEntry(QList<QStandardItem*>)), Qt::QueuedConnection);
connect(worker, SIGNAL(Finished()), this, SLOT(DonePopulating()), Qt::QueuedConnection); connect(worker, SIGNAL(Finished()), this, SLOT(DonePopulating()), Qt::QueuedConnection);
// Use DirectConnection here because worker->Cancel() is thread-safe and we want it to cancel without delay. // Use DirectConnection here because worker->Cancel() is thread-safe and we want it to cancel
// without delay.
connect(this, SIGNAL(ShouldCancelWorker()), worker, SLOT(Cancel()), Qt::DirectConnection); connect(this, SIGNAL(ShouldCancelWorker()), worker, SLOT(Cancel()), Qt::DirectConnection);
QThreadPool::globalInstance()->start(worker); QThreadPool::globalInstance()->start(worker);
current_worker = std::move(worker); current_worker = std::move(worker);
} }
void GameList::SaveInterfaceLayout() void GameList::SaveInterfaceLayout() {
{
UISettings::values.gamelist_header_state = tree_view->header()->saveState(); UISettings::values.gamelist_header_state = tree_view->header()->saveState();
} }
void GameList::LoadInterfaceLayout() void GameList::LoadInterfaceLayout() {
{
auto header = tree_view->header(); auto header = tree_view->header();
if (!header->restoreState(UISettings::values.gamelist_header_state)) { if (!header->restoreState(UISettings::values.gamelist_header_state)) {
// We are using the name column to display icons and titles // We are using the name column to display icons and titles
@ -118,10 +112,8 @@ void GameList::LoadInterfaceLayout()
item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder()); item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder());
} }
void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) {
{ const auto callback = [this, recursion](unsigned* num_entries_out, const std::string& directory,
const auto callback = [this, recursion](unsigned* num_entries_out,
const std::string& directory,
const std::string& virtual_name) -> bool { const std::string& virtual_name) -> bool {
std::string physical_name = directory + DIR_SEP + virtual_name; std::string physical_name = directory + DIR_SEP + virtual_name;
@ -138,7 +130,8 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
emit EntryReady({ emit EntryReady({
new GameListItemPath(QString::fromStdString(physical_name), smdh), new GameListItemPath(QString::fromStdString(physical_name), smdh),
new GameListItem(QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), new GameListItem(
QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
new GameListItemSize(FileUtil::GetSize(physical_name)), new GameListItemSize(FileUtil::GetSize(physical_name)),
}); });
} else if (recursion > 0) { } else if (recursion > 0) {
@ -151,15 +144,13 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
FileUtil::ForeachDirectoryEntry(nullptr, dir_path, callback); FileUtil::ForeachDirectoryEntry(nullptr, dir_path, callback);
} }
void GameListWorker::run() void GameListWorker::run() {
{
stop_processing = false; stop_processing = false;
AddFstEntriesToGameList(dir_path.toStdString(), deep_scan ? 256 : 0); AddFstEntriesToGameList(dir_path.toStdString(), deep_scan ? 256 : 0);
emit Finished(); emit Finished();
} }
void GameListWorker::Cancel() void GameListWorker::Cancel() {
{
disconnect(this, 0, 0, 0); disconnect(this, 0, 0, 0);
stop_processing = true; stop_processing = true;
} }

View File

@ -14,7 +14,6 @@
class GameListWorker; class GameListWorker;
class GameList : public QWidget { class GameList : public QWidget {
Q_OBJECT Q_OBJECT

View File

@ -5,18 +5,14 @@
#pragma once #pragma once
#include <atomic> #include <atomic>
#include <QImage> #include <QImage>
#include <QRunnable> #include <QRunnable>
#include <QStandardItem> #include <QStandardItem>
#include <QString> #include <QString>
#include "citra_qt/util/util.h" #include "citra_qt/util/util.h"
#include "common/string_util.h"
#include "common/color.h" #include "common/color.h"
#include "common/string_util.h"
#include "core/loader/smdh.h" #include "core/loader/smdh.h"
#include "video_core/utils.h" #include "video_core/utils.h"
/** /**
@ -51,19 +47,19 @@ static QPixmap GetDefaultIcon(bool large) {
* @param language title language * @param language title language
* @return QString short title * @return QString short title
*/ */
static QString GetQStringShortTitleFromSMDH(const Loader::SMDH& smdh, Loader::SMDH::TitleLanguage language) { static QString GetQStringShortTitleFromSMDH(const Loader::SMDH& smdh,
Loader::SMDH::TitleLanguage language) {
return QString::fromUtf16(smdh.GetShortTitle(language).data()); return QString::fromUtf16(smdh.GetShortTitle(language).data());
} }
class GameListItem : public QStandardItem { class GameListItem : public QStandardItem {
public: public:
GameListItem(): QStandardItem() {} GameListItem() : QStandardItem() {}
GameListItem(const QString& string): QStandardItem(string) {} GameListItem(const QString& string) : QStandardItem(string) {}
virtual ~GameListItem() override {} virtual ~GameListItem() override {}
}; };
/** /**
* A specialization of GameListItem for path values. * A specialization of GameListItem for path values.
* This class ensures that for every full path value it holds, a correct string representation * This class ensures that for every full path value it holds, a correct string representation
@ -76,9 +72,8 @@ public:
static const int FullPathRole = Qt::UserRole + 1; static const int FullPathRole = Qt::UserRole + 1;
static const int TitleRole = Qt::UserRole + 2; static const int TitleRole = Qt::UserRole + 2;
GameListItemPath(): GameListItem() {} GameListItemPath() : GameListItem() {}
GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data): GameListItem() GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data) : GameListItem() {
{
setData(game_path, FullPathRole); setData(game_path, FullPathRole);
if (!Loader::IsValidSMDH(smdh_data)) { if (!Loader::IsValidSMDH(smdh_data)) {
@ -94,13 +89,15 @@ public:
setData(GetQPixmapFromSMDH(smdh, true), Qt::DecorationRole); setData(GetQPixmapFromSMDH(smdh, true), Qt::DecorationRole);
// Get title form SMDH // Get title form SMDH
setData(GetQStringShortTitleFromSMDH(smdh, Loader::SMDH::TitleLanguage::English), TitleRole); setData(GetQStringShortTitleFromSMDH(smdh, Loader::SMDH::TitleLanguage::English),
TitleRole);
} }
QVariant data(int role) const override { QVariant data(int role) const override {
if (role == Qt::DisplayRole) { if (role == Qt::DisplayRole) {
std::string filename; std::string filename;
Common::SplitPath(data(FullPathRole).toString().toStdString(), nullptr, &filename, nullptr); Common::SplitPath(data(FullPathRole).toString().toStdString(), nullptr, &filename,
nullptr);
QString title = data(TitleRole).toString(); QString title = data(TitleRole).toString();
return QString::fromStdString(filename) + (title.isEmpty() ? "" : "\n " + title); return QString::fromStdString(filename) + (title.isEmpty() ? "" : "\n " + title);
} else { } else {
@ -109,7 +106,6 @@ public:
} }
}; };
/** /**
* A specialization of GameListItem for size values. * A specialization of GameListItem for size values.
* This class ensures that for every numerical size value it holds (in bytes), a correct * This class ensures that for every numerical size value it holds (in bytes), a correct
@ -120,14 +116,12 @@ class GameListItemSize : public GameListItem {
public: public:
static const int SizeRole = Qt::UserRole + 1; static const int SizeRole = Qt::UserRole + 1;
GameListItemSize(): GameListItem() {} GameListItemSize() : GameListItem() {}
GameListItemSize(const qulonglong size_bytes): GameListItem() GameListItemSize(const qulonglong size_bytes) : GameListItem() {
{
setData(size_bytes, SizeRole); setData(size_bytes, SizeRole);
} }
void setData(const QVariant& value, int role) override void setData(const QVariant& value, int role) override {
{
// By specializing setData for SizeRole, we can ensure that the numerical and string // By specializing setData for SizeRole, we can ensure that the numerical and string
// representations of the data are always accurate and in the correct format. // representations of the data are always accurate and in the correct format.
if (role == SizeRole) { if (role == SizeRole) {
@ -141,15 +135,14 @@ public:
/** /**
* This operator is, in practice, only used by the TreeView sorting systems. * This operator is, in practice, only used by the TreeView sorting systems.
* Override it so that it will correctly sort by numerical value instead of by string representation. * Override it so that it will correctly sort by numerical value instead of by string
* representation.
*/ */
bool operator<(const QStandardItem& other) const override bool operator<(const QStandardItem& other) const override {
{
return data(SizeRole).toULongLong() < other.data(SizeRole).toULongLong(); return data(SizeRole).toULongLong() < other.data(SizeRole).toULongLong();
} }
}; };
/** /**
* Asynchronous worker object for populating the game list. * Asynchronous worker object for populating the game list.
* Communicates with other threads through Qt's signal/slot system. * Communicates with other threads through Qt's signal/slot system.
@ -158,8 +151,8 @@ class GameListWorker : public QObject, public QRunnable {
Q_OBJECT Q_OBJECT
public: public:
GameListWorker(QString dir_path, bool deep_scan): GameListWorker(QString dir_path, bool deep_scan)
QObject(), QRunnable(), dir_path(dir_path), deep_scan(deep_scan) {} : QObject(), QRunnable(), dir_path(dir_path), deep_scan(deep_scan) {}
public slots: public slots:
/// Starts the processing of directory tree information. /// Starts the processing of directory tree information.

View File

@ -3,16 +3,13 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <map> #include <map>
#include <QtGlobal>
#include <QKeySequence> #include <QKeySequence>
#include <QShortcut> #include <QShortcut>
#include <QtGlobal>
#include "citra_qt/hotkeys.h" #include "citra_qt/hotkeys.h"
#include "citra_qt/ui_settings.h" #include "citra_qt/ui_settings.h"
struct Hotkey struct Hotkey {
{
Hotkey() : shortcut(nullptr), context(Qt::WindowShortcut) {} Hotkey() : shortcut(nullptr), context(Qt::WindowShortcut) {}
QKeySequence keyseq; QKeySequence keyseq;
@ -25,13 +22,10 @@ typedef std::map<QString, HotkeyMap> HotkeyGroupMap;
HotkeyGroupMap hotkey_groups; HotkeyGroupMap hotkey_groups;
void SaveHotkeys() void SaveHotkeys() {
{
UISettings::values.shortcuts.clear(); UISettings::values.shortcuts.clear();
for (auto group : hotkey_groups) for (auto group : hotkey_groups) {
{ for (auto hotkey : group.second) {
for (auto hotkey : group.second)
{
UISettings::values.shortcuts.emplace_back( UISettings::values.shortcuts.emplace_back(
UISettings::Shortcut(group.first + "/" + hotkey.first, UISettings::Shortcut(group.first + "/" + hotkey.first,
UISettings::ContextualShortcut(hotkey.second.keyseq.toString(), UISettings::ContextualShortcut(hotkey.second.keyseq.toString(),
@ -40,18 +34,16 @@ void SaveHotkeys()
} }
} }
void LoadHotkeys() void LoadHotkeys() {
{ // Make sure NOT to use a reference here because it would become invalid once we call
// Make sure NOT to use a reference here because it would become invalid once we call beginGroup() // beginGroup()
for (auto shortcut : UISettings::values.shortcuts) for (auto shortcut : UISettings::values.shortcuts) {
{
QStringList cat = shortcut.first.split("/"); QStringList cat = shortcut.first.split("/");
Q_ASSERT(cat.size() >= 2); Q_ASSERT(cat.size() >= 2);
// RegisterHotkey assigns default keybindings, so use old values as default parameters // RegisterHotkey assigns default keybindings, so use old values as default parameters
Hotkey& hk = hotkey_groups[cat[0]][cat[1]]; Hotkey& hk = hotkey_groups[cat[0]][cat[1]];
if (!shortcut.second.first.isEmpty()) if (!shortcut.second.first.isEmpty()) {
{
hk.keyseq = QKeySequence::fromString(shortcut.second.first); hk.keyseq = QKeySequence::fromString(shortcut.second.first);
hk.context = (Qt::ShortcutContext)shortcut.second.second; hk.context = (Qt::ShortcutContext)shortcut.second.second;
} }
@ -60,17 +52,15 @@ void LoadHotkeys()
} }
} }
void RegisterHotkey(const QString& group, const QString& action, const QKeySequence& default_keyseq, Qt::ShortcutContext default_context) void RegisterHotkey(const QString& group, const QString& action, const QKeySequence& default_keyseq,
{ Qt::ShortcutContext default_context) {
if (hotkey_groups[group].find(action) == hotkey_groups[group].end()) if (hotkey_groups[group].find(action) == hotkey_groups[group].end()) {
{
hotkey_groups[group][action].keyseq = default_keyseq; hotkey_groups[group][action].keyseq = default_keyseq;
hotkey_groups[group][action].context = default_context; hotkey_groups[group][action].context = default_context;
} }
} }
QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget) QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget) {
{
Hotkey& hk = hotkey_groups[group][action]; Hotkey& hk = hotkey_groups[group][action];
if (!hk.shortcut) if (!hk.shortcut)
@ -79,16 +69,12 @@ QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widge
return hk.shortcut; return hk.shortcut;
} }
GHotkeysDialog::GHotkeysDialog(QWidget* parent) : QWidget(parent) {
GHotkeysDialog::GHotkeysDialog(QWidget* parent): QWidget(parent)
{
ui.setupUi(this); ui.setupUi(this);
for (auto group : hotkey_groups) for (auto group : hotkey_groups) {
{
QTreeWidgetItem* toplevel_item = new QTreeWidgetItem(QStringList(group.first)); QTreeWidgetItem* toplevel_item = new QTreeWidgetItem(QStringList(group.first));
for (auto hotkey : group.second) for (auto hotkey : group.second) {
{
QStringList columns; QStringList columns;
columns << hotkey.first << hotkey.second.keyseq.toString(); columns << hotkey.first << hotkey.second.keyseq.toString();
QTreeWidgetItem* item = new QTreeWidgetItem(columns); QTreeWidgetItem* item = new QTreeWidgetItem(columns);

View File

@ -16,36 +16,42 @@ class QShortcut;
* *
* @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger") * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger")
* @param action Name of the action (e.g. "Start Emulation", "Load Image") * @param action Name of the action (e.g. "Start Emulation", "Load Image")
* @param default_keyseq Default key sequence to assign if the hotkey wasn't present in the settings file before * @param default_keyseq Default key sequence to assign if the hotkey wasn't present in the settings
* @param default_context Default context to assign if the hotkey wasn't present in the settings file before * file before
* @param default_context Default context to assign if the hotkey wasn't present in the settings
* file before
* @warning Both the group and action strings will be displayed in the hotkey settings dialog * @warning Both the group and action strings will be displayed in the hotkey settings dialog
*/ */
void RegisterHotkey(const QString& group, const QString& action, const QKeySequence& default_keyseq = QKeySequence(), Qt::ShortcutContext default_context = Qt::WindowShortcut); void RegisterHotkey(const QString& group, const QString& action,
const QKeySequence& default_keyseq = QKeySequence(),
Qt::ShortcutContext default_context = Qt::WindowShortcut);
/** /**
* Returns a QShortcut object whose activated() signal can be connected to other QObjects' slots. * Returns a QShortcut object whose activated() signal can be connected to other QObjects' slots.
* *
* @param widget Parent widget of the returned QShortcut. * @param widget Parent widget of the returned QShortcut.
* @warning If multiple QWidgets' call this function for the same action, the returned QShortcut will be the same. Thus, you shouldn't rely on the caller really being the QShortcut's parent. * @warning If multiple QWidgets' call this function for the same action, the returned QShortcut
* will be the same. Thus, you shouldn't rely on the caller really being the QShortcut's parent.
*/ */
QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget); QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget);
/** /**
* Saves all registered hotkeys to the settings file. * Saves all registered hotkeys to the settings file.
* *
* @note Each hotkey group will be stored a settings group; For each hotkey inside that group, a settings group will be created to store the key sequence and the hotkey context. * @note Each hotkey group will be stored a settings group; For each hotkey inside that group, a
* settings group will be created to store the key sequence and the hotkey context.
*/ */
void SaveHotkeys(); void SaveHotkeys();
/** /**
* Loads hotkeys from the settings file. * Loads hotkeys from the settings file.
* *
* @note Yet unregistered hotkeys which are present in the settings will automatically be registered. * @note Yet unregistered hotkeys which are present in the settings will automatically be
* registered.
*/ */
void LoadHotkeys(); void LoadHotkeys();
class GHotkeysDialog : public QWidget class GHotkeysDialog : public QWidget {
{
Q_OBJECT Q_OBJECT
public: public:

View File

@ -5,25 +5,15 @@
#include <clocale> #include <clocale>
#include <memory> #include <memory>
#include <thread> #include <thread>
#include <glad/glad.h> #include <glad/glad.h>
#define QT_NO_OPENGL #define QT_NO_OPENGL
#include <QDesktopWidget> #include <QDesktopWidget>
#include <QtGui>
#include <QFileDialog> #include <QFileDialog>
#include <QMessageBox> #include <QMessageBox>
#include "qhexedit.h" #include <QtGui>
#include "citra_qt/bootmanager.h" #include "citra_qt/bootmanager.h"
#include "citra_qt/config.h" #include "citra_qt/config.h"
#include "citra_qt/configure_dialog.h" #include "citra_qt/configure_dialog.h"
#include "citra_qt/game_list.h"
#include "citra_qt/hotkeys.h"
#include "citra_qt/main.h"
#include "citra_qt/ui_settings.h"
// Debugger
#include "citra_qt/debugger/callstack.h" #include "citra_qt/debugger/callstack.h"
#include "citra_qt/debugger/disassembler.h" #include "citra_qt/debugger/disassembler.h"
#include "citra_qt/debugger/graphics.h" #include "citra_qt/debugger/graphics.h"
@ -35,28 +25,29 @@
#include "citra_qt/debugger/profiler.h" #include "citra_qt/debugger/profiler.h"
#include "citra_qt/debugger/ramview.h" #include "citra_qt/debugger/ramview.h"
#include "citra_qt/debugger/registers.h" #include "citra_qt/debugger/registers.h"
#include "citra_qt/game_list.h"
#include "citra_qt/hotkeys.h"
#include "citra_qt/main.h"
#include "citra_qt/ui_settings.h"
#include "common/logging/backend.h"
#include "common/logging/filter.h"
#include "common/logging/log.h"
#include "common/logging/text_formatter.h"
#include "common/microprofile.h" #include "common/microprofile.h"
#include "common/platform.h" #include "common/platform.h"
#include "common/scm_rev.h" #include "common/scm_rev.h"
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "common/string_util.h" #include "common/string_util.h"
#include "common/logging/backend.h"
#include "common/logging/filter.h"
#include "common/logging/log.h"
#include "common/logging/text_formatter.h"
#include "core/core.h"
#include "core/settings.h"
#include "core/system.h"
#include "core/arm/disassembler/load_symbol_map.h" #include "core/arm/disassembler/load_symbol_map.h"
#include "core/core.h"
#include "core/gdbstub/gdbstub.h" #include "core/gdbstub/gdbstub.h"
#include "core/loader/loader.h" #include "core/loader/loader.h"
#include "core/settings.h"
#include "core/system.h"
#include "qhexedit.h"
#include "video_core/video_core.h" #include "video_core/video_core.h"
GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
{
Pica::g_debug_context = Pica::DebugContext::Construct(); Pica::g_debug_context = Pica::DebugContext::Construct();
ui.setupUi(this); ui.setupUi(this);
@ -91,7 +82,7 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr)
graphicsWidget = new GPUCommandStreamWidget(this); graphicsWidget = new GPUCommandStreamWidget(this);
addDockWidget(Qt::RightDockWidgetArea, graphicsWidget); addDockWidget(Qt::RightDockWidgetArea, graphicsWidget);
graphicsWidget ->hide(); graphicsWidget->hide();
graphicsCommandsWidget = new GPUCommandListWidget(this); graphicsCommandsWidget = new GPUCommandListWidget(this);
addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget); addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget);
@ -110,7 +101,8 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr)
graphicsTracingWidget->hide(); graphicsTracingWidget->hide();
auto graphicsSurfaceViewerAction = new QAction(tr("Create Pica surface viewer"), this); auto graphicsSurfaceViewerAction = new QAction(tr("Create Pica surface viewer"), this);
connect(graphicsSurfaceViewerAction, SIGNAL(triggered()), this, SLOT(OnCreateGraphicsSurfaceViewer())); connect(graphicsSurfaceViewerAction, SIGNAL(triggered()), this,
SLOT(OnCreateGraphicsSurfaceViewer()));
QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging")); QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging"));
debug_menu->addAction(graphicsSurfaceViewerAction); debug_menu->addAction(graphicsSurfaceViewerAction);
@ -167,35 +159,44 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr)
UpdateRecentFiles(); UpdateRecentFiles();
// Setup connections // Setup connections
connect(game_list, SIGNAL(GameChosen(QString)), this, SLOT(OnGameListLoadFile(QString)), Qt::DirectConnection); connect(game_list, SIGNAL(GameChosen(QString)), this, SLOT(OnGameListLoadFile(QString)),
Qt::DirectConnection);
connect(ui.action_Configure, SIGNAL(triggered()), this, SLOT(OnConfigure())); connect(ui.action_Configure, SIGNAL(triggered()), this, SLOT(OnConfigure()));
connect(ui.action_Load_File, SIGNAL(triggered()), this, SLOT(OnMenuLoadFile()),Qt::DirectConnection); connect(ui.action_Load_File, SIGNAL(triggered()), this, SLOT(OnMenuLoadFile()),
Qt::DirectConnection);
connect(ui.action_Load_Symbol_Map, SIGNAL(triggered()), this, SLOT(OnMenuLoadSymbolMap())); connect(ui.action_Load_Symbol_Map, SIGNAL(triggered()), this, SLOT(OnMenuLoadSymbolMap()));
connect(ui.action_Select_Game_List_Root, SIGNAL(triggered()), this, SLOT(OnMenuSelectGameListRoot())); connect(ui.action_Select_Game_List_Root, SIGNAL(triggered()), this,
SLOT(OnMenuSelectGameListRoot()));
connect(ui.action_Start, SIGNAL(triggered()), this, SLOT(OnStartGame())); connect(ui.action_Start, SIGNAL(triggered()), this, SLOT(OnStartGame()));
connect(ui.action_Pause, SIGNAL(triggered()), this, SLOT(OnPauseGame())); connect(ui.action_Pause, SIGNAL(triggered()), this, SLOT(OnPauseGame()));
connect(ui.action_Stop, SIGNAL(triggered()), this, SLOT(OnStopGame())); connect(ui.action_Stop, SIGNAL(triggered()), this, SLOT(OnStopGame()));
connect(ui.action_Single_Window_Mode, SIGNAL(triggered(bool)), this, SLOT(ToggleWindowMode())); connect(ui.action_Single_Window_Mode, SIGNAL(triggered(bool)), this, SLOT(ToggleWindowMode()));
connect(this, SIGNAL(EmulationStarting(EmuThread*)), disasmWidget, SLOT(OnEmulationStarting(EmuThread*))); connect(this, SIGNAL(EmulationStarting(EmuThread*)), disasmWidget,
SLOT(OnEmulationStarting(EmuThread*)));
connect(this, SIGNAL(EmulationStopping()), disasmWidget, SLOT(OnEmulationStopping())); connect(this, SIGNAL(EmulationStopping()), disasmWidget, SLOT(OnEmulationStopping()));
connect(this, SIGNAL(EmulationStarting(EmuThread*)), registersWidget, SLOT(OnEmulationStarting(EmuThread*))); connect(this, SIGNAL(EmulationStarting(EmuThread*)), registersWidget,
SLOT(OnEmulationStarting(EmuThread*)));
connect(this, SIGNAL(EmulationStopping()), registersWidget, SLOT(OnEmulationStopping())); connect(this, SIGNAL(EmulationStopping()), registersWidget, SLOT(OnEmulationStopping()));
connect(this, SIGNAL(EmulationStarting(EmuThread*)), render_window, SLOT(OnEmulationStarting(EmuThread*))); connect(this, SIGNAL(EmulationStarting(EmuThread*)), render_window,
SLOT(OnEmulationStarting(EmuThread*)));
connect(this, SIGNAL(EmulationStopping()), render_window, SLOT(OnEmulationStopping())); connect(this, SIGNAL(EmulationStopping()), render_window, SLOT(OnEmulationStopping()));
connect(this, SIGNAL(EmulationStarting(EmuThread*)), graphicsTracingWidget, SLOT(OnEmulationStarting(EmuThread*))); connect(this, SIGNAL(EmulationStarting(EmuThread*)), graphicsTracingWidget,
SLOT(OnEmulationStarting(EmuThread*)));
connect(this, SIGNAL(EmulationStopping()), graphicsTracingWidget, SLOT(OnEmulationStopping())); connect(this, SIGNAL(EmulationStopping()), graphicsTracingWidget, SLOT(OnEmulationStopping()));
// Setup hotkeys // Setup hotkeys
RegisterHotkey("Main Window", "Load File", QKeySequence::Open); RegisterHotkey("Main Window", "Load File", QKeySequence::Open);
RegisterHotkey("Main Window", "Start Emulation"); RegisterHotkey("Main Window", "Start Emulation");
LoadHotkeys(); LoadHotkeys();
connect(GetHotkey("Main Window", "Load File", this), SIGNAL(activated()), this, SLOT(OnMenuLoadFile())); connect(GetHotkey("Main Window", "Load File", this), SIGNAL(activated()), this,
connect(GetHotkey("Main Window", "Start Emulation", this), SIGNAL(activated()), this, SLOT(OnStartGame())); SLOT(OnMenuLoadFile()));
connect(GetHotkey("Main Window", "Start Emulation", this), SIGNAL(activated()), this,
SLOT(OnStartGame()));
std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc); std::string window_title =
Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc);
setWindowTitle(window_title.c_str()); setWindowTitle(window_title.c_str());
show(); show();
@ -208,8 +209,7 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr)
} }
} }
GMainWindow::~GMainWindow() GMainWindow::~GMainWindow() {
{
// will get automatically deleted otherwise // will get automatically deleted otherwise
if (render_window->parent() == nullptr) if (render_window->parent() == nullptr)
delete render_window; delete render_window;
@ -217,19 +217,18 @@ GMainWindow::~GMainWindow()
Pica::g_debug_context.reset(); Pica::g_debug_context.reset();
} }
void GMainWindow::OnDisplayTitleBars(bool show) void GMainWindow::OnDisplayTitleBars(bool show) {
{
QList<QDockWidget*> widgets = findChildren<QDockWidget*>(); QList<QDockWidget*> widgets = findChildren<QDockWidget*>();
if (show) { if (show) {
for (QDockWidget* widget: widgets) { for (QDockWidget* widget : widgets) {
QWidget* old = widget->titleBarWidget(); QWidget* old = widget->titleBarWidget();
widget->setTitleBarWidget(nullptr); widget->setTitleBarWidget(nullptr);
if (old != nullptr) if (old != nullptr)
delete old; delete old;
} }
} else { } else {
for (QDockWidget* widget: widgets) { for (QDockWidget* widget : widgets) {
QWidget* old = widget->titleBarWidget(); QWidget* old = widget->titleBarWidget();
widget->setTitleBarWidget(new QWidget()); widget->setTitleBarWidget(new QWidget());
if (old != nullptr) if (old != nullptr)
@ -249,7 +248,8 @@ bool GMainWindow::InitializeSystem() {
if (!gladLoadGL()) { if (!gladLoadGL()) {
QMessageBox::critical(this, tr("Error while starting Citra!"), QMessageBox::critical(this, tr("Error while starting Citra!"),
tr("Failed to initialize the video core!\n\n" tr("Failed to initialize the video core!\n\n"
"Please ensure that your GPU supports OpenGL 3.3 and that you have the latest graphics driver.")); "Please ensure that your GPU supports OpenGL 3.3 and that you "
"have the latest graphics driver."));
return false; return false;
} }
@ -260,7 +260,8 @@ bool GMainWindow::InitializeSystem() {
case System::Result::ErrorInitVideoCore: case System::Result::ErrorInitVideoCore:
QMessageBox::critical(this, tr("Error while starting Citra!"), QMessageBox::critical(this, tr("Error while starting Citra!"),
tr("Failed to initialize the video core!\n\n" tr("Failed to initialize the video core!\n\n"
"Please ensure that your GPU supports OpenGL 3.3 and that you have the latest graphics driver.")); "Please ensure that your GPU supports OpenGL 3.3 and that you "
"have the latest graphics driver."));
break; break;
default: default:
@ -293,8 +294,12 @@ bool GMainWindow::LoadROM(const std::string& filename) {
QMessageBox popup_error; QMessageBox popup_error;
popup_error.setTextFormat(Qt::RichText); popup_error.setTextFormat(Qt::RichText);
popup_error.setWindowTitle(tr("Error while loading ROM!")); popup_error.setWindowTitle(tr("Error while loading ROM!"));
popup_error.setText(tr("The game that you are trying to load must be decrypted before being used with Citra.<br/><br/>" popup_error.setText(
"For more information on dumping and decrypting games, please see: <a href='https://citra-emu.org/wiki/Dumping-Game-Cartridges'>https://citra-emu.org/wiki/Dumping-Game-Cartridges</a>")); tr("The game that you are trying to load must be decrypted before being used with "
"Citra.<br/><br/>"
"For more information on dumping and decrypting games, please see: <a "
"href='https://citra-emu.org/wiki/Dumping-Game-Cartridges'>https://"
"citra-emu.org/wiki/Dumping-Game-Cartridges</a>"));
popup_error.setIcon(QMessageBox::Critical); popup_error.setIcon(QMessageBox::Critical);
popup_error.exec(); popup_error.exec();
break; break;
@ -306,8 +311,7 @@ bool GMainWindow::LoadROM(const std::string& filename) {
case Loader::ResultStatus::Error: case Loader::ResultStatus::Error:
default: default:
QMessageBox::critical(this, tr("Error while loading ROM!"), QMessageBox::critical(this, tr("Error while loading ROM!"), tr("Unknown error!"));
tr("Unknown error!"));
break; break;
} }
return false; return false;
@ -332,13 +336,20 @@ void GMainWindow::BootGame(const std::string& filename) {
emu_thread->start(); emu_thread->start();
connect(render_window, SIGNAL(Closed()), this, SLOT(OnStopGame())); connect(render_window, SIGNAL(Closed()), this, SLOT(OnStopGame()));
// BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views before the CPU continues // BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views
connect(emu_thread.get(), SIGNAL(DebugModeEntered()), disasmWidget, SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection); // before the CPU continues
connect(emu_thread.get(), SIGNAL(DebugModeEntered()), registersWidget, SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection); connect(emu_thread.get(), SIGNAL(DebugModeEntered()), disasmWidget, SLOT(OnDebugModeEntered()),
connect(emu_thread.get(), SIGNAL(DebugModeEntered()), callstackWidget, SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection); Qt::BlockingQueuedConnection);
connect(emu_thread.get(), SIGNAL(DebugModeLeft()), disasmWidget, SLOT(OnDebugModeLeft()), Qt::BlockingQueuedConnection); connect(emu_thread.get(), SIGNAL(DebugModeEntered()), registersWidget,
connect(emu_thread.get(), SIGNAL(DebugModeLeft()), registersWidget, SLOT(OnDebugModeLeft()), Qt::BlockingQueuedConnection); SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection);
connect(emu_thread.get(), SIGNAL(DebugModeLeft()), callstackWidget, SLOT(OnDebugModeLeft()), Qt::BlockingQueuedConnection); connect(emu_thread.get(), SIGNAL(DebugModeEntered()), callstackWidget,
SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection);
connect(emu_thread.get(), SIGNAL(DebugModeLeft()), disasmWidget, SLOT(OnDebugModeLeft()),
Qt::BlockingQueuedConnection);
connect(emu_thread.get(), SIGNAL(DebugModeLeft()), registersWidget, SLOT(OnDebugModeLeft()),
Qt::BlockingQueuedConnection);
connect(emu_thread.get(), SIGNAL(DebugModeLeft()), callstackWidget, SLOT(OnDebugModeLeft()),
Qt::BlockingQueuedConnection);
// Update the GUI // Update the GUI
registersWidget->OnDebugModeEntered(); registersWidget->OnDebugModeEntered();
@ -393,10 +404,12 @@ void GMainWindow::StoreRecentFile(const std::string& filename) {
} }
void GMainWindow::UpdateRecentFiles() { void GMainWindow::UpdateRecentFiles() {
unsigned int num_recent_files = std::min(UISettings::values.recent_files.size(), static_cast<int>(max_recent_files_item)); unsigned int num_recent_files =
std::min(UISettings::values.recent_files.size(), static_cast<int>(max_recent_files_item));
for (unsigned int i = 0; i < num_recent_files; i++) { for (unsigned int i = 0; i < num_recent_files; i++) {
QString text = QString("&%1. %2").arg(i + 1).arg(QFileInfo(UISettings::values.recent_files[i]).fileName()); QString text = QString("&%1. %2").arg(i + 1).arg(
QFileInfo(UISettings::values.recent_files[i]).fileName());
actions_recent_files[i]->setText(text); actions_recent_files[i]->setText(text);
actions_recent_files[i]->setData(UISettings::values.recent_files[i]); actions_recent_files[i]->setData(UISettings::values.recent_files[i]);
actions_recent_files[i]->setToolTip(UISettings::values.recent_files[i]); actions_recent_files[i]->setToolTip(UISettings::values.recent_files[i]);
@ -420,7 +433,9 @@ void GMainWindow::OnGameListLoadFile(QString game_path) {
} }
void GMainWindow::OnMenuLoadFile() { void GMainWindow::OnMenuLoadFile() {
QString filename = QFileDialog::getOpenFileName(this, tr("Load File"), UISettings::values.roms_path, tr("3DS executable (*.3ds *.3dsx *.elf *.axf *.cci *.cxi)")); QString filename =
QFileDialog::getOpenFileName(this, tr("Load File"), UISettings::values.roms_path,
tr("3DS executable (*.3ds *.3dsx *.elf *.axf *.cci *.cxi)"));
if (!filename.isEmpty()) { if (!filename.isEmpty()) {
UISettings::values.roms_path = QFileInfo(filename).path(); UISettings::values.roms_path = QFileInfo(filename).path();
@ -429,7 +444,8 @@ void GMainWindow::OnMenuLoadFile() {
} }
void GMainWindow::OnMenuLoadSymbolMap() { void GMainWindow::OnMenuLoadSymbolMap() {
QString filename = QFileDialog::getOpenFileName(this, tr("Load Symbol Map"), UISettings::values.symbols_path, tr("Symbol map (*)")); QString filename = QFileDialog::getOpenFileName(
this, tr("Load Symbol Map"), UISettings::values.symbols_path, tr("Symbol map (*)"));
if (!filename.isEmpty()) { if (!filename.isEmpty()) {
UISettings::values.symbols_path = QFileInfo(filename).path(); UISettings::values.symbols_path = QFileInfo(filename).path();
@ -455,7 +471,8 @@ void GMainWindow::OnMenuRecentFile() {
BootGame(filename.toStdString()); BootGame(filename.toStdString());
} else { } else {
// Display an error message and remove the file from the list. // Display an error message and remove the file from the list.
QMessageBox::information(this, tr("File not found"), tr("File \"%1\" not found").arg(filename)); QMessageBox::information(this, tr("File not found"),
tr("File \"%1\" not found").arg(filename));
UISettings::values.recent_files.removeOne(filename); UISettings::values.recent_files.removeOne(filename);
UpdateRecentFiles(); UpdateRecentFiles();
@ -512,8 +529,7 @@ void GMainWindow::ToggleWindowMode() {
void GMainWindow::OnConfigure() { void GMainWindow::OnConfigure() {
ConfigureDialog configureDialog(this); ConfigureDialog configureDialog(this);
auto result = configureDialog.exec(); auto result = configureDialog.exec();
if (result == QDialog::Accepted) if (result == QDialog::Accepted) {
{
configureDialog.applyConfiguration(); configureDialog.applyConfiguration();
render_window->ReloadSetKeymaps(); render_window->ReloadSetKeymaps();
config->Save(); config->Save();
@ -531,8 +547,8 @@ bool GMainWindow::ConfirmClose() {
if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) if (emu_thread == nullptr || !UISettings::values.confirm_before_closing)
return true; return true;
auto answer = QMessageBox::question(this, tr("Citra"), auto answer =
tr("Are you sure you want to close Citra?"), QMessageBox::question(this, tr("Citra"), tr("Are you sure you want to close Citra?"),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No); QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
return answer != QMessageBox::No; return answer != QMessageBox::No;
} }
@ -575,9 +591,7 @@ int main(int argc, char* argv[]) {
Log::SetFilter(&log_filter); Log::SetFilter(&log_filter);
MicroProfileOnThreadCreate("Frontend"); MicroProfileOnThreadCreate("Frontend");
SCOPE_EXIT({ SCOPE_EXIT({ MicroProfileShutdown(); });
MicroProfileShutdown();
});
// Init settings params // Init settings params
QCoreApplication::setOrganizationName("Citra team"); QCoreApplication::setOrganizationName("Citra team");
@ -586,7 +600,8 @@ int main(int argc, char* argv[]) {
QApplication::setAttribute(Qt::AA_X11InitThreads); QApplication::setAttribute(Qt::AA_X11InitThreads);
QApplication app(argc, argv); QApplication app(argc, argv);
// Qt changes the locale and causes issues in float conversion using std::to_string() when generating shaders // Qt changes the locale and causes issues in float conversion using std::to_string() when
// generating shaders
setlocale(LC_ALL, "C"); setlocale(LC_ALL, "C");
GMainWindow main_window; GMainWindow main_window;

View File

@ -7,7 +7,6 @@
#include <memory> #include <memory>
#include <QMainWindow> #include <QMainWindow>
#include "ui_main.h" #include "ui_main.h"
class Config; class Config;
@ -23,11 +22,11 @@ class CallstackWidget;
class GPUCommandStreamWidget; class GPUCommandStreamWidget;
class GPUCommandListWidget; class GPUCommandListWidget;
class GMainWindow : public QMainWindow class GMainWindow : public QMainWindow {
{
Q_OBJECT Q_OBJECT
static const int max_recent_files_item = 10; ///< Max number of recently loaded items to keep track /// Max number of recently loaded items to keep track of
static const int max_recent_files_item = 10;
// TODO: Make use of this! // TODO: Make use of this!
enum { enum {

View File

@ -7,5 +7,4 @@
namespace UISettings { namespace UISettings {
Values values = {}; Values values = {};
} }

View File

@ -4,15 +4,14 @@
#pragma once #pragma once
#include <QByteArray>
#include <QStringList>
#include <QString>
#include <vector> #include <vector>
#include <QByteArray>
#include <QString>
#include <QStringList>
namespace UISettings { namespace UISettings {
using ContextualShortcut = std::pair<QString, int> ; using ContextualShortcut = std::pair<QString, int>;
using Shortcut = std::pair<QString, ContextualShortcut>; using Shortcut = std::pair<QString, ContextualShortcut>;
struct Values { struct Values {
@ -43,5 +42,4 @@ struct Values {
}; };
extern Values values; extern Values values;
} }

View File

@ -1,7 +1,6 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
// Copyright 2014 Tony Wasserka // Copyright 2014 Tony Wasserka
// All rights reserved. // All rights reserved.
// //
@ -32,12 +31,11 @@
#include <cstdlib> #include <cstdlib>
#include <QLineEdit> #include <QLineEdit>
#include <QRegExpValidator> #include <QRegExpValidator>
#include "citra_qt/util/spinbox.h" #include "citra_qt/util/spinbox.h"
#include "common/assert.h" #include "common/assert.h"
CSpinBox::CSpinBox(QWidget* parent) : QAbstractSpinBox(parent), min_value(-100), max_value(100), value(0), base(10), num_digits(0) CSpinBox::CSpinBox(QWidget* parent)
{ : QAbstractSpinBox(parent), min_value(-100), max_value(100), value(0), base(10), num_digits(0) {
// TODO: Might be nice to not immediately call the slot. // TODO: Might be nice to not immediately call the slot.
// Think of an address that is being replaced by a different one, in which case a lot // Think of an address that is being replaced by a different one, in which case a lot
// invalid intermediate addresses would be read from during editing. // invalid intermediate addresses would be read from during editing.
@ -46,8 +44,7 @@ CSpinBox::CSpinBox(QWidget* parent) : QAbstractSpinBox(parent), min_value(-100),
UpdateText(); UpdateText();
} }
void CSpinBox::SetValue(qint64 val) void CSpinBox::SetValue(qint64 val) {
{
auto old_value = value; auto old_value = value;
value = std::max(std::min(val, max_value), min_value); value = std::max(std::min(val, max_value), min_value);
@ -57,8 +54,7 @@ void CSpinBox::SetValue(qint64 val)
} }
} }
void CSpinBox::SetRange(qint64 min, qint64 max) void CSpinBox::SetRange(qint64 min, qint64 max) {
{
min_value = min; min_value = min;
max_value = max; max_value = max;
@ -66,8 +62,7 @@ void CSpinBox::SetRange(qint64 min, qint64 max)
UpdateText(); UpdateText();
} }
void CSpinBox::stepBy(int steps) void CSpinBox::stepBy(int steps) {
{
auto new_value = value; auto new_value = value;
// Scale number of steps by the currently selected digit // Scale number of steps by the currently selected digit
// TODO: Move this code elsewhere and enable it. // TODO: Move this code elsewhere and enable it.
@ -93,8 +88,7 @@ void CSpinBox::stepBy(int steps)
UpdateText(); UpdateText();
} }
QAbstractSpinBox::StepEnabled CSpinBox::stepEnabled() const QAbstractSpinBox::StepEnabled CSpinBox::stepEnabled() const {
{
StepEnabled ret = StepNone; StepEnabled ret = StepNone;
if (value > min_value) if (value > min_value)
@ -106,29 +100,25 @@ QAbstractSpinBox::StepEnabled CSpinBox::stepEnabled() const
return ret; return ret;
} }
void CSpinBox::SetBase(int base) void CSpinBox::SetBase(int base) {
{
this->base = base; this->base = base;
UpdateText(); UpdateText();
} }
void CSpinBox::SetNumDigits(int num_digits) void CSpinBox::SetNumDigits(int num_digits) {
{
this->num_digits = num_digits; this->num_digits = num_digits;
UpdateText(); UpdateText();
} }
void CSpinBox::SetPrefix(const QString& prefix) void CSpinBox::SetPrefix(const QString& prefix) {
{
this->prefix = prefix; this->prefix = prefix;
UpdateText(); UpdateText();
} }
void CSpinBox::SetSuffix(const QString& suffix) void CSpinBox::SetSuffix(const QString& suffix) {
{
this->suffix = suffix; this->suffix = suffix;
UpdateText(); UpdateText();
@ -161,8 +151,7 @@ static QString StringToInputMask(const QString& input) {
return mask; return mask;
} }
void CSpinBox::UpdateText() void CSpinBox::UpdateText() {
{
// If a fixed number of digits is used, we put the line edit in insertion mode by setting an // If a fixed number of digits is used, we put the line edit in insertion mode by setting an
// input mask. // input mask.
QString mask; QString mask;
@ -179,10 +168,9 @@ void CSpinBox::UpdateText()
// The greatest signed 64-bit number has 19 decimal digits. // The greatest signed 64-bit number has 19 decimal digits.
// TODO: Could probably make this more generic with some logarithms. // TODO: Could probably make this more generic with some logarithms.
// For reference, unsigned 64-bit can have up to 20 decimal digits. // For reference, unsigned 64-bit can have up to 20 decimal digits.
int digits = (num_digits != 0) ? num_digits int digits = (num_digits != 0)
: (base == 16) ? 16 ? num_digits
: (base == 10) ? 19 : (base == 16) ? 16 : (base == 10) ? 19 : 0xFF; // fallback case...
: 0xFF; // fallback case...
// Match num_digits digits // Match num_digits digits
// Digits irrelevant to the chosen number base are filtered in the validator // Digits irrelevant to the chosen number base are filtered in the validator
@ -203,29 +191,24 @@ void CSpinBox::UpdateText()
lineEdit()->setCursorPosition(cursor_position); lineEdit()->setCursorPosition(cursor_position);
} }
QString CSpinBox::TextFromValue() QString CSpinBox::TextFromValue() {
{ return prefix + QString(HasSign() ? ((value < 0) ? "-" : "+") : "") +
return prefix QString("%1").arg(std::abs(value), num_digits, base, QLatin1Char('0')).toUpper() +
+ QString(HasSign() ? ((value < 0) ? "-" : "+") : "") suffix;
+ QString("%1").arg(std::abs(value), num_digits, base, QLatin1Char('0')).toUpper()
+ suffix;
} }
qint64 CSpinBox::ValueFromText() qint64 CSpinBox::ValueFromText() {
{
unsigned strpos = prefix.length(); unsigned strpos = prefix.length();
QString num_string = text().mid(strpos, text().length() - strpos - suffix.length()); QString num_string = text().mid(strpos, text().length() - strpos - suffix.length());
return num_string.toLongLong(nullptr, base); return num_string.toLongLong(nullptr, base);
} }
bool CSpinBox::HasSign() const bool CSpinBox::HasSign() const {
{
return base == 10 && min_value < 0; return base == 10 && min_value < 0;
} }
void CSpinBox::OnEditingFinished() void CSpinBox::OnEditingFinished() {
{
// Only update for valid input // Only update for valid input
QString input = lineEdit()->text(); QString input = lineEdit()->text();
int pos = 0; int pos = 0;
@ -233,8 +216,7 @@ void CSpinBox::OnEditingFinished()
SetValue(ValueFromText()); SetValue(ValueFromText());
} }
QValidator::State CSpinBox::validate(QString& input, int& pos) const QValidator::State CSpinBox::validate(QString& input, int& pos) const {
{
if (!prefix.isEmpty() && input.left(prefix.length()) != prefix) if (!prefix.isEmpty() && input.left(prefix.length()) != prefix)
return QValidator::Invalid; return QValidator::Invalid;

View File

@ -1,7 +1,6 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
// Copyright 2014 Tony Wasserka // Copyright 2014 Tony Wasserka
// All rights reserved. // All rights reserved.
// //
@ -29,7 +28,6 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma once #pragma once
#include <QAbstractSpinBox> #include <QAbstractSpinBox>

View File

@ -4,7 +4,6 @@
#include <array> #include <array>
#include <cmath> #include <cmath>
#include "citra_qt/util/util.h" #include "citra_qt/util/util.h"
QFont GetMonospaceFont() { QFont GetMonospaceFont() {
@ -16,10 +15,12 @@ QFont GetMonospaceFont() {
} }
QString ReadableByteSize(qulonglong size) { QString ReadableByteSize(qulonglong size) {
static const std::array<const char*, 6> units = { "B", "KiB", "MiB", "GiB", "TiB", "PiB" }; static const std::array<const char*, 6> units = {"B", "KiB", "MiB", "GiB", "TiB", "PiB"};
if (size == 0) if (size == 0)
return "0"; return "0";
int digit_groups = std::min<int>(static_cast<int>(std::log10(size) / std::log10(1024)), static_cast<int>(units.size())); int digit_groups = std::min<int>(static_cast<int>(std::log10(size) / std::log10(1024)),
return QString("%L1 %2").arg(size / std::pow(1024, digit_groups), 0, 'f', 1) static_cast<int>(units.size()));
return QString("%L1 %2")
.arg(size / std::pow(1024, digit_groups), 0, 'f', 1)
.arg(units[digit_groups]); .arg(units[digit_groups]);
} }

View File

@ -5,7 +5,6 @@
#pragma once #pragma once
#include <cstdlib> #include <cstdlib>
#include "common/common_funcs.h" #include "common/common_funcs.h"
#include "common/logging/log.h" #include "common/logging/log.h"
@ -18,25 +17,29 @@
// enough for our purposes. // enough for our purposes.
template <typename Fn> template <typename Fn>
#if defined(_MSC_VER) #if defined(_MSC_VER)
__declspec(noinline, noreturn) __declspec(noinline, noreturn)
#elif defined(__GNUC__) #elif defined(__GNUC__)
__attribute__((noinline, noreturn, cold)) __attribute__((noinline, noreturn, cold))
#endif #endif
static void assert_noinline_call(const Fn& fn) { static void assert_noinline_call(const Fn& fn) {
fn(); fn();
Crash(); Crash();
exit(1); // Keeps GCC's mouth shut about this actually returning exit(1); // Keeps GCC's mouth shut about this actually returning
} }
#define ASSERT(_a_) \ #define ASSERT(_a_) \
do if (!(_a_)) { assert_noinline_call([] { \ do \
LOG_CRITICAL(Debug, "Assertion Failed!"); \ if (!(_a_)) { \
}); } while (0) assert_noinline_call([] { LOG_CRITICAL(Debug, "Assertion Failed!"); }); \
} \
while (0)
#define ASSERT_MSG(_a_, ...) \ #define ASSERT_MSG(_a_, ...) \
do if (!(_a_)) { assert_noinline_call([&] { \ do \
LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); \ if (!(_a_)) { \
}); } while (0) assert_noinline_call([&] { LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); }); \
} \
while (0)
#define UNREACHABLE() ASSERT_MSG(false, "Unreachable code!") #define UNREACHABLE() ASSERT_MSG(false, "Unreachable code!")
#define UNREACHABLE_MSG(...) ASSERT_MSG(false, __VA_ARGS__) #define UNREACHABLE_MSG(...) ASSERT_MSG(false, __VA_ARGS__)

View File

@ -1,7 +1,6 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
// Copyright 2014 Tony Wasserka // Copyright 2014 Tony Wasserka
// All rights reserved. // All rights reserved.
// //
@ -29,13 +28,11 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma once #pragma once
#include <cstddef> #include <cstddef>
#include <limits> #include <limits>
#include <type_traits> #include <type_traits>
#include "common/common_funcs.h" #include "common/common_funcs.h"
/* /*
@ -111,9 +108,8 @@
* symptoms. * symptoms.
*/ */
#pragma pack(1) #pragma pack(1)
template<std::size_t position, std::size_t bits, typename T> template <std::size_t position, std::size_t bits, typename T>
struct BitField struct BitField {
{
private: private:
// We hide the copy assigment operator here, because the default copy // We hide the copy assigment operator here, because the default copy
// assignment would copy the full storage value, rather than just the bits // assignment would copy the full storage value, rather than just the bits
@ -141,13 +137,10 @@ public:
} }
FORCE_INLINE T Value() const { FORCE_INLINE T Value() const {
if (std::numeric_limits<T>::is_signed) if (std::numeric_limits<T>::is_signed) {
{ std::size_t shift = 8 * sizeof(T) - bits;
std::size_t shift = 8 * sizeof(T)-bits;
return (T)((storage << (shift - position)) >> shift); return (T)((storage << (shift - position)) >> shift);
} } else {
else
{
return (T)((storage & GetMask()) >> position); return (T)((storage & GetMask()) >> position);
} }
} }
@ -162,15 +155,14 @@ private:
// T is an enumeration. Note that T is wrapped within an enable_if in the // T is an enumeration. Note that T is wrapped within an enable_if in the
// former case to workaround compile errors which arise when using // former case to workaround compile errors which arise when using
// std::underlying_type<T>::type directly. // std::underlying_type<T>::type directly.
typedef typename std::conditional < std::is_enum<T>::value, typedef typename std::conditional<std::is_enum<T>::value, std::underlying_type<T>,
std::underlying_type<T>, std::enable_if<true, T>>::type::type StorageType;
std::enable_if < true, T >> ::type::type StorageType;
// Unsigned version of StorageType // Unsigned version of StorageType
typedef typename std::make_unsigned<StorageType>::type StorageTypeU; typedef typename std::make_unsigned<StorageType>::type StorageTypeU;
FORCE_INLINE StorageType GetMask() const { FORCE_INLINE StorageType GetMask() const {
return (((StorageTypeU)~0) >> (8 * sizeof(T)-bits)) << position; return (((StorageTypeU)~0) >> (8 * sizeof(T) - bits)) << position;
} }
StorageType storage; StorageType storage;
@ -186,5 +178,6 @@ private:
#pragma pack() #pragma pack()
#if (__GNUC__ >= 5) || defined(__clang__) || defined(_MSC_VER) #if (__GNUC__ >= 5) || defined(__clang__) || defined(_MSC_VER)
static_assert(std::is_trivially_copyable<BitField<0, 1, unsigned>>::value, "BitField must be trivially copyable"); static_assert(std::is_trivially_copyable<BitField<0, 1, unsigned>>::value,
"BitField must be trivially copyable");
#endif #endif

View File

@ -18,49 +18,60 @@ namespace Common {
#ifdef _WIN32 #ifdef _WIN32
template <typename T> template <typename T>
static inline int CountSetBits(T v) static inline int CountSetBits(T v) {
{
// from https://graphics.stanford.edu/~seander/bithacks.html // from https://graphics.stanford.edu/~seander/bithacks.html
// GCC has this built in, but MSVC's intrinsic will only emit the actual // GCC has this built in, but MSVC's intrinsic will only emit the actual
// POPCNT instruction, which we're not depending on // POPCNT instruction, which we're not depending on
v = v - ((v >> 1) & (T)~(T)0/3); v = v - ((v >> 1) & (T) ~(T)0 / 3);
v = (v & (T)~(T)0/15*3) + ((v >> 2) & (T)~(T)0/15*3); v = (v & (T) ~(T)0 / 15 * 3) + ((v >> 2) & (T) ~(T)0 / 15 * 3);
v = (v + (v >> 4)) & (T)~(T)0/255*15; v = (v + (v >> 4)) & (T) ~(T)0 / 255 * 15;
return (T)(v * ((T)~(T)0/255)) >> (sizeof(T) - 1) * 8; return (T)(v * ((T) ~(T)0 / 255)) >> (sizeof(T) - 1) * 8;
} }
static inline int LeastSignificantSetBit(u8 val) static inline int LeastSignificantSetBit(u8 val) {
{
unsigned long index; unsigned long index;
_BitScanForward(&index, val); _BitScanForward(&index, val);
return (int)index; return (int)index;
} }
static inline int LeastSignificantSetBit(u16 val) static inline int LeastSignificantSetBit(u16 val) {
{
unsigned long index; unsigned long index;
_BitScanForward(&index, val); _BitScanForward(&index, val);
return (int)index; return (int)index;
} }
static inline int LeastSignificantSetBit(u32 val) static inline int LeastSignificantSetBit(u32 val) {
{
unsigned long index; unsigned long index;
_BitScanForward(&index, val); _BitScanForward(&index, val);
return (int)index; return (int)index;
} }
static inline int LeastSignificantSetBit(u64 val) static inline int LeastSignificantSetBit(u64 val) {
{
unsigned long index; unsigned long index;
_BitScanForward64(&index, val); _BitScanForward64(&index, val);
return (int)index; return (int)index;
} }
#else #else
static inline int CountSetBits(u8 val) { return __builtin_popcount(val); } static inline int CountSetBits(u8 val) {
static inline int CountSetBits(u16 val) { return __builtin_popcount(val); } return __builtin_popcount(val);
static inline int CountSetBits(u32 val) { return __builtin_popcount(val); } }
static inline int CountSetBits(u64 val) { return __builtin_popcountll(val); } static inline int CountSetBits(u16 val) {
static inline int LeastSignificantSetBit(u8 val) { return __builtin_ctz(val); } return __builtin_popcount(val);
static inline int LeastSignificantSetBit(u16 val) { return __builtin_ctz(val); } }
static inline int LeastSignificantSetBit(u32 val) { return __builtin_ctz(val); } static inline int CountSetBits(u32 val) {
static inline int LeastSignificantSetBit(u64 val) { return __builtin_ctzll(val); } return __builtin_popcount(val);
}
static inline int CountSetBits(u64 val) {
return __builtin_popcountll(val);
}
static inline int LeastSignificantSetBit(u8 val) {
return __builtin_ctz(val);
}
static inline int LeastSignificantSetBit(u16 val) {
return __builtin_ctz(val);
}
static inline int LeastSignificantSetBit(u32 val) {
return __builtin_ctz(val);
}
static inline int LeastSignificantSetBit(u64 val) {
return __builtin_ctzll(val);
}
#endif #endif
// Similar to std::bitset, this is a class which encapsulates a bitset, i.e. // Similar to std::bitset, this is a class which encapsulates a bitset, i.e.
@ -84,57 +95,62 @@ static inline int LeastSignificantSetBit(u64 val) { return __builtin_ctzll(val);
// TODO: use constexpr when MSVC gets out of the Dark Ages // TODO: use constexpr when MSVC gets out of the Dark Ages
template <typename IntTy> template <typename IntTy>
class BitSet class BitSet {
{
static_assert(!std::is_signed<IntTy>::value, "BitSet should not be used with signed types"); static_assert(!std::is_signed<IntTy>::value, "BitSet should not be used with signed types");
public: public:
// A reference to a particular bit, returned from operator[]. // A reference to a particular bit, returned from operator[].
class Ref class Ref {
{
public: public:
Ref(Ref&& other) : m_bs(other.m_bs), m_mask(other.m_mask) {} Ref(Ref&& other) : m_bs(other.m_bs), m_mask(other.m_mask) {}
Ref(BitSet* bs, IntTy mask) : m_bs(bs), m_mask(mask) {} Ref(BitSet* bs, IntTy mask) : m_bs(bs), m_mask(mask) {}
operator bool() const { return (m_bs->m_val & m_mask) != 0; } operator bool() const {
bool operator=(bool set) return (m_bs->m_val & m_mask) != 0;
{ }
bool operator=(bool set) {
m_bs->m_val = (m_bs->m_val & ~m_mask) | (set ? m_mask : 0); m_bs->m_val = (m_bs->m_val & ~m_mask) | (set ? m_mask : 0);
return set; return set;
} }
private: private:
BitSet* m_bs; BitSet* m_bs;
IntTy m_mask; IntTy m_mask;
}; };
// A STL-like iterator is required to be able to use range-based for loops. // A STL-like iterator is required to be able to use range-based for loops.
class Iterator class Iterator {
{
public: public:
Iterator(const Iterator& other) : m_val(other.m_val), m_bit(other.m_bit) {} Iterator(const Iterator& other) : m_val(other.m_val), m_bit(other.m_bit) {}
Iterator(IntTy val, int bit) : m_val(val), m_bit(bit) {} Iterator(IntTy val, int bit) : m_val(val), m_bit(bit) {}
Iterator& operator=(Iterator other) { new (this) Iterator(other); return *this; } Iterator& operator=(Iterator other) {
int operator*() { return m_bit; } new (this) Iterator(other);
Iterator& operator++() return *this;
{
if (m_val == 0)
{
m_bit = -1;
} }
else int operator*() {
{ return m_bit;
}
Iterator& operator++() {
if (m_val == 0) {
m_bit = -1;
} else {
int bit = LeastSignificantSetBit(m_val); int bit = LeastSignificantSetBit(m_val);
m_val &= ~(1 << bit); m_val &= ~(1 << bit);
m_bit = bit; m_bit = bit;
} }
return *this; return *this;
} }
Iterator operator++(int _) Iterator operator++(int _) {
{
Iterator other(*this); Iterator other(*this);
++*this; ++*this;
return other; return other;
} }
bool operator==(Iterator other) const { return m_bit == other.m_bit; } bool operator==(Iterator other) const {
bool operator!=(Iterator other) const { return m_bit != other.m_bit; } return m_bit == other.m_bit;
}
bool operator!=(Iterator other) const {
return m_bit != other.m_bit;
}
private: private:
IntTy m_val; IntTy m_val;
int m_bit; int m_bit;
@ -142,42 +158,75 @@ public:
BitSet() : m_val(0) {} BitSet() : m_val(0) {}
explicit BitSet(IntTy val) : m_val(val) {} explicit BitSet(IntTy val) : m_val(val) {}
BitSet(std::initializer_list<int> init) BitSet(std::initializer_list<int> init) {
{
m_val = 0; m_val = 0;
for (int bit : init) for (int bit : init)
m_val |= (IntTy)1 << bit; m_val |= (IntTy)1 << bit;
} }
static BitSet AllTrue(size_t count) static BitSet AllTrue(size_t count) {
{ return BitSet(count == sizeof(IntTy) * 8 ? ~(IntTy)0 : (((IntTy)1 << count) - 1));
return BitSet(count == sizeof(IntTy)*8 ? ~(IntTy)0 : (((IntTy)1 << count) - 1));
} }
Ref operator[](size_t bit) { return Ref(this, (IntTy)1 << bit); } Ref operator[](size_t bit) {
const Ref operator[](size_t bit) const { return (*const_cast<BitSet*>(this))[bit]; } return Ref(this, (IntTy)1 << bit);
bool operator==(BitSet other) const { return m_val == other.m_val; } }
bool operator!=(BitSet other) const { return m_val != other.m_val; } const Ref operator[](size_t bit) const {
bool operator<(BitSet other) const { return m_val < other.m_val; } return (*const_cast<BitSet*>(this))[bit];
bool operator>(BitSet other) const { return m_val > other.m_val; } }
BitSet operator|(BitSet other) const { return BitSet(m_val | other.m_val); } bool operator==(BitSet other) const {
BitSet operator&(BitSet other) const { return BitSet(m_val & other.m_val); } return m_val == other.m_val;
BitSet operator^(BitSet other) const { return BitSet(m_val ^ other.m_val); } }
BitSet operator~() const { return BitSet(~m_val); } bool operator!=(BitSet other) const {
BitSet& operator|=(BitSet other) { return *this = *this | other; } return m_val != other.m_val;
BitSet& operator&=(BitSet other) { return *this = *this & other; } }
BitSet& operator^=(BitSet other) { return *this = *this ^ other; } bool operator<(BitSet other) const {
return m_val < other.m_val;
}
bool operator>(BitSet other) const {
return m_val > other.m_val;
}
BitSet operator|(BitSet other) const {
return BitSet(m_val | other.m_val);
}
BitSet operator&(BitSet other) const {
return BitSet(m_val & other.m_val);
}
BitSet operator^(BitSet other) const {
return BitSet(m_val ^ other.m_val);
}
BitSet operator~() const {
return BitSet(~m_val);
}
BitSet& operator|=(BitSet other) {
return *this = *this | other;
}
BitSet& operator&=(BitSet other) {
return *this = *this & other;
}
BitSet& operator^=(BitSet other) {
return *this = *this ^ other;
}
operator u32() = delete; operator u32() = delete;
operator bool() { return m_val != 0; } operator bool() {
return m_val != 0;
}
// Warning: Even though on modern CPUs this is a single fast instruction, // Warning: Even though on modern CPUs this is a single fast instruction,
// Dolphin's official builds do not currently assume POPCNT support on x86, // Dolphin's official builds do not currently assume POPCNT support on x86,
// so slower explicit bit twiddling is generated. Still should generally // so slower explicit bit twiddling is generated. Still should generally
// be faster than a loop. // be faster than a loop.
unsigned int Count() const { return CountSetBits(m_val); } unsigned int Count() const {
return CountSetBits(m_val);
}
Iterator begin() const { Iterator it(m_val, 0); return ++it; } Iterator begin() const {
Iterator end() const { return Iterator(m_val, -1); } Iterator it(m_val, 0);
return ++it;
}
Iterator end() const {
return Iterator(m_val, -1);
}
IntTy m_val; IntTy m_val;
}; };

View File

@ -2,33 +2,29 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <algorithm>
#include <sstream>
#include "common/break_points.h" #include "common/break_points.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include <sstream> bool BreakPoints::IsAddressBreakPoint(u32 iAddress) const {
#include <algorithm>
bool BreakPoints::IsAddressBreakPoint(u32 iAddress) const
{
auto cond = [&iAddress](const TBreakPoint& bp) { return bp.iAddress == iAddress; }; auto cond = [&iAddress](const TBreakPoint& bp) { return bp.iAddress == iAddress; };
auto it = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond); auto it = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond);
return it != m_BreakPoints.end(); return it != m_BreakPoints.end();
} }
bool BreakPoints::IsTempBreakPoint(u32 iAddress) const bool BreakPoints::IsTempBreakPoint(u32 iAddress) const {
{ auto cond = [&iAddress](const TBreakPoint& bp) {
auto cond = [&iAddress](const TBreakPoint& bp) { return bp.iAddress == iAddress && bp.bTemporary; }; return bp.iAddress == iAddress && bp.bTemporary;
};
auto it = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond); auto it = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond);
return it != m_BreakPoints.end(); return it != m_BreakPoints.end();
} }
BreakPoints::TBreakPointsStr BreakPoints::GetStrings() const BreakPoints::TBreakPointsStr BreakPoints::GetStrings() const {
{
TBreakPointsStr bps; TBreakPointsStr bps;
for (auto breakpoint : m_BreakPoints) for (auto breakpoint : m_BreakPoints) {
{ if (!breakpoint.bTemporary) {
if (!breakpoint.bTemporary)
{
std::stringstream bp; std::stringstream bp;
bp << std::hex << breakpoint.iAddress << " " << (breakpoint.bOn ? "n" : ""); bp << std::hex << breakpoint.iAddress << " " << (breakpoint.bOn ? "n" : "");
bps.push_back(bp.str()); bps.push_back(bp.str());
@ -38,10 +34,8 @@ BreakPoints::TBreakPointsStr BreakPoints::GetStrings() const
return bps; return bps;
} }
void BreakPoints::AddFromStrings(const TBreakPointsStr& bps) void BreakPoints::AddFromStrings(const TBreakPointsStr& bps) {
{ for (auto bps_item : bps) {
for (auto bps_item : bps)
{
TBreakPoint bp; TBreakPoint bp;
std::stringstream bpstr; std::stringstream bpstr;
bpstr << std::hex << bps_item; bpstr << std::hex << bps_item;
@ -52,18 +46,15 @@ void BreakPoints::AddFromStrings(const TBreakPointsStr& bps)
} }
} }
void BreakPoints::Add(const TBreakPoint& bp) void BreakPoints::Add(const TBreakPoint& bp) {
{ if (!IsAddressBreakPoint(bp.iAddress)) {
if (!IsAddressBreakPoint(bp.iAddress))
{
m_BreakPoints.push_back(bp); m_BreakPoints.push_back(bp);
//if (jit) // if (jit)
// jit->GetBlockCache()->InvalidateICache(bp.iAddress, 4); // jit->GetBlockCache()->InvalidateICache(bp.iAddress, 4);
} }
} }
void BreakPoints::Add(u32 em_address, bool temp) void BreakPoints::Add(u32 em_address, bool temp) {
{
if (!IsAddressBreakPoint(em_address)) // only add new addresses if (!IsAddressBreakPoint(em_address)) // only add new addresses
{ {
TBreakPoint pt; // breakpoint settings TBreakPoint pt; // breakpoint settings
@ -73,22 +64,20 @@ void BreakPoints::Add(u32 em_address, bool temp)
m_BreakPoints.push_back(pt); m_BreakPoints.push_back(pt);
//if (jit) // if (jit)
// jit->GetBlockCache()->InvalidateICache(em_address, 4); // jit->GetBlockCache()->InvalidateICache(em_address, 4);
} }
} }
void BreakPoints::Remove(u32 em_address) void BreakPoints::Remove(u32 em_address) {
{
auto cond = [&em_address](const TBreakPoint& bp) { return bp.iAddress == em_address; }; auto cond = [&em_address](const TBreakPoint& bp) { return bp.iAddress == em_address; };
auto it = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond); auto it = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond);
if (it != m_BreakPoints.end()) if (it != m_BreakPoints.end())
m_BreakPoints.erase(it); m_BreakPoints.erase(it);
} }
void BreakPoints::Clear() void BreakPoints::Clear() {
{ // if (jit)
//if (jit)
//{ //{
// std::for_each(m_BreakPoints.begin(), m_BreakPoints.end(), // std::for_each(m_BreakPoints.begin(), m_BreakPoints.end(),
// [](const TBreakPoint& bp) // [](const TBreakPoint& bp)

View File

@ -4,28 +4,27 @@
#pragma once #pragma once
#include <vector>
#include <string> #include <string>
#include <vector>
#include "common/common_types.h" #include "common/common_types.h"
class DebugInterface; class DebugInterface;
struct TBreakPoint struct TBreakPoint {
{
u32 iAddress; u32 iAddress;
bool bOn; bool bOn;
bool bTemporary; bool bTemporary;
}; };
// Code breakpoints. // Code breakpoints.
class BreakPoints class BreakPoints {
{
public: public:
typedef std::vector<TBreakPoint> TBreakPoints; typedef std::vector<TBreakPoint> TBreakPoints;
typedef std::vector<std::string> TBreakPointsStr; typedef std::vector<std::string> TBreakPointsStr;
const TBreakPoints& GetBreakPoints() { return m_BreakPoints; } const TBreakPoints& GetBreakPoints() {
return m_BreakPoints;
}
TBreakPointsStr GetStrings() const; TBreakPointsStr GetStrings() const;
void AddFromStrings(const TBreakPointsStr& bps); void AddFromStrings(const TBreakPointsStr& bps);
@ -35,7 +34,7 @@ public:
bool IsTempBreakPoint(u32 iAddress) const; bool IsTempBreakPoint(u32 iAddress) const;
// Add BreakPoint // Add BreakPoint
void Add(u32 em_address, bool temp=false); void Add(u32 em_address, bool temp = false);
void Add(const TBreakPoint& bp); void Add(const TBreakPoint& bp);
// Remove Breakpoint // Remove Breakpoint

View File

@ -35,78 +35,81 @@
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "common/assert.h" #include "common/assert.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/logging/log.h" #include "common/logging/log.h"
template <class T> template <class T>
struct LinkedListItem : public T struct LinkedListItem : public T {
{ LinkedListItem<T>* next;
LinkedListItem<T> *next;
}; };
class PointerWrap; class PointerWrap;
class PointerWrapSection class PointerWrapSection {
{
public: public:
PointerWrapSection(PointerWrap &p, int ver, const char *title) : p_(p), ver_(ver), title_(title) { PointerWrapSection(PointerWrap& p, int ver, const char* title)
} : p_(p), ver_(ver), title_(title) {}
~PointerWrapSection(); ~PointerWrapSection();
bool operator == (const int &v) const { return ver_ == v; } bool operator==(const int& v) const {
bool operator != (const int &v) const { return ver_ != v; } return ver_ == v;
bool operator <= (const int &v) const { return ver_ <= v; } }
bool operator >= (const int &v) const { return ver_ >= v; } bool operator!=(const int& v) const {
bool operator < (const int &v) const { return ver_ < v; } return ver_ != v;
bool operator > (const int &v) const { return ver_ > v; } }
bool operator<=(const int& v) const {
return ver_ <= v;
}
bool operator>=(const int& v) const {
return ver_ >= v;
}
bool operator<(const int& v) const {
return ver_ < v;
}
bool operator>(const int& v) const {
return ver_ > v;
}
operator bool() const { operator bool() const {
return ver_ > 0; return ver_ > 0;
} }
private: private:
PointerWrap &p_; PointerWrap& p_;
int ver_; int ver_;
const char *title_; const char* title_;
}; };
// Wrapper class // Wrapper class
class PointerWrap class PointerWrap {
{ // This makes it a compile error if you forget to define DoState() on non-POD.
// This makes it a compile error if you forget to define DoState() on non-POD. // Which also can be a problem, for example struct tm is non-POD on linux, for whatever reason...
// Which also can be a problem, for example struct tm is non-POD on linux, for whatever reason...
#ifdef _MSC_VER #ifdef _MSC_VER
template<typename T, bool isPOD = std::is_pod<T>::value, bool isPointer = std::is_pointer<T>::value> template <typename T, bool isPOD = std::is_pod<T>::value,
bool isPointer = std::is_pointer<T>::value>
#else #else
template<typename T, bool isPOD = __is_pod(T), bool isPointer = std::is_pointer<T>::value> template <typename T, bool isPOD = __is_pod(T), bool isPointer = std::is_pointer<T>::value>
#endif #endif
struct DoHelper struct DoHelper {
{ static void DoArray(PointerWrap* p, T* x, int count) {
static void DoArray(PointerWrap *p, T *x, int count)
{
for (int i = 0; i < count; ++i) for (int i = 0; i < count; ++i)
p->Do(x[i]); p->Do(x[i]);
} }
static void Do(PointerWrap *p, T &x) static void Do(PointerWrap* p, T& x) {
{
p->DoClass(x); p->DoClass(x);
} }
}; };
template<typename T> template <typename T>
struct DoHelper<T, true, false> struct DoHelper<T, true, false> {
{ static void DoArray(PointerWrap* p, T* x, int count) {
static void DoArray(PointerWrap *p, T *x, int count) p->DoVoid((void*)x, sizeof(T) * count);
{
p->DoVoid((void *)x, sizeof(T) * count);
} }
static void Do(PointerWrap *p, T &x) static void Do(PointerWrap* p, T& x) {
{ p->DoVoid((void*)&x, sizeof(x));
p->DoVoid((void *)&x, sizeof(x));
} }
}; };
@ -124,129 +127,140 @@ public:
ERROR_FAILURE = 2, ERROR_FAILURE = 2,
}; };
u8 **ptr; u8** ptr;
Mode mode; Mode mode;
Error error; Error error;
public: public:
PointerWrap(u8 **ptr_, Mode mode_) : ptr(ptr_), mode(mode_), error(ERROR_NONE) {} PointerWrap(u8** ptr_, Mode mode_) : ptr(ptr_), mode(mode_), error(ERROR_NONE) {}
PointerWrap(unsigned char **ptr_, int mode_) : ptr((u8**)ptr_), mode((Mode)mode_), error(ERROR_NONE) {} PointerWrap(unsigned char** ptr_, int mode_)
: ptr((u8**)ptr_), mode((Mode)mode_), error(ERROR_NONE) {}
PointerWrapSection Section(const char *title, int ver) { PointerWrapSection Section(const char* title, int ver) {
return Section(title, ver, ver); return Section(title, ver, ver);
} }
// The returned object can be compared against the version that was loaded. // The returned object can be compared against the version that was loaded.
// This can be used to support versions as old as minVer. // This can be used to support versions as old as minVer.
// Version = 0 means the section was not found. // Version = 0 means the section was not found.
PointerWrapSection Section(const char *title, int minVer, int ver) { PointerWrapSection Section(const char* title, int minVer, int ver) {
char marker[16] = {0}; char marker[16] = {0};
int foundVersion = ver; int foundVersion = ver;
strncpy(marker, title, sizeof(marker)); strncpy(marker, title, sizeof(marker));
if (!ExpectVoid(marker, sizeof(marker))) if (!ExpectVoid(marker, sizeof(marker))) {
{
// Might be before we added name markers for safety. // Might be before we added name markers for safety.
if (foundVersion == 1 && ExpectVoid(&foundVersion, sizeof(foundVersion))) if (foundVersion == 1 && ExpectVoid(&foundVersion, sizeof(foundVersion)))
DoMarker(title); DoMarker(title);
// Wasn't found, but maybe we can still load the state. // Wasn't found, but maybe we can still load the state.
else else
foundVersion = 0; foundVersion = 0;
} } else
else
Do(foundVersion); Do(foundVersion);
if (error == ERROR_FAILURE || foundVersion < minVer || foundVersion > ver) { if (error == ERROR_FAILURE || foundVersion < minVer || foundVersion > ver) {
LOG_ERROR(Common, "Savestate failure: wrong version %d found for %s", foundVersion, title); LOG_ERROR(Common, "Savestate failure: wrong version %d found for %s", foundVersion,
title);
SetError(ERROR_FAILURE); SetError(ERROR_FAILURE);
return PointerWrapSection(*this, -1, title); return PointerWrapSection(*this, -1, title);
} }
return PointerWrapSection(*this, foundVersion, title); return PointerWrapSection(*this, foundVersion, title);
} }
void SetMode(Mode mode_) {mode = mode_;} void SetMode(Mode mode_) {
Mode GetMode() const {return mode;} mode = mode_;
u8 **GetPPtr() {return ptr;} }
void SetError(Error error_) Mode GetMode() const {
{ return mode;
}
u8** GetPPtr() {
return ptr;
}
void SetError(Error error_) {
if (error < error_) if (error < error_)
error = error_; error = error_;
if (error > ERROR_WARNING) if (error > ERROR_WARNING)
mode = PointerWrap::MODE_MEASURE; mode = PointerWrap::MODE_MEASURE;
} }
bool ExpectVoid(void *data, int size) bool ExpectVoid(void* data, int size) {
{
switch (mode) { switch (mode) {
case MODE_READ: if (memcmp(data, *ptr, size) != 0) return false; break; case MODE_READ:
case MODE_WRITE: memcpy(*ptr, data, size); break; if (memcmp(data, *ptr, size) != 0)
case MODE_MEASURE: break; // MODE_MEASURE - don't need to do anything return false;
break;
case MODE_WRITE:
memcpy(*ptr, data, size);
break;
case MODE_MEASURE:
break; // MODE_MEASURE - don't need to do anything
case MODE_VERIFY: case MODE_VERIFY:
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
DEBUG_ASSERT_MSG(((u8*)data)[i] == (*ptr)[i], DEBUG_ASSERT_MSG(
((u8*)data)[i] == (*ptr)[i],
"Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n", "Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n",
((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i], ((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i], (*ptr)[i], (*ptr)[i],
(*ptr)[i], (*ptr)[i], &(*ptr)[i]); &(*ptr)[i]);
} }
break; break;
default: break; // throw an error? default:
break; // throw an error?
} }
(*ptr) += size; (*ptr) += size;
return true; return true;
} }
void DoVoid(void *data, int size) void DoVoid(void* data, int size) {
{
switch (mode) { switch (mode) {
case MODE_READ: memcpy(data, *ptr, size); break; case MODE_READ:
case MODE_WRITE: memcpy(*ptr, data, size); break; memcpy(data, *ptr, size);
case MODE_MEASURE: break; // MODE_MEASURE - don't need to do anything break;
case MODE_WRITE:
memcpy(*ptr, data, size);
break;
case MODE_MEASURE:
break; // MODE_MEASURE - don't need to do anything
case MODE_VERIFY: case MODE_VERIFY:
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
DEBUG_ASSERT_MSG(((u8*)data)[i] == (*ptr)[i], DEBUG_ASSERT_MSG(
((u8*)data)[i] == (*ptr)[i],
"Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n", "Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n",
((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i], ((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i], (*ptr)[i], (*ptr)[i],
(*ptr)[i], (*ptr)[i], &(*ptr)[i]); &(*ptr)[i]);
} }
break; break;
default: break; // throw an error? default:
break; // throw an error?
} }
(*ptr) += size; (*ptr) += size;
} }
template<class K, class T> template <class K, class T>
void Do(std::map<K, T *> &x) void Do(std::map<K, T*>& x) {
{ if (mode == MODE_READ) {
if (mode == MODE_READ) for (auto it = x.begin(), end = x.end(); it != end; ++it) {
{
for (auto it = x.begin(), end = x.end(); it != end; ++it)
{
if (it->second != nullptr) if (it->second != nullptr)
delete it->second; delete it->second;
} }
} }
T *dv = nullptr; T* dv = nullptr;
DoMap(x, dv); DoMap(x, dv);
} }
template<class K, class T> template <class K, class T>
void Do(std::map<K, T> &x) void Do(std::map<K, T>& x) {
{
T dv = T(); T dv = T();
DoMap(x, dv); DoMap(x, dv);
} }
template<class K, class T> template <class K, class T>
void DoMap(std::map<K, T> &x, T &default_val) void DoMap(std::map<K, T>& x, T& default_val) {
{
unsigned int number = (unsigned int)x.size(); unsigned int number = (unsigned int)x.size();
Do(number); Do(number);
switch (mode) { switch (mode) {
case MODE_READ: case MODE_READ: {
{
x.clear(); x.clear();
while (number > 0) while (number > 0) {
{
K first = K(); K first = K();
Do(first); Do(first);
T second = default_val; T second = default_val;
@ -254,59 +268,48 @@ public:
x[first] = second; x[first] = second;
--number; --number;
} }
} } break;
break;
case MODE_WRITE: case MODE_WRITE:
case MODE_MEASURE: case MODE_MEASURE:
case MODE_VERIFY: case MODE_VERIFY: {
{
typename std::map<K, T>::iterator itr = x.begin(); typename std::map<K, T>::iterator itr = x.begin();
while (number > 0) while (number > 0) {
{
K first = itr->first; K first = itr->first;
Do(first); Do(first);
Do(itr->second); Do(itr->second);
--number; --number;
++itr; ++itr;
} }
} } break;
break;
} }
} }
template<class K, class T> template <class K, class T>
void Do(std::multimap<K, T *> &x) void Do(std::multimap<K, T*>& x) {
{ if (mode == MODE_READ) {
if (mode == MODE_READ) for (auto it = x.begin(), end = x.end(); it != end; ++it) {
{
for (auto it = x.begin(), end = x.end(); it != end; ++it)
{
if (it->second != nullptr) if (it->second != nullptr)
delete it->second; delete it->second;
} }
} }
T *dv = nullptr; T* dv = nullptr;
DoMultimap(x, dv); DoMultimap(x, dv);
} }
template<class K, class T> template <class K, class T>
void Do(std::multimap<K, T> &x) void Do(std::multimap<K, T>& x) {
{
T dv = T(); T dv = T();
DoMultimap(x, dv); DoMultimap(x, dv);
} }
template<class K, class T> template <class K, class T>
void DoMultimap(std::multimap<K, T> &x, T &default_val) void DoMultimap(std::multimap<K, T>& x, T& default_val) {
{
unsigned int number = (unsigned int)x.size(); unsigned int number = (unsigned int)x.size();
Do(number); Do(number);
switch (mode) { switch (mode) {
case MODE_READ: case MODE_READ: {
{
x.clear(); x.clear();
while (number > 0) while (number > 0) {
{
K first = K(); K first = K();
Do(first); Do(first);
T second = default_val; T second = default_val;
@ -314,57 +317,47 @@ public:
x.insert(std::make_pair(first, second)); x.insert(std::make_pair(first, second));
--number; --number;
} }
} } break;
break;
case MODE_WRITE: case MODE_WRITE:
case MODE_MEASURE: case MODE_MEASURE:
case MODE_VERIFY: case MODE_VERIFY: {
{
typename std::multimap<K, T>::iterator itr = x.begin(); typename std::multimap<K, T>::iterator itr = x.begin();
while (number > 0) while (number > 0) {
{
Do(itr->first); Do(itr->first);
Do(itr->second); Do(itr->second);
--number; --number;
++itr; ++itr;
} }
} } break;
break;
} }
} }
// Store vectors. // Store vectors.
template<class T> template <class T>
void Do(std::vector<T *> &x) void Do(std::vector<T*>& x) {
{ T* dv = nullptr;
T *dv = nullptr;
DoVector(x, dv); DoVector(x, dv);
} }
template<class T> template <class T>
void Do(std::vector<T> &x) void Do(std::vector<T>& x) {
{
T dv = T(); T dv = T();
DoVector(x, dv); DoVector(x, dv);
} }
template <class T>
template<class T> void DoPOD(std::vector<T>& x) {
void DoPOD(std::vector<T> &x)
{
T dv = T(); T dv = T();
DoVectorPOD(x, dv); DoVectorPOD(x, dv);
} }
template<class T> template <class T>
void Do(std::vector<T> &x, T &default_val) void Do(std::vector<T>& x, T& default_val) {
{
DoVector(x, default_val); DoVector(x, default_val);
} }
template<class T> template <class T>
void DoVector(std::vector<T> &x, T &default_val) void DoVector(std::vector<T>& x, T& default_val) {
{
u32 vec_size = (u32)x.size(); u32 vec_size = (u32)x.size();
Do(vec_size); Do(vec_size);
x.resize(vec_size, default_val); x.resize(vec_size, default_val);
@ -372,9 +365,8 @@ public:
DoArray(&x[0], vec_size); DoArray(&x[0], vec_size);
} }
template<class T> template <class T>
void DoVectorPOD(std::vector<T> &x, T &default_val) void DoVectorPOD(std::vector<T>& x, T& default_val) {
{
u32 vec_size = (u32)x.size(); u32 vec_size = (u32)x.size();
Do(vec_size); Do(vec_size);
x.resize(vec_size, default_val); x.resize(vec_size, default_val);
@ -383,55 +375,48 @@ public:
} }
// Store deques. // Store deques.
template<class T> template <class T>
void Do(std::deque<T *> &x) void Do(std::deque<T*>& x) {
{ T* dv = nullptr;
T *dv = nullptr;
DoDeque(x, dv); DoDeque(x, dv);
} }
template<class T> template <class T>
void Do(std::deque<T> &x) void Do(std::deque<T>& x) {
{
T dv = T(); T dv = T();
DoDeque(x, dv); DoDeque(x, dv);
} }
template<class T> template <class T>
void DoDeque(std::deque<T> &x, T &default_val) void DoDeque(std::deque<T>& x, T& default_val) {
{
u32 deq_size = (u32)x.size(); u32 deq_size = (u32)x.size();
Do(deq_size); Do(deq_size);
x.resize(deq_size, default_val); x.resize(deq_size, default_val);
u32 i; u32 i;
for(i = 0; i < deq_size; i++) for (i = 0; i < deq_size; i++)
Do(x[i]); Do(x[i]);
} }
// Store STL lists. // Store STL lists.
template<class T> template <class T>
void Do(std::list<T *> &x) void Do(std::list<T*>& x) {
{ T* dv = nullptr;
T *dv = nullptr;
Do(x, dv); Do(x, dv);
} }
template<class T> template <class T>
void Do(std::list<T> &x) void Do(std::list<T>& x) {
{
T dv = T(); T dv = T();
DoList(x, dv); DoList(x, dv);
} }
template<class T> template <class T>
void Do(std::list<T> &x, T &default_val) void Do(std::list<T>& x, T& default_val) {
{
DoList(x, default_val); DoList(x, default_val);
} }
template<class T> template <class T>
void DoList(std::list<T> &x, T &default_val) void DoList(std::list<T>& x, T& default_val) {
{
u32 list_size = (u32)x.size(); u32 list_size = (u32)x.size();
Do(list_size); Do(list_size);
x.resize(list_size, default_val); x.resize(list_size, default_val);
@ -441,15 +426,11 @@ public:
Do(*itr); Do(*itr);
} }
// Store STL sets. // Store STL sets.
template <class T> template <class T>
void Do(std::set<T *> &x) void Do(std::set<T*>& x) {
{ if (mode == MODE_READ) {
if (mode == MODE_READ) for (auto it = x.begin(), end = x.end(); it != end; ++it) {
{
for (auto it = x.begin(), end = x.end(); it != end; ++it)
{
if (*it != nullptr) if (*it != nullptr)
delete *it; delete *it;
} }
@ -458,39 +439,31 @@ public:
} }
template <class T> template <class T>
void Do(std::set<T> &x) void Do(std::set<T>& x) {
{
DoSet(x); DoSet(x);
} }
template <class T> template <class T>
void DoSet(std::set<T> &x) void DoSet(std::set<T>& x) {
{
unsigned int number = (unsigned int)x.size(); unsigned int number = (unsigned int)x.size();
Do(number); Do(number);
switch (mode) switch (mode) {
{ case MODE_READ: {
case MODE_READ:
{
x.clear(); x.clear();
while (number-- > 0) while (number-- > 0) {
{
T it = T(); T it = T();
Do(it); Do(it);
x.insert(it); x.insert(it);
} }
} } break;
break;
case MODE_WRITE: case MODE_WRITE:
case MODE_MEASURE: case MODE_MEASURE:
case MODE_VERIFY: case MODE_VERIFY: {
{
typename std::set<T>::iterator itr = x.begin(); typename std::set<T>::iterator itr = x.begin();
while (number-- > 0) while (number-- > 0)
Do(*itr++); Do(*itr++);
} } break;
break;
default: default:
LOG_ERROR(Common, "Savestate error: invalid mode %d.", mode); LOG_ERROR(Common, "Savestate error: invalid mode %d.", mode);
@ -498,15 +471,19 @@ public:
} }
// Store strings. // Store strings.
void Do(std::string &x) void Do(std::string& x) {
{
int stringLen = (int)x.length() + 1; int stringLen = (int)x.length() + 1;
Do(stringLen); Do(stringLen);
switch (mode) { switch (mode) {
case MODE_READ: x = (char*)*ptr; break; case MODE_READ:
case MODE_WRITE: memcpy(*ptr, x.c_str(), stringLen); break; x = (char*)*ptr;
case MODE_MEASURE: break; break;
case MODE_WRITE:
memcpy(*ptr, x.c_str(), stringLen);
break;
case MODE_MEASURE:
break;
case MODE_VERIFY: case MODE_VERIFY:
DEBUG_ASSERT_MSG((x == (char*)*ptr), DEBUG_ASSERT_MSG((x == (char*)*ptr),
"Savestate verification failure: \"%s\" != \"%s\" (at %p).\n", "Savestate verification failure: \"%s\" != \"%s\" (at %p).\n",
@ -516,15 +493,19 @@ public:
(*ptr) += stringLen; (*ptr) += stringLen;
} }
void Do(std::wstring &x) void Do(std::wstring& x) {
{ int stringLen = sizeof(wchar_t) * ((int)x.length() + 1);
int stringLen = sizeof(wchar_t)*((int)x.length() + 1);
Do(stringLen); Do(stringLen);
switch (mode) { switch (mode) {
case MODE_READ: x = (wchar_t*)*ptr; break; case MODE_READ:
case MODE_WRITE: memcpy(*ptr, x.c_str(), stringLen); break; x = (wchar_t*)*ptr;
case MODE_MEASURE: break; break;
case MODE_WRITE:
memcpy(*ptr, x.c_str(), stringLen);
break;
case MODE_MEASURE:
break;
case MODE_VERIFY: case MODE_VERIFY:
DEBUG_ASSERT_MSG((x == (wchar_t*)*ptr), DEBUG_ASSERT_MSG((x == (wchar_t*)*ptr),
"Savestate verification failure: \"%ls\" != \"%ls\" (at %p).\n", "Savestate verification failure: \"%ls\" != \"%ls\" (at %p).\n",
@ -534,15 +515,14 @@ public:
(*ptr) += stringLen; (*ptr) += stringLen;
} }
template<class T> template <class T>
void DoClass(T &x) { void DoClass(T& x) {
x.DoState(*this); x.DoState(*this);
} }
template<class T> template <class T>
void DoClass(T *&x) { void DoClass(T*& x) {
if (mode == MODE_READ) if (mode == MODE_READ) {
{
if (x != nullptr) if (x != nullptr)
delete x; delete x;
x = new T(); x = new T();
@ -550,81 +530,70 @@ public:
x->DoState(*this); x->DoState(*this);
} }
template<class T> template <class T>
void DoArray(T *x, int count) { void DoArray(T* x, int count) {
DoHelper<T>::DoArray(this, x, count); DoHelper<T>::DoArray(this, x, count);
} }
template<class T> template <class T>
void Do(T &x) { void Do(T& x) {
DoHelper<T>::Do(this, x); DoHelper<T>::Do(this, x);
} }
template<class T> template <class T>
void DoPOD(T &x) { void DoPOD(T& x) {
DoHelper<T>::Do(this, x); DoHelper<T>::Do(this, x);
} }
template<class T> template <class T>
void DoPointer(T* &x, T*const base) { void DoPointer(T*& x, T* const base) {
// pointers can be more than 2^31 apart, but you're using this function wrong if you need that much range // pointers can be more than 2^31 apart, but you're using this function wrong if you need
// that much range
s32 offset = x - base; s32 offset = x - base;
Do(offset); Do(offset);
if (mode == MODE_READ) if (mode == MODE_READ)
x = base + offset; x = base + offset;
} }
template<class T, LinkedListItem<T>* (*TNew)(), void (*TFree)(LinkedListItem<T>*), void (*TDo)(PointerWrap&, T*)> template <class T, LinkedListItem<T>* (*TNew)(), void (*TFree)(LinkedListItem<T>*),
void DoLinkedList(LinkedListItem<T>*& list_start, LinkedListItem<T>** list_end = nullptr) void (*TDo)(PointerWrap&, T*)>
{ void DoLinkedList(LinkedListItem<T>*& list_start, LinkedListItem<T>** list_end = nullptr) {
LinkedListItem<T>* list_cur = list_start; LinkedListItem<T>* list_cur = list_start;
LinkedListItem<T>* prev = nullptr; LinkedListItem<T>* prev = nullptr;
while (true) while (true) {
{
u8 shouldExist = (list_cur ? 1 : 0); u8 shouldExist = (list_cur ? 1 : 0);
Do(shouldExist); Do(shouldExist);
if (shouldExist == 1) if (shouldExist == 1) {
{
LinkedListItem<T>* cur = list_cur ? list_cur : TNew(); LinkedListItem<T>* cur = list_cur ? list_cur : TNew();
TDo(*this, (T*)cur); TDo(*this, (T*)cur);
if (!list_cur) if (!list_cur) {
{ if (mode == MODE_READ) {
if (mode == MODE_READ)
{
cur->next = nullptr; cur->next = nullptr;
list_cur = cur; list_cur = cur;
if (prev) if (prev)
prev->next = cur; prev->next = cur;
else else
list_start = cur; list_start = cur;
} } else {
else
{
TFree(cur); TFree(cur);
continue; continue;
} }
} }
} } else {
else if (mode == MODE_READ) {
{
if (mode == MODE_READ)
{
if (prev) if (prev)
prev->next = nullptr; prev->next = nullptr;
if (list_end) if (list_end)
*list_end = prev; *list_end = prev;
if (list_cur) if (list_cur) {
{
if (list_start == list_cur) if (list_start == list_cur)
list_start = nullptr; list_start = nullptr;
do do {
{
LinkedListItem<T>* next = list_cur->next; LinkedListItem<T>* next = list_cur->next;
TFree(list_cur); TFree(list_cur);
list_cur = next; list_cur = next;
} } while (list_cur);
while (list_cur);
} }
} }
break; break;
@ -634,13 +603,13 @@ public:
} }
} }
void DoMarker(const char* prevName, u32 arbitraryNumber=0x42) void DoMarker(const char* prevName, u32 arbitraryNumber = 0x42) {
{
u32 cookie = arbitraryNumber; u32 cookie = arbitraryNumber;
Do(cookie); Do(cookie);
if(mode == PointerWrap::MODE_READ && cookie != arbitraryNumber) if (mode == PointerWrap::MODE_READ && cookie != arbitraryNumber) {
{ LOG_ERROR(Common, "After \"%s\", found %d (0x%X) instead of save marker %d (0x%X). "
LOG_ERROR(Common, "After \"%s\", found %d (0x%X) instead of save marker %d (0x%X). Aborting savestate load...", prevName, cookie, cookie, arbitraryNumber, arbitraryNumber); "Aborting savestate load...",
prevName, cookie, cookie, arbitraryNumber, arbitraryNumber);
SetError(ERROR_FAILURE); SetError(ERROR_FAILURE);
} }
} }

View File

@ -5,7 +5,6 @@
#pragma once #pragma once
#include <cstddef> #include <cstddef>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/memory_util.h" #include "common/memory_util.h"
@ -14,24 +13,27 @@
// having to prefix them with gen-> or something similar. // having to prefix them with gen-> or something similar.
// Example implementation: // Example implementation:
// class JIT : public CodeBlock<ARMXEmitter> {} // class JIT : public CodeBlock<ARMXEmitter> {}
template<class T> class CodeBlock : public T, NonCopyable template <class T>
{ class CodeBlock : public T, NonCopyable {
private: private:
// A privately used function to set the executable RAM space to something invalid. // A privately used function to set the executable RAM space to something invalid.
// For debugging usefulness it should be used to set the RAM to a host specific breakpoint instruction // For debugging usefulness it should be used to set the RAM to a host specific breakpoint
// instruction
virtual void PoisonMemory() = 0; virtual void PoisonMemory() = 0;
protected: protected:
u8 *region; u8* region;
size_t region_size; size_t region_size;
public: public:
CodeBlock() : region(nullptr), region_size(0) {} CodeBlock() : region(nullptr), region_size(0) {}
virtual ~CodeBlock() { if (region) FreeCodeSpace(); } virtual ~CodeBlock() {
if (region)
FreeCodeSpace();
}
// Call this before you generate any code. // Call this before you generate any code.
void AllocCodeSpace(int size) void AllocCodeSpace(int size) {
{
region_size = size; region_size = size;
region = (u8*)AllocateExecutableMemory(region_size); region = (u8*)AllocateExecutableMemory(region_size);
T::SetCodePtr(region); T::SetCodePtr(region);
@ -39,15 +41,13 @@ public:
// Always clear code space with breakpoints, so that if someone accidentally executes // Always clear code space with breakpoints, so that if someone accidentally executes
// uninitialized, it just breaks into the debugger. // uninitialized, it just breaks into the debugger.
void ClearCodeSpace() void ClearCodeSpace() {
{
PoisonMemory(); PoisonMemory();
ResetCodePtr(); ResetCodePtr();
} }
// Call this when shutting down. Don't rely on the destructor, even though it'll do the job. // Call this when shutting down. Don't rely on the destructor, even though it'll do the job.
void FreeCodeSpace() void FreeCodeSpace() {
{
#ifdef __SYMBIAN32__ #ifdef __SYMBIAN32__
ResetExecutableMemory(region); ResetExecutableMemory(region);
#else #else
@ -57,33 +57,29 @@ public:
region_size = 0; region_size = 0;
} }
bool IsInSpace(const u8 *ptr) bool IsInSpace(const u8* ptr) {
{
return (ptr >= region) && (ptr < (region + region_size)); return (ptr >= region) && (ptr < (region + region_size));
} }
// Cannot currently be undone. Will write protect the entire code region. // Cannot currently be undone. Will write protect the entire code region.
// Start over if you need to change the code (call FreeCodeSpace(), AllocCodeSpace()). // Start over if you need to change the code (call FreeCodeSpace(), AllocCodeSpace()).
void WriteProtect() void WriteProtect() {
{
WriteProtectMemory(region, region_size, true); WriteProtectMemory(region, region_size, true);
} }
void ResetCodePtr() void ResetCodePtr() {
{
T::SetCodePtr(region); T::SetCodePtr(region);
} }
size_t GetSpaceLeft() const size_t GetSpaceLeft() const {
{
return region_size - (T::GetCodePtr() - region); return region_size - (T::GetCodePtr() - region);
} }
u8 *GetBasePtr() { u8* GetBasePtr() {
return region; return region;
} }
size_t GetOffset(const u8 *ptr) const { size_t GetOffset(const u8* ptr) const {
return ptr - region; return ptr - region;
} }
}; };

View File

@ -56,7 +56,7 @@ constexpr u8 Convert8To6(u8 value) {
* @return Result color decoded as Math::Vec4<u8> * @return Result color decoded as Math::Vec4<u8>
*/ */
inline const Math::Vec4<u8> DecodeRGBA8(const u8* bytes) { inline const Math::Vec4<u8> DecodeRGBA8(const u8* bytes) {
return { bytes[3], bytes[2], bytes[1], bytes[0] }; return {bytes[3], bytes[2], bytes[1], bytes[0]};
} }
/** /**
@ -65,7 +65,7 @@ inline const Math::Vec4<u8> DecodeRGBA8(const u8* bytes) {
* @return Result color decoded as Math::Vec4<u8> * @return Result color decoded as Math::Vec4<u8>
*/ */
inline const Math::Vec4<u8> DecodeRGB8(const u8* bytes) { inline const Math::Vec4<u8> DecodeRGB8(const u8* bytes) {
return { bytes[2], bytes[1], bytes[0], 255 }; return {bytes[2], bytes[1], bytes[0], 255};
} }
/** /**
@ -74,7 +74,7 @@ inline const Math::Vec4<u8> DecodeRGB8(const u8* bytes) {
* @return Result color decoded as Math::Vec4<u8> * @return Result color decoded as Math::Vec4<u8>
*/ */
inline const Math::Vec4<u8> DecodeRG8(const u8* bytes) { inline const Math::Vec4<u8> DecodeRG8(const u8* bytes) {
return { bytes[1], bytes[0], 0, 255 }; return {bytes[1], bytes[0], 0, 255};
} }
/** /**
@ -84,8 +84,8 @@ inline const Math::Vec4<u8> DecodeRG8(const u8* bytes) {
*/ */
inline const Math::Vec4<u8> DecodeRGB565(const u8* bytes) { inline const Math::Vec4<u8> DecodeRGB565(const u8* bytes) {
const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes); const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes);
return { Convert5To8((pixel >> 11) & 0x1F), Convert6To8((pixel >> 5) & 0x3F), return {Convert5To8((pixel >> 11) & 0x1F), Convert6To8((pixel >> 5) & 0x3F),
Convert5To8(pixel & 0x1F), 255 }; Convert5To8(pixel & 0x1F), 255};
} }
/** /**
@ -95,8 +95,8 @@ inline const Math::Vec4<u8> DecodeRGB565(const u8* bytes) {
*/ */
inline const Math::Vec4<u8> DecodeRGB5A1(const u8* bytes) { inline const Math::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes); const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes);
return { Convert5To8((pixel >> 11) & 0x1F), Convert5To8((pixel >> 6) & 0x1F), return {Convert5To8((pixel >> 11) & 0x1F), Convert5To8((pixel >> 6) & 0x1F),
Convert5To8((pixel >> 1) & 0x1F), Convert1To8(pixel & 0x1) }; Convert5To8((pixel >> 1) & 0x1F), Convert1To8(pixel & 0x1)};
} }
/** /**
@ -106,8 +106,8 @@ inline const Math::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
*/ */
inline const Math::Vec4<u8> DecodeRGBA4(const u8* bytes) { inline const Math::Vec4<u8> DecodeRGBA4(const u8* bytes) {
const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes); const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes);
return { Convert4To8((pixel >> 12) & 0xF), Convert4To8((pixel >> 8) & 0xF), return {Convert4To8((pixel >> 12) & 0xF), Convert4To8((pixel >> 8) & 0xF),
Convert4To8((pixel >> 4) & 0xF), Convert4To8(pixel & 0xF) }; Convert4To8((pixel >> 4) & 0xF), Convert4To8(pixel & 0xF)};
} }
/** /**
@ -134,7 +134,7 @@ inline u32 DecodeD24(const u8* bytes) {
* @return Resulting values stored as a Math::Vec2 * @return Resulting values stored as a Math::Vec2
*/ */
inline const Math::Vec2<u32> DecodeD24S8(const u8* bytes) { inline const Math::Vec2<u32> DecodeD24S8(const u8* bytes) {
return { static_cast<u32>((bytes[2] << 16) | (bytes[1] << 8) | bytes[0]), bytes[3] }; return {static_cast<u32>((bytes[2] << 16) | (bytes[1] << 8) | bytes[0]), bytes[3]};
} }
/** /**
@ -175,8 +175,8 @@ inline void EncodeRG8(const Math::Vec4<u8>& color, u8* bytes) {
* @param bytes Destination pointer to store encoded color * @param bytes Destination pointer to store encoded color
*/ */
inline void EncodeRGB565(const Math::Vec4<u8>& color, u8* bytes) { inline void EncodeRGB565(const Math::Vec4<u8>& color, u8* bytes) {
*reinterpret_cast<u16_le*>(bytes) = (Convert8To5(color.r()) << 11) | *reinterpret_cast<u16_le*>(bytes) =
(Convert8To6(color.g()) << 5) | Convert8To5(color.b()); (Convert8To5(color.r()) << 11) | (Convert8To6(color.g()) << 5) | Convert8To5(color.b());
} }
/** /**
@ -186,7 +186,8 @@ inline void EncodeRGB565(const Math::Vec4<u8>& color, u8* bytes) {
*/ */
inline void EncodeRGB5A1(const Math::Vec4<u8>& color, u8* bytes) { inline void EncodeRGB5A1(const Math::Vec4<u8>& color, u8* bytes) {
*reinterpret_cast<u16_le*>(bytes) = (Convert8To5(color.r()) << 11) | *reinterpret_cast<u16_le*>(bytes) = (Convert8To5(color.r()) << 11) |
(Convert8To5(color.g()) << 6) | (Convert8To5(color.b()) << 1) | Convert8To1(color.a()); (Convert8To5(color.g()) << 6) |
(Convert8To5(color.b()) << 1) | Convert8To1(color.a());
} }
/** /**
@ -196,7 +197,8 @@ inline void EncodeRGB5A1(const Math::Vec4<u8>& color, u8* bytes) {
*/ */
inline void EncodeRGBA4(const Math::Vec4<u8>& color, u8* bytes) { inline void EncodeRGBA4(const Math::Vec4<u8>& color, u8* bytes) {
*reinterpret_cast<u16_le*>(bytes) = (Convert8To4(color.r()) << 12) | *reinterpret_cast<u16_le*>(bytes) = (Convert8To4(color.r()) << 12) |
(Convert8To4(color.g()) << 8) | (Convert8To4(color.b()) << 4) | Convert8To4(color.a()); (Convert8To4(color.g()) << 8) |
(Convert8To4(color.b()) << 4) | Convert8To4(color.a());
} }
/** /**

View File

@ -7,14 +7,13 @@
#if !defined(ARCHITECTURE_x86_64) && !defined(_M_ARM) #if !defined(ARCHITECTURE_x86_64) && !defined(_M_ARM)
#include <cstdlib> // for exit #include <cstdlib> // for exit
#endif #endif
#include "common_types.h" #include "common_types.h"
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
/// Textually concatenates two tokens. The double-expansion is required by the C preprocessor. /// Textually concatenates two tokens. The double-expansion is required by the C preprocessor.
#define CONCAT2(x, y) DO_CONCAT2(x, y) #define CONCAT2(x, y) DO_CONCAT2(x, y)
#define DO_CONCAT2(x, y) x ## y #define DO_CONCAT2(x, y) x##y
// helper macro to properly align structure members. // helper macro to properly align structure members.
// Calling INSERT_PADDING_BYTES will add a new member variable with a name like "pad121", // Calling INSERT_PADDING_BYTES will add a new member variable with a name like "pad121",
@ -24,9 +23,9 @@
// Inlining // Inlining
#ifdef _WIN32 #ifdef _WIN32
#define FORCE_INLINE __forceinline #define FORCE_INLINE __forceinline
#else #else
#define FORCE_INLINE inline __attribute__((always_inline)) #define FORCE_INLINE inline __attribute__((always_inline))
#endif #endif
#ifndef _MSC_VER #ifndef _MSC_VER
@ -46,7 +45,8 @@
#else #else
inline u32 rotl(u32 x, int shift) { inline u32 rotl(u32 x, int shift) {
shift &= 31; shift &= 31;
if (!shift) return x; if (!shift)
return x;
return (x << shift) | (x >> (32 - shift)); return (x << shift) | (x >> (32 - shift));
} }
#endif #endif
@ -56,17 +56,18 @@ inline u32 rotl(u32 x, int shift) {
#else #else
inline u32 rotr(u32 x, int shift) { inline u32 rotr(u32 x, int shift) {
shift &= 31; shift &= 31;
if (!shift) return x; if (!shift)
return x;
return (x >> shift) | (x << (32 - shift)); return (x >> shift) | (x << (32 - shift));
} }
#endif #endif
inline u64 _rotl64(u64 x, unsigned int shift){ inline u64 _rotl64(u64 x, unsigned int shift) {
unsigned int n = shift % 64; unsigned int n = shift % 64;
return (x << n) | (x >> (64 - n)); return (x << n) | (x >> (64 - n));
} }
inline u64 _rotr64(u64 x, unsigned int shift){ inline u64 _rotr64(u64 x, unsigned int shift) {
unsigned int n = shift % 64; unsigned int n = shift % 64;
return (x >> n) | (x << (64 - n)); return (x >> n) | (x << (64 - n));
} }
@ -74,17 +75,17 @@ inline u64 _rotr64(u64 x, unsigned int shift){
#else // _MSC_VER #else // _MSC_VER
#if (_MSC_VER < 1900) #if (_MSC_VER < 1900)
// Function Cross-Compatibility // Function Cross-Compatibility
#define snprintf _snprintf #define snprintf _snprintf
#endif #endif
// Locale Cross-Compatibility // Locale Cross-Compatibility
#define locale_t _locale_t #define locale_t _locale_t
extern "C" { extern "C" {
__declspec(dllimport) void __stdcall DebugBreak(void); __declspec(dllimport) void __stdcall DebugBreak(void);
} }
#define Crash() {DebugBreak();} #define Crash() DebugBreak()
// cstdlib provides these on MSVC // cstdlib provides these on MSVC
#define rotr _rotr #define rotr _rotr

View File

@ -16,13 +16,13 @@
#define ROOT_DIR "." #define ROOT_DIR "."
#define USERDATA_DIR "user" #define USERDATA_DIR "user"
#ifdef USER_DIR #ifdef USER_DIR
#define EMU_DATA_DIR USER_DIR #define EMU_DATA_DIR USER_DIR
#else #else
#ifdef _WIN32 #ifdef _WIN32
#define EMU_DATA_DIR "Citra Emulator" #define EMU_DATA_DIR "Citra Emulator"
#else #else
#define EMU_DATA_DIR "citra-emu" #define EMU_DATA_DIR "citra-emu"
#endif #endif
#endif #endif
// Dirs in both User and Sys // Dirs in both User and Sys

View File

@ -4,10 +4,8 @@
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
#include "common/assert.h" #include "common/assert.h"
#include "common/key_map.h" #include "common/key_map.h"
#include "emu_window.h" #include "emu_window.h"
#include "video_core/video_core.h" #include "video_core/video_core.h"
@ -44,18 +42,17 @@ void EmuWindow::CirclePadUpdated(float x, float y) {
*/ */
static bool IsWithinTouchscreen(const EmuWindow::FramebufferLayout& layout, unsigned framebuffer_x, static bool IsWithinTouchscreen(const EmuWindow::FramebufferLayout& layout, unsigned framebuffer_x,
unsigned framebuffer_y) { unsigned framebuffer_y) {
return (framebuffer_y >= layout.bottom_screen.top && return (
framebuffer_y < layout.bottom_screen.bottom && framebuffer_y >= layout.bottom_screen.top && framebuffer_y < layout.bottom_screen.bottom &&
framebuffer_x >= layout.bottom_screen.left && framebuffer_x >= layout.bottom_screen.left && framebuffer_x < layout.bottom_screen.right);
framebuffer_x < layout.bottom_screen.right);
} }
std::tuple<unsigned,unsigned> EmuWindow::ClipToTouchScreen(unsigned new_x, unsigned new_y) { std::tuple<unsigned, unsigned> EmuWindow::ClipToTouchScreen(unsigned new_x, unsigned new_y) {
new_x = std::max(new_x, framebuffer_layout.bottom_screen.left); new_x = std::max(new_x, framebuffer_layout.bottom_screen.left);
new_x = std::min(new_x, framebuffer_layout.bottom_screen.right-1); new_x = std::min(new_x, framebuffer_layout.bottom_screen.right - 1);
new_y = std::max(new_y, framebuffer_layout.bottom_screen.top); new_y = std::max(new_y, framebuffer_layout.bottom_screen.top);
new_y = std::min(new_y, framebuffer_layout.bottom_screen.bottom-1); new_y = std::min(new_y, framebuffer_layout.bottom_screen.bottom - 1);
return std::make_tuple(new_x, new_y); return std::make_tuple(new_x, new_y);
} }
@ -64,9 +61,11 @@ void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
return; return;
touch_x = VideoCore::kScreenBottomWidth * (framebuffer_x - framebuffer_layout.bottom_screen.left) / touch_x = VideoCore::kScreenBottomWidth *
(framebuffer_x - framebuffer_layout.bottom_screen.left) /
(framebuffer_layout.bottom_screen.right - framebuffer_layout.bottom_screen.left); (framebuffer_layout.bottom_screen.right - framebuffer_layout.bottom_screen.left);
touch_y = VideoCore::kScreenBottomHeight * (framebuffer_y - framebuffer_layout.bottom_screen.top) / touch_y = VideoCore::kScreenBottomHeight *
(framebuffer_y - framebuffer_layout.bottom_screen.top) /
(framebuffer_layout.bottom_screen.bottom - framebuffer_layout.bottom_screen.top); (framebuffer_layout.bottom_screen.bottom - framebuffer_layout.bottom_screen.top);
touch_pressed = true; touch_pressed = true;
@ -90,16 +89,19 @@ void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) {
TouchPressed(framebuffer_x, framebuffer_y); TouchPressed(framebuffer_x, framebuffer_y);
} }
EmuWindow::FramebufferLayout EmuWindow::FramebufferLayout::DefaultScreenLayout(unsigned width, unsigned height) { EmuWindow::FramebufferLayout EmuWindow::FramebufferLayout::DefaultScreenLayout(unsigned width,
unsigned height) {
// When hiding the widget, the function receives a size of 0 // When hiding the widget, the function receives a size of 0
if (width == 0) width = 1; if (width == 0)
if (height == 0) height = 1; width = 1;
if (height == 0)
height = 1;
EmuWindow::FramebufferLayout res = { width, height, {}, {} }; EmuWindow::FramebufferLayout res = {width, height, {}, {}};
float window_aspect_ratio = static_cast<float>(height) / width; float window_aspect_ratio = static_cast<float>(height) / width;
float emulation_aspect_ratio = static_cast<float>(VideoCore::kScreenTopHeight * 2) / float emulation_aspect_ratio =
VideoCore::kScreenTopWidth; static_cast<float>(VideoCore::kScreenTopHeight * 2) / VideoCore::kScreenTopWidth;
if (window_aspect_ratio > emulation_aspect_ratio) { if (window_aspect_ratio > emulation_aspect_ratio) {
// Window is narrower than the emulation content => apply borders to the top and bottom // Window is narrower than the emulation content => apply borders to the top and bottom
@ -110,8 +112,9 @@ EmuWindow::FramebufferLayout EmuWindow::FramebufferLayout::DefaultScreenLayout(u
res.top_screen.top = (height - viewport_height) / 2; res.top_screen.top = (height - viewport_height) / 2;
res.top_screen.bottom = res.top_screen.top + viewport_height / 2; res.top_screen.bottom = res.top_screen.top + viewport_height / 2;
int bottom_width = static_cast<int>((static_cast<float>(VideoCore::kScreenBottomWidth) / int bottom_width = static_cast<int>(
VideoCore::kScreenTopWidth) * (res.top_screen.right - res.top_screen.left)); (static_cast<float>(VideoCore::kScreenBottomWidth) / VideoCore::kScreenTopWidth) *
(res.top_screen.right - res.top_screen.left));
int bottom_border = ((res.top_screen.right - res.top_screen.left) - bottom_width) / 2; int bottom_border = ((res.top_screen.right - res.top_screen.left) - bottom_width) / 2;
res.bottom_screen.left = bottom_border; res.bottom_screen.left = bottom_border;
@ -127,8 +130,9 @@ EmuWindow::FramebufferLayout EmuWindow::FramebufferLayout::DefaultScreenLayout(u
res.top_screen.top = 0; res.top_screen.top = 0;
res.top_screen.bottom = res.top_screen.top + height / 2; res.top_screen.bottom = res.top_screen.top + height / 2;
int bottom_width = static_cast<int>((static_cast<float>(VideoCore::kScreenBottomWidth) / int bottom_width = static_cast<int>(
VideoCore::kScreenTopWidth) * (res.top_screen.right - res.top_screen.left)); (static_cast<float>(VideoCore::kScreenBottomWidth) / VideoCore::kScreenTopWidth) *
(res.top_screen.right - res.top_screen.left));
int bottom_border = ((res.top_screen.right - res.top_screen.left) - bottom_width) / 2; int bottom_border = ((res.top_screen.right - res.top_screen.left) - bottom_width) / 2;
res.bottom_screen.left = res.top_screen.left + bottom_border; res.bottom_screen.left = res.top_screen.left + bottom_border;

View File

@ -6,10 +6,8 @@
#include <tuple> #include <tuple>
#include <utility> #include <utility>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/math_util.h" #include "common/math_util.h"
#include "core/hle/service/hid/hid.h" #include "core/hle/service/hid/hid.h"
/** /**
@ -30,15 +28,14 @@
* - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please * - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please
* re-read the upper points again and think about it if you don't see this. * re-read the upper points again and think about it if you don't see this.
*/ */
class EmuWindow class EmuWindow {
{
public: public:
/// Data structure to store emuwindow configuration /// Data structure to store emuwindow configuration
struct WindowConfig { struct WindowConfig {
bool fullscreen; bool fullscreen;
int res_width; int res_width;
int res_height; int res_height;
std::pair<unsigned,unsigned> min_client_area_size; std::pair<unsigned, unsigned> min_client_area_size;
}; };
/// Describes the layout of the window framebuffer (size and top/bottom screen positions) /// Describes the layout of the window framebuffer (size and top/bottom screen positions)
@ -193,15 +190,18 @@ public:
/** /**
* Returns currently active configuration. * Returns currently active configuration.
* @note Accesses to the returned object need not be consistent because it may be modified in another thread * @note Accesses to the returned object need not be consistent because it may be modified in
* another thread
*/ */
const WindowConfig& GetActiveConfig() const { const WindowConfig& GetActiveConfig() const {
return active_config; return active_config;
} }
/** /**
* Requests the internal configuration to be replaced by the specified argument at some point in the future. * Requests the internal configuration to be replaced by the specified argument at some point in
* @note This method is thread-safe, because it delays configuration changes to the GUI event loop. Hence there is no guarantee on when the requested configuration will be active. * the future.
* @note This method is thread-safe, because it delays configuration changes to the GUI event
* loop. Hence there is no guarantee on when the requested configuration will be active.
*/ */
void SetConfig(const WindowConfig& val) { void SetConfig(const WindowConfig& val) {
config = val; config = val;
@ -258,7 +258,7 @@ protected:
* Update internal client area size with the given parameter. * Update internal client area size with the given parameter.
* @note EmuWindow implementations will usually use this in window resize event handlers. * @note EmuWindow implementations will usually use this in window resize event handlers.
*/ */
void NotifyClientAreaSizeChanged(const std::pair<unsigned,unsigned>& size) { void NotifyClientAreaSizeChanged(const std::pair<unsigned, unsigned>& size) {
client_area_width = size.first; client_area_width = size.first;
client_area_height = size.second; client_area_height = size.second;
} }
@ -266,9 +266,11 @@ protected:
private: private:
/** /**
* Handler called when the minimal client area was requested to be changed via SetConfig. * Handler called when the minimal client area was requested to be changed via SetConfig.
* For the request to be honored, EmuWindow implementations will usually reimplement this function. * For the request to be honored, EmuWindow implementations will usually reimplement this
* function.
*/ */
virtual void OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) { virtual void OnMinimalClientAreaChangeRequest(
const std::pair<unsigned, unsigned>& minimal_size) {
// By default, ignore this request and do nothing. // By default, ignore this request and do nothing.
} }
@ -277,7 +279,8 @@ private:
unsigned client_area_width; ///< Current client width, should be set by window impl. unsigned client_area_width; ///< Current client width, should be set by window impl.
unsigned client_area_height; ///< Current client height, should be set by window impl. unsigned client_area_height; ///< Current client height, should be set by window impl.
WindowConfig config; ///< Internal configuration (changes pending for being applied in ProcessConfigurationChanges) WindowConfig config; ///< Internal configuration (changes pending for being applied in
/// ProcessConfigurationChanges)
WindowConfig active_config; ///< Internal active configuration WindowConfig active_config; ///< Internal active configuration
bool touch_pressed; ///< True if touchpad area is currently pressed, otherwise false bool touch_pressed; ///< True if touchpad area is currently pressed, otherwise false
@ -291,7 +294,7 @@ private:
/** /**
* Clip the provided coordinates to be inside the touchscreen area. * Clip the provided coordinates to be inside the touchscreen area.
*/ */
std::tuple<unsigned,unsigned> ClipToTouchScreen(unsigned new_x, unsigned new_y); std::tuple<unsigned, unsigned> ClipToTouchScreen(unsigned new_x, unsigned new_y);
Service::HID::PadState pad_state; Service::HID::PadState pad_state;
}; };

View File

@ -9,66 +9,63 @@
#include "common/logging/log.h" #include "common/logging/log.h"
#ifdef _WIN32 #ifdef _WIN32
#include <windows.h> #include <windows.h>
#include <shlobj.h> // for SHGetFolderPath // windows.h needs to be included before other windows headers
#include <shellapi.h> #include <commdlg.h> // for GetSaveFileName
#include <commdlg.h> // for GetSaveFileName #include <direct.h> // getcwd
#include <io.h> #include <io.h>
#include <direct.h> // getcwd #include <shellapi.h>
#include <tchar.h> #include <shlobj.h> // for SHGetFolderPath
#include <tchar.h>
#include "common/string_util.h"
#include "common/string_util.h" // 64 bit offsets for windows
#define fseeko _fseeki64
// 64 bit offsets for windows #define ftello _ftelli64
#define fseeko _fseeki64 #define atoll _atoi64
#define ftello _ftelli64 #define stat64 _stat64
#define atoll _atoi64 #define fstat64 _fstat64
#define stat64 _stat64 #define fileno _fileno
#define fstat64 _fstat64
#define fileno _fileno
#else #else
#ifdef __APPLE__ #ifdef __APPLE__
#include <sys/param.h> #include <sys/param.h>
#endif #endif
#include <cctype> #include <cctype>
#include <cerrno> #include <cerrno>
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include <dirent.h> #include <dirent.h>
#include <pwd.h> #include <pwd.h>
#include <unistd.h> #include <unistd.h>
#endif #endif
#if defined(__APPLE__) #if defined(__APPLE__)
#include <CoreFoundation/CFString.h> #include <CoreFoundation/CFBundle.h>
#include <CoreFoundation/CFURL.h> #include <CoreFoundation/CFString.h>
#include <CoreFoundation/CFBundle.h> #include <CoreFoundation/CFURL.h>
#endif #endif
#include <algorithm> #include <algorithm>
#include <sys/stat.h> #include <sys/stat.h>
#ifndef S_ISDIR #ifndef S_ISDIR
#define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR) #define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR)
#endif #endif
#ifdef BSD4_4 #ifdef BSD4_4
#define stat64 stat #define stat64 stat
#define fstat64 fstat #define fstat64 fstat
#endif #endif
// This namespace has various generic functions related to files and paths. // This namespace has various generic functions related to files and paths.
// The code still needs a ton of cleanup. // The code still needs a ton of cleanup.
// REMEMBER: strdup considered harmful! // REMEMBER: strdup considered harmful!
namespace FileUtil namespace FileUtil {
{
// Remove any ending forward slashes from directory paths // Remove any ending forward slashes from directory paths
// Modifies argument. // Modifies argument.
static void StripTailDirSlashes(std::string &fname) static void StripTailDirSlashes(std::string& fname) {
{ if (fname.length() > 1) {
if (fname.length() > 1)
{
size_t i = fname.length(); size_t i = fname.length();
while (i > 0 && fname[i - 1] == DIR_SEP_CHR) while (i > 0 && fname[i - 1] == DIR_SEP_CHR)
--i; --i;
@ -78,8 +75,7 @@ static void StripTailDirSlashes(std::string &fname)
} }
// Returns true if file filename exists // Returns true if file filename exists
bool Exists(const std::string &filename) bool Exists(const std::string& filename) {
{
struct stat64 file_info; struct stat64 file_info;
std::string copy(filename); std::string copy(filename);
@ -99,8 +95,7 @@ bool Exists(const std::string &filename)
} }
// Returns true if filename is a directory // Returns true if filename is a directory
bool IsDirectory(const std::string &filename) bool IsDirectory(const std::string& filename) {
{
struct stat64 file_info; struct stat64 file_info;
std::string copy(filename); std::string copy(filename);
@ -117,8 +112,8 @@ bool IsDirectory(const std::string &filename)
#endif #endif
if (result < 0) { if (result < 0) {
LOG_WARNING(Common_Filesystem, "stat failed on %s: %s", LOG_WARNING(Common_Filesystem, "stat failed on %s: %s", filename.c_str(),
filename.c_str(), GetLastErrorMsg()); GetLastErrorMsg());
return false; return false;
} }
@ -127,36 +122,32 @@ bool IsDirectory(const std::string &filename)
// Deletes a given filename, return true on success // Deletes a given filename, return true on success
// Doesn't supports deleting a directory // Doesn't supports deleting a directory
bool Delete(const std::string &filename) bool Delete(const std::string& filename) {
{
LOG_INFO(Common_Filesystem, "file %s", filename.c_str()); LOG_INFO(Common_Filesystem, "file %s", filename.c_str());
// Return true because we care about the file no // Return true because we care about the file no
// being there, not the actual delete. // being there, not the actual delete.
if (!Exists(filename)) if (!Exists(filename)) {
{
LOG_WARNING(Common_Filesystem, "%s does not exist", filename.c_str()); LOG_WARNING(Common_Filesystem, "%s does not exist", filename.c_str());
return true; return true;
} }
// We can't delete a directory // We can't delete a directory
if (IsDirectory(filename)) if (IsDirectory(filename)) {
{
LOG_ERROR(Common_Filesystem, "Failed: %s is a directory", filename.c_str()); LOG_ERROR(Common_Filesystem, "Failed: %s is a directory", filename.c_str());
return false; return false;
} }
#ifdef _WIN32 #ifdef _WIN32
if (!DeleteFileW(Common::UTF8ToUTF16W(filename).c_str())) if (!DeleteFileW(Common::UTF8ToUTF16W(filename).c_str())) {
{ LOG_ERROR(Common_Filesystem, "DeleteFile failed on %s: %s", filename.c_str(),
LOG_ERROR(Common_Filesystem, "DeleteFile failed on %s: %s", GetLastErrorMsg());
filename.c_str(), GetLastErrorMsg());
return false; return false;
} }
#else #else
if (unlink(filename.c_str()) == -1) { if (unlink(filename.c_str()) == -1) {
LOG_ERROR(Common_Filesystem, "unlink failed on %s: %s", LOG_ERROR(Common_Filesystem, "unlink failed on %s: %s", filename.c_str(),
filename.c_str(), GetLastErrorMsg()); GetLastErrorMsg());
return false; return false;
} }
#endif #endif
@ -165,16 +156,15 @@ bool Delete(const std::string &filename)
} }
// Returns true if successful, or path already exists. // Returns true if successful, or path already exists.
bool CreateDir(const std::string &path) bool CreateDir(const std::string& path) {
{
LOG_TRACE(Common_Filesystem, "directory %s", path.c_str()); LOG_TRACE(Common_Filesystem, "directory %s", path.c_str());
#ifdef _WIN32 #ifdef _WIN32
if (::CreateDirectoryW(Common::UTF8ToUTF16W(path).c_str(), nullptr)) if (::CreateDirectoryW(Common::UTF8ToUTF16W(path).c_str(), nullptr))
return true; return true;
DWORD error = GetLastError(); DWORD error = GetLastError();
if (error == ERROR_ALREADY_EXISTS) if (error == ERROR_ALREADY_EXISTS) {
{ LOG_WARNING(Common_Filesystem, "CreateDirectory failed on %s: already exists",
LOG_WARNING(Common_Filesystem, "CreateDirectory failed on %s: already exists", path.c_str()); path.c_str());
return true; return true;
} }
LOG_ERROR(Common_Filesystem, "CreateDirectory failed on %s: %i", path.c_str(), error); LOG_ERROR(Common_Filesystem, "CreateDirectory failed on %s: %i", path.c_str(), error);
@ -185,8 +175,7 @@ bool CreateDir(const std::string &path)
int err = errno; int err = errno;
if (err == EEXIST) if (err == EEXIST) {
{
LOG_WARNING(Common_Filesystem, "mkdir failed on %s: already exists", path.c_str()); LOG_WARNING(Common_Filesystem, "mkdir failed on %s: already exists", path.c_str());
return true; return true;
} }
@ -197,20 +186,17 @@ bool CreateDir(const std::string &path)
} }
// Creates the full path of fullPath returns true on success // Creates the full path of fullPath returns true on success
bool CreateFullPath(const std::string &fullPath) bool CreateFullPath(const std::string& fullPath) {
{
int panicCounter = 100; int panicCounter = 100;
LOG_TRACE(Common_Filesystem, "path %s", fullPath.c_str()); LOG_TRACE(Common_Filesystem, "path %s", fullPath.c_str());
if (FileUtil::Exists(fullPath)) if (FileUtil::Exists(fullPath)) {
{
LOG_WARNING(Common_Filesystem, "path exists %s", fullPath.c_str()); LOG_WARNING(Common_Filesystem, "path exists %s", fullPath.c_str());
return true; return true;
} }
size_t position = 0; size_t position = 0;
while (true) while (true) {
{
// Find next sub path // Find next sub path
position = fullPath.find(DIR_SEP_CHR, position); position = fullPath.find(DIR_SEP_CHR, position);
@ -227,8 +213,7 @@ bool CreateFullPath(const std::string &fullPath)
// A safety check // A safety check
panicCounter--; panicCounter--;
if (panicCounter <= 0) if (panicCounter <= 0) {
{
LOG_ERROR(Common, "CreateFullPath: directory structure is too deep"); LOG_ERROR(Common, "CreateFullPath: directory structure is too deep");
return false; return false;
} }
@ -236,15 +221,12 @@ bool CreateFullPath(const std::string &fullPath)
} }
} }
// Deletes a directory filename, returns true on success // Deletes a directory filename, returns true on success
bool DeleteDir(const std::string &filename) bool DeleteDir(const std::string& filename) {
{
LOG_INFO(Common_Filesystem, "directory %s", filename.c_str()); LOG_INFO(Common_Filesystem, "directory %s", filename.c_str());
// check if a directory // check if a directory
if (!FileUtil::IsDirectory(filename)) if (!FileUtil::IsDirectory(filename)) {
{
LOG_ERROR(Common_Filesystem, "Not a directory %s", filename.c_str()); LOG_ERROR(Common_Filesystem, "Not a directory %s", filename.c_str());
return false; return false;
} }
@ -262,71 +244,63 @@ bool DeleteDir(const std::string &filename)
} }
// renames file srcFilename to destFilename, returns true on success // renames file srcFilename to destFilename, returns true on success
bool Rename(const std::string &srcFilename, const std::string &destFilename) bool Rename(const std::string& srcFilename, const std::string& destFilename) {
{ LOG_TRACE(Common_Filesystem, "%s --> %s", srcFilename.c_str(), destFilename.c_str());
LOG_TRACE(Common_Filesystem, "%s --> %s",
srcFilename.c_str(), destFilename.c_str());
#ifdef _WIN32 #ifdef _WIN32
if (_wrename(Common::UTF8ToUTF16W(srcFilename).c_str(), Common::UTF8ToUTF16W(destFilename).c_str()) == 0) if (_wrename(Common::UTF8ToUTF16W(srcFilename).c_str(),
Common::UTF8ToUTF16W(destFilename).c_str()) == 0)
return true; return true;
#else #else
if (rename(srcFilename.c_str(), destFilename.c_str()) == 0) if (rename(srcFilename.c_str(), destFilename.c_str()) == 0)
return true; return true;
#endif #endif
LOG_ERROR(Common_Filesystem, "failed %s --> %s: %s", LOG_ERROR(Common_Filesystem, "failed %s --> %s: %s", srcFilename.c_str(), destFilename.c_str(),
srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); GetLastErrorMsg());
return false; return false;
} }
// copies file srcFilename to destFilename, returns true on success // copies file srcFilename to destFilename, returns true on success
bool Copy(const std::string &srcFilename, const std::string &destFilename) bool Copy(const std::string& srcFilename, const std::string& destFilename) {
{ LOG_TRACE(Common_Filesystem, "%s --> %s", srcFilename.c_str(), destFilename.c_str());
LOG_TRACE(Common_Filesystem, "%s --> %s",
srcFilename.c_str(), destFilename.c_str());
#ifdef _WIN32 #ifdef _WIN32
if (CopyFileW(Common::UTF8ToUTF16W(srcFilename).c_str(), Common::UTF8ToUTF16W(destFilename).c_str(), FALSE)) if (CopyFileW(Common::UTF8ToUTF16W(srcFilename).c_str(),
Common::UTF8ToUTF16W(destFilename).c_str(), FALSE))
return true; return true;
LOG_ERROR(Common_Filesystem, "failed %s --> %s: %s", LOG_ERROR(Common_Filesystem, "failed %s --> %s: %s", srcFilename.c_str(), destFilename.c_str(),
srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); GetLastErrorMsg());
return false; return false;
#else #else
// buffer size // buffer size
#define BSIZE 1024 #define BSIZE 1024
char buffer[BSIZE]; char buffer[BSIZE];
// Open input file // Open input file
FILE *input = fopen(srcFilename.c_str(), "rb"); FILE* input = fopen(srcFilename.c_str(), "rb");
if (!input) if (!input) {
{ LOG_ERROR(Common_Filesystem, "opening input failed %s --> %s: %s", srcFilename.c_str(),
LOG_ERROR(Common_Filesystem, "opening input failed %s --> %s: %s", destFilename.c_str(), GetLastErrorMsg());
srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
return false; return false;
} }
// open output file // open output file
FILE *output = fopen(destFilename.c_str(), "wb"); FILE* output = fopen(destFilename.c_str(), "wb");
if (!output) if (!output) {
{
fclose(input); fclose(input);
LOG_ERROR(Common_Filesystem, "opening output failed %s --> %s: %s", LOG_ERROR(Common_Filesystem, "opening output failed %s --> %s: %s", srcFilename.c_str(),
srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); destFilename.c_str(), GetLastErrorMsg());
return false; return false;
} }
// copy loop // copy loop
while (!feof(input)) while (!feof(input)) {
{
// read input // read input
int rnum = fread(buffer, sizeof(char), BSIZE, input); int rnum = fread(buffer, sizeof(char), BSIZE, input);
if (rnum != BSIZE) if (rnum != BSIZE) {
{ if (ferror(input) != 0) {
if (ferror(input) != 0) LOG_ERROR(Common_Filesystem, "failed reading from source, %s --> %s: %s",
{
LOG_ERROR(Common_Filesystem,
"failed reading from source, %s --> %s: %s",
srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
goto bail; goto bail;
} }
@ -334,10 +308,8 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename)
// write output // write output
int wnum = fwrite(buffer, sizeof(char), rnum, output); int wnum = fwrite(buffer, sizeof(char), rnum, output);
if (wnum != rnum) if (wnum != rnum) {
{ LOG_ERROR(Common_Filesystem, "failed writing to output, %s --> %s: %s",
LOG_ERROR(Common_Filesystem,
"failed writing to output, %s --> %s: %s",
srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
goto bail; goto bail;
} }
@ -356,16 +328,13 @@ bail:
} }
// Returns the size of filename (64bit) // Returns the size of filename (64bit)
u64 GetSize(const std::string &filename) u64 GetSize(const std::string& filename) {
{ if (!Exists(filename)) {
if (!Exists(filename))
{
LOG_ERROR(Common_Filesystem, "failed %s: No such file", filename.c_str()); LOG_ERROR(Common_Filesystem, "failed %s: No such file", filename.c_str());
return 0; return 0;
} }
if (IsDirectory(filename)) if (IsDirectory(filename)) {
{
LOG_ERROR(Common_Filesystem, "failed %s: is a directory", filename.c_str()); LOG_ERROR(Common_Filesystem, "failed %s: is a directory", filename.c_str());
return 0; return 0;
} }
@ -377,65 +346,54 @@ u64 GetSize(const std::string &filename)
if (stat64(filename.c_str(), &buf) == 0) if (stat64(filename.c_str(), &buf) == 0)
#endif #endif
{ {
LOG_TRACE(Common_Filesystem, "%s: %lld", LOG_TRACE(Common_Filesystem, "%s: %lld", filename.c_str(), (long long)buf.st_size);
filename.c_str(), (long long)buf.st_size);
return buf.st_size; return buf.st_size;
} }
LOG_ERROR(Common_Filesystem, "Stat failed %s: %s", LOG_ERROR(Common_Filesystem, "Stat failed %s: %s", filename.c_str(), GetLastErrorMsg());
filename.c_str(), GetLastErrorMsg());
return 0; return 0;
} }
// Overloaded GetSize, accepts file descriptor // Overloaded GetSize, accepts file descriptor
u64 GetSize(const int fd) u64 GetSize(const int fd) {
{
struct stat64 buf; struct stat64 buf;
if (fstat64(fd, &buf) != 0) { if (fstat64(fd, &buf) != 0) {
LOG_ERROR(Common_Filesystem, "GetSize: stat failed %i: %s", LOG_ERROR(Common_Filesystem, "GetSize: stat failed %i: %s", fd, GetLastErrorMsg());
fd, GetLastErrorMsg());
return 0; return 0;
} }
return buf.st_size; return buf.st_size;
} }
// Overloaded GetSize, accepts FILE* // Overloaded GetSize, accepts FILE*
u64 GetSize(FILE *f) u64 GetSize(FILE* f) {
{
// can't use off_t here because it can be 32-bit // can't use off_t here because it can be 32-bit
u64 pos = ftello(f); u64 pos = ftello(f);
if (fseeko(f, 0, SEEK_END) != 0) { if (fseeko(f, 0, SEEK_END) != 0) {
LOG_ERROR(Common_Filesystem, "GetSize: seek failed %p: %s", LOG_ERROR(Common_Filesystem, "GetSize: seek failed %p: %s", f, GetLastErrorMsg());
f, GetLastErrorMsg());
return 0; return 0;
} }
u64 size = ftello(f); u64 size = ftello(f);
if ((size != pos) && (fseeko(f, pos, SEEK_SET) != 0)) { if ((size != pos) && (fseeko(f, pos, SEEK_SET) != 0)) {
LOG_ERROR(Common_Filesystem, "GetSize: seek failed %p: %s", LOG_ERROR(Common_Filesystem, "GetSize: seek failed %p: %s", f, GetLastErrorMsg());
f, GetLastErrorMsg());
return 0; return 0;
} }
return size; return size;
} }
// creates an empty file filename, returns true on success // creates an empty file filename, returns true on success
bool CreateEmptyFile(const std::string &filename) bool CreateEmptyFile(const std::string& filename) {
{
LOG_TRACE(Common_Filesystem, "%s", filename.c_str()); LOG_TRACE(Common_Filesystem, "%s", filename.c_str());
if (!FileUtil::IOFile(filename, "wb")) if (!FileUtil::IOFile(filename, "wb")) {
{ LOG_ERROR(Common_Filesystem, "failed %s: %s", filename.c_str(), GetLastErrorMsg());
LOG_ERROR(Common_Filesystem, "failed %s: %s",
filename.c_str(), GetLastErrorMsg());
return false; return false;
} }
return true; return true;
} }
bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string& directory,
bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directory, DirectoryEntryCallable callback) DirectoryEntryCallable callback) {
{
LOG_TRACE(Common_Filesystem, "directory %s", directory.c_str()); LOG_TRACE(Common_Filesystem, "directory %s", directory.c_str());
// How many files + directories we found // How many files + directories we found
@ -457,7 +415,7 @@ bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directo
do { do {
const std::string virtual_name(Common::UTF16ToUTF8(ffd.cFileName)); const std::string virtual_name(Common::UTF16ToUTF8(ffd.cFileName));
#else #else
DIR *dirp = opendir(directory.c_str()); DIR* dirp = opendir(directory.c_str());
if (!dirp) if (!dirp)
return false; return false;
@ -493,8 +451,8 @@ bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directo
return true; return true;
} }
unsigned ScanDirectoryTree(const std::string &directory, FSTEntry& parent_entry, unsigned int recursion) unsigned ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry,
{ unsigned int recursion) {
const auto callback = [recursion, &parent_entry](unsigned* num_entries_out, const auto callback = [recursion, &parent_entry](unsigned* num_entries_out,
const std::string& directory, const std::string& directory,
const std::string& virtual_name) -> bool { const std::string& virtual_name) -> bool {
@ -526,11 +484,8 @@ unsigned ScanDirectoryTree(const std::string &directory, FSTEntry& parent_entry,
return ForeachDirectoryEntry(&num_entries, directory, callback) ? num_entries : 0; return ForeachDirectoryEntry(&num_entries, directory, callback) ? num_entries : 0;
} }
bool DeleteDirRecursively(const std::string& directory, unsigned int recursion) {
bool DeleteDirRecursively(const std::string &directory, unsigned int recursion) const auto callback = [recursion](unsigned* num_entries_out, const std::string& directory,
{
const auto callback = [recursion](unsigned* num_entries_out,
const std::string& directory,
const std::string& virtual_name) -> bool { const std::string& virtual_name) -> bool {
std::string new_path = directory + DIR_SEP_CHR + virtual_name; std::string new_path = directory + DIR_SEP_CHR + virtual_name;
@ -551,53 +506,53 @@ bool DeleteDirRecursively(const std::string &directory, unsigned int recursion)
} }
// Create directory and copy contents (does not overwrite existing files) // Create directory and copy contents (does not overwrite existing files)
void CopyDir(const std::string &source_path, const std::string &dest_path) void CopyDir(const std::string& source_path, const std::string& dest_path) {
{
#ifndef _WIN32 #ifndef _WIN32
if (source_path == dest_path) return; if (source_path == dest_path)
if (!FileUtil::Exists(source_path)) return; return;
if (!FileUtil::Exists(dest_path)) FileUtil::CreateFullPath(dest_path); if (!FileUtil::Exists(source_path))
return;
if (!FileUtil::Exists(dest_path))
FileUtil::CreateFullPath(dest_path);
DIR *dirp = opendir(source_path.c_str()); DIR* dirp = opendir(source_path.c_str());
if (!dirp) return; if (!dirp)
return;
while (struct dirent* result = readdir(dirp)) { while (struct dirent* result = readdir(dirp)) {
const std::string virtualName(result->d_name); const std::string virtualName(result->d_name);
// check for "." and ".." // check for "." and ".."
if (((virtualName[0] == '.') && (virtualName[1] == '\0')) || if (((virtualName[0] == '.') && (virtualName[1] == '\0')) ||
((virtualName[0] == '.') && (virtualName[1] == '.') && ((virtualName[0] == '.') && (virtualName[1] == '.') && (virtualName[2] == '\0')))
(virtualName[2] == '\0')))
continue; continue;
std::string source, dest; std::string source, dest;
source = source_path + virtualName; source = source_path + virtualName;
dest = dest_path + virtualName; dest = dest_path + virtualName;
if (IsDirectory(source)) if (IsDirectory(source)) {
{
source += '/'; source += '/';
dest += '/'; dest += '/';
if (!FileUtil::Exists(dest)) FileUtil::CreateFullPath(dest); if (!FileUtil::Exists(dest))
FileUtil::CreateFullPath(dest);
CopyDir(source, dest); CopyDir(source, dest);
} } else if (!FileUtil::Exists(dest))
else if (!FileUtil::Exists(dest)) FileUtil::Copy(source, dest); FileUtil::Copy(source, dest);
} }
closedir(dirp); closedir(dirp);
#endif #endif
} }
// Returns the current directory // Returns the current directory
std::string GetCurrentDir() std::string GetCurrentDir() {
{ // Get the current working directory (getcwd uses malloc)
// Get the current working directory (getcwd uses malloc)
#ifdef _WIN32 #ifdef _WIN32
wchar_t *dir; wchar_t* dir;
if (!(dir = _wgetcwd(nullptr, 0))) { if (!(dir = _wgetcwd(nullptr, 0))) {
#else #else
char *dir; char* dir;
if (!(dir = getcwd(nullptr, 0))) { if (!(dir = getcwd(nullptr, 0))) {
#endif #endif
LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: %s", LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: %s", GetLastErrorMsg());
GetLastErrorMsg());
return nullptr; return nullptr;
} }
#ifdef _WIN32 #ifdef _WIN32
@ -610,8 +565,7 @@ std::string GetCurrentDir()
} }
// Sets the current directory to the given directory // Sets the current directory to the given directory
bool SetCurrentDir(const std::string &directory) bool SetCurrentDir(const std::string& directory) {
{
#ifdef _WIN32 #ifdef _WIN32
return _wchdir(Common::UTF8ToUTF16W(directory).c_str()) == 0; return _wchdir(Common::UTF8ToUTF16W(directory).c_str()) == 0;
#else #else
@ -620,8 +574,7 @@ bool SetCurrentDir(const std::string &directory)
} }
#if defined(__APPLE__) #if defined(__APPLE__)
std::string GetBundleDirectory() std::string GetBundleDirectory() {
{
CFURLRef BundleRef; CFURLRef BundleRef;
char AppBundlePath[MAXPATHLEN]; char AppBundlePath[MAXPATHLEN];
// Get the main bundle for the app // Get the main bundle for the app
@ -636,11 +589,9 @@ std::string GetBundleDirectory()
#endif #endif
#ifdef _WIN32 #ifdef _WIN32
std::string& GetExeDirectory() std::string& GetExeDirectory() {
{
static std::string exe_path; static std::string exe_path;
if (exe_path.empty()) if (exe_path.empty()) {
{
wchar_t wchar_exe_path[2048]; wchar_t wchar_exe_path[2048];
GetModuleFileNameW(nullptr, wchar_exe_path, 2048); GetModuleFileNameW(nullptr, wchar_exe_path, 2048);
exe_path = Common::UTF16ToUTF8(wchar_exe_path); exe_path = Common::UTF16ToUTF8(wchar_exe_path);
@ -660,7 +611,8 @@ static const std::string& GetHomeDirectory() {
home_path = envvar; home_path = envvar;
} else { } else {
auto pw = getpwuid(getuid()); auto pw = getpwuid(getuid());
ASSERT_MSG(pw, "$HOME isnt defined, and the current user cant be found in /etc/passwd."); ASSERT_MSG(pw,
"$HOME isnt defined, and the current user cant be found in /etc/passwd.");
home_path = pw->pw_dir; home_path = pw->pw_dir;
} }
} }
@ -699,11 +651,10 @@ static const std::string GetUserDirectory(const std::string& envvar) {
} }
#endif #endif
std::string GetSysDirectory() std::string GetSysDirectory() {
{
std::string sysDir; std::string sysDir;
#if defined (__APPLE__) #if defined(__APPLE__)
sysDir = GetBundleDirectory(); sysDir = GetBundleDirectory();
sysDir += DIR_SEP; sysDir += DIR_SEP;
sysDir += SYSDATA_DIR; sysDir += SYSDATA_DIR;
@ -718,13 +669,11 @@ std::string GetSysDirectory()
// Returns a string with a Citra data dir or file in the user's home // Returns a string with a Citra data dir or file in the user's home
// directory. To be used in "multi-user" mode (that is, installed). // directory. To be used in "multi-user" mode (that is, installed).
const std::string& GetUserPath(const unsigned int DirIDX, const std::string &newPath) const std::string& GetUserPath(const unsigned int DirIDX, const std::string& newPath) {
{
static std::string paths[NUM_PATH_INDICES]; static std::string paths[NUM_PATH_INDICES];
// Set up all paths and files on the first run // Set up all paths and files on the first run
if (paths[D_USER_IDX].empty()) if (paths[D_USER_IDX].empty()) {
{
#ifdef _WIN32 #ifdef _WIN32
paths[D_USER_IDX] = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP; paths[D_USER_IDX] = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP;
paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP; paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP;
@ -764,20 +713,15 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new
paths[F_MAINLOG_IDX] = paths[D_LOGS_IDX] + MAIN_LOG; paths[F_MAINLOG_IDX] = paths[D_LOGS_IDX] + MAIN_LOG;
} }
if (!newPath.empty()) if (!newPath.empty()) {
{ if (!FileUtil::IsDirectory(newPath)) {
if (!FileUtil::IsDirectory(newPath))
{
LOG_ERROR(Common_Filesystem, "Invalid path specified %s", newPath.c_str()); LOG_ERROR(Common_Filesystem, "Invalid path specified %s", newPath.c_str());
return paths[DirIDX]; return paths[DirIDX];
} } else {
else
{
paths[DirIDX] = newPath; paths[DirIDX] = newPath;
} }
switch (DirIDX) switch (DirIDX) {
{
case D_ROOT_IDX: case D_ROOT_IDX:
paths[D_USER_IDX] = paths[D_ROOT_IDX] + DIR_SEP; paths[D_USER_IDX] = paths[D_ROOT_IDX] + DIR_SEP;
paths[D_SYSCONF_IDX] = paths[D_USER_IDX] + SYSCONF_DIR + DIR_SEP; paths[D_SYSCONF_IDX] = paths[D_USER_IDX] + SYSCONF_DIR + DIR_SEP;
@ -828,13 +772,11 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new
return paths[DirIDX]; return paths[DirIDX];
} }
size_t WriteStringToFile(bool text_file, const std::string &str, const char *filename) size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename) {
{
return FileUtil::IOFile(filename, text_file ? "w" : "wb").WriteBytes(str.data(), str.size()); return FileUtil::IOFile(filename, text_file ? "w" : "wb").WriteBytes(str.data(), str.size());
} }
size_t ReadFileToString(bool text_file, const char *filename, std::string &str) size_t ReadFileToString(bool text_file, const char* filename, std::string& str) {
{
IOFile file(filename, text_file ? "r" : "rb"); IOFile file(filename, text_file ? "r" : "rb");
if (!file) if (!file)
@ -886,42 +828,35 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
} }
} }
IOFile::IOFile() IOFile::IOFile() {}
{
}
IOFile::IOFile(const std::string& filename, const char openmode[]) IOFile::IOFile(const std::string& filename, const char openmode[]) {
{
Open(filename, openmode); Open(filename, openmode);
} }
IOFile::~IOFile() IOFile::~IOFile() {
{
Close(); Close();
} }
IOFile::IOFile(IOFile&& other) IOFile::IOFile(IOFile&& other) {
{
Swap(other); Swap(other);
} }
IOFile& IOFile::operator=(IOFile&& other) IOFile& IOFile::operator=(IOFile&& other) {
{
Swap(other); Swap(other);
return *this; return *this;
} }
void IOFile::Swap(IOFile& other) void IOFile::Swap(IOFile& other) {
{
std::swap(m_file, other.m_file); std::swap(m_file, other.m_file);
std::swap(m_good, other.m_good); std::swap(m_good, other.m_good);
} }
bool IOFile::Open(const std::string& filename, const char openmode[]) bool IOFile::Open(const std::string& filename, const char openmode[]) {
{
Close(); Close();
#ifdef _WIN32 #ifdef _WIN32
_wfopen_s(&m_file, Common::UTF8ToUTF16W(filename).c_str(), Common::UTF8ToUTF16W(openmode).c_str()); _wfopen_s(&m_file, Common::UTF8ToUTF16W(filename).c_str(),
Common::UTF8ToUTF16W(openmode).c_str());
#else #else
m_file = fopen(filename.c_str(), openmode); m_file = fopen(filename.c_str(), openmode);
#endif #endif
@ -930,8 +865,7 @@ bool IOFile::Open(const std::string& filename, const char openmode[])
return m_good; return m_good;
} }
bool IOFile::Close() bool IOFile::Close() {
{
if (!IsOpen() || 0 != std::fclose(m_file)) if (!IsOpen() || 0 != std::fclose(m_file))
m_good = false; m_good = false;
@ -939,41 +873,37 @@ bool IOFile::Close()
return m_good; return m_good;
} }
u64 IOFile::GetSize() const u64 IOFile::GetSize() const {
{
if (IsOpen()) if (IsOpen())
return FileUtil::GetSize(m_file); return FileUtil::GetSize(m_file);
return 0; return 0;
} }
bool IOFile::Seek(s64 off, int origin) bool IOFile::Seek(s64 off, int origin) {
{
if (!IsOpen() || 0 != fseeko(m_file, off, origin)) if (!IsOpen() || 0 != fseeko(m_file, off, origin))
m_good = false; m_good = false;
return m_good; return m_good;
} }
u64 IOFile::Tell() const u64 IOFile::Tell() const {
{
if (IsOpen()) if (IsOpen())
return ftello(m_file); return ftello(m_file);
return -1; return -1;
} }
bool IOFile::Flush() bool IOFile::Flush() {
{
if (!IsOpen() || 0 != std::fflush(m_file)) if (!IsOpen() || 0 != std::fflush(m_file))
m_good = false; m_good = false;
return m_good; return m_good;
} }
bool IOFile::Resize(u64 size) bool IOFile::Resize(u64 size) {
{ if (!IsOpen() ||
if (!IsOpen() || 0 != 0 !=
#ifdef _WIN32 #ifdef _WIN32
// ector: _chsize sucks, not 64-bit safe // ector: _chsize sucks, not 64-bit safe
// F|RES: changed to _chsize_s. i think it is 64-bit safe // F|RES: changed to _chsize_s. i think it is 64-bit safe

Some files were not shown because too many files have changed in this diff Show More