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 .keySources = (struct GUIInputKeys[]) {
315 {
316 .name = "GameCube Input (1)",
317 .id = GCN1_INPUT,
318 .keyNames = (const char*[]) {
319 "D-Pad Left",
320 "D-Pad Right",
321 "D-Pad Down",
322 "D-Pad Up",
323 "Z",
324 "R",
325 "L",
326 0,
327 "A",
328 "B",
329 "X",
330 "Y",
331 "Start"
332 },
333 .nKeys = 13
334 },
335 {
336 .name = "GameCube Input (2)",
337 .id = GCN2_INPUT,
338 .keyNames = (const char*[]) {
339 "D-Pad Left",
340 "D-Pad Right",
341 "D-Pad Down",
342 "D-Pad Up",
343 "Z",
344 "R",
345 "L",
346 0,
347 "A",
348 "B",
349 "X",
350 "Y",
351 "Start"
352 },
353 .nKeys = 13
354 },
355 {
356 .name = "Wii Remote Input",
357 .id = WIIMOTE_INPUT,
358 .keyNames = (const char*[]) {
359 "2",
360 "1",
361 "B",
362 "A",
363 "-",
364 0,
365 0,
366 "\1\xE",
367 "Left",
368 "Right",
369 "Down",
370 "Up",
371 "+",
372 0,
373 0,
374 0,
375 "Z",
376 "C",
377 },
378 .nKeys = 18
379 },
380 {
381 .name = "Classic Controller Input",
382 .id = CLASSIC_INPUT,
383 .keyNames = (const char*[]) {
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 0,
400 "Up",
401 "Left",
402 "ZR",
403 "X",
404 "A",
405 "Y",
406 "B",
407 "ZL",
408 0,
409 "R",
410 "+",
411 "\1\xE",
412 "-",
413 "L",
414 "Down",
415 "Right",
416 },
417 .nKeys = 32
418 },
419 { .id = 0 }
420 },
421 .configExtra = (struct GUIMenuItem[]) {
422 {
423 .title = "Video mode",
424 .data = "videoMode",
425 .submenu = 0,
426 .state = 0,
427 .validStates = (const char*[]) {
428 "Autodetect (recommended)",
429 "480i",
430 "480p",
431 "240p",
432 },
433 .nStates = 4
434 },
435 {
436 .title = "Screen mode",
437 .data = "screenMode",
438 .submenu = 0,
439 .state = 0,
440 .validStates = (const char*[]) {
441 "Pixel-Accurate",
442 "Stretched",
443 },
444 .nStates = 2
445 },
446 {
447 .title = "Filtering",
448 .data = "filter",
449 .submenu = 0,
450 .state = 0,
451 .validStates = (const char*[]) {
452 "Pixelated",
453 "Bilinear (smoother)",
454 "Bilinear (pixelated)",
455 },
456 .nStates = 3
457 }
458 },
459 .nConfigExtra = 3,
460 .setup = _setup,
461 .teardown = 0,
462 .gameLoaded = _gameLoaded,
463 .gameUnloaded = _gameUnloaded,
464 .prepareForFrame = 0,
465 .drawFrame = _drawFrame,
466 .paused = _gameUnloaded,
467 .unpaused = _unpaused,
468 .incrementScreenMode = _incrementScreenMode,
469 .setFrameLimiter = _setFrameLimiter,
470 .pollGameInput = _pollGameInput
471 };
472 mGUIInit(&runner, "wii");
473 reconfigureScreen(&runner);
474
475 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_A, GUI_INPUT_SELECT);
476 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_B, GUI_INPUT_BACK);
477 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_TRIGGER_Z, GUI_INPUT_CANCEL);
478 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_UP, GUI_INPUT_UP);
479 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_DOWN, GUI_INPUT_DOWN);
480 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_LEFT, GUI_INPUT_LEFT);
481 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_RIGHT, GUI_INPUT_RIGHT);
482
483 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_2, GUI_INPUT_SELECT);
484 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_1, GUI_INPUT_BACK);
485 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_HOME, GUI_INPUT_CANCEL);
486 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_RIGHT, GUI_INPUT_UP);
487 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_LEFT, GUI_INPUT_DOWN);
488 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_UP, GUI_INPUT_LEFT);
489 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_DOWN, GUI_INPUT_RIGHT);
490
491 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_A, GUI_INPUT_SELECT);
492 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_Y, GUI_INPUT_SELECT);
493 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_B, GUI_INPUT_BACK);
494 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_X, GUI_INPUT_BACK);
495 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_HOME, GUI_INPUT_CANCEL);
496 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_UP, GUI_INPUT_UP);
497 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_DOWN, GUI_INPUT_DOWN);
498 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_LEFT, GUI_INPUT_LEFT);
499 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_RIGHT, GUI_INPUT_RIGHT);
500
501 if (argc > 1) {
502 size_t i;
503 for (i = 0; runner.keySources[i].id; ++i) {
504 mInputMapLoad(&runner.params.keyMap, runner.keySources[i].id, mCoreConfigGetInput(&runner.config));
505 }
506 mGUIRun(&runner, argv[1]);
507 } else {
508 mGUIRunloop(&runner);
509 }
510 mGUIDeinit(&runner);
511
512 free(fifo);
513 free(texmem);
514 free(rescaleTexmem);
515
516 free(outputBuffer);
517 GUIFontDestroy(font);
518
519 free(framebuffer[0]);
520 free(framebuffer[1]);
521
522 return 0;
523}
524
525static void _audioDMA(void) {
526 if (!audioBufferSize) {
527 return;
528 }
529 DCFlushRange(audioBuffer[currentAudioBuffer], audioBufferSize * sizeof(struct GBAStereoSample));
530 AUDIO_InitDMA((u32) audioBuffer[currentAudioBuffer], audioBufferSize * sizeof(struct GBAStereoSample));
531 currentAudioBuffer = (currentAudioBuffer + 1) % 3;
532 audioBufferSize = 0;
533}
534
535static void _drawStart(void) {
536 u32 level = 0;
537 _CPU_ISR_Disable(level);
538 if (referenceRetraceCount > retraceCount) {
539 if (frameLimiter) {
540 VIDEO_WaitVSync();
541 }
542 referenceRetraceCount = retraceCount;
543 }
544 _CPU_ISR_Restore(level);
545
546 GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE);
547 GX_SetColorUpdate(GX_TRUE);
548
549 GX_SetViewport(0, 0, vmode->fbWidth, vmode->efbHeight, 0, 1);
550}
551
552static void _drawEnd(void) {
553 whichFb = !whichFb;
554
555 GX_CopyDisp(framebuffer[whichFb], GX_TRUE);
556 GX_DrawDone();
557 VIDEO_SetNextFramebuffer(framebuffer[whichFb]);
558 VIDEO_Flush();
559
560 u32 level = 0;
561 _CPU_ISR_Disable(level);
562 ++referenceRetraceCount;
563 _CPU_ISR_Restore(level);
564}
565
566static void _setFrameLimiter(struct mGUIRunner* runner, bool limit) {
567 UNUSED(runner);
568 frameLimiter = limit;
569}
570
571static uint32_t _pollInput(const struct mInputMap* map) {
572 PAD_ScanPads();
573 u16 padkeys = PAD_ButtonsHeld(0);
574
575 WPAD_ScanPads();
576 u32 wiiPad = WPAD_ButtonsHeld(0);
577 u32 ext = 0;
578 WPAD_Probe(0, &ext);
579
580 int keys = 0;
581 keys |= mInputMapKeyBits(map, GCN1_INPUT, padkeys, 0);
582 keys |= mInputMapKeyBits(map, GCN2_INPUT, padkeys, 0);
583 keys |= mInputMapKeyBits(map, WIIMOTE_INPUT, wiiPad, 0);
584 if (ext == WPAD_EXP_CLASSIC) {
585 keys |= mInputMapKeyBits(map, CLASSIC_INPUT, wiiPad, 0);
586 }
587 int x = PAD_StickX(0);
588 int y = PAD_StickY(0);
589 int w_x = WPAD_StickX(0, 0);
590 int w_y = WPAD_StickY(0, 0);
591 if (x < -0x20 || w_x < -0x20) {
592 keys |= 1 << GUI_INPUT_LEFT;
593 }
594 if (x > 0x20 || w_x > 0x20) {
595 keys |= 1 << GUI_INPUT_RIGHT;
596 }
597 if (y < -0x20 || w_y <- 0x20) {
598 keys |= 1 << GUI_INPUT_DOWN;
599 }
600 if (y > 0x20 || w_y > 0x20) {
601 keys |= 1 << GUI_INPUT_UP;
602 }
603 return keys;
604}
605
606static enum GUICursorState _pollCursor(unsigned* x, unsigned* y) {
607 ir_t ir;
608 WPAD_IR(0, &ir);
609 if (!ir.smooth_valid) {
610 return GUI_CURSOR_NOT_PRESENT;
611 }
612 *x = ir.sx;
613 *y = ir.sy;
614 WPAD_ScanPads();
615 u32 wiiPad = WPAD_ButtonsHeld(0);
616 if (wiiPad & WPAD_BUTTON_A) {
617 return GUI_CURSOR_DOWN;
618 }
619 return GUI_CURSOR_UP;
620}
621
622void _reproj(int w, int h) {
623 Mtx44 proj;
624 int top = (vmode->efbHeight * hAdjust - h) / 2;
625 int left = (vmode->fbWidth * wAdjust - w) / 2;
626 guOrtho(proj, -top, top + h, -left, left + w, 0, 300);
627 GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
628}
629
630void _reproj2(int w, int h) {
631 Mtx44 proj;
632 int top = h * (1.0 - hStretch) / 2;
633 int left = w * (1.0 - wStretch) / 2;
634 guOrtho(proj, -top, h + top, -left, w + left, 0, 300);
635 GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
636}
637
638void _guiPrepare(void) {
639 _reproj2(vmode->fbWidth * guiScale * wAdjust, vmode->efbHeight * guiScale * hAdjust);
640}
641
642void _setup(struct mGUIRunner* runner) {
643 runner->core->setRotation(runner->core, &rotation);
644 runner->core->setRumble(runner->core, &rumble);
645
646 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_A, GBA_KEY_A);
647 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_B, GBA_KEY_B);
648 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_START, GBA_KEY_START);
649 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_X, GBA_KEY_SELECT);
650 _mapKey(&runner->core->inputMap, GCN2_INPUT, PAD_BUTTON_Y, GBA_KEY_SELECT);
651 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_UP, GBA_KEY_UP);
652 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_DOWN, GBA_KEY_DOWN);
653 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_LEFT, GBA_KEY_LEFT);
654 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_RIGHT, GBA_KEY_RIGHT);
655 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_TRIGGER_L, GBA_KEY_L);
656 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_TRIGGER_R, GBA_KEY_R);
657
658 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_2, GBA_KEY_A);
659 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_1, GBA_KEY_B);
660 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_PLUS, GBA_KEY_START);
661 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_MINUS, GBA_KEY_SELECT);
662 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_RIGHT, GBA_KEY_UP);
663 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_LEFT, GBA_KEY_DOWN);
664 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_UP, GBA_KEY_LEFT);
665 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_DOWN, GBA_KEY_RIGHT);
666 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_B, GBA_KEY_L);
667 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_A, GBA_KEY_R);
668
669 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_A, GBA_KEY_A);
670 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_B, GBA_KEY_B);
671 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_PLUS, GBA_KEY_START);
672 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_MINUS, GBA_KEY_SELECT);
673 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_UP, GBA_KEY_UP);
674 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_DOWN, GBA_KEY_DOWN);
675 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_LEFT, GBA_KEY_LEFT);
676 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_RIGHT, GBA_KEY_RIGHT);
677 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_FULL_L, GBA_KEY_L);
678 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_FULL_R, GBA_KEY_R);
679
680 struct mInputAxis desc = { GBA_KEY_RIGHT, GBA_KEY_LEFT, 0x20, -0x20 };
681 mInputBindAxis(&runner->core->inputMap, GCN1_INPUT, 0, &desc);
682 mInputBindAxis(&runner->core->inputMap, CLASSIC_INPUT, 0, &desc);
683 desc = (struct mInputAxis) { GBA_KEY_UP, GBA_KEY_DOWN, 0x20, -0x20 };
684 mInputBindAxis(&runner->core->inputMap, GCN1_INPUT, 1, &desc);
685 mInputBindAxis(&runner->core->inputMap, CLASSIC_INPUT, 1, &desc);
686
687 outputBuffer = memalign(32, TEX_W * TEX_H * BYTES_PER_PIXEL);
688 runner->core->setVideoBuffer(runner->core, outputBuffer, TEX_W);
689
690 runner->core->setAudioBufferSize(runner->core, SAMPLES);
691
692 double ratio = GBAAudioCalculateRatio(1, audioSampleRate, 1);
693 blip_set_rates(runner->core->getAudioChannel(runner->core, 0), runner->core->frequency(runner->core), 48000 * ratio);
694 blip_set_rates(runner->core->getAudioChannel(runner->core, 1), runner->core->frequency(runner->core), 48000 * ratio);
695
696 frameLimiter = true;
697}
698
699void _gameUnloaded(struct mGUIRunner* runner) {
700 UNUSED(runner);
701 AUDIO_StopDMA();
702 frameLimiter = true;
703}
704
705void _gameLoaded(struct mGUIRunner* runner) {
706 reconfigureScreen(runner);
707 if (runner->core->platform(runner->core) == PLATFORM_GBA && ((struct GBA*) runner->core->board)->memory.hw.devices & HW_GYRO) {
708 int i;
709 for (i = 0; i < 6; ++i) {
710 u32 result = WPAD_SetMotionPlus(0, 1);
711 if (result == WPAD_ERR_NONE) {
712 break;
713 }
714 sleep(1);
715 }
716 }
717 memset(texmem, 0, TEX_W * TEX_H * BYTES_PER_PIXEL);
718 _unpaused(runner);
719}
720
721void _unpaused(struct mGUIRunner* runner) {
722 u32 level = 0;
723 _CPU_ISR_Disable(level);
724 referenceRetraceCount = retraceCount;
725 _CPU_ISR_Restore(level);
726
727 unsigned mode;
728 if (mCoreConfigGetUIntValue(&runner->config, "videoMode", &mode) && mode < VM_MAX) {
729 if (mode != videoMode) {
730 reconfigureScreen(runner);
731 }
732 }
733 if (mCoreConfigGetUIntValue(&runner->config, "screenMode", &mode) && mode < SM_MAX) {
734 screenMode = mode;
735 }
736 if (mCoreConfigGetUIntValue(&runner->config, "filter", &mode) && mode < FM_MAX) {
737 filterMode = mode;
738 switch (mode) {
739 case FM_NEAREST:
740 case FM_LINEAR_2x:
741 default:
742 GX_InitTexObjFilterMode(&tex, GX_NEAR, GX_NEAR);
743 break;
744 case FM_LINEAR_1x:
745 GX_InitTexObjFilterMode(&tex, GX_LINEAR, GX_LINEAR);
746 break;
747 }
748 }
749 float stretch;
750 if (mCoreConfigGetFloatValue(&runner->config, "stretchWidth", &stretch)) {
751 wStretch = fminf(1.0f, fmaxf(0.5f, stretch));
752 }
753 if (mCoreConfigGetFloatValue(&runner->config, "stretchHeight", &stretch)) {
754 hStretch = fminf(1.0f, fmaxf(0.5f, stretch));
755 }
756}
757
758void _drawFrame(struct mGUIRunner* runner, bool faded) {
759 int available = blip_samples_avail(runner->core->getAudioChannel(runner->core, 0));
760 if (available + audioBufferSize > SAMPLES) {
761 available = SAMPLES - audioBufferSize;
762 }
763 available &= ~((32 / sizeof(struct GBAStereoSample)) - 1); // Force align to 32 bytes
764 if (available > 0) {
765 // These appear to be reversed for AUDIO_InitDMA
766 blip_read_samples(runner->core->getAudioChannel(runner->core, 0), &audioBuffer[currentAudioBuffer][audioBufferSize].right, available, true);
767 blip_read_samples(runner->core->getAudioChannel(runner->core, 1), &audioBuffer[currentAudioBuffer][audioBufferSize].left, available, true);
768 audioBufferSize += available;
769 }
770 if (audioBufferSize == SAMPLES && !AUDIO_GetDMAEnableFlag()) {
771 _audioDMA();
772 AUDIO_StartDMA();
773 }
774
775 uint32_t color = 0xFFFFFF3F;
776 if (!faded) {
777 color |= 0xC0;
778 }
779 size_t x, y;
780 uint64_t* texdest = (uint64_t*) texmem;
781 uint64_t* texsrc = (uint64_t*) outputBuffer;
782 for (y = 0; y < coreh; y += 4) {
783 for (x = 0; x < corew >> 2; ++x) {
784 texdest[0 + x * 4 + y * 64] = texsrc[0 + x + y * 64];
785 texdest[1 + x * 4 + y * 64] = texsrc[64 + x + y * 64];
786 texdest[2 + x * 4 + y * 64] = texsrc[128 + x + y * 64];
787 texdest[3 + x * 4 + y * 64] = texsrc[192 + x + y * 64];
788 }
789 }
790 DCFlushRange(texdest, TEX_W * TEX_H * BYTES_PER_PIXEL);
791
792 if (faded) {
793 GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_NOOP);
794 } else {
795 GX_SetBlendMode(GX_BM_NONE, GX_BL_ONE, GX_BL_ZERO, GX_LO_NOOP);
796 }
797 GX_InvalidateTexAll();
798 GX_LoadTexObj(&tex, GX_TEXMAP0);
799
800 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0);
801 s16 vertWidth = TEX_W;
802 s16 vertHeight = TEX_H;
803
804 if (filterMode == FM_LINEAR_2x) {
805 Mtx44 proj;
806 guOrtho(proj, 0, vmode->efbHeight, 0, vmode->fbWidth, 0, 300);
807 GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
808
809 GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
810 GX_Position2s16(0, TEX_H * 2);
811 GX_Color1u32(0xFFFFFFFF);
812 GX_TexCoord2s16(0, 1);
813
814 GX_Position2s16(TEX_W * 2, TEX_H * 2);
815 GX_Color1u32(0xFFFFFFFF);
816 GX_TexCoord2s16(1, 1);
817
818 GX_Position2s16(TEX_W * 2, 0);
819 GX_Color1u32(0xFFFFFFFF);
820 GX_TexCoord2s16(1, 0);
821
822 GX_Position2s16(0, 0);
823 GX_Color1u32(0xFFFFFFFF);
824 GX_TexCoord2s16(0, 0);
825 GX_End();
826
827 GX_SetTexCopySrc(0, 0, TEX_W * 2, TEX_H * 2);
828 GX_SetTexCopyDst(TEX_W * 2, TEX_H * 2, GX_TF_RGB565, GX_FALSE);
829 GX_CopyTex(rescaleTexmem, GX_TRUE);
830 GX_LoadTexObj(&rescaleTex, GX_TEXMAP0);
831 }
832
833 if (screenMode == SM_PA) {
834 vertWidth *= scaleFactor;
835 vertHeight *= scaleFactor;
836 }
837
838 if (screenMode == SM_PA) {
839 _reproj(corew * scaleFactor, coreh * scaleFactor);
840 } else {
841 _reproj2(corew, coreh);
842 }
843
844 GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
845 GX_Position2s16(0, vertHeight);
846 GX_Color1u32(color);
847 GX_TexCoord2s16(0, 1);
848
849 GX_Position2s16(vertWidth, vertHeight);
850 GX_Color1u32(color);
851 GX_TexCoord2s16(1, 1);
852
853 GX_Position2s16(vertWidth, 0);
854 GX_Color1u32(color);
855 GX_TexCoord2s16(1, 0);
856
857 GX_Position2s16(0, 0);
858 GX_Color1u32(color);
859 GX_TexCoord2s16(0, 0);
860 GX_End();
861}
862
863uint16_t _pollGameInput(struct mGUIRunner* runner) {
864 UNUSED(runner);
865 PAD_ScanPads();
866 u16 padkeys = PAD_ButtonsHeld(0);
867 WPAD_ScanPads();
868 u32 wiiPad = WPAD_ButtonsHeld(0);
869 u32 ext = 0;
870 WPAD_Probe(0, &ext);
871 uint16_t keys = mInputMapKeyBits(&runner->core->inputMap, GCN1_INPUT, padkeys, 0);
872 keys |= mInputMapKeyBits(&runner->core->inputMap, GCN2_INPUT, padkeys, 0);
873 keys |= mInputMapKeyBits(&runner->core->inputMap, WIIMOTE_INPUT, wiiPad, 0);
874
875 enum GBAKey angles = mInputMapAxis(&runner->core->inputMap, GCN1_INPUT, 0, PAD_StickX(0));
876 if (angles != GBA_KEY_NONE) {
877 keys |= 1 << angles;
878 }
879 angles = mInputMapAxis(&runner->core->inputMap, GCN1_INPUT, 1, PAD_StickY(0));
880 if (angles != GBA_KEY_NONE) {
881 keys |= 1 << angles;
882 }
883 if (ext == WPAD_EXP_CLASSIC) {
884 keys |= mInputMapKeyBits(&runner->core->inputMap, CLASSIC_INPUT, wiiPad, 0);
885 angles = mInputMapAxis(&runner->core->inputMap, CLASSIC_INPUT, 0, WPAD_StickX(0, 0));
886 if (angles != GBA_KEY_NONE) {
887 keys |= 1 << angles;
888 }
889 angles = mInputMapAxis(&runner->core->inputMap, CLASSIC_INPUT, 1, WPAD_StickY(0, 0));
890 if (angles != GBA_KEY_NONE) {
891 keys |= 1 << angles;
892 }
893 }
894
895 return keys;
896}
897
898void _incrementScreenMode(struct mGUIRunner* runner) {
899 UNUSED(runner);
900 int mode = screenMode | (filterMode << 1);
901 ++mode;
902 screenMode = mode % SM_MAX;
903 filterMode = (mode >> 1) % FM_MAX;
904 mCoreConfigSetUIntValue(&runner->config, "screenMode", screenMode);
905 mCoreConfigSetUIntValue(&runner->config, "filter", filterMode);
906 switch (filterMode) {
907 case FM_NEAREST:
908 case FM_LINEAR_2x:
909 default:
910 GX_InitTexObjFilterMode(&tex, GX_NEAR, GX_NEAR);
911 break;
912 case FM_LINEAR_1x:
913 GX_InitTexObjFilterMode(&tex, GX_LINEAR, GX_LINEAR);
914 break;
915 }
916}
917
918void _setRumble(struct mRumble* rumble, int enable) {
919 UNUSED(rumble);
920 WPAD_Rumble(0, enable);
921 if (enable) {
922 PAD_ControlMotor(0, PAD_MOTOR_RUMBLE);
923 } else {
924 PAD_ControlMotor(0, PAD_MOTOR_STOP);
925 }
926}
927
928void _sampleRotation(struct mRotationSource* source) {
929 UNUSED(source);
930 vec3w_t accel;
931 WPAD_Accel(0, &accel);
932 // These are swapped
933 tiltX = (0x1EA - accel.y) << 22;
934 tiltY = (0x1EA - accel.x) << 22;
935
936 // This doesn't seem to work at all with -TR remotes
937 struct expansion_t exp;
938 WPAD_Expansion(0, &exp);
939 if (exp.type != EXP_MOTION_PLUS) {
940 return;
941 }
942 gyroZ = exp.mp.rz - 0x1FA0;
943 gyroZ <<= 18;
944}
945
946int32_t _readTiltX(struct mRotationSource* source) {
947 UNUSED(source);
948 return tiltX;
949}
950
951int32_t _readTiltY(struct mRotationSource* source) {
952 UNUSED(source);
953 return tiltY;
954}
955
956int32_t _readGyroZ(struct mRotationSource* source) {
957 UNUSED(source);
958 return gyroZ;
959}
960
961static s8 WPAD_StickX(u8 chan, u8 right) {
962 float mag = 0.0;
963 float ang = 0.0;
964 WPADData *data = WPAD_Data(chan);
965
966 switch (data->exp.type) {
967 case WPAD_EXP_NUNCHUK:
968 case WPAD_EXP_GUITARHERO3:
969 if (right == 0) {
970 mag = data->exp.nunchuk.js.mag;
971 ang = data->exp.nunchuk.js.ang;
972 }
973 break;
974 case WPAD_EXP_CLASSIC:
975 if (right == 0) {
976 mag = data->exp.classic.ljs.mag;
977 ang = data->exp.classic.ljs.ang;
978 } else {
979 mag = data->exp.classic.rjs.mag;
980 ang = data->exp.classic.rjs.ang;
981 }
982 break;
983 default:
984 break;
985 }
986
987 /* calculate X value (angle need to be converted into radian) */
988 if (mag > 1.0) {
989 mag = 1.0;
990 } else if (mag < -1.0) {
991 mag = -1.0;
992 }
993 double val = mag * sinf(M_PI * ang / 180.0f);
994
995 return (s8)(val * 128.0f);
996}
997
998static s8 WPAD_StickY(u8 chan, u8 right) {
999 float mag = 0.0;
1000 float ang = 0.0;
1001 WPADData *data = WPAD_Data(chan);
1002
1003 switch (data->exp.type) {
1004 case WPAD_EXP_NUNCHUK:
1005 case WPAD_EXP_GUITARHERO3:
1006 if (right == 0) {
1007 mag = data->exp.nunchuk.js.mag;
1008 ang = data->exp.nunchuk.js.ang;
1009 }
1010 break;
1011 case WPAD_EXP_CLASSIC:
1012 if (right == 0) {
1013 mag = data->exp.classic.ljs.mag;
1014 ang = data->exp.classic.ljs.ang;
1015 } else {
1016 mag = data->exp.classic.rjs.mag;
1017 ang = data->exp.classic.rjs.ang;
1018 }
1019 break;
1020 default:
1021 break;
1022 }
1023
1024 /* calculate X value (angle need to be converted into radian) */
1025 if (mag > 1.0) {
1026 mag = 1.0;
1027 } else if (mag < -1.0) {
1028 mag = -1.0;
1029 }
1030 double val = mag * cosf(M_PI * ang / 180.0f);
1031
1032 return (s8)(val * 128.0f);
1033}
1034
1035void _retraceCallback(u32 count) {
1036 u32 level = 0;
1037 _CPU_ISR_Disable(level);
1038 retraceCount = count;
1039 _CPU_ISR_Restore(level);
1040}