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