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