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 <mgba-util/common.h>
16
17#include <mgba/core/blip_buf.h>
18#include <mgba/core/core.h>
19#include "feature/gui/gui-runner.h"
20#include <mgba/internal/gb/video.h>
21#include <mgba/internal/gba/audio.h>
22#include <mgba/internal/gba/gba.h>
23#include <mgba/internal/gba/input.h>
24#include <mgba-util/gui.h>
25#include <mgba-util/gui/file-select.h>
26#include <mgba-util/gui/font.h>
27#include <mgba-util/gui/menu.h>
28#include <mgba-util/memory.h>
29#include <mgba-util/vfs.h>
30
31#ifdef WIIDRC
32#include <wiidrc/wiidrc.h>
33#endif
34
35#define GCN1_INPUT 0x47434E31
36#define GCN2_INPUT 0x47434E32
37#define WIIMOTE_INPUT 0x5749494D
38#define CLASSIC_INPUT 0x57494943
39#define DRC_INPUT 0x44524355
40
41#define TEX_W 256
42#define TEX_H 224
43
44#define ANALOG_DEADZONE 0x30
45
46static void _mapKey(struct mInputMap* map, uint32_t binding, int nativeKey, enum GBAKey key) {
47 mInputBindKey(map, binding, __builtin_ctz(nativeKey), key);
48}
49
50static enum ScreenMode {
51 SM_PA,
52 SM_SF,
53 SM_MAX
54} screenMode = SM_PA;
55
56static enum FilterMode {
57 FM_NEAREST,
58 FM_LINEAR_1x,
59 FM_LINEAR_2x,
60 FM_MAX
61} filterMode = FM_NEAREST;
62
63static enum VideoMode {
64 VM_AUTODETECT,
65 VM_480i,
66 VM_480p,
67 VM_240p,
68 // TODO: PAL support
69 VM_MAX
70} videoMode = VM_AUTODETECT;
71
72#define SAMPLES 512
73#define BUFFERS 8
74#define GUI_SCALE 1.35f
75#define GUI_SCALE_240p 2.0f
76
77static void _retraceCallback(u32 count);
78
79static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* right);
80static void _audioDMA(void);
81static void _setRumble(struct mRumble* rumble, int enable);
82static void _sampleRotation(struct mRotationSource* source);
83static int32_t _readTiltX(struct mRotationSource* source);
84static int32_t _readTiltY(struct mRotationSource* source);
85static int32_t _readGyroZ(struct mRotationSource* source);
86
87static void _drawStart(void);
88static void _drawEnd(void);
89static uint32_t _pollInput(const struct mInputMap*);
90static enum GUICursorState _pollCursor(unsigned* x, unsigned* y);
91static void _guiPrepare(void);
92
93static void _setup(struct mGUIRunner* runner);
94static void _gameLoaded(struct mGUIRunner* runner);
95static void _gameUnloaded(struct mGUIRunner* runner);
96static void _unpaused(struct mGUIRunner* runner);
97static void _prepareForFrame(struct mGUIRunner* runner);
98static void _drawFrame(struct mGUIRunner* runner, bool faded);
99static uint16_t _pollGameInput(struct mGUIRunner* runner);
100static void _setFrameLimiter(struct mGUIRunner* runner, bool limit);
101static void _incrementScreenMode(struct mGUIRunner* runner);
102
103static s8 WPAD_StickX(u8 chan, u8 right);
104static s8 WPAD_StickY(u8 chan, u8 right);
105
106static void* outputBuffer;
107static struct mAVStream stream;
108static struct mRumble rumble;
109static struct mRotationSource rotation;
110static GXRModeObj* vmode;
111static float wAdjust;
112static float hAdjust;
113static float wStretch = 0.9f;
114static float hStretch = 0.9f;
115static float guiScale = GUI_SCALE;
116static Mtx model, view, modelview;
117static uint16_t* texmem;
118static GXTexObj tex;
119static uint16_t* rescaleTexmem;
120static GXTexObj rescaleTex;
121static uint16_t* interframeTexmem;
122static GXTexObj interframeTex;
123static bool sgbCrop = false;
124static int32_t tiltX;
125static int32_t tiltY;
126static int32_t gyroZ;
127static uint32_t retraceCount;
128static uint32_t referenceRetraceCount;
129static bool frameLimiter = true;
130static int scaleFactor;
131static unsigned corew, coreh;
132static bool interframeBlending = true;
133
134uint32_t* romBuffer;
135size_t romBufferSize;
136
137static void* framebuffer[2] = { 0, 0 };
138static int whichFb = 0;
139
140static struct AudioBuffer {
141 struct GBAStereoSample samples[SAMPLES] __attribute__((__aligned__(32)));
142 volatile size_t size;
143} audioBuffer[BUFFERS] = {0};
144static volatile int currentAudioBuffer = 0;
145static volatile int nextAudioBuffer = 0;
146static double audioSampleRate = 60.0 / 1.001;
147
148static struct GUIFont* font;
149
150static void reconfigureScreen(struct mGUIRunner* runner) {
151 if (runner) {
152 unsigned mode;
153 if (mCoreConfigGetUIntValue(&runner->config, "videoMode", &mode) && mode < VM_MAX) {
154 videoMode = mode;
155 }
156 }
157 wAdjust = 1.f;
158 hAdjust = 1.f;
159 guiScale = GUI_SCALE;
160 audioSampleRate = 60.0 / 1.001;
161
162 s32 signalMode = CONF_GetVideo();
163
164 switch (videoMode) {
165 case VM_AUTODETECT:
166 default:
167 vmode = VIDEO_GetPreferredMode(0);
168 break;
169 case VM_480i:
170 switch (signalMode) {
171 case CONF_VIDEO_NTSC:
172 vmode = &TVNtsc480IntDf;
173 break;
174 case CONF_VIDEO_MPAL:
175 vmode = &TVMpal480IntDf;
176 break;
177 case CONF_VIDEO_PAL:
178 vmode = &TVEurgb60Hz480IntDf;
179 break;
180 }
181 break;
182 case VM_480p:
183 switch (signalMode) {
184 case CONF_VIDEO_NTSC:
185 vmode = &TVNtsc480Prog;
186 break;
187 case CONF_VIDEO_MPAL:
188 vmode = &TVMpal480Prog;
189 break;
190 case CONF_VIDEO_PAL:
191 vmode = &TVEurgb60Hz480Prog;
192 break;
193 }
194 break;
195 case VM_240p:
196 switch (signalMode) {
197 case CONF_VIDEO_NTSC:
198 vmode = &TVNtsc240Ds;
199 break;
200 case CONF_VIDEO_MPAL:
201 vmode = &TVMpal240Ds;
202 break;
203 case CONF_VIDEO_PAL:
204 vmode = &TVEurgb60Hz240Ds;
205 break;
206 }
207 wAdjust = 0.5f;
208 audioSampleRate = 90.0 / 1.50436;
209 guiScale = GUI_SCALE_240p;
210 break;
211 }
212
213 vmode->viWidth = 704;
214 vmode->viXOrigin = 8;
215
216 VIDEO_SetBlack(true);
217 VIDEO_Configure(vmode);
218
219 free(framebuffer[0]);
220 free(framebuffer[1]);
221
222 framebuffer[0] = SYS_AllocateFramebuffer(vmode);
223 framebuffer[1] = SYS_AllocateFramebuffer(vmode);
224 VIDEO_ClearFrameBuffer(vmode, MEM_K0_TO_K1(framebuffer[0]), COLOR_BLACK);
225 VIDEO_ClearFrameBuffer(vmode, MEM_K0_TO_K1(framebuffer[1]), COLOR_BLACK);
226
227 VIDEO_SetNextFramebuffer(MEM_K0_TO_K1(framebuffer[whichFb]));
228 VIDEO_Flush();
229 VIDEO_WaitVSync();
230 if (vmode->viTVMode & VI_NON_INTERLACE) {
231 VIDEO_WaitVSync();
232 }
233 GX_SetViewport(0, 0, vmode->fbWidth, vmode->efbHeight, 0, 1);
234
235 f32 yscale = GX_GetYScaleFactor(vmode->efbHeight, vmode->xfbHeight);
236 u32 xfbHeight = GX_SetDispCopyYScale(yscale);
237 GX_SetScissor(0, 0, vmode->viWidth, vmode->viWidth);
238 GX_SetDispCopySrc(0, 0, vmode->fbWidth, vmode->efbHeight);
239 GX_SetDispCopyDst(vmode->fbWidth, xfbHeight);
240 GX_SetCopyFilter(vmode->aa, vmode->sample_pattern, GX_TRUE, vmode->vfilter);
241 GX_SetFieldMode(vmode->field_rendering, ((vmode->viHeight == 2 * vmode->xfbHeight) ? GX_ENABLE : GX_DISABLE));
242
243 if (runner) {
244 runner->params.width = vmode->fbWidth * guiScale * wAdjust;
245 runner->params.height = vmode->efbHeight * guiScale * hAdjust;
246 if (runner->core) {
247 double ratio = GBAAudioCalculateRatio(1, audioSampleRate, 1);
248 blip_set_rates(runner->core->getAudioChannel(runner->core, 0), runner->core->frequency(runner->core), 48000 * ratio);
249 blip_set_rates(runner->core->getAudioChannel(runner->core, 1), runner->core->frequency(runner->core), 48000 * ratio);
250 }
251 }
252}
253
254int main(int argc, char* argv[]) {
255 VIDEO_Init();
256 VIDEO_SetBlack(true);
257 VIDEO_Flush();
258 VIDEO_WaitVSync();
259 PAD_Init();
260 WPAD_Init();
261 WPAD_SetDataFormat(0, WPAD_FMT_BTNS_ACC_IR);
262#ifdef WIIDRC
263 WiiDRC_Init();
264#endif
265 AUDIO_Init(0);
266 AUDIO_SetDSPSampleRate(AI_SAMPLERATE_48KHZ);
267 AUDIO_RegisterDMACallback(_audioDMA);
268
269 memset(audioBuffer, 0, sizeof(audioBuffer));
270#ifdef FIXED_ROM_BUFFER
271 romBufferSize = SIZE_CART0;
272 romBuffer = SYS_GetArena2Lo();
273 SYS_SetArena2Lo((void*)((intptr_t) romBuffer + romBufferSize));
274#endif
275
276#if !defined(COLOR_16_BIT) && !defined(COLOR_5_6_5)
277#error This pixel format is unsupported. Please use -DCOLOR_16-BIT -DCOLOR_5_6_5
278#endif
279
280 GXColor bg = { 0, 0, 0, 0xFF };
281 void* fifo = memalign(32, 0x40000);
282 memset(fifo, 0, 0x40000);
283 GX_Init(fifo, 0x40000);
284 GX_SetCopyClear(bg, 0x00FFFFFF);
285
286 GX_SetCullMode(GX_CULL_NONE);
287 GX_SetDispCopyGamma(GX_GM_1_0);
288
289 GX_ClearVtxDesc();
290 GX_SetVtxDesc(GX_VA_POS, GX_DIRECT);
291 GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT);
292 GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT);
293
294 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 0);
295 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0);
296 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
297
298 GX_SetNumTevStages(1);
299 GX_SetNumChans(1);
300 GX_SetNumTexGens(1);
301 GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
302 GX_SetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD0, GX_TEXMAP1, GX_COLOR0A0);
303 GX_SetTevOp(GX_TEVSTAGE0, GX_MODULATE);
304 GX_SetTevColorOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_DIVIDE_2, GX_TRUE, GX_TEVPREV);
305 GX_SetTevAlphaOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV);
306 GX_SetTevColorIn(GX_TEVSTAGE1, GX_CC_ZERO, GX_CC_TEXC, GX_CC_ONE, GX_CC_CPREV);
307 GX_SetTevAlphaIn(GX_TEVSTAGE1, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_APREV);
308
309 GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);
310 GX_InvVtxCache();
311 GX_InvalidateTexAll();
312
313 guVector cam = { 0.0f, 0.0f, 0.0f };
314 guVector up = { 0.0f, 1.0f, 0.0f };
315 guVector look = { 0.0f, 0.0f, -1.0f };
316 guLookAt(view, &cam, &up, &look);
317
318 guMtxIdentity(model);
319 guMtxTransApply(model, model, 0.0f, 0.0f, -6.0f);
320 guMtxConcat(view, model, modelview);
321 GX_LoadPosMtxImm(modelview, GX_PNMTX0);
322
323 texmem = memalign(32, TEX_W * TEX_H * BYTES_PER_PIXEL);
324 GX_InitTexObj(&tex, texmem, TEX_W, TEX_H, GX_TF_RGB565, GX_CLAMP, GX_CLAMP, GX_FALSE);
325 interframeTexmem = memalign(32, TEX_W * TEX_H * BYTES_PER_PIXEL);
326 GX_InitTexObj(&interframeTex, interframeTexmem, TEX_W, TEX_H, GX_TF_RGB565, GX_CLAMP, GX_CLAMP, GX_FALSE);
327 rescaleTexmem = memalign(32, TEX_W * TEX_H * 4 * BYTES_PER_PIXEL);
328 GX_InitTexObj(&rescaleTex, rescaleTexmem, TEX_W * 2, TEX_H * 2, GX_TF_RGB565, GX_CLAMP, GX_CLAMP, GX_FALSE);
329 GX_InitTexObjFilterMode(&rescaleTex, GX_LINEAR, GX_LINEAR);
330
331 VIDEO_SetPostRetraceCallback(_retraceCallback);
332
333 font = GUIFontCreate();
334
335 fatInitDefault();
336
337 rumble.setRumble = _setRumble;
338
339 rotation.sample = _sampleRotation;
340 rotation.readTiltX = _readTiltX;
341 rotation.readTiltY = _readTiltY;
342 rotation.readGyroZ = _readGyroZ;
343
344 stream.videoDimensionsChanged = NULL;
345 stream.postVideoFrame = NULL;
346 stream.postAudioFrame = NULL;
347 stream.postAudioBuffer = _postAudioBuffer;
348
349 struct mGUIRunner runner = {
350 .params = {
351 640, 480,
352 font, "",
353 _drawStart, _drawEnd,
354 _pollInput, _pollCursor,
355 0,
356 _guiPrepare, 0,
357 },
358 .keySources = (struct GUIInputKeys[]) {
359 {
360 .name = "GameCube Input (1)",
361 .id = GCN1_INPUT,
362 .keyNames = (const char*[]) {
363 "D-Pad Left",
364 "D-Pad Right",
365 "D-Pad Down",
366 "D-Pad Up",
367 "Z",
368 "R",
369 "L",
370 0,
371 "A",
372 "B",
373 "X",
374 "Y",
375 "Start"
376 },
377 .nKeys = 13
378 },
379 {
380 .name = "GameCube Input (2)",
381 .id = GCN2_INPUT,
382 .keyNames = (const char*[]) {
383 "D-Pad Left",
384 "D-Pad Right",
385 "D-Pad Down",
386 "D-Pad Up",
387 "Z",
388 "R",
389 "L",
390 0,
391 "A",
392 "B",
393 "X",
394 "Y",
395 "Start"
396 },
397 .nKeys = 13
398 },
399 {
400 .name = "Wii Remote Input",
401 .id = WIIMOTE_INPUT,
402 .keyNames = (const char*[]) {
403 "2",
404 "1",
405 "B",
406 "A",
407 "-",
408 0,
409 0,
410 "\1\xE",
411 "Left",
412 "Right",
413 "Down",
414 "Up",
415 "+",
416 0,
417 0,
418 0,
419 "Z",
420 "C",
421 },
422 .nKeys = 18
423 },
424 {
425 .name = "Classic Controller Input",
426 .id = CLASSIC_INPUT,
427 .keyNames = (const char*[]) {
428 0,
429 0,
430 0,
431 0,
432 0,
433 0,
434 0,
435 0,
436 0,
437 0,
438 0,
439 0,
440 0,
441 0,
442 0,
443 0,
444 "Up",
445 "Left",
446 "ZR",
447 "X",
448 "A",
449 "Y",
450 "B",
451 "ZL",
452 0,
453 "R",
454 "+",
455 "\1\xE",
456 "-",
457 "L",
458 "Down",
459 "Right",
460 },
461 .nKeys = 32
462 },
463#ifdef WIIDRC
464 {
465 .name = "Wii U GamePad Input",
466 .id = DRC_INPUT,
467 .keyNames = (const char*[]) {
468 0, // Sync
469 "\1\xE",
470 "-",
471 "+",
472 "R",
473 "L",
474 "ZR",
475 "ZL",
476 "Down",
477 "Up",
478 "Right",
479 "Left",
480 "Y",
481 "X",
482 "B",
483 "A",
484 },
485 .nKeys = 16
486 },
487#endif
488 { .id = 0 }
489 },
490 .configExtra = (struct GUIMenuItem[]) {
491 {
492 .title = "Video mode",
493 .data = "videoMode",
494 .submenu = 0,
495 .state = 0,
496 .validStates = (const char*[]) {
497 "Autodetect (recommended)",
498 "480i",
499 "480p",
500 "240p",
501 },
502 .nStates = 4
503 },
504 {
505 .title = "Screen mode",
506 .data = "screenMode",
507 .submenu = 0,
508 .state = 0,
509 .validStates = (const char*[]) {
510 "Pixel-Accurate",
511 "Stretched",
512 },
513 .nStates = 2
514 },
515 {
516 .title = "Filtering",
517 .data = "filter",
518 .submenu = 0,
519 .state = 0,
520 .validStates = (const char*[]) {
521 "Pixelated",
522 "Bilinear (smoother)",
523 "Bilinear (pixelated)",
524 },
525 .nStates = 3
526 },
527 {
528 .title = "Horizontal stretch",
529 .data = "stretchWidth",
530 .submenu = 0,
531 .state = 7,
532 .validStates = (const char*[]) {
533 "1/2x", "0.6x", "2/3x", "0.7x", "3/4x", "0.8x", "0.9x", "1.0x"
534 },
535 .stateMappings = (const struct GUIVariant[]) {
536 GUI_V_F(0.5f),
537 GUI_V_F(0.6f),
538 GUI_V_F(2.f / 3.f),
539 GUI_V_F(0.7f),
540 GUI_V_F(0.75f),
541 GUI_V_F(0.8f),
542 GUI_V_F(0.9f),
543 GUI_V_F(1.0f),
544 },
545 .nStates = 8
546 },
547 {
548 .title = "Vertical stretch",
549 .data = "stretchHeight",
550 .submenu = 0,
551 .state = 6,
552 .validStates = (const char*[]) {
553 "1/2x", "0.6x", "2/3x", "0.7x", "3/4x", "0.8x", "0.9x", "1.0x"
554 },
555 .stateMappings = (const struct GUIVariant[]) {
556 GUI_V_F(0.5f),
557 GUI_V_F(0.6f),
558 GUI_V_F(2.f / 3.f),
559 GUI_V_F(0.7f),
560 GUI_V_F(0.75f),
561 GUI_V_F(0.8f),
562 GUI_V_F(0.9f),
563 GUI_V_F(1.0f),
564 },
565 .nStates = 8
566 },
567 },
568 .nConfigExtra = 5,
569 .setup = _setup,
570 .teardown = 0,
571 .gameLoaded = _gameLoaded,
572 .gameUnloaded = _gameUnloaded,
573 .prepareForFrame = _prepareForFrame,
574 .drawFrame = _drawFrame,
575 .paused = _gameUnloaded,
576 .unpaused = _unpaused,
577 .incrementScreenMode = _incrementScreenMode,
578 .setFrameLimiter = _setFrameLimiter,
579 .pollGameInput = _pollGameInput
580 };
581 mGUIInit(&runner, "wii");
582 reconfigureScreen(&runner);
583
584 // Make sure screen is properly initialized by drawing a blank frame
585 _drawStart();
586 _drawEnd();
587
588 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_A, GUI_INPUT_SELECT);
589 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_B, GUI_INPUT_BACK);
590 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_TRIGGER_Z, GUI_INPUT_CANCEL);
591 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_UP, GUI_INPUT_UP);
592 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_DOWN, GUI_INPUT_DOWN);
593 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_LEFT, GUI_INPUT_LEFT);
594 _mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_RIGHT, GUI_INPUT_RIGHT);
595
596 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_2, GUI_INPUT_SELECT);
597 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_1, GUI_INPUT_BACK);
598 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_HOME, GUI_INPUT_CANCEL);
599 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_RIGHT, GUI_INPUT_UP);
600 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_LEFT, GUI_INPUT_DOWN);
601 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_UP, GUI_INPUT_LEFT);
602 _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_DOWN, GUI_INPUT_RIGHT);
603
604 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_A, GUI_INPUT_SELECT);
605 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_B, GUI_INPUT_BACK);
606 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_HOME, GUI_INPUT_CANCEL);
607 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_UP, GUI_INPUT_UP);
608 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_DOWN, GUI_INPUT_DOWN);
609 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_LEFT, GUI_INPUT_LEFT);
610 _mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_RIGHT, GUI_INPUT_RIGHT);
611
612#ifdef WIIDRC
613 _mapKey(&runner.params.keyMap, DRC_INPUT, WIIDRC_BUTTON_A, GUI_INPUT_SELECT);
614 _mapKey(&runner.params.keyMap, DRC_INPUT, WIIDRC_BUTTON_B, GUI_INPUT_BACK);
615 _mapKey(&runner.params.keyMap, DRC_INPUT, WIIDRC_BUTTON_X, GUI_INPUT_CANCEL);
616 _mapKey(&runner.params.keyMap, DRC_INPUT, WIIDRC_BUTTON_UP, GUI_INPUT_UP);
617 _mapKey(&runner.params.keyMap, DRC_INPUT, WIIDRC_BUTTON_DOWN, GUI_INPUT_DOWN);
618 _mapKey(&runner.params.keyMap, DRC_INPUT, WIIDRC_BUTTON_LEFT, GUI_INPUT_LEFT);
619 _mapKey(&runner.params.keyMap, DRC_INPUT, WIIDRC_BUTTON_RIGHT, GUI_INPUT_RIGHT);
620#endif
621
622 float stretch = 0;
623 if (mCoreConfigGetFloatValue(&runner.config, "stretchWidth", &stretch)) {
624 wStretch = fminf(1.0f, fmaxf(0.5f, stretch));
625 }
626 if (mCoreConfigGetFloatValue(&runner.config, "stretchHeight", &stretch)) {
627 hStretch = fminf(1.0f, fmaxf(0.5f, stretch));
628 }
629
630 if (argc > 1) {
631 size_t i;
632 for (i = 0; runner.keySources[i].id; ++i) {
633 mInputMapLoad(&runner.params.keyMap, runner.keySources[i].id, mCoreConfigGetInput(&runner.config));
634 }
635 mGUIRun(&runner, argv[1]);
636 } else {
637 mGUIRunloop(&runner);
638 }
639 VIDEO_SetBlack(true);
640 VIDEO_Flush();
641 VIDEO_WaitVSync();
642 mGUIDeinit(&runner);
643
644 free(fifo);
645 free(texmem);
646 free(rescaleTexmem);
647 free(interframeTexmem);
648
649 free(outputBuffer);
650 GUIFontDestroy(font);
651
652 free(framebuffer[0]);
653 free(framebuffer[1]);
654
655 return 0;
656}
657
658static void _audioDMA(void) {
659 struct AudioBuffer* buffer = &audioBuffer[currentAudioBuffer];
660 if (buffer->size != SAMPLES) {
661 return;
662 }
663 DCFlushRange(buffer->samples, SAMPLES * sizeof(struct GBAStereoSample));
664 AUDIO_InitDMA((u32) buffer->samples, SAMPLES * sizeof(struct GBAStereoSample));
665 buffer->size = 0;
666 currentAudioBuffer = (currentAudioBuffer + 1) % BUFFERS;
667}
668
669static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* right) {
670 UNUSED(stream);
671
672 u32 level = 0;
673 _CPU_ISR_Disable(level);
674 struct AudioBuffer* buffer = &audioBuffer[nextAudioBuffer];
675 int available = blip_samples_avail(left);
676 if (available + buffer->size > SAMPLES) {
677 available = SAMPLES - buffer->size;
678 }
679 if (available > 0) {
680 // These appear to be reversed for AUDIO_InitDMA
681 blip_read_samples(left, &buffer->samples[buffer->size].right, available, true);
682 blip_read_samples(right, &buffer->samples[buffer->size].left, available, true);
683 buffer->size += available;
684 }
685 if (buffer->size == SAMPLES) {
686 int next = (nextAudioBuffer + 1) % BUFFERS;
687 if ((currentAudioBuffer + BUFFERS - next) % BUFFERS != 1) {
688 nextAudioBuffer = next;
689 }
690 if (!AUDIO_GetDMAEnableFlag()) {
691 _audioDMA();
692 AUDIO_StartDMA();
693 }
694 }
695 _CPU_ISR_Restore(level);
696}
697
698static void _drawStart(void) {
699 VIDEO_SetBlack(false);
700
701 u32 level = 0;
702 _CPU_ISR_Disable(level);
703 if (referenceRetraceCount > retraceCount) {
704 if (frameLimiter) {
705 VIDEO_WaitVSync();
706 }
707 referenceRetraceCount = retraceCount;
708 } else if (frameLimiter && referenceRetraceCount < retraceCount - 1) {
709 referenceRetraceCount = retraceCount - 1;
710 }
711 _CPU_ISR_Restore(level);
712
713 GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE);
714 GX_SetColorUpdate(GX_TRUE);
715
716 GX_SetViewport(0, 0, vmode->fbWidth, vmode->efbHeight, 0, 1);
717}
718
719static void _drawEnd(void) {
720 GX_CopyDisp(framebuffer[whichFb], GX_TRUE);
721 GX_DrawDone();
722 VIDEO_SetNextFramebuffer(MEM_K0_TO_K1(framebuffer[whichFb]));
723 VIDEO_Flush();
724 whichFb = !whichFb;
725
726 u32 level = 0;
727 _CPU_ISR_Disable(level);
728 ++referenceRetraceCount;
729 _CPU_ISR_Restore(level);
730}
731
732static void _setFrameLimiter(struct mGUIRunner* runner, bool limit) {
733 UNUSED(runner);
734 frameLimiter = limit;
735}
736
737static uint32_t _pollInput(const struct mInputMap* map) {
738 PAD_ScanPads();
739 u16 padkeys = PAD_ButtonsHeld(0);
740
741 WPAD_ScanPads();
742 u32 wiiPad = WPAD_ButtonsHeld(0);
743 u32 ext = 0;
744 WPAD_Probe(0, &ext);
745
746#ifdef WIIDRC
747 u32 drckeys = 0;
748 if (WiiDRC_ScanPads()) {
749 drckeys = WiiDRC_ButtonsHeld();
750 }
751#endif
752
753 int keys = 0;
754 keys |= mInputMapKeyBits(map, GCN1_INPUT, padkeys, 0);
755 keys |= mInputMapKeyBits(map, GCN2_INPUT, padkeys, 0);
756 keys |= mInputMapKeyBits(map, WIIMOTE_INPUT, wiiPad, 0);
757#ifdef WIIDRC
758 keys |= mInputMapKeyBits(map, DRC_INPUT, drckeys, 0);
759#endif
760 if (ext == WPAD_EXP_CLASSIC) {
761 keys |= mInputMapKeyBits(map, CLASSIC_INPUT, wiiPad, 0);
762 }
763 int x = PAD_StickX(0);
764 int y = PAD_StickY(0);
765 int wX = WPAD_StickX(0, 0);
766 int wY = WPAD_StickY(0, 0);
767 ATTRIBUTE_UNUSED int drcX = 0;
768 ATTRIBUTE_UNUSED int drcY = 0;
769#ifdef WIIDRC
770 if (WiiDRC_Connected()) {
771 drcX = WiiDRC_lStickX();
772 drcY = WiiDRC_lStickY();
773 }
774#endif
775 if (x < -ANALOG_DEADZONE || wX < -ANALOG_DEADZONE || drcX < -ANALOG_DEADZONE) {
776 keys |= 1 << GUI_INPUT_LEFT;
777 }
778 if (x > ANALOG_DEADZONE || wX > ANALOG_DEADZONE || drcX > ANALOG_DEADZONE) {
779 keys |= 1 << GUI_INPUT_RIGHT;
780 }
781 if (y < -ANALOG_DEADZONE || wY < -ANALOG_DEADZONE || drcY < -ANALOG_DEADZONE) {
782 keys |= 1 << GUI_INPUT_DOWN;
783 }
784 if (y > ANALOG_DEADZONE || wY > ANALOG_DEADZONE || drcY > ANALOG_DEADZONE) {
785 keys |= 1 << GUI_INPUT_UP;
786 }
787
788 return keys;
789}
790
791static enum GUICursorState _pollCursor(unsigned* x, unsigned* y) {
792 ir_t ir;
793 WPAD_IR(0, &ir);
794 if (!ir.smooth_valid) {
795 return GUI_CURSOR_NOT_PRESENT;
796 }
797 *x = ir.sx;
798 *y = ir.sy;
799 WPAD_ScanPads();
800 u32 wiiPad = WPAD_ButtonsHeld(0);
801 if (wiiPad & WPAD_BUTTON_A) {
802 return GUI_CURSOR_DOWN;
803 }
804 return GUI_CURSOR_UP;
805}
806
807void _reproj(int w, int h) {
808 Mtx44 proj;
809 int top = (vmode->efbHeight * hAdjust - h) / 2;
810 int left = (vmode->fbWidth * wAdjust - w) / 2;
811 guOrtho(proj, -top, top + h, -left, left + w, 0, 300);
812 GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
813}
814
815void _reproj2(int w, int h) {
816 Mtx44 proj;
817 int top = h * (1.0 - hStretch) / 2;
818 int left = w * (1.0 - wStretch) / 2;
819 guOrtho(proj, -top, h + top, -left, w + left, 0, 300);
820 GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
821}
822
823void _guiPrepare(void) {
824 GX_SetNumTevStages(1);
825 _reproj2(vmode->fbWidth * guiScale * wAdjust, vmode->efbHeight * guiScale * hAdjust);
826}
827
828void _setup(struct mGUIRunner* runner) {
829 runner->core->setPeripheral(runner->core, mPERIPH_ROTATION, &rotation);
830 runner->core->setPeripheral(runner->core, mPERIPH_RUMBLE, &rumble);
831 runner->core->setAVStream(runner->core, &stream);
832
833 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_A, GBA_KEY_A);
834 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_B, GBA_KEY_B);
835 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_START, GBA_KEY_START);
836 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_X, GBA_KEY_SELECT);
837 _mapKey(&runner->core->inputMap, GCN2_INPUT, PAD_BUTTON_Y, GBA_KEY_SELECT);
838 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_UP, GBA_KEY_UP);
839 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_DOWN, GBA_KEY_DOWN);
840 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_LEFT, GBA_KEY_LEFT);
841 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_RIGHT, GBA_KEY_RIGHT);
842 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_TRIGGER_L, GBA_KEY_L);
843 _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_TRIGGER_R, GBA_KEY_R);
844
845 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_2, GBA_KEY_A);
846 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_1, GBA_KEY_B);
847 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_PLUS, GBA_KEY_START);
848 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_MINUS, GBA_KEY_SELECT);
849 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_RIGHT, GBA_KEY_UP);
850 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_LEFT, GBA_KEY_DOWN);
851 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_UP, GBA_KEY_LEFT);
852 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_DOWN, GBA_KEY_RIGHT);
853 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_B, GBA_KEY_L);
854 _mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_A, GBA_KEY_R);
855
856 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_A, GBA_KEY_A);
857 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_B, GBA_KEY_B);
858 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_PLUS, GBA_KEY_START);
859 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_MINUS, GBA_KEY_SELECT);
860 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_UP, GBA_KEY_UP);
861 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_DOWN, GBA_KEY_DOWN);
862 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_LEFT, GBA_KEY_LEFT);
863 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_RIGHT, GBA_KEY_RIGHT);
864 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_FULL_L, GBA_KEY_L);
865 _mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_FULL_R, GBA_KEY_R);
866
867#ifdef WIIDRC
868 _mapKey(&runner->core->inputMap, DRC_INPUT, WIIDRC_BUTTON_A, GBA_KEY_A);
869 _mapKey(&runner->core->inputMap, DRC_INPUT, WIIDRC_BUTTON_B, GBA_KEY_B);
870 _mapKey(&runner->core->inputMap, DRC_INPUT, WIIDRC_BUTTON_PLUS, GBA_KEY_START);
871 _mapKey(&runner->core->inputMap, DRC_INPUT, WIIDRC_BUTTON_MINUS, GBA_KEY_SELECT);
872 _mapKey(&runner->core->inputMap, DRC_INPUT, WIIDRC_BUTTON_UP, GBA_KEY_UP);
873 _mapKey(&runner->core->inputMap, DRC_INPUT, WIIDRC_BUTTON_DOWN, GBA_KEY_DOWN);
874 _mapKey(&runner->core->inputMap, DRC_INPUT, WIIDRC_BUTTON_LEFT, GBA_KEY_LEFT);
875 _mapKey(&runner->core->inputMap, DRC_INPUT, WIIDRC_BUTTON_RIGHT, GBA_KEY_RIGHT);
876 _mapKey(&runner->core->inputMap, DRC_INPUT, WIIDRC_BUTTON_L, GBA_KEY_L);
877 _mapKey(&runner->core->inputMap, DRC_INPUT, WIIDRC_BUTTON_R, GBA_KEY_R);
878#endif
879
880 struct mInputAxis desc = { GBA_KEY_RIGHT, GBA_KEY_LEFT, ANALOG_DEADZONE, -ANALOG_DEADZONE };
881 mInputBindAxis(&runner->core->inputMap, GCN1_INPUT, 0, &desc);
882 mInputBindAxis(&runner->core->inputMap, CLASSIC_INPUT, 0, &desc);
883 mInputBindAxis(&runner->core->inputMap, DRC_INPUT, 0, &desc);
884 desc = (struct mInputAxis) { GBA_KEY_UP, GBA_KEY_DOWN, ANALOG_DEADZONE, -ANALOG_DEADZONE };
885 mInputBindAxis(&runner->core->inputMap, GCN1_INPUT, 1, &desc);
886 mInputBindAxis(&runner->core->inputMap, CLASSIC_INPUT, 1, &desc);
887 mInputBindAxis(&runner->core->inputMap, DRC_INPUT, 1, &desc);
888
889 outputBuffer = memalign(32, TEX_W * TEX_H * BYTES_PER_PIXEL);
890 runner->core->setVideoBuffer(runner->core, outputBuffer, TEX_W);
891
892 nextAudioBuffer = 0;
893 currentAudioBuffer = 0;
894 int i;
895 for (i = 0; i < BUFFERS; ++i) {
896 audioBuffer[i].size = 0;
897 }
898 runner->core->setAudioBufferSize(runner->core, SAMPLES);
899
900 double ratio = GBAAudioCalculateRatio(1, audioSampleRate, 1);
901 blip_set_rates(runner->core->getAudioChannel(runner->core, 0), runner->core->frequency(runner->core), 48000 * ratio);
902 blip_set_rates(runner->core->getAudioChannel(runner->core, 1), runner->core->frequency(runner->core), 48000 * ratio);
903
904 frameLimiter = true;
905}
906
907void _gameUnloaded(struct mGUIRunner* runner) {
908 UNUSED(runner);
909 AUDIO_StopDMA();
910 frameLimiter = true;
911 VIDEO_SetBlack(true);
912 VIDEO_Flush();
913 VIDEO_WaitVSync();
914}
915
916void _gameLoaded(struct mGUIRunner* runner) {
917 reconfigureScreen(runner);
918 if (runner->core->platform(runner->core) == mPLATFORM_GBA && ((struct GBA*) runner->core->board)->memory.hw.devices & HW_GYRO) {
919 int i;
920 for (i = 0; i < 6; ++i) {
921 u32 result = WPAD_SetMotionPlus(0, 1);
922 if (result == WPAD_ERR_NONE) {
923 break;
924 }
925 sleep(1);
926 }
927 }
928 memset(texmem, 0, TEX_W * TEX_H * BYTES_PER_PIXEL);
929 memset(interframeTexmem, 0, TEX_W * TEX_H * BYTES_PER_PIXEL);
930 _unpaused(runner);
931}
932
933void _unpaused(struct mGUIRunner* runner) {
934 u32 level = 0;
935 VIDEO_WaitVSync();
936 _CPU_ISR_Disable(level);
937 referenceRetraceCount = retraceCount;
938 _CPU_ISR_Restore(level);
939
940 unsigned mode;
941 if (mCoreConfigGetUIntValue(&runner->config, "videoMode", &mode) && mode < VM_MAX) {
942 if (mode != videoMode) {
943 reconfigureScreen(runner);
944 }
945 }
946 if (mCoreConfigGetUIntValue(&runner->config, "screenMode", &mode) && mode < SM_MAX) {
947 screenMode = mode;
948 }
949 if (mCoreConfigGetUIntValue(&runner->config, "filter", &mode) && mode < FM_MAX) {
950 filterMode = mode;
951 switch (mode) {
952 case FM_NEAREST:
953 case FM_LINEAR_2x:
954 default:
955 GX_InitTexObjFilterMode(&tex, GX_NEAR, GX_NEAR);
956 GX_InitTexObjFilterMode(&interframeTex, GX_NEAR, GX_NEAR);
957 break;
958 case FM_LINEAR_1x:
959 GX_InitTexObjFilterMode(&tex, GX_LINEAR, GX_LINEAR);
960 GX_InitTexObjFilterMode(&interframeTex, GX_LINEAR, GX_LINEAR);
961 break;
962 }
963 }
964 int fakeBool;
965 if (mCoreConfigGetIntValue(&runner->config, "interframeBlending", &fakeBool)) {
966 interframeBlending = fakeBool;
967 }
968 if (mCoreConfigGetIntValue(&runner->config, "sgb.borderCrop", &fakeBool)) {
969 sgbCrop = fakeBool;
970 }
971
972 float stretch;
973 if (mCoreConfigGetFloatValue(&runner->config, "stretchWidth", &stretch)) {
974 wStretch = fminf(1.0f, fmaxf(0.5f, stretch));
975 }
976 if (mCoreConfigGetFloatValue(&runner->config, "stretchHeight", &stretch)) {
977 hStretch = fminf(1.0f, fmaxf(0.5f, stretch));
978 }
979}
980
981void _prepareForFrame(struct mGUIRunner* runner) {
982 if (interframeBlending) {
983 memcpy(interframeTexmem, texmem, TEX_W * TEX_H * BYTES_PER_PIXEL);
984 }
985}
986
987void _drawFrame(struct mGUIRunner* runner, bool faded) {
988 runner->core->desiredVideoDimensions(runner->core, &corew, &coreh);
989 uint32_t color = 0xFFFFFF3F;
990 if (!faded) {
991 color |= 0xC0;
992 }
993 size_t x, y;
994 uint64_t* texdest = (uint64_t*) texmem;
995 uint64_t* texsrc = (uint64_t*) outputBuffer;
996 for (y = 0; y < coreh; y += 4) {
997 for (x = 0; x < corew >> 2; ++x) {
998 texdest[0 + x * 4 + y * 64] = texsrc[0 + x + y * 64];
999 texdest[1 + x * 4 + y * 64] = texsrc[64 + x + y * 64];
1000 texdest[2 + x * 4 + y * 64] = texsrc[128 + x + y * 64];
1001 texdest[3 + x * 4 + y * 64] = texsrc[192 + x + y * 64];
1002 }
1003 }
1004 DCFlushRange(texdest, TEX_W * TEX_H * BYTES_PER_PIXEL);
1005 if (interframeBlending) {
1006 DCFlushRange(interframeTexmem, TEX_W * TEX_H * BYTES_PER_PIXEL);
1007 }
1008
1009 if (faded || interframeBlending) {
1010 GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_NOOP);
1011 } else {
1012 GX_SetBlendMode(GX_BM_NONE, GX_BL_ONE, GX_BL_ZERO, GX_LO_NOOP);
1013 }
1014 GX_InvalidateTexAll();
1015 if (interframeBlending) {
1016 GX_LoadTexObj(&interframeTex, GX_TEXMAP0);
1017 GX_LoadTexObj(&tex, GX_TEXMAP1);
1018 GX_SetNumTevStages(2);
1019 } else {
1020 GX_LoadTexObj(&tex, GX_TEXMAP0);
1021 GX_SetNumTevStages(1);
1022 }
1023
1024 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0);
1025 s16 vertWidth = corew;
1026 s16 vertHeight = coreh;
1027
1028 if (filterMode == FM_LINEAR_2x) {
1029 Mtx44 proj;
1030 guOrtho(proj, 0, vmode->efbHeight, 0, vmode->fbWidth, 0, 300);
1031 GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
1032
1033 GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
1034 GX_Position2s16(0, TEX_H * 2);
1035 GX_Color1u32(0xFFFFFFFF);
1036 GX_TexCoord2f32(0, 1);
1037
1038 GX_Position2s16(TEX_W * 2, TEX_H * 2);
1039 GX_Color1u32(0xFFFFFFFF);
1040 GX_TexCoord2f32(1, 1);
1041
1042 GX_Position2s16(TEX_W * 2, 0);
1043 GX_Color1u32(0xFFFFFFFF);
1044 GX_TexCoord2f32(1, 0);
1045
1046 GX_Position2s16(0, 0);
1047 GX_Color1u32(0xFFFFFFFF);
1048 GX_TexCoord2f32(0, 0);
1049 GX_End();
1050
1051 GX_SetTexCopySrc(0, 0, TEX_W * 2, TEX_H * 2);
1052 GX_SetTexCopyDst(TEX_W * 2, TEX_H * 2, GX_TF_RGB565, GX_FALSE);
1053 GX_CopyTex(rescaleTexmem, GX_TRUE);
1054 GX_LoadTexObj(&rescaleTex, GX_TEXMAP0);
1055 GX_SetNumTevStages(1);
1056 if (!faded) {
1057 GX_SetBlendMode(GX_BM_NONE, GX_BL_ONE, GX_BL_ZERO, GX_LO_NOOP);
1058 }
1059 }
1060
1061 if (screenMode == SM_PA) {
1062 unsigned factorWidth = corew;
1063 unsigned factorHeight = coreh;
1064 if (sgbCrop && factorWidth == 256 && factorHeight == 224) {
1065 factorWidth = GB_VIDEO_HORIZONTAL_PIXELS;
1066 factorHeight = GB_VIDEO_VERTICAL_PIXELS;
1067 }
1068
1069 int hfactor = (vmode->fbWidth * wStretch) / (factorWidth * wAdjust);
1070 int vfactor = (vmode->efbHeight * hStretch) / (factorHeight * hAdjust);
1071 if (hfactor > vfactor) {
1072 scaleFactor = vfactor;
1073 } else {
1074 scaleFactor = hfactor;
1075 }
1076
1077 vertWidth *= scaleFactor;
1078 vertHeight *= scaleFactor;
1079
1080 _reproj(corew * scaleFactor, coreh * scaleFactor);
1081 } else {
1082 _reproj2(corew, coreh);
1083 }
1084
1085 GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
1086 GX_Position2s16(0, vertHeight);
1087 GX_Color1u32(color);
1088 GX_TexCoord2f32(0, coreh / (float) TEX_H);
1089
1090 GX_Position2s16(vertWidth, vertHeight);
1091 GX_Color1u32(color);
1092 GX_TexCoord2f32(corew / (float) TEX_W, coreh / (float) TEX_H);
1093
1094 GX_Position2s16(vertWidth, 0);
1095 GX_Color1u32(color);
1096 GX_TexCoord2f32(corew / (float) TEX_W, 0);
1097
1098 GX_Position2s16(0, 0);
1099 GX_Color1u32(color);
1100 GX_TexCoord2f32(0, 0);
1101 GX_End();
1102}
1103
1104uint16_t _pollGameInput(struct mGUIRunner* runner) {
1105 UNUSED(runner);
1106 PAD_ScanPads();
1107 u16 padkeys = PAD_ButtonsHeld(0);
1108 WPAD_ScanPads();
1109 u32 wiiPad = WPAD_ButtonsHeld(0);
1110 u32 ext = 0;
1111 WPAD_Probe(0, &ext);
1112#ifdef WIIDRC
1113 u32 drckeys = 0;
1114 if (WiiDRC_ScanPads()) {
1115 drckeys = WiiDRC_ButtonsHeld();
1116 }
1117#endif
1118 uint16_t keys = mInputMapKeyBits(&runner->core->inputMap, GCN1_INPUT, padkeys, 0);
1119 keys |= mInputMapKeyBits(&runner->core->inputMap, GCN2_INPUT, padkeys, 0);
1120 keys |= mInputMapKeyBits(&runner->core->inputMap, WIIMOTE_INPUT, wiiPad, 0);
1121#ifdef WIIDRC
1122 keys |= mInputMapKeyBits(&runner->core->inputMap, DRC_INPUT, drckeys, 0);
1123#endif
1124
1125 enum GBAKey angles = mInputMapAxis(&runner->core->inputMap, GCN1_INPUT, 0, PAD_StickX(0));
1126 if (angles != GBA_KEY_NONE) {
1127 keys |= 1 << angles;
1128 }
1129 angles = mInputMapAxis(&runner->core->inputMap, GCN1_INPUT, 1, PAD_StickY(0));
1130 if (angles != GBA_KEY_NONE) {
1131 keys |= 1 << angles;
1132 }
1133 if (ext == WPAD_EXP_CLASSIC) {
1134 keys |= mInputMapKeyBits(&runner->core->inputMap, CLASSIC_INPUT, wiiPad, 0);
1135 angles = mInputMapAxis(&runner->core->inputMap, CLASSIC_INPUT, 0, WPAD_StickX(0, 0));
1136 if (angles != GBA_KEY_NONE) {
1137 keys |= 1 << angles;
1138 }
1139 angles = mInputMapAxis(&runner->core->inputMap, CLASSIC_INPUT, 1, WPAD_StickY(0, 0));
1140 if (angles != GBA_KEY_NONE) {
1141 keys |= 1 << angles;
1142 }
1143 }
1144#ifdef WIIDRC
1145 if (WiiDRC_Connected()) {
1146 keys |= mInputMapKeyBits(&runner->core->inputMap, DRC_INPUT, drckeys, 0);
1147 angles = mInputMapAxis(&runner->core->inputMap, DRC_INPUT, 0, WiiDRC_lStickX());
1148 if (angles != GBA_KEY_NONE) {
1149 keys |= 1 << angles;
1150 }
1151 angles = mInputMapAxis(&runner->core->inputMap, DRC_INPUT, 1, WiiDRC_lStickY());
1152 if (angles != GBA_KEY_NONE) {
1153 keys |= 1 << angles;
1154 }
1155 }
1156#endif
1157
1158 return keys;
1159}
1160
1161void _incrementScreenMode(struct mGUIRunner* runner) {
1162 UNUSED(runner);
1163 int mode = screenMode | (filterMode << 1);
1164 ++mode;
1165 screenMode = mode % SM_MAX;
1166 filterMode = (mode >> 1) % FM_MAX;
1167 mCoreConfigSetUIntValue(&runner->config, "screenMode", screenMode);
1168 mCoreConfigSetUIntValue(&runner->config, "filter", filterMode);
1169 switch (filterMode) {
1170 case FM_NEAREST:
1171 case FM_LINEAR_2x:
1172 default:
1173 GX_InitTexObjFilterMode(&tex, GX_NEAR, GX_NEAR);
1174 GX_InitTexObjFilterMode(&interframeTex, GX_NEAR, GX_NEAR);
1175 break;
1176 case FM_LINEAR_1x:
1177 GX_InitTexObjFilterMode(&tex, GX_LINEAR, GX_LINEAR);
1178 GX_InitTexObjFilterMode(&interframeTex, GX_LINEAR, GX_LINEAR);
1179 break;
1180 }
1181}
1182
1183void _setRumble(struct mRumble* rumble, int enable) {
1184 UNUSED(rumble);
1185 WPAD_Rumble(0, enable);
1186 if (enable) {
1187 PAD_ControlMotor(0, PAD_MOTOR_RUMBLE);
1188 } else {
1189 PAD_ControlMotor(0, PAD_MOTOR_STOP);
1190 }
1191}
1192
1193void _sampleRotation(struct mRotationSource* source) {
1194 UNUSED(source);
1195 vec3w_t accel;
1196 WPAD_Accel(0, &accel);
1197 // These are swapped
1198 tiltX = (0x1EA - accel.y) << 22;
1199 tiltY = (0x1EA - accel.x) << 22;
1200
1201 // This doesn't seem to work at all with -TR remotes
1202 struct expansion_t exp;
1203 WPAD_Expansion(0, &exp);
1204 if (exp.type != EXP_MOTION_PLUS) {
1205 return;
1206 }
1207 gyroZ = exp.mp.rz - 0x1FA0;
1208 gyroZ <<= 18;
1209}
1210
1211int32_t _readTiltX(struct mRotationSource* source) {
1212 UNUSED(source);
1213 return tiltX;
1214}
1215
1216int32_t _readTiltY(struct mRotationSource* source) {
1217 UNUSED(source);
1218 return tiltY;
1219}
1220
1221int32_t _readGyroZ(struct mRotationSource* source) {
1222 UNUSED(source);
1223 return gyroZ;
1224}
1225
1226static s8 WPAD_StickX(u8 chan, u8 right) {
1227 struct expansion_t exp;
1228 WPAD_Expansion(chan, &exp);
1229 struct joystick_t* js = NULL;
1230
1231 switch (exp.type) {
1232 case WPAD_EXP_NUNCHUK:
1233 case WPAD_EXP_GUITARHERO3:
1234 if (right == 0) {
1235 js = &exp.nunchuk.js;
1236 }
1237 break;
1238 case WPAD_EXP_CLASSIC:
1239 if (right == 0) {
1240 js = &exp.classic.ljs;
1241 } else {
1242 js = &exp.classic.rjs;
1243 }
1244 break;
1245 default:
1246 break;
1247 }
1248
1249 if (!js) {
1250 return 0;
1251 }
1252 int centered = (int) js->pos.x - (int) js->center.x;
1253 int range = (int) js->max.x - (int) js->min.x;
1254 int value = (centered * 0xFF) / range;
1255 if (value > 0x7F) {
1256 return 0x7F;
1257 }
1258 if (value < -0x80) {
1259 return -0x80;
1260 }
1261 return value;
1262}
1263
1264static s8 WPAD_StickY(u8 chan, u8 right) {
1265 struct expansion_t exp;
1266 WPAD_Expansion(chan, &exp);
1267 struct joystick_t* js = NULL;
1268
1269 switch (exp.type) {
1270 case WPAD_EXP_NUNCHUK:
1271 case WPAD_EXP_GUITARHERO3:
1272 if (right == 0) {
1273 js = &exp.nunchuk.js;
1274 }
1275 break;
1276 case WPAD_EXP_CLASSIC:
1277 if (right == 0) {
1278 js = &exp.classic.ljs;
1279 } else {
1280 js = &exp.classic.rjs;
1281 }
1282 break;
1283 default:
1284 break;
1285 }
1286
1287 if (!js) {
1288 return 0;
1289 }
1290 int centered = (int) js->pos.y - (int) js->center.y;
1291 int range = (int) js->max.y - (int) js->min.y;
1292 int value = (centered * 0xFF) / range;
1293 if (value > 0x7F) {
1294 return 0x7F;
1295 }
1296 if (value < -0x80) {
1297 return -0x80;
1298 }
1299 return value;
1300}
1301
1302void _retraceCallback(u32 count) {
1303 u32 level = 0;
1304 _CPU_ISR_Disable(level);
1305 retraceCount = count;
1306 _CPU_ISR_Restore(level);
1307}