sf2000/tools/bootLogoChanger.htm
vonmillhausen a6feed8b84 Updated tools to support firmware 1.71
Data Frog released firmware 1.71, a quick bug-fixed version of firmware 1.7, so added support for it to the tools I host
2023-10-19 14:31:40 +01:00

244 lines
12 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Data Frog SF2000 Boot Logo Changer</title>
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" href="tools.css">
</head>
<body>
<h1>Data Frog SF2000 Boot Logo Changer</h1>
<p>This tool can be used to alter the boot logo on an SF2000 handheld gaming console. Please note this tool is provided as-is, and no support will be given if this corrupts your device's bios; make sure you have backups of anything you care about before messing with your device's critical files! 🙂</p>
<hr>
<div id="steps">
<section id="bisrvSection">
<h2>Step 1: Select Original <code>bisrv.asd</code></h2>
<p>Select the <code>bisrv.asd</code> file whose boot logo you want to modify. You should probably make a backup of the file first, just in case! You can find the <code>bisrv.asd</code> file in the <code>bios</code> folder on your microSD card.</p>
<div id="bisrvMessages"></div>
<div class="controlContainer">
<label class="control">Open <code>bisrv.asd</code>: <input id="bisrvSelector" type="file" accept=".asd" onchange="bisrvLoad(event.target.files[0])"></label>
</div>
</section>
</div>
<script src="tools.js"></script>
<script type="text/javascript">
// Global variables...
var bisrvData; // Used to store the binary data from the bisrv.asd file
var logoOffset; // Will contain the offset of the boot logo within the bisrv.asd file
var newLogoData; // Used to store the little-endian RGB565 binary data of the new boot logo
// This function is triggered when the person selects a file in Step 1...
function bisrvLoad(file) {
// Read in the contents of the selected file as array buffer...
var frBisrv = new FileReader();
frBisrv.readAsArrayBuffer(file);
frBisrv.onload = function(event) {
// Clear out any HTML that might already exist after Step 1...
while(document.getElementById("bisrvSection").nextSibling) {
document.getElementById("bisrvSection").nextSibling.remove();
}
// Clear any old messages...
document.getElementById("bisrvMessages").innerHTML = "";
// Read the provided file's data into an array...
bisrvData = new Uint8Array(event.target.result);
// We'll do a hash-check against it...
hashResult = getFirmwareHash(bisrvData);
// The result could be either a Promise if it had a bisrv.asd-like structure and we got
// a hash, or false otherwise... let's check!
if (hashResult instanceof Promise) {
// We got a Promise! Wait for it to finish so we get our bisrv.asd hash...
hashResult.then(function(dataHash) {
// Check the hash against all the known good ones...
switch (knownHash(dataHash)) {
// Mid-March BIOS...
case "03.15":
logoOffset = 0x9B9030;
setMessage("info", "bisrvMessages", "INFO: Mid-March <code>bisrv.asd</code> detected.");
break;
// April 20th BIOS...
case "04.20":
logoOffset = 0x9B91D8;
setMessage("info", "bisrvMessages", "INFO: April 20th <code>bisrv.asd</code> detected.");
break;
// May 15th BIOS...
case "05.15":
logoOffset = 0x9BB0B8;
setMessage("info", "bisrvMessages", "INFO: May 15th <code>bisrv.asd</code> detected.");
break;
// May 22nd BIOS...
case "05.22":
logoOffset = 0x9BB098;
setMessage("info", "bisrvMessages", "INFO: Version 1.5 <code>bisrv.asd</code> detected.");
break;
// August 3rd BIOS...
case "08.03":
logoOffset = 0x9B3530;
setMessage("info", "bisrvMessages", "INFO: Version 1.6 <code>bisrv.asd</code> detected.");
break;
// October 7th BIOS...
case "10.07":
logoOffset = 0x9B1FE8;
setMessage("warning", "bisrvMessages", "WARNING: Version 1.7 <code>bisrv.asd</code> detected; this version has known issues with SNES save states and is not recommended for use.");
break;
// October 13th BIOS...
case "10.13":
logoOffset = 0x9B3618;
setMessage("info", "bisrvMessages", "INFO: Version 1.71 <code>bisrv.asd</code> detected.");
break;
default:
// Huh... wasn't false so had bisrv.asd structure, but didn't return
// a known hash... a new BIOS version? Unknown anyway!
console.log(dataHash);
setMessage("error", "bisrvMessages", "ERROR: While the file you've selected does appear to be generally structured like the SF2000's <code>bisrv.asd</code> BIOS file, the specifics of your file don't match any known SF2000 BIOS version. As such, this tool cannot safely modify the selected file.");
return;
}
// If we're here we've got a good file, so onwards to Step 2 (image selection)...
stepTwo();
});
}
else {
// We got false, so whatever it was, it wasn't a bisrv.asd...
setMessage("error", "bisrvMessages", "ERROR: The file you've selected doesn't appear to have the same data structure as expected for a <code>bisrv.asd</code> file.");
return;
}
};
}
// This function triggers when the user has selected a known `bisrv.asd`
// variant...
function stepTwo() {
// We're going to build up the HTML for Step 2, which has the user
// browse for a PNG or JPEG image file. We need to validate the chosen
// file is an image, and that it has the correct dimensions of 256x100
// pixels; if it is, we convert the image data to an SF2000 format and
// move on to Step 3. First, let's start building our HTML...
var html = "<section id=\"imageSection\"><h2>Step 2: Select New Logo Image File</h2><p>Select the image file you want to use as the new boot logo. It must be 256 pixels wide and 100 pixels tall. Only PNG and JPEG image types are supported. For PNG files, transparency is ignored; also, you might want to make sure your PNG image has any embedded gamma or colour profile information removed first using a metadata scrubbing tool, otherwise the colour output from <i>this</i> tool might be incorrect. The image is displayed centred on a black background, so you may want to factor that into your design as well.</p><div id=\"imageMessages\"></div>";
// Add our file chooser...
html += "<div class=\"controlContainer\"><label class=\"control\"><input id=\"inputImage\" type=\"file\"></label></div>";
// Close our section...
html += "</section>";
// Finally, add a <hr> separator after the last step, and append the new step...
document.getElementById("steps").insertAdjacentHTML("beforeend", "<hr>");
document.getElementById("steps").insertAdjacentHTML("beforeend", html);
// Attach our event handler to our new file input control...
var userImageInput = document.getElementById("inputImage");
userImageInput.addEventListener("change", function() {
// The user has chosen a new file; it should be either a PNG or JPEG
// image for converting to an SF2000 binary image format...
// First let's clear any old messages...
document.getElementById("imageMessages").innerHTML = "";
// And clear up any HTML already added after the current step...
while(document.getElementById("imageSection").nextSibling) {
document.getElementById("imageSection").nextSibling.remove();
}
// Now let's read in the file...
var frImage = new FileReader();
frImage.readAsDataURL(event.target.files[0]);
frImage.onload = function(event) {
// The file is loaded; let's check to make sure we got a PNG or JPEG...
if (!event.target.result.includes("data:image/png;") && !event.target.result.includes("data:image/jpeg;")) {
// Whoops! Doesn't look like the user provided a PNG or a JPEG!
setMessage("error", "imageMessages", "ERROR: The selected file does not appear to be either a PNG or JPEG image; please make sure you're selecting an appropriate image file.");
return;
}
// Now we're going to load the file's data into an Image object, so we can
// access the raw image data...
var img = new Image;
img.src = event.target.result;
img.onload = function(event) {
// Check to make sure the image has the correct dimensions for a
// boot logo...
if (img.width != 256 || img.height != 100) {
setMessage("error", "imageMessages", "ERROR: The selected image does not have dimensions of 256x100px!");
return;
}
// We need to scale the image to the internal resolution used by
// the SF2000 (512x200)...
var imageData = scaleImage(imageToImageData(img), 512, 200, "Nearest Neighbour");
// Next we'll convert the image data to little-endian RGB565
// format for the SF2000...
newLogoData = imageDataToRgb565(imageData);
// On to Step 3!
stepThree();
}
}
});
}
function stepThree() {
var html = "<section id=\"downloadSection\"><h2>Step 3: Download Updated <code>bisrv.asd</code></h2><p>Click the download button for the updated <code>bisrv.asd</code> file; use it to replace the one in the <code>bios</code> folder on your SF2000's microSD card.</p><div id=\"downloadMessages\"></div>";
// Add our download button...
html += "<div class=\"controlContainer\"><div class=\"control\"><input id=\"userDownload\" type=\"button\" value=\"Download\"></div></div>";
// Close our section...
html += "</section>";
// Finally, add a <hr> separator after the last step, and append the new step...
document.getElementById("steps").insertAdjacentHTML("beforeend", "<hr>");
document.getElementById("steps").insertAdjacentHTML("beforeend", html);
// Let's add the event handler for our Download button...
var dButton = document.getElementById("userDownload");
dButton.addEventListener("click", function() {
// So, we should have the original bisrv.asd data in bisrvData; and we should have
// the offset to the original logo data in logoOffset; and finally, we should have
// our new logo's binary data in newLogoData. All we need to do is replace the old
// data bytes with the new bytes, re-calculate the CRC32 bytes for the modified
// file and set them in the data, and send the data to the user's browser. Easy!
// First, replace the logo data...
for (var i = 0; i < newLogoData.length; i++) {
bisrvData[logoOffset + i] = newLogoData[i];
}
// Next, we calculate a new CRC32 for the updated bisrv.asd and apply it...
patchCRC32(bisrvData);
// And finally, send the data to the user's browser as a file download...
downloadToBrowser(bisrvData, "application/octet-stream", "bisrv.asd");
});
}
</script>
<hr>
<p><a rel="license" href="https://creativecommons.org/publicdomain/zero/1.0/">CC0</a>: public domain. Version 1.5, 20231019.1</p>
</body>
</html>