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 <mgba-util/common.h>
16
17#include <mgba/core/blip_buf.h>
18#include <mgba/core/core.h>
19#include "feature/gui/gui-runner.h"
20#include <mgba/internal/gb/video.h>
21#include <mgba/internal/gba/audio.h>
22#include <mgba/internal/gba/gba.h>
23#include <mgba/internal/gba/input.h>
24#include <mgba-util/gui.h>
25#include <mgba-util/gui/file-select.h>
26#include <mgba-util/gui/font.h>
27#include <mgba-util/gui/menu.h>
28#include <mgba-util/memory.h>
29#include <mgba-util/vfs.h>
30
31#define GCN1_INPUT 0x47434E31
32#define GCN2_INPUT 0x47434E32
33#define WIIMOTE_INPUT 0x5749494D
34#define CLASSIC_INPUT 0x57494943
35
36#define TEX_W 256
37#define TEX_H 224
38
39#define ANALOG_DEADZONE 0x30
40
41static void _mapKey(struct mInputMap* map, uint32_t binding, int nativeKey, enum GBAKey key) {
42 mInputBindKey(map, binding, __builtin_ctz(nativeKey), key);
43}
44
45static enum ScreenMode {
46 SM_PA,
47 SM_SF,
48 SM_MAX
49} screenMode = SM_PA;
50
51static enum FilterMode {
52 FM_NEAREST,
53 FM_LINEAR_1x,
54 FM_LINEAR_2x,
55 FM_MAX
56} filterMode = FM_NEAREST;
57
58static enum VideoMode {
59 VM_AUTODETECT,
60 VM_480i,
61 VM_480p,
62 VM_240p,
63 // TODO: PAL support
64 VM_MAX
65} videoMode = VM_AUTODETECT;
66
67#define SAMPLES 1024
68#define GUI_SCALE 1.35f
69#define GUI_SCALE_240p 2.0f
70
71static void _retraceCallback(u32 count);
72
73static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* right);
74static void _audioDMA(void);
75static void _setRumble(struct mRumble* rumble, int enable);
76static void _sampleRotation(struct mRotationSource* source);
77static int32_t _readTiltX(struct mRotationSource* source);
78static int32_t _readTiltY(struct mRotationSource* source);
79static int32_t _readGyroZ(struct mRotationSource* source);
80
81static void _drawStart(void);
82static void _drawEnd(void);
83static uint32_t _pollInput(const struct mInputMap*);
84static enum GUICursorState _pollCursor(unsigned* x, unsigned* y);
85static void _guiPrepare(void);
86
87static void _setup(struct mGUIRunner* runner);
88static void _gameLoaded(struct mGUIRunner* runner);
89static void _gameUnloaded(struct mGUIRunner* runner);
90static void _unpaused(struct mGUIRunner* runner);
91static void _prepareForFrame(struct mGUIRunner* runner);
92static void _drawFrame(struct mGUIRunner* runner, bool faded);
93static uint16_t _pollGameInput(struct mGUIRunner* runner);
94static void _setFrameLimiter(struct mGUIRunner* runner, bool limit);
95static void _incrementScreenMode(struct mGUIRunner* runner);
96
97static s8 WPAD_StickX(u8 chan, u8 right);
98static s8 WPAD_StickY(u8 chan, u8 right);
99
100static void* outputBuffer;
101static struct mAVStream stream;
102static struct mRumble rumble;
103static struct mRotationSource rotation;
104static GXRModeObj* vmode;
105static float wAdjust;
106static float hAdjust;
107static float wStretch = 0.9f;
108static float hStretch = 0.9f;
109static float guiScale = GUI_SCALE;
110static Mtx model, view, modelview;
111static uint16_t* texmem;
112static GXTexObj tex;
113static uint16_t* rescaleTexmem;
114static GXTexObj rescaleTex;
115static uint16_t* interframeTexmem;
116static GXTexObj interframeTex;
117static bool sgbCrop = false;
118static int32_t tiltX;
119static int32_t tiltY;
120static int32_t gyroZ;
121static uint32_t retraceCount;
122static uint32_t referenceRetraceCount;
123static bool frameLimiter = true;
124static int scaleFactor;
125static unsigned corew, coreh;
126static bool interframeBlending = true;
127
128uint32_t* romBuffer;
129size_t romBufferSize;
130
131static void* framebuffer[2] = { 0, 0 };
132static int whichFb = 0;
133
134static struct GBAStereoSample audioBuffer[3][SAMPLES] __attribute__((__aligned__(32)));
135static volatile size_t audioBufferSize = 0;
136static volatile int currentAudioBuffer = 0;
137static double audioSampleRate = 60.0 / 1.001;
138
139static struct GUIFont* font;
140
141static void reconfigureScreen(struct mGUIRunner* runner) {
142 if (runner) {
143 unsigned mode;
144 if (mCoreConfigGetUIntValue(&runner->config, "videoMode", &mode) && mode < VM_MAX) {
145 videoMode = mode;
146 }
147 }
148 wAdjust = 1.f;
149 hAdjust = 1.f;
150 guiScale = GUI_SCALE;
151 audioSampleRate = 60.0 / 1.001;
152
153 s32 signalMode = CONF_GetVideo();
154
155 switch (videoMode) {
156 case VM_AUTODETECT:
157 default:
158 vmode = VIDEO_GetPreferredMode(0);
159 break;
160 case VM_480i:
161 switch (signalMode) {
162 case CONF_VIDEO_NTSC:
163 vmode = &TVNtsc480IntDf;
164 break;
165 case CONF_VIDEO_MPAL:
166 vmode = &TVMpal480IntDf;
167 break;
168 case CONF_VIDEO_PAL:
169 vmode = &TVEurgb60Hz480IntDf;
170 break;
171 }
172 break;
173 case VM_480p:
174 switch (signalMode) {
175 case CONF_VIDEO_NTSC:
176 vmode = &TVNtsc480Prog;
177 break;
178 case CONF_VIDEO_MPAL:
179 vmode = &TVMpal480Prog;
180 break;
181 case CONF_VIDEO_PAL:
182 vmode = &TVEurgb60Hz480Prog;
183 break;
184 }
185 break;
186 case VM_240p:
187 switch (signalMode) {
188 case CONF_VIDEO_NTSC:
189 vmode = &TVNtsc240Ds;
190 break;
191 case CONF_VIDEO_MPAL:
192 vmode = &TVMpal240Ds;
193 break;
194 case CONF_VIDEO_PAL:
195 vmode = &TVEurgb60Hz240Ds;
196 break;
197 }
198 wAdjust = 0.5f;
199 audioSampleRate = 90.0 / 1.50436;
200 guiScale = GUI_SCALE_240p;
201 break;
202 }
203
204 vmode->viWidth = 704;
205 vmode->viXOrigin = 8;
206
207 VIDEO_SetBlack(true);
208 VIDEO_Configure(vmode);
209
210 free(framebuffer[0]);
211 free(framebuffer[1]);
212
213 framebuffer[0] = SYS_AllocateFramebuffer(vmode);
214 framebuffer[1] = SYS_AllocateFramebuffer(vmode);
215 VIDEO_ClearFrameBuffer(vmode, MEM_K0_TO_K1(framebuffer[0]), COLOR_BLACK);
216 VIDEO_ClearFrameBuffer(vmode, MEM_K0_TO_K1(framebuffer[1]), COLOR_BLACK);
217
218 VIDEO_SetNextFramebuffer(MEM_K0_TO_K1(framebuffer[whichFb]));
219 VIDEO_Flush();
220 VIDEO_WaitVSync();
221 if (vmode->viTVMode & VI_NON_INTERLACE) {
222 VIDEO_WaitVSync();
223 }
224 GX_SetViewport(0, 0, vmode->fbWidth, vmode->efbHeight, 0, 1);
225
226 f32 yscale = GX_GetYScaleFactor(vmode->efbHeight, vmode->xfbHeight);
227 u32 xfbHeight = GX_SetDispCopyYScale(yscale);
228 GX_SetScissor(0, 0, vmode->viWidth, vmode->viWidth);
229 GX_SetDispCopySrc(0, 0, vmode->fbWidth, vmode->efbHeight);
230 GX_SetDispCopyDst(vmode->fbWidth, xfbHeight);
231 GX_SetCopyFilter(vmode->aa, vmode->sample_pattern, GX_TRUE, vmode->vfilter);
232 GX_SetFieldMode(vmode->field_rendering, ((vmode->viHeight == 2 * vmode->xfbHeight) ? GX_ENABLE : GX_DISABLE));
233
234 if (runner) {
235 runner->params.width = vmode->fbWidth * guiScale * wAdjust;
236 runner->params.height = vmode->efbHeight * guiScale * hAdjust;
237 if (runner->core) {
238 double ratio = GBAAudioCalculateRatio(1, audioSampleRate, 1);
239 blip_set_rates(runner->core->getAudioChannel(runner->core, 0), runner->core->frequency(runner->core), 48000 * ratio);
240 blip_set_rates(runner->core->getAudioChannel(runner->core, 1), runner->core->frequency(runner->core), 48000 * ratio);
241 }
242 }
243}
244
245int main(int argc, char* argv[]) {
246 VIDEO_Init();
247 VIDEO_SetBlack(true);
248 VIDEO_Flush();
249 VIDEO_WaitVSync();
250 PAD_Init();
251 WPAD_Init();
252 WPAD_SetDataFormat(0, WPAD_FMT_BTNS_ACC_IR);
253 AUDIO_Init(0);
254 AUDIO_SetDSPSampleRate(AI_SAMPLERATE_48KHZ);
255 AUDIO_RegisterDMACallback(_audioDMA);
256
257 memset(audioBuffer, 0, sizeof(audioBuffer));
258#ifdef FIXED_ROM_BUFFER
259 romBufferSize = SIZE_CART0;
260 romBuffer = SYS_GetArena2Lo();
261 SYS_SetArena2Lo((void*)((intptr_t) romBuffer + SIZE_CART0));
262#endif
263
264#if !defined(COLOR_16_BIT) && !defined(COLOR_5_6_5)
265#error This pixel format is unsupported. Please use -DCOLOR_16-BIT -DCOLOR_5_6_5
266#endif
267
268 GXColor bg = { 0, 0, 0, 0xFF };
269 void* fifo = memalign(32, 0x40000);
270 memset(fifo, 0, 0x40000);
271 GX_Init(fifo, 0x40000);
272 GX_SetCopyClear(bg, 0x00FFFFFF);
273
274 GX_SetCullMode(GX_CULL_NONE);
275 GX_SetDispCopyGamma(GX_GM_1_0);
276
277 GX_ClearVtxDesc();
278 GX_SetVtxDesc(GX_VA_POS, GX_DIRECT);
279 GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT);
280 GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT);
281
282 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 0);
283 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0);
284 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
285
286 GX_SetNumTevStages(1);
287 GX_SetNumChans(1);
288 GX_SetNumTexGens(1);
289 GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
290 GX_SetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD0, GX_TEXMAP1, GX_COLOR0A0);
291 GX_SetTevOp(GX_TEVSTAGE0, GX_MODULATE);
292 GX_SetTevColorOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_DIVIDE_2, GX_TRUE, GX_TEVPREV);
293 GX_SetTevAlphaOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV);
294 GX_SetTevColorIn(GX_TEVSTAGE1, GX_CC_ZERO, GX_CC_TEXC, GX_CC_ONE, GX_CC_CPREV);
295 GX_SetTevAlphaIn(GX_TEVSTAGE1, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_APREV);
296
297 GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);
298 GX_InvVtxCache();
299 GX_InvalidateTexAll();
300
301 guVector cam = { 0.0f, 0.0f, 0.0f };
302 guVector up = { 0.0f, 1.0f, 0.0f };
303 guVector look = { 0.0f, 0.0f, -1.0f };
304 guLookAt(view, &cam, &up, &look);
305
306 guMtxIdentity(model);
307 guMtxTransApply(model, model, 0.0f, 0.0f, -6.0f);
308 guMtxConcat(view, model, modelview);
309 GX_LoadPosMtxImm(modelview, GX_PNMTX0);
310
311 texmem = memalign(32, TEX_W * TEX_H * BYTES_PER_PIXEL);
312 GX_InitTexObj(&tex, texmem, TEX_W, TEX_H, GX_TF_RGB565, GX_CLAMP, GX_CLAMP, GX_FALSE);
313 interframeTexmem = memalign(32, TEX_W * TEX_H * BYTES_PER_PIXEL);
314 GX_InitTexObj(&interframeTex, interframeTexmem, TEX_W, TEX_H, GX_TF_RGB565, GX_CLAMP, GX_CLAMP, GX_FALSE);
315 rescaleTexmem = memalign(32, TEX_W * TEX_H * 4 * BYTES_PER_PIXEL);
316 GX_InitTexObj(&rescaleTex, rescaleTexmem, TEX_W * 2, TEX_H * 2, GX_TF_RGB565, GX_CLAMP, GX_CLAMP, GX_FALSE);
317 GX_InitTexObjFilterMode(&rescaleTex, GX_LINEAR, GX_LINEAR);
318
319 VIDEO_SetPostRetraceCallback(_retraceCallback);
320
321 font = GUIFontCreate();
322
323 fatInitDefault();
324
325 rumble.setRumble = _setRumble;
326
327 rotation.sample = _sampleRotation;
328 rotation.readTiltX = _readTiltX;
329 rotation.readTiltY = _readTiltY;
330 rotation.readGyroZ = _readGyroZ;
331
332 stream.videoDimensionsChanged = NULL;
333 stream.postVideoFrame = NULL;
334 stream.postAudioFrame = NULL;
335 stream.postAudioBuffer = _postAudioBuffer;
336
337 struct mGUIRunner runner = {
338 .params = {
339 640, 480,
340 font, "",
341 _drawStart, _drawEnd,
342 _pollInput, _pollCursor,
343 0,
344 _guiPrepare, 0,
345 },
346 .keySources = (struct GUIInputKeys[]) {
347 {
348 .name = "GameCube Input (1)",
349 .id = GCN1_INPUT,
350 .keyNames = (const char*[]) {
351 "D-Pad Left",
352 "D-Pad Right",
353 "D-Pad Down",
354 "D-Pad Up",
355 "Z",
356 "R",
357 "L",
358 0,
359 "A",
360 "B",
361 "X",
362 "Y",
363 "Start"
364 },
365 .nKeys = 13
366 },
367 {
368 .name = "GameCube Input (2)",
369 .id = GCN2_INPUT,
370 .keyNames = (const char*[]) {
371 "D-Pad Left",
372 "D-Pad Right",
373 "D-Pad Down",
374 "D-Pad Up",
375 "Z",
376 "R",
377 "L",
378 0,
379 "A",
380 "B",
381 "X",
382 "Y",
383 "Start"
384 },
385 .nKeys = 13
386 },
387 {
388 .name = "Wii Remote Input",
389 .id = WIIMOTE_INPUT,
390 .keyNames = (const char*[]) {
391 "2",
392 "1",
393 "B",
394 "A",
395 "-",
396 0,
397 0,
398 "\1\xE",
399 "Left",
400 "Right",
401 "Down",
402 "Up",
403 "+",
404 0,
405 0,
406 0,
407 "Z",
408 "C",
409 },
410 .nKeys = 18
411 },
412 {
413 .name = "Classic Controller Input",
414 .id = CLASSIC_INPUT,
415 .keyNames = (const char*[]) {
416 0,
417 0,
418 0,
419 0,
420 0,
421 0,
422 0,
423 0,
424 0,
425 0,
426 0,
427 0,
428 0,
429 0,
430 0,
431 0,
432 "Up",
433 "Left",
434 "ZR",
435 "X",
436 "A",
437 "Y",
438 "B",
439 "ZL",
440 0,
441 "R",
442 "+",
443 "\1\xE",
444 "-",
445 "L",
446 "Down",
447 "Right",
448 },
449 .nKeys = 32
450 },
451 { .id = 0 }
452 },
453 .configExtra = (struct GUIMenuItem[]) {
454 {
455 .title = "Video mode",
456 .data = "videoMode",
457 .submenu = 0,
458 .state = 0,
459 .validStates = (const char*[]) {
460 "Autodetect (recommended)",
461 "480i",
462 "480p",
463 "240p",
464 },
465 .nStates = 4
466 },
467 {
468 .title = "Screen mode",
469 .data = "screenMode",
470 .submenu = 0,
471 .state = 0,
472 .validStates = (const char*[]) {
473 "Pixel-Accurate",
474 "Stretched",
475 },
476 .nStates = 2
477 },
478 {
479 .title = "Filtering",
480 .data = "filter",
481 .submenu = 0,
482 .state = 0,
483 .validStates = (const char*[]) {
484 "Pixelated",
485 "Bilinear (smoother)",
486 "Bilinear (pixelated)",
487 },
488 .nStates = 3
489 },
490 {
491 .title = "Horizontal stretch",
492 .data = "stretchWidth",
493 .submenu = 0,
494 .state = 7,
495 .validStates = (const char*[]) {
496 "1/2x", "0.6x", "2/3x", "0.7x", "3/4x", "0.8x", "0.9x", "1.0x"
497 },
498 .stateMappings = (const struct GUIVariant[]) {
499 GUI_V_F(0.5f),
500 GUI_V_F(0.6f),
501 GUI_V_F(2.f / 3.f),
502 GUI_V_F(0.7f),
503 GUI_V_F(0.75f),
504 GUI_V_F(0.8f),
505 GUI_V_F(0.9f),
506 GUI_V_F(1.0f),
507 },
508 .nStates = 8
509 },
510 {
511 .title = "Vertical stretch",
512 .data = "stretchHeight",
513 .submenu = 0,
514 .state = 6,
515 .validStates = (const char*[]) {
516 "1/2x", "0.6x", "2/3x", "0.7x", "3/4x", "0.8x", "0.9x", "1.0x"
517 },
518 .stateMappings = (const struct GUIVariant[]) {
519 GUI_V_F(0.5f),
520 GUI_V_F(0.6f),
521 GUI_V_F(2.f / 3.f),
522 GUI_V_F(0.7f),
523 GUI_V_F(0.75f),
524 GUI_V_F(0.8f),
525 GUI_V_F(0.9f),
526 GUI_V_F(1.0f),
527 },
528 .nStates = 8
529 },
530 },
531 .nConfigExtra = 5,
532 .setup = _setup,
533 .teardown = 0,
534 .gameLoaded = _gameLoaded,
535 .gameUnloaded = _gameUnloaded,
536 .prepareForFrame = _prepareForFrame,
537 .drawFrame = _drawFrame,
538 .paused = _gameUnloaded,
539 .unpaused = _unpaused,
540 .incrementScreenMode = _incrementScreenMode,
541 .setFrameLimiter = _setFrameLimiter,
542 .pollGameInput = _pollGameInput
543 };
544 mGUIInit(&runner, "wii");
545 reconfigureScreen(&runner);
546
547 // Make sure screen is properly initialized by drawing a blank frame
548 _drawStart();
549 _drawEnd();
550
551 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_A, GUI_INPUT_SELECT);
552 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_B, GUI_INPUT_BACK);
553 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_TRIGGER_Z, GUI_INPUT_CANCEL);
554 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_UP, GUI_INPUT_UP);
555 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_DOWN, GUI_INPUT_DOWN);
556 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_LEFT, GUI_INPUT_LEFT);
557 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_RIGHT, GUI_INPUT_RIGHT);
558
559 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_2, GUI_INPUT_SELECT);
560 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_1, GUI_INPUT_BACK);
561 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_HOME, GUI_INPUT_CANCEL);
562 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_RIGHT, GUI_INPUT_UP);
563 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_LEFT, GUI_INPUT_DOWN);
564 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_UP, GUI_INPUT_LEFT);
565 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_DOWN, GUI_INPUT_RIGHT);
566
567 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_A, GUI_INPUT_SELECT);
568 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_B, GUI_INPUT_BACK);
569 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_HOME, GUI_INPUT_CANCEL);
570 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_UP, GUI_INPUT_UP);
571 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_DOWN, GUI_INPUT_DOWN);
572 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_LEFT, GUI_INPUT_LEFT);
573 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_RIGHT, GUI_INPUT_RIGHT);
574
575
576 float stretch = 0;
577 if (mCoreConfigGetFloatValue(&runner.config, "stretchWidth", &stretch)) {
578 wStretch = fminf(1.0f, fmaxf(0.5f, stretch));
579 }
580 if (mCoreConfigGetFloatValue(&runner.config, "stretchHeight", &stretch)) {
581 hStretch = fminf(1.0f, fmaxf(0.5f, stretch));
582 }
583
584 if (argc > 1) {
585 size_t i;
586 for (i = 0; runner.keySources[i].id; ++i) {
587 mInputMapLoad(&runner.params.keyMap, runner.keySources[i].id, mCoreConfigGetInput(&runner.config));
588 }
589 mGUIRun(&runner, argv[1]);
590 } else {
591 mGUIRunloop(&runner);
592 }
593 VIDEO_SetBlack(true);
594 VIDEO_Flush();
595 VIDEO_WaitVSync();
596 mGUIDeinit(&runner);
597
598 free(fifo);
599 free(texmem);
600 free(rescaleTexmem);
601 free(interframeTexmem);
602
603 free(outputBuffer);
604 GUIFontDestroy(font);
605
606 free(framebuffer[0]);
607 free(framebuffer[1]);
608
609 return 0;
610}
611
612static void _audioDMA(void) {
613 if (!audioBufferSize) {
614 return;
615 }
616 DCFlushRange(audioBuffer[currentAudioBuffer], audioBufferSize * sizeof(struct GBAStereoSample));
617 AUDIO_InitDMA((u32) audioBuffer[currentAudioBuffer], audioBufferSize * sizeof(struct GBAStereoSample));
618 currentAudioBuffer = (currentAudioBuffer + 1) % 3;
619 audioBufferSize = 0;
620}
621
622static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* right) {
623 UNUSED(stream);
624 int available = blip_samples_avail(left);
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(left, &audioBuffer[currentAudioBuffer][audioBufferSize].right, available, true);
632 blip_read_samples(right, &audioBuffer[currentAudioBuffer][audioBufferSize].left, available, true);
633 audioBufferSize += available;
634 }
635 if (audioBufferSize == SAMPLES && !AUDIO_GetDMAEnableFlag()) {
636 _audioDMA();
637 AUDIO_StartDMA();
638 }
639}
640
641static void _drawStart(void) {
642 VIDEO_SetBlack(false);
643
644 u32 level = 0;
645 _CPU_ISR_Disable(level);
646 if (referenceRetraceCount > retraceCount) {
647 if (frameLimiter) {
648 VIDEO_WaitVSync();
649 }
650 referenceRetraceCount = retraceCount;
651 }
652 _CPU_ISR_Restore(level);
653
654 GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE);
655 GX_SetColorUpdate(GX_TRUE);
656
657 GX_SetViewport(0, 0, vmode->fbWidth, vmode->efbHeight, 0, 1);
658}
659
660static void _drawEnd(void) {
661 GX_CopyDisp(framebuffer[whichFb], GX_TRUE);
662 GX_DrawDone();
663 VIDEO_SetNextFramebuffer(MEM_K0_TO_K1(framebuffer[whichFb]));
664 VIDEO_Flush();
665 whichFb = !whichFb;
666
667 u32 level = 0;
668 _CPU_ISR_Disable(level);
669 ++referenceRetraceCount;
670 _CPU_ISR_Restore(level);
671}
672
673static void _setFrameLimiter(struct mGUIRunner* runner, bool limit) {
674 UNUSED(runner);
675 frameLimiter = limit;
676}
677
678static uint32_t _pollInput(const struct mInputMap* map) {
679 PAD_ScanPads();
680 u16 padkeys = PAD_ButtonsHeld(0);
681
682 WPAD_ScanPads();
683 u32 wiiPad = WPAD_ButtonsHeld(0);
684 u32 ext = 0;
685 WPAD_Probe(0, &ext);
686
687 int keys = 0;
688 keys |= mInputMapKeyBits(map, GCN1_INPUT, padkeys, 0);
689 keys |= mInputMapKeyBits(map, GCN2_INPUT, padkeys, 0);
690 keys |= mInputMapKeyBits(map, WIIMOTE_INPUT, wiiPad, 0);
691 if (ext == WPAD_EXP_CLASSIC) {
692 keys |= mInputMapKeyBits(map, CLASSIC_INPUT, wiiPad, 0);
693 }
694 int x = PAD_StickX(0);
695 int y = PAD_StickY(0);
696 int w_x = WPAD_StickX(0, 0);
697 int w_y = WPAD_StickY(0, 0);
698 if (x < -ANALOG_DEADZONE || w_x < -ANALOG_DEADZONE) {
699 keys |= 1 << GUI_INPUT_LEFT;
700 }
701 if (x > ANALOG_DEADZONE || w_x > ANALOG_DEADZONE) {
702 keys |= 1 << GUI_INPUT_RIGHT;
703 }
704 if (y < -ANALOG_DEADZONE || w_y < -ANALOG_DEADZONE) {
705 keys |= 1 << GUI_INPUT_DOWN;
706 }
707 if (y > ANALOG_DEADZONE || w_y > ANALOG_DEADZONE) {
708 keys |= 1 << GUI_INPUT_UP;
709 }
710 return keys;
711}
712
713static enum GUICursorState _pollCursor(unsigned* x, unsigned* y) {
714 ir_t ir;
715 WPAD_IR(0, &ir);
716 if (!ir.smooth_valid) {
717 return GUI_CURSOR_NOT_PRESENT;
718 }
719 *x = ir.sx;
720 *y = ir.sy;
721 WPAD_ScanPads();
722 u32 wiiPad = WPAD_ButtonsHeld(0);
723 if (wiiPad & WPAD_BUTTON_A) {
724 return GUI_CURSOR_DOWN;
725 }
726 return GUI_CURSOR_UP;
727}
728
729void _reproj(int w, int h) {
730 Mtx44 proj;
731 int top = (vmode->efbHeight * hAdjust - h) / 2;
732 int left = (vmode->fbWidth * wAdjust - w) / 2;
733 guOrtho(proj, -top, top + h, -left, left + w, 0, 300);
734 GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
735}
736
737void _reproj2(int w, int h) {
738 Mtx44 proj;
739 int top = h * (1.0 - hStretch) / 2;
740 int left = w * (1.0 - wStretch) / 2;
741 guOrtho(proj, -top, h + top, -left, w + left, 0, 300);
742 GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
743}
744
745void _guiPrepare(void) {
746 GX_SetNumTevStages(1);
747 _reproj2(vmode->fbWidth * guiScale * wAdjust, vmode->efbHeight * guiScale * hAdjust);
748}
749
750void _setup(struct mGUIRunner* runner) {
751 runner->core->setPeripheral(runner->core, mPERIPH_ROTATION, &rotation);
752 runner->core->setPeripheral(runner->core, mPERIPH_RUMBLE, &rumble);
753 runner->core->setAVStream(runner->core, &stream);
754
755 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_A, GBA_KEY_A);
756 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_B, GBA_KEY_B);
757 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_START, GBA_KEY_START);
758 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_X, GBA_KEY_SELECT);
759 _mapKey(&runner->core->inputMap, GCN2_INPUT, PAD_BUTTON_Y, GBA_KEY_SELECT);
760 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_UP, GBA_KEY_UP);
761 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_DOWN, GBA_KEY_DOWN);
762 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_LEFT, GBA_KEY_LEFT);
763 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_RIGHT, GBA_KEY_RIGHT);
764 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_TRIGGER_L, GBA_KEY_L);
765 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_TRIGGER_R, GBA_KEY_R);
766
767 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_2, GBA_KEY_A);
768 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_1, GBA_KEY_B);
769 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_PLUS, GBA_KEY_START);
770 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_MINUS, GBA_KEY_SELECT);
771 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_RIGHT, GBA_KEY_UP);
772 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_LEFT, GBA_KEY_DOWN);
773 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_UP, GBA_KEY_LEFT);
774 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_DOWN, GBA_KEY_RIGHT);
775 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_B, GBA_KEY_L);
776 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_A, GBA_KEY_R);
777
778 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_A, GBA_KEY_A);
779 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_B, GBA_KEY_B);
780 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_PLUS, GBA_KEY_START);
781 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_MINUS, GBA_KEY_SELECT);
782 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_UP, GBA_KEY_UP);
783 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_DOWN, GBA_KEY_DOWN);
784 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_LEFT, GBA_KEY_LEFT);
785 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_RIGHT, GBA_KEY_RIGHT);
786 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_FULL_L, GBA_KEY_L);
787 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_FULL_R, GBA_KEY_R);
788
789 struct mInputAxis desc = { GBA_KEY_RIGHT, GBA_KEY_LEFT, ANALOG_DEADZONE, -ANALOG_DEADZONE };
790 mInputBindAxis(&runner->core->inputMap, GCN1_INPUT, 0, &desc);
791 mInputBindAxis(&runner->core->inputMap, CLASSIC_INPUT, 0, &desc);
792 desc = (struct mInputAxis) { GBA_KEY_UP, GBA_KEY_DOWN, ANALOG_DEADZONE, -ANALOG_DEADZONE };
793 mInputBindAxis(&runner->core->inputMap, GCN1_INPUT, 1, &desc);
794 mInputBindAxis(&runner->core->inputMap, CLASSIC_INPUT, 1, &desc);
795
796 outputBuffer = memalign(32, TEX_W * TEX_H * BYTES_PER_PIXEL);
797 runner->core->setVideoBuffer(runner->core, outputBuffer, TEX_W);
798
799 runner->core->setAudioBufferSize(runner->core, SAMPLES);
800
801 double ratio = GBAAudioCalculateRatio(1, audioSampleRate, 1);
802 blip_set_rates(runner->core->getAudioChannel(runner->core, 0), runner->core->frequency(runner->core), 48000 * ratio);
803 blip_set_rates(runner->core->getAudioChannel(runner->core, 1), runner->core->frequency(runner->core), 48000 * ratio);
804
805 frameLimiter = true;
806}
807
808void _gameUnloaded(struct mGUIRunner* runner) {
809 UNUSED(runner);
810 AUDIO_StopDMA();
811 frameLimiter = true;
812 VIDEO_SetBlack(true);
813 VIDEO_Flush();
814 VIDEO_WaitVSync();
815}
816
817void _gameLoaded(struct mGUIRunner* runner) {
818 reconfigureScreen(runner);
819 if (runner->core->platform(runner->core) == PLATFORM_GBA && ((struct GBA*) runner->core->board)->memory.hw.devices & HW_GYRO) {
820 int i;
821 for (i = 0; i < 6; ++i) {
822 u32 result = WPAD_SetMotionPlus(0, 1);
823 if (result == WPAD_ERR_NONE) {
824 break;
825 }
826 sleep(1);
827 }
828 }
829 memset(texmem, 0, TEX_W * TEX_H * BYTES_PER_PIXEL);
830 memset(interframeTexmem, 0, TEX_W * TEX_H * BYTES_PER_PIXEL);
831 _unpaused(runner);
832}
833
834void _unpaused(struct mGUIRunner* runner) {
835 u32 level = 0;
836 VIDEO_WaitVSync();
837 _CPU_ISR_Disable(level);
838 referenceRetraceCount = retraceCount;
839 _CPU_ISR_Restore(level);
840
841 unsigned mode;
842 if (mCoreConfigGetUIntValue(&runner->config, "videoMode", &mode) && mode < VM_MAX) {
843 if (mode != videoMode) {
844 reconfigureScreen(runner);
845 }
846 }
847 if (mCoreConfigGetUIntValue(&runner->config, "screenMode", &mode) && mode < SM_MAX) {
848 screenMode = mode;
849 }
850 if (mCoreConfigGetUIntValue(&runner->config, "filter", &mode) && mode < FM_MAX) {
851 filterMode = mode;
852 switch (mode) {
853 case FM_NEAREST:
854 case FM_LINEAR_2x:
855 default:
856 GX_InitTexObjFilterMode(&tex, GX_NEAR, GX_NEAR);
857 break;
858 case FM_LINEAR_1x:
859 GX_InitTexObjFilterMode(&tex, GX_LINEAR, GX_LINEAR);
860 break;
861 }
862 }
863 int fakeBool;
864 if (mCoreConfigGetIntValue(&runner->config, "interframeBlending", &fakeBool)) {
865 interframeBlending = fakeBool;
866 }
867 if (mCoreConfigGetIntValue(&runner->config, "sgb.borderCrop", &fakeBool)) {
868 sgbCrop = fakeBool;
869 }
870
871 float stretch;
872 if (mCoreConfigGetFloatValue(&runner->config, "stretchWidth", &stretch)) {
873 wStretch = fminf(1.0f, fmaxf(0.5f, stretch));
874 }
875 if (mCoreConfigGetFloatValue(&runner->config, "stretchHeight", &stretch)) {
876 hStretch = fminf(1.0f, fmaxf(0.5f, stretch));
877 }
878}
879
880void _prepareForFrame(struct mGUIRunner* runner) {
881 if (interframeBlending) {
882 memcpy(interframeTexmem, texmem, TEX_W * TEX_H * BYTES_PER_PIXEL);
883 }
884}
885
886void _drawFrame(struct mGUIRunner* runner, bool faded) {
887 runner->core->desiredVideoDimensions(runner->core, &corew, &coreh);
888 uint32_t color = 0xFFFFFF3F;
889 if (!faded) {
890 color |= 0xC0;
891 }
892 size_t x, y;
893 uint64_t* texdest = (uint64_t*) texmem;
894 uint64_t* texsrc = (uint64_t*) outputBuffer;
895 for (y = 0; y < coreh; y += 4) {
896 for (x = 0; x < corew >> 2; ++x) {
897 texdest[0 + x * 4 + y * 64] = texsrc[0 + x + y * 64];
898 texdest[1 + x * 4 + y * 64] = texsrc[64 + x + y * 64];
899 texdest[2 + x * 4 + y * 64] = texsrc[128 + x + y * 64];
900 texdest[3 + x * 4 + y * 64] = texsrc[192 + x + y * 64];
901 }
902 }
903 DCFlushRange(texdest, TEX_W * TEX_H * BYTES_PER_PIXEL);
904 if (interframeBlending) {
905 DCFlushRange(interframeTexmem, TEX_W * TEX_H * BYTES_PER_PIXEL);
906 }
907
908 if (faded || interframeBlending) {
909 GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_NOOP);
910 } else {
911 GX_SetBlendMode(GX_BM_NONE, GX_BL_ONE, GX_BL_ZERO, GX_LO_NOOP);
912 }
913 GX_InvalidateTexAll();
914 if (interframeBlending) {
915 GX_LoadTexObj(&interframeTex, GX_TEXMAP0);
916 GX_LoadTexObj(&tex, GX_TEXMAP1);
917 GX_SetNumTevStages(2);
918 } else {
919 GX_LoadTexObj(&tex, GX_TEXMAP0);
920 GX_SetNumTevStages(1);
921 }
922
923 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0);
924 s16 vertWidth = corew;
925 s16 vertHeight = coreh;
926
927 if (filterMode == FM_LINEAR_2x) {
928 Mtx44 proj;
929 guOrtho(proj, 0, vmode->efbHeight, 0, vmode->fbWidth, 0, 300);
930 GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
931
932 GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
933 GX_Position2s16(0, TEX_H * 2);
934 GX_Color1u32(0xFFFFFFFF);
935 GX_TexCoord2f32(0, 1);
936
937 GX_Position2s16(TEX_W * 2, TEX_H * 2);
938 GX_Color1u32(0xFFFFFFFF);
939 GX_TexCoord2f32(1, 1);
940
941 GX_Position2s16(TEX_W * 2, 0);
942 GX_Color1u32(0xFFFFFFFF);
943 GX_TexCoord2f32(1, 0);
944
945 GX_Position2s16(0, 0);
946 GX_Color1u32(0xFFFFFFFF);
947 GX_TexCoord2f32(0, 0);
948 GX_End();
949
950 GX_SetTexCopySrc(0, 0, TEX_W * 2, TEX_H * 2);
951 GX_SetTexCopyDst(TEX_W * 2, TEX_H * 2, GX_TF_RGB565, GX_FALSE);
952 GX_CopyTex(rescaleTexmem, GX_TRUE);
953 GX_LoadTexObj(&rescaleTex, GX_TEXMAP0);
954 GX_SetNumTevStages(1);
955 if (!faded) {
956 GX_SetBlendMode(GX_BM_NONE, GX_BL_ONE, GX_BL_ZERO, GX_LO_NOOP);
957 }
958 }
959
960 if (screenMode == SM_PA) {
961 unsigned factorWidth = corew;
962 unsigned factorHeight = coreh;
963 if (sgbCrop && factorWidth == 256 && factorHeight == 224) {
964 factorWidth = GB_VIDEO_HORIZONTAL_PIXELS;
965 factorHeight = GB_VIDEO_VERTICAL_PIXELS;
966 }
967
968 int hfactor = (vmode->fbWidth * wStretch) / (factorWidth * wAdjust);
969 int vfactor = (vmode->efbHeight * hStretch) / (factorHeight * hAdjust);
970 if (hfactor > vfactor) {
971 scaleFactor = vfactor;
972 } else {
973 scaleFactor = hfactor;
974 }
975
976 vertWidth *= scaleFactor;
977 vertHeight *= scaleFactor;
978
979 _reproj(corew * scaleFactor, coreh * scaleFactor);
980 } else {
981 _reproj2(corew, coreh);
982 }
983
984 GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
985 GX_Position2s16(0, vertHeight);
986 GX_Color1u32(color);
987 GX_TexCoord2f32(0, coreh / (float) TEX_H);
988
989 GX_Position2s16(vertWidth, vertHeight);
990 GX_Color1u32(color);
991 GX_TexCoord2f32(corew / (float) TEX_W, coreh / (float) TEX_H);
992
993 GX_Position2s16(vertWidth, 0);
994 GX_Color1u32(color);
995 GX_TexCoord2f32(corew / (float) TEX_W, 0);
996
997 GX_Position2s16(0, 0);
998 GX_Color1u32(color);
999 GX_TexCoord2f32(0, 0);
1000 GX_End();
1001}
1002
1003uint16_t _pollGameInput(struct mGUIRunner* runner) {
1004 UNUSED(runner);
1005 PAD_ScanPads();
1006 u16 padkeys = PAD_ButtonsHeld(0);
1007 WPAD_ScanPads();
1008 u32 wiiPad = WPAD_ButtonsHeld(0);
1009 u32 ext = 0;
1010 WPAD_Probe(0, &ext);
1011 uint16_t keys = mInputMapKeyBits(&runner->core->inputMap, GCN1_INPUT, padkeys, 0);
1012 keys |= mInputMapKeyBits(&runner->core->inputMap, GCN2_INPUT, padkeys, 0);
1013 keys |= mInputMapKeyBits(&runner->core->inputMap, WIIMOTE_INPUT, wiiPad, 0);
1014
1015 enum GBAKey angles = mInputMapAxis(&runner->core->inputMap, GCN1_INPUT, 0, PAD_StickX(0));
1016 if (angles != GBA_KEY_NONE) {
1017 keys |= 1 << angles;
1018 }
1019 angles = mInputMapAxis(&runner->core->inputMap, GCN1_INPUT, 1, PAD_StickY(0));
1020 if (angles != GBA_KEY_NONE) {
1021 keys |= 1 << angles;
1022 }
1023 if (ext == WPAD_EXP_CLASSIC) {
1024 keys |= mInputMapKeyBits(&runner->core->inputMap, CLASSIC_INPUT, wiiPad, 0);
1025 angles = mInputMapAxis(&runner->core->inputMap, CLASSIC_INPUT, 0, WPAD_StickX(0, 0));
1026 if (angles != GBA_KEY_NONE) {
1027 keys |= 1 << angles;
1028 }
1029 angles = mInputMapAxis(&runner->core->inputMap, CLASSIC_INPUT, 1, WPAD_StickY(0, 0));
1030 if (angles != GBA_KEY_NONE) {
1031 keys |= 1 << angles;
1032 }
1033 }
1034
1035 return keys;
1036}
1037
1038void _incrementScreenMode(struct mGUIRunner* runner) {
1039 UNUSED(runner);
1040 int mode = screenMode | (filterMode << 1);
1041 ++mode;
1042 screenMode = mode % SM_MAX;
1043 filterMode = (mode >> 1) % FM_MAX;
1044 mCoreConfigSetUIntValue(&runner->config, "screenMode", screenMode);
1045 mCoreConfigSetUIntValue(&runner->config, "filter", filterMode);
1046 switch (filterMode) {
1047 case FM_NEAREST:
1048 case FM_LINEAR_2x:
1049 default:
1050 GX_InitTexObjFilterMode(&tex, GX_NEAR, GX_NEAR);
1051 break;
1052 case FM_LINEAR_1x:
1053 GX_InitTexObjFilterMode(&tex, GX_LINEAR, GX_LINEAR);
1054 break;
1055 }
1056}
1057
1058void _setRumble(struct mRumble* rumble, int enable) {
1059 UNUSED(rumble);
1060 WPAD_Rumble(0, enable);
1061 if (enable) {
1062 PAD_ControlMotor(0, PAD_MOTOR_RUMBLE);
1063 } else {
1064 PAD_ControlMotor(0, PAD_MOTOR_STOP);
1065 }
1066}
1067
1068void _sampleRotation(struct mRotationSource* source) {
1069 UNUSED(source);
1070 vec3w_t accel;
1071 WPAD_Accel(0, &accel);
1072 // These are swapped
1073 tiltX = (0x1EA - accel.y) << 22;
1074 tiltY = (0x1EA - accel.x) << 22;
1075
1076 // This doesn't seem to work at all with -TR remotes
1077 struct expansion_t exp;
1078 WPAD_Expansion(0, &exp);
1079 if (exp.type != EXP_MOTION_PLUS) {
1080 return;
1081 }
1082 gyroZ = exp.mp.rz - 0x1FA0;
1083 gyroZ <<= 18;
1084}
1085
1086int32_t _readTiltX(struct mRotationSource* source) {
1087 UNUSED(source);
1088 return tiltX;
1089}
1090
1091int32_t _readTiltY(struct mRotationSource* source) {
1092 UNUSED(source);
1093 return tiltY;
1094}
1095
1096int32_t _readGyroZ(struct mRotationSource* source) {
1097 UNUSED(source);
1098 return gyroZ;
1099}
1100
1101static s8 WPAD_StickX(u8 chan, u8 right) {
1102 struct expansion_t exp;
1103 WPAD_Expansion(chan, &exp);
1104 struct joystick_t* js = NULL;
1105
1106 switch (exp.type) {
1107 case WPAD_EXP_NUNCHUK:
1108 case WPAD_EXP_GUITARHERO3:
1109 if (right == 0) {
1110 js = &exp.nunchuk.js;
1111 }
1112 break;
1113 case WPAD_EXP_CLASSIC:
1114 if (right == 0) {
1115 js = &exp.classic.ljs;
1116 } else {
1117 js = &exp.classic.rjs;
1118 }
1119 break;
1120 default:
1121 break;
1122 }
1123
1124 if (!js) {
1125 return 0;
1126 }
1127 int centered = (int) js->pos.x - (int) js->center.x;
1128 int range = (int) js->max.x - (int) js->min.x;
1129 int value = (centered * 0xFF) / range;
1130 if (value > 0x7F) {
1131 return 0x7F;
1132 }
1133 if (value < -0x80) {
1134 return -0x80;
1135 }
1136 return value;
1137}
1138
1139static s8 WPAD_StickY(u8 chan, u8 right) {
1140 struct expansion_t exp;
1141 WPAD_Expansion(chan, &exp);
1142 struct joystick_t* js = NULL;
1143
1144 switch (exp.type) {
1145 case WPAD_EXP_NUNCHUK:
1146 case WPAD_EXP_GUITARHERO3:
1147 if (right == 0) {
1148 js = &exp.nunchuk.js;
1149 }
1150 break;
1151 case WPAD_EXP_CLASSIC:
1152 if (right == 0) {
1153 js = &exp.classic.ljs;
1154 } else {
1155 js = &exp.classic.rjs;
1156 }
1157 break;
1158 default:
1159 break;
1160 }
1161
1162 if (!js) {
1163 return 0;
1164 }
1165 int centered = (int) js->pos.y - (int) js->center.y;
1166 int range = (int) js->max.y - (int) js->min.y;
1167 int value = (centered * 0xFF) / range;
1168 if (value > 0x7F) {
1169 return 0x7F;
1170 }
1171 if (value < -0x80) {
1172 return -0x80;
1173 }
1174 return value;
1175}
1176
1177void _retraceCallback(u32 count) {
1178 u32 level = 0;
1179 _CPU_ISR_Disable(level);
1180 retraceCount = count;
1181 _CPU_ISR_Restore(level);
1182}