dmnt: refactor to use sts:: namespace.
This commit is contained in:
169
stratosphere/dmnt/source/cheat/dmnt_cheat_service.cpp
Normal file
169
stratosphere/dmnt/source/cheat/dmnt_cheat_service.cpp
Normal file
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2019 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include "dmnt_cheat_service.hpp"
|
||||
#include "impl/dmnt_cheat_api.hpp"
|
||||
|
||||
namespace sts::dmnt::cheat {
|
||||
|
||||
/* ========================================================================================= */
|
||||
/* ==================================== Meta Commands ==================================== */
|
||||
/* ========================================================================================= */
|
||||
|
||||
void CheatService::HasCheatProcess(Out<bool> out) {
|
||||
out.SetValue(dmnt::cheat::impl::GetHasActiveCheatProcess());
|
||||
}
|
||||
|
||||
void CheatService::GetCheatProcessEvent(Out<CopiedHandle> out_event) {
|
||||
out_event.SetValue(dmnt::cheat::impl::GetCheatProcessEventHandle());
|
||||
}
|
||||
|
||||
Result CheatService::GetCheatProcessMetadata(Out<CheatProcessMetadata> out_metadata) {
|
||||
return dmnt::cheat::impl::GetCheatProcessMetadata(out_metadata.GetPointer());
|
||||
}
|
||||
|
||||
Result CheatService::ForceOpenCheatProcess() {
|
||||
if (R_FAILED(dmnt::cheat::impl::ForceOpenCheatProcess())) {
|
||||
return ResultDmntCheatNotAttached;
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
/* ========================================================================================= */
|
||||
/* =================================== Memory Commands =================================== */
|
||||
/* ========================================================================================= */
|
||||
|
||||
Result CheatService::GetCheatProcessMappingCount(Out<u64> out_count) {
|
||||
return dmnt::cheat::impl::GetCheatProcessMappingCount(out_count.GetPointer());
|
||||
}
|
||||
|
||||
Result CheatService::GetCheatProcessMappings(OutBuffer<MemoryInfo> mappings, Out<u64> out_count, u64 offset) {
|
||||
if (mappings.buffer == nullptr) {
|
||||
return ResultDmntCheatNullBuffer;
|
||||
}
|
||||
|
||||
return dmnt::cheat::impl::GetCheatProcessMappings(mappings.buffer, mappings.num_elements, out_count.GetPointer(), offset);
|
||||
}
|
||||
|
||||
Result CheatService::ReadCheatProcessMemory(OutBuffer<u8> buffer, u64 address, u64 out_size) {
|
||||
if (buffer.buffer == nullptr) {
|
||||
return ResultDmntCheatNullBuffer;
|
||||
}
|
||||
|
||||
return dmnt::cheat::impl::ReadCheatProcessMemory(address, buffer.buffer, std::min(out_size, buffer.num_elements));
|
||||
}
|
||||
|
||||
Result CheatService::WriteCheatProcessMemory(InBuffer<u8> buffer, u64 address, u64 in_size) {
|
||||
if (buffer.buffer == nullptr) {
|
||||
return ResultDmntCheatNullBuffer;
|
||||
}
|
||||
|
||||
return dmnt::cheat::impl::WriteCheatProcessMemory(address, buffer.buffer, std::min(in_size, buffer.num_elements));
|
||||
}
|
||||
|
||||
Result CheatService::QueryCheatProcessMemory(Out<MemoryInfo> mapping, u64 address) {
|
||||
return dmnt::cheat::impl::QueryCheatProcessMemory(mapping.GetPointer(), address);
|
||||
}
|
||||
|
||||
/* ========================================================================================= */
|
||||
/* =================================== Cheat Commands ==================================== */
|
||||
/* ========================================================================================= */
|
||||
|
||||
Result CheatService::GetCheatCount(Out<u64> out_count) {
|
||||
return dmnt::cheat::impl::GetCheatCount(out_count.GetPointer());
|
||||
}
|
||||
|
||||
Result CheatService::GetCheats(OutBuffer<CheatEntry> cheats, Out<u64> out_count, u64 offset) {
|
||||
if (cheats.buffer == nullptr) {
|
||||
return ResultDmntCheatNullBuffer;
|
||||
}
|
||||
|
||||
return dmnt::cheat::impl::GetCheats(cheats.buffer, cheats.num_elements, out_count.GetPointer(), offset);
|
||||
}
|
||||
|
||||
Result CheatService::GetCheatById(OutBuffer<CheatEntry> cheat, u32 cheat_id) {
|
||||
if (cheat.buffer == nullptr) {
|
||||
return ResultDmntCheatNullBuffer;
|
||||
}
|
||||
|
||||
if (cheat.num_elements < 1) {
|
||||
return ResultDmntCheatInvalidBuffer;
|
||||
}
|
||||
|
||||
return dmnt::cheat::impl::GetCheatById(cheat.buffer, cheat_id);
|
||||
}
|
||||
|
||||
Result CheatService::ToggleCheat(u32 cheat_id) {
|
||||
return dmnt::cheat::impl::ToggleCheat(cheat_id);
|
||||
}
|
||||
|
||||
Result CheatService::AddCheat(InBuffer<CheatDefinition> cheat, Out<u32> out_cheat_id, bool enabled) {
|
||||
if (cheat.buffer == nullptr) {
|
||||
return ResultDmntCheatNullBuffer;
|
||||
}
|
||||
|
||||
if (cheat.num_elements < 1) {
|
||||
return ResultDmntCheatInvalidBuffer;
|
||||
}
|
||||
|
||||
return dmnt::cheat::impl::AddCheat(out_cheat_id.GetPointer(), cheat.buffer, enabled);
|
||||
}
|
||||
|
||||
Result CheatService::RemoveCheat(u32 cheat_id) {
|
||||
return dmnt::cheat::impl::RemoveCheat(cheat_id);
|
||||
}
|
||||
|
||||
/* ========================================================================================= */
|
||||
/* =================================== Address Commands ================================== */
|
||||
/* ========================================================================================= */
|
||||
|
||||
Result CheatService::GetFrozenAddressCount(Out<u64> out_count) {
|
||||
return dmnt::cheat::impl::GetFrozenAddressCount(out_count.GetPointer());
|
||||
}
|
||||
|
||||
Result CheatService::GetFrozenAddresses(OutBuffer<FrozenAddressEntry> frz_addrs, Out<u64> out_count, u64 offset) {
|
||||
if (frz_addrs.buffer == nullptr) {
|
||||
return ResultDmntCheatNullBuffer;
|
||||
}
|
||||
|
||||
return dmnt::cheat::impl::GetFrozenAddresses(frz_addrs.buffer, frz_addrs.num_elements, out_count.GetPointer(), offset);
|
||||
}
|
||||
|
||||
Result CheatService::GetFrozenAddress(Out<FrozenAddressEntry> entry, u64 address) {
|
||||
return dmnt::cheat::impl::GetFrozenAddress(entry.GetPointer(), address);
|
||||
}
|
||||
|
||||
Result CheatService::EnableFrozenAddress(Out<u64> out_value, u64 address, u64 width) {
|
||||
switch (width) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 4:
|
||||
case 8:
|
||||
break;
|
||||
default:
|
||||
return ResultDmntCheatInvalidFreezeWidth;
|
||||
}
|
||||
|
||||
return dmnt::cheat::impl::EnableFrozenAddress(out_value.GetPointer(), address, width);
|
||||
}
|
||||
|
||||
Result CheatService::DisableFrozenAddress(u64 address) {
|
||||
return dmnt::cheat::impl::DisableFrozenAddress(address);
|
||||
}
|
||||
|
||||
}
|
||||
108
stratosphere/dmnt/source/cheat/dmnt_cheat_service.hpp
Normal file
108
stratosphere/dmnt/source/cheat/dmnt_cheat_service.hpp
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2019 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include <stratosphere/dmnt.hpp>
|
||||
|
||||
namespace sts::dmnt::cheat {
|
||||
|
||||
class CheatService final : public IServiceObject {
|
||||
private:
|
||||
enum class CommandId {
|
||||
/* Meta */
|
||||
HasCheatProcess = 65000,
|
||||
GetCheatProcessEvent = 65001,
|
||||
GetCheatProcessMetadata = 65002,
|
||||
ForceOpenCheatProcess = 65003,
|
||||
|
||||
/* Interact with Memory */
|
||||
GetCheatProcessMappingCount = 65100,
|
||||
GetCheatProcessMappings = 65101,
|
||||
ReadCheatProcessMemory = 65102,
|
||||
WriteCheatProcessMemory = 65103,
|
||||
QueryCheatProcessMemory = 65104,
|
||||
|
||||
/* Interact with Cheats */
|
||||
GetCheatCount = 65200,
|
||||
GetCheats = 65201,
|
||||
GetCheatById = 65202,
|
||||
ToggleCheat = 65203,
|
||||
AddCheat = 65204,
|
||||
RemoveCheat = 65205,
|
||||
|
||||
/* Interact with Frozen Addresses */
|
||||
GetFrozenAddressCount = 65300,
|
||||
GetFrozenAddresses = 65301,
|
||||
GetFrozenAddress = 65302,
|
||||
EnableFrozenAddress = 65303,
|
||||
DisableFrozenAddress = 65304,
|
||||
};
|
||||
private:
|
||||
void HasCheatProcess(Out<bool> out);
|
||||
void GetCheatProcessEvent(Out<CopiedHandle> out_event);
|
||||
Result GetCheatProcessMetadata(Out<CheatProcessMetadata> out_metadata);
|
||||
Result ForceOpenCheatProcess();
|
||||
|
||||
Result GetCheatProcessMappingCount(Out<u64> out_count);
|
||||
Result GetCheatProcessMappings(OutBuffer<MemoryInfo> mappings, Out<u64> out_count, u64 offset);
|
||||
Result ReadCheatProcessMemory(OutBuffer<u8> buffer, u64 address, u64 out_size);
|
||||
Result WriteCheatProcessMemory(InBuffer<u8> buffer, u64 address, u64 in_size);
|
||||
Result QueryCheatProcessMemory(Out<MemoryInfo> mapping, u64 address);
|
||||
|
||||
Result GetCheatCount(Out<u64> out_count);
|
||||
Result GetCheats(OutBuffer<CheatEntry> cheats, Out<u64> out_count, u64 offset);
|
||||
Result GetCheatById(OutBuffer<CheatEntry> cheat, u32 cheat_id);
|
||||
Result ToggleCheat(u32 cheat_id);
|
||||
Result AddCheat(InBuffer<CheatDefinition> cheat, Out<u32> out_cheat_id, bool enabled);
|
||||
Result RemoveCheat(u32 cheat_id);
|
||||
|
||||
Result GetFrozenAddressCount(Out<u64> out_count);
|
||||
Result GetFrozenAddresses(OutBuffer<FrozenAddressEntry> addresses, Out<u64> out_count, u64 offset);
|
||||
Result GetFrozenAddress(Out<FrozenAddressEntry> entry, u64 address);
|
||||
Result EnableFrozenAddress(Out<u64> out_value, u64 address, u64 width);
|
||||
Result DisableFrozenAddress(u64 address);
|
||||
|
||||
public:
|
||||
DEFINE_SERVICE_DISPATCH_TABLE {
|
||||
MAKE_SERVICE_COMMAND_META(CheatService, HasCheatProcess),
|
||||
MAKE_SERVICE_COMMAND_META(CheatService, GetCheatProcessEvent),
|
||||
MAKE_SERVICE_COMMAND_META(CheatService, GetCheatProcessMetadata),
|
||||
MAKE_SERVICE_COMMAND_META(CheatService, ForceOpenCheatProcess),
|
||||
|
||||
MAKE_SERVICE_COMMAND_META(CheatService, GetCheatProcessMappingCount),
|
||||
MAKE_SERVICE_COMMAND_META(CheatService, GetCheatProcessMappings),
|
||||
MAKE_SERVICE_COMMAND_META(CheatService, ReadCheatProcessMemory),
|
||||
MAKE_SERVICE_COMMAND_META(CheatService, WriteCheatProcessMemory),
|
||||
MAKE_SERVICE_COMMAND_META(CheatService, QueryCheatProcessMemory),
|
||||
|
||||
MAKE_SERVICE_COMMAND_META(CheatService, GetCheatCount),
|
||||
MAKE_SERVICE_COMMAND_META(CheatService, GetCheats),
|
||||
MAKE_SERVICE_COMMAND_META(CheatService, GetCheatById),
|
||||
MAKE_SERVICE_COMMAND_META(CheatService, ToggleCheat),
|
||||
MAKE_SERVICE_COMMAND_META(CheatService, AddCheat),
|
||||
MAKE_SERVICE_COMMAND_META(CheatService, RemoveCheat),
|
||||
|
||||
MAKE_SERVICE_COMMAND_META(CheatService, GetFrozenAddressCount),
|
||||
MAKE_SERVICE_COMMAND_META(CheatService, GetFrozenAddresses),
|
||||
MAKE_SERVICE_COMMAND_META(CheatService, GetFrozenAddress),
|
||||
MAKE_SERVICE_COMMAND_META(CheatService, EnableFrozenAddress),
|
||||
MAKE_SERVICE_COMMAND_META(CheatService, DisableFrozenAddress),
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
1084
stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp
Normal file
1084
stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp
Normal file
File diff suppressed because it is too large
Load Diff
50
stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.hpp
Normal file
50
stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.hpp
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2019 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include <stratosphere/dmnt.hpp>
|
||||
|
||||
namespace sts::dmnt::cheat::impl {
|
||||
|
||||
bool GetHasActiveCheatProcess();
|
||||
Handle GetCheatProcessEventHandle();
|
||||
Result GetCheatProcessMetadata(CheatProcessMetadata *out);
|
||||
Result ForceOpenCheatProcess();
|
||||
|
||||
Result ReadCheatProcessMemoryUnsafe(u64 process_addr, void *out_data, size_t size);
|
||||
Result WriteCheatProcessMemoryUnsafe(u64 process_addr, void *data, size_t size);
|
||||
|
||||
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 GetCheatCount(u64 *out_count);
|
||||
Result GetCheats(CheatEntry *cheats, size_t max_count, u64 *out_count, u64 offset);
|
||||
Result GetCheatById(CheatEntry *out_cheat, u32 cheat_id);
|
||||
Result ToggleCheat(u32 cheat_id);
|
||||
Result AddCheat(u32 *out_id, const CheatDefinition *def, bool enabled);
|
||||
Result RemoveCheat(u32 cheat_id);
|
||||
|
||||
Result GetFrozenAddressCount(u64 *out_count);
|
||||
Result GetFrozenAddresses(FrozenAddressEntry *frz_addrs, size_t max_count, u64 *out_count, u64 offset);
|
||||
Result GetFrozenAddress(FrozenAddressEntry *frz_addr, u64 address);
|
||||
Result EnableFrozenAddress(u64 *out_value, u64 address, u64 width);
|
||||
Result DisableFrozenAddress(u64 address);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2019 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "dmnt_cheat_debug_events_manager.hpp"
|
||||
|
||||
/* WORKAROUND: This design prevents a kernel deadlock from occurring on 6.0.0+ */
|
||||
|
||||
namespace sts::dmnt::cheat::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
class DebugEventsManager {
|
||||
public:
|
||||
static constexpr size_t NumCores = 4;
|
||||
private:
|
||||
HosMessageQueue message_queues[NumCores];
|
||||
HosThread threads[NumCores];
|
||||
HosSignal continued_signal;
|
||||
private:
|
||||
static void PerCoreThreadFunction(void *_this) {
|
||||
/* This thread will wait on the appropriate message queue. */
|
||||
DebugEventsManager *this_ptr = reinterpret_cast<DebugEventsManager *>(_this);
|
||||
const u32 current_core = svcGetCurrentProcessorNumber();
|
||||
while (true) {
|
||||
/* Receive handle. */
|
||||
Handle debug_handle = this_ptr->WaitReceiveHandle(current_core);
|
||||
|
||||
/* Continue events on the correct core. */
|
||||
R_ASSERT(this_ptr->ContinueDebugEvent(debug_handle));
|
||||
|
||||
/* Signal that we've continued. */
|
||||
this_ptr->SignalContinued();
|
||||
}
|
||||
}
|
||||
|
||||
u32 GetTargetCore(const svc::DebugEventInfo &dbg_event, Handle debug_handle) {
|
||||
/* If we don't need to continue on a specific core, use the system core. */
|
||||
u32 target_core = NumCores - 1;
|
||||
|
||||
/* Retrieve correct core for new thread event. */
|
||||
if (dbg_event.type == svc::DebugEventType::AttachThread) {
|
||||
u64 out64 = 0;
|
||||
u32 out32 = 0;
|
||||
R_ASSERT(svcGetDebugThreadParam(&out64, &out32, debug_handle, dbg_event.info.attach_thread.thread_id, DebugThreadParam_CurrentCore));
|
||||
target_core = out32;
|
||||
}
|
||||
|
||||
return target_core;
|
||||
}
|
||||
|
||||
void SendHandle(const svc::DebugEventInfo &dbg_event, Handle debug_handle) {
|
||||
this->message_queues[GetTargetCore(dbg_event, debug_handle)].Send(static_cast<uintptr_t>(debug_handle));
|
||||
}
|
||||
|
||||
Handle WaitReceiveHandle(u32 core_id) {
|
||||
uintptr_t x = 0;
|
||||
this->message_queues[core_id].Receive(&x);
|
||||
return static_cast<Handle>(x);
|
||||
}
|
||||
|
||||
Result ContinueDebugEvent(Handle debug_handle) {
|
||||
if (GetRuntimeFirmwareVersion() >= FirmwareVersion_300) {
|
||||
return svcContinueDebugEvent(debug_handle, 5, nullptr, 0);
|
||||
} else {
|
||||
return svcLegacyContinueDebugEvent(debug_handle, 5, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void WaitContinued() {
|
||||
this->continued_signal.Wait();
|
||||
this->continued_signal.Reset();
|
||||
}
|
||||
|
||||
void SignalContinued() {
|
||||
this->continued_signal.Signal();
|
||||
}
|
||||
|
||||
public:
|
||||
DebugEventsManager() : message_queues{HosMessageQueue(1), HosMessageQueue(1), HosMessageQueue(1), HosMessageQueue(1)} {
|
||||
for (size_t i = 0; i < NumCores; i++) {
|
||||
/* Create thread. */
|
||||
R_ASSERT(this->threads[i].Initialize(&DebugEventsManager::PerCoreThreadFunction, reinterpret_cast<void *>(this), 0x1000, 24, i));
|
||||
|
||||
/* Set core mask. */
|
||||
R_ASSERT(svcSetThreadCoreMask(this->threads[i].GetHandle(), i, (1u << i)));
|
||||
|
||||
/* Start thread. */
|
||||
R_ASSERT(this->threads[i].Start());
|
||||
}
|
||||
}
|
||||
|
||||
void ContinueCheatProcess(Handle cheat_dbg_hnd) {
|
||||
/* Loop getting all debug events. */
|
||||
svc::DebugEventInfo d;
|
||||
while (R_SUCCEEDED(svcGetDebugEvent(reinterpret_cast<u8 *>(&d), cheat_dbg_hnd))) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
/* Send handle to correct core, wait for continue to finish. */
|
||||
this->SendHandle(d, cheat_dbg_hnd);
|
||||
this->WaitContinued();
|
||||
}
|
||||
};
|
||||
|
||||
/* Manager global. */
|
||||
DebugEventsManager g_events_manager;
|
||||
|
||||
}
|
||||
|
||||
void ContinueCheatProcess(Handle cheat_dbg_hnd) {
|
||||
g_events_manager.ContinueCheatProcess(cheat_dbg_hnd);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2019 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace sts::dmnt::cheat::impl {
|
||||
|
||||
void ContinueCheatProcess(Handle cheat_dbg_hnd);
|
||||
|
||||
}
|
||||
1213
stratosphere/dmnt/source/cheat/impl/dmnt_cheat_vm.cpp
Normal file
1213
stratosphere/dmnt/source/cheat/impl/dmnt_cheat_vm.cpp
Normal file
File diff suppressed because it is too large
Load Diff
306
stratosphere/dmnt/source/cheat/impl/dmnt_cheat_vm.hpp
Normal file
306
stratosphere/dmnt/source/cheat/impl/dmnt_cheat_vm.hpp
Normal file
@@ -0,0 +1,306 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2019 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <stdarg.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include <stratosphere/dmnt.hpp>
|
||||
|
||||
namespace sts::dmnt::cheat::impl {
|
||||
|
||||
enum CheatVmOpcodeType : u32 {
|
||||
CheatVmOpcodeType_StoreStatic = 0,
|
||||
CheatVmOpcodeType_BeginConditionalBlock = 1,
|
||||
CheatVmOpcodeType_EndConditionalBlock = 2,
|
||||
CheatVmOpcodeType_ControlLoop = 3,
|
||||
CheatVmOpcodeType_LoadRegisterStatic = 4,
|
||||
CheatVmOpcodeType_LoadRegisterMemory = 5,
|
||||
CheatVmOpcodeType_StoreStaticToAddress = 6,
|
||||
CheatVmOpcodeType_PerformArithmeticStatic = 7,
|
||||
CheatVmOpcodeType_BeginKeypressConditionalBlock = 8,
|
||||
|
||||
/* These are not implemented by Gateway's VM. */
|
||||
CheatVmOpcodeType_PerformArithmeticRegister = 9,
|
||||
CheatVmOpcodeType_StoreRegisterToAddress = 10,
|
||||
CheatVmOpcodeType_Reserved11 = 11,
|
||||
|
||||
/* This is a meta entry, and not a real opcode. */
|
||||
/* This is to facilitate multi-nybble instruction decoding. */
|
||||
CheatVmOpcodeType_ExtendedWidth = 12,
|
||||
|
||||
/* Extended width opcodes. */
|
||||
CheatVmOpcodeType_BeginRegisterConditionalBlock = 0xC0,
|
||||
CheatVmOpcodeType_SaveRestoreRegister = 0xC1,
|
||||
CheatVmOpcodeType_SaveRestoreRegisterMask = 0xC2,
|
||||
|
||||
/* 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_DebugLog = 0xFFF,
|
||||
};
|
||||
|
||||
enum MemoryAccessType : u32 {
|
||||
MemoryAccessType_MainNso = 0,
|
||||
MemoryAccessType_Heap = 1,
|
||||
};
|
||||
|
||||
enum ConditionalComparisonType : u32 {
|
||||
ConditionalComparisonType_GT = 1,
|
||||
ConditionalComparisonType_GE = 2,
|
||||
ConditionalComparisonType_LT = 3,
|
||||
ConditionalComparisonType_LE = 4,
|
||||
ConditionalComparisonType_EQ = 5,
|
||||
ConditionalComparisonType_NE = 6,
|
||||
};
|
||||
|
||||
enum RegisterArithmeticType : u32 {
|
||||
RegisterArithmeticType_Addition = 0,
|
||||
RegisterArithmeticType_Subtraction = 1,
|
||||
RegisterArithmeticType_Multiplication = 2,
|
||||
RegisterArithmeticType_LeftShift = 3,
|
||||
RegisterArithmeticType_RightShift = 4,
|
||||
|
||||
/* These are not supported by Gateway's VM. */
|
||||
RegisterArithmeticType_LogicalAnd = 5,
|
||||
RegisterArithmeticType_LogicalOr = 6,
|
||||
RegisterArithmeticType_LogicalNot = 7,
|
||||
RegisterArithmeticType_LogicalXor = 8,
|
||||
|
||||
RegisterArithmeticType_None = 9,
|
||||
};
|
||||
|
||||
enum StoreRegisterOffsetType : u32 {
|
||||
StoreRegisterOffsetType_None = 0,
|
||||
StoreRegisterOffsetType_Reg = 1,
|
||||
StoreRegisterOffsetType_Imm = 2,
|
||||
StoreRegisterOffsetType_MemReg = 3,
|
||||
StoreRegisterOffsetType_MemImm = 4,
|
||||
StoreRegisterOffsetType_MemImmReg = 5,
|
||||
};
|
||||
|
||||
enum CompareRegisterValueType : u32 {
|
||||
CompareRegisterValueType_MemoryRelAddr = 0,
|
||||
CompareRegisterValueType_MemoryOfsReg = 1,
|
||||
CompareRegisterValueType_RegisterRelAddr = 2,
|
||||
CompareRegisterValueType_RegisterOfsReg = 3,
|
||||
CompareRegisterValueType_StaticValue = 4,
|
||||
CompareRegisterValueType_OtherRegister = 5,
|
||||
};
|
||||
|
||||
enum SaveRestoreRegisterOpType : u32 {
|
||||
SaveRestoreRegisterOpType_Restore = 0,
|
||||
SaveRestoreRegisterOpType_Save = 1,
|
||||
SaveRestoreRegisterOpType_ClearSaved = 2,
|
||||
SaveRestoreRegisterOpType_ClearRegs = 3,
|
||||
};
|
||||
|
||||
enum DebugLogValueType : u32 {
|
||||
DebugLogValueType_MemoryRelAddr = 0,
|
||||
DebugLogValueType_MemoryOfsReg = 1,
|
||||
DebugLogValueType_RegisterRelAddr = 2,
|
||||
DebugLogValueType_RegisterOfsReg = 3,
|
||||
DebugLogValueType_RegisterValue = 4,
|
||||
};
|
||||
|
||||
union VmInt {
|
||||
u8 bit8;
|
||||
u16 bit16;
|
||||
u32 bit32;
|
||||
u64 bit64;
|
||||
};
|
||||
|
||||
struct StoreStaticOpcode {
|
||||
u32 bit_width;
|
||||
MemoryAccessType mem_type;
|
||||
u32 offset_register;
|
||||
u64 rel_address;
|
||||
VmInt value;
|
||||
};
|
||||
|
||||
struct BeginConditionalOpcode {
|
||||
u32 bit_width;
|
||||
MemoryAccessType mem_type;
|
||||
ConditionalComparisonType cond_type;
|
||||
u64 rel_address;
|
||||
VmInt value;
|
||||
};
|
||||
|
||||
struct EndConditionalOpcode {};
|
||||
|
||||
struct ControlLoopOpcode {
|
||||
bool start_loop;
|
||||
u32 reg_index;
|
||||
u32 num_iters;
|
||||
};
|
||||
|
||||
struct LoadRegisterStaticOpcode {
|
||||
u32 reg_index;
|
||||
u64 value;
|
||||
};
|
||||
|
||||
struct LoadRegisterMemoryOpcode {
|
||||
u32 bit_width;
|
||||
MemoryAccessType mem_type;
|
||||
u32 reg_index;
|
||||
bool load_from_reg;
|
||||
u64 rel_address;
|
||||
};
|
||||
|
||||
struct StoreStaticToAddressOpcode {
|
||||
u32 bit_width;
|
||||
u32 reg_index;
|
||||
bool increment_reg;
|
||||
bool add_offset_reg;
|
||||
u32 offset_reg_index;
|
||||
u64 value;
|
||||
};
|
||||
|
||||
struct PerformArithmeticStaticOpcode {
|
||||
u32 bit_width;
|
||||
u32 reg_index;
|
||||
RegisterArithmeticType math_type;
|
||||
u32 value;
|
||||
};
|
||||
|
||||
struct BeginKeypressConditionalOpcode {
|
||||
u32 key_mask;
|
||||
};
|
||||
|
||||
struct PerformArithmeticRegisterOpcode {
|
||||
u32 bit_width;
|
||||
RegisterArithmeticType math_type;
|
||||
u32 dst_reg_index;
|
||||
u32 src_reg_1_index;
|
||||
u32 src_reg_2_index;
|
||||
bool has_immediate;
|
||||
VmInt value;
|
||||
};
|
||||
|
||||
struct StoreRegisterToAddressOpcode {
|
||||
u32 bit_width;
|
||||
u32 str_reg_index;
|
||||
u32 addr_reg_index;
|
||||
bool increment_reg;
|
||||
StoreRegisterOffsetType ofs_type;
|
||||
MemoryAccessType mem_type;
|
||||
u32 ofs_reg_index;
|
||||
u64 rel_address;
|
||||
};
|
||||
|
||||
struct BeginRegisterConditionalOpcode {
|
||||
u32 bit_width;
|
||||
ConditionalComparisonType cond_type;
|
||||
u32 val_reg_index;
|
||||
CompareRegisterValueType comp_type;
|
||||
MemoryAccessType mem_type;
|
||||
u32 addr_reg_index;
|
||||
u32 other_reg_index;
|
||||
u32 ofs_reg_index;
|
||||
u64 rel_address;
|
||||
VmInt value;
|
||||
};
|
||||
|
||||
struct SaveRestoreRegisterOpcode {
|
||||
u32 dst_index;
|
||||
u32 src_index;
|
||||
SaveRestoreRegisterOpType op_type;
|
||||
};
|
||||
|
||||
struct SaveRestoreRegisterMaskOpcode {
|
||||
SaveRestoreRegisterOpType op_type;
|
||||
bool should_operate[0x10];
|
||||
};
|
||||
|
||||
struct DebugLogOpcode {
|
||||
u32 bit_width;
|
||||
u32 log_id;
|
||||
DebugLogValueType val_type;
|
||||
MemoryAccessType mem_type;
|
||||
u32 addr_reg_index;
|
||||
u32 val_reg_index;
|
||||
u32 ofs_reg_index;
|
||||
u64 rel_address;
|
||||
};
|
||||
|
||||
struct CheatVmOpcode {
|
||||
CheatVmOpcodeType opcode;
|
||||
bool begin_conditional_block;
|
||||
union {
|
||||
StoreStaticOpcode store_static;
|
||||
BeginConditionalOpcode begin_cond;
|
||||
EndConditionalOpcode end_cond;
|
||||
ControlLoopOpcode ctrl_loop;
|
||||
LoadRegisterStaticOpcode ldr_static;
|
||||
LoadRegisterMemoryOpcode ldr_memory;
|
||||
StoreStaticToAddressOpcode str_static;
|
||||
PerformArithmeticStaticOpcode perform_math_static;
|
||||
BeginKeypressConditionalOpcode begin_keypress_cond;
|
||||
PerformArithmeticRegisterOpcode perform_math_reg;
|
||||
StoreRegisterToAddressOpcode str_register;
|
||||
BeginRegisterConditionalOpcode begin_reg_cond;
|
||||
SaveRestoreRegisterOpcode save_restore_reg;
|
||||
SaveRestoreRegisterMaskOpcode save_restore_regmask;
|
||||
DebugLogOpcode debug_log;
|
||||
};
|
||||
};
|
||||
|
||||
class CheatVirtualMachine {
|
||||
public:
|
||||
constexpr static size_t MaximumProgramOpcodeCount = 0x400;
|
||||
constexpr static size_t NumRegisters = 0x10;
|
||||
private:
|
||||
size_t num_opcodes = 0;
|
||||
size_t instruction_ptr = 0;
|
||||
size_t condition_depth = 0;
|
||||
bool decode_success = false;
|
||||
u32 program[MaximumProgramOpcodeCount] = {0};
|
||||
u64 registers[NumRegisters] = {0};
|
||||
u64 saved_values[NumRegisters] = {0};
|
||||
size_t loop_tops[NumRegisters] = {0};
|
||||
private:
|
||||
bool DecodeNextOpcode(CheatVmOpcode *out);
|
||||
void SkipConditionalBlock();
|
||||
void ResetState();
|
||||
|
||||
/* For implementing the DebugLog opcode. */
|
||||
void DebugLog(u32 log_id, u64 value);
|
||||
|
||||
/* For debugging. These will be IFDEF'd out normally. */
|
||||
void OpenDebugLogFile();
|
||||
void CloseDebugLogFile();
|
||||
void LogToDebugFile(const char *format, ...);
|
||||
void LogOpcode(const CheatVmOpcode *opcode);
|
||||
|
||||
static u64 GetVmInt(VmInt value, u32 bit_width);
|
||||
static u64 GetCheatProcessAddress(const CheatProcessMetadata* metadata, MemoryAccessType mem_type, u64 rel_address);
|
||||
public:
|
||||
CheatVirtualMachine() { }
|
||||
|
||||
size_t GetProgramSize() {
|
||||
return this->num_opcodes;
|
||||
}
|
||||
|
||||
bool LoadProgram(const CheatEntry *cheats, size_t num_cheats);
|
||||
void Execute(const CheatProcessMetadata *metadata);
|
||||
#ifdef DMNT_CHEAT_VM_DEBUG_LOG
|
||||
private:
|
||||
FILE *debug_log_file = NULL;
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user