diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/common/typed_address.h | 82 | ||||
| -rw-r--r-- | src/core/debugger/gdbstub_arch.cpp | 4 | ||||
| -rw-r--r-- | src/core/hle/service/cmif_serialization.h | 6 | ||||
| -rw-r--r-- | src/core/hle/service/cmif_types.h | 27 | ||||
| -rw-r--r-- | src/core/memory/cheat_engine.cpp | 46 | ||||
| -rw-r--r-- | src/core/memory/cheat_engine.h | 8 | ||||
| -rw-r--r-- | src/core/memory/dmnt_cheat_types.h | 2 | ||||
| -rw-r--r-- | src/core/memory/dmnt_cheat_vm.cpp | 74 | ||||
| -rw-r--r-- | src/core/memory/dmnt_cheat_vm.h | 22 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_per_game.cpp | 3 |
10 files changed, 180 insertions, 94 deletions
diff --git a/src/common/typed_address.h b/src/common/typed_address.h index 64f4a07c2..d5e743583 100644 --- a/src/common/typed_address.h +++ b/src/common/typed_address.h | |||
| @@ -186,68 +186,68 @@ static_assert(std::is_trivially_destructible_v<PhysicalAddress>); | |||
| 186 | static_assert(std::is_trivially_destructible_v<VirtualAddress>); | 186 | static_assert(std::is_trivially_destructible_v<VirtualAddress>); |
| 187 | static_assert(std::is_trivially_destructible_v<ProcessAddress>); | 187 | static_assert(std::is_trivially_destructible_v<ProcessAddress>); |
| 188 | 188 | ||
| 189 | static_assert(Null<uint64_t> == 0); | 189 | static_assert(Null<uint64_t> == 0U); |
| 190 | static_assert(Null<PhysicalAddress> == Null<uint64_t>); | 190 | static_assert(Null<PhysicalAddress> == Null<uint64_t>); |
| 191 | static_assert(Null<VirtualAddress> == Null<uint64_t>); | 191 | static_assert(Null<VirtualAddress> == Null<uint64_t>); |
| 192 | static_assert(Null<ProcessAddress> == Null<uint64_t>); | 192 | static_assert(Null<ProcessAddress> == Null<uint64_t>); |
| 193 | 193 | ||
| 194 | // Constructor/assignment validations. | 194 | // Constructor/assignment validations. |
| 195 | static_assert([] { | 195 | static_assert([] { |
| 196 | const PhysicalAddress a(5); | 196 | const PhysicalAddress a(5U); |
| 197 | PhysicalAddress b(a); | 197 | PhysicalAddress b(a); |
| 198 | return b; | 198 | return b; |
| 199 | }() == PhysicalAddress(5)); | 199 | }() == PhysicalAddress(5U)); |
| 200 | static_assert([] { | 200 | static_assert([] { |
| 201 | const PhysicalAddress a(5); | 201 | const PhysicalAddress a(5U); |
| 202 | PhysicalAddress b(10); | 202 | PhysicalAddress b(10U); |
| 203 | b = a; | 203 | b = a; |
| 204 | return b; | 204 | return b; |
| 205 | }() == PhysicalAddress(5)); | 205 | }() == PhysicalAddress(5U)); |
| 206 | 206 | ||
| 207 | // Arithmetic validations. | 207 | // Arithmetic validations. |
| 208 | static_assert(PhysicalAddress(10) + 5 == PhysicalAddress(15)); | 208 | static_assert(PhysicalAddress(10U) + 5U == PhysicalAddress(15U)); |
| 209 | static_assert(PhysicalAddress(10) - 5 == PhysicalAddress(5)); | 209 | static_assert(PhysicalAddress(10U) - 5U == PhysicalAddress(5U)); |
| 210 | static_assert([] { | 210 | static_assert([] { |
| 211 | PhysicalAddress v(10); | 211 | PhysicalAddress v(10U); |
| 212 | v += 5; | 212 | v += 5U; |
| 213 | return v; | 213 | return v; |
| 214 | }() == PhysicalAddress(15)); | 214 | }() == PhysicalAddress(15U)); |
| 215 | static_assert([] { | 215 | static_assert([] { |
| 216 | PhysicalAddress v(10); | 216 | PhysicalAddress v(10U); |
| 217 | v -= 5; | 217 | v -= 5U; |
| 218 | return v; | 218 | return v; |
| 219 | }() == PhysicalAddress(5)); | 219 | }() == PhysicalAddress(5U)); |
| 220 | static_assert(PhysicalAddress(10)++ == PhysicalAddress(10)); | 220 | static_assert(PhysicalAddress(10U)++ == PhysicalAddress(10U)); |
| 221 | static_assert(++PhysicalAddress(10) == PhysicalAddress(11)); | 221 | static_assert(++PhysicalAddress(10U) == PhysicalAddress(11U)); |
| 222 | static_assert(PhysicalAddress(10)-- == PhysicalAddress(10)); | 222 | static_assert(PhysicalAddress(10U)-- == PhysicalAddress(10U)); |
| 223 | static_assert(--PhysicalAddress(10) == PhysicalAddress(9)); | 223 | static_assert(--PhysicalAddress(10U) == PhysicalAddress(9U)); |
| 224 | 224 | ||
| 225 | // Logical validations. | 225 | // Logical validations. |
| 226 | static_assert((PhysicalAddress(0b11111111) >> 1) == 0b01111111); | 226 | static_assert((PhysicalAddress(0b11111111U) >> 1) == 0b01111111U); |
| 227 | static_assert((PhysicalAddress(0b10101010) >> 1) == 0b01010101); | 227 | static_assert((PhysicalAddress(0b10101010U) >> 1) == 0b01010101U); |
| 228 | static_assert((PhysicalAddress(0b11111111) << 1) == 0b111111110); | 228 | static_assert((PhysicalAddress(0b11111111U) << 1) == 0b111111110U); |
| 229 | static_assert((PhysicalAddress(0b01010101) << 1) == 0b10101010); | 229 | static_assert((PhysicalAddress(0b01010101U) << 1) == 0b10101010U); |
| 230 | static_assert((PhysicalAddress(0b11111111) & 0b01010101) == 0b01010101); | 230 | static_assert((PhysicalAddress(0b11111111U) & 0b01010101U) == 0b01010101U); |
| 231 | static_assert((PhysicalAddress(0b11111111) & 0b10101010) == 0b10101010); | 231 | static_assert((PhysicalAddress(0b11111111U) & 0b10101010U) == 0b10101010U); |
| 232 | static_assert((PhysicalAddress(0b01010101) & 0b10101010) == 0b00000000); | 232 | static_assert((PhysicalAddress(0b01010101U) & 0b10101010U) == 0b00000000U); |
| 233 | static_assert((PhysicalAddress(0b00000000) | 0b01010101) == 0b01010101); | 233 | static_assert((PhysicalAddress(0b00000000U) | 0b01010101U) == 0b01010101U); |
| 234 | static_assert((PhysicalAddress(0b11111111) | 0b01010101) == 0b11111111); | 234 | static_assert((PhysicalAddress(0b11111111U) | 0b01010101U) == 0b11111111U); |
| 235 | static_assert((PhysicalAddress(0b10101010) | 0b01010101) == 0b11111111); | 235 | static_assert((PhysicalAddress(0b10101010U) | 0b01010101U) == 0b11111111U); |
| 236 | 236 | ||
| 237 | // Comparisons. | 237 | // Comparisons. |
| 238 | static_assert(PhysicalAddress(0) == PhysicalAddress(0)); | 238 | static_assert(PhysicalAddress(0U) == PhysicalAddress(0U)); |
| 239 | static_assert(PhysicalAddress(0) != PhysicalAddress(1)); | 239 | static_assert(PhysicalAddress(0U) != PhysicalAddress(1U)); |
| 240 | static_assert(PhysicalAddress(0) < PhysicalAddress(1)); | 240 | static_assert(PhysicalAddress(0U) < PhysicalAddress(1U)); |
| 241 | static_assert(PhysicalAddress(0) <= PhysicalAddress(1)); | 241 | static_assert(PhysicalAddress(0U) <= PhysicalAddress(1U)); |
| 242 | static_assert(PhysicalAddress(1) > PhysicalAddress(0)); | 242 | static_assert(PhysicalAddress(1U) > PhysicalAddress(0U)); |
| 243 | static_assert(PhysicalAddress(1) >= PhysicalAddress(0)); | 243 | static_assert(PhysicalAddress(1U) >= PhysicalAddress(0U)); |
| 244 | 244 | ||
| 245 | static_assert(!(PhysicalAddress(0) == PhysicalAddress(1))); | 245 | static_assert(!(PhysicalAddress(0U) == PhysicalAddress(1U))); |
| 246 | static_assert(!(PhysicalAddress(0) != PhysicalAddress(0))); | 246 | static_assert(!(PhysicalAddress(0U) != PhysicalAddress(0U))); |
| 247 | static_assert(!(PhysicalAddress(1) < PhysicalAddress(0))); | 247 | static_assert(!(PhysicalAddress(1U) < PhysicalAddress(0U))); |
| 248 | static_assert(!(PhysicalAddress(1) <= PhysicalAddress(0))); | 248 | static_assert(!(PhysicalAddress(1U) <= PhysicalAddress(0U))); |
| 249 | static_assert(!(PhysicalAddress(0) > PhysicalAddress(1))); | 249 | static_assert(!(PhysicalAddress(0U) > PhysicalAddress(1U))); |
| 250 | static_assert(!(PhysicalAddress(0) >= PhysicalAddress(1))); | 250 | static_assert(!(PhysicalAddress(0U) >= PhysicalAddress(1U))); |
| 251 | 251 | ||
| 252 | } // namespace Common | 252 | } // namespace Common |
| 253 | 253 | ||
diff --git a/src/core/debugger/gdbstub_arch.cpp b/src/core/debugger/gdbstub_arch.cpp index f2a407dc8..452f565be 100644 --- a/src/core/debugger/gdbstub_arch.cpp +++ b/src/core/debugger/gdbstub_arch.cpp | |||
| @@ -383,7 +383,7 @@ std::string GDBStubA32::RegRead(const Kernel::KThread* thread, size_t id) const | |||
| 383 | } else if (id == CPSR_REGISTER) { | 383 | } else if (id == CPSR_REGISTER) { |
| 384 | return ValueToHex(context.pstate); | 384 | return ValueToHex(context.pstate); |
| 385 | } else if (id >= D0_REGISTER && id < Q0_REGISTER) { | 385 | } else if (id >= D0_REGISTER && id < Q0_REGISTER) { |
| 386 | return ValueToHex(fprs[id - D0_REGISTER][0]); | 386 | return ValueToHex(fprs[(id - D0_REGISTER) / 2][(id - D0_REGISTER) % 2]); |
| 387 | } else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) { | 387 | } else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) { |
| 388 | return ValueToHex(fprs[id - Q0_REGISTER]); | 388 | return ValueToHex(fprs[id - Q0_REGISTER]); |
| 389 | } else if (id == FPSCR_REGISTER) { | 389 | } else if (id == FPSCR_REGISTER) { |
| @@ -406,7 +406,7 @@ void GDBStubA32::RegWrite(Kernel::KThread* thread, size_t id, std::string_view v | |||
| 406 | } else if (id == CPSR_REGISTER) { | 406 | } else if (id == CPSR_REGISTER) { |
| 407 | context.pstate = HexToValue<u32>(value); | 407 | context.pstate = HexToValue<u32>(value); |
| 408 | } else if (id >= D0_REGISTER && id < Q0_REGISTER) { | 408 | } else if (id >= D0_REGISTER && id < Q0_REGISTER) { |
| 409 | fprs[id - D0_REGISTER] = {HexToValue<u64>(value), 0}; | 409 | fprs[(id - D0_REGISTER) / 2][(id - D0_REGISTER) % 2] = HexToValue<u64>(value); |
| 410 | } else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) { | 410 | } else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) { |
| 411 | fprs[id - Q0_REGISTER] = HexToValue<u128>(value); | 411 | fprs[id - Q0_REGISTER] = HexToValue<u128>(value); |
| 412 | } else if (id == FPSCR_REGISTER) { | 412 | } else if (id == FPSCR_REGISTER) { |
diff --git a/src/core/hle/service/cmif_serialization.h b/src/core/hle/service/cmif_serialization.h index 315475e71..e985fe317 100644 --- a/src/core/hle/service/cmif_serialization.h +++ b/src/core/hle/service/cmif_serialization.h | |||
| @@ -115,6 +115,11 @@ struct ArgumentTraits { | |||
| 115 | static constexpr ArgumentType Type = ArgumentType::InData; | 115 | static constexpr ArgumentType Type = ArgumentType::InData; |
| 116 | }; | 116 | }; |
| 117 | 117 | ||
| 118 | template <typename... Ts> | ||
| 119 | consteval bool ConstIfReference() { | ||
| 120 | return ((!std::is_reference_v<Ts> || std::is_const_v<std::remove_reference_t<Ts>>) && ... && true); | ||
| 121 | } | ||
| 122 | |||
| 118 | struct RequestLayout { | 123 | struct RequestLayout { |
| 119 | u32 copy_handle_count; | 124 | u32 copy_handle_count; |
| 120 | u32 move_handle_count; | 125 | u32 move_handle_count; |
| @@ -435,6 +440,7 @@ void CmifReplyWrapImpl(HLERequestContext& ctx, T& t, Result (T::*f)(A...)) { | |||
| 435 | } | 440 | } |
| 436 | const bool is_domain = Domain ? ctx.GetManager()->IsDomain() : false; | 441 | const bool is_domain = Domain ? ctx.GetManager()->IsDomain() : false; |
| 437 | 442 | ||
| 443 | static_assert(ConstIfReference<A...>(), "Arguments taken by reference must be const"); | ||
| 438 | using MethodArguments = std::tuple<std::remove_cvref_t<A>...>; | 444 | using MethodArguments = std::tuple<std::remove_cvref_t<A>...>; |
| 439 | 445 | ||
| 440 | OutTemporaryBuffers buffers{}; | 446 | OutTemporaryBuffers buffers{}; |
diff --git a/src/core/hle/service/cmif_types.h b/src/core/hle/service/cmif_types.h index dc06169f4..84f4c2456 100644 --- a/src/core/hle/service/cmif_types.h +++ b/src/core/hle/service/cmif_types.h | |||
| @@ -4,10 +4,9 @@ | |||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <memory> | 6 | #include <memory> |
| 7 | #include <span> | ||
| 7 | 8 | ||
| 8 | #include "common/common_funcs.h" | ||
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "core/hle/service/hle_ipc.h" | ||
| 11 | 10 | ||
| 12 | namespace Service { | 11 | namespace Service { |
| 13 | 12 | ||
| @@ -22,8 +21,10 @@ class Out { | |||
| 22 | public: | 21 | public: |
| 23 | using Type = T; | 22 | using Type = T; |
| 24 | 23 | ||
| 24 | /* implicit */ Out(const Out& t) : raw(t.raw) {} | ||
| 25 | /* implicit */ Out(AutoOut<Type>& t) : raw(&t.raw) {} | 25 | /* implicit */ Out(AutoOut<Type>& t) : raw(&t.raw) {} |
| 26 | /* implicit */ Out(Type* t) : raw(t) {} | 26 | /* implicit */ Out(Type* t) : raw(t) {} |
| 27 | Out& operator=(const Out&) = delete; | ||
| 27 | 28 | ||
| 28 | Type* Get() const { | 29 | Type* Get() const { |
| 29 | return raw; | 30 | return raw; |
| @@ -37,6 +38,10 @@ public: | |||
| 37 | return raw; | 38 | return raw; |
| 38 | } | 39 | } |
| 39 | 40 | ||
| 41 | operator Type*() const { | ||
| 42 | return raw; | ||
| 43 | } | ||
| 44 | |||
| 40 | private: | 45 | private: |
| 41 | Type* raw; | 46 | Type* raw; |
| 42 | }; | 47 | }; |
| @@ -113,8 +118,10 @@ class OutCopyHandle { | |||
| 113 | public: | 118 | public: |
| 114 | using Type = T*; | 119 | using Type = T*; |
| 115 | 120 | ||
| 121 | /* implicit */ OutCopyHandle(const OutCopyHandle& t) : raw(t.raw) {} | ||
| 116 | /* implicit */ OutCopyHandle(AutoOut<Type>& t) : raw(&t.raw) {} | 122 | /* implicit */ OutCopyHandle(AutoOut<Type>& t) : raw(&t.raw) {} |
| 117 | /* implicit */ OutCopyHandle(Type* t) : raw(t) {} | 123 | /* implicit */ OutCopyHandle(Type* t) : raw(t) {} |
| 124 | OutCopyHandle& operator=(const OutCopyHandle&) = delete; | ||
| 118 | 125 | ||
| 119 | Type* Get() const { | 126 | Type* Get() const { |
| 120 | return raw; | 127 | return raw; |
| @@ -128,6 +135,10 @@ public: | |||
| 128 | return raw; | 135 | return raw; |
| 129 | } | 136 | } |
| 130 | 137 | ||
| 138 | operator Type*() const { | ||
| 139 | return raw; | ||
| 140 | } | ||
| 141 | |||
| 131 | private: | 142 | private: |
| 132 | Type* raw; | 143 | Type* raw; |
| 133 | }; | 144 | }; |
| @@ -137,8 +148,10 @@ class OutMoveHandle { | |||
| 137 | public: | 148 | public: |
| 138 | using Type = T*; | 149 | using Type = T*; |
| 139 | 150 | ||
| 151 | /* implicit */ OutMoveHandle(const OutMoveHandle& t) : raw(t.raw) {} | ||
| 140 | /* implicit */ OutMoveHandle(AutoOut<Type>& t) : raw(&t.raw) {} | 152 | /* implicit */ OutMoveHandle(AutoOut<Type>& t) : raw(&t.raw) {} |
| 141 | /* implicit */ OutMoveHandle(Type* t) : raw(t) {} | 153 | /* implicit */ OutMoveHandle(Type* t) : raw(t) {} |
| 154 | OutMoveHandle& operator=(const OutMoveHandle&) = delete; | ||
| 142 | 155 | ||
| 143 | Type* Get() const { | 156 | Type* Get() const { |
| 144 | return raw; | 157 | return raw; |
| @@ -152,6 +165,10 @@ public: | |||
| 152 | return raw; | 165 | return raw; |
| 153 | } | 166 | } |
| 154 | 167 | ||
| 168 | operator Type*() const { | ||
| 169 | return raw; | ||
| 170 | } | ||
| 171 | |||
| 155 | private: | 172 | private: |
| 156 | Type* raw; | 173 | Type* raw; |
| 157 | }; | 174 | }; |
| @@ -248,8 +265,10 @@ public: | |||
| 248 | static constexpr BufferAttr Attr = static_cast<BufferAttr>(A | BufferAttr_In | BufferAttr_FixedSize); | 265 | static constexpr BufferAttr Attr = static_cast<BufferAttr>(A | BufferAttr_In | BufferAttr_FixedSize); |
| 249 | using Type = T; | 266 | using Type = T; |
| 250 | 267 | ||
| 268 | /* implicit */ OutLargeData(const OutLargeData& t) : raw(t.raw) {} | ||
| 251 | /* implicit */ OutLargeData(Type* t) : raw(t) {} | 269 | /* implicit */ OutLargeData(Type* t) : raw(t) {} |
| 252 | /* implicit */ OutLargeData(AutoOut<T>& t) : raw(&t.raw) {} | 270 | /* implicit */ OutLargeData(AutoOut<T>& t) : raw(&t.raw) {} |
| 271 | OutLargeData& operator=(const OutLargeData&) = delete; | ||
| 253 | 272 | ||
| 254 | Type* Get() const { | 273 | Type* Get() const { |
| 255 | return raw; | 274 | return raw; |
| @@ -263,6 +282,10 @@ public: | |||
| 263 | return raw; | 282 | return raw; |
| 264 | } | 283 | } |
| 265 | 284 | ||
| 285 | operator Type*() const { | ||
| 286 | return raw; | ||
| 287 | } | ||
| 288 | |||
| 266 | private: | 289 | private: |
| 267 | Type* raw; | 290 | Type* raw; |
| 268 | }; | 291 | }; |
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp index 96fa7fa3a..14d1a3840 100644 --- a/src/core/memory/cheat_engine.cpp +++ b/src/core/memory/cheat_engine.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "core/core_timing.h" | 9 | #include "core/core_timing.h" |
| 10 | #include "core/hle/kernel/k_page_table.h" | 10 | #include "core/hle/kernel/k_page_table.h" |
| 11 | #include "core/hle/kernel/k_process.h" | 11 | #include "core/hle/kernel/k_process.h" |
| 12 | #include "core/hle/kernel/k_process_page_table.h" | ||
| 12 | #include "core/hle/service/hid/hid_server.h" | 13 | #include "core/hle/service/hid/hid_server.h" |
| 13 | #include "core/hle/service/sm/sm.h" | 14 | #include "core/hle/service/sm/sm.h" |
| 14 | #include "core/memory.h" | 15 | #include "core/memory.h" |
| @@ -46,12 +47,23 @@ StandardVmCallbacks::StandardVmCallbacks(System& system_, const CheatProcessMeta | |||
| 46 | 47 | ||
| 47 | StandardVmCallbacks::~StandardVmCallbacks() = default; | 48 | StandardVmCallbacks::~StandardVmCallbacks() = default; |
| 48 | 49 | ||
| 49 | void StandardVmCallbacks::MemoryRead(VAddr address, void* data, u64 size) { | 50 | void StandardVmCallbacks::MemoryReadUnsafe(VAddr address, void* data, u64 size) { |
| 50 | system.ApplicationMemory().ReadBlock(SanitizeAddress(address), data, size); | 51 | // Return zero on invalid address |
| 52 | if (!IsAddressInRange(address) || !system.ApplicationMemory().IsValidVirtualAddress(address)) { | ||
| 53 | std::memset(data, 0, size); | ||
| 54 | return; | ||
| 55 | } | ||
| 56 | |||
| 57 | system.ApplicationMemory().ReadBlock(address, data, size); | ||
| 51 | } | 58 | } |
| 52 | 59 | ||
| 53 | void StandardVmCallbacks::MemoryWrite(VAddr address, const void* data, u64 size) { | 60 | void StandardVmCallbacks::MemoryWriteUnsafe(VAddr address, const void* data, u64 size) { |
| 54 | system.ApplicationMemory().WriteBlock(SanitizeAddress(address), data, size); | 61 | // Skip invalid memory write address |
| 62 | if (!IsAddressInRange(address) || !system.ApplicationMemory().IsValidVirtualAddress(address)) { | ||
| 63 | return; | ||
| 64 | } | ||
| 65 | |||
| 66 | system.ApplicationMemory().WriteBlock(address, data, size); | ||
| 55 | } | 67 | } |
| 56 | 68 | ||
| 57 | u64 StandardVmCallbacks::HidKeysDown() { | 69 | u64 StandardVmCallbacks::HidKeysDown() { |
| @@ -81,21 +93,25 @@ void StandardVmCallbacks::CommandLog(std::string_view data) { | |||
| 81 | data.back() == '\n' ? data.substr(0, data.size() - 1) : data); | 93 | data.back() == '\n' ? data.substr(0, data.size() - 1) : data); |
| 82 | } | 94 | } |
| 83 | 95 | ||
| 84 | VAddr StandardVmCallbacks::SanitizeAddress(VAddr in) const { | 96 | bool StandardVmCallbacks::IsAddressInRange(VAddr in) const { |
| 85 | if ((in < metadata.main_nso_extents.base || | 97 | if ((in < metadata.main_nso_extents.base || |
| 86 | in >= metadata.main_nso_extents.base + metadata.main_nso_extents.size) && | 98 | in >= metadata.main_nso_extents.base + metadata.main_nso_extents.size) && |
| 87 | (in < metadata.heap_extents.base || | 99 | (in < metadata.heap_extents.base || |
| 88 | in >= metadata.heap_extents.base + metadata.heap_extents.size)) { | 100 | in >= metadata.heap_extents.base + metadata.heap_extents.size) && |
| 89 | LOG_ERROR(CheatEngine, | 101 | (in < metadata.alias_extents.base || |
| 102 | in >= metadata.heap_extents.base + metadata.alias_extents.size) && | ||
| 103 | (in < metadata.aslr_extents.base || | ||
| 104 | in >= metadata.heap_extents.base + metadata.aslr_extents.size)) { | ||
| 105 | LOG_DEBUG(CheatEngine, | ||
| 90 | "Cheat attempting to access memory at invalid address={:016X}, if this " | 106 | "Cheat attempting to access memory at invalid address={:016X}, if this " |
| 91 | "persists, " | 107 | "persists, " |
| 92 | "the cheat may be incorrect. However, this may be normal early in execution if " | 108 | "the cheat may be incorrect. However, this may be normal early in execution if " |
| 93 | "the game has not properly set up yet.", | 109 | "the game has not properly set up yet.", |
| 94 | in); | 110 | in); |
| 95 | return 0; ///< Invalid addresses will hard crash | 111 | return false; ///< Invalid addresses will hard crash |
| 96 | } | 112 | } |
| 97 | 113 | ||
| 98 | return in; | 114 | return true; |
| 99 | } | 115 | } |
| 100 | 116 | ||
| 101 | CheatParser::~CheatParser() = default; | 117 | CheatParser::~CheatParser() = default; |
| @@ -211,16 +227,14 @@ void CheatEngine::Initialize() { | |||
| 211 | .base = GetInteger(page_table.GetHeapRegionStart()), | 227 | .base = GetInteger(page_table.GetHeapRegionStart()), |
| 212 | .size = page_table.GetHeapRegionSize(), | 228 | .size = page_table.GetHeapRegionSize(), |
| 213 | }; | 229 | }; |
| 214 | 230 | metadata.aslr_extents = { | |
| 215 | metadata.address_space_extents = { | ||
| 216 | .base = GetInteger(page_table.GetAddressSpaceStart()), | ||
| 217 | .size = page_table.GetAddressSpaceSize(), | ||
| 218 | }; | ||
| 219 | |||
| 220 | metadata.alias_extents = { | ||
| 221 | .base = GetInteger(page_table.GetAliasCodeRegionStart()), | 231 | .base = GetInteger(page_table.GetAliasCodeRegionStart()), |
| 222 | .size = page_table.GetAliasCodeRegionSize(), | 232 | .size = page_table.GetAliasCodeRegionSize(), |
| 223 | }; | 233 | }; |
| 234 | metadata.alias_extents = { | ||
| 235 | .base = GetInteger(page_table.GetAliasRegionStart()), | ||
| 236 | .size = page_table.GetAliasRegionSize(), | ||
| 237 | }; | ||
| 224 | 238 | ||
| 225 | is_pending_reload.exchange(true); | 239 | is_pending_reload.exchange(true); |
| 226 | } | 240 | } |
diff --git a/src/core/memory/cheat_engine.h b/src/core/memory/cheat_engine.h index ced2168d1..619cabaa2 100644 --- a/src/core/memory/cheat_engine.h +++ b/src/core/memory/cheat_engine.h | |||
| @@ -27,17 +27,17 @@ public: | |||
| 27 | StandardVmCallbacks(System& system_, const CheatProcessMetadata& metadata_); | 27 | StandardVmCallbacks(System& system_, const CheatProcessMetadata& metadata_); |
| 28 | ~StandardVmCallbacks() override; | 28 | ~StandardVmCallbacks() override; |
| 29 | 29 | ||
| 30 | void MemoryRead(VAddr address, void* data, u64 size) override; | 30 | void MemoryReadUnsafe(VAddr address, void* data, u64 size) override; |
| 31 | void MemoryWrite(VAddr address, const void* data, u64 size) override; | 31 | void MemoryWriteUnsafe(VAddr address, const void* data, u64 size) override; |
| 32 | u64 HidKeysDown() override; | 32 | u64 HidKeysDown() override; |
| 33 | void DebugLog(u8 id, u64 value) override; | 33 | void DebugLog(u8 id, u64 value) override; |
| 34 | void CommandLog(std::string_view data) override; | 34 | void CommandLog(std::string_view data) override; |
| 35 | 35 | ||
| 36 | private: | 36 | private: |
| 37 | VAddr SanitizeAddress(VAddr address) const; | 37 | bool IsAddressInRange(VAddr address) const; |
| 38 | 38 | ||
| 39 | const CheatProcessMetadata& metadata; | 39 | const CheatProcessMetadata& metadata; |
| 40 | System& system; | 40 | Core::System& system; |
| 41 | }; | 41 | }; |
| 42 | 42 | ||
| 43 | // Intermediary class that parses a text file or other disk format for storing cheats into a | 43 | // Intermediary class that parses a text file or other disk format for storing cheats into a |
diff --git a/src/core/memory/dmnt_cheat_types.h b/src/core/memory/dmnt_cheat_types.h index c6b40e505..64c072d3d 100644 --- a/src/core/memory/dmnt_cheat_types.h +++ b/src/core/memory/dmnt_cheat_types.h | |||
| @@ -18,7 +18,7 @@ struct CheatProcessMetadata { | |||
| 18 | MemoryRegionExtents main_nso_extents{}; | 18 | MemoryRegionExtents main_nso_extents{}; |
| 19 | MemoryRegionExtents heap_extents{}; | 19 | MemoryRegionExtents heap_extents{}; |
| 20 | MemoryRegionExtents alias_extents{}; | 20 | MemoryRegionExtents alias_extents{}; |
| 21 | MemoryRegionExtents address_space_extents{}; | 21 | MemoryRegionExtents aslr_extents{}; |
| 22 | std::array<u8, 0x20> main_nso_build_id{}; | 22 | std::array<u8, 0x20> main_nso_build_id{}; |
| 23 | }; | 23 | }; |
| 24 | 24 | ||
diff --git a/src/core/memory/dmnt_cheat_vm.cpp b/src/core/memory/dmnt_cheat_vm.cpp index 31ffc4fbb..8bc81e72d 100644 --- a/src/core/memory/dmnt_cheat_vm.cpp +++ b/src/core/memory/dmnt_cheat_vm.cpp | |||
| @@ -322,8 +322,9 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { | |||
| 322 | } break; | 322 | } break; |
| 323 | case CheatVmOpcodeType::EndConditionalBlock: { | 323 | case CheatVmOpcodeType::EndConditionalBlock: { |
| 324 | // 20000000 | 324 | // 20000000 |
| 325 | // There's actually nothing left to process here! | 325 | opcode.opcode = EndConditionalOpcode{ |
| 326 | opcode.opcode = EndConditionalOpcode{}; | 326 | .is_else = ((first_dword >> 24) & 0xf) == 1, |
| 327 | }; | ||
| 327 | } break; | 328 | } break; |
| 328 | case CheatVmOpcodeType::ControlLoop: { | 329 | case CheatVmOpcodeType::ControlLoop: { |
| 329 | // 300R0000 VVVVVVVV | 330 | // 300R0000 VVVVVVVV |
| @@ -555,6 +556,18 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { | |||
| 555 | .idx = first_dword & 0xF, | 556 | .idx = first_dword & 0xF, |
| 556 | }; | 557 | }; |
| 557 | } break; | 558 | } break; |
| 559 | case CheatVmOpcodeType::PauseProcess: { | ||
| 560 | /* FF0????? */ | ||
| 561 | /* FF0 = opcode 0xFF0 */ | ||
| 562 | /* Pauses the current process. */ | ||
| 563 | opcode.opcode = PauseProcessOpcode{}; | ||
| 564 | } break; | ||
| 565 | case CheatVmOpcodeType::ResumeProcess: { | ||
| 566 | /* FF0????? */ | ||
| 567 | /* FF0 = opcode 0xFF0 */ | ||
| 568 | /* Pauses the current process. */ | ||
| 569 | opcode.opcode = ResumeProcessOpcode{}; | ||
| 570 | } break; | ||
| 558 | case CheatVmOpcodeType::DebugLog: { | 571 | case CheatVmOpcodeType::DebugLog: { |
| 559 | // FFFTIX## | 572 | // FFFTIX## |
| 560 | // FFFTI0Ma aaaaaaaa | 573 | // FFFTI0Ma aaaaaaaa |
| @@ -621,7 +634,7 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { | |||
| 621 | return valid; | 634 | return valid; |
| 622 | } | 635 | } |
| 623 | 636 | ||
| 624 | void DmntCheatVm::SkipConditionalBlock() { | 637 | void DmntCheatVm::SkipConditionalBlock(bool is_if) { |
| 625 | if (condition_depth > 0) { | 638 | if (condition_depth > 0) { |
| 626 | // We want to continue until we're out of the current block. | 639 | // We want to continue until we're out of the current block. |
| 627 | const std::size_t desired_depth = condition_depth - 1; | 640 | const std::size_t desired_depth = condition_depth - 1; |
| @@ -637,8 +650,12 @@ void DmntCheatVm::SkipConditionalBlock() { | |||
| 637 | // We also support nesting of conditional blocks, and Gateway does not. | 650 | // We also support nesting of conditional blocks, and Gateway does not. |
| 638 | if (skip_opcode.begin_conditional_block) { | 651 | if (skip_opcode.begin_conditional_block) { |
| 639 | condition_depth++; | 652 | condition_depth++; |
| 640 | } else if (std::holds_alternative<EndConditionalOpcode>(skip_opcode.opcode)) { | 653 | } else if (auto end_cond = std::get_if<EndConditionalOpcode>(&skip_opcode.opcode)) { |
| 641 | condition_depth--; | 654 | if (!end_cond->is_else) { |
| 655 | condition_depth--; | ||
| 656 | } else if (is_if && condition_depth - 1 == desired_depth) { | ||
| 657 | break; | ||
| 658 | } | ||
| 642 | } | 659 | } |
| 643 | } | 660 | } |
| 644 | } else { | 661 | } else { |
| @@ -675,6 +692,10 @@ u64 DmntCheatVm::GetCheatProcessAddress(const CheatProcessMetadata& metadata, | |||
| 675 | return metadata.main_nso_extents.base + rel_address; | 692 | return metadata.main_nso_extents.base + rel_address; |
| 676 | case MemoryAccessType::Heap: | 693 | case MemoryAccessType::Heap: |
| 677 | return metadata.heap_extents.base + rel_address; | 694 | return metadata.heap_extents.base + rel_address; |
| 695 | case MemoryAccessType::Alias: | ||
| 696 | return metadata.alias_extents.base + rel_address; | ||
| 697 | case MemoryAccessType::Aslr: | ||
| 698 | return metadata.aslr_extents.base + rel_address; | ||
| 678 | } | 699 | } |
| 679 | } | 700 | } |
| 680 | 701 | ||
| @@ -682,7 +703,6 @@ void DmntCheatVm::ResetState() { | |||
| 682 | registers.fill(0); | 703 | registers.fill(0); |
| 683 | saved_values.fill(0); | 704 | saved_values.fill(0); |
| 684 | loop_tops.fill(0); | 705 | loop_tops.fill(0); |
| 685 | static_registers.fill(0); | ||
| 686 | instruction_ptr = 0; | 706 | instruction_ptr = 0; |
| 687 | condition_depth = 0; | 707 | condition_depth = 0; |
| 688 | decode_success = true; | 708 | decode_success = true; |
| @@ -753,7 +773,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { | |||
| 753 | case 2: | 773 | case 2: |
| 754 | case 4: | 774 | case 4: |
| 755 | case 8: | 775 | case 8: |
| 756 | callbacks->MemoryWrite(dst_address, &dst_value, store_static->bit_width); | 776 | callbacks->MemoryWriteUnsafe(dst_address, &dst_value, store_static->bit_width); |
| 757 | break; | 777 | break; |
| 758 | } | 778 | } |
| 759 | } else if (auto begin_cond = std::get_if<BeginConditionalOpcode>(&cur_opcode.opcode)) { | 779 | } else if (auto begin_cond = std::get_if<BeginConditionalOpcode>(&cur_opcode.opcode)) { |
| @@ -766,7 +786,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { | |||
| 766 | case 2: | 786 | case 2: |
| 767 | case 4: | 787 | case 4: |
| 768 | case 8: | 788 | case 8: |
| 769 | callbacks->MemoryRead(src_address, &src_value, begin_cond->bit_width); | 789 | callbacks->MemoryReadUnsafe(src_address, &src_value, begin_cond->bit_width); |
| 770 | break; | 790 | break; |
| 771 | } | 791 | } |
| 772 | // Check against condition. | 792 | // Check against condition. |
| @@ -794,13 +814,18 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { | |||
| 794 | } | 814 | } |
| 795 | // Skip conditional block if condition not met. | 815 | // Skip conditional block if condition not met. |
| 796 | if (!cond_met) { | 816 | if (!cond_met) { |
| 797 | SkipConditionalBlock(); | 817 | SkipConditionalBlock(true); |
| 798 | } | 818 | } |
| 799 | } else if (std::holds_alternative<EndConditionalOpcode>(cur_opcode.opcode)) { | 819 | } else if (auto end_cond = std::get_if<EndConditionalOpcode>(&cur_opcode.opcode)) { |
| 800 | // Decrement the condition depth. | 820 | if (end_cond->is_else) { |
| 801 | // We will assume, graciously, that mismatched conditional block ends are a nop. | 821 | /* Skip to the end of the conditional block. */ |
| 802 | if (condition_depth > 0) { | 822 | this->SkipConditionalBlock(false); |
| 803 | condition_depth--; | 823 | } else { |
| 824 | /* Decrement the condition depth. */ | ||
| 825 | /* We will assume, graciously, that mismatched conditional block ends are a nop. */ | ||
| 826 | if (condition_depth > 0) { | ||
| 827 | condition_depth--; | ||
| 828 | } | ||
| 804 | } | 829 | } |
| 805 | } else if (auto ctrl_loop = std::get_if<ControlLoopOpcode>(&cur_opcode.opcode)) { | 830 | } else if (auto ctrl_loop = std::get_if<ControlLoopOpcode>(&cur_opcode.opcode)) { |
| 806 | if (ctrl_loop->start_loop) { | 831 | if (ctrl_loop->start_loop) { |
| @@ -832,8 +857,8 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { | |||
| 832 | case 2: | 857 | case 2: |
| 833 | case 4: | 858 | case 4: |
| 834 | case 8: | 859 | case 8: |
| 835 | callbacks->MemoryRead(src_address, ®isters[ldr_memory->reg_index], | 860 | callbacks->MemoryReadUnsafe(src_address, ®isters[ldr_memory->reg_index], |
| 836 | ldr_memory->bit_width); | 861 | ldr_memory->bit_width); |
| 837 | break; | 862 | break; |
| 838 | } | 863 | } |
| 839 | } else if (auto str_static = std::get_if<StoreStaticToAddressOpcode>(&cur_opcode.opcode)) { | 864 | } else if (auto str_static = std::get_if<StoreStaticToAddressOpcode>(&cur_opcode.opcode)) { |
| @@ -849,7 +874,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { | |||
| 849 | case 2: | 874 | case 2: |
| 850 | case 4: | 875 | case 4: |
| 851 | case 8: | 876 | case 8: |
| 852 | callbacks->MemoryWrite(dst_address, &dst_value, str_static->bit_width); | 877 | callbacks->MemoryWriteUnsafe(dst_address, &dst_value, str_static->bit_width); |
| 853 | break; | 878 | break; |
| 854 | } | 879 | } |
| 855 | // Increment register if relevant. | 880 | // Increment register if relevant. |
| @@ -908,7 +933,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { | |||
| 908 | // Check for keypress. | 933 | // Check for keypress. |
| 909 | if ((begin_keypress_cond->key_mask & kDown) != begin_keypress_cond->key_mask) { | 934 | if ((begin_keypress_cond->key_mask & kDown) != begin_keypress_cond->key_mask) { |
| 910 | // Keys not pressed. Skip conditional block. | 935 | // Keys not pressed. Skip conditional block. |
| 911 | SkipConditionalBlock(); | 936 | SkipConditionalBlock(true); |
| 912 | } | 937 | } |
| 913 | } else if (auto perform_math_reg = | 938 | } else if (auto perform_math_reg = |
| 914 | std::get_if<PerformArithmeticRegisterOpcode>(&cur_opcode.opcode)) { | 939 | std::get_if<PerformArithmeticRegisterOpcode>(&cur_opcode.opcode)) { |
| @@ -1007,7 +1032,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { | |||
| 1007 | case 2: | 1032 | case 2: |
| 1008 | case 4: | 1033 | case 4: |
| 1009 | case 8: | 1034 | case 8: |
| 1010 | callbacks->MemoryWrite(dst_address, &dst_value, str_register->bit_width); | 1035 | callbacks->MemoryWriteUnsafe(dst_address, &dst_value, str_register->bit_width); |
| 1011 | break; | 1036 | break; |
| 1012 | } | 1037 | } |
| 1013 | 1038 | ||
| @@ -1086,7 +1111,8 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { | |||
| 1086 | case 2: | 1111 | case 2: |
| 1087 | case 4: | 1112 | case 4: |
| 1088 | case 8: | 1113 | case 8: |
| 1089 | callbacks->MemoryRead(cond_address, &cond_value, begin_reg_cond->bit_width); | 1114 | callbacks->MemoryReadUnsafe(cond_address, &cond_value, |
| 1115 | begin_reg_cond->bit_width); | ||
| 1090 | break; | 1116 | break; |
| 1091 | } | 1117 | } |
| 1092 | } | 1118 | } |
| @@ -1116,7 +1142,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { | |||
| 1116 | 1142 | ||
| 1117 | // Skip conditional block if condition not met. | 1143 | // Skip conditional block if condition not met. |
| 1118 | if (!cond_met) { | 1144 | if (!cond_met) { |
| 1119 | SkipConditionalBlock(); | 1145 | SkipConditionalBlock(true); |
| 1120 | } | 1146 | } |
| 1121 | } else if (auto save_restore_reg = | 1147 | } else if (auto save_restore_reg = |
| 1122 | std::get_if<SaveRestoreRegisterOpcode>(&cur_opcode.opcode)) { | 1148 | std::get_if<SaveRestoreRegisterOpcode>(&cur_opcode.opcode)) { |
| @@ -1178,6 +1204,10 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { | |||
| 1178 | // Store a register to a static register. | 1204 | // Store a register to a static register. |
| 1179 | static_registers[rw_static_reg->static_idx] = registers[rw_static_reg->idx]; | 1205 | static_registers[rw_static_reg->static_idx] = registers[rw_static_reg->idx]; |
| 1180 | } | 1206 | } |
| 1207 | } else if (std::holds_alternative<PauseProcessOpcode>(cur_opcode.opcode)) { | ||
| 1208 | // TODO: Pause cheat process | ||
| 1209 | } else if (std::holds_alternative<ResumeProcessOpcode>(cur_opcode.opcode)) { | ||
| 1210 | // TODO: Resume cheat process | ||
| 1181 | } else if (auto debug_log = std::get_if<DebugLogOpcode>(&cur_opcode.opcode)) { | 1211 | } else if (auto debug_log = std::get_if<DebugLogOpcode>(&cur_opcode.opcode)) { |
| 1182 | // Read value from memory. | 1212 | // Read value from memory. |
| 1183 | u64 log_value = 0; | 1213 | u64 log_value = 0; |
| @@ -1224,7 +1254,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { | |||
| 1224 | case 2: | 1254 | case 2: |
| 1225 | case 4: | 1255 | case 4: |
| 1226 | case 8: | 1256 | case 8: |
| 1227 | callbacks->MemoryRead(val_address, &log_value, debug_log->bit_width); | 1257 | callbacks->MemoryReadUnsafe(val_address, &log_value, debug_log->bit_width); |
| 1228 | break; | 1258 | break; |
| 1229 | } | 1259 | } |
| 1230 | } | 1260 | } |
diff --git a/src/core/memory/dmnt_cheat_vm.h b/src/core/memory/dmnt_cheat_vm.h index 641cb09c4..fed6a24ad 100644 --- a/src/core/memory/dmnt_cheat_vm.h +++ b/src/core/memory/dmnt_cheat_vm.h | |||
| @@ -42,12 +42,16 @@ enum class CheatVmOpcodeType : u32 { | |||
| 42 | DoubleExtendedWidth = 0xF0, | 42 | DoubleExtendedWidth = 0xF0, |
| 43 | 43 | ||
| 44 | // Double-extended width opcodes. | 44 | // Double-extended width opcodes. |
| 45 | PauseProcess = 0xFF0, | ||
| 46 | ResumeProcess = 0xFF1, | ||
| 45 | DebugLog = 0xFFF, | 47 | DebugLog = 0xFFF, |
| 46 | }; | 48 | }; |
| 47 | 49 | ||
| 48 | enum class MemoryAccessType : u32 { | 50 | enum class MemoryAccessType : u32 { |
| 49 | MainNso = 0, | 51 | MainNso = 0, |
| 50 | Heap = 1, | 52 | Heap = 1, |
| 53 | Alias = 2, | ||
| 54 | Aslr = 3, | ||
| 51 | }; | 55 | }; |
| 52 | 56 | ||
| 53 | enum class ConditionalComparisonType : u32 { | 57 | enum class ConditionalComparisonType : u32 { |
| @@ -131,7 +135,9 @@ struct BeginConditionalOpcode { | |||
| 131 | VmInt value{}; | 135 | VmInt value{}; |
| 132 | }; | 136 | }; |
| 133 | 137 | ||
| 134 | struct EndConditionalOpcode {}; | 138 | struct EndConditionalOpcode { |
| 139 | bool is_else; | ||
| 140 | }; | ||
| 135 | 141 | ||
| 136 | struct ControlLoopOpcode { | 142 | struct ControlLoopOpcode { |
| 137 | bool start_loop{}; | 143 | bool start_loop{}; |
| @@ -222,6 +228,10 @@ struct ReadWriteStaticRegisterOpcode { | |||
| 222 | u32 idx{}; | 228 | u32 idx{}; |
| 223 | }; | 229 | }; |
| 224 | 230 | ||
| 231 | struct PauseProcessOpcode {}; | ||
| 232 | |||
| 233 | struct ResumeProcessOpcode {}; | ||
| 234 | |||
| 225 | struct DebugLogOpcode { | 235 | struct DebugLogOpcode { |
| 226 | u32 bit_width{}; | 236 | u32 bit_width{}; |
| 227 | u32 log_id{}; | 237 | u32 log_id{}; |
| @@ -244,8 +254,8 @@ struct CheatVmOpcode { | |||
| 244 | PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode, | 254 | PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode, |
| 245 | PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode, | 255 | PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode, |
| 246 | BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode, | 256 | BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode, |
| 247 | SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, DebugLogOpcode, | 257 | SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, PauseProcessOpcode, |
| 248 | UnrecognizedInstruction> | 258 | ResumeProcessOpcode, DebugLogOpcode, UnrecognizedInstruction> |
| 249 | opcode{}; | 259 | opcode{}; |
| 250 | }; | 260 | }; |
| 251 | 261 | ||
| @@ -256,8 +266,8 @@ public: | |||
| 256 | public: | 266 | public: |
| 257 | virtual ~Callbacks(); | 267 | virtual ~Callbacks(); |
| 258 | 268 | ||
| 259 | virtual void MemoryRead(VAddr address, void* data, u64 size) = 0; | 269 | virtual void MemoryReadUnsafe(VAddr address, void* data, u64 size) = 0; |
| 260 | virtual void MemoryWrite(VAddr address, const void* data, u64 size) = 0; | 270 | virtual void MemoryWriteUnsafe(VAddr address, const void* data, u64 size) = 0; |
| 261 | 271 | ||
| 262 | virtual u64 HidKeysDown() = 0; | 272 | virtual u64 HidKeysDown() = 0; |
| 263 | 273 | ||
| @@ -296,7 +306,7 @@ private: | |||
| 296 | std::array<std::size_t, NumRegisters> loop_tops{}; | 306 | std::array<std::size_t, NumRegisters> loop_tops{}; |
| 297 | 307 | ||
| 298 | bool DecodeNextOpcode(CheatVmOpcode& out); | 308 | bool DecodeNextOpcode(CheatVmOpcode& out); |
| 299 | void SkipConditionalBlock(); | 309 | void SkipConditionalBlock(bool is_if); |
| 300 | void ResetState(); | 310 | void ResetState(); |
| 301 | 311 | ||
| 302 | // For implementing the DebugLog opcode. | 312 | // For implementing the DebugLog opcode. |
diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp index 9d38ab812..4dbe801a9 100644 --- a/src/yuzu/configuration/configure_per_game.cpp +++ b/src/yuzu/configuration/configure_per_game.cpp | |||
| @@ -73,8 +73,11 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st | |||
| 73 | ui->tabWidget->addTab(graphics_advanced_tab.get(), tr("Adv. Graphics")); | 73 | ui->tabWidget->addTab(graphics_advanced_tab.get(), tr("Adv. Graphics")); |
| 74 | ui->tabWidget->addTab(audio_tab.get(), tr("Audio")); | 74 | ui->tabWidget->addTab(audio_tab.get(), tr("Audio")); |
| 75 | ui->tabWidget->addTab(input_tab.get(), tr("Input Profiles")); | 75 | ui->tabWidget->addTab(input_tab.get(), tr("Input Profiles")); |
| 76 | |||
| 76 | // Only show Linux tab on Unix | 77 | // Only show Linux tab on Unix |
| 78 | linux_tab->setVisible(false); | ||
| 77 | #ifdef __unix__ | 79 | #ifdef __unix__ |
| 80 | linux_tab->setVisible(true); | ||
| 78 | ui->tabWidget->addTab(linux_tab.get(), tr("Linux")); | 81 | ui->tabWidget->addTab(linux_tab.get(), tr("Linux")); |
| 79 | #endif | 82 | #endif |
| 80 | 83 | ||