* use alignment for clipped/wrapped text
[open-ps2-loader.git] / src / gui.c
blob3dc1b8098f532e78f1f0bd64d4f3c742d296b186
1 /*
2 Copyright 2010, Volca
3 Licenced under Academic Free License version 3.0
4 Review OpenUsbLd README & LICENSE files for further details.
5 */
7 #include "include/usbld.h"
8 #include "include/gui.h"
9 #include "include/renderman.h"
10 #include "include/menusys.h"
11 #include "include/fntsys.h"
12 #include "include/ioman.h"
13 #include "include/lang.h"
14 #include "include/themes.h"
15 #include "include/pad.h"
16 #include "include/util.h"
17 #include "include/config.h"
18 #include "include/system.h"
20 #include <stdlib.h>
21 #include <libvux.h>
23 static int gScheduledOps;
24 static int gCompletedOps;
25 static int gTerminate;
26 static int gInitComplete;
28 static gui_callback_t gFrameHook;
30 static s32 gSemaId;
31 static s32 gGUILockSemaId;
32 static ee_sema_t gQueueSema;
34 static int screenWidth;
35 static float wideScreenScale;
36 static int screenHeight;
38 // forward decl.
39 static void guiShow();
41 #ifdef __DEBUG
43 #include <timer.h>
45 #define CLOCKS_PER_MILISEC 147456
47 // debug version displays an FPS meter
48 static u32 curtime = 0;
49 static u32 time_since_last = 0;
50 static u32 time_render = 0;
52 #endif
54 struct gui_update_list_t {
55 struct gui_update_t* item;
56 struct gui_update_list_t* next;
59 struct gui_update_list_t *gUpdateList;
60 struct gui_update_list_t *gUpdateEnd;
62 typedef struct {
63 void (*handleInput)(void);
64 void (*renderScreen)(void);
65 short inMenu;
66 } gui_screen_handler_t;
68 static gui_screen_handler_t screenHandlers[] = {{ &menuHandleInputMain, &menuRenderMain, 0 },
69 { &menuHandleInputMenu, &menuRenderMenu, 1 },
70 { &menuHandleInputInfo, &menuRenderInfo, 1 } };
72 // default screen handler (menu screen)
73 static gui_screen_handler_t *screenHandler = &screenHandlers[GUI_SCREEN_MENU];
75 // screen transition handling
76 static gui_screen_handler_t *screenHandlerTarget = NULL;
77 static int transIndex, transMax, transitionX, transitionY;
79 // Helper perlin noise data
80 #define PLASMA_H 32
81 #define PLASMA_W 32
82 #define PLASMA_ROWS_PER_FRAME 6
83 #define FADE_SIZE 256
85 static GSTEXTURE gBackgroundTex;
86 static int pperm[512];
87 static float fadetbl[FADE_SIZE + 1];
89 static VU_VECTOR pgrad3[12] = {{ 1, 1, 0, 1 }, { -1, 1, 0, 1 }, { 1, -1, 0, 1 }, { -1, -1, 0, 1 },
90 { 1, 0, 1, 1 }, { -1, 0, 1, 1 }, { 1, 0, -1, 1 }, { -1, 0, -1, 1 },
91 { 0, 1, 1, 1 }, { 0, -1, 1, 1 }, { 0, 1, -1, 1 }, { 0, -1, -1, 1 } };
93 void guiReloadScreenExtents() {
94 rmGetScreenExtents(&screenWidth, &screenHeight);
97 void guiInit(void) {
98 guiFrameId = 0;
99 guiInactiveFrames = 0;
101 gFrameHook = NULL;
102 gTerminate = 0;
103 gInitComplete = 0;
104 gScheduledOps = 0;
105 gCompletedOps = 0;
107 gUpdateList = NULL;
108 gUpdateEnd = NULL;
110 gQueueSema.init_count = 1;
111 gQueueSema.max_count = 1;
112 gQueueSema.option = 0;
114 gSemaId = CreateSema(&gQueueSema);
115 gGUILockSemaId = CreateSema(&gQueueSema);
117 guiReloadScreenExtents();
119 // background texture - for perlin
120 gBackgroundTex.Width = PLASMA_W;
121 gBackgroundTex.Height = PLASMA_H;
122 gBackgroundTex.Mem = memalign(128, PLASMA_W * PLASMA_H * 4);
123 gBackgroundTex.PSM = GS_PSM_CT32;
124 gBackgroundTex.Filter = GS_FILTER_LINEAR;
125 gBackgroundTex.Vram = 0;
126 gBackgroundTex.VramClut = 0;
127 gBackgroundTex.Clut = NULL;
129 wideScreenScale = 1.0f;
131 // Precalculate the values for the perlin noise plasma
132 int i;
133 for (i = 0; i < 256; ++i) {
134 pperm[i] = rand() % 256;
135 pperm[i + 256] = pperm[i];
138 for (i = 0; i <= FADE_SIZE; ++i) {
139 float t = (float) (i) / FADE_SIZE;
141 fadetbl[i] = t * t * t * (t * (t * 6.0 - 15.0) + 10.0);
145 void guiEnd() {
146 if (gBackgroundTex.Mem)
147 free(gBackgroundTex.Mem);
149 DeleteSema(gSemaId);
150 DeleteSema(gGUILockSemaId);
153 void guiLock(void) {
154 WaitSema(gGUILockSemaId);
157 void guiUnlock(void) {
158 SignalSema(gGUILockSemaId);
161 void guiStartFrame(void) {
162 #ifdef __DEBUG
163 u32 newtime = cpu_ticks() / CLOCKS_PER_MILISEC;
164 time_since_last = newtime - curtime;
165 curtime = newtime;
166 #endif
168 guiLock();
169 rmStartFrame();
170 guiFrameId++;
173 void guiEndFrame(void) {
174 guiUnlock();
175 rmFlush();
177 #ifdef __DEBUG
178 u32 newtime = cpu_ticks() / CLOCKS_PER_MILISEC;
179 time_render = newtime - curtime;
180 #endif
182 rmEndFrame();
185 void guiShowAbout() {
186 char OPLVersion[64];
187 snprintf(OPLVersion, 64, _l(_STR_OUL_VER), USBLD_VERSION);
189 #ifdef VMC
190 strcat(OPLVersion, " VMC");
191 #endif
192 #ifdef __CHILDPROOF
193 strcat(OPLVersion, " CHILDPROOF");
194 #endif
196 diaSetLabel(diaAbout, 1, OPLVersion);
198 diaExecuteDialog(diaAbout, -1, 1, NULL);
201 void guiShowConfig() {
202 // configure the enumerations
203 const char* deviceNames[] = { _l(_STR_USB_GAMES), _l(_STR_NET_GAMES), _l(_STR_HDD_GAMES), NULL };
204 const char* deviceModes[] = { _l(_STR_OFF), _l(_STR_MANUAL), _l(_STR_AUTO), NULL };
206 diaSetEnum(diaConfig, CFG_DEFDEVICE, deviceNames);
207 diaSetEnum(diaConfig, CFG_USBMODE, deviceModes);
208 diaSetEnum(diaConfig, CFG_HDDMODE, deviceModes);
209 diaSetEnum(diaConfig, CFG_ETHMODE, deviceModes);
210 diaSetEnum(diaConfig, CFG_APPMODE, deviceModes);
212 diaSetInt(diaConfig, CFG_DEBUG, gDisableDebug);
213 diaSetString(diaConfig, CFG_EXITTO, gExitPath);
214 diaSetInt(diaConfig, CFG_DANDROP, gEnableDandR);
215 diaSetInt(diaConfig, CFG_HDDSPINDOWN, gHDDSpindown);
216 diaSetInt(diaConfig, CFG_CHECKUSBFRAG, gCheckUSBFragmentation);
217 diaSetInt(diaConfig, CFG_USBDELAY, gUSBDelay);
218 diaSetString(diaConfig, CFG_USBPREFIX, gUSBPrefix);
219 diaSetInt(diaConfig, CFG_LASTPLAYED, gRememberLastPlayed);
220 diaSetInt(diaConfig, CFG_DEFDEVICE, gDefaultDevice);
221 diaSetInt(diaConfig, CFG_USBMODE, gUSBStartMode);
222 diaSetInt(diaConfig, CFG_HDDMODE, gHDDStartMode);
223 diaSetInt(diaConfig, CFG_ETHMODE, gETHStartMode);
224 diaSetInt(diaConfig, CFG_APPMODE, gAPPStartMode);
226 int ret = diaExecuteDialog(diaConfig, -1, 1, NULL);
227 if (ret) {
228 diaGetString(diaConfig, CFG_EXITTO, gExitPath);
229 diaGetInt(diaConfig, CFG_DEBUG, &gDisableDebug);
230 diaGetInt(diaConfig, CFG_DANDROP, &gEnableDandR);
231 diaGetInt(diaConfig, CFG_HDDSPINDOWN, &gHDDSpindown);
232 diaGetInt(diaConfig, CFG_CHECKUSBFRAG, &gCheckUSBFragmentation);
233 diaGetInt(diaConfig, CFG_USBDELAY, &gUSBDelay);
234 diaGetString(diaConfig, CFG_USBPREFIX, gUSBPrefix);
235 diaGetInt(diaConfig, CFG_LASTPLAYED, &gRememberLastPlayed);
236 diaGetInt(diaConfig, CFG_DEFDEVICE, &gDefaultDevice);
237 diaGetInt(diaConfig, CFG_USBMODE, &gUSBStartMode);
238 diaGetInt(diaConfig, CFG_HDDMODE, &gHDDStartMode);
239 diaGetInt(diaConfig, CFG_ETHMODE, &gETHStartMode);
240 diaGetInt(diaConfig, CFG_APPMODE, &gAPPStartMode);
242 applyConfig(-1, -1, gVMode, gVSync);
246 static int curTheme = -1;
248 static int guiUIUpdater(int modified) {
249 if (modified) {
250 int temp;
251 diaGetInt(diaUIConfig, UICFG_THEME, &temp);
252 if (temp != curTheme) {
253 curTheme = temp;
254 if (temp == 0) {
255 diaUIConfig[32].type = UI_COLOUR; // Must be correctly set before doing the diaS/GetColor !!
256 diaUIConfig[36].type = UI_COLOUR;
257 diaUIConfig[40].type = UI_COLOUR;
258 diaUIConfig[44].type = UI_COLOUR;
259 diaSetColor(diaUIConfig, UICFG_BGCOL, gDefaultBgColor);
260 diaSetColor(diaUIConfig, UICFG_UICOL, gDefaultUITextColor);
261 diaSetColor(diaUIConfig, UICFG_TXTCOL, gDefaultTextColor);
262 diaSetColor(diaUIConfig, UICFG_SELCOL, gDefaultSelTextColor);
263 } else if (temp == thmGetGuiValue()) {
264 diaUIConfig[32].type = UI_COLOUR;
265 diaUIConfig[36].type = UI_COLOUR;
266 diaUIConfig[40].type = UI_COLOUR;
267 diaUIConfig[44].type = UI_COLOUR;
268 diaSetColor(diaUIConfig, UICFG_BGCOL, gTheme->bgColor);
269 diaSetU64Color(diaUIConfig, UICFG_UICOL, gTheme->uiTextColor);
270 diaSetU64Color(diaUIConfig, UICFG_TXTCOL, gTheme->textColor);
271 diaSetU64Color(diaUIConfig, UICFG_SELCOL, gTheme->selTextColor);
272 } else {
273 diaUIConfig[32].type = UI_SPACER;
274 diaUIConfig[36].type = UI_SPACER;
275 diaUIConfig[40].type = UI_SPACER;
276 diaUIConfig[44].type = UI_SPACER;
279 temp = !temp;
280 diaSetEnabled(diaUIConfig, UICFG_BGCOL, temp);
281 diaSetEnabled(diaUIConfig, UICFG_UICOL, temp);
282 diaSetEnabled(diaUIConfig, UICFG_TXTCOL, temp);
283 diaSetEnabled(diaUIConfig, UICFG_SELCOL, temp);
287 return 0;
290 void guiShowUIConfig() {
291 curTheme = -1;
293 // configure the enumerations
294 const char* scrollSpeeds[] = { _l(_STR_SLOW), _l(_STR_MEDIUM), _l(_STR_FAST), NULL };
295 const char* vmodeNames[] = { "AUTO", "PAL", "NTSC", NULL };
296 diaSetEnum(diaUIConfig, UICFG_SCROLL, scrollSpeeds);
297 diaSetEnum(diaUIConfig, UICFG_THEME, (const char **) thmGetGuiList());
298 diaSetEnum(diaUIConfig, UICFG_LANG, (const char **) lngGetGuiList());
299 diaSetEnum(diaUIConfig, UICFG_VMODE, vmodeNames);
301 diaSetInt(diaUIConfig, UICFG_SCROLL, gScrollSpeed);
302 diaSetInt(diaUIConfig, UICFG_THEME, thmGetGuiValue());
303 diaSetInt(diaUIConfig, UICFG_LANG, lngGetGuiValue());
304 guiUIUpdater(1);
305 diaSetInt(diaUIConfig, UICFG_AUTOSORT, gAutosort);
306 diaSetInt(diaUIConfig, UICFG_AUTOREFRESH, gAutoRefresh);
307 diaSetInt(diaUIConfig, UICFG_INFOPAGE, gUseInfoScreen);
308 diaSetInt(diaUIConfig, UICFG_COVERART, gEnableArt);
309 diaSetInt(diaUIConfig, UICFG_WIDESCREEN, gWideScreen);
310 diaSetInt(diaUIConfig, UICFG_VMODE, gVMode);
311 diaSetInt(diaUIConfig, UICFG_VSYNC, gVSync);
313 int ret = diaExecuteDialog(diaUIConfig, -1, 1, guiUIUpdater);
314 if (ret) {
315 int themeID = -1, langID = -1, newVMode = gVMode, newVSync = gVSync;
316 diaGetInt(diaUIConfig, UICFG_SCROLL, &gScrollSpeed);
317 diaGetInt(diaUIConfig, UICFG_LANG, &langID);
318 diaGetInt(diaUIConfig, UICFG_THEME, &themeID);
319 if (themeID == 0) {
320 diaGetColor(diaUIConfig, UICFG_BGCOL, gDefaultBgColor);
321 diaGetColor(diaUIConfig, UICFG_UICOL, gDefaultUITextColor);
322 diaGetColor(diaUIConfig, UICFG_TXTCOL, gDefaultTextColor);
323 diaGetColor(diaUIConfig, UICFG_SELCOL, gDefaultSelTextColor);
325 diaGetInt(diaUIConfig, UICFG_AUTOSORT, &gAutosort);
326 diaGetInt(diaUIConfig, UICFG_AUTOREFRESH, &gAutoRefresh);
327 diaGetInt(diaUIConfig, UICFG_INFOPAGE, &gUseInfoScreen);
328 diaGetInt(diaUIConfig, UICFG_COVERART, &gEnableArt);
329 diaGetInt(diaUIConfig, UICFG_WIDESCREEN, &gWideScreen);
330 diaGetInt(diaUIConfig, UICFG_VMODE, &newVMode);
331 diaGetInt(diaUIConfig, UICFG_VSYNC, &newVSync);
333 applyConfig(themeID, langID, newVMode, newVSync);
337 void guiShowIPConfig() {
338 size_t i;
339 // upload current values
340 for (i = 0; i < 4; ++i) {
341 diaSetInt(diaIPConfig, 2 + i, ps2_ip[i]);
342 diaSetInt(diaIPConfig, 6 + i, ps2_netmask[i]);
343 diaSetInt(diaIPConfig, 10 + i, ps2_gateway[i]);
344 diaSetInt(diaIPConfig, 14 + i, pc_ip[i]);
347 diaSetInt(diaIPConfig, 18, gPCPort);
348 diaSetString(diaIPConfig, 19, gPCShareName);
349 diaSetString(diaIPConfig, 20, gPCUserName);
350 diaSetString(diaIPConfig, 21, gPCPassword);
352 // show dialog
353 if (diaExecuteDialog(diaIPConfig, -1, 1, NULL)) {
354 // Ok pressed, store values
355 for (i = 0; i < 4; ++i) {
356 diaGetInt(diaIPConfig, 2 + i, &ps2_ip[i]);
357 diaGetInt(diaIPConfig, 6 + i, &ps2_netmask[i]);
358 diaGetInt(diaIPConfig, 10 + i, &ps2_gateway[i]);
359 diaGetInt(diaIPConfig, 14 + i, &pc_ip[i]);
362 diaGetInt(diaIPConfig, 18, &gPCPort);
363 diaGetString(diaIPConfig, 19, gPCShareName);
364 diaGetString(diaIPConfig, 20, gPCUserName);
365 diaGetString(diaIPConfig, 21, gPCPassword);
366 gIPConfigChanged = 1;
368 applyConfig(-1, -1, gVMode, gVSync);
372 int guiShowKeyboard(char* value, int maxLength) {
373 char tmp[maxLength];
374 strncpy(tmp, value, maxLength);
376 int result = diaShowKeyb(tmp, maxLength);
377 if (result) {
378 strncpy(value, tmp, maxLength);
379 value[maxLength - 1] = '\0';
382 return result;
385 #ifdef VMC
386 typedef struct { // size = 76
387 int VMC_status; // 0=available, 1=busy
388 int VMC_error;
389 int VMC_progress;
390 char VMC_msg[64];
391 }statusVMCparam_t;
393 #define OPERATION_CREATE 0
394 #define OPERATION_CREATING 1
395 #define OPERATION_ABORTING 2
396 #define OPERATION_ENDING 3
397 #define OPERATION_END 4
399 static short vmc_refresh;
400 static int vmc_operation;
401 static statusVMCparam_t vmc_status;
403 int guiVmcNameHandler(char* text, int maxLen) {
404 int result = diaShowKeyb(text, maxLen);
406 if (result)
407 vmc_refresh = 1;
409 return result;
412 static int guiRefreshVMCConfig(item_list_t *support, char* name) {
413 int size = support->itemCheckVMC(name, 0);
415 if (size != -1) {
416 diaSetLabel(diaVMC, VMC_BUTTON_CREATE, _l(_STR_MODIFY));
417 diaSetLabel(diaVMC, VMC_STATUS, _l(_STR_VMC_FILE_EXISTS));
419 if (size == 8)
420 diaSetInt(diaVMC, VMC_SIZE, 0);
421 else if (size == 16)
422 diaSetInt(diaVMC, VMC_SIZE, 1);
423 else if (size == 32)
424 diaSetInt(diaVMC, VMC_SIZE, 2);
425 else if (size == 64)
426 diaSetInt(diaVMC, VMC_SIZE, 3);
427 else {
428 diaSetInt(diaVMC, VMC_SIZE, 0);
429 diaSetLabel(diaVMC, VMC_STATUS, _l(_STR_VMC_FILE_ERROR));
432 if (gEnableDandR) {
433 diaSetEnabled(diaVMC, VMC_SIZE, 1);
434 diaVMC[20].type = UI_SPLITTER;
436 else {
437 diaSetEnabled(diaVMC, VMC_SIZE, 0);
438 diaVMC[20].type = UI_TERMINATOR;
441 else {
442 diaSetLabel(diaVMC, VMC_BUTTON_CREATE, _l(_STR_CREATE));
443 diaSetLabel(diaVMC, VMC_STATUS, _l(_STR_VMC_FILE_NEW));
445 diaSetInt(diaVMC, VMC_SIZE, 0);
446 diaSetEnabled(diaVMC, VMC_SIZE, 1);
447 diaVMC[20].type = UI_TERMINATOR;
450 return size;
453 static int guiVMCUpdater(int modified) {
454 if (vmc_refresh) {
455 vmc_refresh = 0;
456 return VMC_REFRESH;
459 if ((vmc_operation == OPERATION_CREATING) || (vmc_operation == OPERATION_ABORTING)) {
460 int result = fileXioDevctl("genvmc:", 0xC0DE0003, NULL, 0, (void*) &vmc_status, sizeof(vmc_status));
461 if (result == 0) {
462 diaSetLabel(diaVMC, VMC_STATUS, vmc_status.VMC_msg);
463 diaSetInt(diaVMC, VMC_PROGRESS, vmc_status.VMC_progress);
465 if (vmc_status.VMC_error != 0)
466 LOG("genvmc updater: %d\n", vmc_status.VMC_error);
468 if (vmc_status.VMC_status == 0x00) {
469 diaSetLabel(diaVMC, VMC_BUTTON_CREATE, _l(_STR_OK));
470 vmc_operation = OPERATION_ENDING;
471 return VMC_BUTTON_CREATE;
474 else
475 LOG("status result: %d\n", result);
478 return 0;
481 static int guiShowVMCConfig(int id, item_list_t *support, char *VMCName, int slot, int validate) {
482 int result = validate ? VMC_BUTTON_CREATE : 0;
483 char vmc[32];
485 if (strlen(VMCName))
486 strncpy(vmc, VMCName, 32);
487 else {
488 if (validate)
489 return 1; // nothing to validate if no user input
491 char* startup = support->itemGetStartup(id);
492 snprintf(vmc, 32, "%s_%d", startup, slot);
495 vmc_refresh = 0;
496 vmc_operation = OPERATION_CREATE;
497 diaSetEnabled(diaVMC, VMC_NAME, 1);
498 diaSetEnabled(diaVMC, VMC_SIZE, 1);
499 diaSetInt(diaVMC, VMC_PROGRESS, 0);
501 const char* VMCSizes[] = {"8 Mb", "16 Mb", "32 Mb", "64 Mb", NULL};
502 diaSetEnum(diaVMC, VMC_SIZE, VMCSizes);
503 int size = guiRefreshVMCConfig(support, vmc);
504 diaSetString(diaVMC, VMC_NAME, vmc);
506 do {
507 if (result == VMC_BUTTON_CREATE) {
508 if (vmc_operation == OPERATION_CREATE) { // User start creation of VMC
509 int sizeUI;
510 diaGetInt(diaVMC, VMC_SIZE, &sizeUI);
511 if (sizeUI == 1)
512 sizeUI = 16;
513 else if (sizeUI == 2)
514 sizeUI = 32;
515 else if (sizeUI == 3)
516 sizeUI = 64;
517 else
518 sizeUI = 8;
520 if (sizeUI != size) {
521 support->itemCheckVMC(vmc, sizeUI);
523 diaSetEnabled(diaVMC, VMC_NAME, 0);
524 diaSetEnabled(diaVMC, VMC_SIZE, 0);
525 diaSetLabel(diaVMC, VMC_BUTTON_CREATE, _l(_STR_ABORT));
526 vmc_operation = OPERATION_CREATING;
528 else
529 break;
531 else if (vmc_operation == OPERATION_ENDING) {
532 if (validate)
533 break; // directly close VMC config dialog
535 vmc_operation = OPERATION_END;
537 else if (vmc_operation == OPERATION_END) { // User closed creation dialog of VMC
538 break;
540 else if (vmc_operation == OPERATION_CREATING) { // User canceled creation of VMC
541 fileXioDevctl("genvmc:", 0xC0DE0002, NULL, 0, NULL, 0);
542 vmc_operation = OPERATION_ABORTING;
545 else if (result == VMC_BUTTON_DELETE) {
546 if (guiMsgBox(_l(_STR_DELETE_WARNING), 1, diaVMC)) {
547 support->itemCheckVMC(vmc, -1);
548 diaSetString(diaVMC, VMC_NAME, "");
549 break;
552 else if (result == VMC_REFRESH) { // User changed the VMC name
553 diaGetString(diaVMC, VMC_NAME, vmc);
554 size = guiRefreshVMCConfig(support, vmc);
557 result = diaExecuteDialog(diaVMC, result, 1, &guiVMCUpdater);
559 if ((result == 0) && (vmc_operation == OPERATION_CREATE))
560 break;
561 } while (1);
563 return result;
566 #endif
568 int guiShowCompatConfig(int id, item_list_t *support) {
569 int dmaMode = -1, compatMode = 0;
570 char* startup = support->itemGetStartup(id);
571 compatMode = support->itemGetCompatibility(id, &dmaMode);
573 if (dmaMode != -1) {
574 const char* dmaModes[] = { "MDMA 0", "MDMA 1", "MDMA 2", "UDMA 0", "UDMA 1", "UDMA 2", "UDMA 3", "UDMA 4", "UDMA 5", "UDMA 6", NULL };
575 diaSetEnum(diaCompatConfig, COMPAT_MODE_BASE + COMPAT_MODE_COUNT, dmaModes);
576 diaSetInt(diaCompatConfig, COMPAT_MODE_BASE + COMPAT_MODE_COUNT, dmaMode);
577 } else {
578 const char* dmaModes[] = { NULL };
579 diaSetEnum(diaCompatConfig, COMPAT_MODE_BASE + COMPAT_MODE_COUNT, dmaModes);
580 diaSetInt(diaCompatConfig, COMPAT_MODE_BASE + COMPAT_MODE_COUNT, 0);
583 diaSetLabel(diaCompatConfig, COMPAT_GAME, support->itemGetName(id));
585 int i, result = -1;
586 for (i = 0; i < COMPAT_MODE_COUNT; ++i)
587 diaSetInt(diaCompatConfig, COMPAT_MODE_BASE + i, (compatMode & (1 << i)) > 0 ? 1 : 0);
589 // Find out the current game ID
590 char hexid[32];
591 configGetDiscID(startup, hexid);
592 diaSetString(diaCompatConfig, COMPAT_GAMEID, hexid);
594 #ifdef VMC
595 int mode = support->mode;
597 char vmc1[32];
598 configGetVMC(startup, vmc1, mode, 0);
599 diaSetLabel(diaCompatConfig, COMPAT_VMC1_DEFINE, vmc1);
601 char vmc2[32]; // required as diaSetLabel use pointer to value
602 configGetVMC(startup, vmc2, mode, 1);
603 diaSetLabel(diaCompatConfig, COMPAT_VMC2_DEFINE, vmc2);
604 #endif
606 // show dialog
607 do {
608 #ifdef VMC
609 if (strlen(vmc1))
610 diaSetLabel(diaCompatConfig, COMPAT_VMC1_ACTION, _l(_STR_RESET));
611 else
612 diaSetLabel(diaCompatConfig, COMPAT_VMC1_ACTION, _l(_STR_USE_GENERIC));
613 if (strlen(vmc2))
614 diaSetLabel(diaCompatConfig, COMPAT_VMC2_ACTION, _l(_STR_RESET));
615 else
616 diaSetLabel(diaCompatConfig, COMPAT_VMC2_ACTION, _l(_STR_USE_GENERIC));
617 #endif
619 result = diaExecuteDialog(diaCompatConfig, result, 1, NULL);
621 if (result == COMPAT_LOADFROMDISC) {
622 char hexDiscID[15];
623 if (sysGetDiscID(hexDiscID) >= 0)
624 diaSetString(diaCompatConfig, COMPAT_GAMEID, hexDiscID);
625 else
626 guiMsgBox(_l(_STR_ERROR_LOADING_ID), 0, NULL);
628 #ifdef VMC
629 else if (result == COMPAT_VMC1_DEFINE) {
630 if(guiShowVMCConfig(id, support, vmc1, 0, 0))
631 diaGetString(diaVMC, VMC_NAME, vmc1);
632 } else if (result == COMPAT_VMC2_DEFINE) {
633 if(guiShowVMCConfig(id, support, vmc2, 1, 0))
634 diaGetString(diaVMC, VMC_NAME, vmc2);
635 } else if (result == COMPAT_VMC1_ACTION) {
636 if (strlen(vmc1))
637 vmc1[0] = '\0';
638 else
639 snprintf(vmc1, 32, "generic_%d", 0);
640 } else if (result == COMPAT_VMC2_ACTION) {
641 if (strlen(vmc2))
642 vmc2[0] = '\0';
643 else
644 snprintf(vmc2, 32, "generic_%d", 1);
646 #endif
647 } while (result >= COMPAT_NOEXIT);
649 if (result == COMPAT_REMOVE) {
650 configRemoveDiscID(startup);
651 #ifdef VMC
652 configRemoveVMC(startup, mode, 0);
653 configRemoveVMC(startup, mode, 1);
654 #endif
655 support->itemSetCompatibility(id, 0, 7);
656 saveConfig(CONFIG_COMPAT | CONFIG_DNAS | CONFIG_VMC, 1);
657 } else if (result > 0) { // test button pressed or save button
658 compatMode = 0;
659 for (i = 0; i < COMPAT_MODE_COUNT; ++i) {
660 int mdpart;
661 diaGetInt(diaCompatConfig, COMPAT_MODE_BASE + i, &mdpart);
662 compatMode |= (mdpart ? 1 : 0) << i;
665 if (dmaMode != -1)
666 diaGetInt(diaCompatConfig, COMPAT_MODE_BASE + COMPAT_MODE_COUNT, &dmaMode);
668 support->itemSetCompatibility(id, compatMode, dmaMode);
670 diaGetString(diaCompatConfig, COMPAT_GAMEID, hexid);
671 if (hexid[0] != '\0')
672 configSetDiscID(startup, hexid);
673 #ifdef VMC
674 configSetVMC(startup, vmc1, mode, 0);
675 configSetVMC(startup, vmc2, mode, 1);
676 guiShowVMCConfig(id, support, vmc1, 0, 1);
677 guiShowVMCConfig(id, support, vmc2, 1, 1);
678 #endif
680 if (result == COMPAT_SAVE)
681 saveConfig(CONFIG_COMPAT | CONFIG_DNAS | CONFIG_VMC, 1);
684 return result;
687 int guiGetOpCompleted(int opid) {
688 return gCompletedOps > opid;
691 int guiDeferUpdate(struct gui_update_t *op) {
692 WaitSema(gSemaId);
694 struct gui_update_list_t* up = (struct gui_update_list_t*) malloc(sizeof(struct gui_update_list_t));
695 up->item = op;
696 up->next = NULL;
698 if (!gUpdateList) {
699 gUpdateList = up;
700 gUpdateEnd = gUpdateList;
701 } else {
702 gUpdateEnd->next = up;
703 gUpdateEnd = up;
706 SignalSema(gSemaId);
708 return gScheduledOps++;
711 static void guiHandleOp(struct gui_update_t* item) {
712 submenu_list_t* result = NULL;
714 switch (item->type) {
715 case GUI_INIT_DONE:
716 gInitComplete = 1;
717 break;
719 case GUI_OP_ADD_MENU:
720 menuAppendItem(item->menu.menu);
721 break;
723 case GUI_OP_APPEND_MENU:
724 result = submenuAppendItem(item->menu.subMenu, item->submenu.icon_id,
725 item->submenu.text, item->submenu.id, item->submenu.text_id);
727 if (!item->menu.menu->submenu)
728 item->menu.menu->submenu = *item->menu.subMenu;
730 if (item->submenu.selected)
731 item->menu.menu->current = result;
733 break;
735 case GUI_OP_SELECT_MENU:
736 menuSetSelectedItem(item->menu.menu);
737 screenHandler = &screenHandlers[GUI_SCREEN_MAIN];
738 break;
740 case GUI_OP_CLEAR_SUBMENU:
741 submenuDestroy(item->menu.subMenu);
742 item->menu.menu->submenu = NULL;
743 item->menu.menu->current = NULL;
744 item->menu.menu->pagestart = NULL;
745 break;
747 case GUI_OP_SORT:
748 submenuSort(item->menu.subMenu);
749 item->menu.menu->pagestart = NULL;
750 item->menu.menu->submenu = *item->menu.subMenu;
751 break;
753 case GUI_OP_ADD_HINT:
754 // append the hint list in the menu item
755 menuAddHint(item->menu.menu, item->hint.text_id, item->hint.icon_id);
756 break;
758 default:
759 LOG("GUI: ??? (%d)\n", item->type);
763 static void guiHandleDeferredOps(void) {
764 // TODO: Fit into the given time interval, skip rest of operations of over limit
766 while (gUpdateList) {
767 WaitSema(gSemaId);
769 guiHandleOp(gUpdateList->item);
771 if (gNetworkStartup < 0) {
772 gNetworkStartup = 0;
773 guiMsgBox(_l(_STR_NETWORK_STARTUP_ERROR), 0, NULL);
776 struct gui_update_list_t* td = gUpdateList;
777 gUpdateList = gUpdateList->next;
779 free(td);
781 gCompletedOps++;
783 SignalSema(gSemaId);
786 gUpdateEnd = NULL;
789 static int bfadeout = 0x0;
790 static void guiDrawBusy() {
791 if (gTheme->loadingIcon) {
792 GSTEXTURE* texture = thmGetTexture(LOAD0_ICON + guiFrameId % gTheme->loadingIconCount);
793 if (texture && texture->Mem) {
794 u64 mycolor = GS_SETREG_RGBA(0x080, 0x080, 0x080, bfadeout);
795 rmDrawPixmap(texture, gTheme->loadingIcon->posX, gTheme->loadingIcon->posY, gTheme->loadingIcon->aligned, gTheme->loadingIcon->width, gTheme->loadingIcon->height, gTheme->loadingIcon->scaled, mycolor);
800 static int wfadeout = 0x0150;
801 static void guiRenderGreeting() {
802 int fade = wfadeout > 0xFF ? 0xFF : wfadeout;
803 u64 mycolor = GS_SETREG_RGBA(0x10, 0x10, 0x10, fade >> 1);
804 rmDrawRect(0, 0, screenWidth, screenHeight, mycolor);
805 /* char introtxt[255];
806 snprintf(introtxt, 255, _l(_STR_OUL_VER), USBLD_VERSION);
807 fntRenderString(screenWidth >> 1, gTheme->usedHeight >> 1, ALIGN_CENTER, introtxt, GS_SETREG_RGBA(0x060, 0x060, 0x060, wfadeout));
810 GSTEXTURE* logo = thmGetTexture(LOGO_PICTURE);
811 if (logo) {
812 mycolor = GS_SETREG_RGBA(0x080, 0x080, 0x080, fade >> 1);
813 rmDrawPixmap(logo, screenWidth >> 1, gTheme->usedHeight >> 1, ALIGN_CENTER, logo->Width, logo->Height, SCALING_RATIO, mycolor);
816 return;
819 static float mix(float a, float b, float t) {
820 return (1.0 - t) * a + t * b;
823 static float fade(float t) {
824 return fadetbl[(int) (t * FADE_SIZE)];
827 // The same as mix, but with 8 (2*4) values mixed at once
828 static void VU0MixVec(VU_VECTOR *a, VU_VECTOR *b, float mix, VU_VECTOR* res) {
829 asm (
830 "lqc2 vf1, 0(%0)\n" // load the first vector
831 "lqc2 vf2, 0(%1)\n" // load the second vector
832 "lw $2, 0(%2)\n" // load value from ptr to reg
833 "qmtc2 $2, vf3\n" // load the mix value from reg to VU
834 "vaddw.x vf5, vf00, vf00\n" // vf5.x = 1
835 "vsub.x vf4x, vf5x, vf3x\n" // subtract 1 - vf3,x, store the result in vf4.x
836 "vmulax.xyzw ACC, vf1, vf3x\n" // multiply vf1 by vf3.x, store the result in ACC
837 "vmaddx.xyzw vf1, vf2, vf4x\n" // multiply vf2 by vf4.x add ACC, store the result in vf1
838 "sqc2 vf1, 0(%3)\n" // transfer the result in acc to the ee
839 : : "r" (a), "r" (b), "r" (&mix), "r" (res)
843 static float guiCalcPerlin(float x, float y, float z) {
844 // Taken from: http://people.opera.com/patrickl/experiments/canvas/plasma/perlin-noise-classical.js
845 // By Sean McCullough
847 // Find unit grid cell containing point
848 int X = floor(x);
849 int Y = floor(y);
850 int Z = floor(z);
852 // Get relative xyz coordinates of point within that cell
853 x = x - X;
854 y = y - Y;
855 z = z - Z;
857 // Wrap the integer cells at 255 (smaller integer period can be introduced here)
858 X = X & 255;
859 Y = Y & 255;
860 Z = Z & 255;
862 // Calculate a set of eight hashed gradient indices
863 int gi000 = pperm[X + pperm[Y + pperm[Z]]] % 12;
864 int gi001 = pperm[X + pperm[Y + pperm[Z + 1]]] % 12;
865 int gi010 = pperm[X + pperm[Y + 1 + pperm[Z]]] % 12;
866 int gi011 = pperm[X + pperm[Y + 1 + pperm[Z + 1]]] % 12;
867 int gi100 = pperm[X + 1 + pperm[Y + pperm[Z]]] % 12;
868 int gi101 = pperm[X + 1 + pperm[Y + pperm[Z + 1]]] % 12;
869 int gi110 = pperm[X + 1 + pperm[Y + 1 + pperm[Z]]] % 12;
870 int gi111 = pperm[X + 1 + pperm[Y + 1 + pperm[Z + 1]]] % 12;
872 // The gradients of each corner are now:
873 // g000 = grad3[gi000];
874 // g001 = grad3[gi001];
875 // g010 = grad3[gi010];
876 // g011 = grad3[gi011];
877 // g100 = grad3[gi100];
878 // g101 = grad3[gi101];
879 // g110 = grad3[gi110];
880 // g111 = grad3[gi111];
881 // Calculate noise contributions from each of the eight corners
882 VU_VECTOR vec;
883 vec.x = x;
884 vec.y = y;
885 vec.z = z;
886 vec.w = 1;
888 VU_VECTOR a, b;
890 // float n000
891 a.x = Vu0DotProduct(&pgrad3[gi000], &vec);
893 vec.y -= 1;
895 // float n010
896 a.z = Vu0DotProduct(&pgrad3[gi010], &vec);
898 vec.x -= 1;
900 //float n110
901 b.z = Vu0DotProduct(&pgrad3[gi110], &vec);
903 vec.y += 1;
905 // float n100
906 b.x = Vu0DotProduct(&pgrad3[gi100], &vec);
908 vec.z -= 1;
910 // float n101
911 b.y = Vu0DotProduct(&pgrad3[gi101], &vec);
913 vec.y -= 1;
915 // float n111
916 b.w = Vu0DotProduct(&pgrad3[gi111], &vec);
918 vec.x += 1;
920 // float n011
921 a.w = Vu0DotProduct(&pgrad3[gi011], &vec);
923 vec.y += 1;
925 // float n001
926 a.y = Vu0DotProduct(&pgrad3[gi001], &vec);
928 // Compute the fade curve value for each of x, y, z
929 float u = fade(x);
930 float v = fade(y);
931 float w = fade(z);
933 // TODO: Low priority... This could be done on VU0 (xyzw for the first 4 mixes)
934 // The result in sw
935 // Interpolate along x the contributions from each of the corners
936 VU_VECTOR rv;
937 VU0MixVec(&b, &a, u, &rv);
939 // TODO: The VU0MixVec could as well mix the results (as follows) - might improve performance...
940 // Interpolate the four results along y
941 float nxy0 = mix(rv.x, rv.z, v);
942 float nxy1 = mix(rv.y, rv.w, v);
943 // Interpolate the two last results along z
944 float nxyz = mix(nxy0, nxy1, w);
946 return nxyz;
949 static float dir = 0.02;
950 static float perz = -100;
951 static int pery = 0;
952 static unsigned char curbgColor[3] = { 0, 0, 0 };
954 static int cdirection(unsigned char a, unsigned char b) {
955 if (a == b)
956 return 0;
957 else if (a > b)
958 return -1;
959 else
960 return 1;
963 void guiDrawBGPlasma() {
964 int x, y;
966 // transition the colors
967 curbgColor[0] += cdirection(curbgColor[0], gTheme->bgColor[0]);
968 curbgColor[1] += cdirection(curbgColor[1], gTheme->bgColor[1]);
969 curbgColor[2] += cdirection(curbgColor[2], gTheme->bgColor[2]);
971 // it's PLASMA_ROWS_PER_FRAME rows a frame to stop being a resource hog
972 if (pery >= PLASMA_H) {
973 pery = 0;
974 perz += dir;
976 if (perz > 100.0f || perz < -100.0f)
977 dir = -dir;
980 u32 *buf = gBackgroundTex.Mem + PLASMA_W * pery;
981 int ymax = pery + PLASMA_ROWS_PER_FRAME;
983 if (ymax > PLASMA_H)
984 ymax = PLASMA_H;
986 for (y = pery; y < ymax; y++) {
987 for (x = 0; x < PLASMA_W; x++) {
988 u32 fper = guiCalcPerlin((float) (2 * x) / PLASMA_W, (float) (2 * y) / PLASMA_H, perz) * 0x080 + 0x080;
990 *buf = GS_SETREG_RGBA(
991 (u32)(fper * curbgColor[0]) >> 8,
992 (u32)(fper * curbgColor[1]) >> 8,
993 (u32)(fper * curbgColor[2]) >> 8,
994 0x080);
996 ++buf;
1001 pery = ymax;
1002 rmDrawPixmap(&gBackgroundTex, 0, 0, ALIGN_NONE, screenWidth, screenHeight, SCALING_NONE, gDefaultCol);
1005 int guiDrawIconAndText(int iconId, int textId, int font, int x, int y, u64 color) {
1006 GSTEXTURE* iconTex = thmGetTexture(iconId);
1007 if (iconTex && iconTex->Mem) {
1008 rmDrawPixmap(iconTex, x, y, ALIGN_NONE, iconTex->Width, iconTex->Height, SCALING_RATIO, gDefaultCol);
1009 x += iconTex->Width + 2;
1012 x = fntRenderString(font, x, y, ALIGN_NONE, _l(textId), color);
1014 return x;
1017 static void guiDrawOverlays() {
1018 // are there any pending operations?
1019 int pending = ioHasPendingRequests();
1021 if (gInitComplete)
1022 wfadeout--;
1024 if (!pending) {
1025 if (bfadeout > 0x0)
1026 bfadeout -= 0x08;
1027 else
1028 bfadeout = 0x0;
1029 } else {
1030 if (bfadeout < 0x080)
1031 bfadeout += 0x20;
1034 // is init still running?
1035 if (wfadeout > 0)
1036 guiRenderGreeting();
1038 if (bfadeout > 0)
1039 guiDrawBusy();
1041 #ifdef __DEBUG
1042 // fps meter
1043 char fps[10];
1045 if (time_since_last != 0)
1046 snprintf(fps, 10, "%3.1f FPS", 1000.0f / (float) time_since_last);
1047 else
1048 snprintf(fps, 10, "---- FPS");
1050 fntRenderString(FNT_DEFAULT, screenWidth - 60, gTheme->usedHeight - 20, ALIGN_CENTER, fps, GS_SETREG_RGBA(0x060, 0x060, 0x060, 0x060));
1052 snprintf(fps, 10, "%3d ms", time_render);
1054 fntRenderString(FNT_DEFAULT, screenWidth - 60, gTheme->usedHeight - 45, ALIGN_CENTER, fps, GS_SETREG_RGBA(0x060, 0x060, 0x060, 0x060));
1055 #endif
1058 static void guiReadPads() {
1059 if (readPads())
1060 guiInactiveFrames = 0;
1061 else
1062 guiInactiveFrames++;
1065 // renders the screen and handles inputs. Also handles screen transitions between numerous
1066 // screen handlers. For now we only have left-to right screen transition
1067 static void guiShow() {
1068 // is there a transmission effect going on or are
1069 // we in a normal rendering state?
1070 if (screenHandlerTarget) {
1071 // advance the effect
1073 // render the old screen, transposed
1074 rmSetTransposition(transIndex * transitionX, transIndex * transitionY);
1075 screenHandler->renderScreen();
1077 // render new screen transposed again
1078 rmSetTransposition((transIndex - transMax) * transitionX, (transIndex - transMax) * transitionY);
1079 screenHandlerTarget->renderScreen();
1081 // reset transposition to zero
1082 rmSetTransposition(0, 0);
1084 // move the transition indicator forward
1085 transIndex += (min(transIndex, transMax - transIndex) >> 1) + 1;
1087 if (transIndex > transMax) {
1088 transitionX = 0;
1089 transitionY = 0;
1090 screenHandler = screenHandlerTarget;
1091 screenHandlerTarget = NULL;
1093 } else
1094 // render with the set screen handler
1095 screenHandler->renderScreen();
1098 void guiMainLoop(void) {
1099 while (!gTerminate) {
1100 guiStartFrame();
1102 // Read the pad states to prepare for input processing in the screen handler
1103 guiReadPads();
1105 // handle inputs and render screen
1106 if (wfadeout < 0x0FF)
1107 guiShow();
1109 // Render overlaying gui thingies :)
1110 guiDrawOverlays();
1112 // handle deferred operations
1113 guiHandleDeferredOps();
1115 if (gFrameHook)
1116 gFrameHook();
1118 guiEndFrame();
1120 // if not transiting, handle input
1121 // done here so we can use renderman if needed
1122 if (!screenHandlerTarget && screenHandler)
1123 screenHandler->handleInput();
1127 void guiSetFrameHook(gui_callback_t cback) {
1128 gFrameHook = cback;
1131 void guiSwitchScreen(int target, int transition) {
1132 if (transition == TRANSITION_LEFT) {
1133 transitionX = 1;
1134 transMax = screenWidth;
1135 } else if (transition == TRANSITION_RIGHT) {
1136 transitionX = -1;
1137 transMax = screenWidth;
1138 } else if (transition == TRANSITION_UP) {
1139 transitionY = 1;
1140 transMax = screenHeight;
1141 } else if (transition == TRANSITION_DOWN) {
1142 transitionY = -1;
1143 transMax = screenHeight;
1145 transIndex = 0;
1147 screenHandlerTarget = &screenHandlers[target];
1150 struct gui_update_t *guiOpCreate(gui_op_type_t type) {
1151 struct gui_update_t *op = (struct gui_update_t *) malloc(sizeof(struct gui_update_t));
1152 memset(op, 0, sizeof(struct gui_update_t));
1153 op->type = type;
1154 return op;
1157 void guiUpdateScrollSpeed(void) {
1158 // sanitize the settings
1159 if ((gScrollSpeed < 0) || (gScrollSpeed > 2))
1160 gScrollSpeed = 1;
1162 // update the pad delays for KEY_UP and KEY_DOWN
1163 // default delay is 7
1164 // fast - 100 ms
1165 // medium - 300 ms
1166 // slow - 500 ms
1167 setButtonDelay(KEY_UP, 500 - gScrollSpeed * 200); // 0,1,2 -> 500, 300, 100
1168 setButtonDelay(KEY_DOWN, 500 - gScrollSpeed * 200);
1171 void guiUpdateScreenScale(void) {
1172 if (gWideScreen)
1173 wideScreenScale = 0.75f;
1174 else
1175 wideScreenScale = 1.0f;
1177 // apply the scaling to renderman and font rendering
1178 rmSetAspectRatio(wideScreenScale, 1.0f);
1179 fntSetAspectRatio(wideScreenScale, 1.0f);
1182 int guiMsgBox(const char* text, int addAccept, struct UIItem *ui) {
1183 int terminate = 0;
1184 while (!terminate) {
1185 guiStartFrame();
1187 readPads();
1189 if (getKeyOn(KEY_CIRCLE))
1190 terminate = 1;
1191 else if (getKeyOn(KEY_CROSS))
1192 terminate = 2;
1194 if (ui)
1195 diaRenderUI(ui, screenHandler->inMenu, NULL, 0);
1196 else
1197 guiShow();
1199 rmDrawRect(0, 0, screenWidth, screenHeight, gColDarker);
1201 rmDrawLine(50, 75, screenWidth - 50, 75, gColWhite);
1202 rmDrawLine(50, 410, screenWidth - 50, 410, gColWhite);
1204 fntRenderString(FNT_DEFAULT, screenWidth >> 1, gTheme->usedHeight >> 1, ALIGN_CENTER, text, gTheme->textColor);
1205 guiDrawIconAndText(CIRCLE_ICON, _STR_O_BACK, FNT_DEFAULT, 500, 417, gTheme->selTextColor);
1206 if (addAccept)
1207 guiDrawIconAndText(CROSS_ICON, _STR_X_ACCEPT, FNT_DEFAULT, 70, 417, gTheme->selTextColor);
1209 guiEndFrame();
1212 return terminate - 1;
1215 void guiHandleDefferedIO(int *ptr, const unsigned char* message, int type, void *data) {
1216 ioPutRequest(type, data);
1218 while (*ptr) {
1219 guiStartFrame();
1221 readPads();
1223 guiShow();
1225 rmDrawRect(0, 0, screenWidth, screenHeight, gColDarker);
1227 fntRenderString(FNT_DEFAULT, screenWidth >> 1, gTheme->usedHeight >> 1, ALIGN_CENTER, message, gTheme->textColor);
1229 // so the io status icon will be rendered
1230 guiDrawOverlays();
1232 guiEndFrame();
1236 void guiRenderTextScreen(const unsigned char* message) {
1237 guiStartFrame();
1239 guiShow();
1241 rmDrawRect(0, 0, screenWidth, screenHeight, gColDarker);
1243 fntRenderString(FNT_DEFAULT, screenWidth >> 1, gTheme->usedHeight >> 1, ALIGN_CENTER, message, gTheme->textColor);
1245 // so the io status icon will be rendered
1246 guiDrawOverlays();
1248 guiEndFrame();