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