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