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 "util/common.h"
16
17#include "core/core.h"
18#include "feature/gui/gui-runner.h"
19#include "gba/audio.h"
20#include "gba/gba.h"
21#include "gba/input.h"
22#include "util/gui.h"
23#include "util/gui/file-select.h"
24#include "util/gui/font.h"
25#include "util/gui/menu.h"
26#include "util/vfs.h"
27
28#define GCN1_INPUT 0x47434E31
29#define GCN2_INPUT 0x47434E32
30#define WIIMOTE_INPUT 0x5749494D
31#define CLASSIC_INPUT 0x57494943
32
33#define TEX_W 256
34#define TEX_H 160
35
36static void _mapKey(struct mInputMap* map, uint32_t binding, int nativeKey, enum GBAKey key) {
37 mInputBindKey(map, binding, __builtin_ctz(nativeKey), key);
38}
39
40static enum ScreenMode {
41 SM_PA,
42 SM_SF,
43 SM_MAX
44} screenMode = SM_PA;
45
46static enum FilterMode {
47 FM_NEAREST,
48 FM_LINEAR_1x,
49 FM_LINEAR_2x,
50 FM_MAX
51} filterMode = FM_NEAREST;
52
53static enum VideoMode {
54 VM_AUTODETECT,
55 VM_480i,
56 VM_480p,
57 VM_240p,
58 // TODO: PAL support
59 VM_MAX
60} videoMode = VM_AUTODETECT;
61
62#define SAMPLES 1024
63#define GUI_SCALE 1.35f
64#define GUI_SCALE_240p 2.0f
65
66static void _retraceCallback(u32 count);
67
68static void _audioDMA(void);
69static void _setRumble(struct mRumble* rumble, int enable);
70static void _sampleRotation(struct mRotationSource* source);
71static int32_t _readTiltX(struct mRotationSource* source);
72static int32_t _readTiltY(struct mRotationSource* source);
73static int32_t _readGyroZ(struct mRotationSource* source);
74
75static void _drawStart(void);
76static void _drawEnd(void);
77static uint32_t _pollInput(const struct mInputMap*);
78static enum GUICursorState _pollCursor(unsigned* x, unsigned* y);
79static void _guiPrepare(void);
80
81static void _setup(struct mGUIRunner* runner);
82static void _gameLoaded(struct mGUIRunner* runner);
83static void _gameUnloaded(struct mGUIRunner* runner);
84static void _unpaused(struct mGUIRunner* runner);
85static void _drawFrame(struct mGUIRunner* runner, bool faded);
86static uint16_t _pollGameInput(struct mGUIRunner* runner);
87static void _setFrameLimiter(struct mGUIRunner* runner, bool limit);
88static void _incrementScreenMode(struct mGUIRunner* runner);
89
90static s8 WPAD_StickX(u8 chan, u8 right);
91static s8 WPAD_StickY(u8 chan, u8 right);
92
93static void* outputBuffer;
94static struct mRumble rumble;
95static struct mRotationSource rotation;
96static GXRModeObj* vmode;
97static float wAdjust;
98static float hAdjust;
99static float wStretch = 1.0f;
100static float hStretch = 0.9f;
101static float guiScale = GUI_SCALE;
102static Mtx model, view, modelview;
103static uint16_t* texmem;
104static GXTexObj tex;
105static uint16_t* rescaleTexmem;
106static GXTexObj rescaleTex;
107static int32_t tiltX;
108static int32_t tiltY;
109static int32_t gyroZ;
110static uint32_t retraceCount;
111static uint32_t referenceRetraceCount;
112static bool frameLimiter = true;
113static int scaleFactor;
114static unsigned corew, coreh;
115
116static void* framebuffer[2] = { 0, 0 };
117static int whichFb = 0;
118
119static struct GBAStereoSample audioBuffer[3][SAMPLES] __attribute__((__aligned__(32)));
120static volatile size_t audioBufferSize = 0;
121static volatile int currentAudioBuffer = 0;
122static double audioSampleRate = 60.0 / 1.001;
123
124static struct GUIFont* font;
125
126static void reconfigureScreen(struct mGUIRunner* runner) {
127 if (runner) {
128 unsigned mode;
129 if (mCoreConfigGetUIntValue(&runner->config, "videoMode", &mode) && mode < VM_MAX) {
130 videoMode = mode;
131 }
132 }
133 wAdjust = 1.f;
134 hAdjust = 1.f;
135 guiScale = GUI_SCALE;
136 audioSampleRate = 60.0 / 1.001;
137
138 s32 signalMode = CONF_GetVideo();
139
140 switch (videoMode) {
141 case VM_AUTODETECT:
142 default:
143 vmode = VIDEO_GetPreferredMode(0);
144 break;
145 case VM_480i:
146 switch (signalMode) {
147 case CONF_VIDEO_NTSC:
148 vmode = &TVNtsc480IntDf;
149 break;
150 case CONF_VIDEO_MPAL:
151 vmode = &TVMpal480IntDf;
152 break;
153 case CONF_VIDEO_PAL:
154 vmode = &TVEurgb60Hz480IntDf;
155 break;
156 }
157 break;
158 case VM_480p:
159 switch (signalMode) {
160 case CONF_VIDEO_NTSC:
161 vmode = &TVNtsc480Prog;
162 break;
163 case CONF_VIDEO_MPAL:
164 vmode = &TVMpal480Prog;
165 break;
166 case CONF_VIDEO_PAL:
167 vmode = &TVEurgb60Hz480Prog;
168 break;
169 }
170 break;
171 case VM_240p:
172 switch (signalMode) {
173 case CONF_VIDEO_NTSC:
174 vmode = &TVNtsc240Ds;
175 break;
176 case CONF_VIDEO_MPAL:
177 vmode = &TVMpal240Ds;
178 break;
179 case CONF_VIDEO_PAL:
180 vmode = &TVEurgb60Hz240Ds;
181 break;
182 }
183 wAdjust = 0.5f;
184 audioSampleRate = 90.0 / 1.50436;
185 guiScale = GUI_SCALE_240p;
186 break;
187 }
188
189 free(framebuffer[0]);
190 free(framebuffer[1]);
191
192 framebuffer[0] = SYS_AllocateFramebuffer(vmode);
193 framebuffer[1] = SYS_AllocateFramebuffer(vmode);
194
195 VIDEO_SetBlack(true);
196 VIDEO_Configure(vmode);
197 VIDEO_SetNextFramebuffer(framebuffer[whichFb]);
198 VIDEO_Flush();
199 VIDEO_WaitVSync();
200 if (vmode->viTVMode & VI_NON_INTERLACE) {
201 VIDEO_WaitVSync();
202 }
203 VIDEO_SetBlack(false);
204 GX_SetViewport(0, 0, vmode->fbWidth, vmode->efbHeight, 0, 1);
205
206 f32 yscale = GX_GetYScaleFactor(vmode->efbHeight, vmode->xfbHeight);
207 u32 xfbHeight = GX_SetDispCopyYScale(yscale);
208 GX_SetScissor(0, 0, vmode->viWidth, vmode->viWidth);
209 GX_SetDispCopySrc(0, 0, vmode->fbWidth, vmode->efbHeight);
210 GX_SetDispCopyDst(vmode->fbWidth, xfbHeight);
211 GX_SetCopyFilter(vmode->aa, vmode->sample_pattern, GX_TRUE, vmode->vfilter);
212 GX_SetFieldMode(vmode->field_rendering, ((vmode->viHeight == 2 * vmode->xfbHeight) ? GX_ENABLE : GX_DISABLE));
213
214 if (runner) {
215 runner->params.width = vmode->fbWidth * guiScale * wAdjust;
216 runner->params.height = vmode->efbHeight * guiScale * hAdjust;
217 if (runner->core) {
218 double ratio = GBAAudioCalculateRatio(1,audioSampleRate, 1);
219 blip_set_rates(runner->core->getAudioChannel(runner->core, 0), runner->core->frequency(runner->core), 48000 * ratio);
220 blip_set_rates(runner->core->getAudioChannel(runner->core, 1), runner->core->frequency(runner->core), 48000 * ratio);
221
222 runner->core->desiredVideoDimensions(runner->core, &corew, &coreh);
223 int hfactor = vmode->fbWidth / (corew * wAdjust);
224 int vfactor = vmode->efbHeight / (coreh * hAdjust);
225 if (hfactor > vfactor) {
226 scaleFactor = vfactor;
227 } else {
228 scaleFactor = hfactor;
229 }
230 }
231 }
232}
233
234int main(int argc, char* argv[]) {
235 VIDEO_Init();
236 PAD_Init();
237 WPAD_Init();
238 WPAD_SetDataFormat(0, WPAD_FMT_BTNS_ACC_IR);
239 AUDIO_Init(0);
240 AUDIO_SetDSPSampleRate(AI_SAMPLERATE_48KHZ);
241 AUDIO_RegisterDMACallback(_audioDMA);
242
243 memset(audioBuffer, 0, sizeof(audioBuffer));
244
245#if !defined(COLOR_16_BIT) && !defined(COLOR_5_6_5)
246#error This pixel format is unsupported. Please use -DCOLOR_16-BIT -DCOLOR_5_6_5
247#endif
248
249 GXColor bg = { 0, 0, 0, 0xFF };
250 void* fifo = memalign(32, 0x40000);
251 memset(fifo, 0, 0x40000);
252 GX_Init(fifo, 0x40000);
253 GX_SetCopyClear(bg, 0x00FFFFFF);
254
255 GX_SetCullMode(GX_CULL_NONE);
256 GX_SetDispCopyGamma(GX_GM_1_0);
257
258 GX_ClearVtxDesc();
259 GX_SetVtxDesc(GX_VA_POS, GX_DIRECT);
260 GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT);
261 GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT);
262
263 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 0);
264 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0);
265 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
266
267 GX_SetNumChans(1);
268 GX_SetNumTexGens(1);
269 GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
270 GX_SetTevOp(GX_TEVSTAGE0, GX_MODULATE);
271
272 GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);
273 GX_InvVtxCache();
274 GX_InvalidateTexAll();
275
276 guVector cam = { 0.0f, 0.0f, 0.0f };
277 guVector up = { 0.0f, 1.0f, 0.0f };
278 guVector look = { 0.0f, 0.0f, -1.0f };
279 guLookAt(view, &cam, &up, &look);
280
281 guMtxIdentity(model);
282 guMtxTransApply(model, model, 0.0f, 0.0f, -6.0f);
283 guMtxConcat(view, model, modelview);
284 GX_LoadPosMtxImm(modelview, GX_PNMTX0);
285
286 texmem = memalign(32, TEX_W * TEX_H * BYTES_PER_PIXEL);
287 GX_InitTexObj(&tex, texmem, TEX_W, TEX_H, GX_TF_RGB565, GX_CLAMP, GX_CLAMP, GX_FALSE);
288 rescaleTexmem = memalign(32, TEX_W * TEX_H * 4 * BYTES_PER_PIXEL);
289 GX_InitTexObj(&rescaleTex, rescaleTexmem, TEX_W * 2, TEX_H * 2, GX_TF_RGB565, GX_CLAMP, GX_CLAMP, GX_FALSE);
290 GX_InitTexObjFilterMode(&rescaleTex, GX_LINEAR, GX_LINEAR);
291
292 VIDEO_SetPostRetraceCallback(_retraceCallback);
293
294 font = GUIFontCreate();
295
296 fatInitDefault();
297
298 rumble.setRumble = _setRumble;
299
300 rotation.sample = _sampleRotation;
301 rotation.readTiltX = _readTiltX;
302 rotation.readTiltY = _readTiltY;
303 rotation.readGyroZ = _readGyroZ;
304
305 struct mGUIRunner runner = {
306 .params = {
307 720, 480,
308 font, "",
309 _drawStart, _drawEnd,
310 _pollInput, _pollCursor,
311 0,
312 _guiPrepare, 0,
313
314 GUI_PARAMS_TRAIL
315 },
316 .keySources = (struct GUIInputKeys[]) {
317 {
318 .name = "GameCube Input (1)",
319 .id = GCN1_INPUT,
320 .keyNames = (const char*[]) {
321 "D-Pad Left",
322 "D-Pad Right",
323 "D-Pad Down",
324 "D-Pad Up",
325 "Z",
326 "R",
327 "L",
328 0,
329 "A",
330 "B",
331 "X",
332 "Y",
333 "Start"
334 },
335 .nKeys = 13
336 },
337 {
338 .name = "GameCube Input (2)",
339 .id = GCN2_INPUT,
340 .keyNames = (const char*[]) {
341 "D-Pad Left",
342 "D-Pad Right",
343 "D-Pad Down",
344 "D-Pad Up",
345 "Z",
346 "R",
347 "L",
348 0,
349 "A",
350 "B",
351 "X",
352 "Y",
353 "Start"
354 },
355 .nKeys = 13
356 },
357 {
358 .name = "Wii Remote Input",
359 .id = WIIMOTE_INPUT,
360 .keyNames = (const char*[]) {
361 "2",
362 "1",
363 "B",
364 "A",
365 "-",
366 0,
367 0,
368 "\1\xE",
369 "Left",
370 "Right",
371 "Down",
372 "Up",
373 "+",
374 0,
375 0,
376 0,
377 "Z",
378 "C",
379 },
380 .nKeys = 18
381 },
382 {
383 .name = "Classic Controller Input",
384 .id = CLASSIC_INPUT,
385 .keyNames = (const char*[]) {
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 0,
402 "Up",
403 "Left",
404 "ZR",
405 "X",
406 "A",
407 "Y",
408 "B",
409 "ZL",
410 0,
411 "R",
412 "+",
413 "\1\xE",
414 "-",
415 "L",
416 "Down",
417 "Right",
418 },
419 .nKeys = 32
420 },
421 { .id = 0 }
422 },
423 .configExtra = (struct GUIMenuItem[]) {
424 {
425 .title = "Video mode",
426 .data = "videoMode",
427 .submenu = 0,
428 .state = 0,
429 .validStates = (const char*[]) {
430 "Autodetect (recommended)",
431 "480i",
432 "480p",
433 "240p",
434 },
435 .nStates = 4
436 },
437 {
438 .title = "Screen mode",
439 .data = "screenMode",
440 .submenu = 0,
441 .state = 0,
442 .validStates = (const char*[]) {
443 "Pixel-Accurate",
444 "Stretched",
445 },
446 .nStates = 2
447 },
448 {
449 .title = "Filtering",
450 .data = "filter",
451 .submenu = 0,
452 .state = 0,
453 .validStates = (const char*[]) {
454 "Pixelated",
455 "Bilinear (smoother)",
456 "Bilinear (pixelated)",
457 },
458 .nStates = 3
459 }
460 },
461 .nConfigExtra = 3,
462 .setup = _setup,
463 .teardown = 0,
464 .gameLoaded = _gameLoaded,
465 .gameUnloaded = _gameUnloaded,
466 .prepareForFrame = 0,
467 .drawFrame = _drawFrame,
468 .paused = _gameUnloaded,
469 .unpaused = _unpaused,
470 .incrementScreenMode = _incrementScreenMode,
471 .setFrameLimiter = _setFrameLimiter,
472 .pollGameInput = _pollGameInput
473 };
474 mGUIInit(&runner, "wii");
475 reconfigureScreen(&runner);
476
477 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_A, GUI_INPUT_SELECT);
478 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_B, GUI_INPUT_BACK);
479 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_TRIGGER_Z, GUI_INPUT_CANCEL);
480 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_UP, GUI_INPUT_UP);
481 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_DOWN, GUI_INPUT_DOWN);
482 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_LEFT, GUI_INPUT_LEFT);
483 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_RIGHT, GUI_INPUT_RIGHT);
484
485 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_2, GUI_INPUT_SELECT);
486 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_1, GUI_INPUT_BACK);
487 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_HOME, GUI_INPUT_CANCEL);
488 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_RIGHT, GUI_INPUT_UP);
489 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_LEFT, GUI_INPUT_DOWN);
490 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_UP, GUI_INPUT_LEFT);
491 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_DOWN, GUI_INPUT_RIGHT);
492
493 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_A, GUI_INPUT_SELECT);
494 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_Y, GUI_INPUT_SELECT);
495 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_B, GUI_INPUT_BACK);
496 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_X, GUI_INPUT_BACK);
497 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_HOME, GUI_INPUT_CANCEL);
498 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_UP, GUI_INPUT_UP);
499 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_DOWN, GUI_INPUT_DOWN);
500 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_LEFT, GUI_INPUT_LEFT);
501 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_RIGHT, GUI_INPUT_RIGHT);
502
503 if (argc > 1) {
504 size_t i;
505 for (i = 0; runner.keySources[i].id; ++i) {
506 mInputMapLoad(&runner.params.keyMap, runner.keySources[i].id, mCoreConfigGetInput(&runner.config));
507 }
508 mGUIRun(&runner, argv[1]);
509 } else {
510 mGUIRunloop(&runner);
511 }
512 mGUIDeinit(&runner);
513
514 free(fifo);
515 free(texmem);
516 free(rescaleTexmem);
517
518 free(outputBuffer);
519 GUIFontDestroy(font);
520
521 free(framebuffer[0]);
522 free(framebuffer[1]);
523
524 return 0;
525}
526
527static void _audioDMA(void) {
528 if (!audioBufferSize) {
529 return;
530 }
531 DCFlushRange(audioBuffer[currentAudioBuffer], audioBufferSize * sizeof(struct GBAStereoSample));
532 AUDIO_InitDMA((u32) audioBuffer[currentAudioBuffer], audioBufferSize * sizeof(struct GBAStereoSample));
533 currentAudioBuffer = (currentAudioBuffer + 1) % 3;
534 audioBufferSize = 0;
535}
536
537static void _drawStart(void) {
538 u32 level = 0;
539 _CPU_ISR_Disable(level);
540 if (referenceRetraceCount > retraceCount) {
541 if (frameLimiter) {
542 VIDEO_WaitVSync();
543 }
544 referenceRetraceCount = retraceCount;
545 }
546 _CPU_ISR_Restore(level);
547
548 GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE);
549 GX_SetColorUpdate(GX_TRUE);
550
551 GX_SetViewport(0, 0, vmode->fbWidth, vmode->efbHeight, 0, 1);
552}
553
554static void _drawEnd(void) {
555 whichFb = !whichFb;
556
557 GX_CopyDisp(framebuffer[whichFb], GX_TRUE);
558 GX_DrawDone();
559 VIDEO_SetNextFramebuffer(framebuffer[whichFb]);
560 VIDEO_Flush();
561
562 u32 level = 0;
563 _CPU_ISR_Disable(level);
564 ++referenceRetraceCount;
565 _CPU_ISR_Restore(level);
566}
567
568static void _setFrameLimiter(struct mGUIRunner* runner, bool limit) {
569 UNUSED(runner);
570 frameLimiter = limit;
571}
572
573static uint32_t _pollInput(const struct mInputMap* map) {
574 PAD_ScanPads();
575 u16 padkeys = PAD_ButtonsHeld(0);
576
577 WPAD_ScanPads();
578 u32 wiiPad = WPAD_ButtonsHeld(0);
579 u32 ext = 0;
580 WPAD_Probe(0, &ext);
581
582 int keys = 0;
583 keys |= mInputMapKeyBits(map, GCN1_INPUT, padkeys, 0);
584 keys |= mInputMapKeyBits(map, GCN2_INPUT, padkeys, 0);
585 keys |= mInputMapKeyBits(map, WIIMOTE_INPUT, wiiPad, 0);
586 if (ext == WPAD_EXP_CLASSIC) {
587 keys |= mInputMapKeyBits(map, CLASSIC_INPUT, wiiPad, 0);
588 }
589 int x = PAD_StickX(0);
590 int y = PAD_StickY(0);
591 int w_x = WPAD_StickX(0, 0);
592 int w_y = WPAD_StickY(0, 0);
593 if (x < -0x20 || w_x < -0x20) {
594 keys |= 1 << GUI_INPUT_LEFT;
595 }
596 if (x > 0x20 || w_x > 0x20) {
597 keys |= 1 << GUI_INPUT_RIGHT;
598 }
599 if (y < -0x20 || w_y <- 0x20) {
600 keys |= 1 << GUI_INPUT_DOWN;
601 }
602 if (y > 0x20 || w_y > 0x20) {
603 keys |= 1 << GUI_INPUT_UP;
604 }
605 return keys;
606}
607
608static enum GUICursorState _pollCursor(unsigned* x, unsigned* y) {
609 ir_t ir;
610 WPAD_IR(0, &ir);
611 if (!ir.smooth_valid) {
612 return GUI_CURSOR_NOT_PRESENT;
613 }
614 *x = ir.sx;
615 *y = ir.sy;
616 WPAD_ScanPads();
617 u32 wiiPad = WPAD_ButtonsHeld(0);
618 if (wiiPad & WPAD_BUTTON_A) {
619 return GUI_CURSOR_DOWN;
620 }
621 return GUI_CURSOR_UP;
622}
623
624void _reproj(int w, int h) {
625 Mtx44 proj;
626 int top = (vmode->efbHeight * hAdjust - h) / 2;
627 int left = (vmode->fbWidth * wAdjust - w) / 2;
628 guOrtho(proj, -top, top + h, -left, left + w, 0, 300);
629 GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
630}
631
632void _reproj2(int w, int h) {
633 Mtx44 proj;
634 int top = h * (1.0 - hStretch) / 2;
635 int left = w * (1.0 - wStretch) / 2;
636 guOrtho(proj, -top, h + top, -left, w + left, 0, 300);
637 GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
638}
639
640void _guiPrepare(void) {
641 _reproj2(vmode->fbWidth * guiScale * wAdjust, vmode->efbHeight * guiScale * hAdjust);
642}
643
644void _setup(struct mGUIRunner* runner) {
645 runner->core->setRotation(runner->core, &rotation);
646 runner->core->setRumble(runner->core, &rumble);
647
648 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_A, GBA_KEY_A);
649 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_B, GBA_KEY_B);
650 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_START, GBA_KEY_START);
651 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_X, GBA_KEY_SELECT);
652 _mapKey(&runner->core->inputMap, GCN2_INPUT, PAD_BUTTON_Y, GBA_KEY_SELECT);
653 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_UP, GBA_KEY_UP);
654 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_DOWN, GBA_KEY_DOWN);
655 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_LEFT, GBA_KEY_LEFT);
656 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_RIGHT, GBA_KEY_RIGHT);
657 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_TRIGGER_L, GBA_KEY_L);
658 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_TRIGGER_R, GBA_KEY_R);
659
660 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_2, GBA_KEY_A);
661 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_1, GBA_KEY_B);
662 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_PLUS, GBA_KEY_START);
663 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_MINUS, GBA_KEY_SELECT);
664 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_RIGHT, GBA_KEY_UP);
665 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_LEFT, GBA_KEY_DOWN);
666 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_UP, GBA_KEY_LEFT);
667 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_DOWN, GBA_KEY_RIGHT);
668 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_B, GBA_KEY_L);
669 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_A, GBA_KEY_R);
670
671 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_A, GBA_KEY_A);
672 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_B, GBA_KEY_B);
673 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_PLUS, GBA_KEY_START);
674 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_MINUS, GBA_KEY_SELECT);
675 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_UP, GBA_KEY_UP);
676 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_DOWN, GBA_KEY_DOWN);
677 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_LEFT, GBA_KEY_LEFT);
678 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_RIGHT, GBA_KEY_RIGHT);
679 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_FULL_L, GBA_KEY_L);
680 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_FULL_R, GBA_KEY_R);
681
682 struct mInputAxis desc = { GBA_KEY_RIGHT, GBA_KEY_LEFT, 0x20, -0x20 };
683 mInputBindAxis(&runner->core->inputMap, GCN1_INPUT, 0, &desc);
684 mInputBindAxis(&runner->core->inputMap, CLASSIC_INPUT, 0, &desc);
685 desc = (struct mInputAxis) { GBA_KEY_UP, GBA_KEY_DOWN, 0x20, -0x20 };
686 mInputBindAxis(&runner->core->inputMap, GCN1_INPUT, 1, &desc);
687 mInputBindAxis(&runner->core->inputMap, CLASSIC_INPUT, 1, &desc);
688
689 outputBuffer = memalign(32, TEX_W * TEX_H * BYTES_PER_PIXEL);
690 runner->core->setVideoBuffer(runner->core, outputBuffer, TEX_W);
691
692 runner->core->setAudioBufferSize(runner->core, SAMPLES);
693
694 double ratio = GBAAudioCalculateRatio(1, audioSampleRate, 1);
695 blip_set_rates(runner->core->getAudioChannel(runner->core, 0), runner->core->frequency(runner->core), 48000 * ratio);
696 blip_set_rates(runner->core->getAudioChannel(runner->core, 1), runner->core->frequency(runner->core), 48000 * ratio);
697
698 frameLimiter = true;
699}
700
701void _gameUnloaded(struct mGUIRunner* runner) {
702 UNUSED(runner);
703 AUDIO_StopDMA();
704 frameLimiter = true;
705}
706
707void _gameLoaded(struct mGUIRunner* runner) {
708 reconfigureScreen(runner);
709 if (runner->core->platform(runner->core) == PLATFORM_GBA && ((struct GBA*) runner->core->board)->memory.hw.devices & HW_GYRO) {
710 int i;
711 for (i = 0; i < 6; ++i) {
712 u32 result = WPAD_SetMotionPlus(0, 1);
713 if (result == WPAD_ERR_NONE) {
714 break;
715 }
716 sleep(1);
717 }
718 }
719 memset(texmem, 0, TEX_W * TEX_H * BYTES_PER_PIXEL);
720 _unpaused(runner);
721}
722
723void _unpaused(struct mGUIRunner* runner) {
724 u32 level = 0;
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}