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