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