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