mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +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
					
				
					 401 changed files with 19654 additions and 18552 deletions
				
			
		|  | @ -1,4 +1,4 @@ | ||||||
| #!/bin/sh | #!/bin/bash | ||||||
| 
 | 
 | ||||||
| set -e | set -e | ||||||
| set -x | set -x | ||||||
|  | @ -9,6 +9,39 @@ if grep -nr '\s$' src *.yml *.txt *.md Doxyfile .gitignore .gitmodules .travis* | ||||||
|     exit 1 |     exit 1 | ||||||
| fi | fi | ||||||
| 
 | 
 | ||||||
|  | # Only run clang-format on Linux because we don't have 4.0 on OS X images | ||||||
|  | if [ "$TRAVIS_OS_NAME" = "linux" ]; then | ||||||
|  |     # Default clang-format points to default 3.5 version one | ||||||
|  |     CLANG_FORMAT=clang-format-4.0 | ||||||
|  |     $CLANG_FORMAT --version | ||||||
|  | 
 | ||||||
|  |     if [ "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then | ||||||
|  |         # Get list of every file modified in this pull request | ||||||
|  |         files_to_lint="$(git diff --name-only --diff-filter=ACMRTUXB $TRAVIS_COMMIT_RANGE | grep '^src/[^.]*[.]\(cpp\|h\)$')" | ||||||
|  |     else | ||||||
|  |         # Check everything for branch pushes | ||||||
|  |         files_to_lint="$(find src/ -name '*.cpp' -or -name '*.h')" | ||||||
|  |     fi | ||||||
|  | 
 | ||||||
|  |     # Turn off tracing for this because it's too verbose | ||||||
|  |     set +x | ||||||
|  | 
 | ||||||
|  |     for f in $files_to_lint; do | ||||||
|  |         d=$(diff -u "$f" <($CLANG_FORMAT "$f")) | ||||||
|  |         if ! [ -z "$d" ]; then | ||||||
|  |             echo "!!! $f not compliant to coding style, here is the fix:" | ||||||
|  |             echo "$d" | ||||||
|  |             fail=1 | ||||||
|  |         fi | ||||||
|  |     done | ||||||
|  | 
 | ||||||
|  |     set -x | ||||||
|  | 
 | ||||||
|  |     if [ "$fail" = 1 ]; then | ||||||
|  |         exit 1 | ||||||
|  |     fi | ||||||
|  | fi | ||||||
|  | 
 | ||||||
| #if OS is linux or is not set | #if OS is linux or is not set | ||||||
| if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then | if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then | ||||||
|     export CC=gcc-6 |     export CC=gcc-6 | ||||||
|  |  | ||||||
|  | @ -17,6 +17,7 @@ addons: | ||||||
|   apt: |   apt: | ||||||
|     sources: |     sources: | ||||||
|       - ubuntu-toolchain-r-test |       - ubuntu-toolchain-r-test | ||||||
|  |       - llvm-toolchain-precise | ||||||
|     packages: |     packages: | ||||||
|       - gcc-6 |       - gcc-6 | ||||||
|       - g++-6 |       - g++-6 | ||||||
|  | @ -25,6 +26,7 @@ addons: | ||||||
|       - xorg-dev |       - xorg-dev | ||||||
|       - lib32stdc++6 # For CMake |       - lib32stdc++6 # For CMake | ||||||
|       - lftp # To upload builds |       - lftp # To upload builds | ||||||
|  |       - clang-format-4.0 | ||||||
| 
 | 
 | ||||||
| cache: | cache: | ||||||
|   directories: |   directories: | ||||||
|  |  | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| #!/bin/sh | #!/bin/bash | ||||||
| 
 | 
 | ||||||
| # Enforce citra's whitespace policy | # Enforce citra's whitespace policy | ||||||
| git config --local core.whitespace tab-in-indent,trailing-space | git config --local core.whitespace tab-in-indent,trailing-space | ||||||
|  | @ -7,7 +7,7 @@ paths_to_check="src/ CMakeLists.txt" | ||||||
| 
 | 
 | ||||||
| # If there are whitespace errors, print the offending file names and fail. | # If there are whitespace errors, print the offending file names and fail. | ||||||
| if ! git diff --cached --check -- $paths_to_check ; then | if ! git diff --cached --check -- $paths_to_check ; then | ||||||
|     cat<<END; |     cat<<END | ||||||
| 
 | 
 | ||||||
| Error: This commit would contain trailing spaces or tabs, which is against this repo's policy. | Error: This commit would contain trailing spaces or tabs, which is against this repo's policy. | ||||||
| Please correct those issues before commiting. (Use 'git diff --check' for more details) | Please correct those issues before commiting. (Use 'git diff --check' for more details) | ||||||
|  | @ -18,9 +18,26 @@ fi | ||||||
| 
 | 
 | ||||||
| # Check for tabs, since tab-in-indent catches only those at the beginning of a line | # Check for tabs, since tab-in-indent catches only those at the beginning of a line | ||||||
| if git diff --cached -- $paths_to_check | egrep '^\+.*	'; then | if git diff --cached -- $paths_to_check | egrep '^\+.*	'; then | ||||||
|     cat<<END; |     cat<<END | ||||||
| Error: This commit would contain a tab, which is against this repo's policy. | Error: This commit would contain a tab, which is against this repo's policy. | ||||||
| If you know what you are doing, you can try 'git commit --no-verify' to bypass the check. | If you know what you are doing, you can try 'git commit --no-verify' to bypass the check. | ||||||
| END | END | ||||||
|     exit 1 |     exit 1 | ||||||
| fi | fi | ||||||
|  | 
 | ||||||
|  | for f in $(git diff --name-only --diff-filter=ACMRTUXB --cached); do | ||||||
|  |     if ! echo "$f" | egrep -q "[.](cpp|h)$"; then | ||||||
|  |         continue | ||||||
|  |     fi | ||||||
|  |     if ! echo "$f" | egrep -q "^src/"; then | ||||||
|  |         continue | ||||||
|  |     fi | ||||||
|  |     d=$(diff -u "$f" <(clang-format "$f")) | ||||||
|  |     if ! [ -z "$d" ]; then | ||||||
|  |         echo "!!! $f not compliant to coding style, here is the fix:" | ||||||
|  |         echo "$d" | ||||||
|  |         fail=1 | ||||||
|  |     fi | ||||||
|  | done | ||||||
|  | 
 | ||||||
|  | exit "$fail" | ||||||
|  |  | ||||||
							
								
								
									
										88
									
								
								src/.clang-format
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										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 <memory> | ||||||
| #include <string> | #include <string> | ||||||
| 
 |  | ||||||
| #include "audio_core/audio_core.h" | #include "audio_core/audio_core.h" | ||||||
| #include "audio_core/hle/dsp.h" | #include "audio_core/hle/dsp.h" | ||||||
| #include "audio_core/hle/pipe.h" | #include "audio_core/hle/pipe.h" | ||||||
| #include "audio_core/null_sink.h" | #include "audio_core/null_sink.h" | ||||||
| #include "audio_core/sink.h" | #include "audio_core/sink.h" | ||||||
| #include "audio_core/sink_details.h" | #include "audio_core/sink_details.h" | ||||||
| 
 |  | ||||||
| #include "core/core_timing.h" | #include "core/core_timing.h" | ||||||
| #include "core/hle/kernel/vm_manager.h" | #include "core/hle/kernel/vm_manager.h" | ||||||
| #include "core/hle/service/dsp_dsp.h" | #include "core/hle/service/dsp_dsp.h" | ||||||
|  | @ -42,10 +40,18 @@ void Init() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void AddAddressSpace(Kernel::VMManager& address_space) { | void AddAddressSpace(Kernel::VMManager& address_space) { | ||||||
|     auto r0_vma = address_space.MapBackingMemory(DSP::HLE::region0_base, reinterpret_cast<u8*>(&DSP::HLE::g_regions[0]), sizeof(DSP::HLE::SharedMemory), Kernel::MemoryState::IO).MoveFrom(); |     auto r0_vma = address_space | ||||||
|  |                       .MapBackingMemory(DSP::HLE::region0_base, | ||||||
|  |                                         reinterpret_cast<u8*>(&DSP::HLE::g_regions[0]), | ||||||
|  |                                         sizeof(DSP::HLE::SharedMemory), Kernel::MemoryState::IO) | ||||||
|  |                       .MoveFrom(); | ||||||
|     address_space.Reprotect(r0_vma, Kernel::VMAPermission::ReadWrite); |     address_space.Reprotect(r0_vma, Kernel::VMAPermission::ReadWrite); | ||||||
| 
 | 
 | ||||||
|     auto r1_vma = address_space.MapBackingMemory(DSP::HLE::region1_base, reinterpret_cast<u8*>(&DSP::HLE::g_regions[1]), sizeof(DSP::HLE::SharedMemory), Kernel::MemoryState::IO).MoveFrom(); |     auto r1_vma = address_space | ||||||
|  |                       .MapBackingMemory(DSP::HLE::region1_base, | ||||||
|  |                                         reinterpret_cast<u8*>(&DSP::HLE::g_regions[1]), | ||||||
|  |                                         sizeof(DSP::HLE::SharedMemory), Kernel::MemoryState::IO) | ||||||
|  |                       .MoveFrom(); | ||||||
|     address_space.Reprotect(r1_vma, Kernel::VMAPermission::ReadWrite); |     address_space.Reprotect(r1_vma, Kernel::VMAPermission::ReadWrite); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -58,9 +64,9 @@ void SelectSink(std::string sink_id) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     auto iter = std::find_if(g_sink_details.begin(), g_sink_details.end(), [sink_id](const auto& sink_detail) { |     auto iter = | ||||||
|         return sink_detail.id == sink_id; |         std::find_if(g_sink_details.begin(), g_sink_details.end(), | ||||||
|     }); |                      [sink_id](const auto& sink_detail) { return sink_detail.id == sink_id; }); | ||||||
| 
 | 
 | ||||||
|     if (iter == g_sink_details.end()) { |     if (iter == g_sink_details.end()) { | ||||||
|         LOG_ERROR(Audio, "AudioCore::SelectSink given invalid sink_id"); |         LOG_ERROR(Audio, "AudioCore::SelectSink given invalid sink_id"); | ||||||
|  |  | ||||||
|  | @ -12,7 +12,7 @@ class VMManager; | ||||||
| 
 | 
 | ||||||
| namespace AudioCore { | namespace AudioCore { | ||||||
| 
 | 
 | ||||||
| constexpr int native_sample_rate = 32728;  ///< 32kHz
 | constexpr int native_sample_rate = 32728; ///< 32kHz
 | ||||||
| 
 | 
 | ||||||
| /// Initialise Audio Core
 | /// Initialise Audio Core
 | ||||||
| void Init(); | void Init(); | ||||||
|  |  | ||||||
|  | @ -6,31 +6,32 @@ | ||||||
| #include <cstddef> | #include <cstddef> | ||||||
| #include <cstring> | #include <cstring> | ||||||
| #include <vector> | #include <vector> | ||||||
| 
 |  | ||||||
| #include "audio_core/codec.h" | #include "audio_core/codec.h" | ||||||
| 
 |  | ||||||
| #include "common/assert.h" | #include "common/assert.h" | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "common/math_util.h" | #include "common/math_util.h" | ||||||
| 
 | 
 | ||||||
| namespace Codec { | namespace Codec { | ||||||
| 
 | 
 | ||||||
| StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count, const std::array<s16, 16>& adpcm_coeff, ADPCMState& state) { | StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count, | ||||||
|  |                            const std::array<s16, 16>& adpcm_coeff, ADPCMState& state) { | ||||||
|     // GC-ADPCM with scale factor and variable coefficients.
 |     // GC-ADPCM with scale factor and variable coefficients.
 | ||||||
|     // Frames are 8 bytes long containing 14 samples each.
 |     // Frames are 8 bytes long containing 14 samples each.
 | ||||||
|     // Samples are 4 bits (one nibble) long.
 |     // Samples are 4 bits (one nibble) long.
 | ||||||
| 
 | 
 | ||||||
|     constexpr size_t FRAME_LEN = 8; |     constexpr size_t FRAME_LEN = 8; | ||||||
|     constexpr size_t SAMPLES_PER_FRAME = 14; |     constexpr size_t SAMPLES_PER_FRAME = 14; | ||||||
|     constexpr std::array<int, 16> SIGNED_NIBBLES {{ 0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1 }}; |     constexpr std::array<int, 16> SIGNED_NIBBLES = { | ||||||
|  |         {0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1}}; | ||||||
| 
 | 
 | ||||||
|     const size_t ret_size = sample_count % 2 == 0 ? sample_count : sample_count + 1; // Ensure multiple of two.
 |     const size_t ret_size = | ||||||
|  |         sample_count % 2 == 0 ? sample_count : sample_count + 1; // Ensure multiple of two.
 | ||||||
|     StereoBuffer16 ret(ret_size); |     StereoBuffer16 ret(ret_size); | ||||||
| 
 | 
 | ||||||
|     int yn1 = state.yn1, |     int yn1 = state.yn1, yn2 = state.yn2; | ||||||
|         yn2 = state.yn2; |  | ||||||
| 
 | 
 | ||||||
|     const size_t NUM_FRAMES = (sample_count + (SAMPLES_PER_FRAME - 1)) / SAMPLES_PER_FRAME; // Round up.
 |     const size_t NUM_FRAMES = | ||||||
|  |         (sample_count + (SAMPLES_PER_FRAME - 1)) / SAMPLES_PER_FRAME; // Round up.
 | ||||||
|     for (size_t framei = 0; framei < NUM_FRAMES; framei++) { |     for (size_t framei = 0; framei < NUM_FRAMES; framei++) { | ||||||
|         const int frame_header = data[framei * FRAME_LEN]; |         const int frame_header = data[framei * FRAME_LEN]; | ||||||
|         const int scale = 1 << (frame_header & 0xF); |         const int scale = 1 << (frame_header & 0xF); | ||||||
|  | @ -43,7 +44,8 @@ StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count, cons | ||||||
|         // Decodes an audio sample. One nibble produces one sample.
 |         // Decodes an audio sample. One nibble produces one sample.
 | ||||||
|         const auto decode_sample = [&](const int nibble) -> s16 { |         const auto decode_sample = [&](const int nibble) -> s16 { | ||||||
|             const int xn = nibble * scale; |             const int xn = nibble * scale; | ||||||
|             // We first transform everything into 11 bit fixed point, perform the second order digital filter, then transform back.
 |             // We first transform everything into 11 bit fixed point, perform the second order
 | ||||||
|  |             // digital filter, then transform back.
 | ||||||
|             // 0x400 == 0.5 in 11 bit fixed point.
 |             // 0x400 == 0.5 in 11 bit fixed point.
 | ||||||
|             // Filter: y[n] = x[n] + 0.5 + c1 * y[n-1] + c2 * y[n-2]
 |             // Filter: y[n] = x[n] + 0.5 + c1 * y[n-1] + c2 * y[n-2]
 | ||||||
|             int val = ((xn << 11) + 0x400 + coef1 * yn1 + coef2 * yn2) >> 11; |             int val = ((xn << 11) + 0x400 + coef1 * yn1 + coef2 * yn2) >> 11; | ||||||
|  | @ -82,7 +84,8 @@ static s16 SignExtendS8(u8 x) { | ||||||
|     return static_cast<s16>(static_cast<s8>(x)); |     return static_cast<s16>(static_cast<s8>(x)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data, const size_t sample_count) { | StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data, | ||||||
|  |                           const size_t sample_count) { | ||||||
|     ASSERT(num_channels == 1 || num_channels == 2); |     ASSERT(num_channels == 1 || num_channels == 2); | ||||||
| 
 | 
 | ||||||
|     StereoBuffer16 ret(sample_count); |     StereoBuffer16 ret(sample_count); | ||||||
|  | @ -101,7 +104,8 @@ StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data, con | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data, const size_t sample_count) { | StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data, | ||||||
|  |                            const size_t sample_count) { | ||||||
|     ASSERT(num_channels == 1 || num_channels == 2); |     ASSERT(num_channels == 1 || num_channels == 2); | ||||||
| 
 | 
 | ||||||
|     StereoBuffer16 ret(sample_count); |     StereoBuffer16 ret(sample_count); | ||||||
|  | @ -118,5 +122,4 @@ StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data, co | ||||||
| 
 | 
 | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
| 
 |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -6,7 +6,6 @@ | ||||||
| 
 | 
 | ||||||
| #include <array> | #include <array> | ||||||
| #include <vector> | #include <vector> | ||||||
| 
 |  | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| 
 | 
 | ||||||
| namespace Codec { | namespace Codec { | ||||||
|  | @ -29,7 +28,8 @@ struct ADPCMState { | ||||||
|  * @param state ADPCM state, this is updated with new state |  * @param state ADPCM state, this is updated with new state | ||||||
|  * @return Decoded stereo signed PCM16 data, sample_count in length |  * @return Decoded stereo signed PCM16 data, sample_count in length | ||||||
|  */ |  */ | ||||||
| StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count, const std::array<s16, 16>& adpcm_coeff, ADPCMState& state); | StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count, | ||||||
|  |                            const std::array<s16, 16>& adpcm_coeff, ADPCMState& state); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * @param num_channels Number of channels |  * @param num_channels Number of channels | ||||||
|  | @ -37,7 +37,8 @@ StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count, cons | ||||||
|  * @param sample_count Length of buffer in terms of number of samples |  * @param sample_count Length of buffer in terms of number of samples | ||||||
|  * @return Decoded stereo signed PCM16 data, sample_count in length |  * @return Decoded stereo signed PCM16 data, sample_count in length | ||||||
|  */ |  */ | ||||||
| StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data, const size_t sample_count); | StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data, | ||||||
|  |                           const size_t sample_count); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * @param num_channels Number of channels |  * @param num_channels Number of channels | ||||||
|  | @ -45,6 +46,6 @@ StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data, con | ||||||
|  * @param sample_count Length of buffer in terms of number of samples |  * @param sample_count Length of buffer in terms of number of samples | ||||||
|  * @return Decoded stereo signed PCM16 data, sample_count in length |  * @return Decoded stereo signed PCM16 data, sample_count in length | ||||||
|  */ |  */ | ||||||
| StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data, const size_t sample_count); | StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data, | ||||||
| 
 |                            const size_t sample_count); | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -6,30 +6,28 @@ | ||||||
| 
 | 
 | ||||||
| #include <algorithm> | #include <algorithm> | ||||||
| #include <array> | #include <array> | ||||||
| 
 |  | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| 
 | 
 | ||||||
| namespace DSP { | namespace DSP { | ||||||
| namespace HLE { | namespace HLE { | ||||||
| 
 | 
 | ||||||
| constexpr int num_sources = 24; | 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.
 | /// 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>; | using StereoFrame16 = std::array<std::array<s16, 2>, samples_per_frame>; | ||||||
| 
 | 
 | ||||||
| /// The DSP is quadraphonic internally.
 | /// 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. |  * This performs the filter operation defined by FilterT::ProcessSample on the frame in-place. | ||||||
|  * FilterT::ProcessSample is called sequentially on the samples. |  * FilterT::ProcessSample is called sequentially on the samples. | ||||||
|  */ |  */ | ||||||
| template<typename FrameT, typename FilterT> | template <typename FrameT, typename FilterT> | ||||||
| void FilterFrame(FrameT& frame, FilterT& filter) { | void FilterFrame(FrameT& frame, FilterT& filter) { | ||||||
|     std::transform(frame.begin(), frame.end(), frame.begin(), [&filter](const auto& sample) { |     std::transform(frame.begin(), frame.end(), frame.begin(), | ||||||
|         return filter.ProcessSample(sample); |                    [&filter](const auto& sample) { return filter.ProcessSample(sample); }); | ||||||
|     }); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| } // namespace HLE
 | } // namespace HLE
 | ||||||
|  |  | ||||||
|  | @ -4,7 +4,6 @@ | ||||||
| 
 | 
 | ||||||
| #include <array> | #include <array> | ||||||
| #include <memory> | #include <memory> | ||||||
| 
 |  | ||||||
| #include "audio_core/hle/dsp.h" | #include "audio_core/hle/dsp.h" | ||||||
| #include "audio_core/hle/mixers.h" | #include "audio_core/hle/mixers.h" | ||||||
| #include "audio_core/hle/pipe.h" | #include "audio_core/hle/pipe.h" | ||||||
|  | @ -47,10 +46,9 @@ static SharedMemory& WriteRegion() { | ||||||
| // Audio processing and mixing
 | // Audio processing and mixing
 | ||||||
| 
 | 
 | ||||||
| static std::array<Source, num_sources> sources = { | static std::array<Source, num_sources> sources = { | ||||||
|     Source(0), Source(1), Source(2), Source(3), Source(4), Source(5), |     Source(0),  Source(1),  Source(2),  Source(3),  Source(4),  Source(5),  Source(6),  Source(7), | ||||||
|     Source(6), Source(7), Source(8), Source(9), Source(10), Source(11), |     Source(8),  Source(9),  Source(10), Source(11), Source(12), Source(13), Source(14), Source(15), | ||||||
|     Source(12), Source(13), Source(14), Source(15), Source(16), Source(17), |     Source(16), Source(17), Source(18), Source(19), Source(20), Source(21), Source(22), Source(23), | ||||||
|     Source(18), Source(19), Source(20), Source(21), Source(22), Source(23) |  | ||||||
| }; | }; | ||||||
| static Mixers mixers; | static Mixers mixers; | ||||||
| 
 | 
 | ||||||
|  | @ -62,14 +60,16 @@ static StereoFrame16 GenerateCurrentFrame() { | ||||||
| 
 | 
 | ||||||
|     // Generate intermediate mixes
 |     // Generate intermediate mixes
 | ||||||
|     for (size_t i = 0; i < num_sources; i++) { |     for (size_t i = 0; i < num_sources; i++) { | ||||||
|         write.source_statuses.status[i] = sources[i].Tick(read.source_configurations.config[i], read.adpcm_coefficients.coeff[i]); |         write.source_statuses.status[i] = | ||||||
|  |             sources[i].Tick(read.source_configurations.config[i], read.adpcm_coefficients.coeff[i]); | ||||||
|         for (size_t mix = 0; mix < 3; mix++) { |         for (size_t mix = 0; mix < 3; mix++) { | ||||||
|             sources[i].MixInto(intermediate_mixes[mix], mix); |             sources[i].MixInto(intermediate_mixes[mix], mix); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Generate final mix
 |     // Generate final mix
 | ||||||
|     write.dsp_status = mixers.Tick(read.dsp_configuration, read.intermediate_mix_samples, write.intermediate_mix_samples, intermediate_mixes); |     write.dsp_status = mixers.Tick(read.dsp_configuration, read.intermediate_mix_samples, | ||||||
|  |                                    write.intermediate_mix_samples, intermediate_mixes); | ||||||
| 
 | 
 | ||||||
|     StereoFrame16 output_frame = mixers.GetOutput(); |     StereoFrame16 output_frame = mixers.GetOutput(); | ||||||
| 
 | 
 | ||||||
|  | @ -152,7 +152,8 @@ void Shutdown() { | ||||||
| bool Tick() { | bool Tick() { | ||||||
|     StereoFrame16 current_frame = {}; |     StereoFrame16 current_frame = {}; | ||||||
| 
 | 
 | ||||||
|     // TODO: Check dsp::DSP semaphore (which indicates emulated application has finished writing to shared memory region)
 |     // TODO: Check dsp::DSP semaphore (which indicates emulated application has finished writing to
 | ||||||
|  |     // shared memory region)
 | ||||||
|     current_frame = GenerateCurrentFrame(); |     current_frame = GenerateCurrentFrame(); | ||||||
| 
 | 
 | ||||||
|     OutputCurrentFrame(current_frame); |     OutputCurrentFrame(current_frame); | ||||||
|  |  | ||||||
|  | @ -8,9 +8,7 @@ | ||||||
| #include <cstddef> | #include <cstddef> | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <type_traits> | #include <type_traits> | ||||||
| 
 |  | ||||||
| #include "audio_core/hle/common.h" | #include "audio_core/hle/common.h" | ||||||
| 
 |  | ||||||
| #include "common/bit_field.h" | #include "common/bit_field.h" | ||||||
| #include "common/common_funcs.h" | #include "common/common_funcs.h" | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
|  | @ -23,15 +21,15 @@ class Sink; | ||||||
| namespace DSP { | namespace DSP { | ||||||
| namespace HLE { | namespace HLE { | ||||||
| 
 | 
 | ||||||
| // The application-accessible region of DSP memory consists of two parts.
 | // The application-accessible region of DSP memory consists of two parts. Both are marked as IO and
 | ||||||
| // Both are marked as IO and have Read/Write permissions.
 | // have Read/Write permissions.
 | ||||||
| //
 | //
 | ||||||
| // First Region:  0x1FF50000 (Size: 0x8000)
 | // First Region:  0x1FF50000 (Size: 0x8000)
 | ||||||
| // Second Region: 0x1FF70000 (Size: 0x8000)
 | // Second Region: 0x1FF70000 (Size: 0x8000)
 | ||||||
| //
 | //
 | ||||||
| // The DSP reads from each region alternately based on the frame counter for each region much like a
 | // The DSP reads from each region alternately based on the frame counter for each region much like a
 | ||||||
| // double-buffer. The frame counter is located as the very last u16 of each region and is incremented
 | // double-buffer. The frame counter is located as the very last u16 of each region and is
 | ||||||
| // each audio tick.
 | // incremented each audio tick.
 | ||||||
| 
 | 
 | ||||||
| constexpr VAddr region0_base = 0x1FF50000; | constexpr VAddr region0_base = 0x1FF50000; | ||||||
| constexpr VAddr region1_base = 0x1FF70000; | constexpr VAddr region1_base = 0x1FF70000; | ||||||
|  | @ -56,6 +54,7 @@ struct u32_dsp { | ||||||
|     void operator=(u32 new_value) { |     void operator=(u32 new_value) { | ||||||
|         storage = Convert(new_value); |         storage = Convert(new_value); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
|     static constexpr u32 Convert(u32 value) { |     static constexpr u32 Convert(u32 value) { | ||||||
|         return (value << 16) | (value >> 16); |         return (value << 16) | (value >> 16); | ||||||
|  | @ -89,13 +88,13 @@ static_assert(std::is_trivially_copyable<u32_dsp>::value, "u32_dsp isn't trivial | ||||||
| // #: This refers to the order in which they appear in the DspPipe::Audio DSP pipe.
 | // #: This refers to the order in which they appear in the DspPipe::Audio DSP pipe.
 | ||||||
| //    See also: DSP::HLE::PipeRead.
 | //    See also: DSP::HLE::PipeRead.
 | ||||||
| //
 | //
 | ||||||
| // Note that the above addresses do vary slightly between audio firmwares observed; the addresses are
 | // Note that the above addresses do vary slightly between audio firmwares observed; the addresses
 | ||||||
| // not fixed in stone. The addresses above are only an examplar; they're what this implementation
 | // are not fixed in stone. The addresses above are only an examplar; they're what this
 | ||||||
| // does and provides to applications.
 | // implementation does and provides to applications.
 | ||||||
| //
 | //
 | ||||||
| // Application requests the DSP service to convert DSP addresses into ARM11 virtual addresses using the
 | // Application requests the DSP service to convert DSP addresses into ARM11 virtual addresses using
 | ||||||
| // ConvertProcessAddressFromDspDram service call. Applications seem to derive the addresses for the
 | // the ConvertProcessAddressFromDspDram service call. Applications seem to derive the addresses for
 | ||||||
| // second region via:
 | // the second region via:
 | ||||||
| //     second_region_dsp_addr = first_region_dsp_addr | 0x10000
 | //     second_region_dsp_addr = first_region_dsp_addr | 0x10000
 | ||||||
| //
 | //
 | ||||||
| // Applications maintain most of its own audio state, the memory region is used mainly for
 | // Applications maintain most of its own audio state, the memory region is used mainly for
 | ||||||
|  | @ -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.
 | // 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
 | // (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)) | #define INSERT_PADDING_DSPWORDS(num_words) INSERT_PADDING_BYTES(2 * (num_words)) | ||||||
| 
 | 
 | ||||||
| // GCC versions < 5.0 do not implement std::is_trivially_copyable.
 | // GCC versions < 5.0 do not implement std::is_trivially_copyable.
 | ||||||
| // Excluding MSVC because it has weird behaviour for std::is_trivially_copyable.
 | // Excluding MSVC because it has weird behaviour for std::is_trivially_copyable.
 | ||||||
| #if (__GNUC__ >= 5) || defined(__clang__) | #if (__GNUC__ >= 5) || defined(__clang__) | ||||||
|     #define ASSERT_DSP_STRUCT(name, size) \ | #define ASSERT_DSP_STRUCT(name, size)                                                              \ | ||||||
|         static_assert(std::is_standard_layout<name>::value, "DSP structure " #name " doesn't use standard layout"); \ |     static_assert(std::is_standard_layout<name>::value,                                            \ | ||||||
|         static_assert(std::is_trivially_copyable<name>::value, "DSP structure " #name " isn't trivially copyable"); \ |                   "DSP structure " #name " doesn't use standard layout");                          \ | ||||||
|         static_assert(sizeof(name) == (size), "Unexpected struct size for DSP structure " #name) |     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 | #else | ||||||
|     #define ASSERT_DSP_STRUCT(name, size) \ | #define ASSERT_DSP_STRUCT(name, size)                                                              \ | ||||||
|         static_assert(std::is_standard_layout<name>::value, "DSP structure " #name " doesn't use standard layout"); \ |     static_assert(std::is_standard_layout<name>::value,                                            \ | ||||||
|         static_assert(sizeof(name) == (size), "Unexpected struct size for DSP structure " #name) |                   "DSP structure " #name " doesn't use standard layout");                          \ | ||||||
|  |     static_assert(sizeof(name) == (size), "Unexpected struct size for DSP structure " #name) | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| struct SourceConfiguration { | struct SourceConfiguration { | ||||||
|  | @ -130,7 +132,8 @@ struct SourceConfiguration { | ||||||
|             BitField<0, 1, u32_le> format_dirty; |             BitField<0, 1, u32_le> format_dirty; | ||||||
|             BitField<1, 1, u32_le> mono_or_stereo_dirty; |             BitField<1, 1, u32_le> mono_or_stereo_dirty; | ||||||
|             BitField<2, 1, u32_le> adpcm_coefficients_dirty; |             BitField<2, 1, u32_le> adpcm_coefficients_dirty; | ||||||
|             BitField<3, 1, u32_le> partial_embedded_buffer_dirty; ///< Tends to be set when a looped buffer is queued.
 |             /// Tends to be set when a looped buffer is queued.
 | ||||||
|  |             BitField<3, 1, u32_le> partial_embedded_buffer_dirty; | ||||||
|             BitField<4, 1, u32_le> partial_reset_flag; |             BitField<4, 1, u32_le> partial_reset_flag; | ||||||
| 
 | 
 | ||||||
|             BitField<16, 1, u32_le> enable_dirty; |             BitField<16, 1, u32_le> enable_dirty; | ||||||
|  | @ -138,7 +141,8 @@ struct SourceConfiguration { | ||||||
|             BitField<18, 1, u32_le> rate_multiplier_dirty; |             BitField<18, 1, u32_le> rate_multiplier_dirty; | ||||||
|             BitField<19, 1, u32_le> buffer_queue_dirty; |             BitField<19, 1, u32_le> buffer_queue_dirty; | ||||||
|             BitField<20, 1, u32_le> loop_related_dirty; |             BitField<20, 1, u32_le> loop_related_dirty; | ||||||
|             BitField<21, 1, u32_le> play_position_dirty; ///< Tends to also be set when embedded buffer is updated.
 |             /// Tends to also be set when embedded buffer is updated.
 | ||||||
|  |             BitField<21, 1, u32_le> play_position_dirty; | ||||||
|             BitField<22, 1, u32_le> filters_enabled_dirty; |             BitField<22, 1, u32_le> filters_enabled_dirty; | ||||||
|             BitField<23, 1, u32_le> simple_filter_dirty; |             BitField<23, 1, u32_le> simple_filter_dirty; | ||||||
|             BitField<24, 1, u32_le> biquad_filter_dirty; |             BitField<24, 1, u32_le> biquad_filter_dirty; | ||||||
|  | @ -153,9 +157,9 @@ struct SourceConfiguration { | ||||||
|         // Gain control
 |         // Gain control
 | ||||||
| 
 | 
 | ||||||
|         /**
 |         /**
 | ||||||
|          * Gain is between 0.0-1.0. This determines how much will this source appear on |          * Gain is between 0.0-1.0. This determines how much will this source appear on each of the | ||||||
|          * each of the 12 channels that feed into the intermediate mixers. |          * 12 channels that feed into the intermediate mixers. Each of the three intermediate mixers | ||||||
|          * Each of the three intermediate mixers is fed two left and two right channels. |          * is fed two left and two right channels. | ||||||
|          */ |          */ | ||||||
|         float_le gain[3][4]; |         float_le gain[3][4]; | ||||||
| 
 | 
 | ||||||
|  | @ -167,7 +171,7 @@ struct SourceConfiguration { | ||||||
|         enum class InterpolationMode : u8 { |         enum class InterpolationMode : u8 { | ||||||
|             Polyphase = 0, |             Polyphase = 0, | ||||||
|             Linear = 1, |             Linear = 1, | ||||||
|             None = 2 |             None = 2, | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         InterpolationMode interpolation_mode; |         InterpolationMode interpolation_mode; | ||||||
|  | @ -191,8 +195,8 @@ struct SourceConfiguration { | ||||||
|          * This is a normalised biquad filter (second-order). |          * This is a normalised biquad filter (second-order). | ||||||
|          * The transfer function of this filter is: |          * The transfer function of this filter is: | ||||||
|          *     H(z) = (b0 + b1 z^-1 + b2 z^-2) / (1 - a1 z^-1 - a2 z^-2) |          *     H(z) = (b0 + b1 z^-1 + b2 z^-2) / (1 - a1 z^-1 - a2 z^-2) | ||||||
|          * Nintendo chose to negate the feedbackward coefficients. This differs from standard notation |          * Nintendo chose to negate the feedbackward coefficients. This differs from standard | ||||||
|          * as in: https://ccrma.stanford.edu/~jos/filters/Direct_Form_I.html
 |          * notation as in: https://ccrma.stanford.edu/~jos/filters/Direct_Form_I.html
 | ||||||
|          * Values are signed fixed point with 14 fractional bits. |          * Values are signed fixed point with 14 fractional bits. | ||||||
|          */ |          */ | ||||||
|         struct BiquadFilter { |         struct BiquadFilter { | ||||||
|  | @ -239,23 +243,24 @@ struct SourceConfiguration { | ||||||
|             /// Is a looping buffer.
 |             /// Is a looping buffer.
 | ||||||
|             u8 is_looping; |             u8 is_looping; | ||||||
| 
 | 
 | ||||||
|             /// This value is shown in SourceStatus::previous_buffer_id when this buffer has finished.
 |             /// This value is shown in SourceStatus::previous_buffer_id when this buffer has
 | ||||||
|             /// This allows the emulated application to tell what buffer is currently playing
 |             /// finished. This allows the emulated application to tell what buffer is currently
 | ||||||
|  |             /// playing.
 | ||||||
|             u16_le buffer_id; |             u16_le buffer_id; | ||||||
| 
 | 
 | ||||||
|             INSERT_PADDING_DSPWORDS(1); |             INSERT_PADDING_DSPWORDS(1); | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         u16_le buffers_dirty;             ///< Bitmap indicating which buffers are dirty (bit i -> buffers[i])
 |         u16_le buffers_dirty; ///< Bitmap indicating which buffers are dirty (bit i -> buffers[i])
 | ||||||
|         Buffer buffers[4];                ///< Queued Buffers
 |         Buffer buffers[4];    ///< Queued Buffers
 | ||||||
| 
 | 
 | ||||||
|         // Playback controls
 |         // Playback controls
 | ||||||
| 
 | 
 | ||||||
|         u32_dsp loop_related; |         u32_dsp loop_related; | ||||||
|         u8 enable; |         u8 enable; | ||||||
|         INSERT_PADDING_BYTES(1); |         INSERT_PADDING_BYTES(1); | ||||||
|         u16_le sync;                      ///< Application-side sync (See also: SourceStatus::sync)
 |         u16_le sync;           ///< Application-side sync (See also: SourceStatus::sync)
 | ||||||
|         u32_dsp play_position;            ///< Position. (Units: number of samples)
 |         u32_dsp play_position; ///< Position. (Units: number of samples)
 | ||||||
|         INSERT_PADDING_DSPWORDS(2); |         INSERT_PADDING_DSPWORDS(2); | ||||||
| 
 | 
 | ||||||
|         // Embedded Buffer
 |         // Embedded Buffer
 | ||||||
|  | @ -270,13 +275,13 @@ struct SourceConfiguration { | ||||||
| 
 | 
 | ||||||
|         enum class MonoOrStereo : u16_le { |         enum class MonoOrStereo : u16_le { | ||||||
|             Mono = 1, |             Mono = 1, | ||||||
|             Stereo = 2 |             Stereo = 2, | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         enum class Format : u16_le { |         enum class Format : u16_le { | ||||||
|             PCM8 = 0, |             PCM8 = 0, | ||||||
|             PCM16 = 1, |             PCM16 = 1, | ||||||
|             ADPCM = 2 |             ADPCM = 2, | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         union { |         union { | ||||||
|  | @ -299,10 +304,11 @@ struct SourceConfiguration { | ||||||
|         union { |         union { | ||||||
|             u16_le flags2_raw; |             u16_le flags2_raw; | ||||||
|             BitField<0, 1, u16_le> adpcm_dirty; ///< Has the ADPCM info above been changed?
 |             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; |         u16_le buffer_id; | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  | @ -313,11 +319,11 @@ ASSERT_DSP_STRUCT(SourceConfiguration::Configuration::Buffer, 20); | ||||||
| 
 | 
 | ||||||
| struct SourceStatus { | struct SourceStatus { | ||||||
|     struct Status { |     struct Status { | ||||||
|         u8 is_enabled;               ///< Is this channel enabled? (Doesn't have to be playing anything.)
 |         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
 |         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
 |         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
 |         u32_dsp buffer_position;    ///< Number of samples into the current buffer
 | ||||||
|         u16_le current_buffer_id;    ///< Updated when a buffer finishes playing
 |         u16_le current_buffer_id;   ///< Updated when a buffer finishes playing
 | ||||||
|         INSERT_PADDING_DSPWORDS(1); |         INSERT_PADDING_DSPWORDS(1); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  | @ -347,7 +353,8 @@ struct DspConfiguration { | ||||||
|         BitField<28, 1, u32_le> headphones_connected_dirty; |         BitField<28, 1, u32_le> headphones_connected_dirty; | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     /// The DSP has three intermediate audio mixers. This controls the volume level (0.0-1.0) for each at the final mixer
 |     /// The DSP has three intermediate audio mixers. This controls the volume level (0.0-1.0) for
 | ||||||
|  |     /// each at the final mixer.
 | ||||||
|     float_le volume[3]; |     float_le volume[3]; | ||||||
| 
 | 
 | ||||||
|     INSERT_PADDING_DSPWORDS(3); |     INSERT_PADDING_DSPWORDS(3); | ||||||
|  | @ -355,7 +362,7 @@ struct DspConfiguration { | ||||||
|     enum class OutputFormat : u16_le { |     enum class OutputFormat : u16_le { | ||||||
|         Mono = 0, |         Mono = 0, | ||||||
|         Stereo = 1, |         Stereo = 1, | ||||||
|         Surround = 2 |         Surround = 2, | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     OutputFormat output_format; |     OutputFormat output_format; | ||||||
|  | @ -388,8 +395,10 @@ struct DspConfiguration { | ||||||
|         u16_le enable; |         u16_le enable; | ||||||
|         INSERT_PADDING_DSPWORDS(1); |         INSERT_PADDING_DSPWORDS(1); | ||||||
|         u16_le outputs; |         u16_le outputs; | ||||||
|         u32_dsp work_buffer_address; ///< The application allocates a block of memory for the DSP to use as a work buffer.
 |         /// The application allocates a block of memory for the DSP to use as a work buffer.
 | ||||||
|         u16_le frame_count;  ///< Frames to delay by
 |         u32_dsp work_buffer_address; | ||||||
|  |         /// Frames to delay by
 | ||||||
|  |         u16_le frame_count; | ||||||
| 
 | 
 | ||||||
|         // Coefficients
 |         // Coefficients
 | ||||||
|         s16_le g; ///< Fixed point with 7 fractional bits
 |         s16_le g; ///< Fixed point with 7 fractional bits
 | ||||||
|  | @ -506,21 +515,36 @@ ASSERT_DSP_STRUCT(SharedMemory, 0x8000); | ||||||
| extern std::array<SharedMemory, 2> g_regions; | extern std::array<SharedMemory, 2> g_regions; | ||||||
| 
 | 
 | ||||||
| // Structures must have an offset that is a multiple of two.
 | // Structures must have an offset that is a multiple of two.
 | ||||||
| static_assert(offsetof(SharedMemory, frame_counter) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | static_assert(offsetof(SharedMemory, frame_counter) % 2 == 0, | ||||||
| static_assert(offsetof(SharedMemory, source_configurations) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); |               "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | ||||||
| static_assert(offsetof(SharedMemory, source_statuses) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | static_assert(offsetof(SharedMemory, source_configurations) % 2 == 0, | ||||||
| static_assert(offsetof(SharedMemory, adpcm_coefficients) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); |               "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | ||||||
| static_assert(offsetof(SharedMemory, dsp_configuration) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | static_assert(offsetof(SharedMemory, source_statuses) % 2 == 0, | ||||||
| static_assert(offsetof(SharedMemory, dsp_status) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); |               "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | ||||||
| static_assert(offsetof(SharedMemory, final_samples) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | static_assert(offsetof(SharedMemory, adpcm_coefficients) % 2 == 0, | ||||||
| static_assert(offsetof(SharedMemory, intermediate_mix_samples) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); |               "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | ||||||
| static_assert(offsetof(SharedMemory, compressor) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | static_assert(offsetof(SharedMemory, dsp_configuration) % 2 == 0, | ||||||
| static_assert(offsetof(SharedMemory, dsp_debug) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); |               "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | ||||||
| static_assert(offsetof(SharedMemory, unknown10) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | static_assert(offsetof(SharedMemory, dsp_status) % 2 == 0, | ||||||
| static_assert(offsetof(SharedMemory, unknown11) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); |               "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | ||||||
| static_assert(offsetof(SharedMemory, unknown12) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | static_assert(offsetof(SharedMemory, final_samples) % 2 == 0, | ||||||
| static_assert(offsetof(SharedMemory, unknown13) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); |               "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | ||||||
| static_assert(offsetof(SharedMemory, unknown14) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | static_assert(offsetof(SharedMemory, intermediate_mix_samples) % 2 == 0, | ||||||
|  |               "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | ||||||
|  | static_assert(offsetof(SharedMemory, compressor) % 2 == 0, | ||||||
|  |               "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | ||||||
|  | static_assert(offsetof(SharedMemory, dsp_debug) % 2 == 0, | ||||||
|  |               "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | ||||||
|  | static_assert(offsetof(SharedMemory, unknown10) % 2 == 0, | ||||||
|  |               "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | ||||||
|  | static_assert(offsetof(SharedMemory, unknown11) % 2 == 0, | ||||||
|  |               "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | ||||||
|  | static_assert(offsetof(SharedMemory, unknown12) % 2 == 0, | ||||||
|  |               "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | ||||||
|  | static_assert(offsetof(SharedMemory, unknown13) % 2 == 0, | ||||||
|  |               "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | ||||||
|  | static_assert(offsetof(SharedMemory, unknown14) % 2 == 0, | ||||||
|  |               "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | ||||||
| 
 | 
 | ||||||
| #undef INSERT_PADDING_DSPWORDS | #undef INSERT_PADDING_DSPWORDS | ||||||
| #undef ASSERT_DSP_STRUCT | #undef ASSERT_DSP_STRUCT | ||||||
|  |  | ||||||
|  | @ -4,11 +4,9 @@ | ||||||
| 
 | 
 | ||||||
| #include <array> | #include <array> | ||||||
| #include <cstddef> | #include <cstddef> | ||||||
| 
 |  | ||||||
| #include "audio_core/hle/common.h" | #include "audio_core/hle/common.h" | ||||||
| #include "audio_core/hle/dsp.h" | #include "audio_core/hle/dsp.h" | ||||||
| #include "audio_core/hle/filter.h" | #include "audio_core/hle/filter.h" | ||||||
| 
 |  | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "common/math_util.h" | #include "common/math_util.h" | ||||||
| 
 | 
 | ||||||
|  | @ -59,7 +57,9 @@ void SourceFilters::SimpleFilter::Reset() { | ||||||
|     b0 = 1 << 15; |     b0 = 1 << 15; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void SourceFilters::SimpleFilter::Configure(SourceConfiguration::Configuration::SimpleFilter config) { | void SourceFilters::SimpleFilter::Configure( | ||||||
|  |     SourceConfiguration::Configuration::SimpleFilter config) { | ||||||
|  | 
 | ||||||
|     a1 = config.a1; |     a1 = config.a1; | ||||||
|     b0 = config.b0; |     b0 = config.b0; | ||||||
| } | } | ||||||
|  | @ -88,7 +88,9 @@ void SourceFilters::BiquadFilter::Reset() { | ||||||
|     b0 = 1 << 14; |     b0 = 1 << 14; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void SourceFilters::BiquadFilter::Configure(SourceConfiguration::Configuration::BiquadFilter config) { | void SourceFilters::BiquadFilter::Configure( | ||||||
|  |     SourceConfiguration::Configuration::BiquadFilter config) { | ||||||
|  | 
 | ||||||
|     a1 = config.a1; |     a1 = config.a1; | ||||||
|     a2 = config.a2; |     a2 = config.a2; | ||||||
|     b0 = config.b0; |     b0 = config.b0; | ||||||
|  |  | ||||||
|  | @ -5,10 +5,8 @@ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <array> | #include <array> | ||||||
| 
 |  | ||||||
| #include "audio_core/hle/common.h" | #include "audio_core/hle/common.h" | ||||||
| #include "audio_core/hle/dsp.h" | #include "audio_core/hle/dsp.h" | ||||||
| 
 |  | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| 
 | 
 | ||||||
| namespace DSP { | namespace DSP { | ||||||
|  | @ -17,7 +15,9 @@ namespace HLE { | ||||||
| /// Preprocessing filters. There is an independent set of filters for each Source.
 | /// Preprocessing filters. There is an independent set of filters for each Source.
 | ||||||
| class SourceFilters final { | class SourceFilters final { | ||||||
| public: | public: | ||||||
|     SourceFilters() { Reset(); } |     SourceFilters() { | ||||||
|  |         Reset(); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     /// Reset internal state.
 |     /// Reset internal state.
 | ||||||
|     void Reset(); |     void Reset(); | ||||||
|  | @ -54,7 +54,9 @@ private: | ||||||
|     bool biquad_filter_enabled; |     bool biquad_filter_enabled; | ||||||
| 
 | 
 | ||||||
|     struct SimpleFilter { |     struct SimpleFilter { | ||||||
|         SimpleFilter() { Reset(); } |         SimpleFilter() { | ||||||
|  |             Reset(); | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         /// Resets internal state.
 |         /// Resets internal state.
 | ||||||
|         void Reset(); |         void Reset(); | ||||||
|  | @ -80,7 +82,9 @@ private: | ||||||
|     } simple_filter; |     } simple_filter; | ||||||
| 
 | 
 | ||||||
|     struct BiquadFilter { |     struct BiquadFilter { | ||||||
|         BiquadFilter() { Reset(); } |         BiquadFilter() { | ||||||
|  |             Reset(); | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         /// Resets internal state.
 |         /// Resets internal state.
 | ||||||
|         void Reset(); |         void Reset(); | ||||||
|  |  | ||||||
|  | @ -7,7 +7,6 @@ | ||||||
| #include "audio_core/hle/common.h" | #include "audio_core/hle/common.h" | ||||||
| #include "audio_core/hle/dsp.h" | #include "audio_core/hle/dsp.h" | ||||||
| #include "audio_core/hle/mixers.h" | #include "audio_core/hle/mixers.h" | ||||||
| 
 |  | ||||||
| #include "common/assert.h" | #include "common/assert.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
| #include "common/math_util.h" | #include "common/math_util.h" | ||||||
|  | @ -20,11 +19,9 @@ void Mixers::Reset() { | ||||||
|     state = {}; |     state = {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| DspStatus Mixers::Tick(DspConfiguration& config, | DspStatus Mixers::Tick(DspConfiguration& config, const IntermediateMixSamples& read_samples, | ||||||
|         const IntermediateMixSamples& read_samples, |                        IntermediateMixSamples& write_samples, | ||||||
|         IntermediateMixSamples& write_samples, |                        const std::array<QuadFrame32, 3>& input) { | ||||||
|         const std::array<QuadFrame32, 3>& input) |  | ||||||
| { |  | ||||||
|     ParseConfig(config); |     ParseConfig(config); | ||||||
| 
 | 
 | ||||||
|     AuxReturn(read_samples); |     AuxReturn(read_samples); | ||||||
|  | @ -73,13 +70,14 @@ void Mixers::ParseConfig(DspConfiguration& config) { | ||||||
|     if (config.output_format_dirty) { |     if (config.output_format_dirty) { | ||||||
|         config.output_format_dirty.Assign(0); |         config.output_format_dirty.Assign(0); | ||||||
|         state.output_format = config.output_format; |         state.output_format = config.output_format; | ||||||
|         LOG_TRACE(Audio_DSP, "mixers output_format = %zu", static_cast<size_t>(config.output_format)); |         LOG_TRACE(Audio_DSP, "mixers output_format = %zu", | ||||||
|  |                   static_cast<size_t>(config.output_format)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (config.headphones_connected_dirty) { |     if (config.headphones_connected_dirty) { | ||||||
|         config.headphones_connected_dirty.Assign(0); |         config.headphones_connected_dirty.Assign(0); | ||||||
|         // Do nothing.
 |         // Do nothing. (Note: Whether headphones are connected does affect coefficients used for
 | ||||||
|         // (Note: Whether headphones are connected does affect coefficients used for surround sound.)
 |         // surround sound.)
 | ||||||
|         LOG_TRACE(Audio_DSP, "mixers headphones_connected=%hu", config.headphones_connected); |         LOG_TRACE(Audio_DSP, "mixers headphones_connected=%hu", config.headphones_connected); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -94,11 +92,10 @@ static s16 ClampToS16(s32 value) { | ||||||
|     return static_cast<s16>(MathUtil::Clamp(value, -32768, 32767)); |     return static_cast<s16>(MathUtil::Clamp(value, -32768, 32767)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static std::array<s16, 2> AddAndClampToS16(const std::array<s16, 2>& a, const std::array<s16, 2>& b) { | static std::array<s16, 2> AddAndClampToS16(const std::array<s16, 2>& a, | ||||||
|     return { |                                            const std::array<s16, 2>& b) { | ||||||
|         ClampToS16(static_cast<s32>(a[0]) + static_cast<s32>(b[0])), |     return {ClampToS16(static_cast<s32>(a[0]) + static_cast<s32>(b[0])), | ||||||
|         ClampToS16(static_cast<s32>(a[1]) + static_cast<s32>(b[1])) |             ClampToS16(static_cast<s32>(a[1]) + static_cast<s32>(b[1]))}; | ||||||
|     }; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Mixers::DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& samples) { | void Mixers::DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& samples) { | ||||||
|  | @ -106,27 +103,33 @@ void Mixers::DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& sample | ||||||
| 
 | 
 | ||||||
|     switch (state.output_format) { |     switch (state.output_format) { | ||||||
|     case OutputFormat::Mono: |     case OutputFormat::Mono: | ||||||
|         std::transform(current_frame.begin(), current_frame.end(), samples.begin(), current_frame.begin(), |         std::transform( | ||||||
|             [gain](const std::array<s16, 2>& accumulator, const std::array<s32, 4>& sample) -> std::array<s16, 2> { |             current_frame.begin(), current_frame.end(), samples.begin(), current_frame.begin(), | ||||||
|  |             [gain](const std::array<s16, 2>& accumulator, | ||||||
|  |                    const std::array<s32, 4>& sample) -> std::array<s16, 2> { | ||||||
|                 // Downmix to mono
 |                 // Downmix to mono
 | ||||||
|                 s16 mono = ClampToS16(static_cast<s32>((gain * sample[0] + gain * sample[1] + gain * sample[2] + gain * sample[3]) / 2)); |                 s16 mono = ClampToS16(static_cast<s32>( | ||||||
|  |                     (gain * sample[0] + gain * sample[1] + gain * sample[2] + gain * sample[3]) / | ||||||
|  |                     2)); | ||||||
|                 // Mix into current frame
 |                 // Mix into current frame
 | ||||||
|                 return AddAndClampToS16(accumulator, { mono, mono }); |                 return AddAndClampToS16(accumulator, {mono, mono}); | ||||||
|             }); |             }); | ||||||
|         return; |         return; | ||||||
| 
 | 
 | ||||||
|     case OutputFormat::Surround: |     case OutputFormat::Surround: | ||||||
|         // TODO(merry): Implement surround sound.
 |     // TODO(merry): Implement surround sound.
 | ||||||
|         // fallthrough
 |     // fallthrough
 | ||||||
| 
 | 
 | ||||||
|     case OutputFormat::Stereo: |     case OutputFormat::Stereo: | ||||||
|         std::transform(current_frame.begin(), current_frame.end(), samples.begin(), current_frame.begin(), |         std::transform( | ||||||
|             [gain](const std::array<s16, 2>& accumulator, const std::array<s32, 4>& sample) -> std::array<s16, 2> { |             current_frame.begin(), current_frame.end(), samples.begin(), current_frame.begin(), | ||||||
|  |             [gain](const std::array<s16, 2>& accumulator, | ||||||
|  |                    const std::array<s32, 4>& sample) -> std::array<s16, 2> { | ||||||
|                 // Downmix to stereo
 |                 // Downmix to stereo
 | ||||||
|                 s16 left = ClampToS16(static_cast<s32>(gain * sample[0] + gain * sample[2])); |                 s16 left = ClampToS16(static_cast<s32>(gain * sample[0] + gain * sample[2])); | ||||||
|                 s16 right = ClampToS16(static_cast<s32>(gain * sample[1] + gain * sample[3])); |                 s16 right = ClampToS16(static_cast<s32>(gain * sample[1] + gain * sample[3])); | ||||||
|                 // Mix into current frame
 |                 // Mix into current frame
 | ||||||
|                 return AddAndClampToS16(accumulator, { left, right }); |                 return AddAndClampToS16(accumulator, {left, right}); | ||||||
|             }); |             }); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  | @ -135,12 +138,14 @@ void Mixers::DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& sample | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Mixers::AuxReturn(const IntermediateMixSamples& read_samples) { | void Mixers::AuxReturn(const IntermediateMixSamples& read_samples) { | ||||||
|     // NOTE: read_samples.mix{1,2}.pcm32 annoyingly have their dimensions in reverse order to QuadFrame32.
 |     // NOTE: read_samples.mix{1,2}.pcm32 annoyingly have their dimensions in reverse order to
 | ||||||
|  |     // QuadFrame32.
 | ||||||
| 
 | 
 | ||||||
|     if (state.mixer1_enabled) { |     if (state.mixer1_enabled) { | ||||||
|         for (size_t sample = 0; sample < samples_per_frame; sample++) { |         for (size_t sample = 0; sample < samples_per_frame; sample++) { | ||||||
|             for (size_t channel = 0; channel < 4; channel++) { |             for (size_t channel = 0; channel < 4; channel++) { | ||||||
|                 state.intermediate_mix_buffer[1][sample][channel] = read_samples.mix1.pcm32[channel][sample]; |                 state.intermediate_mix_buffer[1][sample][channel] = | ||||||
|  |                     read_samples.mix1.pcm32[channel][sample]; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | @ -148,14 +153,17 @@ void Mixers::AuxReturn(const IntermediateMixSamples& read_samples) { | ||||||
|     if (state.mixer2_enabled) { |     if (state.mixer2_enabled) { | ||||||
|         for (size_t sample = 0; sample < samples_per_frame; sample++) { |         for (size_t sample = 0; sample < samples_per_frame; sample++) { | ||||||
|             for (size_t channel = 0; channel < 4; channel++) { |             for (size_t channel = 0; channel < 4; channel++) { | ||||||
|                 state.intermediate_mix_buffer[2][sample][channel] = read_samples.mix2.pcm32[channel][sample]; |                 state.intermediate_mix_buffer[2][sample][channel] = | ||||||
|  |                     read_samples.mix2.pcm32[channel][sample]; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Mixers::AuxSend(IntermediateMixSamples& write_samples, const std::array<QuadFrame32, 3>& input) { | void Mixers::AuxSend(IntermediateMixSamples& write_samples, | ||||||
|     // NOTE: read_samples.mix{1,2}.pcm32 annoyingly have their dimensions in reverse order to QuadFrame32.
 |                      const std::array<QuadFrame32, 3>& input) { | ||||||
|  |     // NOTE: read_samples.mix{1,2}.pcm32 annoyingly have their dimensions in reverse order to
 | ||||||
|  |     // QuadFrame32.
 | ||||||
| 
 | 
 | ||||||
|     state.intermediate_mix_buffer[0] = input[0]; |     state.intermediate_mix_buffer[0] = input[0]; | ||||||
| 
 | 
 | ||||||
|  | @ -184,7 +192,8 @@ void Mixers::MixCurrentFrame() { | ||||||
|     current_frame.fill({}); |     current_frame.fill({}); | ||||||
| 
 | 
 | ||||||
|     for (size_t mix = 0; mix < 3; mix++) { |     for (size_t mix = 0; mix < 3; mix++) { | ||||||
|         DownmixAndMixIntoCurrentFrame(state.intermediate_mixer_volume[mix], state.intermediate_mix_buffer[mix]); |         DownmixAndMixIntoCurrentFrame(state.intermediate_mixer_volume[mix], | ||||||
|  |                                       state.intermediate_mix_buffer[mix]); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // TODO(merry): Compressor. (We currently assume a disabled compressor.)
 |     // TODO(merry): Compressor. (We currently assume a disabled compressor.)
 | ||||||
|  |  | ||||||
|  | @ -5,7 +5,6 @@ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <array> | #include <array> | ||||||
| 
 |  | ||||||
| #include "audio_core/hle/common.h" | #include "audio_core/hle/common.h" | ||||||
| #include "audio_core/hle/dsp.h" | #include "audio_core/hle/dsp.h" | ||||||
| 
 | 
 | ||||||
|  | @ -20,10 +19,8 @@ public: | ||||||
| 
 | 
 | ||||||
|     void Reset(); |     void Reset(); | ||||||
| 
 | 
 | ||||||
|     DspStatus Tick(DspConfiguration& config, |     DspStatus Tick(DspConfiguration& config, const IntermediateMixSamples& read_samples, | ||||||
|                    const IntermediateMixSamples& read_samples, |                    IntermediateMixSamples& write_samples, const std::array<QuadFrame32, 3>& input); | ||||||
|                    IntermediateMixSamples& write_samples, |  | ||||||
|                    const std::array<QuadFrame32, 3>& input); |  | ||||||
| 
 | 
 | ||||||
|     StereoFrame16 GetOutput() const { |     StereoFrame16 GetOutput() const { | ||||||
|         return current_frame; |         return current_frame; | ||||||
|  | @ -53,7 +50,8 @@ private: | ||||||
|     void AuxSend(IntermediateMixSamples& write_samples, const std::array<QuadFrame32, 3>& input); |     void AuxSend(IntermediateMixSamples& write_samples, const std::array<QuadFrame32, 3>& input); | ||||||
|     /// INTERNAL: Mix current_frame.
 |     /// INTERNAL: Mix current_frame.
 | ||||||
|     void MixCurrentFrame(); |     void MixCurrentFrame(); | ||||||
|     /// INTERNAL: Downmix from quadraphonic to stereo based on status.output_format and accumulate into current_frame.
 |     /// INTERNAL: Downmix from quadraphonic to stereo based on status.output_format and accumulate
 | ||||||
|  |     /// into current_frame.
 | ||||||
|     void DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& samples); |     void DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& samples); | ||||||
|     /// INTERNAL: Generate DspStatus based on internal state.
 |     /// INTERNAL: Generate DspStatus based on internal state.
 | ||||||
|     DspStatus GetCurrentStatus() const; |     DspStatus GetCurrentStatus() const; | ||||||
|  |  | ||||||
|  | @ -4,14 +4,11 @@ | ||||||
| 
 | 
 | ||||||
| #include <array> | #include <array> | ||||||
| #include <vector> | #include <vector> | ||||||
| 
 |  | ||||||
| #include "audio_core/hle/dsp.h" | #include "audio_core/hle/dsp.h" | ||||||
| #include "audio_core/hle/pipe.h" | #include "audio_core/hle/pipe.h" | ||||||
| 
 |  | ||||||
| #include "common/assert.h" | #include "common/assert.h" | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
| 
 |  | ||||||
| #include "core/hle/service/dsp_dsp.h" | #include "core/hle/service/dsp_dsp.h" | ||||||
| 
 | 
 | ||||||
| namespace DSP { | namespace DSP { | ||||||
|  | @ -44,8 +41,10 @@ std::vector<u8> PipeRead(DspPipe pipe_number, u32 length) { | ||||||
|     std::vector<u8>& data = pipe_data[pipe_index]; |     std::vector<u8>& data = pipe_data[pipe_index]; | ||||||
| 
 | 
 | ||||||
|     if (length > data.size()) { |     if (length > data.size()) { | ||||||
|         LOG_WARNING(Audio_DSP, "pipe_number = %zu is out of data, application requested read of %u but %zu remain", |         LOG_WARNING( | ||||||
|                     pipe_index, length, data.size()); |             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()); |         length = static_cast<u32>(data.size()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -95,7 +94,7 @@ static void AudioPipeWriteStructAddresses() { | ||||||
|         0x8000 + offsetof(SharedMemory, unknown11) / 2, |         0x8000 + offsetof(SharedMemory, unknown11) / 2, | ||||||
|         0x8000 + offsetof(SharedMemory, unknown12) / 2, |         0x8000 + offsetof(SharedMemory, unknown12) / 2, | ||||||
|         0x8000 + offsetof(SharedMemory, unknown13) / 2, |         0x8000 + offsetof(SharedMemory, unknown13) / 2, | ||||||
|         0x8000 + offsetof(SharedMemory, unknown14) / 2 |         0x8000 + offsetof(SharedMemory, unknown14) / 2, | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     // Begin with a u16 denoting the number of structs.
 |     // Begin with a u16 denoting the number of structs.
 | ||||||
|  | @ -112,7 +111,8 @@ void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) { | ||||||
|     switch (pipe_number) { |     switch (pipe_number) { | ||||||
|     case DspPipe::Audio: { |     case DspPipe::Audio: { | ||||||
|         if (buffer.size() != 4) { |         if (buffer.size() != 4) { | ||||||
|             LOG_ERROR(Audio_DSP, "DspPipe::Audio: Unexpected buffer length %zu was written", buffer.size()); |             LOG_ERROR(Audio_DSP, "DspPipe::Audio: Unexpected buffer length %zu was written", | ||||||
|  |                       buffer.size()); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -120,7 +120,7 @@ void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) { | ||||||
|             Initalize = 0, |             Initalize = 0, | ||||||
|             Shutdown = 1, |             Shutdown = 1, | ||||||
|             Wakeup = 2, |             Wakeup = 2, | ||||||
|             Sleep = 3 |             Sleep = 3, | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         // The difference between Initialize and Wakeup is that Input state is maintained
 |         // The difference between Initialize and Wakeup is that Input state is maintained
 | ||||||
|  | @ -152,7 +152,9 @@ void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) { | ||||||
|             dsp_state = DspState::Sleeping; |             dsp_state = DspState::Sleeping; | ||||||
|             break; |             break; | ||||||
|         default: |         default: | ||||||
|             LOG_ERROR(Audio_DSP, "Application has requested unknown state transition of DSP hardware %hhu", buffer[0]); |             LOG_ERROR(Audio_DSP, | ||||||
|  |                       "Application has requested unknown state transition of DSP hardware %hhu", | ||||||
|  |                       buffer[0]); | ||||||
|             dsp_state = DspState::Off; |             dsp_state = DspState::Off; | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|  | @ -160,7 +162,8 @@ void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     default: |     default: | ||||||
|         LOG_CRITICAL(Audio_DSP, "pipe_number = %zu unimplemented", static_cast<size_t>(pipe_number)); |         LOG_CRITICAL(Audio_DSP, "pipe_number = %zu unimplemented", | ||||||
|  |                      static_cast<size_t>(pipe_number)); | ||||||
|         UNIMPLEMENTED(); |         UNIMPLEMENTED(); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -6,7 +6,6 @@ | ||||||
| 
 | 
 | ||||||
| #include <cstddef> | #include <cstddef> | ||||||
| #include <vector> | #include <vector> | ||||||
| 
 |  | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| 
 | 
 | ||||||
| namespace DSP { | namespace DSP { | ||||||
|  | @ -19,16 +18,18 @@ enum class DspPipe { | ||||||
|     Debug = 0, |     Debug = 0, | ||||||
|     Dma = 1, |     Dma = 1, | ||||||
|     Audio = 2, |     Audio = 2, | ||||||
|     Binary = 3 |     Binary = 3, | ||||||
| }; | }; | ||||||
| constexpr size_t NUM_DSP_PIPE = 8; | constexpr size_t NUM_DSP_PIPE = 8; | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Reads `length` bytes from the DSP pipe identified with `pipe_number`. |  * Reads `length` bytes from the DSP pipe identified with `pipe_number`. | ||||||
|  * @note Can read up to the maximum value of a u16 in bytes (65,535). |  * @note Can read up to the maximum value of a u16 in bytes (65,535). | ||||||
|  * @note IF an error is encoutered with either an invalid `pipe_number` or `length` value, an empty vector will be returned. |  * @note IF an error is encoutered with either an invalid `pipe_number` or `length` value, an empty | ||||||
|  |  * vector will be returned. | ||||||
|  * @note IF `length` is set to 0, an empty vector will be returned. |  * @note IF `length` is set to 0, an empty vector will be returned. | ||||||
|  * @note IF `length` is greater than the amount of data available, this function will only read the available amount. |  * @note IF `length` is greater than the amount of data available, this function will only read the | ||||||
|  |  * available amount. | ||||||
|  * @param pipe_number a `DspPipe` |  * @param pipe_number a `DspPipe` | ||||||
|  * @param length the number of bytes to read. The max is 65,535 (max of u16). |  * @param length the number of bytes to read. The max is 65,535 (max of u16). | ||||||
|  * @returns a vector of bytes from the specified pipe. On error, will be empty. |  * @returns a vector of bytes from the specified pipe. On error, will be empty. | ||||||
|  | @ -52,8 +53,9 @@ void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer); | ||||||
| enum class DspState { | enum class DspState { | ||||||
|     Off, |     Off, | ||||||
|     On, |     On, | ||||||
|     Sleeping |     Sleeping, | ||||||
| }; | }; | ||||||
|  | 
 | ||||||
| /// Get the state of the DSP
 | /// Get the state of the DSP
 | ||||||
| DspState GetDspState(); | DspState GetDspState(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -4,21 +4,19 @@ | ||||||
| 
 | 
 | ||||||
| #include <algorithm> | #include <algorithm> | ||||||
| #include <array> | #include <array> | ||||||
| 
 |  | ||||||
| #include "audio_core/codec.h" | #include "audio_core/codec.h" | ||||||
| #include "audio_core/hle/common.h" | #include "audio_core/hle/common.h" | ||||||
| #include "audio_core/hle/source.h" | #include "audio_core/hle/source.h" | ||||||
| #include "audio_core/interpolate.h" | #include "audio_core/interpolate.h" | ||||||
| 
 |  | ||||||
| #include "common/assert.h" | #include "common/assert.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
| 
 |  | ||||||
| #include "core/memory.h" | #include "core/memory.h" | ||||||
| 
 | 
 | ||||||
| namespace DSP { | namespace DSP { | ||||||
| namespace HLE { | namespace HLE { | ||||||
| 
 | 
 | ||||||
| SourceStatus::Status Source::Tick(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]) { | SourceStatus::Status Source::Tick(SourceConfiguration::Configuration& config, | ||||||
|  |                                   const s16_le (&adpcm_coeffs)[16]) { | ||||||
|     ParseConfig(config, adpcm_coeffs); |     ParseConfig(config, adpcm_coeffs); | ||||||
| 
 | 
 | ||||||
|     if (state.enabled) { |     if (state.enabled) { | ||||||
|  | @ -47,7 +45,8 @@ void Source::Reset() { | ||||||
|     state = {}; |     state = {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]) { | void Source::ParseConfig(SourceConfiguration::Configuration& config, | ||||||
|  |                          const s16_le (&adpcm_coeffs)[16]) { | ||||||
|     if (!config.dirty_raw) { |     if (!config.dirty_raw) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  | @ -82,7 +81,8 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_l | ||||||
|         LOG_TRACE(Audio_DSP, "source_id=%zu rate=%f", source_id, state.rate_multiplier); |         LOG_TRACE(Audio_DSP, "source_id=%zu rate=%f", source_id, state.rate_multiplier); | ||||||
| 
 | 
 | ||||||
|         if (state.rate_multiplier <= 0) { |         if (state.rate_multiplier <= 0) { | ||||||
|             LOG_ERROR(Audio_DSP, "Was given an invalid rate multiplier: source_id=%zu rate=%f", source_id, state.rate_multiplier); |             LOG_ERROR(Audio_DSP, "Was given an invalid rate multiplier: source_id=%zu rate=%f", | ||||||
|  |                       source_id, state.rate_multiplier); | ||||||
|             state.rate_multiplier = 1.0f; |             state.rate_multiplier = 1.0f; | ||||||
|             // Note: Actual firmware starts producing garbage if this occurs.
 |             // Note: Actual firmware starts producing garbage if this occurs.
 | ||||||
|         } |         } | ||||||
|  | @ -90,37 +90,39 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_l | ||||||
| 
 | 
 | ||||||
|     if (config.adpcm_coefficients_dirty) { |     if (config.adpcm_coefficients_dirty) { | ||||||
|         config.adpcm_coefficients_dirty.Assign(0); |         config.adpcm_coefficients_dirty.Assign(0); | ||||||
|         std::transform(adpcm_coeffs, adpcm_coeffs + state.adpcm_coeffs.size(), state.adpcm_coeffs.begin(), |         std::transform(adpcm_coeffs, adpcm_coeffs + state.adpcm_coeffs.size(), | ||||||
|             [](const auto& coeff) { return static_cast<s16>(coeff); }); |                        state.adpcm_coeffs.begin(), | ||||||
|  |                        [](const auto& coeff) { return static_cast<s16>(coeff); }); | ||||||
|         LOG_TRACE(Audio_DSP, "source_id=%zu adpcm update", source_id); |         LOG_TRACE(Audio_DSP, "source_id=%zu adpcm update", source_id); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (config.gain_0_dirty) { |     if (config.gain_0_dirty) { | ||||||
|         config.gain_0_dirty.Assign(0); |         config.gain_0_dirty.Assign(0); | ||||||
|         std::transform(config.gain[0], config.gain[0] + state.gain[0].size(), state.gain[0].begin(), |         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); |         LOG_TRACE(Audio_DSP, "source_id=%zu gain 0 update", source_id); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (config.gain_1_dirty) { |     if (config.gain_1_dirty) { | ||||||
|         config.gain_1_dirty.Assign(0); |         config.gain_1_dirty.Assign(0); | ||||||
|         std::transform(config.gain[1], config.gain[1] + state.gain[1].size(), state.gain[1].begin(), |         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); |         LOG_TRACE(Audio_DSP, "source_id=%zu gain 1 update", source_id); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (config.gain_2_dirty) { |     if (config.gain_2_dirty) { | ||||||
|         config.gain_2_dirty.Assign(0); |         config.gain_2_dirty.Assign(0); | ||||||
|         std::transform(config.gain[2], config.gain[2] + state.gain[2].size(), state.gain[2].begin(), |         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); |         LOG_TRACE(Audio_DSP, "source_id=%zu gain 2 update", source_id); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (config.filters_enabled_dirty) { |     if (config.filters_enabled_dirty) { | ||||||
|         config.filters_enabled_dirty.Assign(0); |         config.filters_enabled_dirty.Assign(0); | ||||||
|         state.filters.Enable(config.simple_filter_enabled.ToBool(), config.biquad_filter_enabled.ToBool()); |         state.filters.Enable(config.simple_filter_enabled.ToBool(), | ||||||
|         LOG_TRACE(Audio_DSP, "source_id=%zu enable_simple=%hu enable_biquad=%hu", |                              config.biquad_filter_enabled.ToBool()); | ||||||
|                   source_id, config.simple_filter_enabled.Value(), config.biquad_filter_enabled.Value()); |         LOG_TRACE(Audio_DSP, "source_id=%zu enable_simple=%hu enable_biquad=%hu", source_id, | ||||||
|  |                   config.simple_filter_enabled.Value(), config.biquad_filter_enabled.Value()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (config.simple_filter_dirty) { |     if (config.simple_filter_dirty) { | ||||||
|  | @ -138,19 +140,22 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_l | ||||||
|     if (config.interpolation_dirty) { |     if (config.interpolation_dirty) { | ||||||
|         config.interpolation_dirty.Assign(0); |         config.interpolation_dirty.Assign(0); | ||||||
|         state.interpolation_mode = config.interpolation_mode; |         state.interpolation_mode = config.interpolation_mode; | ||||||
|         LOG_TRACE(Audio_DSP, "source_id=%zu interpolation_mode=%zu", source_id, static_cast<size_t>(state.interpolation_mode)); |         LOG_TRACE(Audio_DSP, "source_id=%zu interpolation_mode=%zu", source_id, | ||||||
|  |                   static_cast<size_t>(state.interpolation_mode)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (config.format_dirty || config.embedded_buffer_dirty) { |     if (config.format_dirty || config.embedded_buffer_dirty) { | ||||||
|         config.format_dirty.Assign(0); |         config.format_dirty.Assign(0); | ||||||
|         state.format = config.format; |         state.format = config.format; | ||||||
|         LOG_TRACE(Audio_DSP, "source_id=%zu format=%zu", source_id, static_cast<size_t>(state.format)); |         LOG_TRACE(Audio_DSP, "source_id=%zu format=%zu", source_id, | ||||||
|  |                   static_cast<size_t>(state.format)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (config.mono_or_stereo_dirty || config.embedded_buffer_dirty) { |     if (config.mono_or_stereo_dirty || config.embedded_buffer_dirty) { | ||||||
|         config.mono_or_stereo_dirty.Assign(0); |         config.mono_or_stereo_dirty.Assign(0); | ||||||
|         state.mono_or_stereo = config.mono_or_stereo; |         state.mono_or_stereo = config.mono_or_stereo; | ||||||
|         LOG_TRACE(Audio_DSP, "source_id=%zu mono_or_stereo=%zu", source_id, static_cast<size_t>(state.mono_or_stereo)); |         LOG_TRACE(Audio_DSP, "source_id=%zu mono_or_stereo=%zu", source_id, | ||||||
|  |                   static_cast<size_t>(state.mono_or_stereo)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (config.embedded_buffer_dirty) { |     if (config.embedded_buffer_dirty) { | ||||||
|  | @ -159,15 +164,16 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_l | ||||||
|             config.physical_address, |             config.physical_address, | ||||||
|             config.length, |             config.length, | ||||||
|             static_cast<u8>(config.adpcm_ps), |             static_cast<u8>(config.adpcm_ps), | ||||||
|             { config.adpcm_yn[0], config.adpcm_yn[1] }, |             {config.adpcm_yn[0], config.adpcm_yn[1]}, | ||||||
|             config.adpcm_dirty.ToBool(), |             config.adpcm_dirty.ToBool(), | ||||||
|             config.is_looping.ToBool(), |             config.is_looping.ToBool(), | ||||||
|             config.buffer_id, |             config.buffer_id, | ||||||
|             state.mono_or_stereo, |             state.mono_or_stereo, | ||||||
|             state.format, |             state.format, | ||||||
|             false |             false, | ||||||
|         }); |         }); | ||||||
|         LOG_TRACE(Audio_DSP, "enqueuing embedded addr=0x%08x len=%u id=%hu", config.physical_address, config.length, config.buffer_id); |         LOG_TRACE(Audio_DSP, "enqueuing embedded addr=0x%08x len=%u id=%hu", | ||||||
|  |                   config.physical_address, config.length, config.buffer_id); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (config.buffer_queue_dirty) { |     if (config.buffer_queue_dirty) { | ||||||
|  | @ -179,15 +185,16 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_l | ||||||
|                     b.physical_address, |                     b.physical_address, | ||||||
|                     b.length, |                     b.length, | ||||||
|                     static_cast<u8>(b.adpcm_ps), |                     static_cast<u8>(b.adpcm_ps), | ||||||
|                     { b.adpcm_yn[0], b.adpcm_yn[1] }, |                     {b.adpcm_yn[0], b.adpcm_yn[1]}, | ||||||
|                     b.adpcm_dirty != 0, |                     b.adpcm_dirty != 0, | ||||||
|                     b.is_looping != 0, |                     b.is_looping != 0, | ||||||
|                     b.buffer_id, |                     b.buffer_id, | ||||||
|                     state.mono_or_stereo, |                     state.mono_or_stereo, | ||||||
|                     state.format, |                     state.format, | ||||||
|                     true |                     true, | ||||||
|                 }); |                 }); | ||||||
|                 LOG_TRACE(Audio_DSP, "enqueuing queued %zu addr=0x%08x len=%u id=%hu", i, b.physical_address, b.length, b.buffer_id); |                 LOG_TRACE(Audio_DSP, "enqueuing queued %zu addr=0x%08x len=%u id=%hu", i, | ||||||
|  |                           b.physical_address, b.length, b.buffer_id); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         config.buffers_dirty = 0; |         config.buffers_dirty = 0; | ||||||
|  | @ -218,10 +225,13 @@ void Source::GenerateFrame() { | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         const size_t size_to_copy = std::min(state.current_buffer.size(), current_frame.size() - frame_position); |         const size_t size_to_copy = | ||||||
|  |             std::min(state.current_buffer.size(), current_frame.size() - frame_position); | ||||||
| 
 | 
 | ||||||
|         std::copy(state.current_buffer.begin(), state.current_buffer.begin() + size_to_copy, current_frame.begin() + frame_position); |         std::copy(state.current_buffer.begin(), state.current_buffer.begin() + size_to_copy, | ||||||
|         state.current_buffer.erase(state.current_buffer.begin(), state.current_buffer.begin() + size_to_copy); |                   current_frame.begin() + frame_position); | ||||||
|  |         state.current_buffer.erase(state.current_buffer.begin(), | ||||||
|  |                                    state.current_buffer.begin() + size_to_copy); | ||||||
| 
 | 
 | ||||||
|         frame_position += size_to_copy; |         frame_position += size_to_copy; | ||||||
|         state.next_sample_number += static_cast<u32>(size_to_copy); |         state.next_sample_number += static_cast<u32>(size_to_copy); | ||||||
|  | @ -230,9 +240,9 @@ void Source::GenerateFrame() { | ||||||
|     state.filters.ProcessFrame(current_frame); |     state.filters.ProcessFrame(current_frame); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| bool Source::DequeueBuffer() { | bool Source::DequeueBuffer() { | ||||||
|     ASSERT_MSG(state.current_buffer.empty(), "Shouldn't dequeue; we still have data in current_buffer"); |     ASSERT_MSG(state.current_buffer.empty(), | ||||||
|  |                "Shouldn't dequeue; we still have data in current_buffer"); | ||||||
| 
 | 
 | ||||||
|     if (state.input_queue.empty()) |     if (state.input_queue.empty()) | ||||||
|         return false; |         return false; | ||||||
|  | @ -261,29 +271,34 @@ bool Source::DequeueBuffer() { | ||||||
|             break; |             break; | ||||||
|         case Format::ADPCM: |         case Format::ADPCM: | ||||||
|             DEBUG_ASSERT(num_channels == 1); |             DEBUG_ASSERT(num_channels == 1); | ||||||
|             state.current_buffer = Codec::DecodeADPCM(memory, buf.length, state.adpcm_coeffs, state.adpcm_state); |             state.current_buffer = | ||||||
|  |                 Codec::DecodeADPCM(memory, buf.length, state.adpcm_coeffs, state.adpcm_state); | ||||||
|             break; |             break; | ||||||
|         default: |         default: | ||||||
|             UNIMPLEMENTED(); |             UNIMPLEMENTED(); | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|     } else { |     } else { | ||||||
|         LOG_WARNING(Audio_DSP, "source_id=%zu buffer_id=%hu length=%u: Invalid physical address 0x%08X", |         LOG_WARNING(Audio_DSP, | ||||||
|                                source_id, buf.buffer_id, buf.length, buf.physical_address); |                     "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(); |         state.current_buffer.clear(); | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     switch (state.interpolation_mode) { |     switch (state.interpolation_mode) { | ||||||
|     case InterpolationMode::None: |     case InterpolationMode::None: | ||||||
|         state.current_buffer = AudioInterp::None(state.interp_state, state.current_buffer, state.rate_multiplier); |         state.current_buffer = | ||||||
|  |             AudioInterp::None(state.interp_state, state.current_buffer, state.rate_multiplier); | ||||||
|         break; |         break; | ||||||
|     case InterpolationMode::Linear: |     case InterpolationMode::Linear: | ||||||
|         state.current_buffer = AudioInterp::Linear(state.interp_state, state.current_buffer, state.rate_multiplier); |         state.current_buffer = | ||||||
|  |             AudioInterp::Linear(state.interp_state, state.current_buffer, state.rate_multiplier); | ||||||
|         break; |         break; | ||||||
|     case InterpolationMode::Polyphase: |     case InterpolationMode::Polyphase: | ||||||
|         // TODO(merry): Implement polyphase interpolation
 |         // TODO(merry): Implement polyphase interpolation
 | ||||||
|         state.current_buffer = AudioInterp::Linear(state.interp_state, state.current_buffer, state.rate_multiplier); |         state.current_buffer = | ||||||
|  |             AudioInterp::Linear(state.interp_state, state.current_buffer, state.rate_multiplier); | ||||||
|         break; |         break; | ||||||
|     default: |     default: | ||||||
|         UNIMPLEMENTED(); |         UNIMPLEMENTED(); | ||||||
|  | @ -296,7 +311,8 @@ bool Source::DequeueBuffer() { | ||||||
|     state.buffer_update = buf.from_queue; |     state.buffer_update = buf.from_queue; | ||||||
| 
 | 
 | ||||||
|     LOG_TRACE(Audio_DSP, "source_id=%zu buffer_id=%hu from_queue=%s current_buffer.size()=%zu", |     LOG_TRACE(Audio_DSP, "source_id=%zu buffer_id=%hu from_queue=%s current_buffer.size()=%zu", | ||||||
|                          source_id, buf.buffer_id, buf.from_queue ? "true" : "false", state.current_buffer.size()); |               source_id, buf.buffer_id, buf.from_queue ? "true" : "false", | ||||||
|  |               state.current_buffer.size()); | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -7,13 +7,11 @@ | ||||||
| #include <array> | #include <array> | ||||||
| #include <queue> | #include <queue> | ||||||
| #include <vector> | #include <vector> | ||||||
| 
 |  | ||||||
| #include "audio_core/codec.h" | #include "audio_core/codec.h" | ||||||
| #include "audio_core/hle/common.h" | #include "audio_core/hle/common.h" | ||||||
| #include "audio_core/hle/dsp.h" | #include "audio_core/hle/dsp.h" | ||||||
| #include "audio_core/hle/filter.h" | #include "audio_core/hle/filter.h" | ||||||
| #include "audio_core/interpolate.h" | #include "audio_core/interpolate.h" | ||||||
| 
 |  | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| 
 | 
 | ||||||
| namespace DSP { | namespace DSP { | ||||||
|  | @ -40,13 +38,17 @@ public: | ||||||
|     /**
 |     /**
 | ||||||
|      * This is called once every audio frame. This performs per-source processing every frame. |      * This is called once every audio frame. This performs per-source processing every frame. | ||||||
|      * @param config The new configuration we've got for this Source from the application. |      * @param config The new configuration we've got for this Source from the application. | ||||||
|      * @param adpcm_coeffs ADPCM coefficients to use if config tells us to use them (may contain invalid values otherwise). |      * @param adpcm_coeffs ADPCM coefficients to use if config tells us to use them (may contain | ||||||
|      * @return The current status of this Source. This is given back to the emulated application via SharedMemory. |      * invalid values otherwise). | ||||||
|  |      * @return The current status of this Source. This is given back to the emulated application via | ||||||
|  |      * SharedMemory. | ||||||
|      */ |      */ | ||||||
|     SourceStatus::Status Tick(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]); |     SourceStatus::Status Tick(SourceConfiguration::Configuration& config, | ||||||
|  |                               const s16_le (&adpcm_coeffs)[16]); | ||||||
| 
 | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Mix this source's output into dest, using the gains for the `intermediate_mix_id`-th intermediate mixer. |      * Mix this source's output into dest, using the gains for the `intermediate_mix_id`-th | ||||||
|  |      * intermediate mixer. | ||||||
|      * @param dest The QuadFrame32 to mix into. |      * @param dest The QuadFrame32 to mix into. | ||||||
|      * @param intermediate_mix_id The id of the intermediate mix whose gains we are using. |      * @param intermediate_mix_id The id of the intermediate mix whose gains we are using. | ||||||
|      */ |      */ | ||||||
|  | @ -77,7 +79,7 @@ private: | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     struct BufferOrder { |     struct BufferOrder { | ||||||
|         bool operator() (const Buffer& a, const Buffer& b) const { |         bool operator()(const Buffer& a, const Buffer& b) const { | ||||||
|             // Lower buffer_id comes first.
 |             // Lower buffer_id comes first.
 | ||||||
|             return a.buffer_id > b.buffer_id; |             return a.buffer_id > b.buffer_id; | ||||||
|         } |         } | ||||||
|  | @ -134,7 +136,8 @@ private: | ||||||
|     void ParseConfig(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]); |     void ParseConfig(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]); | ||||||
|     /// INTERNAL: Generate the current audio output for this frame based on our internal state.
 |     /// INTERNAL: Generate the current audio output for this frame based on our internal state.
 | ||||||
|     void GenerateFrame(); |     void GenerateFrame(); | ||||||
|     /// INTERNAL: Dequeues a buffer and does preprocessing on it (decoding, resampling). Puts it into current_buffer.
 |     /// INTERNAL: Dequeues a buffer and does preprocessing on it (decoding, resampling). Puts it
 | ||||||
|  |     /// into current_buffer.
 | ||||||
|     bool DequeueBuffer(); |     bool DequeueBuffer(); | ||||||
|     /// INTERNAL: Generates a SourceStatus::Status based on our internal state.
 |     /// INTERNAL: Generates a SourceStatus::Status based on our internal state.
 | ||||||
|     SourceStatus::Status GetCurrentStatus(); |     SourceStatus::Status GetCurrentStatus(); | ||||||
|  |  | ||||||
|  | @ -3,7 +3,6 @@ | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| #include "audio_core/interpolate.h" | #include "audio_core/interpolate.h" | ||||||
| 
 |  | ||||||
| #include "common/assert.h" | #include "common/assert.h" | ||||||
| #include "common/math_util.h" | #include "common/math_util.h" | ||||||
| 
 | 
 | ||||||
|  | @ -17,7 +16,8 @@ constexpr u64 scale_mask = scale_factor - 1; | ||||||
| /// Here we step over the input in steps of rate_multiplier, until we consume all of the input.
 | /// Here we step over the input in steps of rate_multiplier, until we consume all of the input.
 | ||||||
| /// Three adjacent samples are passed to fn each step.
 | /// Three adjacent samples are passed to fn each step.
 | ||||||
| template <typename Function> | template <typename Function> | ||||||
| static StereoBuffer16 StepOverSamples(State& state, const StereoBuffer16& input, float rate_multiplier, Function fn) { | static StereoBuffer16 StepOverSamples(State& state, const StereoBuffer16& input, | ||||||
|  |                                       float rate_multiplier, Function fn) { | ||||||
|     ASSERT(rate_multiplier > 0); |     ASSERT(rate_multiplier > 0); | ||||||
| 
 | 
 | ||||||
|     if (input.size() < 2) |     if (input.size() < 2) | ||||||
|  | @ -63,23 +63,24 @@ static StereoBuffer16 StepOverSamples(State& state, const StereoBuffer16& input, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| StereoBuffer16 None(State& state, const StereoBuffer16& input, float rate_multiplier) { | StereoBuffer16 None(State& state, const StereoBuffer16& input, float rate_multiplier) { | ||||||
|     return StepOverSamples(state, input, rate_multiplier, [](u64 fraction, const auto& x0, const auto& x1, const auto& x2) { |     return StepOverSamples( | ||||||
|         return x0; |         state, input, rate_multiplier, | ||||||
|     }); |         [](u64 fraction, const auto& x0, const auto& x1, const auto& x2) { return x0; }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| StereoBuffer16 Linear(State& state, const StereoBuffer16& input, float rate_multiplier) { | StereoBuffer16 Linear(State& state, const StereoBuffer16& input, float rate_multiplier) { | ||||||
|     // Note on accuracy: Some values that this produces are +/- 1 from the actual firmware.
 |     // Note on accuracy: Some values that this produces are +/- 1 from the actual firmware.
 | ||||||
|     return StepOverSamples(state, input, rate_multiplier, [](u64 fraction, const auto& x0, const auto& x1, const auto& x2) { |     return StepOverSamples(state, input, rate_multiplier, | ||||||
|         // This is a saturated subtraction. (Verified by black-box fuzzing.)
 |                            [](u64 fraction, const auto& x0, const auto& x1, const auto& x2) { | ||||||
|         s64 delta0 = MathUtil::Clamp<s64>(x1[0] - x0[0], -32768, 32767); |                                // This is a saturated subtraction. (Verified by black-box fuzzing.)
 | ||||||
|         s64 delta1 = MathUtil::Clamp<s64>(x1[1] - x0[1], -32768, 32767); |                                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> { |                                return std::array<s16, 2>{ | ||||||
|             static_cast<s16>(x0[0] + fraction * delta0 / scale_factor), |                                    static_cast<s16>(x0[0] + fraction * delta0 / scale_factor), | ||||||
|             static_cast<s16>(x0[1] + fraction * delta1 / scale_factor) |                                    static_cast<s16>(x0[1] + fraction * delta1 / scale_factor), | ||||||
|         }; |                                }; | ||||||
|     }); |                            }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| } // namespace AudioInterp
 | } // namespace AudioInterp
 | ||||||
|  |  | ||||||
|  | @ -6,7 +6,6 @@ | ||||||
| 
 | 
 | ||||||
| #include <array> | #include <array> | ||||||
| #include <vector> | #include <vector> | ||||||
| 
 |  | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| 
 | 
 | ||||||
| namespace AudioInterp { | namespace AudioInterp { | ||||||
|  | @ -24,7 +23,8 @@ struct State { | ||||||
|  * No interpolation. This is equivalent to a zero-order hold. There is a two-sample predelay. |  * No interpolation. This is equivalent to a zero-order hold. There is a two-sample predelay. | ||||||
|  * @param input Input buffer. |  * @param input Input buffer. | ||||||
|  * @param rate_multiplier Stretch factor. Must be a positive non-zero value. |  * @param rate_multiplier Stretch factor. Must be a positive non-zero value. | ||||||
|  *                        rate_multiplier > 1.0 performs decimation and rate_multipler < 1.0 performs upsampling. |  *                        rate_multiplier > 1.0 performs decimation and rate_multipler < 1.0 | ||||||
|  |  *                        performs upsampling. | ||||||
|  * @return The resampled audio buffer. |  * @return The resampled audio buffer. | ||||||
|  */ |  */ | ||||||
| StereoBuffer16 None(State& state, const StereoBuffer16& input, float rate_multiplier); | StereoBuffer16 None(State& state, const StereoBuffer16& input, float rate_multiplier); | ||||||
|  | @ -33,7 +33,8 @@ StereoBuffer16 None(State& state, const StereoBuffer16& input, float rate_multip | ||||||
|  * Linear interpolation. This is equivalent to a first-order hold. There is a two-sample predelay. |  * Linear interpolation. This is equivalent to a first-order hold. There is a two-sample predelay. | ||||||
|  * @param input Input buffer. |  * @param input Input buffer. | ||||||
|  * @param rate_multiplier Stretch factor. Must be a positive non-zero value. |  * @param rate_multiplier Stretch factor. Must be a positive non-zero value. | ||||||
|  *                        rate_multiplier > 1.0 performs decimation and rate_multipler < 1.0 performs upsampling. |  *                        rate_multiplier > 1.0 performs decimation and rate_multipler < 1.0 | ||||||
|  |  *                        performs upsampling. | ||||||
|  * @return The resampled audio buffer. |  * @return The resampled audio buffer. | ||||||
|  */ |  */ | ||||||
| StereoBuffer16 Linear(State& state, const StereoBuffer16& input, float rate_multiplier); | StereoBuffer16 Linear(State& state, const StereoBuffer16& input, float rate_multiplier); | ||||||
|  |  | ||||||
|  | @ -5,7 +5,6 @@ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <cstddef> | #include <cstddef> | ||||||
| 
 |  | ||||||
| #include "audio_core/audio_core.h" | #include "audio_core/audio_core.h" | ||||||
| #include "audio_core/sink.h" | #include "audio_core/sink.h" | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -3,16 +3,13 @@ | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| #include <list> | #include <list> | ||||||
|  | #include <numeric> | ||||||
| #include <vector> | #include <vector> | ||||||
| 
 |  | ||||||
| #include <SDL.h> | #include <SDL.h> | ||||||
| 
 |  | ||||||
| #include "audio_core/audio_core.h" | #include "audio_core/audio_core.h" | ||||||
| #include "audio_core/sdl2_sink.h" | #include "audio_core/sdl2_sink.h" | ||||||
| 
 |  | ||||||
| #include "common/assert.h" | #include "common/assert.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
| #include <numeric> |  | ||||||
| 
 | 
 | ||||||
| namespace AudioCore { | namespace AudioCore { | ||||||
| 
 | 
 | ||||||
|  | @ -45,7 +42,8 @@ SDL2Sink::SDL2Sink() : impl(std::make_unique<Impl>()) { | ||||||
|     SDL_AudioSpec obtained_audiospec; |     SDL_AudioSpec obtained_audiospec; | ||||||
|     SDL_zero(obtained_audiospec); |     SDL_zero(obtained_audiospec); | ||||||
| 
 | 
 | ||||||
|     impl->audio_device_id = SDL_OpenAudioDevice(nullptr, false, &desired_audiospec, &obtained_audiospec, 0); |     impl->audio_device_id = | ||||||
|  |         SDL_OpenAudioDevice(nullptr, false, &desired_audiospec, &obtained_audiospec, 0); | ||||||
|     if (impl->audio_device_id <= 0) { |     if (impl->audio_device_id <= 0) { | ||||||
|         LOG_CRITICAL(Audio_Sink, "SDL_OpenAudioDevice failed"); |         LOG_CRITICAL(Audio_Sink, "SDL_OpenAudioDevice failed"); | ||||||
|         return; |         return; | ||||||
|  | @ -86,11 +84,12 @@ size_t SDL2Sink::SamplesInQueue() const { | ||||||
| 
 | 
 | ||||||
|     SDL_LockAudioDevice(impl->audio_device_id); |     SDL_LockAudioDevice(impl->audio_device_id); | ||||||
| 
 | 
 | ||||||
|     size_t total_size = std::accumulate(impl->queue.begin(), impl->queue.end(), static_cast<size_t>(0), |     size_t total_size = std::accumulate(impl->queue.begin(), impl->queue.end(), | ||||||
|         [](size_t sum, const auto& buffer) { |                                         static_cast<size_t>(0), [](size_t sum, const auto& buffer) { | ||||||
|             // Division by two because each stereo sample is made of two s16.
 |                                             // Division by two because each stereo sample is made of
 | ||||||
|             return sum + buffer.size() / 2; |                                             // two s16.
 | ||||||
|         }); |                                             return sum + buffer.size() / 2; | ||||||
|  |                                         }); | ||||||
| 
 | 
 | ||||||
|     SDL_UnlockAudioDevice(impl->audio_device_id); |     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) { | void SDL2Sink::Impl::Callback(void* impl_, u8* buffer, int buffer_size_in_bytes) { | ||||||
|     Impl* impl = reinterpret_cast<Impl*>(impl_); |     Impl* impl = reinterpret_cast<Impl*>(impl_); | ||||||
| 
 | 
 | ||||||
|     size_t remaining_size = static_cast<size_t>(buffer_size_in_bytes) / sizeof(s16); // Keep track of size in 16-bit increments.
 |     size_t remaining_size = static_cast<size_t>(buffer_size_in_bytes) / | ||||||
|  |                             sizeof(s16); // Keep track of size in 16-bit increments.
 | ||||||
| 
 | 
 | ||||||
|     while (remaining_size > 0 && !impl->queue.empty()) { |     while (remaining_size > 0 && !impl->queue.empty()) { | ||||||
|         if (impl->queue.front().size() <= remaining_size) { |         if (impl->queue.front().size() <= remaining_size) { | ||||||
|  | @ -111,7 +111,8 @@ void SDL2Sink::Impl::Callback(void* impl_, u8* buffer, int buffer_size_in_bytes) | ||||||
|         } else { |         } else { | ||||||
|             memcpy(buffer, impl->queue.front().data(), remaining_size * sizeof(s16)); |             memcpy(buffer, impl->queue.front().data(), remaining_size * sizeof(s16)); | ||||||
|             buffer += remaining_size * sizeof(s16); |             buffer += remaining_size * sizeof(s16); | ||||||
|             impl->queue.front().erase(impl->queue.front().begin(), impl->queue.front().begin() + remaining_size); |             impl->queue.front().erase(impl->queue.front().begin(), | ||||||
|  |                                       impl->queue.front().begin() + remaining_size); | ||||||
|             remaining_size = 0; |             remaining_size = 0; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -6,7 +6,6 @@ | ||||||
| 
 | 
 | ||||||
| #include <cstddef> | #include <cstddef> | ||||||
| #include <memory> | #include <memory> | ||||||
| 
 |  | ||||||
| #include "audio_core/sink.h" | #include "audio_core/sink.h" | ||||||
| 
 | 
 | ||||||
| namespace AudioCore { | namespace AudioCore { | ||||||
|  |  | ||||||
|  | @ -5,20 +5,21 @@ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <vector> | #include <vector> | ||||||
| 
 |  | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| 
 | 
 | ||||||
| namespace AudioCore { | namespace AudioCore { | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * This class is an interface for an audio sink. An audio sink accepts samples in stereo signed PCM16 format to be output. |  * This class is an interface for an audio sink. An audio sink accepts samples in stereo signed | ||||||
|  * Sinks *do not* handle resampling and expect the correct sample rate. They are dumb outputs. |  * PCM16 format to be output. Sinks *do not* handle resampling and expect the correct sample rate. | ||||||
|  |  * They are dumb outputs. | ||||||
|  */ |  */ | ||||||
| class Sink { | class Sink { | ||||||
| public: | public: | ||||||
|     virtual ~Sink() = default; |     virtual ~Sink() = default; | ||||||
| 
 | 
 | ||||||
|     /// The native rate of this sink. The sink expects to be fed samples that respect this. (Units: samples/sec)
 |     /// The native rate of this sink. The sink expects to be fed samples that respect this. (Units:
 | ||||||
|  |     /// samples/sec)
 | ||||||
|     virtual unsigned int GetNativeSampleRate() const = 0; |     virtual unsigned int GetNativeSampleRate() const = 0; | ||||||
| 
 | 
 | ||||||
|     /**
 |     /**
 | ||||||
|  |  | ||||||
|  | @ -4,10 +4,8 @@ | ||||||
| 
 | 
 | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <vector> | #include <vector> | ||||||
| 
 |  | ||||||
| #include "audio_core/null_sink.h" | #include "audio_core/null_sink.h" | ||||||
| #include "audio_core/sink_details.h" | #include "audio_core/sink_details.h" | ||||||
| 
 |  | ||||||
| #ifdef HAVE_SDL2 | #ifdef HAVE_SDL2 | ||||||
| #include "audio_core/sdl2_sink.h" | #include "audio_core/sdl2_sink.h" | ||||||
| #endif | #endif | ||||||
|  | @ -17,9 +15,9 @@ namespace AudioCore { | ||||||
| // g_sink_details is ordered in terms of desirability, with the best choice at the top.
 | // g_sink_details is ordered in terms of desirability, with the best choice at the top.
 | ||||||
| const std::vector<SinkDetails> g_sink_details = { | const std::vector<SinkDetails> g_sink_details = { | ||||||
| #ifdef HAVE_SDL2 | #ifdef HAVE_SDL2 | ||||||
|     { "sdl2", []() { return std::make_unique<SDL2Sink>(); } }, |     {"sdl2", []() { return std::make_unique<SDL2Sink>(); }}, | ||||||
| #endif | #endif | ||||||
|     { "null", []() { return std::make_unique<NullSink>(); } }, |     {"null", []() { return std::make_unique<NullSink>(); }}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } // namespace AudioCore
 | } // namespace AudioCore
 | ||||||
|  |  | ||||||
|  | @ -5,12 +5,9 @@ | ||||||
| #include <chrono> | #include <chrono> | ||||||
| #include <cmath> | #include <cmath> | ||||||
| #include <vector> | #include <vector> | ||||||
| 
 |  | ||||||
| #include <SoundTouch.h> | #include <SoundTouch.h> | ||||||
| 
 |  | ||||||
| #include "audio_core/audio_core.h" | #include "audio_core/audio_core.h" | ||||||
| #include "audio_core/time_stretch.h" | #include "audio_core/time_stretch.h" | ||||||
| 
 |  | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
| #include "common/math_util.h" | #include "common/math_util.h" | ||||||
|  | @ -26,8 +23,8 @@ static double ClampRatio(double ratio) { | ||||||
|     return MathUtil::Clamp(ratio, MIN_RATIO, MAX_RATIO); |     return MathUtil::Clamp(ratio, MIN_RATIO, MAX_RATIO); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| constexpr double MIN_DELAY_TIME = 0.05; // Units: seconds
 | constexpr double MIN_DELAY_TIME = 0.05;            // Units: seconds
 | ||||||
| constexpr double MAX_DELAY_TIME = 0.25; // Units: seconds
 | constexpr double MAX_DELAY_TIME = 0.25;            // Units: seconds
 | ||||||
| constexpr size_t DROP_FRAMES_SAMPLE_DELAY = 16000; // Units: samples
 | constexpr size_t DROP_FRAMES_SAMPLE_DELAY = 16000; // Units: samples
 | ||||||
| 
 | 
 | ||||||
| constexpr double SMOOTHING_FACTOR = 0.007; | constexpr double SMOOTHING_FACTOR = 0.007; | ||||||
|  | @ -48,7 +45,8 @@ std::vector<s16> TimeStretcher::Process(size_t samples_in_queue) { | ||||||
| 
 | 
 | ||||||
|     double ratio = CalculateCurrentRatio(); |     double ratio = CalculateCurrentRatio(); | ||||||
|     ratio = CorrectForUnderAndOverflow(ratio, samples_in_queue); |     ratio = CorrectForUnderAndOverflow(ratio, samples_in_queue); | ||||||
|     impl->smoothed_ratio = (1.0 - SMOOTHING_FACTOR) * impl->smoothed_ratio + SMOOTHING_FACTOR * ratio; |     impl->smoothed_ratio = | ||||||
|  |         (1.0 - SMOOTHING_FACTOR) * impl->smoothed_ratio + SMOOTHING_FACTOR * ratio; | ||||||
|     impl->smoothed_ratio = ClampRatio(impl->smoothed_ratio); |     impl->smoothed_ratio = ClampRatio(impl->smoothed_ratio); | ||||||
| 
 | 
 | ||||||
|     // SoundTouch's tempo definition the inverse of our ratio definition.
 |     // SoundTouch's tempo definition the inverse of our ratio definition.
 | ||||||
|  | @ -100,7 +98,8 @@ double TimeStretcher::CalculateCurrentRatio() { | ||||||
|     const steady_clock::time_point now = steady_clock::now(); |     const steady_clock::time_point now = steady_clock::now(); | ||||||
|     const std::chrono::duration<double> duration = now - impl->frame_timer; |     const std::chrono::duration<double> duration = now - impl->frame_timer; | ||||||
| 
 | 
 | ||||||
|     const double expected_time = static_cast<double>(impl->samples_queued) / static_cast<double>(native_sample_rate); |     const double expected_time = | ||||||
|  |         static_cast<double>(impl->samples_queued) / static_cast<double>(native_sample_rate); | ||||||
|     const double actual_time = duration.count(); |     const double actual_time = duration.count(); | ||||||
| 
 | 
 | ||||||
|     double ratio; |     double ratio; | ||||||
|  |  | ||||||
|  | @ -5,7 +5,6 @@ | ||||||
| #include <cstddef> | #include <cstddef> | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <vector> | #include <vector> | ||||||
| 
 |  | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| 
 | 
 | ||||||
| namespace AudioCore { | namespace AudioCore { | ||||||
|  | @ -37,7 +36,8 @@ public: | ||||||
|     /**
 |     /**
 | ||||||
|      * Does audio stretching and produces the time-stretched samples. |      * Does audio stretching and produces the time-stretched samples. | ||||||
|      * Timer calculations use sample_delay to determine how much of a margin we have. |      * Timer calculations use sample_delay to determine how much of a margin we have. | ||||||
|      * @param sample_delay How many samples are buffered downstream of this module and haven't been played yet. |      * @param sample_delay How many samples are buffered downstream of this module and haven't been | ||||||
|  |      * played yet. | ||||||
|      * @return Samples to play in interleaved stereo PCM16 format. |      * @return Samples to play in interleaved stereo PCM16 format. | ||||||
|      */ |      */ | ||||||
|     std::vector<s16> Process(size_t sample_delay); |     std::vector<s16> Process(size_t sample_delay); | ||||||
|  | @ -48,7 +48,8 @@ private: | ||||||
| 
 | 
 | ||||||
|     /// INTERNAL: ratio = wallclock time / emulated time
 |     /// INTERNAL: ratio = wallclock time / emulated time
 | ||||||
|     double CalculateCurrentRatio(); |     double CalculateCurrentRatio(); | ||||||
|     /// INTERNAL: If we have too many or too few samples downstream, nudge ratio in the appropriate direction.
 |     /// INTERNAL: If we have too many or too few samples downstream, nudge ratio in the appropriate
 | ||||||
|  |     /// direction.
 | ||||||
|     double CorrectForUnderAndOverflow(double ratio, size_t sample_delay) const; |     double CorrectForUnderAndOverflow(double ratio, size_t sample_delay) const; | ||||||
|     /// INTERNAL: Gets the time-stretched samples from SoundTouch.
 |     /// INTERNAL: Gets the time-stretched samples from SoundTouch.
 | ||||||
|     std::vector<s16> GetSamples(); |     std::vector<s16> GetSamples(); | ||||||
|  |  | ||||||
|  | @ -2,10 +2,10 @@ | ||||||
| // Licensed under GPLv2 or any later version
 | // Licensed under GPLv2 or any later version
 | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| #include <string> |  | ||||||
| #include <thread> |  | ||||||
| #include <iostream> | #include <iostream> | ||||||
| #include <memory> | #include <memory> | ||||||
|  | #include <string> | ||||||
|  | #include <thread> | ||||||
| 
 | 
 | ||||||
| // This needs to be included before getopt.h because the latter #defines symbols used by it
 | // This needs to be included before getopt.h because the latter #defines symbols used by it
 | ||||||
| #include "common/microprofile.h" | #include "common/microprofile.h" | ||||||
|  | @ -13,53 +13,48 @@ | ||||||
| #ifdef _MSC_VER | #ifdef _MSC_VER | ||||||
| #include <getopt.h> | #include <getopt.h> | ||||||
| #else | #else | ||||||
| #include <unistd.h> |  | ||||||
| #include <getopt.h> | #include <getopt.h> | ||||||
|  | #include <unistd.h> | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef _WIN32 | #ifdef _WIN32 | ||||||
| #include <Windows.h> | #include <Windows.h> | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #include "common/logging/log.h" | #include "citra/config.h" | ||||||
|  | #include "citra/emu_window/emu_window_sdl2.h" | ||||||
| #include "common/logging/backend.h" | #include "common/logging/backend.h" | ||||||
| #include "common/logging/filter.h" | #include "common/logging/filter.h" | ||||||
|  | #include "common/logging/log.h" | ||||||
| #include "common/scm_rev.h" | #include "common/scm_rev.h" | ||||||
| #include "common/scope_exit.h" | #include "common/scope_exit.h" | ||||||
| #include "common/string_util.h" | #include "common/string_util.h" | ||||||
| 
 |  | ||||||
| #include "core/settings.h" |  | ||||||
| #include "core/system.h" |  | ||||||
| #include "core/core.h" | #include "core/core.h" | ||||||
| #include "core/gdbstub/gdbstub.h" | #include "core/gdbstub/gdbstub.h" | ||||||
| #include "core/loader/loader.h" | #include "core/loader/loader.h" | ||||||
| 
 | #include "core/settings.h" | ||||||
| #include "citra/config.h" | #include "core/system.h" | ||||||
| #include "citra/emu_window/emu_window_sdl2.h" |  | ||||||
| 
 |  | ||||||
| #include "video_core/video_core.h" | #include "video_core/video_core.h" | ||||||
| 
 | 
 | ||||||
| 
 | static void PrintHelp(const char* argv0) { | ||||||
| static void PrintHelp(const char *argv0) |     std::cout << "Usage: " << argv0 | ||||||
| { |               << " [options] <filename>\n" | ||||||
|     std::cout << "Usage: " << argv0 << " [options] <filename>\n" |  | ||||||
|                  "-g, --gdbport=NUMBER  Enable gdb stub on port NUMBER\n" |                  "-g, --gdbport=NUMBER  Enable gdb stub on port NUMBER\n" | ||||||
|                  "-h, --help            Display this help and exit\n" |                  "-h, --help            Display this help and exit\n" | ||||||
|                  "-v, --version         Output version information and exit\n"; |                  "-v, --version         Output version information and exit\n"; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void PrintVersion() | static void PrintVersion() { | ||||||
| { |  | ||||||
|     std::cout << "Citra " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl; |     std::cout << "Citra " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Application entry point
 | /// Application entry point
 | ||||||
| int main(int argc, char **argv) { | int main(int argc, char** argv) { | ||||||
|     Config config; |     Config config; | ||||||
|     int option_index = 0; |     int option_index = 0; | ||||||
|     bool use_gdbstub = Settings::values.use_gdbstub; |     bool use_gdbstub = Settings::values.use_gdbstub; | ||||||
|     u32 gdb_port = static_cast<u32>(Settings::values.gdbstub_port); |     u32 gdb_port = static_cast<u32>(Settings::values.gdbstub_port); | ||||||
|     char *endarg; |     char* endarg; | ||||||
| #ifdef _WIN32 | #ifdef _WIN32 | ||||||
|     int argc_w; |     int argc_w; | ||||||
|     auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w); |     auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w); | ||||||
|  | @ -72,10 +67,10 @@ int main(int argc, char **argv) { | ||||||
|     std::string boot_filename; |     std::string boot_filename; | ||||||
| 
 | 
 | ||||||
|     static struct option long_options[] = { |     static struct option long_options[] = { | ||||||
|         { "gdbport", required_argument, 0, 'g' }, |         {"gdbport", required_argument, 0, 'g'}, | ||||||
|         { "help", no_argument, 0, 'h' }, |         {"help", no_argument, 0, 'h'}, | ||||||
|         { "version", no_argument, 0, 'v' }, |         {"version", no_argument, 0, 'v'}, | ||||||
|         { 0, 0, 0, 0 } |         {0, 0, 0, 0}, | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     while (optind < argc) { |     while (optind < argc) { | ||||||
|  | @ -86,7 +81,8 @@ int main(int argc, char **argv) { | ||||||
|                 errno = 0; |                 errno = 0; | ||||||
|                 gdb_port = strtoul(optarg, &endarg, 0); |                 gdb_port = strtoul(optarg, &endarg, 0); | ||||||
|                 use_gdbstub = true; |                 use_gdbstub = true; | ||||||
|                 if (endarg == optarg) errno = EINVAL; |                 if (endarg == optarg) | ||||||
|  |                     errno = EINVAL; | ||||||
|                 if (errno != 0) { |                 if (errno != 0) { | ||||||
|                     perror("--gdbport"); |                     perror("--gdbport"); | ||||||
|                     exit(1); |                     exit(1); | ||||||
|  |  | ||||||
|  | @ -3,19 +3,13 @@ | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| #include <memory> | #include <memory> | ||||||
| 
 |  | ||||||
| #include <inih/cpp/INIReader.h> |  | ||||||
| 
 |  | ||||||
| #include <SDL.h> | #include <SDL.h> | ||||||
| 
 | #include <inih/cpp/INIReader.h> | ||||||
| #include "citra/default_ini.h" | #include "citra/default_ini.h" | ||||||
| 
 |  | ||||||
| #include "common/file_util.h" | #include "common/file_util.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
| 
 |  | ||||||
| #include "core/settings.h" |  | ||||||
| 
 |  | ||||||
| #include "config.h" | #include "config.h" | ||||||
|  | #include "core/settings.h" | ||||||
| 
 | 
 | ||||||
| Config::Config() { | Config::Config() { | ||||||
|     // TODO: Don't hardcode the path; let the frontend decide where to put the config files.
 |     // TODO: Don't hardcode the path; let the frontend decide where to put the config files.
 | ||||||
|  | @ -45,15 +39,13 @@ bool Config::LoadINI(const std::string& default_contents, bool retry) { | ||||||
| 
 | 
 | ||||||
| static const std::array<int, Settings::NativeInput::NUM_INPUTS> defaults = { | static const std::array<int, Settings::NativeInput::NUM_INPUTS> defaults = { | ||||||
|     // directly mapped keys
 |     // directly mapped keys
 | ||||||
|     SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X, |     SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_Q, SDL_SCANCODE_W, | ||||||
|     SDL_SCANCODE_Q, SDL_SCANCODE_W, SDL_SCANCODE_1, SDL_SCANCODE_2, |     SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_B, SDL_SCANCODE_T, | ||||||
|     SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_B, |     SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H, SDL_SCANCODE_I, SDL_SCANCODE_K, SDL_SCANCODE_J, | ||||||
|     SDL_SCANCODE_T, SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H, |     SDL_SCANCODE_L, | ||||||
|     SDL_SCANCODE_I, SDL_SCANCODE_K, SDL_SCANCODE_J, SDL_SCANCODE_L, |  | ||||||
| 
 | 
 | ||||||
|     // indirectly mapped keys
 |     // indirectly mapped keys
 | ||||||
|     SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT, |     SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT, SDL_SCANCODE_D, | ||||||
|     SDL_SCANCODE_D, |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| void Config::ReadValues() { | void Config::ReadValues() { | ||||||
|  | @ -62,7 +54,8 @@ void Config::ReadValues() { | ||||||
|         Settings::values.input_mappings[Settings::NativeInput::All[i]] = |         Settings::values.input_mappings[Settings::NativeInput::All[i]] = | ||||||
|             sdl2_config->GetInteger("Controls", Settings::NativeInput::Mapping[i], defaults[i]); |             sdl2_config->GetInteger("Controls", Settings::NativeInput::Mapping[i], defaults[i]); | ||||||
|     } |     } | ||||||
|     Settings::values.pad_circle_modifier_scale = (float)sdl2_config->GetReal("Controls", "pad_circle_modifier_scale", 0.5); |     Settings::values.pad_circle_modifier_scale = | ||||||
|  |         (float)sdl2_config->GetReal("Controls", "pad_circle_modifier_scale", 0.5); | ||||||
| 
 | 
 | ||||||
|     // Core
 |     // Core
 | ||||||
|     Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true); |     Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true); | ||||||
|  | @ -71,19 +64,22 @@ void Config::ReadValues() { | ||||||
|     // Renderer
 |     // Renderer
 | ||||||
|     Settings::values.use_hw_renderer = sdl2_config->GetBoolean("Renderer", "use_hw_renderer", true); |     Settings::values.use_hw_renderer = sdl2_config->GetBoolean("Renderer", "use_hw_renderer", true); | ||||||
|     Settings::values.use_shader_jit = sdl2_config->GetBoolean("Renderer", "use_shader_jit", true); |     Settings::values.use_shader_jit = sdl2_config->GetBoolean("Renderer", "use_shader_jit", true); | ||||||
|     Settings::values.use_scaled_resolution = sdl2_config->GetBoolean("Renderer", "use_scaled_resolution", false); |     Settings::values.use_scaled_resolution = | ||||||
|  |         sdl2_config->GetBoolean("Renderer", "use_scaled_resolution", false); | ||||||
|     Settings::values.use_vsync = sdl2_config->GetBoolean("Renderer", "use_vsync", false); |     Settings::values.use_vsync = sdl2_config->GetBoolean("Renderer", "use_vsync", false); | ||||||
| 
 | 
 | ||||||
|     Settings::values.bg_red   = (float)sdl2_config->GetReal("Renderer", "bg_red",   1.0); |     Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 1.0); | ||||||
|     Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 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
 |     // Audio
 | ||||||
|     Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto"); |     Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto"); | ||||||
|     Settings::values.enable_audio_stretching = sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true); |     Settings::values.enable_audio_stretching = | ||||||
|  |         sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true); | ||||||
| 
 | 
 | ||||||
|     // Data Storage
 |     // Data Storage
 | ||||||
|     Settings::values.use_virtual_sd = sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true); |     Settings::values.use_virtual_sd = | ||||||
|  |         sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true); | ||||||
| 
 | 
 | ||||||
|     // System
 |     // System
 | ||||||
|     Settings::values.is_new_3ds = sdl2_config->GetBoolean("System", "is_new_3ds", false); |     Settings::values.is_new_3ds = sdl2_config->GetBoolean("System", "is_new_3ds", false); | ||||||
|  | @ -94,7 +90,8 @@ void Config::ReadValues() { | ||||||
| 
 | 
 | ||||||
|     // Debugging
 |     // Debugging
 | ||||||
|     Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false); |     Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false); | ||||||
|     Settings::values.gdbstub_port = static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689)); |     Settings::values.gdbstub_port = | ||||||
|  |         static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Config::Reload() { | void Config::Reload() { | ||||||
|  |  | ||||||
|  | @ -6,15 +6,15 @@ | ||||||
| 
 | 
 | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <string> | #include <string> | ||||||
| 
 |  | ||||||
| #include <inih/cpp/INIReader.h> | #include <inih/cpp/INIReader.h> | ||||||
| 
 | 
 | ||||||
| class Config { | class Config { | ||||||
|     std::unique_ptr<INIReader> sdl2_config; |     std::unique_ptr<INIReader> sdl2_config; | ||||||
|     std::string sdl2_config_loc; |     std::string sdl2_config_loc; | ||||||
| 
 | 
 | ||||||
|     bool LoadINI(const std::string& default_contents="", bool retry=true); |     bool LoadINI(const std::string& default_contents = "", bool retry = true); | ||||||
|     void ReadValues(); |     void ReadValues(); | ||||||
|  | 
 | ||||||
| public: | public: | ||||||
|     Config(); |     Config(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -104,5 +104,4 @@ log_filter = *:Info | ||||||
| use_gdbstub=false | use_gdbstub=false | ||||||
| gdbstub_port=24689 | gdbstub_port=24689 | ||||||
| )"; | )"; | ||||||
| 
 |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -5,22 +5,16 @@ | ||||||
| #include <algorithm> | #include <algorithm> | ||||||
| #include <cstdlib> | #include <cstdlib> | ||||||
| #include <string> | #include <string> | ||||||
| 
 |  | ||||||
| #define SDL_MAIN_HANDLED | #define SDL_MAIN_HANDLED | ||||||
| #include <SDL.h> | #include <SDL.h> | ||||||
| 
 |  | ||||||
| #include <glad/glad.h> | #include <glad/glad.h> | ||||||
| 
 | #include "citra/emu_window/emu_window_sdl2.h" | ||||||
| #include "common/key_map.h" | #include "common/key_map.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
| #include "common/scm_rev.h" | #include "common/scm_rev.h" | ||||||
| #include "common/string_util.h" | #include "common/string_util.h" | ||||||
| 
 |  | ||||||
| #include "core/settings.h" |  | ||||||
| #include "core/hle/service/hid/hid.h" | #include "core/hle/service/hid/hid.h" | ||||||
| 
 | #include "core/settings.h" | ||||||
| #include "citra/emu_window/emu_window_sdl2.h" |  | ||||||
| 
 |  | ||||||
| #include "video_core/video_core.h" | #include "video_core/video_core.h" | ||||||
| 
 | 
 | ||||||
| void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { | void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { | ||||||
|  | @ -40,9 +34,9 @@ void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) { | ||||||
| 
 | 
 | ||||||
| void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) { | void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) { | ||||||
|     if (state == SDL_PRESSED) { |     if (state == SDL_PRESSED) { | ||||||
|         KeyMap::PressKey(*this, { key, keyboard_id }); |         KeyMap::PressKey(*this, {key, keyboard_id}); | ||||||
|     } else if (state == SDL_RELEASED) { |     } else if (state == SDL_RELEASED) { | ||||||
|         KeyMap::ReleaseKey(*this, { key, keyboard_id }); |         KeyMap::ReleaseKey(*this, {key, keyboard_id}); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -55,7 +49,8 @@ void EmuWindow_SDL2::OnResize() { | ||||||
| 
 | 
 | ||||||
|     SDL_GetWindowSize(render_window, &width, &height); |     SDL_GetWindowSize(render_window, &width, &height); | ||||||
| 
 | 
 | ||||||
|     NotifyFramebufferLayoutChanged(EmuWindow::FramebufferLayout::DefaultScreenLayout(width, height)); |     NotifyFramebufferLayoutChanged( | ||||||
|  |         EmuWindow::FramebufferLayout::DefaultScreenLayout(width, height)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| EmuWindow_SDL2::EmuWindow_SDL2() { | EmuWindow_SDL2::EmuWindow_SDL2() { | ||||||
|  | @ -80,12 +75,13 @@ EmuWindow_SDL2::EmuWindow_SDL2() { | ||||||
|     SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); |     SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); | ||||||
|     SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); |     SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); | ||||||
| 
 | 
 | ||||||
|     std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc); |     std::string window_title = | ||||||
|     render_window = SDL_CreateWindow(window_title.c_str(), |         Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc); | ||||||
|  |     render_window = SDL_CreateWindow( | ||||||
|  |         window_title.c_str(), | ||||||
|         SDL_WINDOWPOS_UNDEFINED, // x position
 |         SDL_WINDOWPOS_UNDEFINED, // x position
 | ||||||
|         SDL_WINDOWPOS_UNDEFINED, // y position
 |         SDL_WINDOWPOS_UNDEFINED, // y position
 | ||||||
|         VideoCore::kScreenTopWidth, |         VideoCore::kScreenTopWidth, VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight, | ||||||
|         VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight, |  | ||||||
|         SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); |         SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); | ||||||
| 
 | 
 | ||||||
|     if (render_window == nullptr) { |     if (render_window == nullptr) { | ||||||
|  | @ -171,10 +167,14 @@ void EmuWindow_SDL2::DoneCurrent() { | ||||||
| void EmuWindow_SDL2::ReloadSetKeymaps() { | void EmuWindow_SDL2::ReloadSetKeymaps() { | ||||||
|     KeyMap::ClearKeyMapping(keyboard_id); |     KeyMap::ClearKeyMapping(keyboard_id); | ||||||
|     for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { |     for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { | ||||||
|         KeyMap::SetKeyMapping({ Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id }, KeyMap::mapping_targets[i]); |         KeyMap::SetKeyMapping( | ||||||
|  |             {Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id}, | ||||||
|  |             KeyMap::mapping_targets[i]); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest(const std::pair<unsigned, unsigned>& minimal_size) { | void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest( | ||||||
|  |     const std::pair<unsigned, unsigned>& minimal_size) { | ||||||
|  | 
 | ||||||
|     SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second); |     SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -5,7 +5,6 @@ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <utility> | #include <utility> | ||||||
| 
 |  | ||||||
| #include "common/emu_window.h" | #include "common/emu_window.h" | ||||||
| 
 | 
 | ||||||
| struct SDL_Window; | struct SDL_Window; | ||||||
|  | @ -47,7 +46,8 @@ private: | ||||||
|     void OnResize(); |     void OnResize(); | ||||||
| 
 | 
 | ||||||
|     /// Called when a configuration change affects the minimal size of the window
 |     /// Called when a configuration change affects the minimal size of the window
 | ||||||
|     void OnMinimalClientAreaChangeRequest(const std::pair<unsigned, unsigned>& minimal_size) override; |     void OnMinimalClientAreaChangeRequest( | ||||||
|  |         const std::pair<unsigned, unsigned>& minimal_size) override; | ||||||
| 
 | 
 | ||||||
|     /// Is the window still open?
 |     /// Is the window still open?
 | ||||||
|     bool is_open = true; |     bool is_open = true; | ||||||
|  | @ -55,7 +55,7 @@ private: | ||||||
|     /// Internal SDL2 render window
 |     /// Internal SDL2 render window
 | ||||||
|     SDL_Window* render_window; |     SDL_Window* render_window; | ||||||
| 
 | 
 | ||||||
|     using SDL_GLContext = void *; |     using SDL_GLContext = void*; | ||||||
|     /// The OpenGL context associated with the window
 |     /// The OpenGL context associated with the window
 | ||||||
|     SDL_GLContext gl_context; |     SDL_GLContext gl_context; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -2,15 +2,15 @@ | ||||||
| // Microsoft Visual C++ generated include file.
 | // Microsoft Visual C++ generated include file.
 | ||||||
| // Used by pcafe.rc
 | // Used by pcafe.rc
 | ||||||
| //
 | //
 | ||||||
| #define IDI_ICON3                       103 | #define IDI_ICON3 103 | ||||||
| 
 | 
 | ||||||
| // Next default values for new objects
 | // Next default values for new objects
 | ||||||
| //
 | //
 | ||||||
| #ifdef APSTUDIO_INVOKED | #ifdef APSTUDIO_INVOKED | ||||||
| #ifndef APSTUDIO_READONLY_SYMBOLS | #ifndef APSTUDIO_READONLY_SYMBOLS | ||||||
| #define _APS_NEXT_RESOURCE_VALUE        105 | #define _APS_NEXT_RESOURCE_VALUE 105 | ||||||
| #define _APS_NEXT_COMMAND_VALUE         40001 | #define _APS_NEXT_COMMAND_VALUE 40001 | ||||||
| #define _APS_NEXT_CONTROL_VALUE         1001 | #define _APS_NEXT_CONTROL_VALUE 1001 | ||||||
| #define _APS_NEXT_SYMED_VALUE           101 | #define _APS_NEXT_SYMED_VALUE 101 | ||||||
| #endif | #endif | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | @ -9,27 +9,23 @@ | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #include "citra_qt/bootmanager.h" | #include "citra_qt/bootmanager.h" | ||||||
| 
 |  | ||||||
| #include "common/key_map.h" | #include "common/key_map.h" | ||||||
| #include "common/microprofile.h" | #include "common/microprofile.h" | ||||||
| #include "common/scm_rev.h" | #include "common/scm_rev.h" | ||||||
| #include "common/string_util.h" | #include "common/string_util.h" | ||||||
| 
 |  | ||||||
| #include "core/core.h" | #include "core/core.h" | ||||||
| #include "core/settings.h" | #include "core/settings.h" | ||||||
| #include "core/system.h" | #include "core/system.h" | ||||||
| 
 |  | ||||||
| #include "video_core/debug_utils/debug_utils.h" | #include "video_core/debug_utils/debug_utils.h" | ||||||
| #include "video_core/video_core.h" | #include "video_core/video_core.h" | ||||||
| 
 | 
 | ||||||
| #define APP_NAME        "citra" | #define APP_NAME "citra" | ||||||
| #define APP_VERSION     "0.1-" VERSION | #define APP_VERSION "0.1-" VERSION | ||||||
| #define APP_TITLE       APP_NAME " " APP_VERSION | #define APP_TITLE APP_NAME " " APP_VERSION | ||||||
| #define COPYRIGHT       "Copyright (C) 2013-2014 Citra Team" | #define COPYRIGHT "Copyright (C) 2013-2014 Citra Team" | ||||||
| 
 | 
 | ||||||
| EmuThread::EmuThread(GRenderWindow* render_window) : | EmuThread::EmuThread(GRenderWindow* render_window) | ||||||
|     exec_step(false), running(false), stop_run(false), render_window(render_window) { |     : exec_step(false), running(false), stop_run(false), render_window(render_window) {} | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| void EmuThread::run() { | void EmuThread::run() { | ||||||
|     render_window->MakeCurrent(); |     render_window->MakeCurrent(); | ||||||
|  | @ -64,7 +60,7 @@ void EmuThread::run() { | ||||||
|             was_active = false; |             was_active = false; | ||||||
|         } else { |         } else { | ||||||
|             std::unique_lock<std::mutex> lock(running_mutex); |             std::unique_lock<std::mutex> lock(running_mutex); | ||||||
|             running_cv.wait(lock, [this]{ return IsRunning() || exec_step || stop_run; }); |             running_cv.wait(lock, [this] { return IsRunning() || exec_step || stop_run; }); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -78,14 +74,13 @@ void EmuThread::run() { | ||||||
|     render_window->moveContext(); |     render_window->moveContext(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL context.
 | // This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL
 | ||||||
|  | // context.
 | ||||||
| // The corresponding functionality is handled in EmuThread instead
 | // The corresponding functionality is handled in EmuThread instead
 | ||||||
| class GGLWidgetInternal : public QGLWidget | class GGLWidgetInternal : public QGLWidget { | ||||||
| { |  | ||||||
| public: | public: | ||||||
|     GGLWidgetInternal(QGLFormat fmt, GRenderWindow* parent) |     GGLWidgetInternal(QGLFormat fmt, GRenderWindow* parent) | ||||||
|                      : QGLWidget(fmt, parent), parent(parent) { |         : QGLWidget(fmt, parent), parent(parent) {} | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     void paintEvent(QPaintEvent* ev) override { |     void paintEvent(QPaintEvent* ev) override { | ||||||
|         if (do_painting) { |         if (do_painting) { | ||||||
|  | @ -98,37 +93,43 @@ public: | ||||||
|         parent->OnFramebufferSizeChanged(); |         parent->OnFramebufferSizeChanged(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void DisablePainting() { do_painting = false; } |     void DisablePainting() { | ||||||
|     void EnablePainting() { do_painting = true; } |         do_painting = false; | ||||||
|  |     } | ||||||
|  |     void EnablePainting() { | ||||||
|  |         do_painting = true; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     GRenderWindow* parent; |     GRenderWindow* parent; | ||||||
|     bool do_painting; |     bool do_painting; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) : | GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) | ||||||
|     QWidget(parent), keyboard_id(0), emu_thread(emu_thread), child(nullptr) { |     : QWidget(parent), keyboard_id(0), emu_thread(emu_thread), child(nullptr) { | ||||||
| 
 | 
 | ||||||
|     std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc); |     std::string window_title = | ||||||
|  |         Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc); | ||||||
|     setWindowTitle(QString::fromStdString(window_title)); |     setWindowTitle(QString::fromStdString(window_title)); | ||||||
| 
 | 
 | ||||||
|     keyboard_id = KeyMap::NewDeviceId(); |     keyboard_id = KeyMap::NewDeviceId(); | ||||||
|     ReloadSetKeymaps(); |     ReloadSetKeymaps(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GRenderWindow::moveContext() | void GRenderWindow::moveContext() { | ||||||
| { |  | ||||||
|     DoneCurrent(); |     DoneCurrent(); | ||||||
|     // We need to move GL context to the swapping thread in Qt5
 | // We need to move GL context to the swapping thread in Qt5
 | ||||||
| #if QT_VERSION > QT_VERSION_CHECK(5, 0, 0) | #if QT_VERSION > QT_VERSION_CHECK(5, 0, 0) | ||||||
|     // If the thread started running, move the GL Context to the new thread. Otherwise, move it back.
 |     // If the thread started running, move the GL Context to the new thread. Otherwise, move it
 | ||||||
|     auto thread = (QThread::currentThread() == qApp->thread() && emu_thread != nullptr) ? emu_thread : qApp->thread(); |     // back.
 | ||||||
|  |     auto thread = (QThread::currentThread() == qApp->thread() && emu_thread != nullptr) | ||||||
|  |                       ? emu_thread | ||||||
|  |                       : qApp->thread(); | ||||||
|     child->context()->moveToThread(thread); |     child->context()->moveToThread(thread); | ||||||
| #endif | #endif | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GRenderWindow::SwapBuffers() | void GRenderWindow::SwapBuffers() { | ||||||
| { |  | ||||||
| #if !defined(QT_NO_DEBUG) | #if !defined(QT_NO_DEBUG) | ||||||
|     // Qt debug runtime prints a bogus warning on the console if you haven't called makeCurrent
 |     // Qt debug runtime prints a bogus warning on the console if you haven't called makeCurrent
 | ||||||
|     // since the last time you called swapBuffers. This presumably means something if you're using
 |     // since the last time you called swapBuffers. This presumably means something if you're using
 | ||||||
|  | @ -139,54 +140,48 @@ void GRenderWindow::SwapBuffers() | ||||||
|     child->swapBuffers(); |     child->swapBuffers(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GRenderWindow::MakeCurrent() | void GRenderWindow::MakeCurrent() { | ||||||
| { |  | ||||||
|     child->makeCurrent(); |     child->makeCurrent(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GRenderWindow::DoneCurrent() | void GRenderWindow::DoneCurrent() { | ||||||
| { |  | ||||||
|     child->doneCurrent(); |     child->doneCurrent(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GRenderWindow::PollEvents() { | void GRenderWindow::PollEvents() {} | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| // On Qt 5.0+, this correctly gets the size of the framebuffer (pixels).
 | // On Qt 5.0+, this correctly gets the size of the framebuffer (pixels).
 | ||||||
| //
 | //
 | ||||||
| // Older versions get the window size (density independent pixels),
 | // Older versions get the window size (density independent pixels),
 | ||||||
| // and hence, do not support DPI scaling ("retina" displays).
 | // and hence, do not support DPI scaling ("retina" displays).
 | ||||||
| // The result will be a viewport that is smaller than the extent of the window.
 | // The result will be a viewport that is smaller than the extent of the window.
 | ||||||
| void GRenderWindow::OnFramebufferSizeChanged() | void GRenderWindow::OnFramebufferSizeChanged() { | ||||||
| { |     // Screen changes potentially incur a change in screen DPI, hence we should update the
 | ||||||
|     // Screen changes potentially incur a change in screen DPI, hence we should update the framebuffer size
 |     // framebuffer size
 | ||||||
|     qreal pixelRatio = windowPixelRatio(); |     qreal pixelRatio = windowPixelRatio(); | ||||||
|     unsigned width = child->QPaintDevice::width() * pixelRatio; |     unsigned width = child->QPaintDevice::width() * pixelRatio; | ||||||
|     unsigned height = child->QPaintDevice::height() * pixelRatio; |     unsigned height = child->QPaintDevice::height() * pixelRatio; | ||||||
| 
 | 
 | ||||||
|     NotifyFramebufferLayoutChanged(EmuWindow::FramebufferLayout::DefaultScreenLayout(width, height)); |     NotifyFramebufferLayoutChanged( | ||||||
|  |         EmuWindow::FramebufferLayout::DefaultScreenLayout(width, height)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GRenderWindow::BackupGeometry() | void GRenderWindow::BackupGeometry() { | ||||||
| { |  | ||||||
|     geometry = ((QGLWidget*)this)->saveGeometry(); |     geometry = ((QGLWidget*)this)->saveGeometry(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GRenderWindow::RestoreGeometry() | void GRenderWindow::RestoreGeometry() { | ||||||
| { |  | ||||||
|     // We don't want to back up the geometry here (obviously)
 |     // We don't want to back up the geometry here (obviously)
 | ||||||
|     QWidget::restoreGeometry(geometry); |     QWidget::restoreGeometry(geometry); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GRenderWindow::restoreGeometry(const QByteArray& geometry) | void GRenderWindow::restoreGeometry(const QByteArray& geometry) { | ||||||
| { |  | ||||||
|     // Make sure users of this class don't need to deal with backing up the geometry themselves
 |     // Make sure users of this class don't need to deal with backing up the geometry themselves
 | ||||||
|     QWidget::restoreGeometry(geometry); |     QWidget::restoreGeometry(geometry); | ||||||
|     BackupGeometry(); |     BackupGeometry(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| QByteArray GRenderWindow::saveGeometry() | QByteArray GRenderWindow::saveGeometry() { | ||||||
| { |  | ||||||
|     // If we are a top-level widget, store the current geometry
 |     // If we are a top-level widget, store the current geometry
 | ||||||
|     // otherwise, store the last backup
 |     // otherwise, store the last backup
 | ||||||
|     if (parent() == nullptr) |     if (parent() == nullptr) | ||||||
|  | @ -195,8 +190,7 @@ QByteArray GRenderWindow::saveGeometry() | ||||||
|         return geometry; |         return geometry; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| qreal GRenderWindow::windowPixelRatio() | qreal GRenderWindow::windowPixelRatio() { | ||||||
| { |  | ||||||
| #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) | #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) | ||||||
|     // windowHandle() might not be accessible until the window is displayed to screen.
 |     // windowHandle() might not be accessible until the window is displayed to screen.
 | ||||||
|     return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f; |     return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f; | ||||||
|  | @ -210,20 +204,16 @@ void GRenderWindow::closeEvent(QCloseEvent* event) { | ||||||
|     QWidget::closeEvent(event); |     QWidget::closeEvent(event); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GRenderWindow::keyPressEvent(QKeyEvent* event) | void GRenderWindow::keyPressEvent(QKeyEvent* event) { | ||||||
| { |     KeyMap::PressKey(*this, {event->key(), keyboard_id}); | ||||||
|     KeyMap::PressKey(*this, { event->key(), keyboard_id }); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GRenderWindow::keyReleaseEvent(QKeyEvent* event) | void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { | ||||||
| { |     KeyMap::ReleaseKey(*this, {event->key(), keyboard_id}); | ||||||
|     KeyMap::ReleaseKey(*this, { event->key(), keyboard_id }); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GRenderWindow::mousePressEvent(QMouseEvent *event) | void GRenderWindow::mousePressEvent(QMouseEvent* event) { | ||||||
| { |     if (event->button() == Qt::LeftButton) { | ||||||
|     if (event->button() == Qt::LeftButton) |  | ||||||
|     { |  | ||||||
|         auto pos = event->pos(); |         auto pos = event->pos(); | ||||||
|         qreal pixelRatio = windowPixelRatio(); |         qreal pixelRatio = windowPixelRatio(); | ||||||
|         this->TouchPressed(static_cast<unsigned>(pos.x() * pixelRatio), |         this->TouchPressed(static_cast<unsigned>(pos.x() * pixelRatio), | ||||||
|  | @ -231,30 +221,28 @@ void GRenderWindow::mousePressEvent(QMouseEvent *event) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GRenderWindow::mouseMoveEvent(QMouseEvent *event) | void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { | ||||||
| { |  | ||||||
|     auto pos = event->pos(); |     auto pos = event->pos(); | ||||||
|     qreal pixelRatio = windowPixelRatio(); |     qreal pixelRatio = windowPixelRatio(); | ||||||
|     this->TouchMoved(std::max(static_cast<unsigned>(pos.x() * pixelRatio), 0u), |     this->TouchMoved(std::max(static_cast<unsigned>(pos.x() * pixelRatio), 0u), | ||||||
|                      std::max(static_cast<unsigned>(pos.y() * pixelRatio), 0u)); |                      std::max(static_cast<unsigned>(pos.y() * pixelRatio), 0u)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GRenderWindow::mouseReleaseEvent(QMouseEvent *event) | void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { | ||||||
| { |  | ||||||
|     if (event->button() == Qt::LeftButton) |     if (event->button() == Qt::LeftButton) | ||||||
|         this->TouchReleased(); |         this->TouchReleased(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GRenderWindow::ReloadSetKeymaps() | void GRenderWindow::ReloadSetKeymaps() { | ||||||
| { |  | ||||||
|     KeyMap::ClearKeyMapping(keyboard_id); |     KeyMap::ClearKeyMapping(keyboard_id); | ||||||
|     for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { |     for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { | ||||||
|         KeyMap::SetKeyMapping({ Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id }, KeyMap::mapping_targets[i]); |         KeyMap::SetKeyMapping( | ||||||
|  |             {Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id}, | ||||||
|  |             KeyMap::mapping_targets[i]); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height) | void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height) { | ||||||
| { |  | ||||||
|     NotifyClientAreaSizeChanged(std::make_pair(width, height)); |     NotifyClientAreaSizeChanged(std::make_pair(width, height)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -267,7 +255,8 @@ void GRenderWindow::InitRenderTarget() { | ||||||
|         delete layout(); |         delete layout(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, WA_DontShowOnScreen, WA_DeleteOnClose
 |     // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
 | ||||||
|  |     // WA_DontShowOnScreen, WA_DeleteOnClose
 | ||||||
|     QGLFormat fmt; |     QGLFormat fmt; | ||||||
|     fmt.setVersion(3, 3); |     fmt.setVersion(3, 3); | ||||||
|     fmt.setProfile(QGLFormat::CoreProfile); |     fmt.setProfile(QGLFormat::CoreProfile); | ||||||
|  | @ -279,7 +268,8 @@ void GRenderWindow::InitRenderTarget() { | ||||||
|     child = new GGLWidgetInternal(fmt, this); |     child = new GGLWidgetInternal(fmt, this); | ||||||
|     QBoxLayout* layout = new QHBoxLayout(this); |     QBoxLayout* layout = new QHBoxLayout(this); | ||||||
| 
 | 
 | ||||||
|     resize(VideoCore::kScreenTopWidth, VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight); |     resize(VideoCore::kScreenTopWidth, | ||||||
|  |            VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight); | ||||||
|     layout->addWidget(child); |     layout->addWidget(child); | ||||||
|     layout->setMargin(0); |     layout->setMargin(0); | ||||||
|     setLayout(layout); |     setLayout(layout); | ||||||
|  | @ -292,7 +282,8 @@ void GRenderWindow::InitRenderTarget() { | ||||||
|     BackupGeometry(); |     BackupGeometry(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GRenderWindow::OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) { | void GRenderWindow::OnMinimalClientAreaChangeRequest( | ||||||
|  |     const std::pair<unsigned, unsigned>& minimal_size) { | ||||||
|     setMinimumSize(minimal_size.first, minimal_size.second); |     setMinimumSize(minimal_size.first, minimal_size.second); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -306,11 +297,12 @@ void GRenderWindow::OnEmulationStopping() { | ||||||
|     child->EnablePainting(); |     child->EnablePainting(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GRenderWindow::showEvent(QShowEvent * event) { | void GRenderWindow::showEvent(QShowEvent* event) { | ||||||
|     QWidget::showEvent(event); |     QWidget::showEvent(event); | ||||||
| 
 | 
 | ||||||
|  | #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) | ||||||
|     // windowHandle() is not initialized until the Window is shown, so we connect it here.
 |     // windowHandle() is not initialized until the Window is shown, so we connect it here.
 | ||||||
|     #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) |     connect(this->windowHandle(), SIGNAL(screenChanged(QScreen*)), this, | ||||||
|         connect(this->windowHandle(), SIGNAL(screenChanged(QScreen*)), this, SLOT(OnFramebufferSizeChanged()), Qt::UniqueConnection); |             SLOT(OnFramebufferSizeChanged()), Qt::UniqueConnection); | ||||||
|     #endif | #endif | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -5,10 +5,8 @@ | ||||||
| #include <atomic> | #include <atomic> | ||||||
| #include <condition_variable> | #include <condition_variable> | ||||||
| #include <mutex> | #include <mutex> | ||||||
| 
 |  | ||||||
| #include <QGLWidget> | #include <QGLWidget> | ||||||
| #include <QThread> | #include <QThread> | ||||||
| 
 |  | ||||||
| #include "common/emu_window.h" | #include "common/emu_window.h" | ||||||
| #include "common/thread.h" | #include "common/thread.h" | ||||||
| 
 | 
 | ||||||
|  | @ -19,8 +17,7 @@ class GGLWidgetInternal; | ||||||
| class GMainWindow; | class GMainWindow; | ||||||
| class GRenderWindow; | class GRenderWindow; | ||||||
| 
 | 
 | ||||||
| class EmuThread : public QThread | class EmuThread : public QThread { | ||||||
| { |  | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|  | @ -58,7 +55,9 @@ public: | ||||||
|      * @return True if the emulation thread is running, otherwise false |      * @return True if the emulation thread is running, otherwise false | ||||||
|      * @note This function is thread-safe |      * @note This function is thread-safe | ||||||
|      */ |      */ | ||||||
|     bool IsRunning() { return running; } |     bool IsRunning() { | ||||||
|  |         return running; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Requests for the emulation thread to stop running |      * Requests for the emulation thread to stop running | ||||||
|  | @ -81,20 +80,23 @@ signals: | ||||||
|     /**
 |     /**
 | ||||||
|      * Emitted when the CPU has halted execution |      * Emitted when the CPU has halted execution | ||||||
|      * |      * | ||||||
|      * @warning When connecting to this signal from other threads, make sure to specify either Qt::QueuedConnection (invoke slot within the destination object's message thread) or even Qt::BlockingQueuedConnection (additionally block source thread until slot returns) |      * @warning When connecting to this signal from other threads, make sure to specify either | ||||||
|  |      * Qt::QueuedConnection (invoke slot within the destination object's message thread) or even | ||||||
|  |      * Qt::BlockingQueuedConnection (additionally block source thread until slot returns) | ||||||
|      */ |      */ | ||||||
|     void DebugModeEntered(); |     void DebugModeEntered(); | ||||||
| 
 | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Emitted right before the CPU continues execution |      * Emitted right before the CPU continues execution | ||||||
|      * |      * | ||||||
|      * @warning When connecting to this signal from other threads, make sure to specify either Qt::QueuedConnection (invoke slot within the destination object's message thread) or even Qt::BlockingQueuedConnection (additionally block source thread until slot returns) |      * @warning When connecting to this signal from other threads, make sure to specify either | ||||||
|  |      * Qt::QueuedConnection (invoke slot within the destination object's message thread) or even | ||||||
|  |      * Qt::BlockingQueuedConnection (additionally block source thread until slot returns) | ||||||
|      */ |      */ | ||||||
|     void DebugModeLeft(); |     void DebugModeLeft(); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class GRenderWindow : public QWidget, public EmuWindow | class GRenderWindow : public QWidget, public EmuWindow { | ||||||
| { |  | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|  | @ -109,7 +111,7 @@ public: | ||||||
|     void BackupGeometry(); |     void BackupGeometry(); | ||||||
|     void RestoreGeometry(); |     void RestoreGeometry(); | ||||||
|     void restoreGeometry(const QByteArray& geometry); // overridden
 |     void restoreGeometry(const QByteArray& geometry); // overridden
 | ||||||
|     QByteArray saveGeometry();  // overridden
 |     QByteArray saveGeometry();                        // overridden
 | ||||||
| 
 | 
 | ||||||
|     qreal windowPixelRatio(); |     qreal windowPixelRatio(); | ||||||
| 
 | 
 | ||||||
|  | @ -118,9 +120,9 @@ public: | ||||||
|     void keyPressEvent(QKeyEvent* event) override; |     void keyPressEvent(QKeyEvent* event) override; | ||||||
|     void keyReleaseEvent(QKeyEvent* event) override; |     void keyReleaseEvent(QKeyEvent* event) override; | ||||||
| 
 | 
 | ||||||
|     void mousePressEvent(QMouseEvent *event) override; |     void mousePressEvent(QMouseEvent* event) override; | ||||||
|     void mouseMoveEvent(QMouseEvent *event) override; |     void mouseMoveEvent(QMouseEvent* event) override; | ||||||
|     void mouseReleaseEvent(QMouseEvent *event) override; |     void mouseReleaseEvent(QMouseEvent* event) override; | ||||||
| 
 | 
 | ||||||
|     void ReloadSetKeymaps() override; |     void ReloadSetKeymaps() override; | ||||||
| 
 | 
 | ||||||
|  | @ -129,7 +131,7 @@ public: | ||||||
|     void InitRenderTarget(); |     void InitRenderTarget(); | ||||||
| 
 | 
 | ||||||
| public slots: | public slots: | ||||||
|     void moveContext();  // overridden
 |     void moveContext(); // overridden
 | ||||||
| 
 | 
 | ||||||
|     void OnEmulationStarting(EmuThread* emu_thread); |     void OnEmulationStarting(EmuThread* emu_thread); | ||||||
|     void OnEmulationStopping(); |     void OnEmulationStopping(); | ||||||
|  | @ -140,7 +142,8 @@ signals: | ||||||
|     void Closed(); |     void Closed(); | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     void OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) override; |     void OnMinimalClientAreaChangeRequest( | ||||||
|  |         const std::pair<unsigned, unsigned>& minimal_size) override; | ||||||
| 
 | 
 | ||||||
|     GGLWidgetInternal* child; |     GGLWidgetInternal* child; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -3,10 +3,8 @@ | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| #include <QSettings> | #include <QSettings> | ||||||
| 
 |  | ||||||
| #include "citra_qt/config.h" | #include "citra_qt/config.h" | ||||||
| #include "citra_qt/ui_settings.h" | #include "citra_qt/ui_settings.h" | ||||||
| 
 |  | ||||||
| #include "common/file_util.h" | #include "common/file_util.h" | ||||||
| 
 | 
 | ||||||
| Config::Config() { | Config::Config() { | ||||||
|  | @ -20,24 +18,23 @@ Config::Config() { | ||||||
| 
 | 
 | ||||||
| const std::array<QVariant, Settings::NativeInput::NUM_INPUTS> Config::defaults = { | const std::array<QVariant, Settings::NativeInput::NUM_INPUTS> Config::defaults = { | ||||||
|     // directly mapped keys
 |     // directly mapped keys
 | ||||||
|     Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, |     Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_Q, Qt::Key_W, Qt::Key_1, Qt::Key_2, | ||||||
|     Qt::Key_Q, Qt::Key_W, Qt::Key_1, Qt::Key_2, |     Qt::Key_M, Qt::Key_N, Qt::Key_B, Qt::Key_T, Qt::Key_G, Qt::Key_F, Qt::Key_H, Qt::Key_I, | ||||||
|     Qt::Key_M, Qt::Key_N, Qt::Key_B, |     Qt::Key_K, Qt::Key_J, Qt::Key_L, | ||||||
|     Qt::Key_T, Qt::Key_G, Qt::Key_F, Qt::Key_H, |  | ||||||
|     Qt::Key_I, Qt::Key_K, Qt::Key_J, Qt::Key_L, |  | ||||||
| 
 | 
 | ||||||
|     // indirectly mapped keys
 |     // indirectly mapped keys
 | ||||||
|     Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right, |     Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right, Qt::Key_D, | ||||||
|     Qt::Key_D, |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| void Config::ReadValues() { | void Config::ReadValues() { | ||||||
|     qt_config->beginGroup("Controls"); |     qt_config->beginGroup("Controls"); | ||||||
|     for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { |     for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { | ||||||
|         Settings::values.input_mappings[Settings::NativeInput::All[i]] = |         Settings::values.input_mappings[Settings::NativeInput::All[i]] = | ||||||
|             qt_config->value(QString::fromStdString(Settings::NativeInput::Mapping[i]), defaults[i]).toInt(); |             qt_config->value(QString::fromStdString(Settings::NativeInput::Mapping[i]), defaults[i]) | ||||||
|  |                 .toInt(); | ||||||
|     } |     } | ||||||
|     Settings::values.pad_circle_modifier_scale = qt_config->value("pad_circle_modifier_scale", 0.5).toFloat(); |     Settings::values.pad_circle_modifier_scale = | ||||||
|  |         qt_config->value("pad_circle_modifier_scale", 0.5).toFloat(); | ||||||
|     qt_config->endGroup(); |     qt_config->endGroup(); | ||||||
| 
 | 
 | ||||||
|     qt_config->beginGroup("Core"); |     qt_config->beginGroup("Core"); | ||||||
|  | @ -48,17 +45,19 @@ void Config::ReadValues() { | ||||||
|     qt_config->beginGroup("Renderer"); |     qt_config->beginGroup("Renderer"); | ||||||
|     Settings::values.use_hw_renderer = qt_config->value("use_hw_renderer", true).toBool(); |     Settings::values.use_hw_renderer = qt_config->value("use_hw_renderer", true).toBool(); | ||||||
|     Settings::values.use_shader_jit = qt_config->value("use_shader_jit", true).toBool(); |     Settings::values.use_shader_jit = qt_config->value("use_shader_jit", true).toBool(); | ||||||
|     Settings::values.use_scaled_resolution = qt_config->value("use_scaled_resolution", false).toBool(); |     Settings::values.use_scaled_resolution = | ||||||
|  |         qt_config->value("use_scaled_resolution", false).toBool(); | ||||||
|     Settings::values.use_vsync = qt_config->value("use_vsync", false).toBool(); |     Settings::values.use_vsync = qt_config->value("use_vsync", false).toBool(); | ||||||
| 
 | 
 | ||||||
|     Settings::values.bg_red   = qt_config->value("bg_red",   1.0).toFloat(); |     Settings::values.bg_red = qt_config->value("bg_red", 1.0).toFloat(); | ||||||
|     Settings::values.bg_green = qt_config->value("bg_green", 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->endGroup(); | ||||||
| 
 | 
 | ||||||
|     qt_config->beginGroup("Audio"); |     qt_config->beginGroup("Audio"); | ||||||
|     Settings::values.sink_id = qt_config->value("output_engine", "auto").toString().toStdString(); |     Settings::values.sink_id = qt_config->value("output_engine", "auto").toString().toStdString(); | ||||||
|     Settings::values.enable_audio_stretching = qt_config->value("enable_audio_stretching", true).toBool(); |     Settings::values.enable_audio_stretching = | ||||||
|  |         qt_config->value("enable_audio_stretching", true).toBool(); | ||||||
|     qt_config->endGroup(); |     qt_config->endGroup(); | ||||||
| 
 | 
 | ||||||
|     qt_config->beginGroup("Data Storage"); |     qt_config->beginGroup("Data Storage"); | ||||||
|  | @ -84,10 +83,14 @@ void Config::ReadValues() { | ||||||
|     qt_config->beginGroup("UILayout"); |     qt_config->beginGroup("UILayout"); | ||||||
|     UISettings::values.geometry = qt_config->value("geometry").toByteArray(); |     UISettings::values.geometry = qt_config->value("geometry").toByteArray(); | ||||||
|     UISettings::values.state = qt_config->value("state").toByteArray(); |     UISettings::values.state = qt_config->value("state").toByteArray(); | ||||||
|     UISettings::values.renderwindow_geometry = qt_config->value("geometryRenderWindow").toByteArray(); |     UISettings::values.renderwindow_geometry = | ||||||
|     UISettings::values.gamelist_header_state = qt_config->value("gameListHeaderState").toByteArray(); |         qt_config->value("geometryRenderWindow").toByteArray(); | ||||||
|     UISettings::values.microprofile_geometry = qt_config->value("microProfileDialogGeometry").toByteArray(); |     UISettings::values.gamelist_header_state = | ||||||
|     UISettings::values.microprofile_visible = qt_config->value("microProfileDialogVisible", false).toBool(); |         qt_config->value("gameListHeaderState").toByteArray(); | ||||||
|  |     UISettings::values.microprofile_geometry = | ||||||
|  |         qt_config->value("microProfileDialogGeometry").toByteArray(); | ||||||
|  |     UISettings::values.microprofile_visible = | ||||||
|  |         qt_config->value("microProfileDialogVisible", false).toBool(); | ||||||
|     qt_config->endGroup(); |     qt_config->endGroup(); | ||||||
| 
 | 
 | ||||||
|     qt_config->beginGroup("Paths"); |     qt_config->beginGroup("Paths"); | ||||||
|  | @ -106,10 +109,10 @@ void Config::ReadValues() { | ||||||
|         QStringList hotkeys = qt_config->childGroups(); |         QStringList hotkeys = qt_config->childGroups(); | ||||||
|         for (auto hotkey : hotkeys) { |         for (auto hotkey : hotkeys) { | ||||||
|             qt_config->beginGroup(hotkey); |             qt_config->beginGroup(hotkey); | ||||||
|             UISettings::values.shortcuts.emplace_back( |             UISettings::values.shortcuts.emplace_back(UISettings::Shortcut( | ||||||
|                         UISettings::Shortcut(group + "/" + hotkey, |                 group + "/" + hotkey, | ||||||
|                                              UISettings::ContextualShortcut(qt_config->value("KeySeq").toString(), |                 UISettings::ContextualShortcut(qt_config->value("KeySeq").toString(), | ||||||
|                                                                             qt_config->value("Context").toInt()))); |                                                qt_config->value("Context").toInt()))); | ||||||
|             qt_config->endGroup(); |             qt_config->endGroup(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -119,7 +122,7 @@ void Config::ReadValues() { | ||||||
| 
 | 
 | ||||||
|     UISettings::values.single_window_mode = qt_config->value("singleWindowMode", true).toBool(); |     UISettings::values.single_window_mode = qt_config->value("singleWindowMode", true).toBool(); | ||||||
|     UISettings::values.display_titlebar = qt_config->value("displayTitleBars", true).toBool(); |     UISettings::values.display_titlebar = qt_config->value("displayTitleBars", true).toBool(); | ||||||
|     UISettings::values.confirm_before_closing = qt_config->value("confirmClose",true).toBool(); |     UISettings::values.confirm_before_closing = qt_config->value("confirmClose", true).toBool(); | ||||||
|     UISettings::values.first_start = qt_config->value("firstStart", true).toBool(); |     UISettings::values.first_start = qt_config->value("firstStart", true).toBool(); | ||||||
| 
 | 
 | ||||||
|     qt_config->endGroup(); |     qt_config->endGroup(); | ||||||
|  | @ -129,9 +132,10 @@ void Config::SaveValues() { | ||||||
|     qt_config->beginGroup("Controls"); |     qt_config->beginGroup("Controls"); | ||||||
|     for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { |     for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { | ||||||
|         qt_config->setValue(QString::fromStdString(Settings::NativeInput::Mapping[i]), |         qt_config->setValue(QString::fromStdString(Settings::NativeInput::Mapping[i]), | ||||||
|             Settings::values.input_mappings[Settings::NativeInput::All[i]]); |                             Settings::values.input_mappings[Settings::NativeInput::All[i]]); | ||||||
|     } |     } | ||||||
|     qt_config->setValue("pad_circle_modifier_scale", (double)Settings::values.pad_circle_modifier_scale); |     qt_config->setValue("pad_circle_modifier_scale", | ||||||
|  |                         (double)Settings::values.pad_circle_modifier_scale); | ||||||
|     qt_config->endGroup(); |     qt_config->endGroup(); | ||||||
| 
 | 
 | ||||||
|     qt_config->beginGroup("Core"); |     qt_config->beginGroup("Core"); | ||||||
|  | @ -146,9 +150,9 @@ void Config::SaveValues() { | ||||||
|     qt_config->setValue("use_vsync", Settings::values.use_vsync); |     qt_config->setValue("use_vsync", Settings::values.use_vsync); | ||||||
| 
 | 
 | ||||||
|     // Cast to double because Qt's written float values are not human-readable
 |     // 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_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->endGroup(); | ||||||
| 
 | 
 | ||||||
|     qt_config->beginGroup("Audio"); |     qt_config->beginGroup("Audio"); | ||||||
|  |  | ||||||
|  | @ -6,7 +6,6 @@ | ||||||
| 
 | 
 | ||||||
| #include <string> | #include <string> | ||||||
| #include <QVariant> | #include <QVariant> | ||||||
| 
 |  | ||||||
| #include "core/settings.h" | #include "core/settings.h" | ||||||
| 
 | 
 | ||||||
| class QSettings; | class QSettings; | ||||||
|  | @ -17,6 +16,7 @@ class Config { | ||||||
| 
 | 
 | ||||||
|     void ReadValues(); |     void ReadValues(); | ||||||
|     void SaveValues(); |     void SaveValues(); | ||||||
|  | 
 | ||||||
| public: | public: | ||||||
|     Config(); |     Config(); | ||||||
|     ~Config(); |     ~Config(); | ||||||
|  |  | ||||||
|  | @ -3,16 +3,12 @@ | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| #include "audio_core/sink_details.h" | #include "audio_core/sink_details.h" | ||||||
| 
 |  | ||||||
| #include "citra_qt/configure_audio.h" | #include "citra_qt/configure_audio.h" | ||||||
|  | #include "core/settings.h" | ||||||
| #include "ui_configure_audio.h" | #include "ui_configure_audio.h" | ||||||
| 
 | 
 | ||||||
| #include "core/settings.h" | ConfigureAudio::ConfigureAudio(QWidget* parent) | ||||||
| 
 |     : QWidget(parent), ui(std::make_unique<Ui::ConfigureAudio>()) { | ||||||
| ConfigureAudio::ConfigureAudio(QWidget* parent) : |  | ||||||
|         QWidget(parent), |  | ||||||
|         ui(std::make_unique<Ui::ConfigureAudio>()) |  | ||||||
| { |  | ||||||
|     ui->setupUi(this); |     ui->setupUi(this); | ||||||
| 
 | 
 | ||||||
|     ui->output_sink_combo_box->clear(); |     ui->output_sink_combo_box->clear(); | ||||||
|  | @ -24,8 +20,7 @@ ConfigureAudio::ConfigureAudio(QWidget* parent) : | ||||||
|     this->setConfiguration(); |     this->setConfiguration(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ConfigureAudio::~ConfigureAudio() { | ConfigureAudio::~ConfigureAudio() {} | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| void ConfigureAudio::setConfiguration() { | void ConfigureAudio::setConfiguration() { | ||||||
|     int new_sink_index = 0; |     int new_sink_index = 0; | ||||||
|  | @ -41,7 +36,9 @@ void ConfigureAudio::setConfiguration() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ConfigureAudio::applyConfiguration() { | void ConfigureAudio::applyConfiguration() { | ||||||
|     Settings::values.sink_id = ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex()).toStdString(); |     Settings::values.sink_id = | ||||||
|  |         ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex()) | ||||||
|  |             .toStdString(); | ||||||
|     Settings::values.enable_audio_stretching = ui->toggle_audio_stretching->isChecked(); |     Settings::values.enable_audio_stretching = ui->toggle_audio_stretching->isChecked(); | ||||||
|     Settings::Apply(); |     Settings::Apply(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -3,20 +3,15 @@ | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| #include "citra_qt/configure_debug.h" | #include "citra_qt/configure_debug.h" | ||||||
|  | #include "core/settings.h" | ||||||
| #include "ui_configure_debug.h" | #include "ui_configure_debug.h" | ||||||
| 
 | 
 | ||||||
| #include "core/settings.h" | ConfigureDebug::ConfigureDebug(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureDebug) { | ||||||
| 
 |  | ||||||
| ConfigureDebug::ConfigureDebug(QWidget *parent) : |  | ||||||
|     QWidget(parent), |  | ||||||
|     ui(new Ui::ConfigureDebug) |  | ||||||
| { |  | ||||||
|     ui->setupUi(this); |     ui->setupUi(this); | ||||||
|     this->setConfiguration(); |     this->setConfiguration(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ConfigureDebug::~ConfigureDebug() { | ConfigureDebug::~ConfigureDebug() {} | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| void ConfigureDebug::setConfiguration() { | void ConfigureDebug::setConfiguration() { | ||||||
|     ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub); |     ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub); | ||||||
|  |  | ||||||
|  | @ -11,12 +11,11 @@ namespace Ui { | ||||||
| class ConfigureDebug; | class ConfigureDebug; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class ConfigureDebug : public QWidget | class ConfigureDebug : public QWidget { | ||||||
| { |  | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|     explicit ConfigureDebug(QWidget *parent = nullptr); |     explicit ConfigureDebug(QWidget* parent = nullptr); | ||||||
|     ~ConfigureDebug(); |     ~ConfigureDebug(); | ||||||
| 
 | 
 | ||||||
|     void applyConfiguration(); |     void applyConfiguration(); | ||||||
|  |  | ||||||
|  | @ -4,24 +4,17 @@ | ||||||
| 
 | 
 | ||||||
| #include "citra_qt/config.h" | #include "citra_qt/config.h" | ||||||
| #include "citra_qt/configure_dialog.h" | #include "citra_qt/configure_dialog.h" | ||||||
|  | #include "core/settings.h" | ||||||
| #include "ui_configure.h" | #include "ui_configure.h" | ||||||
| 
 | 
 | ||||||
| 
 | ConfigureDialog::ConfigureDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ConfigureDialog) { | ||||||
| #include "core/settings.h" |  | ||||||
| 
 |  | ||||||
| ConfigureDialog::ConfigureDialog(QWidget *parent) : |  | ||||||
|     QDialog(parent), |  | ||||||
|     ui(new Ui::ConfigureDialog) |  | ||||||
| { |  | ||||||
|     ui->setupUi(this); |     ui->setupUi(this); | ||||||
|     this->setConfiguration(); |     this->setConfiguration(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ConfigureDialog::~ConfigureDialog() { | ConfigureDialog::~ConfigureDialog() {} | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| void ConfigureDialog::setConfiguration() { | void ConfigureDialog::setConfiguration() {} | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| void ConfigureDialog::applyConfiguration() { | void ConfigureDialog::applyConfiguration() { | ||||||
|     ui->generalTab->applyConfiguration(); |     ui->generalTab->applyConfiguration(); | ||||||
|  |  | ||||||
|  | @ -11,12 +11,11 @@ namespace Ui { | ||||||
| class ConfigureDialog; | class ConfigureDialog; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class ConfigureDialog : public QDialog | class ConfigureDialog : public QDialog { | ||||||
| { |  | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|     explicit ConfigureDialog(QWidget *parent); |     explicit ConfigureDialog(QWidget* parent); | ||||||
|     ~ConfigureDialog(); |     ~ConfigureDialog(); | ||||||
| 
 | 
 | ||||||
|     void applyConfiguration(); |     void applyConfiguration(); | ||||||
|  |  | ||||||
|  | @ -4,23 +4,20 @@ | ||||||
| 
 | 
 | ||||||
| #include "citra_qt/configure_general.h" | #include "citra_qt/configure_general.h" | ||||||
| #include "citra_qt/ui_settings.h" | #include "citra_qt/ui_settings.h" | ||||||
| #include "ui_configure_general.h" |  | ||||||
| 
 |  | ||||||
| #include "core/settings.h" | #include "core/settings.h" | ||||||
| #include "core/system.h" | #include "core/system.h" | ||||||
|  | #include "ui_configure_general.h" | ||||||
|  | 
 | ||||||
|  | ConfigureGeneral::ConfigureGeneral(QWidget* parent) | ||||||
|  |     : QWidget(parent), ui(new Ui::ConfigureGeneral) { | ||||||
| 
 | 
 | ||||||
| ConfigureGeneral::ConfigureGeneral(QWidget *parent) : |  | ||||||
|     QWidget(parent), |  | ||||||
|     ui(new Ui::ConfigureGeneral) |  | ||||||
| { |  | ||||||
|     ui->setupUi(this); |     ui->setupUi(this); | ||||||
|     this->setConfiguration(); |     this->setConfiguration(); | ||||||
| 
 | 
 | ||||||
|     ui->toggle_cpu_jit->setEnabled(!System::IsPoweredOn()); |     ui->toggle_cpu_jit->setEnabled(!System::IsPoweredOn()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ConfigureGeneral::~ConfigureGeneral() { | ConfigureGeneral::~ConfigureGeneral() {} | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| void ConfigureGeneral::setConfiguration() { | void ConfigureGeneral::setConfiguration() { | ||||||
|     ui->toggle_deepscan->setChecked(UISettings::values.gamedir_deepscan); |     ui->toggle_deepscan->setChecked(UISettings::values.gamedir_deepscan); | ||||||
|  |  | ||||||
|  | @ -11,12 +11,11 @@ namespace Ui { | ||||||
| class ConfigureGeneral; | class ConfigureGeneral; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class ConfigureGeneral : public QWidget | class ConfigureGeneral : public QWidget { | ||||||
| { |  | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|     explicit ConfigureGeneral(QWidget *parent = nullptr); |     explicit ConfigureGeneral(QWidget* parent = nullptr); | ||||||
|     ~ConfigureGeneral(); |     ~ConfigureGeneral(); | ||||||
| 
 | 
 | ||||||
|     void applyConfiguration(); |     void applyConfiguration(); | ||||||
|  |  | ||||||
|  | @ -3,23 +3,20 @@ | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| #include "citra_qt/configure_graphics.h" | #include "citra_qt/configure_graphics.h" | ||||||
| #include "ui_configure_graphics.h" |  | ||||||
| 
 |  | ||||||
| #include "core/settings.h" | #include "core/settings.h" | ||||||
| #include "core/system.h" | #include "core/system.h" | ||||||
|  | #include "ui_configure_graphics.h" | ||||||
|  | 
 | ||||||
|  | ConfigureGraphics::ConfigureGraphics(QWidget* parent) | ||||||
|  |     : QWidget(parent), ui(new Ui::ConfigureGraphics) { | ||||||
| 
 | 
 | ||||||
| ConfigureGraphics::ConfigureGraphics(QWidget *parent) : |  | ||||||
|     QWidget(parent), |  | ||||||
|     ui(new Ui::ConfigureGraphics) |  | ||||||
| { |  | ||||||
|     ui->setupUi(this); |     ui->setupUi(this); | ||||||
|     this->setConfiguration(); |     this->setConfiguration(); | ||||||
| 
 | 
 | ||||||
|     ui->toggle_vsync->setEnabled(!System::IsPoweredOn()); |     ui->toggle_vsync->setEnabled(!System::IsPoweredOn()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ConfigureGraphics::~ConfigureGraphics() { | ConfigureGraphics::~ConfigureGraphics() {} | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| void ConfigureGraphics::setConfiguration() { | void ConfigureGraphics::setConfiguration() { | ||||||
|     ui->toggle_hw_renderer->setChecked(Settings::values.use_hw_renderer); |     ui->toggle_hw_renderer->setChecked(Settings::values.use_hw_renderer); | ||||||
|  |  | ||||||
|  | @ -11,12 +11,11 @@ namespace Ui { | ||||||
| class ConfigureGraphics; | class ConfigureGraphics; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class ConfigureGraphics : public QWidget | class ConfigureGraphics : public QWidget { | ||||||
| { |  | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|     explicit ConfigureGraphics(QWidget *parent = nullptr); |     explicit ConfigureGraphics(QWidget* parent = nullptr); | ||||||
|     ~ConfigureGraphics(); |     ~ConfigureGraphics(); | ||||||
| 
 | 
 | ||||||
|     void applyConfiguration(); |     void applyConfiguration(); | ||||||
|  |  | ||||||
|  | @ -5,38 +5,39 @@ | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <utility> | #include <utility> | ||||||
| #include <QTimer> | #include <QTimer> | ||||||
| 
 |  | ||||||
| #include "citra_qt/configure_input.h" | #include "citra_qt/configure_input.h" | ||||||
| 
 | 
 | ||||||
| ConfigureInput::ConfigureInput(QWidget* parent) : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()) { | ConfigureInput::ConfigureInput(QWidget* parent) | ||||||
|  |     : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()) { | ||||||
|  | 
 | ||||||
|     ui->setupUi(this); |     ui->setupUi(this); | ||||||
| 
 | 
 | ||||||
|     // Initialize mapping of input enum to UI button.
 |     // Initialize mapping of input enum to UI button.
 | ||||||
|     input_mapping = { |     input_mapping = { | ||||||
|         { std::make_pair(Settings::NativeInput::Values::A, ui->buttonA) }, |         {Settings::NativeInput::Values::A, ui->buttonA}, | ||||||
|         { std::make_pair(Settings::NativeInput::Values::B, ui->buttonB) }, |         {Settings::NativeInput::Values::B, ui->buttonB}, | ||||||
|         { std::make_pair(Settings::NativeInput::Values::X, ui->buttonX) }, |         {Settings::NativeInput::Values::X, ui->buttonX}, | ||||||
|         { std::make_pair(Settings::NativeInput::Values::Y, ui->buttonY) }, |         {Settings::NativeInput::Values::Y, ui->buttonY}, | ||||||
|         { std::make_pair(Settings::NativeInput::Values::L, ui->buttonL) }, |         {Settings::NativeInput::Values::L, ui->buttonL}, | ||||||
|         { std::make_pair(Settings::NativeInput::Values::R, ui->buttonR) }, |         {Settings::NativeInput::Values::R, ui->buttonR}, | ||||||
|         { std::make_pair(Settings::NativeInput::Values::ZL, ui->buttonZL) }, |         {Settings::NativeInput::Values::ZL, ui->buttonZL}, | ||||||
|         { std::make_pair(Settings::NativeInput::Values::ZR, ui->buttonZR) }, |         {Settings::NativeInput::Values::ZR, ui->buttonZR}, | ||||||
|         { std::make_pair(Settings::NativeInput::Values::START, ui->buttonStart) }, |         {Settings::NativeInput::Values::START, ui->buttonStart}, | ||||||
|         { std::make_pair(Settings::NativeInput::Values::SELECT, ui->buttonSelect) }, |         {Settings::NativeInput::Values::SELECT, ui->buttonSelect}, | ||||||
|         { std::make_pair(Settings::NativeInput::Values::HOME, ui->buttonHome) }, |         {Settings::NativeInput::Values::HOME, ui->buttonHome}, | ||||||
|         { std::make_pair(Settings::NativeInput::Values::DUP, ui->buttonDpadUp) }, |         {Settings::NativeInput::Values::DUP, ui->buttonDpadUp}, | ||||||
|         { std::make_pair(Settings::NativeInput::Values::DDOWN, ui->buttonDpadDown) }, |         {Settings::NativeInput::Values::DDOWN, ui->buttonDpadDown}, | ||||||
|         { std::make_pair(Settings::NativeInput::Values::DLEFT, ui->buttonDpadLeft) }, |         {Settings::NativeInput::Values::DLEFT, ui->buttonDpadLeft}, | ||||||
|         { std::make_pair(Settings::NativeInput::Values::DRIGHT, ui->buttonDpadRight) }, |         {Settings::NativeInput::Values::DRIGHT, ui->buttonDpadRight}, | ||||||
|         { std::make_pair(Settings::NativeInput::Values::CUP, ui->buttonCStickUp) }, |         {Settings::NativeInput::Values::CUP, ui->buttonCStickUp}, | ||||||
|         { std::make_pair(Settings::NativeInput::Values::CDOWN, ui->buttonCStickDown) }, |         {Settings::NativeInput::Values::CDOWN, ui->buttonCStickDown}, | ||||||
|         { std::make_pair(Settings::NativeInput::Values::CLEFT, ui->buttonCStickLeft) }, |         {Settings::NativeInput::Values::CLEFT, ui->buttonCStickLeft}, | ||||||
|         { std::make_pair(Settings::NativeInput::Values::CRIGHT, ui->buttonCStickRight) }, |         {Settings::NativeInput::Values::CRIGHT, ui->buttonCStickRight}, | ||||||
|         { std::make_pair(Settings::NativeInput::Values::CIRCLE_UP, ui->buttonCircleUp) }, |         {Settings::NativeInput::Values::CIRCLE_UP, ui->buttonCircleUp}, | ||||||
|         { std::make_pair(Settings::NativeInput::Values::CIRCLE_DOWN, ui->buttonCircleDown) }, |         {Settings::NativeInput::Values::CIRCLE_DOWN, ui->buttonCircleDown}, | ||||||
|         { std::make_pair(Settings::NativeInput::Values::CIRCLE_LEFT, ui->buttonCircleLeft) }, |         {Settings::NativeInput::Values::CIRCLE_LEFT, ui->buttonCircleLeft}, | ||||||
|         { std::make_pair(Settings::NativeInput::Values::CIRCLE_RIGHT, ui->buttonCircleRight) }, |         {Settings::NativeInput::Values::CIRCLE_RIGHT, ui->buttonCircleRight}, | ||||||
|         { std::make_pair(Settings::NativeInput::Values::CIRCLE_MODIFIER, ui->buttonCircleMod) }, |         {Settings::NativeInput::Values::CIRCLE_MODIFIER, ui->buttonCircleMod}, | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     // Attach handle click method to each button click.
 |     // Attach handle click method to each button click.
 | ||||||
|  | @ -47,7 +48,10 @@ ConfigureInput::ConfigureInput(QWidget* parent) : QWidget(parent), ui(std::make_ | ||||||
|     setFocusPolicy(Qt::ClickFocus); |     setFocusPolicy(Qt::ClickFocus); | ||||||
|     timer = new QTimer(this); |     timer = new QTimer(this); | ||||||
|     timer->setSingleShot(true); |     timer->setSingleShot(true); | ||||||
|     connect(timer, &QTimer::timeout, this, [&]() { key_pressed = Qt::Key_Escape; setKey(); }); |     connect(timer, &QTimer::timeout, this, [&]() { | ||||||
|  |         key_pressed = Qt::Key_Escape; | ||||||
|  |         setKey(); | ||||||
|  |     }); | ||||||
|     this->setConfiguration(); |     this->setConfiguration(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -59,7 +63,7 @@ void ConfigureInput::handleClick() { | ||||||
|     grabKeyboard(); |     grabKeyboard(); | ||||||
|     grabMouse(); |     grabMouse(); | ||||||
|     changing_button = sender; |     changing_button = sender; | ||||||
|     timer->start(5000); //Cancel after 5 seconds
 |     timer->start(5000); // Cancel after 5 seconds
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ConfigureInput::applyConfiguration() { | void ConfigureInput::applyConfiguration() { | ||||||
|  |  | ||||||
|  | @ -4,9 +4,9 @@ | ||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <QWidget> | #include <memory> | ||||||
| #include <QKeyEvent> | #include <QKeyEvent> | ||||||
| 
 | #include <QWidget> | ||||||
| #include "citra_qt/config.h" | #include "citra_qt/config.h" | ||||||
| #include "core/settings.h" | #include "core/settings.h" | ||||||
| #include "ui_configure_input.h" | #include "ui_configure_input.h" | ||||||
|  | @ -16,7 +16,7 @@ class QString; | ||||||
| class QTimer; | class QTimer; | ||||||
| 
 | 
 | ||||||
| namespace Ui { | namespace Ui { | ||||||
|     class ConfigureInput; | class ConfigureInput; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class ConfigureInput : public QWidget { | class ConfigureInput : public QWidget { | ||||||
|  | @ -39,7 +39,8 @@ private: | ||||||
|     /// Load configuration settings into button text
 |     /// Load configuration settings into button text
 | ||||||
|     void setConfiguration(); |     void setConfiguration(); | ||||||
| 
 | 
 | ||||||
|     /// Check all inputs for duplicate keys. Clears out any other button with the same value as this button's new value.
 |     /// Check all inputs for duplicate keys. Clears out any other button with the same value as this
 | ||||||
|  |     /// button's new value.
 | ||||||
|     void removeDuplicates(const QString& newValue); |     void removeDuplicates(const QString& newValue); | ||||||
| 
 | 
 | ||||||
|     /// Handle key press event for input tab when a button is 'waiting'.
 |     /// Handle key press event for input tab when a button is 'waiting'.
 | ||||||
|  |  | ||||||
|  | @ -4,27 +4,24 @@ | ||||||
| 
 | 
 | ||||||
| #include "citra_qt/configure_system.h" | #include "citra_qt/configure_system.h" | ||||||
| #include "citra_qt/ui_settings.h" | #include "citra_qt/ui_settings.h" | ||||||
|  | #include "core/hle/service/cfg/cfg.h" | ||||||
|  | #include "core/hle/service/fs/archive.h" | ||||||
|  | #include "core/system.h" | ||||||
| #include "ui_configure_system.h" | #include "ui_configure_system.h" | ||||||
| 
 | 
 | ||||||
| #include "core/hle/service/fs/archive.h" |  | ||||||
| #include "core/hle/service/cfg/cfg.h" |  | ||||||
| #include "core/system.h" |  | ||||||
| 
 |  | ||||||
| static const std::array<int, 12> days_in_month = {{ | static const std::array<int, 12> days_in_month = {{ | ||||||
|     31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 |     31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, | ||||||
| }}; | }}; | ||||||
| 
 | 
 | ||||||
| ConfigureSystem::ConfigureSystem(QWidget *parent) : | ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureSystem) { | ||||||
|     QWidget(parent), |  | ||||||
|     ui(new Ui::ConfigureSystem) { |  | ||||||
|     ui->setupUi(this); |     ui->setupUi(this); | ||||||
|     connect(ui->combo_birthmonth, SIGNAL(currentIndexChanged(int)), SLOT(updateBirthdayComboBox(int))); |     connect(ui->combo_birthmonth, SIGNAL(currentIndexChanged(int)), | ||||||
|  |             SLOT(updateBirthdayComboBox(int))); | ||||||
| 
 | 
 | ||||||
|     this->setConfiguration(); |     this->setConfiguration(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ConfigureSystem::~ConfigureSystem() { | ConfigureSystem::~ConfigureSystem() {} | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| void ConfigureSystem::setConfiguration() { | void ConfigureSystem::setConfiguration() { | ||||||
|     enabled = !System::IsPoweredOn(); |     enabled = !System::IsPoweredOn(); | ||||||
|  | @ -54,13 +51,17 @@ void ConfigureSystem::setConfiguration() { | ||||||
| void ConfigureSystem::ReadSystemSettings() { | void ConfigureSystem::ReadSystemSettings() { | ||||||
|     // set username
 |     // set username
 | ||||||
|     username = Service::CFG::GetUsername(); |     username = Service::CFG::GetUsername(); | ||||||
|     // ui->edit_username->setText(QString::fromStdU16String(username)); // TODO(wwylele): Use this when we move to Qt 5.5
 |     // TODO(wwylele): Use this when we move to Qt 5.5
 | ||||||
|     ui->edit_username->setText(QString::fromUtf16(reinterpret_cast<const ushort*>(username.data()))); |     // ui->edit_username->setText(QString::fromStdU16String(username));
 | ||||||
|  |     ui->edit_username->setText( | ||||||
|  |         QString::fromUtf16(reinterpret_cast<const ushort*>(username.data()))); | ||||||
| 
 | 
 | ||||||
|     // set birthday
 |     // set birthday
 | ||||||
|     std::tie(birthmonth, birthday) = Service::CFG::GetBirthday(); |     std::tie(birthmonth, birthday) = Service::CFG::GetBirthday(); | ||||||
|     ui->combo_birthmonth->setCurrentIndex(birthmonth - 1); |     ui->combo_birthmonth->setCurrentIndex(birthmonth - 1); | ||||||
|     updateBirthdayComboBox(birthmonth - 1); // explicitly update it because the signal from setCurrentIndex is not reliable
 |     updateBirthdayComboBox( | ||||||
|  |         birthmonth - | ||||||
|  |         1); // explicitly update it because the signal from setCurrentIndex is not reliable
 | ||||||
|     ui->combo_birthday->setCurrentIndex(birthday - 1); |     ui->combo_birthday->setCurrentIndex(birthday - 1); | ||||||
| 
 | 
 | ||||||
|     // set system language
 |     // set system language
 | ||||||
|  | @ -79,8 +80,10 @@ void ConfigureSystem::applyConfiguration() { | ||||||
|     bool modified = false; |     bool modified = false; | ||||||
| 
 | 
 | ||||||
|     // apply username
 |     // apply username
 | ||||||
|     // std::u16string new_username = ui->edit_username->text().toStdU16String(); // TODO(wwylele): Use this when we move to Qt 5.5
 |     // TODO(wwylele): Use this when we move to Qt 5.5
 | ||||||
|     std::u16string new_username(reinterpret_cast<const char16_t*>(ui->edit_username->text().utf16())); |     // std::u16string new_username = ui->edit_username->text().toStdU16String();
 | ||||||
|  |     std::u16string new_username( | ||||||
|  |         reinterpret_cast<const char16_t*>(ui->edit_username->text().utf16())); | ||||||
|     if (new_username != username) { |     if (new_username != username) { | ||||||
|         Service::CFG::SetUsername(new_username); |         Service::CFG::SetUsername(new_username); | ||||||
|         modified = true; |         modified = true; | ||||||
|  |  | ||||||
|  | @ -11,12 +11,11 @@ namespace Ui { | ||||||
| class ConfigureSystem; | class ConfigureSystem; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class ConfigureSystem : public QWidget | class ConfigureSystem : public QWidget { | ||||||
| { |  | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|     explicit ConfigureSystem(QWidget *parent = nullptr); |     explicit ConfigureSystem(QWidget* parent = nullptr); | ||||||
|     ~ConfigureSystem(); |     ~ConfigureSystem(); | ||||||
| 
 | 
 | ||||||
|     void applyConfiguration(); |     void applyConfiguration(); | ||||||
|  |  | ||||||
|  | @ -3,19 +3,15 @@ | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| #include <QStandardItemModel> | #include <QStandardItemModel> | ||||||
| 
 |  | ||||||
| #include "citra_qt/debugger/callstack.h" | #include "citra_qt/debugger/callstack.h" | ||||||
| 
 |  | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "common/symbols.h" | #include "common/symbols.h" | ||||||
| 
 |  | ||||||
| #include "core/core.h" |  | ||||||
| #include "core/memory.h" |  | ||||||
| #include "core/arm/arm_interface.h" | #include "core/arm/arm_interface.h" | ||||||
| #include "core/arm/disassembler/arm_disasm.h" | #include "core/arm/disassembler/arm_disasm.h" | ||||||
|  | #include "core/core.h" | ||||||
|  | #include "core/memory.h" | ||||||
| 
 | 
 | ||||||
| CallstackWidget::CallstackWidget(QWidget* parent): QDockWidget(parent) | CallstackWidget::CallstackWidget(QWidget* parent) : QDockWidget(parent) { | ||||||
| { |  | ||||||
|     ui.setupUi(this); |     ui.setupUi(this); | ||||||
| 
 | 
 | ||||||
|     callstack_model = new QStandardItemModel(this); |     callstack_model = new QStandardItemModel(this); | ||||||
|  | @ -27,29 +23,26 @@ CallstackWidget::CallstackWidget(QWidget* parent): QDockWidget(parent) | ||||||
|     ui.treeView->setModel(callstack_model); |     ui.treeView->setModel(callstack_model); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void CallstackWidget::OnDebugModeEntered() | void CallstackWidget::OnDebugModeEntered() { | ||||||
| { |  | ||||||
|     // Stack pointer
 |     // Stack pointer
 | ||||||
|     const u32 sp = Core::g_app_core->GetReg(13); |     const u32 sp = Core::g_app_core->GetReg(13); | ||||||
| 
 | 
 | ||||||
|     Clear(); |     Clear(); | ||||||
| 
 | 
 | ||||||
|     int counter = 0; |     int counter = 0; | ||||||
|     for (u32 addr = 0x10000000; addr >= sp; addr -= 4) |     for (u32 addr = 0x10000000; addr >= sp; addr -= 4) { | ||||||
|     { |  | ||||||
|         if (!Memory::IsValidVirtualAddress(addr)) |         if (!Memory::IsValidVirtualAddress(addr)) | ||||||
|             break; |             break; | ||||||
| 
 | 
 | ||||||
|         const u32 ret_addr = Memory::Read32(addr); |         const u32 ret_addr = Memory::Read32(addr); | ||||||
|         const u32 call_addr = ret_addr - 4; //get call address???
 |         const u32 call_addr = ret_addr - 4; // get call address???
 | ||||||
| 
 | 
 | ||||||
|         if (!Memory::IsValidVirtualAddress(call_addr)) |         if (!Memory::IsValidVirtualAddress(call_addr)) | ||||||
|             break; |             break; | ||||||
| 
 | 
 | ||||||
|         /* TODO (mattvail) clean me, move to debugger interface */ |         /* TODO (mattvail) clean me, move to debugger interface */ | ||||||
|         u32 insn = Memory::Read32(call_addr); |         u32 insn = Memory::Read32(call_addr); | ||||||
|         if (ARM_Disasm::Decode(insn) == OP_BL) |         if (ARM_Disasm::Decode(insn) == OP_BL) { | ||||||
|         { |  | ||||||
|             std::string name; |             std::string name; | ||||||
|             // ripped from disasm
 |             // ripped from disasm
 | ||||||
|             u8 cond = (insn >> 28) & 0xf; |             u8 cond = (insn >> 28) & 0xf; | ||||||
|  | @ -63,26 +56,28 @@ void CallstackWidget::OnDebugModeEntered() | ||||||
|             i_offset += 8; |             i_offset += 8; | ||||||
|             const u32 func_addr = call_addr + i_offset; |             const u32 func_addr = call_addr + i_offset; | ||||||
| 
 | 
 | ||||||
|             callstack_model->setItem(counter, 0, new QStandardItem(QString("0x%1").arg(addr, 8, 16, QLatin1Char('0')))); |             callstack_model->setItem( | ||||||
|             callstack_model->setItem(counter, 1, new QStandardItem(QString("0x%1").arg(ret_addr, 8, 16, QLatin1Char('0')))); |                 counter, 0, new QStandardItem(QString("0x%1").arg(addr, 8, 16, QLatin1Char('0')))); | ||||||
|             callstack_model->setItem(counter, 2, new QStandardItem(QString("0x%1").arg(call_addr, 8, 16, QLatin1Char('0')))); |             callstack_model->setItem(counter, 1, new QStandardItem(QString("0x%1").arg( | ||||||
|  |                                                      ret_addr, 8, 16, QLatin1Char('0')))); | ||||||
|  |             callstack_model->setItem(counter, 2, new QStandardItem(QString("0x%1").arg( | ||||||
|  |                                                      call_addr, 8, 16, QLatin1Char('0')))); | ||||||
| 
 | 
 | ||||||
|             name = Symbols::HasSymbol(func_addr) ? Symbols::GetSymbol(func_addr).name : "unknown"; |             name = Symbols::HasSymbol(func_addr) ? Symbols::GetSymbol(func_addr).name : "unknown"; | ||||||
|             callstack_model->setItem(counter, 3, new QStandardItem(QString("%1_%2").arg(QString::fromStdString(name)) |             callstack_model->setItem( | ||||||
|                 .arg(QString("0x%1").arg(func_addr, 8, 16, QLatin1Char('0'))))); |                 counter, 3, new QStandardItem( | ||||||
|  |                                 QString("%1_%2") | ||||||
|  |                                     .arg(QString::fromStdString(name)) | ||||||
|  |                                     .arg(QString("0x%1").arg(func_addr, 8, 16, QLatin1Char('0'))))); | ||||||
| 
 | 
 | ||||||
|             counter++; |             counter++; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void CallstackWidget::OnDebugModeLeft() | void CallstackWidget::OnDebugModeLeft() {} | ||||||
| { |  | ||||||
| 
 | 
 | ||||||
| } | void CallstackWidget::Clear() { | ||||||
| 
 |  | ||||||
| void CallstackWidget::Clear() |  | ||||||
| { |  | ||||||
|     for (int row = 0; row < callstack_model->rowCount(); row++) { |     for (int row = 0; row < callstack_model->rowCount(); row++) { | ||||||
|         for (int column = 0; column < callstack_model->columnCount(); column++) { |         for (int column = 0; column < callstack_model->columnCount(); column++) { | ||||||
|             callstack_model->setItem(row, column, new QStandardItem()); |             callstack_model->setItem(row, column, new QStandardItem()); | ||||||
|  |  | ||||||
|  | @ -7,8 +7,7 @@ | ||||||
| 
 | 
 | ||||||
| class QStandardItemModel; | class QStandardItemModel; | ||||||
| 
 | 
 | ||||||
| class CallstackWidget : public QDockWidget | class CallstackWidget : public QDockWidget { | ||||||
| { |  | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|  |  | ||||||
|  | @ -3,23 +3,20 @@ | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| #include <QShortcut> | #include <QShortcut> | ||||||
| 
 |  | ||||||
| #include "citra_qt/bootmanager.h" | #include "citra_qt/bootmanager.h" | ||||||
| #include "citra_qt/hotkeys.h" |  | ||||||
| #include "citra_qt/debugger/disassembler.h" | #include "citra_qt/debugger/disassembler.h" | ||||||
|  | #include "citra_qt/hotkeys.h" | ||||||
| #include "citra_qt/util/util.h" | #include "citra_qt/util/util.h" | ||||||
| 
 |  | ||||||
| #include "common/break_points.h" | #include "common/break_points.h" | ||||||
| #include "common/symbols.h" | #include "common/symbols.h" | ||||||
| 
 |  | ||||||
| #include "core/core.h" |  | ||||||
| #include "core/memory.h" |  | ||||||
| #include "core/arm/arm_interface.h" | #include "core/arm/arm_interface.h" | ||||||
| #include "core/arm/disassembler/arm_disasm.h" | #include "core/arm/disassembler/arm_disasm.h" | ||||||
|  | #include "core/core.h" | ||||||
|  | #include "core/memory.h" | ||||||
| 
 | 
 | ||||||
| DisassemblerModel::DisassemblerModel(QObject* parent) : | DisassemblerModel::DisassemblerModel(QObject* parent) | ||||||
|     QAbstractListModel(parent), base_address(0), code_size(0), program_counter(0), selection(QModelIndex()) { |     : QAbstractListModel(parent), base_address(0), code_size(0), program_counter(0), | ||||||
| } |       selection(QModelIndex()) {} | ||||||
| 
 | 
 | ||||||
| int DisassemblerModel::columnCount(const QModelIndex& parent) const { | int DisassemblerModel::columnCount(const QModelIndex& parent) const { | ||||||
|     return 3; |     return 3; | ||||||
|  | @ -31,62 +28,60 @@ int DisassemblerModel::rowCount(const QModelIndex& parent) const { | ||||||
| 
 | 
 | ||||||
| QVariant DisassemblerModel::data(const QModelIndex& index, int role) const { | QVariant DisassemblerModel::data(const QModelIndex& index, int role) const { | ||||||
|     switch (role) { |     switch (role) { | ||||||
|         case Qt::DisplayRole: |     case Qt::DisplayRole: { | ||||||
|         { |         u32 address = base_address + index.row() * 4; | ||||||
|             u32 address = base_address + index.row() * 4; |         u32 instr = Memory::Read32(address); | ||||||
|             u32 instr = Memory::Read32(address); |         std::string disassembly = ARM_Disasm::Disassemble(address, instr); | ||||||
|             std::string disassembly = ARM_Disasm::Disassemble(address, instr); |  | ||||||
| 
 | 
 | ||||||
|             if (index.column() == 0) { |         if (index.column() == 0) { | ||||||
|                 return QString("0x%1").arg((uint)(address), 8, 16, QLatin1Char('0')); |             return QString("0x%1").arg((uint)(address), 8, 16, QLatin1Char('0')); | ||||||
|             } else if (index.column() == 1) { |         } else if (index.column() == 1) { | ||||||
|                 return QString::fromStdString(disassembly); |             return QString::fromStdString(disassembly); | ||||||
|             } else if (index.column() == 2) { |         } else if (index.column() == 2) { | ||||||
|                 if(Symbols::HasSymbol(address)) { |             if (Symbols::HasSymbol(address)) { | ||||||
|                     TSymbol symbol = Symbols::GetSymbol(address); |                 TSymbol symbol = Symbols::GetSymbol(address); | ||||||
|                     return QString("%1 - Size:%2").arg(QString::fromStdString(symbol.name)) |                 return QString("%1 - Size:%2") | ||||||
|                                                   .arg(symbol.size / 4); // divide by 4 to get instruction count
 |                     .arg(QString::fromStdString(symbol.name)) | ||||||
|                 } else if (ARM_Disasm::Decode(instr) == OP_BL) { |                     .arg(symbol.size / 4); // divide by 4 to get instruction count
 | ||||||
|                     u32 offset = instr & 0xFFFFFF; |             } else if (ARM_Disasm::Decode(instr) == OP_BL) { | ||||||
|  |                 u32 offset = instr & 0xFFFFFF; | ||||||
| 
 | 
 | ||||||
|                     // Sign-extend the 24-bit offset
 |                 // Sign-extend the 24-bit offset
 | ||||||
|                     if ((offset >> 23) & 1) |                 if ((offset >> 23) & 1) | ||||||
|                         offset |= 0xFF000000; |                     offset |= 0xFF000000; | ||||||
| 
 | 
 | ||||||
|                     // Pre-compute the left-shift and the prefetch offset
 |                 // Pre-compute the left-shift and the prefetch offset
 | ||||||
|                     offset <<= 2; |                 offset <<= 2; | ||||||
|                     offset += 8; |                 offset += 8; | ||||||
| 
 | 
 | ||||||
|                     TSymbol symbol = Symbols::GetSymbol(address + offset); |                 TSymbol symbol = Symbols::GetSymbol(address + offset); | ||||||
|                     return QString("    --> %1").arg(QString::fromStdString(symbol.name)); |                 return QString("    --> %1").arg(QString::fromStdString(symbol.name)); | ||||||
|                 } |  | ||||||
|             } |             } | ||||||
| 
 |  | ||||||
|             break; |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         case Qt::BackgroundRole: |         break; | ||||||
|         { |     } | ||||||
|             unsigned int address = base_address + 4 * index.row(); |  | ||||||
| 
 | 
 | ||||||
|             if (breakpoints.IsAddressBreakPoint(address)) |     case Qt::BackgroundRole: { | ||||||
|                 return QBrush(QColor(0xFF, 0xC0, 0xC0)); |         unsigned int address = base_address + 4 * index.row(); | ||||||
|             else if (address == program_counter) |  | ||||||
|                 return QBrush(QColor(0xC0, 0xC0, 0xFF)); |  | ||||||
| 
 | 
 | ||||||
|             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: |     default: | ||||||
|         { |         break; | ||||||
|             if (index.column() == 0 || index.column() == 1) { // 2 is the symbols column
 |  | ||||||
|                 return GetMonospaceFont(); |  | ||||||
|             } |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         default: |  | ||||||
|             break; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return QVariant(); |     return QVariant(); | ||||||
|  | @ -103,7 +98,7 @@ const BreakPoints& DisassemblerModel::GetBreakPoints() const { | ||||||
| void DisassemblerModel::ParseFromAddress(unsigned int address) { | void DisassemblerModel::ParseFromAddress(unsigned int address) { | ||||||
| 
 | 
 | ||||||
|     // NOTE: A too large value causes lagging when scrolling the disassembly
 |     // NOTE: A too large value causes lagging when scrolling the disassembly
 | ||||||
|     const unsigned int chunk_size = 1000*500; |     const unsigned int chunk_size = 1000 * 500; | ||||||
| 
 | 
 | ||||||
|     // If we haven't loaded anything yet, initialize base address to the parameter address
 |     // If we haven't loaded anything yet, initialize base address to the parameter address
 | ||||||
|     if (code_size == 0) |     if (code_size == 0) | ||||||
|  | @ -165,23 +160,26 @@ void DisassemblerModel::SetNextInstruction(unsigned int address) { | ||||||
|     emit dataChanged(prev_index, prev_index); |     emit dataChanged(prev_index, prev_index); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| DisassemblerWidget::DisassemblerWidget(QWidget* parent, EmuThread* emu_thread) : | DisassemblerWidget::DisassemblerWidget(QWidget* parent, EmuThread* emu_thread) | ||||||
|     QDockWidget(parent), base_addr(0), emu_thread(emu_thread) { |     : QDockWidget(parent), base_addr(0), emu_thread(emu_thread) { | ||||||
| 
 | 
 | ||||||
|     disasm_ui.setupUi(this); |     disasm_ui.setupUi(this); | ||||||
| 
 | 
 | ||||||
|     RegisterHotkey("Disassembler", "Start/Stop", QKeySequence(Qt::Key_F5), Qt::ApplicationShortcut); |     RegisterHotkey("Disassembler", "Start/Stop", QKeySequence(Qt::Key_F5), Qt::ApplicationShortcut); | ||||||
|     RegisterHotkey("Disassembler", "Step", QKeySequence(Qt::Key_F10), Qt::ApplicationShortcut); |     RegisterHotkey("Disassembler", "Step", QKeySequence(Qt::Key_F10), Qt::ApplicationShortcut); | ||||||
|     RegisterHotkey("Disassembler", "Step into", QKeySequence(Qt::Key_F11), Qt::ApplicationShortcut); |     RegisterHotkey("Disassembler", "Step into", QKeySequence(Qt::Key_F11), Qt::ApplicationShortcut); | ||||||
|     RegisterHotkey("Disassembler", "Set Breakpoint", QKeySequence(Qt::Key_F9), Qt::ApplicationShortcut); |     RegisterHotkey("Disassembler", "Set Breakpoint", QKeySequence(Qt::Key_F9), | ||||||
|  |                    Qt::ApplicationShortcut); | ||||||
| 
 | 
 | ||||||
|     connect(disasm_ui.button_step, SIGNAL(clicked()), this, SLOT(OnStep())); |     connect(disasm_ui.button_step, SIGNAL(clicked()), this, SLOT(OnStep())); | ||||||
|     connect(disasm_ui.button_pause, SIGNAL(clicked()), this, SLOT(OnPause())); |     connect(disasm_ui.button_pause, SIGNAL(clicked()), this, SLOT(OnPause())); | ||||||
|     connect(disasm_ui.button_continue, SIGNAL(clicked()), this, SLOT(OnContinue())); |     connect(disasm_ui.button_continue, SIGNAL(clicked()), this, SLOT(OnContinue())); | ||||||
| 
 | 
 | ||||||
|     connect(GetHotkey("Disassembler", "Start/Stop", this), SIGNAL(activated()), this, SLOT(OnToggleStartStop())); |     connect(GetHotkey("Disassembler", "Start/Stop", this), SIGNAL(activated()), this, | ||||||
|  |             SLOT(OnToggleStartStop())); | ||||||
|     connect(GetHotkey("Disassembler", "Step", this), SIGNAL(activated()), this, SLOT(OnStep())); |     connect(GetHotkey("Disassembler", "Step", this), SIGNAL(activated()), this, SLOT(OnStep())); | ||||||
|     connect(GetHotkey("Disassembler", "Step into", this), SIGNAL(activated()), this, SLOT(OnStepInto())); |     connect(GetHotkey("Disassembler", "Step into", this), SIGNAL(activated()), this, | ||||||
|  |             SLOT(OnStepInto())); | ||||||
| 
 | 
 | ||||||
|     setEnabled(false); |     setEnabled(false); | ||||||
| } | } | ||||||
|  | @ -195,7 +193,8 @@ void DisassemblerWidget::Init() { | ||||||
| 
 | 
 | ||||||
|     QModelIndex model_index = model->IndexFromAbsoluteAddress(Core::g_app_core->GetPC()); |     QModelIndex model_index = model->IndexFromAbsoluteAddress(Core::g_app_core->GetPC()); | ||||||
|     disasm_ui.treeView->scrollTo(model_index); |     disasm_ui.treeView->scrollTo(model_index); | ||||||
|     disasm_ui.treeView->selectionModel()->setCurrentIndex(model_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); |     disasm_ui.treeView->selectionModel()->setCurrentIndex( | ||||||
|  |         model_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void DisassemblerWidget::OnContinue() { | void DisassemblerWidget::OnContinue() { | ||||||
|  | @ -234,11 +233,11 @@ void DisassemblerWidget::OnDebugModeEntered() { | ||||||
| 
 | 
 | ||||||
|     QModelIndex model_index = model->IndexFromAbsoluteAddress(next_instr); |     QModelIndex model_index = model->IndexFromAbsoluteAddress(next_instr); | ||||||
|     disasm_ui.treeView->scrollTo(model_index); |     disasm_ui.treeView->scrollTo(model_index); | ||||||
|     disasm_ui.treeView->selectionModel()->setCurrentIndex(model_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); |     disasm_ui.treeView->selectionModel()->setCurrentIndex( | ||||||
|  |         model_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void DisassemblerWidget::OnDebugModeLeft() { | void DisassemblerWidget::OnDebugModeLeft() {} | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| int DisassemblerWidget::SelectedRow() { | int DisassemblerWidget::SelectedRow() { | ||||||
|     QModelIndex index = disasm_ui.treeView->selectionModel()->currentIndex(); |     QModelIndex index = disasm_ui.treeView->selectionModel()->currentIndex(); | ||||||
|  | @ -254,10 +253,12 @@ void DisassemblerWidget::OnEmulationStarting(EmuThread* emu_thread) { | ||||||
|     model = new DisassemblerModel(this); |     model = new DisassemblerModel(this); | ||||||
|     disasm_ui.treeView->setModel(model); |     disasm_ui.treeView->setModel(model); | ||||||
| 
 | 
 | ||||||
|     connect(disasm_ui.treeView->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), |     connect(disasm_ui.treeView->selectionModel(), | ||||||
|         model, SLOT(OnSelectionChanged(const QModelIndex&))); |             SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), model, | ||||||
|  |             SLOT(OnSelectionChanged(const QModelIndex&))); | ||||||
|     connect(disasm_ui.button_breakpoint, SIGNAL(clicked()), model, SLOT(OnSetOrUnsetBreakpoint())); |     connect(disasm_ui.button_breakpoint, SIGNAL(clicked()), model, SLOT(OnSetOrUnsetBreakpoint())); | ||||||
|     connect(GetHotkey("Disassembler", "Set Breakpoint", this), SIGNAL(activated()), model, SLOT(OnSetOrUnsetBreakpoint())); |     connect(GetHotkey("Disassembler", "Set Breakpoint", this), SIGNAL(activated()), model, | ||||||
|  |             SLOT(OnSetOrUnsetBreakpoint())); | ||||||
| 
 | 
 | ||||||
|     Init(); |     Init(); | ||||||
|     setEnabled(true); |     setEnabled(true); | ||||||
|  |  | ||||||
|  | @ -6,17 +6,14 @@ | ||||||
| 
 | 
 | ||||||
| #include <QAbstractListModel> | #include <QAbstractListModel> | ||||||
| #include <QDockWidget> | #include <QDockWidget> | ||||||
| 
 |  | ||||||
| #include "ui_disassembler.h" |  | ||||||
| 
 |  | ||||||
| #include "common/break_points.h" | #include "common/break_points.h" | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
|  | #include "ui_disassembler.h" | ||||||
| 
 | 
 | ||||||
| class QAction; | class QAction; | ||||||
| class EmuThread; | class EmuThread; | ||||||
| 
 | 
 | ||||||
| class DisassemblerModel : public QAbstractListModel | class DisassemblerModel : public QAbstractListModel { | ||||||
| { |  | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|  | @ -46,8 +43,7 @@ private: | ||||||
|     mutable BreakPoints breakpoints; |     mutable BreakPoints breakpoints; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class DisassemblerWidget : public QDockWidget | class DisassemblerWidget : public QDockWidget { | ||||||
| { |  | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|  |  | ||||||
|  | @ -3,75 +3,67 @@ | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| #include <QListView> | #include <QListView> | ||||||
| 
 |  | ||||||
| #include "citra_qt/debugger/graphics.h" | #include "citra_qt/debugger/graphics.h" | ||||||
| #include "citra_qt/util/util.h" | #include "citra_qt/util/util.h" | ||||||
| 
 | 
 | ||||||
| extern GraphicsDebugger g_debugger; | extern GraphicsDebugger g_debugger; | ||||||
| 
 | 
 | ||||||
| GPUCommandStreamItemModel::GPUCommandStreamItemModel(QObject* parent) : QAbstractListModel(parent), command_count(0) | GPUCommandStreamItemModel::GPUCommandStreamItemModel(QObject* parent) | ||||||
| { |     : QAbstractListModel(parent), command_count(0) { | ||||||
|     connect(this, SIGNAL(GXCommandFinished(int)), this, SLOT(OnGXCommandFinishedInternal(int))); |     connect(this, SIGNAL(GXCommandFinished(int)), this, SLOT(OnGXCommandFinishedInternal(int))); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int GPUCommandStreamItemModel::rowCount(const QModelIndex& parent) const | int GPUCommandStreamItemModel::rowCount(const QModelIndex& parent) const { | ||||||
| { |  | ||||||
|     return command_count; |     return command_count; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| QVariant GPUCommandStreamItemModel::data(const QModelIndex& index, int role) const | QVariant GPUCommandStreamItemModel::data(const QModelIndex& index, int role) const { | ||||||
| { |  | ||||||
|     if (!index.isValid()) |     if (!index.isValid()) | ||||||
|         return QVariant(); |         return QVariant(); | ||||||
| 
 | 
 | ||||||
|     int command_index = index.row(); |     int command_index = index.row(); | ||||||
|     const GSP_GPU::Command& command = GetDebugger()->ReadGXCommandHistory(command_index); |     const GSP_GPU::Command& command = GetDebugger()->ReadGXCommandHistory(command_index); | ||||||
|     if (role == Qt::DisplayRole) |     if (role == Qt::DisplayRole) { | ||||||
|     { |  | ||||||
|         std::map<GSP_GPU::CommandId, const char*> command_names = { |         std::map<GSP_GPU::CommandId, const char*> command_names = { | ||||||
|             { GSP_GPU::CommandId::REQUEST_DMA, "REQUEST_DMA" }, |             {GSP_GPU::CommandId::REQUEST_DMA, "REQUEST_DMA"}, | ||||||
|             { GSP_GPU::CommandId::SUBMIT_GPU_CMDLIST, "SUBMIT_GPU_CMDLIST" }, |             {GSP_GPU::CommandId::SUBMIT_GPU_CMDLIST, "SUBMIT_GPU_CMDLIST"}, | ||||||
|             { GSP_GPU::CommandId::SET_MEMORY_FILL, "SET_MEMORY_FILL" }, |             {GSP_GPU::CommandId::SET_MEMORY_FILL, "SET_MEMORY_FILL"}, | ||||||
|             { GSP_GPU::CommandId::SET_DISPLAY_TRANSFER, "SET_DISPLAY_TRANSFER" }, |             {GSP_GPU::CommandId::SET_DISPLAY_TRANSFER, "SET_DISPLAY_TRANSFER"}, | ||||||
|             { GSP_GPU::CommandId::SET_TEXTURE_COPY, "SET_TEXTURE_COPY" }, |             {GSP_GPU::CommandId::SET_TEXTURE_COPY, "SET_TEXTURE_COPY"}, | ||||||
|             { GSP_GPU::CommandId::CACHE_FLUSH, "CACHE_FLUSH" }, |             {GSP_GPU::CommandId::CACHE_FLUSH, "CACHE_FLUSH"}, | ||||||
|         }; |         }; | ||||||
|         const u32* command_data = reinterpret_cast<const u32*>(&command); |         const u32* command_data = reinterpret_cast<const u32*>(&command); | ||||||
|         QString str = QString("%1 %2 %3 %4 %5 %6 %7 %8 %9").arg(command_names[command.id]) |         QString str = QString("%1 %2 %3 %4 %5 %6 %7 %8 %9") | ||||||
|                         .arg(command_data[0], 8, 16, QLatin1Char('0')) |                           .arg(command_names[command.id]) | ||||||
|                         .arg(command_data[1], 8, 16, QLatin1Char('0')) |                           .arg(command_data[0], 8, 16, QLatin1Char('0')) | ||||||
|                         .arg(command_data[2], 8, 16, QLatin1Char('0')) |                           .arg(command_data[1], 8, 16, QLatin1Char('0')) | ||||||
|                         .arg(command_data[3], 8, 16, QLatin1Char('0')) |                           .arg(command_data[2], 8, 16, QLatin1Char('0')) | ||||||
|                         .arg(command_data[4], 8, 16, QLatin1Char('0')) |                           .arg(command_data[3], 8, 16, QLatin1Char('0')) | ||||||
|                         .arg(command_data[5], 8, 16, QLatin1Char('0')) |                           .arg(command_data[4], 8, 16, QLatin1Char('0')) | ||||||
|                         .arg(command_data[6], 8, 16, QLatin1Char('0')) |                           .arg(command_data[5], 8, 16, QLatin1Char('0')) | ||||||
|                         .arg(command_data[7], 8, 16, QLatin1Char('0')); |                           .arg(command_data[6], 8, 16, QLatin1Char('0')) | ||||||
|  |                           .arg(command_data[7], 8, 16, QLatin1Char('0')); | ||||||
|         return QVariant(str); |         return QVariant(str); | ||||||
|     } |     } else { | ||||||
|     else |  | ||||||
|     { |  | ||||||
|         return QVariant(); |         return QVariant(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GPUCommandStreamItemModel::GXCommandProcessed(int total_command_count) | void GPUCommandStreamItemModel::GXCommandProcessed(int total_command_count) { | ||||||
| { |  | ||||||
|     emit GXCommandFinished(total_command_count); |     emit GXCommandFinished(total_command_count); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GPUCommandStreamItemModel::OnGXCommandFinishedInternal(int total_command_count) | void GPUCommandStreamItemModel::OnGXCommandFinishedInternal(int total_command_count) { | ||||||
| { |  | ||||||
|     if (total_command_count == 0) |     if (total_command_count == 0) | ||||||
|         return; |         return; | ||||||
| 
 | 
 | ||||||
|     int prev_command_count = command_count; |     int prev_command_count = command_count; | ||||||
|     command_count = total_command_count; |     command_count = total_command_count; | ||||||
|     emit dataChanged(index(prev_command_count,0), index(total_command_count-1,0)); |     emit dataChanged(index(prev_command_count, 0), index(total_command_count - 1, 0)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | GPUCommandStreamWidget::GPUCommandStreamWidget(QWidget* parent) | ||||||
| GPUCommandStreamWidget::GPUCommandStreamWidget(QWidget* parent) : QDockWidget(tr("Graphics Debugger"), parent) |     : QDockWidget(tr("Graphics Debugger"), parent) { | ||||||
| { |  | ||||||
|     setObjectName("GraphicsDebugger"); |     setObjectName("GraphicsDebugger"); | ||||||
| 
 | 
 | ||||||
|     GPUCommandStreamItemModel* command_model = new GPUCommandStreamItemModel(this); |     GPUCommandStreamItemModel* command_model = new GPUCommandStreamItemModel(this); | ||||||
|  |  | ||||||
|  | @ -6,11 +6,10 @@ | ||||||
| 
 | 
 | ||||||
| #include <QAbstractListModel> | #include <QAbstractListModel> | ||||||
| #include <QDockWidget> | #include <QDockWidget> | ||||||
| 
 |  | ||||||
| #include "video_core/gpu_debugger.h" | #include "video_core/gpu_debugger.h" | ||||||
| 
 | 
 | ||||||
| class GPUCommandStreamItemModel : public QAbstractListModel, public GraphicsDebugger::DebuggerObserver | class GPUCommandStreamItemModel : public QAbstractListModel, | ||||||
| { |                                   public GraphicsDebugger::DebuggerObserver { | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|  | @ -32,8 +31,7 @@ private: | ||||||
|     int command_count; |     int command_count; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class GPUCommandStreamWidget : public QDockWidget | class GPUCommandStreamWidget : public QDockWidget { | ||||||
| { |  | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|  |  | ||||||
|  | @ -3,30 +3,25 @@ | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| #include <QMetaType> | #include <QMetaType> | ||||||
| 
 |  | ||||||
| #include "citra_qt/debugger/graphics_breakpoint_observer.h" | #include "citra_qt/debugger/graphics_breakpoint_observer.h" | ||||||
| 
 | 
 | ||||||
| BreakPointObserverDock::BreakPointObserverDock(std::shared_ptr<Pica::DebugContext> debug_context, | BreakPointObserverDock::BreakPointObserverDock(std::shared_ptr<Pica::DebugContext> debug_context, | ||||||
|                                                const QString& title, QWidget* parent) |                                                const QString& title, QWidget* parent) | ||||||
|     : QDockWidget(title, parent), BreakPointObserver(debug_context) |     : QDockWidget(title, parent), BreakPointObserver(debug_context) { | ||||||
| { |  | ||||||
|     qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event"); |     qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event"); | ||||||
| 
 | 
 | ||||||
|     connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed())); |     connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed())); | ||||||
| 
 | 
 | ||||||
|     // NOTE: This signal is emitted from a non-GUI thread, but connect() takes
 |     // NOTE: This signal is emitted from a non-GUI thread, but connect() takes
 | ||||||
|     //       care of delaying its handling to the GUI thread.
 |     //       care of delaying its handling to the GUI thread.
 | ||||||
|     connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)), |     connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event, void*)), this, | ||||||
|             this, SLOT(OnBreakPointHit(Pica::DebugContext::Event,void*)), |             SLOT(OnBreakPointHit(Pica::DebugContext::Event, void*)), Qt::BlockingQueuedConnection); | ||||||
|             Qt::BlockingQueuedConnection); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void BreakPointObserverDock::OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) | void BreakPointObserverDock::OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) { | ||||||
| { |  | ||||||
|     emit BreakPointHit(event, data); |     emit BreakPointHit(event, data); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void BreakPointObserverDock::OnPicaResume() | void BreakPointObserverDock::OnPicaResume() { | ||||||
| { |  | ||||||
|     emit Resumed(); |     emit Resumed(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -5,7 +5,6 @@ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <QDockWidget> | #include <QDockWidget> | ||||||
| 
 |  | ||||||
| #include "video_core/debug_utils/debug_utils.h" | #include "video_core/debug_utils/debug_utils.h" | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  | @ -13,7 +12,8 @@ | ||||||
|  * This is because the Pica breakpoint callbacks are called from a non-GUI thread, while |  * This is because the Pica breakpoint callbacks are called from a non-GUI thread, while | ||||||
|  * the widget usually wants to perform reactions in the GUI thread. |  * the widget usually wants to perform reactions in the GUI thread. | ||||||
|  */ |  */ | ||||||
| class BreakPointObserverDock : public QDockWidget, protected Pica::DebugContext::BreakPointObserver { | class BreakPointObserverDock : public QDockWidget, | ||||||
|  |                                protected Pica::DebugContext::BreakPointObserver { | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|  |  | ||||||
|  | @ -7,47 +7,39 @@ | ||||||
| #include <QPushButton> | #include <QPushButton> | ||||||
| #include <QTreeView> | #include <QTreeView> | ||||||
| #include <QVBoxLayout> | #include <QVBoxLayout> | ||||||
| 
 |  | ||||||
| #include "citra_qt/debugger/graphics_breakpoints.h" | #include "citra_qt/debugger/graphics_breakpoints.h" | ||||||
| #include "citra_qt/debugger/graphics_breakpoints_p.h" | #include "citra_qt/debugger/graphics_breakpoints_p.h" | ||||||
| 
 |  | ||||||
| #include "common/assert.h" | #include "common/assert.h" | ||||||
| 
 | 
 | ||||||
| BreakPointModel::BreakPointModel(std::shared_ptr<Pica::DebugContext> debug_context, QObject* parent) | BreakPointModel::BreakPointModel(std::shared_ptr<Pica::DebugContext> debug_context, QObject* parent) | ||||||
|     : QAbstractListModel(parent), context_weak(debug_context), |     : QAbstractListModel(parent), context_weak(debug_context), | ||||||
|       at_breakpoint(debug_context->at_breakpoint), |       at_breakpoint(debug_context->at_breakpoint), | ||||||
|       active_breakpoint(debug_context->active_breakpoint) |       active_breakpoint(debug_context->active_breakpoint) {} | ||||||
| { |  | ||||||
| 
 | 
 | ||||||
| } | int BreakPointModel::columnCount(const QModelIndex& parent) const { | ||||||
| 
 |  | ||||||
| int BreakPointModel::columnCount(const QModelIndex& parent) const |  | ||||||
| { |  | ||||||
|     return 1; |     return 1; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int BreakPointModel::rowCount(const QModelIndex& parent) const | int BreakPointModel::rowCount(const QModelIndex& parent) const { | ||||||
| { |  | ||||||
|     return static_cast<int>(Pica::DebugContext::Event::NumEvents); |     return static_cast<int>(Pica::DebugContext::Event::NumEvents); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| QVariant BreakPointModel::data(const QModelIndex& index, int role) const | QVariant BreakPointModel::data(const QModelIndex& index, int role) const { | ||||||
| { |  | ||||||
|     const auto event = static_cast<Pica::DebugContext::Event>(index.row()); |     const auto event = static_cast<Pica::DebugContext::Event>(index.row()); | ||||||
| 
 | 
 | ||||||
|     switch (role) { |     switch (role) { | ||||||
|     case Qt::DisplayRole: |     case Qt::DisplayRole: { | ||||||
|     { |  | ||||||
|         if (index.column() == 0) { |         if (index.column() == 0) { | ||||||
|             static const std::map<Pica::DebugContext::Event, QString> map = { |             static const std::map<Pica::DebugContext::Event, QString> map = { | ||||||
|                 { Pica::DebugContext::Event::PicaCommandLoaded, tr("Pica command loaded") }, |                 {Pica::DebugContext::Event::PicaCommandLoaded, tr("Pica command loaded")}, | ||||||
|                 { Pica::DebugContext::Event::PicaCommandProcessed, tr("Pica command processed") }, |                 {Pica::DebugContext::Event::PicaCommandProcessed, tr("Pica command processed")}, | ||||||
|                 { Pica::DebugContext::Event::IncomingPrimitiveBatch, tr("Incoming primitive batch") }, |                 {Pica::DebugContext::Event::IncomingPrimitiveBatch, tr("Incoming primitive batch")}, | ||||||
|                 { Pica::DebugContext::Event::FinishedPrimitiveBatch, tr("Finished primitive batch") }, |                 {Pica::DebugContext::Event::FinishedPrimitiveBatch, tr("Finished primitive batch")}, | ||||||
|                 { Pica::DebugContext::Event::VertexShaderInvocation, tr("Vertex shader invocation") }, |                 {Pica::DebugContext::Event::VertexShaderInvocation, tr("Vertex shader invocation")}, | ||||||
|                 { Pica::DebugContext::Event::IncomingDisplayTransfer, tr("Incoming display transfer") }, |                 {Pica::DebugContext::Event::IncomingDisplayTransfer, | ||||||
|                 { Pica::DebugContext::Event::GSPCommandProcessed, tr("GSP command processed") }, |                  tr("Incoming display transfer")}, | ||||||
|                 { Pica::DebugContext::Event::BufferSwapped, tr("Buffers swapped") } |                 {Pica::DebugContext::Event::GSPCommandProcessed, tr("GSP command processed")}, | ||||||
|  |                 {Pica::DebugContext::Event::BufferSwapped, tr("Buffers swapped")}, | ||||||
|             }; |             }; | ||||||
| 
 | 
 | ||||||
|             DEBUG_ASSERT(map.size() == static_cast<size_t>(Pica::DebugContext::Event::NumEvents)); |             DEBUG_ASSERT(map.size() == static_cast<size_t>(Pica::DebugContext::Event::NumEvents)); | ||||||
|  | @ -57,23 +49,20 @@ QVariant BreakPointModel::data(const QModelIndex& index, int role) const | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     case Qt::CheckStateRole: |     case Qt::CheckStateRole: { | ||||||
|     { |  | ||||||
|         if (index.column() == 0) |         if (index.column() == 0) | ||||||
|             return data(index, Role_IsEnabled).toBool() ? Qt::Checked : Qt::Unchecked; |             return data(index, Role_IsEnabled).toBool() ? Qt::Checked : Qt::Unchecked; | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     case Qt::BackgroundRole: |     case Qt::BackgroundRole: { | ||||||
|     { |  | ||||||
|         if (at_breakpoint && index.row() == static_cast<int>(active_breakpoint)) { |         if (at_breakpoint && index.row() == static_cast<int>(active_breakpoint)) { | ||||||
|             return QBrush(QColor(0xE0, 0xE0, 0x10)); |             return QBrush(QColor(0xE0, 0xE0, 0x10)); | ||||||
|         } |         } | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     case Role_IsEnabled: |     case Role_IsEnabled: { | ||||||
|     { |  | ||||||
|         auto context = context_weak.lock(); |         auto context = context_weak.lock(); | ||||||
|         return context && context->breakpoints[(int)event].enabled; |         return context && context->breakpoints[(int)event].enabled; | ||||||
|     } |     } | ||||||
|  | @ -84,8 +73,7 @@ QVariant BreakPointModel::data(const QModelIndex& index, int role) const | ||||||
|     return QVariant(); |     return QVariant(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Qt::ItemFlags BreakPointModel::flags(const QModelIndex &index) const | Qt::ItemFlags BreakPointModel::flags(const QModelIndex& index) const { | ||||||
| { |  | ||||||
|     if (!index.isValid()) |     if (!index.isValid()) | ||||||
|         return 0; |         return 0; | ||||||
| 
 | 
 | ||||||
|  | @ -95,14 +83,11 @@ Qt::ItemFlags BreakPointModel::flags(const QModelIndex &index) const | ||||||
|     return flags; |     return flags; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | bool BreakPointModel::setData(const QModelIndex& index, const QVariant& value, int role) { | ||||||
| bool BreakPointModel::setData(const QModelIndex& index, const QVariant& value, int role) |  | ||||||
| { |  | ||||||
|     const auto event = static_cast<Pica::DebugContext::Event>(index.row()); |     const auto event = static_cast<Pica::DebugContext::Event>(index.row()); | ||||||
| 
 | 
 | ||||||
|     switch (role) { |     switch (role) { | ||||||
|     case Qt::CheckStateRole: |     case Qt::CheckStateRole: { | ||||||
|     { |  | ||||||
|         if (index.column() != 0) |         if (index.column() != 0) | ||||||
|             return false; |             return false; | ||||||
| 
 | 
 | ||||||
|  | @ -120,9 +105,7 @@ bool BreakPointModel::setData(const QModelIndex& index, const QVariant& value, i | ||||||
|     return false; |     return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | void BreakPointModel::OnBreakPointHit(Pica::DebugContext::Event event) { | ||||||
| void BreakPointModel::OnBreakPointHit(Pica::DebugContext::Event event) |  | ||||||
| { |  | ||||||
|     auto context = context_weak.lock(); |     auto context = context_weak.lock(); | ||||||
|     if (!context) |     if (!context) | ||||||
|         return; |         return; | ||||||
|  | @ -133,8 +116,7 @@ void BreakPointModel::OnBreakPointHit(Pica::DebugContext::Event event) | ||||||
|                      createIndex(static_cast<int>(event), 0)); |                      createIndex(static_cast<int>(event), 0)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void BreakPointModel::OnResumed() | void BreakPointModel::OnResumed() { | ||||||
| { |  | ||||||
|     auto context = context_weak.lock(); |     auto context = context_weak.lock(); | ||||||
|     if (!context) |     if (!context) | ||||||
|         return; |         return; | ||||||
|  | @ -145,12 +127,10 @@ void BreakPointModel::OnResumed() | ||||||
|     active_breakpoint = context->active_breakpoint; |     active_breakpoint = context->active_breakpoint; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | GraphicsBreakPointsWidget::GraphicsBreakPointsWidget( | ||||||
| GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(std::shared_ptr<Pica::DebugContext> debug_context, |     std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent) | ||||||
|                                                      QWidget* parent) |  | ||||||
|     : QDockWidget(tr("Pica Breakpoints"), parent), |     : QDockWidget(tr("Pica Breakpoints"), parent), | ||||||
|       Pica::DebugContext::BreakPointObserver(debug_context) |       Pica::DebugContext::BreakPointObserver(debug_context) { | ||||||
| { |  | ||||||
|     setObjectName("PicaBreakPointsWidget"); |     setObjectName("PicaBreakPointsWidget"); | ||||||
| 
 | 
 | ||||||
|     status_text = new QLabel(tr("Emulation running")); |     status_text = new QLabel(tr("Emulation running")); | ||||||
|  | @ -165,23 +145,21 @@ GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(std::shared_ptr<Pica::Debug | ||||||
| 
 | 
 | ||||||
|     qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event"); |     qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event"); | ||||||
| 
 | 
 | ||||||
|     connect(breakpoint_list, SIGNAL(doubleClicked(const QModelIndex&)), |     connect(breakpoint_list, SIGNAL(doubleClicked(const QModelIndex&)), this, | ||||||
|             this, SLOT(OnItemDoubleClicked(const QModelIndex&))); |             SLOT(OnItemDoubleClicked(const QModelIndex&))); | ||||||
| 
 | 
 | ||||||
|     connect(resume_button, SIGNAL(clicked()), this, SLOT(OnResumeRequested())); |     connect(resume_button, SIGNAL(clicked()), this, SLOT(OnResumeRequested())); | ||||||
| 
 | 
 | ||||||
|     connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)), |     connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event, void*)), this, | ||||||
|             this, SLOT(OnBreakPointHit(Pica::DebugContext::Event,void*)), |             SLOT(OnBreakPointHit(Pica::DebugContext::Event, void*)), Qt::BlockingQueuedConnection); | ||||||
|             Qt::BlockingQueuedConnection); |  | ||||||
|     connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed())); |     connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed())); | ||||||
| 
 | 
 | ||||||
|     connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)), |     connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event, void*)), breakpoint_model, | ||||||
|             breakpoint_model, SLOT(OnBreakPointHit(Pica::DebugContext::Event)), |             SLOT(OnBreakPointHit(Pica::DebugContext::Event)), Qt::BlockingQueuedConnection); | ||||||
|             Qt::BlockingQueuedConnection); |  | ||||||
|     connect(this, SIGNAL(Resumed()), breakpoint_model, SLOT(OnResumed())); |     connect(this, SIGNAL(Resumed()), breakpoint_model, SLOT(OnResumed())); | ||||||
| 
 | 
 | ||||||
|     connect(this, SIGNAL(BreakPointsChanged(const QModelIndex&,const QModelIndex&)), |     connect(this, SIGNAL(BreakPointsChanged(const QModelIndex&, const QModelIndex&)), | ||||||
|             breakpoint_model, SIGNAL(dataChanged(const QModelIndex&,const QModelIndex&))); |             breakpoint_model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&))); | ||||||
| 
 | 
 | ||||||
|     QWidget* main_widget = new QWidget; |     QWidget* main_widget = new QWidget; | ||||||
|     auto main_layout = new QVBoxLayout; |     auto main_layout = new QVBoxLayout; | ||||||
|  | @ -197,38 +175,32 @@ GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(std::shared_ptr<Pica::Debug | ||||||
|     setWidget(main_widget); |     setWidget(main_widget); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GraphicsBreakPointsWidget::OnPicaBreakPointHit(Event event, void* data) | void GraphicsBreakPointsWidget::OnPicaBreakPointHit(Event event, void* data) { | ||||||
| { |  | ||||||
|     // Process in GUI thread
 |     // Process in GUI thread
 | ||||||
|     emit BreakPointHit(event, data); |     emit BreakPointHit(event, data); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GraphicsBreakPointsWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) | void GraphicsBreakPointsWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) { | ||||||
| { |  | ||||||
|     status_text->setText(tr("Emulation halted at breakpoint")); |     status_text->setText(tr("Emulation halted at breakpoint")); | ||||||
|     resume_button->setEnabled(true); |     resume_button->setEnabled(true); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GraphicsBreakPointsWidget::OnPicaResume() | void GraphicsBreakPointsWidget::OnPicaResume() { | ||||||
| { |  | ||||||
|     // Process in GUI thread
 |     // Process in GUI thread
 | ||||||
|     emit Resumed(); |     emit Resumed(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GraphicsBreakPointsWidget::OnResumed() | void GraphicsBreakPointsWidget::OnResumed() { | ||||||
| { |  | ||||||
|     status_text->setText(tr("Emulation running")); |     status_text->setText(tr("Emulation running")); | ||||||
|     resume_button->setEnabled(false); |     resume_button->setEnabled(false); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GraphicsBreakPointsWidget::OnResumeRequested() | void GraphicsBreakPointsWidget::OnResumeRequested() { | ||||||
| { |  | ||||||
|     if (auto context = context_weak.lock()) |     if (auto context = context_weak.lock()) | ||||||
|         context->Resume(); |         context->Resume(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GraphicsBreakPointsWidget::OnItemDoubleClicked(const QModelIndex& index) | void GraphicsBreakPointsWidget::OnItemDoubleClicked(const QModelIndex& index) { | ||||||
| { |  | ||||||
|     if (!index.isValid()) |     if (!index.isValid()) | ||||||
|         return; |         return; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -5,9 +5,7 @@ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <memory> | #include <memory> | ||||||
| 
 |  | ||||||
| #include <QDockWidget> | #include <QDockWidget> | ||||||
| 
 |  | ||||||
| #include "video_core/debug_utils/debug_utils.h" | #include "video_core/debug_utils/debug_utils.h" | ||||||
| 
 | 
 | ||||||
| class QLabel; | class QLabel; | ||||||
|  |  | ||||||
|  | @ -5,9 +5,7 @@ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <memory> | #include <memory> | ||||||
| 
 |  | ||||||
| #include <QAbstractListModel> | #include <QAbstractListModel> | ||||||
| 
 |  | ||||||
| #include "video_core/debug_utils/debug_utils.h" | #include "video_core/debug_utils/debug_utils.h" | ||||||
| 
 | 
 | ||||||
| class BreakPointModel : public QAbstractListModel { | class BreakPointModel : public QAbstractListModel { | ||||||
|  | @ -23,7 +21,7 @@ public: | ||||||
|     int columnCount(const QModelIndex& parent = QModelIndex()) const override; |     int columnCount(const QModelIndex& parent = QModelIndex()) const override; | ||||||
|     int rowCount(const QModelIndex& parent = QModelIndex()) const override; |     int rowCount(const QModelIndex& parent = QModelIndex()) const override; | ||||||
|     QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; |     QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; | ||||||
|     Qt::ItemFlags flags(const QModelIndex &index) const override; |     Qt::ItemFlags flags(const QModelIndex& index) const override; | ||||||
| 
 | 
 | ||||||
|     bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; |     bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -13,16 +13,13 @@ | ||||||
| #include <QSpinBox> | #include <QSpinBox> | ||||||
| #include <QTreeView> | #include <QTreeView> | ||||||
| #include <QVBoxLayout> | #include <QVBoxLayout> | ||||||
| 
 |  | ||||||
| #include "citra_qt/debugger/graphics_cmdlists.h" | #include "citra_qt/debugger/graphics_cmdlists.h" | ||||||
| #include "citra_qt/util/spinbox.h" | #include "citra_qt/util/spinbox.h" | ||||||
| #include "citra_qt/util/util.h" | #include "citra_qt/util/util.h" | ||||||
| 
 |  | ||||||
| #include "common/vector_math.h" | #include "common/vector_math.h" | ||||||
| 
 | #include "video_core/debug_utils/debug_utils.h" | ||||||
| #include "video_core/pica.h" | #include "video_core/pica.h" | ||||||
| #include "video_core/pica_state.h" | #include "video_core/pica_state.h" | ||||||
| #include "video_core/debug_utils/debug_utils.h" |  | ||||||
| 
 | 
 | ||||||
| QImage LoadTexture(u8* src, const Pica::DebugUtils::TextureInfo& info) { | QImage LoadTexture(u8* src, const Pica::DebugUtils::TextureInfo& info) { | ||||||
|     QImage decoded_image(info.width, info.height, QImage::Format_ARGB32); |     QImage decoded_image(info.width, info.height, QImage::Format_ARGB32); | ||||||
|  | @ -38,7 +35,8 @@ QImage LoadTexture(u8* src, const Pica::DebugUtils::TextureInfo& info) { | ||||||
| 
 | 
 | ||||||
| class TextureInfoWidget : public QWidget { | class TextureInfoWidget : public QWidget { | ||||||
| public: | public: | ||||||
|     TextureInfoWidget(u8* src, const Pica::DebugUtils::TextureInfo& info, QWidget* parent = nullptr) : QWidget(parent) { |     TextureInfoWidget(u8* src, const Pica::DebugUtils::TextureInfo& info, QWidget* parent = nullptr) | ||||||
|  |         : QWidget(parent) { | ||||||
|         QLabel* image_widget = new QLabel; |         QLabel* image_widget = new QLabel; | ||||||
|         QPixmap image_pixmap = QPixmap::fromImage(LoadTexture(src, info)); |         QPixmap image_pixmap = QPixmap::fromImage(LoadTexture(src, info)); | ||||||
|         image_pixmap = image_pixmap.scaled(200, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation); |         image_pixmap = image_pixmap.scaled(200, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation); | ||||||
|  | @ -50,9 +48,7 @@ public: | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| GPUCommandListModel::GPUCommandListModel(QObject* parent) : QAbstractListModel(parent) { | GPUCommandListModel::GPUCommandListModel(QObject* parent) : QAbstractListModel(parent) {} | ||||||
| 
 |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| int GPUCommandListModel::rowCount(const QModelIndex& parent) const { | int GPUCommandListModel::rowCount(const QModelIndex& parent) const { | ||||||
|     return static_cast<int>(pica_trace.writes.size()); |     return static_cast<int>(pica_trace.writes.size()); | ||||||
|  | @ -70,7 +66,7 @@ QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const { | ||||||
| 
 | 
 | ||||||
|     if (role == Qt::DisplayRole) { |     if (role == Qt::DisplayRole) { | ||||||
|         QString content; |         QString content; | ||||||
|         switch ( index.column() ) { |         switch (index.column()) { | ||||||
|         case 0: |         case 0: | ||||||
|             return QString::fromLatin1(Pica::Regs::GetCommandName(write.cmd_id).c_str()); |             return QString::fromLatin1(Pica::Regs::GetCommandName(write.cmd_id).c_str()); | ||||||
|         case 1: |         case 1: | ||||||
|  | @ -88,9 +84,8 @@ QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| QVariant GPUCommandListModel::headerData(int section, Qt::Orientation orientation, int role) const { | QVariant GPUCommandListModel::headerData(int section, Qt::Orientation orientation, int role) const { | ||||||
|     switch(role) { |     switch (role) { | ||||||
|     case Qt::DisplayRole: |     case Qt::DisplayRole: { | ||||||
|     { |  | ||||||
|         switch (section) { |         switch (section) { | ||||||
|         case 0: |         case 0: | ||||||
|             return tr("Command Name"); |             return tr("Command Name"); | ||||||
|  | @ -117,14 +112,14 @@ void GPUCommandListModel::OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& | ||||||
|     endResetModel(); |     endResetModel(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #define COMMAND_IN_RANGE(cmd_id, reg_name)   \ | #define COMMAND_IN_RANGE(cmd_id, reg_name)                                                         \ | ||||||
|     (cmd_id >= PICA_REG_INDEX(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) |      cmd_id < PICA_REG_INDEX(reg_name) + sizeof(decltype(Pica::g_state.regs.reg_name)) / 4) | ||||||
| 
 | 
 | ||||||
| void GPUCommandListWidget::OnCommandDoubleClicked(const QModelIndex& index) { | void GPUCommandListWidget::OnCommandDoubleClicked(const QModelIndex& index) { | ||||||
|     const unsigned int command_id = list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toUInt(); |     const unsigned int command_id = | ||||||
|     if (COMMAND_IN_RANGE(command_id, texture0) || |         list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toUInt(); | ||||||
|         COMMAND_IN_RANGE(command_id, texture1) || |     if (COMMAND_IN_RANGE(command_id, texture0) || COMMAND_IN_RANGE(command_id, texture1) || | ||||||
|         COMMAND_IN_RANGE(command_id, texture2)) { |         COMMAND_IN_RANGE(command_id, texture2)) { | ||||||
| 
 | 
 | ||||||
|         unsigned index; |         unsigned index; | ||||||
|  | @ -148,9 +143,9 @@ void GPUCommandListWidget::OnCommandDoubleClicked(const QModelIndex& index) { | ||||||
| void GPUCommandListWidget::SetCommandInfo(const QModelIndex& index) { | void GPUCommandListWidget::SetCommandInfo(const QModelIndex& index) { | ||||||
|     QWidget* new_info_widget = nullptr; |     QWidget* new_info_widget = nullptr; | ||||||
| 
 | 
 | ||||||
|     const unsigned int command_id = list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toUInt(); |     const unsigned int command_id = | ||||||
|     if (COMMAND_IN_RANGE(command_id, texture0) || |         list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toUInt(); | ||||||
|         COMMAND_IN_RANGE(command_id, texture1) || |     if (COMMAND_IN_RANGE(command_id, texture0) || COMMAND_IN_RANGE(command_id, texture1) || | ||||||
|         COMMAND_IN_RANGE(command_id, texture2)) { |         COMMAND_IN_RANGE(command_id, texture2)) { | ||||||
| 
 | 
 | ||||||
|         unsigned index; |         unsigned index; | ||||||
|  | @ -179,7 +174,8 @@ void GPUCommandListWidget::SetCommandInfo(const QModelIndex& index) { | ||||||
| } | } | ||||||
| #undef COMMAND_IN_RANGE | #undef COMMAND_IN_RANGE | ||||||
| 
 | 
 | ||||||
| GPUCommandListWidget::GPUCommandListWidget(QWidget* parent) : QDockWidget(tr("Pica Command List"), parent) { | GPUCommandListWidget::GPUCommandListWidget(QWidget* parent) | ||||||
|  |     : QDockWidget(tr("Pica Command List"), parent) { | ||||||
|     setObjectName("Pica Command List"); |     setObjectName("Pica Command List"); | ||||||
|     GPUCommandListModel* model = new GPUCommandListModel(this); |     GPUCommandListModel* model = new GPUCommandListModel(this); | ||||||
| 
 | 
 | ||||||
|  | @ -191,23 +187,24 @@ GPUCommandListWidget::GPUCommandListWidget(QWidget* parent) : QDockWidget(tr("Pi | ||||||
|     list_widget->setRootIsDecorated(false); |     list_widget->setRootIsDecorated(false); | ||||||
|     list_widget->setUniformRowHeights(true); |     list_widget->setUniformRowHeights(true); | ||||||
| 
 | 
 | ||||||
| #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) | #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) | ||||||
|     list_widget->header()->setSectionResizeMode(QHeaderView::ResizeToContents); |     list_widget->header()->setSectionResizeMode(QHeaderView::ResizeToContents); | ||||||
| #else | #else | ||||||
|     list_widget->header()->setResizeMode(QHeaderView::ResizeToContents); |     list_widget->header()->setResizeMode(QHeaderView::ResizeToContents); | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|     connect(list_widget->selectionModel(), SIGNAL(currentChanged(const QModelIndex&,const QModelIndex&)), |     connect(list_widget->selectionModel(), | ||||||
|             this, SLOT(SetCommandInfo(const QModelIndex&))); |             SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), this, | ||||||
|     connect(list_widget, SIGNAL(doubleClicked(const QModelIndex&)), |             SLOT(SetCommandInfo(const QModelIndex&))); | ||||||
|             this, SLOT(OnCommandDoubleClicked(const QModelIndex&))); |     connect(list_widget, SIGNAL(doubleClicked(const QModelIndex&)), this, | ||||||
|  |             SLOT(OnCommandDoubleClicked(const QModelIndex&))); | ||||||
| 
 | 
 | ||||||
|     toggle_tracing = new QPushButton(tr("Start Tracing")); |     toggle_tracing = new QPushButton(tr("Start Tracing")); | ||||||
|     QPushButton* copy_all = new QPushButton(tr("Copy All")); |     QPushButton* copy_all = new QPushButton(tr("Copy All")); | ||||||
| 
 | 
 | ||||||
|     connect(toggle_tracing, SIGNAL(clicked()), this, SLOT(OnToggleTracing())); |     connect(toggle_tracing, SIGNAL(clicked()), this, SLOT(OnToggleTracing())); | ||||||
|     connect(this, SIGNAL(TracingFinished(const Pica::DebugUtils::PicaTrace&)), |     connect(this, SIGNAL(TracingFinished(const Pica::DebugUtils::PicaTrace&)), model, | ||||||
|             model, SLOT(OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace&))); |             SLOT(OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace&))); | ||||||
| 
 | 
 | ||||||
|     connect(copy_all, SIGNAL(clicked()), this, SLOT(CopyAllToClipboard())); |     connect(copy_all, SIGNAL(clicked()), this, SLOT(CopyAllToClipboard())); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -6,15 +6,13 @@ | ||||||
| 
 | 
 | ||||||
| #include <QAbstractListModel> | #include <QAbstractListModel> | ||||||
| #include <QDockWidget> | #include <QDockWidget> | ||||||
| 
 |  | ||||||
| #include "video_core/gpu_debugger.h" |  | ||||||
| #include "video_core/debug_utils/debug_utils.h" | #include "video_core/debug_utils/debug_utils.h" | ||||||
|  | #include "video_core/gpu_debugger.h" | ||||||
| 
 | 
 | ||||||
| class QPushButton; | class QPushButton; | ||||||
| class QTreeView; | class QTreeView; | ||||||
| 
 | 
 | ||||||
| class GPUCommandListModel : public QAbstractListModel | class GPUCommandListModel : public QAbstractListModel { | ||||||
| { |  | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|  | @ -27,7 +25,8 @@ public: | ||||||
|     int columnCount(const QModelIndex& parent = QModelIndex()) const override; |     int columnCount(const QModelIndex& parent = QModelIndex()) const override; | ||||||
|     int rowCount(const QModelIndex& parent = QModelIndex()) const override; |     int rowCount(const QModelIndex& parent = QModelIndex()) const override; | ||||||
|     QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; |     QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; | ||||||
|     QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; |     QVariant headerData(int section, Qt::Orientation orientation, | ||||||
|  |                         int role = Qt::DisplayRole) const override; | ||||||
| 
 | 
 | ||||||
| public slots: | public slots: | ||||||
|     void OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& trace); |     void OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& trace); | ||||||
|  | @ -36,8 +35,7 @@ private: | ||||||
|     Pica::DebugUtils::PicaTrace pica_trace; |     Pica::DebugUtils::PicaTrace pica_trace; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class GPUCommandListWidget : public QDockWidget | class GPUCommandListWidget : public QDockWidget { | ||||||
| { |  | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|  |  | ||||||
|  | @ -11,24 +11,20 @@ | ||||||
| #include <QPushButton> | #include <QPushButton> | ||||||
| #include <QScrollArea> | #include <QScrollArea> | ||||||
| #include <QSpinBox> | #include <QSpinBox> | ||||||
| 
 |  | ||||||
| #include "citra_qt/debugger/graphics_surface.h" | #include "citra_qt/debugger/graphics_surface.h" | ||||||
| #include "citra_qt/util/spinbox.h" | #include "citra_qt/util/spinbox.h" | ||||||
| 
 |  | ||||||
| #include "common/color.h" | #include "common/color.h" | ||||||
| 
 |  | ||||||
| #include "core/memory.h" |  | ||||||
| #include "core/hw/gpu.h" | #include "core/hw/gpu.h" | ||||||
| 
 | #include "core/memory.h" | ||||||
| #include "video_core/pica.h" | #include "video_core/pica.h" | ||||||
| #include "video_core/pica_state.h" | #include "video_core/pica_state.h" | ||||||
| #include "video_core/utils.h" | #include "video_core/utils.h" | ||||||
| 
 | 
 | ||||||
| SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_) : QLabel(parent), surface_widget(surface_widget_) {} | SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_) | ||||||
|  |     : QLabel(parent), surface_widget(surface_widget_) {} | ||||||
| SurfacePicture::~SurfacePicture() {} | SurfacePicture::~SurfacePicture() {} | ||||||
| 
 | 
 | ||||||
| void SurfacePicture::mousePressEvent(QMouseEvent* event) | void SurfacePicture::mousePressEvent(QMouseEvent* event) { | ||||||
| { |  | ||||||
|     // Only do something while the left mouse button is held down
 |     // Only do something while the left mouse button is held down
 | ||||||
|     if (!(event->buttons() & Qt::LeftButton)) |     if (!(event->buttons() & Qt::LeftButton)) | ||||||
|         return; |         return; | ||||||
|  | @ -41,18 +37,15 @@ void SurfacePicture::mousePressEvent(QMouseEvent* event) | ||||||
|                              event->y() * pixmap()->height() / height()); |                              event->y() * pixmap()->height() / height()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void SurfacePicture::mouseMoveEvent(QMouseEvent* event) | void SurfacePicture::mouseMoveEvent(QMouseEvent* event) { | ||||||
| { |  | ||||||
|     // We also want to handle the event if the user moves the mouse while holding down the LMB
 |     // We also want to handle the event if the user moves the mouse while holding down the LMB
 | ||||||
|     mousePressEvent(event); |     mousePressEvent(event); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext> debug_context, | GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext> debug_context, | ||||||
|                                                      QWidget* parent) |                                              QWidget* parent) | ||||||
|     : BreakPointObserverDock(debug_context, tr("Pica Surface Viewer"), parent), |     : BreakPointObserverDock(debug_context, tr("Pica Surface Viewer"), parent), | ||||||
|       surface_source(Source::ColorBuffer) |       surface_source(Source::ColorBuffer) { | ||||||
| { |  | ||||||
|     setObjectName("PicaSurface"); |     setObjectName("PicaSurface"); | ||||||
| 
 | 
 | ||||||
|     surface_source_list = new QComboBox; |     surface_source_list = new QComboBox; | ||||||
|  | @ -124,13 +117,20 @@ GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext> | ||||||
| 
 | 
 | ||||||
|     // Connections
 |     // Connections
 | ||||||
|     connect(this, SIGNAL(Update()), this, SLOT(OnUpdate())); |     connect(this, SIGNAL(Update()), this, SLOT(OnUpdate())); | ||||||
|     connect(surface_source_list, SIGNAL(currentIndexChanged(int)), this, SLOT(OnSurfaceSourceChanged(int))); |     connect(surface_source_list, SIGNAL(currentIndexChanged(int)), this, | ||||||
|     connect(surface_address_control, SIGNAL(ValueChanged(qint64)), this, SLOT(OnSurfaceAddressChanged(qint64))); |             SLOT(OnSurfaceSourceChanged(int))); | ||||||
|     connect(surface_width_control, SIGNAL(valueChanged(int)), this, SLOT(OnSurfaceWidthChanged(int))); |     connect(surface_address_control, SIGNAL(ValueChanged(qint64)), this, | ||||||
|     connect(surface_height_control, SIGNAL(valueChanged(int)), this, SLOT(OnSurfaceHeightChanged(int))); |             SLOT(OnSurfaceAddressChanged(qint64))); | ||||||
|     connect(surface_format_control, SIGNAL(currentIndexChanged(int)), this, SLOT(OnSurfaceFormatChanged(int))); |     connect(surface_width_control, SIGNAL(valueChanged(int)), this, | ||||||
|     connect(surface_picker_x_control, SIGNAL(valueChanged(int)), this, SLOT(OnSurfacePickerXChanged(int))); |             SLOT(OnSurfaceWidthChanged(int))); | ||||||
|     connect(surface_picker_y_control, SIGNAL(valueChanged(int)), this, SLOT(OnSurfacePickerYChanged(int))); |     connect(surface_height_control, SIGNAL(valueChanged(int)), this, | ||||||
|  |             SLOT(OnSurfaceHeightChanged(int))); | ||||||
|  |     connect(surface_format_control, SIGNAL(currentIndexChanged(int)), this, | ||||||
|  |             SLOT(OnSurfaceFormatChanged(int))); | ||||||
|  |     connect(surface_picker_x_control, SIGNAL(valueChanged(int)), this, | ||||||
|  |             SLOT(OnSurfacePickerXChanged(int))); | ||||||
|  |     connect(surface_picker_y_control, SIGNAL(valueChanged(int)), this, | ||||||
|  |             SLOT(OnSurfacePickerYChanged(int))); | ||||||
|     connect(save_surface, SIGNAL(clicked()), this, SLOT(SaveSurface())); |     connect(save_surface, SIGNAL(clicked()), this, SLOT(SaveSurface())); | ||||||
| 
 | 
 | ||||||
|     auto main_widget = new QWidget; |     auto main_widget = new QWidget; | ||||||
|  | @ -203,25 +203,21 @@ GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext> | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GraphicsSurfaceWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) | void GraphicsSurfaceWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) { | ||||||
| { |  | ||||||
|     emit Update(); |     emit Update(); | ||||||
|     widget()->setEnabled(true); |     widget()->setEnabled(true); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GraphicsSurfaceWidget::OnResumed() | void GraphicsSurfaceWidget::OnResumed() { | ||||||
| { |  | ||||||
|     widget()->setEnabled(false); |     widget()->setEnabled(false); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GraphicsSurfaceWidget::OnSurfaceSourceChanged(int new_value) | void GraphicsSurfaceWidget::OnSurfaceSourceChanged(int new_value) { | ||||||
| { |  | ||||||
|     surface_source = static_cast<Source>(new_value); |     surface_source = static_cast<Source>(new_value); | ||||||
|     emit Update(); |     emit Update(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GraphicsSurfaceWidget::OnSurfaceAddressChanged(qint64 new_value) | void GraphicsSurfaceWidget::OnSurfaceAddressChanged(qint64 new_value) { | ||||||
| { |  | ||||||
|     if (surface_address != new_value) { |     if (surface_address != new_value) { | ||||||
|         surface_address = static_cast<unsigned>(new_value); |         surface_address = static_cast<unsigned>(new_value); | ||||||
| 
 | 
 | ||||||
|  | @ -230,8 +226,7 @@ void GraphicsSurfaceWidget::OnSurfaceAddressChanged(qint64 new_value) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GraphicsSurfaceWidget::OnSurfaceWidthChanged(int new_value) | void GraphicsSurfaceWidget::OnSurfaceWidthChanged(int new_value) { | ||||||
| { |  | ||||||
|     if (surface_width != static_cast<unsigned>(new_value)) { |     if (surface_width != static_cast<unsigned>(new_value)) { | ||||||
|         surface_width = static_cast<unsigned>(new_value); |         surface_width = static_cast<unsigned>(new_value); | ||||||
| 
 | 
 | ||||||
|  | @ -240,8 +235,7 @@ void GraphicsSurfaceWidget::OnSurfaceWidthChanged(int new_value) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GraphicsSurfaceWidget::OnSurfaceHeightChanged(int new_value) | void GraphicsSurfaceWidget::OnSurfaceHeightChanged(int new_value) { | ||||||
| { |  | ||||||
|     if (surface_height != static_cast<unsigned>(new_value)) { |     if (surface_height != static_cast<unsigned>(new_value)) { | ||||||
|         surface_height = static_cast<unsigned>(new_value); |         surface_height = static_cast<unsigned>(new_value); | ||||||
| 
 | 
 | ||||||
|  | @ -250,8 +244,7 @@ void GraphicsSurfaceWidget::OnSurfaceHeightChanged(int new_value) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GraphicsSurfaceWidget::OnSurfaceFormatChanged(int new_value) | void GraphicsSurfaceWidget::OnSurfaceFormatChanged(int new_value) { | ||||||
| { |  | ||||||
|     if (surface_format != static_cast<Format>(new_value)) { |     if (surface_format != static_cast<Format>(new_value)) { | ||||||
|         surface_format = static_cast<Format>(new_value); |         surface_format = static_cast<Format>(new_value); | ||||||
| 
 | 
 | ||||||
|  | @ -260,24 +253,21 @@ void GraphicsSurfaceWidget::OnSurfaceFormatChanged(int new_value) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GraphicsSurfaceWidget::OnSurfacePickerXChanged(int new_value) | void GraphicsSurfaceWidget::OnSurfacePickerXChanged(int new_value) { | ||||||
| { |  | ||||||
|     if (surface_picker_x != new_value) { |     if (surface_picker_x != new_value) { | ||||||
|         surface_picker_x = new_value; |         surface_picker_x = new_value; | ||||||
|         Pick(surface_picker_x, surface_picker_y); |         Pick(surface_picker_x, surface_picker_y); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GraphicsSurfaceWidget::OnSurfacePickerYChanged(int new_value) | void GraphicsSurfaceWidget::OnSurfacePickerYChanged(int new_value) { | ||||||
| { |  | ||||||
|     if (surface_picker_y != new_value) { |     if (surface_picker_y != new_value) { | ||||||
|         surface_picker_y = new_value; |         surface_picker_y = new_value; | ||||||
|         Pick(surface_picker_x, surface_picker_y); |         Pick(surface_picker_x, surface_picker_y); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GraphicsSurfaceWidget::Pick(int x, int y) | void GraphicsSurfaceWidget::Pick(int x, int y) { | ||||||
| { |  | ||||||
|     surface_picker_x_control->setValue(x); |     surface_picker_x_control->setValue(x); | ||||||
|     surface_picker_y_control->setValue(y); |     surface_picker_y_control->setValue(y); | ||||||
| 
 | 
 | ||||||
|  | @ -312,74 +302,63 @@ void GraphicsSurfaceWidget::Pick(int x, int y) | ||||||
| 
 | 
 | ||||||
|     auto GetText = [offset](Format format, const u8* pixel) { |     auto GetText = [offset](Format format, const u8* pixel) { | ||||||
|         switch (format) { |         switch (format) { | ||||||
|         case Format::RGBA8: |         case Format::RGBA8: { | ||||||
|         { |  | ||||||
|             auto value = Color::DecodeRGBA8(pixel) / 255.0f; |             auto value = Color::DecodeRGBA8(pixel) / 255.0f; | ||||||
|             return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4") |             return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4") | ||||||
|                       .arg(QString::number(value.r(), 'f', 2)) |                 .arg(QString::number(value.r(), 'f', 2)) | ||||||
|                       .arg(QString::number(value.g(), 'f', 2)) |                 .arg(QString::number(value.g(), 'f', 2)) | ||||||
|                       .arg(QString::number(value.b(), 'f', 2)) |                 .arg(QString::number(value.b(), 'f', 2)) | ||||||
|                       .arg(QString::number(value.a(), 'f', 2)); |                 .arg(QString::number(value.a(), 'f', 2)); | ||||||
|         } |         } | ||||||
|         case Format::RGB8: |         case Format::RGB8: { | ||||||
|         { |  | ||||||
|             auto value = Color::DecodeRGB8(pixel) / 255.0f; |             auto value = Color::DecodeRGB8(pixel) / 255.0f; | ||||||
|             return QString("Red: %1, Green: %2, Blue: %3") |             return QString("Red: %1, Green: %2, Blue: %3") | ||||||
|                       .arg(QString::number(value.r(), 'f', 2)) |                 .arg(QString::number(value.r(), 'f', 2)) | ||||||
|                       .arg(QString::number(value.g(), 'f', 2)) |                 .arg(QString::number(value.g(), 'f', 2)) | ||||||
|                       .arg(QString::number(value.b(), 'f', 2)); |                 .arg(QString::number(value.b(), 'f', 2)); | ||||||
|         } |         } | ||||||
|         case Format::RGB5A1: |         case Format::RGB5A1: { | ||||||
|         { |  | ||||||
|             auto value = Color::DecodeRGB5A1(pixel) / 255.0f; |             auto value = Color::DecodeRGB5A1(pixel) / 255.0f; | ||||||
|             return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4") |             return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4") | ||||||
|                       .arg(QString::number(value.r(), 'f', 2)) |                 .arg(QString::number(value.r(), 'f', 2)) | ||||||
|                       .arg(QString::number(value.g(), 'f', 2)) |                 .arg(QString::number(value.g(), 'f', 2)) | ||||||
|                       .arg(QString::number(value.b(), 'f', 2)) |                 .arg(QString::number(value.b(), 'f', 2)) | ||||||
|                       .arg(QString::number(value.a(), 'f', 2)); |                 .arg(QString::number(value.a(), 'f', 2)); | ||||||
|         } |         } | ||||||
|         case Format::RGB565: |         case Format::RGB565: { | ||||||
|         { |  | ||||||
|             auto value = Color::DecodeRGB565(pixel) / 255.0f; |             auto value = Color::DecodeRGB565(pixel) / 255.0f; | ||||||
|             return QString("Red: %1, Green: %2, Blue: %3") |             return QString("Red: %1, Green: %2, Blue: %3") | ||||||
|                       .arg(QString::number(value.r(), 'f', 2)) |                 .arg(QString::number(value.r(), 'f', 2)) | ||||||
|                       .arg(QString::number(value.g(), 'f', 2)) |                 .arg(QString::number(value.g(), 'f', 2)) | ||||||
|                       .arg(QString::number(value.b(), 'f', 2)); |                 .arg(QString::number(value.b(), 'f', 2)); | ||||||
|         } |         } | ||||||
|         case Format::RGBA4: |         case Format::RGBA4: { | ||||||
|         { |  | ||||||
|             auto value = Color::DecodeRGBA4(pixel) / 255.0f; |             auto value = Color::DecodeRGBA4(pixel) / 255.0f; | ||||||
|             return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4") |             return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4") | ||||||
|                       .arg(QString::number(value.r(), 'f', 2)) |                 .arg(QString::number(value.r(), 'f', 2)) | ||||||
|                       .arg(QString::number(value.g(), 'f', 2)) |                 .arg(QString::number(value.g(), 'f', 2)) | ||||||
|                       .arg(QString::number(value.b(), 'f', 2)) |                 .arg(QString::number(value.b(), 'f', 2)) | ||||||
|                       .arg(QString::number(value.a(), 'f', 2)); |                 .arg(QString::number(value.a(), 'f', 2)); | ||||||
|         } |         } | ||||||
|         case Format::IA8: |         case Format::IA8: | ||||||
|             return QString("Index: %1, Alpha: %2") |             return QString("Index: %1, Alpha: %2").arg(pixel[0]).arg(pixel[1]); | ||||||
|                       .arg(pixel[0]) |  | ||||||
|                       .arg(pixel[1]); |  | ||||||
|         case Format::RG8: { |         case Format::RG8: { | ||||||
|             auto value = Color::DecodeRG8(pixel) / 255.0f; |             auto value = Color::DecodeRG8(pixel) / 255.0f; | ||||||
|             return QString("Red: %1, Green: %2") |             return QString("Red: %1, Green: %2") | ||||||
|                       .arg(QString::number(value.r(), 'f', 2)) |                 .arg(QString::number(value.r(), 'f', 2)) | ||||||
|                       .arg(QString::number(value.g(), 'f', 2)); |                 .arg(QString::number(value.g(), 'f', 2)); | ||||||
|         } |         } | ||||||
|         case Format::I8: |         case Format::I8: | ||||||
|             return QString("Index: %1").arg(*pixel); |             return QString("Index: %1").arg(*pixel); | ||||||
|         case Format::A8: |         case Format::A8: | ||||||
|             return QString("Alpha: %1").arg(QString::number(*pixel / 255.0f, 'f', 2)); |             return QString("Alpha: %1").arg(QString::number(*pixel / 255.0f, 'f', 2)); | ||||||
|         case Format::IA4: |         case Format::IA4: | ||||||
|             return QString("Index: %1, Alpha: %2") |             return QString("Index: %1, Alpha: %2").arg(*pixel & 0xF).arg((*pixel & 0xF0) >> 4); | ||||||
|                       .arg(*pixel & 0xF) |         case Format::I4: { | ||||||
|                       .arg((*pixel & 0xF0) >> 4); |  | ||||||
|         case Format::I4: |  | ||||||
|         { |  | ||||||
|             u8 i = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF; |             u8 i = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF; | ||||||
|             return QString("Index: %1").arg(i); |             return QString("Index: %1").arg(i); | ||||||
|         } |         } | ||||||
|         case Format::A4: |         case Format::A4: { | ||||||
|         { |  | ||||||
|             u8 a = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF; |             u8 a = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF; | ||||||
|             return QString("Alpha: %1").arg(QString::number(a / 15.0f, 'f', 2)); |             return QString("Alpha: %1").arg(QString::number(a / 15.0f, 'f', 2)); | ||||||
|         } |         } | ||||||
|  | @ -387,21 +366,20 @@ void GraphicsSurfaceWidget::Pick(int x, int y) | ||||||
|         case Format::ETC1A4: |         case Format::ETC1A4: | ||||||
|             // TODO: Display block information or channel values?
 |             // TODO: Display block information or channel values?
 | ||||||
|             return QString("Compressed data"); |             return QString("Compressed data"); | ||||||
|         case Format::D16: |         case Format::D16: { | ||||||
|         { |  | ||||||
|             auto value = Color::DecodeD16(pixel); |             auto value = Color::DecodeD16(pixel); | ||||||
|             return QString("Depth: %1").arg(QString::number(value / (float)0xFFFF, 'f', 4)); |             return QString("Depth: %1").arg(QString::number(value / (float)0xFFFF, 'f', 4)); | ||||||
|         } |         } | ||||||
|         case Format::D24: |         case Format::D24: { | ||||||
|         { |  | ||||||
|             auto value = Color::DecodeD24(pixel); |             auto value = Color::DecodeD24(pixel); | ||||||
|             return QString("Depth: %1").arg(QString::number(value / (float)0xFFFFFF, 'f', 4)); |             return QString("Depth: %1").arg(QString::number(value / (float)0xFFFFFF, 'f', 4)); | ||||||
|         } |         } | ||||||
|         case Format::D24X8: |         case Format::D24X8: | ||||||
|         case Format::X24S8: |         case Format::X24S8: { | ||||||
|         { |  | ||||||
|             auto values = Color::DecodeD24S8(pixel); |             auto values = Color::DecodeD24S8(pixel); | ||||||
|             return QString("Depth: %1, Stencil: %2").arg(QString::number(values[0] / (float)0xFFFFFF, 'f', 4)).arg(values[1]); |             return QString("Depth: %1, Stencil: %2") | ||||||
|  |                 .arg(QString::number(values[0] / (float)0xFFFFFF, 'f', 4)) | ||||||
|  |                 .arg(values[1]); | ||||||
|         } |         } | ||||||
|         case Format::Unknown: |         case Format::Unknown: | ||||||
|             return QString("Unknown format"); |             return QString("Unknown format"); | ||||||
|  | @ -422,18 +400,18 @@ void GraphicsSurfaceWidget::Pick(int x, int y) | ||||||
|         nibbles.append(QString::number(nibble, 16).toUpper()); |         nibbles.append(QString::number(nibble, 16).toUpper()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     surface_info_label->setText(QString("Raw: 0x%3\n(%4)").arg(nibbles).arg(GetText(surface_format, pixel))); |     surface_info_label->setText( | ||||||
|  |         QString("Raw: 0x%3\n(%4)").arg(nibbles).arg(GetText(surface_format, pixel))); | ||||||
|     surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); |     surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GraphicsSurfaceWidget::OnUpdate() | void GraphicsSurfaceWidget::OnUpdate() { | ||||||
| { |  | ||||||
|     QPixmap pixmap; |     QPixmap pixmap; | ||||||
| 
 | 
 | ||||||
|     switch (surface_source) { |     switch (surface_source) { | ||||||
|     case Source::ColorBuffer: |     case Source::ColorBuffer: { | ||||||
|     { |         // TODO: Store a reference to the registers in the debug context instead of accessing them
 | ||||||
|         // TODO: Store a reference to the registers in the debug context instead of accessing them directly...
 |         // directly...
 | ||||||
| 
 | 
 | ||||||
|         const auto& framebuffer = Pica::g_state.regs.framebuffer; |         const auto& framebuffer = Pica::g_state.regs.framebuffer; | ||||||
| 
 | 
 | ||||||
|  | @ -470,8 +448,7 @@ void GraphicsSurfaceWidget::OnUpdate() | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     case Source::DepthBuffer: |     case Source::DepthBuffer: { | ||||||
|     { |  | ||||||
|         const auto& framebuffer = Pica::g_state.regs.framebuffer; |         const auto& framebuffer = Pica::g_state.regs.framebuffer; | ||||||
| 
 | 
 | ||||||
|         surface_address = framebuffer.GetDepthBufferPhysicalAddress(); |         surface_address = framebuffer.GetDepthBufferPhysicalAddress(); | ||||||
|  | @ -499,8 +476,7 @@ void GraphicsSurfaceWidget::OnUpdate() | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     case Source::StencilBuffer: |     case Source::StencilBuffer: { | ||||||
|     { |  | ||||||
|         const auto& framebuffer = Pica::g_state.regs.framebuffer; |         const auto& framebuffer = Pica::g_state.regs.framebuffer; | ||||||
| 
 | 
 | ||||||
|         surface_address = framebuffer.GetDepthBufferPhysicalAddress(); |         surface_address = framebuffer.GetDepthBufferPhysicalAddress(); | ||||||
|  | @ -522,12 +498,14 @@ void GraphicsSurfaceWidget::OnUpdate() | ||||||
| 
 | 
 | ||||||
|     case Source::Texture0: |     case Source::Texture0: | ||||||
|     case Source::Texture1: |     case Source::Texture1: | ||||||
|     case Source::Texture2: |     case Source::Texture2: { | ||||||
|     { |  | ||||||
|         unsigned texture_index; |         unsigned texture_index; | ||||||
|         if (surface_source == Source::Texture0) texture_index = 0; |         if (surface_source == Source::Texture0) | ||||||
|         else if (surface_source == Source::Texture1) texture_index = 1; |             texture_index = 0; | ||||||
|         else if (surface_source == Source::Texture2) texture_index = 2; |         else if (surface_source == Source::Texture1) | ||||||
|  |             texture_index = 1; | ||||||
|  |         else if (surface_source == Source::Texture2) | ||||||
|  |             texture_index = 2; | ||||||
|         else { |         else { | ||||||
|             qDebug() << "Unknown texture source " << static_cast<int>(surface_source); |             qDebug() << "Unknown texture source " << static_cast<int>(surface_source); | ||||||
|             break; |             break; | ||||||
|  | @ -547,8 +525,7 @@ void GraphicsSurfaceWidget::OnUpdate() | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     case Source::Custom: |     case Source::Custom: { | ||||||
|     { |  | ||||||
|         // Keep user-specified values
 |         // Keep user-specified values
 | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|  | @ -613,7 +590,8 @@ void GraphicsSurfaceWidget::OnUpdate() | ||||||
| 
 | 
 | ||||||
|     } else { |     } else { | ||||||
| 
 | 
 | ||||||
|         ASSERT_MSG(nibbles_per_pixel >= 2, "Depth decoder only supports formats with at least one byte per pixel"); |         ASSERT_MSG(nibbles_per_pixel >= 2, | ||||||
|  |                    "Depth decoder only supports formats with at least one byte per pixel"); | ||||||
|         unsigned bytes_per_pixel = nibbles_per_pixel / 2; |         unsigned bytes_per_pixel = nibbles_per_pixel / 2; | ||||||
| 
 | 
 | ||||||
|         for (unsigned int y = 0; y < surface_height; ++y) { |         for (unsigned int y = 0; y < surface_height; ++y) { | ||||||
|  | @ -621,34 +599,30 @@ void GraphicsSurfaceWidget::OnUpdate() | ||||||
|                 const u32 coarse_y = y & ~7; |                 const u32 coarse_y = y & ~7; | ||||||
|                 u32 offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride; |                 u32 offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride; | ||||||
|                 const u8* pixel = buffer + offset; |                 const u8* pixel = buffer + offset; | ||||||
|                 Math::Vec4<u8> color = { 0, 0, 0, 0 }; |                 Math::Vec4<u8> color = {0, 0, 0, 0}; | ||||||
| 
 | 
 | ||||||
|                 switch(surface_format) { |                 switch (surface_format) { | ||||||
|                 case Format::D16: |                 case Format::D16: { | ||||||
|                 { |  | ||||||
|                     u32 data = Color::DecodeD16(pixel); |                     u32 data = Color::DecodeD16(pixel); | ||||||
|                     color.r() = data & 0xFF; |                     color.r() = data & 0xFF; | ||||||
|                     color.g() = (data >> 8) & 0xFF; |                     color.g() = (data >> 8) & 0xFF; | ||||||
|                     break; |                     break; | ||||||
|                 } |                 } | ||||||
|                 case Format::D24: |                 case Format::D24: { | ||||||
|                 { |  | ||||||
|                     u32 data = Color::DecodeD24(pixel); |                     u32 data = Color::DecodeD24(pixel); | ||||||
|                     color.r() = data & 0xFF; |                     color.r() = data & 0xFF; | ||||||
|                     color.g() = (data >> 8) & 0xFF; |                     color.g() = (data >> 8) & 0xFF; | ||||||
|                     color.b() = (data >> 16) & 0xFF; |                     color.b() = (data >> 16) & 0xFF; | ||||||
|                     break; |                     break; | ||||||
|                 } |                 } | ||||||
|                 case Format::D24X8: |                 case Format::D24X8: { | ||||||
|                 { |  | ||||||
|                     Math::Vec2<u32> data = Color::DecodeD24S8(pixel); |                     Math::Vec2<u32> data = Color::DecodeD24S8(pixel); | ||||||
|                     color.r() = data.x & 0xFF; |                     color.r() = data.x & 0xFF; | ||||||
|                     color.g() = (data.x >> 8) & 0xFF; |                     color.g() = (data.x >> 8) & 0xFF; | ||||||
|                     color.b() = (data.x >> 16) & 0xFF; |                     color.b() = (data.x >> 16) & 0xFF; | ||||||
|                     break; |                     break; | ||||||
|                 } |                 } | ||||||
|                 case Format::X24S8: |                 case Format::X24S8: { | ||||||
|                 { |  | ||||||
|                     Math::Vec2<u32> data = Color::DecodeD24S8(pixel); |                     Math::Vec2<u32> data = Color::DecodeD24S8(pixel); | ||||||
|                     color.r() = color.g() = color.b() = data.y; |                     color.r() = color.g() = color.b() = data.y; | ||||||
|                     break; |                     break; | ||||||
|  | @ -661,7 +635,6 @@ void GraphicsSurfaceWidget::OnUpdate() | ||||||
|                 decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), 255)); |                 decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), 255)); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pixmap = QPixmap::fromImage(decoded_image); |     pixmap = QPixmap::fromImage(decoded_image); | ||||||
|  | @ -682,8 +655,10 @@ void GraphicsSurfaceWidget::SaveSurface() { | ||||||
|     QString bin_filter = tr("Binary data (*.bin)"); |     QString bin_filter = tr("Binary data (*.bin)"); | ||||||
| 
 | 
 | ||||||
|     QString selectedFilter; |     QString selectedFilter; | ||||||
|     QString filename = QFileDialog::getSaveFileName(this, tr("Save Surface"), QString("texture-0x%1.png").arg(QString::number(surface_address, 16)), |     QString filename = QFileDialog::getSaveFileName( | ||||||
|                                                     QString("%1;;%2").arg(png_filter, bin_filter), &selectedFilter); |         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 (filename.isEmpty()) { | ||||||
|         // If the user canceled the dialog, don't save anything.
 |         // If the user canceled the dialog, don't save anything.
 | ||||||
|  | @ -718,19 +693,18 @@ unsigned int GraphicsSurfaceWidget::NibblesPerPixel(GraphicsSurfaceWidget::Forma | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     switch (format) { |     switch (format) { | ||||||
|         case Format::D24X8: |     case Format::D24X8: | ||||||
|         case Format::X24S8: |     case Format::X24S8: | ||||||
|             return 4 * 2; |         return 4 * 2; | ||||||
|         case Format::D24: |     case Format::D24: | ||||||
|             return 3 * 2; |         return 3 * 2; | ||||||
|         case Format::D16: |     case Format::D16: | ||||||
|             return 2 * 2; |         return 2 * 2; | ||||||
|         default: |     default: | ||||||
|             UNREACHABLE_MSG("GraphicsSurfaceWidget::BytesPerPixel: this " |         UNREACHABLE_MSG("GraphicsSurfaceWidget::BytesPerPixel: this should not be reached as this " | ||||||
|                             "should not be reached as this function should " |                         "function should be given a format which is in " | ||||||
|                             "be given a format which is in " |                         "GraphicsSurfaceWidget::Format. Instead got %i", | ||||||
|                             "GraphicsSurfaceWidget::Format. Instead got %i", |                         static_cast<int>(format)); | ||||||
|                             static_cast<int>(format)); |         return 0; | ||||||
|             return 0; |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -4,10 +4,9 @@ | ||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include "citra_qt/debugger/graphics_breakpoint_observer.h" |  | ||||||
| 
 |  | ||||||
| #include <QLabel> | #include <QLabel> | ||||||
| #include <QPushButton> | #include <QPushButton> | ||||||
|  | #include "citra_qt/debugger/graphics_breakpoint_observer.h" | ||||||
| 
 | 
 | ||||||
| class QComboBox; | class QComboBox; | ||||||
| class QSpinBox; | class QSpinBox; | ||||||
|  | @ -15,8 +14,7 @@ class CSpinBox; | ||||||
| 
 | 
 | ||||||
| class GraphicsSurfaceWidget; | class GraphicsSurfaceWidget; | ||||||
| 
 | 
 | ||||||
| class SurfacePicture : public QLabel | class SurfacePicture : public QLabel { | ||||||
| { |  | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|  | @ -29,7 +27,6 @@ protected slots: | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     GraphicsSurfaceWidget* surface_widget; |     GraphicsSurfaceWidget* surface_widget; | ||||||
| 
 |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class GraphicsSurfaceWidget : public BreakPointObserverDock { | class GraphicsSurfaceWidget : public BreakPointObserverDock { | ||||||
|  | @ -38,43 +35,44 @@ class GraphicsSurfaceWidget : public BreakPointObserverDock { | ||||||
|     using Event = Pica::DebugContext::Event; |     using Event = Pica::DebugContext::Event; | ||||||
| 
 | 
 | ||||||
|     enum class Source { |     enum class Source { | ||||||
|         ColorBuffer   = 0, |         ColorBuffer = 0, | ||||||
|         DepthBuffer   = 1, |         DepthBuffer = 1, | ||||||
|         StencilBuffer = 2, |         StencilBuffer = 2, | ||||||
|         Texture0      = 3, |         Texture0 = 3, | ||||||
|         Texture1      = 4, |         Texture1 = 4, | ||||||
|         Texture2      = 5, |         Texture2 = 5, | ||||||
|         Custom        = 6, |         Custom = 6, | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     enum class Format { |     enum class Format { | ||||||
|         // These must match the TextureFormat type!
 |         // These must match the TextureFormat type!
 | ||||||
|         RGBA8        =  0, |         RGBA8 = 0, | ||||||
|         RGB8         =  1, |         RGB8 = 1, | ||||||
|         RGB5A1       =  2, |         RGB5A1 = 2, | ||||||
|         RGB565       =  3, |         RGB565 = 3, | ||||||
|         RGBA4        =  4, |         RGBA4 = 4, | ||||||
|         IA8          =  5, |         IA8 = 5, | ||||||
|         RG8          =  6,  ///< @note Also called HILO8 in 3DBrew.
 |         RG8 = 6, ///< @note Also called HILO8 in 3DBrew.
 | ||||||
|         I8           =  7, |         I8 = 7, | ||||||
|         A8           =  8, |         A8 = 8, | ||||||
|         IA4          =  9, |         IA4 = 9, | ||||||
|         I4           = 10, |         I4 = 10, | ||||||
|         A4           = 11, |         A4 = 11, | ||||||
|         ETC1         = 12,  // compressed
 |         ETC1 = 12, // compressed
 | ||||||
|         ETC1A4       = 13, |         ETC1A4 = 13, | ||||||
|         MaxTextureFormat = 13, |         MaxTextureFormat = 13, | ||||||
|         D16          = 14, |         D16 = 14, | ||||||
|         D24          = 15, |         D24 = 15, | ||||||
|         D24X8        = 16, |         D24X8 = 16, | ||||||
|         X24S8        = 17, |         X24S8 = 17, | ||||||
|         Unknown      = 18, |         Unknown = 18, | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     static unsigned int NibblesPerPixel(Format format); |     static unsigned int NibblesPerPixel(Format format); | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|     GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent = nullptr); |     GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext> debug_context, | ||||||
|  |                           QWidget* parent = nullptr); | ||||||
|     void Pick(int x, int y); |     void Pick(int x, int y); | ||||||
| 
 | 
 | ||||||
| public slots: | public slots: | ||||||
|  | @ -97,7 +95,6 @@ signals: | ||||||
|     void Update(); |     void Update(); | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
| 
 |  | ||||||
|     QComboBox* surface_source_list; |     QComboBox* surface_source_list; | ||||||
|     CSpinBox* surface_address_control; |     CSpinBox* surface_address_control; | ||||||
|     QSpinBox* surface_width_control; |     QSpinBox* surface_width_control; | ||||||
|  |  | ||||||
|  | @ -6,25 +6,18 @@ | ||||||
| #include <array> | #include <array> | ||||||
| #include <iterator> | #include <iterator> | ||||||
| #include <memory> | #include <memory> | ||||||
| 
 |  | ||||||
| #include <boost/range/algorithm/copy.hpp> |  | ||||||
| 
 |  | ||||||
| #include <QBoxLayout> | #include <QBoxLayout> | ||||||
| #include <QComboBox> | #include <QComboBox> | ||||||
| #include <QFileDialog> | #include <QFileDialog> | ||||||
| #include <QMessageBox> | #include <QMessageBox> | ||||||
| #include <QPushButton> | #include <QPushButton> | ||||||
| 
 | #include <boost/range/algorithm/copy.hpp> | ||||||
| #include "citra_qt/debugger/graphics_tracing.h" | #include "citra_qt/debugger/graphics_tracing.h" | ||||||
| 
 |  | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| 
 |  | ||||||
| #include "core/hw/gpu.h" | #include "core/hw/gpu.h" | ||||||
| #include "core/hw/lcd.h" | #include "core/hw/lcd.h" | ||||||
| #include "core/tracer/recorder.h" | #include "core/tracer/recorder.h" | ||||||
| 
 |  | ||||||
| #include "nihstro/float24.h" | #include "nihstro/float24.h" | ||||||
| 
 |  | ||||||
| #include "video_core/pica.h" | #include "video_core/pica.h" | ||||||
| #include "video_core/pica_state.h" | #include "video_core/pica_state.h" | ||||||
| 
 | 
 | ||||||
|  | @ -35,12 +28,16 @@ GraphicsTracingWidget::GraphicsTracingWidget(std::shared_ptr<Pica::DebugContext> | ||||||
|     setObjectName("CiTracing"); |     setObjectName("CiTracing"); | ||||||
| 
 | 
 | ||||||
|     QPushButton* start_recording = new QPushButton(tr("Start Recording")); |     QPushButton* start_recording = new QPushButton(tr("Start Recording")); | ||||||
|     QPushButton* stop_recording = new QPushButton(QIcon::fromTheme("document-save"), tr("Stop and Save")); |     QPushButton* stop_recording = | ||||||
|  |         new QPushButton(QIcon::fromTheme("document-save"), tr("Stop and Save")); | ||||||
|     QPushButton* abort_recording = new QPushButton(tr("Abort Recording")); |     QPushButton* abort_recording = new QPushButton(tr("Abort Recording")); | ||||||
| 
 | 
 | ||||||
|     connect(this, SIGNAL(SetStartTracingButtonEnabled(bool)), start_recording, SLOT(setVisible(bool))); |     connect(this, SIGNAL(SetStartTracingButtonEnabled(bool)), start_recording, | ||||||
|     connect(this, SIGNAL(SetStopTracingButtonEnabled(bool)), stop_recording, SLOT(setVisible(bool))); |             SLOT(setVisible(bool))); | ||||||
|     connect(this, SIGNAL(SetAbortTracingButtonEnabled(bool)), abort_recording, SLOT(setVisible(bool))); |     connect(this, SIGNAL(SetStopTracingButtonEnabled(bool)), stop_recording, | ||||||
|  |             SLOT(setVisible(bool))); | ||||||
|  |     connect(this, SIGNAL(SetAbortTracingButtonEnabled(bool)), abort_recording, | ||||||
|  |             SLOT(setVisible(bool))); | ||||||
|     connect(start_recording, SIGNAL(clicked()), this, SLOT(StartRecording())); |     connect(start_recording, SIGNAL(clicked()), this, SLOT(StartRecording())); | ||||||
|     connect(stop_recording, SIGNAL(clicked()), this, SLOT(StopRecording())); |     connect(stop_recording, SIGNAL(clicked()), this, SLOT(StopRecording())); | ||||||
|     connect(abort_recording, SIGNAL(clicked()), this, SLOT(AbortRecording())); |     connect(abort_recording, SIGNAL(clicked()), this, SLOT(AbortRecording())); | ||||||
|  | @ -74,26 +71,31 @@ void GraphicsTracingWidget::StartRecording() { | ||||||
|     std::array<u32, 4 * 16> default_attributes; |     std::array<u32, 4 * 16> default_attributes; | ||||||
|     for (unsigned i = 0; i < 16; ++i) { |     for (unsigned i = 0; i < 16; ++i) { | ||||||
|         for (unsigned comp = 0; comp < 3; ++comp) { |         for (unsigned comp = 0; comp < 3; ++comp) { | ||||||
|             default_attributes[4 * i + comp] = nihstro::to_float24(Pica::g_state.vs_default_attributes[i][comp].ToFloat32()); |             default_attributes[4 * i + comp] = | ||||||
|  |                 nihstro::to_float24(Pica::g_state.vs_default_attributes[i][comp].ToFloat32()); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     std::array<u32, 4 * 96> vs_float_uniforms; |     std::array<u32, 4 * 96> vs_float_uniforms; | ||||||
|     for (unsigned i = 0; i < 96; ++i) |     for (unsigned i = 0; i < 96; ++i) | ||||||
|         for (unsigned comp = 0; comp < 3; ++comp) |         for (unsigned comp = 0; comp < 3; ++comp) | ||||||
|             vs_float_uniforms[4 * i + comp] = nihstro::to_float24(Pica::g_state.vs.uniforms.f[i][comp].ToFloat32()); |             vs_float_uniforms[4 * i + comp] = | ||||||
|  |                 nihstro::to_float24(Pica::g_state.vs.uniforms.f[i][comp].ToFloat32()); | ||||||
| 
 | 
 | ||||||
|     CiTrace::Recorder::InitialState state; |     CiTrace::Recorder::InitialState state; | ||||||
|     std::copy_n((u32*)&GPU::g_regs, sizeof(GPU::g_regs) / sizeof(u32), std::back_inserter(state.gpu_registers)); |     std::copy_n((u32*)&GPU::g_regs, sizeof(GPU::g_regs) / sizeof(u32), | ||||||
|     std::copy_n((u32*)&LCD::g_regs, sizeof(LCD::g_regs) / sizeof(u32), std::back_inserter(state.lcd_registers)); |                 std::back_inserter(state.gpu_registers)); | ||||||
|     std::copy_n((u32*)&Pica::g_state.regs, sizeof(Pica::g_state.regs) / sizeof(u32), std::back_inserter(state.pica_registers)); |     std::copy_n((u32*)&LCD::g_regs, sizeof(LCD::g_regs) / sizeof(u32), | ||||||
|  |                 std::back_inserter(state.lcd_registers)); | ||||||
|  |     std::copy_n((u32*)&Pica::g_state.regs, sizeof(Pica::g_state.regs) / sizeof(u32), | ||||||
|  |                 std::back_inserter(state.pica_registers)); | ||||||
|     boost::copy(default_attributes, std::back_inserter(state.default_attributes)); |     boost::copy(default_attributes, std::back_inserter(state.default_attributes)); | ||||||
|     boost::copy(shader_binary, std::back_inserter(state.vs_program_binary)); |     boost::copy(shader_binary, std::back_inserter(state.vs_program_binary)); | ||||||
|     boost::copy(swizzle_data, std::back_inserter(state.vs_swizzle_data)); |     boost::copy(swizzle_data, std::back_inserter(state.vs_swizzle_data)); | ||||||
|     boost::copy(vs_float_uniforms, std::back_inserter(state.vs_float_uniforms)); |     boost::copy(vs_float_uniforms, std::back_inserter(state.vs_float_uniforms)); | ||||||
|     //boost::copy(TODO: Not implemented, std::back_inserter(state.gs_program_binary));
 |     // boost::copy(TODO: Not implemented, std::back_inserter(state.gs_program_binary));
 | ||||||
|     //boost::copy(TODO: Not implemented, std::back_inserter(state.gs_swizzle_data));
 |     // boost::copy(TODO: Not implemented, std::back_inserter(state.gs_swizzle_data));
 | ||||||
|     //boost::copy(TODO: Not implemented, std::back_inserter(state.gs_float_uniforms));
 |     // boost::copy(TODO: Not implemented, std::back_inserter(state.gs_float_uniforms));
 | ||||||
| 
 | 
 | ||||||
|     auto recorder = new CiTrace::Recorder(state); |     auto recorder = new CiTrace::Recorder(state); | ||||||
|     context->recorder = std::shared_ptr<CiTrace::Recorder>(recorder); |     context->recorder = std::shared_ptr<CiTrace::Recorder>(recorder); | ||||||
|  | @ -156,11 +158,12 @@ void GraphicsTracingWidget::OnEmulationStopping() { | ||||||
|     if (!context) |     if (!context) | ||||||
|         return; |         return; | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     if (context->recorder) { |     if (context->recorder) { | ||||||
|         auto reply = QMessageBox::question(this, tr("CiTracing still active"), |         auto reply = | ||||||
|                 tr("A CiTrace is still being recorded. Do you want to save it? If not, all recorded data will be discarded."), |             QMessageBox::question(this, tr("CiTracing still active"), | ||||||
|                 QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); |                                   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) { |         if (reply == QMessageBox::Yes) { | ||||||
|             StopRecording(); |             StopRecording(); | ||||||
|  |  | ||||||
|  | @ -12,7 +12,8 @@ class GraphicsTracingWidget : public BreakPointObserverDock { | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|     GraphicsTracingWidget(std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent = nullptr); |     GraphicsTracingWidget(std::shared_ptr<Pica::DebugContext> debug_context, | ||||||
|  |                           QWidget* parent = nullptr); | ||||||
| 
 | 
 | ||||||
| private slots: | private slots: | ||||||
|     void StartRecording(); |     void StartRecording(); | ||||||
|  |  | ||||||
|  | @ -4,7 +4,6 @@ | ||||||
| 
 | 
 | ||||||
| #include <iomanip> | #include <iomanip> | ||||||
| #include <sstream> | #include <sstream> | ||||||
| 
 |  | ||||||
| #include <QBoxLayout> | #include <QBoxLayout> | ||||||
| #include <QFileDialog> | #include <QFileDialog> | ||||||
| #include <QFormLayout> | #include <QFormLayout> | ||||||
|  | @ -15,10 +14,8 @@ | ||||||
| #include <QSignalMapper> | #include <QSignalMapper> | ||||||
| #include <QSpinBox> | #include <QSpinBox> | ||||||
| #include <QTreeView> | #include <QTreeView> | ||||||
| 
 |  | ||||||
| #include "citra_qt/debugger/graphics_vertex_shader.h" | #include "citra_qt/debugger/graphics_vertex_shader.h" | ||||||
| #include "citra_qt/util/util.h" | #include "citra_qt/util/util.h" | ||||||
| 
 |  | ||||||
| #include "video_core/pica.h" | #include "video_core/pica.h" | ||||||
| #include "video_core/pica_state.h" | #include "video_core/pica_state.h" | ||||||
| #include "video_core/shader/shader.h" | #include "video_core/shader/shader.h" | ||||||
|  | @ -28,9 +25,8 @@ using nihstro::Instruction; | ||||||
| using nihstro::SourceRegister; | using nihstro::SourceRegister; | ||||||
| using nihstro::SwizzlePattern; | using nihstro::SwizzlePattern; | ||||||
| 
 | 
 | ||||||
| GraphicsVertexShaderModel::GraphicsVertexShaderModel(GraphicsVertexShaderWidget* parent): QAbstractTableModel(parent), par(parent) { | GraphicsVertexShaderModel::GraphicsVertexShaderModel(GraphicsVertexShaderWidget* parent) | ||||||
| 
 |     : QAbstractTableModel(parent), par(parent) {} | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| int GraphicsVertexShaderModel::columnCount(const QModelIndex& parent) const { | int GraphicsVertexShaderModel::columnCount(const QModelIndex& parent) const { | ||||||
|     return 3; |     return 3; | ||||||
|  | @ -40,10 +36,10 @@ int GraphicsVertexShaderModel::rowCount(const QModelIndex& parent) const { | ||||||
|     return static_cast<int>(par->info.code.size()); |     return static_cast<int>(par->info.code.size()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| QVariant GraphicsVertexShaderModel::headerData(int section, Qt::Orientation orientation, int role) const { | QVariant GraphicsVertexShaderModel::headerData(int section, Qt::Orientation orientation, | ||||||
|     switch(role) { |                                                int role) const { | ||||||
|     case Qt::DisplayRole: |     switch (role) { | ||||||
|     { |     case Qt::DisplayRole: { | ||||||
|         if (section == 0) { |         if (section == 0) { | ||||||
|             return tr("Offset"); |             return tr("Offset"); | ||||||
|         } else if (section == 1) { |         } else if (section == 1) { | ||||||
|  | @ -69,8 +65,8 @@ static std::string SelectorToString(u32 selector) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // e.g. "-c92[a0.x].xyzw"
 | // e.g. "-c92[a0.x].xyzw"
 | ||||||
| static void print_input(std::ostringstream& output, const SourceRegister& input, | static void print_input(std::ostringstream& output, const SourceRegister& input, bool negate, | ||||||
|                         bool negate, const std::string& swizzle_mask, bool align = true, |                         const std::string& swizzle_mask, bool align = true, | ||||||
|                         const std::string& address_register_name = std::string()) { |                         const std::string& address_register_name = std::string()) { | ||||||
|     if (align) |     if (align) | ||||||
|         output << std::setw(4) << std::right; |         output << std::setw(4) << std::right; | ||||||
|  | @ -83,20 +79,18 @@ static void print_input(std::ostringstream& output, const SourceRegister& input, | ||||||
| 
 | 
 | ||||||
| QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) const { | QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) const { | ||||||
|     switch (role) { |     switch (role) { | ||||||
|     case Qt::DisplayRole: |     case Qt::DisplayRole: { | ||||||
|     { |  | ||||||
|         switch (index.column()) { |         switch (index.column()) { | ||||||
|         case 0: |         case 0: | ||||||
|             if (par->info.HasLabel(index.row())) |             if (par->info.HasLabel(index.row())) | ||||||
|                 return QString::fromStdString(par->info.GetLabel(index.row())); |                 return QString::fromStdString(par->info.GetLabel(index.row())); | ||||||
| 
 | 
 | ||||||
|             return QString("%1").arg(4*index.row(), 4, 16, QLatin1Char('0')); |             return QString("%1").arg(4 * index.row(), 4, 16, QLatin1Char('0')); | ||||||
| 
 | 
 | ||||||
|         case 1: |         case 1: | ||||||
|             return QString("%1").arg(par->info.code[index.row()].hex, 8, 16, QLatin1Char('0')); |             return QString("%1").arg(par->info.code[index.row()].hex, 8, 16, QLatin1Char('0')); | ||||||
| 
 | 
 | ||||||
|         case 2: |         case 2: { | ||||||
|         { |  | ||||||
|             std::ostringstream output; |             std::ostringstream output; | ||||||
|             output.flags(std::ios::uppercase); |             output.flags(std::ios::uppercase); | ||||||
| 
 | 
 | ||||||
|  | @ -117,8 +111,9 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con | ||||||
|             const Instruction instr = par->info.code[index.row()]; |             const Instruction instr = par->info.code[index.row()]; | ||||||
|             const OpCode opcode = instr.opcode; |             const OpCode opcode = instr.opcode; | ||||||
|             const OpCode::Info opcode_info = opcode.GetInfo(); |             const OpCode::Info opcode_info = opcode.GetInfo(); | ||||||
|             const u32 operand_desc_id = opcode_info.type == OpCode::Type::MultiplyAdd ? |             const u32 operand_desc_id = opcode_info.type == OpCode::Type::MultiplyAdd | ||||||
|                 instr.mad.operand_desc_id.Value() : instr.common.operand_desc_id.Value(); |                                             ? instr.mad.operand_desc_id.Value() | ||||||
|  |                                             : instr.common.operand_desc_id.Value(); | ||||||
|             const SwizzlePattern swizzle = par->info.swizzle_info[operand_desc_id].pattern; |             const SwizzlePattern swizzle = par->info.swizzle_info[operand_desc_id].pattern; | ||||||
| 
 | 
 | ||||||
|             // longest known instruction name: "setemit "
 |             // longest known instruction name: "setemit "
 | ||||||
|  | @ -136,15 +131,14 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con | ||||||
|                 break; |                 break; | ||||||
| 
 | 
 | ||||||
|             case OpCode::Type::Arithmetic: |             case OpCode::Type::Arithmetic: | ||||||
|             case OpCode::Type::MultiplyAdd: |             case OpCode::Type::MultiplyAdd: { | ||||||
|             { |  | ||||||
|                 // Use custom code for special instructions
 |                 // Use custom code for special instructions
 | ||||||
|                 switch (opcode.EffectiveOpCode()) { |                 switch (opcode.EffectiveOpCode()) { | ||||||
|                 case OpCode::Id::CMP: |                 case OpCode::Id::CMP: { | ||||||
|                 { |  | ||||||
|                     AlignToColumn(kOpcodeColumnWidth); |                     AlignToColumn(kOpcodeColumnWidth); | ||||||
| 
 | 
 | ||||||
|                     // NOTE: CMP always writes both cc components, so we do not consider the dest mask here.
 |                     // NOTE: CMP always writes both cc components, so we do not consider the dest
 | ||||||
|  |                     // mask here.
 | ||||||
|                     output << " cc.xy"; |                     output << " cc.xy"; | ||||||
|                     AlignToColumn(kOutputColumnWidth); |                     AlignToColumn(kOutputColumnWidth); | ||||||
| 
 | 
 | ||||||
|  | @ -152,22 +146,29 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con | ||||||
|                     SourceRegister src2 = instr.common.GetSrc2(false); |                     SourceRegister src2 = instr.common.GetSrc2(false); | ||||||
| 
 | 
 | ||||||
|                     output << ' '; |                     output << ' '; | ||||||
|                     print_input(output, src1, swizzle.negate_src1, swizzle.SelectorToString(false).substr(0,1), false, instr.common.AddressRegisterName()); |                     print_input(output, src1, swizzle.negate_src1, | ||||||
|                     output << ' ' << instr.common.compare_op.ToString(instr.common.compare_op.x) << ' '; |                                 swizzle.SelectorToString(false).substr(0, 1), false, | ||||||
|                     print_input(output, src2, swizzle.negate_src2, swizzle.SelectorToString(true).substr(0,1), false); |                                 instr.common.AddressRegisterName()); | ||||||
|  |                     output << ' ' << instr.common.compare_op.ToString(instr.common.compare_op.x) | ||||||
|  |                            << ' '; | ||||||
|  |                     print_input(output, src2, swizzle.negate_src2, | ||||||
|  |                                 swizzle.SelectorToString(true).substr(0, 1), false); | ||||||
| 
 | 
 | ||||||
|                     output << ", "; |                     output << ", "; | ||||||
| 
 | 
 | ||||||
|                     print_input(output, src1, swizzle.negate_src1, swizzle.SelectorToString(false).substr(1,1), false, instr.common.AddressRegisterName()); |                     print_input(output, src1, swizzle.negate_src1, | ||||||
|                     output << ' ' << instr.common.compare_op.ToString(instr.common.compare_op.y) << ' '; |                                 swizzle.SelectorToString(false).substr(1, 1), false, | ||||||
|                     print_input(output, src2, swizzle.negate_src2, swizzle.SelectorToString(true).substr(1,1), false); |                                 instr.common.AddressRegisterName()); | ||||||
|  |                     output << ' ' << instr.common.compare_op.ToString(instr.common.compare_op.y) | ||||||
|  |                            << ' '; | ||||||
|  |                     print_input(output, src2, swizzle.negate_src2, | ||||||
|  |                                 swizzle.SelectorToString(true).substr(1, 1), false); | ||||||
| 
 | 
 | ||||||
|                     break; |                     break; | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 case OpCode::Id::MAD: |                 case OpCode::Id::MAD: | ||||||
|                 case OpCode::Id::MADI: |                 case OpCode::Id::MADI: { | ||||||
|                 { |  | ||||||
|                     AlignToColumn(kOpcodeColumnWidth); |                     AlignToColumn(kOpcodeColumnWidth); | ||||||
| 
 | 
 | ||||||
|                     bool src_is_inverted = 0 != (opcode_info.subtype & OpCode::Info::SrcInversed); |                     bool src_is_inverted = 0 != (opcode_info.subtype & OpCode::Info::SrcInversed); | ||||||
|  | @ -175,34 +176,42 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con | ||||||
|                     SourceRegister src2 = instr.mad.GetSrc2(src_is_inverted); |                     SourceRegister src2 = instr.mad.GetSrc2(src_is_inverted); | ||||||
|                     SourceRegister src3 = instr.mad.GetSrc3(src_is_inverted); |                     SourceRegister src3 = instr.mad.GetSrc3(src_is_inverted); | ||||||
| 
 | 
 | ||||||
|                     output << std::setw(3) << std::right << instr.mad.dest.Value().GetName() << '.' << swizzle.DestMaskToString(); |                     output << std::setw(3) << std::right << instr.mad.dest.Value().GetName() << '.' | ||||||
|  |                            << swizzle.DestMaskToString(); | ||||||
|                     AlignToColumn(kOutputColumnWidth); |                     AlignToColumn(kOutputColumnWidth); | ||||||
|                     print_input(output, src1, swizzle.negate_src1, SelectorToString(swizzle.src1_selector)); |                     print_input(output, src1, swizzle.negate_src1, | ||||||
|  |                                 SelectorToString(swizzle.src1_selector)); | ||||||
|                     AlignToColumn(kInputOperandColumnWidth); |                     AlignToColumn(kInputOperandColumnWidth); | ||||||
|                     if (src_is_inverted) { |                     if (src_is_inverted) { | ||||||
|                       print_input(output, src2, swizzle.negate_src2, SelectorToString(swizzle.src2_selector)); |                         print_input(output, src2, swizzle.negate_src2, | ||||||
|  |                                     SelectorToString(swizzle.src2_selector)); | ||||||
|                     } else { |                     } else { | ||||||
|                       print_input(output, src2, swizzle.negate_src2, SelectorToString(swizzle.src2_selector), true, instr.mad.AddressRegisterName()); |                         print_input(output, src2, swizzle.negate_src2, | ||||||
|  |                                     SelectorToString(swizzle.src2_selector), true, | ||||||
|  |                                     instr.mad.AddressRegisterName()); | ||||||
|                     } |                     } | ||||||
|                     AlignToColumn(kInputOperandColumnWidth); |                     AlignToColumn(kInputOperandColumnWidth); | ||||||
|                     if (src_is_inverted) { |                     if (src_is_inverted) { | ||||||
|                       print_input(output, src3, swizzle.negate_src3, SelectorToString(swizzle.src3_selector), true, instr.mad.AddressRegisterName()); |                         print_input(output, src3, swizzle.negate_src3, | ||||||
|  |                                     SelectorToString(swizzle.src3_selector), true, | ||||||
|  |                                     instr.mad.AddressRegisterName()); | ||||||
|                     } else { |                     } else { | ||||||
|                       print_input(output, src3, swizzle.negate_src3, SelectorToString(swizzle.src3_selector)); |                         print_input(output, src3, swizzle.negate_src3, | ||||||
|  |                                     SelectorToString(swizzle.src3_selector)); | ||||||
|                     } |                     } | ||||||
|                     AlignToColumn(kInputOperandColumnWidth); |                     AlignToColumn(kInputOperandColumnWidth); | ||||||
|                     break; |                     break; | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 default: |                 default: { | ||||||
|                 { |  | ||||||
|                     AlignToColumn(kOpcodeColumnWidth); |                     AlignToColumn(kOpcodeColumnWidth); | ||||||
| 
 | 
 | ||||||
|                     bool src_is_inverted = 0 != (opcode_info.subtype & OpCode::Info::SrcInversed); |                     bool src_is_inverted = 0 != (opcode_info.subtype & OpCode::Info::SrcInversed); | ||||||
| 
 | 
 | ||||||
|                     if (opcode_info.subtype & OpCode::Info::Dest) { |                     if (opcode_info.subtype & OpCode::Info::Dest) { | ||||||
|                         // e.g. "r12.xy__"
 |                         // e.g. "r12.xy__"
 | ||||||
|                         output << std::setw(3) << std::right << instr.common.dest.Value().GetName() << '.' << swizzle.DestMaskToString(); |                         output << std::setw(3) << std::right << instr.common.dest.Value().GetName() | ||||||
|  |                                << '.' << swizzle.DestMaskToString(); | ||||||
|                     } else if (opcode_info.subtype == OpCode::Info::MOVA) { |                     } else if (opcode_info.subtype == OpCode::Info::MOVA) { | ||||||
|                         output << "  a0." << swizzle.DestMaskToString(); |                         output << "  a0." << swizzle.DestMaskToString(); | ||||||
|                     } |                     } | ||||||
|  | @ -210,14 +219,18 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con | ||||||
| 
 | 
 | ||||||
|                     if (opcode_info.subtype & OpCode::Info::Src1) { |                     if (opcode_info.subtype & OpCode::Info::Src1) { | ||||||
|                         SourceRegister src1 = instr.common.GetSrc1(src_is_inverted); |                         SourceRegister src1 = instr.common.GetSrc1(src_is_inverted); | ||||||
|                         print_input(output, src1, swizzle.negate_src1, swizzle.SelectorToString(false), true, instr.common.AddressRegisterName()); |                         print_input(output, src1, swizzle.negate_src1, | ||||||
|  |                                     swizzle.SelectorToString(false), true, | ||||||
|  |                                     instr.common.AddressRegisterName()); | ||||||
|                         AlignToColumn(kInputOperandColumnWidth); |                         AlignToColumn(kInputOperandColumnWidth); | ||||||
|                     } |                     } | ||||||
| 
 | 
 | ||||||
|                     // TODO: In some cases, the Address Register is used as an index for SRC2 instead of SRC1
 |                     // TODO: In some cases, the Address Register is used as an index for SRC2
 | ||||||
|  |                     // instead of SRC1
 | ||||||
|                     if (opcode_info.subtype & OpCode::Info::Src2) { |                     if (opcode_info.subtype & OpCode::Info::Src2) { | ||||||
|                         SourceRegister src2 = instr.common.GetSrc2(src_is_inverted); |                         SourceRegister src2 = instr.common.GetSrc2(src_is_inverted); | ||||||
|                         print_input(output, src2, swizzle.negate_src2, swizzle.SelectorToString(true)); |                         print_input(output, src2, swizzle.negate_src2, | ||||||
|  |                                     swizzle.SelectorToString(true)); | ||||||
|                         AlignToColumn(kInputOperandColumnWidth); |                         AlignToColumn(kInputOperandColumnWidth); | ||||||
|                     } |                     } | ||||||
|                     break; |                     break; | ||||||
|  | @ -228,8 +241,7 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             case OpCode::Type::Conditional: |             case OpCode::Type::Conditional: | ||||||
|             case OpCode::Type::UniformFlowControl: |             case OpCode::Type::UniformFlowControl: { | ||||||
|             { |  | ||||||
|                 output << ' '; |                 output << ' '; | ||||||
| 
 | 
 | ||||||
|                 switch (opcode.EffectiveOpCode()) { |                 switch (opcode.EffectiveOpCode()) { | ||||||
|  | @ -242,7 +254,8 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con | ||||||
|                         output << '('; |                         output << '('; | ||||||
| 
 | 
 | ||||||
|                         if (instr.flow_control.op != instr.flow_control.JustY) { |                         if (instr.flow_control.op != instr.flow_control.JustY) { | ||||||
|                             if (instr.flow_control.refx) output << '!'; |                             if (instr.flow_control.refx) | ||||||
|  |                                 output << '!'; | ||||||
|                             output << "cc.x"; |                             output << "cc.x"; | ||||||
|                         } |                         } | ||||||
| 
 | 
 | ||||||
|  | @ -253,7 +266,8 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con | ||||||
|                         } |                         } | ||||||
| 
 | 
 | ||||||
|                         if (instr.flow_control.op != instr.flow_control.JustX) { |                         if (instr.flow_control.op != instr.flow_control.JustX) { | ||||||
|                             if (instr.flow_control.refy) output << '!'; |                             if (instr.flow_control.refy) | ||||||
|  |                                 output << '!'; | ||||||
|                             output << "cc.y"; |                             output << "cc.y"; | ||||||
|                         } |                         } | ||||||
| 
 | 
 | ||||||
|  | @ -266,17 +280,23 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con | ||||||
|                     u32 target_addr_else = instr.flow_control.dest_offset; |                     u32 target_addr_else = instr.flow_control.dest_offset; | ||||||
| 
 | 
 | ||||||
|                     if (opcode_info.subtype & OpCode::Info::HasAlternative) { |                     if (opcode_info.subtype & OpCode::Info::HasAlternative) { | ||||||
|                         output << "else jump to 0x" << std::setw(4) << std::right << std::setfill('0') << std::hex << (4 * instr.flow_control.dest_offset); |                         output << "else jump to 0x" << std::setw(4) << std::right | ||||||
|  |                                << std::setfill('0') << std::hex | ||||||
|  |                                << (4 * instr.flow_control.dest_offset); | ||||||
|                     } else if (opcode_info.subtype & OpCode::Info::HasExplicitDest) { |                     } else if (opcode_info.subtype & OpCode::Info::HasExplicitDest) { | ||||||
|                         output << "jump to 0x" << std::setw(4) << std::right << std::setfill('0') << std::hex << (4 * instr.flow_control.dest_offset); |                         output << "jump to 0x" << std::setw(4) << std::right << std::setfill('0') | ||||||
|  |                                << std::hex << (4 * instr.flow_control.dest_offset); | ||||||
|                     } else { |                     } else { | ||||||
|                         // TODO: Handle other cases
 |                         // TODO: Handle other cases
 | ||||||
|                         output << "(unknown destination)"; |                         output << "(unknown destination)"; | ||||||
|                     } |                     } | ||||||
| 
 | 
 | ||||||
|                     if (opcode_info.subtype & OpCode::Info::HasFinishPoint) { |                     if (opcode_info.subtype & OpCode::Info::HasFinishPoint) { | ||||||
|                         output << " (return on 0x" << std::setw(4) << std::right << std::setfill('0') << std::hex |                         output << " (return on 0x" << std::setw(4) << std::right | ||||||
|                                << (4 * instr.flow_control.dest_offset + 4 * instr.flow_control.num_instructions) << ')'; |                                << std::setfill('0') << std::hex | ||||||
|  |                                << (4 * instr.flow_control.dest_offset + | ||||||
|  |                                    4 * instr.flow_control.num_instructions) | ||||||
|  |                                << ')'; | ||||||
|                     } |                     } | ||||||
| 
 | 
 | ||||||
|                     break; |                     break; | ||||||
|  | @ -300,8 +320,7 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con | ||||||
|     case Qt::FontRole: |     case Qt::FontRole: | ||||||
|         return GetMonospaceFont(); |         return GetMonospaceFont(); | ||||||
| 
 | 
 | ||||||
|     case Qt::BackgroundRole: |     case Qt::BackgroundRole: { | ||||||
|     { |  | ||||||
|         // Highlight current instruction
 |         // Highlight current instruction
 | ||||||
|         int current_record_index = par->cycle_index->value(); |         int current_record_index = par->cycle_index->value(); | ||||||
|         if (current_record_index < static_cast<int>(par->debug_data.records.size())) { |         if (current_record_index < static_cast<int>(par->debug_data.records.size())) { | ||||||
|  | @ -319,10 +338,8 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con | ||||||
|         return QBrush(QColor(192, 192, 192)); |         return QBrush(QColor(192, 192, 192)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     // TODO: Draw arrows for each "reachable" instruction to visualize control flow
 |     // TODO: Draw arrows for each "reachable" instruction to visualize control flow
 | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     default: |     default: | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|  | @ -331,23 +348,24 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GraphicsVertexShaderWidget::DumpShader() { | void GraphicsVertexShaderWidget::DumpShader() { | ||||||
|     QString filename = QFileDialog::getSaveFileName(this, tr("Save Shader Dump"), "shader_dump.shbin", |     QString filename = QFileDialog::getSaveFileName( | ||||||
|                                                     tr("Shader Binary (*.shbin)")); |         this, tr("Save Shader Dump"), "shader_dump.shbin", tr("Shader Binary (*.shbin)")); | ||||||
| 
 | 
 | ||||||
|     if (filename.isEmpty()) { |     if (filename.isEmpty()) { | ||||||
|         // If the user canceled the dialog, don't dump anything.
 |         // If the user canceled the dialog, don't dump anything.
 | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     auto& setup  = Pica::g_state.vs; |     auto& setup = Pica::g_state.vs; | ||||||
|     auto& config = Pica::g_state.regs.vs; |     auto& config = Pica::g_state.regs.vs; | ||||||
| 
 | 
 | ||||||
|     Pica::DebugUtils::DumpShader(filename.toStdString(), config, setup, Pica::g_state.regs.vs_output_attributes); |     Pica::DebugUtils::DumpShader(filename.toStdString(), config, setup, | ||||||
|  |                                  Pica::g_state.regs.vs_output_attributes); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| GraphicsVertexShaderWidget::GraphicsVertexShaderWidget(std::shared_ptr< Pica::DebugContext > debug_context, | GraphicsVertexShaderWidget::GraphicsVertexShaderWidget( | ||||||
|                                                        QWidget* parent) |     std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent) | ||||||
|         : BreakPointObserverDock(debug_context, "Pica Vertex Shader", parent) { |     : BreakPointObserverDock(debug_context, "Pica Vertex Shader", parent) { | ||||||
|     setObjectName("PicaVertexShader"); |     setObjectName("PicaVertexShader"); | ||||||
| 
 | 
 | ||||||
|     // Clear input vertex data so that it contains valid float values in case a debug shader
 |     // 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])); |         input_data[i]->setValidator(new QDoubleValidator(input_data[i])); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     breakpoint_warning = new QLabel(tr("(data only available at vertex shader invocation breakpoints)")); |     breakpoint_warning = | ||||||
|  |         new QLabel(tr("(data only available at vertex shader invocation breakpoints)")); | ||||||
| 
 | 
 | ||||||
|     // TODO: Add some button for jumping to the shader entry point
 |     // TODO: Add some button for jumping to the shader entry point
 | ||||||
| 
 | 
 | ||||||
|  | @ -442,7 +461,8 @@ GraphicsVertexShaderWidget::GraphicsVertexShaderWidget(std::shared_ptr< Pica::De | ||||||
| 
 | 
 | ||||||
|     // Set a minimum height so that the size of this label doesn't cause the rest of the bottom
 |     // Set a minimum height so that the size of this label doesn't cause the rest of the bottom
 | ||||||
|     // part of the UI to keep jumping up and down when cycling through instructions.
 |     // part of the UI to keep jumping up and down when cycling through instructions.
 | ||||||
|     instruction_description->setMinimumHeight(instruction_description->fontMetrics().lineSpacing() * 6); |     instruction_description->setMinimumHeight(instruction_description->fontMetrics().lineSpacing() * | ||||||
|  |                                               6); | ||||||
|     instruction_description->setAlignment(Qt::AlignLeft | Qt::AlignTop); |     instruction_description->setAlignment(Qt::AlignLeft | Qt::AlignTop); | ||||||
|     main_layout->addWidget(instruction_description); |     main_layout->addWidget(instruction_description); | ||||||
| 
 | 
 | ||||||
|  | @ -471,7 +491,8 @@ void GraphicsVertexShaderWidget::Reload(bool replace_vertex_data, void* vertex_d | ||||||
|             memcpy(&input_vertex, vertex_data, sizeof(input_vertex)); |             memcpy(&input_vertex, vertex_data, sizeof(input_vertex)); | ||||||
|             for (unsigned attr = 0; attr < 16; ++attr) { |             for (unsigned attr = 0; attr < 16; ++attr) { | ||||||
|                 for (unsigned comp = 0; comp < 4; ++comp) { |                 for (unsigned comp = 0; comp < 4; ++comp) { | ||||||
|                     input_data[4 * attr + comp]->setText(QString("%1").arg(input_vertex.attr[attr][comp].ToFloat32())); |                     input_data[4 * attr + comp]->setText( | ||||||
|  |                         QString("%1").arg(input_vertex.attr[attr][comp].ToFloat32())); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             breakpoint_warning->hide(); |             breakpoint_warning->hide(); | ||||||
|  | @ -498,10 +519,11 @@ void GraphicsVertexShaderWidget::Reload(bool replace_vertex_data, void* vertex_d | ||||||
|         info.swizzle_info.push_back({pattern}); |         info.swizzle_info.push_back({pattern}); | ||||||
| 
 | 
 | ||||||
|     u32 entry_point = Pica::g_state.regs.vs.main_offset; |     u32 entry_point = Pica::g_state.regs.vs.main_offset; | ||||||
|     info.labels.insert({ entry_point, "main" }); |     info.labels.insert({entry_point, "main"}); | ||||||
| 
 | 
 | ||||||
|     // Generate debug information
 |     // Generate debug information
 | ||||||
|     debug_data = Pica::g_state.vs.ProduceDebugInfo(input_vertex, num_attributes, shader_config, shader_setup); |     debug_data = Pica::g_state.vs.ProduceDebugInfo(input_vertex, num_attributes, shader_config, | ||||||
|  |                                                    shader_setup); | ||||||
| 
 | 
 | ||||||
|     // Reload widget state
 |     // Reload widget state
 | ||||||
|     for (int attr = 0; attr < num_attributes; ++attr) { |     for (int attr = 0; attr < num_attributes; ++attr) { | ||||||
|  | @ -537,29 +559,60 @@ void GraphicsVertexShaderWidget::OnCycleIndexChanged(int index) { | ||||||
| 
 | 
 | ||||||
|     auto& record = debug_data.records[index]; |     auto& record = debug_data.records[index]; | ||||||
|     if (record.mask & Pica::Shader::DebugDataRecord::SRC1) |     if (record.mask & Pica::Shader::DebugDataRecord::SRC1) | ||||||
|         text += tr("SRC1: %1, %2, %3, %4\n").arg(record.src1.x.ToFloat32()).arg(record.src1.y.ToFloat32()).arg(record.src1.z.ToFloat32()).arg(record.src1.w.ToFloat32()); |         text += tr("SRC1: %1, %2, %3, %4\n") | ||||||
|  |                     .arg(record.src1.x.ToFloat32()) | ||||||
|  |                     .arg(record.src1.y.ToFloat32()) | ||||||
|  |                     .arg(record.src1.z.ToFloat32()) | ||||||
|  |                     .arg(record.src1.w.ToFloat32()); | ||||||
|     if (record.mask & Pica::Shader::DebugDataRecord::SRC2) |     if (record.mask & Pica::Shader::DebugDataRecord::SRC2) | ||||||
|         text += tr("SRC2: %1, %2, %3, %4\n").arg(record.src2.x.ToFloat32()).arg(record.src2.y.ToFloat32()).arg(record.src2.z.ToFloat32()).arg(record.src2.w.ToFloat32()); |         text += tr("SRC2: %1, %2, %3, %4\n") | ||||||
|  |                     .arg(record.src2.x.ToFloat32()) | ||||||
|  |                     .arg(record.src2.y.ToFloat32()) | ||||||
|  |                     .arg(record.src2.z.ToFloat32()) | ||||||
|  |                     .arg(record.src2.w.ToFloat32()); | ||||||
|     if (record.mask & Pica::Shader::DebugDataRecord::SRC3) |     if (record.mask & Pica::Shader::DebugDataRecord::SRC3) | ||||||
|         text += tr("SRC3: %1, %2, %3, %4\n").arg(record.src3.x.ToFloat32()).arg(record.src3.y.ToFloat32()).arg(record.src3.z.ToFloat32()).arg(record.src3.w.ToFloat32()); |         text += tr("SRC3: %1, %2, %3, %4\n") | ||||||
|  |                     .arg(record.src3.x.ToFloat32()) | ||||||
|  |                     .arg(record.src3.y.ToFloat32()) | ||||||
|  |                     .arg(record.src3.z.ToFloat32()) | ||||||
|  |                     .arg(record.src3.w.ToFloat32()); | ||||||
|     if (record.mask & Pica::Shader::DebugDataRecord::DEST_IN) |     if (record.mask & Pica::Shader::DebugDataRecord::DEST_IN) | ||||||
|         text += tr("DEST_IN: %1, %2, %3, %4\n").arg(record.dest_in.x.ToFloat32()).arg(record.dest_in.y.ToFloat32()).arg(record.dest_in.z.ToFloat32()).arg(record.dest_in.w.ToFloat32()); |         text += tr("DEST_IN: %1, %2, %3, %4\n") | ||||||
|  |                     .arg(record.dest_in.x.ToFloat32()) | ||||||
|  |                     .arg(record.dest_in.y.ToFloat32()) | ||||||
|  |                     .arg(record.dest_in.z.ToFloat32()) | ||||||
|  |                     .arg(record.dest_in.w.ToFloat32()); | ||||||
|     if (record.mask & Pica::Shader::DebugDataRecord::DEST_OUT) |     if (record.mask & Pica::Shader::DebugDataRecord::DEST_OUT) | ||||||
|         text += tr("DEST_OUT: %1, %2, %3, %4\n").arg(record.dest_out.x.ToFloat32()).arg(record.dest_out.y.ToFloat32()).arg(record.dest_out.z.ToFloat32()).arg(record.dest_out.w.ToFloat32()); |         text += tr("DEST_OUT: %1, %2, %3, %4\n") | ||||||
|  |                     .arg(record.dest_out.x.ToFloat32()) | ||||||
|  |                     .arg(record.dest_out.y.ToFloat32()) | ||||||
|  |                     .arg(record.dest_out.z.ToFloat32()) | ||||||
|  |                     .arg(record.dest_out.w.ToFloat32()); | ||||||
| 
 | 
 | ||||||
|     if (record.mask & Pica::Shader::DebugDataRecord::ADDR_REG_OUT) |     if (record.mask & Pica::Shader::DebugDataRecord::ADDR_REG_OUT) | ||||||
|         text += tr("Addres Registers: %1, %2\n").arg(record.address_registers[0]).arg(record.address_registers[1]); |         text += tr("Addres Registers: %1, %2\n") | ||||||
|  |                     .arg(record.address_registers[0]) | ||||||
|  |                     .arg(record.address_registers[1]); | ||||||
|     if (record.mask & Pica::Shader::DebugDataRecord::CMP_RESULT) |     if (record.mask & Pica::Shader::DebugDataRecord::CMP_RESULT) | ||||||
|         text += tr("Compare Result: %1, %2\n").arg(record.conditional_code[0] ? "true" : "false").arg(record.conditional_code[1] ? "true" : "false"); |         text += tr("Compare Result: %1, %2\n") | ||||||
|  |                     .arg(record.conditional_code[0] ? "true" : "false") | ||||||
|  |                     .arg(record.conditional_code[1] ? "true" : "false"); | ||||||
| 
 | 
 | ||||||
|     if (record.mask & Pica::Shader::DebugDataRecord::COND_BOOL_IN) |     if (record.mask & Pica::Shader::DebugDataRecord::COND_BOOL_IN) | ||||||
|         text += tr("Static Condition: %1\n").arg(record.cond_bool ? "true" : "false"); |         text += tr("Static Condition: %1\n").arg(record.cond_bool ? "true" : "false"); | ||||||
|     if (record.mask & Pica::Shader::DebugDataRecord::COND_CMP_IN) |     if (record.mask & Pica::Shader::DebugDataRecord::COND_CMP_IN) | ||||||
|         text += tr("Dynamic Conditions: %1, %2\n").arg(record.cond_cmp[0] ? "true" : "false").arg(record.cond_cmp[1] ? "true" : "false"); |         text += tr("Dynamic Conditions: %1, %2\n") | ||||||
|  |                     .arg(record.cond_cmp[0] ? "true" : "false") | ||||||
|  |                     .arg(record.cond_cmp[1] ? "true" : "false"); | ||||||
|     if (record.mask & Pica::Shader::DebugDataRecord::LOOP_INT_IN) |     if (record.mask & Pica::Shader::DebugDataRecord::LOOP_INT_IN) | ||||||
|         text += tr("Loop Parameters: %1 (repeats), %2 (initializer), %3 (increment), %4\n").arg(record.loop_int.x).arg(record.loop_int.y).arg(record.loop_int.z).arg(record.loop_int.w); |         text += tr("Loop Parameters: %1 (repeats), %2 (initializer), %3 (increment), %4\n") | ||||||
|  |                     .arg(record.loop_int.x) | ||||||
|  |                     .arg(record.loop_int.y) | ||||||
|  |                     .arg(record.loop_int.z) | ||||||
|  |                     .arg(record.loop_int.w); | ||||||
| 
 | 
 | ||||||
|     text += tr("Instruction offset: 0x%1").arg(4 * record.instruction_offset, 4, 16, QLatin1Char('0')); |     text += | ||||||
|  |         tr("Instruction offset: 0x%1").arg(4 * record.instruction_offset, 4, 16, QLatin1Char('0')); | ||||||
|     if (record.mask & Pica::Shader::DebugDataRecord::NEXT_INSTR) { |     if (record.mask & Pica::Shader::DebugDataRecord::NEXT_INSTR) { | ||||||
|         text += tr(" -> 0x%2").arg(4 * record.next_instruction, 4, 16, QLatin1Char('0')); |         text += tr(" -> 0x%2").arg(4 * record.next_instruction, 4, 16, QLatin1Char('0')); | ||||||
|     } else { |     } else { | ||||||
|  | @ -570,6 +623,7 @@ void GraphicsVertexShaderWidget::OnCycleIndexChanged(int index) { | ||||||
| 
 | 
 | ||||||
|     // Emit model update notification and scroll to current instruction
 |     // Emit model update notification and scroll to current instruction
 | ||||||
|     QModelIndex instr_index = model->index(record.instruction_offset, 0); |     QModelIndex instr_index = model->index(record.instruction_offset, 0); | ||||||
|     emit model->dataChanged(instr_index, model->index(record.instruction_offset, model->columnCount())); |     emit model->dataChanged(instr_index, | ||||||
|  |                             model->index(record.instruction_offset, model->columnCount())); | ||||||
|     binary_list->scrollTo(instr_index, QAbstractItemView::EnsureVisible); |     binary_list->scrollTo(instr_index, QAbstractItemView::EnsureVisible); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -5,11 +5,9 @@ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <QAbstractTableModel> | #include <QAbstractTableModel> | ||||||
| 
 | #include <QTreeView> | ||||||
| #include "citra_qt/debugger/graphics_breakpoint_observer.h" | #include "citra_qt/debugger/graphics_breakpoint_observer.h" | ||||||
| 
 |  | ||||||
| #include "nihstro/parser_shbin.h" | #include "nihstro/parser_shbin.h" | ||||||
| 
 |  | ||||||
| #include "video_core/shader/shader.h" | #include "video_core/shader/shader.h" | ||||||
| 
 | 
 | ||||||
| class QLabel; | class QLabel; | ||||||
|  | @ -26,7 +24,8 @@ public: | ||||||
|     int columnCount(const QModelIndex& parent = QModelIndex()) const override; |     int columnCount(const QModelIndex& parent = QModelIndex()) const override; | ||||||
|     int rowCount(const QModelIndex& parent = QModelIndex()) const override; |     int rowCount(const QModelIndex& parent = QModelIndex()) const override; | ||||||
|     QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; |     QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; | ||||||
|     QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; |     QVariant headerData(int section, Qt::Orientation orientation, | ||||||
|  |                         int role = Qt::DisplayRole) const override; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     GraphicsVertexShaderWidget* par; |     GraphicsVertexShaderWidget* par; | ||||||
|  | @ -56,7 +55,9 @@ private slots: | ||||||
|     /**
 |     /**
 | ||||||
|      * Reload widget based on the current PICA200 state |      * Reload widget based on the current PICA200 state | ||||||
|      * @param replace_vertex_data If true, invalidate all current vertex data |      * @param replace_vertex_data If true, invalidate all current vertex data | ||||||
|      * @param vertex_data New vertex data to use, as passed to OnBreakPointHit. May be nullptr to specify that no valid vertex data can be retrieved currently. Only used if replace_vertex_data is true. |      * @param vertex_data New vertex data to use, as passed to OnBreakPointHit. May be nullptr to | ||||||
|  |      * specify that no valid vertex data can be retrieved currently. Only used if | ||||||
|  |      * replace_vertex_data is true. | ||||||
|      */ |      */ | ||||||
|     void Reload(bool replace_vertex_data = false, void* vertex_data = nullptr); |     void Reload(bool replace_vertex_data = false, void* vertex_data = nullptr); | ||||||
| 
 | 
 | ||||||
|  | @ -66,9 +67,12 @@ private: | ||||||
|     GraphicsVertexShaderModel* model; |     GraphicsVertexShaderModel* model; | ||||||
| 
 | 
 | ||||||
|     /// TODO: Move these into a single struct
 |     /// TODO: Move these into a single struct
 | ||||||
|     std::array<QLineEdit*, 4*16> input_data;  // A text box for each of the 4 components of up to 16 vertex attributes
 |     std::array<QLineEdit*, 4 * 16> | ||||||
|     std::array<QWidget*, 16> input_data_container; // QWidget containing the QLayout containing each vertex attribute
 |         input_data; // A text box for each of the 4 components of up to 16 vertex attributes
 | ||||||
|     std::array<QLabel*, 16> input_data_mapping; // A QLabel denoting the shader input attribute which the vertex attribute maps to
 |     std::array<QWidget*, 16> | ||||||
|  |         input_data_container; // QWidget containing the QLayout containing each vertex attribute
 | ||||||
|  |     std::array<QLabel*, 16> input_data_mapping; // A QLabel denoting the shader input attribute
 | ||||||
|  |                                                 // which the vertex attribute maps to
 | ||||||
| 
 | 
 | ||||||
|     // Text to be shown when input vertex data is not retrievable
 |     // Text to be shown when input vertex data is not retrievable
 | ||||||
|     QLabel* breakpoint_warning; |     QLabel* breakpoint_warning; | ||||||
|  |  | ||||||
|  | @ -5,10 +5,8 @@ | ||||||
| #include <QMouseEvent> | #include <QMouseEvent> | ||||||
| #include <QPainter> | #include <QPainter> | ||||||
| #include <QString> | #include <QString> | ||||||
| 
 |  | ||||||
| #include "citra_qt/debugger/profiler.h" | #include "citra_qt/debugger/profiler.h" | ||||||
| #include "citra_qt/util/util.h" | #include "citra_qt/util/util.h" | ||||||
| 
 |  | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "common/microprofile.h" | #include "common/microprofile.h" | ||||||
| #include "common/profiler_reporting.h" | #include "common/profiler_reporting.h" | ||||||
|  | @ -22,57 +20,58 @@ | ||||||
| 
 | 
 | ||||||
| using namespace Common::Profiling; | using namespace Common::Profiling; | ||||||
| 
 | 
 | ||||||
| static QVariant GetDataForColumn(int col, const AggregatedDuration& duration) | static QVariant GetDataForColumn(int col, const AggregatedDuration& duration) { | ||||||
| { |  | ||||||
|     static auto duration_to_float = [](Duration dur) -> float { |     static auto duration_to_float = [](Duration dur) -> float { | ||||||
|         using FloatMs = std::chrono::duration<float, std::chrono::milliseconds::period>; |         using FloatMs = std::chrono::duration<float, std::chrono::milliseconds::period>; | ||||||
|         return std::chrono::duration_cast<FloatMs>(dur).count(); |         return std::chrono::duration_cast<FloatMs>(dur).count(); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     switch (col) { |     switch (col) { | ||||||
|     case 1: return duration_to_float(duration.avg); |     case 1: | ||||||
|     case 2: return duration_to_float(duration.min); |         return duration_to_float(duration.avg); | ||||||
|     case 3: return duration_to_float(duration.max); |     case 2: | ||||||
|     default: return QVariant(); |         return duration_to_float(duration.min); | ||||||
|  |     case 3: | ||||||
|  |         return duration_to_float(duration.max); | ||||||
|  |     default: | ||||||
|  |         return QVariant(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ProfilerModel::ProfilerModel(QObject* parent) : QAbstractItemModel(parent) | ProfilerModel::ProfilerModel(QObject* parent) : QAbstractItemModel(parent) { | ||||||
| { |  | ||||||
|     updateProfilingInfo(); |     updateProfilingInfo(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| QVariant ProfilerModel::headerData(int section, Qt::Orientation orientation, int role) const | QVariant ProfilerModel::headerData(int section, Qt::Orientation orientation, int role) const { | ||||||
| { |  | ||||||
|     if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { |     if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { | ||||||
|         switch (section) { |         switch (section) { | ||||||
|         case 0: return tr("Category"); |         case 0: | ||||||
|         case 1: return tr("Avg"); |             return tr("Category"); | ||||||
|         case 2: return tr("Min"); |         case 1: | ||||||
|         case 3: return tr("Max"); |             return tr("Avg"); | ||||||
|  |         case 2: | ||||||
|  |             return tr("Min"); | ||||||
|  |         case 3: | ||||||
|  |             return tr("Max"); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return QVariant(); |     return QVariant(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| QModelIndex ProfilerModel::index(int row, int column, const QModelIndex& parent) const | QModelIndex ProfilerModel::index(int row, int column, const QModelIndex& parent) const { | ||||||
| { |  | ||||||
|     return createIndex(row, column); |     return createIndex(row, column); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| QModelIndex ProfilerModel::parent(const QModelIndex& child) const | QModelIndex ProfilerModel::parent(const QModelIndex& child) const { | ||||||
| { |  | ||||||
|     return QModelIndex(); |     return QModelIndex(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int ProfilerModel::columnCount(const QModelIndex& parent) const | int ProfilerModel::columnCount(const QModelIndex& parent) const { | ||||||
| { |  | ||||||
|     return 4; |     return 4; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int ProfilerModel::rowCount(const QModelIndex& parent) const | int ProfilerModel::rowCount(const QModelIndex& parent) const { | ||||||
| { |  | ||||||
|     if (parent.isValid()) { |     if (parent.isValid()) { | ||||||
|         return 0; |         return 0; | ||||||
|     } else { |     } else { | ||||||
|  | @ -80,8 +79,7 @@ int ProfilerModel::rowCount(const QModelIndex& parent) const | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| QVariant ProfilerModel::data(const QModelIndex& index, int role) const | QVariant ProfilerModel::data(const QModelIndex& index, int role) const { | ||||||
| { |  | ||||||
|     if (role == Qt::DisplayRole) { |     if (role == Qt::DisplayRole) { | ||||||
|         if (index.row() == 0) { |         if (index.row() == 0) { | ||||||
|             if (index.column() == 0) { |             if (index.column() == 0) { | ||||||
|  | @ -101,14 +99,12 @@ QVariant ProfilerModel::data(const QModelIndex& index, int role) const | ||||||
|     return QVariant(); |     return QVariant(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ProfilerModel::updateProfilingInfo() | void ProfilerModel::updateProfilingInfo() { | ||||||
| { |  | ||||||
|     results = GetTimingResultsAggregator()->GetAggregatedResults(); |     results = GetTimingResultsAggregator()->GetAggregatedResults(); | ||||||
|     emit dataChanged(createIndex(0, 1), createIndex(rowCount() - 1, 3)); |     emit dataChanged(createIndex(0, 1), createIndex(rowCount() - 1, 3)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ProfilerWidget::ProfilerWidget(QWidget* parent) : QDockWidget(parent) | ProfilerWidget::ProfilerWidget(QWidget* parent) : QDockWidget(parent) { | ||||||
| { |  | ||||||
|     ui.setupUi(this); |     ui.setupUi(this); | ||||||
| 
 | 
 | ||||||
|     model = new ProfilerModel(this); |     model = new ProfilerModel(this); | ||||||
|  | @ -118,8 +114,7 @@ ProfilerWidget::ProfilerWidget(QWidget* parent) : QDockWidget(parent) | ||||||
|     connect(&update_timer, SIGNAL(timeout()), model, SLOT(updateProfilingInfo())); |     connect(&update_timer, SIGNAL(timeout()), model, SLOT(updateProfilingInfo())); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ProfilerWidget::setProfilingInfoUpdateEnabled(bool enable) | void ProfilerWidget::setProfilingInfoUpdateEnabled(bool enable) { | ||||||
| { |  | ||||||
|     if (enable) { |     if (enable) { | ||||||
|         update_timer.start(100); |         update_timer.start(100); | ||||||
|         model->updateProfilingInfo(); |         model->updateProfilingInfo(); | ||||||
|  | @ -157,9 +152,7 @@ private: | ||||||
| 
 | 
 | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| MicroProfileDialog::MicroProfileDialog(QWidget* parent) | MicroProfileDialog::MicroProfileDialog(QWidget* parent) : QWidget(parent, Qt::Dialog) { | ||||||
|     : QWidget(parent, Qt::Dialog) |  | ||||||
| { |  | ||||||
|     setObjectName("MicroProfile"); |     setObjectName("MicroProfile"); | ||||||
|     setWindowTitle(tr("MicroProfile")); |     setWindowTitle(tr("MicroProfile")); | ||||||
|     resize(1000, 600); |     resize(1000, 600); | ||||||
|  | @ -175,7 +168,8 @@ MicroProfileDialog::MicroProfileDialog(QWidget* parent) | ||||||
|     layout->addWidget(widget); |     layout->addWidget(widget); | ||||||
|     setLayout(layout); |     setLayout(layout); | ||||||
| 
 | 
 | ||||||
|     // Configure focus so that widget is focusable and the dialog automatically forwards focus to it.
 |     // Configure focus so that widget is focusable and the dialog automatically forwards focus to
 | ||||||
|  |     // it.
 | ||||||
|     setFocusProxy(widget); |     setFocusProxy(widget); | ||||||
|     widget->setFocusPolicy(Qt::StrongFocus); |     widget->setFocusPolicy(Qt::StrongFocus); | ||||||
|     widget->setFocus(); |     widget->setFocus(); | ||||||
|  | @ -207,7 +201,6 @@ void MicroProfileDialog::hideEvent(QHideEvent* ev) { | ||||||
|     QWidget::hideEvent(ev); |     QWidget::hideEvent(ev); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| #if MICROPROFILE_ENABLED | #if MICROPROFILE_ENABLED | ||||||
| 
 | 
 | ||||||
| /// There's no way to pass a user pointer to MicroProfile, so this variable is used to make the
 | /// There's no way to pass a user pointer to MicroProfile, so this variable is used to make the
 | ||||||
|  | @ -308,7 +301,8 @@ void MicroProfileDrawText(int x, int y, u32 hex_color, const char* text, u32 tex | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void MicroProfileDrawBox(int left, int top, int right, int bottom, u32 hex_color, MicroProfileBoxType type) { | void MicroProfileDrawBox(int left, int top, int right, int bottom, u32 hex_color, | ||||||
|  |                          MicroProfileBoxType type) { | ||||||
|     QColor color = QColor::fromRgba(hex_color); |     QColor color = QColor::fromRgba(hex_color); | ||||||
|     QBrush brush = color; |     QBrush brush = color; | ||||||
|     if (type == MicroProfileBoxTypeBar) { |     if (type == MicroProfileBoxTypeBar) { | ||||||
|  | @ -326,7 +320,7 @@ void MicroProfileDrawLine2D(u32 vertices_length, float* vertices, u32 hex_color) | ||||||
|     static std::vector<QPointF> point_buf; |     static std::vector<QPointF> point_buf; | ||||||
| 
 | 
 | ||||||
|     for (u32 i = 0; i < vertices_length; ++i) { |     for (u32 i = 0; i < vertices_length; ++i) { | ||||||
|         point_buf.emplace_back(vertices[i*2 + 0], vertices[i*2 + 1]); |         point_buf.emplace_back(vertices[i * 2 + 0], vertices[i * 2 + 1]); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // hex_color does not include an alpha, so it must be assumed to be 255
 |     // hex_color does not include an alpha, so it must be assumed to be 255
 | ||||||
|  |  | ||||||
|  | @ -7,21 +7,20 @@ | ||||||
| #include <QAbstractItemModel> | #include <QAbstractItemModel> | ||||||
| #include <QDockWidget> | #include <QDockWidget> | ||||||
| #include <QTimer> | #include <QTimer> | ||||||
| 
 |  | ||||||
| #include "ui_profiler.h" |  | ||||||
| 
 |  | ||||||
| #include "common/microprofile.h" | #include "common/microprofile.h" | ||||||
| #include "common/profiler_reporting.h" | #include "common/profiler_reporting.h" | ||||||
|  | #include "ui_profiler.h" | ||||||
| 
 | 
 | ||||||
| class ProfilerModel : public QAbstractItemModel | class ProfilerModel : public QAbstractItemModel { | ||||||
| { |  | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|     ProfilerModel(QObject* parent); |     ProfilerModel(QObject* parent); | ||||||
| 
 | 
 | ||||||
|     QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; |     QVariant headerData(int section, Qt::Orientation orientation, | ||||||
|     QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; |                         int role = Qt::DisplayRole) const override; | ||||||
|  |     QModelIndex index(int row, int column, | ||||||
|  |                       const QModelIndex& parent = QModelIndex()) const override; | ||||||
|     QModelIndex parent(const QModelIndex& child) const override; |     QModelIndex parent(const QModelIndex& child) const override; | ||||||
|     int columnCount(const QModelIndex& parent = QModelIndex()) const override; |     int columnCount(const QModelIndex& parent = QModelIndex()) const override; | ||||||
|     int rowCount(const QModelIndex& parent = QModelIndex()) const override; |     int rowCount(const QModelIndex& parent = QModelIndex()) const override; | ||||||
|  | @ -34,8 +33,7 @@ private: | ||||||
|     Common::Profiling::AggregatedFrameResult results; |     Common::Profiling::AggregatedFrameResult results; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class ProfilerWidget : public QDockWidget | class ProfilerWidget : public QDockWidget { | ||||||
| { |  | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|  | @ -51,7 +49,6 @@ private: | ||||||
|     QTimer update_timer; |     QTimer update_timer; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| class MicroProfileDialog : public QWidget { | class MicroProfileDialog : public QWidget { | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -4,12 +4,9 @@ | ||||||
| 
 | 
 | ||||||
| #include "citra_qt/debugger/ramview.h" | #include "citra_qt/debugger/ramview.h" | ||||||
| 
 | 
 | ||||||
| GRamView::GRamView(QWidget* parent) : QHexEdit(parent) | GRamView::GRamView(QWidget* parent) : QHexEdit(parent) {} | ||||||
| { |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| void GRamView::OnCPUStepped() | void GRamView::OnCPUStepped() { | ||||||
| { |  | ||||||
|     // TODO: QHexEdit doesn't show vertical scroll bars for > 10MB data streams...
 |     // TODO: QHexEdit doesn't show vertical scroll bars for > 10MB data streams...
 | ||||||
|     //setData(QByteArray((const char*)Mem_RAM,sizeof(Mem_RAM)/8));
 |     // setData(QByteArray((const char*)Mem_RAM,sizeof(Mem_RAM)/8));
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -4,8 +4,7 @@ | ||||||
| 
 | 
 | ||||||
| #include "qhexedit.h" | #include "qhexedit.h" | ||||||
| 
 | 
 | ||||||
| class GRamView : public QHexEdit | class GRamView : public QHexEdit { | ||||||
| { |  | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|  |  | ||||||
|  | @ -3,12 +3,10 @@ | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| #include <QTreeWidgetItem> | #include <QTreeWidgetItem> | ||||||
| 
 |  | ||||||
| #include "citra_qt/debugger/registers.h" | #include "citra_qt/debugger/registers.h" | ||||||
| #include "citra_qt/util/util.h" | #include "citra_qt/util/util.h" | ||||||
| 
 |  | ||||||
| #include "core/core.h" |  | ||||||
| #include "core/arm/arm_interface.h" | #include "core/arm/arm_interface.h" | ||||||
|  | #include "core/core.h" | ||||||
| 
 | 
 | ||||||
| RegistersWidget::RegistersWidget(QWidget* parent) : QDockWidget(parent) { | RegistersWidget::RegistersWidget(QWidget* parent) : QDockWidget(parent) { | ||||||
|     cpu_regs_ui.setupUi(this); |     cpu_regs_ui.setupUi(this); | ||||||
|  | @ -16,7 +14,8 @@ RegistersWidget::RegistersWidget(QWidget* parent) : QDockWidget(parent) { | ||||||
|     tree = cpu_regs_ui.treeWidget; |     tree = cpu_regs_ui.treeWidget; | ||||||
|     tree->addTopLevelItem(core_registers = new QTreeWidgetItem(QStringList(tr("Registers")))); |     tree->addTopLevelItem(core_registers = new QTreeWidgetItem(QStringList(tr("Registers")))); | ||||||
|     tree->addTopLevelItem(vfp_registers = new QTreeWidgetItem(QStringList(tr("VFP Registers")))); |     tree->addTopLevelItem(vfp_registers = new QTreeWidgetItem(QStringList(tr("VFP Registers")))); | ||||||
|     tree->addTopLevelItem(vfp_system_registers = new QTreeWidgetItem(QStringList(tr("VFP System Registers")))); |     tree->addTopLevelItem(vfp_system_registers = | ||||||
|  |                               new QTreeWidgetItem(QStringList(tr("VFP System Registers")))); | ||||||
|     tree->addTopLevelItem(cpsr = new QTreeWidgetItem(QStringList("CPSR"))); |     tree->addTopLevelItem(cpsr = new QTreeWidgetItem(QStringList("CPSR"))); | ||||||
| 
 | 
 | ||||||
|     for (int i = 0; i < 16; ++i) { |     for (int i = 0; i < 16; ++i) { | ||||||
|  | @ -63,17 +62,18 @@ void RegistersWidget::OnDebugModeEntered() { | ||||||
|         return; |         return; | ||||||
| 
 | 
 | ||||||
|     for (int i = 0; i < core_registers->childCount(); ++i) |     for (int i = 0; i < core_registers->childCount(); ++i) | ||||||
|         core_registers->child(i)->setText(1, QString("0x%1").arg(Core::g_app_core->GetReg(i), 8, 16, QLatin1Char('0'))); |         core_registers->child(i)->setText( | ||||||
|  |             1, QString("0x%1").arg(Core::g_app_core->GetReg(i), 8, 16, QLatin1Char('0'))); | ||||||
| 
 | 
 | ||||||
|     for (int i = 0; i < vfp_registers->childCount(); ++i) |     for (int i = 0; i < vfp_registers->childCount(); ++i) | ||||||
|         vfp_registers->child(i)->setText(1, QString("0x%1").arg(Core::g_app_core->GetVFPReg(i), 8, 16, QLatin1Char('0'))); |         vfp_registers->child(i)->setText( | ||||||
|  |             1, QString("0x%1").arg(Core::g_app_core->GetVFPReg(i), 8, 16, QLatin1Char('0'))); | ||||||
| 
 | 
 | ||||||
|     UpdateCPSRValues(); |     UpdateCPSRValues(); | ||||||
|     UpdateVFPSystemRegisterValues(); |     UpdateVFPSystemRegisterValues(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RegistersWidget::OnDebugModeLeft() { | void RegistersWidget::OnDebugModeLeft() {} | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| void RegistersWidget::OnEmulationStarting(EmuThread* emu_thread) { | void RegistersWidget::OnEmulationStarting(EmuThread* emu_thread) { | ||||||
|     setEnabled(true); |     setEnabled(true); | ||||||
|  | @ -130,21 +130,24 @@ void RegistersWidget::UpdateCPSRValues() { | ||||||
|     const u32 cpsr_val = Core::g_app_core->GetCPSR(); |     const u32 cpsr_val = Core::g_app_core->GetCPSR(); | ||||||
| 
 | 
 | ||||||
|     cpsr->setText(1, QString("0x%1").arg(cpsr_val, 8, 16, QLatin1Char('0'))); |     cpsr->setText(1, QString("0x%1").arg(cpsr_val, 8, 16, QLatin1Char('0'))); | ||||||
|     cpsr->child(0)->setText(1, QString("b%1").arg(cpsr_val & 0x1F, 5, 2, QLatin1Char('0'))); // M - Mode
 |     cpsr->child(0)->setText( | ||||||
|     cpsr->child(1)->setText(1, QString::number((cpsr_val >> 5) & 1));     // T - State
 |         1, QString("b%1").arg(cpsr_val & 0x1F, 5, 2, QLatin1Char('0'))); // M - Mode
 | ||||||
|     cpsr->child(2)->setText(1, QString::number((cpsr_val >> 6) & 1));     // F - FIQ disable
 |     cpsr->child(1)->setText(1, QString::number((cpsr_val >> 5) & 1));    // T - State
 | ||||||
|     cpsr->child(3)->setText(1, QString::number((cpsr_val >> 7) & 1));     // I - IRQ disable
 |     cpsr->child(2)->setText(1, QString::number((cpsr_val >> 6) & 1));    // F - FIQ disable
 | ||||||
|     cpsr->child(4)->setText(1, QString::number((cpsr_val >> 8) & 1));     // A - Imprecise abort
 |     cpsr->child(3)->setText(1, QString::number((cpsr_val >> 7) & 1));    // I - IRQ disable
 | ||||||
|     cpsr->child(5)->setText(1, QString::number((cpsr_val >> 9) & 1));     // E - Data endianess
 |     cpsr->child(4)->setText(1, QString::number((cpsr_val >> 8) & 1));    // A - Imprecise abort
 | ||||||
|     cpsr->child(6)->setText(1, QString::number((cpsr_val >> 10) & 0x3F)); // IT - If-Then state (DNM)
 |     cpsr->child(5)->setText(1, QString::number((cpsr_val >> 9) & 1));    // E - Data endianess
 | ||||||
|     cpsr->child(7)->setText(1, QString::number((cpsr_val >> 16) & 0xF));  // GE - Greater-than-or-Equal
 |     cpsr->child(6)->setText(1, | ||||||
|     cpsr->child(8)->setText(1, QString::number((cpsr_val >> 20) & 0xF));  // DNM - Do not modify
 |                             QString::number((cpsr_val >> 10) & 0x3F)); // IT - If-Then state (DNM)
 | ||||||
|     cpsr->child(9)->setText(1, QString::number((cpsr_val >> 24) & 1));    // J - Jazelle
 |     cpsr->child(7)->setText(1, | ||||||
|     cpsr->child(10)->setText(1, QString::number((cpsr_val >> 27) & 1));   // Q - Saturation
 |                             QString::number((cpsr_val >> 16) & 0xF)); // GE - Greater-than-or-Equal
 | ||||||
|     cpsr->child(11)->setText(1, QString::number((cpsr_val >> 28) & 1));   // V - Overflow
 |     cpsr->child(8)->setText(1, QString::number((cpsr_val >> 20) & 0xF)); // DNM - Do not modify
 | ||||||
|     cpsr->child(12)->setText(1, QString::number((cpsr_val >> 29) & 1));   // C - Carry/Borrow/Extend
 |     cpsr->child(9)->setText(1, QString::number((cpsr_val >> 24) & 1));   // J - Jazelle
 | ||||||
|     cpsr->child(13)->setText(1, QString::number((cpsr_val >> 30) & 1));   // Z - Zero
 |     cpsr->child(10)->setText(1, QString::number((cpsr_val >> 27) & 1));  // Q - Saturation
 | ||||||
|     cpsr->child(14)->setText(1, QString::number((cpsr_val >> 31) & 1));   // N - Negative/Less than
 |     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() { | void RegistersWidget::CreateVFPSystemRegisterChildren() { | ||||||
|  | @ -188,9 +191,9 @@ void RegistersWidget::CreateVFPSystemRegisterChildren() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RegistersWidget::UpdateVFPSystemRegisterValues() { | void RegistersWidget::UpdateVFPSystemRegisterValues() { | ||||||
|     const u32 fpscr_val   = Core::g_app_core->GetVFPSystemReg(VFP_FPSCR); |     const u32 fpscr_val = Core::g_app_core->GetVFPSystemReg(VFP_FPSCR); | ||||||
|     const u32 fpexc_val   = Core::g_app_core->GetVFPSystemReg(VFP_FPEXC); |     const u32 fpexc_val = Core::g_app_core->GetVFPSystemReg(VFP_FPEXC); | ||||||
|     const u32 fpinst_val  = Core::g_app_core->GetVFPSystemReg(VFP_FPINST); |     const u32 fpinst_val = Core::g_app_core->GetVFPSystemReg(VFP_FPINST); | ||||||
|     const u32 fpinst2_val = Core::g_app_core->GetVFPSystemReg(VFP_FPINST2); |     const u32 fpinst2_val = Core::g_app_core->GetVFPSystemReg(VFP_FPINST2); | ||||||
| 
 | 
 | ||||||
|     QTreeWidgetItem* const fpscr = vfp_system_registers->child(0); |     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(6)->setText(1, QString::number((fpexc_val >> 30) & 1)); | ||||||
|     fpexc->child(7)->setText(1, QString::number((fpexc_val >> 31) & 1)); |     fpexc->child(7)->setText(1, QString::number((fpexc_val >> 31) & 1)); | ||||||
| 
 | 
 | ||||||
|     vfp_system_registers->child(2)->setText(1, QString("0x%1").arg(fpinst_val, 8, 16, QLatin1Char('0'))); |     vfp_system_registers->child(2)->setText( | ||||||
|     vfp_system_registers->child(3)->setText(1, QString("0x%1").arg(fpinst2_val, 8, 16, QLatin1Char('0'))); |         1, QString("0x%1").arg(fpinst_val, 8, 16, QLatin1Char('0'))); | ||||||
|  |     vfp_system_registers->child(3)->setText( | ||||||
|  |         1, QString("0x%1").arg(fpinst2_val, 8, 16, QLatin1Char('0'))); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -2,16 +2,14 @@ | ||||||
| // Licensed under GPLv2 or any later version
 | // Licensed under GPLv2 or any later version
 | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| #include "ui_registers.h" |  | ||||||
| 
 |  | ||||||
| #include <QDockWidget> | #include <QDockWidget> | ||||||
|  | #include "ui_registers.h" | ||||||
| 
 | 
 | ||||||
| class QTreeWidget; | class QTreeWidget; | ||||||
| class QTreeWidgetItem; | class QTreeWidgetItem; | ||||||
| class EmuThread; | class EmuThread; | ||||||
| 
 | 
 | ||||||
| class RegistersWidget : public QDockWidget | class RegistersWidget : public QDockWidget { | ||||||
| { |  | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|  |  | ||||||
|  | @ -5,19 +5,15 @@ | ||||||
| #include <QHeaderView> | #include <QHeaderView> | ||||||
| #include <QThreadPool> | #include <QThreadPool> | ||||||
| #include <QVBoxLayout> | #include <QVBoxLayout> | ||||||
| 
 | #include "common/common_paths.h" | ||||||
|  | #include "common/logging/log.h" | ||||||
|  | #include "common/string_util.h" | ||||||
|  | #include "core/loader/loader.h" | ||||||
| #include "game_list.h" | #include "game_list.h" | ||||||
| #include "game_list_p.h" | #include "game_list_p.h" | ||||||
| #include "ui_settings.h" | #include "ui_settings.h" | ||||||
| 
 | 
 | ||||||
| #include "core/loader/loader.h" | GameList::GameList(QWidget* parent) { | ||||||
| 
 |  | ||||||
| #include "common/common_paths.h" |  | ||||||
| #include "common/logging/log.h" |  | ||||||
| #include "common/string_util.h" |  | ||||||
| 
 |  | ||||||
| GameList::GameList(QWidget* parent) |  | ||||||
| { |  | ||||||
|     QVBoxLayout* layout = new QVBoxLayout; |     QVBoxLayout* layout = new QVBoxLayout; | ||||||
| 
 | 
 | ||||||
|     tree_view = new QTreeView; |     tree_view = new QTreeView; | ||||||
|  | @ -38,9 +34,11 @@ GameList::GameList(QWidget* parent) | ||||||
|     item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, "File type"); |     item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, "File type"); | ||||||
|     item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "Size"); |     item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "Size"); | ||||||
| 
 | 
 | ||||||
|     connect(tree_view, SIGNAL(activated(const QModelIndex&)), this, SLOT(ValidateEntry(const QModelIndex&))); |     connect(tree_view, SIGNAL(activated(const QModelIndex&)), this, | ||||||
|  |             SLOT(ValidateEntry(const QModelIndex&))); | ||||||
| 
 | 
 | ||||||
|     // We must register all custom types with the Qt Automoc system so that we are able to use it with
 |     // We must register all custom types with the Qt Automoc system so that we are able to use it
 | ||||||
|  |     // with
 | ||||||
|     // signals/slots. In this case, QList falls under the umbrells of custom types.
 |     // signals/slots. In this case, QList falls under the umbrells of custom types.
 | ||||||
|     qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>"); |     qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>"); | ||||||
| 
 | 
 | ||||||
|  | @ -48,18 +46,15 @@ GameList::GameList(QWidget* parent) | ||||||
|     setLayout(layout); |     setLayout(layout); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| GameList::~GameList() | GameList::~GameList() { | ||||||
| { |  | ||||||
|     emit ShouldCancelWorker(); |     emit ShouldCancelWorker(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GameList::AddEntry(QList<QStandardItem*> entry_items) | void GameList::AddEntry(QList<QStandardItem*> entry_items) { | ||||||
| { |  | ||||||
|     item_model->invisibleRootItem()->appendRow(entry_items); |     item_model->invisibleRootItem()->appendRow(entry_items); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GameList::ValidateEntry(const QModelIndex& item) | void GameList::ValidateEntry(const QModelIndex& item) { | ||||||
| { |  | ||||||
|     // We don't care about the individual QStandardItem that was selected, but its row.
 |     // We don't care about the individual QStandardItem that was selected, but its row.
 | ||||||
|     int row = item_model->itemFromIndex(item)->row(); |     int row = item_model->itemFromIndex(item)->row(); | ||||||
|     QStandardItem* child_file = item_model->invisibleRootItem()->child(row, COLUMN_NAME); |     QStandardItem* child_file = item_model->invisibleRootItem()->child(row, COLUMN_NAME); | ||||||
|  | @ -73,14 +68,13 @@ void GameList::ValidateEntry(const QModelIndex& item) | ||||||
|     emit GameChosen(file_path); |     emit GameChosen(file_path); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GameList::DonePopulating() | void GameList::DonePopulating() { | ||||||
| { |  | ||||||
|     tree_view->setEnabled(true); |     tree_view->setEnabled(true); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) | void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) { | ||||||
| { |     if (!FileUtil::Exists(dir_path.toStdString()) || | ||||||
|     if (!FileUtil::Exists(dir_path.toStdString()) || !FileUtil::IsDirectory(dir_path.toStdString())) { |         !FileUtil::IsDirectory(dir_path.toStdString())) { | ||||||
|         LOG_ERROR(Frontend, "Could not find game list folder at %s", dir_path.toLocal8Bit().data()); |         LOG_ERROR(Frontend, "Could not find game list folder at %s", dir_path.toLocal8Bit().data()); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  | @ -92,22 +86,22 @@ void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) | ||||||
|     emit ShouldCancelWorker(); |     emit ShouldCancelWorker(); | ||||||
|     GameListWorker* worker = new GameListWorker(dir_path, deep_scan); |     GameListWorker* worker = new GameListWorker(dir_path, deep_scan); | ||||||
| 
 | 
 | ||||||
|     connect(worker, SIGNAL(EntryReady(QList<QStandardItem*>)), this, SLOT(AddEntry(QList<QStandardItem*>)), Qt::QueuedConnection); |     connect(worker, SIGNAL(EntryReady(QList<QStandardItem*>)), this, | ||||||
|  |             SLOT(AddEntry(QList<QStandardItem*>)), Qt::QueuedConnection); | ||||||
|     connect(worker, SIGNAL(Finished()), this, SLOT(DonePopulating()), Qt::QueuedConnection); |     connect(worker, SIGNAL(Finished()), this, SLOT(DonePopulating()), Qt::QueuedConnection); | ||||||
|     // Use DirectConnection here because worker->Cancel() is thread-safe and we want it to cancel without delay.
 |     // Use DirectConnection here because worker->Cancel() is thread-safe and we want it to cancel
 | ||||||
|  |     // without delay.
 | ||||||
|     connect(this, SIGNAL(ShouldCancelWorker()), worker, SLOT(Cancel()), Qt::DirectConnection); |     connect(this, SIGNAL(ShouldCancelWorker()), worker, SLOT(Cancel()), Qt::DirectConnection); | ||||||
| 
 | 
 | ||||||
|     QThreadPool::globalInstance()->start(worker); |     QThreadPool::globalInstance()->start(worker); | ||||||
|     current_worker = std::move(worker); |     current_worker = std::move(worker); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GameList::SaveInterfaceLayout() | void GameList::SaveInterfaceLayout() { | ||||||
| { |  | ||||||
|     UISettings::values.gamelist_header_state = tree_view->header()->saveState(); |     UISettings::values.gamelist_header_state = tree_view->header()->saveState(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GameList::LoadInterfaceLayout() | void GameList::LoadInterfaceLayout() { | ||||||
| { |  | ||||||
|     auto header = tree_view->header(); |     auto header = tree_view->header(); | ||||||
|     if (!header->restoreState(UISettings::values.gamelist_header_state)) { |     if (!header->restoreState(UISettings::values.gamelist_header_state)) { | ||||||
|         // We are using the name column to display icons and titles
 |         // We are using the name column to display icons and titles
 | ||||||
|  | @ -118,10 +112,8 @@ void GameList::LoadInterfaceLayout() | ||||||
|     item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder()); |     item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) | void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) { | ||||||
| { |     const auto callback = [this, recursion](unsigned* num_entries_out, const std::string& directory, | ||||||
|     const auto callback = [this, recursion](unsigned* num_entries_out, |  | ||||||
|                                             const std::string& directory, |  | ||||||
|                                             const std::string& virtual_name) -> bool { |                                             const std::string& virtual_name) -> bool { | ||||||
|         std::string physical_name = directory + DIR_SEP + virtual_name; |         std::string physical_name = directory + DIR_SEP + virtual_name; | ||||||
| 
 | 
 | ||||||
|  | @ -138,7 +130,8 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign | ||||||
| 
 | 
 | ||||||
|             emit EntryReady({ |             emit EntryReady({ | ||||||
|                 new GameListItemPath(QString::fromStdString(physical_name), smdh), |                 new GameListItemPath(QString::fromStdString(physical_name), smdh), | ||||||
|                 new GameListItem(QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), |                 new GameListItem( | ||||||
|  |                     QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), | ||||||
|                 new GameListItemSize(FileUtil::GetSize(physical_name)), |                 new GameListItemSize(FileUtil::GetSize(physical_name)), | ||||||
|             }); |             }); | ||||||
|         } else if (recursion > 0) { |         } else if (recursion > 0) { | ||||||
|  | @ -151,15 +144,13 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign | ||||||
|     FileUtil::ForeachDirectoryEntry(nullptr, dir_path, callback); |     FileUtil::ForeachDirectoryEntry(nullptr, dir_path, callback); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GameListWorker::run() | void GameListWorker::run() { | ||||||
| { |  | ||||||
|     stop_processing = false; |     stop_processing = false; | ||||||
|     AddFstEntriesToGameList(dir_path.toStdString(), deep_scan ? 256 : 0); |     AddFstEntriesToGameList(dir_path.toStdString(), deep_scan ? 256 : 0); | ||||||
|     emit Finished(); |     emit Finished(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GameListWorker::Cancel() | void GameListWorker::Cancel() { | ||||||
| { |  | ||||||
|     disconnect(this, 0, 0, 0); |     disconnect(this, 0, 0, 0); | ||||||
|     stop_processing = true; |     stop_processing = true; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -14,7 +14,6 @@ | ||||||
| 
 | 
 | ||||||
| class GameListWorker; | class GameListWorker; | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| class GameList : public QWidget { | class GameList : public QWidget { | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -5,18 +5,14 @@ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <atomic> | #include <atomic> | ||||||
| 
 |  | ||||||
| #include <QImage> | #include <QImage> | ||||||
| #include <QRunnable> | #include <QRunnable> | ||||||
| #include <QStandardItem> | #include <QStandardItem> | ||||||
| #include <QString> | #include <QString> | ||||||
| 
 |  | ||||||
| #include "citra_qt/util/util.h" | #include "citra_qt/util/util.h" | ||||||
| #include "common/string_util.h" |  | ||||||
| #include "common/color.h" | #include "common/color.h" | ||||||
| 
 | #include "common/string_util.h" | ||||||
| #include "core/loader/smdh.h" | #include "core/loader/smdh.h" | ||||||
| 
 |  | ||||||
| #include "video_core/utils.h" | #include "video_core/utils.h" | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  | @ -51,19 +47,19 @@ static QPixmap GetDefaultIcon(bool large) { | ||||||
|  * @param language title language |  * @param language title language | ||||||
|  * @return QString short title |  * @return QString short title | ||||||
|  */ |  */ | ||||||
| static QString GetQStringShortTitleFromSMDH(const Loader::SMDH& smdh, Loader::SMDH::TitleLanguage language) { | static QString GetQStringShortTitleFromSMDH(const Loader::SMDH& smdh, | ||||||
|  |                                             Loader::SMDH::TitleLanguage language) { | ||||||
|     return QString::fromUtf16(smdh.GetShortTitle(language).data()); |     return QString::fromUtf16(smdh.GetShortTitle(language).data()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class GameListItem : public QStandardItem { | class GameListItem : public QStandardItem { | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|     GameListItem(): QStandardItem() {} |     GameListItem() : QStandardItem() {} | ||||||
|     GameListItem(const QString& string): QStandardItem(string) {} |     GameListItem(const QString& string) : QStandardItem(string) {} | ||||||
|     virtual ~GameListItem() override {} |     virtual ~GameListItem() override {} | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| /**
 | /**
 | ||||||
|  * A specialization of GameListItem for path values. |  * A specialization of GameListItem for path values. | ||||||
|  * This class ensures that for every full path value it holds, a correct string representation |  * This class ensures that for every full path value it holds, a correct string representation | ||||||
|  | @ -76,9 +72,8 @@ public: | ||||||
|     static const int FullPathRole = Qt::UserRole + 1; |     static const int FullPathRole = Qt::UserRole + 1; | ||||||
|     static const int TitleRole = Qt::UserRole + 2; |     static const int TitleRole = Qt::UserRole + 2; | ||||||
| 
 | 
 | ||||||
|     GameListItemPath(): GameListItem() {} |     GameListItemPath() : GameListItem() {} | ||||||
|     GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data): GameListItem() |     GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data) : GameListItem() { | ||||||
|     { |  | ||||||
|         setData(game_path, FullPathRole); |         setData(game_path, FullPathRole); | ||||||
| 
 | 
 | ||||||
|         if (!Loader::IsValidSMDH(smdh_data)) { |         if (!Loader::IsValidSMDH(smdh_data)) { | ||||||
|  | @ -94,13 +89,15 @@ public: | ||||||
|         setData(GetQPixmapFromSMDH(smdh, true), Qt::DecorationRole); |         setData(GetQPixmapFromSMDH(smdh, true), Qt::DecorationRole); | ||||||
| 
 | 
 | ||||||
|         // Get title form SMDH
 |         // Get title form SMDH
 | ||||||
|         setData(GetQStringShortTitleFromSMDH(smdh, Loader::SMDH::TitleLanguage::English), TitleRole); |         setData(GetQStringShortTitleFromSMDH(smdh, Loader::SMDH::TitleLanguage::English), | ||||||
|  |                 TitleRole); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     QVariant data(int role) const override { |     QVariant data(int role) const override { | ||||||
|         if (role == Qt::DisplayRole) { |         if (role == Qt::DisplayRole) { | ||||||
|             std::string filename; |             std::string filename; | ||||||
|             Common::SplitPath(data(FullPathRole).toString().toStdString(), nullptr, &filename, nullptr); |             Common::SplitPath(data(FullPathRole).toString().toStdString(), nullptr, &filename, | ||||||
|  |                               nullptr); | ||||||
|             QString title = data(TitleRole).toString(); |             QString title = data(TitleRole).toString(); | ||||||
|             return QString::fromStdString(filename) + (title.isEmpty() ? "" : "\n    " + title); |             return QString::fromStdString(filename) + (title.isEmpty() ? "" : "\n    " + title); | ||||||
|         } else { |         } else { | ||||||
|  | @ -109,7 +106,6 @@ public: | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| /**
 | /**
 | ||||||
|  * A specialization of GameListItem for size values. |  * A specialization of GameListItem for size values. | ||||||
|  * This class ensures that for every numerical size value it holds (in bytes), a correct |  * This class ensures that for every numerical size value it holds (in bytes), a correct | ||||||
|  | @ -120,14 +116,12 @@ class GameListItemSize : public GameListItem { | ||||||
| public: | public: | ||||||
|     static const int SizeRole = Qt::UserRole + 1; |     static const int SizeRole = Qt::UserRole + 1; | ||||||
| 
 | 
 | ||||||
|     GameListItemSize(): GameListItem() {} |     GameListItemSize() : GameListItem() {} | ||||||
|     GameListItemSize(const qulonglong size_bytes): GameListItem() |     GameListItemSize(const qulonglong size_bytes) : GameListItem() { | ||||||
|     { |  | ||||||
|         setData(size_bytes, SizeRole); |         setData(size_bytes, SizeRole); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void setData(const QVariant& value, int role) override |     void setData(const QVariant& value, int role) override { | ||||||
|     { |  | ||||||
|         // By specializing setData for SizeRole, we can ensure that the numerical and string
 |         // By specializing setData for SizeRole, we can ensure that the numerical and string
 | ||||||
|         // representations of the data are always accurate and in the correct format.
 |         // representations of the data are always accurate and in the correct format.
 | ||||||
|         if (role == SizeRole) { |         if (role == SizeRole) { | ||||||
|  | @ -141,15 +135,14 @@ public: | ||||||
| 
 | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * This operator is, in practice, only used by the TreeView sorting systems. |      * This operator is, in practice, only used by the TreeView sorting systems. | ||||||
|      * Override it so that it will correctly sort by numerical value instead of by string representation. |      * Override it so that it will correctly sort by numerical value instead of by string | ||||||
|  |      * representation. | ||||||
|      */ |      */ | ||||||
|     bool operator<(const QStandardItem& other) const override |     bool operator<(const QStandardItem& other) const override { | ||||||
|     { |  | ||||||
|         return data(SizeRole).toULongLong() < other.data(SizeRole).toULongLong(); |         return data(SizeRole).toULongLong() < other.data(SizeRole).toULongLong(); | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| /**
 | /**
 | ||||||
|  * Asynchronous worker object for populating the game list. |  * Asynchronous worker object for populating the game list. | ||||||
|  * Communicates with other threads through Qt's signal/slot system. |  * Communicates with other threads through Qt's signal/slot system. | ||||||
|  | @ -158,8 +151,8 @@ class GameListWorker : public QObject, public QRunnable { | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|     GameListWorker(QString dir_path, bool deep_scan): |     GameListWorker(QString dir_path, bool deep_scan) | ||||||
|             QObject(), QRunnable(), dir_path(dir_path), deep_scan(deep_scan) {} |         : QObject(), QRunnable(), dir_path(dir_path), deep_scan(deep_scan) {} | ||||||
| 
 | 
 | ||||||
| public slots: | public slots: | ||||||
|     /// Starts the processing of directory tree information.
 |     /// Starts the processing of directory tree information.
 | ||||||
|  |  | ||||||
|  | @ -3,16 +3,13 @@ | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| #include <map> | #include <map> | ||||||
| 
 |  | ||||||
| #include <QtGlobal> |  | ||||||
| #include <QKeySequence> | #include <QKeySequence> | ||||||
| #include <QShortcut> | #include <QShortcut> | ||||||
| 
 | #include <QtGlobal> | ||||||
| #include "citra_qt/hotkeys.h" | #include "citra_qt/hotkeys.h" | ||||||
| #include "citra_qt/ui_settings.h" | #include "citra_qt/ui_settings.h" | ||||||
| 
 | 
 | ||||||
| struct Hotkey | struct Hotkey { | ||||||
| { |  | ||||||
|     Hotkey() : shortcut(nullptr), context(Qt::WindowShortcut) {} |     Hotkey() : shortcut(nullptr), context(Qt::WindowShortcut) {} | ||||||
| 
 | 
 | ||||||
|     QKeySequence keyseq; |     QKeySequence keyseq; | ||||||
|  | @ -25,33 +22,28 @@ typedef std::map<QString, HotkeyMap> HotkeyGroupMap; | ||||||
| 
 | 
 | ||||||
| HotkeyGroupMap hotkey_groups; | HotkeyGroupMap hotkey_groups; | ||||||
| 
 | 
 | ||||||
| void SaveHotkeys() | void SaveHotkeys() { | ||||||
| { |  | ||||||
|     UISettings::values.shortcuts.clear(); |     UISettings::values.shortcuts.clear(); | ||||||
|     for (auto group : hotkey_groups) |     for (auto group : hotkey_groups) { | ||||||
|     { |         for (auto hotkey : group.second) { | ||||||
|         for (auto hotkey : group.second) |  | ||||||
|         { |  | ||||||
|             UISettings::values.shortcuts.emplace_back( |             UISettings::values.shortcuts.emplace_back( | ||||||
|                         UISettings::Shortcut(group.first + "/" + hotkey.first, |                 UISettings::Shortcut(group.first + "/" + hotkey.first, | ||||||
|                                              UISettings::ContextualShortcut(hotkey.second.keyseq.toString(), |                                      UISettings::ContextualShortcut(hotkey.second.keyseq.toString(), | ||||||
|                                                                            hotkey.second.context))); |                                                                     hotkey.second.context))); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void LoadHotkeys() | void LoadHotkeys() { | ||||||
| { |     // Make sure NOT to use a reference here because it would become invalid once we call
 | ||||||
|     // Make sure NOT to use a reference here because it would become invalid once we call beginGroup()
 |     // beginGroup()
 | ||||||
|     for (auto shortcut : UISettings::values.shortcuts) |     for (auto shortcut : UISettings::values.shortcuts) { | ||||||
|     { |  | ||||||
|         QStringList cat = shortcut.first.split("/"); |         QStringList cat = shortcut.first.split("/"); | ||||||
|         Q_ASSERT(cat.size() >= 2); |         Q_ASSERT(cat.size() >= 2); | ||||||
| 
 | 
 | ||||||
|         // RegisterHotkey assigns default keybindings, so use old values as default parameters
 |         // RegisterHotkey assigns default keybindings, so use old values as default parameters
 | ||||||
|         Hotkey& hk = hotkey_groups[cat[0]][cat[1]]; |         Hotkey& hk = hotkey_groups[cat[0]][cat[1]]; | ||||||
|         if (!shortcut.second.first.isEmpty()) |         if (!shortcut.second.first.isEmpty()) { | ||||||
|         { |  | ||||||
|             hk.keyseq = QKeySequence::fromString(shortcut.second.first); |             hk.keyseq = QKeySequence::fromString(shortcut.second.first); | ||||||
|             hk.context = (Qt::ShortcutContext)shortcut.second.second; |             hk.context = (Qt::ShortcutContext)shortcut.second.second; | ||||||
|         } |         } | ||||||
|  | @ -60,17 +52,15 @@ void LoadHotkeys() | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RegisterHotkey(const QString& group, const QString& action, const QKeySequence& default_keyseq, Qt::ShortcutContext default_context) | void RegisterHotkey(const QString& group, const QString& action, const QKeySequence& default_keyseq, | ||||||
| { |                     Qt::ShortcutContext default_context) { | ||||||
|     if (hotkey_groups[group].find(action) == hotkey_groups[group].end()) |     if (hotkey_groups[group].find(action) == hotkey_groups[group].end()) { | ||||||
|     { |  | ||||||
|         hotkey_groups[group][action].keyseq = default_keyseq; |         hotkey_groups[group][action].keyseq = default_keyseq; | ||||||
|         hotkey_groups[group][action].context = default_context; |         hotkey_groups[group][action].context = default_context; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget) | QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget) { | ||||||
| { |  | ||||||
|     Hotkey& hk = hotkey_groups[group][action]; |     Hotkey& hk = hotkey_groups[group][action]; | ||||||
| 
 | 
 | ||||||
|     if (!hk.shortcut) |     if (!hk.shortcut) | ||||||
|  | @ -79,16 +69,12 @@ QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widge | ||||||
|     return hk.shortcut; |     return hk.shortcut; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | GHotkeysDialog::GHotkeysDialog(QWidget* parent) : QWidget(parent) { | ||||||
| GHotkeysDialog::GHotkeysDialog(QWidget* parent): QWidget(parent) |  | ||||||
| { |  | ||||||
|     ui.setupUi(this); |     ui.setupUi(this); | ||||||
| 
 | 
 | ||||||
|     for (auto group : hotkey_groups) |     for (auto group : hotkey_groups) { | ||||||
|     { |  | ||||||
|         QTreeWidgetItem* toplevel_item = new QTreeWidgetItem(QStringList(group.first)); |         QTreeWidgetItem* toplevel_item = new QTreeWidgetItem(QStringList(group.first)); | ||||||
|         for (auto hotkey : group.second) |         for (auto hotkey : group.second) { | ||||||
|         { |  | ||||||
|             QStringList columns; |             QStringList columns; | ||||||
|             columns << hotkey.first << hotkey.second.keyseq.toString(); |             columns << hotkey.first << hotkey.second.keyseq.toString(); | ||||||
|             QTreeWidgetItem* item = new QTreeWidgetItem(columns); |             QTreeWidgetItem* item = new QTreeWidgetItem(columns); | ||||||
|  |  | ||||||
|  | @ -16,36 +16,42 @@ class QShortcut; | ||||||
|  * |  * | ||||||
|  * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger") |  * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger") | ||||||
|  * @param action Name of the action (e.g. "Start Emulation", "Load Image") |  * @param action Name of the action (e.g. "Start Emulation", "Load Image") | ||||||
|  * @param default_keyseq Default key sequence to assign if the hotkey wasn't present in the settings file before |  * @param default_keyseq Default key sequence to assign if the hotkey wasn't present in the settings | ||||||
|  * @param default_context Default context to assign if the hotkey wasn't present in the settings file before |  * file before | ||||||
|  |  * @param default_context Default context to assign if the hotkey wasn't present in the settings | ||||||
|  |  * file before | ||||||
|  * @warning Both the group and action strings will be displayed in the hotkey settings dialog |  * @warning Both the group and action strings will be displayed in the hotkey settings dialog | ||||||
|  */ |  */ | ||||||
| void RegisterHotkey(const QString& group, const QString& action, const QKeySequence& default_keyseq = QKeySequence(), Qt::ShortcutContext default_context = Qt::WindowShortcut); | void RegisterHotkey(const QString& group, const QString& action, | ||||||
|  |                     const QKeySequence& default_keyseq = QKeySequence(), | ||||||
|  |                     Qt::ShortcutContext default_context = Qt::WindowShortcut); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Returns a QShortcut object whose activated() signal can be connected to other QObjects' slots. |  * Returns a QShortcut object whose activated() signal can be connected to other QObjects' slots. | ||||||
|  * |  * | ||||||
|  * @param widget Parent widget of the returned QShortcut. |  * @param widget Parent widget of the returned QShortcut. | ||||||
|  * @warning If multiple QWidgets' call this function for the same action, the returned QShortcut will be the same. Thus, you shouldn't rely on the caller really being the QShortcut's parent. |  * @warning If multiple QWidgets' call this function for the same action, the returned QShortcut | ||||||
|  |  * will be the same. Thus, you shouldn't rely on the caller really being the QShortcut's parent. | ||||||
|  */ |  */ | ||||||
| QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget); | QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Saves all registered hotkeys to the settings file. |  * Saves all registered hotkeys to the settings file. | ||||||
|  * |  * | ||||||
|  * @note Each hotkey group will be stored a settings group; For each hotkey inside that group, a settings group will be created to store the key sequence and the hotkey context. |  * @note Each hotkey group will be stored a settings group; For each hotkey inside that group, a | ||||||
|  |  * settings group will be created to store the key sequence and the hotkey context. | ||||||
|  */ |  */ | ||||||
| void SaveHotkeys(); | void SaveHotkeys(); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Loads hotkeys from the settings file. |  * Loads hotkeys from the settings file. | ||||||
|  * |  * | ||||||
|  * @note Yet unregistered hotkeys which are present in the settings will automatically be registered. |  * @note Yet unregistered hotkeys which are present in the settings will automatically be | ||||||
|  |  * registered. | ||||||
|  */ |  */ | ||||||
| void LoadHotkeys(); | void LoadHotkeys(); | ||||||
| 
 | 
 | ||||||
| class GHotkeysDialog : public QWidget | class GHotkeysDialog : public QWidget { | ||||||
| { |  | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|  |  | ||||||
|  | @ -5,25 +5,15 @@ | ||||||
| #include <clocale> | #include <clocale> | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <thread> | #include <thread> | ||||||
| 
 |  | ||||||
| #include <glad/glad.h> | #include <glad/glad.h> | ||||||
| 
 |  | ||||||
| #define QT_NO_OPENGL | #define QT_NO_OPENGL | ||||||
| #include <QDesktopWidget> | #include <QDesktopWidget> | ||||||
| #include <QtGui> |  | ||||||
| #include <QFileDialog> | #include <QFileDialog> | ||||||
| #include <QMessageBox> | #include <QMessageBox> | ||||||
| #include "qhexedit.h" | #include <QtGui> | ||||||
| 
 |  | ||||||
| #include "citra_qt/bootmanager.h" | #include "citra_qt/bootmanager.h" | ||||||
| #include "citra_qt/config.h" | #include "citra_qt/config.h" | ||||||
| #include "citra_qt/configure_dialog.h" | #include "citra_qt/configure_dialog.h" | ||||||
| #include "citra_qt/game_list.h" |  | ||||||
| #include "citra_qt/hotkeys.h" |  | ||||||
| #include "citra_qt/main.h" |  | ||||||
| #include "citra_qt/ui_settings.h" |  | ||||||
| 
 |  | ||||||
| // Debugger
 |  | ||||||
| #include "citra_qt/debugger/callstack.h" | #include "citra_qt/debugger/callstack.h" | ||||||
| #include "citra_qt/debugger/disassembler.h" | #include "citra_qt/debugger/disassembler.h" | ||||||
| #include "citra_qt/debugger/graphics.h" | #include "citra_qt/debugger/graphics.h" | ||||||
|  | @ -35,28 +25,29 @@ | ||||||
| #include "citra_qt/debugger/profiler.h" | #include "citra_qt/debugger/profiler.h" | ||||||
| #include "citra_qt/debugger/ramview.h" | #include "citra_qt/debugger/ramview.h" | ||||||
| #include "citra_qt/debugger/registers.h" | #include "citra_qt/debugger/registers.h" | ||||||
| 
 | #include "citra_qt/game_list.h" | ||||||
|  | #include "citra_qt/hotkeys.h" | ||||||
|  | #include "citra_qt/main.h" | ||||||
|  | #include "citra_qt/ui_settings.h" | ||||||
|  | #include "common/logging/backend.h" | ||||||
|  | #include "common/logging/filter.h" | ||||||
|  | #include "common/logging/log.h" | ||||||
|  | #include "common/logging/text_formatter.h" | ||||||
| #include "common/microprofile.h" | #include "common/microprofile.h" | ||||||
| #include "common/platform.h" | #include "common/platform.h" | ||||||
| #include "common/scm_rev.h" | #include "common/scm_rev.h" | ||||||
| #include "common/scope_exit.h" | #include "common/scope_exit.h" | ||||||
| #include "common/string_util.h" | #include "common/string_util.h" | ||||||
| #include "common/logging/backend.h" |  | ||||||
| #include "common/logging/filter.h" |  | ||||||
| #include "common/logging/log.h" |  | ||||||
| #include "common/logging/text_formatter.h" |  | ||||||
| 
 |  | ||||||
| #include "core/core.h" |  | ||||||
| #include "core/settings.h" |  | ||||||
| #include "core/system.h" |  | ||||||
| #include "core/arm/disassembler/load_symbol_map.h" | #include "core/arm/disassembler/load_symbol_map.h" | ||||||
|  | #include "core/core.h" | ||||||
| #include "core/gdbstub/gdbstub.h" | #include "core/gdbstub/gdbstub.h" | ||||||
| #include "core/loader/loader.h" | #include "core/loader/loader.h" | ||||||
| 
 | #include "core/settings.h" | ||||||
|  | #include "core/system.h" | ||||||
|  | #include "qhexedit.h" | ||||||
| #include "video_core/video_core.h" | #include "video_core/video_core.h" | ||||||
| 
 | 
 | ||||||
| GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) | GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { | ||||||
| { |  | ||||||
|     Pica::g_debug_context = Pica::DebugContext::Construct(); |     Pica::g_debug_context = Pica::DebugContext::Construct(); | ||||||
| 
 | 
 | ||||||
|     ui.setupUi(this); |     ui.setupUi(this); | ||||||
|  | @ -91,7 +82,7 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) | ||||||
| 
 | 
 | ||||||
|     graphicsWidget = new GPUCommandStreamWidget(this); |     graphicsWidget = new GPUCommandStreamWidget(this); | ||||||
|     addDockWidget(Qt::RightDockWidgetArea, graphicsWidget); |     addDockWidget(Qt::RightDockWidgetArea, graphicsWidget); | ||||||
|     graphicsWidget ->hide(); |     graphicsWidget->hide(); | ||||||
| 
 | 
 | ||||||
|     graphicsCommandsWidget = new GPUCommandListWidget(this); |     graphicsCommandsWidget = new GPUCommandListWidget(this); | ||||||
|     addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget); |     addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget); | ||||||
|  | @ -110,7 +101,8 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) | ||||||
|     graphicsTracingWidget->hide(); |     graphicsTracingWidget->hide(); | ||||||
| 
 | 
 | ||||||
|     auto graphicsSurfaceViewerAction = new QAction(tr("Create Pica surface viewer"), this); |     auto graphicsSurfaceViewerAction = new QAction(tr("Create Pica surface viewer"), this); | ||||||
|     connect(graphicsSurfaceViewerAction, SIGNAL(triggered()), this, SLOT(OnCreateGraphicsSurfaceViewer())); |     connect(graphicsSurfaceViewerAction, SIGNAL(triggered()), this, | ||||||
|  |             SLOT(OnCreateGraphicsSurfaceViewer())); | ||||||
| 
 | 
 | ||||||
|     QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging")); |     QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging")); | ||||||
|     debug_menu->addAction(graphicsSurfaceViewerAction); |     debug_menu->addAction(graphicsSurfaceViewerAction); | ||||||
|  | @ -167,35 +159,44 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) | ||||||
|     UpdateRecentFiles(); |     UpdateRecentFiles(); | ||||||
| 
 | 
 | ||||||
|     // Setup connections
 |     // Setup connections
 | ||||||
|     connect(game_list, SIGNAL(GameChosen(QString)), this, SLOT(OnGameListLoadFile(QString)), Qt::DirectConnection); |     connect(game_list, SIGNAL(GameChosen(QString)), this, SLOT(OnGameListLoadFile(QString)), | ||||||
|  |             Qt::DirectConnection); | ||||||
|     connect(ui.action_Configure, SIGNAL(triggered()), this, SLOT(OnConfigure())); |     connect(ui.action_Configure, SIGNAL(triggered()), this, SLOT(OnConfigure())); | ||||||
|     connect(ui.action_Load_File, SIGNAL(triggered()), this, SLOT(OnMenuLoadFile()),Qt::DirectConnection); |     connect(ui.action_Load_File, SIGNAL(triggered()), this, SLOT(OnMenuLoadFile()), | ||||||
|  |             Qt::DirectConnection); | ||||||
|     connect(ui.action_Load_Symbol_Map, SIGNAL(triggered()), this, SLOT(OnMenuLoadSymbolMap())); |     connect(ui.action_Load_Symbol_Map, SIGNAL(triggered()), this, SLOT(OnMenuLoadSymbolMap())); | ||||||
|     connect(ui.action_Select_Game_List_Root, SIGNAL(triggered()), this, SLOT(OnMenuSelectGameListRoot())); |     connect(ui.action_Select_Game_List_Root, SIGNAL(triggered()), this, | ||||||
|  |             SLOT(OnMenuSelectGameListRoot())); | ||||||
|     connect(ui.action_Start, SIGNAL(triggered()), this, SLOT(OnStartGame())); |     connect(ui.action_Start, SIGNAL(triggered()), this, SLOT(OnStartGame())); | ||||||
|     connect(ui.action_Pause, SIGNAL(triggered()), this, SLOT(OnPauseGame())); |     connect(ui.action_Pause, SIGNAL(triggered()), this, SLOT(OnPauseGame())); | ||||||
|     connect(ui.action_Stop, SIGNAL(triggered()), this, SLOT(OnStopGame())); |     connect(ui.action_Stop, SIGNAL(triggered()), this, SLOT(OnStopGame())); | ||||||
|     connect(ui.action_Single_Window_Mode, SIGNAL(triggered(bool)), this, SLOT(ToggleWindowMode())); |     connect(ui.action_Single_Window_Mode, SIGNAL(triggered(bool)), this, SLOT(ToggleWindowMode())); | ||||||
| 
 | 
 | ||||||
|     connect(this, SIGNAL(EmulationStarting(EmuThread*)), disasmWidget, SLOT(OnEmulationStarting(EmuThread*))); |     connect(this, SIGNAL(EmulationStarting(EmuThread*)), disasmWidget, | ||||||
|  |             SLOT(OnEmulationStarting(EmuThread*))); | ||||||
|     connect(this, SIGNAL(EmulationStopping()), disasmWidget, SLOT(OnEmulationStopping())); |     connect(this, SIGNAL(EmulationStopping()), disasmWidget, SLOT(OnEmulationStopping())); | ||||||
|     connect(this, SIGNAL(EmulationStarting(EmuThread*)), registersWidget, SLOT(OnEmulationStarting(EmuThread*))); |     connect(this, SIGNAL(EmulationStarting(EmuThread*)), registersWidget, | ||||||
|  |             SLOT(OnEmulationStarting(EmuThread*))); | ||||||
|     connect(this, SIGNAL(EmulationStopping()), registersWidget, SLOT(OnEmulationStopping())); |     connect(this, SIGNAL(EmulationStopping()), registersWidget, SLOT(OnEmulationStopping())); | ||||||
|     connect(this, SIGNAL(EmulationStarting(EmuThread*)), render_window, SLOT(OnEmulationStarting(EmuThread*))); |     connect(this, SIGNAL(EmulationStarting(EmuThread*)), render_window, | ||||||
|  |             SLOT(OnEmulationStarting(EmuThread*))); | ||||||
|     connect(this, SIGNAL(EmulationStopping()), render_window, SLOT(OnEmulationStopping())); |     connect(this, SIGNAL(EmulationStopping()), render_window, SLOT(OnEmulationStopping())); | ||||||
|     connect(this, SIGNAL(EmulationStarting(EmuThread*)), graphicsTracingWidget, SLOT(OnEmulationStarting(EmuThread*))); |     connect(this, SIGNAL(EmulationStarting(EmuThread*)), graphicsTracingWidget, | ||||||
|  |             SLOT(OnEmulationStarting(EmuThread*))); | ||||||
|     connect(this, SIGNAL(EmulationStopping()), graphicsTracingWidget, SLOT(OnEmulationStopping())); |     connect(this, SIGNAL(EmulationStopping()), graphicsTracingWidget, SLOT(OnEmulationStopping())); | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     // Setup hotkeys
 |     // Setup hotkeys
 | ||||||
|     RegisterHotkey("Main Window", "Load File", QKeySequence::Open); |     RegisterHotkey("Main Window", "Load File", QKeySequence::Open); | ||||||
|     RegisterHotkey("Main Window", "Start Emulation"); |     RegisterHotkey("Main Window", "Start Emulation"); | ||||||
|     LoadHotkeys(); |     LoadHotkeys(); | ||||||
| 
 | 
 | ||||||
|     connect(GetHotkey("Main Window", "Load File", this), SIGNAL(activated()), this, SLOT(OnMenuLoadFile())); |     connect(GetHotkey("Main Window", "Load File", this), SIGNAL(activated()), this, | ||||||
|     connect(GetHotkey("Main Window", "Start Emulation", this), SIGNAL(activated()), this, SLOT(OnStartGame())); |             SLOT(OnMenuLoadFile())); | ||||||
|  |     connect(GetHotkey("Main Window", "Start Emulation", this), SIGNAL(activated()), this, | ||||||
|  |             SLOT(OnStartGame())); | ||||||
| 
 | 
 | ||||||
|     std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc); |     std::string window_title = | ||||||
|  |         Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc); | ||||||
|     setWindowTitle(window_title.c_str()); |     setWindowTitle(window_title.c_str()); | ||||||
| 
 | 
 | ||||||
|     show(); |     show(); | ||||||
|  | @ -208,8 +209,7 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| GMainWindow::~GMainWindow() | GMainWindow::~GMainWindow() { | ||||||
| { |  | ||||||
|     // will get automatically deleted otherwise
 |     // will get automatically deleted otherwise
 | ||||||
|     if (render_window->parent() == nullptr) |     if (render_window->parent() == nullptr) | ||||||
|         delete render_window; |         delete render_window; | ||||||
|  | @ -217,19 +217,18 @@ GMainWindow::~GMainWindow() | ||||||
|     Pica::g_debug_context.reset(); |     Pica::g_debug_context.reset(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GMainWindow::OnDisplayTitleBars(bool show) | void GMainWindow::OnDisplayTitleBars(bool show) { | ||||||
| { |  | ||||||
|     QList<QDockWidget*> widgets = findChildren<QDockWidget*>(); |     QList<QDockWidget*> widgets = findChildren<QDockWidget*>(); | ||||||
| 
 | 
 | ||||||
|     if (show) { |     if (show) { | ||||||
|         for (QDockWidget* widget: widgets) { |         for (QDockWidget* widget : widgets) { | ||||||
|             QWidget* old = widget->titleBarWidget(); |             QWidget* old = widget->titleBarWidget(); | ||||||
|             widget->setTitleBarWidget(nullptr); |             widget->setTitleBarWidget(nullptr); | ||||||
|             if (old != nullptr) |             if (old != nullptr) | ||||||
|                 delete old; |                 delete old; | ||||||
|         } |         } | ||||||
|     } else { |     } else { | ||||||
|         for (QDockWidget* widget: widgets) { |         for (QDockWidget* widget : widgets) { | ||||||
|             QWidget* old = widget->titleBarWidget(); |             QWidget* old = widget->titleBarWidget(); | ||||||
|             widget->setTitleBarWidget(new QWidget()); |             widget->setTitleBarWidget(new QWidget()); | ||||||
|             if (old != nullptr) |             if (old != nullptr) | ||||||
|  | @ -249,7 +248,8 @@ bool GMainWindow::InitializeSystem() { | ||||||
|     if (!gladLoadGL()) { |     if (!gladLoadGL()) { | ||||||
|         QMessageBox::critical(this, tr("Error while starting Citra!"), |         QMessageBox::critical(this, tr("Error while starting Citra!"), | ||||||
|                               tr("Failed to initialize the video core!\n\n" |                               tr("Failed to initialize the video core!\n\n" | ||||||
|                                  "Please ensure that your GPU supports OpenGL 3.3 and that you have the latest graphics driver.")); |                                  "Please ensure that your GPU supports OpenGL 3.3 and that you " | ||||||
|  |                                  "have the latest graphics driver.")); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -260,7 +260,8 @@ bool GMainWindow::InitializeSystem() { | ||||||
|         case System::Result::ErrorInitVideoCore: |         case System::Result::ErrorInitVideoCore: | ||||||
|             QMessageBox::critical(this, tr("Error while starting Citra!"), |             QMessageBox::critical(this, tr("Error while starting Citra!"), | ||||||
|                                   tr("Failed to initialize the video core!\n\n" |                                   tr("Failed to initialize the video core!\n\n" | ||||||
|                                      "Please ensure that your GPU supports OpenGL 3.3 and that you have the latest graphics driver.")); |                                      "Please ensure that your GPU supports OpenGL 3.3 and that you " | ||||||
|  |                                      "have the latest graphics driver.")); | ||||||
|             break; |             break; | ||||||
| 
 | 
 | ||||||
|         default: |         default: | ||||||
|  | @ -293,8 +294,12 @@ bool GMainWindow::LoadROM(const std::string& filename) { | ||||||
|             QMessageBox popup_error; |             QMessageBox popup_error; | ||||||
|             popup_error.setTextFormat(Qt::RichText); |             popup_error.setTextFormat(Qt::RichText); | ||||||
|             popup_error.setWindowTitle(tr("Error while loading ROM!")); |             popup_error.setWindowTitle(tr("Error while loading ROM!")); | ||||||
|             popup_error.setText(tr("The game that you are trying to load must be decrypted before being used with Citra.<br/><br/>" |             popup_error.setText( | ||||||
|                                   "For more information on dumping and decrypting games, please see: <a href='https://citra-emu.org/wiki/Dumping-Game-Cartridges'>https://citra-emu.org/wiki/Dumping-Game-Cartridges</a>")); |                 tr("The game that you are trying to load must be decrypted before being used with " | ||||||
|  |                    "Citra.<br/><br/>" | ||||||
|  |                    "For more information on dumping and decrypting games, please see: <a " | ||||||
|  |                    "href='https://citra-emu.org/wiki/Dumping-Game-Cartridges'>https://" | ||||||
|  |                    "citra-emu.org/wiki/Dumping-Game-Cartridges</a>")); | ||||||
|             popup_error.setIcon(QMessageBox::Critical); |             popup_error.setIcon(QMessageBox::Critical); | ||||||
|             popup_error.exec(); |             popup_error.exec(); | ||||||
|             break; |             break; | ||||||
|  | @ -306,8 +311,7 @@ bool GMainWindow::LoadROM(const std::string& filename) { | ||||||
|         case Loader::ResultStatus::Error: |         case Loader::ResultStatus::Error: | ||||||
| 
 | 
 | ||||||
|         default: |         default: | ||||||
|             QMessageBox::critical(this, tr("Error while loading ROM!"), |             QMessageBox::critical(this, tr("Error while loading ROM!"), tr("Unknown error!")); | ||||||
|                                   tr("Unknown error!")); |  | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         return false; |         return false; | ||||||
|  | @ -332,13 +336,20 @@ void GMainWindow::BootGame(const std::string& filename) { | ||||||
|     emu_thread->start(); |     emu_thread->start(); | ||||||
| 
 | 
 | ||||||
|     connect(render_window, SIGNAL(Closed()), this, SLOT(OnStopGame())); |     connect(render_window, SIGNAL(Closed()), this, SLOT(OnStopGame())); | ||||||
|     // BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views before the CPU continues
 |     // BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views
 | ||||||
|     connect(emu_thread.get(), SIGNAL(DebugModeEntered()), disasmWidget, SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection); |     // before the CPU continues
 | ||||||
|     connect(emu_thread.get(), SIGNAL(DebugModeEntered()), registersWidget, SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection); |     connect(emu_thread.get(), SIGNAL(DebugModeEntered()), disasmWidget, SLOT(OnDebugModeEntered()), | ||||||
|     connect(emu_thread.get(), SIGNAL(DebugModeEntered()), callstackWidget, SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection); |             Qt::BlockingQueuedConnection); | ||||||
|     connect(emu_thread.get(), SIGNAL(DebugModeLeft()), disasmWidget, SLOT(OnDebugModeLeft()), Qt::BlockingQueuedConnection); |     connect(emu_thread.get(), SIGNAL(DebugModeEntered()), registersWidget, | ||||||
|     connect(emu_thread.get(), SIGNAL(DebugModeLeft()), registersWidget, SLOT(OnDebugModeLeft()), Qt::BlockingQueuedConnection); |             SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection); | ||||||
|     connect(emu_thread.get(), SIGNAL(DebugModeLeft()), callstackWidget, SLOT(OnDebugModeLeft()), Qt::BlockingQueuedConnection); |     connect(emu_thread.get(), SIGNAL(DebugModeEntered()), callstackWidget, | ||||||
|  |             SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection); | ||||||
|  |     connect(emu_thread.get(), SIGNAL(DebugModeLeft()), disasmWidget, SLOT(OnDebugModeLeft()), | ||||||
|  |             Qt::BlockingQueuedConnection); | ||||||
|  |     connect(emu_thread.get(), SIGNAL(DebugModeLeft()), registersWidget, SLOT(OnDebugModeLeft()), | ||||||
|  |             Qt::BlockingQueuedConnection); | ||||||
|  |     connect(emu_thread.get(), SIGNAL(DebugModeLeft()), callstackWidget, SLOT(OnDebugModeLeft()), | ||||||
|  |             Qt::BlockingQueuedConnection); | ||||||
| 
 | 
 | ||||||
|     // Update the GUI
 |     // Update the GUI
 | ||||||
|     registersWidget->OnDebugModeEntered(); |     registersWidget->OnDebugModeEntered(); | ||||||
|  | @ -393,10 +404,12 @@ void GMainWindow::StoreRecentFile(const std::string& filename) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GMainWindow::UpdateRecentFiles() { | void GMainWindow::UpdateRecentFiles() { | ||||||
|     unsigned int num_recent_files = std::min(UISettings::values.recent_files.size(), static_cast<int>(max_recent_files_item)); |     unsigned int num_recent_files = | ||||||
|  |         std::min(UISettings::values.recent_files.size(), static_cast<int>(max_recent_files_item)); | ||||||
| 
 | 
 | ||||||
|     for (unsigned int i = 0; i < num_recent_files; i++) { |     for (unsigned int i = 0; i < num_recent_files; i++) { | ||||||
|         QString text = QString("&%1. %2").arg(i + 1).arg(QFileInfo(UISettings::values.recent_files[i]).fileName()); |         QString text = QString("&%1. %2").arg(i + 1).arg( | ||||||
|  |             QFileInfo(UISettings::values.recent_files[i]).fileName()); | ||||||
|         actions_recent_files[i]->setText(text); |         actions_recent_files[i]->setText(text); | ||||||
|         actions_recent_files[i]->setData(UISettings::values.recent_files[i]); |         actions_recent_files[i]->setData(UISettings::values.recent_files[i]); | ||||||
|         actions_recent_files[i]->setToolTip(UISettings::values.recent_files[i]); |         actions_recent_files[i]->setToolTip(UISettings::values.recent_files[i]); | ||||||
|  | @ -420,7 +433,9 @@ void GMainWindow::OnGameListLoadFile(QString game_path) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GMainWindow::OnMenuLoadFile() { | void GMainWindow::OnMenuLoadFile() { | ||||||
|     QString filename = QFileDialog::getOpenFileName(this, tr("Load File"), UISettings::values.roms_path, tr("3DS executable (*.3ds *.3dsx *.elf *.axf *.cci *.cxi)")); |     QString filename = | ||||||
|  |         QFileDialog::getOpenFileName(this, tr("Load File"), UISettings::values.roms_path, | ||||||
|  |                                      tr("3DS executable (*.3ds *.3dsx *.elf *.axf *.cci *.cxi)")); | ||||||
|     if (!filename.isEmpty()) { |     if (!filename.isEmpty()) { | ||||||
|         UISettings::values.roms_path = QFileInfo(filename).path(); |         UISettings::values.roms_path = QFileInfo(filename).path(); | ||||||
| 
 | 
 | ||||||
|  | @ -429,7 +444,8 @@ void GMainWindow::OnMenuLoadFile() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GMainWindow::OnMenuLoadSymbolMap() { | void GMainWindow::OnMenuLoadSymbolMap() { | ||||||
|     QString filename = QFileDialog::getOpenFileName(this, tr("Load Symbol Map"), UISettings::values.symbols_path, tr("Symbol map (*)")); |     QString filename = QFileDialog::getOpenFileName( | ||||||
|  |         this, tr("Load Symbol Map"), UISettings::values.symbols_path, tr("Symbol map (*)")); | ||||||
|     if (!filename.isEmpty()) { |     if (!filename.isEmpty()) { | ||||||
|         UISettings::values.symbols_path = QFileInfo(filename).path(); |         UISettings::values.symbols_path = QFileInfo(filename).path(); | ||||||
| 
 | 
 | ||||||
|  | @ -455,7 +471,8 @@ void GMainWindow::OnMenuRecentFile() { | ||||||
|         BootGame(filename.toStdString()); |         BootGame(filename.toStdString()); | ||||||
|     } else { |     } else { | ||||||
|         // Display an error message and remove the file from the list.
 |         // Display an error message and remove the file from the list.
 | ||||||
|         QMessageBox::information(this, tr("File not found"), tr("File \"%1\" not found").arg(filename)); |         QMessageBox::information(this, tr("File not found"), | ||||||
|  |                                  tr("File \"%1\" not found").arg(filename)); | ||||||
| 
 | 
 | ||||||
|         UISettings::values.recent_files.removeOne(filename); |         UISettings::values.recent_files.removeOne(filename); | ||||||
|         UpdateRecentFiles(); |         UpdateRecentFiles(); | ||||||
|  | @ -512,8 +529,7 @@ void GMainWindow::ToggleWindowMode() { | ||||||
| void GMainWindow::OnConfigure() { | void GMainWindow::OnConfigure() { | ||||||
|     ConfigureDialog configureDialog(this); |     ConfigureDialog configureDialog(this); | ||||||
|     auto result = configureDialog.exec(); |     auto result = configureDialog.exec(); | ||||||
|     if (result == QDialog::Accepted) |     if (result == QDialog::Accepted) { | ||||||
|     { |  | ||||||
|         configureDialog.applyConfiguration(); |         configureDialog.applyConfiguration(); | ||||||
|         render_window->ReloadSetKeymaps(); |         render_window->ReloadSetKeymaps(); | ||||||
|         config->Save(); |         config->Save(); | ||||||
|  | @ -531,9 +547,9 @@ bool GMainWindow::ConfirmClose() { | ||||||
|     if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) |     if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) | ||||||
|         return true; |         return true; | ||||||
| 
 | 
 | ||||||
|     auto answer = QMessageBox::question(this, tr("Citra"), |     auto answer = | ||||||
|                                         tr("Are you sure you want to close Citra?"), |         QMessageBox::question(this, tr("Citra"), tr("Are you sure you want to close Citra?"), | ||||||
|                                         QMessageBox::Yes | QMessageBox::No, QMessageBox::No); |                               QMessageBox::Yes | QMessageBox::No, QMessageBox::No); | ||||||
|     return answer != QMessageBox::No; |     return answer != QMessageBox::No; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -575,9 +591,7 @@ int main(int argc, char* argv[]) { | ||||||
|     Log::SetFilter(&log_filter); |     Log::SetFilter(&log_filter); | ||||||
| 
 | 
 | ||||||
|     MicroProfileOnThreadCreate("Frontend"); |     MicroProfileOnThreadCreate("Frontend"); | ||||||
|     SCOPE_EXIT({ |     SCOPE_EXIT({ MicroProfileShutdown(); }); | ||||||
|         MicroProfileShutdown(); |  | ||||||
|     }); |  | ||||||
| 
 | 
 | ||||||
|     // Init settings params
 |     // Init settings params
 | ||||||
|     QCoreApplication::setOrganizationName("Citra team"); |     QCoreApplication::setOrganizationName("Citra team"); | ||||||
|  | @ -586,7 +600,8 @@ int main(int argc, char* argv[]) { | ||||||
|     QApplication::setAttribute(Qt::AA_X11InitThreads); |     QApplication::setAttribute(Qt::AA_X11InitThreads); | ||||||
|     QApplication app(argc, argv); |     QApplication app(argc, argv); | ||||||
| 
 | 
 | ||||||
|     // Qt changes the locale and causes issues in float conversion using std::to_string() when generating shaders
 |     // Qt changes the locale and causes issues in float conversion using std::to_string() when
 | ||||||
|  |     // generating shaders
 | ||||||
|     setlocale(LC_ALL, "C"); |     setlocale(LC_ALL, "C"); | ||||||
| 
 | 
 | ||||||
|     GMainWindow main_window; |     GMainWindow main_window; | ||||||
|  |  | ||||||
|  | @ -7,7 +7,6 @@ | ||||||
| 
 | 
 | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <QMainWindow> | #include <QMainWindow> | ||||||
| 
 |  | ||||||
| #include "ui_main.h" | #include "ui_main.h" | ||||||
| 
 | 
 | ||||||
| class Config; | class Config; | ||||||
|  | @ -23,11 +22,11 @@ class CallstackWidget; | ||||||
| class GPUCommandStreamWidget; | class GPUCommandStreamWidget; | ||||||
| class GPUCommandListWidget; | class GPUCommandListWidget; | ||||||
| 
 | 
 | ||||||
| class GMainWindow : public QMainWindow | class GMainWindow : public QMainWindow { | ||||||
| { |  | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| 
 | 
 | ||||||
|     static const int max_recent_files_item = 10; ///< Max number of recently loaded items to keep track
 |     /// Max number of recently loaded items to keep track of
 | ||||||
|  |     static const int max_recent_files_item = 10; | ||||||
| 
 | 
 | ||||||
|     // TODO: Make use of this!
 |     // TODO: Make use of this!
 | ||||||
|     enum { |     enum { | ||||||
|  |  | ||||||
|  | @ -7,5 +7,4 @@ | ||||||
| namespace UISettings { | namespace UISettings { | ||||||
| 
 | 
 | ||||||
| Values values = {}; | Values values = {}; | ||||||
| 
 |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -4,15 +4,14 @@ | ||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <QByteArray> |  | ||||||
| #include <QStringList> |  | ||||||
| #include <QString> |  | ||||||
| 
 |  | ||||||
| #include <vector> | #include <vector> | ||||||
|  | #include <QByteArray> | ||||||
|  | #include <QString> | ||||||
|  | #include <QStringList> | ||||||
| 
 | 
 | ||||||
| namespace UISettings { | namespace UISettings { | ||||||
| 
 | 
 | ||||||
| using ContextualShortcut = std::pair<QString, int> ; | using ContextualShortcut = std::pair<QString, int>; | ||||||
| using Shortcut = std::pair<QString, ContextualShortcut>; | using Shortcut = std::pair<QString, ContextualShortcut>; | ||||||
| 
 | 
 | ||||||
| struct Values { | struct Values { | ||||||
|  | @ -43,5 +42,4 @@ struct Values { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| extern Values values; | extern Values values; | ||||||
| 
 |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,7 +1,6 @@ | ||||||
| // Licensed under GPLv2 or any later version
 | // Licensed under GPLv2 or any later version
 | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| // Copyright 2014 Tony Wasserka
 | // Copyright 2014 Tony Wasserka
 | ||||||
| // All rights reserved.
 | // All rights reserved.
 | ||||||
| //
 | //
 | ||||||
|  | @ -32,12 +31,11 @@ | ||||||
| #include <cstdlib> | #include <cstdlib> | ||||||
| #include <QLineEdit> | #include <QLineEdit> | ||||||
| #include <QRegExpValidator> | #include <QRegExpValidator> | ||||||
| 
 |  | ||||||
| #include "citra_qt/util/spinbox.h" | #include "citra_qt/util/spinbox.h" | ||||||
| #include "common/assert.h" | #include "common/assert.h" | ||||||
| 
 | 
 | ||||||
| CSpinBox::CSpinBox(QWidget* parent) : QAbstractSpinBox(parent), min_value(-100), max_value(100), value(0), base(10), num_digits(0) | CSpinBox::CSpinBox(QWidget* parent) | ||||||
| { |     : QAbstractSpinBox(parent), min_value(-100), max_value(100), value(0), base(10), num_digits(0) { | ||||||
|     // TODO: Might be nice to not immediately call the slot.
 |     // TODO: Might be nice to not immediately call the slot.
 | ||||||
|     //       Think of an address that is being replaced by a different one, in which case a lot
 |     //       Think of an address that is being replaced by a different one, in which case a lot
 | ||||||
|     //       invalid intermediate addresses would be read from during editing.
 |     //       invalid intermediate addresses would be read from during editing.
 | ||||||
|  | @ -46,8 +44,7 @@ CSpinBox::CSpinBox(QWidget* parent) : QAbstractSpinBox(parent), min_value(-100), | ||||||
|     UpdateText(); |     UpdateText(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void CSpinBox::SetValue(qint64 val) | void CSpinBox::SetValue(qint64 val) { | ||||||
| { |  | ||||||
|     auto old_value = value; |     auto old_value = value; | ||||||
|     value = std::max(std::min(val, max_value), min_value); |     value = std::max(std::min(val, max_value), min_value); | ||||||
| 
 | 
 | ||||||
|  | @ -57,8 +54,7 @@ void CSpinBox::SetValue(qint64 val) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void CSpinBox::SetRange(qint64 min, qint64 max) | void CSpinBox::SetRange(qint64 min, qint64 max) { | ||||||
| { |  | ||||||
|     min_value = min; |     min_value = min; | ||||||
|     max_value = max; |     max_value = max; | ||||||
| 
 | 
 | ||||||
|  | @ -66,8 +62,7 @@ void CSpinBox::SetRange(qint64 min, qint64 max) | ||||||
|     UpdateText(); |     UpdateText(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void CSpinBox::stepBy(int steps) | void CSpinBox::stepBy(int steps) { | ||||||
| { |  | ||||||
|     auto new_value = value; |     auto new_value = value; | ||||||
|     // Scale number of steps by the currently selected digit
 |     // Scale number of steps by the currently selected digit
 | ||||||
|     // TODO: Move this code elsewhere and enable it.
 |     // TODO: Move this code elsewhere and enable it.
 | ||||||
|  | @ -93,8 +88,7 @@ void CSpinBox::stepBy(int steps) | ||||||
|     UpdateText(); |     UpdateText(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| QAbstractSpinBox::StepEnabled CSpinBox::stepEnabled() const | QAbstractSpinBox::StepEnabled CSpinBox::stepEnabled() const { | ||||||
| { |  | ||||||
|     StepEnabled ret = StepNone; |     StepEnabled ret = StepNone; | ||||||
| 
 | 
 | ||||||
|     if (value > min_value) |     if (value > min_value) | ||||||
|  | @ -106,29 +100,25 @@ QAbstractSpinBox::StepEnabled CSpinBox::stepEnabled() const | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void CSpinBox::SetBase(int base) | void CSpinBox::SetBase(int base) { | ||||||
| { |  | ||||||
|     this->base = base; |     this->base = base; | ||||||
| 
 | 
 | ||||||
|     UpdateText(); |     UpdateText(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void CSpinBox::SetNumDigits(int num_digits) | void CSpinBox::SetNumDigits(int num_digits) { | ||||||
| { |  | ||||||
|     this->num_digits = num_digits; |     this->num_digits = num_digits; | ||||||
| 
 | 
 | ||||||
|     UpdateText(); |     UpdateText(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void CSpinBox::SetPrefix(const QString& prefix) | void CSpinBox::SetPrefix(const QString& prefix) { | ||||||
| { |  | ||||||
|     this->prefix = prefix; |     this->prefix = prefix; | ||||||
| 
 | 
 | ||||||
|     UpdateText(); |     UpdateText(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void CSpinBox::SetSuffix(const QString& suffix) | void CSpinBox::SetSuffix(const QString& suffix) { | ||||||
| { |  | ||||||
|     this->suffix = suffix; |     this->suffix = suffix; | ||||||
| 
 | 
 | ||||||
|     UpdateText(); |     UpdateText(); | ||||||
|  | @ -161,8 +151,7 @@ static QString StringToInputMask(const QString& input) { | ||||||
|     return mask; |     return mask; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void CSpinBox::UpdateText() | void CSpinBox::UpdateText() { | ||||||
| { |  | ||||||
|     // If a fixed number of digits is used, we put the line edit in insertion mode by setting an
 |     // If a fixed number of digits is used, we put the line edit in insertion mode by setting an
 | ||||||
|     // input mask.
 |     // input mask.
 | ||||||
|     QString mask; |     QString mask; | ||||||
|  | @ -179,10 +168,9 @@ void CSpinBox::UpdateText() | ||||||
|         // The greatest signed 64-bit number has 19 decimal digits.
 |         // The greatest signed 64-bit number has 19 decimal digits.
 | ||||||
|         // TODO: Could probably make this more generic with some logarithms.
 |         // TODO: Could probably make this more generic with some logarithms.
 | ||||||
|         // For reference, unsigned 64-bit can have up to 20 decimal digits.
 |         // For reference, unsigned 64-bit can have up to 20 decimal digits.
 | ||||||
|         int digits = (num_digits != 0) ? num_digits |         int digits = (num_digits != 0) | ||||||
|                      : (base == 16) ? 16 |                          ? num_digits | ||||||
|                      : (base == 10) ? 19 |                          : (base == 16) ? 16 : (base == 10) ? 19 : 0xFF; // fallback case...
 | ||||||
|                      : 0xFF; // fallback case...
 |  | ||||||
| 
 | 
 | ||||||
|         // Match num_digits digits
 |         // Match num_digits digits
 | ||||||
|         // Digits irrelevant to the chosen number base are filtered in the validator
 |         // Digits irrelevant to the chosen number base are filtered in the validator
 | ||||||
|  | @ -203,29 +191,24 @@ void CSpinBox::UpdateText() | ||||||
|     lineEdit()->setCursorPosition(cursor_position); |     lineEdit()->setCursorPosition(cursor_position); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| QString CSpinBox::TextFromValue() | QString CSpinBox::TextFromValue() { | ||||||
| { |     return prefix + QString(HasSign() ? ((value < 0) ? "-" : "+") : "") + | ||||||
|     return prefix |            QString("%1").arg(std::abs(value), num_digits, base, QLatin1Char('0')).toUpper() + | ||||||
|            + QString(HasSign() ? ((value < 0) ? "-" : "+") : "") |            suffix; | ||||||
|            + QString("%1").arg(std::abs(value), num_digits, base, QLatin1Char('0')).toUpper() |  | ||||||
|            + suffix; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| qint64 CSpinBox::ValueFromText() | qint64 CSpinBox::ValueFromText() { | ||||||
| { |  | ||||||
|     unsigned strpos = prefix.length(); |     unsigned strpos = prefix.length(); | ||||||
| 
 | 
 | ||||||
|     QString num_string = text().mid(strpos, text().length() - strpos - suffix.length()); |     QString num_string = text().mid(strpos, text().length() - strpos - suffix.length()); | ||||||
|     return num_string.toLongLong(nullptr, base); |     return num_string.toLongLong(nullptr, base); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool CSpinBox::HasSign() const | bool CSpinBox::HasSign() const { | ||||||
| { |  | ||||||
|     return base == 10 && min_value < 0; |     return base == 10 && min_value < 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void CSpinBox::OnEditingFinished() | void CSpinBox::OnEditingFinished() { | ||||||
| { |  | ||||||
|     // Only update for valid input
 |     // Only update for valid input
 | ||||||
|     QString input = lineEdit()->text(); |     QString input = lineEdit()->text(); | ||||||
|     int pos = 0; |     int pos = 0; | ||||||
|  | @ -233,8 +216,7 @@ void CSpinBox::OnEditingFinished() | ||||||
|         SetValue(ValueFromText()); |         SetValue(ValueFromText()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| QValidator::State CSpinBox::validate(QString& input, int& pos) const | QValidator::State CSpinBox::validate(QString& input, int& pos) const { | ||||||
| { |  | ||||||
|     if (!prefix.isEmpty() && input.left(prefix.length()) != prefix) |     if (!prefix.isEmpty() && input.left(prefix.length()) != prefix) | ||||||
|         return QValidator::Invalid; |         return QValidator::Invalid; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,7 +1,6 @@ | ||||||
| // Licensed under GPLv2 or any later version
 | // Licensed under GPLv2 or any later version
 | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| // Copyright 2014 Tony Wasserka
 | // Copyright 2014 Tony Wasserka
 | ||||||
| // All rights reserved.
 | // All rights reserved.
 | ||||||
| //
 | //
 | ||||||
|  | @ -29,7 +28,6 @@ | ||||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <QAbstractSpinBox> | #include <QAbstractSpinBox> | ||||||
|  |  | ||||||
|  | @ -4,7 +4,6 @@ | ||||||
| 
 | 
 | ||||||
| #include <array> | #include <array> | ||||||
| #include <cmath> | #include <cmath> | ||||||
| 
 |  | ||||||
| #include "citra_qt/util/util.h" | #include "citra_qt/util/util.h" | ||||||
| 
 | 
 | ||||||
| QFont GetMonospaceFont() { | QFont GetMonospaceFont() { | ||||||
|  | @ -16,10 +15,12 @@ QFont GetMonospaceFont() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| QString ReadableByteSize(qulonglong size) { | QString ReadableByteSize(qulonglong size) { | ||||||
|     static const std::array<const char*, 6> units = { "B", "KiB", "MiB", "GiB", "TiB", "PiB" }; |     static const std::array<const char*, 6> units = {"B", "KiB", "MiB", "GiB", "TiB", "PiB"}; | ||||||
|     if (size == 0) |     if (size == 0) | ||||||
|         return "0"; |         return "0"; | ||||||
|     int digit_groups = std::min<int>(static_cast<int>(std::log10(size) / std::log10(1024)), static_cast<int>(units.size())); |     int digit_groups = std::min<int>(static_cast<int>(std::log10(size) / std::log10(1024)), | ||||||
|     return QString("%L1 %2").arg(size / std::pow(1024, digit_groups), 0, 'f', 1) |                                      static_cast<int>(units.size())); | ||||||
|                             .arg(units[digit_groups]); |     return QString("%L1 %2") | ||||||
|  |         .arg(size / std::pow(1024, digit_groups), 0, 'f', 1) | ||||||
|  |         .arg(units[digit_groups]); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -5,7 +5,6 @@ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <cstdlib> | #include <cstdlib> | ||||||
| 
 |  | ||||||
| #include "common/common_funcs.h" | #include "common/common_funcs.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
| 
 | 
 | ||||||
|  | @ -18,25 +17,29 @@ | ||||||
| // enough for our purposes.
 | // enough for our purposes.
 | ||||||
| template <typename Fn> | template <typename Fn> | ||||||
| #if defined(_MSC_VER) | #if defined(_MSC_VER) | ||||||
|     __declspec(noinline, noreturn) | __declspec(noinline, noreturn) | ||||||
| #elif defined(__GNUC__) | #elif defined(__GNUC__) | ||||||
|     __attribute__((noinline, noreturn, cold)) |     __attribute__((noinline, noreturn, cold)) | ||||||
| #endif | #endif | ||||||
| static void assert_noinline_call(const Fn& fn) { |     static void assert_noinline_call(const Fn& fn) { | ||||||
|     fn(); |     fn(); | ||||||
|     Crash(); |     Crash(); | ||||||
|     exit(1); // Keeps GCC's mouth shut about this actually returning
 |     exit(1); // Keeps GCC's mouth shut about this actually returning
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #define ASSERT(_a_) \ | #define ASSERT(_a_)                                                                                \ | ||||||
|     do if (!(_a_)) { assert_noinline_call([] { \ |     do                                                                                             \ | ||||||
|         LOG_CRITICAL(Debug, "Assertion Failed!"); \ |         if (!(_a_)) {                                                                              \ | ||||||
|     }); } while (0) |             assert_noinline_call([] { LOG_CRITICAL(Debug, "Assertion Failed!"); });                \ | ||||||
|  |         }                                                                                          \ | ||||||
|  |     while (0) | ||||||
| 
 | 
 | ||||||
| #define ASSERT_MSG(_a_, ...) \ | #define ASSERT_MSG(_a_, ...)                                                                       \ | ||||||
|     do if (!(_a_)) { assert_noinline_call([&] { \ |     do                                                                                             \ | ||||||
|         LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); \ |         if (!(_a_)) {                                                                              \ | ||||||
|     }); } while (0) |             assert_noinline_call([&] { LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); }); \ | ||||||
|  |         }                                                                                          \ | ||||||
|  |     while (0) | ||||||
| 
 | 
 | ||||||
| #define UNREACHABLE() ASSERT_MSG(false, "Unreachable code!") | #define UNREACHABLE() ASSERT_MSG(false, "Unreachable code!") | ||||||
| #define UNREACHABLE_MSG(...) ASSERT_MSG(false, __VA_ARGS__) | #define UNREACHABLE_MSG(...) ASSERT_MSG(false, __VA_ARGS__) | ||||||
|  | @ -50,4 +53,4 @@ static void assert_noinline_call(const Fn& fn) { | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #define UNIMPLEMENTED() DEBUG_ASSERT_MSG(false, "Unimplemented code!") | #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
 | // Licensed under GPLv2 or any later version
 | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| // Copyright 2014 Tony Wasserka
 | // Copyright 2014 Tony Wasserka
 | ||||||
| // All rights reserved.
 | // All rights reserved.
 | ||||||
| //
 | //
 | ||||||
|  | @ -29,13 +28,11 @@ | ||||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <cstddef> | #include <cstddef> | ||||||
| #include <limits> | #include <limits> | ||||||
| #include <type_traits> | #include <type_traits> | ||||||
| 
 |  | ||||||
| #include "common/common_funcs.h" | #include "common/common_funcs.h" | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  | @ -111,9 +108,8 @@ | ||||||
|  * symptoms. |  * symptoms. | ||||||
|  */ |  */ | ||||||
| #pragma pack(1) | #pragma pack(1) | ||||||
| template<std::size_t position, std::size_t bits, typename T> | template <std::size_t position, std::size_t bits, typename T> | ||||||
| struct BitField | struct BitField { | ||||||
| { |  | ||||||
| private: | private: | ||||||
|     // We hide the copy assigment operator here, because the default copy
 |     // We hide the copy assigment operator here, because the default copy
 | ||||||
|     // assignment would copy the full storage value, rather than just the bits
 |     // assignment would copy the full storage value, rather than just the bits
 | ||||||
|  | @ -141,13 +137,10 @@ public: | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     FORCE_INLINE T Value() const { |     FORCE_INLINE T Value() const { | ||||||
|         if (std::numeric_limits<T>::is_signed) |         if (std::numeric_limits<T>::is_signed) { | ||||||
|         { |             std::size_t shift = 8 * sizeof(T) - bits; | ||||||
|             std::size_t shift = 8 * sizeof(T)-bits; |  | ||||||
|             return (T)((storage << (shift - position)) >> shift); |             return (T)((storage << (shift - position)) >> shift); | ||||||
|         } |         } else { | ||||||
|         else |  | ||||||
|         { |  | ||||||
|             return (T)((storage & GetMask()) >> position); |             return (T)((storage & GetMask()) >> position); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | @ -162,15 +155,14 @@ private: | ||||||
|     // T is an enumeration. Note that T is wrapped within an enable_if in the
 |     // T is an enumeration. Note that T is wrapped within an enable_if in the
 | ||||||
|     // former case to workaround compile errors which arise when using
 |     // former case to workaround compile errors which arise when using
 | ||||||
|     // std::underlying_type<T>::type directly.
 |     // std::underlying_type<T>::type directly.
 | ||||||
|     typedef typename std::conditional < std::is_enum<T>::value, |     typedef typename std::conditional<std::is_enum<T>::value, std::underlying_type<T>, | ||||||
|         std::underlying_type<T>, |                                       std::enable_if<true, T>>::type::type StorageType; | ||||||
|         std::enable_if < true, T >> ::type::type StorageType; |  | ||||||
| 
 | 
 | ||||||
|     // Unsigned version of StorageType
 |     // Unsigned version of StorageType
 | ||||||
|     typedef typename std::make_unsigned<StorageType>::type StorageTypeU; |     typedef typename std::make_unsigned<StorageType>::type StorageTypeU; | ||||||
| 
 | 
 | ||||||
|     FORCE_INLINE StorageType GetMask() const { |     FORCE_INLINE StorageType GetMask() const { | ||||||
|         return (((StorageTypeU)~0) >> (8 * sizeof(T)-bits)) << position; |         return (((StorageTypeU)~0) >> (8 * sizeof(T) - bits)) << position; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     StorageType storage; |     StorageType storage; | ||||||
|  | @ -186,5 +178,6 @@ private: | ||||||
| #pragma pack() | #pragma pack() | ||||||
| 
 | 
 | ||||||
| #if (__GNUC__ >= 5) || defined(__clang__) || defined(_MSC_VER) | #if (__GNUC__ >= 5) || defined(__clang__) || defined(_MSC_VER) | ||||||
| static_assert(std::is_trivially_copyable<BitField<0, 1, unsigned>>::value, "BitField must be trivially copyable"); | static_assert(std::is_trivially_copyable<BitField<0, 1, unsigned>>::value, | ||||||
|  |               "BitField must be trivially copyable"); | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | @ -18,49 +18,60 @@ namespace Common { | ||||||
| 
 | 
 | ||||||
| #ifdef _WIN32 | #ifdef _WIN32 | ||||||
| template <typename T> | template <typename T> | ||||||
| static inline int CountSetBits(T v) | static inline int CountSetBits(T v) { | ||||||
| { |  | ||||||
|     // from https://graphics.stanford.edu/~seander/bithacks.html
 |     // from https://graphics.stanford.edu/~seander/bithacks.html
 | ||||||
|     // GCC has this built in, but MSVC's intrinsic will only emit the actual
 |     // GCC has this built in, but MSVC's intrinsic will only emit the actual
 | ||||||
|     // POPCNT instruction, which we're not depending on
 |     // POPCNT instruction, which we're not depending on
 | ||||||
|     v = v - ((v >> 1) & (T)~(T)0/3); |     v = v - ((v >> 1) & (T) ~(T)0 / 3); | ||||||
|     v = (v & (T)~(T)0/15*3) + ((v >> 2) & (T)~(T)0/15*3); |     v = (v & (T) ~(T)0 / 15 * 3) + ((v >> 2) & (T) ~(T)0 / 15 * 3); | ||||||
|     v = (v + (v >> 4)) & (T)~(T)0/255*15; |     v = (v + (v >> 4)) & (T) ~(T)0 / 255 * 15; | ||||||
|     return (T)(v * ((T)~(T)0/255)) >> (sizeof(T) - 1) * 8; |     return (T)(v * ((T) ~(T)0 / 255)) >> (sizeof(T) - 1) * 8; | ||||||
| } | } | ||||||
| static inline int LeastSignificantSetBit(u8 val) | static inline int LeastSignificantSetBit(u8 val) { | ||||||
| { |  | ||||||
|     unsigned long index; |     unsigned long index; | ||||||
|     _BitScanForward(&index, val); |     _BitScanForward(&index, val); | ||||||
|     return (int)index; |     return (int)index; | ||||||
| } | } | ||||||
| static inline int LeastSignificantSetBit(u16 val) | static inline int LeastSignificantSetBit(u16 val) { | ||||||
| { |  | ||||||
|     unsigned long index; |     unsigned long index; | ||||||
|     _BitScanForward(&index, val); |     _BitScanForward(&index, val); | ||||||
|     return (int)index; |     return (int)index; | ||||||
| } | } | ||||||
| static inline int LeastSignificantSetBit(u32 val) | static inline int LeastSignificantSetBit(u32 val) { | ||||||
| { |  | ||||||
|     unsigned long index; |     unsigned long index; | ||||||
|     _BitScanForward(&index, val); |     _BitScanForward(&index, val); | ||||||
|     return (int)index; |     return (int)index; | ||||||
| } | } | ||||||
| static inline int LeastSignificantSetBit(u64 val) | static inline int LeastSignificantSetBit(u64 val) { | ||||||
| { |  | ||||||
|     unsigned long index; |     unsigned long index; | ||||||
|     _BitScanForward64(&index, val); |     _BitScanForward64(&index, val); | ||||||
|     return (int)index; |     return (int)index; | ||||||
| } | } | ||||||
| #else | #else | ||||||
| static inline int CountSetBits(u8 val) { return __builtin_popcount(val); } | static inline int CountSetBits(u8 val) { | ||||||
| static inline int CountSetBits(u16 val) { return __builtin_popcount(val); } |     return __builtin_popcount(val); | ||||||
| static inline int CountSetBits(u32 val) { return __builtin_popcount(val); } | } | ||||||
| static inline int CountSetBits(u64 val) { return __builtin_popcountll(val); } | static inline int CountSetBits(u16 val) { | ||||||
| static inline int LeastSignificantSetBit(u8 val) { return __builtin_ctz(val); } |     return __builtin_popcount(val); | ||||||
| static inline int LeastSignificantSetBit(u16 val) { return __builtin_ctz(val); } | } | ||||||
| static inline int LeastSignificantSetBit(u32 val) { return __builtin_ctz(val); } | static inline int CountSetBits(u32 val) { | ||||||
| static inline int LeastSignificantSetBit(u64 val) { return __builtin_ctzll(val); } |     return __builtin_popcount(val); | ||||||
|  | } | ||||||
|  | static inline int CountSetBits(u64 val) { | ||||||
|  |     return __builtin_popcountll(val); | ||||||
|  | } | ||||||
|  | static inline int LeastSignificantSetBit(u8 val) { | ||||||
|  |     return __builtin_ctz(val); | ||||||
|  | } | ||||||
|  | static inline int LeastSignificantSetBit(u16 val) { | ||||||
|  |     return __builtin_ctz(val); | ||||||
|  | } | ||||||
|  | static inline int LeastSignificantSetBit(u32 val) { | ||||||
|  |     return __builtin_ctz(val); | ||||||
|  | } | ||||||
|  | static inline int LeastSignificantSetBit(u64 val) { | ||||||
|  |     return __builtin_ctzll(val); | ||||||
|  | } | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| // Similar to std::bitset, this is a class which encapsulates a bitset, i.e.
 | // Similar to std::bitset, this is a class which encapsulates a bitset, i.e.
 | ||||||
|  | @ -84,57 +95,62 @@ static inline int LeastSignificantSetBit(u64 val) { return __builtin_ctzll(val); | ||||||
| // TODO: use constexpr when MSVC gets out of the Dark Ages
 | // TODO: use constexpr when MSVC gets out of the Dark Ages
 | ||||||
| 
 | 
 | ||||||
| template <typename IntTy> | template <typename IntTy> | ||||||
| class BitSet | class BitSet { | ||||||
| { |  | ||||||
|     static_assert(!std::is_signed<IntTy>::value, "BitSet should not be used with signed types"); |     static_assert(!std::is_signed<IntTy>::value, "BitSet should not be used with signed types"); | ||||||
|  | 
 | ||||||
| public: | public: | ||||||
|     // A reference to a particular bit, returned from operator[].
 |     // A reference to a particular bit, returned from operator[].
 | ||||||
|     class Ref |     class Ref { | ||||||
|     { |  | ||||||
|     public: |     public: | ||||||
|         Ref(Ref&& other) : m_bs(other.m_bs), m_mask(other.m_mask) {} |         Ref(Ref&& other) : m_bs(other.m_bs), m_mask(other.m_mask) {} | ||||||
|         Ref(BitSet* bs, IntTy mask) : m_bs(bs), m_mask(mask) {} |         Ref(BitSet* bs, IntTy mask) : m_bs(bs), m_mask(mask) {} | ||||||
|         operator bool() const { return (m_bs->m_val & m_mask) != 0; } |         operator bool() const { | ||||||
|         bool operator=(bool set) |             return (m_bs->m_val & m_mask) != 0; | ||||||
|         { |         } | ||||||
|  |         bool operator=(bool set) { | ||||||
|             m_bs->m_val = (m_bs->m_val & ~m_mask) | (set ? m_mask : 0); |             m_bs->m_val = (m_bs->m_val & ~m_mask) | (set ? m_mask : 0); | ||||||
|             return set; |             return set; | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|     private: |     private: | ||||||
|         BitSet* m_bs; |         BitSet* m_bs; | ||||||
|         IntTy m_mask; |         IntTy m_mask; | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     // A STL-like iterator is required to be able to use range-based for loops.
 |     // A STL-like iterator is required to be able to use range-based for loops.
 | ||||||
|     class Iterator |     class Iterator { | ||||||
|     { |  | ||||||
|     public: |     public: | ||||||
|         Iterator(const Iterator& other) : m_val(other.m_val), m_bit(other.m_bit) {} |         Iterator(const Iterator& other) : m_val(other.m_val), m_bit(other.m_bit) {} | ||||||
|         Iterator(IntTy val, int bit) : m_val(val), m_bit(bit) {} |         Iterator(IntTy val, int bit) : m_val(val), m_bit(bit) {} | ||||||
|         Iterator& operator=(Iterator other) { new (this) Iterator(other); return *this; } |         Iterator& operator=(Iterator other) { | ||||||
|         int operator*() { return m_bit; } |             new (this) Iterator(other); | ||||||
|         Iterator& operator++() |             return *this; | ||||||
|         { |         } | ||||||
|             if (m_val == 0) |         int operator*() { | ||||||
|             { |             return m_bit; | ||||||
|  |         } | ||||||
|  |         Iterator& operator++() { | ||||||
|  |             if (m_val == 0) { | ||||||
|                 m_bit = -1; |                 m_bit = -1; | ||||||
|             } |             } else { | ||||||
|             else |  | ||||||
|             { |  | ||||||
|                 int bit = LeastSignificantSetBit(m_val); |                 int bit = LeastSignificantSetBit(m_val); | ||||||
|                 m_val &= ~(1 << bit); |                 m_val &= ~(1 << bit); | ||||||
|                 m_bit = bit; |                 m_bit = bit; | ||||||
|             } |             } | ||||||
|             return *this; |             return *this; | ||||||
|         } |         } | ||||||
|         Iterator operator++(int _) |         Iterator operator++(int _) { | ||||||
|         { |  | ||||||
|             Iterator other(*this); |             Iterator other(*this); | ||||||
|             ++*this; |             ++*this; | ||||||
|             return other; |             return other; | ||||||
|         } |         } | ||||||
|         bool operator==(Iterator other) const { return m_bit == other.m_bit; } |         bool operator==(Iterator other) const { | ||||||
|         bool operator!=(Iterator other) const { return m_bit != other.m_bit; } |             return m_bit == other.m_bit; | ||||||
|  |         } | ||||||
|  |         bool operator!=(Iterator other) const { | ||||||
|  |             return m_bit != other.m_bit; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|     private: |     private: | ||||||
|         IntTy m_val; |         IntTy m_val; | ||||||
|         int m_bit; |         int m_bit; | ||||||
|  | @ -142,42 +158,75 @@ public: | ||||||
| 
 | 
 | ||||||
|     BitSet() : m_val(0) {} |     BitSet() : m_val(0) {} | ||||||
|     explicit BitSet(IntTy val) : m_val(val) {} |     explicit BitSet(IntTy val) : m_val(val) {} | ||||||
|     BitSet(std::initializer_list<int> init) |     BitSet(std::initializer_list<int> init) { | ||||||
|     { |  | ||||||
|         m_val = 0; |         m_val = 0; | ||||||
|         for (int bit : init) |         for (int bit : init) | ||||||
|             m_val |= (IntTy)1 << bit; |             m_val |= (IntTy)1 << bit; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     static BitSet AllTrue(size_t count) |     static BitSet AllTrue(size_t count) { | ||||||
|     { |         return BitSet(count == sizeof(IntTy) * 8 ? ~(IntTy)0 : (((IntTy)1 << count) - 1)); | ||||||
|         return BitSet(count == sizeof(IntTy)*8 ? ~(IntTy)0 : (((IntTy)1 << count) - 1)); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     Ref operator[](size_t bit) { return Ref(this, (IntTy)1 << bit); } |     Ref operator[](size_t bit) { | ||||||
|     const Ref operator[](size_t bit) const { return (*const_cast<BitSet*>(this))[bit]; } |         return Ref(this, (IntTy)1 << bit); | ||||||
|     bool operator==(BitSet other) const { return m_val == other.m_val; } |     } | ||||||
|     bool operator!=(BitSet other) const { return m_val != other.m_val; } |     const Ref operator[](size_t bit) const { | ||||||
|     bool operator<(BitSet other) const { return m_val < other.m_val; } |         return (*const_cast<BitSet*>(this))[bit]; | ||||||
|     bool operator>(BitSet other) const { return m_val > other.m_val; } |     } | ||||||
|     BitSet operator|(BitSet other) const { return BitSet(m_val | other.m_val); } |     bool operator==(BitSet other) const { | ||||||
|     BitSet operator&(BitSet other) const { return BitSet(m_val & other.m_val); } |         return m_val == other.m_val; | ||||||
|     BitSet operator^(BitSet other) const { return BitSet(m_val ^ other.m_val); } |     } | ||||||
|     BitSet operator~() const { return BitSet(~m_val); } |     bool operator!=(BitSet other) const { | ||||||
|     BitSet& operator|=(BitSet other) { return *this = *this | other; } |         return m_val != other.m_val; | ||||||
|     BitSet& operator&=(BitSet other) { return *this = *this & other; } |     } | ||||||
|     BitSet& operator^=(BitSet other) { return *this = *this ^ other; } |     bool operator<(BitSet other) const { | ||||||
|  |         return m_val < other.m_val; | ||||||
|  |     } | ||||||
|  |     bool operator>(BitSet other) const { | ||||||
|  |         return m_val > other.m_val; | ||||||
|  |     } | ||||||
|  |     BitSet operator|(BitSet other) const { | ||||||
|  |         return BitSet(m_val | other.m_val); | ||||||
|  |     } | ||||||
|  |     BitSet operator&(BitSet other) const { | ||||||
|  |         return BitSet(m_val & other.m_val); | ||||||
|  |     } | ||||||
|  |     BitSet operator^(BitSet other) const { | ||||||
|  |         return BitSet(m_val ^ other.m_val); | ||||||
|  |     } | ||||||
|  |     BitSet operator~() const { | ||||||
|  |         return BitSet(~m_val); | ||||||
|  |     } | ||||||
|  |     BitSet& operator|=(BitSet other) { | ||||||
|  |         return *this = *this | other; | ||||||
|  |     } | ||||||
|  |     BitSet& operator&=(BitSet other) { | ||||||
|  |         return *this = *this & other; | ||||||
|  |     } | ||||||
|  |     BitSet& operator^=(BitSet other) { | ||||||
|  |         return *this = *this ^ other; | ||||||
|  |     } | ||||||
|     operator u32() = delete; |     operator u32() = delete; | ||||||
|     operator bool() { return m_val != 0; } |     operator bool() { | ||||||
|  |         return m_val != 0; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     // Warning: Even though on modern CPUs this is a single fast instruction,
 |     // Warning: Even though on modern CPUs this is a single fast instruction,
 | ||||||
|     // Dolphin's official builds do not currently assume POPCNT support on x86,
 |     // Dolphin's official builds do not currently assume POPCNT support on x86,
 | ||||||
|     // so slower explicit bit twiddling is generated.  Still should generally
 |     // so slower explicit bit twiddling is generated.  Still should generally
 | ||||||
|     // be faster than a loop.
 |     // be faster than a loop.
 | ||||||
|     unsigned int Count() const { return CountSetBits(m_val); } |     unsigned int Count() const { | ||||||
|  |         return CountSetBits(m_val); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     Iterator begin() const { Iterator it(m_val, 0); return ++it; } |     Iterator begin() const { | ||||||
|     Iterator end() const { return Iterator(m_val, -1); } |         Iterator it(m_val, 0); | ||||||
|  |         return ++it; | ||||||
|  |     } | ||||||
|  |     Iterator end() const { | ||||||
|  |         return Iterator(m_val, -1); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     IntTy m_val; |     IntTy m_val; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -2,33 +2,29 @@ | ||||||
| // Licensed under GPLv2 or any later version
 | // Licensed under GPLv2 or any later version
 | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
|  | #include <algorithm> | ||||||
|  | #include <sstream> | ||||||
| #include "common/break_points.h" | #include "common/break_points.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
| 
 | 
 | ||||||
| #include <sstream> | bool BreakPoints::IsAddressBreakPoint(u32 iAddress) const { | ||||||
| #include <algorithm> |  | ||||||
| 
 |  | ||||||
| bool BreakPoints::IsAddressBreakPoint(u32 iAddress) const |  | ||||||
| { |  | ||||||
|     auto cond = [&iAddress](const TBreakPoint& bp) { return bp.iAddress == iAddress; }; |     auto cond = [&iAddress](const TBreakPoint& bp) { return bp.iAddress == iAddress; }; | ||||||
|     auto it   = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond); |     auto it = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond); | ||||||
|     return it != m_BreakPoints.end(); |     return it != m_BreakPoints.end(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool BreakPoints::IsTempBreakPoint(u32 iAddress) const | bool BreakPoints::IsTempBreakPoint(u32 iAddress) const { | ||||||
| { |     auto cond = [&iAddress](const TBreakPoint& bp) { | ||||||
|     auto cond = [&iAddress](const TBreakPoint& bp) { return bp.iAddress == iAddress && bp.bTemporary; }; |         return bp.iAddress == iAddress && bp.bTemporary; | ||||||
|     auto it   = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond); |     }; | ||||||
|  |     auto it = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond); | ||||||
|     return it != m_BreakPoints.end(); |     return it != m_BreakPoints.end(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| BreakPoints::TBreakPointsStr BreakPoints::GetStrings() const | BreakPoints::TBreakPointsStr BreakPoints::GetStrings() const { | ||||||
| { |  | ||||||
|     TBreakPointsStr bps; |     TBreakPointsStr bps; | ||||||
|     for (auto breakpoint : m_BreakPoints) |     for (auto breakpoint : m_BreakPoints) { | ||||||
|     { |         if (!breakpoint.bTemporary) { | ||||||
|         if (!breakpoint.bTemporary) |  | ||||||
|         { |  | ||||||
|             std::stringstream bp; |             std::stringstream bp; | ||||||
|             bp << std::hex << breakpoint.iAddress << " " << (breakpoint.bOn ? "n" : ""); |             bp << std::hex << breakpoint.iAddress << " " << (breakpoint.bOn ? "n" : ""); | ||||||
|             bps.push_back(bp.str()); |             bps.push_back(bp.str()); | ||||||
|  | @ -38,10 +34,8 @@ BreakPoints::TBreakPointsStr BreakPoints::GetStrings() const | ||||||
|     return bps; |     return bps; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void BreakPoints::AddFromStrings(const TBreakPointsStr& bps) | void BreakPoints::AddFromStrings(const TBreakPointsStr& bps) { | ||||||
| { |     for (auto bps_item : bps) { | ||||||
|     for (auto bps_item : bps) |  | ||||||
|     { |  | ||||||
|         TBreakPoint bp; |         TBreakPoint bp; | ||||||
|         std::stringstream bpstr; |         std::stringstream bpstr; | ||||||
|         bpstr << std::hex << bps_item; |         bpstr << std::hex << bps_item; | ||||||
|  | @ -52,18 +46,15 @@ void BreakPoints::AddFromStrings(const TBreakPointsStr& bps) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void BreakPoints::Add(const TBreakPoint& bp) | void BreakPoints::Add(const TBreakPoint& bp) { | ||||||
| { |     if (!IsAddressBreakPoint(bp.iAddress)) { | ||||||
|     if (!IsAddressBreakPoint(bp.iAddress)) |  | ||||||
|     { |  | ||||||
|         m_BreakPoints.push_back(bp); |         m_BreakPoints.push_back(bp); | ||||||
|         //if (jit)
 |         // if (jit)
 | ||||||
|         //    jit->GetBlockCache()->InvalidateICache(bp.iAddress, 4);
 |         //    jit->GetBlockCache()->InvalidateICache(bp.iAddress, 4);
 | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void BreakPoints::Add(u32 em_address, bool temp) | void BreakPoints::Add(u32 em_address, bool temp) { | ||||||
| { |  | ||||||
|     if (!IsAddressBreakPoint(em_address)) // only add new addresses
 |     if (!IsAddressBreakPoint(em_address)) // only add new addresses
 | ||||||
|     { |     { | ||||||
|         TBreakPoint pt; // breakpoint settings
 |         TBreakPoint pt; // breakpoint settings
 | ||||||
|  | @ -73,22 +64,20 @@ void BreakPoints::Add(u32 em_address, bool temp) | ||||||
| 
 | 
 | ||||||
|         m_BreakPoints.push_back(pt); |         m_BreakPoints.push_back(pt); | ||||||
| 
 | 
 | ||||||
|         //if (jit)
 |         // if (jit)
 | ||||||
|         //    jit->GetBlockCache()->InvalidateICache(em_address, 4);
 |         //    jit->GetBlockCache()->InvalidateICache(em_address, 4);
 | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void BreakPoints::Remove(u32 em_address) | void BreakPoints::Remove(u32 em_address) { | ||||||
| { |  | ||||||
|     auto cond = [&em_address](const TBreakPoint& bp) { return bp.iAddress == em_address; }; |     auto cond = [&em_address](const TBreakPoint& bp) { return bp.iAddress == em_address; }; | ||||||
|     auto it   = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond); |     auto it = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond); | ||||||
|     if (it != m_BreakPoints.end()) |     if (it != m_BreakPoints.end()) | ||||||
|         m_BreakPoints.erase(it); |         m_BreakPoints.erase(it); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void BreakPoints::Clear() | void BreakPoints::Clear() { | ||||||
| { |     // if (jit)
 | ||||||
|     //if (jit)
 |  | ||||||
|     //{
 |     //{
 | ||||||
|     //    std::for_each(m_BreakPoints.begin(), m_BreakPoints.end(),
 |     //    std::for_each(m_BreakPoints.begin(), m_BreakPoints.end(),
 | ||||||
|     //        [](const TBreakPoint& bp)
 |     //        [](const TBreakPoint& bp)
 | ||||||
|  |  | ||||||
|  | @ -4,28 +4,27 @@ | ||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <vector> |  | ||||||
| #include <string> | #include <string> | ||||||
| 
 | #include <vector> | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| 
 | 
 | ||||||
| class DebugInterface; | class DebugInterface; | ||||||
| 
 | 
 | ||||||
| struct TBreakPoint | struct TBreakPoint { | ||||||
| { |     u32 iAddress; | ||||||
|     u32  iAddress; |  | ||||||
|     bool bOn; |     bool bOn; | ||||||
|     bool bTemporary; |     bool bTemporary; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // Code breakpoints.
 | // Code breakpoints.
 | ||||||
| class BreakPoints | class BreakPoints { | ||||||
| { |  | ||||||
| public: | public: | ||||||
|     typedef std::vector<TBreakPoint> TBreakPoints; |     typedef std::vector<TBreakPoint> TBreakPoints; | ||||||
|     typedef std::vector<std::string> TBreakPointsStr; |     typedef std::vector<std::string> TBreakPointsStr; | ||||||
| 
 | 
 | ||||||
|     const TBreakPoints& GetBreakPoints() { return m_BreakPoints; } |     const TBreakPoints& GetBreakPoints() { | ||||||
|  |         return m_BreakPoints; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     TBreakPointsStr GetStrings() const; |     TBreakPointsStr GetStrings() const; | ||||||
|     void AddFromStrings(const TBreakPointsStr& bps); |     void AddFromStrings(const TBreakPointsStr& bps); | ||||||
|  | @ -35,7 +34,7 @@ public: | ||||||
|     bool IsTempBreakPoint(u32 iAddress) const; |     bool IsTempBreakPoint(u32 iAddress) const; | ||||||
| 
 | 
 | ||||||
|     // Add BreakPoint
 |     // Add BreakPoint
 | ||||||
|     void Add(u32 em_address, bool temp=false); |     void Add(u32 em_address, bool temp = false); | ||||||
|     void Add(const TBreakPoint& bp); |     void Add(const TBreakPoint& bp); | ||||||
| 
 | 
 | ||||||
|     // Remove Breakpoint
 |     // Remove Breakpoint
 | ||||||
|  | @ -46,5 +45,5 @@ public: | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     TBreakPoints m_BreakPoints; |     TBreakPoints m_BreakPoints; | ||||||
|     u32          m_iBreakOnCount; |     u32 m_iBreakOnCount; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -35,87 +35,90 @@ | ||||||
| #include <type_traits> | #include <type_traits> | ||||||
| #include <utility> | #include <utility> | ||||||
| #include <vector> | #include <vector> | ||||||
| 
 |  | ||||||
| #include "common/assert.h" | #include "common/assert.h" | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
| 
 | 
 | ||||||
| template <class T> | template <class T> | ||||||
| struct LinkedListItem : public T | struct LinkedListItem : public T { | ||||||
| { |     LinkedListItem<T>* next; | ||||||
|     LinkedListItem<T> *next; |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class PointerWrap; | class PointerWrap; | ||||||
| 
 | 
 | ||||||
| class PointerWrapSection | class PointerWrapSection { | ||||||
| { |  | ||||||
| public: | public: | ||||||
|     PointerWrapSection(PointerWrap &p, int ver, const char *title) : p_(p), ver_(ver), title_(title) { |     PointerWrapSection(PointerWrap& p, int ver, const char* title) | ||||||
|     } |         : p_(p), ver_(ver), title_(title) {} | ||||||
|     ~PointerWrapSection(); |     ~PointerWrapSection(); | ||||||
| 
 | 
 | ||||||
|     bool operator == (const int &v) const { return ver_ == v; } |     bool operator==(const int& v) const { | ||||||
|     bool operator != (const int &v) const { return ver_ != v; } |         return ver_ == v; | ||||||
|     bool operator <= (const int &v) const { return ver_ <= v; } |     } | ||||||
|     bool operator >= (const int &v) const { return ver_ >= v; } |     bool operator!=(const int& v) const { | ||||||
|     bool operator <  (const int &v) const { return ver_ < v; } |         return ver_ != v; | ||||||
|     bool operator >  (const int &v) const { return ver_ > v; } |     } | ||||||
|  |     bool operator<=(const int& v) const { | ||||||
|  |         return ver_ <= v; | ||||||
|  |     } | ||||||
|  |     bool operator>=(const int& v) const { | ||||||
|  |         return ver_ >= v; | ||||||
|  |     } | ||||||
|  |     bool operator<(const int& v) const { | ||||||
|  |         return ver_ < v; | ||||||
|  |     } | ||||||
|  |     bool operator>(const int& v) const { | ||||||
|  |         return ver_ > v; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     operator bool() const  { |     operator bool() const { | ||||||
|         return ver_ > 0; |         return ver_ > 0; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     PointerWrap &p_; |     PointerWrap& p_; | ||||||
|     int ver_; |     int ver_; | ||||||
|     const char *title_; |     const char* title_; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // Wrapper class
 | // Wrapper class
 | ||||||
| class PointerWrap | class PointerWrap { | ||||||
| { | // This makes it a compile error if you forget to define DoState() on non-POD.
 | ||||||
|     // This makes it a compile error if you forget to define DoState() on non-POD.
 | // Which also can be a problem, for example struct tm is non-POD on linux, for whatever reason...
 | ||||||
|     // Which also can be a problem, for example struct tm is non-POD on linux, for whatever reason...
 |  | ||||||
| #ifdef _MSC_VER | #ifdef _MSC_VER | ||||||
|     template<typename T, bool isPOD = std::is_pod<T>::value, bool isPointer = std::is_pointer<T>::value> |     template <typename T, bool isPOD = std::is_pod<T>::value, | ||||||
|  |               bool isPointer = std::is_pointer<T>::value> | ||||||
| #else | #else | ||||||
|     template<typename T, bool isPOD = __is_pod(T), bool isPointer = std::is_pointer<T>::value> |     template <typename T, bool isPOD = __is_pod(T), bool isPointer = std::is_pointer<T>::value> | ||||||
| #endif | #endif | ||||||
|     struct DoHelper |     struct DoHelper { | ||||||
|     { |         static void DoArray(PointerWrap* p, T* x, int count) { | ||||||
|         static void DoArray(PointerWrap *p, T *x, int count) |  | ||||||
|         { |  | ||||||
|             for (int i = 0; i < count; ++i) |             for (int i = 0; i < count; ++i) | ||||||
|                 p->Do(x[i]); |                 p->Do(x[i]); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         static void Do(PointerWrap *p, T &x) |         static void Do(PointerWrap* p, T& x) { | ||||||
|         { |  | ||||||
|             p->DoClass(x); |             p->DoClass(x); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     template<typename T> |     template <typename T> | ||||||
|     struct DoHelper<T, true, false> |     struct DoHelper<T, true, false> { | ||||||
|     { |         static void DoArray(PointerWrap* p, T* x, int count) { | ||||||
|         static void DoArray(PointerWrap *p, T *x, int count) |             p->DoVoid((void*)x, sizeof(T) * count); | ||||||
|         { |  | ||||||
|             p->DoVoid((void *)x, sizeof(T) * count); |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         static void Do(PointerWrap *p, T &x) |         static void Do(PointerWrap* p, T& x) { | ||||||
|         { |             p->DoVoid((void*)&x, sizeof(x)); | ||||||
|             p->DoVoid((void *)&x, sizeof(x)); |  | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|     enum Mode { |     enum Mode { | ||||||
|         MODE_READ = 1, // load
 |         MODE_READ = 1, // load
 | ||||||
|         MODE_WRITE, // save
 |         MODE_WRITE,    // save
 | ||||||
|         MODE_MEASURE, // calculate size
 |         MODE_MEASURE,  // calculate size
 | ||||||
|         MODE_VERIFY, // compare
 |         MODE_VERIFY,   // compare
 | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     enum Error { |     enum Error { | ||||||
|  | @ -124,247 +127,237 @@ public: | ||||||
|         ERROR_FAILURE = 2, |         ERROR_FAILURE = 2, | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     u8 **ptr; |     u8** ptr; | ||||||
|     Mode mode; |     Mode mode; | ||||||
|     Error error; |     Error error; | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|     PointerWrap(u8 **ptr_, Mode mode_) : ptr(ptr_), mode(mode_), error(ERROR_NONE) {} |     PointerWrap(u8** ptr_, Mode mode_) : ptr(ptr_), mode(mode_), error(ERROR_NONE) {} | ||||||
|     PointerWrap(unsigned char **ptr_, int mode_) : ptr((u8**)ptr_), mode((Mode)mode_), error(ERROR_NONE) {} |     PointerWrap(unsigned char** ptr_, int mode_) | ||||||
|  |         : ptr((u8**)ptr_), mode((Mode)mode_), error(ERROR_NONE) {} | ||||||
| 
 | 
 | ||||||
|     PointerWrapSection Section(const char *title, int ver) { |     PointerWrapSection Section(const char* title, int ver) { | ||||||
|         return Section(title, ver, ver); |         return Section(title, ver, ver); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // The returned object can be compared against the version that was loaded.
 |     // The returned object can be compared against the version that was loaded.
 | ||||||
|     // This can be used to support versions as old as minVer.
 |     // This can be used to support versions as old as minVer.
 | ||||||
|     // Version = 0 means the section was not found.
 |     // Version = 0 means the section was not found.
 | ||||||
|     PointerWrapSection Section(const char *title, int minVer, int ver) { |     PointerWrapSection Section(const char* title, int minVer, int ver) { | ||||||
|         char marker[16] = {0}; |         char marker[16] = {0}; | ||||||
|         int foundVersion = ver; |         int foundVersion = ver; | ||||||
| 
 | 
 | ||||||
|         strncpy(marker, title, sizeof(marker)); |         strncpy(marker, title, sizeof(marker)); | ||||||
|         if (!ExpectVoid(marker, sizeof(marker))) |         if (!ExpectVoid(marker, sizeof(marker))) { | ||||||
|         { |  | ||||||
|             // Might be before we added name markers for safety.
 |             // Might be before we added name markers for safety.
 | ||||||
|             if (foundVersion == 1 && ExpectVoid(&foundVersion, sizeof(foundVersion))) |             if (foundVersion == 1 && ExpectVoid(&foundVersion, sizeof(foundVersion))) | ||||||
|                 DoMarker(title); |                 DoMarker(title); | ||||||
|             // Wasn't found, but maybe we can still load the state.
 |             // Wasn't found, but maybe we can still load the state.
 | ||||||
|             else |             else | ||||||
|                 foundVersion = 0; |                 foundVersion = 0; | ||||||
|         } |         } else | ||||||
|         else |  | ||||||
|             Do(foundVersion); |             Do(foundVersion); | ||||||
| 
 | 
 | ||||||
|         if (error == ERROR_FAILURE || foundVersion < minVer || foundVersion > ver) { |         if (error == ERROR_FAILURE || foundVersion < minVer || foundVersion > ver) { | ||||||
|             LOG_ERROR(Common, "Savestate failure: wrong version %d found for %s", foundVersion, title); |             LOG_ERROR(Common, "Savestate failure: wrong version %d found for %s", foundVersion, | ||||||
|  |                       title); | ||||||
|             SetError(ERROR_FAILURE); |             SetError(ERROR_FAILURE); | ||||||
|             return PointerWrapSection(*this, -1, title); |             return PointerWrapSection(*this, -1, title); | ||||||
|         } |         } | ||||||
|         return PointerWrapSection(*this, foundVersion, title); |         return PointerWrapSection(*this, foundVersion, title); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void SetMode(Mode mode_) {mode = mode_;} |     void SetMode(Mode mode_) { | ||||||
|     Mode GetMode() const {return mode;} |         mode = mode_; | ||||||
|     u8 **GetPPtr() {return ptr;} |     } | ||||||
|     void SetError(Error error_) |     Mode GetMode() const { | ||||||
|     { |         return mode; | ||||||
|  |     } | ||||||
|  |     u8** GetPPtr() { | ||||||
|  |         return ptr; | ||||||
|  |     } | ||||||
|  |     void SetError(Error error_) { | ||||||
|         if (error < error_) |         if (error < error_) | ||||||
|             error = error_; |             error = error_; | ||||||
|         if (error > ERROR_WARNING) |         if (error > ERROR_WARNING) | ||||||
|             mode = PointerWrap::MODE_MEASURE; |             mode = PointerWrap::MODE_MEASURE; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     bool ExpectVoid(void *data, int size) |     bool ExpectVoid(void* data, int size) { | ||||||
|     { |  | ||||||
|         switch (mode) { |         switch (mode) { | ||||||
|         case MODE_READ:    if (memcmp(data, *ptr, size) != 0) return false; break; |         case MODE_READ: | ||||||
|         case MODE_WRITE: memcpy(*ptr, data, size); break; |             if (memcmp(data, *ptr, size) != 0) | ||||||
|         case MODE_MEASURE: break;  // MODE_MEASURE - don't need to do anything
 |                 return false; | ||||||
|  |             break; | ||||||
|  |         case MODE_WRITE: | ||||||
|  |             memcpy(*ptr, data, size); | ||||||
|  |             break; | ||||||
|  |         case MODE_MEASURE: | ||||||
|  |             break; // MODE_MEASURE - don't need to do anything
 | ||||||
|         case MODE_VERIFY: |         case MODE_VERIFY: | ||||||
|             for (int i = 0; i < size; i++) { |             for (int i = 0; i < size; i++) { | ||||||
|                 DEBUG_ASSERT_MSG(((u8*)data)[i] == (*ptr)[i], |                 DEBUG_ASSERT_MSG( | ||||||
|  |                     ((u8*)data)[i] == (*ptr)[i], | ||||||
|                     "Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n", |                     "Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n", | ||||||
|                     ((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i], |                     ((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i], (*ptr)[i], (*ptr)[i], | ||||||
|                     (*ptr)[i], (*ptr)[i], &(*ptr)[i]); |                     &(*ptr)[i]); | ||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
|         default: break;  // throw an error?
 |         default: | ||||||
|  |             break; // throw an error?
 | ||||||
|         } |         } | ||||||
|         (*ptr) += size; |         (*ptr) += size; | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void DoVoid(void *data, int size) |     void DoVoid(void* data, int size) { | ||||||
|     { |  | ||||||
|         switch (mode) { |         switch (mode) { | ||||||
|         case MODE_READ:    memcpy(data, *ptr, size); break; |         case MODE_READ: | ||||||
|         case MODE_WRITE: memcpy(*ptr, data, size); break; |             memcpy(data, *ptr, size); | ||||||
|         case MODE_MEASURE: break;  // MODE_MEASURE - don't need to do anything
 |             break; | ||||||
|  |         case MODE_WRITE: | ||||||
|  |             memcpy(*ptr, data, size); | ||||||
|  |             break; | ||||||
|  |         case MODE_MEASURE: | ||||||
|  |             break; // MODE_MEASURE - don't need to do anything
 | ||||||
|         case MODE_VERIFY: |         case MODE_VERIFY: | ||||||
|             for (int i = 0; i < size; i++) { |             for (int i = 0; i < size; i++) { | ||||||
|                 DEBUG_ASSERT_MSG(((u8*)data)[i] == (*ptr)[i], |                 DEBUG_ASSERT_MSG( | ||||||
|  |                     ((u8*)data)[i] == (*ptr)[i], | ||||||
|                     "Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n", |                     "Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n", | ||||||
|                     ((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i], |                     ((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i], (*ptr)[i], (*ptr)[i], | ||||||
|                     (*ptr)[i], (*ptr)[i], &(*ptr)[i]); |                     &(*ptr)[i]); | ||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
|         default: break;  // throw an error?
 |         default: | ||||||
|  |             break; // throw an error?
 | ||||||
|         } |         } | ||||||
|         (*ptr) += size; |         (*ptr) += size; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     template<class K, class T> |     template <class K, class T> | ||||||
|     void Do(std::map<K, T *> &x) |     void Do(std::map<K, T*>& x) { | ||||||
|     { |         if (mode == MODE_READ) { | ||||||
|         if (mode == MODE_READ) |             for (auto it = x.begin(), end = x.end(); it != end; ++it) { | ||||||
|         { |  | ||||||
|             for (auto it = x.begin(), end = x.end(); it != end; ++it) |  | ||||||
|             { |  | ||||||
|                 if (it->second != nullptr) |                 if (it->second != nullptr) | ||||||
|                     delete it->second; |                     delete it->second; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         T *dv = nullptr; |         T* dv = nullptr; | ||||||
|         DoMap(x, dv); |         DoMap(x, dv); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     template<class K, class T> |     template <class K, class T> | ||||||
|     void Do(std::map<K, T> &x) |     void Do(std::map<K, T>& x) { | ||||||
|     { |  | ||||||
|         T dv = T(); |         T dv = T(); | ||||||
|         DoMap(x, dv); |         DoMap(x, dv); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     template<class K, class T> |     template <class K, class T> | ||||||
|     void DoMap(std::map<K, T> &x, T &default_val) |     void DoMap(std::map<K, T>& x, T& default_val) { | ||||||
|     { |  | ||||||
|         unsigned int number = (unsigned int)x.size(); |         unsigned int number = (unsigned int)x.size(); | ||||||
|         Do(number); |         Do(number); | ||||||
|         switch (mode) { |         switch (mode) { | ||||||
|         case MODE_READ: |         case MODE_READ: { | ||||||
|             { |             x.clear(); | ||||||
|                 x.clear(); |             while (number > 0) { | ||||||
|                 while (number > 0) |                 K first = K(); | ||||||
|                 { |                 Do(first); | ||||||
|                     K first = K(); |                 T second = default_val; | ||||||
|                     Do(first); |                 Do(second); | ||||||
|                     T second = default_val; |                 x[first] = second; | ||||||
|                     Do(second); |                 --number; | ||||||
|                     x[first] = second; |  | ||||||
|                     --number; |  | ||||||
|                 } |  | ||||||
|             } |             } | ||||||
|             break; |         } break; | ||||||
|         case MODE_WRITE: |         case MODE_WRITE: | ||||||
|         case MODE_MEASURE: |         case MODE_MEASURE: | ||||||
|         case MODE_VERIFY: |         case MODE_VERIFY: { | ||||||
|             { |             typename std::map<K, T>::iterator itr = x.begin(); | ||||||
|                 typename std::map<K, T>::iterator itr = x.begin(); |             while (number > 0) { | ||||||
|                 while (number > 0) |                 K first = itr->first; | ||||||
|                 { |                 Do(first); | ||||||
|                     K first = itr->first; |                 Do(itr->second); | ||||||
|                     Do(first); |                 --number; | ||||||
|                     Do(itr->second); |                 ++itr; | ||||||
|                     --number; |  | ||||||
|                     ++itr; |  | ||||||
|                 } |  | ||||||
|             } |             } | ||||||
|             break; |         } break; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     template<class K, class T> |     template <class K, class T> | ||||||
|     void Do(std::multimap<K, T *> &x) |     void Do(std::multimap<K, T*>& x) { | ||||||
|     { |         if (mode == MODE_READ) { | ||||||
|         if (mode == MODE_READ) |             for (auto it = x.begin(), end = x.end(); it != end; ++it) { | ||||||
|         { |  | ||||||
|             for (auto it = x.begin(), end = x.end(); it != end; ++it) |  | ||||||
|             { |  | ||||||
|                 if (it->second != nullptr) |                 if (it->second != nullptr) | ||||||
|                     delete it->second; |                     delete it->second; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         T *dv = nullptr; |         T* dv = nullptr; | ||||||
|         DoMultimap(x, dv); |         DoMultimap(x, dv); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     template<class K, class T> |     template <class K, class T> | ||||||
|     void Do(std::multimap<K, T> &x) |     void Do(std::multimap<K, T>& x) { | ||||||
|     { |  | ||||||
|         T dv = T(); |         T dv = T(); | ||||||
|         DoMultimap(x, dv); |         DoMultimap(x, dv); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     template<class K, class T> |     template <class K, class T> | ||||||
|     void DoMultimap(std::multimap<K, T> &x, T &default_val) |     void DoMultimap(std::multimap<K, T>& x, T& default_val) { | ||||||
|     { |  | ||||||
|         unsigned int number = (unsigned int)x.size(); |         unsigned int number = (unsigned int)x.size(); | ||||||
|         Do(number); |         Do(number); | ||||||
|         switch (mode) { |         switch (mode) { | ||||||
|         case MODE_READ: |         case MODE_READ: { | ||||||
|             { |             x.clear(); | ||||||
|                 x.clear(); |             while (number > 0) { | ||||||
|                 while (number > 0) |                 K first = K(); | ||||||
|                 { |                 Do(first); | ||||||
|                     K first = K(); |                 T second = default_val; | ||||||
|                     Do(first); |                 Do(second); | ||||||
|                     T second = default_val; |                 x.insert(std::make_pair(first, second)); | ||||||
|                     Do(second); |                 --number; | ||||||
|                     x.insert(std::make_pair(first, second)); |  | ||||||
|                     --number; |  | ||||||
|                 } |  | ||||||
|             } |             } | ||||||
|             break; |         } break; | ||||||
|         case MODE_WRITE: |         case MODE_WRITE: | ||||||
|         case MODE_MEASURE: |         case MODE_MEASURE: | ||||||
|         case MODE_VERIFY: |         case MODE_VERIFY: { | ||||||
|             { |             typename std::multimap<K, T>::iterator itr = x.begin(); | ||||||
|                 typename std::multimap<K, T>::iterator itr = x.begin(); |             while (number > 0) { | ||||||
|                 while (number > 0) |                 Do(itr->first); | ||||||
|                 { |                 Do(itr->second); | ||||||
|                     Do(itr->first); |                 --number; | ||||||
|                     Do(itr->second); |                 ++itr; | ||||||
|                     --number; |  | ||||||
|                     ++itr; |  | ||||||
|                 } |  | ||||||
|             } |             } | ||||||
|             break; |         } break; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Store vectors.
 |     // Store vectors.
 | ||||||
|     template<class T> |     template <class T> | ||||||
|     void Do(std::vector<T *> &x) |     void Do(std::vector<T*>& x) { | ||||||
|     { |         T* dv = nullptr; | ||||||
|         T *dv = nullptr; |  | ||||||
|         DoVector(x, dv); |         DoVector(x, dv); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     template<class T> |     template <class T> | ||||||
|     void Do(std::vector<T> &x) |     void Do(std::vector<T>& x) { | ||||||
|     { |  | ||||||
|         T dv = T(); |         T dv = T(); | ||||||
|         DoVector(x, dv); |         DoVector(x, dv); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 |     template <class T> | ||||||
|     template<class T> |     void DoPOD(std::vector<T>& x) { | ||||||
|     void DoPOD(std::vector<T> &x) |  | ||||||
|     { |  | ||||||
|         T dv = T(); |         T dv = T(); | ||||||
|         DoVectorPOD(x, dv); |         DoVectorPOD(x, dv); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     template<class T> |     template <class T> | ||||||
|     void Do(std::vector<T> &x, T &default_val) |     void Do(std::vector<T>& x, T& default_val) { | ||||||
|     { |  | ||||||
|         DoVector(x, default_val); |         DoVector(x, default_val); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     template<class T> |     template <class T> | ||||||
|     void DoVector(std::vector<T> &x, T &default_val) |     void DoVector(std::vector<T>& x, T& default_val) { | ||||||
|     { |  | ||||||
|         u32 vec_size = (u32)x.size(); |         u32 vec_size = (u32)x.size(); | ||||||
|         Do(vec_size); |         Do(vec_size); | ||||||
|         x.resize(vec_size, default_val); |         x.resize(vec_size, default_val); | ||||||
|  | @ -372,9 +365,8 @@ public: | ||||||
|             DoArray(&x[0], vec_size); |             DoArray(&x[0], vec_size); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     template<class T> |     template <class T> | ||||||
|     void DoVectorPOD(std::vector<T> &x, T &default_val) |     void DoVectorPOD(std::vector<T>& x, T& default_val) { | ||||||
|     { |  | ||||||
|         u32 vec_size = (u32)x.size(); |         u32 vec_size = (u32)x.size(); | ||||||
|         Do(vec_size); |         Do(vec_size); | ||||||
|         x.resize(vec_size, default_val); |         x.resize(vec_size, default_val); | ||||||
|  | @ -383,55 +375,48 @@ public: | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Store deques.
 |     // Store deques.
 | ||||||
|     template<class T> |     template <class T> | ||||||
|     void Do(std::deque<T *> &x) |     void Do(std::deque<T*>& x) { | ||||||
|     { |         T* dv = nullptr; | ||||||
|         T *dv = nullptr; |  | ||||||
|         DoDeque(x, dv); |         DoDeque(x, dv); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     template<class T> |     template <class T> | ||||||
|     void Do(std::deque<T> &x) |     void Do(std::deque<T>& x) { | ||||||
|     { |  | ||||||
|         T dv = T(); |         T dv = T(); | ||||||
|         DoDeque(x, dv); |         DoDeque(x, dv); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     template<class T> |     template <class T> | ||||||
|     void DoDeque(std::deque<T> &x, T &default_val) |     void DoDeque(std::deque<T>& x, T& default_val) { | ||||||
|     { |  | ||||||
|         u32 deq_size = (u32)x.size(); |         u32 deq_size = (u32)x.size(); | ||||||
|         Do(deq_size); |         Do(deq_size); | ||||||
|         x.resize(deq_size, default_val); |         x.resize(deq_size, default_val); | ||||||
|         u32 i; |         u32 i; | ||||||
|         for(i = 0; i < deq_size; i++) |         for (i = 0; i < deq_size; i++) | ||||||
|             Do(x[i]); |             Do(x[i]); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Store STL lists.
 |     // Store STL lists.
 | ||||||
|     template<class T> |     template <class T> | ||||||
|     void Do(std::list<T *> &x) |     void Do(std::list<T*>& x) { | ||||||
|     { |         T* dv = nullptr; | ||||||
|         T *dv = nullptr; |  | ||||||
|         Do(x, dv); |         Do(x, dv); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     template<class T> |     template <class T> | ||||||
|     void Do(std::list<T> &x) |     void Do(std::list<T>& x) { | ||||||
|     { |  | ||||||
|         T dv = T(); |         T dv = T(); | ||||||
|         DoList(x, dv); |         DoList(x, dv); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     template<class T> |     template <class T> | ||||||
|     void Do(std::list<T> &x, T &default_val) |     void Do(std::list<T>& x, T& default_val) { | ||||||
|     { |  | ||||||
|         DoList(x, default_val); |         DoList(x, default_val); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     template<class T> |     template <class T> | ||||||
|     void DoList(std::list<T> &x, T &default_val) |     void DoList(std::list<T>& x, T& default_val) { | ||||||
|     { |  | ||||||
|         u32 list_size = (u32)x.size(); |         u32 list_size = (u32)x.size(); | ||||||
|         Do(list_size); |         Do(list_size); | ||||||
|         x.resize(list_size, default_val); |         x.resize(list_size, default_val); | ||||||
|  | @ -441,15 +426,11 @@ public: | ||||||
|             Do(*itr); |             Do(*itr); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     // Store STL sets.
 |     // Store STL sets.
 | ||||||
|     template <class T> |     template <class T> | ||||||
|     void Do(std::set<T *> &x) |     void Do(std::set<T*>& x) { | ||||||
|     { |         if (mode == MODE_READ) { | ||||||
|         if (mode == MODE_READ) |             for (auto it = x.begin(), end = x.end(); it != end; ++it) { | ||||||
|         { |  | ||||||
|             for (auto it = x.begin(), end = x.end(); it != end; ++it) |  | ||||||
|             { |  | ||||||
|                 if (*it != nullptr) |                 if (*it != nullptr) | ||||||
|                     delete *it; |                     delete *it; | ||||||
|             } |             } | ||||||
|  | @ -458,39 +439,31 @@ public: | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     template <class T> |     template <class T> | ||||||
|     void Do(std::set<T> &x) |     void Do(std::set<T>& x) { | ||||||
|     { |  | ||||||
|         DoSet(x); |         DoSet(x); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     template <class T> |     template <class T> | ||||||
|     void DoSet(std::set<T> &x) |     void DoSet(std::set<T>& x) { | ||||||
|     { |  | ||||||
|         unsigned int number = (unsigned int)x.size(); |         unsigned int number = (unsigned int)x.size(); | ||||||
|         Do(number); |         Do(number); | ||||||
| 
 | 
 | ||||||
|         switch (mode) |         switch (mode) { | ||||||
|         { |         case MODE_READ: { | ||||||
|         case MODE_READ: |             x.clear(); | ||||||
|             { |             while (number-- > 0) { | ||||||
|                 x.clear(); |                 T it = T(); | ||||||
|                 while (number-- > 0) |                 Do(it); | ||||||
|                 { |                 x.insert(it); | ||||||
|                     T it = T(); |  | ||||||
|                     Do(it); |  | ||||||
|                     x.insert(it); |  | ||||||
|                 } |  | ||||||
|             } |             } | ||||||
|             break; |         } break; | ||||||
|         case MODE_WRITE: |         case MODE_WRITE: | ||||||
|         case MODE_MEASURE: |         case MODE_MEASURE: | ||||||
|         case MODE_VERIFY: |         case MODE_VERIFY: { | ||||||
|             { |             typename std::set<T>::iterator itr = x.begin(); | ||||||
|                 typename std::set<T>::iterator itr = x.begin(); |             while (number-- > 0) | ||||||
|                 while (number-- > 0) |                 Do(*itr++); | ||||||
|                     Do(*itr++); |         } break; | ||||||
|             } |  | ||||||
|             break; |  | ||||||
| 
 | 
 | ||||||
|         default: |         default: | ||||||
|             LOG_ERROR(Common, "Savestate error: invalid mode %d.", mode); |             LOG_ERROR(Common, "Savestate error: invalid mode %d.", mode); | ||||||
|  | @ -498,51 +471,58 @@ public: | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Store strings.
 |     // Store strings.
 | ||||||
|     void Do(std::string &x) |     void Do(std::string& x) { | ||||||
|     { |  | ||||||
|         int stringLen = (int)x.length() + 1; |         int stringLen = (int)x.length() + 1; | ||||||
|         Do(stringLen); |         Do(stringLen); | ||||||
| 
 | 
 | ||||||
|         switch (mode) { |         switch (mode) { | ||||||
|         case MODE_READ:        x = (char*)*ptr; break; |         case MODE_READ: | ||||||
|         case MODE_WRITE:    memcpy(*ptr, x.c_str(), stringLen); break; |             x = (char*)*ptr; | ||||||
|         case MODE_MEASURE: break; |             break; | ||||||
|  |         case MODE_WRITE: | ||||||
|  |             memcpy(*ptr, x.c_str(), stringLen); | ||||||
|  |             break; | ||||||
|  |         case MODE_MEASURE: | ||||||
|  |             break; | ||||||
|         case MODE_VERIFY: |         case MODE_VERIFY: | ||||||
|             DEBUG_ASSERT_MSG((x == (char*)*ptr), |             DEBUG_ASSERT_MSG((x == (char*)*ptr), | ||||||
|                 "Savestate verification failure: \"%s\" != \"%s\" (at %p).\n", |                              "Savestate verification failure: \"%s\" != \"%s\" (at %p).\n", | ||||||
|                 x.c_str(), (char*)*ptr, ptr); |                              x.c_str(), (char*)*ptr, ptr); | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         (*ptr) += stringLen; |         (*ptr) += stringLen; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void Do(std::wstring &x) |     void Do(std::wstring& x) { | ||||||
|     { |         int stringLen = sizeof(wchar_t) * ((int)x.length() + 1); | ||||||
|         int stringLen = sizeof(wchar_t)*((int)x.length() + 1); |  | ||||||
|         Do(stringLen); |         Do(stringLen); | ||||||
| 
 | 
 | ||||||
|         switch (mode) { |         switch (mode) { | ||||||
|         case MODE_READ:        x = (wchar_t*)*ptr; break; |         case MODE_READ: | ||||||
|         case MODE_WRITE:    memcpy(*ptr, x.c_str(), stringLen); break; |             x = (wchar_t*)*ptr; | ||||||
|         case MODE_MEASURE: break; |             break; | ||||||
|  |         case MODE_WRITE: | ||||||
|  |             memcpy(*ptr, x.c_str(), stringLen); | ||||||
|  |             break; | ||||||
|  |         case MODE_MEASURE: | ||||||
|  |             break; | ||||||
|         case MODE_VERIFY: |         case MODE_VERIFY: | ||||||
|             DEBUG_ASSERT_MSG((x == (wchar_t*)*ptr), |             DEBUG_ASSERT_MSG((x == (wchar_t*)*ptr), | ||||||
|                 "Savestate verification failure: \"%ls\" != \"%ls\" (at %p).\n", |                              "Savestate verification failure: \"%ls\" != \"%ls\" (at %p).\n", | ||||||
|                 x.c_str(), (wchar_t*)*ptr, ptr); |                              x.c_str(), (wchar_t*)*ptr, ptr); | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         (*ptr) += stringLen; |         (*ptr) += stringLen; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     template<class T> |     template <class T> | ||||||
|     void DoClass(T &x) { |     void DoClass(T& x) { | ||||||
|         x.DoState(*this); |         x.DoState(*this); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     template<class T> |     template <class T> | ||||||
|     void DoClass(T *&x) { |     void DoClass(T*& x) { | ||||||
|         if (mode == MODE_READ) |         if (mode == MODE_READ) { | ||||||
|         { |  | ||||||
|             if (x != nullptr) |             if (x != nullptr) | ||||||
|                 delete x; |                 delete x; | ||||||
|             x = new T(); |             x = new T(); | ||||||
|  | @ -550,81 +530,70 @@ public: | ||||||
|         x->DoState(*this); |         x->DoState(*this); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     template<class T> |     template <class T> | ||||||
|     void DoArray(T *x, int count) { |     void DoArray(T* x, int count) { | ||||||
|         DoHelper<T>::DoArray(this, x, count); |         DoHelper<T>::DoArray(this, x, count); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     template<class T> |     template <class T> | ||||||
|     void Do(T &x) { |     void Do(T& x) { | ||||||
|         DoHelper<T>::Do(this, x); |         DoHelper<T>::Do(this, x); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     template<class T> |     template <class T> | ||||||
|     void DoPOD(T &x) { |     void DoPOD(T& x) { | ||||||
|         DoHelper<T>::Do(this, x); |         DoHelper<T>::Do(this, x); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     template<class T> |     template <class T> | ||||||
|     void DoPointer(T* &x, T*const base) { |     void DoPointer(T*& x, T* const base) { | ||||||
|         // pointers can be more than 2^31 apart, but you're using this function wrong if you need that much range
 |         // pointers can be more than 2^31 apart, but you're using this function wrong if you need
 | ||||||
|  |         // that much range
 | ||||||
|         s32 offset = x - base; |         s32 offset = x - base; | ||||||
|         Do(offset); |         Do(offset); | ||||||
|         if (mode == MODE_READ) |         if (mode == MODE_READ) | ||||||
|             x = base + offset; |             x = base + offset; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     template<class T, LinkedListItem<T>* (*TNew)(), void (*TFree)(LinkedListItem<T>*), void (*TDo)(PointerWrap&, T*)> |     template <class T, LinkedListItem<T>* (*TNew)(), void (*TFree)(LinkedListItem<T>*), | ||||||
|     void DoLinkedList(LinkedListItem<T>*& list_start, LinkedListItem<T>** list_end = nullptr) |               void (*TDo)(PointerWrap&, T*)> | ||||||
|     { |     void DoLinkedList(LinkedListItem<T>*& list_start, LinkedListItem<T>** list_end = nullptr) { | ||||||
|         LinkedListItem<T>* list_cur = list_start; |         LinkedListItem<T>* list_cur = list_start; | ||||||
|         LinkedListItem<T>* prev = nullptr; |         LinkedListItem<T>* prev = nullptr; | ||||||
| 
 | 
 | ||||||
|         while (true) |         while (true) { | ||||||
|         { |  | ||||||
|             u8 shouldExist = (list_cur ? 1 : 0); |             u8 shouldExist = (list_cur ? 1 : 0); | ||||||
|             Do(shouldExist); |             Do(shouldExist); | ||||||
|             if (shouldExist == 1) |             if (shouldExist == 1) { | ||||||
|             { |  | ||||||
|                 LinkedListItem<T>* cur = list_cur ? list_cur : TNew(); |                 LinkedListItem<T>* cur = list_cur ? list_cur : TNew(); | ||||||
|                 TDo(*this, (T*)cur); |                 TDo(*this, (T*)cur); | ||||||
|                 if (!list_cur) |                 if (!list_cur) { | ||||||
|                 { |                     if (mode == MODE_READ) { | ||||||
|                     if (mode == MODE_READ) |  | ||||||
|                     { |  | ||||||
|                         cur->next = nullptr; |                         cur->next = nullptr; | ||||||
|                         list_cur = cur; |                         list_cur = cur; | ||||||
|                         if (prev) |                         if (prev) | ||||||
|                             prev->next = cur; |                             prev->next = cur; | ||||||
|                         else |                         else | ||||||
|                             list_start = cur; |                             list_start = cur; | ||||||
|                     } |                     } else { | ||||||
|                     else |  | ||||||
|                     { |  | ||||||
|                         TFree(cur); |                         TFree(cur); | ||||||
|                         continue; |                         continue; | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } else { | ||||||
|             else |                 if (mode == MODE_READ) { | ||||||
|             { |  | ||||||
|                 if (mode == MODE_READ) |  | ||||||
|                 { |  | ||||||
|                     if (prev) |                     if (prev) | ||||||
|                         prev->next = nullptr; |                         prev->next = nullptr; | ||||||
|                     if (list_end) |                     if (list_end) | ||||||
|                         *list_end = prev; |                         *list_end = prev; | ||||||
|                     if (list_cur) |                     if (list_cur) { | ||||||
|                     { |  | ||||||
|                         if (list_start == list_cur) |                         if (list_start == list_cur) | ||||||
|                             list_start = nullptr; |                             list_start = nullptr; | ||||||
|                         do |                         do { | ||||||
|                         { |  | ||||||
|                             LinkedListItem<T>* next = list_cur->next; |                             LinkedListItem<T>* next = list_cur->next; | ||||||
|                             TFree(list_cur); |                             TFree(list_cur); | ||||||
|                             list_cur = next; |                             list_cur = next; | ||||||
|                         } |                         } while (list_cur); | ||||||
|                         while (list_cur); |  | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|                 break; |                 break; | ||||||
|  | @ -634,13 +603,13 @@ public: | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void DoMarker(const char* prevName, u32 arbitraryNumber=0x42) |     void DoMarker(const char* prevName, u32 arbitraryNumber = 0x42) { | ||||||
|     { |  | ||||||
|         u32 cookie = arbitraryNumber; |         u32 cookie = arbitraryNumber; | ||||||
|         Do(cookie); |         Do(cookie); | ||||||
|         if(mode == PointerWrap::MODE_READ && cookie != arbitraryNumber) |         if (mode == PointerWrap::MODE_READ && cookie != arbitraryNumber) { | ||||||
|         { |             LOG_ERROR(Common, "After \"%s\", found %d (0x%X) instead of save marker %d (0x%X). " | ||||||
|             LOG_ERROR(Common, "After \"%s\", found %d (0x%X) instead of save marker %d (0x%X). Aborting savestate load...", prevName, cookie, cookie, arbitraryNumber, arbitraryNumber); |                               "Aborting savestate load...", | ||||||
|  |                       prevName, cookie, cookie, arbitraryNumber, arbitraryNumber); | ||||||
|             SetError(ERROR_FAILURE); |             SetError(ERROR_FAILURE); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -5,7 +5,6 @@ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <cstddef> | #include <cstddef> | ||||||
| 
 |  | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "common/memory_util.h" | #include "common/memory_util.h" | ||||||
| 
 | 
 | ||||||
|  | @ -14,24 +13,27 @@ | ||||||
| // having to prefix them with gen-> or something similar.
 | // having to prefix them with gen-> or something similar.
 | ||||||
| // Example implementation:
 | // Example implementation:
 | ||||||
| // class JIT : public CodeBlock<ARMXEmitter> {}
 | // class JIT : public CodeBlock<ARMXEmitter> {}
 | ||||||
| template<class T> class CodeBlock : public T, NonCopyable | template <class T> | ||||||
| { | class CodeBlock : public T, NonCopyable { | ||||||
| private: | private: | ||||||
|     // A privately used function to set the executable RAM space to something invalid.
 |     // A privately used function to set the executable RAM space to something invalid.
 | ||||||
|     // For debugging usefulness it should be used to set the RAM to a host specific breakpoint instruction
 |     // For debugging usefulness it should be used to set the RAM to a host specific breakpoint
 | ||||||
|  |     // instruction
 | ||||||
|     virtual void PoisonMemory() = 0; |     virtual void PoisonMemory() = 0; | ||||||
| 
 | 
 | ||||||
| protected: | protected: | ||||||
|     u8 *region; |     u8* region; | ||||||
|     size_t region_size; |     size_t region_size; | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|     CodeBlock() : region(nullptr), region_size(0) {} |     CodeBlock() : region(nullptr), region_size(0) {} | ||||||
|     virtual ~CodeBlock() { if (region) FreeCodeSpace(); } |     virtual ~CodeBlock() { | ||||||
|  |         if (region) | ||||||
|  |             FreeCodeSpace(); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     // Call this before you generate any code.
 |     // Call this before you generate any code.
 | ||||||
|     void AllocCodeSpace(int size) |     void AllocCodeSpace(int size) { | ||||||
|     { |  | ||||||
|         region_size = size; |         region_size = size; | ||||||
|         region = (u8*)AllocateExecutableMemory(region_size); |         region = (u8*)AllocateExecutableMemory(region_size); | ||||||
|         T::SetCodePtr(region); |         T::SetCodePtr(region); | ||||||
|  | @ -39,15 +41,13 @@ public: | ||||||
| 
 | 
 | ||||||
|     // Always clear code space with breakpoints, so that if someone accidentally executes
 |     // Always clear code space with breakpoints, so that if someone accidentally executes
 | ||||||
|     // uninitialized, it just breaks into the debugger.
 |     // uninitialized, it just breaks into the debugger.
 | ||||||
|     void ClearCodeSpace() |     void ClearCodeSpace() { | ||||||
|     { |  | ||||||
|         PoisonMemory(); |         PoisonMemory(); | ||||||
|         ResetCodePtr(); |         ResetCodePtr(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Call this when shutting down. Don't rely on the destructor, even though it'll do the job.
 |     // Call this when shutting down. Don't rely on the destructor, even though it'll do the job.
 | ||||||
|     void FreeCodeSpace() |     void FreeCodeSpace() { | ||||||
|     { |  | ||||||
| #ifdef __SYMBIAN32__ | #ifdef __SYMBIAN32__ | ||||||
|         ResetExecutableMemory(region); |         ResetExecutableMemory(region); | ||||||
| #else | #else | ||||||
|  | @ -57,33 +57,29 @@ public: | ||||||
|         region_size = 0; |         region_size = 0; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     bool IsInSpace(const u8 *ptr) |     bool IsInSpace(const u8* ptr) { | ||||||
|     { |  | ||||||
|         return (ptr >= region) && (ptr < (region + region_size)); |         return (ptr >= region) && (ptr < (region + region_size)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Cannot currently be undone. Will write protect the entire code region.
 |     // Cannot currently be undone. Will write protect the entire code region.
 | ||||||
|     // Start over if you need to change the code (call FreeCodeSpace(), AllocCodeSpace()).
 |     // Start over if you need to change the code (call FreeCodeSpace(), AllocCodeSpace()).
 | ||||||
|     void WriteProtect() |     void WriteProtect() { | ||||||
|     { |  | ||||||
|         WriteProtectMemory(region, region_size, true); |         WriteProtectMemory(region, region_size, true); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void ResetCodePtr() |     void ResetCodePtr() { | ||||||
|     { |  | ||||||
|         T::SetCodePtr(region); |         T::SetCodePtr(region); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     size_t GetSpaceLeft() const |     size_t GetSpaceLeft() const { | ||||||
|     { |  | ||||||
|         return region_size - (T::GetCodePtr() - region); |         return region_size - (T::GetCodePtr() - region); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     u8 *GetBasePtr() { |     u8* GetBasePtr() { | ||||||
|         return region; |         return region; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     size_t GetOffset(const u8 *ptr) const { |     size_t GetOffset(const u8* ptr) const { | ||||||
|         return ptr - region; |         return ptr - region; | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -56,7 +56,7 @@ constexpr u8 Convert8To6(u8 value) { | ||||||
|  * @return Result color decoded as Math::Vec4<u8> |  * @return Result color decoded as Math::Vec4<u8> | ||||||
|  */ |  */ | ||||||
| inline const Math::Vec4<u8> DecodeRGBA8(const u8* bytes) { | inline const Math::Vec4<u8> DecodeRGBA8(const u8* bytes) { | ||||||
|     return { bytes[3], bytes[2], bytes[1], bytes[0] }; |     return {bytes[3], bytes[2], bytes[1], bytes[0]}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  | @ -65,7 +65,7 @@ inline const Math::Vec4<u8> DecodeRGBA8(const u8* bytes) { | ||||||
|  * @return Result color decoded as Math::Vec4<u8> |  * @return Result color decoded as Math::Vec4<u8> | ||||||
|  */ |  */ | ||||||
| inline const Math::Vec4<u8> DecodeRGB8(const u8* bytes) { | inline const Math::Vec4<u8> DecodeRGB8(const u8* bytes) { | ||||||
|     return { bytes[2], bytes[1], bytes[0], 255 }; |     return {bytes[2], bytes[1], bytes[0], 255}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  | @ -74,7 +74,7 @@ inline const Math::Vec4<u8> DecodeRGB8(const u8* bytes) { | ||||||
|  * @return Result color decoded as Math::Vec4<u8> |  * @return Result color decoded as Math::Vec4<u8> | ||||||
|  */ |  */ | ||||||
| inline const Math::Vec4<u8> DecodeRG8(const u8* bytes) { | inline const Math::Vec4<u8> DecodeRG8(const u8* bytes) { | ||||||
|     return { bytes[1], bytes[0], 0, 255 }; |     return {bytes[1], bytes[0], 0, 255}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  | @ -84,8 +84,8 @@ inline const Math::Vec4<u8> DecodeRG8(const u8* bytes) { | ||||||
|  */ |  */ | ||||||
| inline const Math::Vec4<u8> DecodeRGB565(const u8* bytes) { | inline const Math::Vec4<u8> DecodeRGB565(const u8* bytes) { | ||||||
|     const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes); |     const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes); | ||||||
|     return { Convert5To8((pixel >> 11) & 0x1F), Convert6To8((pixel >> 5) & 0x3F), |     return {Convert5To8((pixel >> 11) & 0x1F), Convert6To8((pixel >> 5) & 0x3F), | ||||||
|         Convert5To8(pixel & 0x1F), 255 }; |             Convert5To8(pixel & 0x1F), 255}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  | @ -95,8 +95,8 @@ inline const Math::Vec4<u8> DecodeRGB565(const u8* bytes) { | ||||||
|  */ |  */ | ||||||
| inline const Math::Vec4<u8> DecodeRGB5A1(const u8* bytes) { | inline const Math::Vec4<u8> DecodeRGB5A1(const u8* bytes) { | ||||||
|     const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes); |     const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes); | ||||||
|     return { Convert5To8((pixel >> 11) & 0x1F), Convert5To8((pixel >> 6) & 0x1F), |     return {Convert5To8((pixel >> 11) & 0x1F), Convert5To8((pixel >> 6) & 0x1F), | ||||||
|         Convert5To8((pixel >> 1) & 0x1F), Convert1To8(pixel & 0x1) }; |             Convert5To8((pixel >> 1) & 0x1F), Convert1To8(pixel & 0x1)}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  | @ -106,8 +106,8 @@ inline const Math::Vec4<u8> DecodeRGB5A1(const u8* bytes) { | ||||||
|  */ |  */ | ||||||
| inline const Math::Vec4<u8> DecodeRGBA4(const u8* bytes) { | inline const Math::Vec4<u8> DecodeRGBA4(const u8* bytes) { | ||||||
|     const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes); |     const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes); | ||||||
|     return { Convert4To8((pixel >> 12) & 0xF), Convert4To8((pixel >> 8) & 0xF), |     return {Convert4To8((pixel >> 12) & 0xF), Convert4To8((pixel >> 8) & 0xF), | ||||||
|         Convert4To8((pixel >> 4) & 0xF), Convert4To8(pixel & 0xF) }; |             Convert4To8((pixel >> 4) & 0xF), Convert4To8(pixel & 0xF)}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  | @ -134,7 +134,7 @@ inline u32 DecodeD24(const u8* bytes) { | ||||||
|  * @return Resulting values stored as a Math::Vec2 |  * @return Resulting values stored as a Math::Vec2 | ||||||
|  */ |  */ | ||||||
| inline const Math::Vec2<u32> DecodeD24S8(const u8* bytes) { | inline const Math::Vec2<u32> DecodeD24S8(const u8* bytes) { | ||||||
|     return { static_cast<u32>((bytes[2] << 16) | (bytes[1] << 8) | bytes[0]), bytes[3] }; |     return {static_cast<u32>((bytes[2] << 16) | (bytes[1] << 8) | bytes[0]), bytes[3]}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  | @ -175,8 +175,8 @@ inline void EncodeRG8(const Math::Vec4<u8>& color, u8* bytes) { | ||||||
|  * @param bytes Destination pointer to store encoded color |  * @param bytes Destination pointer to store encoded color | ||||||
|  */ |  */ | ||||||
| inline void EncodeRGB565(const Math::Vec4<u8>& color, u8* bytes) { | inline void EncodeRGB565(const Math::Vec4<u8>& color, u8* bytes) { | ||||||
|     *reinterpret_cast<u16_le*>(bytes) = (Convert8To5(color.r()) << 11) | |     *reinterpret_cast<u16_le*>(bytes) = | ||||||
|         (Convert8To6(color.g()) << 5) | Convert8To5(color.b()); |         (Convert8To5(color.r()) << 11) | (Convert8To6(color.g()) << 5) | Convert8To5(color.b()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  | @ -186,7 +186,8 @@ inline void EncodeRGB565(const Math::Vec4<u8>& color, u8* bytes) { | ||||||
|  */ |  */ | ||||||
| inline void EncodeRGB5A1(const Math::Vec4<u8>& color, u8* bytes) { | inline void EncodeRGB5A1(const Math::Vec4<u8>& color, u8* bytes) { | ||||||
|     *reinterpret_cast<u16_le*>(bytes) = (Convert8To5(color.r()) << 11) | |     *reinterpret_cast<u16_le*>(bytes) = (Convert8To5(color.r()) << 11) | | ||||||
|         (Convert8To5(color.g()) << 6) | (Convert8To5(color.b()) << 1) | Convert8To1(color.a()); |                                         (Convert8To5(color.g()) << 6) | | ||||||
|  |                                         (Convert8To5(color.b()) << 1) | Convert8To1(color.a()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  | @ -196,7 +197,8 @@ inline void EncodeRGB5A1(const Math::Vec4<u8>& color, u8* bytes) { | ||||||
|  */ |  */ | ||||||
| inline void EncodeRGBA4(const Math::Vec4<u8>& color, u8* bytes) { | inline void EncodeRGBA4(const Math::Vec4<u8>& color, u8* bytes) { | ||||||
|     *reinterpret_cast<u16_le*>(bytes) = (Convert8To4(color.r()) << 12) | |     *reinterpret_cast<u16_le*>(bytes) = (Convert8To4(color.r()) << 12) | | ||||||
|         (Convert8To4(color.g()) << 8) | (Convert8To4(color.b()) << 4) | Convert8To4(color.a()); |                                         (Convert8To4(color.g()) << 8) | | ||||||
|  |                                         (Convert8To4(color.b()) << 4) | Convert8To4(color.a()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  |  | ||||||
|  | @ -7,14 +7,13 @@ | ||||||
| #if !defined(ARCHITECTURE_x86_64) && !defined(_M_ARM) | #if !defined(ARCHITECTURE_x86_64) && !defined(_M_ARM) | ||||||
| #include <cstdlib> // for exit | #include <cstdlib> // for exit | ||||||
| #endif | #endif | ||||||
| 
 |  | ||||||
| #include "common_types.h" | #include "common_types.h" | ||||||
| 
 | 
 | ||||||
| #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) | #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) | ||||||
| 
 | 
 | ||||||
| /// Textually concatenates two tokens. The double-expansion is required by the C preprocessor.
 | /// Textually concatenates two tokens. The double-expansion is required by the C preprocessor.
 | ||||||
| #define CONCAT2(x, y) DO_CONCAT2(x, y) | #define CONCAT2(x, y) DO_CONCAT2(x, y) | ||||||
| #define DO_CONCAT2(x, y) x ## y | #define DO_CONCAT2(x, y) x##y | ||||||
| 
 | 
 | ||||||
| // helper macro to properly align structure members.
 | // helper macro to properly align structure members.
 | ||||||
| // Calling INSERT_PADDING_BYTES will add a new member variable with a name like "pad121",
 | // Calling INSERT_PADDING_BYTES will add a new member variable with a name like "pad121",
 | ||||||
|  | @ -24,9 +23,9 @@ | ||||||
| 
 | 
 | ||||||
| // Inlining
 | // Inlining
 | ||||||
| #ifdef _WIN32 | #ifdef _WIN32 | ||||||
|     #define FORCE_INLINE __forceinline | #define FORCE_INLINE __forceinline | ||||||
| #else | #else | ||||||
|     #define FORCE_INLINE inline __attribute__((always_inline)) | #define FORCE_INLINE inline __attribute__((always_inline)) | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #ifndef _MSC_VER | #ifndef _MSC_VER | ||||||
|  | @ -46,7 +45,8 @@ | ||||||
| #else | #else | ||||||
| inline u32 rotl(u32 x, int shift) { | inline u32 rotl(u32 x, int shift) { | ||||||
|     shift &= 31; |     shift &= 31; | ||||||
|     if (!shift) return x; |     if (!shift) | ||||||
|  |         return x; | ||||||
|     return (x << shift) | (x >> (32 - shift)); |     return (x << shift) | (x >> (32 - shift)); | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  | @ -56,17 +56,18 @@ inline u32 rotl(u32 x, int shift) { | ||||||
| #else | #else | ||||||
| inline u32 rotr(u32 x, int shift) { | inline u32 rotr(u32 x, int shift) { | ||||||
|     shift &= 31; |     shift &= 31; | ||||||
|     if (!shift) return x; |     if (!shift) | ||||||
|  |         return x; | ||||||
|     return (x >> shift) | (x << (32 - shift)); |     return (x >> shift) | (x << (32 - shift)); | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| inline u64 _rotl64(u64 x, unsigned int shift){ | inline u64 _rotl64(u64 x, unsigned int shift) { | ||||||
|     unsigned int n = shift % 64; |     unsigned int n = shift % 64; | ||||||
|     return (x << n) | (x >> (64 - n)); |     return (x << n) | (x >> (64 - n)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| inline u64 _rotr64(u64 x, unsigned int shift){ | inline u64 _rotr64(u64 x, unsigned int shift) { | ||||||
|     unsigned int n = shift % 64; |     unsigned int n = shift % 64; | ||||||
|     return (x >> n) | (x << (64 - n)); |     return (x >> n) | (x << (64 - n)); | ||||||
| } | } | ||||||
|  | @ -74,17 +75,17 @@ inline u64 _rotr64(u64 x, unsigned int shift){ | ||||||
| #else // _MSC_VER
 | #else // _MSC_VER
 | ||||||
| 
 | 
 | ||||||
| #if (_MSC_VER < 1900) | #if (_MSC_VER < 1900) | ||||||
|     // Function Cross-Compatibility
 | // Function Cross-Compatibility
 | ||||||
|     #define snprintf _snprintf | #define snprintf _snprintf | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| // Locale Cross-Compatibility
 | // Locale Cross-Compatibility
 | ||||||
| #define locale_t _locale_t | #define locale_t _locale_t | ||||||
| 
 | 
 | ||||||
| extern "C" { | extern "C" { | ||||||
|     __declspec(dllimport) void __stdcall DebugBreak(void); | __declspec(dllimport) void __stdcall DebugBreak(void); | ||||||
| } | } | ||||||
| #define Crash() {DebugBreak();} | #define Crash() DebugBreak() | ||||||
| 
 | 
 | ||||||
| // cstdlib provides these on MSVC
 | // cstdlib provides these on MSVC
 | ||||||
| #define rotr _rotr | #define rotr _rotr | ||||||
|  |  | ||||||
|  | @ -16,13 +16,13 @@ | ||||||
| #define ROOT_DIR "." | #define ROOT_DIR "." | ||||||
| #define USERDATA_DIR "user" | #define USERDATA_DIR "user" | ||||||
| #ifdef USER_DIR | #ifdef USER_DIR | ||||||
|     #define EMU_DATA_DIR USER_DIR | #define EMU_DATA_DIR USER_DIR | ||||||
| #else | #else | ||||||
|     #ifdef _WIN32 | #ifdef _WIN32 | ||||||
|         #define EMU_DATA_DIR "Citra Emulator" | #define EMU_DATA_DIR "Citra Emulator" | ||||||
|     #else | #else | ||||||
|         #define EMU_DATA_DIR "citra-emu" | #define EMU_DATA_DIR "citra-emu" | ||||||
|     #endif | #endif | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| // Dirs in both User and Sys
 | // Dirs in both User and Sys
 | ||||||
|  | @ -31,32 +31,32 @@ | ||||||
| #define JAP_DIR "JAP" | #define JAP_DIR "JAP" | ||||||
| 
 | 
 | ||||||
| // Subdirs in the User dir returned by GetUserPath(D_USER_IDX)
 | // Subdirs in the User dir returned by GetUserPath(D_USER_IDX)
 | ||||||
| #define CONFIG_DIR               "config" | #define CONFIG_DIR "config" | ||||||
| #define GAMECONFIG_DIR           "game_config" | #define GAMECONFIG_DIR "game_config" | ||||||
| #define MAPS_DIR                 "maps" | #define MAPS_DIR "maps" | ||||||
| #define CACHE_DIR                "cache" | #define CACHE_DIR "cache" | ||||||
| #define SDMC_DIR                 "sdmc" | #define SDMC_DIR "sdmc" | ||||||
| #define NAND_DIR                 "nand" | #define NAND_DIR "nand" | ||||||
| #define SYSDATA_DIR              "sysdata" | #define SYSDATA_DIR "sysdata" | ||||||
| #define SHADERCACHE_DIR          "shader_cache" | #define SHADERCACHE_DIR "shader_cache" | ||||||
| #define STATESAVES_DIR           "state_saves" | #define STATESAVES_DIR "state_saves" | ||||||
| #define SCREENSHOTS_DIR          "screenShots" | #define SCREENSHOTS_DIR "screenShots" | ||||||
| #define DUMP_DIR                 "dump" | #define DUMP_DIR "dump" | ||||||
| #define DUMP_TEXTURES_DIR        "textures" | #define DUMP_TEXTURES_DIR "textures" | ||||||
| #define DUMP_FRAMES_DIR          "frames" | #define DUMP_FRAMES_DIR "frames" | ||||||
| #define DUMP_AUDIO_DIR           "audio" | #define DUMP_AUDIO_DIR "audio" | ||||||
| #define LOGS_DIR                 "logs" | #define LOGS_DIR "logs" | ||||||
| #define SHADERS_DIR              "shaders" | #define SHADERS_DIR "shaders" | ||||||
| #define SYSCONF_DIR              "sysconf" | #define SYSCONF_DIR "sysconf" | ||||||
| 
 | 
 | ||||||
| // Filenames
 | // Filenames
 | ||||||
| // Files in the directory returned by GetUserPath(D_CONFIG_IDX)
 | // Files in the directory returned by GetUserPath(D_CONFIG_IDX)
 | ||||||
| #define EMU_CONFIG        "emu.ini" | #define EMU_CONFIG "emu.ini" | ||||||
| #define DEBUGGER_CONFIG   "debugger.ini" | #define DEBUGGER_CONFIG "debugger.ini" | ||||||
| #define LOGGER_CONFIG     "logger.ini" | #define LOGGER_CONFIG "logger.ini" | ||||||
| 
 | 
 | ||||||
| // Sys files
 | // Sys files
 | ||||||
| #define SHARED_FONT       "shared_font.bin" | #define SHARED_FONT "shared_font.bin" | ||||||
| 
 | 
 | ||||||
| // Files in the directory returned by GetUserPath(D_LOGS_IDX)
 | // Files in the directory returned by GetUserPath(D_LOGS_IDX)
 | ||||||
| #define MAIN_LOG "emu.log" | #define MAIN_LOG "emu.log" | ||||||
|  |  | ||||||
|  | @ -32,18 +32,18 @@ | ||||||
| #endif | #endif | ||||||
| #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::uint16_t u16; ///< 16-bit unsigned short
 | ||||||
| typedef std::uint32_t u32; ///< 32-bit unsigned word
 | typedef std::uint32_t u32; ///< 32-bit unsigned word
 | ||||||
| typedef std::uint64_t u64; ///< 64-bit unsigned int
 | 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::int16_t s16; ///< 16-bit signed short
 | ||||||
| typedef std::int32_t s32; ///< 32-bit signed word
 | typedef std::int32_t s32; ///< 32-bit signed word
 | ||||||
| typedef std::int64_t s64; ///< 64-bit signed int
 | typedef std::int64_t s64; ///< 64-bit signed int
 | ||||||
| 
 | 
 | ||||||
| typedef float   f32; ///< 32-bit floating point
 | typedef float f32;  ///< 32-bit floating point
 | ||||||
| typedef double  f64; ///< 64-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
 | // TODO: It would be nice to eventually replace these with strong types that prevent accidental
 | ||||||
| // conversion between each other.
 | // conversion between each other.
 | ||||||
|  |  | ||||||
Some files were not shown because too many files have changed in this diff Show more
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue