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 GBAWiiLog(struct GBAThread* thread, enum GBALogLevel level, const char* format, va_list args);
26
27static void _audioDMA(void);
28static void _setRumble(struct GBARumble* rumble, int enable);
29static void _sampleRotation(struct GBARotationSource* source);
30static int32_t _readTiltX(struct GBARotationSource* source);
31static int32_t _readTiltY(struct GBARotationSource* source);
32static int32_t _readGyroZ(struct GBARotationSource* source);
33
34static void _drawStart(void);
35static void _drawEnd(void);
36static int _pollInput(void);
37static void _guiPrepare(void);
38static void _guiFinish(void);
39
40static void _setup(struct GBAGUIRunner* runner);
41static void _gameLoaded(struct GBAGUIRunner* runner);
42static void _gameUnloaded(struct GBAGUIRunner* runner);
43static void _prepareForFrame(struct GBAGUIRunner* runner);
44static void _drawFrame(struct GBAGUIRunner* runner, bool faded);
45static uint16_t _pollGameInput(struct GBAGUIRunner* runner);
46
47static struct GBAVideoSoftwareRenderer renderer;
48static struct GBARumble rumble;
49static struct GBARotationSource rotation;
50static FILE* logfile;
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 AUDIO_Init(0);
73 AUDIO_SetDSPSampleRate(AI_SAMPLERATE_48KHZ);
74 AUDIO_RegisterDMACallback(_audioDMA);
75
76 memset(audioBuffer, 0, sizeof(audioBuffer));
77
78#if !defined(COLOR_16_BIT) && !defined(COLOR_5_6_5)
79#error This pixel format is unsupported. Please use -DCOLOR_16-BIT -DCOLOR_5_6_5
80#endif
81
82 mode = VIDEO_GetPreferredMode(0);
83 framebuffer[0] = SYS_AllocateFramebuffer(mode);
84 framebuffer[1] = SYS_AllocateFramebuffer(mode);
85
86 VIDEO_Configure(mode);
87 VIDEO_SetNextFramebuffer(framebuffer[whichFb]);
88 VIDEO_SetBlack(FALSE);
89 VIDEO_Flush();
90 VIDEO_WaitVSync();
91 if (mode->viTVMode & VI_NON_INTERLACE) {
92 VIDEO_WaitVSync();
93 }
94
95 GXColor bg = { 0, 0, 0, 0xFF };
96 void* fifo = memalign(32, 0x40000);
97 memset(fifo, 0, 0x40000);
98 GX_Init(fifo, 0x40000);
99 GX_SetCopyClear(bg, 0x00FFFFFF);
100 GX_SetViewport(0, 0, mode->fbWidth, mode->efbHeight, 0, 1);
101
102 f32 yscale = GX_GetYScaleFactor(mode->efbHeight, mode->xfbHeight);
103 u32 xfbHeight = GX_SetDispCopyYScale(yscale);
104 GX_SetScissor(0, 0, mode->viWidth, mode->viWidth);
105 GX_SetDispCopySrc(0, 0, mode->fbWidth, mode->efbHeight);
106 GX_SetDispCopyDst(mode->fbWidth, xfbHeight);
107 GX_SetCopyFilter(mode->aa, mode->sample_pattern, GX_TRUE, mode->vfilter);
108 GX_SetFieldMode(mode->field_rendering, ((mode->viHeight == 2 * mode->xfbHeight) ? GX_ENABLE : GX_DISABLE));
109
110 GX_SetCullMode(GX_CULL_NONE);
111 GX_CopyDisp(framebuffer[whichFb], GX_TRUE);
112 GX_SetDispCopyGamma(GX_GM_1_0);
113
114 GX_ClearVtxDesc();
115 GX_SetVtxDesc(GX_VA_POS, GX_DIRECT);
116 GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT);
117 GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT);
118
119 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 0);
120 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0);
121 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
122
123 GX_SetNumChans(1);
124 GX_SetNumTexGens(1);
125 GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
126 GX_SetTevOp(GX_TEVSTAGE0, GX_MODULATE);
127
128 GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);
129 GX_InvVtxCache();
130 GX_InvalidateTexAll();
131
132 guVector cam = { 0.0f, 0.0f, 0.0f };
133 guVector up = { 0.0f, 1.0f, 0.0f };
134 guVector look = { 0.0f, 0.0f, -1.0f };
135 guLookAt(view, &cam, &up, &look);
136
137 guMtxIdentity(model);
138 guMtxTransApply(model, model, 0.0f, 0.0f, -6.0f);
139 guMtxConcat(view, model, modelview);
140 GX_LoadPosMtxImm(modelview, GX_PNMTX0);
141
142 texmem = memalign(32, 256 * 256 * BYTES_PER_PIXEL);
143 memset(texmem, 0, 256 * 256 * BYTES_PER_PIXEL);
144 GX_InitTexObj(&tex, texmem, 256, 256, GX_TF_RGB565, GX_CLAMP, GX_CLAMP, GX_FALSE);
145
146 font = GUIFontCreate();
147
148 fatInitDefault();
149
150 logfile = fopen("/mgba.log", "w");
151
152 rumble.setRumble = _setRumble;
153
154 rotation.sample = _sampleRotation;
155 rotation.readTiltX = _readTiltX;
156 rotation.readTiltY = _readTiltY;
157 rotation.readGyroZ = _readGyroZ;
158
159 struct GBAGUIRunner runner = {
160 .params = {
161 352, 230,
162 font, "/",
163 _drawStart, _drawEnd, _pollInput,
164 _guiPrepare, _guiFinish,
165
166 GUI_PARAMS_TRAIL
167 },
168 .setup = _setup,
169 .teardown = 0,
170 .gameLoaded = _gameLoaded,
171 .gameUnloaded = _gameUnloaded,
172 .prepareForFrame = _prepareForFrame,
173 .drawFrame = _drawFrame,
174 .pollGameInput = _pollGameInput
175 };
176 GBAGUIInit(&runner, 0);
177 GBAGUIRunloop(&runner);
178 GBAGUIDeinit(&runner);
179
180 fclose(logfile);
181 free(fifo);
182
183 free(renderer.outputBuffer);
184 GUIFontDestroy(font);
185
186 return 0;
187}
188
189void GBAWiiLog(struct GBAThread* thread, enum GBALogLevel level, const char* format, va_list args) {
190 UNUSED(thread);
191 UNUSED(level);
192 if (!logfile) {
193 return;
194 }
195 vfprintf(logfile, format, args);
196 fprintf(logfile, "\n");
197 fflush(logfile);
198}
199
200static void _audioDMA(void) {
201 if (!audioBufferSize) {
202 return;
203 }
204 DCFlushRange(audioBuffer[currentAudioBuffer], audioBufferSize * sizeof(struct GBAStereoSample));
205 AUDIO_InitDMA((u32) audioBuffer[currentAudioBuffer], audioBufferSize * sizeof(struct GBAStereoSample));
206 currentAudioBuffer = (currentAudioBuffer + 1) % 3;
207 audioBufferSize = 0;
208}
209
210static void _drawStart(void) {
211 VIDEO_WaitVSync();
212 GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE);
213 GX_SetColorUpdate(GX_TRUE);
214
215 GX_SetViewport(0, 0, mode->fbWidth, mode->efbHeight, 0, 1);
216}
217
218static void _drawEnd(void) {
219 GX_DrawDone();
220
221 whichFb = !whichFb;
222
223 GX_CopyDisp(framebuffer[whichFb], GX_TRUE);
224 VIDEO_SetNextFramebuffer(framebuffer[whichFb]);
225 VIDEO_Flush();
226}
227
228static int _pollInput(void) {
229 PAD_ScanPads();
230 u16 padkeys = PAD_ButtonsHeld(0);
231
232 WPAD_ScanPads();
233 u32 wiiPad = WPAD_ButtonsHeld(0);
234 u32 ext = 0;
235 WPAD_Probe(0, &ext);
236
237 int keys = 0;
238 int x = PAD_StickX(0);
239 int y = PAD_StickY(0);
240 if (x < -0x40) {
241 keys |= 1 << GUI_INPUT_LEFT;
242 }
243 if (x > 0x40) {
244 keys |= 1 << GUI_INPUT_RIGHT;
245 }
246 if (y < -0x40) {
247 keys |= 1 << GUI_INPUT_DOWN;
248 }
249 if (y > 0x40) {
250 keys |= 1 << GUI_INPUT_UP;
251 }
252 if ((padkeys & PAD_BUTTON_A) || (wiiPad & WPAD_BUTTON_2) ||
253 ((ext == WPAD_EXP_CLASSIC) && (wiiPad & (WPAD_CLASSIC_BUTTON_A | WPAD_CLASSIC_BUTTON_Y)))) {
254 keys |= 1 << GUI_INPUT_SELECT;
255 }
256 if ((padkeys & PAD_BUTTON_B) || (wiiPad & WPAD_BUTTON_1) ||
257 ((ext == WPAD_EXP_CLASSIC) && (wiiPad & (WPAD_CLASSIC_BUTTON_B | WPAD_CLASSIC_BUTTON_X)))) {
258 keys |= 1 << GUI_INPUT_BACK;
259 }
260 if ((padkeys & PAD_TRIGGER_Z) || (wiiPad & WPAD_BUTTON_HOME) ||
261 ((ext == WPAD_EXP_CLASSIC) && (wiiPad & (WPAD_CLASSIC_BUTTON_HOME)))) {
262 keys |= 1 << GUI_INPUT_CANCEL;
263 }
264 if ((padkeys & PAD_BUTTON_LEFT)|| (wiiPad & WPAD_BUTTON_UP) ||
265 ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_LEFT))) {
266 keys |= 1 << GUI_INPUT_LEFT;
267 }
268 if ((padkeys & PAD_BUTTON_RIGHT) || (wiiPad & WPAD_BUTTON_DOWN) ||
269 ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_RIGHT))) {
270 keys |= 1 << GUI_INPUT_RIGHT;
271 }
272 if ((padkeys & PAD_BUTTON_UP) || (wiiPad & WPAD_BUTTON_RIGHT) ||
273 ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_UP))) {
274 keys |= 1 << GUI_INPUT_UP;
275 }
276 if ((padkeys & PAD_BUTTON_DOWN) || (wiiPad & WPAD_BUTTON_LEFT) ||
277 ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_DOWN))) {
278 keys |= 1 << GUI_INPUT_DOWN;
279 }
280 return keys;
281}
282
283void _guiPrepare(void) {
284 Mtx44 proj;
285 guOrtho(proj, -20, 240, 0, 352, 0, 300);
286 GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
287}
288
289void _guiFinish(void) {
290 Mtx44 proj;
291 guOrtho(proj, -10, VIDEO_VERTICAL_PIXELS + 10, 0, VIDEO_HORIZONTAL_PIXELS, 0, 300);
292 GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
293}
294
295void _setup(struct GBAGUIRunner* runner) {
296 struct GBAOptions opts = {
297 .useBios = true,
298 .logLevel = 0,
299 .idleOptimization = IDLE_LOOP_DETECT
300 };
301 GBAConfigLoadDefaults(&runner->context.config, &opts);
302 runner->context.gba->logHandler = GBAWiiLog;
303 runner->context.gba->rumble = &rumble;
304 runner->context.gba->rotationSource = &rotation;
305
306 GBAVideoSoftwareRendererCreate(&renderer);
307 renderer.outputBuffer = memalign(32, 256 * 256 * BYTES_PER_PIXEL);
308 renderer.outputBufferStride = 256;
309 runner->context.renderer = &renderer.d;
310
311 GBAAudioResizeBuffer(&runner->context.gba->audio, SAMPLES);
312
313#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
314 blip_set_rates(runner->context.gba->audio.left, GBA_ARM7TDMI_FREQUENCY, 48000);
315 blip_set_rates(runner->context.gba->audio.right, GBA_ARM7TDMI_FREQUENCY, 48000);
316#endif
317}
318
319void _gameUnloaded(struct GBAGUIRunner* runner) {
320 UNUSED(runner);
321 AUDIO_StopDMA();
322}
323
324void _gameLoaded(struct GBAGUIRunner* runner) {
325 WPAD_SetDataFormat(0, WPAD_FMT_BTNS_ACC);
326 if (runner->context.gba->memory.hw.devices & HW_GYRO) {
327 int i;
328 for (i = 0; i < 6; ++i) {
329 u32 result = WPAD_SetMotionPlus(0, 1);
330 if (result == WPAD_ERR_NONE) {
331 break;
332 }
333 sleep(1);
334 }
335 }
336}
337
338void _prepareForFrame(struct GBAGUIRunner* runner) {
339#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
340 int available = blip_samples_avail(runner->context.gba->audio.left);
341 if (available + audioBufferSize > SAMPLES) {
342 available = SAMPLES - audioBufferSize;
343 }
344 available &= ~((32 / sizeof(struct GBAStereoSample)) - 1); // Force align to 32 bytes
345 if (available > 0) {
346 blip_read_samples(runner->context.gba->audio.left, &audioBuffer[currentAudioBuffer][audioBufferSize].left, available, true);
347 blip_read_samples(runner->context.gba->audio.right, &audioBuffer[currentAudioBuffer][audioBufferSize].right, available, true);
348 audioBufferSize += available;
349 }
350 if (audioBufferSize == SAMPLES && !AUDIO_GetDMAEnableFlag()) {
351 _audioDMA();
352 AUDIO_StartDMA();
353 }
354#endif
355}
356
357void _drawFrame(struct GBAGUIRunner* runner, bool faded) {
358 UNUSED(runner);
359 uint32_t color = 0xFFFFFF3F;
360 if (!faded) {
361 color |= 0xC0;
362 }
363 size_t x, y;
364 uint64_t* texdest = (uint64_t*) texmem;
365 uint64_t* texsrc = (uint64_t*) renderer.outputBuffer;
366 for (y = 0; y < VIDEO_VERTICAL_PIXELS; y += 4) {
367 for (x = 0; x < VIDEO_HORIZONTAL_PIXELS >> 2; ++x) {
368 texdest[0 + x * 4 + y * 64] = texsrc[0 + x + y * 64];
369 texdest[1 + x * 4 + y * 64] = texsrc[64 + x + y * 64];
370 texdest[2 + x * 4 + y * 64] = texsrc[128 + x + y * 64];
371 texdest[3 + x * 4 + y * 64] = texsrc[192 + x + y * 64];
372 }
373 }
374 DCFlushRange(texdest, 256 * 256 * BYTES_PER_PIXEL);
375
376 _drawStart();
377
378 if (faded) {
379 GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_NOOP);
380 } else {
381 GX_SetBlendMode(GX_BM_NONE, GX_BL_ONE, GX_BL_ZERO, GX_LO_NOOP);
382 }
383 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0);
384 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
385 GX_InvalidateTexAll();
386 GX_LoadTexObj(&tex, GX_TEXMAP0);
387
388 GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
389 GX_Position2s16(0, 256);
390 GX_Color1u32(color);
391 GX_TexCoord2s16(0, 1);
392
393 GX_Position2s16(256, 256);
394 GX_Color1u32(color);
395 GX_TexCoord2s16(1, 1);
396
397 GX_Position2s16(256, 0);
398 GX_Color1u32(color);
399 GX_TexCoord2s16(1, 0);
400
401 GX_Position2s16(0, 0);
402 GX_Color1u32(color);
403 GX_TexCoord2s16(0, 0);
404 GX_End();
405
406 _drawEnd();
407}
408
409uint16_t _pollGameInput(struct GBAGUIRunner* runner) {
410 UNUSED(runner);
411 PAD_ScanPads();
412 u16 padkeys = PAD_ButtonsHeld(0);
413 WPAD_ScanPads();
414 u32 wiiPad = WPAD_ButtonsHeld(0);
415 u32 ext = 0;
416 uint16_t keys = 0;
417 WPAD_Probe(0, &ext);
418
419 if ((padkeys & PAD_BUTTON_A) || (wiiPad & WPAD_BUTTON_2) ||
420 ((ext == WPAD_EXP_CLASSIC) && (wiiPad & (WPAD_CLASSIC_BUTTON_A | WPAD_CLASSIC_BUTTON_Y)))) {
421 keys |= 1 << GBA_KEY_A;
422 }
423 if ((padkeys & PAD_BUTTON_B) || (wiiPad & WPAD_BUTTON_1) ||
424 ((ext == WPAD_EXP_CLASSIC) && (wiiPad & (WPAD_CLASSIC_BUTTON_B | WPAD_CLASSIC_BUTTON_X)))) {
425 keys |= 1 << GBA_KEY_B;
426 }
427 if ((padkeys & PAD_TRIGGER_L) || (wiiPad & WPAD_BUTTON_B) ||
428 ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_FULL_L))) {
429 keys |= 1 << GBA_KEY_L;
430 }
431 if ((padkeys & PAD_TRIGGER_R) || (wiiPad & WPAD_BUTTON_A) ||
432 ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_FULL_R))) {
433 keys |= 1 << GBA_KEY_R;
434 }
435 if ((padkeys & PAD_BUTTON_START) || (wiiPad & WPAD_BUTTON_PLUS) ||
436 ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_PLUS))) {
437 keys |= 1 << GBA_KEY_START;
438 }
439 if ((padkeys & (PAD_BUTTON_X | PAD_BUTTON_Y)) || (wiiPad & WPAD_BUTTON_MINUS) ||
440 ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_MINUS))) {
441 keys |= 1 << GBA_KEY_SELECT;
442 }
443 if ((padkeys & PAD_BUTTON_LEFT) || (wiiPad & WPAD_BUTTON_UP) ||
444 ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_LEFT))) {
445 keys |= 1 << GBA_KEY_LEFT;
446 }
447 if ((padkeys & PAD_BUTTON_RIGHT) || (wiiPad & WPAD_BUTTON_DOWN) ||
448 ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_RIGHT))) {
449 keys |= 1 << GBA_KEY_RIGHT;
450 }
451 if ((padkeys & PAD_BUTTON_UP) || (wiiPad & WPAD_BUTTON_RIGHT) ||
452 ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_UP))) {
453 keys |= 1 << GBA_KEY_UP;
454 }
455 if ((padkeys & PAD_BUTTON_DOWN) || (wiiPad & WPAD_BUTTON_LEFT) ||
456 ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_DOWN))) {
457 keys |= 1 << GBA_KEY_DOWN;
458 }
459 int x = PAD_StickX(0);
460 int y = PAD_StickY(0);
461 if (x < -0x40) {
462 keys |= 1 << GBA_KEY_LEFT;
463 }
464 if (x > 0x40) {
465 keys |= 1 << GBA_KEY_RIGHT;
466 }
467 if (y < -0x40) {
468 keys |= 1 << GBA_KEY_DOWN;
469 }
470 if (y > 0x40) {
471 keys |= 1 << GBA_KEY_UP;
472 }
473 return keys;
474}
475
476void _setRumble(struct GBARumble* rumble, int enable) {
477 UNUSED(rumble);
478 WPAD_Rumble(0, enable);
479 if (enable) {
480 PAD_ControlMotor(0, PAD_MOTOR_RUMBLE);
481 } else {
482 PAD_ControlMotor(0, PAD_MOTOR_STOP);
483 }
484}
485
486void _sampleRotation(struct GBARotationSource* source) {
487 UNUSED(source);
488 vec3w_t accel;
489 WPAD_Accel(0, &accel);
490 // These are swapped
491 tiltX = (accel.y - 0x1EA) << 22;
492 tiltY = (accel.x - 0x1EA) << 22;
493
494 // This doesn't seem to work at all with -TR remotes
495 struct expansion_t exp;
496 WPAD_Expansion(0, &exp);
497 if (exp.type != EXP_MOTION_PLUS) {
498 return;
499 }
500 gyroZ = exp.mp.rz - 0x1FA0;
501 gyroZ <<= 18;
502}
503
504int32_t _readTiltX(struct GBARotationSource* source) {
505 UNUSED(source);
506 return tiltX;
507}
508
509int32_t _readTiltY(struct GBARotationSource* source) {
510 UNUSED(source);
511 return tiltY;
512}
513
514int32_t _readGyroZ(struct GBARotationSource* source) {
515 UNUSED(source);
516 return gyroZ;
517}