Created
August 31, 2023 11:19
-
-
Save kjhf/b1278e434d3c83c4ca57869dc37a15f6 to your computer and use it in GitHub Desktop.
Splashtag Titles as wikimarkup for Inkipedia for Splatoon 3
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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