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