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