117 lines
3.6 KiB
JavaScript
117 lines
3.6 KiB
JavaScript
const quotes = ['"', "'"]
|
|
function stringify (nbt, space = false) {
|
|
const sep = ',' + (space ? ' ' : '')
|
|
switch (nbt.type) {
|
|
case 'byte':
|
|
return nbt.value + 'b'
|
|
case 'short':
|
|
return nbt.value + 's'
|
|
case 'int':
|
|
return nbt.value.toString()
|
|
case 'long':
|
|
return nbt.value + 'l'
|
|
case 'float':
|
|
return nbt.value + 'f'
|
|
case 'double':
|
|
return nbt.value + 'd'
|
|
case 'string':
|
|
return stringifyStr(nbt.value, true)
|
|
case 'list':
|
|
return `[${
|
|
nbt.value.value
|
|
.map((tag) => stringify({ type: nbt.value.type, value: tag }, space))
|
|
.join(sep)
|
|
}]`
|
|
case 'compound':
|
|
return `{${Object.entries(nbt.value).map(([key, prop]) =>
|
|
stringifyStr(key, false) + ':' + (space ? ' ' : '') + stringify(prop)
|
|
).join(sep)}}`
|
|
case 'byteArray':
|
|
return `[B;${space ? ' ' : ''}${
|
|
nbt.value
|
|
.map((value) => stringify({ type: 'byte', value }))
|
|
.join(sep)
|
|
}]`
|
|
case 'intArray':
|
|
return `[I;${space ? ' ' : ''}${
|
|
nbt.value
|
|
.map((value) => stringify({ type: 'int', value }))
|
|
.join(sep)
|
|
}]`
|
|
case 'longArray':
|
|
return `[L;${space ? ' ' : ''}${
|
|
nbt.value
|
|
.map((value) => stringify({ type: 'long', value }))
|
|
.join(sep)
|
|
}]`
|
|
case 'bool':
|
|
return nbt.value.toString()
|
|
default:
|
|
throw new Error('Unknown type: ' + nbt.type)
|
|
}
|
|
|
|
function stringifyStr (str, forceQuotes = false) {
|
|
const regex = /['\\]/g
|
|
if (regex.test(str) || /[\s|"]/g.test(str) || forceQuotes) {
|
|
str = `'${str.replace(regex, (m) => '\\' + m)}'`
|
|
}
|
|
return str
|
|
}
|
|
}
|
|
function parse (snbt) {
|
|
const num = parseInt(snbt)
|
|
if (!isNaN(num)) {
|
|
if (snbt.endsWith('b')) {
|
|
return { type: 'byte', value: num }
|
|
} else if (snbt.endsWith('s')) {
|
|
return { type: 'short', value: num }
|
|
} else if (snbt.endsWith('l')) {
|
|
return { type: 'long', value: num }
|
|
} else if (snbt.endsWith('f')) {
|
|
return { type: 'float', value: num }
|
|
} else if (snbt.endsWith('d')) {
|
|
return { type: 'double', value: num }
|
|
} else {
|
|
return { type: 'int', value: num }
|
|
}
|
|
} else if (snbt === 'true') { return true } else if (snbt === 'false') { return false } else if (snbt.startsWith('{') && snbt.endsWith('}')) {
|
|
const entries = split(snbt.slice(1, -1), ',').map(e => split(e, ':')).map(([key, prop]) => [parseStr(key), parse(prop)])
|
|
|
|
const obj = {}
|
|
entries.forEach(([key, prop]) => (obj[key] = prop))
|
|
return { type: 'compound', value: obj }
|
|
// } else if (snbt.startsWith('[') && snbt.endsWith(']')) {
|
|
} else {
|
|
return { type: 'string', value: parseStr(snbt) }
|
|
}
|
|
// worst string parser ever
|
|
function parseStr (str) {
|
|
const unquotedWl = /[a-zA-Z0-9+\-.]/g
|
|
// check quotes
|
|
for (const quote of quotes) {
|
|
if (str.startsWith(quote) && str.startsWith(quote)) {
|
|
return str.replace(new RegExp(`\\\\|\\${quote}`, 'g'), (m) => m.slice(1))
|
|
}
|
|
}
|
|
// if (!unquotedWl.test(str)) throw new Error('Invalid character in unquoted string: ' + str)
|
|
return str
|
|
}
|
|
function split (str, char, times = Infinity) {
|
|
let quote = null
|
|
let split = ''
|
|
const result = []
|
|
for (let i = 0; i < str.length; i++) {
|
|
if (quotes.includes(str[i]) && str[i - 1] !== '\\') {
|
|
if (!quote) { quote = str[i] } else if (str[i] === quote) { quote = null }
|
|
} else if (str[i] === char || !quote) {
|
|
result.push(split.trim())
|
|
split = ''
|
|
} else {
|
|
split += str[i]
|
|
}
|
|
}
|
|
return [...result, split]
|
|
}
|
|
}
|
|
|
|
module.exports = { stringify, parse }
|