From 3eb14b33fb37207dea35540c9338e45da2786667 Mon Sep 17 00:00:00 2001 From: nerduminer Date: Sun, 19 Sep 2021 17:33:10 -0400 Subject: [PATCH 1/2] Update Linux Version Now uses shared memory, with the added benefit of being faster and not requiring sudo permission. However, due to Dolphin immediately unlinking the shared memory after its created on Linux, the library now has to run before a game is started while constantly attempting to access the shared memory object. A bug with Python also causes the object to be deleted after the process ends. The only superficial effect of this update on Linux is that a warning is posted after the library finishes its test. --- memorylib_lin.py | 105 +++++++++++++++++++++++++++++++++++++++++++++++ run.sh | 4 +- 2 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 memorylib_lin.py diff --git a/memorylib_lin.py b/memorylib_lin.py new file mode 100644 index 0000000..f4ee69a --- /dev/null +++ b/memorylib_lin.py @@ -0,0 +1,105 @@ +import ctypes +import struct +import os +import sys +from struct import pack, unpack +from subprocess import check_output +from multiprocessing import shared_memory + +class Dolphin(object): + def __init__(self): + self.pid = -1 + self.memory = None + + def reset(self): + self.pid = -1 + self.memory = None + + def find_dolphin(self): + try: + if check_output(["pidof", "dolphin-emu"]) != '\n': + self.pid = int(check_output(["pidof", "dolphin-emu"])) + if check_output(["pidof", "dolphin-emu-qt2"]) != '\n': + self.pid = int(check_output(["pidof", "dolphin-emu-qt2"])) + if check_output(["pidof", "dolphin-emu-wx"]) != '\n': + self.pid = int(check_output(["pidof", "dolphin-emu-wx"])) + except Exception: #subprocess.CalledProcessError + # Do nothing because self.pid cant be modified until a successful run of pidof + pass + + if self.pid == -1: + return False + + return True + + def init_shared_memory(self): + print("Waiting for shared memory...") + while True: + try: + self.memory = shared_memory.SharedMemory('dolphin-emu.'+str(self.pid)) + return True + except FileNotFoundError: + pass + + def read_ram(self, offset, size): + return self.memory.buf[offset:offset+size] + + def write_ram(self, offset, data): + self.memory.buf[offset:offset+len(data)] = data + + def read_uint32(self, addr): + assert addr >= 0x80000000 + value = self.read_ram(addr-0x80000000, 4) + + return struct.unpack(">I", value)[0] + + def write_uint32(self, addr, val): + assert addr >= 0x80000000 + return self.write_ram(addr - 0x80000000, pack(">I", val)) + + def read_float(self, addr): + assert addr >= 0x80000000 + value = self.read_ram(addr - 0x80000000, 4) + + return struct.unpack(">f", value)[0] + + def write_float(self, addr, val): + assert addr >= 0x80000000 + return self.write_ram(addr - 0x80000000, struct.pack(">f", val)) + + +if __name__ == "__main__": + dolphin = Dolphin() + import multiprocessing + + if dolphin.find_dolphin(): + + print("Found Dolphin! ") + else: + print("Didn't find Dolphin") + + print(dolphin.pid) + + if dolphin.init_shared_memory(): + print("We found MEM1 and/or MEM2!") + else: + print("We didn't find it...") + + import random + randint = random.randint + from timeit import default_timer + + start = default_timer() + + print("Testing Shared Memory Method") + start = default_timer() + count = 500000 + for i in range(count): + value = randint(0, 2**32-1) + dolphin.write_uint32(0x80000000, value) + + result = dolphin.read_uint32(0x80000000) + assert result == value + diff = default_timer()-start + print(count/diff, "per sec") + print("time: ", diff) \ No newline at end of file diff --git a/run.sh b/run.sh index ca650bc..689ed65 100755 --- a/run.sh +++ b/run.sh @@ -1,4 +1,4 @@ #!/bin/bash -echo "dolphin-memory-lib requires sudo permission to read and write to the emulator process memory." -sudo python memtest_lin.py +echo "dolphin-memory-lib requires you to run the library before you start a game on the emulator." +python memorylib_lin.py read -n1 -r From ed790c82a50a1f9376196a128d3cc6cf651318a6 Mon Sep 17 00:00:00 2001 From: nerduminer Date: Sun, 19 Sep 2021 17:33:50 -0400 Subject: [PATCH 2/2] Remove old file --- memtest_lin.py | 187 ------------------------------------------------- 1 file changed, 187 deletions(-) delete mode 100644 memtest_lin.py diff --git a/memtest_lin.py b/memtest_lin.py deleted file mode 100644 index da375b0..0000000 --- a/memtest_lin.py +++ /dev/null @@ -1,187 +0,0 @@ -import ctypes -import struct -import os -import sys -from subprocess import check_output -from ctypes import sizeof, addressof, POINTER, pointer - -# Various Linux structs needed for operation - -class iovec(ctypes.Structure): - _fields_ = [("iov_base",ctypes.c_void_p),("iov_len",ctypes.c_size_t)] - -libc = ctypes.cdll.LoadLibrary("libc.so.6") -vm = libc.process_vm_readv -vm.argtypes = [ctypes.c_int, POINTER(iovec), ctypes.c_ulong, POINTER(iovec), ctypes.c_ulong, ctypes.c_ulong] -vmwrite = libc.process_vm_writev -vmwrite.argtypes = [ctypes.c_int, POINTER(iovec), ctypes.c_ulong, POINTER(iovec), ctypes.c_ulong, ctypes.c_ulong] - - -# The following code is a port of aldelaro5's Dolphin memory access methods -# for Linux into Python+ctypes. -# https://github.com/aldelaro5/Dolphin-memory-engine - -""" -MIT License - -Copyright (c) 2017 aldelaro5 - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE.""" - -class Dolphin(object): - def __init__(self): - self.pid = -1 - self.handle = -1 - - self.address_start = 0 - self.mem1_start = 0 - self.mem2_start = 0 - self.mem2_exists = False - - def find_dolphin(self): - try: - if check_output(["pidof", "dolphin-emu"]) != '\n': - self.pid = int(check_output(["pidof", "dolphin-emu"])) - if check_output(["pidof", "dolphin-emu-qt2"]) != '\n': - self.pid = int(check_output(["pidof", "dolphin-emu-qt2"])) - if check_output(["pidof", "dolphin-emu-wx"]) != '\n': - self.pid = int(check_output(["pidof", "dolphin-emu-wx"])) - except Exception: #subprocess.CalledProcessError - # Do nothing because self.pid cant be modified until a successful run of pidof - pass - - if self.pid == -1: - return False - - return True - - def get_emu_info(self): - MEM1_found = False - try: - maps_file = open("/proc/{}/maps".format(self.pid), 'r') - except IOError: - print("Cant open maps for process {}".format(self.pid)) - heap_info = None - for line in maps_file: - foundDevShmDolphin = False - if '/dev/shm/dolphinmem' in line: - heap_info = line.split() - if '/dev/shm/dolphin-emu' in line: - heap_info = line.split() - if heap_info is None: - continue - else: - offset = 0 - offset_str = "0x" + str(heap_info[2]) - offset = int(offset_str, 16) - if offset != 0 and offset != 0x2000000: - continue - first_address = 0 - second_address = 0 - index_dash = heap_info[0].find('-') - - first_address_str = "0x" + str(heap_info[0][: index_dash]) - second_address_str = "0x" + str(heap_info[0][(index_dash + 1):]) - - first_address = int(first_address_str, 16) - second_address = int(second_address_str, 16) - - if (second_address - first_address) == 0x4000000 and offset == 0x2000000: - self.mem2_start = first_address - self.mem2_exists = True - if (second_address - first_address) == 0x2000000 and offset == 0x0: - self.address_start = first_address - - if self.address_start == 0: - return False - return True - - def read_ram(self, offset, size): - buffer_ = (ctypes.c_char*size)() - nread = ctypes.c_size_t - local = (iovec*1)() - remote = (iovec*1)() - local[0].iov_base = ctypes.addressof(buffer_) - local[0].iov_len = size - remote[0].iov_base = ctypes.c_void_p(self.address_start + offset) - remote[0].iov_len = size - nread = vm(self.pid, local, 1, remote, 1, 0) - if nread != size: - return False, buffer_ - return True, buffer_ - - def write_ram(self, offset, data): - buffer_ = (ctypes.c_char*len(data))(*data) - nwrote = ctypes.c_size_t - local = (iovec*1)() - remote = (iovec*1)() - local[0].iov_base = ctypes.addressof(buffer_) - local[0].iov_len = len(data) - remote[0].iov_base = ctypes.c_void_p(self.address_start + offset) - remote[0].iov_len = len(data) - nwrote = vmwrite(self.pid, local, 1, remote, 1, 0) - if nwrote != len(data): - return False - return True - - def read_uint32(self, addr): - assert addr >= 0x80000000 - success, value = self.read_ram(addr-0x80000000, 4) - - if success: - return struct.unpack(">I", value)[0] - else: - return None - - def read_float(self, addr): - assert addr >= 0x80000000 - success, value = self.read_ram(addr - 0x80000000, 4) - - if success: - return struct.unpack(">f", value)[0] - else: - return None - - def write_float(self, addr, val): - assert addr >= 0x80000000 - return self.write_ram(addr - 0x80000000, struct.pack(">f", val)) - - -if __name__ == "__main__": - dolphin = Dolphin() - - if dolphin.find_dolphin(): - - print("Found Dolphin! ") - else: - print("Didn't find Dolphin") - - print(dolphin.pid) - - if dolphin.get_emu_info(): - print("We found MEM1 and/or MEM2!", dolphin.address_start, dolphin.mem2_start) - else: - print("We didn't find it...") - print(dolphin.write_ram(0, b"GMS")) - success, result = dolphin.read_ram(0, 8) - print(result[0:8]) - - print(dolphin.write_ram(0, b"AWA")) - success, result = dolphin.read_ram(0, 8) - print(result[0:8])