mirror of
				https://git.zaroz.cloud/nintendo-back-up/yuzu/yuzu.git
				synced 2025-05-12 00:45:25 +00:00 
			
		
		
		
	Merge pull request #2086 from linkmauve/clang-format
Add clang-format as part of our {commit,travis}-time checks
			
			
This commit is contained in:
		
						commit
						d5d2ca8058
					
				@ -1,4 +1,4 @@
 | 
			
		||||
#!/bin/sh
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
 | 
			
		||||
set -e
 | 
			
		||||
set -x
 | 
			
		||||
@ -9,6 +9,39 @@ if grep -nr '\s$' src *.yml *.txt *.md Doxyfile .gitignore .gitmodules .travis*
 | 
			
		||||
    exit 1
 | 
			
		||||
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 [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then
 | 
			
		||||
    export CC=gcc-6
 | 
			
		||||
 | 
			
		||||
@ -17,6 +17,7 @@ addons:
 | 
			
		||||
  apt:
 | 
			
		||||
    sources:
 | 
			
		||||
      - ubuntu-toolchain-r-test
 | 
			
		||||
      - llvm-toolchain-precise
 | 
			
		||||
    packages:
 | 
			
		||||
      - gcc-6
 | 
			
		||||
      - g++-6
 | 
			
		||||
@ -25,6 +26,7 @@ addons:
 | 
			
		||||
      - xorg-dev
 | 
			
		||||
      - lib32stdc++6 # For CMake
 | 
			
		||||
      - lftp # To upload builds
 | 
			
		||||
      - clang-format-4.0
 | 
			
		||||
 | 
			
		||||
cache:
 | 
			
		||||
  directories:
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
#!/bin/sh
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
 | 
			
		||||
# Enforce citra's whitespace policy
 | 
			
		||||
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 ! 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.
 | 
			
		||||
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
 | 
			
		||||
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.
 | 
			
		||||
If you know what you are doing, you can try 'git commit --no-verify' to bypass the check.
 | 
			
		||||
END
 | 
			
		||||
    exit 1
 | 
			
		||||
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
									
								
							
							
						
						
									
										88
									
								
								src/.clang-format
									
									
									
									
									
										Normal 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
 | 
			
		||||
...
 | 
			
		||||
@ -4,14 +4,12 @@
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
#include "audio_core/audio_core.h"
 | 
			
		||||
#include "audio_core/hle/dsp.h"
 | 
			
		||||
#include "audio_core/hle/pipe.h"
 | 
			
		||||
#include "audio_core/null_sink.h"
 | 
			
		||||
#include "audio_core/sink.h"
 | 
			
		||||
#include "audio_core/sink_details.h"
 | 
			
		||||
 | 
			
		||||
#include "core/core_timing.h"
 | 
			
		||||
#include "core/hle/kernel/vm_manager.h"
 | 
			
		||||
#include "core/hle/service/dsp_dsp.h"
 | 
			
		||||
@ -42,10 +40,18 @@ void Init() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -58,9 +64,9 @@ void SelectSink(std::string sink_id) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto iter = std::find_if(g_sink_details.begin(), g_sink_details.end(), [sink_id](const auto& sink_detail) {
 | 
			
		||||
        return sink_detail.id == sink_id;
 | 
			
		||||
    });
 | 
			
		||||
    auto iter =
 | 
			
		||||
        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()) {
 | 
			
		||||
        LOG_ERROR(Audio, "AudioCore::SelectSink given invalid sink_id");
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,7 @@ class VMManager;
 | 
			
		||||
 | 
			
		||||
namespace AudioCore {
 | 
			
		||||
 | 
			
		||||
constexpr int native_sample_rate = 32728;  ///< 32kHz
 | 
			
		||||
constexpr int native_sample_rate = 32728; ///< 32kHz
 | 
			
		||||
 | 
			
		||||
/// Initialise Audio Core
 | 
			
		||||
void Init();
 | 
			
		||||
 | 
			
		||||
@ -6,31 +6,32 @@
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include "audio_core/codec.h"
 | 
			
		||||
 | 
			
		||||
#include "common/assert.h"
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
#include "common/math_util.h"
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
    // Frames are 8 bytes long containing 14 samples each.
 | 
			
		||||
    // Samples are 4 bits (one nibble) long.
 | 
			
		||||
 | 
			
		||||
    constexpr size_t FRAME_LEN = 8;
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
    int yn1 = state.yn1,
 | 
			
		||||
        yn2 = state.yn2;
 | 
			
		||||
    int yn1 = state.yn1, 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++) {
 | 
			
		||||
        const int frame_header = data[framei * FRAME_LEN];
 | 
			
		||||
        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.
 | 
			
		||||
        const auto decode_sample = [&](const int nibble) -> s16 {
 | 
			
		||||
            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.
 | 
			
		||||
            // 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;
 | 
			
		||||
@ -82,7 +84,8 @@ static s16 SignExtendS8(u8 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);
 | 
			
		||||
 | 
			
		||||
    StereoBuffer16 ret(sample_count);
 | 
			
		||||
@ -101,7 +104,8 @@ StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data, con
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
    StereoBuffer16 ret(sample_count);
 | 
			
		||||
@ -118,5 +122,4 @@ StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data, co
 | 
			
		||||
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,6 @@
 | 
			
		||||
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
 | 
			
		||||
namespace Codec {
 | 
			
		||||
@ -29,7 +28,8 @@ struct ADPCMState {
 | 
			
		||||
 * @param state ADPCM state, this is updated with new state
 | 
			
		||||
 * @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
 | 
			
		||||
@ -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
 | 
			
		||||
 * @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
 | 
			
		||||
@ -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
 | 
			
		||||
 * @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);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -6,30 +6,28 @@
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <array>
 | 
			
		||||
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
 | 
			
		||||
namespace DSP {
 | 
			
		||||
namespace HLE {
 | 
			
		||||
 | 
			
		||||
constexpr int num_sources = 24;
 | 
			
		||||
constexpr int samples_per_frame = 160;     ///< Samples per audio frame at native sample rate
 | 
			
		||||
constexpr int samples_per_frame = 160; ///< Samples per audio frame at native sample rate
 | 
			
		||||
 | 
			
		||||
/// The final output to the speakers is stereo. Preprocessing output in Source is also stereo.
 | 
			
		||||
using StereoFrame16 = std::array<std::array<s16, 2>, samples_per_frame>;
 | 
			
		||||
 | 
			
		||||
/// The DSP is quadraphonic internally.
 | 
			
		||||
using QuadFrame32   = std::array<std::array<s32, 4>, samples_per_frame>;
 | 
			
		||||
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.
 | 
			
		||||
 * FilterT::ProcessSample is called sequentially on the samples.
 | 
			
		||||
 */
 | 
			
		||||
template<typename FrameT, typename FilterT>
 | 
			
		||||
template <typename FrameT, typename FilterT>
 | 
			
		||||
void FilterFrame(FrameT& frame, FilterT& filter) {
 | 
			
		||||
    std::transform(frame.begin(), frame.end(), frame.begin(), [&filter](const auto& sample) {
 | 
			
		||||
        return filter.ProcessSample(sample);
 | 
			
		||||
    });
 | 
			
		||||
    std::transform(frame.begin(), frame.end(), frame.begin(),
 | 
			
		||||
                   [&filter](const auto& sample) { return filter.ProcessSample(sample); });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace HLE
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,6 @@
 | 
			
		||||
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <memory>
 | 
			
		||||
 | 
			
		||||
#include "audio_core/hle/dsp.h"
 | 
			
		||||
#include "audio_core/hle/mixers.h"
 | 
			
		||||
#include "audio_core/hle/pipe.h"
 | 
			
		||||
@ -47,10 +46,9 @@ static SharedMemory& WriteRegion() {
 | 
			
		||||
// Audio processing and mixing
 | 
			
		||||
 | 
			
		||||
static std::array<Source, num_sources> sources = {
 | 
			
		||||
    Source(0), Source(1), Source(2), Source(3), Source(4), Source(5),
 | 
			
		||||
    Source(6), Source(7), Source(8), Source(9), Source(10), Source(11),
 | 
			
		||||
    Source(12), Source(13), Source(14), Source(15), Source(16), Source(17),
 | 
			
		||||
    Source(18), Source(19), Source(20), Source(21), Source(22), Source(23)
 | 
			
		||||
    Source(0),  Source(1),  Source(2),  Source(3),  Source(4),  Source(5),  Source(6),  Source(7),
 | 
			
		||||
    Source(8),  Source(9),  Source(10), Source(11), Source(12), Source(13), Source(14), Source(15),
 | 
			
		||||
    Source(16), Source(17), Source(18), Source(19), Source(20), Source(21), Source(22), Source(23),
 | 
			
		||||
};
 | 
			
		||||
static Mixers mixers;
 | 
			
		||||
 | 
			
		||||
@ -62,14 +60,16 @@ static StereoFrame16 GenerateCurrentFrame() {
 | 
			
		||||
 | 
			
		||||
    // Generate intermediate mixes
 | 
			
		||||
    for (size_t i = 0; i < num_sources; i++) {
 | 
			
		||||
        write.source_statuses.status[i] = sources[i].Tick(read.source_configurations.config[i], read.adpcm_coefficients.coeff[i]);
 | 
			
		||||
        write.source_statuses.status[i] =
 | 
			
		||||
            sources[i].Tick(read.source_configurations.config[i], read.adpcm_coefficients.coeff[i]);
 | 
			
		||||
        for (size_t mix = 0; mix < 3; mix++) {
 | 
			
		||||
            sources[i].MixInto(intermediate_mixes[mix], mix);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Generate final mix
 | 
			
		||||
    write.dsp_status = mixers.Tick(read.dsp_configuration, read.intermediate_mix_samples, write.intermediate_mix_samples, intermediate_mixes);
 | 
			
		||||
    write.dsp_status = mixers.Tick(read.dsp_configuration, read.intermediate_mix_samples,
 | 
			
		||||
                                   write.intermediate_mix_samples, intermediate_mixes);
 | 
			
		||||
 | 
			
		||||
    StereoFrame16 output_frame = mixers.GetOutput();
 | 
			
		||||
 | 
			
		||||
@ -152,7 +152,8 @@ void Shutdown() {
 | 
			
		||||
bool Tick() {
 | 
			
		||||
    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();
 | 
			
		||||
 | 
			
		||||
    OutputCurrentFrame(current_frame);
 | 
			
		||||
 | 
			
		||||
@ -8,9 +8,7 @@
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <type_traits>
 | 
			
		||||
 | 
			
		||||
#include "audio_core/hle/common.h"
 | 
			
		||||
 | 
			
		||||
#include "common/bit_field.h"
 | 
			
		||||
#include "common/common_funcs.h"
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
@ -23,15 +21,15 @@ class Sink;
 | 
			
		||||
namespace DSP {
 | 
			
		||||
namespace HLE {
 | 
			
		||||
 | 
			
		||||
// The application-accessible region of DSP memory consists of two parts.
 | 
			
		||||
// Both are marked as IO and have Read/Write permissions.
 | 
			
		||||
// The application-accessible region of DSP memory consists of two parts. Both are marked as IO and
 | 
			
		||||
// have Read/Write permissions.
 | 
			
		||||
//
 | 
			
		||||
// First Region:  0x1FF50000 (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
 | 
			
		||||
// double-buffer. The frame counter is located as the very last u16 of each region and is incremented
 | 
			
		||||
// each audio tick.
 | 
			
		||||
// double-buffer. The frame counter is located as the very last u16 of each region and is
 | 
			
		||||
// incremented each audio tick.
 | 
			
		||||
 | 
			
		||||
constexpr VAddr region0_base = 0x1FF50000;
 | 
			
		||||
constexpr VAddr region1_base = 0x1FF70000;
 | 
			
		||||
@ -56,6 +54,7 @@ struct u32_dsp {
 | 
			
		||||
    void operator=(u32 new_value) {
 | 
			
		||||
        storage = Convert(new_value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    static constexpr u32 Convert(u32 value) {
 | 
			
		||||
        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.
 | 
			
		||||
//    See also: DSP::HLE::PipeRead.
 | 
			
		||||
//
 | 
			
		||||
// Note that the above addresses do vary slightly between audio firmwares observed; the addresses are
 | 
			
		||||
// not fixed in stone. The addresses above are only an examplar; they're what this implementation
 | 
			
		||||
// does and provides to applications.
 | 
			
		||||
// Note that the above addresses do vary slightly between audio firmwares observed; the addresses
 | 
			
		||||
// are not fixed in stone. The addresses above are only an examplar; they're what this
 | 
			
		||||
// implementation does and provides to applications.
 | 
			
		||||
//
 | 
			
		||||
// Application requests the DSP service to convert DSP addresses into ARM11 virtual addresses using the
 | 
			
		||||
// ConvertProcessAddressFromDspDram service call. Applications seem to derive the addresses for the
 | 
			
		||||
// second region via:
 | 
			
		||||
// Application requests the DSP service to convert DSP addresses into ARM11 virtual addresses using
 | 
			
		||||
// the ConvertProcessAddressFromDspDram service call. Applications seem to derive the addresses for
 | 
			
		||||
// the second region via:
 | 
			
		||||
//     second_region_dsp_addr = first_region_dsp_addr | 0x10000
 | 
			
		||||
//
 | 
			
		||||
// Applications maintain most of its own audio state, the memory region is used mainly for
 | 
			
		||||
@ -103,21 +102,24 @@ static_assert(std::is_trivially_copyable<u32_dsp>::value, "u32_dsp isn't trivial
 | 
			
		||||
//
 | 
			
		||||
// In the documentation below, filter and effect transfer functions are specified in the z domain.
 | 
			
		||||
// (If you are more familiar with the Laplace transform, z = exp(sT). The z domain is the digital
 | 
			
		||||
//  frequency domain, just like how the s domain is the analog frequency domain.)
 | 
			
		||||
// frequency domain, just like how the s domain is the analog frequency domain.)
 | 
			
		||||
 | 
			
		||||
#define INSERT_PADDING_DSPWORDS(num_words) INSERT_PADDING_BYTES(2 * (num_words))
 | 
			
		||||
 | 
			
		||||
// GCC versions < 5.0 do not implement std::is_trivially_copyable.
 | 
			
		||||
// Excluding MSVC because it has weird behaviour for std::is_trivially_copyable.
 | 
			
		||||
#if (__GNUC__ >= 5) || defined(__clang__)
 | 
			
		||||
    #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_trivially_copyable<name>::value, "DSP structure " #name " isn't trivially copyable"); \
 | 
			
		||||
        static_assert(sizeof(name) == (size), "Unexpected struct size for DSP structure " #name)
 | 
			
		||||
#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_trivially_copyable<name>::value,                                         \
 | 
			
		||||
                  "DSP structure " #name " isn't trivially copyable");                             \
 | 
			
		||||
    static_assert(sizeof(name) == (size), "Unexpected struct size for DSP structure " #name)
 | 
			
		||||
#else
 | 
			
		||||
    #define ASSERT_DSP_STRUCT(name, size) \
 | 
			
		||||
        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)
 | 
			
		||||
#define ASSERT_DSP_STRUCT(name, size)                                                              \
 | 
			
		||||
    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)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
struct SourceConfiguration {
 | 
			
		||||
@ -130,7 +132,8 @@ struct SourceConfiguration {
 | 
			
		||||
            BitField<0, 1, u32_le> format_dirty;
 | 
			
		||||
            BitField<1, 1, u32_le> mono_or_stereo_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<16, 1, u32_le> enable_dirty;
 | 
			
		||||
@ -138,7 +141,8 @@ struct SourceConfiguration {
 | 
			
		||||
            BitField<18, 1, u32_le> rate_multiplier_dirty;
 | 
			
		||||
            BitField<19, 1, u32_le> buffer_queue_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<23, 1, u32_le> simple_filter_dirty;
 | 
			
		||||
            BitField<24, 1, u32_le> biquad_filter_dirty;
 | 
			
		||||
@ -153,9 +157,9 @@ struct SourceConfiguration {
 | 
			
		||||
        // Gain control
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Gain is between 0.0-1.0. This determines how much will this source appear on
 | 
			
		||||
         * each of the 12 channels that feed into the intermediate mixers.
 | 
			
		||||
         * Each of the three intermediate mixers is fed two left and two right channels.
 | 
			
		||||
         * Gain is between 0.0-1.0. This determines how much will this source appear on each of the
 | 
			
		||||
         * 12 channels that feed into the intermediate mixers. Each of the three intermediate mixers
 | 
			
		||||
         * is fed two left and two right channels.
 | 
			
		||||
         */
 | 
			
		||||
        float_le gain[3][4];
 | 
			
		||||
 | 
			
		||||
@ -167,7 +171,7 @@ struct SourceConfiguration {
 | 
			
		||||
        enum class InterpolationMode : u8 {
 | 
			
		||||
            Polyphase = 0,
 | 
			
		||||
            Linear = 1,
 | 
			
		||||
            None = 2
 | 
			
		||||
            None = 2,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        InterpolationMode interpolation_mode;
 | 
			
		||||
@ -191,8 +195,8 @@ struct SourceConfiguration {
 | 
			
		||||
         * This is a normalised biquad filter (second-order).
 | 
			
		||||
         * The transfer function of this filter is:
 | 
			
		||||
         *     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
 | 
			
		||||
         * as in: https://ccrma.stanford.edu/~jos/filters/Direct_Form_I.html
 | 
			
		||||
         * Nintendo chose to negate the feedbackward coefficients. This differs from standard
 | 
			
		||||
         * notation as in: https://ccrma.stanford.edu/~jos/filters/Direct_Form_I.html
 | 
			
		||||
         * Values are signed fixed point with 14 fractional bits.
 | 
			
		||||
         */
 | 
			
		||||
        struct BiquadFilter {
 | 
			
		||||
@ -239,23 +243,24 @@ struct SourceConfiguration {
 | 
			
		||||
            /// Is a looping buffer.
 | 
			
		||||
            u8 is_looping;
 | 
			
		||||
 | 
			
		||||
            /// This value is shown in SourceStatus::previous_buffer_id when this buffer has finished.
 | 
			
		||||
            /// This allows the emulated application to tell what buffer is currently playing
 | 
			
		||||
            /// This value is shown in SourceStatus::previous_buffer_id when this buffer has
 | 
			
		||||
            /// finished. This allows the emulated application to tell what buffer is currently
 | 
			
		||||
            /// playing.
 | 
			
		||||
            u16_le buffer_id;
 | 
			
		||||
 | 
			
		||||
            INSERT_PADDING_DSPWORDS(1);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        u16_le buffers_dirty;             ///< Bitmap indicating which buffers are dirty (bit i -> buffers[i])
 | 
			
		||||
        Buffer buffers[4];                ///< Queued Buffers
 | 
			
		||||
        u16_le buffers_dirty; ///< Bitmap indicating which buffers are dirty (bit i -> buffers[i])
 | 
			
		||||
        Buffer buffers[4];    ///< Queued Buffers
 | 
			
		||||
 | 
			
		||||
        // Playback controls
 | 
			
		||||
 | 
			
		||||
        u32_dsp loop_related;
 | 
			
		||||
        u8 enable;
 | 
			
		||||
        INSERT_PADDING_BYTES(1);
 | 
			
		||||
        u16_le sync;                      ///< Application-side sync (See also: SourceStatus::sync)
 | 
			
		||||
        u32_dsp play_position;            ///< Position. (Units: number of samples)
 | 
			
		||||
        u16_le sync;           ///< Application-side sync (See also: SourceStatus::sync)
 | 
			
		||||
        u32_dsp play_position; ///< Position. (Units: number of samples)
 | 
			
		||||
        INSERT_PADDING_DSPWORDS(2);
 | 
			
		||||
 | 
			
		||||
        // Embedded Buffer
 | 
			
		||||
@ -270,13 +275,13 @@ struct SourceConfiguration {
 | 
			
		||||
 | 
			
		||||
        enum class MonoOrStereo : u16_le {
 | 
			
		||||
            Mono = 1,
 | 
			
		||||
            Stereo = 2
 | 
			
		||||
            Stereo = 2,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        enum class Format : u16_le {
 | 
			
		||||
            PCM8 = 0,
 | 
			
		||||
            PCM16 = 1,
 | 
			
		||||
            ADPCM = 2
 | 
			
		||||
            ADPCM = 2,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        union {
 | 
			
		||||
@ -299,10 +304,11 @@ struct SourceConfiguration {
 | 
			
		||||
        union {
 | 
			
		||||
            u16_le flags2_raw;
 | 
			
		||||
            BitField<0, 1, u16_le> adpcm_dirty; ///< Has the ADPCM info above been changed?
 | 
			
		||||
            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;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
@ -313,11 +319,11 @@ ASSERT_DSP_STRUCT(SourceConfiguration::Configuration::Buffer, 20);
 | 
			
		||||
 | 
			
		||||
struct SourceStatus {
 | 
			
		||||
    struct Status {
 | 
			
		||||
        u8 is_enabled;               ///< Is this channel enabled? (Doesn't have to be playing anything.)
 | 
			
		||||
        u8 current_buffer_id_dirty;  ///< Non-zero when current_buffer_id changes
 | 
			
		||||
        u16_le sync;                 ///< Is set by the DSP to the value of SourceConfiguration::sync
 | 
			
		||||
        u32_dsp buffer_position;     ///< Number of samples into the current buffer
 | 
			
		||||
        u16_le current_buffer_id;    ///< Updated when a buffer finishes playing
 | 
			
		||||
        u8 is_enabled; ///< Is this channel enabled? (Doesn't have to be playing anything.)
 | 
			
		||||
        u8 current_buffer_id_dirty; ///< Non-zero when current_buffer_id changes
 | 
			
		||||
        u16_le sync;                ///< Is set by the DSP to the value of SourceConfiguration::sync
 | 
			
		||||
        u32_dsp buffer_position;    ///< Number of samples into the current buffer
 | 
			
		||||
        u16_le current_buffer_id;   ///< Updated when a buffer finishes playing
 | 
			
		||||
        INSERT_PADDING_DSPWORDS(1);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
@ -347,7 +353,8 @@ struct DspConfiguration {
 | 
			
		||||
        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];
 | 
			
		||||
 | 
			
		||||
    INSERT_PADDING_DSPWORDS(3);
 | 
			
		||||
@ -355,7 +362,7 @@ struct DspConfiguration {
 | 
			
		||||
    enum class OutputFormat : u16_le {
 | 
			
		||||
        Mono = 0,
 | 
			
		||||
        Stereo = 1,
 | 
			
		||||
        Surround = 2
 | 
			
		||||
        Surround = 2,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    OutputFormat output_format;
 | 
			
		||||
@ -388,8 +395,10 @@ struct DspConfiguration {
 | 
			
		||||
        u16_le enable;
 | 
			
		||||
        INSERT_PADDING_DSPWORDS(1);
 | 
			
		||||
        u16_le outputs;
 | 
			
		||||
        u32_dsp work_buffer_address; ///< The application allocates a block of memory for the DSP to use as a work buffer.
 | 
			
		||||
        u16_le frame_count;  ///< Frames to delay by
 | 
			
		||||
        /// The application allocates a block of memory for the DSP to use as a work buffer.
 | 
			
		||||
        u32_dsp work_buffer_address;
 | 
			
		||||
        /// Frames to delay by
 | 
			
		||||
        u16_le frame_count;
 | 
			
		||||
 | 
			
		||||
        // Coefficients
 | 
			
		||||
        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;
 | 
			
		||||
 | 
			
		||||
// 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, source_configurations) % 2 == 0, "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, adpcm_coefficients) % 2 == 0, "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, dsp_status) % 2 == 0, "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, 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");
 | 
			
		||||
static_assert(offsetof(SharedMemory, frame_counter) % 2 == 0,
 | 
			
		||||
              "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
 | 
			
		||||
static_assert(offsetof(SharedMemory, source_configurations) % 2 == 0,
 | 
			
		||||
              "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, adpcm_coefficients) % 2 == 0,
 | 
			
		||||
              "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, dsp_status) % 2 == 0,
 | 
			
		||||
              "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, 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 ASSERT_DSP_STRUCT
 | 
			
		||||
 | 
			
		||||
@ -4,11 +4,9 @@
 | 
			
		||||
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
 | 
			
		||||
#include "audio_core/hle/common.h"
 | 
			
		||||
#include "audio_core/hle/dsp.h"
 | 
			
		||||
#include "audio_core/hle/filter.h"
 | 
			
		||||
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
#include "common/math_util.h"
 | 
			
		||||
 | 
			
		||||
@ -59,7 +57,9 @@ void SourceFilters::SimpleFilter::Reset() {
 | 
			
		||||
    b0 = 1 << 15;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SourceFilters::SimpleFilter::Configure(SourceConfiguration::Configuration::SimpleFilter config) {
 | 
			
		||||
void SourceFilters::SimpleFilter::Configure(
 | 
			
		||||
    SourceConfiguration::Configuration::SimpleFilter config) {
 | 
			
		||||
 | 
			
		||||
    a1 = config.a1;
 | 
			
		||||
    b0 = config.b0;
 | 
			
		||||
}
 | 
			
		||||
@ -88,7 +88,9 @@ void SourceFilters::BiquadFilter::Reset() {
 | 
			
		||||
    b0 = 1 << 14;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SourceFilters::BiquadFilter::Configure(SourceConfiguration::Configuration::BiquadFilter config) {
 | 
			
		||||
void SourceFilters::BiquadFilter::Configure(
 | 
			
		||||
    SourceConfiguration::Configuration::BiquadFilter config) {
 | 
			
		||||
 | 
			
		||||
    a1 = config.a1;
 | 
			
		||||
    a2 = config.a2;
 | 
			
		||||
    b0 = config.b0;
 | 
			
		||||
 | 
			
		||||
@ -5,10 +5,8 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <array>
 | 
			
		||||
 | 
			
		||||
#include "audio_core/hle/common.h"
 | 
			
		||||
#include "audio_core/hle/dsp.h"
 | 
			
		||||
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
 | 
			
		||||
namespace DSP {
 | 
			
		||||
@ -17,7 +15,9 @@ namespace HLE {
 | 
			
		||||
/// Preprocessing filters. There is an independent set of filters for each Source.
 | 
			
		||||
class SourceFilters final {
 | 
			
		||||
public:
 | 
			
		||||
    SourceFilters() { Reset(); }
 | 
			
		||||
    SourceFilters() {
 | 
			
		||||
        Reset();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Reset internal state.
 | 
			
		||||
    void Reset();
 | 
			
		||||
@ -54,7 +54,9 @@ private:
 | 
			
		||||
    bool biquad_filter_enabled;
 | 
			
		||||
 | 
			
		||||
    struct SimpleFilter {
 | 
			
		||||
        SimpleFilter() { Reset(); }
 | 
			
		||||
        SimpleFilter() {
 | 
			
		||||
            Reset();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// Resets internal state.
 | 
			
		||||
        void Reset();
 | 
			
		||||
@ -80,7 +82,9 @@ private:
 | 
			
		||||
    } simple_filter;
 | 
			
		||||
 | 
			
		||||
    struct BiquadFilter {
 | 
			
		||||
        BiquadFilter() { Reset(); }
 | 
			
		||||
        BiquadFilter() {
 | 
			
		||||
            Reset();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// Resets internal state.
 | 
			
		||||
        void Reset();
 | 
			
		||||
 | 
			
		||||
@ -7,7 +7,6 @@
 | 
			
		||||
#include "audio_core/hle/common.h"
 | 
			
		||||
#include "audio_core/hle/dsp.h"
 | 
			
		||||
#include "audio_core/hle/mixers.h"
 | 
			
		||||
 | 
			
		||||
#include "common/assert.h"
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
#include "common/math_util.h"
 | 
			
		||||
@ -20,11 +19,9 @@ void Mixers::Reset() {
 | 
			
		||||
    state = {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DspStatus Mixers::Tick(DspConfiguration& config,
 | 
			
		||||
        const IntermediateMixSamples& read_samples,
 | 
			
		||||
        IntermediateMixSamples& write_samples,
 | 
			
		||||
        const std::array<QuadFrame32, 3>& input)
 | 
			
		||||
{
 | 
			
		||||
DspStatus Mixers::Tick(DspConfiguration& config, const IntermediateMixSamples& read_samples,
 | 
			
		||||
                       IntermediateMixSamples& write_samples,
 | 
			
		||||
                       const std::array<QuadFrame32, 3>& input) {
 | 
			
		||||
    ParseConfig(config);
 | 
			
		||||
 | 
			
		||||
    AuxReturn(read_samples);
 | 
			
		||||
@ -73,13 +70,14 @@ void Mixers::ParseConfig(DspConfiguration& config) {
 | 
			
		||||
    if (config.output_format_dirty) {
 | 
			
		||||
        config.output_format_dirty.Assign(0);
 | 
			
		||||
        state.output_format = config.output_format;
 | 
			
		||||
        LOG_TRACE(Audio_DSP, "mixers output_format = %zu", static_cast<size_t>(config.output_format));
 | 
			
		||||
        LOG_TRACE(Audio_DSP, "mixers output_format = %zu",
 | 
			
		||||
                  static_cast<size_t>(config.output_format));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (config.headphones_connected_dirty) {
 | 
			
		||||
        config.headphones_connected_dirty.Assign(0);
 | 
			
		||||
        // Do nothing.
 | 
			
		||||
        // (Note: Whether headphones are connected does affect coefficients used for surround sound.)
 | 
			
		||||
        // Do nothing. (Note: Whether headphones are connected does affect coefficients used for
 | 
			
		||||
        // surround sound.)
 | 
			
		||||
        LOG_TRACE(Audio_DSP, "mixers headphones_connected=%hu", config.headphones_connected);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -94,11 +92,10 @@ static s16 ClampToS16(s32 value) {
 | 
			
		||||
    return static_cast<s16>(MathUtil::Clamp(value, -32768, 32767));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static std::array<s16, 2> AddAndClampToS16(const std::array<s16, 2>& a, const std::array<s16, 2>& b) {
 | 
			
		||||
    return {
 | 
			
		||||
        ClampToS16(static_cast<s32>(a[0]) + static_cast<s32>(b[0])),
 | 
			
		||||
        ClampToS16(static_cast<s32>(a[1]) + static_cast<s32>(b[1]))
 | 
			
		||||
    };
 | 
			
		||||
static std::array<s16, 2> AddAndClampToS16(const std::array<s16, 2>& a,
 | 
			
		||||
                                           const std::array<s16, 2>& b) {
 | 
			
		||||
    return {ClampToS16(static_cast<s32>(a[0]) + static_cast<s32>(b[0])),
 | 
			
		||||
            ClampToS16(static_cast<s32>(a[1]) + static_cast<s32>(b[1]))};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Mixers::DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& samples) {
 | 
			
		||||
@ -106,27 +103,33 @@ void Mixers::DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& sample
 | 
			
		||||
 | 
			
		||||
    switch (state.output_format) {
 | 
			
		||||
    case OutputFormat::Mono:
 | 
			
		||||
        std::transform(current_frame.begin(), current_frame.end(), samples.begin(), current_frame.begin(),
 | 
			
		||||
            [gain](const std::array<s16, 2>& accumulator, const std::array<s32, 4>& sample) -> std::array<s16, 2> {
 | 
			
		||||
        std::transform(
 | 
			
		||||
            current_frame.begin(), current_frame.end(), samples.begin(), current_frame.begin(),
 | 
			
		||||
            [gain](const std::array<s16, 2>& accumulator,
 | 
			
		||||
                   const std::array<s32, 4>& sample) -> std::array<s16, 2> {
 | 
			
		||||
                // Downmix to mono
 | 
			
		||||
                s16 mono = ClampToS16(static_cast<s32>((gain * sample[0] + gain * sample[1] + gain * sample[2] + gain * sample[3]) / 2));
 | 
			
		||||
                s16 mono = ClampToS16(static_cast<s32>(
 | 
			
		||||
                    (gain * sample[0] + gain * sample[1] + gain * sample[2] + gain * sample[3]) /
 | 
			
		||||
                    2));
 | 
			
		||||
                // Mix into current frame
 | 
			
		||||
                return AddAndClampToS16(accumulator, { mono, mono });
 | 
			
		||||
                return AddAndClampToS16(accumulator, {mono, mono});
 | 
			
		||||
            });
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    case OutputFormat::Surround:
 | 
			
		||||
        // TODO(merry): Implement surround sound.
 | 
			
		||||
        // fallthrough
 | 
			
		||||
    // TODO(merry): Implement surround sound.
 | 
			
		||||
    // fallthrough
 | 
			
		||||
 | 
			
		||||
    case OutputFormat::Stereo:
 | 
			
		||||
        std::transform(current_frame.begin(), current_frame.end(), samples.begin(), current_frame.begin(),
 | 
			
		||||
            [gain](const std::array<s16, 2>& accumulator, const std::array<s32, 4>& sample) -> std::array<s16, 2> {
 | 
			
		||||
        std::transform(
 | 
			
		||||
            current_frame.begin(), current_frame.end(), samples.begin(), current_frame.begin(),
 | 
			
		||||
            [gain](const std::array<s16, 2>& accumulator,
 | 
			
		||||
                   const std::array<s32, 4>& sample) -> std::array<s16, 2> {
 | 
			
		||||
                // Downmix to stereo
 | 
			
		||||
                s16 left = ClampToS16(static_cast<s32>(gain * sample[0] + gain * sample[2]));
 | 
			
		||||
                s16 right = ClampToS16(static_cast<s32>(gain * sample[1] + gain * sample[3]));
 | 
			
		||||
                // Mix into current frame
 | 
			
		||||
                return AddAndClampToS16(accumulator, { left, right });
 | 
			
		||||
                return AddAndClampToS16(accumulator, {left, right});
 | 
			
		||||
            });
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
@ -135,12 +138,14 @@ void Mixers::DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& sample
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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) {
 | 
			
		||||
        for (size_t sample = 0; sample < samples_per_frame; sample++) {
 | 
			
		||||
            for (size_t channel = 0; channel < 4; channel++) {
 | 
			
		||||
                state.intermediate_mix_buffer[1][sample][channel] = read_samples.mix1.pcm32[channel][sample];
 | 
			
		||||
                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) {
 | 
			
		||||
        for (size_t sample = 0; sample < samples_per_frame; sample++) {
 | 
			
		||||
            for (size_t channel = 0; channel < 4; channel++) {
 | 
			
		||||
                state.intermediate_mix_buffer[2][sample][channel] = read_samples.mix2.pcm32[channel][sample];
 | 
			
		||||
                state.intermediate_mix_buffer[2][sample][channel] =
 | 
			
		||||
                    read_samples.mix2.pcm32[channel][sample];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Mixers::AuxSend(IntermediateMixSamples& write_samples, const std::array<QuadFrame32, 3>& input) {
 | 
			
		||||
    // NOTE: read_samples.mix{1,2}.pcm32 annoyingly have their dimensions in reverse order to QuadFrame32.
 | 
			
		||||
void Mixers::AuxSend(IntermediateMixSamples& write_samples,
 | 
			
		||||
                     const std::array<QuadFrame32, 3>& input) {
 | 
			
		||||
    // NOTE: read_samples.mix{1,2}.pcm32 annoyingly have their dimensions in reverse order to
 | 
			
		||||
    // QuadFrame32.
 | 
			
		||||
 | 
			
		||||
    state.intermediate_mix_buffer[0] = input[0];
 | 
			
		||||
 | 
			
		||||
@ -184,7 +192,8 @@ void Mixers::MixCurrentFrame() {
 | 
			
		||||
    current_frame.fill({});
 | 
			
		||||
 | 
			
		||||
    for (size_t mix = 0; mix < 3; mix++) {
 | 
			
		||||
        DownmixAndMixIntoCurrentFrame(state.intermediate_mixer_volume[mix], state.intermediate_mix_buffer[mix]);
 | 
			
		||||
        DownmixAndMixIntoCurrentFrame(state.intermediate_mixer_volume[mix],
 | 
			
		||||
                                      state.intermediate_mix_buffer[mix]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // TODO(merry): Compressor. (We currently assume a disabled compressor.)
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,6 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <array>
 | 
			
		||||
 | 
			
		||||
#include "audio_core/hle/common.h"
 | 
			
		||||
#include "audio_core/hle/dsp.h"
 | 
			
		||||
 | 
			
		||||
@ -20,10 +19,8 @@ public:
 | 
			
		||||
 | 
			
		||||
    void Reset();
 | 
			
		||||
 | 
			
		||||
    DspStatus Tick(DspConfiguration& config,
 | 
			
		||||
                   const IntermediateMixSamples& read_samples,
 | 
			
		||||
                   IntermediateMixSamples& write_samples,
 | 
			
		||||
                   const std::array<QuadFrame32, 3>& input);
 | 
			
		||||
    DspStatus Tick(DspConfiguration& config, const IntermediateMixSamples& read_samples,
 | 
			
		||||
                   IntermediateMixSamples& write_samples, const std::array<QuadFrame32, 3>& input);
 | 
			
		||||
 | 
			
		||||
    StereoFrame16 GetOutput() const {
 | 
			
		||||
        return current_frame;
 | 
			
		||||
@ -53,7 +50,8 @@ private:
 | 
			
		||||
    void AuxSend(IntermediateMixSamples& write_samples, const std::array<QuadFrame32, 3>& input);
 | 
			
		||||
    /// INTERNAL: Mix current_frame.
 | 
			
		||||
    void MixCurrentFrame();
 | 
			
		||||
    /// INTERNAL: Downmix from quadraphonic to stereo based on status.output_format and accumulate into current_frame.
 | 
			
		||||
    /// INTERNAL: Downmix from quadraphonic to stereo based on status.output_format and accumulate
 | 
			
		||||
    /// into current_frame.
 | 
			
		||||
    void DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& samples);
 | 
			
		||||
    /// INTERNAL: Generate DspStatus based on internal state.
 | 
			
		||||
    DspStatus GetCurrentStatus() const;
 | 
			
		||||
 | 
			
		||||
@ -4,14 +4,11 @@
 | 
			
		||||
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include "audio_core/hle/dsp.h"
 | 
			
		||||
#include "audio_core/hle/pipe.h"
 | 
			
		||||
 | 
			
		||||
#include "common/assert.h"
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
 | 
			
		||||
#include "core/hle/service/dsp_dsp.h"
 | 
			
		||||
 | 
			
		||||
namespace DSP {
 | 
			
		||||
@ -44,8 +41,10 @@ std::vector<u8> PipeRead(DspPipe pipe_number, u32 length) {
 | 
			
		||||
    std::vector<u8>& data = pipe_data[pipe_index];
 | 
			
		||||
 | 
			
		||||
    if (length > data.size()) {
 | 
			
		||||
        LOG_WARNING(Audio_DSP, "pipe_number = %zu is out of data, application requested read of %u but %zu remain",
 | 
			
		||||
                    pipe_index, length, data.size());
 | 
			
		||||
        LOG_WARNING(
 | 
			
		||||
            Audio_DSP,
 | 
			
		||||
            "pipe_number = %zu is out of data, application requested read of %u but %zu remain",
 | 
			
		||||
            pipe_index, length, data.size());
 | 
			
		||||
        length = static_cast<u32>(data.size());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -95,7 +94,7 @@ static void AudioPipeWriteStructAddresses() {
 | 
			
		||||
        0x8000 + offsetof(SharedMemory, unknown11) / 2,
 | 
			
		||||
        0x8000 + offsetof(SharedMemory, unknown12) / 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.
 | 
			
		||||
@ -112,7 +111,8 @@ void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) {
 | 
			
		||||
    switch (pipe_number) {
 | 
			
		||||
    case DspPipe::Audio: {
 | 
			
		||||
        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;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -120,7 +120,7 @@ void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) {
 | 
			
		||||
            Initalize = 0,
 | 
			
		||||
            Shutdown = 1,
 | 
			
		||||
            Wakeup = 2,
 | 
			
		||||
            Sleep = 3
 | 
			
		||||
            Sleep = 3,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // 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;
 | 
			
		||||
            break;
 | 
			
		||||
        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;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
@ -160,7 +162,8 @@ void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    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();
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,6 @@
 | 
			
		||||
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
 | 
			
		||||
namespace DSP {
 | 
			
		||||
@ -19,16 +18,18 @@ enum class DspPipe {
 | 
			
		||||
    Debug = 0,
 | 
			
		||||
    Dma = 1,
 | 
			
		||||
    Audio = 2,
 | 
			
		||||
    Binary = 3
 | 
			
		||||
    Binary = 3,
 | 
			
		||||
};
 | 
			
		||||
constexpr size_t NUM_DSP_PIPE = 8;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 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 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 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 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.
 | 
			
		||||
@ -52,8 +53,9 @@ void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer);
 | 
			
		||||
enum class DspState {
 | 
			
		||||
    Off,
 | 
			
		||||
    On,
 | 
			
		||||
    Sleeping
 | 
			
		||||
    Sleeping,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Get the state of the DSP
 | 
			
		||||
DspState GetDspState();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -4,21 +4,19 @@
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <array>
 | 
			
		||||
 | 
			
		||||
#include "audio_core/codec.h"
 | 
			
		||||
#include "audio_core/hle/common.h"
 | 
			
		||||
#include "audio_core/hle/source.h"
 | 
			
		||||
#include "audio_core/interpolate.h"
 | 
			
		||||
 | 
			
		||||
#include "common/assert.h"
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
 | 
			
		||||
#include "core/memory.h"
 | 
			
		||||
 | 
			
		||||
namespace DSP {
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
    if (state.enabled) {
 | 
			
		||||
@ -47,7 +45,8 @@ void Source::Reset() {
 | 
			
		||||
    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) {
 | 
			
		||||
        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);
 | 
			
		||||
 | 
			
		||||
        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;
 | 
			
		||||
            // Note: Actual firmware starts producing garbage if this occurs.
 | 
			
		||||
        }
 | 
			
		||||
@ -90,37 +90,39 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_l
 | 
			
		||||
 | 
			
		||||
    if (config.adpcm_coefficients_dirty) {
 | 
			
		||||
        config.adpcm_coefficients_dirty.Assign(0);
 | 
			
		||||
        std::transform(adpcm_coeffs, adpcm_coeffs + state.adpcm_coeffs.size(), state.adpcm_coeffs.begin(),
 | 
			
		||||
            [](const auto& coeff) { return static_cast<s16>(coeff); });
 | 
			
		||||
        std::transform(adpcm_coeffs, adpcm_coeffs + state.adpcm_coeffs.size(),
 | 
			
		||||
                       state.adpcm_coeffs.begin(),
 | 
			
		||||
                       [](const auto& coeff) { return static_cast<s16>(coeff); });
 | 
			
		||||
        LOG_TRACE(Audio_DSP, "source_id=%zu adpcm update", source_id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (config.gain_0_dirty) {
 | 
			
		||||
        config.gain_0_dirty.Assign(0);
 | 
			
		||||
        std::transform(config.gain[0], config.gain[0] + state.gain[0].size(), state.gain[0].begin(),
 | 
			
		||||
            [](const auto& coeff) { return static_cast<float>(coeff); });
 | 
			
		||||
                       [](const auto& coeff) { return static_cast<float>(coeff); });
 | 
			
		||||
        LOG_TRACE(Audio_DSP, "source_id=%zu gain 0 update", source_id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (config.gain_1_dirty) {
 | 
			
		||||
        config.gain_1_dirty.Assign(0);
 | 
			
		||||
        std::transform(config.gain[1], config.gain[1] + state.gain[1].size(), state.gain[1].begin(),
 | 
			
		||||
            [](const auto& coeff) { return static_cast<float>(coeff); });
 | 
			
		||||
                       [](const auto& coeff) { return static_cast<float>(coeff); });
 | 
			
		||||
        LOG_TRACE(Audio_DSP, "source_id=%zu gain 1 update", source_id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (config.gain_2_dirty) {
 | 
			
		||||
        config.gain_2_dirty.Assign(0);
 | 
			
		||||
        std::transform(config.gain[2], config.gain[2] + state.gain[2].size(), state.gain[2].begin(),
 | 
			
		||||
            [](const auto& coeff) { return static_cast<float>(coeff); });
 | 
			
		||||
                       [](const auto& coeff) { return static_cast<float>(coeff); });
 | 
			
		||||
        LOG_TRACE(Audio_DSP, "source_id=%zu gain 2 update", source_id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (config.filters_enabled_dirty) {
 | 
			
		||||
        config.filters_enabled_dirty.Assign(0);
 | 
			
		||||
        state.filters.Enable(config.simple_filter_enabled.ToBool(), config.biquad_filter_enabled.ToBool());
 | 
			
		||||
        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());
 | 
			
		||||
        state.filters.Enable(config.simple_filter_enabled.ToBool(),
 | 
			
		||||
                             config.biquad_filter_enabled.ToBool());
 | 
			
		||||
        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) {
 | 
			
		||||
@ -138,19 +140,22 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_l
 | 
			
		||||
    if (config.interpolation_dirty) {
 | 
			
		||||
        config.interpolation_dirty.Assign(0);
 | 
			
		||||
        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) {
 | 
			
		||||
        config.format_dirty.Assign(0);
 | 
			
		||||
        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) {
 | 
			
		||||
        config.mono_or_stereo_dirty.Assign(0);
 | 
			
		||||
        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) {
 | 
			
		||||
@ -159,15 +164,16 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_l
 | 
			
		||||
            config.physical_address,
 | 
			
		||||
            config.length,
 | 
			
		||||
            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.is_looping.ToBool(),
 | 
			
		||||
            config.buffer_id,
 | 
			
		||||
            state.mono_or_stereo,
 | 
			
		||||
            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) {
 | 
			
		||||
@ -179,15 +185,16 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_l
 | 
			
		||||
                    b.physical_address,
 | 
			
		||||
                    b.length,
 | 
			
		||||
                    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.is_looping != 0,
 | 
			
		||||
                    b.buffer_id,
 | 
			
		||||
                    state.mono_or_stereo,
 | 
			
		||||
                    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;
 | 
			
		||||
@ -218,10 +225,13 @@ void Source::GenerateFrame() {
 | 
			
		||||
            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);
 | 
			
		||||
        state.current_buffer.erase(state.current_buffer.begin(), state.current_buffer.begin() + size_to_copy);
 | 
			
		||||
        std::copy(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;
 | 
			
		||||
        state.next_sample_number += static_cast<u32>(size_to_copy);
 | 
			
		||||
@ -230,9 +240,9 @@ void Source::GenerateFrame() {
 | 
			
		||||
    state.filters.ProcessFrame(current_frame);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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())
 | 
			
		||||
        return false;
 | 
			
		||||
@ -261,29 +271,34 @@ bool Source::DequeueBuffer() {
 | 
			
		||||
            break;
 | 
			
		||||
        case Format::ADPCM:
 | 
			
		||||
            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;
 | 
			
		||||
        default:
 | 
			
		||||
            UNIMPLEMENTED();
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        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);
 | 
			
		||||
        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);
 | 
			
		||||
        state.current_buffer.clear();
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    switch (state.interpolation_mode) {
 | 
			
		||||
    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;
 | 
			
		||||
    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;
 | 
			
		||||
    case InterpolationMode::Polyphase:
 | 
			
		||||
        // 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;
 | 
			
		||||
    default:
 | 
			
		||||
        UNIMPLEMENTED();
 | 
			
		||||
@ -296,7 +311,8 @@ bool Source::DequeueBuffer() {
 | 
			
		||||
    state.buffer_update = buf.from_queue;
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -7,13 +7,11 @@
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <queue>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include "audio_core/codec.h"
 | 
			
		||||
#include "audio_core/hle/common.h"
 | 
			
		||||
#include "audio_core/hle/dsp.h"
 | 
			
		||||
#include "audio_core/hle/filter.h"
 | 
			
		||||
#include "audio_core/interpolate.h"
 | 
			
		||||
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
 | 
			
		||||
namespace DSP {
 | 
			
		||||
@ -40,13 +38,17 @@ public:
 | 
			
		||||
    /**
 | 
			
		||||
     * 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 adpcm_coeffs ADPCM coefficients to use if config tells us to use them (may contain invalid values otherwise).
 | 
			
		||||
     * @return The current status of this Source. This is given back to the emulated application via SharedMemory.
 | 
			
		||||
     * @param adpcm_coeffs ADPCM coefficients to use if config tells us to use them (may contain
 | 
			
		||||
     * 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 intermediate_mix_id The id of the intermediate mix whose gains we are using.
 | 
			
		||||
     */
 | 
			
		||||
@ -77,7 +79,7 @@ private:
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
            return a.buffer_id > b.buffer_id;
 | 
			
		||||
        }
 | 
			
		||||
@ -134,7 +136,8 @@ private:
 | 
			
		||||
    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.
 | 
			
		||||
    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();
 | 
			
		||||
    /// INTERNAL: Generates a SourceStatus::Status based on our internal state.
 | 
			
		||||
    SourceStatus::Status GetCurrentStatus();
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,6 @@
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include "audio_core/interpolate.h"
 | 
			
		||||
 | 
			
		||||
#include "common/assert.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.
 | 
			
		||||
/// Three adjacent samples are passed to fn each step.
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
    if (input.size() < 2)
 | 
			
		||||
@ -63,23 +63,24 @@ static StereoBuffer16 StepOverSamples(State& state, const StereoBuffer16& input,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 x0;
 | 
			
		||||
    });
 | 
			
		||||
    return StepOverSamples(
 | 
			
		||||
        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) {
 | 
			
		||||
    // 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) {
 | 
			
		||||
        // This is a saturated subtraction. (Verified by black-box fuzzing.)
 | 
			
		||||
        s64 delta0 = MathUtil::Clamp<s64>(x1[0] - x0[0], -32768, 32767);
 | 
			
		||||
        s64 delta1 = MathUtil::Clamp<s64>(x1[1] - x0[1], -32768, 32767);
 | 
			
		||||
    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.)
 | 
			
		||||
                               s64 delta0 = MathUtil::Clamp<s64>(x1[0] - x0[0], -32768, 32767);
 | 
			
		||||
                               s64 delta1 = MathUtil::Clamp<s64>(x1[1] - x0[1], -32768, 32767);
 | 
			
		||||
 | 
			
		||||
        return std::array<s16, 2> {
 | 
			
		||||
            static_cast<s16>(x0[0] + fraction * delta0 / scale_factor),
 | 
			
		||||
            static_cast<s16>(x0[1] + fraction * delta1 / scale_factor)
 | 
			
		||||
        };
 | 
			
		||||
    });
 | 
			
		||||
                               return std::array<s16, 2>{
 | 
			
		||||
                                   static_cast<s16>(x0[0] + fraction * delta0 / scale_factor),
 | 
			
		||||
                                   static_cast<s16>(x0[1] + fraction * delta1 / scale_factor),
 | 
			
		||||
                               };
 | 
			
		||||
                           });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace AudioInterp
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,6 @@
 | 
			
		||||
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
 | 
			
		||||
namespace AudioInterp {
 | 
			
		||||
@ -24,7 +23,8 @@ struct State {
 | 
			
		||||
 * No interpolation. This is equivalent to a zero-order hold. There is a two-sample predelay.
 | 
			
		||||
 * @param input Input buffer.
 | 
			
		||||
 * @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.
 | 
			
		||||
 */
 | 
			
		||||
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.
 | 
			
		||||
 * @param input Input buffer.
 | 
			
		||||
 * @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.
 | 
			
		||||
 */
 | 
			
		||||
StereoBuffer16 Linear(State& state, const StereoBuffer16& input, float rate_multiplier);
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,6 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
 | 
			
		||||
#include "audio_core/audio_core.h"
 | 
			
		||||
#include "audio_core/sink.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -3,16 +3,13 @@
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include <list>
 | 
			
		||||
#include <numeric>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include <SDL.h>
 | 
			
		||||
 | 
			
		||||
#include "audio_core/audio_core.h"
 | 
			
		||||
#include "audio_core/sdl2_sink.h"
 | 
			
		||||
 | 
			
		||||
#include "common/assert.h"
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
#include <numeric>
 | 
			
		||||
 | 
			
		||||
namespace AudioCore {
 | 
			
		||||
 | 
			
		||||
@ -45,7 +42,8 @@ SDL2Sink::SDL2Sink() : impl(std::make_unique<Impl>()) {
 | 
			
		||||
    SDL_AudioSpec 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) {
 | 
			
		||||
        LOG_CRITICAL(Audio_Sink, "SDL_OpenAudioDevice failed");
 | 
			
		||||
        return;
 | 
			
		||||
@ -86,11 +84,12 @@ size_t SDL2Sink::SamplesInQueue() const {
 | 
			
		||||
 | 
			
		||||
    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 sum, const auto& buffer) {
 | 
			
		||||
            // Division by two because each stereo sample is made of two s16.
 | 
			
		||||
            return sum + buffer.size() / 2;
 | 
			
		||||
        });
 | 
			
		||||
    size_t total_size = std::accumulate(impl->queue.begin(), impl->queue.end(),
 | 
			
		||||
                                        static_cast<size_t>(0), [](size_t sum, const auto& buffer) {
 | 
			
		||||
                                            // Division by two because each stereo sample is made of
 | 
			
		||||
                                            // two s16.
 | 
			
		||||
                                            return sum + buffer.size() / 2;
 | 
			
		||||
                                        });
 | 
			
		||||
 | 
			
		||||
    SDL_UnlockAudioDevice(impl->audio_device_id);
 | 
			
		||||
 | 
			
		||||
@ -100,7 +99,8 @@ size_t SDL2Sink::SamplesInQueue() const {
 | 
			
		||||
void SDL2Sink::Impl::Callback(void* impl_, u8* buffer, int buffer_size_in_bytes) {
 | 
			
		||||
    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()) {
 | 
			
		||||
        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 {
 | 
			
		||||
            memcpy(buffer, impl->queue.front().data(), 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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,6 @@
 | 
			
		||||
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
#include <memory>
 | 
			
		||||
 | 
			
		||||
#include "audio_core/sink.h"
 | 
			
		||||
 | 
			
		||||
namespace AudioCore {
 | 
			
		||||
 | 
			
		||||
@ -5,20 +5,21 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
 | 
			
		||||
namespace AudioCore {
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This class is an interface for an audio sink. An audio sink accepts samples in stereo signed PCM16 format to be output.
 | 
			
		||||
 * Sinks *do not* handle resampling and expect the correct sample rate. They are dumb outputs.
 | 
			
		||||
 * This class is an interface for an audio sink. An audio sink accepts samples in stereo signed
 | 
			
		||||
 * PCM16 format to be output. Sinks *do not* handle resampling and expect the correct sample rate.
 | 
			
		||||
 * They are dumb outputs.
 | 
			
		||||
 */
 | 
			
		||||
class Sink {
 | 
			
		||||
public:
 | 
			
		||||
    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;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 | 
			
		||||
@ -4,10 +4,8 @@
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include "audio_core/null_sink.h"
 | 
			
		||||
#include "audio_core/sink_details.h"
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_SDL2
 | 
			
		||||
#include "audio_core/sdl2_sink.h"
 | 
			
		||||
#endif
 | 
			
		||||
@ -17,9 +15,9 @@ namespace AudioCore {
 | 
			
		||||
// g_sink_details is ordered in terms of desirability, with the best choice at the top.
 | 
			
		||||
const std::vector<SinkDetails> g_sink_details = {
 | 
			
		||||
#ifdef HAVE_SDL2
 | 
			
		||||
    { "sdl2", []() { return std::make_unique<SDL2Sink>(); } },
 | 
			
		||||
    {"sdl2", []() { return std::make_unique<SDL2Sink>(); }},
 | 
			
		||||
#endif
 | 
			
		||||
    { "null", []() { return std::make_unique<NullSink>(); } },
 | 
			
		||||
    {"null", []() { return std::make_unique<NullSink>(); }},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace AudioCore
 | 
			
		||||
 | 
			
		||||
@ -5,12 +5,9 @@
 | 
			
		||||
#include <chrono>
 | 
			
		||||
#include <cmath>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include <SoundTouch.h>
 | 
			
		||||
 | 
			
		||||
#include "audio_core/audio_core.h"
 | 
			
		||||
#include "audio_core/time_stretch.h"
 | 
			
		||||
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
#include "common/math_util.h"
 | 
			
		||||
@ -26,8 +23,8 @@ static double ClampRatio(double ratio) {
 | 
			
		||||
    return MathUtil::Clamp(ratio, MIN_RATIO, MAX_RATIO);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
constexpr double MIN_DELAY_TIME = 0.05; // Units: seconds
 | 
			
		||||
constexpr double MAX_DELAY_TIME = 0.25; // Units: seconds
 | 
			
		||||
constexpr double MIN_DELAY_TIME = 0.05;            // Units: seconds
 | 
			
		||||
constexpr double MAX_DELAY_TIME = 0.25;            // Units: seconds
 | 
			
		||||
constexpr size_t DROP_FRAMES_SAMPLE_DELAY = 16000; // Units: samples
 | 
			
		||||
 | 
			
		||||
constexpr double SMOOTHING_FACTOR = 0.007;
 | 
			
		||||
@ -48,7 +45,8 @@ std::vector<s16> TimeStretcher::Process(size_t samples_in_queue) {
 | 
			
		||||
 | 
			
		||||
    double ratio = CalculateCurrentRatio();
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
    // 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 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();
 | 
			
		||||
 | 
			
		||||
    double ratio;
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,6 @@
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
 | 
			
		||||
namespace AudioCore {
 | 
			
		||||
@ -37,7 +36,8 @@ public:
 | 
			
		||||
    /**
 | 
			
		||||
     * Does audio stretching and produces the time-stretched samples.
 | 
			
		||||
     * 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.
 | 
			
		||||
     */
 | 
			
		||||
    std::vector<s16> Process(size_t sample_delay);
 | 
			
		||||
@ -48,7 +48,8 @@ private:
 | 
			
		||||
 | 
			
		||||
    /// INTERNAL: ratio = wallclock time / emulated time
 | 
			
		||||
    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;
 | 
			
		||||
    /// INTERNAL: Gets the time-stretched samples from SoundTouch.
 | 
			
		||||
    std::vector<s16> GetSamples();
 | 
			
		||||
 | 
			
		||||
@ -2,10 +2,10 @@
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <thread>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <thread>
 | 
			
		||||
 | 
			
		||||
// This needs to be included before getopt.h because the latter #defines symbols used by it
 | 
			
		||||
#include "common/microprofile.h"
 | 
			
		||||
@ -13,53 +13,48 @@
 | 
			
		||||
#ifdef _MSC_VER
 | 
			
		||||
#include <getopt.h>
 | 
			
		||||
#else
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <getopt.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
#include <Windows.h>
 | 
			
		||||
#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/filter.h"
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
#include "common/scm_rev.h"
 | 
			
		||||
#include "common/scope_exit.h"
 | 
			
		||||
#include "common/string_util.h"
 | 
			
		||||
 | 
			
		||||
#include "core/settings.h"
 | 
			
		||||
#include "core/system.h"
 | 
			
		||||
#include "core/core.h"
 | 
			
		||||
#include "core/gdbstub/gdbstub.h"
 | 
			
		||||
#include "core/loader/loader.h"
 | 
			
		||||
 | 
			
		||||
#include "citra/config.h"
 | 
			
		||||
#include "citra/emu_window/emu_window_sdl2.h"
 | 
			
		||||
 | 
			
		||||
#include "core/settings.h"
 | 
			
		||||
#include "core/system.h"
 | 
			
		||||
#include "video_core/video_core.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void PrintHelp(const char *argv0)
 | 
			
		||||
{
 | 
			
		||||
    std::cout << "Usage: " << argv0 << " [options] <filename>\n"
 | 
			
		||||
static void PrintHelp(const char* argv0) {
 | 
			
		||||
    std::cout << "Usage: " << argv0
 | 
			
		||||
              << " [options] <filename>\n"
 | 
			
		||||
                 "-g, --gdbport=NUMBER  Enable gdb stub on port NUMBER\n"
 | 
			
		||||
                 "-h, --help            Display this help 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Application entry point
 | 
			
		||||
int main(int argc, char **argv) {
 | 
			
		||||
int main(int argc, char** argv) {
 | 
			
		||||
    Config config;
 | 
			
		||||
    int option_index = 0;
 | 
			
		||||
    bool use_gdbstub = Settings::values.use_gdbstub;
 | 
			
		||||
    u32 gdb_port = static_cast<u32>(Settings::values.gdbstub_port);
 | 
			
		||||
    char *endarg;
 | 
			
		||||
    char* endarg;
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
    int argc_w;
 | 
			
		||||
    auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w);
 | 
			
		||||
@ -72,10 +67,10 @@ int main(int argc, char **argv) {
 | 
			
		||||
    std::string boot_filename;
 | 
			
		||||
 | 
			
		||||
    static struct option long_options[] = {
 | 
			
		||||
        { "gdbport", required_argument, 0, 'g' },
 | 
			
		||||
        { "help", no_argument, 0, 'h' },
 | 
			
		||||
        { "version", no_argument, 0, 'v' },
 | 
			
		||||
        { 0, 0, 0, 0 }
 | 
			
		||||
        {"gdbport", required_argument, 0, 'g'},
 | 
			
		||||
        {"help", no_argument, 0, 'h'},
 | 
			
		||||
        {"version", no_argument, 0, 'v'},
 | 
			
		||||
        {0, 0, 0, 0},
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    while (optind < argc) {
 | 
			
		||||
@ -86,7 +81,8 @@ int main(int argc, char **argv) {
 | 
			
		||||
                errno = 0;
 | 
			
		||||
                gdb_port = strtoul(optarg, &endarg, 0);
 | 
			
		||||
                use_gdbstub = true;
 | 
			
		||||
                if (endarg == optarg) errno = EINVAL;
 | 
			
		||||
                if (endarg == optarg)
 | 
			
		||||
                    errno = EINVAL;
 | 
			
		||||
                if (errno != 0) {
 | 
			
		||||
                    perror("--gdbport");
 | 
			
		||||
                    exit(1);
 | 
			
		||||
 | 
			
		||||
@ -3,19 +3,13 @@
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
 | 
			
		||||
#include <inih/cpp/INIReader.h>
 | 
			
		||||
 | 
			
		||||
#include <SDL.h>
 | 
			
		||||
 | 
			
		||||
#include <inih/cpp/INIReader.h>
 | 
			
		||||
#include "citra/default_ini.h"
 | 
			
		||||
 | 
			
		||||
#include "common/file_util.h"
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
 | 
			
		||||
#include "core/settings.h"
 | 
			
		||||
 | 
			
		||||
#include "config.h"
 | 
			
		||||
#include "core/settings.h"
 | 
			
		||||
 | 
			
		||||
Config::Config() {
 | 
			
		||||
    // 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 = {
 | 
			
		||||
    // directly mapped keys
 | 
			
		||||
    SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X,
 | 
			
		||||
    SDL_SCANCODE_Q, SDL_SCANCODE_W, SDL_SCANCODE_1, SDL_SCANCODE_2,
 | 
			
		||||
    SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_B,
 | 
			
		||||
    SDL_SCANCODE_T, SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H,
 | 
			
		||||
    SDL_SCANCODE_I, SDL_SCANCODE_K, SDL_SCANCODE_J, SDL_SCANCODE_L,
 | 
			
		||||
    SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_Q, SDL_SCANCODE_W,
 | 
			
		||||
    SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_B, SDL_SCANCODE_T,
 | 
			
		||||
    SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H, SDL_SCANCODE_I, SDL_SCANCODE_K, SDL_SCANCODE_J,
 | 
			
		||||
    SDL_SCANCODE_L,
 | 
			
		||||
 | 
			
		||||
    // indirectly mapped keys
 | 
			
		||||
    SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT,
 | 
			
		||||
    SDL_SCANCODE_D,
 | 
			
		||||
    SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT, SDL_SCANCODE_D,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void Config::ReadValues() {
 | 
			
		||||
@ -62,7 +54,8 @@ void Config::ReadValues() {
 | 
			
		||||
        Settings::values.input_mappings[Settings::NativeInput::All[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
 | 
			
		||||
    Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true);
 | 
			
		||||
@ -71,19 +64,22 @@ void Config::ReadValues() {
 | 
			
		||||
    // Renderer
 | 
			
		||||
    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_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.bg_red   = (float)sdl2_config->GetReal("Renderer", "bg_red",   1.0);
 | 
			
		||||
    Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 1.0);
 | 
			
		||||
    Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 1.0);
 | 
			
		||||
    Settings::values.bg_blue  = (float)sdl2_config->GetReal("Renderer", "bg_blue",  1.0);
 | 
			
		||||
    Settings::values.bg_blue = (float)sdl2_config->GetReal("Renderer", "bg_blue", 1.0);
 | 
			
		||||
 | 
			
		||||
    // Audio
 | 
			
		||||
    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
 | 
			
		||||
    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
 | 
			
		||||
    Settings::values.is_new_3ds = sdl2_config->GetBoolean("System", "is_new_3ds", false);
 | 
			
		||||
@ -94,7 +90,8 @@ void Config::ReadValues() {
 | 
			
		||||
 | 
			
		||||
    // Debugging
 | 
			
		||||
    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() {
 | 
			
		||||
 | 
			
		||||
@ -6,15 +6,15 @@
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
#include <inih/cpp/INIReader.h>
 | 
			
		||||
 | 
			
		||||
class Config {
 | 
			
		||||
    std::unique_ptr<INIReader> sdl2_config;
 | 
			
		||||
    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();
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    Config();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -104,5 +104,4 @@ log_filter = *:Info
 | 
			
		||||
use_gdbstub=false
 | 
			
		||||
gdbstub_port=24689
 | 
			
		||||
)";
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -5,22 +5,16 @@
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <cstdlib>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
#define SDL_MAIN_HANDLED
 | 
			
		||||
#include <SDL.h>
 | 
			
		||||
 | 
			
		||||
#include <glad/glad.h>
 | 
			
		||||
 | 
			
		||||
#include "citra/emu_window/emu_window_sdl2.h"
 | 
			
		||||
#include "common/key_map.h"
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
#include "common/scm_rev.h"
 | 
			
		||||
#include "common/string_util.h"
 | 
			
		||||
 | 
			
		||||
#include "core/settings.h"
 | 
			
		||||
#include "core/hle/service/hid/hid.h"
 | 
			
		||||
 | 
			
		||||
#include "citra/emu_window/emu_window_sdl2.h"
 | 
			
		||||
 | 
			
		||||
#include "core/settings.h"
 | 
			
		||||
#include "video_core/video_core.h"
 | 
			
		||||
 | 
			
		||||
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) {
 | 
			
		||||
    if (state == SDL_PRESSED) {
 | 
			
		||||
        KeyMap::PressKey(*this, { key, keyboard_id });
 | 
			
		||||
        KeyMap::PressKey(*this, {key, keyboard_id});
 | 
			
		||||
    } 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);
 | 
			
		||||
 | 
			
		||||
    NotifyFramebufferLayoutChanged(EmuWindow::FramebufferLayout::DefaultScreenLayout(width, height));
 | 
			
		||||
    NotifyFramebufferLayoutChanged(
 | 
			
		||||
        EmuWindow::FramebufferLayout::DefaultScreenLayout(width, height));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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_ALPHA_SIZE, 0);
 | 
			
		||||
 | 
			
		||||
    std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc);
 | 
			
		||||
    render_window = SDL_CreateWindow(window_title.c_str(),
 | 
			
		||||
    std::string window_title =
 | 
			
		||||
        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, // y position
 | 
			
		||||
        VideoCore::kScreenTopWidth,
 | 
			
		||||
        VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight,
 | 
			
		||||
        VideoCore::kScreenTopWidth, VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight,
 | 
			
		||||
        SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
 | 
			
		||||
 | 
			
		||||
    if (render_window == nullptr) {
 | 
			
		||||
@ -171,10 +167,14 @@ void EmuWindow_SDL2::DoneCurrent() {
 | 
			
		||||
void EmuWindow_SDL2::ReloadSetKeymaps() {
 | 
			
		||||
    KeyMap::ClearKeyMapping(keyboard_id);
 | 
			
		||||
    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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,6 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <utility>
 | 
			
		||||
 | 
			
		||||
#include "common/emu_window.h"
 | 
			
		||||
 | 
			
		||||
struct SDL_Window;
 | 
			
		||||
@ -47,7 +46,8 @@ private:
 | 
			
		||||
    void OnResize();
 | 
			
		||||
 | 
			
		||||
    /// 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?
 | 
			
		||||
    bool is_open = true;
 | 
			
		||||
@ -55,7 +55,7 @@ private:
 | 
			
		||||
    /// Internal SDL2 render window
 | 
			
		||||
    SDL_Window* render_window;
 | 
			
		||||
 | 
			
		||||
    using SDL_GLContext = void *;
 | 
			
		||||
    using SDL_GLContext = void*;
 | 
			
		||||
    /// The OpenGL context associated with the window
 | 
			
		||||
    SDL_GLContext gl_context;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -2,15 +2,15 @@
 | 
			
		||||
// Microsoft Visual C++ generated include file.
 | 
			
		||||
// Used by pcafe.rc
 | 
			
		||||
//
 | 
			
		||||
#define IDI_ICON3                       103
 | 
			
		||||
#define IDI_ICON3 103
 | 
			
		||||
 | 
			
		||||
// Next default values for new objects
 | 
			
		||||
//
 | 
			
		||||
#ifdef APSTUDIO_INVOKED
 | 
			
		||||
#ifndef APSTUDIO_READONLY_SYMBOLS
 | 
			
		||||
#define _APS_NEXT_RESOURCE_VALUE        105
 | 
			
		||||
#define _APS_NEXT_COMMAND_VALUE         40001
 | 
			
		||||
#define _APS_NEXT_CONTROL_VALUE         1001
 | 
			
		||||
#define _APS_NEXT_SYMED_VALUE           101
 | 
			
		||||
#define _APS_NEXT_RESOURCE_VALUE 105
 | 
			
		||||
#define _APS_NEXT_COMMAND_VALUE 40001
 | 
			
		||||
#define _APS_NEXT_CONTROL_VALUE 1001
 | 
			
		||||
#define _APS_NEXT_SYMED_VALUE 101
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@ -9,27 +9,23 @@
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "citra_qt/bootmanager.h"
 | 
			
		||||
 | 
			
		||||
#include "common/key_map.h"
 | 
			
		||||
#include "common/microprofile.h"
 | 
			
		||||
#include "common/scm_rev.h"
 | 
			
		||||
#include "common/string_util.h"
 | 
			
		||||
 | 
			
		||||
#include "core/core.h"
 | 
			
		||||
#include "core/settings.h"
 | 
			
		||||
#include "core/system.h"
 | 
			
		||||
 | 
			
		||||
#include "video_core/debug_utils/debug_utils.h"
 | 
			
		||||
#include "video_core/video_core.h"
 | 
			
		||||
 | 
			
		||||
#define APP_NAME        "citra"
 | 
			
		||||
#define APP_VERSION     "0.1-" VERSION
 | 
			
		||||
#define APP_TITLE       APP_NAME " " APP_VERSION
 | 
			
		||||
#define COPYRIGHT       "Copyright (C) 2013-2014 Citra Team"
 | 
			
		||||
#define APP_NAME "citra"
 | 
			
		||||
#define APP_VERSION "0.1-" VERSION
 | 
			
		||||
#define APP_TITLE APP_NAME " " APP_VERSION
 | 
			
		||||
#define COPYRIGHT "Copyright (C) 2013-2014 Citra Team"
 | 
			
		||||
 | 
			
		||||
EmuThread::EmuThread(GRenderWindow* render_window) :
 | 
			
		||||
    exec_step(false), running(false), stop_run(false), render_window(render_window) {
 | 
			
		||||
}
 | 
			
		||||
EmuThread::EmuThread(GRenderWindow* render_window)
 | 
			
		||||
    : exec_step(false), running(false), stop_run(false), render_window(render_window) {}
 | 
			
		||||
 | 
			
		||||
void EmuThread::run() {
 | 
			
		||||
    render_window->MakeCurrent();
 | 
			
		||||
@ -64,7 +60,7 @@ void EmuThread::run() {
 | 
			
		||||
            was_active = false;
 | 
			
		||||
        } else {
 | 
			
		||||
            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();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
class GGLWidgetInternal : public QGLWidget
 | 
			
		||||
{
 | 
			
		||||
class GGLWidgetInternal : public QGLWidget {
 | 
			
		||||
public:
 | 
			
		||||
    GGLWidgetInternal(QGLFormat fmt, GRenderWindow* parent)
 | 
			
		||||
                     : QGLWidget(fmt, parent), parent(parent) {
 | 
			
		||||
    }
 | 
			
		||||
        : QGLWidget(fmt, parent), parent(parent) {}
 | 
			
		||||
 | 
			
		||||
    void paintEvent(QPaintEvent* ev) override {
 | 
			
		||||
        if (do_painting) {
 | 
			
		||||
@ -98,37 +93,43 @@ public:
 | 
			
		||||
        parent->OnFramebufferSizeChanged();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void DisablePainting() { do_painting = false; }
 | 
			
		||||
    void EnablePainting() { do_painting = true; }
 | 
			
		||||
    void DisablePainting() {
 | 
			
		||||
        do_painting = false;
 | 
			
		||||
    }
 | 
			
		||||
    void EnablePainting() {
 | 
			
		||||
        do_painting = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    GRenderWindow* parent;
 | 
			
		||||
    bool do_painting;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) :
 | 
			
		||||
    QWidget(parent), keyboard_id(0), emu_thread(emu_thread), child(nullptr) {
 | 
			
		||||
GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
 | 
			
		||||
    : 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));
 | 
			
		||||
 | 
			
		||||
    keyboard_id = KeyMap::NewDeviceId();
 | 
			
		||||
    ReloadSetKeymaps();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GRenderWindow::moveContext()
 | 
			
		||||
{
 | 
			
		||||
void GRenderWindow::moveContext() {
 | 
			
		||||
    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 the thread started running, move the GL Context to the new thread. Otherwise, move it back.
 | 
			
		||||
    auto thread = (QThread::currentThread() == qApp->thread() && emu_thread != nullptr) ? emu_thread : qApp->thread();
 | 
			
		||||
    // If the thread started running, move the GL Context to the new thread. Otherwise, move it
 | 
			
		||||
    // back.
 | 
			
		||||
    auto thread = (QThread::currentThread() == qApp->thread() && emu_thread != nullptr)
 | 
			
		||||
                      ? emu_thread
 | 
			
		||||
                      : qApp->thread();
 | 
			
		||||
    child->context()->moveToThread(thread);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GRenderWindow::SwapBuffers()
 | 
			
		||||
{
 | 
			
		||||
void GRenderWindow::SwapBuffers() {
 | 
			
		||||
#if !defined(QT_NO_DEBUG)
 | 
			
		||||
    // 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
 | 
			
		||||
@ -139,54 +140,48 @@ void GRenderWindow::SwapBuffers()
 | 
			
		||||
    child->swapBuffers();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GRenderWindow::MakeCurrent()
 | 
			
		||||
{
 | 
			
		||||
void GRenderWindow::MakeCurrent() {
 | 
			
		||||
    child->makeCurrent();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GRenderWindow::DoneCurrent()
 | 
			
		||||
{
 | 
			
		||||
void GRenderWindow::DoneCurrent() {
 | 
			
		||||
    child->doneCurrent();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GRenderWindow::PollEvents() {
 | 
			
		||||
}
 | 
			
		||||
void GRenderWindow::PollEvents() {}
 | 
			
		||||
 | 
			
		||||
// On Qt 5.0+, this correctly gets the size of the framebuffer (pixels).
 | 
			
		||||
//
 | 
			
		||||
// Older versions get the window size (density independent pixels),
 | 
			
		||||
// and hence, do not support DPI scaling ("retina" displays).
 | 
			
		||||
// The result will be a viewport that is smaller than the extent of the window.
 | 
			
		||||
void GRenderWindow::OnFramebufferSizeChanged()
 | 
			
		||||
{
 | 
			
		||||
    // Screen changes potentially incur a change in screen DPI, hence we should update the framebuffer size
 | 
			
		||||
void GRenderWindow::OnFramebufferSizeChanged() {
 | 
			
		||||
    // Screen changes potentially incur a change in screen DPI, hence we should update the
 | 
			
		||||
    // framebuffer size
 | 
			
		||||
    qreal pixelRatio = windowPixelRatio();
 | 
			
		||||
    unsigned width = child->QPaintDevice::width() * 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();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GRenderWindow::RestoreGeometry()
 | 
			
		||||
{
 | 
			
		||||
void GRenderWindow::RestoreGeometry() {
 | 
			
		||||
    // We don't want to back up the geometry here (obviously)
 | 
			
		||||
    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
 | 
			
		||||
    QWidget::restoreGeometry(geometry);
 | 
			
		||||
    BackupGeometry();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QByteArray GRenderWindow::saveGeometry()
 | 
			
		||||
{
 | 
			
		||||
QByteArray GRenderWindow::saveGeometry() {
 | 
			
		||||
    // If we are a top-level widget, store the current geometry
 | 
			
		||||
    // otherwise, store the last backup
 | 
			
		||||
    if (parent() == nullptr)
 | 
			
		||||
@ -195,8 +190,7 @@ QByteArray GRenderWindow::saveGeometry()
 | 
			
		||||
        return geometry;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
qreal GRenderWindow::windowPixelRatio()
 | 
			
		||||
{
 | 
			
		||||
qreal GRenderWindow::windowPixelRatio() {
 | 
			
		||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
 | 
			
		||||
    // windowHandle() might not be accessible until the window is displayed to screen.
 | 
			
		||||
    return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f;
 | 
			
		||||
@ -210,20 +204,16 @@ void GRenderWindow::closeEvent(QCloseEvent* event) {
 | 
			
		||||
    QWidget::closeEvent(event);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GRenderWindow::keyPressEvent(QKeyEvent* event)
 | 
			
		||||
{
 | 
			
		||||
    KeyMap::PressKey(*this, { event->key(), keyboard_id });
 | 
			
		||||
void GRenderWindow::keyPressEvent(QKeyEvent* event) {
 | 
			
		||||
    KeyMap::PressKey(*this, {event->key(), keyboard_id});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GRenderWindow::keyReleaseEvent(QKeyEvent* event)
 | 
			
		||||
{
 | 
			
		||||
    KeyMap::ReleaseKey(*this, { event->key(), keyboard_id });
 | 
			
		||||
void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
 | 
			
		||||
    KeyMap::ReleaseKey(*this, {event->key(), keyboard_id});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GRenderWindow::mousePressEvent(QMouseEvent *event)
 | 
			
		||||
{
 | 
			
		||||
    if (event->button() == Qt::LeftButton)
 | 
			
		||||
    {
 | 
			
		||||
void GRenderWindow::mousePressEvent(QMouseEvent* event) {
 | 
			
		||||
    if (event->button() == Qt::LeftButton) {
 | 
			
		||||
        auto pos = event->pos();
 | 
			
		||||
        qreal pixelRatio = windowPixelRatio();
 | 
			
		||||
        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();
 | 
			
		||||
    qreal pixelRatio = windowPixelRatio();
 | 
			
		||||
    this->TouchMoved(std::max(static_cast<unsigned>(pos.x() * 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)
 | 
			
		||||
        this->TouchReleased();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GRenderWindow::ReloadSetKeymaps()
 | 
			
		||||
{
 | 
			
		||||
void GRenderWindow::ReloadSetKeymaps() {
 | 
			
		||||
    KeyMap::ClearKeyMapping(keyboard_id);
 | 
			
		||||
    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));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -267,7 +255,8 @@ void GRenderWindow::InitRenderTarget() {
 | 
			
		||||
        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;
 | 
			
		||||
    fmt.setVersion(3, 3);
 | 
			
		||||
    fmt.setProfile(QGLFormat::CoreProfile);
 | 
			
		||||
@ -279,7 +268,8 @@ void GRenderWindow::InitRenderTarget() {
 | 
			
		||||
    child = new GGLWidgetInternal(fmt, this);
 | 
			
		||||
    QBoxLayout* layout = new QHBoxLayout(this);
 | 
			
		||||
 | 
			
		||||
    resize(VideoCore::kScreenTopWidth, VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight);
 | 
			
		||||
    resize(VideoCore::kScreenTopWidth,
 | 
			
		||||
           VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight);
 | 
			
		||||
    layout->addWidget(child);
 | 
			
		||||
    layout->setMargin(0);
 | 
			
		||||
    setLayout(layout);
 | 
			
		||||
@ -292,7 +282,8 @@ void GRenderWindow::InitRenderTarget() {
 | 
			
		||||
    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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -306,11 +297,12 @@ void GRenderWindow::OnEmulationStopping() {
 | 
			
		||||
    child->EnablePainting();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GRenderWindow::showEvent(QShowEvent * event) {
 | 
			
		||||
void GRenderWindow::showEvent(QShowEvent* 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.
 | 
			
		||||
    #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
 | 
			
		||||
        connect(this->windowHandle(), SIGNAL(screenChanged(QScreen*)), this, SLOT(OnFramebufferSizeChanged()), Qt::UniqueConnection);
 | 
			
		||||
    #endif
 | 
			
		||||
    connect(this->windowHandle(), SIGNAL(screenChanged(QScreen*)), this,
 | 
			
		||||
            SLOT(OnFramebufferSizeChanged()), Qt::UniqueConnection);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -5,10 +5,8 @@
 | 
			
		||||
#include <atomic>
 | 
			
		||||
#include <condition_variable>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
 | 
			
		||||
#include <QGLWidget>
 | 
			
		||||
#include <QThread>
 | 
			
		||||
 | 
			
		||||
#include "common/emu_window.h"
 | 
			
		||||
#include "common/thread.h"
 | 
			
		||||
 | 
			
		||||
@ -19,8 +17,7 @@ class GGLWidgetInternal;
 | 
			
		||||
class GMainWindow;
 | 
			
		||||
class GRenderWindow;
 | 
			
		||||
 | 
			
		||||
class EmuThread : public QThread
 | 
			
		||||
{
 | 
			
		||||
class EmuThread : public QThread {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
@ -58,7 +55,9 @@ public:
 | 
			
		||||
     * @return True if the emulation thread is running, otherwise false
 | 
			
		||||
     * @note This function is thread-safe
 | 
			
		||||
     */
 | 
			
		||||
    bool IsRunning() { return running; }
 | 
			
		||||
    bool IsRunning() {
 | 
			
		||||
        return running;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Requests for the emulation thread to stop running
 | 
			
		||||
@ -81,20 +80,23 @@ signals:
 | 
			
		||||
    /**
 | 
			
		||||
     * 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();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 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();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class GRenderWindow : public QWidget, public EmuWindow
 | 
			
		||||
{
 | 
			
		||||
class GRenderWindow : public QWidget, public EmuWindow {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
@ -109,7 +111,7 @@ public:
 | 
			
		||||
    void BackupGeometry();
 | 
			
		||||
    void RestoreGeometry();
 | 
			
		||||
    void restoreGeometry(const QByteArray& geometry); // overridden
 | 
			
		||||
    QByteArray saveGeometry();  // overridden
 | 
			
		||||
    QByteArray saveGeometry();                        // overridden
 | 
			
		||||
 | 
			
		||||
    qreal windowPixelRatio();
 | 
			
		||||
 | 
			
		||||
@ -118,9 +120,9 @@ public:
 | 
			
		||||
    void keyPressEvent(QKeyEvent* event) override;
 | 
			
		||||
    void keyReleaseEvent(QKeyEvent* event) override;
 | 
			
		||||
 | 
			
		||||
    void mousePressEvent(QMouseEvent *event) override;
 | 
			
		||||
    void mouseMoveEvent(QMouseEvent *event) override;
 | 
			
		||||
    void mouseReleaseEvent(QMouseEvent *event) override;
 | 
			
		||||
    void mousePressEvent(QMouseEvent* event) override;
 | 
			
		||||
    void mouseMoveEvent(QMouseEvent* event) override;
 | 
			
		||||
    void mouseReleaseEvent(QMouseEvent* event) override;
 | 
			
		||||
 | 
			
		||||
    void ReloadSetKeymaps() override;
 | 
			
		||||
 | 
			
		||||
@ -129,7 +131,7 @@ public:
 | 
			
		||||
    void InitRenderTarget();
 | 
			
		||||
 | 
			
		||||
public slots:
 | 
			
		||||
    void moveContext();  // overridden
 | 
			
		||||
    void moveContext(); // overridden
 | 
			
		||||
 | 
			
		||||
    void OnEmulationStarting(EmuThread* emu_thread);
 | 
			
		||||
    void OnEmulationStopping();
 | 
			
		||||
@ -140,7 +142,8 @@ signals:
 | 
			
		||||
    void Closed();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    void OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) override;
 | 
			
		||||
    void OnMinimalClientAreaChangeRequest(
 | 
			
		||||
        const std::pair<unsigned, unsigned>& minimal_size) override;
 | 
			
		||||
 | 
			
		||||
    GGLWidgetInternal* child;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -3,10 +3,8 @@
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include <QSettings>
 | 
			
		||||
 | 
			
		||||
#include "citra_qt/config.h"
 | 
			
		||||
#include "citra_qt/ui_settings.h"
 | 
			
		||||
 | 
			
		||||
#include "common/file_util.h"
 | 
			
		||||
 | 
			
		||||
Config::Config() {
 | 
			
		||||
@ -20,24 +18,23 @@ Config::Config() {
 | 
			
		||||
 | 
			
		||||
const std::array<QVariant, Settings::NativeInput::NUM_INPUTS> Config::defaults = {
 | 
			
		||||
    // directly mapped keys
 | 
			
		||||
    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_M, Qt::Key_N, Qt::Key_B,
 | 
			
		||||
    Qt::Key_T, Qt::Key_G, Qt::Key_F, Qt::Key_H,
 | 
			
		||||
    Qt::Key_I, Qt::Key_K, Qt::Key_J, Qt::Key_L,
 | 
			
		||||
    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_M, Qt::Key_N, Qt::Key_B, 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
 | 
			
		||||
    Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right,
 | 
			
		||||
    Qt::Key_D,
 | 
			
		||||
    Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right, Qt::Key_D,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void Config::ReadValues() {
 | 
			
		||||
    qt_config->beginGroup("Controls");
 | 
			
		||||
    for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++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->beginGroup("Core");
 | 
			
		||||
@ -48,17 +45,19 @@ void Config::ReadValues() {
 | 
			
		||||
    qt_config->beginGroup("Renderer");
 | 
			
		||||
    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_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.bg_red   = qt_config->value("bg_red",   1.0).toFloat();
 | 
			
		||||
    Settings::values.bg_red = qt_config->value("bg_red", 1.0).toFloat();
 | 
			
		||||
    Settings::values.bg_green = qt_config->value("bg_green", 1.0).toFloat();
 | 
			
		||||
    Settings::values.bg_blue  = qt_config->value("bg_blue",  1.0).toFloat();
 | 
			
		||||
    Settings::values.bg_blue = qt_config->value("bg_blue", 1.0).toFloat();
 | 
			
		||||
    qt_config->endGroup();
 | 
			
		||||
 | 
			
		||||
    qt_config->beginGroup("Audio");
 | 
			
		||||
    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->beginGroup("Data Storage");
 | 
			
		||||
@ -84,10 +83,14 @@ void Config::ReadValues() {
 | 
			
		||||
    qt_config->beginGroup("UILayout");
 | 
			
		||||
    UISettings::values.geometry = qt_config->value("geometry").toByteArray();
 | 
			
		||||
    UISettings::values.state = qt_config->value("state").toByteArray();
 | 
			
		||||
    UISettings::values.renderwindow_geometry = qt_config->value("geometryRenderWindow").toByteArray();
 | 
			
		||||
    UISettings::values.gamelist_header_state = qt_config->value("gameListHeaderState").toByteArray();
 | 
			
		||||
    UISettings::values.microprofile_geometry = qt_config->value("microProfileDialogGeometry").toByteArray();
 | 
			
		||||
    UISettings::values.microprofile_visible = qt_config->value("microProfileDialogVisible", false).toBool();
 | 
			
		||||
    UISettings::values.renderwindow_geometry =
 | 
			
		||||
        qt_config->value("geometryRenderWindow").toByteArray();
 | 
			
		||||
    UISettings::values.gamelist_header_state =
 | 
			
		||||
        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->beginGroup("Paths");
 | 
			
		||||
@ -106,10 +109,10 @@ void Config::ReadValues() {
 | 
			
		||||
        QStringList hotkeys = qt_config->childGroups();
 | 
			
		||||
        for (auto hotkey : hotkeys) {
 | 
			
		||||
            qt_config->beginGroup(hotkey);
 | 
			
		||||
            UISettings::values.shortcuts.emplace_back(
 | 
			
		||||
                        UISettings::Shortcut(group + "/" + hotkey,
 | 
			
		||||
                                             UISettings::ContextualShortcut(qt_config->value("KeySeq").toString(),
 | 
			
		||||
                                                                            qt_config->value("Context").toInt())));
 | 
			
		||||
            UISettings::values.shortcuts.emplace_back(UISettings::Shortcut(
 | 
			
		||||
                group + "/" + hotkey,
 | 
			
		||||
                UISettings::ContextualShortcut(qt_config->value("KeySeq").toString(),
 | 
			
		||||
                                               qt_config->value("Context").toInt())));
 | 
			
		||||
            qt_config->endGroup();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -119,7 +122,7 @@ void Config::ReadValues() {
 | 
			
		||||
 | 
			
		||||
    UISettings::values.single_window_mode = qt_config->value("singleWindowMode", 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();
 | 
			
		||||
 | 
			
		||||
    qt_config->endGroup();
 | 
			
		||||
@ -129,9 +132,10 @@ void Config::SaveValues() {
 | 
			
		||||
    qt_config->beginGroup("Controls");
 | 
			
		||||
    for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++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->beginGroup("Core");
 | 
			
		||||
@ -146,9 +150,9 @@ void Config::SaveValues() {
 | 
			
		||||
    qt_config->setValue("use_vsync", Settings::values.use_vsync);
 | 
			
		||||
 | 
			
		||||
    // Cast to double because Qt's written float values are not human-readable
 | 
			
		||||
    qt_config->setValue("bg_red",   (double)Settings::values.bg_red);
 | 
			
		||||
    qt_config->setValue("bg_red", (double)Settings::values.bg_red);
 | 
			
		||||
    qt_config->setValue("bg_green", (double)Settings::values.bg_green);
 | 
			
		||||
    qt_config->setValue("bg_blue",  (double)Settings::values.bg_blue);
 | 
			
		||||
    qt_config->setValue("bg_blue", (double)Settings::values.bg_blue);
 | 
			
		||||
    qt_config->endGroup();
 | 
			
		||||
 | 
			
		||||
    qt_config->beginGroup("Audio");
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,6 @@
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <QVariant>
 | 
			
		||||
 | 
			
		||||
#include "core/settings.h"
 | 
			
		||||
 | 
			
		||||
class QSettings;
 | 
			
		||||
@ -17,6 +16,7 @@ class Config {
 | 
			
		||||
 | 
			
		||||
    void ReadValues();
 | 
			
		||||
    void SaveValues();
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    Config();
 | 
			
		||||
    ~Config();
 | 
			
		||||
 | 
			
		||||
@ -3,16 +3,12 @@
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include "audio_core/sink_details.h"
 | 
			
		||||
 | 
			
		||||
#include "citra_qt/configure_audio.h"
 | 
			
		||||
#include "core/settings.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->output_sink_combo_box->clear();
 | 
			
		||||
@ -24,8 +20,7 @@ ConfigureAudio::ConfigureAudio(QWidget* parent) :
 | 
			
		||||
    this->setConfiguration();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ConfigureAudio::~ConfigureAudio() {
 | 
			
		||||
}
 | 
			
		||||
ConfigureAudio::~ConfigureAudio() {}
 | 
			
		||||
 | 
			
		||||
void ConfigureAudio::setConfiguration() {
 | 
			
		||||
    int new_sink_index = 0;
 | 
			
		||||
@ -41,7 +36,9 @@ void ConfigureAudio::setConfiguration() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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::Apply();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -3,20 +3,15 @@
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include "citra_qt/configure_debug.h"
 | 
			
		||||
#include "core/settings.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);
 | 
			
		||||
    this->setConfiguration();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ConfigureDebug::~ConfigureDebug() {
 | 
			
		||||
}
 | 
			
		||||
ConfigureDebug::~ConfigureDebug() {}
 | 
			
		||||
 | 
			
		||||
void ConfigureDebug::setConfiguration() {
 | 
			
		||||
    ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub);
 | 
			
		||||
 | 
			
		||||
@ -11,12 +11,11 @@ namespace Ui {
 | 
			
		||||
class ConfigureDebug;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class ConfigureDebug : public QWidget
 | 
			
		||||
{
 | 
			
		||||
class ConfigureDebug : public QWidget {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    explicit ConfigureDebug(QWidget *parent = nullptr);
 | 
			
		||||
    explicit ConfigureDebug(QWidget* parent = nullptr);
 | 
			
		||||
    ~ConfigureDebug();
 | 
			
		||||
 | 
			
		||||
    void applyConfiguration();
 | 
			
		||||
 | 
			
		||||
@ -4,24 +4,17 @@
 | 
			
		||||
 | 
			
		||||
#include "citra_qt/config.h"
 | 
			
		||||
#include "citra_qt/configure_dialog.h"
 | 
			
		||||
#include "core/settings.h"
 | 
			
		||||
#include "ui_configure.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include "core/settings.h"
 | 
			
		||||
 | 
			
		||||
ConfigureDialog::ConfigureDialog(QWidget *parent) :
 | 
			
		||||
    QDialog(parent),
 | 
			
		||||
    ui(new Ui::ConfigureDialog)
 | 
			
		||||
{
 | 
			
		||||
ConfigureDialog::ConfigureDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ConfigureDialog) {
 | 
			
		||||
    ui->setupUi(this);
 | 
			
		||||
    this->setConfiguration();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ConfigureDialog::~ConfigureDialog() {
 | 
			
		||||
}
 | 
			
		||||
ConfigureDialog::~ConfigureDialog() {}
 | 
			
		||||
 | 
			
		||||
void ConfigureDialog::setConfiguration() {
 | 
			
		||||
}
 | 
			
		||||
void ConfigureDialog::setConfiguration() {}
 | 
			
		||||
 | 
			
		||||
void ConfigureDialog::applyConfiguration() {
 | 
			
		||||
    ui->generalTab->applyConfiguration();
 | 
			
		||||
 | 
			
		||||
@ -11,12 +11,11 @@ namespace Ui {
 | 
			
		||||
class ConfigureDialog;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class ConfigureDialog : public QDialog
 | 
			
		||||
{
 | 
			
		||||
class ConfigureDialog : public QDialog {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    explicit ConfigureDialog(QWidget *parent);
 | 
			
		||||
    explicit ConfigureDialog(QWidget* parent);
 | 
			
		||||
    ~ConfigureDialog();
 | 
			
		||||
 | 
			
		||||
    void applyConfiguration();
 | 
			
		||||
 | 
			
		||||
@ -4,23 +4,20 @@
 | 
			
		||||
 | 
			
		||||
#include "citra_qt/configure_general.h"
 | 
			
		||||
#include "citra_qt/ui_settings.h"
 | 
			
		||||
#include "ui_configure_general.h"
 | 
			
		||||
 | 
			
		||||
#include "core/settings.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);
 | 
			
		||||
    this->setConfiguration();
 | 
			
		||||
 | 
			
		||||
    ui->toggle_cpu_jit->setEnabled(!System::IsPoweredOn());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ConfigureGeneral::~ConfigureGeneral() {
 | 
			
		||||
}
 | 
			
		||||
ConfigureGeneral::~ConfigureGeneral() {}
 | 
			
		||||
 | 
			
		||||
void ConfigureGeneral::setConfiguration() {
 | 
			
		||||
    ui->toggle_deepscan->setChecked(UISettings::values.gamedir_deepscan);
 | 
			
		||||
 | 
			
		||||
@ -11,12 +11,11 @@ namespace Ui {
 | 
			
		||||
class ConfigureGeneral;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class ConfigureGeneral : public QWidget
 | 
			
		||||
{
 | 
			
		||||
class ConfigureGeneral : public QWidget {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    explicit ConfigureGeneral(QWidget *parent = nullptr);
 | 
			
		||||
    explicit ConfigureGeneral(QWidget* parent = nullptr);
 | 
			
		||||
    ~ConfigureGeneral();
 | 
			
		||||
 | 
			
		||||
    void applyConfiguration();
 | 
			
		||||
 | 
			
		||||
@ -3,23 +3,20 @@
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include "citra_qt/configure_graphics.h"
 | 
			
		||||
#include "ui_configure_graphics.h"
 | 
			
		||||
 | 
			
		||||
#include "core/settings.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);
 | 
			
		||||
    this->setConfiguration();
 | 
			
		||||
 | 
			
		||||
    ui->toggle_vsync->setEnabled(!System::IsPoweredOn());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ConfigureGraphics::~ConfigureGraphics() {
 | 
			
		||||
}
 | 
			
		||||
ConfigureGraphics::~ConfigureGraphics() {}
 | 
			
		||||
 | 
			
		||||
void ConfigureGraphics::setConfiguration() {
 | 
			
		||||
    ui->toggle_hw_renderer->setChecked(Settings::values.use_hw_renderer);
 | 
			
		||||
 | 
			
		||||
@ -11,12 +11,11 @@ namespace Ui {
 | 
			
		||||
class ConfigureGraphics;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class ConfigureGraphics : public QWidget
 | 
			
		||||
{
 | 
			
		||||
class ConfigureGraphics : public QWidget {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    explicit ConfigureGraphics(QWidget *parent = nullptr);
 | 
			
		||||
    explicit ConfigureGraphics(QWidget* parent = nullptr);
 | 
			
		||||
    ~ConfigureGraphics();
 | 
			
		||||
 | 
			
		||||
    void applyConfiguration();
 | 
			
		||||
 | 
			
		||||
@ -5,38 +5,39 @@
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <utility>
 | 
			
		||||
#include <QTimer>
 | 
			
		||||
 | 
			
		||||
#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);
 | 
			
		||||
 | 
			
		||||
    // Initialize mapping of input enum to UI button.
 | 
			
		||||
    input_mapping = {
 | 
			
		||||
        { std::make_pair(Settings::NativeInput::Values::A, ui->buttonA) },
 | 
			
		||||
        { std::make_pair(Settings::NativeInput::Values::B, ui->buttonB) },
 | 
			
		||||
        { std::make_pair(Settings::NativeInput::Values::X, ui->buttonX) },
 | 
			
		||||
        { std::make_pair(Settings::NativeInput::Values::Y, ui->buttonY) },
 | 
			
		||||
        { std::make_pair(Settings::NativeInput::Values::L, ui->buttonL) },
 | 
			
		||||
        { std::make_pair(Settings::NativeInput::Values::R, ui->buttonR) },
 | 
			
		||||
        { std::make_pair(Settings::NativeInput::Values::ZL, ui->buttonZL) },
 | 
			
		||||
        { std::make_pair(Settings::NativeInput::Values::ZR, ui->buttonZR) },
 | 
			
		||||
        { std::make_pair(Settings::NativeInput::Values::START, ui->buttonStart) },
 | 
			
		||||
        { std::make_pair(Settings::NativeInput::Values::SELECT, ui->buttonSelect) },
 | 
			
		||||
        { std::make_pair(Settings::NativeInput::Values::HOME, ui->buttonHome) },
 | 
			
		||||
        { std::make_pair(Settings::NativeInput::Values::DUP, ui->buttonDpadUp) },
 | 
			
		||||
        { std::make_pair(Settings::NativeInput::Values::DDOWN, ui->buttonDpadDown) },
 | 
			
		||||
        { std::make_pair(Settings::NativeInput::Values::DLEFT, ui->buttonDpadLeft) },
 | 
			
		||||
        { std::make_pair(Settings::NativeInput::Values::DRIGHT, ui->buttonDpadRight) },
 | 
			
		||||
        { std::make_pair(Settings::NativeInput::Values::CUP, ui->buttonCStickUp) },
 | 
			
		||||
        { std::make_pair(Settings::NativeInput::Values::CDOWN, ui->buttonCStickDown) },
 | 
			
		||||
        { std::make_pair(Settings::NativeInput::Values::CLEFT, ui->buttonCStickLeft) },
 | 
			
		||||
        { std::make_pair(Settings::NativeInput::Values::CRIGHT, ui->buttonCStickRight) },
 | 
			
		||||
        { std::make_pair(Settings::NativeInput::Values::CIRCLE_UP, ui->buttonCircleUp) },
 | 
			
		||||
        { std::make_pair(Settings::NativeInput::Values::CIRCLE_DOWN, ui->buttonCircleDown) },
 | 
			
		||||
        { std::make_pair(Settings::NativeInput::Values::CIRCLE_LEFT, ui->buttonCircleLeft) },
 | 
			
		||||
        { std::make_pair(Settings::NativeInput::Values::CIRCLE_RIGHT, ui->buttonCircleRight) },
 | 
			
		||||
        { std::make_pair(Settings::NativeInput::Values::CIRCLE_MODIFIER, ui->buttonCircleMod) },
 | 
			
		||||
        {Settings::NativeInput::Values::A, ui->buttonA},
 | 
			
		||||
        {Settings::NativeInput::Values::B, ui->buttonB},
 | 
			
		||||
        {Settings::NativeInput::Values::X, ui->buttonX},
 | 
			
		||||
        {Settings::NativeInput::Values::Y, ui->buttonY},
 | 
			
		||||
        {Settings::NativeInput::Values::L, ui->buttonL},
 | 
			
		||||
        {Settings::NativeInput::Values::R, ui->buttonR},
 | 
			
		||||
        {Settings::NativeInput::Values::ZL, ui->buttonZL},
 | 
			
		||||
        {Settings::NativeInput::Values::ZR, ui->buttonZR},
 | 
			
		||||
        {Settings::NativeInput::Values::START, ui->buttonStart},
 | 
			
		||||
        {Settings::NativeInput::Values::SELECT, ui->buttonSelect},
 | 
			
		||||
        {Settings::NativeInput::Values::HOME, ui->buttonHome},
 | 
			
		||||
        {Settings::NativeInput::Values::DUP, ui->buttonDpadUp},
 | 
			
		||||
        {Settings::NativeInput::Values::DDOWN, ui->buttonDpadDown},
 | 
			
		||||
        {Settings::NativeInput::Values::DLEFT, ui->buttonDpadLeft},
 | 
			
		||||
        {Settings::NativeInput::Values::DRIGHT, ui->buttonDpadRight},
 | 
			
		||||
        {Settings::NativeInput::Values::CUP, ui->buttonCStickUp},
 | 
			
		||||
        {Settings::NativeInput::Values::CDOWN, ui->buttonCStickDown},
 | 
			
		||||
        {Settings::NativeInput::Values::CLEFT, ui->buttonCStickLeft},
 | 
			
		||||
        {Settings::NativeInput::Values::CRIGHT, ui->buttonCStickRight},
 | 
			
		||||
        {Settings::NativeInput::Values::CIRCLE_UP, ui->buttonCircleUp},
 | 
			
		||||
        {Settings::NativeInput::Values::CIRCLE_DOWN, ui->buttonCircleDown},
 | 
			
		||||
        {Settings::NativeInput::Values::CIRCLE_LEFT, ui->buttonCircleLeft},
 | 
			
		||||
        {Settings::NativeInput::Values::CIRCLE_RIGHT, ui->buttonCircleRight},
 | 
			
		||||
        {Settings::NativeInput::Values::CIRCLE_MODIFIER, ui->buttonCircleMod},
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // Attach handle click method to each button click.
 | 
			
		||||
@ -47,7 +48,10 @@ ConfigureInput::ConfigureInput(QWidget* parent) : QWidget(parent), ui(std::make_
 | 
			
		||||
    setFocusPolicy(Qt::ClickFocus);
 | 
			
		||||
    timer = new QTimer(this);
 | 
			
		||||
    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();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -59,7 +63,7 @@ void ConfigureInput::handleClick() {
 | 
			
		||||
    grabKeyboard();
 | 
			
		||||
    grabMouse();
 | 
			
		||||
    changing_button = sender;
 | 
			
		||||
    timer->start(5000); //Cancel after 5 seconds
 | 
			
		||||
    timer->start(5000); // Cancel after 5 seconds
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ConfigureInput::applyConfiguration() {
 | 
			
		||||
 | 
			
		||||
@ -4,9 +4,9 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <QWidget>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <QKeyEvent>
 | 
			
		||||
 | 
			
		||||
#include <QWidget>
 | 
			
		||||
#include "citra_qt/config.h"
 | 
			
		||||
#include "core/settings.h"
 | 
			
		||||
#include "ui_configure_input.h"
 | 
			
		||||
@ -16,7 +16,7 @@ class QString;
 | 
			
		||||
class QTimer;
 | 
			
		||||
 | 
			
		||||
namespace Ui {
 | 
			
		||||
    class ConfigureInput;
 | 
			
		||||
class ConfigureInput;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class ConfigureInput : public QWidget {
 | 
			
		||||
@ -39,7 +39,8 @@ private:
 | 
			
		||||
    /// Load configuration settings into button text
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
    /// Handle key press event for input tab when a button is 'waiting'.
 | 
			
		||||
 | 
			
		||||
@ -4,27 +4,24 @@
 | 
			
		||||
 | 
			
		||||
#include "citra_qt/configure_system.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 "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 = {{
 | 
			
		||||
    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) :
 | 
			
		||||
    QWidget(parent),
 | 
			
		||||
    ui(new Ui::ConfigureSystem) {
 | 
			
		||||
ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureSystem) {
 | 
			
		||||
    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();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ConfigureSystem::~ConfigureSystem() {
 | 
			
		||||
}
 | 
			
		||||
ConfigureSystem::~ConfigureSystem() {}
 | 
			
		||||
 | 
			
		||||
void ConfigureSystem::setConfiguration() {
 | 
			
		||||
    enabled = !System::IsPoweredOn();
 | 
			
		||||
@ -54,13 +51,17 @@ void ConfigureSystem::setConfiguration() {
 | 
			
		||||
void ConfigureSystem::ReadSystemSettings() {
 | 
			
		||||
    // set username
 | 
			
		||||
    username = Service::CFG::GetUsername();
 | 
			
		||||
    // ui->edit_username->setText(QString::fromStdU16String(username)); // TODO(wwylele): Use this when we move to Qt 5.5
 | 
			
		||||
    ui->edit_username->setText(QString::fromUtf16(reinterpret_cast<const ushort*>(username.data())));
 | 
			
		||||
    // TODO(wwylele): Use this when we move to Qt 5.5
 | 
			
		||||
    // ui->edit_username->setText(QString::fromStdU16String(username));
 | 
			
		||||
    ui->edit_username->setText(
 | 
			
		||||
        QString::fromUtf16(reinterpret_cast<const ushort*>(username.data())));
 | 
			
		||||
 | 
			
		||||
    // set birthday
 | 
			
		||||
    std::tie(birthmonth, birthday) = Service::CFG::GetBirthday();
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
    // set system language
 | 
			
		||||
@ -79,8 +80,10 @@ void ConfigureSystem::applyConfiguration() {
 | 
			
		||||
    bool modified = false;
 | 
			
		||||
 | 
			
		||||
    // apply username
 | 
			
		||||
    // std::u16string new_username = ui->edit_username->text().toStdU16String(); // 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()));
 | 
			
		||||
    // TODO(wwylele): Use this when we move to Qt 5.5
 | 
			
		||||
    // 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) {
 | 
			
		||||
        Service::CFG::SetUsername(new_username);
 | 
			
		||||
        modified = true;
 | 
			
		||||
 | 
			
		||||
@ -11,12 +11,11 @@ namespace Ui {
 | 
			
		||||
class ConfigureSystem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class ConfigureSystem : public QWidget
 | 
			
		||||
{
 | 
			
		||||
class ConfigureSystem : public QWidget {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    explicit ConfigureSystem(QWidget *parent = nullptr);
 | 
			
		||||
    explicit ConfigureSystem(QWidget* parent = nullptr);
 | 
			
		||||
    ~ConfigureSystem();
 | 
			
		||||
 | 
			
		||||
    void applyConfiguration();
 | 
			
		||||
 | 
			
		||||
@ -3,19 +3,15 @@
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include <QStandardItemModel>
 | 
			
		||||
 | 
			
		||||
#include "citra_qt/debugger/callstack.h"
 | 
			
		||||
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
#include "common/symbols.h"
 | 
			
		||||
 | 
			
		||||
#include "core/core.h"
 | 
			
		||||
#include "core/memory.h"
 | 
			
		||||
#include "core/arm/arm_interface.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);
 | 
			
		||||
 | 
			
		||||
    callstack_model = new QStandardItemModel(this);
 | 
			
		||||
@ -27,29 +23,26 @@ CallstackWidget::CallstackWidget(QWidget* parent): QDockWidget(parent)
 | 
			
		||||
    ui.treeView->setModel(callstack_model);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CallstackWidget::OnDebugModeEntered()
 | 
			
		||||
{
 | 
			
		||||
void CallstackWidget::OnDebugModeEntered() {
 | 
			
		||||
    // Stack pointer
 | 
			
		||||
    const u32 sp = Core::g_app_core->GetReg(13);
 | 
			
		||||
 | 
			
		||||
    Clear();
 | 
			
		||||
 | 
			
		||||
    int counter = 0;
 | 
			
		||||
    for (u32 addr = 0x10000000; addr >= sp; addr -= 4)
 | 
			
		||||
    {
 | 
			
		||||
    for (u32 addr = 0x10000000; addr >= sp; addr -= 4) {
 | 
			
		||||
        if (!Memory::IsValidVirtualAddress(addr))
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        const u32 ret_addr = Memory::Read32(addr);
 | 
			
		||||
        const u32 call_addr = ret_addr - 4; //get call address???
 | 
			
		||||
        const u32 call_addr = ret_addr - 4; // get call address???
 | 
			
		||||
 | 
			
		||||
        if (!Memory::IsValidVirtualAddress(call_addr))
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        /* TODO (mattvail) clean me, move to debugger interface */
 | 
			
		||||
        u32 insn = Memory::Read32(call_addr);
 | 
			
		||||
        if (ARM_Disasm::Decode(insn) == OP_BL)
 | 
			
		||||
        {
 | 
			
		||||
        if (ARM_Disasm::Decode(insn) == OP_BL) {
 | 
			
		||||
            std::string name;
 | 
			
		||||
            // ripped from disasm
 | 
			
		||||
            u8 cond = (insn >> 28) & 0xf;
 | 
			
		||||
@ -63,26 +56,28 @@ void CallstackWidget::OnDebugModeEntered()
 | 
			
		||||
            i_offset += 8;
 | 
			
		||||
            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(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'))));
 | 
			
		||||
            callstack_model->setItem(
 | 
			
		||||
                counter, 0, new QStandardItem(QString("0x%1").arg(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";
 | 
			
		||||
            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')))));
 | 
			
		||||
            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')))));
 | 
			
		||||
 | 
			
		||||
            counter++;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CallstackWidget::OnDebugModeLeft()
 | 
			
		||||
{
 | 
			
		||||
void CallstackWidget::OnDebugModeLeft() {}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CallstackWidget::Clear()
 | 
			
		||||
{
 | 
			
		||||
void CallstackWidget::Clear() {
 | 
			
		||||
    for (int row = 0; row < callstack_model->rowCount(); row++) {
 | 
			
		||||
        for (int column = 0; column < callstack_model->columnCount(); column++) {
 | 
			
		||||
            callstack_model->setItem(row, column, new QStandardItem());
 | 
			
		||||
 | 
			
		||||
@ -7,8 +7,7 @@
 | 
			
		||||
 | 
			
		||||
class QStandardItemModel;
 | 
			
		||||
 | 
			
		||||
class CallstackWidget : public QDockWidget
 | 
			
		||||
{
 | 
			
		||||
class CallstackWidget : public QDockWidget {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
 | 
			
		||||
@ -3,23 +3,20 @@
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include <QShortcut>
 | 
			
		||||
 | 
			
		||||
#include "citra_qt/bootmanager.h"
 | 
			
		||||
#include "citra_qt/hotkeys.h"
 | 
			
		||||
#include "citra_qt/debugger/disassembler.h"
 | 
			
		||||
#include "citra_qt/hotkeys.h"
 | 
			
		||||
#include "citra_qt/util/util.h"
 | 
			
		||||
 | 
			
		||||
#include "common/break_points.h"
 | 
			
		||||
#include "common/symbols.h"
 | 
			
		||||
 | 
			
		||||
#include "core/core.h"
 | 
			
		||||
#include "core/memory.h"
 | 
			
		||||
#include "core/arm/arm_interface.h"
 | 
			
		||||
#include "core/arm/disassembler/arm_disasm.h"
 | 
			
		||||
#include "core/core.h"
 | 
			
		||||
#include "core/memory.h"
 | 
			
		||||
 | 
			
		||||
DisassemblerModel::DisassemblerModel(QObject* parent) :
 | 
			
		||||
    QAbstractListModel(parent), base_address(0), code_size(0), program_counter(0), selection(QModelIndex()) {
 | 
			
		||||
}
 | 
			
		||||
DisassemblerModel::DisassemblerModel(QObject* parent)
 | 
			
		||||
    : QAbstractListModel(parent), base_address(0), code_size(0), program_counter(0),
 | 
			
		||||
      selection(QModelIndex()) {}
 | 
			
		||||
 | 
			
		||||
int DisassemblerModel::columnCount(const QModelIndex& parent) const {
 | 
			
		||||
    return 3;
 | 
			
		||||
@ -31,62 +28,60 @@ int DisassemblerModel::rowCount(const QModelIndex& parent) const {
 | 
			
		||||
 | 
			
		||||
QVariant DisassemblerModel::data(const QModelIndex& index, int role) const {
 | 
			
		||||
    switch (role) {
 | 
			
		||||
        case Qt::DisplayRole:
 | 
			
		||||
        {
 | 
			
		||||
            u32 address = base_address + index.row() * 4;
 | 
			
		||||
            u32 instr = Memory::Read32(address);
 | 
			
		||||
            std::string disassembly = ARM_Disasm::Disassemble(address, instr);
 | 
			
		||||
    case Qt::DisplayRole: {
 | 
			
		||||
        u32 address = base_address + index.row() * 4;
 | 
			
		||||
        u32 instr = Memory::Read32(address);
 | 
			
		||||
        std::string disassembly = ARM_Disasm::Disassemble(address, instr);
 | 
			
		||||
 | 
			
		||||
            if (index.column() == 0) {
 | 
			
		||||
                return QString("0x%1").arg((uint)(address), 8, 16, QLatin1Char('0'));
 | 
			
		||||
            } else if (index.column() == 1) {
 | 
			
		||||
                return QString::fromStdString(disassembly);
 | 
			
		||||
            } else if (index.column() == 2) {
 | 
			
		||||
                if(Symbols::HasSymbol(address)) {
 | 
			
		||||
                    TSymbol symbol = Symbols::GetSymbol(address);
 | 
			
		||||
                    return QString("%1 - Size:%2").arg(QString::fromStdString(symbol.name))
 | 
			
		||||
                                                  .arg(symbol.size / 4); // divide by 4 to get instruction count
 | 
			
		||||
                } else if (ARM_Disasm::Decode(instr) == OP_BL) {
 | 
			
		||||
                    u32 offset = instr & 0xFFFFFF;
 | 
			
		||||
        if (index.column() == 0) {
 | 
			
		||||
            return QString("0x%1").arg((uint)(address), 8, 16, QLatin1Char('0'));
 | 
			
		||||
        } else if (index.column() == 1) {
 | 
			
		||||
            return QString::fromStdString(disassembly);
 | 
			
		||||
        } else if (index.column() == 2) {
 | 
			
		||||
            if (Symbols::HasSymbol(address)) {
 | 
			
		||||
                TSymbol symbol = Symbols::GetSymbol(address);
 | 
			
		||||
                return QString("%1 - Size:%2")
 | 
			
		||||
                    .arg(QString::fromStdString(symbol.name))
 | 
			
		||||
                    .arg(symbol.size / 4); // divide by 4 to get instruction count
 | 
			
		||||
            } else if (ARM_Disasm::Decode(instr) == OP_BL) {
 | 
			
		||||
                u32 offset = instr & 0xFFFFFF;
 | 
			
		||||
 | 
			
		||||
                    // Sign-extend the 24-bit offset
 | 
			
		||||
                    if ((offset >> 23) & 1)
 | 
			
		||||
                        offset |= 0xFF000000;
 | 
			
		||||
                // Sign-extend the 24-bit offset
 | 
			
		||||
                if ((offset >> 23) & 1)
 | 
			
		||||
                    offset |= 0xFF000000;
 | 
			
		||||
 | 
			
		||||
                    // Pre-compute the left-shift and the prefetch offset
 | 
			
		||||
                    offset <<= 2;
 | 
			
		||||
                    offset += 8;
 | 
			
		||||
                // Pre-compute the left-shift and the prefetch offset
 | 
			
		||||
                offset <<= 2;
 | 
			
		||||
                offset += 8;
 | 
			
		||||
 | 
			
		||||
                    TSymbol symbol = Symbols::GetSymbol(address + offset);
 | 
			
		||||
                    return QString("    --> %1").arg(QString::fromStdString(symbol.name));
 | 
			
		||||
                }
 | 
			
		||||
                TSymbol symbol = Symbols::GetSymbol(address + offset);
 | 
			
		||||
                return QString("    --> %1").arg(QString::fromStdString(symbol.name));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case Qt::BackgroundRole:
 | 
			
		||||
        {
 | 
			
		||||
            unsigned int address = base_address + 4 * index.row();
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
            if (breakpoints.IsAddressBreakPoint(address))
 | 
			
		||||
                return QBrush(QColor(0xFF, 0xC0, 0xC0));
 | 
			
		||||
            else if (address == program_counter)
 | 
			
		||||
                return QBrush(QColor(0xC0, 0xC0, 0xFF));
 | 
			
		||||
    case Qt::BackgroundRole: {
 | 
			
		||||
        unsigned int address = base_address + 4 * index.row();
 | 
			
		||||
 | 
			
		||||
            break;
 | 
			
		||||
        if (breakpoints.IsAddressBreakPoint(address))
 | 
			
		||||
            return QBrush(QColor(0xFF, 0xC0, 0xC0));
 | 
			
		||||
        else if (address == program_counter)
 | 
			
		||||
            return QBrush(QColor(0xC0, 0xC0, 0xFF));
 | 
			
		||||
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    case Qt::FontRole: {
 | 
			
		||||
        if (index.column() == 0 || index.column() == 1) { // 2 is the symbols column
 | 
			
		||||
            return GetMonospaceFont();
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        case Qt::FontRole:
 | 
			
		||||
        {
 | 
			
		||||
            if (index.column() == 0 || index.column() == 1) { // 2 is the symbols column
 | 
			
		||||
                return GetMonospaceFont();
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        default:
 | 
			
		||||
            break;
 | 
			
		||||
    default:
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return QVariant();
 | 
			
		||||
@ -103,7 +98,7 @@ const BreakPoints& DisassemblerModel::GetBreakPoints() const {
 | 
			
		||||
void DisassemblerModel::ParseFromAddress(unsigned int address) {
 | 
			
		||||
 | 
			
		||||
    // 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 (code_size == 0)
 | 
			
		||||
@ -165,23 +160,26 @@ void DisassemblerModel::SetNextInstruction(unsigned int address) {
 | 
			
		||||
    emit dataChanged(prev_index, prev_index);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DisassemblerWidget::DisassemblerWidget(QWidget* parent, EmuThread* emu_thread) :
 | 
			
		||||
    QDockWidget(parent), base_addr(0), emu_thread(emu_thread) {
 | 
			
		||||
DisassemblerWidget::DisassemblerWidget(QWidget* parent, EmuThread* emu_thread)
 | 
			
		||||
    : QDockWidget(parent), base_addr(0), emu_thread(emu_thread) {
 | 
			
		||||
 | 
			
		||||
    disasm_ui.setupUi(this);
 | 
			
		||||
 | 
			
		||||
    RegisterHotkey("Disassembler", "Start/Stop", QKeySequence(Qt::Key_F5), Qt::ApplicationShortcut);
 | 
			
		||||
    RegisterHotkey("Disassembler", "Step", QKeySequence(Qt::Key_F10), 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_pause, SIGNAL(clicked()), this, SLOT(OnPause()));
 | 
			
		||||
    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 into", this), SIGNAL(activated()), this, SLOT(OnStepInto()));
 | 
			
		||||
    connect(GetHotkey("Disassembler", "Step into", this), SIGNAL(activated()), this,
 | 
			
		||||
            SLOT(OnStepInto()));
 | 
			
		||||
 | 
			
		||||
    setEnabled(false);
 | 
			
		||||
}
 | 
			
		||||
@ -195,7 +193,8 @@ void DisassemblerWidget::Init() {
 | 
			
		||||
 | 
			
		||||
    QModelIndex model_index = model->IndexFromAbsoluteAddress(Core::g_app_core->GetPC());
 | 
			
		||||
    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() {
 | 
			
		||||
@ -234,11 +233,11 @@ void DisassemblerWidget::OnDebugModeEntered() {
 | 
			
		||||
 | 
			
		||||
    QModelIndex model_index = model->IndexFromAbsoluteAddress(next_instr);
 | 
			
		||||
    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() {
 | 
			
		||||
    QModelIndex index = disasm_ui.treeView->selectionModel()->currentIndex();
 | 
			
		||||
@ -254,10 +253,12 @@ void DisassemblerWidget::OnEmulationStarting(EmuThread* emu_thread) {
 | 
			
		||||
    model = new DisassemblerModel(this);
 | 
			
		||||
    disasm_ui.treeView->setModel(model);
 | 
			
		||||
 | 
			
		||||
    connect(disasm_ui.treeView->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)),
 | 
			
		||||
        model, SLOT(OnSelectionChanged(const QModelIndex&)));
 | 
			
		||||
    connect(disasm_ui.treeView->selectionModel(),
 | 
			
		||||
            SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), model,
 | 
			
		||||
            SLOT(OnSelectionChanged(const QModelIndex&)));
 | 
			
		||||
    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();
 | 
			
		||||
    setEnabled(true);
 | 
			
		||||
 | 
			
		||||
@ -6,17 +6,14 @@
 | 
			
		||||
 | 
			
		||||
#include <QAbstractListModel>
 | 
			
		||||
#include <QDockWidget>
 | 
			
		||||
 | 
			
		||||
#include "ui_disassembler.h"
 | 
			
		||||
 | 
			
		||||
#include "common/break_points.h"
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
#include "ui_disassembler.h"
 | 
			
		||||
 | 
			
		||||
class QAction;
 | 
			
		||||
class EmuThread;
 | 
			
		||||
 | 
			
		||||
class DisassemblerModel : public QAbstractListModel
 | 
			
		||||
{
 | 
			
		||||
class DisassemblerModel : public QAbstractListModel {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
@ -46,8 +43,7 @@ private:
 | 
			
		||||
    mutable BreakPoints breakpoints;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class DisassemblerWidget : public QDockWidget
 | 
			
		||||
{
 | 
			
		||||
class DisassemblerWidget : public QDockWidget {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
 | 
			
		||||
@ -3,75 +3,67 @@
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include <QListView>
 | 
			
		||||
 | 
			
		||||
#include "citra_qt/debugger/graphics.h"
 | 
			
		||||
#include "citra_qt/util/util.h"
 | 
			
		||||
 | 
			
		||||
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)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int GPUCommandStreamItemModel::rowCount(const QModelIndex& parent) const
 | 
			
		||||
{
 | 
			
		||||
int GPUCommandStreamItemModel::rowCount(const QModelIndex& parent) const {
 | 
			
		||||
    return command_count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QVariant GPUCommandStreamItemModel::data(const QModelIndex& index, int role) const
 | 
			
		||||
{
 | 
			
		||||
QVariant GPUCommandStreamItemModel::data(const QModelIndex& index, int role) const {
 | 
			
		||||
    if (!index.isValid())
 | 
			
		||||
        return QVariant();
 | 
			
		||||
 | 
			
		||||
    int command_index = index.row();
 | 
			
		||||
    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 = {
 | 
			
		||||
            { GSP_GPU::CommandId::REQUEST_DMA, "REQUEST_DMA" },
 | 
			
		||||
            { GSP_GPU::CommandId::SUBMIT_GPU_CMDLIST, "SUBMIT_GPU_CMDLIST" },
 | 
			
		||||
            { GSP_GPU::CommandId::SET_MEMORY_FILL, "SET_MEMORY_FILL" },
 | 
			
		||||
            { GSP_GPU::CommandId::SET_DISPLAY_TRANSFER, "SET_DISPLAY_TRANSFER" },
 | 
			
		||||
            { GSP_GPU::CommandId::SET_TEXTURE_COPY, "SET_TEXTURE_COPY" },
 | 
			
		||||
            { GSP_GPU::CommandId::CACHE_FLUSH, "CACHE_FLUSH" },
 | 
			
		||||
            {GSP_GPU::CommandId::REQUEST_DMA, "REQUEST_DMA"},
 | 
			
		||||
            {GSP_GPU::CommandId::SUBMIT_GPU_CMDLIST, "SUBMIT_GPU_CMDLIST"},
 | 
			
		||||
            {GSP_GPU::CommandId::SET_MEMORY_FILL, "SET_MEMORY_FILL"},
 | 
			
		||||
            {GSP_GPU::CommandId::SET_DISPLAY_TRANSFER, "SET_DISPLAY_TRANSFER"},
 | 
			
		||||
            {GSP_GPU::CommandId::SET_TEXTURE_COPY, "SET_TEXTURE_COPY"},
 | 
			
		||||
            {GSP_GPU::CommandId::CACHE_FLUSH, "CACHE_FLUSH"},
 | 
			
		||||
        };
 | 
			
		||||
        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])
 | 
			
		||||
                        .arg(command_data[0], 8, 16, QLatin1Char('0'))
 | 
			
		||||
                        .arg(command_data[1], 8, 16, QLatin1Char('0'))
 | 
			
		||||
                        .arg(command_data[2], 8, 16, QLatin1Char('0'))
 | 
			
		||||
                        .arg(command_data[3], 8, 16, QLatin1Char('0'))
 | 
			
		||||
                        .arg(command_data[4], 8, 16, QLatin1Char('0'))
 | 
			
		||||
                        .arg(command_data[5], 8, 16, QLatin1Char('0'))
 | 
			
		||||
                        .arg(command_data[6], 8, 16, QLatin1Char('0'))
 | 
			
		||||
                        .arg(command_data[7], 8, 16, QLatin1Char('0'));
 | 
			
		||||
        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[1], 8, 16, QLatin1Char('0'))
 | 
			
		||||
                          .arg(command_data[2], 8, 16, QLatin1Char('0'))
 | 
			
		||||
                          .arg(command_data[3], 8, 16, QLatin1Char('0'))
 | 
			
		||||
                          .arg(command_data[4], 8, 16, QLatin1Char('0'))
 | 
			
		||||
                          .arg(command_data[5], 8, 16, QLatin1Char('0'))
 | 
			
		||||
                          .arg(command_data[6], 8, 16, QLatin1Char('0'))
 | 
			
		||||
                          .arg(command_data[7], 8, 16, QLatin1Char('0'));
 | 
			
		||||
        return QVariant(str);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
    } else {
 | 
			
		||||
        return QVariant();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GPUCommandStreamItemModel::GXCommandProcessed(int total_command_count)
 | 
			
		||||
{
 | 
			
		||||
void GPUCommandStreamItemModel::GXCommandProcessed(int 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)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    int prev_command_count = 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) : QDockWidget(tr("Graphics Debugger"), parent)
 | 
			
		||||
{
 | 
			
		||||
GPUCommandStreamWidget::GPUCommandStreamWidget(QWidget* parent)
 | 
			
		||||
    : QDockWidget(tr("Graphics Debugger"), parent) {
 | 
			
		||||
    setObjectName("GraphicsDebugger");
 | 
			
		||||
 | 
			
		||||
    GPUCommandStreamItemModel* command_model = new GPUCommandStreamItemModel(this);
 | 
			
		||||
 | 
			
		||||
@ -6,11 +6,10 @@
 | 
			
		||||
 | 
			
		||||
#include <QAbstractListModel>
 | 
			
		||||
#include <QDockWidget>
 | 
			
		||||
 | 
			
		||||
#include "video_core/gpu_debugger.h"
 | 
			
		||||
 | 
			
		||||
class GPUCommandStreamItemModel : public QAbstractListModel, public GraphicsDebugger::DebuggerObserver
 | 
			
		||||
{
 | 
			
		||||
class GPUCommandStreamItemModel : public QAbstractListModel,
 | 
			
		||||
                                  public GraphicsDebugger::DebuggerObserver {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
@ -32,8 +31,7 @@ private:
 | 
			
		||||
    int command_count;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class GPUCommandStreamWidget : public QDockWidget
 | 
			
		||||
{
 | 
			
		||||
class GPUCommandStreamWidget : public QDockWidget {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
 | 
			
		||||
@ -3,30 +3,25 @@
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include <QMetaType>
 | 
			
		||||
 | 
			
		||||
#include "citra_qt/debugger/graphics_breakpoint_observer.h"
 | 
			
		||||
 | 
			
		||||
BreakPointObserverDock::BreakPointObserverDock(std::shared_ptr<Pica::DebugContext> debug_context,
 | 
			
		||||
                                               const QString& title, QWidget* parent)
 | 
			
		||||
    : QDockWidget(title, parent), BreakPointObserver(debug_context)
 | 
			
		||||
{
 | 
			
		||||
    : QDockWidget(title, parent), BreakPointObserver(debug_context) {
 | 
			
		||||
    qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event");
 | 
			
		||||
 | 
			
		||||
    connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed()));
 | 
			
		||||
 | 
			
		||||
    // NOTE: This signal is emitted from a non-GUI thread, but connect() takes
 | 
			
		||||
    //       care of delaying its handling to the GUI thread.
 | 
			
		||||
    connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)),
 | 
			
		||||
            this, SLOT(OnBreakPointHit(Pica::DebugContext::Event,void*)),
 | 
			
		||||
            Qt::BlockingQueuedConnection);
 | 
			
		||||
    connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event, void*)), this,
 | 
			
		||||
            SLOT(OnBreakPointHit(Pica::DebugContext::Event, void*)), Qt::BlockingQueuedConnection);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BreakPointObserverDock::OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data)
 | 
			
		||||
{
 | 
			
		||||
void BreakPointObserverDock::OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) {
 | 
			
		||||
    emit BreakPointHit(event, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BreakPointObserverDock::OnPicaResume()
 | 
			
		||||
{
 | 
			
		||||
void BreakPointObserverDock::OnPicaResume() {
 | 
			
		||||
    emit Resumed();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,6 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <QDockWidget>
 | 
			
		||||
 | 
			
		||||
#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
 | 
			
		||||
 * 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
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
 | 
			
		||||
@ -7,47 +7,39 @@
 | 
			
		||||
#include <QPushButton>
 | 
			
		||||
#include <QTreeView>
 | 
			
		||||
#include <QVBoxLayout>
 | 
			
		||||
 | 
			
		||||
#include "citra_qt/debugger/graphics_breakpoints.h"
 | 
			
		||||
#include "citra_qt/debugger/graphics_breakpoints_p.h"
 | 
			
		||||
 | 
			
		||||
#include "common/assert.h"
 | 
			
		||||
 | 
			
		||||
BreakPointModel::BreakPointModel(std::shared_ptr<Pica::DebugContext> debug_context, QObject* parent)
 | 
			
		||||
    : QAbstractListModel(parent), context_weak(debug_context),
 | 
			
		||||
      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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int BreakPointModel::rowCount(const QModelIndex& parent) const
 | 
			
		||||
{
 | 
			
		||||
int BreakPointModel::rowCount(const QModelIndex& parent) const {
 | 
			
		||||
    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());
 | 
			
		||||
 | 
			
		||||
    switch (role) {
 | 
			
		||||
    case Qt::DisplayRole:
 | 
			
		||||
    {
 | 
			
		||||
    case Qt::DisplayRole: {
 | 
			
		||||
        if (index.column() == 0) {
 | 
			
		||||
            static const std::map<Pica::DebugContext::Event, QString> map = {
 | 
			
		||||
                { Pica::DebugContext::Event::PicaCommandLoaded, tr("Pica command loaded") },
 | 
			
		||||
                { Pica::DebugContext::Event::PicaCommandProcessed, tr("Pica command processed") },
 | 
			
		||||
                { Pica::DebugContext::Event::IncomingPrimitiveBatch, tr("Incoming primitive batch") },
 | 
			
		||||
                { Pica::DebugContext::Event::FinishedPrimitiveBatch, tr("Finished primitive batch") },
 | 
			
		||||
                { Pica::DebugContext::Event::VertexShaderInvocation, tr("Vertex shader invocation") },
 | 
			
		||||
                { Pica::DebugContext::Event::IncomingDisplayTransfer, tr("Incoming display transfer") },
 | 
			
		||||
                { Pica::DebugContext::Event::GSPCommandProcessed, tr("GSP command processed") },
 | 
			
		||||
                { Pica::DebugContext::Event::BufferSwapped, tr("Buffers swapped") }
 | 
			
		||||
                {Pica::DebugContext::Event::PicaCommandLoaded, tr("Pica command loaded")},
 | 
			
		||||
                {Pica::DebugContext::Event::PicaCommandProcessed, tr("Pica command processed")},
 | 
			
		||||
                {Pica::DebugContext::Event::IncomingPrimitiveBatch, tr("Incoming primitive batch")},
 | 
			
		||||
                {Pica::DebugContext::Event::FinishedPrimitiveBatch, tr("Finished primitive batch")},
 | 
			
		||||
                {Pica::DebugContext::Event::VertexShaderInvocation, tr("Vertex shader invocation")},
 | 
			
		||||
                {Pica::DebugContext::Event::IncomingDisplayTransfer,
 | 
			
		||||
                 tr("Incoming display transfer")},
 | 
			
		||||
                {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));
 | 
			
		||||
@ -57,23 +49,20 @@ QVariant BreakPointModel::data(const QModelIndex& index, int role) const
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    case Qt::CheckStateRole:
 | 
			
		||||
    {
 | 
			
		||||
    case Qt::CheckStateRole: {
 | 
			
		||||
        if (index.column() == 0)
 | 
			
		||||
            return data(index, Role_IsEnabled).toBool() ? Qt::Checked : Qt::Unchecked;
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    case Qt::BackgroundRole:
 | 
			
		||||
    {
 | 
			
		||||
    case Qt::BackgroundRole: {
 | 
			
		||||
        if (at_breakpoint && index.row() == static_cast<int>(active_breakpoint)) {
 | 
			
		||||
            return QBrush(QColor(0xE0, 0xE0, 0x10));
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    case Role_IsEnabled:
 | 
			
		||||
    {
 | 
			
		||||
    case Role_IsEnabled: {
 | 
			
		||||
        auto context = context_weak.lock();
 | 
			
		||||
        return context && context->breakpoints[(int)event].enabled;
 | 
			
		||||
    }
 | 
			
		||||
@ -84,8 +73,7 @@ QVariant BreakPointModel::data(const QModelIndex& index, int role) const
 | 
			
		||||
    return QVariant();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Qt::ItemFlags BreakPointModel::flags(const QModelIndex &index) const
 | 
			
		||||
{
 | 
			
		||||
Qt::ItemFlags BreakPointModel::flags(const QModelIndex& index) const {
 | 
			
		||||
    if (!index.isValid())
 | 
			
		||||
        return 0;
 | 
			
		||||
 | 
			
		||||
@ -95,14 +83,11 @@ Qt::ItemFlags BreakPointModel::flags(const QModelIndex &index) const
 | 
			
		||||
    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());
 | 
			
		||||
 | 
			
		||||
    switch (role) {
 | 
			
		||||
    case Qt::CheckStateRole:
 | 
			
		||||
    {
 | 
			
		||||
    case Qt::CheckStateRole: {
 | 
			
		||||
        if (index.column() != 0)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
@ -120,9 +105,7 @@ bool BreakPointModel::setData(const QModelIndex& index, const QVariant& value, i
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void BreakPointModel::OnBreakPointHit(Pica::DebugContext::Event event)
 | 
			
		||||
{
 | 
			
		||||
void BreakPointModel::OnBreakPointHit(Pica::DebugContext::Event event) {
 | 
			
		||||
    auto context = context_weak.lock();
 | 
			
		||||
    if (!context)
 | 
			
		||||
        return;
 | 
			
		||||
@ -133,8 +116,7 @@ void BreakPointModel::OnBreakPointHit(Pica::DebugContext::Event event)
 | 
			
		||||
                     createIndex(static_cast<int>(event), 0));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BreakPointModel::OnResumed()
 | 
			
		||||
{
 | 
			
		||||
void BreakPointModel::OnResumed() {
 | 
			
		||||
    auto context = context_weak.lock();
 | 
			
		||||
    if (!context)
 | 
			
		||||
        return;
 | 
			
		||||
@ -145,12 +127,10 @@ void BreakPointModel::OnResumed()
 | 
			
		||||
    active_breakpoint = context->active_breakpoint;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(std::shared_ptr<Pica::DebugContext> debug_context,
 | 
			
		||||
                                                     QWidget* parent)
 | 
			
		||||
GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(
 | 
			
		||||
    std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent)
 | 
			
		||||
    : QDockWidget(tr("Pica Breakpoints"), parent),
 | 
			
		||||
      Pica::DebugContext::BreakPointObserver(debug_context)
 | 
			
		||||
{
 | 
			
		||||
      Pica::DebugContext::BreakPointObserver(debug_context) {
 | 
			
		||||
    setObjectName("PicaBreakPointsWidget");
 | 
			
		||||
 | 
			
		||||
    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");
 | 
			
		||||
 | 
			
		||||
    connect(breakpoint_list, SIGNAL(doubleClicked(const QModelIndex&)),
 | 
			
		||||
            this, SLOT(OnItemDoubleClicked(const QModelIndex&)));
 | 
			
		||||
    connect(breakpoint_list, SIGNAL(doubleClicked(const QModelIndex&)), this,
 | 
			
		||||
            SLOT(OnItemDoubleClicked(const QModelIndex&)));
 | 
			
		||||
 | 
			
		||||
    connect(resume_button, SIGNAL(clicked()), this, SLOT(OnResumeRequested()));
 | 
			
		||||
 | 
			
		||||
    connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)),
 | 
			
		||||
            this, SLOT(OnBreakPointHit(Pica::DebugContext::Event,void*)),
 | 
			
		||||
            Qt::BlockingQueuedConnection);
 | 
			
		||||
    connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event, void*)), this,
 | 
			
		||||
            SLOT(OnBreakPointHit(Pica::DebugContext::Event, void*)), Qt::BlockingQueuedConnection);
 | 
			
		||||
    connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed()));
 | 
			
		||||
 | 
			
		||||
    connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)),
 | 
			
		||||
            breakpoint_model, SLOT(OnBreakPointHit(Pica::DebugContext::Event)),
 | 
			
		||||
            Qt::BlockingQueuedConnection);
 | 
			
		||||
    connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event, void*)), breakpoint_model,
 | 
			
		||||
            SLOT(OnBreakPointHit(Pica::DebugContext::Event)), Qt::BlockingQueuedConnection);
 | 
			
		||||
    connect(this, SIGNAL(Resumed()), breakpoint_model, SLOT(OnResumed()));
 | 
			
		||||
 | 
			
		||||
    connect(this, SIGNAL(BreakPointsChanged(const QModelIndex&,const QModelIndex&)),
 | 
			
		||||
            breakpoint_model, SIGNAL(dataChanged(const QModelIndex&,const QModelIndex&)));
 | 
			
		||||
    connect(this, SIGNAL(BreakPointsChanged(const QModelIndex&, const QModelIndex&)),
 | 
			
		||||
            breakpoint_model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)));
 | 
			
		||||
 | 
			
		||||
    QWidget* main_widget = new QWidget;
 | 
			
		||||
    auto main_layout = new QVBoxLayout;
 | 
			
		||||
@ -197,38 +175,32 @@ GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(std::shared_ptr<Pica::Debug
 | 
			
		||||
    setWidget(main_widget);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GraphicsBreakPointsWidget::OnPicaBreakPointHit(Event event, void* data)
 | 
			
		||||
{
 | 
			
		||||
void GraphicsBreakPointsWidget::OnPicaBreakPointHit(Event event, void* data) {
 | 
			
		||||
    // Process in GUI thread
 | 
			
		||||
    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"));
 | 
			
		||||
    resume_button->setEnabled(true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GraphicsBreakPointsWidget::OnPicaResume()
 | 
			
		||||
{
 | 
			
		||||
void GraphicsBreakPointsWidget::OnPicaResume() {
 | 
			
		||||
    // Process in GUI thread
 | 
			
		||||
    emit Resumed();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GraphicsBreakPointsWidget::OnResumed()
 | 
			
		||||
{
 | 
			
		||||
void GraphicsBreakPointsWidget::OnResumed() {
 | 
			
		||||
    status_text->setText(tr("Emulation running"));
 | 
			
		||||
    resume_button->setEnabled(false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GraphicsBreakPointsWidget::OnResumeRequested()
 | 
			
		||||
{
 | 
			
		||||
void GraphicsBreakPointsWidget::OnResumeRequested() {
 | 
			
		||||
    if (auto context = context_weak.lock())
 | 
			
		||||
        context->Resume();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GraphicsBreakPointsWidget::OnItemDoubleClicked(const QModelIndex& index)
 | 
			
		||||
{
 | 
			
		||||
void GraphicsBreakPointsWidget::OnItemDoubleClicked(const QModelIndex& index) {
 | 
			
		||||
    if (!index.isValid())
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -5,9 +5,7 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
 | 
			
		||||
#include <QDockWidget>
 | 
			
		||||
 | 
			
		||||
#include "video_core/debug_utils/debug_utils.h"
 | 
			
		||||
 | 
			
		||||
class QLabel;
 | 
			
		||||
 | 
			
		||||
@ -5,9 +5,7 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
 | 
			
		||||
#include <QAbstractListModel>
 | 
			
		||||
 | 
			
		||||
#include "video_core/debug_utils/debug_utils.h"
 | 
			
		||||
 | 
			
		||||
class BreakPointModel : public QAbstractListModel {
 | 
			
		||||
@ -23,7 +21,7 @@ public:
 | 
			
		||||
    int columnCount(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;
 | 
			
		||||
    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;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -13,16 +13,13 @@
 | 
			
		||||
#include <QSpinBox>
 | 
			
		||||
#include <QTreeView>
 | 
			
		||||
#include <QVBoxLayout>
 | 
			
		||||
 | 
			
		||||
#include "citra_qt/debugger/graphics_cmdlists.h"
 | 
			
		||||
#include "citra_qt/util/spinbox.h"
 | 
			
		||||
#include "citra_qt/util/util.h"
 | 
			
		||||
 | 
			
		||||
#include "common/vector_math.h"
 | 
			
		||||
 | 
			
		||||
#include "video_core/debug_utils/debug_utils.h"
 | 
			
		||||
#include "video_core/pica.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 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 {
 | 
			
		||||
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;
 | 
			
		||||
        QPixmap image_pixmap = QPixmap::fromImage(LoadTexture(src, info));
 | 
			
		||||
        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 {
 | 
			
		||||
    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) {
 | 
			
		||||
        QString content;
 | 
			
		||||
        switch ( index.column() ) {
 | 
			
		||||
        switch (index.column()) {
 | 
			
		||||
        case 0:
 | 
			
		||||
            return QString::fromLatin1(Pica::Regs::GetCommandName(write.cmd_id).c_str());
 | 
			
		||||
        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 {
 | 
			
		||||
    switch(role) {
 | 
			
		||||
    case Qt::DisplayRole:
 | 
			
		||||
    {
 | 
			
		||||
    switch (role) {
 | 
			
		||||
    case Qt::DisplayRole: {
 | 
			
		||||
        switch (section) {
 | 
			
		||||
        case 0:
 | 
			
		||||
            return tr("Command Name");
 | 
			
		||||
@ -117,14 +112,14 @@ void GPUCommandListModel::OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace&
 | 
			
		||||
    endResetModel();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define COMMAND_IN_RANGE(cmd_id, reg_name)   \
 | 
			
		||||
    (cmd_id >= PICA_REG_INDEX(reg_name) &&   \
 | 
			
		||||
#define COMMAND_IN_RANGE(cmd_id, reg_name)                                                         \
 | 
			
		||||
    (cmd_id >= PICA_REG_INDEX(reg_name) &&                                                         \
 | 
			
		||||
     cmd_id < PICA_REG_INDEX(reg_name) + sizeof(decltype(Pica::g_state.regs.reg_name)) / 4)
 | 
			
		||||
 | 
			
		||||
void GPUCommandListWidget::OnCommandDoubleClicked(const QModelIndex& index) {
 | 
			
		||||
    const unsigned int command_id = list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toUInt();
 | 
			
		||||
    if (COMMAND_IN_RANGE(command_id, texture0) ||
 | 
			
		||||
        COMMAND_IN_RANGE(command_id, texture1) ||
 | 
			
		||||
    const unsigned int command_id =
 | 
			
		||||
        list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toUInt();
 | 
			
		||||
    if (COMMAND_IN_RANGE(command_id, texture0) || COMMAND_IN_RANGE(command_id, texture1) ||
 | 
			
		||||
        COMMAND_IN_RANGE(command_id, texture2)) {
 | 
			
		||||
 | 
			
		||||
        unsigned index;
 | 
			
		||||
@ -148,9 +143,9 @@ void GPUCommandListWidget::OnCommandDoubleClicked(const QModelIndex& index) {
 | 
			
		||||
void GPUCommandListWidget::SetCommandInfo(const QModelIndex& index) {
 | 
			
		||||
    QWidget* new_info_widget = nullptr;
 | 
			
		||||
 | 
			
		||||
    const unsigned int command_id = list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toUInt();
 | 
			
		||||
    if (COMMAND_IN_RANGE(command_id, texture0) ||
 | 
			
		||||
        COMMAND_IN_RANGE(command_id, texture1) ||
 | 
			
		||||
    const unsigned int command_id =
 | 
			
		||||
        list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toUInt();
 | 
			
		||||
    if (COMMAND_IN_RANGE(command_id, texture0) || COMMAND_IN_RANGE(command_id, texture1) ||
 | 
			
		||||
        COMMAND_IN_RANGE(command_id, texture2)) {
 | 
			
		||||
 | 
			
		||||
        unsigned index;
 | 
			
		||||
@ -179,7 +174,8 @@ void GPUCommandListWidget::SetCommandInfo(const QModelIndex& index) {
 | 
			
		||||
}
 | 
			
		||||
#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");
 | 
			
		||||
    GPUCommandListModel* model = new GPUCommandListModel(this);
 | 
			
		||||
 | 
			
		||||
@ -191,23 +187,24 @@ GPUCommandListWidget::GPUCommandListWidget(QWidget* parent) : QDockWidget(tr("Pi
 | 
			
		||||
    list_widget->setRootIsDecorated(false);
 | 
			
		||||
    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);
 | 
			
		||||
#else
 | 
			
		||||
    list_widget->header()->setResizeMode(QHeaderView::ResizeToContents);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    connect(list_widget->selectionModel(), SIGNAL(currentChanged(const QModelIndex&,const QModelIndex&)),
 | 
			
		||||
            this, SLOT(SetCommandInfo(const QModelIndex&)));
 | 
			
		||||
    connect(list_widget, SIGNAL(doubleClicked(const QModelIndex&)),
 | 
			
		||||
            this, SLOT(OnCommandDoubleClicked(const QModelIndex&)));
 | 
			
		||||
    connect(list_widget->selectionModel(),
 | 
			
		||||
            SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), this,
 | 
			
		||||
            SLOT(SetCommandInfo(const QModelIndex&)));
 | 
			
		||||
    connect(list_widget, SIGNAL(doubleClicked(const QModelIndex&)), this,
 | 
			
		||||
            SLOT(OnCommandDoubleClicked(const QModelIndex&)));
 | 
			
		||||
 | 
			
		||||
    toggle_tracing = new QPushButton(tr("Start Tracing"));
 | 
			
		||||
    QPushButton* copy_all = new QPushButton(tr("Copy All"));
 | 
			
		||||
 | 
			
		||||
    connect(toggle_tracing, SIGNAL(clicked()), this, SLOT(OnToggleTracing()));
 | 
			
		||||
    connect(this, SIGNAL(TracingFinished(const Pica::DebugUtils::PicaTrace&)),
 | 
			
		||||
            model, SLOT(OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace&)));
 | 
			
		||||
    connect(this, SIGNAL(TracingFinished(const Pica::DebugUtils::PicaTrace&)), model,
 | 
			
		||||
            SLOT(OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace&)));
 | 
			
		||||
 | 
			
		||||
    connect(copy_all, SIGNAL(clicked()), this, SLOT(CopyAllToClipboard()));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -6,15 +6,13 @@
 | 
			
		||||
 | 
			
		||||
#include <QAbstractListModel>
 | 
			
		||||
#include <QDockWidget>
 | 
			
		||||
 | 
			
		||||
#include "video_core/gpu_debugger.h"
 | 
			
		||||
#include "video_core/debug_utils/debug_utils.h"
 | 
			
		||||
#include "video_core/gpu_debugger.h"
 | 
			
		||||
 | 
			
		||||
class QPushButton;
 | 
			
		||||
class QTreeView;
 | 
			
		||||
 | 
			
		||||
class GPUCommandListModel : public QAbstractListModel
 | 
			
		||||
{
 | 
			
		||||
class GPUCommandListModel : public QAbstractListModel {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
@ -27,7 +25,8 @@ public:
 | 
			
		||||
    int columnCount(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 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:
 | 
			
		||||
    void OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& trace);
 | 
			
		||||
@ -36,8 +35,7 @@ private:
 | 
			
		||||
    Pica::DebugUtils::PicaTrace pica_trace;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class GPUCommandListWidget : public QDockWidget
 | 
			
		||||
{
 | 
			
		||||
class GPUCommandListWidget : public QDockWidget {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
 | 
			
		||||
@ -11,24 +11,20 @@
 | 
			
		||||
#include <QPushButton>
 | 
			
		||||
#include <QScrollArea>
 | 
			
		||||
#include <QSpinBox>
 | 
			
		||||
 | 
			
		||||
#include "citra_qt/debugger/graphics_surface.h"
 | 
			
		||||
#include "citra_qt/util/spinbox.h"
 | 
			
		||||
 | 
			
		||||
#include "common/color.h"
 | 
			
		||||
 | 
			
		||||
#include "core/memory.h"
 | 
			
		||||
#include "core/hw/gpu.h"
 | 
			
		||||
 | 
			
		||||
#include "core/memory.h"
 | 
			
		||||
#include "video_core/pica.h"
 | 
			
		||||
#include "video_core/pica_state.h"
 | 
			
		||||
#include "video_core/utils.h"
 | 
			
		||||
 | 
			
		||||
SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_) : QLabel(parent), surface_widget(surface_widget_) {}
 | 
			
		||||
SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_)
 | 
			
		||||
    : QLabel(parent), surface_widget(surface_widget_) {}
 | 
			
		||||
SurfacePicture::~SurfacePicture() {}
 | 
			
		||||
 | 
			
		||||
void SurfacePicture::mousePressEvent(QMouseEvent* event)
 | 
			
		||||
{
 | 
			
		||||
void SurfacePicture::mousePressEvent(QMouseEvent* event) {
 | 
			
		||||
    // Only do something while the left mouse button is held down
 | 
			
		||||
    if (!(event->buttons() & Qt::LeftButton))
 | 
			
		||||
        return;
 | 
			
		||||
@ -41,18 +37,15 @@ void SurfacePicture::mousePressEvent(QMouseEvent* event)
 | 
			
		||||
                             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
 | 
			
		||||
    mousePressEvent(event);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext> debug_context,
 | 
			
		||||
                                                     QWidget* parent)
 | 
			
		||||
                                             QWidget* parent)
 | 
			
		||||
    : BreakPointObserverDock(debug_context, tr("Pica Surface Viewer"), parent),
 | 
			
		||||
      surface_source(Source::ColorBuffer)
 | 
			
		||||
{
 | 
			
		||||
      surface_source(Source::ColorBuffer) {
 | 
			
		||||
    setObjectName("PicaSurface");
 | 
			
		||||
 | 
			
		||||
    surface_source_list = new QComboBox;
 | 
			
		||||
@ -124,13 +117,20 @@ GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext>
 | 
			
		||||
 | 
			
		||||
    // Connections
 | 
			
		||||
    connect(this, SIGNAL(Update()), this, SLOT(OnUpdate()));
 | 
			
		||||
    connect(surface_source_list, SIGNAL(currentIndexChanged(int)), this, SLOT(OnSurfaceSourceChanged(int)));
 | 
			
		||||
    connect(surface_address_control, SIGNAL(ValueChanged(qint64)), this, SLOT(OnSurfaceAddressChanged(qint64)));
 | 
			
		||||
    connect(surface_width_control, SIGNAL(valueChanged(int)), this, SLOT(OnSurfaceWidthChanged(int)));
 | 
			
		||||
    connect(surface_height_control, SIGNAL(valueChanged(int)), this, SLOT(OnSurfaceHeightChanged(int)));
 | 
			
		||||
    connect(surface_format_control, SIGNAL(currentIndexChanged(int)), this, SLOT(OnSurfaceFormatChanged(int)));
 | 
			
		||||
    connect(surface_picker_x_control, SIGNAL(valueChanged(int)), this, SLOT(OnSurfacePickerXChanged(int)));
 | 
			
		||||
    connect(surface_picker_y_control, SIGNAL(valueChanged(int)), this, SLOT(OnSurfacePickerYChanged(int)));
 | 
			
		||||
    connect(surface_source_list, SIGNAL(currentIndexChanged(int)), this,
 | 
			
		||||
            SLOT(OnSurfaceSourceChanged(int)));
 | 
			
		||||
    connect(surface_address_control, SIGNAL(ValueChanged(qint64)), this,
 | 
			
		||||
            SLOT(OnSurfaceAddressChanged(qint64)));
 | 
			
		||||
    connect(surface_width_control, SIGNAL(valueChanged(int)), this,
 | 
			
		||||
            SLOT(OnSurfaceWidthChanged(int)));
 | 
			
		||||
    connect(surface_height_control, SIGNAL(valueChanged(int)), this,
 | 
			
		||||
            SLOT(OnSurfaceHeightChanged(int)));
 | 
			
		||||
    connect(surface_format_control, SIGNAL(currentIndexChanged(int)), this,
 | 
			
		||||
            SLOT(OnSurfaceFormatChanged(int)));
 | 
			
		||||
    connect(surface_picker_x_control, SIGNAL(valueChanged(int)), this,
 | 
			
		||||
            SLOT(OnSurfacePickerXChanged(int)));
 | 
			
		||||
    connect(surface_picker_y_control, SIGNAL(valueChanged(int)), this,
 | 
			
		||||
            SLOT(OnSurfacePickerYChanged(int)));
 | 
			
		||||
    connect(save_surface, SIGNAL(clicked()), this, SLOT(SaveSurface()));
 | 
			
		||||
 | 
			
		||||
    auto main_widget = new QWidget;
 | 
			
		||||
@ -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();
 | 
			
		||||
    widget()->setEnabled(true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GraphicsSurfaceWidget::OnResumed()
 | 
			
		||||
{
 | 
			
		||||
void GraphicsSurfaceWidget::OnResumed() {
 | 
			
		||||
    widget()->setEnabled(false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GraphicsSurfaceWidget::OnSurfaceSourceChanged(int new_value)
 | 
			
		||||
{
 | 
			
		||||
void GraphicsSurfaceWidget::OnSurfaceSourceChanged(int new_value) {
 | 
			
		||||
    surface_source = static_cast<Source>(new_value);
 | 
			
		||||
    emit Update();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GraphicsSurfaceWidget::OnSurfaceAddressChanged(qint64 new_value)
 | 
			
		||||
{
 | 
			
		||||
void GraphicsSurfaceWidget::OnSurfaceAddressChanged(qint64 new_value) {
 | 
			
		||||
    if (surface_address != 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)) {
 | 
			
		||||
        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)) {
 | 
			
		||||
        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)) {
 | 
			
		||||
        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) {
 | 
			
		||||
        surface_picker_x = new_value;
 | 
			
		||||
        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) {
 | 
			
		||||
        surface_picker_y = new_value;
 | 
			
		||||
        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_y_control->setValue(y);
 | 
			
		||||
 | 
			
		||||
@ -312,74 +302,63 @@ void GraphicsSurfaceWidget::Pick(int x, int y)
 | 
			
		||||
 | 
			
		||||
    auto GetText = [offset](Format format, const u8* pixel) {
 | 
			
		||||
        switch (format) {
 | 
			
		||||
        case Format::RGBA8:
 | 
			
		||||
        {
 | 
			
		||||
        case Format::RGBA8: {
 | 
			
		||||
            auto value = Color::DecodeRGBA8(pixel) / 255.0f;
 | 
			
		||||
            return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4")
 | 
			
		||||
                      .arg(QString::number(value.r(), 'f', 2))
 | 
			
		||||
                      .arg(QString::number(value.g(), 'f', 2))
 | 
			
		||||
                      .arg(QString::number(value.b(), 'f', 2))
 | 
			
		||||
                      .arg(QString::number(value.a(), 'f', 2));
 | 
			
		||||
                .arg(QString::number(value.r(), 'f', 2))
 | 
			
		||||
                .arg(QString::number(value.g(), 'f', 2))
 | 
			
		||||
                .arg(QString::number(value.b(), 'f', 2))
 | 
			
		||||
                .arg(QString::number(value.a(), 'f', 2));
 | 
			
		||||
        }
 | 
			
		||||
        case Format::RGB8:
 | 
			
		||||
        {
 | 
			
		||||
        case Format::RGB8: {
 | 
			
		||||
            auto value = Color::DecodeRGB8(pixel) / 255.0f;
 | 
			
		||||
            return QString("Red: %1, Green: %2, Blue: %3")
 | 
			
		||||
                      .arg(QString::number(value.r(), 'f', 2))
 | 
			
		||||
                      .arg(QString::number(value.g(), 'f', 2))
 | 
			
		||||
                      .arg(QString::number(value.b(), 'f', 2));
 | 
			
		||||
                .arg(QString::number(value.r(), 'f', 2))
 | 
			
		||||
                .arg(QString::number(value.g(), 'f', 2))
 | 
			
		||||
                .arg(QString::number(value.b(), 'f', 2));
 | 
			
		||||
        }
 | 
			
		||||
        case Format::RGB5A1:
 | 
			
		||||
        {
 | 
			
		||||
        case Format::RGB5A1: {
 | 
			
		||||
            auto value = Color::DecodeRGB5A1(pixel) / 255.0f;
 | 
			
		||||
            return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4")
 | 
			
		||||
                      .arg(QString::number(value.r(), 'f', 2))
 | 
			
		||||
                      .arg(QString::number(value.g(), 'f', 2))
 | 
			
		||||
                      .arg(QString::number(value.b(), 'f', 2))
 | 
			
		||||
                      .arg(QString::number(value.a(), 'f', 2));
 | 
			
		||||
                .arg(QString::number(value.r(), 'f', 2))
 | 
			
		||||
                .arg(QString::number(value.g(), 'f', 2))
 | 
			
		||||
                .arg(QString::number(value.b(), 'f', 2))
 | 
			
		||||
                .arg(QString::number(value.a(), 'f', 2));
 | 
			
		||||
        }
 | 
			
		||||
        case Format::RGB565:
 | 
			
		||||
        {
 | 
			
		||||
        case Format::RGB565: {
 | 
			
		||||
            auto value = Color::DecodeRGB565(pixel) / 255.0f;
 | 
			
		||||
            return QString("Red: %1, Green: %2, Blue: %3")
 | 
			
		||||
                      .arg(QString::number(value.r(), 'f', 2))
 | 
			
		||||
                      .arg(QString::number(value.g(), 'f', 2))
 | 
			
		||||
                      .arg(QString::number(value.b(), 'f', 2));
 | 
			
		||||
                .arg(QString::number(value.r(), 'f', 2))
 | 
			
		||||
                .arg(QString::number(value.g(), 'f', 2))
 | 
			
		||||
                .arg(QString::number(value.b(), 'f', 2));
 | 
			
		||||
        }
 | 
			
		||||
        case Format::RGBA4:
 | 
			
		||||
        {
 | 
			
		||||
        case Format::RGBA4: {
 | 
			
		||||
            auto value = Color::DecodeRGBA4(pixel) / 255.0f;
 | 
			
		||||
            return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4")
 | 
			
		||||
                      .arg(QString::number(value.r(), 'f', 2))
 | 
			
		||||
                      .arg(QString::number(value.g(), 'f', 2))
 | 
			
		||||
                      .arg(QString::number(value.b(), 'f', 2))
 | 
			
		||||
                      .arg(QString::number(value.a(), 'f', 2));
 | 
			
		||||
                .arg(QString::number(value.r(), 'f', 2))
 | 
			
		||||
                .arg(QString::number(value.g(), 'f', 2))
 | 
			
		||||
                .arg(QString::number(value.b(), 'f', 2))
 | 
			
		||||
                .arg(QString::number(value.a(), 'f', 2));
 | 
			
		||||
        }
 | 
			
		||||
        case Format::IA8:
 | 
			
		||||
            return QString("Index: %1, Alpha: %2")
 | 
			
		||||
                      .arg(pixel[0])
 | 
			
		||||
                      .arg(pixel[1]);
 | 
			
		||||
            return QString("Index: %1, Alpha: %2").arg(pixel[0]).arg(pixel[1]);
 | 
			
		||||
        case Format::RG8: {
 | 
			
		||||
            auto value = Color::DecodeRG8(pixel) / 255.0f;
 | 
			
		||||
            return QString("Red: %1, Green: %2")
 | 
			
		||||
                      .arg(QString::number(value.r(), 'f', 2))
 | 
			
		||||
                      .arg(QString::number(value.g(), 'f', 2));
 | 
			
		||||
                .arg(QString::number(value.r(), 'f', 2))
 | 
			
		||||
                .arg(QString::number(value.g(), 'f', 2));
 | 
			
		||||
        }
 | 
			
		||||
        case Format::I8:
 | 
			
		||||
            return QString("Index: %1").arg(*pixel);
 | 
			
		||||
        case Format::A8:
 | 
			
		||||
            return QString("Alpha: %1").arg(QString::number(*pixel / 255.0f, 'f', 2));
 | 
			
		||||
        case Format::IA4:
 | 
			
		||||
            return QString("Index: %1, Alpha: %2")
 | 
			
		||||
                      .arg(*pixel & 0xF)
 | 
			
		||||
                      .arg((*pixel & 0xF0) >> 4);
 | 
			
		||||
        case Format::I4:
 | 
			
		||||
        {
 | 
			
		||||
            return QString("Index: %1, Alpha: %2").arg(*pixel & 0xF).arg((*pixel & 0xF0) >> 4);
 | 
			
		||||
        case Format::I4: {
 | 
			
		||||
            u8 i = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF;
 | 
			
		||||
            return QString("Index: %1").arg(i);
 | 
			
		||||
        }
 | 
			
		||||
        case Format::A4:
 | 
			
		||||
        {
 | 
			
		||||
        case Format::A4: {
 | 
			
		||||
            u8 a = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF;
 | 
			
		||||
            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:
 | 
			
		||||
            // TODO: Display block information or channel values?
 | 
			
		||||
            return QString("Compressed data");
 | 
			
		||||
        case Format::D16:
 | 
			
		||||
        {
 | 
			
		||||
        case Format::D16: {
 | 
			
		||||
            auto value = Color::DecodeD16(pixel);
 | 
			
		||||
            return QString("Depth: %1").arg(QString::number(value / (float)0xFFFF, 'f', 4));
 | 
			
		||||
        }
 | 
			
		||||
        case Format::D24:
 | 
			
		||||
        {
 | 
			
		||||
        case Format::D24: {
 | 
			
		||||
            auto value = Color::DecodeD24(pixel);
 | 
			
		||||
            return QString("Depth: %1").arg(QString::number(value / (float)0xFFFFFF, 'f', 4));
 | 
			
		||||
        }
 | 
			
		||||
        case Format::D24X8:
 | 
			
		||||
        case Format::X24S8:
 | 
			
		||||
        {
 | 
			
		||||
        case Format::X24S8: {
 | 
			
		||||
            auto values = Color::DecodeD24S8(pixel);
 | 
			
		||||
            return QString("Depth: %1, Stencil: %2").arg(QString::number(values[0] / (float)0xFFFFFF, 'f', 4)).arg(values[1]);
 | 
			
		||||
            return QString("Depth: %1, Stencil: %2")
 | 
			
		||||
                .arg(QString::number(values[0] / (float)0xFFFFFF, 'f', 4))
 | 
			
		||||
                .arg(values[1]);
 | 
			
		||||
        }
 | 
			
		||||
        case Format::Unknown:
 | 
			
		||||
            return QString("Unknown format");
 | 
			
		||||
@ -422,18 +400,18 @@ void GraphicsSurfaceWidget::Pick(int x, int y)
 | 
			
		||||
        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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GraphicsSurfaceWidget::OnUpdate()
 | 
			
		||||
{
 | 
			
		||||
void GraphicsSurfaceWidget::OnUpdate() {
 | 
			
		||||
    QPixmap pixmap;
 | 
			
		||||
 | 
			
		||||
    switch (surface_source) {
 | 
			
		||||
    case Source::ColorBuffer:
 | 
			
		||||
    {
 | 
			
		||||
        // TODO: Store a reference to the registers in the debug context instead of accessing them directly...
 | 
			
		||||
    case Source::ColorBuffer: {
 | 
			
		||||
        // TODO: Store a reference to the registers in the debug context instead of accessing them
 | 
			
		||||
        // directly...
 | 
			
		||||
 | 
			
		||||
        const auto& framebuffer = Pica::g_state.regs.framebuffer;
 | 
			
		||||
 | 
			
		||||
@ -470,8 +448,7 @@ void GraphicsSurfaceWidget::OnUpdate()
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    case Source::DepthBuffer:
 | 
			
		||||
    {
 | 
			
		||||
    case Source::DepthBuffer: {
 | 
			
		||||
        const auto& framebuffer = Pica::g_state.regs.framebuffer;
 | 
			
		||||
 | 
			
		||||
        surface_address = framebuffer.GetDepthBufferPhysicalAddress();
 | 
			
		||||
@ -499,8 +476,7 @@ void GraphicsSurfaceWidget::OnUpdate()
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    case Source::StencilBuffer:
 | 
			
		||||
    {
 | 
			
		||||
    case Source::StencilBuffer: {
 | 
			
		||||
        const auto& framebuffer = Pica::g_state.regs.framebuffer;
 | 
			
		||||
 | 
			
		||||
        surface_address = framebuffer.GetDepthBufferPhysicalAddress();
 | 
			
		||||
@ -522,12 +498,14 @@ void GraphicsSurfaceWidget::OnUpdate()
 | 
			
		||||
 | 
			
		||||
    case Source::Texture0:
 | 
			
		||||
    case Source::Texture1:
 | 
			
		||||
    case Source::Texture2:
 | 
			
		||||
    {
 | 
			
		||||
    case Source::Texture2: {
 | 
			
		||||
        unsigned texture_index;
 | 
			
		||||
        if (surface_source == Source::Texture0) texture_index = 0;
 | 
			
		||||
        else if (surface_source == Source::Texture1) texture_index = 1;
 | 
			
		||||
        else if (surface_source == Source::Texture2) texture_index = 2;
 | 
			
		||||
        if (surface_source == Source::Texture0)
 | 
			
		||||
            texture_index = 0;
 | 
			
		||||
        else if (surface_source == Source::Texture1)
 | 
			
		||||
            texture_index = 1;
 | 
			
		||||
        else if (surface_source == Source::Texture2)
 | 
			
		||||
            texture_index = 2;
 | 
			
		||||
        else {
 | 
			
		||||
            qDebug() << "Unknown texture source " << static_cast<int>(surface_source);
 | 
			
		||||
            break;
 | 
			
		||||
@ -547,8 +525,7 @@ void GraphicsSurfaceWidget::OnUpdate()
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    case Source::Custom:
 | 
			
		||||
    {
 | 
			
		||||
    case Source::Custom: {
 | 
			
		||||
        // Keep user-specified values
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
@ -613,7 +590,8 @@ void GraphicsSurfaceWidget::OnUpdate()
 | 
			
		||||
 | 
			
		||||
    } 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;
 | 
			
		||||
 | 
			
		||||
        for (unsigned int y = 0; y < surface_height; ++y) {
 | 
			
		||||
@ -621,34 +599,30 @@ void GraphicsSurfaceWidget::OnUpdate()
 | 
			
		||||
                const u32 coarse_y = y & ~7;
 | 
			
		||||
                u32 offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride;
 | 
			
		||||
                const u8* pixel = buffer + offset;
 | 
			
		||||
                Math::Vec4<u8> color = { 0, 0, 0, 0 };
 | 
			
		||||
                Math::Vec4<u8> color = {0, 0, 0, 0};
 | 
			
		||||
 | 
			
		||||
                switch(surface_format) {
 | 
			
		||||
                case Format::D16:
 | 
			
		||||
                {
 | 
			
		||||
                switch (surface_format) {
 | 
			
		||||
                case Format::D16: {
 | 
			
		||||
                    u32 data = Color::DecodeD16(pixel);
 | 
			
		||||
                    color.r() = data & 0xFF;
 | 
			
		||||
                    color.g() = (data >> 8) & 0xFF;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case Format::D24:
 | 
			
		||||
                {
 | 
			
		||||
                case Format::D24: {
 | 
			
		||||
                    u32 data = Color::DecodeD24(pixel);
 | 
			
		||||
                    color.r() = data & 0xFF;
 | 
			
		||||
                    color.g() = (data >> 8) & 0xFF;
 | 
			
		||||
                    color.b() = (data >> 16) & 0xFF;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case Format::D24X8:
 | 
			
		||||
                {
 | 
			
		||||
                case Format::D24X8: {
 | 
			
		||||
                    Math::Vec2<u32> data = Color::DecodeD24S8(pixel);
 | 
			
		||||
                    color.r() = data.x & 0xFF;
 | 
			
		||||
                    color.g() = (data.x >> 8) & 0xFF;
 | 
			
		||||
                    color.b() = (data.x >> 16) & 0xFF;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case Format::X24S8:
 | 
			
		||||
                {
 | 
			
		||||
                case Format::X24S8: {
 | 
			
		||||
                    Math::Vec2<u32> data = Color::DecodeD24S8(pixel);
 | 
			
		||||
                    color.r() = color.g() = color.b() = data.y;
 | 
			
		||||
                    break;
 | 
			
		||||
@ -661,7 +635,6 @@ void GraphicsSurfaceWidget::OnUpdate()
 | 
			
		||||
                decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), 255));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pixmap = QPixmap::fromImage(decoded_image);
 | 
			
		||||
@ -682,8 +655,10 @@ void GraphicsSurfaceWidget::SaveSurface() {
 | 
			
		||||
    QString bin_filter = tr("Binary data (*.bin)");
 | 
			
		||||
 | 
			
		||||
    QString selectedFilter;
 | 
			
		||||
    QString filename = QFileDialog::getSaveFileName(this, tr("Save Surface"), QString("texture-0x%1.png").arg(QString::number(surface_address, 16)),
 | 
			
		||||
                                                    QString("%1;;%2").arg(png_filter, bin_filter), &selectedFilter);
 | 
			
		||||
    QString filename = QFileDialog::getSaveFileName(
 | 
			
		||||
        this, tr("Save Surface"),
 | 
			
		||||
        QString("texture-0x%1.png").arg(QString::number(surface_address, 16)),
 | 
			
		||||
        QString("%1;;%2").arg(png_filter, bin_filter), &selectedFilter);
 | 
			
		||||
 | 
			
		||||
    if (filename.isEmpty()) {
 | 
			
		||||
        // If the user canceled the dialog, don't save anything.
 | 
			
		||||
@ -718,19 +693,18 @@ unsigned int GraphicsSurfaceWidget::NibblesPerPixel(GraphicsSurfaceWidget::Forma
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    switch (format) {
 | 
			
		||||
        case Format::D24X8:
 | 
			
		||||
        case Format::X24S8:
 | 
			
		||||
            return 4 * 2;
 | 
			
		||||
        case Format::D24:
 | 
			
		||||
            return 3 * 2;
 | 
			
		||||
        case Format::D16:
 | 
			
		||||
            return 2 * 2;
 | 
			
		||||
        default:
 | 
			
		||||
            UNREACHABLE_MSG("GraphicsSurfaceWidget::BytesPerPixel: this "
 | 
			
		||||
                            "should not be reached as this function should "
 | 
			
		||||
                            "be given a format which is in "
 | 
			
		||||
                            "GraphicsSurfaceWidget::Format. Instead got %i",
 | 
			
		||||
                            static_cast<int>(format));
 | 
			
		||||
            return 0;
 | 
			
		||||
    case Format::D24X8:
 | 
			
		||||
    case Format::X24S8:
 | 
			
		||||
        return 4 * 2;
 | 
			
		||||
    case Format::D24:
 | 
			
		||||
        return 3 * 2;
 | 
			
		||||
    case Format::D16:
 | 
			
		||||
        return 2 * 2;
 | 
			
		||||
    default:
 | 
			
		||||
        UNREACHABLE_MSG("GraphicsSurfaceWidget::BytesPerPixel: this should not be reached as this "
 | 
			
		||||
                        "function should be given a format which is in "
 | 
			
		||||
                        "GraphicsSurfaceWidget::Format. Instead got %i",
 | 
			
		||||
                        static_cast<int>(format));
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -4,10 +4,9 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "citra_qt/debugger/graphics_breakpoint_observer.h"
 | 
			
		||||
 | 
			
		||||
#include <QLabel>
 | 
			
		||||
#include <QPushButton>
 | 
			
		||||
#include "citra_qt/debugger/graphics_breakpoint_observer.h"
 | 
			
		||||
 | 
			
		||||
class QComboBox;
 | 
			
		||||
class QSpinBox;
 | 
			
		||||
@ -15,8 +14,7 @@ class CSpinBox;
 | 
			
		||||
 | 
			
		||||
class GraphicsSurfaceWidget;
 | 
			
		||||
 | 
			
		||||
class SurfacePicture : public QLabel
 | 
			
		||||
{
 | 
			
		||||
class SurfacePicture : public QLabel {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
@ -29,7 +27,6 @@ protected slots:
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    GraphicsSurfaceWidget* surface_widget;
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class GraphicsSurfaceWidget : public BreakPointObserverDock {
 | 
			
		||||
@ -38,43 +35,44 @@ class GraphicsSurfaceWidget : public BreakPointObserverDock {
 | 
			
		||||
    using Event = Pica::DebugContext::Event;
 | 
			
		||||
 | 
			
		||||
    enum class Source {
 | 
			
		||||
        ColorBuffer   = 0,
 | 
			
		||||
        DepthBuffer   = 1,
 | 
			
		||||
        ColorBuffer = 0,
 | 
			
		||||
        DepthBuffer = 1,
 | 
			
		||||
        StencilBuffer = 2,
 | 
			
		||||
        Texture0      = 3,
 | 
			
		||||
        Texture1      = 4,
 | 
			
		||||
        Texture2      = 5,
 | 
			
		||||
        Custom        = 6,
 | 
			
		||||
        Texture0 = 3,
 | 
			
		||||
        Texture1 = 4,
 | 
			
		||||
        Texture2 = 5,
 | 
			
		||||
        Custom = 6,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    enum class Format {
 | 
			
		||||
        // These must match the TextureFormat type!
 | 
			
		||||
        RGBA8        =  0,
 | 
			
		||||
        RGB8         =  1,
 | 
			
		||||
        RGB5A1       =  2,
 | 
			
		||||
        RGB565       =  3,
 | 
			
		||||
        RGBA4        =  4,
 | 
			
		||||
        IA8          =  5,
 | 
			
		||||
        RG8          =  6,  ///< @note Also called HILO8 in 3DBrew.
 | 
			
		||||
        I8           =  7,
 | 
			
		||||
        A8           =  8,
 | 
			
		||||
        IA4          =  9,
 | 
			
		||||
        I4           = 10,
 | 
			
		||||
        A4           = 11,
 | 
			
		||||
        ETC1         = 12,  // compressed
 | 
			
		||||
        ETC1A4       = 13,
 | 
			
		||||
        RGBA8 = 0,
 | 
			
		||||
        RGB8 = 1,
 | 
			
		||||
        RGB5A1 = 2,
 | 
			
		||||
        RGB565 = 3,
 | 
			
		||||
        RGBA4 = 4,
 | 
			
		||||
        IA8 = 5,
 | 
			
		||||
        RG8 = 6, ///< @note Also called HILO8 in 3DBrew.
 | 
			
		||||
        I8 = 7,
 | 
			
		||||
        A8 = 8,
 | 
			
		||||
        IA4 = 9,
 | 
			
		||||
        I4 = 10,
 | 
			
		||||
        A4 = 11,
 | 
			
		||||
        ETC1 = 12, // compressed
 | 
			
		||||
        ETC1A4 = 13,
 | 
			
		||||
        MaxTextureFormat = 13,
 | 
			
		||||
        D16          = 14,
 | 
			
		||||
        D24          = 15,
 | 
			
		||||
        D24X8        = 16,
 | 
			
		||||
        X24S8        = 17,
 | 
			
		||||
        Unknown      = 18,
 | 
			
		||||
        D16 = 14,
 | 
			
		||||
        D24 = 15,
 | 
			
		||||
        D24X8 = 16,
 | 
			
		||||
        X24S8 = 17,
 | 
			
		||||
        Unknown = 18,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    static unsigned int NibblesPerPixel(Format format);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent = nullptr);
 | 
			
		||||
    GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext> debug_context,
 | 
			
		||||
                          QWidget* parent = nullptr);
 | 
			
		||||
    void Pick(int x, int y);
 | 
			
		||||
 | 
			
		||||
public slots:
 | 
			
		||||
@ -97,7 +95,6 @@ signals:
 | 
			
		||||
    void Update();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
 | 
			
		||||
    QComboBox* surface_source_list;
 | 
			
		||||
    CSpinBox* surface_address_control;
 | 
			
		||||
    QSpinBox* surface_width_control;
 | 
			
		||||
 | 
			
		||||
@ -6,25 +6,18 @@
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <iterator>
 | 
			
		||||
#include <memory>
 | 
			
		||||
 | 
			
		||||
#include <boost/range/algorithm/copy.hpp>
 | 
			
		||||
 | 
			
		||||
#include <QBoxLayout>
 | 
			
		||||
#include <QComboBox>
 | 
			
		||||
#include <QFileDialog>
 | 
			
		||||
#include <QMessageBox>
 | 
			
		||||
#include <QPushButton>
 | 
			
		||||
 | 
			
		||||
#include <boost/range/algorithm/copy.hpp>
 | 
			
		||||
#include "citra_qt/debugger/graphics_tracing.h"
 | 
			
		||||
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
 | 
			
		||||
#include "core/hw/gpu.h"
 | 
			
		||||
#include "core/hw/lcd.h"
 | 
			
		||||
#include "core/tracer/recorder.h"
 | 
			
		||||
 | 
			
		||||
#include "nihstro/float24.h"
 | 
			
		||||
 | 
			
		||||
#include "video_core/pica.h"
 | 
			
		||||
#include "video_core/pica_state.h"
 | 
			
		||||
 | 
			
		||||
@ -35,12 +28,16 @@ GraphicsTracingWidget::GraphicsTracingWidget(std::shared_ptr<Pica::DebugContext>
 | 
			
		||||
    setObjectName("CiTracing");
 | 
			
		||||
 | 
			
		||||
    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"));
 | 
			
		||||
 | 
			
		||||
    connect(this, SIGNAL(SetStartTracingButtonEnabled(bool)), start_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(this, SIGNAL(SetStartTracingButtonEnabled(bool)), start_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(stop_recording, SIGNAL(clicked()), this, SLOT(StopRecording()));
 | 
			
		||||
    connect(abort_recording, SIGNAL(clicked()), this, SLOT(AbortRecording()));
 | 
			
		||||
@ -74,26 +71,31 @@ void GraphicsTracingWidget::StartRecording() {
 | 
			
		||||
    std::array<u32, 4 * 16> default_attributes;
 | 
			
		||||
    for (unsigned i = 0; i < 16; ++i) {
 | 
			
		||||
        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;
 | 
			
		||||
    for (unsigned i = 0; i < 96; ++i)
 | 
			
		||||
        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;
 | 
			
		||||
    std::copy_n((u32*)&GPU::g_regs, sizeof(GPU::g_regs) / sizeof(u32), std::back_inserter(state.gpu_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));
 | 
			
		||||
    std::copy_n((u32*)&GPU::g_regs, sizeof(GPU::g_regs) / sizeof(u32),
 | 
			
		||||
                std::back_inserter(state.gpu_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(shader_binary, std::back_inserter(state.vs_program_binary));
 | 
			
		||||
    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(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_float_uniforms));
 | 
			
		||||
    // 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_float_uniforms));
 | 
			
		||||
 | 
			
		||||
    auto recorder = new CiTrace::Recorder(state);
 | 
			
		||||
    context->recorder = std::shared_ptr<CiTrace::Recorder>(recorder);
 | 
			
		||||
@ -156,11 +158,12 @@ void GraphicsTracingWidget::OnEmulationStopping() {
 | 
			
		||||
    if (!context)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    if (context->recorder) {
 | 
			
		||||
        auto reply = 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);
 | 
			
		||||
        auto reply =
 | 
			
		||||
            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);
 | 
			
		||||
 | 
			
		||||
        if (reply == QMessageBox::Yes) {
 | 
			
		||||
            StopRecording();
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,8 @@ class GraphicsTracingWidget : public BreakPointObserverDock {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
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:
 | 
			
		||||
    void StartRecording();
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,6 @@
 | 
			
		||||
 | 
			
		||||
#include <iomanip>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
 | 
			
		||||
#include <QBoxLayout>
 | 
			
		||||
#include <QFileDialog>
 | 
			
		||||
#include <QFormLayout>
 | 
			
		||||
@ -15,10 +14,8 @@
 | 
			
		||||
#include <QSignalMapper>
 | 
			
		||||
#include <QSpinBox>
 | 
			
		||||
#include <QTreeView>
 | 
			
		||||
 | 
			
		||||
#include "citra_qt/debugger/graphics_vertex_shader.h"
 | 
			
		||||
#include "citra_qt/util/util.h"
 | 
			
		||||
 | 
			
		||||
#include "video_core/pica.h"
 | 
			
		||||
#include "video_core/pica_state.h"
 | 
			
		||||
#include "video_core/shader/shader.h"
 | 
			
		||||
@ -28,9 +25,8 @@ using nihstro::Instruction;
 | 
			
		||||
using nihstro::SourceRegister;
 | 
			
		||||
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 {
 | 
			
		||||
    return 3;
 | 
			
		||||
@ -40,10 +36,10 @@ int GraphicsVertexShaderModel::rowCount(const QModelIndex& parent) const {
 | 
			
		||||
    return static_cast<int>(par->info.code.size());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QVariant GraphicsVertexShaderModel::headerData(int section, Qt::Orientation orientation, int role) const {
 | 
			
		||||
    switch(role) {
 | 
			
		||||
    case Qt::DisplayRole:
 | 
			
		||||
    {
 | 
			
		||||
QVariant GraphicsVertexShaderModel::headerData(int section, Qt::Orientation orientation,
 | 
			
		||||
                                               int role) const {
 | 
			
		||||
    switch (role) {
 | 
			
		||||
    case Qt::DisplayRole: {
 | 
			
		||||
        if (section == 0) {
 | 
			
		||||
            return tr("Offset");
 | 
			
		||||
        } else if (section == 1) {
 | 
			
		||||
@ -69,8 +65,8 @@ static std::string SelectorToString(u32 selector) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// e.g. "-c92[a0.x].xyzw"
 | 
			
		||||
static void print_input(std::ostringstream& output, const SourceRegister& input,
 | 
			
		||||
                        bool negate, const std::string& swizzle_mask, bool align = true,
 | 
			
		||||
static void print_input(std::ostringstream& output, const SourceRegister& input, bool negate,
 | 
			
		||||
                        const std::string& swizzle_mask, bool align = true,
 | 
			
		||||
                        const std::string& address_register_name = std::string()) {
 | 
			
		||||
    if (align)
 | 
			
		||||
        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 {
 | 
			
		||||
    switch (role) {
 | 
			
		||||
    case Qt::DisplayRole:
 | 
			
		||||
    {
 | 
			
		||||
    case Qt::DisplayRole: {
 | 
			
		||||
        switch (index.column()) {
 | 
			
		||||
        case 0:
 | 
			
		||||
            if (par->info.HasLabel(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:
 | 
			
		||||
            return QString("%1").arg(par->info.code[index.row()].hex, 8, 16, QLatin1Char('0'));
 | 
			
		||||
 | 
			
		||||
        case 2:
 | 
			
		||||
        {
 | 
			
		||||
        case 2: {
 | 
			
		||||
            std::ostringstream output;
 | 
			
		||||
            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 OpCode opcode = instr.opcode;
 | 
			
		||||
            const OpCode::Info opcode_info = opcode.GetInfo();
 | 
			
		||||
            const u32 operand_desc_id = opcode_info.type == OpCode::Type::MultiplyAdd ?
 | 
			
		||||
                instr.mad.operand_desc_id.Value() : instr.common.operand_desc_id.Value();
 | 
			
		||||
            const u32 operand_desc_id = opcode_info.type == OpCode::Type::MultiplyAdd
 | 
			
		||||
                                            ? instr.mad.operand_desc_id.Value()
 | 
			
		||||
                                            : instr.common.operand_desc_id.Value();
 | 
			
		||||
            const SwizzlePattern swizzle = par->info.swizzle_info[operand_desc_id].pattern;
 | 
			
		||||
 | 
			
		||||
            // longest known instruction name: "setemit "
 | 
			
		||||
@ -136,15 +131,14 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case OpCode::Type::Arithmetic:
 | 
			
		||||
            case OpCode::Type::MultiplyAdd:
 | 
			
		||||
            {
 | 
			
		||||
            case OpCode::Type::MultiplyAdd: {
 | 
			
		||||
                // Use custom code for special instructions
 | 
			
		||||
                switch (opcode.EffectiveOpCode()) {
 | 
			
		||||
                case OpCode::Id::CMP:
 | 
			
		||||
                {
 | 
			
		||||
                case OpCode::Id::CMP: {
 | 
			
		||||
                    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";
 | 
			
		||||
                    AlignToColumn(kOutputColumnWidth);
 | 
			
		||||
 | 
			
		||||
@ -152,22 +146,29 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
 | 
			
		||||
                    SourceRegister src2 = instr.common.GetSrc2(false);
 | 
			
		||||
 | 
			
		||||
                    output << ' ';
 | 
			
		||||
                    print_input(output, src1, swizzle.negate_src1, swizzle.SelectorToString(false).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);
 | 
			
		||||
                    print_input(output, src1, swizzle.negate_src1,
 | 
			
		||||
                                swizzle.SelectorToString(false).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 << ", ";
 | 
			
		||||
 | 
			
		||||
                    print_input(output, src1, swizzle.negate_src1, swizzle.SelectorToString(false).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);
 | 
			
		||||
                    print_input(output, src1, swizzle.negate_src1,
 | 
			
		||||
                                swizzle.SelectorToString(false).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;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                case OpCode::Id::MAD:
 | 
			
		||||
                case OpCode::Id::MADI:
 | 
			
		||||
                {
 | 
			
		||||
                case OpCode::Id::MADI: {
 | 
			
		||||
                    AlignToColumn(kOpcodeColumnWidth);
 | 
			
		||||
 | 
			
		||||
                    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 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);
 | 
			
		||||
                    print_input(output, src1, swizzle.negate_src1, SelectorToString(swizzle.src1_selector));
 | 
			
		||||
                    print_input(output, src1, swizzle.negate_src1,
 | 
			
		||||
                                SelectorToString(swizzle.src1_selector));
 | 
			
		||||
                    AlignToColumn(kInputOperandColumnWidth);
 | 
			
		||||
                    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 {
 | 
			
		||||
                      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);
 | 
			
		||||
                    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 {
 | 
			
		||||
                      print_input(output, src3, swizzle.negate_src3, SelectorToString(swizzle.src3_selector));
 | 
			
		||||
                        print_input(output, src3, swizzle.negate_src3,
 | 
			
		||||
                                    SelectorToString(swizzle.src3_selector));
 | 
			
		||||
                    }
 | 
			
		||||
                    AlignToColumn(kInputOperandColumnWidth);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                default:
 | 
			
		||||
                {
 | 
			
		||||
                default: {
 | 
			
		||||
                    AlignToColumn(kOpcodeColumnWidth);
 | 
			
		||||
 | 
			
		||||
                    bool src_is_inverted = 0 != (opcode_info.subtype & OpCode::Info::SrcInversed);
 | 
			
		||||
 | 
			
		||||
                    if (opcode_info.subtype & OpCode::Info::Dest) {
 | 
			
		||||
                        // 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) {
 | 
			
		||||
                        output << "  a0." << swizzle.DestMaskToString();
 | 
			
		||||
                    }
 | 
			
		||||
@ -210,14 +219,18 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
 | 
			
		||||
 | 
			
		||||
                    if (opcode_info.subtype & OpCode::Info::Src1) {
 | 
			
		||||
                        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);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    // 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) {
 | 
			
		||||
                        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);
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
@ -228,8 +241,7 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            case OpCode::Type::Conditional:
 | 
			
		||||
            case OpCode::Type::UniformFlowControl:
 | 
			
		||||
            {
 | 
			
		||||
            case OpCode::Type::UniformFlowControl: {
 | 
			
		||||
                output << ' ';
 | 
			
		||||
 | 
			
		||||
                switch (opcode.EffectiveOpCode()) {
 | 
			
		||||
@ -242,7 +254,8 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
 | 
			
		||||
                        output << '(';
 | 
			
		||||
 | 
			
		||||
                        if (instr.flow_control.op != instr.flow_control.JustY) {
 | 
			
		||||
                            if (instr.flow_control.refx) output << '!';
 | 
			
		||||
                            if (instr.flow_control.refx)
 | 
			
		||||
                                output << '!';
 | 
			
		||||
                            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.refy) output << '!';
 | 
			
		||||
                            if (instr.flow_control.refy)
 | 
			
		||||
                                output << '!';
 | 
			
		||||
                            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;
 | 
			
		||||
 | 
			
		||||
                    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) {
 | 
			
		||||
                        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 {
 | 
			
		||||
                        // TODO: Handle other cases
 | 
			
		||||
                        output << "(unknown destination)";
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (opcode_info.subtype & OpCode::Info::HasFinishPoint) {
 | 
			
		||||
                        output << " (return on 0x" << std::setw(4) << std::right << std::setfill('0') << std::hex
 | 
			
		||||
                               << (4 * instr.flow_control.dest_offset + 4 * instr.flow_control.num_instructions) << ')';
 | 
			
		||||
                        output << " (return on 0x" << std::setw(4) << std::right
 | 
			
		||||
                               << std::setfill('0') << std::hex
 | 
			
		||||
                               << (4 * instr.flow_control.dest_offset +
 | 
			
		||||
                                   4 * instr.flow_control.num_instructions)
 | 
			
		||||
                               << ')';
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    break;
 | 
			
		||||
@ -300,8 +320,7 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
 | 
			
		||||
    case Qt::FontRole:
 | 
			
		||||
        return GetMonospaceFont();
 | 
			
		||||
 | 
			
		||||
    case Qt::BackgroundRole:
 | 
			
		||||
    {
 | 
			
		||||
    case Qt::BackgroundRole: {
 | 
			
		||||
        // Highlight current instruction
 | 
			
		||||
        int current_record_index = par->cycle_index->value();
 | 
			
		||||
        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));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // TODO: Draw arrows for each "reachable" instruction to visualize control flow
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
@ -331,23 +348,24 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GraphicsVertexShaderWidget::DumpShader() {
 | 
			
		||||
    QString filename = QFileDialog::getSaveFileName(this, tr("Save Shader Dump"), "shader_dump.shbin",
 | 
			
		||||
                                                    tr("Shader Binary (*.shbin)"));
 | 
			
		||||
    QString filename = QFileDialog::getSaveFileName(
 | 
			
		||||
        this, tr("Save Shader Dump"), "shader_dump.shbin", tr("Shader Binary (*.shbin)"));
 | 
			
		||||
 | 
			
		||||
    if (filename.isEmpty()) {
 | 
			
		||||
        // If the user canceled the dialog, don't dump anything.
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto& setup  = Pica::g_state.vs;
 | 
			
		||||
    auto& setup = Pica::g_state.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,
 | 
			
		||||
                                                       QWidget* parent)
 | 
			
		||||
        : BreakPointObserverDock(debug_context, "Pica Vertex Shader", parent) {
 | 
			
		||||
GraphicsVertexShaderWidget::GraphicsVertexShaderWidget(
 | 
			
		||||
    std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent)
 | 
			
		||||
    : BreakPointObserverDock(debug_context, "Pica Vertex Shader", parent) {
 | 
			
		||||
    setObjectName("PicaVertexShader");
 | 
			
		||||
 | 
			
		||||
    // Clear input vertex data so that it contains valid float values in case a debug shader
 | 
			
		||||
@ -365,7 +383,8 @@ GraphicsVertexShaderWidget::GraphicsVertexShaderWidget(std::shared_ptr< Pica::De
 | 
			
		||||
        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
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
    // 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);
 | 
			
		||||
    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));
 | 
			
		||||
            for (unsigned attr = 0; attr < 16; ++attr) {
 | 
			
		||||
                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();
 | 
			
		||||
@ -498,10 +519,11 @@ void GraphicsVertexShaderWidget::Reload(bool replace_vertex_data, void* vertex_d
 | 
			
		||||
        info.swizzle_info.push_back({pattern});
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
    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
 | 
			
		||||
    for (int attr = 0; attr < num_attributes; ++attr) {
 | 
			
		||||
@ -537,29 +559,60 @@ void GraphicsVertexShaderWidget::OnCycleIndexChanged(int index) {
 | 
			
		||||
 | 
			
		||||
    auto& record = debug_data.records[index];
 | 
			
		||||
    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)
 | 
			
		||||
        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)
 | 
			
		||||
        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)
 | 
			
		||||
        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)
 | 
			
		||||
        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)
 | 
			
		||||
        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)
 | 
			
		||||
        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)
 | 
			
		||||
        text += tr("Static Condition: %1\n").arg(record.cond_bool ? "true" : "false");
 | 
			
		||||
    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)
 | 
			
		||||
        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) {
 | 
			
		||||
        text += tr(" -> 0x%2").arg(4 * record.next_instruction, 4, 16, QLatin1Char('0'));
 | 
			
		||||
    } else {
 | 
			
		||||
@ -570,6 +623,7 @@ void GraphicsVertexShaderWidget::OnCycleIndexChanged(int index) {
 | 
			
		||||
 | 
			
		||||
    // Emit model update notification and scroll to current instruction
 | 
			
		||||
    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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -5,11 +5,9 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <QAbstractTableModel>
 | 
			
		||||
 | 
			
		||||
#include <QTreeView>
 | 
			
		||||
#include "citra_qt/debugger/graphics_breakpoint_observer.h"
 | 
			
		||||
 | 
			
		||||
#include "nihstro/parser_shbin.h"
 | 
			
		||||
 | 
			
		||||
#include "video_core/shader/shader.h"
 | 
			
		||||
 | 
			
		||||
class QLabel;
 | 
			
		||||
@ -26,7 +24,8 @@ public:
 | 
			
		||||
    int columnCount(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 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:
 | 
			
		||||
    GraphicsVertexShaderWidget* par;
 | 
			
		||||
@ -56,7 +55,9 @@ private slots:
 | 
			
		||||
    /**
 | 
			
		||||
     * Reload widget based on the current PICA200 state
 | 
			
		||||
     * @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);
 | 
			
		||||
 | 
			
		||||
@ -66,9 +67,12 @@ private:
 | 
			
		||||
    GraphicsVertexShaderModel* model;
 | 
			
		||||
 | 
			
		||||
    /// 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<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
 | 
			
		||||
    std::array<QLineEdit*, 4 * 16>
 | 
			
		||||
        input_data; // A text box for each of the 4 components of up to 16 vertex attributes
 | 
			
		||||
    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
 | 
			
		||||
    QLabel* breakpoint_warning;
 | 
			
		||||
 | 
			
		||||
@ -5,10 +5,8 @@
 | 
			
		||||
#include <QMouseEvent>
 | 
			
		||||
#include <QPainter>
 | 
			
		||||
#include <QString>
 | 
			
		||||
 | 
			
		||||
#include "citra_qt/debugger/profiler.h"
 | 
			
		||||
#include "citra_qt/util/util.h"
 | 
			
		||||
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
#include "common/microprofile.h"
 | 
			
		||||
#include "common/profiler_reporting.h"
 | 
			
		||||
@ -22,57 +20,58 @@
 | 
			
		||||
 | 
			
		||||
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 {
 | 
			
		||||
        using FloatMs = std::chrono::duration<float, std::chrono::milliseconds::period>;
 | 
			
		||||
        return std::chrono::duration_cast<FloatMs>(dur).count();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    switch (col) {
 | 
			
		||||
    case 1: return duration_to_float(duration.avg);
 | 
			
		||||
    case 2: return duration_to_float(duration.min);
 | 
			
		||||
    case 3: return duration_to_float(duration.max);
 | 
			
		||||
    default: return QVariant();
 | 
			
		||||
    case 1:
 | 
			
		||||
        return duration_to_float(duration.avg);
 | 
			
		||||
    case 2:
 | 
			
		||||
        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();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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) {
 | 
			
		||||
        switch (section) {
 | 
			
		||||
        case 0: return tr("Category");
 | 
			
		||||
        case 1: return tr("Avg");
 | 
			
		||||
        case 2: return tr("Min");
 | 
			
		||||
        case 3: return tr("Max");
 | 
			
		||||
        case 0:
 | 
			
		||||
            return tr("Category");
 | 
			
		||||
        case 1:
 | 
			
		||||
            return tr("Avg");
 | 
			
		||||
        case 2:
 | 
			
		||||
            return tr("Min");
 | 
			
		||||
        case 3:
 | 
			
		||||
            return tr("Max");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QModelIndex ProfilerModel::parent(const QModelIndex& child) const
 | 
			
		||||
{
 | 
			
		||||
QModelIndex ProfilerModel::parent(const QModelIndex& child) const {
 | 
			
		||||
    return QModelIndex();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ProfilerModel::columnCount(const QModelIndex& parent) const
 | 
			
		||||
{
 | 
			
		||||
int ProfilerModel::columnCount(const QModelIndex& parent) const {
 | 
			
		||||
    return 4;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ProfilerModel::rowCount(const QModelIndex& parent) const
 | 
			
		||||
{
 | 
			
		||||
int ProfilerModel::rowCount(const QModelIndex& parent) const {
 | 
			
		||||
    if (parent.isValid()) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    } 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 (index.row() == 0) {
 | 
			
		||||
            if (index.column() == 0) {
 | 
			
		||||
@ -101,14 +99,12 @@ QVariant ProfilerModel::data(const QModelIndex& index, int role) const
 | 
			
		||||
    return QVariant();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ProfilerModel::updateProfilingInfo()
 | 
			
		||||
{
 | 
			
		||||
void ProfilerModel::updateProfilingInfo() {
 | 
			
		||||
    results = GetTimingResultsAggregator()->GetAggregatedResults();
 | 
			
		||||
    emit dataChanged(createIndex(0, 1), createIndex(rowCount() - 1, 3));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ProfilerWidget::ProfilerWidget(QWidget* parent) : QDockWidget(parent)
 | 
			
		||||
{
 | 
			
		||||
ProfilerWidget::ProfilerWidget(QWidget* parent) : QDockWidget(parent) {
 | 
			
		||||
    ui.setupUi(this);
 | 
			
		||||
 | 
			
		||||
    model = new ProfilerModel(this);
 | 
			
		||||
@ -118,8 +114,7 @@ ProfilerWidget::ProfilerWidget(QWidget* parent) : QDockWidget(parent)
 | 
			
		||||
    connect(&update_timer, SIGNAL(timeout()), model, SLOT(updateProfilingInfo()));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ProfilerWidget::setProfilingInfoUpdateEnabled(bool enable)
 | 
			
		||||
{
 | 
			
		||||
void ProfilerWidget::setProfilingInfoUpdateEnabled(bool enable) {
 | 
			
		||||
    if (enable) {
 | 
			
		||||
        update_timer.start(100);
 | 
			
		||||
        model->updateProfilingInfo();
 | 
			
		||||
@ -157,9 +152,7 @@ private:
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
MicroProfileDialog::MicroProfileDialog(QWidget* parent)
 | 
			
		||||
    : QWidget(parent, Qt::Dialog)
 | 
			
		||||
{
 | 
			
		||||
MicroProfileDialog::MicroProfileDialog(QWidget* parent) : QWidget(parent, Qt::Dialog) {
 | 
			
		||||
    setObjectName("MicroProfile");
 | 
			
		||||
    setWindowTitle(tr("MicroProfile"));
 | 
			
		||||
    resize(1000, 600);
 | 
			
		||||
@ -175,7 +168,8 @@ MicroProfileDialog::MicroProfileDialog(QWidget* parent)
 | 
			
		||||
    layout->addWidget(widget);
 | 
			
		||||
    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);
 | 
			
		||||
    widget->setFocusPolicy(Qt::StrongFocus);
 | 
			
		||||
    widget->setFocus();
 | 
			
		||||
@ -207,7 +201,6 @@ void MicroProfileDialog::hideEvent(QHideEvent* ev) {
 | 
			
		||||
    QWidget::hideEvent(ev);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#if MICROPROFILE_ENABLED
 | 
			
		||||
 | 
			
		||||
/// 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);
 | 
			
		||||
    QBrush brush = color;
 | 
			
		||||
    if (type == MicroProfileBoxTypeBar) {
 | 
			
		||||
@ -326,7 +320,7 @@ void MicroProfileDrawLine2D(u32 vertices_length, float* vertices, u32 hex_color)
 | 
			
		||||
    static std::vector<QPointF> point_buf;
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
@ -7,21 +7,20 @@
 | 
			
		||||
#include <QAbstractItemModel>
 | 
			
		||||
#include <QDockWidget>
 | 
			
		||||
#include <QTimer>
 | 
			
		||||
 | 
			
		||||
#include "ui_profiler.h"
 | 
			
		||||
 | 
			
		||||
#include "common/microprofile.h"
 | 
			
		||||
#include "common/profiler_reporting.h"
 | 
			
		||||
#include "ui_profiler.h"
 | 
			
		||||
 | 
			
		||||
class ProfilerModel : public QAbstractItemModel
 | 
			
		||||
{
 | 
			
		||||
class ProfilerModel : public QAbstractItemModel {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    ProfilerModel(QObject* parent);
 | 
			
		||||
 | 
			
		||||
    QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
 | 
			
		||||
    QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override;
 | 
			
		||||
    QVariant headerData(int section, Qt::Orientation orientation,
 | 
			
		||||
                        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;
 | 
			
		||||
    int columnCount(const QModelIndex& parent = QModelIndex()) const override;
 | 
			
		||||
    int rowCount(const QModelIndex& parent = QModelIndex()) const override;
 | 
			
		||||
@ -34,8 +33,7 @@ private:
 | 
			
		||||
    Common::Profiling::AggregatedFrameResult results;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ProfilerWidget : public QDockWidget
 | 
			
		||||
{
 | 
			
		||||
class ProfilerWidget : public QDockWidget {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
@ -51,7 +49,6 @@ private:
 | 
			
		||||
    QTimer update_timer;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MicroProfileDialog : public QWidget {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -4,12 +4,9 @@
 | 
			
		||||
 | 
			
		||||
#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...
 | 
			
		||||
    //setData(QByteArray((const char*)Mem_RAM,sizeof(Mem_RAM)/8));
 | 
			
		||||
    // setData(QByteArray((const char*)Mem_RAM,sizeof(Mem_RAM)/8));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -4,8 +4,7 @@
 | 
			
		||||
 | 
			
		||||
#include "qhexedit.h"
 | 
			
		||||
 | 
			
		||||
class GRamView : public QHexEdit
 | 
			
		||||
{
 | 
			
		||||
class GRamView : public QHexEdit {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
 | 
			
		||||
@ -3,12 +3,10 @@
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include <QTreeWidgetItem>
 | 
			
		||||
 | 
			
		||||
#include "citra_qt/debugger/registers.h"
 | 
			
		||||
#include "citra_qt/util/util.h"
 | 
			
		||||
 | 
			
		||||
#include "core/core.h"
 | 
			
		||||
#include "core/arm/arm_interface.h"
 | 
			
		||||
#include "core/core.h"
 | 
			
		||||
 | 
			
		||||
RegistersWidget::RegistersWidget(QWidget* parent) : QDockWidget(parent) {
 | 
			
		||||
    cpu_regs_ui.setupUi(this);
 | 
			
		||||
@ -16,7 +14,8 @@ RegistersWidget::RegistersWidget(QWidget* parent) : QDockWidget(parent) {
 | 
			
		||||
    tree = cpu_regs_ui.treeWidget;
 | 
			
		||||
    tree->addTopLevelItem(core_registers = new QTreeWidgetItem(QStringList(tr("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")));
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < 16; ++i) {
 | 
			
		||||
@ -63,17 +62,18 @@ void RegistersWidget::OnDebugModeEntered() {
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    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)
 | 
			
		||||
        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();
 | 
			
		||||
    UpdateVFPSystemRegisterValues();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RegistersWidget::OnDebugModeLeft() {
 | 
			
		||||
}
 | 
			
		||||
void RegistersWidget::OnDebugModeLeft() {}
 | 
			
		||||
 | 
			
		||||
void RegistersWidget::OnEmulationStarting(EmuThread* emu_thread) {
 | 
			
		||||
    setEnabled(true);
 | 
			
		||||
@ -130,21 +130,24 @@ void RegistersWidget::UpdateCPSRValues() {
 | 
			
		||||
    const u32 cpsr_val = Core::g_app_core->GetCPSR();
 | 
			
		||||
 | 
			
		||||
    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(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(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(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(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(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(11)->setText(1, QString::number((cpsr_val >> 28) & 1));   // V - Overflow
 | 
			
		||||
    cpsr->child(12)->setText(1, QString::number((cpsr_val >> 29) & 1));   // C - Carry/Borrow/Extend
 | 
			
		||||
    cpsr->child(13)->setText(1, QString::number((cpsr_val >> 30) & 1));   // Z - Zero
 | 
			
		||||
    cpsr->child(14)->setText(1, QString::number((cpsr_val >> 31) & 1));   // N - Negative/Less than
 | 
			
		||||
    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(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(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(6)->setText(1,
 | 
			
		||||
                            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(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(11)->setText(1, QString::number((cpsr_val >> 28) & 1));  // V - Overflow
 | 
			
		||||
    cpsr->child(12)->setText(1, QString::number((cpsr_val >> 29) & 1));  // C - Carry/Borrow/Extend
 | 
			
		||||
    cpsr->child(13)->setText(1, QString::number((cpsr_val >> 30) & 1));  // Z - Zero
 | 
			
		||||
    cpsr->child(14)->setText(1, QString::number((cpsr_val >> 31) & 1));  // N - Negative/Less than
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RegistersWidget::CreateVFPSystemRegisterChildren() {
 | 
			
		||||
@ -188,9 +191,9 @@ void RegistersWidget::CreateVFPSystemRegisterChildren() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RegistersWidget::UpdateVFPSystemRegisterValues() {
 | 
			
		||||
    const u32 fpscr_val   = Core::g_app_core->GetVFPSystemReg(VFP_FPSCR);
 | 
			
		||||
    const u32 fpexc_val   = Core::g_app_core->GetVFPSystemReg(VFP_FPEXC);
 | 
			
		||||
    const u32 fpinst_val  = Core::g_app_core->GetVFPSystemReg(VFP_FPINST);
 | 
			
		||||
    const u32 fpscr_val = Core::g_app_core->GetVFPSystemReg(VFP_FPSCR);
 | 
			
		||||
    const u32 fpexc_val = Core::g_app_core->GetVFPSystemReg(VFP_FPEXC);
 | 
			
		||||
    const u32 fpinst_val = Core::g_app_core->GetVFPSystemReg(VFP_FPINST);
 | 
			
		||||
    const u32 fpinst2_val = Core::g_app_core->GetVFPSystemReg(VFP_FPINST2);
 | 
			
		||||
 | 
			
		||||
    QTreeWidgetItem* const fpscr = vfp_system_registers->child(0);
 | 
			
		||||
@ -228,6 +231,8 @@ void RegistersWidget::UpdateVFPSystemRegisterValues() {
 | 
			
		||||
    fpexc->child(6)->setText(1, QString::number((fpexc_val >> 30) & 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(3)->setText(1, QString("0x%1").arg(fpinst2_val, 8, 16, QLatin1Char('0')));
 | 
			
		||||
    vfp_system_registers->child(2)->setText(
 | 
			
		||||
        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')));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -2,16 +2,14 @@
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include "ui_registers.h"
 | 
			
		||||
 | 
			
		||||
#include <QDockWidget>
 | 
			
		||||
#include "ui_registers.h"
 | 
			
		||||
 | 
			
		||||
class QTreeWidget;
 | 
			
		||||
class QTreeWidgetItem;
 | 
			
		||||
class EmuThread;
 | 
			
		||||
 | 
			
		||||
class RegistersWidget : public QDockWidget
 | 
			
		||||
{
 | 
			
		||||
class RegistersWidget : public QDockWidget {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
 | 
			
		||||
@ -5,19 +5,15 @@
 | 
			
		||||
#include <QHeaderView>
 | 
			
		||||
#include <QThreadPool>
 | 
			
		||||
#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_p.h"
 | 
			
		||||
#include "ui_settings.h"
 | 
			
		||||
 | 
			
		||||
#include "core/loader/loader.h"
 | 
			
		||||
 | 
			
		||||
#include "common/common_paths.h"
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
#include "common/string_util.h"
 | 
			
		||||
 | 
			
		||||
GameList::GameList(QWidget* parent)
 | 
			
		||||
{
 | 
			
		||||
GameList::GameList(QWidget* parent) {
 | 
			
		||||
    QVBoxLayout* layout = new QVBoxLayout;
 | 
			
		||||
 | 
			
		||||
    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_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.
 | 
			
		||||
    qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>");
 | 
			
		||||
 | 
			
		||||
@ -48,18 +46,15 @@ GameList::GameList(QWidget* parent)
 | 
			
		||||
    setLayout(layout);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GameList::~GameList()
 | 
			
		||||
{
 | 
			
		||||
GameList::~GameList() {
 | 
			
		||||
    emit ShouldCancelWorker();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GameList::AddEntry(QList<QStandardItem*> entry_items)
 | 
			
		||||
{
 | 
			
		||||
void GameList::AddEntry(QList<QStandardItem*> 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.
 | 
			
		||||
    int row = item_model->itemFromIndex(item)->row();
 | 
			
		||||
    QStandardItem* child_file = item_model->invisibleRootItem()->child(row, COLUMN_NAME);
 | 
			
		||||
@ -73,14 +68,13 @@ void GameList::ValidateEntry(const QModelIndex& item)
 | 
			
		||||
    emit GameChosen(file_path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GameList::DonePopulating()
 | 
			
		||||
{
 | 
			
		||||
void GameList::DonePopulating() {
 | 
			
		||||
    tree_view->setEnabled(true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GameList::PopulateAsync(const QString& dir_path, bool deep_scan)
 | 
			
		||||
{
 | 
			
		||||
    if (!FileUtil::Exists(dir_path.toStdString()) || !FileUtil::IsDirectory(dir_path.toStdString())) {
 | 
			
		||||
void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) {
 | 
			
		||||
    if (!FileUtil::Exists(dir_path.toStdString()) ||
 | 
			
		||||
        !FileUtil::IsDirectory(dir_path.toStdString())) {
 | 
			
		||||
        LOG_ERROR(Frontend, "Could not find game list folder at %s", dir_path.toLocal8Bit().data());
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
@ -92,22 +86,22 @@ void GameList::PopulateAsync(const QString& dir_path, bool deep_scan)
 | 
			
		||||
    emit ShouldCancelWorker();
 | 
			
		||||
    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);
 | 
			
		||||
    // 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);
 | 
			
		||||
 | 
			
		||||
    QThreadPool::globalInstance()->start(worker);
 | 
			
		||||
    current_worker = std::move(worker);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GameList::SaveInterfaceLayout()
 | 
			
		||||
{
 | 
			
		||||
void GameList::SaveInterfaceLayout() {
 | 
			
		||||
    UISettings::values.gamelist_header_state = tree_view->header()->saveState();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GameList::LoadInterfaceLayout()
 | 
			
		||||
{
 | 
			
		||||
void GameList::LoadInterfaceLayout() {
 | 
			
		||||
    auto header = tree_view->header();
 | 
			
		||||
    if (!header->restoreState(UISettings::values.gamelist_header_state)) {
 | 
			
		||||
        // 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());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion)
 | 
			
		||||
{
 | 
			
		||||
    const auto callback = [this, recursion](unsigned* num_entries_out,
 | 
			
		||||
                                            const std::string& directory,
 | 
			
		||||
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 std::string& virtual_name) -> bool {
 | 
			
		||||
        std::string physical_name = directory + DIR_SEP + virtual_name;
 | 
			
		||||
 | 
			
		||||
@ -138,7 +130,8 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
 | 
			
		||||
 | 
			
		||||
            emit EntryReady({
 | 
			
		||||
                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)),
 | 
			
		||||
            });
 | 
			
		||||
        } else if (recursion > 0) {
 | 
			
		||||
@ -151,15 +144,13 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
 | 
			
		||||
    FileUtil::ForeachDirectoryEntry(nullptr, dir_path, callback);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GameListWorker::run()
 | 
			
		||||
{
 | 
			
		||||
void GameListWorker::run() {
 | 
			
		||||
    stop_processing = false;
 | 
			
		||||
    AddFstEntriesToGameList(dir_path.toStdString(), deep_scan ? 256 : 0);
 | 
			
		||||
    emit Finished();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GameListWorker::Cancel()
 | 
			
		||||
{
 | 
			
		||||
void GameListWorker::Cancel() {
 | 
			
		||||
    disconnect(this, 0, 0, 0);
 | 
			
		||||
    stop_processing = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -14,7 +14,6 @@
 | 
			
		||||
 | 
			
		||||
class GameListWorker;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class GameList : public QWidget {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -5,18 +5,14 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <atomic>
 | 
			
		||||
 | 
			
		||||
#include <QImage>
 | 
			
		||||
#include <QRunnable>
 | 
			
		||||
#include <QStandardItem>
 | 
			
		||||
#include <QString>
 | 
			
		||||
 | 
			
		||||
#include "citra_qt/util/util.h"
 | 
			
		||||
#include "common/string_util.h"
 | 
			
		||||
#include "common/color.h"
 | 
			
		||||
 | 
			
		||||
#include "common/string_util.h"
 | 
			
		||||
#include "core/loader/smdh.h"
 | 
			
		||||
 | 
			
		||||
#include "video_core/utils.h"
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@ -51,19 +47,19 @@ static QPixmap GetDefaultIcon(bool large) {
 | 
			
		||||
 * @param language title language
 | 
			
		||||
 * @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());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class GameListItem : public QStandardItem {
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    GameListItem(): QStandardItem() {}
 | 
			
		||||
    GameListItem(const QString& string): QStandardItem(string) {}
 | 
			
		||||
    GameListItem() : QStandardItem() {}
 | 
			
		||||
    GameListItem(const QString& string) : QStandardItem(string) {}
 | 
			
		||||
    virtual ~GameListItem() override {}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A specialization of GameListItem for path values.
 | 
			
		||||
 * 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 TitleRole = Qt::UserRole + 2;
 | 
			
		||||
 | 
			
		||||
    GameListItemPath(): GameListItem() {}
 | 
			
		||||
    GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data): GameListItem()
 | 
			
		||||
    {
 | 
			
		||||
    GameListItemPath() : GameListItem() {}
 | 
			
		||||
    GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data) : GameListItem() {
 | 
			
		||||
        setData(game_path, FullPathRole);
 | 
			
		||||
 | 
			
		||||
        if (!Loader::IsValidSMDH(smdh_data)) {
 | 
			
		||||
@ -94,13 +89,15 @@ public:
 | 
			
		||||
        setData(GetQPixmapFromSMDH(smdh, true), Qt::DecorationRole);
 | 
			
		||||
 | 
			
		||||
        // 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 {
 | 
			
		||||
        if (role == Qt::DisplayRole) {
 | 
			
		||||
            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();
 | 
			
		||||
            return QString::fromStdString(filename) + (title.isEmpty() ? "" : "\n    " + title);
 | 
			
		||||
        } else {
 | 
			
		||||
@ -109,7 +106,6 @@ public:
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A specialization of GameListItem for size values.
 | 
			
		||||
 * This class ensures that for every numerical size value it holds (in bytes), a correct
 | 
			
		||||
@ -120,14 +116,12 @@ class GameListItemSize : public GameListItem {
 | 
			
		||||
public:
 | 
			
		||||
    static const int SizeRole = Qt::UserRole + 1;
 | 
			
		||||
 | 
			
		||||
    GameListItemSize(): GameListItem() {}
 | 
			
		||||
    GameListItemSize(const qulonglong size_bytes): GameListItem()
 | 
			
		||||
    {
 | 
			
		||||
    GameListItemSize() : GameListItem() {}
 | 
			
		||||
    GameListItemSize(const qulonglong size_bytes) : GameListItem() {
 | 
			
		||||
        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
 | 
			
		||||
        // representations of the data are always accurate and in the correct format.
 | 
			
		||||
        if (role == SizeRole) {
 | 
			
		||||
@ -141,15 +135,14 @@ public:
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 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();
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Asynchronous worker object for populating the game list.
 | 
			
		||||
 * Communicates with other threads through Qt's signal/slot system.
 | 
			
		||||
@ -158,8 +151,8 @@ class GameListWorker : public QObject, public QRunnable {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    GameListWorker(QString dir_path, bool deep_scan):
 | 
			
		||||
            QObject(), QRunnable(), dir_path(dir_path), deep_scan(deep_scan) {}
 | 
			
		||||
    GameListWorker(QString dir_path, bool deep_scan)
 | 
			
		||||
        : QObject(), QRunnable(), dir_path(dir_path), deep_scan(deep_scan) {}
 | 
			
		||||
 | 
			
		||||
public slots:
 | 
			
		||||
    /// Starts the processing of directory tree information.
 | 
			
		||||
 | 
			
		||||
@ -3,16 +3,13 @@
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include <map>
 | 
			
		||||
 | 
			
		||||
#include <QtGlobal>
 | 
			
		||||
#include <QKeySequence>
 | 
			
		||||
#include <QShortcut>
 | 
			
		||||
 | 
			
		||||
#include <QtGlobal>
 | 
			
		||||
#include "citra_qt/hotkeys.h"
 | 
			
		||||
#include "citra_qt/ui_settings.h"
 | 
			
		||||
 | 
			
		||||
struct Hotkey
 | 
			
		||||
{
 | 
			
		||||
struct Hotkey {
 | 
			
		||||
    Hotkey() : shortcut(nullptr), context(Qt::WindowShortcut) {}
 | 
			
		||||
 | 
			
		||||
    QKeySequence keyseq;
 | 
			
		||||
@ -25,33 +22,28 @@ typedef std::map<QString, HotkeyMap> HotkeyGroupMap;
 | 
			
		||||
 | 
			
		||||
HotkeyGroupMap hotkey_groups;
 | 
			
		||||
 | 
			
		||||
void SaveHotkeys()
 | 
			
		||||
{
 | 
			
		||||
void SaveHotkeys() {
 | 
			
		||||
    UISettings::values.shortcuts.clear();
 | 
			
		||||
    for (auto group : hotkey_groups)
 | 
			
		||||
    {
 | 
			
		||||
        for (auto hotkey : group.second)
 | 
			
		||||
        {
 | 
			
		||||
    for (auto group : hotkey_groups) {
 | 
			
		||||
        for (auto hotkey : group.second) {
 | 
			
		||||
            UISettings::values.shortcuts.emplace_back(
 | 
			
		||||
                        UISettings::Shortcut(group.first + "/" + hotkey.first,
 | 
			
		||||
                                             UISettings::ContextualShortcut(hotkey.second.keyseq.toString(),
 | 
			
		||||
                                                                           hotkey.second.context)));
 | 
			
		||||
                UISettings::Shortcut(group.first + "/" + hotkey.first,
 | 
			
		||||
                                     UISettings::ContextualShortcut(hotkey.second.keyseq.toString(),
 | 
			
		||||
                                                                    hotkey.second.context)));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void LoadHotkeys()
 | 
			
		||||
{
 | 
			
		||||
    // Make sure NOT to use a reference here because it would become invalid once we call beginGroup()
 | 
			
		||||
    for (auto shortcut : UISettings::values.shortcuts)
 | 
			
		||||
    {
 | 
			
		||||
void LoadHotkeys() {
 | 
			
		||||
    // Make sure NOT to use a reference here because it would become invalid once we call
 | 
			
		||||
    // beginGroup()
 | 
			
		||||
    for (auto shortcut : UISettings::values.shortcuts) {
 | 
			
		||||
        QStringList cat = shortcut.first.split("/");
 | 
			
		||||
        Q_ASSERT(cat.size() >= 2);
 | 
			
		||||
 | 
			
		||||
        // RegisterHotkey assigns default keybindings, so use old values as default parameters
 | 
			
		||||
        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.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)
 | 
			
		||||
{
 | 
			
		||||
    if (hotkey_groups[group].find(action) == hotkey_groups[group].end())
 | 
			
		||||
    {
 | 
			
		||||
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()) {
 | 
			
		||||
        hotkey_groups[group][action].keyseq = default_keyseq;
 | 
			
		||||
        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];
 | 
			
		||||
 | 
			
		||||
    if (!hk.shortcut)
 | 
			
		||||
@ -79,16 +69,12 @@ QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widge
 | 
			
		||||
    return hk.shortcut;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
GHotkeysDialog::GHotkeysDialog(QWidget* parent): QWidget(parent)
 | 
			
		||||
{
 | 
			
		||||
GHotkeysDialog::GHotkeysDialog(QWidget* parent) : QWidget(parent) {
 | 
			
		||||
    ui.setupUi(this);
 | 
			
		||||
 | 
			
		||||
    for (auto group : hotkey_groups)
 | 
			
		||||
    {
 | 
			
		||||
    for (auto group : hotkey_groups) {
 | 
			
		||||
        QTreeWidgetItem* toplevel_item = new QTreeWidgetItem(QStringList(group.first));
 | 
			
		||||
        for (auto hotkey : group.second)
 | 
			
		||||
        {
 | 
			
		||||
        for (auto hotkey : group.second) {
 | 
			
		||||
            QStringList columns;
 | 
			
		||||
            columns << hotkey.first << hotkey.second.keyseq.toString();
 | 
			
		||||
            QTreeWidgetItem* item = new QTreeWidgetItem(columns);
 | 
			
		||||
 | 
			
		||||
@ -16,36 +16,42 @@ class QShortcut;
 | 
			
		||||
 *
 | 
			
		||||
 * @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 default_keyseq Default key sequence to assign if the hotkey wasn't present in the settings file before
 | 
			
		||||
 * @param default_context Default context 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
 | 
			
		||||
 * 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
 | 
			
		||||
 */
 | 
			
		||||
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.
 | 
			
		||||
 *
 | 
			
		||||
 * @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);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 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();
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 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();
 | 
			
		||||
 | 
			
		||||
class GHotkeysDialog : public QWidget
 | 
			
		||||
{
 | 
			
		||||
class GHotkeysDialog : public QWidget {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
 | 
			
		||||
@ -5,25 +5,15 @@
 | 
			
		||||
#include <clocale>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <thread>
 | 
			
		||||
 | 
			
		||||
#include <glad/glad.h>
 | 
			
		||||
 | 
			
		||||
#define QT_NO_OPENGL
 | 
			
		||||
#include <QDesktopWidget>
 | 
			
		||||
#include <QtGui>
 | 
			
		||||
#include <QFileDialog>
 | 
			
		||||
#include <QMessageBox>
 | 
			
		||||
#include "qhexedit.h"
 | 
			
		||||
 | 
			
		||||
#include <QtGui>
 | 
			
		||||
#include "citra_qt/bootmanager.h"
 | 
			
		||||
#include "citra_qt/config.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/disassembler.h"
 | 
			
		||||
#include "citra_qt/debugger/graphics.h"
 | 
			
		||||
@ -35,28 +25,29 @@
 | 
			
		||||
#include "citra_qt/debugger/profiler.h"
 | 
			
		||||
#include "citra_qt/debugger/ramview.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/platform.h"
 | 
			
		||||
#include "common/scm_rev.h"
 | 
			
		||||
#include "common/scope_exit.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/core.h"
 | 
			
		||||
#include "core/gdbstub/gdbstub.h"
 | 
			
		||||
#include "core/loader/loader.h"
 | 
			
		||||
 | 
			
		||||
#include "core/settings.h"
 | 
			
		||||
#include "core/system.h"
 | 
			
		||||
#include "qhexedit.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();
 | 
			
		||||
 | 
			
		||||
    ui.setupUi(this);
 | 
			
		||||
@ -91,7 +82,7 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr)
 | 
			
		||||
 | 
			
		||||
    graphicsWidget = new GPUCommandStreamWidget(this);
 | 
			
		||||
    addDockWidget(Qt::RightDockWidgetArea, graphicsWidget);
 | 
			
		||||
    graphicsWidget ->hide();
 | 
			
		||||
    graphicsWidget->hide();
 | 
			
		||||
 | 
			
		||||
    graphicsCommandsWidget = new GPUCommandListWidget(this);
 | 
			
		||||
    addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget);
 | 
			
		||||
@ -110,7 +101,8 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr)
 | 
			
		||||
    graphicsTracingWidget->hide();
 | 
			
		||||
 | 
			
		||||
    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"));
 | 
			
		||||
    debug_menu->addAction(graphicsSurfaceViewerAction);
 | 
			
		||||
@ -167,35 +159,44 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr)
 | 
			
		||||
    UpdateRecentFiles();
 | 
			
		||||
 | 
			
		||||
    // 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_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_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_Pause, SIGNAL(triggered()), this, SLOT(OnPauseGame()));
 | 
			
		||||
    connect(ui.action_Stop, SIGNAL(triggered()), this, SLOT(OnStopGame()));
 | 
			
		||||
    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(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(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(EmulationStarting(EmuThread*)), graphicsTracingWidget, SLOT(OnEmulationStarting(EmuThread*)));
 | 
			
		||||
    connect(this, SIGNAL(EmulationStarting(EmuThread*)), graphicsTracingWidget,
 | 
			
		||||
            SLOT(OnEmulationStarting(EmuThread*)));
 | 
			
		||||
    connect(this, SIGNAL(EmulationStopping()), graphicsTracingWidget, SLOT(OnEmulationStopping()));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // Setup hotkeys
 | 
			
		||||
    RegisterHotkey("Main Window", "Load File", QKeySequence::Open);
 | 
			
		||||
    RegisterHotkey("Main Window", "Start Emulation");
 | 
			
		||||
    LoadHotkeys();
 | 
			
		||||
 | 
			
		||||
    connect(GetHotkey("Main Window", "Load File", this), SIGNAL(activated()), this, SLOT(OnMenuLoadFile()));
 | 
			
		||||
    connect(GetHotkey("Main Window", "Start Emulation", this), SIGNAL(activated()), this, SLOT(OnStartGame()));
 | 
			
		||||
    connect(GetHotkey("Main Window", "Load File", this), SIGNAL(activated()), this,
 | 
			
		||||
            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());
 | 
			
		||||
 | 
			
		||||
    show();
 | 
			
		||||
@ -208,8 +209,7 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GMainWindow::~GMainWindow()
 | 
			
		||||
{
 | 
			
		||||
GMainWindow::~GMainWindow() {
 | 
			
		||||
    // will get automatically deleted otherwise
 | 
			
		||||
    if (render_window->parent() == nullptr)
 | 
			
		||||
        delete render_window;
 | 
			
		||||
@ -217,19 +217,18 @@ GMainWindow::~GMainWindow()
 | 
			
		||||
    Pica::g_debug_context.reset();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GMainWindow::OnDisplayTitleBars(bool show)
 | 
			
		||||
{
 | 
			
		||||
void GMainWindow::OnDisplayTitleBars(bool show) {
 | 
			
		||||
    QList<QDockWidget*> widgets = findChildren<QDockWidget*>();
 | 
			
		||||
 | 
			
		||||
    if (show) {
 | 
			
		||||
        for (QDockWidget* widget: widgets) {
 | 
			
		||||
        for (QDockWidget* widget : widgets) {
 | 
			
		||||
            QWidget* old = widget->titleBarWidget();
 | 
			
		||||
            widget->setTitleBarWidget(nullptr);
 | 
			
		||||
            if (old != nullptr)
 | 
			
		||||
                delete old;
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        for (QDockWidget* widget: widgets) {
 | 
			
		||||
        for (QDockWidget* widget : widgets) {
 | 
			
		||||
            QWidget* old = widget->titleBarWidget();
 | 
			
		||||
            widget->setTitleBarWidget(new QWidget());
 | 
			
		||||
            if (old != nullptr)
 | 
			
		||||
@ -249,7 +248,8 @@ bool GMainWindow::InitializeSystem() {
 | 
			
		||||
    if (!gladLoadGL()) {
 | 
			
		||||
        QMessageBox::critical(this, tr("Error while starting Citra!"),
 | 
			
		||||
                              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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -260,7 +260,8 @@ bool GMainWindow::InitializeSystem() {
 | 
			
		||||
        case System::Result::ErrorInitVideoCore:
 | 
			
		||||
            QMessageBox::critical(this, tr("Error while starting Citra!"),
 | 
			
		||||
                                  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;
 | 
			
		||||
 | 
			
		||||
        default:
 | 
			
		||||
@ -293,8 +294,12 @@ bool GMainWindow::LoadROM(const std::string& filename) {
 | 
			
		||||
            QMessageBox popup_error;
 | 
			
		||||
            popup_error.setTextFormat(Qt::RichText);
 | 
			
		||||
            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/>"
 | 
			
		||||
                                  "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.setText(
 | 
			
		||||
                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.exec();
 | 
			
		||||
            break;
 | 
			
		||||
@ -306,8 +311,7 @@ bool GMainWindow::LoadROM(const std::string& filename) {
 | 
			
		||||
        case Loader::ResultStatus::Error:
 | 
			
		||||
 | 
			
		||||
        default:
 | 
			
		||||
            QMessageBox::critical(this, tr("Error while loading ROM!"),
 | 
			
		||||
                                  tr("Unknown error!"));
 | 
			
		||||
            QMessageBox::critical(this, tr("Error while loading ROM!"), tr("Unknown error!"));
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
@ -332,13 +336,20 @@ void GMainWindow::BootGame(const std::string& filename) {
 | 
			
		||||
    emu_thread->start();
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
    connect(emu_thread.get(), SIGNAL(DebugModeEntered()), disasmWidget, SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection);
 | 
			
		||||
    connect(emu_thread.get(), SIGNAL(DebugModeEntered()), registersWidget, SLOT(OnDebugModeEntered()), 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);
 | 
			
		||||
    // BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views
 | 
			
		||||
    // before the CPU continues
 | 
			
		||||
    connect(emu_thread.get(), SIGNAL(DebugModeEntered()), disasmWidget, SLOT(OnDebugModeEntered()),
 | 
			
		||||
            Qt::BlockingQueuedConnection);
 | 
			
		||||
    connect(emu_thread.get(), SIGNAL(DebugModeEntered()), registersWidget,
 | 
			
		||||
            SLOT(OnDebugModeEntered()), 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
 | 
			
		||||
    registersWidget->OnDebugModeEntered();
 | 
			
		||||
@ -393,10 +404,12 @@ void GMainWindow::StoreRecentFile(const std::string& filename) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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++) {
 | 
			
		||||
        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]->setData(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() {
 | 
			
		||||
    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()) {
 | 
			
		||||
        UISettings::values.roms_path = QFileInfo(filename).path();
 | 
			
		||||
 | 
			
		||||
@ -429,7 +444,8 @@ void GMainWindow::OnMenuLoadFile() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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()) {
 | 
			
		||||
        UISettings::values.symbols_path = QFileInfo(filename).path();
 | 
			
		||||
 | 
			
		||||
@ -455,7 +471,8 @@ void GMainWindow::OnMenuRecentFile() {
 | 
			
		||||
        BootGame(filename.toStdString());
 | 
			
		||||
    } else {
 | 
			
		||||
        // 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);
 | 
			
		||||
        UpdateRecentFiles();
 | 
			
		||||
@ -512,8 +529,7 @@ void GMainWindow::ToggleWindowMode() {
 | 
			
		||||
void GMainWindow::OnConfigure() {
 | 
			
		||||
    ConfigureDialog configureDialog(this);
 | 
			
		||||
    auto result = configureDialog.exec();
 | 
			
		||||
    if (result == QDialog::Accepted)
 | 
			
		||||
    {
 | 
			
		||||
    if (result == QDialog::Accepted) {
 | 
			
		||||
        configureDialog.applyConfiguration();
 | 
			
		||||
        render_window->ReloadSetKeymaps();
 | 
			
		||||
        config->Save();
 | 
			
		||||
@ -531,9 +547,9 @@ bool GMainWindow::ConfirmClose() {
 | 
			
		||||
    if (emu_thread == nullptr || !UISettings::values.confirm_before_closing)
 | 
			
		||||
        return true;
 | 
			
		||||
 | 
			
		||||
    auto answer = QMessageBox::question(this, tr("Citra"),
 | 
			
		||||
                                        tr("Are you sure you want to close Citra?"),
 | 
			
		||||
                                        QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
 | 
			
		||||
    auto answer =
 | 
			
		||||
        QMessageBox::question(this, tr("Citra"), tr("Are you sure you want to close Citra?"),
 | 
			
		||||
                              QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
 | 
			
		||||
    return answer != QMessageBox::No;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -575,9 +591,7 @@ int main(int argc, char* argv[]) {
 | 
			
		||||
    Log::SetFilter(&log_filter);
 | 
			
		||||
 | 
			
		||||
    MicroProfileOnThreadCreate("Frontend");
 | 
			
		||||
    SCOPE_EXIT({
 | 
			
		||||
        MicroProfileShutdown();
 | 
			
		||||
    });
 | 
			
		||||
    SCOPE_EXIT({ MicroProfileShutdown(); });
 | 
			
		||||
 | 
			
		||||
    // Init settings params
 | 
			
		||||
    QCoreApplication::setOrganizationName("Citra team");
 | 
			
		||||
@ -586,7 +600,8 @@ int main(int argc, char* argv[]) {
 | 
			
		||||
    QApplication::setAttribute(Qt::AA_X11InitThreads);
 | 
			
		||||
    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");
 | 
			
		||||
 | 
			
		||||
    GMainWindow main_window;
 | 
			
		||||
 | 
			
		||||
@ -7,7 +7,6 @@
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <QMainWindow>
 | 
			
		||||
 | 
			
		||||
#include "ui_main.h"
 | 
			
		||||
 | 
			
		||||
class Config;
 | 
			
		||||
@ -23,11 +22,11 @@ class CallstackWidget;
 | 
			
		||||
class GPUCommandStreamWidget;
 | 
			
		||||
class GPUCommandListWidget;
 | 
			
		||||
 | 
			
		||||
class GMainWindow : public QMainWindow
 | 
			
		||||
{
 | 
			
		||||
class GMainWindow : public QMainWindow {
 | 
			
		||||
    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!
 | 
			
		||||
    enum {
 | 
			
		||||
 | 
			
		||||
@ -7,5 +7,4 @@
 | 
			
		||||
namespace UISettings {
 | 
			
		||||
 | 
			
		||||
Values values = {};
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -4,15 +4,14 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <QByteArray>
 | 
			
		||||
#include <QStringList>
 | 
			
		||||
#include <QString>
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <QByteArray>
 | 
			
		||||
#include <QString>
 | 
			
		||||
#include <QStringList>
 | 
			
		||||
 | 
			
		||||
namespace UISettings {
 | 
			
		||||
 | 
			
		||||
using ContextualShortcut = std::pair<QString, int> ;
 | 
			
		||||
using ContextualShortcut = std::pair<QString, int>;
 | 
			
		||||
using Shortcut = std::pair<QString, ContextualShortcut>;
 | 
			
		||||
 | 
			
		||||
struct Values {
 | 
			
		||||
@ -43,5 +42,4 @@ struct Values {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern Values values;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,6 @@
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Copyright 2014 Tony Wasserka
 | 
			
		||||
// All rights reserved.
 | 
			
		||||
//
 | 
			
		||||
@ -32,12 +31,11 @@
 | 
			
		||||
#include <cstdlib>
 | 
			
		||||
#include <QLineEdit>
 | 
			
		||||
#include <QRegExpValidator>
 | 
			
		||||
 | 
			
		||||
#include "citra_qt/util/spinbox.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.
 | 
			
		||||
    //       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.
 | 
			
		||||
@ -46,8 +44,7 @@ CSpinBox::CSpinBox(QWidget* parent) : QAbstractSpinBox(parent), min_value(-100),
 | 
			
		||||
    UpdateText();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CSpinBox::SetValue(qint64 val)
 | 
			
		||||
{
 | 
			
		||||
void CSpinBox::SetValue(qint64 val) {
 | 
			
		||||
    auto old_value = 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;
 | 
			
		||||
    max_value = max;
 | 
			
		||||
 | 
			
		||||
@ -66,8 +62,7 @@ void CSpinBox::SetRange(qint64 min, qint64 max)
 | 
			
		||||
    UpdateText();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CSpinBox::stepBy(int steps)
 | 
			
		||||
{
 | 
			
		||||
void CSpinBox::stepBy(int steps) {
 | 
			
		||||
    auto new_value = value;
 | 
			
		||||
    // Scale number of steps by the currently selected digit
 | 
			
		||||
    // TODO: Move this code elsewhere and enable it.
 | 
			
		||||
@ -93,8 +88,7 @@ void CSpinBox::stepBy(int steps)
 | 
			
		||||
    UpdateText();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QAbstractSpinBox::StepEnabled CSpinBox::stepEnabled() const
 | 
			
		||||
{
 | 
			
		||||
QAbstractSpinBox::StepEnabled CSpinBox::stepEnabled() const {
 | 
			
		||||
    StepEnabled ret = StepNone;
 | 
			
		||||
 | 
			
		||||
    if (value > min_value)
 | 
			
		||||
@ -106,29 +100,25 @@ QAbstractSpinBox::StepEnabled CSpinBox::stepEnabled() const
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CSpinBox::SetBase(int base)
 | 
			
		||||
{
 | 
			
		||||
void CSpinBox::SetBase(int base) {
 | 
			
		||||
    this->base = base;
 | 
			
		||||
 | 
			
		||||
    UpdateText();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CSpinBox::SetNumDigits(int num_digits)
 | 
			
		||||
{
 | 
			
		||||
void CSpinBox::SetNumDigits(int num_digits) {
 | 
			
		||||
    this->num_digits = num_digits;
 | 
			
		||||
 | 
			
		||||
    UpdateText();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CSpinBox::SetPrefix(const QString& prefix)
 | 
			
		||||
{
 | 
			
		||||
void CSpinBox::SetPrefix(const QString& prefix) {
 | 
			
		||||
    this->prefix = prefix;
 | 
			
		||||
 | 
			
		||||
    UpdateText();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CSpinBox::SetSuffix(const QString& suffix)
 | 
			
		||||
{
 | 
			
		||||
void CSpinBox::SetSuffix(const QString& suffix) {
 | 
			
		||||
    this->suffix = suffix;
 | 
			
		||||
 | 
			
		||||
    UpdateText();
 | 
			
		||||
@ -161,8 +151,7 @@ static QString StringToInputMask(const QString& input) {
 | 
			
		||||
    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
 | 
			
		||||
    // input mask.
 | 
			
		||||
    QString mask;
 | 
			
		||||
@ -179,10 +168,9 @@ void CSpinBox::UpdateText()
 | 
			
		||||
        // The greatest signed 64-bit number has 19 decimal digits.
 | 
			
		||||
        // TODO: Could probably make this more generic with some logarithms.
 | 
			
		||||
        // For reference, unsigned 64-bit can have up to 20 decimal digits.
 | 
			
		||||
        int digits = (num_digits != 0) ? num_digits
 | 
			
		||||
                     : (base == 16) ? 16
 | 
			
		||||
                     : (base == 10) ? 19
 | 
			
		||||
                     : 0xFF; // fallback case...
 | 
			
		||||
        int digits = (num_digits != 0)
 | 
			
		||||
                         ? num_digits
 | 
			
		||||
                         : (base == 16) ? 16 : (base == 10) ? 19 : 0xFF; // fallback case...
 | 
			
		||||
 | 
			
		||||
        // Match num_digits digits
 | 
			
		||||
        // Digits irrelevant to the chosen number base are filtered in the validator
 | 
			
		||||
@ -203,29 +191,24 @@ void CSpinBox::UpdateText()
 | 
			
		||||
    lineEdit()->setCursorPosition(cursor_position);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QString CSpinBox::TextFromValue()
 | 
			
		||||
{
 | 
			
		||||
    return prefix
 | 
			
		||||
           + QString(HasSign() ? ((value < 0) ? "-" : "+") : "")
 | 
			
		||||
           + QString("%1").arg(std::abs(value), num_digits, base, QLatin1Char('0')).toUpper()
 | 
			
		||||
           + suffix;
 | 
			
		||||
QString CSpinBox::TextFromValue() {
 | 
			
		||||
    return prefix + QString(HasSign() ? ((value < 0) ? "-" : "+") : "") +
 | 
			
		||||
           QString("%1").arg(std::abs(value), num_digits, base, QLatin1Char('0')).toUpper() +
 | 
			
		||||
           suffix;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
qint64 CSpinBox::ValueFromText()
 | 
			
		||||
{
 | 
			
		||||
qint64 CSpinBox::ValueFromText() {
 | 
			
		||||
    unsigned strpos = prefix.length();
 | 
			
		||||
 | 
			
		||||
    QString num_string = text().mid(strpos, text().length() - strpos - suffix.length());
 | 
			
		||||
    return num_string.toLongLong(nullptr, base);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool CSpinBox::HasSign() const
 | 
			
		||||
{
 | 
			
		||||
bool CSpinBox::HasSign() const {
 | 
			
		||||
    return base == 10 && min_value < 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CSpinBox::OnEditingFinished()
 | 
			
		||||
{
 | 
			
		||||
void CSpinBox::OnEditingFinished() {
 | 
			
		||||
    // Only update for valid input
 | 
			
		||||
    QString input = lineEdit()->text();
 | 
			
		||||
    int pos = 0;
 | 
			
		||||
@ -233,8 +216,7 @@ void CSpinBox::OnEditingFinished()
 | 
			
		||||
        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)
 | 
			
		||||
        return QValidator::Invalid;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,6 @@
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Copyright 2014 Tony Wasserka
 | 
			
		||||
// All rights reserved.
 | 
			
		||||
//
 | 
			
		||||
@ -29,7 +28,6 @@
 | 
			
		||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | 
			
		||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <QAbstractSpinBox>
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,6 @@
 | 
			
		||||
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <cmath>
 | 
			
		||||
 | 
			
		||||
#include "citra_qt/util/util.h"
 | 
			
		||||
 | 
			
		||||
QFont GetMonospaceFont() {
 | 
			
		||||
@ -16,10 +15,12 @@ QFont GetMonospaceFont() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
        return "0";
 | 
			
		||||
    int digit_groups = std::min<int>(static_cast<int>(std::log10(size) / std::log10(1024)), static_cast<int>(units.size()));
 | 
			
		||||
    return QString("%L1 %2").arg(size / std::pow(1024, digit_groups), 0, 'f', 1)
 | 
			
		||||
                            .arg(units[digit_groups]);
 | 
			
		||||
    int digit_groups = std::min<int>(static_cast<int>(std::log10(size) / std::log10(1024)),
 | 
			
		||||
                                     static_cast<int>(units.size()));
 | 
			
		||||
    return QString("%L1 %2")
 | 
			
		||||
        .arg(size / std::pow(1024, digit_groups), 0, 'f', 1)
 | 
			
		||||
        .arg(units[digit_groups]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,6 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <cstdlib>
 | 
			
		||||
 | 
			
		||||
#include "common/common_funcs.h"
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
 | 
			
		||||
@ -18,25 +17,29 @@
 | 
			
		||||
// enough for our purposes.
 | 
			
		||||
template <typename Fn>
 | 
			
		||||
#if defined(_MSC_VER)
 | 
			
		||||
    __declspec(noinline, noreturn)
 | 
			
		||||
__declspec(noinline, noreturn)
 | 
			
		||||
#elif defined(__GNUC__)
 | 
			
		||||
    __attribute__((noinline, noreturn, cold))
 | 
			
		||||
#endif
 | 
			
		||||
static void assert_noinline_call(const Fn& fn) {
 | 
			
		||||
    static void assert_noinline_call(const Fn& fn) {
 | 
			
		||||
    fn();
 | 
			
		||||
    Crash();
 | 
			
		||||
    exit(1); // Keeps GCC's mouth shut about this actually returning
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define ASSERT(_a_) \
 | 
			
		||||
    do if (!(_a_)) { assert_noinline_call([] { \
 | 
			
		||||
        LOG_CRITICAL(Debug, "Assertion Failed!"); \
 | 
			
		||||
    }); } while (0)
 | 
			
		||||
#define ASSERT(_a_)                                                                                \
 | 
			
		||||
    do                                                                                             \
 | 
			
		||||
        if (!(_a_)) {                                                                              \
 | 
			
		||||
            assert_noinline_call([] { LOG_CRITICAL(Debug, "Assertion Failed!"); });                \
 | 
			
		||||
        }                                                                                          \
 | 
			
		||||
    while (0)
 | 
			
		||||
 | 
			
		||||
#define ASSERT_MSG(_a_, ...) \
 | 
			
		||||
    do if (!(_a_)) { assert_noinline_call([&] { \
 | 
			
		||||
        LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); \
 | 
			
		||||
    }); } while (0)
 | 
			
		||||
#define ASSERT_MSG(_a_, ...)                                                                       \
 | 
			
		||||
    do                                                                                             \
 | 
			
		||||
        if (!(_a_)) {                                                                              \
 | 
			
		||||
            assert_noinline_call([&] { LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); }); \
 | 
			
		||||
        }                                                                                          \
 | 
			
		||||
    while (0)
 | 
			
		||||
 | 
			
		||||
#define UNREACHABLE() ASSERT_MSG(false, "Unreachable code!")
 | 
			
		||||
#define UNREACHABLE_MSG(...) ASSERT_MSG(false, __VA_ARGS__)
 | 
			
		||||
@ -50,4 +53,4 @@ static void assert_noinline_call(const Fn& fn) {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define UNIMPLEMENTED() DEBUG_ASSERT_MSG(false, "Unimplemented code!")
 | 
			
		||||
#define UNIMPLEMENTED_MSG(_a_, ...) ASSERT_MSG(false, _a_, __VA_ARGS__)
 | 
			
		||||
#define UNIMPLEMENTED_MSG(_a_, ...) ASSERT_MSG(false, _a_, __VA_ARGS__)
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,6 @@
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Copyright 2014 Tony Wasserka
 | 
			
		||||
// All rights reserved.
 | 
			
		||||
//
 | 
			
		||||
@ -29,13 +28,11 @@
 | 
			
		||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | 
			
		||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
#include <limits>
 | 
			
		||||
#include <type_traits>
 | 
			
		||||
 | 
			
		||||
#include "common/common_funcs.h"
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@ -111,9 +108,8 @@
 | 
			
		||||
 * symptoms.
 | 
			
		||||
 */
 | 
			
		||||
#pragma pack(1)
 | 
			
		||||
template<std::size_t position, std::size_t bits, typename T>
 | 
			
		||||
struct BitField
 | 
			
		||||
{
 | 
			
		||||
template <std::size_t position, std::size_t bits, typename T>
 | 
			
		||||
struct BitField {
 | 
			
		||||
private:
 | 
			
		||||
    // We hide the copy assigment operator here, because the default copy
 | 
			
		||||
    // assignment would copy the full storage value, rather than just the bits
 | 
			
		||||
@ -141,13 +137,10 @@ public:
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    FORCE_INLINE T Value() const {
 | 
			
		||||
        if (std::numeric_limits<T>::is_signed)
 | 
			
		||||
        {
 | 
			
		||||
            std::size_t shift = 8 * sizeof(T)-bits;
 | 
			
		||||
        if (std::numeric_limits<T>::is_signed) {
 | 
			
		||||
            std::size_t shift = 8 * sizeof(T) - bits;
 | 
			
		||||
            return (T)((storage << (shift - position)) >> shift);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
        } else {
 | 
			
		||||
            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
 | 
			
		||||
    // former case to workaround compile errors which arise when using
 | 
			
		||||
    // std::underlying_type<T>::type directly.
 | 
			
		||||
    typedef typename std::conditional < std::is_enum<T>::value,
 | 
			
		||||
        std::underlying_type<T>,
 | 
			
		||||
        std::enable_if < true, T >> ::type::type StorageType;
 | 
			
		||||
    typedef typename std::conditional<std::is_enum<T>::value, std::underlying_type<T>,
 | 
			
		||||
                                      std::enable_if<true, T>>::type::type StorageType;
 | 
			
		||||
 | 
			
		||||
    // Unsigned version of StorageType
 | 
			
		||||
    typedef typename std::make_unsigned<StorageType>::type StorageTypeU;
 | 
			
		||||
 | 
			
		||||
    FORCE_INLINE StorageType GetMask() const {
 | 
			
		||||
        return (((StorageTypeU)~0) >> (8 * sizeof(T)-bits)) << position;
 | 
			
		||||
        return (((StorageTypeU)~0) >> (8 * sizeof(T) - bits)) << position;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    StorageType storage;
 | 
			
		||||
@ -186,5 +178,6 @@ private:
 | 
			
		||||
#pragma pack()
 | 
			
		||||
 | 
			
		||||
#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
 | 
			
		||||
 | 
			
		||||
@ -18,49 +18,60 @@ namespace Common {
 | 
			
		||||
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
template <typename T>
 | 
			
		||||
static inline int CountSetBits(T v)
 | 
			
		||||
{
 | 
			
		||||
static inline int CountSetBits(T v) {
 | 
			
		||||
    // from https://graphics.stanford.edu/~seander/bithacks.html
 | 
			
		||||
    // GCC has this built in, but MSVC's intrinsic will only emit the actual
 | 
			
		||||
    // POPCNT instruction, which we're not depending on
 | 
			
		||||
    v = v - ((v >> 1) & (T)~(T)0/3);
 | 
			
		||||
    v = (v & (T)~(T)0/15*3) + ((v >> 2) & (T)~(T)0/15*3);
 | 
			
		||||
    v = (v + (v >> 4)) & (T)~(T)0/255*15;
 | 
			
		||||
    return (T)(v * ((T)~(T)0/255)) >> (sizeof(T) - 1) * 8;
 | 
			
		||||
    v = v - ((v >> 1) & (T) ~(T)0 / 3);
 | 
			
		||||
    v = (v & (T) ~(T)0 / 15 * 3) + ((v >> 2) & (T) ~(T)0 / 15 * 3);
 | 
			
		||||
    v = (v + (v >> 4)) & (T) ~(T)0 / 255 * 15;
 | 
			
		||||
    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;
 | 
			
		||||
    _BitScanForward(&index, val);
 | 
			
		||||
    return (int)index;
 | 
			
		||||
}
 | 
			
		||||
static inline int LeastSignificantSetBit(u16 val)
 | 
			
		||||
{
 | 
			
		||||
static inline int LeastSignificantSetBit(u16 val) {
 | 
			
		||||
    unsigned long index;
 | 
			
		||||
    _BitScanForward(&index, val);
 | 
			
		||||
    return (int)index;
 | 
			
		||||
}
 | 
			
		||||
static inline int LeastSignificantSetBit(u32 val)
 | 
			
		||||
{
 | 
			
		||||
static inline int LeastSignificantSetBit(u32 val) {
 | 
			
		||||
    unsigned long index;
 | 
			
		||||
    _BitScanForward(&index, val);
 | 
			
		||||
    return (int)index;
 | 
			
		||||
}
 | 
			
		||||
static inline int LeastSignificantSetBit(u64 val)
 | 
			
		||||
{
 | 
			
		||||
static inline int LeastSignificantSetBit(u64 val) {
 | 
			
		||||
    unsigned long index;
 | 
			
		||||
    _BitScanForward64(&index, val);
 | 
			
		||||
    return (int)index;
 | 
			
		||||
}
 | 
			
		||||
#else
 | 
			
		||||
static inline int CountSetBits(u8 val) { return __builtin_popcount(val); }
 | 
			
		||||
static inline int CountSetBits(u16 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 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); }
 | 
			
		||||
static inline int CountSetBits(u8 val) {
 | 
			
		||||
    return __builtin_popcount(val);
 | 
			
		||||
}
 | 
			
		||||
static inline int CountSetBits(u16 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 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
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
 | 
			
		||||
template <typename IntTy>
 | 
			
		||||
class BitSet
 | 
			
		||||
{
 | 
			
		||||
class BitSet {
 | 
			
		||||
    static_assert(!std::is_signed<IntTy>::value, "BitSet should not be used with signed types");
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    // A reference to a particular bit, returned from operator[].
 | 
			
		||||
    class Ref
 | 
			
		||||
    {
 | 
			
		||||
    class Ref {
 | 
			
		||||
    public:
 | 
			
		||||
        Ref(Ref&& other) : m_bs(other.m_bs), m_mask(other.m_mask) {}
 | 
			
		||||
        Ref(BitSet* bs, IntTy mask) : m_bs(bs), m_mask(mask) {}
 | 
			
		||||
        operator bool() const { return (m_bs->m_val & m_mask) != 0; }
 | 
			
		||||
        bool operator=(bool set)
 | 
			
		||||
        {
 | 
			
		||||
        operator bool() const {
 | 
			
		||||
            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);
 | 
			
		||||
            return set;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        BitSet* m_bs;
 | 
			
		||||
        IntTy m_mask;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // A STL-like iterator is required to be able to use range-based for loops.
 | 
			
		||||
    class Iterator
 | 
			
		||||
    {
 | 
			
		||||
    class Iterator {
 | 
			
		||||
    public:
 | 
			
		||||
        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& operator=(Iterator other) { new (this) Iterator(other); return *this; }
 | 
			
		||||
        int operator*() { return m_bit; }
 | 
			
		||||
        Iterator& operator++()
 | 
			
		||||
        {
 | 
			
		||||
            if (m_val == 0)
 | 
			
		||||
            {
 | 
			
		||||
        Iterator& operator=(Iterator other) {
 | 
			
		||||
            new (this) Iterator(other);
 | 
			
		||||
            return *this;
 | 
			
		||||
        }
 | 
			
		||||
        int operator*() {
 | 
			
		||||
            return m_bit;
 | 
			
		||||
        }
 | 
			
		||||
        Iterator& operator++() {
 | 
			
		||||
            if (m_val == 0) {
 | 
			
		||||
                m_bit = -1;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
            } else {
 | 
			
		||||
                int bit = LeastSignificantSetBit(m_val);
 | 
			
		||||
                m_val &= ~(1 << bit);
 | 
			
		||||
                m_bit = bit;
 | 
			
		||||
            }
 | 
			
		||||
            return *this;
 | 
			
		||||
        }
 | 
			
		||||
        Iterator operator++(int _)
 | 
			
		||||
        {
 | 
			
		||||
        Iterator operator++(int _) {
 | 
			
		||||
            Iterator other(*this);
 | 
			
		||||
            ++*this;
 | 
			
		||||
            return other;
 | 
			
		||||
        }
 | 
			
		||||
        bool operator==(Iterator other) const { return m_bit == other.m_bit; }
 | 
			
		||||
        bool operator!=(Iterator other) const { return m_bit != other.m_bit; }
 | 
			
		||||
        bool operator==(Iterator other) const {
 | 
			
		||||
            return m_bit == other.m_bit;
 | 
			
		||||
        }
 | 
			
		||||
        bool operator!=(Iterator other) const {
 | 
			
		||||
            return m_bit != other.m_bit;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        IntTy m_val;
 | 
			
		||||
        int m_bit;
 | 
			
		||||
@ -142,42 +158,75 @@ public:
 | 
			
		||||
 | 
			
		||||
    BitSet() : m_val(0) {}
 | 
			
		||||
    explicit BitSet(IntTy val) : m_val(val) {}
 | 
			
		||||
    BitSet(std::initializer_list<int> init)
 | 
			
		||||
    {
 | 
			
		||||
    BitSet(std::initializer_list<int> init) {
 | 
			
		||||
        m_val = 0;
 | 
			
		||||
        for (int bit : init)
 | 
			
		||||
            m_val |= (IntTy)1 << bit;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static BitSet AllTrue(size_t count)
 | 
			
		||||
    {
 | 
			
		||||
        return BitSet(count == sizeof(IntTy)*8 ? ~(IntTy)0 : (((IntTy)1 << count) - 1));
 | 
			
		||||
    static BitSet AllTrue(size_t count) {
 | 
			
		||||
        return BitSet(count == sizeof(IntTy) * 8 ? ~(IntTy)0 : (((IntTy)1 << count) - 1));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ref operator[](size_t bit) { return Ref(this, (IntTy)1 << bit); }
 | 
			
		||||
    const Ref operator[](size_t bit) const { return (*const_cast<BitSet*>(this))[bit]; }
 | 
			
		||||
    bool operator==(BitSet other) const { return m_val == other.m_val; }
 | 
			
		||||
    bool operator!=(BitSet other) const { return m_val != other.m_val; }
 | 
			
		||||
    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; }
 | 
			
		||||
    Ref operator[](size_t bit) {
 | 
			
		||||
        return Ref(this, (IntTy)1 << bit);
 | 
			
		||||
    }
 | 
			
		||||
    const Ref operator[](size_t bit) const {
 | 
			
		||||
        return (*const_cast<BitSet*>(this))[bit];
 | 
			
		||||
    }
 | 
			
		||||
    bool operator==(BitSet other) const {
 | 
			
		||||
        return m_val == other.m_val;
 | 
			
		||||
    }
 | 
			
		||||
    bool operator!=(BitSet other) const {
 | 
			
		||||
        return m_val != other.m_val;
 | 
			
		||||
    }
 | 
			
		||||
    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 bool() { return m_val != 0; }
 | 
			
		||||
    operator bool() {
 | 
			
		||||
        return m_val != 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Warning: Even though on modern CPUs this is a single fast instruction,
 | 
			
		||||
    // Dolphin's official builds do not currently assume POPCNT support on x86,
 | 
			
		||||
    // so slower explicit bit twiddling is generated.  Still should generally
 | 
			
		||||
    // 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 end() const { return Iterator(m_val, -1); }
 | 
			
		||||
    Iterator begin() const {
 | 
			
		||||
        Iterator it(m_val, 0);
 | 
			
		||||
        return ++it;
 | 
			
		||||
    }
 | 
			
		||||
    Iterator end() const {
 | 
			
		||||
        return Iterator(m_val, -1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    IntTy m_val;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -2,33 +2,29 @@
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include "common/break_points.h"
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
 | 
			
		||||
bool BreakPoints::IsAddressBreakPoint(u32 iAddress) const
 | 
			
		||||
{
 | 
			
		||||
bool BreakPoints::IsAddressBreakPoint(u32 iAddress) const {
 | 
			
		||||
    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();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool BreakPoints::IsTempBreakPoint(u32 iAddress) const
 | 
			
		||||
{
 | 
			
		||||
    auto cond = [&iAddress](const TBreakPoint& bp) { return bp.iAddress == iAddress && bp.bTemporary; };
 | 
			
		||||
    auto it   = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond);
 | 
			
		||||
bool BreakPoints::IsTempBreakPoint(u32 iAddress) const {
 | 
			
		||||
    auto cond = [&iAddress](const TBreakPoint& bp) {
 | 
			
		||||
        return bp.iAddress == iAddress && bp.bTemporary;
 | 
			
		||||
    };
 | 
			
		||||
    auto it = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond);
 | 
			
		||||
    return it != m_BreakPoints.end();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BreakPoints::TBreakPointsStr BreakPoints::GetStrings() const
 | 
			
		||||
{
 | 
			
		||||
BreakPoints::TBreakPointsStr BreakPoints::GetStrings() const {
 | 
			
		||||
    TBreakPointsStr bps;
 | 
			
		||||
    for (auto breakpoint : m_BreakPoints)
 | 
			
		||||
    {
 | 
			
		||||
        if (!breakpoint.bTemporary)
 | 
			
		||||
        {
 | 
			
		||||
    for (auto breakpoint : m_BreakPoints) {
 | 
			
		||||
        if (!breakpoint.bTemporary) {
 | 
			
		||||
            std::stringstream bp;
 | 
			
		||||
            bp << std::hex << breakpoint.iAddress << " " << (breakpoint.bOn ? "n" : "");
 | 
			
		||||
            bps.push_back(bp.str());
 | 
			
		||||
@ -38,10 +34,8 @@ BreakPoints::TBreakPointsStr BreakPoints::GetStrings() const
 | 
			
		||||
    return bps;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BreakPoints::AddFromStrings(const TBreakPointsStr& bps)
 | 
			
		||||
{
 | 
			
		||||
    for (auto bps_item : bps)
 | 
			
		||||
    {
 | 
			
		||||
void BreakPoints::AddFromStrings(const TBreakPointsStr& bps) {
 | 
			
		||||
    for (auto bps_item : bps) {
 | 
			
		||||
        TBreakPoint bp;
 | 
			
		||||
        std::stringstream bpstr;
 | 
			
		||||
        bpstr << std::hex << bps_item;
 | 
			
		||||
@ -52,18 +46,15 @@ void BreakPoints::AddFromStrings(const TBreakPointsStr& bps)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BreakPoints::Add(const TBreakPoint& bp)
 | 
			
		||||
{
 | 
			
		||||
    if (!IsAddressBreakPoint(bp.iAddress))
 | 
			
		||||
    {
 | 
			
		||||
void BreakPoints::Add(const TBreakPoint& bp) {
 | 
			
		||||
    if (!IsAddressBreakPoint(bp.iAddress)) {
 | 
			
		||||
        m_BreakPoints.push_back(bp);
 | 
			
		||||
        //if (jit)
 | 
			
		||||
        // if (jit)
 | 
			
		||||
        //    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
 | 
			
		||||
    {
 | 
			
		||||
        TBreakPoint pt; // breakpoint settings
 | 
			
		||||
@ -73,22 +64,20 @@ void BreakPoints::Add(u32 em_address, bool temp)
 | 
			
		||||
 | 
			
		||||
        m_BreakPoints.push_back(pt);
 | 
			
		||||
 | 
			
		||||
        //if (jit)
 | 
			
		||||
        // if (jit)
 | 
			
		||||
        //    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 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())
 | 
			
		||||
        m_BreakPoints.erase(it);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BreakPoints::Clear()
 | 
			
		||||
{
 | 
			
		||||
    //if (jit)
 | 
			
		||||
void BreakPoints::Clear() {
 | 
			
		||||
    // if (jit)
 | 
			
		||||
    //{
 | 
			
		||||
    //    std::for_each(m_BreakPoints.begin(), m_BreakPoints.end(),
 | 
			
		||||
    //        [](const TBreakPoint& bp)
 | 
			
		||||
 | 
			
		||||
@ -4,28 +4,27 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
 | 
			
		||||
class DebugInterface;
 | 
			
		||||
 | 
			
		||||
struct TBreakPoint
 | 
			
		||||
{
 | 
			
		||||
    u32  iAddress;
 | 
			
		||||
struct TBreakPoint {
 | 
			
		||||
    u32 iAddress;
 | 
			
		||||
    bool bOn;
 | 
			
		||||
    bool bTemporary;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Code breakpoints.
 | 
			
		||||
class BreakPoints
 | 
			
		||||
{
 | 
			
		||||
class BreakPoints {
 | 
			
		||||
public:
 | 
			
		||||
    typedef std::vector<TBreakPoint> TBreakPoints;
 | 
			
		||||
    typedef std::vector<std::string> TBreakPointsStr;
 | 
			
		||||
 | 
			
		||||
    const TBreakPoints& GetBreakPoints() { return m_BreakPoints; }
 | 
			
		||||
    const TBreakPoints& GetBreakPoints() {
 | 
			
		||||
        return m_BreakPoints;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TBreakPointsStr GetStrings() const;
 | 
			
		||||
    void AddFromStrings(const TBreakPointsStr& bps);
 | 
			
		||||
@ -35,7 +34,7 @@ public:
 | 
			
		||||
    bool IsTempBreakPoint(u32 iAddress) const;
 | 
			
		||||
 | 
			
		||||
    // Add BreakPoint
 | 
			
		||||
    void Add(u32 em_address, bool temp=false);
 | 
			
		||||
    void Add(u32 em_address, bool temp = false);
 | 
			
		||||
    void Add(const TBreakPoint& bp);
 | 
			
		||||
 | 
			
		||||
    // Remove Breakpoint
 | 
			
		||||
@ -46,5 +45,5 @@ public:
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    TBreakPoints m_BreakPoints;
 | 
			
		||||
    u32          m_iBreakOnCount;
 | 
			
		||||
    u32 m_iBreakOnCount;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -35,87 +35,90 @@
 | 
			
		||||
#include <type_traits>
 | 
			
		||||
#include <utility>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include "common/assert.h"
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
 | 
			
		||||
template <class T>
 | 
			
		||||
struct LinkedListItem : public T
 | 
			
		||||
{
 | 
			
		||||
    LinkedListItem<T> *next;
 | 
			
		||||
struct LinkedListItem : public T {
 | 
			
		||||
    LinkedListItem<T>* next;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class PointerWrap;
 | 
			
		||||
 | 
			
		||||
class PointerWrapSection
 | 
			
		||||
{
 | 
			
		||||
class PointerWrapSection {
 | 
			
		||||
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();
 | 
			
		||||
 | 
			
		||||
    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; }
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    PointerWrap &p_;
 | 
			
		||||
    PointerWrap& p_;
 | 
			
		||||
    int ver_;
 | 
			
		||||
    const char *title_;
 | 
			
		||||
    const char* title_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Wrapper class
 | 
			
		||||
class PointerWrap
 | 
			
		||||
{
 | 
			
		||||
    // 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...
 | 
			
		||||
class PointerWrap {
 | 
			
		||||
// 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...
 | 
			
		||||
#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
 | 
			
		||||
    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
 | 
			
		||||
    struct DoHelper
 | 
			
		||||
    {
 | 
			
		||||
        static void DoArray(PointerWrap *p, T *x, int count)
 | 
			
		||||
        {
 | 
			
		||||
    struct DoHelper {
 | 
			
		||||
        static void DoArray(PointerWrap* p, T* x, int count) {
 | 
			
		||||
            for (int i = 0; i < count; ++i)
 | 
			
		||||
                p->Do(x[i]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        static void Do(PointerWrap *p, T &x)
 | 
			
		||||
        {
 | 
			
		||||
        static void Do(PointerWrap* p, T& x) {
 | 
			
		||||
            p->DoClass(x);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    template<typename T>
 | 
			
		||||
    struct DoHelper<T, true, false>
 | 
			
		||||
    {
 | 
			
		||||
        static void DoArray(PointerWrap *p, T *x, int count)
 | 
			
		||||
        {
 | 
			
		||||
            p->DoVoid((void *)x, sizeof(T) * count);
 | 
			
		||||
    template <typename T>
 | 
			
		||||
    struct DoHelper<T, true, false> {
 | 
			
		||||
        static void DoArray(PointerWrap* p, T* x, int count) {
 | 
			
		||||
            p->DoVoid((void*)x, sizeof(T) * count);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        static void Do(PointerWrap *p, T &x)
 | 
			
		||||
        {
 | 
			
		||||
            p->DoVoid((void *)&x, sizeof(x));
 | 
			
		||||
        static void Do(PointerWrap* p, T& x) {
 | 
			
		||||
            p->DoVoid((void*)&x, sizeof(x));
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    enum Mode {
 | 
			
		||||
        MODE_READ = 1, // load
 | 
			
		||||
        MODE_WRITE, // save
 | 
			
		||||
        MODE_MEASURE, // calculate size
 | 
			
		||||
        MODE_VERIFY, // compare
 | 
			
		||||
        MODE_WRITE,    // save
 | 
			
		||||
        MODE_MEASURE,  // calculate size
 | 
			
		||||
        MODE_VERIFY,   // compare
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    enum Error {
 | 
			
		||||
@ -124,247 +127,237 @@ public:
 | 
			
		||||
        ERROR_FAILURE = 2,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    u8 **ptr;
 | 
			
		||||
    u8** ptr;
 | 
			
		||||
    Mode mode;
 | 
			
		||||
    Error error;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    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(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) {}
 | 
			
		||||
 | 
			
		||||
    PointerWrapSection Section(const char *title, int ver) {
 | 
			
		||||
    PointerWrapSection Section(const char* title, int ver) {
 | 
			
		||||
        return Section(title, ver, ver);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // The returned object can be compared against the version that was loaded.
 | 
			
		||||
    // This can be used to support versions as old as minVer.
 | 
			
		||||
    // 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};
 | 
			
		||||
        int foundVersion = ver;
 | 
			
		||||
 | 
			
		||||
        strncpy(marker, title, sizeof(marker));
 | 
			
		||||
        if (!ExpectVoid(marker, sizeof(marker)))
 | 
			
		||||
        {
 | 
			
		||||
        if (!ExpectVoid(marker, sizeof(marker))) {
 | 
			
		||||
            // Might be before we added name markers for safety.
 | 
			
		||||
            if (foundVersion == 1 && ExpectVoid(&foundVersion, sizeof(foundVersion)))
 | 
			
		||||
                DoMarker(title);
 | 
			
		||||
            // Wasn't found, but maybe we can still load the state.
 | 
			
		||||
            else
 | 
			
		||||
                foundVersion = 0;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        } else
 | 
			
		||||
            Do(foundVersion);
 | 
			
		||||
 | 
			
		||||
        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);
 | 
			
		||||
            return PointerWrapSection(*this, -1, title);
 | 
			
		||||
        }
 | 
			
		||||
        return PointerWrapSection(*this, foundVersion, title);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void SetMode(Mode mode_) {mode = mode_;}
 | 
			
		||||
    Mode GetMode() const {return mode;}
 | 
			
		||||
    u8 **GetPPtr() {return ptr;}
 | 
			
		||||
    void SetError(Error error_)
 | 
			
		||||
    {
 | 
			
		||||
    void SetMode(Mode mode_) {
 | 
			
		||||
        mode = mode_;
 | 
			
		||||
    }
 | 
			
		||||
    Mode GetMode() const {
 | 
			
		||||
        return mode;
 | 
			
		||||
    }
 | 
			
		||||
    u8** GetPPtr() {
 | 
			
		||||
        return ptr;
 | 
			
		||||
    }
 | 
			
		||||
    void SetError(Error error_) {
 | 
			
		||||
        if (error < error_)
 | 
			
		||||
            error = error_;
 | 
			
		||||
        if (error > ERROR_WARNING)
 | 
			
		||||
            mode = PointerWrap::MODE_MEASURE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool ExpectVoid(void *data, int size)
 | 
			
		||||
    {
 | 
			
		||||
    bool ExpectVoid(void* data, int size) {
 | 
			
		||||
        switch (mode) {
 | 
			
		||||
        case MODE_READ:    if (memcmp(data, *ptr, size) != 0) 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_READ:
 | 
			
		||||
            if (memcmp(data, *ptr, size) != 0)
 | 
			
		||||
                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:
 | 
			
		||||
            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",
 | 
			
		||||
                    ((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i],
 | 
			
		||||
                    (*ptr)[i], (*ptr)[i], &(*ptr)[i]);
 | 
			
		||||
                    ((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i], (*ptr)[i], (*ptr)[i],
 | 
			
		||||
                    &(*ptr)[i]);
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        default: break;  // throw an error?
 | 
			
		||||
        default:
 | 
			
		||||
            break; // throw an error?
 | 
			
		||||
        }
 | 
			
		||||
        (*ptr) += size;
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void DoVoid(void *data, int size)
 | 
			
		||||
    {
 | 
			
		||||
    void DoVoid(void* data, int size) {
 | 
			
		||||
        switch (mode) {
 | 
			
		||||
        case MODE_READ:    memcpy(data, *ptr, size); break;
 | 
			
		||||
        case MODE_WRITE: memcpy(*ptr, data, size); break;
 | 
			
		||||
        case MODE_MEASURE: break;  // MODE_MEASURE - don't need to do anything
 | 
			
		||||
        case MODE_READ:
 | 
			
		||||
            memcpy(data, *ptr, size);
 | 
			
		||||
            break;
 | 
			
		||||
        case MODE_WRITE:
 | 
			
		||||
            memcpy(*ptr, data, size);
 | 
			
		||||
            break;
 | 
			
		||||
        case MODE_MEASURE:
 | 
			
		||||
            break; // MODE_MEASURE - don't need to do anything
 | 
			
		||||
        case MODE_VERIFY:
 | 
			
		||||
            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",
 | 
			
		||||
                    ((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i],
 | 
			
		||||
                    (*ptr)[i], (*ptr)[i], &(*ptr)[i]);
 | 
			
		||||
                    ((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i], (*ptr)[i], (*ptr)[i],
 | 
			
		||||
                    &(*ptr)[i]);
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        default: break;  // throw an error?
 | 
			
		||||
        default:
 | 
			
		||||
            break; // throw an error?
 | 
			
		||||
        }
 | 
			
		||||
        (*ptr) += size;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template<class K, class T>
 | 
			
		||||
    void Do(std::map<K, T *> &x)
 | 
			
		||||
    {
 | 
			
		||||
        if (mode == MODE_READ)
 | 
			
		||||
        {
 | 
			
		||||
            for (auto it = x.begin(), end = x.end(); it != end; ++it)
 | 
			
		||||
            {
 | 
			
		||||
    template <class K, class T>
 | 
			
		||||
    void Do(std::map<K, T*>& x) {
 | 
			
		||||
        if (mode == MODE_READ) {
 | 
			
		||||
            for (auto it = x.begin(), end = x.end(); it != end; ++it) {
 | 
			
		||||
                if (it->second != nullptr)
 | 
			
		||||
                    delete it->second;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        T *dv = nullptr;
 | 
			
		||||
        T* dv = nullptr;
 | 
			
		||||
        DoMap(x, dv);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template<class K, class T>
 | 
			
		||||
    void Do(std::map<K, T> &x)
 | 
			
		||||
    {
 | 
			
		||||
    template <class K, class T>
 | 
			
		||||
    void Do(std::map<K, T>& x) {
 | 
			
		||||
        T dv = T();
 | 
			
		||||
        DoMap(x, dv);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template<class K, class T>
 | 
			
		||||
    void DoMap(std::map<K, T> &x, T &default_val)
 | 
			
		||||
    {
 | 
			
		||||
    template <class K, class T>
 | 
			
		||||
    void DoMap(std::map<K, T>& x, T& default_val) {
 | 
			
		||||
        unsigned int number = (unsigned int)x.size();
 | 
			
		||||
        Do(number);
 | 
			
		||||
        switch (mode) {
 | 
			
		||||
        case MODE_READ:
 | 
			
		||||
            {
 | 
			
		||||
                x.clear();
 | 
			
		||||
                while (number > 0)
 | 
			
		||||
                {
 | 
			
		||||
                    K first = K();
 | 
			
		||||
                    Do(first);
 | 
			
		||||
                    T second = default_val;
 | 
			
		||||
                    Do(second);
 | 
			
		||||
                    x[first] = second;
 | 
			
		||||
                    --number;
 | 
			
		||||
                }
 | 
			
		||||
        case MODE_READ: {
 | 
			
		||||
            x.clear();
 | 
			
		||||
            while (number > 0) {
 | 
			
		||||
                K first = K();
 | 
			
		||||
                Do(first);
 | 
			
		||||
                T second = default_val;
 | 
			
		||||
                Do(second);
 | 
			
		||||
                x[first] = second;
 | 
			
		||||
                --number;
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        } break;
 | 
			
		||||
        case MODE_WRITE:
 | 
			
		||||
        case MODE_MEASURE:
 | 
			
		||||
        case MODE_VERIFY:
 | 
			
		||||
            {
 | 
			
		||||
                typename std::map<K, T>::iterator itr = x.begin();
 | 
			
		||||
                while (number > 0)
 | 
			
		||||
                {
 | 
			
		||||
                    K first = itr->first;
 | 
			
		||||
                    Do(first);
 | 
			
		||||
                    Do(itr->second);
 | 
			
		||||
                    --number;
 | 
			
		||||
                    ++itr;
 | 
			
		||||
                }
 | 
			
		||||
        case MODE_VERIFY: {
 | 
			
		||||
            typename std::map<K, T>::iterator itr = x.begin();
 | 
			
		||||
            while (number > 0) {
 | 
			
		||||
                K first = itr->first;
 | 
			
		||||
                Do(first);
 | 
			
		||||
                Do(itr->second);
 | 
			
		||||
                --number;
 | 
			
		||||
                ++itr;
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        } break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template<class K, class T>
 | 
			
		||||
    void Do(std::multimap<K, T *> &x)
 | 
			
		||||
    {
 | 
			
		||||
        if (mode == MODE_READ)
 | 
			
		||||
        {
 | 
			
		||||
            for (auto it = x.begin(), end = x.end(); it != end; ++it)
 | 
			
		||||
            {
 | 
			
		||||
    template <class K, class T>
 | 
			
		||||
    void Do(std::multimap<K, T*>& x) {
 | 
			
		||||
        if (mode == MODE_READ) {
 | 
			
		||||
            for (auto it = x.begin(), end = x.end(); it != end; ++it) {
 | 
			
		||||
                if (it->second != nullptr)
 | 
			
		||||
                    delete it->second;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        T *dv = nullptr;
 | 
			
		||||
        T* dv = nullptr;
 | 
			
		||||
        DoMultimap(x, dv);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template<class K, class T>
 | 
			
		||||
    void Do(std::multimap<K, T> &x)
 | 
			
		||||
    {
 | 
			
		||||
    template <class K, class T>
 | 
			
		||||
    void Do(std::multimap<K, T>& x) {
 | 
			
		||||
        T dv = T();
 | 
			
		||||
        DoMultimap(x, dv);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template<class K, class T>
 | 
			
		||||
    void DoMultimap(std::multimap<K, T> &x, T &default_val)
 | 
			
		||||
    {
 | 
			
		||||
    template <class K, class T>
 | 
			
		||||
    void DoMultimap(std::multimap<K, T>& x, T& default_val) {
 | 
			
		||||
        unsigned int number = (unsigned int)x.size();
 | 
			
		||||
        Do(number);
 | 
			
		||||
        switch (mode) {
 | 
			
		||||
        case MODE_READ:
 | 
			
		||||
            {
 | 
			
		||||
                x.clear();
 | 
			
		||||
                while (number > 0)
 | 
			
		||||
                {
 | 
			
		||||
                    K first = K();
 | 
			
		||||
                    Do(first);
 | 
			
		||||
                    T second = default_val;
 | 
			
		||||
                    Do(second);
 | 
			
		||||
                    x.insert(std::make_pair(first, second));
 | 
			
		||||
                    --number;
 | 
			
		||||
                }
 | 
			
		||||
        case MODE_READ: {
 | 
			
		||||
            x.clear();
 | 
			
		||||
            while (number > 0) {
 | 
			
		||||
                K first = K();
 | 
			
		||||
                Do(first);
 | 
			
		||||
                T second = default_val;
 | 
			
		||||
                Do(second);
 | 
			
		||||
                x.insert(std::make_pair(first, second));
 | 
			
		||||
                --number;
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        } break;
 | 
			
		||||
        case MODE_WRITE:
 | 
			
		||||
        case MODE_MEASURE:
 | 
			
		||||
        case MODE_VERIFY:
 | 
			
		||||
            {
 | 
			
		||||
                typename std::multimap<K, T>::iterator itr = x.begin();
 | 
			
		||||
                while (number > 0)
 | 
			
		||||
                {
 | 
			
		||||
                    Do(itr->first);
 | 
			
		||||
                    Do(itr->second);
 | 
			
		||||
                    --number;
 | 
			
		||||
                    ++itr;
 | 
			
		||||
                }
 | 
			
		||||
        case MODE_VERIFY: {
 | 
			
		||||
            typename std::multimap<K, T>::iterator itr = x.begin();
 | 
			
		||||
            while (number > 0) {
 | 
			
		||||
                Do(itr->first);
 | 
			
		||||
                Do(itr->second);
 | 
			
		||||
                --number;
 | 
			
		||||
                ++itr;
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        } break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Store vectors.
 | 
			
		||||
    template<class T>
 | 
			
		||||
    void Do(std::vector<T *> &x)
 | 
			
		||||
    {
 | 
			
		||||
        T *dv = nullptr;
 | 
			
		||||
    template <class T>
 | 
			
		||||
    void Do(std::vector<T*>& x) {
 | 
			
		||||
        T* dv = nullptr;
 | 
			
		||||
        DoVector(x, dv);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template<class T>
 | 
			
		||||
    void Do(std::vector<T> &x)
 | 
			
		||||
    {
 | 
			
		||||
    template <class T>
 | 
			
		||||
    void Do(std::vector<T>& x) {
 | 
			
		||||
        T dv = T();
 | 
			
		||||
        DoVector(x, dv);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    template<class T>
 | 
			
		||||
    void DoPOD(std::vector<T> &x)
 | 
			
		||||
    {
 | 
			
		||||
    template <class T>
 | 
			
		||||
    void DoPOD(std::vector<T>& x) {
 | 
			
		||||
        T dv = T();
 | 
			
		||||
        DoVectorPOD(x, dv);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template<class T>
 | 
			
		||||
    void Do(std::vector<T> &x, T &default_val)
 | 
			
		||||
    {
 | 
			
		||||
    template <class T>
 | 
			
		||||
    void Do(std::vector<T>& x, T& default_val) {
 | 
			
		||||
        DoVector(x, default_val);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template<class T>
 | 
			
		||||
    void DoVector(std::vector<T> &x, T &default_val)
 | 
			
		||||
    {
 | 
			
		||||
    template <class T>
 | 
			
		||||
    void DoVector(std::vector<T>& x, T& default_val) {
 | 
			
		||||
        u32 vec_size = (u32)x.size();
 | 
			
		||||
        Do(vec_size);
 | 
			
		||||
        x.resize(vec_size, default_val);
 | 
			
		||||
@ -372,9 +365,8 @@ public:
 | 
			
		||||
            DoArray(&x[0], vec_size);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template<class T>
 | 
			
		||||
    void DoVectorPOD(std::vector<T> &x, T &default_val)
 | 
			
		||||
    {
 | 
			
		||||
    template <class T>
 | 
			
		||||
    void DoVectorPOD(std::vector<T>& x, T& default_val) {
 | 
			
		||||
        u32 vec_size = (u32)x.size();
 | 
			
		||||
        Do(vec_size);
 | 
			
		||||
        x.resize(vec_size, default_val);
 | 
			
		||||
@ -383,55 +375,48 @@ public:
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Store deques.
 | 
			
		||||
    template<class T>
 | 
			
		||||
    void Do(std::deque<T *> &x)
 | 
			
		||||
    {
 | 
			
		||||
        T *dv = nullptr;
 | 
			
		||||
    template <class T>
 | 
			
		||||
    void Do(std::deque<T*>& x) {
 | 
			
		||||
        T* dv = nullptr;
 | 
			
		||||
        DoDeque(x, dv);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template<class T>
 | 
			
		||||
    void Do(std::deque<T> &x)
 | 
			
		||||
    {
 | 
			
		||||
    template <class T>
 | 
			
		||||
    void Do(std::deque<T>& x) {
 | 
			
		||||
        T dv = T();
 | 
			
		||||
        DoDeque(x, dv);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template<class T>
 | 
			
		||||
    void DoDeque(std::deque<T> &x, T &default_val)
 | 
			
		||||
    {
 | 
			
		||||
    template <class T>
 | 
			
		||||
    void DoDeque(std::deque<T>& x, T& default_val) {
 | 
			
		||||
        u32 deq_size = (u32)x.size();
 | 
			
		||||
        Do(deq_size);
 | 
			
		||||
        x.resize(deq_size, default_val);
 | 
			
		||||
        u32 i;
 | 
			
		||||
        for(i = 0; i < deq_size; i++)
 | 
			
		||||
        for (i = 0; i < deq_size; i++)
 | 
			
		||||
            Do(x[i]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Store STL lists.
 | 
			
		||||
    template<class T>
 | 
			
		||||
    void Do(std::list<T *> &x)
 | 
			
		||||
    {
 | 
			
		||||
        T *dv = nullptr;
 | 
			
		||||
    template <class T>
 | 
			
		||||
    void Do(std::list<T*>& x) {
 | 
			
		||||
        T* dv = nullptr;
 | 
			
		||||
        Do(x, dv);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template<class T>
 | 
			
		||||
    void Do(std::list<T> &x)
 | 
			
		||||
    {
 | 
			
		||||
    template <class T>
 | 
			
		||||
    void Do(std::list<T>& x) {
 | 
			
		||||
        T dv = T();
 | 
			
		||||
        DoList(x, dv);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template<class T>
 | 
			
		||||
    void Do(std::list<T> &x, T &default_val)
 | 
			
		||||
    {
 | 
			
		||||
    template <class T>
 | 
			
		||||
    void Do(std::list<T>& x, T& default_val) {
 | 
			
		||||
        DoList(x, default_val);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template<class T>
 | 
			
		||||
    void DoList(std::list<T> &x, T &default_val)
 | 
			
		||||
    {
 | 
			
		||||
    template <class T>
 | 
			
		||||
    void DoList(std::list<T>& x, T& default_val) {
 | 
			
		||||
        u32 list_size = (u32)x.size();
 | 
			
		||||
        Do(list_size);
 | 
			
		||||
        x.resize(list_size, default_val);
 | 
			
		||||
@ -441,15 +426,11 @@ public:
 | 
			
		||||
            Do(*itr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // Store STL sets.
 | 
			
		||||
    template <class T>
 | 
			
		||||
    void Do(std::set<T *> &x)
 | 
			
		||||
    {
 | 
			
		||||
        if (mode == MODE_READ)
 | 
			
		||||
        {
 | 
			
		||||
            for (auto it = x.begin(), end = x.end(); it != end; ++it)
 | 
			
		||||
            {
 | 
			
		||||
    void Do(std::set<T*>& x) {
 | 
			
		||||
        if (mode == MODE_READ) {
 | 
			
		||||
            for (auto it = x.begin(), end = x.end(); it != end; ++it) {
 | 
			
		||||
                if (*it != nullptr)
 | 
			
		||||
                    delete *it;
 | 
			
		||||
            }
 | 
			
		||||
@ -458,39 +439,31 @@ public:
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template <class T>
 | 
			
		||||
    void Do(std::set<T> &x)
 | 
			
		||||
    {
 | 
			
		||||
    void Do(std::set<T>& x) {
 | 
			
		||||
        DoSet(x);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template <class T>
 | 
			
		||||
    void DoSet(std::set<T> &x)
 | 
			
		||||
    {
 | 
			
		||||
    void DoSet(std::set<T>& x) {
 | 
			
		||||
        unsigned int number = (unsigned int)x.size();
 | 
			
		||||
        Do(number);
 | 
			
		||||
 | 
			
		||||
        switch (mode)
 | 
			
		||||
        {
 | 
			
		||||
        case MODE_READ:
 | 
			
		||||
            {
 | 
			
		||||
                x.clear();
 | 
			
		||||
                while (number-- > 0)
 | 
			
		||||
                {
 | 
			
		||||
                    T it = T();
 | 
			
		||||
                    Do(it);
 | 
			
		||||
                    x.insert(it);
 | 
			
		||||
                }
 | 
			
		||||
        switch (mode) {
 | 
			
		||||
        case MODE_READ: {
 | 
			
		||||
            x.clear();
 | 
			
		||||
            while (number-- > 0) {
 | 
			
		||||
                T it = T();
 | 
			
		||||
                Do(it);
 | 
			
		||||
                x.insert(it);
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        } break;
 | 
			
		||||
        case MODE_WRITE:
 | 
			
		||||
        case MODE_MEASURE:
 | 
			
		||||
        case MODE_VERIFY:
 | 
			
		||||
            {
 | 
			
		||||
                typename std::set<T>::iterator itr = x.begin();
 | 
			
		||||
                while (number-- > 0)
 | 
			
		||||
                    Do(*itr++);
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        case MODE_VERIFY: {
 | 
			
		||||
            typename std::set<T>::iterator itr = x.begin();
 | 
			
		||||
            while (number-- > 0)
 | 
			
		||||
                Do(*itr++);
 | 
			
		||||
        } break;
 | 
			
		||||
 | 
			
		||||
        default:
 | 
			
		||||
            LOG_ERROR(Common, "Savestate error: invalid mode %d.", mode);
 | 
			
		||||
@ -498,51 +471,58 @@ public:
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Store strings.
 | 
			
		||||
    void Do(std::string &x)
 | 
			
		||||
    {
 | 
			
		||||
    void Do(std::string& x) {
 | 
			
		||||
        int stringLen = (int)x.length() + 1;
 | 
			
		||||
        Do(stringLen);
 | 
			
		||||
 | 
			
		||||
        switch (mode) {
 | 
			
		||||
        case MODE_READ:        x = (char*)*ptr; break;
 | 
			
		||||
        case MODE_WRITE:    memcpy(*ptr, x.c_str(), stringLen); break;
 | 
			
		||||
        case MODE_MEASURE: break;
 | 
			
		||||
        case MODE_READ:
 | 
			
		||||
            x = (char*)*ptr;
 | 
			
		||||
            break;
 | 
			
		||||
        case MODE_WRITE:
 | 
			
		||||
            memcpy(*ptr, x.c_str(), stringLen);
 | 
			
		||||
            break;
 | 
			
		||||
        case MODE_MEASURE:
 | 
			
		||||
            break;
 | 
			
		||||
        case MODE_VERIFY:
 | 
			
		||||
            DEBUG_ASSERT_MSG((x == (char*)*ptr),
 | 
			
		||||
                "Savestate verification failure: \"%s\" != \"%s\" (at %p).\n",
 | 
			
		||||
                x.c_str(), (char*)*ptr, ptr);
 | 
			
		||||
                             "Savestate verification failure: \"%s\" != \"%s\" (at %p).\n",
 | 
			
		||||
                             x.c_str(), (char*)*ptr, ptr);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        (*ptr) += stringLen;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void Do(std::wstring &x)
 | 
			
		||||
    {
 | 
			
		||||
        int stringLen = sizeof(wchar_t)*((int)x.length() + 1);
 | 
			
		||||
    void Do(std::wstring& x) {
 | 
			
		||||
        int stringLen = sizeof(wchar_t) * ((int)x.length() + 1);
 | 
			
		||||
        Do(stringLen);
 | 
			
		||||
 | 
			
		||||
        switch (mode) {
 | 
			
		||||
        case MODE_READ:        x = (wchar_t*)*ptr; break;
 | 
			
		||||
        case MODE_WRITE:    memcpy(*ptr, x.c_str(), stringLen); break;
 | 
			
		||||
        case MODE_MEASURE: break;
 | 
			
		||||
        case MODE_READ:
 | 
			
		||||
            x = (wchar_t*)*ptr;
 | 
			
		||||
            break;
 | 
			
		||||
        case MODE_WRITE:
 | 
			
		||||
            memcpy(*ptr, x.c_str(), stringLen);
 | 
			
		||||
            break;
 | 
			
		||||
        case MODE_MEASURE:
 | 
			
		||||
            break;
 | 
			
		||||
        case MODE_VERIFY:
 | 
			
		||||
            DEBUG_ASSERT_MSG((x == (wchar_t*)*ptr),
 | 
			
		||||
                "Savestate verification failure: \"%ls\" != \"%ls\" (at %p).\n",
 | 
			
		||||
                x.c_str(), (wchar_t*)*ptr, ptr);
 | 
			
		||||
                             "Savestate verification failure: \"%ls\" != \"%ls\" (at %p).\n",
 | 
			
		||||
                             x.c_str(), (wchar_t*)*ptr, ptr);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        (*ptr) += stringLen;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template<class T>
 | 
			
		||||
    void DoClass(T &x) {
 | 
			
		||||
    template <class T>
 | 
			
		||||
    void DoClass(T& x) {
 | 
			
		||||
        x.DoState(*this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template<class T>
 | 
			
		||||
    void DoClass(T *&x) {
 | 
			
		||||
        if (mode == MODE_READ)
 | 
			
		||||
        {
 | 
			
		||||
    template <class T>
 | 
			
		||||
    void DoClass(T*& x) {
 | 
			
		||||
        if (mode == MODE_READ) {
 | 
			
		||||
            if (x != nullptr)
 | 
			
		||||
                delete x;
 | 
			
		||||
            x = new T();
 | 
			
		||||
@ -550,81 +530,70 @@ public:
 | 
			
		||||
        x->DoState(*this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template<class T>
 | 
			
		||||
    void DoArray(T *x, int count) {
 | 
			
		||||
    template <class T>
 | 
			
		||||
    void DoArray(T* x, int count) {
 | 
			
		||||
        DoHelper<T>::DoArray(this, x, count);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template<class T>
 | 
			
		||||
    void Do(T &x) {
 | 
			
		||||
    template <class T>
 | 
			
		||||
    void Do(T& x) {
 | 
			
		||||
        DoHelper<T>::Do(this, x);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template<class T>
 | 
			
		||||
    void DoPOD(T &x) {
 | 
			
		||||
    template <class T>
 | 
			
		||||
    void DoPOD(T& x) {
 | 
			
		||||
        DoHelper<T>::Do(this, x);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template<class T>
 | 
			
		||||
    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
 | 
			
		||||
    template <class T>
 | 
			
		||||
    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
 | 
			
		||||
        s32 offset = x - base;
 | 
			
		||||
        Do(offset);
 | 
			
		||||
        if (mode == MODE_READ)
 | 
			
		||||
            x = base + offset;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template<class T, LinkedListItem<T>* (*TNew)(), void (*TFree)(LinkedListItem<T>*), void (*TDo)(PointerWrap&, T*)>
 | 
			
		||||
    void DoLinkedList(LinkedListItem<T>*& list_start, LinkedListItem<T>** list_end = nullptr)
 | 
			
		||||
    {
 | 
			
		||||
    template <class T, LinkedListItem<T>* (*TNew)(), void (*TFree)(LinkedListItem<T>*),
 | 
			
		||||
              void (*TDo)(PointerWrap&, T*)>
 | 
			
		||||
    void DoLinkedList(LinkedListItem<T>*& list_start, LinkedListItem<T>** list_end = nullptr) {
 | 
			
		||||
        LinkedListItem<T>* list_cur = list_start;
 | 
			
		||||
        LinkedListItem<T>* prev = nullptr;
 | 
			
		||||
 | 
			
		||||
        while (true)
 | 
			
		||||
        {
 | 
			
		||||
        while (true) {
 | 
			
		||||
            u8 shouldExist = (list_cur ? 1 : 0);
 | 
			
		||||
            Do(shouldExist);
 | 
			
		||||
            if (shouldExist == 1)
 | 
			
		||||
            {
 | 
			
		||||
            if (shouldExist == 1) {
 | 
			
		||||
                LinkedListItem<T>* cur = list_cur ? list_cur : TNew();
 | 
			
		||||
                TDo(*this, (T*)cur);
 | 
			
		||||
                if (!list_cur)
 | 
			
		||||
                {
 | 
			
		||||
                    if (mode == MODE_READ)
 | 
			
		||||
                    {
 | 
			
		||||
                if (!list_cur) {
 | 
			
		||||
                    if (mode == MODE_READ) {
 | 
			
		||||
                        cur->next = nullptr;
 | 
			
		||||
                        list_cur = cur;
 | 
			
		||||
                        if (prev)
 | 
			
		||||
                            prev->next = cur;
 | 
			
		||||
                        else
 | 
			
		||||
                            list_start = cur;
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                    } else {
 | 
			
		||||
                        TFree(cur);
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                if (mode == MODE_READ)
 | 
			
		||||
                {
 | 
			
		||||
            } else {
 | 
			
		||||
                if (mode == MODE_READ) {
 | 
			
		||||
                    if (prev)
 | 
			
		||||
                        prev->next = nullptr;
 | 
			
		||||
                    if (list_end)
 | 
			
		||||
                        *list_end = prev;
 | 
			
		||||
                    if (list_cur)
 | 
			
		||||
                    {
 | 
			
		||||
                    if (list_cur) {
 | 
			
		||||
                        if (list_start == list_cur)
 | 
			
		||||
                            list_start = nullptr;
 | 
			
		||||
                        do
 | 
			
		||||
                        {
 | 
			
		||||
                        do {
 | 
			
		||||
                            LinkedListItem<T>* next = list_cur->next;
 | 
			
		||||
                            TFree(list_cur);
 | 
			
		||||
                            list_cur = next;
 | 
			
		||||
                        }
 | 
			
		||||
                        while (list_cur);
 | 
			
		||||
                        } while (list_cur);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                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;
 | 
			
		||||
        Do(cookie);
 | 
			
		||||
        if(mode == PointerWrap::MODE_READ && cookie != arbitraryNumber)
 | 
			
		||||
        {
 | 
			
		||||
            LOG_ERROR(Common, "After \"%s\", found %d (0x%X) instead of save marker %d (0x%X). Aborting savestate load...", prevName, cookie, cookie, arbitraryNumber, arbitraryNumber);
 | 
			
		||||
        if (mode == PointerWrap::MODE_READ && cookie != arbitraryNumber) {
 | 
			
		||||
            LOG_ERROR(Common, "After \"%s\", found %d (0x%X) instead of save marker %d (0x%X). "
 | 
			
		||||
                              "Aborting savestate load...",
 | 
			
		||||
                      prevName, cookie, cookie, arbitraryNumber, arbitraryNumber);
 | 
			
		||||
            SetError(ERROR_FAILURE);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,6 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
#include "common/memory_util.h"
 | 
			
		||||
 | 
			
		||||
@ -14,24 +13,27 @@
 | 
			
		||||
// having to prefix them with gen-> or something similar.
 | 
			
		||||
// Example implementation:
 | 
			
		||||
// class JIT : public CodeBlock<ARMXEmitter> {}
 | 
			
		||||
template<class T> class CodeBlock : public T, NonCopyable
 | 
			
		||||
{
 | 
			
		||||
template <class T>
 | 
			
		||||
class CodeBlock : public T, NonCopyable {
 | 
			
		||||
private:
 | 
			
		||||
    // 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;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    u8 *region;
 | 
			
		||||
    u8* region;
 | 
			
		||||
    size_t region_size;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    CodeBlock() : region(nullptr), region_size(0) {}
 | 
			
		||||
    virtual ~CodeBlock() { if (region) FreeCodeSpace(); }
 | 
			
		||||
    virtual ~CodeBlock() {
 | 
			
		||||
        if (region)
 | 
			
		||||
            FreeCodeSpace();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Call this before you generate any code.
 | 
			
		||||
    void AllocCodeSpace(int size)
 | 
			
		||||
    {
 | 
			
		||||
    void AllocCodeSpace(int size) {
 | 
			
		||||
        region_size = size;
 | 
			
		||||
        region = (u8*)AllocateExecutableMemory(region_size);
 | 
			
		||||
        T::SetCodePtr(region);
 | 
			
		||||
@ -39,15 +41,13 @@ public:
 | 
			
		||||
 | 
			
		||||
    // Always clear code space with breakpoints, so that if someone accidentally executes
 | 
			
		||||
    // uninitialized, it just breaks into the debugger.
 | 
			
		||||
    void ClearCodeSpace()
 | 
			
		||||
    {
 | 
			
		||||
    void ClearCodeSpace() {
 | 
			
		||||
        PoisonMemory();
 | 
			
		||||
        ResetCodePtr();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Call this when shutting down. Don't rely on the destructor, even though it'll do the job.
 | 
			
		||||
    void FreeCodeSpace()
 | 
			
		||||
    {
 | 
			
		||||
    void FreeCodeSpace() {
 | 
			
		||||
#ifdef __SYMBIAN32__
 | 
			
		||||
        ResetExecutableMemory(region);
 | 
			
		||||
#else
 | 
			
		||||
@ -57,33 +57,29 @@ public:
 | 
			
		||||
        region_size = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool IsInSpace(const u8 *ptr)
 | 
			
		||||
    {
 | 
			
		||||
    bool IsInSpace(const u8* ptr) {
 | 
			
		||||
        return (ptr >= region) && (ptr < (region + region_size));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Cannot currently be undone. Will write protect the entire code region.
 | 
			
		||||
    // Start over if you need to change the code (call FreeCodeSpace(), AllocCodeSpace()).
 | 
			
		||||
    void WriteProtect()
 | 
			
		||||
    {
 | 
			
		||||
    void WriteProtect() {
 | 
			
		||||
        WriteProtectMemory(region, region_size, true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void ResetCodePtr()
 | 
			
		||||
    {
 | 
			
		||||
    void ResetCodePtr() {
 | 
			
		||||
        T::SetCodePtr(region);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    size_t GetSpaceLeft() const
 | 
			
		||||
    {
 | 
			
		||||
    size_t GetSpaceLeft() const {
 | 
			
		||||
        return region_size - (T::GetCodePtr() - region);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    u8 *GetBasePtr() {
 | 
			
		||||
    u8* GetBasePtr() {
 | 
			
		||||
        return region;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    size_t GetOffset(const u8 *ptr) const {
 | 
			
		||||
    size_t GetOffset(const u8* ptr) const {
 | 
			
		||||
        return ptr - region;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -56,7 +56,7 @@ constexpr u8 Convert8To6(u8 value) {
 | 
			
		||||
 * @return Result color decoded as Math::Vec4<u8>
 | 
			
		||||
 */
 | 
			
		||||
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>
 | 
			
		||||
 */
 | 
			
		||||
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>
 | 
			
		||||
 */
 | 
			
		||||
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) {
 | 
			
		||||
    const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes);
 | 
			
		||||
    return { Convert5To8((pixel >> 11) & 0x1F), Convert6To8((pixel >> 5) & 0x3F),
 | 
			
		||||
        Convert5To8(pixel & 0x1F), 255 };
 | 
			
		||||
    return {Convert5To8((pixel >> 11) & 0x1F), Convert6To8((pixel >> 5) & 0x3F),
 | 
			
		||||
            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) {
 | 
			
		||||
    const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes);
 | 
			
		||||
    return { Convert5To8((pixel >> 11) & 0x1F), Convert5To8((pixel >> 6) & 0x1F),
 | 
			
		||||
        Convert5To8((pixel >> 1) & 0x1F), Convert1To8(pixel & 0x1) };
 | 
			
		||||
    return {Convert5To8((pixel >> 11) & 0x1F), Convert5To8((pixel >> 6) & 0x1F),
 | 
			
		||||
            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) {
 | 
			
		||||
    const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes);
 | 
			
		||||
    return { Convert4To8((pixel >> 12) & 0xF), Convert4To8((pixel >> 8) & 0xF),
 | 
			
		||||
        Convert4To8((pixel >> 4) & 0xF), Convert4To8(pixel & 0xF) };
 | 
			
		||||
    return {Convert4To8((pixel >> 12) & 0xF), Convert4To8((pixel >> 8) & 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
 | 
			
		||||
 */
 | 
			
		||||
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
 | 
			
		||||
 */
 | 
			
		||||
inline void EncodeRGB565(const Math::Vec4<u8>& color, u8* bytes) {
 | 
			
		||||
    *reinterpret_cast<u16_le*>(bytes) = (Convert8To5(color.r()) << 11) |
 | 
			
		||||
        (Convert8To6(color.g()) << 5) | Convert8To5(color.b());
 | 
			
		||||
    *reinterpret_cast<u16_le*>(bytes) =
 | 
			
		||||
        (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) {
 | 
			
		||||
    *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) {
 | 
			
		||||
    *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());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 | 
			
		||||
@ -7,14 +7,13 @@
 | 
			
		||||
#if !defined(ARCHITECTURE_x86_64) && !defined(_M_ARM)
 | 
			
		||||
#include <cstdlib> // for exit
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "common_types.h"
 | 
			
		||||
 | 
			
		||||
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
 | 
			
		||||
 | 
			
		||||
/// Textually concatenates two tokens. The double-expansion is required by the C preprocessor.
 | 
			
		||||
#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.
 | 
			
		||||
// Calling INSERT_PADDING_BYTES will add a new member variable with a name like "pad121",
 | 
			
		||||
@ -24,9 +23,9 @@
 | 
			
		||||
 | 
			
		||||
// Inlining
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
    #define FORCE_INLINE __forceinline
 | 
			
		||||
#define FORCE_INLINE __forceinline
 | 
			
		||||
#else
 | 
			
		||||
    #define FORCE_INLINE inline __attribute__((always_inline))
 | 
			
		||||
#define FORCE_INLINE inline __attribute__((always_inline))
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef _MSC_VER
 | 
			
		||||
@ -46,7 +45,8 @@
 | 
			
		||||
#else
 | 
			
		||||
inline u32 rotl(u32 x, int shift) {
 | 
			
		||||
    shift &= 31;
 | 
			
		||||
    if (!shift) return x;
 | 
			
		||||
    if (!shift)
 | 
			
		||||
        return x;
 | 
			
		||||
    return (x << shift) | (x >> (32 - shift));
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
@ -56,17 +56,18 @@ inline u32 rotl(u32 x, int shift) {
 | 
			
		||||
#else
 | 
			
		||||
inline u32 rotr(u32 x, int shift) {
 | 
			
		||||
    shift &= 31;
 | 
			
		||||
    if (!shift) return x;
 | 
			
		||||
    if (!shift)
 | 
			
		||||
        return x;
 | 
			
		||||
    return (x >> shift) | (x << (32 - shift));
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
inline u64 _rotl64(u64 x, unsigned int shift){
 | 
			
		||||
inline u64 _rotl64(u64 x, unsigned int shift) {
 | 
			
		||||
    unsigned int n = shift % 64;
 | 
			
		||||
    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;
 | 
			
		||||
    return (x >> n) | (x << (64 - n));
 | 
			
		||||
}
 | 
			
		||||
@ -74,17 +75,17 @@ inline u64 _rotr64(u64 x, unsigned int shift){
 | 
			
		||||
#else // _MSC_VER
 | 
			
		||||
 | 
			
		||||
#if (_MSC_VER < 1900)
 | 
			
		||||
    // Function Cross-Compatibility
 | 
			
		||||
    #define snprintf _snprintf
 | 
			
		||||
// Function Cross-Compatibility
 | 
			
		||||
#define snprintf _snprintf
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// Locale Cross-Compatibility
 | 
			
		||||
#define locale_t _locale_t
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
#define rotr _rotr
 | 
			
		||||
 | 
			
		||||
@ -16,13 +16,13 @@
 | 
			
		||||
#define ROOT_DIR "."
 | 
			
		||||
#define USERDATA_DIR "user"
 | 
			
		||||
#ifdef USER_DIR
 | 
			
		||||
    #define EMU_DATA_DIR USER_DIR
 | 
			
		||||
#define EMU_DATA_DIR USER_DIR
 | 
			
		||||
#else
 | 
			
		||||
    #ifdef _WIN32
 | 
			
		||||
        #define EMU_DATA_DIR "Citra Emulator"
 | 
			
		||||
    #else
 | 
			
		||||
        #define EMU_DATA_DIR "citra-emu"
 | 
			
		||||
    #endif
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
#define EMU_DATA_DIR "Citra Emulator"
 | 
			
		||||
#else
 | 
			
		||||
#define EMU_DATA_DIR "citra-emu"
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// Dirs in both User and Sys
 | 
			
		||||
@ -31,32 +31,32 @@
 | 
			
		||||
#define JAP_DIR "JAP"
 | 
			
		||||
 | 
			
		||||
// Subdirs in the User dir returned by GetUserPath(D_USER_IDX)
 | 
			
		||||
#define CONFIG_DIR               "config"
 | 
			
		||||
#define GAMECONFIG_DIR           "game_config"
 | 
			
		||||
#define MAPS_DIR                 "maps"
 | 
			
		||||
#define CACHE_DIR                "cache"
 | 
			
		||||
#define SDMC_DIR                 "sdmc"
 | 
			
		||||
#define NAND_DIR                 "nand"
 | 
			
		||||
#define SYSDATA_DIR              "sysdata"
 | 
			
		||||
#define SHADERCACHE_DIR          "shader_cache"
 | 
			
		||||
#define STATESAVES_DIR           "state_saves"
 | 
			
		||||
#define SCREENSHOTS_DIR          "screenShots"
 | 
			
		||||
#define DUMP_DIR                 "dump"
 | 
			
		||||
#define DUMP_TEXTURES_DIR        "textures"
 | 
			
		||||
#define DUMP_FRAMES_DIR          "frames"
 | 
			
		||||
#define DUMP_AUDIO_DIR           "audio"
 | 
			
		||||
#define LOGS_DIR                 "logs"
 | 
			
		||||
#define SHADERS_DIR              "shaders"
 | 
			
		||||
#define SYSCONF_DIR              "sysconf"
 | 
			
		||||
#define CONFIG_DIR "config"
 | 
			
		||||
#define GAMECONFIG_DIR "game_config"
 | 
			
		||||
#define MAPS_DIR "maps"
 | 
			
		||||
#define CACHE_DIR "cache"
 | 
			
		||||
#define SDMC_DIR "sdmc"
 | 
			
		||||
#define NAND_DIR "nand"
 | 
			
		||||
#define SYSDATA_DIR "sysdata"
 | 
			
		||||
#define SHADERCACHE_DIR "shader_cache"
 | 
			
		||||
#define STATESAVES_DIR "state_saves"
 | 
			
		||||
#define SCREENSHOTS_DIR "screenShots"
 | 
			
		||||
#define DUMP_DIR "dump"
 | 
			
		||||
#define DUMP_TEXTURES_DIR "textures"
 | 
			
		||||
#define DUMP_FRAMES_DIR "frames"
 | 
			
		||||
#define DUMP_AUDIO_DIR "audio"
 | 
			
		||||
#define LOGS_DIR "logs"
 | 
			
		||||
#define SHADERS_DIR "shaders"
 | 
			
		||||
#define SYSCONF_DIR "sysconf"
 | 
			
		||||
 | 
			
		||||
// Filenames
 | 
			
		||||
// Files in the directory returned by GetUserPath(D_CONFIG_IDX)
 | 
			
		||||
#define EMU_CONFIG        "emu.ini"
 | 
			
		||||
#define DEBUGGER_CONFIG   "debugger.ini"
 | 
			
		||||
#define LOGGER_CONFIG     "logger.ini"
 | 
			
		||||
#define EMU_CONFIG "emu.ini"
 | 
			
		||||
#define DEBUGGER_CONFIG "debugger.ini"
 | 
			
		||||
#define LOGGER_CONFIG "logger.ini"
 | 
			
		||||
 | 
			
		||||
// Sys files
 | 
			
		||||
#define SHARED_FONT       "shared_font.bin"
 | 
			
		||||
#define SHARED_FONT "shared_font.bin"
 | 
			
		||||
 | 
			
		||||
// Files in the directory returned by GetUserPath(D_LOGS_IDX)
 | 
			
		||||
#define MAIN_LOG "emu.log"
 | 
			
		||||
 | 
			
		||||
@ -32,18 +32,18 @@
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
typedef std::uint8_t  u8;  ///< 8-bit unsigned byte
 | 
			
		||||
typedef std::uint8_t u8;   ///< 8-bit unsigned byte
 | 
			
		||||
typedef std::uint16_t u16; ///< 16-bit unsigned short
 | 
			
		||||
typedef std::uint32_t u32; ///< 32-bit unsigned word
 | 
			
		||||
typedef std::uint64_t u64; ///< 64-bit unsigned int
 | 
			
		||||
 | 
			
		||||
typedef std::int8_t  s8;  ///< 8-bit signed byte
 | 
			
		||||
typedef std::int8_t s8;   ///< 8-bit signed byte
 | 
			
		||||
typedef std::int16_t s16; ///< 16-bit signed short
 | 
			
		||||
typedef std::int32_t s32; ///< 32-bit signed word
 | 
			
		||||
typedef std::int64_t s64; ///< 64-bit signed int
 | 
			
		||||
 | 
			
		||||
typedef float   f32; ///< 32-bit floating point
 | 
			
		||||
typedef double  f64; ///< 64-bit floating point
 | 
			
		||||
typedef float f32;  ///< 32-bit floating point
 | 
			
		||||
typedef double f64; ///< 64-bit floating point
 | 
			
		||||
 | 
			
		||||
// TODO: It would be nice to eventually replace these with strong types that prevent accidental
 | 
			
		||||
// conversion between each other.
 | 
			
		||||
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue
	
	Block a user