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