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