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 runner->core->setRotation(runner->core, &rotation);
525 runner->core->setRumble(runner->core, &rumble);
526
527 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_A, GBA_KEY_A);
528 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_B, GBA_KEY_B);
529 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_START, GBA_KEY_START);
530 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_X, GBA_KEY_SELECT);
531 _mapKey(&runner->core->inputMap, GCN2_INPUT, PAD_BUTTON_Y, GBA_KEY_SELECT);
532 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_UP, GBA_KEY_UP);
533 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_DOWN, GBA_KEY_DOWN);
534 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_LEFT, GBA_KEY_LEFT);
535 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_RIGHT, GBA_KEY_RIGHT);
536 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_TRIGGER_L, GBA_KEY_L);
537 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_TRIGGER_R, GBA_KEY_R);
538
539 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_2, GBA_KEY_A);
540 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_1, GBA_KEY_B);
541 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_PLUS, GBA_KEY_START);
542 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_MINUS, GBA_KEY_SELECT);
543 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_RIGHT, GBA_KEY_UP);
544 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_LEFT, GBA_KEY_DOWN);
545 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_UP, GBA_KEY_LEFT);
546 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_DOWN, GBA_KEY_RIGHT);
547 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_B, GBA_KEY_L);
548 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_A, GBA_KEY_R);
549
550 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_A, GBA_KEY_A);
551 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_B, GBA_KEY_B);
552 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_PLUS, GBA_KEY_START);
553 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_MINUS, GBA_KEY_SELECT);
554 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_UP, GBA_KEY_UP);
555 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_DOWN, GBA_KEY_DOWN);
556 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_LEFT, GBA_KEY_LEFT);
557 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_RIGHT, GBA_KEY_RIGHT);
558 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_FULL_L, GBA_KEY_L);
559 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_FULL_R, GBA_KEY_R);
560
561 struct mInputAxis desc = { GBA_KEY_RIGHT, GBA_KEY_LEFT, 0x20, -0x20 };
562 mInputBindAxis(&runner->core->inputMap, GCN1_INPUT, 0, &desc);
563 mInputBindAxis(&runner->core->inputMap, CLASSIC_INPUT, 0, &desc);
564 desc = (struct mInputAxis) { GBA_KEY_UP, GBA_KEY_DOWN, 0x20, -0x20 };
565 mInputBindAxis(&runner->core->inputMap, GCN1_INPUT, 1, &desc);
566 mInputBindAxis(&runner->core->inputMap, CLASSIC_INPUT, 1, &desc);
567
568 outputBuffer = memalign(32, 256 * 256 * BYTES_PER_PIXEL);
569 runner->core->setVideoBuffer(runner->core, outputBuffer, 256);
570
571 runner->core->setAudioBufferSize(runner->core, SAMPLES);
572
573 double ratio = GBAAudioCalculateRatio(1, 60 / 1.001, 1);
574 blip_set_rates(runner->core->getAudioChannel(runner->core, 0), runner->core->frequency(runner->core), 48000 * ratio);
575 blip_set_rates(runner->core->getAudioChannel(runner->core, 1), runner->core->frequency(runner->core), 48000 * ratio);
576}
577
578void _gameUnloaded(struct mGUIRunner* runner) {
579 UNUSED(runner);
580 AUDIO_StopDMA();
581}
582
583void _gameLoaded(struct mGUIRunner* runner) {
584 reconfigureScreen(runner->core, vmode);
585 if (runner->core->platform(runner->core) == PLATFORM_GBA && ((struct GBA*) runner->core->board)->memory.hw.devices & HW_GYRO) {
586 int i;
587 for (i = 0; i < 6; ++i) {
588 u32 result = WPAD_SetMotionPlus(0, 1);
589 if (result == WPAD_ERR_NONE) {
590 break;
591 }
592 sleep(1);
593 }
594 }
595 _unpaused(runner);
596}
597
598void _unpaused(struct mGUIRunner* runner) {
599 u32 level = 0;
600 _CPU_ISR_Disable(level);
601 referenceRetraceCount = retraceCount;
602 _CPU_ISR_Restore(level);
603
604 unsigned mode;
605 if (mCoreConfigGetUIntValue(&runner->core->config, "screenMode", &mode) && mode < SM_MAX) {
606 screenMode = mode;
607 }
608 if (mCoreConfigGetUIntValue(&runner->core->config, "filter", &mode) && mode < FM_MAX) {
609 switch (mode) {
610 case FM_NEAREST:
611 default:
612 GX_InitTexObjFilterMode(&tex, GX_NEAR, GX_NEAR);
613 break;
614 case FM_LINEAR:
615 GX_InitTexObjFilterMode(&tex, GX_LINEAR, GX_LINEAR);
616 break;
617 }
618 }
619 _guiFinish();
620}
621
622void _drawFrame(struct mGUIRunner* runner, bool faded) {
623 int available = blip_samples_avail(runner->core->getAudioChannel(runner->core, 0));
624 if (available + audioBufferSize > SAMPLES) {
625 available = SAMPLES - audioBufferSize;
626 }
627 available &= ~((32 / sizeof(struct GBAStereoSample)) - 1); // Force align to 32 bytes
628 if (available > 0) {
629 // These appear to be reversed for AUDIO_InitDMA
630 blip_read_samples(runner->core->getAudioChannel(runner->core, 0), &audioBuffer[currentAudioBuffer][audioBufferSize].right, available, true);
631 blip_read_samples(runner->core->getAudioChannel(runner->core, 1), &audioBuffer[currentAudioBuffer][audioBufferSize].left, available, true);
632 audioBufferSize += available;
633 }
634 if (audioBufferSize == SAMPLES && !AUDIO_GetDMAEnableFlag()) {
635 _audioDMA();
636 AUDIO_StartDMA();
637 }
638
639 uint32_t color = 0xFFFFFF3F;
640 if (!faded) {
641 color |= 0xC0;
642 }
643 size_t x, y;
644 uint64_t* texdest = (uint64_t*) texmem;
645 uint64_t* texsrc = (uint64_t*) outputBuffer;
646 for (y = 0; y < coreh; y += 4) {
647 for (x = 0; x < corew >> 2; ++x) {
648 texdest[0 + x * 4 + y * 64] = texsrc[0 + x + y * 64];
649 texdest[1 + x * 4 + y * 64] = texsrc[64 + x + y * 64];
650 texdest[2 + x * 4 + y * 64] = texsrc[128 + x + y * 64];
651 texdest[3 + x * 4 + y * 64] = texsrc[192 + x + y * 64];
652 }
653 }
654 DCFlushRange(texdest, 256 * 256 * BYTES_PER_PIXEL);
655
656 if (faded) {
657 GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_NOOP);
658 } else {
659 GX_SetBlendMode(GX_BM_NONE, GX_BL_ONE, GX_BL_ZERO, GX_LO_NOOP);
660 }
661 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0);
662 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
663 GX_InvalidateTexAll();
664 GX_LoadTexObj(&tex, GX_TEXMAP0);
665
666 s16 vertSize = 256;
667 if (screenMode == SM_PA) {
668 vertSize *= scaleFactor;
669 }
670
671 GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
672 GX_Position2s16(0, vertSize);
673 GX_Color1u32(color);
674 GX_TexCoord2s16(0, 1);
675
676 GX_Position2s16(vertSize, vertSize);
677 GX_Color1u32(color);
678 GX_TexCoord2s16(1, 1);
679
680 GX_Position2s16(vertSize, 0);
681 GX_Color1u32(color);
682 GX_TexCoord2s16(1, 0);
683
684 GX_Position2s16(0, 0);
685 GX_Color1u32(color);
686 GX_TexCoord2s16(0, 0);
687 GX_End();
688}
689
690uint16_t _pollGameInput(struct mGUIRunner* runner) {
691 UNUSED(runner);
692 PAD_ScanPads();
693 u16 padkeys = PAD_ButtonsHeld(0);
694 WPAD_ScanPads();
695 u32 wiiPad = WPAD_ButtonsHeld(0);
696 u32 ext = 0;
697 WPAD_Probe(0, &ext);
698 uint16_t keys = mInputMapKeyBits(&runner->core->inputMap, GCN1_INPUT, padkeys, 0);
699 keys |= mInputMapKeyBits(&runner->core->inputMap, GCN2_INPUT, padkeys, 0);
700 keys |= mInputMapKeyBits(&runner->core->inputMap, WIIMOTE_INPUT, wiiPad, 0);
701
702 enum GBAKey angles = mInputMapAxis(&runner->core->inputMap, GCN1_INPUT, 0, PAD_StickX(0));
703 if (angles != GBA_KEY_NONE) {
704 keys |= 1 << angles;
705 }
706 angles = mInputMapAxis(&runner->core->inputMap, GCN1_INPUT, 1, PAD_StickY(0));
707 if (angles != GBA_KEY_NONE) {
708 keys |= 1 << angles;
709 }
710 if (ext == WPAD_EXP_CLASSIC) {
711 keys |= mInputMapKeyBits(&runner->core->inputMap, CLASSIC_INPUT, wiiPad, 0);
712 angles = mInputMapAxis(&runner->core->inputMap, CLASSIC_INPUT, 0, WPAD_StickX(0, 0));
713 if (angles != GBA_KEY_NONE) {
714 keys |= 1 << angles;
715 }
716 angles = mInputMapAxis(&runner->core->inputMap, CLASSIC_INPUT, 1, WPAD_StickY(0, 0));
717 if (angles != GBA_KEY_NONE) {
718 keys |= 1 << angles;
719 }
720 }
721
722 return keys;
723}
724
725void _setRumble(struct mRumble* rumble, int enable) {
726 UNUSED(rumble);
727 WPAD_Rumble(0, enable);
728 if (enable) {
729 PAD_ControlMotor(0, PAD_MOTOR_RUMBLE);
730 } else {
731 PAD_ControlMotor(0, PAD_MOTOR_STOP);
732 }
733}
734
735void _sampleRotation(struct mRotationSource* source) {
736 UNUSED(source);
737 vec3w_t accel;
738 WPAD_Accel(0, &accel);
739 // These are swapped
740 tiltX = (0x1EA - accel.y) << 22;
741 tiltY = (0x1EA - accel.x) << 22;
742
743 // This doesn't seem to work at all with -TR remotes
744 struct expansion_t exp;
745 WPAD_Expansion(0, &exp);
746 if (exp.type != EXP_MOTION_PLUS) {
747 return;
748 }
749 gyroZ = exp.mp.rz - 0x1FA0;
750 gyroZ <<= 18;
751}
752
753int32_t _readTiltX(struct mRotationSource* source) {
754 UNUSED(source);
755 return tiltX;
756}
757
758int32_t _readTiltY(struct mRotationSource* source) {
759 UNUSED(source);
760 return tiltY;
761}
762
763int32_t _readGyroZ(struct mRotationSource* source) {
764 UNUSED(source);
765 return gyroZ;
766}
767
768static s8 WPAD_StickX(u8 chan, u8 right) {
769 float mag = 0.0;
770 float ang = 0.0;
771 WPADData *data = WPAD_Data(chan);
772
773 switch (data->exp.type) {
774 case WPAD_EXP_NUNCHUK:
775 case WPAD_EXP_GUITARHERO3:
776 if (right == 0) {
777 mag = data->exp.nunchuk.js.mag;
778 ang = data->exp.nunchuk.js.ang;
779 }
780 break;
781 case WPAD_EXP_CLASSIC:
782 if (right == 0) {
783 mag = data->exp.classic.ljs.mag;
784 ang = data->exp.classic.ljs.ang;
785 } else {
786 mag = data->exp.classic.rjs.mag;
787 ang = data->exp.classic.rjs.ang;
788 }
789 break;
790 default:
791 break;
792 }
793
794 /* calculate X value (angle need to be converted into radian) */
795 if (mag > 1.0) {
796 mag = 1.0;
797 } else if (mag < -1.0) {
798 mag = -1.0;
799 }
800 double val = mag * sinf(M_PI * ang / 180.0f);
801
802 return (s8)(val * 128.0f);
803}
804
805static s8 WPAD_StickY(u8 chan, u8 right) {
806 float mag = 0.0;
807 float ang = 0.0;
808 WPADData *data = WPAD_Data(chan);
809
810 switch (data->exp.type) {
811 case WPAD_EXP_NUNCHUK:
812 case WPAD_EXP_GUITARHERO3:
813 if (right == 0) {
814 mag = data->exp.nunchuk.js.mag;
815 ang = data->exp.nunchuk.js.ang;
816 }
817 break;
818 case WPAD_EXP_CLASSIC:
819 if (right == 0) {
820 mag = data->exp.classic.ljs.mag;
821 ang = data->exp.classic.ljs.ang;
822 } else {
823 mag = data->exp.classic.rjs.mag;
824 ang = data->exp.classic.rjs.ang;
825 }
826 break;
827 default:
828 break;
829 }
830
831 /* calculate X value (angle need to be converted into radian) */
832 if (mag > 1.0) {
833 mag = 1.0;
834 } else if (mag < -1.0) {
835 mag = -1.0;
836 }
837 double val = mag * cosf(M_PI * ang / 180.0f);
838
839 return (s8)(val * 128.0f);
840}
841
842void _retraceCallback(u32 count) {
843 u32 level = 0;
844 _CPU_ISR_Disable(level);
845 retraceCount = count;
846 _CPU_ISR_Restore(level);
847}