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;
91
92static void* framebuffer[2] = { 0, 0 };
93static int whichFb = 0;
94
95static struct GBAStereoSample audioBuffer[3][SAMPLES] __attribute__((__aligned__(32)));
96static volatile size_t audioBufferSize = 0;
97static volatile int currentAudioBuffer = 0;
98
99static struct GUIFont* font;
100
101static void reconfigureScreen(struct mCore* core, GXRModeObj* vmode) {
102 free(framebuffer[0]);
103 free(framebuffer[1]);
104
105 framebuffer[0] = SYS_AllocateFramebuffer(vmode);
106 framebuffer[1] = SYS_AllocateFramebuffer(vmode);
107
108 VIDEO_SetBlack(true);
109 VIDEO_Configure(vmode);
110 VIDEO_SetNextFramebuffer(framebuffer[whichFb]);
111 VIDEO_SetBlack(false);
112 VIDEO_Flush();
113 VIDEO_WaitVSync();
114 if (vmode->viTVMode & VI_NON_INTERLACE) {
115 VIDEO_WaitVSync();
116 }
117 GX_SetViewport(0, 0, vmode->fbWidth, vmode->efbHeight, 0, 1);
118
119 f32 yscale = GX_GetYScaleFactor(vmode->efbHeight, vmode->xfbHeight);
120 u32 xfbHeight = GX_SetDispCopyYScale(yscale);
121 GX_SetScissor(0, 0, vmode->viWidth, vmode->viWidth);
122 GX_SetDispCopySrc(0, 0, vmode->fbWidth, vmode->efbHeight);
123 GX_SetDispCopyDst(vmode->fbWidth, xfbHeight);
124 GX_SetCopyFilter(vmode->aa, vmode->sample_pattern, GX_TRUE, vmode->vfilter);
125 GX_SetFieldMode(vmode->field_rendering, ((vmode->viHeight == 2 * vmode->xfbHeight) ? GX_ENABLE : GX_DISABLE));
126
127 if (core) {
128 unsigned width = VIDEO_HORIZONTAL_PIXELS;
129 unsigned height = VIDEO_VERTICAL_PIXELS;
130 core->desiredVideoDimensions(core, &width, &height);
131 int hfactor = vmode->fbWidth / width;
132 int vfactor = vmode->efbHeight / height;
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 .pollGameInput = _pollGameInput
366 };
367 mGUIInit(&runner, "wii");
368 if (argc > 1) {
369 mGUIRun(&runner, argv[1]);
370 } else {
371 mGUIRunloop(&runner);
372 }
373 mGUIDeinit(&runner);
374
375 free(fifo);
376
377 free(outputBuffer);
378 GUIFontDestroy(font);
379
380 free(framebuffer[0]);
381 free(framebuffer[1]);
382
383 return 0;
384}
385
386static void _audioDMA(void) {
387 if (!audioBufferSize) {
388 return;
389 }
390 DCFlushRange(audioBuffer[currentAudioBuffer], audioBufferSize * sizeof(struct GBAStereoSample));
391 AUDIO_InitDMA((u32) audioBuffer[currentAudioBuffer], audioBufferSize * sizeof(struct GBAStereoSample));
392 currentAudioBuffer = (currentAudioBuffer + 1) % 3;
393 audioBufferSize = 0;
394}
395
396static void _drawStart(void) {
397 u32 level = 0;
398 _CPU_ISR_Disable(level);
399 if (referenceRetraceCount >= retraceCount) {
400 VIDEO_WaitVSync();
401 }
402 _CPU_ISR_Restore(level);
403
404 GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE);
405 GX_SetColorUpdate(GX_TRUE);
406
407 GX_SetViewport(0, 0, vmode->fbWidth, vmode->efbHeight, 0, 1);
408}
409
410static void _drawEnd(void) {
411 whichFb = !whichFb;
412
413 GX_CopyDisp(framebuffer[whichFb], GX_TRUE);
414 GX_DrawDone();
415 VIDEO_SetNextFramebuffer(framebuffer[whichFb]);
416 VIDEO_Flush();
417
418 u32 level = 0;
419 _CPU_ISR_Disable(level);
420 ++referenceRetraceCount;
421 _CPU_ISR_Restore(level);
422}
423
424static uint32_t _pollInput(void) {
425 PAD_ScanPads();
426 u16 padkeys = PAD_ButtonsHeld(0);
427
428 WPAD_ScanPads();
429 u32 wiiPad = WPAD_ButtonsHeld(0);
430 u32 ext = 0;
431 WPAD_Probe(0, &ext);
432
433 int keys = 0;
434 int x = PAD_StickX(0);
435 int y = PAD_StickY(0);
436 int w_x = WPAD_StickX(0, 0);
437 int w_y = WPAD_StickY(0, 0);
438 if (x < -0x20 || w_x < -0x20) {
439 keys |= 1 << GUI_INPUT_LEFT;
440 }
441 if (x > 0x20 || w_x > 0x20) {
442 keys |= 1 << GUI_INPUT_RIGHT;
443 }
444 if (y < -0x20 || w_y <- 0x20) {
445 keys |= 1 << GUI_INPUT_DOWN;
446 }
447 if (y > 0x20 || w_y > 0x20) {
448 keys |= 1 << GUI_INPUT_UP;
449 }
450 if ((padkeys & PAD_BUTTON_A) || (wiiPad & WPAD_BUTTON_2) ||
451 ((ext == WPAD_EXP_CLASSIC) && (wiiPad & (WPAD_CLASSIC_BUTTON_A | WPAD_CLASSIC_BUTTON_Y)))) {
452 keys |= 1 << GUI_INPUT_SELECT;
453 }
454 if ((padkeys & PAD_BUTTON_B) || (wiiPad & WPAD_BUTTON_1) || (wiiPad & WPAD_BUTTON_B) ||
455 ((ext == WPAD_EXP_CLASSIC) && (wiiPad & (WPAD_CLASSIC_BUTTON_B | WPAD_CLASSIC_BUTTON_X)))) {
456 keys |= 1 << GUI_INPUT_BACK;
457 }
458 if ((padkeys & PAD_TRIGGER_Z) || (wiiPad & WPAD_BUTTON_HOME) ||
459 ((ext == WPAD_EXP_CLASSIC) && (wiiPad & (WPAD_CLASSIC_BUTTON_HOME)))) {
460 keys |= 1 << GUI_INPUT_CANCEL;
461 }
462 if ((padkeys & PAD_BUTTON_LEFT)|| (wiiPad & WPAD_BUTTON_UP) ||
463 ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_LEFT))) {
464 keys |= 1 << GUI_INPUT_LEFT;
465 }
466 if ((padkeys & PAD_BUTTON_RIGHT) || (wiiPad & WPAD_BUTTON_DOWN) ||
467 ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_RIGHT))) {
468 keys |= 1 << GUI_INPUT_RIGHT;
469 }
470 if ((padkeys & PAD_BUTTON_UP) || (wiiPad & WPAD_BUTTON_RIGHT) ||
471 ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_UP))) {
472 keys |= 1 << GUI_INPUT_UP;
473 }
474 if ((padkeys & PAD_BUTTON_DOWN) || (wiiPad & WPAD_BUTTON_LEFT) ||
475 ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_DOWN))) {
476 keys |= 1 << GUI_INPUT_DOWN;
477 }
478 return keys;
479}
480
481static enum GUICursorState _pollCursor(unsigned* x, unsigned* y) {
482 ir_t ir;
483 WPAD_IR(0, &ir);
484 if (!ir.smooth_valid) {
485 return GUI_CURSOR_NOT_PRESENT;
486 }
487 *x = ir.sx;
488 *y = ir.sy;
489 WPAD_ScanPads();
490 u32 wiiPad = WPAD_ButtonsHeld(0);
491 if (wiiPad & WPAD_BUTTON_A) {
492 return GUI_CURSOR_DOWN;
493 }
494 return GUI_CURSOR_UP;
495}
496
497void _reproj(int w, int h) {
498 Mtx44 proj;
499 int top = (vmode->efbHeight - h) / 2;
500 int left = (vmode->fbWidth - w) / 2;
501 guOrtho(proj, -top, top + h, -left, left + w, 0, 300);
502 GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
503}
504
505void _reproj2(int w, int h) {
506 Mtx44 proj;
507 s16 top = 20;
508 guOrtho(proj, -top, top + h, 0, w, 0, 300);
509 GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
510}
511
512void _guiPrepare(void) {
513 _reproj2(vmode->fbWidth * GUI_SCALE, vmode->efbHeight * GUI_SCALE);
514}
515
516void _guiFinish(void) {
517 if (screenMode == SM_PA) {
518 _reproj(VIDEO_HORIZONTAL_PIXELS * scaleFactor, VIDEO_VERTICAL_PIXELS * scaleFactor);
519 } else {
520 _reproj2(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
521 }
522}
523
524void _setup(struct mGUIRunner* runner) {
525 ((struct GBA*) runner->core->board)->rumble = &rumble;
526 ((struct GBA*) runner->core->board)->rotationSource = &rotation;
527
528 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_A, GBA_KEY_A);
529 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_B, GBA_KEY_B);
530 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_START, GBA_KEY_START);
531 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_X, GBA_KEY_SELECT);
532 _mapKey(&runner->core->inputMap, GCN2_INPUT, PAD_BUTTON_Y, GBA_KEY_SELECT);
533 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_UP, GBA_KEY_UP);
534 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_DOWN, GBA_KEY_DOWN);
535 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_LEFT, GBA_KEY_LEFT);
536 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_RIGHT, GBA_KEY_RIGHT);
537 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_TRIGGER_L, GBA_KEY_L);
538 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_TRIGGER_R, GBA_KEY_R);
539
540 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_2, GBA_KEY_A);
541 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_1, GBA_KEY_B);
542 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_PLUS, GBA_KEY_START);
543 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_MINUS, GBA_KEY_SELECT);
544 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_RIGHT, GBA_KEY_UP);
545 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_LEFT, GBA_KEY_DOWN);
546 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_UP, GBA_KEY_LEFT);
547 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_DOWN, GBA_KEY_RIGHT);
548 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_B, GBA_KEY_L);
549 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_A, GBA_KEY_R);
550
551 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_A, GBA_KEY_A);
552 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_B, GBA_KEY_B);
553 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_PLUS, GBA_KEY_START);
554 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_MINUS, GBA_KEY_SELECT);
555 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_UP, GBA_KEY_UP);
556 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_DOWN, GBA_KEY_DOWN);
557 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_LEFT, GBA_KEY_LEFT);
558 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_RIGHT, GBA_KEY_RIGHT);
559 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_FULL_L, GBA_KEY_L);
560 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_FULL_R, GBA_KEY_R);
561
562 struct mInputAxis desc = { GBA_KEY_RIGHT, GBA_KEY_LEFT, 0x20, -0x20 };
563 mInputBindAxis(&runner->core->inputMap, GCN1_INPUT, 0, &desc);
564 mInputBindAxis(&runner->core->inputMap, CLASSIC_INPUT, 0, &desc);
565 desc = (struct mInputAxis) { GBA_KEY_UP, GBA_KEY_DOWN, 0x20, -0x20 };
566 mInputBindAxis(&runner->core->inputMap, GCN1_INPUT, 1, &desc);
567 mInputBindAxis(&runner->core->inputMap, CLASSIC_INPUT, 1, &desc);
568
569 outputBuffer = memalign(32, 256 * 256 * BYTES_PER_PIXEL);
570 runner->core->setVideoBuffer(runner->core, outputBuffer, 256);
571
572 runner->core->setAudioBufferSize(runner->core, SAMPLES);
573
574 double ratio = GBAAudioCalculateRatio(1, 60 / 1.001, 1);
575 blip_set_rates(runner->core->getAudioChannel(runner->core, 0), GBA_ARM7TDMI_FREQUENCY, 48000 * ratio);
576 blip_set_rates(runner->core->getAudioChannel(runner->core, 1), GBA_ARM7TDMI_FREQUENCY, 48000 * ratio);
577}
578
579void _gameUnloaded(struct mGUIRunner* runner) {
580 UNUSED(runner);
581 AUDIO_StopDMA();
582}
583
584void _gameLoaded(struct mGUIRunner* runner) {
585 reconfigureScreen(runner->core, vmode);
586 if (((struct GBA*) runner->core->board)->memory.hw.devices & HW_GYRO) {
587 int i;
588 for (i = 0; i < 6; ++i) {
589 u32 result = WPAD_SetMotionPlus(0, 1);
590 if (result == WPAD_ERR_NONE) {
591 break;
592 }
593 sleep(1);
594 }
595 }
596 _unpaused(runner);
597}
598
599void _unpaused(struct mGUIRunner* runner) {
600 u32 level = 0;
601 _CPU_ISR_Disable(level);
602 referenceRetraceCount = retraceCount;
603 _CPU_ISR_Restore(level);
604
605 unsigned mode;
606 if (mCoreConfigGetUIntValue(&runner->core->config, "screenMode", &mode) && mode < SM_MAX) {
607 screenMode = mode;
608 }
609 if (mCoreConfigGetUIntValue(&runner->core->config, "filter", &mode) && mode < FM_MAX) {
610 switch (mode) {
611 case FM_NEAREST:
612 default:
613 GX_InitTexObjFilterMode(&tex, GX_NEAR, GX_NEAR);
614 break;
615 case FM_LINEAR:
616 GX_InitTexObjFilterMode(&tex, GX_LINEAR, GX_LINEAR);
617 break;
618 }
619 }
620 _guiFinish();
621}
622
623void _drawFrame(struct mGUIRunner* runner, bool faded) {
624 int available = blip_samples_avail(runner->core->getAudioChannel(runner->core, 0));
625 if (available + audioBufferSize > SAMPLES) {
626 available = SAMPLES - audioBufferSize;
627 }
628 available &= ~((32 / sizeof(struct GBAStereoSample)) - 1); // Force align to 32 bytes
629 if (available > 0) {
630 // These appear to be reversed for AUDIO_InitDMA
631 blip_read_samples(runner->core->getAudioChannel(runner->core, 0), &audioBuffer[currentAudioBuffer][audioBufferSize].right, available, true);
632 blip_read_samples(runner->core->getAudioChannel(runner->core, 1), &audioBuffer[currentAudioBuffer][audioBufferSize].left, available, true);
633 audioBufferSize += available;
634 }
635 if (audioBufferSize == SAMPLES && !AUDIO_GetDMAEnableFlag()) {
636 _audioDMA();
637 AUDIO_StartDMA();
638 }
639
640 uint32_t color = 0xFFFFFF3F;
641 if (!faded) {
642 color |= 0xC0;
643 }
644 size_t x, y;
645 uint64_t* texdest = (uint64_t*) texmem;
646 uint64_t* texsrc = (uint64_t*) outputBuffer;
647 for (y = 0; y < VIDEO_VERTICAL_PIXELS; y += 4) {
648 for (x = 0; x < VIDEO_HORIZONTAL_PIXELS >> 2; ++x) {
649 texdest[0 + x * 4 + y * 64] = texsrc[0 + x + y * 64];
650 texdest[1 + x * 4 + y * 64] = texsrc[64 + x + y * 64];
651 texdest[2 + x * 4 + y * 64] = texsrc[128 + x + y * 64];
652 texdest[3 + x * 4 + y * 64] = texsrc[192 + x + y * 64];
653 }
654 }
655 DCFlushRange(texdest, 256 * 256 * BYTES_PER_PIXEL);
656
657 if (faded) {
658 GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_NOOP);
659 } else {
660 GX_SetBlendMode(GX_BM_NONE, GX_BL_ONE, GX_BL_ZERO, GX_LO_NOOP);
661 }
662 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0);
663 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
664 GX_InvalidateTexAll();
665 GX_LoadTexObj(&tex, GX_TEXMAP0);
666
667 s16 vertSize = 256;
668 if (screenMode == SM_PA) {
669 vertSize *= scaleFactor;
670 }
671
672 GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
673 GX_Position2s16(0, vertSize);
674 GX_Color1u32(color);
675 GX_TexCoord2s16(0, 1);
676
677 GX_Position2s16(vertSize, vertSize);
678 GX_Color1u32(color);
679 GX_TexCoord2s16(1, 1);
680
681 GX_Position2s16(vertSize, 0);
682 GX_Color1u32(color);
683 GX_TexCoord2s16(1, 0);
684
685 GX_Position2s16(0, 0);
686 GX_Color1u32(color);
687 GX_TexCoord2s16(0, 0);
688 GX_End();
689}
690
691uint16_t _pollGameInput(struct mGUIRunner* runner) {
692 UNUSED(runner);
693 PAD_ScanPads();
694 u16 padkeys = PAD_ButtonsHeld(0);
695 WPAD_ScanPads();
696 u32 wiiPad = WPAD_ButtonsHeld(0);
697 u32 ext = 0;
698 WPAD_Probe(0, &ext);
699 uint16_t keys = mInputMapKeyBits(&runner->core->inputMap, GCN1_INPUT, padkeys, 0);
700 keys |= mInputMapKeyBits(&runner->core->inputMap, GCN2_INPUT, padkeys, 0);
701 keys |= mInputMapKeyBits(&runner->core->inputMap, WIIMOTE_INPUT, wiiPad, 0);
702
703 enum GBAKey angles = mInputMapAxis(&runner->core->inputMap, GCN1_INPUT, 0, PAD_StickX(0));
704 if (angles != GBA_KEY_NONE) {
705 keys |= 1 << angles;
706 }
707 angles = mInputMapAxis(&runner->core->inputMap, GCN1_INPUT, 1, PAD_StickY(0));
708 if (angles != GBA_KEY_NONE) {
709 keys |= 1 << angles;
710 }
711 if (ext == WPAD_EXP_CLASSIC) {
712 keys |= mInputMapKeyBits(&runner->core->inputMap, CLASSIC_INPUT, wiiPad, 0);
713 angles = mInputMapAxis(&runner->core->inputMap, CLASSIC_INPUT, 0, WPAD_StickX(0, 0));
714 if (angles != GBA_KEY_NONE) {
715 keys |= 1 << angles;
716 }
717 angles = mInputMapAxis(&runner->core->inputMap, CLASSIC_INPUT, 1, WPAD_StickY(0, 0));
718 if (angles != GBA_KEY_NONE) {
719 keys |= 1 << angles;
720 }
721 }
722
723 return keys;
724}
725
726void _setRumble(struct mRumble* rumble, int enable) {
727 UNUSED(rumble);
728 WPAD_Rumble(0, enable);
729 if (enable) {
730 PAD_ControlMotor(0, PAD_MOTOR_RUMBLE);
731 } else {
732 PAD_ControlMotor(0, PAD_MOTOR_STOP);
733 }
734}
735
736void _sampleRotation(struct mRotationSource* source) {
737 UNUSED(source);
738 vec3w_t accel;
739 WPAD_Accel(0, &accel);
740 // These are swapped
741 tiltX = (accel.y - 0x1EA) << 22;
742 tiltY = (accel.x - 0x1EA) << 22;
743
744 // This doesn't seem to work at all with -TR remotes
745 struct expansion_t exp;
746 WPAD_Expansion(0, &exp);
747 if (exp.type != EXP_MOTION_PLUS) {
748 return;
749 }
750 gyroZ = exp.mp.rz - 0x1FA0;
751 gyroZ <<= 18;
752}
753
754int32_t _readTiltX(struct mRotationSource* source) {
755 UNUSED(source);
756 return tiltX;
757}
758
759int32_t _readTiltY(struct mRotationSource* source) {
760 UNUSED(source);
761 return tiltY;
762}
763
764int32_t _readGyroZ(struct mRotationSource* source) {
765 UNUSED(source);
766 return gyroZ;
767}
768
769static s8 WPAD_StickX(u8 chan, u8 right) {
770 float mag = 0.0;
771 float ang = 0.0;
772 WPADData *data = WPAD_Data(chan);
773
774 switch (data->exp.type) {
775 case WPAD_EXP_NUNCHUK:
776 case WPAD_EXP_GUITARHERO3:
777 if (right == 0) {
778 mag = data->exp.nunchuk.js.mag;
779 ang = data->exp.nunchuk.js.ang;
780 }
781 break;
782 case WPAD_EXP_CLASSIC:
783 if (right == 0) {
784 mag = data->exp.classic.ljs.mag;
785 ang = data->exp.classic.ljs.ang;
786 } else {
787 mag = data->exp.classic.rjs.mag;
788 ang = data->exp.classic.rjs.ang;
789 }
790 break;
791 default:
792 break;
793 }
794
795 /* calculate X value (angle need to be converted into radian) */
796 if (mag > 1.0) {
797 mag = 1.0;
798 } else if (mag < -1.0) {
799 mag = -1.0;
800 }
801 double val = mag * sinf(M_PI * ang / 180.0f);
802
803 return (s8)(val * 128.0f);
804}
805
806static s8 WPAD_StickY(u8 chan, u8 right) {
807 float mag = 0.0;
808 float ang = 0.0;
809 WPADData *data = WPAD_Data(chan);
810
811 switch (data->exp.type) {
812 case WPAD_EXP_NUNCHUK:
813 case WPAD_EXP_GUITARHERO3:
814 if (right == 0) {
815 mag = data->exp.nunchuk.js.mag;
816 ang = data->exp.nunchuk.js.ang;
817 }
818 break;
819 case WPAD_EXP_CLASSIC:
820 if (right == 0) {
821 mag = data->exp.classic.ljs.mag;
822 ang = data->exp.classic.ljs.ang;
823 } else {
824 mag = data->exp.classic.rjs.mag;
825 ang = data->exp.classic.rjs.ang;
826 }
827 break;
828 default:
829 break;
830 }
831
832 /* calculate X value (angle need to be converted into radian) */
833 if (mag > 1.0) {
834 mag = 1.0;
835 } else if (mag < -1.0) {
836 mag = -1.0;
837 }
838 double val = mag * cosf(M_PI * ang / 180.0f);
839
840 return (s8)(val * 128.0f);
841}
842
843void _retraceCallback(u32 count) {
844 u32 level = 0;
845 _CPU_ISR_Disable(level);
846 retraceCount = count;
847 _CPU_ISR_Restore(level);
848}