3 Licenced under Academic Free License version 3.0
4 Review OpenUsbLd README & LICENSE files for further details.
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"
19 #include "include/ethsupport.h"
24 static int gScheduledOps
;
25 static int gCompletedOps
;
26 static int gTerminate
;
27 static int gInitComplete
;
29 static gui_callback_t gFrameHook
;
32 static s32 gGUILockSemaId
;
33 static ee_sema_t gQueueSema
;
35 static int screenWidth
;
36 static float wideScreenScale
;
37 static int screenHeight
;
40 static void guiShow();
46 #define CLOCKS_PER_MILISEC 147456
48 // debug version displays an FPS meter
49 static u32 curtime
= 0;
50 static u32 time_since_last
= 0;
51 static u32 time_render
= 0;
55 struct gui_update_list_t
{
56 struct gui_update_t
* item
;
57 struct gui_update_list_t
* next
;
60 struct gui_update_list_t
*gUpdateList
;
61 struct gui_update_list_t
*gUpdateEnd
;
64 void (*handleInput
)(void);
65 void (*renderScreen
)(void);
67 } gui_screen_handler_t
;
69 static gui_screen_handler_t screenHandlers
[] = {{ &menuHandleInputMain
, &menuRenderMain
, 0 },
70 { &menuHandleInputMenu
, &menuRenderMenu
, 1 },
71 { &menuHandleInputInfo
, &menuRenderInfo
, 1 } };
73 // default screen handler (menu screen)
74 static gui_screen_handler_t
*screenHandler
= &screenHandlers
[GUI_SCREEN_MENU
];
76 // screen transition handling
77 static gui_screen_handler_t
*screenHandlerTarget
= NULL
;
78 static int transIndex
, transMax
, transitionX
, transitionY
;
80 // Helper perlin noise data
83 #define PLASMA_ROWS_PER_FRAME 6
86 static GSTEXTURE gBackgroundTex
;
87 static int pperm
[512];
88 static float fadetbl
[FADE_SIZE
+ 1];
90 static VU_VECTOR pgrad3
[12] = {{ 1, 1, 0, 1 }, { -1, 1, 0, 1 }, { 1, -1, 0, 1 }, { -1, -1, 0, 1 },
91 { 1, 0, 1, 1 }, { -1, 0, 1, 1 }, { 1, 0, -1, 1 }, { -1, 0, -1, 1 },
92 { 0, 1, 1, 1 }, { 0, -1, 1, 1 }, { 0, 1, -1, 1 }, { 0, -1, -1, 1 } };
94 void guiReloadScreenExtents() {
95 rmGetScreenExtents(&screenWidth
, &screenHeight
);
100 guiInactiveFrames
= 0;
111 gQueueSema
.init_count
= 1;
112 gQueueSema
.max_count
= 1;
113 gQueueSema
.option
= 0;
115 gSemaId
= CreateSema(&gQueueSema
);
116 gGUILockSemaId
= CreateSema(&gQueueSema
);
118 guiReloadScreenExtents();
120 // background texture - for perlin
121 gBackgroundTex
.Width
= PLASMA_W
;
122 gBackgroundTex
.Height
= PLASMA_H
;
123 gBackgroundTex
.Mem
= memalign(128, PLASMA_W
* PLASMA_H
* 4);
124 gBackgroundTex
.PSM
= GS_PSM_CT32
;
125 gBackgroundTex
.Filter
= GS_FILTER_LINEAR
;
126 gBackgroundTex
.Vram
= 0;
127 gBackgroundTex
.VramClut
= 0;
128 gBackgroundTex
.Clut
= NULL
;
130 wideScreenScale
= 1.0f
;
132 // Precalculate the values for the perlin noise plasma
134 for (i
= 0; i
< 256; ++i
) {
135 pperm
[i
] = rand() % 256;
136 pperm
[i
+ 256] = pperm
[i
];
139 for (i
= 0; i
<= FADE_SIZE
; ++i
) {
140 float t
= (float) (i
) / FADE_SIZE
;
142 fadetbl
[i
] = t
* t
* t
* (t
* (t
* 6.0 - 15.0) + 10.0);
147 if (gBackgroundTex
.Mem
)
148 free(gBackgroundTex
.Mem
);
151 DeleteSema(gGUILockSemaId
);
155 WaitSema(gGUILockSemaId
);
158 void guiUnlock(void) {
159 SignalSema(gGUILockSemaId
);
162 void guiStartFrame(void) {
164 u32 newtime
= cpu_ticks() / CLOCKS_PER_MILISEC
;
165 time_since_last
= newtime
- curtime
;
174 void guiEndFrame(void) {
179 u32 newtime
= cpu_ticks() / CLOCKS_PER_MILISEC
;
180 time_render
= newtime
- curtime
;
186 void guiShowAbout() {
188 snprintf(OPLVersion
, 64, _l(_STR_OUL_VER
), USBLD_VERSION
);
191 strcat(OPLVersion
, " VMC");
194 strcat(OPLVersion
, " RTL");
197 strcat(OPLVersion
, " CHILDPROOF");
200 diaSetLabel(diaAbout
, 1, OPLVersion
);
202 diaExecuteDialog(diaAbout
, -1, 1, NULL
);
205 void guiShowConfig() {
206 // configure the enumerations
207 const char* deviceNames
[] = { _l(_STR_USB_GAMES
), _l(_STR_NET_GAMES
), _l(_STR_HDD_GAMES
), NULL
};
208 const char* deviceModes
[] = { _l(_STR_OFF
), _l(_STR_MANUAL
), _l(_STR_AUTO
), NULL
};
210 diaSetEnum(diaConfig
, CFG_DEFDEVICE
, deviceNames
);
211 diaSetEnum(diaConfig
, CFG_USBMODE
, deviceModes
);
212 diaSetEnum(diaConfig
, CFG_HDDMODE
, deviceModes
);
213 diaSetEnum(diaConfig
, CFG_ETHMODE
, deviceModes
);
214 diaSetEnum(diaConfig
, CFG_APPMODE
, deviceModes
);
216 diaSetInt(diaConfig
, CFG_DEBUG
, gDisableDebug
);
217 diaSetString(diaConfig
, CFG_EXITTO
, gExitPath
);
218 diaSetInt(diaConfig
, CFG_DANDROP
, gEnableDandR
);
219 diaSetInt(diaConfig
, CFG_HDDSPINDOWN
, gHDDSpindown
);
220 diaSetInt(diaConfig
, CFG_CHECKUSBFRAG
, gCheckUSBFragmentation
);
221 diaSetInt(diaConfig
, CFG_USBDELAY
, gUSBDelay
);
222 diaSetString(diaConfig
, CFG_USBPREFIX
, gUSBPrefix
);
223 diaSetString(diaConfig
, CFG_ETHPREFIX
, gETHPrefix
);
224 diaSetInt(diaConfig
, CFG_LASTPLAYED
, gRememberLastPlayed
);
225 diaSetInt(diaConfig
, CFG_DEFDEVICE
, gDefaultDevice
);
226 diaSetInt(diaConfig
, CFG_USBMODE
, gUSBStartMode
);
227 diaSetInt(diaConfig
, CFG_HDDMODE
, gHDDStartMode
);
228 diaSetInt(diaConfig
, CFG_ETHMODE
, gETHStartMode
);
229 diaSetInt(diaConfig
, CFG_APPMODE
, gAPPStartMode
);
231 int ret
= diaExecuteDialog(diaConfig
, -1, 1, NULL
);
233 diaGetString(diaConfig
, CFG_EXITTO
, gExitPath
);
234 diaGetInt(diaConfig
, CFG_DEBUG
, &gDisableDebug
);
235 diaGetInt(diaConfig
, CFG_DANDROP
, &gEnableDandR
);
236 diaGetInt(diaConfig
, CFG_HDDSPINDOWN
, &gHDDSpindown
);
237 diaGetInt(diaConfig
, CFG_CHECKUSBFRAG
, &gCheckUSBFragmentation
);
238 diaGetInt(diaConfig
, CFG_USBDELAY
, &gUSBDelay
);
239 diaGetString(diaConfig
, CFG_USBPREFIX
, gUSBPrefix
);
240 diaGetString(diaConfig
, CFG_ETHPREFIX
, gETHPrefix
);
241 diaGetInt(diaConfig
, CFG_LASTPLAYED
, &gRememberLastPlayed
);
242 diaGetInt(diaConfig
, CFG_DEFDEVICE
, &gDefaultDevice
);
243 diaGetInt(diaConfig
, CFG_USBMODE
, &gUSBStartMode
);
244 diaGetInt(diaConfig
, CFG_HDDMODE
, &gHDDStartMode
);
245 diaGetInt(diaConfig
, CFG_ETHMODE
, &gETHStartMode
);
246 diaGetInt(diaConfig
, CFG_APPMODE
, &gAPPStartMode
);
252 static int curTheme
= -1;
254 static int guiUIUpdater(int modified
) {
257 diaGetInt(diaUIConfig
, UICFG_THEME
, &temp
);
258 if (temp
!= curTheme
) {
261 diaUIConfig
[32].type
= UI_COLOUR
; // Must be correctly set before doing the diaS/GetColor !!
262 diaUIConfig
[36].type
= UI_COLOUR
;
263 diaUIConfig
[40].type
= UI_COLOUR
;
264 diaUIConfig
[44].type
= UI_COLOUR
;
265 diaSetColor(diaUIConfig
, UICFG_BGCOL
, gDefaultBgColor
);
266 diaSetColor(diaUIConfig
, UICFG_UICOL
, gDefaultUITextColor
);
267 diaSetColor(diaUIConfig
, UICFG_TXTCOL
, gDefaultTextColor
);
268 diaSetColor(diaUIConfig
, UICFG_SELCOL
, gDefaultSelTextColor
);
269 } else if (temp
== thmGetGuiValue()) {
270 diaUIConfig
[32].type
= UI_COLOUR
;
271 diaUIConfig
[36].type
= UI_COLOUR
;
272 diaUIConfig
[40].type
= UI_COLOUR
;
273 diaUIConfig
[44].type
= UI_COLOUR
;
274 diaSetColor(diaUIConfig
, UICFG_BGCOL
, gTheme
->bgColor
);
275 diaSetU64Color(diaUIConfig
, UICFG_UICOL
, gTheme
->uiTextColor
);
276 diaSetU64Color(diaUIConfig
, UICFG_TXTCOL
, gTheme
->textColor
);
277 diaSetU64Color(diaUIConfig
, UICFG_SELCOL
, gTheme
->selTextColor
);
279 diaUIConfig
[32].type
= UI_SPACER
;
280 diaUIConfig
[36].type
= UI_SPACER
;
281 diaUIConfig
[40].type
= UI_SPACER
;
282 diaUIConfig
[44].type
= UI_SPACER
;
286 diaSetEnabled(diaUIConfig
, UICFG_BGCOL
, temp
);
287 diaSetEnabled(diaUIConfig
, UICFG_UICOL
, temp
);
288 diaSetEnabled(diaUIConfig
, UICFG_TXTCOL
, temp
);
289 diaSetEnabled(diaUIConfig
, UICFG_SELCOL
, temp
);
296 void guiShowUIConfig() {
299 // configure the enumerations
300 const char* scrollSpeeds
[] = { _l(_STR_SLOW
), _l(_STR_MEDIUM
), _l(_STR_FAST
), NULL
};
301 const char* vmodeNames
[] = { _l(_STR_AUTO
), "PAL", "NTSC", NULL
};
302 diaSetEnum(diaUIConfig
, UICFG_SCROLL
, scrollSpeeds
);
303 diaSetEnum(diaUIConfig
, UICFG_THEME
, (const char **) thmGetGuiList());
304 diaSetEnum(diaUIConfig
, UICFG_LANG
, (const char **) lngGetGuiList());
305 diaSetEnum(diaUIConfig
, UICFG_VMODE
, vmodeNames
);
307 diaSetInt(diaUIConfig
, UICFG_SCROLL
, gScrollSpeed
);
308 diaSetInt(diaUIConfig
, UICFG_THEME
, thmGetGuiValue());
309 diaSetInt(diaUIConfig
, UICFG_LANG
, lngGetGuiValue());
311 diaSetInt(diaUIConfig
, UICFG_AUTOSORT
, gAutosort
);
312 diaSetInt(diaUIConfig
, UICFG_AUTOREFRESH
, gAutoRefresh
);
313 diaSetInt(diaUIConfig
, UICFG_INFOPAGE
, gUseInfoScreen
);
314 diaSetInt(diaUIConfig
, UICFG_COVERART
, gEnableArt
);
315 diaSetInt(diaUIConfig
, UICFG_WIDESCREEN
, gWideScreen
);
316 diaSetInt(diaUIConfig
, UICFG_VMODE
, gVMode
);
317 diaSetInt(diaUIConfig
, UICFG_VSYNC
, gVSync
);
319 int ret
= diaExecuteDialog(diaUIConfig
, -1, 1, guiUIUpdater
);
321 int themeID
= -1, langID
= -1;
322 diaGetInt(diaUIConfig
, UICFG_SCROLL
, &gScrollSpeed
);
323 diaGetInt(diaUIConfig
, UICFG_LANG
, &langID
);
324 diaGetInt(diaUIConfig
, UICFG_THEME
, &themeID
);
326 diaGetColor(diaUIConfig
, UICFG_BGCOL
, gDefaultBgColor
);
327 diaGetColor(diaUIConfig
, UICFG_UICOL
, gDefaultUITextColor
);
328 diaGetColor(diaUIConfig
, UICFG_TXTCOL
, gDefaultTextColor
);
329 diaGetColor(diaUIConfig
, UICFG_SELCOL
, gDefaultSelTextColor
);
331 diaGetInt(diaUIConfig
, UICFG_AUTOSORT
, &gAutosort
);
332 diaGetInt(diaUIConfig
, UICFG_AUTOREFRESH
, &gAutoRefresh
);
333 diaGetInt(diaUIConfig
, UICFG_INFOPAGE
, &gUseInfoScreen
);
334 diaGetInt(diaUIConfig
, UICFG_COVERART
, &gEnableArt
);
335 diaGetInt(diaUIConfig
, UICFG_WIDESCREEN
, &gWideScreen
);
336 diaGetInt(diaUIConfig
, UICFG_VMODE
, &gVMode
);
337 diaGetInt(diaUIConfig
, UICFG_VSYNC
, &gVSync
);
339 applyConfig(themeID
, langID
);
343 void guiShowIPConfig() {
345 // upload current values
346 for (i
= 0; i
< 4; ++i
) {
347 if (gNetworkStartup
!= ERROR_ETH_NOT_STARTED
) {
348 diaSetEnabled(diaIPConfig
, 2 + i
, 0);
349 diaSetEnabled(diaIPConfig
, 6 + i
, 0);
350 diaSetEnabled(diaIPConfig
, 10 + i
, 0);
352 diaSetEnabled(diaIPConfig
, 2 + i
, 1);
353 diaSetEnabled(diaIPConfig
, 6 + i
, 1);
354 diaSetEnabled(diaIPConfig
, 10 + i
, 1);
356 diaSetInt(diaIPConfig
, 2 + i
, ps2_ip
[i
]);
357 diaSetInt(diaIPConfig
, 6 + i
, ps2_netmask
[i
]);
358 diaSetInt(diaIPConfig
, 10 + i
, ps2_gateway
[i
]);
359 diaSetInt(diaIPConfig
, 14 + i
, pc_ip
[i
]);
362 diaSetInt(diaIPConfig
, 18, gPCPort
);
363 diaSetString(diaIPConfig
, 19, gPCShareName
);
364 diaSetString(diaIPConfig
, 20, gPCUserName
);
365 diaSetString(diaIPConfig
, 21, gPCPassword
);
367 if (gNetworkStartup
== 0) {
368 diaSetLabel(diaIPConfig
, NETCFG_OK
, _l(_STR_OK
));
369 diaIPConfig
[64].type
= UI_SPACER
;
370 } else if (gNetworkStartup
>= ERROR_ETH_SMB_LOGON
) {
371 diaSetLabel(diaIPConfig
, NETCFG_OK
, _l(_STR_RECONNECT
));
372 diaIPConfig
[64].type
= UI_TERMINATOR
;
374 diaSetLabel(diaIPConfig
, NETCFG_OK
, _l(_STR_OK
));
375 diaIPConfig
[64].type
= UI_TERMINATOR
;
379 int result
= diaExecuteDialog(diaIPConfig
, -1, 1, NULL
);
382 for (i
= 0; i
< 4; ++i
) {
383 diaGetInt(diaIPConfig
, 2 + i
, &ps2_ip
[i
]);
384 diaGetInt(diaIPConfig
, 6 + i
, &ps2_netmask
[i
]);
385 diaGetInt(diaIPConfig
, 10 + i
, &ps2_gateway
[i
]);
386 diaGetInt(diaIPConfig
, 14 + i
, &pc_ip
[i
]);
389 diaGetInt(diaIPConfig
, 18, &gPCPort
);
390 diaGetString(diaIPConfig
, 19, gPCShareName
);
391 diaGetString(diaIPConfig
, 20, gPCUserName
);
392 diaGetString(diaIPConfig
, 21, gPCPassword
);
393 gIPConfigChanged
= 1;
395 if (result
== NETCFG_RECONNECT
)
396 gNetworkStartup
= ERROR_ETH_SMB_LOGON
;
402 int guiShowKeyboard(char* value
, int maxLength
) {
404 strncpy(tmp
, value
, maxLength
);
406 int result
= diaShowKeyb(tmp
, maxLength
);
408 strncpy(value
, tmp
, maxLength
);
409 value
[maxLength
- 1] = '\0';
416 typedef struct { // size = 76
417 int VMC_status
; // 0=available, 1=busy
423 #define OPERATION_CREATE 0
424 #define OPERATION_CREATING 1
425 #define OPERATION_ABORTING 2
426 #define OPERATION_ENDING 3
427 #define OPERATION_END 4
429 static short vmc_refresh
;
430 static int vmc_operation
;
431 static statusVMCparam_t vmc_status
;
433 int guiVmcNameHandler(char* text
, int maxLen
) {
434 int result
= diaShowKeyb(text
, maxLen
);
442 static int guiRefreshVMCConfig(item_list_t
*support
, char* name
) {
443 int size
= support
->itemCheckVMC(name
, 0);
446 diaSetLabel(diaVMC
, VMC_BUTTON_CREATE
, _l(_STR_MODIFY
));
447 diaSetLabel(diaVMC
, VMC_STATUS
, _l(_STR_VMC_FILE_EXISTS
));
450 diaSetInt(diaVMC
, VMC_SIZE
, 0);
452 diaSetInt(diaVMC
, VMC_SIZE
, 1);
454 diaSetInt(diaVMC
, VMC_SIZE
, 2);
456 diaSetInt(diaVMC
, VMC_SIZE
, 3);
458 diaSetInt(diaVMC
, VMC_SIZE
, 0);
459 diaSetLabel(diaVMC
, VMC_STATUS
, _l(_STR_VMC_FILE_ERROR
));
463 diaSetEnabled(diaVMC
, VMC_SIZE
, 1);
464 diaVMC
[20].type
= UI_SPLITTER
;
467 diaSetEnabled(diaVMC
, VMC_SIZE
, 0);
468 diaVMC
[20].type
= UI_TERMINATOR
;
472 diaSetLabel(diaVMC
, VMC_BUTTON_CREATE
, _l(_STR_CREATE
));
473 diaSetLabel(diaVMC
, VMC_STATUS
, _l(_STR_VMC_FILE_NEW
));
475 diaSetInt(diaVMC
, VMC_SIZE
, 0);
476 diaSetEnabled(diaVMC
, VMC_SIZE
, 1);
477 diaVMC
[20].type
= UI_TERMINATOR
;
483 static int guiVMCUpdater(int modified
) {
489 if ((vmc_operation
== OPERATION_CREATING
) || (vmc_operation
== OPERATION_ABORTING
)) {
490 int result
= fileXioDevctl("genvmc:", 0xC0DE0003, NULL
, 0, (void*) &vmc_status
, sizeof(vmc_status
));
492 diaSetLabel(diaVMC
, VMC_STATUS
, vmc_status
.VMC_msg
);
493 diaSetInt(diaVMC
, VMC_PROGRESS
, vmc_status
.VMC_progress
);
495 if (vmc_status
.VMC_error
!= 0)
496 LOG("GUI VMCUpdater: %d\n", vmc_status
.VMC_error
);
498 if (vmc_status
.VMC_status
== 0x00) {
499 diaSetLabel(diaVMC
, VMC_BUTTON_CREATE
, _l(_STR_OK
));
500 vmc_operation
= OPERATION_ENDING
;
501 return VMC_BUTTON_CREATE
;
505 LOG("GUI Status result: %d\n", result
);
511 static int guiShowVMCConfig(int id
, item_list_t
*support
, char *VMCName
, int slot
, int validate
) {
512 int result
= validate
? VMC_BUTTON_CREATE
: 0;
516 strncpy(vmc
, VMCName
, 32);
519 return 1; // nothing to validate if no user input
521 char* startup
= support
->itemGetStartup(id
);
522 snprintf(vmc
, 32, "%s_%d", startup
, slot
);
526 vmc_operation
= OPERATION_CREATE
;
527 diaSetEnabled(diaVMC
, VMC_NAME
, 1);
528 diaSetEnabled(diaVMC
, VMC_SIZE
, 1);
529 diaSetInt(diaVMC
, VMC_PROGRESS
, 0);
531 const char* VMCSizes
[] = {"8 Mb", "16 Mb", "32 Mb", "64 Mb", NULL
};
532 diaSetEnum(diaVMC
, VMC_SIZE
, VMCSizes
);
533 int size
= guiRefreshVMCConfig(support
, vmc
);
534 diaSetString(diaVMC
, VMC_NAME
, vmc
);
537 if (result
== VMC_BUTTON_CREATE
) {
538 if (vmc_operation
== OPERATION_CREATE
) { // User start creation of VMC
540 diaGetInt(diaVMC
, VMC_SIZE
, &sizeUI
);
543 else if (sizeUI
== 2)
545 else if (sizeUI
== 3)
550 if (sizeUI
!= size
) {
551 support
->itemCheckVMC(vmc
, sizeUI
);
553 diaSetEnabled(diaVMC
, VMC_NAME
, 0);
554 diaSetEnabled(diaVMC
, VMC_SIZE
, 0);
555 diaSetLabel(diaVMC
, VMC_BUTTON_CREATE
, _l(_STR_ABORT
));
556 vmc_operation
= OPERATION_CREATING
;
561 else if (vmc_operation
== OPERATION_ENDING
) {
563 break; // directly close VMC config dialog
565 vmc_operation
= OPERATION_END
;
567 else if (vmc_operation
== OPERATION_END
) { // User closed creation dialog of VMC
570 else if (vmc_operation
== OPERATION_CREATING
) { // User canceled creation of VMC
571 fileXioDevctl("genvmc:", 0xC0DE0002, NULL
, 0, NULL
, 0);
572 vmc_operation
= OPERATION_ABORTING
;
575 else if (result
== VMC_BUTTON_DELETE
) {
576 if (guiMsgBox(_l(_STR_DELETE_WARNING
), 1, diaVMC
)) {
577 support
->itemCheckVMC(vmc
, -1);
578 diaSetString(diaVMC
, VMC_NAME
, "");
582 else if (result
== VMC_REFRESH
) { // User changed the VMC name
583 diaGetString(diaVMC
, VMC_NAME
, vmc
);
584 size
= guiRefreshVMCConfig(support
, vmc
);
587 result
= diaExecuteDialog(diaVMC
, result
, 1, &guiVMCUpdater
);
589 if ((result
== 0) && (vmc_operation
== OPERATION_CREATE
))
597 int guiAltStartupNameHandler(char* text
, int maxLen
) {
600 int result
= diaShowKeyb(text
, maxLen
);
602 for (i
= 0; text
[i
]; i
++) {
603 if (text
[i
] > 96 && text
[i
] < 123)
611 int guiShowCompatConfig(int id
, item_list_t
*support
, config_set_t
* configSet
) {
612 int dmaMode
= 7; // defaulting to UDMA 4
613 if (support
->haveCompatibilityMode
== COMPAT_FULL
) {
614 configGetInt(configSet
, CONFIG_ITEM_DMA
, &dmaMode
);
615 const char* dmaModes
[] = { "MDMA 0", "MDMA 1", "MDMA 2", "UDMA 0", "UDMA 1", "UDMA 2", "UDMA 3", "UDMA 4", "UDMA 5", "UDMA 6", NULL
};
616 diaSetEnum(diaCompatConfig
, COMPAT_DMA
, dmaModes
);
617 diaSetInt(diaCompatConfig
, COMPAT_DMA
, dmaMode
);
619 const char* dmaModes
[] = { NULL
};
620 diaSetEnum(diaCompatConfig
, COMPAT_DMA
, dmaModes
);
621 diaSetInt(diaCompatConfig
, COMPAT_DMA
, 0);
624 diaSetLabel(diaCompatConfig
, COMPAT_GAME
, support
->itemGetName(id
));
627 configGetInt(configSet
, CONFIG_ITEM_COMPAT
, &compatMode
);
629 for (i
= 0; i
< COMPAT_MODE_COUNT
; ++i
)
630 diaSetInt(diaCompatConfig
, COMPAT_MODE_BASE
+ i
, (compatMode
& (1 << i
)) > 0 ? 1 : 0);
633 configGetInt(configSet
, CONFIG_ITEM_CDVDMAN_TIMER
, &timer
);
634 diaSetInt(diaCompatConfig
, COMPAT_CDVDMAN_TIMER
, timer
);
636 // Find out the current game ID
638 configGetStrCopy(configSet
, CONFIG_ITEM_DNAS
, hexid
);
639 diaSetString(diaCompatConfig
, COMPAT_GAMEID
, hexid
);
642 configGetStrCopy(configSet
, CONFIG_ITEM_ALTSTARTUP
, altStartup
);
643 diaSetString(diaCompatConfig
, COMPAT_ALTSTARTUP
, altStartup
);
647 configGetVMC(configSet
, vmc1
, 0);
648 diaSetLabel(diaCompatConfig
, COMPAT_VMC1_DEFINE
, vmc1
);
650 char vmc2
[32]; // required as diaSetLabel use pointer to value
651 configGetVMC(configSet
, vmc2
, 1);
652 diaSetLabel(diaCompatConfig
, COMPAT_VMC2_DEFINE
, vmc2
);
659 diaSetLabel(diaCompatConfig
, COMPAT_VMC1_ACTION
, _l(_STR_RESET
));
661 diaSetLabel(diaCompatConfig
, COMPAT_VMC1_ACTION
, _l(_STR_USE_GENERIC
));
663 diaSetLabel(diaCompatConfig
, COMPAT_VMC2_ACTION
, _l(_STR_RESET
));
665 diaSetLabel(diaCompatConfig
, COMPAT_VMC2_ACTION
, _l(_STR_USE_GENERIC
));
668 result
= diaExecuteDialog(diaCompatConfig
, result
, 1, NULL
);
670 if (result
== COMPAT_LOADFROMDISC
) {
672 if (sysGetDiscID(hexDiscID
) >= 0)
673 diaSetString(diaCompatConfig
, COMPAT_GAMEID
, hexDiscID
);
675 guiMsgBox(_l(_STR_ERROR_LOADING_ID
), 0, NULL
);
678 else if (result
== COMPAT_VMC1_DEFINE
) {
679 if(guiShowVMCConfig(id
, support
, vmc1
, 0, 0))
680 diaGetString(diaVMC
, VMC_NAME
, vmc1
);
681 } else if (result
== COMPAT_VMC2_DEFINE
) {
682 if(guiShowVMCConfig(id
, support
, vmc2
, 1, 0))
683 diaGetString(diaVMC
, VMC_NAME
, vmc2
);
684 } else if (result
== COMPAT_VMC1_ACTION
) {
688 snprintf(vmc1
, 32, "generic_%d", 0);
689 } else if (result
== COMPAT_VMC2_ACTION
) {
693 snprintf(vmc2
, 32, "generic_%d", 1);
696 } while (result
>= COMPAT_NOEXIT
);
698 if (result
== COMPAT_REMOVE
) {
699 configRemoveKey(configSet
, CONFIG_ITEM_DMA
);
700 configRemoveKey(configSet
, CONFIG_ITEM_COMPAT
);
701 configRemoveKey(configSet
, CONFIG_ITEM_DNAS
);
702 configRemoveKey(configSet
, CONFIG_ITEM_ALTSTARTUP
);
704 configRemoveVMC(configSet
, 0);
705 configRemoveVMC(configSet
, 1);
708 } else if (result
> 0) { // test button pressed or save button
710 for (i
= 0; i
< COMPAT_MODE_COUNT
; ++i
) {
712 diaGetInt(diaCompatConfig
, COMPAT_MODE_BASE
+ i
, &mdpart
);
713 compatMode
|= (mdpart
? 1 : 0) << i
;
716 if (support
->haveCompatibilityMode
== COMPAT_FULL
) {
717 diaGetInt(diaCompatConfig
, COMPAT_DMA
, &dmaMode
);
719 configSetInt(configSet
, CONFIG_ITEM_DMA
, dmaMode
);
721 configRemoveKey(configSet
, CONFIG_ITEM_DMA
);
725 configSetInt(configSet
, CONFIG_ITEM_COMPAT
, compatMode
);
727 configRemoveKey(configSet
, CONFIG_ITEM_COMPAT
);
729 diaGetInt(diaCompatConfig
, COMPAT_CDVDMAN_TIMER
, &timer
);
731 configSetInt(configSet
, CONFIG_ITEM_CDVDMAN_TIMER
, timer
);
733 configRemoveKey(configSet
, CONFIG_ITEM_CDVDMAN_TIMER
);
735 diaGetString(diaCompatConfig
, COMPAT_GAMEID
, hexid
);
736 if (hexid
[0] != '\0')
737 configSetStr(configSet
, CONFIG_ITEM_DNAS
, hexid
);
739 diaGetString(diaCompatConfig
, COMPAT_ALTSTARTUP
, altStartup
);
740 if (altStartup
[0] != '\0')
741 configSetStr(configSet
, CONFIG_ITEM_ALTSTARTUP
, altStartup
);
743 configRemoveKey(configSet
, CONFIG_ITEM_ALTSTARTUP
);
746 configSetVMC(configSet
, vmc1
, 0);
747 configSetVMC(configSet
, vmc2
, 1);
748 guiShowVMCConfig(id
, support
, vmc1
, 0, 1);
749 guiShowVMCConfig(id
, support
, vmc2
, 1, 1);
752 if (result
== COMPAT_SAVE
)
759 int guiGetOpCompleted(int opid
) {
760 return gCompletedOps
> opid
;
763 int guiDeferUpdate(struct gui_update_t
*op
) {
766 struct gui_update_list_t
* up
= (struct gui_update_list_t
*) malloc(sizeof(struct gui_update_list_t
));
772 gUpdateEnd
= gUpdateList
;
774 gUpdateEnd
->next
= up
;
780 return gScheduledOps
++;
783 static void guiHandleOp(struct gui_update_t
* item
) {
784 submenu_list_t
* result
= NULL
;
786 switch (item
->type
) {
791 case GUI_OP_ADD_MENU
:
792 menuAppendItem(item
->menu
.menu
);
795 case GUI_OP_APPEND_MENU
:
796 result
= submenuAppendItem(item
->menu
.subMenu
, item
->submenu
.icon_id
,
797 item
->submenu
.text
, item
->submenu
.id
, item
->submenu
.text_id
);
799 if (!item
->menu
.menu
->submenu
) { // first subitem in list
800 item
->menu
.menu
->submenu
= result
;
801 item
->menu
.menu
->current
= result
;
802 item
->menu
.menu
->pagestart
= result
;
803 } else if (item
->submenu
.selected
) { // remember last game feat.
804 item
->menu
.menu
->current
= result
;
805 item
->menu
.menu
->pagestart
= result
;
806 item
->menu
.menu
->remindLast
= 1;
811 case GUI_OP_SELECT_MENU
:
812 menuSetSelectedItem(item
->menu
.menu
);
813 screenHandler
= &screenHandlers
[GUI_SCREEN_MAIN
];
816 case GUI_OP_CLEAR_SUBMENU
:
817 submenuDestroy(item
->menu
.subMenu
);
818 item
->menu
.menu
->submenu
= NULL
;
819 item
->menu
.menu
->current
= NULL
;
820 item
->menu
.menu
->pagestart
= NULL
;
824 submenuSort(item
->menu
.subMenu
);
825 item
->menu
.menu
->submenu
= *item
->menu
.subMenu
;
827 if (!item
->menu
.menu
->remindLast
)
828 item
->menu
.menu
->current
= item
->menu
.menu
->submenu
;
830 item
->menu
.menu
->pagestart
= item
->menu
.menu
->current
;
833 case GUI_OP_ADD_HINT
:
834 // append the hint list in the menu item
835 menuAddHint(item
->menu
.menu
, item
->hint
.text_id
, item
->hint
.icon_id
);
839 LOG("GUI: ??? (%d)\n", item
->type
);
843 static void guiHandleDeferredOps(void) {
844 // TODO: Fit into the given time interval, skip rest of operations of over limit
846 while (gUpdateList
) {
849 guiHandleOp(gUpdateList
->item
);
851 struct gui_update_list_t
* td
= gUpdateList
;
852 gUpdateList
= gUpdateList
->next
;
864 static int bfadeout
= 0x0;
865 static void guiDrawBusy() {
866 if (gTheme
->loadingIcon
) {
867 GSTEXTURE
* texture
= thmGetTexture(LOAD0_ICON
+ (guiFrameId
>> 1) % gTheme
->loadingIconCount
);
868 if (texture
&& texture
->Mem
) {
869 u64 mycolor
= GS_SETREG_RGBA(0x080, 0x080, 0x080, bfadeout
);
870 rmDrawPixmap(texture
, gTheme
->loadingIcon
->posX
, gTheme
->loadingIcon
->posY
, gTheme
->loadingIcon
->aligned
, gTheme
->loadingIcon
->width
, gTheme
->loadingIcon
->height
, gTheme
->loadingIcon
->scaled
, mycolor
);
875 static int wfadeout
= 0x0150;
876 static void guiRenderGreeting() {
877 int fade
= wfadeout
> 0xFF ? 0xFF : wfadeout
;
878 u64 mycolor
= GS_SETREG_RGBA(0x10, 0x10, 0x10, fade
>> 1);
879 rmDrawRect(0, 0, screenWidth
, screenHeight
, mycolor
);
881 GSTEXTURE
* logo
= thmGetTexture(LOGO_PICTURE
);
883 mycolor
= GS_SETREG_RGBA(0x080, 0x080, 0x080, fade
>> 1);
884 rmDrawPixmap(logo
, screenWidth
>> 1, gTheme
->usedHeight
>> 1, ALIGN_CENTER
, logo
->Width
, logo
->Height
, SCALING_RATIO
, mycolor
);
890 static float mix(float a
, float b
, float t
) {
891 return (1.0 - t
) * a
+ t
* b
;
894 static float fade(float t
) {
895 return fadetbl
[(int) (t
* FADE_SIZE
)];
898 // The same as mix, but with 8 (2*4) values mixed at once
899 static void VU0MixVec(VU_VECTOR
*a
, VU_VECTOR
*b
, float mix
, VU_VECTOR
* res
) {
901 "lqc2 vf1, 0(%0)\n" // load the first vector
902 "lqc2 vf2, 0(%1)\n" // load the second vector
903 "lw $2, 0(%2)\n" // load value from ptr to reg
904 "qmtc2 $2, vf3\n" // load the mix value from reg to VU
905 "vaddw.x vf5, vf00, vf00\n" // vf5.x = 1
906 "vsub.x vf4x, vf5x, vf3x\n" // subtract 1 - vf3,x, store the result in vf4.x
907 "vmulax.xyzw ACC, vf1, vf3x\n" // multiply vf1 by vf3.x, store the result in ACC
908 "vmaddx.xyzw vf1, vf2, vf4x\n" // multiply vf2 by vf4.x add ACC, store the result in vf1
909 "sqc2 vf1, 0(%3)\n" // transfer the result in acc to the ee
910 : : "r" (a
), "r" (b
), "r" (&mix
), "r" (res
)
914 static float guiCalcPerlin(float x
, float y
, float z
) {
915 // Taken from: http://people.opera.com/patrickl/experiments/canvas/plasma/perlin-noise-classical.js
916 // By Sean McCullough
918 // Find unit grid cell containing point
923 // Get relative xyz coordinates of point within that cell
928 // Wrap the integer cells at 255 (smaller integer period can be introduced here)
933 // Calculate a set of eight hashed gradient indices
934 int gi000
= pperm
[X
+ pperm
[Y
+ pperm
[Z
]]] % 12;
935 int gi001
= pperm
[X
+ pperm
[Y
+ pperm
[Z
+ 1]]] % 12;
936 int gi010
= pperm
[X
+ pperm
[Y
+ 1 + pperm
[Z
]]] % 12;
937 int gi011
= pperm
[X
+ pperm
[Y
+ 1 + pperm
[Z
+ 1]]] % 12;
938 int gi100
= pperm
[X
+ 1 + pperm
[Y
+ pperm
[Z
]]] % 12;
939 int gi101
= pperm
[X
+ 1 + pperm
[Y
+ pperm
[Z
+ 1]]] % 12;
940 int gi110
= pperm
[X
+ 1 + pperm
[Y
+ 1 + pperm
[Z
]]] % 12;
941 int gi111
= pperm
[X
+ 1 + pperm
[Y
+ 1 + pperm
[Z
+ 1]]] % 12;
943 // The gradients of each corner are now:
944 // g000 = grad3[gi000];
945 // g001 = grad3[gi001];
946 // g010 = grad3[gi010];
947 // g011 = grad3[gi011];
948 // g100 = grad3[gi100];
949 // g101 = grad3[gi101];
950 // g110 = grad3[gi110];
951 // g111 = grad3[gi111];
952 // Calculate noise contributions from each of the eight corners
962 a
.x
= Vu0DotProduct(&pgrad3
[gi000
], &vec
);
967 a
.z
= Vu0DotProduct(&pgrad3
[gi010
], &vec
);
972 b
.z
= Vu0DotProduct(&pgrad3
[gi110
], &vec
);
977 b
.x
= Vu0DotProduct(&pgrad3
[gi100
], &vec
);
982 b
.y
= Vu0DotProduct(&pgrad3
[gi101
], &vec
);
987 b
.w
= Vu0DotProduct(&pgrad3
[gi111
], &vec
);
992 a
.w
= Vu0DotProduct(&pgrad3
[gi011
], &vec
);
997 a
.y
= Vu0DotProduct(&pgrad3
[gi001
], &vec
);
999 // Compute the fade curve value for each of x, y, z
1004 // TODO: Low priority... This could be done on VU0 (xyzw for the first 4 mixes)
1006 // Interpolate along x the contributions from each of the corners
1008 VU0MixVec(&b
, &a
, u
, &rv
);
1010 // TODO: The VU0MixVec could as well mix the results (as follows) - might improve performance...
1011 // Interpolate the four results along y
1012 float nxy0
= mix(rv
.x
, rv
.z
, v
);
1013 float nxy1
= mix(rv
.y
, rv
.w
, v
);
1014 // Interpolate the two last results along z
1015 float nxyz
= mix(nxy0
, nxy1
, w
);
1020 static float dir
= 0.02;
1021 static float perz
= -100;
1022 static int pery
= 0;
1023 static unsigned char curbgColor
[3] = { 0, 0, 0 };
1025 static int cdirection(unsigned char a
, unsigned char b
) {
1034 void guiDrawBGPlasma() {
1037 // transition the colors
1038 curbgColor
[0] += cdirection(curbgColor
[0], gTheme
->bgColor
[0]);
1039 curbgColor
[1] += cdirection(curbgColor
[1], gTheme
->bgColor
[1]);
1040 curbgColor
[2] += cdirection(curbgColor
[2], gTheme
->bgColor
[2]);
1042 // it's PLASMA_ROWS_PER_FRAME rows a frame to stop being a resource hog
1043 if (pery
>= PLASMA_H
) {
1047 if (perz
> 100.0f
|| perz
< -100.0f
)
1051 u32
*buf
= gBackgroundTex
.Mem
+ PLASMA_W
* pery
;
1052 int ymax
= pery
+ PLASMA_ROWS_PER_FRAME
;
1054 if (ymax
> PLASMA_H
)
1057 for (y
= pery
; y
< ymax
; y
++) {
1058 for (x
= 0; x
< PLASMA_W
; x
++) {
1059 u32 fper
= guiCalcPerlin((float) (2 * x
) / PLASMA_W
, (float) (2 * y
) / PLASMA_H
, perz
) * 0x080 + 0x080;
1061 *buf
= GS_SETREG_RGBA(
1062 (u32
)(fper
* curbgColor
[0]) >> 8,
1063 (u32
)(fper
* curbgColor
[1]) >> 8,
1064 (u32
)(fper
* curbgColor
[2]) >> 8,
1073 rmDrawPixmap(&gBackgroundTex
, 0, 0, ALIGN_NONE
, screenWidth
, screenHeight
, SCALING_NONE
, gDefaultCol
);
1076 int guiDrawIconAndText(int iconId
, int textId
, int font
, int x
, int y
, u64 color
) {
1077 GSTEXTURE
* iconTex
= thmGetTexture(iconId
);
1078 if (iconTex
&& iconTex
->Mem
) {
1079 rmDrawPixmap(iconTex
, x
, y
, ALIGN_NONE
, iconTex
->Width
, iconTex
->Height
, SCALING_RATIO
, gDefaultCol
);
1080 x
+= iconTex
->Width
+ 2;
1083 x
= fntRenderString(font
, x
, y
, ALIGN_NONE
, 0, 0, _l(textId
), color
);
1088 static void guiDrawOverlays() {
1089 // are there any pending operations?
1090 int pending
= ioHasPendingRequests();
1098 if (bfadeout
< 0x080)
1109 if (time_since_last
!= 0)
1110 snprintf(fps
, 20, "%3d ms %3.1f FPS", time_render
, 1000.0f
/ (float) time_since_last
);
1112 snprintf(fps
, 20, "%3d ms ----- FPS", time_render
);
1114 fntRenderString(gTheme
->fonts
[0], screenWidth
- 90, 30, ALIGN_CENTER
, 0, 0, fps
, GS_SETREG_RGBA(0x060, 0x060, 0x060, 0x060));
1118 static void guiReadPads() {
1120 guiInactiveFrames
= 0;
1122 guiInactiveFrames
++;
1125 // renders the screen and handles inputs. Also handles screen transitions between numerous
1126 // screen handlers. For now we only have left-to right screen transition
1127 static void guiShow() {
1128 // is there a transmission effect going on or are
1129 // we in a normal rendering state?
1130 if (screenHandlerTarget
) {
1131 // advance the effect
1133 // render the old screen, transposed
1134 rmSetTransposition(transIndex
* transitionX
, transIndex
* transitionY
);
1135 screenHandler
->renderScreen();
1137 // render new screen transposed again
1138 rmSetTransposition((transIndex
- transMax
) * transitionX
, (transIndex
- transMax
) * transitionY
);
1139 screenHandlerTarget
->renderScreen();
1141 // reset transposition to zero
1142 rmSetTransposition(0, 0);
1144 // move the transition indicator forward
1145 transIndex
+= (min(transIndex
, transMax
- transIndex
) >> 1) + 1;
1147 if (transIndex
> transMax
) {
1150 screenHandler
= screenHandlerTarget
;
1151 screenHandlerTarget
= NULL
;
1154 // render with the set screen handler
1155 screenHandler
->renderScreen();
1158 void guiIntroLoop(void) {
1165 if (wfadeout
< 0x0FF)
1172 guiRenderGreeting();
1176 guiHandleDeferredOps();
1180 if (!screenHandlerTarget
&& screenHandler
)
1181 screenHandler
->handleInput();
1185 void guiMainLoop(void) {
1186 while (!gTerminate
) {
1189 // Read the pad states to prepare for input processing in the screen handler
1192 // handle inputs and render screen
1195 // Render overlaying gui thingies :)
1198 // handle deferred operations
1199 guiHandleDeferredOps();
1203 // if not transiting, handle input
1204 // done here so we can use renderman if needed
1205 if (!screenHandlerTarget
&& screenHandler
)
1206 screenHandler
->handleInput();
1213 void guiSetFrameHook(gui_callback_t cback
) {
1217 void guiSwitchScreen(int target
, int transition
) {
1218 if (transition
== TRANSITION_LEFT
) {
1220 transMax
= screenWidth
;
1221 } else if (transition
== TRANSITION_RIGHT
) {
1223 transMax
= screenWidth
;
1224 } else if (transition
== TRANSITION_UP
) {
1226 transMax
= screenHeight
;
1227 } else if (transition
== TRANSITION_DOWN
) {
1229 transMax
= screenHeight
;
1233 screenHandlerTarget
= &screenHandlers
[target
];
1236 struct gui_update_t
*guiOpCreate(gui_op_type_t type
) {
1237 struct gui_update_t
*op
= (struct gui_update_t
*) malloc(sizeof(struct gui_update_t
));
1238 memset(op
, 0, sizeof(struct gui_update_t
));
1243 void guiUpdateScrollSpeed(void) {
1244 // sanitize the settings
1245 if ((gScrollSpeed
< 0) || (gScrollSpeed
> 2))
1248 // update the pad delays for KEY_UP and KEY_DOWN
1249 // default delay is 7
1253 setButtonDelay(KEY_UP
, 500 - gScrollSpeed
* 200); // 0,1,2 -> 500, 300, 100
1254 setButtonDelay(KEY_DOWN
, 500 - gScrollSpeed
* 200);
1257 void guiUpdateScreenScale(void) {
1259 wideScreenScale
= 0.75f
;
1261 wideScreenScale
= 1.0f
;
1263 // apply the scaling to renderman and font rendering
1264 rmSetAspectRatio(wideScreenScale
, 1.0f
);
1265 fntSetAspectRatio(wideScreenScale
, 1.0f
);
1268 int guiMsgBox(const char* text
, int addAccept
, struct UIItem
*ui
) {
1270 while (!terminate
) {
1275 if (getKeyOn(KEY_CIRCLE
))
1277 else if (getKeyOn(KEY_CROSS
))
1281 diaRenderUI(ui
, screenHandler
->inMenu
, NULL
, 0);
1285 rmDrawRect(0, 0, screenWidth
, screenHeight
, gColDarker
);
1287 rmDrawLine(50, 75, screenWidth
- 50, 75, gColWhite
);
1288 rmDrawLine(50, 410, screenWidth
- 50, 410, gColWhite
);
1290 fntRenderString(gTheme
->fonts
[0], screenWidth
>> 1, gTheme
->usedHeight
>> 1, ALIGN_CENTER
, 0, 0, text
, gTheme
->textColor
);
1291 guiDrawIconAndText(CIRCLE_ICON
, _STR_O_BACK
, gTheme
->fonts
[0], 500, 417, gTheme
->selTextColor
);
1293 guiDrawIconAndText(CROSS_ICON
, _STR_X_ACCEPT
, gTheme
->fonts
[0], 70, 417, gTheme
->selTextColor
);
1298 return terminate
- 1;
1301 void guiHandleDeferedIO(int *ptr
, const unsigned char* message
, int type
, void *data
) {
1302 ioPutRequest(type
, data
);
1305 guiRenderTextScreen(message
);
1308 void guiRenderTextScreen(const unsigned char* message
) {
1313 rmDrawRect(0, 0, screenWidth
, screenHeight
, gColDarker
);
1315 fntRenderString(gTheme
->fonts
[0], screenWidth
>> 1, gTheme
->usedHeight
>> 1, ALIGN_CENTER
, 0, 0, message
, gTheme
->textColor
);