From 7b7a9fac8a3e3cfb743a3bed8d372e8a3ede9d48 Mon Sep 17 00:00:00 2001 From: thom2305 Date: Sun, 6 Jul 2025 17:36:11 -0400 Subject: [PATCH] smol updates --- background.js | 307 ++++++++++++++++++++--------- classDefinitions.js | 274 ++++++++++++-------------- content.js | 459 ++++++++++++++++++++++++++------------------ manifest.json | 1 + popup.js | 113 +++++++---- 5 files changed, 682 insertions(+), 472 deletions(-) diff --git a/background.js b/background.js index 96a0869..879182c 100644 --- a/background.js +++ b/background.js @@ -1,27 +1,37 @@ // background.js -// Function to block specific requests -function blockRequest(details) { - // Check if the request URL matches the file you want to block - if (details.url.includes("title_manager.js")) { - console.log("Blocking request for:", details.url); - return { cancel: true }; // Cancel the request +const runtimeAPI = typeof browser !== 'undefined' ? browser : chrome; +const storageAPI = runtimeAPI.storage.local; + +let currentBgmAudio = null; +let bgmEnabledState = false; +let logs = []; // In-memory log cache +let currentPoints = 0; // In-memory points cache + +const DEBUG_REDIRECT_SERIAL = "PC156494873"; // Centralized constant + +// --- Initialization on startup --- +async function initializeBackground() { + try { + const result = await storageAPI.get(['bgmEnabled', 'logs', 'pts']); + bgmEnabledState = result.bgmEnabled === true; + logs = result.logs || []; + currentPoints = result.pts || 0; + console.log("Background: Initial states loaded. BGM:", bgmEnabledState, "Logs count:", logs.length, "Points:", currentPoints); + } catch (error) { + console.error("Background: Error during initialization:", error); } } +initializeBackground(); -// Check if the browser is Firefox or Chrome -const isFirefox = typeof browser !== "undefined"; -// background.js +// --- Web Request Listeners --- -// Handle all miip:// requests -chrome.webRequest.onBeforeRequest.addListener( +// 1. Block specific requests (title_manager.js) +runtimeAPI.webRequest.onBeforeRequest.addListener( function(details) { - console.log("[DEBUG] Intercepted URL:", details.url); - - if (details.url.startsWith('miip://')) { - const redirectUrl = chrome.runtime.getURL("images/mii.bmp"); - console.log("[DEBUG] Redirecting to:", redirectUrl); - return { redirectUrl: redirectUrl }; + if (details.url.includes("title_manager.js")) { + console.log("Background: Blocking request for:", details.url); + return { cancel: true }; } return {}; }, @@ -29,84 +39,189 @@ chrome.webRequest.onBeforeRequest.addListener( ["blocking"] ); -let logs = []; - -chrome.webRequest.onBeforeRequest.addListener( - (details) => { - const url = new URL(details.url); - - if (url.pathname === '/_extension_storage') { - const params = new URLSearchParams(url.search); - - // GET Requests - if (params.has('get')) { - const key = params.get('get'); - console.log("Get key: " + key) - return new Promise(resolve => { - chrome.storage.local.get(key, result => { - if (key === 'logs') { - resolve({ - redirectUrl: `data:application/json,${JSON.stringify({ - value: logs - })}` - }); - } else { - resolve({ - redirectUrl: `data:application/json,${JSON.stringify({ - value: result[key] - })}` - }); - } - }); - }); - } - - // SET Requests - if (params.has('set_key')) { - const key = params.get('set_key'); - const value = params.get('set_value'); - console.log("Set key: " + key + " with value: " + value) - if (key === 'slogs') { - console.log("Set key: logs with value: " + value) - logs.push(`[SET] ${new Date().toISOString()}: ${value}`); - chrome.storage.local.set({ logs }); - } else { - chrome.storage.local.set({ [key]: value }); +// 2. Handle all miip:// requests +runtimeAPI.webRequest.onBeforeRequest.addListener( + function(details) { + console.log("Background: Intercepted URL:", details.url); + if (details.url.startsWith('miip://')) { + const redirectUrl = runtimeAPI.runtime.getURL("images/mii.bmp"); + console.log("Background: Redirecting miip:// to:", redirectUrl); + return { redirectUrl: redirectUrl }; } - return { cancel: true }; - } - - // CLEAR Logs - if (params.has('clogs')) { - console.log("Clear Logs") - logs = []; - chrome.storage.local.set({ logs: [] }); - return { cancel: true }; - } - if (params.has('set_key') && params.get('set_key') === 'spts') { - const pointsToAdd = parseInt(params.get('set_value')) || 0; - - chrome.storage.local.get(['pts'], (result) => { - const currentPoints = parseInt(result.pts) || 0; - const newTotal = currentPoints + pointsToAdd; - - // Update both in-memory and storage - logs.push(`[POINTS] Added ${pointsToAdd} points (Total: ${newTotal})`); - chrome.storage.local.set({ - pts: newTotal, - logs - }); - }); - - return { cancel: true }; - } - if (params.has('cpts')) { - console.log("Clear Points") - chrome.storage.local.set({ pts: 0 }); - return { cancel: true }; - } + return {}; + }, + { urls: [""] }, // Listen for all URLs to catch miip:// scheme + ["blocking"] +); + +// 3. Redirect debug.jsp if Serial parameter is missing +runtimeAPI.webRequest.onBeforeRequest.addListener( + function(details) { + const url = new URL(details.url); + if (url.pathname.includes('/oss/serv/debug.jsp')) { + const serialParam = url.searchParams.get('Serial'); + if (!serialParam) { + console.warn("Background: Intercepted debug.jsp without Serial. Adding static Serial and redirecting."); + url.searchParams.set('Serial', DEBUG_REDIRECT_SERIAL); + const redirectUrl = url.toString(); + console.log("Background: Redirecting to:", redirectUrl); + return { redirectUrl: redirectUrl }; + } + } + return {}; + }, + { urls: ["*://oss-auth.thecheese.io/oss/serv/debug.jsp*"], types: ["main_frame"] }, + ["blocking"] +); + +// --- BGM Playback Functions (managed within background script) --- +function playBGM(url) { + if (currentBgmAudio) { + currentBgmAudio.pause(); + currentBgmAudio.currentTime = 0; + currentBgmAudio = null; } - }, - { urls: ["*://*/_extension_storage*"] }, - ["blocking"] -); \ No newline at end of file + + currentBgmAudio = new Audio(url); + currentBgmAudio.loop = true; + currentBgmAudio.volume = 0.5; // Default volume + + currentBgmAudio.play() + .then(() => console.log("Background: BGM started playing:", url)) + .catch(e => console.error("Background: Error playing BGM:", e)); +} + +function stopBGM() { + if (currentBgmAudio) { + currentBgmAudio.pause(); + currentBgmAudio.currentTime = 0; + currentBgmAudio = null; + console.log("Background: BGM stopped."); + } +} + +async function setBGMEnabled(enabled) { + bgmEnabledState = enabled; + try { + await storageAPI.set({ bgmEnabled: enabled }); + console.log("Background: BGM enabled state saved to storage:", enabled); + if (!enabled) { + stopBGM(); + } + } catch (error) { + console.error("Background: Error saving BGM enabled state:", error); + } +} + +async function setFontDisabled(disabled) { + try { + await storageAPI.set({ fontDisabled: disabled }); + console.log("Background: Font disabled state saved to storage:", disabled); + } catch (error) { + console.error("Background: Error saving font disabled state:", error); + } +} + + +// --- Main Message Listener (handles communication from popup and content script) --- +runtimeAPI.runtime.onMessage.addListener((request, sender, sendResponse) => { + // Return true for asynchronous sendResponse or Promise to keep the message channel open. + console.log("Background: Received message:", request.action, "from sender:", sender.tab ? "Content Script (Tab ID: " + sender.tab.id + ")" : "Popup/Extension"); + + switch (request.action) { + // --- BGM Control --- + case "playBGM": + // Only play if BGM is enabled by user preference + if (bgmEnabledState) { + playBGM(request.url); // Use request.url from the content script + } else { + console.log("Background: BGM disabled by user preference, not playing."); + } + sendResponse({ success: true }); + return false; // Synchronous acknowledgment + + case "stopBGM": + stopBGM(); + sendResponse({ success: true }); + return false; + + case "setBGMEnabled": // This handles toggle state and playback logic + setBGMEnabled(request.enabled); + sendResponse({ success: true }); + return false; + + case "getBGMEnabled": + sendResponse({ enabled: bgmEnabledState }); // Return the current in-memory state + return false; + + case "setBGMVolume": + if (currentBgmAudio) { + currentBgmAudio.volume = request.volume; + console.log("Background: BGM volume set to:", request.volume); + } + sendResponse({ success: true }); + return false; + + // --- Storage Actions --- + case "getStorage": + // Handles requests to get data from storage, including 'logs' + return (async () => { + const result = await storageAPI.get(request.key); + let valueToReturn; + if (request.key === 'logs') { + valueToReturn = logs; // Return in-memory logs + } else if (request.key === 'pts') { + valueToReturn = currentPoints; // Return in-memory points + } else { + valueToReturn = result[request.key]; + } + console.log(`Background: getStorage for ${request.key}, returning:`, valueToReturn); + return { value: valueToReturn }; // Send back the result (as a Promise) + })(); + + case "setStorage": + // Handles requests to set data in storage, including 'slog' for logs + return (async () => { + console.log(`Background: setStorage for ${request.key}, value:`, request.value); + if (request.key === 'slog') { // Special handling for logs (append) + logs.push(`[${new Date().toISOString()}] ${request.value}`); + // Optional: Limit log size if necessary (e.g., to last 100 entries) + if (logs.length > 100) logs = logs.slice(logs.length - 100); // Keep last 100 + await storageAPI.set({ logs: logs }); // Persist logs to local storage + } else if (request.key === 'spts') { // Special handling for points (add to existing) + const pointsToAdd = parseInt(request.value) || 0; + currentPoints += pointsToAdd; + await storageAPI.set({ pts: currentPoints }); + console.log("Background: Points updated to:", currentPoints); + } else { // Generic set for other keys + await storageAPI.set({ [request.key]: request.value }); + } + return { success: true }; // Acknowledge completion + })(); + + case "clearLogs": + return (async () => { + console.log("Background: Clearing logs."); + logs = []; // Clear in-memory logs + await storageAPI.set({ logs: [] }); // Clear stored logs + return { success: true }; + })(); + + case "clearPoints": + return (async () => { + console.log("Background: Clearing points."); + currentPoints = 0; // Clear in-memory points + await storageAPI.set({ pts: 0 }); + return { success: true }; + })(); + + // --- Font Control --- + case "setFontDisabled": /* ... */ return false; + case "getFontDisabled": /* ... */ return false; + + default: + console.warn("Background: Unrecognized message action:", request.action); + sendResponse({ success: false, error: "Unrecognized action" }); + return false; + } +}); \ No newline at end of file diff --git a/classDefinitions.js b/classDefinitions.js index 69edc81..3bea3a9 100644 --- a/classDefinitions.js +++ b/classDefinitions.js @@ -1,31 +1,18 @@ // classDefinitions.js (This code runs in the page's main world) -// IMPORTANT: This script will now receive URLs via window.postMessage from the content script. +// IMPORTANT: This script will now directly read variables prepended by the content script. + +// These will be populated by the prepended script content from content.js +// Make sure they are defined globally here, so they can be accessed. +//let _wiiShopSoundUrls_ = window._wiiShopSoundUrls_ || []; +//let _wiiShopBgmUrl_ = window._wiiShopBgmUrl_ || ''; +//let _bgmInitiallyEnabled = window._bgmInitiallyEnabled || false; // Capture the BGM state -// These will be populated by the message listener -let _wiiShopSoundUrls_ = []; -let _wiiShopBgmUrl_ = ''; -let _bgmInitiallyEnabled = false; // This flag will also be passed via postMessage if needed, or handled by content.js let _bgmAlreadyPlaying = false; // This flag can be managed internally by wiiSound // Add a variable to track the current status (matching your original logic) let _currentstatus = ""; -// --- Listener for messages from the content script --- -window.addEventListener('message', (event) => { - // Ensure the message is from our extension and the correct type - if (event.source === window && event.data && event.data.type === "WII_SHOP_EXTENSION_INIT") { - _wiiShopSoundUrls_ = event.data.soundUrls; - _wiiShopBgmUrl_ = event.data.bgmUrl; - _bgmInitiallyEnabled = event.data.bgmInitiallyEnabled; // Capture this if needed in classDefinitions.js - console.log("classDefinitions.js received URLs and initial BGM state from content script."); - - // If you need to trigger BGM playback from *within* classDefinitions.js based on initial state, do it here - // However, your content.js already handles this by sending a message to the background, which is better. - // The _bgmInitiallyEnabled flag is now more for reference within this script if needed by other logic. - } -}); - // Existing global variables exposed (these remain the same as your original) var ecsUrl = ""; @@ -48,6 +35,14 @@ window.lv = lv; window._currentstatus = _currentstatus; // Expose _currentstatus to the global scope +const _sendPageMessage = (action, payload = {}) => { + window.postMessage({ + type: 'WII_SHOP_EXTENSION_MESSAGE', // Custom type to identify our messages + action: action, + payload: payload + }, '*'); // Use '*' for targetOrigin if unsure, but specify if possible for security. +}; + // Define the trace function const trace = function(...args) { if (typeof extensionStorage !== 'undefined' && typeof extensionStorage.slog === 'function') { @@ -60,84 +55,77 @@ const trace = function(...args) { const storageCache = {}; const STORAGE_ENDPOINT = '/_extension_storage'; // This endpoint must be intercepted by background.js +// --- extensionStorage (UPDATED to use postMessage bridge) --- const extensionStorage = { - set: (key, value) => { - storageCache[key] = value; - fetch(`${STORAGE_ENDPOINT}?set_key=${encodeURIComponent(key)}&set_value=${encodeURIComponent(value)}`) - .catch(e => console.error("[Page] Error sending storage_set:", e)); + // Asynchronous set operation + set: async (key, value) => { + _sendPageMessage('extStorageSet', { key, value }); }, - get: (key) => { - if (key in storageCache) return storageCache[key]; + // Asynchronous get operation (requires awaiting a response via postMessage listener) + get: async (key) => { + return new Promise(resolve => { + const listener = (event) => { + // Ensure the message is from our extension, is a response, and matches our request + if (event.source === window && + event.data.type === 'WII_SHOP_EXTENSION_RESPONSE' && + event.data.action === 'extStorageGet' && + event.data.key === key) { // Match key for specific requests - const request = new XMLHttpRequest(); - // Use synchronous XHR for get, which is fine in page context - request.open('GET', `${STORAGE_ENDPOINT}?get=${encodeURIComponent(key)}`, false); - try { - request.send(null); - if (request.status === 200 || request.status === 0) { // status 0 for local file access in some contexts - const data = JSON.parse(request.responseText); - storageCache[key] = data.value; - return data.value; - } - } catch (e) { - console.error("[Page] Error getting storage via XMLHttpRequest:", e); - } - return null; + window.removeEventListener('message', listener); // Clean up listener + if (event.data.error) { + console.error("Error receiving extStorageGet response:", event.data.error); + resolve(null); // Resolve with null on error + } else { + resolve(event.data.payload.value); + } + } + }; + window.addEventListener('message', listener); // Set up listener BEFORE sending message + _sendPageMessage('extStorageGet', { key }); // Send the request + }); }, slog: (message) => { - fetch(`${STORAGE_ENDPOINT}?set_key=slogs&set_value=${encodeURIComponent(message)}`) - .catch(e => console.error("[Page] Error sending slog:", e)); + _sendPageMessage('extStorageSet', { key: 'slogs', value: message }); }, - getLogs: () => { - return extensionStorage.get('logs') || []; + getLogs: async () => { + const result = await extensionStorage.get('logs'); + return result || []; }, clearLogs: () => { - fetch(`${STORAGE_ENDPOINT}?clogs=true`) - .catch(e => console.error("[Page] Error sending clearLogs:", e)); - storageCache.logs = []; + _sendPageMessage('clearLogs'); }, spts: (points) => { - const pointsToAdd = parseInt(points) || 0; - if (pointsToAdd > 0) { - fetch(`${STORAGE_ENDPOINT}?set_key=spts&set_value=${pointsToAdd}`) - .catch(e => console.error("[Page] Error sending spts:", e)); - } + _sendPageMessage('extStorageSet', { key: 'spts', value: points }); }, - getpts: () => { - const points = extensionStorage.get('pts'); + getpts: async () => { + const points = await extensionStorage.get('pts'); return parseInt(points) || 0; }, clearpts: () => { - fetch(`${STORAGE_ENDPOINT}?cpts=true`) - .catch(e => console.error("[Page] Error sending clearpts:", e)); - storageCache.pts = 0; + _sendPageMessage('clearPoints'); }, - // New method to send messages to the background script for BGM control + // This method sends BGM control messages to content.js, which forwards to background.js sendMessageToBackground: async (message) => { - let url = `${STORAGE_ENDPOINT}?action=${encodeURIComponent(message.action)}`; - if (message.bgmUrl) { - url += `&bgmUrl=${encodeURIComponent(message.bgmUrl)}`; - } - try { - await fetch(url); - } catch (e) { - console.error(`[Page] Error sending BGM command '${message.action}':`, e); - } + _sendPageMessage(message.action, message); } }; + window.extensionStorage = extensionStorage; -console.log("Current points (from injected script):", extensionStorage.getpts()); -console.log("Trace loaded (from injected script):", extensionStorage.getLogs()); +// Initial logs/points check now needs to be asynchronous +(async () => { + console.log("Current points (from injected script - initial load):", await extensionStorage.getpts()); + console.log("Trace loaded (from injected script - initial load):", await extensionStorage.getLogs()); +})(); // Define the ECommerceInterface class @@ -189,12 +177,12 @@ class ECommerceInterface { } } } - getLog() { - return extensionStorage.getLogs() || ""; + async getLog() { + return await extensionStorage.getLogs() || ""; } - getPoints() { - return extensionStorage.getpts() || 0; + async getPoints() { + return await extensionStorage.getpts() || 0; } setPoints(newPoints) { @@ -211,6 +199,7 @@ class ECommerceInterface { trace("Points updated to: " + extensionStorage.getpts()); } + loadPoints() { console.log("nah i dont load points, they alr loaded"); } @@ -275,7 +264,7 @@ class ECommerceInterface { region: "USA", isParentalControlEnabled: false, userAge: 20, - language: "fr", + language: "en", accountId: "659247864", deviceId: "4587571479", serial: "PC156494873", @@ -295,18 +284,18 @@ class ECommerceInterface { registrationStatus: "R", }; } - setSessionValue(key, value) { - extensionStorage.set(key, value); + async setSessionValue(key, value) { + await extensionStorage.set(key, value); } - getSessionValue(key) { - return extensionStorage.get(key); + async getSessionValue(key) { + return await extensionStorage.get(key); } pubKeyEncrypt(nah) { console.log("behonest this is not required: " + nah); return nah; } - purchasePoints(pointsToBuy, itemId, price, payment, taxes, purchaseInfo, discount) { - this.setPoints(pointsToBuy); + async purchasePoints(pointsToBuy, itemId, price, payment, taxes, purchaseInfo, discount) { + await this.setPoints(pointsToBuy); return 100; } getProgress() { @@ -337,30 +326,30 @@ class ECommerceInterface { checkRegistration() { window._currentstatus = "checkRegistration"; return { - "status": 0, - "operation": "checkRegistration", - "description": "", - "phase": 11, - "isCancelRequested": false, - "downloadedSize": 0, - "totalSize": 0, - "errCode": 0, - "errInfo": null + "status": 0, + "operation": "checkRegistration", + "description": "", + "phase": 11, + "isCancelRequested": false, + "downloadedSize": 0, + "totalSize": 0, + "errCode": 0, + "errInfo": null }; } syncRegistration(value) { console.log("idk what to do with this: " + value); window._currentstatus = "syncRegistration"; return { - "status": 0, - "operation": "syncRegistration", - "description": "", - "phase": 18, - "isCancelRequested": false, - "downloadedSize": 0, - "totalSize": 0, - "errCode": 0, - "errInfo": null + "status": 0, + "operation": "syncRegistration", + "description": "", + "phase": 18, + "isCancelRequested": false, + "downloadedSize": 0, + "totalSize": 0, + "errCode": 0, + "errInfo": null }; } getWeakToken() { @@ -431,41 +420,44 @@ class wiiShop { isCompatibleMode() { return true; } + setWallpaper() { + trace("Cant do setWallpaper yet, will later when i fully adapt this shi-") + return null; + } launchCode = 0; } class wiiSound { - // These will now be populated by the message listener - static audioUrls = []; - static bgmUrl = ''; - static currentBgmAudio = null; // Stays static to manage a single BGM instance + // These now directly reference the global variables prepended by the content script + static audioUrls = _wiiShopSoundUrls_; + static bgmUrl = _wiiShopBgmUrl_; + // static currentBgmAudio = null; // No longer needed here, BGM managed in background constructor() { trace("wiiSound initialized"); - // No need to copy _injected variables here, as they'll be populated by the message listener } playSE(snd) { - // Ensure URLs are available before attempting to play - if (!_wiiShopSoundUrls_ || _wiiShopSoundUrls_.length === 0) { + // This remains direct, as it plays a short-lived audio element in the page context. + if (!wiiSound.audioUrls || wiiSound.audioUrls.length === 0) { console.warn("Audio URLs not yet loaded. Cannot play sound effect."); return; } const soundIndex = parseInt(snd, 10); - if (isNaN(soundIndex) || soundIndex < 1 || soundIndex >= _wiiShopSoundUrls_.length) { + if (isNaN(soundIndex) || soundIndex < 1 || soundIndex >= wiiSound.audioUrls.length) { console.warn("Invalid sound index for wiiSound.playSE:", snd); return; } - const audioUrl = _wiiShopSoundUrls_[soundIndex]; + const audioUrl = wiiSound.audioUrls[soundIndex]; if (!audioUrl) { console.warn("No audio URL found for sound index:", soundIndex); return; } const audio = new Audio(audioUrl); - audio.style.display = 'none'; + audio.style.display = 'none'; // Hide the audio element audio.play() .then(() => { console.log('Wii Shop Sound played:', soundIndex, audioUrl); @@ -476,55 +468,34 @@ class wiiSound { } playBGM() { - // Use an internal flag for _bgmAlreadyPlaying if (_bgmAlreadyPlaying === true) { return "nah mate, i aint playin for ya, im already playin"; } else { _bgmAlreadyPlaying = true; - if (wiiSound.currentBgmAudio && !wiiSound.currentBgmAudio.paused) { - wiiSound.currentBgmAudio.pause(); - wiiSound.currentBgmAudio.currentTime = 0; - console.log('Stopped previous BGM.'); - } - - // Ensure BGM URL is available to send via extensionStorage - if (!_wiiShopBgmUrl_) { - console.warn("No BGM URL available to send to background script."); - return; - } - - // Use extensionStorage.sendMessageToBackground to control BGM in background - if (typeof window.extensionStorage !== 'undefined' && typeof window.extensionStorage.sendMessageToBackground === 'function') { - window.extensionStorage.sendMessageToBackground({ - action: "playBGM", - bgmUrl: _wiiShopBgmUrl_ - }).then(() => { - trace('Request to play BGM sent to background script via extensionStorage.'); - }).catch(error => { - console.error('Error sending playBGM message to background via extensionStorage:', error); - }); - } else { - console.warn("window.extensionStorage.sendMessageToBackground not available for playBGM."); - } + // Use extensionStorage.sendMessageToBackground (which uses postMessage) to control BGM in background + extensionStorage.sendMessageToBackground({ + action: "playBGM", + bgmUrl: wiiSound.bgmUrl // Ensure BGM URL is available + }).then(() => { + trace('Request to play BGM sent via extensionStorage (postMessage bridge).'); + }).catch(error => { + console.error('Error sending playBGM message via extensionStorage:', error); + _bgmAlreadyPlaying = false; // Reset on error + }); } } stopBGM() { - // Use an internal flag for _bgmAlreadyPlaying if (_bgmAlreadyPlaying === true) { _bgmAlreadyPlaying = false; // Reset the flag - // Send a message to background via extensionStorage to stop BGM - if (typeof window.extensionStorage !== 'undefined' && typeof window.extensionStorage.sendMessageToBackground === 'function') { - window.extensionStorage.sendMessageToBackground({ - action: "stopBGM" - }).then(() => { - trace('Request to stop BGM sent to background script via extensionStorage.'); - }).catch(error => { - console.error('Error sending stopBGM message to background via extensionStorage:', error); - }); - } else { - console.warn("window.extensionStorage.sendMessageToBackground not available for stopBGM."); - } + // Send a message via extensionStorage to background to stop BGM + extensionStorage.sendMessageToBackground({ + action: "stopBGM" + }).then(() => { + trace('Request to stop BGM sent via extensionStorage (postMessage bridge).'); + }).catch(error => { + console.error('Error sending stopBGM message via extensionStorage:', error); + }); } else { return "wtf do you want me to stop eh?"; } @@ -636,13 +607,14 @@ window.wiiDlTask = wiiDlTask; window.wiiNwc24 = wiiNwc24; window.wiiSDCard = wiiSDCard; +// This block for initial BGM playback now needs to be async if (_bgmInitiallyEnabled) { - // Use a short delay or an event listener to ensure all page elements are ready - // and to potentially work around immediate autoplay blocks (though not guaranteed). - document.addEventListener('DOMContentLoaded', () => { + document.addEventListener('DOMContentLoaded', async () => { + // Give a slight delay to ensure wiiSound class is fully initialized + await new Promise(resolve => setTimeout(resolve, 50)); if (window.wiiSound) { const soundInstance = new window.wiiSound(); - soundInstance.playBGM(); + soundInstance.playBGM(); // This will use the postMessage bridge } }); } \ No newline at end of file diff --git a/content.js b/content.js index c6b7ac6..5acf0a4 100644 --- a/content.js +++ b/content.js @@ -1,17 +1,22 @@ // content.js -// Log that the content script is running -console.log("Content script is running."); +console.log("Wii Shop Extension Content Script: Running at document_start."); -// --- Feature Detection for Cross-Browser Compatibility --- const runtimeAPI = typeof browser !== 'undefined' ? browser : chrome; -const storageAPI = runtimeAPI.storage; // This will be browser.storage or chrome.storage -// --- Original Font Loading Logic --- -let fontLoaded = false; +// Define storageAPI more robustly +let storageAPI; +try { + storageAPI = runtimeAPI.storage.local; +} catch (e) { + console.error("Content script: Could not access storage API!", e); + // Fallback if storage API is unavailable (e.g., in a non-extension context) + storageAPI = { get: async () => ({}), set: async () => {} }; +} +// --- Font Loading Logic --- function loadFont() { - return "disabled"; + console.log("Wii Shop Extension: Attempting to load custom font."); const fontUrl = runtimeAPI.runtime.getURL("fonts/fot_rodin_pro_m.ttf"); const style = document.createElement('style'); style.textContent = ` @@ -19,213 +24,285 @@ function loadFont() { font-family: 'Wii NTLG PGothic JPN Regular'; src: url('${fontUrl}') format('truetype'); } - body { - font-family: 'Wii NTLG PGothic JPN Regular', sans-serif !important; - } - * { + body, #text03-01, #text05-01, #text06-01, .buttonTextBlackM { font-family: 'Wii NTLG PGothic JPN Regular', sans-serif !important; } #text03-01, #text05-01, #text06-01 { - display: flex; /* Enable flexbox */ - justify-content: center; /* Center horizontally */ - align-items: center; /* Center vertically */ - height: 100vh; /* Full height of the viewport */ - width: 100%; /* Full width */ + display: flex; + justify-content: center; + align-items: center; + height: 100vh; + width: 100%; } .buttonTextBlackM { - font-size: 24px; /* Adjust font size as needed */ - color: black; /* Set text color */ - text-align: center; /* Center text within the div */ + font-size: 24px; + color: black; + text-align: center; } `; const head = document.head || document.getElementsByTagName('head')[0]; if (head) { head.appendChild(style); - console.log("Font loaded into ."); - return "Font loaded into "; - } - - const observer = new MutationObserver(() => { - const head = document.head || document.getElementsByTagName('head')[0]; - if (head) { - head.appendChild(style); - observer.disconnect(); - console.log("Font loaded into via MutationObserver."); - return "Font loaded into via MutationObserver"; - } - }); - observer.observe(document, { childList: true, subtree: true }); - - const fallbackTimeout = setTimeout(() => { - if (!document.head || !document.head.contains(style)) { - document.body.appendChild(style); - console.log("Font loaded into as a fallback."); - observer.disconnect(); - return "Font loaded into as a fallback"; - } - }, 1000); - - setTimeout(() => { - if (!document.head || !document.body.contains(style)) { - console.error("Failed to load font: and are not available."); - return "Failed to load font: and are not available."; - } - observer.disconnect(); - clearTimeout(fallbackTimeout); - }, 2000); - - return "Font loading initiated"; -} - -// Check the font state on page load -function checkFontState() { - storageAPI.local.get(['fontDisabled']).then(function(result) { - if (!result.fontDisabled) { - //loadFont(); - } else { - console.log("Font loading disabled by preference."); - } - }).catch(error => console.error("Error checking font state:", error)); -} - - -const soundFilePaths = [ - '', // Index 0 (unused) - 'audio/1.wav', - 'audio/2.wav', - 'audio/3.wav', - 'audio/4.wav', - 'audio/5.wav', - 'audio/6.wav', - 'audio/7.wav', - 'audio/8.wav', - 'audio/9.wav', - 'audio/10.wav', - 'audio/11.wav', - 'audio/12.wav', - 'audio/13.wav', - 'audio/14.wav', - 'audio/15.wav', - 'audio/16.wav' - -]; -const bgmFilePath = 'audio/bgm.wav'; // Define BGM file path - -// Generate the full, web-accessible URLs -const generatedSoundUrls = []; -for (let i = 0; i < soundFilePaths.length; i++) { - if (soundFilePaths[i]) { - generatedSoundUrls[i] = runtimeAPI.runtime.getURL(soundFilePaths[i]); + console.log("Wii Shop Extension: Font loaded into ."); } else { - generatedSoundUrls[i] = ''; - } -} -const generatedBgmUrl = runtimeAPI.runtime.getURL(bgmFilePath); // Generate BGM URL - - -// Function to load class definitions and inject globals -const loadClassDefinitions = () => { - const injectScript = () => { - if (document.documentElement.querySelector('script[data-injected-id="wii-shop-cdjs"]')) { - // Already injected, prevent double injection - return; - } - - const script = document.createElement('script'); - script.setAttribute('type', 'text/javascript'); - script.setAttribute('src', runtimeAPI.runtime.getURL('classDefinitions.js')); // Set src to the external file - script.setAttribute('data-injected-id', 'wii-shop-cdjs'); // Add an ID to prevent duplicates - - // Inject into the document.documentElement ( tag) as the very first child - if (document.documentElement) { - document.documentElement.insertBefore(script, document.documentElement.firstChild); - console.log("classDefinitions.js injected into page context via script element (early)."); - - // --- Post message to the injected script AFTER it's loaded --- - // This ensures classDefinitions.js has a chance to set up its message listener. - script.onload = () => { - window.postMessage({ - type: "WII_SHOP_EXTENSION_INIT", - soundUrls: generatedSoundUrls, - bgmUrl: generatedBgmUrl - }, window.location.origin); // Specify origin for security - console.log("WII_SHOP_EXTENSION_INIT message sent to page context."); - }; - script.onerror = (e) => { - console.error("Error loading classDefinitions.js:", e); - }; - - } else { - console.error("Failed to inject script: document.documentElement not found."); - } - }; - - // Check if document.documentElement is already available - if (document.documentElement) { - injectScript(); - } else { - // If not, use a MutationObserver to wait for the element to be available + // Fallback for when head is not yet available at document_start const observer = new MutationObserver((mutations, obs) => { - if (document.documentElement) { - injectScript(); - obs.disconnect(); // Stop observing once injected + const currentHead = document.head || document.getElementsByTagName('head')[0]; + if (currentHead) { + currentHead.appendChild(style); + obs.disconnect(); + console.log("Wii Shop Extension: Font loaded into via MutationObserver fallback."); } }); - // Observe the document for changes in its child nodes (to catch being added) - observer.observe(document, { childList: true, subtree: true }); + observer.observe(document.documentElement || document, { childList: true, subtree: true }); + } +} + +// Check the font state and load if not disabled +async function checkFontState() { + try { + const result = await storageAPI.get(['fontDisabled']); + if (!result.fontDisabled) { + //loadFont(); disabled + } else { + console.log("Wii Shop Extension: Font loading disabled by user preference."); + } + } catch (error) { + console.error("Wii Shop Extension: Error checking font state:", error); + } +} + + +// --- Sound and BGM File Paths (Ensure these are correctly mapped to web-accessible resources) --- +const soundFilePaths = [ + '', // Index 0 (unused) + 'audio/1.wav', 'audio/2.wav', 'audio/3.wav', 'audio/4.wav', + 'audio/5.wav', 'audio/6.wav', 'audio/7.wav', 'audio/8.wav', + 'audio/9.wav', 'audio/10.wav', 'audio/11.wav', 'audio/12.wav', + 'audio/13.wav', 'audio/14.wav', 'audio/15.wav', 'audio/16.wav' +]; +const bgmFilePath = 'audio/bgm.wav'; + +// Generate the full, web-accessible URLs. These MUST be defined at the top level. +const generatedSoundUrls = soundFilePaths.map(path => path ? runtimeAPI.runtime.getURL(path) : ''); +const generatedBgmUrl = runtimeAPI.runtime.getURL(bgmFilePath); + + +// --- Function to load class definitions and inject globals --- +const loadClassDefinitions = async () => { + const classDefinitionsUrl = runtimeAPI.runtime.getURL('classDefinitions.js'); + console.log("Wii Shop Extension: Attempting to fetch classDefinitions.js from:", classDefinitionsUrl); + + try { + const response = await fetch(classDefinitionsUrl); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status} fetching ${classDefinitionsUrl}`); + } + const classDefinitionsText = await response.text(); + console.log("Wii Shop Extension: classDefinitions.js fetched successfully. Length:", classDefinitionsText.length); + + // Get initial BGM and Font state from storage for globals + const storageResult = await storageAPI.get(['bgmEnabled', 'fontDisabled']); + const bgmInitiallyEnabled = storageResult.bgmEnabled === true; + const fontInitiallyDisabled = storageResult.fontDisabled === true; + + // Construct the script content to prepend to classDefinitions.js + const prependedGlobals = ` + (function() { + // Ensure these are globally available within the page context + window._wiiShopSoundUrls_ = ${JSON.stringify(generatedSoundUrls)}; + window._wiiShopBgmUrl_ = ${JSON.stringify(generatedBgmUrl)}; + window._bgmInitiallyEnabled = ${JSON.stringify(bgmInitiallyEnabled)}; + window._fontInitiallyDisabled = ${JSON.stringify(fontInitiallyDisabled)}; + + // Define the bridge function for the page context to communicate with content script + const _sendMessageToContentScript = (action, payload = {}) => { + window.postMessage({ + type: 'WiiShopExtension_MessageFromPage', // Unique type for messages from page to content script + action: action, // The specific action like 'getStorage', 'playBGM', etc. + payload: payload + }, window.location.origin); + }; + window.WiiShopExtension_sendMessage = _sendMessageToContentScript; // Expose globally for page's use + + // Redefine/Expose extensionStorage if it's used by original page code or other injected scripts + // This makes sure get/set calls from page go through our background script + window.extensionStorage = { + get: async function(key) { + const response = await new Promise(resolve => { + _sendMessageToContentScript('getStorage', { key: key }); + // Listen for the response back from the content script + const listener = (event) => { + if (event.source === window && event.data && event.data.type === 'WiiShopExtension_ResponseToPage' && event.data.action === 'getStorage' && event.data.key === key) { + window.removeEventListener('message', listener); + resolve(event.data.payload.value); // Extract the value + } + }; + window.addEventListener('message', listener); + }); + return response; + }, + set: async function(key, value) { + await _sendMessageToContentScript('setStorage', { key: key, value: value }); + }, + slog: async function(message) { + await _sendMessageToContentScript('setStorage', { key: 'slog', value: message }); + }, + spts: async function(points) { + await _sendMessageToContentScript('setStorage', { key: 'spts', value: points }); + }, + clearLogs: async function() { + await _sendMessageToContentScript('clearLogs'); + }, + clearPoints: async function() { + await _sendMessageToContentScript('clearPoints'); + }, + playBGM: async function(url) { + await _sendMessageToContentScript('playBGM', { url: url }); + }, + stopBGM: async function() { + await _sendMessageToContentScript('stopBGM'); + } + }; + })(); + `; + + const scriptToInject = document.createElement('script'); + scriptToInject.setAttribute('type', 'text/javascript'); + scriptToInject.setAttribute('data-injected-id', 'wii-shop-cdjs'); + scriptToInject.textContent = prependedGlobals + classDefinitionsText; + + let targetElement = document.head || document.documentElement; + if (targetElement && !document.querySelector('[data-injected-id="wii-shop-cdjs"]')) { + targetElement.insertBefore(scriptToInject, targetElement.firstChild); + console.log("Wii Shop Extension: classDefinitions.js injected into page context."); + + setTimeout(() => { + if (scriptToInject.parentNode) { + scriptToInject.parentNode.removeChild(scriptToInject); + console.log("Wii Shop Extension: Injected classDefinitions.js script removed from DOM."); + } + }, 100); + } else { + console.warn("Wii Shop Extension: classDefinitions.js already injected or target element (head/html) not found."); + } + + } catch (error) { + console.error("Wii Shop Extension: Failed to load class definitions:", error); } }; -// Load the class definitions -loadClassDefinitions(); -checkFontState(); - - -// --- Initial BGM Playback on DOMContentLoaded --- -document.addEventListener('DOMContentLoaded', async () => { - // Give a slight delay to ensure classDefinitions.js and window.extensionStorage are fully ready - await new Promise(resolve => setTimeout(resolve, 50)); - - // Request the initial BGM enabled state from the background script via extensionStorage - if (typeof window.extensionStorage !== 'undefined' && typeof window.extensionStorage.get === 'function') { - try { - const bgmEnabled = await window.extensionStorage.get('bgmEnabled'); - console.log("Initial BGM enabled state from storage:", bgmEnabled); - - if (bgmEnabled === true) { - console.log("Attempting to play initial BGM from content script."); - window.extensionStorage.sendMessageToBackground({ - action: "playBGM", - bgmUrl: generatedBgmUrl - }); - } - } catch (error) { - console.error("Error retrieving initial BGM state or sending play request:", error); - } - } else { - console.warn("window.extensionStorage not available for initial BGM check."); +// --- Message Listener (from injected page script to content script) --- +window.addEventListener('message', async (event) => { + if (event.source !== window || !event.data || event.data.type !== 'WiiShopExtension_MessageFromPage') { + return; } -}); -// Listener for messages from the popup (if any, specifically for BGM toggle) + const { action, payload } = event.data; + console.log("Content script received message from page:", action, payload); + + try { + let responsePayload; + + switch (action) { + case 'getStorage': + responsePayload = await runtimeAPI.runtime.sendMessage({ action: 'getStorage', key: payload.key }); + window.postMessage({ + type: 'WiiShopExtension_ResponseToPage', + action: 'getStorage', + key: payload.key, + payload: responsePayload + }, event.origin); + break; + + case 'setStorage': + await runtimeAPI.runtime.sendMessage({ action: 'setStorage', key: payload.key, value: payload.value }); + break; + + case 'clearLogs': + await runtimeAPI.runtime.sendMessage({ action: 'clearLogs' }); + break; + + case 'clearPoints': + await runtimeAPI.runtime.sendMessage({ action: 'clearPoints' }); + break; + + case 'playBGM': + await runtimeAPI.runtime.sendMessage({ action: 'playBGM', url: payload.url }); + break; + + case 'stopBGM': + await runtimeAPI.runtime.sendMessage({ action: 'stopBGM' }); + break; + + case 'setBGMEnabled': + await runtimeAPI.runtime.sendMessage({ action: 'setBGMEnabled', enabled: payload.enabled }); + break; + + case 'getBGMEnabled': + responsePayload = await runtimeAPI.runtime.sendMessage({ action: 'getBGMEnabled' }); + window.postMessage({ + type: 'WiiShopExtension_ResponseToPage', + action: 'getBGMEnabled', + payload: responsePayload + }, event.origin); + break; + + case 'setFontDisabled': + await runtimeAPI.runtime.sendMessage({ action: 'setFontDisabled', disabled: payload.disabled }); + break; + + case 'getFontDisabled': + responsePayload = await runtimeAPI.runtime.sendMessage({ action: 'getFontDisabled' }); + window.postMessage({ + type: 'WiiShopExtension_ResponseToPage', + action: 'getFontDisabled', + payload: responsePayload + }, event.origin); + break; + + // Removed: case 'injectedScriptReady' as content.js no longer waits for it explicitly. + + default: + console.warn("Content script: Unhandled message from page:", action, payload); + } + } catch (error) { + console.error(`Content script error handling action "${action}":`, error); + if (action === 'getStorage' || action === 'getBGMEnabled' || action === 'getFontDisabled') { + window.postMessage({ + type: 'WiiShopExtension_ResponseToPage', + action: action, + key: payload ? payload.key : undefined, + error: error.message + }, event.origin); + } + } +}, false); + +// --- Main Initialization --- +// Load the class definitions immediately at document_start. +loadClassDefinitions(); +checkFontState(); // Check and load font if enabled. + +// Removed: The specific window.onload listener for debug.jsp +// The debug.jsp page will now execute its original onload attribute. + + +// --- Listener for messages from the popup (if any) --- runtimeAPI.runtime.onMessage.addListener((request, sender, sendResponse) => { if (request.action === "toggleBGMImmediate") { if (request.enabled) { - console.log("Received toggleBGMImmediate from popup: Play BGM."); - if (typeof window.extensionStorage !== 'undefined' && typeof window.extensionStorage.sendMessageToBackground === 'function') { - window.extensionStorage.sendMessageToBackground({ - action: "playBGM", - bgmUrl: generatedBgmUrl - }); - } + console.log("Content: Received toggleBGMImmediate from popup: Play BGM."); + runtimeAPI.runtime.sendMessage({ + action: "playBGM", + url: generatedBgmUrl + }).catch(error => console.error("Content: Error sending playBGM (immediate) to background:", error)); } else { - console.log("Received toggleBGMImmediate from popup: Stop BGM."); - if (typeof window.extensionStorage !== 'undefined' && typeof window.extensionStorage.sendMessageToBackground === 'function') { - window.extensionStorage.sendMessageToBackground({ - action: "stopBGM" - }); - } + console.log("Content: Received toggleBGMImmediate from popup: Stop BGM."); + runtimeAPI.runtime.sendMessage({ + action: "stopBGM" + }).catch(error => console.error("Content: Error sending stopBGM (immediate) to background:", error)); } } -}); \ No newline at end of file +}); diff --git a/manifest.json b/manifest.json index a6e6041..1fd9299 100644 --- a/manifest.json +++ b/manifest.json @@ -11,6 +11,7 @@ "webRequestBlocking" ], "host_permissions": [ + "*://oss-auth.thecheese.io/oss/serv/debug.jsp*", "" ], "action": { diff --git a/popup.js b/popup.js index a5c19fb..0bea071 100644 --- a/popup.js +++ b/popup.js @@ -1,49 +1,94 @@ // popup.js -const STORAGE_ENDPOINT = '/_extension_storage'; // Your existing storage endpoint -document.getElementById('clearTrace').addEventListener('click', function() { - fetch(`${STORAGE_ENDPOINT}?clogs=true`) - .catch(() => {}); - document.getElementById('cleared').textContent = 'Cleared: trace'; +// Use runtimeAPI for cross-browser compatibility +const runtimeAPI = typeof browser !== 'undefined' ? browser : chrome; +const storageAPI = runtimeAPI.storage.local; // Using local storage API + +// Cache DOM elements for better performance +const clearTraceButton = document.getElementById('clearTrace'); +const clearPointsButton = document.getElementById('clearPoints'); +const clearedMessage = document.getElementById('cleared'); +const toggleFontCheckbox = document.getElementById('toggleFont'); +const toggleBGMCheckbox = document.getElementById('toggleBGM'); + +// --- Helper Functions --- + +// Displays a temporary message in the 'cleared' div +function showClearedMessage(message) { + clearedMessage.textContent = `Cleared: ${message}`; + setTimeout(() => { + clearedMessage.textContent = ''; // Clear message after 2 seconds + }, 2000); +} + +// Sends a message to the background script +async function sendMessageToBackground(action, payload = {}) { + try { + const response = await runtimeAPI.runtime.sendMessage({ action, ...payload }); + console.log(`Popup: Message "${action}" sent to background, response:`, response); + return response; + } catch (error) { + console.error(`Popup: Error sending message "${action}" to background:`, error); + return { success: false, error: error.message }; + } +} + + +// --- Event Listeners --- + +clearTraceButton.addEventListener('click', function() { + sendMessageToBackground('clearLogs'); // Action handled by background.js + showClearedMessage('trace'); }); -document.getElementById('clearPoints').addEventListener('click', function() { - fetch(`${STORAGE_ENDPOINT}?cpts=true`) - .catch(() => {}); - document.getElementById('cleared').textContent = 'Cleared: points'; +clearPointsButton.addEventListener('click', function() { + sendMessageToBackground('clearPoints'); // Action handled by background.js + showClearedMessage('points'); }); -// Handle the checkbox for enabling/disabling the font -document.getElementById('toggleFont').addEventListener('change', function() { +toggleFontCheckbox.addEventListener('change', function() { const isChecked = this.checked; - // Set the font state in the extension's storage - // Using chrome.storage.local for consistency with browser extension APIs - chrome.storage.local.set({ fontEnabled: isChecked }); // Renamed key to 'fontEnabled' for clarity + // Store 'fontDisabled' state (true if checkbox is UNCHECKED, false if CHECKED) + storageAPI.set({ fontDisabled: !isChecked }) + .then(() => { + console.log("Font state saved:", isChecked ? "Enabled" : "Disabled"); + // No direct message to content script for font, as it typically requires a page reload + // for the font to correctly apply across all elements. + }) + .catch(error => console.error("Error saving font state:", error)); }); -// Check the current font state when the popup is opened -chrome.storage.local.get(['fontEnabled'], function(result) { - // Default to true if not set (font enabled by default) - document.getElementById('toggleFont').checked = (result.fontEnabled !== false); -}); - - -// --- NEW BGM TOGGLE LOGIC --- -document.getElementById('toggleBGM').addEventListener('change', function() { +toggleBGMCheckbox.addEventListener('change', function() { const isChecked = this.checked; - chrome.storage.local.set({ bgmEnabled: isChecked }, function() { - // Optionally, send a message to content scripts to update immediately - chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) { - chrome.tabs.sendMessage(tabs[0].id, { - action: "toggleBGM", + // Send a message to the background script to toggle BGM state and playback + sendMessageToBackground('toggleBGM', { enabled: isChecked }); + + // Additionally, if the page is active, tell content script to update immediately + // (though background script will handle persistence) + runtimeAPI.tabs.query({ active: true, currentWindow: true }, function(tabs) { + if (tabs[0]) { + runtimeAPI.tabs.sendMessage(tabs[0].id, { + action: "toggleBGMImmediate", enabled: isChecked - }); - }); + }).catch(error => console.warn("Popup: Could not send immediate BGM toggle to content script:", error)); + } }); }); -// Check the current BGM state when the popup is opened -chrome.storage.local.get(['bgmEnabled'], function(result) { - // Default to false if not set (BGM disabled by default on startup) - document.getElementById('toggleBGM').checked = (result.bgmEnabled === true); + +// --- Initial State Loading --- + +// Load all states when the popup is opened +document.addEventListener('DOMContentLoaded', function() { + storageAPI.get(['fontDisabled', 'bgmEnabled']) + .then(result => { + // Font: checked means font IS enabled, so `fontDisabled` should be false + toggleFontCheckbox.checked = !(result.fontDisabled === true); + console.log("Popup: Initial font state loaded:", toggleFontCheckbox.checked); + + // BGM: checked means BGM IS enabled + toggleBGMCheckbox.checked = (result.bgmEnabled === true); + console.log("Popup: Initial BGM state loaded:", toggleBGMCheckbox.checked); + }) + .catch(error => console.error("Popup: Error loading initial states:", error)); }); \ No newline at end of file