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