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