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