UNPKG

@xmcl/mod-parser

Version:

The utilities to parse Forge/Liteloader/Fabric/Quilt mod metadata.

1,517 lines (1,496 loc) 58.3 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // index.ts var mod_parser_exports = {}; __export(mod_parser_exports, { CorruptedForgeConfigError: () => CorruptedForgeConfigError, ForgeConfig: () => ForgeConfig, ForgeModParseFailedError: () => ForgeModParseFailedError, readFabricMod: () => readFabricMod, readForgeMod: () => readForgeMod, readForgeModAsm: () => readForgeModAsm, readForgeModJson: () => readForgeModJson, readForgeModManifest: () => readForgeModManifest, readForgeModToml: () => readForgeModToml, readLiteloaderMod: () => readLiteloaderMod, readQuiltMod: () => readQuiltMod }); module.exports = __toCommonJS(mod_parser_exports); // forge.ts var import_system = require("@xmcl/system"); // ../../node_modules/.pnpm/smol-toml@1.4.1/node_modules/smol-toml/dist/error.js function getLineColFromPtr(string, ptr) { let lines = string.slice(0, ptr).split(/\r\n|\n|\r/g); return [lines.length, lines.pop().length + 1]; } function makeCodeBlock(string, line, column) { let lines = string.split(/\r\n|\n|\r/g); let codeblock = ""; let numberLen = (Math.log10(line + 1) | 0) + 1; for (let i = line - 1; i <= line + 1; i++) { let l = lines[i - 1]; if (!l) continue; codeblock += i.toString().padEnd(numberLen, " "); codeblock += ": "; codeblock += l; codeblock += "\n"; if (i === line) { codeblock += " ".repeat(numberLen + column + 2); codeblock += "^\n"; } } return codeblock; } var TomlError = class extends Error { line; column; codeblock; constructor(message, options) { const [line, column] = getLineColFromPtr(options.toml, options.ptr); const codeblock = makeCodeBlock(options.toml, line, column); super(`Invalid TOML document: ${message} ${codeblock}`, options); this.line = line; this.column = column; this.codeblock = codeblock; } }; // ../../node_modules/.pnpm/smol-toml@1.4.1/node_modules/smol-toml/dist/util.js function indexOfNewline(str, start = 0, end = str.length) { let idx = str.indexOf("\n", start); if (str[idx - 1] === "\r") idx--; return idx <= end ? idx : -1; } function skipComment(str, ptr) { for (let i = ptr; i < str.length; i++) { let c = str[i]; if (c === "\n") return i; if (c === "\r" && str[i + 1] === "\n") return i + 1; if (c < " " && c !== " " || c === "\x7F") { throw new TomlError("control characters are not allowed in comments", { toml: str, ptr }); } } return str.length; } function skipVoid(str, ptr, banNewLines, banComments) { let c; while ((c = str[ptr]) === " " || c === " " || !banNewLines && (c === "\n" || c === "\r" && str[ptr + 1] === "\n")) ptr++; return banComments || c !== "#" ? ptr : skipVoid(str, skipComment(str, ptr), banNewLines); } function skipUntil(str, ptr, sep, end, banNewLines = false) { if (!end) { ptr = indexOfNewline(str, ptr); return ptr < 0 ? str.length : ptr; } for (let i = ptr; i < str.length; i++) { let c = str[i]; if (c === "#") { i = indexOfNewline(str, i); } else if (c === sep) { return i + 1; } else if (c === end || banNewLines && (c === "\n" || c === "\r" && str[i + 1] === "\n")) { return i; } } throw new TomlError("cannot find end of structure", { toml: str, ptr }); } function getStringEnd(str, seek) { let first = str[seek]; let target = first === str[seek + 1] && str[seek + 1] === str[seek + 2] ? str.slice(seek, seek + 3) : first; seek += target.length - 1; do seek = str.indexOf(target, ++seek); while (seek > -1 && first !== "'" && str[seek - 1] === "\\" && (str[seek - 2] !== "\\" || str[seek - 3] === "\\")); if (seek > -1) { seek += target.length; if (target.length > 1) { if (str[seek] === first) seek++; if (str[seek] === first) seek++; } } return seek; } // ../../node_modules/.pnpm/smol-toml@1.4.1/node_modules/smol-toml/dist/date.js var DATE_TIME_RE = /^(\d{4}-\d{2}-\d{2})?[T ]?(?:(\d{2}):\d{2}:\d{2}(?:\.\d+)?)?(Z|[-+]\d{2}:\d{2})?$/i; var TomlDate = class extends Date { #hasDate = false; #hasTime = false; #offset = null; constructor(date) { let hasDate = true; let hasTime = true; let offset = "Z"; if (typeof date === "string") { let match = date.match(DATE_TIME_RE); if (match) { if (!match[1]) { hasDate = false; date = `0000-01-01T${date}`; } hasTime = !!match[2]; hasTime && date[10] === " " && (date = date.replace(" ", "T")); if (match[2] && +match[2] > 23) { date = ""; } else { offset = match[3] || null; date = date.toUpperCase(); if (!offset && hasTime) date += "Z"; } } else { date = ""; } } super(date); if (!isNaN(this.getTime())) { this.#hasDate = hasDate; this.#hasTime = hasTime; this.#offset = offset; } } isDateTime() { return this.#hasDate && this.#hasTime; } isLocal() { return !this.#hasDate || !this.#hasTime || !this.#offset; } isDate() { return this.#hasDate && !this.#hasTime; } isTime() { return this.#hasTime && !this.#hasDate; } isValid() { return this.#hasDate || this.#hasTime; } toISOString() { let iso = super.toISOString(); if (this.isDate()) return iso.slice(0, 10); if (this.isTime()) return iso.slice(11, 23); if (this.#offset === null) return iso.slice(0, -1); if (this.#offset === "Z") return iso; let offset = +this.#offset.slice(1, 3) * 60 + +this.#offset.slice(4, 6); offset = this.#offset[0] === "-" ? offset : -offset; let offsetDate = new Date(this.getTime() - offset * 6e4); return offsetDate.toISOString().slice(0, -1) + this.#offset; } static wrapAsOffsetDateTime(jsDate, offset = "Z") { let date = new TomlDate(jsDate); date.#offset = offset; return date; } static wrapAsLocalDateTime(jsDate) { let date = new TomlDate(jsDate); date.#offset = null; return date; } static wrapAsLocalDate(jsDate) { let date = new TomlDate(jsDate); date.#hasTime = false; date.#offset = null; return date; } static wrapAsLocalTime(jsDate) { let date = new TomlDate(jsDate); date.#hasDate = false; date.#offset = null; return date; } }; // ../../node_modules/.pnpm/smol-toml@1.4.1/node_modules/smol-toml/dist/primitive.js var INT_REGEX = /^((0x[0-9a-fA-F](_?[0-9a-fA-F])*)|(([+-]|0[ob])?\d(_?\d)*))$/; var FLOAT_REGEX = /^[+-]?\d(_?\d)*(\.\d(_?\d)*)?([eE][+-]?\d(_?\d)*)?$/; var LEADING_ZERO = /^[+-]?0[0-9_]/; var ESCAPE_REGEX = /^[0-9a-f]{4,8}$/i; var ESC_MAP = { b: "\b", t: " ", n: "\n", f: "\f", r: "\r", '"': '"', "\\": "\\" }; function parseString(str, ptr = 0, endPtr = str.length) { let isLiteral = str[ptr] === "'"; let isMultiline = str[ptr++] === str[ptr] && str[ptr] === str[ptr + 1]; if (isMultiline) { endPtr -= 2; if (str[ptr += 2] === "\r") ptr++; if (str[ptr] === "\n") ptr++; } let tmp = 0; let isEscape; let parsed = ""; let sliceStart = ptr; while (ptr < endPtr - 1) { let c = str[ptr++]; if (c === "\n" || c === "\r" && str[ptr] === "\n") { if (!isMultiline) { throw new TomlError("newlines are not allowed in strings", { toml: str, ptr: ptr - 1 }); } } else if (c < " " && c !== " " || c === "\x7F") { throw new TomlError("control characters are not allowed in strings", { toml: str, ptr: ptr - 1 }); } if (isEscape) { isEscape = false; if (c === "u" || c === "U") { let code = str.slice(ptr, ptr += c === "u" ? 4 : 8); if (!ESCAPE_REGEX.test(code)) { throw new TomlError("invalid unicode escape", { toml: str, ptr: tmp }); } try { parsed += String.fromCodePoint(parseInt(code, 16)); } catch { throw new TomlError("invalid unicode escape", { toml: str, ptr: tmp }); } } else if (isMultiline && (c === "\n" || c === " " || c === " " || c === "\r")) { ptr = skipVoid(str, ptr - 1, true); if (str[ptr] !== "\n" && str[ptr] !== "\r") { throw new TomlError("invalid escape: only line-ending whitespace may be escaped", { toml: str, ptr: tmp }); } ptr = skipVoid(str, ptr); } else if (c in ESC_MAP) { parsed += ESC_MAP[c]; } else { throw new TomlError("unrecognized escape sequence", { toml: str, ptr: tmp }); } sliceStart = ptr; } else if (!isLiteral && c === "\\") { tmp = ptr - 1; isEscape = true; parsed += str.slice(sliceStart, tmp); } } return parsed + str.slice(sliceStart, endPtr - 1); } function parseValue(value, toml, ptr, integersAsBigInt) { if (value === "true") return true; if (value === "false") return false; if (value === "-inf") return -Infinity; if (value === "inf" || value === "+inf") return Infinity; if (value === "nan" || value === "+nan" || value === "-nan") return NaN; if (value === "-0") return integersAsBigInt ? 0n : 0; let isInt = INT_REGEX.test(value); if (isInt || FLOAT_REGEX.test(value)) { if (LEADING_ZERO.test(value)) { throw new TomlError("leading zeroes are not allowed", { toml, ptr }); } value = value.replace(/_/g, ""); let numeric = +value; if (isNaN(numeric)) { throw new TomlError("invalid number", { toml, ptr }); } if (isInt) { if ((isInt = !Number.isSafeInteger(numeric)) && !integersAsBigInt) { throw new TomlError("integer value cannot be represented losslessly", { toml, ptr }); } if (isInt || integersAsBigInt === true) numeric = BigInt(value); } return numeric; } const date = new TomlDate(value); if (!date.isValid()) { throw new TomlError("invalid value", { toml, ptr }); } return date; } // ../../node_modules/.pnpm/smol-toml@1.4.1/node_modules/smol-toml/dist/extract.js function sliceAndTrimEndOf(str, startPtr, endPtr, allowNewLines) { let value = str.slice(startPtr, endPtr); let commentIdx = value.indexOf("#"); if (commentIdx > -1) { skipComment(str, commentIdx); value = value.slice(0, commentIdx); } let trimmed = value.trimEnd(); if (!allowNewLines) { let newlineIdx = value.indexOf("\n", trimmed.length); if (newlineIdx > -1) { throw new TomlError("newlines are not allowed in inline tables", { toml: str, ptr: startPtr + newlineIdx }); } } return [trimmed, commentIdx]; } function extractValue(str, ptr, end, depth, integersAsBigInt) { if (depth === 0) { throw new TomlError("document contains excessively nested structures. aborting.", { toml: str, ptr }); } let c = str[ptr]; if (c === "[" || c === "{") { let [value, endPtr2] = c === "[" ? parseArray(str, ptr, depth, integersAsBigInt) : parseInlineTable(str, ptr, depth, integersAsBigInt); let newPtr = end ? skipUntil(str, endPtr2, ",", end) : endPtr2; if (endPtr2 - newPtr && end === "}") { let nextNewLine = indexOfNewline(str, endPtr2, newPtr); if (nextNewLine > -1) { throw new TomlError("newlines are not allowed in inline tables", { toml: str, ptr: nextNewLine }); } } return [value, newPtr]; } let endPtr; if (c === '"' || c === "'") { endPtr = getStringEnd(str, ptr); let parsed = parseString(str, ptr, endPtr); if (end) { endPtr = skipVoid(str, endPtr, end !== "]"); if (str[endPtr] && str[endPtr] !== "," && str[endPtr] !== end && str[endPtr] !== "\n" && str[endPtr] !== "\r") { throw new TomlError("unexpected character encountered", { toml: str, ptr: endPtr }); } endPtr += +(str[endPtr] === ","); } return [parsed, endPtr]; } endPtr = skipUntil(str, ptr, ",", end); let slice = sliceAndTrimEndOf(str, ptr, endPtr - +(str[endPtr - 1] === ","), end === "]"); if (!slice[0]) { throw new TomlError("incomplete key-value declaration: no value specified", { toml: str, ptr }); } if (end && slice[1] > -1) { endPtr = skipVoid(str, ptr + slice[1]); endPtr += +(str[endPtr] === ","); } return [ parseValue(slice[0], str, ptr, integersAsBigInt), endPtr ]; } // ../../node_modules/.pnpm/smol-toml@1.4.1/node_modules/smol-toml/dist/struct.js var KEY_PART_RE = /^[a-zA-Z0-9-_]+[ \t]*$/; function parseKey(str, ptr, end = "=") { let dot = ptr - 1; let parsed = []; let endPtr = str.indexOf(end, ptr); if (endPtr < 0) { throw new TomlError("incomplete key-value: cannot find end of key", { toml: str, ptr }); } do { let c = str[ptr = ++dot]; if (c !== " " && c !== " ") { if (c === '"' || c === "'") { if (c === str[ptr + 1] && c === str[ptr + 2]) { throw new TomlError("multiline strings are not allowed in keys", { toml: str, ptr }); } let eos = getStringEnd(str, ptr); if (eos < 0) { throw new TomlError("unfinished string encountered", { toml: str, ptr }); } dot = str.indexOf(".", eos); let strEnd = str.slice(eos, dot < 0 || dot > endPtr ? endPtr : dot); let newLine = indexOfNewline(strEnd); if (newLine > -1) { throw new TomlError("newlines are not allowed in keys", { toml: str, ptr: ptr + dot + newLine }); } if (strEnd.trimStart()) { throw new TomlError("found extra tokens after the string part", { toml: str, ptr: eos }); } if (endPtr < eos) { endPtr = str.indexOf(end, eos); if (endPtr < 0) { throw new TomlError("incomplete key-value: cannot find end of key", { toml: str, ptr }); } } parsed.push(parseString(str, ptr, eos)); } else { dot = str.indexOf(".", ptr); let part = str.slice(ptr, dot < 0 || dot > endPtr ? endPtr : dot); if (!KEY_PART_RE.test(part)) { throw new TomlError("only letter, numbers, dashes and underscores are allowed in keys", { toml: str, ptr }); } parsed.push(part.trimEnd()); } } } while (dot + 1 && dot < endPtr); return [parsed, skipVoid(str, endPtr + 1, true, true)]; } function parseInlineTable(str, ptr, depth, integersAsBigInt) { let res = {}; let seen = /* @__PURE__ */ new Set(); let c; let comma = 0; ptr++; while ((c = str[ptr++]) !== "}" && c) { let err = { toml: str, ptr: ptr - 1 }; if (c === "\n") { throw new TomlError("newlines are not allowed in inline tables", err); } else if (c === "#") { throw new TomlError("inline tables cannot contain comments", err); } else if (c === ",") { throw new TomlError("expected key-value, found comma", err); } else if (c !== " " && c !== " ") { let k; let t = res; let hasOwn = false; let [key, keyEndPtr] = parseKey(str, ptr - 1); for (let i = 0; i < key.length; i++) { if (i) t = hasOwn ? t[k] : t[k] = {}; k = key[i]; if ((hasOwn = Object.hasOwn(t, k)) && (typeof t[k] !== "object" || seen.has(t[k]))) { throw new TomlError("trying to redefine an already defined value", { toml: str, ptr }); } if (!hasOwn && k === "__proto__") { Object.defineProperty(t, k, { enumerable: true, configurable: true, writable: true }); } } if (hasOwn) { throw new TomlError("trying to redefine an already defined value", { toml: str, ptr }); } let [value, valueEndPtr] = extractValue(str, keyEndPtr, "}", depth - 1, integersAsBigInt); seen.add(value); t[k] = value; ptr = valueEndPtr; comma = str[ptr - 1] === "," ? ptr - 1 : 0; } } if (comma) { throw new TomlError("trailing commas are not allowed in inline tables", { toml: str, ptr: comma }); } if (!c) { throw new TomlError("unfinished table encountered", { toml: str, ptr }); } return [res, ptr]; } function parseArray(str, ptr, depth, integersAsBigInt) { let res = []; let c; ptr++; while ((c = str[ptr++]) !== "]" && c) { if (c === ",") { throw new TomlError("expected value, found comma", { toml: str, ptr: ptr - 1 }); } else if (c === "#") ptr = skipComment(str, ptr); else if (c !== " " && c !== " " && c !== "\n" && c !== "\r") { let e = extractValue(str, ptr - 1, "]", depth - 1, integersAsBigInt); res.push(e[0]); ptr = e[1]; } } if (!c) { throw new TomlError("unfinished array encountered", { toml: str, ptr }); } return [res, ptr]; } // ../../node_modules/.pnpm/smol-toml@1.4.1/node_modules/smol-toml/dist/parse.js function peekTable(key, table, meta, type) { var _a, _b; let t = table; let m = meta; let k; let hasOwn = false; let state; for (let i = 0; i < key.length; i++) { if (i) { t = hasOwn ? t[k] : t[k] = {}; m = (state = m[k]).c; if (type === 0 && (state.t === 1 || state.t === 2)) { return null; } if (state.t === 2) { let l = t.length - 1; t = t[l]; m = m[l].c; } } k = key[i]; if ((hasOwn = Object.hasOwn(t, k)) && ((_a = m[k]) == null ? void 0 : _a.t) === 0 && ((_b = m[k]) == null ? void 0 : _b.d)) { return null; } if (!hasOwn) { if (k === "__proto__") { Object.defineProperty(t, k, { enumerable: true, configurable: true, writable: true }); Object.defineProperty(m, k, { enumerable: true, configurable: true, writable: true }); } m[k] = { t: i < key.length - 1 && type === 2 ? 3 : type, d: false, i: 0, c: {} }; } } state = m[k]; if (state.t !== type && !(type === 1 && state.t === 3)) { return null; } if (type === 2) { if (!state.d) { state.d = true; t[k] = []; } t[k].push(t = {}); state.c[state.i++] = state = { t: 1, d: false, i: 0, c: {} }; } if (state.d) { return null; } state.d = true; if (type === 1) { t = hasOwn ? t[k] : t[k] = {}; } else if (type === 0 && hasOwn) { return null; } return [k, t, state.c]; } function parse(toml, { maxDepth = 1e3, integersAsBigInt } = {}) { let res = {}; let meta = {}; let tbl = res; let m = meta; for (let ptr = skipVoid(toml, 0); ptr < toml.length; ) { if (toml[ptr] === "[") { let isTableArray = toml[++ptr] === "["; let k = parseKey(toml, ptr += +isTableArray, "]"); if (isTableArray) { if (toml[k[1] - 1] !== "]") { throw new TomlError("expected end of table declaration", { toml, ptr: k[1] - 1 }); } k[1]++; } let p = peekTable( k[0], res, meta, isTableArray ? 2 : 1 /* Type.EXPLICIT */ ); if (!p) { throw new TomlError("trying to redefine an already defined table or value", { toml, ptr }); } m = p[2]; tbl = p[1]; ptr = k[1]; } else { let k = parseKey(toml, ptr); let p = peekTable( k[0], tbl, m, 0 /* Type.DOTTED */ ); if (!p) { throw new TomlError("trying to redefine an already defined table or value", { toml, ptr }); } let v = extractValue(toml, k[1], void 0, maxDepth, integersAsBigInt); p[1][p[0]] = v[0]; ptr = v[1]; } ptr = skipVoid(toml, ptr, true); if (toml[ptr] && toml[ptr] !== "\n" && toml[ptr] !== "\r") { throw new TomlError("each key-value declaration must be followed by an end-of-line", { toml, ptr }); } ptr = skipVoid(toml, ptr); } return res; } // forge.ts var import_asm = require("@xmcl/asm"); var ModAnnotationVisitor = class extends import_asm.AnnotationVisitor { constructor(map) { super(import_asm.Opcodes.ASM5); this.map = map; } visit(s, o) { if (s === "value") { this.map.modid = o; } else { this.map[s] = o; } } }; var McVersionAnnotationVisitor = class extends import_asm.AnnotationVisitor { constructor(map) { super(import_asm.Opcodes.ASM5); this.map = map; } visit(s, o) { if (s === "value") { this.map(o); } } }; var DummyModConstructorVisitor = class extends import_asm.MethodVisitor { constructor(parent, api) { super(api); this.parent = parent; } stack = []; visitLdcInsn(value) { this.stack.push(value); } visitFieldInsn(opcode, owner, name, desc) { if (opcode === import_asm.Opcodes.PUTFIELD) { const last = this.stack.pop(); if (last) { if (name === "modId") { this.parent.guess.modid = last; } else if (name === "version") { this.parent.guess.version = last; } else if (name === "name") { this.parent.guess.name = last; } else if (name === "url") { this.parent.guess.url = last; } else if (name === "parent") { this.parent.guess.parent = last; } else if (name === "mcversion") { this.parent.guess.mcversion = last; } } } } }; var ModClassVisitor = class extends import_asm.ClassVisitor { constructor(result, guess, corePlugin) { super(import_asm.Opcodes.ASM5); this.result = result; this.guess = guess; this.corePlugin = corePlugin; } fields = {}; className = ""; isDummyModContainer = false; isPluginClass = false; mcVersionInPlugin = ""; pluginName = ""; validateType(desc) { if (desc.indexOf("net/minecraftforge") !== -1) { this.result.usedForgePackage = true; } if (desc.indexOf("net/minecraft") !== -1) { this.result.usedMinecraftPackage = true; } if (desc.indexOf("cpw/mods/fml") !== -1) { this.result.usedLegacyFMLPackage = true; } if (desc.indexOf("net/minecraft/client") !== -1) { this.result.usedMinecraftClientPackage = true; } } visit(version, access, name, signature, superName, interfaces) { this.className = name; this.isPluginClass = name === this.corePlugin; if (superName === "net/minecraftforge/fml/common/DummyModContainer") { this.isDummyModContainer = true; } this.validateType(superName); for (const intef of interfaces) { this.validateType(intef); if (intef.indexOf("net/minecraftforge/fml/relauncher/IFMLLoadingPlugin") !== -1) { this.result.fmlPluginClassName = name; } } } visitMethod(access, name, desc, signature, exceptions) { if (this.isDummyModContainer && name === "<init>") { return new DummyModConstructorVisitor(this, import_asm.Opcodes.ASM5); } this.validateType(desc); return null; } visitField(access, name, desc, signature, value) { this.fields[name] = value; return null; } visitAnnotation(desc, visible) { if (desc === "Lnet/minecraftforge/fml/common/Mod;" || desc === "Lcpw/mods/fml/common/Mod;") { const annotationData = { modid: "", name: "", version: "", dependencies: "", useMetadata: true, clientSideOnly: false, serverSideOnly: false, acceptedMinecraftVersions: "", acceptableRemoteVersions: "", acceptableSaveVersions: "", modLanguage: "java", modLanguageAdapter: "", value: "" }; this.result.modAnnotations.push(annotationData); return new ModAnnotationVisitor(annotationData); } else if (desc === "Lnet/minecraftforge/fml/relauncher/IFMLLoadingPlugin$MCVersion;") { return new McVersionAnnotationVisitor((v) => { this.result.fmlPluginMcVersion = v; }); } return null; } visitEnd() { if ((this.className === "Config" || this.className === "net/optifine/Config" || this.className === "notch/net/optifine/Config") && this.fields && this.fields.OF_NAME) { this.result.modAnnotations.push({ modid: this.fields.OF_NAME, name: this.fields.OF_NAME, mcversion: this.fields.MC_VERSION, version: `${this.fields.OF_EDITION}_${this.fields.OF_RELEASE}`, description: "OptiFine is a Minecraft optimization mod. It allows Minecraft to run faster and look better with full support for HD textures and many configuration options.", authorList: ["sp614x"], url: "https://optifine.net", clientSideOnly: true, serverSideOnly: false, value: "", dependencies: "", useMetadata: false, acceptableRemoteVersions: "", acceptableSaveVersions: "", acceptedMinecraftVersions: `[${this.fields.MC_VERSION}]`, modLanguage: "java", modLanguageAdapter: "" }); } for (const [k, v] of Object.entries(this.fields)) { switch (k.toUpperCase()) { case "MODID": case "MOD_ID": this.guess.modid = this.guess.modid || v; break; case "MODNAME": case "MOD_NAME": this.guess.name = this.guess.name || v; break; case "VERSION": case "MOD_VERSION": this.guess.version = this.guess.version || v; break; case "MCVERSION": this.guess.mcversion = this.guess.mcversion || v; break; } } } }; async function readForgeModManifest(mod, manifestStore = {}) { const fs = await (0, import_system.resolveFileSystem)(mod); if (!await fs.existsFile("META-INF/MANIFEST.MF")) { return void 0; } const data = await fs.readFile("META-INF/MANIFEST.MF"); const manifest = data.toString().split("\n").map((l) => l.trim()).filter((l) => l.length > 0).map((l) => l.split(":").map((s) => s.trim())).reduce((a, b) => ({ ...a, [b[0]]: b[1] }), {}); Object.assign(manifestStore, manifest); const metadata = { modid: "", name: "", authors: [], version: "", description: "", url: "" }; if (typeof manifest.TweakName === "string") { metadata.modid = manifest.TweakName; metadata.name = manifest.TweakName; } if (typeof manifest.TweakAuthor === "string") { metadata.authors = [manifest.TweakAuthor]; } if (typeof manifest.TweakVersion === "string") { metadata.version = manifest.TweakVersion; } if (manifest.TweakMetaFile) { const file = manifest.TweakMetaFile; if (await fs.existsFile(`META-INF/${file}`)) { const metadataContent = await fs.readFile(`META-INF/${file}`, "utf-8").then((s) => s.replace(/^\uFEFF/, "")).then(JSON.parse); if (metadataContent.id) { metadata.modid = metadataContent.id; } if (metadataContent.name) { metadata.name = metadataContent.name; } if (metadataContent.version) { metadata.version = metadataContent.version; } if (metadataContent.authors) { metadata.authors = metadataContent.authors; } if (metadataContent.description) { metadata.description = metadataContent.description; } if (metadataContent.url) { metadata.url = metadataContent.url; } } } return metadata; } async function readForgeModToml(mod, manifest, fileName = "mods.toml") { const fs = await (0, import_system.resolveFileSystem)(mod); const existed = await fs.existsFile("META-INF/" + fileName); const all = []; if (existed) { const str = await fs.readFile("META-INF/" + fileName, "utf-8"); const root = parse(str); if (root.mods instanceof Array) { for (const mod2 of root.mods) { const tomlMod = mod2; if (typeof tomlMod === "object" && !(tomlMod instanceof TomlDate) && !(tomlMod instanceof Array)) { const modObject = { modid: tomlMod.modId ?? "", authors: tomlMod.authors ?? root.authors ?? "", // eslint-disable-next-line no-template-curly-in-string version: tomlMod.version === "${file.jarVersion}" && typeof (manifest == null ? void 0 : manifest["Implementation-Version"]) === "string" ? manifest == null ? void 0 : manifest["Implementation-Version"] : tomlMod.version, displayName: tomlMod.displayName ?? "", description: tomlMod.description ?? "", displayURL: tomlMod.displayURL ?? root.displayURL ?? "", updateJSONURL: tomlMod.updateJSONURL ?? root.updateJSONURL ?? "", provides: tomlMod.provides ?? [], dependencies: [], logoFile: tomlMod.logoFile ?? "", credits: tomlMod.credits ?? "", loaderVersion: root.loaderVersion ?? "", modLoader: root.modLoader ?? "", issueTrackerURL: root.issueTrackerURL ?? "" }; all.push(modObject); } } } if (typeof root.dependencies === "object") { for (const mod2 of all) { const dep = root.dependencies[mod2.modid]; if (dep) { mod2.dependencies = dep; } } } } return all; } async function readForgeModAsm(mod, manifest = {}) { const fs = await (0, import_system.resolveFileSystem)(mod); let corePluginClass; if (manifest) { if (typeof manifest.FMLCorePlugin === "string") { const clazz = manifest.FMLCorePlugin.replace(/\./g, "/"); if (await fs.existsFile(clazz) || await fs.existsFile(`/${clazz}`) || await fs.existsFile(`/${clazz}.class`) || await fs.existsFile(clazz + ".class")) { corePluginClass = clazz; } } } const result = { usedForgePackage: false, usedLegacyFMLPackage: false, usedMinecraftClientPackage: false, usedMinecraftPackage: false, modAnnotations: [] }; const guessing = {}; await fs.walkFiles("/", async (f) => { if (!f.endsWith(".class")) { return; } const data = await fs.readFile(f); const visitor = new ModClassVisitor(result, guessing, corePluginClass); new import_asm.ClassReader(data).accept(visitor); }); if (result.modAnnotations.length === 0 && guessing.modid && (result.usedForgePackage || result.usedLegacyFMLPackage)) { result.modAnnotations.push({ modid: guessing.modid ?? "", name: guessing.name ?? "", version: guessing.version ?? "", dependencies: guessing.dependencies ?? "", useMetadata: guessing.useMetadata ?? false, clientSideOnly: guessing.clientSideOnly ?? false, serverSideOnly: guessing.serverSideOnly ?? false, acceptedMinecraftVersions: guessing.acceptedMinecraftVersions ?? "", acceptableRemoteVersions: guessing.acceptableRemoteVersions ?? "", acceptableSaveVersions: guessing.acceptableSaveVersions ?? "", modLanguage: guessing.modLanguage ?? "java", modLanguageAdapter: guessing.modLanguageAdapter ?? "", value: guessing.value ?? "" }); } return result; } async function readForgeModJson(mod) { const fs = await (0, import_system.resolveFileSystem)(mod); const all = []; function normalize(json) { const metadata = { modid: "", name: "", description: "", version: "", mcversion: "", url: "", updateUrl: "", updateJSON: "", authorList: [], credits: "", logoFile: "", screenshots: [], parent: "", useDependencyInformation: false, requiredMods: [], dependencies: [], dependants: [] }; metadata.modid = json.modid ?? metadata.modid; metadata.name = json.name ?? metadata.name; metadata.description = json.description ?? metadata.description; metadata.version = json.version ?? metadata.version; metadata.mcversion = json.mcversion ?? metadata.mcversion; metadata.url = json.url ?? metadata.url; metadata.updateUrl = json.updateUrl ?? metadata.updateUrl; metadata.updateJSON = json.updateJSON ?? metadata.updateJSON; metadata.authorList = json.authorList ?? metadata.authorList; metadata.credits = json.credits ?? metadata.credits; metadata.logoFile = json.logoFile ?? metadata.logoFile; metadata.screenshots = json.screenshots ?? metadata.screenshots; metadata.parent = json.parent ?? metadata.parent; metadata.useDependencyInformation = json.useDependencyInformation ?? metadata.useDependencyInformation; metadata.requiredMods = json.requiredMods ?? metadata.requiredMods; metadata.dependencies = json.dependencies ?? metadata.dependencies; metadata.dependants = json.dependants ?? metadata.dependants; return metadata; } function readJsonMetadata(json) { const modList = []; if (json instanceof Array) { modList.push(...json); } else if (json.modList instanceof Array) { modList.push(...json.modList); } else if (json.modid) { modList.push(json); } all.push(...modList.map(normalize)); } if (await fs.existsFile("mcmod.info")) { try { const json = JSON.parse((await fs.readFile("mcmod.info", "utf-8")).replace(/^\uFEFF/, "")); readJsonMetadata(json); } catch (e) { } } else if (await fs.existsFile("cccmod.info")) { try { const text = (await fs.readFile("cccmod.info", "utf-8")).replace(/^\uFEFF/, "").replace(/\n\n/g, "\\n").replace(/\n/g, ""); const json = JSON.parse(text); readJsonMetadata(json); } catch (e) { } } else if (await fs.existsFile("neimod.info")) { try { const text = (await fs.readFile("neimod.info", "utf-8")).replace(/^\uFEFF/, "").replace(/\n\n/g, "\\n").replace(/\n/g, ""); const json = JSON.parse(text); readJsonMetadata(json); } catch (e) { } } else { const files = await fs.listFiles("./"); const infoFile = files.find((f) => f.endsWith(".info")); if (infoFile) { try { const text = (await fs.readFile(infoFile, "utf-8")).replace(/^\uFEFF/, "").replace(/\n\n/g, "\\n").replace(/\n/g, ""); const json = JSON.parse(text); readJsonMetadata(json); } catch (e) { } } } return all; } async function readForgeMod(mod) { const fs = await (0, import_system.resolveFileSystem)(mod); try { const jsons = await readForgeModJson(fs); const manifest = {}; const manifestMetadata = await readForgeModManifest(fs, manifest); const tomls = await readForgeModToml(fs, manifest); const base = await readForgeModAsm(fs, manifest).catch(() => ({ usedLegacyFMLPackage: false, usedForgePackage: false, usedMinecraftPackage: false, usedMinecraftClientPackage: false, modAnnotations: [] })); if (jsons.length === 0 && (!manifestMetadata || !manifestMetadata.modid) && tomls.length === 0 && base.modAnnotations.length === 0) { throw new ForgeModParseFailedError(mod, base, manifest); } const result = { mcmodInfo: jsons, manifest, manifestMetadata: (manifestMetadata == null ? void 0 : manifestMetadata.modid) ? manifestMetadata : void 0, modsToml: tomls, ...base }; return result; } finally { if (mod !== fs) fs.close(); } } var ForgeModParseFailedError = class extends Error { constructor(mod, asm, manifest) { super("Cannot find the mod metadata in the mod!"); this.mod = mod; this.asm = asm; this.manifest = manifest; this.name = "ForgeModParseFailedError"; } }; // forgeConfig.ts var CorruptedForgeConfigError = class extends Error { constructor(reason, line) { super(`CorruptedForgeConfigError by ${reason}: ${line}`); this.reason = reason; this.line = line; } name = "CorruptedForgeConfigError"; }; var ForgeConfig; ((ForgeConfig2) => { function stringify2(config) { let content = "# Configuration file\n\n\n"; const propIndent = " "; const arrIndent = " "; Object.keys(config).forEach((cat) => { content += `${cat} { `; config[cat].properties.forEach((prop) => { if (prop.comment) { const lines = prop.comment.split("\n"); for (const l of lines) { content += `${propIndent}# ${l} `; } } if (prop.value instanceof Array) { content += `${propIndent}${prop.type}:${prop.name} < `; prop.value.forEach((v) => { content += `${arrIndent}${v} `; }); content += `${propIndent}> `; } else { content += `${propIndent}${prop.type}:${prop.name}=${prop.value} `; } content += "\n"; }); content += "}\n\n"; }); return content; } ForgeConfig2.stringify = stringify2; function parse2(body) { const lines = body.split("\n").map((s) => s.trim()).filter((s) => s.length !== 0); let category; let pendingCategory; const parseVal = (type, value) => { const map = { I: Number.parseInt, D: Number.parseFloat, S: (s) => s, B: (s) => s === "true" }; const handler = map[type]; return handler(value); }; const config = {}; let inlist = false; let comment; let last; const readProp = (type, line) => { line = line.substring(line.indexOf(":") + 1, line.length); const pair = line.split("="); if (pair.length === 0 || pair.length === 1) { let value; let name; if (line.endsWith(" <")) { value = []; name = line.substring(0, line.length - 2); inlist = true; } if (!category) { throw new CorruptedForgeConfigError("MissingCategory", line); } config[category].properties.push(last = { name, type, value, comment }); } else { inlist = false; if (!category) { throw new CorruptedForgeConfigError("MissingCategory", line); } config[category].properties.push({ name: pair[0], value: parseVal(type, pair[1]), type, comment }); } comment = void 0; }; for (const line of lines) { if (inlist) { if (!last) { throw new CorruptedForgeConfigError("CorruptedList", line); } if (line === ">") { inlist = false; } else if (line.endsWith(" >")) { last.value.push(parseVal(last.type, line.substring(0, line.length - 2))); inlist = false; } else { last.value.push(parseVal(last.type, line)); } continue; } switch (line.charAt(0)) { case "#": if (!comment) { comment = line.substring(1, line.length).trim(); } else { comment = comment.concat("\n", line.substring(1, line.length).trim()); } break; case "I": case "D": case "S": case "B": readProp(line.charAt(0), line); break; case "<": break; case "{": if (pendingCategory) { category = pendingCategory; config[category] = { comment, properties: [] }; comment = void 0; } else { throw new CorruptedForgeConfigError("MissingCategory", line); } break; case "}": category = void 0; break; default: if (!category) { if (line.endsWith("{")) { category = line.substring(0, line.length - 1).trim(); config[category] = { comment, properties: [] }; comment = void 0; } else { pendingCategory = line; } } else { throw new CorruptedForgeConfigError("Duplicated", line); } } } return config; } ForgeConfig2.parse = parse2; })(ForgeConfig || (ForgeConfig = {})); // liteloader.ts var import_system2 = require("@xmcl/system"); async function readLiteloaderMod(mod) { const fs = await (0, import_system2.resolveFileSystem)(mod); try { const text = await fs.readFile("litemod.json", "utf-8").then((s) => s.replace(/^\uFEFF/, "")).catch(() => void 0); if (!text) { throw Object.assign(new Error("Illegal input type! Expect a jar file contains litemod.json"), { mod, name: "IllegalInputType" }); } const metadata = JSON.parse(text.trim(), (key, value) => key === "revision" ? Number.parseInt(value, 10) : value); if (!metadata.version) { metadata.version = `${metadata.mcversion}:${metadata.revision || 0}`; } return metadata; } finally { if (fs !== mod) fs.close(); } } // fabric.ts var import_system3 = require("@xmcl/system"); async function readFabricMod(file) { const fs = await (0, import_system3.resolveFileSystem)(file); try { const content = await fs.readFile("fabric.mod.json", "utf-8"); return JSON.parse(content.replace(/^\uFEFF/g, "").replace(/\n/g, "")); } finally { if (file !== fs) fs.close(); } } // quilt.ts var import_system4 = require("@xmcl/system"); async function readQuiltMod(file) { const fs = await (0, import_system4.resolveFileSystem)(file); try { const content = await fs.readFile("quilt.mod.json", "utf-8"); return JSON.parse(content.replace(/^\uFEFF/g, "").replace(/\n/g, "")); } finally { if (fs !== file) fs.close(); } } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { CorruptedForgeConfigError, ForgeConfig, ForgeModParseFailedError, readFabricMod, readForgeMod, readForgeModAsm, readForgeModJson, readForgeModManifest, readForgeModToml, readLiteloaderMod, readQuiltMod }); /*! Bundled license information: smol-toml/dist/error.js: (*! * Copyright (c) Squirrel Chat et al., All rights reserved. * SPDX-License-Identifier: BSD-3-Clause * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its contributors * may be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *) smol-toml/dist/util.js: (*! * Copyright (c) Squirrel Chat et al., All rights reserved. * SPDX-License-Identifier: BSD-3-Clause * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its contributors * may be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *) smol-toml/dist/date.js: (*! * Copyright (c) Squirrel Chat et al., All rights reserved. * SPDX-License-Identifier: BSD-3-Clause * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its contributors * may be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *) smol-toml/dist/primitive.js: (*! * Copyright (c) Squirrel Chat et al., All rights reserved. * SPDX-License-Identifier: BSD-3-Clause * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its contributors * may be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *) smol-toml/dist/extract.js: (*! * Copyright (c) Squirrel Chat et al., All rights reserved. * SPDX-License-Identifier: BSD-3-Clause * * Redistribution and use in source and b