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