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