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