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_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0);
787 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
788 GX_InvalidateTexAll();
789 GX_LoadTexObj(&tex, GX_TEXMAP0);
790
791 s16 vertSize = 256;
792 if (screenMode == SM_PA) {
793 vertSize *= scaleFactor;
794 }
795
796 GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
797 GX_Position2s16(0, vertSize);
798 GX_Color1u32(color);
799 GX_TexCoord2s16(0, 1);
800
801 GX_Position2s16(vertSize, vertSize);
802 GX_Color1u32(color);
803 GX_TexCoord2s16(1, 1);
804
805 GX_Position2s16(vertSize, 0);
806 GX_Color1u32(color);
807 GX_TexCoord2s16(1, 0);
808
809 GX_Position2s16(0, 0);
810 GX_Color1u32(color);
811 GX_TexCoord2s16(0, 0);
812 GX_End();
813}
814
815uint16_t _pollGameInput(struct mGUIRunner* runner) {
816 UNUSED(runner);
817 PAD_ScanPads();
818 u16 padkeys = PAD_ButtonsHeld(0);
819 WPAD_ScanPads();
820 u32 wiiPad = WPAD_ButtonsHeld(0);
821 u32 ext = 0;
822 WPAD_Probe(0, &ext);
823 uint16_t keys = mInputMapKeyBits(&runner->core->inputMap, GCN1_INPUT, padkeys, 0);
824 keys |= mInputMapKeyBits(&runner->core->inputMap, GCN2_INPUT, padkeys, 0);
825 keys |= mInputMapKeyBits(&runner->core->inputMap, WIIMOTE_INPUT, wiiPad, 0);
826
827 enum GBAKey angles = mInputMapAxis(&runner->core->inputMap, GCN1_INPUT, 0, PAD_StickX(0));
828 if (angles != GBA_KEY_NONE) {
829 keys |= 1 << angles;
830 }
831 angles = mInputMapAxis(&runner->core->inputMap, GCN1_INPUT, 1, PAD_StickY(0));
832 if (angles != GBA_KEY_NONE) {
833 keys |= 1 << angles;
834 }
835 if (ext == WPAD_EXP_CLASSIC) {
836 keys |= mInputMapKeyBits(&runner->core->inputMap, CLASSIC_INPUT, wiiPad, 0);
837 angles = mInputMapAxis(&runner->core->inputMap, CLASSIC_INPUT, 0, WPAD_StickX(0, 0));
838 if (angles != GBA_KEY_NONE) {
839 keys |= 1 << angles;
840 }
841 angles = mInputMapAxis(&runner->core->inputMap, CLASSIC_INPUT, 1, WPAD_StickY(0, 0));
842 if (angles != GBA_KEY_NONE) {
843 keys |= 1 << angles;
844 }
845 }
846
847 return keys;
848}
849
850void _incrementScreenMode(struct mGUIRunner* runner) {
851 UNUSED(runner);
852 int mode = screenMode | (filterMode << 1);
853 ++mode;
854 screenMode = mode % SM_MAX;
855 filterMode = (mode >> 1) % FM_MAX;
856 mCoreConfigSetUIntValue(&runner->config, "screenMode", screenMode);
857 mCoreConfigSetUIntValue(&runner->config, "filter", filterMode);
858 if (screenMode == SM_PA) {
859 _reproj(corew * scaleFactor, coreh * scaleFactor);
860 } else {
861 _reproj2(corew, coreh);
862 }
863 switch (filterMode) {
864 case FM_NEAREST:
865 default:
866 GX_InitTexObjFilterMode(&tex, GX_NEAR, GX_NEAR);
867 break;
868 case FM_LINEAR:
869 GX_InitTexObjFilterMode(&tex, GX_LINEAR, GX_LINEAR);
870 break;
871 }
872}
873
874void _setRumble(struct mRumble* rumble, int enable) {
875 UNUSED(rumble);
876 WPAD_Rumble(0, enable);
877 if (enable) {
878 PAD_ControlMotor(0, PAD_MOTOR_RUMBLE);
879 } else {
880 PAD_ControlMotor(0, PAD_MOTOR_STOP);
881 }
882}
883
884void _sampleRotation(struct mRotationSource* source) {
885 UNUSED(source);
886 vec3w_t accel;
887 WPAD_Accel(0, &accel);
888 // These are swapped
889 tiltX = (0x1EA - accel.y) << 22;
890 tiltY = (0x1EA - accel.x) << 22;
891
892 // This doesn't seem to work at all with -TR remotes
893 struct expansion_t exp;
894 WPAD_Expansion(0, &exp);
895 if (exp.type != EXP_MOTION_PLUS) {
896 return;
897 }
898 gyroZ = exp.mp.rz - 0x1FA0;
899 gyroZ <<= 18;
900}
901
902int32_t _readTiltX(struct mRotationSource* source) {
903 UNUSED(source);
904 return tiltX;
905}
906
907int32_t _readTiltY(struct mRotationSource* source) {
908 UNUSED(source);
909 return tiltY;
910}
911
912int32_t _readGyroZ(struct mRotationSource* source) {
913 UNUSED(source);
914 return gyroZ;
915}
916
917static s8 WPAD_StickX(u8 chan, u8 right) {
918 float mag = 0.0;
919 float ang = 0.0;
920 WPADData *data = WPAD_Data(chan);
921
922 switch (data->exp.type) {
923 case WPAD_EXP_NUNCHUK:
924 case WPAD_EXP_GUITARHERO3:
925 if (right == 0) {
926 mag = data->exp.nunchuk.js.mag;
927 ang = data->exp.nunchuk.js.ang;
928 }
929 break;
930 case WPAD_EXP_CLASSIC:
931 if (right == 0) {
932 mag = data->exp.classic.ljs.mag;
933 ang = data->exp.classic.ljs.ang;
934 } else {
935 mag = data->exp.classic.rjs.mag;
936 ang = data->exp.classic.rjs.ang;
937 }
938 break;
939 default:
940 break;
941 }
942
943 /* calculate X value (angle need to be converted into radian) */
944 if (mag > 1.0) {
945 mag = 1.0;
946 } else if (mag < -1.0) {
947 mag = -1.0;
948 }
949 double val = mag * sinf(M_PI * ang / 180.0f);
950
951 return (s8)(val * 128.0f);
952}
953
954static s8 WPAD_StickY(u8 chan, u8 right) {
955 float mag = 0.0;
956 float ang = 0.0;
957 WPADData *data = WPAD_Data(chan);
958
959 switch (data->exp.type) {
960 case WPAD_EXP_NUNCHUK:
961 case WPAD_EXP_GUITARHERO3:
962 if (right == 0) {
963 mag = data->exp.nunchuk.js.mag;
964 ang = data->exp.nunchuk.js.ang;
965 }
966 break;
967 case WPAD_EXP_CLASSIC:
968 if (right == 0) {
969 mag = data->exp.classic.ljs.mag;
970 ang = data->exp.classic.ljs.ang;
971 } else {
972 mag = data->exp.classic.rjs.mag;
973 ang = data->exp.classic.rjs.ang;
974 }
975 break;
976 default:
977 break;
978 }
979
980 /* calculate X value (angle need to be converted into radian) */
981 if (mag > 1.0) {
982 mag = 1.0;
983 } else if (mag < -1.0) {
984 mag = -1.0;
985 }
986 double val = mag * cosf(M_PI * ang / 180.0f);
987
988 return (s8)(val * 128.0f);
989}
990
991void _retraceCallback(u32 count) {
992 u32 level = 0;
993 _CPU_ISR_Disable(level);
994 retraceCount = count;
995 _CPU_ISR_Restore(level);
996}