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