/** * PSFree Enhanced UI * Compatible with PS4 FW 9.00 Browser * TODO: Implement controller navigation and optimize for PS4 display */ // Status constants const STATUS = { WAITING: 'waiting', RUNNING: 'running', SUCCESS: 'success', ERROR: 'error' }; // Global state let state = { status: STATUS.RUNNING, // Changed from WAITING to RUNNING because it auto-runs progress: 0, selectedPayload: 'payload.bin', // Always uses the default payload customPayloads: [], settings: { autoRun: true, // Changed to true for auto-run verboseLogging: false, safeMode: true }, controllerNavigation: { enabled: false, currentFocusIndex: 0, focusableElements: [] } }; // TODO: Exploit will run automatically when the page loads // Original console.log function const originalLog = console.log; // Override console.log to also update the UI console.log = function(...args) { // Call original console.log originalLog.apply(console, args); // Update UI console const message = args.map(arg => { if (typeof arg === 'object') { return JSON.stringify(arg); } return arg; }).join(' '); appendToConsole(message); }; // Function to append text to the console function appendToConsole(text) { const consoleElement = document.getElementById('console'); if (consoleElement) { consoleElement.textContent += text + '\n'; consoleElement.scrollTop = consoleElement.scrollHeight; } } // Function to clear the console function clearConsole() { const consoleElement = document.getElementById('console'); if (consoleElement) { consoleElement.textContent = ''; } } // Function to update the UI status function updateStatus(status, message) { state.status = status; const statusIcon = document.getElementById('status-icon'); const statusText = document.getElementById('status-text'); if (!statusIcon || !statusText) return; // Remove all status classes statusIcon.classList.remove('status-waiting', 'status-running', 'status-success', 'status-error'); // Add the appropriate class statusIcon.classList.add(`status-${status}`); // Update the status text statusText.textContent = message; // TODO: Exploit run and reset buttons have been removed } // Function to update the progress bar function updateProgress(percent) { state.progress = percent; const progressBar = document.getElementById('progress-bar'); if (progressBar) { progressBar.style.width = `${percent}%`; } } // Function to handle tab switching function switchTab(tabId) { // Hide all tab contents document.querySelectorAll('.tab-content').forEach(content => { content.classList.remove('active'); }); // Deactivate all tabs document.querySelectorAll('.tab').forEach(tab => { tab.classList.remove('active'); }); // Activate the selected tab and content const tabContent = document.getElementById(`tab-${tabId}`); const tabElement = document.querySelector(`.tab[data-tab="${tabId}"]`); if (tabContent && tabElement) { tabContent.classList.add('active'); tabElement.classList.add('active'); } } // Function to add a custom payload function addCustomPayload(name, data) { const payloadList = document.getElementById('payload-list'); if (!payloadList) return; // Create a new payload item const payloadItem = document.createElement('div'); payloadItem.className = 'payload-item'; payloadItem.dataset.payload = name; payloadItem.setAttribute('tabindex', '0'); // Make focusable for controller navigation // Create the payload content payloadItem.innerHTML = `

Custom payload (${formatBytes(data.byteLength)})

`; // Add the payload to the list payloadList.appendChild(payloadItem); // Store the payload data state.customPayloads.push({ name: name, data: data }); // Save to localStorage saveCustomPayloads(); // Update controller navigation updateFocusableElements(); } // Function to format bytes to human-readable format function formatBytes(bytes, decimals = 2) { if (bytes === 0) return '0 Bytes'; const k = 1024; const dm = decimals < 0 ? 0 : decimals; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]; } // Function to save custom payloads to localStorage function saveCustomPayloads() { try { // We can only store the names in localStorage, not the actual binary data const payloadNames = state.customPayloads.map(p => p.name); localStorage.setItem('customPayloadNames', JSON.stringify(payloadNames)); } catch (e) { console.log('Error saving custom payloads to localStorage:', e); } } // Function to save settings to localStorage function saveSettings() { try { localStorage.setItem('settings', JSON.stringify(state.settings)); console.log('Settings saved'); } catch (e) { console.log('Error saving settings to localStorage:', e); } } // Function to load settings from localStorage function loadSettings() { try { const savedSettings = localStorage.getItem('settings'); if (savedSettings) { state.settings = JSON.parse(savedSettings); // Update UI to reflect loaded settings const autoRunElement = document.getElementById('auto-run'); const verboseLoggingElement = document.getElementById('verbose-logging'); const safeModeElement = document.getElementById('safe-mode'); if (autoRunElement) autoRunElement.checked = state.settings.autoRun; if (verboseLoggingElement) verboseLoggingElement.checked = state.settings.verboseLogging; if (safeModeElement) safeModeElement.checked = state.settings.safeMode; } } catch (e) { console.log('Error loading settings from localStorage:', e); } } // Function to detect firmware version function detectFirmware() { const userAgent = navigator.userAgent; let firmware = null; // Detect PS4 firmware const ps4Match = userAgent.match(/PlayStation 4\/([0-9.]+)/); if (ps4Match && ps4Match[1]) { firmware = { console: 'PS4', version: ps4Match[1] }; } // Detect PS5 firmware const ps5Match = userAgent.match(/PlayStation 5\/([0-9.]+)/); if (ps5Match && ps5Match[1]) { firmware = { console: 'PS5', version: ps5Match[1] }; } return firmware; } // Function to check browser compatibility function checkCompatibility() { const userAgent = navigator.userAgent; let isCompatible = true; let message = ''; // Check if running on PS4/PS5 browser if (userAgent.includes('PlayStation 4') || userAgent.includes('PlayStation 5')) { console.log('PlayStation browser detected. Optimal compatibility.'); // Enable controller navigation if on PlayStation state.controllerNavigation.enabled = true; } else { console.log('WARNING: Not running in PlayStation browser. Some features may not work.'); isCompatible = false; message = 'Not running in PlayStation browser'; } // Check specific firmware version const firmware = detectFirmware(); if (firmware) { console.log(`Detected ${firmware.console} FW ${firmware.version}`); if (firmware.console === 'PS4' && firmware.version === '9.00') { console.log('Detected PS4 FW 9.00. Optimal compatibility.'); } } return { isCompatible, message, firmware }; } // Function to run the exploit function runExploit() { updateStatus(STATUS.RUNNING, 'Running exploit...'); updateProgress(5); // Clear the console if not in verbose mode if (!state.settings.verboseLogging) { clearConsole(); } console.log('Starting PSFree exploit...'); // The actual exploit will be triggered here via the import of lapse.mjs // This is handled by the original code in index.html // Listen for exploit progress events document.addEventListener('exploitProgress', function(event) { updateProgress(event.detail.percent); }); // Listen for exploit status events document.addEventListener('exploitStatus', function(event) { updateStatus(event.detail.status, event.detail.message); }); } // Function to reset the exploit function resetExploit() { updateStatus(STATUS.WAITING, 'Ready to start'); updateProgress(0); if (!state.settings.verboseLogging) { clearConsole(); } console.log('Exploit reset. Ready to start again.'); // Reload the page to reset everything if (confirm('The page will reload to reset the exploit. Continue?')) { window.location.reload(); } } // Function to update focusable elements for controller navigation function updateFocusableElements() { if (!state.controllerNavigation.enabled) return; // Get all focusable elements state.controllerNavigation.focusableElements = Array.from(document.querySelectorAll('button, .tab, .payload-item, input[type="checkbox"]')); // Reset focus index state.controllerNavigation.currentFocusIndex = 0; } // Function to handle controller navigation function handleControllerNavigation(event) { if (!state.controllerNavigation.enabled) return; const elements = state.controllerNavigation.focusableElements; let currentIndex = state.controllerNavigation.currentFocusIndex; // Handle navigation with D-pad switch (event.key) { case 'ArrowUp': currentIndex = Math.max(0, currentIndex - 1); break; case 'ArrowDown': currentIndex = Math.min(elements.length - 1, currentIndex + 1); break; case 'ArrowLeft': // If in tabs, navigate between tabs if (elements[currentIndex].classList.contains('tab')) { const tabs = Array.from(document.querySelectorAll('.tab')); const tabIndex = tabs.indexOf(elements[currentIndex]); if (tabIndex > 0) { currentIndex = elements.indexOf(tabs[tabIndex - 1]); } } else { currentIndex = Math.max(0, currentIndex - 1); } break; case 'ArrowRight': // If in tabs, navigate between tabs if (elements[currentIndex].classList.contains('tab')) { const tabs = Array.from(document.querySelectorAll('.tab')); const tabIndex = tabs.indexOf(elements[currentIndex]); if (tabIndex < tabs.length - 1) { currentIndex = elements.indexOf(tabs[tabIndex + 1]); } } else { currentIndex = Math.min(elements.length - 1, currentIndex + 1); } break; case 'Enter': // Simulate click on the focused element elements[currentIndex].click(); return; } // Update focus if (currentIndex !== state.controllerNavigation.currentFocusIndex) { // Remove focus from all elements elements.forEach(el => { el.classList.remove('controller-focus'); }); // Add focus to the current element elements[currentIndex].classList.add('controller-focus'); elements[currentIndex].focus(); // Update current index state.controllerNavigation.currentFocusIndex = currentIndex; } } // Setup event listeners function setupEventListeners() { // Tab switching - only for console tab document.querySelectorAll('.tab').forEach(tab => { tab.addEventListener('click', () => { switchTab(tab.dataset.tab); }); }); // TODO: Run exploit, reset, upload payload, and settings buttons have been removed // Controller navigation document.addEventListener('keydown', handleControllerNavigation); // Custom events from exploit document.addEventListener('exploitProgress', function(event) { updateProgress(event.detail.percent); }); document.addEventListener('exploitStatus', function(event) { updateStatus(event.detail.status, event.detail.message); }); // Payload events document.addEventListener('payloadLoaded', function(event) { console.log(`Payload loaded: ${event.detail.name} (${formatBytes(event.detail.size)})`); }); document.addEventListener('payloadError', function(event) { console.log(`Error loading payload ${event.detail.name}: ${event.detail.error}`); updateStatus(STATUS.ERROR, `Error loading payload: ${event.detail.error}`); }); } // Initialize the UI function initUI() { console.log('Initializing PSFree UI...'); // Load settings loadSettings(); // Setup event listeners setupEventListeners(); // Check compatibility checkCompatibility(); // Update focusable elements for controller navigation updateFocusableElements(); // Always run exploit automatically setTimeout(() => { console.log('Automatically running exploit...'); runExploit(); }, 1000); console.log('UI initialized. Exploit will auto-run.'); // TODO: Exploit will always auto-run without conditions } // Initialize when the DOM is fully loaded document.addEventListener('DOMContentLoaded', initUI);