diff --git a/dist/sak32009-get-data-from-steam-steamdb.meta.js b/dist/sak32009-get-data-from-steam-steamdb.meta.js index 052704a..505cf8f 100644 --- a/dist/sak32009-get-data-from-steam-steamdb.meta.js +++ b/dist/sak32009-get-data-from-steam-steamdb.meta.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Get Data from Steam / SteamDB // @namespace sak32009-gaxvyvrguokgtog -// @version 25.12.29.2 +// @version 26.01.18.1 // @author Sak32009 // @description Get Data from Steam / SteamDB (ex Get DLC Info from SteamDB) // @license MIT @@ -16,7 +16,7 @@ // @match *://steamdb.info/app/* // @match *://steamdb.info/depot/* // @match *://store.steampowered.com/app/* -// @require https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js +// @require https://cdn.jsdelivr.net/npm/jquery@4.0.0/dist/jquery.min.js // @require https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js // @require https://cdn.jsdelivr.net/npm/uzip@0.20201231.0/UZIP.js // @require https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.js @@ -34,5 +34,5 @@ // @grant window.close // @run-at document-end // @noframes -// @updatedAt Mon, 29 Dec 2025 19:24:20 GMT +// @updatedAt Sun, 18 Jan 2026 16:22:38 GMT // ==/UserScript== \ No newline at end of file diff --git a/dist/sak32009-get-data-from-steam-steamdb.user.js b/dist/sak32009-get-data-from-steam-steamdb.user.js index 23ef043..1723818 100644 --- a/dist/sak32009-get-data-from-steam-steamdb.user.js +++ b/dist/sak32009-get-data-from-steam-steamdb.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Get Data from Steam / SteamDB // @namespace sak32009-gaxvyvrguokgtog -// @version 25.12.29.2 +// @version 26.01.18.1 // @author Sak32009 // @description Get Data from Steam / SteamDB (ex Get DLC Info from SteamDB) // @license MIT @@ -16,7 +16,7 @@ // @match *://steamdb.info/app/* // @match *://steamdb.info/depot/* // @match *://store.steampowered.com/app/* -// @require https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js +// @require https://cdn.jsdelivr.net/npm/jquery@4.0.0/dist/jquery.min.js // @require https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js // @require https://cdn.jsdelivr.net/npm/uzip@0.20201231.0/UZIP.js // @require https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.js @@ -34,7 +34,7 @@ // @grant window.close // @run-at document-end // @noframes -// @updatedAt Mon, 29 Dec 2025 19:24:20 GMT +// @updatedAt Sun, 18 Jan 2026 16:22:38 GMT // ==/UserScript== (function (Nr, lodash, O, uzip, w, Or) { @@ -61,8 +61,8 @@ const d=new Set;const pr = async e=>{d.has(e)||(d.add(e),(t=>{typeof GM_addStyle=="function"?GM_addStyle(t):(document.head||document.documentElement).appendChild(document.createElement("style")).append(t);})(e));}; - var It=typeof GM_addValueChangeListener<"u"?GM_addValueChangeListener:void 0,Lt=typeof GM_download<"u"?GM_download:void 0,st=typeof GM_getValue<"u"?GM_getValue:void 0,St=typeof GM_openInTab<"u"?GM_openInTab:void 0,K=typeof GM_setValue<"u"?GM_setValue:void 0,Nt=typeof GM_xmlhttpRequest<"u"?GM_xmlhttpRequest:void 0,Ot=typeof unsafeWindow<"u"?unsafeWindow:void 0;class S{static storageKey="depots";static duration=7200*1e3;static getCacheEntries(){return st(this.storageKey,{})}static getCacheEntriesByAppId(r){return this.getCacheEntries()[r]}static addCacheEntry(r,t,p){const s=Date.now()+this.duration,o=this.getCacheEntriesByAppId(r)||{};o[t]={...p,expire:s};const n=this.getCacheEntries();n[r]=o,K(this.storageKey,n);}static removeCacheEntryByAppId(r){const t=this.getCacheEntries();return typeof t[r]>"u"?false:(delete t[r],K(this.storageKey,t),true)}static removeCacheEntryByDepotId(r,t){const p=this.getCacheEntriesByAppId(r);if(typeof p>"u")return false;delete p[t];const s={...this.getCacheEntries(),[r]:p};return K(this.storageKey,s),true}static clearExpiredCacheEntries(){const r=Date.now(),t=this.getCacheEntries();let p=false;for(const a in t){const s=t[a];for(const o in s)s[o].expire{Nt({...r,onload:s=>{if(s.readyState!==4||s.status!==200){a(new Error(`xhr -> status: ${s.status}`));return}t&&this.addCacheEntry(r.url,s.response),p({cached:false,data:s.response});},onerror:s=>{a(new Error(`xhr -> ${s.error}`));}});})}static getCacheEntries(){return st(this.storageKey,{})}static getCacheData(r){const p=this.getCacheEntries()[r];if(typeof p>"u")return;const a=Date.now();if(p.expire>=a)return p.data;this.removeCacheEntry(r);}static addCacheEntry(r,t){const a=Date.now()+this.duration,s=this.getCacheEntries();s[r]={data:t,expire:a},K(this.storageKey,s);}static removeCacheEntry(r){const t=this.getCacheEntries();return typeof t[r]>"u"?false:(delete t[r],K(this.storageKey,t),true)}static clearExpiredCacheEntries(){const r=Date.now(),t=this.getCacheEntries();for(const[p,{expire:a}]of Object.entries(t))a"u"||typeof n>"u")continue;const i=ot(n.trim()),b=o.trim();t[i]=b;}return t}static withSteamDB(r={},t={}){const p={ok:[],not_ok:[],not_found:[],not_in:[],errors:[]},a=Object.fromEntries(Object.entries(t).map(([s,o])=>[s.toLowerCase(),{fileName:s,fileHash:o}]));for(const[s,o]of Object.entries(r)){const n=this.line(o,s),i=s.toLowerCase();if(typeof a[i]<"u"){const{fileName:b,fileHash:d}=a[i],m=this.line(d,b),c=o.toLowerCase().match(/^\w+|\w+$/g);if(c!==null){const[u,f]=c;if(typeof u>"u"||typeof f>"u")continue;const h=d.toLowerCase();h.startsWith(u)&&h.endsWith(f)?p.ok.push(m):p.not_ok.push(m);}else p.errors.push(m);delete a[i];}else p.not_found.push(n);}for(const{fileName:s,fileHash:o}of Object.values(a))p.not_in.push(this.line(o,s));return p}}class Rt{static mimeToExtension(r){return {"image/jpeg":"jpg","image/png":"png","image/bmp":"bmp"}[r]??r.split("/").pop()??"bin"}static async getUint8FromArrayBuffer(r,t){const p=new Blob([r],{type:t}),a=await createImageBitmap(p),s=document.createElement("canvas");s.width=a.width,s.height=a.height;const o=s.getContext("2d");if(o===null)throw new Error("could not get 2d context from canvas.");o.drawImage(a,0,0),a.close();const n=await new Promise(b=>{s.toBlob(d=>{b(d);},t,1);});if(n===null)throw new Error("failed to create blob from canvas.");return new Uint8Array(await n.arrayBuffer())}static async getBufferFromImage(r,t){const p=await J.fetch({url:r,method:"GET",responseType:"arraybuffer"}),a=await this.getUint8FromArrayBuffer(p.data,t),s=new URL(r).pathname,o=O.parse(s).name,n=this.mimeToExtension(t);return {name:`${o}.${n}`,content:a}}static async getZipped(r,t,p,a,s){const o={},n=r.length;let i=0;const b=r.map(async d=>{try{const m=await this.getBufferFromImage(d,t);o[m.name]=m.content;}catch(m){a(m);}finally{i+=1,p(i,n);}});Promise.all(b).then(()=>{const d=uzip.encode(o),m=new Uint8Array(d);s(m);}).catch(d=>{a(d);});}}const zt="Get Data from Steam / SteamDB",Bt="25.12.29.2",Mt=zt,Pt=Bt,dr="https://steamdb.info/api",Gt="https://shared.fastly.steamstatic.com",Ft=`${Gt}/community_assets/images/apps`,Ut="da39a3ee5e6b4b0d3255bfef95601890afd80709",Vt=Ut.replace(/^(.{10}).*(.{10})$/,"$1***$2");class Ht{appTitle=`${Mt} v${Pt}`;appUrl=new URL(window.location.href);appOptions={withDLCUnknowns:false};appScrollOptions={block:"center",inline:"center",behavior:"smooth"};appTabActive=null;appTabLoaderTime=0;byteSizeOptions={precision:2,units:"iec"}}function Z(e){const r=e.replaceAll(/[\r\n]+/g,"");return lodash.unescape(r)}function y(e){const r=Z(e);return lodash.escape(r)}function Rr(e){if(typeof e.jQuery<"u")return e.jQuery;if(typeof e.$<"u")return e.$}function Kt(){const e=Ot,r=Rr(e);if(typeof r<"u")return r;if(typeof e.wrappedJSObject<"u"){const t=e.wrappedJSObject,p=Rr(t);if(typeof p<"u")return p}}class Wt extends Ht{appData={appId:"_NOT_FOUND_",name:"_NOT_FOUND_",dlc:{},dlcCount:0,dlcUnknowns:{},dlcUnknownsCount:0,dlcCountAll:0,relatedDLC:{},relatedDLCCount:0,achievements:{},achievementsCount:0,achievementsImages:[],achievementsImagesCount:0,items:{},itemsCount:0,depotId:"_NOT_FOUND_",depotManifestId:"_NOT_FOUND_",depots:{},depotsCount:0,depotFileHashes:{},depotFileHashesCount:0,launchBinaryPath:"_NOT_FOUND_",launchBinaryBaseName:"_NOT_FOUND_",iconUrl:"_NOT_FOUND_",iconUrlBaseName:"_NOT_FOUND_",iconUrlFileName:"_NOT_FOUND_",user:{isLoggedIn:false}};constructor(){super();}getSteamAchievementImageUrl(r){return `${Ft}/${this.appData.appId}/${r}`}hasSteamDBTab(r){return w(`a.tabnav-tab[aria-controls="${r}"]`).length>0}getSteamAppId(){const t=w("div[data-appid]").attr("data-appid");this.appData.appId=t;}getSteamAppName(){const t=w("div#appHubAppName").text().trim();this.appData.name=y(t);}getSteamAppDLC(){const r=w("a.game_area_dlc_row[data-ds-appid]");for(const t of r.toArray()){const p=w(t),a=p.attr("data-ds-appid"),s=p.find("> div.game_area_dlc_name"),o=y(s.text().trim());this.appData.dlc[a]=o,this.appData.dlcCount+=1,this.appData.dlcCountAll+=1;}}getSteamDBUserIsLoggedIn(){const t=w("div.header-user > a.header-login").length===0;this.appData.user.isLoggedIn=t;}getSteamDBAppId(){const t=w(".scope-app[data-appid]").attr("data-appid");this.appData.appId=t;}getSteamDBAppName(){const t=w('.pagehead h1[itemprop="name"]').text().trim();this.appData.name=y(t);}getSteamDBAppIconUrl(){const t=w(".pagehead img.app-icon.avatar").attr("src"),p=new URL(t).pathname;this.appData.iconUrl=t,this.appData.iconUrlBaseName=O.basename(p),this.appData.iconUrlFileName=O.parse(p).name;}getSteamDBAppLaunchBinaryPath(){const t=w("#config.tab-pane .launch-option").first().find("> table > tbody > tr:first-child > td:nth-child(2) > code").text().trim();this.appData.launchBinaryPath=y(t),this.appData.launchBinaryBaseName=O.basename(jt(this.appData.launchBinaryPath));}getSteamDBAppDLC(){const r=w("#dlc.tab-pane > table > tbody > tr.app[data-appid]");for(const t of r.toArray()){const p=w(t),a=p.attr("data-appid"),s=p.find("td:nth-of-type(2)"),o=y(s.text().trim());s.hasClass("muted")?(this.appData.dlcUnknowns[a]=o,this.appData.dlcUnknownsCount+=1):(this.appData.dlc[a]=o,this.appData.dlcCount+=1),this.appData.dlcCountAll+=1;}}async getSteamDBAppAchievements(){return new Promise(r=>{const t=w("#stats.tab-pane > #js-achievements > .achievements_list");if(t.length>0){const p=t.closest("#js-achievements").html();r({cached:true,data:p});}else {const p=J.fetch({url:`${dr}/RenderAppSection/?section=stats&appid=${this.appData.appId}`,method:"GET",headers:{Accept:"text/html","X-Requested-With":"XMLHttpRequest"},responseType:"text"});r(p);}}).then(r=>{const p=w("
").html(r.data).find("> .achievements_list > .achievement");for(const a of p.toArray()){const s=w(a),o=s.find(".achievement_api"),n=y(o.text().trim()),i=s.find(".achievement_name"),b=y(i.text().trim()),d=s.find(".achievement_desc"),m=d.find(".achievement_spoiler"),c=m.length>0,u=c?1:0,f=y((c?m:d).text().trim()),v=s.find("img.achievement_image").attr("data-name"),T=this.getSteamAchievementImageUrl(v),X=s.find("img.achievement_image_small").attr("data-name"),C=this.getSteamAchievementImageUrl(X);this.appData.achievements[n]={name:n,displayName:b,hidden:u,description:f,icon:T,icongray:C},this.appData.achievementsCount+=1,this.appData.achievementsImages.push(T,C),this.appData.achievementsImagesCount+=2;}return r})}getSteamDBAppDepotsData(r){const t=r.closest("table").find('> thead > tr > th:contains("Manifest ID")').length>0;for(const p of r.toArray()){const a=w(p),s=a.attr("data-depotid"),o=a.find("> td:nth-child(2)");if(o.find('span:contains("Unused")').length>0)continue;const i=Z(o.find("span.i.muted").text().trim()),b=Z(o.find('> span:not(:contains("Windows")):not(:contains("macOS")):not(:contains("Linux")):not(:contains("Shared Install")):not(:contains("Depot from ")):not(:contains("DLC ")):not(:contains("64-bit")):not(:contains("Unused")):not(.i.muted)').text().trim()),d=i.length>0?i:b,m=[];o.find('span:contains("Windows")').length>0&&m.push("windows"),o.find('span:contains("macOS")').length>0&&m.push("mac"),o.find('span:contains("Linux")').length>0&&m.push("linux"),m.length===0&&m.push("all");const h=o.find('span:contains("Shared Install")'),v=h.length>0,T=/Depot from (?\d+)/u.exec(h.text().trim()),F=T===null?"":T[1],X=o.find('span:contains("DLC ")'),C=X.length>0,U=/DLC (?\d+)/u.exec(X.text().trim()),I=U===null?"":U[1],V=Number.parseInt(a.find(`> td:nth-child(${t?4:3})`).attr("data-sort"));this.appData.depots[s]={name:d,size:V,os:m,type:C?"dlc":"game",isDLC:C,isDLCValue:I,isSharedInstall:v,isSharedInstallValue:F},this.appData.depotsCount+=1;}}getSteamDBAppDepots(){const r=w('#depots.tab-pane > h2:contains("Depots")').next("table").find("> tbody > tr[data-depotid]");this.getSteamDBAppDepotsData(r);}getSteamDBAppDepotsDLC(){const r=w('#depots.tab-pane > h2:contains("Inner depots from DLC")').next("table").find("> tbody > tr[data-depotid]");this.getSteamDBAppDepotsData(r);}async getSteamDBAppRelatedDLC(){return new Promise(r=>{const t=w("#linked.tab-pane > #js-linked-apps > table");if(t.length>0){const p=t.prop("outerHTML");r({cached:true,data:p});}else {const p=J.fetch({url:`${dr}/RenderLinkedApps/?appid=${this.appData.appId}`,method:"GET",headers:{Accept:"text/html","X-Requested-With":"XMLHttpRequest"},responseType:"text"});r(p);}}).then(r=>{const p=w("
").html(r.data).find("> table > tbody > tr.app[data-appid]");for(const a of p.toArray()){const s=w(a),o=s.attr("data-appid"),n=s.find("td:nth-of-type(2)").text().trim().toLowerCase(),i=Z(s.find("td:nth-of-type(3) > a > b").text().trim());n==="dlc"&&(this.appData.relatedDLC[o]=i,this.appData.relatedDLCCount+=1);}return r})}async getSteamDBAppItems(){return J.fetch({url:`${dr}/RenderAppSection/?section=items&appid=${this.appData.appId}`,method:"GET",headers:{Accept:"text/html","X-Requested-With":"XMLHttpRequest"},responseType:"text"}).then(r=>{const p=w("
").html(r.data).find("> .list > .economy-item");for(const a of p.toArray()){const s={appid:this.appData.appId},o=w(a),n=o.find("h4.economy-item-name"),i=n.find("i"),b=y((i.length>0?i:n.textWithoutChildren()).text().trim());s.name=b;const d=n.find("a").text().trim().slice(1);s.itemdefid=d;const m=o.find("div.economy-item-description");m.length>0&&(s.description=y(m.html().trim()));let c=null;for(const u of o.find(".table tr").toArray()){const f=w(u);if(f.hasClass("web-assets-hr")&&(c=f.find("td:first-child").text().slice(0,-1).trim()),c!==null){const h=[];for(const v of f.find("td").toArray()){const T=w(v),F=y(T.text().trim());h.push(F);}s[c]=h.join(";");}else {const h=f.find("td:first-child").text().trim(),v=y(f.find("td:last-child").text().trim());s[h]=v;}f.hasClass("web-assets-bottom")&&(c=null);}this.appData.items[d]=s,this.appData.itemsCount+=1;}return r})}getSteamDBAppDepotPageHashes(){const r=Kt();if(typeof r>"u")return;const p=r("#files.tab-pane .table.depot-files").DataTable().data().toArray();for(const a of p){const s=w("").html(a[0]),o=s.find("a"),n=ot(Z((o.length>0?o:s).textWithoutChildren().text().trim()));let i=w("").html(a[1]).text().trim();const b=typeof a[2]=="object"?a[2].display:a[2];b.length!==0&&(i.length===0&&b==="0"&&(i=Vt),this.appData.depotFileHashes[n]=i,this.appData.depotFileHashesCount+=1);}}getSteamDBAppDepotPageId(){const t=w(".scope-depot[data-depotid]").attr("data-depotid");this.appData.depotId=t;}getSteamDBAppDepotPageManifestId(){const t=w("#files.tab-pane > p").first().find("> b").first().text().trim();this.appData.depotManifestId=t;}}class Yt extends Wt{parserTagKeyRe=/\{(\w+)\}/g;constructor(){super();}parserReplaceDlcsTag(r,t){const a=r.attr("index-start-from-zero")==="true",s=r.attr("index-prefix"),o=typeof s<"u"?Number.parseInt(s):0,n=r.attr("separator"),i=typeof n<"u"?n:` + var It=typeof GM_addValueChangeListener<"u"?GM_addValueChangeListener:void 0,Lt=typeof GM_download<"u"?GM_download:void 0,st=typeof GM_getValue<"u"?GM_getValue:void 0,St=typeof GM_openInTab<"u"?GM_openInTab:void 0,W=typeof GM_setValue<"u"?GM_setValue:void 0,Nt=typeof GM_xmlhttpRequest<"u"?GM_xmlhttpRequest:void 0,Ot=typeof unsafeWindow<"u"?unsafeWindow:void 0;class S{static storageKey="depots";static duration=7200*1e3;static getCacheEntries(){return st(this.storageKey,{})}static getCacheEntriesByAppId(r){return this.getCacheEntries()[r]}static addCacheEntry(r,t,p){const s=Date.now()+this.duration,o=this.getCacheEntriesByAppId(r)||{};o[t]={...p,expire:s};const n=this.getCacheEntries();n[r]=o,W(this.storageKey,n);}static removeCacheEntryByAppId(r){const t=this.getCacheEntries();return typeof t[r]>"u"?false:(delete t[r],W(this.storageKey,t),true)}static removeCacheEntryByDepotId(r,t){const p=this.getCacheEntriesByAppId(r);if(typeof p>"u")return false;delete p[t];const s={...this.getCacheEntries(),[r]:p};return W(this.storageKey,s),true}static clearExpiredCacheEntries(){const r=Date.now(),t=this.getCacheEntries();let p=false;for(const a in t){const s=t[a];for(const o in s)s[o].expire{Nt({...r,onload:s=>{if(s.readyState!==4||s.status!==200){a(new Error(`xhr -> status: ${s.status}`));return}t&&this.addCacheEntry(r.url,s.response),p({cached:false,data:s.response});},onerror:s=>{a(new Error(`xhr -> ${s.error}`));}});})}static getCacheEntries(){return st(this.storageKey,{})}static getCacheData(r){const p=this.getCacheEntries()[r];if(typeof p>"u")return;const a=Date.now();if(p.expire>=a)return p.data;this.removeCacheEntry(r);}static addCacheEntry(r,t){const a=Date.now()+this.duration,s=this.getCacheEntries();s[r]={data:t,expire:a},W(this.storageKey,s);}static removeCacheEntry(r){const t=this.getCacheEntries();return typeof t[r]>"u"?false:(delete t[r],W(this.storageKey,t),true)}static clearExpiredCacheEntries(){const r=Date.now(),t=this.getCacheEntries();for(const[p,{expire:a}]of Object.entries(t))a"u"||typeof n>"u")continue;const i=ot(n.trim()),b=o.trim();t[i]=b;}return t}static withSteamDB(r={},t={}){const p={ok:[],not_ok:[],not_found:[],not_in:[],errors:[]},a=Object.fromEntries(Object.entries(t).map(([s,o])=>[s.toLowerCase(),{fileName:s,fileHash:o}]));for(const[s,o]of Object.entries(r)){const n=this.line(o,s),i=s.toLowerCase();if(typeof a[i]<"u"){const{fileName:b,fileHash:d}=a[i],m=this.line(d,b),c=o.toLowerCase().match(/^\w+|\w+$/g);if(c!==null){const[u,f]=c;if(typeof u>"u"||typeof f>"u")continue;const h=d.toLowerCase();h.startsWith(u)&&h.endsWith(f)?p.ok.push(m):p.not_ok.push(m);}else p.errors.push(m);delete a[i];}else p.not_found.push(n);}for(const{fileName:s,fileHash:o}of Object.values(a))p.not_in.push(this.line(o,s));return p}}class Rt{static mimeToExtension(r){return {"image/jpeg":"jpg","image/png":"png","image/bmp":"bmp"}[r]??r.split("/").pop()??"bin"}static async getUint8FromArrayBuffer(r,t){const p=new Blob([r],{type:t}),a=await createImageBitmap(p),s=document.createElement("canvas");s.width=a.width,s.height=a.height;const o=s.getContext("2d");if(o===null)throw new Error("could not get 2d context from canvas.");o.drawImage(a,0,0),a.close();const n=await new Promise(b=>{s.toBlob(d=>{b(d);},t,1);});if(n===null)throw new Error("failed to create blob from canvas.");return new Uint8Array(await n.arrayBuffer())}static async getBufferFromImage(r,t){const p=await J.fetch({url:r,method:"GET",responseType:"arraybuffer"}),a=await this.getUint8FromArrayBuffer(p.data,t),s=new URL(r).pathname,o=O.parse(s).name,n=this.mimeToExtension(t);return {name:`${o}.${n}`,content:a}}static async getZipped(r,t,p,a,s){const o={},n=r.length;let i=0;const b=r.map(async d=>{try{const m=await this.getBufferFromImage(d,t);o[m.name]=m.content;}catch(m){a(m);}finally{i+=1,p(i,n);}});Promise.all(b).then(()=>{const d=uzip.encode(o),m=new Uint8Array(d);s(m);}).catch(d=>{a(d);});}}const zt="Get Data from Steam / SteamDB",Bt="26.01.18.1",Mt=zt,Pt=Bt,dr="https://steamdb.info/api",Gt="https://shared.fastly.steamstatic.com",Ft=`${Gt}/community_assets/images/apps`,Ut="da39a3ee5e6b4b0d3255bfef95601890afd80709",Vt=Ut.replace(/^(.{10}).*(.{10})$/,"$1***$2");class Ht{appTitle=`${Mt} v${Pt}`;appUrl=new URL(window.location.href);appOptions={withDLCUnknowns:false};appScrollOptions={block:"center",inline:"center",behavior:"smooth"};appTabActive=null;appTabLoaderTime=0;byteSizeOptions={precision:2,units:"iec"}}function Z(e){const r=e.replaceAll(/[\r\n]+/g,"");return lodash.unescape(r)}function y(e){const r=Z(e);return lodash.escape(r)}function Rr(e){if(typeof e.jQuery<"u")return e.jQuery;if(typeof e.$<"u")return e.$}function Wt(){const e=Ot,r=Rr(e);if(typeof r<"u")return r;if(typeof e.wrappedJSObject<"u"){const t=e.wrappedJSObject,p=Rr(t);if(typeof p<"u")return p}}class Kt extends Ht{appData={appId:"_NOT_FOUND_",name:"_NOT_FOUND_",dlc:{},dlcCount:0,dlcUnknowns:{},dlcUnknownsCount:0,dlcCountAll:0,relatedDLC:{},relatedDLCCount:0,achievements:{},achievementsCount:0,achievementsImages:[],achievementsImagesCount:0,items:{},itemsCount:0,depotId:"_NOT_FOUND_",depotManifestId:"_NOT_FOUND_",depots:{},depotsCount:0,depotFileHashes:{},depotFileHashesCount:0,launchBinaryPath:"_NOT_FOUND_",launchBinaryBaseName:"_NOT_FOUND_",iconUrl:"_NOT_FOUND_",iconUrlBaseName:"_NOT_FOUND_",iconUrlFileName:"_NOT_FOUND_",user:{isLoggedIn:false}};constructor(){super();}getSteamAchievementImageUrl(r){return `${Ft}/${this.appData.appId}/${r}`}hasSteamDBTab(r){return w(`a.tabnav-tab[aria-controls="${r}"]`).length>0}getSteamAppId(){const t=w("div[data-appid]").attr("data-appid");this.appData.appId=t;}getSteamAppName(){const t=w("div#appHubAppName").text().trim();this.appData.name=y(t);}getSteamAppDLC(){const r=w("a.game_area_dlc_row[data-ds-appid]");for(const t of r.toArray()){const p=w(t),a=p.attr("data-ds-appid"),s=p.find("> div.game_area_dlc_name"),o=y(s.textWithoutChildren().text().trim());this.appData.dlc[a]=o,this.appData.dlcCount+=1,this.appData.dlcCountAll+=1;}}getSteamDBUserIsLoggedIn(){const t=w("div.header-user > a.header-login").length===0;this.appData.user.isLoggedIn=t;}getSteamDBAppId(){const t=w(".scope-app[data-appid]").attr("data-appid");this.appData.appId=t;}getSteamDBAppName(){const t=w('.pagehead h1[itemprop="name"]').text().trim();this.appData.name=y(t);}getSteamDBAppIconUrl(){const t=w(".pagehead img.app-icon.avatar").attr("src"),p=new URL(t).pathname;this.appData.iconUrl=t,this.appData.iconUrlBaseName=O.basename(p),this.appData.iconUrlFileName=O.parse(p).name;}getSteamDBAppLaunchBinaryPath(){const t=w("#config.tab-pane .launch-option").first().find("> table > tbody > tr:first-child > td:nth-child(2) > code").text().trim();this.appData.launchBinaryPath=y(t),this.appData.launchBinaryBaseName=O.basename(jt(this.appData.launchBinaryPath));}getSteamDBAppDLC(){const r=w("#dlc.tab-pane > table > tbody > tr.app[data-appid]");for(const t of r.toArray()){const p=w(t),a=p.attr("data-appid"),s=p.find("td:nth-of-type(2)"),o=y(s.text().trim());s.hasClass("muted")?(this.appData.dlcUnknowns[a]=o,this.appData.dlcUnknownsCount+=1):(this.appData.dlc[a]=o,this.appData.dlcCount+=1),this.appData.dlcCountAll+=1;}}async getSteamDBAppAchievements(){return new Promise(r=>{const t=w("#stats.tab-pane > #js-achievements > .achievements_list");if(t.length>0){const p=t.closest("#js-achievements").html();r({cached:true,data:p});}else {const p=J.fetch({url:`${dr}/RenderAppSection/?section=stats&appid=${this.appData.appId}`,method:"GET",headers:{Accept:"text/html","X-Requested-With":"XMLHttpRequest"},responseType:"text"});r(p);}}).then(r=>{const p=w("
").html(r.data).find("> .achievements_list > .achievement");for(const a of p.toArray()){const s=w(a),o=s.find(".achievement_api"),n=y(o.text().trim()),i=s.find(".achievement_name"),b=y(i.text().trim()),d=s.find(".achievement_desc"),m=d.find(".achievement_spoiler"),c=m.length>0,u=c?1:0,f=y((c?m:d).text().trim()),v=s.find("img.achievement_image").attr("data-name"),T=this.getSteamAchievementImageUrl(v),X=s.find("img.achievement_image_small").attr("data-name"),C=this.getSteamAchievementImageUrl(X);this.appData.achievements[n]={name:n,displayName:b,hidden:u,description:f,icon:T,icongray:C},this.appData.achievementsCount+=1,this.appData.achievementsImages.push(T,C),this.appData.achievementsImagesCount+=2;}return r})}getSteamDBAppDepotsData(r){const t=r.closest("table").find('> thead > tr > th:contains("Manifest ID")').length>0;for(const p of r.toArray()){const a=w(p),s=a.attr("data-depotid"),o=a.find("> td:nth-child(2)");if(o.find('span:contains("Unused")').length>0)continue;const i=Z(o.find("span.i.muted").text().trim()),b=Z(o.find('> span:not(:contains("Windows")):not(:contains("macOS")):not(:contains("Linux")):not(:contains("Shared Install")):not(:contains("Depot from ")):not(:contains("DLC ")):not(:contains("64-bit")):not(:contains("Unused")):not(.i.muted)').text().trim()),d=i.length>0?i:b,m=[];o.find('span:contains("Windows")').length>0&&m.push("windows"),o.find('span:contains("macOS")').length>0&&m.push("mac"),o.find('span:contains("Linux")').length>0&&m.push("linux"),m.length===0&&m.push("all");const h=o.find('span:contains("Shared Install")'),v=h.length>0,T=/Depot from (?\d+)/u.exec(h.text().trim()),F=T===null?"":T[1],X=o.find('span:contains("DLC ")'),C=X.length>0,U=/DLC (?\d+)/u.exec(X.text().trim()),I=U===null?"":U[1],V=Number.parseInt(a.find(`> td:nth-child(${t?4:3})`).attr("data-sort"));this.appData.depots[s]={name:d,size:V,os:m,type:C?"dlc":"game",isDLC:C,isDLCValue:I,isSharedInstall:v,isSharedInstallValue:F},this.appData.depotsCount+=1;}}getSteamDBAppDepots(){const r=w('#depots.tab-pane > h2:contains("Depots")').next("table").find("> tbody > tr[data-depotid]");this.getSteamDBAppDepotsData(r);}getSteamDBAppDepotsDLC(){const r=w('#depots.tab-pane > h2:contains("Inner depots from DLC")').next("table").find("> tbody > tr[data-depotid]");this.getSteamDBAppDepotsData(r);}async getSteamDBAppRelatedDLC(){return new Promise(r=>{const t=w("#linked.tab-pane > #js-linked-apps > table");if(t.length>0){const p=t.prop("outerHTML");r({cached:true,data:p});}else {const p=J.fetch({url:`${dr}/RenderLinkedApps/?appid=${this.appData.appId}`,method:"GET",headers:{Accept:"text/html","X-Requested-With":"XMLHttpRequest"},responseType:"text"});r(p);}}).then(r=>{const p=w("
").html(r.data).find("> table > tbody > tr.app[data-appid]");for(const a of p.toArray()){const s=w(a),o=s.attr("data-appid"),n=s.find("td:nth-of-type(2)").text().trim().toLowerCase(),i=Z(s.find("td:nth-of-type(3) > a > b").text().trim());n==="dlc"&&(this.appData.relatedDLC[o]=i,this.appData.relatedDLCCount+=1);}return r})}async getSteamDBAppItems(){return J.fetch({url:`${dr}/RenderAppSection/?section=items&appid=${this.appData.appId}`,method:"GET",headers:{Accept:"text/html","X-Requested-With":"XMLHttpRequest"},responseType:"text"}).then(r=>{const p=w("
").html(r.data).find("> .list > .economy-item");for(const a of p.toArray()){const s={appid:this.appData.appId},o=w(a),n=o.find("h4.economy-item-name"),i=n.find("i"),b=y((i.length>0?i:n.textWithoutChildren()).text().trim());s.name=b;const d=n.find("a").text().trim().slice(1);s.itemdefid=d;const m=o.find("div.economy-item-description");m.length>0&&(s.description=y(m.html().trim()));let c=null;for(const u of o.find(".table tr").toArray()){const f=w(u);if(f.hasClass("web-assets-hr")&&(c=f.find("td:first-child").text().slice(0,-1).trim()),c!==null){const h=[];for(const v of f.find("td").toArray()){const T=w(v),F=y(T.text().trim());h.push(F);}s[c]=h.join(";");}else {const h=f.find("td:first-child").text().trim(),v=y(f.find("td:last-child").text().trim());s[h]=v;}f.hasClass("web-assets-bottom")&&(c=null);}this.appData.items[d]=s,this.appData.itemsCount+=1;}return r})}getSteamDBAppDepotPageHashes(){const r=Wt();if(typeof r>"u")return;const p=r("#files.tab-pane .table.depot-files").DataTable().data().toArray();for(const a of p){const s=w("").html(a[0]),o=s.find("a"),n=ot(Z((o.length>0?o:s).textWithoutChildren().text().trim()));let i=w("").html(a[1]).text().trim();const b=typeof a[2]=="object"?a[2].display:a[2];b.length!==0&&(i.length===0&&b==="0"&&(i=Vt),this.appData.depotFileHashes[n]=i,this.appData.depotFileHashesCount+=1);}}getSteamDBAppDepotPageId(){const t=w(".scope-depot[data-depotid]").attr("data-depotid");this.appData.depotId=t;}getSteamDBAppDepotPageManifestId(){const t=w("#files.tab-pane > p").first().find("> b").first().text().trim();this.appData.depotManifestId=t;}}class Yt extends Kt{parserTagKeyRe=/\{(\w+)\}/g;constructor(){super();}parserReplaceDlcsTag(r,t){const a=r.attr("index-start-from-zero")==="true",s=r.attr("index-prefix"),o=typeof s<"u"?Number.parseInt(s):0,n=r.attr("separator"),i=typeof n<"u"?n:` `,b=this.appTabActive==="sk-tab-related-dlc"?this.appData.relatedDLC:this.appOptions.withDLCUnknowns?{...this.appData.dlc,...this.appData.dlcUnknowns}:this.appData.dlc;let d=a?0:1;const m=[];for(const[c,u]of Object.entries(b)){const f=d.toString().padStart(o,"0");m.push(t.replaceAll(this.parserTagKeyRe,(h,v)=>({dlcId:c,dlcIndex:f,dlcName:u})[v])),d+=1;}return m.join(i)}parserReplaceAchievementsTag(r,t){const p=r.attr("separator"),a=typeof p<"u"?p:` `,s=[];for(const o of Object.values(this.appData.achievements))s.push(t.replaceAll(this.parserTagKeyRe,(n,i)=>{const b=o.icon,d=new URL(b).pathname,m=O.basename(d),c=O.parse(d).name,u=o.icongray,f=new URL(u).pathname,h=O.basename(f),v=O.parse(f).name;return {achievementName:o.name,achievementDisplayName:o.displayName,achievementHidden:o.hidden,achievementDescription:o.description,achievementIcon:b,achievementIconBase:m,achievementIconBmp:`${c}.bmp`,achievementIconGray:u,achievementIconGrayBase:h,achievementIconGrayBmp:`${v}.bmp`}[i]}));return s.join(a)}parserReplaceItemsTag(r,t){const p=r.attr("separator"),a=typeof p<"u"?p:` `,s=[];for(const o of Object.values(this.appData.items))s.push(t.replaceAll(this.parserTagKeyRe,(n,i)=>({itemDefId:o.itemdefid,itemData:JSON.stringify(o,null,4)})[i]));return s.join(a)}parserReplaceDataTag(r){return this.appData[r]}parserReplaceTags(r){const p=$("
").html(r).find("dlcs, items, achievements, data").uniqueOutersHTML();for(const a of p){const o=$("
").html(a).children(),n=o.text(),i=o.prop("tagName");let b="";switch(i){case "DLCS":b=this.parserReplaceDlcsTag(o,n);break;case "ITEMS":b=this.parserReplaceItemsTag(o,n);break;case "ACHIEVEMENTS":b=this.parserReplaceAchievementsTag(o,n);break;case "DATA":b=this.parserReplaceDataTag(n);break}r=r.replaceAll(a,b);}return r}parseTags(r){const t=r.callback,p=r.file,a=p.text.replaceAll(/\r\n|\n/g,` @@ -807,6 +807,6 @@ DLC{dlcIndex}={dlcId}\r `;async function Ep(e,r="text/plain",t="utf8"){return new Promise((p,a)=>{const s=new Blob([e],{type:`${r};charset=${t}`}),o=new FileReader;o.addEventListener("load",()=>{o.result!==null?p(o.result):a(new Error("failed to read blob as data url."));}),o.addEventListener("error",n=>{a(n);}),o.readAsDataURL(s);})}async function Mr(e,r,t="text/plain",p="utf8"){const a=await Ep(r,t,p);return new Promise((s,o)=>{Lt({url:a,name:e,onload:()=>{s(true);},onerror:()=>{s(false);},ontimeout:()=>{s(false);}});})}class Tp extends Yt{$wrapper;$body;$modal;$modalNav;$modalBody;$modalTabContent;modalId="sk-modal";constructor(){super(),this.$wrapper=$("
").addClass("pbs-wrapper pbs-revert"),this.$body=$("
").addClass("pbs-body").attr({"data-bs-theme":"dark"}),this.$body.appendTo(this.$wrapper),this.$modal=$(vp).attr({id:this.modalId}),this.$modal.appendTo(this.$body);const r=this.$modal.find("> .pbs-modal-dialog > .pbs-modal-content");r.find("> .pbs-modal-header > .pbs-modal-title").text(this.appTitle),this.$modalBody=r.find("> .pbs-modal-body"),this.$modalNav=this.$modalBody.find("> .pbs-modal-nav"),this.$modalTabContent=this.$modalBody.find("> .pbs-tab-content"),$("