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 VIDEO_WaitVSync();
725 _CPU_ISR_Disable(level);
726 referenceRetraceCount = retraceCount;
727 _CPU_ISR_Restore(level);
728
729 unsigned mode;
730 if (mCoreConfigGetUIntValue(&runner->config, "videoMode", &mode) && mode < VM_MAX) {
731 if (mode != videoMode) {
732 reconfigureScreen(runner);
733 }
734 }
735 if (mCoreConfigGetUIntValue(&runner->config, "screenMode", &mode) && mode < SM_MAX) {
736 screenMode = mode;
737 }
738 if (mCoreConfigGetUIntValue(&runner->config, "filter", &mode) && mode < FM_MAX) {
739 filterMode = mode;
740 switch (mode) {
741 case FM_NEAREST:
742 case FM_LINEAR_2x:
743 default:
744 GX_InitTexObjFilterMode(&tex, GX_NEAR, GX_NEAR);
745 break;
746 case FM_LINEAR_1x:
747 GX_InitTexObjFilterMode(&tex, GX_LINEAR, GX_LINEAR);
748 break;
749 }
750 }
751 float stretch;
752 if (mCoreConfigGetFloatValue(&runner->config, "stretchWidth", &stretch)) {
753 wStretch = fminf(1.0f, fmaxf(0.5f, stretch));
754 }
755 if (mCoreConfigGetFloatValue(&runner->config, "stretchHeight", &stretch)) {
756 hStretch = fminf(1.0f, fmaxf(0.5f, stretch));
757 }
758}
759
760void _drawFrame(struct mGUIRunner* runner, bool faded) {
761 int available = blip_samples_avail(runner->core->getAudioChannel(runner->core, 0));
762 if (available + audioBufferSize > SAMPLES) {
763 available = SAMPLES - audioBufferSize;
764 }
765 available &= ~((32 / sizeof(struct GBAStereoSample)) - 1); // Force align to 32 bytes
766 if (available > 0) {
767 // These appear to be reversed for AUDIO_InitDMA
768 blip_read_samples(runner->core->getAudioChannel(runner->core, 0), &audioBuffer[currentAudioBuffer][audioBufferSize].right, available, true);
769 blip_read_samples(runner->core->getAudioChannel(runner->core, 1), &audioBuffer[currentAudioBuffer][audioBufferSize].left, available, true);
770 audioBufferSize += available;
771 }
772 if (audioBufferSize == SAMPLES && !AUDIO_GetDMAEnableFlag()) {
773 _audioDMA();
774 AUDIO_StartDMA();
775 }
776
777 uint32_t color = 0xFFFFFF3F;
778 if (!faded) {
779 color |= 0xC0;
780 }
781 size_t x, y;
782 uint64_t* texdest = (uint64_t*) texmem;
783 uint64_t* texsrc = (uint64_t*) outputBuffer;
784 for (y = 0; y < coreh; y += 4) {
785 for (x = 0; x < corew >> 2; ++x) {
786 texdest[0 + x * 4 + y * 64] = texsrc[0 + x + y * 64];
787 texdest[1 + x * 4 + y * 64] = texsrc[64 + x + y * 64];
788 texdest[2 + x * 4 + y * 64] = texsrc[128 + x + y * 64];
789 texdest[3 + x * 4 + y * 64] = texsrc[192 + x + y * 64];
790 }
791 }
792 DCFlushRange(texdest, TEX_W * TEX_H * BYTES_PER_PIXEL);
793
794 if (faded) {
795 GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_NOOP);
796 } else {
797 GX_SetBlendMode(GX_BM_NONE, GX_BL_ONE, GX_BL_ZERO, GX_LO_NOOP);
798 }
799 GX_InvalidateTexAll();
800 GX_LoadTexObj(&tex, GX_TEXMAP0);
801
802 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0);
803 s16 vertWidth = TEX_W;
804 s16 vertHeight = TEX_H;
805
806 if (filterMode == FM_LINEAR_2x) {
807 Mtx44 proj;
808 guOrtho(proj, 0, vmode->efbHeight, 0, vmode->fbWidth, 0, 300);
809 GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
810
811 GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
812 GX_Position2s16(0, TEX_H * 2);
813 GX_Color1u32(0xFFFFFFFF);
814 GX_TexCoord2s16(0, 1);
815
816 GX_Position2s16(TEX_W * 2, TEX_H * 2);
817 GX_Color1u32(0xFFFFFFFF);
818 GX_TexCoord2s16(1, 1);
819
820 GX_Position2s16(TEX_W * 2, 0);
821 GX_Color1u32(0xFFFFFFFF);
822 GX_TexCoord2s16(1, 0);
823
824 GX_Position2s16(0, 0);
825 GX_Color1u32(0xFFFFFFFF);
826 GX_TexCoord2s16(0, 0);
827 GX_End();
828
829 GX_SetTexCopySrc(0, 0, TEX_W * 2, TEX_H * 2);
830 GX_SetTexCopyDst(TEX_W * 2, TEX_H * 2, GX_TF_RGB565, GX_FALSE);
831 GX_CopyTex(rescaleTexmem, GX_TRUE);
832 GX_LoadTexObj(&rescaleTex, GX_TEXMAP0);
833 }
834
835 if (screenMode == SM_PA) {
836 vertWidth *= scaleFactor;
837 vertHeight *= scaleFactor;
838 }
839
840 if (screenMode == SM_PA) {
841 _reproj(corew * scaleFactor, coreh * scaleFactor);
842 } else {
843 _reproj2(corew, coreh);
844 }
845
846 GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
847 GX_Position2s16(0, vertHeight);
848 GX_Color1u32(color);
849 GX_TexCoord2s16(0, 1);
850
851 GX_Position2s16(vertWidth, vertHeight);
852 GX_Color1u32(color);
853 GX_TexCoord2s16(1, 1);
854
855 GX_Position2s16(vertWidth, 0);
856 GX_Color1u32(color);
857 GX_TexCoord2s16(1, 0);
858
859 GX_Position2s16(0, 0);
860 GX_Color1u32(color);
861 GX_TexCoord2s16(0, 0);
862 GX_End();
863}
864
865uint16_t _pollGameInput(struct mGUIRunner* runner) {
866 UNUSED(runner);
867 PAD_ScanPads();
868 u16 padkeys = PAD_ButtonsHeld(0);
869 WPAD_ScanPads();
870 u32 wiiPad = WPAD_ButtonsHeld(0);
871 u32 ext = 0;
872 WPAD_Probe(0, &ext);
873 uint16_t keys = mInputMapKeyBits(&runner->core->inputMap, GCN1_INPUT, padkeys, 0);
874 keys |= mInputMapKeyBits(&runner->core->inputMap, GCN2_INPUT, padkeys, 0);
875 keys |= mInputMapKeyBits(&runner->core->inputMap, WIIMOTE_INPUT, wiiPad, 0);
876
877 enum GBAKey angles = mInputMapAxis(&runner->core->inputMap, GCN1_INPUT, 0, PAD_StickX(0));
878 if (angles != GBA_KEY_NONE) {
879 keys |= 1 << angles;
880 }
881 angles = mInputMapAxis(&runner->core->inputMap, GCN1_INPUT, 1, PAD_StickY(0));
882 if (angles != GBA_KEY_NONE) {
883 keys |= 1 << angles;
884 }
885 if (ext == WPAD_EXP_CLASSIC) {
886 keys |= mInputMapKeyBits(&runner->core->inputMap, CLASSIC_INPUT, wiiPad, 0);
887 angles = mInputMapAxis(&runner->core->inputMap, CLASSIC_INPUT, 0, WPAD_StickX(0, 0));
888 if (angles != GBA_KEY_NONE) {
889 keys |= 1 << angles;
890 }
891 angles = mInputMapAxis(&runner->core->inputMap, CLASSIC_INPUT, 1, WPAD_StickY(0, 0));
892 if (angles != GBA_KEY_NONE) {
893 keys |= 1 << angles;
894 }
895 }
896
897 return keys;
898}
899
900void _incrementScreenMode(struct mGUIRunner* runner) {
901 UNUSED(runner);
902 int mode = screenMode | (filterMode << 1);
903 ++mode;
904 screenMode = mode % SM_MAX;
905 filterMode = (mode >> 1) % FM_MAX;
906 mCoreConfigSetUIntValue(&runner->config, "screenMode", screenMode);
907 mCoreConfigSetUIntValue(&runner->config, "filter", filterMode);
908 switch (filterMode) {
909 case FM_NEAREST:
910 case FM_LINEAR_2x:
911 default:
912 GX_InitTexObjFilterMode(&tex, GX_NEAR, GX_NEAR);
913 break;
914 case FM_LINEAR_1x:
915 GX_InitTexObjFilterMode(&tex, GX_LINEAR, GX_LINEAR);
916 break;
917 }
918}
919
920void _setRumble(struct mRumble* rumble, int enable) {
921 UNUSED(rumble);
922 WPAD_Rumble(0, enable);
923 if (enable) {
924 PAD_ControlMotor(0, PAD_MOTOR_RUMBLE);
925 } else {
926 PAD_ControlMotor(0, PAD_MOTOR_STOP);
927 }
928}
929
930void _sampleRotation(struct mRotationSource* source) {
931 UNUSED(source);
932 vec3w_t accel;
933 WPAD_Accel(0, &accel);
934 // These are swapped
935 tiltX = (0x1EA - accel.y) << 22;
936 tiltY = (0x1EA - accel.x) << 22;
937
938 // This doesn't seem to work at all with -TR remotes
939 struct expansion_t exp;
940 WPAD_Expansion(0, &exp);
941 if (exp.type != EXP_MOTION_PLUS) {
942 return;
943 }
944 gyroZ = exp.mp.rz - 0x1FA0;
945 gyroZ <<= 18;
946}
947
948int32_t _readTiltX(struct mRotationSource* source) {
949 UNUSED(source);
950 return tiltX;
951}
952
953int32_t _readTiltY(struct mRotationSource* source) {
954 UNUSED(source);
955 return tiltY;
956}
957
958int32_t _readGyroZ(struct mRotationSource* source) {
959 UNUSED(source);
960 return gyroZ;
961}
962
963static s8 WPAD_StickX(u8 chan, u8 right) {
964 float mag = 0.0;
965 float ang = 0.0;
966 WPADData *data = WPAD_Data(chan);
967
968 switch (data->exp.type) {
969 case WPAD_EXP_NUNCHUK:
970 case WPAD_EXP_GUITARHERO3:
971 if (right == 0) {
972 mag = data->exp.nunchuk.js.mag;
973 ang = data->exp.nunchuk.js.ang;
974 }
975 break;
976 case WPAD_EXP_CLASSIC:
977 if (right == 0) {
978 mag = data->exp.classic.ljs.mag;
979 ang = data->exp.classic.ljs.ang;
980 } else {
981 mag = data->exp.classic.rjs.mag;
982 ang = data->exp.classic.rjs.ang;
983 }
984 break;
985 default:
986 break;
987 }
988
989 /* calculate X value (angle need to be converted into radian) */
990 if (mag > 1.0) {
991 mag = 1.0;
992 } else if (mag < -1.0) {
993 mag = -1.0;
994 }
995 double val = mag * sinf(M_PI * ang / 180.0f);
996
997 return (s8)(val * 128.0f);
998}
999
1000static s8 WPAD_StickY(u8 chan, u8 right) {
1001 float mag = 0.0;
1002 float ang = 0.0;
1003 WPADData *data = WPAD_Data(chan);
1004
1005 switch (data->exp.type) {
1006 case WPAD_EXP_NUNCHUK:
1007 case WPAD_EXP_GUITARHERO3:
1008 if (right == 0) {
1009 mag = data->exp.nunchuk.js.mag;
1010 ang = data->exp.nunchuk.js.ang;
1011 }
1012 break;
1013 case WPAD_EXP_CLASSIC:
1014 if (right == 0) {
1015 mag = data->exp.classic.ljs.mag;
1016 ang = data->exp.classic.ljs.ang;
1017 } else {
1018 mag = data->exp.classic.rjs.mag;
1019 ang = data->exp.classic.rjs.ang;
1020 }
1021 break;
1022 default:
1023 break;
1024 }
1025
1026 /* calculate X value (angle need to be converted into radian) */
1027 if (mag > 1.0) {
1028 mag = 1.0;
1029 } else if (mag < -1.0) {
1030 mag = -1.0;
1031 }
1032 double val = mag * cosf(M_PI * ang / 180.0f);
1033
1034 return (s8)(val * 128.0f);
1035}
1036
1037void _retraceCallback(u32 count) {
1038 u32 level = 0;
1039 _CPU_ISR_Disable(level);
1040 retraceCount = count;
1041 _CPU_ISR_Restore(level);
1042}