Skip to content

Instantly share code, notes, and snippets.

@kjhf
Created August 31, 2023 11:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kjhf/b1278e434d3c83c4ca57869dc37a15f6 to your computer and use it in GitHub Desktop.
Save kjhf/b1278e434d3c83c4ca57869dc37a15f6 to your computer and use it in GitHub Desktop.
Splashtag Titles as wikimarkup for Inkipedia for Splatoon 3
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Splashtag Titles</title>
<style>
:root {
color : #000000;
font-family: Arial, sans-serif;
font-size : 16px;
}
body {
background: #ffffff;
}
</style>
<script>
// Load all files from an <input type="file"> element
async function loadFiles(id) {
let files = document.getElementById(id).files;
let loaded = [];
let errors = [];
// No files selected
if (files.length == 0)
return { loaded: [], errors: [ "No file selected for " + id ] };
// Load all files
for (let file of files) {
try {
let data = JSON.parse(await file.text());
data.locale = file.name.replace(".json", "");
loaded.push(data);
}
catch(err) { errors.push(`Could not load file ${file.name} because: ${err}`); }
}
return { loaded: loaded, errors: errors };
}
// Parse the gendered components of an adjective
function parseAdjective(text) {
let ret = [ "", "", "", "" ];
// Null-transform text
let alone = text.replace(/\[.*?\]/g, "");
// Process all meta commands in the text
while (text.indexOf("[") != -1) {
let x = text.indexOf("[") + 1;
let y = text.indexOf("]", x);
let params = text.substring(x, y);
// Append everything before the opening square bracket
let part = text.substring(0, x - 1);
for (let z = 0; z < 4; z++)
ret[z] += part;
// Retain only the part after the closing square bracket
text = text.substring(y + 1);
// The meta command is not a gendered set
if (
!params.startsWith("group=00c9 type=0005") ||
params.endsWith("params=")
) continue;
// Convert the parameter text into 16-bit numbers
let shorts = new Uint16Array(new Uint8Array(params
.substring(params.indexOf("params=") + 7)
.split(" ").map(b=>parseInt(b, 16))
).buffer);
// Process gendered components
params = [];
for (x = 0; x < shorts.length;) {
let length = shorts[x++] / 2;
params.push(String.fromCharCode(
... shorts.slice(x, x + length)));
x += length;
}
if (params.length != 4)
throw "Parameter length mismatch";
// Append gendered components
for (x = 0; x < 4; x++)
ret[x] += params[x];
}
// Append any remaining text
for (let x = 0; x < 4; x++)
ret[x] += text;
// Remove unused variants
for (let x = 0; x < 4; x++) {
if (
x != 0 && ret[x] == alone ||
x == 0 && ret[3] != alone && ret[x] == alone
) ret[x] = "";
}
// Neutral variant if only gendered is available
if (!ret[2] && ret[3])
ret[2] = alone;
return ret;
}
// Process locales file for title strings
function parseLocales(locales) {
let ret = {
adjectives: {},
subjects : {}
};
// Process all locales
for (let locale of locales) {
// Process adjectives
for (let adj of Object.entries(locale["CommonMsg/Byname/BynameAdjective"])) {
let id = parseInt(adj[0]);
let item = ret.adjectives[id];
if (!item)
item = ret.adjectives[id] = {};
item[locale.locale] = parseAdjective(adj[1]);
}
// Process subjects
for (let sub of Object.entries(locale["CommonMsg/Byname/BynameSubject"])) {
let id = sub[0].split("_");
id[0] = parseInt(id[0]);
let item = ret.subjects[id[0]];
if (!item)
item = ret.subjects[id[0]] = {};
if (!item[locale.locale])
item[locale.locale] = new Array(2);
item[locale.locale][id[1]] = sub[1].replace(/\[.*?\]/g, "");
// Meta command patterns. Left here for reference if needed.
//if (sub[1].indexOf("[") == -1) continue;
//let x = sub[1].indexOf("[") + 1;
//let params = sub[1].substring(x, sub[1].indexOf("]", x));
//if (
// (
// params.indexOf("group=00c9") == -1 ||
// params.indexOf("type=0005" ) == -1
// ) &&
// !(
// params.startsWith("ruby=") ||
// params == "/ruby"
// ) &&
// !params.endsWith("params=") &&
// !params.endsWith("params=01 ff ff 00") &&
// !params.endsWith("params=02 ff ff 00") &&
// !params.endsWith("params=03 ff ff 00") &&
// !params.endsWith("params=04 ff ff 00")
//) console.log(locale.locale, sub[0], sub[1]);
}
// Eliminate duplicate subjects
for (let sub of Object.values(ret.subjects)) {
for (let lang of Object.values(sub)) {
if (lang[1] == lang[0]) {
let dupe = DUPE_SUBJECTS[locale.locale] || DUPE_SUBJECTS.USen;
lang[0] += dupe[0];
lang[1] += dupe[1];
}
}
}
}
return ret;
}
// Duplicate subject suffixes
let DUPE_SUBJECTS = {
"EUde": [ " (m)", " (w)" ],
"USen": [ " (m)", " (f)" ]
};
// Sorting comparator
function sortOrder(a, b) {
return a.USen[0].toLowerCase().localeCompare(b.USen[0].toLowerCase());
}
// Produce output to the user
function output(id, list, useEnglishOnly, oldFormatObtain) {
let out = [];
if (useEnglishOnly) {
/*
!Title
!How to obtain
!Added (season)
*/
for (let item of list) {
if (oldFormatObtain) {
out.push("|-\n" +
"|" + item.USen.filter(x=>x).map(x=>"'''"+x+"'''").join(" /<br>") + "\n" +
"|" + item.HowToGet + ",<br>Season " + item.Season
);
} else {
out.push("|-\n" +
"|" + item.USen.filter(x=>x).map(x=>"'''"+x+"'''").join(" /<br>") + "\n" +
"|" + item.HowToGet + "\n" +
"|" + item.Season
);
}
}
}
else {
/*
!English
!Japanese
!Dutch
!French<br>(Canada)
!French<br>(France)
!German
!Italian*
!Russian
!Spanish<br>(Latin America)
!Spanish<br>(Spain)
!Chinese<br>(Simplified)
!Chinese<br>(Traditional)
!Korean
*/
for (let item of list) {
out.push("|-\n" +
"|" + item.USen.filter(x=>x).map(x=>"'''"+x+"'''").join(" /<br>") + "\n" +
"|" + item.JPja.filter(x=>x).map(x=>"'''"+x+"'''").join(" /<br>") + "\n" +
"|" + item.EUnl.filter(x=>x).map(x=>"'''"+x+"'''").join(" /<br>") + "\n" +
"|" + item.USfr.filter(x=>x).map(x=>"'''"+x+"'''").join(" /<br>") + "\n" +
"|" + item.EUfr.filter(x=>x).map(x=>"'''"+x+"'''").join(" /<br>") + "\n" +
"|" + item.EUde.filter(x=>x).map(x=>"'''"+x+"'''").join(" /<br>") + "\n" +
"|" + item.EUit.filter(x=>x).map(x=>"'''"+x+"'''").join(" /<br>") + "\n" +
"|" + item.EUru.filter(x=>x).map(x=>"'''"+x+"'''").join(" /<br>") + "\n" +
"|" + item.USes.filter(x=>x).map(x=>"'''"+x+"'''").join(" /<br>") + "\n" +
"|" + item.EUes.filter(x=>x).map(x=>"'''"+x+"'''").join(" /<br>") + "\n" +
"|" + item.CNzh.filter(x=>x).map(x=>"'''"+x+"'''").join(" /<br>") + "\n" +
"|" + item.TWzh.filter(x=>x).map(x=>"'''"+x+"'''").join(" /<br>") + "\n" +
"|" + item.KRko.filter(x=>x).map(x=>"'''"+x+"'''").join(" /<br>")
);
if (oldFormatObtain) {
out.push("|" + item.HowToGet + ",<br>Season " + item.Season);
}
}
}
document.getElementById(id).value = out.join("\n");
}
// Process all selected files
async function process() {
let useEnglishOnly = document.getElementById("englishOnly").checked;
let useOtherLanguages = document.getElementById("otherLanguages").checked;
let oldFormatObtain = document.getElementById("oldFormatObtain").checked;
if (!(useEnglishOnly ^ useOtherLanguages)) {
throw new Error(`Bad state for the checkboxes. useEnglishOnly=${useEnglishOnly} useOtherLanguages=${useOtherLanguages}`);
}
// Load files into memory
let locales = await loadFiles("locales");
let adjectives = await loadFiles("adjectives");
let subjects = await loadFiles("subjects");
let errors = [ ... locales.errors, ... adjectives.errors, ... subjects.errors ];
if (errors.length != 0) {
console.log(errors.join("\n"));
alert("One or more errors occurred. See the console output.");
return;
}
// Process locales into titles
locales = locales.loaded;
let titles = parseLocales(locales);
console.log(titles); // For debugging
// Associate unlock data
for (let unlock of adjectives.loaded[0])
Object.assign(titles.adjectives[unlock.Id], unlock);
for (let unlock of subjects .loaded[0])
Object.assign(titles.subjects [unlock.Id], unlock);
// Process wikitext output
adjectives = Object.values(titles.adjectives).filter(a=>a.USen[0]);
adjectives.sort(sortOrder);
output("adjectivesOut", adjectives, useEnglishOnly, oldFormatObtain);
subjects = Object.values(titles.subjects ).filter(s=>s.USen[0]);
subjects .sort(sortOrder);
output("subjectsOut", subjects, useEnglishOnly, oldFormatObtain);
}
</script>
</head>
<body>
<div style="display: inline-grid; grid-template-columns: auto auto; gap: 4px; align-items: center;">
<div>Languages:</div>
<input type="file" id="locales" multiple>
<div>Adjectives:</div>
<input type="file" id="adjectives" multiple>
<div>Subjects:</div>
<input type="file" id="subjects" multiple>
<div>
<input type="checkbox" id="oldFormatObtain" name="oldFormatObtain">
<label for="oldFormatObtain">Use old format for Obtain, Season</label>
</div>
<br>
<div>
<input type="radio" id="englishOnly" name="outputMode" value="englishOnly" checked>
<label for="englishOnly">English only</label><br>
<input type="radio" id="otherLanguages" name="outputMode" value="otherLanguages">
<label for="otherLanguages">In other languages</label><br>
</div>
<div style="grid-area: span 1 / span 2;">
<button onclick="process();">Process</button>
</div>
</div>
<div style="display: flex; flex-direction: column; row-gap: 4px;">
<div style="margin-top: 16px;">Adjectives</div>
<textarea style="height: 250px; border: 1px solid #cccccc;" id="adjectivesOut"></textarea>
<div style="margin-top: 16px;">Subjects</div>
<textarea style="height: 250px; border: 1px solid #cccccc;" id="subjectsOut"></textarea>
</div>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment