dmnt: refactor to use sts:: namespace.

This commit is contained in:
Michael Scire 2019-09-16 01:22:08 -07:00 committed by SciresM
parent a750e55f75
commit 78a730ddf6
36 changed files with 3684 additions and 4318 deletions

View file

@ -26,7 +26,7 @@ endif
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source
SOURCES := source source/cheat source/cheat/impl
DATA := data
INCLUDES := include ../../common/include
EXEFS_SRC := exefs_src

View 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);
}
}

View 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),
};
};
}

File diff suppressed because it is too large Load diff

View 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);
}

View file

@ -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);
}
}

View file

@ -16,16 +16,10 @@
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
struct OverrideKey {
u64 key_combination;
bool override_by_default;
};
namespace sts::dmnt::cheat::impl {
class DmntConfigManager {
public:
static void RefreshConfiguration();
void ContinueCheatProcess(Handle cheat_dbg_hnd);
static OverrideKey GetTitleCheatEnableKey(u64 tid);
static bool HasCheatEnableButton(u64 tid);
};
}

File diff suppressed because it is too large Load diff

View 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
};
}

View file

@ -1,97 +0,0 @@
/*
* 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 <map>
#include <switch.h>
#include "dmnt_config.hpp"
#include "dmnt_cheat_debug_events_manager.hpp"
/* WORKAROUND: This design prevents a kernel deadlock from occurring on 6.0.0+ */
static HosThread g_per_core_threads[DmntCheatDebugEventsManager::NumCores];
static HosMessageQueue *g_per_core_queues[DmntCheatDebugEventsManager::NumCores];
static HosSignal g_continued_signal;
void DmntCheatDebugEventsManager::PerCoreThreadFunc(void *arg) {
/* This thread will simply wait on the appropriate message queue. */
size_t current_core = reinterpret_cast<size_t>(arg);
while (true) {
Handle debug_handle = 0;
/* Get the debug handle. */
{
uintptr_t x = 0;
g_per_core_queues[current_core]->Receive(&x);
debug_handle = static_cast<Handle>(x);
}
/* Continue the process, if needed. */
if ((GetRuntimeFirmwareVersion() >= FirmwareVersion_300)) {
svcContinueDebugEvent(debug_handle, 5, nullptr, 0);
} else {
svcLegacyContinueDebugEvent(debug_handle, 5, 0);
}
g_continued_signal.Signal();
}
}
void DmntCheatDebugEventsManager::ContinueCheatProcess(Handle cheat_dbg_hnd) {
/* Loop getting debug events. */
DebugEventInfo dbg_event;
while (R_SUCCEEDED(svcGetDebugEvent((u8 *)&dbg_event, cheat_dbg_hnd))) {
/* ... */
}
size_t target_core = DmntCheatDebugEventsManager::NumCores - 1;
/* Retrieve correct core for new thread event. */
if (dbg_event.type == DebugEventType::AttachThread) {
u64 out64;
u32 out32;
R_ASSERT(svcGetDebugThreadParam(&out64, &out32, cheat_dbg_hnd, dbg_event.info.attach_thread.thread_id, DebugThreadParam_CurrentCore));
target_core = out32;
}
/* Make appropriate thread continue. */
g_per_core_queues[target_core]->Send(static_cast<uintptr_t>(cheat_dbg_hnd));
/* Wait. */
g_continued_signal.Wait();
g_continued_signal.Reset();
}
void DmntCheatDebugEventsManager::Initialize() {
/* Spawn per core resources. */
for (size_t i = 0; i < DmntCheatDebugEventsManager::NumCores; i++) {
/* Create queue. */
g_per_core_queues[i] = new HosMessageQueue(1);
/* Create thread. */
if (R_FAILED(g_per_core_threads[i].Initialize(&DmntCheatDebugEventsManager::PerCoreThreadFunc, reinterpret_cast<void *>(i), 0x1000, 24, i))) {
std::abort();
}
/* Set core mask. */
if (R_FAILED(svcSetThreadCoreMask(g_per_core_threads[i].GetHandle(), i, (1u << i)))) {
std::abort();
}
/* Start thread. */
if (R_FAILED(g_per_core_threads[i].Start())) {
std::abort();
}
}
}

View file

@ -1,131 +0,0 @@
/*
* 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 "dmnt_cheat_types.hpp"
struct StackFrame {
u64 fp;
u64 lr;
};
struct AttachProcessInfo {
u64 title_id;
u64 process_id;
char name[0xC];
u32 flags;
u64 user_exception_context_address; /* 5.0.0+ */
};
struct AttachThreadInfo {
u64 thread_id;
u64 tls_address;
u64 entrypoint;
};
/* TODO: ExitProcessInfo */
/* TODO: ExitThreadInfo */
enum class DebugExceptionType : u32 {
UndefinedInstruction = 0,
InstructionAbort = 1,
DataAbort = 2,
AlignmentFault = 3,
DebuggerAttached = 4,
BreakPoint = 5,
UserBreak = 6,
DebuggerBreak = 7,
BadSvc = 8,
UnknownNine = 9,
};
struct UndefinedInstructionInfo {
u32 insn;
};
struct DataAbortInfo {
u64 address;
};
struct AlignmentFaultInfo {
u64 address;
};
struct UserBreakInfo {
u64 break_reason;
u64 address;
u64 size;
};
struct BadSvcInfo {
u32 id;
};
union SpecificExceptionInfo {
UndefinedInstructionInfo undefined_instruction;
DataAbortInfo data_abort;
AlignmentFaultInfo alignment_fault;
UserBreakInfo user_break;
BadSvcInfo bad_svc;
u64 raw;
};
struct ExceptionInfo {
DebugExceptionType type;
u64 address;
SpecificExceptionInfo specific;
};
enum class DebugEventType : u32 {
AttachProcess = 0,
AttachThread = 1,
ExitProcess = 2,
ExitThread = 3,
Exception = 4
};
union DebugInfo {
AttachProcessInfo attach_process;
AttachThreadInfo attach_thread;
ExceptionInfo exception;
};
struct DebugEventInfo {
DebugEventType type;
u32 flags;
u64 thread_id;
union {
DebugInfo info;
u64 _[0x40/sizeof(u64)];
};
};
static_assert(sizeof(DebugEventInfo) >= 0x50, "Incorrect DebugEventInfo definition!");
class DmntCheatDebugEventsManager {
public:
static constexpr size_t NumCores = 4;
private:
static void PerCoreThreadFunc(void *arg);
public:
static void ContinueCheatProcess(Handle cheat_dbg_hnd);
static void Initialize();
};

File diff suppressed because it is too large Load diff

View file

@ -1,83 +0,0 @@
/*
* 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 "dmnt_cheat_types.hpp"
class DmntCheatManager {
public:
static constexpr size_t MaxCheatCount = 0x80;
static constexpr size_t MaxFrozenAddressCount = 0x80;
private:
static Handle PrepareDebugNextApplication();
static void OnNewApplicationLaunch();
static void DetectThread(void *arg);
static void VmThread(void *arg);
static void DebugEventsThread(void *arg);
static void StartDebugEventsThread();
static void WaitDebugEventsThread();
static bool HasActiveCheatProcess();
static void CloseActiveCheatProcess();
static void ContinueCheatProcess();
static void ResetCheatEntry(size_t i);
static void ResetAllCheatEntries();
static CheatEntry *GetFreeCheatEntry();
static CheatEntry *GetCheatEntryById(size_t i);
static CheatEntry *GetCheatEntryByReadableName(const char *readable_name);
static bool ParseCheats(const char *cht_txt, size_t len);
static bool LoadCheats(u64 title_id, const u8 *build_id);
static bool ParseCheatToggles(const char *s, size_t len);
static bool LoadCheatToggles(u64 title_id);
static void SaveCheatToggles(u64 title_id);
static void ResetFrozenAddresses();
public:
static bool GetHasActiveCheatProcess();
static Handle GetCheatProcessEventHandle();
static Result GetCheatProcessMetadata(CheatProcessMetadata *out);
static Result ForceOpenCheatProcess();
static Result ReadCheatProcessMemoryForVm(u64 proc_addr, void *out_data, size_t size);
static Result WriteCheatProcessMemoryForVm(u64 proc_addr, const void *data, size_t size);
static Result GetCheatProcessMappingCount(u64 *out_count);
static Result GetCheatProcessMappings(MemoryInfo *mappings, size_t max_count, u64 *out_count, u64 offset);
static Result ReadCheatProcessMemory(u64 proc_addr, void *out_data, size_t size);
static Result WriteCheatProcessMemory(u64 proc_addr, const void *data, size_t size);
static Result QueryCheatProcessMemory(MemoryInfo *mapping, u64 address);
static Result GetCheatCount(u64 *out_count);
static Result GetCheats(CheatEntry *cheats, size_t max_count, u64 *out_count, u64 offset);
static Result GetCheatById(CheatEntry *out_cheat, u32 cheat_id);
static Result ToggleCheat(u32 cheat_id);
static Result AddCheat(u32 *out_id, CheatDefinition *def, bool enabled);
static Result RemoveCheat(u32 cheat_id);
static Result GetFrozenAddressCount(u64 *out_count);
static Result GetFrozenAddresses(FrozenAddressEntry *frz_addrs, size_t max_count, u64 *out_count, u64 offset);
static Result GetFrozenAddress(FrozenAddressEntry *frz_addr, u64 address);
static Result EnableFrozenAddress(u64 *out_value, u64 address, u64 width);
static Result DisableFrozenAddress(u64 address);
static void InitializeCheatManager();
};

View file

@ -1,175 +0,0 @@
/*
* 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 "dmnt_cheat_manager.hpp"
/* ========================================================================================= */
/* ==================================== Meta Commands ==================================== */
/* ========================================================================================= */
void DmntCheatService::HasCheatProcess(Out<bool> out) {
out.SetValue(DmntCheatManager::GetHasActiveCheatProcess());
}
void DmntCheatService::GetCheatProcessEvent(Out<CopiedHandle> out_event) {
out_event.SetValue(DmntCheatManager::GetCheatProcessEventHandle());
}
Result DmntCheatService::GetCheatProcessMetadata(Out<CheatProcessMetadata> out_metadata) {
return DmntCheatManager::GetCheatProcessMetadata(out_metadata.GetPointer());
}
Result DmntCheatService::ForceOpenCheatProcess() {
if (R_FAILED(DmntCheatManager::ForceOpenCheatProcess())) {
return ResultDmntCheatNotAttached;
}
return ResultSuccess;
}
/* ========================================================================================= */
/* =================================== Memory Commands =================================== */
/* ========================================================================================= */
Result DmntCheatService::GetCheatProcessMappingCount(Out<u64> out_count) {
return DmntCheatManager::GetCheatProcessMappingCount(out_count.GetPointer());
}
Result DmntCheatService::GetCheatProcessMappings(OutBuffer<MemoryInfo> mappings, Out<u64> out_count, u64 offset) {
if (mappings.buffer == nullptr) {
return ResultDmntCheatNullBuffer;
}
return DmntCheatManager::GetCheatProcessMappings(mappings.buffer, mappings.num_elements, out_count.GetPointer(), offset);
}
Result DmntCheatService::ReadCheatProcessMemory(OutBuffer<u8> buffer, u64 address, u64 out_size) {
if (buffer.buffer == nullptr) {
return ResultDmntCheatNullBuffer;
}
u64 sz = out_size;
if (buffer.num_elements < sz) {
sz = buffer.num_elements;
}
return DmntCheatManager::ReadCheatProcessMemory(address, buffer.buffer, sz);
}
Result DmntCheatService::WriteCheatProcessMemory(InBuffer<u8> buffer, u64 address, u64 in_size) {
if (buffer.buffer == nullptr) {
return ResultDmntCheatNullBuffer;
}
u64 sz = in_size;
if (buffer.num_elements < sz) {
sz = buffer.num_elements;
}
return DmntCheatManager::WriteCheatProcessMemory(address, buffer.buffer, sz);
}
Result DmntCheatService::QueryCheatProcessMemory(Out<MemoryInfo> mapping, u64 address) {
return DmntCheatManager::QueryCheatProcessMemory(mapping.GetPointer(), address);
}
/* ========================================================================================= */
/* =================================== Cheat Commands ==================================== */
/* ========================================================================================= */
Result DmntCheatService::GetCheatCount(Out<u64> out_count) {
return DmntCheatManager::GetCheatCount(out_count.GetPointer());
}
Result DmntCheatService::GetCheats(OutBuffer<CheatEntry> cheats, Out<u64> out_count, u64 offset) {
if (cheats.buffer == nullptr) {
return ResultDmntCheatNullBuffer;
}
return DmntCheatManager::GetCheats(cheats.buffer, cheats.num_elements, out_count.GetPointer(), offset);
}
Result DmntCheatService::GetCheatById(OutBuffer<CheatEntry> cheat, u32 cheat_id) {
if (cheat.buffer == nullptr) {
return ResultDmntCheatNullBuffer;
}
if (cheat.num_elements < 1) {
return ResultDmntCheatInvalidBuffer;
}
return DmntCheatManager::GetCheatById(cheat.buffer, cheat_id);
}
Result DmntCheatService::ToggleCheat(u32 cheat_id) {
return DmntCheatManager::ToggleCheat(cheat_id);
}
Result DmntCheatService::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 DmntCheatManager::AddCheat(out_cheat_id.GetPointer(), cheat.buffer, enabled);
}
Result DmntCheatService::RemoveCheat(u32 cheat_id) {
return DmntCheatManager::RemoveCheat(cheat_id);
}
/* ========================================================================================= */
/* =================================== Address Commands ================================== */
/* ========================================================================================= */
Result DmntCheatService::GetFrozenAddressCount(Out<u64> out_count) {
return DmntCheatManager::GetFrozenAddressCount(out_count.GetPointer());
}
Result DmntCheatService::GetFrozenAddresses(OutBuffer<FrozenAddressEntry> frz_addrs, Out<u64> out_count, u64 offset) {
if (frz_addrs.buffer == nullptr) {
return ResultDmntCheatNullBuffer;
}
return DmntCheatManager::GetFrozenAddresses(frz_addrs.buffer, frz_addrs.num_elements, out_count.GetPointer(), offset);
}
Result DmntCheatService::GetFrozenAddress(Out<FrozenAddressEntry> entry, u64 address) {
return DmntCheatManager::GetFrozenAddress(entry.GetPointer(), address);
}
Result DmntCheatService::EnableFrozenAddress(Out<u64> out_value, u64 address, u64 width) {
switch (width) {
case 1:
case 2:
case 4:
case 8:
break;
default:
return ResultDmntCheatInvalidFreezeWidth;
}
return DmntCheatManager::EnableFrozenAddress(out_value.GetPointer(), address, width);
}
Result DmntCheatService::DisableFrozenAddress(u64 address) {
return DmntCheatManager::DisableFrozenAddress(address);
}

View file

@ -1,105 +0,0 @@
/*
* 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 "dmnt_cheat_types.hpp"
class DmntCheatService 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(DmntCheatService, HasCheatProcess),
MAKE_SERVICE_COMMAND_META(DmntCheatService, GetCheatProcessEvent),
MAKE_SERVICE_COMMAND_META(DmntCheatService, GetCheatProcessMetadata),
MAKE_SERVICE_COMMAND_META(DmntCheatService, ForceOpenCheatProcess),
MAKE_SERVICE_COMMAND_META(DmntCheatService, GetCheatProcessMappingCount),
MAKE_SERVICE_COMMAND_META(DmntCheatService, GetCheatProcessMappings),
MAKE_SERVICE_COMMAND_META(DmntCheatService, ReadCheatProcessMemory),
MAKE_SERVICE_COMMAND_META(DmntCheatService, WriteCheatProcessMemory),
MAKE_SERVICE_COMMAND_META(DmntCheatService, QueryCheatProcessMemory),
MAKE_SERVICE_COMMAND_META(DmntCheatService, GetCheatCount),
MAKE_SERVICE_COMMAND_META(DmntCheatService, GetCheats),
MAKE_SERVICE_COMMAND_META(DmntCheatService, GetCheatById),
MAKE_SERVICE_COMMAND_META(DmntCheatService, ToggleCheat),
MAKE_SERVICE_COMMAND_META(DmntCheatService, AddCheat),
MAKE_SERVICE_COMMAND_META(DmntCheatService, RemoveCheat),
MAKE_SERVICE_COMMAND_META(DmntCheatService, GetFrozenAddressCount),
MAKE_SERVICE_COMMAND_META(DmntCheatService, GetFrozenAddresses),
MAKE_SERVICE_COMMAND_META(DmntCheatService, GetFrozenAddress),
MAKE_SERVICE_COMMAND_META(DmntCheatService, EnableFrozenAddress),
MAKE_SERVICE_COMMAND_META(DmntCheatService, DisableFrozenAddress),
};
};

View file

@ -1,56 +0,0 @@
/*
* 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>
struct MemoryRegionExtents {
u64 base;
u64 size;
};
struct CheatProcessMetadata {
u64 process_id;
u64 title_id;
MemoryRegionExtents main_nso_extents;
MemoryRegionExtents heap_extents;
MemoryRegionExtents alias_extents;
MemoryRegionExtents address_space_extents;
u8 main_nso_build_id[0x20];
};
struct CheatDefinition {
char readable_name[0x40];
uint32_t num_opcodes;
uint32_t opcodes[0x100];
};
struct CheatEntry {
bool enabled;
uint32_t cheat_id;
CheatDefinition definition;
};
struct FrozenAddressValue {
u64 value;
u8 width;
};
struct FrozenAddressEntry {
u64 address;
FrozenAddressValue value;
};

File diff suppressed because it is too large Load diff

View file

@ -1,304 +0,0 @@
/*
* 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 <stdarg.h>
#include <stratosphere.hpp>
#include "dmnt_cheat_types.hpp"
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 DmntCheatVm {
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:
DmntCheatVm() { }
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
};

View file

@ -1,157 +0,0 @@
/*
* 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 <string.h>
#include <stratosphere.hpp>
#include "dmnt_hid.hpp"
#include "dmnt_config.hpp"
#include "ini.h"
/* Support variables. */
static OverrideKey g_default_cheat_enable_key = {
.key_combination = KEY_L,
.override_by_default = true
};
/* Static buffer for loader.ini contents at runtime. */
static char g_config_ini_data[0x800];
static OverrideKey ParseOverrideKey(const char *value) {
OverrideKey cfg;
/* Parse on by default. */
if (value[0] == '!') {
cfg.override_by_default = true;
value++;
} else {
cfg.override_by_default = false;
}
/* Parse key combination. */
if (strcasecmp(value, "A") == 0) {
cfg.key_combination = KEY_A;
} else if (strcasecmp(value, "B") == 0) {
cfg.key_combination = KEY_B;
} else if (strcasecmp(value, "X") == 0) {
cfg.key_combination = KEY_X;
} else if (strcasecmp(value, "Y") == 0) {
cfg.key_combination = KEY_Y;
} else if (strcasecmp(value, "LS") == 0) {
cfg.key_combination = KEY_LSTICK;
} else if (strcasecmp(value, "RS") == 0) {
cfg.key_combination = KEY_RSTICK;
} else if (strcasecmp(value, "L") == 0) {
cfg.key_combination = KEY_L;
} else if (strcasecmp(value, "R") == 0) {
cfg.key_combination = KEY_R;
} else if (strcasecmp(value, "ZL") == 0) {
cfg.key_combination = KEY_ZL;
} else if (strcasecmp(value, "ZR") == 0) {
cfg.key_combination = KEY_ZR;
} else if (strcasecmp(value, "PLUS") == 0) {
cfg.key_combination = KEY_PLUS;
} else if (strcasecmp(value, "MINUS") == 0) {
cfg.key_combination = KEY_MINUS;
} else if (strcasecmp(value, "DLEFT") == 0) {
cfg.key_combination = KEY_DLEFT;
} else if (strcasecmp(value, "DUP") == 0) {
cfg.key_combination = KEY_DUP;
} else if (strcasecmp(value, "DRIGHT") == 0) {
cfg.key_combination = KEY_DRIGHT;
} else if (strcasecmp(value, "DDOWN") == 0) {
cfg.key_combination = KEY_DDOWN;
} else if (strcasecmp(value, "SL") == 0) {
cfg.key_combination = KEY_SL;
} else if (strcasecmp(value, "SR") == 0) {
cfg.key_combination = KEY_SR;
} else {
cfg.key_combination = 0;
}
return cfg;
}
static int DmntIniHandler(void *user, const char *section, const char *name, const char *value) {
/* Taken and modified, with love, from Rajkosto's implementation. */
if (strcasecmp(section, "default_config") == 0) {
if (strcasecmp(name, "cheat_enable_key") == 0) {
g_default_cheat_enable_key = ParseOverrideKey(value);
}
} else {
return 0;
}
return 1;
}
static int DmntTitleSpecificIniHandler(void *user, const char *section, const char *name, const char *value) {
/* We'll output an override key when relevant. */
OverrideKey *user_cfg = reinterpret_cast<OverrideKey *>(user);
if (strcasecmp(section, "override_config") == 0) {
if (strcasecmp(name, "cheat_enable_key") == 0) {
*user_cfg = ParseOverrideKey(value);
}
} else {
return 0;
}
return 1;
}
void DmntConfigManager::RefreshConfiguration() {
FILE *config = fopen("sdmc:/atmosphere/loader.ini", "r");
if (config == NULL) {
return;
}
memset(g_config_ini_data, 0, sizeof(g_config_ini_data));
fread(g_config_ini_data, 1, sizeof(g_config_ini_data) - 1, config);
fclose(config);
ini_parse_string(g_config_ini_data, DmntIniHandler, NULL);
}
OverrideKey DmntConfigManager::GetTitleCheatEnableKey(u64 tid) {
OverrideKey cfg = g_default_cheat_enable_key;
char path[FS_MAX_PATH+1] = {0};
snprintf(path, FS_MAX_PATH, "sdmc:/atmosphere/titles/%016lx/config.ini", tid);
FILE *config = fopen(path, "r");
if (config != NULL) {
ON_SCOPE_EXIT { fclose(config); };
/* Parse current title ini. */
ini_parse_file(config, DmntTitleSpecificIniHandler, &cfg);
}
return cfg;
}
static bool HasOverrideKey(OverrideKey *cfg) {
u64 kDown = 0;
bool keys_triggered = (R_SUCCEEDED(HidManagement::GetKeysDown(&kDown)) && ((kDown & cfg->key_combination) != 0));
return (cfg->override_by_default ^ keys_triggered);
}
bool DmntConfigManager::HasCheatEnableButton(u64 tid) {
/* Unconditionally refresh loader.ini contents. */
RefreshConfiguration();
OverrideKey title_cfg = GetTitleCheatEnableKey(tid);
return HasOverrideKey(&title_cfg);
}

View file

@ -24,9 +24,7 @@
#include <stratosphere.hpp>
#include "dmnt_service.hpp"
#include "dmnt_cheat_service.hpp"
#include "dmnt_cheat_manager.hpp"
#include "dmnt_config.hpp"
#include "cheat/dmnt_cheat_service.hpp"
extern "C" {
extern u32 __start__;
@ -96,14 +94,6 @@ void __appExit(void) {
int main(int argc, char **argv)
{
consoleDebugInit(debugDevice_SVC);
/* Initialize configuration manager. */
DmntConfigManager::RefreshConfiguration();
/* Start cheat manager. */
DmntCheatManager::InitializeCheatManager();
/* Nintendo uses four threads. Add a fifth for our cheat service. */
static auto s_server_manager = WaitableManager(5);
@ -111,9 +101,7 @@ int main(int argc, char **argv)
/* TODO: Implement rest of dmnt:- in ams.tma development branch. */
/* server_manager->AddWaitable(new ServiceServer<DebugMonitorService>("dmnt:-", 4)); */
s_server_manager.AddWaitable(new ServiceServer<DmntCheatService>("dmnt:cht", 1));
s_server_manager.AddWaitable(new ServiceServer<sts::dmnt::cheat::CheatService>("dmnt:cht", 1));
/* Loop forever, servicing our services. */
s_server_manager.Process();

View file

@ -18,133 +18,137 @@
#include <switch.h>
#include <stratosphere.hpp>
class DebugMonitorService final : public IServiceObject {
private:
enum class CommandId {
BreakDebugProcess = 0,
TerminateDebugProcess = 1,
CloseHandle = 2,
LoadImage = 3,
GetProcessId = 4,
GetProcessHandle = 5,
WaitSynchronization = 6,
GetDebugEvent = 7,
GetProcessModuleInfo = 8,
GetProcessList = 9,
GetThreadList = 10,
GetDebugThreadContext = 11,
ContinueDebugEvent = 12,
ReadDebugProcessMemory = 13,
WriteDebugProcessMemory = 14,
SetDebugThreadContext = 15,
GetDebugThreadParam = 16,
InitializeThreadInfo = 17,
SetHardwareBreakPoint = 18,
QueryDebugProcessMemory = 19,
GetProcessMemoryDetails = 20,
AttachByProgramId = 21,
AttachOnLaunch = 22,
GetDebugMonitorProcessId = 23,
GetJitDebugProcessList = 25,
CreateCoreDump = 26,
GetAllDebugThreadInfo = 27,
TargetIO_FileOpen = 29,
TargetIO_FileClose = 30,
TargetIO_FileRead = 31,
TargetIO_FileWrite = 32,
TargetIO_FileSetAttributes = 33,
TargetIO_FileGetInformation = 34,
TargetIO_FileSetTime = 35,
TargetIO_FileSetSize = 36,
TargetIO_FileDelete = 37,
TargetIO_FileMove = 38,
TargetIO_DirectoryCreate = 39,
TargetIO_DirectoryDelete = 40,
TargetIO_DirectoryRename = 41,
TargetIO_DirectoryGetCount = 42,
TargetIO_DirectoryOpen = 43,
TargetIO_DirectoryGetNext = 44,
TargetIO_DirectoryClose = 45,
TargetIO_GetFreeSpace = 46,
TargetIO_GetVolumeInformation = 47,
InitiateCoreDump = 48,
ContinueCoreDump = 49,
AddTTYToCoreDump = 50,
AddImageToCoreDump = 51,
CloseCoreDump = 52,
CancelAttach = 53,
};
private:
Result BreakDebugProcess(Handle debug_hnd);
Result TerminateDebugProcess(Handle debug_hnd);
Result CloseHandle(Handle debug_hnd);
Result GetProcessId(Out<u64> out_pid, Handle hnd);
Result GetProcessHandle(Out<Handle> out_hnd, u64 pid);
Result WaitSynchronization(Handle hnd, u64 ns);
namespace sts::dmnt {
Result TargetIO_FileOpen(OutBuffer<u64> out_hnd, InBuffer<char> path, int open_mode, u32 create_mode);
Result TargetIO_FileClose(InBuffer<u64> hnd);
Result TargetIO_FileRead(InBuffer<u64> hnd, OutBuffer<u8, BufferType_Type1> out_data, Out<u32> out_read, u64 offset);
Result TargetIO_FileWrite(InBuffer<u64> hnd, InBuffer<u8, BufferType_Type1> data, Out<u32> out_written, u64 offset);
Result TargetIO_FileSetAttributes(InBuffer<char> path, InBuffer<u8> attributes);
Result TargetIO_FileGetInformation(InBuffer<char> path, OutBuffer<u64> out_info, Out<int> is_directory);
Result TargetIO_FileSetTime(InBuffer<char> path, u64 create, u64 access, u64 modify);
Result TargetIO_FileSetSize(InBuffer<char> path, u64 size);
Result TargetIO_FileDelete(InBuffer<char> path);
Result TargetIO_FileMove(InBuffer<char> path0, InBuffer<char> path1);
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MAKE_SERVICE_COMMAND_META(DebugMonitorService, BreakDebugProcess),
MAKE_SERVICE_COMMAND_META(DebugMonitorService, TerminateDebugProcess),
MAKE_SERVICE_COMMAND_META(DebugMonitorService, CloseHandle),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, LoadImage),
MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetProcessId),
MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetProcessHandle),
MAKE_SERVICE_COMMAND_META(DebugMonitorService, WaitSynchronization),
//MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetDebugEvent),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetProcessModuleInfo),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetProcessList),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetThreadList),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetDebugThreadContext),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, ContinueDebugEvent),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, ReadDebugProcessMemory),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, WriteDebugProcessMemory),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, SetDebugThreadContext),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetDebugThreadParam),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, InitializeThreadInfo),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, SetHardwareBreakPoint),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, QueryDebugProcessMemory),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetProcessMemoryDetails),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, AttachByProgramId),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, AttachOnLaunch),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetDebugMonitorProcessId),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetJitDebugProcessList),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, CreateCoreDump),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetAllDebugThreadInfo),
MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_FileOpen),
MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_FileClose),
MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_FileRead),
MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_FileWrite),
MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_FileSetAttributes),
MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_FileGetInformation),
MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_FileSetTime),
MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_FileSetSize),
MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_FileDelete),
MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_FileMove),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_DirectoryCreate),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_DirectoryDelete),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_DirectoryRename),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_DirectoryGetCount),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_DirectoryOpen),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_DirectoryGetNext),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_DirectoryClose),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_GetFreeSpace),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_GetVolumeInformation),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, InitiateCoreDump),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, ContinueCoreDump),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, AddTTYToCoreDump),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, AddImageToCoreDump),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, CloseCoreDump),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, CancelAttach),
};
};
class DebugMonitorService final : public IServiceObject {
private:
enum class CommandId {
BreakDebugProcess = 0,
TerminateDebugProcess = 1,
CloseHandle = 2,
LoadImage = 3,
GetProcessId = 4,
GetProcessHandle = 5,
WaitSynchronization = 6,
GetDebugEvent = 7,
GetProcessModuleInfo = 8,
GetProcessList = 9,
GetThreadList = 10,
GetDebugThreadContext = 11,
ContinueDebugEvent = 12,
ReadDebugProcessMemory = 13,
WriteDebugProcessMemory = 14,
SetDebugThreadContext = 15,
GetDebugThreadParam = 16,
InitializeThreadInfo = 17,
SetHardwareBreakPoint = 18,
QueryDebugProcessMemory = 19,
GetProcessMemoryDetails = 20,
AttachByProgramId = 21,
AttachOnLaunch = 22,
GetDebugMonitorProcessId = 23,
GetJitDebugProcessList = 25,
CreateCoreDump = 26,
GetAllDebugThreadInfo = 27,
TargetIO_FileOpen = 29,
TargetIO_FileClose = 30,
TargetIO_FileRead = 31,
TargetIO_FileWrite = 32,
TargetIO_FileSetAttributes = 33,
TargetIO_FileGetInformation = 34,
TargetIO_FileSetTime = 35,
TargetIO_FileSetSize = 36,
TargetIO_FileDelete = 37,
TargetIO_FileMove = 38,
TargetIO_DirectoryCreate = 39,
TargetIO_DirectoryDelete = 40,
TargetIO_DirectoryRename = 41,
TargetIO_DirectoryGetCount = 42,
TargetIO_DirectoryOpen = 43,
TargetIO_DirectoryGetNext = 44,
TargetIO_DirectoryClose = 45,
TargetIO_GetFreeSpace = 46,
TargetIO_GetVolumeInformation = 47,
InitiateCoreDump = 48,
ContinueCoreDump = 49,
AddTTYToCoreDump = 50,
AddImageToCoreDump = 51,
CloseCoreDump = 52,
CancelAttach = 53,
};
private:
Result BreakDebugProcess(Handle debug_hnd);
Result TerminateDebugProcess(Handle debug_hnd);
Result CloseHandle(Handle debug_hnd);
Result GetProcessId(Out<u64> out_pid, Handle hnd);
Result GetProcessHandle(Out<Handle> out_hnd, u64 pid);
Result WaitSynchronization(Handle hnd, u64 ns);
Result TargetIO_FileOpen(OutBuffer<u64> out_hnd, InBuffer<char> path, int open_mode, u32 create_mode);
Result TargetIO_FileClose(InBuffer<u64> hnd);
Result TargetIO_FileRead(InBuffer<u64> hnd, OutBuffer<u8, BufferType_Type1> out_data, Out<u32> out_read, u64 offset);
Result TargetIO_FileWrite(InBuffer<u64> hnd, InBuffer<u8, BufferType_Type1> data, Out<u32> out_written, u64 offset);
Result TargetIO_FileSetAttributes(InBuffer<char> path, InBuffer<u8> attributes);
Result TargetIO_FileGetInformation(InBuffer<char> path, OutBuffer<u64> out_info, Out<int> is_directory);
Result TargetIO_FileSetTime(InBuffer<char> path, u64 create, u64 access, u64 modify);
Result TargetIO_FileSetSize(InBuffer<char> path, u64 size);
Result TargetIO_FileDelete(InBuffer<char> path);
Result TargetIO_FileMove(InBuffer<char> path0, InBuffer<char> path1);
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MAKE_SERVICE_COMMAND_META(DebugMonitorService, BreakDebugProcess),
MAKE_SERVICE_COMMAND_META(DebugMonitorService, TerminateDebugProcess),
MAKE_SERVICE_COMMAND_META(DebugMonitorService, CloseHandle),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, LoadImage),
MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetProcessId),
MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetProcessHandle),
MAKE_SERVICE_COMMAND_META(DebugMonitorService, WaitSynchronization),
//MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetDebugEvent),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetProcessModuleInfo),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetProcessList),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetThreadList),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetDebugThreadContext),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, ContinueDebugEvent),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, ReadDebugProcessMemory),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, WriteDebugProcessMemory),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, SetDebugThreadContext),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetDebugThreadParam),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, InitializeThreadInfo),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, SetHardwareBreakPoint),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, QueryDebugProcessMemory),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetProcessMemoryDetails),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, AttachByProgramId),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, AttachOnLaunch),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetDebugMonitorProcessId),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetJitDebugProcessList),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, CreateCoreDump),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetAllDebugThreadInfo),
MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_FileOpen),
MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_FileClose),
MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_FileRead),
MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_FileWrite),
MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_FileSetAttributes),
MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_FileGetInformation),
MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_FileSetTime),
MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_FileSetSize),
MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_FileDelete),
MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_FileMove),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_DirectoryCreate),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_DirectoryDelete),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_DirectoryRename),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_DirectoryGetCount),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_DirectoryOpen),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_DirectoryGetNext),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_DirectoryClose),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_GetFreeSpace),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_GetVolumeInformation),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, InitiateCoreDump),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, ContinueCoreDump),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, AddTTYToCoreDump),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, AddImageToCoreDump),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, CloseCoreDump),
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, CancelAttach),
};
};
}

View file

@ -14,41 +14,44 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <switch.h>
#include "dmnt_service.hpp"
Result DebugMonitorService::BreakDebugProcess(Handle debug_hnd) {
/* Nintendo discards the output of this command, but we will return it. */
return svcBreakDebugProcess(debug_hnd);
}
namespace sts::dmnt {
Result DebugMonitorService::TerminateDebugProcess(Handle debug_hnd) {
/* Nintendo discards the output of this command, but we will return it. */
return svcTerminateDebugProcess(debug_hnd);
}
Result DebugMonitorService::BreakDebugProcess(Handle debug_hnd) {
/* Nintendo discards the output of this command, but we will return it. */
return svcBreakDebugProcess(debug_hnd);
}
Result DebugMonitorService::CloseHandle(Handle debug_hnd) {
/* Nintendo discards the output of this command, but we will return it. */
/* This command is, entertainingly, also pretty unsafe in general... */
return svcCloseHandle(debug_hnd);
}
Result DebugMonitorService::TerminateDebugProcess(Handle debug_hnd) {
/* Nintendo discards the output of this command, but we will return it. */
return svcTerminateDebugProcess(debug_hnd);
}
Result DebugMonitorService::GetProcessId(Out<u64> out_pid, Handle hnd) {
/* Nintendo discards the output of this command, but we will return it. */
return svcGetProcessId(out_pid.GetPointer(), hnd);
}
Result DebugMonitorService::CloseHandle(Handle debug_hnd) {
/* Nintendo discards the output of this command, but we will return it. */
/* This command is, entertainingly, also pretty unsafe in general... */
return svcCloseHandle(debug_hnd);
}
Result DebugMonitorService::GetProcessHandle(Out<Handle> out_hnd, u64 pid) {
R_TRY_CATCH(svcDebugActiveProcess(out_hnd.GetPointer(), pid)) {
R_CATCH(ResultKernelAlreadyExists) {
return ResultDebugAlreadyAttached;
}
} R_END_TRY_CATCH;
Result DebugMonitorService::GetProcessId(Out<u64> out_pid, Handle hnd) {
/* Nintendo discards the output of this command, but we will return it. */
return svcGetProcessId(out_pid.GetPointer(), hnd);
}
return ResultSuccess;
}
Result DebugMonitorService::GetProcessHandle(Out<Handle> out_hnd, u64 pid) {
R_TRY_CATCH(svcDebugActiveProcess(out_hnd.GetPointer(), pid)) {
R_CATCH(ResultKernelAlreadyExists) {
return ResultDebugAlreadyAttached;
}
} R_END_TRY_CATCH;
return ResultSuccess;
}
Result DebugMonitorService::WaitSynchronization(Handle hnd, u64 ns) {
/* Nintendo discards the output of this command, but we will return it. */
return svcWaitSynchronizationSingle(hnd, ns);
}
Result DebugMonitorService::WaitSynchronization(Handle hnd, u64 ns) {
/* Nintendo discards the output of this command, but we will return it. */
return svcWaitSynchronizationSingle(hnd, ns);
}

View file

@ -15,259 +15,266 @@
*/
#include <unordered_map>
#include <switch.h>
#include "dmnt_service.hpp"
enum TIOCreateOption : u32 {
TIOCreateOption_CreateNew = 1,
TIOCreateOption_CreateAlways = 2,
TIOCreateOption_OpenExisting = 3,
TIOCreateOption_OpenAlways = 4,
TIOCreateOption_ResetSize = 5,
};
namespace sts::dmnt {
/* Nintendo uses actual pointers as file handles. We'll add a layer of indirection... */
static bool g_sd_initialized = false;
static HosMutex g_sd_lock;
static FsFileSystem g_sd_fs;
namespace {
static HosMutex g_file_handle_lock;
static u64 g_cur_fd = 0;
static std::unordered_map<u64, FsFile> g_file_handles;
enum TIOCreateOption : u32 {
TIOCreateOption_CreateNew = 1,
TIOCreateOption_CreateAlways = 2,
TIOCreateOption_OpenExisting = 3,
TIOCreateOption_OpenAlways = 4,
TIOCreateOption_ResetSize = 5,
};
static Result EnsureSdInitialized() {
std::scoped_lock<HosMutex> lk(g_sd_lock);
if (g_sd_initialized) {
return ResultSuccess;
}
/* Nintendo uses actual pointers as file handles. We'll add a layer of indirection... */
bool g_sd_initialized = false;
HosMutex g_sd_lock;
FsFileSystem g_sd_fs;
R_TRY(fsMountSdcard(&g_sd_fs));
g_sd_initialized = true;
return ResultSuccess;
}
HosMutex g_file_handle_lock;
u64 g_cur_fd;
std::unordered_map<u64, FsFile> g_file_handles;
static u64 GetNewFileHandle(FsFile f) {
std::scoped_lock<HosMutex> lk(g_file_handle_lock);
Result EnsureSdInitialized() {
std::scoped_lock<HosMutex> lk(g_sd_lock);
if (g_sd_initialized) {
return ResultSuccess;
}
u64 fd = g_cur_fd++;
g_file_handles[fd] = f;
return fd;
}
static Result GetFileByHandle(FsFile *out, u64 handle) {
std::scoped_lock<HosMutex> lk(g_file_handle_lock);
if (g_file_handles.find(handle) != g_file_handles.end()) {
*out = g_file_handles[handle];
return ResultSuccess;
}
return ResultFsInvalidArgument;
}
static Result CloseFileByHandle(u64 handle) {
std::scoped_lock<HosMutex> lk(g_file_handle_lock);
if (g_file_handles.find(handle) != g_file_handles.end()) {
fsFileClose(&g_file_handles[handle]);
g_file_handles.erase(handle);
return ResultSuccess;
}
return ResultFsInvalidArgument;
}
static void FixPath(char *dst, size_t dst_size, InBuffer<char> &path) {
dst[dst_size - 1] = 0;
strncpy(dst, "/", dst_size - 1);
const char *src = path.buffer;
size_t src_idx = 0;
size_t dst_idx = 1;
while (src_idx < path.num_elements && (src[src_idx] == '/' || src[src_idx] == '\\')) {
src_idx++;
}
while (src_idx < path.num_elements && dst_idx < dst_size - 1 && src[src_idx] != 0) {
if (src[src_idx] == '\\') {
dst[dst_idx] = '/';
} else {
dst[dst_idx] = src[src_idx];
R_TRY(fsMountSdcard(&g_sd_fs));
g_sd_initialized = true;
return ResultSuccess;
}
u64 GetNewFileHandle(FsFile f) {
std::scoped_lock<HosMutex> lk(g_file_handle_lock);
u64 fd = g_cur_fd++;
g_file_handles[fd] = f;
return fd;
}
Result GetFileByHandle(FsFile *out, u64 handle) {
std::scoped_lock<HosMutex> lk(g_file_handle_lock);
if (g_file_handles.find(handle) != g_file_handles.end()) {
*out = g_file_handles[handle];
return ResultSuccess;
}
return ResultFsInvalidArgument;
}
Result CloseFileByHandle(u64 handle) {
std::scoped_lock<HosMutex> lk(g_file_handle_lock);
if (g_file_handles.find(handle) != g_file_handles.end()) {
fsFileClose(&g_file_handles[handle]);
g_file_handles.erase(handle);
return ResultSuccess;
}
return ResultFsInvalidArgument;
}
void FixPath(char *dst, size_t dst_size, InBuffer<char> &path) {
dst[dst_size - 1] = 0;
strncpy(dst, "/", dst_size - 1);
const char *src = path.buffer;
size_t src_idx = 0;
size_t dst_idx = 1;
while (src_idx < path.num_elements && (src[src_idx] == '/' || src[src_idx] == '\\')) {
src_idx++;
}
while (src_idx < path.num_elements && dst_idx < dst_size - 1 && src[src_idx] != 0) {
if (src[src_idx] == '\\') {
dst[dst_idx] = '/';
} else {
dst[dst_idx] = src[src_idx];
}
src_idx++;
dst_idx++;
}
if (dst_idx < dst_size) {
dst[dst_idx] = 0;
}
}
src_idx++;
dst_idx++;
}
if (dst_idx < dst_size) {
dst[dst_idx] = 0;
}
}
Result DebugMonitorService::TargetIO_FileOpen(OutBuffer<u64> out_hnd, InBuffer<char> path, int open_mode, u32 create_mode) {
if (out_hnd.num_elements != 1) {
/* Serialization error. */
return ResultKernelConnectionClosed;
}
Result DebugMonitorService::TargetIO_FileOpen(OutBuffer<u64> out_hnd, InBuffer<char> path, int open_mode, u32 create_mode) {
if (out_hnd.num_elements != 1) {
/* Serialization error. */
return ResultKernelConnectionClosed;
R_TRY(EnsureSdInitialized());
char fs_path[FS_MAX_PATH];
FixPath(fs_path, sizeof(fs_path), path);
/* Create file as required by mode. */
if (create_mode == TIOCreateOption_CreateAlways) {
fsFsDeleteFile(&g_sd_fs, fs_path);
R_TRY(fsFsCreateFile(&g_sd_fs, fs_path, 0, 0));
} else if (create_mode == TIOCreateOption_CreateNew) {
R_TRY(fsFsCreateFile(&g_sd_fs, fs_path, 0, 0));
} else if (create_mode == TIOCreateOption_OpenAlways) {
fsFsCreateFile(&g_sd_fs, fs_path, 0, 0);
}
/* Open the file, guard to prevent failure to close. */
FsFile f;
R_TRY(fsFsOpenFile(&g_sd_fs, fs_path, open_mode, &f));
auto file_guard = SCOPE_GUARD {
fsFileClose(&f);
};
/* Set size if needed. */
if (create_mode == TIOCreateOption_ResetSize) {
R_TRY(fsFileSetSize(&f, 0));
}
/* Cancel guard, output file handle. */
file_guard.Cancel();
out_hnd[0] = GetNewFileHandle(f);
return ResultSuccess;
}
R_TRY(EnsureSdInitialized());
Result DebugMonitorService::TargetIO_FileClose(InBuffer<u64> hnd) {
if (hnd.num_elements != 1) {
/* Serialization error. */
return ResultKernelConnectionClosed;
}
char fs_path[FS_MAX_PATH];
FixPath(fs_path, sizeof(fs_path), path);
/* Create file as required by mode. */
if (create_mode == TIOCreateOption_CreateAlways) {
fsFsDeleteFile(&g_sd_fs, fs_path);
R_TRY(fsFsCreateFile(&g_sd_fs, fs_path, 0, 0));
} else if (create_mode == TIOCreateOption_CreateNew) {
R_TRY(fsFsCreateFile(&g_sd_fs, fs_path, 0, 0));
} else if (create_mode == TIOCreateOption_OpenAlways) {
fsFsCreateFile(&g_sd_fs, fs_path, 0, 0);
return CloseFileByHandle(hnd[0]);
}
/* Open the file, guard to prevent failure to close. */
FsFile f;
R_TRY(fsFsOpenFile(&g_sd_fs, fs_path, open_mode, &f));
auto file_guard = SCOPE_GUARD {
fsFileClose(&f);
};
Result DebugMonitorService::TargetIO_FileRead(InBuffer<u64> hnd, OutBuffer<u8, BufferType_Type1> out_data, Out<u32> out_read, u64 offset) {
if (hnd.num_elements != 1) {
/* Serialization error. */
return ResultKernelConnectionClosed;
}
/* Set size if needed. */
if (create_mode == TIOCreateOption_ResetSize) {
R_TRY(fsFileSetSize(&f, 0));
FsFile f;
size_t read = 0;
R_TRY(GetFileByHandle(&f, hnd[0]));
R_TRY(fsFileRead(&f, offset, out_data.buffer, out_data.num_elements, FS_READOPTION_NONE, &read));
out_read.SetValue(static_cast<u32>(read));
return ResultSuccess;
}
/* Cancel guard, output file handle. */
file_guard.Cancel();
out_hnd[0] = GetNewFileHandle(f);
Result DebugMonitorService::TargetIO_FileWrite(InBuffer<u64> hnd, InBuffer<u8, BufferType_Type1> data, Out<u32> out_written, u64 offset) {
if (hnd.num_elements != 1) {
/* Serialization error. */
return ResultKernelConnectionClosed;
}
return ResultSuccess;
}
FsFile f;
Result DebugMonitorService::TargetIO_FileClose(InBuffer<u64> hnd) {
if (hnd.num_elements != 1) {
/* Serialization error. */
return ResultKernelConnectionClosed;
R_TRY(GetFileByHandle(&f, hnd[0]));
R_TRY(fsFileWrite(&f, offset, data.buffer, data.num_elements, FS_WRITEOPTION_NONE));
out_written.SetValue(data.num_elements);
return ResultSuccess;
}
return CloseFileByHandle(hnd[0]);
}
Result DebugMonitorService::TargetIO_FileRead(InBuffer<u64> hnd, OutBuffer<u8, BufferType_Type1> out_data, Out<u32> out_read, u64 offset) {
if (hnd.num_elements != 1) {
/* Serialization error. */
return ResultKernelConnectionClosed;
Result DebugMonitorService::TargetIO_FileSetAttributes(InBuffer<char> path, InBuffer<u8> attributes) {
/* I don't really know why this command exists, Horizon doesn't allow you to set any attributes. */
/* N just returns ResultSuccess unconditionally here. */
return ResultSuccess;
}
FsFile f;
size_t read = 0;
Result DebugMonitorService::TargetIO_FileGetInformation(InBuffer<char> path, OutBuffer<u64> out_info, Out<int> is_directory) {
if (out_info.num_elements != 4) {
/* Serialization error. */
return ResultKernelConnectionClosed;
}
R_TRY(GetFileByHandle(&f, hnd[0]));
R_TRY(fsFileRead(&f, offset, out_data.buffer, out_data.num_elements, FS_READOPTION_NONE, &read));
R_TRY(EnsureSdInitialized());
out_read.SetValue(static_cast<u32>(read));
return ResultSuccess;
}
char fs_path[FS_MAX_PATH];
FixPath(fs_path, sizeof(fs_path), path);
Result DebugMonitorService::TargetIO_FileWrite(InBuffer<u64> hnd, InBuffer<u8, BufferType_Type1> data, Out<u32> out_written, u64 offset) {
if (hnd.num_elements != 1) {
/* Serialization error. */
return ResultKernelConnectionClosed;
for (size_t i = 0; i < out_info.num_elements; i++) {
out_info[i] = 0;
}
is_directory.SetValue(0);
FsFile f;
if (R_SUCCEEDED(fsFsOpenFile(&g_sd_fs, fs_path, FS_OPEN_READ, &f))) {
ON_SCOPE_EXIT { fsFileClose(&f); };
/* N doesn't check this return code. */
fsFileGetSize(&f, &out_info[0]);
/* TODO: N does not call fsFsGetFileTimestampRaw here, but we possibly could. */
} else {
FsDir dir;
R_TRY(fsFsOpenDirectory(&g_sd_fs, fs_path, FS_DIROPEN_FILE | FS_DIROPEN_DIRECTORY, &dir));
fsDirClose(&dir);
is_directory.SetValue(1);
}
return ResultSuccess;
}
FsFile f;
R_TRY(GetFileByHandle(&f, hnd[0]));
R_TRY(fsFileWrite(&f, offset, data.buffer, data.num_elements, FS_WRITEOPTION_NONE));
out_written.SetValue(data.num_elements);
return ResultSuccess;
}
Result DebugMonitorService::TargetIO_FileSetAttributes(InBuffer<char> path, InBuffer<u8> attributes) {
/* I don't really know why this command exists, Horizon doesn't allow you to set any attributes. */
/* N just returns ResultSuccess unconditionally here. */
return ResultSuccess;
}
Result DebugMonitorService::TargetIO_FileGetInformation(InBuffer<char> path, OutBuffer<u64> out_info, Out<int> is_directory) {
if (out_info.num_elements != 4) {
/* Serialization error. */
return ResultKernelConnectionClosed;
Result DebugMonitorService::TargetIO_FileSetTime(InBuffer<char> path, u64 create, u64 access, u64 modify) {
/* This is another function that doesn't really need to exist, because Horizon doesn't let you set anything. */
return ResultSuccess;
}
R_TRY(EnsureSdInitialized());
Result DebugMonitorService::TargetIO_FileSetSize(InBuffer<char> input, u64 size) {
/* Why does this function take in a path and not a file handle? */
char fs_path[FS_MAX_PATH];
FixPath(fs_path, sizeof(fs_path), path);
/* We will try to be better than N, here. N only treats input as a path. */
if (input.num_elements == sizeof(u64)) {
FsFile f;
if (R_SUCCEEDED(GetFileByHandle(&f, reinterpret_cast<u64 *>(input.buffer)[0]))) {
return fsFileSetSize(&f, size);
}
}
for (size_t i = 0; i < out_info.num_elements; i++) {
out_info[i] = 0;
}
is_directory.SetValue(0);
R_TRY(EnsureSdInitialized());
FsFile f;
if (R_SUCCEEDED(fsFsOpenFile(&g_sd_fs, fs_path, FS_OPEN_READ, &f))) {
char fs_path[FS_MAX_PATH];
FixPath(fs_path, sizeof(fs_path), input);
FsFile f;
R_TRY(fsFsOpenFile(&g_sd_fs, fs_path, FS_OPEN_WRITE, &f));
ON_SCOPE_EXIT { fsFileClose(&f); };
/* N doesn't check this return code. */
fsFileGetSize(&f, &out_info[0]);
/* TODO: N does not call fsFsGetFileTimestampRaw here, but we possibly could. */
} else {
FsDir dir;
R_TRY(fsFsOpenDirectory(&g_sd_fs, fs_path, FS_DIROPEN_FILE | FS_DIROPEN_DIRECTORY, &dir));
fsDirClose(&dir);
is_directory.SetValue(1);
return fsFileSetSize(&f, size);
}
return ResultSuccess;
}
Result DebugMonitorService::TargetIO_FileDelete(InBuffer<char> path) {
R_TRY(EnsureSdInitialized());
Result DebugMonitorService::TargetIO_FileSetTime(InBuffer<char> path, u64 create, u64 access, u64 modify) {
/* This is another function that doesn't really need to exist, because Horizon doesn't let you set anything. */
return ResultSuccess;
}
char fs_path[FS_MAX_PATH];
FixPath(fs_path, sizeof(fs_path), path);
Result DebugMonitorService::TargetIO_FileSetSize(InBuffer<char> input, u64 size) {
/* Why does this function take in a path and not a file handle? */
/* We will try to be better than N, here. N only treats input as a path. */
if (input.num_elements == sizeof(u64)) {
FsFile f;
if (R_SUCCEEDED(GetFileByHandle(&f, reinterpret_cast<u64 *>(input.buffer)[0]))) {
return fsFileSetSize(&f, size);
}
return fsFsDeleteFile(&g_sd_fs, fs_path);
}
R_TRY(EnsureSdInitialized());
Result DebugMonitorService::TargetIO_FileMove(InBuffer<char> path0, InBuffer<char> path1) {
R_TRY(EnsureSdInitialized());
char fs_path[FS_MAX_PATH];
FixPath(fs_path, sizeof(fs_path), input);
char fs_path0[FS_MAX_PATH];
char fs_path1[FS_MAX_PATH];
FixPath(fs_path0, sizeof(fs_path0), path0);
FixPath(fs_path1, sizeof(fs_path1), path1);
FsFile f;
R_TRY(fsFsOpenFile(&g_sd_fs, fs_path, FS_OPEN_WRITE, &f));
ON_SCOPE_EXIT { fsFileClose(&f); };
return fsFsRenameFile(&g_sd_fs, fs_path0, fs_path1);
}
return fsFileSetSize(&f, size);
}
Result DebugMonitorService::TargetIO_FileDelete(InBuffer<char> path) {
R_TRY(EnsureSdInitialized());
char fs_path[FS_MAX_PATH];
FixPath(fs_path, sizeof(fs_path), path);
return fsFsDeleteFile(&g_sd_fs, fs_path);
}
Result DebugMonitorService::TargetIO_FileMove(InBuffer<char> path0, InBuffer<char> path1) {
R_TRY(EnsureSdInitialized());
char fs_path0[FS_MAX_PATH];
char fs_path1[FS_MAX_PATH];
FixPath(fs_path0, sizeof(fs_path0), path0);
FixPath(fs_path1, sizeof(fs_path1), path1);
return fsFsRenameFile(&g_sd_fs, fs_path0, fs_path1);
}

View file

@ -1,269 +0,0 @@
/* inih -- simple .INI file parser
inih is released under the New BSD license (see LICENSE.txt). Go to the project
home page for more info:
https://github.com/benhoyt/inih
*/
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "ini.h"
#if !INI_USE_STACK
#include <stdlib.h>
#endif
#define MAX_SECTION 50
#define MAX_NAME 50
/* Used by ini_parse_string() to keep track of string parsing state. */
typedef struct {
const char* ptr;
size_t num_left;
} ini_parse_string_ctx;
/* Strip whitespace chars off end of given string, in place. Return s. */
static char* rstrip(char* s)
{
char* p = s + strlen(s);
while (p > s && isspace((unsigned char)(*--p)))
*p = '\0';
return s;
}
/* Return pointer to first non-whitespace char in given string. */
static char* lskip(const char* s)
{
while (*s && isspace((unsigned char)(*s)))
s++;
return (char*)s;
}
/* Return pointer to first char (of chars) or inline comment in given string,
or pointer to null at end of string if neither found. Inline comment must
be prefixed by a whitespace character to register as a comment. */
static char* find_chars_or_comment(const char* s, const char* chars)
{
#if INI_ALLOW_INLINE_COMMENTS
int was_space = 0;
while (*s && (!chars || !strchr(chars, *s)) &&
!(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) {
was_space = isspace((unsigned char)(*s));
s++;
}
#else
while (*s && (!chars || !strchr(chars, *s))) {
s++;
}
#endif
return (char*)s;
}
/* Version of strncpy that ensures dest (size bytes) is null-terminated. */
static char* strncpy0(char* dest, const char* src, size_t size)
{
strncpy(dest, src, size - 1);
dest[size - 1] = '\0';
return dest;
}
/* See documentation in header file. */
int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
void* user)
{
/* Uses a fair bit of stack (use heap instead if you need to) */
#if INI_USE_STACK
char line[INI_MAX_LINE];
int max_line = INI_MAX_LINE;
#else
char* line;
int max_line = INI_INITIAL_ALLOC;
#endif
#if INI_ALLOW_REALLOC
char* new_line;
int offset;
#endif
char section[MAX_SECTION] = "";
char prev_name[MAX_NAME] = "";
char* start;
char* end;
char* name;
char* value;
int lineno = 0;
int error = 0;
#if !INI_USE_STACK
line = (char*)malloc(INI_INITIAL_ALLOC);
if (!line) {
return -2;
}
#endif
#if INI_HANDLER_LINENO
#define HANDLER(u, s, n, v) handler(u, s, n, v, lineno)
#else
#define HANDLER(u, s, n, v) handler(u, s, n, v)
#endif
/* Scan through stream line by line */
while (reader(line, max_line, stream) != NULL) {
#if INI_ALLOW_REALLOC
offset = strlen(line);
while (offset == max_line - 1 && line[offset - 1] != '\n') {
max_line *= 2;
if (max_line > INI_MAX_LINE)
max_line = INI_MAX_LINE;
new_line = realloc(line, max_line);
if (!new_line) {
free(line);
return -2;
}
line = new_line;
if (reader(line + offset, max_line - offset, stream) == NULL)
break;
if (max_line >= INI_MAX_LINE)
break;
offset += strlen(line + offset);
}
#endif
lineno++;
start = line;
#if INI_ALLOW_BOM
if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
(unsigned char)start[1] == 0xBB &&
(unsigned char)start[2] == 0xBF) {
start += 3;
}
#endif
start = lskip(rstrip(start));
if (strchr(INI_START_COMMENT_PREFIXES, *start)) {
/* Start-of-line comment */
}
#if INI_ALLOW_MULTILINE
else if (*prev_name && *start && start > line) {
/* Non-blank line with leading whitespace, treat as continuation
of previous name's value (as per Python configparser). */
if (!HANDLER(user, section, prev_name, start) && !error)
error = lineno;
}
#endif
else if (*start == '[') {
/* A "[section]" line */
end = find_chars_or_comment(start + 1, "]");
if (*end == ']') {
*end = '\0';
strncpy0(section, start + 1, sizeof(section));
*prev_name = '\0';
}
else if (!error) {
/* No ']' found on section line */
error = lineno;
}
}
else if (*start) {
/* Not a comment, must be a name[=:]value pair */
end = find_chars_or_comment(start, "=:");
if (*end == '=' || *end == ':') {
*end = '\0';
name = rstrip(start);
value = end + 1;
#if INI_ALLOW_INLINE_COMMENTS
end = find_chars_or_comment(value, NULL);
if (*end)
*end = '\0';
#endif
value = lskip(value);
rstrip(value);
/* Valid name[=:]value pair found, call handler */
strncpy0(prev_name, name, sizeof(prev_name));
if (!HANDLER(user, section, name, value) && !error)
error = lineno;
}
else if (!error) {
/* No '=' or ':' found on name[=:]value line */
error = lineno;
}
}
#if INI_STOP_ON_FIRST_ERROR
if (error)
break;
#endif
}
#if !INI_USE_STACK
free(line);
#endif
return error;
}
/* See documentation in header file. */
int ini_parse_file(FILE* file, ini_handler handler, void* user)
{
return ini_parse_stream((ini_reader)fgets, file, handler, user);
}
/* See documentation in header file. */
int ini_parse(const char* filename, ini_handler handler, void* user)
{
FILE* file;
int error;
file = fopen(filename, "r");
if (!file)
return -1;
error = ini_parse_file(file, handler, user);
fclose(file);
return error;
}
/* An ini_reader function to read the next line from a string buffer. This
is the fgets() equivalent used by ini_parse_string(). */
static char* ini_reader_string(char* str, int num, void* stream) {
ini_parse_string_ctx* ctx = (ini_parse_string_ctx*)stream;
const char* ctx_ptr = ctx->ptr;
size_t ctx_num_left = ctx->num_left;
char* strp = str;
char c;
if (ctx_num_left == 0 || num < 2)
return NULL;
while (num > 1 && ctx_num_left != 0) {
c = *ctx_ptr++;
ctx_num_left--;
*strp++ = c;
if (c == '\n')
break;
num--;
}
*strp = '\0';
ctx->ptr = ctx_ptr;
ctx->num_left = ctx_num_left;
return str;
}
/* See documentation in header file. */
int ini_parse_string(const char* string, ini_handler handler, void* user) {
ini_parse_string_ctx ctx;
ctx.ptr = string;
ctx.num_left = strlen(string);
return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler,
user);
}

View file

@ -1,130 +0,0 @@
/* inih -- simple .INI file parser
inih is released under the New BSD license (see LICENSE.txt). Go to the project
home page for more info:
https://github.com/benhoyt/inih
*/
#ifndef __INI_H__
#define __INI_H__
/* Make this header file easier to include in C++ code */
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
/* Nonzero if ini_handler callback should accept lineno parameter. */
#ifndef INI_HANDLER_LINENO
#define INI_HANDLER_LINENO 0
#endif
/* Typedef for prototype of handler function. */
#if INI_HANDLER_LINENO
typedef int (*ini_handler)(void* user, const char* section,
const char* name, const char* value,
int lineno);
#else
typedef int (*ini_handler)(void* user, const char* section,
const char* name, const char* value);
#endif
/* Typedef for prototype of fgets-style reader function. */
typedef char* (*ini_reader)(char* str, int num, void* stream);
/* Parse given INI-style file. May have [section]s, name=value pairs
(whitespace stripped), and comments starting with ';' (semicolon). Section
is "" if name=value pair parsed before any section heading. name:value
pairs are also supported as a concession to Python's configparser.
For each name=value pair parsed, call handler function with given user
pointer as well as section, name, and value (data only valid for duration
of handler call). Handler should return nonzero on success, zero on error.
Returns 0 on success, line number of first error on parse error (doesn't
stop on first error), -1 on file open error, or -2 on memory allocation
error (only when INI_USE_STACK is zero).
*/
int ini_parse(const char* filename, ini_handler handler, void* user);
/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't
close the file when it's finished -- the caller must do that. */
int ini_parse_file(FILE* file, ini_handler handler, void* user);
/* Same as ini_parse(), but takes an ini_reader function pointer instead of
filename. Used for implementing custom or string-based I/O (see also
ini_parse_string). */
int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
void* user);
/* Same as ini_parse(), but takes a zero-terminated string with the INI data
instead of a file. Useful for parsing INI data from a network socket or
already in memory. */
int ini_parse_string(const char* string, ini_handler handler, void* user);
/* Nonzero to allow multi-line value parsing, in the style of Python's
configparser. If allowed, ini_parse() will call the handler with the same
name for each subsequent line parsed. */
#ifndef INI_ALLOW_MULTILINE
#define INI_ALLOW_MULTILINE 1
#endif
/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of
the file. See http://code.google.com/p/inih/issues/detail?id=21 */
#ifndef INI_ALLOW_BOM
#define INI_ALLOW_BOM 1
#endif
/* Chars that begin a start-of-line comment. Per Python configparser, allow
both ; and # comments at the start of a line by default. */
#ifndef INI_START_COMMENT_PREFIXES
#define INI_START_COMMENT_PREFIXES ";#"
#endif
/* Nonzero to allow inline comments (with valid inline comment characters
specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match
Python 3.2+ configparser behaviour. */
#ifndef INI_ALLOW_INLINE_COMMENTS
#define INI_ALLOW_INLINE_COMMENTS 1
#endif
#ifndef INI_INLINE_COMMENT_PREFIXES
#define INI_INLINE_COMMENT_PREFIXES ";"
#endif
/* Nonzero to use stack for line buffer, zero to use heap (malloc/free). */
#ifndef INI_USE_STACK
#define INI_USE_STACK 1
#endif
/* Maximum line length for any line in INI file (stack or heap). Note that
this must be 3 more than the longest line (due to '\r', '\n', and '\0'). */
#ifndef INI_MAX_LINE
#define INI_MAX_LINE 200
#endif
/* Nonzero to allow heap line buffer to grow via realloc(), zero for a
fixed-size buffer of INI_MAX_LINE bytes. Only applies if INI_USE_STACK is
zero. */
#ifndef INI_ALLOW_REALLOC
#define INI_ALLOW_REALLOC 0
#endif
/* Initial size in bytes for heap line buffer. Only applies if INI_USE_STACK
is zero. */
#ifndef INI_INITIAL_ALLOC
#define INI_INITIAL_ALLOC 200
#endif
/* Stop parsing on first error (default is to keep parsing). */
#ifndef INI_STOP_ON_FIRST_ERROR
#define INI_STOP_ON_FIRST_ERROR 0
#endif
#ifdef __cplusplus
}
#endif
#endif /* __INI_H__ */

View file

@ -1,66 +0,0 @@
/*
* 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 "pm_shim.h"
/* Atmosphere extension commands. */
Result pmdmntAtmosphereGetProcessInfo(Handle* out, u64 *tid_out, FsStorageId *sid_out, u64 pid) {
IpcCommand c;
ipcInitialize(&c);
Service *s = pmdmntGetServiceSession();
struct {
u64 magic;
u64 cmd_id;
u64 pid;
} *raw;
raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 65000;
raw->pid = pid;
Result rc = serviceIpcDispatch(s);
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
struct {
u64 magic;
u64 result;
u64 title_id;
FsStorageId storage_id;
} *resp;
serviceIpcParse(s, &r, sizeof(*resp));
resp = r.Raw;
rc = resp->result;
if (R_SUCCEEDED(rc)) {
if (out) {
*out = r.Handles[0];
} else {
svcCloseHandle(r.Handles[0]);
}
if (tid_out) *tid_out = resp->title_id;
if (sid_out) *sid_out = resp->storage_id;
}
}
return rc;
}

View file

@ -1,19 +0,0 @@
/**
* @file pm_shim.h
* @brief Process Management (pm) IPC wrapper.
* @author SciresM
* @copyright libnx Authors
*/
#pragma once
#include <switch.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Atmosphere extension commands. */
Result pmdmntAtmosphereGetProcessInfo(Handle* out, u64 *tid_out, FsStorageId *sid_out, u64 pid);
#ifdef __cplusplus
}
#endif

View file

@ -32,7 +32,7 @@ CFLAGS := -g -Wall -O2 -ffunction-sections \
CFLAGS += $(INCLUDE) -D__SWITCH__
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++17
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -flto -std=gnu++17
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)

View file

@ -17,7 +17,4 @@
#pragma once
#include <switch.h>
class HidManagement {
public:
static Result GetKeysDown(u64 *keys);
};
#include "dmnt/dmnt_cheat_types.hpp"

View file

@ -0,0 +1,62 @@
/*
* 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 "../ncm/ncm_types.hpp"
namespace sts::dmnt::cheat {
struct CheatProcessMetadata {
struct MemoryRegionExtents {
u64 base;
u64 size;
};
u64 process_id;
ncm::TitleId title_id;
MemoryRegionExtents main_nso_extents;
MemoryRegionExtents heap_extents;
MemoryRegionExtents alias_extents;
MemoryRegionExtents aslr_extents;
u8 main_nso_build_id[0x20];
};
static_assert(std::is_pod<CheatProcessMetadata>::value && sizeof(CheatProcessMetadata) == 0x70, "CheatProcessMetadata definition!");
struct CheatDefinition {
char readable_name[0x40];
uint32_t num_opcodes;
uint32_t opcodes[0x100];
};
struct CheatEntry {
bool enabled;
uint32_t cheat_id;
CheatDefinition definition;
};
struct FrozenAddressValue {
u64 value;
u8 width;
};
struct FrozenAddressEntry {
u64 address;
FrozenAddressValue value;
};
}

View file

@ -224,24 +224,33 @@ class HosSignal {
mutexUnlock(&m);
}
void Wait() {
void Wait(bool reset = false) {
mutexLock(&m);
while (!signaled) {
condvarWait(&cv, &m);
}
if (reset) {
this->signaled = false;
}
mutexUnlock(&m);
}
bool TryWait() {
bool TryWait(bool reset = false) {
mutexLock(&m);
bool success = signaled;
if (reset) {
this->signaled = false;
}
mutexUnlock(&m);
return success;
}
Result TimedWait(u64 ns) {
Result TimedWait(u64 ns, bool reset = false) {
mutexLock(&m);
TimeoutHelper timeout_helper(ns);
@ -251,6 +260,10 @@ class HosSignal {
}
}
if (reset) {
this->signaled = false;
}
mutexUnlock(&m);
return true;
}

View file

@ -20,4 +20,5 @@
#include "pm/pm_types.hpp"
#include "pm/pm_boot_mode_api.hpp"
#include "pm/pm_info_api.hpp"
#include "pm/pm_shell_api.hpp"
#include "pm/pm_shell_api.hpp"
#include "pm/pm_dmnt_api.hpp"

View file

@ -14,23 +14,19 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <switch.h>
#include <string.h>
#include <stratosphere.hpp>
#pragma once
#include "dmnt_hid.hpp"
#include "../ldr.hpp"
#include "pm_types.hpp"
static HosMutex g_hid_keys_down_lock;
namespace sts::pm::dmnt {
Result HidManagement::GetKeysDown(u64 *keys) {
std::scoped_lock<HosMutex> lk(g_hid_keys_down_lock);
/* Debug Monitor API. */
Result StartProcess(u64 process_id);
Result GetProcessId(u64 *out_process_id, const ncm::TitleId title_id);
Result GetApplicationProcessId(u64 *out_process_id);
Result HookToCreateApplicationProcess(Handle *out_handle);
Result AtmosphereGetProcessInfo(Handle *out_handle, ncm::TitleLocation *out_loc, u64 process_id);
Result AtmosphereGetCurrentLimitInfo(u64 *out_current_value, u64 *out_limit_value, ResourceLimitGroup group, LimitableResource resource);
hidScanInput();
*keys = 0;
for (int controller = 0; controller < 10; controller++) {
*keys |= hidKeysHeld((HidControllerID) controller);
}
return ResultSuccess;
}

View file

@ -94,3 +94,94 @@ Result pminfoAtmosphereHasLaunchedTitle(bool *out, u64 tid) {
return rc;
}
Result pmdmntAtmosphereGetProcessInfo(Handle* out, u64 *tid_out, u8 *sid_out, u64 pid) {
IpcCommand c;
ipcInitialize(&c);
Service *s = pmdmntGetServiceSession();
struct {
u64 magic;
u64 cmd_id;
u64 pid;
} *raw;
raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 65000;
raw->pid = pid;
Result rc = serviceIpcDispatch(s);
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
struct {
u64 magic;
u64 result;
u64 title_id;
FsStorageId storage_id;
} *resp;
serviceIpcParse(s, &r, sizeof(*resp));
resp = r.Raw;
rc = resp->result;
if (R_SUCCEEDED(rc)) {
if (out) {
*out = r.Handles[0];
} else {
svcCloseHandle(r.Handles[0]);
}
if (tid_out) *tid_out = resp->title_id;
if (sid_out) *sid_out = resp->storage_id;
}
}
return rc;
}
Result pmdmntAtmosphereGetCurrentLimitInfo(u64 *out_cur, u64 *out_lim, u32 group, u32 resource) {
IpcCommand c;
ipcInitialize(&c);
Service *s = pmdmntGetServiceSession();
struct {
u64 magic;
u64 cmd_id;
u32 group;
u32 resource;
} *raw;
raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 65001;
raw->group = group;
raw->resource = resource;
Result rc = serviceIpcDispatch(s);
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
struct {
u64 magic;
u64 result;
u64 cur_value;
u64 lim_value;
} *resp;
serviceIpcParse(s, &r, sizeof(*resp));
resp = r.Raw;
rc = resp->result;
if (R_SUCCEEDED(rc)) {
if (out_cur) *out_cur = resp->cur_value;
if (out_lim) *out_lim = resp->lim_value;
}
}
return rc;
}

View file

@ -14,6 +14,9 @@ extern "C" {
Result pminfoAtmosphereGetProcessId(u64 *out_pid, u64 tid);
Result pminfoAtmosphereHasLaunchedTitle(bool *out, u64 tid);
Result pmdmntAtmosphereGetProcessInfo(Handle *out, u64 *tid_out, u8 *sid_out, u64 pid);
Result pmdmntAtmosphereGetCurrentLimitInfo(u64 *out_cur, u64 *out_lim, u32 group, u32 resource);
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,54 @@
/*
* 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 <stratosphere.hpp>
#include <stratosphere/pm.hpp>
#include "pm_ams.h"
namespace sts::pm::dmnt {
/* Debug Monitor API. */
Result StartProcess(u64 process_id) {
return pmdmntStartProcess(process_id);
}
Result GetProcessId(u64 *out_process_id, const ncm::TitleId title_id) {
return pmdmntGetTitlePid(out_process_id, static_cast<u64>(title_id));
}
Result GetApplicationProcessId(u64 *out_process_id) {
return pmdmntGetApplicationPid(out_process_id);
}
Result HookToCreateApplicationProcess(Handle *out_handle) {
return pmdmntEnableDebugForApplication(out_handle);
}
Result AtmosphereGetProcessInfo(Handle *out_handle, ncm::TitleLocation *out_loc, u64 process_id) {
*out_handle = INVALID_HANDLE;
*out_loc = {};
return pmdmntAtmosphereGetProcessInfo(out_handle, reinterpret_cast<u64 *>(&out_loc->title_id), &out_loc->storage_id, process_id);
}
Result AtmosphereGetCurrentLimitInfo(u64 *out_current_value, u64 *out_limit_value, ResourceLimitGroup group, LimitableResource resource) {
*out_current_value = 0;
*out_limit_value = 0;
return pmdmntAtmosphereGetCurrentLimitInfo(out_current_value, out_limit_value, group, resource);
}
}