1
0
Fork 0

[v0.1.3] Added Older Dolphin support (e.g. Lua-Core) on windows

This commit is contained in:
sup39 2023-04-16 19:17:00 +09:00
parent 0ddaa379e5
commit cb366e0cdb
No known key found for this signature in database
GPG key ID: 19D72AAF429D55D9
5 changed files with 142 additions and 4 deletions

30
3RD-PARTY-LICENSE.txt Normal file
View 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.

View file

@ -1,4 +1,6 @@
# Change Log # Change Log
## 0.1.3 (2023/04/16)
- Added Older Dolphin support (e.g. Lua-Core) on windows
## 0.1.2 (2023/03/27) ## 0.1.2 (2023/03/27)
- Fixed MEM2 range - Fixed MEM2 range
## 0.1.1 (2023/03/27) ## 0.1.1 (2023/03/27)

View file

@ -1,6 +1,6 @@
[metadata] [metadata]
name = supDolphinWS-server name = supDolphinWS-server
version = 0.1.2 version = 0.1.3
author = sup39 author = sup39
author_email = sms@sup39.dev author_email = sms@sup39.dev
description = A WebSocket server for accessing memory of emulated games in Dolphin description = A WebSocket server for accessing memory of emulated games in Dolphin

View file

@ -5,6 +5,11 @@ import psutil
import struct import struct
from multiprocessing.shared_memory import SharedMemory 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_START = 0x80000000
MEM1_END = 0x81800000 MEM1_END = 0x81800000
MEM2_START = 0x90000000 MEM2_START = 0x90000000
@ -16,14 +21,23 @@ dolphinProcNames = \
else {'dolphin-emu', 'dolphin-emu-qt2', 'dolphin-emu-wx'} else {'dolphin-emu', 'dolphin-emu-qt2', 'dolphin-emu-wx'}
def try_get_memory(pid): def try_get_memory(pid):
try: return SharedMemory('dolphin-emu.'+str(pid)) # newer Dolphin => SharedMemory
except FileNotFoundError: return None 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={}): def find_memory(exclude_pid={}):
try: return next( try: return next(
(p.pid, m) (p.pid, m)
for p in psutil.process_iter(['pid', 'name']) 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)] for m in [try_get_memory(p.pid)]
if m is not None if m is not None
) )

View 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