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