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