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 _CPU_ISR_Disable(level);
737 referenceRetraceCount = retraceCount;
738 _CPU_ISR_Restore(level);
739
740 unsigned mode;
741 if (mCoreConfigGetUIntValue(&runner->config, "videoMode", &mode) && mode < VM_MAX) {
742 if (mode != videoMode) {
743 reconfigureScreen(runner);
744 }
745 }
746 if (mCoreConfigGetUIntValue(&runner->config, "screenMode", &mode) && mode < SM_MAX) {
747 screenMode = mode;
748 }
749 if (mCoreConfigGetUIntValue(&runner->config, "filter", &mode) && mode < FM_MAX) {
750 filterMode = mode;
751 switch (mode) {
752 case FM_NEAREST:
753 case FM_LINEAR_2x:
754 default:
755 GX_InitTexObjFilterMode(&tex, GX_NEAR, GX_NEAR);
756 break;
757 case FM_LINEAR_1x:
758 GX_InitTexObjFilterMode(&tex, GX_LINEAR, GX_LINEAR);
759 break;
760 }
761 }
762 float stretch;
763 if (mCoreConfigGetFloatValue(&runner->config, "stretchWidth", &stretch)) {
764 wStretch = fminf(1.0f, fmaxf(0.5f, stretch));
765 }
766 if (mCoreConfigGetFloatValue(&runner->config, "stretchHeight", &stretch)) {
767 hStretch = fminf(1.0f, fmaxf(0.5f, stretch));
768 }
769}
770
771void _drawFrame(struct mGUIRunner* runner, bool faded) {
772 int available = blip_samples_avail(runner->core->getAudioChannel(runner->core, 0));
773 if (available + audioBufferSize > SAMPLES) {
774 available = SAMPLES - audioBufferSize;
775 }
776 available &= ~((32 / sizeof(struct GBAStereoSample)) - 1); // Force align to 32 bytes
777 if (available > 0) {
778 // These appear to be reversed for AUDIO_InitDMA
779 blip_read_samples(runner->core->getAudioChannel(runner->core, 0), &audioBuffer[currentAudioBuffer][audioBufferSize].right, available, true);
780 blip_read_samples(runner->core->getAudioChannel(runner->core, 1), &audioBuffer[currentAudioBuffer][audioBufferSize].left, available, true);
781 audioBufferSize += available;
782 }
783 if (audioBufferSize == SAMPLES && !AUDIO_GetDMAEnableFlag()) {
784 _audioDMA();
785 AUDIO_StartDMA();
786 }
787
788 uint32_t color = 0xFFFFFF3F;
789 if (!faded) {
790 color |= 0xC0;
791 }
792 size_t x, y;
793 uint64_t* texdest = (uint64_t*) texmem;
794 uint64_t* texsrc = (uint64_t*) outputBuffer;
795 for (y = 0; y < coreh; y += 4) {
796 for (x = 0; x < corew >> 2; ++x) {
797 texdest[0 + x * 4 + y * 64] = texsrc[0 + x + y * 64];
798 texdest[1 + x * 4 + y * 64] = texsrc[64 + x + y * 64];
799 texdest[2 + x * 4 + y * 64] = texsrc[128 + x + y * 64];
800 texdest[3 + x * 4 + y * 64] = texsrc[192 + x + y * 64];
801 }
802 }
803 DCFlushRange(texdest, TEX_W * TEX_H * BYTES_PER_PIXEL);
804
805 if (faded) {
806 GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_NOOP);
807 } else {
808 GX_SetBlendMode(GX_BM_NONE, GX_BL_ONE, GX_BL_ZERO, GX_LO_NOOP);
809 }
810 GX_InvalidateTexAll();
811 GX_LoadTexObj(&tex, GX_TEXMAP0);
812
813 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0);
814 s16 vertWidth = TEX_W;
815 s16 vertHeight = TEX_H;
816
817 if (filterMode == FM_LINEAR_2x) {
818 Mtx44 proj;
819 guOrtho(proj, 0, vmode->efbHeight, 0, vmode->fbWidth, 0, 300);
820 GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
821
822 GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
823 GX_Position2s16(0, TEX_H * 2);
824 GX_Color1u32(0xFFFFFFFF);
825 GX_TexCoord2s16(0, 1);
826
827 GX_Position2s16(TEX_W * 2, TEX_H * 2);
828 GX_Color1u32(0xFFFFFFFF);
829 GX_TexCoord2s16(1, 1);
830
831 GX_Position2s16(TEX_W * 2, 0);
832 GX_Color1u32(0xFFFFFFFF);
833 GX_TexCoord2s16(1, 0);
834
835 GX_Position2s16(0, 0);
836 GX_Color1u32(0xFFFFFFFF);
837 GX_TexCoord2s16(0, 0);
838 GX_End();
839
840 GX_SetTexCopySrc(0, 0, TEX_W * 2, TEX_H * 2);
841 GX_SetTexCopyDst(TEX_W * 2, TEX_H * 2, GX_TF_RGB565, GX_FALSE);
842 GX_CopyTex(rescaleTexmem, GX_TRUE);
843 GX_LoadTexObj(&rescaleTex, GX_TEXMAP0);
844 }
845
846 if (screenMode == SM_PA) {
847 vertWidth *= scaleFactor;
848 vertHeight *= scaleFactor;
849 }
850
851 if (screenMode == SM_PA) {
852 _reproj(corew * scaleFactor, coreh * scaleFactor);
853 } else {
854 _reproj2(corew, coreh);
855 }
856
857 GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
858 GX_Position2s16(0, vertHeight);
859 GX_Color1u32(color);
860 GX_TexCoord2s16(0, 1);
861
862 GX_Position2s16(vertWidth, vertHeight);
863 GX_Color1u32(color);
864 GX_TexCoord2s16(1, 1);
865
866 GX_Position2s16(vertWidth, 0);
867 GX_Color1u32(color);
868 GX_TexCoord2s16(1, 0);
869
870 GX_Position2s16(0, 0);
871 GX_Color1u32(color);
872 GX_TexCoord2s16(0, 0);
873 GX_End();
874}
875
876uint16_t _pollGameInput(struct mGUIRunner* runner) {
877 UNUSED(runner);
878 PAD_ScanPads();
879 u16 padkeys = PAD_ButtonsHeld(0);
880 WPAD_ScanPads();
881 u32 wiiPad = WPAD_ButtonsHeld(0);
882 u32 ext = 0;
883 WPAD_Probe(0, &ext);
884 uint16_t keys = mInputMapKeyBits(&runner->core->inputMap, GCN1_INPUT, padkeys, 0);
885 keys |= mInputMapKeyBits(&runner->core->inputMap, GCN2_INPUT, padkeys, 0);
886 keys |= mInputMapKeyBits(&runner->core->inputMap, WIIMOTE_INPUT, wiiPad, 0);
887
888 enum GBAKey angles = mInputMapAxis(&runner->core->inputMap, GCN1_INPUT, 0, PAD_StickX(0));
889 if (angles != GBA_KEY_NONE) {
890 keys |= 1 << angles;
891 }
892 angles = mInputMapAxis(&runner->core->inputMap, GCN1_INPUT, 1, PAD_StickY(0));
893 if (angles != GBA_KEY_NONE) {
894 keys |= 1 << angles;
895 }
896 if (ext == WPAD_EXP_CLASSIC) {
897 keys |= mInputMapKeyBits(&runner->core->inputMap, CLASSIC_INPUT, wiiPad, 0);
898 angles = mInputMapAxis(&runner->core->inputMap, CLASSIC_INPUT, 0, WPAD_StickX(0, 0));
899 if (angles != GBA_KEY_NONE) {
900 keys |= 1 << angles;
901 }
902 angles = mInputMapAxis(&runner->core->inputMap, CLASSIC_INPUT, 1, WPAD_StickY(0, 0));
903 if (angles != GBA_KEY_NONE) {
904 keys |= 1 << angles;
905 }
906 }
907
908 return keys;
909}
910
911void _incrementScreenMode(struct mGUIRunner* runner) {
912 UNUSED(runner);
913 int mode = screenMode | (filterMode << 1);
914 ++mode;
915 screenMode = mode % SM_MAX;
916 filterMode = (mode >> 1) % FM_MAX;
917 mCoreConfigSetUIntValue(&runner->config, "screenMode", screenMode);
918 mCoreConfigSetUIntValue(&runner->config, "filter", filterMode);
919 switch (filterMode) {
920 case FM_NEAREST:
921 case FM_LINEAR_2x:
922 default:
923 GX_InitTexObjFilterMode(&tex, GX_NEAR, GX_NEAR);
924 break;
925 case FM_LINEAR_1x:
926 GX_InitTexObjFilterMode(&tex, GX_LINEAR, GX_LINEAR);
927 break;
928 }
929}
930
931void _setRumble(struct mRumble* rumble, int enable) {
932 UNUSED(rumble);
933 WPAD_Rumble(0, enable);
934 if (enable) {
935 PAD_ControlMotor(0, PAD_MOTOR_RUMBLE);
936 } else {
937 PAD_ControlMotor(0, PAD_MOTOR_STOP);
938 }
939}
940
941void _sampleRotation(struct mRotationSource* source) {
942 UNUSED(source);
943 vec3w_t accel;
944 WPAD_Accel(0, &accel);
945 // These are swapped
946 tiltX = (0x1EA - accel.y) << 22;
947 tiltY = (0x1EA - accel.x) << 22;
948
949 // This doesn't seem to work at all with -TR remotes
950 struct expansion_t exp;
951 WPAD_Expansion(0, &exp);
952 if (exp.type != EXP_MOTION_PLUS) {
953 return;
954 }
955 gyroZ = exp.mp.rz - 0x1FA0;
956 gyroZ <<= 18;
957}
958
959int32_t _readTiltX(struct mRotationSource* source) {
960 UNUSED(source);
961 return tiltX;
962}
963
964int32_t _readTiltY(struct mRotationSource* source) {
965 UNUSED(source);
966 return tiltY;
967}
968
969int32_t _readGyroZ(struct mRotationSource* source) {
970 UNUSED(source);
971 return gyroZ;
972}
973
974static s8 WPAD_StickX(u8 chan, u8 right) {
975 float mag = 0.0;
976 float ang = 0.0;
977 WPADData *data = WPAD_Data(chan);
978
979 switch (data->exp.type) {
980 case WPAD_EXP_NUNCHUK:
981 case WPAD_EXP_GUITARHERO3:
982 if (right == 0) {
983 mag = data->exp.nunchuk.js.mag;
984 ang = data->exp.nunchuk.js.ang;
985 }
986 break;
987 case WPAD_EXP_CLASSIC:
988 if (right == 0) {
989 mag = data->exp.classic.ljs.mag;
990 ang = data->exp.classic.ljs.ang;
991 } else {
992 mag = data->exp.classic.rjs.mag;
993 ang = data->exp.classic.rjs.ang;
994 }
995 break;
996 default:
997 break;
998 }
999
1000 /* calculate X value (angle need to be converted into radian) */
1001 if (mag > 1.0) {
1002 mag = 1.0;
1003 } else if (mag < -1.0) {
1004 mag = -1.0;
1005 }
1006 double val = mag * sinf(M_PI * ang / 180.0f);
1007
1008 return (s8)(val * 128.0f);
1009}
1010
1011static s8 WPAD_StickY(u8 chan, u8 right) {
1012 float mag = 0.0;
1013 float ang = 0.0;
1014 WPADData *data = WPAD_Data(chan);
1015
1016 switch (data->exp.type) {
1017 case WPAD_EXP_NUNCHUK:
1018 case WPAD_EXP_GUITARHERO3:
1019 if (right == 0) {
1020 mag = data->exp.nunchuk.js.mag;
1021 ang = data->exp.nunchuk.js.ang;
1022 }
1023 break;
1024 case WPAD_EXP_CLASSIC:
1025 if (right == 0) {
1026 mag = data->exp.classic.ljs.mag;
1027 ang = data->exp.classic.ljs.ang;
1028 } else {
1029 mag = data->exp.classic.rjs.mag;
1030 ang = data->exp.classic.rjs.ang;
1031 }
1032 break;
1033 default:
1034 break;
1035 }
1036
1037 /* calculate X value (angle need to be converted into radian) */
1038 if (mag > 1.0) {
1039 mag = 1.0;
1040 } else if (mag < -1.0) {
1041 mag = -1.0;
1042 }
1043 double val = mag * cosf(M_PI * ang / 180.0f);
1044
1045 return (s8)(val * 128.0f);
1046}
1047
1048void _retraceCallback(u32 count) {
1049 u32 level = 0;
1050 _CPU_ISR_Disable(level);
1051 retraceCount = count;
1052 _CPU_ISR_Restore(level);
1053}