restructure, add check_version()

This commit is contained in:
gen2brain 2013-04-06 11:11:44 +02:00
parent fbeb2d9ecb
commit 835df57b97
2 changed files with 235 additions and 113 deletions

View file

@ -18,31 +18,37 @@ import os
import sys
import ctypes as C
try:
from m64py.core.defs import *
from m64py.core.config import Config
from m64py.loader import load, unload_library
from m64py.utils import log, version_split
from m64py.archive import ROM_TYPE
from m64py.platform import DLL_EXT, DEFAULT_DYNLIB, SEARCH_DIRS
from m64py.core.vidext import vidext
except ImportError, err:
sys.stderr.write("Error: Can't import m64py modules%s%s%s" % (
os.linesep, str(err), os.linesep))
sys.exit(1)
from m64py.core.defs import *
from m64py.core.config import Config
from m64py.loader import load, unload_library
from m64py.utils import log, version_split
from m64py.opts import VERBOSE
from m64py.archive import ROM_TYPE
from m64py.platform import DLL_EXT, DEFAULT_DYNLIB, SEARCH_DIRS
from m64py.core.vidext import vidext
def py_debug_callback(context, level, message):
if level <= 1:
def debug_callback(context, level, message):
if level <= M64MSG_ERROR:
sys.stderr.write("%s: %s\n" % (context, message))
elif level == 2:
elif level == M64MSG_WARNING:
sys.stderr.write("%s: %s\n" % (context, message))
elif level == 3 or level == 4:
elif level == M64MSG_INFO or level == M64MSG_STATUS:
sys.stderr.write("%s: %s\n" % (context, message))
elif level > 4:
pass
elif level == M64MSG_VERBOSE:
if VERBOSE:
sys.stderr.write("%s: %s\n" % (context, message))
def state_callback(context, param, value):
if param == M64CORE_VIDEO_SIZE:
sys.stderr.write("%s: %s: %s\n" % (context, "M64CORE_VIDEO_SIZE", value))
elif param == M64CORE_VIDEO_MODE:
sys.stderr.write("%s: %s: %s\n" % (context, "M64CORE_VIDEO_MODE", value))
DEBUGFUNC = C.CFUNCTYPE(None, C.c_char_p, C.c_int, C.c_char_p)
DEBUG_CALLBACK = DEBUGFUNC(py_debug_callback)
STATEFUNC = C.CFUNCTYPE(None, C.c_char_p, C.c_int, C.c_int)
DEBUG_CALLBACK = DEBUGFUNC(debug_callback)
STATE_CALLBACK = STATEFUNC(state_callback)
class Core:
"""Mupen64Plus Core library"""
@ -61,48 +67,55 @@ class Core:
self.core_name = "Mupen64Plus Core"
self.core_version = "Unknown"
def get_handle(self):
"""Retrieves core library handle."""
return self.m64p
def core_load(self, path=None, vidext=False):
"""Loads core library."""
try:
if path is not None:
if path is None:
raise Exception("'%s' library not found." % self.core_name)
else:
self.m64p = load(path)
else:
self.m64p = None
raise IOError("'%s' library not found." % self.core_name)
rval = self.m64p.CoreStartup(C.c_int(API_VERSION), None,
C.c_char_p(os.path.dirname(path)),
"Core", DEBUG_CALLBACK, None, None)
if rval != M64ERR_SUCCESS:
log.debug("core_load()")
log.warn("error starting '%s' library" % self.core_name)
self.core_shutdown()
unload_library(self.m64p)
del self.m64p
sys.exit(1)
ver_ptr = C.pointer(C.c_int())
name_ptr = C.pointer(C.c_char_p())
rval = self.m64p.PluginGetVersion(None, ver_ptr, None, name_ptr, None)
if rval == M64ERR_SUCCESS:
self.config = Config(self)
self.core_name = name_ptr.contents.value
self.core_version = version_split(ver_ptr.contents.value)
log.info("attached to library '%s' version %s" %
(self.core_name, self.core_version))
if vidext:
self.override_vidext()
else:
self.m64p = None
log.debug("core_load()")
log.warn(self.error_message(rval))
self.core_path = path
self.check_version()
self.core_startup(path, vidext)
except Exception, err:
self.m64p = None
log.exception(str(err))
def get_handle(self):
"""Retrieves core library handle."""
return self.m64p
def check_version(self):
"""Checks core API version."""
version = self.plugin_get_version(self.m64p, self.core_path)
if version:
plugin_type, plugin_version, plugin_api, plugin_name, plugin_cap = version
if plugin_type != M64PLUGIN_CORE:
raise Exception("library '%s' is invalid, this is not the emulator core." % (
os.path.basename(self.core_path)))
elif plugin_version < MINIMUM_CORE_VERSION:
raise Exception("library '%s' is incompatible, core version %s is below minimum supported %s." % (
os.path.basename(self.core_path), version_split(plugin_version), version_split(MINIMUM_CORE_VERSION)))
elif plugin_api & 0xffff0000 != CORE_API_VERSION & 0xffff0000:
raise Exception("library '%s' is incompatible, core API major version %s doesn't match application (%s)." % (
os.path.basename(core_path), version_split(plugin_version), version_split(CORE_API_VERSION)))
else:
config_ver, debug_ver, vidext_ver = self.core_get_api_versions()
if config_ver & 0xffff0000 != CONFIG_API_VERSION & 0xffff0000:
raise Exception("emulator core '%s' is incompatible, config API major version %s doesn't match application: (%s)" % (
os.path.basename(self.core_path), version_split(self.config_version), version_split(CONFIG_API_VERSION)))
self.core_name = plugin_name
self.core_version = plugin_version
log.info("attached to library '%s' version %s" %
(self.core_name, version_split(self.core_version)))
if plugin_cap & M64CAPS_DYNAREC:
log.info("includes support for Dynamic Recompiler.")
if plugin_cap & M64CAPS_DEBUGGER:
log.info("includes support for MIPS r4300 Debugger.")
if plugin_cap & M64CAPS_CORE_COMPARE:
log.info("includes support for r4300 Core Comparison.")
def error_message(self, return_code):
"""Returns description of the error"""
@ -110,6 +123,74 @@ class Core:
rval = self.m64p.CoreErrorMessage(return_code)
return rval
def core_startup(self, path, vidext):
"""Initializes libmupen64plus for use by allocating memory,
creating data structures, and loading the configuration file."""
rval = self.m64p.CoreStartup(C.c_int(CORE_API_VERSION), None,
C.c_char_p(os.path.dirname(path)),
"Core", DEBUG_CALLBACK, "State", STATE_CALLBACK)
if rval == M64ERR_SUCCESS:
if vidext: self.override_vidext()
self.config = Config(self)
else:
log.debug("core_startup()")
log.warn("error starting '%s' library" % self.core_name)
self.core_shutdown()
unload_library(self.m64p)
del self.m64p
sys.exit(1)
def core_shutdown(self):
"""Saves the config file, then destroys
data structures and releases allocated memory."""
if self.m64p:
self.m64p.CoreShutdown()
unload_library(self.m64p)
del self.m64p
self.m64p = None
return M64ERR_SUCCESS
def plugin_get_version(self, handle, path):
"""Retrieves version information from the plugin."""
try:
type_ptr = C.pointer(C.c_int())
ver_ptr = C.pointer(C.c_int())
api_ptr = C.pointer(C.c_int())
name_ptr = C.pointer(C.c_char_p())
cap_ptr = C.pointer(C.c_int())
rval = handle.PluginGetVersion(
type_ptr, ver_ptr, api_ptr, name_ptr, cap_ptr)
except AttributeError:
unload_library(handle)
log.warn("library '%s' is invalid, no PluginGetVersion() function found." % (
os.path.basename(path)))
except OSError, err:
log.debug("plugin_get_version()")
log.warn(str(err))
else:
if rval == M64ERR_SUCCESS:
return (type_ptr.contents.value, ver_ptr.contents.value, api_ptr.contents.value,
name_ptr.contents.value, cap_ptr.contents.value)
else:
log.debug("plugin_get_version()")
log.warn(self.error_message(rval))
return None
def core_get_api_versions(self):
"""Retrieves API version information from the core library."""
config_ver_ptr = C.pointer(C.c_int())
debug_ver_ptr = C.pointer(C.c_int())
vidext_ver_ptr = C.pointer(C.c_int())
rval = self.m64p.CoreGetAPIVersions(
config_ver_ptr, debug_ver_ptr, vidext_ver_ptr, None)
if rval == M64ERR_SUCCESS:
return (config_ver_ptr.contents.value, debug_ver_ptr.contents.value,
vidext_ver_ptr.contents.value)
else:
log.debug("core_get_api_versions()")
log.warn(self.error_message(rval))
return None
def find_plugins(self, path=None):
"""Searches for plugins in defined directories."""
self.plugin_files = []
@ -132,58 +213,41 @@ class Core:
M64PLUGIN_INPUT: {}
}
for plugin_path in self.plugin_files:
try:
plugin_handle = C.cdll.LoadLibrary(plugin_path)
type_ptr = C.pointer(C.c_int())
ver_ptr = C.pointer(C.c_int())
api_ptr = C.pointer(C.c_int())
name_ptr = C.pointer(C.c_char_p())
rval = plugin_handle.PluginGetVersion(
type_ptr, ver_ptr, api_ptr, name_ptr, None)
if rval != M64ERR_SUCCESS:
log.debug("plugin_load_try()")
log.warn(self.error_message(rval))
continue
except AttributeError:
unload_library(plugin_handle)
continue
except OSError, err:
log.debug("plugin_load_try()")
log.warn(str(err))
continue
else:
plugin_type = type_ptr.contents.value
plugin_version = version_split(ver_ptr.contents.value)
plugin_desc = name_ptr.contents.value
plugin_handle = C.cdll.LoadLibrary(plugin_path)
version = self.plugin_get_version(plugin_handle, plugin_path)
if version:
plugin_type, plugin_version, plugin_api, plugin_desc, plugin_cap = version
plugin_name = os.path.basename(plugin_path)
self.plugin_map[plugin_type][plugin_name] = (plugin_handle, plugin_path,
PLUGIN_NAME[plugin_type], plugin_desc, plugin_version)
self.plugin_startup(plugin_handle, PLUGIN_NAME[plugin_type])
def core_shutdown(self):
"""Saves the config file, then destroys
data structures and releases allocated memory."""
if self.m64p:
self.m64p.CoreShutdown()
unload_library(self.m64p)
del self.m64p
self.m64p = None
return M64ERR_SUCCESS
def plugin_startup(self, handle, name):
"""This function initializes plugin for use by allocating memory,
creating data structures, and loading the configuration data."""
rval = handle.PluginStartup(C.c_void_p(self.m64p._handle),
name, DEBUG_CALLBACK)
if rval not in [M64ERR_SUCCESS, M64ERR_ALREADY_INIT]:
log.debug("plugin_startup()")
log.warn(self.error_message(rval))
log.warn("%s plugin failed to start." % (name))
def plugin_shutdown(self, handle):
"""This function destroys data structures and releases
memory allocated by the plugin library. """
rval = handle.PluginShutdown()
if rval != M64ERR_SUCCESS:
log.debug("plugin_shutdown()")
log.warn(self.error_message(rval))
def plugins_shutdown(self):
"""Destroys data structures and releases allocated memory."""
for plugin_type in PLUGIN_ORDER:
plugin = self.plugins[plugin_type]
if not plugin:
plugin_map = self.plugin_map[plugin_type].values()[0]
else:
plugin_map = self.plugin_map[plugin_type][plugin]
(plugin_handle, plugin_path, plugin_name,
plugin_desc, plugin_version) = plugin_map
rval = plugin_handle.PluginShutdown()
if rval != M64ERR_SUCCESS:
log.debug("plugins_shutdown()")
log.warn(self.error_message(rval))
if self.plugin_map:
for plugin_type in self.plugin_map.keys():
for plugin_map in self.plugin_map[plugin_type].values():
(plugin_handle, plugin_path, plugin_name,
plugin_desc, plugin_version) = plugin_map
self.plugin_shutdown(plugin_handle)
def plugins_unload(self):
"""Unloads plugins from plugin_map."""
@ -207,16 +271,7 @@ class Core:
(plugin_handle, plugin_path, plugin_name,
plugin_desc, plugin_version) = plugin_map
rval = plugin_handle.PluginStartup(
C.c_void_p(self.m64p._handle),
plugin_name,
DEBUG_CALLBACK)
if rval != M64ERR_SUCCESS:
log.debug("attach_plugins()")
log.warn(self.error_message(rval))
log.warn("%s plugin library '%s' failed to start." % (
plugin_name, plugin_path))
self.plugin_startup(plugin_handle, plugin_name)
rval = self.m64p.CoreAttachPlugin(
C.c_int(plugin_type),
C.c_void_p(plugin_handle._handle))
@ -227,7 +282,7 @@ class Core:
plugin_name))
else:
log.info("using %s plugin: '%s' v%s" % (
plugin_name, plugin_desc, plugin_version))
plugin_name, plugin_desc, version_split(plugin_version)))
def detach_plugins(self):
"""Detaches plugins from the emulator core,
@ -331,18 +386,30 @@ class Core:
log.warn(self.error_message(rval))
return rval
def core_state_query(self):
def core_state_query(self, state):
"""Query the emulator core for the
value of a state parameter."""
state_ptr = C.pointer(C.c_int())
rval = self.m64p.CoreDoCommand(
M64CMD_CORE_STATE_QUERY,
C.c_int(M64CORE_EMU_STATE), state_ptr)
C.c_int(state), state_ptr)
if rval != M64ERR_SUCCESS:
log.debug("core_state_query()")
log.warn(self.error_message(rval))
return state_ptr.contents.value
def core_state_set(self, state, value):
"""Sets the value of a state
parameter in the emulator core."""
value_ptr = C.pointer(C.c_int(value))
rval = self.m64p.CoreDoCommand(
M64CMD_CORE_STATE_SET,
C.c_int(state), value_ptr)
if rval != M64ERR_SUCCESS:
log.debug("core_state_set()")
log.warn(self.error_message(rval))
return value_ptr.contents.value
def state_load(self):
"""Loads a saved state file from the current slot."""
rval = self.m64p.CoreDoCommand(
@ -399,6 +466,15 @@ class Core:
log.warn(self.error_message(rval))
return rval
def reset(self):
"""Reset the emulated machine."""
rval = self.m64p.CoreDoCommand(
M64CMD_RESET, C.c_int(1))
if rval != M64ERR_SUCCESS:
log.debug("reset()")
log.warn(self.error_message(rval))
return rval
def override_vidext(self):
"""Overrides the core's internal SDL-based OpenGL functions."""
rval = self.m64p.CoreOverrideVidExt(C.pointer(vidext))

View file

@ -17,8 +17,10 @@
import ctypes as C
CORE_NAME = "mupen64plus"
API_VERSION = 0x20001
FRONTEND_VERSION = "0.1.0"
CORE_API_VERSION = 0x20001
CONFIG_API_VERSION = 0x20000
MINIMUM_CORE_VERSION = 0x016300
FRONTEND_VERSION = "0.1.1"
SIZE_1X = (320, 240)
SIZE_2X = (640, 480)
@ -70,6 +72,12 @@ M64CORE_VIDEO_MODE = 2
M64CORE_SAVESTATE_SLOT = 3
M64CORE_SPEED_FACTOR = 4
M64CORE_SPEED_LIMITER = 5
M64CORE_VIDEO_SIZE = 6
M64CORE_AUDIO_VOLUME = 7
M64CORE_AUDIO_MUTE = 8
M64CORE_INPUT_GAMESHARK = 9
M64CORE_STATE_LOADCOMPLETE = 10
M64CORE_STATE_SAVECOMPLETE = 11
M64CMD_NOP = 0
M64CMD_ROM_OPEN = 1
@ -89,6 +97,9 @@ M64CMD_SEND_SDL_KEYUP = 14
M64CMD_SET_FRAME_CALLBACK = 15
M64CMD_TAKE_NEXT_SCREENSHOT = 16
M64CMD_CORE_STATE_SET = 17
M64CMD_READ_SCREEN = 18
M64CMD_RESET = 19
M64CMD_ADVANCE_FRAME = 20
M64P_GL_DOUBLEBUFFER = 1
M64P_GL_BUFFER_SIZE = 2
@ -128,6 +139,9 @@ PLUGIN_NAME = {
M64PLUGIN_INPUT: "Input"
}
m64p_error = C.c_int
m64p_GLattr = C.c_int
class m64p_rom_header(C.Structure):
_fields_ = [
('init_PI_BSB_DOM1_LAT_REG', C.c_ubyte),
@ -162,3 +176,35 @@ class m64p_cheat_code(C.Structure):
('address', C.c_uint),
('value', C.c_int),
]
class m64p_2d_size(C.Structure):
_fields_ = [
('uiWidth', C.c_uint),
('uiHeight', C.c_uint)
]
FuncInit =C.CFUNCTYPE(m64p_error)
FuncQuit = C.CFUNCTYPE(m64p_error)
FuncListModes = C.CFUNCTYPE(m64p_error, C.POINTER(m64p_2d_size), C.POINTER(C.c_int))
FuncSetMode = C.CFUNCTYPE(m64p_error, C.c_int, C.c_int, C.c_int, C.c_int)
FuncGLGetProc = C.CFUNCTYPE(C.c_void_p, C.c_char_p)
FuncGLSetAttr = C.CFUNCTYPE(m64p_error, m64p_GLattr, C.c_int)
FuncGLGetAttr = C.CFUNCTYPE(m64p_error, m64p_GLattr, C.POINTER(C.c_int))
FuncGLSwapBuf = C.CFUNCTYPE(m64p_error)
FuncSetCaption = C.CFUNCTYPE(m64p_error, C.c_char_p)
FuncToggleFS= C.CFUNCTYPE(m64p_error)
class m64p_video_extension_functions(C.Structure):
_fields_ = [
('Functions', C.c_uint),
('VidExtFuncInit', FuncInit),
('VidExtFuncQuit', FuncQuit),
('VidExtFuncListModes', FuncListModes),
('VidExtFuncSetMode', FuncSetMode),
('VidExtFuncGLGetProc', FuncGLGetProc),
('VidExtFuncGLSetAttr', FuncGLSetAttr),
('VidExtFuncGLGetAttr', FuncGLGetAttr),
('VidExtFuncGLSwapBuf', FuncGLSwapBuf),
('VidExtFuncSetCaption', FuncSetCaption),
('VidExtFuncToggleFS', FuncToggleFS),
]