From 9740e16dae0b55baf5e4939fe2ada36184762d66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Fern=C3=A1ndez=20Serrata?= <76864299+Rudxain@users.noreply.github.com> Date: Fri, 21 Oct 2022 00:31:53 -0400 Subject: [PATCH 01/10] Update shuffle.js --- shuffle.js | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/shuffle.js b/shuffle.js index c64485f..dd43f06 100644 --- a/shuffle.js +++ b/shuffle.js @@ -2,38 +2,47 @@ const fs = require('fs') const plist = require('plist'); const assets = require('./assets.json') -try { // god-tier crash prevention system +/** + * returns a pseudo-random 32bit unsigned integer + * in the interval [0, `n`) + */ +const randU32 = (n = 2**32) => Math.random() * n >>> 0; Array.prototype.shuffle = function() { - let length = this.length; let unshuffled = this; let shuffled = []; + const {length} = this; let unshuffled = this; let shuffled = []; while (shuffled.length !== length) { - let index = Math.floor(Math.random() * unshuffled.length); + let index = randU32(unshuffled.length); shuffled.push(unshuffled[index]); - unshuffled = unshuffled.filter((x, y) => y !== (index))} + unshuffled = unshuffled.filter((_, y) => y !== index) + } return shuffled; -} +} + +let undupe = arr => arr.filter((x, y) => arr.indexOf(x) == y); -function plistToJson(file) { +let plistToJson = file => { let data = plist.parse(file) for (let key in data.frames) { let fileData = data.frames[key]; for (let innerKey in fileData) { - if (typeof fileData[innerKey] == 'string') { - if (!fileData[innerKey].length) delete fileData[innerKey] - else fileData[innerKey] = JSON.parse(fileData[innerKey].replace(/{/g, '[').replace(/}/g, ']')); + let fdik = fileData[innerKey]; + if (typeof == 'string') { + if (!fdik.length) delete fileData[innerKey] + else fileData[innerKey] = JSON.parse(fdik.replace(/{/g, '[').replace(/}/g, ']')); } }} return data.frames } +try { // god-tier crash prevention system + if (!fs.existsSync('./pack')) fs.mkdirSync('./pack'); -function glow(name) { return name.replace("_001.png", "_glow_001.png") } -function undupe (arr) { return arr.filter((x, y) => arr.indexOf(x) == y) } -//function spriteRegex(name) { return new RegExp(`(${name.replace(".", "\\.")}<\/key>\\s*)((.|\\n)+?<\\/dict>)`) } +let glow = name => name.replace("_001.png", "_glow_001.png"); +//const spriteRegex = name => new RegExp(`(${name.replace(".", "\\.")}<\/key>\\s*)((.|\\n)+?<\\/dict>)`); let iconRegex = /^.+?_(\d+?)_.+/ -let forms = assets.forms +let {forms} = assets let sheetList = Object.keys(assets.sheets) let glowName = sheetList.filter(x => x.startsWith('GJ_GameSheetGlow')) From d5a923ef8e1cf86e96885f618c1b88699cc6dff5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Fern=C3=A1ndez=20Serrata?= <76864299+Rudxain@users.noreply.github.com> Date: Fri, 21 Oct 2022 00:46:04 -0400 Subject: [PATCH 02/10] Fix `mkdir` race condition --- shuffle.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shuffle.js b/shuffle.js index dd43f06..3fb8780 100644 --- a/shuffle.js +++ b/shuffle.js @@ -36,7 +36,7 @@ let plistToJson = file => { try { // god-tier crash prevention system -if (!fs.existsSync('./pack')) fs.mkdirSync('./pack'); +fs.mkdirSync('./pack', { recursive: true, mode: 0o766 }); let glow = name => name.replace("_001.png", "_glow_001.png"); //const spriteRegex = name => new RegExp(`(${name.replace(".", "\\.")}<\/key>\\s*)((.|\\n)+?<\\/dict>)`); From 78b774d184cea41a80897bd696fb8bbe8e797b73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Fern=C3=A1ndez=20Serrata?= <76864299+Rudxain@users.noreply.github.com> Date: Fri, 21 Oct 2022 01:09:29 -0400 Subject: [PATCH 03/10] Update shuffle.js --- shuffle.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/shuffle.js b/shuffle.js index 3fb8780..a095841 100644 --- a/shuffle.js +++ b/shuffle.js @@ -18,6 +18,10 @@ Array.prototype.shuffle = function() { return shuffled; } +/** + * get unique/distinct elements (same order) + * @param {any[]} arr + */ let undupe = arr => arr.filter((x, y) => arr.indexOf(x) == y); let plistToJson = file => { @@ -60,7 +64,8 @@ let glowBackups = [] let glowSheet = plistToJson(glowPlist) resources.forEach(x => { - if (x.startsWith('PlayerExplosion_') && x.endsWith('-uhd.plist')) sheetNames.push(x.slice(0, -6)) + if (x.startsWith('PlayerExplosion_') && x.endsWith('-uhd.plist')) + sheetNames.push(x.slice(0, -6)) // -6 removes ".plist", efficiently }) sheetNames.forEach(x => { From ee4725894e2a0f03cf73b4b776b257b059332133 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Fern=C3=A1ndez=20Serrata?= <76864299+Rudxain@users.noreply.github.com> Date: Fri, 21 Oct 2022 12:56:13 -0400 Subject: [PATCH 04/10] Update shuffle.js --- shuffle.js | 259 ++++++++++++++++++++++++++++------------------------- 1 file changed, 138 insertions(+), 121 deletions(-) diff --git a/shuffle.js b/shuffle.js index a095841..3aeaf10 100644 --- a/shuffle.js +++ b/shuffle.js @@ -1,3 +1,5 @@ +//@ts-check +'use strict'; const fs = require('fs') const plist = require('plist'); const assets = require('./assets.json') @@ -8,148 +10,163 @@ const assets = require('./assets.json') */ const randU32 = (n = 2**32) => Math.random() * n >>> 0; -Array.prototype.shuffle = function() { - const {length} = this; let unshuffled = this; let shuffled = []; - while (shuffled.length !== length) { - let index = randU32(unshuffled.length); - shuffled.push(unshuffled[index]); - unshuffled = unshuffled.filter((_, y) => y !== index) - } - return shuffled; -} +/** + * + * @param {any[]} unshuffled + */ +const shuffle = unshuffled => { + const {length} = unshuffled, shuffled = []; + while (shuffled.length !== length) { + let index = randU32(unshuffled.length); + shuffled.push(unshuffled[index]); + unshuffled = unshuffled.filter((_, y) => y !== index) + } + return shuffled; + }; /** * get unique/distinct elements (same order) * @param {any[]} arr */ -let undupe = arr => arr.filter((x, y) => arr.indexOf(x) == y); - -let plistToJson = file => { - let data = plist.parse(file) - for (let key in data.frames) { - let fileData = data.frames[key]; - for (let innerKey in fileData) { - let fdik = fileData[innerKey]; - if (typeof == 'string') { - if (!fdik.length) delete fileData[innerKey] - else fileData[innerKey] = JSON.parse(fdik.replace(/{/g, '[').replace(/}/g, ']')); +const undupe = arr => arr.filter((x, y) => arr.indexOf(x) == y); + +const plistToJson = (/**@type {string}*/ file) => { + const data = plist.parse(file) + for (const out_k in data.frames) { + let fileData = data.frames[out_k]; + for (const in_k in fileData) { + let fdik = fileData[in_k]; + if (typeof fdik == 'string') { + if (fdik.length == 0) delete fileData[in_k] + else fileData[in_k] = JSON.parse(fdik.replace(/{/g, '[').replace(/}/g, ']')); } }} return data.frames } -try { // god-tier crash prevention system - -fs.mkdirSync('./pack', { recursive: true, mode: 0o766 }); - -let glow = name => name.replace("_001.png", "_glow_001.png"); -//const spriteRegex = name => new RegExp(`(${name.replace(".", "\\.")}<\/key>\\s*)((.|\\n)+?<\\/dict>)`); -let iconRegex = /^.+?_(\d+?)_.+/ - -let {forms} = assets -let sheetList = Object.keys(assets.sheets) -let glowName = sheetList.filter(x => x.startsWith('GJ_GameSheetGlow')) - -// newlines/CRs are usually present in text files, strip them out so they aren't part of the pathname -let gdPath = process.argv[2] ?? fs.readFileSync('directory.txt', 'utf8').replace(/[\n\r]/g, '') - -if (!fs.existsSync(gdPath)) throw "Couldn't find your GD directory! Make sure to enter the correct file path in directory.txt" -let glowPlist = fs.readFileSync(`${gdPath}/${glowName[0]}.plist`, 'utf8') -let sheetNames = sheetList.filter(x => !glowName.includes(x)) -let resources = fs.readdirSync(gdPath) - -let plists = [] -let sheets = [] -let glowBackups = [] -let glowSheet = plistToJson(glowPlist) - -resources.forEach(x => { - if (x.startsWith('PlayerExplosion_') && x.endsWith('-uhd.plist')) - sheetNames.push(x.slice(0, -6)) // -6 removes ".plist", efficiently -}) - -sheetNames.forEach(x => { - let file = fs.readFileSync(`${gdPath}/${x}.plist`, 'utf8') - plists.push(file) - try { sheets.push(plistToJson(file)) } - catch(e) { throw `Error parsing ${x}.plist - ${e.message}` } -}) - -sheets.forEach((gameSheet, sheetNum) => { - let plist = plists[sheetNum] - let name = sheetNames[sheetNum] - if (!name.startsWith('PlayerExplosion_')) console.log("Shuffling " + name) - else if (name == "PlayerExplosion_01-uhd") console.log("Shuffling death effects") - - let sizes = {} - Object.keys(gameSheet).forEach(x => { - let obj = gameSheet[x] - obj.name = x - if (sheetNum == sheetNames.findIndex(y => y.startsWith('GJ_GameSheet02')) && forms.some(y => x.startsWith(y))) { - let form = forms.find(y => x.startsWith(y)) - if (!sizes[form]) sizes[form] = [obj] - else sizes[form].push(obj) - } - else { - let sizeDiff = assets.sheets[name] || 30 - let size = obj.textureRect[1].map(x => Math.round(x / sizeDiff) * sizeDiff).join() - if (name.startsWith('PlayerExplosion')) size = "deatheffect" - if (!sizes[size]) sizes[size] = [obj] - else sizes[size].push(obj) - } +try { // god-tier crash prevention system + + fs.mkdirSync('./pack', { recursive: true, mode: 0o766 }); + + const glow = (/**@type {string}*/ name) => name.replace("_001.png", "_glow_001.png"); + //const spriteRegex = name => new RegExp(`(${name.replace(".", "\\.")}<\/key>\\s*)((.|\\n)+?<\\/dict>)`); + const iconRegex = /^.+?_(\d+?)_.+/ + + const + {forms} = assets, + sheetList = Object.keys(assets.sheets), + glowName = sheetList.filter(x => x.startsWith('GJ_GameSheetGlow')); + + // newlines/CRs are usually present in text files, strip them out so they aren't part of the pathname + const gdPath = process.argv[2] ?? fs.readFileSync('directory.txt', 'utf8').replace(/[\n\r]/g, '') + + if (!fs.existsSync(gdPath)) + throw "Couldn't find your GD directory! Make sure to enter the correct file path in directory.txt" + const glowPlist = fs.readFileSync(`${gdPath}/${glowName[0]}.plist`, 'utf8') + const sheetNames = sheetList.filter(x => !glowName.includes(x)) + const resources = fs.readdirSync(gdPath) + + const plists = [] + const sheets = [] + const glowBackups = [] + const glowSheet = plistToJson(glowPlist) + + resources.forEach(x => { + if (x.startsWith('PlayerExplosion_') && x.endsWith('-uhd.plist')) + sheetNames.push(x.slice(0, -6)) // -6 removes ".plist", efficiently }) - - Object.keys(sizes).forEach(obj => { - let objects = sizes[obj] - if (objects.length == 1) return delete sizes[obj] - let iconMode = forms.includes(obj) - let oldNames = objects.map(x => x.name) - if (iconMode) oldNames = undupe(oldNames.map(x => x.replace(iconRegex, "$1"))) - let newNames = oldNames.shuffle() - if (iconMode) { - let iconList = {} - oldNames.forEach((x, y) => iconList[x] = newNames[y]) - newNames = iconList - } - oldNames.forEach((x, y) => { - let newName = newNames[iconMode ? x : y] - if (iconMode) { - plist = plist.replace(new RegExp(`${obj}_${x}_`, "g"), `###${obj}_${newName}_`) - glowPlist = glowPlist.replace(`${obj}_${x}_`, `###${obj}_${newName}_`) + sheetNames.forEach(x => { + let file = fs.readFileSync(`${gdPath}/${x}.plist`, 'utf8') + plists.push(file) + try { sheets.push(plistToJson(file)) } + catch(e) { throw `Error parsing ${x}.plist - ${e.message}` } + }) + + sheets.forEach((gameSheet, sheetNum) => { + let plist = plists[sheetNum] + let name = sheetNames[sheetNum] + if (!name.startsWith('PlayerExplosion_')) console.log("Shuffling " + name) + else if (name == "PlayerExplosion_01-uhd") console.log("Shuffling death effects") + + let sizes = {} + Object.keys(gameSheet).forEach(x => { + let obj = gameSheet[x] + obj.name = x + if (sheetNum == sheetNames.findIndex(y => y.startsWith('GJ_GameSheet02')) && forms.some(y => x.startsWith(y))) { + let form = forms.find(y => x.startsWith(y)) + if (!sizes[form]) sizes[form] = [obj] + else sizes[form].push(obj) } else { - plist = plist.replace(`${x}`, `###${newName}`) - if (glowSheet[glow(x)]) { - glowBackups.push(glow(x)) - glowPlist = glowPlist.replace(`${glow(x)}`, `###${glow(newName)}`) - } + /**@type {number}*/ + let sizeDiff = assets.sheets[name] || 30 + let size = obj.textureRect[1].map(x => Math.round(x / sizeDiff) * sizeDiff).join() + if (name.startsWith('PlayerExplosion')) size = "deatheffect" + if (!sizes[size]) sizes[size] = [obj] + else sizes[size].push(obj) } }) + + Object.keys(sizes).forEach(obj => { + /**@type {{name: string}[]}*/ + let objects = sizes[obj] + if (objects.length == 1) return delete sizes[obj] + let iconMode = forms.includes(obj) + let oldNames = objects.map(x => x.name) + if (iconMode) oldNames = undupe(oldNames.map(x => x.replace(iconRegex, "$1"))) + let newNames = shuffle(oldNames) + if (iconMode) { + let iconList = {} + oldNames.forEach((x, y) => iconList[x] = newNames[y]) + newNames = iconList + } + + oldNames.forEach((x, y) => { + let newName = newNames[iconMode ? x : y] + if (iconMode) { + plist = plist.replace(new RegExp(`${obj}_${x}_`, "g"), `###${obj}_${newName}_`) + glowPlist = glowPlist.replace(`${obj}_${x}_`, `###${obj}_${newName}_`) + } + else { + plist = plist.replace(`${x}`, `###${newName}`) + if (glowSheet[glow(x)]) { + glowBackups.push(glow(x)) + glowPlist = glowPlist.replace(`${glow(x)}`, `###${glow(newName)}`) + } + } + }) + }) + plist = plist.replace(/###/g, "") + fs.writeFileSync('./pack/' + sheetNames[sheetNum] + '.plist', plist, 'utf8') }) - plist = plist.replace(/###/g, "") - fs.writeFileSync('./pack/' + sheetNames[sheetNum] + '.plist', plist, 'utf8') -}) -console.log("Shuffling misc textures") -let specialGrounds = [] -assets.sprites.forEach(img => { - let spriteMatch = img.split("|") - let foundTextures = resources.filter(x => x.match(new RegExp(`^${spriteMatch[0].replace("#", "\\d+?")}-uhd\\.${spriteMatch[1] || "png"}`))) + console.log("Shuffling misc textures") + let specialGrounds = [] + assets.sprites.forEach(img => { + let spriteMatch = img.split("|") + let foundTextures = resources.filter(x => x.match(new RegExp(`^${spriteMatch[0].replace("#", "\\d+?")}-uhd\\.${spriteMatch[1] || "png"}`))) - if (spriteMatch[2] == "*") specialGrounds = specialGrounds.concat(foundTextures.map(x => x.slice(0, 15))) - if (spriteMatch[2] == "g1") foundTextures = foundTextures.filter(x => !specialGrounds.some(y => x.startsWith(y))) - if (spriteMatch[2] == "g2") foundTextures = foundTextures.filter(x => specialGrounds.some(y => x.startsWith(y))) + if (spriteMatch[2] == "*") specialGrounds = specialGrounds.concat(foundTextures.map(x => x.slice(0, 15))) + if (spriteMatch[2] == "g1") foundTextures = foundTextures.filter(x => !specialGrounds.some(y => x.startsWith(y))) + if (spriteMatch[2] == "g2") foundTextures = foundTextures.filter(x => specialGrounds.some(y => x.startsWith(y))) - let shuffledTextures = foundTextures.shuffle() - foundTextures.forEach((x, y) => fs.copyFileSync(`${gdPath}/${x}`, `./pack/${shuffledTextures[y]}`)) -}) + let shuffledTextures = shuffle(foundTextures) + foundTextures.forEach((x, y) => fs.copyFileSync(`${gdPath}/${x}`, `./pack/${shuffledTextures[y]}`)) + }) -let emptyDict = glowPlist.match(/\s*aliases<\/key>(.|\n)+?<\/dict>/)[0].replace(/{\d+,\d+}/g, "{0, 0}") -let mappedBackups = glowBackups.reverse().map(x => `${x}${emptyDict}`).join("") -glowPlist = fs.writeFileSync('./pack/GJ_GameSheetGlow-uhd.plist', glowPlist.replace(/###/g, "").replace(/\s*frames<\/key>\s*/g, "$&" + mappedBackups), 'utf8') -console.log("Randomization complete!") + let emptyDict = glowPlist.match(/\s*aliases<\/key>(.|\n)+?<\/dict>/)[0].replace(/{\d+,\d+}/g, "{0, 0}") + let mappedBackups = glowBackups.reverse().map(x => `${x}${emptyDict}`).join('') + glowPlist = fs.writeFileSync('./pack/GJ_GameSheetGlow-uhd.plist', glowPlist.replace(/###/g, "").replace(/\s*frames<\/key>\s*/g, "$&" + mappedBackups), 'utf8') + console.log("Randomization complete!") } -catch(e) { console.log(e); fs.writeFileSync('crash_log.txt', e.stack ? `Something went wrong! Send this error to Colon and he'll get around to fixing it at some point.\n\n${e.stack}` : e, 'utf8') } +catch(e) { + console.error(e); + fs.writeFileSync( + 'crash_log.txt', + e.stack ? `Something went wrong! Send this error to Colon and he'll get around to fixing it at some point.\n\n${e.stack}` : e, + 'utf8' + ) +} From b7ae88582bf298127e227a033ac85310388d89c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Fern=C3=A1ndez=20Serrata?= <76864299+Rudxain@users.noreply.github.com> Date: Fri, 21 Oct 2022 13:10:41 -0400 Subject: [PATCH 05/10] Update shuffle.js --- shuffle.js | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/shuffle.js b/shuffle.js index 3aeaf10..9f62eeb 100644 --- a/shuffle.js +++ b/shuffle.js @@ -33,9 +33,9 @@ const undupe = arr => arr.filter((x, y) => arr.indexOf(x) == y); const plistToJson = (/**@type {string}*/ file) => { const data = plist.parse(file) for (const out_k in data.frames) { - let fileData = data.frames[out_k]; + const fileData = data.frames[out_k]; for (const in_k in fileData) { - let fdik = fileData[in_k]; + const fdik = fileData[in_k]; if (typeof fdik == 'string') { if (fdik.length == 0) delete fileData[in_k] else fileData[in_k] = JSON.parse(fdik.replace(/{/g, '[').replace(/}/g, ']')); @@ -44,9 +44,12 @@ const plistToJson = (/**@type {string}*/ file) => { return data.frames } +/** working directory */ +const wd = './pack/'; + try { // god-tier crash prevention system - fs.mkdirSync('./pack', { recursive: true, mode: 0o766 }); + fs.mkdirSync(wd, { recursive: true, mode: 0o766 }); const glow = (/**@type {string}*/ name) => name.replace("_001.png", "_glow_001.png"); //const spriteRegex = name => new RegExp(`(${name.replace(".", "\\.")}<\/key>\\s*)((.|\\n)+?<\\/dict>)`); @@ -62,7 +65,7 @@ try { // god-tier crash prevention system if (!fs.existsSync(gdPath)) throw "Couldn't find your GD directory! Make sure to enter the correct file path in directory.txt" - const glowPlist = fs.readFileSync(`${gdPath}/${glowName[0]}.plist`, 'utf8') + let glowPlist = fs.readFileSync(`${gdPath}/${glowName[0]}.plist`, 'utf8') const sheetNames = sheetList.filter(x => !glowName.includes(x)) const resources = fs.readdirSync(gdPath) @@ -137,27 +140,28 @@ try { // god-tier crash prevention system } }) }) - plist = plist.replace(/###/g, "") - fs.writeFileSync('./pack/' + sheetNames[sheetNum] + '.plist', plist, 'utf8') + plist = plist.replace(/###/g, '') + fs.writeFileSync(wd + sheetNames[sheetNum] + '.plist', plist, 'utf8') }) console.log("Shuffling misc textures") - let specialGrounds = [] + /**@type {string[]}*/ + const specialGrounds = [] assets.sprites.forEach(img => { - let spriteMatch = img.split("|") + const spriteMatch = img.split("|") let foundTextures = resources.filter(x => x.match(new RegExp(`^${spriteMatch[0].replace("#", "\\d+?")}-uhd\\.${spriteMatch[1] || "png"}`))) - if (spriteMatch[2] == "*") specialGrounds = specialGrounds.concat(foundTextures.map(x => x.slice(0, 15))) + if (spriteMatch[2] == "*") specialGrounds.push(...foundTextures.map(x => x.slice(0, 15))) // in-place `concat` if (spriteMatch[2] == "g1") foundTextures = foundTextures.filter(x => !specialGrounds.some(y => x.startsWith(y))) if (spriteMatch[2] == "g2") foundTextures = foundTextures.filter(x => specialGrounds.some(y => x.startsWith(y))) let shuffledTextures = shuffle(foundTextures) - foundTextures.forEach((x, y) => fs.copyFileSync(`${gdPath}/${x}`, `./pack/${shuffledTextures[y]}`)) + foundTextures.forEach((x, y) => fs.copyFileSync(`${gdPath}/${x}`, wd + shuffledTextures[y])) }) let emptyDict = glowPlist.match(/\s*aliases<\/key>(.|\n)+?<\/dict>/)[0].replace(/{\d+,\d+}/g, "{0, 0}") let mappedBackups = glowBackups.reverse().map(x => `${x}${emptyDict}`).join('') - glowPlist = fs.writeFileSync('./pack/GJ_GameSheetGlow-uhd.plist', glowPlist.replace(/###/g, "").replace(/\s*frames<\/key>\s*/g, "$&" + mappedBackups), 'utf8') + glowPlist = fs.writeFileSync(wd + 'GJ_GameSheetGlow-uhd.plist', glowPlist.replace(/###/g, "").replace(/\s*frames<\/key>\s*/g, "$&" + mappedBackups), 'utf8') console.log("Randomization complete!") } From e16b76b6c460956e04c120341760ee8848348cf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Fern=C3=A1ndez=20Serrata?= <76864299+Rudxain@users.noreply.github.com> Date: Fri, 21 Oct 2022 13:26:42 -0400 Subject: [PATCH 06/10] Update shuffle.js --- shuffle.js | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/shuffle.js b/shuffle.js index 9f62eeb..0d23f7a 100644 --- a/shuffle.js +++ b/shuffle.js @@ -30,15 +30,20 @@ const shuffle = unshuffled => { */ const undupe = arr => arr.filter((x, y) => arr.indexOf(x) == y); -const plistToJson = (/**@type {string}*/ file) => { +/** + * convert plist string to JSON, then JSON to `Object` + * @param {string} file + * @return {import('plist').PlistObject} + */ +const plistToJson = file => { + /**@type {import('plist').PlistObject}*/ const data = plist.parse(file) - for (const out_k in data.frames) { - const fileData = data.frames[out_k]; - for (const in_k in fileData) { - const fdik = fileData[in_k]; + for (const fileData of Object.values(data.frames)) { + for (const k in fileData) { + const fdik = fileData[k]; if (typeof fdik == 'string') { - if (fdik.length == 0) delete fileData[in_k] - else fileData[in_k] = JSON.parse(fdik.replace(/{/g, '[').replace(/}/g, ']')); + if (fdik.length == 0) delete fileData[k] + else fileData[k] = JSON.parse(fdik.replace(/{/g, '[').replace(/}/g, ']')); } }} return data.frames @@ -69,6 +74,7 @@ try { // god-tier crash prevention system const sheetNames = sheetList.filter(x => !glowName.includes(x)) const resources = fs.readdirSync(gdPath) + /**@type {string[]}*/ const plists = [] const sheets = [] const glowBackups = [] @@ -80,7 +86,7 @@ try { // god-tier crash prevention system }) sheetNames.forEach(x => { - let file = fs.readFileSync(`${gdPath}/${x}.plist`, 'utf8') + const file = fs.readFileSync(`${gdPath}/${x}.plist`, 'utf8') plists.push(file) try { sheets.push(plistToJson(file)) } catch(e) { throw `Error parsing ${x}.plist - ${e.message}` } @@ -111,11 +117,11 @@ try { // god-tier crash prevention system } }) - Object.keys(sizes).forEach(obj => { + Object.keys(sizes).forEach(k => { /**@type {{name: string}[]}*/ - let objects = sizes[obj] - if (objects.length == 1) return delete sizes[obj] - let iconMode = forms.includes(obj) + let objects = sizes[k] + if (objects.length == 1) return delete sizes[k] + let iconMode = forms.includes(k) let oldNames = objects.map(x => x.name) if (iconMode) oldNames = undupe(oldNames.map(x => x.replace(iconRegex, "$1"))) let newNames = shuffle(oldNames) @@ -128,8 +134,8 @@ try { // god-tier crash prevention system oldNames.forEach((x, y) => { let newName = newNames[iconMode ? x : y] if (iconMode) { - plist = plist.replace(new RegExp(`${obj}_${x}_`, "g"), `###${obj}_${newName}_`) - glowPlist = glowPlist.replace(`${obj}_${x}_`, `###${obj}_${newName}_`) + plist = plist.replace(new RegExp(`${k}_${x}_`, "g"), `###${k}_${newName}_`) + glowPlist = glowPlist.replace(`${k}_${x}_`, `###${k}_${newName}_`) } else { plist = plist.replace(`${x}`, `###${newName}`) From c3e07561f991ff6bfba327b3b678852307e848f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Fern=C3=A1ndez=20Serrata?= <76864299+Rudxain@users.noreply.github.com> Date: Fri, 21 Oct 2022 13:46:37 -0400 Subject: [PATCH 07/10] `plistToJson` --- shuffle.js | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/shuffle.js b/shuffle.js index 0d23f7a..1e755d9 100644 --- a/shuffle.js +++ b/shuffle.js @@ -36,17 +36,18 @@ const undupe = arr => arr.filter((x, y) => arr.indexOf(x) == y); * @return {import('plist').PlistObject} */ const plistToJson = file => { - /**@type {import('plist').PlistObject}*/ - const data = plist.parse(file) - for (const fileData of Object.values(data.frames)) { - for (const k in fileData) { - const fdik = fileData[k]; + const {frames: datFrames} = plist.parse(file); + // not using `Object.values`, because we want to mutate in-place + for (const out_k in datFrames) { + const fileData = datFrames[out_k]; + for (const in_k in fileData) { + const fdik = fileData[in_k]; if (typeof fdik == 'string') { - if (fdik.length == 0) delete fileData[k] - else fileData[k] = JSON.parse(fdik.replace(/{/g, '[').replace(/}/g, ']')); + if (fdik.length == 0) delete fileData[in_k] + else fileData[in_k] = JSON.parse(fdik.replace(/{/g, '[').replace(/}/g, ']')); } }} - return data.frames + return datFrames } /** working directory */ @@ -119,9 +120,9 @@ try { // god-tier crash prevention system Object.keys(sizes).forEach(k => { /**@type {{name: string}[]}*/ - let objects = sizes[k] + const objects = sizes[k] if (objects.length == 1) return delete sizes[k] - let iconMode = forms.includes(k) + const iconMode = forms.includes(k) let oldNames = objects.map(x => x.name) if (iconMode) oldNames = undupe(oldNames.map(x => x.replace(iconRegex, "$1"))) let newNames = shuffle(oldNames) From 485475631ea0ca050d61c5610b7594d4d7542ec0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Fern=C3=A1ndez=20Serrata?= <76864299+Rudxain@users.noreply.github.com> Date: Fri, 21 Oct 2022 13:51:51 -0400 Subject: [PATCH 08/10] format shuffle --- shuffle.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/shuffle.js b/shuffle.js index 1e755d9..3907182 100644 --- a/shuffle.js +++ b/shuffle.js @@ -15,14 +15,14 @@ const randU32 = (n = 2**32) => Math.random() * n >>> 0; * @param {any[]} unshuffled */ const shuffle = unshuffled => { - const {length} = unshuffled, shuffled = []; - while (shuffled.length !== length) { - let index = randU32(unshuffled.length); - shuffled.push(unshuffled[index]); - unshuffled = unshuffled.filter((_, y) => y !== index) - } - return shuffled; - }; + const {length} = unshuffled, shuffled = []; + while (shuffled.length !== length) { + let index = randU32(unshuffled.length); + shuffled.push(unshuffled[index]); + unshuffled = unshuffled.filter((_, y) => y !== index) + } + return shuffled; +}; /** * get unique/distinct elements (same order) From bf9b4543cc9bdf448b64f74bf00268d525d59714 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Fern=C3=A1ndez=20Serrata?= <76864299+Rudxain@users.noreply.github.com> Date: Fri, 21 Oct 2022 13:56:08 -0400 Subject: [PATCH 09/10] shorter names --- shuffle.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/shuffle.js b/shuffle.js index 3907182..25d539a 100644 --- a/shuffle.js +++ b/shuffle.js @@ -11,17 +11,17 @@ const assets = require('./assets.json') const randU32 = (n = 2**32) => Math.random() * n >>> 0; /** - * - * @param {any[]} unshuffled + * https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm + * @param {any[]} inp */ -const shuffle = unshuffled => { - const {length} = unshuffled, shuffled = []; - while (shuffled.length !== length) { - let index = randU32(unshuffled.length); - shuffled.push(unshuffled[index]); - unshuffled = unshuffled.filter((_, y) => y !== index) +const shuffle = inp => { + const {length} = inp, out = []; + while (out.length !== length) { + let index = randU32(inp.length); + out.push(inp[index]); + inp = inp.filter((_, y) => y !== index) } - return shuffled; + return out; }; /** From 186521f4020d062ab828523d335e4c550c4bd354 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Fern=C3=A1ndez=20Serrata?= <76864299+Rudxain@users.noreply.github.com> Date: Fri, 21 Oct 2022 14:02:17 -0400 Subject: [PATCH 10/10] optimized `shuffle` to O(n) from O(n^2) --- shuffle.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/shuffle.js b/shuffle.js index 25d539a..f371746 100644 --- a/shuffle.js +++ b/shuffle.js @@ -12,16 +12,16 @@ const randU32 = (n = 2**32) => Math.random() * n >>> 0; /** * https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm - * @param {any[]} inp + * https://github.com/steventhomson/array-generic-shuffle/blob/master/shuffle.js + * @param {any[]} a */ -const shuffle = inp => { - const {length} = inp, out = []; - while (out.length !== length) { - let index = randU32(inp.length); - out.push(inp[index]); - inp = inp.filter((_, y) => y !== index) +const shuffle = a => { + let len = a.length; + while (len > 0) { + const i = randU32(len); + len--; + [a[len], a[i]] = [a[i], a[len]]; // swap } - return out; }; /**