Compare commits
7 Commits
pgl
...
dmnt_new_f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
79d7e9197c | ||
|
|
11b8a92458 | ||
|
|
332dbdd497 | ||
|
|
98cc051387 | ||
|
|
f2944d36ba | ||
|
|
46d79387e8 | ||
|
|
0bb2c0a04f |
@@ -27,7 +27,7 @@ This software is licensed under the terms of the GPLv2, with exemptions for spec
|
||||
You can find a copy of the license in the [LICENSE file](LICENSE).
|
||||
|
||||
Exemptions:
|
||||
* The [yuzu emulator project](https://github.com/yuzu-emu/yuzu) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the Atmosphère project as GPLv2 or later.
|
||||
* The [yuzu Nintendo Switch emulator](https://github.com/yuzu-emu/yuzu) and the [Ryujinx Team and Contributors](https://github.com/orgs/Ryujinx) are exempt from GPLv2 licensing. They are permitted, each at their individual discretion, to instead license any source code authored for the Atmosphère project as either GPLv2 or later or the [MIT license](https://github.com/Atmosphere-NX/Atmosphere/blob/master/docs/licensing_exemptions/MIT_LICENSE). In doing so, they may alter, supplement, or entirely remove the copyright notice for each file they choose to relicense. Neither the Atmosphère project nor its individual contributors shall assert their moral rights against any of the aforementioned projects.
|
||||
* [Nintendo](https://github.com/Nintendo) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the Atmosphère project under the Zero-Clause BSD license.
|
||||
|
||||
Credits
|
||||
|
||||
21
docs/licensing_exemptions/MIT_LICENSE
Normal file
21
docs/licensing_exemptions/MIT_LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018-2020 Atmosphère-NX
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -6,7 +6,7 @@
|
||||
[subrepo]
|
||||
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
|
||||
branch = master
|
||||
commit = 96825c7524c333536dcffadb76341bf599785538
|
||||
parent = d81a3bdc36ddf3d7b4f3d7fae5cb85afc047b546
|
||||
commit = c1fe12fcbd661ae47673af5f224804c92eebdbb5
|
||||
parent = 98cc051387515977dcdede8ab0f1b09077f2f24b
|
||||
method = merge
|
||||
cmdver = 0.4.1
|
||||
|
||||
@@ -10,7 +10,7 @@ This software is licensed under the terms of the GPLv2, with exemptions for spec
|
||||
You can find a copy of the license in the [LICENSE file](LICENSE).
|
||||
|
||||
Exemptions:
|
||||
* The [yuzu emulator project](https://github.com/yuzu-emu/yuzu) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the atmosphere-libs project as GPLv2 or later.
|
||||
* The [yuzu Nintendo Switch emulator](https://github.com/yuzu-emu/yuzu) and the [Ryujinx Team and Contributors](https://github.com/orgs/Ryujinx) are exempt from GPLv2 licensing. They are permitted, each at their individual discretion, to instead license any source code authored for the atmosphere-libs project as either GPLv2 or later or the [MIT license](https://github.com/Atmosphere-NX/Atmosphere/blob/master/docs/licensing_exemptions/MIT_LICENSE). In doing so, they may alter, supplement, or entirely remove the copyright notice for each file they choose to relicense. Neither the Atmosphère project nor its individual contributors shall assert their moral rights against any of the aforementioned projects.
|
||||
* [Nintendo](https://github.com/Nintendo) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the atmosphere-libs project under the Zero-Clause BSD license.
|
||||
|
||||
Credits
|
||||
|
||||
@@ -10,7 +10,7 @@ This software is licensed under the terms of the GPLv2, with exemptions for spec
|
||||
You can find a copy of the license in the [LICENSE file](LICENSE).
|
||||
|
||||
Exemptions:
|
||||
* The [yuzu emulator project](https://github.com/yuzu-emu/yuzu) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the libmesosphere project as GPLv2 or later.
|
||||
* The [yuzu Nintendo Switch emulator](https://github.com/yuzu-emu/yuzu) and the [Ryujinx Team and Contributors](https://github.com/orgs/Ryujinx) are exempt from GPLv2 licensing. They are permitted, each at their individual discretion, to instead license any source code authored for the libmesosphere project as either GPLv2 or later or the [MIT license](https://github.com/Atmosphere-NX/Atmosphere/blob/master/docs/licensing_exemptions/MIT_LICENSE). In doing so, they may alter, supplement, or entirely remove the copyright notice for each file they choose to relicense. Neither the Atmosphère project nor its individual contributors shall assert their moral rights against any of the aforementioned projects.
|
||||
* [Nintendo](https://github.com/Nintendo) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the libmesosphere project under the Zero-Clause BSD license.
|
||||
|
||||
Credits
|
||||
|
||||
@@ -94,6 +94,7 @@ namespace ams::kern {
|
||||
KProcessAddress FindFreeArea(KProcessAddress region_start, size_t region_num_pages, size_t num_pages, size_t alignment, size_t offset, size_t guard_pages) const;
|
||||
|
||||
void Update(KMemoryBlockManagerUpdateAllocator *allocator, KProcessAddress address, size_t num_pages, KMemoryState state, KMemoryPermission perm, KMemoryAttribute attr);
|
||||
void UpdateLock(KMemoryBlockManagerUpdateAllocator *allocator, KProcessAddress address, size_t num_pages, void (KMemoryBlock::*lock_func)(KMemoryPermission new_perm), KMemoryPermission perm);
|
||||
|
||||
iterator FindIterator(KProcessAddress address) const {
|
||||
return this->memory_block_tree.find(KMemoryBlock(address, 1, KMemoryState_Free, KMemoryPermission_None, KMemoryAttribute_None));
|
||||
|
||||
@@ -197,6 +197,7 @@ namespace ams::kern {
|
||||
cur_address += cur_info.GetSize();
|
||||
remaining_pages -= cur_info.GetNumPages();
|
||||
}
|
||||
it++;
|
||||
}
|
||||
|
||||
/* Find the iterator now that we've updated. */
|
||||
@@ -227,6 +228,86 @@ namespace ams::kern {
|
||||
}
|
||||
}
|
||||
|
||||
void KMemoryBlockManager::UpdateLock(KMemoryBlockManagerUpdateAllocator *allocator, KProcessAddress address, size_t num_pages, void (KMemoryBlock::*lock_func)(KMemoryPermission new_perm), KMemoryPermission perm) {
|
||||
/* Ensure for auditing that we never end up with an invalid tree. */
|
||||
KScopedMemoryBlockManagerAuditor auditor(this);
|
||||
MESOSPHERE_ASSERT(util::IsAligned(GetInteger(address), PageSize));
|
||||
|
||||
KProcessAddress cur_address = address;
|
||||
size_t remaining_pages = num_pages;
|
||||
iterator it = this->FindIterator(address);
|
||||
iterator prev = it, next = it;
|
||||
bool check_coalesce_prev = false, check_coalesce_next = false;
|
||||
|
||||
while (remaining_pages > 0) {
|
||||
const size_t remaining_size = remaining_pages * PageSize;
|
||||
KMemoryInfo cur_info = it->GetMemoryInfo();
|
||||
|
||||
/* If we need to, create a new block before and insert it. */
|
||||
if (cur_info.address != GetInteger(cur_address)) {
|
||||
KMemoryBlock *new_block = allocator->Allocate();
|
||||
|
||||
it->Split(new_block, cur_address);
|
||||
it = this->memory_block_tree.insert(*new_block);
|
||||
it++;
|
||||
|
||||
cur_info = it->GetMemoryInfo();
|
||||
cur_address = cur_info.GetAddress();
|
||||
} else if (cur_address == address && cur_address != this->start_address) {
|
||||
/* If there's a previous, we should check for coalescing. */
|
||||
check_coalesce_prev = true;
|
||||
prev--;
|
||||
} else if (cur_info.GetSize() > remaining_size) {
|
||||
/* If we need to, create a new block after and insert it. */
|
||||
KMemoryBlock *new_block = allocator->Allocate();
|
||||
|
||||
it->Split(new_block, cur_address + remaining_size);
|
||||
it = this->memory_block_tree.insert(*new_block);
|
||||
|
||||
cur_info = it->GetMemoryInfo();
|
||||
} else if (cur_info.GetSize() == remaining_size) {
|
||||
/* Otherwise if we can map precisely, we may need to check for coalescing against next block. */
|
||||
next = it;
|
||||
++next;
|
||||
if (next != this->memory_block_tree.end()) {
|
||||
check_coalesce_next = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Call the locked update function. */
|
||||
(std::addressof(*it)->*lock_func)(perm);
|
||||
cur_address += cur_info.GetSize();
|
||||
remaining_pages -= cur_info.GetNumPages();
|
||||
it++;
|
||||
}
|
||||
|
||||
/* If we should try to coalesce prev, do so. */
|
||||
if (check_coalesce_prev) {
|
||||
it = prev;
|
||||
it++;
|
||||
if (prev->HasSameProperties(*it)) {
|
||||
KMemoryBlock *block = std::addressof(*it);
|
||||
const size_t pages = it->GetNumPages();
|
||||
this->memory_block_tree.erase(it);
|
||||
allocator->Free(block);
|
||||
prev->Add(pages);
|
||||
}
|
||||
}
|
||||
|
||||
/* If we should try to coalesce next, do so. */
|
||||
if (check_coalesce_next) {
|
||||
it = next;
|
||||
it--;
|
||||
if (it->HasSameProperties(*next)) {
|
||||
KMemoryBlock *block = std::addressof(*next);
|
||||
const size_t pages = next->GetNumPages();
|
||||
this->memory_block_tree.erase(next);
|
||||
allocator->Free(block);
|
||||
it->Add(pages);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Debug. */
|
||||
bool KMemoryBlockManager::CheckState() const {
|
||||
/* If we fail, we should dump blocks. */
|
||||
|
||||
@@ -14,7 +14,7 @@ This software is licensed under the terms of the GPLv2, with exemptions for spec
|
||||
You can find a copy of the license in the [LICENSE file](LICENSE).
|
||||
|
||||
Exemptions:
|
||||
* The [yuzu emulator project](https://github.com/yuzu-emu/yuzu) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the libstratosphere project as GPLv2 or later.
|
||||
* The [yuzu Nintendo Switch emulator](https://github.com/yuzu-emu/yuzu) and the [Ryujinx Team and Contributors](https://github.com/orgs/Ryujinx) are exempt from GPLv2 licensing. They are permitted, each at their individual discretion, to instead license any source code authored for the libstratosphere project as either GPLv2 or later or the [MIT license](https://github.com/Atmosphere-NX/Atmosphere/blob/master/docs/licensing_exemptions/MIT_LICENSE). In doing so, they may alter, supplement, or entirely remove the copyright notice for each file they choose to relicense. Neither the Atmosphère project nor its individual contributors shall assert their moral rights against any of the aforementioned projects.
|
||||
* [Nintendo](https://github.com/Nintendo) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the libstratosphere project under the Zero-Clause BSD license.
|
||||
|
||||
Credits
|
||||
|
||||
@@ -66,6 +66,14 @@ namespace ams::dmnt::cheat {
|
||||
return dmnt::cheat::impl::QueryCheatProcessMemory(mapping.GetPointer(), address);
|
||||
}
|
||||
|
||||
Result CheatService::BreakCheatProcess() {
|
||||
return dmnt::cheat::impl::BreakCheatProcess();
|
||||
}
|
||||
|
||||
Result CheatService::ContinueCheatProcess() {
|
||||
return dmnt::cheat::impl::ContinueCheatProcess();
|
||||
}
|
||||
|
||||
/* ========================================================================================= */
|
||||
/* =================================== Cheat Commands ==================================== */
|
||||
/* ========================================================================================= */
|
||||
@@ -95,6 +103,18 @@ namespace ams::dmnt::cheat {
|
||||
return dmnt::cheat::impl::RemoveCheat(cheat_id);
|
||||
}
|
||||
|
||||
Result CheatService::ReadStaticRegister(sf::Out<u64> out, u8 which) {
|
||||
return dmnt::cheat::impl::ReadStaticRegister(out.GetPointer(), which);
|
||||
}
|
||||
|
||||
Result CheatService::WriteStaticRegister(u8 which, u64 value) {
|
||||
return dmnt::cheat::impl::WriteStaticRegister(which, value);
|
||||
}
|
||||
|
||||
Result CheatService::ResetStaticRegisters() {
|
||||
return dmnt::cheat::impl::ResetStaticRegisters();
|
||||
}
|
||||
|
||||
/* ========================================================================================= */
|
||||
/* =================================== Address Commands ================================== */
|
||||
/* ========================================================================================= */
|
||||
|
||||
@@ -33,14 +33,19 @@ namespace ams::dmnt::cheat {
|
||||
ReadCheatProcessMemory = 65102,
|
||||
WriteCheatProcessMemory = 65103,
|
||||
QueryCheatProcessMemory = 65104,
|
||||
BreakCheatProcess = 65105,
|
||||
ContinueCheatProcess = 65106,
|
||||
|
||||
/* Interact with Cheats */
|
||||
GetCheatCount = 65200,
|
||||
GetCheats = 65201,
|
||||
GetCheatById = 65202,
|
||||
ToggleCheat = 65203,
|
||||
AddCheat = 65204,
|
||||
RemoveCheat = 65205,
|
||||
GetCheatCount = 65200,
|
||||
GetCheats = 65201,
|
||||
GetCheatById = 65202,
|
||||
ToggleCheat = 65203,
|
||||
AddCheat = 65204,
|
||||
RemoveCheat = 65205,
|
||||
ReadStaticRegister = 65206,
|
||||
WriteStaticRegister = 65207,
|
||||
ResetStaticRegisters = 65208,
|
||||
|
||||
/* Interact with Frozen Addresses */
|
||||
GetFrozenAddressCount = 65300,
|
||||
@@ -60,6 +65,8 @@ namespace ams::dmnt::cheat {
|
||||
Result ReadCheatProcessMemory(const sf::OutBuffer &buffer, u64 address, u64 out_size);
|
||||
Result WriteCheatProcessMemory(const sf::InBuffer &buffer, u64 address, u64 in_size);
|
||||
Result QueryCheatProcessMemory(sf::Out<MemoryInfo> mapping, u64 address);
|
||||
Result BreakCheatProcess();
|
||||
Result ContinueCheatProcess();
|
||||
|
||||
Result GetCheatCount(sf::Out<u64> out_count);
|
||||
Result GetCheats(const sf::OutArray<CheatEntry> &cheats, sf::Out<u64> out_count, u64 offset);
|
||||
@@ -67,6 +74,9 @@ namespace ams::dmnt::cheat {
|
||||
Result ToggleCheat(u32 cheat_id);
|
||||
Result AddCheat(const CheatDefinition &cheat, sf::Out<u32> out_cheat_id, bool enabled);
|
||||
Result RemoveCheat(u32 cheat_id);
|
||||
Result ReadStaticRegister(sf::Out<u64> out, u8 which);
|
||||
Result WriteStaticRegister(u8 which, u64 value);
|
||||
Result ResetStaticRegisters();
|
||||
|
||||
Result GetFrozenAddressCount(sf::Out<u64> out_count);
|
||||
Result GetFrozenAddresses(const sf::OutArray<FrozenAddressEntry> &addresses, sf::Out<u64> out_count, u64 offset);
|
||||
@@ -86,6 +96,8 @@ namespace ams::dmnt::cheat {
|
||||
MAKE_SERVICE_COMMAND_META(ReadCheatProcessMemory),
|
||||
MAKE_SERVICE_COMMAND_META(WriteCheatProcessMemory),
|
||||
MAKE_SERVICE_COMMAND_META(QueryCheatProcessMemory),
|
||||
MAKE_SERVICE_COMMAND_META(BreakCheatProcess),
|
||||
MAKE_SERVICE_COMMAND_META(ContinueCheatProcess),
|
||||
|
||||
MAKE_SERVICE_COMMAND_META(GetCheatCount),
|
||||
MAKE_SERVICE_COMMAND_META(GetCheats),
|
||||
@@ -93,6 +105,9 @@ namespace ams::dmnt::cheat {
|
||||
MAKE_SERVICE_COMMAND_META(ToggleCheat),
|
||||
MAKE_SERVICE_COMMAND_META(AddCheat),
|
||||
MAKE_SERVICE_COMMAND_META(RemoveCheat),
|
||||
MAKE_SERVICE_COMMAND_META(ReadStaticRegister),
|
||||
MAKE_SERVICE_COMMAND_META(WriteStaticRegister),
|
||||
MAKE_SERVICE_COMMAND_META(ResetStaticRegisters),
|
||||
|
||||
MAKE_SERVICE_COMMAND_META(GetFrozenAddressCount),
|
||||
MAKE_SERVICE_COMMAND_META(GetFrozenAddresses),
|
||||
|
||||
@@ -34,6 +34,7 @@ namespace ams::dmnt::cheat::impl {
|
||||
static constexpr s32 DebugEventsThreadPriority = -1;
|
||||
private:
|
||||
os::Mutex cheat_lock;
|
||||
os::Event unsafe_break_event;
|
||||
os::Event debug_events_event; /* Autoclear. */
|
||||
os::ThreadType detect_thread, debug_events_thread;
|
||||
os::SystemEvent cheat_process_event;
|
||||
@@ -41,6 +42,7 @@ namespace ams::dmnt::cheat::impl {
|
||||
CheatProcessMetadata cheat_process_metadata = {};
|
||||
|
||||
os::ThreadType vm_thread;
|
||||
bool broken_unsafe = false;
|
||||
bool needs_reload_vm = false;
|
||||
CheatVirtualMachine cheat_vm;
|
||||
|
||||
@@ -88,6 +90,8 @@ namespace ams::dmnt::cheat::impl {
|
||||
for (size_t i = 0; i < MaxCheatCount; i++) {
|
||||
this->ResetCheatEntry(i);
|
||||
}
|
||||
|
||||
this->cheat_vm.ResetStaticRegisters();
|
||||
}
|
||||
|
||||
CheatEntry *GetCheatEntryById(size_t i) {
|
||||
@@ -122,6 +126,10 @@ namespace ams::dmnt::cheat::impl {
|
||||
|
||||
void CloseActiveCheatProcess() {
|
||||
if (this->cheat_process_debug_handle != svc::InvalidHandle) {
|
||||
/* We don't need to do any unsafe brekaing. */
|
||||
this->broken_unsafe = false;
|
||||
this->unsafe_break_event.Signal();
|
||||
|
||||
/* Knock out the debug events thread. */
|
||||
os::CancelThreadSynchronization(std::addressof(this->debug_events_thread));
|
||||
|
||||
@@ -185,7 +193,7 @@ namespace ams::dmnt::cheat::impl {
|
||||
}
|
||||
|
||||
public:
|
||||
CheatProcessManager() : cheat_lock(false), debug_events_event(os::EventClearMode_AutoClear), cheat_process_event(os::EventClearMode_AutoClear, true) {
|
||||
CheatProcessManager() : cheat_lock(false), unsafe_break_event(os::EventClearMode_ManualClear), debug_events_event(os::EventClearMode_AutoClear), cheat_process_event(os::EventClearMode_AutoClear, true) {
|
||||
/* Learn whether we should enable cheats by default. */
|
||||
{
|
||||
u8 en = 0;
|
||||
@@ -257,6 +265,19 @@ namespace ams::dmnt::cheat::impl {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result BreakCheatProcessUnsafe() {
|
||||
this->broken_unsafe = true;
|
||||
this->unsafe_break_event.Clear();
|
||||
return svcBreakDebugProcess(this->GetCheatProcessHandle());
|
||||
}
|
||||
|
||||
Result ContinueCheatProcessUnsafe() {
|
||||
this->broken_unsafe = false;
|
||||
this->unsafe_break_event.Signal();
|
||||
dmnt::cheat::impl::ContinueCheatProcess(this->GetCheatProcessHandle());
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result GetCheatProcessMappingCount(u64 *out_count) {
|
||||
std::scoped_lock lk(this->cheat_lock);
|
||||
|
||||
@@ -335,6 +356,22 @@ namespace ams::dmnt::cheat::impl {
|
||||
return svcQueryDebugProcessMemory(mapping, &tmp, this->GetCheatProcessHandle(), address);
|
||||
}
|
||||
|
||||
Result BreakCheatProcess() {
|
||||
std::scoped_lock lk(this->cheat_lock);
|
||||
|
||||
R_TRY(this->EnsureCheatProcess());
|
||||
|
||||
return this->BreakCheatProcessUnsafe();
|
||||
}
|
||||
|
||||
Result ContinueCheatProcess() {
|
||||
std::scoped_lock lk(this->cheat_lock);
|
||||
|
||||
R_TRY(this->EnsureCheatProcess());
|
||||
|
||||
return this->ContinueCheatProcessUnsafe();
|
||||
}
|
||||
|
||||
Result GetCheatCount(u64 *out_count) {
|
||||
std::scoped_lock lk(this->cheat_lock);
|
||||
|
||||
@@ -436,6 +473,35 @@ namespace ams::dmnt::cheat::impl {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result ReadStaticRegister(u64 *out, size_t which) {
|
||||
std::scoped_lock lk(this->cheat_lock);
|
||||
|
||||
R_TRY(this->EnsureCheatProcess());
|
||||
R_UNLESS(which < CheatVirtualMachine::NumStaticRegisters, ResultCheatInvalid());
|
||||
|
||||
*out = this->cheat_vm.GetStaticRegister(which);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result WriteStaticRegister(size_t which, u64 value) {
|
||||
std::scoped_lock lk(this->cheat_lock);
|
||||
|
||||
R_TRY(this->EnsureCheatProcess());
|
||||
R_UNLESS(which < CheatVirtualMachine::NumStaticRegisters, ResultCheatInvalid());
|
||||
|
||||
this->cheat_vm.SetStaticRegister(which, value);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result ResetStaticRegisters() {
|
||||
std::scoped_lock lk(this->cheat_lock);
|
||||
|
||||
R_TRY(this->EnsureCheatProcess());
|
||||
|
||||
this->cheat_vm.ResetStaticRegisters();
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result GetFrozenAddressCount(u64 *out_count) {
|
||||
std::scoped_lock lk(this->cheat_lock);
|
||||
|
||||
@@ -533,7 +599,20 @@ namespace ams::dmnt::cheat::impl {
|
||||
this_ptr->debug_events_event.Wait();
|
||||
while (true) {
|
||||
while (R_SUCCEEDED(svcWaitSynchronizationSingle(this_ptr->GetCheatProcessHandle(), std::numeric_limits<u64>::max()))) {
|
||||
std::scoped_lock lk(this_ptr->cheat_lock);
|
||||
this_ptr->cheat_lock.Lock();
|
||||
ON_SCOPE_EXIT { this_ptr->cheat_lock.Unlock(); };
|
||||
|
||||
/* If we did an unsafe break, wait until we're not broken. */
|
||||
if (this_ptr->broken_unsafe) {
|
||||
this_ptr->cheat_lock.Unlock();
|
||||
this_ptr->unsafe_break_event.Wait();
|
||||
this_ptr->cheat_lock.Lock();
|
||||
if (this_ptr->GetCheatProcessHandle() != svc::InvalidHandle) {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle any pending debug events. */
|
||||
if (this_ptr->HasActiveCheatProcess()) {
|
||||
@@ -680,6 +759,10 @@ namespace ams::dmnt::cheat::impl {
|
||||
/* Cancel process guard. */
|
||||
proc_guard.Cancel();
|
||||
|
||||
/* Reset broken state. */
|
||||
this->broken_unsafe = false;
|
||||
this->unsafe_break_event.Signal();
|
||||
|
||||
/* If new process, start the process. */
|
||||
if (on_process_launch) {
|
||||
this->StartProcess(this->cheat_process_metadata.process_id);
|
||||
@@ -1021,6 +1104,14 @@ namespace ams::dmnt::cheat::impl {
|
||||
return GetReference(g_cheat_process_manager).WriteCheatProcessMemoryUnsafe(process_addr, data, size);
|
||||
}
|
||||
|
||||
Result BreakCheatProcessUnsafe() {
|
||||
return GetReference(g_cheat_process_manager).BreakCheatProcessUnsafe();
|
||||
}
|
||||
|
||||
Result ContinueCheatProcessUnsafe() {
|
||||
return GetReference(g_cheat_process_manager).ContinueCheatProcessUnsafe();
|
||||
}
|
||||
|
||||
Result GetCheatProcessMappingCount(u64 *out_count) {
|
||||
return GetReference(g_cheat_process_manager).GetCheatProcessMappingCount(out_count);
|
||||
}
|
||||
@@ -1041,6 +1132,14 @@ namespace ams::dmnt::cheat::impl {
|
||||
return GetReference(g_cheat_process_manager).QueryCheatProcessMemory(mapping, address);
|
||||
}
|
||||
|
||||
Result BreakCheatProcess() {
|
||||
return GetReference(g_cheat_process_manager).BreakCheatProcess();
|
||||
}
|
||||
|
||||
Result ContinueCheatProcess() {
|
||||
return GetReference(g_cheat_process_manager).ContinueCheatProcess();
|
||||
}
|
||||
|
||||
Result GetCheatCount(u64 *out_count) {
|
||||
return GetReference(g_cheat_process_manager).GetCheatCount(out_count);
|
||||
}
|
||||
@@ -1065,6 +1164,18 @@ namespace ams::dmnt::cheat::impl {
|
||||
return GetReference(g_cheat_process_manager).RemoveCheat(cheat_id);
|
||||
}
|
||||
|
||||
Result ReadStaticRegister(u64 *out, size_t which) {
|
||||
return GetReference(g_cheat_process_manager).ReadStaticRegister(out, which);
|
||||
}
|
||||
|
||||
Result WriteStaticRegister(size_t which, u64 value) {
|
||||
return GetReference(g_cheat_process_manager).WriteStaticRegister(which, value);
|
||||
}
|
||||
|
||||
Result ResetStaticRegisters() {
|
||||
return GetReference(g_cheat_process_manager).ResetStaticRegisters();
|
||||
}
|
||||
|
||||
Result GetFrozenAddressCount(u64 *out_count) {
|
||||
return GetReference(g_cheat_process_manager).GetFrozenAddressCount(out_count);
|
||||
}
|
||||
|
||||
@@ -28,11 +28,16 @@ namespace ams::dmnt::cheat::impl {
|
||||
Result ReadCheatProcessMemoryUnsafe(u64 process_addr, void *out_data, size_t size);
|
||||
Result WriteCheatProcessMemoryUnsafe(u64 process_addr, void *data, size_t size);
|
||||
|
||||
Result BreakCheatProcessUnsafe();
|
||||
Result ContinueCheatProcessUnsafe();
|
||||
|
||||
Result GetCheatProcessMappingCount(u64 *out_count);
|
||||
Result GetCheatProcessMappings(MemoryInfo *mappings, size_t max_count, u64 *out_count, u64 offset);
|
||||
Result ReadCheatProcessMemory(u64 proc_addr, void *out_data, size_t size);
|
||||
Result WriteCheatProcessMemory(u64 proc_addr, const void *data, size_t size);
|
||||
Result QueryCheatProcessMemory(MemoryInfo *mapping, u64 address);
|
||||
Result BreakCheatProcess();
|
||||
Result ContinueCheatProcess();
|
||||
|
||||
Result GetCheatCount(u64 *out_count);
|
||||
Result GetCheats(CheatEntry *cheats, size_t max_count, u64 *out_count, u64 offset);
|
||||
@@ -40,6 +45,9 @@ namespace ams::dmnt::cheat::impl {
|
||||
Result ToggleCheat(u32 cheat_id);
|
||||
Result AddCheat(u32 *out_id, const CheatDefinition &def, bool enabled);
|
||||
Result RemoveCheat(u32 cheat_id);
|
||||
Result ReadStaticRegister(u64 *out, size_t which);
|
||||
Result WriteStaticRegister(size_t which, u64 value);
|
||||
Result ResetStaticRegisters();
|
||||
|
||||
Result GetFrozenAddressCount(u64 *out_count);
|
||||
Result GetFrozenAddresses(FrozenAddressEntry *frz_addrs, size_t max_count, u64 *out_count, u64 offset);
|
||||
|
||||
@@ -237,6 +237,22 @@ namespace ams::dmnt::cheat::impl {
|
||||
this->LogToDebugFile("Act[%02x]: %d\n", i, opcode->save_restore_regmask.should_operate[i]);
|
||||
}
|
||||
break;
|
||||
case CheatVmOpcodeType_ReadWriteStaticRegister:
|
||||
this->LogToDebugFile("Opcode: Read/Write Static Register\n");
|
||||
if (opcode->rw_static_reg.static_idx < NumReadableStaticRegisters) {
|
||||
this->LogToDebugFile("Op Type: ReadStaticRegister\n");
|
||||
} else {
|
||||
this->LogToDebugFile("Op Type: WriteStaticRegister\n");
|
||||
}
|
||||
this->LogToDebugFile("Reg Idx: %x\n", opcode->rw_static_reg.idx);
|
||||
this->LogToDebugFile("Stc Idx: %x\n", opcode->rw_static_reg.static_idx);
|
||||
break;
|
||||
case CheatVmOpcodeType_BreakProcess:
|
||||
this->LogToDebugFile("Opcode: Break Cheat Process\n");
|
||||
break;
|
||||
case CheatVmOpcodeType_ContinueProcess:
|
||||
this->LogToDebugFile("Opcode: Continue Cheat Process\n");
|
||||
break;
|
||||
case CheatVmOpcodeType_DebugLog:
|
||||
this->LogToDebugFile("Opcode: Debug Log\n");
|
||||
this->LogToDebugFile("Bit Width: %x\n", opcode->debug_log.bit_width);
|
||||
@@ -570,6 +586,30 @@ namespace ams::dmnt::cheat::impl {
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CheatVmOpcodeType_ReadWriteStaticRegister:
|
||||
{
|
||||
/* C3000XXx */
|
||||
/* C3 = opcode 0xC3. */
|
||||
/* XX = static register index. */
|
||||
/* x = register index. */
|
||||
opcode.rw_static_reg.static_idx = ((first_dword >> 4) & 0xFF);
|
||||
opcode.rw_static_reg.idx = (first_dword & 0xF);
|
||||
}
|
||||
break;
|
||||
case CheatVmOpcodeType_BreakProcess:
|
||||
{
|
||||
/* FF0????? */
|
||||
/* FF0 = opcode 0xFF0 */
|
||||
/* Breaks the current process. */
|
||||
}
|
||||
break;
|
||||
case CheatVmOpcodeType_ContinueProcess:
|
||||
{
|
||||
/* FF1????? */
|
||||
/* FF1 = opcode 0xFF1 */
|
||||
/* Continues the current process. */
|
||||
}
|
||||
break;
|
||||
case CheatVmOpcodeType_DebugLog:
|
||||
{
|
||||
/* FFFTIX## */
|
||||
@@ -1174,6 +1214,21 @@ namespace ams::dmnt::cheat::impl {
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CheatVmOpcodeType_ReadWriteStaticRegister:
|
||||
if (cur_opcode.rw_static_reg.static_idx < NumReadableStaticRegisters) {
|
||||
/* Load a register with a static register. */
|
||||
this->registers[cur_opcode.rw_static_reg.idx] = this->static_registers[cur_opcode.rw_static_reg.static_idx];
|
||||
} else {
|
||||
/* Store a register to a static register. */
|
||||
this->static_registers[cur_opcode.rw_static_reg.static_idx] = this->registers[cur_opcode.rw_static_reg.idx];
|
||||
}
|
||||
break;
|
||||
case CheatVmOpcodeType_BreakProcess:
|
||||
dmnt::cheat::impl::BreakCheatProcessUnsafe();
|
||||
break;
|
||||
case CheatVmOpcodeType_ContinueProcess:
|
||||
dmnt::cheat::impl::ContinueCheatProcessUnsafe();
|
||||
break;
|
||||
case CheatVmOpcodeType_DebugLog:
|
||||
{
|
||||
/* Read value from memory. */
|
||||
|
||||
@@ -42,12 +42,15 @@ namespace ams::dmnt::cheat::impl {
|
||||
CheatVmOpcodeType_BeginRegisterConditionalBlock = 0xC0,
|
||||
CheatVmOpcodeType_SaveRestoreRegister = 0xC1,
|
||||
CheatVmOpcodeType_SaveRestoreRegisterMask = 0xC2,
|
||||
CheatVmOpcodeType_ReadWriteStaticRegister = 0xC3,
|
||||
|
||||
/* This is a meta entry, and not a real opcode. */
|
||||
/* This is to facilitate multi-nybble instruction decoding. */
|
||||
CheatVmOpcodeType_DoubleExtendedWidth = 0xF0,
|
||||
|
||||
/* Double-extended width opcodes. */
|
||||
CheatVmOpcodeType_BreakProcess = 0xFF0,
|
||||
CheatVmOpcodeType_ContinueProcess = 0xFF1,
|
||||
CheatVmOpcodeType_DebugLog = 0xFFF,
|
||||
};
|
||||
|
||||
@@ -223,6 +226,11 @@ namespace ams::dmnt::cheat::impl {
|
||||
bool should_operate[0x10];
|
||||
};
|
||||
|
||||
struct ReadWriteStaticRegisterOpcode {
|
||||
u32 static_idx;
|
||||
u32 idx;
|
||||
};
|
||||
|
||||
struct DebugLogOpcode {
|
||||
u32 bit_width;
|
||||
u32 log_id;
|
||||
@@ -252,6 +260,7 @@ namespace ams::dmnt::cheat::impl {
|
||||
BeginRegisterConditionalOpcode begin_reg_cond;
|
||||
SaveRestoreRegisterOpcode save_restore_reg;
|
||||
SaveRestoreRegisterMaskOpcode save_restore_regmask;
|
||||
ReadWriteStaticRegisterOpcode rw_static_reg;
|
||||
DebugLogOpcode debug_log;
|
||||
};
|
||||
};
|
||||
@@ -260,6 +269,9 @@ namespace ams::dmnt::cheat::impl {
|
||||
public:
|
||||
constexpr static size_t MaximumProgramOpcodeCount = 0x400;
|
||||
constexpr static size_t NumRegisters = 0x10;
|
||||
constexpr static size_t NumReadableStaticRegisters = 0x80;
|
||||
constexpr static size_t NumWritableStaticRegisters = 0x80;
|
||||
constexpr static size_t NumStaticRegisters = NumReadableStaticRegisters + NumWritableStaticRegisters;
|
||||
private:
|
||||
size_t num_opcodes = 0;
|
||||
size_t instruction_ptr = 0;
|
||||
@@ -268,6 +280,7 @@ namespace ams::dmnt::cheat::impl {
|
||||
u32 program[MaximumProgramOpcodeCount] = {0};
|
||||
u64 registers[NumRegisters] = {0};
|
||||
u64 saved_values[NumRegisters] = {0};
|
||||
u64 static_registers[NumStaticRegisters] = {0};
|
||||
size_t loop_tops[NumRegisters] = {0};
|
||||
private:
|
||||
bool DecodeNextOpcode(CheatVmOpcode *out);
|
||||
@@ -294,6 +307,18 @@ namespace ams::dmnt::cheat::impl {
|
||||
|
||||
bool LoadProgram(const CheatEntry *cheats, size_t num_cheats);
|
||||
void Execute(const CheatProcessMetadata *metadata);
|
||||
|
||||
u64 GetStaticRegister(size_t which) const {
|
||||
return this->static_registers[which];
|
||||
}
|
||||
|
||||
void SetStaticRegister(size_t which, u64 value) {
|
||||
this->static_registers[which] = value;
|
||||
}
|
||||
|
||||
void ResetStaticRegisters() {
|
||||
std::memset(this->static_registers, 0, sizeof(this->static_registers));
|
||||
}
|
||||
#ifdef DMNT_CHEAT_VM_DEBUG_LOG
|
||||
private:
|
||||
fs::FileHandle debug_log_file;
|
||||
|
||||
Reference in New Issue
Block a user