3ds/wiiu mii data decoding and encoding using struct-fu sample
by arian_
HTML
<!--
<img id="image" data-src="https://mii-unsecure.ariankordi.net/miis/image.png?verifyCRC16=0&type=all_body_sugar&shaderType=switch&data=">
-->
<img id="image" data-src="https://mii-unsecure.ariankordi.net/miis/image.png?verifyCRC16=0&type=all_body_sugar&shaderType=switch&data=">
<div id="global-error-box" style="display:none;background:#ffeeee;color:#000;border:2px solid red;padding:1em;margin:1em 0;font-family:monospace">
<div style="font-weight:bold;font-size:1.2em"><span id="global-error-text-error">⚠️ JS Error:</span><span id="global-error-text-promise-rejection">⚠️ JS Promise Rejection:</span></div>
<div id="global-error-message" style="white-space:pre-wrap;margin-top:0.5em"></div>
<button id="global-error-hide-button" style="margin-top:1em">Close</button>
</div>
<form id="mii-form">
<textarea id="mii-input" rows="3" style="width:100%;font-family:monospace"
placeholder="Base64 or Hex FFLiMiiDataCore / StoreData (74 or 96 bytes)"></textarea>
<button type="submit">Parse</button>
</form>
<img id="image" src="" style="display:none">
<button id="json-to-ffsd-btn" type="button">Convert JSON to FFSD and render</button>
<textarea id="output" rows="30" style="width:100%;font-family:monospace"></textarea>
<div style="margin-top:1em">
<label for="base-url-input">Image base URL:</label>
<input id="base-url-input" type="text" style="width:60%;font-family:monospace" value="https://mii-unsecure.ariankordi.net/miis/image.png?verifyCRC16=0&type=all_body_sugar&shaderType=switch&data=">
</div>
<!-- Global error handling snippet. -->
<script>
(function () {
var box = document.getElementById('global-error-box');
var message = document.getElementById('global-error-message');
var closeBtn = document.getElementById('global-error-hide-button');
var textError = document.getElementById('global-error-text-error');
var textPromiseRejection = document.getElementById('global-error-text-promise-rejection');
...
JavaScript
//import _ from 'https://debian.local:8445/assets/struct-fu/lib-browser-working.js';
//import { Buffer } from 'https://cdn.jsdelivr.net/npm/[email protected]/+esm';
//import _ from 'https://cdn.jsdelivr.net/npm/[email protected]/+esm';
function assertField(fieldName, actual, expected) {
if (actual === expected) {
console.log('%c' + fieldName + ': expected ' + expected + ', got ' + actual + ' ✅', 'color: green; font-weight: bold;');
} else {
console.error('%c' + fieldName + ': expected ' + expected + ', got ' + actual + ' ❌', 'color: red; font-weight: bold;');
}
}
var stripSpaces = function(str) {
return str.replace(/\s+/g, '');
};
var hexToUint8Array = function(hex) {
return new Uint8Array(hex.match(/.{1,2}/g).map(function(byte) {
return parseInt(byte, 16);
}));
};
var base64ToUint8Array = function(base64) {
// Replace URL-safe Base64 characters
var normalizedBase64 = base64.replace(/-/g, '+').replace(/_/g, '/');
// Custom function to pad the string with '=' manually
var padBase64 = function(str) {
while (str.length % 4 !== 0) {
str += '=';
}
return str;
};
// Add padding if necessary
var paddedBase64 = padBase64(normalizedBase64);
var binaryString = atob(paddedBase64);
var len = binaryString.length;
var bytes = new Uint8Array(len);
for (var i = 0; i < len; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
return bytes;
};
var uint8ArrayToBase64 = function(data) {
return btoa(String.fromCharCode.apply(null, data));
};
var byteToHex = function(num) {
return ('0' + num.toString(16)).slice(-2);
};
var parseHexOrB64TextStringToUint8Array = function(text) {
var inputData;
// decode it to a uint8array whether it's hex or base64
var textData = stripSpaces(text);
// check if it's base 16 exclusively, otherwise assume base64
if (/^[0-9a-fA-F]+$/.test(textData))
inputData = hexToUint8Array(textData);
else
...