@chicowall/grf-loader
Version:
A loader for GRF files (Ragnarok Online game file)
2 lines • 15.3 kB
JavaScript
"use strict";var le=Object.create;var S=Object.defineProperty;var fe=Object.getOwnPropertyDescriptor;var ue=Object.getOwnPropertyNames;var xe=Object.getPrototypeOf,de=Object.prototype.hasOwnProperty;var he=(n,e)=>{for(var t in e)S(n,t,{get:e[t],enumerable:!0})},V=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of ue(e))!de.call(n,o)&&o!==t&&S(n,o,{get:()=>e[o],enumerable:!(r=fe(e,o))||r.enumerable});return n};var W=(n,e,t)=>(t=n!=null?le(xe(n)):{},V(e||!n||!n.__esModule?S(t,"default",{value:n,enumerable:!0}):t,n)),me=n=>V(S({},"__esModule",{value:!0}),n);var ze={};he(ze,{GRF_ERROR_CODES:()=>oe,GrfBrowser:()=>P,GrfError:()=>h,GrfNode:()=>B,bufferPool:()=>R,countBadChars:()=>U,countC1ControlChars:()=>T,countReplacementChars:()=>w,fixMojibake:()=>k,hasIconvLite:()=>Z,isMojibake:()=>M,normalizeEncodingPath:()=>ee,normalizeFilename:()=>j,toMojibake:()=>J});module.exports=me(ze);var H=W(require("pako"),1),ie=W(require("jdataview"),1);var E=new Uint8Array([128,64,32,16,8,4,2,1]),c=new Uint8Array(8),p=new Uint8Array(8),y=new Uint8Array(8),pe=new Uint8Array([58,50,42,34,26,18,10,2,60,52,44,36,28,20,12,4,62,54,46,38,30,22,14,6,64,56,48,40,32,24,16,8,57,49,41,33,25,17,9,1,59,51,43,35,27,19,11,3,61,53,45,37,29,21,13,5,63,55,47,39,31,23,15,7]),ge=new Uint8Array([40,8,48,16,56,24,64,32,39,7,47,15,55,23,63,31,38,6,46,14,54,22,62,30,37,5,45,13,53,21,61,29,36,4,44,12,52,20,60,28,35,3,43,11,51,19,59,27,34,2,42,10,50,18,58,26,33,1,41,9,49,17,57,25]),be=new Uint8Array([16,7,20,21,29,12,28,17,1,15,23,26,5,18,31,10,2,8,24,14,32,27,3,9,19,13,30,6,22,11,4,25]),K=[new Uint8Array([239,3,65,253,216,116,30,71,38,239,251,34,179,216,132,30,57,172,167,96,98,193,205,186,92,150,144,89,5,59,122,133,64,253,30,200,231,138,139,33,218,67,100,159,45,20,177,114,245,91,200,182,156,55,118,236,57,160,163,5,82,110,15,217]),new Uint8Array([167,221,13,120,158,11,227,149,96,54,54,79,249,96,90,163,17,36,210,135,200,82,117,236,187,193,76,186,36,254,143,25,218,19,102,175,73,208,144,6,140,106,251,145,55,141,13,120,191,73,17,244,35,229,206,59,85,188,162,87,232,34,116,206]),new Uint8Array([44,234,193,191,74,36,31,194,121,71,162,124,182,217,104,21,128,86,93,1,51,253,244,174,222,48,7,155,229,131,155,104,73,180,46,131,31,194,181,124,162,25,216,229,124,47,131,218,247,107,144,254,196,1,90,151,97,166,61,64,11,88,230,61]),new Uint8Array([77,209,178,15,40,189,228,120,246,74,15,147,139,23,209,164,58,236,201,53,147,86,126,203,85,32,160,254,108,137,23,98,23,98,75,177,180,222,209,135,201,20,60,74,126,168,226,125,160,159,246,92,106,9,141,240,15,227,83,37,149,54,40,203])];function Ee(n,e){for(let t=0;t<64;++t){let r=pe[t]-1;n[e+(r>>3&7)]&E[r&7]&&(c[t>>3&7]|=E[t&7])}n.set(c,e),c.set(y)}function ye(n,e){for(let t=0;t<64;++t){let r=ge[t]-1;n[e+(r>>3&7)]&E[r&7]&&(c[t>>3&7]|=E[t&7])}n.set(c,e),c.set(y)}function Ue(n,e){for(let t=0;t<32;++t){let r=be[t]-1;n[e+(r>>3)]&E[r&7]&&(c[(t>>3)+4]|=E[t&7])}n.set(c,e),c.set(y)}function Ae(n,e){c[0]=(n[e+7]<<5|n[e+4]>>3)&63,c[1]=(n[e+4]<<1|n[e+5]>>7)&63,c[2]=(n[e+4]<<5|n[e+5]>>3)&63,c[3]=(n[e+5]<<1|n[e+6]>>7)&63,c[4]=(n[e+5]<<5|n[e+6]>>3)&63,c[5]=(n[e+6]<<1|n[e+7]>>7)&63,c[6]=(n[e+6]<<5|n[e+7]>>3)&63,c[7]=(n[e+7]<<1|n[e+4]>>7)&63,n.set(c,e),c.set(y)}function Fe(n,e){for(let t=0;t<4;++t)c[t]=K[t][n[t*2+0+e]]&240|K[t][n[t*2+1+e]]&15;n.set(c,e),c.set(y)}function Ce(n,e){for(let t=0;t<8;t++)p[t]=n[e+t];Ae(p,0),Fe(p,0),Ue(p,0),n[e+0]^=p[4],n[e+1]^=p[5],n[e+2]^=p[6],n[e+3]^=p[7]}function G(n,e){Ee(n,e),Ce(n,e),ye(n,e)}function X(n,e,t){let r=t.toString().length,o=r<3?1:r<5?r+1:r<7?r+9:r+15,a=e>>3;for(let i=0;i<20&&i<a;++i)G(n,i*8);for(let i=20,l=-1;i<a;++i){if(i%o===0){G(n,i*8);continue}++l&&l%7===0&&_e(n,i*8)}}function Q(n,e){let t=e>>3;for(let r=0;r<20&&r<t;++r)G(n,r*8)}function _e(n,e){c[0]=n[e+3],c[1]=n[e+4],c[2]=n[e+6],c[3]=n[e+0],c[4]=n[e+1],c[5]=n[e+2],c[6]=n[e+5],c[7]=Re[n[e+7]],n.set(c,e),c.set(y)}var Re=(()=>{let n=new Uint8Array([0,43,108,128,1,104,72,119,96,255,185,192,254,235]),e=new Uint8Array(Array.from({length:256},(r,o)=>o)),t=n.length;for(let r=0;r<t;r+=2)e[n[r+0]]=n[r+1],e[n[r+1]]=n[r+0];return e})();var d=null;try{typeof process<"u"&&process.versions?.node&&(d=require("iconv-lite"))}catch{d=null}function Z(){return d!==null}function T(n){let e=0;for(let t of n){let r=t.charCodeAt(0);r>=128&&r<=159&&e++}return e}function w(n){let e=0;for(let t of n)t==="\uFFFD"&&e++;return e}function U(n){return w(n)+T(n)}function N(n,e){let t=e.toLowerCase();if((t==="cp949"||t==="euc-kr")&&d)try{let r=Buffer.from(n);return d.decode(r,"cp949")}catch{}try{let r=t==="cp949"?"euc-kr":t;return new TextDecoder(r,{fatal:!1}).decode(n)}catch{return Array.from(n).map(r=>String.fromCharCode(r)).join("")}}function q(n,e){let t=N(n,e),r=T(t),o=w(t),a=r+o;return{text:t,badChars:a,c1Chars:r,replacementChars:o}}var ve=[/[ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞß][¡-þ]/,/À¯/,/Àú/,/ÀÎ/,/Ÿ/,/Æä/,/ÀÌ/,/½º/,/¾Æ/,/¸ð/,/¸®/,/¿¡/,/Áö/,/µ¥/,/ÅØ/,/½ºÆ®/,/¸ÁÅä/];function M(n){if(!n||n.length===0||/[\uAC00-\uD7AF]/.test(n))return!1;for(let r of ve)if(r.test(n))return!0;let e=0;for(let r of n){let o=r.charCodeAt(0);o>=128&&o<=255&&e++}return e/n.length>.3}function k(n){if(!d)return n;try{let e=d.encode(n,"windows-1252"),t=d.decode(e,"cp949"),r=/[\uAC00-\uD7AF]/.test(t),o=U(t),a=U(n);return r&&o<=a?t:n}catch{return n}}function J(n){if(!d)return n;try{let e=d.encode(n,"cp949");return d.decode(e,"windows-1252")}catch{return n}}function j(n){return M(n)?k(n):n}function ee(n){let t=n.split(/[\\/]/).map(o=>j(o)),r=n.includes("\\")?"\\":"/";return t.join(r)}function te(n,e=.01){if(n.length===0)return"utf-8";let t=0,r=0,o=0,a=0;for(let u of n){if(!u.some(b=>b>127))continue;a++,o+=u.length;let s=q(u,"utf-8"),m=q(u,"cp949");t+=s.badChars,r+=m.badChars}if(a===0)return"utf-8";let i=o>0?t/o:0,l=o>0?r/o:0;return i<e?"utf-8":l<i?"cp949":"utf-8"}var oe={INVALID_MAGIC:"GRF_INVALID_MAGIC",UNSUPPORTED_VERSION:"GRF_UNSUPPORTED_VERSION",NOT_LOADED:"GRF_NOT_LOADED",FILE_NOT_FOUND:"GRF_FILE_NOT_FOUND",AMBIGUOUS_PATH:"GRF_AMBIGUOUS_PATH",DECOMPRESS_FAIL:"GRF_DECOMPRESS_FAIL",CORRUPT_TABLE:"GRF_CORRUPT_TABLE",LIMIT_EXCEEDED:"GRF_LIMIT_EXCEEDED",INVALID_OFFSET:"GRF_INVALID_OFFSET",DECRYPT_REQUIRED:"GRF_DECRYPT_REQUIRED"},h=class extends Error{constructor(t,r,o){super(r);this.code=t;this.context=o;this.name="GrfError"}},Se=1,Te=2,we=4,Pe="Master of Magic",_=46,ne=Uint32Array.BYTES_PER_ELEMENT*2,Be=256*1024*1024,Oe=5e5,Ie=.01;function A(n){return n.toLowerCase().replace(/\\/g,"/")}function re(n){let e=n.lastIndexOf(".");return e===-1||e===n.length-1?"":n.substring(e+1).toLowerCase()}function De(n,e){return N(n,e==="euc-kr"||e==="cp949"?"cp949":e)}var F=class{constructor(e,t){this.fd=e;this.version=512;this.fileCount=0;this.loaded=!1;this.files=new Map;this.normalizedIndex=new Map;this.extensionIndex=new Map;this.fileTableOffset=0;this.cache=new Map;this.cacheMaxSize=50;this.cacheOrder=[];this._stats={fileCount:0,badNameCount:0,collisionCount:0,extensionStats:new Map,detectedEncoding:"utf-8"};this.options={filenameEncoding:t?.filenameEncoding??"auto",autoDetectThreshold:t?.autoDetectThreshold??Ie,maxFileUncompressedBytes:t?.maxFileUncompressedBytes??Be,maxEntries:t?.maxEntries??Oe}}async getStreamReader(e,t){let r=await this.getStreamBuffer(this.fd,e,t);return new ie.default(r,void 0,void 0,!0)}async load(){this.loaded||(await this.parseHeader(),await this.parseFileList(),this.loaded=!0)}async parseHeader(){let e=await this.getStreamReader(0,_),t=e.getString(15);if(t!==Pe)throw new h("INVALID_MAGIC","Not a GRF file (invalid signature)",{signature:t});e.skip(15);let r=e.tell();if(e.seek(42),this.version=e.getUint32(),this.version!==512&&this.version!==768)throw new h("UNSUPPORTED_VERSION",`Unsupported version "0x${this.version.toString(16)}"`,{version:this.version});if(e.seek(r),this.version===512){this.fileTableOffset=e.getUint32()+_;let o=e.getUint32();this.fileCount=e.getUint32()-o-7}else{let o=e.getUint32(),a=e.getUint32();if(a>>>8){this.version=512,e.seek(r),this.fileTableOffset=e.getUint32()+_;let i=e.getUint32();this.fileCount=e.getUint32()-i-7}else this.fileTableOffset=a*4294967296+o+_,this.fileCount=e.getUint32()}if(this.fileCount>this.options.maxEntries)throw new h("LIMIT_EXCEEDED",`File count ${this.fileCount} exceeds limit ${this.options.maxEntries}`,{fileCount:this.fileCount,maxEntries:this.options.maxEntries})}async parseFileList(){let e=this.version===768?4:0,t=await this.getStreamReader(this.fileTableOffset+e,ne),r=t.getUint32(),o=t.getUint32(),a=await this.getStreamBuffer(this.fd,this.fileTableOffset+e+ne,r),i;try{i=H.default.inflate(a)}catch(f){throw new h("CORRUPT_TABLE","Failed to decompress file table",{compressedSize:r,realSize:o,error:f instanceof Error?f.message:String(f)})}if(i.length!==o)throw new h("CORRUPT_TABLE",`File table size mismatch: expected ${o}, got ${i.length}`,{expected:o,actual:i.length});let l=this.options.filenameEncoding;if(this.options.filenameEncoding==="auto"){let f=[],s=0,m=Math.min(200,this.fileCount),b=this.version===768?21:17;for(let x=0;x<m&&s<i.length;x++){let g=s;for(;i[g]!==0&&g<i.length;)g++;let I=i.subarray(s,g);f.push(I),s=g+1+b}l=te(f,this.options.autoDetectThreshold)}this._stats.detectedEncoding=l,this._stats.badNameCount=0,this._stats.collisionCount=0,this._stats.extensionStats.clear();let u=this.version===768?21:17;for(let f=0,s=0;f<this.fileCount;++f){if(s>=i.length)throw new h("CORRUPT_TABLE",`Unexpected end of file table at entry ${f}`,{position:s,dataLength:i.length,entryIndex:f});let m=s;for(;i[m]!==0&&m<i.length;)m++;let b=i.slice(s,m),x=De(b,l);if(U(x)>0&&this._stats.badNameCount++,s=m+1,s+u>i.length)throw new h("CORRUPT_TABLE",`Incomplete entry data at entry ${f}`,{position:s,dataLength:i.length,entryIndex:f});let g=i[s++]|i[s++]<<8|i[s++]<<16|i[s++]<<24,I=i[s++]|i[s++]<<8|i[s++]<<16|i[s++]<<24,ae=i[s++]|i[s++]<<8|i[s++]<<16|i[s++]<<24,ce=i[s++],D;if(this.version===768){let v=(i[s++]|i[s++]<<8|i[s++]<<16|i[s++]<<24)>>>0;D=((i[s++]|i[s++]<<8|i[s++]<<16|i[s++]<<24)>>>0)*4294967296+v}else D=(i[s++]|i[s++]<<8|i[s++]<<16|i[s++]<<24)>>>0;let L={compressedSize:g,lengthAligned:I,realSize:ae,type:ce,offset:D,rawNameBytes:b};if(!(L.realSize>this.options.maxFileUncompressedBytes)&&L.type&Se){this.files.set(x,L);let v=A(x),z=this.normalizedIndex.get(v);z?(z.push(x),this._stats.collisionCount++):this.normalizedIndex.set(v,[x]);let C=re(x);if(C){let Y=this.extensionIndex.get(C);Y?Y.push(x):this.extensionIndex.set(C,[x]),this._stats.extensionStats.set(C,(this._stats.extensionStats.get(C)||0)+1)}}}this._stats.fileCount=this.files.size}decodeEntry(e,t){return t.type&Te?X(e,t.lengthAligned,t.compressedSize):t.type&we&&Q(e,t.lengthAligned),t.realSize===t.compressedSize?e:H.default.inflate(e)}addToCache(e,t){if(this.cacheOrder.length>=this.cacheMaxSize){let r=this.cacheOrder.shift();r&&this.cache.delete(r)}this.cache.set(e,t),this.cacheOrder.push(e)}getFromCache(e){let t=this.cache.get(e);if(t){let r=this.cacheOrder.indexOf(e);r>-1&&(this.cacheOrder.splice(r,1),this.cacheOrder.push(e))}return t}clearCache(){this.cache.clear(),this.cacheOrder=[]}async getFile(e){if(!this.loaded)return Promise.resolve({data:null,error:"GRF not loaded yet"});let t=this.resolvePath(e);if(t.status==="not_found")return Promise.resolve({data:null,error:`File "${e}" not found`});if(t.status==="ambiguous")return Promise.resolve({data:null,error:`Ambiguous path "${e}": ${t.candidates?.length} matches found. Use exact path: ${t.candidates?.slice(0,5).join(", ")}${(t.candidates?.length||0)>5?"...":""}`});let r=t.matchedPath,o=this.getFromCache(r);if(o)return Promise.resolve({data:o,error:null});let a=this.files.get(r);if(!a)return{data:null,error:`File "${r}" not found`};let i=await this.getStreamBuffer(this.fd,a.offset+_,a.lengthAligned);try{let l=this.decodeEntry(i,a);return this.addToCache(r,l),Promise.resolve({data:l,error:null})}catch(l){return{data:null,error:l instanceof Error?l.message:String(l)}}}resolvePath(e){if(this.files.has(e))return{status:"found",matchedPath:e};let t=A(e),r=this.normalizedIndex.get(t);return!r||r.length===0?{status:"not_found"}:r.length===1?{status:"found",matchedPath:r[0]}:{status:"ambiguous",candidates:r}}hasFile(e){return this.resolvePath(e).status==="found"}getEntry(e){let t=this.resolvePath(e);return t.status!=="found"||!t.matchedPath?null:this.files.get(t.matchedPath)||null}find(e={}){let{ext:t,contains:r,endsWith:o,regex:a,limit:i}=e,l=[];if(t&&!r&&!o&&!a){let u=t.toLowerCase().replace(/^\./,"");l=this.extensionIndex.get(u)||[]}else for(let u of this.files.keys()){if(t){let f=t.toLowerCase().replace(/^\./,"");if(re(u)!==f)continue}if(r){let f=A(u),s=A(r);if(!f.includes(s))continue}if(o){let f=A(u),s=A(o);if(!f.endsWith(s))continue}if(!(a&&!a.test(u))&&(l.push(u),i&&l.length>=i))break}return i&&l.length>i&&(l=l.slice(0,i)),l}getFilesByExtension(e){let t=e.toLowerCase().replace(/^\./,"");return this.extensionIndex.get(t)||[]}listExtensions(){return Array.from(this.extensionIndex.keys()).sort()}listFiles(){return Array.from(this.files.keys())}getStats(){return{...this._stats,extensionStats:new Map(this._stats.extensionStats)}}getDetectedEncoding(){return this._stats.detectedEncoding}async reloadWithEncoding(e){this.options.filenameEncoding=e,this.files.clear(),this.normalizedIndex.clear(),this.extensionIndex.clear(),this.clearCache(),this.loaded=!1,await this.load()}};var P=class extends F{constructor(e,t){super(e,t)}async getStreamBuffer(e,t,r){return new Promise((o,a)=>{let i=new FileReader;i.onerror=a,i.onload=()=>o(new Uint8Array(i.result)),i.readAsArrayBuffer(e.slice(t,t+r))})}};var O=require("fs"),se=require("util");var $=class{constructor(){this.pools=new Map;this.maxPoolSize=10;this.poolSizes=[1024,4096,8192,16384,32768,65536,131072,262144];for(let e of this.poolSizes)this.pools.set(e,[])}getPoolSize(e){for(let t of this.poolSizes)if(e<=t)return t;return null}acquire(e){let t=this.getPoolSize(e);if(t===null)return Buffer.allocUnsafe(e);let r=this.pools.get(t);if(r){let o=r.find(a=>!a.inUse);if(o)return o.inUse=!0,o.buffer.subarray(0,e);if(r.length<this.maxPoolSize){let a=Buffer.allocUnsafe(t);return r.push({buffer:a,inUse:!0}),a.subarray(0,e)}}return Buffer.allocUnsafe(e)}release(e){let t=e.buffer.byteLength,r=this.pools.get(t);if(r){let o=r.find(a=>a.buffer===e||a.buffer.buffer===e.buffer);o&&(o.inUse=!1)}}clear(){for(let e of this.pools.values())e.length=0}stats(){let e=[];for(let[t,r]of this.pools.entries())e.push({size:t,total:r.length,inUse:r.filter(o=>o.inUse).length});return e}},R=new $;var Le=(0,se.promisify)(O.read),B=class extends F{constructor(e,t){super(e,t),this.useBufferPool=t?.useBufferPool??!0;try{if(!(0,O.fstatSync)(e).isFile())throw new Error("GRFNode: file descriptor must point to a regular file")}catch{throw new Error("GRFNode: invalid file descriptor")}}async getStreamBuffer(e,t,r){let o=this.useBufferPool?R.acquire(r):Buffer.allocUnsafe(r),{bytesRead:a}=await Le(e,o,0,r,t);if(a!==r)throw this.useBufferPool&&R.release(o),new Error("Not a GRF file (invalid signature)");return o}};0&&(module.exports={GRF_ERROR_CODES,GrfBrowser,GrfError,GrfNode,bufferPool,countBadChars,countC1ControlChars,countReplacementChars,fixMojibake,hasIconvLite,isMojibake,normalizeEncodingPath,normalizeFilename,toMojibake});
//# sourceMappingURL=index.cjs.map