smol updates

This commit is contained in:
thom2305 2025-07-06 17:36:11 -04:00
parent 555031d406
commit 7b7a9fac8a
No known key found for this signature in database
GPG Key ID: 0171038FEE1BEF12
5 changed files with 682 additions and 472 deletions

View File

@ -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: ["<all_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"]
);
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;
}
});

View File

@ -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
}
});
}

View File

@ -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 <head>.");
return "Font loaded into <head>";
}
const observer = new MutationObserver(() => {
const head = document.head || document.getElementsByTagName('head')[0];
if (head) {
head.appendChild(style);
observer.disconnect();
console.log("Font loaded into <head> via MutationObserver.");
return "Font loaded into <head> 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 <body> as a fallback.");
observer.disconnect();
return "Font loaded into <body> as a fallback";
}
}, 1000);
setTimeout(() => {
if (!document.head || !document.body.contains(style)) {
console.error("Failed to load font: <head> and <body> are not available.");
return "Failed to load font: <head> and <body> 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 <head>.");
} 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 (<html> 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 <html> 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 <head> via MutationObserver fallback.");
}
});
// Observe the document for changes in its child nodes (to catch <html> 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));
}
}
});
});

View File

@ -11,6 +11,7 @@
"webRequestBlocking"
],
"host_permissions": [
"*://oss-auth.thecheese.io/oss/serv/debug.jsp*",
"<all_urls>"
],
"action": {

113
popup.js
View File

@ -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));
});