all repos — mgba @ 25b4faef1262e37154e955d21d0fe1befa07e23b

mGBA Game Boy Advance Emulator

src/platform/python/mgba/core.py (view raw)

  1# Copyright (c) 2013-2016 Jeffrey Pfau
  2#
  3# This Source Code Form is subject to the terms of the Mozilla Public
  4# License, v. 2.0. If a copy of the MPL was not distributed with this
  5# file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6from ._pylib import ffi, lib
  7from . import tile
  8from cached_property import cached_property
  9
 10def find(path):
 11    core = lib.mCoreFind(path.encode('UTF-8'))
 12    if core == ffi.NULL:
 13        return None
 14    return Core._init(core)
 15
 16def findVF(vf):
 17    core = lib.mCoreFindVF(vf.handle)
 18    if core == ffi.NULL:
 19        return None
 20    return Core._init(core)
 21
 22def loadPath(path):
 23    core = find(path)
 24    if not core or not core.loadFile(path):
 25        return None
 26    return core
 27
 28def loadVF(vf):
 29    core = findVF(vf)
 30    if not core or not core.loadROM(vf):
 31        return None
 32    return core
 33
 34def needsReset(f):
 35    def wrapper(self, *args, **kwargs):
 36        if not self._wasReset:
 37            raise RuntimeError("Core must be reset first")
 38        return f(self, *args, **kwargs)
 39    return wrapper
 40
 41def protected(f):
 42    def wrapper(self, *args, **kwargs):
 43        if self._protected:
 44            raise RuntimeError("Core is protected")
 45        return f(self, *args, **kwargs)
 46    return wrapper
 47
 48class Core(object):
 49    def __init__(self, native):
 50        self._core = native
 51        self._wasReset = False
 52        self._protected = False
 53
 54    @cached_property
 55    def tiles(self):
 56        return tile.TileView(self)
 57
 58    @classmethod
 59    def _init(cls, native):
 60        core = ffi.gc(native, native.deinit)
 61        success = bool(core.init(core))
 62        lib.mCoreInitConfig(core, ffi.NULL)
 63        if not success:
 64            raise RuntimeError("Failed to initialize core")
 65        return cls._detect(core)
 66
 67    def _deinit(self):
 68        self._core.deinit(self._core)
 69
 70    @classmethod
 71    def _detect(cls, core):
 72        if hasattr(cls, 'PLATFORM_GBA') and core.platform(core) == cls.PLATFORM_GBA:
 73            return GBA(core)
 74        if hasattr(cls, 'PLATFORM_GB') and core.platform(core) == cls.PLATFORM_GB:
 75            return GB(core)
 76        return Core(core)
 77
 78    @protected
 79    def loadFile(self, path):
 80        return bool(lib.mCoreLoadFile(self._core, path.encode('UTF-8')))
 81
 82    def isROM(self, vf):
 83        return bool(self._core.isROM(vf.handle))
 84
 85    def loadROM(self, vf):
 86        return bool(self._core.loadROM(self._core, vf.handle))
 87
 88    def loadSave(self, vf):
 89        return bool(self._core.loadSave(self._core, vf.handle))
 90
 91    def loadTemporarySave(self, vf):
 92        return bool(self._core.loadTemporarySave(self._core, vf.handle))
 93
 94    @protected
 95    def loadPatch(self, vf):
 96        return bool(self._core.loadPatch(self._core, vf.handle))
 97
 98    def autoloadSave(self):
 99        return bool(lib.mCoreAutoloadSave(self._core))
100
101    def autoloadPatch(self):
102        return bool(lib.mCoreAutoloadPatch(self._core))
103
104    def platform(self):
105        return self._core.platform(self._core)
106
107    def desiredVideoDimensions(self):
108        width = ffi.new("unsigned*")
109        height = ffi.new("unsigned*")
110        self._core.desiredVideoDimensions(self._core, width, height)
111        return width[0], height[0]
112
113    def setVideoBuffer(self, image):
114        self._core.setVideoBuffer(self._core, image.buffer, image.stride)
115
116    @protected
117    def reset(self):
118        self._core.reset(self._core)
119        self._wasReset = True
120
121    @needsReset
122    @protected
123    def runFrame(self):
124        self._core.runFrame(self._core)
125
126    @needsReset
127    @protected
128    def runLoop(self):
129        self._core.runLoop(self._core)
130
131    @needsReset
132    def step(self):
133        self._core.step(self._core)
134
135    @staticmethod
136    def _keysToInt(*args, **kwargs):
137        keys = 0
138        if 'raw' in kwargs:
139            keys = kwargs['raw']
140        for key in args:
141            keys |= 1 << key
142        return keys
143
144    def setKeys(self, *args, **kwargs):
145        self._core.setKeys(self._core, self._keysToInt(*args, **kwargs))
146
147    def addKeys(self, *args, **kwargs):
148        self._core.addKeys(self._core, self._keysToInt(*args, **kwargs))
149
150    def clearKeys(self, *args, **kwargs):
151        self._core.clearKeys(self._core, self._keysToInt(*args, **kwargs))
152
153    @needsReset
154    def frameCounter(self):
155        return self._core.frameCounter(self._core)
156
157    def frameCycles(self):
158        return self._core.frameCycles(self._core)
159
160    def frequency(self):
161        return self._core.frequency(self._core)
162
163    def getGameTitle(self):
164        title = ffi.new("char[16]")
165        self._core.getGameTitle(self._core, title)
166        return ffi.string(title, 16).decode("ascii")
167
168    def getGameCode(self):
169        code = ffi.new("char[12]")
170        self._core.getGameCode(self._core, code)
171        return ffi.string(code, 12).decode("ascii")
172
173class ICoreOwner(object):
174    def claim(self):
175        raise NotImplementedError
176
177    def release(self):
178        raise NotImplementedError
179
180    def __enter__(self):
181        self.core = self.claim()
182        self.core._protected = True
183        return self.core
184
185    def __exit__(self, type, value, traceback):
186        self.core._protected = False
187        self.release()
188
189class IRunner(object):
190    def pause(self):
191        raise NotImplementedError
192
193    def unpause(self):
194        raise NotImplementedError
195
196    def useCore(self):
197        raise NotImplementedError
198
199    def isRunning(self):
200        raise NotImplementedError
201
202    def isPaused(self):
203        raise NotImplementedError
204
205if hasattr(lib, 'PLATFORM_GBA'):
206    from .gba import GBA
207    Core.PLATFORM_GBA = lib.PLATFORM_GBA
208
209if hasattr(lib, 'PLATFORM_GB'):
210    from .gb import GB
211    from .lr35902 import LR35902Core
212    Core.PLATFORM_GB = lib.PLATFORM_GB