diff --git a/dist/scripting/citra.py b/dist/scripting/citra.py index 507662033..0336f2de5 100644 --- a/dist/scripting/citra.py +++ b/dist/scripting/citra.py @@ -1,15 +1,21 @@ +# Copyright Citra Emulator Project / Azahar Emulator Project +# Licensed under GPLv2 or any later version +# Refer to the license.txt file included. + import struct import random import enum import socket CURRENT_REQUEST_VERSION = 1 -MAX_REQUEST_DATA_SIZE = 32 -MAX_PACKET_SIZE = 48 +MAX_REQUEST_DATA_SIZE = 1024 +MAX_PACKET_SIZE = 1024 + 0x10 class RequestType(enum.IntEnum): ReadMemory = 1, - WriteMemory = 2 + WriteMemory = 2, + ProcessList = 3, + SetGetProcess = 4, CITRA_PORT = 45987 @@ -34,6 +40,55 @@ class Citra: return raw_reply[4*4:] return None + def process_list(self): + processes = {} + read_processes = 0 + while True: + request_data = struct.pack("II", read_processes, 0x7FFFFFFF) + request, request_id = self._generate_header(RequestType.ProcessList, len(request_data)) + request += request_data + self.socket.sendto(request, (self.address, CITRA_PORT)) + + raw_reply = self.socket.recv(MAX_PACKET_SIZE) + reply_data = self._read_and_validate_header(raw_reply, request_id, RequestType.ProcessList) + + if reply_data: + read_count = struct.unpack("I", reply_data[0:4])[0] + reply_data = reply_data[4:] + if read_count == 0: + break + read_processes += read_count + for i in range(read_count): + proc_data = reply_data[i * 0x14 : (i + 1) * 0x14] + proc_id, title_id, proc_name = struct.unpack(">> c.read_memory(0x100000, 4) diff --git a/src/core/cheats/cheat_base.h b/src/core/cheats/cheat_base.h index d8a5924cd..634b1643c 100644 --- a/src/core/cheats/cheat_base.h +++ b/src/core/cheats/cheat_base.h @@ -1,10 +1,11 @@ -// Copyright 2018 Citra Emulator Project +// Copyright Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once #include +#include "common/common_types.h" namespace Core { class System; @@ -14,7 +15,7 @@ namespace Cheats { class CheatBase { public: virtual ~CheatBase(); - virtual void Execute(Core::System& system) const = 0; + virtual void Execute(Core::System& system, u32 process_id) const = 0; virtual bool IsEnabled() const = 0; virtual void SetEnabled(bool enabled) = 0; diff --git a/src/core/cheats/cheats.cpp b/src/core/cheats/cheats.cpp index 30b8e56cc..2cdb75ad1 100644 --- a/src/core/cheats/cheats.cpp +++ b/src/core/cheats/cheats.cpp @@ -1,4 +1,4 @@ -// Copyright 2018 Citra Emulator Project +// Copyright Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -25,7 +25,8 @@ CheatEngine::~CheatEngine() { } } -void CheatEngine::Connect() { +void CheatEngine::Connect(u32 process_id) { + this->process_id = process_id; event = system.CoreTiming().RegisterEvent( "CheatCore::run_event", [this](u64 thread_id, s64 cycle_late) { RunCallback(thread_id, cycle_late); }); @@ -107,7 +108,7 @@ void CheatEngine::RunCallback([[maybe_unused]] std::uintptr_t user_data, s64 cyc std::shared_lock lock{cheats_list_mutex}; for (const auto& cheat : cheats_list) { if (cheat->IsEnabled()) { - cheat->Execute(system); + cheat->Execute(system, process_id); } } } diff --git a/src/core/cheats/cheats.h b/src/core/cheats/cheats.h index 8f8127d74..c3a9e840c 100644 --- a/src/core/cheats/cheats.h +++ b/src/core/cheats/cheats.h @@ -1,4 +1,4 @@ -// Copyright 2018 Citra Emulator Project +// Copyright Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -30,7 +30,7 @@ public: ~CheatEngine(); /// Registers the cheat execution callback. - void Connect(); + void Connect(u32 process_id); /// Returns a span of the currently active cheats. std::span> GetCheats() const; @@ -50,6 +50,10 @@ public: /// Saves currently active cheats to file for the specified title id. void SaveCheatFile(u64 title_id) const; + u32 GetConnectedPID() const { + return process_id; + } + private: /// The cheat execution callback. void RunCallback(std::uintptr_t user_data, s64 cycles_late); @@ -58,6 +62,7 @@ private: Core::System& system; Core::TimingEventType* event; std::optional loaded_title_id; + u32 process_id = 0xFFFFFFFF; std::vector> cheats_list; mutable std::shared_mutex cheats_list_mutex; }; diff --git a/src/core/cheats/gateway_cheat.cpp b/src/core/cheats/gateway_cheat.cpp index b30fda542..176264b7d 100644 --- a/src/core/cheats/gateway_cheat.cpp +++ b/src/core/cheats/gateway_cheat.cpp @@ -56,10 +56,10 @@ static inline std::enable_if_t> CompOp(const GatewayCheat: } } -static inline void LoadOffsetOp(Memory::MemorySystem& memory, const GatewayCheat::CheatLine& line, - State& state) { +static inline void LoadOffsetOp(Memory::MemorySystem& memory, const Kernel::Process& process, + const GatewayCheat::CheatLine& line, State& state) { u32 addr = line.address + state.offset; - state.offset = memory.Read32(addr); + state.offset = memory.Read32(process, addr); } static inline void LoopOp(const GatewayCheat::CheatLine& line, State& state) { @@ -145,6 +145,7 @@ static inline void JokerOp(const GatewayCheat::CheatLine& line, State& state, } static inline void PatchOp(const GatewayCheat::CheatLine& line, State& state, Core::System& system, + const Kernel::Process& process, std::span cheat_lines) { if (state.if_flag > 0) { // Skip over the additional patch lines @@ -166,7 +167,7 @@ static inline void PatchOp(const GatewayCheat::CheatLine& line, State& state, Co state.current_line_nr++; } first = !first; - system.Memory().Write32(addr, tmp); + system.Memory().Write32(process, addr, tmp); addr += 4; num_bytes -= 4; } @@ -174,7 +175,7 @@ static inline void PatchOp(const GatewayCheat::CheatLine& line, State& state, Co u32 tmp = (first ? cheat_lines[state.current_line_nr].first : cheat_lines[state.current_line_nr].value) >> bit_offset; - system.Memory().Write8(addr, static_cast(tmp)); + system.Memory().Write8(process, addr, static_cast(tmp)); addr += 1; num_bytes -= 1; bit_offset += 8; @@ -229,16 +230,27 @@ GatewayCheat::GatewayCheat(std::string name_, std::string code, std::string comm GatewayCheat::~GatewayCheat() = default; -void GatewayCheat::Execute(Core::System& system) const { +void GatewayCheat::Execute(Core::System& system, u32 process_id) const { State state; Memory::MemorySystem& memory = system.Memory(); - auto Read8 = [&memory](VAddr addr) { return memory.Read8(addr); }; - auto Read16 = [&memory](VAddr addr) { return memory.Read16(addr); }; - auto Read32 = [&memory](VAddr addr) { return memory.Read32(addr); }; - auto Write8 = [&memory](VAddr addr, u8 value) { memory.Write8(addr, value); }; - auto Write16 = [&memory](VAddr addr, u16 value) { memory.Write16(addr, value); }; - auto Write32 = [&memory](VAddr addr, u32 value) { memory.Write32(addr, value); }; + std::shared_ptr process = system.Kernel().GetProcessById(process_id); + if (!process) { + return; + } + + auto Read8 = [&memory, &process](VAddr addr) { return memory.Read8(*process, addr); }; + auto Read16 = [&memory, &process](VAddr addr) { return memory.Read16(*process, addr); }; + auto Read32 = [&memory, &process](VAddr addr) { return memory.Read32(*process, addr); }; + auto Write8 = [&memory, &process](VAddr addr, u8 value) { + memory.Write8(*process, addr, value); + }; + auto Write16 = [&memory, &process](VAddr addr, u16 value) { + memory.Write16(*process, addr, value); + }; + auto Write32 = [&memory, &process](VAddr addr, u32 value) { + memory.Write32(*process, addr, value); + }; for (state.current_line_nr = 0; state.current_line_nr < cheat_lines.size(); state.current_line_nr++) { @@ -261,7 +273,7 @@ void GatewayCheat::Execute(Core::System& system) const { // EXXXXXXX YYYYYYYY // Copies YYYYYYYY bytes from (current code location + 8) to [XXXXXXXX + offset]. // We need to call this here to skip the additional patch lines - PatchOp(line, state, system, cheat_lines); + PatchOp(line, state, system, *process, cheat_lines); break; case CheatType::Terminator: // D0000000 00000000 - ENDIF @@ -336,7 +348,7 @@ void GatewayCheat::Execute(Core::System& system) const { break; case CheatType::LoadOffset: // BXXXXXXX 00000000 - offset = word[XXXXXXX+offset] - LoadOffsetOp(system.Memory(), line, state); + LoadOffsetOp(system.Memory(), *process, line, state); break; case CheatType::Loop: { // C0000000 YYYYYYYY - LOOP next block YYYYYYYY times @@ -417,7 +429,7 @@ void GatewayCheat::Execute(Core::System& system) const { case CheatType::Patch: { // EXXXXXXX YYYYYYYY // Copies YYYYYYYY bytes from (current code location + 8) to [XXXXXXXX + offset]. - PatchOp(line, state, system, cheat_lines); + PatchOp(line, state, system, *process, cheat_lines); break; } } diff --git a/src/core/cheats/gateway_cheat.h b/src/core/cheats/gateway_cheat.h index ff200af59..dadfd2042 100644 --- a/src/core/cheats/gateway_cheat.h +++ b/src/core/cheats/gateway_cheat.h @@ -1,4 +1,4 @@ -// Copyright 2018 Citra Emulator Project +// Copyright Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -59,7 +59,7 @@ public: GatewayCheat(std::string name, std::string code, std::string comments); ~GatewayCheat(); - void Execute(Core::System& system) const override; + void Execute(Core::System& system, u32 process_id) const override; bool IsEnabled() const override; void SetEnabled(bool enabled) override; diff --git a/src/core/core.cpp b/src/core/core.cpp index 34df96e09..98ac85eba 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -394,7 +394,7 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st } cheat_engine.LoadCheatFile(title_id); - cheat_engine.Connect(); + cheat_engine.Connect(process->process_id); perf_stats = std::make_unique(title_id); @@ -814,9 +814,11 @@ void System::serialize(Archive& ar, const unsigned int file_version) { // This needs to be set from somewhere - might as well be here! if (Archive::is_loading::value) { + u32 cheats_pid; + ar & cheats_pid; timing->UnlockEventQueue(); memory->SetDSP(*dsp_core); - cheat_engine.Connect(); + cheat_engine.Connect(cheats_pid); gpu->Sync(); // Re-register gpu callback, because gsp service changed after service_manager got @@ -824,6 +826,9 @@ void System::serialize(Archive& ar, const unsigned int file_version) { auto gsp = service_manager->GetService("gsp::Gpu"); gpu->SetInterruptHandler( [gsp](Service::GSP::InterruptId interrupt_id) { gsp->SignalInterrupt(interrupt_id); }); + } else { + u32 cheats_pid = cheat_engine.GetConnectedPID(); + ar & cheats_pid; } save_state_status = SaveStateStatus::NONE; diff --git a/src/core/memory.cpp b/src/core/memory.cpp index ab4eff6aa..c49aa2704 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -1,4 +1,4 @@ -// Copyright 2015 Citra Emulator Project +// Copyright Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -448,8 +448,8 @@ void MemorySystem::UnregisterPageTable(std::shared_ptr page_table) { } template -T MemorySystem::Read(const VAddr vaddr) { - const u8* page_pointer = impl->current_page_table->pointers[vaddr >> CITRA_PAGE_BITS]; +T MemorySystem::Read(const std::shared_ptr& page_table, const VAddr vaddr) { + const u8* page_pointer = page_table->pointers[vaddr >> CITRA_PAGE_BITS]; if (page_pointer) { // NOTE: Avoid adding any extra logic to this fast-path block T value; @@ -472,7 +472,7 @@ T MemorySystem::Read(const VAddr vaddr) { } } - PageType type = impl->current_page_table->attributes[vaddr >> CITRA_PAGE_BITS]; + PageType type = page_table->attributes[vaddr >> CITRA_PAGE_BITS]; switch (type) { case PageType::Unmapped: LOG_ERROR(HW_Memory, "unmapped Read{} @ 0x{:08X} at PC 0x{:08X}", sizeof(T) * 8, vaddr, @@ -496,8 +496,9 @@ T MemorySystem::Read(const VAddr vaddr) { } template -void MemorySystem::Write(const VAddr vaddr, const T data) { - u8* page_pointer = impl->current_page_table->pointers[vaddr >> CITRA_PAGE_BITS]; +void MemorySystem::Write(const std::shared_ptr& page_table, const VAddr vaddr, + const T data) { + u8* page_pointer = page_table->pointers[vaddr >> CITRA_PAGE_BITS]; if (page_pointer) { // NOTE: Avoid adding any extra logic to this fast-path block std::memcpy(&page_pointer[vaddr & CITRA_PAGE_MASK], &data, sizeof(T)); @@ -521,7 +522,7 @@ void MemorySystem::Write(const VAddr vaddr, const T data) { } } - PageType type = impl->current_page_table->attributes[vaddr >> CITRA_PAGE_BITS]; + PageType type = page_table->attributes[vaddr >> CITRA_PAGE_BITS]; switch (type) { case PageType::Unmapped: LOG_ERROR(HW_Memory, "unmapped Write{} 0x{:08X} @ 0x{:08X} at PC 0x{:08X}", @@ -773,19 +774,35 @@ void MemorySystem::RasterizerMarkRegionCached(PAddr start, u32 size, bool cached } u8 MemorySystem::Read8(const VAddr addr) { - return Read(addr); + return Read(impl->current_page_table, addr); +} + +u8 MemorySystem::Read8(const Kernel::Process& process, VAddr addr) { + return Read(process.vm_manager.page_table, addr); } u16 MemorySystem::Read16(const VAddr addr) { - return Read(addr); + return Read(impl->current_page_table, addr); +} + +u16 MemorySystem::Read16(const Kernel::Process& process, VAddr addr) { + return Read(process.vm_manager.page_table, addr); } u32 MemorySystem::Read32(const VAddr addr) { - return Read(addr); + return Read(impl->current_page_table, addr); +} + +u32 MemorySystem::Read32(const Kernel::Process& process, VAddr addr) { + return Read(process.vm_manager.page_table, addr); } u64 MemorySystem::Read64(const VAddr addr) { - return Read(addr); + return Read(impl->current_page_table, addr); +} + +u64 MemorySystem::Read64(const Kernel::Process& process, VAddr addr) { + return Read(process.vm_manager.page_table, addr); } void MemorySystem::ReadBlock(const Kernel::Process& process, const VAddr src_addr, @@ -799,19 +816,35 @@ void MemorySystem::ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size } void MemorySystem::Write8(const VAddr addr, const u8 data) { - Write(addr, data); + Write(impl->current_page_table, addr, data); +} + +void MemorySystem::Write8(const Kernel::Process& process, const VAddr addr, const u8 data) { + Write(process.vm_manager.page_table, addr, data); } void MemorySystem::Write16(const VAddr addr, const u16 data) { - Write(addr, data); + Write(impl->current_page_table, addr, data); +} + +void MemorySystem::Write16(const Kernel::Process& process, const VAddr addr, const u16 data) { + Write(process.vm_manager.page_table, addr, data); } void MemorySystem::Write32(const VAddr addr, const u32 data) { - Write(addr, data); + Write(impl->current_page_table, addr, data); +} + +void MemorySystem::Write32(const Kernel::Process& process, const VAddr addr, const u32 data) { + Write(process.vm_manager.page_table, addr, data); } void MemorySystem::Write64(const VAddr addr, const u64 data) { - Write(addr, data); + Write(impl->current_page_table, addr, data); +} + +void MemorySystem::Write64(const Kernel::Process& process, const VAddr addr, const u64 data) { + Write(process.vm_manager.page_table, addr, data); } bool MemorySystem::WriteExclusive8(const VAddr addr, const u8 data, const u8 expected) { diff --git a/src/core/memory.h b/src/core/memory.h index 45bf54814..a3245f8ca 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -1,4 +1,4 @@ -// Copyright 2014 Citra Emulator Project +// Copyright Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -286,6 +286,17 @@ public: */ u8 Read8(VAddr addr); + /** + * Reads an 8-bit unsigned value from the process' address space + * at the given virtual address. + * + * @param process The process to read from. + * @param addr The virtual address to read the 8-bit value from. + * + * @returns the read 8-bit unsigned value. + */ + u8 Read8(const Kernel::Process& process, VAddr addr); + /** * Reads a 16-bit unsigned value from the current process' address space * at the given virtual address. @@ -296,6 +307,17 @@ public: */ u16 Read16(VAddr addr); + /** + * Reads a 16-bit unsigned value from the process' address space + * at the given virtual address. + * + * @param process The process to read from. + * @param addr The virtual address to read the 16-bit value from. + * + * @returns the read 16-bit unsigned value. + */ + u16 Read16(const Kernel::Process& process, VAddr addr); + /** * Reads a 32-bit unsigned value from the current process' address space * at the given virtual address. @@ -306,6 +328,17 @@ public: */ u32 Read32(VAddr addr); + /** + * Reads a 32-bit unsigned value from the process' address space + * at the given virtual address. + * + * @param process The process to read from. + * @param addr The virtual address to read the 32-bit value from. + * + * @returns the read 32-bit unsigned value. + */ + u32 Read32(const Kernel::Process& process, VAddr addr); + /** * Reads a 64-bit unsigned value from the current process' address space * at the given virtual address. @@ -316,6 +349,17 @@ public: */ u64 Read64(VAddr addr); + /** + * Reads a 64-bit unsigned value from the process' address space + * at the given virtual address. + * + * @param process The process to read from. + * @param addr The virtual address to read the 64-bit value from. + * + * @returns the read 64-bit value. + */ + u64 Read64(const Kernel::Process& process, VAddr addr); + /** * Writes an 8-bit unsigned integer to the given virtual address in * the current process' address space. @@ -327,6 +371,18 @@ public: */ void Write8(VAddr addr, u8 data); + /** + * Writes an 8-bit unsigned integer to the given virtual address in + * the process' address space. + * + * @param process The process to write to. + * @param addr The virtual address to write the 8-bit unsigned integer to. + * @param data The 8-bit unsigned integer to write to the given virtual address. + * + * @post The memory at the given virtual address contains the specified data value. + */ + void Write8(const Kernel::Process& process, VAddr addr, u8 data); + /** * Writes a 16-bit unsigned integer to the given virtual address in * the current process' address space. @@ -338,6 +394,18 @@ public: */ void Write16(VAddr addr, u16 data); + /** + * Writes a 16-bit unsigned integer to the given virtual address in + * the process' address space. + * + * @param process The process to write to. + * @param addr The virtual address to write the 16-bit unsigned integer to. + * @param data The 16-bit unsigned integer to write to the given virtual address. + * + * @post The memory range [addr, sizeof(data)) contains the given data value. + */ + void Write16(const Kernel::Process& process, VAddr addr, u16 data); + /** * Writes a 32-bit unsigned integer to the given virtual address in * the current process' address space. @@ -349,6 +417,18 @@ public: */ void Write32(VAddr addr, u32 data); + /** + * Writes a 32-bit unsigned integer to the given virtual address in + * the process' address space. + * + * @param process The process to write to. + * @param addr The virtual address to write the 32-bit unsigned integer to. + * @param data The 32-bit unsigned integer to write to the given virtual address. + * + * @post The memory range [addr, sizeof(data)) contains the given data value. + */ + void Write32(const Kernel::Process& process, VAddr addr, u32 data); + /** * Writes a 64-bit unsigned integer to the given virtual address in * the current process' address space. @@ -360,6 +440,18 @@ public: */ void Write64(VAddr addr, u64 data); + /** + * Writes a 64-bit unsigned integer to the given virtual address in + * the process' address space. + * + * @param process The process to write to. + * @param addr The virtual address to write the 64-bit unsigned integer to. + * @param data The 64-bit unsigned integer to write to the given virtual address. + * + * @post The memory range [addr, sizeof(data)) contains the given data value. + */ + void Write64(const Kernel::Process& process, VAddr addr, u64 data); + /** * Writes a {8, 16, 32, 64}-bit unsigned integer to the given virtual address in * the current process' address space if and only if the address contains @@ -555,10 +647,10 @@ public: private: template - T Read(const VAddr vaddr); + T Read(const std::shared_ptr& page_table, const VAddr vaddr); template - void Write(const VAddr vaddr, const T data); + void Write(const std::shared_ptr& page_table, const VAddr vaddr, const T data); template bool WriteExclusive(const VAddr vaddr, const T data, const T expected); diff --git a/src/core/rpc/packet.h b/src/core/rpc/packet.h index c68200834..fb1ee6d5a 100644 --- a/src/core/rpc/packet.h +++ b/src/core/rpc/packet.h @@ -1,4 +1,4 @@ -// Copyright 2018 Citra Emulator Project +// Copyright Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -15,6 +15,8 @@ enum class PacketType : u32 { Undefined = 0, ReadMemory = 1, WriteMemory = 2, + ProcessList = 3, + SetGetProcess = 4, }; struct PacketHeader { @@ -24,11 +26,21 @@ struct PacketHeader { u32 packet_size; }; +#pragma pack(push, 1) +struct ProcessInfo { + u32 process_id; + u64 title_id; + std::array process_name; +}; +static_assert(sizeof(ProcessInfo) == 0x14, "Incorrect ProcessInfo size"); +#pragma pack(pop) + constexpr u32 CURRENT_VERSION = 1; constexpr u32 MIN_PACKET_SIZE = sizeof(PacketHeader); -constexpr u32 MAX_PACKET_DATA_SIZE = 32; +constexpr u32 MAX_PACKET_DATA_SIZE = 1024; constexpr u32 MAX_PACKET_SIZE = MIN_PACKET_SIZE + MAX_PACKET_DATA_SIZE; constexpr u32 MAX_READ_SIZE = MAX_PACKET_DATA_SIZE; +constexpr u32 MAX_PROCESSES_IN_LIST = (MAX_PACKET_DATA_SIZE - sizeof(u32)) / sizeof(ProcessInfo); class Packet { public: @@ -69,9 +81,6 @@ public: } private: - void HandleReadMemory(u32 address, u32 data_size); - void HandleWriteMemory(u32 address, std::span data); - struct PacketHeader header; std::array packet_data; diff --git a/src/core/rpc/rpc_server.cpp b/src/core/rpc/rpc_server.cpp index 4dd9c2e76..44c4b1685 100644 --- a/src/core/rpc/rpc_server.cpp +++ b/src/core/rpc/rpc_server.cpp @@ -1,9 +1,10 @@ -// Copyright 2019 Citra Emulator Project +// Copyright Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #include "common/logging/log.h" #include "core/core.h" +#include "core/hle/kernel/process.h" #include "core/memory.h" #include "core/rpc/packet.h" #include "core/rpc/rpc_server.h" @@ -22,10 +23,23 @@ void RPCServer::HandleReadMemory(Packet& packet, u32 address, u32 data_size) { if (data_size > MAX_READ_SIZE) { return; } + u32 read_size = data_size; // Note: Memory read occurs asynchronously from the state of the emulator - system.Memory().ReadBlock(address, packet.GetPacketData().data(), data_size); - packet.SetPacketDataSize(data_size); + if (selected_pid == 0xFFFFFFFF) { + LOG_ERROR(RPC_Server, "No target process selected, memory access may be invalid."); + system.Memory().ReadBlock(address, packet.GetPacketData().data(), data_size); + } else { + auto process = system.Kernel().GetProcessById(selected_pid); + if (process) { + system.Memory().ReadBlock(*process, address, packet.GetPacketData().data(), data_size); + } else { + LOG_ERROR(RPC_Server, "Selected process does not exist."); + read_size = 0; + } + } + + packet.SetPacketDataSize(read_size); packet.SendReply(); } @@ -33,9 +47,21 @@ void RPCServer::HandleWriteMemory(Packet& packet, u32 address, std::span= Memory::PROCESS_IMAGE_VADDR && address <= Memory::PROCESS_IMAGE_VADDR_END) || (address >= Memory::HEAP_VADDR && address <= Memory::HEAP_VADDR_END) || + (address >= Memory::LINEAR_HEAP_VADDR && address <= Memory::LINEAR_HEAP_VADDR_END) || (address >= Memory::N3DS_EXTRA_RAM_VADDR && address <= Memory::N3DS_EXTRA_RAM_VADDR_END)) { // Note: Memory write occurs asynchronously from the state of the emulator - system.Memory().WriteBlock(address, data.data(), data.size()); + if (selected_pid == 0xFFFFFFFF) { + LOG_ERROR(RPC_Server, "No target process selected, memory access may be invalid."); + system.Memory().WriteBlock(address, data.data(), data.size()); + } else { + auto process = system.Kernel().GetProcessById(selected_pid); + if (process) { + system.Memory().WriteBlock(*process, address, data.data(), data.size()); + } else { + LOG_ERROR(RPC_Server, "Selected process does not exist."); + } + } + // If the memory happens to be executable code, make sure the changes become visible // Is current core correct here? @@ -45,11 +71,57 @@ void RPCServer::HandleWriteMemory(Packet& packet, u32 address, std::span(process_list.size())); + const u32 end = std::min(start + max_amount, static_cast(process_list.size())); + const u32 count = std::min(end - start, MAX_PROCESSES_IN_LIST); + + u8* out_data = packet.GetPacketData().data(); + u32 written_bytes = 0; + + memcpy(out_data + written_bytes, &count, sizeof(count)); + written_bytes += sizeof(count); + + for (u32 i = start; i < start + count; i++) { + ProcessInfo info{}; + info.process_id = process_list[i]->process_id; + info.title_id = process_list[i]->codeset->program_id; + memcpy(info.process_name.data(), process_list[i]->codeset->name.data(), + std::min(process_list[i]->codeset->name.size(), info.process_name.size())); + + memcpy(out_data + written_bytes, &info, sizeof(ProcessInfo)); + written_bytes += sizeof(ProcessInfo); + } + + packet.SetPacketDataSize(written_bytes); + packet.SendReply(); +} + +void RPCServer::HandleSetGetProcess(Packet& packet, u32 operation, u32 process_id) { + u8* out_data = packet.GetPacketData().data(); + u32 written_bytes = 0; + + if (operation == 0) { + // Get + memcpy(out_data + written_bytes, &selected_pid, sizeof(selected_pid)); + written_bytes += sizeof(selected_pid); + } else { + // Set + selected_pid = process_id; + } + + packet.SetPacketDataSize(written_bytes); + packet.SendReply(); +} + bool RPCServer::ValidatePacket(const PacketHeader& packet_header) { if (packet_header.version <= CURRENT_VERSION) { switch (packet_header.packet_type) { case PacketType::ReadMemory: case PacketType::WriteMemory: + case PacketType::ProcessList: + case PacketType::SetGetProcess: if (packet_header.packet_size >= (sizeof(u32) * 2)) { return true; } @@ -66,26 +138,34 @@ void RPCServer::HandleSingleRequest(std::unique_ptr request_packet) { const auto packet_data = request_packet->GetPacketData(); if (ValidatePacket(request_packet->GetHeader())) { - // Currently, all request types use the address/data_size wire format - u32 address = 0; - u32 data_size = 0; - std::memcpy(&address, packet_data.data(), sizeof(address)); - std::memcpy(&data_size, packet_data.data() + sizeof(address), sizeof(data_size)); + // Currently, all request types use to arguments + u32 arg1 = 0; + u32 arg2 = 0; + std::memcpy(&arg1, packet_data.data(), sizeof(arg1)); + std::memcpy(&arg2, packet_data.data() + sizeof(arg1), sizeof(arg2)); switch (request_packet->GetPacketType()) { case PacketType::ReadMemory: - if (data_size > 0 && data_size <= MAX_READ_SIZE) { - HandleReadMemory(*request_packet, address, data_size); + if (arg2 > 0 && arg2 <= MAX_READ_SIZE) { + HandleReadMemory(*request_packet, arg1, arg2); success = true; } break; case PacketType::WriteMemory: - if (data_size > 0 && data_size <= MAX_PACKET_DATA_SIZE - (sizeof(u32) * 2)) { - const auto data = packet_data.subspan(sizeof(u32) * 2, data_size); - HandleWriteMemory(*request_packet, address, data); + if (arg2 > 0 && arg2 <= MAX_PACKET_DATA_SIZE - (sizeof(u32) * 2)) { + const auto data = packet_data.subspan(sizeof(u32) * 2, arg2); + HandleWriteMemory(*request_packet, arg1, data); success = true; } break; + case PacketType::ProcessList: + HandleProcessList(*request_packet, arg1, arg2); + success = true; + break; + case PacketType::SetGetProcess: + HandleSetGetProcess(*request_packet, arg1, arg2); + success = true; + break; default: break; } diff --git a/src/core/rpc/rpc_server.h b/src/core/rpc/rpc_server.h index f34d97059..90cad82b3 100644 --- a/src/core/rpc/rpc_server.h +++ b/src/core/rpc/rpc_server.h @@ -1,4 +1,4 @@ -// Copyright 2018 Citra Emulator Project +// Copyright Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -29,6 +29,8 @@ public: private: void HandleReadMemory(Packet& packet, u32 address, u32 data_size); void HandleWriteMemory(Packet& packet, u32 address, std::span data); + void HandleProcessList(Packet& packet, u32 start_index, u32 max_amount); + void HandleSetGetProcess(Packet& packet, u32 operation, u32 process_id); bool ValidatePacket(const PacketHeader& packet_header); void HandleSingleRequest(std::unique_ptr request); void HandleRequestsLoop(std::stop_token stop_token); @@ -37,6 +39,7 @@ private: Core::System& system; Common::SPSCQueue, true> request_queue; std::jthread request_handler_thread; + u32 selected_pid = 0xFFFFFFFF; }; } // namespace Core::RPC