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 "core/core.h"
18#include "feature/gui/gui-runner.h"
19#include "gba/audio.h"
20#include "gba/gba.h"
21#include "gba/input.h"
22#include "util/gui.h"
23#include "util/gui/file-select.h"
24#include "util/gui/font.h"
25#include "util/gui/menu.h"
26#include "util/vfs.h"
27
28#define GCN1_INPUT 0x47434E31
29#define GCN2_INPUT 0x47434E32
30#define WIIMOTE_INPUT 0x5749494D
31#define CLASSIC_INPUT 0x57494943
32
33static void _mapKey(struct mInputMap* map, uint32_t binding, int nativeKey, enum GBAKey key) {
34 mInputBindKey(map, binding, __builtin_ctz(nativeKey), key);
35}
36
37static enum ScreenMode {
38 SM_PA,
39 SM_SF,
40 SM_MAX
41} screenMode = SM_PA;
42
43static enum FilterMode {
44 FM_NEAREST,
45 FM_LINEAR,
46 FM_MAX
47} filterMode = FM_NEAREST;
48
49static enum VideoMode {
50 VM_AUTODETECT,
51 VM_480i,
52 VM_480p,
53 VM_240p,
54 // TODO: PAL support
55 VM_MAX
56} videoMode = VM_AUTODETECT;
57
58#define SAMPLES 1024
59#define GUI_SCALE 1.35f
60
61static void _retraceCallback(u32 count);
62
63static void _audioDMA(void);
64static void _setRumble(struct mRumble* rumble, int enable);
65static void _sampleRotation(struct mRotationSource* source);
66static int32_t _readTiltX(struct mRotationSource* source);
67static int32_t _readTiltY(struct mRotationSource* source);
68static int32_t _readGyroZ(struct mRotationSource* source);
69
70static void _drawStart(void);
71static void _drawEnd(void);
72static uint32_t _pollInput(const struct mInputMap*);
73static enum GUICursorState _pollCursor(unsigned* x, unsigned* y);
74static void _guiPrepare(void);
75static void _guiFinish(void);
76
77static void _setup(struct mGUIRunner* runner);
78static void _gameLoaded(struct mGUIRunner* runner);
79static void _gameUnloaded(struct mGUIRunner* runner);
80static void _unpaused(struct mGUIRunner* runner);
81static void _drawFrame(struct mGUIRunner* runner, bool faded);
82static uint16_t _pollGameInput(struct mGUIRunner* runner);
83static void _setFrameLimiter(struct mGUIRunner* runner, bool limit);
84static void _incrementScreenMode(struct mGUIRunner* runner);
85
86static s8 WPAD_StickX(u8 chan, u8 right);
87static s8 WPAD_StickY(u8 chan, u8 right);
88
89static void* outputBuffer;
90static struct mRumble rumble;
91static struct mRotationSource rotation;
92static GXRModeObj* vmode;
93static float wAdjust;
94static float hAdjust;
95static Mtx model, view, modelview;
96static uint16_t* texmem;
97static GXTexObj tex;
98static int32_t tiltX;
99static int32_t tiltY;
100static int32_t gyroZ;
101static uint32_t retraceCount;
102static uint32_t referenceRetraceCount;
103static bool frameLimiter = true;
104static int scaleFactor;
105static unsigned corew, coreh;
106
107static void* framebuffer[2] = { 0, 0 };
108static int whichFb = 0;
109
110static struct GBAStereoSample audioBuffer[3][SAMPLES] __attribute__((__aligned__(32)));
111static volatile size_t audioBufferSize = 0;
112static volatile int currentAudioBuffer = 0;
113
114static struct GUIFont* font;
115
116static void reconfigureScreen(struct mGUIRunner* runner) {
117 if (runner) {
118 unsigned mode;
119 if (mCoreConfigGetUIntValue(&runner->config, "videoMode", &mode) && mode < VM_MAX) {
120 videoMode = mode;
121 }
122 }
123 wAdjust = 1.f;
124 hAdjust = 1.f;
125
126 s32 signalMode = CONF_GetVideo();
127
128 switch (videoMode) {
129 case VM_AUTODETECT:
130 default:
131 vmode = VIDEO_GetPreferredMode(0);
132 break;
133 case VM_480i:
134 switch (signalMode) {
135 case CONF_VIDEO_NTSC:
136 vmode = &TVNtsc480IntDf;
137 break;
138 case CONF_VIDEO_MPAL:
139 vmode = &TVMpal480IntDf;
140 break;
141 case CONF_VIDEO_PAL:
142 vmode = &TVEurgb60Hz480IntDf;
143 break;
144 }
145 break;
146 case VM_480p:
147 switch (signalMode) {
148 case CONF_VIDEO_NTSC:
149 vmode = &TVNtsc480Prog;
150 break;
151 case CONF_VIDEO_MPAL:
152 vmode = &TVMpal480Prog;
153 break;
154 case CONF_VIDEO_PAL:
155 vmode = &TVEurgb60Hz480Prog;
156 break;
157 }
158 break;
159 case VM_240p:
160 switch (signalMode) {
161 case CONF_VIDEO_NTSC:
162 vmode = &TVNtsc240Ds;
163 break;
164 case CONF_VIDEO_MPAL:
165 vmode = &TVMpal240Ds;
166 break;
167 case CONF_VIDEO_PAL:
168 vmode = &TVEurgb60Hz240Ds;
169 break;
170 }
171 break;
172 wAdjust = 0.5f;
173 break;
174 }
175
176 free(framebuffer[0]);
177 free(framebuffer[1]);
178
179 framebuffer[0] = SYS_AllocateFramebuffer(vmode);
180 framebuffer[1] = SYS_AllocateFramebuffer(vmode);
181
182 VIDEO_SetBlack(true);
183 VIDEO_Configure(vmode);
184 VIDEO_SetNextFramebuffer(framebuffer[whichFb]);
185 VIDEO_Flush();
186 VIDEO_WaitVSync();
187 if (vmode->viTVMode & VI_NON_INTERLACE) {
188 VIDEO_WaitVSync();
189 }
190 VIDEO_SetBlack(false);
191 GX_SetViewport(0, 0, vmode->fbWidth, vmode->efbHeight, 0, 1);
192
193 f32 yscale = GX_GetYScaleFactor(vmode->efbHeight, vmode->xfbHeight);
194 u32 xfbHeight = GX_SetDispCopyYScale(yscale);
195 GX_SetScissor(0, 0, vmode->viWidth, vmode->viWidth);
196 GX_SetDispCopySrc(0, 0, vmode->fbWidth, vmode->efbHeight);
197 GX_SetDispCopyDst(vmode->fbWidth, xfbHeight);
198 GX_SetCopyFilter(vmode->aa, vmode->sample_pattern, GX_TRUE, vmode->vfilter);
199 GX_SetFieldMode(vmode->field_rendering, ((vmode->viHeight == 2 * vmode->xfbHeight) ? GX_ENABLE : GX_DISABLE));
200
201 if (runner) {
202 runner->params.width = vmode->fbWidth * GUI_SCALE;
203 runner->params.height = vmode->efbHeight * GUI_SCALE;
204 if (runner->core) {
205 runner->core->desiredVideoDimensions(runner->core, &corew, &coreh);
206 int hfactor = vmode->fbWidth / (corew * wAdjust);
207 int vfactor = vmode->efbHeight / (coreh * hAdjust);
208 if (hfactor > vfactor) {
209 scaleFactor = vfactor;
210 } else {
211 scaleFactor = hfactor;
212 }
213 }
214 }
215}
216
217int main(int argc, char* argv[]) {
218 VIDEO_Init();
219 PAD_Init();
220 WPAD_Init();
221 WPAD_SetDataFormat(0, WPAD_FMT_BTNS_ACC_IR);
222 AUDIO_Init(0);
223 AUDIO_SetDSPSampleRate(AI_SAMPLERATE_48KHZ);
224 AUDIO_RegisterDMACallback(_audioDMA);
225
226 memset(audioBuffer, 0, sizeof(audioBuffer));
227
228#if !defined(COLOR_16_BIT) && !defined(COLOR_5_6_5)
229#error This pixel format is unsupported. Please use -DCOLOR_16-BIT -DCOLOR_5_6_5
230#endif
231
232 GXColor bg = { 0, 0, 0, 0xFF };
233 void* fifo = memalign(32, 0x40000);
234 memset(fifo, 0, 0x40000);
235 GX_Init(fifo, 0x40000);
236 GX_SetCopyClear(bg, 0x00FFFFFF);
237
238 GX_SetCullMode(GX_CULL_NONE);
239 GX_SetDispCopyGamma(GX_GM_1_0);
240
241 GX_ClearVtxDesc();
242 GX_SetVtxDesc(GX_VA_POS, GX_DIRECT);
243 GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT);
244 GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT);
245
246 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 0);
247 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0);
248 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
249
250 GX_SetNumChans(1);
251 GX_SetNumTexGens(1);
252 GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
253 GX_SetTevOp(GX_TEVSTAGE0, GX_MODULATE);
254
255 GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);
256 GX_InvVtxCache();
257 GX_InvalidateTexAll();
258
259 guVector cam = { 0.0f, 0.0f, 0.0f };
260 guVector up = { 0.0f, 1.0f, 0.0f };
261 guVector look = { 0.0f, 0.0f, -1.0f };
262 guLookAt(view, &cam, &up, &look);
263
264 guMtxIdentity(model);
265 guMtxTransApply(model, model, 0.0f, 0.0f, -6.0f);
266 guMtxConcat(view, model, modelview);
267 GX_LoadPosMtxImm(modelview, GX_PNMTX0);
268
269 texmem = memalign(32, 256 * 256 * BYTES_PER_PIXEL);
270 memset(texmem, 0, 256 * 256 * BYTES_PER_PIXEL);
271 GX_InitTexObj(&tex, texmem, 256, 256, GX_TF_RGB565, GX_CLAMP, GX_CLAMP, GX_FALSE);
272
273 VIDEO_SetPostRetraceCallback(_retraceCallback);
274
275 font = GUIFontCreate();
276
277 fatInitDefault();
278
279 rumble.setRumble = _setRumble;
280
281 rotation.sample = _sampleRotation;
282 rotation.readTiltX = _readTiltX;
283 rotation.readTiltY = _readTiltY;
284 rotation.readGyroZ = _readGyroZ;
285
286 struct mGUIRunner runner = {
287 .params = {
288 720, 480,
289 font, "",
290 _drawStart, _drawEnd,
291 _pollInput, _pollCursor,
292 0,
293 _guiPrepare, _guiFinish,
294
295 GUI_PARAMS_TRAIL
296 },
297 .keySources = (struct GUIInputKeys[]) {
298 {
299 .name = "GameCube Input (1)",
300 .id = GCN1_INPUT,
301 .keyNames = (const char*[]) {
302 "D-Pad Left",
303 "D-Pad Right",
304 "D-Pad Down",
305 "D-Pad Up",
306 "Z",
307 "R",
308 "L",
309 0,
310 "A",
311 "B",
312 "X",
313 "Y",
314 "Start"
315 },
316 .nKeys = 13
317 },
318 {
319 .name = "GameCube Input (2)",
320 .id = GCN2_INPUT,
321 .keyNames = (const char*[]) {
322 "D-Pad Left",
323 "D-Pad Right",
324 "D-Pad Down",
325 "D-Pad Up",
326 "Z",
327 "R",
328 "L",
329 0,
330 "A",
331 "B",
332 "X",
333 "Y",
334 "Start"
335 },
336 .nKeys = 13
337 },
338 {
339 .name = "Wii Remote Input",
340 .id = WIIMOTE_INPUT,
341 .keyNames = (const char*[]) {
342 "2",
343 "1",
344 "B",
345 "A",
346 "-",
347 0,
348 0,
349 "\1\xE",
350 "Left",
351 "Right",
352 "Down",
353 "Up",
354 "+",
355 0,
356 0,
357 0,
358 "Z",
359 "C",
360 },
361 .nKeys = 18
362 },
363 {
364 .name = "Classic Controller Input",
365 .id = CLASSIC_INPUT,
366 .keyNames = (const char*[]) {
367 0,
368 0,
369 0,
370 0,
371 0,
372 0,
373 0,
374 0,
375 0,
376 0,
377 0,
378 0,
379 0,
380 0,
381 0,
382 0,
383 "Up",
384 "Left",
385 "ZR",
386 "X",
387 "A",
388 "Y",
389 "B",
390 "ZL",
391 0,
392 "R",
393 "+",
394 "\1\xE",
395 "-",
396 "L",
397 "Down",
398 "Right",
399 },
400 .nKeys = 32
401 },
402 { .id = 0 }
403 },
404 .configExtra = (struct GUIMenuItem[]) {
405 {
406 .title = "Video mode",
407 .data = "videoMode",
408 .submenu = 0,
409 .state = 0,
410 .validStates = (const char*[]) {
411 "Autodetect (recommended)",
412 "480i",
413 "480p",
414 "240p",
415 },
416 .nStates = 4
417 },
418 {
419 .title = "Screen mode",
420 .data = "screenMode",
421 .submenu = 0,
422 .state = 0,
423 .validStates = (const char*[]) {
424 "Pixel-Accurate",
425 "Stretched",
426 },
427 .nStates = 2
428 },
429 {
430 .title = "Filtering",
431 .data = "filter",
432 .submenu = 0,
433 .state = 0,
434 .validStates = (const char*[]) {
435 "Pixelated",
436 "Resampled",
437 },
438 .nStates = 2
439 }
440 },
441 .nConfigExtra = 3,
442 .setup = _setup,
443 .teardown = 0,
444 .gameLoaded = _gameLoaded,
445 .gameUnloaded = _gameUnloaded,
446 .prepareForFrame = 0,
447 .drawFrame = _drawFrame,
448 .paused = _gameUnloaded,
449 .unpaused = _unpaused,
450 .incrementScreenMode = _incrementScreenMode,
451 .setFrameLimiter = _setFrameLimiter,
452 .pollGameInput = _pollGameInput
453 };
454 mGUIInit(&runner, "wii");
455 reconfigureScreen(&runner);
456
457 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_A, GUI_INPUT_SELECT);
458 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_B, GUI_INPUT_BACK);
459 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_TRIGGER_Z, GUI_INPUT_CANCEL);
460 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_UP, GUI_INPUT_UP);
461 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_DOWN, GUI_INPUT_DOWN);
462 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_LEFT, GUI_INPUT_LEFT);
463 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_RIGHT, GUI_INPUT_RIGHT);
464
465 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_2, GUI_INPUT_SELECT);
466 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_1, GUI_INPUT_BACK);
467 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_HOME, GUI_INPUT_CANCEL);
468 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_RIGHT, GUI_INPUT_UP);
469 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_LEFT, GUI_INPUT_DOWN);
470 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_UP, GUI_INPUT_LEFT);
471 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_DOWN, GUI_INPUT_RIGHT);
472
473 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_A, GUI_INPUT_SELECT);
474 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_Y, GUI_INPUT_SELECT);
475 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_B, GUI_INPUT_BACK);
476 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_X, GUI_INPUT_BACK);
477 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_HOME, GUI_INPUT_CANCEL);
478 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_UP, GUI_INPUT_UP);
479 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_DOWN, GUI_INPUT_DOWN);
480 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_LEFT, GUI_INPUT_LEFT);
481 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_RIGHT, GUI_INPUT_RIGHT);
482
483 if (argc > 1) {
484 size_t i;
485 for (i = 0; runner.keySources[i].id; ++i) {
486 mInputMapLoad(&runner.params.keyMap, runner.keySources[i].id, mCoreConfigGetInput(&runner.config));
487 }
488 mGUIRun(&runner, argv[1]);
489 } else {
490 mGUIRunloop(&runner);
491 }
492 mGUIDeinit(&runner);
493
494 free(fifo);
495
496 free(outputBuffer);
497 GUIFontDestroy(font);
498
499 free(framebuffer[0]);
500 free(framebuffer[1]);
501
502 return 0;
503}
504
505static void _audioDMA(void) {
506 if (!audioBufferSize) {
507 return;
508 }
509 DCFlushRange(audioBuffer[currentAudioBuffer], audioBufferSize * sizeof(struct GBAStereoSample));
510 AUDIO_InitDMA((u32) audioBuffer[currentAudioBuffer], audioBufferSize * sizeof(struct GBAStereoSample));
511 currentAudioBuffer = (currentAudioBuffer + 1) % 3;
512 audioBufferSize = 0;
513}
514
515static void _drawStart(void) {
516 u32 level = 0;
517 _CPU_ISR_Disable(level);
518 if (referenceRetraceCount >= retraceCount) {
519 if (frameLimiter) {
520 VIDEO_WaitVSync();
521 }
522 referenceRetraceCount = retraceCount;
523 }
524 _CPU_ISR_Restore(level);
525
526 GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE);
527 GX_SetColorUpdate(GX_TRUE);
528
529 GX_SetViewport(0, 0, vmode->fbWidth, vmode->efbHeight, 0, 1);
530}
531
532static void _drawEnd(void) {
533 whichFb = !whichFb;
534
535 GX_CopyDisp(framebuffer[whichFb], GX_TRUE);
536 GX_DrawDone();
537 VIDEO_SetNextFramebuffer(framebuffer[whichFb]);
538 VIDEO_Flush();
539
540 u32 level = 0;
541 _CPU_ISR_Disable(level);
542 ++referenceRetraceCount;
543 _CPU_ISR_Restore(level);
544}
545
546static void _setFrameLimiter(struct mGUIRunner* runner, bool limit) {
547 UNUSED(runner);
548 frameLimiter = limit;
549}
550
551static uint32_t _pollInput(const struct mInputMap* map) {
552 PAD_ScanPads();
553 u16 padkeys = PAD_ButtonsHeld(0);
554
555 WPAD_ScanPads();
556 u32 wiiPad = WPAD_ButtonsHeld(0);
557 u32 ext = 0;
558 WPAD_Probe(0, &ext);
559
560 int keys = 0;
561 keys |= mInputMapKeyBits(map, GCN1_INPUT, padkeys, 0);
562 keys |= mInputMapKeyBits(map, GCN2_INPUT, padkeys, 0);
563 keys |= mInputMapKeyBits(map, WIIMOTE_INPUT, wiiPad, 0);
564 if (ext == WPAD_EXP_CLASSIC) {
565 keys |= mInputMapKeyBits(map, CLASSIC_INPUT, wiiPad, 0);
566 }
567 int x = PAD_StickX(0);
568 int y = PAD_StickY(0);
569 int w_x = WPAD_StickX(0, 0);
570 int w_y = WPAD_StickY(0, 0);
571 if (x < -0x20 || w_x < -0x20) {
572 keys |= 1 << GUI_INPUT_LEFT;
573 }
574 if (x > 0x20 || w_x > 0x20) {
575 keys |= 1 << GUI_INPUT_RIGHT;
576 }
577 if (y < -0x20 || w_y <- 0x20) {
578 keys |= 1 << GUI_INPUT_DOWN;
579 }
580 if (y > 0x20 || w_y > 0x20) {
581 keys |= 1 << GUI_INPUT_UP;
582 }
583 return keys;
584}
585
586static enum GUICursorState _pollCursor(unsigned* x, unsigned* y) {
587 ir_t ir;
588 WPAD_IR(0, &ir);
589 if (!ir.smooth_valid) {
590 return GUI_CURSOR_NOT_PRESENT;
591 }
592 *x = ir.sx;
593 *y = ir.sy;
594 WPAD_ScanPads();
595 u32 wiiPad = WPAD_ButtonsHeld(0);
596 if (wiiPad & WPAD_BUTTON_A) {
597 return GUI_CURSOR_DOWN;
598 }
599 return GUI_CURSOR_UP;
600}
601
602void _reproj(int w, int h) {
603 Mtx44 proj;
604 int top = (vmode->efbHeight * hAdjust - h) / 2;
605 int left = (vmode->fbWidth * wAdjust - w) / 2;
606 guOrtho(proj, -top, top + h, -left, left + w, 0, 300);
607 GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
608}
609
610void _reproj2(int w, int h) {
611 Mtx44 proj;
612 s16 top = 20;
613 guOrtho(proj, -top, top + h, 0, w, 0, 300);
614 GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
615}
616
617void _guiPrepare(void) {
618 _reproj2(vmode->fbWidth * GUI_SCALE, vmode->efbHeight * GUI_SCALE);
619}
620
621void _guiFinish(void) {
622 if (screenMode == SM_PA) {
623 _reproj(corew * scaleFactor, coreh * scaleFactor);
624 } else {
625 _reproj2(corew, coreh);
626 }
627}
628
629void _setup(struct mGUIRunner* runner) {
630 runner->core->setRotation(runner->core, &rotation);
631 runner->core->setRumble(runner->core, &rumble);
632
633 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_A, GBA_KEY_A);
634 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_B, GBA_KEY_B);
635 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_START, GBA_KEY_START);
636 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_X, GBA_KEY_SELECT);
637 _mapKey(&runner->core->inputMap, GCN2_INPUT, PAD_BUTTON_Y, GBA_KEY_SELECT);
638 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_UP, GBA_KEY_UP);
639 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_DOWN, GBA_KEY_DOWN);
640 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_LEFT, GBA_KEY_LEFT);
641 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_RIGHT, GBA_KEY_RIGHT);
642 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_TRIGGER_L, GBA_KEY_L);
643 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_TRIGGER_R, GBA_KEY_R);
644
645 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_2, GBA_KEY_A);
646 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_1, GBA_KEY_B);
647 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_PLUS, GBA_KEY_START);
648 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_MINUS, GBA_KEY_SELECT);
649 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_RIGHT, GBA_KEY_UP);
650 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_LEFT, GBA_KEY_DOWN);
651 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_UP, GBA_KEY_LEFT);
652 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_DOWN, GBA_KEY_RIGHT);
653 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_B, GBA_KEY_L);
654 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_A, GBA_KEY_R);
655
656 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_A, GBA_KEY_A);
657 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_B, GBA_KEY_B);
658 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_PLUS, GBA_KEY_START);
659 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_MINUS, GBA_KEY_SELECT);
660 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_UP, GBA_KEY_UP);
661 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_DOWN, GBA_KEY_DOWN);
662 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_LEFT, GBA_KEY_LEFT);
663 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_RIGHT, GBA_KEY_RIGHT);
664 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_FULL_L, GBA_KEY_L);
665 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_FULL_R, GBA_KEY_R);
666
667 struct mInputAxis desc = { GBA_KEY_RIGHT, GBA_KEY_LEFT, 0x20, -0x20 };
668 mInputBindAxis(&runner->core->inputMap, GCN1_INPUT, 0, &desc);
669 mInputBindAxis(&runner->core->inputMap, CLASSIC_INPUT, 0, &desc);
670 desc = (struct mInputAxis) { GBA_KEY_UP, GBA_KEY_DOWN, 0x20, -0x20 };
671 mInputBindAxis(&runner->core->inputMap, GCN1_INPUT, 1, &desc);
672 mInputBindAxis(&runner->core->inputMap, CLASSIC_INPUT, 1, &desc);
673
674 outputBuffer = memalign(32, 256 * 256 * BYTES_PER_PIXEL);
675 runner->core->setVideoBuffer(runner->core, outputBuffer, 256);
676
677 runner->core->setAudioBufferSize(runner->core, SAMPLES);
678
679 double ratio = GBAAudioCalculateRatio(1, 60 / 1.001, 1);
680 blip_set_rates(runner->core->getAudioChannel(runner->core, 0), runner->core->frequency(runner->core), 48000 * ratio);
681 blip_set_rates(runner->core->getAudioChannel(runner->core, 1), runner->core->frequency(runner->core), 48000 * ratio);
682
683 frameLimiter = true;
684}
685
686void _gameUnloaded(struct mGUIRunner* runner) {
687 UNUSED(runner);
688 AUDIO_StopDMA();
689 frameLimiter = true;
690}
691
692void _gameLoaded(struct mGUIRunner* runner) {
693 reconfigureScreen(runner);
694 if (runner->core->platform(runner->core) == PLATFORM_GBA && ((struct GBA*) runner->core->board)->memory.hw.devices & HW_GYRO) {
695 int i;
696 for (i = 0; i < 6; ++i) {
697 u32 result = WPAD_SetMotionPlus(0, 1);
698 if (result == WPAD_ERR_NONE) {
699 break;
700 }
701 sleep(1);
702 }
703 }
704 _unpaused(runner);
705}
706
707void _unpaused(struct mGUIRunner* runner) {
708 u32 level = 0;
709 _CPU_ISR_Disable(level);
710 referenceRetraceCount = retraceCount;
711 _CPU_ISR_Restore(level);
712
713 unsigned mode;
714 if (mCoreConfigGetUIntValue(&runner->config, "videoMode", &mode) && mode < VM_MAX) {
715 if (mode != videoMode) {
716 reconfigureScreen(runner);
717 }
718 }
719 if (mCoreConfigGetUIntValue(&runner->config, "screenMode", &mode) && mode < SM_MAX) {
720 screenMode = mode;
721 }
722 if (mCoreConfigGetUIntValue(&runner->config, "filter", &mode) && mode < FM_MAX) {
723 filterMode = mode;
724 switch (mode) {
725 case FM_NEAREST:
726 default:
727 GX_InitTexObjFilterMode(&tex, GX_NEAR, GX_NEAR);
728 break;
729 case FM_LINEAR:
730 GX_InitTexObjFilterMode(&tex, GX_LINEAR, GX_LINEAR);
731 break;
732 }
733 }
734 _guiFinish();
735}
736
737void _drawFrame(struct mGUIRunner* runner, bool faded) {
738 int available = blip_samples_avail(runner->core->getAudioChannel(runner->core, 0));
739 if (available + audioBufferSize > SAMPLES) {
740 available = SAMPLES - audioBufferSize;
741 }
742 available &= ~((32 / sizeof(struct GBAStereoSample)) - 1); // Force align to 32 bytes
743 if (available > 0) {
744 // These appear to be reversed for AUDIO_InitDMA
745 blip_read_samples(runner->core->getAudioChannel(runner->core, 0), &audioBuffer[currentAudioBuffer][audioBufferSize].right, available, true);
746 blip_read_samples(runner->core->getAudioChannel(runner->core, 1), &audioBuffer[currentAudioBuffer][audioBufferSize].left, available, true);
747 audioBufferSize += available;
748 }
749 if (audioBufferSize == SAMPLES && !AUDIO_GetDMAEnableFlag()) {
750 _audioDMA();
751 AUDIO_StartDMA();
752 }
753
754 uint32_t color = 0xFFFFFF3F;
755 if (!faded) {
756 color |= 0xC0;
757 }
758 size_t x, y;
759 uint64_t* texdest = (uint64_t*) texmem;
760 uint64_t* texsrc = (uint64_t*) outputBuffer;
761 for (y = 0; y < coreh; y += 4) {
762 for (x = 0; x < corew >> 2; ++x) {
763 texdest[0 + x * 4 + y * 64] = texsrc[0 + x + y * 64];
764 texdest[1 + x * 4 + y * 64] = texsrc[64 + x + y * 64];
765 texdest[2 + x * 4 + y * 64] = texsrc[128 + x + y * 64];
766 texdest[3 + x * 4 + y * 64] = texsrc[192 + x + y * 64];
767 }
768 }
769 DCFlushRange(texdest, 256 * 256 * BYTES_PER_PIXEL);
770
771 if (faded) {
772 GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_NOOP);
773 } else {
774 GX_SetBlendMode(GX_BM_NONE, GX_BL_ONE, GX_BL_ZERO, GX_LO_NOOP);
775 }
776 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0);
777 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
778 GX_InvalidateTexAll();
779 GX_LoadTexObj(&tex, GX_TEXMAP0);
780
781 s16 vertSize = 256;
782 if (screenMode == SM_PA) {
783 vertSize *= scaleFactor;
784 }
785
786 GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
787 GX_Position2s16(0, vertSize);
788 GX_Color1u32(color);
789 GX_TexCoord2s16(0, 1);
790
791 GX_Position2s16(vertSize, vertSize);
792 GX_Color1u32(color);
793 GX_TexCoord2s16(1, 1);
794
795 GX_Position2s16(vertSize, 0);
796 GX_Color1u32(color);
797 GX_TexCoord2s16(1, 0);
798
799 GX_Position2s16(0, 0);
800 GX_Color1u32(color);
801 GX_TexCoord2s16(0, 0);
802 GX_End();
803}
804
805uint16_t _pollGameInput(struct mGUIRunner* runner) {
806 UNUSED(runner);
807 PAD_ScanPads();
808 u16 padkeys = PAD_ButtonsHeld(0);
809 WPAD_ScanPads();
810 u32 wiiPad = WPAD_ButtonsHeld(0);
811 u32 ext = 0;
812 WPAD_Probe(0, &ext);
813 uint16_t keys = mInputMapKeyBits(&runner->core->inputMap, GCN1_INPUT, padkeys, 0);
814 keys |= mInputMapKeyBits(&runner->core->inputMap, GCN2_INPUT, padkeys, 0);
815 keys |= mInputMapKeyBits(&runner->core->inputMap, WIIMOTE_INPUT, wiiPad, 0);
816
817 enum GBAKey angles = mInputMapAxis(&runner->core->inputMap, GCN1_INPUT, 0, PAD_StickX(0));
818 if (angles != GBA_KEY_NONE) {
819 keys |= 1 << angles;
820 }
821 angles = mInputMapAxis(&runner->core->inputMap, GCN1_INPUT, 1, PAD_StickY(0));
822 if (angles != GBA_KEY_NONE) {
823 keys |= 1 << angles;
824 }
825 if (ext == WPAD_EXP_CLASSIC) {
826 keys |= mInputMapKeyBits(&runner->core->inputMap, CLASSIC_INPUT, wiiPad, 0);
827 angles = mInputMapAxis(&runner->core->inputMap, CLASSIC_INPUT, 0, WPAD_StickX(0, 0));
828 if (angles != GBA_KEY_NONE) {
829 keys |= 1 << angles;
830 }
831 angles = mInputMapAxis(&runner->core->inputMap, CLASSIC_INPUT, 1, WPAD_StickY(0, 0));
832 if (angles != GBA_KEY_NONE) {
833 keys |= 1 << angles;
834 }
835 }
836
837 return keys;
838}
839
840void _incrementScreenMode(struct mGUIRunner* runner) {
841 UNUSED(runner);
842 int mode = screenMode | (filterMode << 1);
843 ++mode;
844 screenMode = mode % SM_MAX;
845 filterMode = (mode >> 1) % FM_MAX;
846 mCoreConfigSetUIntValue(&runner->config, "screenMode", screenMode);
847 mCoreConfigSetUIntValue(&runner->config, "filter", filterMode);
848 if (screenMode == SM_PA) {
849 _reproj(corew * scaleFactor, coreh * scaleFactor);
850 } else {
851 _reproj2(corew, coreh);
852 }
853 switch (filterMode) {
854 case FM_NEAREST:
855 default:
856 GX_InitTexObjFilterMode(&tex, GX_NEAR, GX_NEAR);
857 break;
858 case FM_LINEAR:
859 GX_InitTexObjFilterMode(&tex, GX_LINEAR, GX_LINEAR);
860 break;
861 }
862}
863
864void _setRumble(struct mRumble* rumble, int enable) {
865 UNUSED(rumble);
866 WPAD_Rumble(0, enable);
867 if (enable) {
868 PAD_ControlMotor(0, PAD_MOTOR_RUMBLE);
869 } else {
870 PAD_ControlMotor(0, PAD_MOTOR_STOP);
871 }
872}
873
874void _sampleRotation(struct mRotationSource* source) {
875 UNUSED(source);
876 vec3w_t accel;
877 WPAD_Accel(0, &accel);
878 // These are swapped
879 tiltX = (0x1EA - accel.y) << 22;
880 tiltY = (0x1EA - accel.x) << 22;
881
882 // This doesn't seem to work at all with -TR remotes
883 struct expansion_t exp;
884 WPAD_Expansion(0, &exp);
885 if (exp.type != EXP_MOTION_PLUS) {
886 return;
887 }
888 gyroZ = exp.mp.rz - 0x1FA0;
889 gyroZ <<= 18;
890}
891
892int32_t _readTiltX(struct mRotationSource* source) {
893 UNUSED(source);
894 return tiltX;
895}
896
897int32_t _readTiltY(struct mRotationSource* source) {
898 UNUSED(source);
899 return tiltY;
900}
901
902int32_t _readGyroZ(struct mRotationSource* source) {
903 UNUSED(source);
904 return gyroZ;
905}
906
907static s8 WPAD_StickX(u8 chan, u8 right) {
908 float mag = 0.0;
909 float ang = 0.0;
910 WPADData *data = WPAD_Data(chan);
911
912 switch (data->exp.type) {
913 case WPAD_EXP_NUNCHUK:
914 case WPAD_EXP_GUITARHERO3:
915 if (right == 0) {
916 mag = data->exp.nunchuk.js.mag;
917 ang = data->exp.nunchuk.js.ang;
918 }
919 break;
920 case WPAD_EXP_CLASSIC:
921 if (right == 0) {
922 mag = data->exp.classic.ljs.mag;
923 ang = data->exp.classic.ljs.ang;
924 } else {
925 mag = data->exp.classic.rjs.mag;
926 ang = data->exp.classic.rjs.ang;
927 }
928 break;
929 default:
930 break;
931 }
932
933 /* calculate X value (angle need to be converted into radian) */
934 if (mag > 1.0) {
935 mag = 1.0;
936 } else if (mag < -1.0) {
937 mag = -1.0;
938 }
939 double val = mag * sinf(M_PI * ang / 180.0f);
940
941 return (s8)(val * 128.0f);
942}
943
944static s8 WPAD_StickY(u8 chan, u8 right) {
945 float mag = 0.0;
946 float ang = 0.0;
947 WPADData *data = WPAD_Data(chan);
948
949 switch (data->exp.type) {
950 case WPAD_EXP_NUNCHUK:
951 case WPAD_EXP_GUITARHERO3:
952 if (right == 0) {
953 mag = data->exp.nunchuk.js.mag;
954 ang = data->exp.nunchuk.js.ang;
955 }
956 break;
957 case WPAD_EXP_CLASSIC:
958 if (right == 0) {
959 mag = data->exp.classic.ljs.mag;
960 ang = data->exp.classic.ljs.ang;
961 } else {
962 mag = data->exp.classic.rjs.mag;
963 ang = data->exp.classic.rjs.ang;
964 }
965 break;
966 default:
967 break;
968 }
969
970 /* calculate X value (angle need to be converted into radian) */
971 if (mag > 1.0) {
972 mag = 1.0;
973 } else if (mag < -1.0) {
974 mag = -1.0;
975 }
976 double val = mag * cosf(M_PI * ang / 180.0f);
977
978 return (s8)(val * 128.0f);
979}
980
981void _retraceCallback(u32 count) {
982 u32 level = 0;
983 _CPU_ISR_Disable(level);
984 retraceCount = count;
985 _CPU_ISR_Restore(level);
986}