@cruncheevos/core
Version:
Parse and generate achievements and leaderboards for RetroAchievements.org
18 lines (13 loc) • 33.3 kB
JavaScript
(function(d,S){typeof exports=="object"&&typeof module<"u"?S(exports):typeof define=="function"&&define.amd?define(["exports"],S):(d=typeof globalThis<"u"?globalThis:d||self,S(d.cruncheevos={}))})(this,(function(d){"use strict";function S(e){return Object.entries(e).reduce((t,r)=>(t[r[1]]=r[0],t),{})}function Y(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,i)=>{let o=t[i];return typeof o=="string"?o=`"${o}"`:typeof o=="symbol"?o=String(o):i>=t.length&&(o=""),r+o}).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 i of r)(x(i)||Array.isArray(i))&&P(i);Object.freeze(r)}}return Object.freeze(e)}function v(e,t){const r=new Error(t);return r.cause=e,r}function ee(e){const t=[];let r=!1,i=0;for(let o=0;o<e.length;o++){const n=e[o];if(t[i]=t[i]||"",r&&n=="\\"&&e[o+1]=='"'){t[i]+='"',o++;continue}if(n=='"'){r=!r;continue}if(n==":"&&!r){i++;continue}t[i]+=n}return t}function T(e){return e.match(/[:"]/g)?`"${e.replace(/"/g,'\\"')}"`:e}const w={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 te(e){const t=new TextEncoder().encode(e),r=[];for(let i=0;i<t.length;i+=4){const o=[...t.slice(i,i+4)].reverse().map(n=>n.toString(16).padStart(2,"0")).join("");r.push(parseInt(o,16))}return r}function E(e){return function(...t){const r=new m;return b.call(r,e,...t),r}}const q=E("");q.one=function(e){if(arguments.length>1)throw new Error("expected only one condition argument, but got "+arguments.length);return new F(e)},q.str=function(e,t){return re(...te(e).map((r,i)=>{let o=t(r>16777215?"32bit":r>65535?"24bit":r>255?"16bit":"8bit",["Value","",r]);return i>0?o.withLast({lvalue:{value:o.conditions[o.conditions.length-1].lvalue.value+i*4}}):o}))};const Ie=E("Trigger"),Se=E("ResetIf"),Re=E("PauseIf"),xe=E("AddHits"),Ne=E("SubHits"),Ae=E("Measured"),Le=E("Measured%"),Ce=E("MeasuredIf"),ze=E("ResetNextIf"),re=E("AndNext"),Me=E("OrNext"),Oe=(...e)=>new m().also("once",...e),_=new WeakMap;class m{constructor(){this.conditions=[],_.set(this,"")}trigger(...t){return b.call(this,"Trigger",...t),this}resetIf(...t){return b.call(this,"ResetIf",...t),this}pauseIf(...t){return b.call(this,"PauseIf",...t),this}addHits(...t){return b.call(this,"AddHits",...t),this}subHits(...t){return b.call(this,"SubHits",...t),this}measured(...t){return b.call(this,"Measured",...t),this}measuredPercent(...t){return b.call(this,"Measured%",...t),this}measuredIf(...t){return b.call(this,"MeasuredIf",...t),this}resetNextIf(...t){return b.call(this,"ResetNextIf",...t),this}andNext(...t){return b.call(this,"AndNext",...t),this}orNext(...t){return b.call(this,"OrNext",...t),this}also(...t){return b.call(this,"",...t),this}once(...t){return b.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 m().also(...r)}withLast(t){return this.map((r,i,o)=>i!==o.length-1?r:r.with(t))}toString(){return this.conditions.join("_")}toJSON(){return this.toString()}}const je=/\s+/;function b(e,...t){let r=0;const i=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(je)[1])),!1}return n instanceof m&&n.conditions.length===0?!1:!!n});if(i.length===0)return;const o=_.get(this);if(o==="AndNext"||o==="OrNext"){const n=this.conditions[this.conditions.length-1];n.flag===""&&(this.conditions[this.conditions.length-1]=n.with({flag:o}))}for(let n=0;n<i.length;n++){const a=i[n];if(a instanceof m){i.splice(n,1,...a),n--;continue}let l=new F(a);const u=n===i.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 k(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 A=(()=>{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},i=S(r);return delete i[""],{forReading:{toRaw:e},forCalc:{toRaw:t},toRaw:r,fromRaw:i}})(),L=(()=>{const e={Mem:"",Delta:"d",Prior:"p",BCD:"b",Invert:"~"};return{withSize:{toRaw:e,array:Object.keys(e),fromRaw:S(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:S(e)}})(),V=(()=>{const e={Float:"F",FloatBE:"B",Double32:"H",Double32BE:"I",MBF32:"M",MBF32LE:"L"};return{toRaw:e,fromRaw:S(e)}})(),y={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 ie(e){return A.forReading.toRaw.hasOwnProperty(e.flag)}function oe(e){return A.forCalc.toRaw.hasOwnProperty(e.flag)}function Pe(e){return e.lvalue.type==="Recall"&&N(e)===!1}function ne(e){return e.flag==="Measured"&&N(e)===!1}function se(e){return e.flag==="Measured"&&N(e)&&y.isLegalForCalc(e.cmp)}function N(e){const t=!!e.rvalue.type,r=!!e.rvalue.size;return!r&&!t?!1:r?t:L.withSize.toRaw.hasOwnProperty(e.rvalue.type)?r:!0}const B={value(e,t){if(L.withSize.array.some(i=>i===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=k(e.value)}catch(i){throw v(i,`${t}: ${i.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 i=-294967040,o=4294967040;if(e.value<i||e.value>o)throw new Error(`expected ${t} to be within the range of ${i} .. ${o}, but got `+s`${e.value}`)}else if(e.type!=="")throw new Error(`expected valid ${t} type, but got `+s`${e.type}`);if(L.withoutSize.array.some(i=>i===e.type)&&e.size)throw new Error(`${t} value cannot have size specified, but got `+s`${e.size}`);return e.type==="Value"?{...e,value:k(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(oe(e)!==!1){if(e.cmp){if(y.isLegalForCalc(e.cmp)===!1)throw new Error(`expected an accumulation operator (${y.forCalc.join(" ")}), but got `+s`${e.cmp}`);if(N(e)===!1)throw new Error("rvalue must be fully provided if operator is specified");e.rvalue=B.value(e.rvalue,"rvalue")}else if(e.cmp===""&&N(e))throw new Error(`expected an accumulation operator (${y.forCalc.join(" ")}), but got ""`)}},memoryComparisons(e){if(!(ie(e)===!1||Pe(e)||ne(e)||se(e))&&(e.rvalue=B.value(e.rvalue,"rvalue"),y.isLegalForReading(e.cmp)===!1||!e.cmp))throw new Error(`expected comparison operator (${y.forReading.join(" ")}), but got `+s`${e.cmp}`)}},ae={enums(e){if(A.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&&V.toRaw.hasOwnProperty(t.size)===!1)throw new Error(`expected valid ${r} size, but got `+s`${t.size}`)}if(e.cmp&&y.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(oe(e)&&e.hits>0)throw new Error(`hits value cannot be specified with ${e.flag} condition flag`)}},H={flag(e){const t=e.match(p.flag);if(!t)return["",e];const r=t[1];if(A.fromRaw.hasOwnProperty(r.toUpperCase())===!1)throw new Error(s`expected a legal condition flag, but got ${t[0]}`);return[A.fromRaw[r.toUpperCase()],e.slice(t[0].length)]},value(e){const t={type:"Mem",size:"",value:0};let r=null,i=!0;if((r=e.match(p.type))&&(e=e.slice(r[0].length),t.type=L.withSize.fromRaw[r[1].toLowerCase()],i=!1),e.startsWith("{recall}"))e=e.slice(8),t.type="Recall";else if(r=e.match(p.valueFloat))e=e.slice(r[0].length),t.type="Float",t.value=Number(r[1]);else if(r=e.match(p.memAddress)){if(e=e.slice(r[0].length),r[1].toLowerCase()==="0x")if(r=e.match(p.sizesRegular))e=e.slice(r[0].length),t.size=G.fromRaw[r[1].toUpperCase()];else if(e.match(p.hexValue))t.size="16bit";else throw new Error(s`expected valid size specifier, but got ${e.slice(0,6)}`);else if(r=e.match(p.sizesExt))e=e.slice(r[0].length),t.size=V.fromRaw[r[1].toUpperCase()];else throw new Error(s`expected valid size specifier, but got ${e.slice(0,6)}`);if(r=e.match(p.hexValue)){e=e.slice(r[0].length);const o=r[1];t.value=+("0x"+o)}else throw new Error(s`expected memory address as hex number, but got ${e.slice(0,6)}`)}else if(i&&(r=e.match(p.valueHex)))e=e.slice(r[0].length),t.type="Value",t.value=k(parseInt(r[1].replace(p.hexPrefix,"0x")));else if(i&&(r=e.match(p.valueInteger)))e=e.slice(r[0].length),t.type="Value",t.value=k(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(p.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(p.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 i=Number(r);if(i>4294967295)throw new Error(`expected hits to be within the range of 0x0 .. 0xFFFFFFFF, but got ${r}`);return[i,e.slice(t[0].length)]}else throw new Error(s`expected hits as unsigned integer, but got ${r}`)}};function Te(e){e=e.trim();const t={flag:"",lvalue:{type:"",size:"",value:0},cmp:"",rvalue:{type:"",size:"",value:0},hits:0};[t.flag,e]=H.flag(e);try{[t.lvalue,e]=H.value(e)}catch(r){throw v(r,`lvalue: ${r.message}`)}if(e){[t.cmp,e]=H.cmp(e);const r=ie(t),i=y.isLegalForReading(t.cmp);if((i||y.isLegalForCalc(t.cmp))===!1)throw r?new Error(`expected comparison operator (${y.forReading.join(" ")}), but got `+s`${t.cmp}`):new Error(`expected calculation operator (${y.forCalc.join(" ")}), but got `+s`${t.cmp}`);try{[t.rvalue,e]=H.value(e)}catch(n){throw v(n,`rvalue: ${n.message}`)}e&&([t.hits,e]=H.hits(e)),r===!1&&i&&(t.cmp="",t.rvalue={type:"",size:"",value:0})}return t}function le(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 ke(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 ce(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+=L.withSize.toRaw[e.type],V.toRaw.hasOwnProperty(e.size)?(t+="f",t+=V.toRaw[e.size]):(t+="0x",t+=G.toRaw[e.size]),t+=e.value.toString(16)),t}function ue(e){e.forEach((t,r)=>{const i=W(r);t.forEach((o,n)=>{if(ne(o))throw new Error(`${i}, condition ${n+1}: cannot have Measured condition without rvalue specified`);if(se(o))throw new Error(`${i}, condition ${n+1}: expected comparison operator (${y.forReading.join(" ")}), but got `+s`${o.cmp}`)})})}function he(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 F{constructor(t){if(t instanceof F)return t;if(typeof t=="string")Object.assign(this,Te(t));else if(Array.isArray(t))Object.assign(this,ke(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}`);ae.enums(this),this.lvalue=B.value(this.lvalue,"lvalue"),B.memoryComparisons(this),B.calculations(this),ae.hits(this),P(this)}with(t){return new F({...this,...t,lvalue:{...this.lvalue,...he(t.lvalue)},rvalue:{...this.rvalue,...he(t.rvalue)}})}toString(){let t="";return this.flag!==""&&(t+=A.toRaw[this.flag]+":"),t+=ce(this.lvalue),N(this)&&(t+=this.cmp,t+=ce(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=N(this);return[this.flag,this.lvalue.type,this.lvalue.size,le(this.lvalue),this.cmp,r?this.rvalue.type:"",t?"":r?this.rvalue.size:"",t?"":r?le(this.rvalue):"",this.hits>0?this.hits.toString():""]}}function C(e,t={}){const{considerLegacyValueFormat:r=!1}=t,i=e.split(r?"$":new RegExp("(?<!0x)S")).map(n=>n.trim().length>0?n.split("_"):[]),o=r&&i.every(n=>n.every(a=>a.match(p.flag)===null));return i.map((n,a)=>n.map((l,u)=>{if(o){l.match(p.legacyTrailingFloat)&&(l=l.replace(p.legacyTrailingFloat,$=>"f"+$));const c=l.match(p.legacyValue);if(c){const $=c[1]||"+";let f=Number(c[2]);f<0&&(f=k(f)),f>2147483647&&(f=2147483647),$==="-"&&f>0&&(f=-f),l=f.toString()}l=(u===n.length-1?"M":"A")+":"+l}try{return new F(l)}catch(c){const h=W(a);throw v(c,`${h}, condition ${u+1}: ${c.message}`)}}))}function K(e,t={}){const r=[];if(typeof e=="string")return C(e,t);if(Array.isArray(e)){const i=[];for(let o=0;o<e.length;o++){const n=e[o];try{n instanceof m?i.push(...n):i.push(new F(n))}catch(a){throw v(a,`conditions[${o}]: ${a.message}`)}}r.push(i)}else if(e instanceof m)r.push([...e]);else if(x(e)){let i=!1;const o=[];for(const a in e){const l=a.match(/^(?:core|alt([1-9]\d*))$/);if(l)l[0]==="core"?i=!0:l[1]&&o.push(Number(l[1]));else throw new Error(`conditions.${a}: group name must be "core" or "alt1", "alt2"...`)}if(!i)throw new Error('conditions: expected "core" group');o.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",...o.map(a=>`alt${a}`)];for(const a of n){const l=e[a];if(typeof l=="string")try{r.push(...C(l,t))}catch(u){throw v(u,`conditions.${a}: ${u.message}`)}else if(l instanceof m)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 m?u.push(...h):u.push(new F(h))}catch(h){throw v(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 p=(()=>{const e=[...y.forCalc,...y.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(V.toRaw).filter(Boolean).join("|")+")","i"),memAddress:/^(0x|f)/i,type:new RegExp("^("+Object.values(L.withSize.toRaw).filter(Boolean).join("|")+")","i"),valueHex:/^(-?h[\dabcdef]+)/i,valueInteger:/^(-?\d+)/,valueFloat:/^f(-?\d+\.\d+)/i,legacyTrailingFloat:/(-?\d+\.\d+)$/,legacyValue:/^v([-+])?([-+]?\d+)$/i}})(),de=["missable","progression","win_condition"],Ve=new Set(["",...de]),z={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((i,o)=>{const n=W(o);i.forEach((a,l)=>{a.flag==="Measured"&&t.push([n,l]),a.flag==="Measured%"&&r.push([n,l])})}),r.length>0&&t.length>0){const i=t[0],o=r[0];throw new Error(`${i[0]}, condition ${i[1]+1}: Measured conflicts with ${o[0]}, condition ${o[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,Ve.has(e)===!1)throw new Error(`expected type to be one of: [${[...de].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 o of r)if(/^\.+$/.test(o))throw new Error(`encountered ${o} within ${e}, path can only go down`);const i=r[r.length-1];if(/^.+\.(png|jpe?g|gif)$/.test(i)===!1)throw new Error(`expected badge filename to be *.(png|jpg|jpeg|gif) but got "${i}"`);return e}else throw new Error(t)}},Be=/^\s+$/;function He(e){const t=ee(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(Be)&&(r="");const[i,o]=t[0].split("|");return{id:w.andNormalizeId(i),setId:o===void 0?o:w.andNormalizeId(o,"setId"),title:t[2],description:t[3],type:r,author:t[7],points:Number(t[8]),badge:z.andNormalizeBadge(t[13]||""),conditions:C(t[1])}}const fe=Symbol();class M{constructor(t){const r=t instanceof M;if(typeof t=="string")Object.assign(this,He(t));else if(x(t)&&r===!1){let i=t.conditions;t[fe]||(i=K(t.conditions)),Object.assign(this,{id:w.andNormalizeId(t.id),setId:t.setId===void 0?t.setId:w.andNormalizeId(t.setId,"setId"),title:t.title,description:t.description,author:t.author,points:t.points,type:t.type,badge:z.andNormalizeBadge(t.badge),conditions:i})}else throw new Error("achievement data must be an object or string with achievement code, but got "+(r?"another Achievement instance":s`${t}`));w.string(this.title,"title"),w.string(this.description,"description"),this.author=z.andNormalizeAuthor(this.author),z.points(this.points),this.type=z.andNormalizeAchievementType(this.type),ue(this.conditions),z.measuredConditionsMixing(this.conditions),P(this)}with(t){return new M({...this,...t,[fe]:t.hasOwnProperty("conditions")===!1})}toString(t="achievement"){const r=this.conditions.map(i=>i.map(o=>o.toString()).join("_")).join("S");if(t==="conditions")return r;if(t==="achievement"||t==="achievement-legacy"){let i="";return i+=this.id,t==="achievement"&&this.setId!==void 0&&(i+="|"+this.setId),i+=":",i+=`"${r}":`,i+=T(this.title)+":",i+=T(this.description),i+=":::",i+=this.type+":",i+=T(this.author)+":",i+=this.points,i+=":::::",i+=this.badge.startsWith("local\\\\")?`"${this.badge}"`:this.badge,i}else throw new Error(s`unexpected achievement data toString request: ${t}`)}}const ge=new Set(["start","cancel","submit","value"]),pe=new Set(["SCORE","TIME","FRAMES","MILLISECS","SECS","TIMESECS","MINUTES","SECS_AS_MINS","VALUE","UNSIGNED","TENS","HUNDREDS","THOUSANDS","FIXED1","FIXED2","FIXED3"]),I={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 w.andNormalizeId(e)},andNormalizeConditions(e){let t;if(typeof e=="string")t=De(e);else if(x(e))t=Object.keys(e).reduce((r,i)=>{if(ge.has(i)===!1)throw new Error(`expected leaderboard condition group name to be one of: [${[...ge].join(", ")}], but got `+s`${i}`);return r[i]=K(e[i],{considerLegacyValueFormat:i==="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(o=>o.flag==="Measured")===!1)for(let o=0;o<r.length;o++){const n=r[o];if(n.flag===""){r[o]=n.with({flag:"Measured"});break}}return t},leaderboardType(e){if(pe.has(e)===!1)throw new Error(`expected type to be one of: [${[...pe].join(", ")}], but got `+s`${e}`)},measuredConditions(e){for(const t of["start","cancel","submit","value"])try{t!=="value"&&ue(e[t]),I.lackOfMeasuredPercent(e[t])}catch(r){throw v(r,`${Y(t)}, `+r.message)}},lackOfMeasuredPercent(e){e.forEach((t,r)=>{t.forEach((i,o)=>{if(i.flag==="Measured%"){const n=W(r);throw new Error(`${n}, condition ${o+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 X(e,t="S"){return e.map(r=>r.map(i=>i.toString()).join("_")).join(t)}function Ue(e){const t=ee(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,i]=t[0].split("|");return{id:I.andNormalizeLeaderboardId(r),setId:i===void 0?i:w.andNormalizeId(i,"setId"),conditions:I.andNormalizeConditions({start:t[1],cancel:t[2],submit:t[3],value:t[4]}),type:t[5],title:t[6],description:t[7],lowerIsBetter:I.andNormalizeLowerIsBetter(t[8])}}const me=Symbol();class O{constructor(t){const r=t instanceof O;if(typeof t=="string")Object.assign(this,Ue(t));else if(x(t)&&r===!1){let i=t.conditions;t[me]||(i=I.andNormalizeConditions(t.conditions)),Object.assign(this,{...t,id:I.andNormalizeLeaderboardId(t.id),setId:t.setId===void 0?t.setId:w.andNormalizeId(t.setId,"setId"),title:t.title,description:t.description,type:t.type,lowerIsBetter:I.andNormalizeLowerIsBetter(t.lowerIsBetter),conditions:i})}else throw new Error("leaderboard data must be an object or string with leaderboard code, but got "+(r?"another Leaderboard instance":s`${t}`));w.string(this.title,"title"),w.string(this.description,"description"),I.leaderboardType(this.type),I.measuredConditions(this.conditions),P(this)}with(t){return new O({...this,...t,[me]:t.hasOwnProperty("conditions")===!1})}toString(t="leaderboard"){const r=[X(this.conditions.start),X(this.conditions.cancel),X(this.conditions.submit),X(this.conditions.value,"$")].map(i=>`"${i}"`);if(t==="conditions")return r.join(":");if(t==="leaderboard"||t==="leaderboard-legacy"){let i="";return i+="L"+this.id,t==="leaderboard"&&this.setId!==void 0&&(i+="|"+this.setId),i+=":",i+=r.join(":")+":",i+=this.type+":",i+=T(this.title)+":",i+=T(this.description)+":",i+=Number(this.lowerIsBetter),i}else throw new Error(s`unexpected leaderboard data toString request: ${t}`)}}function De(e){const t={start:null,cancel:null,submit:null,value:null};let r=null;for(const[i,o]of[["STA","start"],["CAN","cancel"],["SUB","submit"],["VAL","value"]]){const n=i==="VAL";if(r=e.match(new RegExp(n?/VAL:(.+)/:`${i}:(.+?)::`))){e=e.slice(r[0].length);try{t[o]=K(r[1],{considerLegacyValueFormat:n})}catch(a){throw v(a,`${Y(o)}, ${a.message}`)}}else throw new Error(`expected ${i}:<conditions>::, but got ${e.slice(0,6)}`)}return t}const Q=new WeakMap;function*we(){for(const e in this)yield this[e]}class We{constructor(t){this.achievements={[Symbol.iterator]:we},this.leaderboards={[Symbol.iterator]:we};const{gameId:r,id:i,title:o}=t;this.gameId=w.andNormalizeId(r,"gameId"),i!==void 0&&(this.id=w.andNormalizeId(i,"id")),w.nonEmptyString(o,"achievement set title"),this.title=o,Q.set(this,{achievementIdCounter:111000001,leaderboardIdCounter:111000001})}addAchievement(t){const r=Q.get(this);let i=t instanceof M?t:new M(typeof t=="string"?t:{...t,id:t.id||r.achievementIdCounter});this.id!==i.setId&&(i=i.with({setId:this.id}));const{id:o}=i;if(this.achievements[o])throw new Error(`achievement with id ${o}: "${this.achievements[o].title}", already exists`);return this.achievements[o]=i,i.id>=r.achievementIdCounter&&(r.achievementIdCounter=Math.max(r.achievementIdCounter+1,i.id+1)),this}addLeaderboard(t){const r=Q.get(this);let i=t instanceof O?t:new O(typeof t=="string"?t:{...t,id:t.id||r.leaderboardIdCounter});this.id!==i.setId&&(i=i.with({setId:this.id}));const{id:o}=i;if(this.leaderboards[o])throw new Error(`leaderboard with id ${o}: "${this.leaderboards[o].title}", already exists`);return this.leaderboards[o]=i,i.id>=r.leaderboardIdCounter&&(r.leaderboardIdCounter=Math.max(r.leaderboardIdCounter+1,i.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((i,o)=>Number(i)-Number(o)).forEach(i=>{r+=this.achievements[i].toString(t==="set-legacy"?"achievement-legacy":"achievement")+`
`}),Object.keys(this.leaderboards).sort((i,o)=>Number(i)-Number(o)).forEach(i=>{r+=this.leaderboards[i].toString(t==="set-legacy"?"leaderboard-legacy":"leaderboard")+`
`}),r}}const be=Symbol("isRichLookupOrFormat"),Ge=new Set(["VALUE","SCORE","POINTS","TIME","FRAMES","MILLISECS","SECS","MINUTES","SECS_AS_MINS","FLOAT1","FLOAT2","FLOAT3","FLOAT4","FLOAT5","FLOAT6"]);function Xe(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 i=[];let o=t[0],n=t[0];for(let a=1;a<t.length;a++)t[a]-n===1||(i.push([o,n]),o=t[a]),n=t[a];return i.push([o,n]),{notNumbers:r,formattedRanges:i.map(([a,l])=>a===l?`${a}`:`${a}-${l}`).join(",")}}function qe(e){return e&&typeof e!="string"&&e[be]}function ye(e,...t){return e.map((r,i)=>{let o=i===e.length-1?"":t[i];return qe(o)&&(o=o.at()),`${r}${o}`}).join("")}function U(e){const t=e instanceof m?e.conditions:Array.isArray(e)?e:[e];return t[t.length-1].flag!=="Measured"}function J(e){return(e instanceof m?e.conditions:[e]).length===1?e.toString().replace("M:","").replace(" ",""):e.toString()}function $e(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(i){if(typeof i=="string"){try{C(i,{considerLegacyValueFormat:!0})}catch(o){throw v(o,s`Rich Presence Format ${t} got invalid string input: ${o.message}`)}return`@${t}(${i})`}if(i instanceof F||i instanceof m){if(U(i))throw new Error(s`Rich Presence Format ${t} got invalid input: must have at least one condition with Measured flag, but got ${i.toString()}`);return`@${t}(${J(i)})`}throw new Error(s`Rich Presence Format ${t} got invalid input: ${i}`)}}}function ve(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(Ge.has(r)===!1)throw new Error(s`Rich Presence Format ${t} got unexpected type: ${r}`);return g(e)}function Ee(e){const{name:t,values:r,defaultAt:i,compressRanges:o=!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(i!==void 0)if(typeof i=="string"){try{var l=C(i,{considerLegacyValueFormat:!0})}catch(c){throw v(c,s`Rich Presence Lookup ${t} got invalid defaultAt: ${c.message}`)}if(U(l[0]))throw new Error(s`Rich Presence Lookup ${t} got invalid input: must have at least one condition with Measured flag, but got ${i}`);n=`@${t}(${i})`}else if(i instanceof F||i instanceof m){if(U(i))throw new Error(s`Rich Presence Lookup ${t} got invalid defaultAt: must have at least one condition with Measured flag, but got ${i}`);n=`@${t}(${J(i)})`}else throw new Error(s`Rich Presence Lookup ${t} defaultAt expected to be a string, Condition or ConditionBuilder, but got ${i}`);let u=r;if(o){const c=a.reduce((h,[$,f])=>(h[f]||(h[f]=[]),h[f].push($),h),{});u=Object.entries(c).reduce((h,[$,f])=>{const{notNumbers:Z,formattedRanges:Fe}=Xe(f);Fe&&(h[Fe]=$);for(const _e of Z)h[_e]=$;return h},{})}return{[be]:!0,name:t,toString(c="dec"){let h=`Lookup:${t}`;for(const $ in u){let f=$;f!=="*"&&c.startsWith("hex")&&(f=f.replace(/\d+/g,Z=>j(Number(Z),c!=="hex-lowercase"))),h+=`
${f}=${u[$]}`}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 $=C(h,{considerLegacyValueFormat:!0})}catch(f){throw v(f,s`Rich Presence Lookup ${t} got error when parsing input: ${f.message}`)}if(U($[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 F||h instanceof m){if(U(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}(${J(h)})`}else throw new Error(s`Rich Presence Lookup ${t} got invalid input: ${h}`)}}}const R=e=>{const{format:t={},lookup:r={},lookupDefaultParameters:i={}}=e,o=Object.keys(t).reduce((u,c)=>(u[c]=ve({name:c,type:t[c]}),u),{}),n=Object.keys(r).reduce((u,c)=>(u[c]=Ee({compressRanges:i.compressRanges,...r[c],name:c}),u),{}),a=e.displays({lookup:n,format:o,tag:ye,macro:R.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 $e(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:o,displayStrings:l,macro:R.macro,toString(){return[Object.values(o).join(`
`),Object.values(n).map(u=>u.toString(r[u.name].keyFormat||i.keyFormat)).join(`
`)].join(`
`).trim()+`
Display:
`+l.join(`
`)}}};R.display=$e,R.format=ve,R.lookup=Ee,R.tag=ye,R.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"})},d.Achievement=M,d.AchievementSet=We,d.Condition=F,d.ConditionBuilder=m,d.Leaderboard=O,d.RichPresence=R,d.addHits=xe,d.andNext=re,d.define=q,d.measured=Ae,d.measuredIf=Ce,d.measuredPercent=Le,d.once=Oe,d.orNext=Me,d.pauseIf=Re,d.resetIf=Se,d.resetNextIf=ze,d.stringToNumberLE=te,d.subHits=Ne,d.trigger=Ie,Object.defineProperty(d,Symbol.toStringTag,{value:"Module"})}));