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