src/platform/openemu/mGBAGameCore.m (view raw)
1/*
2 Copyright (c) 2016, Jeffrey Pfau
3
4 Redistribution and use in source and binary forms, with or without
5 modification, are permitted provided that the following conditions are met:
6 * Redistributions of source code must retain the above copyright
7 notice, this list of conditions and the following disclaimer.
8 * Redistributions in binary form must reproduce the above copyright
9 notice, this list of conditions and the following disclaimer in the
10 documentation and/or other materials provided with the distribution.
11
12 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ''AS IS''
13 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
15 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
16 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
17 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
18 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
19 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
20 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
21 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
22 POSSIBILITY OF SUCH DAMAGE.
23 */
24
25#import "mGBAGameCore.h"
26
27#include "util/common.h"
28
29#include "gba/cheats.h"
30#include "gba/renderers/video-software.h"
31#include "gba/serialize.h"
32#include "gba/context/context.h"
33#include "util/circle-buffer.h"
34#include "util/memory.h"
35#include "util/vfs.h"
36
37#import <OpenEmuBase/OERingBuffer.h>
38#import "OEGBASystemResponderClient.h"
39#import <OpenGL/gl.h>
40
41#define SAMPLES 1024
42
43@interface mGBAGameCore () <OEGBASystemResponderClient>
44{
45 struct GBAContext context;
46 struct GBAVideoSoftwareRenderer renderer;
47 struct GBACheatDevice cheats;
48 struct GBACheatSet cheatSet;
49 uint16_t keys;
50}
51@end
52
53@implementation mGBAGameCore
54
55- (id)init
56{
57 if ((self = [super init]))
58 {
59 // TODO: Add a log handler
60 GBAContextInit(&context, 0);
61 struct GBAOptions opts = {
62 .useBios = true,
63 .idleOptimization = IDLE_LOOP_REMOVE
64 };
65 GBAConfigLoadDefaults(&context.config, &opts);
66 GBAVideoSoftwareRendererCreate(&renderer);
67 renderer.outputBuffer = malloc(256 * VIDEO_VERTICAL_PIXELS * BYTES_PER_PIXEL);
68 renderer.outputBufferStride = 256;
69 context.renderer = &renderer.d;
70 GBAAudioResizeBuffer(&context.gba->audio, SAMPLES);
71 GBACheatDeviceCreate(&cheats);
72 GBACheatAttachDevice(context.gba, &cheats);
73 GBACheatSetInit(&cheatSet, "openemu");
74 GBACheatAddSet(&cheats, &cheatSet);
75 keys = 0;
76 }
77
78 return self;
79}
80
81- (void)dealloc
82{
83 GBAContextDeinit(&context);
84 GBACheatRemoveSet(&cheats, &cheatSet);
85 GBACheatDeviceDestroy(&cheats);
86 GBACheatSetDeinit(&cheatSet);
87 free(renderer.outputBuffer);
88
89 [super dealloc];
90}
91
92#pragma mark - Execution
93
94- (BOOL)loadFileAtPath:(NSString *)path error:(NSError **)error
95{
96 UNUSED(error);
97 if (!GBAContextLoadROM(&context, [path UTF8String], true)) {
98 return NO;
99 }
100 GBAContextStart(&context);
101 return YES;
102}
103
104- (void)executeFrame
105{
106 GBAContextFrame(&context, keys);
107
108 int16_t samples[SAMPLES * 2];
109 size_t available = 0;
110#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
111 available = blip_samples_avail(context.gba->audio.left);
112 blip_read_samples(context.gba->audio.left, samples, available, true);
113 blip_read_samples(context.gba->audio.right, samples + 1, available, true);
114#else
115#error BLIP_BUF is required for now
116#endif
117 [[self ringBufferAtIndex:0] write:samples maxLength:available * 4];
118}
119
120- (void)resetEmulation
121{
122 ARMReset(context.cpu);
123}
124
125- (void)stopEmulation
126{
127 NSLog(@"Stopping");
128 GBAContextStop(&context);
129 [super stopEmulation];
130}
131
132- (void)setupEmulation
133{
134#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
135 blip_set_rates(context.gba->audio.left, GBA_ARM7TDMI_FREQUENCY, 32768);
136 blip_set_rates(context.gba->audio.right, GBA_ARM7TDMI_FREQUENCY, 32768);
137#endif
138}
139
140#pragma mark - Video
141
142- (OEIntSize)aspectSize
143{
144 return OEIntSizeMake(3, 2);
145}
146
147- (OEIntRect)screenRect
148{
149 return OEIntRectMake(0, 0, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
150}
151
152- (OEIntSize)bufferSize
153{
154 return OEIntSizeMake(256, VIDEO_VERTICAL_PIXELS);
155}
156
157- (const void *)videoBuffer
158{
159 return renderer.outputBuffer;
160}
161
162- (GLenum)pixelFormat
163{
164 return GL_RGBA;
165}
166
167- (GLenum)pixelType
168{
169 return GL_UNSIGNED_INT_8_8_8_8_REV;
170}
171
172- (GLenum)internalPixelFormat
173{
174 return GL_RGB8;
175}
176
177- (NSTimeInterval)frameInterval
178{
179 return GBA_ARM7TDMI_FREQUENCY / (double) VIDEO_TOTAL_LENGTH;
180}
181
182#pragma mark - Audio
183
184- (NSUInteger)channelCount
185{
186 return 2;
187}
188
189- (double)audioSampleRate
190{
191 return 32768;
192}
193
194#pragma mark - Save State
195
196- (NSData *)serializeStateWithError:(NSError **)outError
197{
198 UNUSED(outError);
199 // TODO memory VFile that self-manages memory
200 return nil;
201}
202
203- (BOOL)deserializeState:(NSData *)state withError:(NSError **)outError
204{
205 UNUSED(outError);
206 // TODO VFileFromConstMemory
207 struct VFile* vf = VFileFromMemory((void*) state.bytes, state.length);
208 return GBALoadStateNamed(context.gba, vf, 0);
209}
210
211- (void)saveStateToFileAtPath:(NSString *)fileName completionHandler:(void (^)(BOOL, NSError *))block
212{
213 struct VFile* vf = VFileOpen([fileName UTF8String], O_CREAT | O_TRUNC | O_RDWR);
214 block(GBASaveStateNamed(context.gba, vf, 0), nil);
215}
216
217- (void)loadStateFromFileAtPath:(NSString *)fileName completionHandler:(void (^)(BOOL, NSError *))block
218{
219 struct VFile* vf = VFileOpen([fileName UTF8String], O_RDONLY);
220 block(GBALoadStateNamed(context.gba, vf, 0), nil);
221}
222
223#pragma mark - Input
224
225const int GBAMap[] = {
226 GBA_KEY_UP,
227 GBA_KEY_DOWN,
228 GBA_KEY_LEFT,
229 GBA_KEY_RIGHT,
230 GBA_KEY_A,
231 GBA_KEY_B,
232 GBA_KEY_L,
233 GBA_KEY_R,
234 GBA_KEY_START,
235 GBA_KEY_SELECT
236};
237
238- (oneway void)didPushGBAButton:(OEGBAButton)button forPlayer:(NSUInteger)player
239{
240 UNUSED(player);
241 keys |= 1 << GBAMap[button];
242}
243
244- (oneway void)didReleaseGBAButton:(OEGBAButton)button forPlayer:(NSUInteger)player
245{
246 UNUSED(player);
247 keys &= ~(1 << GBAMap[button]);
248}
249
250@end
251