From 835df57b97bfa7d5068c7912ee51526b78cfdd80 Mon Sep 17 00:00:00 2001 From: gen2brain Date: Sat, 6 Apr 2013 11:11:44 +0200 Subject: [PATCH] restructure, add check_version() --- src/m64py/core/core.py | 298 ++++++++++++++++++++++++++--------------- src/m64py/core/defs.py | 50 ++++++- 2 files changed, 235 insertions(+), 113 deletions(-) diff --git a/src/m64py/core/core.py b/src/m64py/core/core.py index 795340e..e36ca95 100644 --- a/src/m64py/core/core.py +++ b/src/m64py/core/core.py @@ -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)) diff --git a/src/m64py/core/defs.py b/src/m64py/core/defs.py index 4d97e1a..f7e3df3 100644 --- a/src/m64py/core/defs.py +++ b/src/m64py/core/defs.py @@ -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), + ]