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 }
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 .nConfigExtra = 3,
475 .setup = _setup,
476 .teardown = 0,
477 .gameLoaded = _gameLoaded,
478 .gameUnloaded = _gameUnloaded,
479 .prepareForFrame = 0,
480 .drawFrame = _drawFrame,
481 .paused = _gameUnloaded,
482 .unpaused = _unpaused,
483 .incrementScreenMode = _incrementScreenMode,
484 .setFrameLimiter = _setFrameLimiter,
485 .pollGameInput = _pollGameInput
486 };
487 mGUIInit(&runner, "wii");
488 reconfigureScreen(&runner);
489
490 // Make sure screen is properly initialized by drawing a blank frame
491 _drawStart();
492 _drawEnd();
493
494 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_A, GUI_INPUT_SELECT);
495 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_B, GUI_INPUT_BACK);
496 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_TRIGGER_Z, GUI_INPUT_CANCEL);
497 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_UP, GUI_INPUT_UP);
498 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_DOWN, GUI_INPUT_DOWN);
499 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_LEFT, GUI_INPUT_LEFT);
500 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_RIGHT, GUI_INPUT_RIGHT);
501
502 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_2, GUI_INPUT_SELECT);
503 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_1, GUI_INPUT_BACK);
504 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_HOME, GUI_INPUT_CANCEL);
505 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_RIGHT, GUI_INPUT_UP);
506 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_LEFT, GUI_INPUT_DOWN);
507 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_UP, GUI_INPUT_LEFT);
508 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_DOWN, GUI_INPUT_RIGHT);
509
510 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_A, GUI_INPUT_SELECT);
511 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_Y, GUI_INPUT_SELECT);
512 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_B, GUI_INPUT_BACK);
513 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_X, GUI_INPUT_BACK);
514 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_HOME, GUI_INPUT_CANCEL);
515 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_UP, GUI_INPUT_UP);
516 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_DOWN, GUI_INPUT_DOWN);
517 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_LEFT, GUI_INPUT_LEFT);
518 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_RIGHT, GUI_INPUT_RIGHT);
519
520 if (argc > 1) {
521 size_t i;
522 for (i = 0; runner.keySources[i].id; ++i) {
523 mInputMapLoad(&runner.params.keyMap, runner.keySources[i].id, mCoreConfigGetInput(&runner.config));
524 }
525 mGUIRun(&runner, argv[1]);
526 } else {
527 mGUIRunloop(&runner);
528 }
529 VIDEO_SetBlack(true);
530 VIDEO_Flush();
531 VIDEO_WaitVSync();
532 mGUIDeinit(&runner);
533
534 free(fifo);
535 free(texmem);
536 free(rescaleTexmem);
537
538 free(outputBuffer);
539 GUIFontDestroy(font);
540
541 free(framebuffer[0]);
542 free(framebuffer[1]);
543
544 return 0;
545}
546
547static void _audioDMA(void) {
548 if (!audioBufferSize) {
549 return;
550 }
551 DCFlushRange(audioBuffer[currentAudioBuffer], audioBufferSize * sizeof(struct GBAStereoSample));
552 AUDIO_InitDMA((u32) audioBuffer[currentAudioBuffer], audioBufferSize * sizeof(struct GBAStereoSample));
553 currentAudioBuffer = (currentAudioBuffer + 1) % 3;
554 audioBufferSize = 0;
555}
556
557static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* right) {
558 UNUSED(stream);
559 int available = blip_samples_avail(left);
560 if (available + audioBufferSize > SAMPLES) {
561 available = SAMPLES - audioBufferSize;
562 }
563 available &= ~((32 / sizeof(struct GBAStereoSample)) - 1); // Force align to 32 bytes
564 if (available > 0) {
565 // These appear to be reversed for AUDIO_InitDMA
566 blip_read_samples(left, &audioBuffer[currentAudioBuffer][audioBufferSize].right, available, true);
567 blip_read_samples(right, &audioBuffer[currentAudioBuffer][audioBufferSize].left, available, true);
568 audioBufferSize += available;
569 }
570 if (audioBufferSize == SAMPLES && !AUDIO_GetDMAEnableFlag()) {
571 _audioDMA();
572 AUDIO_StartDMA();
573 }
574}
575
576static void _drawStart(void) {
577 VIDEO_SetBlack(false);
578
579 u32 level = 0;
580 _CPU_ISR_Disable(level);
581 if (referenceRetraceCount > retraceCount) {
582 if (frameLimiter) {
583 VIDEO_WaitVSync();
584 }
585 referenceRetraceCount = retraceCount;
586 }
587 _CPU_ISR_Restore(level);
588
589 GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE);
590 GX_SetColorUpdate(GX_TRUE);
591
592 GX_SetViewport(0, 0, vmode->fbWidth, vmode->efbHeight, 0, 1);
593}
594
595static void _drawEnd(void) {
596 GX_CopyDisp(framebuffer[whichFb], GX_TRUE);
597 GX_DrawDone();
598 VIDEO_SetNextFramebuffer(framebuffer[whichFb]);
599 VIDEO_Flush();
600 whichFb = !whichFb;
601
602 u32 level = 0;
603 _CPU_ISR_Disable(level);
604 ++referenceRetraceCount;
605 _CPU_ISR_Restore(level);
606}
607
608static void _setFrameLimiter(struct mGUIRunner* runner, bool limit) {
609 UNUSED(runner);
610 frameLimiter = limit;
611}
612
613static uint32_t _pollInput(const struct mInputMap* map) {
614 PAD_ScanPads();
615 u16 padkeys = PAD_ButtonsHeld(0);
616
617 WPAD_ScanPads();
618 u32 wiiPad = WPAD_ButtonsHeld(0);
619 u32 ext = 0;
620 WPAD_Probe(0, &ext);
621
622 int keys = 0;
623 keys |= mInputMapKeyBits(map, GCN1_INPUT, padkeys, 0);
624 keys |= mInputMapKeyBits(map, GCN2_INPUT, padkeys, 0);
625 keys |= mInputMapKeyBits(map, WIIMOTE_INPUT, wiiPad, 0);
626 if (ext == WPAD_EXP_CLASSIC) {
627 keys |= mInputMapKeyBits(map, CLASSIC_INPUT, wiiPad, 0);
628 }
629 int x = PAD_StickX(0);
630 int y = PAD_StickY(0);
631 int w_x = WPAD_StickX(0, 0);
632 int w_y = WPAD_StickY(0, 0);
633 if (x < -ANALOG_DEADZONE || w_x < -ANALOG_DEADZONE) {
634 keys |= 1 << GUI_INPUT_LEFT;
635 }
636 if (x > ANALOG_DEADZONE || w_x > ANALOG_DEADZONE) {
637 keys |= 1 << GUI_INPUT_RIGHT;
638 }
639 if (y < -ANALOG_DEADZONE || w_y < -ANALOG_DEADZONE) {
640 keys |= 1 << GUI_INPUT_DOWN;
641 }
642 if (y > ANALOG_DEADZONE || w_y > ANALOG_DEADZONE) {
643 keys |= 1 << GUI_INPUT_UP;
644 }
645 return keys;
646}
647
648static enum GUICursorState _pollCursor(unsigned* x, unsigned* y) {
649 ir_t ir;
650 WPAD_IR(0, &ir);
651 if (!ir.smooth_valid) {
652 return GUI_CURSOR_NOT_PRESENT;
653 }
654 *x = ir.sx;
655 *y = ir.sy;
656 WPAD_ScanPads();
657 u32 wiiPad = WPAD_ButtonsHeld(0);
658 if (wiiPad & WPAD_BUTTON_A) {
659 return GUI_CURSOR_DOWN;
660 }
661 return GUI_CURSOR_UP;
662}
663
664void _reproj(int w, int h) {
665 Mtx44 proj;
666 int top = (vmode->efbHeight * hAdjust - h) / 2;
667 int left = (vmode->fbWidth * wAdjust - w) / 2;
668 guOrtho(proj, -top, top + h, -left, left + w, 0, 300);
669 GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
670}
671
672void _reproj2(int w, int h) {
673 Mtx44 proj;
674 int top = h * (1.0 - hStretch) / 2;
675 int left = w * (1.0 - wStretch) / 2;
676 guOrtho(proj, -top, h + top, -left, w + left, 0, 300);
677 GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
678}
679
680void _guiPrepare(void) {
681 _reproj2(vmode->fbWidth * guiScale * wAdjust, vmode->efbHeight * guiScale * hAdjust);
682}
683
684void _setup(struct mGUIRunner* runner) {
685 runner->core->setPeripheral(runner->core, mPERIPH_ROTATION, &rotation);
686 runner->core->setPeripheral(runner->core, mPERIPH_RUMBLE, &rumble);
687 runner->core->setAVStream(runner->core, &stream);
688
689 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_A, GBA_KEY_A);
690 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_B, GBA_KEY_B);
691 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_START, GBA_KEY_START);
692 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_X, GBA_KEY_SELECT);
693 _mapKey(&runner->core->inputMap, GCN2_INPUT, PAD_BUTTON_Y, GBA_KEY_SELECT);
694 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_UP, GBA_KEY_UP);
695 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_DOWN, GBA_KEY_DOWN);
696 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_LEFT, GBA_KEY_LEFT);
697 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_RIGHT, GBA_KEY_RIGHT);
698 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_TRIGGER_L, GBA_KEY_L);
699 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_TRIGGER_R, GBA_KEY_R);
700
701 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_2, GBA_KEY_A);
702 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_1, GBA_KEY_B);
703 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_PLUS, GBA_KEY_START);
704 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_MINUS, GBA_KEY_SELECT);
705 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_RIGHT, GBA_KEY_UP);
706 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_LEFT, GBA_KEY_DOWN);
707 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_UP, GBA_KEY_LEFT);
708 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_DOWN, GBA_KEY_RIGHT);
709 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_B, GBA_KEY_L);
710 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_A, GBA_KEY_R);
711
712 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_A, GBA_KEY_A);
713 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_B, GBA_KEY_B);
714 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_PLUS, GBA_KEY_START);
715 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_MINUS, GBA_KEY_SELECT);
716 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_UP, GBA_KEY_UP);
717 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_DOWN, GBA_KEY_DOWN);
718 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_LEFT, GBA_KEY_LEFT);
719 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_RIGHT, GBA_KEY_RIGHT);
720 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_FULL_L, GBA_KEY_L);
721 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_FULL_R, GBA_KEY_R);
722
723 struct mInputAxis desc = { GBA_KEY_RIGHT, GBA_KEY_LEFT, ANALOG_DEADZONE, -ANALOG_DEADZONE };
724 mInputBindAxis(&runner->core->inputMap, GCN1_INPUT, 0, &desc);
725 mInputBindAxis(&runner->core->inputMap, CLASSIC_INPUT, 0, &desc);
726 desc = (struct mInputAxis) { GBA_KEY_UP, GBA_KEY_DOWN, ANALOG_DEADZONE, -ANALOG_DEADZONE };
727 mInputBindAxis(&runner->core->inputMap, GCN1_INPUT, 1, &desc);
728 mInputBindAxis(&runner->core->inputMap, CLASSIC_INPUT, 1, &desc);
729
730 outputBuffer = memalign(32, TEX_W * TEX_H * BYTES_PER_PIXEL);
731 runner->core->setVideoBuffer(runner->core, outputBuffer, TEX_W);
732
733 runner->core->setAudioBufferSize(runner->core, SAMPLES);
734
735 double ratio = GBAAudioCalculateRatio(1, audioSampleRate, 1);
736 blip_set_rates(runner->core->getAudioChannel(runner->core, 0), runner->core->frequency(runner->core), 48000 * ratio);
737 blip_set_rates(runner->core->getAudioChannel(runner->core, 1), runner->core->frequency(runner->core), 48000 * ratio);
738
739 frameLimiter = true;
740}
741
742void _gameUnloaded(struct mGUIRunner* runner) {
743 UNUSED(runner);
744 AUDIO_StopDMA();
745 frameLimiter = true;
746 VIDEO_SetBlack(true);
747 VIDEO_Flush();
748 VIDEO_WaitVSync();
749}
750
751void _gameLoaded(struct mGUIRunner* runner) {
752 reconfigureScreen(runner);
753 if (runner->core->platform(runner->core) == PLATFORM_GBA && ((struct GBA*) runner->core->board)->memory.hw.devices & HW_GYRO) {
754 int i;
755 for (i = 0; i < 6; ++i) {
756 u32 result = WPAD_SetMotionPlus(0, 1);
757 if (result == WPAD_ERR_NONE) {
758 break;
759 }
760 sleep(1);
761 }
762 }
763 memset(texmem, 0, TEX_W * TEX_H * BYTES_PER_PIXEL);
764 _unpaused(runner);
765}
766
767void _unpaused(struct mGUIRunner* runner) {
768 u32 level = 0;
769 VIDEO_WaitVSync();
770 _CPU_ISR_Disable(level);
771 referenceRetraceCount = retraceCount;
772 _CPU_ISR_Restore(level);
773
774 unsigned mode;
775 if (mCoreConfigGetUIntValue(&runner->config, "videoMode", &mode) && mode < VM_MAX) {
776 if (mode != videoMode) {
777 reconfigureScreen(runner);
778 }
779 }
780 if (mCoreConfigGetUIntValue(&runner->config, "screenMode", &mode) && mode < SM_MAX) {
781 screenMode = mode;
782 }
783 if (mCoreConfigGetUIntValue(&runner->config, "filter", &mode) && mode < FM_MAX) {
784 filterMode = mode;
785 switch (mode) {
786 case FM_NEAREST:
787 case FM_LINEAR_2x:
788 default:
789 GX_InitTexObjFilterMode(&tex, GX_NEAR, GX_NEAR);
790 break;
791 case FM_LINEAR_1x:
792 GX_InitTexObjFilterMode(&tex, GX_LINEAR, GX_LINEAR);
793 break;
794 }
795 }
796 float stretch;
797 if (mCoreConfigGetFloatValue(&runner->config, "stretchWidth", &stretch)) {
798 wStretch = fminf(1.0f, fmaxf(0.5f, stretch));
799 }
800 if (mCoreConfigGetFloatValue(&runner->config, "stretchHeight", &stretch)) {
801 hStretch = fminf(1.0f, fmaxf(0.5f, stretch));
802 }
803}
804
805void _drawFrame(struct mGUIRunner* runner, bool faded) {
806 runner->core->desiredVideoDimensions(runner->core, &corew, &coreh);
807 uint32_t color = 0xFFFFFF3F;
808 if (!faded) {
809 color |= 0xC0;
810 }
811 size_t x, y;
812 uint64_t* texdest = (uint64_t*) texmem;
813 uint64_t* texsrc = (uint64_t*) outputBuffer;
814 for (y = 0; y < coreh; y += 4) {
815 for (x = 0; x < corew >> 2; ++x) {
816 texdest[0 + x * 4 + y * 64] = texsrc[0 + x + y * 64];
817 texdest[1 + x * 4 + y * 64] = texsrc[64 + x + y * 64];
818 texdest[2 + x * 4 + y * 64] = texsrc[128 + x + y * 64];
819 texdest[3 + x * 4 + y * 64] = texsrc[192 + x + y * 64];
820 }
821 }
822 DCFlushRange(texdest, TEX_W * TEX_H * BYTES_PER_PIXEL);
823
824 if (faded) {
825 GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_NOOP);
826 } else {
827 GX_SetBlendMode(GX_BM_NONE, GX_BL_ONE, GX_BL_ZERO, GX_LO_NOOP);
828 }
829 GX_InvalidateTexAll();
830 GX_LoadTexObj(&tex, GX_TEXMAP0);
831
832 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0);
833 s16 vertWidth = corew;
834 s16 vertHeight = coreh;
835
836 if (filterMode == FM_LINEAR_2x) {
837 Mtx44 proj;
838 guOrtho(proj, 0, vmode->efbHeight, 0, vmode->fbWidth, 0, 300);
839 GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
840
841 GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
842 GX_Position2s16(0, TEX_H * 2);
843 GX_Color1u32(0xFFFFFFFF);
844 GX_TexCoord2f32(0, 1);
845
846 GX_Position2s16(TEX_W * 2, TEX_H * 2);
847 GX_Color1u32(0xFFFFFFFF);
848 GX_TexCoord2f32(1, 1);
849
850 GX_Position2s16(TEX_W * 2, 0);
851 GX_Color1u32(0xFFFFFFFF);
852 GX_TexCoord2f32(1, 0);
853
854 GX_Position2s16(0, 0);
855 GX_Color1u32(0xFFFFFFFF);
856 GX_TexCoord2f32(0, 0);
857 GX_End();
858
859 GX_SetTexCopySrc(0, 0, TEX_W * 2, TEX_H * 2);
860 GX_SetTexCopyDst(TEX_W * 2, TEX_H * 2, GX_TF_RGB565, GX_FALSE);
861 GX_CopyTex(rescaleTexmem, GX_TRUE);
862 GX_LoadTexObj(&rescaleTex, GX_TEXMAP0);
863 }
864
865 int hfactor = vmode->fbWidth / (corew * wAdjust);
866 int vfactor = vmode->efbHeight / (coreh * hAdjust);
867 if (hfactor > vfactor) {
868 scaleFactor = vfactor;
869 } else {
870 scaleFactor = hfactor;
871 }
872
873 if (screenMode == SM_PA) {
874 vertWidth *= scaleFactor;
875 vertHeight *= scaleFactor;
876 }
877
878 if (screenMode == SM_PA) {
879 _reproj(corew * scaleFactor, coreh * scaleFactor);
880 } else {
881 _reproj2(corew, coreh);
882 }
883
884 GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
885 GX_Position2s16(0, vertHeight);
886 GX_Color1u32(color);
887 GX_TexCoord2f32(0, coreh / (float) TEX_H);
888
889 GX_Position2s16(vertWidth, vertHeight);
890 GX_Color1u32(color);
891 GX_TexCoord2f32(corew / (float) TEX_W, coreh / (float) TEX_H);
892
893 GX_Position2s16(vertWidth, 0);
894 GX_Color1u32(color);
895 GX_TexCoord2f32(corew / (float) TEX_W, 0);
896
897 GX_Position2s16(0, 0);
898 GX_Color1u32(color);
899 GX_TexCoord2f32(0, 0);
900 GX_End();
901}
902
903uint16_t _pollGameInput(struct mGUIRunner* runner) {
904 UNUSED(runner);
905 PAD_ScanPads();
906 u16 padkeys = PAD_ButtonsHeld(0);
907 WPAD_ScanPads();
908 u32 wiiPad = WPAD_ButtonsHeld(0);
909 u32 ext = 0;
910 WPAD_Probe(0, &ext);
911 uint16_t keys = mInputMapKeyBits(&runner->core->inputMap, GCN1_INPUT, padkeys, 0);
912 keys |= mInputMapKeyBits(&runner->core->inputMap, GCN2_INPUT, padkeys, 0);
913 keys |= mInputMapKeyBits(&runner->core->inputMap, WIIMOTE_INPUT, wiiPad, 0);
914
915 enum GBAKey angles = mInputMapAxis(&runner->core->inputMap, GCN1_INPUT, 0, PAD_StickX(0));
916 if (angles != GBA_KEY_NONE) {
917 keys |= 1 << angles;
918 }
919 angles = mInputMapAxis(&runner->core->inputMap, GCN1_INPUT, 1, PAD_StickY(0));
920 if (angles != GBA_KEY_NONE) {
921 keys |= 1 << angles;
922 }
923 if (ext == WPAD_EXP_CLASSIC) {
924 keys |= mInputMapKeyBits(&runner->core->inputMap, CLASSIC_INPUT, wiiPad, 0);
925 angles = mInputMapAxis(&runner->core->inputMap, CLASSIC_INPUT, 0, WPAD_StickX(0, 0));
926 if (angles != GBA_KEY_NONE) {
927 keys |= 1 << angles;
928 }
929 angles = mInputMapAxis(&runner->core->inputMap, CLASSIC_INPUT, 1, WPAD_StickY(0, 0));
930 if (angles != GBA_KEY_NONE) {
931 keys |= 1 << angles;
932 }
933 }
934
935 return keys;
936}
937
938void _incrementScreenMode(struct mGUIRunner* runner) {
939 UNUSED(runner);
940 int mode = screenMode | (filterMode << 1);
941 ++mode;
942 screenMode = mode % SM_MAX;
943 filterMode = (mode >> 1) % FM_MAX;
944 mCoreConfigSetUIntValue(&runner->config, "screenMode", screenMode);
945 mCoreConfigSetUIntValue(&runner->config, "filter", filterMode);
946 switch (filterMode) {
947 case FM_NEAREST:
948 case FM_LINEAR_2x:
949 default:
950 GX_InitTexObjFilterMode(&tex, GX_NEAR, GX_NEAR);
951 break;
952 case FM_LINEAR_1x:
953 GX_InitTexObjFilterMode(&tex, GX_LINEAR, GX_LINEAR);
954 break;
955 }
956}
957
958void _setRumble(struct mRumble* rumble, int enable) {
959 UNUSED(rumble);
960 WPAD_Rumble(0, enable);
961 if (enable) {
962 PAD_ControlMotor(0, PAD_MOTOR_RUMBLE);
963 } else {
964 PAD_ControlMotor(0, PAD_MOTOR_STOP);
965 }
966}
967
968void _sampleRotation(struct mRotationSource* source) {
969 UNUSED(source);
970 vec3w_t accel;
971 WPAD_Accel(0, &accel);
972 // These are swapped
973 tiltX = (0x1EA - accel.y) << 22;
974 tiltY = (0x1EA - accel.x) << 22;
975
976 // This doesn't seem to work at all with -TR remotes
977 struct expansion_t exp;
978 WPAD_Expansion(0, &exp);
979 if (exp.type != EXP_MOTION_PLUS) {
980 return;
981 }
982 gyroZ = exp.mp.rz - 0x1FA0;
983 gyroZ <<= 18;
984}
985
986int32_t _readTiltX(struct mRotationSource* source) {
987 UNUSED(source);
988 return tiltX;
989}
990
991int32_t _readTiltY(struct mRotationSource* source) {
992 UNUSED(source);
993 return tiltY;
994}
995
996int32_t _readGyroZ(struct mRotationSource* source) {
997 UNUSED(source);
998 return gyroZ;
999}
1000
1001static s8 WPAD_StickX(u8 chan, u8 right) {
1002 struct expansion_t exp;
1003 WPAD_Expansion(chan, &exp);
1004 struct joystick_t* js = NULL;
1005
1006 switch (exp.type) {
1007 case WPAD_EXP_NUNCHUK:
1008 case WPAD_EXP_GUITARHERO3:
1009 if (right == 0) {
1010 js = &exp.nunchuk.js;
1011 }
1012 break;
1013 case WPAD_EXP_CLASSIC:
1014 if (right == 0) {
1015 js = &exp.classic.ljs;
1016 } else {
1017 js = &exp.classic.rjs;
1018 }
1019 break;
1020 default:
1021 break;
1022 }
1023
1024 if (!js) {
1025 return 0;
1026 }
1027 int centered = (int) js->pos.x - (int) js->center.x;
1028 int range = js->max.x - js->min.x;
1029 return (centered * 0xFF) / range;
1030}
1031
1032static s8 WPAD_StickY(u8 chan, u8 right) {
1033 struct expansion_t exp;
1034 WPAD_Expansion(chan, &exp);
1035 struct joystick_t* js = NULL;
1036
1037 switch (exp.type) {
1038 case WPAD_EXP_NUNCHUK:
1039 case WPAD_EXP_GUITARHERO3:
1040 if (right == 0) {
1041 js = &exp.nunchuk.js;
1042 }
1043 break;
1044 case WPAD_EXP_CLASSIC:
1045 if (right == 0) {
1046 js = &exp.classic.ljs;
1047 } else {
1048 js = &exp.classic.rjs;
1049 }
1050 break;
1051 default:
1052 break;
1053 }
1054
1055 if (!js) {
1056 return 0;
1057 }
1058 int centered = (int) js->pos.y - (int) js->center.y;
1059 int range = js->max.y - js->min.y;
1060 return (centered * 0xFF) / range;
1061}
1062
1063void _retraceCallback(u32 count) {
1064 u32 level = 0;
1065 _CPU_ISR_Disable(level);
1066 retraceCount = count;
1067 _CPU_ISR_Restore(level);
1068}