mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2025-01-03 19:14:44 +00:00
dmnt: refactor to use sts:: namespace.
This commit is contained in:
parent
a750e55f75
commit
78a730ddf6
36 changed files with 3684 additions and 4318 deletions
|
@ -26,7 +26,7 @@ endif
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
TARGET := $(notdir $(CURDIR))
|
TARGET := $(notdir $(CURDIR))
|
||||||
BUILD := build
|
BUILD := build
|
||||||
SOURCES := source
|
SOURCES := source source/cheat source/cheat/impl
|
||||||
DATA := data
|
DATA := data
|
||||||
INCLUDES := include ../../common/include
|
INCLUDES := include ../../common/include
|
||||||
EXEFS_SRC := exefs_src
|
EXEFS_SRC := exefs_src
|
||||||
|
|
169
stratosphere/dmnt/source/cheat/dmnt_cheat_service.cpp
Normal file
169
stratosphere/dmnt/source/cheat/dmnt_cheat_service.cpp
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2019 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <switch.h>
|
||||||
|
#include "dmnt_cheat_service.hpp"
|
||||||
|
#include "impl/dmnt_cheat_api.hpp"
|
||||||
|
|
||||||
|
namespace sts::dmnt::cheat {
|
||||||
|
|
||||||
|
/* ========================================================================================= */
|
||||||
|
/* ==================================== Meta Commands ==================================== */
|
||||||
|
/* ========================================================================================= */
|
||||||
|
|
||||||
|
void CheatService::HasCheatProcess(Out<bool> out) {
|
||||||
|
out.SetValue(dmnt::cheat::impl::GetHasActiveCheatProcess());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheatService::GetCheatProcessEvent(Out<CopiedHandle> out_event) {
|
||||||
|
out_event.SetValue(dmnt::cheat::impl::GetCheatProcessEventHandle());
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CheatService::GetCheatProcessMetadata(Out<CheatProcessMetadata> out_metadata) {
|
||||||
|
return dmnt::cheat::impl::GetCheatProcessMetadata(out_metadata.GetPointer());
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CheatService::ForceOpenCheatProcess() {
|
||||||
|
if (R_FAILED(dmnt::cheat::impl::ForceOpenCheatProcess())) {
|
||||||
|
return ResultDmntCheatNotAttached;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================================================================================= */
|
||||||
|
/* =================================== Memory Commands =================================== */
|
||||||
|
/* ========================================================================================= */
|
||||||
|
|
||||||
|
Result CheatService::GetCheatProcessMappingCount(Out<u64> out_count) {
|
||||||
|
return dmnt::cheat::impl::GetCheatProcessMappingCount(out_count.GetPointer());
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CheatService::GetCheatProcessMappings(OutBuffer<MemoryInfo> mappings, Out<u64> out_count, u64 offset) {
|
||||||
|
if (mappings.buffer == nullptr) {
|
||||||
|
return ResultDmntCheatNullBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dmnt::cheat::impl::GetCheatProcessMappings(mappings.buffer, mappings.num_elements, out_count.GetPointer(), offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CheatService::ReadCheatProcessMemory(OutBuffer<u8> buffer, u64 address, u64 out_size) {
|
||||||
|
if (buffer.buffer == nullptr) {
|
||||||
|
return ResultDmntCheatNullBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dmnt::cheat::impl::ReadCheatProcessMemory(address, buffer.buffer, std::min(out_size, buffer.num_elements));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CheatService::WriteCheatProcessMemory(InBuffer<u8> buffer, u64 address, u64 in_size) {
|
||||||
|
if (buffer.buffer == nullptr) {
|
||||||
|
return ResultDmntCheatNullBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dmnt::cheat::impl::WriteCheatProcessMemory(address, buffer.buffer, std::min(in_size, buffer.num_elements));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CheatService::QueryCheatProcessMemory(Out<MemoryInfo> mapping, u64 address) {
|
||||||
|
return dmnt::cheat::impl::QueryCheatProcessMemory(mapping.GetPointer(), address);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================================================================================= */
|
||||||
|
/* =================================== Cheat Commands ==================================== */
|
||||||
|
/* ========================================================================================= */
|
||||||
|
|
||||||
|
Result CheatService::GetCheatCount(Out<u64> out_count) {
|
||||||
|
return dmnt::cheat::impl::GetCheatCount(out_count.GetPointer());
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CheatService::GetCheats(OutBuffer<CheatEntry> cheats, Out<u64> out_count, u64 offset) {
|
||||||
|
if (cheats.buffer == nullptr) {
|
||||||
|
return ResultDmntCheatNullBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dmnt::cheat::impl::GetCheats(cheats.buffer, cheats.num_elements, out_count.GetPointer(), offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CheatService::GetCheatById(OutBuffer<CheatEntry> cheat, u32 cheat_id) {
|
||||||
|
if (cheat.buffer == nullptr) {
|
||||||
|
return ResultDmntCheatNullBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cheat.num_elements < 1) {
|
||||||
|
return ResultDmntCheatInvalidBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dmnt::cheat::impl::GetCheatById(cheat.buffer, cheat_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CheatService::ToggleCheat(u32 cheat_id) {
|
||||||
|
return dmnt::cheat::impl::ToggleCheat(cheat_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CheatService::AddCheat(InBuffer<CheatDefinition> cheat, Out<u32> out_cheat_id, bool enabled) {
|
||||||
|
if (cheat.buffer == nullptr) {
|
||||||
|
return ResultDmntCheatNullBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cheat.num_elements < 1) {
|
||||||
|
return ResultDmntCheatInvalidBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dmnt::cheat::impl::AddCheat(out_cheat_id.GetPointer(), cheat.buffer, enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CheatService::RemoveCheat(u32 cheat_id) {
|
||||||
|
return dmnt::cheat::impl::RemoveCheat(cheat_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================================================================================= */
|
||||||
|
/* =================================== Address Commands ================================== */
|
||||||
|
/* ========================================================================================= */
|
||||||
|
|
||||||
|
Result CheatService::GetFrozenAddressCount(Out<u64> out_count) {
|
||||||
|
return dmnt::cheat::impl::GetFrozenAddressCount(out_count.GetPointer());
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CheatService::GetFrozenAddresses(OutBuffer<FrozenAddressEntry> frz_addrs, Out<u64> out_count, u64 offset) {
|
||||||
|
if (frz_addrs.buffer == nullptr) {
|
||||||
|
return ResultDmntCheatNullBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dmnt::cheat::impl::GetFrozenAddresses(frz_addrs.buffer, frz_addrs.num_elements, out_count.GetPointer(), offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CheatService::GetFrozenAddress(Out<FrozenAddressEntry> entry, u64 address) {
|
||||||
|
return dmnt::cheat::impl::GetFrozenAddress(entry.GetPointer(), address);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CheatService::EnableFrozenAddress(Out<u64> out_value, u64 address, u64 width) {
|
||||||
|
switch (width) {
|
||||||
|
case 1:
|
||||||
|
case 2:
|
||||||
|
case 4:
|
||||||
|
case 8:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return ResultDmntCheatInvalidFreezeWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dmnt::cheat::impl::EnableFrozenAddress(out_value.GetPointer(), address, width);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CheatService::DisableFrozenAddress(u64 address) {
|
||||||
|
return dmnt::cheat::impl::DisableFrozenAddress(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
108
stratosphere/dmnt/source/cheat/dmnt_cheat_service.hpp
Normal file
108
stratosphere/dmnt/source/cheat/dmnt_cheat_service.hpp
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2019 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <switch.h>
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include <stratosphere/dmnt.hpp>
|
||||||
|
|
||||||
|
namespace sts::dmnt::cheat {
|
||||||
|
|
||||||
|
class CheatService final : public IServiceObject {
|
||||||
|
private:
|
||||||
|
enum class CommandId {
|
||||||
|
/* Meta */
|
||||||
|
HasCheatProcess = 65000,
|
||||||
|
GetCheatProcessEvent = 65001,
|
||||||
|
GetCheatProcessMetadata = 65002,
|
||||||
|
ForceOpenCheatProcess = 65003,
|
||||||
|
|
||||||
|
/* Interact with Memory */
|
||||||
|
GetCheatProcessMappingCount = 65100,
|
||||||
|
GetCheatProcessMappings = 65101,
|
||||||
|
ReadCheatProcessMemory = 65102,
|
||||||
|
WriteCheatProcessMemory = 65103,
|
||||||
|
QueryCheatProcessMemory = 65104,
|
||||||
|
|
||||||
|
/* Interact with Cheats */
|
||||||
|
GetCheatCount = 65200,
|
||||||
|
GetCheats = 65201,
|
||||||
|
GetCheatById = 65202,
|
||||||
|
ToggleCheat = 65203,
|
||||||
|
AddCheat = 65204,
|
||||||
|
RemoveCheat = 65205,
|
||||||
|
|
||||||
|
/* Interact with Frozen Addresses */
|
||||||
|
GetFrozenAddressCount = 65300,
|
||||||
|
GetFrozenAddresses = 65301,
|
||||||
|
GetFrozenAddress = 65302,
|
||||||
|
EnableFrozenAddress = 65303,
|
||||||
|
DisableFrozenAddress = 65304,
|
||||||
|
};
|
||||||
|
private:
|
||||||
|
void HasCheatProcess(Out<bool> out);
|
||||||
|
void GetCheatProcessEvent(Out<CopiedHandle> out_event);
|
||||||
|
Result GetCheatProcessMetadata(Out<CheatProcessMetadata> out_metadata);
|
||||||
|
Result ForceOpenCheatProcess();
|
||||||
|
|
||||||
|
Result GetCheatProcessMappingCount(Out<u64> out_count);
|
||||||
|
Result GetCheatProcessMappings(OutBuffer<MemoryInfo> mappings, Out<u64> out_count, u64 offset);
|
||||||
|
Result ReadCheatProcessMemory(OutBuffer<u8> buffer, u64 address, u64 out_size);
|
||||||
|
Result WriteCheatProcessMemory(InBuffer<u8> buffer, u64 address, u64 in_size);
|
||||||
|
Result QueryCheatProcessMemory(Out<MemoryInfo> mapping, u64 address);
|
||||||
|
|
||||||
|
Result GetCheatCount(Out<u64> out_count);
|
||||||
|
Result GetCheats(OutBuffer<CheatEntry> cheats, Out<u64> out_count, u64 offset);
|
||||||
|
Result GetCheatById(OutBuffer<CheatEntry> cheat, u32 cheat_id);
|
||||||
|
Result ToggleCheat(u32 cheat_id);
|
||||||
|
Result AddCheat(InBuffer<CheatDefinition> cheat, Out<u32> out_cheat_id, bool enabled);
|
||||||
|
Result RemoveCheat(u32 cheat_id);
|
||||||
|
|
||||||
|
Result GetFrozenAddressCount(Out<u64> out_count);
|
||||||
|
Result GetFrozenAddresses(OutBuffer<FrozenAddressEntry> addresses, Out<u64> out_count, u64 offset);
|
||||||
|
Result GetFrozenAddress(Out<FrozenAddressEntry> entry, u64 address);
|
||||||
|
Result EnableFrozenAddress(Out<u64> out_value, u64 address, u64 width);
|
||||||
|
Result DisableFrozenAddress(u64 address);
|
||||||
|
|
||||||
|
public:
|
||||||
|
DEFINE_SERVICE_DISPATCH_TABLE {
|
||||||
|
MAKE_SERVICE_COMMAND_META(CheatService, HasCheatProcess),
|
||||||
|
MAKE_SERVICE_COMMAND_META(CheatService, GetCheatProcessEvent),
|
||||||
|
MAKE_SERVICE_COMMAND_META(CheatService, GetCheatProcessMetadata),
|
||||||
|
MAKE_SERVICE_COMMAND_META(CheatService, ForceOpenCheatProcess),
|
||||||
|
|
||||||
|
MAKE_SERVICE_COMMAND_META(CheatService, GetCheatProcessMappingCount),
|
||||||
|
MAKE_SERVICE_COMMAND_META(CheatService, GetCheatProcessMappings),
|
||||||
|
MAKE_SERVICE_COMMAND_META(CheatService, ReadCheatProcessMemory),
|
||||||
|
MAKE_SERVICE_COMMAND_META(CheatService, WriteCheatProcessMemory),
|
||||||
|
MAKE_SERVICE_COMMAND_META(CheatService, QueryCheatProcessMemory),
|
||||||
|
|
||||||
|
MAKE_SERVICE_COMMAND_META(CheatService, GetCheatCount),
|
||||||
|
MAKE_SERVICE_COMMAND_META(CheatService, GetCheats),
|
||||||
|
MAKE_SERVICE_COMMAND_META(CheatService, GetCheatById),
|
||||||
|
MAKE_SERVICE_COMMAND_META(CheatService, ToggleCheat),
|
||||||
|
MAKE_SERVICE_COMMAND_META(CheatService, AddCheat),
|
||||||
|
MAKE_SERVICE_COMMAND_META(CheatService, RemoveCheat),
|
||||||
|
|
||||||
|
MAKE_SERVICE_COMMAND_META(CheatService, GetFrozenAddressCount),
|
||||||
|
MAKE_SERVICE_COMMAND_META(CheatService, GetFrozenAddresses),
|
||||||
|
MAKE_SERVICE_COMMAND_META(CheatService, GetFrozenAddress),
|
||||||
|
MAKE_SERVICE_COMMAND_META(CheatService, EnableFrozenAddress),
|
||||||
|
MAKE_SERVICE_COMMAND_META(CheatService, DisableFrozenAddress),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
1084
stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp
Normal file
1084
stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp
Normal file
File diff suppressed because it is too large
Load diff
50
stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.hpp
Normal file
50
stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.hpp
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2019 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include <stratosphere/dmnt.hpp>
|
||||||
|
|
||||||
|
namespace sts::dmnt::cheat::impl {
|
||||||
|
|
||||||
|
bool GetHasActiveCheatProcess();
|
||||||
|
Handle GetCheatProcessEventHandle();
|
||||||
|
Result GetCheatProcessMetadata(CheatProcessMetadata *out);
|
||||||
|
Result ForceOpenCheatProcess();
|
||||||
|
|
||||||
|
Result ReadCheatProcessMemoryUnsafe(u64 process_addr, void *out_data, size_t size);
|
||||||
|
Result WriteCheatProcessMemoryUnsafe(u64 process_addr, void *data, size_t size);
|
||||||
|
|
||||||
|
Result GetCheatProcessMappingCount(u64 *out_count);
|
||||||
|
Result GetCheatProcessMappings(MemoryInfo *mappings, size_t max_count, u64 *out_count, u64 offset);
|
||||||
|
Result ReadCheatProcessMemory(u64 proc_addr, void *out_data, size_t size);
|
||||||
|
Result WriteCheatProcessMemory(u64 proc_addr, const void *data, size_t size);
|
||||||
|
Result QueryCheatProcessMemory(MemoryInfo *mapping, u64 address);
|
||||||
|
|
||||||
|
Result GetCheatCount(u64 *out_count);
|
||||||
|
Result GetCheats(CheatEntry *cheats, size_t max_count, u64 *out_count, u64 offset);
|
||||||
|
Result GetCheatById(CheatEntry *out_cheat, u32 cheat_id);
|
||||||
|
Result ToggleCheat(u32 cheat_id);
|
||||||
|
Result AddCheat(u32 *out_id, const CheatDefinition *def, bool enabled);
|
||||||
|
Result RemoveCheat(u32 cheat_id);
|
||||||
|
|
||||||
|
Result GetFrozenAddressCount(u64 *out_count);
|
||||||
|
Result GetFrozenAddresses(FrozenAddressEntry *frz_addrs, size_t max_count, u64 *out_count, u64 offset);
|
||||||
|
Result GetFrozenAddress(FrozenAddressEntry *frz_addr, u64 address);
|
||||||
|
Result EnableFrozenAddress(u64 *out_value, u64 address, u64 width);
|
||||||
|
Result DisableFrozenAddress(u64 address);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,127 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2019 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "dmnt_cheat_debug_events_manager.hpp"
|
||||||
|
|
||||||
|
/* WORKAROUND: This design prevents a kernel deadlock from occurring on 6.0.0+ */
|
||||||
|
|
||||||
|
namespace sts::dmnt::cheat::impl {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class DebugEventsManager {
|
||||||
|
public:
|
||||||
|
static constexpr size_t NumCores = 4;
|
||||||
|
private:
|
||||||
|
HosMessageQueue message_queues[NumCores];
|
||||||
|
HosThread threads[NumCores];
|
||||||
|
HosSignal continued_signal;
|
||||||
|
private:
|
||||||
|
static void PerCoreThreadFunction(void *_this) {
|
||||||
|
/* This thread will wait on the appropriate message queue. */
|
||||||
|
DebugEventsManager *this_ptr = reinterpret_cast<DebugEventsManager *>(_this);
|
||||||
|
const u32 current_core = svcGetCurrentProcessorNumber();
|
||||||
|
while (true) {
|
||||||
|
/* Receive handle. */
|
||||||
|
Handle debug_handle = this_ptr->WaitReceiveHandle(current_core);
|
||||||
|
|
||||||
|
/* Continue events on the correct core. */
|
||||||
|
R_ASSERT(this_ptr->ContinueDebugEvent(debug_handle));
|
||||||
|
|
||||||
|
/* Signal that we've continued. */
|
||||||
|
this_ptr->SignalContinued();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GetTargetCore(const svc::DebugEventInfo &dbg_event, Handle debug_handle) {
|
||||||
|
/* If we don't need to continue on a specific core, use the system core. */
|
||||||
|
u32 target_core = NumCores - 1;
|
||||||
|
|
||||||
|
/* Retrieve correct core for new thread event. */
|
||||||
|
if (dbg_event.type == svc::DebugEventType::AttachThread) {
|
||||||
|
u64 out64 = 0;
|
||||||
|
u32 out32 = 0;
|
||||||
|
R_ASSERT(svcGetDebugThreadParam(&out64, &out32, debug_handle, dbg_event.info.attach_thread.thread_id, DebugThreadParam_CurrentCore));
|
||||||
|
target_core = out32;
|
||||||
|
}
|
||||||
|
|
||||||
|
return target_core;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SendHandle(const svc::DebugEventInfo &dbg_event, Handle debug_handle) {
|
||||||
|
this->message_queues[GetTargetCore(dbg_event, debug_handle)].Send(static_cast<uintptr_t>(debug_handle));
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle WaitReceiveHandle(u32 core_id) {
|
||||||
|
uintptr_t x = 0;
|
||||||
|
this->message_queues[core_id].Receive(&x);
|
||||||
|
return static_cast<Handle>(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ContinueDebugEvent(Handle debug_handle) {
|
||||||
|
if (GetRuntimeFirmwareVersion() >= FirmwareVersion_300) {
|
||||||
|
return svcContinueDebugEvent(debug_handle, 5, nullptr, 0);
|
||||||
|
} else {
|
||||||
|
return svcLegacyContinueDebugEvent(debug_handle, 5, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaitContinued() {
|
||||||
|
this->continued_signal.Wait();
|
||||||
|
this->continued_signal.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SignalContinued() {
|
||||||
|
this->continued_signal.Signal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
DebugEventsManager() : message_queues{HosMessageQueue(1), HosMessageQueue(1), HosMessageQueue(1), HosMessageQueue(1)} {
|
||||||
|
for (size_t i = 0; i < NumCores; i++) {
|
||||||
|
/* Create thread. */
|
||||||
|
R_ASSERT(this->threads[i].Initialize(&DebugEventsManager::PerCoreThreadFunction, reinterpret_cast<void *>(this), 0x1000, 24, i));
|
||||||
|
|
||||||
|
/* Set core mask. */
|
||||||
|
R_ASSERT(svcSetThreadCoreMask(this->threads[i].GetHandle(), i, (1u << i)));
|
||||||
|
|
||||||
|
/* Start thread. */
|
||||||
|
R_ASSERT(this->threads[i].Start());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContinueCheatProcess(Handle cheat_dbg_hnd) {
|
||||||
|
/* Loop getting all debug events. */
|
||||||
|
svc::DebugEventInfo d;
|
||||||
|
while (R_SUCCEEDED(svcGetDebugEvent(reinterpret_cast<u8 *>(&d), cheat_dbg_hnd))) {
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send handle to correct core, wait for continue to finish. */
|
||||||
|
this->SendHandle(d, cheat_dbg_hnd);
|
||||||
|
this->WaitContinued();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Manager global. */
|
||||||
|
DebugEventsManager g_events_manager;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContinueCheatProcess(Handle cheat_dbg_hnd) {
|
||||||
|
g_events_manager.ContinueCheatProcess(cheat_dbg_hnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -16,16 +16,10 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <switch.h>
|
#include <switch.h>
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
struct OverrideKey {
|
namespace sts::dmnt::cheat::impl {
|
||||||
u64 key_combination;
|
|
||||||
bool override_by_default;
|
|
||||||
};
|
|
||||||
|
|
||||||
class DmntConfigManager {
|
void ContinueCheatProcess(Handle cheat_dbg_hnd);
|
||||||
public:
|
|
||||||
static void RefreshConfiguration();
|
|
||||||
|
|
||||||
static OverrideKey GetTitleCheatEnableKey(u64 tid);
|
}
|
||||||
static bool HasCheatEnableButton(u64 tid);
|
|
||||||
};
|
|
1213
stratosphere/dmnt/source/cheat/impl/dmnt_cheat_vm.cpp
Normal file
1213
stratosphere/dmnt/source/cheat/impl/dmnt_cheat_vm.cpp
Normal file
File diff suppressed because it is too large
Load diff
306
stratosphere/dmnt/source/cheat/impl/dmnt_cheat_vm.hpp
Normal file
306
stratosphere/dmnt/source/cheat/impl/dmnt_cheat_vm.hpp
Normal file
|
@ -0,0 +1,306 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2019 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include <stratosphere/dmnt.hpp>
|
||||||
|
|
||||||
|
namespace sts::dmnt::cheat::impl {
|
||||||
|
|
||||||
|
enum CheatVmOpcodeType : u32 {
|
||||||
|
CheatVmOpcodeType_StoreStatic = 0,
|
||||||
|
CheatVmOpcodeType_BeginConditionalBlock = 1,
|
||||||
|
CheatVmOpcodeType_EndConditionalBlock = 2,
|
||||||
|
CheatVmOpcodeType_ControlLoop = 3,
|
||||||
|
CheatVmOpcodeType_LoadRegisterStatic = 4,
|
||||||
|
CheatVmOpcodeType_LoadRegisterMemory = 5,
|
||||||
|
CheatVmOpcodeType_StoreStaticToAddress = 6,
|
||||||
|
CheatVmOpcodeType_PerformArithmeticStatic = 7,
|
||||||
|
CheatVmOpcodeType_BeginKeypressConditionalBlock = 8,
|
||||||
|
|
||||||
|
/* These are not implemented by Gateway's VM. */
|
||||||
|
CheatVmOpcodeType_PerformArithmeticRegister = 9,
|
||||||
|
CheatVmOpcodeType_StoreRegisterToAddress = 10,
|
||||||
|
CheatVmOpcodeType_Reserved11 = 11,
|
||||||
|
|
||||||
|
/* This is a meta entry, and not a real opcode. */
|
||||||
|
/* This is to facilitate multi-nybble instruction decoding. */
|
||||||
|
CheatVmOpcodeType_ExtendedWidth = 12,
|
||||||
|
|
||||||
|
/* Extended width opcodes. */
|
||||||
|
CheatVmOpcodeType_BeginRegisterConditionalBlock = 0xC0,
|
||||||
|
CheatVmOpcodeType_SaveRestoreRegister = 0xC1,
|
||||||
|
CheatVmOpcodeType_SaveRestoreRegisterMask = 0xC2,
|
||||||
|
|
||||||
|
/* This is a meta entry, and not a real opcode. */
|
||||||
|
/* This is to facilitate multi-nybble instruction decoding. */
|
||||||
|
CheatVmOpcodeType_DoubleExtendedWidth = 0xF0,
|
||||||
|
|
||||||
|
/* Double-extended width opcodes. */
|
||||||
|
CheatVmOpcodeType_DebugLog = 0xFFF,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum MemoryAccessType : u32 {
|
||||||
|
MemoryAccessType_MainNso = 0,
|
||||||
|
MemoryAccessType_Heap = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ConditionalComparisonType : u32 {
|
||||||
|
ConditionalComparisonType_GT = 1,
|
||||||
|
ConditionalComparisonType_GE = 2,
|
||||||
|
ConditionalComparisonType_LT = 3,
|
||||||
|
ConditionalComparisonType_LE = 4,
|
||||||
|
ConditionalComparisonType_EQ = 5,
|
||||||
|
ConditionalComparisonType_NE = 6,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum RegisterArithmeticType : u32 {
|
||||||
|
RegisterArithmeticType_Addition = 0,
|
||||||
|
RegisterArithmeticType_Subtraction = 1,
|
||||||
|
RegisterArithmeticType_Multiplication = 2,
|
||||||
|
RegisterArithmeticType_LeftShift = 3,
|
||||||
|
RegisterArithmeticType_RightShift = 4,
|
||||||
|
|
||||||
|
/* These are not supported by Gateway's VM. */
|
||||||
|
RegisterArithmeticType_LogicalAnd = 5,
|
||||||
|
RegisterArithmeticType_LogicalOr = 6,
|
||||||
|
RegisterArithmeticType_LogicalNot = 7,
|
||||||
|
RegisterArithmeticType_LogicalXor = 8,
|
||||||
|
|
||||||
|
RegisterArithmeticType_None = 9,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum StoreRegisterOffsetType : u32 {
|
||||||
|
StoreRegisterOffsetType_None = 0,
|
||||||
|
StoreRegisterOffsetType_Reg = 1,
|
||||||
|
StoreRegisterOffsetType_Imm = 2,
|
||||||
|
StoreRegisterOffsetType_MemReg = 3,
|
||||||
|
StoreRegisterOffsetType_MemImm = 4,
|
||||||
|
StoreRegisterOffsetType_MemImmReg = 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum CompareRegisterValueType : u32 {
|
||||||
|
CompareRegisterValueType_MemoryRelAddr = 0,
|
||||||
|
CompareRegisterValueType_MemoryOfsReg = 1,
|
||||||
|
CompareRegisterValueType_RegisterRelAddr = 2,
|
||||||
|
CompareRegisterValueType_RegisterOfsReg = 3,
|
||||||
|
CompareRegisterValueType_StaticValue = 4,
|
||||||
|
CompareRegisterValueType_OtherRegister = 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum SaveRestoreRegisterOpType : u32 {
|
||||||
|
SaveRestoreRegisterOpType_Restore = 0,
|
||||||
|
SaveRestoreRegisterOpType_Save = 1,
|
||||||
|
SaveRestoreRegisterOpType_ClearSaved = 2,
|
||||||
|
SaveRestoreRegisterOpType_ClearRegs = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum DebugLogValueType : u32 {
|
||||||
|
DebugLogValueType_MemoryRelAddr = 0,
|
||||||
|
DebugLogValueType_MemoryOfsReg = 1,
|
||||||
|
DebugLogValueType_RegisterRelAddr = 2,
|
||||||
|
DebugLogValueType_RegisterOfsReg = 3,
|
||||||
|
DebugLogValueType_RegisterValue = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
union VmInt {
|
||||||
|
u8 bit8;
|
||||||
|
u16 bit16;
|
||||||
|
u32 bit32;
|
||||||
|
u64 bit64;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StoreStaticOpcode {
|
||||||
|
u32 bit_width;
|
||||||
|
MemoryAccessType mem_type;
|
||||||
|
u32 offset_register;
|
||||||
|
u64 rel_address;
|
||||||
|
VmInt value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BeginConditionalOpcode {
|
||||||
|
u32 bit_width;
|
||||||
|
MemoryAccessType mem_type;
|
||||||
|
ConditionalComparisonType cond_type;
|
||||||
|
u64 rel_address;
|
||||||
|
VmInt value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EndConditionalOpcode {};
|
||||||
|
|
||||||
|
struct ControlLoopOpcode {
|
||||||
|
bool start_loop;
|
||||||
|
u32 reg_index;
|
||||||
|
u32 num_iters;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LoadRegisterStaticOpcode {
|
||||||
|
u32 reg_index;
|
||||||
|
u64 value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LoadRegisterMemoryOpcode {
|
||||||
|
u32 bit_width;
|
||||||
|
MemoryAccessType mem_type;
|
||||||
|
u32 reg_index;
|
||||||
|
bool load_from_reg;
|
||||||
|
u64 rel_address;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StoreStaticToAddressOpcode {
|
||||||
|
u32 bit_width;
|
||||||
|
u32 reg_index;
|
||||||
|
bool increment_reg;
|
||||||
|
bool add_offset_reg;
|
||||||
|
u32 offset_reg_index;
|
||||||
|
u64 value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PerformArithmeticStaticOpcode {
|
||||||
|
u32 bit_width;
|
||||||
|
u32 reg_index;
|
||||||
|
RegisterArithmeticType math_type;
|
||||||
|
u32 value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BeginKeypressConditionalOpcode {
|
||||||
|
u32 key_mask;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PerformArithmeticRegisterOpcode {
|
||||||
|
u32 bit_width;
|
||||||
|
RegisterArithmeticType math_type;
|
||||||
|
u32 dst_reg_index;
|
||||||
|
u32 src_reg_1_index;
|
||||||
|
u32 src_reg_2_index;
|
||||||
|
bool has_immediate;
|
||||||
|
VmInt value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StoreRegisterToAddressOpcode {
|
||||||
|
u32 bit_width;
|
||||||
|
u32 str_reg_index;
|
||||||
|
u32 addr_reg_index;
|
||||||
|
bool increment_reg;
|
||||||
|
StoreRegisterOffsetType ofs_type;
|
||||||
|
MemoryAccessType mem_type;
|
||||||
|
u32 ofs_reg_index;
|
||||||
|
u64 rel_address;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BeginRegisterConditionalOpcode {
|
||||||
|
u32 bit_width;
|
||||||
|
ConditionalComparisonType cond_type;
|
||||||
|
u32 val_reg_index;
|
||||||
|
CompareRegisterValueType comp_type;
|
||||||
|
MemoryAccessType mem_type;
|
||||||
|
u32 addr_reg_index;
|
||||||
|
u32 other_reg_index;
|
||||||
|
u32 ofs_reg_index;
|
||||||
|
u64 rel_address;
|
||||||
|
VmInt value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SaveRestoreRegisterOpcode {
|
||||||
|
u32 dst_index;
|
||||||
|
u32 src_index;
|
||||||
|
SaveRestoreRegisterOpType op_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SaveRestoreRegisterMaskOpcode {
|
||||||
|
SaveRestoreRegisterOpType op_type;
|
||||||
|
bool should_operate[0x10];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DebugLogOpcode {
|
||||||
|
u32 bit_width;
|
||||||
|
u32 log_id;
|
||||||
|
DebugLogValueType val_type;
|
||||||
|
MemoryAccessType mem_type;
|
||||||
|
u32 addr_reg_index;
|
||||||
|
u32 val_reg_index;
|
||||||
|
u32 ofs_reg_index;
|
||||||
|
u64 rel_address;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CheatVmOpcode {
|
||||||
|
CheatVmOpcodeType opcode;
|
||||||
|
bool begin_conditional_block;
|
||||||
|
union {
|
||||||
|
StoreStaticOpcode store_static;
|
||||||
|
BeginConditionalOpcode begin_cond;
|
||||||
|
EndConditionalOpcode end_cond;
|
||||||
|
ControlLoopOpcode ctrl_loop;
|
||||||
|
LoadRegisterStaticOpcode ldr_static;
|
||||||
|
LoadRegisterMemoryOpcode ldr_memory;
|
||||||
|
StoreStaticToAddressOpcode str_static;
|
||||||
|
PerformArithmeticStaticOpcode perform_math_static;
|
||||||
|
BeginKeypressConditionalOpcode begin_keypress_cond;
|
||||||
|
PerformArithmeticRegisterOpcode perform_math_reg;
|
||||||
|
StoreRegisterToAddressOpcode str_register;
|
||||||
|
BeginRegisterConditionalOpcode begin_reg_cond;
|
||||||
|
SaveRestoreRegisterOpcode save_restore_reg;
|
||||||
|
SaveRestoreRegisterMaskOpcode save_restore_regmask;
|
||||||
|
DebugLogOpcode debug_log;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
class CheatVirtualMachine {
|
||||||
|
public:
|
||||||
|
constexpr static size_t MaximumProgramOpcodeCount = 0x400;
|
||||||
|
constexpr static size_t NumRegisters = 0x10;
|
||||||
|
private:
|
||||||
|
size_t num_opcodes = 0;
|
||||||
|
size_t instruction_ptr = 0;
|
||||||
|
size_t condition_depth = 0;
|
||||||
|
bool decode_success = false;
|
||||||
|
u32 program[MaximumProgramOpcodeCount] = {0};
|
||||||
|
u64 registers[NumRegisters] = {0};
|
||||||
|
u64 saved_values[NumRegisters] = {0};
|
||||||
|
size_t loop_tops[NumRegisters] = {0};
|
||||||
|
private:
|
||||||
|
bool DecodeNextOpcode(CheatVmOpcode *out);
|
||||||
|
void SkipConditionalBlock();
|
||||||
|
void ResetState();
|
||||||
|
|
||||||
|
/* For implementing the DebugLog opcode. */
|
||||||
|
void DebugLog(u32 log_id, u64 value);
|
||||||
|
|
||||||
|
/* For debugging. These will be IFDEF'd out normally. */
|
||||||
|
void OpenDebugLogFile();
|
||||||
|
void CloseDebugLogFile();
|
||||||
|
void LogToDebugFile(const char *format, ...);
|
||||||
|
void LogOpcode(const CheatVmOpcode *opcode);
|
||||||
|
|
||||||
|
static u64 GetVmInt(VmInt value, u32 bit_width);
|
||||||
|
static u64 GetCheatProcessAddress(const CheatProcessMetadata* metadata, MemoryAccessType mem_type, u64 rel_address);
|
||||||
|
public:
|
||||||
|
CheatVirtualMachine() { }
|
||||||
|
|
||||||
|
size_t GetProgramSize() {
|
||||||
|
return this->num_opcodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LoadProgram(const CheatEntry *cheats, size_t num_cheats);
|
||||||
|
void Execute(const CheatProcessMetadata *metadata);
|
||||||
|
#ifdef DMNT_CHEAT_VM_DEBUG_LOG
|
||||||
|
private:
|
||||||
|
FILE *debug_log_file = NULL;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
@ -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();
|
|
||||||
};
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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),
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -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
|
@ -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
|
|
||||||
};
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -24,9 +24,7 @@
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
#include "dmnt_service.hpp"
|
#include "dmnt_service.hpp"
|
||||||
#include "dmnt_cheat_service.hpp"
|
#include "cheat/dmnt_cheat_service.hpp"
|
||||||
#include "dmnt_cheat_manager.hpp"
|
|
||||||
#include "dmnt_config.hpp"
|
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
extern u32 __start__;
|
extern u32 __start__;
|
||||||
|
@ -96,14 +94,6 @@ void __appExit(void) {
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
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. */
|
/* Nintendo uses four threads. Add a fifth for our cheat service. */
|
||||||
static auto s_server_manager = WaitableManager(5);
|
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. */
|
/* TODO: Implement rest of dmnt:- in ams.tma development branch. */
|
||||||
/* server_manager->AddWaitable(new ServiceServer<DebugMonitorService>("dmnt:-", 4)); */
|
/* server_manager->AddWaitable(new ServiceServer<DebugMonitorService>("dmnt:-", 4)); */
|
||||||
|
s_server_manager.AddWaitable(new ServiceServer<sts::dmnt::cheat::CheatService>("dmnt:cht", 1));
|
||||||
|
|
||||||
s_server_manager.AddWaitable(new ServiceServer<DmntCheatService>("dmnt:cht", 1));
|
|
||||||
|
|
||||||
/* Loop forever, servicing our services. */
|
/* Loop forever, servicing our services. */
|
||||||
s_server_manager.Process();
|
s_server_manager.Process();
|
||||||
|
|
|
@ -18,133 +18,137 @@
|
||||||
#include <switch.h>
|
#include <switch.h>
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
class DebugMonitorService final : public IServiceObject {
|
namespace sts::dmnt {
|
||||||
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);
|
class DebugMonitorService final : public IServiceObject {
|
||||||
Result TargetIO_FileClose(InBuffer<u64> hnd);
|
private:
|
||||||
Result TargetIO_FileRead(InBuffer<u64> hnd, OutBuffer<u8, BufferType_Type1> out_data, Out<u32> out_read, u64 offset);
|
enum class CommandId {
|
||||||
Result TargetIO_FileWrite(InBuffer<u64> hnd, InBuffer<u8, BufferType_Type1> data, Out<u32> out_written, u64 offset);
|
BreakDebugProcess = 0,
|
||||||
Result TargetIO_FileSetAttributes(InBuffer<char> path, InBuffer<u8> attributes);
|
TerminateDebugProcess = 1,
|
||||||
Result TargetIO_FileGetInformation(InBuffer<char> path, OutBuffer<u64> out_info, Out<int> is_directory);
|
CloseHandle = 2,
|
||||||
Result TargetIO_FileSetTime(InBuffer<char> path, u64 create, u64 access, u64 modify);
|
LoadImage = 3,
|
||||||
Result TargetIO_FileSetSize(InBuffer<char> path, u64 size);
|
GetProcessId = 4,
|
||||||
Result TargetIO_FileDelete(InBuffer<char> path);
|
GetProcessHandle = 5,
|
||||||
Result TargetIO_FileMove(InBuffer<char> path0, InBuffer<char> path1);
|
WaitSynchronization = 6,
|
||||||
public:
|
GetDebugEvent = 7,
|
||||||
DEFINE_SERVICE_DISPATCH_TABLE {
|
GetProcessModuleInfo = 8,
|
||||||
MAKE_SERVICE_COMMAND_META(DebugMonitorService, BreakDebugProcess),
|
GetProcessList = 9,
|
||||||
MAKE_SERVICE_COMMAND_META(DebugMonitorService, TerminateDebugProcess),
|
GetThreadList = 10,
|
||||||
MAKE_SERVICE_COMMAND_META(DebugMonitorService, CloseHandle),
|
GetDebugThreadContext = 11,
|
||||||
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, LoadImage),
|
ContinueDebugEvent = 12,
|
||||||
MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetProcessId),
|
ReadDebugProcessMemory = 13,
|
||||||
MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetProcessHandle),
|
WriteDebugProcessMemory = 14,
|
||||||
MAKE_SERVICE_COMMAND_META(DebugMonitorService, WaitSynchronization),
|
SetDebugThreadContext = 15,
|
||||||
//MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetDebugEvent),
|
GetDebugThreadParam = 16,
|
||||||
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetProcessModuleInfo),
|
InitializeThreadInfo = 17,
|
||||||
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetProcessList),
|
SetHardwareBreakPoint = 18,
|
||||||
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetThreadList),
|
QueryDebugProcessMemory = 19,
|
||||||
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetDebugThreadContext),
|
GetProcessMemoryDetails = 20,
|
||||||
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, ContinueDebugEvent),
|
AttachByProgramId = 21,
|
||||||
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, ReadDebugProcessMemory),
|
AttachOnLaunch = 22,
|
||||||
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, WriteDebugProcessMemory),
|
GetDebugMonitorProcessId = 23,
|
||||||
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, SetDebugThreadContext),
|
GetJitDebugProcessList = 25,
|
||||||
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetDebugThreadParam),
|
CreateCoreDump = 26,
|
||||||
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, InitializeThreadInfo),
|
GetAllDebugThreadInfo = 27,
|
||||||
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, SetHardwareBreakPoint),
|
TargetIO_FileOpen = 29,
|
||||||
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, QueryDebugProcessMemory),
|
TargetIO_FileClose = 30,
|
||||||
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetProcessMemoryDetails),
|
TargetIO_FileRead = 31,
|
||||||
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, AttachByProgramId),
|
TargetIO_FileWrite = 32,
|
||||||
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, AttachOnLaunch),
|
TargetIO_FileSetAttributes = 33,
|
||||||
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetDebugMonitorProcessId),
|
TargetIO_FileGetInformation = 34,
|
||||||
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetJitDebugProcessList),
|
TargetIO_FileSetTime = 35,
|
||||||
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, CreateCoreDump),
|
TargetIO_FileSetSize = 36,
|
||||||
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetAllDebugThreadInfo),
|
TargetIO_FileDelete = 37,
|
||||||
MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_FileOpen),
|
TargetIO_FileMove = 38,
|
||||||
MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_FileClose),
|
TargetIO_DirectoryCreate = 39,
|
||||||
MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_FileRead),
|
TargetIO_DirectoryDelete = 40,
|
||||||
MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_FileWrite),
|
TargetIO_DirectoryRename = 41,
|
||||||
MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_FileSetAttributes),
|
TargetIO_DirectoryGetCount = 42,
|
||||||
MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_FileGetInformation),
|
TargetIO_DirectoryOpen = 43,
|
||||||
MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_FileSetTime),
|
TargetIO_DirectoryGetNext = 44,
|
||||||
MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_FileSetSize),
|
TargetIO_DirectoryClose = 45,
|
||||||
MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_FileDelete),
|
TargetIO_GetFreeSpace = 46,
|
||||||
MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_FileMove),
|
TargetIO_GetVolumeInformation = 47,
|
||||||
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_DirectoryCreate),
|
InitiateCoreDump = 48,
|
||||||
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_DirectoryDelete),
|
ContinueCoreDump = 49,
|
||||||
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_DirectoryRename),
|
AddTTYToCoreDump = 50,
|
||||||
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_DirectoryGetCount),
|
AddImageToCoreDump = 51,
|
||||||
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_DirectoryOpen),
|
CloseCoreDump = 52,
|
||||||
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_DirectoryGetNext),
|
CancelAttach = 53,
|
||||||
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_DirectoryClose),
|
};
|
||||||
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_GetFreeSpace),
|
private:
|
||||||
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, TargetIO_GetVolumeInformation),
|
Result BreakDebugProcess(Handle debug_hnd);
|
||||||
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, InitiateCoreDump),
|
Result TerminateDebugProcess(Handle debug_hnd);
|
||||||
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, ContinueCoreDump),
|
Result CloseHandle(Handle debug_hnd);
|
||||||
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, AddTTYToCoreDump),
|
Result GetProcessId(Out<u64> out_pid, Handle hnd);
|
||||||
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, AddImageToCoreDump),
|
Result GetProcessHandle(Out<Handle> out_hnd, u64 pid);
|
||||||
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, CloseCoreDump),
|
Result WaitSynchronization(Handle hnd, u64 ns);
|
||||||
// MAKE_SERVICE_COMMAND_META(DebugMonitorService, CancelAttach),
|
|
||||||
};
|
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),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -14,41 +14,44 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <switch.h>
|
|
||||||
#include "dmnt_service.hpp"
|
#include "dmnt_service.hpp"
|
||||||
|
|
||||||
Result DebugMonitorService::BreakDebugProcess(Handle debug_hnd) {
|
namespace sts::dmnt {
|
||||||
/* Nintendo discards the output of this command, but we will return it. */
|
|
||||||
return svcBreakDebugProcess(debug_hnd);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DebugMonitorService::TerminateDebugProcess(Handle debug_hnd) {
|
Result DebugMonitorService::BreakDebugProcess(Handle debug_hnd) {
|
||||||
/* Nintendo discards the output of this command, but we will return it. */
|
/* Nintendo discards the output of this command, but we will return it. */
|
||||||
return svcTerminateDebugProcess(debug_hnd);
|
return svcBreakDebugProcess(debug_hnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result DebugMonitorService::CloseHandle(Handle debug_hnd) {
|
Result DebugMonitorService::TerminateDebugProcess(Handle debug_hnd) {
|
||||||
/* Nintendo discards the output of this command, but we will return it. */
|
/* Nintendo discards the output of this command, but we will return it. */
|
||||||
/* This command is, entertainingly, also pretty unsafe in general... */
|
return svcTerminateDebugProcess(debug_hnd);
|
||||||
return svcCloseHandle(debug_hnd);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Result DebugMonitorService::GetProcessId(Out<u64> out_pid, Handle hnd) {
|
Result DebugMonitorService::CloseHandle(Handle debug_hnd) {
|
||||||
/* Nintendo discards the output of this command, but we will return it. */
|
/* Nintendo discards the output of this command, but we will return it. */
|
||||||
return svcGetProcessId(out_pid.GetPointer(), hnd);
|
/* This command is, entertainingly, also pretty unsafe in general... */
|
||||||
}
|
return svcCloseHandle(debug_hnd);
|
||||||
|
}
|
||||||
|
|
||||||
Result DebugMonitorService::GetProcessHandle(Out<Handle> out_hnd, u64 pid) {
|
Result DebugMonitorService::GetProcessId(Out<u64> out_pid, Handle hnd) {
|
||||||
R_TRY_CATCH(svcDebugActiveProcess(out_hnd.GetPointer(), pid)) {
|
/* Nintendo discards the output of this command, but we will return it. */
|
||||||
R_CATCH(ResultKernelAlreadyExists) {
|
return svcGetProcessId(out_pid.GetPointer(), hnd);
|
||||||
return ResultDebugAlreadyAttached;
|
}
|
||||||
}
|
|
||||||
} R_END_TRY_CATCH;
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,259 +15,266 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <switch.h>
|
|
||||||
#include "dmnt_service.hpp"
|
#include "dmnt_service.hpp"
|
||||||
|
|
||||||
enum TIOCreateOption : u32 {
|
namespace sts::dmnt {
|
||||||
TIOCreateOption_CreateNew = 1,
|
|
||||||
TIOCreateOption_CreateAlways = 2,
|
|
||||||
TIOCreateOption_OpenExisting = 3,
|
|
||||||
TIOCreateOption_OpenAlways = 4,
|
|
||||||
TIOCreateOption_ResetSize = 5,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Nintendo uses actual pointers as file handles. We'll add a layer of indirection... */
|
namespace {
|
||||||
static bool g_sd_initialized = false;
|
|
||||||
static HosMutex g_sd_lock;
|
|
||||||
static FsFileSystem g_sd_fs;
|
|
||||||
|
|
||||||
static HosMutex g_file_handle_lock;
|
enum TIOCreateOption : u32 {
|
||||||
static u64 g_cur_fd = 0;
|
TIOCreateOption_CreateNew = 1,
|
||||||
static std::unordered_map<u64, FsFile> g_file_handles;
|
TIOCreateOption_CreateAlways = 2,
|
||||||
|
TIOCreateOption_OpenExisting = 3,
|
||||||
|
TIOCreateOption_OpenAlways = 4,
|
||||||
|
TIOCreateOption_ResetSize = 5,
|
||||||
|
};
|
||||||
|
|
||||||
static Result EnsureSdInitialized() {
|
/* Nintendo uses actual pointers as file handles. We'll add a layer of indirection... */
|
||||||
std::scoped_lock<HosMutex> lk(g_sd_lock);
|
bool g_sd_initialized = false;
|
||||||
if (g_sd_initialized) {
|
HosMutex g_sd_lock;
|
||||||
return ResultSuccess;
|
FsFileSystem g_sd_fs;
|
||||||
}
|
|
||||||
|
|
||||||
R_TRY(fsMountSdcard(&g_sd_fs));
|
HosMutex g_file_handle_lock;
|
||||||
g_sd_initialized = true;
|
u64 g_cur_fd;
|
||||||
return ResultSuccess;
|
std::unordered_map<u64, FsFile> g_file_handles;
|
||||||
}
|
|
||||||
|
|
||||||
static u64 GetNewFileHandle(FsFile f) {
|
Result EnsureSdInitialized() {
|
||||||
std::scoped_lock<HosMutex> lk(g_file_handle_lock);
|
std::scoped_lock<HosMutex> lk(g_sd_lock);
|
||||||
|
if (g_sd_initialized) {
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
u64 fd = g_cur_fd++;
|
R_TRY(fsMountSdcard(&g_sd_fs));
|
||||||
g_file_handles[fd] = f;
|
g_sd_initialized = true;
|
||||||
return fd;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Result GetFileByHandle(FsFile *out, u64 handle) {
|
u64 GetNewFileHandle(FsFile f) {
|
||||||
std::scoped_lock<HosMutex> lk(g_file_handle_lock);
|
std::scoped_lock<HosMutex> lk(g_file_handle_lock);
|
||||||
|
|
||||||
if (g_file_handles.find(handle) != g_file_handles.end()) {
|
u64 fd = g_cur_fd++;
|
||||||
*out = g_file_handles[handle];
|
g_file_handles[fd] = f;
|
||||||
return ResultSuccess;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResultFsInvalidArgument;
|
Result GetFileByHandle(FsFile *out, u64 handle) {
|
||||||
}
|
std::scoped_lock<HosMutex> lk(g_file_handle_lock);
|
||||||
|
|
||||||
static Result CloseFileByHandle(u64 handle) {
|
if (g_file_handles.find(handle) != g_file_handles.end()) {
|
||||||
std::scoped_lock<HosMutex> lk(g_file_handle_lock);
|
*out = g_file_handles[handle];
|
||||||
|
return ResultSuccess;
|
||||||
if (g_file_handles.find(handle) != g_file_handles.end()) {
|
}
|
||||||
fsFileClose(&g_file_handles[handle]);
|
|
||||||
g_file_handles.erase(handle);
|
return ResultFsInvalidArgument;
|
||||||
return ResultSuccess;
|
}
|
||||||
}
|
|
||||||
|
Result CloseFileByHandle(u64 handle) {
|
||||||
return ResultFsInvalidArgument;
|
std::scoped_lock<HosMutex> lk(g_file_handle_lock);
|
||||||
}
|
|
||||||
|
if (g_file_handles.find(handle) != g_file_handles.end()) {
|
||||||
static void FixPath(char *dst, size_t dst_size, InBuffer<char> &path) {
|
fsFileClose(&g_file_handles[handle]);
|
||||||
dst[dst_size - 1] = 0;
|
g_file_handles.erase(handle);
|
||||||
strncpy(dst, "/", dst_size - 1);
|
return ResultSuccess;
|
||||||
|
}
|
||||||
const char *src = path.buffer;
|
|
||||||
size_t src_idx = 0;
|
return ResultFsInvalidArgument;
|
||||||
size_t dst_idx = 1;
|
}
|
||||||
while (src_idx < path.num_elements && (src[src_idx] == '/' || src[src_idx] == '\\')) {
|
|
||||||
src_idx++;
|
void FixPath(char *dst, size_t dst_size, InBuffer<char> &path) {
|
||||||
}
|
dst[dst_size - 1] = 0;
|
||||||
|
strncpy(dst, "/", dst_size - 1);
|
||||||
while (src_idx < path.num_elements && dst_idx < dst_size - 1 && src[src_idx] != 0) {
|
|
||||||
if (src[src_idx] == '\\') {
|
const char *src = path.buffer;
|
||||||
dst[dst_idx] = '/';
|
size_t src_idx = 0;
|
||||||
} else {
|
size_t dst_idx = 1;
|
||||||
dst[dst_idx] = src[src_idx];
|
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) {
|
Result DebugMonitorService::TargetIO_FileOpen(OutBuffer<u64> out_hnd, InBuffer<char> path, int open_mode, u32 create_mode) {
|
||||||
dst[dst_idx] = 0;
|
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) {
|
R_TRY(EnsureSdInitialized());
|
||||||
if (out_hnd.num_elements != 1) {
|
|
||||||
/* Serialization error. */
|
char fs_path[FS_MAX_PATH];
|
||||||
return ResultKernelConnectionClosed;
|
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];
|
return CloseFileByHandle(hnd[0]);
|
||||||
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. */
|
Result DebugMonitorService::TargetIO_FileRead(InBuffer<u64> hnd, OutBuffer<u8, BufferType_Type1> out_data, Out<u32> out_read, u64 offset) {
|
||||||
FsFile f;
|
if (hnd.num_elements != 1) {
|
||||||
R_TRY(fsFsOpenFile(&g_sd_fs, fs_path, open_mode, &f));
|
/* Serialization error. */
|
||||||
auto file_guard = SCOPE_GUARD {
|
return ResultKernelConnectionClosed;
|
||||||
fsFileClose(&f);
|
}
|
||||||
};
|
|
||||||
|
|
||||||
/* Set size if needed. */
|
FsFile f;
|
||||||
if (create_mode == TIOCreateOption_ResetSize) {
|
size_t read = 0;
|
||||||
R_TRY(fsFileSetSize(&f, 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. */
|
Result DebugMonitorService::TargetIO_FileWrite(InBuffer<u64> hnd, InBuffer<u8, BufferType_Type1> data, Out<u32> out_written, u64 offset) {
|
||||||
file_guard.Cancel();
|
if (hnd.num_elements != 1) {
|
||||||
out_hnd[0] = GetNewFileHandle(f);
|
/* Serialization error. */
|
||||||
|
return ResultKernelConnectionClosed;
|
||||||
|
}
|
||||||
|
|
||||||
return ResultSuccess;
|
FsFile f;
|
||||||
}
|
|
||||||
|
|
||||||
Result DebugMonitorService::TargetIO_FileClose(InBuffer<u64> hnd) {
|
R_TRY(GetFileByHandle(&f, hnd[0]));
|
||||||
if (hnd.num_elements != 1) {
|
R_TRY(fsFileWrite(&f, offset, data.buffer, data.num_elements, FS_WRITEOPTION_NONE));
|
||||||
/* Serialization error. */
|
|
||||||
return ResultKernelConnectionClosed;
|
out_written.SetValue(data.num_elements);
|
||||||
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
return CloseFileByHandle(hnd[0]);
|
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. */
|
||||||
Result DebugMonitorService::TargetIO_FileRead(InBuffer<u64> hnd, OutBuffer<u8, BufferType_Type1> out_data, Out<u32> out_read, u64 offset) {
|
return ResultSuccess;
|
||||||
if (hnd.num_elements != 1) {
|
|
||||||
/* Serialization error. */
|
|
||||||
return ResultKernelConnectionClosed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FsFile f;
|
Result DebugMonitorService::TargetIO_FileGetInformation(InBuffer<char> path, OutBuffer<u64> out_info, Out<int> is_directory) {
|
||||||
size_t read = 0;
|
if (out_info.num_elements != 4) {
|
||||||
|
/* Serialization error. */
|
||||||
|
return ResultKernelConnectionClosed;
|
||||||
|
}
|
||||||
|
|
||||||
R_TRY(GetFileByHandle(&f, hnd[0]));
|
R_TRY(EnsureSdInitialized());
|
||||||
R_TRY(fsFileRead(&f, offset, out_data.buffer, out_data.num_elements, FS_READOPTION_NONE, &read));
|
|
||||||
|
|
||||||
out_read.SetValue(static_cast<u32>(read));
|
char fs_path[FS_MAX_PATH];
|
||||||
return ResultSuccess;
|
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) {
|
for (size_t i = 0; i < out_info.num_elements; i++) {
|
||||||
if (hnd.num_elements != 1) {
|
out_info[i] = 0;
|
||||||
/* Serialization error. */
|
}
|
||||||
return ResultKernelConnectionClosed;
|
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;
|
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. */
|
||||||
R_TRY(GetFileByHandle(&f, hnd[0]));
|
return ResultSuccess;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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];
|
/* We will try to be better than N, here. N only treats input as a path. */
|
||||||
FixPath(fs_path, sizeof(fs_path), 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++) {
|
R_TRY(EnsureSdInitialized());
|
||||||
out_info[i] = 0;
|
|
||||||
}
|
|
||||||
is_directory.SetValue(0);
|
|
||||||
|
|
||||||
FsFile f;
|
char fs_path[FS_MAX_PATH];
|
||||||
if (R_SUCCEEDED(fsFsOpenFile(&g_sd_fs, fs_path, FS_OPEN_READ, &f))) {
|
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); };
|
ON_SCOPE_EXIT { fsFileClose(&f); };
|
||||||
|
|
||||||
/* N doesn't check this return code. */
|
return fsFileSetSize(&f, size);
|
||||||
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;
|
Result DebugMonitorService::TargetIO_FileDelete(InBuffer<char> path) {
|
||||||
}
|
R_TRY(EnsureSdInitialized());
|
||||||
|
|
||||||
Result DebugMonitorService::TargetIO_FileSetTime(InBuffer<char> path, u64 create, u64 access, u64 modify) {
|
char fs_path[FS_MAX_PATH];
|
||||||
/* This is another function that doesn't really need to exist, because Horizon doesn't let you set anything. */
|
FixPath(fs_path, sizeof(fs_path), path);
|
||||||
return ResultSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DebugMonitorService::TargetIO_FileSetSize(InBuffer<char> input, u64 size) {
|
return fsFsDeleteFile(&g_sd_fs, fs_path);
|
||||||
/* 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
R_TRY(EnsureSdInitialized());
|
Result DebugMonitorService::TargetIO_FileMove(InBuffer<char> path0, InBuffer<char> path1) {
|
||||||
|
R_TRY(EnsureSdInitialized());
|
||||||
|
|
||||||
char fs_path[FS_MAX_PATH];
|
char fs_path0[FS_MAX_PATH];
|
||||||
FixPath(fs_path, sizeof(fs_path), input);
|
char fs_path1[FS_MAX_PATH];
|
||||||
|
FixPath(fs_path0, sizeof(fs_path0), path0);
|
||||||
|
FixPath(fs_path1, sizeof(fs_path1), path1);
|
||||||
|
|
||||||
FsFile f;
|
return fsFsRenameFile(&g_sd_fs, fs_path0, fs_path1);
|
||||||
R_TRY(fsFsOpenFile(&g_sd_fs, fs_path, FS_OPEN_WRITE, &f));
|
}
|
||||||
ON_SCOPE_EXIT { fsFileClose(&f); };
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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__ */
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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
|
|
|
@ -32,7 +32,7 @@ CFLAGS := -g -Wall -O2 -ffunction-sections \
|
||||||
|
|
||||||
CFLAGS += $(INCLUDE) -D__SWITCH__
|
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)
|
ASFLAGS := -g $(ARCH)
|
||||||
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
||||||
|
|
|
@ -17,7 +17,4 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <switch.h>
|
#include <switch.h>
|
||||||
|
|
||||||
class HidManagement {
|
#include "dmnt/dmnt_cheat_types.hpp"
|
||||||
public:
|
|
||||||
static Result GetKeysDown(u64 *keys);
|
|
||||||
};
|
|
|
@ -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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -224,24 +224,33 @@ class HosSignal {
|
||||||
mutexUnlock(&m);
|
mutexUnlock(&m);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Wait() {
|
void Wait(bool reset = false) {
|
||||||
mutexLock(&m);
|
mutexLock(&m);
|
||||||
|
|
||||||
while (!signaled) {
|
while (!signaled) {
|
||||||
condvarWait(&cv, &m);
|
condvarWait(&cv, &m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (reset) {
|
||||||
|
this->signaled = false;
|
||||||
|
}
|
||||||
|
|
||||||
mutexUnlock(&m);
|
mutexUnlock(&m);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TryWait() {
|
bool TryWait(bool reset = false) {
|
||||||
mutexLock(&m);
|
mutexLock(&m);
|
||||||
|
|
||||||
bool success = signaled;
|
bool success = signaled;
|
||||||
|
if (reset) {
|
||||||
|
this->signaled = false;
|
||||||
|
}
|
||||||
|
|
||||||
mutexUnlock(&m);
|
mutexUnlock(&m);
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result TimedWait(u64 ns) {
|
Result TimedWait(u64 ns, bool reset = false) {
|
||||||
mutexLock(&m);
|
mutexLock(&m);
|
||||||
TimeoutHelper timeout_helper(ns);
|
TimeoutHelper timeout_helper(ns);
|
||||||
|
|
||||||
|
@ -251,6 +260,10 @@ class HosSignal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (reset) {
|
||||||
|
this->signaled = false;
|
||||||
|
}
|
||||||
|
|
||||||
mutexUnlock(&m);
|
mutexUnlock(&m);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,4 +20,5 @@
|
||||||
#include "pm/pm_types.hpp"
|
#include "pm/pm_types.hpp"
|
||||||
#include "pm/pm_boot_mode_api.hpp"
|
#include "pm/pm_boot_mode_api.hpp"
|
||||||
#include "pm/pm_info_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"
|
|
@ -14,23 +14,19 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <switch.h>
|
#pragma once
|
||||||
#include <string.h>
|
|
||||||
#include <stratosphere.hpp>
|
|
||||||
|
|
||||||
#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) {
|
/* Debug Monitor API. */
|
||||||
std::scoped_lock<HosMutex> lk(g_hid_keys_down_lock);
|
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;
|
|
||||||
}
|
}
|
|
@ -94,3 +94,94 @@ Result pminfoAtmosphereHasLaunchedTitle(bool *out, u64 tid) {
|
||||||
|
|
||||||
return rc;
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -14,6 +14,9 @@ extern "C" {
|
||||||
Result pminfoAtmosphereGetProcessId(u64 *out_pid, u64 tid);
|
Result pminfoAtmosphereGetProcessId(u64 *out_pid, u64 tid);
|
||||||
Result pminfoAtmosphereHasLaunchedTitle(bool *out, 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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
54
stratosphere/libstratosphere/source/pm/pm_dmnt_api.cpp
Normal file
54
stratosphere/libstratosphere/source/pm/pm_dmnt_api.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue