@cruncheevos/core
Version:
Parse and generate achievements and leaderboards for RetroAchievements.org
1,486 lines (1,481 loc) • 44.4 kB
JavaScript
function H(e) {
return Object.entries(e).reduce((t, r) => (t[r[1]] = r[0], t), {});
}
function ae(e) {
return e[0].toUpperCase() + e.slice(1);
}
function j(e, t = !1) {
let r = Math.abs(e).toString(16);
return t && (r = r.toUpperCase()), `${e < 0 ? "-" : ""}0x` + r;
}
function s(e, ...t) {
return e.map((r, o) => {
let i = t[o];
return typeof i == "string" ? i = `"${i}"` : typeof i == "symbol" ? i = String(i) : o >= t.length && (i = ""), r + i;
}).join("");
}
function x(e) {
return Object.prototype.toString.call(e) === "[object Object]";
}
function D(e, t = {}) {
return !(e === null || typeof e == "symbol" || typeof e == "boolean" || typeof e == "string" && e.trim().length === 0 || (e = Number(e), Number.isNaN(e) || Number.isFinite(e) === !1) || t.isInteger && Number.isInteger(e) === !1 || t.isPositive && e < 0);
}
function P(e) {
for (const t in e) {
const r = e[t];
if (x(r))
P(r);
else if (Array.isArray(r)) {
for (const o of r)
(x(o) || Array.isArray(o)) && P(o);
Object.freeze(r);
}
}
return Object.freeze(e);
}
function $(e, t) {
const r = new Error(t);
return r.cause = e, r;
}
function le(e) {
const t = [];
let r = !1, o = 0;
for (let i = 0; i < e.length; i++) {
const n = e[i];
if (t[o] = t[o] || "", r && n == "\\" && e[i + 1] == '"') {
t[o] += '"', i++;
continue;
}
if (n == '"') {
r = !r;
continue;
}
if (n == ":" && !r) {
o++;
continue;
}
t[o] += n;
}
return t;
}
function M(e) {
return e.match(/[:"]/g) ? `"${e.replace(/"/g, '\\"')}"` : e;
}
const b = {
andNormalizeId(e, t = "id") {
const r = e;
if (typeof e == "string") {
if (e.trim().length === 0)
throw new Error(`expected ${t} as unsigned integer, but got ""`);
e = Number(e);
}
if (Number.isInteger(e) === !1)
throw new Error(
`expected ${t} as unsigned integer, but got ` + s`${r}`
);
if (e < 0 || e >= Number.MAX_SAFE_INTEGER)
throw new Error(
`expected ${t} to be within the range of 0x0 .. 0xFFFFFFFF, but got ` + s`${r}`
);
return e;
},
string(e, t = "title") {
if (typeof e != "string")
throw new Error(`expected ${t} as string, but got ` + s`${e}`);
},
nonEmptyString(e, t = "title") {
if (typeof e != "string" || e.trim().length === 0)
throw new Error(
`expected ${t} as non-empty string, but got ` + s`${e}`
);
}
};
function W(e) {
return e === 0 ? "Core" : `Alt ${e}`;
}
function Ee(e) {
const t = new TextEncoder().encode(e), r = [];
for (let o = 0; o < t.length; o += 4) {
const i = [...t.slice(o, o + 4)].reverse().map((n) => n.toString(16).padStart(2, "0")).join("");
r.push(parseInt(i, 16));
}
return r;
}
function v(e) {
return function(...t) {
const r = new p();
return m.call(r, e, ...t), r;
};
}
const ce = v("");
ce.one = function(e) {
if (arguments.length > 1)
throw new Error("expected only one condition argument, but got " + arguments.length);
return new E(e);
};
ce.str = function(e, t) {
return Fe(
...Ee(e).map((r, o) => {
let i = t(
r > 16777215 ? "32bit" : r > 65535 ? "24bit" : r > 255 ? "16bit" : "8bit",
["Value", "", r]
);
return o > 0 ? i.withLast({
lvalue: { value: i.conditions[i.conditions.length - 1].lvalue.value + o * 4 }
}) : i;
})
);
};
const Pe = v("Trigger"), Te = v("ResetIf"), ke = v("PauseIf"), Ve = v("AddHits"), Be = v("SubHits"), Ue = v("Measured"), De = v("Measured%"), He = v("MeasuredIf"), We = v("ResetNextIf"), Fe = v("AndNext"), Ge = v("OrNext"), Xe = (...e) => new p().also("once", ...e), _ = /* @__PURE__ */ new WeakMap();
class p {
constructor() {
this.conditions = [], _.set(this, "");
}
trigger(...t) {
return m.call(this, "Trigger", ...t), this;
}
resetIf(...t) {
return m.call(this, "ResetIf", ...t), this;
}
pauseIf(...t) {
return m.call(this, "PauseIf", ...t), this;
}
addHits(...t) {
return m.call(this, "AddHits", ...t), this;
}
subHits(...t) {
return m.call(this, "SubHits", ...t), this;
}
measured(...t) {
return m.call(this, "Measured", ...t), this;
}
measuredPercent(...t) {
return m.call(this, "Measured%", ...t), this;
}
measuredIf(...t) {
return m.call(this, "MeasuredIf", ...t), this;
}
resetNextIf(...t) {
return m.call(this, "ResetNextIf", ...t), this;
}
andNext(...t) {
return m.call(this, "AndNext", ...t), this;
}
orNext(...t) {
return m.call(this, "OrNext", ...t), this;
}
also(...t) {
return m.call(this, "", ...t), this;
}
once(...t) {
return m.call(this, "", "once", ...t), this;
}
*[Symbol.iterator]() {
for (const t of this.conditions)
yield t;
}
map(t) {
const r = this.conditions.map(t);
return new p().also(...r);
}
withLast(t) {
return this.map((r, o, i) => o !== i.length - 1 ? r : r.with(t));
}
toString() {
return this.conditions.join("_");
}
toJSON() {
return this.toString();
}
}
const xe = /\s+/;
function m(e, ...t) {
let r = 0;
const o = t.filter((n, a) => {
if (typeof n == "string" && (n === "once" || n.startsWith("hits"))) {
if (a > 0)
throw new Error("strings 'once' and 'hits %number%' must be placed before any conditions");
return n === "once" && (r = 1), n.startsWith("hits") && (r = parseInt(n.split(xe)[1])), !1;
}
return n instanceof p && n.conditions.length === 0 ? !1 : !!n;
});
if (o.length === 0)
return;
const i = _.get(this);
if (i === "AndNext" || i === "OrNext") {
const n = this.conditions[this.conditions.length - 1];
n.flag === "" && (this.conditions[this.conditions.length - 1] = n.with({
flag: i
}));
}
for (let n = 0; n < o.length; n++) {
const a = o[n];
if (a instanceof p) {
o.splice(n, 1, ...a), n--;
continue;
}
let l = new E(a);
const u = n === o.length - 1, c = u && (e === "AndNext" || e === "OrNext");
u && r > 0 && (l = l.with({ hits: r })), e && l.flag === "" && c === !1 && (l = l.with({ flag: e })), this.conditions.push(l);
}
_.set(this, e);
}
function T(e) {
const t = e < 0 ? e + 1 + 4294967295 : e;
if (e < -2147483648)
throw new Error(
`${e} (${j(e)}) underflows into positive ${t} (${j(
t
)}), it's very unlikely you intended for that to happen`
);
return t;
}
const N = (() => {
const e = {
"": "",
PauseIf: "P",
ResetIf: "R",
ResetNextIf: "Z",
AddHits: "C",
SubHits: "D",
AndNext: "N",
OrNext: "O",
Measured: "M",
"Measured%": "G",
MeasuredIf: "Q",
Trigger: "T"
}, t = {
AddSource: "A",
SubSource: "B",
AddAddress: "I",
Remember: "K"
}, r = {
...e,
...t
}, o = H(r);
return delete o[""], {
forReading: {
toRaw: e
},
forCalc: {
toRaw: t
},
toRaw: r,
fromRaw: o
};
})(), A = (() => {
const e = {
Mem: "",
Delta: "d",
Prior: "p",
BCD: "b",
Invert: "~"
};
return {
withSize: {
toRaw: e,
array: Object.keys(e),
fromRaw: H(e)
},
withoutSize: {
array: ["Value", "Float", "Recall"]
}
};
})(), G = (() => {
const e = {
"": "",
Bit0: "M",
Bit1: "N",
Bit2: "O",
Bit3: "P",
Bit4: "Q",
Bit5: "R",
Bit6: "S",
Bit7: "T",
Lower4: "L",
Upper4: "U",
"8bit": "H",
"16bit": " ",
"24bit": "W",
"32bit": "X",
"16bitBE": "I",
"24bitBE": "J",
"32bitBE": "G",
BitCount: "K"
};
return {
toRaw: e,
fromRaw: H(e)
};
})(), k = (() => {
const e = {
Float: "F",
FloatBE: "B",
Double32: "H",
Double32BE: "I",
MBF32: "M",
MBF32LE: "L"
};
return {
toRaw: e,
fromRaw: H(e)
};
})(), w = {
forReading: ["=", "!=", "<", "<=", ">", ">="],
forCalc: ["+", "-", "*", "/", "%", "&", "^"],
isLegalForReading(e) {
return typeof e == "string" && this.forReading.includes(e);
},
isLegalForCalc(e) {
return typeof e == "string" && this.forCalc.includes(e);
},
isLegal(e) {
return this.isLegalForReading(e) || this.isLegalForCalc(e);
}
};
function ue(e) {
return N.forReading.toRaw.hasOwnProperty(e.flag);
}
function he(e) {
return N.forCalc.toRaw.hasOwnProperty(e.flag);
}
function Ie(e) {
return e.lvalue.type === "Recall" && I(e) === !1;
}
function de(e) {
return e.flag === "Measured" && I(e) === !1;
}
function ge(e) {
return e.flag === "Measured" && I(e) && w.isLegalForCalc(e.cmp);
}
function I(e) {
const t = !!e.rvalue.type, r = !!e.rvalue.size;
return !r && !t ? !1 : r ? t : A.withSize.toRaw.hasOwnProperty(e.rvalue.type) ? r : !0;
}
const O = {
value(e, t) {
if (A.withSize.array.some((o) => o === e.type)) {
if (Number.isInteger(e.value) === !1)
throw new Error(
`expected ${t} memory address as unsigned integer, but got ` + s`${e.value}`
);
if (e.value < 0 || e.value > 4294967295)
throw new Error(
`expected ${t} memory address to be within the range of 0x0 .. 0xFFFFFFFF, but got ` + s`${e.value}`
);
} else if (e.type === "Recall") {
if (e.value !== 0 && (t === "lvalue" || e.size !== void 0))
throw new Error(
`expected Recall ${t} value to be 0, but got ` + s`${e.value}`
);
} else if (e.type === "Value") {
if (Number.isInteger(e.value) === !1)
throw new Error(`expected ${t} as integer, but got ` + s`${e.value}`);
try {
var r = T(e.value);
} catch (o) {
throw $(o, `${t}: ${o.message}`);
}
if (r > 4294967295)
throw new Error(
`expected ${t} to be within the range of 0x0 .. 0xFFFFFFFF, but got ` + s`${e.value}`
);
} else if (e.type === "Float") {
if (Number.isNaN(e.value) || Number.isFinite(e.value) === !1)
throw new Error(`expected ${t} as float, but got ` + s`${e.value}`);
const o = -294967040, i = 4294967040;
if (e.value < o || e.value > i)
throw new Error(
`expected ${t} to be within the range of ${o} .. ${i}, but got ` + s`${e.value}`
);
} else if (e.type !== "")
throw new Error(`expected valid ${t} type, but got ` + s`${e.type}`);
if (A.withoutSize.array.some((o) => o === e.type) && e.size)
throw new Error(
`${t} value cannot have size specified, but got ` + s`${e.size}`
);
return e.type === "Value" ? {
...e,
value: T(e.value)
} : e.type === "Recall" && t === "rvalue" ? {
...e,
size: e.size === void 0 ? "" : e.size,
value: e.value === void 0 ? 0 : e.value
} : e;
},
calculations(e) {
if (he(e) !== !1) {
if (e.cmp) {
if (w.isLegalForCalc(e.cmp) === !1)
throw new Error(
`expected an accumulation operator (${w.forCalc.join(" ")}), but got ` + s`${e.cmp}`
);
if (I(e) === !1)
throw new Error("rvalue must be fully provided if operator is specified");
e.rvalue = O.value(e.rvalue, "rvalue");
} else if (e.cmp === "" && I(e))
throw new Error(`expected an accumulation operator (${w.forCalc.join(" ")}), but got ""`);
}
},
memoryComparisons(e) {
if (!(ue(e) === !1 || Ie(e) || de(e) || ge(e)) && (e.rvalue = O.value(e.rvalue, "rvalue"), w.isLegalForReading(e.cmp) === !1 || !e.cmp))
throw new Error(
`expected comparison operator (${w.forReading.join(" ")}), but got ` + s`${e.cmp}`
);
}
}, Z = {
enums(e) {
if (N.toRaw.hasOwnProperty(e.flag) === !1)
throw new Error(s`expected valid condition flag, but got ${e.flag}`);
for (const [t, r] of [
[e.lvalue, "lvalue"],
[e.rvalue, "rvalue"]
]) {
if (t.type === "Recall") {
if (t.size !== "" && (r === "lvalue" || t.size !== void 0))
throw new Error(
`expected Recall ${r} size to be empty string, but got ` + s`${t.size}`
);
continue;
}
if (G.toRaw.hasOwnProperty(t.size) === !1 && k.toRaw.hasOwnProperty(t.size) === !1)
throw new Error(`expected valid ${r} size, but got ` + s`${t.size}`);
}
if (e.cmp && w.isLegal(e.cmp) === !1)
throw new Error(s`expected an operator or lack of it, but got ${e.cmp}`);
},
hits(e) {
if (Number.isInteger(e.hits) === !1)
throw new Error(s`expected hits as unsigned integer, but got ${e.hits}`);
if (e.hits < 0 || e.hits > 4294967295)
throw new Error(
`expected hits to be within the range of 0x0 .. 0xFFFFFFFF, but got ${e.hits}`
);
if (he(e) && e.hits > 0)
throw new Error(`hits value cannot be specified with ${e.flag} condition flag`);
}
}, C = {
flag(e) {
const t = e.match(f.flag);
if (!t)
return ["", e];
const r = t[1];
if (N.fromRaw.hasOwnProperty(r.toUpperCase()) === !1)
throw new Error(s`expected a legal condition flag, but got ${t[0]}`);
return [N.fromRaw[r.toUpperCase()], e.slice(t[0].length)];
},
value(e) {
const t = {
type: "Mem",
size: "",
value: 0
};
let r = null, o = !0;
if ((r = e.match(f.type)) && (e = e.slice(r[0].length), t.type = A.withSize.fromRaw[r[1].toLowerCase()], o = !1), e.startsWith("{recall}"))
e = e.slice(8), t.type = "Recall";
else if (r = e.match(f.valueFloat))
e = e.slice(r[0].length), t.type = "Float", t.value = Number(r[1]);
else if (r = e.match(f.memAddress)) {
if (e = e.slice(r[0].length), r[1].toLowerCase() === "0x")
if (r = e.match(f.sizesRegular))
e = e.slice(r[0].length), t.size = G.fromRaw[r[1].toUpperCase()];
else if (e.match(f.hexValue))
t.size = "16bit";
else
throw new Error(s`expected valid size specifier, but got ${e.slice(0, 6)}`);
else if (r = e.match(f.sizesExt))
e = e.slice(r[0].length), t.size = k.fromRaw[r[1].toUpperCase()];
else
throw new Error(s`expected valid size specifier, but got ${e.slice(0, 6)}`);
if (r = e.match(f.hexValue)) {
e = e.slice(r[0].length);
const i = r[1];
t.value = +("0x" + i);
} else
throw new Error(
s`expected memory address as hex number, but got ${e.slice(0, 6)}`
);
} else if (o && (r = e.match(f.valueHex)))
e = e.slice(r[0].length), t.type = "Value", t.value = T(parseInt(r[1].replace(f.hexPrefix, "0x")));
else if (o && (r = e.match(f.valueInteger)))
e = e.slice(r[0].length), t.type = "Value", t.value = T(Number(r[1]));
else
throw new Error(s`expected proper definition, but got ${e.slice(0, 6)}`);
return [t, e];
},
cmp(e) {
const t = e.match(f.cmp);
if (!t)
throw new Error(s`expected an operator, but got ${e.slice(0, 6)}`);
return [t[1], e.slice(t[0].length)];
},
hits(e) {
const t = e.match(f.hits);
if (!t)
throw new Error(s`expected hits definition, but got ${e}`);
const r = t[1];
if (D(r, { isInteger: !0, isPositive: !0 })) {
const o = Number(r);
if (o > 4294967295)
throw new Error(
`expected hits to be within the range of 0x0 .. 0xFFFFFFFF, but got ${r}`
);
return [o, e.slice(t[0].length)];
} else
throw new Error(s`expected hits as unsigned integer, but got ${r}`);
}
};
function Se(e) {
e = e.trim();
const t = {
flag: "",
lvalue: {
type: "",
size: "",
value: 0
},
cmp: "",
rvalue: {
type: "",
size: "",
value: 0
},
hits: 0
};
[t.flag, e] = C.flag(e);
try {
[t.lvalue, e] = C.value(e);
} catch (r) {
throw $(r, `lvalue: ${r.message}`);
}
if (e) {
[t.cmp, e] = C.cmp(e);
const r = ue(t), o = w.isLegalForReading(t.cmp);
if ((o || w.isLegalForCalc(t.cmp)) === !1)
throw r ? new Error(
`expected comparison operator (${w.forReading.join(" ")}), but got ` + s`${t.cmp}`
) : new Error(
`expected calculation operator (${w.forCalc.join(" ")}), but got ` + s`${t.cmp}`
);
try {
[t.rvalue, e] = C.value(e);
} catch (n) {
throw $(n, `rvalue: ${n.message}`);
}
e && ([t.hits, e] = C.hits(e)), r === !1 && o && (t.cmp = "", t.rvalue = {
type: "",
size: "",
value: 0
});
}
return t;
}
function Y(e) {
if (e.type === "Value") {
const t = e.value - 4294967295 - 1;
return t >= -4096 && t < 0 ? t.toString() : e.value >= 1e5 ? j(e.value) : e.value.toString();
} else if (e.type)
return e.type !== "Float" || e.value >= 1e5 ? j(e.value) : e.value.toString();
}
function Re(e) {
const t = e[4] === void 0 && e[5] === void 0 && e[6] === void 0 && e[7] === void 0;
return {
flag: e[0],
lvalue: {
type: e[1],
size: e[2],
value: e[3]
},
cmp: t ? "" : e[4],
rvalue: {
type: t ? "" : e[5],
size: t ? "" : e[6],
value: t ? 0 : e[7]
},
hits: e[8] === void 0 ? 0 : e[8]
};
}
function ee(e) {
let t = "";
return e.type === "Value" ? t += e.value : e.type === "Float" ? (t += "f", t += e.value, Number.isInteger(e.value) && (t += ".0")) : e.type === "Recall" ? t += "{recall}" : (t += A.withSize.toRaw[e.type], k.toRaw.hasOwnProperty(e.size) ? (t += "f", t += k.toRaw[e.size]) : (t += "0x", t += G.toRaw[e.size]), t += e.value.toString(16)), t;
}
function fe(e) {
e.forEach((t, r) => {
const o = W(r);
t.forEach((i, n) => {
if (de(i))
throw new Error(
`${o}, condition ${n + 1}: cannot have Measured condition without rvalue specified`
);
if (ge(i))
throw new Error(
`${o}, condition ${n + 1}: expected comparison operator (${w.forReading.join(" ")}), but got ` + s`${i.cmp}`
);
});
});
}
function te(e) {
return Array.isArray(e) ? Object.assign(
{},
typeof e[0] == "string" && { type: e[0] },
typeof e[1] == "string" && { size: e[1] },
typeof e[2] == "number" && { value: e[2] }
) : e;
}
class E {
constructor(t) {
if (t instanceof E)
return t;
if (typeof t == "string")
Object.assign(this, Se(t));
else if (Array.isArray(t))
Object.assign(this, Re(t));
else if (x(t))
this.flag = t.flag, this.cmp = t.cmp, this.rvalue = { ...t.rvalue }, this.lvalue = { ...t.lvalue }, this.hits = t.hits;
else
throw new Error(
s`condition data must be an array, object or string with condition code, but got ${t}`
);
Z.enums(this), this.lvalue = O.value(this.lvalue, "lvalue"), O.memoryComparisons(this), O.calculations(this), Z.hits(this), P(this);
}
with(t) {
return new E({
...this,
...t,
lvalue: { ...this.lvalue, ...te(t.lvalue) },
rvalue: { ...this.rvalue, ...te(t.rvalue) }
});
}
toString() {
let t = "";
return this.flag !== "" && (t += N.toRaw[this.flag] + ":"), t += ee(this.lvalue), I(this) && (t += this.cmp, t += ee(this.rvalue), this.hits && (t += "." + this.hits + ".")), t;
}
toArray() {
return [
this.flag,
this.lvalue.type,
this.lvalue.size,
this.lvalue.value,
this.cmp,
this.rvalue.type,
this.rvalue.size,
this.rvalue.value,
this.hits
];
}
toArrayPretty() {
const t = this.rvalue.type === "Recall", r = I(this);
return [
this.flag,
this.lvalue.type,
this.lvalue.size,
Y(this.lvalue),
this.cmp,
r ? this.rvalue.type : "",
t ? "" : r ? this.rvalue.size : "",
t ? "" : r ? Y(this.rvalue) : "",
this.hits > 0 ? this.hits.toString() : ""
];
}
}
function L(e, t = {}) {
const { considerLegacyValueFormat: r = !1 } = t, o = e.split(r ? "$" : new RegExp("(?<!0x)S")).map((n) => n.trim().length > 0 ? n.split("_") : []), i = r && o.every(
(n) => n.every((a) => a.match(f.flag) === null)
);
return o.map(
(n, a) => n.map((l, u) => {
if (i) {
l.match(f.legacyTrailingFloat) && (l = l.replace(
f.legacyTrailingFloat,
(y) => "f" + y
));
const c = l.match(f.legacyValue);
if (c) {
const y = c[1] || "+";
let d = Number(c[2]);
d < 0 && (d = T(d)), d > 2147483647 && (d = 2147483647), y === "-" && d > 0 && (d = -d), l = d.toString();
}
l = (u === n.length - 1 ? "M" : "A") + ":" + l;
}
try {
return new E(l);
} catch (c) {
const h = W(a);
throw $(c, `${h}, condition ${u + 1}: ${c.message}`);
}
})
);
}
function Q(e, t = {}) {
const r = [];
if (typeof e == "string")
return L(e, t);
if (Array.isArray(e)) {
const o = [];
for (let i = 0; i < e.length; i++) {
const n = e[i];
try {
n instanceof p ? o.push(...n) : o.push(new E(n));
} catch (a) {
throw $(a, `conditions[${i}]: ${a.message}`);
}
}
r.push(o);
} else if (e instanceof p)
r.push([...e]);
else if (x(e)) {
let o = !1;
const i = [];
for (const a in e) {
const l = a.match(/^(?:core|alt([1-9]\d*))$/);
if (l)
l[0] === "core" ? o = !0 : l[1] && i.push(Number(l[1]));
else
throw new Error(`conditions.${a}: group name must be "core" or "alt1", "alt2"...`);
}
if (!o)
throw new Error('conditions: expected "core" group');
i.sort((a, l) => a - l).forEach((a, l) => {
if (a !== l + 1)
throw new Error(
`conditions: expected "alt${l + 1}" group, but got "alt${a}", make sure there are no gaps`
);
});
const n = ["core", ...i.map((a) => `alt${a}`)];
for (const a of n) {
const l = e[a];
if (typeof l == "string")
try {
r.push(...L(l, t));
} catch (u) {
throw $(u, `conditions.${a}: ${u.message}`);
}
else if (l instanceof p)
r.push([...l]);
else if (Array.isArray(l)) {
const u = [];
for (let c = 0; c < l.length; c++)
try {
const h = l[c];
h instanceof p ? u.push(...h) : u.push(new E(h));
} catch (h) {
throw $(h, `conditions.${a}[${c}]: ${h.message}`);
}
r.push(u);
} else
throw new Error(
`conditions.${a}: expected an array of conditions or string, but got ` + s`${l}`
);
}
} else
throw new Error(
s`expected conditions as object, array of arrays or string, but got ${e}`
);
return r;
}
const f = (() => {
const e = [...w.forCalc, ...w.forReading].sort((t, r) => r.length - t.length).map(
(t) => t.split("").map((r) => `\\${r}`).join("")
);
return {
cmp: new RegExp(`^(${e.join("|")})`),
flag: /^(.*?):/i,
hits: /^\.(.*)\./,
hexPrefix: /h/i,
hexValue: /^([\dabcdef]+)/i,
sizesRegular: new RegExp(
"^(" + Object.values(G.toRaw).filter(Boolean).join("|") + ")",
"i"
),
sizesExt: new RegExp("^(" + Object.values(k.toRaw).filter(Boolean).join("|") + ")", "i"),
memAddress: /^(0x|f)/i,
type: new RegExp(
"^(" + Object.values(A.withSize.toRaw).filter(Boolean).join("|") + ")",
"i"
),
valueHex: /^(-?h[\dabcdef]+)/i,
valueInteger: /^(-?\d+)/,
valueFloat: /^f(-?\d+\.\d+)/i,
legacyTrailingFloat: /(-?\d+\.\d+)$/,
legacyValue: /^v([-+])?([-+]?\d+)$/i
};
})(), pe = ["missable", "progression", "win_condition"], Ne = /* @__PURE__ */ new Set(["", ...pe]), R = {
points(e) {
if (D(e, { isInteger: !0, isPositive: !0 }) === !1)
throw new Error(
"expected points value to be a positive integer, but got " + s`${e}`
);
},
measuredConditionsMixing(e) {
const t = [], r = [];
if (e.forEach((o, i) => {
const n = W(i);
o.forEach((a, l) => {
a.flag === "Measured" && t.push([n, l]), a.flag === "Measured%" && r.push([n, l]);
});
}), r.length > 0 && t.length > 0) {
const o = t[0], i = r[0];
throw new Error(
`${o[0]}, condition ${o[1] + 1}: Measured conflicts with ${i[0]}, condition ${i[1] + 1} Measured%, make sure you exclusively use Measured or Measured%`
);
}
},
andNormalizeAuthor(e) {
if (e == null && (e = ""), typeof e != "string")
throw new Error(s`expected author as string, but got ${e}`);
return e || "cruncheevos";
},
andNormalizeAchievementType(e) {
if (e = e === void 0 ? "" : e, Ne.has(e) === !1)
throw new Error(
`expected type to be one of: [${[...pe].join(", ")}], or empty string, or undefined, but got ` + s`${e}`
);
return e;
},
andNormalizeBadge(e) {
const t = s`expected badge as unsigned integer or filepath starting with local\\\\ and going strictly down, but got ${e}`;
if (e == null)
return "00000";
if (D(e, { isInteger: !0 })) {
const r = Number(e);
if (r < 0 || r > 4294967295)
throw new Error(
`expected badge id to be within the range of 0x0 .. 0xFFFFFFFF, but got ${e}`
);
return e.toString().padStart(5, "0");
} else if (typeof e == "string") {
const r = e.split("\\\\");
if (r.length < 2 || r[0] !== "local")
throw new Error(t);
for (const i of r)
if (/^\.+$/.test(i))
throw new Error(`encountered ${i} within ${e}, path can only go down`);
const o = r[r.length - 1];
if (/^.+\.(png|jpe?g|gif)$/.test(o) === !1)
throw new Error(`expected badge filename to be *.(png|jpg|jpeg|gif) but got "${o}"`);
return e;
} else
throw new Error(t);
}
}, Ae = /^\s+$/;
function Le(e) {
const t = le(e);
if (t.length !== 13 && t.length !== 14)
throw new Error(
"got an unexpected amount of data when parsing raw achievement string, either there's not enough data or it's not escaped/quoted correctly"
);
let r = t[6];
r.match(Ae) && (r = "");
const [o, i] = t[0].split("|");
return {
id: b.andNormalizeId(o),
setId: i === void 0 ? i : b.andNormalizeId(i, "setId"),
title: t[2],
description: t[3],
type: r,
author: t[7],
points: Number(t[8]),
badge: R.andNormalizeBadge(t[13] || ""),
conditions: L(t[1])
};
}
const re = Symbol();
class V {
constructor(t) {
const r = t instanceof V;
if (typeof t == "string")
Object.assign(this, Le(t));
else if (x(t) && r === !1) {
let o = t.conditions;
t[re] || (o = Q(t.conditions)), Object.assign(this, {
id: b.andNormalizeId(t.id),
setId: t.setId === void 0 ? t.setId : b.andNormalizeId(t.setId, "setId"),
title: t.title,
description: t.description,
author: t.author,
points: t.points,
type: t.type,
badge: R.andNormalizeBadge(t.badge),
conditions: o
});
} else
throw new Error(
"achievement data must be an object or string with achievement code, but got " + (r ? "another Achievement instance" : s`${t}`)
);
b.string(this.title, "title"), b.string(this.description, "description"), this.author = R.andNormalizeAuthor(this.author), R.points(this.points), this.type = R.andNormalizeAchievementType(this.type), fe(this.conditions), R.measuredConditionsMixing(this.conditions), P(this);
}
with(t) {
return new V({
...this,
...t,
[re]: t.hasOwnProperty("conditions") === !1
});
}
toString(t = "achievement") {
const r = this.conditions.map((o) => o.map((i) => i.toString()).join("_")).join("S");
if (t === "conditions")
return r;
if (t === "achievement" || t === "achievement-legacy") {
let o = "";
return o += this.id, t === "achievement" && this.setId !== void 0 && (o += "|" + this.setId), o += ":", o += `"${r}":`, o += M(this.title) + ":", o += M(this.description), o += ":::", o += this.type + ":", o += M(this.author) + ":", o += this.points, o += ":::::", o += this.badge.startsWith("local\\\\") ? `"${this.badge}"` : this.badge, o;
} else
throw new Error(s`unexpected achievement data toString request: ${t}`);
}
}
const oe = /* @__PURE__ */ new Set(["start", "cancel", "submit", "value"]), ie = /* @__PURE__ */ new Set([
"SCORE",
"TIME",
"FRAMES",
"MILLISECS",
"SECS",
"TIMESECS",
"MINUTES",
"SECS_AS_MINS",
"VALUE",
"UNSIGNED",
"TENS",
"HUNDREDS",
"THOUSANDS",
"FIXED1",
"FIXED2",
"FIXED3"
]), F = {
andNormalizeLeaderboardId(e) {
if (typeof e == "string")
if (e.startsWith("L"))
e = e.slice(1);
else
throw new Error(`expected id to start with L, but got "${e}"`);
return b.andNormalizeId(e);
},
andNormalizeConditions(e) {
let t;
if (typeof e == "string")
t = ze(e);
else if (x(e))
t = Object.keys(e).reduce((r, o) => {
if (oe.has(o) === !1)
throw new Error(
`expected leaderboard condition group name to be one of: [${[
...oe
].join(", ")}], but got ` + s`${o}`
);
return r[o] = Q(e[o], {
considerLegacyValueFormat: o === "value"
}), r;
}, {});
else
throw new Error(s`expected conditions to be an object, but got ${e}`);
for (const r of t.value)
if (r.some((i) => i.flag === "Measured") === !1)
for (let i = 0; i < r.length; i++) {
const n = r[i];
if (n.flag === "") {
r[i] = n.with({ flag: "Measured" });
break;
}
}
return t;
},
leaderboardType(e) {
if (ie.has(e) === !1)
throw new Error(
`expected type to be one of: [${[...ie].join(", ")}], but got ` + s`${e}`
);
},
measuredConditions(e) {
for (const t of ["start", "cancel", "submit", "value"])
try {
t !== "value" && fe(e[t]), F.lackOfMeasuredPercent(e[t]);
} catch (r) {
throw $(r, `${ae(t)}, ` + r.message);
}
},
lackOfMeasuredPercent(e) {
e.forEach((t, r) => {
t.forEach((o, i) => {
if (o.flag === "Measured%") {
const n = W(r);
throw new Error(
`${n}, condition ${i + 1}: Measured% conditions are not allowed in leaderboards`
);
}
});
});
},
andNormalizeLowerIsBetter(e) {
if (typeof e == "string")
return e.length > 0 && e !== "0";
if (typeof e == "boolean")
return e;
throw new Error(
s`expected lowerIsBetter as boolean or string, but got ${e}`
);
}
};
function U(e, t = "S") {
return e.map((r) => r.map((o) => o.toString()).join("_")).join(t);
}
function Ce(e) {
const t = le(e);
if (t.length !== 9)
throw new Error(
"got an unexpected amount of data when parsing raw leaderboard string, either there's not enough data or it's not escaped/quoted correctly"
);
const [r, o] = t[0].split("|");
return {
id: F.andNormalizeLeaderboardId(r),
setId: o === void 0 ? o : b.andNormalizeId(o, "setId"),
conditions: F.andNormalizeConditions({
start: t[1],
cancel: t[2],
submit: t[3],
value: t[4]
}),
type: t[5],
title: t[6],
description: t[7],
lowerIsBetter: F.andNormalizeLowerIsBetter(t[8])
};
}
const ne = Symbol();
class B {
constructor(t) {
const r = t instanceof B;
if (typeof t == "string")
Object.assign(this, Ce(t));
else if (x(t) && r === !1) {
let o = t.conditions;
t[ne] || (o = F.andNormalizeConditions(t.conditions)), Object.assign(this, {
...t,
id: F.andNormalizeLeaderboardId(t.id),
setId: t.setId === void 0 ? t.setId : b.andNormalizeId(t.setId, "setId"),
title: t.title,
description: t.description,
type: t.type,
lowerIsBetter: F.andNormalizeLowerIsBetter(t.lowerIsBetter),
conditions: o
});
} else
throw new Error(
"leaderboard data must be an object or string with leaderboard code, but got " + (r ? "another Leaderboard instance" : s`${t}`)
);
b.string(this.title, "title"), b.string(this.description, "description"), F.leaderboardType(this.type), F.measuredConditions(this.conditions), P(this);
}
with(t) {
return new B({
...this,
...t,
[ne]: t.hasOwnProperty("conditions") === !1
});
}
toString(t = "leaderboard") {
const r = [
U(this.conditions.start),
U(this.conditions.cancel),
U(this.conditions.submit),
U(this.conditions.value, "$")
].map((o) => `"${o}"`);
if (t === "conditions")
return r.join(":");
if (t === "leaderboard" || t === "leaderboard-legacy") {
let o = "";
return o += "L" + this.id, t === "leaderboard" && this.setId !== void 0 && (o += "|" + this.setId), o += ":", o += r.join(":") + ":", o += this.type + ":", o += M(this.title) + ":", o += M(this.description) + ":", o += Number(this.lowerIsBetter), o;
} else
throw new Error(s`unexpected leaderboard data toString request: ${t}`);
}
}
function ze(e) {
const t = {
start: null,
cancel: null,
submit: null,
value: null
};
let r = null;
for (const [o, i] of [
["STA", "start"],
["CAN", "cancel"],
["SUB", "submit"],
["VAL", "value"]
]) {
const n = o === "VAL";
if (r = e.match(new RegExp(n ? /VAL:(.+)/ : `${o}:(.+?)::`))) {
e = e.slice(r[0].length);
try {
t[i] = Q(r[1], {
considerLegacyValueFormat: n
});
} catch (a) {
throw $(a, `${ae(i)}, ${a.message}`);
}
} else
throw new Error(`expected ${o}:<conditions>::, but got ${e.slice(0, 6)}`);
}
return t;
}
const q = /* @__PURE__ */ new WeakMap();
function* se() {
for (const e in this)
yield this[e];
}
class qe {
constructor(t) {
this.achievements = {
[Symbol.iterator]: se
}, this.leaderboards = {
[Symbol.iterator]: se
};
const { gameId: r, id: o, title: i } = t;
this.gameId = b.andNormalizeId(r, "gameId"), o !== void 0 && (this.id = b.andNormalizeId(o, "id")), b.nonEmptyString(i, "achievement set title"), this.title = i, q.set(this, {
achievementIdCounter: 111000001,
leaderboardIdCounter: 111000001
});
}
addAchievement(t) {
const r = q.get(this);
let o = t instanceof V ? t : new V(
typeof t == "string" ? t : {
...t,
id: t.id || r.achievementIdCounter
}
);
this.id !== o.setId && (o = o.with({ setId: this.id }));
const { id: i } = o;
if (this.achievements[i])
throw new Error(`achievement with id ${i}: "${this.achievements[i].title}", already exists`);
return this.achievements[i] = o, o.id >= r.achievementIdCounter && (r.achievementIdCounter = Math.max(r.achievementIdCounter + 1, o.id + 1)), this;
}
addLeaderboard(t) {
const r = q.get(this);
let o = t instanceof B ? t : new B(
typeof t == "string" ? t : {
...t,
id: t.id || r.leaderboardIdCounter
}
);
this.id !== o.setId && (o = o.with({ setId: this.id }));
const { id: i } = o;
if (this.leaderboards[i])
throw new Error(`leaderboard with id ${i}: "${this.leaderboards[i].title}", already exists`);
return this.leaderboards[i] = o, o.id >= r.leaderboardIdCounter && (r.leaderboardIdCounter = Math.max(r.leaderboardIdCounter + 1, o.id + 1)), this;
}
*[Symbol.iterator]() {
for (const t of this.achievements)
yield t;
for (const t of this.leaderboards)
yield t;
}
toString(t = "set") {
let r = "";
return r += `1.0
`, r += this.title + `
`, Object.keys(this.achievements).sort((o, i) => Number(o) - Number(i)).forEach((o) => {
r += this.achievements[o].toString(
t === "set-legacy" ? "achievement-legacy" : "achievement"
) + `
`;
}), Object.keys(this.leaderboards).sort((o, i) => Number(o) - Number(i)).forEach((o) => {
r += this.leaderboards[o].toString(
t === "set-legacy" ? "leaderboard-legacy" : "leaderboard"
) + `
`;
}), r;
}
}
const me = Symbol("isRichLookupOrFormat"), Me = /* @__PURE__ */ new Set([
"VALUE",
"SCORE",
"POINTS",
"TIME",
"FRAMES",
"MILLISECS",
"SECS",
"MINUTES",
"SECS_AS_MINS",
"FLOAT1",
"FLOAT2",
"FLOAT3",
"FLOAT4",
"FLOAT5",
"FLOAT6"
]);
function Oe(e) {
const { numbers: t, notNumbers: r } = e.reduce(
(a, l) => {
const u = Number(l);
return Number.isNaN(u) ? a.notNumbers.push(l) : a.numbers.push(u), a;
},
{ numbers: [], notNumbers: [] }
);
if (t.length === 0)
return { formattedRanges: "", notNumbers: r };
const o = [];
let i = t[0], n = t[0];
for (let a = 1; a < t.length; a++)
t[a] - n === 1 || (o.push([i, n]), i = t[a]), n = t[a];
return o.push([i, n]), {
notNumbers: r,
formattedRanges: o.map(([a, l]) => a === l ? `${a}` : `${a}-${l}`).join(",")
};
}
function je(e) {
return e && typeof e != "string" && e[me];
}
function we(e, ...t) {
return e.map((r, o) => {
let i = o === e.length - 1 ? "" : t[o];
return je(i) && (i = i.at()), `${r}${i}`;
}).join("");
}
function z(e) {
const t = e instanceof p ? e.conditions : Array.isArray(e) ? e : [e];
return t[t.length - 1].flag !== "Measured";
}
function K(e) {
return (e instanceof p ? e.conditions : [e]).length === 1 ? e.toString().replace("M:", "").replace(" ", "") : e.toString();
}
function be(e, t) {
return `?${e}?${t}`;
}
function g(e) {
const { name: t, type: r } = e;
return {
name: t,
type: r,
toString() {
return `Format:${t}
FormatType=${r}`;
},
at(o) {
if (typeof o == "string") {
try {
L(o, {
considerLegacyValueFormat: !0
});
} catch (i) {
throw $(
i,
s`Rich Presence Format ${t} got invalid string input: ${i.message}`
);
}
return `@${t}(${o})`;
}
if (o instanceof E || o instanceof p) {
if (z(o))
throw new Error(
s`Rich Presence Format ${t} got invalid input: must have at least one condition with Measured flag, but got ${o.toString()}`
);
return `@${t}(${K(o)})`;
}
throw new Error(s`Rich Presence Format ${t} got invalid input: ${o}`);
}
};
}
function ye(e) {
const { name: t, type: r } = e;
if (typeof t != "string")
throw new Error(
s`Rich Presence Format expected to have a name as string, but got ${t}`
);
if (Me.has(r) === !1)
throw new Error(s`Rich Presence Format ${t} got unexpected type: ${r}`);
return g(e);
}
function $e(e) {
const { name: t, values: r, defaultAt: o, compressRanges: i = !0 } = e;
let n = "";
if (typeof t != "string")
throw new Error(
s`Rich Presence Lookup expected to have a name as string, but got ${t}`
);
const a = Object.entries(r || {});
if (a.length === 0)
throw new Error(
s`Rich Presence Lookup ${t} must define at least one key-value pair`
);
for (const [c, h] of a)
if (c !== "*" && D(c, { isInteger: !0, isPositive: !0 }) === !1)
throw new Error(
s`Rich Presence Lookup ${t} got invalid key-value pair ${c}: ${h}, value must be positive integer or "*"`
);
if (o !== void 0)
if (typeof o == "string") {
try {
var l = L(o, {
considerLegacyValueFormat: !0
});
} catch (c) {
throw $(
c,
s`Rich Presence Lookup ${t} got invalid defaultAt: ${c.message}`
);
}
if (z(l[0]))
throw new Error(
s`Rich Presence Lookup ${t} got invalid input: must have at least one condition with Measured flag, but got ${o}`
);
n = `@${t}(${o})`;
} else if (o instanceof E || o instanceof p) {
if (z(o))
throw new Error(
s`Rich Presence Lookup ${t} got invalid defaultAt: must have at least one condition with Measured flag, but got ${o}`
);
n = `@${t}(${K(o)})`;
} else
throw new Error(
s`Rich Presence Lookup ${t} defaultAt expected to be a string, Condition or ConditionBuilder, but got ${o}`
);
let u = r;
if (i) {
const c = a.reduce((h, [y, d]) => (h[d] || (h[d] = []), h[d].push(y), h), {});
u = Object.entries(c).reduce((h, [y, d]) => {
const { notNumbers: X, formattedRanges: J } = Oe(d);
J && (h[J] = y);
for (const ve of X)
h[ve] = y;
return h;
}, {});
}
return {
[me]: !0,
name: t,
toString(c = "dec") {
let h = `Lookup:${t}`;
for (const y in u) {
let d = y;
d !== "*" && c.startsWith("hex") && (d = d.replace(
/\d+/g,
(X) => j(Number(X), c !== "hex-lowercase")
)), h += `
${d}=${u[y]}`;
}
return h;
},
at: function(h) {
if (h === void 0) {
if (n)
return n;
throw new Error(`Rich Presence Lookup ${t} got no input, neither defaultAt specified`);
}
if (typeof h == "string") {
try {
var y = L(h, {
considerLegacyValueFormat: !0
});
} catch (d) {
throw $(
d,
s`Rich Presence Lookup ${t} got error when parsing input: ${d.message}`
);
}
if (z(y[0]))
throw new Error(
s`Rich Presence Lookup ${t} got invalid input: must have at least one condition with Measured flag, but got ${h}`
);
return `@${t}(${h})`;
} else if (h instanceof E || h instanceof p) {
if (z(h))
throw new Error(
s`Rich Presence Lookup ${t} got invalid input: must have at least one condition with Measured flag, but got ${h}`
);
return `@${t}(${K(h)})`;
} else
throw new Error(s`Rich Presence Lookup ${t} got invalid input: ${h}`);
}
};
}
const S = (e) => {
const { format: t = {}, lookup: r = {}, lookupDefaultParameters: o = {} } = e, i = Object.keys(t).reduce((u, c) => (u[c] = ye({
name: c,
type: t[c]
}), u), {}), n = Object.keys(r).reduce((u, c) => (u[c] = $e({
compressRanges: o.compressRanges,
...r[c],
name: c
}), u), {}), a = e.displays({
lookup: n,
format: i,
tag: we,
macro: S.macro
});
if (a.length === 0)
throw new Error("Rich Presence displays must return at least one display string");
const l = a.map((u, c) => {
if (typeof u == "string")
return u;
if (Array.isArray(u)) {
if (u.length !== 2)
throw new Error(
`Rich Presence displays[${c}] must be either a string or an array with two strings`
);
return be(u[0], u[1]);
}
throw new Error(
`Rich Presence displays[${c}] must be either a string or an array with two strings`
);
});
return {
lookup: n,
format: i,
displayStrings: l,
macro: S.macro,
toString() {
return [
Object.values(i).join(`
`),
Object.values(n).map((u) => u.toString(r[u.name].keyFormat || o.keyFormat)).join(`
`)
].join(`
`).trim() + `
Display:
` + l.join(`
`);
}
};
};
S.display = be;
S.format = ye;
S.lookup = $e;
S.tag = we;
S.macro = {
Number: g({ name: "Number", type: "VALUE" }),
Unsigned: g({ name: "Unsigned", type: "UNSIGNED" }),
Score: g({ name: "Score", type: "SCORE" }),
Centiseconds: g({ name: "Centiseconds", type: "MILLISECS" }),
Seconds: g({ name: "Seconds", type: "SECS" }),
Minutes: g({ name: "Minutes", type: "MINUTES" }),
Fixed1: g({ name: "Fixed1", type: "FIXED1" }),
Fixed2: g({ name: "Fixed2", type: "FIXED2" }),
Fixed3: g({ name: "Fixed3", type: "FIXED3" }),
Float1: g({ name: "Float1", type: "FLOAT1" }),
Float2: g({ name: "Float2", type: "FLOAT2" }),
Float3: g({ name: "Float3", type: "FLOAT3" }),
Float4: g({ name: "Float4", type: "FLOAT4" }),
Float5: g({ name: "Float5", type: "FLOAT5" }),
Float6: g({ name: "Float6", type: "FLOAT6" }),
ASCIIChar: g({ name: "ASCIIChar", type: "ASCIIChar" }),
UnicodeChar: g({ name: "UnicodeChar", type: "UnicodeChar" })
};
export {
V as Achievement,
qe as AchievementSet,
E as Condition,
p as ConditionBuilder,
B as Leaderboard,
S as RichPresence,
Ve as addHits,
Fe as andNext,
ce as define,
Ue as measured,
He as measuredIf,
De as measuredPercent,
Xe as once,
Ge as orNext,
ke as pauseIf,
Te as resetIf,
We as resetNextIf,
Ee as stringToNumberLE,
Be as subHits,
Pe as trigger
};