/*
***********************************************************************************
fpPS4 Temmie's Launcher
design.js
This file contains tools, functions and variables related for rendering and
updating main GUI
Quick note: This is probably the largest file on this project... again!
***********************************************************************************
*/
temp_DESIGN = {
/*
Import design modules
*/
msgsys: temp_MSGSYS, // Message system
about: temp_ABOUTSCREEN, // About screen
input: temp_INPUT_DESIGN, // Input handling
animations: temp_ANIMATIONS, // Animarions
settingsMenu: temp_SETTINGSGUI, // Settings GUI
sceneManager: temp_SCENEMANAGER, // Scene Manager
quickSettings: temp_QUICKSETTINGS, // Quick settings (Right)
/*
General / Misc. variables
*/
// Dom elements with error class
btnWithErrorClass: [],
/*
General / Misc. functions
*/
// Add btn error class to DOM
addErrorClass: function(){
// Get current dom name
const domId = this.input.currentList + '_' + this.input.currentIndex;
// Check if dom id is defined and if it already exists on list
if (domId !== void 0 && this.btnWithErrorClass.indexOf(domId) === -1){
// Push current element to list
this.addClass(domId, 'BTN_GUI_OPTIONS_ERROR');
this.btnWithErrorClass.push(domId);
}
},
// Remove error class on dom
removeErrorClass: function(){
// Check if lists has entries
if (this.btnWithErrorClass.length !== 0){
// Remove class from elements
this.btnWithErrorClass.forEach(function(cDom){
TMS.removeClass(cDom, 'BTN_GUI_OPTIONS_ERROR');
});
// Reset list
this.btnWithErrorClass = [];
}
// End
return 0;
},
// Add class to dom
addClass: function(domId, className){
// Check if data was provided
if (domId !== void 0 && className !== void 0){
// Remove previous affected buttons
this.removeErrorClass();
// Remove class
TMS.removeClass(domId, className);
// Add class
TMS.addClass(domId, className);
}
},
/*
Window functions
*/
// [Window] On focus action
winOnFocusAction: function(){
this.input.focus();
},
// [Window] On blur action
winOnBlurAction: function(){return;},
// Focus main window
focusMainWindow: function(){
// Show window
APP.win.show();
APP.win.focus();
// Update scaling
setTimeout(function(){
APP.design.updateCanvasScale();
}, 100);
},
// Initialize window functions
initWindowFn: function(){
// Update canvas scaling
APP.win.on('resize', function(){
APP.design.updateCanvasScale();
});
APP.win.on('maximize', function(){
APP.design.toggleFullscreen();
});
APP.win.on('restore', function(){
APP.design.updateCanvasScale();
});
// On focus
APP.win.on('focus', function(){
APP.design.winOnFocusAction();
});
// On blur
APP.win.on('blur', function(){
APP.design.winOnBlurAction();
});
// On close
APP.win.on('close', function(){
APP.exit();
});
// Start canvas check
window.requestAnimationFrame(APP.design.updateCanvas);
},
// Update canvas size
updateCanvas: function(){
// Check canvas scale
APP.design.updateCanvasScale();
// End
window.requestAnimationFrame(APP.design.updateCanvas);
},
// Scale canvas
updateCanvasScale: function(){
// Variables
var scaleMode = APP.settings.data.screenScaleMode,
width = TMS.getCssData('APP_CANVAS', 'offsetWidth'),
height = TMS.getCssData('APP_CANVAS', 'offsetHeight'),
windowWidth = parseFloat(TMS.getCssData('APP_LOCKINPUT', 'width').replace('px', '')), // window.outerWidth,
windowHeight = parseFloat(TMS.getCssData('APP_LOCKINPUT', 'height').replace('px', '')), // window.outerHeight,
finalScale = Math.min((windowWidth / width), (windowHeight / height));
// Scale mode
switch (scaleMode){
// Zoom
case 'zoom':
TMS.css('APP_CANVAS', {'transform': 'none', 'zoom': finalScale});
break;
// Transform (Image it's more blurry)
case 'transform':
TMS.css('APP_CANVAS', {'zoom': '1', 'transform': 'scale(' + finalScale +')'});
break;
}
// Update position
TMS.css('APP_CANVAS', {
'top': 'calc(50% - ' + parseFloat(height / 2) + 'px)',
'left': 'calc(50% - ' + parseFloat(width / 2) + 'px)'
});
},
// Update screen res
updateCanvasRes: function(width, height){
// Variables
var w = APP.settings.data.screenWidth,
h = APP.settings.data.screenHeight;
// Check provided res
if (width !== void 0 && parseInt(width) !== NaN){
w = parseInt(width);
}
if (height !== void 0 && parseInt(height) !== NaN){
h = parseInt(height);
}
if (w < 1000){
w = 1000;
}
if (h < 720){
h = 720;
}
// Perform update
TMS.css('APP_CANVAS', {'width': w + 'px', 'height': h + 'px'});
// Update inner canvas zoom
TMS.css('APP_CANVAS_INNER', {'zoom': APP.settings.data.guiZoomScale});
},
// Toggle maximize
toggleMaximize: function(){
// Get window state
var winState = APP.win.cWindow.state;
// Switch between modes
switch (winState){
// Default window
case 'normal':
this.toggleFullscreen();
break;
// If maximized
case 'maximized':
APP.win.restore();
break;
}
// Update canvas scale
setTimeout(function(){
APP.design.updateCanvasScale();
}, 50);
},
// Toggle fullscreen
toggleFullscreen: function(){
// Check if isn't running on editor
if (APP.urlParams.get('dev') !== 'true'){
APP.win.toggleFullscreen();
}
},
/*
Forms variables
*/
// Forms list
formList: {
'iconBoot': '
',
'labelBootInfo': '%_DATA_%
',
'msgsys': ''
},
/*
Form functions
*/
// Append form into HTML
appendForm: function(formId, innerData, location){
// Check if form exists
if (formId !== void 0 && this.formList[formId] !== void 0){
// Variables
var sLocation = location,
replaceData = innerData;
// Check if innerData is available
if (innerData === void 0){
replaceData = '';
}
// Set default location
if (location === void 0){
sLocation = 'APP_CANVAS_INNER';
}
// Get form data
const formHtml = this.formList[formId].replace('%_DATA_%', replaceData);
// End
TMS.append(sLocation, formHtml);
}
},
/*
Boot sequence variables
*/
// Message to show on boot
bootMessageData: {},
/*
Boot sequence functions
*/
// Load font before loading settings
loadExternalFont: function(){
// Variables
var fontPath = APP.settings.data.externalFontPath,
fontDom = document.getElementById('SETTINGS_CUSTOM_FONT');
// Check if external font file exists
if (APP.fs.existsSync(fontPath) === !0){
// Check if DOM exists
if (fontDom !== null){
document.getElementById('SETTINGS_CUSTOM_FONT').innerHTML = '@font-face { font-family: customFont; src: url(\"' + fontPath + '\");';
} else {
TMS.append('ASSETS_LIST', '');
}
}
},
// Check if needs to show some message before boot
bootCheck: function(){
// Focus launcher window
APP.win.focus();
// Check if msg variable have metadata (messages)
if (this.bootMessageData.msgName !== ''){
// Log error code
APP.log.add({data: this.bootMessageData});
// Display message
APP.design.msgsys.displayMsg(this.bootMessageData);
} else {
// (Interpreter) Display intro message
if (APP.urlParams.get('skipBootList') !== 'true'){
APP.scriptInterpreter.run('boot_defaultBootProcess');
} else {
console.clear();
window.top.MAIN.log('INFO - INIT OK');
}
}
},
// Update background theme
updateBackgroundTheme: function(){
// Get settings
const settingsData = APP.settings.data;
// Update CSS
TMS.css('APP_CANVAS_BG_COLOR', {'background-image': `linear-gradient(140deg, ${settingsData.backgroundGradient.toString()})`});
TMS.css('APP_CANVAS', {'box-shadow': `0px 0px 120px ${settingsData.backgroundGradient[0]}90`});
// End
return 0;
},
// Check if needs to go on fullscreen on boot
bootCheckFullscreen: function(){
// Get settings
const settingsData = APP.settings.data;
// Check if fullscreen is on boot is enabled
if (settingsData.appIsLoading === !0 && settingsData.bootFullscreen === !0 && APP.win.isFullscreen === !1){
this.toggleFullscreen();
this.updateCanvasRes();
}
// End
return 0;
},
// Cache all images on img dir
cacheImagesBoot: function(){
// Create vars and process img path
var htmlTemp = '';
APP.tools.getDirFiles(`${APP.settings.appPath}/img`).forEach(function(cImg){
// Fix path and check if current entry is a file
const cDir = APP.tools.fixPath(cImg);
if (APP.path.parse(cDir).ext !== '') {
APP.log.add({ data: `INFO - (Design) Caching image: ${cDir}` });
htmlTemp = htmlTemp + `

`;
}
});
// Append HTML
TMS.append('ASSETS_LIST', `${htmlTemp}
`);
return 0;
},
/*
Lang functions
*/
// Update GUI language
updateLang: function(){
// Seleced lang database
const cLang = APP.lang.selected;
// If lang isn't English, update GUI
if (APP.settings.data.appLanguage !== 'english'){
// Update innerHTML
Object.keys(cLang.innerHTML).forEach(function(domId){
if (document.getElementById(domId) !== null && cLang.innerHTML[domId] !== ''){
document.getElementById(domId).innerHTML = cLang.innerHTML[domId];
}
});
}
},
/*
Gamelist variables
*/
// Current game list html
currentGameList: '',
/*
Gamelist functions
*/
// Render list
renderGameList: function(callback){
// Variables
var nextBtn = '',
prevBtn = '',
entryClass = '',
gList = Object.keys(APP.gameList.list),
displayMode = APP.settings.data.gameListMode,
tempHtml = APP.lang.getVariable('gameList_errorEntryListEmpty');
// Reset top actions background color
TMS.css('APP_MAIN_TOP', {'background-color': '#0000'});
// Switch from display mode
switch (displayMode){
// List
case 'list':
// Set entry mode
entryClass = 'GAMELIST_ENTRY_LIST';
// Set list
APP.design.input.setList({
enableOutOfBoundsFn: !0,
list: 'APP_GAMELIST_ENTRY',
index: APP.design.input.gListIndexPos,
length: (Object.keys(APP.gameList.list).length - 1),
// If user press up, focus top actions menu
onStart: function(){
return APP.design.bakedFunctions.GAMELIST_gotoTop();
},
// If user press down, return to first item on list
onEnd: function(){
TMS.css('APP_MAIN_GAMELIST', {'scroll-behavior': 'auto'});
return {position: 0, skipProcess: !1};
}
});
// Assign buttons
prevBtn = 'ARROW_UP';
nextBtn = 'ARROW_DOWN';
break;
}
// Get metadata
const getMetadataInfo = function(mode, data){
// Variables
var res = '',
gMetadata = '
';
// Empty game metadata if is homebrew
if (data.status === 'hb'){
gMetadata = '';
}
switch (mode){
// List mode
case 'list':
res = '' + gMetadata + '
' + APP.lang.getVariable('gameList_entryStatus_' + data.status) + '
';
}
return res;
}
// Check if there's items on entry list
if (gList.length !== 0){
// Reset list
tempHtml = '';
/*
[WIP] - Process entry list
*/
gList.forEach(function(cEntry, cIndex){
// Variables
var metadataHtml = '',
entryName = cEntry,
langId = APP.lang.selected.langId,
entryMetadata = APP.gameList.list[cEntry];
// Get entry name from PARAM.SFO and set metadata html
if (Object.keys(entryMetadata.paramSfo).length !== 0){
entryName = entryMetadata.paramSfo['TITLE' + langId];
metadataHtml = getMetadataInfo(displayMode, {name: entryName, status: entryMetadata.status, missingFiles: APP.tools.convertArrayToString(entryMetadata.missingFiles),
info: entryMetadata.paramSfo.TITLE_ID + ' - ' + APP.lang.getVariable('gameList_entryVersion', [entryMetadata.paramSfo.VERSION])});
} else {
metadataHtml = getMetadataInfo(displayMode, {name: entryName, status: entryMetadata.status, info: APP.lang.getVariable('gameList_entryStatus_hb')});
}
// Add entry
tempHtml = tempHtml + '';
});
// Update button labels
this.input.updateButtonLabels({
resetInput: !0,
target: 'MAIN_GAME_LIST',
displayButtons: ['ACTION_0', 'ACTION_2', 'ACTION_12'],
buttonLabels: {'ACTION_0': 'run', 'ACTION_2': 'hackList', 'ACTION_12': 'menu'},
// Bind input actions
callback: function(){
/*
Actions
*/
// Run entry
APP.input.setActionFn('ACTION_0', function(){
APP.design.input.selectMainAction();
APP.design.input.gListIndexPos = APP.design.input.currentIndex;
APP.design.animations['ANIMATION_startEmu_' + displayMode]();
});
// Open hack list
APP.input.setActionFn('ACTION_2', function(){
APP.design.bakedFunctions.GAMELIST_gotoHackList();
});
// Home: Goto top
APP.input.setActionFn('ACTION_12', function(){
APP.design.bakedFunctions.GAMELIST_gotoTop();
});
// Next / Prev buttons
APP.input.setActionFn(prevBtn, function(){
APP.design.input.moveCursor('prev');
});
APP.input.setActionFn(nextBtn, function(){
APP.design.input.moveCursor('next');
});
}
});
// Render selected game
this.displaySelectedGame();
} else {
// If there's no entries, goto top
this.bakedFunctions.GAMELIST_gotoTop();
}
// Check if need to update html
if (tempHtml !== this.currentGameList){
// Append HTML and update currentGameList
document.getElementById('APP_MAIN_GAMELIST').innerHTML = tempHtml;
this.currentGameList = tempHtml;
}
/*
End
*/
// Set window blur action
APP.design.winOnBlurAction = function(){return;}
// Execute callback
if (typeof callback === 'function'){
callback();
}
// End
return 0;
},
// Update GUI on selecing a game
displaySelectedGame: function(){
// Variables
var cIndex = APP.design.input.currentIndex,
entryMetadata = APP.gameList.list[Object.keys(APP.gameList.list)[cIndex]],
res = entryMetadata;
// Check if current list is game list
if (APP.design.input.currentList === 'APP_GAMELIST_ENTRY' && entryMetadata !== void 0){
// Set selected game
APP.gameList.selectedGame = entryMetadata.entryName;
// Set scroll behavior to smooth
TMS.css('APP_MAIN_GAMELIST', {'scroll-behavior': 'smooth'});
// Hide top bg
TMS.css('APP_MAIN_TOP', {'background-color': '#0000'});
// Give focus
TMS.focus('APP_GAMELIST_ENTRY_' + cIndex);
// Scroll entry to center
if (APP.settings.data.gameListMode !== 'orbis'){
TMS.scrollCenter('APP_GAMELIST_ENTRY_' + cIndex);
}
// Set background image
TMS.css('APP_CANVAS_BG', {'background-image': `url(\"${entryMetadata.img_background}\")`});
// Check if app is loading
if (APP.settings.appIsLoading === !0){
res = 0;
}
} else {
// Focus current index
TMS.focus(APP.design.input.currentList + '_' + cIndex);
res = 0;
}
/*
End
Return entry metadata
*/
return res;
},
/*
Utility functions
*/
// Get checkbox state
getCheckboxState: function(target){
if (target !== void 0 && target !== ''){
// Variables
var res = 'on',
status = JSON.parse(APP.tools.getVariable(target));
// Get state
if (status === !1){
res = 'off';
}
return res;
}
},
/*
Toggle checkbox [WIP]
data: {Object}
target: String - Name of affected variable
domId: String - DOM ID of checkbox
saveSettings: Boolean - If true, will save settings after process
callback: Function - function to be execute after process is complete
toggleClass: String - Name of css class to be used on toggle (_on or _off)
*/
toggleCheckbox: function(data){
// Check if data is provided
if (data !== void 0 && Object.keys(data).length !== 0){
// Reset add button class
APP.design.removeErrorClass();
// Get data
var svgState = 'on',
removeClass = 'off',
toggleClass = 'IMG_GUI_CHECKBOX',
checkboxState = APP.tools.getVariable(data.target);
// Check if toggle class is defined
if (data.toggleClass !== void 0 && data.toggleClass !== ''){
toggleClass = data.toggleClass;
}
// Process state
if (checkboxState === !0){
svgState = 'off';
removeClass = 'on';
checkboxState = !1;
} else {
checkboxState = !0;
}
// Update data
(Function('"use strict";return APP.' + data.target + '=' + checkboxState + ';')());
// Update class
TMS.addClass(data.domId, toggleClass + '_' + svgState);
TMS.removeClass(data.domId, toggleClass + '_' + removeClass);
// Update icon
if (document.getElementById(data.domId) !== null){
document.getElementById(data.domId).src = 'img/svg/checkbox-' + svgState + '.svg';
}
// Save settings
if (data.saveSettings === !0){
APP.settings.save();
}
// Callback
if (typeof data.callback === 'function'){
data.callback();
}
}
},
/*
Baked GUI functions
*/
bakedFunctions: {
// Game list - Default onclick action
GAMELIST_defaultOnclickAction: function(index){
APP.design.renderGameList();
APP.design.input.currentIndex = index;
APP.design.displaySelectedGame();
},
// Game list - Show about screen
GAMELIST_showAbout: function(){
// Save current cursor index
if (APP.design.input.currentList === 'APP_GAMELIST_ENTRY'){
APP.design.input.gListIndexPos = APP.design.input.currentIndex;
}
// Fade out bg and fade in "4" icon
TMS.css('APP_CANVAS_BG', {'opacity': '0'});
// Call about screen
APP.design.msgsys.displayMsg({showBgIcon: !0, msgName: 'general_showAbout'});
// Fix scroll
document.getElementById('APP_LOCKINPUT').onmousewheel = function(data){
document.getElementById('fpPS4_TL_ABOUT_DATA').scrollBy({top: data.deltaY, left: 0});
};
},
// About: Close and return to game list
GAMELIST_closeAbout: function(){
// Reset mousewheel
document.getElementById('APP_LOCKINPUT').onmousewheel = null;
// Fade out "4" and fade in bg
TMS.css('APP_CANVAS_BG', {'opacity': '0.62'});
TMS.css('APP_CANVAS_BG_ICON', {'opacity': '0'});
// Render game list
APP.design.renderGameList(function(){
APP.design.displaySelectedGame();
APP.input.releaseInput();
});
},
// Game list - Go to top actions if user press up while first entry is selected
GAMELIST_gotoTop: function(){
// Lock input
APP.input.lockInput();
// Show top actions menu bg
TMS.css('APP_MAIN_TOP', {'background-color': '#000a'});
// Save current cursor index
APP.design.input.gListIndexPos = APP.design.input.currentIndex;
// Set new list
APP.design.input.setList({
index: 1,
length: 3,
list: 'INPUT_GUI_TOP',
enableOutOfBoundsFn: !0
});
// Update button labels
APP.design.input.updateButtonLabels({
resetInput: !0,
target: 'MAIN_GAME_LIST',
displayButtons: ['ACTION_0', 'ACTION_1'],
buttonLabels: {'ACTION_0': 'select', 'ACTION_1': 'back'},
// Bind input actions
callback: function(){
// Bind buttons
APP.input.setActionFn('ACTION_0', function(){
APP.design.input.selectMainAction();
});
APP.input.setActionFn('ARROW_LEFT', function(){
APP.design.input.moveCursor('prev');
});
APP.input.setActionFn('ARROW_RIGHT', function(){
APP.design.input.moveCursor('next');
});
// Home, Down Arrow and Back: Return to list
APP.input.setActionFn('ARROW_DOWN', function(){
APP.design.renderGameList(function(){ APP.design.displaySelectedGame(); });
});
APP.input.setActionFn('ACTION_1', function(){
APP.design.renderGameList(function(){ APP.design.displaySelectedGame(); });
});
APP.input.setActionFn('ACTION_12', function(){
APP.design.renderGameList(function(){ APP.design.displaySelectedGame(); });
});
// Release input
APP.input.releaseInput();
}
});
// Return
return {position: 0, skipProcess: !0};
},
// Game list - Open Hack list
GAMELIST_gotoHackList: function(){
// Lock input
APP.input.lockInput();
// Variables
var htmlTemp = '',
cGame = APP.gameList.selectedGame,
hList = Object.keys(APP.emumanager.hackList),
checkboxInput = APP.settings.data.input_toggleCheckBox,
bLabels = '{"ACTION_1": "back", "' + checkboxInput + '": "toggleHack"}',
jsonPath = APP.settings.nwPath + '/Settings/Game Settings/' + cGame + '.json',
cGameSettings = JSON.parse(APP.fs.readFileSync(jsonPath, 'utf8'));
// Set current game settings
APP.emumanager.cGameSettings = cGameSettings;
APP.emumanager.tempSettings = JSON.stringify(cGameSettings);
/*
Process hack list
*/
hList.forEach(function(cHack, cIndex){
// Get hack description
var hackDesc = APP.emumanager.hackList[cHack],
cTarget = 'emumanager.cGameSettings.hackList.' + cHack,
hStatus = APP.design.getCheckboxState(cTarget);
// Update html
htmlTemp = htmlTemp + '';
});
// Update button labels
APP.design.input.updateButtonLabels({
resetInput: !0,
target: 'MAIN_GAME_LIST',
buttonLabels: JSON.parse(bLabels),
displayButtons: [checkboxInput, 'ACTION_1'],
callback: function(){
// Render quick settings
APP.design.quickSettings.show({
width: 44,
showTitle: !1,
content: htmlTemp,
// Execute action after closing quicksettings
onClose: function(){
// Update game settings
try {
// Stringify settings
const newSettings = JSON.stringify(APP.emumanager.cGameSettings);
// Check if need to update file
if (newSettings !== APP.emumanager.tempSettings){
// Update file
APP.log.add({data: 'INFO - (Hack list) Updating settings for ' + cGame});
APP.fs.writeFileSync(jsonPath, newSettings, 'utf8');
}
// Update emumanager variables
APP.emumanager.tempSettings = '';
APP.emumanager.cGameSettings = {};
// Render game list
APP.design.renderGameList(function(){ APP.design.displaySelectedGame(); });
} catch (err) {
throw new Error(err);
}
},
// Callback
callback: function(){
// Set input list
APP.design.input.setList({
index: 0,
enableOutOfBoundsFn: !1,
list: 'MAIN_QUICK_SETTINGS',
length: parseInt(hList.length - 1)
});
// Bind actions
APP.input.setActionFn(checkboxInput, function(){
APP.design.input.selectMainAction();
});
APP.input.setActionFn('ARROW_UP', function(){
APP.design.input.moveCursor('prev');
});
APP.input.setActionFn('ARROW_DOWN', function(){
APP.design.input.moveCursor('next');
});
// Close quick settings
APP.input.setActionFn('ACTION_1', function(){
APP.design.quickSettings.close();
});
APP.input.setActionFn('ARROW_LEFT', function(){
APP.design.quickSettings.close();
});
}
});
}
});
}
}
}
// Delete imported modules
delete temp_MSGSYS;
delete temp_ANIMATIONS;
delete temp_ABOUTSCREEN;
delete temp_SETTINGSGUI;
delete temp_SCENEMANAGER;
delete temp_INPUT_DESIGN;
delete temp_QUICKSETTINGS;