[v0.1.3] Added Older Dolphin support (e.g. Lua-Core) on windows
This commit is contained in:
parent
0ddaa379e5
commit
cb366e0cdb
5 changed files with 142 additions and 4 deletions
30
3RD-PARTY-LICENSE.txt
Normal file
30
3RD-PARTY-LICENSE.txt
Normal file
|
@ -0,0 +1,30 @@
|
|||
3RD PARTY LICENSES
|
||||
|
||||
* dolphin-memory-engine (modified)
|
||||
(https://github.com/aldelaro5/Dolphin-memory-engine)
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Copyright (c) 2017 aldelaro5
|
||||
|
||||
#############################################################################
|
||||
LICENSE TEXTS
|
||||
#############################################################################
|
||||
|
||||
MIT License
|
||||
|
||||
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.
|
|
@ -1,4 +1,6 @@
|
|||
# Change Log
|
||||
## 0.1.3 (2023/04/16)
|
||||
- Added Older Dolphin support (e.g. Lua-Core) on windows
|
||||
## 0.1.2 (2023/03/27)
|
||||
- Fixed MEM2 range
|
||||
## 0.1.1 (2023/03/27)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[metadata]
|
||||
name = supDolphinWS-server
|
||||
version = 0.1.2
|
||||
version = 0.1.3
|
||||
author = sup39
|
||||
author_email = sms@sup39.dev
|
||||
description = A WebSocket server for accessing memory of emulated games in Dolphin
|
||||
|
|
|
@ -5,6 +5,11 @@ import psutil
|
|||
import struct
|
||||
from multiprocessing.shared_memory import SharedMemory
|
||||
|
||||
if os.name == 'nt':
|
||||
from .memory_windows import try_get_memory as try_get_memory_direct
|
||||
else:
|
||||
try_get_memory_direct = None
|
||||
|
||||
MEM1_START = 0x80000000
|
||||
MEM1_END = 0x81800000
|
||||
MEM2_START = 0x90000000
|
||||
|
@ -16,14 +21,23 @@ dolphinProcNames = \
|
|||
else {'dolphin-emu', 'dolphin-emu-qt2', 'dolphin-emu-wx'}
|
||||
|
||||
def try_get_memory(pid):
|
||||
try: return SharedMemory('dolphin-emu.'+str(pid))
|
||||
except FileNotFoundError: return None
|
||||
# newer Dolphin => SharedMemory
|
||||
try:
|
||||
return SharedMemory('dolphin-emu.'+str(pid))
|
||||
except FileNotFoundError: pass
|
||||
# old Dolphin (on windows) => direct memory access via win32 api
|
||||
if try_get_memory_direct:
|
||||
try:
|
||||
return try_get_memory_direct(pid)
|
||||
except:
|
||||
import traceback
|
||||
print(traceback.format_exc())
|
||||
|
||||
def find_memory(exclude_pid={}):
|
||||
try: return next(
|
||||
(p.pid, m)
|
||||
for p in psutil.process_iter(['pid', 'name'])
|
||||
if p.pid not in exclude_pid and p.name() in dolphinProcNames
|
||||
if p.pid not in exclude_pid and p.name() in dolphinProcNames and p.status() == 'running'
|
||||
for m in [try_get_memory(p.pid)]
|
||||
if m is not None
|
||||
)
|
||||
|
|
92
src/supDolphinWS/server/memory_windows.py
Normal file
92
src/supDolphinWS/server/memory_windows.py
Normal file
|
@ -0,0 +1,92 @@
|
|||
# SPDX-License-Identifier: MIT
|
||||
# Copyright (c) 2023 sup39
|
||||
'''
|
||||
The `try_get_memory` function is based on
|
||||
`WindowsDolphinProcess::obtainEmuRAMInformations()`
|
||||
(https://github.com/aldelaro5/Dolphin-memory-engine/blob/master/Source/DolphinProcess/Windows/WindowsDolphinProcess.cpp#L47)
|
||||
from aldelaro5's Dolphin memory engine
|
||||
(https://github.com/aldelaro5/Dolphin-memory-engine)
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Copyright (c) 2017 aldelaro5
|
||||
'''
|
||||
|
||||
import ctypes
|
||||
from ctypes import wintypes
|
||||
wintypes.SIZE_T = ctypes.c_uint32 if ctypes.sizeof(ctypes.c_void_p) == 4 else ctypes.c_uint64
|
||||
|
||||
PROCESS_QUERY_INFORMATION = 0x0400
|
||||
PROCESS_VM_READ = 0x0010
|
||||
PROCESS_VM_WRITE = 0x0020
|
||||
MEM_MAPPED = 0x40000
|
||||
|
||||
class MEMORY_BASIC_INFORMATION(ctypes.Structure):
|
||||
_fields_ = [
|
||||
("BaseAddress", wintypes.LPVOID),
|
||||
("AllocationBase", wintypes.LPVOID),
|
||||
("AllocationProtect", wintypes.DWORD),
|
||||
("RegionSize", wintypes.SIZE_T),
|
||||
("State", wintypes.DWORD),
|
||||
("Protect", wintypes.DWORD),
|
||||
("Type", wintypes.DWORD)
|
||||
]
|
||||
|
||||
kernel32 = ctypes.WinDLL("kernel32.dll")
|
||||
virtual_query_ex = kernel32.VirtualQueryEx
|
||||
virtual_query_ex.argtypes = [wintypes.HANDLE, wintypes.LPCVOID, ctypes.POINTER(MEMORY_BASIC_INFORMATION), wintypes.SIZE_T]
|
||||
virtual_query_ex.restype = wintypes.SIZE_T
|
||||
|
||||
class FakeMemorySlice(bytes):
|
||||
def tobytes(self): return self
|
||||
|
||||
class ProcessMemoryView:
|
||||
def __init__(self, hProcess, baseAddr):
|
||||
self.hProcess = hProcess
|
||||
self.baseAddr = baseAddr
|
||||
def __getitem__(self, rg):
|
||||
if type(rg) is not slice: raise TypeError('Index must be `slice`')
|
||||
assert rg.start is not None and rg.stop is not None, 'Start and stop of the slice must be given'
|
||||
addr = rg.start
|
||||
size = rg.stop - rg.start
|
||||
buf = ctypes.create_string_buffer(size)
|
||||
nbyte = ctypes.c_size_t(0)
|
||||
assert kernel32.ReadProcessMemory(
|
||||
self.hProcess,
|
||||
ctypes.c_void_p(self.baseAddr+addr),
|
||||
buf,
|
||||
ctypes.c_size_t(size),
|
||||
ctypes.byref(nbyte),
|
||||
), f'Failed to read process memory. Error code: {kernel32.GetLastError()}'
|
||||
return FakeMemorySlice(buf[slice(None, None, rg.step)])
|
||||
def __setitem__(self, rg, payload):
|
||||
if type(rg) is not slice: raise TypeError('Index must be `slice`')
|
||||
assert rg.start is not None, 'Start of the slice must be given'
|
||||
addr = rg.start
|
||||
nbyte = ctypes.c_size_t(0)
|
||||
assert kernel32.WriteProcessMemory(
|
||||
self.hProcess,
|
||||
ctypes.c_void_p(self.baseAddr+addr),
|
||||
bytes(payload),
|
||||
ctypes.c_size_t(len(payload)),
|
||||
ctypes.byref(nbyte)
|
||||
), f'Failed to write process memory. Error code: {kernel32.GetLastError()}'
|
||||
return nbyte.value
|
||||
def __len__(self):
|
||||
return 0x2000000 # TODO
|
||||
|
||||
class FakeSharedMemory:
|
||||
def __init__(self, hProcess, baseAddr):
|
||||
self.hProcess = hProcess
|
||||
self.buf = ProcessMemoryView(hProcess, baseAddr)
|
||||
def __del__(self):
|
||||
self.close()
|
||||
def close(self):
|
||||
kernel32.CloseHandle(self.hProcess)
|
||||
|
||||
def try_get_memory(pid): # TODO MEM2
|
||||
hProcess = kernel32.OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_VM_WRITE, False, pid)
|
||||
baseAddr = 0
|
||||
memory_info = MEMORY_BASIC_INFORMATION()
|
||||
while virtual_query_ex(hProcess, baseAddr, ctypes.byref(memory_info), ctypes.sizeof(memory_info)):
|
||||
if memory_info.RegionSize == 0x2000000 and memory_info.Type == MEM_MAPPED:
|
||||
return FakeSharedMemory(hProcess, baseAddr)
|
||||
baseAddr += memory_info.RegionSize
|
Reference in a new issue