src/platform/wii/main.c (view raw)
1/* Copyright (c) 2013-2015 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/. */
6#define asm __asm__
7
8#include <fat.h>
9#include <gccore.h>
10#include <ogc/machine/processor.h>
11#include <malloc.h>
12#include <unistd.h>
13#include <wiiuse/wpad.h>
14
15#include "util/common.h"
16
17#include "gba/renderers/video-software.h"
18#include "gba/context/context.h"
19#include "gba/gui/gui-runner.h"
20#include "util/gui.h"
21#include "util/gui/file-select.h"
22#include "util/gui/font.h"
23#include "util/gui/menu.h"
24#include "util/vfs.h"
25
26#define GCN1_INPUT 0x47434E31
27#define GCN2_INPUT 0x47434E32
28#define WIIMOTE_INPUT 0x5749494D
29#define CLASSIC_INPUT 0x57494943
30
31static void _mapKey(struct GBAInputMap* map, uint32_t binding, int nativeKey, enum GBAKey key) {
32 GBAInputBindKey(map, binding, __builtin_ctz(nativeKey), key);
33}
34
35static enum ScreenMode {
36 SM_PA,
37 SM_SF,
38 SM_MAX
39} screenMode = SM_PA;
40
41enum FilterMode {
42 FM_NEAREST,
43 FM_LINEAR,
44 FM_MAX
45};
46
47#define SAMPLES 1024
48
49static void _retraceCallback(u32 count);
50
51static void _audioDMA(void);
52static void _setRumble(struct GBARumble* rumble, int enable);
53static void _sampleRotation(struct GBARotationSource* source);
54static int32_t _readTiltX(struct GBARotationSource* source);
55static int32_t _readTiltY(struct GBARotationSource* source);
56static int32_t _readGyroZ(struct GBARotationSource* source);
57
58static void _drawStart(void);
59static void _drawEnd(void);
60static uint32_t _pollInput(void);
61static enum GUICursorState _pollCursor(int* x, int* y);
62static void _guiPrepare(void);
63static void _guiFinish(void);
64
65static void _setup(struct GBAGUIRunner* runner);
66static void _teardown(struct GBAGUIRunner* runner);
67static void _gameLoaded(struct GBAGUIRunner* runner);
68static void _gameUnloaded(struct GBAGUIRunner* runner);
69static void _unpaused(struct GBAGUIRunner* runner);
70static void _drawFrame(struct GBAGUIRunner* runner, bool faded);
71static uint16_t _pollGameInput(struct GBAGUIRunner* runner);
72
73static s8 WPAD_StickX(u8 chan, u8 right);
74static s8 WPAD_StickY(u8 chan, u8 right);
75
76static struct GBAVideoSoftwareRenderer renderer;
77static struct GBARumble rumble;
78static struct GBARotationSource rotation;
79static GXRModeObj* vmode;
80static Mtx model, view, modelview;
81static uint16_t* texmem;
82static GXTexObj tex;
83static int32_t tiltX;
84static int32_t tiltY;
85static int32_t gyroZ;
86static uint32_t retraceCount;
87static uint32_t referenceRetraceCount;
88static int scaleFactor;
89
90static void* framebuffer[2] = { 0, 0 };
91static int whichFb = 0;
92
93static struct GBAStereoSample audioBuffer[3][SAMPLES] __attribute__((__aligned__(32)));
94static volatile size_t audioBufferSize = 0;
95static volatile int currentAudioBuffer = 0;
96
97static struct GUIFont* font;
98
99static void reconfigureScreen(GXRModeObj* vmode) {
100 free(framebuffer[0]);
101 free(framebuffer[1]);
102
103 framebuffer[0] = SYS_AllocateFramebuffer(vmode);
104 framebuffer[1] = SYS_AllocateFramebuffer(vmode);
105
106 VIDEO_SetBlack(true);
107 VIDEO_Configure(vmode);
108 VIDEO_SetNextFramebuffer(framebuffer[whichFb]);
109 VIDEO_SetBlack(false);
110 VIDEO_Flush();
111 VIDEO_WaitVSync();
112 if (vmode->viTVMode & VI_NON_INTERLACE) {
113 VIDEO_WaitVSync();
114 }
115 GX_SetViewport(0, 0, vmode->fbWidth, vmode->efbHeight, 0, 1);
116
117 f32 yscale = GX_GetYScaleFactor(vmode->efbHeight, vmode->xfbHeight);
118 u32 xfbHeight = GX_SetDispCopyYScale(yscale);
119 GX_SetScissor(0, 0, vmode->viWidth, vmode->viWidth);
120 GX_SetDispCopySrc(0, 0, vmode->fbWidth, vmode->efbHeight);
121 GX_SetDispCopyDst(vmode->fbWidth, xfbHeight);
122 GX_SetCopyFilter(vmode->aa, vmode->sample_pattern, GX_TRUE, vmode->vfilter);
123 GX_SetFieldMode(vmode->field_rendering, ((vmode->viHeight == 2 * vmode->xfbHeight) ? GX_ENABLE : GX_DISABLE));
124
125 int hfactor = vmode->fbWidth / VIDEO_HORIZONTAL_PIXELS;
126 int vfactor = vmode->efbHeight / VIDEO_VERTICAL_PIXELS;
127 if (hfactor > vfactor) {
128 scaleFactor = vfactor;
129 } else {
130 scaleFactor = hfactor;
131 }
132};
133
134int main() {
135 VIDEO_Init();
136 PAD_Init();
137 WPAD_Init();
138 WPAD_SetDataFormat(0, WPAD_FMT_BTNS_ACC_IR);
139 AUDIO_Init(0);
140 AUDIO_SetDSPSampleRate(AI_SAMPLERATE_48KHZ);
141 AUDIO_RegisterDMACallback(_audioDMA);
142
143 memset(audioBuffer, 0, sizeof(audioBuffer));
144
145#if !defined(COLOR_16_BIT) && !defined(COLOR_5_6_5)
146#error This pixel format is unsupported. Please use -DCOLOR_16-BIT -DCOLOR_5_6_5
147#endif
148
149 vmode = VIDEO_GetPreferredMode(0);
150
151 GXColor bg = { 0, 0, 0, 0xFF };
152 void* fifo = memalign(32, 0x40000);
153 memset(fifo, 0, 0x40000);
154 GX_Init(fifo, 0x40000);
155 GX_SetCopyClear(bg, 0x00FFFFFF);
156
157 reconfigureScreen(vmode);
158
159 GX_SetCullMode(GX_CULL_NONE);
160 GX_SetDispCopyGamma(GX_GM_1_0);
161
162 GX_ClearVtxDesc();
163 GX_SetVtxDesc(GX_VA_POS, GX_DIRECT);
164 GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT);
165 GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT);
166
167 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 0);
168 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0);
169 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
170
171 GX_SetNumChans(1);
172 GX_SetNumTexGens(1);
173 GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
174 GX_SetTevOp(GX_TEVSTAGE0, GX_MODULATE);
175
176 GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);
177 GX_InvVtxCache();
178 GX_InvalidateTexAll();
179
180 guVector cam = { 0.0f, 0.0f, 0.0f };
181 guVector up = { 0.0f, 1.0f, 0.0f };
182 guVector look = { 0.0f, 0.0f, -1.0f };
183 guLookAt(view, &cam, &up, &look);
184
185 guMtxIdentity(model);
186 guMtxTransApply(model, model, 0.0f, 0.0f, -6.0f);
187 guMtxConcat(view, model, modelview);
188 GX_LoadPosMtxImm(modelview, GX_PNMTX0);
189
190 texmem = memalign(32, 256 * 256 * BYTES_PER_PIXEL);
191 memset(texmem, 0, 256 * 256 * BYTES_PER_PIXEL);
192 GX_InitTexObj(&tex, texmem, 256, 256, GX_TF_RGB565, GX_CLAMP, GX_CLAMP, GX_FALSE);
193
194 VIDEO_SetPostRetraceCallback(_retraceCallback);
195
196 font = GUIFontCreate();
197
198 fatInitDefault();
199
200 rumble.setRumble = _setRumble;
201
202 rotation.sample = _sampleRotation;
203 rotation.readTiltX = _readTiltX;
204 rotation.readTiltY = _readTiltY;
205 rotation.readGyroZ = _readGyroZ;
206
207 struct GBAGUIRunner runner = {
208 .params = {
209 vmode->fbWidth * 1.6, vmode->efbHeight * 1.6,
210 font, "",
211 _drawStart, _drawEnd,
212 _pollInput, _pollCursor,
213 0,
214 _guiPrepare, _guiFinish,
215
216 GUI_PARAMS_TRAIL
217 },
218 .configExtra = (struct GUIMenuItem[]) {
219 {
220 .title = "Screen mode",
221 .data = "screenMode",
222 .submenu = 0,
223 .state = 0,
224 .validStates = (const char*[]) {
225 "Pixel-Accurate",
226 "Stretched",
227 0
228 }
229 },
230 {
231 .title = "Filtering",
232 .data = "filter",
233 .submenu = 0,
234 .state = 0,
235 .validStates = (const char*[]) {
236 "Pixelated",
237 "Resampled",
238 0
239 }
240 }
241 },
242 .nConfigExtra = 2,
243 .setup = _setup,
244 .teardown = _teardown,
245 .gameLoaded = _gameLoaded,
246 .gameUnloaded = _gameUnloaded,
247 .prepareForFrame = 0,
248 .drawFrame = _drawFrame,
249 .paused = _gameUnloaded,
250 .unpaused = _unpaused,
251 .pollGameInput = _pollGameInput
252 };
253 GBAGUIInit(&runner, "wii");
254 GBAGUIRunloop(&runner);
255 GBAGUIDeinit(&runner);
256
257 free(fifo);
258
259 free(renderer.outputBuffer);
260 GUIFontDestroy(font);
261
262 free(framebuffer[0]);
263 free(framebuffer[1]);
264
265 return 0;
266}
267
268static void _audioDMA(void) {
269 if (!audioBufferSize) {
270 return;
271 }
272 DCFlushRange(audioBuffer[currentAudioBuffer], audioBufferSize * sizeof(struct GBAStereoSample));
273 AUDIO_InitDMA((u32) audioBuffer[currentAudioBuffer], audioBufferSize * sizeof(struct GBAStereoSample));
274 currentAudioBuffer = (currentAudioBuffer + 1) % 3;
275 audioBufferSize = 0;
276}
277
278static void _drawStart(void) {
279 u32 level = 0;
280 _CPU_ISR_Disable(level);
281 if (referenceRetraceCount >= retraceCount) {
282 VIDEO_WaitVSync();
283 }
284 _CPU_ISR_Restore(level);
285
286 GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE);
287 GX_SetColorUpdate(GX_TRUE);
288
289 GX_SetViewport(0, 0, vmode->fbWidth, vmode->efbHeight, 0, 1);
290}
291
292static void _drawEnd(void) {
293 whichFb = !whichFb;
294
295 GX_CopyDisp(framebuffer[whichFb], GX_TRUE);
296 GX_DrawDone();
297 VIDEO_SetNextFramebuffer(framebuffer[whichFb]);
298 VIDEO_Flush();
299
300 u32 level = 0;
301 _CPU_ISR_Disable(level);
302 ++referenceRetraceCount;
303 _CPU_ISR_Restore(level);
304}
305
306static uint32_t _pollInput(void) {
307 PAD_ScanPads();
308 u16 padkeys = PAD_ButtonsHeld(0);
309
310 WPAD_ScanPads();
311 u32 wiiPad = WPAD_ButtonsHeld(0);
312 u32 ext = 0;
313 WPAD_Probe(0, &ext);
314
315 int keys = 0;
316 int x = PAD_StickX(0);
317 int y = PAD_StickY(0);
318 int w_x = WPAD_StickX(0,0);
319 int w_y = WPAD_StickY(0,0);
320 if (x < -0x40 || w_x < -0x40) {
321 keys |= 1 << GUI_INPUT_LEFT;
322 }
323 if (x > 0x40 || w_x > 0x40) {
324 keys |= 1 << GUI_INPUT_RIGHT;
325 }
326 if (y < -0x40 || w_y <- 0x40) {
327 keys |= 1 << GUI_INPUT_DOWN;
328 }
329 if (y > 0x40 || w_y > 0x40) {
330 keys |= 1 << GUI_INPUT_UP;
331 }
332 if ((padkeys & PAD_BUTTON_A) || (wiiPad & WPAD_BUTTON_2) ||
333 ((ext == WPAD_EXP_CLASSIC) && (wiiPad & (WPAD_CLASSIC_BUTTON_A | WPAD_CLASSIC_BUTTON_Y)))) {
334 keys |= 1 << GUI_INPUT_SELECT;
335 }
336 if ((padkeys & PAD_BUTTON_B) || (wiiPad & WPAD_BUTTON_1) || (wiiPad & WPAD_BUTTON_B) ||
337 ((ext == WPAD_EXP_CLASSIC) && (wiiPad & (WPAD_CLASSIC_BUTTON_B | WPAD_CLASSIC_BUTTON_X)))) {
338 keys |= 1 << GUI_INPUT_BACK;
339 }
340 if ((padkeys & PAD_TRIGGER_Z) || (wiiPad & WPAD_BUTTON_HOME) ||
341 ((ext == WPAD_EXP_CLASSIC) && (wiiPad & (WPAD_CLASSIC_BUTTON_HOME)))) {
342 keys |= 1 << GUI_INPUT_CANCEL;
343 }
344 if ((padkeys & PAD_BUTTON_LEFT)|| (wiiPad & WPAD_BUTTON_UP) ||
345 ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_LEFT))) {
346 keys |= 1 << GUI_INPUT_LEFT;
347 }
348 if ((padkeys & PAD_BUTTON_RIGHT) || (wiiPad & WPAD_BUTTON_DOWN) ||
349 ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_RIGHT))) {
350 keys |= 1 << GUI_INPUT_RIGHT;
351 }
352 if ((padkeys & PAD_BUTTON_UP) || (wiiPad & WPAD_BUTTON_RIGHT) ||
353 ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_UP))) {
354 keys |= 1 << GUI_INPUT_UP;
355 }
356 if ((padkeys & PAD_BUTTON_DOWN) || (wiiPad & WPAD_BUTTON_LEFT) ||
357 ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_DOWN))) {
358 keys |= 1 << GUI_INPUT_DOWN;
359 }
360 return keys;
361}
362
363static enum GUICursorState _pollCursor(int* x, int* y) {
364 ir_t ir;
365 WPAD_IR(0, &ir);
366 if (!ir.smooth_valid) {
367 return GUI_CURSOR_NOT_PRESENT;
368 }
369 *x = ir.sx;
370 *y = ir.sy;
371 WPAD_ScanPads();
372 u32 wiiPad = WPAD_ButtonsHeld(0);
373 if (wiiPad & WPAD_BUTTON_A) {
374 return GUI_CURSOR_DOWN;
375 }
376 return GUI_CURSOR_UP;
377}
378
379void _reproj(int w, int h) {
380 Mtx44 proj;
381 int top = (vmode->efbHeight - h) / 2;
382 int left = (vmode->fbWidth - w) / 2;
383 guOrtho(proj, -top, top + h, -left, left + w, 0, 300);
384 GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
385}
386
387void _reproj2(int w, int h) {
388 Mtx44 proj;
389 s16 top = 20;
390 guOrtho(proj, -top, top + h, 0, w, 0, 300);
391 GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
392}
393
394void _guiPrepare(void) {
395 _reproj2(vmode->fbWidth * 1.6, vmode->efbHeight * 1.6);
396}
397
398void _guiFinish(void) {
399 if (screenMode == SM_PA) {
400 _reproj(VIDEO_HORIZONTAL_PIXELS * scaleFactor, VIDEO_VERTICAL_PIXELS * scaleFactor);
401 } else {
402 _reproj2(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
403 }
404}
405
406void _setup(struct GBAGUIRunner* runner) {
407 runner->context.gba->rumble = &rumble;
408 runner->context.gba->rotationSource = &rotation;
409
410 struct GBAOptions opts = {
411 .useBios = true,
412 .idleOptimization = IDLE_LOOP_DETECT
413 };
414 GBAConfigLoadDefaults(&runner->context.config, &opts);
415 _mapKey(&runner->context.inputMap, GCN1_INPUT, PAD_BUTTON_A, GBA_KEY_A);
416 _mapKey(&runner->context.inputMap, GCN1_INPUT, PAD_BUTTON_B, GBA_KEY_B);
417 _mapKey(&runner->context.inputMap, GCN1_INPUT, PAD_BUTTON_START, GBA_KEY_START);
418 _mapKey(&runner->context.inputMap, GCN1_INPUT, PAD_BUTTON_X, GBA_KEY_SELECT);
419 _mapKey(&runner->context.inputMap, GCN2_INPUT, PAD_BUTTON_Y, GBA_KEY_SELECT);
420 _mapKey(&runner->context.inputMap, GCN1_INPUT, PAD_BUTTON_UP, GBA_KEY_UP);
421 _mapKey(&runner->context.inputMap, GCN1_INPUT, PAD_BUTTON_DOWN, GBA_KEY_DOWN);
422 _mapKey(&runner->context.inputMap, GCN1_INPUT, PAD_BUTTON_LEFT, GBA_KEY_LEFT);
423 _mapKey(&runner->context.inputMap, GCN1_INPUT, PAD_BUTTON_RIGHT, GBA_KEY_RIGHT);
424 _mapKey(&runner->context.inputMap, GCN1_INPUT, PAD_TRIGGER_L, GBA_KEY_L);
425 _mapKey(&runner->context.inputMap, GCN1_INPUT, PAD_TRIGGER_R, GBA_KEY_R);
426
427 _mapKey(&runner->context.inputMap, WIIMOTE_INPUT, WPAD_BUTTON_2, GBA_KEY_A);
428 _mapKey(&runner->context.inputMap, WIIMOTE_INPUT, WPAD_BUTTON_1, GBA_KEY_B);
429 _mapKey(&runner->context.inputMap, WIIMOTE_INPUT, WPAD_BUTTON_PLUS, GBA_KEY_START);
430 _mapKey(&runner->context.inputMap, WIIMOTE_INPUT, WPAD_BUTTON_MINUS, GBA_KEY_SELECT);
431 _mapKey(&runner->context.inputMap, WIIMOTE_INPUT, WPAD_BUTTON_RIGHT, GBA_KEY_UP);
432 _mapKey(&runner->context.inputMap, WIIMOTE_INPUT, WPAD_BUTTON_LEFT, GBA_KEY_DOWN);
433 _mapKey(&runner->context.inputMap, WIIMOTE_INPUT, WPAD_BUTTON_UP, GBA_KEY_LEFT);
434 _mapKey(&runner->context.inputMap, WIIMOTE_INPUT, WPAD_BUTTON_DOWN, GBA_KEY_RIGHT);
435 _mapKey(&runner->context.inputMap, WIIMOTE_INPUT, WPAD_BUTTON_B, GBA_KEY_L);
436 _mapKey(&runner->context.inputMap, WIIMOTE_INPUT, WPAD_BUTTON_A, GBA_KEY_R);
437
438 _mapKey(&runner->context.inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_A, GBA_KEY_A);
439 _mapKey(&runner->context.inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_B, GBA_KEY_B);
440 _mapKey(&runner->context.inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_PLUS, GBA_KEY_START);
441 _mapKey(&runner->context.inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_MINUS, GBA_KEY_SELECT);
442 _mapKey(&runner->context.inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_UP, GBA_KEY_UP);
443 _mapKey(&runner->context.inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_DOWN, GBA_KEY_DOWN);
444 _mapKey(&runner->context.inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_LEFT, GBA_KEY_LEFT);
445 _mapKey(&runner->context.inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_RIGHT, GBA_KEY_RIGHT);
446 _mapKey(&runner->context.inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_FULL_L, GBA_KEY_L);
447 _mapKey(&runner->context.inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_FULL_R, GBA_KEY_R);
448
449 struct GBAAxis desc = { GBA_KEY_RIGHT, GBA_KEY_LEFT, 0x40, -0x40 };
450 GBAInputBindAxis(&runner->context.inputMap, GCN1_INPUT, 0, &desc);
451 GBAInputBindAxis(&runner->context.inputMap, CLASSIC_INPUT, 0, &desc);
452 desc = (struct GBAAxis) { GBA_KEY_UP, GBA_KEY_DOWN, 0x40, -0x40 };
453 GBAInputBindAxis(&runner->context.inputMap, GCN1_INPUT, 1, &desc);
454 GBAInputBindAxis(&runner->context.inputMap, CLASSIC_INPUT, 1, &desc);
455 GBAInputMapLoad(&runner->context.inputMap, GCN1_INPUT, GBAConfigGetInput(&runner->context.config));
456 GBAInputMapLoad(&runner->context.inputMap, GCN2_INPUT, GBAConfigGetInput(&runner->context.config));
457 GBAInputMapLoad(&runner->context.inputMap, WIIMOTE_INPUT, GBAConfigGetInput(&runner->context.config));
458 GBAInputMapLoad(&runner->context.inputMap, CLASSIC_INPUT, GBAConfigGetInput(&runner->context.config));
459
460 GBAVideoSoftwareRendererCreate(&renderer);
461 renderer.outputBuffer = memalign(32, 256 * 256 * BYTES_PER_PIXEL);
462 renderer.outputBufferStride = 256;
463 runner->context.renderer = &renderer.d;
464
465 GBAAudioResizeBuffer(&runner->context.gba->audio, SAMPLES);
466
467#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
468 double ratio = GBAAudioCalculateRatio(1, 60 / 1.001, 1);
469 blip_set_rates(runner->context.gba->audio.left, GBA_ARM7TDMI_FREQUENCY, 48000 * ratio);
470 blip_set_rates(runner->context.gba->audio.right, GBA_ARM7TDMI_FREQUENCY, 48000 * ratio);
471#endif
472}
473
474void _teardown(struct GBAGUIRunner* runner) {
475 GBAInputMapSave(&runner->context.inputMap, GCN1_INPUT, GBAConfigGetInput(&runner->context.config));
476 GBAInputMapSave(&runner->context.inputMap, GCN2_INPUT, GBAConfigGetInput(&runner->context.config));
477 GBAInputMapSave(&runner->context.inputMap, WIIMOTE_INPUT, GBAConfigGetInput(&runner->context.config));
478 GBAInputMapSave(&runner->context.inputMap, CLASSIC_INPUT, GBAConfigGetInput(&runner->context.config));
479}
480
481void _gameUnloaded(struct GBAGUIRunner* runner) {
482 UNUSED(runner);
483 AUDIO_StopDMA();
484}
485
486void _gameLoaded(struct GBAGUIRunner* runner) {
487 if (runner->context.gba->memory.hw.devices & HW_GYRO) {
488 int i;
489 for (i = 0; i < 6; ++i) {
490 u32 result = WPAD_SetMotionPlus(0, 1);
491 if (result == WPAD_ERR_NONE) {
492 break;
493 }
494 sleep(1);
495 }
496 }
497 _unpaused(runner);
498}
499
500void _unpaused(struct GBAGUIRunner* runner) {
501 u32 level = 0;
502 _CPU_ISR_Disable(level);
503 referenceRetraceCount = retraceCount;
504 _CPU_ISR_Restore(level);
505
506 unsigned mode;
507 if (GBAConfigGetUIntValue(&runner->context.config, "screenMode", &mode) && mode < SM_MAX) {
508 screenMode = mode;
509 }
510 if (GBAConfigGetUIntValue(&runner->context.config, "filter", &mode) && mode < FM_MAX) {
511 switch (mode) {
512 case FM_NEAREST:
513 default:
514 GX_InitTexObjFilterMode(&tex, GX_NEAR, GX_NEAR);
515 break;
516 case FM_LINEAR:
517 GX_InitTexObjFilterMode(&tex, GX_LINEAR, GX_LINEAR);
518 break;
519 }
520 }
521 _guiFinish();
522}
523
524void _drawFrame(struct GBAGUIRunner* runner, bool faded) {
525#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
526 int available = blip_samples_avail(runner->context.gba->audio.left);
527 if (available + audioBufferSize > SAMPLES) {
528 available = SAMPLES - audioBufferSize;
529 }
530 available &= ~((32 / sizeof(struct GBAStereoSample)) - 1); // Force align to 32 bytes
531 if (available > 0) {
532 // These appear to be reversed for AUDIO_InitDMA
533 blip_read_samples(runner->context.gba->audio.left, &audioBuffer[currentAudioBuffer][audioBufferSize].right, available, true);
534 blip_read_samples(runner->context.gba->audio.right, &audioBuffer[currentAudioBuffer][audioBufferSize].left, available, true);
535 audioBufferSize += available;
536 }
537 if (audioBufferSize == SAMPLES && !AUDIO_GetDMAEnableFlag()) {
538 _audioDMA();
539 AUDIO_StartDMA();
540 }
541#endif
542
543 uint32_t color = 0xFFFFFF3F;
544 if (!faded) {
545 color |= 0xC0;
546 }
547 size_t x, y;
548 uint64_t* texdest = (uint64_t*) texmem;
549 uint64_t* texsrc = (uint64_t*) renderer.outputBuffer;
550 for (y = 0; y < VIDEO_VERTICAL_PIXELS; y += 4) {
551 for (x = 0; x < VIDEO_HORIZONTAL_PIXELS >> 2; ++x) {
552 texdest[0 + x * 4 + y * 64] = texsrc[0 + x + y * 64];
553 texdest[1 + x * 4 + y * 64] = texsrc[64 + x + y * 64];
554 texdest[2 + x * 4 + y * 64] = texsrc[128 + x + y * 64];
555 texdest[3 + x * 4 + y * 64] = texsrc[192 + x + y * 64];
556 }
557 }
558 DCFlushRange(texdest, 256 * 256 * BYTES_PER_PIXEL);
559
560 if (faded) {
561 GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_NOOP);
562 } else {
563 GX_SetBlendMode(GX_BM_NONE, GX_BL_ONE, GX_BL_ZERO, GX_LO_NOOP);
564 }
565 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0);
566 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
567 GX_InvalidateTexAll();
568 GX_LoadTexObj(&tex, GX_TEXMAP0);
569
570 s16 vertSize = 256;
571 if (screenMode == SM_PA) {
572 vertSize *= scaleFactor;
573 }
574
575 GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
576 GX_Position2s16(0, vertSize);
577 GX_Color1u32(color);
578 GX_TexCoord2s16(0, 1);
579
580 GX_Position2s16(vertSize, vertSize);
581 GX_Color1u32(color);
582 GX_TexCoord2s16(1, 1);
583
584 GX_Position2s16(vertSize, 0);
585 GX_Color1u32(color);
586 GX_TexCoord2s16(1, 0);
587
588 GX_Position2s16(0, 0);
589 GX_Color1u32(color);
590 GX_TexCoord2s16(0, 0);
591 GX_End();
592}
593
594uint16_t _pollGameInput(struct GBAGUIRunner* runner) {
595 UNUSED(runner);
596 PAD_ScanPads();
597 u16 padkeys = PAD_ButtonsHeld(0);
598 WPAD_ScanPads();
599 u32 wiiPad = WPAD_ButtonsHeld(0);
600 u32 ext = 0;
601 WPAD_Probe(0, &ext);
602 uint16_t keys = GBAInputMapKeyBits(&runner->context.inputMap, GCN1_INPUT, padkeys, 0);
603 keys |= GBAInputMapKeyBits(&runner->context.inputMap, GCN2_INPUT, padkeys, 0);
604 keys |= GBAInputMapKeyBits(&runner->context.inputMap, WIIMOTE_INPUT, wiiPad, 0);
605
606 enum GBAKey angles = GBAInputMapAxis(&runner->context.inputMap, GCN1_INPUT, 0, PAD_StickX(0));
607 if (angles != GBA_KEY_NONE) {
608 keys |= 1 << angles;
609 }
610 angles = GBAInputMapAxis(&runner->context.inputMap, GCN1_INPUT, 1, PAD_StickY(0));
611 if (angles != GBA_KEY_NONE) {
612 keys |= 1 << angles;
613 }
614 /*if (ext == WPAD_EXP_CLASSIC) {
615 keys |= GBAInputMapKeyBits(&runner->context.inputMap, CLASSIC_INPUT, wiiPad, 0);
616 angles = GBAInputMapAxis(&runner->context.inputMap, CLASSIC_INPUT, 0, WPAD_StickX(0, 0));
617 if (angles != GBA_KEY_NONE) {
618 keys |= 1 << angles;
619 }
620 angles = GBAInputMapAxis(&runner->context.inputMap, CLASSIC_INPUT, 1, WPAD_StickY(0, 0));
621 if (angles != GBA_KEY_NONE) {
622 keys |= 1 << angles;
623 }
624 }*/
625
626 return keys;
627}
628
629void _setRumble(struct GBARumble* rumble, int enable) {
630 UNUSED(rumble);
631 WPAD_Rumble(0, enable);
632 if (enable) {
633 PAD_ControlMotor(0, PAD_MOTOR_RUMBLE);
634 } else {
635 PAD_ControlMotor(0, PAD_MOTOR_STOP);
636 }
637}
638
639void _sampleRotation(struct GBARotationSource* source) {
640 UNUSED(source);
641 vec3w_t accel;
642 WPAD_Accel(0, &accel);
643 // These are swapped
644 tiltX = (accel.y - 0x1EA) << 22;
645 tiltY = (accel.x - 0x1EA) << 22;
646
647 // This doesn't seem to work at all with -TR remotes
648 struct expansion_t exp;
649 WPAD_Expansion(0, &exp);
650 if (exp.type != EXP_MOTION_PLUS) {
651 return;
652 }
653 gyroZ = exp.mp.rz - 0x1FA0;
654 gyroZ <<= 18;
655}
656
657int32_t _readTiltX(struct GBARotationSource* source) {
658 UNUSED(source);
659 return tiltX;
660}
661
662int32_t _readTiltY(struct GBARotationSource* source) {
663 UNUSED(source);
664 return tiltY;
665}
666
667int32_t _readGyroZ(struct GBARotationSource* source) {
668 UNUSED(source);
669 return gyroZ;
670}
671
672static s8 WPAD_StickX(u8 chan, u8 right) {
673 float mag = 0.0;
674 float ang = 0.0;
675 WPADData *data = WPAD_Data(chan);
676
677 switch (data->exp.type) {
678 case WPAD_EXP_NUNCHUK:
679 case WPAD_EXP_GUITARHERO3:
680 if (right == 0) {
681 mag = data->exp.nunchuk.js.mag;
682 ang = data->exp.nunchuk.js.ang;
683 }
684 break;
685 case WPAD_EXP_CLASSIC:
686 if (right == 0) {
687 mag = data->exp.classic.ljs.mag;
688 ang = data->exp.classic.ljs.ang;
689 } else {
690 mag = data->exp.classic.rjs.mag;
691 ang = data->exp.classic.rjs.ang;
692 }
693 break;
694 default:
695 break;
696 }
697
698 /* calculate X value (angle need to be converted into radian) */
699 if (mag > 1.0) {
700 mag = 1.0;
701 } else if (mag < -1.0) {
702 mag = -1.0;
703 }
704 double val = mag * sinf(M_PI * ang / 180.0f);
705
706 return (s8)(val * 128.0f);
707}
708
709static s8 WPAD_StickY(u8 chan, u8 right) {
710 float mag = 0.0;
711 float ang = 0.0;
712 WPADData *data = WPAD_Data(chan);
713
714 switch (data->exp.type) {
715 case WPAD_EXP_NUNCHUK:
716 case WPAD_EXP_GUITARHERO3:
717 if (right == 0) {
718 mag = data->exp.nunchuk.js.mag;
719 ang = data->exp.nunchuk.js.ang;
720 }
721 break;
722 case WPAD_EXP_CLASSIC:
723 if (right == 0) {
724 mag = data->exp.classic.ljs.mag;
725 ang = data->exp.classic.ljs.ang;
726 } else {
727 mag = data->exp.classic.rjs.mag;
728 ang = data->exp.classic.rjs.ang;
729 }
730 break;
731 default:
732 break;
733 }
734
735 /* calculate X value (angle need to be converted into radian) */
736 if (mag > 1.0) {
737 mag = 1.0;
738 } else if (mag < -1.0) {
739 mag = -1.0;
740 }
741 double val = mag * cosf(M_PI * ang / 180.0f);
742
743 return (s8)(val * 128.0f);
744}
745
746void _retraceCallback(u32 count) {
747 u32 level = 0;
748 _CPU_ISR_Disable(level);
749 retraceCount = count;
750 _CPU_ISR_Restore(level);
751}