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