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