@cruncheevos/core
Version:
Parse and generate achievements and leaderboards for RetroAchievements.org
18 lines (13 loc) • 32.6 kB
JavaScript
(function(f,R){typeof exports=="object"&&typeof module<"u"?R(exports):typeof define=="function"&&define.amd?define(["exports"],R):(f=typeof globalThis<"u"?globalThis:f||self,R(f.cruncheevos={}))})(this,function(f){"use strict";function R(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,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 N(e){return Object.prototype.toString.call(e)==="[object Object]"}function U(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(N(r))P(r);else if(Array.isArray(r)){for(const o of r)(N(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 ee(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 T(e){return e.match(/[:"]/g)?`"${e.replace(/"/g,'\\"')}"`:e}const F={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},title(e,t="title"){if(typeof e!="string"||e.trim().length===0)throw new Error(`expected ${t} as non-empty string, but got `+s`${e}`)},andNormalizeDescription(e){if(e==null)return"";if(typeof e!="string")throw new Error(s`expected description as string, but got ${e}`);return e}};function W(e){return e===0?"Core":`Alt ${e}`}function te(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 m;return w.call(r,e,...t),r}}const q=v("");q.one=function(e){if(arguments.length>1)throw new Error("expected only one condition argument, but got "+arguments.length);return new E(e)},q.str=function(e,t){return re(...te(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 Se=v("Trigger"),Re=v("ResetIf"),xe=v("PauseIf"),Ne=v("AddHits"),Ie=v("SubHits"),Ae=v("Measured"),Le=v("Measured%"),Ce=v("MeasuredIf"),ze=v("ResetNextIf"),re=v("AndNext"),Me=v("OrNext"),Oe=(...e)=>new m().also("once",...e),_=new WeakMap;class m{constructor(){this.conditions=[],_.set(this,"")}trigger(...t){return w.call(this,"Trigger",...t),this}resetIf(...t){return w.call(this,"ResetIf",...t),this}pauseIf(...t){return w.call(this,"PauseIf",...t),this}addHits(...t){return w.call(this,"AddHits",...t),this}subHits(...t){return w.call(this,"SubHits",...t),this}measured(...t){return w.call(this,"Measured",...t),this}measuredPercent(...t){return w.call(this,"Measured%",...t),this}measuredIf(...t){return w.call(this,"MeasuredIf",...t),this}resetNextIf(...t){return w.call(this,"ResetNextIf",...t),this}andNext(...t){return w.call(this,"AndNext",...t),this}orNext(...t){return w.call(this,"OrNext",...t),this}also(...t){return w.call(this,"",...t),this}once(...t){return w.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,o,i)=>o!==i.length-1?r:r.with(t))}toString(){return this.conditions.join("_")}toJSON(){return this.toString()}}const je=/\s+/;function w(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(je)[1])),!1}return n instanceof m&&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 m){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 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},o=R(r);return delete o[""],{forReading:{toRaw:e},forCalc:{toRaw:t},toRaw:r,fromRaw:o}})(),L=(()=>{const e={Mem:"",Delta:"d",Prior:"p",BCD:"b",Invert:"~"};return{withSize:{toRaw:e,array:Object.keys(e),fromRaw:R(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:R(e)}})(),V=(()=>{const e={Float:"F",FloatBE:"B",Double32:"H",Double32BE:"I",MBF32:"M",MBF32LE:"L"};return{toRaw:e,fromRaw:R(e)}})(),b={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 oe(e){return A.forReading.toRaw.hasOwnProperty(e.flag)}function ie(e){return A.forCalc.toRaw.hasOwnProperty(e.flag)}function Pe(e){return e.lvalue.type==="Recall"&&I(e)===!1}function ne(e){return e.flag==="Measured"&&I(e)===!1}function se(e){return e.flag==="Measured"&&I(e)&&b.isLegalForCalc(e.cmp)}function I(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(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=k(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(L.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: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(ie(e)!==!1){if(e.cmp){if(b.isLegalForCalc(e.cmp)===!1)throw new Error(`expected an accumulation operator (${b.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=B.value(e.rvalue,"rvalue")}else if(e.cmp===""&&I(e))throw new Error(`expected an accumulation operator (${b.forCalc.join(" ")}), but got ""`)}},memoryComparisons(e){if(!(oe(e)===!1||Pe(e)||ne(e)||se(e))&&(e.rvalue=B.value(e.rvalue,"rvalue"),b.isLegalForReading(e.cmp)===!1||!e.cmp))throw new Error(`expected comparison operator (${b.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&&b.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(ie(e)&&e.hits>0)throw new Error(`hits value cannot be specified with ${e.flag} condition flag`)}},D={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,o=!0;if((r=e.match(p.type))&&(e=e.slice(r[0].length),t.type=L.withSize.fromRaw[r[1].toLowerCase()],o=!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 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(p.valueHex)))e=e.slice(r[0].length),t.type="Value",t.value=k(parseInt(r[1].replace(p.hexPrefix,"0x")));else if(o&&(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(U(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 Te(e){e=e.trim();const t={flag:"",lvalue:{type:"",size:"",value:0},cmp:"",rvalue:{type:"",size:"",value:0},hits:0};[t.flag,e]=D.flag(e);try{[t.lvalue,e]=D.value(e)}catch(r){throw $(r,`lvalue: ${r.message}`)}if(e){[t.cmp,e]=D.cmp(e);const r=oe(t),o=b.isLegalForReading(t.cmp);if((o||b.isLegalForCalc(t.cmp))===!1)throw r?new Error(`expected comparison operator (${b.forReading.join(" ")}), but got `+s`${t.cmp}`):new Error(`expected calculation operator (${b.forCalc.join(" ")}), but got `+s`${t.cmp}`);try{[t.rvalue,e]=D.value(e)}catch(n){throw $(n,`rvalue: ${n.message}`)}e&&([t.hits,e]=D.hits(e)),r===!1&&o&&(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 o=W(r);t.forEach((i,n)=>{if(ne(i))throw new Error(`${o}, condition ${n+1}: cannot have Measured condition without rvalue specified`);if(se(i))throw new Error(`${o}, condition ${n+1}: expected comparison operator (${b.forReading.join(" ")}), but got `+s`${i.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 E{constructor(t){if(t instanceof E)return t;if(typeof t=="string")Object.assign(this,Te(t));else if(Array.isArray(t))Object.assign(this,ke(t));else if(N(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 E({...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),I(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=I(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,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(p.flag)===null));return o.map((n,a)=>n.map((l,u)=>{if(i){l.match(p.legacyTrailingFloat)&&(l=l.replace(p.legacyTrailingFloat,y=>"f"+y));const c=l.match(p.legacyValue);if(c){const y=c[1]||"+";let d=Number(c[2]);d<0&&(d=k(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 K(e,t={}){const r=[];if(typeof e=="string")return C(e,t);if(Array.isArray(e)){const o=[];for(let i=0;i<e.length;i++){const n=e[i];try{n instanceof m?o.push(...n):o.push(new E(n))}catch(a){throw $(a,`conditions[${i}]: ${a.message}`)}}r.push(o)}else if(e instanceof m)r.push([...e]);else if(N(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(...C(l,t))}catch(u){throw $(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 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 p=(()=>{const e=[...b.forCalc,...b.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}})(),fe=["missable","progression","win_condition"],Ve=new Set(["",...fe]),z={points(e){if(U(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,Ve.has(e)===!1)throw new Error(`expected type to be one of: [${[...fe].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(U(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)}},Be=/^\s+$/;function De(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];return r.match(Be)&&(r=""),{id:F.andNormalizeId(t[0]),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 de=Symbol();class M{constructor(t){const r=t instanceof M;if(typeof t=="string")Object.assign(this,De(t));else if(N(t)&&r===!1){let o=t.conditions;t[de]||(o=K(t.conditions)),Object.assign(this,{id:F.andNormalizeId(t.id),title:t.title,description:t.description,author:t.author,points:t.points,type:t.type,badge:z.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}`));F.title(this.title),this.description=F.andNormalizeDescription(this.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,[de]: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"){let o="";return o+=this.id+":",o+=`"${r}":`,o+=T(this.title)+":",o+=T(this.description),o+=":::",o+=this.type+":",o+=T(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 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"]),S={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 F.andNormalizeId(e)},andNormalizeConditions(e){let t;if(typeof e=="string")t=Ue(e);else if(N(e))t=Object.keys(e).reduce((r,o)=>{if(ge.has(o)===!1)throw new Error(`expected leaderboard condition group name to be one of: [${[...ge].join(", ")}], but got `+s`${o}`);return r[o]=K(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(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]),S.lackOfMeasuredPercent(e[t])}catch(r){throw $(r,`${Y(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 X(e,t="S"){return e.map(r=>r.map(o=>o.toString()).join("_")).join(t)}function He(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");return{id:S.andNormalizeLeaderboardId(t[0]),conditions:S.andNormalizeConditions({start:t[1],cancel:t[2],submit:t[3],value:t[4]}),type:t[5],title:t[6],description:t[7],lowerIsBetter:S.andNormalizeLowerIsBetter(t[8])}}const me=Symbol();class O{constructor(t){const r=t instanceof O;if(typeof t=="string")Object.assign(this,He(t));else if(N(t)&&r===!1){let o=t.conditions;t[me]||(o=S.andNormalizeConditions(t.conditions)),Object.assign(this,{...t,id:S.andNormalizeLeaderboardId(t.id),title:t.title,description:t.description,type:t.type,lowerIsBetter:S.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}`));F.title(this.title),this.description=F.andNormalizeDescription(this.description),S.leaderboardType(this.type),S.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(o=>`"${o}"`);if(t==="conditions")return r.join(":");if(t==="leaderboard"){let o="";return o+="L"+this.id+":",o+=r.join(":")+":",o+=this.type+":",o+=T(this.title)+":",o+=T(this.description)+":",o+=Number(this.lowerIsBetter),o}else throw new Error(s`unexpected leaderboard data toString request: ${t}`)}}function Ue(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]=K(r[1],{considerLegacyValueFormat:n})}catch(a){throw $(a,`${Y(i)}, ${a.message}`)}}else throw new Error(`expected ${o}:<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,title:o}=t;this.gameId=F.andNormalizeId(r,"gameId"),F.title(o,"achievement set title"),this.title=o,Q.set(this,{achievementIdCounter:111000001,leaderboardIdCounter:111000001})}addAchievement(t){const r=Q.get(this),o=t instanceof M?t:new M(typeof t=="string"?t:{...t,id:t.id||r.achievementIdCounter}),{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),o=t instanceof O?t:new O(typeof t=="string"?t:{...t,id:t.id||r.leaderboardIdCounter}),{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(){let t="";return t+=`1.0
`,t+=this.title+`
`,Object.keys(this.achievements).sort((r,o)=>Number(r)-Number(o)).forEach(r=>{t+=this.achievements[r].toString()+`
`}),Object.keys(this.leaderboards).sort((r,o)=>Number(r)-Number(o)).forEach(r=>{t+=this.leaderboards[r].toString()+`
`}),t}}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 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 qe(e){return e&&typeof e!="string"&&e[be]}function ye(e,...t){return e.map((r,o)=>{let i=o===e.length-1?"":t[o];return qe(i)&&(i=i.at()),`${r}${i}`}).join("")}function H(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(o){if(typeof o=="string"){try{C(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 m){if(H(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}(${J(o)})`}throw new Error(s`Rich Presence Format ${t} got invalid input: ${o}`)}}}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: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!=="*"&&U(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=C(o,{considerLegacyValueFormat:!0})}catch(c){throw $(c,s`Rich Presence Lookup ${t} got invalid defaultAt: ${c.message}`)}if(H(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 m){if(H(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}(${J(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:Z,formattedRanges:Fe}=Xe(d);Fe&&(h[Fe]=y);for(const _e of Z)h[_e]=y;return h},{})}return{[be]:!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,Z=>j(Number(Z),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=C(h,{considerLegacyValueFormat:!0})}catch(d){throw $(d,s`Rich Presence Lookup ${t} got error when parsing input: ${d.message}`)}if(H(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 m){if(H(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 x=e=>{const{format:t={},lookup:r={},lookupDefaultParameters:o={}}=e,i=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:o.compressRanges,...r[c],name:c}),u),{}),a=e.displays({lookup:n,format:i,tag:ye,macro:x.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:i,displayStrings:l,macro:x.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(`
`)}}};x.display=$e,x.format=ve,x.lookup=Ee,x.tag=ye,x.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"})},f.Achievement=M,f.AchievementSet=We,f.Condition=E,f.ConditionBuilder=m,f.Leaderboard=O,f.RichPresence=x,f.addHits=Ne,f.andNext=re,f.define=q,f.measured=Ae,f.measuredIf=Ce,f.measuredPercent=Le,f.once=Oe,f.orNext=Me,f.pauseIf=xe,f.resetIf=Re,f.resetNextIf=ze,f.stringToNumberLE=te,f.subHits=Ie,f.trigger=Se,Object.defineProperty(f,Symbol.toStringTag,{value:"Module"})});