diff options
43 files changed, 1406 insertions, 256 deletions
diff --git a/src/common/bit_field.h b/src/common/bit_field.h index 7433c39ba..902e668e3 100644 --- a/src/common/bit_field.h +++ b/src/common/bit_field.h | |||
| @@ -34,6 +34,7 @@ | |||
| 34 | #include <limits> | 34 | #include <limits> |
| 35 | #include <type_traits> | 35 | #include <type_traits> |
| 36 | #include "common/common_funcs.h" | 36 | #include "common/common_funcs.h" |
| 37 | #include "common/swap.h" | ||
| 37 | 38 | ||
| 38 | /* | 39 | /* |
| 39 | * Abstract bitfield class | 40 | * Abstract bitfield class |
| @@ -108,7 +109,7 @@ | |||
| 108 | * symptoms. | 109 | * symptoms. |
| 109 | */ | 110 | */ |
| 110 | #pragma pack(1) | 111 | #pragma pack(1) |
| 111 | template <std::size_t Position, std::size_t Bits, typename T> | 112 | template <std::size_t Position, std::size_t Bits, typename T, typename EndianTag = LETag> |
| 112 | struct BitField { | 113 | struct BitField { |
| 113 | private: | 114 | private: |
| 114 | // UnderlyingType is T for non-enum types and the underlying type of T if | 115 | // UnderlyingType is T for non-enum types and the underlying type of T if |
| @@ -121,6 +122,8 @@ private: | |||
| 121 | // We store the value as the unsigned type to avoid undefined behaviour on value shifting | 122 | // We store the value as the unsigned type to avoid undefined behaviour on value shifting |
| 122 | using StorageType = std::make_unsigned_t<UnderlyingType>; | 123 | using StorageType = std::make_unsigned_t<UnderlyingType>; |
| 123 | 124 | ||
| 125 | using StorageTypeWithEndian = typename AddEndian<StorageType, EndianTag>::type; | ||
| 126 | |||
| 124 | public: | 127 | public: |
| 125 | /// Constants to allow limited introspection of fields if needed | 128 | /// Constants to allow limited introspection of fields if needed |
| 126 | static constexpr std::size_t position = Position; | 129 | static constexpr std::size_t position = Position; |
| @@ -170,7 +173,7 @@ public: | |||
| 170 | } | 173 | } |
| 171 | 174 | ||
| 172 | constexpr FORCE_INLINE void Assign(const T& value) { | 175 | constexpr FORCE_INLINE void Assign(const T& value) { |
| 173 | storage = (storage & ~mask) | FormatValue(value); | 176 | storage = (static_cast<StorageType>(storage) & ~mask) | FormatValue(value); |
| 174 | } | 177 | } |
| 175 | 178 | ||
| 176 | constexpr T Value() const { | 179 | constexpr T Value() const { |
| @@ -182,7 +185,7 @@ public: | |||
| 182 | } | 185 | } |
| 183 | 186 | ||
| 184 | private: | 187 | private: |
| 185 | StorageType storage; | 188 | StorageTypeWithEndian storage; |
| 186 | 189 | ||
| 187 | static_assert(bits + position <= 8 * sizeof(T), "Bitfield out of range"); | 190 | static_assert(bits + position <= 8 * sizeof(T), "Bitfield out of range"); |
| 188 | 191 | ||
| @@ -193,3 +196,6 @@ private: | |||
| 193 | static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable in a BitField"); | 196 | static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable in a BitField"); |
| 194 | }; | 197 | }; |
| 195 | #pragma pack() | 198 | #pragma pack() |
| 199 | |||
| 200 | template <std::size_t Position, std::size_t Bits, typename T> | ||
| 201 | using BitFieldBE = BitField<Position, Bits, T, BETag>; | ||
diff --git a/src/common/swap.h b/src/common/swap.h index 0e219747f..b3eab1324 100644 --- a/src/common/swap.h +++ b/src/common/swap.h | |||
| @@ -17,6 +17,8 @@ | |||
| 17 | 17 | ||
| 18 | #pragma once | 18 | #pragma once |
| 19 | 19 | ||
| 20 | #include <type_traits> | ||
| 21 | |||
| 20 | #if defined(_MSC_VER) | 22 | #if defined(_MSC_VER) |
| 21 | #include <cstdlib> | 23 | #include <cstdlib> |
| 22 | #elif defined(__linux__) | 24 | #elif defined(__linux__) |
| @@ -170,7 +172,7 @@ struct swap_struct_t { | |||
| 170 | using swapped_t = swap_struct_t; | 172 | using swapped_t = swap_struct_t; |
| 171 | 173 | ||
| 172 | protected: | 174 | protected: |
| 173 | T value = T(); | 175 | T value; |
| 174 | 176 | ||
| 175 | static T swap(T v) { | 177 | static T swap(T v) { |
| 176 | return F::swap(v); | 178 | return F::swap(v); |
| @@ -605,52 +607,154 @@ struct swap_double_t { | |||
| 605 | } | 607 | } |
| 606 | }; | 608 | }; |
| 607 | 609 | ||
| 608 | #if COMMON_LITTLE_ENDIAN | 610 | template <typename T> |
| 609 | using u16_le = u16; | 611 | struct swap_enum_t { |
| 610 | using u32_le = u32; | 612 | static_assert(std::is_enum_v<T>); |
| 611 | using u64_le = u64; | 613 | using base = std::underlying_type_t<T>; |
| 614 | |||
| 615 | public: | ||
| 616 | swap_enum_t() = default; | ||
| 617 | swap_enum_t(const T& v) : value(swap(v)) {} | ||
| 618 | |||
| 619 | swap_enum_t& operator=(const T& v) { | ||
| 620 | value = swap(v); | ||
| 621 | return *this; | ||
| 622 | } | ||
| 623 | |||
| 624 | operator T() const { | ||
| 625 | return swap(value); | ||
| 626 | } | ||
| 627 | |||
| 628 | explicit operator base() const { | ||
| 629 | return static_cast<base>(swap(value)); | ||
| 630 | } | ||
| 612 | 631 | ||
| 613 | using s16_le = s16; | 632 | protected: |
| 614 | using s32_le = s32; | 633 | T value{}; |
| 615 | using s64_le = s64; | 634 | // clang-format off |
| 635 | using swap_t = std::conditional_t< | ||
| 636 | std::is_same_v<base, u16>, swap_16_t<u16>, std::conditional_t< | ||
| 637 | std::is_same_v<base, s16>, swap_16_t<s16>, std::conditional_t< | ||
| 638 | std::is_same_v<base, u32>, swap_32_t<u32>, std::conditional_t< | ||
| 639 | std::is_same_v<base, s32>, swap_32_t<s32>, std::conditional_t< | ||
| 640 | std::is_same_v<base, u64>, swap_64_t<u64>, std::conditional_t< | ||
| 641 | std::is_same_v<base, s64>, swap_64_t<s64>, void>>>>>>; | ||
| 642 | // clang-format on | ||
| 643 | static T swap(T x) { | ||
| 644 | return static_cast<T>(swap_t::swap(static_cast<base>(x))); | ||
| 645 | } | ||
| 646 | }; | ||
| 616 | 647 | ||
| 617 | using float_le = float; | 648 | struct SwapTag {}; // Use the different endianness from the system |
| 618 | using double_le = double; | 649 | struct KeepTag {}; // Use the same endianness as the system |
| 619 | 650 | ||
| 620 | using u64_be = swap_struct_t<u64, swap_64_t<u64>>; | 651 | template <typename T, typename Tag> |
| 621 | using s64_be = swap_struct_t<s64, swap_64_t<s64>>; | 652 | struct AddEndian; |
| 622 | 653 | ||
| 623 | using u32_be = swap_struct_t<u32, swap_32_t<u32>>; | 654 | // KeepTag specializations |
| 624 | using s32_be = swap_struct_t<s32, swap_32_t<s32>>; | ||
| 625 | 655 | ||
| 626 | using u16_be = swap_struct_t<u16, swap_16_t<u16>>; | 656 | template <typename T> |
| 627 | using s16_be = swap_struct_t<s16, swap_16_t<s16>>; | 657 | struct AddEndian<T, KeepTag> { |
| 658 | using type = T; | ||
| 659 | }; | ||
| 628 | 660 | ||
| 629 | using float_be = swap_struct_t<float, swap_float_t<float>>; | 661 | // SwapTag specializations |
| 630 | using double_be = swap_struct_t<double, swap_double_t<double>>; | 662 | |
| 631 | #else | 663 | template <> |
| 664 | struct AddEndian<u8, SwapTag> { | ||
| 665 | using type = u8; | ||
| 666 | }; | ||
| 667 | |||
| 668 | template <> | ||
| 669 | struct AddEndian<u16, SwapTag> { | ||
| 670 | using type = swap_struct_t<u16, swap_16_t<u16>>; | ||
| 671 | }; | ||
| 672 | |||
| 673 | template <> | ||
| 674 | struct AddEndian<u32, SwapTag> { | ||
| 675 | using type = swap_struct_t<u32, swap_32_t<u32>>; | ||
| 676 | }; | ||
| 632 | 677 | ||
| 633 | using u64_le = swap_struct_t<u64, swap_64_t<u64>>; | 678 | template <> |
| 634 | using s64_le = swap_struct_t<s64, swap_64_t<s64>>; | 679 | struct AddEndian<u64, SwapTag> { |
| 680 | using type = swap_struct_t<u64, swap_64_t<u64>>; | ||
| 681 | }; | ||
| 682 | |||
| 683 | template <> | ||
| 684 | struct AddEndian<s8, SwapTag> { | ||
| 685 | using type = s8; | ||
| 686 | }; | ||
| 635 | 687 | ||
| 636 | using u32_le = swap_struct_t<u32, swap_32_t<u32>>; | 688 | template <> |
| 637 | using s32_le = swap_struct_t<s32, swap_32_t<s32>>; | 689 | struct AddEndian<s16, SwapTag> { |
| 690 | using type = swap_struct_t<s16, swap_16_t<s16>>; | ||
| 691 | }; | ||
| 638 | 692 | ||
| 639 | using u16_le = swap_struct_t<u16, swap_16_t<u16>>; | 693 | template <> |
| 640 | using s16_le = swap_struct_t<s16, swap_16_t<s16>>; | 694 | struct AddEndian<s32, SwapTag> { |
| 695 | using type = swap_struct_t<s32, swap_32_t<s32>>; | ||
| 696 | }; | ||
| 697 | |||
| 698 | template <> | ||
| 699 | struct AddEndian<s64, SwapTag> { | ||
| 700 | using type = swap_struct_t<s64, swap_64_t<s64>>; | ||
| 701 | }; | ||
| 702 | |||
| 703 | template <> | ||
| 704 | struct AddEndian<float, SwapTag> { | ||
| 705 | using type = swap_struct_t<float, swap_float_t<float>>; | ||
| 706 | }; | ||
| 707 | |||
| 708 | template <> | ||
| 709 | struct AddEndian<double, SwapTag> { | ||
| 710 | using type = swap_struct_t<double, swap_double_t<double>>; | ||
| 711 | }; | ||
| 712 | |||
| 713 | template <typename T> | ||
| 714 | struct AddEndian<T, SwapTag> { | ||
| 715 | static_assert(std::is_enum_v<T>); | ||
| 716 | using type = swap_enum_t<T>; | ||
| 717 | }; | ||
| 641 | 718 | ||
| 642 | using float_le = swap_struct_t<float, swap_float_t<float>>; | 719 | // Alias LETag/BETag as KeepTag/SwapTag depending on the system |
| 643 | using double_le = swap_struct_t<double, swap_double_t<double>>; | 720 | #if COMMON_LITTLE_ENDIAN |
| 644 | 721 | ||
| 645 | using u16_be = u16; | 722 | using LETag = KeepTag; |
| 646 | using u32_be = u32; | 723 | using BETag = SwapTag; |
| 647 | using u64_be = u64; | ||
| 648 | 724 | ||
| 649 | using s16_be = s16; | 725 | #else |
| 650 | using s32_be = s32; | ||
| 651 | using s64_be = s64; | ||
| 652 | 726 | ||
| 653 | using float_be = float; | 727 | using BETag = KeepTag; |
| 654 | using double_be = double; | 728 | using LETag = SwapTag; |
| 655 | 729 | ||
| 656 | #endif | 730 | #endif |
| 731 | |||
| 732 | // Aliases for LE types | ||
| 733 | using u16_le = AddEndian<u16, LETag>::type; | ||
| 734 | using u32_le = AddEndian<u32, LETag>::type; | ||
| 735 | using u64_le = AddEndian<u64, LETag>::type; | ||
| 736 | |||
| 737 | using s16_le = AddEndian<s16, LETag>::type; | ||
| 738 | using s32_le = AddEndian<s32, LETag>::type; | ||
| 739 | using s64_le = AddEndian<s64, LETag>::type; | ||
| 740 | |||
| 741 | template <typename T> | ||
| 742 | using enum_le = std::enable_if_t<std::is_enum_v<T>, typename AddEndian<T, LETag>::type>; | ||
| 743 | |||
| 744 | using float_le = AddEndian<float, LETag>::type; | ||
| 745 | using double_le = AddEndian<double, LETag>::type; | ||
| 746 | |||
| 747 | // Aliases for BE types | ||
| 748 | using u16_be = AddEndian<u16, BETag>::type; | ||
| 749 | using u32_be = AddEndian<u32, BETag>::type; | ||
| 750 | using u64_be = AddEndian<u64, BETag>::type; | ||
| 751 | |||
| 752 | using s16_be = AddEndian<s16, BETag>::type; | ||
| 753 | using s32_be = AddEndian<s32, BETag>::type; | ||
| 754 | using s64_be = AddEndian<s64, BETag>::type; | ||
| 755 | |||
| 756 | template <typename T> | ||
| 757 | using enum_be = std::enable_if_t<std::is_enum_v<T>, typename AddEndian<T, BETag>::type>; | ||
| 758 | |||
| 759 | using float_be = AddEndian<float, BETag>::type; | ||
| 760 | using double_be = AddEndian<double, BETag>::type; | ||
diff --git a/src/common/uint128.cpp b/src/common/uint128.cpp index 2238a52c5..32bf56730 100644 --- a/src/common/uint128.cpp +++ b/src/common/uint128.cpp | |||
| @@ -1,3 +1,7 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 1 | #ifdef _MSC_VER | 5 | #ifdef _MSC_VER |
| 2 | #include <intrin.h> | 6 | #include <intrin.h> |
| 3 | 7 | ||
diff --git a/src/common/uint128.h b/src/common/uint128.h index 52e6b46eb..a3be2a2cb 100644 --- a/src/common/uint128.h +++ b/src/common/uint128.h | |||
| @@ -1,3 +1,8 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 1 | 6 | ||
| 2 | #include <utility> | 7 | #include <utility> |
| 3 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 6319414ba..bbbe60896 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -31,6 +31,8 @@ add_library(core STATIC | |||
| 31 | file_sys/bis_factory.h | 31 | file_sys/bis_factory.h |
| 32 | file_sys/card_image.cpp | 32 | file_sys/card_image.cpp |
| 33 | file_sys/card_image.h | 33 | file_sys/card_image.h |
| 34 | file_sys/cheat_engine.cpp | ||
| 35 | file_sys/cheat_engine.h | ||
| 34 | file_sys/content_archive.cpp | 36 | file_sys/content_archive.cpp |
| 35 | file_sys/content_archive.h | 37 | file_sys/content_archive.h |
| 36 | file_sys/control_metadata.cpp | 38 | file_sys/control_metadata.cpp |
| @@ -107,6 +109,8 @@ add_library(core STATIC | |||
| 107 | hle/kernel/client_port.h | 109 | hle/kernel/client_port.h |
| 108 | hle/kernel/client_session.cpp | 110 | hle/kernel/client_session.cpp |
| 109 | hle/kernel/client_session.h | 111 | hle/kernel/client_session.h |
| 112 | hle/kernel/code_set.cpp | ||
| 113 | hle/kernel/code_set.h | ||
| 110 | hle/kernel/errors.h | 114 | hle/kernel/errors.h |
| 111 | hle/kernel/handle_table.cpp | 115 | hle/kernel/handle_table.cpp |
| 112 | hle/kernel/handle_table.h | 116 | hle/kernel/handle_table.h |
diff --git a/src/core/core.cpp b/src/core/core.cpp index 89b3fb418..a88e332be 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -32,6 +32,7 @@ | |||
| 32 | #include "core/perf_stats.h" | 32 | #include "core/perf_stats.h" |
| 33 | #include "core/settings.h" | 33 | #include "core/settings.h" |
| 34 | #include "core/telemetry_session.h" | 34 | #include "core/telemetry_session.h" |
| 35 | #include "file_sys/cheat_engine.h" | ||
| 35 | #include "frontend/applets/profile_select.h" | 36 | #include "frontend/applets/profile_select.h" |
| 36 | #include "frontend/applets/software_keyboard.h" | 37 | #include "frontend/applets/software_keyboard.h" |
| 37 | #include "frontend/applets/web_browser.h" | 38 | #include "frontend/applets/web_browser.h" |
| @@ -205,6 +206,7 @@ struct System::Impl { | |||
| 205 | GDBStub::Shutdown(); | 206 | GDBStub::Shutdown(); |
| 206 | Service::Shutdown(); | 207 | Service::Shutdown(); |
| 207 | service_manager.reset(); | 208 | service_manager.reset(); |
| 209 | cheat_engine.reset(); | ||
| 208 | telemetry_session.reset(); | 210 | telemetry_session.reset(); |
| 209 | gpu_core.reset(); | 211 | gpu_core.reset(); |
| 210 | 212 | ||
| @@ -255,6 +257,8 @@ struct System::Impl { | |||
| 255 | CpuCoreManager cpu_core_manager; | 257 | CpuCoreManager cpu_core_manager; |
| 256 | bool is_powered_on = false; | 258 | bool is_powered_on = false; |
| 257 | 259 | ||
| 260 | std::unique_ptr<FileSys::CheatEngine> cheat_engine; | ||
| 261 | |||
| 258 | /// Frontend applets | 262 | /// Frontend applets |
| 259 | std::unique_ptr<Core::Frontend::ProfileSelectApplet> profile_selector; | 263 | std::unique_ptr<Core::Frontend::ProfileSelectApplet> profile_selector; |
| 260 | std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard; | 264 | std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard; |
| @@ -453,6 +457,13 @@ Tegra::DebugContext* System::GetGPUDebugContext() const { | |||
| 453 | return impl->debug_context.get(); | 457 | return impl->debug_context.get(); |
| 454 | } | 458 | } |
| 455 | 459 | ||
| 460 | void System::RegisterCheatList(const std::vector<FileSys::CheatList>& list, | ||
| 461 | const std::string& build_id, VAddr code_region_start, | ||
| 462 | VAddr code_region_end) { | ||
| 463 | impl->cheat_engine = | ||
| 464 | std::make_unique<FileSys::CheatEngine>(list, build_id, code_region_start, code_region_end); | ||
| 465 | } | ||
| 466 | |||
| 456 | void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) { | 467 | void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) { |
| 457 | impl->virtual_filesystem = std::move(vfs); | 468 | impl->virtual_filesystem = std::move(vfs); |
| 458 | } | 469 | } |
diff --git a/src/core/core.h b/src/core/core.h index ba76a41d8..4d83b93cc 100644 --- a/src/core/core.h +++ b/src/core/core.h | |||
| @@ -20,6 +20,7 @@ class WebBrowserApplet; | |||
| 20 | } // namespace Core::Frontend | 20 | } // namespace Core::Frontend |
| 21 | 21 | ||
| 22 | namespace FileSys { | 22 | namespace FileSys { |
| 23 | class CheatList; | ||
| 23 | class VfsFilesystem; | 24 | class VfsFilesystem; |
| 24 | } // namespace FileSys | 25 | } // namespace FileSys |
| 25 | 26 | ||
| @@ -253,6 +254,9 @@ public: | |||
| 253 | 254 | ||
| 254 | std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const; | 255 | std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const; |
| 255 | 256 | ||
| 257 | void RegisterCheatList(const std::vector<FileSys::CheatList>& list, const std::string& build_id, | ||
| 258 | VAddr code_region_start, VAddr code_region_end); | ||
| 259 | |||
| 256 | void SetProfileSelector(std::unique_ptr<Frontend::ProfileSelectApplet> applet); | 260 | void SetProfileSelector(std::unique_ptr<Frontend::ProfileSelectApplet> applet); |
| 257 | 261 | ||
| 258 | const Frontend::ProfileSelectApplet& GetProfileSelector() const; | 262 | const Frontend::ProfileSelectApplet& GetProfileSelector() const; |
diff --git a/src/core/file_sys/cheat_engine.cpp b/src/core/file_sys/cheat_engine.cpp new file mode 100644 index 000000000..09ca9d705 --- /dev/null +++ b/src/core/file_sys/cheat_engine.cpp | |||
| @@ -0,0 +1,493 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <locale> | ||
| 6 | #include "common/hex_util.h" | ||
| 7 | #include "common/microprofile.h" | ||
| 8 | #include "common/swap.h" | ||
| 9 | #include "core/core.h" | ||
| 10 | #include "core/core_timing.h" | ||
| 11 | #include "core/core_timing_util.h" | ||
| 12 | #include "core/file_sys/cheat_engine.h" | ||
| 13 | #include "core/hle/kernel/process.h" | ||
| 14 | #include "core/hle/service/hid/controllers/controller_base.h" | ||
| 15 | #include "core/hle/service/hid/controllers/npad.h" | ||
| 16 | #include "core/hle/service/hid/hid.h" | ||
| 17 | #include "core/hle/service/sm/sm.h" | ||
| 18 | |||
| 19 | namespace FileSys { | ||
| 20 | |||
| 21 | constexpr u64 CHEAT_ENGINE_TICKS = Core::Timing::BASE_CLOCK_RATE / 60; | ||
| 22 | constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF; | ||
| 23 | |||
| 24 | u64 Cheat::Address() const { | ||
| 25 | u64 out; | ||
| 26 | std::memcpy(&out, raw.data(), sizeof(u64)); | ||
| 27 | return Common::swap64(out) & 0xFFFFFFFFFF; | ||
| 28 | } | ||
| 29 | |||
| 30 | u64 Cheat::ValueWidth(u64 offset) const { | ||
| 31 | return Value(offset, width); | ||
| 32 | } | ||
| 33 | |||
| 34 | u64 Cheat::Value(u64 offset, u64 width) const { | ||
| 35 | u64 out; | ||
| 36 | std::memcpy(&out, raw.data() + offset, sizeof(u64)); | ||
| 37 | out = Common::swap64(out); | ||
| 38 | if (width == 8) | ||
| 39 | return out; | ||
| 40 | return out & ((1ull << (width * CHAR_BIT)) - 1); | ||
| 41 | } | ||
| 42 | |||
| 43 | u32 Cheat::KeypadValue() const { | ||
| 44 | u32 out; | ||
| 45 | std::memcpy(&out, raw.data(), sizeof(u32)); | ||
| 46 | return Common::swap32(out) & 0x0FFFFFFF; | ||
| 47 | } | ||
| 48 | |||
| 49 | void CheatList::SetMemoryParameters(VAddr main_begin, VAddr heap_begin, VAddr main_end, | ||
| 50 | VAddr heap_end, MemoryWriter writer, MemoryReader reader) { | ||
| 51 | this->main_region_begin = main_begin; | ||
| 52 | this->main_region_end = main_end; | ||
| 53 | this->heap_region_begin = heap_begin; | ||
| 54 | this->heap_region_end = heap_end; | ||
| 55 | this->writer = writer; | ||
| 56 | this->reader = reader; | ||
| 57 | } | ||
| 58 | |||
| 59 | MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70)); | ||
| 60 | |||
| 61 | void CheatList::Execute() { | ||
| 62 | MICROPROFILE_SCOPE(Cheat_Engine); | ||
| 63 | |||
| 64 | std::fill(scratch.begin(), scratch.end(), 0); | ||
| 65 | in_standard = false; | ||
| 66 | for (std::size_t i = 0; i < master_list.size(); ++i) { | ||
| 67 | LOG_DEBUG(Common_Filesystem, "Executing block #{:08X} ({})", i, master_list[i].first); | ||
| 68 | current_block = i; | ||
| 69 | ExecuteBlock(master_list[i].second); | ||
| 70 | } | ||
| 71 | |||
| 72 | in_standard = true; | ||
| 73 | for (std::size_t i = 0; i < standard_list.size(); ++i) { | ||
| 74 | LOG_DEBUG(Common_Filesystem, "Executing block #{:08X} ({})", i, standard_list[i].first); | ||
| 75 | current_block = i; | ||
| 76 | ExecuteBlock(standard_list[i].second); | ||
| 77 | } | ||
| 78 | } | ||
| 79 | |||
| 80 | CheatList::CheatList(ProgramSegment master, ProgramSegment standard) | ||
| 81 | : master_list(master), standard_list(standard) {} | ||
| 82 | |||
| 83 | bool CheatList::EvaluateConditional(const Cheat& cheat) const { | ||
| 84 | using ComparisonFunction = bool (*)(u64, u64); | ||
| 85 | constexpr std::array<ComparisonFunction, 6> comparison_functions{ | ||
| 86 | [](u64 a, u64 b) { return a > b; }, [](u64 a, u64 b) { return a >= b; }, | ||
| 87 | [](u64 a, u64 b) { return a < b; }, [](u64 a, u64 b) { return a <= b; }, | ||
| 88 | [](u64 a, u64 b) { return a == b; }, [](u64 a, u64 b) { return a != b; }, | ||
| 89 | }; | ||
| 90 | |||
| 91 | if (cheat.type == CodeType::ConditionalInput) { | ||
| 92 | const auto applet_resource = Core::System::GetInstance() | ||
| 93 | .ServiceManager() | ||
| 94 | .GetService<Service::HID::Hid>("hid") | ||
| 95 | ->GetAppletResource(); | ||
| 96 | if (applet_resource == nullptr) { | ||
| 97 | LOG_WARNING( | ||
| 98 | Common_Filesystem, | ||
| 99 | "Attempted to evaluate input conditional, but applet resource is not initialized!"); | ||
| 100 | return false; | ||
| 101 | } | ||
| 102 | |||
| 103 | const auto press_state = | ||
| 104 | applet_resource | ||
| 105 | ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad) | ||
| 106 | .GetAndResetPressState(); | ||
| 107 | return ((press_state & cheat.KeypadValue()) & KEYPAD_BITMASK) != 0; | ||
| 108 | } | ||
| 109 | |||
| 110 | ASSERT(cheat.type == CodeType::Conditional); | ||
| 111 | |||
| 112 | const auto offset = | ||
| 113 | cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin; | ||
| 114 | ASSERT(static_cast<u8>(cheat.comparison_op.Value()) < 6); | ||
| 115 | auto* function = comparison_functions[static_cast<u8>(cheat.comparison_op.Value())]; | ||
| 116 | const auto addr = cheat.Address() + offset; | ||
| 117 | |||
| 118 | return function(reader(cheat.width, SanitizeAddress(addr)), cheat.ValueWidth(8)); | ||
| 119 | } | ||
| 120 | |||
| 121 | void CheatList::ProcessBlockPairs(const Block& block) { | ||
| 122 | block_pairs.clear(); | ||
| 123 | |||
| 124 | u64 scope = 0; | ||
| 125 | std::map<u64, u64> pairs; | ||
| 126 | |||
| 127 | for (std::size_t i = 0; i < block.size(); ++i) { | ||
| 128 | const auto& cheat = block[i]; | ||
| 129 | |||
| 130 | switch (cheat.type) { | ||
| 131 | case CodeType::Conditional: | ||
| 132 | case CodeType::ConditionalInput: | ||
| 133 | pairs.insert_or_assign(scope, i); | ||
| 134 | ++scope; | ||
| 135 | break; | ||
| 136 | case CodeType::EndConditional: { | ||
| 137 | --scope; | ||
| 138 | const auto idx = pairs.at(scope); | ||
| 139 | block_pairs.insert_or_assign(idx, i); | ||
| 140 | break; | ||
| 141 | } | ||
| 142 | case CodeType::Loop: { | ||
| 143 | if (cheat.end_of_loop) { | ||
| 144 | --scope; | ||
| 145 | const auto idx = pairs.at(scope); | ||
| 146 | block_pairs.insert_or_assign(idx, i); | ||
| 147 | } else { | ||
| 148 | pairs.insert_or_assign(scope, i); | ||
| 149 | ++scope; | ||
| 150 | } | ||
| 151 | break; | ||
| 152 | } | ||
| 153 | } | ||
| 154 | } | ||
| 155 | } | ||
| 156 | |||
| 157 | void CheatList::WriteImmediate(const Cheat& cheat) { | ||
| 158 | const auto offset = | ||
| 159 | cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin; | ||
| 160 | const auto& register_3 = scratch.at(cheat.register_3); | ||
| 161 | |||
| 162 | const auto addr = cheat.Address() + offset + register_3; | ||
| 163 | LOG_DEBUG(Common_Filesystem, "writing value={:016X} to addr={:016X}", addr, | ||
| 164 | cheat.Value(8, cheat.width)); | ||
| 165 | writer(cheat.width, SanitizeAddress(addr), cheat.ValueWidth(8)); | ||
| 166 | } | ||
| 167 | |||
| 168 | void CheatList::BeginConditional(const Cheat& cheat) { | ||
| 169 | if (EvaluateConditional(cheat)) { | ||
| 170 | return; | ||
| 171 | } | ||
| 172 | |||
| 173 | const auto iter = block_pairs.find(current_index); | ||
| 174 | ASSERT(iter != block_pairs.end()); | ||
| 175 | current_index = iter->second - 1; | ||
| 176 | } | ||
| 177 | |||
| 178 | void CheatList::EndConditional(const Cheat& cheat) { | ||
| 179 | LOG_DEBUG(Common_Filesystem, "Ending conditional block."); | ||
| 180 | } | ||
| 181 | |||
| 182 | void CheatList::Loop(const Cheat& cheat) { | ||
| 183 | if (cheat.end_of_loop.Value()) | ||
| 184 | ASSERT(!cheat.end_of_loop.Value()); | ||
| 185 | |||
| 186 | auto& register_3 = scratch.at(cheat.register_3); | ||
| 187 | const auto iter = block_pairs.find(current_index); | ||
| 188 | ASSERT(iter != block_pairs.end()); | ||
| 189 | ASSERT(iter->first < iter->second); | ||
| 190 | |||
| 191 | for (int i = cheat.Value(4, 4); i >= 0; --i) { | ||
| 192 | register_3 = i; | ||
| 193 | for (std::size_t c = iter->first + 1; c < iter->second; ++c) { | ||
| 194 | current_index = c; | ||
| 195 | ExecuteSingleCheat( | ||
| 196 | (in_standard ? standard_list : master_list)[current_block].second[c]); | ||
| 197 | } | ||
| 198 | } | ||
| 199 | |||
| 200 | current_index = iter->second; | ||
| 201 | } | ||
| 202 | |||
| 203 | void CheatList::LoadImmediate(const Cheat& cheat) { | ||
| 204 | auto& register_3 = scratch.at(cheat.register_3); | ||
| 205 | |||
| 206 | LOG_DEBUG(Common_Filesystem, "setting register={:01X} equal to value={:016X}", cheat.register_3, | ||
| 207 | cheat.Value(4, 8)); | ||
| 208 | register_3 = cheat.Value(4, 8); | ||
| 209 | } | ||
| 210 | |||
| 211 | void CheatList::LoadIndexed(const Cheat& cheat) { | ||
| 212 | const auto offset = | ||
| 213 | cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin; | ||
| 214 | auto& register_3 = scratch.at(cheat.register_3); | ||
| 215 | |||
| 216 | const auto addr = (cheat.load_from_register.Value() ? register_3 : offset) + cheat.Address(); | ||
| 217 | LOG_DEBUG(Common_Filesystem, "writing indexed value to register={:01X}, addr={:016X}", | ||
| 218 | cheat.register_3, addr); | ||
| 219 | register_3 = reader(cheat.width, SanitizeAddress(addr)); | ||
| 220 | } | ||
| 221 | |||
| 222 | void CheatList::StoreIndexed(const Cheat& cheat) { | ||
| 223 | const auto& register_3 = scratch.at(cheat.register_3); | ||
| 224 | |||
| 225 | const auto addr = | ||
| 226 | register_3 + (cheat.add_additional_register.Value() ? scratch.at(cheat.register_6) : 0); | ||
| 227 | LOG_DEBUG(Common_Filesystem, "writing value={:016X} to addr={:016X}", | ||
| 228 | cheat.Value(4, cheat.width), addr); | ||
| 229 | writer(cheat.width, SanitizeAddress(addr), cheat.ValueWidth(4)); | ||
| 230 | } | ||
| 231 | |||
| 232 | void CheatList::RegisterArithmetic(const Cheat& cheat) { | ||
| 233 | using ArithmeticFunction = u64 (*)(u64, u64); | ||
| 234 | constexpr std::array<ArithmeticFunction, 5> arithmetic_functions{ | ||
| 235 | [](u64 a, u64 b) { return a + b; }, [](u64 a, u64 b) { return a - b; }, | ||
| 236 | [](u64 a, u64 b) { return a * b; }, [](u64 a, u64 b) { return a << b; }, | ||
| 237 | [](u64 a, u64 b) { return a >> b; }, | ||
| 238 | }; | ||
| 239 | |||
| 240 | using ArithmeticOverflowCheck = bool (*)(u64, u64); | ||
| 241 | constexpr std::array<ArithmeticOverflowCheck, 5> arithmetic_overflow_checks{ | ||
| 242 | [](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() - b); }, // a + b | ||
| 243 | [](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() + b); }, // a - b | ||
| 244 | [](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() / b); }, // a * b | ||
| 245 | [](u64 a, u64 b) { return b >= 64 || (a & ~((1ull << (64 - b)) - 1)) != 0; }, // a << b | ||
| 246 | [](u64 a, u64 b) { return b >= 64 || (a & ((1ull << b) - 1)) != 0; }, // a >> b | ||
| 247 | }; | ||
| 248 | |||
| 249 | static_assert(sizeof(arithmetic_functions) == sizeof(arithmetic_overflow_checks), | ||
| 250 | "Missing or have extra arithmetic overflow checks compared to functions!"); | ||
| 251 | |||
| 252 | auto& register_3 = scratch.at(cheat.register_3); | ||
| 253 | |||
| 254 | ASSERT(static_cast<u8>(cheat.arithmetic_op.Value()) < 5); | ||
| 255 | auto* function = arithmetic_functions[static_cast<u8>(cheat.arithmetic_op.Value())]; | ||
| 256 | auto* overflow_function = | ||
| 257 | arithmetic_overflow_checks[static_cast<u8>(cheat.arithmetic_op.Value())]; | ||
| 258 | LOG_DEBUG(Common_Filesystem, "performing arithmetic with register={:01X}, value={:016X}", | ||
| 259 | cheat.register_3, cheat.ValueWidth(4)); | ||
| 260 | |||
| 261 | if (overflow_function(register_3, cheat.ValueWidth(4))) { | ||
| 262 | LOG_WARNING(Common_Filesystem, | ||
| 263 | "overflow will occur when performing arithmetic operation={:02X} with operands " | ||
| 264 | "a={:016X}, b={:016X}!", | ||
| 265 | static_cast<u8>(cheat.arithmetic_op.Value()), register_3, cheat.ValueWidth(4)); | ||
| 266 | } | ||
| 267 | |||
| 268 | register_3 = function(register_3, cheat.ValueWidth(4)); | ||
| 269 | } | ||
| 270 | |||
| 271 | void CheatList::BeginConditionalInput(const Cheat& cheat) { | ||
| 272 | if (EvaluateConditional(cheat)) | ||
| 273 | return; | ||
| 274 | |||
| 275 | const auto iter = block_pairs.find(current_index); | ||
| 276 | ASSERT(iter != block_pairs.end()); | ||
| 277 | current_index = iter->second - 1; | ||
| 278 | } | ||
| 279 | |||
| 280 | VAddr CheatList::SanitizeAddress(VAddr in) const { | ||
| 281 | if ((in < main_region_begin || in >= main_region_end) && | ||
| 282 | (in < heap_region_begin || in >= heap_region_end)) { | ||
| 283 | LOG_ERROR(Common_Filesystem, | ||
| 284 | "Cheat attempting to access memory at invalid address={:016X}, if this persists, " | ||
| 285 | "the cheat may be incorrect. However, this may be normal early in execution if " | ||
| 286 | "the game has not properly set up yet.", | ||
| 287 | in); | ||
| 288 | return 0; ///< Invalid addresses will hard crash | ||
| 289 | } | ||
| 290 | |||
| 291 | return in; | ||
| 292 | } | ||
| 293 | |||
| 294 | void CheatList::ExecuteSingleCheat(const Cheat& cheat) { | ||
| 295 | using CheatOperationFunction = void (CheatList::*)(const Cheat&); | ||
| 296 | constexpr std::array<CheatOperationFunction, 9> cheat_operation_functions{ | ||
| 297 | &CheatList::WriteImmediate, &CheatList::BeginConditional, | ||
| 298 | &CheatList::EndConditional, &CheatList::Loop, | ||
| 299 | &CheatList::LoadImmediate, &CheatList::LoadIndexed, | ||
| 300 | &CheatList::StoreIndexed, &CheatList::RegisterArithmetic, | ||
| 301 | &CheatList::BeginConditionalInput, | ||
| 302 | }; | ||
| 303 | |||
| 304 | const auto index = static_cast<u8>(cheat.type.Value()); | ||
| 305 | ASSERT(index < sizeof(cheat_operation_functions)); | ||
| 306 | const auto op = cheat_operation_functions[index]; | ||
| 307 | (this->*op)(cheat); | ||
| 308 | } | ||
| 309 | |||
| 310 | void CheatList::ExecuteBlock(const Block& block) { | ||
| 311 | encountered_loops.clear(); | ||
| 312 | |||
| 313 | ProcessBlockPairs(block); | ||
| 314 | for (std::size_t i = 0; i < block.size(); ++i) { | ||
| 315 | current_index = i; | ||
| 316 | ExecuteSingleCheat(block[i]); | ||
| 317 | i = current_index; | ||
| 318 | } | ||
| 319 | } | ||
| 320 | |||
| 321 | CheatParser::~CheatParser() = default; | ||
| 322 | |||
| 323 | CheatList CheatParser::MakeCheatList(CheatList::ProgramSegment master, | ||
| 324 | CheatList::ProgramSegment standard) const { | ||
| 325 | return {master, standard}; | ||
| 326 | } | ||
| 327 | |||
| 328 | TextCheatParser::~TextCheatParser() = default; | ||
| 329 | |||
| 330 | CheatList TextCheatParser::Parse(const std::vector<u8>& data) const { | ||
| 331 | std::stringstream ss; | ||
| 332 | ss.write(reinterpret_cast<const char*>(data.data()), data.size()); | ||
| 333 | |||
| 334 | std::vector<std::string> lines; | ||
| 335 | std::string stream_line; | ||
| 336 | while (std::getline(ss, stream_line)) { | ||
| 337 | // Remove a trailing \r | ||
| 338 | if (!stream_line.empty() && stream_line.back() == '\r') | ||
| 339 | stream_line.pop_back(); | ||
| 340 | lines.push_back(std::move(stream_line)); | ||
| 341 | } | ||
| 342 | |||
| 343 | CheatList::ProgramSegment master_list; | ||
| 344 | CheatList::ProgramSegment standard_list; | ||
| 345 | |||
| 346 | for (std::size_t i = 0; i < lines.size(); ++i) { | ||
| 347 | auto line = lines[i]; | ||
| 348 | |||
| 349 | if (!line.empty() && (line[0] == '[' || line[0] == '{')) { | ||
| 350 | const auto master = line[0] == '{'; | ||
| 351 | const auto begin = master ? line.find('{') : line.find('['); | ||
| 352 | const auto end = master ? line.rfind('}') : line.rfind(']'); | ||
| 353 | |||
| 354 | ASSERT(begin != std::string::npos && end != std::string::npos); | ||
| 355 | |||
| 356 | const std::string patch_name{line.begin() + begin + 1, line.begin() + end}; | ||
| 357 | CheatList::Block block{}; | ||
| 358 | |||
| 359 | while (i < lines.size() - 1) { | ||
| 360 | line = lines[++i]; | ||
| 361 | if (!line.empty() && (line[0] == '[' || line[0] == '{')) { | ||
| 362 | --i; | ||
| 363 | break; | ||
| 364 | } | ||
| 365 | |||
| 366 | if (line.size() < 8) | ||
| 367 | continue; | ||
| 368 | |||
| 369 | Cheat out{}; | ||
| 370 | out.raw = ParseSingleLineCheat(line); | ||
| 371 | block.push_back(out); | ||
| 372 | } | ||
| 373 | |||
| 374 | (master ? master_list : standard_list).emplace_back(patch_name, block); | ||
| 375 | } | ||
| 376 | } | ||
| 377 | |||
| 378 | return MakeCheatList(master_list, standard_list); | ||
| 379 | } | ||
| 380 | |||
| 381 | std::array<u8, 16> TextCheatParser::ParseSingleLineCheat(const std::string& line) const { | ||
| 382 | std::array<u8, 16> out{}; | ||
| 383 | |||
| 384 | if (line.size() < 8) | ||
| 385 | return out; | ||
| 386 | |||
| 387 | const auto word1 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data(), 8}); | ||
| 388 | std::memcpy(out.data(), word1.data(), sizeof(u32)); | ||
| 389 | |||
| 390 | if (line.size() < 17 || line[8] != ' ') | ||
| 391 | return out; | ||
| 392 | |||
| 393 | const auto word2 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 9, 8}); | ||
| 394 | std::memcpy(out.data() + sizeof(u32), word2.data(), sizeof(u32)); | ||
| 395 | |||
| 396 | if (line.size() < 26 || line[17] != ' ') { | ||
| 397 | // Perform shifting in case value is truncated early. | ||
| 398 | const auto type = static_cast<CodeType>((out[0] & 0xF0) >> 4); | ||
| 399 | if (type == CodeType::Loop || type == CodeType::LoadImmediate || | ||
| 400 | type == CodeType::StoreIndexed || type == CodeType::RegisterArithmetic) { | ||
| 401 | std::memcpy(out.data() + 8, out.data() + 4, sizeof(u32)); | ||
| 402 | std::memset(out.data() + 4, 0, sizeof(u32)); | ||
| 403 | } | ||
| 404 | |||
| 405 | return out; | ||
| 406 | } | ||
| 407 | |||
| 408 | const auto word3 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 18, 8}); | ||
| 409 | std::memcpy(out.data() + 2 * sizeof(u32), word3.data(), sizeof(u32)); | ||
| 410 | |||
| 411 | if (line.size() < 35 || line[26] != ' ') { | ||
| 412 | // Perform shifting in case value is truncated early. | ||
| 413 | const auto type = static_cast<CodeType>((out[0] & 0xF0) >> 4); | ||
| 414 | if (type == CodeType::WriteImmediate || type == CodeType::Conditional) { | ||
| 415 | std::memcpy(out.data() + 12, out.data() + 8, sizeof(u32)); | ||
| 416 | std::memset(out.data() + 8, 0, sizeof(u32)); | ||
| 417 | } | ||
| 418 | |||
| 419 | return out; | ||
| 420 | } | ||
| 421 | |||
| 422 | const auto word4 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 27, 8}); | ||
| 423 | std::memcpy(out.data() + 3 * sizeof(u32), word4.data(), sizeof(u32)); | ||
| 424 | |||
| 425 | return out; | ||
| 426 | } | ||
| 427 | |||
| 428 | u64 MemoryReadImpl(u32 width, VAddr addr) { | ||
| 429 | switch (width) { | ||
| 430 | case 1: | ||
| 431 | return Memory::Read8(addr); | ||
| 432 | case 2: | ||
| 433 | return Memory::Read16(addr); | ||
| 434 | case 4: | ||
| 435 | return Memory::Read32(addr); | ||
| 436 | case 8: | ||
| 437 | return Memory::Read64(addr); | ||
| 438 | default: | ||
| 439 | UNREACHABLE(); | ||
| 440 | return 0; | ||
| 441 | } | ||
| 442 | } | ||
| 443 | |||
| 444 | void MemoryWriteImpl(u32 width, VAddr addr, u64 value) { | ||
| 445 | switch (width) { | ||
| 446 | case 1: | ||
| 447 | Memory::Write8(addr, static_cast<u8>(value)); | ||
| 448 | break; | ||
| 449 | case 2: | ||
| 450 | Memory::Write16(addr, static_cast<u16>(value)); | ||
| 451 | break; | ||
| 452 | case 4: | ||
| 453 | Memory::Write32(addr, static_cast<u32>(value)); | ||
| 454 | break; | ||
| 455 | case 8: | ||
| 456 | Memory::Write64(addr, value); | ||
| 457 | break; | ||
| 458 | default: | ||
| 459 | UNREACHABLE(); | ||
| 460 | } | ||
| 461 | } | ||
| 462 | |||
| 463 | CheatEngine::CheatEngine(std::vector<CheatList> cheats, const std::string& build_id, | ||
| 464 | VAddr code_region_start, VAddr code_region_end) | ||
| 465 | : cheats(std::move(cheats)) { | ||
| 466 | auto& core_timing{Core::System::GetInstance().CoreTiming()}; | ||
| 467 | event = core_timing.RegisterEvent( | ||
| 468 | "CheatEngine::FrameCallback::" + build_id, | ||
| 469 | [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); }); | ||
| 470 | core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event); | ||
| 471 | |||
| 472 | const auto& vm_manager = Core::System::GetInstance().CurrentProcess()->VMManager(); | ||
| 473 | for (auto& list : this->cheats) { | ||
| 474 | list.SetMemoryParameters(code_region_start, vm_manager.GetHeapRegionBaseAddress(), | ||
| 475 | code_region_end, vm_manager.GetHeapRegionEndAddress(), | ||
| 476 | &MemoryWriteImpl, &MemoryReadImpl); | ||
| 477 | } | ||
| 478 | } | ||
| 479 | |||
| 480 | CheatEngine::~CheatEngine() { | ||
| 481 | auto& core_timing{Core::System::GetInstance().CoreTiming()}; | ||
| 482 | core_timing.UnscheduleEvent(event, 0); | ||
| 483 | } | ||
| 484 | |||
| 485 | void CheatEngine::FrameCallback(u64 userdata, int cycles_late) { | ||
| 486 | for (auto& list : cheats) | ||
| 487 | list.Execute(); | ||
| 488 | |||
| 489 | auto& core_timing{Core::System::GetInstance().CoreTiming()}; | ||
| 490 | core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS - cycles_late, event); | ||
| 491 | } | ||
| 492 | |||
| 493 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/cheat_engine.h b/src/core/file_sys/cheat_engine.h new file mode 100644 index 000000000..7ed69a2c8 --- /dev/null +++ b/src/core/file_sys/cheat_engine.h | |||
| @@ -0,0 +1,227 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <map> | ||
| 8 | #include <set> | ||
| 9 | #include <vector> | ||
| 10 | #include <queue> | ||
| 11 | #include "common/bit_field.h" | ||
| 12 | #include "common/common_types.h" | ||
| 13 | |||
| 14 | namespace Core::Timing { | ||
| 15 | struct EventType; | ||
| 16 | } | ||
| 17 | |||
| 18 | namespace FileSys { | ||
| 19 | |||
| 20 | enum class CodeType : u32 { | ||
| 21 | // 0TMR00AA AAAAAAAA YYYYYYYY YYYYYYYY | ||
| 22 | // Writes a T sized value Y to the address A added to the value of register R in memory domain M | ||
| 23 | WriteImmediate = 0, | ||
| 24 | |||
| 25 | // 1TMC00AA AAAAAAAA YYYYYYYY YYYYYYYY | ||
| 26 | // Compares the T sized value Y to the value at address A in memory domain M using the | ||
| 27 | // conditional function C. If success, continues execution. If failure, jumps to the matching | ||
| 28 | // EndConditional statement. | ||
| 29 | Conditional = 1, | ||
| 30 | |||
| 31 | // 20000000 | ||
| 32 | // Terminates a Conditional or ConditionalInput block. | ||
| 33 | EndConditional = 2, | ||
| 34 | |||
| 35 | // 300R0000 VVVVVVVV | ||
| 36 | // Starts looping V times, storing the current count in register R. | ||
| 37 | // Loop block is terminated with a matching 310R0000. | ||
| 38 | Loop = 3, | ||
| 39 | |||
| 40 | // 400R0000 VVVVVVVV VVVVVVVV | ||
| 41 | // Sets the value of register R to the value V. | ||
| 42 | LoadImmediate = 4, | ||
| 43 | |||
| 44 | // 5TMRI0AA AAAAAAAA | ||
| 45 | // Sets the value of register R to the value of width T at address A in memory domain M, with | ||
| 46 | // the current value of R added to the address if I == 1. | ||
| 47 | LoadIndexed = 5, | ||
| 48 | |||
| 49 | // 6T0RIFG0 VVVVVVVV VVVVVVVV | ||
| 50 | // Writes the value V of width T to the memory address stored in register R. Adds the value of | ||
| 51 | // register G to the final calculation if F is nonzero. Increments the value of register R by T | ||
| 52 | // after operation if I is nonzero. | ||
| 53 | StoreIndexed = 6, | ||
| 54 | |||
| 55 | // 7T0RA000 VVVVVVVV | ||
| 56 | // Performs the arithmetic operation A on the value in register R and the value V of width T, | ||
| 57 | // storing the result in register R. | ||
| 58 | RegisterArithmetic = 7, | ||
| 59 | |||
| 60 | // 8KKKKKKK | ||
| 61 | // Checks to see if any of the buttons defined by the bitmask K are pressed. If any are, | ||
| 62 | // execution continues. If none are, execution skips to the next EndConditional command. | ||
| 63 | ConditionalInput = 8, | ||
| 64 | }; | ||
| 65 | |||
| 66 | enum class MemoryType : u32 { | ||
| 67 | // Addressed relative to start of main NSO | ||
| 68 | MainNSO = 0, | ||
| 69 | |||
| 70 | // Addressed relative to start of heap | ||
| 71 | Heap = 1, | ||
| 72 | }; | ||
| 73 | |||
| 74 | enum class ArithmeticOp : u32 { | ||
| 75 | Add = 0, | ||
| 76 | Sub = 1, | ||
| 77 | Mult = 2, | ||
| 78 | LShift = 3, | ||
| 79 | RShift = 4, | ||
| 80 | }; | ||
| 81 | |||
| 82 | enum class ComparisonOp : u32 { | ||
| 83 | GreaterThan = 1, | ||
| 84 | GreaterThanEqual = 2, | ||
| 85 | LessThan = 3, | ||
| 86 | LessThanEqual = 4, | ||
| 87 | Equal = 5, | ||
| 88 | Inequal = 6, | ||
| 89 | }; | ||
| 90 | |||
| 91 | union Cheat { | ||
| 92 | std::array<u8, 16> raw; | ||
| 93 | |||
| 94 | BitField<4, 4, CodeType> type; | ||
| 95 | BitField<0, 4, u32> width; // Can be 1, 2, 4, or 8. Measured in bytes. | ||
| 96 | BitField<0, 4, u32> end_of_loop; | ||
| 97 | BitField<12, 4, MemoryType> memory_type; | ||
| 98 | BitField<8, 4, u32> register_3; | ||
| 99 | BitField<8, 4, ComparisonOp> comparison_op; | ||
| 100 | BitField<20, 4, u32> load_from_register; | ||
| 101 | BitField<20, 4, u32> increment_register; | ||
| 102 | BitField<20, 4, ArithmeticOp> arithmetic_op; | ||
| 103 | BitField<16, 4, u32> add_additional_register; | ||
| 104 | BitField<28, 4, u32> register_6; | ||
| 105 | |||
| 106 | u64 Address() const; | ||
| 107 | u64 ValueWidth(u64 offset) const; | ||
| 108 | u64 Value(u64 offset, u64 width) const; | ||
| 109 | u32 KeypadValue() const; | ||
| 110 | }; | ||
| 111 | |||
| 112 | class CheatParser; | ||
| 113 | |||
| 114 | // Represents a full collection of cheats for a game. The Execute function should be called every | ||
| 115 | // interval that all cheats should be executed. Clients should not directly instantiate this class | ||
| 116 | // (hence private constructor), they should instead receive an instance from CheatParser, which | ||
| 117 | // guarantees the list is always in an acceptable state. | ||
| 118 | class CheatList { | ||
| 119 | public: | ||
| 120 | friend class CheatParser; | ||
| 121 | |||
| 122 | using Block = std::vector<Cheat>; | ||
| 123 | using ProgramSegment = std::vector<std::pair<std::string, Block>>; | ||
| 124 | |||
| 125 | // (width in bytes, address, value) | ||
| 126 | using MemoryWriter = void (*)(u32, VAddr, u64); | ||
| 127 | // (width in bytes, address) -> value | ||
| 128 | using MemoryReader = u64 (*)(u32, VAddr); | ||
| 129 | |||
| 130 | void SetMemoryParameters(VAddr main_begin, VAddr heap_begin, VAddr main_end, VAddr heap_end, | ||
| 131 | MemoryWriter writer, MemoryReader reader); | ||
| 132 | |||
| 133 | void Execute(); | ||
| 134 | |||
| 135 | private: | ||
| 136 | CheatList(ProgramSegment master, ProgramSegment standard); | ||
| 137 | |||
| 138 | void ProcessBlockPairs(const Block& block); | ||
| 139 | void ExecuteSingleCheat(const Cheat& cheat); | ||
| 140 | |||
| 141 | void ExecuteBlock(const Block& block); | ||
| 142 | |||
| 143 | bool EvaluateConditional(const Cheat& cheat) const; | ||
| 144 | |||
| 145 | // Individual cheat operations | ||
| 146 | void WriteImmediate(const Cheat& cheat); | ||
| 147 | void BeginConditional(const Cheat& cheat); | ||
| 148 | void EndConditional(const Cheat& cheat); | ||
| 149 | void Loop(const Cheat& cheat); | ||
| 150 | void LoadImmediate(const Cheat& cheat); | ||
| 151 | void LoadIndexed(const Cheat& cheat); | ||
| 152 | void StoreIndexed(const Cheat& cheat); | ||
| 153 | void RegisterArithmetic(const Cheat& cheat); | ||
| 154 | void BeginConditionalInput(const Cheat& cheat); | ||
| 155 | |||
| 156 | VAddr SanitizeAddress(VAddr in) const; | ||
| 157 | |||
| 158 | // Master Codes are defined as codes that cannot be disabled and are run prior to all | ||
| 159 | // others. | ||
| 160 | ProgramSegment master_list; | ||
| 161 | // All other codes | ||
| 162 | ProgramSegment standard_list; | ||
| 163 | |||
| 164 | bool in_standard = false; | ||
| 165 | |||
| 166 | // 16 (0x0-0xF) scratch registers that can be used by cheats | ||
| 167 | std::array<u64, 16> scratch{}; | ||
| 168 | |||
| 169 | MemoryWriter writer = nullptr; | ||
| 170 | MemoryReader reader = nullptr; | ||
| 171 | |||
| 172 | u64 main_region_begin{}; | ||
| 173 | u64 heap_region_begin{}; | ||
| 174 | u64 main_region_end{}; | ||
| 175 | u64 heap_region_end{}; | ||
| 176 | |||
| 177 | u64 current_block{}; | ||
| 178 | // The current index of the cheat within the current Block | ||
| 179 | u64 current_index{}; | ||
| 180 | |||
| 181 | // The 'stack' of the program. When a conditional or loop statement is encountered, its index is | ||
| 182 | // pushed onto this queue. When a end block is encountered, the condition is checked. | ||
| 183 | std::map<u64, u64> block_pairs; | ||
| 184 | |||
| 185 | std::set<u64> encountered_loops; | ||
| 186 | }; | ||
| 187 | |||
| 188 | // Intermediary class that parses a text file or other disk format for storing cheats into a | ||
| 189 | // CheatList object, that can be used for execution. | ||
| 190 | class CheatParser { | ||
| 191 | public: | ||
| 192 | virtual ~CheatParser(); | ||
| 193 | |||
| 194 | virtual CheatList Parse(const std::vector<u8>& data) const = 0; | ||
| 195 | |||
| 196 | protected: | ||
| 197 | CheatList MakeCheatList(CheatList::ProgramSegment master, | ||
| 198 | CheatList::ProgramSegment standard) const; | ||
| 199 | }; | ||
| 200 | |||
| 201 | // CheatParser implementation that parses text files | ||
| 202 | class TextCheatParser final : public CheatParser { | ||
| 203 | public: | ||
| 204 | ~TextCheatParser() override; | ||
| 205 | |||
| 206 | CheatList Parse(const std::vector<u8>& data) const override; | ||
| 207 | |||
| 208 | private: | ||
| 209 | std::array<u8, 16> ParseSingleLineCheat(const std::string& line) const; | ||
| 210 | }; | ||
| 211 | |||
| 212 | // Class that encapsulates a CheatList and manages its interaction with memory and CoreTiming | ||
| 213 | class CheatEngine final { | ||
| 214 | public: | ||
| 215 | CheatEngine(std::vector<CheatList> cheats, const std::string& build_id, VAddr code_region_start, | ||
| 216 | VAddr code_region_end); | ||
| 217 | ~CheatEngine(); | ||
| 218 | |||
| 219 | private: | ||
| 220 | void FrameCallback(u64 userdata, int cycles_late); | ||
| 221 | |||
| 222 | Core::Timing::EventType* event; | ||
| 223 | |||
| 224 | std::vector<CheatList> cheats; | ||
| 225 | }; | ||
| 226 | |||
| 227 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h index 5d4d05c82..15b9e6624 100644 --- a/src/core/file_sys/content_archive.h +++ b/src/core/file_sys/content_archive.h | |||
| @@ -24,13 +24,26 @@ namespace FileSys { | |||
| 24 | 24 | ||
| 25 | union NCASectionHeader; | 25 | union NCASectionHeader; |
| 26 | 26 | ||
| 27 | /// Describes the type of content within an NCA archive. | ||
| 27 | enum class NCAContentType : u8 { | 28 | enum class NCAContentType : u8 { |
| 29 | /// Executable-related data | ||
| 28 | Program = 0, | 30 | Program = 0, |
| 31 | |||
| 32 | /// Metadata. | ||
| 29 | Meta = 1, | 33 | Meta = 1, |
| 34 | |||
| 35 | /// Access control data. | ||
| 30 | Control = 2, | 36 | Control = 2, |
| 37 | |||
| 38 | /// Information related to the game manual | ||
| 39 | /// e.g. Legal information, etc. | ||
| 31 | Manual = 3, | 40 | Manual = 3, |
| 41 | |||
| 42 | /// System data. | ||
| 32 | Data = 4, | 43 | Data = 4, |
| 33 | Data_Unknown5 = 5, ///< Seems to be used on some system archives | 44 | |
| 45 | /// Data that can be accessed by applications. | ||
| 46 | PublicData = 5, | ||
| 34 | }; | 47 | }; |
| 35 | 48 | ||
| 36 | enum class NCASectionCryptoType : u8 { | 49 | enum class NCASectionCryptoType : u8 { |
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index 61706966e..2b09e5d35 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include <cstddef> | 7 | #include <cstddef> |
| 8 | #include <cstring> | 8 | #include <cstring> |
| 9 | 9 | ||
| 10 | #include "common/file_util.h" | ||
| 10 | #include "common/hex_util.h" | 11 | #include "common/hex_util.h" |
| 11 | #include "common/logging/log.h" | 12 | #include "common/logging/log.h" |
| 12 | #include "core/file_sys/content_archive.h" | 13 | #include "core/file_sys/content_archive.h" |
| @@ -232,6 +233,57 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const { | |||
| 232 | return !CollectPatches(patch_dirs, build_id).empty(); | 233 | return !CollectPatches(patch_dirs, build_id).empty(); |
| 233 | } | 234 | } |
| 234 | 235 | ||
| 236 | static std::optional<CheatList> ReadCheatFileFromFolder(u64 title_id, | ||
| 237 | const std::array<u8, 0x20>& build_id_, | ||
| 238 | const VirtualDir& base_path, bool upper) { | ||
| 239 | const auto build_id_raw = Common::HexArrayToString(build_id_, upper); | ||
| 240 | const auto build_id = build_id_raw.substr(0, sizeof(u64) * 2); | ||
| 241 | const auto file = base_path->GetFile(fmt::format("{}.txt", build_id)); | ||
| 242 | |||
| 243 | if (file == nullptr) { | ||
| 244 | LOG_INFO(Common_Filesystem, "No cheats file found for title_id={:016X}, build_id={}", | ||
| 245 | title_id, build_id); | ||
| 246 | return std::nullopt; | ||
| 247 | } | ||
| 248 | |||
| 249 | std::vector<u8> data(file->GetSize()); | ||
| 250 | if (file->Read(data.data(), data.size()) != data.size()) { | ||
| 251 | LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}, build_id={}", | ||
| 252 | title_id, build_id); | ||
| 253 | return std::nullopt; | ||
| 254 | } | ||
| 255 | |||
| 256 | TextCheatParser parser; | ||
| 257 | return parser.Parse(data); | ||
| 258 | } | ||
| 259 | |||
| 260 | std::vector<CheatList> PatchManager::CreateCheatList(const std::array<u8, 32>& build_id_) const { | ||
| 261 | std::vector<CheatList> out; | ||
| 262 | |||
| 263 | const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); | ||
| 264 | auto patch_dirs = load_dir->GetSubdirectories(); | ||
| 265 | std::sort(patch_dirs.begin(), patch_dirs.end(), | ||
| 266 | [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); | ||
| 267 | |||
| 268 | out.reserve(patch_dirs.size()); | ||
| 269 | for (const auto& subdir : patch_dirs) { | ||
| 270 | auto cheats_dir = subdir->GetSubdirectory("cheats"); | ||
| 271 | if (cheats_dir != nullptr) { | ||
| 272 | auto res = ReadCheatFileFromFolder(title_id, build_id_, cheats_dir, true); | ||
| 273 | if (res.has_value()) { | ||
| 274 | out.push_back(std::move(*res)); | ||
| 275 | continue; | ||
| 276 | } | ||
| 277 | |||
| 278 | res = ReadCheatFileFromFolder(title_id, build_id_, cheats_dir, false); | ||
| 279 | if (res.has_value()) | ||
| 280 | out.push_back(std::move(*res)); | ||
| 281 | } | ||
| 282 | } | ||
| 283 | |||
| 284 | return out; | ||
| 285 | } | ||
| 286 | |||
| 235 | static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) { | 287 | static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) { |
| 236 | const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); | 288 | const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); |
| 237 | if ((type != ContentRecordType::Program && type != ContentRecordType::Data) || | 289 | if ((type != ContentRecordType::Program && type != ContentRecordType::Data) || |
| @@ -403,6 +455,8 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam | |||
| 403 | } | 455 | } |
| 404 | if (IsDirValidAndNonEmpty(mod->GetSubdirectory("romfs"))) | 456 | if (IsDirValidAndNonEmpty(mod->GetSubdirectory("romfs"))) |
| 405 | AppendCommaIfNotEmpty(types, "LayeredFS"); | 457 | AppendCommaIfNotEmpty(types, "LayeredFS"); |
| 458 | if (IsDirValidAndNonEmpty(mod->GetSubdirectory("cheats"))) | ||
| 459 | AppendCommaIfNotEmpty(types, "Cheats"); | ||
| 406 | 460 | ||
| 407 | if (types.empty()) | 461 | if (types.empty()) |
| 408 | continue; | 462 | continue; |
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h index b8a1652fd..3e3ac6aca 100644 --- a/src/core/file_sys/patch_manager.h +++ b/src/core/file_sys/patch_manager.h | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include <memory> | 8 | #include <memory> |
| 9 | #include <string> | 9 | #include <string> |
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "core/file_sys/cheat_engine.h" | ||
| 11 | #include "core/file_sys/nca_metadata.h" | 12 | #include "core/file_sys/nca_metadata.h" |
| 12 | #include "core/file_sys/vfs.h" | 13 | #include "core/file_sys/vfs.h" |
| 13 | 14 | ||
| @@ -45,6 +46,9 @@ public: | |||
| 45 | // Used to prevent expensive copies in NSO loader. | 46 | // Used to prevent expensive copies in NSO loader. |
| 46 | bool HasNSOPatch(const std::array<u8, 0x20>& build_id) const; | 47 | bool HasNSOPatch(const std::array<u8, 0x20>& build_id) const; |
| 47 | 48 | ||
| 49 | // Creates a CheatList object with all | ||
| 50 | std::vector<CheatList> CreateCheatList(const std::array<u8, 0x20>& build_id) const; | ||
| 51 | |||
| 48 | // Currently tracked RomFS patches: | 52 | // Currently tracked RomFS patches: |
| 49 | // - Game Updates | 53 | // - Game Updates |
| 50 | // - LayeredFS | 54 | // - LayeredFS |
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index 128199063..1c6bacace 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp | |||
| @@ -94,7 +94,7 @@ static ContentRecordType GetCRTypeFromNCAType(NCAContentType type) { | |||
| 94 | case NCAContentType::Control: | 94 | case NCAContentType::Control: |
| 95 | return ContentRecordType::Control; | 95 | return ContentRecordType::Control; |
| 96 | case NCAContentType::Data: | 96 | case NCAContentType::Data: |
| 97 | case NCAContentType::Data_Unknown5: | 97 | case NCAContentType::PublicData: |
| 98 | return ContentRecordType::Data; | 98 | return ContentRecordType::Data; |
| 99 | case NCAContentType::Manual: | 99 | case NCAContentType::Manual: |
| 100 | // TODO(DarkLordZach): Peek at NCA contents to differentiate Manual and Legal. | 100 | // TODO(DarkLordZach): Peek at NCA contents to differentiate Manual and Legal. |
diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h index 455d1f346..fae54bcc7 100644 --- a/src/core/hle/ipc.h +++ b/src/core/hle/ipc.h | |||
| @@ -39,10 +39,10 @@ struct CommandHeader { | |||
| 39 | union { | 39 | union { |
| 40 | u32_le raw_low; | 40 | u32_le raw_low; |
| 41 | BitField<0, 16, CommandType> type; | 41 | BitField<0, 16, CommandType> type; |
| 42 | BitField<16, 4, u32_le> num_buf_x_descriptors; | 42 | BitField<16, 4, u32> num_buf_x_descriptors; |
| 43 | BitField<20, 4, u32_le> num_buf_a_descriptors; | 43 | BitField<20, 4, u32> num_buf_a_descriptors; |
| 44 | BitField<24, 4, u32_le> num_buf_b_descriptors; | 44 | BitField<24, 4, u32> num_buf_b_descriptors; |
| 45 | BitField<28, 4, u32_le> num_buf_w_descriptors; | 45 | BitField<28, 4, u32> num_buf_w_descriptors; |
| 46 | }; | 46 | }; |
| 47 | 47 | ||
| 48 | enum class BufferDescriptorCFlag : u32 { | 48 | enum class BufferDescriptorCFlag : u32 { |
| @@ -53,28 +53,28 @@ struct CommandHeader { | |||
| 53 | 53 | ||
| 54 | union { | 54 | union { |
| 55 | u32_le raw_high; | 55 | u32_le raw_high; |
| 56 | BitField<0, 10, u32_le> data_size; | 56 | BitField<0, 10, u32> data_size; |
| 57 | BitField<10, 4, BufferDescriptorCFlag> buf_c_descriptor_flags; | 57 | BitField<10, 4, BufferDescriptorCFlag> buf_c_descriptor_flags; |
| 58 | BitField<31, 1, u32_le> enable_handle_descriptor; | 58 | BitField<31, 1, u32> enable_handle_descriptor; |
| 59 | }; | 59 | }; |
| 60 | }; | 60 | }; |
| 61 | static_assert(sizeof(CommandHeader) == 8, "CommandHeader size is incorrect"); | 61 | static_assert(sizeof(CommandHeader) == 8, "CommandHeader size is incorrect"); |
| 62 | 62 | ||
| 63 | union HandleDescriptorHeader { | 63 | union HandleDescriptorHeader { |
| 64 | u32_le raw_high; | 64 | u32_le raw_high; |
| 65 | BitField<0, 1, u32_le> send_current_pid; | 65 | BitField<0, 1, u32> send_current_pid; |
| 66 | BitField<1, 4, u32_le> num_handles_to_copy; | 66 | BitField<1, 4, u32> num_handles_to_copy; |
| 67 | BitField<5, 4, u32_le> num_handles_to_move; | 67 | BitField<5, 4, u32> num_handles_to_move; |
| 68 | }; | 68 | }; |
| 69 | static_assert(sizeof(HandleDescriptorHeader) == 4, "HandleDescriptorHeader size is incorrect"); | 69 | static_assert(sizeof(HandleDescriptorHeader) == 4, "HandleDescriptorHeader size is incorrect"); |
| 70 | 70 | ||
| 71 | struct BufferDescriptorX { | 71 | struct BufferDescriptorX { |
| 72 | union { | 72 | union { |
| 73 | BitField<0, 6, u32_le> counter_bits_0_5; | 73 | BitField<0, 6, u32> counter_bits_0_5; |
| 74 | BitField<6, 3, u32_le> address_bits_36_38; | 74 | BitField<6, 3, u32> address_bits_36_38; |
| 75 | BitField<9, 3, u32_le> counter_bits_9_11; | 75 | BitField<9, 3, u32> counter_bits_9_11; |
| 76 | BitField<12, 4, u32_le> address_bits_32_35; | 76 | BitField<12, 4, u32> address_bits_32_35; |
| 77 | BitField<16, 16, u32_le> size; | 77 | BitField<16, 16, u32> size; |
| 78 | }; | 78 | }; |
| 79 | 79 | ||
| 80 | u32_le address_bits_0_31; | 80 | u32_le address_bits_0_31; |
| @@ -103,10 +103,10 @@ struct BufferDescriptorABW { | |||
| 103 | u32_le address_bits_0_31; | 103 | u32_le address_bits_0_31; |
| 104 | 104 | ||
| 105 | union { | 105 | union { |
| 106 | BitField<0, 2, u32_le> flags; | 106 | BitField<0, 2, u32> flags; |
| 107 | BitField<2, 3, u32_le> address_bits_36_38; | 107 | BitField<2, 3, u32> address_bits_36_38; |
| 108 | BitField<24, 4, u32_le> size_bits_32_35; | 108 | BitField<24, 4, u32> size_bits_32_35; |
| 109 | BitField<28, 4, u32_le> address_bits_32_35; | 109 | BitField<28, 4, u32> address_bits_32_35; |
| 110 | }; | 110 | }; |
| 111 | 111 | ||
| 112 | VAddr Address() const { | 112 | VAddr Address() const { |
| @@ -128,8 +128,8 @@ struct BufferDescriptorC { | |||
| 128 | u32_le address_bits_0_31; | 128 | u32_le address_bits_0_31; |
| 129 | 129 | ||
| 130 | union { | 130 | union { |
| 131 | BitField<0, 16, u32_le> address_bits_32_47; | 131 | BitField<0, 16, u32> address_bits_32_47; |
| 132 | BitField<16, 16, u32_le> size; | 132 | BitField<16, 16, u32> size; |
| 133 | }; | 133 | }; |
| 134 | 134 | ||
| 135 | VAddr Address() const { | 135 | VAddr Address() const { |
| @@ -167,8 +167,8 @@ struct DomainMessageHeader { | |||
| 167 | struct { | 167 | struct { |
| 168 | union { | 168 | union { |
| 169 | BitField<0, 8, CommandType> command; | 169 | BitField<0, 8, CommandType> command; |
| 170 | BitField<8, 8, u32_le> input_object_count; | 170 | BitField<8, 8, u32> input_object_count; |
| 171 | BitField<16, 16, u32_le> size; | 171 | BitField<16, 16, u32> size; |
| 172 | }; | 172 | }; |
| 173 | u32_le object_id; | 173 | u32_le object_id; |
| 174 | INSERT_PADDING_WORDS(2); | 174 | INSERT_PADDING_WORDS(2); |
diff --git a/src/core/hle/kernel/code_set.cpp b/src/core/hle/kernel/code_set.cpp new file mode 100644 index 000000000..1f434e9af --- /dev/null +++ b/src/core/hle/kernel/code_set.cpp | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | // Copyright 2019 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "core/hle/kernel/code_set.h" | ||
| 6 | |||
| 7 | namespace Kernel { | ||
| 8 | |||
| 9 | CodeSet::CodeSet() = default; | ||
| 10 | CodeSet::~CodeSet() = default; | ||
| 11 | |||
| 12 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/code_set.h b/src/core/hle/kernel/code_set.h new file mode 100644 index 000000000..834fd23d2 --- /dev/null +++ b/src/core/hle/kernel/code_set.h | |||
| @@ -0,0 +1,90 @@ | |||
| 1 | // Copyright 2019 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <cstddef> | ||
| 8 | #include <memory> | ||
| 9 | #include <vector> | ||
| 10 | |||
| 11 | #include "common/common_types.h" | ||
| 12 | |||
| 13 | namespace Kernel { | ||
| 14 | |||
| 15 | /** | ||
| 16 | * Represents executable data that may be loaded into a kernel process. | ||
| 17 | * | ||
| 18 | * A code set consists of three basic segments: | ||
| 19 | * - A code (AKA text) segment, | ||
| 20 | * - A read-only data segment (rodata) | ||
| 21 | * - A data segment | ||
| 22 | * | ||
| 23 | * The code segment is the portion of the object file that contains | ||
| 24 | * executable instructions. | ||
| 25 | * | ||
| 26 | * The read-only data segment in the portion of the object file that | ||
| 27 | * contains (as one would expect) read-only data, such as fixed constant | ||
| 28 | * values and data structures. | ||
| 29 | * | ||
| 30 | * The data segment is similar to the read-only data segment -- it contains | ||
| 31 | * variables and data structures that have predefined values, however, | ||
| 32 | * entities within this segment can be modified. | ||
| 33 | */ | ||
| 34 | struct CodeSet final { | ||
| 35 | /// A single segment within a code set. | ||
| 36 | struct Segment final { | ||
| 37 | /// The byte offset that this segment is located at. | ||
| 38 | std::size_t offset = 0; | ||
| 39 | |||
| 40 | /// The address to map this segment to. | ||
| 41 | VAddr addr = 0; | ||
| 42 | |||
| 43 | /// The size of this segment in bytes. | ||
| 44 | u32 size = 0; | ||
| 45 | }; | ||
| 46 | |||
| 47 | explicit CodeSet(); | ||
| 48 | ~CodeSet(); | ||
| 49 | |||
| 50 | CodeSet(const CodeSet&) = delete; | ||
| 51 | CodeSet& operator=(const CodeSet&) = delete; | ||
| 52 | |||
| 53 | CodeSet(CodeSet&&) = default; | ||
| 54 | CodeSet& operator=(CodeSet&&) = default; | ||
| 55 | |||
| 56 | Segment& CodeSegment() { | ||
| 57 | return segments[0]; | ||
| 58 | } | ||
| 59 | |||
| 60 | const Segment& CodeSegment() const { | ||
| 61 | return segments[0]; | ||
| 62 | } | ||
| 63 | |||
| 64 | Segment& RODataSegment() { | ||
| 65 | return segments[1]; | ||
| 66 | } | ||
| 67 | |||
| 68 | const Segment& RODataSegment() const { | ||
| 69 | return segments[1]; | ||
| 70 | } | ||
| 71 | |||
| 72 | Segment& DataSegment() { | ||
| 73 | return segments[2]; | ||
| 74 | } | ||
| 75 | |||
| 76 | const Segment& DataSegment() const { | ||
| 77 | return segments[2]; | ||
| 78 | } | ||
| 79 | |||
| 80 | /// The overall data that backs this code set. | ||
| 81 | std::shared_ptr<std::vector<u8>> memory; | ||
| 82 | |||
| 83 | /// The segments that comprise this code set. | ||
| 84 | std::array<Segment, 3> segments; | ||
| 85 | |||
| 86 | /// The entry point address for this code set. | ||
| 87 | VAddr entrypoint = 0; | ||
| 88 | }; | ||
| 89 | |||
| 90 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 0743670ad..98e87313b 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp | |||
| @@ -2,7 +2,6 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <map> | ||
| 6 | #include <utility> | 5 | #include <utility> |
| 7 | #include <vector> | 6 | #include <vector> |
| 8 | 7 | ||
| @@ -10,8 +9,11 @@ | |||
| 10 | #include "core/core.h" | 9 | #include "core/core.h" |
| 11 | #include "core/hle/kernel/errors.h" | 10 | #include "core/hle/kernel/errors.h" |
| 12 | #include "core/hle/kernel/handle_table.h" | 11 | #include "core/hle/kernel/handle_table.h" |
| 12 | #include "core/hle/kernel/kernel.h" | ||
| 13 | #include "core/hle/kernel/mutex.h" | 13 | #include "core/hle/kernel/mutex.h" |
| 14 | #include "core/hle/kernel/object.h" | 14 | #include "core/hle/kernel/object.h" |
| 15 | #include "core/hle/kernel/process.h" | ||
| 16 | #include "core/hle/kernel/scheduler.h" | ||
| 15 | #include "core/hle/kernel/thread.h" | 17 | #include "core/hle/kernel/thread.h" |
| 16 | #include "core/hle/result.h" | 18 | #include "core/hle/result.h" |
| 17 | #include "core/memory.h" | 19 | #include "core/memory.h" |
| @@ -57,41 +59,47 @@ static void TransferMutexOwnership(VAddr mutex_addr, SharedPtr<Thread> current_t | |||
| 57 | } | 59 | } |
| 58 | } | 60 | } |
| 59 | 61 | ||
| 60 | ResultCode Mutex::TryAcquire(HandleTable& handle_table, VAddr address, Handle holding_thread_handle, | 62 | Mutex::Mutex(Core::System& system) : system{system} {} |
| 63 | Mutex::~Mutex() = default; | ||
| 64 | |||
| 65 | ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle, | ||
| 61 | Handle requesting_thread_handle) { | 66 | Handle requesting_thread_handle) { |
| 62 | // The mutex address must be 4-byte aligned | 67 | // The mutex address must be 4-byte aligned |
| 63 | if ((address % sizeof(u32)) != 0) { | 68 | if ((address % sizeof(u32)) != 0) { |
| 64 | return ERR_INVALID_ADDRESS; | 69 | return ERR_INVALID_ADDRESS; |
| 65 | } | 70 | } |
| 66 | 71 | ||
| 72 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); | ||
| 73 | Thread* const current_thread = system.CurrentScheduler().GetCurrentThread(); | ||
| 67 | SharedPtr<Thread> holding_thread = handle_table.Get<Thread>(holding_thread_handle); | 74 | SharedPtr<Thread> holding_thread = handle_table.Get<Thread>(holding_thread_handle); |
| 68 | SharedPtr<Thread> requesting_thread = handle_table.Get<Thread>(requesting_thread_handle); | 75 | SharedPtr<Thread> requesting_thread = handle_table.Get<Thread>(requesting_thread_handle); |
| 69 | 76 | ||
| 70 | // TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of another | 77 | // TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of another |
| 71 | // thread. | 78 | // thread. |
| 72 | ASSERT(requesting_thread == GetCurrentThread()); | 79 | ASSERT(requesting_thread == current_thread); |
| 73 | 80 | ||
| 74 | u32 addr_value = Memory::Read32(address); | 81 | const u32 addr_value = Memory::Read32(address); |
| 75 | 82 | ||
| 76 | // If the mutex isn't being held, just return success. | 83 | // If the mutex isn't being held, just return success. |
| 77 | if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) { | 84 | if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) { |
| 78 | return RESULT_SUCCESS; | 85 | return RESULT_SUCCESS; |
| 79 | } | 86 | } |
| 80 | 87 | ||
| 81 | if (holding_thread == nullptr) | 88 | if (holding_thread == nullptr) { |
| 82 | return ERR_INVALID_HANDLE; | 89 | return ERR_INVALID_HANDLE; |
| 90 | } | ||
| 83 | 91 | ||
| 84 | // Wait until the mutex is released | 92 | // Wait until the mutex is released |
| 85 | GetCurrentThread()->SetMutexWaitAddress(address); | 93 | current_thread->SetMutexWaitAddress(address); |
| 86 | GetCurrentThread()->SetWaitHandle(requesting_thread_handle); | 94 | current_thread->SetWaitHandle(requesting_thread_handle); |
| 87 | 95 | ||
| 88 | GetCurrentThread()->SetStatus(ThreadStatus::WaitMutex); | 96 | current_thread->SetStatus(ThreadStatus::WaitMutex); |
| 89 | GetCurrentThread()->InvalidateWakeupCallback(); | 97 | current_thread->InvalidateWakeupCallback(); |
| 90 | 98 | ||
| 91 | // Update the lock holder thread's priority to prevent priority inversion. | 99 | // Update the lock holder thread's priority to prevent priority inversion. |
| 92 | holding_thread->AddMutexWaiter(GetCurrentThread()); | 100 | holding_thread->AddMutexWaiter(current_thread); |
| 93 | 101 | ||
| 94 | Core::System::GetInstance().PrepareReschedule(); | 102 | system.PrepareReschedule(); |
| 95 | 103 | ||
| 96 | return RESULT_SUCCESS; | 104 | return RESULT_SUCCESS; |
| 97 | } | 105 | } |
| @@ -102,7 +110,8 @@ ResultCode Mutex::Release(VAddr address) { | |||
| 102 | return ERR_INVALID_ADDRESS; | 110 | return ERR_INVALID_ADDRESS; |
| 103 | } | 111 | } |
| 104 | 112 | ||
| 105 | auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(GetCurrentThread(), address); | 113 | auto* const current_thread = system.CurrentScheduler().GetCurrentThread(); |
| 114 | auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(current_thread, address); | ||
| 106 | 115 | ||
| 107 | // There are no more threads waiting for the mutex, release it completely. | 116 | // There are no more threads waiting for the mutex, release it completely. |
| 108 | if (thread == nullptr) { | 117 | if (thread == nullptr) { |
| @@ -111,7 +120,7 @@ ResultCode Mutex::Release(VAddr address) { | |||
| 111 | } | 120 | } |
| 112 | 121 | ||
| 113 | // Transfer the ownership of the mutex from the previous owner to the new one. | 122 | // Transfer the ownership of the mutex from the previous owner to the new one. |
| 114 | TransferMutexOwnership(address, GetCurrentThread(), thread); | 123 | TransferMutexOwnership(address, current_thread, thread); |
| 115 | 124 | ||
| 116 | u32 mutex_value = thread->GetWaitHandle(); | 125 | u32 mutex_value = thread->GetWaitHandle(); |
| 117 | 126 | ||
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h index 81e62d497..b904de2e8 100644 --- a/src/core/hle/kernel/mutex.h +++ b/src/core/hle/kernel/mutex.h | |||
| @@ -5,32 +5,34 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "core/hle/kernel/object.h" | ||
| 9 | 8 | ||
| 10 | union ResultCode; | 9 | union ResultCode; |
| 11 | 10 | ||
| 12 | namespace Kernel { | 11 | namespace Core { |
| 12 | class System; | ||
| 13 | } | ||
| 13 | 14 | ||
| 14 | class HandleTable; | 15 | namespace Kernel { |
| 15 | class Thread; | ||
| 16 | 16 | ||
| 17 | class Mutex final { | 17 | class Mutex final { |
| 18 | public: | 18 | public: |
| 19 | explicit Mutex(Core::System& system); | ||
| 20 | ~Mutex(); | ||
| 21 | |||
| 19 | /// Flag that indicates that a mutex still has threads waiting for it. | 22 | /// Flag that indicates that a mutex still has threads waiting for it. |
| 20 | static constexpr u32 MutexHasWaitersFlag = 0x40000000; | 23 | static constexpr u32 MutexHasWaitersFlag = 0x40000000; |
| 21 | /// Mask of the bits in a mutex address value that contain the mutex owner. | 24 | /// Mask of the bits in a mutex address value that contain the mutex owner. |
| 22 | static constexpr u32 MutexOwnerMask = 0xBFFFFFFF; | 25 | static constexpr u32 MutexOwnerMask = 0xBFFFFFFF; |
| 23 | 26 | ||
| 24 | /// Attempts to acquire a mutex at the specified address. | 27 | /// Attempts to acquire a mutex at the specified address. |
| 25 | static ResultCode TryAcquire(HandleTable& handle_table, VAddr address, | 28 | ResultCode TryAcquire(VAddr address, Handle holding_thread_handle, |
| 26 | Handle holding_thread_handle, Handle requesting_thread_handle); | 29 | Handle requesting_thread_handle); |
| 27 | 30 | ||
| 28 | /// Releases the mutex at the specified address. | 31 | /// Releases the mutex at the specified address. |
| 29 | static ResultCode Release(VAddr address); | 32 | ResultCode Release(VAddr address); |
| 30 | 33 | ||
| 31 | private: | 34 | private: |
| 32 | Mutex() = default; | 35 | Core::System& system; |
| 33 | ~Mutex() = default; | ||
| 34 | }; | 36 | }; |
| 35 | 37 | ||
| 36 | } // namespace Kernel | 38 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 65c51003d..0d782e4ba 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 10 | #include "core/core.h" | 10 | #include "core/core.h" |
| 11 | #include "core/file_sys/program_metadata.h" | 11 | #include "core/file_sys/program_metadata.h" |
| 12 | #include "core/hle/kernel/code_set.h" | ||
| 12 | #include "core/hle/kernel/errors.h" | 13 | #include "core/hle/kernel/errors.h" |
| 13 | #include "core/hle/kernel/kernel.h" | 14 | #include "core/hle/kernel/kernel.h" |
| 14 | #include "core/hle/kernel/process.h" | 15 | #include "core/hle/kernel/process.h" |
| @@ -50,9 +51,6 @@ void SetupMainThread(Process& owner_process, KernelCore& kernel, VAddr entry_poi | |||
| 50 | } | 51 | } |
| 51 | } // Anonymous namespace | 52 | } // Anonymous namespace |
| 52 | 53 | ||
| 53 | CodeSet::CodeSet() = default; | ||
| 54 | CodeSet::~CodeSet() = default; | ||
| 55 | |||
| 56 | SharedPtr<Process> Process::Create(Core::System& system, std::string&& name) { | 54 | SharedPtr<Process> Process::Create(Core::System& system, std::string&& name) { |
| 57 | auto& kernel = system.Kernel(); | 55 | auto& kernel = system.Kernel(); |
| 58 | 56 | ||
| @@ -212,7 +210,7 @@ void Process::FreeTLSSlot(VAddr tls_address) { | |||
| 212 | } | 210 | } |
| 213 | 211 | ||
| 214 | void Process::LoadModule(CodeSet module_, VAddr base_addr) { | 212 | void Process::LoadModule(CodeSet module_, VAddr base_addr) { |
| 215 | const auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions, | 213 | const auto MapSegment = [&](const CodeSet::Segment& segment, VMAPermission permissions, |
| 216 | MemoryState memory_state) { | 214 | MemoryState memory_state) { |
| 217 | const auto vma = vm_manager | 215 | const auto vma = vm_manager |
| 218 | .MapMemoryBlock(segment.addr + base_addr, module_.memory, | 216 | .MapMemoryBlock(segment.addr + base_addr, module_.memory, |
| @@ -222,16 +220,17 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) { | |||
| 222 | }; | 220 | }; |
| 223 | 221 | ||
| 224 | // Map CodeSet segments | 222 | // Map CodeSet segments |
| 225 | MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic); | 223 | MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::Code); |
| 226 | MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable); | 224 | MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeData); |
| 227 | MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable); | 225 | MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeData); |
| 228 | 226 | ||
| 229 | // Clear instruction cache in CPU JIT | 227 | // Clear instruction cache in CPU JIT |
| 230 | system.InvalidateCpuInstructionCaches(); | 228 | system.InvalidateCpuInstructionCaches(); |
| 231 | } | 229 | } |
| 232 | 230 | ||
| 233 | Process::Process(Core::System& system) | 231 | Process::Process(Core::System& system) |
| 234 | : WaitObject{system.Kernel()}, address_arbiter{system}, system{system} {} | 232 | : WaitObject{system.Kernel()}, address_arbiter{system}, mutex{system}, system{system} {} |
| 233 | |||
| 235 | Process::~Process() = default; | 234 | Process::~Process() = default; |
| 236 | 235 | ||
| 237 | void Process::Acquire(Thread* thread) { | 236 | void Process::Acquire(Thread* thread) { |
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index 47ffd4ad3..1bd7bf5c1 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h | |||
| @@ -7,13 +7,13 @@ | |||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <bitset> | 8 | #include <bitset> |
| 9 | #include <cstddef> | 9 | #include <cstddef> |
| 10 | #include <memory> | ||
| 11 | #include <string> | 10 | #include <string> |
| 12 | #include <vector> | 11 | #include <vector> |
| 13 | #include <boost/container/static_vector.hpp> | 12 | #include <boost/container/static_vector.hpp> |
| 14 | #include "common/common_types.h" | 13 | #include "common/common_types.h" |
| 15 | #include "core/hle/kernel/address_arbiter.h" | 14 | #include "core/hle/kernel/address_arbiter.h" |
| 16 | #include "core/hle/kernel/handle_table.h" | 15 | #include "core/hle/kernel/handle_table.h" |
| 16 | #include "core/hle/kernel/mutex.h" | ||
| 17 | #include "core/hle/kernel/process_capability.h" | 17 | #include "core/hle/kernel/process_capability.h" |
| 18 | #include "core/hle/kernel/vm_manager.h" | 18 | #include "core/hle/kernel/vm_manager.h" |
| 19 | #include "core/hle/kernel/wait_object.h" | 19 | #include "core/hle/kernel/wait_object.h" |
| @@ -33,6 +33,8 @@ class KernelCore; | |||
| 33 | class ResourceLimit; | 33 | class ResourceLimit; |
| 34 | class Thread; | 34 | class Thread; |
| 35 | 35 | ||
| 36 | struct CodeSet; | ||
| 37 | |||
| 36 | struct AddressMapping { | 38 | struct AddressMapping { |
| 37 | // Address and size must be page-aligned | 39 | // Address and size must be page-aligned |
| 38 | VAddr address; | 40 | VAddr address; |
| @@ -65,46 +67,6 @@ enum class ProcessStatus { | |||
| 65 | DebugBreak, | 67 | DebugBreak, |
| 66 | }; | 68 | }; |
| 67 | 69 | ||
| 68 | struct CodeSet final { | ||
| 69 | struct Segment { | ||
| 70 | std::size_t offset = 0; | ||
| 71 | VAddr addr = 0; | ||
| 72 | u32 size = 0; | ||
| 73 | }; | ||
| 74 | |||
| 75 | explicit CodeSet(); | ||
| 76 | ~CodeSet(); | ||
| 77 | |||
| 78 | Segment& CodeSegment() { | ||
| 79 | return segments[0]; | ||
| 80 | } | ||
| 81 | |||
| 82 | const Segment& CodeSegment() const { | ||
| 83 | return segments[0]; | ||
| 84 | } | ||
| 85 | |||
| 86 | Segment& RODataSegment() { | ||
| 87 | return segments[1]; | ||
| 88 | } | ||
| 89 | |||
| 90 | const Segment& RODataSegment() const { | ||
| 91 | return segments[1]; | ||
| 92 | } | ||
| 93 | |||
| 94 | Segment& DataSegment() { | ||
| 95 | return segments[2]; | ||
| 96 | } | ||
| 97 | |||
| 98 | const Segment& DataSegment() const { | ||
| 99 | return segments[2]; | ||
| 100 | } | ||
| 101 | |||
| 102 | std::shared_ptr<std::vector<u8>> memory; | ||
| 103 | |||
| 104 | std::array<Segment, 3> segments; | ||
| 105 | VAddr entrypoint = 0; | ||
| 106 | }; | ||
| 107 | |||
| 108 | class Process final : public WaitObject { | 70 | class Process final : public WaitObject { |
| 109 | public: | 71 | public: |
| 110 | enum : u64 { | 72 | enum : u64 { |
| @@ -165,6 +127,16 @@ public: | |||
| 165 | return address_arbiter; | 127 | return address_arbiter; |
| 166 | } | 128 | } |
| 167 | 129 | ||
| 130 | /// Gets a reference to the process' mutex lock. | ||
| 131 | Mutex& GetMutex() { | ||
| 132 | return mutex; | ||
| 133 | } | ||
| 134 | |||
| 135 | /// Gets a const reference to the process' mutex lock | ||
| 136 | const Mutex& GetMutex() const { | ||
| 137 | return mutex; | ||
| 138 | } | ||
| 139 | |||
| 168 | /// Gets the current status of the process | 140 | /// Gets the current status of the process |
| 169 | ProcessStatus GetStatus() const { | 141 | ProcessStatus GetStatus() const { |
| 170 | return status; | 142 | return status; |
| @@ -327,6 +299,11 @@ private: | |||
| 327 | /// Per-process address arbiter. | 299 | /// Per-process address arbiter. |
| 328 | AddressArbiter address_arbiter; | 300 | AddressArbiter address_arbiter; |
| 329 | 301 | ||
| 302 | /// The per-process mutex lock instance used for handling various | ||
| 303 | /// forms of services, such as lock arbitration, and condition | ||
| 304 | /// variable related facilities. | ||
| 305 | Mutex mutex; | ||
| 306 | |||
| 330 | /// Random values for svcGetInfo RandomEntropy | 307 | /// Random values for svcGetInfo RandomEntropy |
| 331 | std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy; | 308 | std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy; |
| 332 | 309 | ||
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 047fa0c19..a6a17efe7 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -551,9 +551,9 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr, | |||
| 551 | return ERR_INVALID_ADDRESS; | 551 | return ERR_INVALID_ADDRESS; |
| 552 | } | 552 | } |
| 553 | 553 | ||
| 554 | auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | 554 | auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess(); |
| 555 | return Mutex::TryAcquire(handle_table, mutex_addr, holding_thread_handle, | 555 | return current_process->GetMutex().TryAcquire(mutex_addr, holding_thread_handle, |
| 556 | requesting_thread_handle); | 556 | requesting_thread_handle); |
| 557 | } | 557 | } |
| 558 | 558 | ||
| 559 | /// Unlock a mutex | 559 | /// Unlock a mutex |
| @@ -571,7 +571,8 @@ static ResultCode ArbitrateUnlock(VAddr mutex_addr) { | |||
| 571 | return ERR_INVALID_ADDRESS; | 571 | return ERR_INVALID_ADDRESS; |
| 572 | } | 572 | } |
| 573 | 573 | ||
| 574 | return Mutex::Release(mutex_addr); | 574 | auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess(); |
| 575 | return current_process->GetMutex().Release(mutex_addr); | ||
| 575 | } | 576 | } |
| 576 | 577 | ||
| 577 | enum class BreakType : u32 { | 578 | enum class BreakType : u32 { |
| @@ -1340,11 +1341,15 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var | |||
| 1340 | "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}", | 1341 | "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}", |
| 1341 | mutex_addr, condition_variable_addr, thread_handle, nano_seconds); | 1342 | mutex_addr, condition_variable_addr, thread_handle, nano_seconds); |
| 1342 | 1343 | ||
| 1343 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | 1344 | auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess(); |
| 1345 | const auto& handle_table = current_process->GetHandleTable(); | ||
| 1344 | SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); | 1346 | SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); |
| 1345 | ASSERT(thread); | 1347 | ASSERT(thread); |
| 1346 | 1348 | ||
| 1347 | CASCADE_CODE(Mutex::Release(mutex_addr)); | 1349 | const auto release_result = current_process->GetMutex().Release(mutex_addr); |
| 1350 | if (release_result.IsError()) { | ||
| 1351 | return release_result; | ||
| 1352 | } | ||
| 1348 | 1353 | ||
| 1349 | SharedPtr<Thread> current_thread = GetCurrentThread(); | 1354 | SharedPtr<Thread> current_thread = GetCurrentThread(); |
| 1350 | current_thread->SetCondVarWaitAddress(condition_variable_addr); | 1355 | current_thread->SetCondVarWaitAddress(condition_variable_addr); |
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index 3def3e52c..22bf55ce7 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp | |||
| @@ -20,16 +20,16 @@ namespace Kernel { | |||
| 20 | namespace { | 20 | namespace { |
| 21 | const char* GetMemoryStateName(MemoryState state) { | 21 | const char* GetMemoryStateName(MemoryState state) { |
| 22 | static constexpr const char* names[] = { | 22 | static constexpr const char* names[] = { |
| 23 | "Unmapped", "Io", | 23 | "Unmapped", "Io", |
| 24 | "Normal", "CodeStatic", | 24 | "Normal", "Code", |
| 25 | "CodeMutable", "Heap", | 25 | "CodeData", "Heap", |
| 26 | "Shared", "Unknown1", | 26 | "Shared", "Unknown1", |
| 27 | "ModuleCodeStatic", "ModuleCodeMutable", | 27 | "ModuleCode", "ModuleCodeData", |
| 28 | "IpcBuffer0", "Stack", | 28 | "IpcBuffer0", "Stack", |
| 29 | "ThreadLocal", "TransferMemoryIsolated", | 29 | "ThreadLocal", "TransferMemoryIsolated", |
| 30 | "TransferMemory", "ProcessMemory", | 30 | "TransferMemory", "ProcessMemory", |
| 31 | "Inaccessible", "IpcBuffer1", | 31 | "Inaccessible", "IpcBuffer1", |
| 32 | "IpcBuffer3", "KernelStack", | 32 | "IpcBuffer3", "KernelStack", |
| 33 | }; | 33 | }; |
| 34 | 34 | ||
| 35 | return names[ToSvcMemoryState(state)]; | 35 | return names[ToSvcMemoryState(state)]; |
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index b96980f8f..7cdff6094 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h | |||
| @@ -165,12 +165,12 @@ enum class MemoryState : u32 { | |||
| 165 | Unmapped = 0x00, | 165 | Unmapped = 0x00, |
| 166 | Io = 0x01 | FlagMapped, | 166 | Io = 0x01 | FlagMapped, |
| 167 | Normal = 0x02 | FlagMapped | FlagQueryPhysicalAddressAllowed, | 167 | Normal = 0x02 | FlagMapped | FlagQueryPhysicalAddressAllowed, |
| 168 | CodeStatic = 0x03 | CodeFlags | FlagMapProcess, | 168 | Code = 0x03 | CodeFlags | FlagMapProcess, |
| 169 | CodeMutable = 0x04 | CodeFlags | FlagMapProcess | FlagCodeMemory, | 169 | CodeData = 0x04 | DataFlags | FlagMapProcess | FlagCodeMemory, |
| 170 | Heap = 0x05 | DataFlags | FlagCodeMemory, | 170 | Heap = 0x05 | DataFlags | FlagCodeMemory, |
| 171 | Shared = 0x06 | FlagMapped | FlagMemoryPoolAllocated, | 171 | Shared = 0x06 | FlagMapped | FlagMemoryPoolAllocated, |
| 172 | ModuleCodeStatic = 0x08 | CodeFlags | FlagModule | FlagMapProcess, | 172 | ModuleCode = 0x08 | CodeFlags | FlagModule | FlagMapProcess, |
| 173 | ModuleCodeMutable = 0x09 | DataFlags | FlagModule | FlagMapProcess | FlagCodeMemory, | 173 | ModuleCodeData = 0x09 | DataFlags | FlagModule | FlagMapProcess | FlagCodeMemory, |
| 174 | 174 | ||
| 175 | IpcBuffer0 = 0x0A | FlagMapped | FlagQueryPhysicalAddressAllowed | FlagMemoryPoolAllocated | | 175 | IpcBuffer0 = 0x0A | FlagMapped | FlagQueryPhysicalAddressAllowed | FlagMemoryPoolAllocated | |
| 176 | IPCFlags | FlagSharedDevice | FlagSharedDeviceAligned, | 176 | IPCFlags | FlagSharedDevice | FlagSharedDeviceAligned, |
| @@ -617,6 +617,9 @@ private: | |||
| 617 | VAddr new_map_region_base = 0; | 617 | VAddr new_map_region_base = 0; |
| 618 | VAddr new_map_region_end = 0; | 618 | VAddr new_map_region_end = 0; |
| 619 | 619 | ||
| 620 | VAddr main_code_region_base = 0; | ||
| 621 | VAddr main_code_region_end = 0; | ||
| 622 | |||
| 620 | VAddr tls_io_region_base = 0; | 623 | VAddr tls_io_region_base = 0; |
| 621 | VAddr tls_io_region_end = 0; | 624 | VAddr tls_io_region_end = 0; |
| 622 | 625 | ||
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index c750d70ac..9c44e27c6 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -215,7 +215,21 @@ IDisplayController::IDisplayController() : ServiceFramework("IDisplayController" | |||
| 215 | 215 | ||
| 216 | IDisplayController::~IDisplayController() = default; | 216 | IDisplayController::~IDisplayController() = default; |
| 217 | 217 | ||
| 218 | IDebugFunctions::IDebugFunctions() : ServiceFramework("IDebugFunctions") {} | 218 | IDebugFunctions::IDebugFunctions() : ServiceFramework{"IDebugFunctions"} { |
| 219 | // clang-format off | ||
| 220 | static const FunctionInfo functions[] = { | ||
| 221 | {0, nullptr, "NotifyMessageToHomeMenuForDebug"}, | ||
| 222 | {1, nullptr, "OpenMainApplication"}, | ||
| 223 | {10, nullptr, "EmulateButtonEvent"}, | ||
| 224 | {20, nullptr, "InvalidateTransitionLayer"}, | ||
| 225 | {30, nullptr, "RequestLaunchApplicationWithUserAndArgumentForDebug"}, | ||
| 226 | {40, nullptr, "GetAppletResourceUsageInfo"}, | ||
| 227 | }; | ||
| 228 | // clang-format on | ||
| 229 | |||
| 230 | RegisterHandlers(functions); | ||
| 231 | } | ||
| 232 | |||
| 219 | IDebugFunctions::~IDebugFunctions() = default; | 233 | IDebugFunctions::~IDebugFunctions() = default; |
| 220 | 234 | ||
| 221 | ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) | 235 | ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) |
diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h index 929035034..e584b92ec 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.h +++ b/src/core/hle/service/hid/controllers/debug_pad.h | |||
| @@ -41,20 +41,20 @@ private: | |||
| 41 | struct PadState { | 41 | struct PadState { |
| 42 | union { | 42 | union { |
| 43 | u32_le raw{}; | 43 | u32_le raw{}; |
| 44 | BitField<0, 1, u32_le> a; | 44 | BitField<0, 1, u32> a; |
| 45 | BitField<1, 1, u32_le> b; | 45 | BitField<1, 1, u32> b; |
| 46 | BitField<2, 1, u32_le> x; | 46 | BitField<2, 1, u32> x; |
| 47 | BitField<3, 1, u32_le> y; | 47 | BitField<3, 1, u32> y; |
| 48 | BitField<4, 1, u32_le> l; | 48 | BitField<4, 1, u32> l; |
| 49 | BitField<5, 1, u32_le> r; | 49 | BitField<5, 1, u32> r; |
| 50 | BitField<6, 1, u32_le> zl; | 50 | BitField<6, 1, u32> zl; |
| 51 | BitField<7, 1, u32_le> zr; | 51 | BitField<7, 1, u32> zr; |
| 52 | BitField<8, 1, u32_le> plus; | 52 | BitField<8, 1, u32> plus; |
| 53 | BitField<9, 1, u32_le> minus; | 53 | BitField<9, 1, u32> minus; |
| 54 | BitField<10, 1, u32_le> d_left; | 54 | BitField<10, 1, u32> d_left; |
| 55 | BitField<11, 1, u32_le> d_up; | 55 | BitField<11, 1, u32> d_up; |
| 56 | BitField<12, 1, u32_le> d_right; | 56 | BitField<12, 1, u32> d_right; |
| 57 | BitField<13, 1, u32_le> d_down; | 57 | BitField<13, 1, u32> d_down; |
| 58 | }; | 58 | }; |
| 59 | }; | 59 | }; |
| 60 | static_assert(sizeof(PadState) == 0x4, "PadState is an invalid size"); | 60 | static_assert(sizeof(PadState) == 0x4, "PadState is an invalid size"); |
| @@ -62,7 +62,7 @@ private: | |||
| 62 | struct Attributes { | 62 | struct Attributes { |
| 63 | union { | 63 | union { |
| 64 | u32_le raw{}; | 64 | u32_le raw{}; |
| 65 | BitField<0, 1, u32_le> connected; | 65 | BitField<0, 1, u32> connected; |
| 66 | }; | 66 | }; |
| 67 | }; | 67 | }; |
| 68 | static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size"); | 68 | static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size"); |
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 18c7a94e6..4ff50b3cd 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h | |||
| @@ -39,13 +39,13 @@ public: | |||
| 39 | union { | 39 | union { |
| 40 | u32_le raw{}; | 40 | u32_le raw{}; |
| 41 | 41 | ||
| 42 | BitField<0, 1, u32_le> pro_controller; | 42 | BitField<0, 1, u32> pro_controller; |
| 43 | BitField<1, 1, u32_le> handheld; | 43 | BitField<1, 1, u32> handheld; |
| 44 | BitField<2, 1, u32_le> joycon_dual; | 44 | BitField<2, 1, u32> joycon_dual; |
| 45 | BitField<3, 1, u32_le> joycon_left; | 45 | BitField<3, 1, u32> joycon_left; |
| 46 | BitField<4, 1, u32_le> joycon_right; | 46 | BitField<4, 1, u32> joycon_right; |
| 47 | 47 | ||
| 48 | BitField<6, 1, u32_le> pokeball; // TODO(ogniK): Confirm when possible | 48 | BitField<6, 1, u32> pokeball; // TODO(ogniK): Confirm when possible |
| 49 | }; | 49 | }; |
| 50 | }; | 50 | }; |
| 51 | static_assert(sizeof(NPadType) == 4, "NPadType is an invalid size"); | 51 | static_assert(sizeof(NPadType) == 4, "NPadType is an invalid size"); |
| @@ -150,43 +150,43 @@ private: | |||
| 150 | union { | 150 | union { |
| 151 | u64_le raw{}; | 151 | u64_le raw{}; |
| 152 | // Button states | 152 | // Button states |
| 153 | BitField<0, 1, u64_le> a; | 153 | BitField<0, 1, u64> a; |
| 154 | BitField<1, 1, u64_le> b; | 154 | BitField<1, 1, u64> b; |
| 155 | BitField<2, 1, u64_le> x; | 155 | BitField<2, 1, u64> x; |
| 156 | BitField<3, 1, u64_le> y; | 156 | BitField<3, 1, u64> y; |
| 157 | BitField<4, 1, u64_le> l_stick; | 157 | BitField<4, 1, u64> l_stick; |
| 158 | BitField<5, 1, u64_le> r_stick; | 158 | BitField<5, 1, u64> r_stick; |
| 159 | BitField<6, 1, u64_le> l; | 159 | BitField<6, 1, u64> l; |
| 160 | BitField<7, 1, u64_le> r; | 160 | BitField<7, 1, u64> r; |
| 161 | BitField<8, 1, u64_le> zl; | 161 | BitField<8, 1, u64> zl; |
| 162 | BitField<9, 1, u64_le> zr; | 162 | BitField<9, 1, u64> zr; |
| 163 | BitField<10, 1, u64_le> plus; | 163 | BitField<10, 1, u64> plus; |
| 164 | BitField<11, 1, u64_le> minus; | 164 | BitField<11, 1, u64> minus; |
| 165 | 165 | ||
| 166 | // D-Pad | 166 | // D-Pad |
| 167 | BitField<12, 1, u64_le> d_left; | 167 | BitField<12, 1, u64> d_left; |
| 168 | BitField<13, 1, u64_le> d_up; | 168 | BitField<13, 1, u64> d_up; |
| 169 | BitField<14, 1, u64_le> d_right; | 169 | BitField<14, 1, u64> d_right; |
| 170 | BitField<15, 1, u64_le> d_down; | 170 | BitField<15, 1, u64> d_down; |
| 171 | 171 | ||
| 172 | // Left JoyStick | 172 | // Left JoyStick |
| 173 | BitField<16, 1, u64_le> l_stick_left; | 173 | BitField<16, 1, u64> l_stick_left; |
| 174 | BitField<17, 1, u64_le> l_stick_up; | 174 | BitField<17, 1, u64> l_stick_up; |
| 175 | BitField<18, 1, u64_le> l_stick_right; | 175 | BitField<18, 1, u64> l_stick_right; |
| 176 | BitField<19, 1, u64_le> l_stick_down; | 176 | BitField<19, 1, u64> l_stick_down; |
| 177 | 177 | ||
| 178 | // Right JoyStick | 178 | // Right JoyStick |
| 179 | BitField<20, 1, u64_le> r_stick_left; | 179 | BitField<20, 1, u64> r_stick_left; |
| 180 | BitField<21, 1, u64_le> r_stick_up; | 180 | BitField<21, 1, u64> r_stick_up; |
| 181 | BitField<22, 1, u64_le> r_stick_right; | 181 | BitField<22, 1, u64> r_stick_right; |
| 182 | BitField<23, 1, u64_le> r_stick_down; | 182 | BitField<23, 1, u64> r_stick_down; |
| 183 | 183 | ||
| 184 | // Not always active? | 184 | // Not always active? |
| 185 | BitField<24, 1, u64_le> left_sl; | 185 | BitField<24, 1, u64> left_sl; |
| 186 | BitField<25, 1, u64_le> left_sr; | 186 | BitField<25, 1, u64> left_sr; |
| 187 | 187 | ||
| 188 | BitField<26, 1, u64_le> right_sl; | 188 | BitField<26, 1, u64> right_sl; |
| 189 | BitField<27, 1, u64_le> right_sr; | 189 | BitField<27, 1, u64> right_sr; |
| 190 | }; | 190 | }; |
| 191 | }; | 191 | }; |
| 192 | static_assert(sizeof(ControllerPadState) == 8, "ControllerPadState is an invalid size"); | 192 | static_assert(sizeof(ControllerPadState) == 8, "ControllerPadState is an invalid size"); |
| @@ -200,12 +200,12 @@ private: | |||
| 200 | struct ConnectionState { | 200 | struct ConnectionState { |
| 201 | union { | 201 | union { |
| 202 | u32_le raw{}; | 202 | u32_le raw{}; |
| 203 | BitField<0, 1, u32_le> IsConnected; | 203 | BitField<0, 1, u32> IsConnected; |
| 204 | BitField<1, 1, u32_le> IsWired; | 204 | BitField<1, 1, u32> IsWired; |
| 205 | BitField<2, 1, u32_le> IsLeftJoyConnected; | 205 | BitField<2, 1, u32> IsLeftJoyConnected; |
| 206 | BitField<3, 1, u32_le> IsLeftJoyWired; | 206 | BitField<3, 1, u32> IsLeftJoyWired; |
| 207 | BitField<4, 1, u32_le> IsRightJoyConnected; | 207 | BitField<4, 1, u32> IsRightJoyConnected; |
| 208 | BitField<5, 1, u32_le> IsRightJoyWired; | 208 | BitField<5, 1, u32> IsRightJoyWired; |
| 209 | }; | 209 | }; |
| 210 | }; | 210 | }; |
| 211 | static_assert(sizeof(ConnectionState) == 4, "ConnectionState is an invalid size"); | 211 | static_assert(sizeof(ConnectionState) == 4, "ConnectionState is an invalid size"); |
| @@ -240,23 +240,23 @@ private: | |||
| 240 | struct NPadProperties { | 240 | struct NPadProperties { |
| 241 | union { | 241 | union { |
| 242 | s64_le raw{}; | 242 | s64_le raw{}; |
| 243 | BitField<11, 1, s64_le> is_vertical; | 243 | BitField<11, 1, s64> is_vertical; |
| 244 | BitField<12, 1, s64_le> is_horizontal; | 244 | BitField<12, 1, s64> is_horizontal; |
| 245 | BitField<13, 1, s64_le> use_plus; | 245 | BitField<13, 1, s64> use_plus; |
| 246 | BitField<14, 1, s64_le> use_minus; | 246 | BitField<14, 1, s64> use_minus; |
| 247 | }; | 247 | }; |
| 248 | }; | 248 | }; |
| 249 | 249 | ||
| 250 | struct NPadDevice { | 250 | struct NPadDevice { |
| 251 | union { | 251 | union { |
| 252 | u32_le raw{}; | 252 | u32_le raw{}; |
| 253 | BitField<0, 1, s32_le> pro_controller; | 253 | BitField<0, 1, s32> pro_controller; |
| 254 | BitField<1, 1, s32_le> handheld; | 254 | BitField<1, 1, s32> handheld; |
| 255 | BitField<2, 1, s32_le> handheld_left; | 255 | BitField<2, 1, s32> handheld_left; |
| 256 | BitField<3, 1, s32_le> handheld_right; | 256 | BitField<3, 1, s32> handheld_right; |
| 257 | BitField<4, 1, s32_le> joycon_left; | 257 | BitField<4, 1, s32> joycon_left; |
| 258 | BitField<5, 1, s32_le> joycon_right; | 258 | BitField<5, 1, s32> joycon_right; |
| 259 | BitField<6, 1, s32_le> pokeball; | 259 | BitField<6, 1, s32> pokeball; |
| 260 | }; | 260 | }; |
| 261 | }; | 261 | }; |
| 262 | 262 | ||
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h index 012b6e0dd..76fc340e9 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.h +++ b/src/core/hle/service/hid/controllers/touchscreen.h | |||
| @@ -33,8 +33,8 @@ private: | |||
| 33 | struct Attributes { | 33 | struct Attributes { |
| 34 | union { | 34 | union { |
| 35 | u32 raw{}; | 35 | u32 raw{}; |
| 36 | BitField<0, 1, u32_le> start_touch; | 36 | BitField<0, 1, u32> start_touch; |
| 37 | BitField<1, 1, u32_le> end_touch; | 37 | BitField<1, 1, u32> end_touch; |
| 38 | }; | 38 | }; |
| 39 | }; | 39 | }; |
| 40 | static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size"); | 40 | static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size"); |
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index 7cc58db4c..498602de5 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h | |||
| @@ -4,6 +4,9 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "core/hle/service/hid/controllers/controller_base.h" | ||
| 8 | #include "core/hle/service/service.h" | ||
| 9 | |||
| 7 | #include "controllers/controller_base.h" | 10 | #include "controllers/controller_base.h" |
| 8 | #include "core/hle/service/service.h" | 11 | #include "core/hle/service/service.h" |
| 9 | 12 | ||
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index 9df7ac50f..d65693fc7 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp | |||
| @@ -319,15 +319,14 @@ public: | |||
| 319 | } | 319 | } |
| 320 | 320 | ||
| 321 | ASSERT(vm_manager | 321 | ASSERT(vm_manager |
| 322 | .MirrorMemory(*map_address, nro_addr, nro_size, | 322 | .MirrorMemory(*map_address, nro_addr, nro_size, Kernel::MemoryState::ModuleCode) |
| 323 | Kernel::MemoryState::ModuleCodeStatic) | ||
| 324 | .IsSuccess()); | 323 | .IsSuccess()); |
| 325 | ASSERT(vm_manager.UnmapRange(nro_addr, nro_size).IsSuccess()); | 324 | ASSERT(vm_manager.UnmapRange(nro_addr, nro_size).IsSuccess()); |
| 326 | 325 | ||
| 327 | if (bss_size > 0) { | 326 | if (bss_size > 0) { |
| 328 | ASSERT(vm_manager | 327 | ASSERT(vm_manager |
| 329 | .MirrorMemory(*map_address + nro_size, bss_addr, bss_size, | 328 | .MirrorMemory(*map_address + nro_size, bss_addr, bss_size, |
| 330 | Kernel::MemoryState::ModuleCodeStatic) | 329 | Kernel::MemoryState::ModuleCode) |
| 331 | .IsSuccess()); | 330 | .IsSuccess()); |
| 332 | ASSERT(vm_manager.UnmapRange(bss_addr, bss_size).IsSuccess()); | 331 | ASSERT(vm_manager.UnmapRange(bss_addr, bss_size).IsSuccess()); |
| 333 | } | 332 | } |
| @@ -388,8 +387,7 @@ public: | |||
| 388 | const auto& nro_size = iter->second.size; | 387 | const auto& nro_size = iter->second.size; |
| 389 | 388 | ||
| 390 | ASSERT(vm_manager | 389 | ASSERT(vm_manager |
| 391 | .MirrorMemory(heap_addr, mapped_addr, nro_size, | 390 | .MirrorMemory(heap_addr, mapped_addr, nro_size, Kernel::MemoryState::ModuleCode) |
| 392 | Kernel::MemoryState::ModuleCodeStatic) | ||
| 393 | .IsSuccess()); | 391 | .IsSuccess()); |
| 394 | ASSERT(vm_manager.UnmapRange(mapped_addr, nro_size).IsSuccess()); | 392 | ASSERT(vm_manager.UnmapRange(mapped_addr, nro_size).IsSuccess()); |
| 395 | 393 | ||
diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp index 1f462e087..2a61593e2 100644 --- a/src/core/hle/service/lm/lm.cpp +++ b/src/core/hle/service/lm/lm.cpp | |||
| @@ -42,7 +42,7 @@ private: | |||
| 42 | union { | 42 | union { |
| 43 | BitField<0, 16, Flags> flags; | 43 | BitField<0, 16, Flags> flags; |
| 44 | BitField<16, 8, Severity> severity; | 44 | BitField<16, 8, Severity> severity; |
| 45 | BitField<24, 8, u32_le> verbosity; | 45 | BitField<24, 8, u32> verbosity; |
| 46 | }; | 46 | }; |
| 47 | u32_le payload_size; | 47 | u32_le payload_size; |
| 48 | 48 | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h index 0f02a1a18..4f6042b00 100644 --- a/src/core/hle/service/nvdrv/devices/nvdevice.h +++ b/src/core/hle/service/nvdrv/devices/nvdevice.h | |||
| @@ -19,11 +19,11 @@ public: | |||
| 19 | virtual ~nvdevice() = default; | 19 | virtual ~nvdevice() = default; |
| 20 | union Ioctl { | 20 | union Ioctl { |
| 21 | u32_le raw; | 21 | u32_le raw; |
| 22 | BitField<0, 8, u32_le> cmd; | 22 | BitField<0, 8, u32> cmd; |
| 23 | BitField<8, 8, u32_le> group; | 23 | BitField<8, 8, u32> group; |
| 24 | BitField<16, 14, u32_le> length; | 24 | BitField<16, 14, u32> length; |
| 25 | BitField<30, 1, u32_le> is_in; | 25 | BitField<30, 1, u32> is_in; |
| 26 | BitField<31, 1, u32_le> is_out; | 26 | BitField<31, 1, u32> is_out; |
| 27 | }; | 27 | }; |
| 28 | 28 | ||
| 29 | /** | 29 | /** |
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index 6057c7f26..8b1920f22 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "common/file_util.h" | 10 | #include "common/file_util.h" |
| 11 | #include "common/logging/log.h" | 11 | #include "common/logging/log.h" |
| 12 | #include "core/hle/kernel/code_set.h" | ||
| 12 | #include "core/hle/kernel/process.h" | 13 | #include "core/hle/kernel/process.h" |
| 13 | #include "core/hle/kernel/vm_manager.h" | 14 | #include "core/hle/kernel/vm_manager.h" |
| 14 | #include "core/loader/elf.h" | 15 | #include "core/loader/elf.h" |
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index 4fad0c0dd..5de02a94b 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include "core/file_sys/romfs_factory.h" | 14 | #include "core/file_sys/romfs_factory.h" |
| 15 | #include "core/file_sys/vfs_offset.h" | 15 | #include "core/file_sys/vfs_offset.h" |
| 16 | #include "core/gdbstub/gdbstub.h" | 16 | #include "core/gdbstub/gdbstub.h" |
| 17 | #include "core/hle/kernel/code_set.h" | ||
| 17 | #include "core/hle/kernel/process.h" | 18 | #include "core/hle/kernel/process.h" |
| 18 | #include "core/hle/kernel/vm_manager.h" | 19 | #include "core/hle/kernel/vm_manager.h" |
| 19 | #include "core/hle/service/filesystem/filesystem.h" | 20 | #include "core/hle/service/filesystem/filesystem.h" |
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 6ded0b707..0eb9fd7f7 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp | |||
| @@ -7,10 +7,13 @@ | |||
| 7 | #include <lz4.h> | 7 | #include <lz4.h> |
| 8 | #include "common/common_funcs.h" | 8 | #include "common/common_funcs.h" |
| 9 | #include "common/file_util.h" | 9 | #include "common/file_util.h" |
| 10 | #include "common/hex_util.h" | ||
| 10 | #include "common/logging/log.h" | 11 | #include "common/logging/log.h" |
| 11 | #include "common/swap.h" | 12 | #include "common/swap.h" |
| 13 | #include "core/core.h" | ||
| 12 | #include "core/file_sys/patch_manager.h" | 14 | #include "core/file_sys/patch_manager.h" |
| 13 | #include "core/gdbstub/gdbstub.h" | 15 | #include "core/gdbstub/gdbstub.h" |
| 16 | #include "core/hle/kernel/code_set.h" | ||
| 14 | #include "core/hle/kernel/process.h" | 17 | #include "core/hle/kernel/process.h" |
| 15 | #include "core/hle/kernel/vm_manager.h" | 18 | #include "core/hle/kernel/vm_manager.h" |
| 16 | #include "core/loader/nso.h" | 19 | #include "core/loader/nso.h" |
| @@ -164,6 +167,16 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, | |||
| 164 | std::memcpy(program_image.data(), pi_header.data() + 0x100, program_image.size()); | 167 | std::memcpy(program_image.data(), pi_header.data() + 0x100, program_image.size()); |
| 165 | } | 168 | } |
| 166 | 169 | ||
| 170 | // Apply cheats if they exist and the program has a valid title ID | ||
| 171 | if (pm) { | ||
| 172 | const auto cheats = pm->CreateCheatList(nso_header.build_id); | ||
| 173 | if (!cheats.empty()) { | ||
| 174 | Core::System::GetInstance().RegisterCheatList( | ||
| 175 | cheats, Common::HexArrayToString(nso_header.build_id), load_base, | ||
| 176 | load_base + program_image.size()); | ||
| 177 | } | ||
| 178 | } | ||
| 179 | |||
| 167 | // Load codeset for current process | 180 | // Load codeset for current process |
| 168 | codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image)); | 181 | codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image)); |
| 169 | process.LoadModule(std::move(codeset), load_base); | 182 | process.LoadModule(std::move(codeset), load_base); |
diff --git a/src/core/memory.h b/src/core/memory.h index 3f60d868c..1d38cdca8 100644 --- a/src/core/memory.h +++ b/src/core/memory.h | |||
| @@ -6,9 +6,6 @@ | |||
| 6 | 6 | ||
| 7 | #include <cstddef> | 7 | #include <cstddef> |
| 8 | #include <string> | 8 | #include <string> |
| 9 | #include <tuple> | ||
| 10 | #include <vector> | ||
| 11 | #include <boost/icl/interval_map.hpp> | ||
| 12 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 13 | 10 | ||
| 14 | namespace Common { | 11 | namespace Common { |
diff --git a/src/input_common/sdl/sdl.h b/src/input_common/sdl/sdl.h index 02a8d2e2c..d7f24c68a 100644 --- a/src/input_common/sdl/sdl.h +++ b/src/input_common/sdl/sdl.h | |||
| @@ -24,17 +24,19 @@ namespace InputCommon::SDL { | |||
| 24 | 24 | ||
| 25 | class State { | 25 | class State { |
| 26 | public: | 26 | public: |
| 27 | /// Unresisters SDL device factories and shut them down. | 27 | using Pollers = std::vector<std::unique_ptr<Polling::DevicePoller>>; |
| 28 | |||
| 29 | /// Unregisters SDL device factories and shut them down. | ||
| 28 | virtual ~State() = default; | 30 | virtual ~State() = default; |
| 29 | 31 | ||
| 30 | virtual std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> GetPollers( | 32 | virtual Pollers GetPollers(Polling::DeviceType type) = 0; |
| 31 | InputCommon::Polling::DeviceType type) = 0; | ||
| 32 | }; | 33 | }; |
| 33 | 34 | ||
| 34 | class NullState : public State { | 35 | class NullState : public State { |
| 35 | public: | 36 | public: |
| 36 | std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> GetPollers( | 37 | Pollers GetPollers(Polling::DeviceType type) override { |
| 37 | InputCommon::Polling::DeviceType type) override {} | 38 | return {}; |
| 39 | } | ||
| 38 | }; | 40 | }; |
| 39 | 41 | ||
| 40 | std::unique_ptr<State> Init(); | 42 | std::unique_ptr<State> Init(); |
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index 6e8376549..b132d77f5 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp | |||
| @@ -650,9 +650,9 @@ private: | |||
| 650 | }; | 650 | }; |
| 651 | } // namespace Polling | 651 | } // namespace Polling |
| 652 | 652 | ||
| 653 | std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> SDLState::GetPollers( | 653 | SDLState::Pollers SDLState::GetPollers(InputCommon::Polling::DeviceType type) { |
| 654 | InputCommon::Polling::DeviceType type) { | 654 | Pollers pollers; |
| 655 | std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> pollers; | 655 | |
| 656 | switch (type) { | 656 | switch (type) { |
| 657 | case InputCommon::Polling::DeviceType::Analog: | 657 | case InputCommon::Polling::DeviceType::Analog: |
| 658 | pollers.emplace_back(std::make_unique<Polling::SDLAnalogPoller>(*this)); | 658 | pollers.emplace_back(std::make_unique<Polling::SDLAnalogPoller>(*this)); |
| @@ -660,8 +660,9 @@ std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> SDLState::GetPo | |||
| 660 | case InputCommon::Polling::DeviceType::Button: | 660 | case InputCommon::Polling::DeviceType::Button: |
| 661 | pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this)); | 661 | pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this)); |
| 662 | break; | 662 | break; |
| 663 | return pollers; | ||
| 664 | } | 663 | } |
| 664 | |||
| 665 | return pollers; | ||
| 665 | } | 666 | } |
| 666 | 667 | ||
| 667 | } // namespace SDL | 668 | } // namespace SDL |
diff --git a/src/input_common/sdl/sdl_impl.h b/src/input_common/sdl/sdl_impl.h index fec82fbe6..2579741d6 100644 --- a/src/input_common/sdl/sdl_impl.h +++ b/src/input_common/sdl/sdl_impl.h | |||
| @@ -25,7 +25,7 @@ public: | |||
| 25 | /// Initializes and registers SDL device factories | 25 | /// Initializes and registers SDL device factories |
| 26 | SDLState(); | 26 | SDLState(); |
| 27 | 27 | ||
| 28 | /// Unresisters SDL device factories and shut them down. | 28 | /// Unregisters SDL device factories and shut them down. |
| 29 | ~SDLState() override; | 29 | ~SDLState() override; |
| 30 | 30 | ||
| 31 | /// Handle SDL_Events for joysticks from SDL_PollEvent | 31 | /// Handle SDL_Events for joysticks from SDL_PollEvent |
| @@ -35,8 +35,7 @@ public: | |||
| 35 | std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const std::string& guid, int port); | 35 | std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const std::string& guid, int port); |
| 36 | 36 | ||
| 37 | /// Get all DevicePoller that use the SDL backend for a specific device type | 37 | /// Get all DevicePoller that use the SDL backend for a specific device type |
| 38 | std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> GetPollers( | 38 | Pollers GetPollers(Polling::DeviceType type) override; |
| 39 | InputCommon::Polling::DeviceType type) override; | ||
| 40 | 39 | ||
| 41 | /// Used by the Pollers during config | 40 | /// Used by the Pollers during config |
| 42 | std::atomic<bool> polling = false; | 41 | std::atomic<bool> polling = false; |
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 37f09ce5f..d0284bdf4 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt | |||
| @@ -1,4 +1,5 @@ | |||
| 1 | add_executable(tests | 1 | add_executable(tests |
| 2 | common/bit_field.cpp | ||
| 2 | common/param_package.cpp | 3 | common/param_package.cpp |
| 3 | common/ring_buffer.cpp | 4 | common/ring_buffer.cpp |
| 4 | core/arm/arm_test_common.cpp | 5 | core/arm/arm_test_common.cpp |
diff --git a/src/tests/common/bit_field.cpp b/src/tests/common/bit_field.cpp new file mode 100644 index 000000000..8ca1889f9 --- /dev/null +++ b/src/tests/common/bit_field.cpp | |||
| @@ -0,0 +1,90 @@ | |||
| 1 | // Copyright 2019 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <array> | ||
| 6 | #include <cstring> | ||
| 7 | #include <type_traits> | ||
| 8 | #include <catch2/catch.hpp> | ||
| 9 | #include "common/bit_field.h" | ||
| 10 | |||
| 11 | TEST_CASE("BitField", "[common]") { | ||
| 12 | enum class TestEnum : u32 { | ||
| 13 | A = 0b10111101, | ||
| 14 | B = 0b10101110, | ||
| 15 | C = 0b00001111, | ||
| 16 | }; | ||
| 17 | |||
| 18 | union LEBitField { | ||
| 19 | u32_le raw; | ||
| 20 | BitField<0, 6, u32> a; | ||
| 21 | BitField<6, 4, s32> b; | ||
| 22 | BitField<10, 8, TestEnum> c; | ||
| 23 | BitField<18, 14, u32> d; | ||
| 24 | } le_bitfield; | ||
| 25 | |||
| 26 | union BEBitField { | ||
| 27 | u32_be raw; | ||
| 28 | BitFieldBE<0, 6, u32> a; | ||
| 29 | BitFieldBE<6, 4, s32> b; | ||
| 30 | BitFieldBE<10, 8, TestEnum> c; | ||
| 31 | BitFieldBE<18, 14, u32> d; | ||
| 32 | } be_bitfield; | ||
| 33 | |||
| 34 | static_assert(sizeof(LEBitField) == sizeof(u32)); | ||
| 35 | static_assert(sizeof(BEBitField) == sizeof(u32)); | ||
| 36 | static_assert(std::is_trivially_copyable_v<LEBitField>); | ||
| 37 | static_assert(std::is_trivially_copyable_v<BEBitField>); | ||
| 38 | |||
| 39 | std::array<u8, 4> raw{{ | ||
| 40 | 0b01101100, | ||
| 41 | 0b11110110, | ||
| 42 | 0b10111010, | ||
| 43 | 0b11101100, | ||
| 44 | }}; | ||
| 45 | |||
| 46 | std::memcpy(&le_bitfield, &raw, sizeof(raw)); | ||
| 47 | std::memcpy(&be_bitfield, &raw, sizeof(raw)); | ||
| 48 | |||
| 49 | // bit fields: 11101100101110'10111101'1001'101100 | ||
| 50 | REQUIRE(le_bitfield.raw == 0b11101100'10111010'11110110'01101100); | ||
| 51 | REQUIRE(le_bitfield.a == 0b101100); | ||
| 52 | REQUIRE(le_bitfield.b == -7); // 1001 as two's complement | ||
| 53 | REQUIRE(le_bitfield.c == TestEnum::A); | ||
| 54 | REQUIRE(le_bitfield.d == 0b11101100101110); | ||
| 55 | |||
| 56 | le_bitfield.a.Assign(0b000111); | ||
| 57 | le_bitfield.b.Assign(-1); | ||
| 58 | le_bitfield.c.Assign(TestEnum::C); | ||
| 59 | le_bitfield.d.Assign(0b01010101010101); | ||
| 60 | std::memcpy(&raw, &le_bitfield, sizeof(raw)); | ||
| 61 | // bit fields: 01010101010101'00001111'1111'000111 | ||
| 62 | REQUIRE(le_bitfield.raw == 0b01010101'01010100'00111111'11000111); | ||
| 63 | REQUIRE(raw == std::array<u8, 4>{{ | ||
| 64 | 0b11000111, | ||
| 65 | 0b00111111, | ||
| 66 | 0b01010100, | ||
| 67 | 0b01010101, | ||
| 68 | }}); | ||
| 69 | |||
| 70 | // bit fields: 01101100111101'10101110'1011'101100 | ||
| 71 | REQUIRE(be_bitfield.raw == 0b01101100'11110110'10111010'11101100); | ||
| 72 | REQUIRE(be_bitfield.a == 0b101100); | ||
| 73 | REQUIRE(be_bitfield.b == -5); // 1011 as two's complement | ||
| 74 | REQUIRE(be_bitfield.c == TestEnum::B); | ||
| 75 | REQUIRE(be_bitfield.d == 0b01101100111101); | ||
| 76 | |||
| 77 | be_bitfield.a.Assign(0b000111); | ||
| 78 | be_bitfield.b.Assign(-1); | ||
| 79 | be_bitfield.c.Assign(TestEnum::C); | ||
| 80 | be_bitfield.d.Assign(0b01010101010101); | ||
| 81 | std::memcpy(&raw, &be_bitfield, sizeof(raw)); | ||
| 82 | // bit fields: 01010101010101'00001111'1111'000111 | ||
| 83 | REQUIRE(be_bitfield.raw == 0b01010101'01010100'00111111'11000111); | ||
| 84 | REQUIRE(raw == std::array<u8, 4>{{ | ||
| 85 | 0b01010101, | ||
| 86 | 0b01010100, | ||
| 87 | 0b00111111, | ||
| 88 | 0b11000111, | ||
| 89 | }}); | ||
| 90 | } | ||
diff --git a/src/video_core/rasterizer_cache.h b/src/video_core/rasterizer_cache.h index ecd9986a0..9fc9f3056 100644 --- a/src/video_core/rasterizer_cache.h +++ b/src/video_core/rasterizer_cache.h | |||
| @@ -132,7 +132,7 @@ protected: | |||
| 132 | } | 132 | } |
| 133 | 133 | ||
| 134 | /// Register an object into the cache | 134 | /// Register an object into the cache |
| 135 | void Register(const T& object) { | 135 | virtual void Register(const T& object) { |
| 136 | std::lock_guard<std::recursive_mutex> lock{mutex}; | 136 | std::lock_guard<std::recursive_mutex> lock{mutex}; |
| 137 | 137 | ||
| 138 | object->SetIsRegistered(true); | 138 | object->SetIsRegistered(true); |
| @@ -142,7 +142,7 @@ protected: | |||
| 142 | } | 142 | } |
| 143 | 143 | ||
| 144 | /// Unregisters an object from the cache | 144 | /// Unregisters an object from the cache |
| 145 | void Unregister(const T& object) { | 145 | virtual void Unregister(const T& object) { |
| 146 | std::lock_guard<std::recursive_mutex> lock{mutex}; | 146 | std::lock_guard<std::recursive_mutex> lock{mutex}; |
| 147 | 147 | ||
| 148 | object->SetIsRegistered(false); | 148 | object->SetIsRegistered(false); |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 39dcf71ce..0235317c0 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp | |||
| @@ -933,7 +933,7 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool pres | |||
| 933 | // If surface parameters changed and we care about keeping the previous data, recreate | 933 | // If surface parameters changed and we care about keeping the previous data, recreate |
| 934 | // the surface from the old one | 934 | // the surface from the old one |
| 935 | Surface new_surface{RecreateSurface(surface, params)}; | 935 | Surface new_surface{RecreateSurface(surface, params)}; |
| 936 | UnregisterSurface(surface); | 936 | Unregister(surface); |
| 937 | Register(new_surface); | 937 | Register(new_surface); |
| 938 | if (new_surface->IsUploaded()) { | 938 | if (new_surface->IsUploaded()) { |
| 939 | RegisterReinterpretSurface(new_surface); | 939 | RegisterReinterpretSurface(new_surface); |
| @@ -941,7 +941,7 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool pres | |||
| 941 | return new_surface; | 941 | return new_surface; |
| 942 | } else { | 942 | } else { |
| 943 | // Delete the old surface before creating a new one to prevent collisions. | 943 | // Delete the old surface before creating a new one to prevent collisions. |
| 944 | UnregisterSurface(surface); | 944 | Unregister(surface); |
| 945 | } | 945 | } |
| 946 | } | 946 | } |
| 947 | 947 | ||
| @@ -1295,12 +1295,12 @@ static bool IsReinterpretInvalidSecond(const Surface render_surface, | |||
| 1295 | bool RasterizerCacheOpenGL::PartialReinterpretSurface(Surface triggering_surface, | 1295 | bool RasterizerCacheOpenGL::PartialReinterpretSurface(Surface triggering_surface, |
| 1296 | Surface intersect) { | 1296 | Surface intersect) { |
| 1297 | if (IsReinterpretInvalid(triggering_surface, intersect)) { | 1297 | if (IsReinterpretInvalid(triggering_surface, intersect)) { |
| 1298 | UnregisterSurface(intersect); | 1298 | Unregister(intersect); |
| 1299 | return false; | 1299 | return false; |
| 1300 | } | 1300 | } |
| 1301 | if (!LayerFitReinterpretSurface(*this, triggering_surface, intersect)) { | 1301 | if (!LayerFitReinterpretSurface(*this, triggering_surface, intersect)) { |
| 1302 | if (IsReinterpretInvalidSecond(triggering_surface, intersect)) { | 1302 | if (IsReinterpretInvalidSecond(triggering_surface, intersect)) { |
| 1303 | UnregisterSurface(intersect); | 1303 | Unregister(intersect); |
| 1304 | return false; | 1304 | return false; |
| 1305 | } | 1305 | } |
| 1306 | FlushObject(intersect); | 1306 | FlushObject(intersect); |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index 0efcafd07..c644271d0 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h | |||
| @@ -538,13 +538,17 @@ private: | |||
| 538 | return nullptr; | 538 | return nullptr; |
| 539 | } | 539 | } |
| 540 | 540 | ||
| 541 | void Register(const Surface& object) { | ||
| 542 | RasterizerCache<Surface>::Register(object); | ||
| 543 | } | ||
| 544 | |||
| 541 | /// Unregisters an object from the cache | 545 | /// Unregisters an object from the cache |
| 542 | void UnregisterSurface(const Surface& object) { | 546 | void Unregister(const Surface& object) { |
| 543 | if (object->IsReinterpreted()) { | 547 | if (object->IsReinterpreted()) { |
| 544 | auto interval = GetReinterpretInterval(object); | 548 | auto interval = GetReinterpretInterval(object); |
| 545 | reinterpreted_surfaces.erase(interval); | 549 | reinterpreted_surfaces.erase(interval); |
| 546 | } | 550 | } |
| 547 | Unregister(object); | 551 | RasterizerCache<Surface>::Unregister(object); |
| 548 | } | 552 | } |
| 549 | }; | 553 | }; |
| 550 | 554 | ||