@gmvjs/all 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +45 -2
- package/dist/gmv.iife.js +204 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.js +142 -0
- package/dist/index.js.map +1 -0
- package/package.json +85 -4
package/dist/gmv.iife.js
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
var GMV=(function(st){"use strict";class To extends Error{motionOnly=!0}const vt=t=>t;function Cl(t,...e){const n=t.base??t,s=(n.games??[]).filter(a=>e.includes(a.id)),o=e.filter(a=>!s.some(i=>i.id===a));if(o.length)throw new Error(`gmv: в движке ${n.id} нет пресетов ${o.join(", ")}`);const r=new Set(s.flatMap(a=>a.formats));return{...n,base:n,games:s,formats:(n.formats??[]).filter(a=>r.has(a.id))}}const is={groups:{},presets:{},exporters:{}};function lm(t){const e=t.i18n?.en;e&&(Object.assign(is.groups,e.groups),Object.assign(is.presets,e.presets),Object.assign(is.exporters,e.exporters))}let Cn="ru";function cm(t){Cn=t}function fm(){return Cn}const um={"session.readingArchive":{ru:"Чтение архива {name}…",en:"Reading archive {name}…"},"session.archiveError":{ru:"Ошибка архива: {error}",en:"Archive error: {error}"},"session.archives":{ru:"Архивов: {n}",en:"Archives: {n}"},"session.archivesSkipped":{ru:"Архивов: {n}; пропущено файлов: {k} ({list})",en:"Archives: {n}; files skipped: {k} ({list})"},"session.indexing":{ru:"Индексация папки: файлов {n}",en:"Indexing folder: {n} files"},"session.folderTextures":{ru:"Индексация текстур папки…",en:"Indexing folder textures…"},"session.findArchives":{ru:"Поиск архивов в папке…",en:"Looking for archives in the folder…"},"session.loadingName":{ru:"Загрузка {name}…",en:"Loading {name}…"},"session.companions":{ru:"Запомнено: {list} — теперь выберите сами архивы",en:"Remembered: {list} — now choose the archives themselves"},"session.folder":{ru:"Папка «{name}»: файлов {n}",en:'Folder "{name}": {n} files'},"session.dict":{ru:"{id} {name}: текстур {n}",en:"{id} {name}: {n} textures"},"session.dictErrors":{ru:", ошибок {n} ({first})",en:", {n} errors ({first})"},"session.dictError":{ru:"Ошибка {id}: {error}",en:"{id} error: {error}"},"session.noModel":{ru:"Нет файла модели, подходящего под выбранный формат",en:"No model file matches the selected format"},"session.loading":{ru:"Загрузка…",en:"Loading…"},"session.loaded":{ru:"Загружено: {name}",en:"Loaded: {name}"},"session.error":{ru:"Ошибка: {error}",en:"Error: {error}"},"session.noMotionModel":{ru:"Сначала загрузите модель, поддерживающую внешние анимации",en:"Load a model that supports external animations first"},"session.motionsAdded":{ru:"Добавлено анимаций: {n} ({name})",en:"Animations added: {n} ({name})"},"session.motionError":{ru:"Ошибка анимации: {error}",en:"Animation error: {error}"},"tex.noFile":{ru:"нет файла",en:"no file"},"tex.error":{ru:"ошибка: {error}",en:"error: {error}"},"tex.normalNoFile":{ru:"N нет файла",en:"N no file"},"tex.pbrFactors":{ru:" (множители)",en:" (factors)"},"scene.model":{ru:"Модель",en:"Model"},"scene.geometry":{ru:"Геометрия уровня",en:"Level geometry"},"scene.objects":{ru:"Объекты",en:"Objects"},"scene.otherObjects":{ru:"объекты",en:"objects"},"scene.mesh":{ru:"меш {i}",en:"mesh {i}"},"scene.meshes":{ru:"{n} меш.",en:"{n} meshes"},"scene.tris":{ru:"{n} тр.",en:"{n} tris"}};function yt(t,e={}){const n=um[t];return(n?n[Cn]:t).replace(/\{(\w+)\}/g,(o,r)=>e[r]!==void 0?String(e[r]):`{${r}}`)}function hm(t){const e=Cn==="en"?is.presets[t.id]:void 0;return{title:e?.title??t.title,hint:e?.hint??t.hint??"",group:Cn==="en"?is.groups[t.group]??t.group:t.group,archiveLabel:e?.archiveLabel??t.ui?.archiveLabel,motionLabel:e?.motionLabel??t.ui?.motionLabel}}function dm(t,e=""){return t?Cn==="en"&&t.i18n?.en?.title||t.title:e}function pm(t){const e=Cn==="en"?is.exporters[t.id]:void 0;return{title:e?.title??t.title,description:e?.description??t.description,field:n=>{const s=e?.fields?.[n.id];return{label:s?.label??n.label,hint:s?.hint??n.hint,placeholder:s?.placeholder??n.placeholder}}}}function Ot(t,e,n){let s="";for(let o=0;o<n&&t[e+o];o++)s+=String.fromCharCode(t[e+o]);return s}function Uo(t,e,n){let s="";for(let o=e;o<n&&t[o];o++)s+=String.fromCharCode(t[o]);return s}function Ln(t,e,n){let s="";for(let o=e;o<n;o++)s+=String.fromCharCode(t[o]);return s}const en=t=>(t.charCodeAt(0)|t.charCodeAt(1)<<8|t.charCodeAt(2)<<16|t.charCodeAt(3)<<24)>>>0;function Fe(t){const e=t&32768?-1:1,n=t>>10&31,s=t&1023;return n===0?e*5960464477539063e-23*s:n===31?s?NaN:e*(1/0):e*2**(n-15)*(1+s/1024)}function Rn(t){return t.buffer.slice(t.byteOffset,t.byteOffset+t.byteLength)}async function Fo(t,e=64){if(typeof Blob<"u"&&t instanceof Blob)return new Uint8Array(await t.slice(0,e).arrayBuffer());const n=await t.arrayBuffer();return new Uint8Array(n,0,Math.min(e,n.byteLength))}function It(t,e,n=0){if(t.length<n+e.length)return!1;for(let s=0;s<e.length;s++)if(t[n+s]!==e.charCodeAt(s))return!1;return!0}const At=(t,e)=>t.length>=e+4?(t[e]|t[e+1]<<8|t[e+2]<<16|t[e+3]<<24)>>>0:-1,Ee=t=>{const e=t.lastIndexOf(".");return e<0?"":t.slice(e+1).toLowerCase()},gt=t=>t.replace(/\\/g,"/").toLowerCase(),On=t=>t.replace(/\.[^./]+$/,""),Ge=t=>t.replace(/\\/g,"/").split("/").pop()??t,mm=new TextDecoder,Wt=t=>mm.decode(t),gm=Object.freeze(Object.defineProperty({__proto__:null,ascii:Ln,baseName:Ge,cstr:Ot,cstrRange:Uo,decodeText:Wt,extOf:Ee,fourCC:en,half:Fe,hasMagic:It,normPath:gt,readHead:Fo,stripExt:On,toArrayBuffer:Rn,u32le:At},Symbol.toStringTag,{value:"Module"}));class Ll{engines=[];formats=[];archives=[];codecs=[];dictionaries=[];games=[];materials=[];exporters=[];constructor(e=[]){for(const n of e)this.use(n)}use(e){const n=this.engines.findIndex(o=>o.id===e.id);if(n>=0){const o=this.engines[n],r=e.base??e;o.base&&e.base&&o.base===e.base&&(e=Cl(r,...new Set([...o.games??[],...e.games??[]].map(a=>a.id)))),this.remove(e.id)}lm(e),this.engines.push(e);const s=(o,r,a)=>{for(const i of r??[]){if(o.some(l=>l.id===i.id))throw new Error(`gmv: ${a} с id «${i.id}» уже зарегистрирован`);o.push(i)}};return s(this.formats,e.formats,"формат"),s(this.archives,e.archives,"архив"),s(this.codecs,e.codecs,"кодек"),s(this.dictionaries,e.dictionaries,"словарь"),s(this.games,e.games,"пресет"),s(this.materials,e.materials,"материалы"),s(this.exporters,e.exporters,"экспорт"),this}remove(e){const n=this.engines.findIndex(r=>r.id===e);if(n<0)return;const s=this.engines[n];this.engines.splice(n,1);const o=(r,a)=>{for(const i of a??[]){const l=r.indexOf(i);l>=0&&r.splice(l,1)}};o(this.formats,s.formats),o(this.archives,s.archives),o(this.codecs,s.codecs),o(this.dictionaries,s.dictionaries),o(this.games,s.games),o(this.materials,s.materials),o(this.exporters,s.exporters)}exportersFor(e,n){return this.exporters.filter(s=>(!s.onlyFrom||s.from?.includes(n))&&(!s.accepts||s.accepts(e))).map(s=>({exporter:s,recommended:!!s.from?.includes(n)})).sort((s,o)=>Number(o.recommended)-Number(s.recommended))}format(e){return this.formats.find(n=>n.id===e)}game(e){return this.games.find(n=>n.id===e)}engine(e){return this.engines.find(n=>n.id===e)}presetAvailable(e){return e.formats.some(n=>!!this.format(n))}presetExtensions(e){return e.extensions?e.extensions:[...new Set(e.formats.flatMap(n=>this.format(n)?.extensions??[]))]}presetMotionExtensions(e){return[...new Set(e.formats.flatMap(n=>this.format(n)?.motionExtensions??[]))]}presetForFormat(e,n){const s=n?Ee(n):"";return s&&this.games.find(o=>o.formats.includes(e)&&this.presetExtensions(o).includes(s))||this.games.find(o=>o.formats.includes(e))}get motionOnlyExtensions(){const e=new Set(this.formats.flatMap(n=>n.extensions));return[...new Set(this.formats.flatMap(n=>n.motionExtensions??[]))].filter(n=>!e.has(n))}get modelExtensions(){return[...new Set(this.formats.flatMap(e=>[...e.extensions,...e.motionExtensions??[]]))]}isPathModel(e,n){return this.formats.some(s=>s.pathPattern?.test(e)&&(!n||n.includes(s.id)))}archiveExtensions(){return[...new Set(this.archives.flatMap(e=>[...e.extensions,...e.companionExtensions??[]]))]}isArchive(e){return this.archiveExtensions().includes(Ee(e))}dictionaryFor(e){const n=Ee(e);return this.dictionaries.find(s=>s.extensions.includes(n))}codecFor(e){const n=Ee(e);return this.codecs.find(s=>s.extensions.includes(n))}isImage(e){return!!this.codecFor(e)||/\.(png|jpe?g|bmp|webp|gif)$/i.test(e)}async archiveFor(e){const n=Ee(e.name),s=this.archives.filter(i=>i.extensions.includes(n));if(s.length<=1)return s[0]??null;const o=await Fo(e,64);let r=null,a=-1;for(const i of s){const l=i.sniff?i.sniff(o,e.name):.1;l>a&&(a=l,r=i)}return a>0?r:s.find(i=>i.id==="zip")??null}async detect(e,n=[]){const s=Ee(e.name);let o=this.formats.filter(c=>c.extensions.includes(s));const r=!o.length;if(r&&(o=this.formats.filter(c=>c.sniff)),!o.length)return null;const i=r||o.some(c=>c.sniff)?await Fo(e,256):null,l=o.map(c=>{let f=r?0:.5;if(i&&c.sniff){const h=c.sniff(i,e.name);if(h<=0)return{format:c,confidence:0};f+=(r?1:.5)*h}return n.includes(c.id)&&(f+=.25),{format:c,confidence:Math.min(1,f),rank:f}}).filter(c=>c.confidence>(r?.6:0)).sort((c,f)=>(f.rank??0)-(c.rank??0)).map(({format:c,confidence:f})=>({format:c,confidence:f}));return l.length?{format:l[0].format,confidence:l[0].confidence,candidates:l}:null}async decodeImage(e){if(e.decoded)return e.decoded;const n=await e.arrayBuffer(),s=this.codecFor(e.name);if(s)return s.decode(n,e.name);if(typeof createImageBitmap!="function")throw new Error(`нет декодера для ${e.name}`);const o=await createImageBitmap(new Blob([n]),{premultiplyAlpha:"none",colorSpaceConversion:"none"});return{bitmap:o,width:o.width,height:o.height}}}const wm=[[/_c[oa]$/,["_nohq","_nofhq","_no"]],[/__d(_a)?$/,["__n_d","__n"]],[/_diff(use)?(?=_\d{3}|$)/,["_normal","_n","_ddn","_nrm"]],[/_b\.color$/,["_nm.normal","_n.normal","_nm","_n"]],[/_(albedo|basecolor|base_color|color|col|alb)$/,["_normal","_n","_nrm","_nm"]],[/_d$/,["_n","_nm","_normal","_ddn"]],[/_c$/,["_n","_nm"]],[/_dif$/,["_nrm","_nor","_n"]]],ym=["_n","_normal","_nm","_nrm","_norm","_ddn","_local","_bump_n","_nohq"];function Rl(t){if(!t)return[];const e=t.replace(/\\/g,"/"),n=e.slice(0,e.lastIndexOf("/")+1),s=Ge(e).replace(/\.(dds|tga|png|jpe?g|paa|pac|pct|vtf|bmp|pcx|gif|webp|wal|xbt)$/i,"").toLowerCase(),o=new Set;for(const[a,i]of wm)if(a.test(s))for(const l of i)o.add(s.replace(a,l));const r=/^([a-z]+)_diff_(\d{3})_[a-z]_[a-z]+$/.exec(s);r&&o.add(`${r[1]}_normal_${r[2]}`);for(const a of ym)o.add(s+a);return o.delete(s),[...o].map(a=>n+a)}function Ol(t){const e=t.rgba;if(!e)return{normal:!0,packing:"rgb"};const n=t.width*t.height,s=Math.max(1,Math.floor(n/4096));let o=0,r=0,a=0,i=0,l=0,c=0,f=0,h=0;const u=e[0],d=e[3];for(let p=0;p<n;p+=s){const m=p*4,g=e[m],w=e[m+1],y=e[m+2],M=e[m+3];r+=g,a+=w,i+=y,l+=M,o++,c+=Math.abs(g-u),f+=Math.abs(M-d);const x=g/127.5-1,v=w/127.5-1,b=y/127.5-1,k=Math.hypot(x,v,b);k>.75&&k<1.25&&b>-.1&&h++}return r/=o,a/=o,i/=o,l/=o,c/=o,f/=o,(r<16||r>239||c<6)&&l>60&&l<196&&(f>2||Math.abs(l-128)<24)&&a>60&&a<196?{normal:!0,packing:"ag"}:i<40&&r>60&&r<196&&a>60&&a<196?{normal:!0,packing:"rg"}:{normal:i>150&&h/o>.6&&r>70&&r<185&&a>70&&a<185,packing:"rgb"}}function Il(t){const e=t.vertexCount,n=new Float32Array(e*3),s=new Float32Array(e*3),o=new Float32Array(e*4),r=t.pos,a=t.uv,i=t.indices;for(let l=0;l+2<i.length;l+=3){const c=i[l],f=i[l+1],h=i[l+2],u=r[f*3]-r[c*3],d=r[f*3+1]-r[c*3+1],p=r[f*3+2]-r[c*3+2],m=r[h*3]-r[c*3],g=r[h*3+1]-r[c*3+1],w=r[h*3+2]-r[c*3+2],y=a[f*2]-a[c*2],M=a[f*2+1]-a[c*2+1],x=a[h*2]-a[c*2],v=a[h*2+1]-a[c*2+1],b=y*v-x*M;if(Math.abs(b)<1e-12)continue;const k=1/b,E=k*(v*u-M*m),A=k*(v*d-M*g),S=k*(v*p-M*w),$=k*(y*m-x*u),T=k*(y*g-x*d),U=k*(y*w-x*p);for(const _ of[c,f,h])n[_*3]+=E,n[_*3+1]+=A,n[_*3+2]+=S,s[_*3]+=$,s[_*3+1]+=T,s[_*3+2]+=U}for(let l=0;l<e;l++){const c=t.nor[l*3],f=t.nor[l*3+1],h=t.nor[l*3+2];let u=n[l*3],d=n[l*3+1],p=n[l*3+2];const m=u*c+d*f+p*h;u-=c*m,d-=f*m,p-=h*m;let g=Math.hypot(u,d,p);if(g<1e-8){const v=Math.abs(c)<.9?1:0,b=v?0:1;u=b*h-0*f,d=0*c-v*h,p=v*f-b*c,g=Math.hypot(u,d,p)||1}u/=g,d/=g,p/=g;const w=f*p-h*d,y=h*u-c*p,M=c*d-f*u,x=w*s[l*3]+y*s[l*3+1]+M*s[l*3+2]<0?-1:1;o[l*4]=u,o[l*4+1]=d,o[l*4+2]=p,o[l*4+3]=x}return o}function Hr(t){if(!t)return[];const e=t.replace(/\\/g,"/"),n=e.slice(0,e.lastIndexOf("/")+1),s=Ge(e).replace(/\.(dds|tga|png|jpe?g|paa|pac|pct|vtf|bmp|pcx|gif|webp|wal|xbt)$/i,"").toLowerCase().replace(/_(d|diff|diffuse|albedo|basecolor|base_color|color|col|c|co|b\.color)$/,""),o=a=>n+s+a,r=(a,i)=>({map:a,channel:i});return[{ao:r(o("_orm"),0),roughness:r(o("_orm"),1),metallic:r(o("_orm"),2),source:"_orm"},{roughness:r(o("_rma"),0),metallic:r(o("_rma"),1),ao:r(o("_rma"),2),source:"_rma"},{metallic:r(o("_mra"),0),roughness:r(o("_mra"),1),ao:r(o("_mra"),2),source:"_mra"},{roughness:r(o("_roughness"),0),metallic:r(o("_metallic"),0),ao:r(o("_ao"),0),source:"_roughness"},{roughness:r(o("_rough"),0),metallic:r(o("_metal"),0),source:"_rough"},{specular:r(o("_smdi"),1),gloss:r(o("_smdi"),2),source:"Arma _smdi"}]}const bm=["ao","roughness","gloss","metallic","specular"];function Dl(t){return[...new Set(bm.map(e=>t[e]?.map).filter(e=>!!e&&!e.startsWith("#")))]}function Pl(t,e){let n=1,s=1;for(const f of e.values())f.width*f.height>n*s&&(n=f.width,s=f.height);const o=new Uint8Array(n*s*4),r=Math.round(255*Math.min(1,Math.max(0,t.roughnessFactor??1))),a=Math.round(255*Math.min(1,Math.max(0,t.metallicFactor??1))),i=(f,h,u)=>{const d=f?f.image??e.get(f.map):void 0;if(!f||!d?.rgba)return null;const p=Math.min(d.width-1,h*d.width/n|0),m=Math.min(d.height-1,u*d.height/s|0);return d.rgba[(m*d.width+p)*4+f.channel]},l=!!(t.roughness||t.gloss),c=!!t.metallic;for(let f=0;f<s;f++)for(let h=0;h<n;h++){const u=(f*n+h)*4,d=i(t.ao,h,f),p=i(t.roughness,h,f),m=i(t.gloss,h,f),g=i(t.metallic,h,f),w=i(t.specular,h,f);o[u]=d??255,o[u+1]=p!==null?p*r/255:m!==null?255-m:l||t.roughnessFactor!==void 0?r:153,o[u+2]=g!==null?g*a/255:c||t.metallicFactor!==void 0?a:0,o[u+3]=w??(t.specularFactor!==void 0?Math.round(255*Math.min(1,Math.max(0,t.specularFactor))):128)}return{width:n,height:s,rgba:o}}const Wr=/\.(dds|tga|png|jpe?g|pcx|bmp|webp|paa|pac|pct|tex|xbt)$/;function Qt(t){const e=new Map,n=new Map,s=new Map;for(const r of t.entries)if(e.set(r.path,r),Wr.test(r.path))for(const a of t.textureKeys?t.textureKeys(r):[On(r.path)])(!t.keepFirstTextureKey||!n.has(a))&&n.set(a,r);const o={name:t.name,kind:t.kind,version:t.version,entries:t.entries,index:e,byKey:n,byBase:t.byBase,lookup:r=>t.lookup?t.lookup(r):e.get(r)??null,read:r=>t.read(r),blobOf:t.blobOf?r=>t.blobOf(r):void 0,findTexture:t.findTexture?r=>t.findTexture(r):void 0,fileOf(r){let a=s.get(r);return a||(a={name:t.displayName?t.displayName(r):Ge(r.path),path:r.path,archive:o,arrayBuffer:t.fileBytes?()=>t.fileBytes(r):async()=>Rn(await t.read(r))},s.set(r,a)),a}};return o}const ht=async(t,e,n)=>new Uint8Array(await t.slice(e,n).arrayBuffer()),vm=t=>(t.webkitRelativePath||t.name).replace(/\\/g,"/");class Nl{entries=[];full=new Map;byBase=new Map;bySuffix=new Map;root="";add(e){const n=vm(e),s=n.toLowerCase();!this.root&&n.includes("/")&&(this.root=n.split("/")[0]);const o={path:s,size:e.size,src:e};this.entries.push(o),this.full.has(s)||this.full.set(s,o);const r=s.split("/"),a=r[r.length-1];this.byBase.has(a)||this.byBase.set(a,o);for(let i=0;i<r.length;i++){const l=r.slice(i).join("/");this.bySuffix.has(l)||this.bySuffix.set(l,o)}}archive(){const{full:e,bySuffix:n}=this;return Qt({name:this.root||"папка",kind:"folder",version:0,entries:this.entries,byBase:this.byBase,lookup:s=>e.get(s)??n.get(s)??null,read:async s=>new Uint8Array(await s.src.arrayBuffer()),fileBytes:s=>s.src.arrayBuffer(),blobOf:s=>s.src,displayName:s=>s.src.name,keepFirstTextureKey:!0,textureKeys:s=>{const o=On(s.path).split("/");return Wr.test(s.path)?o.map((r,a)=>o.slice(a).join("/")):[]}})}}function zl(t){const e=new Nl;for(const n of t)e.add(n);return e.archive()}async function xm(t,e,n=5e3){const s=new Nl;for(let o=0;o<t.length;o+=n){e?.(o,t.length),await new Promise(r=>setTimeout(r,0));for(let r=o;r<Math.min(t.length,o+n);r++)s.add(t[r])}return e?.(t.length,t.length),await new Promise(o=>setTimeout(o,0)),s.archive()}class zs{constructor(e,n){this.decoder=e,this.archives=n}decoder;archives;loose=[];dict=new Map;cache=new WeakMap;memoMap=new Map;addFiles(e){for(const n of e){const s=On(gt(n.webkitRelativePath||n.name));this.loose.push({file:n,key:s,base:s.split("/").pop()})}}addDecoded(e,n,s="dict"){this.dict.set(e.toLowerCase(),{name:`${e}.${s}`,decoded:n,arrayBuffer:async()=>new ArrayBuffer(0)})}get looseCount(){return this.loose.length}static candidates(e){const s=On(gt(e)).replace(/^[a-z]:\//,"").replace(/^(\.\/)+/,"").replace(/^\/+/,"").split("/"),o=[];for(let r=0;r<s.length;r++){const a=s.slice(r).join("/");if(r>0&&s[r-1]==="data"){o.unshift(a);continue}o.push(a)}return[...new Set(o)]}find(e){if(!e)return null;const n=zs.candidates(e),o=(n.includes(On(gt(e)))?On(gt(e)):n[0]).split("/").pop(),r=this.dict.get(o);if(r)return r;for(const l of n){for(const c of this.archives){const f=c.byKey.get(l)??c.byKey.get("textures/"+l)??c.byKey.get("models/"+l);if(f)return c.fileOf(f)}if(l.includes("/")||l===o){const c=this.loose.find(f=>f.key===l)??this.loose.find(f=>f.key.endsWith("/"+l));if(c)return c.file}}const a=this.loose.find(l=>l.base===o);if(a)return a.file;for(const l of this.archives){const c=l.findTexture?.(e);if(c)return l.fileOf(c)}const i=this.baseIndex().get(o);return i?i[0].fileOf(i[1]):null}baseIndex(){const e="\0baseIndex:"+this.archives.length;let n=this.memoMap.get(e);if(!n){n=new Map;for(const s of this.archives)for(const[o,r]of s.byKey){const a=o.slice(o.lastIndexOf("/")+1);n.has(a)||n.set(a,[s,r])}this.memoMap.set(e,n)}return n}findEntry(e){for(const n of[e,e.replace(/^materials\//,"")])for(const s of this.archives){const o=s.lookup(n);if(o)return[s,o]}return null}findByBase(e){for(const n of this.archives){const s=n.byBase?.get(e);if(s)return[n,s]}return null}async readText(e){return Wt(await e[0].read(e[1]))}async memo(e,n){if(this.memoMap.has(e))return this.memoMap.get(e);const s=await n();return this.memoMap.set(e,s),s}invalidate(){this.memoMap=new Map}async decode(e){if(e.decoded)return e.decoded;let n=this.cache.get(e);return n||(n=await this.decoder.decodeImage(e),this.cache.set(e,n)),n}}const Mm=80;class Bl{constructor(e,n){if(this.registry=e,!e.games.length)throw new Error("gmv: в реестре нет ни одного пресета");this.preset=n&&e.game(n)||e.games[0],this.textures=new zs(e,this.archives)}registry;archives=[];companions=[];textures;preset;model=null;format=null;main=null;motions=[];lastFailure=null;overrides=new Map;mapsCache=new Map;engineMaps(e){const n=this.format,s=this.model;if(!n?.resolveMaps||!s)return Promise.resolve(null);const o=`${e.texture}|${e.shader??""}`;let r=this.mapsCache.get(o);return r||(r=n.resolveMaps(e,s,this.textures).catch(a=>(console.warn(a),null)),this.mapsCache.set(o,r)),r}listeners=new Map;on(e,n){let s=this.listeners.get(e);return s||this.listeners.set(e,s=new Set),s.add(n),()=>s.delete(n)}emit(e,n){for(const s of this.listeners.get(e)??[])s(n)}status(e,n=!1){this.emit("status",{text:e,error:n})}progress(e,n,s){this.emit("progress",e===void 0?null:{text:e,done:n,total:s})}yieldUI(){return new Promise(e=>setTimeout(e,0))}setPreset(e){const n=this.registry.game(e)??this.registry.games[0];return this.preset=n,this.emit("preset",n),n}get modelExtensions(){return this.registry.presetExtensions(this.preset)}get motionExtensions(){const e=this.registry.presetMotionExtensions(this.preset);return this.preset.ui?.motionAny?[...new Set([...e,...this.modelExtensions])]:e}async addFiles(e){const n=Array.from(e),s=this.registry,o=this.registry.motionOnlyExtensions,r=n.filter(f=>s.isArchive(f.name)),a=n.filter(f=>!s.isArchive(f.name)&&s.dictionaryFor(f.name)),i=n.filter(f=>!s.isArchive(f.name)&&s.isImage(f.name)),l=n.filter(f=>o.includes(Ee(f.name))),c=n.filter(f=>!r.includes(f)&&!a.includes(f)&&!i.includes(f)&&!l.includes(f));r.length&&await this.addArchives(r);for(const f of a)await this.addTextureDictionary(f);i.length&&this.addTextures(i),c.length&&await this.loadModel(c);for(const f of l)await this.loadMotions(f)}async addArchives(e){const n=[];let s=0;const o=a=>this.registry.archives.some(i=>i.companionExtensions?.includes(Ee(a.name)));for(const a of e)o(a)&&!this.companions.some(i=>i.name===a.name&&i.size===a.size)&&this.companions.push(a);const r=[...e,...this.companions.filter(a=>!e.includes(a))];if(e.every(o)){this.status(yt("session.companions",{list:e.map(a=>a.name).join(", ")}));return}try{for(const[a,i]of e.entries())try{const l=await this.registry.archiveFor(i);if(!l)continue;this.status(yt("session.readingArchive",{name:i.name})),this.progress(yt("session.readingArchive",{name:i.name}),a,e.length),await this.yieldUI();const c=await l.open(i,r);c&&(this.archives.push(c),s++)}catch(l){console.error(l),n.push(`${i.name}: ${In(l)}`)}}finally{this.progress()}n.length?(this.archivesChanged(yt("session.archivesSkipped",{n:this.archives.length,k:n.length,list:n.slice(0,3).join("; ")+(n.length>3?"…":"")})),s||this.status(yt("session.archiveError",{error:n.slice(0,3).join("; ")}),!0)):this.archivesChanged(yt("session.archives",{n:this.archives.length}))}async addFolderAsync(e){try{const n=await xm(e,(s,o)=>this.progress(yt("session.indexing",{n:o}),s,o));return this.archives.push(n),this.progress(yt("session.folderTextures")),await this.yieldUI(),this.archivesChanged(yt("session.folder",{name:n.name,n:n.entries.length})),n}finally{this.progress()}}addFolder(e){const n=zl(e);return this.archives.push(n),this.archivesChanged(yt("session.folder",{name:n.name,n:n.entries.length})),n}async openFolderArchives(e){if(!e.blobOf)return 0;const n=new Set(this.registry.archives.filter(i=>i.openFromFolder).flatMap(i=>i.extensions)),s=new Set(this.registry.archives.filter(i=>i.openFromFolder).flatMap(i=>i.companionExtensions??[])),o=[],r=[];this.progress(yt("session.findArchives"));for(const i of e.entries){if(s.has(Ee(i.path))&&!n.has(Ee(i.path))){const h=e.blobOf(i);h&&r.push(h);continue}if(!n.has(Ee(i.path)))continue;const l=e.blobOf(i),c=l?await this.registry.archiveFor(l):null;if(!l||!c?.openFromFolder)continue;const f=new Uint8Array(await l.slice(0,64).arrayBuffer());c.sniff&&!c.sniff(f,l.name)||typeof c.openFromFolder=="function"&&!c.openFromFolder(f,l.name)||o.push(l)}if(this.progress(),!o.length)return 0;const a=this.archives.length;return await this.addArchives([...o,...r]),this.archives.length-a}archivesChanged(e){this.textures.invalidate(),this.status(e),this.emit("archives",this.archives),this.emit("textures",void 0)}addTextures(e){this.textures.addFiles(e.filter(n=>this.registry.isImage(n.name))),this.emit("textures",void 0)}async addTextureDictionary(e){const n=this.registry.dictionaryFor(e.name);if(n)try{const{textures:s,errors:o}=await n.parse(await e.arrayBuffer());for(const r of s)this.textures.addDecoded(r.name,r,n.id);this.status(yt("session.dict",{id:n.id.toUpperCase(),name:e.name,n:s.length})+(o.length?yt("session.dictErrors",{n:o.length,first:o[0]}):""),o.length>0&&!s.length),this.emit("textures",void 0)}catch(s){console.error(s),this.status(yt("session.dictError",{id:n.id.toUpperCase(),error:In(s)}),!0)}}lastLoad=null;async setModelOption(e,n){if(!this.lastLoad||!this.main)return null;const s={...this.lastLoad.options,[e]:n};return this.loadModel([this.main,...this.lastLoad.files.filter(o=>o!==this.main)],s,!0)}async loadModel(e,n={},s=!1){let o=null,r=null,a=null;for(const i of e){const l=await this.registry.detect(i,this.preset.formats);if(l){if(this.preset.formats.includes(l.format.id)){o=i,r=l.format;break}a||(a={file:i,format:l.format})}}if(!o&&a){o=a.file,r=a.format;const i=this.registry.presetForFormat(r.id,o.name);i&&this.setPreset(i.id)}if(!o||!r)return this.status(yt("session.noModel"),!0),null;this.status(yt("session.loading")),this.progress(yt("session.loadingName",{name:o.name})),await this.yieldUI();try{const i=await r.load(o,this.loadContext(e,o,n));return this.lastLoad={files:e,options:n},i.normalY??=this.registry.engine(r.engine)?.normalY??"dx",this.model=i,this.format=r,this.main=o,this.lastFailure=null,this.overrides.clear(),this.mapsCache.clear(),this.motions.length=0,this.motions.push(...i.motions.map(l=>({...l,source:l.source??"model"}))),this.emit("model",{model:i,main:o,format:r,keepView:s}),r.afterLoad&&await r.afterLoad(this.afterLoadContext(o,i)),this.emit("motions",this.motions),this.status(yt("session.loaded",{name:o.name})),i}catch(i){return i instanceof To&&this.model?.loadMotions?(await this.loadMotions(o),this.model):(console.error(i),this.lastFailure={file:o,format:r.id,error:In(i)},this.status(yt("session.error",{error:In(i)}),!0),null)}finally{this.progress()}}loadContext(e,n,s={}){const o=this.archives;return{files:e,archives:o,options:s,async sibling(r){const a=r.toLowerCase(),i=e.find(l=>l.name.toLowerCase()===a);if(i)return i.arrayBuffer();if(n.path){const l=n.path.replace(/[^/]*$/,"");for(const c of o){const f=c.lookup(l+a);if(f)return Rn(await c.read(f))}}return null},async readPath(r){for(const a of o){const i=a.lookup(r);if(i)return a.read(i)}return null}}}afterLoadContext(e,n){return{main:e,model:n,textures:this.textures,addMotions:(s,o)=>{this.motions.push(...s.map(r=>({...r,source:o})))},addTextureDictionary:s=>this.addTextureDictionary(s)}}async loadMotions(e){if(!this.model?.loadMotions)return this.status(yt("session.noMotionModel"),!0),[];try{const n=await this.model.loadMotions(e);return this.motions.push(...n.map(s=>({...s,source:e.name}))),this.emit("motions",this.motions),this.status(yt("session.motionsAdded",{n:n.length,name:e.name})),n}catch(n){return console.error(n),this.status(yt("session.motionError",{error:In(n)}),!0),[]}}setTextureOverride(e,n){this.overrides.set(e,n)}async textureSource(e){const n=this.model,s=n?.meshes[e];return!n||!s?null:this.findTextureSource(n,s,e)}async findTextureSource(e,n,s){let o=this.overrides.get(s)??null;if(!o&&n.image&&(o={name:"embedded",decoded:n.image,arrayBuffer:async()=>new ArrayBuffer(0)}),o||(o=this.textures.find(n.texture)),!o&&this.format?.resolveTexture)try{o=await this.format.resolveTexture(n,e,this.textures)}catch(r){console.warn(r)}for(const r of this.registry.materials){if(o)break;try{o=await r.resolve(n,this.textures)}catch(a){console.warn(a)}}return!o&&n.texture&&(o=this.sameFolderTexture(n.texture)),o}async resolveTexture(e){const n=this.model,s=n?.meshes[e];if(!n||!s)return{meshIndex:e,image:null,label:"",ok:!1};const o=await this.findTextureSource(n,s,e);if(!o)return{meshIndex:e,image:null,label:s.texture?yt("tex.noFile"):"",ok:!1};try{const r=await this.textures.decode(o);return{meshIndex:e,image:r,label:`${r.width}×${r.height}`,ok:!0,source:o.path??o.name}}catch(r){return{meshIndex:e,image:null,label:yt("tex.error",{error:In(r)}),ok:!1}}}async resolveNormal(e,n){const s=this.model?.meshes[e],o={meshIndex:e,image:null,packing:"rgb",label:"",ok:!1};if(!s)return o;const r=(await this.engineMaps(s))?.normal;if(r)return{meshIndex:e,image:r.image,packing:r.packing??"rgb",label:r.label??`N ${r.image.width}×${r.image.height}`,ok:!0};const a=[];s.normalImage&&a.push([()=>({name:"embedded",decoded:s.normalImage,arrayBuffer:async()=>new ArrayBuffer(0)}),!0]),s.normalMap&&a.push([()=>this.textures.find(s.normalMap),!0]);for(const l of[n,s.texture])for(const c of Rl(l??""))a.push([()=>this.textures.find(c),!1]);const i=new Set;for(const[l,c]of a){const f=l();if(!f)continue;const h=f.path??f.name;if(!i.has(h)){i.add(h);try{const u=await this.textures.decode(f),d=Ol(u);if(!d.normal&&!c)continue;return{meshIndex:e,image:u,packing:d.packing,label:`N ${u.width}×${u.height}${d.packing!=="rgb"?" "+d.packing.toUpperCase():""}`,ok:!0}}catch{}}}return s.normalMap?{...o,label:yt("tex.normalNoFile")}:o}async resolveSurface(e,n){const s=this.model?.meshes[e],o={meshIndex:e,image:null,label:"",ok:!1};if(!s)return o;const r=(await this.engineMaps(s))?.surface;if(r)return{meshIndex:e,image:r.image,label:r.label??"PBR",ok:!0};const a=[...s.surface?[s.surface]:[],...Hr(n??"").concat(n!==s.texture?Hr(s.texture):[])];for(const[i,l]of a.entries()){const c=i===0&&!!s.surface,f=new Map;for(const p of Dl(l)){const m=this.textures.find(p);if(m)try{f.set(p,await this.textures.decode(m))}catch{}}const h=l.roughnessFactor!==void 0||l.metallicFactor!==void 0||l.specularFactor!==void 0;if(!f.size&&!(c&&h))continue;const u=Pl(l,f),d=[l.metallic&&"M",(l.roughness||l.gloss)&&"R",l.ao&&"AO",l.specular&&"S"].filter(Boolean).join("·");return{meshIndex:e,image:u,label:`PBR${d?" "+d:""}${f.size?"":yt("tex.pbrFactors")}`,ok:!0}}return o}sameFolderTexture(e){const n=this.main;if(!n?.archive||!n.path)return null;const s=n.path.replace(/[^/]*$/,""),o=Ge(e).toLowerCase().replace(/\.[^.]+$/,""),r=n.archive.entries.filter(l=>l.path.startsWith(s)&&!l.path.slice(s.length).includes("/")&&this.registry.isImage(l.path)&&!/_(glow|n|s|normal|spec|local|bump|gloss|h)\.[^.]+$/.test(l.path)),a=l=>Ge(l).replace(/\.[^.]+$/,""),i=r.find(l=>a(l.path)===o)??r.find(l=>a(l.path)==="skin")??(r.length===1?r[0]:void 0);return i?n.archive.fileOf(i):null}archiveModels(e="",n=400){const s=[...this.modelExtensions,...this.motionExtensions].map(i=>"."+i),o=e.trim().toLowerCase(),r=[];let a=0;for(const i of this.archives)for(const l of i.entries)!s.some(c=>l.path.endsWith(c))&&!this.registry.isPathModel(l.path,this.preset.formats)||(a++,!(r.length>=n||o&&!l.path.includes(o))&&r.push({archive:i,entry:l}));return{items:r,total:a}}async openEntry(e){const n=e.archive.fileOf(e.entry),s=Ee(e.entry.path);this.registry.presetMotionExtensions(this.preset).includes(s)&&!this.modelExtensions.includes(s)?await this.loadMotions(n):await this.loadModel([n])}}const In=t=>t instanceof Error?t.message:String(t);async function Vl(t,e){const{main:n,model:s}=t;if(!n.archive||!n.path||!s.loadMotions)return 0;const o=n.path.replace(/[^/]*$/,"");let r=0;for(const a of n.archive.entries){if(r>=Mm)break;if(!(!a.path.startsWith(o)||!a.path.endsWith("."+e)||a.path.slice(o.length).includes("/")))try{const i=n.archive.fileOf(a);t.addMotions(await s.loadMotions(i),i.name),r++}catch(i){console.warn(i)}}return r}function jl(t,e=16){const n=[];for(let s=0;s<t.length;s+=e){const o=t.subarray(s,s+e),r=Array.from(o,i=>i.toString(16).padStart(2,"0")).join(" ").padEnd(e*3-1),a=Array.from(o,i=>i>=32&&i<127?String.fromCharCode(i):".").join("");n.push(`${s.toString(16).padStart(4,"0")} ${r} ${a}`)}return n.join(`
|
|
2
|
+
`)}async function km(t,e=64){let n=new Uint8Array(0);try{n=await Fo(t.file,e)}catch{}const s=typeof Blob<"u"&&t.file instanceof Blob?t.file.size:void 0;return[`gmv ${t.appVersion??""}`.trim(),`Файл: ${"path"in t.file&&t.file.path||t.file.name}${s!==void 0?` (${s} байт)`:""}`,t.preset?`Пресет: ${t.preset}`:"",t.format?`Формат: ${t.format}`:"",`Ошибка: ${t.error}`,`Сигнатура: ${Array.from(n.subarray(0,4),o=>o>=32&&o<127?String.fromCharCode(o):".").join("")}`,`Заголовок (${n.length} байт):`,jl(n)].filter(Boolean).join(`
|
|
3
|
+
`)}function nn(){const t=new Float64Array(16);return t[0]=t[5]=t[10]=t[15]=1,t}function Lt(t,e){const n=new Float64Array(16);return _o(t,e,n),n}function _o(t,e,n){for(let s=0;s<4;s++)for(let o=0;o<4;o++){let r=0;for(let a=0;a<4;a++)r+=t[a*4+o]*e[s*4+a];n[s*4+o]=r}}function Vt(t){const e=new Float64Array(16);for(let n=0;n<3;n++)for(let s=0;s<3;s++)e[s*4+n]=t[n*4+s];for(let n=0;n<3;n++)e[12+n]=-(t[n*4]*t[12]+t[n*4+1]*t[13]+t[n*4+2]*t[14]);return e[15]=1,e}function Co(t){const e=new Float64Array(16);for(let n=0;n<4;n++)for(let s=0;s<4;s++)e[s*4+n]=t[n*4+s];return e}function Xe(t,e,n=[1,1,1]){const s=new Float64Array(16);return Ae(e,t,n,s),s}function Ae(t,e,n,s){const o=e[0],r=e[1],a=e[2],i=e[3],l=r*r,c=a*a,f=i*i,h=r*a,u=r*i,d=a*i,p=o*r,m=o*a,g=o*i,w=[1-2*(c+f),2*(h-g),2*(u+m),2*(h+g),1-2*(l+f),2*(d-p),2*(u-m),2*(d+p),1-2*(l+c)];for(let y=0;y<3;y++)for(let M=0;M<3;M++)s[y*4+M]=w[M*3+y]*n[y];s[3]=s[7]=s[11]=0,s[12]=t[0],s[13]=t[1],s[14]=t[2],s[15]=1}function Kr(t){const e=Math.hypot(t[0],t[1],t[2]),n=Math.hypot(t[4],t[5],t[6]),s=Math.hypot(t[8],t[9],t[10]),r=t[0]*(t[5]*t[10]-t[9]*t[6])-t[4]*(t[1]*t[10]-t[9]*t[2])+t[8]*(t[1]*t[6]-t[5]*t[2])<0?-e:e,a=[r||1,n||1,s||1],i=(b,k)=>t[k*4+b]/a[k],l=i(0,0),c=i(0,1),f=i(0,2),h=i(1,0),u=i(1,1),d=i(1,2),p=i(2,0),m=i(2,1),g=i(2,2),w=l+u+g;let y,M,x,v;if(w>0){const b=Math.sqrt(w+1)*2;y=b/4,M=(m-d)/b,x=(f-p)/b,v=(h-c)/b}else if(l>u&&l>g){const b=Math.sqrt(1+l-u-g)*2;y=(m-d)/b,M=b/4,x=(c+h)/b,v=(f+p)/b}else if(u>g){const b=Math.sqrt(1+u-l-g)*2;y=(f-p)/b,M=(c+h)/b,x=b/4,v=(d+m)/b}else{const b=Math.sqrt(1+g-l-u)*2;y=(h-c)/b,M=(f+p)/b,x=(d+m)/b,v=b/4}return{t:[t[12],t[13],t[14]],q:[y,M,x,v],s:a}}function Yr(t,e){const n=t.length-1;if(n<=0||e<=t[0][0])return[0,0,0];if(e>=t[n][0])return[n,n,0];let s=0,o=n;for(;o-s>1;){const r=s+o>>1;t[r][0]<=e?s=r:o=r}return[s,o,(e-t[s][0])/(t[o][0]-t[s][0]||1)]}function Dn(t,e){const[n,s,o]=Yr(t,e),r=t[n][1],a=t[s][1];return[r[0]+(a[0]-r[0])*o,r[1]+(a[1]-r[1])*o,r[2]+(a[2]-r[2])*o]}function ls(t,e){const[n,s,o]=Yr(t,e),r=Array.from(t[n][1]);let a=Array.from(t[s][1]);r[0]*a[0]+r[1]*a[1]+r[2]*a[2]+r[3]*a[3]<0&&(a=a.map(c=>-c));const i=r.map((c,f)=>c+(a[f]-c)*o),l=Math.hypot(...i)||1;return i.map(c=>c/l)}const Em=Object.freeze(Object.defineProperty({__proto__:null,compose:Ae,identity:nn,keySegment:Yr,matToTRS:Kr,mulCol:Lt,mulColInto:_o,quatMat:Xe,rigidInv:Vt,sampleQuat:ls,sampleVec:Dn,transpose:Co},Symbol.toStringTag,{value:"Module"}));function Am(t,e){return[t[0]*e[0]-t[1]*e[1]-t[2]*e[2]-t[3]*e[3],t[0]*e[1]+t[1]*e[0]+t[2]*e[3]-t[3]*e[2],t[0]*e[2]-t[1]*e[3]+t[2]*e[0]+t[3]*e[1],t[0]*e[3]+t[1]*e[2]-t[2]*e[1]+t[3]*e[0]]}function $m(t,e){const n=Math.max(0,Math.min(e.cols-1,e.x)),s=Math.max(0,Math.min(e.rows-1,e.y)),o=Math.floor(n),r=Math.floor(s),a=Math.min(e.cols-1,o+1),i=Math.min(e.rows-1,r+1),l=n-o,c=s-r,f=[[r*e.cols+o,(1-l)*(1-c)],[r*e.cols+a,l*(1-c)],[i*e.cols+o,(1-l)*c],[i*e.cols+a,l*c]];let h=null,u=null;if(t.pos){h=[0,0,0];for(const[d,p]of f){if(!p)continue;const m=Dn(t.pos,d);for(let g=0;g<3;g++)h[g]+=p*m[g]}}if(t.rot){u=[0,0,0,0];let d=null;for(const[m,g]of f){if(!g)continue;const w=ls(t.rot,m);d||(d=w);const y=d[0]*w[0]+d[1]*w[1]+d[2]*w[2]+d[3]*w[3]<0?-g:g;for(let M=0;M<4;M++)u[M]+=y*w[M]}const p=Math.hypot(u[0],u[1],u[2],u[3])||1;u=u.map(m=>m/p)}return{t:h,q:u}}class Kt{n;nodes;parent;world;skin;worldV;skinV;rest;w64;tmp=new Float64Array(16);tmp2=new Float64Array(16);constructor(e){const n=this.n=e.length;this.nodes=e,this.parent=e.map(o=>o.helper?-1:o.parent),this.rest=e.map(o=>Kr(o.local)),this.world=new Float32Array(16*n),this.skin=new Float32Array(16*n);const s=o=>Array.from({length:n},(r,a)=>o.subarray(16*a,16*a+16));this.worldV=s(this.world),this.skinV=s(this.skin),this.w64=Array.from({length:n},()=>new Float64Array(16))}pose(e,n,s){const o=e&&e.kind==="node"?e:null,r=c=>{const f=c.duration;return(s?f>0?(n%f+f)%f:0:Math.min(Math.max(n,0),f))*c.tps},a=o?r(o):0,i=o?.layers?.length?o.layers.map(c=>({l:c,tick:c.frame??r(c.motion)})):null;let l=null;if(o?.blend?.motions.length){const c=o.blend,f=Math.max(0,Math.min(c.cols-1,c.x)),h=Math.max(0,Math.min(c.rows-1,c.y)),u=Math.floor(f),d=Math.floor(h),p=Math.min(c.cols-1,u+1),m=Math.min(c.rows-1,d+1),g=f-u,w=h-d,y=o.duration,M=y>0?s?(n%y+y)%y/y:Math.min(Math.max(n/y,0),1):0;l=[];for(const[x,v]of[[d*c.cols+u,(1-g)*(1-w)],[d*c.cols+p,g*(1-w)],[m*c.cols+u,(1-g)*w],[m*c.cols+p,g*w]]){const b=c.motions[x];if(!b||v<1e-6)continue;const k=l.find(E=>E.mo===b);k?k.w+=v:l.push({mo:b,w:v,tick:M*b.duration*b.tps})}}for(let c=0;c<this.n;c++){const f=this.nodes[c],h=o?o.chan.get(c):void 0,u=this.rest[c];let d=f.local;if(h||i||l){let m=h?.pos?Dn(h.pos,a):u.t,g=h?.rot?ls(h.rot,a):u.q;const w=h?.scl?Dn(h.scl,a):u.s;let y=!!h;if(l){const M=[0,0,0],x=[0,0,0,0];let v=!1,b=null;for(const{mo:k,w:E,tick:A}of l){const S=k.chan.get(c);S&&(v=!0);const $=S?.pos?Dn(S.pos,A):u.t,T=S?.rot?ls(S.rot,A):u.q;for(let _=0;_<3;_++)M[_]+=$[_]*E;b??=T;const U=b[0]*T[0]+b[1]*T[1]+b[2]*T[2]+b[3]*T[3]<0?-E:E;for(let _=0;_<4;_++)x[_]+=T[_]*U}if(v){const k=Math.hypot(x[0],x[1],x[2],x[3])||1;m=M,g=x.map(E=>E/k),y=!0}}if(i)for(const{l:M,tick:x}of i){const v=M.motion.chan.get(c);if(!(!v||M.mask&&!M.mask.has(c)||M.fill&&h)){if(y=!0,M.grid&&!v.add){const b=$m(v,M.grid);b.t&&(m=b.t),b.q&&(g=b.q);continue}if(v.add){if(v.add.pos){const b=Dn(v.add.pos,x);m=[m[0]+b[0],m[1]+b[1],m[2]+b[2]]}v.add.rot&&(g=Am(g,ls(v.add.rot,x)))}else v.pos&&(m=Dn(v.pos,x)),v.rot&&(g=ls(v.rot,x))}}y&&(Ae(m,g,w,this.tmp),d=this.tmp)}const p=this.w64[c];f.parent<0?p.set(d):_o(this.w64[f.parent],d,p),this.worldV[c].set(p),f.offset?(_o(p,f.offset,this.tmp2),this.skinV[c].set(this.tmp2)):this.skinV[c].set(p)}}}const Sm=(t,e)=>Math.floor(t.meshes[e].indices.length/3);function Tm(t){const e=t.instances,n=new Set(e?.meshes??[]),s=i=>({id:`m${i}`,name:t.meshes[i].name||yt("scene.mesh",{i}),kind:"mesh",mesh:i,note:yt("scene.tris",{n:Sm(t,i)})}),o=t.meshes.map((i,l)=>l).filter(i=>!n.has(i));if(!e?.placements.length)return[{id:"model",name:yt("scene.model"),kind:"group",note:yt("scene.meshes",{n:o.length}),children:o.map(s)}];const r=new Map;e.placements.forEach((i,l)=>{const c=i.group||yt("scene.otherObjects");(r.get(c)??r.set(c,[]).get(c)).push(l)});const a=i=>e.protos[i.proto].parts.reduce((l,c)=>l+Math.floor(c.indices.length/3),0);return[{id:"geom",name:yt("scene.geometry"),kind:"group",note:yt("scene.meshes",{n:o.length}),children:o.map(s)},{id:"objects",name:yt("scene.objects"),kind:"group",note:String(e.placements.length),children:[...r].sort((i,l)=>l[1].length-i[1].length).map(([i,l])=>({id:`g:${i}`,name:i,kind:"group",note:String(l.length),children:l.map(c=>{const f=e.placements[c],h=e.protos[f.proto];return{id:`i${c}`,name:f.name,kind:"instance",instance:c,note:`${h.name.split("/").pop()} · ${yt("scene.tris",{n:a(f)})}`}})}))}]}function Gl(t){return t.kind==="group"?(t.children??[]).flatMap(Gl):[t]}function Zr(t,e){const n=t.instances,s=n?.placements[e];if(!n||!s?.ranges)return[];const o=s.pose,r=n.protos[s.proto],a=o[0]*(o[5]*o[10]-o[6]*o[9])-o[1]*(o[4]*o[10]-o[6]*o[8])+o[2]*(o[4]*o[9]-o[5]*o[8]);for(const i of s.ranges){const l=r.parts[i.part],c=t.meshes[i.mesh];if(!(!l||!c)){for(let f=0;f<i.vCount;f++){const h=l.pos[f*3],u=l.pos[f*3+1],d=l.pos[f*3+2],p=(i.vStart+f)*3;c.pos[p]=h*o[0]+u*o[4]+d*o[8]+o[12],c.pos[p+1]=h*o[1]+u*o[5]+d*o[9]+o[13],c.pos[p+2]=h*o[2]+u*o[6]+d*o[10]+o[14];const m=l.nor[f*3],g=l.nor[f*3+1],w=l.nor[f*3+2],y=m*o[0]+g*o[4]+w*o[8],M=m*o[1]+g*o[5]+w*o[9],x=m*o[2]+g*o[6]+w*o[10],v=Math.hypot(y,M,x)||1;c.nor[p]=y/v,c.nor[p+1]=M/v,c.nor[p+2]=x/v}for(let f=0;f+2<i.iCount;f+=3){const h=l.indices[f],u=l.indices[f+1],d=l.indices[f+2],p=i.iStart+f;c.indices[p]=i.vStart+h,c.indices[p+1]=i.vStart+(a<0?d:u),c.indices[p+2]=i.vStart+(a<0?u:d)}}}return s.ranges.map(i=>({mesh:i.mesh,vStart:i.vStart,vCount:i.vCount,iStart:i.iStart,iCount:i.iCount}))}function Um(t,e,n){const s=t.instances?.placements[e];if(!s)return[];const o=Math.cos(n),r=Math.sin(n),a=s.pose;for(let i=0;i<3;i++){const l=a[i*4],c=a[i*4+2];a[i*4]=l*o-c*r,a[i*4+2]=l*r+c*o}return Zr(t,e)}function Fm(t,e,n){const s=t.instances?.placements[e];return s?(s.pose[12]=s.pose[12]+n[0],s.pose[13]=s.pose[13]+n[1],s.pose[14]=s.pose[14]+n[2],Zr(t,e)):[]}function qr(t,e){const n=t.meshes[e].indices,s=t.instances?.placements.filter(a=>a.hidden&&a.ranges?.some(i=>i.mesh===e));if(!s?.length)return Uint32Array.from(n);const o=new Uint8Array(n.length);for(const a of s)for(const i of a.ranges)i.mesh===e&&o.fill(1,i.iStart,i.iStart+i.iCount);const r=[];for(let a=0;a+2<n.length;a+=3)o[a]||r.push(n[a],n[a+1],n[a+2]);return Uint32Array.from(r)}function _m(t,e,n){const s=t.instances?.placements;if(!s)return-1;for(let o=0;o<s.length;o++)for(const r of s[o].ranges??[])if(r.mesh===e&&n>=r.iStart&&n<r.iStart+r.iCount)return o;return-1}function Xl(t,e,n,s={}){const[o,r,a]=[e[0],e[1],e[2]],[i,l,c]=[n[0],n[1],n[2]];let f=null,h=1/0;return t.meshes.forEach((u,d)=>{if(s.skip?.(d))return;const p=u.pos,m=s.indices?.(d)??u.indices;for(let g=0;g+2<m.length;g+=3){const w=m[g]*3,y=m[g+1]*3,M=m[g+2]*3;if(w===y&&y===M)continue;const x=p[w],v=p[w+1],b=p[w+2],k=p[y]-x,E=p[y+1]-v,A=p[y+2]-b,S=p[M]-x,$=p[M+1]-v,T=p[M+2]-b,U=l*T-c*$,_=c*S-i*T,R=i*$-l*S,L=k*U+E*_+A*R;if(Math.abs(L)<1e-12)continue;const O=1/L,N=o-x,D=r-v,F=a-b,C=(N*U+D*_+F*R)*O;if(C<0||C>1)continue;const P=D*A-F*E,I=F*k-N*A,B=N*E-D*k,z=(i*P+l*I+c*B)*O;if(z<0||C+z>1)continue;const j=(S*P+$*I+T*B)*O;j<=0||j>=h||s.clip&&s.clip(o+i*j,r+l*j,a+c*j)||(h=j,f={mesh:d,index:g,t:j})}}),f}async function Hl(t,e){const n=new Blob([t]).stream().pipeThrough(new DecompressionStream(e));return new Uint8Array(await new Response(n).arrayBuffer())}const Lo=t=>Hl(t,"deflate"),Qr=t=>Hl(t,"deflate-raw");async function cs(t){const e=Math.min(t.size,65557),n=await ht(t,t.size-e,t.size),s=new DataView(n.buffer,n.byteOffset,n.byteLength);let o=-1;for(let f=n.length-22;f>=0;f--)if(s.getUint32(f,!0)===101010256){o=f;break}if(o<0)throw new Error(`${t.name}: не ZIP (нет конца каталога)`);const r=s.getUint32(o+12,!0),a=s.getUint32(o+16,!0);if(a===4294967295)throw new Error(`${t.name}: ZIP64 не поддерживается`);const i=await ht(t,a,a+r),l=new DataView(i.buffer,i.byteOffset,i.byteLength),c=[];for(let f=0;f+46<=i.length&&l.getUint32(f,!0)===33639248;){const h=l.getUint16(f+10,!0),u=l.getUint32(f+20,!0),d=l.getUint32(f+24,!0),p=l.getUint16(f+28,!0),m=l.getUint16(f+30,!0),g=l.getUint16(f+32,!0),w=l.getUint32(f+42,!0);let y="";for(let M=0;M<p;M++)y+=String.fromCharCode(i[f+46+M]);y.endsWith("/")||c.push({path:gt(y),method:h,comp:u,size:d,off:w}),f+=46+p+m+g}return Qt({name:t.name,kind:"zip",version:0,entries:c,async read(f){const h=new DataView(await t.slice(f.off,f.off+30).arrayBuffer()),u=f.off+30+h.getUint16(26,!0)+h.getUint16(28,!0),d=await ht(t,u,u+f.comp);if(f.method===0)return d;if(f.method===8)return Qr(d);throw new Error("метод сжатия ZIP "+f.method+" не поддерживается")}})}function Ro(t){if(!t)return{s:0,max:0};let e=0;for(;!(t>>>e&1);)e++;let n=0;for(;t>>>e+n&1;)n++;return{s:e,max:2**n-1}}const fs=t=>t.charCodeAt(0)|t.charCodeAt(1)<<8|t.charCodeAt(2)<<16|t.charCodeAt(3)<<24;function Pn(t){const e=new DataView(t);if(t.byteLength<128||e.getUint32(0,!0)!==542327876)throw new Error("Не DDS файл");const n=e.getUint32(12,!0),s=e.getUint32(16,!0),o=e.getUint32(80,!0),r=e.getUint32(84,!0),a=e.getUint32(88,!0);let i=128,l=0;if(o&4)if(r===fs("DXT1"))l=1;else if(r===fs("DXT2")||r===fs("DXT3"))l=3;else if(r===fs("DXT4")||r===fs("DXT5"))l=5;else if(r===fs("DX10")){const x=e.getUint32(128,!0);if(i=148,x===71||x===72)l=1;else if(x===74||x===75)l=3;else if(x===77||x===78)l=5;else if(x===28||x===29)l=-1;else if(x===87||x===91)l=-2;else throw new Error("Неподдерживаемый DXGI формат DDS: "+x)}else throw new Error("Неподдерживаемый fourCC DDS");if(l>0)return{width:s,height:n,rgba:_e(e,i,s,n,l)};const c=new Uint8Array(s*n*4);if(l===-1||l===-2){for(let x=0;x<s*n;x++){const v=i+x*4,b=e.getUint8(v),k=e.getUint8(v+1),E=e.getUint8(v+2),A=e.getUint8(v+3);c.set(l===-1?[b,k,E,A]:[E,k,b,A],x*4)}return{width:s,height:n,rgba:c}}const f=a>>3;if(f===1){const x=!(o&131072)&&!(o&64)&&!!(o&2);for(let v=0;v<s*n;v++){const b=e.getUint8(i+v);c.set(x?[255,255,255,b]:[b,b,b,255],v*4)}return{width:s,height:n,rgba:c}}if(![2,3,4].includes(f))throw new Error("Неподдерживаемый несжатый DDS ("+a+" бит)");const h=e.getUint32(92,!0),u=e.getUint32(96,!0),d=e.getUint32(100,!0),p=o&1?e.getUint32(104,!0):0,m=Ro(h),g=Ro(u),w=Ro(d),y=Ro(p),M=(x,v)=>v.max?Math.round((x>>>v.s&v.max)*255/v.max):255;for(let x=0;x<s*n;x++){let v=0;for(let b=0;b<f;b++)v|=e.getUint8(i+x*f+b)<<8*b;c[x*4]=M(v&h,m),c[x*4+1]=M(v&u,g),c[x*4+2]=M(v&d,w),c[x*4+3]=y.max?M(v&p,y):255}return{width:s,height:n,rgba:c}}function _e(t,e,n,s,o){const r=new Uint8Array(n*s*4),a=n+3>>2,i=s+3>>2,l=[0,0,0,0],c=[0,0,0,0],f=[0,0,0,0],h=[255,255,255,255],u=new Uint8Array(16);let d=e;const p=m=>[(m>>11&31)*255/31|0,(m>>5&63)*255/63|0,(m&31)*255/31|0];for(let m=0;m<i;m++)for(let g=0;g<a;g++){if(o===3){for(let b=0;b<8;b++){const k=t.getUint8(d+b);u[2*b]=(k&15)*17,u[2*b+1]=(k>>4)*17}d+=8}else if(o===5){const b=t.getUint8(d),k=t.getUint8(d+1),E=[b,k,0,0,0,0,0,0];if(b>k)for(let $=1;$<=6;$++)E[$+1]=((7-$)*b+$*k)/7|0;else{for(let $=1;$<=4;$++)E[$+1]=((5-$)*b+$*k)/5|0;E[6]=0,E[7]=255}const A=t.getUint32(d+2,!0),S=t.getUint16(d+6,!0);for(let $=0;$<16;$++){const T=$*3;let U;T<=29?U=A>>>T&7:T===30?U=(A>>>30|S<<2)&7:U=S>>>T-32&7,u[$]=E[U]}d+=8}const w=t.getUint16(d,!0),y=t.getUint16(d+2,!0),M=t.getUint32(d+4,!0);d+=8;const x=p(w),v=p(y);for(let b=0;b<3;b++){const k=[l,c,f][b];k[0]=x[b],k[1]=v[b],o>1||w>y?(k[2]=(2*x[b]+v[b])/3|0,k[3]=(x[b]+2*v[b])/3|0):(k[2]=x[b]+v[b]>>1,k[3]=0)}h[3]=o===1&&w<=y?0:255;for(let b=0;b<4;b++)for(let k=0;k<4;k++){const E=g*4+k,A=m*4+b;if(E>=n||A>=s)continue;const S=M>>>2*(b*4+k)&3,$=(A*n+E)*4;r[$]=l[S],r[$+1]=c[S],r[$+2]=f[S],r[$+3]=o===1?h[S]:u[b*4+k]}}return r}function Wl(t){const e=new Uint8Array(t),n=new DataView(t);if(e[0]!==10||e[2]!==1||e[3]!==8)throw new Error("PCX: неподдерживаемый формат");const s=n.getUint16(8,!0)-n.getUint16(4,!0)+1,o=n.getUint16(10,!0)-n.getUint16(6,!0)+1,r=e[65],a=n.getUint16(66,!0),i=r*a,l=new Uint8Array(i*o);let c=128;for(let u=0;u<l.length&&c<e.length;){let d=e[c++],p=1;for((d&192)===192&&(p=d&63,d=e[c++]);p-- >0&&u<l.length;)l[u++]=d}const f=new Uint8Array(s*o*4),h=r===1&&e[e.length-769]===12?e.subarray(e.length-768):null;for(let u=0;u<o;u++)for(let d=0;d<s;d++){const p=(u*s+d)*4;let m,g,w;if(r===1){const y=l[u*i+d];h?(m=h[y*3],g=h[y*3+1],w=h[y*3+2]):m=g=w=y}else m=l[u*i+d],g=l[u*i+a+d],w=l[u*i+2*a+d];f[p]=m,f[p+1]=g,f[p+2]=w,f[p+3]=255}return{width:s,height:o,rgba:f}}function Kl(t){const e=new Uint8Array(t),n=new DataView(t);if(t.byteLength<18)throw new Error("TGA: файл слишком короткий");const s=e[0],o=e[1],r=e[2],a=n.getUint16(5,!0),i=e[7],l=n.getUint16(12,!0),c=n.getUint16(14,!0),f=e[16],h=(e[17]&32)!==0;if(![1,2,3,9,10,11].includes(r)||!l||!c)throw new Error("TGA: неподдерживаемый тип "+r);let u=18+s,d=null;const p=i>>3;o===1&&(d=e.subarray(u,u+a*p),u+=a*p);const m=f>>3,g=new Uint8Array(l*c*m);if(!(r>=9))g.set(e.subarray(u,u+g.length));else for(let M=0;M<g.length;){const x=e[u++],v=(x&127)+1;if(x&128){for(let b=0;b<v&&M<g.length;b++)for(let k=0;k<m;k++)g[M++]=e[u+k];u+=m}else for(let b=0;b<v*m&&M<g.length;b++)g[M++]=e[u++]}const y=new Uint8Array(l*c*4);for(let M=0;M<c;M++){const x=h?M:c-1-M;for(let v=0;v<l;v++){const b=(M*l+v)*m,k=(x*l+v)*4;let E,A,S,$=255;if(d){const T=g[b]*p;S=d[T],A=d[T+1],E=d[T+2],p===4&&($=d[T+3])}else if(m===1)E=A=S=g[b];else if(m===2){const T=g[b]|g[b+1]<<8;E=(T>>10&31)*255/31,A=(T>>5&31)*255/31,S=(T&31)*255/31}else S=g[b],A=g[b+1],E=g[b+2],m===4&&($=g[b+3]);y[k]=E,y[k+1]=A,y[k+2]=S,y[k+3]=$}}return{width:l,height:c,rgba:y}}const Jr=vt({id:"core",title:"Общие форматы",i18n:{en:{title:"Common formats",exporters:{obj:{title:"OBJ + MTL (Blender, 3ds Max, any 3D editor)",description:"Bind-pose geometry, UVs, normals and materials with DDS textures — one ZIP. OBJ does not store bones or animations.",fields:{name:{label:"File name"},scale:{label:"Scale"}}}},groups:{Игры:"Games"}}},archives:[{id:"zip",title:"ZIP (pk3, pk4, iwd, CryEngine .pak)",engine:"core",extensions:["zip","pk3","pk4","iwd","pak"],sniff:t=>It(t,"PK")||It(t,"PK")?1:0,openFromFolder:(t,e)=>/\.pak$/i.test(e),open:t=>cs(t)}],exporters:[{id:"obj",title:"OBJ + MTL (Blender, 3ds Max, любой 3D-редактор)",target:"obj",description:"Геометрия в bind-позе, UV, нормали и материалы с текстурами DDS — одним ZIP. Кости и анимации OBJ не хранит.",fields:[{id:"name",label:"Имя файла",type:"text",default:(t,e)=>e},{id:"scale",label:"Масштаб",type:"number",min:0,step:.01,default:1}],run:async(t,e,n)=>(await Promise.resolve().then(()=>a2)).exportObj(t,e,n)}],codecs:[{id:"dds",extensions:["dds"],decode:t=>Pn(t)},{id:"tga",extensions:["tga"],decode:t=>Kl(t)},{id:"pcx",extensions:["pcx"],decode:t=>Wl(t)}]});function Yl(t,e,n){const s=t[e],o=t[e+1],r=[s,o,0,0,0,0,0,0];if(s>o)for(let c=1;c<=6;c++)r[c+1]=((7-c)*s+c*o)/7|0;else{for(let c=1;c<=4;c++)r[c+1]=((5-c)*s+c*o)/5|0;r[6]=0,r[7]=255}let a=0,i=0,l=e+2;for(let c=0;c<16;c++)i<3&&(a|=t[l++]<<i,i+=8),n[c]=r[a&7],a>>>=3,i-=3}function Oo(t,e,n,s,o){const r=new Uint8Array(n*s*4),a=new Uint8Array(16),i=new Uint8Array(16),l=n+3>>2,c=s+3>>2;let f=e;for(let h=0;h<c;h++)for(let u=0;u<l;u++){Yl(t,f,a),f+=8,o&&(Yl(t,f,i),f+=8);for(let d=0;d<16;d++){const p=u*4+(d&3),m=h*4+(d>>2);if(p>=n||m>=s)continue;const g=(m*n+p)*4;if(o){r[g]=a[d],r[g+1]=i[d];const w=a[d]/127.5-1,y=i[d]/127.5-1;r[g+2]=Math.round((Math.sqrt(Math.max(0,1-w*w-y*y))*.5+.5)*255)}else r[g]=r[g+1]=r[g+2]=a[d];r[g+3]=255}}return r}const Cm=[52428,34952,61166,60616,51328,65260,65224,60544,51200,65516,65152,59392,65512,65280,65520,61440,63248,142,28928,2254,140,29456,12544,36046,2188,12560,26214,13932,6120,4080,29070,14748,43690,61680,23130,13260,15420,21930,38550,42330,29646,5064,12876,15324,27030,49980,39270,1632,626,1252,20032,10016,51510,37740,14790,25500,37686,40134,33150,59160,52464,4044,30532,60962],Lm=[2858963024,1784303680,1515864576,1414570152,2779054080,2694860880,1431675040,1515868240,2857697280,2857719040,2863289600,2425393296,2492765332,2762253476,2846200912,705315408,2777960512,172118100,2779096320,1436590240,2829603924,1785348160,2762231808,437912832,5285028,2862977168,342452500,1768494080,2693105056,2860651540,1352967248,1784283648,2846195712,1351655592,2829094992,606348324,11162880,613566756,608801316,1352993360,1342874960,2863285316,1717960704,2778768800,1352683680,1764256040,1152035396,1717986816,2856600644,1420317864,2508232064,2526451200,2824098984,2157286784,2853442580,2526412800,2863272980,2689618080,2695210400,2516582400,1082146944,2846402984,2863311428,709513812],Rm=[15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,2,8,2,2,8,8,15,2,8,2,2,8,8,2,2,15,15,6,8,2,8,15,15,2,8,2,2,2,15,15,6,6,2,6,8,15,15,2,2,15,15,15,15,15,2,2,15],Om=[3,3,15,15,8,3,15,15,8,8,6,6,6,5,3,3,3,3,8,15,3,3,6,10,5,8,8,6,8,5,15,15,8,15,3,5,6,10,8,15,15,3,15,5,15,15,15,15,3,15,5,5,5,8,5,10,5,10,8,13,15,12,3,3],Im=[15,8,8,3,15,15,3,8,15,15,15,15,15,15,15,8,15,8,15,3,15,8,15,8,3,15,6,10,15,15,10,8,15,3,15,10,10,8,9,10,6,15,8,15,3,6,6,8,15,3,15,15,15,15,15,15,15,15,15,15,3,15,15,8],Dm=[0,21,43,64],Pm=[0,9,18,27,37,46,55,64],Nm=[0,4,9,13,17,21,26,30,34,38,43,47,51,55,60,64],Zl=t=>t===2?Dm:t===3?Pm:Nm,zm=[{ns:3,pb:4,rb:0,isb:0,cb:4,ab:0,epb:1,spb:0,ib:3,ib2:0},{ns:2,pb:6,rb:0,isb:0,cb:6,ab:0,epb:0,spb:1,ib:3,ib2:0},{ns:3,pb:6,rb:0,isb:0,cb:5,ab:0,epb:0,spb:0,ib:2,ib2:0},{ns:2,pb:6,rb:0,isb:0,cb:7,ab:0,epb:1,spb:0,ib:2,ib2:0},{ns:1,pb:0,rb:2,isb:1,cb:5,ab:6,epb:0,spb:0,ib:2,ib2:3},{ns:1,pb:0,rb:2,isb:0,cb:7,ab:8,epb:0,spb:0,ib:2,ib2:2},{ns:1,pb:0,rb:0,isb:0,cb:7,ab:7,epb:1,spb:0,ib:4,ib2:0},{ns:2,pb:6,rb:0,isb:0,cb:5,ab:5,epb:1,spb:0,ib:2,ib2:0}],Bm=new Int32Array(24),ta=new Uint8Array(16),ea=new Uint8Array(16),na=new Uint32Array(4);function Vm(t,e,n){for(let b=0;b<4;b++)na[b]=(t[e+b*4]|t[e+b*4+1]<<8|t[e+b*4+2]<<16|t[e+b*4+3]<<24)>>>0;let s=0;const o=b=>{if(!b)return 0;const k=s>>>5,E=s&31;let A=na[k]>>>E;return E+b>32&&k<3&&(A|=na[k+1]<<32-E),s+=b,(A&(1<<b)-1)>>>0};let r=0;for(;r<8&&!o(1);)r++;if(r>=8){n.fill(0);return}const a=zm[r],i=o(a.pb),l=o(a.rb),c=o(a.isb),f=a.ns,h=f*2,u=Bm;for(let b=0;b<h;b++)u[b*4+3]=255;for(let b=0;b<3;b++)for(let k=0;k<h;k++)u[k*4+b]=o(a.cb);if(a.ab)for(let b=0;b<h;b++)u[b*4+3]=o(a.ab);if(a.epb)for(let b=0;b<h;b++){const k=o(1);for(let E=0;E<4;E++)(E<3||a.ab)&&(u[b*4+E]=u[b*4+E]<<1|k)}else if(a.spb)for(let b=0;b<f;b++){const k=o(1);for(let E=b*2;E<=b*2+1;E++)for(let A=0;A<3;A++)u[E*4+A]=u[E*4+A]<<1|k}const d=a.cb+(a.epb||a.spb?1:0),p=a.ab?a.ab+(a.epb?1:0):0;for(let b=0;b<h;b++){for(let k=0;k<3;k++)u[b*4+k]=u[b*4+k]<<8-d|u[b*4+k]>>2*d-8;u[b*4+3]=p?u[b*4+3]<<8-p|u[b*4+3]>>2*p-8:255}const m=Cm[i],g=Lm[i],w=Rm[i],y=Om[i],M=Im[i];for(let b=0;b<16;b++){const k=b===0||f===2&&b===w||f===3&&(b===y||b===M);ta[b]=o(k?a.ib-1:a.ib)}if(a.ib2)for(let b=0;b<16;b++)ea[b]=o(b===0?a.ib2-1:a.ib2);const x=Zl(a.ib),v=a.ib2?Zl(a.ib2):x;for(let b=0;b<16;b++){const k=f===1?0:f===2?m>>b&1:g>>b*2&3,E=k*8,A=k*8+4;let S=ta[b],$=ta[b],T=x,U=x;a.ib2&&(c?(S=ea[b],T=v):($=ea[b],U=v));const _=b*4,R=T[S],L=U[$];for(let O=0;O<3;O++)n[_+O]=(64-R)*u[E+O]+R*u[A+O]+32>>6;if(n[_+3]=(64-L)*u[E+3]+L*u[A+3]+32>>6,l){const O=n[_+3];n[_+3]=n[_+l-1],n[_+l-1]=O}}}function Io(t,e,n,s){const o=new Uint8Array(n*s*4),r=new Uint8Array(64),a=n+3>>2,i=s+3>>2;for(let l=0;l<i;l++)for(let c=0;c<a;c++){Vm(t,e+(l*a+c)*16,r);for(let f=0;f<16;f++){const h=c*4+(f&3),u=l*4+(f>>2);if(h<n&&u<s){const d=(u*n+h)*4,p=f*4;o[d]=r[p],o[d+1]=r[p+1],o[d+2]=r[p+2],o[d+3]=r[p+3]}}}return o}function sa(t,e=!0){const n=[{w:t.width,h:t.height,px:t.rgba}];for(;e;){const{w:i,h:l,px:c}=n[n.length-1];if(i===1&&l===1)break;const f=Math.max(1,i>>1),h=Math.max(1,l>>1),u=new Uint8Array(f*h*4);for(let d=0;d<h;d++)for(let p=0;p<f;p++)for(let m=0;m<4;m++){let g=0,w=0;for(const[y,M]of[[0,0],[1,0],[0,1],[1,1]]){const x=Math.min(i-1,p*2+y),v=Math.min(l-1,d*2+M);g+=c[(v*i+x)*4+m],w++}u[(d*f+p)*4+m]=Math.round(g/w)}n.push({w:f,h,px:u})}const s=n.reduce((i,l)=>i+l.w*l.h*4,0),o=new Uint8Array(128+s),r=new DataView(o.buffer);r.setUint32(0,542327876,!0),r.setUint32(4,124,!0),r.setUint32(8,4111|(n.length>1?131072:0),!0),r.setUint32(12,t.height,!0),r.setUint32(16,t.width,!0),r.setUint32(20,t.width*4,!0),r.setUint32(28,n.length,!0),r.setUint32(76,32,!0),r.setUint32(80,65,!0),r.setUint32(88,32,!0),r.setUint32(92,16711680,!0),r.setUint32(96,65280,!0),r.setUint32(100,255,!0),r.setUint32(104,4278190080,!0),r.setUint32(108,4096|(n.length>1?4194312:0),!0);let a=128;for(const i of n)for(let l=0;l<i.w*i.h;l++)o[a++]=i.px[l*4+2],o[a++]=i.px[l*4+1],o[a++]=i.px[l*4],o[a++]=i.px[l*4+3];return o}function jm(t){const e=t.rgba;if(!e)return!1;for(let n=3;n<e.length;n+=16)if(e[n]<250)return!0;return!1}const Gm=(()=>{const t=new Uint32Array(256);for(let e=0;e<256;e++){let n=e;for(let s=0;s<8;s++)n=n&1?3988292384^n>>>1:n>>>1;t[e]=n>>>0}return t})();function Do(t){let e=4294967295;for(let n=0;n<t.length;n++)e=Gm[(e^t[n])&255]^e>>>8;return(e^4294967295)>>>0}function Xm(t){const e=[...t],n=new TextEncoder,s=[],o=[];let r=0;const a=new Date,i=a.getHours()<<11|a.getMinutes()<<5|a.getSeconds()>>1,l=a.getFullYear()-1980<<9|a.getMonth()+1<<5|a.getDate();for(const[p,m]of e){const g=n.encode(p.replace(/\\/g,"/")),w=Do(m),y=new Uint8Array(30+g.length),M=new DataView(y.buffer);M.setUint32(0,67324752,!0),M.setUint16(4,20,!0),M.setUint16(6,2048,!0),M.setUint16(8,0,!0),M.setUint16(10,i,!0),M.setUint16(12,l,!0),M.setUint32(14,w,!0),M.setUint32(18,m.length,!0),M.setUint32(22,m.length,!0),M.setUint16(26,g.length,!0),y.set(g,30);const x=new Uint8Array(46+g.length),v=new DataView(x.buffer);v.setUint32(0,33639248,!0),v.setUint16(4,20,!0),v.setUint16(6,20,!0),v.setUint16(8,2048,!0),v.setUint16(10,0,!0),v.setUint16(12,i,!0),v.setUint16(14,l,!0),v.setUint32(16,w,!0),v.setUint32(20,m.length,!0),v.setUint32(24,m.length,!0),v.setUint16(28,g.length,!0),v.setUint32(42,r,!0),x.set(g,46),s.push(y,m),o.push(x),r+=y.length+m.length}const c=o.reduce((p,m)=>p+m.length,0),f=new Uint8Array(22),h=new DataView(f.buffer);h.setUint32(0,101010256,!0),h.setUint16(8,e.length,!0),h.setUint16(10,e.length,!0),h.setUint32(12,c,!0),h.setUint32(16,r,!0);const u=new Uint8Array(r+c+22);let d=0;for(const p of[...s,...o,f])u.set(p,d),d+=p.length;return u}function ge(t,e){const n=new Float32Array(t.length);return ql(t,e,n),Ql(n),n}function ql(t,e,n){for(let s=0;s+2<e.length;s+=3){const o=e[s]*3,r=e[s+1]*3,a=e[s+2]*3,i=t[r]-t[o],l=t[r+1]-t[o+1],c=t[r+2]-t[o+2],f=t[a]-t[o],h=t[a+1]-t[o+1],u=t[a+2]-t[o+2],d=l*u-c*h,p=c*f-i*u,m=i*h-l*f;for(const g of[o,r,a])n[g]+=d,n[g+1]+=p,n[g+2]+=m}}function Ql(t){for(let e=0;e<t.length;e+=3){const n=Math.hypot(t[e],t[e+1],t[e+2])||1;t[e]/=n,t[e+1]/=n,t[e+2]/=n}}function $e(t,e){return t>65535?Uint32Array.from(e):Uint16Array.from(e)}function Po(){return{min:[1/0,1/0,1/0],max:[-1/0,-1/0,-1/0]}}function dt(t){const e=Po();for(const n of t)Jl(e,n.pos);return e}function Jl(t,e){for(let n=0;n<e.length;n++){const s=n%3,o=e[n];o<t.min[s]&&(t.min[s]=o),o>t.max[s]&&(t.max[s]=o)}}function Jt(t,e){t.pose(null,0,!1);const{min:n,max:s}=Po();for(const o of e)for(let r=0;r<o.vertexCount;r++){const a=o.pos[r*3],i=o.pos[r*3+1],l=o.pos[r*3+2],c=[0,0,0];for(let f=0;f<4;f++){const h=o.weight[r*4+f];if(!h)continue;const u=t.skinV[o.bone[r*4+f]];c[0]+=h*(u[0]*a+u[4]*i+u[8]*l+u[12]),c[1]+=h*(u[1]*a+u[5]*i+u[9]*l+u[13]),c[2]+=h*(u[2]*a+u[6]*i+u[10]*l+u[14])}for(let f=0;f<3;f++)c[f]<n[f]&&(n[f]=c[f]),c[f]>s[f]&&(s[f]=c[f])}return{min:n,max:s}}function No(t){const e=[];for(let n=2;n<t.length;n++){const s=t[n-2],o=t[n-1],r=t[n];s===o||o===r||s===r||(n&1?e.push(s,r,o):e.push(s,o,r))}return e}const Ut=t=>t.reduce((e,n)=>e+n.indices.length/3,0),le=t=>t.reduce((e,n)=>e+n.vertexCount,0),oa=Object.freeze(Object.defineProperty({__proto__:null,accumulateNormals:ql,boundsOf:dt,computeNormals:ge,emptyBounds:Po,extendBounds:Jl,makeIndices:$e,normalize3:Ql,skinnedBounds:Jt,stripToTris:No,sumTris:Ut,sumVerts:le},Symbol.toStringTag,{value:"Module"}));function tc(t=[]){return new Ll([Jr,...t.filter(e=>e.id!=="core")])}const Hm={presets:{"stalker-soc":{title:"S.T.A.L.K.E.R.: Shadow of Chernobyl",hint:"OGF v4. Animations are inside the model or in an external .omf. gamedata.db* archives open through “Game folder” (the file-table key is taken from your game’s bin/XR_3DA.exe) or with “Open .db…” together with XR_3DA.exe; mod .xdb archives and an unpacked gamedata folder open too. External .omf files referenced by a model are attached automatically when the gamedata folder or archives are open; torso animations play together with legs (choose in “Other body parts”). Levels include grass from level.details; SDK sources: .object with skeleton and animations, .skl/.skls.",archiveLabel:"Open .db…"},"stalker-cs":{title:"S.T.A.L.K.E.R.: Clear Sky",hint:"OGF v4. Open resources.db0–db2 (models and textures) and patches/xpatch_*.db, or a gamedata folder. External .omf files referenced by a model are attached automatically when the gamedata folder or archives are open; torso animations play together with legs (choose in “Other body parts”). Levels include grass from level.details; SDK sources: .object with skeleton and animations, .skl/.skls.",archiveLabel:"Open .db…"},"stalker-cop":{title:"S.T.A.L.K.E.R.: Call of Pripyat",hint:"OGF v4, including 3- and 4-weight vertices. .db archives as in Clear Sky. External .omf files referenced by a model are attached automatically when the gamedata folder or archives are open; torso animations play together with legs (choose in “Other body parts”). Levels include grass from level.details; SDK sources: .object with skeleton and animations, .skl/.skls.",archiveLabel:"Open .db…"}},exporters:{"xray-sdk":{title:"S.T.A.L.K.E.R.: X-Ray SDK 0.7 sources",description:"For LevelEditor and ActorEditor (Call of Pripyat, SDK 0.7): .object geometry (large maps are split into cells), a .level + scene_object.part scene, DDS + .thm textures — one ZIP. Lights, sectors, the AI grid and the xrLC build are done in the SDK.",fields:{name:{label:"Level name",hint:'Latin letters, digits, "_": rawdata/objects/gmv/<name>, rawdata/levels/<name>.level'},cell:{label:"Cell, m",placeholder:"auto",hint:"Split the geometry into objects (0 — a single object; 32–64 suits maps)"},scale:{label:"Scale",hint:"Metres per model unit (X-Ray: 1 unit = 1 m)"},instances:{label:"Level objects as references",hint:"Each object model becomes one .object in props, the scene holds references with poses. Uncheck to merge objects into the cell geometry"}}}}},ec="эффект",nc=["db","xdb",..."0123456789".split("").flatMap(t=>["db"+t,"xdb"+t]),"dba","dbb","dbc","dbd"],Wm={motionLabel:"+ .omf",invertQuat:!0,archives:!0,archiveExts:[...nc,"exe"],archiveLabel:"Открыть .db…"},ra=(t,e,n)=>({id:t,group:"Игры",title:e,engine:"xray-v1",formats:["xray-ogf","xray-object","xray-level","xray-dm","xray-scene"],hint:n,ui:Wm,status:"beta",markers:["gamedata","fsgame.ltx"]}),sc=vt({id:"xray-v1",title:"X-Ray Engine 1.x",i18n:{en:Hm},formats:[{id:"xray-ogf",title:"X-Ray OGF",engine:"xray-v1",extensions:["ogf"],motionExtensions:["omf","skl","skls"],status:"beta",sniff:t=>At(t,0)===1?t[8]===4?1:.6:.2,load:async(t,e)=>(await Promise.resolve().then(()=>Cf)).loadOgf(t,e)},{id:"xray-object",title:"X-Ray SDK .object",engine:"xray-v1",extensions:["object"],motionExtensions:["omf","skl","skls"],status:"beta",sniff:t=>At(t,0)===30583?1:0,load:async(t,e)=>(await Promise.resolve().then(()=>Cf)).loadObject(t,e)},{id:"xray-level",title:"X-Ray: скомпилированный уровень (level + level.geom)",engine:"xray-v1",extensions:[],pathPattern:/(^|\/)levels\/[^/]+\/level$/,status:"beta",sniff:t=>At(t,0)===1&&At(t,4)===4&&At(t,8)!==16?.9:0,load:async(t,e)=>(await Promise.resolve().then(()=>x2)).loadLevel(t,e)},{id:"xray-dm",title:"X-Ray: деталь (.dm — трава, кусты)",engine:"xray-v1",extensions:["dm"],status:"beta",sniff:t=>/^[\w\\.-]{2,}\0/.test(Ln(t,0,Math.min(t.length,64)))?.8:0,load:async t=>{const{parseDM:e,dmMesh:n}=await Promise.resolve().then(()=>Gf),{boundsOf:s}=await Promise.resolve().then(()=>oa),o=e(new Uint8Array(await t.arrayBuffer())),r=n(o,o.texture);return{meshes:[r],skeleton:null,motions:[],bounds:s([r]),flipZ:!0,info:[`Деталь X-Ray: ${o.shader} · ${o.texture}`,`Вершин ${r.vertexCount}, треугольников ${o.indices.length/3}, масштаб ${o.minScale}…${o.maxScale}`]}}},{id:"xray-scene",title:"X-Ray SDK: уровень (.level + scene_object.part)",engine:"xray-v1",extensions:["level"],status:"beta",sniff:t=>/^\s*\[(build_params|camera|guid|level_options|level_tag|version)\]/.test(Ln(t,0,Math.min(t.length,64)))?1:0,load:async(t,e)=>(await Promise.resolve().then(()=>C2)).loadSceneLevel(t,e)}],archives:[{id:"xray-db",title:"X-Ray .db / .xdb (Чистое небо, Зов Припяти)",engine:"xray-v1",extensions:nc,companionExtensions:["exe"],openFromFolder:(t,e)=>At(t,0)===666||At(t,0)===0&&/^gamedata\.db[0-9a-z]$/i.test(e),sniff:t=>At(t,0)===666?1:At(t,0)===0?.5:0,open:async(t,e)=>(await Promise.resolve().then(()=>z2)).openXrayDb(t,e)}],exporters:[{id:"xray-sdk",title:"S.T.A.L.K.E.R.: исходники X-Ray SDK 0.7",target:"xray",description:"Для LevelEditor и ActorEditor (Зов Припяти, SDK 0.7): геометрия .object (большие карты режутся на ячейки), сцена .level + scene_object.part, текстуры DDS + .thm — одним ZIP. Свет, секторы, AI-сетку и сборку xrLC делаете в SDK.",from:["4a","xray-v1"],fields:[{id:"name",label:"Имя уровня",type:"text",hint:"Латиница, цифры, «_»: rawdata/objects/gmv/<имя>, rawdata/levels/<имя>.level",default:(t,e)=>e.toLowerCase().replace(/[^a-z0-9_]+/g,"_").replace(/^_+|_+$/g,"")||"level"},{id:"cell",label:"Ячейка, м",type:"number",min:0,step:8,placeholder:"авто",hint:"Разбиение геометрии на объекты (0 — одним объектом; для карт удобно 32–64)"},{id:"scale",label:"Масштаб",type:"number",min:0,step:.01,default:1,hint:"Сколько метров в одной единице модели (X-Ray: 1 единица = 1 м)"},{id:"instances",label:"Объекты уровня — ссылками",type:"checkbox",default:!0,hint:"Каждая модель объекта одним .object в props, в сцене — ссылки с позами. Снимите, чтобы влить объекты в геометрию ячеек",when:t=>!!t.instances}],run:async(t,e,n)=>{const{exportXraySdk:s}=await Promise.resolve().then(()=>K2),o=String(e.name||n.baseName);return{...await s(t,{name:o,cell:e.cell===""||e.cell===void 0?void 0:+e.cell,scale:+e.scale||1,hidden:n.hidden,texture:a=>n.texture(a),xraySource:n.sourceEngine==="xray-v1",instances:e.instances!==!1}),name:`${o}_xray_sdk`}}}],games:[ra("stalker-soc","S.T.A.L.K.E.R.: Тень Чернобыля","OGF v4. Анимации — внутри модели или во внешнем .omf. Архивы gamedata.db* открываются через «Папку игры» (ключ таблиц файлов берётся из bin/XR_3DA.exe вашей игры) или кнопкой «Открыть .db…» вместе с XR_3DA.exe; .xdb модов и распакованная gamedata тоже открываются. Внешние .omf из ссылок модели подключаются сами, если открыта папка gamedata или архивы; анимации торса играют вместе с ногами (выбор — «Другие части тела»). Уровни — с травой из level.details; исходники SDK: .object со скелетом и анимациями, .skl/.skls."),ra("stalker-cs","S.T.A.L.K.E.R.: Чистое небо","OGF v4. Откройте resources.db0–db2 (модели и текстуры) и patches/xpatch_*.db или папку gamedata. Внешние .omf из ссылок модели подключаются сами, если открыта папка gamedata или архивы; анимации торса играют вместе с ногами (выбор — «Другие части тела»). Уровни — с травой из level.details; исходники SDK: .object со скелетом и анимациями, .skl/.skls."),ra("stalker-cop","S.T.A.L.K.E.R.: Зов Припяти","OGF v4, в том числе 3/4-весовые вершины. Архивы .db — как у Чистого неба. Внешние .omf из ссылок модели подключаются сами, если открыта папка gamedata или архивы; анимации торса играют вместе с ногами (выбор — «Другие части тела»). Уровни — с травой из level.details; исходники SDK: .object со скелетом и анимациями, .skl/.skls.")]}),Km={presets:{fo3:{hint:"NIF 20.2.0.7. Open the Meshes/Textures .bsa and pick a model from the list (or load .nif and textures as files). Characters and creatures come with their skeleton (skeleton.nif) and .kf animations.",archiveLabel:"Open .bsa…"},fnv:{hint:"NIF 20.2.0.7. .bsa archives (zlib). Skinning via skeleton.nif, .kf animations (characters/_male/locomotion/mtidle.kf and others).",archiveLabel:"Open .bsa…"}}},Ym={archives:!0,archiveExts:["bsa"],archiveLabel:"Открыть .bsa…",zUp:!0,motionLabel:"+ .kf"};function oc(t){let e=0;for(;e<64&&e<t.length&&t[e]!==10;)e++;const n=e+1;return n+17>t.length||At(t,n+5)<3?0:At(t,n+13)}const rc=t=>It(t,"BSA\0")&&t.length>=8?At(t,4):0,ac=t=>/^(Gamebryo|NetImmerse)/.test(Ln(t,0,Math.min(t.length,20))),ic=(t,e,n,s)=>({id:t,group:"Игры",title:e,engine:"gamebryo",formats:["gamebryo-nif"],hint:n,ui:Ym,status:"stable",markers:s}),lc=vt({id:"gamebryo",title:"Gamebryo (Bethesda)",i18n:{en:Km},formats:[{id:"gamebryo-nif",title:"NetImmerse/Gamebryo NIF",engine:"gamebryo",extensions:["nif"],motionExtensions:["kf"],status:"stable",sniff:t=>ac(t)?oc(t)>=83?.5:1:0,load:async(t,e)=>(await Promise.resolve().then(()=>au)).loadNif(t,e)}],archives:[{id:"bsa",title:"Bethesda BSA (v103–105)",engine:"gamebryo",extensions:["bsa"],sniff:t=>{const e=rc(t);return e===103||e===104?1:e?.5:0},open:async t=>(await Promise.resolve().then(()=>iu)).openBsa(t)}],games:[ic("fo3","Fallout 3","NIF 20.2.0.7. Откройте Meshes/Textures .bsa и выберите модель из списка (или загрузите .nif и текстуры файлами). Персонажи и существа — со скелетом (skeleton.nif) и анимациями .kf.",["fallout3.esm"]),ic("fnv","Fallout: New Vegas","NIF 20.2.0.7. Архивы .bsa (zlib). Скининг по skeleton.nif, анимации .kf (characters/_male/locomotion/mtidle.kf и др.).",["falloutnv.esm"])]}),Zm={presets:{skyrim:{hint:"NIF 20.2.0.7, BSA v104. Skinning and .kf animations (Havok .hkx is not supported).",archiveLabel:"Open .bsa…"},skyrimse:{hint:"BSTriShape, BSA v105 (LZ4). Skinned SSE meshes are not shown yet.",archiveLabel:"Open .bsa…"}}},qm={archives:!0,archiveExts:["bsa"],archiveLabel:"Открыть .bsa…",zUp:!0,motionLabel:"+ .kf"},cc=(t,e,n,s)=>({id:t,group:"Игры",title:e,engine:"creation1",formats:["creation-nif"],hint:n,ui:qm,status:"stable",markers:s}),fc=vt({id:"creation1",title:"Creation Engine 1 (Bethesda)",i18n:{en:Zm},formats:[{id:"creation-nif",title:"Creation NIF (Skyrim LE/SE)",engine:"creation1",extensions:["nif"],motionExtensions:["kf"],status:"stable",sniff:t=>ac(t)&&oc(t)>=83?1:0,load:async(t,e)=>(await Promise.resolve().then(()=>au)).loadNif(t,e)}],archives:[{id:"creation-bsa",title:"Bethesda BSA (v104 Skyrim LE, v105 Skyrim SE)",engine:"creation1",extensions:["bsa"],sniff:t=>{const e=rc(t);return e===105?1:e===104?.5:0},open:async t=>(await Promise.resolve().then(()=>iu)).openBsa(t)}],games:[cc("skyrim","The Elder Scrolls V: Skyrim (LE)","NIF 20.2.0.7, BSA v104. Скининг и анимации .kf (Havok .hkx не поддержан).",["skyrim.esm"]),cc("skyrimse","The Elder Scrolls V: Skyrim Special Edition","BSTriShape, BSA v105 (LZ4). Скинованные меши SSE пока не показываются.",["skyrim.esm","skyrimse.exe"])]});function Qm(t){return t&4294901760?(t>>>14&261888|t>>>16&63|196608)>>>0:t<<8>>>0}function we(t,e,n){const s=new DataView(t),o=[];let r=e;for(;r+12<=n;){const a=s.getUint32(r,!0),i=s.getUint32(r+4,!0),l=s.getUint32(r+8,!0);o.push({type:a,size:i,ver:Qm(l),start:r+12,end:Math.min(r+12+i,n)}),r+=12+i}return o}const Jm={827611204:1,861165636:3,894720068:5},t1={1:1,2:3,3:3,4:5,5:5},e1=(t,e,n)=>n?Math.max(1,t+3>>2)*Math.max(1,e+3>>2)*(n===1?8:16):0;function n1(t,e){const n=we(t,e.start,e.end),s=n.find(b=>b.type===1);if(!s)return null;const o=new DataView(t),r=new Uint8Array(t);let a=s.start;const i=o.getUint32(a,!0);if(a+=8,i!==8&&i!==9)throw new Error("платформа TXD "+i+" не поддерживается (нужен PC D3D8/D3D9)");const l=Uo(r,a,a+32).toLowerCase();a+=64;const c=o.getUint32(a,!0);a+=4;const f=o.getUint32(a,!0);a+=4;const h=o.getUint16(a,!0),u=o.getUint16(a+2,!0),d=r[a+5],p=r[a+7];a+=8;let m=Jm[f]??t1[p]??0;const g=(c&8192)!==0,w=(c&16384)!==0;let y=null;if(g?(y=r.subarray(a,a+1024),a+=1024):w&&(y=r.subarray(a,a+64),a+=128),!d)return null;const M=o.getUint32(a,!0);if(a+=4,m&&M!==e1(h,u,m)&&(m=0),m)return{name:l,width:h,height:u,rgba:_e(o,a,h,u,m)};const x=new Uint8Array(h*u*4),v=c&3840;for(let b=0;b<h*u;b++){let k=0,E=0,A=0,S=255;if(g){const $=r[a+b]*4;k=y[$],E=y[$+1],A=y[$+2],S=y[$+3]}else if(w){const $=r[a+(b>>1)],T=(b&1?$>>4:$&15)*4;k=y[T],E=y[T+1],A=y[T+2],S=y[T+3]}else if(v===1280||v===1536){const $=a+b*4;A=r[$],E=r[$+1],k=r[$+2],S=v===1280?r[$+3]:255}else if(v===256||v===2560){const $=o.getUint16(a+b*2,!0);k=($>>10&31)*255/31,E=($>>5&31)*255/31,A=($&31)*255/31,S=v===256?$>>15?255:0:255}else if(v===512){const $=o.getUint16(a+b*2,!0);k=($>>11)*255/31,E=($>>5&63)*255/63,A=($&31)*255/31}else if(v===768){const $=o.getUint16(a+b*2,!0);S=($>>12)*17,k=($>>8&15)*17,E=($>>4&15)*17,A=($&15)*17}else if(v===1024)k=E=A=r[a+b];else throw new Error("неизвестный формат растра 0x"+c.toString(16));x[b*4]=k,x[b*4+1]=E,x[b*4+2]=A,x[b*4+3]=S}return{name:l,width:h,height:u,rgba:x}}function s1(t){const e=we(t,0,t.byteLength).find(o=>o.type===22);if(!e)throw new Error("Не TXD (нет Texture Dictionary)");const n=[],s=[];for(const o of we(t,e.start,e.end))if(o.type===21)try{const r=n1(t,o);r&&n.push(r)}catch(r){s.push(r.message)}return{textures:n,errors:s}}const o1={presets:{gta3:{hint:"DFF + TXD from gta3.img. For IMG v1 select the .img and the .dir with the same name together. Animations (IFP) — later.",archiveLabel:"Open .img (+ .dir for III/VC)…"},gtavc:{hint:"DFF + TXD from gta3.img (IMG v1: .img + .dir).",archiveLabel:"Open .img (+ .dir for III/VC)…"},gtasa:{hint:"DFF + TXD from gta3.img / player.img (IMG v2, single file). Character skinning is supported.",archiveLabel:"Open .img (+ .dir for III/VC)…"}}},r1={archives:!0,archiveExts:["img","dir"],archiveLabel:"Открыть .img (+ .dir для III/VC)…",zUp:!0},aa=(t,e,n,s)=>({id:t,group:"Игры",title:e,engine:"renderware-v3",formats:["rw-dff"],hint:n,ui:r1,status:"beta",markers:s}),uc=vt({id:"renderware-v3",title:"RenderWare 3",i18n:{en:o1},formats:[{id:"rw-dff",title:"RenderWare DFF",engine:"renderware-v3",extensions:["dff"],status:"beta",sniff:t=>At(t,0)===16?1:At(t,0)===43?.6:.2,load:async t=>(await Promise.resolve().then(()=>rg)).convertDff(await t.arrayBuffer()),async afterLoad({main:t,addTextureDictionary:e}){if(!t.archive||!t.path)return;const n=t.archive.lookup(t.path.replace(/\.dff$/,".txd"));n&&await e(t.archive.fileOf(n))}}],archives:[{id:"img",title:"GTA IMG (v1 .img + .dir, v2 VER2)",engine:"renderware-v3",extensions:["img"],companionExtensions:["dir"],sniff:t=>It(t,"VER2")?1:.5,async open(t,e){const n=t.name.toLowerCase().replace(/\.img$/,""),s=e.find(o=>o.name.toLowerCase()===n+".dir")??null;return(await Promise.resolve().then(()=>ig)).openImg(t,s)}}],dictionaries:[{id:"txd",extensions:["txd"],parse:t=>s1(t)}],games:[aa("gta3","Grand Theft Auto III","DFF + TXD из gta3.img. Для IMG v1 выберите .img и одноимённый .dir вместе. Анимации (IFP) — позже.",["gta3.exe"]),aa("gtavc","Grand Theft Auto: Vice City","DFF + TXD из gta3.img (IMG v1: .img + .dir).",["gta-vc.exe"]),aa("gtasa","Grand Theft Auto: San Andreas","DFF + TXD из gta3.img / player.img (IMG v2, один файл). Скин персонажей поддержан.",["gta_sa.exe"])]}),a1={presets:{penumbra:{hint:'Collada models (.dae) via assimp. Choose "Game folder" — models and textures are found automatically.'}}},i1={zUp:!0},hc=vt({id:"hpl1",title:"HPL Engine 1",i18n:{en:a1},formats:[{id:"hpl1-dae",title:"HPL1 Collada (.dae)",engine:"hpl1",extensions:["dae"],status:"beta",load:async(t,e)=>(await Promise.resolve().then(()=>lf)).loadWithAssimp(e.files,t)}],games:[((t,e,n)=>({id:t,group:"Игры",title:e,engine:"hpl1",formats:["hpl1-dae"],hint:n,ui:i1,status:"beta"}))("penumbra","Penumbra: Overture / Black Plague / Requiem (HPL1)","Модели Collada (.dae) через assimp. Выберите «Папку игры» — модели и текстуры найдутся сами.")]});function l1(t){const e=/<\s*Diffuse\b[^>]*?\bFile\s*=\s*"([^"]*)"/i.exec(t);return e?e[1]:""}const c1={presets:{amnesia:{hint:".dae (Collada) models + .mat materials. Skeletons and animations are inside the .dae. Binary .msh is not supported yet."},amfp:{hint:"Same as TDD: .dae + .mat."}}},f1={zUp:!0},dc=(t,e,n)=>({id:t,group:"Игры",title:e,engine:"hpl2",formats:["hpl2-dae"],hint:n,ui:f1,status:"beta"}),pc=vt({id:"hpl2",title:"HPL Engine 2",i18n:{en:c1},formats:[{id:"hpl2-dae",title:"HPL2 Collada (.dae) + .mat",engine:"hpl2",extensions:["dae"],status:"beta",load:async(t,e)=>(await Promise.resolve().then(()=>lf)).loadWithAssimp(e.files,t)}],materials:[{id:"hpl-mat",async resolve(t,e){if(!t.material)return null;const n=Ge(t.material).toLowerCase().replace(/\.[^.]+$/,""),s=e.findByBase(n+".mat");if(!s)return null;const o=l1(await e.readText(s));return o?e.find(o):null}}],games:[dc("amnesia","Amnesia: The Dark Descent (HPL2)","Модели .dae (Collada) + материалы .mat. Скелеты и анимации — внутри .dae. Бинарный .msh пока не поддержан."),dc("amfp","Amnesia: A Machine for Pigs (HPL2)","Как для TDD: .dae + .mat.")]}),u1={presets:{quake:{hint:"Open pak0.pak: the palette (gfx/palette.lmp) is taken from it. The skin is embedded in the MDL.",archiveLabel:"Open .pak…"},quake2:{hint:"Models from pak0.pak; PCX skins are looked up in the archive. Animations are grouped by frame names.",archiveLabel:"Open .pak…"}}},mc=t=>({archives:!0,archiveExts:[t],archiveLabel:`Открыть .${t}…`,zUp:!0}),gc=vt({id:"idtech2",normalY:"gl",title:"id Tech 2",i18n:{en:u1},formats:[{id:"idtech-mdl",title:"Quake MDL",engine:"idtech2",extensions:["mdl"],status:"beta",sniff:t=>It(t,"IDPO")?1:0,async load(t,e){const{loadMdl:n}=await Promise.resolve().then(()=>cu);return n(await t.arrayBuffer(),await e.readPath("gfx/palette.lmp"))}},{id:"idtech-md2",title:"Quake II MD2",engine:"idtech2",extensions:["md2"],status:"beta",sniff:t=>It(t,"IDP2")?1:0,load:async t=>(await Promise.resolve().then(()=>cu)).loadMd2(await t.arrayBuffer())}],archives:[{id:"pak",title:"Quake PAK",engine:"idtech2",extensions:["pak"],sniff:t=>It(t,"PACK")?1:0,open:async t=>(await Promise.resolve().then(()=>dg)).openPak(t)}],games:[{id:"quake",group:"Игры",title:"Quake (id Tech 2) — MDL",engine:"idtech2",formats:["idtech-mdl"],ui:mc("pak"),status:"beta",markers:["id1/pak0.pak"],hint:"Откройте pak0.pak: палитра (gfx/palette.lmp) берётся оттуда. Скин встроен в MDL."},{id:"quake2",group:"Игры",title:"Quake II — MD2",engine:"idtech2",formats:["idtech-md2"],ui:mc("pak"),status:"beta",markers:["baseq2/pak0.pak"],hint:"Модели из pak0.pak, скины PCX ищутся в архиве. Анимации — по группам кадров."}]}),wc=vt({id:"idtech3",normalY:"gl",title:"id Tech 3",i18n:{en:{presets:{quake3:{hint:"pk3 = ZIP. TGA/JPG textures by shader name; .shader scripts are not parsed yet.",archiveLabel:"Open .pk3…"}}}},formats:[{id:"idtech-md3",title:"Quake III MD3",engine:"idtech3",extensions:["md3"],status:"beta",sniff:t=>It(t,"IDP3")?1:0,load:async t=>(await Promise.resolve().then(()=>mg)).loadMd3(await t.arrayBuffer())}],games:[{id:"quake3",group:"Игры",title:"Quake III Arena — MD3",engine:"idtech3",formats:["idtech-md3"],ui:(t=>({archives:!0,archiveExts:[t],archiveLabel:`Открыть .${t}…`,zUp:!0}))("pk3"),status:"beta",markers:["baseq3/pak0.pk3"],hint:"pk3 = ZIP. Текстуры TGA/JPG по имени шейдера; .shader-скрипты пока не разбираются."}]});function h1(t,e=new Map){const n=t.replace(/\/\*[\s\S]*?\*\//g," ").replace(/\/\/.*$/gm," "),s=/"([^"]*)"|\{|\}|[^\s{}"]+/g;let o=0,r="",a="",i=null,l=!1,c;const f=[];for(;c=s.exec(n);)f.push(c[1]!==void 0?c[1]:c[0]);for(let h=0;h<f.length;h++){const u=f[h],d=u.toLowerCase();if(u==="{")o===0&&(r=a.toLowerCase(),i={diffuse:"",editor:"",anyMap:""}),o++,l=o>1?!1:l;else if(u==="}"){if(o--,o===0&&i){const p=i.diffuse||i.editor||i.anyMap;p&&r&&!e.has(r)&&e.set(r,p),i=null}l=!1}else if(o===0)d!=="table"&&d!=="material"&&d!=="skin"&&d!=="particle"&&(a=u);else if(i){if(d==="diffusemap"&&(f[h-1]??"").toLowerCase()!=="blend"&&f[h+1]&&f[h+1]!=="{")i.diffuse||(i.diffuse=f[h+1]);else if(d==="qer_editorimage")i.editor||(i.editor=f[h+1]??"");else if(d==="blend"&&(f[h+1]??"").toLowerCase()==="diffusemap")l=!0;else if(d==="map"){let p=h+1;for(;/^(clamp|zeroclamp|alphazeroclamp)$/i.test(f[p]??"");)p++;const m=f[p]??"";l&&!i.diffuse?i.diffuse=m:!i.anyMap&&m&&m!=="{"&&m!=="}"&&(i.anyMap=m)}}}return e}const d1={presets:{doom3:{hint:"pk4 = ZIP. Textures come from .mtr materials, .md5anim animations from the model folder.",archiveLabel:"Open .pk4…"},quake4:{hint:"The same MD5 format as Doom 3.",archiveLabel:"Open .pk4…"}}},yc={...(t=>({archives:!0,archiveExts:[t],archiveLabel:`Открыть .${t}…`,zUp:!0}))("pk4"),motionLabel:"+ .md5anim"},bc=vt({id:"idtech4",normalY:"gl",title:"id Tech 4",i18n:{en:d1},formats:[{id:"idtech-md5",title:"Doom 3 MD5mesh",engine:"idtech4",extensions:["md5mesh"],motionExtensions:["md5anim"],status:"beta",sniff:t=>/MD5Version/.test(Ln(t,0,Math.min(t.length,64)))?1:.2,load:async t=>(await Promise.resolve().then(()=>bg)).loadMd5(Wt(await t.arrayBuffer())),async afterLoad(t){await Vl(t,"md5anim")}}],materials:[{id:"idtech-mtr",async resolve(t,e){if(!t.texture)return null;const n=await e.memo("idtech:mtr",async()=>{const s=new Map;for(const o of e.archives)for(const r of o.entries)if(r.path.endsWith(".mtr"))try{h1(Wt(await o.read(r)),s)}catch{}return s});if(!n.size)return null;for(const s of zs.candidates(t.texture))for(const o of[n.get(s),s+"_d"]){if(!o)continue;const r=e.find(o);if(r)return r}return null}}],games:[{id:"doom3",group:"Игры",title:"Doom 3 — MD5 (id Tech 4)",engine:"idtech4",formats:["idtech-md5"],ui:yc,status:"beta",markers:["base/pak000.pk4"],hint:"pk4 = ZIP. Текстуры берутся из материалов .mtr, анимации .md5anim — из папки модели."},{id:"quake4",group:"Игры",title:"Quake 4 — MD5 (id Tech 4)",engine:"idtech4",formats:["idtech-md5"],ui:yc,status:"beta",markers:["q4base/pak001.pk4"],hint:"Тот же формат MD5, что в Doom 3."}]}),p1={title:"Unreal Engine 2",presets:{lineage2:{title:"Lineage II (Unreal Engine 2) — static meshes, characters and monsters",hint:"Open “Game folder” (the client folder with StaticMeshes, Animations, Textures, SysTextures) — the packages are decrypted and attached automatically. Static meshes are StaticMesh objects from .usx, characters and monsters are SkeletalMesh objects from .ukx with the animations of their package; textures and materials (Shader, FinalBlend) come from .utx. Maps MAPS/xx_yy.unr show the terrain with its layers and every StaticMeshActor (meshes and textures come from the open packages). Lineage2Ver111/121 encryption is supported (Prelude, C1–C4); the RSA of later chronicles (Ver41x) is not.",archiveLabel:"Open packages…"},ue2:{title:"Unreal Engine 2 (UT2004, Splinter Cell, Postal 2…) — PSK/PSA export",hint:'Export the model with UModel (UEViewer) to PSK/PSKX (+ PSA for animations, textures as TGA/PNG) and open the export folder with "Game folder". Only Lineage II packages are read natively (separate entry).'}}},Bs={motionLabel:"+ .psa",zUp:!0};function m1(t,e){const n=Ge(t).toLowerCase().replace(/\.[^.]+$/,""),s=n.replace(/^(mi|m|mat)_/,""),o=new Set;for(const r of[n,s,"t_"+s])for(const a of["","_d","_diffuse","_basecolor","_albedo","_color","_bc","_df"])o.add(r+a);for(const r of o){const a=e.find(r);if(a)return a}return null}function Vs(t){return{id:`${t}-psk`,title:"ActorX PSK/PSKX",engine:t,extensions:["psk","pskx"],motionExtensions:["psa"],status:"bridge",sniff:e=>It(e,"ACTRHEAD")?1:.2,load:async e=>(await Promise.resolve().then(()=>Ag)).loadPsk(await e.arrayBuffer()),resolveTexture:async(e,n,s)=>e.texture?m1(e.texture,s):null}}const vc=vt({id:"unreal2",title:"Unreal Engine 2",i18n:{en:p1},archives:[{id:"ue2",title:"Пакеты Unreal Engine 2 (.usx, .utx, .ukx; Lineage II — с расшифровкой)",engine:"unreal2",extensions:["usx","utx","ukx"],sniff:t=>At(t,0)===2653586369||/^L\0i\0n\0e\0a\0g\0e\x002\0V\0e\0r\0/.test(String.fromCharCode(...t.subarray(0,22)))?1:0,openFromFolder:(t,e)=>/\.(usx|utx|ukx)$/i.test(e),open:async t=>(await Promise.resolve().then(()=>Au)).openUe2Package(t)}],codecs:[{id:"ue2-texture",extensions:["utex"],decode:async t=>(await Promise.resolve().then(()=>Au)).unpackRgba(t)}],formats:[{id:"ue2-staticmesh",title:"Unreal Engine 2: StaticMesh (из пакета .usx)",engine:"unreal2",extensions:["ue2sm"],status:"beta",load:async t=>(await Promise.resolve().then(()=>Fu)).loadUe2StaticMesh(t)},{id:"ue2-map",title:"Unreal Engine 2: карта (.unr — ландшафт и StaticMesh)",engine:"unreal2",extensions:["unr"],status:"beta",sniff:t=>At(t,0)===2653586369||/^L\0i\0n\0e\0a\0g\0e\x002\0/.test(String.fromCharCode(...t.subarray(0,16)))?1:0,load:async t=>(await Promise.resolve().then(()=>Kg)).loadUe2Map(t)},{id:"ue2-skeletalmesh",title:"Unreal Engine 2: SkeletalMesh с анимациями (из пакета .ukx)",engine:"unreal2",extensions:["ue2skm"],status:"beta",load:async t=>(await Promise.resolve().then(()=>Fu)).loadUe2SkeletalMesh(t)},Vs("unreal2")],games:[{id:"lineage2",group:"Игры",title:"Lineage II (Unreal Engine 2) — статика, персонажи и монстры",engine:"unreal2",formats:["ue2-staticmesh","ue2-skeletalmesh","ue2-map"],status:"beta",markers:["System/l2.exe","StaticMeshes","SysTextures"],ui:{archives:!0,archiveExts:["usx","utx","ukx"],archiveLabel:"Открыть пакеты…",zUp:!0},hint:"Откройте «Папку игры» (папку клиента с StaticMeshes, Animations, Textures, SysTextures) — пакеты расшифруются и подключатся сами. Статика — объекты StaticMesh из .usx, персонажи и монстры — SkeletalMesh из .ukx с анимациями своего пакета; текстуры и материалы (Shader, FinalBlend) — из .utx. Карты MAPS/xx_yy.unr — ландшафт со слоями и все StaticMeshActor (меши и текстуры берутся из открытых пакетов). Поддержано шифрование Lineage2Ver111/121 (Prelude, C1–C4); RSA поздних хроник (Ver41x) — нет."},{id:"ue2",group:"Игры",title:"Unreal Engine 2 (UT2004, Splinter Cell, Postal 2…) — экспорт PSK/PSA",engine:"unreal2",formats:["unreal2-psk"],ui:Bs,status:"bridge",hint:"Экспортируйте модель утилитой UModel (UEViewer) в PSK/PSKX (+ PSA для анимаций, текстуры в TGA/PNG) и выберите «Папку игры» с экспортом. Нативно читаются только пакеты Lineage II (отдельный пункт)."}]}),xc=vt({id:"unreal1",title:"Unreal Engine 1, через экспорт PSK",i18n:{en:{title:"Unreal Engine 1, via PSK export",presets:{ue1:{title:"Unreal Engine 1 (Unreal, Unreal Tournament, Deus Ex…) — PSK/PSA export",hint:'Export the model with UModel (UEViewer) to PSK/PSKX (+ PSA for animations, textures as TGA/PNG) and open the export folder with "Game folder". Native .u/.utx are not read yet.'}}}},formats:[Vs("unreal1")],games:[{id:"ue1",group:"Игры",title:"Unreal Engine 1 (Unreal, Unreal Tournament, Deus Ex…) — экспорт PSK/PSA",engine:"unreal1",formats:["unreal1-psk"],ui:Bs,status:"bridge",hint:"Экспортируйте модель утилитой UModel (UEViewer) в PSK/PSKX (+ PSA для анимаций, текстуры в TGA/PNG) и выберите «Папку игры» с экспортом. Нативные .u/.utx пока не читаются."}]}),Mc=vt({id:"unreal3",title:"Unreal Engine 3, через экспорт PSK",i18n:{en:{title:"Unreal Engine 3, via PSK export",presets:{ue3:{title:"Unreal Engine 3 (Gears of War, Mass Effect, BioShock Infinite…) — PSK/PSA export",hint:'Export the model with UModel (UEViewer) to PSK/PSKX (+ PSA for animations, textures as TGA/PNG) and open the export folder with "Game folder". Native .upk/.u are not read yet.'}}}},formats:[Vs("unreal3")],games:[{id:"ue3",group:"Игры",title:"Unreal Engine 3 (Gears of War, Mass Effect, BioShock Infinite…) — экспорт PSK/PSA",engine:"unreal3",formats:["unreal3-psk"],ui:Bs,status:"bridge",hint:"Экспортируйте модель утилитой UModel (UEViewer) в PSK/PSKX (+ PSA для анимаций, текстуры в TGA/PNG) и выберите «Папку игры» с экспортом. Нативные .upk/.u пока не читаются."}]}),kc=vt({id:"unreal4",title:"Unreal Engine 4, через экспорт PSK",i18n:{en:{title:"Unreal Engine 4, via PSK export",presets:{ue4:{title:"Unreal Engine 4 (PUBG, ARK, Borderlands 3…) — PSK/PSA export (FModel/UModel)",hint:'FModel/UModel: export to PSK/PSKX/PSA and textures to PNG. Open glTF from FModel via "Formats (assimp) → glTF". Native .pak/.uasset are not read yet.'}}}},formats:[Vs("unreal4")],games:[{id:"ue4",group:"Игры",title:"Unreal Engine 4 (PUBG, ARK, Borderlands 3…) — экспорт PSK/PSA (FModel/UModel)",engine:"unreal4",formats:["unreal4-psk"],ui:Bs,status:"bridge",hint:"FModel/UModel: экспорт в PSK/PSKX/PSA и текстур в PNG. glTF из FModel открывайте через «Форматы (assimp) → glTF». Нативные .pak/.uasset пока не читаются."}]}),Ec=vt({id:"unreal5",title:"Unreal Engine 5, через экспорт PSK",i18n:{en:{title:"Unreal Engine 5, via PSK export",presets:{ue5:{title:"Unreal Engine 5 (Fortnite, S.T.A.L.K.E.R. 2…) — PSK/PSA export (FModel)",hint:'FModel: export to PSK/PSKX/PSA and textures to PNG. Open glTF from FModel via "Formats (assimp) → glTF". Native .pak/.utoc/.uasset are not read yet.'}}}},formats:[Vs("unreal5")],games:[{id:"ue5",group:"Игры",title:"Unreal Engine 5 (Fortnite, S.T.A.L.K.E.R. 2…) — экспорт PSK/PSA (FModel)",engine:"unreal5",formats:["unreal5-psk"],ui:Bs,status:"bridge",hint:"FModel: экспорт в PSK/PSKX/PSA и текстур в PNG. glTF из FModel открывайте через «Форматы (assimp) → glTF». Нативные .pak/.utoc/.uasset пока не читаются."}]}),Ac=vt({id:"goldsrc",title:"GoldSrc (Valve)",i18n:{en:{presets:{goldsrc:{hint:'MDL with animations. External textures (*T.mdl) and animations (*01.mdl) are picked up when you open the "Game folder…" (models folder). BSP/WAD maps — not yet.'}}}},formats:[{id:"goldsrc-mdl",title:"GoldSrc MDL (v10)",engine:"goldsrc",extensions:["mdl"],status:"beta",sniff:t=>!It(t,"IDST")||At(t,4)!==10||t.length>=208&&At(t,204)===0&&At(t,180)>0?0:1,load:async(t,e)=>(await Promise.resolve().then(()=>qg)).loadGoldsrcMdl(t,e)}],games:[{id:"goldsrc",group:"Игры",title:"Half-Life 1 / Counter-Strike 1.6 (GoldSrc)",engine:"goldsrc",formats:["goldsrc-mdl"],ui:{zUp:!0},status:"beta",markers:["valve/liblist.gam"],hint:"MDL с анимациями. Текстуры во внешнем *T.mdl и анимации в *01.mdl подтянутся, если открыть «Папку игры…» (папка models). Карты BSP/WAD пока нет."}]}),ia={0:4,1:4,2:3,3:3,4:2,5:1,6:2,7:1,8:1,11:4,12:4,16:4,17:2,18:2,19:2,21:2,22:2};function $c(t,e,n){if(t===13||t===20)return Math.max(1,e+3>>2)*Math.max(1,n+3>>2)*8;if(t===14||t===15)return Math.max(1,e+3>>2)*Math.max(1,n+3>>2)*16;if(!(t in ia))throw new Error("VTF: формат "+t+" не поддерживается");return e*n*ia[t]}function g1(t){const e=new DataView(t),n=new Uint8Array(t);if(t.byteLength<64||e.getUint32(0,!0)!==4609110)throw new Error("Не VTF");const s=e.getUint32(12,!0),o=e.getUint16(16,!0),r=e.getUint16(18,!0),a=e.getUint32(20,!0),i=e.getUint16(24,!0)||1,l=e.getInt32(52,!0),c=n[56]||1,f=e.getInt32(57,!0),h=n[61],u=n[62];let d=s;f>=0&&h&&u&&(d+=$c(f,h,u));const p=a&16384?6:1;for(let w=c-1;w>=1;w--)d+=$c(l,Math.max(1,o>>w),Math.max(1,r>>w))*i*p;if(l===13||l===20||l===14||l===15)return{width:o,height:r,rgba:_e(e,d,o,r,l===14?3:l===15?5:1)};const m=ia[l];if(!m)throw new Error("VTF: формат "+l+" не поддерживается");const g=new Uint8Array(o*r*4);for(let w=0;w<o*r;w++){const y=d+w*m;let M=0,x=0,v=0,b=255;switch(l){case 0:M=n[y],x=n[y+1],v=n[y+2],b=n[y+3];break;case 1:b=n[y],v=n[y+1],x=n[y+2],M=n[y+3];break;case 2:M=n[y],x=n[y+1],v=n[y+2];break;case 3:v=n[y],x=n[y+1],M=n[y+2];break;case 11:b=n[y],M=n[y+1],x=n[y+2],v=n[y+3];break;case 12:v=n[y],x=n[y+1],M=n[y+2],b=n[y+3];break;case 16:v=n[y],x=n[y+1],M=n[y+2];break;case 4:{const k=e.getUint16(y,!0);M=(k>>11)*255/31,x=(k>>5&63)*255/63,v=(k&31)*255/31;break}case 17:{const k=e.getUint16(y,!0);v=(k>>11)*255/31,x=(k>>5&63)*255/63,M=(k&31)*255/31;break}case 5:case 7:M=x=v=n[y];break;case 8:b=n[y];break;case 6:M=x=v=n[y],b=n[y+1];break;default:throw new Error("VTF: формат "+l+" не поддерживается")}g[w*4]=M,g[w*4+1]=x,g[w*4+2]=v,g[w*4+3]=b}return{width:o,height:r,rgba:g}}function w1(t){return ye(t,"$basetexture")}function ye(t,e){const n=e.replace(/\$/g,"\\$"),s=t.replace(/\/\/[^\r\n]*/g,""),o=new RegExp(`(?:^|[\\s{])"?${n}"?[ \\t]+(?:"([^"\\r\\n]*)"|([^\\s"{}]+))`,"i").exec(s);return o?(o[1]??o[2]??"").trim():""}function zo(t){const e=(/^\s*"?([\w.]+)"?/.exec(t.replace(/^\uFEFF/,"").replace(/^\s*\/\/.*$/gm,""))?.[1]??"").toLowerCase(),n=s=>{const o=ye(t,s);return!!o&&o!=="0"};return{shader:e,base:w1(t),include:e==="patch"?ye(t,"include"):"",alphaTest:n("$alphatest"),translucent:n("$translucent")||n("$additive")}}const xn=t=>t.replace(/\\/g,"/").toLowerCase().replace(/^\/+/,"").replace(/^materials\//,"").replace(/\.(vmt|vtf|tth)$/,""),y1={presets:{source:{hint:'MDL + VVD + VTX (v44–49). Open *_dir.vpk together with *_000.vpk… or the "Game folder…". VMT/VTF materials are matched automatically. No skeleton or animations yet.',archiveLabel:"Open *_dir.vpk (+ parts)…"},vtmb:{hint:'MDL v2531 models (+ .dx80.vtx) and BSP v17 maps (maps/*.bsp: brushes, displacements, lightmap, props). .tth/.ttz textures via VMT. Open the "Game folder…" — the whole game folder: Vampire/pack*.vpk and Unofficial_Patch are picked up automatically. Characters get their skeleton and animations — their own and the shared ones from include models (models/character/shared/…, ~1,500 per character, frames are read on selection).',archiveLabel:"Open packNNN.vpk…"}}};function la(t,e){const n=e.findEntry(t+".vtf");if(n)return n[0].fileOf(n[1]);const s=e.findEntry(t+".tth");if(!s)return null;const o=e.findEntry(t+".ttz");return{name:Ge(t)+".vtf",path:t+".vtf",arrayBuffer:async()=>(await Promise.resolve().then(()=>Jg)).tthToVtf(await s[0].read(s[1]),o?await o[0].read(o[1]):null)}}async function Sc(t,e){for(let n of t)if(!/\.vtf$/.test(n))for(let s=0;s<3;s++){const o=e.findEntry(n);if(!o)break;const r=await e.readText(o),a=zo(r);if(a.include){n="materials/"+xn(a.include)+".vmt";continue}return r}return null}async function Tc(t,e){for(const n of t){if(/\.vtf$/.test(n)){const a=la(n.replace(/\.vtf$/,""),e);if(a)return a;continue}const s=await Sc([n],e),o=s?zo(s).base:"";if(!o)continue;const r=la("materials/"+xn(o),e);if(r)return r}return null}async function Uc(t,e){const n=await Sc(t,e);if(!n)return null;const s=zo(n).shader,o=/^(water|refract)$/.test(s)?ye(n,"$normalmap"):ye(n,"$bumpmap");if(!o||/_dudv$/i.test(o))return null;const r=la("materials/"+xn(o),e);if(!r)return null;const a=await e.decode(r);return{normal:{image:a,packing:"rgb",label:`N ${a.width}×${a.height}`}}}const Fc=t=>t.materialPaths?.length?t.materialPaths:t.texture?["materials/"+t.texture+".vmt"]:[],_c=vt({id:"source1",title:"Source 1 (Valve, Troika)",i18n:{en:y1},formats:[{id:"source-mdl",title:"Source MDL (v44–49, VTMB v2531)",engine:"source1",extensions:["mdl"],status:"beta",sniff:t=>{if(!It(t,"IDST"))return 0;const e=At(t,4);return e>=44&&e<=49||e===2531?1:e===10?0:.5},load:async(t,e)=>(await Promise.resolve().then(()=>u3)).loadSourceMdl(t,e),resolveTexture:(t,e,n)=>Tc(t.materialPaths??[],n),resolveMaps:(t,e,n)=>Uc(t.materialPaths??[],n)},{id:"source-bsp",title:"Source BSP (VTMB v17; v19–21 без проверки)",engine:"source1",extensions:["bsp"],status:"beta",sniff:t=>It(t,"VBSP")?1:0,load:async(t,e)=>(await Promise.resolve().then(()=>x3)).loadSourceBsp(t,e),resolveTexture:(t,e,n)=>Tc(Fc(t),n),resolveMaps:(t,e,n)=>Uc(Fc(t),n)}],archives:[{id:"vpk",title:"Valve VPK v1/v2",engine:"source1",extensions:["vpk"],sniff:(t,e)=>At(t,0)===1437209140?1:/^pack\d{3}\.vpk$/i.test(e)?0:.5,async open(t,e){const{openVpk:n,vpkPart:s}=await Promise.resolve().then(()=>E3),o=s(t.name);if(o&&o[1]!=="dir")return null;const r=new Map;if(o)for(const a of e){const i=s(a.name);i&&i[0]===o[0]&&i[1]!=="dir"&&r.set(parseInt(i[1],10),a)}return n(t,r)}},{id:"vpk-troika",title:"VPK Troika (Vampire: The Masquerade – Bloodlines, packNNN.vpk)",engine:"source1",extensions:["vpk"],sniff:(t,e)=>At(t,0)!==1437209140&&/^pack\d{3}\.vpk$/i.test(e)?1:0,openFromFolder:(t,e)=>At(t,0)!==1437209140&&/^pack\d{3}\.vpk$/i.test(e),async open(t){return t.size<=9?null:(await Promise.resolve().then(()=>S3)).openTroikaVpk(t)}}],codecs:[{id:"vtf",extensions:["vtf"],decode:t=>g1(t)}],games:[{id:"source",group:"Игры",title:"Source: HL2, CS:S, TF2, GMod, L4D, Portal, CS:GO…",engine:"source1",formats:["source-mdl"],status:"beta",markers:["gameinfo.txt"],ui:{archives:!0,archiveExts:["vpk"],archiveLabel:"Открыть *_dir.vpk (+ части)…",zUp:!0},hint:"MDL + VVD + VTX (v44–49). Откройте *_dir.vpk вместе с *_000.vpk… или «Папку игры…». Материалы VMT/VTF подбираются сами. У персонажей — скелет и анимации, свои и общие из include-моделей (models/character/shared/…, ~1 500 на персонажа, кадры читаются при выборе)."},{id:"vtmb",group:"Игры",title:"Vampire: The Masquerade – Bloodlines (Troika Source)",engine:"source1",formats:["source-mdl","source-bsp"],status:"beta",markers:["vampire/pack001.vpk"],ui:{archives:!0,archiveExts:["vpk"],archiveLabel:"Открыть packNNN.vpk…",zUp:!0},hint:"Модели MDL v2531 (+ .dx80.vtx) и карты BSP v17 (maps/*.bsp: браши, дисплейсменты, карта освещения, реквизит). Текстуры .tth/.ttz через VMT. Откройте «Папку игры…» — папку игры целиком: Vampire/pack*.vpk и Unofficial_Patch подключатся сами. У персонажей — скелет и анимации, свои и общие из include-моделей (models/character/shared/…, ~1 500 на персонажа, кадры читаются при выборе)."}]}),Cc=vt({id:"source2",title:"Source 2 (Valve), через экспорт glTF",i18n:{en:{title:"Source 2 (Valve), via glTF export",presets:{source2:{title:"Source 2: Dota 2, CS2, Half-Life: Alyx — via glTF export",hint:"Native vmdl_c/vtex_c are not read yet. Export models to glTF/GLB with Source 2 Viewer (ValveResourceFormat) and open them here."}}}},games:[{id:"source2",group:"Игры",title:"Source 2: Dota 2, CS2, Half-Life: Alyx — через экспорт glTF",engine:"source2",formats:["assimp"],extensions:["glb","gltf"],ui:{zUp:!0},status:"bridge",hint:"Нативные vmdl_c/vtex_c пока не читаются. Экспортируйте модели в glTF/GLB в Source 2 Viewer (ValveResourceFormat) и откройте здесь."}]}),Lc=vt({id:"iw",title:"IW Engine (Call of Duty), через экспорт Cast",i18n:{en:{title:"IW Engine (Call of Duty), via Cast export",presets:{cod:{title:"Call of Duty (CoD4 and later, Black Ops, MW, Warzone…) — Cast export",hint:'Export the model with Greyhound / Wraith / Mako to Cast (textures as PNG/DDS) and open the export with "Game folder…". Animations are .cast too: the "+ animation" button or a click in the list. SEModel/SEAnim: convert to Cast with SECast. CoD1/CoD2 (.iwd: xmodel/xmodelsurfs) are not supported yet.',motionLabel:"+ animation (.cast)"}}}},formats:[{id:"cast",title:"Cast",engine:"iw",extensions:["cast"],motionExtensions:["cast"],status:"bridge",sniff:t=>It(t,"cast")?1:0,load:async t=>{const e=await Promise.resolve().then(()=>F3);return e.convertCast(e.parseCast(await t.arrayBuffer()))}}],games:[{id:"cod",group:"Игры",title:"Call of Duty (CoD4 и новее, Black Ops, MW, Warzone…) — экспорт Cast",engine:"iw",formats:["cast"],status:"bridge",ui:{motionAny:!0,motionLabel:"+ анимация (.cast)",zUp:!0},hint:"Выгрузите модель утилитой Greyhound / Wraith / Mako в формате Cast (текстуры — PNG/DDS) и откройте «Папку игры…» с выгрузкой. Анимации — тоже .cast: кнопка «+ анимация» или клик в списке. SEModel/SEAnim: сконвертируйте в Cast утилитой SECast. CoD1/CoD2 (.iwd: xmodel/xmodelsurfs) пока не поддержаны."}]}),Rc={1052692:3,1052693:4,1052694:5},Oc=vt({id:"fce",title:"FCE (EA): Need for Speed III / High Stakes",i18n:{en:{title:"FCE (EA): Need for Speed III / High Stakes",presets:{nfs34:{title:"Need for Speed III: Hot Pursuit / NFS: High Stakes — cars (FCE)",hint:"Open car.viv (a BIGF container) — it holds car.fce and car00.tga. LOD0 is shown. NFS World has its own entry (@gmvjs/eagl4); NFS Underground/MW/Carbon, FIFA and Frostbite are not read yet.",archiveLabel:"Open car.viv…"}}}},formats:[{id:"fce",title:"NFS FCE3/FCE4",engine:"fce",extensions:["fce"],status:"beta",sniff:t=>Rc[At(t,0)]?1:0,load:async t=>(await Promise.resolve().then(()=>R3)).convertFce(await t.arrayBuffer())}],archives:[{id:"bigf",title:"EA BIGF (VIV/BIG)",engine:"fce",extensions:["viv","big"],sniff:t=>It(t,"BIGF")||It(t,"BIG4")?1:0,open:async t=>(await Promise.resolve().then(()=>I3)).openBigf(t)}],games:[{id:"nfs34",group:"Игры",title:"Need for Speed III: Hot Pursuit / NFS: High Stakes — автомобили (FCE)",engine:"fce",formats:["fce"],status:"beta",ui:{archives:!0,archiveExts:["viv","big"],archiveLabel:"Открыть car.viv…"},hint:"Откройте car.viv (контейнер BIGF) — внутри car.fce и car00.tga. Показывается LOD0. NFS World — отдельный пункт (@gmvjs/eagl4); NFS Underground/MW/Carbon, FIFA и Frostbite пока не читаются."}]}),b1={title:"EAGL 4 (EA, Black Box): Need for Speed World",presets:{nfsworld:{title:"Need for Speed: World (Black Box) — cars and the city",hint:"Open “Game folder” (the folder with nfsw.exe). Cars: CARS/<car>/GEOMETRY.bin — textures come from TEXTURES.BIN next to it, CARS/TEXTURES.BIN and GLOBAL; LOD A is shown: the body and the stock kit, wheels and brake discs are placed by the body wheel arches, other tuning parts are in the mesh list. The “Model look” panel picks the paint colour, a vinyl (CARS/VINYLS, on the sides or the hood) and wheels with rims from the CARS/WHEELS* libraries; the licence plate is the “world” plate background; LOD B–E are in the mesh list. City: TRACKS/L5RA.BUN — the whole city (about a minute, 7M triangle budget), TRACKS/STREAML5RA_<N>.BUN — a single section; shared objects and textures are taken from STREAML5RA_0x*.BUN files."}}},v1=/(^|\/)tracks\/(stream[a-z]\d[a-z]{2}_\d+|[a-z]\d[a-z]{2})\.bun$/i,x1=/^(stream[a-z]\d[a-z]{2}_\d+|[a-z]\d[a-z]{2})\.bun$/i,Ic=vt({id:"eagl4",title:"EAGL 4 (EA, Black Box): Need for Speed World",i18n:{en:b1},formats:[{id:"nfsw-car",title:"NFS World: машина (CARS/<машина>/GEOMETRY.bin)",engine:"eagl4",extensions:["bin"],status:"beta",pathPattern:/(^|\/)geometry\.bin$/,sniff:t=>At(t,0)===2148745216?1:0,load:async(t,e)=>(await Promise.resolve().then(()=>ow)).loadNfsCar(t,e)},{id:"nfsw-track",title:"NFS World: трасса (TRACKS/L5RA.BUN — город целиком, STREAML5RA_<N>.BUN — секция)",engine:"eagl4",extensions:["bun"],status:"beta",pathPattern:v1,sniff:(t,e)=>x1.test(e)?1:0,load:async(t,e)=>(await Promise.resolve().then(()=>dw)).loadNfsTrack(t,e)}],games:[{id:"nfsworld",group:"Игры",title:"Need for Speed: World (Black Box) — машины и город",engine:"eagl4",formats:["nfsw-car","nfsw-track"],extensions:[],status:"beta",markers:["nfsw.exe","CARS"],ui:{zUp:!0},hint:"Откройте «Папку игры» (папку с nfsw.exe). Машины: CARS/<машина>/GEOMETRY.bin — текстуры из TEXTURES.BIN рядом, CARS/TEXTURES.BIN и GLOBAL; показан LOD A: кузов и стоковый обвес, колёса и тормозные диски расставлены по аркам кузова, остальные детали тюнинга — в списке мешей. Панель «Вид модели»: цвет окраски, винил (CARS/VINYLS, на бока или капот) и колёса с дисками из библиотек CARS/WHEELS*; номер — фон «мирового» номера; LOD B–E — в списке мешей. Город: TRACKS/L5RA.BUN — весь город (около минуты, бюджет 7 млн треугольников), TRACKS/STREAML5RA_<N>.BUN — одна секция; общие объекты и текстуры берутся из файлов STREAML5RA_0x*.BUN."}]});function M1(t){const e=o=>{const r={};return o.replace(/([\w:.-]+)\s*=\s*"([^"]*)"/g,(a,i,l)=>(r[i.toLowerCase()]=l,"")),r},n=o=>{let r="";for(const a of o.matchAll(/<Texture\b[^>]*>/gi)){const i=e(a[0]);if(i.file){if(/^diffuse$/i.test(i.map??""))return i.file;r||(r=i.file)}}return r},s=/<SubMaterials>([\s\S]*?)<\/SubMaterials>/i.exec(t);if(s){const o=[],r=/<Material\b[^>]*?(?:\/>|>[\s\S]*?<\/Material>)/gi;let a;for(;a=r.exec(s[1]);)o.push(n(a[0]));return o}return[n(t)]}async function Dc(t,e,n){if(t.matId===void 0)return null;const s=[],o=e.meta?.mtlName,r=e.meta?.sourcePath;o&&s.push(gt(o).replace(/^\//,"")+".mtl"),r&&s.push(r.replace(/\.[^./]+$/,".mtl"));for(const a of s){const i=n.findEntry(a);if(!i)continue;const l=await n.memo("cry:mtl:"+a,async()=>M1(Wt(await i[0].read(i[1])))),c=l[t.matId]!==void 0?l[t.matId]:l[0];if(!c)continue;const f=n.find(c);if(f)return f}return null}const k1={title:"CryEngine 1",presets:{farcry:{title:"Far Cry (CryEngine 1) — .cgf, .cga, characters and animations",hint:"Open “Game folder” (FarCry) — the FCData/*.pak archives attach automatically. Models .cgf/.cga come from Objects.pak, textures from Textures*.pak; characters get their animations from the .cal next to the model (hundreds, read on selection). Levels are Levels/<level>/<level>.cry: terrain coloured from level.pak, water, objects, entities with models and vegetation.",archiveLabel:"Open .pak…"}}},E1=1417245251,Pc=vt({id:"cryengine1",title:"CryEngine 1",i18n:{en:k1},formats:[{id:"cry1-level",title:"Far Cry: уровень (.cry редактора + level.pak)",engine:"cryengine1",extensions:["cry"],status:"beta",sniff:t=>t[0]===80&&t[1]===75?1:0,load:async(t,e)=>(await Promise.resolve().then(()=>Mw)).loadFc1Level(t,e)},{id:"cry1-cgf",title:"CryEngine 1 CGF/CGA (CryTek — Far Cry)",engine:"cryengine1",extensions:["cgf","cga"],status:"beta",motionExtensions:["caf"],sniff:t=>At(t,0)===E1?1:0,async load(t){const{cry1Model:e,parseCry1:n,readCaf:s}=await Promise.resolve().then(()=>vh),o=await t.arrayBuffer(),r=e(o,t.name),a=r.meta?.cry1Bones;return r.meta={...r.meta,sourcePath:t.path??""},a&&(r.loadMotions=async i=>[s(n(await i.arrayBuffer()),a,i.name.replace(/\.caf$/i,""))]),r},async afterLoad(t){const e=t.main.archive,n=t.main.path??"",s=t.model;if(!e||!s.loadMotions||!s.meta?.cry1Bones)return;const o=e.lookup(n.replace(/(_lod\d+)?\.[^./]+$/i,".cal"))??e.lookup(n.replace(/\.[^./]+$/,".cal"));if(!o)return;const{parseCal:r}=await Promise.resolve().then(()=>vh),a=r(Wt(await e.read(o))),i=n.replace(/objects\/.*$/i,"")+"objects/characters/animations/",l=new Set,c=[];for(const{name:f,path:h}of a){const u=e.lookup(gt(i+h))??e.lookup(gt("objects/characters/animations/"+h));!u||l.has(f)||(l.add(f),c.push({kind:"node",name:f,tps:1,chan:new Map,duration:0,load:async()=>({...(await s.loadMotions(e.fileOf(u)))[0],name:f})}))}c.length&&t.addMotions(c,o.path.split("/").pop())},resolveTexture:(t,e,n)=>Dc(t,e,n)}],games:[{id:"farcry",group:"Игры",title:"Far Cry (CryEngine 1) — .cgf, .cga, персонажи и анимации",engine:"cryengine1",formats:["cry1-cgf","cry1-level"],status:"beta",markers:["FCData","Bin32/FarCry.exe"],ui:{archives:!0,archiveExts:["pak"],archiveLabel:"Открыть .pak…",zUp:!0,motionLabel:"+ .caf"},hint:"Откройте «Папку игры» (FarCry) — архивы FCData/*.pak подключатся сами. Модели .cgf/.cga из Objects.pak, текстуры из Textures*.pak; у персонажей анимации из .cal рядом с моделью (сотни, читаются при выборе). Уровни — Levels/<уровень>/<уровень>.cry: ландшафт с цветом из level.pak, вода, объекты, сущности с моделями и растительность."}]}),A1={title:"CryEngine 3/5",presets:{cryengine:{title:"CryEngine 3/5 (Crysis 2/3, Hunt, KCD…) — .cgf/.cga/.skin, static",hint:"Only the CrCh container with streamed geometry is read natively (LOD0, no skeleton or node transforms); .mtl materials → DDS textures. Checked on synthetic files only.",archiveLabel:"Open .pak (ZIP)…"},"cryengine-dae":{title:"CryEngine (Far Cry 1, Crysis, MWO, ArcheAge, Star Citizen…) — via Cryengine Converter (Collada)",hint:'Unpack the .pak (it is a ZIP), convert models: cgf-converter <file.cgf> -objectdir <data folder> (Collada with skeleton), then open the data folder with "Game folder…" (Textures/…dds are picked up).'}}},$1=1749250627,S1=1870031139,Nc=vt({id:"cryengine3",title:"CryEngine 3/5",i18n:{en:A1},formats:[{id:"cry3-cgf",title:"CryEngine 3+ CGF/CGA/SKIN (CrCh)",engine:"cryengine3",extensions:["cgf","cga","skin","cgam","skinm"],status:"beta",sniff:t=>{const e=At(t,0);return e===$1?1:e===S1?.7:0},async load(t){const e=(await Promise.resolve().then(()=>$w)).convertCgf(await t.arrayBuffer());return e.meta={...e.meta,sourcePath:t.path??""},e},resolveTexture:(t,e,n)=>Dc(t,e,n)}],games:[{id:"cryengine",group:"Игры",title:"CryEngine 3/5 (Crysis 2/3, Hunt, KCD…) — .cgf/.cga/.skin, статика",engine:"cryengine3",formats:["cry3-cgf"],status:"beta",ui:{archives:!0,archiveExts:["pak"],archiveLabel:"Открыть .pak (ZIP)…",zUp:!0},hint:"Нативно читается только контейнер CrCh с потоковой геометрией (LOD0, без скелета и трансформов узлов); материалы .mtl → текстуры DDS. Проверено лишь на синтетике."},{id:"cryengine-dae",group:"Игры",title:"CryEngine (Far Cry 1, Crysis, MWO, ArcheAge, Star Citizen…) — через Cryengine Converter (Collada)",engine:"cryengine3",formats:["assimp"],extensions:["dae"],ui:{zUp:!0},status:"bridge",hint:"Распакуйте .pak (это ZIP), сконвертируйте модели: cgf-converter <файл.cgf> -objectdir <папка данных> (Collada со скелетом), затем «Папка игры…» с папкой данных (Textures/…dds подхватятся)."}]}),zc=vt({id:"torque3d",title:"Torque3D (BeamNG)",i18n:{en:{presets:{beamng:{hint:`Open a mod (.zip) or the vehicles/<car> folder with "Game folder" and pick a .pc configuration. Parts missing from the mod (shared game parts) are found if the game's content/vehicles is open too.`,archiveLabel:"Open mod (.zip)…"}}}},formats:[{id:"torque3d-pc",title:"BeamNG: конфигурация машины (.pc)",engine:"torque3d",extensions:["pc"],status:"beta",sniff:t=>{const e=new TextDecoder().decode(t);return/^\s*\{/.test(e)&&/"(parts|mainPartName|format|model)"\s*:/.test(e)?1:0},load:async(t,e)=>(await Promise.resolve().then(()=>Ph)).loadVehicle(t,e)}],materials:[{id:"torque3d-materials",async resolve(t,e){if(!t.material)return null;const{collectMaterials:n}=await Promise.resolve().then(()=>Ph),o=(await e.memo("torque3d-materials",()=>n(e.archives))).get(t.material.toLowerCase());return o?.color?e.find(o.color):null}}],games:[{id:"beamng",group:"Игры",title:"BeamNG.drive",engine:"torque3d",formats:["torque3d-pc"],status:"beta",ui:{zUp:!0,archives:!0,archiveExts:["zip"],archiveLabel:"Открыть мод (.zip)…"},markers:["beamng.drive.exe"],hint:"Откройте мод (.zip) или папку vehicles/<машина> через «Папку игры» и выберите конфигурацию .pc. Детали, которых нет в моде (общие из игры), подтянутся, если открыт и content/vehicles игры."}]}),T1={title:"Real Virtuality 3 (Arma 2)",presets:{arma2:{hint:"PBO from the AddOns folder; binarized P3D.",archiveLabel:"Open .pbo…"}}},Bc=t=>It(t,"ODOL")&&t.length>=8?At(t,4):0,Vc=68,jc=vt({id:"real-virtuality3",title:"Real Virtuality 3 (Arma 2)",i18n:{en:T1},archives:[{id:"pbo",title:"PBO (Arma, DayZ)",engine:"real-virtuality3",extensions:["pbo"],sniff:t=>t[0]===0&&t[1]===115&&t[2]===114&&t[3]===101&&t[4]===86?1:t[0]?.3:0,open:async t=>(await Promise.resolve().then(()=>zh)).openPbo(t)}],codecs:[{id:"paa",extensions:["paa","pac"],decode:async t=>(await Promise.resolve().then(()=>jh)).decodePaa(t)}],formats:[{id:"rv3-p3d",title:"Arma 2 P3D (ODOL)",engine:"real-virtuality3",extensions:["p3d"],status:"beta",sniff:t=>{const e=Bc(t);return e?e<Vc?1:.5:It(t,"MLOD")?.6:0},load:async(t,e)=>(await Promise.resolve().then(()=>ar)).loadP3d(t,e)},{id:"rv3-class",title:"Arma 2: класс из config.bin (модель + камуфляж + снаряжение)",engine:"real-virtuality3",extensions:["armacfg"],status:"beta",load:async(t,e)=>(await Promise.resolve().then(()=>ar)).loadClass(t,e)}],games:[{id:"arma2",group:"Игры",title:"Arma 2 / Operation Arrowhead / DayZ Mod",engine:"real-virtuality3",formats:["rv3-p3d","rv3-class"],status:"beta",ui:{archives:!0,archiveExts:["pbo"],archiveLabel:"Открыть .pbo…"},markers:["arma2oa.exe","arma2.exe"],hint:"PBO из папки AddOns; бинаризованные P3D."}]}),Gc=vt({id:"real-virtuality4",title:"Real Virtuality 4 (Arma 3)",i18n:{en:{title:"Real Virtuality 4 (Arma 3)",presets:{arma3:{hint:'Open the mod folder (@Mod/addons) with "Game folder" or select .pbo files. .p3d models are in the archives; the "config" folder of each PBO lists classes (weapons with attachments, uniforms, vests, helmets, soldiers with gear) with textures from the config.',archiveLabel:"Open .pbo (all from addons is fine)…"}}}},archives:[{id:"rv4-pbo",title:"PBO (Arma 3)",engine:"real-virtuality4",extensions:["pbo"],sniff:t=>t[0]===0&&t[1]===115&&t[2]===114&&t[3]===101&&t[4]===86?1:t[0]?.3:0,open:async t=>(await Promise.resolve().then(()=>zh)).openPbo(t)}],codecs:[{id:"rv4-paa",extensions:["paa","pac"],decode:async t=>(await Promise.resolve().then(()=>jh)).decodePaa(t)}],formats:[{id:"rv4-p3d",title:"Arma 3 P3D (ODOL v68+)",engine:"real-virtuality4",extensions:["p3d"],status:"beta",sniff:t=>{const e=Bc(t);return e?e>=Vc?1:.5:It(t,"MLOD")?.6:0},load:async(t,e)=>(await Promise.resolve().then(()=>ar)).loadP3d(t,e)},{id:"rv4-class",title:"Arma 3: класс из config.bin (модель + камуфляж + снаряжение)",engine:"real-virtuality4",extensions:["armacfg"],status:"beta",load:async(t,e)=>(await Promise.resolve().then(()=>ar)).loadClass(t,e)}],games:[{id:"arma3",group:"Игры",title:"Arma 3",engine:"real-virtuality4",formats:["rv4-p3d","rv4-class"],status:"beta",ui:{archives:!0,archiveExts:["pbo"],archiveLabel:"Открыть .pbo (можно все из addons)…"},markers:["arma3_x64.exe","arma3.exe"],hint:"Откройте папку мода (@Mod/addons) через «Папку игры» или выберите .pbo. Модели .p3d — в архивах; в папке «config» каждого PBO — классы (оружие с обвесом, форма, бронежилеты, шлемы, солдаты с экипировкой) с текстурами из конфига."}]}),U1={presets:{gta5:{title:"GTA V (mods: ydr/ydd/yft/ytd)",hint:'Mod files (Add-On, stream/). Open the folder with "Game folder": .ytd textures are matched by name. A character is its .yft (assembled from *_000_* components). Encrypted game RPF archives are not supported: extract files with OpenIV or CodeWalker.'}}},F1=927159122;function Xc(t){const e=(t>>>27&1)+((t>>>26&1)<<1)+((t>>>25&1)<<2)+((t>>>24&1)<<3)+((t>>>17&127)<<4)+((t>>>11&63)<<5)+((t>>>7&15)<<6)+((t>>>5&3)<<7)+((t>>>4&1)<<8);return(512<<(t&15))*e}class _1{constructor(e,n,s){this.u8=e,this.version=n,this.sysSize=s,this.dv=new DataView(e.buffer,e.byteOffset,e.byteLength)}u8;version;sysSize;dv;ptr(e){const n=this.dv.getUint32(e,!0);if(!n)return 0;const s=n>>>28,o=n&268435455;if(s===5)return o;if(s===6)return this.sysSize+o;throw new Error(`RAGE: странный указатель 0x${n.toString(16)} по смещению 0x${e.toString(16)}`)}u8at(e){return this.u8[e]}u16(e){return this.dv.getUint16(e,!0)}u32(e){return this.dv.getUint32(e,!0)}f32(e){return this.dv.getFloat32(e,!0)}str(e){if(!e)return"";const n=this.u8.indexOf(0,e);let s="";for(let o=e;o<(n<0?this.u8.length:n);o++)s+=String.fromCharCode(this.u8[o]);return s}ptrList(e){const n=this.ptr(e),s=this.u16(e+8),o=[];for(let r=0;r<s&&n;r++)o.push(this.ptr(n+r*8));return o}}async function js(t){const e=new DataView(t);if(t.byteLength<16||e.getUint32(0,!0)!==F1)throw t.byteLength>=4&&e.getUint32(0,!0)===943936338?new Error("RSC8 (GTA V Enhanced, Gen9) пока не поддержан — нужен файл для обычной версии (RSC7)"):new Error("Не ресурс RAGE (нет RSC7) — возможно, файл из зашифрованного RPF; извлеките его через OpenIV/CodeWalker");const n=e.getUint32(4,!0),s=Xc(e.getUint32(8,!0));Xc(e.getUint32(12,!0));const o=await Qr(new Uint8Array(t,16));if(o.length<s)throw new Error(`RAGE: распаковано ${o.length} байт, ожидалось не меньше ${s}`);return new _1(o,n,s)}function Hc(t){let e=0;for(const n of t.toLowerCase())e=e+n.charCodeAt(0)|0,e=e+(e<<10)|0,e^=e>>>6;return e=e+(e<<3)|0,e^=e>>>11,e=e+(e<<15)|0,e>>>0}const C1=t=>At(t,0)===927159122?.8:At(t,0)===943936338?.5:0,L1=async({main:t,model:e,textures:n})=>{const s=t.archive;if(!s||!t.path)return;const{parseYtd:o}=await Promise.resolve().then(()=>io),r=h=>s.lookup(h)??s.entries.find(u=>u.path.endsWith("/"+h.split("/").pop())),a=new Set,i=async h=>{if(!a.has(h.path)){a.add(h.path);try{const{textures:u}=await o(await s.fileOf(h).arrayBuffer());for(const m of u)n.addDecoded(m.name,m,"ytd");const d=h.path.split("/").pop().replace(/\.ytd$/,""),p=u.find(m=>/_diff_/.test(m.name))??u[0];p&&!u.some(m=>m.name.toLowerCase()===d)&&n.addDecoded(d,p,"ytd")}catch{}}},l=t.path.replace(/[^/]*$/,""),c=new Set([t.path.replace(/\.[^.]+$/,".ytd")]);for(const h of e.meshes)h.texture&&!h.image&&c.add(`${l}${h.texture.toLowerCase()}.ytd`);for(const h of c){const u=r(h);u&&await i(u)}const f=/^([a-z]+)_(\d{3})_/.exec(t.name.toLowerCase());for(const h of e.meshes){if(!h.texture||h.image||n.find(h.texture))continue;const u=/^([a-z]+)_diff_(\d{3})_([a-z])_/.exec(h.texture.toLowerCase());if(!u)continue;const d=f&&f[1]===u[1]?f[2]:"000",p=new RegExp(`(^|/)${u[1]}_diff_${d}_a_[a-z]+\\.ytd$`),m=s.entries.find(g=>p.test(g.path));m&&(await i(m),h.texture=m.path.split("/").pop().replace(/\.ytd$/,""))}},ca=(t,e,n,s)=>({id:t,title:n,engine:"rage",extensions:[e],status:"beta",sniff:C1,load:s,afterLoad:L1}),Wc=vt({id:"rage",title:"RAGE (GTA V)",i18n:{en:U1},formats:[ca("rage-ydr","ydr","GTA V drawable (.ydr)",async t=>(await Promise.resolve().then(()=>io)).loadYdr(t)),ca("rage-ydd","ydd","GTA V словарь drawable (.ydd: одежда, части персонажа)",async t=>(await Promise.resolve().then(()=>io)).loadYdd(t)),ca("rage-yft","yft","GTA V фрагмент (.yft: машины, персонажи)",async(t,e)=>(await Promise.resolve().then(()=>io)).loadYft(t,e))],dictionaries:[{id:"ytd",extensions:["ytd"],parse:async t=>(await Promise.resolve().then(()=>io)).parseYtd(t)}],games:[{id:"gta5",group:"Игры",title:"GTA V (моды: ydr/ydd/yft/ytd)",engine:"rage",formats:["rage-ydr","rage-ydd","rage-yft"],status:"beta",ui:{zUp:!0},markers:["gta5.exe","playgtav.exe"],hint:"Файлы мода (Add-On, stream/). Откройте папку через «Папку игры»: текстуры .ytd подтянутся по именам. Персонаж — его .yft (соберётся из компонентов *_000_*). Зашифрованные RPF игры не поддерживаются: извлеките файлы через OpenIV или CodeWalker."}]}),R1=[4,8,12,16,4,4,4,8,4,4,8,4,8,4,4,4,8],O1=t=>{if(t.length<18)return!1;const e="<CombineXMesh>";for(let n=0;n<e.length;n++)if(t[4+n]!==e.charCodeAt(n))return!1;return!0};function Kc(t){const e=[];for(const n of t.matchAll(/<Material\b([^>]*)\/?>/g)){const s=o=>new RegExp(`\\b${o}="([^"]*)"`).exec(n[1])?.[1]??"";e.push({parts:s("MeshParts").split(",").map(o=>o.trim().toLowerCase()).filter(Boolean),diffuse:s("DiffuseMap")||s("AlbedoMap"),normal:s("NormalMap"),shading:s("ShadingMap"),specular:s("SpecularMap"),name:s("Name"),alpha:/alpha/i.test(s("Blending"))||s("AlphaKill")==="true"})}return e}function I1(t,e){const n=new Float32Array(16);for(let s=0;s<4;s++)for(let o=0;o<4;o++){let r=0;for(let a=0;a<4;a++)r+=t[s*4+a]*e[a*4+o];n[s*4+o]=r}return n}function Yc(t){const e=new Uint8Array(t),n=new DataView(t),s=n.getUint32(0,!0),o=new TextDecoder("latin1").decode(e.subarray(4,4+s)).replace(/\0+$/,"");let r=4+s;const a=n.getUint32(r,!0);if(a!==20151108&&a!==20180835&&a!==20160902)throw new Error(`MeshCache: неизвестная сигнатура 0x${a.toString(16)} после XML`);const i=n.getUint32(r+4,!0);r+=36;const l=[];let c=0;const f=(m,g)=>{let w="";for(let y=0;y<g-1;y++)w+=String.fromCharCode(e[m+y]);return w},h=new Map,u=m=>{if(m+12+64>e.length)return!1;const g=n.getInt16(m,!0),w=n.getUint16(m+2,!0),y=n.getUint32(m+8,!0);if(w>=i||h.has(w)||(g===-2?h.size>0:!h.has(g))||y<1||y>128||m+12+y+64>e.length||e[m+12+y-1]!==0)return!1;for(let v=0;v<y-1;v++){const b=e[m+12+v];if(b<32||b>126)return!1}const M=m+12+y,x=v=>n.getFloat32(M+v*4,!0);return Math.abs(x(15)-1)<1e-4&&Math.abs(x(3))<1e-4&&Math.abs(x(7))<1e-4&&Math.abs(x(11))<1e-4},d=Kc(o).map(m=>m.name).filter(m=>m.length>1),p=(m,g)=>{const w=n.getUint32(m,!0),y=n.getUint32(m+4,!0),M=n.getUint32(m+8,!0);if(!w||w>4e6||y>8e6||M>256)throw new Error("bad mesh");const x=m+12+M;let v=-1;for(let I=x,B=Math.min(e.length-16,x+(1<<20));I<B;I++){const z=n.getUint32(I,!0);if(z<1||z>16||n.getUint32(I+4,!0)!==0)continue;const j=I+4+z*8;if(j+8<=e.length&&e[j]===255&&e[j+1]===0&&e[j+4]===17){v=I;break}}if(v<0)throw new Error("no decl");let b="";const k=new TextDecoder("latin1").decode(e.subarray(x,v));for(const I of d)if(k.includes(I+"\0")){b=I;break}const E=n.getUint32(v,!0);v+=4;let A=0,S=-1,$=2,T=-1,U=1,_=-1,R=4,L=!1;for(let I=0;I<E;I++){const B=v+I*8,z=n.getUint16(B+2,!0),j=e[B+4],X=e[B+6],J=e[B+7];A=Math.max(A,z+(R1[j]??4)),X===0&&S<0?(S=z,$=j):X===5&&J===0?(T=z,U=j):(X===3||X===5&&J===1)&&_<0?(_=z,R=j):(X===1||X===2)&&(L=!0)}if(v+=8*(E+1),v+A*w>e.length)throw new Error("vertices out of range");const O=new Float32Array(w*3),N=new Float32Array(w*3),D=new Float32Array(w*2);for(let I=0;I<w;I++){const B=v+I*A;if($===16)for(let z=0;z<3;z++)O[I*3+z]=Fe(n.getUint16(B+S+z*2,!0));else for(let z=0;z<3;z++)O[I*3+z]=n.getFloat32(B+S+z*4,!0);if(T>=0&&(U===1?(D[I*2]=n.getFloat32(B+T,!0),D[I*2+1]=n.getFloat32(B+T+4,!0)):U===15&&(D[I*2]=Fe(n.getUint16(B+T,!0)),D[I*2+1]=Fe(n.getUint16(B+T+2,!0)))),_>=0){const z=B+_;R===4?(N[I*3]=e[z+2]/127.5-1,N[I*3+1]=e[z+1]/127.5-1,N[I*3+2]=e[z]/127.5-1):R===8||R===5?(N[I*3]=e[z]/127.5-1,N[I*3+1]=e[z+1]/127.5-1,N[I*3+2]=e[z+2]/127.5-1):R===2&&(N[I*3]=n.getFloat32(z,!0),N[I*3+1]=n.getFloat32(z+4,!0),N[I*3+2]=n.getFloat32(z+8,!0))}}v+=A*w;const F=w>65535,C=new Uint32Array(y*3);if(v+(F?4:2)*y*3>e.length)throw new Error("indices out of range");for(let I=0;I<y*3;I++)C[I]=F?n.getUint32(v+I*4,!0):n.getUint16(v+I*2,!0);return v+=(F?4:2)*y*3,a===20151108&&(v+=4),{mesh:{name:g,texture:"",vertexCount:w,pos:O,nor:N,uv:D,bone:new Float32Array(w*4),weight:new Float32Array(w*4),indices:C,skinned:!1},skinned:L,end:v,material:b}};for(let m=0;m<i;m++){if(!u(r)){let E=r;for(;E<e.length-80&&!u(E);)E++;if(E>=e.length-80)break;c+=E-r,r=E}const g=n.getInt16(r,!0),w=n.getUint16(r+2,!0),y=n.getUint32(r+8,!0),M=f(r+12,y);r+=12+y;const x=new Float32Array(e.slice(r,r+64).buffer);r+=64;const v=g>=0?h.get(g):void 0,b={index:w,parent:g,name:M,local:x,world:v?I1(x,v.world):x,mesh:null,skinned:!1};if(l.push(b),h.set(w,b),/^cdt|_cdt$/i.test(M)&&!/^bone/i.test(M))continue;if(!n.getUint32(r,!0)){r+=4;continue}try{const E=p(r,M);b.mesh=E.mesh,b.skinned=E.skinned,b.material=E.material,r=E.end}catch{}}return{xml:o,materials:Kc(o),nodes:l,skipped:c,snow:a===20180835}}function Zc(t,e){for(let n=0;n<t.vertexCount;n++){const s=t.pos[n*3],o=t.pos[n*3+1],r=t.pos[n*3+2];t.pos[n*3]=s*e[0]+o*e[4]+r*e[8]+e[12],t.pos[n*3+1]=s*e[1]+o*e[5]+r*e[9]+e[13],t.pos[n*3+2]=s*e[2]+o*e[6]+r*e[10]+e[14];const a=t.nor[n*3],i=t.nor[n*3+1],l=t.nor[n*3+2];t.nor[n*3]=a*e[0]+i*e[4]+l*e[8],t.nor[n*3+1]=a*e[1]+i*e[5]+l*e[9],t.nor[n*3+2]=a*e[2]+i*e[6]+l*e[10]}}const qc=vt({id:"spintires",title:"Spintires / MudRunner",i18n:{en:{presets:{snowrunner:{hint:"Open the mod .pak (zip): meshes are prebuild/meshes/*, .pct textures are in pc.pak (open both). Whole trucks from classes/trucks/*.xml.",archiveLabel:"Open mod .pak (+ pc.pak with textures)…"},mudrunner:{hint:'Open the mod folder (with Media/ or _m/, _t/) with "Game folder": meshes are extension-less files in MeshCache or _m, textures are in TextureCache, _t, colors/default.'}}}},codecs:[{id:"pct",extensions:["pct"],decode:async t=>(await Promise.resolve().then(()=>my)).decodePct(t)}],formats:[{id:"spintires-meshcache",title:"MudRunner / Spintires MeshCache (CombineXMesh)",engine:"spintires",extensions:["xmesh"],status:"beta",pathPattern:/(^|\/)(meshcache|_m|prebuild\/meshes)\/[^/.]+$/,sniff:t=>O1(t)?1:0,load:async(t,e)=>(await Promise.resolve().then(()=>dr)).loadMeshCache(t,e),resolveTexture:async(t,e,n)=>(await Promise.resolve().then(()=>dr)).resolveSpintiresTexture(t,n)},{id:"spintires-truck",title:"MudRunner: машина из classes/trucks/*.xml (кузов + колёса + навесное)",engine:"spintires",extensions:[],status:"beta",pathPattern:/(^|\/)classes\/trucks\/[^/]+\.xml$/,sniff:t=>{const e=new TextDecoder("latin1").decode(t);return/<(_templates|Truck|TruckAddon)\b/.test(e)?.9:0},load:async(t,e)=>(await Promise.resolve().then(()=>dr)).loadTruck(t,e),resolveTexture:async(t,e,n)=>(await Promise.resolve().then(()=>dr)).resolveSpintiresTexture(t,n)}],games:[{id:"snowrunner",group:"Игры",title:"SnowRunner / Expeditions",engine:"spintires",formats:["spintires-meshcache","spintires-truck"],status:"beta",ui:{archives:!0,archiveExts:["pak","zip"],archiveLabel:"Открыть .pak мода (+ pc.pak с текстурами)…"},markers:["snowrunner.exe"],hint:"Откройте .pak мода (zip): меши — prebuild/meshes/*, текстуры .pct — в pc.pak (откройте оба). Полные машины по classes/trucks/*.xml."},{id:"mudrunner",group:"Игры",title:"MudRunner / Spintires",engine:"spintires",formats:["spintires-meshcache","spintires-truck"],status:"beta",markers:["mudrunner.exe","spintires.exe"],hint:"Откройте папку мода (с Media/ или _m/, _t/) через «Папку игры»: меши — файлы без расширения в MeshCache или _m, текстуры — TextureCache, _t, colors/default."}]}),Qc=vt({id:"4a",title:"4A Engine",i18n:{en:{presets:{metro2033:{hint:"Select content.vfi together with content.vfs0 and textures.vfs0/1 (or the game folder) and open maps/<level>/level (level geometry and objects) or meshes/…/*.model.",archiveLabel:"Open content.vfi + .vfs…"}}}},formats:[{id:"4a-level",title:"4A: уровень (maps/<уровень>/level + level.geom_pc)",engine:"4a",extensions:[],pathPattern:/(^|\/)maps\/[^/]+\/level$/,status:"beta",sniff:t=>t[0]===1&&t[4]===4&&t[8]===16&&t[9]===0&&t[10]===0?1:0,load:async(t,e)=>(await Promise.resolve().then(()=>lo)).loadMetroLevel(t,e),resolveTexture:async(t,e,n)=>(await Promise.resolve().then(()=>lo)).resolveMetroTexture(t.texture,n),resolveMaps:async(t,e,n)=>(await Promise.resolve().then(()=>lo)).resolveMetroMaps(t.texture,n)},{id:"4a-model",title:"4A: модель (.model, .mesh + .skeleton)",engine:"4a",extensions:["model","mesh"],motionExtensions:["motion"],status:"beta",sniff:t=>t[0]===1&&t[4]===64&&t[1]===0&&t[5]===0||t[0]===9&&t[1]===0&&t[2]===0&&t[3]===0?1:0,load:async(t,e)=>(await Promise.resolve().then(()=>Ed)).loadMetroModel(t,e),afterLoad:async t=>{const e=t.main.archive,n=t.main.path??"";if(!e||!t.model.loadMotions)return;const{listMetroMotions:s,meshesRoot:o}=await Promise.resolve().then(()=>Ed),r=o(n).replace(/meshes\/$/,""),a=s(t.model,e,r);a.length&&t.addMotions(a,"motions")},resolveTexture:async(t,e,n)=>(await Promise.resolve().then(()=>lo)).resolveMetroTexture(t.texture,n),resolveMaps:async(t,e,n)=>(await Promise.resolve().then(()=>lo)).resolveMetroMaps(t.texture,n)}],archives:[{id:"4a-vfi",title:"4A: индекс .vfi + данные .vfs (Metro 2033)",engine:"4a",extensions:["vfi"],companionExtensions:["vfs0","vfs1","vfs2"],sniff:t=>t[0]===255&&t[1]===255&&t[2]===255&&t[3]===255&&t[4]===20?1:0,open:async(t,e)=>(await Promise.resolve().then(()=>_y)).openVfi(t,e)}],games:[{id:"metro2033",group:"Игры",title:"Metro 2033 (2010)",engine:"4a",formats:["4a-level","4a-model"],hint:"Выберите content.vfi вместе с content.vfs0 и textures.vfs0/1 (или папку игры) и откройте maps/<уровень>/level (геометрия и объекты уровня) или meshes/…/*.model.",ui:{archives:!0,archiveExts:["vfi","vfs0","vfs1"],archiveLabel:"Открыть content.vfi + .vfs…"},status:"beta",markers:["metro2033.exe","content.vfi"]}]}),Jc=vt({id:"lithtech",title:"LithTech Jupiter EX",i18n:{en:{presets:{"fear-online":{hint:"Open the game folder (the *.arch01 archives and the Game folder with Game/database/Loki.gamedb are picked up automatically) and choose a .mdl model: weapons weaponsf/…, characters charf/… and char/…, props models/…. Levels: Game/worlds/…/<map>.wld (geometry and prefabs). Animations are found automatically: the model’s own, sets in its folder and its anim/ subfolders (players: Game/charf/…/anim), shared AI sets char/ai/global/…; they are read when picked; add any other set with «+ .mdl».",archiveLabel:"Open *.arch01 archives…"}}}},formats:[{id:"lithtech-mdl",title:"LithTech: модель (.mdl + .mesh, F.E.A.R. Online)",engine:"lithtech",extensions:["mdl","mesh"],motionExtensions:["mdl"],status:"beta",sniff:t=>t[0]===77&&t[1]===79&&t[2]===68&&t[3]===76?t[4]===42?1:.8:t[0]===77&&t[1]===69&&t[2]===83&&t[3]===72&&t[4]===1&&t[5]===0?.9:0,load:async(t,e)=>(await Promise.resolve().then(()=>tb)).loadLithtechModel(t,e)},{id:"lithtech-world",title:"LithTech: уровень (.wld + .wldclnt + .wldmsh, F.E.A.R. Online)",engine:"lithtech",extensions:["wld"],status:"beta",sniff:t=>t[0]===87&&t[1]===76&&t[2]===68&&(t[3]===80||t[3]===67)?1:0,load:async(t,e)=>(await Promise.resolve().then(()=>sb)).loadLithtechWorld(t,e)}],archives:[{id:"lithtech-ltar",title:"LithTech: архив LTAR (.arch00, .arch01)",engine:"lithtech",extensions:["arch01","arch00"],openFromFolder:!0,sniff:t=>t[0]===76&&t[1]===84&&t[2]===65&&t[3]===82&&t[4]===3?1:0,open:async t=>(await Promise.resolve().then(()=>ab)).openLtar(t)}],codecs:[{id:"lithtech-tex",extensions:["tex"],decode:async t=>(await Promise.resolve().then(()=>Oy)).decodeTex(t)}],games:[{id:"fear-online",group:"Игры",title:"F.E.A.R. Online (2014)",engine:"lithtech",formats:["lithtech-mdl","lithtech-world"],hint:"Откройте папку игры (архивы *.arch01 и папка Game с базой Game/database/Loki.gamedb подключатся сами) и выберите модель .mdl: оружие weaponsf/…, персонажи charf/… и char/…, предметы models/…. Уровни: Game/worlds/…/<карта>.wld (геометрия и префабы). Анимации подключаются сами: свои, наборы из папки модели и её anim/ (у игроков — Game/charf/…/anim), общие наборы ИИ char/ai/global/…; читаются при выборе; другой набор — кнопкой «+ .mdl».",ui:{archives:!0,archiveExts:["arch01","arch00"],archiveLabel:"Открыть архивы *.arch01…"},status:"beta",markers:["engine.exe","models_00.arch01"]}]}),tf=vt({id:"vostok",title:"Vostok Engine (Survarium)",i18n:{en:{presets:{survarium:{hint:"Open the game folder (or select all resources.*.db files — updates are applied on top) and choose a model folder models/…/*.model or *.skinned_model, a character outfit models/character/…/*.outfit (parts + head) or a level projects/<level>/<mode>.vlevel.",archiveLabel:"Open resources.*.db…"}}}},formats:[{id:"vostok-model",title:"Survarium: модель (папка *.model / *.skinned_model), комплект персонажа (*.outfit), уровень (*.vlevel)",engine:"vostok",extensions:["model","skinned_model","outfit","vlevel"],status:"beta",sniff:t=>String.fromCharCode(...t.subarray(0,7))==="VOSTOK-"?1:0,load:async(t,e)=>(await Promise.resolve().then(()=>np)).loadVostokModel(t,e),afterLoad:async t=>{const e=t.main.archive;if(!e||!t.model.loadMotions)return;const n=(await Promise.resolve().then(()=>np)).listVostokMotions(t.model,e);n.length&&t.addMotions(n,"animations")}}],archives:[{id:"vostok-db",title:"Survarium: архивы resources.NN.db",engine:"vostok",extensions:["db"],openFromFolder:!0,sniff:t=>String.fromCharCode(...t.subarray(0,13))==="little-endian"?1:0,open:async(t,e)=>(await Promise.resolve().then(()=>ub)).openVostokDb(t,e)}],games:[{id:"survarium",group:"Игры",title:"Survarium (2015)",engine:"vostok",formats:["vostok-model"],hint:"Откройте папку game (или выберите все resources.*.db — обновления подключатся поверх) и выберите папку модели models/…/*.model или *.skinned_model, комплект персонажа models/character/…/*.outfit (части + голова) или уровень projects/<уровень>/<режим>.vlevel.",ui:{archives:!0,archiveExts:["db"],archiveLabel:"Открыть resources.*.db…"},status:"beta",markers:["resources.00.db"]}]}),ef=vt({id:"dunia1",title:"Dunia Engine 1 (Far Cry 2)",i18n:{en:{presets:{farcry2:{hint:"Open the game folder (or Data_Win32): the .fat/.dat archives connect by themselves and file names are taken from the game's own resource lists. Models are graphics/…/*.xbg; levels are worlds/<world>/maps/*.fc2level (a 1×1 km map: terrain and objects) and worlds/<world>/<world>.fc2level (the whole world, terrain only).",archiveLabel:"Open *.fat + *.dat…"}}}},codecs:[{id:"xbt",extensions:["xbt"],decode:async t=>(await Promise.resolve().then(()=>kb)).decodeXbt(t)}],formats:[{id:"dunia-xbg",title:"Far Cry 2: модель .xbg",engine:"dunia1",extensions:["xbg"],motionExtensions:["mab"],status:"beta",sniff:t=>String.fromCharCode(...t.subarray(0,4))==="HSEM"?1:0,load:async(t,e)=>(await Promise.resolve().then(()=>fp)).loadXbg(t,e),afterLoad:async t=>{const e=t.main.archive;if(!e||!t.model.loadMotions)return;const n=(await Promise.resolve().then(()=>fp)).listMabMotions(t.model,e);n.length&&t.addMotions(n,"animations")}},{id:"dunia-level",title:"Far Cry 2: уровень (worlds/<мир>/maps/*.fc2level — карта 1×1 км с объектами; <мир>.fc2level — весь мир)",engine:"dunia1",extensions:["fc2level"],status:"beta",sniff:t=>String.fromCharCode(...t.subarray(0,12))===`DUNIA-LEVEL
|
|
4
|
+
`?1:0,load:async(t,e)=>{const n=new TextDecoder().decode(await t.arrayBuffer());return(await Promise.resolve().then(()=>nv)).loadDuniaLevel(n,t,e)}}],archives:[{id:"dunia-fat",title:"Far Cry 2: архивы .fat + .dat",engine:"dunia1",extensions:["fat"],companionExtensions:["dat"],openFromFolder:!0,sniff:t=>String.fromCharCode(...t.subarray(0,4))==="2TAF"?1:0,open:async(t,e)=>(await Promise.resolve().then(()=>Pb)).openDuniaArchive(t,e)}],games:[{id:"farcry2",group:"Игры",title:"Far Cry 2 (2008)",engine:"dunia1",formats:["dunia-xbg","dunia-level"],hint:"Откройте папку игры (или Data_Win32): архивы .fat/.dat подключатся сами, имена файлов берутся из списков ресурсов игры. Модели — graphics/…/*.xbg, уровни — worlds/<мир>/maps/*.fc2level (карта 1×1 км: ландшафт и объекты) и worlds/<мир>/<мир>.fc2level (весь мир — ландшафт).",ui:{archives:!0,archiveExts:["fat","dat"],archiveLabel:"Открыть *.fat + *.dat…"},status:"beta",markers:["common.fat","worlds.fat"]}]}),nf=vt({id:"enfusion",title:"Enfusion (Arma Reforger)",i18n:{en:{presets:{reforger:{title:"Arma Reforger (Enfusion)",hint:"Open “Game folder” (Arma Reforger or its addons): the .pak archives connect by themselves. Assembled objects are the prefabs Prefabs/…/*.et: vehicles with doors, wheels and roof, characters with head, uniform and gear, weapons with attachments. Single models are Assets/…/*.xob (vehicles, weapons, characters, buildings, vegetation); the .emat material and .edds textures are found from the model’s references. The most detailed LOD is shown. Worlds are worlds/<world>/<world>.ent (Arland, Everon = Eden, Cain…): terrain with its colour map and the objects (buildings, vegetation, rocks) at simpler LODs; this needs the whole game folder (resourceDatabase.rdb in addons).",archiveLabel:"Open .pak (addons/*/data*.pak)…"}}}},archives:[{id:"enfusion-pak",title:"Enfusion PAK (Arma Reforger)",engine:"enfusion",extensions:["pak"],openFromFolder:t=>It(t,"FORM")&&t[8]===80&&t[9]===65&&t[10]===67&&t[11]===49,sniff:t=>It(t,"FORM")&&String.fromCharCode(t[8],t[9],t[10],t[11])==="PAC1"?1:0,open:async t=>(await Promise.resolve().then(()=>rv)).openEnfusionPak(t)}],codecs:[{id:"edds",extensions:["edds"],decode:async t=>(await Promise.resolve().then(()=>av)).decodeEdds(t)}],formats:[{id:"enfusion-xob",title:"Enfusion XOB (Arma Reforger)",engine:"enfusion",extensions:["xob"],status:"beta",sniff:t=>It(t,"FORM")&&String.fromCharCode(t[8],t[9],t[10],t[11])==="XOB9"?1:0,load:async(t,e)=>(await Promise.resolve().then(()=>i0)).loadXob(t,e)},{id:"enfusion-world",title:"Enfusion: мир .ent (ландшафт и объекты)",engine:"enfusion",extensions:["ent"],status:"beta",sniff:t=>It(t,"EBIN")?1:0,load:async(t,e)=>(await Promise.resolve().then(()=>Vv)).loadEnfusionWorld(t,e)},{id:"enfusion-et",title:"Enfusion: префаб .et (техника, персонажи, оружие в сборе)",engine:"enfusion",extensions:["et"],status:"beta",sniff:t=>/^\s*[A-Za-z_]\w*\s*(:\s*"\{[0-9A-F]{16}\}[^"]*")?\s*\{/.test(new TextDecoder().decode(t.subarray(0,200)))?.8:0,load:async(t,e)=>(await Promise.resolve().then(()=>Sv)).loadPrefabModel(t,e)}],games:[{id:"reforger",group:"Игры",title:"Arma Reforger (Enfusion)",engine:"enfusion",formats:["enfusion-et","enfusion-xob","enfusion-world"],status:"beta",ui:{archives:!0,archiveExts:["pak"],archiveLabel:"Открыть .pak (addons/*/data*.pak)…"},markers:["ArmaReforgerSteam.exe","addons"],hint:"Откройте «Папку игры» (Arma Reforger или её addons): архивы .pak подключатся сами. Собранные объекты — префабы Prefabs/…/*.et: техника с дверями, колёсами и крышей, персонажи с головой, формой и снаряжением, оружие с обвесом. Отдельные модели — Assets/…/*.xob (техника, оружие, персонажи, здания, растительность); материал .emat и текстуры .edds находятся по ссылкам из модели. Показан старший LOD. Миры — worlds/<мир>/<мир>.ent (Arland, Everon = Eden, Cain…): ландшафт с цветом и объекты (здания, растительность, камни) по LOD попроще; нужна «Папка игры» целиком (resourceDatabase.rdb в addons)."}]}),D1={title:"assimp (bridge)",groups:{"Форматы (assimp)":"Formats (assimp)"},presets:{"assimp-auto":{title:"Auto (any assimp format)",hint:"For .obj/.gltf, select the .mtl/.bin together with the model."},obj:{hint:"Select the .obj and .mtl together (Ctrl/Shift)."},other:{title:"Other (LWO, BVH, DXF, 3MF, MS3D, ASE…)"}}},P1=25;function N1(t,e){for(const n of t.materials?.[e]?.properties??[])if(n.key==="?mat.name"&&typeof n.value=="string")return n.value;return""}function z1(t,e){for(const n of t.materials?.[e]?.properties??[])if(n.key==="$tex.file"&&(n.semantic===1||n.semantic===12)&&typeof n.value=="string")return n.value;return""}function B1(t,e){const n=t.materials?.[e]?.properties??[],s=f=>n.find(h=>h.key==="$tex.file"&&h.semantic===f&&typeof h.value=="string")?.value,o=f=>{const h=n.find(u=>u.key===f)?.value;return typeof h=="number"?h:Array.isArray(h)&&typeof h[0]=="number"?h[0]:void 0},r=s(18),a=s(15),i=s(16),l=s(17),c={metallic:a?{map:a,channel:a===i?2:0}:r?{map:r,channel:2}:void 0,roughness:i?{map:i,channel:a===i?1:0}:r?{map:r,channel:1}:void 0,ao:l?{map:l,channel:0}:void 0,metallicFactor:o("$mat.metallicFactor"),roughnessFactor:o("$mat.roughnessFactor"),source:"assimp"};return c.metallic||c.roughness||c.ao||c.metallicFactor!==void 0||c.roughnessFactor!==void 0?c:void 0}function sf(t){const e=[],n=[];(function l(c,f){const h=e.length;e.push({name:c.name,parent:f,local:c.transformation?Co(c.transformation):nn(),offset:null});for(const u of c.meshes??[])n.push({m:u,node:h});for(const u of c.children??[])l(u,h)})(t.rootnode,-1);const s=new Map;e.forEach((l,c)=>{s.has(l.name)||s.set(l.name,c)});for(const l of t.meshes??[])for(const c of l.bones??[]){const f=s.get(c.name);f!==void 0&&!e[f].offset&&c.offsetmatrix&&(e[f].offset=Co(c.offsetmatrix))}const o=[];for(const l of n){const c=t.meshes?.[l.m];if(!c)continue;const f=c.vertices??[],h=f.length/3;if(!h)continue;const u=[];for(const b of c.faces??[])for(let k=1;k+1<b.length;k++)u.push(b[0],b[k],b[k+1]);if(!u.length)continue;const d=Float32Array.from(f),p=new Float32Array(h*2);let m;if(c.normals&&c.normals.length===h*3){m=Float32Array.from(c.normals);for(let b=0;b<h;b++){const k=Math.hypot(m[b*3],m[b*3+1],m[b*3+2])||1;m[b*3]/=k,m[b*3+1]/=k,m[b*3+2]/=k}}else m=ge(d,u);const g=c.texturecoords?.[0],w=c.numuvcomponents?.[0]||2;if(g)for(let b=0;b<h;b++)p[b*2]=g[b*w],p[b*2+1]=1-g[b*w+1];const y=new Float32Array(h*4),M=new Float32Array(h*4);let x=-1;const v=()=>(x<0&&(x=e.length,e.push({name:"#rigid",parent:l.node,local:nn(),offset:null})),x);if(c.bones&&c.bones.length){const b=Array.from({length:h},()=>[]);for(const k of c.bones){const E=s.get(k.name);if(E!==void 0)for(const[A,S]of k.weights??[])A<h&&S>0&&b[A].push([E,S])}for(let k=0;k<h;k++){const E=b[k].sort((S,$)=>$[1]-S[1]).slice(0,4),A=E.reduce((S,$)=>S+$[1],0);if(!E.length){y[k*4]=v(),M[k*4]=1;continue}E.forEach(([S,$],T)=>{y[k*4+T]=S,M[k*4+T]=$/A})}}else{const b=v();for(let k=0;k<h;k++)y[k*4]=b,M[k*4]=1}o.push({name:c.name||`mesh ${l.m}`,texture:z1(t,c.materialindex),material:N1(t,c.materialindex),surface:B1(t,c.materialindex),shader:"",vertexCount:h,pos:d,nor:m,uv:p,bone:y,weight:M,indices:$e(h,u),skinned:!0})}if(!o.length)throw new Error("В сцене нет геометрии");const r=new Kt(e),a=(t.animations??[]).map((l,c)=>{const f=l.tickspersecond&&l.tickspersecond>0?l.tickspersecond:P1,h=new Map;let u=0;for(const d of l.channels??[]){const p=s.get(d.name??d.nodename);if(p===void 0)continue;const m={pos:d.positionkeys?.length?d.positionkeys:null,rot:d.rotationkeys?.length?d.rotationkeys:null,scl:d.scalingkeys?.length?d.scalingkeys:null};for(const g of[m.pos,m.rot,m.scl])g&&(u=Math.max(u,g[g.length-1][0]));h.set(p,m)}return{kind:"node",name:l.name||`anim ${c}`,tps:f,chan:h,duration:(l.duration&&l.duration>0?l.duration:u)/f||1/f}}),i=Jt(r,o);return{meshes:o,skeleton:r,motions:a,bounds:i,flipZ:!1,info:[`Мешей: ${o.length}, вершин: ${le(o)}, треугольников: ${Ut(o)}`,`Узлов/костей: ${r.n}`,`Анимаций: ${a.length}`]}}let us={},Nn=null;function fa(t){us={...us,...t},Nn=null}const Bo="assimpjs не найден. Скопируйте assimpjs-all.js и assimpjs-all.wasm (или assimpjs.js/.wasm) из npm-пакета assimpjs/dist в папку lib/ и перезагрузите страницу.";function V1(t){return new Promise((e,n)=>{const s=document.createElement("script");s.src=t,s.onload=()=>e(),s.onerror=()=>{s.remove(),n(new Error("нет "+t))},document.head.append(s)})}function of(){return Nn||(Nn=(async()=>{if(us.factory)return us.factory();const t=globalThis,e=us.baseUrl??(typeof document<"u"?new URL("lib/",document.baseURI).href:"");if(!t.assimpjs){if(typeof document>"u")throw new Error(Bo);let n=!1;for(const s of us.scripts??["assimpjs-all.js","assimpjs.js"])try{await V1(new URL(s,e).href),n=!0;break}catch{}if(!n||!t.assimpjs)throw new Error(Bo)}return t.assimpjs({locateFile:n=>new URL(n,e).href})})(),Nn.catch(()=>{Nn=null}),Nn)}async function rf(t,e){const n=await of(),s=new n.FileList;for(const r of[e,...t.filter(a=>a!==e)])s.AddFile(r.name,new Uint8Array(await r.arrayBuffer()));const o=n.ConvertFileList(s,"assjson");if(!o.IsSuccess()||o.FileCount()===0)throw new Error("assimp не смог прочитать файл ("+o.GetErrorCode()+")");return JSON.parse(Wt(o.GetFile(0).GetContent()))}async function af(t,e){return sf(await rf(t,e))}const j1=Object.freeze(Object.defineProperty({__proto__:null,ASSIMP_MISSING:Bo,configureAssimp:fa,getAssimp:of,loadWithAssimp:af,readScene:rf},Symbol.toStringTag,{value:"Module"})),ua=["gltf","glb","fbx","obj","dae","3ds","stl","ply","blend","x","md5mesh","md2","md3","bvh","lwo","off","dxf","ms3d","smd","3mf","ase","ac","ifc","xgl","cob","b3d","ogex"],G1={zUp:!0},Se=(t,e,n,s)=>({id:t,group:"Форматы (assimp)",title:e,engine:"assimp",formats:["assimp"],ui:G1,status:"bridge",hint:s,extensions:n}),ha=vt({id:"assimp",title:"assimp (мост)",i18n:{en:D1},formats:[{id:"assimp",title:"assimp (glTF, FBX, OBJ, DAE…)",engine:"assimp",extensions:ua,status:"bridge",load:async(t,e)=>(await Promise.resolve().then(()=>j1)).loadWithAssimp(e.files,t)}],games:[Se("assimp-auto","Авто (любой формат assimp)",ua,"Для .obj/.gltf выберите вместе с моделью .mtl/.bin."),Se("gltf","glTF / GLB",["gltf","glb"]),Se("fbx","FBX",["fbx"]),Se("obj","Wavefront OBJ",["obj"],"Выберите .obj и .mtl одновременно (Ctrl/Shift)."),Se("dae","Collada (DAE)",["dae"]),Se("3ds","3DS",["3ds"]),Se("blend","Blender (.blend)",["blend"]),Se("stl","STL",["stl"]),Se("ply","PLY",["ply"]),Se("x","DirectX (.x)",["x"]),Se("md","Quake/Doom: MD2, MD3, MD5",["md2","md3","md5mesh"]),Se("smd","Source: SMD",["smd"]),Se("other","Прочие (LWO, BVH, DXF, 3MF, MS3D, ASE…)",["lwo","bvh","dxf","3mf","ms3d","ase","ac","off","ifc","xgl","cob","b3d","ogex"])]}),lf=Object.freeze(Object.defineProperty({__proto__:null,ASSIMP_EXTENSIONS:ua,ASSIMP_MISSING:Bo,assimpEngine:ha,configureAssimp:fa,convertScene:sf,loadWithAssimp:af},Symbol.toStringTag,{value:"Module"})),cf=[Jr,sc,lc,fc,uc,hc,pc,gc,wc,bc,xc,vc,Mc,kc,Ec,Ac,_c,Cc,Lc,Oc,Ic,Pc,Nc,zc,Gc,jc,Wc,qc,Qc,Jc,tf,ef,nf,ha];function hs(t,e){const n=new Float32Array(16);for(let s=0;s<4;s++)for(let o=0;o<4;o++){let r=0;for(let a=0;a<4;a++)r+=t[a*4+o]*e[s*4+a];n[s*4+o]=r}return n}function X1(t,e,n,s){const o=1/Math.tan(t/2),r=1/(n-s);return new Float32Array([o/e,0,0,0,0,o,0,0,0,0,(s+n)*r,-1,0,0,2*s*n*r,0])}function H1(t,e,n){let s=t[0]-e[0],o=t[1]-e[1],r=t[2]-e[2],a=Math.hypot(s,o,r)||1;s/=a,o/=a,r/=a;let i=n[1]*r-n[2]*o,l=n[2]*s-n[0]*r,c=n[0]*o-n[1]*s;a=Math.hypot(i,l,c)||1,i/=a,l/=a,c/=a;const f=o*c-r*l,h=r*i-s*c,u=s*l-o*i;return new Float32Array([i,f,s,0,l,h,o,0,c,u,r,0,-(i*t[0]+l*t[1]+c*t[2]),-(f*t[0]+h*t[1]+u*t[2]),-(s*t[0]+o*t[1]+r*t[2]),1])}const ff=new Float32Array([1,0,0,0,0,1,0,0,0,0,-1,0,0,0,0,1]),W1=new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]),uf=new Float32Array([1,0,0,0,0,0,-1,0,0,1,0,0,0,0,0,1]);function hf(t){const e=Array.from({length:16},(o,r)=>t[r]),n=new Float64Array(16);n[0]=e[5]*e[10]*e[15]-e[5]*e[11]*e[14]-e[9]*e[6]*e[15]+e[9]*e[7]*e[14]+e[13]*e[6]*e[11]-e[13]*e[7]*e[10],n[4]=-e[4]*e[10]*e[15]+e[4]*e[11]*e[14]+e[8]*e[6]*e[15]-e[8]*e[7]*e[14]-e[12]*e[6]*e[11]+e[12]*e[7]*e[10],n[8]=e[4]*e[9]*e[15]-e[4]*e[11]*e[13]-e[8]*e[5]*e[15]+e[8]*e[7]*e[13]+e[12]*e[5]*e[11]-e[12]*e[7]*e[9],n[12]=-e[4]*e[9]*e[14]+e[4]*e[10]*e[13]+e[8]*e[5]*e[14]-e[8]*e[6]*e[13]-e[12]*e[5]*e[10]+e[12]*e[6]*e[9],n[1]=-e[1]*e[10]*e[15]+e[1]*e[11]*e[14]+e[9]*e[2]*e[15]-e[9]*e[3]*e[14]-e[13]*e[2]*e[11]+e[13]*e[3]*e[10],n[5]=e[0]*e[10]*e[15]-e[0]*e[11]*e[14]-e[8]*e[2]*e[15]+e[8]*e[3]*e[14]+e[12]*e[2]*e[11]-e[12]*e[3]*e[10],n[9]=-e[0]*e[9]*e[15]+e[0]*e[11]*e[13]+e[8]*e[1]*e[15]-e[8]*e[3]*e[13]-e[12]*e[1]*e[11]+e[12]*e[3]*e[9],n[13]=e[0]*e[9]*e[14]-e[0]*e[10]*e[13]-e[8]*e[1]*e[14]+e[8]*e[2]*e[13]+e[12]*e[1]*e[10]-e[12]*e[2]*e[9],n[2]=e[1]*e[6]*e[15]-e[1]*e[7]*e[14]-e[5]*e[2]*e[15]+e[5]*e[3]*e[14]+e[13]*e[2]*e[7]-e[13]*e[3]*e[6],n[6]=-e[0]*e[6]*e[15]+e[0]*e[7]*e[14]+e[4]*e[2]*e[15]-e[4]*e[3]*e[14]-e[12]*e[2]*e[7]+e[12]*e[3]*e[6],n[10]=e[0]*e[5]*e[15]-e[0]*e[7]*e[13]-e[4]*e[1]*e[15]+e[4]*e[3]*e[13]+e[12]*e[1]*e[7]-e[12]*e[3]*e[5],n[14]=-e[0]*e[5]*e[14]+e[0]*e[6]*e[13]+e[4]*e[1]*e[14]-e[4]*e[2]*e[13]-e[12]*e[1]*e[6]+e[12]*e[2]*e[5],n[3]=-e[1]*e[6]*e[11]+e[1]*e[7]*e[10]+e[5]*e[2]*e[11]-e[5]*e[3]*e[10]-e[9]*e[2]*e[7]+e[9]*e[3]*e[6],n[7]=e[0]*e[6]*e[11]-e[0]*e[7]*e[10]-e[4]*e[2]*e[11]+e[4]*e[3]*e[10]+e[8]*e[2]*e[7]-e[8]*e[3]*e[6],n[11]=-e[0]*e[5]*e[11]+e[0]*e[7]*e[9]+e[4]*e[1]*e[11]-e[4]*e[3]*e[9]-e[8]*e[1]*e[7]+e[8]*e[3]*e[5],n[15]=e[0]*e[5]*e[10]-e[0]*e[6]*e[9]-e[4]*e[1]*e[10]+e[4]*e[2]*e[9]+e[8]*e[1]*e[6]-e[8]*e[2]*e[5];const s=e[0]*n[0]+e[1]*n[4]+e[2]*n[8]+e[3]*n[12];if(!s)return null;for(let o=0;o<16;o++)n[o]/=s;return n}function df(t,e){const n=Float32Array.of(1/0,1/0,1/0,-1/0,-1/0,-1/0);for(let s=0;s<e.length;s++){const o=e[s]*3;for(let r=0;r<3;r++){const a=t[o+r];a<n[r]&&(n[r]=a),a>n[3+r]&&(n[3+r]=a)}}return n}function K1(t,e,n,s=4096){const o=Math.floor(e.length/3);if(o<s||!(n>0))return null;const r=df(t,e),a=[0,1,2].map(x=>Math.max(1,Math.min(64,Math.ceil((r[3+x]-r[x])/n))));if(a[0]*a[1]*a[2]<2)return null;const i=new Uint32Array(o);for(let x=0;x<o;x++){let v=0;for(let b=2;b>=0;b--){const k=(t[e[x*3]*3+b]+t[e[x*3+1]*3+b]+t[e[x*3+2]*3+b])/3,E=Math.min(a[b]-1,Math.max(0,Math.floor((k-r[b])/n)));v=v*a[b]+E}i[x]=v}const l=a[0]*a[1]*a[2],c=new Uint32Array(l+1);for(let x=0;x<o;x++)c[i[x]+1]++;const f=[];for(let x=0;x<l;x++)c[x+1]&&f.push(x);for(let x=0;x<l;x++)c[x+1]+=c[x];const h=new Int32Array(l).fill(-1);f.forEach((x,v)=>{h[x]=v});const u=f.length,d=new Uint32Array(u),p=new Uint32Array(u),m=new Float32Array(u*6);f.forEach((x,v)=>{d[v]=c[x]*3,p[v]=(c[x+1]-c[x])*3,m.set([1/0,1/0,1/0,-1/0,-1/0,-1/0],v*6)});const g=c.slice(0,l),w=e instanceof Uint32Array?new Uint32Array(o*3):new Uint16Array(o*3),y=new Uint32Array(o),M=new Uint32Array(o);for(let x=0;x<o;x++){const v=g[i[x]]++,b=h[i[x]];y[x]=v,M[x]=b;for(let k=0;k<3;k++){const E=e[x*3+k];w[v*3+k]=E;for(let A=0;A<3;A++){const S=t[E*3+A],$=b*6;S<m[$+A]&&(m[$+A]=S),S>m[$+3+A]&&(m[$+3+A]=S)}}}return{sorted:w,slot:y,cellOf:M,start:d,count:p,box:m}}function Y1(t,e,n,s,o){for(let r=s;r<o;r++){const a=t.cellOf[r]*6;for(let i=0;i<3;i++){const l=n[r*3+i]*3;for(let c=0;c<3;c++){const f=e[l+c];f<t.box[a+c]&&(t.box[a+c]=f),f>t.box[a+3+c]&&(t.box[a+3+c]=f)}}}}function pf(t,e,n,s,o){if(s&&o<1/0){let a=0;for(let i=0;i<3;i++){const l=s[i],c=t[e+i],f=t[e+3+i],h=l<c?c-l:l>f?l-f:0;a+=h*h}if(a>o*o)return!1}let r=63;for(let a=0;a<8&&r;a++){const i=t[e+(a&1?3:0)],l=t[e+1+(a&2?3:0)],c=t[e+2+(a&4?3:0)],f=n[0]*i+n[4]*l+n[8]*c+n[12],h=n[1]*i+n[5]*l+n[9]*c+n[13],u=n[2]*i+n[6]*l+n[10]*c+n[14],d=n[3]*i+n[7]*l+n[11]*c+n[15];let p=0;f<-d&&(p|=1),f>d&&(p|=2),h<-d&&(p|=4),h>d&&(p|=8),u<-d&&(p|=16),u>d&&(p|=32),r&=p}return r===0}function Z1(t,e,n,s,o=[]){o.length=0;const r=t.start.length;for(let a=0;a<r;a++){if(!pf(t.box,a*6,e,n,s))continue;const i=t.start[a],l=t.count[a],c=o.length;c&&o[c-2]+o[c-1]===i?o[c-1]+=l:o.push(i,l)}return o}const q1=`#version 300 es
|
|
5
|
+
precision highp float;
|
|
6
|
+
layout(location=0) in vec3 aPos; layout(location=1) in vec3 aNor; layout(location=2) in vec2 aUV;
|
|
7
|
+
layout(location=3) in vec4 aBone; layout(location=4) in vec4 aWeight; layout(location=5) in vec4 aTan; layout(location=6) in vec2 aUV2; layout(location=7) in float aAlpha; layout(location=8) in vec4 aShade;
|
|
8
|
+
uniform mat4 uProj, uMV, uModel; uniform sampler2D uBones; uniform bool uSkinned;
|
|
9
|
+
out vec3 vN, vP, vO, vS; out vec4 vT; out vec2 vUV, vUV2; out float vY, vA;
|
|
10
|
+
mat4 bm(float f){ int i=int(f+0.5); return mat4(texelFetch(uBones,ivec2(0,i),0),texelFetch(uBones,ivec2(1,i),0),texelFetch(uBones,ivec2(2,i),0),texelFetch(uBones,ivec2(3,i),0)); }
|
|
11
|
+
void main(){
|
|
12
|
+
vec4 p=vec4(aPos,1.0); vec3 n=aNor, t=aTan.xyz;
|
|
13
|
+
if(uSkinned){ mat4 m=bm(aBone.x)*aWeight.x; if(aWeight.y!=0.0)m+=bm(aBone.y)*aWeight.y; if(aWeight.z!=0.0)m+=bm(aBone.z)*aWeight.z; if(aWeight.w!=0.0)m+=bm(aBone.w)*aWeight.w; p=m*p; n=mat3(m)*n; t=mat3(m)*t; }
|
|
14
|
+
vO=aPos; vec4 q=uMV*p; vP=q.xyz; vN=mat3(uMV)*n; vT=vec4(mat3(uMV)*t,aTan.w); vUV=aUV; vUV2=aUV2; vA=aAlpha; vS=aShade.rgb*2.0; vY=(uModel*p).y; gl_Position=uProj*q;
|
|
15
|
+
}`,Q1=`#version 300 es
|
|
16
|
+
precision highp float;
|
|
17
|
+
in vec3 vN, vP, vO, vS; in vec4 vT; in vec2 vUV, vUV2; in float vY, vA; out vec4 o;
|
|
18
|
+
// слои ландшафта: номер слоя на тексель (uTIdx, R8), текстуры слоёв (массив), период повтора, подкраска, средний цвет
|
|
19
|
+
uniform bool uTerr, uTHasN, uTHasM; uniform highp sampler2D uTIdx; uniform highp sampler2DArray uTLay, uTNrm, uTMid; uniform vec2 uTPar[32], uTMPar[32]; uniform vec3 uTMean[32], uTMMean[32]; uniform float uTFade;
|
|
20
|
+
uniform mat4 uMV;
|
|
21
|
+
// нормаль слоёв ландшафта (касательное пространство: X, Z модели) и AO — из terrainColor
|
|
22
|
+
vec3 gTerrN=vec3(0.0,0.0,1.0); float gTerrAO=1.0;
|
|
23
|
+
vec3 terrainColor(vec3 sat){
|
|
24
|
+
vec2 sz=vec2(textureSize(uTIdx,0)), f=vUV*sz-0.5, w=fract(f); ivec2 i0=ivec2(floor(f)), mx=ivec2(sz)-1;
|
|
25
|
+
vec3 acc=vec3(0.0); vec2 nxy=vec2(0.0); float ao=0.0;
|
|
26
|
+
for(int k=0;k<4;k++){
|
|
27
|
+
ivec2 d=ivec2(k&1,k>>1);
|
|
28
|
+
int L=int(texelFetch(uTIdx,clamp(i0+d,ivec2(0),mx),0).r*255.0+0.5);
|
|
29
|
+
float wk=(d.x==1?w.x:1.0-w.x)*(d.y==1?w.y:1.0-w.y);
|
|
30
|
+
if(wk<=0.0) continue;
|
|
31
|
+
vec2 pr=uTPar[L], tuv=vO.xz/pr.x;
|
|
32
|
+
vec3 c=texture(uTLay,vec3(tuv,float(L))).rgb;
|
|
33
|
+
// средний масштаб: изменчивость (MiddleScaleUV) — отношение к среднему цвету, сила MiddleBCRBlend
|
|
34
|
+
if(uTHasM){ vec2 mp=uTMPar[L]; if(mp.y>0.0){ vec3 mc=texture(uTMid,vec3(vO.xz/mp.x,float(L))).rgb; c*=mix(vec3(1.0),mc/max(uTMMean[L],vec3(0.03)),mp.y); } }
|
|
35
|
+
if(uTHasN){ vec4 nh=texture(uTNrm,vec3(tuv,float(L))); nxy+=wk*(nh.rg*2.0-1.0); ao+=wk*nh.a; } else ao+=wk;
|
|
36
|
+
// подкраска цветовой картой: узор слоя, тон и яркость — от карты (SatMapBlend)
|
|
37
|
+
acc+=wk*mix(c,c*sat/max(uTMean[L],vec3(0.03)),pr.y);
|
|
38
|
+
}
|
|
39
|
+
// вдали — только цветовая карта (как у игры: детали до DetailMaxDistance)
|
|
40
|
+
float fd=clamp((length(vP)-uTFade*0.5)/(uTFade*0.5),0.0,1.0);
|
|
41
|
+
// DirectX: зелёный — к −V; V по +Z модели
|
|
42
|
+
nxy=vec2(nxy.x,-nxy.y)*(1.0-fd);
|
|
43
|
+
gTerrN=vec3(nxy,sqrt(max(0.0,1.0-dot(nxy,nxy)))); gTerrAO=mix(ao,1.0,fd);
|
|
44
|
+
return mix(clamp(acc*mix(1.0,ao,0.5),0.0,1.0),sat,fd);
|
|
45
|
+
}
|
|
46
|
+
// карта освещения уровня (яркость в R) по второму набору UV
|
|
47
|
+
uniform sampler2D uLm; uniform bool uHasLm; uniform bool uLmRgb; uniform float uLmK;
|
|
48
|
+
// рельеф (Source bump lightmaps): три карты по направлениям базиса, смешиваются по нормали из карты нормалей (касательное пространство)
|
|
49
|
+
uniform highp sampler2DArray uLmB; uniform bool uHasLmB;
|
|
50
|
+
bool gBump=false; vec3 gBumpN=vec3(0.0,0.0,1.0);
|
|
51
|
+
vec3 lmap(){
|
|
52
|
+
vec3 l;
|
|
53
|
+
if(gBump){
|
|
54
|
+
vec3 w=max(vec3(dot(gBumpN,vec3(0.81649658,0.0,0.57735027)),dot(gBumpN,vec3(-0.40824829,0.70710678,0.57735027)),dot(gBumpN,vec3(-0.40824829,-0.70710678,0.57735027))),0.0);
|
|
55
|
+
w*=w;
|
|
56
|
+
l=(w.x*texture(uLmB,vec3(vUV2,0.0)).rgb+w.y*texture(uLmB,vec3(vUV2,1.0)).rgb+w.z*texture(uLmB,vec3(vUV2,2.0)).rgb)/max(w.x+w.y+w.z,1e-4);
|
|
57
|
+
} else l=texture(uLm,vUV2).rgb;
|
|
58
|
+
return (uLmRgb ? l : vec3(l.r))*uLmK;
|
|
59
|
+
}
|
|
60
|
+
// плоскость отсечения (отражение воды): в координатах модели, рисуется то, что с её положительной стороны
|
|
61
|
+
uniform bool uClipOn; uniform vec4 uClipPl;
|
|
62
|
+
// вода и стекло: отражение (uRefl), снимок сцены и его глубина (uRefr, uDepth); uFxK — искажение преломления/отражения, туман: начало/конец
|
|
63
|
+
uniform int uFx; uniform sampler2D uRefl, uRefr, uDepth; uniform vec2 uScreen, uNF, uFxScroll; uniform vec3 uFxRT, uFxLT, uFogC; uniform vec4 uFxK; uniform bool uFxRefl, uFxFog; uniform float uTime;
|
|
64
|
+
float linDepth(float z){ float n=uNF.x, f=uNF.y; return 2.0*n*f/(f+n-(z*2.0-1.0)*(f-n)); }
|
|
65
|
+
// срез сверху: всё выше uClipY (мировая высота показа) не рисуется — чтобы заглянуть внутрь уровня
|
|
66
|
+
uniform float uClipY;
|
|
67
|
+
uniform sampler2D uTex, uNorm, uSurf; uniform bool uHasTex, uAlphaTest, uFlat, uHasNorm, uHasSurf; uniform vec3 uFlatColor, uBase; uniform float uFlatAlpha;
|
|
68
|
+
// uNormPack: 0 — xyz в RGB, 1 — XY в RG (BC5), 2 — X в альфе, Y в зелёном (DXT5nm); uNormY: +1 OpenGL (Y вверх), -1 Direct3D; uMirror: -1 при отражённой сцене
|
|
69
|
+
uniform int uNormPack, uView; uniform float uNormY, uMirror, uNI;
|
|
70
|
+
vec3 tangentNormal(vec4 t){
|
|
71
|
+
vec3 n;
|
|
72
|
+
if(uNormPack==0) n=t.rgb*2.0-1.0;
|
|
73
|
+
else { vec2 xy=(uNormPack==1?t.rg:t.ag)*2.0-1.0; n=vec3(xy,sqrt(max(0.0,1.0-dot(xy,xy)))); }
|
|
74
|
+
n.y*=uNormY;
|
|
75
|
+
return normalize(n);
|
|
76
|
+
}
|
|
77
|
+
const float PI=3.14159265;
|
|
78
|
+
// процедурное окружение для отражений (небо сверху, земля снизу), размывается по шероховатости
|
|
79
|
+
vec3 envColor(vec3 R, float rough){
|
|
80
|
+
vec3 sky=mix(vec3(0.55,0.60,0.68),vec3(0.28,0.38,0.58),clamp(R.y,0.0,1.0));
|
|
81
|
+
vec3 gnd=mix(vec3(0.30,0.30,0.31),vec3(0.14,0.12,0.11),clamp(-R.y*3.0,0.0,1.0));
|
|
82
|
+
vec3 e=R.y>0.0?sky:gnd;
|
|
83
|
+
return mix(e,vec3(0.30,0.32,0.36),rough*rough);
|
|
84
|
+
}
|
|
85
|
+
// аппроксимация интеграла BRDF для окружения (Karis, UE4 mobile)
|
|
86
|
+
vec3 envBRDF(vec3 F0, float rough, float NdV){
|
|
87
|
+
vec4 r=rough*vec4(-1.0,-0.0275,-0.572,0.022)+vec4(1.0,0.0425,1.04,-0.04);
|
|
88
|
+
float a004=min(r.x*r.x,exp2(-9.28*NdV))*r.x+r.y;
|
|
89
|
+
vec2 AB=vec2(-1.04,1.04)*a004+r.zw;
|
|
90
|
+
return F0*AB.x+AB.y;
|
|
91
|
+
}
|
|
92
|
+
vec4 screenFx(){
|
|
93
|
+
vec2 suv=gl_FragCoord.xy/uScreen, d=vec2(0.0);
|
|
94
|
+
vec3 N=normalize(vN), V=normalize(-vP);
|
|
95
|
+
if(dot(N,V)<0.0) N=-N;
|
|
96
|
+
if(uHasNorm){
|
|
97
|
+
// две копии карты нормалей с разной прокруткой — рябь не повторяется явно
|
|
98
|
+
vec2 sc=uFxScroll*uTime;
|
|
99
|
+
vec3 a=tangentNormal(texture(uNorm,vUV+sc)), b=tangentNormal(texture(uNorm,vUV*0.73+vec2(0.31,0.17)-sc*0.6));
|
|
100
|
+
d=(a.xy+b.xy)*0.5;
|
|
101
|
+
} else d=vec2(sin(vO.x*0.05+uTime*1.3)+sin(vO.y*0.07-uTime),cos(vO.y*0.05+uTime)+cos(vO.x*0.06-uTime*0.8))*0.12;
|
|
102
|
+
float wz=-vP.z;
|
|
103
|
+
vec2 ru=clamp(suv+d*uFxK.x,vec2(0.001),vec2(0.999));
|
|
104
|
+
float sz=linDepth(texture(uDepth,ru).r);
|
|
105
|
+
if(sz<wz){ ru=suv; sz=linDepth(texture(uDepth,ru).r); } // искажение захватило то, что перед поверхностью
|
|
106
|
+
vec3 refr=texture(uRefr,ru).rgb*uFxRT;
|
|
107
|
+
// стекло: преломление и блик окружения ($envmap × $envmaptint) по Френелю
|
|
108
|
+
if(uFx==2) return vec4(refr+envColor(reflect(-V,normalize(N+vec3(d*0.5,0.0))),0.2)*uFxLT*mix(0.15,1.0,pow(1.0-max(dot(N,V),0.0),3.0)),1.0);
|
|
109
|
+
if(uFxFog){ float th=max(0.0,sz-wz)*length(vP)/max(wz,1e-3); refr=mix(refr,uFogC,clamp((th-uFxK.z)/max(uFxK.w-uFxK.z,1.0),0.0,1.0)); }
|
|
110
|
+
vec3 refl=uFxRefl ? texture(uRefl,clamp(suv+d*uFxK.y,vec2(0.001),vec2(0.999))).rgb : envColor(reflect(-V,N),0.3);
|
|
111
|
+
float fr=mix(0.25,1.0,pow(1.0-max(dot(N,V),0.0),3.0));
|
|
112
|
+
return vec4(mix(refr,refl*uFxLT,fr),1.0);
|
|
113
|
+
}
|
|
114
|
+
void main(){
|
|
115
|
+
if(vY>uClipY) discard;
|
|
116
|
+
if(uClipOn&&dot(vec4(vO,1.0),uClipPl)<-0.25) discard;
|
|
117
|
+
if(uFlat){ o=vec4(uFlatColor,uFlatAlpha); return; }
|
|
118
|
+
if(uFx>0){ o=screenFx(); return; }
|
|
119
|
+
vec4 c=uHasTex?texture(uTex,vUV):vec4(uBase,1.0);
|
|
120
|
+
if(uAlphaTest&&c.a<0.5) discard;
|
|
121
|
+
if(uTerr) c.rgb=terrainColor(c.rgb);
|
|
122
|
+
c.rgb*=vS; // запечённый свет по вершинам (без него — 1)
|
|
123
|
+
// нормали моделей бывают развёрнуты как угодно (обход граней у игр разный, сцена может быть отражена):
|
|
124
|
+
// освещаем сторону, обращённую к камере
|
|
125
|
+
vec3 V=normalize(-vP), N=normalize(vN);
|
|
126
|
+
float side=dot(N,V)<0.0?-1.0:1.0;
|
|
127
|
+
vec3 Ng=N*side, Nw=Ng, nt=vec3(0.0,0.0,1.0);
|
|
128
|
+
if(uTerr&&uTHasN){
|
|
129
|
+
vec3 T=mat3(uMV)*vec3(1.0,0.0,0.0), B=mat3(uMV)*vec3(0.0,0.0,1.0);
|
|
130
|
+
T=normalize(T-Ng*dot(Ng,T)); B=normalize(B-Ng*dot(Ng,B)-T*dot(T,B));
|
|
131
|
+
vec3 tn=normalize(vec3(gTerrN.xy*uNI,max(gTerrN.z,0.001)));
|
|
132
|
+
Nw=normalize(T*tn.x+B*tn.y+Ng*tn.z);
|
|
133
|
+
}
|
|
134
|
+
if(uHasNorm){
|
|
135
|
+
vec3 T=vT.xyz-N*dot(N,vT.xyz);
|
|
136
|
+
if(dot(T,T)>1e-10){
|
|
137
|
+
T=normalize(T);
|
|
138
|
+
vec3 B=cross(N,T)*vT.w*uMirror;
|
|
139
|
+
// как в Normal Map Studio: сила рельефа масштабирует XY касательной нормали
|
|
140
|
+
nt=tangentNormal(texture(uNorm,vUV));
|
|
141
|
+
nt=normalize(vec3(nt.xy*uNI,max(nt.z,0.001)));
|
|
142
|
+
Nw=normalize(mat3(T,B,N)*nt)*side;
|
|
143
|
+
// базис рельефа Source — в осях текстуры как они записаны в карте (без пересчёта Y под соглашение показа)
|
|
144
|
+
gBump=uHasLmB; gBumpN=vec3(nt.x,nt.y*uNormY,nt.z);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if(uView==1){ o=vec4(Nw*0.5+0.5,1.0); return; } // нормали (пространство камеры)
|
|
148
|
+
if(uView==2){ o=vec4(nt*0.5+0.5,1.0); return; } // карта нормалей (касательное пространство)
|
|
149
|
+
if(uView==3&&!uHasSurf){ o=vec4(0.0,0.6,0.0,1.0); return; } // поверхность: нет карты — AO 0, шероховатость 0.6, металл 0
|
|
150
|
+
vec3 L=normalize(vec3(0.4,0.6,1.0)), H=normalize(L+V);
|
|
151
|
+
vec3 alb=pow(c.rgb,vec3(2.2));
|
|
152
|
+
if(uHasSurf){
|
|
153
|
+
// PBR (metal/rough, GGX): R — AO, G — шероховатость, B — металличность, A — блик неметалла (0.5 = F0 0.04)
|
|
154
|
+
vec4 sf=texture(uSurf,vUV);
|
|
155
|
+
float ao=sf.r, rough=clamp(sf.g,0.045,1.0), metal=sf.b, a=rough*rough, a2=a*a;
|
|
156
|
+
if(uView==3){ o=vec4(sf.rgb,1.0); return; }
|
|
157
|
+
vec3 F0=mix(vec3(0.08*sf.a),alb,metal);
|
|
158
|
+
float NdL=max(dot(Nw,L),0.0), NdV=max(dot(Nw,V),1e-3), NdH=max(dot(Nw,H),0.0), VdH=max(dot(V,H),0.0);
|
|
159
|
+
float geo=smoothstep(0.0,0.15,dot(Ng,L));
|
|
160
|
+
float dd=NdH*NdH*(a2-1.0)+1.0, D=a2/(PI*dd*dd);
|
|
161
|
+
float k=(rough+1.0)*(rough+1.0)/8.0, G=(NdL/(NdL*(1.0-k)+k))*(NdV/(NdV*(1.0-k)+k));
|
|
162
|
+
vec3 F=F0+(1.0-F0)*pow(1.0-VdH,5.0);
|
|
163
|
+
vec3 spec=D*G*F/max(4.0*NdL*NdV,1e-4);
|
|
164
|
+
vec3 kd=(1.0-F)*(1.0-metal);
|
|
165
|
+
vec3 direct=(kd*alb/PI+spec)*vec3(1.0,0.97,0.92)*NdL*geo*PI*1.2;
|
|
166
|
+
vec3 amb=mix(vec3(0.05,0.05,0.06),vec3(0.20,0.22,0.26),Nw.y*0.5+0.5);
|
|
167
|
+
vec3 R=reflect(-V,Nw);
|
|
168
|
+
vec3 ambient=(alb*(1.0-metal)*amb+envColor(R,rough)*envBRDF(F0,rough,NdV))*ao;
|
|
169
|
+
if(uHasLm){ vec3 lm=lmap(); direct*=lm*0.5; ambient=ambient*0.25+alb*(1.0-metal)*lm*ao; }
|
|
170
|
+
o=vec4(pow(direct+ambient,vec3(1.0/2.2)),vA);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
// освещение из Normal Map Studio: линейный цвет, полусферический фон, «геометрическая» тень, блик Блинна
|
|
174
|
+
float ndl=max(dot(Nw,L),0.0), geo=smoothstep(0.0,0.15,dot(Ng,L));
|
|
175
|
+
float sp=uHasNorm?pow(max(dot(Nw,H),0.0),32.0)*0.25*geo*step(0.0,ndl):0.0;
|
|
176
|
+
vec3 amb=mix(vec3(0.05,0.05,0.06),vec3(0.20,0.22,0.26),Nw.y*0.5+0.5);
|
|
177
|
+
vec3 col=alb*(amb+vec3(1.0,0.97,0.92)*ndl*geo*1.2)+vec3(sp);
|
|
178
|
+
if(uHasLm){ vec3 lm=lmap(); col=alb*(lm*(0.75+0.25*ndl*geo)+amb*0.15)+vec3(sp)*lm; }
|
|
179
|
+
o=vec4(pow(col,vec3(1.0/2.2)),vA);
|
|
180
|
+
}`,J1=`#version 300 es
|
|
181
|
+
void main(){ vec2 p=vec2(gl_VertexID==1?3.0:-1.0, gl_VertexID==2?3.0:-1.0); gl_Position=vec4(p,0.0,1.0); }`,t2=`#version 300 es
|
|
182
|
+
precision mediump float; uniform sampler2D uT; out vec4 o; void main(){ o=texelFetch(uT,ivec2(gl_FragCoord.xy),0); }`,e2=`#version 300 es
|
|
183
|
+
layout(location=0) in vec3 aPos; uniform mat4 uProj, uMV; void main(){ gl_Position=uProj*uMV*vec4(aPos,1.0); }`,n2=`#version 300 es
|
|
184
|
+
precision mediump float; uniform vec4 uColor; out vec4 o; void main(){ o=uColor; }`;function da(t,e,n){const s=(r,a)=>{const i=t.createShader(r);if(t.shaderSource(i,a),t.compileShader(i),!t.getShaderParameter(i,t.COMPILE_STATUS))throw new Error(t.getShaderInfoLog(i)??"shader");return i},o=t.createProgram();if(t.attachShader(o,s(t.VERTEX_SHADER,e)),t.attachShader(o,s(t.FRAGMENT_SHADER,n)),t.linkProgram(o),!t.getProgramParameter(o,t.LINK_STATUS))throw new Error(t.getProgramInfoLog(o)??"program");return o}const pa={background:[.12,.14,.17],grid:[.35,.4,.45,1],skeleton:[1,.85,.2,1],wire:[.9,.95,1],base:[.7,.72,.76]},s2={background:[.93,.94,.96],grid:[.7,.73,.78,1],skeleton:[.85,.45,.05,1],wire:[.15,.2,.3],base:[.62,.64,.68]};class mf{constructor(e){this.canvas=e;const n=e.getContext("webgl2",{antialias:!0});if(!n)throw new Error("WebGL2 недоступен");this.gl=n,this.p=da(n,q1,Q1),this.lp=da(n,e2,n2),this.qp=da(n,J1,t2),this.qVao=n.createVertexArray();const s=(o,r)=>n.getUniformLocation(o,r);this.U={proj:s(this.p,"uProj"),mv:s(this.p,"uMV"),model:s(this.p,"uModel"),clipY:s(this.p,"uClipY"),bones:s(this.p,"uBones"),skinned:s(this.p,"uSkinned"),tex:s(this.p,"uTex"),hasTex:s(this.p,"uHasTex"),alpha:s(this.p,"uAlphaTest"),flat:s(this.p,"uFlat"),flatColor:s(this.p,"uFlatColor"),flatAlpha:s(this.p,"uFlatAlpha"),lm:s(this.p,"uLm"),hasLm:s(this.p,"uHasLm"),lmRgb:s(this.p,"uLmRgb"),lmK:s(this.p,"uLmK"),base:s(this.p,"uBase"),terr:s(this.p,"uTerr"),tIdx:s(this.p,"uTIdx"),tLay:s(this.p,"uTLay"),tNrm:s(this.p,"uTNrm"),tMid:s(this.p,"uTMid"),tHasN:s(this.p,"uTHasN"),tHasM:s(this.p,"uTHasM"),tMPar:s(this.p,"uTMPar"),tMMean:s(this.p,"uTMMean"),tPar:s(this.p,"uTPar"),tMean:s(this.p,"uTMean"),tFade:s(this.p,"uTFade"),lmB:s(this.p,"uLmB"),hasLmB:s(this.p,"uHasLmB"),clipOn:s(this.p,"uClipOn"),clipPl:s(this.p,"uClipPl"),fx:s(this.p,"uFx"),refl:s(this.p,"uRefl"),refr:s(this.p,"uRefr"),depth:s(this.p,"uDepth"),screen:s(this.p,"uScreen"),nf:s(this.p,"uNF"),fxScroll:s(this.p,"uFxScroll"),fxRT:s(this.p,"uFxRT"),fxLT:s(this.p,"uFxLT"),fogC:s(this.p,"uFogC"),fxK:s(this.p,"uFxK"),fxRefl:s(this.p,"uFxRefl"),fxFog:s(this.p,"uFxFog"),time:s(this.p,"uTime"),norm:s(this.p,"uNorm"),hasNorm:s(this.p,"uHasNorm"),normPack:s(this.p,"uNormPack"),normY:s(this.p,"uNormY"),mirror:s(this.p,"uMirror"),view:s(this.p,"uView"),ni:s(this.p,"uNI"),surf:s(this.p,"uSurf"),hasSurf:s(this.p,"uHasSurf")},this.LU={proj:s(this.lp,"uProj"),mv:s(this.lp,"uMV"),color:s(this.lp,"uColor")},this.boneTex=n.createTexture(),this.gridVao=this.mkLineVao(),this.skelVao=this.mkLineVao(),this.aniso=n.getExtension("EXT_texture_filter_anisotropic")}canvas;gl;meshes=[];theme=pa;bounds={center:[0,0,0],radius:1};p;lp;qp;qVao;fxT=null;fxFailed=!1;lmBTex=null;fxStats={meshes:0,reflection:!1};U;LU;boneTex;gridVao;skelVao;aniso;boneCount=0;gridN=0;skelN=0;flip=ff;flipOn=!0;zUp=!1;normalY=-1;yaw0=Math.PI;rawBounds={min:[0,0,0],max:[1,1,1]};highlight=[];lmTex=null;layTex=null;layNrm=null;layMid=null;layMPar=new Float32Array(64);layMMean=new Float32Array(96);layPar=new Float32Array(64);layMean=new Float32Array(96);layFade=150;lmScale=1;lmRgb=!1;drawStats={tris:0,calls:0,cells:0};lastPVM=null;get zUpOn(){return this.zUp}toView(e,n,s){let o=this.zUp?[e,s,-n]:[e,n,s];return this.flipOn&&(o=[o[0],o[1],-o[2]]),o}mkLineVao(){const e=this.gl,n=e.createVertexArray(),s=e.createBuffer();return e.bindVertexArray(n),e.bindBuffer(e.ARRAY_BUFFER,s),e.enableVertexAttribArray(0),e.vertexAttribPointer(0,3,e.FLOAT,!1,0,0),{vao:n,buf:s}}buf(e,n){const s=this.gl,o=s.createBuffer();return s.bindBuffer(e,o),s.bufferData(e,n,s.STATIC_DRAW),o}attribs(e){const n=this.gl;[[e.bPos,3],[e.bNor,3],[e.bUV,2],[e.bBone,4],[e.bWt,4],[e.bTan,4],[e.bUV2,2],[e.bA,1]].forEach(([o,r],a)=>{o?(n.bindBuffer(n.ARRAY_BUFFER,o),n.enableVertexAttribArray(a),n.vertexAttribPointer(a,r,n.FLOAT,!1,0,0)):(n.disableVertexAttribArray(a),a===4||a===7?n.vertexAttrib4f(a,1,0,0,0):n.vertexAttrib4f(a,0,0,0,0))}),e.bS?(n.bindBuffer(n.ARRAY_BUFFER,e.bS),n.enableVertexAttribArray(8),n.vertexAttribPointer(8,4,n.UNSIGNED_BYTE,!0,0,0)):(n.disableVertexAttribArray(8),n.vertexAttrib4f(8,.5,.5,.5,.5))}clear(){const e=this.gl;for(const n of this.meshes){e.deleteVertexArray(n.vao),n.vaoW&&e.deleteVertexArray(n.vaoW),n.tex&&e.deleteTexture(n.tex),n.norm&&e.deleteTexture(n.norm),n.surf&&e.deleteTexture(n.surf),n.tIdx&&e.deleteTexture(n.tIdx),n.vaoS&&e.deleteVertexArray(n.vaoS);for(const s of[n.bPos,n.bNor,n.bUV,n.bBone,n.bWt,n.bTan,n.bIdx,n.bLine,n.bUV2,n.bA,n.bS,n.bIdxS])s&&e.deleteBuffer(s)}this.meshes=[],this.highlight=[],this.layTex&&(e.deleteTexture(this.layTex),this.layTex=null),this.layNrm&&(e.deleteTexture(this.layNrm),this.layNrm=null),this.layMid&&(e.deleteTexture(this.layMid),this.layMid=null)}dispose(){const e=this.gl;this.clear(),e.deleteTexture(this.boneTex);for(const n of[this.gridVao,this.skelVao])e.deleteVertexArray(n.vao),e.deleteBuffer(n.buf);e.deleteProgram(this.p),e.deleteProgram(this.lp),e.deleteProgram(this.qp),e.deleteVertexArray(this.qVao),this.freeTargets(),this.lmBTex&&e.deleteTexture(this.lmBTex)}setModel(e){const n=this.gl;this.clear();const s=e.bounds,o=Math.max(s.max[0]-s.min[0],s.max[1]-s.min[1],s.max[2]-s.min[2]),r=Math.max(8,o/16),a=!e.skeleton;this.drawStats={tris:0,calls:0,cells:0};for(const i of e.meshes){const l=this.buf(n.ARRAY_BUFFER,i.pos),c=this.buf(n.ARRAY_BUFFER,i.nor),f=this.buf(n.ARRAY_BUFFER,i.uv),h=!!e.skeleton,u=h?this.buf(n.ARRAY_BUFFER,i.bone):null,d=h?this.buf(n.ARRAY_BUFFER,i.weight):null,p=this.buf(n.ARRAY_BUFFER,Il(i)),m=i.uv2?this.buf(n.ARRAY_BUFFER,i.uv2):null,g=i.vertexAlpha?this.buf(n.ARRAY_BUFFER,i.vertexAlpha):null,w=i.vertexShade?this.buf(n.ARRAY_BUFFER,i.vertexShade):null,y=n.createVertexArray(),M={fx:i.screenFx??null,count:i.indices.length,data:i,idx:i.indices.slice(),visible:!i.defaultHidden,tex:null,norm:null,normPack:"rgb",surf:null,tIdx:null,itype:i.indices instanceof Uint32Array?n.UNSIGNED_INT:n.UNSIGNED_SHORT,bPos:l,bNor:c,bUV:f,bBone:u,bWt:d,bTan:p,bUV2:m,bA:g,bS:w,vao:y,bIdx:null,box:a&&!i.dynamic?df(i.pos,i.indices):null};if(n.bindVertexArray(y),this.attribs(M),M.bIdx=this.buf(n.ELEMENT_ARRAY_BUFFER,M.idx),M.box){const x=K1(i.pos,M.idx,r);x&&(M.cells=x,M.vaoS=n.createVertexArray(),n.bindVertexArray(M.vaoS),this.attribs(M),M.bIdxS=this.buf(n.ELEMENT_ARRAY_BUFFER,x.sorted))}i.terrainIndex&&e.terrainLayers&&(M.tIdx=this.uploadR8(i.terrainIndex)),this.meshes.push(M)}n.bindVertexArray(null),this.setTerrainLayers(e.terrainLayers??null),this.boneCount=e.skeleton?e.skeleton.n:0,this.skelN=0,this.flipOn=!!e.flipZ,this.flip=this.flipOn?ff:W1,this.yaw0=e.cameraYaw??(this.flipOn?Math.PI:0),this.zUp=!1,this.normalY=e.normalY==="gl"?1:-1,this.rawBounds=e.bounds,n.activeTexture(n.TEXTURE1),n.bindTexture(n.TEXTURE_2D,this.boneTex),n.texImage2D(n.TEXTURE_2D,0,n.RGBA32F,4,Math.max(1,this.boneCount),0,n.RGBA,n.FLOAT,null),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_MIN_FILTER,n.NEAREST),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_MAG_FILTER,n.NEAREST),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_WRAP_S,n.CLAMP_TO_EDGE),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_WRAP_T,n.CLAMP_TO_EDGE),this.setLightmap(e.lightmap??null),this.buildGrid(),this.computeBounds()}setLightmap(e){const n=this.gl;if(this.lmTex&&(n.deleteTexture(this.lmTex),this.lmTex=null),this.lmBTex&&(n.deleteTexture(this.lmBTex),this.lmBTex=null),!e)return;if(e.bump&&e.rgb){const o=n.createTexture();n.activeTexture(n.TEXTURE9),n.bindTexture(n.TEXTURE_2D_ARRAY,o),n.pixelStorei(n.UNPACK_ALIGNMENT,1),n.texImage3D(n.TEXTURE_2D_ARRAY,0,n.RGB8,e.width,e.height,3,0,n.RGB,n.UNSIGNED_BYTE,null),e.bump.forEach((r,a)=>n.texSubImage3D(n.TEXTURE_2D_ARRAY,0,0,0,a,e.width,e.height,1,n.RGB,n.UNSIGNED_BYTE,r)),n.generateMipmap(n.TEXTURE_2D_ARRAY),n.texParameteri(n.TEXTURE_2D_ARRAY,n.TEXTURE_MIN_FILTER,n.LINEAR_MIPMAP_LINEAR),n.texParameteri(n.TEXTURE_2D_ARRAY,n.TEXTURE_MAG_FILTER,n.LINEAR),n.texParameteri(n.TEXTURE_2D_ARRAY,n.TEXTURE_WRAP_S,n.CLAMP_TO_EDGE),n.texParameteri(n.TEXTURE_2D_ARRAY,n.TEXTURE_WRAP_T,n.CLAMP_TO_EDGE),this.lmBTex=o}const s=n.createTexture();n.activeTexture(n.TEXTURE4),n.bindTexture(n.TEXTURE_2D,s),n.pixelStorei(n.UNPACK_ALIGNMENT,1),e.rgb?n.texImage2D(n.TEXTURE_2D,0,n.RGB8,e.width,e.height,0,n.RGB,n.UNSIGNED_BYTE,e.data):n.texImage2D(n.TEXTURE_2D,0,n.R8,e.width,e.height,0,n.RED,n.UNSIGNED_BYTE,e.data),this.lmRgb=!!e.rgb,n.generateMipmap(n.TEXTURE_2D),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_MIN_FILTER,n.LINEAR_MIPMAP_LINEAR),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_MAG_FILTER,n.LINEAR),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_WRAP_S,n.CLAMP_TO_EDGE),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_WRAP_T,n.CLAMP_TO_EDGE),this.lmTex=s,this.lmScale=e.scale??1}get hasLightmap(){return!!this.lmTex}get hasBumpLightmap(){return!!this.lmBTex}freeTargets(){const e=this.gl,n=this.fxT;if(n){for(const s of[n.fbA,n.fbB,n.fbR])e.deleteFramebuffer(s);for(const s of[n.rbColor,n.rbDepth,n.rbReflDepth])e.deleteRenderbuffer(s);for(const s of[n.texColor,n.texDepth,n.texRefl])e.deleteTexture(s);this.fxT=null}}targets(e,n){if(this.fxFailed)return null;if(this.fxT&&this.fxT.w===e&&this.fxT.h===n)return this.fxT;this.freeTargets();const s=this.gl,o=(u,d,p,m,g)=>{const w=s.createTexture();s.activeTexture(s.TEXTURE10),s.bindTexture(s.TEXTURE_2D,w),s.texImage2D(s.TEXTURE_2D,0,p,u,d,0,m,g,null);const y=m===s.DEPTH_COMPONENT?s.NEAREST:s.LINEAR;return s.texParameteri(s.TEXTURE_2D,s.TEXTURE_MIN_FILTER,y),s.texParameteri(s.TEXTURE_2D,s.TEXTURE_MAG_FILTER,y),s.texParameteri(s.TEXTURE_2D,s.TEXTURE_WRAP_S,s.CLAMP_TO_EDGE),s.texParameteri(s.TEXTURE_2D,s.TEXTURE_WRAP_T,s.CLAMP_TO_EDGE),s.bindTexture(s.TEXTURE_2D,null),w},r=Math.min(4,s.getParameter(s.MAX_SAMPLES)),a=(u,d,p,m)=>{const g=s.createRenderbuffer();return s.bindRenderbuffer(s.RENDERBUFFER,g),m>0?s.renderbufferStorageMultisample(s.RENDERBUFFER,m,u,d,p):s.renderbufferStorage(s.RENDERBUFFER,u,d,p),g},i=Math.max(1,e>>1),l=Math.max(1,n>>1),c={w:e,h:n,rw:i,rh:l,fbA:s.createFramebuffer(),rbColor:a(s.RGBA8,e,n,r),rbDepth:a(s.DEPTH_COMPONENT24,e,n,r),fbB:s.createFramebuffer(),texColor:o(e,n,s.RGBA8,s.RGBA,s.UNSIGNED_BYTE),texDepth:o(e,n,s.DEPTH_COMPONENT24,s.DEPTH_COMPONENT,s.UNSIGNED_INT),fbR:s.createFramebuffer(),texRefl:o(i,l,s.RGBA8,s.RGBA,s.UNSIGNED_BYTE),rbReflDepth:a(s.DEPTH_COMPONENT24,i,l,0)};let f=!0;const h=()=>{f&&=s.checkFramebufferStatus(s.FRAMEBUFFER)===s.FRAMEBUFFER_COMPLETE};return s.bindFramebuffer(s.FRAMEBUFFER,c.fbA),s.framebufferRenderbuffer(s.FRAMEBUFFER,s.COLOR_ATTACHMENT0,s.RENDERBUFFER,c.rbColor),s.framebufferRenderbuffer(s.FRAMEBUFFER,s.DEPTH_ATTACHMENT,s.RENDERBUFFER,c.rbDepth),h(),s.bindFramebuffer(s.FRAMEBUFFER,c.fbB),s.framebufferTexture2D(s.FRAMEBUFFER,s.COLOR_ATTACHMENT0,s.TEXTURE_2D,c.texColor,0),s.framebufferTexture2D(s.FRAMEBUFFER,s.DEPTH_ATTACHMENT,s.TEXTURE_2D,c.texDepth,0),h(),s.bindFramebuffer(s.FRAMEBUFFER,c.fbR),s.framebufferTexture2D(s.FRAMEBUFFER,s.COLOR_ATTACHMENT0,s.TEXTURE_2D,c.texRefl,0),s.framebufferRenderbuffer(s.FRAMEBUFFER,s.DEPTH_ATTACHMENT,s.RENDERBUFFER,c.rbReflDepth),h(),s.bindFramebuffer(s.FRAMEBUFFER,null),this.fxT=c,f?c:(this.freeTargets(),this.fxFailed=!0,null)}uploadR8(e){const n=this.gl,s=n.createTexture();return n.activeTexture(n.TEXTURE5),n.bindTexture(n.TEXTURE_2D,s),n.pixelStorei(n.UNPACK_ALIGNMENT,1),n.texImage2D(n.TEXTURE_2D,0,n.R8,e.width,e.height,0,n.RED,n.UNSIGNED_BYTE,e.data),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_MIN_FILTER,n.NEAREST),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_MAG_FILTER,n.NEAREST),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_WRAP_S,n.CLAMP_TO_EDGE),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_WRAP_T,n.CLAMP_TO_EDGE),s}uploadArray(e,n){const s=this.gl,o=Math.min(32,e.length);if(!o)return null;const r=e[0].width,a=e[0].height,i=s.createTexture();s.activeTexture(s.TEXTURE0+n),s.bindTexture(s.TEXTURE_2D_ARRAY,i),s.pixelStorei(s.UNPACK_ALIGNMENT,1),s.texImage3D(s.TEXTURE_2D_ARRAY,0,s.RGBA,r,a,o,0,s.RGBA,s.UNSIGNED_BYTE,null);for(let l=0;l<o;l++){const c=e[l];c.rgba&&c.width===r&&c.height===a&&s.texSubImage3D(s.TEXTURE_2D_ARRAY,0,0,0,l,r,a,1,s.RGBA,s.UNSIGNED_BYTE,c.rgba)}return s.generateMipmap(s.TEXTURE_2D_ARRAY),s.texParameteri(s.TEXTURE_2D_ARRAY,s.TEXTURE_MIN_FILTER,s.LINEAR_MIPMAP_LINEAR),s.texParameteri(s.TEXTURE_2D_ARRAY,s.TEXTURE_MAG_FILTER,s.LINEAR),s.texParameteri(s.TEXTURE_2D_ARRAY,s.TEXTURE_WRAP_S,s.REPEAT),s.texParameteri(s.TEXTURE_2D_ARRAY,s.TEXTURE_WRAP_T,s.REPEAT),this.aniso&&s.texParameterf(s.TEXTURE_2D_ARRAY,this.aniso.TEXTURE_MAX_ANISOTROPY_EXT,8),i}setTerrainLayers(e){const n=this.gl;for(const o of["layTex","layNrm","layMid"]){const r=this[o];r&&(n.deleteTexture(r),this[o]=null)}if(!e||!e.images.length)return;const s=Math.min(32,e.images.length);this.layTex=this.uploadArray(e.images,6),e.normals?.length===e.images.length&&(this.layNrm=this.uploadArray(e.normals,7)),e.middle?.length===e.images.length&&(this.layMid=this.uploadArray(e.middle,8)),this.layPar.fill(0),this.layMean.fill(.5),this.layMPar.fill(0),this.layMMean.fill(.5);for(let o=0;o<s;o++){this.layPar[o*2]=e.period[o]>0?e.period[o]:4,this.layPar[o*2+1]=e.tint[o]??.5;const r=e.mean[o]??[.5,.5,.5];this.layMean[o*3]=r[0],this.layMean[o*3+1]=r[1],this.layMean[o*3+2]=r[2],this.layMPar[o*2]=e.midPeriod?.[o]??75,this.layMPar[o*2+1]=e.midBlend?.[o]??0;const a=e.midMean?.[o]??[.5,.5,.5];this.layMMean[o*3]=a[0],this.layMMean[o*3+1]=a[1],this.layMMean[o*3+2]=a[2]}this.layFade=e.fade>0?e.fade:150}updateGeometry(){const e=this.gl;e.bindVertexArray(null);for(const n of this.meshes)n.data.dynamic&&(e.bindBuffer(e.ARRAY_BUFFER,n.bPos),e.bufferSubData(e.ARRAY_BUFFER,0,n.data.pos),e.bindBuffer(e.ARRAY_BUFFER,n.bNor),e.bufferSubData(e.ARRAY_BUFFER,0,n.data.nor))}setZUp(e){this.zUp=e,this.buildGrid()}heightRange(){const{min:e,max:n}=this.rawBounds;return this.zUp?[e[2],n[2]]:[e[1],n[1]]}setMeshVisible(e,n){const s=this.meshes[e];s&&(s.visible=n)}isMeshVisible(e){return this.meshes[e]?.visible??!1}gpuIndices(e){return this.meshes[e]?.idx??null}setRangeVisible(e,n,s,o){const r=this.meshes[e];if(!r||s<=0)return;const a=Math.min(n+s,r.idx.length);if(o)for(let i=n;i<a;i++)r.idx[i]=r.data.indices[i];else{const i=r.data.indices[n];r.idx.fill(i,n,a)}this.uploadIndices(r,n,a)}syncSorted(e,n,s,o){const r=e.cells,a=Math.floor(n/3),i=Math.ceil(s/3);if(o&&e.box){const l=e.box,c=e.data.pos,f=e.data.indices;for(let h=a*3;h<i*3;h++){const u=f[h]*3;for(let d=0;d<3;d++){const p=c[u+d];p<l[d]&&(l[d]=p),p>l[3+d]&&(l[3+d]=p)}}r&&Y1(r,c,f,a,i)}if(r){for(let l=a;l<i;l++){const c=r.slot[l]*3;r.sorted[c]=e.idx[l*3],r.sorted[c+1]=e.idx[l*3+1],r.sorted[c+2]=e.idx[l*3+2]}e.sortDirty=!0}}updateRange(e,n,s,o,r,a=!1){const i=this.gl,l=this.meshes[e];if(l){if(i.bindVertexArray(null),i.bindBuffer(i.ARRAY_BUFFER,l.bPos),i.bufferSubData(i.ARRAY_BUFFER,n*12,l.data.pos.subarray(n*3,(n+s)*3)),i.bindBuffer(i.ARRAY_BUFFER,l.bNor),i.bufferSubData(i.ARRAY_BUFFER,n*12,l.data.nor.subarray(n*3,(n+s)*3)),!a){for(let c=o;c<o+r;c++)l.idx[c]=l.data.indices[c];this.uploadIndices(l,o,o+r)}this.syncSorted(l,o,o+r,!0)}}uploadIndices(e,n,s){const o=this.gl,r=e.idx.BYTES_PER_ELEMENT;o.bindVertexArray(e.vao),o.bufferSubData(o.ELEMENT_ARRAY_BUFFER,n*r,e.idx.subarray(n,s)),o.bindVertexArray(null),this.syncSorted(e,n,s,!1),e.vaoW&&(o.deleteVertexArray(e.vaoW),e.vaoW=void 0),e.bLine&&(o.deleteBuffer(e.bLine),e.bLine=void 0)}setHighlight(e){this.highlight=e}computeBounds(){const{min:e,max:n}=this.rawBounds;let s=[(e[0]+n[0])/2,(e[1]+n[1])/2,(e[2]+n[2])/2];this.zUp&&(s=[s[0],s[2],-s[1]]),this.flipOn&&(s[2]=-s[2]),this.bounds={center:s,radius:Math.hypot(n[0]-e[0],n[1]-e[1],n[2]-e[2])/2||1}}buildGrid(){const e=this.gl,{min:n,max:s}=this.rawBounds,o=this.zUp?[n[0],n[2],-s[1]]:n,r=this.zUp?[s[0],s[2],-n[1]]:s,a=Math.max(r[0]-o[0],r[2]-o[2],(r[1]-o[1])*.6)||1,i=[];for(let l=-10;l<=10;l++){const c=l*a/10;i.push(-a,o[1],c,a,o[1],c,c,o[1],-a,c,o[1],a)}e.bindVertexArray(this.gridVao.vao),e.bindBuffer(e.ARRAY_BUFFER,this.gridVao.buf),e.bufferData(e.ARRAY_BUFFER,new Float32Array(i),e.STATIC_DRAW),e.bindVertexArray(null),this.gridN=i.length/3}fit(e){this.computeBounds();const n=this.bounds;e.reset(),e.yaw=this.yaw0,e.target=[...n.center],e.dist=n.radius*2.6}setTexture(e,n){const s=this.gl,o=this.meshes[e];o&&(o.tex&&(s.deleteTexture(o.tex),o.tex=null),n&&(o.tex=this.upload(n)))}setNormalMap(e,n,s="rgb"){const o=this.gl,r=this.meshes[e];r&&(r.norm&&(o.deleteTexture(r.norm),r.norm=null),r.normPack=s,n&&(r.norm=this.upload(n)))}setSurface(e,n){const s=this.gl,o=this.meshes[e];o&&(o.surf&&(s.deleteTexture(o.surf),o.surf=null),n&&(o.surf=this.upload(n)))}upload(e){const n=this.gl,s=n.createTexture();return n.activeTexture(n.TEXTURE0),n.bindTexture(n.TEXTURE_2D,s),n.pixelStorei(n.UNPACK_FLIP_Y_WEBGL,!1),n.pixelStorei(n.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!1),n.pixelStorei(n.UNPACK_ALIGNMENT,1),e.rgba?n.texImage2D(n.TEXTURE_2D,0,n.RGBA,e.width,e.height,0,n.RGBA,n.UNSIGNED_BYTE,e.rgba):e.bitmap&&n.texImage2D(n.TEXTURE_2D,0,n.RGBA,n.RGBA,n.UNSIGNED_BYTE,e.bitmap),n.generateMipmap(n.TEXTURE_2D),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_MIN_FILTER,n.LINEAR_MIPMAP_LINEAR),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_MAG_FILTER,n.LINEAR),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_WRAP_S,n.REPEAT),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_WRAP_T,n.REPEAT),this.aniso&&n.texParameterf(n.TEXTURE_2D,this.aniso.TEXTURE_MAX_ANISOTROPY_EXT,8),s}updatePose(e){const n=this.gl,s=e?e.n:0;if(!e||!s)return;n.activeTexture(n.TEXTURE1),n.bindTexture(n.TEXTURE_2D,this.boneTex),n.pixelStorei(n.UNPACK_ALIGNMENT,4),n.texSubImage2D(n.TEXTURE_2D,0,0,0,4,s,n.RGBA,n.FLOAT,e.skin);const o=[];for(let r=0;r<s;r++){const a=e.parent[r];if(a<0)continue;const i=e.worldV[a],l=e.worldV[r];o.push(i[12],i[13],i[14],l[12],l[13],l[14])}n.bindVertexArray(this.skelVao.vao),n.bindBuffer(n.ARRAY_BUFFER,this.skelVao.buf),n.bufferData(n.ARRAY_BUFFER,new Float32Array(o),n.DYNAMIC_DRAW),n.bindVertexArray(null),this.skelN=o.length/3}draw(e,n){const s=this.gl,o=this.canvas,r=globalThis.devicePixelRatio||1,a=this.theme,i=Math.max(1,Math.floor(o.clientWidth*r)),l=Math.max(1,Math.floor(o.clientHeight*r));(o.width!==i||o.height!==l)&&(o.width=i,o.height=l);const c=this.bounds.radius,f=e.mode==="fly"?Math.max(.01,Math.min(.1,c*.001)):Math.max(c*.01,e.dist*.01),h=e.mode==="fly"?c*4+100:c*100+e.dist*2,u=X1(e.mode==="fly"?1:.8,i/l,f,h),d=this.zUp?hs(this.flip,uf):this.flip,p=hs(e.view(),this.flip),m=this.zUp?hs(p,uf):p;s.enable(s.DEPTH_TEST),s.useProgram(this.p),s.uniformMatrix4fv(this.U.proj,!1,u),s.uniformMatrix4fv(this.U.model,!1,d),this.lastPVM=hs(u,m);const g=n.drawDistance&&n.drawDistance>0?n.drawDistance:1/0,w=_=>{const R=hf(_);return R?[R[12]/R[15],R[13]/R[15],R[14]/R[15]]:null},y=this.lastPVM,M=w(m),x=this.drawStats={tris:0,calls:0,cells:0},v=[];s.uniform1f(this.U.clipY,n.clipY??1e30),s.uniform1i(this.U.bones,1),s.uniform1i(this.U.tex,0),s.uniform1i(this.U.norm,2),s.uniform1i(this.U.surf,3),s.uniform1f(this.U.normY,this.normalY*(n.normalFlipY?-1:1)),s.uniform1i(this.U.view,typeof n.normalView=="number"?n.normalView:n.normalView?1:0),s.uniform1f(this.U.ni,n.normalStrength??1),s.uniform1i(this.U.skinned,this.boneCount>0?1:0),s.uniform1i(this.U.lm,4),s.uniform1f(this.U.lmK,this.lmScale*(n.lightmapScale??1)),s.uniform1i(this.U.lmRgb,this.lmRgb?1:0),s.activeTexture(s.TEXTURE4),s.bindTexture(s.TEXTURE_2D,this.lmTex),s.uniform1i(this.U.lmB,9),s.activeTexture(s.TEXTURE9),s.bindTexture(s.TEXTURE_2D_ARRAY,this.lmBTex),s.uniform1i(this.U.tIdx,5),s.uniform1i(this.U.tLay,6),s.uniform2fv(this.U.tPar,this.layPar),s.uniform3fv(this.U.tMean,this.layMean),s.uniform1f(this.U.tFade,this.layFade),s.activeTexture(s.TEXTURE6),s.bindTexture(s.TEXTURE_2D_ARRAY,this.layTex),s.uniform1i(this.U.tNrm,7),s.uniform1i(this.U.tMid,8),s.uniform1i(this.U.tHasN,this.layNrm&&n.normals!==!1?1:0),s.uniform1i(this.U.tHasM,this.layMid?1:0),s.uniform2fv(this.U.tMPar,this.layMPar),s.uniform3fv(this.U.tMMean,this.layMMean),s.activeTexture(s.TEXTURE7),s.bindTexture(s.TEXTURE_2D_ARRAY,this.layNrm),s.activeTexture(s.TEXTURE8),s.bindTexture(s.TEXTURE_2D_ARRAY,this.layMid),s.activeTexture(s.TEXTURE1),s.bindTexture(s.TEXTURE_2D,this.boneTex),s.uniform1i(this.U.refl,10),s.uniform1i(this.U.refr,11),s.uniform1i(this.U.depth,12),s.uniform1i(this.U.fx,0),s.uniform1i(this.U.clipOn,0),s.uniform1i(this.U.flat,0),s.uniform1f(this.U.flatAlpha,1);const b=(_,R,L)=>{s.activeTexture(s.TEXTURE0);const O=n.tex&&_.tex;s.bindTexture(s.TEXTURE_2D,O?_.tex:null),s.uniform1i(this.U.hasTex,O?1:0),s.uniform3fv(this.U.base,_.data.color??a.base);const N=n.normals!==!1&&_.norm;s.activeTexture(s.TEXTURE2),s.bindTexture(s.TEXTURE_2D,N?_.norm:null),s.uniform1i(this.U.hasNorm,N?1:0),s.uniform1i(this.U.normPack,_.normPack==="rg"?1:_.normPack==="ag"?2:0);const D=n.pbr!==!1&&_.surf;s.activeTexture(s.TEXTURE3),s.bindTexture(s.TEXTURE_2D,D?_.surf:null),s.uniform1i(this.U.hasSurf,D?1:0),s.uniform1i(this.U.alpha,n.alpha||_.data.alphaTest?1:0);const F=!!(this.lmTex&&_.data.uv2&&n.lightmap!==!1);s.uniform1i(this.U.hasLm,F?1:0),s.uniform1i(this.U.hasLmB,F&&this.lmBTex&&n.bumpLightmap!==!1?1:0);const C=!!(O&&_.tIdx&&this.layTex&&n.terrainLayers!==!1);if(s.uniform1i(this.U.terr,C?1:0),s.activeTexture(s.TEXTURE5),s.bindTexture(s.TEXTURE_2D,C?_.tIdx:null),_.cells&&_.vaoS){s.bindVertexArray(_.vaoS),_.sortDirty&&(s.bufferSubData(s.ELEMENT_ARRAY_BUFFER,0,_.cells.sorted),_.sortDirty=!1),Z1(_.cells,R,L,g,v);const P=_.idx.BYTES_PER_ELEMENT;for(let I=0;I<v.length;I+=2)s.drawElements(s.TRIANGLES,v[I+1],_.itype,v[I]*P),x.tris+=v[I+1]/3,x.calls++;x.cells+=_.cells.start.length;return}s.bindVertexArray(_.vao),s.drawElements(s.TRIANGLES,_.count,_.itype,0),x.tris+=_.count/3,x.calls++},k=(_,R,L)=>_.visible&&(!_.box||pf(_.box,0,R,L,g)),E=n.screenFx!==!1,A=this.meshes.filter(_=>_.fx&&k(_,y,M)),S=E&&A.length?this.targets(i,l):null,$=(_,R,L,O)=>{s.uniformMatrix4fv(this.U.mv,!1,_),s.enable(s.POLYGON_OFFSET_FILL),s.polygonOffset(1,1);for(const D of this.meshes)!D.bA&&!D.fx&&k(D,R,L)&&b(D,R,L);s.disable(s.POLYGON_OFFSET_FILL);const N=this.meshes.filter(D=>(D.bA||O&&D.fx)&&k(D,R,L));if(N.length){s.enable(s.BLEND),s.blendFunc(s.SRC_ALPHA,s.ONE_MINUS_SRC_ALPHA),s.depthFunc(s.LEQUAL),s.depthMask(!1);for(const D of N)D.fx&&s.vertexAttrib4f(7,D.fx.fallback.opacity,0,0,0),b(D,R,L),D.fx&&s.vertexAttrib4f(7,1,0,0,0);s.depthMask(!0),s.depthFunc(s.LESS),s.disable(s.BLEND)}},T=()=>{s.clearColor(a.background[0],a.background[1],a.background[2],1),s.clear(s.COLOR_BUFFER_BIT|s.DEPTH_BUFFER_BIT)};this.fxStats={meshes:S?A.length:0,reflection:!1};let U=null;if(S&&M){let _=1/0;for(const R of A){const L=R.fx.plane;if(R.fx.kind!=="water"||!L)continue;const O=Math.abs(L[0]*M[0]+L[1]*M[1]+L[2]*M[2]-L[3]);O<_&&(_=O,U=L)}}if(S&&U&&M){const[_,R,L,O]=U,N=new Float32Array([1-2*_*_,-2*_*R,-2*_*L,0,-2*R*_,1-2*R*R,-2*R*L,0,-2*L*_,-2*L*R,1-2*L*L,0,2*O*_,2*O*R,2*O*L,1]),D=hs(m,N),F=hs(u,D),C=w(D),P=_*M[0]+R*M[1]+L*M[2]-O>=0?1:-1;s.bindFramebuffer(s.FRAMEBUFFER,S.fbR),s.viewport(0,0,S.rw,S.rh),T(),s.uniform1i(this.U.clipOn,1),s.uniform4f(this.U.clipPl,_*P,R*P,L*P,-O*P),s.uniform1f(this.U.mirror,this.flipOn?1:-1);const I={...x};$(D,F,C,!1),Object.assign(x,I),s.uniform1i(this.U.clipOn,0),this.fxStats.reflection=!0}if(s.uniform1f(this.U.mirror,this.flipOn?-1:1),s.bindFramebuffer(s.FRAMEBUFFER,S?S.fbA:null),s.viewport(0,0,i,l),T(),$(m,y,M,!S),S){s.bindFramebuffer(s.READ_FRAMEBUFFER,S.fbA),s.bindFramebuffer(s.DRAW_FRAMEBUFFER,S.fbB),s.blitFramebuffer(0,0,i,l,0,0,i,l,s.COLOR_BUFFER_BIT|s.DEPTH_BUFFER_BIT,s.NEAREST),s.bindFramebuffer(s.FRAMEBUFFER,S.fbA),s.activeTexture(s.TEXTURE10),s.bindTexture(s.TEXTURE_2D,this.fxStats.reflection?S.texRefl:null),s.activeTexture(s.TEXTURE11),s.bindTexture(s.TEXTURE_2D,S.texColor),s.activeTexture(s.TEXTURE12),s.bindTexture(s.TEXTURE_2D,S.texDepth),s.uniform2f(this.U.screen,i,l),s.uniform2f(this.U.nf,f,h),s.uniform1f(this.U.time,performance.now()/1e3%3600),s.uniformMatrix4fv(this.U.mv,!1,m),s.depthFunc(s.LEQUAL);for(const _ of A){const R=_.fx,L=R.plane,O=!!(U&&L&&L.every((N,D)=>Math.abs(N-U[D])<(D===3?1:.01)));s.uniform1i(this.U.fx,R.kind==="water"?1:2),s.uniform1i(this.U.fxRefl,O?1:0),s.uniform3fv(this.U.fxRT,R.refractTint),s.uniform3fv(this.U.fxLT,R.reflectTint??(R.kind==="glass"?[0,0,0]:[1,1,1])),s.uniform4f(this.U.fxK,R.refract,R.reflect??0,R.fog?.start??0,R.fog?.end??1),s.uniform1i(this.U.fxFog,R.fog?1:0),s.uniform3fv(this.U.fogC,R.fog?.color??[0,0,0]),s.uniform2fv(this.U.fxScroll,R.scroll??[0,0]),b(_,y,M)}s.uniform1i(this.U.fx,0),s.depthFunc(s.LESS);for(const _ of[10,11,12])s.activeTexture(s.TEXTURE0+_),s.bindTexture(s.TEXTURE_2D,null)}if(n.wire){s.uniform1i(this.U.flat,1),s.uniform3f(this.U.flatColor,a.wire[0],a.wire[1],a.wire[2]);for(const _ of this.meshes)if(_.visible){if(!_.vaoW){const R=_.idx,L=R instanceof Uint32Array?new Uint32Array(R.length*2):new Uint16Array(R.length*2);for(let O=0;O+2<R.length;O+=3)L.set([R[O],R[O+1],R[O+1],R[O+2],R[O+2],R[O]],O*2);_.vaoW=s.createVertexArray(),s.bindVertexArray(_.vaoW),this.attribs(_),_.bLine=this.buf(s.ELEMENT_ARRAY_BUFFER,L),_.lineCount=L.length}s.bindVertexArray(_.vaoW),s.drawElements(s.LINES,_.lineCount,_.itype,0)}}if(this.highlight.length){s.uniform1i(this.U.flat,1),s.uniform3f(this.U.flatColor,1,.62,.15),s.uniform1f(this.U.flatAlpha,.45),s.enable(s.BLEND),s.blendFunc(s.SRC_ALPHA,s.ONE_MINUS_SRC_ALPHA),s.depthFunc(s.LEQUAL);for(const _ of this.highlight){const R=this.meshes[_.mesh];R?.visible&&(s.bindVertexArray(R.vao),s.drawElements(s.TRIANGLES,_.iCount,R.itype,_.iStart*R.idx.BYTES_PER_ELEMENT))}s.depthFunc(s.LESS),s.disable(s.BLEND)}s.uniform1f(this.U.flatAlpha,1),s.useProgram(this.lp),s.uniformMatrix4fv(this.LU.proj,!1,u),s.uniformMatrix4fv(this.LU.mv,!1,p),n.grid&&this.gridN&&(s.uniform4fv(this.LU.color,a.grid),s.bindVertexArray(this.gridVao.vao),s.drawArrays(s.LINES,0,this.gridN)),n.skeleton&&this.skelN&&(s.uniformMatrix4fv(this.LU.mv,!1,m),s.disable(s.DEPTH_TEST),s.uniform4fv(this.LU.color,a.skeleton),s.bindVertexArray(this.skelVao.vao),s.drawArrays(s.LINES,0,this.skelN)),S&&(s.bindFramebuffer(s.READ_FRAMEBUFFER,S.fbA),s.bindFramebuffer(s.DRAW_FRAMEBUFFER,S.fbB),s.blitFramebuffer(0,0,i,l,0,0,i,l,s.COLOR_BUFFER_BIT,s.NEAREST),s.bindFramebuffer(s.FRAMEBUFFER,null),s.disable(s.DEPTH_TEST),s.useProgram(this.qp),s.activeTexture(s.TEXTURE11),s.bindTexture(s.TEXTURE_2D,S.texColor),s.uniform1i(s.getUniformLocation(this.qp,"uT"),11),s.bindVertexArray(this.qVao),s.drawArrays(s.TRIANGLES,0,3),s.bindTexture(s.TEXTURE_2D,null),s.enable(s.DEPTH_TEST)),s.bindVertexArray(null)}}class gf{yaw=Math.PI;pitch=.15;dist=3;target=[0,1,0];mode="orbit";speed=5;constructor(){this.reset()}reset(){this.yaw=Math.PI,this.pitch=.15,this.dist=3,this.target=[0,1,0]}back(){const e=Math.cos(this.pitch);return[e*Math.sin(this.yaw),Math.sin(this.pitch),e*Math.cos(this.yaw)]}eye(){const e=this.back();return[this.target[0]+this.dist*e[0],this.target[1]+this.dist*e[1],this.target[2]+this.dist*e[2]]}view(){return H1(this.eye(),this.target,[0,1,0])}rotate(e,n){if(this.mode==="fly"){const s=this.eye();this.yaw-=e*.005,this.pitch=Math.max(-1.55,Math.min(1.55,this.pitch+n*.005));const o=this.back();this.target=[s[0]-this.dist*o[0],s[1]-this.dist*o[1],s[2]-this.dist*o[2]];return}this.yaw-=e*.01,this.pitch=Math.max(-1.5,Math.min(1.5,this.pitch+n*.01))}zoom(e){if(this.mode==="fly"){this.speed=Math.max(.05,Math.min(1e4,this.speed/e));return}this.dist=Math.max(.01,this.dist*e)}pan(e,n){const s=this.eye(),o=[this.target[0]-s[0],this.target[1]-s[1],this.target[2]-s[2]],r=Math.hypot(...o);for(let f=0;f<3;f++)o[f]/=r;let a=[-o[2],0,o[0]];const i=Math.hypot(...a)||1;a=a.map(f=>f/i);const l=[a[1]*o[2]-a[2]*o[1],a[2]*o[0]-a[0]*o[2],a[0]*o[1]-a[1]*o[0]],c=this.mode==="fly"?this.speed*.004:this.dist*.0015;for(let f=0;f<3;f++)this.target[f]+=(-a[f]*e+l[f]*n)*c}move(e,n,s,o){const r=this.back(),a=this.speed*o,i=[-r[0],-r[1],-r[2]],l=[Math.cos(this.yaw),0,-Math.sin(this.yaw)];for(let c=0;c<3;c++)this.target[c]+=(i[c]*e+l[c]*n)*a;this.target[1]+=s*a}setMode(e,n=10){if(e===this.mode)return;const s=this.eye();e==="fly"?(this.dist=.5,this.speed=Math.max(.5,Math.min(50,n/20))):this.dist=Math.max(1,n*.5);const o=this.back();this.target=[s[0]-this.dist*o[0],s[1]-this.dist*o[1],s[2]-this.dist*o[2]],this.mode=e}}class wf{constructor(e,n={}){this.canvas=e,this.opts=n,this.renderer=new mf(e),this.renderer.theme=n.theme??pa,n.controls!==!1&&this.attachControls()}canvas;opts;renderer;camera=new gf;options={tex:!0,wire:!1,skeleton:!1,grid:!0,alpha:!1,normals:!0,normalFlipY:!1,normalView:!1,pbr:!0};state={motion:null,time:0,playing:!0,speed:1,loop:!0,invert:!1};model=null;tool="camera";raf=0;last=0;animKey="";animMotion=null;cleanup=[];keys=new Set;setTheme(e){this.renderer.theme=e}setModel(e){this.model=e,this.state.motion=null,this.state.time=0,this.state.invert=!1,this.animKey="",this.animMotion=null,this.renderer.setModel(e),this.renderer.setZUp(!!e.defaultZUp),this.fit(),e.skeleton&&(e.skeleton.pose(null,0,!1,!1),this.renderer.updatePose(e.skeleton))}setTexture(e,n){this.renderer.setTexture(e,n)}setSurface(e,n){this.renderer.setSurface(e,n)}setNormalMap(e,n,s="rgb"){this.renderer.setNormalMap(e,n,s)}setZUp(e){this.renderer.setZUp(e),this.fit()}setCameraMode(e){this.camera.setMode(e,this.renderer.bounds.radius),this.keys.clear()}get cameraMode(){return this.camera.mode}rayAt(e,n){const s=this.renderer.lastPVM,o=s&&hf(s);if(!o)return null;const r=this.canvas.getBoundingClientRect(),a=(e-r.left)/r.width*2-1,i=1-(n-r.top)/r.height*2,l=d=>{const p=[0,1,2,3].map(m=>o[m]*a+o[4+m]*i+o[8+m]*d+o[12+m]);return[p[0]/p[3],p[1]/p[3],p[2]/p[3]]},c=l(-1),f=l(1),h=[f[0]-c[0],f[1]-c[1],f[2]-c[2]],u=Math.hypot(h[0],h[1],h[2])||1;return{origin:c,dir:h.map(d=>d/u)}}pickAt(e,n){const s=this.model,o=this.rayAt(e,n);if(!s||!o)return null;const r=this.options.clipY,a=this.renderer.zUpOn;return Xl(s,o.origin,o.dir,{skip:i=>!this.renderer.isMeshVisible(i),indices:i=>this.renderer.gpuIndices(i)??s.meshes[i].indices,clip:r===void 0?void 0:(i,l,c)=>(a?c:l)>r})}focusBox(e,n){const s=this.renderer.toView((e[0]+n[0])/2,(e[1]+n[1])/2,(e[2]+n[2])/2),o=Math.max(.25,Math.hypot(n[0]-e[0],n[1]-e[1],n[2]-e[2])/2),r=this.camera;if(r.mode==="orbit"){r.target=s,r.dist=o*2.6;return}const a=r.eye(),i=[a[0]-r.target[0],a[1]-r.target[1],a[2]-r.target[2]],l=Math.hypot(i[0],i[1],i[2])||1,c=o*2.6-r.dist;r.target=[s[0]+i[0]/l*c,s[1]+i[1]/l*c,s[2]+i[2]/l*c]}heightRange(){return this.renderer.heightRange()}fit(){const e=this.camera.mode;this.camera.mode="orbit",this.renderer.fit(this.camera),e==="fly"&&this.camera.setMode("fly",this.renderer.bounds.radius)}aimGrid(){const e=this.state.motion;return e&&e.kind==="node"?e.blend??e.layers?.find(n=>n.grid)?.grid??null:null}setMotion(e){this.state.motion=e,this.state.time=0,this.state.playing=!0,e&&this.model?.detectInvert&&(this.state.invert=this.model.detectInvert(e))}seek(e){const n=this.state.motion;n&&(this.state.time=Math.max(0,Math.min(1,e))*n.duration,this.state.playing=!1)}start(){if(this.raf)return;this.last=performance.now();const e=n=>{this.frame(n),this.raf=requestAnimationFrame(e)};this.raf=requestAnimationFrame(e)}stop(){this.raf&&cancelAnimationFrame(this.raf),this.raf=0}frame(e){const n=this.state,s=Math.min(.1,(e-this.last)/1e3);this.last=e;const o=n.motion,r=this.model,a=r?.skeleton??null;if(o&&n.playing&&(n.time+=s*n.speed,n.loop?n.time=o.duration>0?n.time%o.duration:0:n.time>=o.duration&&(n.time=o.duration,n.playing=!1,this.opts.onEnded?.())),r?.animate){const i=n.time.toFixed(4);(i!==this.animKey||o!==this.animMotion)&&(this.animKey=i,this.animMotion=o,r.animate(o,n.time,n.loop),this.renderer.updateGeometry())}if(a&&(a.pose(o,n.time,n.loop,n.invert),this.renderer.updatePose(a)),this.camera.mode==="fly"&&this.keys.size){const i=(...c)=>c.some(f=>this.keys.has(f))?1:0,l=this.keys.has("ShiftLeft")||this.keys.has("ShiftRight")?4:this.keys.has("AltLeft")||this.keys.has("AltRight")?.25:1;this.camera.move(i("KeyW","ArrowUp")-i("KeyS","ArrowDown"),i("KeyD","ArrowRight")-i("KeyA","ArrowLeft"),i("KeyE","Space","PageUp")-i("KeyQ","KeyC","PageDown"),s*l)}this.opts.onFrame?.(n),this.renderer.draw(this.camera,this.options)}attachControls(){const e=this.canvas;let n=null,s=null;const o=(c,f,h)=>{e.addEventListener(c,f,h),this.cleanup.push(()=>e.removeEventListener(c,f,h))};o("pointerdown",c=>{n={x:c.clientX,y:c.clientY},s={x:c.clientX,y:c.clientY,moved:!1},e.setPointerCapture(c.pointerId)}),o("pointerup",c=>{s&&!s.moved&&c.button===0&&this.tool!=="camera"&&this.opts.onPick?.(this.pickAt(c.clientX,c.clientY),c),n=null,s=null}),o("pointermove",c=>{if(!n)return;const f=c.clientX-n.x,h=c.clientY-n.y;if(n={x:c.clientX,y:c.clientY},s&&Math.hypot(c.clientX-s.x,c.clientY-s.y)>4&&(s.moved=!0),!s?.moved)return;const u=(c.buttons&1)!==0&&!c.shiftKey,d=u&&c.altKey?this.aimGrid():null;if(d){d.x=Math.max(0,Math.min(d.cols-1,d.x-f*.008)),d.y=Math.max(0,Math.min(d.rows-1,d.y-h*.008)),this.opts.onAim?.(d);return}u&&(this.tool==="rotate"||this.tool==="move")&&this.opts.onToolDrag?.(f,h,c)||(c.buttons&2||c.shiftKey||c.buttons&4?this.camera.pan(f,h):this.camera.rotate(f,h))}),o("wheel",c=>{c.preventDefault(),this.camera.zoom(Math.exp(c.deltaY*.001))},{passive:!1}),o("contextmenu",c=>c.preventDefault());const r=c=>{const f=c.target;return!!f&&(f.tagName==="INPUT"&&!["checkbox","range","button"].includes(f.type)||f.tagName==="TEXTAREA"||f.tagName==="SELECT"||f.isContentEditable)},a=c=>{this.camera.mode!=="fly"||r(c)||(this.keys.add(c.code),/^(Key[WASDQEC]|Space|Arrow|Page)/.test(c.code)&&c.preventDefault())},i=c=>{this.keys.delete(c.code)},l=()=>this.keys.clear();addEventListener("keydown",a),addEventListener("keyup",i),addEventListener("blur",l),this.cleanup.push(()=>{removeEventListener("keydown",a),removeEventListener("keyup",i),removeEventListener("blur",l)})}dispose(){this.stop();for(const e of this.cleanup.splice(0))e();this.renderer.dispose()}}function o2(t,e={}){const n=new wf(t,e),s=new Bl(tc(cf),e.preset),o=async()=>{const r=s.model;r&&await Promise.all(r.meshes.map(async(a,i)=>{const l=await s.resolveTexture(i);l.image&&n.setTexture(i,l.image)}))};return s.on("model",({model:r})=>{n.setModel(r),o()}),s.on("textures",()=>{o()}),n.start(),{viewer:n,session:s,applyTextures:o}}const ma=t=>t.toLowerCase().replace(/\.[a-z0-9]+$/,"").replace(/[^a-z0-9_]+/g,"_").replace(/^_+|_+$/g,"")||"tex";async function r2(t,e,n){const s=ma(String(e.name||n.baseName)),o=+(e.scale??1)||1,r=new Map,a=new TextEncoder,i=t.skeleton;i&&i.pose(null,0,!1);const l=[`# gmv: ${n.preset.title}`,`mtllib ${s}.mtl`],c=[],f=new Map;let h=1,u=0,d=0,p=0,m=0;for(let g=0;g<t.meshes.length;g++){if(n.hidden.has(g))continue;const w=t.meshes[g],y=qr(t,g);if(y.length<3)continue;const M=w.texture||`mesh_${g}`;let x=f.get(M);if(!x){x=`${ma(M.split(/[\\/]/).pop()??M)}_${f.size}`,f.set(M,x),c.push(`newmtl ${x}`,"Kd 1 1 1");const b=await n.texture(g).catch(()=>null),k=`textures/${x}.dds`;b?.dds&&b.dds.length>128?(r.set(k,b.dds),p++,c.push(`map_Kd ${k}`)):b?.image?.rgba&&(r.set(k,sa({width:b.image.width,height:b.image.height,rgba:b.image.rgba})),m++,c.push(`map_Kd ${k}`)),c.push("")}l.push(`o ${ma(w.name||"mesh")}_${g}`,`usemtl ${x}`);const v=w.vertexCount;for(let b=0;b<v;b++){let k=w.pos[b*3],E=w.pos[b*3+1],A=w.pos[b*3+2],S=w.nor[b*3],$=w.nor[b*3+1],T=w.nor[b*3+2];if(i&&w.skinned){let _=0,R=0,L=0,O=0,N=0,D=0,F=0;for(let C=0;C<4;C++){const P=w.weight[b*4+C];if(!P)continue;const I=i.skinV[w.bone[b*4+C]];F+=P,_+=P*(k*I[0]+E*I[4]+A*I[8]+I[12]),R+=P*(k*I[1]+E*I[5]+A*I[9]+I[13]),L+=P*(k*I[2]+E*I[6]+A*I[10]+I[14]),O+=P*(S*I[0]+$*I[4]+T*I[8]),N+=P*(S*I[1]+$*I[5]+T*I[9]),D+=P*(S*I[2]+$*I[6]+T*I[10])}F&&(k=_,E=R,A=L,S=O,$=N,T=D)}t.defaultZUp&&([E,A]=[A,-E],[$,T]=[T,-$]),t.flipZ&&(A=-A,T=-T),l.push(`v ${(k*o).toFixed(5)} ${(E*o).toFixed(5)} ${(A*o).toFixed(5)}`),l.push(`vt ${w.uv[b*2].toFixed(5)} ${(1-w.uv[b*2+1]).toFixed(5)}`);const U=Math.hypot(S,$,T)||1;l.push(`vn ${(S/U).toFixed(4)} ${($/U).toFixed(4)} ${(T/U).toFixed(4)}`)}for(let b=0;b+2<y.length;b+=3){const k=[y[b],t.flipZ?y[b+2]:y[b+1],t.flipZ?y[b+1]:y[b+2]].map(E=>E+h);l.push(`f ${k.map(E=>`${E}/${E}/${E}`).join(" ")}`)}h+=v,u+=y.length/3,d++}return r.set(`${s}.obj`,a.encode(l.join(`
|
|
185
|
+
`)+`
|
|
186
|
+
`)),r.set(`${s}.mtl`,a.encode(c.join(`
|
|
187
|
+
`))),{files:r,name:`${s}_obj`,report:[`OBJ: ${d} мешей, ${u} треугольников, ${f.size} материалов`,`Текстуры: ${p} DDS скопировано, ${m} перекодировано в DDS без сжатия`,...i?["Скелетная модель выгружена в bind-позе (кости и анимации OBJ не хранит)."]:[],...t.flipZ?["Координаты переведены в правостороннюю систему (Z отражён, обход граней развёрнут)."]:[]]}}const a2=Object.freeze(Object.defineProperty({__proto__:null,exportObj:r2},Symbol.toStringTag,{value:"Module"})),ds=4096,yf=60,bf=2,Gs=256-bf+yf,te=Gs*2-1,Vo=te-1,i2=16384,vf=new Uint8Array(256),xf=new Uint8Array(256);{let t=0,e=0;[[3,1],[4,3],[5,8],[6,12],[7,24],[8,16]].forEach(([n,s])=>{for(let o=0;o<s;o++,e++)for(let r=0;r<1<<8-n;r++)vf[t]=e,xf[t]=n,t++})}function Mn(t,e){let n=0;const s=e??(t[0]|t[1]<<8|t[2]<<16|t[3]<<24)>>>0;if(e===void 0&&(n=4),s>Math.max(65536,t.length*64))throw new Error(`LZHUF: неправдоподобный размер ${s} (данные зашифрованы или повреждены)`);const o=new Uint8Array(s),r=new Uint32Array(te+1),a=new Int32Array(te+Gs),i=new Int32Array(te);for(let x=0;x<Gs;x++)r[x]=1,i[x]=x+te,a[x+te]=x;for(let x=0,v=Gs;v<=Vo;x+=2,v++)r[v]=r[x]+r[x+1],i[v]=x,a[x]=a[x+1]=v;r[te]=65535,a[Vo]=0;let l=0,c=0;const f=()=>{for(;c<=8;)l=(l|(n<t.length?t[n++]:0)<<8-c)&65535,c+=8},h=()=>{f();const x=l;return l=l<<1&65535,c--,x>>15&1},u=()=>{f();const x=l;return l=l<<8&65535,c-=8,x>>8&255},d=()=>{let x=0;for(let v=0;v<te;v++)i[v]>=te&&(r[x]=r[v]+1>>1,i[x]=i[v],x++);for(let v=0,b=Gs;b<te;v+=2,b++){const k=r[b]=r[v]+r[v+1];let E=b-1;for(;k<r[E];)E--;E++,r.copyWithin(E+1,E,b),r[E]=k,i.copyWithin(E+1,E,b),i[E]=v}for(let v=0;v<te;v++){const b=i[v];b>=te?a[b]=v:a[b]=a[b+1]=v}},p=x=>{r[Vo]===i2&&d();let v=a[x+te];do{const b=++r[v];let k=v+1;if(b>r[k]){for(;b>r[++k];);k--,r[v]=r[k],r[k]=b;const E=i[v];a[E]=k,E<te&&(a[E+1]=k);const A=i[k];i[k]=E,a[A]=v,A<te&&(a[A+1]=v),i[v]=A,v=k}}while((v=a[v])!==0)},m=()=>{let x=i[Vo];for(;x<te;)x=i[x+h()];return x-=te,p(x),x},g=()=>{let x=u();const v=vf[x]<<6;let b=xf[x]-2;for(;b--;)x=(x<<1)+h();return v|x&63},w=new Uint8Array(ds).fill(32);let y=ds-yf,M=0;for(;M<s;){const x=m();if(x<256)o[M++]=x,w[y]=x,y=y+1&ds-1;else{const v=y-g()-1&ds-1,b=x-255+bf;for(let k=0;k<b&&M<s;k++){const E=w[v+k&ds-1];o[M++]=E,w[y]=E,y=y+1&ds-1}}}return o}const be={HEADER:1,TEXTURE:2,VERTICES:3,INDICES:4,CHILDREN:9,S_BONE_NAMES:13,S_MOTIONS:14,S_SMPARAMS:15,S_IKDATA:16,S_USERDATA:17,S_DESC:18,S_MOTION_REFS0:19,S_MOTION_REFS1:24},Mf=["NORMAL","HIERRARHY","PROGRESSIVE","SKELETON_ANIM","SKELETON_GEOMDEF_PM","SKELETON_GEOMDEF_ST","LOD","TREE_ST","PARTICLE_EFFECT","PARTICLE_GROUP","SKELETON_RIGID","TREE_PM"],ga=30;class sn{constructor(e,n,s){this.pos=n,this.end=s,this.dv=new DataView(e),this.u8=new Uint8Array(e)}pos;end;dv;u8;byte(){return this.dv.getUint8(this.pos++)}u16(){const e=this.dv.getUint16(this.pos,!0);return this.pos+=2,e}u32(){const e=this.dv.getUint32(this.pos,!0);return this.pos+=4,e}f32(){const e=this.dv.getFloat32(this.pos,!0);return this.pos+=4,e}skip(e){this.pos+=e}get eof(){return this.pos>=this.end}stringz(){let e="";for(;this.pos<this.end;){const n=this.u8[this.pos++];if(!n)break;e+=String.fromCharCode(n)}return e}}function Xs(t,e,n){const s=new DataView(t),o=[];let r=e;for(;r+8<=n;){const a=s.getUint32(r,!0),i=s.getUint32(r+4,!0);if(r+8+i>n)break;o.push({id:a&2147483647,comp:(a&2147483648)!==0,offset:r+8,size:i}),r+=8+i}return o}function l2(t,e){const n=new sn(t,e.offset,e.offset+e.size),s=n.byte(),o=n.byte(),r=n.u16(),a=[n.f32(),n.f32(),n.f32()],i=[n.f32(),n.f32(),n.f32()];return{version:s,type:o,shader:r,bmin:a,bmax:i}}function c2(t,e,n,s,o){const r=new DataView(t),a=r.getUint32(e.offset,!0),i=r.getUint32(e.offset+4,!0),l=e.offset+8,c=i?Math.floor((e.size-8)/i):0;if(![32,60,64,70,72,76].includes(c))throw new Error(`Неподдерживаемый формат вершин 0x${a.toString(16)} (stride ${c})`);const f=new Float32Array(i*3),h=new Float32Array(i*3),u=new Float32Array(i*2),d=new Float32Array(i*4),p=new Float32Array(i*4),m=b=>r.getFloat32(b,!0),g=b=>r.getUint16(b,!0),w=b=>Math.min(b,Math.max(0,o-1));for(let b=0;b<i;b++){const k=l+b*c;let E,A,S,$=[0,0,0,0],T=[1,0,0,0];if(c===32)E=k,A=k+12,S=k+24;else if(c===60)E=k,A=k+12,S=k+48,$[0]=r.getUint32(k+56,!0);else if(c===64){$[0]=g(k),$[1]=g(k+2),E=k+4,A=k+16,S=k+56;const U=m(k+52);T=[1-U,U,0,0]}else if(c===70||c===72){$=[g(k),g(k+2),g(k+4),0],E=k+6,A=k+18,S=k+62;const U=m(k+54),_=m(k+58);T=[U,_,1-U-_,0]}else{$=[g(k),g(k+2),g(k+4),g(k+6)],E=k+8,A=k+20,S=k+68;const U=m(k+56),_=m(k+60),R=m(k+64);T=[U,_,R,1-U-_-R]}for(let U=0;U<3;U++)f[b*3+U]=m(E+4*U),h[b*3+U]=m(A+4*U);u[b*2]=m(S),u[b*2+1]=m(S+4);for(let U=0;U<4;U++)d[b*4+U]=w($[U]),p[b*4+U]=T[U]}const y=r.getUint32(n.offset,!0),M=new Uint16Array(y);for(let b=0;b<y;b++)M[b]=r.getUint16(n.offset+4+b*2,!0);let x="",v="";if(s){const b=new sn(t,s.offset,s.offset+s.size);x=b.stringz(),v=b.stringz()}return{texture:x,shader:v,vertexCount:i,pos:f,nor:h,uv:u,bone:d,weight:p,indices:M,skinned:c!==32,stride:c}}function kf(t,e){if(!e.some(a=>a.comp))return{buf:t,chunks:e};const n=e.map(a=>{const i=new Uint8Array(t,a.offset,a.size);return a.comp?Mn(i):i}),s=new Uint8Array(n.reduce((a,i)=>a+i.length,0)),o=[];let r=0;return n.forEach((a,i)=>{s.set(a,r),o.push({id:e[i].id,comp:!1,offset:r,size:a.length}),r+=a.length}),{buf:s.buffer,chunks:o}}function Ef(t,e,n,s){const{buf:o,chunks:r}=kf(t,e),a=f=>r.find(h=>h.id===f),i=a(be.VERTICES),l=a(be.INDICES);i&&l&&n.push(c2(o,i,l,a(be.TEXTURE),s));const c=a(be.CHILDREN);if(c)for(const f of Xs(o,c.offset,c.offset+c.size))Ef(o,Xs(o,f.offset,f.offset+f.size),n,s)}function f2(t,e){const n=r=>{const a=new sn(t,e.offset,e.offset+e.size),i=r?a.u32():1/0,l=[];for(;!a.eof&&l.length<i;){const c=a.stringz(),f=a.stringz();a.skip(60),l.push({name:c,parentName:f})}return a.pos!==a.end||r&&l.length!==i?null:l},s=n(!0)??n(!1);if(!s)throw new Error("Не удалось разобрать S_BONE_NAMES");const o=new Map(s.map((r,a)=>[r.name.toLowerCase(),a]));return s.map(r=>({name:r.name,parent:r.parentName?o.get(r.parentName.toLowerCase())??-1:-1,rot:[0,0,0],pos:[0,0,0]}))}function u2(t,e,n){const s=r=>{const a=new sn(t,e.offset,e.offset+e.size),i=[];for(let l=0;l<n.length;l++){if(a.pos+4>a.end||(a.u32(),a.stringz(),a.skip(184+(r?4:0)),a.pos+24>a.end))return null;const c=[a.f32(),a.f32(),a.f32()],f=[a.f32(),a.f32(),a.f32()];a.skip(16),i.push({rot:c,pos:f})}return a.pos===a.end?i:null},o=s(!0)??s(!1);return o&&o.forEach((r,a)=>{n[a].rot=r.rot,n[a].pos=r.pos}),!!o}function wa(t,e,n,s,o){const r=new DataView(t),a=[];let i=e;for(let l=0;l<o;l++){if(i+1>n)return null;const c=r.getUint8(i++);let f,h,u=!1;if(c&2){if(i+8>n)return null;f=new Int16Array(4);for(let d=0;d<4;d++)f[d]=r.getInt16(i+2*d,!0);i+=8}else{if(i+4+s*8>n)return null;i+=4,f=new Int16Array(s*4);for(let d=0;d<s*4;d++)f[d]=r.getInt16(i+2*d,!0);i+=s*8}if(c&1){const d=(c&4)!==0,p=s*3*(d?2:1);if(i+4+p+24>n)return null;i+=4;const m=new Float32Array(s*3),g=i;for(let M=0;M<s*3;M++)m[M]=d?r.getInt16(g+2*M,!0):r.getInt8(g+M);i+=p;const w=[r.getFloat32(i,!0),r.getFloat32(i+4,!0),r.getFloat32(i+8,!0)],y=[r.getFloat32(i+12,!0),r.getFloat32(i+16,!0),r.getFloat32(i+20,!0)];i+=24;for(let M=0;M<s*3;M++)m[M]=m[M]*w[M%3]+y[M%3];h=m}else{if(i+12>n)return null;h=new Float32Array([r.getFloat32(i,!0),r.getFloat32(i+4,!0),r.getFloat32(i+8,!0)]),i+=12,u=!0}a.push({rot:f,trans:h,constT:u})}return{tracks:a,p:i}}function Af(t,e){try{const n=new sn(t,e.offset,e.offset+e.size),s=n.u16(),o=n.u16(),r=[],a=[];for(let l=0;l<o;l++){const c={name:n.stringz(),bones:[]},f=n.u16();for(let h=0;h<f;h++){const u=n.stringz(),d=s===1?r.length:n.u32();r[d]=u,c.bones.push(u)}a.push(c)}if(n.pos>n.end)return null;const i=[];try{const l=n.u16();for(let c=0;c<l&&n.pos<n.end;c++){const f=n.stringz(),h=n.u32(),u=n.u16(),d=n.u16(),p=n.f32();if(n.skip(12),s>=4){const m=n.u32();for(let g=0;g<m;g++){for(;n.pos<n.end&&n.byte()!==10;);n.skip(8*n.u32())}}i.push({name:f,flags:h,boneOrPart:u,motion:d,speed:p})}n.pos>n.end&&(i.length=0)}catch{i.length=0}return{version:s,trackBones:r,parts:a,defs:i}}catch{return null}}function h2(t,e,n){const s=new sn(t,e.offset,e.offset+e.size),o=s.stringz(),r=s.u32(),a=s.pos,i=e.offset+e.size;if(!r||r>1e5)return null;let l=wa(t,a,i,r,n);if(!l||l.p!==i){l=null;for(let c=1;c<=1024&&!l;c++){const f=wa(t,a,i,r,c);f&&f.p===i&&(l=f)}l||(l=wa(t,a,i,r,n))}return l?{kind:"xray",name:o,frames:r,tracks:l.tracks,duration:r/ga}:null}function $f(t,e,n,s){const o=[],r=new Map(n.map((h,u)=>[h.name.toLowerCase(),u])),a=h=>r.get((h??"").toLowerCase())??-1,i=s?.trackBones.length?s.trackBones.map(a):null,l=i?i.length:n.length,c=s?.parts.map(h=>new Set(h.bones.map(a)))??[],f=new Map(s?.defs.map(h=>[h.motion,h])??[]);for(const h of Xs(t,e.offset,e.offset+e.size))if(h.id!==0)try{const u=h2(t,h,l);if(!u)continue;if(i){const p=[];i.forEach((m,g)=>{m>=0&&u.tracks[g]&&(p[m]=u.tracks[g])}),u.tracks=p}const d=f.get(h.id-1);if(d){const p=(d.flags&1)!==0,m=p?new Set([d.boneOrPart]):c[d.boneOrPart];if(m&&m.size&&(p||c.length>1)){const g=[];for(const w of m)u.tracks[w]&&(g[w]=u.tracks[w]);u.tracks=g,u.part=p?ec:s.parts[d.boneOrPart].name}}o.push(u)}catch{}return o}function Sf(t,e){const n=Xs(t,0,t.byteLength),s=n.find(r=>r.id===be.S_MOTIONS);if(!s)throw new Error("В файле нет чанка анимаций (S_MOTIONS)");const o=n.find(r=>r.id===be.S_SMPARAMS);return $f(t,s,e,o?Af(t,o):null)}function d2(t){const{buf:e,chunks:n}=kf(t,Xs(t,0,t.byteLength)),s=n.find(M=>M.id===be.HEADER);if(!s)throw new Error("Не OGF файл: нет заголовка");const o=l2(e,s);if(o.version!==4)throw new Error(`Поддерживается только OGF версии 4 (в файле v${o.version})`);const r=M=>n.find(x=>x.id===M),a=r(be.S_BONE_NAMES),i=a?f2(e,a):[],l=r(be.S_IKDATA),c=l&&i.length?u2(e,l,i):!1,f=[];if(Ef(e,n,f,i.length),!f.length)throw new Error(`Геометрия не найдена (тип модели: ${Mf[o.type]??o.type})`);const h=[1/0,1/0,1/0],u=[-1/0,-1/0,-1/0];let d=0;for(const M of f){d+=M.indices.length/3;for(let x=0;x<M.pos.length;x++){const v=x%3;M.pos[x]<h[v]&&(h[v]=M.pos[x]),M.pos[x]>u[v]&&(u[v]=M.pos[x])}}const p=r(be.S_MOTIONS),m=r(be.S_SMPARAMS),g=p?$f(e,p,i,m?Af(e,m):null):[];let w=[];try{const M=r(be.S_MOTION_REFS0),x=r(be.S_MOTION_REFS1);if(M)w=new sn(e,M.offset,M.offset+M.size).stringz().split(",").map(v=>v.trim()).filter(Boolean);else if(x){const v=new sn(e,x.offset,x.offset+x.size),b=v.u32();for(let k=0;k<b;k++)w.push(v.stringz())}}catch{}let y=null;try{const M=r(be.S_DESC);if(M){const x=new sn(e,M.offset,M.offset+M.size);y={source:x.stringz(),tool:x.stringz()},x.u32(),y.owner=x.stringz()}}catch{}return{info:{type:Mf[o.type]??String(o.type),tris:d,ikOk:c,motionRefs:w,desc:y},meshes:f,bones:i,motions:g,bounds:{min:h,max:u}}}const Ce=1/32767;function ya(t,e,n){for(let s=0;s<4;s++)for(let o=0;o<4;o++)n[s*4+o]=t[s*4]*e[o]+t[s*4+1]*e[4+o]+t[s*4+2]*e[8+o]+t[s*4+3]*e[12+o]}function p2(t,e){for(let n=0;n<3;n++)for(let s=0;s<3;s++)e[n*4+s]=t[s*4+n];for(let n=0;n<3;n++)e[12+n]=-(t[12]*t[n*4]+t[13]*t[n*4+1]+t[14]*t[n*4+2]);e[3]=e[7]=e[11]=0,e[15]=1}function Tf(t,e,n,s){const o=Math.cos(t[0]),r=Math.sin(t[0]),a=Math.cos(t[1]),i=Math.sin(t[1]),l=Math.cos(t[2]),c=Math.sin(t[2]),f=[l*a+c*r*i,c*o,-l*i+c*r*a,-c*a+l*r*i,l*o,c*i+l*r*a,o*i,-r,o*a];for(let h=0;h<3;h++)for(let u=0;u<3;u++)n[h*4+u]=s?f[u*3+h]:f[h*3+u];n[3]=n[7]=n[11]=0,n[12]=e[0],n[13]=e[1],n[14]=e[2],n[15]=1}function m2(t,e,n,s,o,r,a,i){const l=t*t,c=e*e,f=n*n,h=t*e,u=t*n,d=e*n,p=s*t,m=s*e,g=s*n;i[0]=1-2*(c+f),i[1]=2*(h-g),i[2]=2*(u+m),i[3]=0,i[4]=2*(h+g),i[5]=1-2*(l+f),i[6]=2*(d-p),i[7]=0,i[8]=2*(u-m),i[9]=2*(d+p),i[10]=1-2*(l+c),i[11]=0,i[12]=o,i[13]=r,i[14]=a,i[15]=1}function g2(t,e,n,s,o,r){const a=t.rot;let i,l,c,f;if(a.length===4)i=a[0]*Ce,l=a[1]*Ce,c=a[2]*Ce,f=a[3]*Ce;else{const g=e*4,w=n*4,y=a[g]*Ce,M=a[g+1]*Ce,x=a[g+2]*Ce,v=a[g+3]*Ce;let b=a[w]*Ce,k=a[w+1]*Ce,E=a[w+2]*Ce,A=a[w+3]*Ce;y*b+M*k+x*E+v*A<0&&(b=-b,k=-k,E=-E,A=-A),i=y+(b-y)*s,l=M+(k-M)*s,c=x+(E-x)*s,f=v+(A-v)*s}const h=Math.hypot(i,l,c,f)||1;i/=h,l/=h,c/=h,f/=h,o&&(i=-i,l=-l,c=-c);const u=t.trans;let d,p,m;if(u.length===3)d=u[0],p=u[1],m=u[2];else{const g=e*3,w=n*3;d=u[g]+(u[w]-u[g])*s,p=u[g+1]+(u[w+1]-u[g+1])*s,m=u[g+2]+(u[w+2]-u[g+2])*s}m2(i,l,c,f,d,p,m,r)}class Hs{constructor(e,n=!1){this.transposeBind=n;const s=this.n=e.length;this.parent=e.map(c=>c.parent);const o=[],r=new Uint8Array(s),a=c=>{r[c]||(r[c]=1,this.parent[c]>=0&&a(this.parent[c]),o.push(c))};for(let c=0;c<s;c++)a(c);this.order=o;const i=()=>new Float32Array(16*s),l=c=>Array.from({length:s},(f,h)=>c.subarray(16*h,16*h+16));this.bindLocal=i(),this.bindWorld=i(),this.invBind=i(),this.world=i(),this.skin=i(),this.bindLocalV=l(this.bindLocal),this.bindWorldV=l(this.bindWorld),this.invV=l(this.invBind),this.worldV=l(this.world),this.skinV=l(this.skin);for(const c of o){Tf(e[c].rot,e[c].pos,this.bindLocalV[c],n);const f=this.parent[c];f<0?this.bindWorldV[c].set(this.bindLocalV[c]):ya(this.bindLocalV[c],this.bindWorldV[f],this.bindWorldV[c]),p2(this.bindWorldV[c],this.invV[c])}for(let c=0;c<16*s;c++)this.skin[c]=c%16%5===0?1:0;this.world.set(this.bindWorld)}transposeBind;n;parent;order;bindLocal;bindWorld;invBind;world;skin;bindLocalV;bindWorldV;invV;worldV;skinV;scratch=new Float32Array(16);bindVariant="";bindErr=[0,0];local(e,n,s){Tf(e,n,s,this.transposeBind)}static auto(e,n){const s=new Hs(e,!1);if(!s.n)return s;const o=new Hs(e,!0),r=c=>{let f=0,h=0;for(const u of n){if(!u.skinned)continue;const d=Math.max(1,Math.floor(u.vertexCount/1500));for(let p=0;p<u.vertexCount;p+=d){let m=u.bone[p*4],g=u.weight[p*4];for(let y=1;y<4;y++)u.weight[p*4+y]>g&&(g=u.weight[p*4+y],m=u.bone[p*4+y]);const w=c.bindWorldV[m];f+=Math.hypot(u.pos[p*3]-w[12],u.pos[p*3+1]-w[13],u.pos[p*3+2]-w[14]),h++}}return h?f/h:0},a=r(s),i=r(o),l=i<a*.98?o:s;return l.bindVariant=l===o?"B (транспонированный)":"A",l.bindErr=[a,i],l}pose(e,n,s,o=!1){if(!this.n)return;const r=e&&e.kind==="xray"?e:null,a=(l,c)=>{const f=l.frames;let h=n*ga;h=c?(h%f+f)%f:Math.min(Math.max(h,0),f-1);const u=Math.floor(h);return[u,c?(u+1)%f:Math.min(u+1,f-1),h-u]},i=r?[[r,...a(r,s)],...(r.layers??[]).map(l=>[l,...a(l,!0)])]:[];for(const l of this.order){const c=this.scratch;let f=!1;for(const[u,d,p,m]of i){const g=u.tracks[l];if(g){g2(g,d,p,m,o,c),f=!0;break}}f||c.set(this.bindLocalV[l]);const h=this.parent[l];h<0?this.worldV[l].set(c):ya(c,this.worldV[h],this.worldV[l]),ya(this.invV[l],this.worldV[l],this.skinV[l])}}detectInvert(e,n){const s=this.motionError(e,n,!1);return this.motionError(e,n,!0)<s*.9}motionError(e,n,s){const o=[];for(const c of e){if(!c.skinned)continue;const f=c.indices.length/3,h=Math.max(1,Math.floor(f/1500));for(let u=0;u<f;u+=h)o.push([c,c.indices[u*3],c.indices[u*3+1]])}if(!o.length)return 0;const r=(c,f,h)=>{const u=c.pos[f*3],d=c.pos[f*3+1],p=c.pos[f*3+2];h[0]=h[1]=h[2]=0;for(let m=0;m<4;m++){const g=c.weight[f*4+m];if(!g)continue;const w=this.skinV[c.bone[f*4+m]];h[0]+=g*(u*w[0]+d*w[4]+p*w[8]+w[12]),h[1]+=g*(u*w[1]+d*w[5]+p*w[9]+w[13]),h[2]+=g*(u*w[2]+d*w[6]+p*w[10]+w[14])}},a=[0,0,0],i=[0,0,0];return(c=>{let f=0,h=0;for(const u of[0,Math.floor(n.frames/2),n.frames-1]){this.pose(n,u/ga,!1,c);for(const[d,p,m]of o){r(d,p,a),r(d,m,i);const g=Math.hypot(a[0]-i[0],a[1]-i[1],a[2]-i[2]),w=Math.hypot(d.pos[p*3]-d.pos[m*3],d.pos[p*3+1]-d.pos[m*3+1],d.pos[p*3+2]-d.pos[m*3+2]);f+=Math.abs(g-w),h+=w}}return f/(h||1)})(s)}}async function Uf(t,e,n,s){const o=await t.arrayBuffer(),r=(t.name.split(".").pop()??"").toLowerCase();if(r==="skl"||r==="skls"){const{parseSkl:a,parseSMotions:i,smotionToXray:l}=await Promise.resolve().then(()=>A0),c=new Uint8Array(o);return(r==="skl"?[a(c)]:i(c)).map(h=>l(h,e.map(u=>u.name),(u,d,p)=>n.local(u,d,p),s))}return Sf(o,e)}async function Ff(t,e,n){const s=[],o=[];if(!n||!e.length)return{list:s,missing:o};for(const r of t){const a=r.replace(/\\/g,"/").toLowerCase().replace(/\.omf$/,"")+".omf";let i=null;for(const l of[`meshes/${a}`,`gamedata/meshes/${a}`,a])if(i=await n.readPath(l).catch(()=>null),i)break;if(!i){o.push(r);continue}try{const l=a.split("/").pop();for(const c of Sf(i.buffer.slice(i.byteOffset,i.byteOffset+i.byteLength),e))s.push({...c,source:l})}catch{o.push(r)}}return{list:s,missing:o}}const _f=(t,e,n)=>`Внешние анимации (.omf): ${t.join(", ")}${e?` — подключено ${e}`:""}${n.length?` (не найдены: ${n.join(", ")} — откройте папку gamedata или архивы игры)`:""}`;async function w2(t,e){const n=d2(await t.arrayBuffer()),{list:s,missing:o}=await Ff(n.info.motionRefs,n.bones,e);n.motions.push(...s),n.meshes.forEach((f,h)=>{f.name=`#${h}`});const r=n.meshes.map(f=>({...f,name:f.name??""})),a=Hs.auto(n.bones,r),i=r.reduce((f,h)=>f+h.vertexCount,0),l=n.info,c=[];return c.push(`Тип: ${l.type}`,`Мешей: ${r.length}, вершин: ${i}, треугольников: ${l.tris}`,`Костей: ${n.bones.length}${n.bones.length&&!l.ikOk?" (IK-данные не разобраны — bind-поза неточна)":""}`),a.bindVariant&&c.push(`Углы костей: вариант ${a.bindVariant}`),c.push(`Анимаций внутри: ${n.motions.length-s.length}`),l.motionRefs.length&&c.push(_f(l.motionRefs,s.length,o)),l.desc&&c.push(`Источник: ${l.desc.source}`,`Автор: ${l.desc.owner??""}`),{meshes:r,skeleton:a.n?a:null,motions:n.motions,bounds:n.bounds,flipZ:!0,info:c,detectInvert:f=>f.kind==="xray"?a.detectInvert(r,f):!1,loadMotions:async f=>Uf(f,n.bones,a)}}async function y2(t,e){const{parseObject:n,objectMeshes:s,objectSkeleton:o}=await Promise.resolve().then(()=>F2),r=n(await t.arrayBuffer()),a=o(r),i=a?new Map(a.bones.map((x,v)=>[x.wmap.toLowerCase(),v])):void 0;a&&a.bones.forEach((x,v)=>{i.has(x.name.toLowerCase())||i.set(x.name.toLowerCase(),v)});const l=s(r,"auto",i);if(!l.length)throw new Error("В объекте нет граней");const c=l.map(x=>{const v=x.pos.length/3;return{name:x.name,texture:x.surface?.texture??"",shader:x.surface?.eshader??"",material:x.surface?.gamemtl,vertexCount:v,pos:x.pos,nor:x.nor,uv:x.uv,bone:x.bone??new Float32Array(v*4),weight:x.weight??new Float32Array(v*4).map((b,k)=>k%4===0?1:0),indices:$e(v,x.indices),skinned:!!x.bone}}),f=c.reduce((x,v)=>x+v.vertexCount,0),h=c.reduce((x,v)=>x+v.indices.length/3,0),u=[`X-Ray SDK .object: мешей ${r.meshes.length}, поверхностей ${r.surfaces.length}, вершин ${f}, треугольников ${h}`];for(const x of r.surfaces.slice(0,12))u.push(` • ${x.name}: ${x.texture} | ${x.eshader} | ${x.cshader} | ${x.gamemtl}`);r.revision&&u.push(`Автор: ${r.revision.owner}`);const d={meshes:c,skeleton:null,motions:[],bounds:dt(c),flipZ:!0,info:u};if(!a)return d;const p=a.bones.map(x=>({name:x.name,parent:x.parent,rot:x.rot,pos:x.pos})),m=Hs.auto(p,c),g=a.parts.length?a.parts:void 0;if(a.motions)try{const{parseSMotions:x,smotionToXray:v}=await Promise.resolve().then(()=>A0);for(const b of x(a.motions))d.motions.push(v(b,p.map(k=>k.name),(k,E,A)=>m.local(k,E,A),g))}catch(x){u.push(`Анимации объекта не прочитаны: ${x.message}`)}const w=d.motions.length,{list:y,missing:M}=await Ff(a.motionRefs,p,e);return d.motions.push(...y),u.push(`Костей: ${p.length}${m.bindVariant?` (углы: вариант ${m.bindVariant})`:""}, частей тела: ${a.parts.length}, анимаций внутри: ${w}`),a.motionRefs.length&&u.push(_f(a.motionRefs,y.length,M)),{...d,skeleton:m,detectInvert:x=>x.kind==="xray"?m.detectInvert(c,x):!1,loadMotions:async x=>Uf(x,p,m,g)}}const Cf=Object.freeze(Object.defineProperty({__proto__:null,loadObject:y2,loadOgf:w2},Symbol.toStringTag,{value:"Module"})),ps={HEADER:1,SHADERS:2,VISUALS:3,VB:9,IB:10,SWIS:11},zn={HEADER:1,SWIDATA:6,VCONTAINER:7,ICONTAINER:8,TREEDEF2:12,SWICONTAINER:20,GCONTAINER:21},b2=new Set([0,2,7,11]);function jo(t){const e=new DataView(t.buffer,t.byteOffset,t.byteLength),n=[];let s=0;for(;s+8<=t.length;){const o=e.getUint32(s,!0),r=e.getUint32(s+4,!0);if(s+8+r>t.length)break;let a=t.subarray(s+8,s+8+r);o&2147483648&&(a=Mn(a)),n.push({id:o&2147483647,data:a}),s+=8+r}return n}const Le=t=>new DataView(t.buffer,t.byteOffset,t.byteLength);function Lf(t){const e=Le(t),n=e.getUint32(0,!0),s=[],o={0:4,1:8,2:12,3:16,4:4,5:4,6:4,7:8,8:4,9:8,10:4,11:8,12:4,13:8,15:4,16:8};let r=4;for(let a=0;a<n;a++){let i=0,l=-1,c=2,f=-1,h=-1,u=1,d=-1,p=-1;for(;;){const g=e.getUint16(r,!0),w=e.getUint16(r+2,!0),y=e.getUint8(r+4),M=e.getUint8(r+6),x=e.getUint8(r+7);if(r+=8,g===255)break;i=Math.max(i,w+(o[y]??4)),M===0&&l<0?(l=w,c=y):M===3?f=w:M===6?d=w:M===7?p=w:M===5&&x===0&&(h=w,u=y)}const m=e.getUint32(r,!0);r+=4,s.push({stride:i,count:m,data:new DataView(t.buffer,t.byteOffset+r,i*m),pos:l,posType:c,nor:f,tc:h,tcType:u,tan:d,bin:p}),r+=i*m}return s}function Rf(t){const e=Le(t),n=e.getUint32(0,!0),s=[];let o=4;for(let r=0;r<n;r++){const a=e.getUint32(o,!0);o+=4;const i=new Uint16Array(a);for(let l=0;l<a;l++)i[l]=e.getUint16(o+l*2,!0);s.push(i),o+=a*2}return s}const Of=(t,e)=>e+28<=t.byteLength&&t.getUint32(e+16,!0)>0?[t.getUint32(e+20,!0),t.getUint16(e+24,!0)]:null;function If(t){const e=Le(t),n=e.getUint32(0,!0),s=[];let o=4;for(let r=0;r<n&&o+20<=t.length;r++)s.push(Of(e,o)),o+=20+e.getUint32(o+16,!0)*8;return s}function Df(t,e=[]){const n=[];for(const s of jo(t)){const o=jo(s.data),r=o.find(k=>k.id===zn.HEADER);if(!r)continue;const a=Le(r.data),i=a.getUint8(1),l=a.getUint16(2,!0),c=o.find(k=>k.id===zn.GCONTAINER),f=o.find(k=>k.id===zn.VCONTAINER),h=o.find(k=>k.id===zn.ICONTAINER);let u=-1,d=0,p=0,m=-1,g=0,w=0;if(c){const k=Le(c.data);[u,d,p,m,g,w]=[0,4,8,12,16,20].map(E=>k.getUint32(E,!0))}else if(f&&h){const k=Le(f.data),E=Le(h.data);[u,d,p]=[k.getUint32(0,!0),k.getUint32(4,!0),k.getUint32(8,!0)],[m,g,w]=[E.getUint32(0,!0),E.getUint32(4,!0),E.getUint32(8,!0)]}const y=o.find(k=>k.id===zn.SWIDATA),M=o.find(k=>k.id===zn.SWICONTAINER),x=y?Of(Le(y.data),0):M&&M.data.length>=4?e[Le(M.data).getUint32(0,!0)]??null:null;if(x){const[k,E]=x;k+E*3<=w&&(g+=k,w=E*3)}let v=null;const b=o.find(k=>k.id===zn.TREEDEF2);if(b){const k=Le(b.data);v=new Float32Array(16);for(let E=0;E<16;E++)v[E]=k.getFloat32(E*4,!0)}n.push({type:i,shader:l,vb:u,vbOff:d,vbCount:p,ib:m,ibOff:g,ibCount:w,xform:v})}return n}function Pf(t){const e=t.indexOf("/");return e<0?[t,""]:[t.slice(0,e),t.slice(e+1).split(",")[0]]}function Nf(t,e,n){const s=jo(t),o=jo(e),r=s.find(v=>v.id===ps.SHADERS),a=s.find(v=>v.id===ps.VISUALS),i=s.find(v=>v.id===ps.HEADER),l=o.find(v=>v.id===ps.VB),c=o.find(v=>v.id===ps.IB);if(!r||!a||!l||!c)throw new Error("Не уровень X-Ray: нет шейдеров, визуалов или буферов level.geom");const f=[];{const v=Le(r.data),b=v.getUint32(0,!0);let k=4;for(let E=0;E<b;E++){let A="";for(;k<r.data.length&&r.data[k];)A+=String.fromCharCode(r.data[k++]);k++,f.push(A)}}const h=o.find(v=>v.id===ps.SWIS),u=Lf(l.data),d=Rf(c.data),p=Df(a.data,h?If(h.data):[]),m=new Map;let g=0,w=0;for(const v of p){if(!b2.has(v.type)||v.vb<0)continue;const b=u[v.vb],k=d[v.ib];if(!b||!k||b.pos<0||v.vbOff+v.vbCount>b.count||v.ibOff+v.ibCount>k.length){w++;continue}if(b.posType!==2&&b.posType!==3){w++;continue}let E=m.get(v.shader);E||m.set(v.shader,E={pos:[],nor:[],uv:[],idx:[]});const A=E.pos.length/3,S=b.data,$=v.xform;for(let T=0;T<v.vbCount;T++){const U=(v.vbOff+T)*b.stride;let _=S.getFloat32(U+b.pos,!0),R=S.getFloat32(U+b.pos+4,!0),L=S.getFloat32(U+b.pos+8,!0),O=0,N=1,D=0;b.nor>=0&&(O=S.getUint8(U+b.nor+2)/255*2-1,N=S.getUint8(U+b.nor+1)/255*2-1,D=S.getUint8(U+b.nor)/255*2-1),$&&([_,R,L]=[_*$[0]+R*$[4]+L*$[8]+$[12],_*$[1]+R*$[5]+L*$[9]+$[13],_*$[2]+R*$[6]+L*$[10]+$[14]],[O,N,D]=[O*$[0]+N*$[4]+D*$[8],O*$[1]+N*$[5]+D*$[9],O*$[2]+N*$[6]+D*$[10]]);const F=Math.hypot(O,N,D)||1;E.pos.push(_,R,L),E.nor.push(O/F,N/F,D/F);let C=0,P=0;if(b.tc>=0)if(b.tcType===1)C=S.getFloat32(U+b.tc,!0),P=S.getFloat32(U+b.tc+4,!0);else if(b.tcType===7)C=S.getInt16(U+b.tc,!0)/2048,P=S.getInt16(U+b.tc+2,!0)/2048;else{const I=b.tan>=0?S.getUint8(U+b.tan+3)/255:0,B=b.bin>=0?S.getUint8(U+b.bin+3)/255:0;C=(S.getInt16(U+b.tc,!0)+I)/1024,P=(S.getInt16(U+b.tc+2,!0)+B)/1024}E.uv.push(C,P)}for(let T=0;T<v.ibCount;T++)E.idx.push(A+k[v.ibOff+T]);g++}const y=[...m].map(([v,b])=>{const[k,E]=Pf(f[v]??""),A=b.pos.length/3;return{name:`${E||"?"} · ${k}`,texture:E,shader:k,vertexCount:A,pos:Float32Array.from(b.pos),nor:Float32Array.from(b.nor),uv:Float32Array.from(b.uv),bone:new Float32Array(A*4),weight:new Float32Array(A*4).map((S,$)=>$%4===0?1:0),indices:Uint32Array.from(b.idx),skinned:!1}}).filter(v=>v.indices.length),x=[`Уровень X-Ray (скомпилированный, версия ${i?Le(i.data).getUint16(0,!0):0}): ${n}`,`Визуалов ${p.length}, показано ${g}${w?`, пропущено ${w}`:""}; буферов VB ${u.length}, IB ${d.length}; шейдеров ${f.length}`,`Мешей (по шейдерам) ${y.length}, вершин ${y.reduce((v,b)=>v+b.vertexCount,0)}, треугольников ${y.reduce((v,b)=>v+b.indices.length/3,0)}`];return{meshes:y,skeleton:null,motions:[],bounds:dt(y),flipZ:!0,info:x}}async function v2(t,e){const n=await e.sibling("level.geom");if(!n)throw new Error("Рядом с level нет level.geom");const s=(t.path??"").split("/").slice(-2,-1)[0]??"",o=Nf(new Uint8Array(await t.arrayBuffer()),new Uint8Array(n),s||t.name),r=await e.sibling("level.details");if(r)try{const{parseDetails:a,parseCForm:i,buildDetails:l}=await Promise.resolve().then(()=>Gf),c=a(new Uint8Array(r)),f=await e.sibling("level.cform");if(!f)o.info.push("Трава: рядом нет level.cform — высоты не найти, трава не показана");else{const h=l(c,i(new Uint8Array(f)));o.meshes.push(...h.meshes),o.bounds=dt(o.meshes);const u=h.meshes.reduce((d,p)=>d+p.indices.length/3,0);o.info.push(`Трава (level.details): моделей ${c.models.length}, поставлено ${h.items} из ${h.candidates}${h.keep<1?` (прорежено до ${Math.round(h.keep*100)} %)`:""}, треугольников ${u}`)}}catch(a){o.info.push(`Трава: ${a.message}`)}return o}const x2=Object.freeze(Object.defineProperty({__proto__:null,levelModel:Nf,loadLevel:v2,parseIBs:Rf,parseSwis:If,parseVBs:Lf,parseVisuals:Df,splitShader:Pf},Symbol.toStringTag,{value:"Module"})),Ws=t=>new DataView(t.buffer,t.byteOffset,t.byteLength);function zf(t){const e=Ws(t),n=new Map;for(let s=0;s+8<=t.length;){const o=e.getUint32(s,!0)&2147483647,r=e.getUint32(s+4,!0);if(s+8+r>t.length)break;n.set(o,t.subarray(s+8,s+8+r)),s+=8+r}return n}function Bf(t){const e=Ws(t);let n=0;const s=()=>{const p=t.indexOf(0,n);if(p<0)throw new Error(".dm: строка не закрыта");const m=new TextDecoder("latin1").decode(t.subarray(n,p));return n=p+1,m},o=s(),r=s();if(n+20>t.length)throw new Error(".dm обрезан");const a=e.getUint32(n,!0),i=e.getFloat32(n+4,!0),l=e.getFloat32(n+8,!0),c=e.getUint32(n+12,!0),f=e.getUint32(n+16,!0);if(n+=20,c>65536||f%3||n+c*20+f*2>t.length)throw new Error(".dm: неверное число вершин или индексов");const h=new Float32Array(c*3),u=new Float32Array(c*2),d=new Uint16Array(f);for(let p=0;p<c;p++,n+=20)h[p*3]=e.getFloat32(n,!0),h[p*3+1]=e.getFloat32(n+4,!0),h[p*3+2]=e.getFloat32(n+8,!0),u[p*2]=e.getFloat32(n+12,!0),u[p*2+1]=e.getFloat32(n+16,!0);for(let p=0;p<f;p++,n+=2)d[p]=e.getUint16(n,!0);return{shader:o,texture:r,flags:a,minScale:i,maxScale:l,pos:h,uv:u,indices:d}}function M2(t,e){const n=t.pos.length/3,s=new Float32Array(n*3);for(let o=0;o<n;o++)s[o*3+1]=1;return{name:e,texture:t.texture,shader:t.shader,vertexCount:n,pos:t.pos,nor:s,uv:t.uv,bone:new Float32Array(n*4),weight:new Float32Array(n*4).map((o,r)=>r%4===0?1:0),indices:t.indices,skinned:!1,alphaTest:!0}}function k2(t){const e=zf(t),n=e.get(0),s=e.get(1),o=e.get(2);if(!n||n.length<24||!s||!o)throw new Error("level.details: нет заголовка, моделей или ячеек");const r=Ws(n),[a,,i,l,c,f]=[0,4,8,12,16,20].map(u=>r.getUint32(u,!0));if(a!==3)throw new Error(`level.details версии ${a} не поддерживается`);if(c*f*16>o.length)throw new Error("level.details: ячейки обрезаны");const h=[...zf(s)].sort((u,d)=>u[0]-d[0]).map(([,u])=>Bf(u));return{version:a,offsX:i,offsZ:l,sizeX:c,sizeZ:f,models:h,slots:Ws(o)}}function E2(t){const e=Ws(t),n=e.getUint32(0,!0),s=e.getUint32(4,!0),o=e.getUint32(8,!0);if(n!==4||36+s*12+o*16>t.length)throw new Error("level.cform: неизвестный формат");const r=new Float32Array(s*3),a=new Uint32Array(o*3);for(let i=0,l=36;i<s*3;i++,l+=4)r[i]=e.getFloat32(l,!0);for(let i=0,l=36+s*12;i<o;i++,l+=16)a[i*3]=e.getUint32(l,!0),a[i*3+1]=e.getUint32(l+4,!0),a[i*3+2]=e.getUint32(l+8,!0);return{verts:r,tris:a}}class Ks{constructor(e){this.s=e}s;next(){return this.s=Math.imul(this.s,214013)+2531011>>>0,this.s>>>16&32767}randI(e){return this.next()%e}randF(e,n){return e+(n-e)*this.next()/32767}randFs(e){return this.randF(-e,e)}}const A2=(()=>{let t=[[0]];for(let e=1;e<16;e*=2)t=Array.from({length:e*2},(n,s)=>Array.from({length:e*2},(o,r)=>4*t[s%e][r%e]+[[0,2],[3,1]][+(s>=e)][+(r>=e)]));return Uint8Array.from(t.flat())})(),Vf=15e5,jf=.3,on=2;function $2(t,e,n=Vf){const{sizeX:s,sizeZ:o,offsX:r,offsZ:a,slots:i,models:l}=t,c=l.length,f=Math.ceil(on/jf),h=jf/1.7,u=new Uint8Array(s*o),d=new Float32Array(s*o),p=new Float32Array(s*o);for(let F=0;F<s*o;F++){const C=i.getUint32(F*16,!0),P=i.getUint32(F*16+4,!0),I=[C>>>20&63,C>>>26&63,P&63,P>>>6&63];let B=!1;for(let z=0;z<4;z++)I[z]<c&&i.getUint16(F*16+8+z*2,!0)&&(B=!0);B&&(u[F]=1,d[F]=(C&4095)*.2-200,p[F]=d[F]+(C>>>12&255)*.1)}const{verts:m,tris:g}=e,w=g.length/3,y=new Uint32Array(s*o+1),M=(F,C)=>{const P=g[F*3]*3,I=g[F*3+1]*3,B=g[F*3+2]*3,z=Math.floor(Math.min(m[P],m[I],m[B])/on)+r,j=Math.floor(Math.max(m[P],m[I],m[B])/on)+r,X=Math.floor(Math.min(m[P+2],m[I+2],m[B+2])/on)+a,J=Math.floor(Math.max(m[P+2],m[I+2],m[B+2])/on)+a,G=Math.min(m[P+1],m[I+1],m[B+1]),H=Math.max(m[P+1],m[I+1],m[B+1]);for(let K=Math.max(0,X);K<=Math.min(o-1,J);K++)for(let Q=Math.max(0,z);Q<=Math.min(s-1,j);Q++){const V=K*s+Q;u[V]&&H>=d[V]-.01&&G<=p[V]+.01&&C(V)}};for(let F=0;F<w;F++)M(F,C=>y[C+1]++);for(let F=0;F<s*o;F++)y[F+1]+=y[F];const x=y.slice(0,s*o),v=new Uint32Array(y[s*o]);for(let F=0;F<w;F++)M(F,C=>{v[x[C]++]=F});const b=l.map(F=>F.indices.length/3),k=new Float32Array(16),E=new Int32Array(4),A=[],S=(F,C)=>{for(let P=0;P<o;P++)for(let I=0;I<s;I++){const B=P*s+I;if(!u[B])continue;const z=B*16,j=i.getUint32(z,!0),X=i.getUint32(z+4,!0);E[0]=j>>>20&63,E[1]=j>>>26&63,E[2]=X&63,E[3]=X>>>6&63;for(let rt=0;rt<4;rt++){const mt=i.getUint16(z+8+rt*2,!0);for(let xt=0;xt<4;xt++)k[rt*4+xt]=255*(mt>>xt*4&15)/15}const J=I-r,G=P-a,H=(302455168^Math.imul(J,G))>>>0,K=new Ks(H),Q=new Ks(H),V=new Ks(H),W=new Ks(H),Y=J*on,tt=G*on,nt=p[B],et=d[B];for(let rt=0;rt<=f;rt++)for(let mt=0;mt<=f;mt++){const xt=Q.randI(16),St=Q.randI(16);A.length=0;const Pt=Math.min(mt,f-1),Me=Math.min(rt,f-1),de=Pt/f,ze=Me/f;for(let pe=0;pe<4;pe++){if(E[pe]>=c)continue;const Be=k[pe*4]*(1-de)+k[pe*4+1]*de,Ve=k[pe*4+2]*(1-de)+k[pe*4+3]*de;Math.min(255,Math.max(0,Math.floor(Be*(1-ze)+Ve*ze+.5)))>A2[(Pt+xt)%16*16+(Me+St)%16]&&A.push(pe)}if(!A.length)continue;const yo=E[A.length===1?A[0]:A[K.randI(A.length)]],bo=mt/f*on+Y+Q.randFs(h),Nr=rt/f*on+tt+Q.randFs(h);let ts=et-5;if(C){for(let pe=y[B];pe<y[B+1];pe++){const Be=v[pe],Ve=g[Be*3]*3,Fn=g[Be*3+1]*3,yn=g[Be*3+2]*3,Qe=m[Ve],Te=m[Ve+2],xo=m[Fn]-Qe,es=m[Fn+2]-Te,Br=m[yn]-Qe,Ls=m[yn+2]-Te,ns=xo*Ls-Br*es;if(Math.abs(ns)<1e-9)continue;const Mo=bo-Qe,ko=Nr-Te,bn=(Mo*Ls-Br*ko)/ns,ss=(xo*ko-Mo*es)/ns;if(bn<0||ss<0||bn+ss>1)continue;const Rs=m[Ve+1]+bn*(m[Fn+1]-m[Ve+1])+ss*(m[yn+1]-m[Ve+1]);Rs<=nt+.001&&Rs>ts&&(ts=Rs)}if(ts<et)continue}const zr=l[yo],xl=W.randF(zr.minScale*.5,zr.maxScale*.9),vo=V.randF(0,Math.PI*2);F(yo,bo,ts,Nr,xl,vo,B)}}};let $=0,T=0;S(F=>{$+=b[F],T++},!1);const U=$>n?n/$:1,_=new Ks(24301),R=l.map(()=>[]),L=l.map(()=>[]),O=l.map(()=>[]);let N=0;S((F,C,P,I,B,z)=>{if(U<1&&_.next()/32768>=U)return;const j=l[F],X=Math.cos(z),J=Math.sin(z),G=R[F],H=G.length/3;if(!(H+j.pos.length/3>4e6)){for(let K=0;K<j.pos.length;K+=3){const Q=j.pos[K]*B,V=j.pos[K+1]*B,W=j.pos[K+2]*B;G.push(C+Q*X+W*J,P+V,I-Q*J+W*X)}for(let K=0;K<j.uv.length;K++)L[F].push(j.uv[K]);for(let K=0;K<j.indices.length;K++)O[F].push(H+j.indices[K]);N++}},!0);const D=[];return l.forEach((F,C)=>{if(!O[C].length)return;const P=R[C].length/3,I=new Float32Array(P*3);for(let B=0;B<P;B++)I[B*3+1]=1;D.push({name:`Трава · ${F.texture}`,texture:F.texture,shader:F.shader,vertexCount:P,pos:Float32Array.from(R[C]),nor:I,uv:Float32Array.from(L[C]),bone:new Float32Array(P*4),weight:new Float32Array(P*4).map((B,z)=>z%4===0?1:0),indices:Uint32Array.from(O[C]),skinned:!1,alphaTest:!0})}),{meshes:D,items:N,candidates:T,keep:U}}const Gf=Object.freeze(Object.defineProperty({__proto__:null,DETAIL_TRI_BUDGET:Vf,buildDetails:$2,dmMesh:M2,parseCForm:E2,parseDM:Bf,parseDetails:k2},Symbol.toStringTag,{value:"Module"})),Dt={MAIN:30583,VERSION:2304,FLAGS:2307,SURFACES2:2310,SURFACES3:2311,MESHES:2320,USERDATA:2322,SMOTIONS:2326,BONES:2337,REVISION:2338,BONEPARTS2:2339,LOD_REF:2341,SMOTION_REFS:2342},Xf={DEF:2,BIND_POSE:3},Rt={VERSION:4096,NAME:4097,FLAGS:4098,BBOX:4100,VERTS:4101,FACES:4102,VMREFS:4104,SFACE:4105,OPTIONS:4112,VMAPS1:4113,VMAPS2:4114,SG:4115},S2=t=>String.fromCharCode(t<128?t:t>=192?t+848:t===168?1025:t===184?1105:t);let Bn=class{constructor(e,n,s){this.buf=e,this.pos=n,this.end=s,this.dv=new DataView(e),this.u8=new Uint8Array(e)}buf;pos;end;dv;u8;b(){return this.dv.getUint8(this.pos++)}w(){const e=this.dv.getUint16(this.pos,!0);return this.pos+=2,e}d(){const e=this.dv.getUint32(this.pos,!0);return this.pos+=4,e}f(){const e=this.dv.getFloat32(this.pos,!0);return this.pos+=4,e}sz(){let e="";for(;this.pos<this.end;){const n=this.u8[this.pos++];if(!n)break;e+=S2(n)}return e}};function Vn(t,e,n){const s=new DataView(t),o=[];let r=e;for(;r+8<=n;){const a=s.getUint32(r,!0),i=s.getUint32(r+4,!0);if(r+8+i>n)throw new Error(`.object: чанк 0x${a.toString(16)} выходит за границы`);o.push({id:a,start:r+8,end:r+8+i}),r+=8+i}return o}function T2(t,e,n){const s={name:"",flags:0,options:[0,0],points:new Float32Array(0),faces:new Uint32Array(0),sg:null,vmrefs:[],sfaces:[],vmaps:[]};for(const o of Vn(t,e,n)){const r=new Bn(t,o.start,o.end);switch(o.id){case Rt.NAME:s.name=r.sz();break;case Rt.FLAGS:s.flags=r.b();break;case Rt.OPTIONS:s.options=[r.d(),r.d()];break;case Rt.VERTS:{const a=r.d();s.points=new Float32Array(a*3);for(let i=0;i<a*3;i++)s.points[i]=r.f();break}case Rt.FACES:{const a=r.d();s.faces=new Uint32Array(a*6);for(let i=0;i<a*6;i++)s.faces[i]=r.d();break}case Rt.SG:{const a=o.end-o.start>>2;s.sg=new Uint32Array(a);for(let i=0;i<a;i++)s.sg[i]=r.d();break}case Rt.VMREFS:{const a=r.d();for(let i=0;i<a;i++){const l=r.b(),c=[];for(let f=0;f<l;f++)c.push([r.d(),r.d()]);s.vmrefs.push(c)}break}case Rt.SFACE:{const a=r.w();for(let i=0;i<a;i++){const l=r.sz(),c=r.d(),f=new Uint32Array(c);for(let h=0;h<c;h++)f[h]=r.d();s.sfaces.push({surface:l,faces:f})}break}case Rt.VMAPS2:case Rt.VMAPS1:{const a=l=>{const c=new Bn(t,o.start,o.end),f=c.d(),h=[];for(let u=0;u<f;u++){const d=c.sz(),p=c.b(),m=o.id===Rt.VMAPS2?c.b()!==0:!1,g=c.b(),w=c.d();if(c.pos+w*p*4>o.end)return null;const y=new Float32Array(w*p);for(let v=0;v<w*p;v++)y[v]=c.f();const M=new Uint32Array(l?w:0);if(l){if(c.pos+w*4>o.end)return null;for(let v=0;v<w;v++)M[v]=c.d()}let x=null;if(m){if(c.pos+w*4>o.end)return null;x=new Uint32Array(w);for(let v=0;v<w;v++)x[v]=c.d()}h.push({name:d,dim:p,poly:m,type:g,data:y,vertices:M,faces:x})}return c.pos===o.end?h:null},i=a(!0)??(o.id===Rt.VMAPS1?a(!1):null);if(!i)throw new Error(`.object: чанк меша 0x${o.id.toString(16)} разобран неверно`);s.vmaps.push(...i),r.pos=o.end;break}}if(r.pos>o.end)throw new Error(`.object: чанк меша 0x${o.id.toString(16)} разобран неверно`)}return s}function Hf(t){const e=Vn(t,0,t.byteLength).find(s=>s.id===Dt.MAIN);if(!e)throw new Error("Не объект X-Ray SDK (.object): нет чанка 0x7777");const n={version:0,flags:0,userdata:"",lodRef:"",surfaces:[],meshes:[],revision:null,extra:[]};for(const s of Vn(t,e.start,e.end)){const o=new Bn(t,s.start,s.end);switch(s.id){case Dt.VERSION:n.version=o.w();break;case Dt.FLAGS:n.flags=o.d();break;case Dt.USERDATA:n.userdata=o.sz();break;case Dt.LOD_REF:n.lodRef=o.sz();break;case Dt.MESHES:for(const r of Vn(t,s.start,s.end))n.meshes.push(T2(t,r.start,r.end));break;case Dt.SURFACES3:case Dt.SURFACES2:{const r=o.d();for(let a=0;a<r;a++){const i={name:o.sz(),eshader:o.sz(),cshader:o.sz(),gamemtl:s.id===Dt.SURFACES3?o.sz():"default",texture:o.sz(),vmap:o.sz(),flags:o.d(),fvf:o.d()};o.d(),n.surfaces.push(i)}break}case Dt.REVISION:n.revision={owner:o.sz(),created:o.d(),modifier:o.sz(),modified:o.d()};break;default:n.extra.push({id:s.id,data:new Uint8Array(t.slice(s.start,s.end))})}}if(n.version!==16)throw new Error(`.object версии 0x${n.version.toString(16)} не поддерживается (нужна 0x10, SDK ТЧ/ЧН/ЗП)`);return n}function Wf(t,e="auto",n){const s=[];for(const o of t.meshes){const r=o.faces.length/6,a=o.points,i=new Float32Array(r*3);for(let m=0;m<r;m++){const g=o.faces[m*6]*3,w=o.faces[m*6+2]*3,y=o.faces[m*6+4]*3,M=a[w]-a[g],x=a[w+1]-a[g+1],v=a[w+2]-a[g+2],b=a[y]-a[g],k=a[y+1]-a[g+1],E=a[y+2]-a[g+2];let A=x*E-v*k,S=v*b-M*E,$=M*k-x*b;const T=Math.hypot(A,S,$)||1;A/=T,S/=T,$/=T,i[m*3]=A,i[m*3+1]=S,i[m*3+2]=$}const l=Array.from({length:a.length/3},()=>[]);for(let m=0;m<r;m++)for(let g=0;g<3;g++)l[o.faces[m*6+g*2]].push(m);const c=o.sg,f=e==="cop"||e==="auto"&&!!c&&c.every(m=>m<8),h=new Set;if(f){for(let m=0;m<r;m++)for(let g=0;g<3;g++)if(c[m]>>g&1){const w=o.faces[m*6+g*2],y=o.faces[m*6+(g+1)%3*2];h.add(w<y?`${w}_${y}`:`${y}_${w}`)}}const u=(m,g)=>{const w=[];for(let y=0;y<3;y++){const M=o.faces[m*6+y*2];M!==g&&w.push(M)}return w},d=(m,g)=>{if(!c)return l[g];if(!f){const M=c[m];return M?l[g].filter(x=>x===m||(c[x]&M)!==0):[m]}const w=new Set([m]),y=[m];for(;y.length;){const M=y.pop();for(const x of u(M,g))if(!h.has(g<x?`${g}_${x}`:`${x}_${g}`))for(const v of l[g])!w.has(v)&&u(v,g).includes(x)&&(w.add(v),y.push(v))}return[...w]},p=m=>{const g=o.vmrefs[m];if(!g)return[0,0];for(const[w,y]of g){const M=o.vmaps[w];if(M&&M.type===0&&M.dim===2)return[M.data[y*2],M.data[y*2+1]]}return[0,0]};for(const m of o.sfaces.length?o.sfaces:[{surface:t.surfaces[0]?.name??"",faces:Uint32Array.from({length:r},(g,w)=>w)}]){const g=[],w=[],y=[],M=[],x=new Map;for(const v of m.faces)for(let b=0;b<3;b++){const k=o.faces[v*6+b*2],E=o.faces[v*6+b*2+1];let A=0,S=0,$=0;for(const O of d(v,k))A+=i[O*3],S+=i[O*3+1],$+=i[O*3+2];const T=Math.hypot(A,S,$)||1;A/=T,S/=T,$/=T;const[U,_]=p(E),R=`${k}|${U}|${_}|${A.toFixed(3)}|${S.toFixed(3)}|${$.toFixed(3)}`;let L=x.get(R);L===void 0&&(L=g.length/3,x.set(R,L),g.push(a[k*3],a[k*3+1],a[k*3+2]),w.push(A,S,$),y.push(U,_)),M.push(L)}M.length&&s.push({name:`${o.name}: ${m.surface}`,surface:t.surfaces.find(v=>v.name===m.surface),pos:Float32Array.from(g),nor:Float32Array.from(w),uv:Float32Array.from(y),indices:Uint32Array.from(M)})}}return s}function U2(t){const e=h=>t.extra.find(u=>u.id===h)?.data,n=e(Dt.BONES);if(!n)return null;const s=n.buffer.slice(n.byteOffset,n.byteOffset+n.byteLength),o=[];for(const h of Vn(s,0,s.byteLength)){const u=Vn(s,h.start,h.end),d=u.find(v=>v.id===Xf.DEF),p=u.find(v=>v.id===Xf.BIND_POSE);if(!d)continue;const m=new Bn(s,d.start,d.end),g=m.sz(),w=m.sz(),y=m.sz();let M=[0,0,0],x=[0,0,0];if(p&&p.end-p.start>=24){const v=new Bn(s,p.start,p.end);M=[v.f(),v.f(),v.f()],x=[v.f(),v.f(),v.f()]}o.push({name:g,parent:w,wmap:y,rot:x,pos:M})}if(!o.length)return null;const r=new Map(o.map((h,u)=>[h.name.toLowerCase(),u])),a=o.map(h=>({name:h.name,parent:h.parent?r.get(h.parent.toLowerCase())??-1:-1,wmap:h.wmap||h.name,rot:h.rot,pos:h.pos})),i=[],l=e(Dt.BONEPARTS2);if(l)try{const h=l.buffer.slice(l.byteOffset,l.byteOffset+l.byteLength),u=new Bn(h,0,h.byteLength),d=u.d();for(let p=0;p<d;p++){const m=u.sz(),g=u.d(),w=[];for(let y=0;y<g;y++)w.push(u.sz());i.push({name:m,bones:w})}}catch{i.length=0}const c=[],f=e(Dt.SMOTION_REFS);if(f){const h=f.buffer.slice(f.byteOffset,f.byteOffset+f.byteLength),u=new Bn(h,0,h.byteLength),d=h.byteLength>=4?new DataView(h).getUint32(0,!0):0;if(d>0&&d<1e3){u.d();for(let p=0;p<d&&u.pos<u.end;p++)c.push(u.sz())}else c.push(...u.sz().split(",").map(p=>p.trim()).filter(Boolean))}return{bones:a,parts:i,motions:e(Dt.SMOTIONS)??null,motionRefs:c}}const F2=Object.freeze(Object.defineProperty({__proto__:null,EMESH:Rt,EOBJ:Dt,chunksOf:Vn,objectMeshes:Wf,objectSkeleton:U2,parseObject:Hf},Symbol.toStringTag,{value:"Module"}));function ba(t){const e=new Map;let n=null;for(const s of t.split(/\r?\n/)){const o=s.replace(/;.*$/,"").trim();if(!o)continue;const r=/^\[([^\]]+)\]/.exec(o);if(r){n=new Map,e.set(r[1].trim(),n);continue}const a=o.indexOf("=");n&&a>0&&n.set(o.slice(0,a).trim(),o.slice(a+1).trim())}return e}const va=(t,e)=>t?t.split(",").map(n=>+n.trim()):e;function Kf(t){const e=[];for(const[n,s]of t)!/^object_\d+$/.test(n)||!s.get("reference_name")||e.push({name:s.get("name")??n,reference:s.get("reference_name"),flags:+(s.get("flags")??0),position:va(s.get("position"),[0,0,0]),rotation:va(s.get("rotation"),[0,0,0]),scale:va(s.get("scale"),[1,1,1])});return e}function Yf(t){const e=t[1],n=t[0],s=t[2],o=Math.sin(e),r=Math.cos(e),a=Math.sin(n),i=Math.cos(n),l=Math.sin(s),c=Math.cos(s),f=r*c,h=r*l,u=o*c,d=o*l,p=[f-a*d,h+a*u,-i*o,-i*l,i*c,a,u+a*h,d-a*f,i*r];return[p[0],p[3],p[6],p[1],p[4],p[7],p[2],p[5],p[8]]}function Zf(t,e){const n=Yf(e.rotation),[s,o,r]=e.scale,[a,i,l]=e.position,c=t.pos.length/3,f=new Float32Array(c*3),h=new Float32Array(c*3);for(let u=0;u<c;u++){const d=t.pos[u*3]*s,p=t.pos[u*3+1]*o,m=t.pos[u*3+2]*r;f[u*3]=d*n[0]+p*n[3]+m*n[6]+a,f[u*3+1]=d*n[1]+p*n[4]+m*n[7]+i,f[u*3+2]=d*n[2]+p*n[5]+m*n[8]+l;const g=t.nor[u*3]/s,w=t.nor[u*3+1]/o,y=t.nor[u*3+2]/r;let M=g*n[0]+w*n[3]+y*n[6],x=g*n[1]+w*n[4]+y*n[7],v=g*n[2]+w*n[5]+y*n[8];const b=Math.hypot(M,x,v)||1;M/=b,x/=b,v/=b,h[u*3]=M,h[u*3+1]=x,h[u*3+2]=v}return{pos:f,nor:h}}async function qf(t,e){const n=e.toLowerCase().replace(/\\/g,"/");for(const s of t.archives){const o=s.lookup(n)??s.entries.find(r=>r.path.endsWith("/"+n));if(o)return s.read(o)}return null}async function _2(t,e){const s=ba(new TextDecoder("windows-1251").decode(await t.arrayBuffer())).get("level_options"),o=t.name.replace(/\.level$/i,""),a=`${(t.path??"").replace(/[^/]*$/,"")}${o}/scene_object.part`;let i=await e.readPath(a).catch(()=>null);if(i||(i=await qf(e,`rawdata/levels/${o}/scene_object.part`)),!i)throw new Error(`Нет ${o}/scene_object.part рядом с ${t.name}: откройте папку SDK (editors/rawdata) целиком`);const l=Kf(ba(new TextDecoder("windows-1251").decode(i))),c=new Map,f=new Set,h=new Map;let u=0;for(const m of l){let g=c.get(m.reference);if(g===void 0){const w=await qf(e,`rawdata/objects/${m.reference}.object`);try{g=w?Wf(Hf(w.buffer.slice(w.byteOffset,w.byteOffset+w.byteLength))):null}catch{g=null}c.set(m.reference,g)}if(!g){f.add(m.reference);continue}u++;for(const w of g){const y=w.surface,M=`${y?.texture??""}|${y?.eshader??""}`;let x=h.get(M);x||h.set(M,x={texture:y?.texture??"",shader:y?.eshader??"",material:y?.gamemtl,pos:[],nor:[],uv:[],idx:[]});const{pos:v,nor:b}=Zf(w,m),k=x.pos.length/3;for(let E=0;E<v.length;E++)x.pos.push(v[E]),x.nor.push(b[E]);for(let E=0;E<w.uv.length;E++)x.uv.push(w.uv[E]);for(let E=0;E<w.indices.length;E++)x.idx.push(w.indices[E]+k)}}const d=[...h.values()].map(m=>{const g=m.pos.length/3;return{name:`${m.texture||"(без текстуры)"} · ${m.shader}`,texture:m.texture,shader:m.shader,material:m.material,vertexCount:g,pos:Float32Array.from(m.pos),nor:Float32Array.from(m.nor),uv:Float32Array.from(m.uv),bone:new Float32Array(g*4),weight:new Float32Array(g*4).map((w,y)=>y%4===0?1:0),indices:Uint32Array.from(m.idx),skinned:!1}});if(!d.length)throw new Error(`Объекты уровня не найдены (${f.size} ссылок): откройте папку SDK с rawdata/objects`);const p=[`Уровень SDK: ${s?.get("level_path")??o}, объектов ${l.length}, показано ${u}, мешей (по текстурам) ${d.length}`];return f.size&&p.push(`Нет файлов объектов (${f.size}): ${[...f].slice(0,6).join(", ")}${f.size>6?"…":""}`),{meshes:d,skeleton:null,motions:[],bounds:dt(d),flipZ:!0,info:p}}const C2=Object.freeze(Object.defineProperty({__proto__:null,loadSceneLevel:_2,parseIni:ba,placeMesh:Zf,sceneObjects:Kf,sceneRotation:Yf},Symbol.toStringTag,{value:"Module"}));function Ys(t,e,n){let s=e,o=0,r=0,a=0;const i=n.length,l=d=>{throw new Error(`LZO: ${d} (вход ${s-e}, выход ${o}/${i})`)},c=d=>{o+d<=i&&s+d<=t.length||l("выход за границы при копировании литералов"),n.set(t.subarray(s,s+d),o),o+=d,s+=d},f=(d,p)=>{d<0&&l("ссылка назад за начало"),o+p>i&&l("переполнение выхода");for(let m=0;m<p;m++)n[o++]=n[d+m]},h=d=>{let p=0;for(;t[s]===0;)p+=255,s++,s>=t.length&&l("обрыв");return p+d+t[s++]};let u=0;for(t[s]>17&&(r=t[s++]-17,r<4?u=3:(c(r),u=1));;){if((!(s<t.length)||!(o<=i))&&l("обрыв входа"),u===0){if(r=t[s++],r>=16){u=2;continue}r===0&&(r=h(15)),c(r+3),u=1;continue}if(u===1){if(r=t[s++],r>=16){u=2;continue}a=o-2049-(r>>2)-(t[s++]<<2),f(a,3)}else if(u===2)if(r>=64)a=o-1-(r>>2&7)-(t[s++]<<3),f(a,(r>>5)+1);else if(r>=32)r&=31,r===0&&(r=h(31)),a=o-1-((t[s]|t[s+1]<<8)>>2),s+=2,f(a,r+2);else if(r>=16){if(a=o-((r&8)<<11),r&=7,r===0&&(r=h(7)),a-=(t[s]|t[s+1]<<8)>>2,s+=2,a===o)return s;a-=16384,f(a,r+2)}else a=o-1-(r>>2)-(t[s++]<<2),f(a,2);else{c(r),r=t[s++],u=2;continue}if(r=t[s-2]&3,r===0?u=0:u=3,s>=t.length&&o>=i)return s}}function Qf(t,e,n,s,o){for(;e<n;){const r=t[e++];let a=r>>4;if(a===15){let f;do f=t[e++],a+=f;while(f===255)}for(let f=0;f<a;f++)s[o++]=t[e++];if(e>=n)break;const i=t[e]|t[e+1]<<8;e+=2;let l=r&15;if(l===15){let f;do f=t[e++],l+=f;while(f===255)}l+=4;let c=o-i;if(i===0||c<0)throw new Error("LZ4: неверное смещение");for(let f=0;f<l;f++)s[o++]=s[c++]}return o}function L2(t,e){const n=new DataView(t.buffer,t.byteOffset,t.byteLength);if(n.getUint32(0,!0)!==407708164)throw new Error("LZ4: нет сигнатуры кадра");const s=t[4];let o=6;s&8&&(o+=8),s&1&&(o+=4),o+=1;const r=!!(s&16),a=new Uint8Array(e);let i=0;for(;;){const l=n.getUint32(o,!0);if(o+=4,l===0)break;const c=(l&2147483648)!==0,f=l&2147483647;c?(a.set(t.subarray(o,o+f),i),i+=f):i=Qf(t,o,o+f,a,i),o+=f+(r?4:0)}return i===e?a:a.subarray(0,i)}const R2=1,xa=2147483648;async function O2(t){let e=0;for(let n=0;n<64&&e+8<=t.size;n++){const s=await ht(t,e,e+8),o=new DataView(s.buffer,s.byteOffset,8),r=o.getUint32(0,!0),a=o.getUint32(4,!0);if(e+8+a>t.size)return null;if((r&~xa)>>>0===R2)return{id:r,off:e+8,size:a};e+=8+a}return null}function Jf(t){const e=new DataView(t.buffer,t.byteOffset,t.byteLength),n=[];let s=0;for(;s+2<=t.length;){const o=e.getUint16(s,!0);if(o<16||s+2+o>t.length)throw new Error(`таблица файлов повреждена (смещение ${s})`);const r=e.getUint32(s+2,!0),a=e.getUint32(s+6,!0);let i="";for(let c=s+14;c<s+2+o-4;c++)i+=String.fromCharCode(t[c]);const l=e.getUint32(s+2+o-4,!0);s+=2+o,!(!r&&!a)&&n.push({path:gt(i),size:r,off:l,real:r,packed:a})}return n}const ms=new Map,Ma="gmv.xray.socKeys";function I2(){try{const t=JSON.parse(globalThis.localStorage?.getItem(Ma)??"[]");for(const[e,n]of t)n&&Number.isFinite(n.tableSeed)&&Number.isFinite(n.encryptSeed)&&Number.isFinite(n.iterations)&&ms.set(e,n)}catch{}}function D2(){try{globalThis.localStorage?.setItem(Ma,JSON.stringify([...ms]))}catch{}}let ka=!1;const P2=()=>{ms.clear(),ka=!0;try{globalThis.localStorage?.removeItem(Ma)}catch{}};function Go(t,e){try{const n=Jf(t);return n.length&&!n.some(s=>s.off+s.packed>e)?n:null}catch{return null}}async function N2(t,e=[]){const n=await O2(t);if(!n)throw new Error(`${t.name}: не архив X-Ray (нет таблицы файлов)`);const s=await ht(t,n.off,n.off+n.size);let o=null;try{o=Go(n.id&xa?Mn(s):s,t.size)}catch{o=null}if(!o&&n.id&xa){const{findSocKey:r,socDecode:a}=await Promise.resolve().then(()=>qv),i=f=>!!Go(f,t.size);ka||(ka=!0,I2());for(const f of ms.values()){try{o=Go(Mn(a(f,s)),t.size)}catch{o=null}if(o)break}const l=e.filter(f=>/\.exe$/i.test(f.name)).sort((f,h)=>+!/xr_3da/i.test(f.name)-+!/xr_3da/i.test(h.name)),c=[];for(const f of o?[]:l){const h=`${f.name}:${f.size}`;if(ms.has(h))continue;let u=null;try{u=r(new Uint8Array(await f.arrayBuffer()),s,i)}catch(d){c.push(`${f.webkitRelativePath||f.name}: ${d.message}`);continue}if(!u){c.push(`${f.webkitRelativePath||f.name} (${f.size} Б) — ключ не найден`);continue}if(ms.set(h,u),D2(),o=Go(Mn(a(u,s)),t.size),o)break}if(!o)throw new Error(`${t.name}: таблица файлов зашифрована (архивы «Тени Чернобыля»). Откройте папку игры целиком («Папка игры…») или выберите кнопкой «Открыть .db…» сначала bin\\XR_3DA.exe, затем архивы — ключ берётся из него. ${c.length?`Проверены: ${c.join("; ")}.`:`Исполняемых файлов игры среди выбранных нет (выбрано файлов: ${e.length}).`}`)}if(!o)throw new Error(`${t.name}: таблица файлов повреждена.`);return Qt({name:t.name,kind:"xray-db",version:0,entries:o,async read(r){const a=await ht(t,r.off,r.off+r.packed);if(r.packed===r.real)return a;const i=new Uint8Array(r.real);return Ys(a,0,i),i}})}const z2=Object.freeze(Object.defineProperty({__proto__:null,openXrayDb:N2,parseDbTable:Jf,resetSocKeys:P2},Symbol.toStringTag,{value:"Module"})),Xo={MIPMAPS:1,DITHER:256,DIFFUSE_DETAIL:1<<23,HAS_ALPHA:1<<25},tu={DXT1:0,DXT5:3};function eu(t){const e=[],n=c=>e.push(c&255),s=c=>{n(c),n(c>>8)},o=c=>{s(c&65535),s(c>>>16&65535)},r=c=>{const f=new Uint8Array(new Float32Array([c]).buffer);for(const h of f)n(h)},a=c=>{for(let f=0;f<c.length;f++)n(c.charCodeAt(f));n(0)},i=(c,f)=>{o(c);const h=e.length;o(0),f();const u=e.length-h-4;e[h]=u&255,e[h+1]=u>>8&255,e[h+2]=u>>16&255,e[h+3]=u>>>24&255},l=Xo.MIPMAPS|(t.alpha?Xo.HAS_ALPHA|Xo.DITHER:0)|(t.detail?Xo.DIFFUSE_DETAIL:0);return i(2064,()=>s(18)),i(2067,()=>o(1)),i(2066,()=>{o(t.format??(t.alpha?tu.DXT5:tu.DXT1)),o(l),o(0),o(0),o(0),o(0),o(t.width),o(t.height)}),i(2068,()=>o(0)),i(2069,()=>{a(t.detail??""),r(t.detailScale??1)}),i(2070,()=>{o(1),r(0)}),i(2071,()=>{r(.05),o(t.bump?2:1),a(t.bump??"")}),i(2072,()=>a("")),i(2073,()=>n(0)),Uint8Array.from(e)}class B2{buf=new Uint8Array(65536);dv=new DataView(this.buf.buffer);pos=0;need(e){if(this.pos+e<=this.buf.length)return;let n=this.buf.length*2;for(;n<this.pos+e;)n*=2;const s=new Uint8Array(n);s.set(this.buf),this.buf=s,this.dv=new DataView(s.buffer)}b(e){return this.need(1),this.dv.setUint8(this.pos++,e),this}w(e){return this.need(2),this.dv.setUint16(this.pos,e,!0),this.pos+=2,this}d(e){return this.need(4),this.dv.setUint32(this.pos,e>>>0,!0),this.pos+=4,this}f(e){return this.need(4),this.dv.setFloat32(this.pos,e,!0),this.pos+=4,this}sz(e){const n=V2(e);return this.need(n.length+1),this.buf.set(n,this.pos),this.pos+=n.length,this.b(0)}bytes(e){return this.need(e.length),this.buf.set(e,this.pos),this.pos+=e.length,this}chunk(e,n){this.d(e);const s=this.pos;return this.d(0),n(this),this.dv.setUint32(s,this.pos-s-4,!0),this}done(){return this.buf.slice(0,this.pos)}}function V2(t){const e=new Uint8Array(t.length);for(let n=0;n<t.length;n++){const s=t.charCodeAt(n);e[n]=s<128?s:s>=1040&&s<=1103?s-848:s===1025?168:s===1105?184:63}return e}function j2(t,e){const n=t.pos,s=t.indices,o=Math.floor(s.length/3),r=new Map,a=new Int32Array(n.length/3),i=[],l=t.nor,c=$=>Math.round($*1e3);for(let $=0;$<n.length/3;$++){const T=`${n[$*3]}|${n[$*3+1]}|${n[$*3+2]}`+(l?`|${c(l[$*3])}|${c(l[$*3+1])}|${c(l[$*3+2])}`:"");let U=r.get(T);U===void 0&&(U=i.length/3,r.set(T,U),i.push(n[$*3],n[$*3+1],n[$*3+2])),a[$]=U}const f=i.length/3,h=new Array(f*2).fill(NaN),u=[],d=[],p=new Map,m=[],g=($,T)=>{const U=`${$}|${T}`;let _=p.get(U);return _===void 0&&(_=d.length,p.set(U,_),d.push([$,T])),_},w=new Map,y=$=>typeof t.surface=="number"?t.surface:t.surface[$]??0;for(let $=0;$<o;$++)for(let T=0;T<3;T++){const U=s[$*3+T],_=a[U];Number.isNaN(h[_*2])&&(h[_*2]=t.uv[U*2],h[_*2+1]=t.uv[U*2+1])}for(let $=0;$<o;$++){for(let U=0;U<3;U++){const _=s[$*3+U],R=a[_],L=t.uv[_*2],O=t.uv[_*2+1];let N;L===h[R*2]&&O===h[R*2+1]?N=g(0,R):(N=g(1,u.length),u.push({u:L,v:O,point:R,face:$})),m.push(R,N)}const T=y($);(w.get(T)??w.set(T,[]).get(T)).push($)}const M=$=>{const T=a[s[$*3]]*3,U=a[s[$*3+1]]*3,_=a[s[$*3+2]]*3,R=i[U]-i[T],L=i[U+1]-i[T+1],O=i[U+2]-i[T+2],N=i[_]-i[T],D=i[_+1]-i[T+1],F=i[_+2]-i[T+2],C=[L*F-O*D,O*N-R*F,R*D-L*N],P=Math.hypot(C[0],C[1],C[2])||1;return[C[0]/P,C[1]/P,C[2]/P]},x=new Map;for(let $=0;$<o;$++)for(let T=0;T<3;T++){const U=a[s[$*3+T]],_=a[s[$*3+(T+1)%3]],R=U<_?`${U}_${_}`:`${_}_${U}`;(x.get(R)??x.set(R,[]).get(R)).push([$,T])}const v=new Array(o).fill(0),b=t.nor,k=($,T)=>{const U=s[$*3+T];return[b[U*3],b[U*3+1],b[U*3+2]]},E=($,T)=>$[0]*T[0]+$[1]*T[1]+$[2]*T[2];for(const $ of x.values()){if($.length!==2){for(const[O,N]of $)v[O]|=1<<N;continue}const[[T,U],[_,R]]=$;let L;if(b){const O=a[s[T*3+U]],N=(F,C)=>[0,1,2].find(P=>a[s[F*3+P]]===C),D=a[s[T*3+(U+1)%3]];L=E(k(T,U),k(_,N(_,O)))<.999||E(k(T,(U+1)%3),k(_,N(_,D)))<.999}else L=E(M(T),M(_))<e;L&&(v[T]|=1<<U,v[_]|=1<<R)}const A=[1/0,1/0,1/0],S=[-1/0,-1/0,-1/0];for(let $=0;$<i.length;$++)A[$%3]=Math.min(A[$%3],i[$]),S[$%3]=Math.max(S[$%3],i[$]);return{points:i,faces:m,sg:v,vmrefs:d,base:h,poly:u,sfaces:w,bbox:[A,S]}}function nu(t){const e=Math.cos((t.hardAngleDeg??60)*Math.PI/180),n=Math.floor(Date.now()/1e3),s=new B2,o=r=>t.surfaces[r]?.name??`surface_${r}`;return s.chunk(Dt.MAIN,r=>{r.chunk(Dt.VERSION,a=>a.w(16)),r.chunk(Dt.USERDATA,a=>a.sz("")),r.chunk(Dt.LOD_REF,a=>a.sz("")),r.chunk(Dt.FLAGS,a=>a.d(t.dynamic?1:0)),r.chunk(Dt.MESHES,a=>{t.meshes.forEach((i,l)=>a.chunk(l,c=>{const f=j2(i,e),h=f.points.length/3,u=f.faces.length/6;c.chunk(Rt.VERSION,d=>d.w(17)),c.chunk(Rt.NAME,d=>d.sz(i.name||`mesh_${l}`)),c.chunk(Rt.BBOX,d=>{for(const p of[...f.bbox[0],...f.bbox[1]])d.f(h?p:0)}),c.chunk(Rt.FLAGS,d=>d.b(1)),c.chunk(Rt.OPTIONS,d=>d.d(0).d(0)),c.chunk(Rt.VERTS,d=>{d.d(h);for(const p of f.points)d.f(p)}),c.chunk(Rt.FACES,d=>{d.d(u);for(const p of f.faces)d.d(p)}),c.chunk(Rt.SG,d=>{for(const p of f.sg)d.d(p)}),c.chunk(Rt.VMREFS,d=>{d.d(f.vmrefs.length);for(const[p,m]of f.vmrefs)d.b(1).d(p).d(m)}),c.chunk(Rt.SFACE,d=>{d.w(f.sfaces.size);for(const[p,m]of f.sfaces){d.sz(o(p)).d(m.length);for(const g of m)d.d(g)}}),c.chunk(Rt.VMAPS2,d=>{d.d(f.poly.length?2:1),d.sz("Texture").b(2).b(0).b(0).d(h);for(let p=0;p<h;p++)d.f(f.base[p*2]).f(f.base[p*2+1]);for(let p=0;p<h;p++)d.d(p);if(f.poly.length){d.sz("Texture").b(2).b(1).b(0).d(f.poly.length);for(const p of f.poly)d.f(p.u).f(p.v);for(const p of f.poly)d.d(p.point);for(const p of f.poly)d.d(p.face)}})}))}),r.chunk(Dt.SURFACES3,a=>{a.d(t.surfaces.length);for(const i of t.surfaces)a.sz(i.name).sz(i.eshader??"default").sz(i.cshader??"default").sz(i.gamemtl??"default").sz(i.texture).sz("Texture").d(i.twoSided?1:0).d(274).d(1)}),r.chunk(Dt.REVISION,a=>a.sz(t.owner??"gmv").d(n).sz(t.owner??"gmv").d(n))}),s.done()}function G2(t){const e=[t[0],t[3],t[6],t[1],t[4],t[7],t[2],t[5],t[8]],n=Math.asin(Math.max(-1,Math.min(1,e[5])));let s,o;return Math.abs(e[5])<.99999?(s=Math.atan2(-e[2],e[8]),o=Math.atan2(-e[3],e[4])):(s=0,o=Math.atan2(e[1],e[0])),[n,s,o]}const He=t=>(Math.abs(t)<5e-7?0:t).toFixed(6),rn=t=>t.map(He).join(", ");function su(t){let e="";for(const n of Object.keys(t).sort()){e+=`[${n}]\r
|
|
188
|
+
`;const s=t[n];for(const o of Object.keys(s).sort()){const r=s[o];e+=` ${o.padEnd(33)}=${r===""?"":" "+r}\r
|
|
189
|
+
`}e+=` \r
|
|
190
|
+
`}return e}function ou(t){const e=new Uint8Array(t.length);for(let n=0;n<t.length;n++){const s=t.charCodeAt(n);e[n]=s<128?s:s>=1040&&s<=1103?s-848:s===1025?168:s===1105?184:63}return e}const ru=()=>{let t=0n;for(let e=0;e<8;e++)t=t<<8n|BigInt(Math.floor(Math.random()*256));return t.toString()};function X2(t){const e=ru(),n=ru(),s=Math.floor(Date.now()/1e3),o={};t.objects.forEach((l,c)=>{const f=l.rotation??(l.matrix?G2(l.matrix):[0,0,0]);o[`object_${c}`]={clsid:"2",co_flags:"0",flags:"0",name:l.name,position:rn(l.position),reference_name:l.reference,rotation:rn(f),scale:rn(l.scale??[1,1,1]),version:"18"}});const r=su({appendrandom:{AppendRandomMaxRotation:rn([0,0,0]),AppendRandomMaxScale:rn([1,1,1]),AppendRandomMinRotation:rn([0,0,0]),AppendRandomMinScale:rn([1,1,1]),AppendRandomObjects_size:"0"},guid:{guid_g0:e,guid_g1:n},main:{flags:"0",objects_count:String(t.objects.length),version:"0"},modif:{name:"",time:"0"},...o});let a=t.camera;if(!a){const l=[0,0,0];for(const c of t.objects)for(let f=0;f<3;f++)l[f]+=c.position[f]/Math.max(1,t.objects.length);a={pos:[l[0],l[1]+20,l[2]-30],hpb:[0,-.5,0]}}const i=su({build_params:{light_jitter_samples:"9",light_pixel_per_meter:He(20),light_quality:"2",light_quality_reserved:"29795",light_rms:"4",light_rms_zero:"4",reserved_0:He(0),reserved_1:He(0),reserved_2:He(0),reserved_3:He(0),reserved_4:He(0),reserved_5:He(0),smooth_angle:He(75),weld_distance:He(.005)},camera:{hpb:rn(a.hpb),pos:rn(a.pos)},guid:{guid_g0:e,guid_g1:n},level_options:{bop:'""',game_type:"1",level_path:t.name,level_prefix:t.name,light_hemi_quality:"3",light_sun_quality:"0",map_version:"1.0",version:"12",version_bp:"9"},level_tag:{create_time:String(s),owner:t.owner??"gmv"},version:{value:"5"}});return new Map([[`${t.name}.level`,ou(i)],[`${t.name}/scene_object.part`,ou(r)]])}const jn=t=>t.toLowerCase().replace(/\.[a-z0-9]+$/,"").replace(/[^a-z0-9_]+/g,"_").replace(/^_+|_+$/g,"")||"tex";function H2(t,e,n){const s=t.meshes[e],o=s.vertexCount,r=t.skeleton,a=new Float32Array(o*3),i=new Float32Array(o*3);r&&r.pose(null,0,!1);for(let c=0;c<o;c++){let f=s.pos[c*3],h=s.pos[c*3+1],u=s.pos[c*3+2],d=s.nor[c*3],p=s.nor[c*3+1],m=s.nor[c*3+2];if(r&&s.skinned){let w=0,y=0,M=0,x=0,v=0,b=0,k=0;for(let E=0;E<4;E++){const A=s.weight[c*4+E];if(!A)continue;const S=r.skinV[s.bone[c*4+E]];k+=A,w+=A*(f*S[0]+h*S[4]+u*S[8]+S[12]),y+=A*(f*S[1]+h*S[5]+u*S[9]+S[13]),M+=A*(f*S[2]+h*S[6]+u*S[10]+S[14]),x+=A*(d*S[0]+p*S[4]+m*S[8]),v+=A*(d*S[1]+p*S[5]+m*S[9]),b+=A*(d*S[2]+p*S[6]+m*S[10])}k&&(f=w,h=y,u=M,d=x,p=v,m=b)}t.defaultZUp&&([h,u]=[u,-h],[p,m]=[m,-p]),t.flipZ||(u=-u,m=-m);const g=Math.hypot(d,p,m)||1;a[c*3]=f*n,a[c*3+1]=h*n,a[c*3+2]=u*n,i[c*3]=d/g,i[c*3+1]=p/g,i[c*3+2]=m/g}const l=qr(t,e);if(!t.flipZ)for(let c=0;c+2<l.length;c+=3){const f=l[c+1];l[c+1]=l[c+2],l[c+2]=f}return{pos:a,nor:i,indices:l}}async function W2(t,e){const n=jn(e.name),s=(e.prefix??`gmv\\${n}`).replace(/\//g,"\\").replace(/\\+$/,""),o=e.scale??1,r=new Map,a=[],i=e.instances!==!1&&t.instances&&t.flipZ&&!t.defaultZUp?t.instances:null,l=new Set(i?.meshes??[]),c=t.meshes.map((U,_)=>_).filter(U=>!e.hidden?.has(U)&&t.meshes[U].indices.length>=3),f=new Map(c.filter(U=>!l.has(U)).map(U=>[U,H2(t,U,o)])),h=[1/0,1/0,1/0],u=[-1/0,-1/0,-1/0];for(const U of f.values())for(let _=0;_<U.pos.length;_++)h[_%3]=Math.min(h[_%3],U.pos[_]),u[_%3]=Math.max(u[_%3],U.pos[_]);const d=Math.max(u[0]-h[0],u[2]-h[2]),p=[...f.values()].reduce((U,_)=>U+_.indices.length/3,0),m=e.cell??(d>400?64:d>100||p>6e4?32:0),g=[],w=new Map,y=new Map;let M=0,x=0,v=0;for(const U of c){const _=t.meshes[U],R=_.texture||`mesh_${U}`;if(e.xraySource){const O=`${_.texture}|${_.shader??""}|${_.material??""}`;let N=g.findIndex(D=>`${D.texture}|${D.eshader}|${D.gamemtl}`===O);N<0&&(N=g.push({name:`${jn(_.texture.split("\\").pop()??"s")}_${g.length}`,texture:_.texture,eshader:_.shader||"default",gamemtl:_.material||"default"})-1),w.set(U,N);continue}let L=y.get(R);if(!L){const O=jn(R.split(/[\\/]/).pop()??R);let N=O,D=1;for(;[...y.values()].includes(`${s}\\${N}`);)N=`${O}_${D++}`;L=`${s}\\${N}`,y.set(R,L);const F=e.texture?await e.texture(U).catch(()=>null):null,C=`gamedata/textures/${L.replace(/\\/g,"/")}`;if(F?.dds&&F.dds.length>128){r.set(`${C}.dds`,F.dds);const P=new DataView(F.dds.buffer,F.dds.byteOffset),I=String.fromCharCode(F.dds[84],F.dds[85],F.dds[86],F.dds[87]);r.set(`${C}.thm`,eu({width:P.getUint32(16,!0),height:P.getUint32(12,!0),alpha:I!=="DXT1"})),M++}else if(F?.image?.rgba){const P=jm(F.image);r.set(`${C}.dds`,sa({width:F.image.width,height:F.image.height,rgba:F.image.rgba})),r.set(`${C}.thm`,eu({width:F.image.width,height:F.image.height,alpha:P})),x++}else v++;g.push({name:N,texture:L})}w.set(U,g.findIndex(O=>O.texture===L))}const b=new Map;for(const[U,_]of f)for(let R=0;R+2<_.indices.length;R+=3){let L="all";if(m>0){const N=_.indices[R]*3,D=_.indices[R+1]*3,F=_.indices[R+2]*3,C=(_.pos[N]+_.pos[D]+_.pos[F])/3,P=(_.pos[N+2]+_.pos[D+2]+_.pos[F+2])/3;L=`${Math.floor((C-h[0])/m)}_${Math.floor((P-h[2])/m)}`}const O=b.get(L)??b.set(L,new Map).get(L);(O.get(U)??O.set(U,[]).get(U)).push(R)}const k=[];let E=0;for(const[U,_]of b){const R=[];for(const[F,C]of _){const P=f.get(F),I=t.meshes[F],B=new Map,z=[],j=[],X=[],J=[];for(const G of C)for(let H=0;H<3;H++){const K=P.indices[G+H];let Q=B.get(K);Q===void 0&&(Q=z.length/3,B.set(K,Q),z.push(P.pos[K*3],P.pos[K*3+1],P.pos[K*3+2]),j.push(P.nor[K*3],P.nor[K*3+1],P.nor[K*3+2]),X.push(I.uv[K*2],I.uv[K*2+1])),J.push(Q)}E+=C.length,R.push({name:jn(I.name||`mesh_${F}`),pos:z,nor:j,uv:X,indices:J,surface:w.get(F)})}const L=m>0?`${n}_${U}`:n,O=`${s}\\${L}`,N=[...new Set(R.map(F=>F.surface))],D=new Map(N.map((F,C)=>[F,C]));for(const F of R)F.surface=D.get(F.surface);r.set(`rawdata/objects/${O.replace(/\\/g,"/")}.object`,nu({meshes:R,surfaces:N.map(F=>g[F])})),k.push({name:L,reference:O})}const A=k.map(U=>({...U,position:[0,0,0]}));let S=0,$=0,T=0;if(i){const U=new Map,_=new Set(i.placements.map(D=>D.proto)),R=[],L=new Set,O=(D,F)=>i.protos[D].parts.filter(C=>!e.hidden?.has(C.mesh)&&w.has(C.mesh)&&C.indices.length>=3).map((C,P)=>{const I=C.pos.length/3,B=[],z=[];for(let X=0;X<I;X++){let J=C.pos[X*3],G=C.pos[X*3+1],H=C.pos[X*3+2],K=C.nor[X*3],Q=C.nor[X*3+1],V=C.nor[X*3+2];if(F){[J,G,H]=[J*F[0]+G*F[4]+H*F[8]+F[12],J*F[1]+G*F[5]+H*F[9]+F[13],J*F[2]+G*F[6]+H*F[10]+F[14]],[K,Q,V]=[K*F[0]+Q*F[4]+V*F[8],K*F[1]+Q*F[5]+V*F[9],K*F[2]+Q*F[6]+V*F[10]];const W=Math.hypot(K,Q,V)||1;K/=W,Q/=W,V/=W}B.push(J*o,G*o,H*o),z.push(K,Q,V)}const j=Array.from(C.indices);if(F)for(let X=0;X+2<j.length;X+=3){const J=j[X+1];j[X+1]=j[X+2],j[X+2]=J}return{name:`part_${P}`,pos:B,nor:z,uv:C.uv,indices:j,surface:w.get(C.mesh)}}),N=(D,F)=>{const C=[...new Set(F.map(I=>I.surface))],P=new Map(C.map((I,B)=>[I,B]));for(const I of F)I.surface=P.get(I.surface);r.set(`rawdata/objects/${D.replace(/\\/g,"/")}.object`,nu({meshes:F,surfaces:C.map(I=>g[I])}))};for(const D of i.placements){if(!_.has(D.proto)||D.hidden)continue;const F=D.pose,C=[F[0],F[1],F[2],F[4],F[5],F[6],F[8],F[9],F[10]],P=[Math.hypot(C[0],C[1],C[2]),Math.hypot(C[3],C[4],C[5]),Math.hypot(C[6],C[7],C[8])];if(C[0]*(C[4]*C[8]-C[5]*C[7])-C[1]*(C[3]*C[8]-C[5]*C[6])+C[2]*(C[3]*C[7]-C[4]*C[6])<0){const X=O(D.proto,F);R.push(...X),T++;for(const J of X)S+=J.indices.length/3;continue}let B=U.get(D.proto);if(B===void 0){const X=O(D.proto,null);if(!X.length){U.set(D.proto,"");continue}let J=jn(i.protos[D.proto].name.split(/[\\/]/).pop()??"obj"),G=J,H=1;for(;L.has(G);)G=`${J}_${H++}`;L.add(G),B=`${s}\\props\\${G}`,N(B,X),U.set(D.proto,B),$++}if(!B)continue;for(const X of i.protos[D.proto].parts)S+=X.indices.length/3;let z=jn(D.name),j=1;for(;L.has(z);)z=`${jn(D.name)}_${j++}`;L.add(z),A.push({name:z,reference:B,position:[F[12]*o,F[13]*o,F[14]*o],matrix:C.map((X,J)=>X/P[Math.floor(J/3)]),scale:P})}if(R.length){const D=`${s}\\${n}_mirrored`;N(D,R),A.push({name:`${n}_mirrored`,reference:D,position:[0,0,0]})}}for(const[U,_]of X2({name:n,objects:A}))r.set(`rawdata/levels/${U}`,_);if(a.push(`Объектов .object: ${k.length}${m>0?` (ячейки ${m} м)`:""}, треугольников: ${E}, размер сцены: ${(u[0]-h[0]).toFixed(1)} × ${(u[1]-h[1]).toFixed(1)} × ${(u[2]-h[2]).toFixed(1)} м`,...i?[`Объекты уровня: ${i.placements.length} экземпляров, ${$} моделей в ${s}\\props (треугольников ${S})${T?`; зеркальных ${T} — влиты в ${n}_mirrored`:""}`]:[],e.xraySource?`Текстуры, шейдеры и материалы — игровые (${g.length} поверхностей), файлы текстур не копируются`:`Текстуры: ${M} DDS скопировано, ${x} перекодировано в DDS без сжатия${v?`, ${v} не найдено (объект ссылается на имя, файл положите сами)`:""}`,`Сцена: rawdata/levels/${n}.level — откройте в LevelEditor, добавьте свет, секторы/порталы и спавн, затем соберите xrLC.`),!e.xraySource){a.push("Шейдеры поверхностей — «default», материал — «default»: настройте в редакторе (стекло, листва, двусторонние грани).");const U=[...new Set(c.map(_=>t.meshes[_].material).filter(_=>!!_))];U.length&&a.push(`Материалы исходника (для настройки game material): ${U.slice(0,30).join(", ")}${U.length>30?"…":""}`)}return t.skeleton&&a.push("Скелетная модель выгружена в bind-позе как статика (кости и анимации в .object пока не пишутся)."),t.flipZ||a.push("Координаты переведены в левостороннюю систему X-Ray (Z отражён, обход граней развёрнут)."),{files:r,report:a}}const K2=Object.freeze(Object.defineProperty({__proto__:null,exportXraySdk:W2},Symbol.toStringTag,{value:"Module"})),Y2=["meshes/characters/_male/skeleton.nif","meshes/actors/character/character assets/skeleton.nif"];async function Z2(t,e){const n=a=>a.buffer.slice(a.byteOffset,a.byteOffset+a.byteLength),s=await e.sibling("skeleton.nif");if(s)return{skeleton:s,skeletonName:"skeleton.nif"};const o=t.toLowerCase().replace(/\\/g,"/").split("/").slice(0,-1),r=new Set;for(let a=o.length-1;a>=1;a--)r.add([...o.slice(0,a),"skeleton.nif"].join("/"));for(const a of[...r,...Y2]){const i=await e.readPath(a).catch(()=>null);if(i)return{skeleton:n(i),skeletonName:a}}return null}async function q2(t,e){const{convertNif:n,hasSkin:s}=await Promise.resolve().then(()=>g4),o=await t.arrayBuffer(),r=s(o)&&!/^skeleton/i.test(t.name)?await Z2(t.path??"",e):null;return n(o,r??{})}const au=Object.freeze(Object.defineProperty({__proto__:null,loadNif:q2},Symbol.toStringTag,{value:"Module"}));async function Q2(t){const e=new DataView(await t.slice(0,36).arrayBuffer());if(e.byteLength<36||e.getUint32(0,!0)!==4281154)throw new Error(`${t.name}: не BSA (TES4+)`);const n=e.getUint32(4,!0),s=e.getUint32(8,!0),o=e.getUint32(12,!0),r=e.getUint32(16,!0),a=e.getUint32(20,!0),i=e.getUint32(24,!0),l=e.getUint32(28,!0);if(![103,104,105].includes(n))throw new Error(`${t.name}: версия BSA ${n} не поддерживается`);if(!(o&1)||!(o&2))throw new Error(`${t.name}: в архиве нет имён папок/файлов`);const c=n===105?24:16,f=r*c+i+r+a*16+l,h=await ht(t,s,s+f),u=new DataView(h.buffer,h.byteOffset,h.byteLength);let d=0;const p=[];for(let M=0;M<r;M++)p.push(u.getUint32(d+8,!0)),d+=c;const m=[];for(let M=0;M<r;M++){const x=h[d],v=Ln(h,d+1,d+x);d+=1+x;for(let b=0;b<p[M];b++)m.push({dir:v,size:u.getUint32(d+8,!0),off:u.getUint32(d+12,!0)}),d+=16}const g=!!(o&4),w=!!(o&256)&&n!==103,y=[];for(const M of m){let x=d;for(;h[x]!==0&&x<h.length;)x++;const v=Ln(h,d,x);d=x+1,y.push({path:gt(M.dir?M.dir+"\\"+v:v),off:M.off,size:M.size&1073741823,compressed:g!==!!(M.size&1073741824)})}return Qt({name:t.name,kind:"bsa",version:n,entries:y,async read(M){const x=await ht(t,M.off,M.off+M.size);let v=w?1+x[0]:0;if(!M.compressed)return x.subarray(v);const b=new DataView(x.buffer,x.byteOffset).getUint32(v,!0);return v+=4,n===105?L2(x.subarray(v),b):Lo(x.subarray(v))}})}const iu=Object.freeze(Object.defineProperty({__proto__:null,openBsa:Q2},Symbol.toStringTag,{value:"Module"})),Yt={STRUCT:1,EXT:3,MATERIAL:7,MATLIST:8,FRAMELIST:14,GEOMETRY:15,CLUMP:16,ATOMIC:20,GEOLIST:26,SKIN:278,HANIM:286,BINMESH:1294,FRAMENAME:39056126},J2=/(_dam|_vlo)$/i;function tg(t,e){const n=we(t,e.start,e.end),s=n.find(c=>c.type===Yt.STRUCT),o=new DataView(t),r=new Uint8Array(t);if(!s)throw new Error("DFF: нет структуры списка кадров");const a=o.getUint32(s.start,!0),i=[];let l=s.start+4;for(let c=0;c<a;c++,l+=56){const f=[];for(let h=0;h<12;h++)f.push(o.getFloat32(l+h*4,!0));i.push({m:f,parent:o.getInt32(l+48,!0),name:"",hanim:null})}return n.filter(c=>c.type===Yt.EXT).forEach((c,f)=>{const h=i[f];if(h){for(const u of we(t,c.start,c.end))if(u.type===Yt.FRAMENAME)h.name=Uo(r,u.start,u.end);else if(u.type===Yt.HANIM){const d=o.getInt32(u.start+4,!0),p=o.getUint32(u.start+8,!0),m=[];if(p>0)for(let g=0;g<p;g++)m.push({nodeId:o.getInt32(u.start+20+g*12,!0)});h.hanim={nodeId:d,hierarchy:m}}}}),i}function eg(t,e){const n=we(t,e.start,e.end),s=n.find(u=>u.type===Yt.STRUCT),o=new DataView(t),r=new Uint8Array(t);if(!s)return[];const a=o.getUint32(s.start,!0),i=[];for(let u=0;u<a;u++)i.push(o.getInt32(s.start+4+u*4,!0));const l=n.filter(u=>u.type===Yt.MATERIAL).map(u=>{const d=we(t,u.start,u.end),p=d.find(y=>y.type===Yt.STRUCT),m=[r[p.start+4]/255,r[p.start+5]/255,r[p.start+6]/255],g=o.getUint32(p.start+12,!0);let w="";if(g){const y=d.find(M=>M.type===6);if(y){const M=we(t,y.start,y.end).filter(x=>x.type===2);M[0]&&(w=Uo(r,M[0].start,M[0].end).toLowerCase())}}return{color:m,tex:w}}),c=()=>({color:[.7,.7,.7],tex:""});let f=0;const h=[];for(let u=0;u<a;u++)h.push(i[u]<0?l[f++]??c():h[i[u]]??c());return h}function ng(t,e,n){const s=new DataView(t),o=new Uint8Array(t),r=o[e.start],a=o[e.start+1];let i=e.start+4+a;if(i+n*20+r*64>e.end)return null;const l=o.slice(i,i+n*4);i+=n*4;const c=new Float32Array(n*4);for(let h=0;h<n*4;h++)c[h]=s.getFloat32(i+h*4,!0);i+=n*16;const f=[];for(let h=0;h<r;h++){const u=new Float64Array(16);for(let d=0;d<16;d++)u[d]=s.getFloat32(i+h*64+d*4,!0);f.push(u)}return{nb:r,bones:l,weights:c,inv:f}}function sg(t,e,n){const s=we(t,e.start,e.end),o=s.find(E=>E.type===Yt.STRUCT),r=new DataView(t);if(!o)throw new Error("нет структуры геометрии");let a=o.start;const i=r.getUint32(a,!0),l=r.getUint32(a+4,!0),c=r.getUint32(a+8,!0),f=r.getUint32(a+12,!0);if(a+=16,n<212992&&(a+=12),i&16777216)throw new Error("нативная (консольная) геометрия не поддерживается");i&8&&(a+=c*4);let h=i>>>16&255;h||(h=i&4?1:i&128?2:0);const u=new Float32Array(c*2);for(let E=0;E<h;E++){if(E===0)for(let A=0;A<c*2;A++)u[A]=r.getFloat32(a+A*4,!0);a+=c*8}const d=[],p=[];for(let E=0;E<l;E++,a+=8){const A=r.getUint16(a,!0),S=r.getUint16(a+2,!0),$=r.getUint16(a+4,!0),T=r.getUint16(a+6,!0);d.push(S,A,T),p.push($)}if(f<1)throw new Error("нет morph target с вершинами");a+=16;const m=r.getUint32(a,!0),g=r.getUint32(a+4,!0);if(a+=8,!m)throw new Error("в геометрии нет вершин");const w=new Float32Array(c*3);for(let E=0;E<c*3;E++)w[E]=r.getFloat32(a+E*4,!0);a+=c*12;let y;if(g){y=new Float32Array(c*3);for(let E=0;E<c*3;E++)y[E]=r.getFloat32(a+E*4,!0)}else y=ge(w,d);const M=s.find(E=>E.type===Yt.MATLIST),x=M?eg(t,M):[];let v=null,b=null;const k=s.find(E=>E.type===Yt.EXT);if(k){for(const E of we(t,k.start,k.end))if(E.type===Yt.BINMESH){const A=r.getUint32(E.start,!0)&1,S=r.getUint32(E.start+4,!0),$=[];let T=E.start+12;for(let U=0;U<S&&T+8<=E.end;U++){const _=r.getUint32(T,!0),R=r.getUint32(T+4,!0);T+=8;const L=new Uint32Array(_);for(let O=0;O<_;O++)L[O]=r.getUint32(T+O*4,!0);T+=_*4,$.push({mat:R,idx:L})}v={strip:A,meshes:$}}else if(E.type===Yt.SKIN)try{b=ng(t,E,c)}catch{b=null}}return{nv:c,pos:w,nor:y,uv:u,tris:d,triMat:p,mats:x,bin:v,skin:b}}function og(t){const e=we(t,0,t.byteLength).find(y=>y.type===Yt.CLUMP);if(!e)throw new Error("Не DFF: нет Clump");const n=e.ver,s=we(t,e.start,e.end),o=s.find(y=>y.type===Yt.FRAMELIST),r=s.find(y=>y.type===Yt.GEOLIST);if(!o||!r)throw new Error("В DFF нет списка кадров или геометрий");const a=tg(t,o),i=[],l=we(t,r.start,r.end).filter(y=>y.type===Yt.GEOMETRY).map((y,M)=>{try{return sg(t,y,n)}catch(x){return i.push(`геометрия #${M}: ${x.message}`),null}}),c=a.map(y=>{const M=new Float64Array(16);for(let x=0;x<3;x++)M[x]=y.m[x],M[4+x]=y.m[3+x],M[8+x]=y.m[6+x],M[12+x]=y.m[9+x];return M[15]=1,{name:y.name,parent:y.parent,local:M,offset:null}}),f=a.find(y=>y.hanim&&y.hanim.hierarchy.length),h=f?f.hanim.hierarchy.map(y=>a.findIndex(M=>M.hanim&&M.hanim.nodeId===y.nodeId)):[],u=[],d=new DataView(t);let p=0;for(const y of s.filter(M=>M.type===Yt.ATOMIC)){const M=we(t,y.start,y.end).find(T=>T.type===Yt.STRUCT);if(!M)continue;const x=d.getUint32(M.start,!0),v=d.getUint32(M.start+4,!0),b=l[v],k=a[x];if(!b||!k||J2.test(k.name))continue;const E=new Float32Array(b.nv*4),A=new Float32Array(b.nv*4),S=b.skin&&h.length&&b.skin.nb<=h.length?b.skin:null;if(S){p++,S.inv.forEach((T,U)=>{const _=h[U];_>=0&&!c[_].offset&&(c[_].offset=T)});for(let T=0;T<b.nv*4;T++){const U=h[S.bones[T]];E[T]=U>=0?U:x,A[T]=S.weights[T]}}else for(let T=0;T<b.nv;T++)E[T*4]=x,A[T*4]=1;let $;if(b.bin){const T=b.bin;$=T.meshes.map(U=>({mat:U.mat,tris:T.strip?No(U.idx):Array.from(U.idx)}))}else{const T=new Map;b.triMat.forEach((U,_)=>{T.has(U)||T.set(U,[]),T.get(U).push(b.tris[_*3],b.tris[_*3+1],b.tris[_*3+2])}),$=[...T].map(([U,_])=>({mat:U,tris:_}))}for(const T of $){if(!T.tris.length)continue;const U=b.mats[T.mat]??{color:[.7,.72,.76],tex:""};u.push({name:`${k.name||"frame "+x}${$.length>1?" #"+T.mat:""}`,texture:U.tex,color:U.color,shader:"",vertexCount:b.nv,pos:b.pos,nor:b.nor,uv:b.uv,bone:E,weight:A,indices:$e(b.nv,T.tris),skinned:!0})}}if(!u.length)throw new Error("В DFF не найдено геометрии"+(i.length?" ("+i.join("; ")+")":""));const m=new Kt(c),g=Jt(m,u),w=[`RenderWare 0x${n.toString(16)}, кадров: ${a.length}`,`Мешей: ${u.length}, вершин (с повторами): ${le(u)}, треугольников: ${Ut(u)}`,p?`Скин: ${p} геом., костей: ${h.length} (анимации IFP — следующая часть)`:"Без скелета"];return i.length&&w.push("Замечания:",...i.map(y=>" • "+y)),{meshes:u,skeleton:m,motions:[],bounds:g,flipZ:!1,defaultZUp:!0,cameraYaw:Math.PI,info:w}}const rg=Object.freeze(Object.defineProperty({__proto__:null,convertDff:og},Symbol.toStringTag,{value:"Module"})),Ho=2048,lu=(t,e)=>{let n="";for(let s=0;s<24&&t[e+s];s++)n+=String.fromCharCode(t[e+s]);return n.toLowerCase()};async function ag(t,e){const n=await ht(t,0,8),s=String.fromCharCode(n[0],n[1],n[2],n[3])==="VER2",o=[];if(s){const r=new DataView(n.buffer,n.byteOffset).getUint32(4,!0),a=await ht(t,8,8+r*32),i=new DataView(a.buffer,a.byteOffset,a.byteLength);for(let l=0;l<r;l++){const c=l*32,f=i.getUint16(c+4,!0),h=i.getUint16(c+6,!0);o.push({path:lu(a,c+8),off:i.getUint32(c,!0)*Ho,size:(h||f)*Ho})}}else{if(!e)throw new Error(`${t.name}: для IMG v1 (GTA III/VC) выберите вместе с ним одноимённый .dir`);const r=new Uint8Array(await e.arrayBuffer()),a=new DataView(r.buffer);for(let i=0;i+32<=r.length;i+=32)o.push({path:lu(r,i+8),off:a.getUint32(i,!0)*Ho,size:a.getUint32(i+4,!0)*Ho})}return Qt({name:t.name,kind:"img",version:s?2:1,entries:o,read:r=>ht(t,r.off,r.off+r.size),textureKeys:()=>[]})}const ig=Object.freeze(Object.defineProperty({__proto__:null,openImg:ag},Symbol.toStringTag,{value:"Module"}));function lg(t,e){const n=[];return t.forEach((s,o)=>{const r=s.replace(/\d+$/,"")||s,a=n[n.length-1];a&&a.name===r?(a.count++,a.duration=a.count/e):n.push({kind:"vertex",name:r,start:o,count:1,fps:e,duration:1/e})}),n.filter(s=>s.count>1).length?n:[{kind:"vertex",name:"все кадры",start:0,count:t.length,fps:e,duration:t.length/e}]}function cg(t){return(e,n,s)=>{const o=e&&e.kind==="vertex"?e:null;let r=0,a=0,i=0;if(o){const l=o.count;let c=n*o.fps;c=s?(c%l+l)%l:Math.min(Math.max(c,0),l-1);const f=Math.floor(c);i=c-f,r=o.start+f,a=o.start+(s?(f+1)%l:Math.min(f+1,l-1))}for(const l of t)for(const[c,f]of[[l.pos,l.frames.pos],[l.nor,l.frames.nor]]){const h=f[r],u=f[a];for(let d=0;d<c.length;d++)c[d]=h[d]+(u[d]-h[d])*i}}}function Ea(t,e,n,s){const o=e.length>1?lg(e,n):[];return{meshes:t,skeleton:null,motions:o,bounds:dt(t),flipZ:!1,defaultZUp:!0,cameraYaw:Math.PI/2,info:s,animate:cg(t)}}function Aa(t,e,n,s,o){const r=n.length/2,a=o.map(i=>ge(i,s));return{name:t,texture:e,shader:"",vertexCount:r,pos:Float32Array.from(o[0]),nor:Float32Array.from(a[0]),uv:n,bone:new Float32Array(r*4),weight:new Float32Array(r*4),indices:$e(r,s),skinned:!1,dynamic:!0,frames:{pos:o,nor:a}}}function fg(t){const e=new DataView(t),n=new Uint8Array(t);if(t.byteLength<68||e.getUint32(0,!0)!==844121161||e.getInt32(4,!0)!==8)throw new Error("Не MD2 (IDP2 v8)");const s=E=>e.getInt32(E,!0),o=s(8),r=s(12),a=s(16),i=s(20),l=s(32),c=s(40),f=s(44),h=s(48),u=s(52),d=s(56),p=new Map,m=[],g=[],w=[];for(let E=0;E<l;E++)for(let A=0;A<3;A++){const S=e.getUint16(u+E*12+A*2,!0),$=e.getUint16(u+E*12+6+A*2,!0),T=S+$*65536;let U=p.get(T);U===void 0&&(U=m.length,p.set(T,U),m.push(S),g.push($)),w.push(U)}const y=m.length,M=new Float32Array(y*2);for(let E=0;E<y;E++)M[E*2]=e.getInt16(h+g[E]*4,!0)/o,M[E*2+1]=e.getInt16(h+g[E]*4+2,!0)/r;const x=[],v=[];for(let E=0;E<c;E++){const A=d+E*a,S=[e.getFloat32(A,!0),e.getFloat32(A+4,!0),e.getFloat32(A+8,!0)],$=[e.getFloat32(A+12,!0),e.getFloat32(A+16,!0),e.getFloat32(A+20,!0)];v.push(Ot(n,A+24,16));const T=new Float32Array(y*3);for(let U=0;U<y;U++){const _=A+40+m[U]*4;for(let R=0;R<3;R++)T[U*3+R]=n[_+R]*S[R]+$[R]}x.push(T)}if(!x.length)throw new Error("В MD2 нет кадров");const b=i?Ot(n,f,64):"",k=Aa("md2",b,M,w,x);return Ea([k],v,10,[`Quake II MD2: вершин ${y}, треугольников ${l}, кадров ${c}`,b?`Скин: ${b}`:"Скин не указан (выберите текстуру вручную)"])}function ug(t,e){const n=new DataView(t),s=new Uint8Array(t);if(t.byteLength<84||n.getUint32(0,!0)!==1330660425||n.getInt32(4,!0)!==6)throw new Error("Не MDL (IDPO v6)");const o=$=>n.getFloat32($,!0),r=$=>n.getInt32($,!0),a=[o(8),o(12),o(16)],i=[o(20),o(24),o(28)],l=r(48),c=r(52),f=r(56),h=r(60),u=r(64),d=r(68);let p=84,m=null;for(let $=0;$<l;$++){const T=r(p);if(p+=4,T===0)m||(m=s.slice(p,p+c*f)),p+=c*f;else{const U=r(p);p+=4+U*4,m||(m=s.slice(p,p+c*f)),p+=U*c*f}}const g=[];for(let $=0;$<h;$++,p+=12)g.push({seam:r(p),s:r(p+4),t:r(p+8)});const w=[];for(let $=0;$<u;$++,p+=16)w.push({front:r(p),v:[r(p+4),r(p+8),r(p+12)]});const y=new Map,M=[],x=[],v=[];for(const $ of w)for(const T of $.v){const U=!$.front&&g[T].seam,_=T*2+(U?1:0);let R=y.get(_);R===void 0&&(R=M.length,y.set(_,R),M.push(T),x.push((g[T].s+(U?c/2:0)+.5)/c,(g[T].t+.5)/f)),v.push(R)}const b=[],k=[],E=$=>{k.push(Ot(s,$+8,16));const T=new Float32Array(M.length*3);for(let U=0;U<M.length;U++)for(let _=0;_<3;_++)T[U*3+_]=s[$+24+M[U]*4+_]*a[_]+i[_];return b.push(T),$+24+h*4};for(let $=0;$<d;$++){const T=r(p);if(p+=4,T===0)p=E(p);else{const U=r(p);p+=12+U*4;for(let _=0;_<U;_++)p=E(p)}}if(!b.length)throw new Error("В MDL нет кадров");const A=Aa("mdl","",Float32Array.from(x),v,b),S=new Uint8Array(c*f*4);if(m)for(let $=0;$<c*f;$++){const T=m[$];e?(S[$*4]=e[T*3],S[$*4+1]=e[T*3+1],S[$*4+2]=e[T*3+2]):S[$*4]=S[$*4+1]=S[$*4+2]=T,S[$*4+3]=255}return A.image={width:c,height:f,rgba:S},Ea([A],k,10,[`Quake MDL: вершин ${M.length}, треугольников ${u}, кадров ${b.length}`,e?"Палитра: gfx/palette.lmp":"Палитра Quake не найдена (откройте pak0.pak) — текстура в градациях серого"])}const cu=Object.freeze(Object.defineProperty({__proto__:null,loadMd2:fg,loadMdl:ug},Symbol.toStringTag,{value:"Module"}));async function hg(t){const e=new DataView(await t.slice(0,12).arrayBuffer());if(e.byteLength<12||e.getUint32(0,!0)!==1262698832)throw new Error(`${t.name}: не PAK (нет сигнатуры PACK)`);const n=e.getUint32(4,!0),s=e.getUint32(8,!0),o=await ht(t,n,n+s),r=new DataView(o.buffer,o.byteOffset,o.byteLength),a=[];for(let i=0;i+64<=s;i+=64){let l="";for(let c=0;c<56&&o[i+c];c++)l+=String.fromCharCode(o[i+c]);a.push({path:gt(l),off:r.getUint32(i+56,!0),size:r.getUint32(i+60,!0)})}return Qt({name:t.name,kind:"pak",version:1,entries:a,read:i=>ht(t,i.off,i.off+i.size)})}const dg=Object.freeze(Object.defineProperty({__proto__:null,openPak:hg},Symbol.toStringTag,{value:"Module"}));function pg(t){const e=new DataView(t),n=new Uint8Array(t);if(t.byteLength<108||e.getUint32(0,!0)!==860898377||e.getInt32(4,!0)!==15)throw new Error("Не MD3 (IDP3 v15)");const s=e.getInt32(76,!0),o=e.getInt32(80,!0),r=e.getInt32(84,!0),a=e.getInt32(100,!0),i=[];let l=a,c=0;for(let u=0;u<r;u++){const d=$=>e.getInt32(l+$,!0),p=Ot(n,l+4,64),m=d(72),g=d(76),w=d(80),y=d(84),M=d(88),x=d(92),v=d(96),b=d(100),k=d(104),E=[];for(let $=0;$<y*3;$++)E.push(e.getInt32(l+M+$*4,!0));const A=new Float32Array(w*2);for(let $=0;$<w*2;$++)A[$]=e.getFloat32(l+v+$*4,!0);const S=[];for(let $=0;$<m;$++){const T=new Float32Array(w*3);for(let U=0;U<w;U++){const _=l+b+($*w+U)*8;for(let R=0;R<3;R++)T[U*3+R]=e.getInt16(_+R*2,!0)/64}S.push(T)}i.push(Aa(p||`surface ${u}`,g?Ot(n,l+x,64):"",A,E,S)),c+=y,l+=k}if(!i.length)throw new Error("В MD3 нет поверхностей");const f=Array.from({length:s},()=>""),h=Ea(i,f,15,[`Quake III MD3: поверхностей ${i.length}, треугольников ${c}, кадров ${s}, тегов ${o}`,"Теги (привязка частей игрока/оружия) не используются."]);return s>1&&(h.motions=[{kind:"vertex",name:"все кадры",start:0,count:s,fps:15,duration:s/15}]),h}const mg=Object.freeze(Object.defineProperty({__proto__:null,loadMd3:pg},Symbol.toStringTag,{value:"Module"})),gg=/^"([^"]+)"\s+(-?\d+)\s+\(\s*(\S+)\s+(\S+)\s+(\S+)\s*\)\s+\(\s*(\S+)\s+(\S+)\s+(\S+)\s*\)/,fu=t=>t.split(/\r?\n/).map(e=>e.replace(/\/\/.*$/,"").trim()).filter(Boolean),uu=(t,e,n)=>-Math.sqrt(Math.max(0,1-t*t-e*e-n*n)),wg=(t,e)=>{const[n,s,o,r]=t,a=o*e[2]-r*e[1]+n*e[0],i=r*e[0]-s*e[2]+n*e[1],l=s*e[1]-o*e[0]+n*e[2];return[e[0]+2*(o*l-r*i),e[1]+2*(r*a-s*l),e[2]+2*(s*i-o*a)]};function hu(t){const e=fu(t),n=[],s=[];for(let o=0;o<e.length;o++)if(/^joints\s*\{/.test(e[o]))for(o++;o<e.length&&e[o]!=="}";o++){const r=gg.exec(e[o]);r&&n.push({name:r[1],parent:+r[2],pos:[+r[3],+r[4],+r[5]],q:[uu(+r[6],+r[7],+r[8]),+r[6],+r[7],+r[8]]})}else if(/^mesh\s*\{/.test(e[o])){const r={shader:"",verts:[],tris:[],weights:[]};for(o++;o<e.length&&e[o]!=="}";o++){const a=e[o];let i;(i=/^shader\s+"([^"]*)"/.exec(a))?r.shader=i[1]:(i=/^vert\s+(\d+)\s+\(\s*(\S+)\s+(\S+)\s*\)\s+(\d+)\s+(\d+)/.exec(a))?r.verts[+i[1]]={u:+i[2],v:+i[3],start:+i[4],count:+i[5]}:(i=/^tri\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/.exec(a))?r.tris[+i[1]]=[+i[2],+i[3],+i[4]]:(i=/^weight\s+(\d+)\s+(\d+)\s+(\S+)\s+\(\s*(\S+)\s+(\S+)\s+(\S+)\s*\)/.exec(a))&&(r.weights[+i[1]]={joint:+i[2],bias:+i[3],pos:[+i[4],+i[5],+i[6]]})}s.push(r)}if(!n.length||!s.length)throw new Error("Не MD5mesh: нет joints/mesh");return{joints:n,meshes:s}}function du(t,e){const n=fu(t),o=(c=>{for(const f of n){const h=c.exec(f);if(h)return+h[1]}return 0})(/^frameRate\s+(\d+)/)||24,r=[],a=[],i=[];for(let c=0;c<n.length;c++){const f=/^(hierarchy|bounds|baseframe|frame)\s*(\d*)\s*\{/.exec(n[c]);if(!f)continue;const h=[];for(c++;c<n.length&&n[c]!=="}";c++)if(f[1]==="hierarchy"){const u=/^"([^"]+)"\s+(-?\d+)\s+(\d+)\s+(\d+)/.exec(n[c]);u&&r.push({name:u[1],flags:+u[3],start:+u[4]})}else if(f[1]==="baseframe"){const u=/^\(\s*(\S+)\s+(\S+)\s+(\S+)\s*\)\s*\(\s*(\S+)\s+(\S+)\s+(\S+)\s*\)/.exec(n[c]);u&&a.push({p:[+u[1],+u[2],+u[3]],q:[+u[4],+u[5],+u[6]]})}else if(f[1]==="frame")for(const u of n[c].split(/\s+/))h.push(+u);f[1]==="frame"&&(i[+f[2]]=h)}if(!r.length||!i.length)throw new Error("Не MD5anim: нет hierarchy/frame");const l=new Map;return r.forEach((c,f)=>{const h=e.get(c.name.toLowerCase())??f,u=[],d=[];i.forEach((p,m)=>{const g=a[f].p.slice(),w=a[f].q.slice();let y=c.start;c.flags&1&&(g[0]=p[y++]),c.flags&2&&(g[1]=p[y++]),c.flags&4&&(g[2]=p[y++]),c.flags&8&&(w[0]=p[y++]),c.flags&16&&(w[1]=p[y++]),c.flags&32&&(w[2]=p[y++]),u.push([m,g]),d.push([m,[uu(w[0],w[1],w[2]),w[0],w[1],w[2]]])}),l.set(h,{pos:u,rot:d,scl:null})}),{fps:o,nFrames:i.length,chan:l}}function yg(t){const{joints:e,meshes:n}=hu(t),s=e.map(c=>Xe(c.q,c.pos)),o=e.map((c,f)=>({name:c.name,parent:c.parent,offset:Vt(s[f]),local:c.parent>=0?Lt(Vt(s[c.parent]),s[f]):s[f]})),r=n.map((c,f)=>{const h=c.verts.length,u=new Float32Array(h*3),d=new Float32Array(h*2),p=new Float32Array(h*4),m=new Float32Array(h*4);for(let w=0;w<h;w++){const y=c.verts[w];if(!y)continue;d[w*2]=y.u,d[w*2+1]=y.v;const M=c.weights.slice(y.start,y.start+y.count).filter(Boolean),x=[0,0,0];for(const k of M){const E=e[k.joint],A=wg(E.q,k.pos);for(let S=0;S<3;S++)x[S]+=k.bias*(E.pos[S]+A[S])}u.set(x,w*3);const v=M.slice().sort((k,E)=>E.bias-k.bias).slice(0,4),b=v.reduce((k,E)=>k+E.bias,0)||1;v.forEach((k,E)=>{p[w*4+E]=k.joint,m[w*4+E]=k.bias/b})}const g=[];for(const w of c.tris)w&&g.push(w[0],w[1],w[2]);return{name:`mesh ${f}`,texture:c.shader,shader:c.shader,vertexCount:h,pos:u,nor:ge(u,g),uv:d,bone:p,weight:m,indices:$e(h,g),skinned:!0}}),a=new Kt(o),i=Jt(a,r),l=new Map(e.map((c,f)=>[c.name.toLowerCase(),f]));return{meshes:r,skeleton:a,motions:[],bounds:i,flipZ:!1,defaultZUp:!0,cameraYaw:Math.PI/2,info:[`MD5mesh: костей ${e.length}, мешей ${r.length}, треугольников ${Ut(r)}`,"Анимации: .md5anim (из папки модели в pk4 подгружаются сами)"],loadMotions:async c=>{const f=du(Wt(await c.arrayBuffer()),l);return[{kind:"node",name:c.name.replace(/\.[^.]+$/,""),tps:f.fps,chan:f.chan,duration:f.nFrames/f.fps,frames:f.nFrames}]}}}const bg=Object.freeze(Object.defineProperty({__proto__:null,loadMd5:yg,parseMd5anim:du,parseMd5mesh:hu},Symbol.toStringTag,{value:"Module"}));function $a(t){const e=new DataView(t),n=new Uint8Array(t),s={};let o=0;for(;o+32<=t.byteLength;){const r=Ot(n,o,20),a=e.getInt32(o+24,!0),i=e.getInt32(o+28,!0);if(a<0||i<0)break;s[r]={size:a,count:i,start:o+32},o+=32+a*i}return s}const vg=([t,e,n,s])=>[t,-e,n,-s],xg=([t,e,n,s])=>[t,-e,-n,-s];function Mg(t,e){const n=new DataView(t),s=new Uint8Array(t),o=[];for(let r=0;r<e.count;r++){const a=e.start+r*e.size,i=l=>n.getFloat32(a+76+l*4,!0);o.push({name:Ot(s,a,64),parent:n.getInt32(a+72,!0),q:[i(3),i(0),i(1),i(2)],p:[i(4),i(5),i(6)]})}return o.forEach((r,a)=>{(r.parent===a||r.parent<0)&&(r.parent=-1)}),o}const kg=(t,e)=>t===3||t===1&&e||t===2&&!e,pu=(t,e,n)=>vg(kg(e,n)?xg(t):t);function mu(t,e){const n=[];return t.forEach((s,o)=>{const r=Xe(pu(s.q,e,s.parent<0),[s.p[0],-s.p[1],s.p[2]]);n[o]=s.parent<0?r:Lt(n[s.parent],r)}),n}function gu(t){const e=$a(t),n=new DataView(t),s=new Uint8Array(t),o=e.PNTS0000,r=e.VTXW0000??e.VTXW3200,a=e.FACE0000??e.FACE3200,i=!!e.VTXW3200,l=e.MATT0000;if(!o||!r||!a)throw new Error("Не PSK/PSKX: нет PNTS/VTXW/FACE");const c=new Float32Array(o.count*3);for(let y=0;y<o.count;y++)c[y*3]=n.getFloat32(o.start+y*12,!0),c[y*3+1]=-n.getFloat32(o.start+y*12+4,!0),c[y*3+2]=n.getFloat32(o.start+y*12+8,!0);const f=r.count,h=new Float32Array(f*3),u=new Float32Array(f*2),d=new Uint32Array(f);for(let y=0;y<f;y++){const M=r.start+y*r.size,x=i?n.getUint32(M,!0):n.getUint16(M,!0);d[y]=x,h.set(c.subarray(x*3,x*3+3),y*3),u[y*2]=n.getFloat32(M+4,!0),u[y*2+1]=n.getFloat32(M+8,!0)}const p=new Map;for(let y=0;y<a.count;y++){const M=a.start+y*a.size,x=i?n.getUint32(M,!0):n.getUint16(M,!0),v=i?n.getUint32(M+4,!0):n.getUint16(M+2,!0),b=i?n.getUint32(M+8,!0):n.getUint16(M+4,!0),k=i?s[M+12]:s[M+6];p.has(k)||p.set(k,[]),p.get(k).push(x,v,b)}const m=[];if(l)for(let y=0;y<l.count;y++)m.push(Ot(s,l.start+y*l.size,64));const g=e.REFSKELT?Mg(t,e.REFSKELT):[],w=[];if(e.RAWWEIGHTS&&g.length){const y=e.RAWWEIGHTS;for(let M=0;M<o.count;M++)w[M]=[];for(let M=0;M<y.count;M++){const x=y.start+M*y.size,v=n.getInt32(x+4,!0);w[v]&&w[v].push([n.getInt32(x+8,!0),n.getFloat32(x,!0)])}}return{points:c,pos:h,uv:u,wp:d,byMat:p,mats:m,bones:g,weights:w}}function Eg(t){const e=gu(t),n=e.pos.length/3,s=e.bones.length>0,o=new Float32Array(n*4),r=new Float32Array(n*4);let a=null,i=1;if(s){for(let g=0;g<n;g++){const w=(e.weights[e.wp[g]]??[]).slice().sort((M,x)=>x[1]-M[1]).slice(0,4),y=w.reduce((M,x)=>M+x[1],0)||1;w.forEach(([M,x],v)=>{o[g*4+v]=Math.min(M,e.bones.length-1),r[g*4+v]=x/y}),w.length||(r[g*4]=1)}let u=1/0;const d=Math.max(1,Math.floor(n/1500));for(const g of[1,0,2,3]){const w=mu(e.bones,g);let y=0,M=0;for(let x=0;x<n;x+=d){const v=w[o[x*4]];y+=Math.hypot(e.pos[x*3]-v[12],e.pos[x*3+1]-v[13],e.pos[x*3+2]-v[14]),M++}y/=M||1,y<u*.97&&(u=y,i=g)}const p=mu(e.bones,i),m=e.bones.map((g,w)=>({name:g.name,parent:g.parent,local:g.parent<0?p[w]:Lt(Vt(p[g.parent]),p[w]),offset:Vt(p[w])}));a=new Kt(m)}const l=[];for(const[u,d]of e.byMat)l.push({name:e.mats[u]||`material ${u}`,texture:e.mats[u]??"",shader:"",vertexCount:n,pos:e.pos,nor:ge(e.pos,d),uv:e.uv,bone:o,weight:r,indices:$e(n,d),skinned:!0});if(!l.length)throw new Error("В PSK нет граней");const c=a?Jt(a,l):dt([{pos:e.pos}]),f=new Map(e.bones.map((u,d)=>[u.name.toLowerCase(),d])),h=[`${s?"PSK":"PSKX"}: вершин (wedges) ${n}, треугольников ${Ut(l)}, материалов ${l.length}`];return s&&h.push(`Костей: ${e.bones.length}, трактовка кватернионов: вариант ${i}`,"Анимации: добавьте .psa кнопкой"),{meshes:l,skeleton:a,motions:[],bounds:c,flipZ:!1,defaultZUp:!0,cameraYaw:Math.PI/2,info:h,loadMotions:s?async u=>wu(await u.arrayBuffer(),e.bones,f,i):void 0}}function wu(t,e,n,s){const o=$a(t),r=new DataView(t),a=new Uint8Array(t),i=o.ANIMINFO,l=o.ANIMKEYS,c=o.BONENAMES;if(!i||!l||!c)throw new Error("Не PSA: нет ANIMINFO/ANIMKEYS/BONENAMES");const f=[];for(let d=0;d<c.count;d++)f.push(Ot(a,c.start+d*c.size,64).toLowerCase());const h=f.map(d=>n.get(d)??-1),u=[];for(let d=0;d<i.count;d++){const p=i.start+d*i.size,m=Ot(a,p,64),g=r.getInt32(p+128,!0),w=r.getFloat32(p+152,!0)||30,y=r.getInt32(p+160,!0),M=r.getInt32(p+164,!0);if(g<=0||M<=0)continue;const x=new Map;for(let v=0;v<g;v++){const b=h[v]??-1;if(b<0)continue;const k=[],E=[],A=e[b].parent<0;for(let S=0;S<M;S++){const $=l.start+((y+S)*g+v)*l.size,T=[r.getFloat32($+24,!0),r.getFloat32($+12,!0),r.getFloat32($+16,!0),r.getFloat32($+20,!0)];k.push([S,[r.getFloat32($,!0),-r.getFloat32($+4,!0),r.getFloat32($+8,!0)]]),E.push([S,pu(T,s,A)])}x.set(b,{pos:k,rot:E,scl:null})}u.push({kind:"node",name:m,tps:w,chan:x,duration:M/w,frames:M})}return u}const Ag=Object.freeze(Object.defineProperty({__proto__:null,loadPsk:Eg,parsePsa:wu,parsePsk:gu,readPskChunks:$a},Symbol.toStringTag,{value:"Module"})),$g=2653586369;function yu(t){if(t.length<28)return 0;let e="";for(let s=0;s<28;s+=2)e+=String.fromCharCode(t[s]|t[s+1]<<8);const n=/^Lineage2Ver(\d{3})$/.exec(e);return n?+n[1]:0}function bu(t,e=""){const n=yu(t);if(!n)return t;let s;if(n===111)s=172;else if(n>=120&&n<=121)s=t[28]^193;else throw new Error(`${e||"Файл"}: шифрование Lineage2Ver${n} (RSA поздних хроник) не поддерживается — откройте файлы клиента Prelude/C1–C4 или распакуйте пакет утилитой (l2encdec).`);const o=new Uint8Array(t.length-28);for(let r=0;r<o.length;r++)o[r]=t[28+r]^s;return o}class We{constructor(e,n=0,s=0){this.u8=e,this.ver=n,this.lic=s,this.dv=new DataView(e.buffer,e.byteOffset,e.byteLength)}u8;ver;lic;dv;pos=0;need(e){if(this.pos+e>this.u8.length||e<0)throw new Error(`Unreal: чтение за концом данных (${this.pos}+${e} > ${this.u8.length})`)}u8v(){return this.need(1),this.u8[this.pos++]}u16(){this.need(2);const e=this.dv.getUint16(this.pos,!0);return this.pos+=2,e}i32(){this.need(4);const e=this.dv.getInt32(this.pos,!0);return this.pos+=4,e}u32(){this.need(4);const e=this.dv.getUint32(this.pos,!0);return this.pos+=4,e}f32(){this.need(4);const e=this.dv.getFloat32(this.pos,!0);return this.pos+=4,e}idx(){let e=this.u8v();const n=e&128;let s=e&63;if(e&64){let o=6;for(let r=0;r<4&&(e=this.u8v(),s|=(e&127)<<o,o+=7,!!(e&128));r++);}return n?-s:s}vec(){return[this.f32(),this.f32(),this.f32()]}str(){const e=this.idx();if(!e)return"";if(e>0){this.need(e);const o=new TextDecoder("latin1").decode(this.u8.subarray(this.pos,this.pos+e-1));return this.pos+=e,o}const n=-e;this.need(n*2);let s="";for(let o=0;o<n-1;o++)s+=String.fromCharCode(this.dv.getUint16(this.pos+o*2,!0));return this.pos+=n*2,s}bytes(e){this.need(e);const n=this.u8.subarray(this.pos,this.pos+e);return this.pos+=e,n}arr(e,n=16777216){const s=this.idx();if(s<0||s>n)throw new Error(`Unreal: неверная длина массива ${s} @${this.pos}`);const o=new Array(s);for(let r=0;r<s;r++)o[r]=e(this);return o}}function Sg(t,e,n=""){const s=yu(e),o=s?28:0;let r=0;return s===111?r=172:s>=120&&s<=121?r=e[28]^193:s&&bu(e,n),{size:a=>a-o,read:async(a,i)=>{const l=await t(a+o,i+o);if(!r)return l;const c=new Uint8Array(l.length);for(let f=0;f<l.length;f++)c[f]=l[f]^r;return c}}}class Zs{constructor(e,n){this.fileName=e,this.readRange=n}fileName;readRange;ver=0;lic=0;flags=0;names=[];imports=[];exports=[];static fromBytes(e,n=""){const s=new Zs(n,async(o,r)=>e.subarray(o,r));return s.parseTables(e,0,e,0,e.length),s}static async open(e,n,s=""){const o=new Zs(s,e),r=await e(0,Math.min(n,64)),a=new DataView(r.buffer,r.byteOffset,r.byteLength);if(r.length<36||a.getUint32(0,!0)!==$g)throw new Error(`${s||"Файл"}: не пакет Unreal (нет сигнатуры 0x9E2A83C1)`);const i=a.getInt32(16,!0),l=a.getInt32(24,!0),c=a.getInt32(32,!0),f=a.getInt32(12,!0);if(i<0||i>n||l<0||l>n||c<0||c>n)throw new Error(`${s}: повреждённый заголовок пакета`);let h=Math.min(n,i+Math.max(4096,f*48)),u=await e(i,h);for(let m=0;m<6;m++)try{o.names=[];const g=new We(u);for(let w=0;w<f;w++)o.names.push(g.str()),g.u32();break}catch(g){if(h>=n)throw g;h=Math.min(n,i+(h-i)*4),u=await e(i,h)}const d=Math.min(c,l),p=await e(d,n);return o.parseTables(r,-1,p,d,n),o}parseTables(e,n,s,o,r){const a=new We(e);if(a.u32(),this.ver=a.u16(),this.lic=a.u16(),this.flags=a.u32(),this.ver<61||this.ver>140)throw new Error(`${this.fileName}: версия пакета ${this.ver} — поддерживается Unreal Engine 2 (61–140)`);const i=a.i32(),l=a.i32(),c=a.i32(),f=a.i32(),h=a.i32(),u=a.i32();if([i,c,h].some(p=>p<0||p>4e6)||[l,f,u].some(p=>p<0||p>r))throw new Error(`${this.fileName}: повреждённый заголовок пакета`);if(n>=0){a.pos=l,this.names=[];for(let p=0;p<i;p++)this.names.push(a.str()),a.u32()}const d=new We(s);d.pos=u-o;for(let p=0;p<h;p++)this.imports.push({classPackage:this.names[d.idx()]??"",className:this.names[d.idx()]??"",outer:d.i32(),name:this.names[d.idx()]??""});d.pos=f-o;for(let p=0;p<c;p++){const m=d.idx(),g=d.idx(),w=d.i32(),y=this.names[d.idx()]??"",M=d.u32(),x=d.idx(),v=x>0?d.idx():0;this.exports.push({classIndex:m,superIndex:g,outer:w,name:y,flags:M,size:x,offset:v,className:""})}for(const p of this.exports)p.className=p.classIndex===0?"Class":this.objName(p.classIndex)}objName(e){return e>0?this.exports[e-1]?.name??"":e<0?this.imports[-e-1]?.name??"":""}objPath(e){const n=[];for(let s=0;e&&s<16;s++)if(e>0){const o=this.exports[e-1];if(!o)break;n.unshift(o.name),e=o.outer}else{const o=this.imports[-e-1];if(!o)break;n.unshift(o.name),e=o.outer}return n.join(".")}objClass(e){return e>0?this.exports[e-1]?.className??"":e<0?this.imports[-e-1]?.className??"":""}async readerHead(e,n){return new We(e.size>0?await this.readRange(e.offset,e.offset+Math.min(e.size,n)):new Uint8Array(0),this.ver,this.lic)}async reader(e){return new We(e.size>0?await this.readRange(e.offset,e.offset+e.size):new Uint8Array(0),this.ver,this.lic)}name(e){return this.names[e]??`name#${e}`}}const oe={BYTE:1,INT:2,BOOL:3,FLOAT:4,OBJECT:5,NAME:6,STRING:7,CLASS:8,STRUCT:10,VECTOR:11,ROTATOR:12,STR:13};function Oe(t,e){const n=new Map;for(let s=0;s<4096;s++){const o=t.name(e.idx());if(o==="None")break;const r=e.u8v(),a=r&15,i=r>>4&7,l=!!(r&128),c=a===oe.STRUCT?t.name(e.idx()):"";let f;switch(i){case 0:f=1;break;case 1:f=2;break;case 2:f=4;break;case 3:f=12;break;case 4:f=16;break;case 5:f=e.u8v();break;case 6:f=e.u16();break;default:f=e.i32()}let h=0;if(l&&a!==oe.BOOL){const m=e.u8v();h=m<128?m:m<192?(m&127)<<8|e.u8v():(m&63)<<24|e.u8v()<<16|e.u8v()<<8|e.u8v()}const u=e.pos+(a===oe.BOOL?0:f);let d;const p=new We(e.u8.subarray(e.pos,u),e.ver,e.lic);switch(a){case oe.BOOL:d=l;break;case oe.BYTE:d=f>=1?p.u8v():0;break;case oe.INT:d=f>=4?p.i32():0;break;case oe.FLOAT:d=f>=4?p.f32():0;break;case oe.OBJECT:case oe.CLASS:d=f?p.idx():0;break;case oe.NAME:d=f?t.name(p.idx()):"";break;case oe.STR:case oe.STRING:d=f?p.str():"";break;case oe.VECTOR:d=f>=12?p.vec():[0,0,0];break;case oe.ROTATOR:d=f>=12?[p.i32(),p.i32(),p.i32()]:[0,0,0];break;case oe.STRUCT:d={struct:c,data:p.u8};break;default:d={array:p.u8}}a!==oe.BOOL&&(e.pos=u),n.set(h?`${o}[${h}]`:o,{name:o,type:a,index:h,value:d})}return n}function Tg(t,e){if(!(t.flags&33554432))return;const n=e.idx();e.idx(),e.u32(),e.u32(),e.i32(),n&&e.idx()}const gs=(t,e,n=0)=>{const s=t.get(e)?.value;return typeof s=="number"?s:typeof s=="boolean"?+s:n},an=(t,e)=>{const n=t.get(e)?.value;return typeof n=="number"?n:0},re={P8:0,RGBA7:1,RGB16:2,DXT1:3,RGB8:4,RGBA8:5,NODATA:6,DXT3:7,DXT5:8,L8:9,G16:10,RRRGGGBBB:11},Sa=new Set(["Texture","Cubemap","ScriptedTexture","Shader","FinalBlend","Combiner","TexPanner","TexScaler","TexRotator","TexOscillator","TexEnvMap","TexCoordSource","TexMatrix","TexModifier","VariableTexPanner","MaterialSwitch","MaterialSequence","ColorModifier","OpacityModifier","ConstantColor","FadeColor","ParticleMaterial"]),Ug={Shader:["Diffuse","SelfIllumination","Specular","Opacity"],FinalBlend:["Material"],Combiner:["Material1","Material2","Mask"],MaterialSwitch:["Materials[0]","Materials","Current"],MaterialSequence:["SequenceItems"],ColorModifier:["Material"],OpacityModifier:["Material"],ParticleMaterial:["BitmapMaterial"]},Fg=["Material"];async function Ta(t,e,n,s=0,o={alpha:"none",twoSided:!1}){if(!e||s>12)return null;const r=await n(t,e);if(!r)return null;const{pkg:a,exp:i}=r;if(i.className==="Texture"||i.className==="Cubemap"){const f=Oe(a,await a.reader(i));return o.alpha==="none"&&(f.get("bMasked")?.value===!0||f.get("bAlphaTexture")?.value===!0)&&(o.alpha="test"),{pkg:a,exp:i,...o}}const l=Oe(a,await a.reader(i));if(l.get("TwoSided")?.value===!0&&(o.twoSided=!0),i.className==="FinalBlend"){const f=gs(l,"FrameBufferBlending");(f===2||f===3)&&(o.alpha=o.alpha==="none"?"blend":o.alpha),l.get("AlphaTest")?.value===!0&&(o.alpha="test")}i.className==="Shader"&&an(l,"Opacity")&&(o.alpha=o.alpha==="none"?"test":o.alpha);let c=Ug[i.className]??Fg;if(i.className==="Combiner"){const f=gs(l,"CombineOperation");c=f===0?["Material1","Material2"]:f===7?["Mask","Material2","Material1"]:["Material2","Material1"];const h=an(l,"Material2");f>=2&&f<=6&&h&&/TexEnvMap/.test(a.objClass(h))&&(c=["Material1","Material2"])}for(const f of c){const h=an(l,f);if(!h)continue;const u=await Ta(a,h,n,s+1,o);if(u)return u}return null}async function Ua(t,e){const n=await t.reader(e),s=Oe(t,n),o=gs(s,"Format");t.ver>=123&&t.lic>=16&&n.i32();const r=_g(n);if(!r)throw new Error(`${e.name}: у текстуры нет мипов`);return{fmt:o,...r,props:s}}async function Fa(t,e,n){const{fmt:s,props:o,...r}=await Ua(t,e),{w:a,h:i,data:l}=r,c=a*i,f=new Uint8Array(c*4),h=new DataView(l.buffer,l.byteOffset,l.byteLength);switch(s){case re.DXT1:case re.DXT3:case re.DXT5:{const u=Math.max(1,a+3>>2),d=Math.max(1,i+3>>2),p=u*d*(s===re.DXT1?8:16);if(l.length<p)throw new Error(`${e.name}: данные DXT обрезаны`);return{width:a,height:i,rgba:_e(h,0,a,i,s===re.DXT1?1:s===re.DXT3?3:5)}}case re.RGBA8:case re.RGB8:{const u=s===re.RGBA8?4:3;for(let d=0;d<c&&d*u+u<=l.length;d++)f[d*4]=l[d*u+2],f[d*4+1]=l[d*u+1],f[d*4+2]=l[d*u],f[d*4+3]=u===4?l[d*u+3]:255;break}case re.L8:for(let u=0;u<c&&u<l.length;u++)f[u*4]=f[u*4+1]=f[u*4+2]=l[u],f[u*4+3]=255;break;case re.G16:for(let u=0;u<c&&u*2+1<l.length;u++)f[u*4]=f[u*4+1]=f[u*4+2]=l[u*2+1],f[u*4+3]=255;break;case re.P8:{const u=await Cg(t,an(o,"Palette"),n),d=o.get("bMasked")?.value===!0;for(let p=0;p<c&&p<l.length;p++){const m=l[p],g=m*4;f[p*4]=u[g]??m,f[p*4+1]=u[g+1]??m,f[p*4+2]=u[g+2]??m,f[p*4+3]=d&&m===0?0:u.length&&u[g+3]||255}break}default:throw new Error(`${e.name}: формат текстуры ${s} не поддерживается`)}return{width:a,height:i,rgba:f}}function _g(t){const e=t.idx();for(let n=0;n<e&&n<16;n++){t.ver>=63&&t.i32();const s=t.idx(),o=t.bytes(s),r=t.i32(),a=t.i32();if(t.u8v(),t.u8v(),s>0&&r>0&&a>0)return{w:r,h:a,data:o}}return null}async function Cg(t,e,n){const s=e?await n(t,e):null;if(!s)return new Uint8Array(0);const o=await s.pkg.reader(s.exp);Oe(s.pkg,o);const r=o.idx();return o.bytes(Math.max(0,Math.min(256,r))*4).slice()}const qs=new Map,vu=new WeakMap,xu=t=>t.replace(/^.*[\\/]/,"").replace(/\.[^.]+$/,"").toLowerCase();function Lg(t){let e=vu.get(t);return e||(e=new Map,t.exports.forEach((n,s)=>{const o=t.objPath(s+1).toLowerCase();(!e.has(o)||Sa.has(n.className))&&e.set(o,n),e.set(`${o}|${n.className.toLowerCase()}`,n)}),vu.set(t,e)),e}const Ie=async(t,e)=>{if(e>0){const n=t.exports[e-1];return n?{pkg:t,exp:n}:null}if(e<0){const n=t.imports[-e-1];if(!n)return null;const s=[n.name];let o=n.outer,r="";for(let f=0;o&&f<16;f++){const h=o<0?t.imports[-o-1]:t.exports[o-1];if(!h)break;if(o<0&&!h.outer){r=h.name;break}s.unshift(h.name),o=h.outer}const a=qs.get(r.toLowerCase());if(!a)return null;const i=Lg(a),l=s.join(".").toLowerCase(),c=i.get(`${l}|${n.className.toLowerCase()}`)??i.get(l);return c?{pkg:a,exp:c}:null}return null};let ws=null;function _a(t){if(!ws||ws.size!==qs.size){const e=new Map;for(const n of qs.values())n.exports.forEach((s,o)=>{if(Sa.has(s.className)){const r=s.name.toLowerCase();(!e.has(r)||s.className!=="Texture")&&e.set(r,{pkg:n,ref:o+1})}});ws={size:qs.size,map:e}}return ws.map.get(t.toLowerCase())??null}function Mu(t){_a("");let e=null;for(const n of ws.map.keys())t.test(n)&&(e===null||n<e)&&(e=n);return e?ws.map.get(e):null}async function Rg(t){const e=await ht(t,0,Math.min(t.size,128)),n=Sg((i,l)=>ht(t,i,l),e,t.name),s=await Zs.open(n.read,n.size(t.size),t.name),o=xu(t.name);qs.set(o,s);const r=[];s.exports.forEach((i,l)=>{const c=Sa.has(i.className)?"tex":i.className==="StaticMesh"?"sm":i.className==="SkeletalMesh"?"skm":null;if(!c||i.size<=0)return;const f=`${o}/${s.objPath(l+1).toLowerCase().replace(/\./g,"/")}.${c==="tex"?"utex":c==="sm"?"ue2sm":"ue2skm"}`;r.push({path:f,exp:l,kind:c,size:i.size})});const a=Qt({name:t.name,kind:"ue2",version:s.ver,entries:r,textureKeys:i=>{const l=i.path.replace(/\.utex$/,"");return[l,l.slice(l.indexOf("/")+1)]},keepFirstTextureKey:!0,async read(i){if(i.kind==="tex"){const l=await Ta(s,i.exp+1,Ie);if(!l)throw new Error(`${s.objPath(i.exp+1)}: у материала не найдена базовая текстура`);const c=await Fa(l.pkg,l.exp,Ie);return ku(c.width,c.height,c.rgba)}return new TextEncoder().encode(JSON.stringify({exp:i.exp}))}});return a.ue2=s,a}function ku(t,e,n){const s=new Uint8Array(12+n.length),o=new DataView(s.buffer);return s.set([71,77,86,82]),o.setUint32(4,t,!0),o.setUint32(8,e,!0),s.set(n,12),s}function Og(t){const e=new DataView(t);if(t.byteLength<12||e.getUint32(0,!0)!==1381387591)throw new Error("Не текстура пакета Unreal");const n=e.getUint32(4,!0),s=e.getUint32(8,!0);return{width:n,height:s,rgba:new Uint8Array(t,12,n*s*4)}}const Eu=t=>t&&"ue2"in t?t.ue2:null,Au=Object.freeze(Object.defineProperty({__proto__:null,findMaterialByName:_a,findMaterialMatching:Mu,openUe2Package:Rg,packRgba:ku,packageName:xu,resolveObj:Ie,ue2PackageOf:Eu,unpackRgba:Og},Symbol.toStringTag,{value:"Module"})),ce=.01;function Ig(t,e){const n=e?.value;if(!n?.array)return[];const s=new We(n.array,t.ver,t.lic),o=s.idx(),r=[];for(let a=0;a<o&&s.pos<s.u8.length;a++)r.push(Oe(t,s));return r}const kn=(t,e,n)=>(t.i32(),t.arr(e,n)),ys=(t,e)=>{t.need(e),t.pos+=e},$u=new WeakMap;async function bs(t,e){const n=e?t.objPath(e):"";if(!e)return{name:"",alpha:"none",twoSided:!1};try{const s=await Ta(t,e,Ie);if(!s)return{name:n,alpha:"none",twoSided:!1,error:await Ie(t,e)?"нет базовой текстуры":`пакет «${t.objPath(e).split(".")[0]}» не открыт`};let o=$u.get(s.pkg);o||$u.set(s.pkg,o=new Map);const r=s.pkg.exports.indexOf(s.exp);let a=o.get(r);return a||o.set(r,a=Fa(s.pkg,s.exp,Ie).then(i=>({...i,name:s.exp.name}))),{name:n,image:await a,alpha:s.alpha,twoSided:s.twoSided}}catch(s){return{name:n,alpha:"none",twoSided:!1,error:s.message}}}function Ca(t,e,n,s,o,r,a,i){const l=n.length/3;return{name:t,texture:e.name,vertexCount:l,pos:Float32Array.from(n),nor:Float32Array.from(s),uv:Float32Array.from(o),bone:a?Float32Array.from(a):new Float32Array(l*4),weight:i?Float32Array.from(i):new Float32Array(l*4).map((c,f)=>f%4===0?1:0),indices:l>65535?Uint32Array.from(r):Uint16Array.from(r),skinned:!!a,...e.image?{image:e.image}:{},...e.alpha!=="none"?{alphaTest:!0}:{}}}async function Su(t){const e=Eu(t.archive);if(!e)throw new Error("Меш Unreal Engine 2 открывается из пакета (.usx/.ukx): откройте пакет или «Папку игры»");const{exp:n}=JSON.parse(new TextDecoder().decode(new Uint8Array(await t.arrayBuffer()))),s=e.exports[n];if(!s)throw new Error("Объект не найден в пакете");return{pkg:e,exp:s,r:await e.reader(s)}}async function Dg(t){const{pkg:e,exp:n}=await Su(t),s=await La(e,n);if(!s.meshes.length)throw new Error(`${n.name}: в StaticMesh нет треугольников`);const o=[`Unreal Engine 2 StaticMesh: ${e.objPath(e.exports.indexOf(n)+1)} (пакет v${e.ver}/${e.lic})`,`Секций ${s.sections}, вершин ${s.verts}, треугольников ${s.tris}`];return s.missing.length&&o.push(`Материалы без текстуры: ${[...new Set(s.missing)].join("; ")}`),{meshes:s.meshes,skeleton:null,motions:[],bounds:dt(s.meshes),flipZ:!1,defaultZUp:!0,cameraYaw:0,info:o}}async function La(t,e,n=[],s=t){const o=await t.reader(e),r=Oe(t,o);ys(o,41);const a=o.arr(p=>(p.i32(),{first:p.u16(),minV:p.u16(),maxV:p.u16(),unk:p.u16(),faces:p.u16()}),4096);ys(o,25);const i=o.arr(p=>[p.f32(),p.f32(),p.f32(),p.f32(),p.f32(),p.f32()]);o.i32(),o.arr(p=>p.bytes(4)),o.i32(),o.arr(p=>p.bytes(4)),o.i32();const l=o.arr(p=>{const m=p.arr(g=>[g.f32(),g.f32()]);return p.i32(),p.i32(),m},16),c=o.arr(p=>p.u16());o.i32();const f=Ig(t,r.get("Materials")),h=[],u=[],d=l[0]??[];for(let p=0;p<a.length;p++){const m=a[p];if(!m.faces)continue;const g=n[p]?await bs(s,n[p]):await bs(t,f[p]?an(f[p],"Material"):0);g.error&&u.push(`${g.name||"—"}: ${g.error}`);const w=new Map,y=[],M=[],x=[],v=[];for(let b=0;b<m.faces*3;b++){const k=c[m.first+b];if(k===void 0||k>=i.length)continue;let E=w.get(k);if(E===void 0){E=y.length/3,w.set(k,E);const A=i[k];y.push(A[0]*ce,-A[1]*ce,A[2]*ce),M.push(A[3],-A[4],A[5]);const S=d[k]??[0,0];x.push(S[0],S[1])}v.push(E)}v.length&&h.push(Ca(`${p} · ${g.name?g.name.split(".").pop():"без материала"}`,g,y,M,x,v))}return{meshes:h,missing:u,sections:a.length,verts:i.length,tris:c.length/3|0}}const Tu=(t,e,n,s,o)=>o?[s,-t,e,-n]:[s,t,-e,n];async function Pg(t){const{pkg:e,exp:n,r:s}=await Su(t);Oe(e,s),ys(s,41);const o=s.i32();if(s.i32(),s.arr(F=>F.u32()),o<=1)throw new Error(`${n.name}: старая версия меша ${o}`);const r=s.arr(F=>F.idx(),4096),a=s.vec(),i=s.vec(),l=[s.i32(),s.i32(),s.i32()];s.arr(F=>F.u16()),s.arr(F=>F.bytes(8)),s.arr(F=>F.u16()),s.arr(F=>F.bytes(10));const c=s.arr(F=>({flags:F.u32(),tex:F.i32()}),4096);ys(s,24),o>=3&&(s.i32(),s.idx(),ys(s,52)),o>=4&&s.f32(),o>=5&&s.i32(),o>=6&&s.u8v(),s.arr(F=>F.vec());const f=s.arr(F=>{const C=e.name(F.idx());F.u32();const P=[F.f32(),F.f32(),F.f32(),F.f32()],I=F.vec();F.f32(),F.vec(),F.i32();const B=F.i32();return{name:C,q:P,t:I,parent:B}},1024),h=s.idx();s.i32(),s.arr(F=>{F.arr(C=>C.u16()),F.i32()}),s.arr(F=>F.bytes(4)),s.arr(F=>F.idx()),s.arr(F=>F.idx()),s.arr(F=>F.bytes(48));const u=s.arr(F=>Ng(F),16);s.idx();const d=kn(s,F=>F.vec()),p=kn(s,F=>({v:F.u16(),u:F.f32(),w:F.f32()})),m=kn(s,F=>{const C=[F.u16(),F.u16(),F.u16()],P=F.u8v();return F.u8v(),F.u32(),{w:C,m:P}}),g=kn(s,F=>({wt:F.f32(),p:F.u16(),b:F.u16()}));let w;if(d.length&&p.length&&m.length)w={points:d,wedges:p,infl:g,faces:m};else{const F=u[0];if(!F||!F.points.length)throw new Error(`${n.name}: в SkeletalMesh нет геометрии`);const C=[];if(!F.wedges.length&&(F.stream.length||F.softIdx.length)){const P=F.stream.length?F.stream:F.points.map(z=>[z[0],z[1],z[2],0,0,0,0,0]),I=P.map(z=>[z[0],z[1],z[2]]),B=P.map((z,j)=>({v:j,u:z[6],w:z[7]}));for(const z of F.soft)for(let j=0;j<z.numFaces;j++)C.push({w:[0,1,2].map(X=>F.softIdx[z.firstFace*3+j*3+X]??0),m:z.mat});for(const z of F.rigid)for(let j=0;j<z.numFaces;j++)C.push({w:[0,1,2].map(X=>F.rigidIdx[z.firstFace*3+j*3+X]??0),m:z.mat});w={points:I,wedges:B,infl:[],faces:C}}else{for(const P of F.soft)for(let I=0;I<P.numFaces;I++){const B=F.faces[P.firstFace+I];B&&C.push({w:B.w,m:P.mat})}for(const P of F.rigid)for(let I=0;I<P.numFaces;I++)C.push({w:[0,1,2].map(B=>F.rigidIdx[P.firstFace*3+I*3+B]??0),m:P.mat});w={points:F.points,wedges:F.wedges,infl:F.infl,faces:C}}}const y=f.map((F,C)=>({name:F.name,q:Tu(F.q[0],F.q[1],F.q[2],F.q[3],C===0),t:[F.t[0]*ce,-F.t[1]*ce,F.t[2]*ce],parent:C===0?-1:F.parent})),M=y.map(F=>{const C=new Float64Array(16);return Ae(F.t,F.q,[1,1,1],C),C}),x=[];y.forEach((F,C)=>{x[C]=F.parent>=0&&F.parent<C?Lt(x[F.parent],M[C]):M[C]});const b=[{name:"Mesh",parent:-1,local:zg(a,i,l),offset:null,helper:!0}];y.forEach((F,C)=>b.push({name:F.name,parent:(F.parent>=0?F.parent:-1)+1,local:M[C],offset:Bg(x[C])}));const k=w.points.map(()=>[]);for(const F of w.infl)F.p<k.length&&F.b<y.length&&F.wt>1e-6&&k[F.p].push([F.b+1,F.wt]);for(const F of k){F.sort((P,I)=>I[1]-P[1]),F.length>4&&(F.length=4);const C=F.reduce((P,I)=>P+I[1],0)||1;for(const P of F)P[1]/=C}const E=w.points.map(F=>[F[0]*ce,-F[1]*ce,F[2]*ce]),A=w.points.map(()=>[0,0,0]);for(const F of w.faces){const[C,P,I]=F.w.map(H=>w.wedges[H]?.v??0),B=E[C],z=E[P],j=E[I];if(!B||!z||!j)continue;const X=[z[0]-B[0],z[1]-B[1],z[2]-B[2]],J=[j[0]-B[0],j[1]-B[1],j[2]-B[2]],G=[X[1]*J[2]-X[2]*J[1],X[2]*J[0]-X[0]*J[2],X[0]*J[1]-X[1]*J[0]];for(const H of[C,P,I])for(let K=0;K<3;K++)A[H][K]+=G[K]}const S=new Map;for(const F of w.faces){let C=S.get(F.m);C||S.set(F.m,C=[]),C.push(...F.w)}const $=[],T=[];for(const[F,C]of[...S].sort((P,I)=>P[0]-I[0])){const P=c[F],I=P?r[P.tex]??0:r[F]??0;let B=await bs(e,I);if(!B.image){const Q=/^(.*?)_(m\d\d)((?:_[a-z]+)?)$/i.exec(n.name),V=String(F).padStart(2,"0"),W=Q?[`${Q[1]}_t${V}_${Q[2]}${Q[3]}`,`${Q[1]}_t${V}${Q[3]}`,`${Q[1]}_t${V}`]:[],Y=/^(.+_m\d{3})_([a-z]{1,2})$/i.exec(n.name),tt=/^drop_(.+_m\d{3}_t\d\d_[a-z]{1,2})_m\d\d$/i.exec(n.name);if(tt&&W.unshift(tt[1]),Y&&W.push(new RegExp(`^${Y[1].toLowerCase()}_t\\d\\d_${Y[2].toLowerCase()}$`)),Q){const nt=Q[1].split("_");for(let et=nt.length;et>=1;et--){const rt=nt.slice(0,et).join("_");for(const mt of["","_ori","_sh"])W.push(`${rt}_t${V}${mt}`)}}for(const nt of W){const et=typeof nt=="string"?_a(nt):Mu(nt);if(et){const rt=await bs(et.pkg,et.ref);if(rt.image){B={...rt,name:rt.name+" (по имени меша)"};break}}}}P&&P.flags&6&&(B.alpha=B.alpha==="none"?"test":B.alpha),B.image||T.push(`${F}: ${B.name||"нет текстуры в меше"}${B.error?" — "+B.error:""}`);const z=new Map,j=[],X=[],J=[],G=[],H=[],K=[];for(const Q of C){let V=z.get(Q);if(V===void 0){const W=w.wedges[Q];if(!W||W.v>=E.length)continue;V=j.length/3,z.set(Q,V),j.push(...E[W.v]);const Y=A[W.v],tt=Math.hypot(Y[0],Y[1],Y[2])||1;X.push(Y[0]/tt,Y[1]/tt,Y[2]/tt),J.push(W.u,W.w);const nt=k[W.v];for(let et=0;et<4;et++)G.push(nt[et]?.[0]??(et===0?1:0)),H.push(nt[et]?.[1]??(et===0&&!nt.length?1:0))}K.push(V)}K.length&&$.push(Ca(`${F} · ${B.name?B.name.split(".").pop():"материал "+F}`,B,j,X,J,K,G,H))}if(!$.length)throw new Error(`${n.name}: в SkeletalMesh нет треугольников`);const U=new Kt(b);U.pose(null,0,!1);const _=[],R=new Map(y.map((F,C)=>[F.name.toLowerCase(),C])),L=[],O=h?await Ie(e,h):null;if(O&&O.exp.className==="MeshAnimation")L.push(O);else for(const F of e.exports)if(F.className==="MeshAnimation")try{const C=await Vg(e,F);C.filter(P=>R.has(P.toLowerCase())).length>=Math.max(3,C.length*.8)&&C.length<=y.length+4&&L.push({pkg:e,exp:F})}catch{}const N=[];for(const F of L)try{const C=await jg(F.pkg,F.exp);for(const P of C.seqs)_.push(Gg(P,C.bones,R,y,F.exp.name,L.length>1));N.push(`${F.exp.name}: ${C.seqs.length}`)}catch(C){N.push(`${F.exp.name}: ошибка — ${C.message}`)}const D=[`Unreal Engine 2 SkeletalMesh: ${e.objPath(e.exports.indexOf(n)+1)} (пакет v${e.ver}/${e.lic}, меш v${o})`,`Костей ${y.length}, точек ${w.points.length}, вершин ${w.wedges.length}, треугольников ${w.faces.length}${d.length?"":" (из LOD 0)"}`,N.length?`Анимации: ${N.join("; ")}`:"Анимаций для этого скелета в пакете нет"];return T.length&&D.push(`Материалы без текстуры: ${[...new Set(T)].join("; ")}`),{meshes:$,skeleton:U,motions:_,bounds:Jt(U,$),flipZ:!1,defaultZUp:!0,cameraYaw:Math.PI/2,info:D}}function Ng(t){t.arr(h=>h.u32()),t.arr(h=>h.bytes(16)),t.i32();const e=h=>{const u=h.u16();h.u16(),h.u16(),h.u16(),h.u16(),h.u16(),h.u16();const d=h.u16(),p=h.u16();return{mat:u,firstFace:d,numFaces:p}},n=t.arr(e,4096),s=t.arr(e,4096),o=t.arr(h=>h.u16());t.i32();const r=t.arr(h=>h.u16());t.i32(),t.i32(),t.i32(),t.i32();const a=t.arr(h=>[h.f32(),h.f32(),h.f32(),h.f32(),h.f32(),h.f32(),h.f32(),h.f32()]),i=kn(t,h=>({wt:h.f32(),p:h.u16(),b:h.u16()})),l=kn(t,h=>({v:h.u16(),u:h.f32(),w:h.f32()})),c=kn(t,h=>{const u=[h.u16(),h.u16(),h.u16()];return h.u16(),{w:u}}),f=kn(t,h=>h.vec());return ys(t,24),{points:f,wedges:l,infl:i,faces:c,soft:n,rigid:s,rigidIdx:r,softIdx:o,stream:a}}function zg(t,e,n){const s=w=>w*Math.PI/32768,o=s(n[0]),r=s(n[1]),a=s(n[2]),i=Math.cos(o),l=Math.sin(o),c=Math.cos(r),f=Math.sin(r),h=Math.cos(a),u=Math.sin(a),d=[[i*c,i*f,l],[u*l*c-h*f,u*l*f+h*c,-u*i],[-(h*l*c+u*f),c*u-h*l*f,h*i]],p=new Float64Array(16),m=[1,-1,1];for(let w=0;w<3;w++)for(let y=0;y<3;y++)p[w*4+y]=m[y]*d[w][y]*t[w]*m[w];p[15]=1;const g=[e[0]*ce,-e[1]*ce,e[2]*ce];for(let w=0;w<3;w++)p[12+w]=-(p[w]*g[0]+p[4+w]*g[1]+p[8+w]*g[2]);return p}function Bg(t){const e=t[0],n=t[4],s=t[8],o=t[1],r=t[5],a=t[9],i=t[2],l=t[6],c=t[10],f=r*c-a*l,h=-(o*c-a*i),u=o*l-r*i,d=e*f+n*h+s*u||1,p=[f/d,h/d,u/d,-(n*c-s*l)/d,(e*c-s*i)/d,-(e*l-n*i)/d,(n*a-s*r)/d,-(e*a-s*o)/d,(e*r-n*o)/d],m=new Float64Array(16);for(let g=0;g<3;g++)for(let w=0;w<3;w++)m[w*4+g]=p[g*3+w];for(let g=0;g<3;g++)m[12+g]=-(m[g]*t[12]+m[4+g]*t[13]+m[8+g]*t[14]);return m[15]=1,m}async function Vg(t,e){for(let n=8192;;n*=4)try{const s=await t.readerHead(e,n);return Oe(t,s),s.i32(),s.arr(o=>{const r=t.name(o.idx());return o.u32(),o.i32(),r},1024)}catch(s){if(n>=e.size)throw s}}const Uu=new WeakMap;function jg(t,e){let n=Uu.get(e);return n||Uu.set(e,n=(async()=>{const s=await t.reader(e);Oe(t,s),s.i32();const o=s.arr(l=>{const c=t.name(l.idx());return l.u32(),l.i32(),c},1024),r=l=>{l.u32();const c=l.arr(u=>[u.f32(),u.f32(),u.f32(),u.f32()]),f=l.arr(u=>u.vec()),h=l.arr(u=>u.f32());return{quat:c,pos:f,time:h}},a=s.arr(l=>{l.vec();const c=l.f32();l.i32(),l.u32(),l.arr(h=>h.i32());const f=l.arr(r,1024);return r(l),{trackTime:c,tracks:f}},1e5),i=s.arr(l=>{t.ver>=115&&l.f32();const c=t.name(l.idx());l.arr(u=>u.idx()),l.i32();const f=l.i32();l.arr(u=>{u.f32(),u.idx(),t.ver>=112&&u.idx()});const h=l.f32();return t.lic>=1&&(l.i32(),l.i32(),t.lic>=2&&l.i32(),l.idx()),{name:c,frames:f,rate:h}},1e5);return{bones:o,seqs:i.map((l,c)=>({...l,trackTime:a[c]?.trackTime??l.frames,tracks:a[c]?.tracks??[]}))}})()),n}function Gg(t,e,n,s,o,r){const a=new Map,i=Math.max(1,t.frames),l=t.trackTime>0?i/t.trackTime:1;t.tracks.forEach((f,h)=>{const u=n.get((e[h]??"").toLowerCase());if(u===void 0||!f.quat.length)return;const d=u===0,p=Math.max(f.quat.length,f.pos.length,1),m=f.time.length>=p?f.time.map(y=>y*l):Array.from({length:p},(y,M)=>M*i/p),g=[],w=[];for(let y=0;y<p;y++){const M=f.quat[Math.min(y,f.quat.length-1)];g.push([m[y],Tu(M[0],M[1],M[2],M[3],d)]);const x=f.pos.length?f.pos[Math.min(y,f.pos.length-1)]:null;w.push([m[y],x?[x[0]*ce,-x[1]*ce,x[2]*ce]:s[u].t])}p>1&&m[p-1]<i&&(g.push([i,g[0][1]]),w.push([i,w[0][1]])),a.set(u+1,{pos:w,rot:g,scl:null})});const c=t.rate>0?t.rate:30;return{kind:"node",name:r?`${t.name} · ${o}`:t.name,tps:c,chan:a,duration:i/c,frames:i}}const Fu=Object.freeze(Object.defineProperty({__proto__:null,loadUe2SkeletalMesh:Pg,loadUe2StaticMesh:Dg,makeMesh:Ca,material:bs,staticMeshParts:La},Symbol.toStringTag,{value:"Module"}));function _u(t,e,n){const s=e,o=[s,0,0,-s,0,0,0,s,0,0,-s,0,0,0,s,0,0,-s],r=[0,2,4,2,1,4,1,3,4,3,0,4,2,0,5,1,2,5,3,1,5,0,3,5],a=Float32Array.from(o),i=Float32Array.from(o.map(l=>Math.sign(l)));return{name:t,texture:"",vertexCount:6,pos:a,nor:i,uv:new Float32Array(12),bone:new Float32Array(0),weight:new Float32Array(0),indices:Uint16Array.from(r),skinned:!1,color:n}}class vs{groups=new Map;scene={meshes:[],protos:[],placements:[]};protoParts=[];tris=0;group(e,n,s,o){let r=this.groups.get(e);return r||(r={index:this.groups.size,key:e,name:n,proto:s,hidden:o,nV:0,nI:0,items:[]},this.groups.set(e,r)),r}proto(e,n,s,o,r=!1){const a=n.map(i=>this.group(`${s}|${i.texture}|${i.material??""}|${i.color?.join(",")??""}`,o(i),i,r));return this.protoParts.push(a.map(i=>i.index)),this.scene.protos.push({name:e,parts:n.map((i,l)=>({mesh:a[l].index,pos:i.pos,nor:i.nor,uv:i.uv,indices:Uint32Array.from(i.indices)}))}),this.groupsByIndex=null,this.scene.protos.length-1}groupsByIndex=null;place(e,n,s,o,r){const a=this.groupsByIndex??=[...this.groups.values()],i=s[0]*(s[5]*s[10]-s[6]*s[9])-s[1]*(s[4]*s[10]-s[6]*s[8])+s[2]*(s[4]*s[9]-s[5]*s[8]),l=[];this.protoParts[e].forEach((c,f)=>{const h=a[c],u=n[f];l.push({mesh:c,part:f,vStart:h.nV,vCount:u.vertexCount,iStart:h.nI,iCount:u.indices.length}),h.items.push({p:u,M:s,flip:i<0}),h.nV+=u.vertexCount,h.nI+=u.indices.length,this.tris+=u.indices.length/3}),this.scene.placements.push({proto:e,name:o,pose:Array.from(s),group:r,ranges:l})}build(){const e=[];for(const n of this.groups.values()){const s=new Float32Array(n.nV*3),o=new Float32Array(n.nV*3),r=new Float32Array(n.nV*2),a=n.nV>65535?new Uint32Array(n.nI):new Uint16Array(n.nI);let i=0,l=0;for(const{p:f,M:h,flip:u}of n.items){const d=f.vertexCount;for(let m=0;m<d;m++){const g=f.pos[m*3],w=f.pos[m*3+1],y=f.pos[m*3+2],M=(i+m)*3;s[M]=g*h[0]+w*h[4]+y*h[8]+h[12],s[M+1]=g*h[1]+w*h[5]+y*h[9]+h[13],s[M+2]=g*h[2]+w*h[6]+y*h[10]+h[14];const x=f.nor[m*3],v=f.nor[m*3+1],b=f.nor[m*3+2],k=x*h[0]+v*h[4]+b*h[8],E=x*h[1]+v*h[5]+b*h[9],A=x*h[2]+v*h[6]+b*h[10],S=Math.hypot(k,E,A)||1;o[M]=k/S,o[M+1]=E/S,o[M+2]=A/S,r[(i+m)*2]=f.uv[m*2],r[(i+m)*2+1]=f.uv[m*2+1]}const p=f.indices;for(let m=0;m+2<p.length;m+=3)a[l+m]=i+p[m],a[l+m+1]=i+p[u?m+2:m+1],a[l+m+2]=i+p[u?m+1:m+2];i+=d,l+=p.length}n.items=[];const c=n.proto;e.push({name:n.name,texture:c.texture,material:c.material,normalMap:c.normalMap,surface:c.surface,color:c.color,alphaTest:c.alphaTest,...c.image?{image:c.image}:{},...c.screenFx?{screenFx:c.screenFx}:{},vertexCount:n.nV,pos:s,nor:o,uv:r,bone:new Float32Array(0),weight:new Float32Array(0),indices:a,skinned:!1,...n.hidden?{defaultHidden:!0}:{}})}return this.scene.meshes=e.map((n,s)=>s),e}}const xs=.01;function Ms(t,e,n){const s=t.get(e)?.value;if(!s?.data||s.data.length<12)return n;const o=new DataView(s.data.buffer,s.data.byteOffset,12);return s.struct==="Rotator"?[o.getInt32(0,!0),o.getInt32(4,!0),o.getInt32(8,!0)]:[o.getFloat32(0,!0),o.getFloat32(4,!0),o.getFloat32(8,!0)]}function Cu(t){const e=h=>h*Math.PI/32768,n=e(t[0]),s=e(t[1]),o=e(t[2]),r=Math.cos(n),a=Math.sin(n),i=Math.cos(s),l=Math.sin(s),c=Math.cos(o),f=Math.sin(o);return[[r*i,r*l,a],[f*a*i-c*l,f*a*l+c*i,-f*r],[-(c*a*i+f*l),i*f-c*a*l,c*r]]}function Lu(t,e,n){const s=Cu(e),o=[1,-1,1],r=new Array(16).fill(0);for(let a=0;a<3;a++)for(let i=0;i<3;i++)r[a*4+i]=o[i]*s[a][i]*n[a]*o[a];return r[12]=t[0]*xs,r[13]=-t[1]*xs,r[14]=t[2]*xs,r[15]=1,r}function Xg(t,e){const n=e?.value;if(!n?.array)return typeof e?.value=="number"?[e.value]:[];try{return new We(n.array,t.ver,t.lic).arr(o=>o.idx(),256)}catch{return[]}}async function Hg(t,e,n){const s=an(e,"TerrainMap"),o=s?await Ie(t,s):null;if(!o)return n.push(`Ландшафт: карта высот ${s?t.objPath(s):"—"} не найдена (откройте папку игры: Textures/T_xx_yy.utx)`),[];const r=await Ua(o.pkg,o.exp);if(r.fmt!==re.G16)return n.push(`Ландшафт: карта высот в формате ${r.fmt}, ожидался G16`),[];const a=r.w,i=r.h,l=new DataView(r.data.buffer,r.data.byteOffset,r.data.byteLength),c=Ms(e,"TerrainScale",[64,64,64]),f=Ms(e,"Location",[0,0,0]),h=(v,b)=>l.getUint16((Math.min(i-1,b)*a+Math.min(a-1,v))*2,!0),u=e.get("QuadVisibilityBitmap")?.value?.array;let d=null;if(u)try{const v=new We(u,t.ver,t.lic),b=v.idx();d=v.bytes(Math.min(b*4,v.u8.length-v.pos))}catch{d=null}const p=(v,b)=>{if(!d)return!0;const k=b*a+v;return(d[k>>3]??255)>>(k&7)&1},m=a*i,g=new Float32Array(m*3),w=new Float32Array(m*3);for(let v=0;v<i;v++)for(let b=0;b<a;b++){const k=v*a+b,E=f[0]+(b-a/2)*c[0],A=f[1]+(v-i/2)*c[1],S=f[2]+(h(b,v)-32768)*c[2]/256;g[k*3]=E*xs,g[k*3+1]=-A*xs,g[k*3+2]=S*xs}for(let v=0;v<i;v++)for(let b=0;b<a;b++){const k=v*a+b,E=(h(b+1,v)-h(Math.max(0,b-1),v))*c[2]/256,A=(h(b,v+1)-h(b,Math.max(0,v-1)))*c[2]/256,S=-E/(2*c[0]),$=A/(2*c[1]),T=Math.hypot(S,$,1);w[k*3]=S/T,w[k*3+1]=$/T,w[k*3+2]=1/T}const y=[];for(let v=0;v+1<i;v++)for(let b=0;b+1<a;b++){if(!p(b,v))continue;const k=v*a+b;y.push(k,k+a,k+1,k+1,k+a,k+a+1)}const M=[];for(let v=0;v<32;v++){const b=e.get(v?`Layers[${v}]`:"Layers")?.value;if(!b?.data)continue;let k;try{k=Oe(t,new We(b.data,t.ver,t.lic))}catch{continue}const E=an(k,"Texture");if(!E)continue;const A=await bs(t,E);let S=null;const $=an(k,"AlphaMap"),T=$?await Ie(t,$):null;if(T)try{const U=await Ua(T.pkg,T.exp),_=U.fmt===re.L8||U.fmt===re.G16?null:await Fa(T.pkg,T.exp,Ie);S=new Float32Array(m);for(let R=0;R<i;R++)for(let L=0;L<a;L++){const O=Math.min(U.w-1,Math.floor(L*U.w/a)),N=Math.min(U.h-1,Math.floor(R*U.h/i));S[R*a+L]=_?_.rgba[(N*U.w+O)*4+3]/255:U.fmt===re.L8?U.data[N*U.w+O]/255:U.data[(N*U.w+O)*2+1]/255}}catch{S=null}M.push({tex:A.image??null,name:A.name,alpha:S,us:gs(k,"UScale",1)||1,vs:gs(k,"VScale",1)||1})}M.length||M.push({tex:null,name:"",alpha:null,us:1,vs:1});const x=[];return M.forEach((v,b)=>{if(b>0&&v.alpha&&!v.alpha.some($=>$>.02))return;const k=new Float32Array(m*2),E=v.tex?.width??256,A=v.tex?.height??256;for(let $=0;$<i;$++)for(let T=0;T<a;T++){const U=$*a+T;k[U*2]=((T-a/2)*c[0]+f[0])/(v.us*E),k[U*2+1]=(($-i/2)*c[1]+f[1])/(v.vs*A)}let S=y;if(b>0&&v.alpha){S=[];for(let $=0;$<y.length;$+=3)(v.alpha[y[$]]>.02||v.alpha[y[$+1]]>.02||v.alpha[y[$+2]]>.02)&&S.push(y[$],y[$+1],y[$+2])}S.length&&x.push({name:`Ландшафт · ${b===0?"основа":"слой "+b} · ${v.name.split(".").pop()||"—"}`,texture:v.name,vertexCount:m,pos:g,nor:w,uv:k,bone:new Float32Array(0),weight:new Float32Array(0),indices:Uint32Array.from(S),skinned:!1,...v.tex?{image:v.tex}:{color:[.4,.45,.3]},...b>0&&v.alpha?{vertexAlpha:v.alpha}:{}})}),n.push(`Ландшафт ${a}×${i} клеток по ${c[0]}×${c[1]} uu (${(a*c[0]/100).toFixed(0)} м), слоёв ${x.length}`),x}async function Wg(t){const e=bu(new Uint8Array(await t.arrayBuffer()),t.name),n=Zs.fromBytes(e,t.name),s=[`Карта Unreal Engine 2: ${t.name} (пакет v${n.ver}/${n.lic})`],o=[],r=new vs,a=new Map,i=new Set;let l=0,c=0,f=0;for(const u of n.exports){const d=u.className;if(d!=="TerrainInfo"&&!/StaticMeshActor$/.test(d)){d==="Model"&&f++;continue}let p;try{const b=await n.reader(u);Tg(u,b),p=Oe(n,b)}catch{continue}if(d==="TerrainInfo"){try{o.push(...await Hg(n,p,s))}catch(b){s.push(`Ландшафт: ошибка — ${b.message}`)}continue}if(c++,p.get("bHidden")?.value===!0)continue;const m=an(p,"StaticMesh");if(!m)continue;const g=Xg(n,p.get("Skins")),w=`${n.objPath(m)}|${g.join(",")}`;let y=a.get(w);if(y===void 0){y=null;const b=await Ie(n,m);if(b&&b.exp.className==="StaticMesh")try{const k=await La(b.pkg,b.exp,g,n);k.meshes.length&&(y={pi:r.proto(w,k.meshes,"sm",E=>`объекты · ${(E.texture||E.name).split(".").pop()}`),parts:k.meshes})}catch{i.add(n.objPath(m))}else i.add(n.objPath(m));a.set(w,y)}if(!y)continue;const M=gs(p,"DrawScale",1)||1,x=Ms(p,"DrawScale3D",[1,1,1]),v=Lu(Ms(p,"Location",[0,0,0]),Ms(p,"Rotation",[0,0,0]),[x[0]*M,x[1]*M,x[2]*M]);r.place(y.pi,y.parts,v,u.name,n.objPath(m).split(".")[0]??"StaticMesh"),l++}s.push(`Объектов StaticMesh: ${l} из ${c}, разных мешей ${[...a.values()].filter(Boolean).length}`),i.size&&s.push(`Не найдено мешей: ${i.size} (${[...i].slice(0,5).join(", ")}${i.size>5?"…":""}) — откройте «Папку игры» с StaticMeshes`),f&&s.push(`BSP (Model: ${f}) не показывается — на картах Lineage II это небо-зона (купол, облака) и объёмы`);const h=[...r.build(),...o];if(!h.length)throw new Error(`${t.name}: на карте нет ни ландшафта, ни объектов (нужны пакеты из папки игры)`);return s.push(`Треугольников: ${Math.round(h.reduce((u,d)=>u+d.indices.length/3,0)).toLocaleString("ru")}`),{meshes:h,instances:r.scene,skeleton:null,motions:[],bounds:dt(h),flipZ:!1,defaultZUp:!0,cameraYaw:0,info:s}}const Kg=Object.freeze(Object.defineProperty({__proto__:null,actorMatrix:Lu,loadUe2Map:Wg,propVec:Ms,rotatorAxes:Cu},Symbol.toStringTag,{value:"Module"})),Ru=t=>{const e=Math.sin(t[2]*.5),n=Math.cos(t[2]*.5),s=Math.sin(t[1]*.5),o=Math.cos(t[1]*.5),r=Math.sin(t[0]*.5),a=Math.cos(t[0]*.5);return[a*o*n+r*s*e,r*o*n-a*s*e,a*s*n+r*o*e,a*o*e-r*s*n]};function Yg(t,e,n,s,o){let r=n+s,a=o,i=t[r],l=t[r+1],c=0;for(;l<=a&&l>0&&c++<4096;)a-=l,r+=(i+1)*2,i=t[r],l=t[r+1];return e.getInt16(i>a?r+(a+1)*2:r+i*2,!0)}function Ou(t,e={}){const n=new DataView(t),s=new Uint8Array(t),o=S=>n.getInt32(S,!0),r=S=>n.getFloat32(S,!0);if(t.byteLength<244||o(0)!==1414743113||o(4)!==10)throw new Error("Не GoldSrc MDL (IDST v10)");const a=o(140),i=o(144),l=o(164),c=o(168),f=o(172),h=o(180),u=o(184),d=o(200),p=o(204),m=o(208);if(!p&&h)throw new Error("Это файл текстур GoldSrc (*T.mdl) — откройте основную модель, текстуры подхватятся сами");let g={dv:n,u8:s,n:h,o:u,nSkinRef:o(192),oSkin:d};if(!h&&e.tex){const S=new DataView(e.tex);g={dv:S,u8:new Uint8Array(e.tex),n:S.getInt32(180,!0),o:S.getInt32(184,!0),nSkinRef:S.getInt32(192,!0),oSkin:S.getInt32(200,!0)}}const w=S=>S<g.nSkinRef?g.dv.getInt16(g.oSkin+S*2,!0):S,y=[];for(let S=0;S<g.n;S++){const $=g.o+S*80,T=g.dv.getInt32($+64,!0),U=g.dv.getInt32($+68,!0),_=g.dv.getInt32($+72,!0),R=g.dv.getInt32($+76,!0),L=new Uint8Array(U*_*4),O=R+U*_;for(let N=0;N<U*_;N++){const D=g.u8[R+N];L[N*4]=g.u8[O+D*3],L[N*4+1]=g.u8[O+D*3+1],L[N*4+2]=g.u8[O+D*3+2],L[N*4+3]=T&64&&D===255?0:255}y.push({name:Ot(g.u8,$,64),width:U,height:_,image:{width:U,height:_,rgba:L}})}const M=[];for(let S=0;S<a;S++){const $=i+S*112,T=[],U=[];for(let _=0;_<6;_++)T.push(r($+64+_*4)),U.push(r($+88+_*4));M.push({name:Ot(s,$,32),parent:o($+32),val:T,sc:U})}const x=M.map(S=>({name:S.name,parent:S.parent,local:Xe(Ru([S.val[3],S.val[4],S.val[5]]),[S.val[0],S.val[1],S.val[2]]),offset:null})),v=[];for(let S=0;S<p;S++){const $=o(m+S*76+64),T=o(m+S*76+72);if(!$)continue;let U=T;for(let D=0;D<$&&!o(U+72);D++)U=T+(D+1)*112;if(U>=T+$*112)continue;const _=o(U+72),R=o(U+76),L=o(U+84),O=o(U+88),N=o(U+100);for(let D=0;D<_;D++){const F=R+D*20,C=o(F+4),P=o(F+8),I=w(P),B=y[I]??{width:256,height:256,name:"",image:void 0},z=new Map,j=[],X=[],J=[],G=[],H=[],K=(Y,tt,nt,et)=>{const rt=`${Y}|${tt}|${nt}|${et}`;let mt=z.get(rt);return mt===void 0&&(mt=z.size,z.set(rt,mt),j.push(r(O+Y*12),r(O+Y*12+4),r(O+Y*12+8)),X.push(r(N+tt*12),r(N+tt*12+4),r(N+tt*12+8)),J.push(nt/B.width,et/B.height),G.push(s[L+Y])),mt};for(let Y=C;;){let tt=n.getInt16(Y,!0);if(Y+=2,tt===0)break;const nt=tt<0;tt=Math.abs(tt);const et=[];for(let rt=0;rt<tt;rt++,Y+=8)et.push(K(n.getInt16(Y,!0),n.getInt16(Y+2,!0),n.getInt16(Y+4,!0),n.getInt16(Y+6,!0)));for(let rt=2;rt<tt;rt++)nt?H.push(et[0],et[rt-1],et[rt]):rt&1?H.push(et[rt-1],et[rt-2],et[rt]):H.push(et[rt-2],et[rt-1],et[rt])}const Q=z.size,V=new Float32Array(Q*4),W=new Float32Array(Q*4);for(let Y=0;Y<Q;Y++)V[Y*4]=Math.min(G[Y],a-1),W[Y*4]=1;v.push({name:`body ${S} / mesh ${D}`,texture:B.name,shader:"",image:B.image,vertexCount:Q,pos:Float32Array.from(j),nor:Float32Array.from(X),uv:Float32Array.from(J),bone:V,weight:W,indices:Uint16Array.from(H),skinned:!0})}}if(!v.length)throw new Error("В MDL нет геометрии");const b=new Kt(x),k=Jt(b,v),E=[];for(let S=0;S<l;S++){const $=c+S*176,T=Ot(s,$,32),U=r($+32)||30,_=o($+56),R=o($+124),L=o($+156),O=L===0?null:e.seq?.get(L),N=L===0?{dv:n,u8:s}:O?{dv:new DataView(O),u8:new Uint8Array(O)}:null;if(!N||_<=0)continue;const D=new Map;for(let F=0;F<a;F++){const C=R+F*12,P=[];for(let z=0;z<6;z++)P.push(N.dv.getUint16(C+z*2,!0));const I=[],B=[];for(let z=0;z<_;z++){const j=[];for(let X=0;X<6;X++)j.push(M[F].val[X]+(P[X]?Yg(N.u8,N.dv,C,P[X],z)*M[F].sc[X]:0));I.push([z,[j[0],j[1],j[2]]]),B.push([z,Ru([j[3],j[4],j[5]])])}D.set(F,{pos:I,rot:B,scl:null})}E.push({kind:"node",name:T,tps:U,chan:D,duration:_/U,frames:_})}const A=[`GoldSrc MDL: костей ${a}, мешей ${v.length}, треугольников ${Ut(v)}, текстур ${y.length}`,`Анимаций: ${E.length} из ${l}${f>1&&E.length<l?" (нет внешних файлов NN.mdl с остальными)":""}`];return!h&&!e.tex&&A.push("Текстуры во внешнем файле *T.mdl — выберите его вместе с моделью или откройте «Папку игры…»"),{meshes:v,skeleton:b,motions:E,bounds:k,flipZ:!1,defaultZUp:!0,cameraYaw:Math.PI/2,info:A}}async function Zg(t,e){const n=await t.arrayBuffer(),s=new DataView(n);if(n.byteLength<8||s.getInt32(0,!0)!==1414743113||s.getInt32(4,!0)!==10)throw new Error("Не GoldSrc MDL (нужна сигнатура IDST и версия 10)");const o=t.name.toLowerCase().replace(/\.mdl$/,""),r={seq:new Map},a=s.getInt32(172,!0);r.tex=await e.sibling(o+"t.mdl");for(let i=1;i<a;i++){const l=await e.sibling(`${o}${String(i).padStart(2,"0")}.mdl`);l&&r.seq.set(i,l)}return Ou(n,r)}const qg=Object.freeze(Object.defineProperty({__proto__:null,loadGoldsrc:Ou,loadGoldsrcMdl:Zg},Symbol.toStringTag,{value:"Module"}));function Iu(t){return t.length>=12&&t[0]===84&&t[1]===84&&t[2]===72&&t[3]===0}async function Qg(t,e){if(!Iu(t))throw new Error("Не TTH");const n=new DataView(t.buffer,t.byteOffset,t.byteLength),s=t[6],o=n.getUint32(8,!0),r=12+(s+1)*8,a=n.getUint32(12+s*8,!0);if(r+4>t.length||n.getUint32(r,!0)!==4609110)throw new Error("TTH: нет VTF внутри");const i=t.subarray(r,Math.min(t.length,r+o)),l=e&&e.length?await Lo(e):new Uint8Array(0),c=new Uint8Array(Math.max(a,i.length+l.length));return c.set(i),c.set(l.subarray(0,c.length-i.length),i.length),c.buffer}const Jg=Object.freeze(Object.defineProperty({__proto__:null,isTth:Iu,tthToVtf:Qg},Symbol.toStringTag,{value:"Module"}));function Ra(t){const e=new DataView(t),n=new Uint8Array(t),s=l=>e.getInt32(l,!0),o=l=>e.getFloat32(l,!0),r=s(240),a=s(244),i=[];if(r<=0||r>512||a+r*160>t.byteLength)return i;for(let l=0;l<r;l++){const c=a+l*160,f=(h,u)=>Array.from({length:u},(d,p)=>o(c+h+p*4));i.push({name:Ot(n,c+s(c),64),parent:s(c+4),pos:f(32,3),q:f(44,4),ps:f(60,3),rs:f(72,4),poseToBone:f(88,12),flags:s(c+136)})}return i}function t3(t){return t.map(e=>{const n=e.poseToBone,s=new Float64Array(16);for(let o=0;o<3;o++)for(let r=0;r<4;r++)s[r*4+o]=n[o*4+r];return s[15]=1,{name:e.name,parent:e.parent,local:Xe([e.q[3],e.q[0],e.q[1],e.q[2]],e.pos),offset:s}})}function Wo(t){const e=new DataView(t),n=new Uint8Array(t),s=i=>e.getInt32(i,!0),o=s(264),r=s(268),a=[];if(o<=0||o>1e4||r+o*72>t.byteLength)return a;for(let i=0;i<o;i++){const l=r+i*72,c=s(l+12),f=e.getFloat32(l+4,!0)||30;if(c<=0||c>1e5)continue;const h=s(l+16),u=l+s(l+20),d=[];if(h>0&&h<4096&&u>0&&u+h*44<=t.byteLength)for(let p=0;p<h;p++){const m=u+p*44;d.push({end:s(m),pos:[0,1,2].map(g=>e.getFloat32(m+32+g*4,!0))})}a.push({name:Ot(n,l+s(l),128).replace(/^@/,""),fps:f,frames:c,data:l+s(l+48),moves:d,flags:s(l+8),index:i})}return a}function e3(t,e,n,s){let o=s;for(let r=0;r<1e4&&n+2<=t.length;r++){const a=t[n],i=t[n+1];if(!i)return 0;if(o<i)return e.getInt16(n+2+2*Math.min(o,a-1),!0);o-=i,n+=2+2*a}return 0}const Oa=(t,e)=>[t[3]*e[0]+t[0]*e[3]+t[1]*e[2]-t[2]*e[1],t[3]*e[1]-t[0]*e[2]+t[1]*e[3]+t[2]*e[0],t[3]*e[2]+t[0]*e[1]-t[1]*e[0]+t[2]*e[3],t[3]*e[3]-t[0]*e[0]-t[1]*e[1]-t[2]*e[2]],n3=t=>[-t[0],-t[1],-t[2],t[3]],s3=[0,0,-Math.SQRT1_2,Math.SQRT1_2];function o3(t,e){let n=0,s=[0,0,0];for(const o of t){if(e<=o.end){const r=o.end>n?(e-n)/(o.end-n):1;return[0,1,2].map(a=>s[a]+(o.pos[a]-s[a])*r)}n=o.end,s=o.pos}return s}function Ia(t,e,n,s){const o=new DataView(t),r=new Uint8Array(t),a=new Map,i=n.length,l=n.map((d,p)=>{const m=e.data+p*32;if(m+32>t.byteLength)return{offs:[],hasPos:!1,hasRot:!1};const g=Array.from({length:7},(w,y)=>o.getInt32(m+4+y*4,!0));return{offs:g,hasPos:g.slice(0,3).some(w=>w),hasRot:g.slice(3).some(w=>w)}}),c=n.map(()=>[]),f=n.map(()=>[]),h=new Array(i),u=(e.flags&16)!==0;for(let d=0;d<e.frames;d++){for(let p=0;p<i;p++){const m=n[p],g=l[p],w=e.data+p*32,y=x=>g.offs[x]?e3(r,o,w+g.offs[x],d):0;if(u){g.hasPos&&c[p].push([d,[0,1,2].map(v=>g.offs[v]?m.pos[v]+y(v)*m.ps[v]:0)]);let x=[0,0,0,1];if(g.hasRot){x=[0,1,2,3].map(b=>y(3+b)*m.rs[b]);const v=Math.hypot(x[0],x[1],x[2],x[3])||1;x=x.map(b=>b/v)}h[p]=x;continue}if(g.hasPos||m.parent<0&&e.moves.length){const x=[0,1,2].map(v=>m.pos[v]+y(v)*m.ps[v]);if(m.parent<0&&e.moves.length){const v=o3(e.moves,d);x[0]+=v[1],x[1]-=v[0],x[2]+=v[2]}c[p].push([d,x])}let M=m.q;if(g.hasRot){M=[0,1,2,3].map(v=>y(3+v)*m.rs[v]);const x=Math.hypot(M[0],M[1],M[2],M[3])||1;M=M.map(v=>v/x),m.parent<0&&(M=Oa(s3,M))}h[p]=M}for(let p=0;p<i;p++){if(u||!(n[p].flags&2)||!l[p].hasRot||n[p].parent<0)continue;let m=[0,0,0,1],g=n[p].parent;for(;g>=0&&n[g].parent>=0;)m=Oa(h[g],m),g=n[g].parent;h[p]=Oa(n3(m),h[p])}for(let p=0;p<i;p++)if(l[p].hasRot){const m=h[p];f[p].push([d,[m[3],m[0],m[1],m[2]]])}}return n.forEach((d,p)=>{const m=s.get(d.name.toLowerCase()),g=l[p],w=g.hasPos||d.parent<0&&e.moves.length>0;m===void 0||!w&&!g.hasRot||a.set(m,{pos:w?c[p]:null,rot:g.hasRot?f[p]:null,scl:null})}),{kind:"node",name:e.name,tps:e.fps,chan:a,duration:Math.max(1,e.frames-1)/e.fps,frames:e.frames}}const r3=(t,e)=>[t[0]*e[0]-t[1]*e[1]-t[2]*e[2]-t[3]*e[3],t[0]*e[1]+t[1]*e[0]+t[2]*e[3]-t[3]*e[2],t[0]*e[2]-t[1]*e[3]+t[2]*e[0]+t[3]*e[1],t[0]*e[3]+t[1]*e[2]-t[2]*e[1]+t[3]*e[0]],Ko=(t,e)=>{if(!t||!t.length)return null;const n=t[t.length-1][0],s=n>0?e%(n+1):0;let o=t[0];for(const r of t)if(r[0]<=s)o=r;else break;return o[1]};function a3(t,e,n,s){const o=new Map,r=e.frames??Math.round(e.duration*e.tps)+1,a=new Set([...t.chan.keys(),...e.chan.keys()]);for(const i of a){const l=t.chan.get(i),c=e.chan.get(i);if(n==="fill"){o.set(i,c??l);continue}if(!c){o.set(i,l);continue}const f=s[i]??{t:[0,0,0],q:[1,0,0,0]},h=[],u=[];for(let d=0;d<r;d++){const p=Ko(l?.pos,d)??f.t,m=Ko(l?.rot,d)??f.q,g=Ko(c.pos,d),w=Ko(c.rot,d);h.push([d,g?[p[0]+g[0],p[1]+g[1],p[2]+g[2]]:p]),u.push([d,w?r3(m,w):m])}o.set(i,{pos:h,rot:u,scl:null})}return{...e,chan:o}}function Du(t){const e=new DataView(t),n=new Uint8Array(t),s=e.getInt32(404,!0),o=e.getInt32(408,!0),r=[];if(s<=0||s>64||o<=0||o+s*116>t.byteLength)return r;for(let a=0;a<s;a++){const i=o+a*116,l=i+e.getInt32(i,!0);l>0&&l<t.byteLength&&r.push(Ot(n,l,256).toLowerCase().replace(/\\/g,"/"))}return r}function i3(t){const e=new DataView(t),n=new Uint8Array(t),s=h=>e.getInt32(h,!0),o=h=>e.getFloat32(h,!0),r=s(272),a=s(276),i=s(384),l=s(388),c=[];if(r<=0||r>1e4||a<=0||a+r*764>t.byteLength)return c;const f=[];if(i>0&&i<64&&l>0&&l+i*20<=t.byteLength)for(let h=0;h<i;h++){const u=l+h*20;f.push(Ot(n,u+s(u),64))}for(let h=0;h<r;h++){const u=a+h*764,d=s(u+572),p=s(u+576);if(d<1||p<1||d>16||p>8||d*p<2)continue;const m=[];for(let w=0;w<p;w++)for(let y=0;y<d;y++)m.push(s(u+56+(y*8+w)*4));const g=[0,1].filter(w=>(w?p:d)>1).map(w=>({name:f[s(u+580+w*4)]??`параметр ${w+1}`,start:o(u+588+w*4),end:o(u+596+w*4)}));c.push({name:Ot(n,u+s(u),128),cols:d,rows:p,anims:m,params:g})}return c}function l3(t){const e=new DataView(t),n=m=>e.getInt32(m,!0),s=m=>e.getFloat32(m,!0);if(t.byteLength<64||n(0)!==1448297545)throw new Error("Не VVD");const o=n(16),r=n(48),a=n(52),i=n(56),l=n(60),c=Math.floor((l-i)/48)||o;let f=null;if(r>0){f=[];for(let m=0;m<r;m++){const g=n(a+m*12),w=n(a+m*12+4),y=n(a+m*12+8);if(g>=0)for(let M=0;M<y;M++)f.push(w+M)}}const h=f?f.length:o,u=new Float32Array(h*3),d=new Float32Array(h*3),p=new Float32Array(h*2);for(let m=0;m<h;m++){const g=i+(f?f[m]:m)*48;if(g+48>t.byteLength||f&&f[m]>=c)throw new Error("VVD: вершина вне файла");for(let w=0;w<3;w++)u[m*3+w]=s(g+16+w*4),d[m*3+w]=s(g+28+w*4);p[m*2]=s(g+40),p[m*2+1]=s(g+44)}return{count:h,pos:u,nor:d,uv:p}}function Pu(t,e,n,s,o){const r=new DataView(t),a=h=>r.getInt32(h,!0),i=h=>r.getUint16(h,!0),l=[],c=a(28),f=a(32);if(c!==e.length)throw new Error("VTX: число частей тела не совпадает");for(let h=0;h<c;h++){const u=f+h*8,d=a(u),p=a(u+4),m=[];if(d<1){l.push(m);continue}const g=u+p,w=g+a(g+4),y=a(w),M=a(w+4);if(y!==e[h].meshes.length)throw new Error("VTX: число мешей не совпадает");for(let x=0;x<y;x++){const v=w+M+x*o,b=a(v),k=a(v+4),E=[];if(b<0||b>4096)throw new Error("VTX: странное число strip group");for(let A=0;A<b;A++){const S=v+k+A*s,$=a(S),T=a(S+4),U=a(S+8),_=a(S+12);if($<0||U<0||S+T+$*9>t.byteLength||S+_+U*2>t.byteLength)throw new Error("VTX: выход за границы");for(let R=0;R<U;R++){const L=i(S+_+R*2);if(L>=$)throw new Error("VTX: индекс вне группы");const O=i(S+T+L*9+4),N=e[h].vertBase+e[h].meshes[x].vertexOffset+O;if(N>=n)throw new Error("VTX: вершина вне VVD");E.push(N)}}m.push(E)}l.push(m)}return l}function c3(t,e,n){const s=new DataView(t),o=u=>s.getInt32(u,!0),r=u=>s.getUint16(u,!0),a=[],i=o(28),l=o(32);if(i!==e.length)throw new Error("VTX: число частей тела не совпадает");const c=[],f=[];for(let u=0;u<i;u++){const d=l+u*8;if(o(d)<1)continue;const m=d+o(d+4),g=m+o(m+4),w=o(g),y=g+o(g+4);if(w!==e[u].meshes.length)throw new Error("VTX: число мешей не совпадает");for(let M=0;M<w;M++){const x=y+M*8,v=r(x),b=x+o(x+4);for(let k=0;k<v;k++){const E=b+k*20,A={b:u,k:M,nV:r(E),nI:r(E+2),vo:E+o(E+8),io:E+o(E+12)};if(A.vo<0||A.io<0||A.io+A.nI*2>t.byteLength)throw new Error("VTX: выход за границы");c.push(A)}}}const h=(u,d,p)=>{for(let m=0;m<d;m++)p(u+m*8)};for(let u=0;u<i;u++){const d=l+u*8;for(let p=0;p<o(d);p++){const m=d+o(d+4)+p*8,g=o(m),w=m+o(m+4);for(let y=0;y<g;y++){const M=w+y*12;h(M+o(M+4),o(M),x=>{const v=x+o(x+4);for(let b=0;b<r(x);b++){const k=v+b*20;f.push(k+o(k+8),k+o(k+12))}})}}}f.push(t.byteLength),f.sort((u,d)=>u-d);for(let u=0;u<i;u++)a.push(e[u].meshes.map(()=>[]));for(const u of c){if(!u.nV)continue;const d=f.find(M=>M>u.vo)??t.byteLength,p=e[u.b],m=p.meshes[u.k],g=a[u.b][u.k],w=M=>{if(u.vo+u.nV*M>t.byteLength)return!1;for(let x=0;x<u.nV;x++)if(r(u.vo+x*M+M-2)>=m.numVertices)return!1;return!0},y=[Math.floor((d-u.vo)/u.nV),12,2].find(M=>M>=2&&M<=16&&w(M));if(!y)throw new Error("VTX: не удалось определить размер вершины");for(let M=0;M<u.nI;M++){const x=r(u.io+M*2);if(x>=u.nV)throw new Error("VTX: индекс вне группы");const v=r(u.vo+x*y+y-2),b=p.vertBase+m.vertexOffset+v;if(v>=m.numVertices||b>=n)throw new Error("VTX: вершина вне меша");g.push(b)}}return a}function Nu(t,e,n,s,o,r){let a=null,i;if(n.byteLength>=36&&new DataView(n).getInt32(0,!0)===107&&(a=c3(n,t,e.count)),!a)for(const[c,f]of[[25,9],[33,9],[25,12],[33,12]])try{a=Pu(n,t,e.count,c,f);break}catch(h){i=h}if(!a)for(const c of t)c.vertBase=c.cumBase??0;if(!a)for(const[c,f]of[[25,9],[33,9]])try{a=Pu(n,t,e.count,c,f);break}catch(h){i=h}if(!a)throw new Error("Не удалось разобрать VTX: "+(i instanceof Error?i.message:""));const l=[];if(t.forEach((c,f)=>c.meshes.forEach((h,u)=>{const d=a[f][u];if(!d||!d.length)return;const p=new Map,m=[],g=d.map($=>{let T=p.get($);return T===void 0&&(T=m.length,p.set($,T),m.push($)),T}),w=m.length,y=new Float32Array(w*3),M=new Float32Array(w*3),x=new Float32Array(w*2),v=new Float32Array(w*4),b=new Float32Array(w*4);m.forEach(($,T)=>{y.set(e.pos.subarray($*3,$*3+3),T*3),e.nor&&M.set(e.nor.subarray($*3,$*3+3),T*3),x.set(e.uv.subarray($*2,$*2+2),T*2),e.bone&&e.weight&&(v.set(e.bone.subarray($*4,$*4+4),T*4),b.set(e.weight.subarray($*4,$*4+4),T*4))});const k=s[o[h.material]!==void 0?o[h.material]:h.material]??"",A=(r.length?r:[""]).map($=>`materials/${$&&!$.endsWith("/")?$+"/":$}${k}.vmt`.replace(/\\/g,"/").toLowerCase().replace(/\/+/g,"/")),S=$e(w,g);l.push({name:`body ${f} / mesh ${u}`,texture:k,materialPaths:A,shader:"",vertexCount:w,pos:y,nor:e.nor?M:ge(y,S),uv:x,bone:v,weight:b,indices:S,skinned:!!e.bone})})),!l.length)throw new Error("В Source-модели нет геометрии");return l}function Da(t,e,n=0){const s=new DataView(t),o=new Uint8Array(t),r=F=>s.getInt32(F,!0),a=F=>s.getFloat32(F,!0);if(r(0)!==1414743113||r(4)!==2531)throw new Error("Не MDL v2531 (VTMB)");const i=r(292),l=r(296),c=r(300),f=r(304),h=r(308),u=r(316),d=r(320),p=r(324),m=[];for(let F=0;F<i;F++){const C=l+F*20;m.push(Ot(o,C+r(C),128))}const g=[];for(let F=0;F<c;F++)g.push(Ot(o,r(f+F*4),256).replace(/\\/g,"/").toLowerCase());const w=[],y=Math.max(0,Math.min(n,r(312)-1));for(let F=0;F<h;F++)w.push(s.getInt16(u+(y*h+F)*2,!0));const M=[],x=[];let v=0;for(let F=0;F<d;F++){const C=p+F*16,P=r(C+4),I=C+r(C+12);if(!P){M.push({meshes:[],vertBase:v,cumBase:v});continue}const B=r(I+136),z=I+r(I+140),j=r(I+144),X=I+r(I+148),J=r(I+156),G=[],H=J===0?44:J===1?12:J===2?8:0;if(!H)throw new Error(`MDL v2531: тип вершин ${J} не поддерживается`);if(B<0||B>4096||j<0||X+j*H>t.byteLength)throw new Error("MDL v2531: неожиданная раскладка модели");for(let K=0;K<B;K++){const Q=z+K*60;G.push({material:r(Q),numVertices:r(Q+8),vertexOffset:r(Q+12)})}M.push({meshes:G,vertBase:v,cumBase:v}),x.push({off:X,n:j,type:J,o:[a(I+160),a(I+164),a(I+168)],s:[a(I+172),a(I+176),a(I+180)]}),v+=j}const b=new Float32Array(v*3),k=new Float32Array(v*3),E=new Float32Array(v*2),A=Ra(t),S=A.length>1&&x.some(F=>F.type===0),$=new Float32Array(v*4),T=new Float32Array(v*4);let U=0,_=!1;for(const F of x)for(let C=0;C<F.n;C++,U++)if(F.type===0){const P=F.off+C*44;for(let B=0;B<3;B++)b[U*3+B]=a(P+12+B*4),k[U*3+B]=a(P+24+B*4);const I=o[P]+o[P+1]+o[P+2]||1;for(let B=0;B<3;B++){const z=s.getInt16(P+4+B*2,!0);o[P+B]&&z>=0&&z<A.length&&($[U*4+B]=z,T[U*4+B]=o[P+B]/I)}!o[P]&&!o[P+1]&&!o[P+2]&&(T[U*4]=1),E[U*2]=a(P+36),E[U*2+1]=a(P+40)}else if(F.type===1){const P=F.off+C*12;_=!0;for(let I=0;I<3;I++)b[U*3+I]=F.o[I]+s.getUint16(P+I*2,!0)*F.s[I];E[U*2]=o[P+9]/255,E[U*2+1]=o[P+11]/255}else{const P=F.off+C*8;_=!0;for(let I=0;I<3;I++)b[U*3+I]=F.o[I]+o[P+I]/255*F.s[I];E[U*2]=o[P+5]/255,E[U*2+1]=o[P+7]/255}if(S){let F=0;for(const C of x){if(C.type)for(let P=0;P<C.n;P++)T[(F+P)*4]=1;F+=C.n}}const R=Nu(M,{count:v,pos:b,nor:_?null:k,uv:E,...S?{bone:$,weight:T}:{}},e,m,w,g),L=[`VTMB MDL v2531: частей тела ${d}, мешей ${R.length}, треугольников ${Ut(R)}, текстур ${i}, костей ${A.length}`];if(!S)return{meshes:R,skeleton:null,motions:[],bounds:dt(R),flipZ:!1,defaultZUp:!0,cameraYaw:Math.PI/2,info:L};const O=new Kt(t3(A)),N=new Map(A.map((F,C)=>[F.name.toLowerCase(),C])),D=Wo(t).map(F=>Ia(t,F,A,N));return{meshes:R,skeleton:O,motions:D,bounds:Jt(O,R),flipZ:!1,defaultZUp:!0,cameraYaw:Math.PI/2,info:L}}function Pa(t,e,n){const s=new DataView(t),o=new Uint8Array(t),r=b=>s.getInt32(b,!0);if(r(0)!==1414743113)throw new Error("Не Source MDL");const a=r(4);if(a<44||a>49)throw new Error(`Source MDL v${a} пока не поддерживается (44–49)`);const i=r(204),l=r(208),c=r(212),f=r(216),h=r(220),u=r(228),d=r(232),p=r(236),m=[];for(let b=0;b<i;b++){const k=l+b*64;m.push(Ot(o,k+r(k),128))}const g=[];for(let b=0;b<c;b++)g.push(Ot(o,r(f+b*4),256).replace(/\\/g,"/").toLowerCase());const w=[];for(let b=0;b<h;b++)w.push(s.getInt16(u+b*2,!0));const y=[];let M=0;for(let b=0;b<d;b++){const k=p+b*16,E=r(k+4),A=k+r(k+12);if(!E){y.push({meshes:[],vertBase:0});continue}const S=r(A+72),$=A+r(A+76),T=r(A+80),U=r(A+84),_=[];for(let L=0;L<S;L++){const O=$+L*116;_.push({material:r(O),numVertices:r(O+8),vertexOffset:r(O+12)})}const R=U%48===0?U/48:M;y.push({meshes:_,vertBase:R,cumBase:M}),M+=T}const x=l3(e),v=Nu(y,x,n,m,w,g);return{meshes:v,skeleton:null,motions:[],bounds:dt(v),flipZ:!1,defaultZUp:!0,cameraYaw:Math.PI/2,info:[`Source MDL v${a}: частей тела ${d}, мешей ${v.length}, треугольников ${Ut(v)}`,"LOD 0, поза покоя. Скелет и анимации (sequences/.ani) — следующая часть."]}}async function f3(t,e){const n=await t.arrayBuffer(),s=new DataView(n);if(n.byteLength<8||s.getInt32(0,!0)!==1414743113)throw new Error("Не Source MDL (нет сигнатуры IDST)");const o=s.getInt32(4,!0),r=t.name.toLowerCase().replace(/\.mdl$/,"");if(o>=44&&o<=49){const a=await e.sibling(r+".vvd");let i=null;for(const l of[".dx90.vtx",".dx80.vtx",".sw.vtx",".vtx"])if(i=await e.sibling(r+l),i)break;if(!a||!i)throw new Error(`Для Source-модели нужны ${r}.vvd и ${r}.dx90.vtx рядом (выберите их вместе или откройте «Папку игры…»/VPK)`);return Pa(n,a,i)}if(o===2531){let a=null;const i=t.path&&/(^|\/)models\//.test(t.path)?t.path.slice(t.path.search(/(^|\/)models\//)).replace(/^\//,"").replace(/\.mdl$/,""):"";for(const c of[".dx80.vtx",".dx7_2bone.vtx",".vtx"]){if(a=await e.sibling(r+c),!a&&i){const f=await e.readPath(i+c);f&&(a=f.slice().buffer)}if(a)break}if(!a)throw new Error(`Для модели VTMB нужен ${r}.dx80.vtx рядом (выберите его вместе с .mdl или откройте папку игры)`);const l=Da(n,a);return l.skeleton&&await Na(l,n,c=>e.readPath(c)),l}throw new Error(`Source MDL версии ${o} не поддерживается`)}async function zu(t,e,n=0,s=!1){const o=c=>c?c.byteOffset===0&&c.byteLength===c.buffer.byteLength?c.buffer:c.slice().buffer:null,r=o(await e(t));if(!r||r.byteLength<8)return null;const a=new DataView(r).getInt32(4,!0),i=t.replace(/\.mdl$/,""),l=async c=>{for(const f of c){const h=o(await e(i+f));if(h)return h}return null};if(a===2531){const c=await l([".dx80.vtx",".dx7_2bone.vtx",".vtx"]);if(!c)return null;const f=Da(r,c,n);return s&&f.skeleton&&await Na(f,r,e),f}if(a>=44&&a<=49){const c=await l([".vvd"]),f=await l([".dx90.vtx",".dx80.vtx",".sw.vtx",".vtx"]);return c&&f?Pa(r,c,f):null}return null}async function Na(t,e,n){const s=Ra(e),o=new Map(s.map((y,M)=>[y.name.toLowerCase(),M])),r=new Set,a=new Set(t.motions.map(y=>y.name.toLowerCase())),i=Du(e);let l=0,c=0;const f=new Set;Wo(e).forEach(y=>{if(y.flags&16){const M=t.motions.find(x=>x.name===y.name);M&&f.add(M)}});let h=0;const u=(y,M,x)=>{const v=new Map(Wo(y).map(b=>[b.index,b]));for(const b of i3(y)){const k=b.anims.map($=>v.get($));if(k.some($=>!$))continue;const E=k[0],A=`${b.name} ⇄ ${b.params.map($=>$.name).join(", ")}`;if(a.has(A.toLowerCase()))continue;a.add(A.toLowerCase());const S={kind:"node",name:A,tps:E.fps,chan:new Map,duration:Math.max(1,E.frames-1)/E.fps,frames:E.frames,source:x,load:async()=>{const $=new Map,T=k.map(U=>{let _=$.get(U.index);return _||$.set(U.index,_=Ia(y,U,M,o)),_});return{...T[0],name:A,blend:{motions:T,cols:b.cols,rows:b.rows,x:(b.cols-1)/2,y:(b.rows-1)/2,params:b.params}}}};t.motions.push(S),h++}};for(u(e,s,"model");i.length&&r.size<64;){const y=i.shift();if(r.has(y))continue;r.add(y);const M=await n(y);if(!M){c++;continue}const x=M.byteOffset===0&&M.byteLength===M.buffer.byteLength?M.buffer:M.slice().buffer;if(x.byteLength<420||new DataView(x).getInt32(4,!0)!==2531)continue;l++,i.push(...Du(x));const v=Ra(x),b=y.split("/").pop().replace(/\.mdl$/,"");u(x,v,b);for(const k of Wo(x)){const E=a.has(k.name.toLowerCase()),A=E?`${k.name} (${b})`:k.name;if(a.has(A.toLowerCase()))continue;a.add(A.toLowerCase());const S={kind:"node",name:A,tps:k.fps,chan:new Map,duration:Math.max(1,k.frames-1)/k.fps,frames:k.frames,source:b,load:async()=>Ia(x,k,v,o)};t.motions.push(S),k.flags&16&&f.add(S)}}const d=s.map(y=>({t:y.pos,q:[y.q[3],y.q[0],y.q[1],y.q[2]]})),p=y=>y.name.toLowerCase().replace(/ \(.*\)$/,""),m=new Map;for(const y of t.motions)m.has(p(y))||m.set(p(y),y);const g=y=>f.has(y)||/_layer$/.test(p(y));let w=0;for(const y of t.motions){if(y.kind!=="node"||!g(y))continue;const M=p(y).replace(/_(layer|delta)$/,""),x=M.split("_")[0],v=[m.get(`${x}_idle`),m.get(M)].find(A=>A&&!g(A))??za(t.motions.filter(A=>!g(A)));if(!v||v.kind!=="node")continue;const b=y.load??null,k={...y},E=f.has(y)?"add":"fill";y.load=async()=>{const A=b?await b():k,S=v.load&&!v.chan.size?await v.load():v;return{...a3(S,A,E,d),name:y.name}},y.source=`${y.source&&y.source!=="model"?y.source+" · ":""}${E==="add"?"+":"поверх"} ${v.name}`,w++}(l||c)&&t.info.push(`Анимации: своих и общих ${t.motions.length} (include-моделей ${l}${c?`, не найдено ${c}`:""}); частичных и дельта-анимаций поверх стойки ${w}; смешиваемых последовательностей ${h}`)}function za(t,e){const n=[...t.filter(r=>!/^pc|pcidles/i.test(r.source??"")),...t.filter(r=>/^pc|pcidles/i.test(r.source??""))],s=r=>n.find(a=>r(a.name.toLowerCase().replace(/ \(.*\)$/,""))),o=e?.toLowerCase().replace(/^@/,"");return(o&&o!=="none"?s(r=>r===o):void 0)??s(r=>r==="idle01"||r==="idle"||r==="idle1")??s(r=>/^stance_.*idle_1$/.test(r))??s(r=>/^idle/.test(r))??s(r=>/(^|_)idle$/.test(r)&&!/feed|layer/.test(r))??n.find(r=>!r.source)??null}async function Bu(t,e,n=0){const s=t.skeleton;if(!s)return t.meshes;let o=e;o?.load&&o.kind==="node"&&!o.chan.size&&(o=await o.load()),s.pose(o,n,!1);const r=s.skinV;return t.meshes.map(a=>{if(!a.skinned)return a;const i=a.vertexCount,l=new Float32Array(i*3),c=new Float32Array(i*3);for(let f=0;f<i;f++){const h=a.pos[f*3],u=a.pos[f*3+1],d=a.pos[f*3+2],p=a.nor[f*3],m=a.nor[f*3+1],g=a.nor[f*3+2];for(let y=0;y<4;y++){const M=a.weight[f*4+y];if(!M)continue;const x=r[a.bone[f*4+y]];l[f*3]+=M*(x[0]*h+x[4]*u+x[8]*d+x[12]),l[f*3+1]+=M*(x[1]*h+x[5]*u+x[9]*d+x[13]),l[f*3+2]+=M*(x[2]*h+x[6]*u+x[10]*d+x[14]),c[f*3]+=M*(x[0]*p+x[4]*m+x[8]*g),c[f*3+1]+=M*(x[1]*p+x[5]*m+x[9]*g),c[f*3+2]+=M*(x[2]*p+x[6]*m+x[10]*g)}const w=Math.hypot(c[f*3],c[f*3+1],c[f*3+2])||1;for(let y=0;y<3;y++)c[f*3+y]/=w}return{...a,pos:l,nor:c,skinned:!1}})}const u3=Object.freeze(Object.defineProperty({__proto__:null,bakePose:Bu,loadMdlByPath:zu,loadSource:Pa,loadSourceMdl:f3,loadVtmb:Da,pickIdle:za,vtmbIncludeMotions:Na},Symbol.toStringTag,{value:"Module"})),h3=2,d3=4,p3=64,m3=128,g3=256,w3=512,y3=2048;function b3(t){const e=[];for(const n of t.match(/\{[^{}]*\}/g)??[]){const s=new Map;for(const o of n.matchAll(/"([^"]*)"\s+"([^"]*)"/g))s.has(o[1].toLowerCase())||s.set(o[1].toLowerCase(),o[2]);e.push(s)}return e}const Qs=t=>{const e=(t??"").trim().split(/\s+/).map(Number);return e.length>=3&&e.every(Number.isFinite)?e.slice(0,3):[0,0,0]};function Ba(t,e,n=1){const s=Math.PI/180,[o,r,a]=[t[0]*s,t[1]*s,t[2]*s],i=Math.sin(o),l=Math.cos(o),c=Math.sin(r),f=Math.cos(r),h=Math.sin(a),u=Math.cos(a);return[l*f*n,l*c*n,-i*n,0,(h*i*f-u*c)*n,(h*i*c+u*f)*n,h*l*n,0,(u*i*f+h*c)*n,(u*i*c-h*f)*n,u*l*n,0,e[0],e[1],e[2],1]}function Js(t,e){const n=ye(t,e),s=n.replace(/[[\]{}]/g," ").trim().split(/\s+/).map(Number);return s.length!==3||!s.every(Number.isFinite)?null:new RegExp(`"?${e.replace(/\$/g,"\\$")}"?[ ]+"?([[{])`,"i").exec(t)?.[1]==="{"||s.some(r=>r>4)?s.map(r=>r/255):s}function Vu(t,e,n){const s=(r,a)=>{const i=parseFloat(ye(t,r));return Number.isFinite(i)?i:a},o=r=>r.map(a=>Math.min(1.5,Math.max(0,a)));if(e==="water"){const r=Js(t,"$fogcolor")??[.05,.08,.08],a=ye(t,"$refracttexture"),i=parseFloat(ye(t,"texturescrollrate"))||0,l=(parseFloat(ye(t,"texturescrollangle"))||0)*Math.PI/180,c=ye(t,"$fogenable")!=="0";return{kind:"water",refractTint:o(Js(t,"$refracttint")??(a?[1,1,1]:r)),reflectTint:o(Js(t,"$reflecttint")??[.8,.8,.8]),refract:Math.min(.05,s("$refractamount",a?20:8)*8e-4),reflect:Math.min(.05,s("$reflectamount",30)*5e-4),...c?{fog:{color:o(r),start:s("$fogstart",0),end:Math.max(s("$fogend",512),s("$fogstart",0)+1)}}:{},...i?{scroll:[Math.cos(l)*i,Math.sin(l)*i]}:{},fallback:{color:r.map((f,h)=>Math.min(1,.6*f+[.08,.13,.15][h])),opacity:.8}}}if(e==="refract"||!n&&ye(t,"$refracttexture")){const r=o(Js(t,"$refracttint")??[1,1,1]),a=ye(t,"$envmap")?o(Js(t,"$envmaptint")??[1,1,1]):null;return{kind:"glass",refractTint:r,...a?{reflectTint:a}:{},refract:Math.min(.06,s("$refractamount",1)*.01),fallback:{color:r.map(i=>Math.min(1,i)),opacity:.3}}}return null}async function v3(t,e){const n=await t.arrayBuffer(),s=new DataView(n),o=new Uint8Array(n);if(n.byteLength<1036||s.getUint32(0,!0)!==1347633750)throw new Error("Не Source BSP (нет VBSP)");const r=s.getInt32(4,!0);if(r<17||r>21)throw new Error(`Source BSP v${r} не поддерживается (17–21)`);const a=q=>{const Z=8+q*16,[ot,ut]=r===21&&s.getInt32(Z,!0)<256&&s.getInt32(Z+4,!0)>=1036?[s.getInt32(Z+4,!0),s.getInt32(Z+8,!0)]:[s.getInt32(Z,!0),s.getInt32(Z+4,!0)];return ot>0&&ut>0&&ot+ut<=n.byteLength?[ot,ut]:[0,0]},i=q=>s.getFloat32(q,!0),l=q=>s.getInt32(q,!0),c=[],[f,h]=a(3),u=h/12,[d]=a(1),[p]=a(12),[m]=a(13),[g,w]=a(6),y=w/72,[M,x]=a(2),v=x/32,[b,k]=a(44),[E,A]=a(43),[S,$]=a(7),T=r===17?104:56,U=Math.floor($/T),[_,R]=a(14),L=R/48,[O,N]=a(26),[D]=a(33);let[F,C]=a(8);if(C||([F,C]=a(53)),!U||!u)throw new Error(`${t.name}: в карте нет граней`);const P=q=>{const Z=l(m+q*4),ot=Math.abs(Z);return s.getUint16(p+ot*4+(Z<0?2:0),!0)},I=[];for(let q=0;q<v;q++){const Z=l(M+q*32+12),ot=Z>=0&&Z*4<k?l(b+Z*4):-1;let ut="";if(ot>=0)for(let at=E+ot;at<E+A&&o[at];at++)ut+=String.fromCharCode(o[at]);I.push(ut)}const B=q=>[l(M+q*32+16)||256,l(M+q*32+20)||256],z=q=>{const Z=S+q*T;return r===17?{plane:s.getUint16(Z+32,!0),side:o[Z+34],firstEdge:l(Z+36),numEdges:s.getInt16(Z+40,!0),texinfo:s.getInt16(Z+42,!0),disp:s.getInt16(Z+44,!0),lightofs:l(Z+72),lmMins:[l(Z+80),l(Z+84)],lmSize:[l(Z+88),l(Z+92)]}:{plane:s.getUint16(Z,!0),side:o[Z+2],firstEdge:l(Z+4),numEdges:s.getInt16(Z+8,!0),texinfo:s.getInt16(Z+10,!0),disp:s.getInt16(Z+12,!0),lightofs:l(Z+20),lmMins:[l(Z+28),l(Z+32)],lmSize:[l(Z+36),l(Z+40)]}},[j,X]=a(0);let J="";for(let q=j;q<j+X&&o[q];q++)J+=String.fromCharCode(o[q]);const G=b3(J);let H=null;const[K,Q]=a(40);if(Q)try{const q=new Blob([o.subarray(K,K+Q)]);Object.defineProperty(q,"name",{value:"pakfile.zip"}),H=await cs(q)}catch{H=null}const V=new TextDecoder("latin1"),W=new Map,Y=async q=>{let Z=xn(q);const ot=Z,ut=W.get(ot);if(ut)return ut;const at={stem:Z,alphaTest:!1,translucent:!1,tool:/^tools\//.test(Z),found:!1};for(let lt=0;lt<3;lt++){const it="materials/"+Z+".vmt",wt=H?.lookup(it),Et=wt?await H.read(wt):await e.readPath(it);if(!Et)break;at.found=!0;const bt=zo(V.decode(Et));if(wt&&bt.base&&(at.tex=xn(bt.base)),bt.include){Z=xn(bt.include),at.stem=Z;continue}at.alphaTest=bt.alphaTest,at.translucent=bt.translucent;const Tt=V.decode(Et),Bt=ye(Tt,"$basetexture2"),_t=Vu(Tt,bt.shader,!!bt.base);_t&&(/waterbeneath/.test(Z)?at.tool=!0:at.fx=_t),/worldvertextransition/.test(bt.shader)&&Bt&&(at.wvt=xn(Bt)),(/^tools\//.test(Z)||/^(nodraw|toolsnodraw)$/.test(bt.shader))&&(at.tool=!0);break}if(!at.found&&/^maps\/[^/]+\//.test(Z)){const lt=Z.replace(/^maps\/[^/]+\//,"").replace(/(_-?\d+){3}$/,""),it=await Y(lt);return W.set(ot,it),it}return W.set(ot,at),at},tt=2048,nt=[];let et=4,rt=0,mt=4;const xt=new Map,St=new Set,Pt=[];for(let q=0;q<U;q++)Pt.push(z(q));if(C){const q=Pt.map((Z,ot)=>({f:Z,i:ot})).filter(({f:Z})=>Z.lightofs>=0&&Z.lmSize[0]>=0&&Z.lmSize[1]>=0&&Z.lmSize[0]<512&&Z.lmSize[1]<512).sort((Z,ot)=>ot.f.lmSize[1]-Z.f.lmSize[1]);for(const{f:Z,i:ot}of q){const ut=Z.lmSize[0]+1,at=Z.lmSize[1]+1;if(Z.lightofs+ut*at*4>C)continue;Z.texinfo>=0&&Z.texinfo<y&&l(g+Z.texinfo*72+64)&y3&&Z.lightofs+ut*at*16<=C&&St.add(ot),et+ut+2>tt&&(et=0,rt+=mt+2,mt=0);const lt={f:ot,x:et+1,y:rt+1,w:ut,h:at};nt.push(lt),xt.set(ot,lt),et+=ut+2,mt=Math.max(mt,at)}}const Me=nt.length?1<<Math.ceil(Math.log2(rt+mt+2)):0;let de=null,ze=null;if(Me&&Me<=8192){de=new Uint8Array(tt*Me*3),St.size&&(ze=[new Uint8Array(tt*Me*3),new Uint8Array(tt*Me*3),new Uint8Array(tt*Me*3)]);for(let ot=0;ot<4;ot++)for(let ut=0;ut<4;ut++)de.fill(128,(ot*tt+ut)*3,(ot*tt+ut)*3+3);const q=ot=>{const ut=s.getInt8(ot+3),at=Math.pow(2,ut)/255;return[o[ot]*at,o[ot+1]*at,o[ot+2]*at]},Z=ot=>Math.min(255,Math.round(Math.pow(Math.min(4,ot),1/2.2)*127.5));if(ze)for(const ot of ze)ot.set(de.subarray(0,tt*4*3));for(const ot of nt){const ut=Pt[ot.f],at=ze&&St.has(ot.f),lt=ot.w*ot.h*4;for(let it=-1;it<=ot.h;it++)for(let wt=-1;wt<=ot.w;wt++){const Et=Math.min(ot.w-1,Math.max(0,wt)),bt=Math.min(ot.h-1,Math.max(0,it)),Tt=F+ut.lightofs+(bt*ot.w+Et)*4,Bt=q(Tt),_t=((ot.y+it)*tt+ot.x+wt)*3;for(let pt=0;pt<3;pt++)de[_t+pt]=Z(Bt[pt]);if(ze)for(let pt=0;pt<3;pt++){const jt=ze[pt];if(at){const ct=q(Tt+(pt+1)*lt);for(let ie=0;ie<3;ie++)jt[_t+ie]=Z(ct[ie])}else for(let ct=0;ct<3;ct++)jt[_t+ct]=de[_t+ct]}}}}const[yo,bo]=a(10),[Nr,ts]=a(5),[zr,xl]=a(16),vo=r===19&&bo%56===0?56:32,pe=Math.floor(bo/vo),Be=q=>q>=0&&q<pe?s.getInt16(yo+q*vo+6,!0)&511:-1,Ve=q=>{if(!ts||!L)return-1;let Z=l(_+36);for(let ot=0;Z>=0&&ot<4096;ot++){if((Z+1)*32>ts)return-1;const ut=Nr+Z*32,at=d+l(ut)*20,lt=i(at)*q[0]+i(at+4)*q[1]+i(at+8)*q[2]-i(at+12);Z=l(ut+(lt>=0?4:8))}return-1-Z},Fn=G.find(q=>q.get("classname")==="sky_camera"),yn=Qs(Fn?.get("origin")),Qe=+(Fn?.get("scale")??16)||16;let Te=Fn?Be(Ve(yn)):-1;const xo=G.find(q=>/^info_player_start$/.test(q.get("classname")??""));Te>=0&&xo&&Be(Ve(Qs(xo.get("origin"))))===Te&&(Te=-1);const es=new Set;if(Te>=0)for(let q=0;q<pe;q++){if(Be(q)!==Te)continue;const Z=yo+q*vo,ot=s.getUint16(Z+20,!0),ut=s.getUint16(Z+22,!0);for(let at=0;at<ut&&(ot+at)*2<xl;at++)es.add(s.getUint16(zr+(ot+at)*2,!0))}const Br=q=>[(q[0]-yn[0])*Qe,(q[1]-yn[1])*Qe,(q[2]-yn[2])*Qe],Ls=new Map;Ls.set(0,[0,0,0]);for(const q of G){const Z=/^\*(\d+)$/.exec(q.get("model")??"");if(!Z)continue;const ot=q.get("classname")??"";/^(trigger_|func_areaportal|func_occluder|func_viscluster|env_|func_ladder|func_clip|func_precipitation)/.test(ot)||Ls.set(+Z[1],Qs(q.get("origin")))}const ns=new Map,Mo=q=>{let Z=ns.get(q);return Z||(Z={pos:[],nor:[],uv:[],uv2:[],idx:[],alpha:[]},ns.set(q,Z)),Z},ko=new Map;for(let q=0;q<v;q++)ko.set(q,await Y(I[q]));let bn=0,ss=0,Rs=0;const K0=new Set;for(const[q,Z]of Ls){if(q>=L)continue;const ot=l(_+q*48+40),ut=l(_+q*48+44);for(let at=ot;at<ot+ut&&at<U;at++){if(K0.has(at))continue;K0.add(at);const lt=Pt[at];if(lt.texinfo<0||lt.texinfo>=y||lt.numEdges<3){bn++;continue}const it=g+lt.texinfo*72,wt=l(it+64),Et=l(it+68);if(wt&(d3|h3|m3|g3|w3|p3)){bn++;continue}const bt=ko.get(Et);if(!bt||bt.tool){bn++;continue}const Tt=es.has(at),[Bt,_t]=B(Et),pt=[i(d+lt.plane*20),i(d+lt.plane*20+4),i(d+lt.plane*20+8)];lt.side&&(pt[0]=-pt[0],pt[1]=-pt[1],pt[2]=-pt[2]);let jt="";if(bt.fx?.kind==="water"){if(pt[2]<.7){bn++;continue}const Mt=P(lt.firstEdge),Gt=pt[0]*(i(f+Mt*12)+Z[0])+pt[1]*(i(f+Mt*12+4)+Z[1])+pt[2]*(i(f+Mt*12+8)+Z[2]);jt="#"+[...pt.map(vn=>vn.toFixed(3)),Gt.toFixed(1)].join(",")}const ct=Mo((Tt?"sky|":"")+bt.stem+jt),ie=xt.get(at),Is=(Mt,Gt)=>i(it+Mt*16)*Gt[0]+i(it+Mt*16+4)*Gt[1]+i(it+Mt*16+8)*Gt[2]+i(it+Mt*16+12),$o=(Mt,Gt)=>i(it+32+Mt*16)*Gt[0]+i(it+32+Mt*16+4)*Gt[1]+i(it+32+Mt*16+8)*Gt[2]+i(it+32+Mt*16+12),os=(Mt,Gt,vn)=>{const Xr=[Mt[0]+Z[0],Mt[1]+Z[1],Mt[2]+Z[2]],Xt=Tt?Br(Xr):Xr;ct.pos.push(Xt[0],Xt[1],Xt[2]),ct.nor.push(Gt[0],Gt[1],Gt[2]),ct.uv.push(Is(0,vn)/Bt,Is(1,vn)/_t),ie?ct.uv2.push((ie.x+Math.min(ie.w-.5,Math.max(.5,$o(0,vn)-lt.lmMins[0]+.5)))/tt,(ie.y+Math.min(ie.h-.5,Math.max(.5,$o(1,vn)-lt.lmMins[1]+.5)))/Me):ct.uv2.push(-1,-1)},Je=[];for(let Mt=0;Mt<lt.numEdges;Mt++){const Gt=P(lt.firstEdge+Mt);Je.push([i(f+Gt*12),i(f+Gt*12+4),i(f+Gt*12+8)])}if(lt.disp>=0&&N&<.disp*176<N&&Je.length===4){const Mt=O+lt.disp*176,Gt=[i(Mt),i(Mt+4),i(Mt+8)],vn=l(Mt+12),Xr=l(Mt+20),Xt=(1<<Xr)+1;let rm=0,am=1/0;Je.forEach((ft,Ct)=>{const kt=Math.hypot(ft[0]-Gt[0],ft[1]-Gt[1],ft[2]-Gt[2]);kt<am&&(am=kt,rm=Ct)});const Ps=[0,1,2,3].map(ft=>Je[(rm+ft)%4]),Ue=ct.pos.length/3;for(let ft=0;ft<Xt;ft++)for(let Ct=0;Ct<Xt;Ct++){const kt=ft/(Xt-1),me=Ct/(Xt-1),Ht=[0,1,2].map(ke=>Ps[0][ke]+(Ps[1][ke]-Ps[0][ke])*kt),_n=[0,1,2].map(ke=>Ps[3][ke]+(Ps[2][ke]-Ps[3][ke])*kt),rs=[0,1,2].map(ke=>Ht[ke]+(_n[ke]-Ht[ke])*me),as=D+(vn+ft*Xt+Ct)*20,Ns=i(as+12);os([rs[0]+i(as)*Ns,rs[1]+i(as+4)*Ns,rs[2]+i(as+8)*Ns],pt,rs)}const Fl=ct.idx.length;for(let ft=0;ft<Xt-1;ft++)for(let Ct=0;Ct<Xt-1;Ct++){const kt=Ue+ft*Xt+Ct,me=kt+1,Ht=kt+Xt,_n=Ht+1;Ct+ft&1?ct.idx.push(kt,Ht,me,me,Ht,_n):ct.idx.push(kt,Ht,_n,kt,_n,me)}const tn=new Float32Array(Xt*Xt*3);for(let ft=Fl;ft<ct.idx.length;ft+=3){const Ct=ct.idx[ft]-Ue,kt=ct.idx[ft+1]-Ue,me=ct.idx[ft+2]-Ue,Ht=(So,R4)=>ct.pos[(Ue+So)*3+R4],_n=Ht(kt,0)-Ht(Ct,0),rs=Ht(kt,1)-Ht(Ct,1),as=Ht(kt,2)-Ht(Ct,2),Ns=Ht(me,0)-Ht(Ct,0),ke=Ht(me,1)-Ht(Ct,1),im=Ht(me,2)-Ht(Ct,2),_4=rs*im-as*ke,C4=as*Ns-_n*im,L4=_n*ke-rs*Ns;for(const So of[Ct,kt,me])tn[So*3]+=_4,tn[So*3+1]+=C4,tn[So*3+2]+=L4}let _l=0;for(let ft=0;ft<Xt*Xt;ft++)_l+=tn[ft*3]*pt[0]+tn[ft*3+1]*pt[1]+tn[ft*3+2]*pt[2];const F4=_l<0?-1:1;if(_l<0)for(let ft=Fl;ft<ct.idx.length;ft+=3){const Ct=ct.idx[ft+1];ct.idx[ft+1]=ct.idx[ft+2],ct.idx[ft+2]=Ct}for(let ft=0;ft<Xt*Xt;ft++){const Ct=Math.hypot(tn[ft*3],tn[ft*3+1],tn[ft*3+2])||1;for(let kt=0;kt<3;kt++)ct.nor[(Ue+ft)*3+kt]=tn[ft*3+kt]/Ct*F4}if(bt.wvt){const ft=Mo((Tt?"sky|":"")+"wvt|"+bt.stem),Ct=ft.pos.length/3;for(let kt=0;kt<Xt*Xt;kt++){for(let me=0;me<3;me++)ft.pos.push(ct.pos[(Ue+kt)*3+me]),ft.nor.push(ct.nor[(Ue+kt)*3+me]);ft.uv.push(ct.uv[(Ue+kt)*2],ct.uv[(Ue+kt)*2+1]),ft.uv2.push(ct.uv2[(Ue+kt)*2],ct.uv2[(Ue+kt)*2+1]),ft.alpha.push(Math.min(1,Math.max(0,i(D+(vn+kt)*20+16)/255)))}for(let kt=Fl;kt<ct.idx.length;kt++)ft.idx.push(ct.idx[kt]-Ue+Ct)}ss++;continue}const Ds=ct.pos.length/3;for(const Mt of Je)os(Mt,pt,Mt);for(let Mt=1;Mt+1<Je.length;Mt++)ct.idx.push(Ds,Ds+Mt+1,Ds+Mt);Rs++}}const Ml=[];let Y0=0,Z0=0,kl=0,El=0;for(const[q,Z]of ns){if(!Z.idx.length)continue;const ot=q.startsWith("sky|"),ut=ot?q.slice(4):q,[at,lt]=ut.split("#"),it=at.startsWith("wvt|"),wt=it?at.slice(4):at,Et=W.get(wt)??[...W.values()].find(ct=>ct.stem===wt),bt=Z.pos.length/3,Tt=Float32Array.from(Z.pos),Bt=!!de&&Z.uv2.some(ct=>ct>=0),_t=it?Et.wvt:wt,pt=it?void 0:Et?.fx,jt=lt?lt.split(",").map(Number):void 0;pt&&El++,ot&&(Z0+=Z.idx.length/3),it&&kl++,Ml.push({name:`${ot?"небо 3D":"карта"} · ${_t}${it?" (слой)":""}`,texture:_t,materialPaths:[it?"materials/"+_t+".vtf":Et?.tex?"materials/"+Et.tex+".vtf":"materials/"+wt+".vmt"],vertexCount:bt,pos:Tt,nor:Float32Array.from(Z.nor),uv:Float32Array.from(Z.uv),...it?{vertexAlpha:Float32Array.from(Z.alpha)}:{},...pt?{screenFx:{...pt,...jt?{plane:jt}:{}},color:pt.fallback.color}:{},...Bt?{uv2:Float32Array.from(Z.uv2.map((ct,ie)=>ct<0?1.5/(ie&1?Me:tt):ct))}:{},bone:new Float32Array(0),weight:new Float32Array(0),indices:bt>65535?Uint32Array.from(Z.idx):Uint16Array.from(Z.idx),skinned:!1,...Et?.alphaTest||Et?.translucent?{alphaTest:!0}:{}}),Y0+=Z.idx.length/3}c.push(`Source BSP v${r}: ${t.name} — граней ${Rs.toLocaleString("ru")}${ss?`, дисплейсментов ${ss}`:""}, материалов ${Ml.length}, треугольников ${Math.round(Y0).toLocaleString("ru")} (служебных граней пропущено ${bn})`),es.size&&c.push(`3D-небо: ${es.size} граней в масштабе ×${Qe} от sky_camera (меши «небо 3D · …»), треугольников ${Math.round(Z0).toLocaleString("ru")}`),El&&c.push(`Вода и стекло с преломлением: ${El} мешей (отражение и преломление по снимку экрана)`),kl&&c.push(`Смешивание текстур дисплейсментов (WorldVertexTransition): ${kl} слоёв`);const Vr=[...W.values()].filter(q=>!q.found&&!q.tool).map(q=>q.stem);Vr.length&&c.push(`Не найдено материалов: ${Vr.length} (${Vr.slice(0,4).join(", ")}${Vr.length>4?"…":""})`);const Eo=new vs,jr=new Map,Os=new Set,q0=new Map,T4=q=>{if(/\/shared\//.test(q)){let Z=q0.get(q);return Z||q0.set(q,Z=e.readPath(q)),Z}return e.readPath(q)};let Al=0;const $l=new Map,Q0=async(q,Z,ot=!1,ut)=>{const at=`${q}#${Z}${ot?"#sky":""}${ut!==void 0?"#"+ut:""}`;if(jr.has(at))return jr.get(at);let lt=null;try{const it=await zu(q,T4,Z,ut!==void 0);if(it&&it.skeleton&&ut!==void 0){const wt=za(it.motions,ut);wt&&(it.meshes=await Bu(it,wt,0),Al++,$l.set(wt.name,($l.get(wt.name)??0)+1))}if(it&&it.meshes.length){const wt=[];for(const Et of it.meshes){let bt=xn(Et.materialPaths?.[0]??Et.texture),Tt=null;for(const Bt of Et.materialPaths??[]){const _t=await Y(Bt);if(_t.found){Tt=_t,bt=_t.stem;break}}Tt?.tool||wt.push({...Et,texture:bt,materialPaths:void 0,...Tt?.alphaTest||Tt?.translucent?{alphaTest:!0}:{},...Tt?.fx?{screenFx:Tt.fx,color:Tt.fx.fallback.color}:{}})}wt.length&&(lt={pi:Eo.proto(at,wt,ot?"sky":"prop",Et=>`${ot?"небо 3D · ":""}реквизит · ${Et.texture.split("/").slice(-2).join("/")}`),parts:wt})}else Os.add(q)}catch{Os.add(q)}return jr.set(at,lt),lt};let J0=0,tm=0,Gr=0;const em=q=>q.map((Z,ot)=>ot<12?ot%4===3?Z:Z*Qe:ot<15?(Z-yn[ot-12])*Qe:Z),[Sl,U4]=a(35);if(U4>=4){const q=l(Sl);for(let Z=0;Z<q&&Sl+4+Z*16+16<=n.byteLength;Z++){const ot=Sl+4+Z*16,ut=String.fromCharCode(o[ot],o[ot+1],o[ot+2],o[ot+3]);s.getUint16(ot+6,!0);const at=l(ot+8),lt=l(ot+12);if(ut!=="prps"&&ut!=="sprp"||at<=0||at+lt>n.byteLength)continue;let it=at;const wt=l(it);if(it+=4,wt<0||wt>65535)continue;const Et=[];for(let pt=0;pt<wt;pt++){let jt="";for(let ct=0;ct<128&&o[it+pt*128+ct];ct++)jt+=String.fromCharCode(o[it+pt*128+ct]);Et.push(jt.toLowerCase().replace(/\\/g,"/"))}it+=wt*128;const bt=l(it),Tt=it+4;it+=4+bt*2;const Bt=l(it);if(it+=4,Bt<=0)continue;const _t=Math.floor((at+lt-it)/Bt);if(!(_t<32))for(let pt=0;pt<Bt;pt++){const jt=it+pt*_t,ct=s.getUint16(jt+24,!0),ie=_t>=36?l(jt+32):0,Is=Et[ct];if(!Is)continue;const $o=s.getUint16(jt+26,!0),os=Te>=0&&$o<bt&&Be(s.getUint16(Tt+$o*2,!0))===Te,Je=await Q0(Is,ie>0&&ie<64?ie:0,os);if(!Je)continue;const Ds=Ba([i(jt+12),i(jt+16),i(jt+20)],[i(jt),i(jt+4),i(jt+8)]);Eo.place(Je.pi,Je.parts,os?em(Ds):Ds,Is.split("/").pop(),os?"небо 3D":"prop_static"),os&&Gr++,J0++}}}for(const q of G){const Z=(q.get("model")??"").toLowerCase().replace(/\\/g,"/");if(!/\.mdl$/.test(Z))continue;const ot=q.get("classname")??"";if(/^(env_sprite|info_|light|npc_maker)/.test(ot))continue;const ut=+(q.get("skin")??0)||0,at=Qs(q.get("origin")),lt=Te>=0&&Be(Ve(at))===Te,it=/^models\/character\//.test(Z)||/^npc_/.test(ot),wt=await Q0(Z,ut,lt,it?q.get("defaultanim")||q.get("demo_sequence")||q.get("sequence")||"":void 0);if(!wt)continue;const Et=+(q.get("modelscale")??1)||1,bt=Ba(Qs(q.get("angles")),at,Et);Eo.place(wt.pi,wt.parts,lt?em(bt):bt,q.get("targetname")||Z.split("/").pop(),lt?"небо 3D":ot||"entity"),lt&&Gr++,tm++}const nm=Eo.build(),sm=nm.filter(q=>q.screenFx).length;sm&&c.push(`Реквизит с преломлением (Refract): ${sm} мешей`),c.push(`Реквизит: статического ${J0}, из сущностей ${tm}${Gr?` (в 3D-небе ${Gr})`:""}, разных моделей ${[...jr.values()].filter(Boolean).length}`),Al&&c.push(`Персонажи: ${Al} моделей поставлены в позу первого кадра (${[...$l].sort((q,Z)=>Z[1]-q[1]).slice(0,5).map(([q,Z])=>`${q} ×${Z}`).join(", ")})`),Os.size&&c.push(`Не найдено моделей: ${Os.size} (${[...Os].slice(0,4).join(", ")}${Os.size>4?"…":""})`);const je=[...nm,...Ml],Ao=G.find(q=>q.get("classname")==="worldspawn")?.get("skyname")?.toLowerCase();let om=!1;if(Ao&&je.length){const q=dt(je),Z=[0,1,2].map(lt=>(q.min[lt]+q.max[lt])/2),ot=1.5*Math.hypot(q.max[0]-Z[0],q.max[1]-Z[1],q.max[2]-Z[2])||1e3,at=[["ft",[1,0,0],[0,-1,0]],["rt",[0,-1,0],[-1,0,0]],["bk",[-1,0,0],[0,1,0]],["lf",[0,1,0],[1,0,0]]].map(([lt,it,wt])=>[lt,(Et,bt)=>[0,1,2].map(Tt=>Z[Tt]+ot*(it[Tt]+wt[Tt]*(2*Et-1)+[0,0,1][Tt]*(1-2*bt))),it]);at.push(["up",(lt,it)=>[Z[0]+ot*(2*it-1),Z[1]+ot*(1-2*lt),Z[2]+ot],[0,0,1]]),at.push(["dn",(lt,it)=>[Z[0]+ot*(1-2*it),Z[1]+ot*(1-2*lt),Z[2]-ot],[0,0,-1]]);for(const[lt,it,wt]of at){if(!await e.readPath(`materials/skybox/${Ao}${lt}.vmt`))continue;const Et=[0,0,1,0,1,1,0,1],bt=new Float32Array(12);for(let Bt=0;Bt<4;Bt++)bt.set(it(Et[Bt*2],Et[Bt*2+1]),Bt*3);const Tt=new Float32Array(12).map((Bt,_t)=>-wt[_t%3]);je.push({name:`небо 2D · ${Ao}${lt}`,texture:`skybox/${Ao}${lt}`,materialPaths:[`materials/skybox/${Ao}${lt}.vmt`],vertexCount:4,pos:bt,nor:Tt,uv:Float32Array.from(Et),...de?{uv2:Float32Array.from([0,0,0,0,0,0,0,0].map((Bt,_t)=>1.5/(_t&1?Me:tt)))}:{},bone:new Float32Array(0),weight:new Float32Array(0),indices:Uint16Array.from([0,2,1,0,3,2]),skinned:!1,defaultHidden:!0}),om=!0}}const Tl=je.filter(q=>!q.name.startsWith("небо 3D"));if(Tl.length&&Tl.length<je.length){const q=dt(Tl),Z=[0,1,2].map(at=>(q.min[at]+q.max[at])/2),ot=Math.hypot(q.max[0]-Z[0],q.max[1]-Z[1],q.max[2]-Z[2])||1;let ut=0;for(const at of je){if(!at.name.startsWith("небо 3D"))continue;let lt=0;for(let it=0;it<at.pos.length;it+=3)lt=Math.max(lt,Math.hypot(at.pos[it]-Z[0],at.pos[it+1]-Z[1],at.pos[it+2]-Z[2]));lt>2*ot&&(at.defaultHidden=!0,ut++)}ut&&c.push(`3D-небо: ${ut} мешей дальнего фона скрыты (включаются в списке мешей)`)}if(!je.length)throw new Error(`${t.name}: нет видимой геометрии`);de&&c.push(`Карта освещения: ${nt.length} граней, атлас ${tt}×${Me}${St.size?`; с рельефом (3 направления базиса) ${St.size}`:""}`);const Ul=G.find(q=>q.get("classname")==="worldspawn")?.get("skyname");return Ul&&c.push(om?`2D-небо: ${Ul} — куб «небо 2D · …» вокруг карты, по умолчанию скрыт (закрывает вид снаружи), включается в списке мешей`:`Небо: ${Ul} (текстуры не найдены)`),{meshes:je,instances:Eo.scene,skeleton:null,motions:[],bounds:dt(je.some(q=>!q.name.startsWith("небо"))?je.filter(q=>!q.name.startsWith("небо")):je),flipZ:!1,defaultZUp:!0,cameraYaw:0,info:c,...de?{lightmap:{width:tt,height:Me,data:de,rgb:!0,scale:2,...ze?{bump:ze}:{}}}:{}}}const x3=Object.freeze(Object.defineProperty({__proto__:null,angleMatrix:Ba,loadSourceBsp:v3,vmtScreenFx:Vu},Symbol.toStringTag,{value:"Module"}));function M3(t){const e=/^(.*)_(dir|\d{3})\.vpk$/i.exec(t);return e?[e[1].toLowerCase(),e[2].toLowerCase()]:null}async function k3(t,e){const n=new DataView(await t.slice(0,28).arrayBuffer());if(n.byteLength<12||n.getUint32(0,!0)!==1437209140)throw new Error(`${t.name}: не VPK`);const s=n.getUint32(4,!0),o=n.getUint32(8,!0);if(s!==1&&s!==2)throw new Error(`${t.name}: версия VPK ${s} не поддерживается`);const r=s===2?28:12,a=await ht(t,r,r+o),i=new DataView(a.buffer,a.byteOffset,a.byteLength);let l=0;const c=()=>{let u="";for(;l<a.length&&a[l];)u+=String.fromCharCode(a[l++]);return l++,u},f=[],h=u=>u===" "?"":u;for(;;){const u=c();if(!u)break;for(;;){const d=c();if(!d)break;for(;;){const p=c();if(!p)break;const m=i.getUint32(l,!0),g=i.getUint16(l+4,!0),w=i.getUint16(l+6,!0),y=i.getUint32(l+8,!0),M=i.getUint32(l+12,!0);l+=18;const x=h(d),v=gt((x?x+"/":"")+h(p)+(h(u)?"."+h(u):""));f.push({path:v,crc:m,preloadOff:l,preload:g,archive:w,offset:y,length:M}),l+=g}}}return Qt({name:t.name,kind:"vpk",version:s,entries:f,textureKeys:()=>[],async read(u){const d=a.subarray(u.preloadOff,u.preloadOff+u.preload);let p=new Uint8Array(0);if(u.length){const g=u.archive===32767,w=g?t:e.get(u.archive),y=g?r+o:0;if(!w)throw new Error(`нет части VPK №${u.archive} (выберите все файлы *_NNN.vpk вместе с *_dir.vpk)`);p=await ht(w,y+u.offset,y+u.offset+u.length)}if(!d.length)return p;const m=new Uint8Array(d.length+p.length);return m.set(d),m.set(p,d.length),m}})}const E3=Object.freeze(Object.defineProperty({__proto__:null,openVpk:k3,vpkPart:M3},Symbol.toStringTag,{value:"Module"})),A3=/^pack\d{3}\.vpk$/i;async function $3(t){if(t.size<9)throw new Error(`${t.name}: не VPK`);const e=new DataView(await t.slice(t.size-9).arrayBuffer()),n=e.getUint32(0,!0),s=e.getUint32(4,!0);if(s>t.size-9||n>1e6)throw new Error(`${t.name}: не VPK Troika`);const o=await ht(t,s,t.size-9),r=new DataView(o.buffer,o.byteOffset,o.byteLength),a=[];let i=0;for(let l=0;l<n;l++){if(i+4>o.length)throw new Error(`${t.name}: каталог VPK обрезан`);const c=r.getUint32(i,!0);if(i+=4,c>1024||i+c+8>o.length)throw new Error(`${t.name}: каталог VPK повреждён`);let f="";for(let d=0;d<c;d++){const p=o[i+d];p&&(f+=String.fromCharCode(p))}i+=c;const h=r.getUint32(i,!0),u=r.getUint32(i+4,!0);i+=8,!(h+u>s)&&a.push({path:gt(f).replace(/^\/+/,""),size:u,offset:h,length:u})}return Qt({name:t.name,kind:"vpk",version:0,entries:a,read:l=>ht(t,l.offset,l.offset+l.length)})}const S3=Object.freeze(Object.defineProperty({__proto__:null,TROIKA_VPK_RE:A3,openTroikaVpk:$3},Symbol.toStringTag,{value:"Module"})),ln={model:en("modl"),mesh:en("mesh"),skel:en("skel"),bone:en("bone"),matl:en("matl"),file:en("file"),anim:en("anim"),curve:en("curv")},T3={98:[Uint8Array,1,1],104:[Uint16Array,2,1],105:[Int32Array,4,1],108:[BigUint64Array,8,1],102:[Float32Array,4,1],100:[Float64Array,8,1],12918:[Float32Array,4,2],13174:[Float32Array,4,3],13430:[Float32Array,4,4]};function ju(t){const e=new DataView(t),n=new Uint8Array(t),s=new TextDecoder;if(t.byteLength<16||e.getUint32(0,!0)!==1953718627)throw new Error("Не Cast (нет сигнатуры «cast»)");const o=e.getUint32(8,!0);let r=16;const a=()=>{const c=e.getUint16(r,!0),f=e.getUint16(r+2,!0),h=e.getUint32(r+4,!0);r+=8;const u=s.decode(n.subarray(r,r+f));r+=f;let d;if(c===115){const p=[];for(let m=0;m<h;m++){let g=r;for(;g<n.length&&n[g];)g++;p.push(s.decode(n.subarray(r,g))),r=g+1}d=h===1?p[0]:p}else{const p=T3[c];if(!p)throw new Error("Cast: неизвестный тип свойства 0x"+c.toString(16));const m=h*p[2],g=m*p[1];d=new p[0](t.slice(r,r+g)),r+=g}return{name:u,value:d}},i=()=>{const c=e.getUint32(r,!0),f=e.getBigUint64(r+8,!0).toString(),h=e.getUint32(r+16,!0),u=e.getUint32(r+20,!0);r+=24;const d={};for(let m=0;m<h;m++){const g=a();d[g.name]=g.value}const p=[];for(let m=0;m<u;m++)p.push(i());return{id:c,hash:f,props:d,kids:p}},l=[];for(let c=0;c<o;c++)l.push(i());return l}const to=t=>t&&typeof t!="string"&&!Array.isArray(t)?t:null,Ke=t=>{const e=to(t);return e&&!(e instanceof BigUint64Array)?e:null},Va=(t,e,n)=>{const s=t[e];return s===void 0?n:typeof s=="string"?Number(s):Array.isArray(s)?n:s.length?Number(s[0]):n},cn=(t,e="")=>typeof t=="string"?t:Array.isArray(t)?t[0]??e:e,ja=(t,e=0)=>[t[e*4+3],t[e*4],t[e*4+1],t[e*4+2]];function U3(t){let e=null;for(const w of t){const y=w.kids.find(M=>M.id===ln.model);y&&(e=y)}if(!e)throw t.some(y=>y.kids.some(M=>M.id===ln.anim))?new To("Это файл анимации Cast (не модель)"):new Error("В Cast нет модели");const n=e.kids.find(w=>w.id===ln.skel),s=n?n.kids.filter(w=>w.id===ln.bone):[],o=s.length,r=new Array(o),a=new Array(o),i=new Array(o).fill(!1),l=s.map((w,y)=>{let M=Va(w.props,"p",-1);return(M<0||M>=o||M===y)&&(M=-1),{name:cn(w.props.n,`bone${y}`),parent:M,props:w.props}}),c=()=>Xe([1,0,0,0],[0,0,0]),f=w=>{if(i[w])return;i[w]=!0;const y=l[w],M=y.props;y.parent>=0&&f(y.parent);const x=y.parent>=0?r[y.parent]:null,v=Ke(M.s),b=v&&v.length>=3?[v[0],v[1],v[2]]:[1,1,1],k=Ke(M.lp),E=Ke(M.lr),A=Ke(M.wp),S=Ke(M.wr);k&&E&&(a[w]=Xe(ja(E),[k[0],k[1],k[2]],b)),A&&S&&(r[w]=Xe(ja(S),[A[0],A[1],A[2]])),r[w]||(r[w]=x?Lt(x,a[w]??c()):a[w]??c()),a[w]||(a[w]=x?Lt(Vt(x),r[w]):r[w])};for(let w=0;w<o;w++)f(w);const h=l.map((w,y)=>({name:w.name,parent:w.parent,local:a[y],offset:Vt(r[y])})),u=new Map;for(const w of e.kids.filter(y=>y.id===ln.matl)){const y=w.kids.filter(x=>x.id===ln.file).map(x=>({slot:cn(x.props.n),path:cn(x.props.p)})),M=y.find(x=>/albedo|diffuse|basecolor|base_color|color/i.test(x.slot))??y[0];u.set(w.hash,{name:cn(w.props.n),tex:M?M.path:""})}const d=[];for(const w of e.kids.filter(y=>y.id===ln.mesh)){const y=w.props,M=Ke(y.vp),x=to(y.f);if(!M||!x)continue;const v=M.length/3,b=Float32Array.from(M),k=Array.from(x,Number),E=new Float32Array(v*2),A=Ke(y.u0),S=Ke(y.vn);if(A&&A.length>=v*2)for(let D=0;D<v*2;D++)E[D]=A[D];const $=S&&S.length>=v*3?Float32Array.from(Array.prototype.slice.call(S,0,v*3)):ge(b,k),T=new Float32Array(v*4),U=new Float32Array(v*4),_=Va(y,"mi",0),R=to(y.wb),L=Ke(y.wv);if(o&&R&&L&&_>0)for(let D=0;D<v;D++){const F=[];for(let I=0;I<_;I++){const B=L[D*_+I],z=Number(R[D*_+I]);B>0&&z<o&&F.push([z,B])}F.sort((I,B)=>B[1]-I[1]);const C=F.slice(0,4),P=C.reduce((I,B)=>I+B[1],0)||1;C.length?C.forEach(([I,B],z)=>{T[D*4+z]=I,U[D*4+z]=B/P}):U[D*4]=1}else for(let D=0;D<v;D++)U[D*4]=1;const O=to(y.m),N=O&&O.length?u.get(O[0].toString()):void 0;d.push({name:cn(y.n,`mesh ${d.length}`),texture:N?N.tex:"",shader:N?N.name:"",vertexCount:v,pos:b,nor:$,uv:E,bone:T,weight:U,indices:$e(v,k),skinned:!0})}if(!d.length)throw new Error("В Cast-модели нет мешей");let p=null,m;o?(p=new Kt(h),m=Jt(p,d)):m=dt(d);const g=new Map(l.map((w,y)=>[w.name.toLowerCase(),y]));return{meshes:d,skeleton:p,motions:[],bounds:m,flipZ:!1,defaultZUp:!0,cameraYaw:Math.PI/2,info:[`Cast: «${cn(e.props.n,"model")}», мешей ${d.length}, треугольников ${Ut(d)}, костей ${o}, материалов ${u.size}`,o?"Анимации: .cast с анимацией — кнопкой «+ анимация» или кликом в списке":"Статичная модель"],loadMotions:o?async w=>Gu(ju(await w.arrayBuffer()),g,h):void 0}}function Gu(t,e,n){const s=[];for(const o of t)for(const r of o.kids.filter(a=>a.id===ln.anim)){const a=Va(r.props,"fr",30)||30,i=new Map,l=new Map;let c=0;for(const f of r.kids.filter(h=>h.id===ln.curve)){const h=e.get(cn(f.props.nn).toLowerCase()),u=to(f.props.kb),d=Ke(f.props.kv);if(h===void 0||!u||!d)continue;const p=cn(f.props.kp),m=l.get(h)??{pos:new Map,rot:new Map};l.set(h,m);for(let g=0;g<u.length;g++){const w=Number(u[g]);if(c=Math.max(c,w),p==="rq")m.rot.set(w,ja(d,g));else if(p==="tx"||p==="ty"||p==="tz"){const y=m.pos.get(w)??[NaN,NaN,NaN];y["xyz".indexOf(p[1])]=d[g],m.pos.set(w,y)}}}for(const[f,h]of l){const u=n[f].local,d=[u[12],u[13],u[14]],p=[...h.pos].sort((g,w)=>g[0]-w[0]).map(([g,w])=>[g,w.map((y,M)=>Number.isNaN(y)?d[M]:y)]),m=[...h.rot].sort((g,w)=>g[0]-w[0]);i.set(f,{pos:p.length?p:null,rot:m.length?m:null,scl:null})}i.size&&s.push({kind:"node",name:cn(r.props.n,`anim ${s.length}`),tps:a,chan:i,duration:(c+1)/a,frames:c+1})}return s}const F3=Object.freeze(Object.defineProperty({__proto__:null,convertCast:U3,convertCastAnim:Gu,parseCast:ju},Symbol.toStringTag,{value:"Module"})),_3=[7940,8248,8488];function C3(t,e,n,s){const o=r=>t.getUint32(r,!0);for(let r=768;r+1024<=e;r+=4){if(o(r)!==0||o(r+512)!==0||o(r+256)===0||o(r+768)===0)continue;let a=0,i=0,l=0,c=!0;for(let f=0;f<64;f++){const h=o(r+f*4),u=o(r+256+f*4),d=o(r+512+f*4),p=o(r+768+f*4);if(i===n&&l===s)break;if(h!==i||d!==l||u===0&&p===0){c=!1;break}i+=u,l+=p,a=f+1}if(c&&a&&i===n&&l===s){const f=h=>Array.from({length:a},(u,d)=>o(h+d*4));return{o:r,n:a,first:f(r),num:f(r+256),tfirst:f(r+512),tnum:f(r+768)}}}return null}function Xu(t){const e=new DataView(t),n=new Uint8Array(t),s=E=>e.getUint32(E,!0),o=E=>e.getFloat32(E,!0);if(t.byteLength<32)throw new Error("FCE: файл слишком короткий");const r=Rc[s(0)];if(!r)throw new Error("Не FCE (неизвестная сигнатура 0x"+s(0).toString(16)+")");const a=s(8),i=s(12),l=s(20),c=s(24),f=s(28);if(!i||!a||i>2e5||a>4e5)throw new Error("FCE: неверные счётчики");let h=0,u=null;for(const E of _3){if(E+l+i*12>t.byteLength||E+f+a*56>t.byteLength||E+c+i*12>t.byteLength)continue;let A=!0;for(let $=0;$<Math.min(i,64);$++){const T=o(E+l+$*12);if(!Number.isFinite(T)||Math.abs(T)>1e3){A=!1;break}}if(!A)continue;const S=C3(e,E,i,a);if(S){h=E,u=S;break}}if(!u)throw new Error("FCE: не удалось найти таблицы частей (версия формата не распознана)");const d=u.n,p=u.o,m=u;let g=Array.from({length:d},(E,A)=>`part ${A}`);for(let E=32;E+64*d<=h;E++){if(n[E]!==58)continue;let A=!0;for(let S=0;S<d&&A;S++){n[E+64*S]!==58&&(A=!1);for(let $=1;$<8&&A;$++){const T=n[E+64*S+$];T!==0&&(T<32||T>126)&&(A=!1)}}if(A){g=Array.from({length:d},(S,$)=>{let T="";for(let U=0;U<64&&n[E+64*$+U];U++)T+=String.fromCharCode(n[E+64*$+U]);return T});break}}const w=new Float32Array(i*3),y=new Float32Array(i*3);for(let E=0;E<i*3;E++)w[E]=o(h+l+E*4),y[E]=o(h+c+E*4);const M=Array.from({length:d},(E,A)=>[o(p-768+A*12),o(p-768+A*12+4),o(p-768+A*12+8)]);let x=0,v=0;m.first.forEach((E,A)=>{const S=[0,0,0];for(let $=0;$<m.num[A];$++)for(let T=0;T<3;T++)S[T]+=w[(E+$)*3+T]/m.num[A];x+=Math.hypot(...S),v+=Math.hypot(...M[A])});const b=x<v*.5+.01*d,k=[];for(let E=0;E<d;E++)for(let A=m.tfirst[E];A<m.tfirst[E]+m.tnum[E];A++){const S=h+f+A*56,$=[s(S+4),s(S+8),s(S+12)].map(T=>m.first[E]+T);$.some(T=>T>=i)||k.push({part:E,page:s(S),v:$,u:[o(S+32),o(S+36),o(S+40)],w:[o(S+44),o(S+48),o(S+52)]})}return{ver:r,hs:h,names:g,pos:w,nor:y,partPos:M,local:b,tris:k,nVerts:i}}function L3(t){const e=Xu(t),n=/^:[mltp]/i,s=m=>!n.test(e.names[m]);let o=m=>s(m);e.names.some((m,g)=>s(g))||(o=()=>!0);const r=new Map;for(const m of e.tris){if(!o(m.part))continue;const g=m.part*1e3+(m.page&1023);r.has(g)||r.set(g,{part:m.part,page:m.page,tris:[]}),r.get(g).tris.push(m)}const a=[];for(const m of r.values()){const g=new Map,w=[],y=[],M=[],x=[],v=e.local?e.partPos[m.part]:[0,0,0];for(const k of m.tris)for(let E=0;E<3;E++){const A=k.v[E],S=Number.isFinite(k.u[E])?k.u[E]:0,$=Number.isFinite(k.w[E])?k.w[E]:0,T=`${A}|${S}|${$}`;let U=g.get(T);if(U===void 0){U=g.size,g.set(T,U);for(let _=0;_<3;_++)w.push(e.pos[A*3+_]+v[_]),y.push(e.nor[A*3+_]);M.push(S,1-$)}x.push(U)}const b=g.size;a.push({name:`${e.names[m.part]} (tex ${m.page})`,texture:m.page===0?"car00":`car${String(m.page).padStart(2,"0")}`,shader:"",vertexCount:b,pos:Float32Array.from(w),nor:Float32Array.from(y),uv:Float32Array.from(M),bone:new Float32Array(b*4),weight:new Float32Array(b*4),indices:$e(b,x),skinned:!1})}if(!a.length)throw new Error("FCE: нет видимой геометрии");const{min:i,max:l}=Po();for(const m of a)for(let g=0;g<m.pos.length;g++){const w=g%3;m.pos[g]<i[w]&&(i[w]=m.pos[g]),m.pos[g]>l[w]&&(l[w]=m.pos[g])}const c=[0,1,2].map(m=>l[m]-i[m]),f=[0,1,2].sort((m,g)=>c[m]-c[g]),h=f[0],u=f[1],d=f[2];for(const m of a)for(const g of[m.pos,m.nor])for(let w=0;w<g.length;w+=3){const y=[g[w],g[w+1],g[w+2]];g[w]=y[u],g[w+1]=y[h],g[w+2]=y[d]}const p={min:[i[u],i[h],i[d]],max:[l[u],l[h],l[d]]};return{meshes:a,skeleton:null,motions:[],bounds:p,flipZ:!1,defaultZUp:!1,cameraYaw:0,info:[`NFS FCE${e.ver===3?"3 (NFS3)":e.ver===4?"4 (NFS4 HS)":"4M (MCO)"}: частей ${e.names.length}, показано мешей ${a.length}, треугольников ${Ut(a)}`,`Заголовок 0x${e.hs.toString(16)}, вершины ${e.local?"локальны (смещены на позицию части)":"глобальны"}; оси подобраны по габаритам. Показан LOD0 (части :M/:L/:T скрыты).`,"Текстура: car00.tga из того же VIV/папки."]}}const R3=Object.freeze(Object.defineProperty({__proto__:null,convertFce:L3,parseFce:Xu},Symbol.toStringTag,{value:"Module"}));async function O3(t){const e=new DataView(await t.slice(0,16).arrayBuffer());if(e.byteLength<16)throw new Error(`${t.name}: не BIGF/VIV`);const n=String.fromCharCode(...[0,1,2,3].map(f=>e.getUint8(f)));if(n!=="BIGF"&&n!=="BIG4")throw new Error(`${t.name}: не BIGF/VIV`);const s=e.getUint32(8,!1),o=e.getUint32(12,!1);if(!s||s>2e5)throw new Error(`${t.name}: странное число файлов (${s})`);const r=Math.min(o>16?o-16:s*64,16*1024*1024),a=await ht(t,16,16+r),i=new DataView(a.buffer,a.byteOffset,a.byteLength),l=[];let c=0;for(let f=0;f<s&&c+9<=a.length;f++){const h=i.getUint32(c,!1),u=i.getUint32(c+4,!1);c+=8;let d="";for(;c<a.length&&a[c];)d+=String.fromCharCode(a[c++]);c++,l.push({path:gt(d),off:h,size:u})}return Qt({name:t.name,kind:"bigf",version:0,entries:l,read:f=>ht(t,f.off,f.off+f.size)})}const I3=Object.freeze(Object.defineProperty({__proto__:null,openBigf:O3},Symbol.toStringTag,{value:"Module"}));function D3(t){let e=0;const n=()=>{const v=e>>3,b=v<t.length?t[v]>>7-(e&7)&1:0;return e++,b},s=v=>{let b=0;for(let k=0;k<v;k++)b=b*2+n();return b},o=()=>{if(n())return s(2);let v=1;for(;!n();)if(++v>30)throw new Error("HUFF: поток повреждён");const b=v+2;return s(b)+(1<<b)-4},r=s(16);let a;r&32768?(r&256&&s(32),a=s(32)):(r&256&&s(24),a=s(24));const i=s(8),l=[],c=[],f=[];let h=0,u=0,d=1;for(;;){h*=2,l[d]=h,f[d]=h-u;const v=o();if(u+=v,h+=v,c[d]=v,d>32)throw new Error("HUFF: таблица кодов повреждена");const b=v!==0&&(h<<16-d&65535)===0;if(d++,b)break}const p=d-1,m=new Uint8Array(256),g=[];let w=-1;for(let v=0;v<u;v++){let b=o()+1,k=0;for(;w=w+1&255,m[w]||b--,b!==0;)if(++k>1048576)throw new Error("HUFF: неверный список символов");m[w]=1,g.push(w)}const y=new Uint8Array(a);let M=0;const x=t.length*8+64;for(;M<a&&e<=x;){let v=1,b=n();for(;!(c[v]&&b>=l[v]&&b<l[v]+c[v]);)if(v++,b=b*2+n(),v>p)throw new Error("HUFF: неверный код");const k=g[b-f[v]];if(k!==i){y[M++]=k;continue}const E=o();if(E){const A=M?y[M-1]:0;for(let S=0;S<E&&M<a;S++)y[M++]=A;continue}if(n()===0){y[M++]=s(8);continue}break}if(r===13051||r===45819){let v=0;for(let b=0;b<a;b++)v=v+y[b]&255,y[b]=v}else if(r===13563||r===46331){let v=0,b=0;for(let k=0;k<a;k++)v=v+y[k]&255,b=b+v&255,y[k]=b}return y}function P3(t){if(t[0]!==74||t[1]!==68||t[2]!==76||t[3]!==90)throw new Error("JDLZ: нет сигнатуры");const e=(t[8]|t[9]<<8|t[10]<<16|t[11]<<24)>>>0,n=new Uint8Array(e);let s=1,o=1,r=16,a=0;for(;r<t.length&&a<e;){if(s===1&&(s=t[r++]|256),o===1&&(o=t[r++]|256),s&1){const i=t[r],l=t[r+1];let c,f;if(o&1?(c=(l|(i&240)<<4)+3,f=(i&15)+1):(f=(l|(i&224)<<3)+17,c=(i&31)+3),r+=2,f>a)throw new Error("JDLZ: неверное смещение");for(let h=0;h<c&&a<e;h++,a++)n[a]=n[a-f];o>>>=1}else n[a++]=t[r++];s>>>=1}return n}const Hu=2148745216,Yo=3006267392,N3=1712403336,Ga=1430524194;function Nt(t,e,n){const s=new DataView(t.buffer,t.byteOffset,t.byteLength),o=[];let r=e;for(;r+8<=n;){const a=s.getUint32(r,!0),i=s.getUint32(r+4,!0);if(r+8+i>n)break;o.push({id:a,off:r+8,size:i}),r+=8+i}return o}function fn(t,e,n,s,o=6,r=[]){for(const a of Nt(t,e,n))a.id===s?r.push(a):a.id&2147483648&&o>0&&a.size>=8&&fn(t,a.off,a.off+a.size,s,o-1,r);return r}function ve(t,e,n){for(;e+4<=n&&t[e]===17&&t[e+1]===17&&t[e+2]===17&&t[e+3]===17;)e+=4;return e}function eo(t,e,n,s){const o=new DataView(t.buffer,t.byteOffset,t.byteLength);if(e+24>t.length||o.getUint32(e,!0)!==Ga)return t.slice(e,e+s);const r=new Uint8Array(s),a=Math.min(t.length,e+n);let i=e;for(;i+24<=a&&o.getUint32(i,!0)===Ga;){const l=o.getUint32(i+4,!0),c=o.getUint32(i+8,!0),f=o.getUint32(i+12,!0);if(c<24||i+c>t.length)throw new Error("Black Box: блок сжатия выходит за файл");const h=t.subarray(i+24,i+c),u=String.fromCharCode(h[0],h[1],h[2],h[3]),d=u==="JDLZ"?P3(h):u==="HUFF"?D3(h.subarray(16)):h.subarray(0,l);r.set(d.subarray(0,Math.max(0,Math.min(d.length,s-f))),f),i+=c}return r}function no(t){const e=new DataView(t.buffer,t.byteOffset,t.byteLength);return t.length<16||e.getUint32(0,!0)!==N3?t:eo(t,16,t.length-16,e.getUint32(4,!0))}function z3(t){let e=4294967295;for(let n=0;n<t.length;n++)e=Math.imul(e,33)+t.charCodeAt(n)>>>0;return e}const so=t=>t.charCodeAt(0)|t.charCodeAt(1)<<8|t.charCodeAt(2)<<16|t.charCodeAt(3)<<24,un={DXT1:so("DXT1"),DXT3:so("DXT3"),DXT5:so("DXT5"),ATI2:so("ATI2"),ATI1:so("ATI1")},Xa=(t,e,n)=>{let s="";for(let o=0;o<n&&t[e+o];o++)s+=String.fromCharCode(t[e+o]);return s};function Ha(t,e){const n=new DataView(t.buffer,t.byteOffset,t.byteLength);return{hash:n.getUint32(e+12,!0),w:n.getUint32(e+32,!0),h:n.getUint32(e+36,!0),dataSize:n.getUint32(e+24,!0),dataOff:n.getUint32(e+80,!0),name:Xa(t,e+145,t[e+144])}}function Gn(t,e=new Map){const n=new DataView(t.buffer,t.byteOffset,t.byteLength);for(const s of fn(t,0,t.length,Yo,4)){const o=Nt(t,s.off,s.off+s.size),r=o.find(d=>d.id===3006332928),a=o.find(d=>d.id===3006398464);if(!r)continue;const i=Nt(t,r.off,r.off+r.size),l=i.find(d=>d.id===858849281),c=l?Xa(t,l.off+4,28):"",f=i.find(d=>d.id===858849283),h=i.find(d=>d.id===858849284),u=i.find(d=>d.id===858849285);if(f)for(let d=0;d+36<=f.size;d+=36){const p=f.off+d,m=n.getUint32(p,!0),g=n.getUint32(p+4,!0),w=n.getUint32(p+8,!0),y=n.getUint32(p+12,!0),M=n.getUint32(p+16,!0);if(!y||M>y||g+24>t.length||n.getUint32(g,!0)!==Ga)continue;let x=null;const v=()=>x??=eo(t,g,w,y),b=v(),k=Ha(b,y-M),E=new DataView(b.buffer,b.byteOffset).getUint32(y-M+192,!0);e.has(m)||e.set(m,{hash:m,name:k.name||m.toString(16),width:k.w,height:k.h,fourcc:E,pack:c,data:()=>v().subarray(0,y-M)})}else if(h&&a){const d=Nt(t,a.off,a.off+a.size).find(w=>w.id===858914818);if(!d)continue;const p=ve(t,d.off,d.off+d.size);let m=h.off,g=0;for(;m+145<=h.off+h.size;){const w=Ha(t,m),y=t[m+144],M=u&&u.off+g*32+16<=u.off+u.size?n.getUint32(u.off+g*32+12,!0):0,x=p+w.dataOff,v=w.dataSize;!e.has(w.hash)&&x+v<=t.length&&e.set(w.hash,{hash:w.hash,name:w.name,width:w.w,height:w.h,fourcc:M,pack:c,data:()=>t.subarray(x,x+v)}),m+=145+y,g++}}}return e}function B3(t){const e=new DataView(t.buffer,t.byteOffset,t.byteLength),n=[];for(const s of fn(t,0,t.length,Yo,4)){const o=Nt(t,s.off,s.off+s.size).find(c=>c.id===3006332928);if(!o)continue;const r=Nt(t,o.off,o.off+o.size),a=r.find(c=>c.id===858849281),i=a?Xa(t,a.off+4,28):"",l=r.find(c=>c.id===858849283);if(l)for(let c=0;c+36<=l.size;c+=36){const f=l.off+c;n.push({hash:e.getUint32(f,!0),off:e.getUint32(f+4,!0),csize:e.getUint32(f+8,!0),usize:e.getUint32(f+12,!0),infoSize:e.getUint32(f+16,!0),pack:i})}}return n}function V3(t,e){const n=eo(e,0,e.length,t.usize),s=Ha(n,t.usize-t.infoSize),o=new DataView(n.buffer,n.byteOffset).getUint32(t.usize-t.infoSize+192,!0);return{hash:t.hash,name:s.name||t.hash.toString(16),width:s.w,height:s.h,fourcc:o,pack:t.pack,data:()=>n.subarray(0,t.usize-t.infoSize)}}function Wu(t,e,n,s,o,r,a,i){const l=t[e],c=t[e+1],f=[l,c,0,0,0,0,0,0];if(l>c)for(let d=1;d<=6;d++)f[d+1]=((7-d)*l+d*c)/7|0;else{for(let d=1;d<=4;d++)f[d+1]=((5-d)*l+d*c)/5|0;f[6]=0,f[7]=255}let h=t[e+2]|t[e+3]<<8|t[e+4]<<16,u=t[e+5]|t[e+6]<<8|t[e+7]<<16;for(let d=0;d<16;d++){let p;d<8?(p=h&7,h>>=3):(p=u&7,u>>=3);const m=a*4+(d&3),g=i*4+(d>>2);m<o&&g<r&&(n[(g*o+m)*4+s]=f[p])}}function Zo(t){const e=t.data(),n=t.width,s=t.height,o=new DataView(e.buffer,e.byteOffset,e.byteLength),r=Math.max(1,n+3>>2),a=Math.max(1,s+3>>2);if(t.fourcc===un.DXT1||t.fourcc===un.DXT3||t.fourcc===un.DXT5){const l=r*a*(t.fourcc===un.DXT1?8:16);if(e.length<l)throw new Error(`${t.name}: данные DXT обрезаны`);return{width:n,height:s,rgba:_e(o,0,n,s,t.fourcc===un.DXT1?1:t.fourcc===un.DXT3?3:5),name:t.name}}const i=new Uint8Array(n*s*4);if(t.fourcc===un.ATI2||t.fourcc===un.ATI1){const l=t.fourcc===un.ATI2,c=l?16:8;if(e.length<r*a*c)throw new Error(`${t.name}: данные ATI обрезаны`);for(let f=0;f<a;f++)for(let h=0;h<r;h++){const u=(f*r+h)*c;Wu(e,u,i,0,n,s,h,f),l&&Wu(e,u+8,i,1,n,s,h,f)}for(let f=0;f<n*s;f++){if(l){const h=i[f*4]/127.5-1,u=i[f*4+1]/127.5-1;i[f*4+2]=Math.round((Math.sqrt(Math.max(0,1-h*h-u*u))*.5+.5)*255)}else i[f*4+1]=i[f*4+2]=i[f*4];i[f*4+3]=255}return{width:n,height:s,rgba:i,name:t.name}}if(t.fourcc===21||t.fourcc===0&&e.length>=n*s*4){for(let l=0;l<n*s;l++)i[l*4]=e[l*4+2],i[l*4+1]=e[l*4+1],i[l*4+2]=e[l*4],i[l*4+3]=e[l*4+3];return{width:n,height:s,rgba:i,name:t.name}}throw new Error(`${t.name}: формат текстуры 0x${t.fourcc.toString(16)} не поддерживается`)}const qo=[{id:"grey",label:"серебристый",rgb:[.62,.64,.68]},{id:"white",label:"белый",rgb:[.9,.9,.9]},{id:"black",label:"чёрный",rgb:[.06,.06,.07]},{id:"red",label:"красный",rgb:[.65,.04,.04]},{id:"orange",label:"оранжевый",rgb:[.85,.35,.02]},{id:"yellow",label:"жёлтый",rgb:[.9,.72,.05]},{id:"green",label:"зелёный",rgb:[.06,.4,.12]},{id:"blue",label:"синий",rgb:[.04,.16,.55]},{id:"lightblue",label:"голубой",rgb:[.25,.55,.85]},{id:"purple",label:"фиолетовый",rgb:[.35,.08,.5]}];function j3(t){const e=no(t),n=new DataView(e.buffer,e.byteOffset,e.byteLength),s=Nt(e,0,e.length),o=s.find(h=>h.id===2147732992);if(!o)return null;const r=Nt(e,o.off,o.off+o.size),a=r.find(h=>h.id===249345),i=r.find(h=>h.id===249346);if(!a||!i)return null;const l=n.getUint32(a.off+8,!0),c=Gn(e),f=[];for(let h=0;h<l&&(h+1)*208<=i.size+4;h++){const u=i.off+h*208,d=n.getUint32(u+64,!0),p=n.getUint32(u+204,!0),m=u+208+4<=i.off+i.size?n.getUint32(u+208,!0):128,g=c.get(p);if(!g)continue;const w=g.data();w.length<g.width*g.height||f.push({mask:w.subarray(0,g.width*g.height),width:g.width,height:g.height,color:[(d>>>16&255)/255,(d>>>8&255)/255,(d&255)/255],threshold:m||128})}return f.length?{hash:n.getUint32(a.off+16,!0),layers:f}:null}const G3={sides:[{u0:.09,v0:.2,u1:.27,v1:.78,flip:!1,rot:!0},{u0:.73,v0:.2,u1:.91,v1:.78,flip:!0,rot:!0}],hood:[{u0:.36,v0:.13,u1:.64,v1:.33,flip:!1,rot:!1}]};function X3(t,e,n,s="sides"){const o=new Uint8Array(t*t*4);for(let r=0;r<t*t;r++)o[r*4]=e[0]*255,o[r*4+1]=e[1]*255,o[r*4+2]=e[2]*255,o[r*4+3]=255;if(n)for(const r of G3[s]){const a=Math.floor(r.u0*t),i=Math.ceil(r.u1*t),l=Math.floor(r.v0*t),c=Math.ceil(r.v1*t);for(const f of n.layers){const h=i-a,u=c-l,d=r.rot?u:h,p=r.rot?h:u,m=Math.min(d/f.width,p/f.height),g=f.width*m,w=f.height*m,y=(d-g)/2,M=(p-w)/2;for(let x=l;x<c;x++)for(let v=a;v<i;v++){let b,k;r.rot?(b=x-l,k=v-a):(b=v-a,k=x-l),r.flip&&(b=d-1-b,k=p-1-k);const E=(b-y)/m,A=(k-M)/m;if(E<0||A<0||E>=f.width-1||A>=f.height-1)continue;const S=E|0,$=A|0,T=E-S,U=A-$,_=f.mask,R=f.width,L=(_[$*R+S]*(1-T)+_[$*R+S+1]*T)*(1-U)+(_[($+1)*R+S]*(1-T)+_[($+1)*R+S+1]*T)*U,O=Math.max(0,Math.min(1,(f.threshold-L)/12+.5));if(O<=0)continue;const N=(x*t+v)*4;for(let D=0;D<3;D++)o[N+D]=Math.round(o[N+D]*(1-O)+f.color[D]*255*O)}}}return{width:t,height:t,rgba:o,name:"кожа кузова"}}function H3(t){const e=t.rgba;if(!e)return!1;let n=765,s=0;for(let o=0;o<e.length;o+=388){const r=e[o]+e[o+1]+e[o+2];n=Math.min(n,r),s=Math.max(s,r)}return s-n<24}const Wa=new WeakMap;function Ku(t,e,n){const s=t.archives[0]??Wa;let o=Wa.get(s);return o||(o=new Map,Wa.set(s,o)),o.has(e)||o.set(e,n()),o.get(e)}function W3(t,e){return Ku(t,"vinyls:"+e,async()=>{const n=`${e}cars/vinyls/`,s=new Set;for(const a of t.archives)for(const i of a.entries)i.path.startsWith(n)&&i.path.endsWith(".vyl")&&s.add(parseInt(i.path.slice(n.length,-4),16));if(!s.size)return[];const o=new Map,r=await t.readPath(e+"global/commerce.bin");if(r){let a="";for(let i=0;i<=r.length;i++){const l=r[i]??0;if(l>=32&&l<127){a+=String.fromCharCode(l);continue}if(a.length>=3){const c=z3(a);s.has(c)&&!o.has(c)&&o.set(c,a)}a=""}}return[...s].map(a=>({id:a.toString(16).toUpperCase().padStart(8,"0"),label:o.get(a)??`0x${a.toString(16).padStart(8,"0")}`})).sort((a,i)=>a.label.localeCompare(i.label))})}async function K3(t,e,n){const s=await t.readPath(`${e}cars/vinyls/${n.toLowerCase()}.vyl`);return s?j3(s):null}function Yu(t){const e=new DataView(t.buffer,t.byteOffset,t.byteLength),n=[],s=Nt(t,0,t.length).find(a=>a.id===Hu);if(!s)return n;const o=(a,i,l)=>{for(const c of Nt(t,a,i)){if(c.id===1261572)return c;if(c.id&2147483648&&l>0){const f=o(c.off,c.off+c.size,l-1);if(f)return f}}return null},r=o(s.off,s.off+s.size,2);if(!r)return n;for(let a=0;a+36<=r.size;a+=36){const i=r.off+a,l=e.getUint32(i+4,!0),c=e.getUint32(i+8,!0),f=e.getUint32(i+12,!0);if(!f||l>=t.length)continue;let h;try{h=eo(t,l,c,f)}catch{continue}const u=Nt(h,0,h.length).find(m=>m.id===2148745232),d=u&&Nt(h,u.off,u.off+u.size).find(m=>m.id===1261585);if(!d)continue;let p="";for(let m=ve(h,d.off,d.off+d.size)+160;m<d.off+d.size&&h[m];m++)p+=String.fromCharCode(h[m]);n.push({name:p,data:h})}return n}function Y3(t,e){return Ku(t,"wheels:"+e,async()=>{const n=new Set;for(const o of t.archives)for(const r of o.entries){const a=/^(.*\/)?(wheels\d*)\/geometry\.bin$/.exec(r.path);a&&r.path.startsWith(e)&&n.add(a[2])}const s=[];for(const o of[...n].sort()){const r=await t.readPath(`${e}${o}/geometry.bin`);if(r)for(const{name:a}of Yu(r)){const i=/^(.+)_STYLE(\d+)_(\d+)_\d+_A$/.exec(a);i&&s.push({id:`${o}/${a}`,label:`${i[1].replace(/_/g," ")} ${+i[2]} — ${i[3]}″`,lib:o,name:a})}}return s.sort((o,r)=>o.label.localeCompare(r.label,void 0,{numeric:!0}))})}async function Z3(t,e,n){const[s,o]=n.split("/"),r=await t.readPath(`${e}${s}/geometry.bin`);if(!r)return null;const a=Yu(r).find(c=>c.name===o);if(!a)return null;const i=new Map,l=await t.readPath(`${e}${s}/textures.bin`);if(l)try{Gn(no(l),i)}catch{}return{data:a.data,name:o,tex:i}}const Zu=116,q3=[32,36,44,28,40,48];function qu(t,e){const n=new DataView(t.buffer,t.byteOffset,t.byteLength),s=Nt(t,0,t.length).find(l=>l.id===Hu);if(!s)throw new Error("Не геометрия Black Box (нет чанка 0x80134000)");const o=fn(t,s.off,s.off+s.size,1261572,2)[0],r=[],a=[];if(!o){const l=fn(t,s.off,s.off+s.size,2148745232,1);if(!l.length)throw new Error("GEOMETRY.bin: нет ни таблицы объектов 0x134004, ни объектов 0x80134010");for(const c of l){const f=t.subarray(c.off-8,c.off+c.size),h=Qo(f);if(!(e&&!e(h)))try{r.push(oo(f,0,h))}catch(u){a.push(`${h}: ${u.message}`)}}return{parts:r,total:l.length,skipped:a}}const i=Math.floor(o.size/36);for(let l=0;l<i;l++){const c=o.off+l*36,f=n.getUint32(c,!0),h=n.getUint32(c+4,!0),u=n.getUint32(c+8,!0),d=n.getUint32(c+12,!0);if(!d||h>=t.length)continue;let p;try{p=eo(t,h,u,d)}catch{a.push(f.toString(16));continue}const m=Qo(p);if(!(e&&!e(m)))try{r.push(oo(p,f,m))}catch(g){a.push(`${m}: ${g.message}`)}}return{parts:r,total:i,skipped:a}}function Qo(t){const e=Nt(t,0,t.length).find(o=>o.id===2148745232),n=e&&Nt(t,e.off,e.off+e.size).find(o=>o.id===1261585);if(!n)return"";let s="";for(let o=ve(t,n.off,n.off+n.size)+160;o<n.off+n.size&&t[o];o++)s+=String.fromCharCode(t[o]);return s}function oo(t,e,n){const s=new DataView(t.buffer,t.byteOffset,t.byteLength),o=Nt(t,0,t.length).find(L=>L.id===2148745232);if(!o)throw new Error("нет объекта 0x80134010");const r=Nt(t,o.off,o.off+o.size),a=r.find(L=>L.id===2148745472),i={hash:e,name:n,subs:[]},l=r.find(L=>L.id===1261594);if(l){i.markers=[];for(let L=ve(t,l.off,l.off+l.size);L+80<=l.off+l.size;L+=80){const O=new Float32Array(16);for(let N=0;N<16;N++)O[N]=s.getFloat32(L+16+N*4,!0);i.markers.push({hash:s.getUint32(L,!0),m:O})}}if(!a)return i;const c=Nt(t,a.off,a.off+a.size),f=c.find(L=>L.id===1264386),h=c.find(L=>L.id===1264387),u=c.filter(L=>L.id===1264385);if(!f||!h||!u.length)return i;const d=ve(t,f.off,f.off+f.size),p=Math.floor((f.off+f.size-d)/Zu),m=ve(t,h.off,h.off+h.size),g=Math.floor((h.off+h.size-m)/2),w=r.find(L=>L.id===1261586),y=[];if(w)for(let L=w.off;L+8<=w.off+w.size;L+=8)y.push(s.getUint32(L,!0));const M=Array.from({length:p},(L,O)=>{const N=d+O*Zu,D=s.getUint32(N+4,!0),F=t[N+40],C=t[N+41];return{key:D,normal:C!==F&&C<y.length?y[C]:void 0,texture:F<y.length?y[F]:D,format:s.getUint32(N+8,!0),verts:s.getUint32(N+64,!0),count:s.getUint32(N+68,!0),start:s.getUint32(N+76,!0)}}),x=u.map(L=>ve(t,L.off,L.off+L.size)),v=u.map((L,O)=>L.off+L.size-x[O]),b=[],k=[];{let L=0,O=0;for(const N of M){if(L>=u.length){b.length=0;break}b.push(L),O+=N.verts;const D=q3.find(F=>O*F===v[L]);D&&(k[L]=D,L++,O=0)}(L!==u.length||O)&&(b.length=0)}const E=[];for(const L of M)E.includes(L.format)||E.push(L.format);const A=L=>b.length?b[L]:E.length===u.length?E.indexOf(M[L].format):0,S=u.map((L,O)=>{const N=M.reduce((C,P,I)=>C+(A(I)===O?P.verts:0),0),D=b.length?k[O]:N?Math.floor(v[O]/N):32,F=D>=24&&D<=64?D:32;return{start:x[O],stride:F,count:Math.floor(v[O]/F)}}),$=c.find(L=>L.id===1264646),T=c.filter(L=>L.id===1264645);let U=null;const _=[];if($&&T.length===u.length){const L=ve(t,$.off,$.off+$.size);U=new Float32Array(Math.floor(($.off+$.size-L)/4));for(let O=0;O<U.length;O++)U[O]=s.getFloat32(L+O*4,!0);for(const O of T){const N=[];for(let D=ve(t,O.off,O.off+O.size);D+16<=O.off+O.size;D+=16)N.push([s.getUint32(D,!0),s.getUint32(D+4,!0),s.getUint32(D+8,!0)]);_.push(N)}}const R=(L,O)=>{if(!U)return-1;for(const[N,D,F]of _[L]??[])if(O>=N&&O<=D)return(F+1)*16<=U.length?F*16:-1;return-1};for(const[L,O]of M.entries()){const N=S[A(L)];if(!N||!O.count||O.start+O.count>g)continue;const D=new Map,F=new Uint32Array(O.count),C=[],P=[],I=[],B=[];let z=!1,j=!1;for(let G=0;G<O.count;G++){const H=s.getUint16(m+(O.start+G)*2,!0);if(H>=N.count){z=!0;break}let K=D.get(H);if(K===void 0){K=D.size,D.set(H,K);const Q=N.start+H*N.stride;if(N.stride!==32){C.push(s.getFloat32(Q,!0),s.getFloat32(Q+4,!0),s.getFloat32(Q+8,!0)),I.push(s.getFloat32(Q+20,!0),s.getFloat32(Q+24,!0));{const W=Q+(N.stride>=36?28:12);B.push(t[W],t[W+1],t[W+2],t[W+3])}if(N.stride>=36){let W=s.getInt16(Q+12,!0)/32767,Y=s.getInt16(Q+14,!0)/32767,tt=s.getInt16(Q+16,!0)/32767;const nt=Math.hypot(W,Y,tt);nt>.5&&nt<1.5?(W/=nt,Y/=nt,tt/=nt):j=!0,P.push(W,Y,tt)}else j=!0,P.push(0,0,1);const V=R(A(L),H);if(V>=0){const W=U,Y=C.length-3,tt=C[Y],nt=C[Y+1],et=C[Y+2],rt=P[Y],mt=P[Y+1],xt=P[Y+2];for(let Pt=0;Pt<3;Pt++)C[Y+Pt]=tt*W[V+Pt]+nt*W[V+4+Pt]+et*W[V+8+Pt]+W[V+12+Pt],P[Y+Pt]=rt*W[V+Pt]+mt*W[V+4+Pt]+xt*W[V+8+Pt];const St=Math.hypot(P[Y],P[Y+1],P[Y+2])||1;P[Y]=P[Y]/St,P[Y+1]=P[Y+1]/St,P[Y+2]=P[Y+2]/St}}else{C.push(s.getInt16(Q,!0)/4096,s.getInt16(Q+2,!0)/4096,s.getInt16(Q+4,!0)/4096),I.push(s.getInt16(Q+8,!0)/4096,s.getInt16(Q+10,!0)/4096);let V=s.getInt16(Q+16,!0)/32767,W=s.getInt16(Q+18,!0)/32767,Y=s.getInt16(Q+20,!0)/32767;const tt=Math.hypot(V,W,Y)||1;V/=tt,W/=tt,Y/=tt,P.push(V,W,Y)}}F[G]=K}if(z)continue;const X=Float32Array.from(C),J={texture:O.texture,key:O.key,normal:O.normal,pos:X,nor:j?ge(X,F):Float32Array.from(P),uv:Float32Array.from(I),indices:F};B.length===C.length/3*4&&(J.shade=Uint8Array.from(B)),i.subs.push(J)}return i}function Qu(t){const e=t.toUpperCase();return!(/_(STYLE\d+|KITW\d+|KIT(?!00_)\d+)_/.test(e)||/DAMAGE[1-9]/.test(e)||/DRIVER_FEMALE|WINDOW_CARSKINUV|_TIRE|_BRAKE_|_BRAKE$|_WHEEL|_RIM/.test(e))}const Ka={3788055326:{label:"окраска кузова (создаётся игрой)",color:[.62,.64,.68]},907533564:{label:"надпись на стекле (создаётся игрой)",color:[.92,.92,.9]}},Ya=3788055326,Ju=2846507359,th=3355834336,Q3=2855649663;function eh(t,e,n){const a=[t[4],t[5],t[6]],i=[t[0],t[1],t[2]],l=[t[8],t[9],t[10]],c=[t[12]+l[0]*.003,t[13]+l[1]*.003,t[14]+l[2]*.003],f=(u,d)=>[0,1,2].map(p=>c[p]+a[p]*u*.15+i[p]*d*.075),h=Float32Array.from([...f(1,1),...f(-1,1),...f(-1,-1),...f(1,-1)]);return{name:n,texture:e.name??"plate",vertexCount:4,pos:h,nor:Float32Array.from([...l,...l,...l,...l]),uv:Float32Array.from([0,0,1,0,1,1,0,1]),bone:new Float32Array(0),weight:new Float32Array(0),indices:new Uint16Array([0,1,2,0,2,3,0,2,1,0,3,2]),skinned:!1,image:e}}function J3(t,e){const n=e.rgba,s=new Uint8Array(n.length);for(let o=0;o<n.length;o+=4){const r=n[o+3]/255;for(let a=0;a<3;a++)s[o+a]=Math.round(t[a]*255*(1-r)+n[o+a]*r);s[o+3]=255}return{width:e.width,height:e.height,rgba:s,name:e.name}}async function tw(t,e){const n=new Uint8Array(await t.arrayBuffer()),s=e.options??{},o=(qo.find(C=>C.id===s.paint)??qo[0]).rgb,r=C=>/_([A-E])$/i.exec(C)?.[1]?.toUpperCase()??"A",{parts:a,total:i,skipped:l}=qu(n),c=new Map,f=[],h=(C,P)=>{if(!C)return;const I=c.size;try{Gn(no(C instanceof Uint8Array?C:new Uint8Array(C)),c)}catch{}c.size>I&&f.push(`${P} (${c.size-I})`)};h(await e.sibling("TEXTURES.BIN"),"TEXTURES.BIN машины");const u=(t.path??"").replace(/[^/]*$/,""),d=/(^|\/)cars\/[^/]+\/$/i.test(u)?u.replace(/[^/]+\/$/,""):"cars/",p=d.replace(/cars\/$/i,"");h(await e.readPath(d+"textures.bin"),"CARS/TEXTURES.BIN");for(const[C,P]of[["global/globalb.lzc","GLOBAL/GlobalB.lzc"],["global/ingamea.lzc","GLOBAL/InGameA.lzc"]]){const I=c.size;for(const[B,z]of await nw(e,p+C))c.has(B)||c.set(B,z);c.size>I&&f.push(`${P} (${c.size-I})`)}let m=null;try{const C=await e.readPath(p+"global/licenseplates.bin"),P=C&&B3(C).find(B=>B.hash===Ju),I=P&&await e.readPath(`${p}global/licenseplates/${Ju.toString(16).toUpperCase().padStart(8,"0")}.stp`.toLowerCase());P&&I&&(m=V3(P,I),f.push("GLOBAL/LicensePlates (номер)"))}catch{}const g=new Map,w=new Set,y=C=>{if(g.has(C))return g.get(C);const P=c.get(C);let I=null;if(P)try{I=Zo(P)}catch(B){w.add(B.message)}return g.set(C,I),I},M=new Map;for(const C of c.values())M.set(C.name.toUpperCase(),C.hash);const x=(t.path??"").match(/(?:^|\/)cars\/([^/]+)\//i)?.[1]?.toUpperCase()??[...c.values()].find(C=>/^CARTEMPLATETEXTURES_/i.test(C.pack))?.pack.replace(/^CARTEMPLATETEXTURES_/i,"").toUpperCase()??"",v=C=>{const P=C.toUpperCase(),I=/_(KIT\w*?\d+)_/.exec(P)?.[1]??"KIT00",B=/HEADLIGHT_GLASS/.test(P)?"HEADLIGHT_GLASS":/BRAKELIGHT_GLASS/.test(P)?"BRAKELIGHT_GLASS":/HEADLIGHT/.test(P)?"HEADLIGHT":/BRAKELIGHT/.test(P)?"BRAKELIGHT":/WINDOW/.test(P)?"WINDOW":"";if(B==="WINDOW")return M.get("WINDOW_FRONT");if(B)for(const z of new Set([I,"KIT00"]))for(const j of["OFF","ON"]){const X=M.get(`${x}_${z}_${B}_${j}`);if(X!==void 0)return X}},b=new Set(a.map(C=>C.name.toUpperCase())),k=C=>Qu(C)&&!(/_DAMAGE0_/i.test(C)&&b.has(C.toUpperCase().replace("_DAMAGE0_","_"))),E=[],A=new Set,S=new Set;for(const C of a){const P=B=>{const z=B.key??B.texture;return c.has(z)||Ka[z]?z:v(C.name)??(c.has(B.texture)?B.texture:z)},I=new Map;for(const B of C.subs){const z=P(B),j=Ka[z]&&z===Ya&&B.texture!==z&&c.has(B.texture)?B.texture:0,X=`${z}:${j}`;I.has(X)||I.set(X,{h:z,over:j,list:[]}),I.get(X).list.push(B)}for(const{h:B,over:z,list:j}of I.values()){const X=j.reduce((xt,St)=>xt+St.pos.length/3,0),J=j.reduce((xt,St)=>xt+St.indices.length,0),G=new Float32Array(X*3),H=new Float32Array(X*3),K=new Float32Array(X*2),Q=X>65535?new Uint32Array(J):new Uint16Array(J);let V=0,W=0;for(const xt of j){G.set(xt.pos,V*3),H.set(xt.nor,V*3),K.set(xt.uv,V*2);for(let St=0;St<xt.indices.length;St++)Q[W+St]=xt.indices[St]+V;V+=xt.pos.length/3,W+=xt.indices.length}const Y=c.get(B),tt=y(B),nt=Ka[B],et=Y?.name??nt?.label??`0x${B.toString(16).padStart(8,"0")}`;Y||(nt?S:A).add(et);const rt={name:`${C.name} · ${et}`,texture:et,vertexCount:X,pos:G,nor:H,uv:K,bone:new Float32Array(X*4),weight:new Float32Array(X*4).map((xt,St)=>St%4===0?1:0),indices:Q,skinned:!1},mt=z?y(z):null;tt?rt.image=tt:nt&&mt?(rt.image=J3(B===Ya?o:nt.color,mt),rt.name=`${C.name} · ${et} + ${c.get(z).name}`):nt?rt.color=B===Ya?[...o]:nt.color:rt.color=[.6,.6,.6],tt&&Y&&/GRILL|MESH|BADG|DECAL|LOGO|EMBLEM|TIRE|RIM|WHEEL|ROTOR|CALIPER/i.test(Y.name)&&(rt.alphaTest=!0),(r(C.name)!=="A"||!k(C.name)||!tt&&!nt&&/DOORLINE/i.test(C.name))&&(rt.defaultHidden=!0),E.push(rt)}}if(!E.length)throw new Error("GEOMETRY.bin: не найдено ни одного меша");const $=C=>r(C.name.split(" · ")[0])==="A",T=!E.some(C=>!C.defaultHidden);if(T){for(let C=E.length-1;C>=0;C--)$(E[C])||E.splice(C,1);for(const C of E)delete C.defaultHidden;sw(E)}let U,_="";if(!T&&s.wheel&&s.wheel!=="none")try{const C=await Z3(e,d,s.wheel);if(C){_=C.name;for(const[P,I]of C.tex)c.has(P)||c.set(P,I);U=ew(oo(C.data,0,C.name),P=>{const I=c.get(P);let B=null;if(I)try{B=Zo(I)}catch{}return{img:B,name:I?.name??P.toString(16)}})}}catch{}const R=T?null:nh(E,U);let L="";if(!T){const C=E.filter(P=>/_SKIN1$/i.test(P.texture)&&P.image&&H3(P.image));if(C.length){let P=null;if(s.vinyl&&s.vinyl!=="none"){try{P=await K3(e,p,s.vinyl)}catch{P=null}L=P?"":"винил не прочитан"}const I=X3(1024,o,P,s.place||"sides");for(const B of C)B.image=I}}let O=0;if(!T&&m){let C=null;try{C=Zo(m)}catch{}const P=new Set;for(const I of a)if(!(r(I.name)!=="A"||!k(I.name)||!I.markers))for(const B of I.markers)(B.hash===th||B.hash===Q3)&&!P.has(B.hash)&&C&&(P.add(B.hash),O++,E.push(eh(B.m,C,`номер ${B.hash===th?"задний":"передний"} · ${m.name}`)))}const N=E.filter(C=>!C.defaultHidden),D=[`Need for Speed: World — ${T?"библиотека деталей":"машина"}: объектов ${i}, деталей LOD A ${a.filter(C=>r(C.name)==="A").length} (LOD B–E — ${a.filter(C=>r(C.name)!=="A").length}, скрыты), мешей ${E.length} (видно ${N.length}), вершин ${le(N)}, треугольников ${Ut(N)}`,"По умолчанию показан кузов BASE и стоковый обвес KIT00; остальные обвесы (KIT/KITW), капоты STYLE и повреждения — в списке мешей.",O?`Номерной знак (${O}) — фон «мирового» номера LICENSEPLATE_WORLD на маркере; надпись игрок выбирает сам.`:"",R?`Колёса и тормозные диски расставлены по аркам кузова (оси x ${R.front.toFixed(2)} / ${R.rear.toFixed(2)} м, колея ${(R.half*2).toFixed(2)} м): в файлах игры их позиций нет — игра берёт их из настроек машины. Диски (WHEELS*) выбираются тюнингом и не ставятся.`:"Колёса и тормоза — в списке мешей (в начале координат).",f.length?`Текстуры: ${f.join(", ")}`:"Текстуры не найдены: откройте «Папку игры» или выберите TEXTURES.BIN вместе с GEOMETRY.bin"];S.size&&D.push(`Без файла (создаются игрой): ${[...S].join("; ")} — показаны цветом`),A.size&&D.push(`Текстуры не найдены: ${[...A].slice(0,12).join(", ")}${A.size>12?"…":""}`),w.size&&D.push(`Не декодированы: ${[...w].slice(0,5).join("; ")}`),l.length&&D.push(`Пропущены объекты: ${l.slice(0,5).join("; ")}`),_&&D.push(`Колёса: ${_} (библиотека дисков игры)`),L&&D.push(L);const F=[];if(!T){if(F.push({id:"paint",label:"Окраска",values:qo.map(C=>({id:C.id,label:C.label})),value:s.paint??qo[0].id}),E.some(C=>/_SKIN1$/i.test(C.texture))){const C=await W3(e,p).catch(()=>[]);C.length&&(F.push({id:"vinyl",label:"Винил",values:[{id:"none",label:"нет"},...C],value:s.vinyl??"none"}),F.push({id:"place",label:"Место винила",values:[{id:"sides",label:"бока"},{id:"hood",label:"капот"}],value:s.place??"sides"}))}if(R){const C=await Y3(e,d).catch(()=>[]);C.length&&F.push({id:"wheel",label:"Колёса",values:[{id:"none",label:"шины машины (без дисков)"},...C.map(P=>({id:P.id,label:P.label}))],value:s.wheel??"none"})}}return{meshes:E,skeleton:null,motions:[],bounds:dt(N.length?N:E),flipZ:!1,defaultZUp:!0,cameraYaw:0,info:D.filter(Boolean),options:F}}function nh(t,e){const n=F=>F.name.split(" · ")[0].toUpperCase(),s=F=>t.filter(C=>new RegExp(`_KIT00_${F}_TIRE_A$|_${F}_TIRE_A$`).test(n(C))),o=F=>t.filter(C=>new RegExp(`_KIT00_${F}_BRAKE_A$|_${F}_BRAKE_A$`).test(n(C))),r=s("FRONT"),a=s("REAR").length?s("REAR"):r;if(!r.length)return null;const i=dt(r),l=(i.max[2]-i.min[2])/2,c=i.max[1]-i.min[1];if(!(l>.1&&l<1))return null;const f=t.filter(F=>!F.defaultHidden&&/_BASE_A$|_KIT00_(DAMAGE0_)?(BODY|FRONT_BUMPER|REAR_BUMPER|SKIRT\w*|SIDE\w*|LEFT_DOOR|RIGHT_DOOR)_A$/.test(n(F)));if(!f.length)return null;let h=1/0,u=0;for(const F of f)for(let C=2;C<F.pos.length;C+=3)h=Math.min(h,F.pos[C]);for(const F of f)for(let C=0;C<F.pos.length;C+=3)F.pos[C+2]<h+l*2&&(u=Math.max(u,Math.abs(F.pos[C+1])));const d=.05,p=new Map,m=new Map;for(const F of f)for(let C=0;C<F.pos.length;C+=3){const P=Math.abs(F.pos[C+1]),I=Math.round(F.pos[C]/d);P>u*.75&&p.set(I,Math.min(p.get(I)??1/0,F.pos[C+2])),P>u*.4&&m.set(I,Math.min(m.get(I)??1/0,F.pos[C+2]))}const g=[...p.keys()],w=g.map(F=>p.get(F)).sort((F,C)=>F-C),y=w[Math.floor(w.length*.2)]??0,M=Math.min(...g),x=Math.max(...g),v=[],b=new Set(g),k=[...g].sort((F,C)=>F-C);for(let F=0;F+1<k.length;F++){const C=k[F],P=k[F+1],I=P-C-1;for(let B=C+1;B<P;B++)p.set(B,m.get(B)??(I<=3?Math.min(p.get(C),p.get(P)):y+l*1.5))}for(let F=M;F<=x;F++)v.push(F);const E=[];for(const F of[.6,.8,.9,1]){let C=null;for(const P of v){const I=p.get(P);I-y<l*F||(C&&P-C.b<=2?(C.b=P,C.top=Math.max(C.top,I)):(C={a:P,b:P,top:I,score:0},E.push(C)),b.has(P)&&(C.score+=Math.min(I-y,l*2)))}}const A=x-M,S=F=>{const C=(F.b-F.a+1)*d;return C>l*.8&&C<l*4},$=F=>E.filter(C=>S(C)&&F((C.a+C.b)/2)).sort((C,P)=>P.score-C.score)[0],T=$(F=>F>M+A*.68)??$(F=>F>M+A*.6),U=$(F=>F<M+A*.32)??$(F=>F<M+A*.4);if(!T||!U)return null;const _=F=>({x:(F.a+F.b)/2*d,z:F.top-l-.01}),R=_(T),L=_(U),O=u-c/2;let N=1;if(e?.length){const F=dt(e),C=(F.max[2]-F.min[2])/2;C>.05&&(N=l/C)}const D=(F,C,P,I,B,z=1,j=!0)=>{const X=j?-I:I,J=j?I*(O+c/2):I*(O-c/2);for(const G of F){const H=new Float32Array(G.pos.length),K=new Float32Array(G.nor.length);for(let V=0;V<G.pos.length;V+=3)H[V]=X*G.pos[V]*z+C,H[V+1]=X*G.pos[V+1]*z+J,H[V+2]=G.pos[V+2]*z+P,K[V]=X*G.nor[V],K[V+1]=X*G.nor[V+1],K[V+2]=G.nor[V+2];const Q={...G,name:`${B} · ${G.name}`,pos:H,nor:K};delete Q.defaultHidden,t.push(Q)}};for(const[F,C,P]of[["FRONT",R.x,R.z],["REAR",L.x,L.z]]){const I=F==="FRONT"?r:a,B=o(F);for(const z of[1,-1]){const j=`колесо ${F==="FRONT"?"переднее":"заднее"} ${z>0?"левое":"правое"}`;e?.length?D(e,C,P,z,j,N):D(I,C,P,z,j),D(B,C,P,z,j,1,!1)}}return{front:R.x,rear:L.x,half:O}}function ew(t,e){const n=new Map;for(const o of t.subs)n.has(o.texture)||n.set(o.texture,[]),n.get(o.texture).push(o);const s=[];for(const[o,r]of n){const a=r.reduce((w,y)=>w+y.pos.length/3,0),i=r.reduce((w,y)=>w+y.indices.length,0),l=new Float32Array(a*3),c=new Float32Array(a*3),f=new Float32Array(a*2),h=new Uint32Array(i);let u=0,d=0;for(const w of r){l.set(w.pos,u*3),c.set(w.nor,u*3),f.set(w.uv,u*2);for(let y=0;y<w.indices.length;y++)h[d+y]=w.indices[y]+u;u+=w.pos.length/3,d+=w.indices.length}const{img:p,name:m}=e(o),g={name:`${t.name} · ${m}`,texture:m,vertexCount:a,pos:l,nor:c,uv:f,bone:new Float32Array(0),weight:new Float32Array(0),indices:h,skinned:!1};p?(g.image=p,/TIRE/i.test(m)&&(g.alphaTest=!0)):g.color=[.5,.5,.5],s.push(g)}return s}const sh=new Map;async function nw(t,e){const n=t.archives[0]??null,s=sh.get(e);if(s&&s.key===n)return s.tex;const o=new Map;try{const r=await t.readPath(e);r&&Gn(no(r),o)}catch{}return sh.set(e,{key:n,tex:o}),o}function sw(t){const e=new Map;for(const r of t){const a=r.name.split(" · ")[0];e.has(a)||e.set(a,[]),e.get(a).push(r)}if(e.size<2)return;let n=0;for(const r of e.values()){const a=dt(r);n=Math.max(n,a.max[0]-a.min[0],a.max[1]-a.min[1])}const s=n*1.25||1,o=Math.ceil(Math.sqrt(e.size));[...e.values()].forEach((r,a)=>{const i=a%o*s,l=-Math.floor(a/o)*s;for(const c of r)for(let f=0;f<c.pos.length;f+=3)c.pos[f]+=i,c.pos[f+1]+=l})}const ow=Object.freeze(Object.defineProperty({__proto__:null,loadNfsCar:tw,nfsPartVisible:Qu,objectName:Qo,parseNfsGeometry:qu,parseObject:oo,placeWheels:nh,plateQuad:eh},Symbol.toStringTag,{value:"Module"})),oh=72,rh=96,rw=(t,e,n)=>{let s="";for(let o=0;o<n&&t[e+o];o++)s+=String.fromCharCode(t[e+o]);return s};function aw(t){const e=Nt(t,0,t.length).find(o=>o.id===2148745232),n=e&&Nt(t,e.off,e.off+e.size).find(o=>o.id===1261585);if(!n)return 0;const s=ve(t,n.off,n.off+n.size);return new DataView(t.buffer,t.byteOffset,t.byteLength).getUint32(s+16,!0)}function Jo(t,e=!0){const n=new DataView(t.buffer,t.byteOffset,t.byteLength),s=new Map,o=[],r=Nt(t,0,t.length);for(const a of r)if(a.id===2148745216&&e)for(const i of fn(t,a.off,a.off+a.size,2148745232,1)){const l=t.subarray(i.off-8,i.off+i.size),c=aw(l);c&&!s.has(c)&&s.set(c,{hash:c,name:Qo(l),data:l})}else if(a.id===2147696896){const i=Nt(t,a.off,a.off+a.size),l=i.find(u=>u.id===213250),c=i.find(u=>u.id===213251);if(!l||!c)continue;const f=[],h=[];for(let u=ve(t,l.off,l.off+l.size);u+oh<=l.off+l.size;u+=oh)f.push({name:rw(t,u,24),solids:[n.getUint32(u+24,!0),n.getUint32(u+28,!0),n.getUint32(u+32,!0)],radius:n.getFloat32(u+56,!0)});for(let u=ve(t,c.off,c.off+c.size);u+rh<=c.off+c.size;u+=rh){const d=new Float32Array(12);for(let m=0;m<9;m++)d[m]=n.getFloat32(u+44+m*4,!0);for(let m=0;m<3;m++)d[9+m]=n.getFloat32(u+32+m*4,!0);const p=n.getUint32(u+84,!0);p>=f.length||!d.every(Number.isFinite)||h.push({info:p,m:d,min:[n.getFloat32(u,!0),n.getFloat32(u+4,!0),n.getFloat32(u+8,!0)],max:[n.getFloat32(u+16,!0),n.getFloat32(u+20,!0),n.getFloat32(u+24,!0)],flags:n.getUint32(u+12,!0)})}o.push({infos:f,insts:h})}return{objects:s,groups:o}}function iw(t){return async(e,n,s)=>{const o=t.blobOf?.(e);return o?new Uint8Array(await o.slice(n,Math.min(o.size,n+s)).arrayBuffer()):(await t.read(e)).subarray(n,n+s)}}async function ah(t,e,n,s){let r=await t(e,0,Math.min(n,32768)),a=0,i=0;const l=async(c,f)=>c>=a&&c+f<=a+r.length?r.subarray(c-a,c-a+f):(r=await t(e,c,Math.max(f,Math.min(32768,n-c))),a=c,r.subarray(0,f));for(let c=0;i+8<=n&&c<4096;c++){const f=await l(i,8),h=new DataView(f.buffer,f.byteOffset,8),u=h.getUint32(0,!0),d=h.getUint32(4,!0);if(i+8+d>n)break;if(u===2147696896)s.scen.add(e);else if(u===Yo||u===2148745216){const p=Math.min(d,16384),m=await l(i+8,p),g=new DataView(m.buffer,m.byteOffset,m.byteLength);if(u===Yo)for(const w of fn(m,0,m.length,858849282,2))for(let y=0;y+8<=w.size;y+=8){const M=g.getUint32(w.off+y,!0);s.tex.has(M)||s.tex.set(M,e)}else for(const w of fn(m,0,m.length,1261571,2))for(let y=ve(m,w.off,w.off+w.size);y+8<=w.off+w.size;y+=8){const M=g.getUint32(y,!0);M&&!s.obj.has(M)&&s.obj.set(M,e)}}i+=8+d}}const ih=new WeakMap;function lw(t,e,n){let s=ih.get(t);s||(s=new Map,ih.set(t,s));let o=s.get(n);return o||(o=(async()=>{const r={obj:new Map,tex:new Map,scen:new Set},a=iw(t);let i=0;const l=async()=>{for(;i<e.length;){const c=e[i++],f=c.size??t.blobOf?.(c)?.size??0;if(f)try{await ah(a,c,f,r)}catch{}}};return await Promise.all(Array.from({length:8},l)),r})(),s.set(n,o)),o}const Za=1<<20;class qa{list=[];open=new Map;tris=0;bad=0;add(e,n,s,o,r=!0){const a=s.pos.length/3,i=`${e}:${n}`;for(let d=0;d<s.pos.length;d++){const p=s.pos[d];if(!(p>-1e6&&p<1e6)){this.bad++;return}}let l=this.open.get(i);if(!l||l.nv+a>Za||l.ni+s.indices.length>Za*3){l&&this.shrink(l);const d=Math.max(a,4096);l={tex:e,normal:n,pos:new Float32Array(d*3),nor:new Float32Array(d*3),uv:new Float32Array(d*2),shade:new Uint8Array(d*4),idx:new Uint32Array(Math.max(s.indices.length,8192)),nv:0,ni:0},this.open.set(i,l),this.list.push(l)}this.grow(l,a,s.indices.length);const c=s.pos,f=s.nor,h=l.nv*3;for(let d=0;d<a;d++){const p=c[d*3],m=c[d*3+1],g=c[d*3+2],w=f[d*3],y=f[d*3+1],M=f[d*3+2];if(o){l.pos[h+d*3]=p*o[0]+m*o[3]+g*o[6]+o[9],l.pos[h+d*3+1]=p*o[1]+m*o[4]+g*o[7]+o[10],l.pos[h+d*3+2]=p*o[2]+m*o[5]+g*o[8]+o[11];let x=w*o[0]+y*o[3]+M*o[6],v=w*o[1]+y*o[4]+M*o[7],b=w*o[2]+y*o[5]+M*o[8];const k=Math.hypot(x,v,b)||1;x/=k,v/=k,b/=k,l.nor[h+d*3]=x,l.nor[h+d*3+1]=v,l.nor[h+d*3+2]=b}else l.pos[h+d*3]=p,l.pos[h+d*3+1]=m,l.pos[h+d*3+2]=g,l.nor[h+d*3]=w,l.nor[h+d*3+1]=y,l.nor[h+d*3+2]=M}if(l.uv.set(s.uv,l.nv*2),s.shade&&r){const d=s.shade,p=l.shade,m=l.nv*4;for(let g=0;g<d.length;g++)p[m+g]=(g&3)===3?255:Math.round(45+d[g]*.65)}else l.shade.fill(128,l.nv*4,(l.nv+a)*4);const u=o?o[0]*(o[4]*o[8]-o[5]*o[7])-o[1]*(o[3]*o[8]-o[5]*o[6])+o[2]*(o[3]*o[7]-o[4]*o[6])<0:!1;for(let d=0;d<s.indices.length;d+=3){const p=s.indices[d]+l.nv,m=s.indices[d+1]+l.nv,g=s.indices[d+2]+l.nv;l.idx[l.ni+d]=p,l.idx[l.ni+d+1]=u?g:m,l.idx[l.ni+d+2]=u?m:g}l.nv+=a,l.ni+=s.indices.length,this.tris+=s.indices.length/3}grow(e,n,s){if((e.nv+n)*3>e.pos.length){const o=Math.min(Za,Math.max(e.nv+n,e.pos.length/3*2))*3;for(const i of["pos","nor"]){const l=new Float32Array(o);l.set(e[i].subarray(0,e.nv*3)),e[i]=l}const r=new Float32Array(o/3*2);r.set(e.uv.subarray(0,e.nv*2)),e.uv=r;const a=new Uint8Array(o/3*4);a.set(e.shade.subarray(0,e.nv*4)),e.shade=a}if(e.ni+s>e.idx.length){const o=new Uint32Array(Math.max(e.ni+s,e.idx.length*2));o.set(e.idx.subarray(0,e.ni)),e.idx=o}}shrink(e){e.pos=e.pos.slice(0,e.nv*3),e.nor=e.nor.slice(0,e.nv*3),e.uv=e.uv.slice(0,e.nv*2),e.shade=e.shade.slice(0,e.nv*4),e.idx=e.idx.slice(0,e.ni)}finish(){for(const e of this.open.values())this.shrink(e);return this.open.clear(),this.list}}const cw=/(^|\/)tracks\/(stream)?[a-z]\d[a-z]{2}(_[^/]*)?\.bun$/i;function Qa(t,e){if(Math.max(t.width,t.height)<=e)return t;const n=t.fourcc===827611204||t.fourcc===826889281?8:t.fourcc===861165636||t.fourcc===894720068||t.fourcc===843666497?16:0;let s=t.width,o=t.height,r=0;const a=(l,c)=>n?Math.max(1,l+3>>2)*Math.max(1,c+3>>2)*n:l*c*4,i=t.data();for(;Math.max(s,o)>e&&r+a(s,o)+a(Math.max(1,s>>1),Math.max(1,o>>1))<=i.length;)r+=a(s,o),s=Math.max(1,s>>1),o=Math.max(1,o>>1);return r?{...t,width:s,height:o,data:()=>i.subarray(r,r+a(s,o))}:t}async function fw(t,e,n={}){const s=Date.now(),o=t.path??t.name.toLowerCase(),r=o.replace(/[^/]*$/,""),a=/([a-z]\d[a-z]{2})(?:_[^/]*)?\.bun$/i.exec(o)?.[1]?.toLowerCase()??"l5ra",i=t.archive??e.archives.find(G=>G.lookup(o)),l=[],c=i?i.entries.filter(G=>G.path.startsWith(r)&&G.path.slice(r.length).startsWith(`stream${a}_`)&&G.path.endsWith(".bun")&&!G.path.slice(r.length).includes("/")):[],f=G=>/_(\d+)\.bun$/.exec(G.path)?.[1],h=!/^stream/i.test(t.name)&&/\.bun$/i.test(t.name);if(h&&!i)throw new Error("Город NFS World собирается из файлов STREAM рядом с L5RA.BUN: откройте «Папку игры»");const u=n.maxTex??(h?128:512);let d;const p=i&&c.length?await lw(i,c.filter(G=>h?!f(G)||+f(G)>=2e4:G.path!==o),r+a+(h?"":":all")):null;if(h){const G=c.filter(H=>f(H)&&+f(H)<2e4);d=[...G,...[...p.scen].filter(H=>!G.includes(H))].map(H=>({entry:H,label:H.path.slice(r.length)}))}else d=[{u:new Uint8Array(await t.arrayBuffer()),label:t.name}];const m=new Map,g=[],w=async G=>i.read(G),y=(G,H)=>{for(const[K,Q]of G.objects)m.has(K)||m.set(K,{src:H,tris:lh(Q.data)})};let M=0,x=0;for(const G of d){const H=G.u??await w(G.entry),K=Jo(H,!0);for(const Q of K.groups)for(const V of Q.insts){const W=Q.infos[V.info],Y=W.solids[0];if(!Y)continue;if(M++,/^shadow|shadowgeo/i.test(W.name)){x++;continue}const tt=/_CHOP/i.test(W.name)||V.m[9]===0&&V.m[10]===0&&V.m[11]===0,nt=Math.max(V.max[0]-V.min[0],V.max[1]-V.min[1],V.max[2]-V.min[2]);g.push({name:W.name,inst:V,solid:Y,chop:tt,size:nt,rank:tt?0:/^pan_/i.test(W.name)?1:2,ocean:/ocean/i.test(W.name),lods:W.solids,baked:/_CHOP|^TRN_/i.test(W.name)})}y(K,h?{entry:G.entry}:{u:H})}const v=new Set;for(const G of g)if(!m.has(G.solid))for(const H of G.lods)H&&!m.has(H)&&v.add(H);if(p&&v.size){const G=new Set;for(const H of v){const K=p.obj.get(H);K&&G.add(K)}for(const H of G)try{const K=await w(H);y(Jo(K),h?{entry:H}:{u:K})}catch{}}v.clear();for(const G of g)if(!m.has(G.solid)){const H=G.lods.find(K=>K&&m.has(K));H?G.solid=H:v.add(G.solid)}let b=0;if(h)for(const G of g){if(G.rank!==2)continue;const H=[G.lods[2],G.lods[1]].find(K=>K&&K!==G.solid&&m.has(K));H&&(G.solid=H,b++)}g.sort((G,H)=>G.rank-H.rank||H.size-G.size);const k=n.budget??(h?7e6:2e7),E=new Map;let A=0,S=0,$=0,T=0;const U=[0,0,0];for(const G of g){const H=m.get(G.solid);if(!(!H||!H.tris)){if(T+=H.tris,U[G.rank]=U[G.rank]+H.tris,$+H.tris>k){A++;continue}$+=H.tris,S++,E.has(H.src)||E.set(H.src,[]),E.get(H.src).push(G)}}const _=new qa,R=new qa,L=new qa,O=new Set;for(const[G,H]of E){let K;try{K=Jo(G.u??await w(G.entry))}catch{continue}const Q=new Map;for(const V of H){let W=Q.get(V.solid);if(W===void 0){const et=K.objects.get(V.solid);try{W=et?oo(et.data,V.solid,et.name):null}catch{W=null}Q.set(V.solid,W)}if(!W)continue;const Y=V.inst.m,tt=Y[0]===1&&Y[4]===1&&Y[8]===1&&!Y[9]&&!Y[10]&&!Y[11],nt=V.rank===1?V.ocean?L:R:_;for(const et of W.subs){const rt=h?0:et.normal??0;nt.add(et.texture,rt,et,tt?null:Y,V.baked),O.add(et.texture),rt&&O.add(rt)}}}const N=_.finish(),D=new Set(R.finish()),F=new Set(L.finish());N.push(...F,...D);const C=new Map,P=r.replace(/tracks\/$/i,"");if(p){const G=new Set;for(const H of O){const K=p.tex.get(H);K&&G.add(K)}for(const H of G)try{const K=await w(H),Q=Gn(K);for(const[V,W]of Q)O.has(V)&&!C.has(V)&&C.set(V,Qa(W,u))}catch{}}if(!h&&d[0].u)for(const[G,H]of Gn(d[0].u))C.has(G)||C.set(G,Qa(H,u));for(const G of["global/globalb.lzc","global/ingamea.lzc"]){if([...O].every(H=>C.has(H)))break;try{const H=await e.readPath(P+G);if(H)for(const[K,Q]of Gn(no(H)))O.has(K)&&!C.has(K)&&C.set(K,Qa(Q,u))}catch{}}const I=new Map,B=new Set,z=G=>{if(I.has(G))return I.get(G);const H=C.get(G);let K=null;if(H)try{K=Zo(H)}catch(Q){B.add(Q.message)}return I.set(G,K),K},j=[],X=new Set;let J=0;for(const G of N){const H=C.get(G.tex),K=z(G.tex),Q=H?.name??`0x${G.tex.toString(16).padStart(8,"0")}`;H||X.add(G.tex);const V={name:Q,texture:Q,vertexCount:G.nv,pos:G.pos,nor:G.nor,uv:G.uv,indices:G.nv>65535?G.idx:Uint16Array.from(G.idx),bone:new Float32Array(0),weight:new Float32Array(0),skinned:!1};K?(V.image=K,hw(K)&&(V.alphaTest=!0)):V.color=[.55,.55,.55];const W=G.normal?C.get(G.normal):void 0;if(W&&/_N(OR(MAL)?)?$/i.test(W.name)){const Y=z(G.normal);Y&&(V.normalImage=Y,V.normalMap=W.name,J++)}G.shade.some((Y,tt)=>(tt&3)!==3&&Math.abs(Y-128)>1)&&(V.vertexShade=G.shade),D.has(G)&&(V.name=`панорама · ${Q}`,V.defaultHidden=!0),F.has(G)&&(V.name=`океан · ${Q}`),/^sfx_/i.test(Q)&&(V.defaultHidden=!0),j.push(V)}if(!j.length)throw new Error("NFS World: в файле трассы нет расставленных объектов");if(l.push(`Need for Speed: World — ${h?`город ${a.toUpperCase()} целиком (${d.length} секций)`:`секция ${t.name}`}: экземпляров ${M}, показано ${S}, мешей ${j.length}, вершин ${le(j)}, треугольников ${Ut(j)}`,`Текстуры: найдено ${C.size} из ${O.size} ${J?` (с картами нормалей; мешей с ними: ${J})`:""}, запечённый свет — цветом вершин${h?`, уменьшены до ${u} px`:""}${X.size?`; нет ${X.size} (показаны серым): ${[...X].slice(0,8).map(G=>G.toString(16).padStart(8,"0")).join(", ")}${X.size>8?"…":""}`:""}`),A&&l.push(`Бюджет ${(k/1e6).toFixed(0)} млн треугольников (вся расстановка — ${(T/1e6).toFixed(1)} млн: нарезка ${(U[0]/1e6).toFixed(1)}, панорама ${(U[1]/1e6).toFixed(1)}, экземпляры ${(U[2]/1e6).toFixed(1)}): пропущено мелких экземпляров ${A}`),b&&l.push(`Упрощённые LOD у экземпляров: ${b}`),x&&l.push(`Скрыта теневая геометрия (SHADOW_*): ${x}`),_.bad+R.bad+L.bad&&l.push(`Пропущены подмеши с испорченными вершинами: ${_.bad+R.bad+L.bad}`),D.size&&l.push("Дальняя панорама (горы и острова на горизонте, PAN_*) скрыта — включается в списке мешей"),v.size){const G=[...new Set(g.filter(H=>v.has(H.solid)).map(H=>H.name))];l.push(`Объекты не найдены: ${v.size} (${G.slice(0,6).join(", ")}${G.length>6?"…":""})`)}return B.size&&l.push(`Не декодированы: ${[...B].slice(0,4).join("; ")}`),!p&&!h&&l.push("Общие объекты и текстуры лежат в соседних файлах STREAM…_0x*.BUN: откройте «Папку игры», чтобы их подхватить"),l.push(`Время загрузки: ${((Date.now()-s)/1e3).toFixed(1)} с`),{meshes:j,skeleton:null,motions:[],bounds:uw(j.some(G=>!G.defaultHidden&&!G.name.startsWith("океан"))?j.filter(G=>!G.defaultHidden&&!G.name.startsWith("океан")):j),flipZ:!1,defaultZUp:!0,cameraYaw:0,info:l}}function lh(t){const e=new DataView(t.buffer,t.byteOffset,t.byteLength);let n=0;for(const s of fn(t,0,t.length,1264386,3))for(let o=ve(t,s.off,s.off+s.size);o+ch<=s.off+s.size;o+=ch)n+=e.getUint32(o+68,!0)/3;return Math.floor(n)}const ch=116;function uw(t){const e=dt(t),n=t.reduce((l,c)=>l+c.vertexCount,0);if(n<5e3)return e;const s=Math.max(1,Math.floor(n/2e5)),o=[[],[],[]];let r=0;for(const l of t)for(let c=0;c<l.vertexCount;c++,r++)if(r%s===0)for(let f=0;f<3;f++)o[f].push(l.pos[c*3+f]);const a=[0,0,0],i=[0,0,0];for(let l=0;l<3;l++){const c=o[l].sort((u,d)=>u-d),f=c[Math.floor(c.length*.002)],h=c[Math.ceil(c.length*.998)-1];a[l]=Math.max(e.min[l],f),i[l]=Math.min(e.max[l],h)}return{...e,min:a,max:i}}function hw(t){const e=t.rgba;if(!e)return!1;let n=0;for(let s=3;s<e.length;s+=16)e[s]<128&&n++;return n>e.length/16*.02}const dw=Object.freeze(Object.defineProperty({__proto__:null,NFS_TRACK_RE:cw,indexTrackFile:ah,loadNfsTrack:fw,objectTris:lh,scanTrack:Jo},Symbol.toStringTag,{value:"Module"})),fh="CryTek",hn={MESH:3435921408,BONEANIM:3435921411,BONENAMES:3435921413,NODE:3435921419,MTL:3435921420,CTRL:3435921421,TIMING:3435921422,BONEINIT:3435921426},xe=.01,uh=t=>t.length>=20&&String.fromCharCode(...t.subarray(0,6))===fh;function hh(t){const e=t instanceof Uint8Array?t:new Uint8Array(t),n=new DataView(e.buffer,e.byteOffset,e.byteLength);if(!uh(e))throw new Error("Не файл CryEngine 1 (нет сигнатуры CryTek)");const s=n.getUint32(8,!0),o=n.getUint32(12,!0),r=n.getUint32(16,!0);if(o!==1860)throw new Error(`CryTek: версия 0x${o.toString(16)} не поддерживается (нужна 0x744 — Far Cry)`);if(r+4>e.length)throw new Error("CryTek: таблица чанков за концом файла");const a=n.getUint32(r,!0);if(a>1e5||r+4+a*16>e.length)throw new Error("CryTek: неверная таблица чанков");const i=Array.from({length:a},(c,f)=>({type:n.getUint32(r+4+f*16,!0),ver:n.getUint32(r+8+f*16,!0),off:n.getUint32(r+12+f*16,!0),id:n.getUint32(r+16+f*16,!0),size:0})),l=[...i.map(c=>c.off),r].sort((c,f)=>c-f);for(const c of i)c.size=(l.find(f=>f>c.off)??r)-c.off;return{fileType:s,chunks:i,byId:new Map(i.map(c=>[c.id,c])),dv:n,u8:e}}const tr=(t,e,n)=>{let s=e;for(;s<e+n&&s<t.length&&t[s];)s++;return new TextDecoder("latin1").decode(t.subarray(e,s))};function dh(t,e){const n=tr(t.u8,e.off+16,64),s=/\(([^)]*)\)/.exec(n),o=e.ver===1861,r=o?112:176,a=o?108:236,i=o?64:128,l=h=>e.size>=r+(h+1)*a?tr(t.u8,e.off+r+h*a,i).replace(/^\xff+/,""):"",c=!o&&e.size>2552?e.size-2552>>2:0,f=[];for(let h=0;h<c;h++)f.push(t.dv.getInt32(e.off+2552+h*4,!0));return{name:n.replace(/\(.*$/,""),shader:s?.[1]??"",diffuse:c?"":l(1),bump:c?"":l(4),children:f}}function ph(t,e){const n=t.dv,s=e.off,o=a=>n.getInt32(s+a,!0),r=a=>n.getFloat32(s+a,!0);return{id:e.id,name:tr(t.u8,s+16,64),object:o(80),parent:o(84),mtl:o(92),tm:Array.from({length:16},(a,i)=>r(100+i*4)),pos:[r(164),r(168),r(172)],rot:[r(176),r(180),r(184),r(188)],scl:[r(192),r(196),r(200)],ctrl:[o(204),o(208),o(212)]}}function mh(t,e){const n=t.dv,s=e.off,o=e.off+e.size,r=t.u8[s+16],a=n.getInt32(s+20,!0),i=n.getInt32(s+24,!0),l=n.getInt32(s+28,!0);if(a<0||l<0||i<0||s+36+a*24+l*20>o)throw new Error("CryTek: меш обрезан");let c=s+36;const f=new Float32Array(a*3),h=new Float32Array(a*3);for(let w=0;w<a;w++,c+=24)for(let y=0;y<3;y++)f[w*3+y]=n.getFloat32(c+y*4,!0),h[w*3+y]=n.getFloat32(c+12+y*4,!0);const u=new Int32Array(l*3),d=new Int32Array(l);for(let w=0;w<l;w++,c+=20)u[w*3]=n.getInt32(c,!0),u[w*3+1]=n.getInt32(c+4,!0),u[w*3+2]=n.getInt32(c+8,!0),d[w]=n.getInt32(c+12,!0);let p=null,m=null;if(i>0&&c+i*8+l*12<=o){p=new Float32Array(i*2);for(let w=0;w<i*2;w++,c+=4)p[w]=n.getFloat32(c,!0);m=new Int32Array(l*3);for(let w=0;w<l*3;w++,c+=4)m[w]=n.getInt32(c,!0)}let g=null;if(r&&c+a*4<=o){const w=[];let y=c,M=!0;for(let x=0;x<a&&M;x++){const v=y+4<=o?n.getInt32(y,!0):-1;if(y+=4,v<1||v>64||y+v*20>o){M=!1;break}const b=[];for(let k=0;k<v;k++,y+=20)b.push([n.getInt32(y,!0),[n.getFloat32(y+4,!0),n.getFloat32(y+8,!0),n.getFloat32(y+12,!0)],n.getFloat32(y+16,!0)]);w.push(b)}M&&(g=w)}return{id:e.id,nv:a,pos:f,nor:h,faces:u,mat:d,uvs:p,tfaces:m,links:g}}function gh(t){const e=t.chunks.find(l=>l.type===hn.BONEANIM),n=t.chunks.find(l=>l.type===hn.BONENAMES),s=t.chunks.find(l=>l.type===hn.BONEINIT);if(!e||!n)return null;const o=t.dv,r=o.getUint32(e.off+16,!0);if(!r||r>1024||e.off+20+r*152>e.off+e.size)return null;const a=[];{let l=n.off+4;const c=n.off+n.size;for(let f=0;f<o.getUint32(n.off,!0)&&l<c;f++){const h=tr(t.u8,l,c-l);a.push(h),l+=h.length+1}}const i=[];for(let l=0;l<r;l++){const c=e.off+20+l*152,f=o.getInt32(c,!0);i[f]={name:a[f]??`bone${f}`,parent:o.getInt32(c+4,!0),ctrl:o.getUint32(c+12,!0),bind:[1,0,0,0,1,0,0,0,1,0,0,0]}}if(s&&o.getUint32(s.off+4,!0)===r)for(let l=0;l<r;l++)i[l].bind=Array.from({length:12},(c,f)=>o.getFloat32(s.off+8+l*48+f*4,!0));return i}function wh(t,e=xe){const n=new Float64Array(16);for(let s=0;s<3;s++)for(let o=0;o<3;o++)n[o*4+s]=t[s*3+o];return n[12]=t[9]*e,n[13]=t[10]*e,n[14]=t[11]*e,n[15]=1,n}function yh(t){const e=t[0],n=t[4],s=t[8],o=t[1],r=t[5],a=t[9],i=t[2],l=t[6],c=t[10],f=r*c-a*l,h=-(o*c-a*i),u=o*l-r*i,d=e*f+n*h+s*u||1,p=[f,-(n*c-s*l),n*a-s*r,h,e*c-s*i,-(e*a-s*o),u,-(e*l-n*i),e*r-n*o].map(g=>g/d),m=new Float64Array(16);for(let g=0;g<3;g++)for(let w=0;w<3;w++)m[w*4+g]=p[g*3+w];for(let g=0;g<3;g++)m[12+g]=-(m[g]*t[12]+m[4+g]*t[13]+m[8+g]*t[14]);return m[15]=1,m}function pw(t){return wh([t[0],t[1],t[2],t[4],t[5],t[6],t[8],t[9],t[10],t[12],t[13],t[14]])}function Ja(t){const e=t.chunks.find(s=>s.type===hn.TIMING);if(!e)return{secsPerTick:.00020833333333333335,ticksPerFrame:160,start:0,end:0};const n=t.dv;return{secsPerTick:n.getFloat32(e.off+16,!0)||.00020833333333333335,ticksPerFrame:n.getInt32(e.off+20,!0)||160,start:n.getInt32(e.off+56,!0),end:n.getInt32(e.off+60,!0)}}const mw=t=>{const e=Math.hypot(t[0],t[1],t[2]);if(e<1e-12)return[1,0,0,0];const n=Math.sin(e)/e;return[Math.cos(e),t[0]*n,t[1]*n,t[2]*n]};function gw(t,e,n){const s=Ja(t),o=new Map(e.map((l,c)=>[l.ctrl,c])),r=new Map;let a=0;for(const l of t.chunks){if(l.type!==hn.CTRL||l.ver!==2087)continue;const c=t.dv,f=c.getUint32(l.off,!0),h=c.getUint32(l.off+4,!0),u=o.get(h);if(u===void 0||l.off+8+f*28>l.off+l.size)continue;const d=[],p=[];for(let m=0;m<f;m++){const g=l.off+8+m*28,w=c.getInt32(g,!0)*s.secsPerTick;d.push([w,[c.getFloat32(g+4,!0)*xe,c.getFloat32(g+8,!0)*xe,c.getFloat32(g+12,!0)*xe]]);const y=mw([c.getFloat32(g+16,!0),c.getFloat32(g+20,!0),c.getFloat32(g+24,!0)]);p.push([w,[y[0],-y[1],-y[2],-y[3]]]),a=Math.max(a,w)}r.set(u,{pos:d,rot:p,scl:null})}const i=Math.max(a,(s.end-s.start)*s.ticksPerFrame*s.secsPerTick);return{kind:"node",name:n,tps:1,chan:r,duration:i,frames:Math.round(i*30)+1}}function ww(t,e,n,s){const o=t.byId.get(e);if(!o||o.type!==hn.CTRL||o.ver!==2086)return null;const r=t.dv,a=r.getInt32(o.off+16,!0),i=r.getInt32(o.off+20,!0),l=o.off+32,c=a===9?36:a===10?40:a===3?16:a===4?20:a===6?40:0;if(!c||i<=0||l+i*c>o.off+o.size)return null;const f=[];let h=[1,0,0,0];for(let u=0;u<i;u++){const d=l+u*c,p=r.getInt32(d,!0)*s,m=[1,2,3,4].map(g=>r.getFloat32(d+g*4,!0));if(n==="rot"){let g;if(a===10){const w=m[3]/2,y=Math.sin(w),M=[Math.cos(w),m[0]*y,m[1]*y,m[2]*y];h=yw(h,M),g=h}else g=[m[3],m[0],m[1],m[2]];f.push([p,[g[0],-g[1],-g[2],-g[3]]])}else f.push([p,n==="pos"?[m[0]*xe,m[1]*xe,m[2]*xe]:[m[0],m[1],m[2]]])}return f}const yw=(t,e)=>[t[0]*e[0]-t[1]*e[1]-t[2]*e[2]-t[3]*e[3],t[0]*e[1]+t[1]*e[0]+t[2]*e[3]-t[3]*e[2],t[0]*e[2]-t[1]*e[3]+t[2]*e[0]+t[3]*e[1],t[0]*e[3]+t[1]*e[2]-t[2]*e[1]+t[3]*e[0]],bw=/plants|leaf|leaves|grass|fence|alphatest|alphablend|alphaadd|decal|glass|muzzle|flare|hologram/i;function bh(t,e=""){const n=hh(t);if(n.fileType===4294901761)throw new Error("Это анимация CryEngine 1 (.caf) — откройте модель персонажа, анимации подключаются из её .cal");const s=n.chunks.filter(v=>v.type===hn.NODE).map(v=>ph(n,v)),o=new Map;for(const v of n.chunks)if(v.type===hn.MTL)try{o.set(v.id,dh(n,v))}catch{}const r=gh(n),a=Ja(n),i=new Map(s.map((v,b)=>[v.id,b])),l=[],c=(v,b=0)=>{if(l[v])return l[v];const k=s[v],E=pw(k.tm),A=i.get(k.parent);return l[v]=A!==void 0&&b<64?Lt(c(A,b+1),E):E},f=s.map(v=>["pos","rot","scl"].map((b,k)=>ww(n,v.ctrl[k],b,a.secsPerTick))),h=v=>!!v&&v.length>1&&v.some(([,b])=>b.some((k,E)=>Math.abs(k-v[0][1][E])>1e-4)),u=!r&&f.some(v=>v.some(h)),d=[];if(r){const v=r.map(b=>wh(b.bind));r.forEach((b,k)=>{const E=b.parent>=0?Lt(Vt(v[b.parent]),v[k]):v[k];d.push({name:b.name,parent:b.parent,local:E,offset:Vt(v[k])})})}else u&&s.forEach((v,b)=>{const k=c(b),E=i.get(v.parent);d.push({name:v.name,parent:E??-1,local:E!==void 0?Lt(yh(c(E)),k):k,offset:yh(k)})});const p=new Map;let m=0;if(s.forEach((v,b)=>{const k=n.byId.get(v.object);if(!k||k.type!==hn.MESH)return;const E=mh(n,k);m++;const A=c(b),S=o.get(v.mtl)??null,$=new Float32Array(E.nv*3),T=new Float32Array(E.nv*3),U=[];for(let L=0;L<E.nv;L++){const O=E.pos[L*3],N=E.pos[L*3+1],D=E.pos[L*3+2],F=r&&E.links?.[L];if(F&&F.length){let J=0,G=0,H=0,K=0;for(const[Q,V,W]of F){const Y=r[Q]?.bind;Y&&(J+=W*(V[0]*Y[0]+V[1]*Y[3]+V[2]*Y[6]+Y[9]),G+=W*(V[0]*Y[1]+V[1]*Y[4]+V[2]*Y[7]+Y[10]),H+=W*(V[0]*Y[2]+V[1]*Y[5]+V[2]*Y[8]+Y[11]),K+=W)}K=K||1,$[L*3]=J/K*xe,$[L*3+1]=G/K*xe,$[L*3+2]=H/K*xe,U.push(F.map(([Q,,V])=>[Q,V]).sort((Q,V)=>V[1]-Q[1]).slice(0,4))}else{const J=O*xe,G=N*xe,H=D*xe;$[L*3]=A[0]*J+A[4]*G+A[8]*H+A[12],$[L*3+1]=A[1]*J+A[5]*G+A[9]*H+A[13],$[L*3+2]=A[2]*J+A[6]*G+A[10]*H+A[14],U.push(u?[[b,1]]:[])}const C=E.nor[L*3],P=E.nor[L*3+1],I=E.nor[L*3+2];let B=A[0]*C+A[4]*P+A[8]*I,z=A[1]*C+A[5]*P+A[9]*I,j=A[2]*C+A[6]*P+A[10]*I;F&&F.length&&(B=C,z=P,j=I);const X=Math.hypot(B,z,j)||1;T[L*3]=B/X,T[L*3+1]=z/X,T[L*3+2]=j/X}const _=A[0]*(A[5]*A[10]-A[6]*A[9])-A[4]*(A[1]*A[10]-A[2]*A[9])+A[8]*(A[1]*A[6]-A[2]*A[5])<0,R=E.faces.length/3;for(let L=0;L<R;L++){const O=S?.children.length?o.get(S.children[E.mat[L]]??-1)??null:S,N=`${b}|${O?O.name+"|"+O.diffuse:"none"}`;let D=p.get(N);D||p.set(N,D={mtl:O,P:[],N:[],U:[],B:[],W:[],I:[]});const F=_?[0,2,1]:[0,1,2];for(const C of F){const P=E.faces[L*3+C];if(P<0||P>=E.nv)continue;D.P.push($[P*3],$[P*3+1],$[P*3+2]),D.N.push(T[P*3],T[P*3+1],T[P*3+2]);const I=E.tfaces?E.tfaces[L*3+C]:-1;D.U.push(E.uvs&&I>=0&&I*2+1<E.uvs.length?E.uvs[I*2]:0,E.uvs&&I>=0&&I*2+1<E.uvs.length?1-E.uvs[I*2+1]:0);const B=U[P];for(let z=0;z<4;z++)D.B.push(B[z]?.[0]??0),D.W.push(B[z]?.[1]??(z===0&&!B.length?1:0));D.I.push(D.I.length)}}}),!m)throw new Error("В файле CryEngine 1 нет мешей");const g=/^(nodraw|nouvmap|tbd)$/i,w=[...p.values()].filter(v=>v.I.length).map((v,b)=>{const k=v.P.length/3,E=v.mtl?.shader??"",A=(v.mtl?.diffuse??"").replace(/\\/g,"/"),S=new Map,$=[],T=[],U=[],_=[],R=[],L=[];for(let D=0;D<k;D++){const F=`${v.P[D*3]},${v.P[D*3+1]},${v.P[D*3+2]},${v.N[D*3].toFixed(4)},${v.N[D*3+1].toFixed(4)},${v.N[D*3+2].toFixed(4)},${v.U[D*2]},${v.U[D*2+1]}`;let C=S.get(F);if(C===void 0){C=$.length/3,S.set(F,C),$.push(v.P[D*3],v.P[D*3+1],v.P[D*3+2]),T.push(v.N[D*3],v.N[D*3+1],v.N[D*3+2]),U.push(v.U[D*2],v.U[D*2+1]);for(let P=0;P<4;P++)_.push(v.B[D*4+P]),R.push(v.W[D*4+P])}L.push(C)}const O=$.length/3,N=!!d.length;return{name:`${v.mtl?.name||"mesh "+b}${E?" · "+E:""}`,texture:A,shader:E,normalMap:v.mtl?.bump?v.mtl.bump.replace(/\\/g,"/"):void 0,vertexCount:O,pos:Float32Array.from($),nor:Float32Array.from(T),uv:Float32Array.from(U),bone:N?Float32Array.from(_):new Float32Array(O*4),weight:N?Float32Array.from(R):new Float32Array(O*4).map((D,F)=>F%4===0?1:0),indices:O>65535?Uint32Array.from(L):Uint16Array.from(L),skinned:N,...g.test(E)||/nodraw/i.test(A)?{defaultHidden:!0}:{},...bw.test(E)&&!/bark/i.test(E)?{alphaTest:!0}:{}}}),y=d.length?new Kt(d):null,M=[];if(u){const v=new Map;let b=0;s.forEach((k,E)=>{const[A,S,$]=f[E];if(!(!A&&!S&&!$)){for(const T of[A,S,$])for(const[U]of T??[])b=Math.max(b,U);v.set(E,{pos:A,rot:S,scl:$})}}),v.size&&M.push({kind:"node",name:"анимация объекта",tps:1,chan:v,duration:b||1,frames:Math.round(b*30)+1})}const x=[`CryEngine 1 (Far Cry, 0x744): ${e}`,`Узлов ${s.length}, мешей ${m}, материалов ${o.size}; частей ${w.length}, вершин ${w.reduce((v,b)=>v+b.vertexCount,0)}, треугольников ${Ut(w)}`];return r&&x.push(`Костей: ${r.length} (bind-позы из BoneInitialPos)`),u&&x.push(`Анимированные узлы (.cga): ${M[0]?.chan.size??0}`),{meshes:w,skeleton:y,motions:M,bounds:dt(w),flipZ:!1,defaultZUp:!0,cameraYaw:0,info:x,meta:{cry1Bones:r??void 0}}}function vw(t){const e=[];for(const n of t.split(/\r?\n/)){const s=n.replace(/\/\/.*$/,"").trim(),o=/^([^=\s#$]+)\s*=\s*(\S+\.caf)\s*$/i.exec(s);o&&e.push({name:o[1],path:o[2].replace(/\\/g,"/")})}return e}const vh=Object.freeze(Object.defineProperty({__proto__:null,CRY1_SCALE:xe,CRYTEK_MAGIC:fh,cry1Model:bh,isCryTek:uh,parseCal:vw,parseCry1:hh,readBones:gh,readCaf:gw,readMesh:mh,readMtl:dh,readNode:ph,readTiming:Ja},Symbol.toStringTag,{value:"Module"}));function dn(t){const e={};for(const n of t.matchAll(/([\w:.-]+)\s*=\s*"([^"]*)"/g))e[n[1]]=n[2].replace(/"/g,'"').replace(/</g,"<").replace(/>/g,">").replace(/&/g,"&");return e}const ti=(t,e)=>{const n=(t??"").split(",").map(Number);return n.length===3&&n.every(Number.isFinite)?n:e};function xh(t,e=new Map){const n=dn(/<Heightmap [^>]*>/.exec(t)?.[0]??""),s=dn(/<LevelInfo [^>]*>/.exec(t)?.[0]??""),o=+(n.Width??s.HeightmapSize??1024)||1024,r=+(n.UnitSize??s.HeightmapUnitSize??2)||2,a=+(n.WaterLevel??s.WaterLevel??0)||0,i=[],l=/<Object\s[^>]*?(\/?)>/g;let c;const f=new Map,h=/<Vegetation(?:Map)?\s*>([\s\S]*?)<\/Vegetation(?:Map)?>/.exec(t)?.[1]??"";for(const d of h.matchAll(/<Object\s[^>]*>/g)){const p=dn(d[0]);p.FileName&&p.Index!==void 0&&f.set(+p.Index,{index:+p.Index,file:p.FileName,hidden:p.Hidden==="1"})}for(;c=l.exec(t);){const d=dn(c[0]),p=d.Type??"";if(!p||p==="AIPoint"||p==="TagPoint")continue;let m="";if(p==="Brush")m=d.Prefab??"";else if(p==="Entity"||p==="SimpleEntity"){if(c[1]!=="/"){const g=t.indexOf("</Object>",c.index),w=t.slice(c.index,g<0?c.index+4e3:g),y=/<Properties\s[^>]*>/.exec(w)?.[0];y&&(m=Object.values(dn(y)).find(M=>/\.cg[af]$/i.test(M))??"")}!m&&d.Geometry&&(m=d.Geometry)}else p==="PrototypeEntity"&&(m=e.get((d.Prototype??"").toUpperCase())??"");!m||!/\.cg[af]$/i.test(m)||i.push({type:p,name:d.Name??"",model:m,pos:ti(d.Pos,[0,0,0]),angles:ti(d.Angles,[0,0,0]),scale:ti(d.Scale,[1,1,1]),layer:d.Layer??"",hidden:d.HiddenInGame==="1"})}const u=[...t.matchAll(/<Library Name="([^"]+)"/g)].map(d=>d[1]);return{size:o,unit:r,water:a,objects:i,vegetation:f,libraries:u}}function Mh(t,e=new Map){for(const n of t.matchAll(/<EntityPrototype\s[^>]*>[\s\S]*?(?=<EntityPrototype\s|$)/g)){const s=dn(/<EntityPrototype\s[^>]*>/.exec(n[0])[0]).Id,o=/<Properties\s[^>]*>/.exec(n[0])?.[0],r=o?Object.values(dn(o)).find(a=>/\.cg[af]$/i.test(a)):void 0;s&&r&&e.set(s.toUpperCase(),r)}return e}function kh(t){const e=new DataView(t.buffer,t.byteOffset,t.byteLength),n=Math.floor(t.length/19),s=[];for(let o=0;o<n;o++){const r=o*19;s.push({x:e.getFloat32(r,!0),y:e.getFloat32(r+4,!0),z:e.getFloat32(r+8,!0),s:e.getFloat32(r+12,!0),i:t[r+16]})}return s}function Eh(t,e,n){const[s,o,r]=e.map(p=>p*Math.PI/180),a=Math.cos(s),i=Math.sin(s),l=Math.cos(o),c=Math.sin(o),f=Math.cos(r),h=Math.sin(r),u=[f*l,f*c*i-h*a,f*c*a+h*i,h*l,h*c*i+f*a,h*c*a-f*i,-c,l*i,l*a],d=new Array(16).fill(0);for(let p=0;p<3;p++)for(let m=0;m<3;m++)d[p*4+m]=u[m*3+p]*n[p];return d[12]=t[0],d[13]=t[1],d[14]=t[2],d[15]=1,d}function Ah(t,e,n,s,o){const r=Math.floor(e/s)+1,a=r*r,i=new Float32Array(a*3),l=new Float32Array(a*3),c=new Float32Array(a*2),f=(d,p)=>t[Math.min(e-1,d)*e+Math.min(e-1,p)]/256;for(let d=0;d<r;d++)for(let p=0;p<r;p++){const m=d*s,g=p*s,w=d*r+p;i[w*3]=m*n,i[w*3+1]=g*n,i[w*3+2]=f(m,g);const y=f(m+s,g)-f(Math.max(0,m-s),g),M=f(m,g+s)-f(m,Math.max(0,g-s)),x=2*s*n,v=-y/x,b=-M/x,k=Math.hypot(v,b,1);l[w*3]=v/k,l[w*3+1]=b/k,l[w*3+2]=1/k,c[w*2]=g/e,c[w*2+1]=m/e}const h=new Uint32Array((r-1)*(r-1)*6);let u=0;for(let d=0;d+1<r;d++)for(let p=0;p+1<r;p++){const m=d*r+p;h[u++]=m,h[u++]=m+r,h[u++]=m+r+1,h[u++]=m,h[u++]=m+r+1,h[u++]=m+1}return{name:"Ландшафт",texture:o?"terrain/cover_low.dds":"",vertexCount:a,pos:i,nor:l,uv:c,bone:new Float32Array(0),weight:new Float32Array(0),indices:h,skinned:!1,...o?{image:o}:{color:[.45,.42,.3]}}}function $h(t,e,n,s){const o=Math.max(1,Math.round(32/n)),r=[],a=[],i=e*n,l=i*.25,c=(d,p,m,g)=>{const w=r.length/3;r.push(d,p,s,m,p,s,d,g,s,m,g,s),a.push(w,w+1,w+3,w,w+3,w+2)};for(let d=0;d<e;d+=o)for(let p=0;p<e;p+=o){let m=1/0;for(let g=d;g<=Math.min(e-1,d+o);g+=2)for(let w=p;w<=Math.min(e-1,p+o);w+=2)m=Math.min(m,t[g*e+w]/256);m<s&&c(d*n,p*n,(d+o)*n,(p+o)*n)}if(!a.length)return null;c(-l,-l,i+l,0),c(-l,i,i+l,i+l),c(-l,0,0,i),c(i,0,i+l,i);const f=r.length/3,h=new Float32Array(f*3),u=new Float32Array(f*2);for(let d=0;d<f;d++)h[d*3+2]=1;return{name:"Вода",texture:"",color:[.16,.3,.4],vertexCount:f,pos:Float32Array.from(r),nor:h,uv:u,bone:new Float32Array(0),weight:new Float32Array(0),indices:f>65535?Uint32Array.from(a):Uint16Array.from(a),skinned:!1,vertexAlpha:new Float32Array(f).fill(.6)}}const Sh=25e5;async function xw(t,e){const n=await t.arrayBuffer(),s=await cs(Object.assign(new Blob([n]),{name:t.name})),o=async U=>{const _=s.lookup(U)??s.entries.find(R=>R.path.toLowerCase()===U)??null;return _?s.read(_):null},r=await o("level.editor_xml");if(!r)throw new Error(`${t.name}: нет level.editor_xml — это не файл уровня Far Cry`);const a=Wt(r),i=new Map;for(const U of[...a.matchAll(/<Library Name="([^"]+)"/g)].map(_=>_[1])){const _=await e.readPath(gt(`editor/entitylibrary/${U}.xml`));_&&Mh(Wt(_),i)}const l=(t.path??"").replace(/[^/]*$/,""),c=[];let f=0;for(const U of a.matchAll(/<Layer\s[^>]*External="1"[^>]*>/g)){const _=dn(U[0]);if(!_.Name||_.Exportable==="0")continue;const R=t.archive?.lookup(`${l}layers/${_.Name.toLowerCase()}.lyr`);R&&(c.push(Wt(await t.archive.read(R))),f++)}const h=xh(c.length?a+`
|
|
191
|
+
`+c.join(`
|
|
192
|
+
`):a,i),u=[`Уровень Far Cry: ${t.name} — ландшафт ${h.size}×${h.size} × ${h.unit} м, вода ${h.water} м${f?`, внешних слоёв объектов: ${f}`:""}`],d=l;let p=null;const m=t.archive,g=m?.lookup(d+"level.pak");if(m&&g){const U=m.blobOf?.(g);try{p=U?await cs(U):await cs(Object.assign(new Blob([Rn(await m.read(g))]),{name:"level.pak"}))}catch{p=null}}const w=async U=>{const _=p?.lookup(U);return _?p.read(_):null},y=await o("heightmapdataw.editor_data")??await w("terrain/land_map.h16"),M=[];let x=null;if(y&&y.length>=h.size*h.size*2){x=new Uint16Array(Rn(y.subarray(0,h.size*h.size*2)));let U=null;const _=await w("terrain/cover_low.dds");if(_)try{U=Pn(Rn(_))}catch{U=null}M.push(Ah(x,h.size,h.unit,h.size>512?2:1,U));const R=$h(x,h.size,h.unit,h.water);R&&M.push(R),u.push(`Цвет ландшафта: ${U?`${U.width}×${U.height} (level.pak)`:"нет (не найден level.pak рядом)"}`)}else u.push("Нет карты высот");const v=new vs,b=new Map,k=new Set,E=async(U,_)=>{const R=gt(U.replace(/\\/g,"/")).replace(/\/{2,}/g,"/");if(b.has(R+"|"+_))return b.get(R+"|"+_);let L=null;try{const O=await e.readPath(R);if(O){const D=bh(Rn(O),R.split("/").pop()).meshes.filter(F=>!F.defaultHidden);D.length&&(L={pi:v.proto(R,D,_,F=>`${_==="veg"?"растения":"объекты"} · ${(F.texture||F.name).split("/").pop()}`),parts:D})}else k.add(R)}catch{k.add(R)}return b.set(R+"|"+_,L),L};let A=0;for(const U of h.objects){if(U.hidden)continue;const _=await E(U.model,"obj");_&&(v.place(_.pi,_.parts,Eh(U.pos,U.angles,U.scale),U.name,U.layer||U.type),A++)}u.push(`Объектов: ${A} из ${h.objects.length} (Brush ${h.objects.filter(U=>U.type==="Brush").length}, сущности с моделью ${h.objects.filter(U=>U.type!=="Brush").length})`);const S=await o("vegetationinstancesarray.editor_data");if(S&&x){const U=kh(S),_=new Map;for(const I of U){const B=h.vegetation.get(I.i);if(!B||B.hidden)continue;let z=_.get(I.i);z||_.set(I.i,z=[]),z.push(I)}let R=0;const L=new Map;for(const[I,B]of _){const z=await E(h.vegetation.get(I).file,"veg");if(!z)continue;const j=z.parts.reduce((X,J)=>X+J.indices.length/3,0);L.set(I,{...z,tris:j}),R+=j*B.length}const O=new Map;let N=Sh,D=[...L.keys()].sort((I,B)=>L.get(I).tris*_.get(I).length-L.get(B).tris*_.get(B).length);for(;D.length;){const I=N/D.length,B=D[0],z=L.get(B).tris*_.get(B).length;if(z<=I){O.set(B,1),N-=z,D=D.slice(1);continue}for(const j of D)O.set(j,Math.max(0,I/(L.get(j).tris*_.get(j).length)));break}const F=Math.min(...O.values(),1);let C=0,P=0;for(const[I,B]of _){const z=L.get(I);if(!z)continue;const j=h.vegetation.get(I).file.split(/[\\/]/).pop();B.forEach((X,J)=>{P++;const G=O.get(I)??1;if(G<1&&(J*2654435761>>>0)/4294967296>=G)return;const H=Math.floor(X.x/h.unit),K=Math.floor(X.y/h.unit),Q=X.z||(H>=0&&K>=0&&H<h.size&&K<h.size?x[H*h.size+K]/256:0),V=X.s>0?X.s:1;v.place(z.pi,z.parts,[V,0,0,0,0,V,0,0,0,0,V,0,X.x,X.y,Q,1],j,"Растительность"),C++})}u.push(`Растительность: ${C} из ${P}${F<1?` (массовые растения прорежены до ${Math.max(1,Math.round(F*100))} % — бюджет ${Sh/1e6} млн треугольников)`:""}, видов ${L.size}`)}k.size&&u.push(`Не найдено моделей: ${k.size} (${[...k].slice(0,5).join(", ")}${k.size>5?"…":""})`);const $=M.reduce((U,_)=>U+_.indices.length/3,0),T=[...v.build(),...M];if(u.push(`Треугольников: ${Math.round(v.tris+$).toLocaleString("ru")}`),!T.length)throw new Error(`${t.name}: в уровне нет ни ландшафта, ни объектов`);return{meshes:T,instances:v.scene,skeleton:null,motions:[],bounds:dt(T),flipZ:!1,defaultZUp:!0,cameraYaw:0,info:u}}const Mw=Object.freeze(Object.defineProperty({__proto__:null,loadFc1Level:xw,objectMatrix:Eh,parseEditorXml:xh,parsePrototypes:Mh,parseVegetation:kh,terrainMesh:Ah,waterMesh:$h,xmlAttrs:dn},Symbol.toStringTag,{value:"Module"})),ei={stream:3435921430,subsets:3435921431,mtlname:3435921428},ro={POS:0,UV:2,INDICES:5},er={CrCh:1749250627,CryT:1417245251,ivo:1870031139};function Th(t){const e=new DataView(t),n=l=>e.getUint32(l,!0),s=t.byteLength>=4?n(0):0;if(s===er.CryT)throw new Error("Контейнер «CryT/CryTek» (Far Cry 1, MWO, ArcheAge) нативно не читается — сконвертируйте Cryengine Converter в Collada (.dae)");if(s===er.ivo)throw new Error("Формат «#ivo» (Star Citizen) нативно не читается — сконвертируйте Cryengine Converter в Collada (.dae)");if(s!==er.CrCh||t.byteLength<16)throw new Error("Не CryEngine CGF (нет сигнатуры CrCh)");const o=n(4),r=n(8),a=n(12);if(r<=0||r>1e5||a+r*20>t.byteLength)throw new Error("CGF: неверная таблица чанков");const i=[];for(let l=0;l<r;l++){const c=a+l*20,f={type:n(c),ver:n(c+4)&2147483647,id:n(c+8),size:n(c+12),off:n(c+16)};if(f.off+f.size>t.byteLength)throw new Error(`CGF: раскладка таблицы чанков версии 0x${o.toString(16)} не распознана`);i.push(f)}return{ver:o,chunks:i.sort((l,c)=>l.off-c.off)}}function kw(t,e){for(const n of[0,16,20]){const s=e.off+n;if(s+24>e.off+e.size)continue;const o=t.getUint32(s+4,!0),r=t.getUint32(s+8,!0),a=t.getUint32(s+12,!0);if(!(o>20||!r||!a||a>64||s+24+r*a>e.off+e.size))return{type:o,n:r,bpe:a,data:s+24}}return null}function Ew(t,e){for(const n of[0,16,20]){const s=e.off+n;if(s+16>e.off+e.size)continue;const o=t.getUint32(s+4,!0);if(!o||o>4096)continue;const r=Math.floor((e.off+e.size-s-16)/o);if(r<28||r>64)continue;const a=[];for(let i=0;i<o;i++){const l=s+16+i*r;a.push({first:t.getUint32(l,!0),count:t.getUint32(l+4,!0),mat:t.getUint32(l+16,!0)})}return a}return null}function Aw(t){const{ver:e,chunks:n}=Th(t),s=new DataView(t),o=new Uint8Array(t),r=[];let a={};for(const b of n){if(b.type!==ei.stream)continue;const k=kw(s,b);k&&(a[k.type]&&(r.push(a),a={}),a[k.type]=k)}Object.keys(a).length&&r.push(a);const i=n.filter(b=>b.type===ei.subsets).map(b=>Ew(s,b)).filter(b=>!!b),l=r.findIndex(b=>b[ro.POS]&&b[ro.INDICES]);if(l<0)throw new Error(`В CGF (версия 0x${e.toString(16)}) не найдены потоки позиций/индексов (возможно, сжатый формат или иная версия)`);const c=r[l],f=c[ro.POS],h=c[ro.INDICES],u=f.n;if(f.bpe!==12&&f.bpe!==16)throw new Error(`Формат позиций (${f.bpe} байт/вершина) не поддержан`);const d=new Float32Array(u*3);for(let b=0;b<u;b++)for(let k=0;k<3;k++)d[b*3+k]=s.getFloat32(f.data+b*f.bpe+k*4,!0);const p=new Uint32Array(h.n);for(let b=0;b<h.n;b++)p[b]=h.bpe===2?s.getUint16(h.data+b*2,!0):s.getUint32(h.data+b*4,!0);for(let b=0;b<p.length;b++)if(p[b]>=u)throw new Error("CGF: индекс вершины вне диапазона (раскладка потока не совпала)");const m=new Float32Array(u*2),g=c[ro.UV];if(g&&g.n>=u)for(let b=0;b<u;b++)g.bpe===8?(m[b*2]=s.getFloat32(g.data+b*8,!0),m[b*2+1]=s.getFloat32(g.data+b*8+4,!0)):g.bpe===4&&(m[b*2]=Fe(s.getUint16(g.data+b*4,!0)),m[b*2+1]=Fe(s.getUint16(g.data+b*4+2,!0)));const w=i[Math.min(l,i.length-1)]??[{first:0,count:p.length,mat:0}],y=ge(d,p),M=[];for(const b of w){if(b.first>p.length||b.count>p.length)continue;const k=p.subarray(b.first,Math.min(p.length,b.first+b.count));k.length<3||M.push({name:`subset (mat ${b.mat})`,texture:"",matId:b.mat,shader:"",vertexCount:u,pos:d,nor:y,uv:m,bone:new Float32Array(u*4),weight:new Float32Array(u*4),indices:$e(u,k),skinned:!1})}if(!M.length)throw new Error("В CGF нет треугольников");let x="";const v=n.find(b=>b.type===ei.mtlname);if(v){let b="";for(let k=v.off;k<v.off+v.size;k++){const E=o[k];E>=32&&E<127?b+=String.fromCharCode(E):(b.length>x.length&&(x=b),b="")}b.length>x.length&&(x=b)}return{meshes:M,skeleton:null,motions:[],bounds:dt([{pos:d}]),flipZ:!1,defaultZUp:!0,cameraYaw:Math.PI,meta:{mtlName:x},info:[`CryEngine CGF (CrCh 0x${e.toString(16)}): вершин ${u}, подмешей ${M.length}, треугольников ${Ut(M)}${r.length>1?`, групп потоков ${r.length} (показана №${l})`:""}`,"Только статика: трансформы узлов, скелет и анимации не применяются.",x?`Материал: ${x}.mtl`:"Имя .mtl не найдено — материал берётся рядом по имени файла"]}}const $w=Object.freeze(Object.defineProperty({__proto__:null,CRY_MAGIC:er,convertCgf:Aw,parseChunks:Th},Symbol.toStringTag,{value:"Module"})),zt=(t,e)=>{const n=new RegExp(`\\s${e}\\s*=\\s*"([^"]*)"`).exec(t);return n?n[1]:void 0},nr=t=>{const e=[];for(const n of t.trim().split(/\s+/))n&&e.push(+n);return e};function Sw(t,e,n){const s=new RegExp(`<${e}[\\s>/]|</${e}>`,"g");s.lastIndex=n;let o=0,r;for(;r=s.exec(t);)if(r[0].startsWith("</")){if(--o===0)return r.index}else{const a=t.indexOf(">",r.index);if(t[a-1]!=="/")o++;else if(o===0)return a}return t.length}function fe(t,e,n=0,s=t.length){const o=[],r=new RegExp(`<${e}[\\s>/]`,"g");r.lastIndex=n;let a;for(;(a=r.exec(t))&&a.index<s;){const i=t.indexOf(">",a.index),l=t.slice(a.index,i+1);if(l.endsWith("/>")){o.push({tag:l,body:"",start:a.index});continue}const c=Sw(t,e,a.index);o.push({tag:l,body:t.slice(i+1,c),start:a.index}),r.lastIndex=c+e.length+3}return o}const De=(t,e)=>fe(t,e)[0];function Tw(t){const e=new Map,n=new Map,s=[],o=De(t,"mesh");if(!o)return{sources:e,vertices:n,prims:s};for(const r of fe(o.body,"source")){const a=De(r.body,"float_array"),i=De(r.body,"accessor");a&&e.set(zt(r.tag,"id")??"",{data:nr(a.body),stride:+(i?zt(i.tag,"stride")??"1":"1")})}for(const r of fe(o.body,"vertices")){const a=fe(r.body,"input").find(i=>zt(i.tag,"semantic")==="POSITION");a&&n.set(zt(r.tag,"id")??"",(zt(a.tag,"source")??"").replace(/^#/,""))}for(const r of["triangles","polylist","polygons"])for(const a of fe(o.body,r)){const i=fe(a.body,"input").map(d=>({semantic:zt(d.tag,"semantic")??"",source:(zt(d.tag,"source")??"").replace(/^#/,""),offset:+(zt(d.tag,"offset")??0),set:+(zt(d.tag,"set")??0)})),l=r==="polylist"?De(a.body,"vcount"):void 0,c=fe(a.body,"p"),f=[],h=r==="triangles"?null:[],u=Math.max(...i.map(d=>d.offset))+1;for(const d of c){const p=nr(d.body);for(const m of p)f.push(m);r==="polygons"&&h.push(p.length/u)}if(l)for(const d of nr(l.body))h.push(d);s.push({material:zt(a.tag,"material")??"",inputs:i,count:+(zt(a.tag,"count")??0),vcount:h,p:f})}return{sources:e,vertices:n,prims:s}}function Uw(t){const e=t.slice(0,(()=>{const r=t.search(/<node[\s>]/);return r<0?t.length:r})());let n=nn();const s=/<(matrix|translate|rotate|scale)[^>]*>([^<]*)<\/\1>/g;let o;for(;o=s.exec(e);){const r=nr(o[2]);let a=nn();if(o[1]==="matrix")a=Co(r);else if(o[1]==="translate")a[12]=r[0],a[13]=r[1],a[14]=r[2];else if(o[1]==="scale")a[0]=r[0],a[5]=r[1],a[10]=r[2];else{const[i,l,c,f]=r,h=Math.hypot(i,l,c)||1,u=f*Math.PI/180,d=[i/h,l/h,c/h],p=Math.cos(u),m=Math.sin(u),g=1-p,w=[p+d[0]*d[0]*g,d[0]*d[1]*g-d[2]*m,d[0]*d[2]*g+d[1]*m,d[1]*d[0]*g+d[2]*m,p+d[1]*d[1]*g,d[1]*d[2]*g-d[0]*m,d[2]*d[0]*g-d[1]*m,d[2]*d[1]*g+d[0]*m,p+d[2]*d[2]*g];for(let y=0;y<3;y++)for(let M=0;M<3;M++)a[M*4+y]=w[y*3+M]}n=Lt(n,a)}return n}function Fw(t,e={}){const n=/<up_axis>\s*(\w+)\s*<\/up_axis>/.exec(t)?.[1]??"Y_UP",s=new Map,o=De(t,"library_images");if(o)for(const g of fe(o.body,"image")){const w=/<init_from>\s*([^<]*?)\s*<\/init_from>/.exec(g.body)?.[1]??"";s.set(zt(g.tag,"id")??"",decodeURIComponent(w.replace(/^file:\/+/,"")))}const r=new Map,a=De(t,"library_effects");if(a)for(const g of fe(a.body,"effect")){const w=De(g.body,"diffuse")??De(g.body,"emission"),y=w?/<texture\s[^>]*texture="([^"]*)"/.exec(w.body)?.[1]:void 0;if(!y)continue;let M=y;const x=new RegExp(`<newparam sid="${y}">[\\s\\S]*?<source>([^<]*)</source>`).exec(g.body)?.[1];x&&(M=new RegExp(`<newparam sid="${x}">[\\s\\S]*?<init_from>([^<]*)</init_from>`).exec(g.body)?.[1]??M),r.set(zt(g.tag,"id")??"",s.get(M)??M)}const i=new Map,l=De(t,"library_materials");if(l)for(const g of fe(l.body,"material")){const w=/<instance_effect url="#([^"]*)"/.exec(g.body)?.[1]??"";i.set(zt(g.tag,"id")??"",{name:zt(g.tag,"name")??zt(g.tag,"id")??"",texture:r.get(w)??""})}const c=new Map,f=De(t,"library_geometries");if(f)for(const g of fe(f.body,"geometry"))c.set(zt(g.tag,"id")??"",g);const h=[],u=[],d=e.nodes?new Set([...e.nodes].map(g=>g.toLowerCase())):null,p=(g,w)=>{for(const y of fe(g,"node")){const M=zt(y.tag,"name")??zt(y.tag,"id")??"",x=Lt(w,Uw(y.body)),v=!d||d.has(M.toLowerCase()),b=y.body.slice(0,(()=>{const k=y.body.search(/<node[\s>]/);return k<0?y.body.length:k})());for(const k of fe(b,"instance_geometry")){if(u.push(M),!v)continue;const E=c.get((zt(k.tag,"url")??"").replace(/^#/,""));if(!E)continue;const A=new Map;for(const S of fe(k.body,"instance_material"))A.set(zt(S.tag,"symbol")??"",(zt(S.tag,"target")??"").replace(/^#/,""));for(const S of _w(Tw(E.body),M,x,$=>i.get(A.get($)??$)??{name:$,texture:""}))h.push(S)}p(y.body,x)}},m=De(t,"library_visual_scenes");if(m)for(const g of fe(m.body,"visual_scene"))p(g.body,nn());return{upAxis:n,meshes:h,nodeNames:u}}function _w(t,e,n,s){const o=[],r=Lt(n,nn());for(const a of t.prims){const i=a.inputs.find(A=>A.semantic==="VERTEX"),l=a.inputs.find(A=>A.semantic==="NORMAL"),c=a.inputs.filter(A=>A.semantic==="TEXCOORD").sort((A,S)=>A.set-S.set)[0];if(!i)continue;const f=t.sources.get(t.vertices.get(i.source)??i.source),h=l&&t.sources.get(l.source),u=c&&t.sources.get(c.source);if(!f)continue;const d=Math.max(...a.inputs.map(A=>A.offset))+1,p=[];if(a.vcount){let A=0;for(const S of a.vcount){for(let $=1;$+1<S;$++)p.push(A,A+$,A+$+1);A+=S}}else for(let A=0;A<a.p.length/d;A++)p.push(A);const m=new Map,g=[],w=[],y=[],M=[];for(const A of p){const S=A*d,$=a.p[S+i.offset],T=l?a.p[S+l.offset]:-1,U=c?a.p[S+c.offset]:-1,_=`${$}/${T}/${U}`;let R=m.get(_);if(R===void 0){R=m.size,m.set(_,R);const L=f.stride,O=f.data[$*L],N=f.data[$*L+1],D=f.data[$*L+2];if(g.push(n[0]*O+n[4]*N+n[8]*D+n[12],n[1]*O+n[5]*N+n[9]*D+n[13],n[2]*O+n[6]*N+n[10]*D+n[14]),h&&T>=0){const F=h.stride,C=h.data[T*F],P=h.data[T*F+1],I=h.data[T*F+2];w.push(r[0]*C+r[4]*P+r[8]*I,r[1]*C+r[5]*P+r[9]*I,r[2]*C+r[6]*P+r[10]*I)}u&&U>=0?y.push(u.data[U*u.stride],1-u.data[U*u.stride+1]):y.push(0,0)}M.push(R)}const x=m.size;if(!x)continue;const v=Float32Array.from(g);let b;if(w.length===x*3){b=Float32Array.from(w);for(let A=0;A<x;A++){const S=Math.hypot(b[A*3],b[A*3+1],b[A*3+2])||1;b[A*3]/=S,b[A*3+1]/=S,b[A*3+2]/=S}}else b=ge(v,M);const k=s(a.material),E=new Float32Array(x*4);for(let A=0;A<x;A++)E[A*4]=1;o.push({name:e,nodeName:e,world:n,texture:k.texture,material:k.name,shader:"",vertexCount:x,pos:v,nor:b,uv:Float32Array.from(y),bone:new Float32Array(x*4),weight:E,indices:$e(x,M),skinned:!1})}return o}function ni(t){let e=0;const n=t.length,s=()=>{for(;;){for(;e<n&&/[\s,]/.test(t[e]);)e++;if(t[e]==="/"&&t[e+1]==="/"){for(;e<n&&t[e]!==`
|
|
193
|
+
`;)e++;continue}if(t[e]==="/"&&t[e+1]==="*"){const i=t.indexOf("*/",e+2);e=i<0?n:i+2;continue}break}},o=i=>{const l=t.slice(0,e).split(`
|
|
194
|
+
`).length;return new Error(`jbeam: ${i} (строка ${l})`)},r=()=>{s();const i=t[e];if(i==="{"){e++;const f={};for(;;){if(s(),t[e]==="}")return e++,f;if(e>=n)throw o("незакрытый объект");const h=r();if(s(),t[e]!==":")throw o("ожидалось «:»");e++,f[String(h)]=r()}}if(i==="["){e++;const f=[];for(;;){if(s(),t[e]==="]")return e++,f;if(e>=n)throw o("незакрытый массив");f.push(r())}}if(i==='"'){let f="";for(e++;e<n&&t[e]!=='"';)if(t[e]==="\\"){const h=t[e+1];f+=h==="n"?`
|
|
195
|
+
`:h==="t"?" ":h,e+=2}else f+=t[e++];return e++,f}const l=/^(-?(?:\d+\.?\d*|\.\d+)(?:[eE][-+]?\d+)?|true|false|null|nil|-?inf|nan)/i.exec(t.slice(e,e+40));if(!l)throw o(`неожиданный символ «${i??"конец"}»`);e+=l[0].length;const c=l[0].toLowerCase();return c==="true"?!0:c==="false"?!1:c==="null"||c==="nil"?null:c==="inf"?1/0:c==="-inf"?-1/0:c==="nan"?NaN:+l[0]};return r()}function Uh(t,e){return typeof t!="string"?t??null:t.startsWith("$=")?Lw(t.slice(2),e):/^\$[A-Za-z_]\w*$/.test(t)?e.has(t)?e.get(t):null:t}const si=(t,e,n=0)=>{const s=Uh(t,e);return typeof s=="number"&&Number.isFinite(s)?s:typeof s=="boolean"?+s:n},Cw={case:(...t)=>{for(let e=0;e+1<t.length;e+=2)if(t[e]!==null&&t[e]!==!1)return t[e+1];return t.length%2?t[t.length-1]:null},min:(...t)=>Math.min(...t.map(Number)),max:(...t)=>Math.max(...t.map(Number)),abs:t=>Math.abs(Number(t)),clamp:(t,e,n)=>Math.min(Number(n),Math.max(Number(e),Number(t))),round:t=>Math.round(Number(t)),floor:t=>Math.floor(Number(t)),ceil:t=>Math.ceil(Number(t)),sqrt:t=>Math.sqrt(Number(t)),sin:t=>Math.sin(Number(t)),cos:t=>Math.cos(Number(t)),tan:t=>Math.tan(Number(t)),rad:t=>Number(t)*Math.PI/180,deg:t=>Number(t)*180/Math.PI};function Lw(t,e){const n=t.match(/\$?[A-Za-z_][\w.]*|\d+\.?\d*(?:[eE][-+]?\d+)?|\.\d+|==|~=|!=|<=|>=|&&|\|\||[-+*/%^()<>,!?:]|"[^"]*"/g)??[];let s=0;const o=()=>n[s],r=()=>n[s++],a=w=>w!==null&&w!==!1,i=w=>typeof w=="number"?w:typeof w=="boolean"?+w:Number(w)||0,l=()=>{const w=r();if(w===void 0)return null;if(w==="("){const y=g();return r(),y}if(w==="-")return-i(c());if(w==="not"||w==="!")return!a(c());if(w.startsWith('"'))return w.slice(1,-1);if(/^[\d.]/.test(w))return+w;if(w.startsWith("$"))return e.has(w)?e.get(w):null;if(w==="nil")return null;if(w==="true")return!0;if(w==="false")return!1;if(o()==="("){r();const y=[];for(;o()!==")"&&s<n.length;)y.push(g()),o()===","&&r();r();const M=Cw[w.replace(/^math\./,"")];return M?M(...y):null}return null},c=()=>l(),f=()=>{let w=c();for(;o()==="^";)r(),w=i(w)**i(c());return w},h=()=>{let w=f();for(let y=o();y==="*"||y==="/"||y==="%";y=o()){r();const M=i(f());w=y==="*"?i(w)*M:y==="/"?i(w)/M:i(w)%M}return w},u=()=>{let w=h();for(let y=o();y==="+"||y==="-";y=o()){r();const M=i(h());w=y==="+"?i(w)+M:i(w)-M}return w},d=()=>{let w=u();for(let y=o();y&&["==","~=","!=","<",">","<=",">="].includes(y);y=o()){r();const M=u();w=y==="=="?w===M:y==="~="||y==="!="?w!==M:y==="<"?i(w)<i(M):y===">"?i(w)>i(M):y==="<="?i(w)<=i(M):i(w)>=i(M)}return w},p=()=>{let w=d();for(;o()==="and"||o()==="&&";){r();const y=d();w=a(w)?y:w}return w},m=()=>{let w=p();for(;o()==="or"||o()==="||";){r();const y=p();w=a(w)?w:y}return w},g=()=>{const w=m();if(o()==="?"){r();const y=g();r();const M=g();return a(w)?y:M}return w};return g()}const En=t=>!!t&&typeof t=="object"&&!Array.isArray(t);function sr(t){return Array.isArray(t)?t.slice(1).filter(e=>Array.isArray(e)):[]}const oi=t=>{for(let e=t.length-1;e>=0;e--)if(En(t[e]))return t[e];return null};function Fh(t){const e=[];for(const n of sr(t.data.slots))e.push({type:String(n[0]??""),def:String(n[1]??""),opts:oi(n)});for(const n of sr(t.data.slots2)){const s=Array.isArray(n[1])?n[1].map(String):[String(n[0]??"")];for(const o of s.length?s:[String(n[0]??"")])e.push({type:String(n[0]??o),def:String(n[3]??""),opts:oi(n)})}return e}async function _h(t){const e=new Map;for(const n of t)for(const s of n.entries){if(!s.path.endsWith(".jbeam"))continue;let o;try{o=ni(Wt(await n.read(s)))}catch{continue}if(En(o))for(const[r,a]of Object.entries(o))En(a)&&!e.has(r)&&e.set(r,{name:r,slotType:String(a.slotType??""),data:a,file:s.path})}return e}function Ch(t,e,n,s={}){const o=[],r=[],a=[],i=new Set,l=new Map;for(const u of t.values())l.has(u.slotType)||l.set(u.slotType,[]),l.get(u.slotType).push(u.name);const c=(u,d)=>{if(!u||i.has(u))return;const p=t.get(u);if(!p){r.push(u);return}i.add(u),o.push({part:p,offsets:d});for(const m of Fh(p)){const g=Object.prototype.hasOwnProperty.call(n,m.type)?n[m.type]:m.def,w=m.opts&&En(m.opts.nodeOffset)?[...d,m.opts.nodeOffset]:d;if(g&&!t.has(g)){const y=t.has(m.def)?m.def:l.get(m.type)?.[0];if(y){a.push(`${g} → ${y}`),c(y,w);continue}}c(g,w)}};c(e,[]);const f=new Map;for(const u of o){const d=u.part.data.variables;if(!Array.isArray(d)||!Array.isArray(d[0]))continue;const p=d[0].map(String),m=p.indexOf("name"),g=p.indexOf("default");for(const w of sr(d))m>=0&&g>=0&&f.set(String(w[m]),w[g]??null)}for(const[u,d]of Object.entries(s))f.set(u,d);const h=[];for(const u of o)for(const d of sr(u.part.data.flexbodies))typeof d[0]!="string"||!d[0]||h.push({mesh:d[0],part:u.part.name,opts:oi(d),offsets:u.offsets});return{chosen:o,flexbodies:h,missing:r,substituted:a,vars:f}}const or=(t,e,n)=>En(t)?[si(t.x,e,n),si(t.y,e,n),si(t.z,e,n)]:[n,n,n];function Rw([t,e,n]){const[s,o,r]=[t,e,n].map(u=>u*Math.PI/180),a=Math.cos(s),i=Math.sin(s),l=Math.cos(o),c=Math.sin(o),f=Math.cos(r),h=Math.sin(r);return[f*l,f*c*i-h*a,f*c*a+h*i,h*l,h*c*i+f*a,h*c*a-f*i,-c,l*i,l*a]}function Lh(t,e,n){const s=e.opts,o=or(s?.pos,n,0),r=or(s?.rot,n,0),a=or(s?.scale,n,1),i=Rw(r),l=t.vertexCount,c=new Float32Array(l*3),f=new Float32Array(l*3);for(let p=0;p<l;p++){const m=t.pos[p*3]*a[0],g=t.pos[p*3+1]*a[1],w=t.pos[p*3+2]*a[2];c[p*3]=i[0]*m+i[1]*g+i[2]*w+o[0],c[p*3+1]=i[3]*m+i[4]*g+i[5]*w+o[1],c[p*3+2]=i[6]*m+i[7]*g+i[8]*w+o[2];const y=t.nor[p*3],M=t.nor[p*3+1],x=t.nor[p*3+2];f[p*3]=i[0]*y+i[1]*M+i[2]*x,f[p*3+1]=i[3]*y+i[4]*M+i[5]*x,f[p*3+2]=i[6]*y+i[7]*M+i[8]*x}let h=0,u=0,d=0;for(const p of e.offsets){const[m,g,w]=or(p,n,0);h+=m,u+=g,d+=w}if(h||u||d)for(let p=0;p<l;p++){const m=c[p*3];c[p*3]=m+Math.sign(m)*h,c[p*3+1]+=u,c[p*3+2]+=d}return{...t,name:`${e.mesh} · ${e.part}`,pos:c,nor:f}}async function Ow(t,e){const n=ni(Wt(await t.arrayBuffer()));if(!n||typeof n!="object"||!n.parts)throw new Error('Не конфигурация BeamNG (.pc): нет "parts"');const s=[...e.archives];t.archive&&!s.includes(t.archive)&&s.push(t.archive);const o=await _h(s);if(!o.size)throw new Error("Не найдено ни одного .jbeam — откройте мод целиком («Папка игры…» или zip)");const r=n.model??(t.path?/vehicles\/([^/]+)\//.exec(t.path)?.[1]:void 0)??"";let a=n.mainPartName??"";if(o.has(a)||(a=[...o.values()].find(w=>w.slotType==="main"&&w.file.includes(`/vehicles/${r.toLowerCase()}/`))?.name??[...o.values()].find(w=>w.slotType==="main")?.name??""),!a)throw new Error('Не найдена главная деталь (slotType "main")');const i=Ch(o,a,n.parts,n.vars??{}),l=new Set(i.flexbodies.map(w=>w.mesh.toLowerCase())),c=new Map,f=[];for(const w of s)for(const y of w.entries)y.path.endsWith(".dae")&&f.push([w,y]);const h=`/vehicles/${r.toLowerCase()}/`;f.sort((w,y)=>+y[1].path.includes(h)-+w[1].path.includes(h));for(const[w,y]of f){if([...l].every(b=>c.has(b)))break;const M=Wt(await w.read(y)),x=M.toLowerCase(),v=new Set([...l].filter(b=>!c.has(b)&&(x.includes(`name="${b}"`)||x.includes(`id="${b}"`))));if(v.size)for(const b of Fw(M,{nodes:v}).meshes){const k=b.nodeName.toLowerCase();c.has(k)||c.set(k,[]),c.get(k).push(b)}}const u=n.paints?.[0]?.baseColor,d=await Dh(s),p=[],m=new Set;for(const w of i.flexbodies){const y=c.get(w.mesh.toLowerCase());if(!y){m.add(w.mesh);continue}for(const M of y){const x=Lh(M,w,i.vars),v=d.get((M.material??"").toLowerCase());v?.normal&&(x.normalMap=v.normal),v?.surface&&(x.surface=v.surface),v?.color?x.texture=v.color:x.color=(v?.factor??u??[.7,.7,.7]).slice(0,3).map(b=>Math.min(1,b)),p.push(x)}}if(!p.length)throw new Error(`Ни один меш не найден в .dae (нужно ${l.size}: ${[...l].slice(0,5).join(", ")}…)`);const g=[`BeamNG: ${r} (${t.name}), деталей ${i.chosen.length}, мешей ${p.length}, вершин ${le(p)}, треугольников ${Ut(p)}`,"Пропсы (руль, приборы, фары-источники) и деформация пока не показываются."];return i.missing.length&&g.push(`Нет деталей (в моде не хватает файлов игры?): ${i.missing.slice(0,8).join(", ")}${i.missing.length>8?"…":""}`),i.substituted.length&&g.push(`Заменены деталями слота по умолчанию: ${i.substituted.slice(0,6).join(", ")}${i.substituted.length>6?"…":""}`),m.size&&g.push(`Нет мешей в .dae: ${[...m].slice(0,8).join(", ")}${m.size>8?"…":""}`),{meshes:p,skeleton:null,motions:[],bounds:dt(p),flipZ:!1,defaultZUp:!0,cameraYaw:0,info:g,meta:{beamng:{model:r,parts:i.chosen.map(w=>w.part.name)}}}}const ri=/(^|\/)(null|generic_[a-z]*|white)(\.[a-z]+)*\.(dds|png|jpe?g|tga)$/i,Rh=(t,e,n)=>{for(const s of e)s&&!t.has(s.toLowerCase())&&t.set(s.toLowerCase(),n)};function Oh(t,e){let n;try{n=ni(t)}catch{return}if(En(n))for(const[s,o]of Object.entries(n)){if(!En(o))continue;let r="",a,i="",l;const c=Array.isArray(o.Stages)?o.Stages:[];for(const f of c)if(En(f)){l||(l=Iw(f)),!i&&typeof f.normalMap=="string"&&f.normalMap&&!/generic_n|null\./i.test(f.normalMap)&&(i=f.normalMap),!a&&Array.isArray(f.baseColorFactor)&&(a=f.baseColorFactor.slice(0,3).map(Number));for(const h of["baseColorMap","colorMap","diffuseMap"]){const u=f[h];if(typeof u=="string"&&u&&!ri.test(u)){r=u;break}}if(r)break}!r&&typeof o.diffuseMap=="string"&&!ri.test(o.diffuseMap)&&(r=o.diffuseMap),!(!r&&!a)&&Rh(e,[s,typeof o.mapTo=="string"?o.mapTo:"",typeof o.name=="string"?o.name:""],{color:r,factor:a,...i?{normal:i}:{},...l?{surface:l}:{}})}}function Iw(t){const e=o=>typeof t[o]=="string"&&t[o]&&!/null\./i.test(t[o])?{map:t[o],channel:0}:void 0,n=o=>typeof t[o]=="number"?t[o]:void 0,s={roughness:e("roughnessMap"),metallic:e("metallicMap"),ao:e("ambientOcclusionMap"),roughnessFactor:n("roughnessFactor"),metallicFactor:n("metallicFactor"),source:"BeamNG"};return s.roughness||s.metallic||s.ao||s.roughnessFactor!==void 0||s.metallicFactor!==void 0?s:void 0}function Ih(t,e){const n=/(?:singleton|new)\s+Material\s*\(\s*([\w.]+)\s*\)\s*\{([\s\S]*?)\n\s*\};?/g;let s;for(;s=n.exec(t);){const o=s[2],r=/mapTo\s*=\s*"([^"]*)"/.exec(o)?.[1],a=[...o.matchAll(/(?:diffuseMap|colorMap|baseColorMap)\s*(?:\[\s*(\d+)\s*\])?\s*=\s*"([^"]*)"/g)].map(p=>({layer:+(p[1]??0),path:p[2]})).filter(p=>p.path&&!ri.test(p.path)).sort((p,m)=>p.layer-m.layer),i=/(?:baseColorFactor|diffuseColor)\s*(?:\[\s*0\s*\])?\s*=\s*"([^"]*)"/.exec(o)?.[1]?.trim().split(/\s+/).map(Number),l=i&&i.length>=3&&i.every(Number.isFinite)?i.slice(0,3):void 0;if(!a.length&&!l)continue;const c=[...o.matchAll(/normalMap\s*(?:\[\s*(\d+)\s*\])?\s*=\s*"([^"]*)"/g)].map(p=>p[2]).find(p=>p&&!/generic_n|null\./i.test(p)),f=p=>{const m=new RegExp(`${p}\\s*\\[\\s*0\\s*\\]\\s*=\\s*"([^"]*)"`).exec(o)?.[1];return m&&!/null\./i.test(m)?{map:m,channel:0}:void 0},h=p=>{const m=new RegExp(`${p}\\s*\\[\\s*0\\s*\\]\\s*=\\s*"([\\d.]+)"`).exec(o)?.[1];return m!==void 0?+m:void 0},u={roughness:f("roughnessMap"),metallic:f("metallicMap"),ao:f("ambientOcclusionMap"),roughnessFactor:h("roughnessFactor"),metallicFactor:h("metallicFactor"),source:"BeamNG"},d=u.roughness||u.metallic||u.ao||u.roughnessFactor!==void 0||u.metallicFactor!==void 0?u:void 0;Rh(e,[s[1],r],{color:a[0]?.path??"",factor:l,...c?{normal:c}:{},...d?{surface:d}:{}})}}async function Dh(t){const e=new Map;for(const n of["json","cs"])for(const s of t)for(const o of s.entries){if(!(n==="json"?/materials\.json$/.test(o.path):/(^|\/)materials\.cs$/.test(o.path)))continue;const a=Wt(await s.read(o));n==="json"?Oh(a,e):Ih(a,e)}return e}const Ph=Object.freeze(Object.defineProperty({__proto__:null,assemble:Ch,collectMaterials:Dh,collectParts:_h,evalValue:Uh,loadVehicle:Ow,placeMesh:Lh,readMaterialsCs:Ih,readMaterialsJson:Oh,slotsOf:Fh},Symbol.toStringTag,{value:"Module"}));function rr(t,e,n,s=!1){const o=new Uint8Array(n),r=new Uint8Array(4096).fill(32);let a=e,i=0,l=4078,c=0,f=0;const h=u=>{o[i++]=u,r[l]=u,l=l+1&4095,f=f+(s?u<<24>>24:u)|0};for(;i<n;){if(c>>=1,!(c&256)){if(a>=t.length)throw new Error("LZSS: обрыв данных");c=t[a++]|65280}if(c&1){h(t[a++]);continue}const u=t[a++],d=t[a++],p=l-(u|(d&240)<<4),m=(d&15)+3;if(i+m>n)throw new Error("LZSS: переполнение");for(let g=0;g<m;g++)h(r[p+g&4095])}if(a+4<=t.length&&(t[a]|t[a+1]<<8|t[a+2]<<16|t[a+3]<<24|0)!==f)throw new Error("LZSS: контрольная сумма не совпала");return{data:o,end:a+4}}const Dw={unit:"units",vehicle:"vehicles",uniform:"uniforms",wearable:"gear",weapon:"weapons",item:"items"},Pw=1449489011,Nw=1131442803;function Nh(t){const e=new DataView(t.buffer,t.byteOffset,t.byteLength),n={},s=[];let o=0;const r=()=>{const a=t.indexOf(0,o);if(a<0)return null;let i="";for(let l=o;l<a;l++)i+=String.fromCharCode(t[l]);return o=a+1,i};for(;;){const a=r();if(a===null||o+20>t.length)return null;const i=e.getUint32(o,!0),l=e.getUint32(o+4,!0),c=e.getUint32(o+16,!0);if(o+=20,!a){if(i===Pw&&!s.length){for(;;){const f=r();if(f===null)return null;if(!f)break;const h=r();if(h===null)return null;n[f.toLowerCase()]=h}continue}break}s.push({name:a,mime:i,orig:l,size:c})}return{props:n,entries:s,dataStart:o}}async function zw(t){let e=65536,n=null;for(;;){const i=await ht(t,0,Math.min(e,t.size));if(n=Nh(i),n||e>=t.size)break;e*=4}if(!n)throw new Error(`${t.name}: повреждённый заголовок PBO`);const s=gt(n.props.prefix??t.name.replace(/\.(pbo|ebo|xbo)$/i,"")).replace(/\/+$/,""),o=[];let r=n.dataStart;for(const i of n.entries)o.push({path:gt(`${s}/${i.name}`),off:r,size:i.size,orig:i.orig||i.size,packed:i.mime===Nw&&i.orig>0&&i.orig!==i.size}),r+=i.size;const a=o.find(i=>i.path===`${s}/config.bin`||i.path==="config.bin");if(a)try{const{parseRap:i,isRap:l}=await Promise.resolve().then(()=>Vw),{listClasses:c}=await Promise.resolve().then(()=>Gw);let f=await ht(t,a.off,a.off+a.size);if(a.packed&&(f=rr(f,0,a.orig).data),l(f))for(const h of c(i(f)))o.push({path:gt(`${s}/config/${Dw[h.kind]}/${h.cls}.armacfg`),off:0,size:0,orig:0,packed:!1,virtual:JSON.stringify({cfg:h.cfg,cls:h.cls,kind:h.kind}),title:h.title})}catch{}return Qt({name:t.name,kind:"pbo",version:1,entries:o,displayName:i=>i.virtual?`${i.title&&i.title.toLowerCase()!==i.path.split("/").pop().replace(/\.armacfg$/,"")?`${i.title} — `:""}${i.path.split("/").pop()}`:i.path.split("/").pop(),async read(i){if(i.virtual)return new TextEncoder().encode(i.virtual);const l=await ht(t,i.off,i.off+i.size);return i.packed?rr(l,0,i.orig).data:l}})}const zh=Object.freeze(Object.defineProperty({__proto__:null,openPbo:zw,parsePboHeader:Nh},Symbol.toStringTag,{value:"Module"})),Bh={65281:{name:"DXT1",dxt:1},65282:{name:"DXT2",dxt:3},65283:{name:"DXT3",dxt:3},65284:{name:"DXT4",dxt:5},65285:{name:"DXT5",dxt:5},17476:{name:"ARGB4444",bpp:2},5461:{name:"ARGB1555",bpp:2},34952:{name:"ARGB8888",bpp:4},32896:{name:"AI88",bpp:2}};function Vh(t){const e=new DataView(t.buffer,t.byteOffset,t.byteLength),n=Bh[e.getUint16(0,!0)];if(!n)throw new Error(`PAA: неизвестный тип 0x${e.getUint16(0,!0).toString(16)}`);let s=2,o;for(;s+12<=t.length&&t[s]===71&&t[s+1]===71&&t[s+2]===65&&t[s+3]===84;){const a=String.fromCharCode(t[s+4],t[s+5],t[s+6],t[s+7]),i=e.getUint32(s+8,!0);a==="CGVA"&&i>=4&&(o=e.getUint32(s+12,!0)),s+=12+i}s+=2+3*e.getUint16(s,!0);const r=[];for(;s+4<=t.length;){let a=e.getUint16(s,!0);const i=e.getUint16(s+2,!0);if(!a&&!i)break;const l=!!(a&32768);a&=32767;const c=t[s+4]|t[s+5]<<8|t[s+6]<<16;r.push({w:a,h:i,off:s+7,size:c,lzo:l}),s+=7+c}return{type:n.name,mips:r,avgColor:o}}function Bw(t,e=2048){const n=new Uint8Array(t),s=Vh(n),o=Bh[new DataView(t).getUint16(0,!0)],r=s.mips.find(h=>Math.max(h.w,h.h)<=e)??s.mips[s.mips.length-1];if(!r)throw new Error("PAA: нет мипмапов");const{w:a,h:i}=r;if(o.dxt){const h=Math.max(1,a+3>>2)*Math.max(1,i+3>>2)*(o.dxt===1?8:16);let u=n.subarray(r.off,r.off+r.size);if(r.lzo){const d=new Uint8Array(h);Ys(n,r.off,d),u=d}return{width:a,height:i,rgba:_e(new DataView(u.buffer,u.byteOffset,u.byteLength),0,a,i,o.dxt)}}const l=a*i*o.bpp,c=rr(n,r.off,l,!0).data,f=new Uint8Array(a*i*4);for(let h=0;h<a*i;h++){let u=0,d=0,p=0,m=255;if(o.bpp===4)p=c[h*4],d=c[h*4+1],u=c[h*4+2],m=c[h*4+3];else{const g=c[h*2]|c[h*2+1]<<8;o.name==="ARGB4444"?(p=(g&15)*17,d=(g>>4&15)*17,u=(g>>8&15)*17,m=(g>>12&15)*17):o.name==="ARGB1555"?(p=(g&31)*255/31,d=(g>>5&31)*255/31,u=(g>>10&31)*255/31,m=g&32768?255:0):(u=d=p=g&255,m=g>>8)}f[h*4]=u,f[h*4+1]=d,f[h*4+2]=p,f[h*4+3]=m}return{width:a,height:i,rgba:f}}const jh=Object.freeze(Object.defineProperty({__proto__:null,decodePaa:Bw,paaInfo:Vh},Symbol.toStringTag,{value:"Module"})),ai=t=>t.length>=4&&t[0]===0&&t[1]===114&&t[2]===97&&t[3]===80;function Gh(t){if(!ai(t))throw new Error("Не rapified-конфиг (нет «\\0raP»)");const e=new DataView(t.buffer,t.byteOffset,t.byteLength),n=new TextDecoder,s=i=>{let l=i;for(;l<t.length&&t[l];)l++;return[n.decode(t.subarray(i,l)),l+1]},o=i=>{let l=0,c=0;for(let f=0;f<5;f++){const h=t[i++];if(l+=(h&127)*2**c,c+=7,h<128)break}return[l,i]},r=i=>{let l;[l,i]=o(i);const c=[];for(let f=0;f<l;f++){const h=t[i++];if(h===0||h===4){let u;[u,i]=s(i),c.push(u)}else if(h===1)c.push(e.getFloat32(i,!0)),i+=4;else if(h===2)c.push(e.getInt32(i,!0)),i+=4;else if(h===3){let u;[u,i]=r(i),c.push(u)}else throw new Error(`rap: неизвестный тип элемента массива ${h}`)}return[c,i]},a=(i,l,c,f)=>{if(f>64)throw new Error("rap: слишком глубокая вложенность");let[h,u]=s(l),d;[d,u]=o(u);const p={name:i,base:h,values:new Map,classes:new Map,externs:new Set,parent:c};for(let m=0;m<d;m++){const g=t[u++];let w;if(g===0){[w,u]=s(u);const y=e.getUint32(u,!0);u+=4,p.classes.set(w.toLowerCase(),a(w,y,p,f+1))}else if(g===1){const y=t[u++];if([w,u]=s(u),y===0||y===4){let M;[M,u]=s(u),p.values.set(w.toLowerCase(),M)}else if(y===1)p.values.set(w.toLowerCase(),e.getFloat32(u,!0)),u+=4;else if(y===2)p.values.set(w.toLowerCase(),e.getInt32(u,!0)),u+=4;else throw new Error(`rap: неизвестный тип значения ${y}`)}else if(g===2||g===5){g===5&&(u+=4),[w,u]=s(u);let y;[y,u]=r(u),p.values.set(w.toLowerCase(),y)}else if(g===3)[w,u]=s(u),p.externs.add(w.toLowerCase());else if(g===4)[w,u]=s(u);else throw new Error(`rap: неизвестный тип записи ${g} (смещение ${u-1})`)}return p};return a("",16,null,0)}function Xh(t,e,n){const s=new Set;for(let o=t;o&&!s.has(o);o=ii(o,n)){s.add(o);const r=o.values.get(e.toLowerCase());if(r!==void 0)return r}}function ii(t,e){if(!t.base)return null;const n=t.base.toLowerCase();for(let s=t.parent;s;s=s.parent){const o=s.classes.get(n);if(o&&o!==t)return o}if(e){const s=[];for(let a=t.parent;a&&a.parent;a=a.parent)s.unshift(a.name.toLowerCase());let o=e;for(const a of s)o=o?.classes.get(a);const r=o?.classes.get(n);if(r&&r!==t)return r}return null}function Hh(t){const e={name:"",base:"",values:new Map,classes:new Map,externs:new Set,parent:null},n=(s,o,r)=>{for(const[a,i]of o.values)s.values.set(a,i);for(const[a,i]of o.classes){const l=s.classes.get(a);l&&r<1?n(l,i,r+1):s.classes.set(a,{...i,values:new Map(i.values),classes:new Map(i.classes),externs:new Set(i.externs),parent:s})}};for(const s of t)n(e,s,0);return e}const Vw=Object.freeze(Object.defineProperty({__proto__:null,isRap:ai,mergeRap:Hh,parseRap:Gh,rapBase:ii,rapGet:Xh},Symbol.toStringTag,{value:"Module"})),li=new WeakMap;async function Wh(t){if(li.has(t))return li.get(t);let e=null;const n=t.entries.find(s=>/(^|\/)config\.bin$/.test(s.path)&&!s.path.includes("/config/"));if(n)try{const s=await t.read(n);ai(s)&&(e=Gh(s))}catch{e=null}return li.set(t,e),e}async function ci(t){const e=[];for(const n of t)if(n.kind==="pbo"){const s=await Wh(n);s&&e.push(s)}return Hh(e)}const Pe=t=>{const e=gt(t).replace(/^\/+/,"");return e&&!/\.p3d$/.test(e)?`${e}.p3d`:e},Kh=t=>gt(t).replace(/^\/+/,""),ee=t=>typeof t=="string"?t:"",ao=t=>Array.isArray(t)?t.flat(4).filter(e=>typeof e=="string"):[],Zt=(t,e,n)=>Xh(t,e,n);function Xn(t,e,n){const s=new Set;for(let o=t;o&&!s.has(o);o=ii(o,n)){s.add(o);const r=o.classes.get(e.toLowerCase());if(r)return r}return null}function jw(t){const e=[],n=(s,o)=>s.values.has(o);for(const s of t.classes.get("cfgvehicles")?.classes.values()??[]){if(!(n(s,"model")||n(s,"hiddenselectionstextures")||n(s,"linkeditems"))||s.values.get("scope")===0)continue;const o=n(s,"uniformclass")||n(s,"linkeditems")||/soldier|man|_f$/i.test(s.base)&&n(s,"hiddenselectionstextures");e.push({cfg:"cfgvehicles",cls:s.name,kind:o?"unit":"vehicle",title:ee(s.values.get("displayname"))||s.name})}for(const s of t.classes.get("cfgweapons")?.classes.values()??[]){const o=s.classes.get("iteminfo");if(!(n(s,"model")||n(s,"hiddenselectionstextures")||o||s.classes.has("linkeditems"))||s.values.get("scope")===0)continue;const r=o?.values.has("uniformclass")?"uniform":o?.values.has("uniformmodel")?"wearable":s.classes.has("weaponslotsinfo")||s.classes.has("linkeditems")||/rifle|pistol|launcher|mgun|smg/i.test(s.base)?"weapon":"item";e.push({cfg:"cfgweapons",cls:s.name,kind:r,title:ee(s.values.get("displayname"))||s.name})}return e}function Hn(t,e){const n=ao(Zt(t,"hiddenselectionstextures",e));let s=ao(Zt(t,"hiddenselections",e));!s.length&&n.length&&(s=["camo","camo1","camo2","camo3","camo4","camo5"].slice(0,n.length));const o=new Map;return s.forEach((r,a)=>{n[a]&&o.set(r.toLowerCase(),n[a])}),o}const Gw=Object.freeze(Object.defineProperty({__proto__:null,archiveConfig:Wh,get:Zt,hiddenTextures:Hn,listClasses:jw,mergedConfig:ci,modelPath:Pe,str:ee,strs:ao,sub:Xn,texPath:Kh},Symbol.toStringTag,{value:"Module"}));let Yh=class{constructor(e,n){this.u8=e,this.v=n,this.dv=new DataView(e.buffer,e.byteOffset,e.byteLength)}u8;v;p=0;dv;u8_(){return this.u8[this.p++]}bool(){return this.u8[this.p++]!==0}u16(){const e=this.dv.getUint16(this.p,!0);return this.p+=2,e}i32(){const e=this.dv.getInt32(this.p,!0);return this.p+=4,e}u32(){const e=this.dv.getUint32(this.p,!0);return this.p+=4,e}f32(){const e=this.dv.getFloat32(this.p,!0);return this.p+=4,e}vec(){return[this.f32(),this.f32(),this.f32()]}skip(e){this.p+=e}str(){const e=this.u8.indexOf(0,this.p),n=e<0?this.u8.length:e;let s="";for(let o=this.p;o<n;o++)s+=String.fromCharCode(this.u8[o]);return this.p=n+1,s}compressed(e){if(e===0)return new Uint8Array(0);if(this.v>=44){if(!(this.v>=64?this.bool():e>=1024)){const a=this.u8.subarray(this.p,this.p+e);return this.p+=e,a}const r=new Uint8Array(e);return this.p=Ys(this.u8,this.p,r),r}if(e<1024){const o=this.u8.subarray(this.p,this.p+e);return this.p+=e,o}const{data:n,end:s}=rr(this.u8,this.p,e);return this.p=s,n}carr(e){const n=this.u32();return{n,data:this.compressed(n*e)}}condensed(e){const n=this.u32();if(this.bool()){const s=this.u8.slice(this.p,this.p+e);return this.p+=e,{n,data:s,fill:!0}}return{n,data:this.compressed(n*e),fill:!1}}indexArray(){const e=this.v>=69?4:2,{n,data:s}=this.carr(e),o=new DataView(s.buffer,s.byteOffset,s.byteLength),r=new Int32Array(n);for(let a=0;a<n;a++)r[a]=e===4?o.getInt32(a*4,!0):o.getUint16(a*2,!0)===65535?-1:o.getUint16(a*2,!0);return r}};function Zh(t){if(t.length<8||String.fromCharCode(t[0],t[1],t[2],t[3])!=="ODOL")throw String.fromCharCode(t[0],t[1],t[2],t[3])==="MLOD"?new Error("MLOD (небинаризованный P3D) пока не поддержан — нужен бинаризованный ODOL из PBO"):new Error("Не ODOL");const e=new DataView(t.buffer,t.byteOffset,t.byteLength).getUint32(4,!0);if(e<28||e>75)throw new Error(`ODOL v${e} не поддержан (поддерживаются 28–75)`);const n=new Yh(t,e);n.p=8,e>=59&&n.u32(),e>=58&&n.str(),e>=74&&n.skip(8);const s=n.u32(),o=[];for(let d=0;d<s;d++)o.push(n.f32());n.i32(),n.f32(),n.f32(),n.i32(),n.i32(),n.i32(),n.vec(),n.u32(),n.u32(),n.f32(),n.vec(),n.vec(),e>=70&&n.f32(),e>=71&&n.f32(),e>=52&&(n.vec(),n.vec());const r=n.vec();n.vec();const a=n.vec();n.skip(36),n.skip(4),e>=73&&n.skip(1),e>=42&&n.skip(16),e>=43&&n.skip(8),e>=33&&n.skip(1),e>=37&&n.skip(5),e>=48&&n.skip(4),n.bool();let i=null;const l=n.str();if(l){e>=23&&n.bool();const d=n.u32(),p=[];for(let m=0;m<d;m++)p.push([n.str(),n.str()]);e>40&&n.str(),i={name:l,bones:p}}n.u8_();{const d=n.u32();n.compressed(d*4)}n.skip(16),e>=72&&n.skip(4),e>=53&&n.skip(1),e>=54&&n.skip(1),n.skip(12),n.skip(4),e>=38&&n.skip(1);const c=n.str();if(n.str(),n.bool(),e>=31&&n.skip(4),e>=57&&n.skip(s*12),e>=30&&n.bool()){const d=n.u32(),p=[];for(let g=0;g<d;g++){const w=n.u32();if(p.push(w),n.str(),n.str(),n.skip(16),e>=56&&n.skip(8),n.skip(4),w<=7)n.skip(8);else if(w===8)n.skip(32);else if(w===9)n.skip(e>=55?8:4);else throw new Error(`ODOL: неизвестный тип анимации ${w}`)}const m=n.u32();for(let g=0;g<m;g++){const w=n.u32();for(let y=0;y<w;y++)n.skip(4*n.u32())}for(let g=0;g<m;g++)for(let w=0;w<d;w++)n.i32()!==-1&&p[w]!==8&&p[w]!==9&&n.skip(24)}const f=[],h=[];for(let d=0;d<s;d++)f.push(n.u32());for(let d=0;d<s;d++)h.push(n.u32());const u=new Map;return{version:e,resolutions:o,skeleton:i,modelClass:c,centerOfMass:a,boundingCenter:r,lod(d){let p=u.get(d);if(!p){if(d<0||d>=s)throw new Error(`ODOL: нет LOD ${d}`);const m=new Yh(t,e);m.p=f[d],p=Xw(m,o[d]),u.set(d,p)}return p}}}function Xw(t,e){const n=t.v,s=[];for(let b=t.u32(),k=0;k<b;k++){const E=t.str(),A=new Float32Array(12);for(let U=0;U<12;U++)A[U]=t.f32();t.i32();const S=t.i32(),$=t.i32(),T=n>=40?t.i32():-1;s.push({model:E,matrix:A,selection:S,bone:$,section:T})}const o=t.u32(),r=new Int32Array(o);for(let b=0;b<o;b++)r[b]=t.i32();for(let b=t.u32(),k=0;k<b;k++)t.skip(4*t.u32());n>=50?t.u32():t.condensed(4),n>=51&&t.f32(),t.skip(8),t.skip(40);const a=[];for(let b=t.u32(),k=0;k<b;k++)a.push(t.str());const i=[];for(let b=t.u32(),k=0;k<b;k++)i.push(Hw(t));t.indexArray(),t.indexArray();const l=t.u32();t.u32(),t.u16();const c=new Array(l),f=n>=69;for(let b=0;b<l;b++){const k=t.u8_(),E=new Int32Array(k);for(let A=0;A<k;A++)E[A]=f?t.i32():t.u16();c[b]=E}const h=[];for(let b=t.u32(),k=0;k<b;k++){const E=t.i32(),A=t.i32();t.i32(),t.i32(),t.u32();const S=t.dv.getInt16(t.p,!0);t.p+=2,t.u32();const $=t.i32(),T=$===-1?t.str():void 0;n>=36?(t.skip(4*t.u32()),n>=67&&t.i32()>=1&&t.skip(44)):t.skip(4),h.push({faceLo:E,faceHi:A,texture:S,material:$,materialName:T})}const u=[];for(let b=t.u32(),k=0;k<b;k++){const E=t.str();t.indexArray(),t.i32();const A=t.bool(),S=t.carr(4),$=new DataView(S.data.buffer,S.data.byteOffset,S.data.byteLength),T=[];for(let _=0;_<S.n;_++)T.push($.getInt32(_*4,!0));const U=t.indexArray();t.compressed(t.u32()),u.push({name:E,sectional:A,sections:T,vertices:U})}const d=[];for(let b=t.u32(),k=0;k<b;k++)d.push([t.str(),t.str()]);for(let b=t.u32(),k=0;k<b;k++)t.f32(),t.skip(12*t.u32());t.skip(12),t.bool(),t.u32(),n>=50&&t.condensed(4);const p=[],m=()=>{let b=0,k=0,E=1,A=1;const S=n>=45;S&&(b=t.f32(),k=t.f32(),E=t.f32(),A=t.f32());const $=t.u32(),T=t.bool(),U=S?4:8,_=T?t.u8.subarray(t.p,t.p+=U):t.compressed($*U),R=new DataView(_.buffer,_.byteOffset,_.byteLength),L=new Float32Array($*2),O=E-b,N=A-k;for(let D=0;D<$;D++){const F=T?0:D*U;S?(L[D*2]=(R.getInt16(F,!0)+32767)*(1/65536)*O+b,L[D*2+1]=(R.getInt16(F+2,!0)+32767)*(1/65536)*N+k):(L[D*2]=R.getFloat32(F,!0),L[D*2+1]=R.getFloat32(F+4,!0))}p.push(L)};m();for(let b=t.u32(),k=1;k<b;k++)m();const g=t.carr(12),w=new DataView(g.data.buffer,g.data.byteOffset,g.data.byteLength),y=new Float32Array(g.n*3);for(let b=0;b<g.n*3;b++)y[b]=w.getFloat32(b*4,!0);const M=new Float32Array(g.n*3);if(n>=45){const b=t.condensed(4),k=new DataView(b.data.buffer,b.data.byteOffset,b.data.byteLength);for(let E=0;E<b.n;E++){const A=k.getInt32(b.fill?0:E*4,!0),S=$=>{let T=A>>$&1023;return T>511&&(T-=1024),T*(-1/511)};M[E*3]=S(0),M[E*3+1]=S(10),M[E*3+2]=S(20)}}else{const b=t.condensed(12),k=new DataView(b.data.buffer,b.data.byteOffset,b.data.byteLength);for(let E=0;E<b.n*3;E++)M[E]=k.getFloat32(b.fill?E%3*4:E*4,!0)}t.carr(n>=45?8:24);const x=t.carr(12),v=new Uint8Array(x.data);return t.carr(32),{resolution:e,textures:a,materials:i,pos:y,nor:M,uv:p[0]??new Float32Array(g.n*2),faces:c,sections:h,selections:u,proxies:s,properties:d,subSkeleton:r,boneRef:v,wide:f}}function Hw(t){const e=t.str(),n=t.u32();t.skip(116),n===3&&t.skip(1),n>=6&&t.str(),n>=4&&t.skip(8);const s=n>6?t.u32():0,o=n>8?t.u32():0,r=[],a=()=>{n>=5&&t.skip(4);const i=t.str();return n>=8&&t.skip(4),n>=11&&t.skip(1),i};if(n<8)for(let i=0;i<s;i++)t.skip(52),r.push(a());else{for(let i=0;i<s;i++)r.push(a());for(let i=0;i<o;i++)t.skip(52)}return n>=10&&a(),{name:e,stages:r}}function fi(t){if(t<1e3)return`визуальный ${t.toFixed(t%1?2:0)}`;if(t<1200)return"вид пилота";if(t<1300)return"вид стрелка";if(t>=1e4&&t<2e4)return"тень";const e=n=>Math.abs(t-n)<n*.01;return e(1e13)?"геометрия":e(1e15)?"память":e(2e15)?"land contact":e(3e15)?"roadway":e(4e15)?"пути":e(5e15)?"hitpoints":e(6e15)?"геометрия обзора":e(7e15)?"геометрия огня":`служебный ${t.toExponential(1)}`}function ui(t){const e=/^#\(\w+,\d+,\d+,\d+\)color\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)/i.exec(t);return e?[+e[1],+e[2],+e[3]]:null}function Ww(t){if(!t)return;const e=/color\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)/i.exec(t);return t.startsWith("#")?e?{specularFactor:+e[2],roughnessFactor:1-+e[3],source:"Arma smdi (цвет)"}:void 0:{specular:{map:t,channel:1},gloss:{map:t,channel:2},source:"Arma _smdi"}}function qh(t,e){const n=t.resolutions.findIndex(o=>o<1e3);if(n>=0)return n;const s=t.resolutions.findIndex(o=>o<1300);return s>=0?s:0}function Qh(t,e="",n,s=[0,0,0]){const o=[],r=t.wide,a=new Int32Array(t.faces.length+1),i=r?16:8,l=r?20:10;for(let u=0;u<t.faces.length;u++)a[u+1]=a[u]+(t.faces[u].length===4?l:i);const c=u=>t.selections.filter(d=>d.sectional&&d.sections.includes(u)).map(d=>d.name),f=u=>c(u).filter(d=>!/^proxy:/i.test(d)),h=new Map;for(const u of t.proxies)h.set(u.section,(h.get(u.section)??0)+1);return t.sections.forEach((u,d)=>{const p=c(d);if(p.some(R=>/^proxy:/i.test(R))||p.some(R=>/^zasleh/i.test(R)))return;const m=[],g=h.get(d)??0;if(g){let R=0;for(let L=0;L<t.faces.length&&a[L]<u.faceHi;L++)a[L]>=u.faceLo&&R++;if(R<=g)return}for(let R=0;R<t.faces.length;R++){if(a[R]<u.faceLo)continue;if(a[R]>=u.faceHi)break;const L=t.faces[R];m.push(L[0],L[1],L[2]),L.length===4&&m.push(L[0],L[2],L[3])}if(!m.length)return;const w=new Map,y=[],M=new Uint32Array(m.length);m.forEach((R,L)=>{let O=w.get(R);O===void 0&&(O=y.length,w.set(R,O),y.push(R)),M[L]=O});const x=y.length,v=new Float32Array(x*3),b=new Float32Array(x*3),k=new Float32Array(x*2);y.forEach((R,L)=>{let O=t.pos[R*3]+s[0],N=t.pos[R*3+1]+s[1],D=t.pos[R*3+2]+s[2],F=t.nor[R*3],C=t.nor[R*3+1],P=t.nor[R*3+2];if(n){const I=n;[O,N,D]=[I[0]*O+I[3]*N+I[6]*D+I[9],I[1]*O+I[4]*N+I[7]*D+I[10],I[2]*O+I[5]*N+I[8]*D+I[11]],[F,C,P]=[I[0]*F+I[3]*C+I[6]*P,I[1]*F+I[4]*C+I[7]*P,I[2]*F+I[5]*C+I[8]*P]}v[L*3]=O,v[L*3+1]=N,v[L*3+2]=D,b[L*3]=F,b[L*3+1]=C,b[L*3+2]=P,k[L*2]=t.uv[R*2],k[L*2+1]=t.uv[R*2+1]});const E=u.texture>=0?t.textures[u.texture]??"":"",A=u.material>=0?t.materials[u.material]?.name??"":u.materialName??"",S=u.material>=0?t.materials[u.material]?.stages.find(R=>/_smdi\.pa[ac]$|,smdi\)$/i.test(R)):void 0,$=u.material>=0?t.materials[u.material]?.stages.find(R=>/_no(hq|fhq|fex|px)?\.pa[ac]$|_nohq/i.test(R)):void 0,T=f(d),U={name:`${e}${T[0]??`секция ${d}`}`,texture:ui(E)?"":E,material:A,normalMap:$&&!/^#/.test($)?$:void 0,surface:Ww(S),vertexCount:x,pos:v,nor:b,uv:k,bone:new Float32Array(x*4),weight:new Float32Array(x*4),indices:M,skinned:!1},_=ui(E);_?U.color=_:/characters_f\\heads\\|\\hl_\w*_co\.paa$/i.test(E)&&(U.color=[.78,.6,.5]),U.arma={selections:T,section:d,texture:E,material:A},o.push(U)}),o}const Kw=t=>t.arma;function Yw(t,e,n){const s=qh(t),o=t.lod(s),r=Qh(o,"",void 0,t.boundingCenter);if(!r.length)throw new Error(`В LOD ${s} (${fi(t.resolutions[s])}) нет граней`);const a=t.resolutions.map((l,c)=>`${c===s?"▶":" "}${c}: ${fi(l)}`).join(", "),i=[`Arma P3D (ODOL v${t.version}): ${e}, LOD ${s} из ${t.resolutions.length}, мешей ${r.length}, вершин ${le(r)}, треугольников ${Ut(r)}`,`LOD: ${a}`];return t.skeleton&&i.push(`Скелет: ${t.skeleton.name} (${t.skeleton.bones.length} костей)`),o.proxies.length&&i.push(`Прокси: ${[...new Set(o.proxies.map(l=>l.model))].slice(0,8).join(", ")}${o.proxies.length>8?"…":""}`),{model:{meshes:r,skeleton:null,motions:[],bounds:dt(r),flipZ:!0,info:i,meta:{arma:{lodIndex:s}}},lod:o,lodIndex:s}}async function Zw(t,e){const n=await t.readPath(e);return n?Zh(n):null}function Jh(t,e){let n=0;for(const s of t){const o=Kw(s);if(!o)continue;const r=o.selections.map(i=>e.get(i.toLowerCase())).find(i=>i!==void 0);if(!r)continue;const a=ui(r);a?(s.texture="",s.color=a):(s.texture=Kh(r),s.color=void 0),n++}return n}function qw(t,e){const n=[];for(const s of["cfgvehicles","cfgweapons"])for(const o of t.classes.get(s)?.classes.values()??[]){const r=Pe(ee(Zt(o,"model",t))),a=Pe(ee(Xn(o,"iteminfo",t)?.values.get("uniformmodel")));(r===e||a===e)&&o.values.has("hiddenselectionstextures")&&n.push(o)}return n}async function Qw(t,e){const n=Zh(new Uint8Array(await t.arrayBuffer())),{model:s}=Yw(n,t.name);if(t.path&&e.archives.some(o=>o.kind==="pbo")){const o=await ci(e.archives),r=qw(o,t.path);r.length&&(Jh(s.meshes,Hn(r[0],o)),s.info.push(`Текстуры из класса ${r[0].name}${r.length>1?` (вариантов: ${r.length}; остальные — в папке config/ архивов)`:""}`))}return s}function Jw(t,e,n,s){const o={v:t.classes.get("cfgvehicles"),w:t.classes.get("cfgweapons")},r=(e==="cfgvehicles"?o.v:o.w)?.classes.get(n.toLowerCase());if(!r)throw new Error(`Класс ${n} не найден в ${e}`);const a=[],i=(d,p)=>{const m=Xn(d,"iteminfo",t),g=ee(m?.values.get("uniformmodel")??(m?Zt(m,"uniformmodel",t):void 0));if(m&&ee(Zt(m,"uniformclass",t))){const w=o.v?.classes.get(ee(Zt(m,"uniformclass",t)).toLowerCase());return w?(a.push({title:`${p}${w.name}`,path:Pe(ee(Zt(w,"model",t))),tex:Hn(w,t),from:w.name}),!0):(s.push(`Форма ${d.name}: класс ${ee(Zt(m,"uniformclass",t))} не найден (из игры?)`),!1)}return g&&!/empty/i.test(g)?(a.push({title:`${p}${d.name}`,path:Pe(g),tex:Hn(d,t),from:d.name}),!0):!1};if(e==="cfgvehicles"){a.push({title:r.name,path:Pe(ee(Zt(r,"model",t))),tex:Hn(r,t),from:r.name});for(const d of ao(Zt(r,"linkeditems",t))){const p=o.w?.classes.get(d.toLowerCase());if(!p){/^item(map|compass|watch|radio|gps)$/i.test(d)||s.push(`Нет класса снаряжения ${d} (из игры?)`);continue}i(p,"")}return a}if(i(r,""))return a;a.push({title:r.name,path:Pe(ee(Zt(r,"model",t))),tex:Hn(r,t),from:r.name});const l=Xn(r,"linkeditems",t),c=Xn(r,"weaponslotsinfo",t),f=d=>c&&d?ee(Zt(Xn(c,d,t)??c,"linkproxy",t)):"",h=new Set,u=(d,p,m)=>{const g=o.w?.classes.get(p.toLowerCase());if(!g){s.push(`Обвес ${p} (${d}): класс не найден (из игры или другого мода)`);return}h.add(d.toLowerCase()),a.push({title:`${d}: ${g.name}${m}`,path:Pe(ee(Zt(g,"model",t))),tex:Hn(g,t),from:g.name,proxy:f(d)})};if(l)for(const d of l.classes.values())u(ee(Zt(d,"slot",t)),ee(Zt(d,"item",t)),"");if(c){const d=[];for(const p of c.classes.values()){if(h.has(p.name.toLowerCase())||!ee(Zt(p,"linkproxy",t)))continue;const m=Zt(p,"compatibleitems",t),g=Xn(p,"compatibleitems",t),y=[...ao(m),...g?[...g.values.keys()].filter(M=>g.values.get(M)===1):[]].find(M=>o.w?.classes.has(M.toLowerCase()));y?u(p.name,y," (совместимый)"):d.push(p.name)}d.length&&s.push(`Слоты без обвеса: ${d.join(", ")} — откройте вместе PBO мода с прицелами/глушителями, и совместимые предметы встанут сами`)}return a}async function ty(t,e){const n=JSON.parse(Wt(await t.arrayBuffer())),s=await ci(e.archives),o=[],r=Jw(s,n.cfg,n.cls,o),a=[];let i=null,l=0,c="";for(const u of r){if(!u.path){o.push(`${u.title}: модель не задана`);continue}let d;try{d=await Zw(e,u.path)}catch(y){o.push(`${u.title}: ${y.message}`);continue}if(!d){o.push(`${u.title}: нет файла ${u.path} (модель из игры или другого мода)`);continue}const p=qh(d),m=d.lod(p);let g;if(u.proxy&&i){const y=Pe(u.proxy).replace(/\.p3d$/,""),M=i.o.lod(i.li),x=M.proxies.find(v=>Pe(v.model).replace(/\.p3d$/,"")===y)??M.proxies.find(v=>Pe(v.model).split("/").pop().replace(/\.p3d$/,"")===y.split("/").pop());if(x){g=x.matrix.slice();const v=i.o.boundingCenter;g[9]+=v[0],g[10]+=v[1],g[11]+=v[2]}else o.push(`${u.title}: в модели нет прокси ${u.proxy}`)}const w=Qh(m,r.length>1?`${u.from??u.title} · `:"",g,d.boundingCenter);Jh(w,u.tex),a.push(...w),l++,i||(i={o:d,li:p}),d.skeleton&&!c&&(c=d.skeleton.name)}if(!a.length)throw new Error(`Класс ${n.cls}: ни одной модели не загружено. ${o.join("; ")}`);const f=`Arma: ${n.cls} (${n.kind}), моделей ${l}/${r.length}, мешей ${a.length}, вершин ${le(a)}, треугольников ${Ut(a)}`,h=i?`LOD основной модели: ${i.li} (${fi(i.o.resolutions[i.li])})`:"";return{meshes:a,skeleton:null,motions:[],bounds:dt(a),flipZ:!0,info:[f,...r.map(u=>`• ${u.title}: ${u.path}`),h,...c?[`Скелет: ${c}`]:[],...o].filter(Boolean),meta:{arma:{cls:n.cls}}}}const ar=Object.freeze(Object.defineProperty({__proto__:null,loadClass:ty,loadP3d:Qw},Symbol.toStringTag,{value:"Module"})),ey=new Set(["diffusesampler","texturesampler","diffusesampler2","platebgsampler"].map(Hc)),ny=new Set(["bumpsampler","normalsampler","bumpsampler2"].map(Hc)),sy=[0,4,4,8,0,8,12,16,4,4,4,0,0,0,0,0];function oy(t,e){if(!e)return{shaders:[],texDict:0};const n=t.ptr(e+8),s=[];for(const o of t.ptrList(e+16)){if(!o){s.push({name:0,diffuse:"",normal:"",textures:[]});continue}const r=t.ptr(o),a=t.u8at(o+16),i=t.u32(o+8),l=[];let c="",f="",h=0;for(let d=0;d<a;d++)t.u8at(r+d*16)&&(h+=16*t.u8at(r+d*16));const u=r+a*16+h;for(let d=0;d<a;d++){if(t.u8at(r+d*16)!==0)continue;const m=t.ptr(r+d*16+8);if(!m)continue;const g=t.str(t.ptr(m+40));if(!g)continue;l.push(g);const w=t.u32(u+d*4);!c&&ey.has(w)&&(c=g),!f&&ny.has(w)&&(f=g)}s.push({name:i,diffuse:c||l[0]||"",normal:f,textures:l})}return{shaders:s,texDict:n}}function ry(t,e){const n=t.ptr(e+24),s=t.ptr(e+56);if(!n||!s)return null;const o=t.u16(n+8),r=t.ptr(n+16)||t.ptr(n+32),a=t.u32(n+24),i=t.ptr(n+48);if(!r||!i||!a)return null;const l=t.u32(i),c=t.u32(i+8),f=t.u32(i+12),h=b=>b<8?c>>>b*4&15:f>>>(b-8)*4&15,u=new Array(16).fill(-1),d=new Array(16).fill(0);let p=0;for(let b=0;b<16;b++)l>>>b&1&&(u[b]=p,d[b]=h(b),p+=sy[d[b]]);const m=new Float32Array(a*3),g=new Float32Array(a*3),w=new Float32Array(a*2),y=(b,k,E,A,S)=>{if(k===6||k===7||k===5||k===2)for(let $=0;$<S;$++)E[A+$]=$<(k===2?1:k===5?2:3)?t.f32(b+$*4):0;else if(k===1||k===3)for(let $=0;$<S;$++)E[A+$]=$<(k===1?2:4)?Fe(t.u16(b+$*2)):0;else if(k===10)for(let $=0;$<S;$++)E[A+$]=(t.u8at(b+$)<<24>>24)/127;else if(k===9||k===8)for(let $=0;$<S;$++)E[A+$]=t.u8at(b+$)/127.5-1};for(let b=0;b<a;b++){const k=r+b*o;u[0]>=0&&y(k+u[0],d[0],m,b*3,3),u[3]>=0&&y(k+u[3],d[3],g,b*3,3),u[6]>=0&&y(k+u[6],d[6],w,b*2,2)}const M=t.u32(s+8),x=t.ptr(s+16),v=new Uint32Array(M);for(let b=0;b<M;b++)v[b]=t.u16(x+b*2);return{pos:m,nor:g,uv:w,idx:v}}function ir(t,e,n="",s=0){const{shaders:o,texDict:r}=oy(t,t.ptr(e+16)),a=[80,88,96,104].map(h=>t.ptr(e+h));let i=a[s]||a.find(h=>h)||0;const l=[],c=i?t.ptrList(i):[];c.forEach((h,u)=>{const d=t.ptrList(h+8),p=t.ptr(h+32);d.forEach((m,g)=>{if(!m)return;const w=ry(t,m);if(!w||!w.idx.length)return;const y=p?t.u16(p+g*2):0,M=o[y],x=w.pos.length/3;l.push({name:`${n}${c.length>1?`m${u}·`:""}${g}`,texture:M?.diffuse??"",normalMap:M?.normal||void 0,vertexCount:x,pos:w.pos,nor:w.nor,uv:w.uv,bone:new Float32Array(x*4),weight:new Float32Array(x*4),indices:w.idx,skinned:!1,materialPaths:M?.textures})}),i=0});const f=t.ptr(e+24);return{name:"",meshes:l,shaders:o,embedded:r,bones:f?t.u16(f+94):0}}function td(t){const e=t.ptr(32),n=t.u16(40),s=t.ptr(48),o=t.u16(56),r=[];for(let a=0;a<o;a++){const i=t.ptr(s+a*8);i&&r.push({hash:a<n&&e?t.u32(e+a*4):0,at:i})}return r}function ay(t){const e=t.ptr(48),n=t.ptr(56),s=t.u32(72),o=[];e&&o.push(e);for(let r=0;r<s&&n;r++){const a=t.ptr(n+r*8);a&&o.push(a)}return o}const ks=t=>t.charCodeAt(0)|t.charCodeAt(1)<<8|t.charCodeAt(2)<<16|t.charCodeAt(3)<<24,hi=ks("DXT1"),di=ks("DXT3"),ed=ks("DXT5"),nd=ks("ATI1"),pi=ks("ATI2"),sd=ks("BC7 "),od=t=>t>65535?String.fromCharCode(t&255,t>>8&255,t>>16&255,t>>>24).trim():`D3DFMT ${t}`;function iy(t,e,n=2048){const s=t.str(t.ptr(e+40));let o=t.u16(e+80),r=t.u16(e+82);const a=t.u32(e+88),i=Math.max(1,t.u8at(e+93));let l=t.ptr(e+112);if(!l)throw new Error(`${s}: нет данных`);const c=a===hi||a===nd?8:a===di||a===ed||a===pi||a===sd?16:0,f=a===21||a===22||a===32?4:a===50||a===28?1:0;if(!c&&!f)throw new Error(`${s}: формат ${od(a)} не поддержан`);const h=(d,p)=>c?Math.max(1,d+3>>2)*Math.max(1,p+3>>2)*c:d*p*f;for(let d=1;d<i&&Math.max(o,r)>n;d++)l+=h(o,r),o=Math.max(1,o>>1),r=Math.max(1,r>>1);if(l+h(o,r)>t.u8.length)throw new Error(`${s}: данные за пределами ресурса`);let u;if(a===hi||a===di||a===ed)u=_e(new DataView(t.u8.buffer,t.u8.byteOffset+l),0,o,r,a===hi?1:a===di?3:5);else if(a===nd||a===pi)u=Oo(t.u8,l,o,r,a===pi);else if(a===sd)u=Io(t.u8,l,o,r);else{u=new Uint8Array(o*r*4);for(let d=0;d<o*r;d++)if(f===4){const p=t.u8[l+d*4],m=t.u8[l+d*4+1],g=t.u8[l+d*4+2],w=t.u8[l+d*4+3];a===32?(u[d*4]=p,u[d*4+1]=m,u[d*4+2]=g):(u[d*4]=g,u[d*4+1]=m,u[d*4+2]=p),u[d*4+3]=a===22?255:w}else{const p=t.u8[l+d];a===28?(u[d*4]=u[d*4+1]=u[d*4+2]=255,u[d*4+3]=p):(u[d*4]=u[d*4+1]=u[d*4+2]=p,u[d*4+3]=255)}}return{name:s,width:o,height:r,rgba:u,format:od(a)}}function rd(t,e=0){const n=[],s=[];for(const o of t.ptrList(e+48))if(o)try{n.push(iy(t,o))}catch(r){s.push(r.message)}return{textures:n,errors:s}}function lr(t,e,n){if(e.embedded)for(const s of rd(t,e.embedded).textures)n.set(s.name.toLowerCase(),s);for(const s of e.meshes){const o=n.get(s.texture.toLowerCase());o&&(s.image=o);const r=s.normalMap?n.get(s.normalMap.toLowerCase()):void 0;r&&(s.normalImage=r)}}function mi(t,e){if(!t.length)throw new Error("В ресурсе нет геометрии (LOD High пуст)");return e[0]+=`, мешей ${t.length}, вершин ${le(t)}, треугольников ${Ut(t)}`,{meshes:t,skeleton:null,motions:[],bounds:dt(t),defaultZUp:!0,info:e}}async function ly(t){const e=await js(await t.arrayBuffer()),n=ir(e,0);return lr(e,n,new Map),mi(n.meshes,[`GTA V drawable ${t.name} (RSC7 v${e.version})`,...n.bones?[`Костей: ${n.bones}`]:[]])}async function cy(t){const e=await js(await t.arrayBuffer()),n=new Map,s=[],o=td(e);for(const[r,a]of o.entries()){const i=ir(e,a.at,o.length>1?`${r}·`:"");lr(e,i,n),s.push(...i.meshes)}return mi(s,[`GTA V словарь drawable ${t.name} (RSC7 v${e.version}), drawable: ${o.length}`])}const fy=["head","berd","hair","uppr","lowr","hand","feet","teef","accs","task","decl","jbib"];function uy(t){if(!t.archive||!t.path)return[];const e=t.path.replace(/[^/]*$/,""),n=[];for(const s of t.archive.entries)s.path.startsWith(e)&&n.push([t.archive,s]);return n}async function hy(t,e){const n=await js(await t.arrayBuffer()),s=new Map,o=[],r=ay(n);for(const[i,l]of r.entries()){const c=ir(n,l,r.length>1?`${i}·`:"");if(lr(n,c,s),o.push(...c.meshes),i===0&&r.length>1)break}const a=[`GTA V фрагмент ${t.name} (RSC7 v${n.version})`];if(le(o)<64){o.length=0;const i=t.name.replace(/\.yft$/i,"").toLowerCase();let l=uy(t).filter(([,h])=>/\.ydd$/.test(h.path));const c=e.archives.flatMap(h=>h.entries.filter(u=>u.path.includes(`/${i}/`)&&/\.ydd$/.test(u.path)).map(u=>[h,u]));l.length||(l=c);const f=[];for(const h of fy){const u=l.find(([,p])=>new RegExp(`(^|/)${h}_000_[a-z]\\.ydd$`).test(p.path));if(!u)continue;const d=await js(await u[0].fileOf(u[1]).arrayBuffer());for(const p of td(d)){const m=ir(d,p.at,`${h}·`);lr(d,m,s),o.push(...m.meshes.filter(g=>g.vertexCount>3))}f.push(h)}f.length&&a.push(`Персонаж: компоненты ${f.join(", ")} (варианты 000). Остальные варианты — отдельными .ydd`)}return mi(o,a)}async function dy(t){const e=await js(t);return rd(e,0)}const io=Object.freeze(Object.defineProperty({__proto__:null,loadYdd:cy,loadYdr:ly,loadYft:hy,parseYtd:dy},Symbol.toStringTag,{value:"Module"}));function py(t,e=2048){const n=new Uint8Array(t),s=new DataView(t),o=new Map;for(let M=0;M+6<=n.length;){const x=s.getUint16(M,!0),v=s.getUint32(M+2,!0);if(v<=M||v>n.length+16||(o.set(x,[M+6,Math.min(v,n.length)]),x===255))break;M=v}const r=o.get(258),a=o.get(255);if(!o.has(240)||!r||!a)throw new Error("PCT: нет заголовка или данных");const i=s.getUint32(r[0],!0),l=s.getUint32(r[0]+4,!0),c=o.has(242)?s.getUint32(o.get(242)[0],!0):0,f=o.has(249)?s.getUint32(o.get(249)[0],!0):1,h=o.get(263),u=[];if(h)for(let M=h[0];M+8<=h[1]&&u.length<f;M+=8)u.push([s.getUint32(M,!0),s.getUint32(M+4,!0)]);u.length||u.push([0,a[1]-a[0]]);const d=Array.from({length:f},(M,x)=>Math.max(1,i>>x)*Math.max(1,l>>x)).reduce((M,x)=>M+x,0),p=(a[1]-a[0])/d;let m=0;for(;m+1<u.length&&Math.max(i>>m,l>>m)>e;)m++;const g=Math.max(1,i>>m),w=Math.max(1,l>>m),y=a[0]+u[m][0];if(p>.75&&p<1.25)return{width:g,height:w,rgba:Io(n,y,g,w)};if(p>.4&&p<.6)return{width:g,height:w,rgba:_e(new DataView(t,y),0,g,w,1)};throw new Error(`PCT: формат 0x${c.toString(16)} (${p.toFixed(2)} байт/пиксель) не поддержан`)}const my=Object.freeze(Object.defineProperty({__proto__:null,decodePct:py},Symbol.toStringTag,{value:"Module"}));function gi(t){const e={tag:"#root",attrs:{},children:[],parent:null};let n=e,s=0;for(;s<t.length;){const o=t.indexOf("<",s);if(o<0)break;if(t.startsWith("<!--",o)){const u=t.indexOf("-->",o+4);s=u<0?t.length:u+3;continue}if(t[o+1]==="?"||t[o+1]==="!"){const u=t.indexOf(">",o);s=u<0?t.length:u+1;continue}let r=o+1,a="";for(;r<t.length;r++){const u=t[r];if(a)u===a&&(a="");else if(u==='"'||u==="'")a=u;else if(u===">")break}const i=t.slice(o+1,r);if(s=r+1,i.startsWith("/")){n.parent&&(n=n.parent);continue}const l=i.endsWith("/"),c=l?i.slice(0,-1):i,f=/^\s*([\w.:-]+)/.exec(c);if(!f)continue;const h={tag:f[1],attrs:{},children:[],parent:n};for(const u of c.slice(f[0].length).matchAll(/([\w.:-]+)\s*=\s*("([^"]*)"|'([^']*)')/g))h.attrs[u[1]]=u[3]??u[4]??"";n.children.push(h),l||(n=h)}return e}const Re=(t,e)=>{for(const n of t.children){if(n.tag===e)return n;const s=Re(n,e);if(s)return s}},cr=(t,e,n=[])=>{for(const s of t.children)s.tag===e&&n.push(s),cr(s,e,n);return n};function ad(t,e){const n=t.attrs._template;if(!n)return t.attrs;const s=cr(e,"_templates");for(const o of s){const r=Re(o,n);if(r)return{...ad(r,e),...t.attrs}}return t.attrs}const id=t=>{const e=(t??"").replace(/[()]/g,"").split(/[;,]/).map(n=>parseFloat(n));return[e[0]||0,e[1]||0,e[2]||0]},Es=(t,e=0)=>{const n=parseFloat(t??"");return Number.isFinite(n)?n:e},ld=t=>t.shading?{metallic:{map:As(t.shading),channel:0},roughness:{map:As(t.shading),channel:1},ao:{map:As(t.shading),channel:2},source:"SnowRunner ShadingMap"}:t.specular?{specular:{map:As(t.specular),channel:0},source:"MudRunner SpecularMap"}:void 0,As=t=>t.replace(/\\/g,"/").replace(/\.[^./]+$/,"").replace(/\//g,"_"),cd=(t,e)=>t.materials.find(n=>n.parts.includes(e.name.toLowerCase()))??(e.material?t.materials.find(n=>n.name===e.material):void 0)??(t.materials.length===1?t.materials[0]:void 0);async function gy(t,e){const n=Yc(await t.arrayBuffer()),s=[];for(const r of n.nodes){if(!r.mesh)continue;r.skinned||Zc(r.mesh,r.world);const a=cd(n,r);if(/^off\./i.test(a?.diffuse??""))continue;r.mesh.texture=a?.diffuse??"",a?.normal&&(r.mesh.normalMap=As(a.normal));const i=a&&ld(a);i&&(r.mesh.surface=i),r.mesh.material=a?.name,s.push(r.mesh)}if(!s.length)throw new Error("В MeshCache нет видимых мешей (только коллизии/кости)");const o=[`${n.snow?"SnowRunner":"MudRunner"} MeshCache ${t.name}: узлов ${n.nodes.length}, мешей ${s.length}, вершин ${le(s)}, треугольников ${Ut(s)}`,`Материалы: ${n.materials.map(r=>`${r.name||"—"} → ${r.diffuse}`).join("; ")}`];return n.skipped&&o.push(`Пропущено служебных данных (коллизии): ${n.skipped} байт`),{meshes:s,skeleton:null,motions:[],bounds:dt(s),flipZ:!0,info:o}}async function wy(t,e){if(!t.texture)return null;const n=t.texture.toLowerCase().replace(/\\/g,"/").replace(/\.[^./]+$/,"").replace(/\//g,"_"),s=Ge(t.texture).toLowerCase().replace(/\.[^.]+$/,"");for(const o of[`texturecache/${n}`,`media/texturecache/${n}`,`_t/${n}`,`prebuild/textures/pct/${n}`,`pct/${n}`,`colors/default/${n}`,`colors/default/${s}`,n,s]){const r=e.find(o);if(r)return r}return null}function fr(t,e){for(const n of t)for(const s of n.entries)if(e(s.path))return[n,s];return null}function ur(t,e){const n=e.toLowerCase().replace(/\\/g,"/").replace(/\//g,"_");return fr(t,s=>s.endsWith(`/meshcache/${n}`)||s===`meshcache/${n}`||s.endsWith(`/_m/${n}`)||s.endsWith(`prebuild/meshes/${n}`))??fr(t,s=>new RegExp(`(^|/)_m/${n.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}_[a-z0-9]{6,10}$`).test(s))}async function hr(t){const e=Yc(await t[0].read(t[1]).then(s=>s.slice().buffer)),n=[];for(const s of e.nodes){if(!s.mesh)continue;s.skinned||Zc(s.mesh,s.world);const o=cd(e,s);if(/^off\./i.test(o?.diffuse??""))continue;s.mesh.texture=o?.diffuse??"",o?.normal&&(s.mesh.normalMap=As(o.normal));const r=o&&ld(o);r&&(s.mesh.surface=r),n.push(s.mesh)}return{f:e,meshes:n}}function wi(t,e,n,s=!1){for(const o of t){o.name=`${e}${o.name}`;for(let r=0;r<o.vertexCount;r++)s&&(o.pos[r*3]=-o.pos[r*3],o.pos[r*3+2]=-o.pos[r*3+2],o.nor[r*3]=-o.nor[r*3],o.nor[r*3+2]=-o.nor[r*3+2]),o.pos[r*3]+=n[0],o.pos[r*3+1]+=n[1],o.pos[r*3+2]+=n[2]}}async function yy(t,e){const n=gi(Wt(await t.arrayBuffer())),s=[...e.archives];t.archive&&!s.includes(t.archive)&&s.push(t.archive);const o=Re(n,"Truck"),r=Re(n,"TruckAddon"),a=[],i=(o&&Re(o,"PhysicsModel")?.attrs.Mesh)??(r&&(Re(r,"PhysicsModel")?.attrs.Mesh??Re(r,"Model")?.attrs.Mesh));if(!i)throw new Error("В XML нет <Truck>/<TruckAddon> с PhysicsModel Mesh=…");const l=ur(s,i);if(!l)throw new Error(`Нет меша ${i} в MeshCache/_m (откройте папку мода целиком)`);const c=await hr(l),f=[...c.meshes];let h=0,u=0;if(o){for(const w of cr(Re(o,"PhysicsModel")??o,"Model")){const y=w.attrs.Mesh?ur(s,w.attrs.Mesh):null;if(!y){w.attrs.Mesh&&a.push(`Нет меша ${w.attrs.Mesh}`);continue}const M=id(w.attrs.Org),x=await hr(y);wi(x.meshes,`${w.attrs.Mesh.split("/").pop()} · `,M),f.push(...x.meshes),u++}const p=Re(o,"Wheels"),m=Re(o,"WheelsSet"),g=m&&Re(m,"WheelType");if(p){const w=Es(p.attrs.HardpointDefaultY),y=Es(p.attrs.HardpointDefaultZ),M=p.children.filter(b=>b.tag==="Wheel").map(b=>ad(b,n)),x=b=>g?.attrs.Type??b.DefaultWheelType??"",v=new Map;for(const[b,k]of M.entries()){if(k.Pos){const L=(k.WheelType??p.attrs.DefaultWheelType??"").toLowerCase();if(!v.has(L)){const P=fr(s,I=>I.endsWith(`classes/wheels/${L}.xml`));v.set(L,P?gi(Wt(await P[0].read(P[1]))):null)}const O=v.get(L),N=id(k.Pos),D=N[2]<0,F=(P,I)=>(O?cr(O,P).find(B=>!I||B.attrs.Name===I):void 0)?.attrs.Mesh;let C=!1;for(const P of[F("TruckTire",k.Tire??p.attrs.DefaultTire),F("TruckRim",k.Rim??p.attrs.DefaultRim)]){const I=P?ur(s,P):null;if(!I)continue;const B=await hr(I);wi(B.meshes,`колесо ${b} · `,N,D),f.push(...B.meshes),C=!0}C?h++:b===0&&a.push(`Нет шины/диска для колёс типа ${L}`);continue}if(g?.attrs.Wheels&&!g.attrs.Wheels.split(",").map(L=>+L).includes(b))continue;const E=x(k).toLowerCase();if(!v.has(E)){const L=fr(s,O=>O.endsWith(`classes/wheels/${E}.xml`));v.set(E,L?gi(Wt(await L[0].read(L[1]))):null)}const A=v.get(E),S=A&&Re(A,"TruckWheel"),$=S?.attrs.Mesh?ur(s,S.attrs.Mesh):null;if(!$){b===0&&a.push(`Нет колеса ${E} (классы wheels/ или меш)`);continue}const T=k.RightSide==="true",U=w+Es(k.HardpointOffsetY)+Es(g?.attrs.HardpointOffsetY),_=y+Es(g?.attrs.HardpointOffsetZ),R=await hr($);wi(R.meshes,`колесо ${b} · `,[Es(k.HardpointX),U,T?-_:_],T),f.push(...R.meshes),h++}}}const d=`${c.f.snow?"SnowRunner":"MudRunner"}: ${t.name.replace(/\.xml$/i,"")} — меш ${i}, колёс ${h}, навесных моделей ${u}, мешей ${f.length}, вершин ${le(f)}, треугольников ${Ut(f)}`;return{meshes:f,skeleton:null,motions:[],bounds:dt(f),flipZ:!0,info:[d,...a]}}const dr=Object.freeze(Object.defineProperty({__proto__:null,loadMeshCache:gy,loadTruck:yy,resolveSpintiresTexture:wy},Symbol.toStringTag,{value:"Module"})),yi=(t,e,n)=>Math.max(1,Math.ceil(t/4))*Math.max(1,Math.ceil(e/4))*n,by=(t,e)=>{let n=0;for(let s=t;s>=1;s>>=1)n+=yi(s,s,e);return n};function vy(t,e){for(const[n,s]of[["DXT1",8],["DXT5",16]]){if(e===by(t,s))return{fourcc:n,mips:Math.log2(t)+1};if(e===yi(t,t,s))return{fourcc:n,mips:1}}return null}function fd(t){const e=[...t].sort((f,h)=>h.side-f.side),n=[];let s=null,o=0;for(const f of e){const h=vy(f.side,f.data.length);if(!(!h||s&&h.fourcc!==s)&&!(n.length&&f.side*2!==n[n.length-1].side)&&(s=h.fourcc,n.push(f),o+=h.mips,h.mips>1))break}if(!s||!n.length)throw new Error("4A: неизвестный формат текстуры");const r=n[0].side,a=n.reduce((f,h)=>f+h.data.length,0),i=new Uint8Array(128+a),l=new DataView(i.buffer);l.setUint32(0,542327876,!0),l.setUint32(4,124,!0),l.setUint32(8,528391|(o>1?131072:0),!0),l.setUint32(12,r,!0),l.setUint32(16,r,!0),l.setUint32(20,yi(r,r,s==="DXT1"?8:16),!0),l.setUint32(28,o,!0),l.setUint32(76,32,!0),l.setUint32(80,4,!0);for(let f=0;f<4;f++)l.setUint8(84+f,s.charCodeAt(f));l.setUint32(108,4096|(o>1?4194312:0),!0);let c=128;for(const f of n)i.set(f.data,c),c+=f.data.length;return i}const ud=32;function pr(t){const e=new DataView(t.buffer,t.byteOffset,t.byteLength),n=[];for(let s=0;s+8<=t.length;){const o=e.getUint32(s,!0),r=e.getUint32(s+4,!0);if(s+8+r>t.length)break;n.push({id:o,data:t.subarray(s+8,s+8+r)}),s+=8+r}return n}const Wn=t=>new DataView(t.buffer,t.byteOffset,t.byteLength);function hd(t){const e=Wn(t).getUint16(0,!0),n=[];let s=2;const o=()=>{let r="";for(;s<t.length&&t[s];)r+=String.fromCharCode(t[s++]);return s++,r};for(let r=0;r<e&&s<t.length;r++){const a=o(),i=o(),l=o();n.push({shader:a,texture:i,material:l,flags:Wn(t).getUint32(s,!0)}),s+=4}return n}function dd(t,e,n){const s=pr(t),o=pr(e),r=s.find(b=>b.id===1),a=s.find(b=>b.id===2),i=s.find(b=>b.id===3),l=o.find(b=>b.id===9),c=o.find(b=>b.id===10);if(!a||!i||!l||!c)throw new Error("Не уровень 4A: нет шейдеров, визуалов или буферов level.geom_pc");const f=hd(a.data),h=Wn(l.data),u=Wn(c.data),d=l.data.length/ud,p=c.data.length/2,m=new Map;let g=0,w=0,y=0;for(const b of pr(i.data)){const k=pr(b.data),E=k.find(N=>N.id===21),A=k.find(N=>N.id===1);if(!E||!A){y++;continue}const S=Wn(E.data),[$,T,U,_]=[4,8,12,16].map(N=>S.getUint32(N,!0)),R=Wn(A.data).getUint16(2,!0);if($+T>d||U+_>p){w++;continue}let L=m.get(R);L||m.set(R,L={pos:[],nor:[],uv:[],uv2:[],idx:[]});const O=L.pos.length/3;for(let N=0;N<T;N++){const D=($+N)*ud;L.pos.push(h.getFloat32(D,!0),h.getFloat32(D+4,!0),h.getFloat32(D+8,!0));const F=h.getUint8(D+14)/255*2-1,C=h.getUint8(D+13)/255*2-1,P=h.getUint8(D+12)/255*2-1,I=Math.hypot(F,C,P)||1;L.nor.push(F/I,C/I,P/I),L.uv.push(h.getInt16(D+24,!0)/1024,h.getInt16(D+26,!0)/1024),L.uv2.push(h.getInt16(D+28,!0)/32768,h.getInt16(D+30,!0)/32768)}for(let N=0;N<_;N++)L.idx.push(O+u.getUint16((U+N)*2,!0));g++}const M=[...m].map(([b,k])=>{const E=f[b],A=k.pos.length/3;return{name:`${E?.texture||"?"} · ${E?.material??""}`,texture:E?.texture??"",shader:E?.shader,material:E?.material,vertexCount:A,pos:Float32Array.from(k.pos),nor:Float32Array.from(k.nor),uv:Float32Array.from(k.uv),uv2:Float32Array.from(k.uv2),alphaTest:/aref|wallmark|alpha/i.test(E?.shader??"")||void 0,bone:new Float32Array(A*4),weight:new Float32Array(A*4).map((S,$)=>$%4===0?1:0),indices:Uint32Array.from(k.idx),skinned:!1}}).filter(b=>b.indices.length),v=[`Уровень 4A (Metro 2033, версия ${r?Wn(r.data).getUint32(0,!0):0}): ${n}`,`Визуалов с геометрией ${g}, узлов иерархии ${y}${w?`, пропущено ${w}`:""}; шейдеров ${f.length}`,`Мешей (по шейдерам) ${M.length}, вершин ${M.reduce((b,k)=>b+k.vertexCount,0)}, треугольников ${M.reduce((b,k)=>b+k.indices.length/3,0)}`];return{meshes:M,skeleton:null,motions:[],bounds:dt(M),flipZ:!0,info:v}}async function xy(t,e){const n=await e.sibling("level.geom_pc");if(!n)throw new Error("Рядом с level нет level.geom_pc");const s=(t.path??"").split("/").slice(-2,-1)[0]??t.name,o=dd(new Uint8Array(await t.arrayBuffer()),new Uint8Array(n),s),r=await e.sibling("level.lmap_pc");r&&r.byteLength===16777216&&(o.lightmap={width:4096,height:4096,data:new Uint8Array(r),scale:2.5},o.info.push("Освещение: карта level.lmap_pc (4096×4096)"));const a=await e.sibling("level.bin");if(!a)return o.info.push("Нет level.bin — объекты уровня не показаны"),o;try{const i=t.path??"",l=i.toLowerCase().lastIndexOf("maps/"),c=(l>=0?i.slice(0,l):"")+"meshes/",{placeLevelObjects:f}=await Promise.resolve().then(()=>M4),h=await f(new Uint8Array(a),d=>e.readPath(c+d)),u=o.meshes.length;o.meshes.push(...h.meshes),o.instances={meshes:h.instances.meshes.map(d=>d+u),protos:h.instances.protos.map(d=>({...d,parts:d.parts.map(p=>({...p,mesh:p.mesh+u}))})),placements:h.instances.placements.map(d=>({...d,ranges:d.ranges?.map(p=>({...p,mesh:p.mesh+u}))}))},o.bounds=dt(o.meshes),o.info.push(...h.info)}catch(i){o.info.push(`level.bin: ${i.message}`)}return o}async function pd(t,e){if(!t)return null;const n="textures/"+t.toLowerCase().replace(/\\/g,"/"),s=o=>o.buffer.slice(o.byteOffset,o.byteOffset+o.byteLength);for(const o of e.archives){const r=[2048,1024,512].filter(c=>o.lookup(`${n}.${c}`));if(!r.length)continue;const a=r[r.length-1],i=fd([{side:a,data:await o.read(o.lookup(`${n}.${a}`))}]);let l=null;return{name:n.split("/").pop()+".dds",path:`${n}.dds`,decoded:Pn(s(i)),arrayBuffer:async()=>(l||(l=fd(await Promise.all(r.map(async c=>({side:c,data:await o.read(o.lookup(`${n}.${c}`))}))))),s(l))}}return null}async function My(t,e){if(!t)return null;const n=await pd(t+"_bump",e);return n?.decoded?.rgba?md(n.decoded):null}function md(t){if(!t.rgba)return null;const{width:e,height:n,rgba:s}=t,o=e*n,r=new Uint8Array(o*4),a=new Uint8Array(o*4),i=e/64,l=(c,f)=>s[((f+n)%n*e+(c+e)%e)*4+3]/255;for(let c=0;c<n;c++)for(let f=0;f<e;f++){const h=(l(f+1,c)-l(f-1,c))*.5*i,u=(l(f,c+1)-l(f,c-1))*.5*i,d=Math.hypot(h,u,1),p=(c*e+f)*4;r[p]=Math.round((-h/d*.5+.5)*255),r[p+1]=Math.round((-u/d*.5+.5)*255),r[p+2]=Math.round((1/d*.5+.5)*255),r[p+3]=255;const m=s[p+1]/255;a[p]=255,a[p+1]=Math.round((1-.7*m)*255),a[p+2]=0,a[p+3]=Math.round(m*255)}return{normal:{image:{width:e,height:n,rgba:r},packing:"rgb",label:`N ${e}×${n} (_bump)`},surface:{image:{width:e,height:n,rgba:a},label:"PBR (_bump)"}}}const lo=Object.freeze(Object.defineProperty({__proto__:null,loadMetroLevel:xy,metroBumpMaps:md,metroLevelModel:dd,parseShaders:hd,resolveMetroMaps:My,resolveMetroTexture:pd},Symbol.toStringTag,{value:"Module"})),bi=30;function gd(t){const e=new DataView(t.buffer,t.byteOffset,t.byteLength),n=new Map;for(let s=0;s+8<=t.length;){const o=e.getUint32(s,!0),r=e.getUint32(s+4,!0);if(s+8+r>t.length)break;n.set(o,t.subarray(s+8,s+8+r)),s+=8+r}return n}function wd(t){const e=gd(t),n=e.get(0),s=e.get(1);if(!n||n.length<26||!s||s.length<22)throw new Error("Не анимация 4A (.motion): нет заголовка");const o=new DataView(n.buffer,n.byteOffset,n.byteLength),r=new DataView(s.buffer,s.byteOffset,s.byteLength),a=[];for(let i=0;i<128;i++)n[10+(i>>3)]&1<<(i&7)&&a.push(i);return{skeletonCrc:o.getUint32(4,!0),bones:a,keys:r.getUint32(14,!0)}}function ky(t,e,n){const s=wd(t),o=gd(t).get(2);if(!o)throw new Error("В .motion нет треков");const r=new DataView(o.buffer,o.byteOffset,o.byteLength),a=s.keys,i=new Map;let l=0;const c=(f,h)=>{const u=r.getInt16(f,!0)/32767,d=r.getInt16(f+2,!0)/32767,p=r.getInt16(f+4,!0)/32767;if(h){const g=(r.getInt16(f,!0)&1)<<1|r.getInt16(f+2,!0)&1,w=[u*Math.SQRT1_2,d*Math.SQRT1_2,p*Math.SQRT1_2];return w.splice(g,0,Math.sqrt(Math.max(0,1-w[0]*w[0]-w[1]*w[1]-w[2]*w[2]))),[w[3],-w[0],-w[1],-w[2]]}return[Math.sqrt(Math.max(0,1-u*u-d*d-p*p)),-u,-d,-p]};for(const f of s.bones){if(l>=o.length)throw new Error(`.motion: треки кончились на кости ${f}`);const h=o[l++],u=h&2?a:1;if(l+=8,l+u*6>o.length)throw new Error(".motion: повреждён трек поворота");const d=[];for(let g=0;g<u;g++)d.push([g,c(l+g*6,(h&32)!==0)]);l+=u*6;let p;if(h&1){l+=8;const g=l,w=l+a*6,y=w+12;if(y+12>o.length)throw new Error(".motion: повреждён трек позиции");const M=[0,1,2].map(v=>r.getFloat32(w+v*4,!0)),x=[0,1,2].map(v=>r.getFloat32(y+v*4,!0));p=[];for(let v=0;v<a;v++)p.push([v,[0,1,2].map(b=>r.getInt16(g+v*6+b*2,!0)*M[b]+x[b])]);l=y+12}else p=[[0,[r.getFloat32(l,!0),r.getFloat32(l+4,!0),r.getFloat32(l+8,!0)]]],l+=12;const m=n(f);m>=0&&i.set(m,{rot:d,pos:p,scl:null})}if(l!==o.length)throw new Error(`.motion: прочитано ${l} из ${o.length} байт треков`);return{kind:"node",name:e,tps:bi,chan:i,duration:Math.max(a-1,0)/bi||1/bi,frames:a}}function Kn(t){const e=new DataView(t.buffer,t.byteOffset,t.byteLength),n=[];for(let s=0;s+8<=t.length;){const o=e.getUint32(s,!0),r=e.getUint32(s+4,!0);if(s+8+r>t.length)break;n.push({id:o,data:t.subarray(s+8,s+8+r)}),s+=8+r}return n}const co=t=>new DataView(t.buffer,t.byteOffset,t.byteLength),mr=t=>new TextDecoder("windows-1251").decode(t).split("\0"),yd=(t,e,n)=>{const s=t.getUint8(e+2)/255*2-1,o=t.getUint8(e+1)/255*2-1,r=t.getUint8(e)/255*2-1,a=Math.hypot(s,o,r)||1;n.push(s/a,o/a,r/a)};function vi(t){const e=[];for(const n of Kn(t)){const s=Kn(n.data),o=s.find(M=>M.id===2),r=s.find(M=>M.id===3),a=s.find(M=>M.id===5),i=s.findIndex(M=>M.id===3||M.id===5),l=i>=0?s[i+1]:void 0;if(!r&&!a||!l)continue;const[c="",f="",h=""]=o?mr(o.data):[],u={texture:c,shader:f,material:h,pos:[],nor:[],uv:[],idx:[]};if(r){const M=co(r.data),x=M.getUint32(4,!0);for(let v=0;v<x&&8+v*32+32<=r.data.length;v++){const b=8+v*32;u.pos.push(M.getFloat32(b,!0),M.getFloat32(b+4,!0),M.getFloat32(b+8,!0)),yd(M,b+12,u.nor),u.uv.push(M.getFloat32(b+24,!0),M.getFloat32(b+28,!0))}}else{const M=a.data,x=co(M),v=M[0],b=Array.from(M.subarray(1,1+v)),k=1+v*61,E=x.getUint32(k,!0);u.bones=[],u.weights=[];for(let A=0;A<E&&k+4+A*32+32<=M.length;A++){const S=k+4+A*32;u.pos.push(x.getInt16(S,!0)/2720,x.getInt16(S+2,!0)/2720,x.getInt16(S+4,!0)/2720),yd(x,S+8,u.nor);let $=0;for(let T=0;T<4;T++)$+=M[S+24+T];for(let T=0;T<4;T++)u.bones.push(b[Math.floor(M[S+20+T]/3)]??0),u.weights.push($?M[S+24+T]/$:T===0?1:0);u.uv.push(x.getInt16(S+28,!0)/2048,x.getInt16(S+30,!0)/2048)}}const d=co(l.data),p=d.getUint16(0,!0),m=d.getUint16(2,!0),g=l.data.length-4>>1,w=Math.min(m?p*3:g,g),y=u.pos.length/3;for(let M=0;M<w;M++){const x=d.getUint16(4+M*2,!0);u.idx.push(x<y?x:0)}e.push(u)}return e}function bd(t){const e=Kn(t),n=e.find(a=>a.id===13),s=e.find(a=>a.id===19),o=n&&n.data.length>=4?co(n.data).getUint32(0,!0):0,r=s?(mr(s.data)[0]??"").split(",").map(a=>a.trim().toLowerCase().replace(/\\/g,"/")).filter(Boolean):[];return{bones:vd(t),crc:o,motionFolders:r}}function vd(t){const e=Kn(t).find(l=>l.id===13);if(!e)throw new Error("В .skeleton нет чанка костей");const n=co(e.data),s=n.getUint16(4,!0),o=[],r=new TextDecoder("windows-1251");let a=6;const i=()=>{let l=a;for(;l<e.data.length&&e.data[l];)l++;const c=r.decode(e.data.subarray(a,l));return a=l+1,c};for(let l=0;l<s&&a<e.data.length;l++){const c=i(),f=i(),h=[0,1,2].map(d=>n.getFloat32(a+d*4,!0)),u=[0,1,2].map(d=>n.getFloat32(a+12+d*4,!0));a+=26,o.push({name:c,parent:f,rot:h,pos:u})}return o}function xd(t,e){const[n,s,o]=t,r=Math.cos(n),a=Math.sin(n),i=Math.cos(s),l=Math.sin(s),c=Math.cos(o),f=Math.sin(o),h=[[i*c+l*a*f,-i*f+l*a*c,l*r],[r*f,r*c,-a],[-l*c+i*a*f,l*f+i*a*c,i*r]],u=new Float64Array(16);for(let d=0;d<3;d++)for(let p=0;p<3;p++)u[p*4+d]=h[d][p];return u[12]=e[0],u[13]=e[1],u[14]=e[2],u[15]=1,u}function Ey(t){const e=new Map(t.map((i,l)=>[i.name,l])),n=t.map(i=>e.get(i.parent)??-1),s=t.map(i=>xd(i.rot,i.pos)),o=new Array(t.length),r=(i,l)=>{const c=new Float64Array(16);for(let f=0;f<4;f++)for(let h=0;h<4;h++)c[f*4+h]=i[h]*l[f*4]+i[4+h]*l[f*4+1]+i[8+h]*l[f*4+2]+i[12+h]*l[f*4+3];return c},a=(i,l=0)=>{if(o[i])return o[i];const c=n[i];return o[i]=c>=0&&l<256?r(a(c,l+1),s[i]):s[i]};return t.map((i,l)=>{const c=a(l),f=new Float64Array(16);for(let h=0;h<3;h++)for(let u=0;u<3;u++)f[u*4+h]=c[h*4+u];for(let h=0;h<3;h++)f[12+h]=-(c[h*4]*c[12]+c[h*4+1]*c[13]+c[h*4+2]*c[14]);return f[15]=1,{name:i.name,parent:n[l],local:s[l],offset:f}})}function xi(t,e,n=!1){return t.filter(s=>n||s.idx.length).map(s=>{const o=s.pos.length/3,r=new Float32Array(o*4),a=new Float32Array(o*4);if(e&&s.bones)r.set(s.bones),a.set(s.weights);else for(let i=0;i<o;i++)a[i*4]=1;return{name:s.texture||"?",texture:s.texture,shader:s.shader,material:s.material,vertexCount:o,pos:Float32Array.from(s.pos),nor:Float32Array.from(s.nor),uv:Float32Array.from(s.uv),bone:r,weight:a,indices:o>65535?Uint32Array.from(s.idx):Uint16Array.from(s.idx),skinned:e&&!!s.bones,alphaTest:/aref|wallmark|alpha/i.test(s.shader)||void 0,defaultHidden:/^\$|^light\\/i.test(s.texture)||void 0}})}function Md(t){const e=t.toLowerCase().replace(/\\/g,"/"),n=e.lastIndexOf("meshes/");return n>=0?t.slice(0,n+7):""}async function Mi(t,e){const n=Kn(t),s={parts:[],bones:null,skeletonCrc:0,motionFolders:[],skeletonName:"",meshNames:[],missing:[]},o=n.find(i=>i.id===9);o&&s.parts.push(...vi(o.data));const r=n.find(i=>i.id===20),a=n.find(i=>i.id===16);if(r&&(s.skeletonName=mr(r.data)[0]??""),a&&a.data.length>4){const i=mr(a.data.subarray(4)),l=i.find(c=>c)??"";s.meshNames=l.split(",").map(c=>c.trim()).filter(Boolean);for(const c of s.meshNames){const f=c.toLowerCase().replace(/\\/g,"/")+".mesh",h=await e(f);if(!h){s.missing.push(f);continue}const u=Kn(h).find(d=>d.id===9);u&&s.parts.push(...vi(u.data))}}if(s.skeletonName){const i=s.skeletonName.toLowerCase().replace(/\\/g,"/")+".skeleton",l=await e(i);if(l){const c=bd(l);s.bones=c.bones,s.skeletonCrc=c.crc,s.motionFolders=c.motionFolders}else s.missing.push(i)}return s}function Ay(t){const e=new Map(t.map((a,i)=>[a.name,i])),n=new Uint8Array(t.length),s=[],o=(a,i=0)=>{if(n[a])return;n[a]=1;const l=e.get(t[a].parent);l!==void 0&&i<256&&o(l,i+1),s.push(a)};t.forEach((a,i)=>o(i));const r=new Array(t.length);return s.forEach((a,i)=>{r[a]=i}),{sorted:s.map(a=>t[a]),remap:r}}function kd(t,e){const n=!!t.bones&&t.parts.some(c=>c.bones),s=n?Ay(t.bones):null,o=xi(t.parts,n);if(s){for(const c of o)if(c.skinned)for(let f=0;f<c.bone.length;f++)c.bone[f]=s.remap[c.bone[f]]??0}if(!o.length)throw new Error(`В модели 4A нет геометрии${t.missing.length?` (не найдены: ${t.missing.join(", ")})`:""}`);const r=s?new Kt(Ey(s.sorted)):null,a=o.reduce((c,f)=>c+f.indices.length/3,0),i=[`Модель 4A (Metro 2033): ${e}`,`Мешей ${o.length}, вершин ${o.reduce((c,f)=>c+f.vertexCount,0)}, треугольников ${a}`,...t.skeletonName?[`Скелет ${t.skeletonName}: костей ${t.bones?.length??0}`]:[],...t.meshNames.length?[`Части: ${t.meshNames.join(", ")}`]:[],...t.missing.length?[`Не найдены: ${t.missing.join(", ")} — откройте content.vfi или папку content`]:[]],l={meshes:o,skeleton:r,motions:[],bounds:r?Jt(r,o):dt(o),flipZ:!0,info:i};if(s){const c=f=>s.remap[f]??-1;l.meta={metroSkeletonCrc:t.skeletonCrc,metroMotionFolders:t.motionFolders},l.loadMotions=async f=>{const h=new Uint8Array(await f.arrayBuffer()),u=wd(h);if(t.skeletonCrc&&u.skeletonCrc!==t.skeletonCrc)throw new Error(`анимация для другого скелета (crc ${u.skeletonCrc.toString(16)}, нужен ${t.skeletonCrc.toString(16)})`);return[ky(h,f.name.replace(/\.motion$/i,""),c)]}}return l}function $y(t,e,n){const s=t.meta?.metroMotionFolders??[];if(!s.length||!t.loadMotions)return[];const o=s.map(a=>`${n}motions/${a.replace(/\/+$/,"")}/`),r=[];for(const a of e.entries){if(!a.path.endsWith(".motion"))continue;const i=o.find(c=>a.path.startsWith(c));if(!i)continue;const l=a.path.slice(i.length).replace(/\.motion$/,"");r.push({kind:"node",name:l,tps:30,chan:new Map,duration:0,load:async()=>(await t.loadMotions(e.fileOf(a)))[0]})}return r.sort((a,i)=>a.name.localeCompare(i.name))}async function Sy(t,e){const n=Md(t.path??""),s=async r=>{const a=await e.readPath(n+r);if(a)return a;const i=await e.sibling(r.split("/").pop());return i?new Uint8Array(i):null},o=await Mi(new Uint8Array(await t.arrayBuffer()),s);return kd(o,t.path??t.name)}const Ed=Object.freeze(Object.defineProperty({__proto__:null,listMetroMotions:$y,loadMetroModel:Sy,meshesRoot:Md,metroBoneLocal:xd,metroChunks:Kn,metroModelFromData:kd,parseMetroMeshes:vi,parseMetroSkeleton:vd,parseMetroSkeletonInfo:bd,partsToMeshes:xi,readMetroModel:Mi},Symbol.toStringTag,{value:"Module"})),Ty=[4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0];function Ad(t,e=0){const n=t[e],s=(n&2)!==0,o=r=>s?(t[r]|t[r+1]<<8|t[r+2]<<16|t[r+3]<<24)>>>0:t[r];return s?[9,o(e+1),o(e+5)]:[3,o(e+1),o(e+2)]}function Uy(t,e,n,s){const o=t[e],[r,a,i]=Ad(t,e);if(s+i>n.length)throw new Error("QuickLZ: выход за границы буфера");if(!(o&1))return n.set(t.subarray(e+r,e+r+i),s),i;const l=o>>2&3;if(l!==3)throw new Error(`QuickLZ: уровень ${l} не поддерживается (нужен 3)`);const c=e+a,f=s,h=s+i-1,u=h-6-4;let d=e+r,p=s,m=1;const g=w=>(t[w]|t[w+1]<<8|t[w+2]<<16|t[w+3]<<24)>>>0;for(;;){if(m===1&&(m=g(d),d+=4),m&1){const w=d+4<=c?g(d):(t[d]|(t[d+1]??0)<<8|(t[d+2]??0)<<16)>>>0;m>>>=1;let y,M;(w&3)===0?(y=(w&255)>>>2,M=3,d+=1):(w&2)===0?(y=(w&65535)>>>2,M=3,d+=2):(w&1)===0?(y=(w&65535)>>>6,M=(w>>>2&15)+3,d+=2):(w&127)!==3?(y=w>>>7&131071,M=(w>>>2&31)+2,d+=3):(y=w>>>15,M=(w>>>7&255)+3,d+=4);const x=p-y;if(x<f||y===0)throw new Error("QuickLZ: ссылка за начало данных");for(let v=0;v<M;v++)n[p+v]=n[x+v];p+=M}else if(p<u){const w=Ty[m&15];for(let y=0;y<w;y++)n[p+y]=t[d+y];m>>>=w,p+=w,d+=w}else{for(;p<=h;)m===1&&(d+=4,m=2147483648),n[p++]=t[d++],m>>>=1;return i}if(d>c+4)throw new Error("QuickLZ: поток повреждён")}}function $d(t,e){const n=new Uint8Array(e);let s=0,o=0;for(;o<e&&s<t.length;){const[,r]=Ad(t,s);o+=Uy(t,s,n,o),s+=r}if(o!==e)throw new Error(`QuickLZ: распаковано ${o} из ${e} байт`);return n}function Sd(t){const e=new DataView(t.buffer,t.byteOffset,t.byteLength),n=[];let s=0;for(;s+8<=t.length;){const o=e.getUint32(s,!0),r=e.getUint32(s+8-4,!0),a=s+8;if(s=a+r,o===4294967295)continue;const i={vfs:"",vfsSize:0,files:[]};for(let l=a;l+8<=a+r;){const c=e.getUint32(l,!0),f=e.getUint32(l+4,!0),h=l+8;if(l=h+f,c===0){let u="";for(let d=h;d<h+f-4&&t[d];d++)u+=String.fromCharCode(t[d]);i.vfs=u,i.vfsSize=e.getUint32(h+f-4,!0)}else if(c===1)for(let u=h;u+20<=h+f;){const d=t[u],p=e.getUint32(u+4,!0),m=e.getUint32(u+8,!0),g=e.getUint32(u+12,!0),w=e.getUint32(u+16,!0);let y="";for(let M=0;M<w-1;M++)y+=String.fromCharCode(t[u+20+M]^d);i.files.push({path:y,off:p,size:m,packed:g}),u+=20+w}}n.push(i)}return n}async function Fy(t,e){const n=Sd(await ht(t,0,t.size)),s=new Map(e.map(a=>[a.name.toLowerCase(),a])),o=[],r=[];for(const a of n){if(!s.has(a.vfs.toLowerCase())){r.push(a.vfs);continue}for(const i of a.files)(i.size||i.packed)&&o.push({path:gt(i.path.replace(/^content\\/i,"")),size:i.size,vfs:a.vfs.toLowerCase(),off:i.off,packed:i.packed})}if(!o.length)throw new Error(`${t.name}: не найдены файлы данных ${r.join(", ")} — выберите их вместе с .vfi (или папку игры)`);return Qt({name:t.name,kind:"4a-vfi",version:2033,entries:o,async read(a){const i=s.get(a.vfs),l=await ht(i,a.off,a.off+a.packed);return a.packed===a.size?l:$d(l,a.size)}})}const _y=Object.freeze(Object.defineProperty({__proto__:null,openVfi:Fy,parseVfi:Sd},Symbol.toStringTag,{value:"Module"})),Cy=new Map([[3923445847,0],[1628322711,1]]);function Ly(t){const e=new DataView(t.buffer,t.byteOffset,t.byteLength);if(t.length<28||String.fromCharCode(t[0],t[1],t[2],t[3])!=="GADB")throw new Error("Не база LithTech (нет «GADB»)");const n=e.getUint32(8,!0),s=e.getUint32(12,!0),o=e.getUint32(24,!0),r=28,a=new TextDecoder("latin1"),i=new Map,l=M=>{if(M>=n||M>0&&t[r+M-1]!==0||!t[r+M])return null;let x=i.get(M);if(x===void 0){let v=r+M;for(;v<r+n&&t[v];)v++;x=a.decode(t.subarray(r+M,v)),i.set(M,x)}return x},c=r+n;let f=c+o*8;if(e.getUint32(f,!0)!==s)throw new Error("База LithTech: неожиданная структура (число категорий)");f+=4;const h=[];let u=0;for(let M=0;M<s;M++){const x=l(e.getUint32(f,!0))??"",v=e.getUint32(f+4,!0);f+=12;const b=[];for(let k=0;k<v;k++){const E=e.getUint32(f+4,!0),A=e.getUint32(f+8,!0),S=e.getUint32(f+12,!0);if(b.push({na:A,ast:S,vals:f+20}),f+=20+E*4,f>t.length)throw new Error("База LithTech обрезана")}u+=v,h.push({name:x,recs:b})}const d=h.findIndex(M=>M.name==="_Structures"),p=M=>Array.from({length:M.na},(x,v)=>{const b=c+(M.ast+v)*8;return{hash:e.getUint32(b,!0),type:t[b+4]&15,count:t[b+5],idx:e.getUint16(b+6,!0)}}),m=(M,x)=>e.getUint32(M.vals+x*4,!0),g=(M,x,v)=>{for(const b of p(M))if(b.type===3||b.type===4){const k=Array.from({length:b.count},(E,A)=>l(m(M,b.idx+A)));k.some(E=>E&&/\.mat$/i.test(E))&&v.push([(Cy.get(b.hash)??5)+x*10,k])}else if(b.type===9&&x<2&&d>=0)for(let k=0;k<b.count;k++){const E=m(M,b.idx+k);if(E===4294967295||E>>>16!==d)continue;const A=h[d].recs[E&65535];A&&g(A,x+1,v)}},w=new Map,y=new Map;for(const M of h)for(const x of M.recs){let v=null;for(const A of p(x))if(!(A.type!==3&&A.type!==4))for(let S=0;S<A.count;S++){const $=l(m(x,A.idx+S));$&&/\.mdl$/i.test($)&&(v??=[]).push($.replace(/\\/g,"/").toLowerCase())}if(!v)continue;const b=[];if(g(x,0,b),!b.length)continue;b.sort((A,S)=>A[0]-S[0]);const[k,E]=b[0];for(const A of v)(y.get(A)??1e9)<=k||(y.set(A,k),w.set(A,E.map(S=>S??"")))}return{modelMaterials:w,categories:s,records:u}}function Ry(t){const e=new Uint8Array(t);if(e.length<16||String.fromCharCode(e[0],e[1],e[2],e[3])!=="TEXR")throw new Error("Не текстура LithTech (нет «TEXR»)");const n=String.fromCharCode(e[12],e[13],e[14],e[15])==="DDS "?12:e.indexOf(68);return Pn(t.slice(n))}function Td(t){const e=new DataView(t.buffer,t.byteOffset,t.byteLength);if(t.length<26||String.fromCharCode(t[0],t[1],t[2],t[3])!=="LTMI")throw new Error("Не материал LithTech (нет «LTMI»)");const n=e.getUint32(8,!0),s=e.getUint16(12,!0),o=e.getUint16(14,!0),r=e.getUint16(24,!0);let a="";for(let u=26;u<26+r&&t[u];u++)a+=String.fromCharCode(t[u]);const i=26+r,l=i+n,c=u=>{let d="";for(let p=i+u;p<l&&t[p];p++)d+=String.fromCharCode(t[p]);return d},f={shader:a,textures:{},values:{}};let h=l+4;for(let u=0;u<s+o&&h+2<=t.length;u++){const d=e.getUint16(h,!0);if(h+2+d+4>t.length)break;let p="";for(let g=0;g<d;g++)p+=String.fromCharCode(t[h+2+g]);const m=e.getUint32(h+2+d,!0);if(h+=2+d+8,/^t[A-Z]/.test(p)){const g=c(m);g&&(f.textures[p]=g)}else i+m+4<=l&&(f.values[p]=e.getFloat32(i+m,!0))}return f}const Oy=Object.freeze(Object.defineProperty({__proto__:null,decodeTex:Ry,parseLtMaterial:Td},Symbol.toStringTag,{value:"Module"})),Iy=1/20,Dy=t=>{const e=new Float64Array(16);return Ae(t.pos,[t.rot[3],t.rot[0],t.rot[1],t.rot[2]],[1,1,1],e),e};function Py(t){const e=t.map(Dy);return t.map((n,s)=>({name:n.name,parent:n.parent,local:n.parent>=0?Lt(Vt(e[n.parent]),e[s]):e[s],offset:Vt(e[s])}))}function Ny(t,e,n,s){const o=new DataView(t.buffer,t.byteOffset,t.byteLength),r=12+e;if(r+8+n*2>t.length)throw new Error(".anim: блок анимации за концом файла");const a=o.getUint16(r+4,!0),i=o.getUint16(r+6,!0),l=[];for(let d=0;d<n;d++)l.push([t[r+8+2*d],t[r+9+2*d]]);let c=r+8+2*n;const f=[];for(let d=0;d<i;d++,c+=2)f.push(r+o.getUint16(c,!0));const h=c,u=h+s*i*2;if(u+a*6>t.length)throw new Error(".anim: таблица ключей за концом файла");return{nConst:a,nAnim:i,table:l,offs:f,mapAt:h,constAt:u}}const Ud=(t,e)=>[t.getInt16(e,!0),t.getInt16(e+2,!0),t.getInt16(e+4,!0)],zy=t=>t.map(e=>e*Iy),By=t=>{const e=t[0]/32767,n=t[1]/32767,s=t[2]/32767;return[Math.sqrt(Math.max(0,1-e*e-n*n-s*s)),e,n,s]},Vy=(t,e,n)=>t.map((s,o)=>s+(e[o]-s)*n),jy=(t,e,n)=>{const s=t[0]*e[0]+t[1]*e[1]+t[2]*e[2]+t[3]*e[3],o=t.map((a,i)=>a+((s<0?-e[i]:e[i])-a)*n),r=Math.hypot(...o)||1;return o.map(a=>a/r)};function Fd(t){return t.map((e,n)=>n).sort((e,n)=>t[e].nameOff-t[n].nameOff)}function gr(t,e){const n=new Map(e.map((r,a)=>[r.name.toLowerCase(),a]));let s=0;return{order:Fd(t).map(r=>{const a=n.get(t[r].name.toLowerCase());return a!==void 0&&s++,a??-1}),matched:s}}function _d(t,e,n){const s=new DataView(e.buffer,e.byteOffset,e.byteLength),o=t.times.length,r=Ny(e,t.off,n.length,o),a=f=>f+1<r.nAnim?r.offs[f+1]:e.length,i=(f,h,u)=>{if(f>=r.nAnim){const w=r.constAt+6*(f-r.nAnim);if(f-r.nAnim>=r.nConst)throw new Error(`.anim «${t.name}»: трек ${f} вне списка`);return[[0,h(Ud(s,w))]]}const d=r.offs[f],p=Math.floor((a(f)-d)/6),m=[],g=w=>h(Ud(s,d+6*Math.min(Math.max(w,0),Math.max(p-1,0))));for(let w=0;w<o;w++){const y=r.mapAt+2*(w*r.nAnim+f),M=e[y],x=e[y+1]/255;m.push([t.times[w],x>0?u(g(M),g(M+1),x):g(M)])}return m},l=new Map;r.table.forEach(([f,h],u)=>{const d=n[u];d===void 0||d<0||f===255&&h===255||l.set(d,{pos:f===255?null:i(f,zy,Vy),rot:h===255?null:i(h,By,jy),scl:null})});const c=t.times[o-1]??0;return{kind:"node",name:t.name,tps:1e3,chan:l,duration:c/1e3,frames:o}}const Gy={1:8,2:12,3:16,4:4,8:4};function wr(t){const e=new DataView(t.buffer,t.byteOffset,t.byteLength);if(t.length<80||String.fromCharCode(t[0],t[1],t[2],t[3])!=="MODL")throw new Error("Не модель LithTech (нет «MODL»)");const n=e.getUint32(4,!0);if(n!==42)throw new Error(`MODL версии ${n} не поддерживается (известна 42 — F.E.A.R. Online)`);const s=e.getUint32(16,!0),o=e.getUint32(20,!0),r=e.getUint32(36,!0),a=80,i=S=>{let $="";for(let T=a+S;T<a+r&&t[T];T++)$+=String.fromCharCode(t[T]);return $},l=[];let c=a+r+4*e.getUint32(76,!0);if(c+s*37>t.length)throw new Error("MODL: узлы выходят за конец файла");const f=[];for(let S=0;S<s;S++,c+=37){for(;f.length&&f[f.length-1].left===0;)f.pop();const $=f.length?f[f.length-1].i:-1;f.length&&f[f.length-1].left--;const T=_=>e.getFloat32(c+_,!0),U=e.getUint32(c,!0);l.push({name:i(U),parent:$,pos:[T(5),T(9),T(13)],rot:[T(17),T(21),T(25),T(29)],nameOff:U}),f.push({i:S,left:e.getUint32(c+33,!0)})}const h=[];try{let S=c+4;for(let $=0,T=e.getUint32(12,!0);$<T;$++){S+=12;const U=i(e.getUint32(S,!0)),_=e.getUint32(S+8,!0),R=e.getUint32(S+20,!0);if(S+=24,S+R*4>t.length||R>1e5)throw new Error("обрезано");const L=[];for(let O=0;O<R;O++)L.push(e.getUint32(S+O*4,!0)&16777215);S+=R*4,h.push({name:U,off:_,times:L})}}catch{h.length=0}const u=l.map((S,$)=>$).sort((S,$)=>l[S].nameOff-l[$].nameOff),d={nodes:l,nameOrder:u,anims:h,meshPath:null,lod0:[],lodGroups:[],palettes:new Map,pieces:[],sectionPiece:new Map};let p=-1;for(let S=t.length-6;S>c;S--)if(t[S]===46&&t[S+1]===109&&t[S+2]===101&&t[S+3]===115&&t[S+4]===104&&t[S+5]===0){p=S+5;break}if(p<0)return d;let m=p;for(;m>c&&t[m-1]>=32;)m--;const g=p-m;if(e.getUint16(m-2,!0)!==g)return d;d.meshPath=String.fromCharCode(...t.subarray(m,p));const w=Array.from({length:10},(S,$)=>e.getUint32(m-2-40+$*4,!0));for(let S=0;S<o;S++)d.pieces.push(i(e.getUint32(m-2-40-o*4+S*4,!0)));let y=p+1+3;const M=t.subarray(y,y+w[0]);y+=w[0];const x=Array.from({length:w[1]},(S,$)=>e.getUint16(y+$*2,!0));y+=w[1]*2;const v=Array.from({length:w[2]},(S,$)=>Array.from({length:5},(T,U)=>e.getUint32(y+$*20+U*4,!0)));y+=w[2]*20;const b=Array.from({length:w[3]},(S,$)=>({dist:e.getFloat32(y+$*12,!0),first:e.getUint32(y+$*12+4,!0),n:e.getUint32(y+$*12+8,!0)}));if(y+w[3]*12>t.length)throw new Error("MODL: таблица LOD выходит за конец файла");for(const S of v)for(let $=0;$<(S[4]||1);$++)d.palettes.set(S[3]+$,Array.from(M.subarray(S[1],S[1]+S[2]),T=>d.nameOrder[T]??T)),d.pieces[S[0]]&&d.sectionPiece.set(S[3]+$,d.pieces[S[0]]);const k=[];for(const S of b){const $=k[k.length-1];$&&S.dist<$[$.length-1].dist?$.push(S):k.push([S])}const E=S=>{const $=[];for(let T=S.first;T<S.first+S.n;T++){const U=v[x[T]];if(U)for(let _=0;_<(U[4]||1);_++)$.push(U[3]+_)}return $},A=new Set;for(const S of k){d.lodGroups.push(S.map(_=>({dist:_.dist,sections:E(_)})));const $=S.filter(_=>_.dist>0&&_.dist<1),U=($.length?$:S).reduce((_,R)=>R.dist<_.dist?R:_);for(const _ of E(U))A.add(_)}return d.lod0=[...A].sort((S,$)=>S-$),d}function Cd(t){const e=new DataView(t.buffer,t.byteOffset,t.byteLength);if(t.length<40||String.fromCharCode(t[0],t[1],t[2],t[3])!=="MESH")throw new Error("Не геометрия LithTech (нет «MESH»)");const n=e.getUint32(28,!0);let s=32+n;const o=e.getUint32(s+4,!0),r=e.getUint32(s+8,!0);s+=12;const a=s,i=s+o;let l=i+r;if(l+8>t.length)throw new Error("MESH: данные выходят за конец файла");const c=e.getUint32(l,!0);l+=4;const f=[];for(let d=0;d<c;d++){const p=e.getUint32(l,!0);l+=4;const m=[];for(let g=0;g<p;g++,l+=4)m.push({type:t[l+1],usage:t[l+2],index:t[l+3]});f.push(m)}const h=e.getUint32(l,!0);l+=4;const u=[];for(let d=0;d<h;d++,l+=36){const p=m=>e.getUint32(l+m*4,!0);u.push({vcount:p(1),istart:p(2),vstart:p(3),tris:p(4),material:p(5),palCount:p(6),palOff:p(7),decl:p(8)})}if(l>t.length)throw new Error("MESH: таблица секций обрезана");return{sections:u,decls:f,palettes:t.subarray(32,32+n),u:t,vAt:a,iAt:i}}function Ld(t,e,n){const s=t.sections[e],o=t.decls[s.decl];if(!o)throw new Error(`MESH: секция ${e} ссылается на объявление ${s.decl}`);let r=0;const a={};for(const M of o){const x=Gy[M.type];if(x===void 0)throw new Error(`MESH: неизвестный тип элемента вершины ${M.type}`);M.index===0&&!(M.usage in a)&&(a[M.usage]={off:r,type:M.type}),r+=x}if(!a[0]||!a[4])return null;const{u:i}=t,l=new DataView(i.buffer,i.byteOffset,i.byteLength);if(t.iAt+(s.istart+s.tris*3)*2>i.length)throw new Error(`MESH: индексы секции ${e} выходят за границы данных`);const c=new Uint16Array(s.tris*3);let f=65535,h=0;for(let M=0;M<c.length;M++){const x=l.getUint16(t.iAt+(s.istart+M)*2,!0);c[M]=x,x<f&&(f=x),x>h&&(h=x)}if(!c.length)return null;const u=h-f+1,d=t.vAt+(s.vstart+f)*r;if(d+u*r>t.iAt)throw new Error(`MESH: вершины секции ${e} выходят за границы данных`);if(f)for(let M=0;M<c.length;M++)c[M]-=f;const p=new Float32Array(u*3),m=new Float32Array(u*3),g=new Float32Array(u*2),w=new Float32Array(u*4),y=new Float32Array(u*4);for(let M=0;M<u;M++){const x=d+M*r,v=x+a[0].off;if(p[M*3]=l.getFloat32(v,!0),p[M*3+1]=l.getFloat32(v+4,!0),p[M*3+2]=l.getFloat32(v+8,!0),a[1]){const k=x+a[1].off;m[M*3]=l.getFloat32(k,!0),m[M*3+1]=l.getFloat32(k+4,!0),m[M*3+2]=l.getFloat32(k+8,!0)}const b=x+a[4].off;if(a[4].type===8?(g[M*2]=l.getInt16(b,!0)/32767,g[M*2+1]=l.getInt16(b+2,!0)/32767):(g[M*2]=l.getFloat32(b,!0),g[M*2+1]=l.getFloat32(b+4,!0)),a[6]&&a[7]&&n?.length){const k=x+a[6].off,E=x+a[7].off,A=[i[k+2],i[k+1],i[k],i[k+3]];let S=0;for(let $=0;$<4;$++)S+=A[$];for(let $=0;$<4;$++)w[M*4+$]=n[i[E+$]]??0,y[M*4+$]=S?A[$]/S:$===0?1:0}else w[M*4]=n?.[0]??0,y[M*4]=1}return{section:e,material:s.material,mesh:{name:`#${e}`,texture:"",vertexCount:u,pos:p,nor:m,uv:g,bone:w,weight:y,indices:c,skinned:!1}}}function Rd(t,e){if(!t.lodGroups.length)return t.lod0.length?t.lod0:e.sections.map((o,r)=>r);const n=o=>o.sections.reduce((r,a)=>r+(e.sections[a]?.vcount??0),0),s=new Set;for(const o of t.lodGroups){const r=o.some(l=>l.dist>0&&l.dist<1),a=r?o.filter(l=>l.dist>0):o;let i=a[0];for(const l of a)(n(l)>n(i)||n(l)===n(i)&&l.dist<i.dist)&&(i=l);for(const l of i.sections)s.add(l)}return[...s].sort((o,r)=>o-r)}function Xy(t,e,n){const s=e.sections[n];return t&&s.palCount&&s.palOff+s.palCount<=e.palettes.length?Array.from(e.palettes.subarray(s.palOff,s.palOff+s.palCount),o=>t.nameOrder[o]??o):t?.palettes.get(n)}function Hy(t,e,n){const s=t?Rd(t,e):e.sections.map((r,a)=>a),o=[];for(const r of s){if(r>=e.sections.length)continue;const a=Ld(e,r,Xy(t,e,r));a&&(a.mesh.name=t?.sectionPiece.get(r)??`#${r}`,a.mesh.matId=a.material,o.push(a.mesh))}if(!o.length)throw new Error("В модели нет секций с UV");return{meshes:o,skeleton:null,motions:[],bounds:dt(o),info:n,flipZ:!0}}const Od=new WeakMap;async function Id(t){for(const e of t){let n=Od.get(e);if(!n){const o=e.entries.filter(a=>/(^|\/)database\/[^/]+\.gamedb$/.test(a.path));o.sort((a,i)=>Number(/loki\.gamedb$/.test(i.path))-Number(/loki\.gamedb$/.test(a.path))||(i.size??0)-(a.size??0));const r=o[0];n=r?e.read(r).then(a=>{try{return Ly(a)}catch(i){return console.warn(i),null}}):Promise.resolve(null),Od.set(e,n)}const s=await n;if(s)return s}return null}function Dd(t,e){if(!t)return null;const n=gt(e).split("/");for(let s=0;s<n.length;s++){const o=t.modelMaterials.get(n.slice(s).join("/"));if(o)return o}return null}async function pn(t,e,n){const s=gt(e),o=await t.readPath(s);if(o)return o;const r=n.path?n.path.slice(0,Math.max(0,n.path.lastIndexOf("/game/")+6)):"";if(r){const i=await t.readPath(r+s);if(i)return i}const a=await t.sibling(s.split("/").pop());return a?new Uint8Array(a):null}async function Wy(t,e){let n=new Uint8Array(await t.arrayBuffer()),s=t.path??t.name;if(String.fromCharCode(n[0],n[1],n[2],n[3])==="MESH"){const a=(t.path??t.name).replace(/\.mesh$/i,".mdl"),i=await pn(e,a,t);if(i){s=a;const l=n;return n=i,ki(wr(n),l,s,t,e)}return ki(null,n,t.path??t.name,t,e)}const o=wr(n);if(!o.meshPath)throw new To("В .mdl нет геометрии — это набор анимаций (откройте модель персонажа или оружия)");const r=await pn(e,o.meshPath,t);if(!r)throw new Error(`Не найдена геометрия ${o.meshPath} — откройте архивы игры (*.arch01) или папку Game`);return ki(o,r,s,t,e)}async function ki(t,e,n,s,o){const r=Cd(e),a=[`LithTech Jupiter EX (F.E.A.R. Online): ${t?"MODL v42":"MESH без .mdl"}`,`Секций в .mesh: ${r.sections.length}${t?`, секции LOD: ${Rd(t,r).join(", ")}`:""}`];t&&a.push(`Узлов: ${t.nodes.length}${t.pieces.length?`, куски: ${t.pieces.join(", ")}`:""}`);const i=Hy(t,r,a);t&&t.nodes.length>1&&await Jy(i,t,n,s,o);const l=await Id(o.archives);let c=Dd(l,n);if(c){a.push(`Материалы из базы игры: ${c.filter(Boolean).length}`);const h=i.meshes.filter(u=>(u.matId??0)<c.length&&!c[u.matId??0]);for(const u of h)u.defaultHidden=!0;h.length&&a.push(`Скрыты куски без материала в базе: ${[...new Set(h.map(u=>u.name))].join(", ")}`)}else c=await Ky(o.archives,n,Math.max(...i.meshes.map(h=>(h.matId??0)+1))),a.push(l?"Модели нет в базе игры — материалы подобраны по папке":"База игры (Game/database/Loki.gamedb) не открыта — материалы подобраны по папке");const f=await Ei(i.meshes,h=>c?.[h.matId??0],s,o);return f.length&&a.push(`Не найдены материалы: ${f.slice(0,3).join(", ")}${f.length>3?"…":""}`),i}async function Ei(t,e,n,s){const o=new Map,r=i=>{let l=o.get(i);return l||(l=pn(s,i,n).then(c=>{try{return c?Td(c):null}catch{return null}}),o.set(i,l)),l},a=new Set;for(const i of t){const l=e(i);if(!l)continue;i.material=l;const c=await r(l);if(!c){a.add(l);continue}i.shader=c.shader;const f=c.textures;i.texture=f.tDiffuseMap??f.tDiffuse??f.tBaseMap??Object.values(f)[0]??"",f.tNormalMap&&(i.normalMap=f.tNormalMap),f.tSpecularMap&&(i.surface={specular:{map:f.tSpecularMap,channel:1},source:"LithTech .mat"}),/alpha|cutout|clip|foliage|decal/i.test(c.shader)&&(i.alphaTest=!0)}return[...a]}async function Ky(t,e,n){const s=gt(e).replace(/[^/]*$/,"");for(const o of t){const r=o.entries.map(a=>a.path).filter(a=>a.startsWith(s)&&/\.mat$/.test(a)&&!/(_drop|blood_|camo_)/.test(a)&&a.slice(s.length).split("/").length<=2);if(r.length===n)return r.sort()}return null}async function Pd(t,e,n,s,o,r){const a=await pn(r,e.replace(/\.mdl$/i,".anim"),o);if(!a||String.fromCharCode(a[0],a[1],a[2],a[3])!=="ANIM")return null;const i=s?Fd(n):gr(t.nodes,n).order,l=[];let c=0;for(const f of t.anims)try{l.push(_d(f,a,i))}catch{c++}return{list:l,bad:c}}const Nd=new WeakMap;async function Yy(t,e){let n=Nd.get(t);if(n||Nd.set(t,n=new Map),!n.has(e.path)){let s=null;try{s=wr(await t.read(e)),(s.meshPath||!s.anims.length)&&(s=null)}catch{s=null}n.set(e.path,s)}return n.get(e.path)}const Zy=/^char\/ai\/global\/[^/]+\/anims?\/[^/]+\.mdl$/;async function qy(t,e,n){const s=gt(t).replace(/[^/]*$/,""),o=gt(t),r=[],a=/(^|\/)charf?\//.test(s),i=new Set;for(const l of n.archives)for(const c of l.entries){if(!c.path.endsWith(".mdl")||c.path===o||i.has(c.path)||!(c.path.startsWith(s)&&(()=>{const d=c.path.slice(s.length);return!d.includes("/")||/^anims?\//.test(d)})())&&!(a&&Zy.test(c.path)))continue;i.add(c.path);const h=await Yy(l,c);if(!h||!n.archives.some(d=>d.lookup(c.path.replace(/\.mdl$/,".anim"))))continue;const{matched:u}=gr(h.nodes,e.nodes);u<h.nodes.length*.9||u<e.nodes.length*.5||r.push({path:c.path,set:h})}return r.sort((l,c)=>l.path.localeCompare(c.path))}function Qy(t,e,n,s,o){const r=gr(t.nodes,n).order,a=e.split("/").pop().replace(/\.mdl$/i,"");let i=null;const l=()=>i??=pn(o,e.replace(/\.mdl$/i,".anim"),s).then(c=>{if(!c||String.fromCharCode(c[0],c[1],c[2],c[3])!=="ANIM")throw new Error(`Не найден ${e.replace(/\.mdl$/i,".anim")}`);return c});return t.anims.map(c=>{const f=`${a}/${c.name}`;return{kind:"node",name:f,tps:1e3,chan:new Map,duration:(c.times[c.times.length-1]??0)/1e3,frames:c.times.length,source:a,load:async()=>({..._d(c,await l(),r),name:f,source:a})}})}async function Jy(t,e,n,s,o){const r=new Kt(Py(e.nodes));t.skeleton=r;for(const c of t.meshes)c.skinned=!0;t.bounds=Jt(r,t.meshes),t.loadMotions=async c=>{const f=wr(new Uint8Array(await c.arrayBuffer()));if(!f.anims.length)throw new Error("В .mdl нет анимаций");const{matched:h}=gr(f.nodes,e.nodes);if(!h)throw new Error("Узлы набора анимаций не совпадают с узлами модели");const u=await Pd(f,c.path??c.name,e.nodes,!1,c,o);if(!u)throw new Error(`Не найден ${(c.path??c.name).replace(/\.mdl$/i,".anim")}`);return u.list};let a=0;if(e.anims.length){const c=await Pd(e,n,e.nodes,!0,s,o);c?(t.motions.push(...c.list),a+=c.bad):t.info.push(`Анимаций в .mdl: ${e.anims.length}, но .anim не найден`)}const i=[];for(const{path:c,set:f}of await qy(n,e,o))t.motions.push(...Qy(f,c,e.nodes,s,o)),i.push(c.split("/").pop());const l=i.length>6?`${i.slice(0,6).join(", ")} и ещё ${i.length-6}`:i.join(", ");t.motions.length&&t.info.push(`Анимаций: ${t.motions.length}${i.length?` (наборов рядом: ${i.length} — ${l}; читаются при выборе)`:""}${a?`, не прочитано ${a}`:""}`)}const tb=Object.freeze(Object.defineProperty({__proto__:null,applyMaterials:Ei,findGameDb:Id,loadLithtechModel:Wy,materialsFor:Dd,readAny:pn},Symbol.toStringTag,{value:"Module"}));function zd(t){const e=new DataView(t.buffer,t.byteOffset,t.byteLength);if(t.length<48||String.fromCharCode(t[0],t[1],t[2],t[3])!=="WLDC")throw new Error("Не файл уровня LithTech (нет «WLDC»)");let n=48;const s=()=>{const i=e.getUint16(n,!0);if(n+2+i>t.length)throw new Error(".wldclnt: строка за концом файла");const l=String.fromCharCode(...t.subarray(n+2,n+2+i));return n+=2+i,l},o=[];for(let i=0,l=e.getUint32(32,!0);i<l;i++){const c=e.getUint32(n+4,!0);n+=8;const f=s();if(n+=4,c>1e5)throw new Error(".wldclnt: неверное число материалов");const h=[];for(let u=0;u<c;u++)h.push(s());o.push({mesh:f,mats:h})}const r=[],a=e.getUint32(20,!0);for(;n+66<=t.length&&r.length<a;){const i=e.getUint16(n+64,!0);if(!i||i>260||n+66+i>t.length)break;const l=t.subarray(n+66,n+66+i);if(!l.every(h=>h>=32&&h<127))break;const c=h=>e.getFloat32(n+h*4,!0),f=[c(9),c(10),c(11),c(12)];if(Math.abs(Math.hypot(...f)-1)>.01)break;r.push({path:String.fromCharCode(...l),pos:[c(6),c(7),c(8)],rot:f}),n+=66+i}return{regions:o,instances:r}}function Bd(t){const e=new DataView(t.buffer,t.byteOffset,t.byteLength);if(t.length<52||String.fromCharCode(t[0],t[1],t[2],t[3])!=="PRFB")throw new Error("Не префаб LithTech (нет «PRFB»)");const n=g=>e.getUint32(4+g*4,!0),s=n(8),o=n(9),r=n(10),a=n(11),i=52,l=i+r;let c=l+a;if(r+a>s||c+8>t.length)throw new Error(".inst: данные выходят за конец файла");const f=[],h=e.getUint32(c,!0);if(c+=4,h>64)throw new Error(".inst: неверное число объявлений вершин");for(let g=0;g<h;g++){const w=e.getUint32(c,!0);if(c+=4,c+w*4>t.length)throw new Error(".inst: объявления вершин обрезаны");const y=[];for(let M=0;M<w;M++,c+=4)y.push({type:t[c+1],usage:t[c+2],index:t[c+3]});f.push(y)}const u=e.getUint32(c,!0);c+=4;const d=[];for(let g=0;g<u;g++,c+=36){if(c+36>t.length)throw new Error(".inst: таблица секций обрезана");const w=y=>e.getUint32(c+y*4,!0);d.push({vcount:w(1),istart:w(2),vstart:w(3),tris:w(4),material:w(5),palCount:w(6),palOff:w(7),decl:w(8)})}const p=[],m=c;for(let g=0;g<o&&c<t.length;g++){let w="";for(;c<t.length&&t[c];)w+=String.fromCharCode(t[c++]);if(/[^\x20-\x7e]/.test(w))break;c=m+(c-m+4&-4),p.push(w)}return{mesh:{sections:d,decls:f,palettes:new Uint8Array(0),u:t,vAt:i,iAt:l},mats:p}}function Vd(t,e){const n=[];for(let s=0;s<t.sections.length;s++){const o=Ld(t,s,void 0);if(!o)continue;const r=e[o.material]??`#${o.material}`;/shadowvolume/i.test(r)||n.push({mat:r,m:o.mesh,k:s})}return n}const eb=t=>{const e=[1/0,1/0,1/0,-1/0,-1/0,-1/0];for(let n=0;n<t.length;n+=3)for(let s=0;s<3;s++){const o=t[n+s];o<e[s]&&(e[s]=o),o>e[s+3]&&(e[s+3]=o)}return e};function jd(t,e){const n=new DataView(t.buffer,t.byteOffset,t.byteLength),s=new Set,o=(r,a)=>Math.abs(r-a)<=.01+Math.abs(a)*1e-5;for(let r=48;r+32<=t.length;r++){const a=n.getUint32(r+28,!0);if(!(a>65535))for(let i=0;i<e.length;i++){const l=e[i].get(a);if(!l||s.has(`${i}:${a}`))continue;let c=!0;for(let f=0;f<6&&c;f++)c=o(n.getFloat32(r+f*4,!0),l[f]);c&&s.add(`${i}:${a}`)}}return s}function Ai(t,e){return[...t].map(n=>{const s=n.pos.length/3,o=new Float32Array(s*4);for(let r=0;r<s;r++)o[r*4]=1;return{name:`${e}${n.mat.split("\\").pop()?.replace(/\.mat$/i,"")||"?"}`,texture:"",material:n.mat,vertexCount:s,pos:Float32Array.from(n.pos),nor:Float32Array.from(n.nor),uv:Float32Array.from(n.uv),bone:new Float32Array(s*4),weight:o,indices:s>65535?Uint32Array.from(n.idx):Uint16Array.from(n.idx),skinned:!1}})}async function nb(t,e){const n=t.path??t.name,s=await pn(e,n.replace(/\.(wld|wldmsh|wldsrvr|wldclnt)$/i,".wldclnt"),t);if(!s)throw new Error("Не найден .wldclnt уровня — откройте папку игры (Game/worlds/…) целиком");const o=zd(s),r=["LithTech Jupiter EX (F.E.A.R. Online): уровень",`Областей: ${o.regions.length}, экземпляров префабов: ${o.instances.length}`],a=new Map,i=new Map;let l=0,c=0;const f=[],h=[];for(const T of o.regions){const U=await pn(e,T.mesh,t);if(!U){f.push(T.mesh),h.push([]);continue}h.push(Vd(Cd(U),T.mats))}const u=h.map(T=>new Map(T.map(U=>[U.k,eb(U.m.pos)]))),d=jd(s,u),p=h.reduce((T,U)=>T+U.length,0),m=d.size>=p/2,g=(T,U,_)=>{let R=T.get(U);R||T.set(U,R={mat:U,pos:[],nor:[],uv:[],idx:[]});const L=R.pos.length/3;for(let O=0;O<_.pos.length;O++)R.pos.push(_.pos[O]),R.nor.push(_.nor[O]);for(let O=0;O<_.uv.length;O++)R.uv.push(_.uv[O]);for(let O=0;O<_.indices.length;O++)R.idx.push(L+_.indices[O])};h.forEach((T,U)=>{for(const{mat:_,m:R,k:L}of T){const O=u[U].get(L),N=Math.hypot((O[0]+O[3])/2,(O[1]+O[4])/2,(O[2]+O[5])/2)<1500;m&&N&&!d.has(`${U}:${L}`)?(g(i,_,R),c+=R.indices.length/3):(g(a,_,R),l+=R.indices.length/3)}}),f.length&&r.push(`Не найдена геометрия: ${f.slice(0,3).join(", ")}${f.length>3?"…":""}`),r.push(`Геометрия уровня: ${l} треугольников, ${a.size} материалов`),c&&r.push(`Подвижные объекты (двери, окна, вентиляторы…): ${c} треугольников — в своих координатах, без позиции на карте; скрыты, включаются в слоях`);const w=[...Ai(a.values(),""),...Ai(i.values(),"WorldModel · ").map(T=>({...T,defaultHidden:!0}))],y={meshes:[],protos:[],placements:[]},M=new Map,x=new Map,v=new Map,b=new Set;let k=0;const E=T=>[...M.keys()].indexOf(T);for(const T of o.instances){let U=x.get(T.path);if(U===void 0){const L=await pn(e,T.path,t);let O=[];if(L)try{const N=Bd(L);O=Vd(N.mesh,N.mats)}catch{O=[]}if(!O.length){b.add(T.path),x.set(T.path,-1);continue}U=y.protos.length,x.set(T.path,U);for(const N of O)M.has(N.mat)||M.set(N.mat,{mat:N.mat,pos:[],nor:[],uv:[],idx:[]});y.protos.push({name:T.path,parts:O.map(N=>({mesh:E(N.mat),pos:N.m.pos,nor:N.m.nor,uv:N.m.uv,indices:Uint32Array.from(N.m.indices)}))}),v.set(U,O)}if(U<0)continue;const _=new Float64Array(16);Ae(T.pos,[T.rot[3],T.rot[0],T.rot[1],T.rot[2]],[1,1,1],_);const R=[];for(const[L,O]of v.get(U).entries()){const N=M.get(O.mat),D=N.pos.length/3,F=O.m.vertexCount;R.push({mesh:E(O.mat),part:L,vStart:D,vCount:F,iStart:N.idx.length,iCount:O.m.indices.length});for(let C=0;C<F;C++){const P=O.m.pos[C*3],I=O.m.pos[C*3+1],B=O.m.pos[C*3+2];N.pos.push(P*_[0]+I*_[4]+B*_[8]+_[12],P*_[1]+I*_[5]+B*_[9]+_[13],P*_[2]+I*_[6]+B*_[10]+_[14]);const z=O.m.nor[C*3],j=O.m.nor[C*3+1],X=O.m.nor[C*3+2];N.nor.push(z*_[0]+j*_[4]+X*_[8],z*_[1]+j*_[5]+X*_[9],z*_[2]+j*_[6]+X*_[10]),N.uv.push(O.m.uv[C*2],O.m.uv[C*2+1])}for(let C=0;C<O.m.indices.length;C++)N.idx.push(D+O.m.indices[C])}y.placements.push({proto:U,name:T.path.split("\\").pop().replace(/\.inst$/i,""),pose:Array.from(_),group:T.path.split("\\").slice(1,-1).join("/")||"prefabs",ranges:R}),k++}const A=Ai(M.values(),"префабы · ");y.meshes=A.map((T,U)=>w.length+U);for(const T of y.protos)for(const U of T.parts)U.mesh+=w.length;for(const T of y.placements)for(const U of T.ranges??[])U.mesh+=w.length;r.push(`Префабов показано: ${k} (моделей ${y.protos.length})${b.size?`, не найдено: ${b.size} (${[...b].slice(0,3).join(", ")}${b.size>3?"…":""})`:""}`);const S=[...w,...A];if(!S.length)throw new Error("В уровне нет геометрии с текстурными координатами");const $=await Ei(S,T=>T.material,t,e);return $.length&&r.push(`Не найдены материалы: ${$.length} (${$.slice(0,3).join(", ")}${$.length>3?"…":""})`),{meshes:S,instances:y.placements.length?y:void 0,skeleton:null,motions:[],bounds:dt(S),info:r,flipZ:!0}}const sb=Object.freeze(Object.defineProperty({__proto__:null,findStaticSections:jd,loadLithtechWorld:nb,parsePrefab:Bd,parseWldClient:zd},Symbol.toStringTag,{value:"Module"}));function Gd(t){const e=new DataView(t.buffer,t.byteOffset,t.byteLength);if(t.length<48||t[0]!==76||t[1]!==84||t[2]!==65||t[3]!==82)throw new Error("Не архив LithTech (нет «LTAR»)");const n=e.getUint32(4,!0),s=e.getUint32(8,!0),o=e.getUint32(12,!0),r=e.getUint32(16,!0);if(n!==3)throw new Error(`LTAR версии ${n} не поддерживается (известна 3)`);const a=48,i=a+s,l=i+r*32;if(l+o*16>t.length)throw new Error("Индекс LTAR обрезан");const c=d=>{let p="";for(let m=a+d;m<i&&t[m];m++)p+=String.fromCharCode(t[m]);return p},f=Array.from({length:r},(d,p)=>{const m=i+p*32;return{name:c(e.getUint32(m,!0)),off:Number(e.getBigUint64(m+4,!0)),packed:Number(e.getBigUint64(m+12,!0)),size:Number(e.getBigUint64(m+20,!0)),comp:e.getUint32(m+28,!0)}}),h=[];let u=0;for(let d=0;d<o;d++){const p=l+d*16,m=c(e.getUint32(p,!0)),g=e.getUint32(p+12,!0);for(const w of f.slice(u,u+g))h.push({path:gt(m?`${m}/${w.name}`:w.name),off:w.off,packed:w.packed,size:w.size,comp:w.comp});u+=g}return{version:n,files:h}}async function ob(t,e){const n=new Blob([t]).stream().pipeThrough(new DecompressionStream("deflate")),s=new Uint8Array(await new Response(n).arrayBuffer());if(s.length!==e)throw new Error(`LTAR: распаковано ${s.length} из ${e} байт`);return s}async function rb(t){const e=await ht(t,0,48),n=new DataView(e.buffer,e.byteOffset,48),s=48+n.getUint32(8,!0)+n.getUint32(16,!0)*32+n.getUint32(12,!0)*16,o=Gd(await ht(t,0,s)),r=o.files.filter(a=>!a.path.endsWith(".crc")).map(a=>({path:a.path,size:a.size,off:a.off,packed:a.packed,comp:a.comp}));return Qt({name:t.name,kind:"ltar",version:o.version,entries:r,async read(a){const i=await ht(t,a.off,a.off+a.packed);return a.comp?ob(i,a.size):i}})}const ab=Object.freeze(Object.defineProperty({__proto__:null,openLtar:rb,parseLtarIndex:Gd},Symbol.toStringTag,{value:"Module"})),$i=/^resources\.(\d+)\.db$/i,ib=/\.(skinned_)?model$/,yr=`VOSTOK-MODEL
|
|
196
|
+
`,br=`VOSTOK-OUTFIT
|
|
197
|
+
`,vr=`VOSTOK-LEVEL
|
|
198
|
+
`,lb=["boot","hand","helmet","mask","back"];function Xd(t){const e=new Map;for(const n of t){const s=/^(models\/character\/([^/]+)\/[^/]+)\/(.+?)_(torso|legs)_(s\d)(_prem)?\.skinned_model$/.exec(n);if(!s||s[4]!=="torso")continue;const[,o,r,a,,i,l=""]=s,c=`${o}/${a}_legs_${i}${l}.skinned_model`,f=[n,...t.has(c)?[c]:[]];for(const h of lb){const u=`${o}/${a}_${h}.skinned_model`;t.has(u)&&f.push(u)}if(r==="human"){const h=f.some(p=>p.endsWith("_helmet.skinned_model")),d=`models/character/human/head/head_default${f.some(p=>p.endsWith("_mask.skinned_model"))?"_mask":h?"_helmet":""}.skinned_model`;t.has(d)?f.push(d):t.has("models/character/human/head/head_default.skinned_model")&&f.push("models/character/human/head/head_default.skinned_model")}e.set(`${o}/${a}${l}_${i}.outfit`,f)}return e}function Hd(t){const e=new DataView(t.buffer,t.byteOffset,t.byteLength);if(String.fromCharCode(...t.subarray(0,13))!=="little-endian")throw new Error("Не архив Survarium (нет «little-endian»)");const n=a=>Number(e.getBigUint64(a,!0)),s=[],o=new Set,r=(a,i,l)=>{for(let c=a;c;c=n(c+48)){if(c+76>t.length||o.has(c)||l>64)throw new Error("Индекс .db повреждён");o.add(c);const f=e.getUint16(c+72,!0);let h=c+75,u="";for(;h<t.length&&t[h];)u+=String.fromCharCode(t[h++]);const d=i+u.toLowerCase();if(f&1){r(n(c+8),d+"/",l+1);continue}let p=c;f&512&&(p=n(c+16)),s.push({path:d,flags:f,size:p?n(p):0,off:p?n(p+8):0})}};return r(n(960),"",0),s}async function cb(t){const e=await ht(t,0,1024),n=new DataView(e.buffer,e.byteOffset,e.byteLength).getUint32(20,!0);return Hd(await ht(t,0,Math.min(t.size,n+1024)))}async function fb(t,e){const n=e.filter(a=>$i.test(a.name)).sort((a,i)=>+$i.exec(a.name)[1]-+$i.exec(i.name)[1]);if(n.length||n.push(t),n[0]!==t)return null;const s=new Map;for(const a of n)for(const i of await cb(a)){if(i.flags&2048){s.delete(i.path);continue}s.set(i.path,{path:i.path,size:i.size,file:a,off:i.off})}const o=new Set;for(const a of s.keys()){const i=/^(.*?\.(?:skinned_)?model)\//.exec(a);i&&o.add(i[1])}for(const a of o)s.has(a)||s.set(a,{path:a,size:0,file:null,off:0});const r=new Map;for(const[a,i]of Xd(o))r.set(a,br+i.join(`
|
|
199
|
+
`));for(const a of s.keys()){const i=/^(projects\/[^/]+\/[^/]+)\/client_project$/.exec(a);i&&r.set(`${i[1]}.vlevel`,vr+a)}for(const[a,i]of r)s.has(a)||s.set(a,{path:a,size:0,file:null,off:0,text:i});return Qt({name:n.length>1?`${n[0].name} … ${n[n.length-1].name}`:t.name,kind:"vostok-db",version:1,entries:[...s.values()],async read(a){return a.file?ht(a.file,a.off,a.off+a.size):new TextEncoder().encode(a.text??yr+a.path)}})}const ub=Object.freeze(Object.defineProperty({__proto__:null,LEVEL_MARK:vr,MODEL_DIR_RE:ib,MODEL_MARK:yr,OUTFIT_MARK:br,openVostokDb:fb,outfitEntries:Xd,parseDbIndex:Hd},Symbol.toStringTag,{value:"Module"})),Wd=30;let Kd=typeof process<"u"&&process.env?.VOSTOK_EULER||"zyx-";const hb=(t,e)=>[t[0]*e[0]-t[1]*e[1]-t[2]*e[2]-t[3]*e[3],t[0]*e[1]+t[1]*e[0]+t[2]*e[3]-t[3]*e[2],t[0]*e[2]-t[1]*e[3]+t[2]*e[0]+t[3]*e[1],t[0]*e[3]+t[1]*e[2]-t[2]*e[1]+t[3]*e[0]];function Si(t,e,n){const s=Kd.endsWith("-")?-1:1,o={x:t*s,y:e*s,z:n*s};let r=[1,0,0,0];for(const a of Kd.replace("-","")){const i=o[a]/2,l=Math.cos(i),c=Math.sin(i);r=hb(r,a==="x"?[l,c,0,0]:a==="y"?[l,0,c,0]:[l,0,0,c])}return r}function db(t){const e=[],n=(s,o)=>{for(const[r,a]of Object.entries(s))!a||typeof a!="object"||Array.isArray(a)||(e.push({name:r,parent:o}),n(a,e.length-1))};return n(mn(t),-1),e}function pb(t){const e=new DataView(t.buffer,t.byteOffset,t.byteLength),n=e.getUint16(0,!0),s=[];let o=2;for(let r=0;r<n;r++){let a="";for(;o<t.length&&t[o];)a+=String.fromCharCode(t[o++]);if(o++,o+36>t.length)throw new Error("bind_pose обрезан");const i=Array.from({length:9},(l,c)=>e.getFloat32(o+c*4,!0));s.push({name:a,pos:i.slice(0,3),euler:i.slice(3,6),scale:i.slice(6,9)}),o+=36}return s}const mb=t=>{const e=new Float64Array(16);return Ae(t.pos,Si(t.euler[0],t.euler[1],t.euler[2]),[1,1,1],e),e};function Yd(t,e){const n=new Map(e.map(o=>[o.name,o])),s=t.map(o=>{const r=n.get(o.name);return r?mb(r):null});for(let o=0;o<t.length;o++)if(!s[o]){const r=t[o].parent;s[o]=r>=0?s[r]:(()=>{const a=new Float64Array(16);return a[0]=a[5]=a[10]=a[15]=1,a})()}return t.map((o,r)=>({name:o.name,parent:o.parent,local:o.parent>=0?Lt(Vt(s[o.parent]),s[r]):s[r],offset:Vt(s[r])}))}function gb(t){const e=new DataView(t.buffer,t.byteOffset,t.byteLength),n=e.getUint16(0,!0),s=n*9;let o=4+e.getUint32(4,!0);const r=i=>i+4<=t.length&&(()=>{const l=e.getUint32(i,!0);return l>=1&&l<1e5&&i+4+l*8<=t.length&&e.getFloat32(i+4+(l-1)*8+4,!0)===-1e9})();if(!r(o))for(o=4+s*8;o<t.length&&!r(o);)o+=4;const a=[];for(let i=0;i<s;i++){if(!r(o))throw new Error(`.b-spline: канал ${i} повреждён`);const l=e.getUint32(o,!0),c=[],f=[];for(let u=0;u<l;u++)c.push(e.getFloat32(o+4+u*8,!0)),u<l-1&&f.push(e.getFloat32(o+8+u*8,!0));const h=c[c.length-1];c.push(h,h,h),a.push({knots:c,ctrl:f}),o+=4+l*8}return{bones:n,channels:a}}function wb(t,e){const{knots:n,ctrl:s}=t,o=s.length;if(o===1)return s[0];const r=n[3],a=n[o];e=Math.min(Math.max(e,r),a);let i=3;for(;i<o-1&&e>=n[i+1];)i++;const l=[0,1,2,3].map(c=>s[Math.min(Math.max(i-3+c,0),o-1)]);for(let c=1;c<=3;c++)for(let f=3;f>=c;f--){const h=i-3+f,u=n[h+4-c]-n[h],d=u>0?(e-n[h])/u:0;l[f]=(1-d)*l[f-1]+d*l[f]}return l[3]}function yb(t){const e=mn(t),n=e.bones_names;return Array.isArray(n)?n.map(String):[]}function bb(t,e,n,s){const{bones:o,channels:r}=gb(e);if(n.length&&n.length!==o)throw new Error(`.b-spline: ${o} костей, в bones_names ${n.length}`);const a=new Map(s.map((f,h)=>[f.name,h]));let i=0;for(const f of r)i=Math.max(i,f.knots[f.knots.length-1]);const l=Math.max(1,Math.round(i)),c=new Map;for(let f=0;f<o;f++){const h=a.get(n[f]??"");if(h===void 0)continue;const u=r.slice(f*9,f*9+9),d=[],p=[],m=[];for(let g=0;g<=l;g++){const w=u.map(y=>wb(y,g));d.push([g,w.slice(0,3)]),p.push([g,Si(w[3],w[4],w[5])]),m.push([g,w.slice(6,9)])}c.set(h,{pos:d,rot:p,scl:m})}return{kind:"node",name:t,tps:Wd,chan:c,duration:l/Wd,frames:l+1}}function mn(t){const e=new DataView(t.buffer,t.byteOffset,t.byteLength),n=o=>{let r="";for(let a=o;a<t.length&&t[a];a++)r+=String.fromCharCode(t[a]);return r},s=(o,r)=>{if(o+16>t.length||r>32)throw new Error("Двоичный конфиг Vostok повреждён");const a=e.getUint32(o,!0),i=t[o+5],l=e.getUint16(o+6,!0),c=o?n(e.getUint32(o+8,!0)):"";switch(i){case 48:{const f={};for(let h=0;h<l;h++){const[u,d]=s(a+16*h,r+1);f[u]=d}return[c,f]}case 64:return[c,Array.from({length:l},(f,h)=>s(a+16*h,r+1)[1])];case 80:return[c,n(a)];case 0:case 16:return[c,e.getInt32(o,!0)];case 32:return[c,e.getFloat32(o,!0)];case 112:return[c,[0,4,8].map(f=>e.getFloat32(a+f,!0))];default:return[c,null]}};if(t.length<16)throw new Error("Двоичный конфиг Vostok пуст");return s(0,0)[1]}const qt=t=>t&&typeof t=="object"&&!Array.isArray(t)?t:null;function Zd(t){const e=new DataView(t.buffer,t.byteOffset,t.byteLength);let n=-1,s=0,o=0,r=-1,a=0,i=0,l=!1;for(let y=0;y+8<=t.length;){const M=e.getUint32(y,!0),x=e.getUint32(y+4,!0),v=y+8;if(v+x>t.length)throw new Error("converted_model: чанк выходит за конец файла");M===3?(s=e.getUint32(v,!0),n=v+4,o=x-4):M===4?(a=e.getUint32(v,!0),r=v+4,i=x-4):M===5&&(l=!0),y=v+x}if(n<0||r<0||!s)throw new Error("converted_model: нет вершин или индексов");const c=o/s,f=i/a;if(!Number.isInteger(c)||c<16||f!==2&&f!==4)throw new Error(`converted_model: неизвестный формат вершин (${o} байт на ${s} вершин)`);const h=c===32&&!l?24:c-4,u=l?(c-32)/4:0;if(l&&(!Number.isInteger(u)||u<0||u>3))throw new Error(`converted_model: скиновая вершина ${c} байт не поддерживается`);const d=new Float32Array(s*3),p=new Float32Array(s*2),m=l?new Float32Array(s*4):void 0,g=l?new Float32Array(s*4):void 0;for(let y=0;y<s;y++){const M=n+y*c;if(d[y*3]=e.getFloat32(M,!0),d[y*3+1]=e.getFloat32(M+4,!0),d[y*3+2]=e.getFloat32(M+8,!0),p[y*2]=Fe(e.getUint16(M+h,!0)),p[y*2+1]=Fe(e.getUint16(M+h+2,!0)),m&&g){let x=1;for(let v=0;v<=u;v++){m[y*4+v]=t[M+12+v];const b=v<u?e.getFloat32(M+16+v*4,!0):x;g[y*4+v]=b,x-=b}}}const w=s>65535||f===4?new Uint32Array(a):new Uint16Array(a);for(let y=0;y<a;y++){const M=f===2?e.getUint16(r+y*2,!0):e.getUint32(r+y*4,!0);if(M>=s)throw new Error(`converted_model: индекс ${M} вне ${s} вершин`);w[y]=M}return{pos:d,uv:p,indices:w,count:s,stride:c,bone:m,weight:g}}function qd(t){const e=mn(t),n=qt(qt(qt(e.material)?.g_stage)?.effect),s=r=>{const a=qt(n?.[r])?.value;return typeof a=="string"&&a?a:void 0},o=qt(n?.use_alpha_test)?.value===1;return{albedo:s("t_albedo"),normal:s("t_normal"),smoothness:s("t_smoothness"),metalness:s("t_metalness"),...o?{alphaTest:o}:{}}}const fo=t=>`textures/${t}`,Ti=t=>(Array.isArray(t)?t:t&&typeof t=="object"?Object.values(t):[]).filter(e=>typeof e=="string");function Qd(t,e,n=null,s=0){const o=t.entries.filter(c=>c.path.startsWith(e+"/render/")&&c.path.endsWith("/converted_model")).map(c=>({entry:c,name:c.path.slice(e.length+8,-16)})).filter(c=>!c.name.includes("/")),r=qt(n?.lod_hierrarchy);if(r){const c=Object.keys(r).filter(u=>/^LOD\d+$/.test(u)).sort((u,d)=>+u.slice(3)-+d.slice(3)),f=new Set(Object.values(r).flatMap(u=>Ti(qt(u)?.surfaces))),h=u=>new Set([...u?Ti(qt(r[u])?.surfaces):[],...Ti(qt(r.unassigned)?.surfaces)]);for(let u=Math.min(s,c.length-1);u>=0;u--){const d=h(c[u]),p=o.filter(m=>d.has(m.name)||!f.has(m.name)&&!/(^|_)lod_?\d/.test(m.name));if(p.length)return p}}const a=o.filter(c=>!/^lod\d+_/.test(c.name));if(a.length)return a;const i=o.map(c=>+/^lod(\d+)_/.exec(c.name)[1]),l=Math.min(...i);return o.filter((c,f)=>i[f]===l)}async function Jd(t,e){const n=t.lookup(e+"/skeleton"),s=t.lookup(e+"/render/bind_pose"),o=n?mn(await t.read(n)).skeleton:null,r=typeof o=="string"?gt(o).replace(/^resources\//,"")+".skeleton":"",a=r?t.lookup(r):null;return!a||!s?null:{path:r,bones:db(await t.read(a)),bind:pb(await t.read(s))}}async function xr(t,e,n,s,o=0){const r=t.lookup(e+"/settings");let a=null;if(r)try{a=mn(await t.read(r))}catch{}const i=qt(a?.material_settings),l=Qd(t,e,a,o);if(!l.length)throw new Error(`В ${e} нет частей render/*/converted_model`);const c=n?new Map(n.map((d,p)=>[d.name,p])):null,f=c&&s?s.map(d=>c.get(d.name)??0):null,h=[],u=[];for(const d of l){const p=Zd(await t.read(d.entry)),m=p.count,g=new Float32Array(m*4),w=new Float32Array(m*4),y=!!(f&&p.bone&&p.weight);if(y)for(let v=0;v<m*4;v++)g[v]=f[p.bone[v]]??0,w[v]=p.weight[v];else for(let v=0;v<m;v++)w[v*4]=1;const M={name:d.name,texture:"",vertexCount:m,pos:p.pos,nor:ge(p.pos,p.indices),uv:p.uv,bone:g,weight:w,indices:p.indices,skinned:y},x=qt(i?.[d.name])?.material_name;if(typeof x=="string"&&x){M.material=x;const v=t.lookup(`material_instances/${gt(x)}.material`);if(v)try{const b=qd(await t.read(v));b.albedo&&(M.texture=fo(b.albedo)),b.normal&&(M.normalMap=fo(b.normal)),b.alphaTest&&(M.alphaTest=!0),(b.smoothness||b.metalness)&&(M.surface={...b.smoothness?{gloss:{map:fo(b.smoothness),channel:0}}:{},...b.metalness?{metallic:{map:fo(b.metalness),channel:0}}:{},source:"Survarium .material"})}catch{u.push(x)}else u.push(x)}if(!M.texture){const v=t.lookup(`${e}/render/${d.name}/export_properties`);if(v)try{const b=qt(mn(await t.read(v)).source)?.texture_name;typeof b=="string"&&(M.texture=fo(b))}catch{}}h.push(M)}return{meshes:h,missing:u,parts:l.length,matsOk:!!i}}const tp=t=>`Не найдены материалы: ${t.slice(0,3).join(", ")}${t.length>3?"…":""}`;function ep(t,e,n,s){t.meta={vostokSkeleton:s},t.loadMotions=async o=>{const r=new Uint8Array(await o.arrayBuffer()),a=o.path??o.name,i=e.lookup(a.replace(/\.b-spline$/,".bones_names")),l=i?yb(await e.read(i)):[];return[bb(o.name.replace(/\.b-spline$/,""),r,l,n)]}}async function vb(t,e){const n=new Uint8Array(await t.arrayBuffer()),s=new TextDecoder().decode(n.subarray(0,Math.min(n.length,4096)));if(s.startsWith(br))return xb(s,t,e);if(s.startsWith(vr))return(await Promise.resolve().then(()=>S4)).loadVostokLevel(s,t,e);if(!s.startsWith(yr))throw new Error("Откройте папку модели *.model / *.skinned_model из архивов resources.*.db");const o=gt(s.slice(yr.length)),r=t.archive??e.archives.find(h=>h.lookup(o));if(!r)throw new Error("Модель Survarium открывается только из архивов resources.*.db");const a=[`Survarium (Vostok): ${o.split("/").pop()}`];let i=null;if(o.endsWith(".skinned_model"))try{i=await Jd(r,o),a.push(i?`Скелет: ${i.path} (${i.bones.length} костей)`:"Скелет не найден (нет .skeleton или bind_pose)")}catch(h){a.push(`Скелет не прочитан: ${h.message}`)}const l=await xr(r,o,i?.bones??null,i?.bind??null);a.splice(1,0,`Частей: ${l.parts}${l.matsOk?"":" (нет settings — материалы по export_properties)"}`),l.missing.length&&a.push(tp(l.missing));const c=i?new Kt(Yd(i.bones,i.bind)):null,f={meshes:l.meshes,skeleton:c,motions:[],bounds:c?Jt(c,l.meshes):dt(l.meshes),info:a,flipZ:!0};return i&&ep(f,r,i.bones,i.path),f}async function xb(t,e,n){const s=t.slice(br.length).split(`
|
|
200
|
+
`).map(g=>gt(g.trim())).filter(Boolean),o=e.archive??n.archives.find(g=>g.lookup(s[0]));if(!o||!s.length)throw new Error("Комплект Survarium открывается только из архивов resources.*.db");const r=[];for(const g of s)try{r.push({dir:g,sk:await Jd(o,g)})}catch{r.push({dir:g,sk:null})}const a=r.find(g=>g.sk)?.sk;if(!a)throw new Error("У частей комплекта не найден скелет");const i=[],l=new Set;for(const g of r)for(const w of g.sk?.path===a.path?g.sk.bind:[])l.has(w.name)||(l.add(w.name),i.push(w));const c=[],f=[],h=[],u=[];for(const g of r){if(!g.sk||g.sk.path!==a.path){u.push(g.dir.split("/").pop());continue}try{const w=await xr(o,g.dir,a.bones,g.sk.bind),y=g.dir.split("/").pop().replace(/\.skinned_model$/,"");for(const M of w.meshes)M.name=`${y} · ${M.name}`;c.push(...w.meshes),f.push(...w.missing),h.push(y)}catch{u.push(g.dir.split("/").pop())}}if(!c.length)throw new Error("Части комплекта не прочитаны");const d=new Kt(Yd(a.bones,i)),p=[`Survarium (Vostok): комплект ${e.name.replace(/\.outfit$/,"")}`,`Части: ${h.join(", ")}`,`Скелет: ${a.path} (${a.bones.length} костей)`];u.length&&p.push(`Пропущены (другой скелет или не прочитаны): ${u.join(", ")}`),f.length&&p.push(tp(f));const m={meshes:c,skeleton:d,motions:[],bounds:Jt(d,c),info:p,flipZ:!0};return ep(m,o,a.bones,a.path),m}function Mb(t,e){const n=t.meta?.vostokSkeleton;if(!n||!t.loadMotions)return[];const s=/^animations\/skeletons\/weapons\/([^/.]+)/.exec(n),o=s?s[1]:null,r=[];for(const a of e.entries){if(!a.path.endsWith(".b-spline")||!a.path.startsWith("animations/single/"))continue;const i=a.path.slice(18);(o?!i.startsWith(`weapons/${o}/item/`):/\/item\//.test(i)||i.startsWith("fx/")||i.startsWith("objects/"))||r.push({kind:"node",name:i.replace(/\.b-spline$/,""),tps:30,chan:new Map,duration:0,load:async()=>(await t.loadMotions(e.fileOf(a)))[0]})}return r.sort((a,i)=>a.name.localeCompare(i.name))}const np=Object.freeze(Object.defineProperty({__proto__:null,listVostokMotions:Mb,loadVostokModel:vb,modelMeshes:xr,modelParts:Qd,parseConvertedModel:Zd,parseVostokConfig:mn,parseVostokMaterial:qd,tbl:qt},Symbol.toStringTag,{value:"Module"}));function sp(t){const e=new DataView(t);if(t.byteLength<32||e.getUint32(0,!0)!==5784148)throw new Error("Не текстура Dunia (нет «TBX»)");const n=e.getUint32(8,!0);if(n+128>t.byteLength||e.getUint32(n,!0)!==542327876)throw new Error("XBT: нет DDS после заголовка");return t.slice(n)}const kb=Object.freeze(Object.defineProperty({__proto__:null,decodeXbt:t=>Pn(sp(t)),xbtDds:sp},Symbol.toStringTag,{value:"Module"})),Ui=4315969e-11,Fi=.7071068;function Eb(t,e,n){const s=(t&32767)*Ui-Fi,o=(e&32767)*Ui-Fi,r=(n&32767)*Ui-Fi,a=Math.sqrt(Math.max(0,1-s*s-o*o-r*r)),i=t>>15,l=e>>15,c=i===0&&l===1?[s,o,a,r]:i===1&&l===1?[s,o,r,a]:i===1?[s,a,o,r]:[a,s,o,r];return[c[3],c[0],c[1],c[2]]}const Ab=(t,e)=>{const n=[];for(let s=0;s<5;s++){const o=t.getUint32(e+s*4,!0);for(let r=0;r<32;r++)o&1<<r&&n.push(s*32+r)}return n},$b=(t,e,n)=>{const s=t[0]*e[0]+t[1]*e[1]+t[2]*e[2]+t[3]*e[3],o=s<0?-1:1,r=t.map((i,l)=>i+(o*e[l]-i)*n),a=Math.hypot(...r)||1;return r.map(i=>i/a)};function Sb(t){const e=new DataView(t.buffer,t.byteOffset,t.byteLength);if(t.length<176||t[0]!==76)throw new Error(`Анимация Dunia: версия 0x${(t[0]??0).toString(16)} не поддерживается (нужна 0x4c — Far Cry 2)`);const n=t[2],s=Array.from({length:10},(w,y)=>{const M=e.getUint32(136+y*4,!0);return M?M+16:0}),o=[0,1,2,3].map(w=>Ab(e,16+w*20)),r=e.getFloat32(132,!0),a=w=>Eb(e.getUint16(w,!0),e.getUint16(w+2,!0),e.getUint16(w+4,!0)),i=w=>[e.getFloat32(w,!0),e.getFloat32(w+4,!0),e.getFloat32(w+8,!0)],l=(w,y)=>{if(w+y>t.length)throw new Error(".mab: раздел за концом файла")};if(!s[3])throw new Error(".mab: нет поворотов по ключам");const c=e.getUint16(s[3],!0),f=e.getUint16(s[3]+2,!0);if(c!==o[1].length)throw new Error(`.mab: дорожек ${c}, а в маске ${o[1].length}`);const h=r>0&&f>1?(f-1)/r:n>=3?30:15,u=[15,30].find(w=>Math.abs(h-w)<w*.02)??h,d={version:n,duration:r,rate:u,keys:f,rootRot:null,rootPos:null,constRot:new Map,rotKeys:new Map,constPos:new Map,posKeys:new Map};if(s[1]){const w=e.getUint16(s[1]+2,!0);l(s[1]+8,w*6),d.rootRot=Array.from({length:w},(y,M)=>a(s[1]+8+M*6))}if(s[0]){const w=e.getUint16(s[0]+2,!0);l(s[0]+8,w*12),d.rootPos=Array.from({length:w},(y,M)=>i(s[0]+8+M*12))}if(s[2]){const w=e.getUint32(s[2],!0);l(s[2]+8,w*6),o[0].slice(0,w).forEach((y,M)=>d.constRot.set(y,a(s[2]+8+M*6)))}if(s[4]){const w=e.getUint32(s[4],!0);l(s[4]+8,w*12),o[2].slice(0,w).forEach((y,M)=>d.constPos.set(y,i(s[4]+8+M*12)))}if(s[5]){const w=e.getUint16(s[5],!0),y=e.getUint16(s[5]+2,!0);l(s[5]+8,w*y*12),o[3].slice(0,w).forEach((M,x)=>d.posKeys.set(M,Array.from({length:y},(v,b)=>i(s[5]+8+(x*y+b)*12))))}const p=c?Math.ceil(f/8):0;l(s[3]+8,(p+1)*4);const m=Array.from({length:p+1},(w,y)=>e.getUint32(s[3]+8+y*4,!0)),g=Array.from({length:c},()=>new Array(f).fill(null));for(let w=0;w<p;w++){let y=s[3]+m[w];l(y,c*7);for(let x=0;x<c;x++)w*8<f&&(g[x][w*8]=a(y+x*6));y+=c*6;const M=t.subarray(y,y+c);y+=c,y+=y&1;for(let x=0;x<c;x++)for(let v=0;v<8;v++){if(!(M[x]&1<<v))continue;l(y,6);const b=w*8+1+v;b<f&&(g[x][b]=a(y)),y+=6}}return g.forEach((w,y)=>{let M=-1;for(let x=0;x<f;x++)if(w[x]){if(M>=0&&x-M>1)for(let v=M+1;v<x;v++)w[v]=$b(w[M],w[x],(v-M)/(x-M));M=x}for(let x=M+1;x<f&&M>=0;x++)w[x]=w[M];M>=0&&d.rotKeys.set(o[1][y],w)}),d}const uo=(t,e)=>[t[0]*e[0]-t[1]*e[1]-t[2]*e[2]-t[3]*e[3],t[0]*e[1]+t[1]*e[0]+t[2]*e[3]-t[3]*e[2],t[0]*e[2]-t[1]*e[3]+t[2]*e[0]+t[3]*e[1],t[0]*e[3]+t[1]*e[2]-t[2]*e[1]+t[3]*e[0]];function op(t){const e=t[0],n=t[5],s=t[10],o=e+n+s;let r;if(o>0){const i=Math.sqrt(o+1)*2;r=[i/4,(t[6]-t[9])/i,(t[8]-t[2])/i,(t[1]-t[4])/i]}else if(e>n&&e>s){const i=Math.sqrt(1+e-n-s)*2;r=[(t[6]-t[9])/i,i/4,(t[4]+t[1])/i,(t[8]+t[2])/i]}else if(n>s){const i=Math.sqrt(1+n-e-s)*2;r=[(t[8]-t[2])/i,(t[4]+t[1])/i,i/4,(t[9]+t[6])/i]}else{const i=Math.sqrt(1+s-e-n)*2;r=[(t[1]-t[4])/i,(t[8]+t[2])/i,(t[9]+t[6])/i,i/4]}const a=Math.hypot(...r)||1;return r.map(i=>i/a)}function Tb(t,e,n,s=new Map,o){const r=new Map,a=f=>{const h=n[f];if(h===void 0||h<0)return null;let u=r.get(h);return u||(u={pos:null,rot:null,scl:null},r.set(h,u)),u},i=(f,h)=>{const u=s.get(f);return u?uo(op(u),h):h},l=(f,h)=>{const u=s.get(f);return u?[0,1,2].map(d=>u[d]*h[0]+u[4+d]*h[1]+u[8+d]*h[2]+u[12+d]):h};for(const[f,h]of e.constRot){const u=a(f);u&&(u.rot=[[0,i(f,h)]])}for(const[f,h]of e.rotKeys){const u=a(f);u&&(u.rot=h.map((d,p)=>[p,i(f,d)]))}for(const[f,h]of e.constPos){const u=a(f);u&&(u.pos=[[0,l(f,h)]])}for(const[f,h]of e.posKeys){const u=a(f);u&&(u.pos=h.map((d,p)=>[p,l(f,d)]))}const c=Math.max(e.rootPos?.length??0,e.rootRot?.length??0);if(o&&c>1&&n[o.bone]!==void 0&&n[o.bone]>=0){const f=e.rootRot?.[0]??[1,0,0,0],h=e.rootPos?.[0]??[0,0,0],u=rp(f);let d=!1;const p=Array.from({length:c},(m,g)=>{const w=e.rootRot?.[g]??f,y=e.rootPos?.[g]??h,M=uo(u,w),x=ap(u,y.map((v,b)=>v-h[b]));return(Math.abs(M[0])<.9999||Math.hypot(...x)>.001)&&(d=!0),{q:M,t:x}});if(d){const m=o.bone,g=Math.max(c,e.keys),w=x=>e.rotKeys.get(m)?.[Math.min(x,e.keys-1)]??e.constRot.get(m)??o.rot,y=x=>e.posKeys.get(m)?.[Math.min(x,e.keys-1)]??e.constPos.get(m)??o.pos,M=a(m);M.rot=[],M.pos=[];for(let x=0;x<g;x++){const v=p[Math.min(x,c-1)],b=ap(v.q,y(x)).map((k,E)=>k+v.t[E]);M.rot.push([x,i(m,uo(v.q,w(x)))]),M.pos.push([x,l(m,b)])}}}return{kind:"node",name:t,tps:e.rate,chan:r,duration:e.duration,frames:e.keys}}const rp=t=>[t[0],-t[1],-t[2],-t[3]],ap=(t,e)=>uo(uo(t,[0,e[0],e[1],e[2]]),rp(t)).slice(1);function Ub(t){const e=new DataView(t.buffer,t.byteOffset,t.byteLength);if(t.length<28||String.fromCharCode(t[2],t[1],t[0])!=="SKL")throw new Error("Не скелет Dunia (нет «SKL»)");const n=e.getUint16(16,!0),s=e.getUint32(8,!0),o=[];let r=20;const a=i=>i+8<=t.length&&e.getUint32(i,!0)===s&&e.getUint32(i+4,!0)===7;for(let i=0;i<n;i++){for(;r<t.length&&!a(r);)r++;if(r+58>t.length)throw new Error(".skeleton: записей меньше, чем объявлено");const l=h=>e.getFloat32(r+8+h*4,!0),c=e.getUint16(r+42,!0);let f="";for(let h=r+50;h+4<Math.min(t.length,r+80);h++){const u=e.getUint32(h,!0);if(u<1||u>64||h+4+u>=t.length||t[h+4+u]>=32&&t[h+4+u]<127)continue;let d=!0,p="";for(let m=0;m<u&&d;m++){const g=t[h+4+m];d=g>=32&&g<127,p+=String.fromCharCode(g)}if(d){f=p;break}}o.push({name:f,parent:c===65535?-1:c,rot:[l(3),l(0),l(1),l(2)],pos:[l(4),l(5),l(6)]}),r+=58}return o}function _i(t){const e=new DataView(t.buffer,t.byteOffset,t.byteLength);if(t.length<32||e.getUint32(0,!0)!==1296388936)throw new Error("Не модель Dunia (нет «MESH»)");const n=(s,o,r)=>{const a=[];for(;s+20<=o;){const i=String.fromCharCode(t[s+3],t[s+2],t[s+1],t[s]),l=e.getUint32(s+8,!0),c=e.getUint32(s+12,!0),f=e.getUint32(s+16,!0);if(l<20||s+l>o||c>l-20||r>8)throw new Error(`XBG: чанк ${i} повреждён`);const h=s+l-c;a.push({tag:i,at:s,size:l,data:h,dsize:c,kids:f?n(s+20,h,r+1):[]}),s+=l}return a};return n(32,t.length,0)}let Yn=class{constructor(e,n){this.u=e,this.p=n,this.dv=new DataView(e.buffer,e.byteOffset,e.byteLength)}u;p;dv;u8(){return this.u[this.p++]}u16(){const e=this.dv.getUint16(this.p,!0);return this.p+=2,e}u32(){const e=this.dv.getUint32(this.p,!0);return this.p+=4,e}i32(){const e=this.dv.getInt32(this.p,!0);return this.p+=4,e}f32(){const e=this.dv.getFloat32(this.p,!0);return this.p+=4,e}str(){const e=this.u32();if(e>4096||this.p+e>this.u.length)throw new Error(`Dunia: строка длиной ${e} на ${this.p-4}`);let n="";for(let s=0;s<e;s++)n+=String.fromCharCode(this.u[this.p+s]);return this.p+=e+1,n}align16(){this.p=this.p+15&-16}};function Ci(t){const e=_i(t),n=(u,d=e)=>{for(const p of d){if(p.tag===u)return p;const m=n(u,p.kids);if(m)return m}},s={materials:[],nodes:[],parts:[],clusters:[],lods:[],pcmp:[0,1/32768],ucmp:[0,1/32768],u:t,o2bm:[]},o=n("RMTL");if(o){const u=new Yn(t,o.data),d=u.u32();u.u32();for(let p=0;p<d;p++)s.materials.push(u.str())}const r=n("NODE");if(r){const u=new Yn(t,r.data),d=u.u32();u.u32();for(let p=0;p<d;p++){u.i32(),u.i32();const m=u.i32(),g=[u.f32(),u.f32(),u.f32(),u.f32()],w=[u.f32(),u.f32(),u.f32()],y=[u.f32(),u.f32(),u.f32()];u.i32(),u.f32(),u.f32();const M=u.str();u.u32(),s.nodes.push({name:M,parent:m,rot:[g[3],g[0],g[1],g[2]],pos:w,scale:y})}}const a=n("SKND"),i=n("SKID"),l=[];if(i){const u=new Yn(t,i.data),d=u.u32();for(let p=0;p<d;p++){u.u32(),u.u16();const m=u.u16();l.push(m===65535?-1:m)}}if(a){const u=new Yn(t,a.data),d=u.u32();for(let m=0;m<d;m++){u.p+=44;const g=u.u32();u.u32(),s.parts.push({name:u.str(),lod:g,node:l[m]??-1})}const p=a.kids.find(m=>m.tag==="CLUS");if(p){const m=new Yn(t,p.data);for(let g=0;g<d&&m.p<p.data+p.dsize;g++){const w=m.u32(),y=[];for(let M=0;M<w;M++){const x=m.u16(),v=m.u16();m.p+=10;const b=[];for(let k=0;k<48;k++)b.push(m.u16());y.push({mat:x,tris:v,palette:b})}s.clusters.push(y)}}}const c=n("LODS");if(!c)throw new Error("XBG: нет геометрии (LODS)");{const u=new Yn(t,c.data),d=u.u32();for(let p=0;p<d;p++){const m=u.f32(),g=u.u32(),w=[];for(let E=0;E<g;E++)w.push({format:u.u32(),stride:u.u32(),vcount:u.u32(),base:u.u32()});const y=u.u32(),M=[];for(let E=0;E<y;E++){const A=u.u32(),S=u.u32(),$=u.u32(),T=u.u32(),U=u.u32(),_=u.u32();u.u32();const R=w[A];if(!R)throw new Error("XBG: примитив ссылается на несуществующий буфер");M.push({buffer:A,part:S,cluster:$,firstIndex:T,lastVertex:U,firstVertex:(_-R.base)/R.stride})}const x=u.u32();u.align16();const v=u.p;u.p+=x;const b=u.u32();u.align16();const k=u.p;if(u.p+=b*2,u.p>c.data+c.dsize||x!==w.reduce((E,A)=>E+A.vcount*A.stride,0))throw new Error("XBG: буфер LOD повреждён");s.lods.push({dist:m,buffers:w,prims:M,vAt:v,iAt:k,icount:b})}}const f=n("O2BM");if(f&&f.dsize>=4){const u=new DataView(t.buffer,t.byteOffset,t.byteLength),d=u.getUint32(f.data,!0);if(4+d*64<=f.dsize)for(let p=0;p<d;p++)s.o2bm.push(Float64Array.from({length:16},(m,g)=>u.getFloat32(f.data+4+p*64+g*4,!0)))}const h=u=>{const d=n(u);if(!d||d.dsize<8)return null;const p=new DataView(t.buffer,t.byteOffset+d.data,8);return[p.getFloat32(0,!0),p.getFloat32(4,!0)]};return s.pcmp=h("PCMP")??s.pcmp,s.ucmp=h("UCMP")??s.ucmp,s}function Li(t){const e=_i(t).find(i=>i.tag==="DMTL");if(!e)throw new Error("XBM: нет чанка DMTL");const n=new Yn(t,e.data+5),s=n.str(),o=n.str(),r=n.u32(),a={};for(let i=0;i<r&&i<64;i++){const l=n.str(),c=n.str();l&&(a[c]=l)}return{name:s,shader:o,textures:a}}function Ri(t){const e=[...new Set(t)],n=new Set,s=e.filter(d=>/HEAD/.test(d)&&!/EYES|ROOT/.test(d));if(s.length<2&&!e.some(d=>/DUMMY/.test(d)))return n;const o=d=>d.replace(/^P_[A-Z]+_/,"").replace(/_LOD\d+$/,"").replace(/_/g,""),r=s.find(d=>/CAUCASIANHEAD01/.test(d))??s[0],a=r&&/NUBIAN/.test(r)?"NUBIAN":"CAUCASIAN",i=(d,p=[])=>{if(d.length<2)return;let m=d[0];for(const g of p){const w=d.find(y=>g.test(y));if(w){m=w;break}}for(const g of d)g!==m&&n.add(g)};for(const d of e)/DUMMY/.test(d)&&n.add(d);for(const d of s)d!==r&&n.add(d);if(r)for(const d of e.filter(p=>/EYES/.test(p)))o(d).replace(/^EYES/,"")!==o(r)&&n.add(d);const l=e.filter(d=>!n.has(d)&&!s.includes(d)&&!/EYES/.test(d)),c=(d,p)=>l.filter(m=>d.test(m)&&!0),f=c(/_UB_|DRESS/);i(f);const h=f.find(d=>!n.has(d))??"",u=/BEATER/.test(h)?"BEATER":/VEST/.test(h)?"LONGVEST":"SHIRT";for(const d of[/_ARMS$/,/BCHEST/,/_LEGS$/]){const p=c(d);i(p,d.source==="_ARMS$"?[new RegExp(`${a}_${u}_ARMS`),new RegExp(a)]:[new RegExp(a)])}i(c(/HAIR/),[a==="NUBIAN"?/AFRO/:/CAUC/]),i(c(/HAT|CAP\d|BEANIE|BANDANA/),[/NOHAT/]),i(c(/GLASS|GOGGLE/),[/NOGLASS/]),i(c(/_LB_.*(PANT|JEANS|SKIRT)|LEGS_.*SKIRT/)),i(c(/BOOT/)),i(c(/GEAR|BACKPACK|STRAPS|LEATHER_VEST|TACTICAL_VEST|FLACK_JACKET|VESTSPORT/),[/NOGEAR/]);for(const d of["BRACELET","NECKLACE"])i(c(new RegExp(d)));return n}const Oi={16:{normal:-1,skin:!1},32:{normal:16,skin:!1},40:{normal:24,skin:!0}},Fb=new Set(["Leaf","BigLeaf","Grass","Hair","Decal"]),Ii=(t,...e)=>{for(const n of e)if(t[n])return t[n]};function Di(t){const e=Mr(t.nodes).map(l=>Vt(l.offset)),n=t.o2bm.map(l=>Vt(l)),s=new Float64Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]);if(!n.length)return{nodes:[],frame:s};const o=(l,c)=>Math.hypot(l[12]-c[12],l[13]-c[13],l[14]-c[14])+Math.hypot(...[0,1,2,4,5,6,8,9,10].map(f=>l[f]-c[f])),r=(l,c)=>{const f=[];for(const h of n){const u=Lt(l,h);let d=-1,p=.02;if(e.forEach((m,g)=>{const w=o(m,u);w<p&&(p=w,d=g)}),f.push(d),f.length>=c&&f.every(m=>m<0))break}return f};let a={n:-1,nodes:[],frame:s};const i=r(s,n.length);return a={n:i.filter(l=>l>=0).length,nodes:i,frame:s},a.n<n.length&&e.forEach((l,c)=>{const f=Lt(l,Vt(n[0])),h=r(f,8);if(h.length<n.length&&h.every(p=>p<0))return;const u=h.length===n.length?h:r(f,n.length),d=u.filter(p=>p>=0).length;d>a.n&&(a={n:d,nodes:u,frame:f})}),{nodes:a.nodes,frame:a.frame}}function ip(t,e){const n=new Map(t.map((o,r)=>[o.name.toLowerCase(),r])),s=[];return t.forEach((o,r)=>{s[r]=o.parent>=0?Lt(s[o.parent],o.local):Float64Array.from(o.local)}),t.map((o,r)=>{const a=e.get(o.name.toLowerCase()),i=a===void 0?void 0:n.get(a);return i===void 0||i===o.parent||i>=r?o:{...o,parent:i,local:Lt(Vt(s[i]),s[r])}})}function Mr(t,e){const n=t.map(o=>{const r=new Float64Array(16);return Ae(o.pos,o.rot,o.scale,r),r}),s=[];return t.forEach((o,r)=>{s[r]=o.parent>=0&&o.parent<r?Lt(s[o.parent],n[r]):n[r]}),t.map((o,r)=>({name:o.name,parent:o.parent>=0&&o.parent<r?o.parent:-1,local:n[r],offset:e?Lt(Vt(s[r]),e):Vt(s[r])}))}function lp(t,e=!1){const n=t.replace(/\//g,"\\").toLowerCase().split("\\"),s=n[n.length-1].replace(/\.xbg$/,""),o=n.slice(0,-1),r=[`${o.join("\\")}\\${s}_ref.skeleton`];for(let a=o.length;a>=2;a--){const i=o.slice(0,a).join("\\");r.push(`${i}\\hips_ref.skeleton`,`${i}\\pelvis_ref.skeleton`,`${i}\\_common\\pelvis_ref.skeleton`)}return e&&r.push("graphics\\characters\\_common\\pelvis_ref.skeleton"),r}async function Pi(t,e,n,s=0,o){const r=o??Ci(t),a=r.lods[Math.min(s,r.lods.length-1)];if(!a)throw new Error("XBG: нет LOD");const i=new DataView(t.buffer,t.byteOffset,t.byteLength);for(const g of a.buffers)if(!Oi[g.stride])throw new Error(`XBG: вершина ${g.stride} байт (формат ${g.format.toString(16)}) пока не поддерживается`);const l=a.buffers.some(g=>Oi[g.stride].skin),c=async g=>{const w=e?.lookup(g);if(!e||!w)return null;try{return Li(await e.read(w))}catch{return null}},f=await Promise.all(r.materials.map(g=>{if(!n)return c(g);const w=g.toLowerCase();let y=n.get(w);return y||(y=c(g),n.set(w,y)),y})),[h,u]=r.pcmp,[d,p]=r.ucmp,m=[];if(a.prims.forEach((g,w)=>{const y=a.prims[w+1],M=y?y.firstIndex:a.icount,x=r.clusters[g.part]?.[g.cluster],v=g.firstVertex,b=g.lastVertex-v+1,k=a.buffers[g.buffer],E=Oi[k.stride];if(b<=0||M<=g.firstIndex)return;const A=new Float32Array(b*3),S=new Float32Array(b*3),$=new Float32Array(b*2),T=new Float32Array(b*4),U=new Float32Array(b*4);for(let I=0;I<b;I++){const B=a.vAt+k.base+(v+I)*k.stride;for(let z=0;z<3;z++)A[I*3+z]=i.getInt16(B+z*2,!0)*u+h;if($[I*2]=i.getInt16(B+8,!0)*p+d,$[I*2+1]=i.getInt16(B+10,!0)*p+d,E.normal>=0){const z=B+E.normal;S[I*3]=t[z+2]/127.5-1,S[I*3+1]=t[z+1]/127.5-1,S[I*3+2]=t[z]/127.5-1}if(E.skin&&x){let z=0;for(let j=0;j<4;j++){const X=t[B+16+j]/255;U[I*4+j]=X,z+=X,T[I*4+j]=x.palette[t[B+20+j]]??0}if(z>0)for(let j=0;j<4;j++)U[I*4+j]/=z;else U[I*4]=1}else U[I*4]=1}const _=M-g.firstIndex,R=b>65535?new Uint32Array(_):new Uint16Array(_);for(let I=0;I+2<_;I+=3){const B=a.iAt+(g.firstIndex+I)*2;R[I]=i.getUint16(B,!0)-v,R[I+1]=i.getUint16(B+4,!0)-v,R[I+2]=i.getUint16(B+2,!0)-v}for(let I=0;I<_;I++)if(R[I]>=b)throw new Error("XBG: индекс вне вершин примитива");const L=E.normal>=0?S:ge(A,R),O=r.parts[g.part],N=x?r.materials[x.mat]:void 0,D=x?f[x.mat]:null,F=(O?.name??`part${g.part}`).replace(/_LOD\d+$/i,""),C=D?Ii(D.textures,"DiffuseTexture1","SkinTexture","FabricTexture","DiffuseTexture","DiffuseTexture2"):void 0,P={name:`${F} · ${D?.name??N?.split("\\").pop()??"?"}`,texture:C??"",material:N,vertexCount:b,pos:A,nor:L,uv:$,bone:T,weight:U,indices:R,skinned:E.skin&&!!x};if(D){const I=Ii(D.textures,"NormalTexture1","NormalTexture"),B=Ii(D.textures,"SpecularTexture1","SpecularTexture");I&&(P.normalMap=I),B&&(P.surface={specular:{map:B,channel:0},source:"Far Cry 2 .xbm"}),P.shader=D.shader,Fb.has(D.shader)&&(P.alphaTest=!0)}(/STATE0*[2-9]$/i.test(F)||/_N_/i.test(F)||/LIGHT(HALO|CONE|BEAM)/i.test(D?.name??""))&&(P.defaultHidden=!0),m.push(P)}),!m.length)throw new Error("XBG: нет примитивов в ближнем LOD");return{x:r,lod:a,meshes:m,skinned:l,missing:r.materials.filter((g,w)=>!f[w])}}function cp(t,e){if(!t.nodes.length)return e;const n=new Kt(Mr(t.nodes,t.o2bm.length?Di(t).frame:void 0));return n.pose(null,0,!1),e.map(s=>{if(!s.skinned)return s;const o=new Float32Array(s.pos.length),r=new Float32Array(s.nor.length);for(let a=0;a<s.vertexCount;a++){const i=s.pos[a*3],l=s.pos[a*3+1],c=s.pos[a*3+2],f=s.nor[a*3],h=s.nor[a*3+1],u=s.nor[a*3+2];let d=0;for(let m=0;m<4;m++){const g=s.weight[a*4+m];if(!g)continue;const w=n.skinV[s.bone[a*4+m]];if(w){d+=g;for(let y=0;y<3;y++)o[a*3+y]+=g*(w[y]*i+w[4+y]*l+w[8+y]*c+w[12+y]),r[a*3+y]+=g*(w[y]*f+w[4+y]*h+w[8+y]*u)}}d||(o[a*3]=i,o[a*3+1]=l,o[a*3+2]=c,r[a*3]=f,r[a*3+1]=h,r[a*3+2]=u);const p=Math.hypot(r[a*3],r[a*3+1],r[a*3+2])||1;for(let m=0;m<3;m++)r[a*3+m]/=p}return{...s,pos:o,nor:r,skinned:!1,bone:new Float32Array(0),weight:new Float32Array(0)}})}async function _b(t,e){const n=t.archive??e.archives.find(p=>p.kind==="dunia-fat")??null,{x:s,lod:o,meshes:r,skinned:a,missing:i}=await Pi(new Uint8Array(await t.arrayBuffer()),n),l=Ri(r.map(p=>p.name.split(" · ")[0]));for(const p of r)l.has(p.name.split(" · ")[0])&&(p.defaultHidden=!0);let c=null,f=null,h=null;if(a&&s.nodes.length){h=s.o2bm.length?Di(s).frame:null;let p=Mr(s.nodes,h??void 0);const m=t.path??t.name;for(const g of n?lp(m,s.nodes.some(w=>w.name==="Pelvis")):[]){const w=n.lookup(g);if(!(!w||!w.size))try{f={path:g,bones:Ub(await n.read(w))};break}catch{}}if(f){const g=f.bones;p=ip(p,new Map(g.filter(w=>w.parent>=0).map(w=>[w.name.toLowerCase(),g[w.parent].name.toLowerCase()])))}c=new Kt(p)}const u=[`Far Cry 2 (Dunia): ${t.name}`,`LOD: ${s.lods.length} (показан ближний, до ${o.dist} м), примитивов ${o.prims.length}, вершин ${o.buffers.reduce((p,m)=>p+m.vcount,0)}${a?", скин":""}`,`Узлов: ${s.nodes.length}, частей: ${new Set(o.prims.map(p=>p.part)).size}, материалов: ${s.materials.length}`];l.size&&u.push(`Кит персонажа: показан один вариант на слот, скрыто частей ${l.size} (включаются в списке мешей)`),i.length&&u.push(`Не найдены материалы: ${i.slice(0,3).map(p=>p.split("\\").pop()).join(", ")}${i.length>3?"…":""}`),f&&u.push(`Скелет анимаций: ${f.path.split("\\").slice(-2).join("\\")} (${f.bones.length} костей)`);const d={meshes:r,skeleton:c,motions:[],bounds:c?Jt(c,r):dt(r),info:u,defaultZUp:!0};return c&&f&&Cb(d,c,s,f),d}function Cb(t,e,n,s){const o=new Map(n.nodes.map((c,f)=>[c.name.toLowerCase(),f])),r=s.bones.map(c=>o.get(c.name.toLowerCase())??-1),a=[];e.nodes.forEach((c,f)=>{a[f]=c.parent>=0?Lt(a[c.parent],c.local):Float64Array.from(c.local)});const i=new Map;let l;s.bones.forEach((c,f)=>{const h=r[f];if(c.parent>=0||h<0)return;const u=e.nodes[h].parent;u>=0&&i.set(f,Vt(a[u])),l||(l={bone:f,rot:op(a[h]),pos:[a[h][12],a[h][13],a[h][14]]})}),t.meta={...t.meta,duniaSkeleton:s.path},t.loadMotions=async c=>{const f=new Uint8Array(await c.arrayBuffer());return[Tb(c.name.replace(/\.mab$/i,""),Sb(f),r,i,l)]}}function Lb(t,e){const n=t.meta?.duniaSkeleton;if(!n||!t.loadMotions)return[];const s=n.replace(/\\/g,"/").toLowerCase().split("/").slice(0,-1).join("/")+"/",o=[];for(const r of e.entries){if(!r.path.endsWith(".mab")||!r.path.startsWith(s))continue;const a=r.path.slice(s.length).replace(/\.mab$/,"");o.push({kind:"node",name:a,tps:30,chan:new Map,duration:0,load:async()=>(await t.loadMotions(e.fileOf(r)))[0]})}return o.sort((r,a)=>r.name.localeCompare(a.name))}const fp=Object.freeze(Object.defineProperty({__proto__:null,bakeBindPose:cp,kitHiddenParts:Ri,listMabMotions:Lb,loadXbg:_b,o2bmNodes:Di,parseXbg:Ci,parseXbm:Li,readChunks:_i,reparentNodes:ip,skeletonCandidates:lp,xbgMeshes:Pi,xbgSkeletonNodes:Mr},Symbol.toStringTag,{value:"Module"}));function up(t){const e=new DataView(t.buffer,t.byteOffset,t.byteLength);if(t.length<16||e.getUint32(0,!0)!==1178686514)throw new Error("Не архив Dunia (нет «FAT2»)");const n=e.getUint32(4,!0);if(n!==5)throw new Error(`Архив Dunia версии ${n} не поддерживается (нужна 5 — Far Cry 2)`);const s=e.getUint32(12,!0);if(16+s*16>t.length)throw new Error(".fat: оглавление обрезано");const o=[];for(let r=0;r<s;r++){const a=16+r*16,i=e.getUint32(a+4,!0),l=e.getUint32(a+8,!0),c=e.getUint32(a+12,!0),f=i&3,h=l&1073741823;o.push({hash:e.getUint32(a,!0),size:f===0?h:i>>>2,scheme:f,csize:h,off:c*4+(l>>>30)})}return o}async function $s(t,e,n=0){if(e.scheme===0)return ht(t,e.off,e.off+(n?Math.min(n,e.csize):e.csize));if(e.scheme!==1)throw new Error(`Dunia: схема сжатия ${e.scheme} не поддерживается`);if(n){for(const o of n<=64?[512,2048]:[2048]){const r=new Uint8Array(Math.min(Math.max(n,1024),e.size));try{Ys(await ht(t,e.off,e.off+Math.min(e.csize,o)),0,r)}catch{}const a=r.subarray(0,Math.min(n,r.length));if(o>=e.csize||a.some(i=>i!==0))return a}return new Uint8Array(0)}const s=new Uint8Array(e.size);return Ys(await ht(t,e.off,e.off+e.csize),0,s),s}const Zn=t=>Do(new TextEncoder().encode(t.replace(/\//g,"\\").toLowerCase()))>>>0;function hp(t){const e=[];for(const n of t.matchAll(/ID="([^"]+\.[A-Za-z0-9]{2,5})"/g))e.push(n[1]);return e}const Ni=`DUNIA-LEVEL
|
|
201
|
+
`,Rb=["world1","world2","tmpla","mp_02_s_shanty","mp_06_m_ranch","mp_07_m_dogon","mp_08_m_colony","mp_10_l_fishingvillage","mp_11_l_savanna","mp_12_l_fueldepot","mp_14_woodlands","mp_15_mine","mp_16_airbase","mp_17_dunes","mp_18_greenhouse","mp_21_town","mp_24_dusttown","mp_25_prison","mp_dlc01_resort","mp_dlc02_lake","mp_dlc04_bridge","mp_dlc05_clinic","mp_dlc09_jungle","mp_dlc_gym"],zi=/\.fat$/i,Ob=/^(sound|sound_english|shadersobj)\.fat$/i,dp=t=>/^patch\.fat$/i.test(t)?3:/^common\.fat$/i.test(t)?0:/^worlds/i.test(t)?1:2,kr=t=>(t.webkitRelativePath||t.name).replace(/\\/g,"/"),Ib=t=>t.scheme===1&&t.size>=256*1024&&t.size/Math.max(1,t.csize)>=3;async function Db(t,e){const n=e.filter(x=>zi.test(x.name)&&!Ob.test(x.name));if(n.includes(t)||n.push(t),n.sort((x,v)=>dp(x.name)-dp(v.name)||kr(x).localeCompare(kr(v))),n[0]!==t)return null;const s=new Map,o=[];for(const x of n){const v=kr(x).replace(zi,".dat").toLowerCase(),b=e.find(k=>kr(k).toLowerCase()===v)??e.find(k=>k.name.toLowerCase()===x.name.replace(zi,".dat").toLowerCase());if(!b){o.push(x.name);continue}for(const k of up(new Uint8Array(await x.arrayBuffer())))s.set(k.hash,{fe:k,dat:b})}if(!s.size)throw new Error(`Нет .dat рядом с ${o.join(", ")||t.name}: выберите пары .fat + .dat или папку Data_Win32`);const r=new Map,a=new Set;for(const{fe:x,dat:v}of s.values()){if(!Ib(x))continue;const b=`${x.size}/${x.csize}`;if(a.has(b))continue;let k;try{k=await $s(v,x,16)}catch{continue}if(String.fromCharCode(...k.subarray(0,6))==="<root>"){a.add(b);try{const E=new TextDecoder("latin1").decode(await $s(v,x));for(const A of hp(E)){const S=Zn(A);s.has(S)&&!r.has(S)&&r.set(S,A)}}catch{}}}const i=new Set(Rb);for(const x of r.values()){const v=/^worlds\\([^\\]+)\\/i.exec(x);v&&i.add(v[1].toLowerCase())}const l=new Map;for(const x of i){const v=s.get(Zn(`worlds\\${x}\\generated\\${x}.game.xml`)),b=s.get(Zn(`worlds\\${x}\\generated\\${x}.sectorsdep.fcb`));v&&b&&l.set(x,{name:x,gx:v,end:b.fe.off,dat:b.dat,start:0})}for(const x of l.values())for(const v of l.values())v.dat===x.dat&&v.end<x.end&&v.end>x.start&&(x.start=v.end);const c=[];await Promise.all([...l.values()].map(async x=>{const v=(S,$,T,U,_,R=!1)=>({world:x.name,name:S,x0:$,y0:T,w:U,h:_,...R?{terrainOnly:R}:{}}),b=(S,$)=>{const T=Ni+JSON.stringify($);c.push({path:S,size:T.length,fe:x.gx.fe,dat:x.gx.dat,text:T})};let k=[],E=0,A=0;try{const{xmlElements:S}=await Promise.resolve().then(()=>Vb);for(const $ of S(await $s(x.gx.dat,x.gx.fe)))$.name==="GridMapSectors"&&!E&&(E=+$.attrs.CountX||0,A=+$.attrs.CountY||0),$.name==="Map"&&$.attrs.Name&&k.push({name:$.attrs.Name.split(/[\\/]/).pop().replace(/\.nomad$/i,""),x:+$.attrs.SectorOffsetX||0,y:+$.attrs.SectorOffsetY||0,cx:+$.attrs.CountX||0,cy:+$.attrs.CountY||0})}catch{k=[]}if(k=k.filter(S=>S.cx>0&&S.cy>0),k.length>1){const S=Math.max(...k.map(T=>T.x+T.cx)),$=Math.max(...k.map(T=>T.y+T.cy));b(`worlds/${x.name}/${x.name}.fc2level`,v(x.name,0,0,S,$,!0));for(const T of k)b(`worlds/${x.name}/maps/${T.name.toLowerCase()}.fc2level`,v(T.name,T.x,T.y,T.cx,T.cy))}else{const S=k[0];b(`worlds/${x.name}/${x.name}.fc2level`,v(S?.name??x.name,S?.x??0,S?.y??0,S?.cx||E||16,S?.cy||A||16))}}));const f=new Map(c.map(x=>[x.path,x])),h=[...c],u=new Map,d=(x,v)=>{let b=u.get(x);if(b)return b;let k=s.get(x);if(!k)return null;if(/\.xbt$/i.test(v)){const E=s.get(Zn(v.replace(/\.xbt$/i,"_mip0.xbt")));E&&E.fe.size>0&&(k=E)}return b={path:v,size:k.fe.size,fe:k.fe,dat:k.dat},u.set(x,b),b};for(const[x,v]of r)h.push(d(x,v.replace(/\\/g,"/").toLowerCase()));h.sort((x,v)=>x.path.localeCompare(v.path));const p=[...s.values()],m=new Map,g=x=>{let v=m.get(x);return v||(v=p.filter(b=>b.dat===x).sort((b,k)=>b.fe.off-k.fe.off),m.set(x,v)),v},w=x=>({name:x.name,gameXml:x.gx,dat:x.dat,end:x.end,files:()=>g(x.dat).filter(v=>v.fe.off>=x.start&&v.fe.off<x.end)}),y={duniaWorld(x){const v=l.get(x.toLowerCase());return v?w(v):null},duniaWorlds:()=>[...l.values()].map(w),datRefs:g,readRef:(x,v)=>$s(x.dat,x.fe,v),refByHash:x=>s.get(x>>>0),nameOfHash:x=>r.get(x>>>0)},M=Qt({name:n.length>1?`${n[0].name} … (${n.length} .fat)`:t.name,kind:"dunia-fat",version:5,entries:h,lookup:x=>{const v=x.replace(/\\/g,"/").toLowerCase();return f.get(v)??d(Zn(x),v)},read:x=>x.text!==void 0?Promise.resolve(new TextEncoder().encode(x.text)):$s(x.dat,x.fe),findTexture:x=>/\.xbt$/i.test(x)?d(Zn(x),x.replace(/\\/g,"/").toLowerCase()):null});return Object.assign(M,y)}const Bi=t=>t&&typeof t.duniaWorld=="function"?t:null,Pb=Object.freeze(Object.defineProperty({__proto__:null,LEVEL_MARK:Ni,duniaApi:Bi,duniaHash:Zn,namesFromResourceXml:hp,openDuniaArchive:Db,parseFat:up,readFatEntry:$s},Symbol.toStringTag,{value:"Module"})),Ne=t=>Do(new TextEncoder().encode(t))>>>0;function pp(t){const e=new DataView(t.buffer,t.byteOffset,t.byteLength);if(t.length<16||e.getUint32(0,!0)!==1178821230)throw new Error("Не .fcb (нет «nbCF»)");if(e.getUint16(4,!0)!==2)throw new Error(`.fcb версии ${e.getUint16(4,!0)} не поддерживается (нужна 2 — Far Cry 2)`);let n=16;const s=i=>{if(n+i>t.length)throw new Error(".fcb обрезан")},o=()=>{s(1);const i=t[n++];if(i<254)return[i,!1];s(4);const l=e.getUint32(n,!0);return n+=4,[l,i===254]},r=[],a=i=>{if(i>64)throw new Error(".fcb: слишком глубокая вложенность");const[l,c]=o();if(c){const u=r[l];if(!u)throw new Error(".fcb: ссылка на неизвестный объект");return u}const f={type:0,values:new Map,children:[]};r.push(f),s(4),f.type=e.getUint32(n,!0),n+=4;const[h]=o();for(let u=0;u<h;u++){s(4);const d=e.getUint32(n,!0);n+=4;const p=n,[m,g]=o();if(g){const w=n;n=p-m;const[y,M]=o();if(M)throw new Error(".fcb: двойная ссылка значения");s(y),f.values.set(d,t.subarray(n,n+y)),n=w}else s(m),f.values.set(d,t.subarray(n,n+m)),n+=m}for(let u=0;u<l;u++)f.children.push(a(i+1));return f};return a(0)}const Nb=t=>new DataView(t.buffer,t.byteOffset,t.byteLength),An=t=>{if(!t)return;let e=t.indexOf(0);return e<0&&(e=t.length),new TextDecoder("latin1").decode(t.subarray(0,e))},mp=t=>{if(!t||t.length<12)return;const e=Nb(t);return[e.getFloat32(0,!0),e.getFloat32(4,!0),e.getFloat32(8,!0)]};function Ss(t,e,n=[],s=new Set){if(s.has(t))return n;s.add(t),t.type===e&&n.push(t);for(const o of t.children)Ss(o,e,n,s);return n}function gp(t){if(t.length<5||t[0]!==0)throw new Error("Не двоичный XML Dunia");const e=new DataView(t.buffer,t.byteOffset,t.byteLength);let n=2;const s=()=>{if(n>=t.length)throw new Error("Двоичный XML обрезан");const u=t[n++];if(u<254)return u;if(u===254||n+4>t.length)throw new Error("Двоичный XML: неверное число");const d=e.getUint32(n,!0);return n+=4,d},o=s();s(),s();const r=u=>{if(u>256)throw new Error("Двоичный XML: слишком глубоко");const d=s(),p=s(),m=s(),g=s(),w=[];for(let M=0;M<m;M++)s(),w.push([s(),s()]);const y=[];for(let M=0;M<g;M++)y.push(r(u+1));return{n:d,v:p,a:w,c:y}},a=r(0);if(n+o>t.length)throw new Error("Двоичный XML: таблица строк обрезана");const i=t.subarray(n,n+o),l=new TextDecoder("utf-8"),c=new Map,f=u=>{let d=c.get(u);if(d===void 0){let p=i.indexOf(0,u);p<0&&(p=i.length),d=l.decode(i.subarray(u,p)),c.set(u,d)}return d},h=u=>({name:f(u.n),value:f(u.v),attrs:Object.fromEntries(u.a.map(([d,p])=>[f(d),f(p)])),children:u.c.map(h)});return h(a)}const zb=(t,e)=>t?.children.find(n=>n.name===e),Bb=(t,e)=>t?.children.filter(n=>n.name===e)??[];function wp(t){const e=[];if(t[0]===0){const o=(r,a)=>{e.push({name:r.name,attrs:r.attrs,depth:a});for(const i of r.children)o(i,a+1)};return o(gp(t),0),e}const n=new TextDecoder("utf-8").decode(t);let s=0;for(const o of n.matchAll(/<(\/?)([A-Za-z_][\w.-]*)([^>]*?)(\/?)>/g)){if(o[1]){s--;continue}const r={};for(const a of o[3].matchAll(/([\w.:-]+)\s*=\s*"([^"]*)"/g))r[a[1]]=a[2].replace(/"/g,'"').replace(/</g,"<").replace(/>/g,">").replace(/&/g,"&");e.push({name:o[2],attrs:r,depth:s}),o[4]||s++}return e}const Vb=Object.freeze(Object.defineProperty({__proto__:null,parseRml:gp,rmlAll:Bb,rmlChild:zb,xmlElements:wp},Symbol.toStringTag,{value:"Module"})),yp=t=>{if(t.length<160||new DataView(t.buffer,t.byteOffset,t.byteLength).getUint32(8,!0)!==136)return!1;const n=new TextDecoder("latin1").decode(t.subarray(24,136));return/realtrees?\\/i.test(n)};function jb(t){if(!yp(t))throw new Error("Не RealTree");const e=new DataView(t.buffer,t.byteOffset,t.byteLength),n=e.getUint32(20,!0);let s=t.indexOf(0,24);(s<0||s>136)&&(s=136);const o=new TextDecoder("latin1").decode(t.subarray(24,s)),r=Math.min(t.length,136+n);let a=1/0;const i=Array.from({length:12},(x,v)=>e.getUint32(280+v*4,!0));for(const x of[5,6,8,9,11])i[x]>65536&&(a=Math.min(a,i[x]));for(let x=136;x+4<=r;x+=4){const v=e.getUint32(x,!0);v>=16777216&&v<a&&(v&4294901760)===(a&4294901760)&&(a=v)}if(!Number.isFinite(a))throw new Error("RealTree: нет указателей");const l=a-864,c=x=>x-l+136,[f,h,u,d]=[i[4],i[5],i[10],i[11]],p=[];let m=null,g=0;for(let x=0;x<f;x++){const v=c(h)+x*76;if(v<136||v+76>r)break;const b=e.getFloat32(v+52,!0),k=e.getFloat32(v+56,!0),E=e.getFloat32(v+60,!0),A=e.getUint32(v+64,!0),S=e.getFloat32(v+68,!0);if(A>1e5||![b,k,E,S].every(Number.isFinite)||Math.max(Math.abs(b),Math.abs(k),Math.abs(E))>1e3)break;(!m||A>m.tips[m.tips.length-1])&&(m={pts:[],tips:[],scale:S>0&&S<10?S:1},p.push(m)),m.pts.push([b,k,E]),m.tips.push(A),g=Math.max(g,E)}const w=[];for(let x=0;x<u;x++){const v=c(d)+x*92;if(v<136||v+92>r)break;const b=A=>e.getFloat32(v+A,!0),k=[b(20),b(24),b(28)],E=[b(68),b(72),b(76)];![...k,...E].every(Number.isFinite)||Math.hypot(...E)>50||Math.hypot(...E)>.001&&w.push({n:k,a:E})}const y=[],M=new TextDecoder("latin1").decode(t.subarray(r,Math.min(t.length,r+4096)));for(const x of M.matchAll(/graphics\\[\w\\ .-]+?\.mlm/gi))y.push(x[0]);return{path:o,branches:p.filter(x=>x.pts.length>=2),cards:w,materials:y,height:g}}const Gb=.7,Xb=1.1,bp=(t,e)=>[t[0]-e[0],t[1]-e[1],t[2]-e[2]],Vi=(t,e)=>[t[1]*e[2]-t[2]*e[1],t[2]*e[0]-t[0]*e[2],t[0]*e[1]-t[1]*e[0]],ji=t=>{const e=Math.hypot(t[0],t[1],t[2])||1;return[t[0]/e,t[1]/e,t[2]/e]};function Hb(t,e,n,s=4){const o=Math.max(.02,t.height*.025),r=[],a=[],i=[],l=[];for(const h of t.branches){const u=h.tips[0]+1,d=r.length/3;let p=0;h.pts.forEach((m,g)=>{const w=ji(bp(h.pts[Math.min(g+1,h.pts.length-1)],h.pts[Math.max(g-1,0)])),y=Math.abs(w[2])<.9?[0,0,1]:[1,0,0],M=ji(Vi(w,y)),x=Vi(w,M),v=o*h.scale*Math.max(.15,Math.pow((h.tips[g]+1)/u,Xb));g&&(p+=Math.hypot(...bp(m,h.pts[g-1])));for(let b=0;b<=s;b++){const k=b/s*Math.PI*2,E=Math.cos(k),A=Math.sin(k),S=M[0]*E+x[0]*A,$=M[1]*E+x[1]*A,T=M[2]*E+x[2]*A;r.push(m[0]+S*v,m[1]+$*v,m[2]+T*v),a.push(S,$,T),i.push(b/s,p)}});for(let m=0;m+1<h.pts.length;m++)for(let g=0;g<s;g++){const w=d+m*(s+1)+g,y=w+s+1;l.push(w,y,w+1,w+1,y,y+1)}}const c=[],f=(h,u,d,p,m,g,w)=>({name:h,texture:u.texture,material:u.material,normalMap:u.normal,vertexCount:d.length/3,pos:Float32Array.from(d),nor:Float32Array.from(p),uv:Float32Array.from(m),bone:new Float32Array(0),weight:new Float32Array(0),indices:d.length/3>65535?Uint32Array.from(g):Uint16Array.from(g),skinned:!1,...w?{alphaTest:!0}:{}});if(l.length&&c.push(f("RealTree · ветви (приближённо)",e,r,a,i,l,!1)),t.cards.length){const h=[],u=[],d=[],p=[];let m=0;t.branches.forEach((g,w)=>{if(!(w===0&&t.branches.length>1))for(const y of[g.pts.length-1,Math.floor(g.pts.length/2)]){const M=g.pts[y],x=t.cards[m++%t.cards.length],v=x.a.map(A=>A*Gb),b=ji(x.n),k=Vi(b,v),E=h.length/3;for(const[A,S,$,T]of[[-1,-1,0,1],[1,-1,1,1],[1,1,1,0],[-1,1,0,0]])h.push(M[0]+v[0]*A+k[0]*S,M[1]+v[1]*A+k[1]*S,M[2]+v[2]*A+k[2]*S),u.push(b[0],b[1],b[2]),d.push($,T);p.push(E,E+1,E+2,E,E+2,E+3)}}),p.length&&c.push(f("RealTree · листва (приближённо)",n,h,u,d,p,!0))}return c}const ue=64,Ye=65,vp=3909095506,Gi=4e6,Wb=12e3,xp=1,Mp=3e6;function Kb(t){let e=t>>>0;return()=>{e=e+1831565813>>>0;let n=e;return n=Math.imul(n^n>>>15,n|1),n^=n+Math.imul(n^n>>>7,n|61),((n^n>>>14)>>>0)/4294967296}}const ae={Entity:Ne("Entity"),WorldSector:Ne("WorldSector"),hidName:Ne("hidName"),hidPos:Ne("hidPos"),hidAngles:Ne("hidAngles"),hidMeshName:Ne("hidMeshName"),X:Ne("X"),Y:Ne("Y"),model:3214621276,archetype:879405875,layer:1229720050,layerName:3312423428};function kp(t){const e=[];let n=0,s=0,o=1,r=1,a=-1;for(const i of wp(t)){if(i.name==="GridMapSectors"&&!n&&(n=+i.attrs.CountX||0,s=+i.attrs.CountY||0),i.name==="GridWorldMaps"&&(o=+i.attrs.CountX||1,r=+i.attrs.CountY||1),i.name==="Layers"){a=i.depth;continue}a>=0&&i.depth<=a&&(a=-1),a>=0&&i.depth===a+1&&i.name==="Layer"&&e.push({name:(i.attrs.Name??"").trim(),texture:i.attrs.Texture??"",normal:i.attrs.NormalMap??"",tiling:+i.attrs.Tiling||20})}return{layers:e,countX:n*o,countY:s*r}}function Ep(t){const e=new DataView(t.buffer,t.byteOffset,t.byteLength);if(t.length<592+Ye*Ye*4||e.getUint32(0,!0)!==vp)throw new Error("Не ландшафт сектора Far Cry 2");const n=new Float32Array(Ye*Ye),s=new Uint8Array(Ye*Ye);for(let r=0;r<Ye*Ye;r++)n[r]=e.getUint16(592+r*4,!0)/128,s[r]=t[592+r*4+3];const o=e.getFloat32(60,!0);return{id:e.getUint32(20,!0),min:e.getFloat32(28,!0),max:e.getFloat32(32,!0),layers:[t[36],t[37],t[38],t[39]],height:n,mask:s,water:Number.isFinite(o)&&o>0?o:0}}function Yb(t,e){for(let n=3;n>=0;n--)if(t&1<<n&&e[n]!==255)return e[n];for(const n of e)if(n!==255)return n;return-1}function Ap(t){const e=r=>r*Math.PI/360,n=[Math.cos(e(t[0])),Math.sin(e(t[0])),0,0],s=[Math.cos(e(t[1])),0,Math.sin(e(t[1])),0],o=[Math.cos(e(t[2])),0,0,Math.sin(e(t[2]))];return $p($p(o,s),n)}const $p=(t,e)=>[t[0]*e[0]-t[1]*e[1]-t[2]*e[2]-t[3]*e[3],t[0]*e[1]+t[1]*e[0]+t[2]*e[3]-t[3]*e[2],t[0]*e[2]-t[1]*e[3]+t[2]*e[0]+t[3]*e[1],t[0]*e[3]+t[1]*e[2]-t[2]*e[1]+t[3]*e[0]],Sp=t=>{const e=(n,s)=>{const o=An(n.values.get(ae.model));if(o)return o;if(!(s>6))for(const r of n.children){if(r.type===ae.Entity)continue;const a=e(r,s+1);if(a)return a}};return e(t,0)},Tp=t=>{for(const e of Ss(t,Ne("object"))){const n=An(e.values.get(ae.hidMeshName));if(n&&n.includes(";"))return n.split(";").filter(Boolean)}return[]};function Up(t){const e=new Map;for(const n of Ss(t,ae.Entity)){const s=An(n.values.get(ae.hidName)),o=Sp(n);s&&o&&!e.has(s)&&e.set(s,{model:o,parts:Tp(n)})}return e}const Zb=Ne("CNewParticlesComponent"),qb=Ne("CDynamicLightComponent"),Qb=423866672;function Fp(t,e){const n=[],s=new Set,o=[];let r=0;const a=(i,l)=>{if(i.type===ae.layer&&(l=An(i.values.get(ae.layerName))??l),i.type===ae.Entity){const c=mp(i.values.get(ae.hidPos)),f=An(i.values.get(ae.archetype));let h=Sp(i),u=Tp(i);if(!h&&f){const d=e.get(f);d?(h=d.model,u.length||(u=d.parts)):s.add(f)}if(c&&h)n.push({name:An(i.values.get(ae.hidName))??f??"entity",model:h,parts:u,pos:c,angles:mp(i.values.get(ae.hidAngles))??[0,0,0],layer:l,...f?{archetype:f}:{}});else if(c){r++;const d=An(i.values.get(ae.hidName))??"entity",p=Ss(i,Zb)[0];p?o.push({kind:"particle",name:An(p.values.get(Qb))??d,pos:c}):Ss(i,qb).length&&o.push({kind:"light",name:d,pos:c})}}for(const c of i.children)a(c,l)};return a(t,"main"),{list:n,noModel:r,unknownArchetype:s,markers:o}}function _p(t){const e=[];for(const n of Ss(t,290258663)){const s=n.children.find(u=>u.type===2710340740);if(!s)continue;const o=u=>{const d=s.values.get(u);if(!d||d.length<4)return null;const p=new DataView(d.buffer,d.byteOffset,d.byteLength),m=p.getUint32(0,!0);return m*4+4<=d.length?{dv:p,n:m}:null},r=o(2311635093),a=o(1487750114),i=o(2039231445),l=o(3531669696),c=o(2384941770),f=o(2563143869);if(!r||!a||!i||!l||!c||a.n!==r.n)continue;let h=0;for(let u=0;u<r.n;u++){const d=r.dv.getUint32(4+u*4,!0),p=a.dv.getUint32(4+u*4,!0);for(let m=0;m<p&&h<i.n;m++,h++){const g=i.dv.getUint32(4+h*4,!0),w=g&255,y=g>>>8;for(let M=y;M<y+w&&M<l.n&&M<c.n;M++){const x=l.dv.getUint16(4+M*4,!0)/10,v=l.dv.getUint16(6+M*4,!0)/10,b=c.dv.getFloat32(4+M*4,!0),k=f&&M<f.n?(f.dv.getUint32(4+M*4,!0)&65535)/65536*360:0;e.push({hash:d,pos:[x,v,b],yaw:k})}}}}return e}async function Ts(t,e,n){const s=new Array(t.length);let o=0;return await Promise.all(Array.from({length:Math.min(e,t.length)},async()=>{for(;o<t.length;){const r=o++;s[r]=await n(t[r])}})),s}const Cp=(t,e,n=52e4)=>{let s=1;for(;(Math.floor(t*ue/s)+1)*(Math.floor(e*ue/s)+1)>n&&s<ue;)s*=2;return s};function Lp(t,e,n,s,o){const r=t.w*ue/o+1,a=t.h*ue/o+1,i=r*a,l=new Float32Array(i).fill(NaN),c=new Int16Array(i).fill(-1),f=[1,2,3].map(()=>new Int16Array(i).fill(-1));for(let x=0;x<a;x++)for(let v=0;v<r;v++){const b=v*o,k=x*o;let E=Math.floor(b/ue),A=Math.floor(k/ue);E>=t.w&&(E=t.w-1),A>=t.h&&(A=t.h-1);const S=e.get((t.y0+A)*n+t.x0+E);if(!S)continue;const $=b-E*ue,T=k-A*ue,U=T*Ye+$,_=x*r+v,R=S.mask[U];l[_]=S.height[U];const L=S.layers.findIndex(O=>O!==255);c[_]=L>=0?S.layers[L]:-1;for(let O=1;O<=3;O++)O>L&&R&1<<O&&S.layers[O]!==255&&(f[O-1][_]=S.layers[O])}const h=new Float32Array(i*3),u=(x,v,b)=>{const k=l[Math.min(a-1,Math.max(0,v))*r+Math.min(r-1,Math.max(0,x))];return Number.isNaN(k)?b:k};for(let x=0;x<a;x++)for(let v=0;v<r;v++){const b=l[x*r+v];if(Number.isNaN(b))continue;const k=(u(v+1,x,b)-u(v-1,x,b))/(2*o),E=(u(v,x+1,b)-u(v,x-1,b))/(2*o),A=Math.hypot(k,E,1);h[(x*r+v)*3]=-k/A,h[(x*r+v)*3+1]=-E/A,h[(x*r+v)*3+2]=1/A}const d=[],p=new Int32Array(i),m=t.x0*ue,g=t.y0*ue,w=x=>!Number.isNaN(l[x])&&!Number.isNaN(l[x+1])&&!Number.isNaN(l[x+r])&&!Number.isNaN(l[x+r+1]),y=(x,v,b,k)=>{p.fill(-1);const E=[],A=[],S=[],$=[],T=[],U=s[x],_=(U?.tiling??20)/ue,R=(O,N)=>{const D=N*r+O;let F=p[D];if(F<0){F=p[D]=E.length/3;const C=m+O*o,P=g+N*o;E.push(C,P,l[D]),S.push(h[D*3],h[D*3+1],h[D*3+2]),A.push(C*_,P*_),k&&$.push(k(D))}return F};for(let O=0;O+1<a;O++)for(let N=0;N+1<r;N++){const D=O*r+N;if(!b(D)||!w(D))continue;const F=R(N,O),C=R(N+1,O),P=R(N,O+1),I=R(N+1,O+1);T.push(F,C,I,F,I,P)}if(!T.length)return;const L=E.length/3;d.push({name:v,texture:U?.texture??"",normalMap:U?.normal||void 0,vertexCount:L,pos:Float32Array.from(E),nor:Float32Array.from(S),uv:Float32Array.from(A),bone:new Float32Array(0),weight:new Float32Array(0),indices:L>65535?Uint32Array.from(T):Uint16Array.from(T),skinned:!1,...U?{}:{color:[.45,.42,.36]},...k?{vertexAlpha:Float32Array.from($)}:{}})},M=x=>s[x]?.name||(x<0?"без слоя":`слой ${x}`);for(const x of[...new Set(c)].sort((v,b)=>v-b))y(x,`Ландшафт · ${M(x)}`,v=>c[v]===x);return f.forEach((x,v)=>{for(const b of[...new Set(x)].filter(k=>k>=0).sort((k,E)=>k-E))y(b,`Ландшафт · ${M(b)} (наложение ${v+1})`,k=>x[k]===b||x[k+1]===b||x[k+r]===b||x[k+r+1]===b,k=>x[k]===b?1:0)}),d}function Rp(t,e,n,s){const o=[],r=[],a=ue/s;for(let f=0;f<t.h;f++)for(let h=0;h<t.w;h++){const u=e.get((t.y0+f)*n+t.x0+h);if(!u||!u.water||u.min>=u.water)continue;const d=(t.x0+h)*ue,p=(t.y0+f)*ue,m=u.water;for(let g=0;g<a;g++)for(let w=0;w<a;w++){let y=1/0;for(const[b,k]of[[0,0],[1,0],[0,1],[1,1]])y=Math.min(y,u.height[(g+k)*s*Ye+(w+b)*s]);if(y>=m)continue;const M=o.length/3,x=d+w*s,v=p+g*s;o.push(x,v,m,x+s,v,m,x,v+s,m,x+s,v+s,m),r.push(M,M+1,M+3,M,M+3,M+2)}}if(!r.length)return null;const i=o.length/3,l=new Float32Array(i*3),c=new Float32Array(i*2);for(let f=0;f<i;f++)l[f*3+2]=1,c[f*2]=o[f*3]/8,c[f*2+1]=o[f*3+1]/8;return{name:"Вода",texture:"",color:[.16,.3,.36],vertexCount:i,pos:Float32Array.from(o),nor:l,uv:c,bone:new Float32Array(0),weight:new Float32Array(0),indices:i>65535?Uint32Array.from(r):Uint16Array.from(r),skinned:!1,vertexAlpha:new Float32Array(i).fill(.7)}}const Xi=t=>t>=23536&&t<=26e3&&(t-23536)%12===0,Op=new WeakMap,Ip=new WeakMap;async function Dp(t,e){const n=e.filter(r=>Xi(r.fe.size)),s=await Ts(n,16,async r=>{try{return await t.readRef(r,24)}catch{return null}}),o=[];return s.forEach((r,a)=>{if(!r||r.length<24)return;const i=new DataView(r.buffer,r.byteOffset,r.byteLength);i.getUint32(0,!0)===vp&&i.getUint32(4,!0)===7&&o.push({ref:n[a],id:i.getUint32(20,!0)})}),o}function Jb(t,e){let n=Ip.get(t);n||(n=new Map,Ip.set(t,n));let s=n.get(e);return s||(s=(async()=>{const o=[];let r=null;for(const a of await Dp(t,t.datRefs(e)))(!r||r.terrain.has(a.id))&&(r={start:a.ref.fe.off,terrain:new Map,end:0},o.push(r)),r.terrain.set(a.id,a.ref),r.end=a.ref.fe.off;return o})(),n.set(e,s)),s}function Pp(t,e){let n=Op.get(t);n||(n=new Map,Op.set(t,n));let s=n.get(e.name);return s||(s=(async()=>{let o=e.files();const r=await Dp(t,o);let a=new Map(r.map(l=>[l.id,l.ref]));if(!a.size||a.size<r.length){const l=await Jb(t,e.dat),c=t.duniaWorlds().filter(h=>h.dat===e.dat).sort((h,u)=>h.end-u.end),f=c.findIndex(h=>h.name===e.name);if(l.length===c.length&&f>=0){const h=l[f],u=l[f+1]?.start??1/0;a=h.terrain,o=t.datRefs(e.dat).filter(d=>d.fe.off>=h.start&&d.fe.off<u)}}let i=0;for(const l of a.values())i=Math.max(i,l.fe.off);return{files:o,terrain:a,terrainEnd:i}})(),n.set(e.name,s)),s}function Np(t,e,n){return n.objects??=(async()=>{const s=new Map,o=async l=>{let f=0,h=0;for(let u=0;u<l.length&&f<2;u+=2048){const d=l.slice(u,u+2048),p=await Ts(d,32,async g=>{try{return await t.readRef(g,64)}catch{return null}});let m=0;p.forEach((g,w)=>{const y=g&&Hi(g);if(!y)return;const M=y[1]*65536+y[0];let x=s.get(M);x||(x=[],s.set(M,x)),x.push(d[w]),m++}),h+=m,f=m||!h?0:f+1}return h},r=n.files.filter(l=>l.fe.size>=64),a=r.filter(l=>l.fe.off<=n.terrainEnd&&!Xi(l.fe.size)),i=r.filter(l=>l.fe.off>n.terrainEnd);return a.length<Wb?(await o([...a,...i]),n.full=!0):await o(i)||(await o(a),n.full=!0),s})(),n.objects}function Hi(t){if(t.length<24)return null;const e=new DataView(t.buffer,t.byteOffset,t.byteLength);if(e.getUint32(0,!0)!==1178821230)return null;let n=16;if(t[n]>=254&&(n+=4),n++,e.getUint32(n,!0)!==ae.WorldSector)return null;n+=4;let s=t[n++];if(s>=254)return null;let o=-1,r=-1;for(;s>0&&n+5<=t.length;s--){const a=e.getUint32(n,!0),i=t[n+4];if(n+=5,i>=254||n+i>t.length)break;i===4&&a===ae.X&&(o=e.getUint32(n,!0)),i===4&&a===ae.Y&&(r=e.getUint32(n,!0)),n+=i}return o>=0&&r>=0?[o,r]:null}async function tv(t,e,n){const s=JSON.parse(t.slice(Ni.length)),o=e.archive??n.archives.find(M=>Bi(M))??null,r=Bi(o),a=r?.duniaWorld(s.world);if(!o||!r||!a)throw new Error("Уровень Far Cry 2 открывается из архивов игры (папка Data_Win32)");const i=kp(await r.readRef(a.gameXml)),l=i.countX||s.x0+s.w,c=await Pp(r,a),f=new Map,h=[];for(let M=s.y0;M<s.y0+s.h;M++)for(let x=s.x0;x<s.x0+s.w;x++){const v=c.terrain.get(M*l+x);v&&h.push(v)}await Ts(h,8,async M=>{try{const x=Ep(await r.readRef(M));f.set(x.id,x)}catch{}});const u=Cp(s.w,s.h),d=Lp(s,f,l,i.layers,u),p=Rp(s,f,l,u);p&&d.push(p);const m=new vs,g=[`Far Cry 2 (Dunia): ${s.world} · ${s.name} — сектора ${s.x0}…${s.x0+s.w-1} × ${s.y0}…${s.y0+s.h-1} (${s.w*64}×${s.h*64} м)`,`Ландшафт: секторов ${f.size} из ${s.w*s.h}, сетка ${u} м, слоёв ${d.length}`];let w=0;if(s.terrainOnly)g.push("Весь мир — только ландшафт; объекты — на картах worlds/…/maps/*.fc2level");else{const M=await Np(r,a,c),x=[],v=(b,k)=>b>=s.x0&&b<s.x0+s.w&&k>=s.y0&&k<s.y0+s.h;for(let b=s.y0;b<s.y0+s.h;b++)for(let k=s.x0;k<s.x0+s.w;k++)x.push(...M.get(b*65536+k)??[]);if(!c.full&&h.length){const b=Math.min(...h.map(S=>S.fe.off)),k=Math.max(...h.map(S=>S.fe.off)),E=c.files.filter(S=>S.fe.off>b-65536&&S.fe.off<k+65536&&S.fe.size>=64&&!Xi(S.fe.size));(await Ts(E,32,async S=>{try{return await r.readRef(S,64)}catch{return null}})).forEach((S,$)=>{const T=S&&Hi(S);T&&v(T[0],T[1])&&!x.includes(E[$])&&x.push(E[$])})}w=await ev(o,r,a.name,x,m,b=>g.push(b))}const y=[...m.build(),...d];if(!y.length)throw new Error("В области уровня нет секторов");return!w&&!s.terrainOnly&&g.push("Объекты не найдены"),g.push(p?`Вода: по уровню сектора (${[...new Set([...f.values()].map(M=>M.water).filter(Boolean))].slice(0,5).join(", ")} м), полупрозрачная плоскость там, где земля ниже`:"Воды в области нет"),g.push("Не показаны: небо; деревья RealTree — приближённо"),{meshes:y,instances:m.scene,skeleton:null,motions:[],bounds:dt(y),info:g,defaultZUp:!0}}async function ev(t,e,n,s,o,r){let a=new Map;const i=t.lookup(`worlds/${n}/generated/entitylibrary.fcb`);if(i)try{a=Up(pp(await t.read(i)))}catch{r("entitylibrary.fcb не прочитана")}const l=[],c=new Set;let f=0,h=0,u=0;const d=[];await Ts(s,8,async z=>{let j;try{j=pp(await e.readRef(z))}catch{return}const X=Fp(j,a);X.list.length&&h++,l.push(...X.list),f+=X.noModel,d.push(...X.markers);for(const J of _p(j)){const G=e.nameOfHash(J.hash);l.push({name:G?.split("\\").pop()?.replace(/\.xbg$/i,"")??`#${J.hash.toString(16)}`,model:`#${J.hash.toString(16).padStart(8,"0")}`,parts:[],pos:J.pos,angles:[0,0,J.yaw],layer:"collections"}),u++}for(const J of X.unknownArchetype)c.add(J)}),l.sort((z,j)=>z.name.localeCompare(j.name)||z.pos[0]-j.pos[0]||z.pos[1]-j.pos[1]||z.pos[2]-j.pos[2]);const p=new Map,m=new Map;for(const z of l){const j=z.layer==="collections"?m:p;j.set(z.model.toLowerCase(),(j.get(z.model.toLowerCase())??0)+1)}const g=new Map,w=new Set,y=new Map,M=async z=>{const j=await Promise.all(z.materials.map(async G=>{const H=t.lookup(G.replace(/\.mlm$/i,".xbm"));try{return H?{path:G,x:Li(await t.read(H))}:null}catch{return null}})),X=(G,...H)=>{for(const K of H)if(G?.textures[K])return G.textures[K]},J=G=>{const H=j.find(K=>K&&/trunk|bark/i.test(K.x.shader+K.x.name)===G)??j[G?0:1]??null;return{material:H?.path,texture:X(H?.x,"DiffuseTexture1","DiffuseTexture")??"",normal:X(H?.x,"NormalTexture1","NormalTexture")}};return Hb(z,J(!0),J(!1))},x=z=>z.startsWith("#")?e.nameOfHash(parseInt(z.slice(1),16))??z:z,v=async z=>{if(z.startsWith("#")){const X=e.refByHash(parseInt(z.slice(1),16));return X?e.readRef(X):null}const j=t.lookup(z);return j?t.read(j):null};await Ts([...new Set([...p.keys(),...m.keys()])],8,async z=>{try{const j=await v(z);if(!j){g.set(z,null);return}if(String.fromCharCode(j[0],j[1],j[2],j[3])!=="HSEM"){if(g.set(z,null),yp(j)){w.add(z);try{y.set(z,await M(jb(j)))}catch{}}return}g.set(z,{u:j,x:Ci(j)})}catch{g.set(z,null)}});const b=new Map,k=(z,j)=>{const X=y.get(z);if(X)return X.reduce((K,Q)=>K+Q.indices.length/3,0);const J=g.get(z),G=J?.x.lods[Math.min(j,(J?.x.lods.length??1)-1)];if(!J||!G)return 0;let H=0;return G.prims.forEach((K,Q)=>{const V=J.x.parts[K.part]?.name??"";/STATE0*[2-9]$|_N_/i.test(V.replace(/_LOD\d+$/i,""))||(H+=((G.prims[Q+1]?.firstIndex??G.icount)-K.firstIndex)/3)}),H};let E=0;for(const[z,j]of p)E+=j*k(z,0);const A=E;if(E>Gi){const z=[...p].sort((j,X)=>X[1]*k(X[0],0)-j[1]*k(j[0],0));for(const[j,X]of z){if(E<=Gi)break;const J=g.get(j);if(!J||J.x.lods.length<2)continue;const G=Math.min(2,J.x.lods.length-1);E-=X*(k(j,0)-k(j,G)),b.set(j,G)}}const S=z=>Math.min(xp,(g.get(z)?.x.lods.length??1)-1),$=new Map;let T=0;{const z=[...m].map(([X,J])=>({k:X,t:J*k(X,S(X))})).filter(X=>X.t>0).sort((X,J)=>X.t-J.t);let j=Mp;z.forEach((X,J)=>{T+=X.t;const G=Math.min(1,j/(z.length-J)/X.t);$.set(X.k,G),j-=G*X.t})}const U=new Map,_=new Map,R=new Set;let L=0;const O=(z,j,X)=>{const J=z.toLowerCase(),G=`${J}|${X}|${j.join(";")}`;let H=_.get(G);return H||(H=(async()=>{const K=y.get(J);if(K?.length)return{pi:o.proto(x(z),K,"static",V=>V.name),parts:K};const Q=g.get(J);if(!Q)return w.has(J)||R.add(z),null;try{const V=await Pi(Q.u,t,U,X,Q.x),W=V.skinned?cp(V.x,V.meshes):V.meshes;V.skinned&&L++;const Y=new Set(j.map(rt=>rt.toUpperCase())),tt=Y.size?new Set:Ri(W.map(rt=>rt.name.split(" · ")[0])),nt=W.filter(rt=>{const mt=rt.name.split(" · ")[0];return!rt.defaultHidden&&!tt.has(mt)&&(!Y.size||Y.has(mt.toUpperCase()))});if(!nt.length)return null;const et=rt=>rt.material?.split("\\").pop()||rt.texture.split("\\").pop()||"?";return{pi:o.proto(x(z),nt,"static",et),parts:nt}}catch{return R.add(z),null}})(),_.set(G,H)),H};let N=0,D=0,F=0;const C=[],P=Kb(24301);let I=0;for(const z of l){const j=z.layer==="collections",X=z.model.toLowerCase();if(j&&($.get(X)??1)<1&&P()>=$.get(X)){I++;continue}const J=await O(z.model,z.parts,j?S(X):b.get(X)??0);if(!J){w.has(z.model.toLowerCase())&&(F++,C.push({name:z.name,pos:z.pos}));continue}const G=new Float64Array(16);Ae(z.pos,Ap(z.angles),[1,1,1],G);const H=x(z.model),K=H.startsWith("#")?"collections":H.replace(/\\/g,"/").split("/").slice(0,-1).join("/")||"graphics";o.place(J.pi,J.parts,G,z.name,z.layer==="collections"?`collections/${K}`:K),N++,z.layer==="collections"&&D++}const B=(z,j,X,J,G)=>{if(!z.length)return;const H=_u(j,J,X),K=o.proto(j,[H],"marker",()=>j,!0);for(const Q of z){const V=new Float64Array(16);Ae(Q.pos,[1,0,0,0],[1,1,1],V),o.place(K,[H],V,Q.name,G)}};return B(d.filter(z=>z.kind==="particle"),"Метки · частицы",[.3,.8,1],.3,"particle"),B(d.filter(z=>z.kind==="light"),"Метки · свет",[1,.85,.3],.3,"light"),B(C,"Метки · деревья RealTree (не разобраны)",[.25,.75,.2],1.5,"realtree"),r(`Объекты: ${N} из ${l.length} с моделью (секторов с объектами ${h}, файлов секторов ${s.length}; моделей ${[..._.keys()].length}, треугольников ${o.tris})`),b.size&&r(`Дальний LOD у ${b.size} моделей (ближние LOD всех объектов — ${Math.round(A/1e6*10)/10} млн треугольников, бюджет ${Gi/1e6} млн)`),u&&r(`Коллекции (трава, кусты, камни, мусор — кисть редактора), LOD${xp}: ${D} из ${u}${I?` (массовые модели прорежены: всего ${Math.round(T/1e5)/10} млн треугольников, бюджет ${Mp/1e6} млн)`:""}${w.size?`; деревья и кусты RealTree (.rtr): моделей ${w.size}, приближённо — ветви трубками по узлам, листва карточками у концов ветвей (сетку игра строит сама)${F?`, не разобрано ${F} экземпляров`:""}`:""}`),f&&r(`Без модели (логика, точки, звук, частицы, свет): ${f}`),(d.length||C.length)&&r(`Метки (скрыты, включаются в списке мешей): частицы ${d.filter(z=>z.kind==="particle").length}, свет ${d.filter(z=>z.kind==="light").length}, деревья RealTree ${C.length}`),L&&r(`Модели со скелетом (люди, животные) — в bind-позе: ${L}`),R.size&&r(`Модели не найдены: ${R.size} (${[...R].slice(0,3).join(", ")}${R.size>3?"…":""})`),c.size&&r(`Архетипы не найдены в entitylibrary.fcb: ${c.size} (${[...c].slice(0,2).join(", ")})`),N}const nv=Object.freeze(Object.defineProperty({__proto__:null,anglesQuat:Ap,entityLibrary:Up,loadDuniaLevel:tv,parseTerrainSector:Ep,sectorCollections:_p,sectorEntities:Fp,terrainMeshes:Lp,terrainStep:Cp,vertexLayer:Yb,waterMesh:Rp,worldDescriptor:kp,worldIndex:Pp,worldObjects:Np,worldSectorXY:Hi},Symbol.toStringTag,{value:"Module"})),Wi=(t,e)=>String.fromCharCode(t[e],t[e+1],t[e+2],t[e+3]),sv=(t,e)=>(t[e]<<24|t[e+1]<<16|t[e+2]<<8|t[e+3])>>>0,$t=t=>t.replace(/^\{[0-9A-Fa-f]{16}\}/,"").replace(/\\/g,"/").replace(/^\/+/,"").toLowerCase(),zp=t=>t.length>=12&&Wi(t,0)==="FORM"&&Wi(t,8)==="PAC1";function Bp(t,e){const n=new DataView(t.buffer,t.byteOffset,t.byteLength),s=[],o=new TextDecoder;let r=0;const a=(i,l)=>{if(r+2>t.length||l>64)throw new Error("PAK: дерево FILE повреждено");const c=t[r],f=t[r+1],h=o.decode(t.subarray(r+2,r+2+f));if(r+=2+f,c===0){const u=n.getUint32(r,!0);r+=4;const d=h?`${i}${h}/`:i;for(let p=0;p<u;p++)a(d,l+1)}else{const u=n.getUint32(r,!0),d=n.getUint32(r+4,!0),p=n.getUint32(r+8,!0),m=t[r+18]!==0;r+=24,s.push({path:(i+h).toLowerCase(),size:p,off:u,csize:d,usize:p,zlib:m,file:e})}};return a("",0),s}async function Us(t){const e=await ht(t.file,t.off,t.off+t.csize);if(!t.zlib)return e;const n=await Lo(e);return n.length===t.usize?n:n.subarray(0,t.usize)}async function Vp(t,e){return t.zlib||t.csize<=e?Us(t):ht(t.file,t.off,t.off+e)}async function jp(t,e,n,s,o){const r=$t(e);for(const a of t){if(a.kind!=="enfusion-pak")continue;const i=a.lookup(r);if(!i)continue;if(i.zlib)return Us(i);const l=await ht(i.file,i.off,i.off+Math.min(i.csize,n)),c=s(l);return c===null||c>=i.csize?l.length>=i.csize?l:Us(i):c<=l.length?l.subarray(0,c):ht(i.file,i.off,i.off+c)}return o()}async function ov(t){const e=await ht(t,0,12);if(!zp(e))throw new Error("Не архив Enfusion (нет FORM/PAC1)");let n=12,s=null;for(;n+8<=t.size;){const a=await ht(t,n,n+8),i=Wi(a,0),l=sv(a,4);if(i==="FILE"){s=await ht(t,n+8,n+8+l);break}n+=8+l}if(!s)throw new Error(`${t.name}: в архиве нет оглавления FILE`);const o=Bp(s,t),r=new Map(o.map(a=>[a.path,a]));return Qt({name:t.name,kind:"enfusion-pak",version:1,entries:o,read:Us,lookup:a=>r.get($t(a))??null})}const rv=Object.freeze(Object.defineProperty({__proto__:null,enfusionPath:$t,isEnfusionPak:zp,openEnfusionPak:ov,parsePakTree:Bp,readPakEntry:Us,readPakHead:Vp,readPakPrefix:jp},Symbol.toStringTag,{value:"Module"})),Gp=542327876,Xp=808540228;function Ki(t,e,n,s=128){let o=0;for(let r=0;r<65536;r++){if(e+4>t.length)throw new Error("LZ4: цепочка обрезана");const a=t[e]|t[e+1]<<8|t[e+2]<<16,i=t[e+3];if(e+=4,o=Qf(t,e,e+a,n,o),e+=a,i&s)break}if(o!==n.length)throw new Error(`LZ4: распаковано ${o} из ${n.length} байт`);return e}function Hp(t,e){if(t.length<152)return null;const n=new DataView(t.buffer,t.byteOffset,t.byteLength);if(n.getUint32(0,!0)!==Gp)return null;const s=n.getUint32(12,!0),o=n.getUint32(16,!0),r=Math.max(1,n.getUint32(28,!0));let a=n.getUint32(80,!0)&4&&n.getUint32(84,!0)===Xp?148:128;const i=[];for(let c=0;c<r&&a+8<=t.length;c++){const f=String.fromCharCode(t[a],t[a+1],t[a+2],t[a+3]);if(f!=="COPY"&&f!=="LZ4 ")break;i.push(n.getUint32(a+4,!0)),a+=8}if(!i.length||i.length<r)return null;let l=a;for(let c=0;c<i.length;c++){l+=i[c];const f=r-1-c;if(Math.max(o>>f,s>>f)<=e&&(c+1===i.length||Math.max(o>>f-1,s>>f-1)>e))return l}return l}function Yi(t,e=2048){const n=new Uint8Array(t),s=new DataView(t);if(n.length<128||s.getUint32(0,!0)!==Gp)throw new Error("Не EDDS (нет сигнатуры DDS)");const o=s.getUint32(12,!0),r=s.getUint32(16,!0),a=Math.max(1,s.getUint32(28,!0)),i=s.getUint32(80,!0),l=s.getUint32(84,!0);let c=128,f=0;i&4&&l===Xp&&(f=s.getUint32(128,!0),c=148);const h=c,u=[];for(let v=0;v<a;v++){const b=String.fromCharCode(n[c],n[c+1],n[c+2],n[c+3]);if(b!=="COPY"&&b!=="LZ4 ")break;u.push({kind:b,size:s.getUint32(c+4,!0),off:0,level:0}),c+=8}if(!u.length)throw new Error("EDDS: нет таблицы мипов");u.forEach((v,b)=>{v.off=c,c+=v.size,v.level=a-1-b});let d=u[u.length-1];for(let v=u.length-1;v>=0;v--){const b=u[v];if(d=b,Math.max(r>>b.level,o>>b.level)<=e)break}const p=Math.max(1,r>>d.level),m=Math.max(1,o>>d.level);let g;if(d.kind==="COPY")g=n.subarray(d.off,d.off+d.size);else{const v=s.getUint32(d.off,!0);g=new Uint8Array(v),Ki(n,d.off+4,g,255)}const w=new DataView(g.buffer,g.byteOffset,g.byteLength),y=v=>({width:p,height:m,rgba:v});if(f)switch(f){case 71:case 72:return y(_e(w,0,p,m,1));case 74:case 75:return y(_e(w,0,p,m,3));case 77:case 78:return y(_e(w,0,p,m,5));case 80:case 81:return y(Oo(g,0,p,m,!1));case 83:case 84:return y(Oo(g,0,p,m,!0));case 98:case 99:return y(Io(g,0,p,m));case 28:case 29:return y(g.slice(0,p*m*4));case 87:case 88:case 91:case 93:{const v=new Uint8Array(p*m*4);for(let b=0;b<p*m;b++)v[b*4]=g[b*4+2],v[b*4+1]=g[b*4+1],v[b*4+2]=g[b*4],v[b*4+3]=f===88||f===93?255:g[b*4+3];return y(v)}default:throw new Error(`EDDS: формат DXGI ${f} не поддерживается`)}const M=new Uint8Array(h+g.length);M.set(n.subarray(0,h)),M.set(g,h);const x=new DataView(M.buffer);return x.setUint32(12,m,!0),x.setUint32(16,p,!0),x.setUint32(28,1,!0),Pn(M.buffer)}const av=Object.freeze(Object.defineProperty({__proto__:null,decodeEdds:Yi,eddsBytesNeeded:Hp,lz4Chain:Ki},Symbol.toStringTag,{value:"Module"})),Zi=(t,e)=>String.fromCharCode(t[e],t[e+1],t[e+2],t[e+3]),iv=(t,e)=>(t[e]<<24|t[e+1]<<16|t[e+2]<<8|t[e+3])>>>0;function lv(t){if(Zi(t,0)!=="FORM"||Zi(t,8)!=="ANIM")throw new Error("Не анимация Enfusion (нет FORM/ANIM)");let e=null,n=null;const s=(g,w,y)=>{for(;g+8<=w;){const M=Zi(t,g),x=iv(t,g+4);M==="HEAD"&&!e?e={off:g+8,size:x}:M==="DATA"&&!n?n={off:g+8,size:x}:/^SET\d$/.test(M)&&y<2&&s(g+8,g+8+x,y+1),g+=8+x}};if(s(12,t.length,0),!e||!n)throw new Error("ANM: нет HEAD/DATA");const o=e,r=n,a=new DataView(t.buffer,t.byteOffset,t.byteLength),i=a.getUint16(o.off,!0),l=a.getUint16(o.off+2,!0)||30,c=a.getUint16(o.off+4,!0),f=a.getUint16(o.off+6,!0),h=new TextDecoder().decode(t.subarray(o.off+8,o.off+8+f)).split("\0"),u=[];let d=o.off+8+f,p=r.off;const m=r.off+r.size;for(let g=0;g<c;g++,d+=32){const w=a.getUint16(d,!0),y=a.getUint16(d+2,!0),M=a.getUint32(d+4,!0),x=a.getFloat32(d+8,!0),v=a.getFloat32(d+12,!0),b=a.getFloat32(d+16,!0),k=a.getFloat32(d+20,!0),E=U=>{if(U>=i)return Array.from({length:U},(R,L)=>L);const _=[];for(let R=0;R<U;R++)_.push(a.getUint16(p+R*2,!0));return p+=U*2,_};if(p>m)throw new Error("ANM: данные обрезаны");const A=E(w),S=[];for(let U=0;U<w;U++,p+=6)S.push([A[U],[x+v*a.getUint16(p,!0),x+v*a.getUint16(p+2,!0),x+v*a.getUint16(p+4,!0)]]);const $=E(y),T=[];for(let U=0;U<y;U++,p+=8){const _=[0,2,4,6].map(L=>b+k*a.getUint16(p+L,!0)),R=Math.hypot(_[0],_[1],_[2],_[3])||1;T.push([$[U],[_[3]/R,_[0]/R,_[1]/R,_[2]/R]])}u.push({name:h[g]??`кость ${g}`,pos:S,rot:T,flags:M,additive:(M&65536)!==0})}if(p>m)throw new Error("ANM: данные обрезаны");return{frames:i,fps:l,tracks:u}}const cv=(t,e)=>[t[0]*e[0]-t[1]*e[1]-t[2]*e[2]-t[3]*e[3],t[0]*e[1]+t[1]*e[0]+t[2]*e[3]-t[3]*e[2],t[0]*e[2]-t[1]*e[3]+t[2]*e[0]+t[3]*e[1],t[0]*e[3]+t[1]*e[2]-t[2]*e[1]+t[3]*e[0]];function fv(t,e,n,s){const o=new Map;for(const r of t.tracks){const a=n.get(r.name.toLowerCase());if(a===void 0)continue;let i=r.pos.length?r.pos:null,l=r.rot.length?r.rot:null;const c=r.additive?{pos:i,rot:l}:void 0;if(r.additive){const f=s?.nodes[a];if(!f)continue;const h=Kr(f.local);i=i?i.map(([u,d])=>[u,[h.t[0]+d[0],h.t[1]+d[1],h.t[2]+d[2]]]):null,l=l?l.map(([u,d])=>[u,cv(h.q,d)]):null}o.set(a,c?{pos:i,rot:l,scl:null,add:c}:{pos:i,rot:l,scl:null})}return{kind:"node",name:e,tps:t.fps,chan:o,duration:Math.max(1,t.frames-1)/t.fps,frames:t.frames}}const Wp=t=>[t[3],t[0],t[1],t[2]];function Kp(t){const e=t.bones.length,n=new Array(e).fill(-1),s=65535;for(let f=0;f<e;f++){const h=t.bones[f].child;for(let u=h,d=0;u!==s&&u<e&&d<4096;d++)n[u]=f,u=t.bones[u].next}const o=new Array(e),r=[],a=new Uint8Array(e),i=f=>{a[f]||(a[f]=1,n[f]>=0&&i(n[f]),r.push(f))};for(let f=0;f<e;f++)i(f);for(const f of r){const h=t.bones[f],u=Xe(Wp(h.q),h.t);o[f]=n[f]>=0?Lt(o[n[f]],u):u}if(n.some((f,h)=>f>=h))throw new Error("XOB: порядок костей не подходит для скелета");const l=t.bones.map((f,h)=>({name:f.name,parent:n[h],local:Xe(Wp(f.q),f.t),offset:Vt(o[h]??nn())})),c=new Kt(l);return c.pose(null,0,!1),c}function Yp(t){const e=new Map,n=t.split(/\r?\n/),s=/^\s*(\w+)\s*(?::\s*"([^"]+)")?/.exec(t),o=s?.[1]??"",r=s?.[2],a=[];for(const i of n.slice(1)){const l=i.trim();if(!l)continue;if(l==="}"){a.pop();continue}const c=/^(\w+)\s*(.*?)\s*$/.exec(l);if(!c)continue;let[,f,h]=c;if(h.endsWith("{")){a.push(f);continue}h.startsWith('"')&&h.endsWith('"')&&(h=h.slice(1,-1)),e.set([...a,f].join("."),h)}return{cls:o,parent:r,props:e}}const qi=t=>t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055,Qi=t=>{const e=t?.split(/\s+/).map(Number);return e&&e.length>=3&&e.every(Number.isFinite)?[qi(e[0]),qi(e[1]),qi(e[2])]:null},uv=/Glass|Decal|TreeCrown|Grass|Particle|Cable/,hv=/^(MatLightPortal|MatLightSource|MatDummy|HDREffect|.*Effect|MatWaterPool|SubsurfaceProfile)$/;let Ji={key:null,mats:new Map,tex:new Map};const Zp=t=>{const e=t.archives[0]??null;return Ji.key!==e&&(Ji={key:e,mats:new Map,tex:new Map}),Ji},ne={limit:1024,lite:!1};function gn(t,e,n=ne.limit){if(n=Math.min(n,ne.limit),!t||!/\.edds$/i.test(t))return Promise.resolve(null);const s=Zp(e),o=`${$t(t)}@${n}`;let r=s.tex.get(o);return r||(r=(async()=>{const a=await jp(e.archives,t,512,i=>Hp(i,n),()=>e.readPath($t(t)));return a?{...Yi(a.slice().buffer,n),name:$t(t)}:null})().catch(()=>null),s.tex.size>96&&(s.tex.clear(),s.mats.clear()),s.tex.set(o,r)),r}function qp(t,e){const n=Zp(e),s=`${$t(t)}@${ne.limit}${ne.lite?"l":""}`;let o=n.mats.get(s);return o||(n.mats.size>512&&n.mats.clear(),o=dv(t,e),n.mats.set(s,o)),o}async function dv(t,e){let n="";const s=new Map,o=[];for(let c=t,f=0;c&&f<8;f++){const h=await e.readPath($t(c));if(!h){if(f===0)return null;break}const u=Yp(new TextDecoder().decode(h));n||(n=u.cls),o.unshift(u.props),c=u.parent}for(const c of o)for(const[f,h]of c)s.set(f,h);const r={cls:n,texturePath:"",uvSet:0};if(hv.test(n))return r.hidden=!0,r;const a=ne.lite?null:await gn(s.get("NMOMap")??s.get("GlobalNMOMap"),e);if(a&&(r.normal=Jp(a)),n==="MatPBRMulti"){if(r.uvSet=/2/.test(s.get("UVSrcGlobalMaps")??"")?1:0,!s.get("MaskMap")&&!s.get("GlobalMCRMap")&&!s.get("GlobalMCMap")&&s.get("BCR_1")){r.uvSet=0;const f=await gn(s.get("BCR_1"),e);if(f){const h=f.rgba.slice();for(let d=3;d<h.length;d+=4)h[d]=255;r.image={width:f.width,height:f.height,rgba:h,name:$t(s.get("BCR_1"))},r.texturePath=r.image.name;const u=ne.lite?null:await gn(s.get("NMO_1"),e);return u&&(r.normal=Jp(u)),r.surface=Qp(f,3,u,"Enfusion BCR_1 + NMO_1"),r}}const c=await pv(s,e,a);if(c){r.image=c.image,r.texturePath=c.path;const f=`#enf:${c.path}:surface`,h=c.surface;r.surface={roughness:{map:f,channel:1,image:h},metallic:{map:f,channel:2,image:h},ao:{map:f,channel:0,image:h},source:"Enfusion PBRMulti (слои по маске)"}}else r.color=Qi(s.get("Color_1"))??void 0;return r}const i=s.get("BCRMap")??s.get("AlbedoMap")??s.get("CamoAlbedoMap")??s.get("BCRMap2"),l=await gn(i,e);if(l){r.texturePath=$t(i);const c=await gn(s.get("OpacityMap"),e),f=l.rgba.slice(),h=/Glass/.test(n);if(c&&c.width===l.width&&c.height===l.height)for(let u=3;u<f.length;u+=4)f[u]=c.rgba[u-3];else if(!/Particle|MatCommon/.test(n))for(let u=3;u<f.length;u+=4)f[u]=h?90:255;r.image={width:l.width,height:l.height,rgba:f,name:r.texturePath},c&&uv.test(n)&&!h&&(r.alphaTest=!0),r.surface=Qp(/_bcr\.edds$/i.test(i)?l:null,3,a,"Enfusion BCR + NMO")}else r.color=Qi(s.get("Color"))??(/Glass/.test(n)?[.6,.7,.75]:void 0);return r}function Qp(t,e,n,s){if(!t&&!n)return;const o={source:s};return t&&(o.roughness={map:`#enf:${t.name??"bcr"}`,channel:e,image:t}),n?(o.metallic={map:`#enf:${n.name??"nmo"}`,channel:2,image:n},o.ao={map:`#enf:${n.name??"nmo"}`,channel:3,image:n}):o.metallicFactor=0,o}function Jp(t){const e=t.rgba,n=new Uint8Array(e.length);for(let s=0;s<e.length;s+=4){const o=e[s]/127.5-1,r=e[s+1]/127.5-1;n[s]=e[s],n[s+1]=e[s+1],n[s+2]=Math.round((Math.sqrt(Math.max(0,1-o*o-r*r))*.5+.5)*255),n[s+3]=255}return{width:t.width,height:t.height,rgba:n,name:t.name}}async function pv(t,e,n){const s=[];for(let w=1;w<=4;w++){if(w>1&&t.get(`Enabled_${w}`)==="0"){s.push(null);continue}const y=Qi(t.get(`Color_${w}`)),M=await gn(t.get(`BCR_${w}`),e,64);if(!y&&!M){s.push(null);continue}let x=[.5,.5,.5],v=.6;if(M?.rgba){const A=[0,0,0,0],S=M.width*M.height;for(let $=0;$<S;$++)for(let T=0;T<4;T++)A[T]+=M.rgba[$*4+T];x=A.slice(0,3).map($=>$/S/255),v=A[3]/S/255}const b=y?y.map((A,S)=>Math.min(1,A*x[S]/.5)):x,k=Number(t.get(`Roughness_${w}`)??1),E=Number(t.get(`Metalness_${w}`)??0);s.push({c:b,r:Math.min(1,Math.max(.02,v*(Number.isFinite(k)?k:1))),m:Math.min(1,Math.max(0,Number.isFinite(E)?E:0))})}const o=t.get("MaskMap"),r=t.get("GlobalMCRMap")??t.get("GlobalMCMap"),a=ne.lite?128:1024,i=await gn(o,e,a),l=await gn(r,e,a),c=s[0]??s.find(w=>w)??null;if(!c)return null;const f=l??i;if(!f)return null;const h=f.width,u=f.height,d=new Uint8Array(h*u*4),p=new Uint8Array(h*u*4),m=(w,y,M,x)=>{if(!w?.rgba)return-1;const v=Math.min(w.width-1,y*w.width/h|0),b=Math.min(w.height-1,M*w.height/u|0);return w.rgba[(b*w.width+v)*4+x]/255};for(let w=0;w<u;w++)for(let y=0;y<h;y++){let M=c.c.slice(),x=c.r,v=c.m;for(let E=1;E<4;E++){const A=s[E];if(!A)continue;const S=m(i,y,w,E-1);S>0&&(M=M.map(($,T)=>$+(A.c[T]-$)*S),x+=(A.r-x)*S,v+=(A.m-v)*S)}const b=(w*h+y)*4;for(let E=0;E<3;E++){const A=l?Math.min(1.6,m(l,y,w,E)*2):1;d[b+E]=Math.min(255,M[E]*A*255)}d[b+3]=255;const k=l?m(l,y,w,3):.5;p[b]=n?Math.round(Math.max(0,m(n,y,w,3))*255):255,p[b+1]=Math.round(Math.min(1,x*Math.min(2,k*2))*255),p[b+2]=Math.round(v*255),p[b+3]=128}const g=$t(r??o??"");return{image:{width:h,height:u,rgba:d,name:g},surface:{width:h,height:u,rgba:p,name:g+":surface"},path:g}}const tl=268435456,el=536870912,$n=(t,e)=>String.fromCharCode(t[e],t[e+1],t[e+2],t[e+3]),t0=(t,e)=>(t[e]<<24|t[e+1]<<16|t[e+2]<<8|t[e+3])>>>0;function ho(t){if($n(t,0)!=="FORM"||$n(t,8)!=="XOB9")throw new Error($n(t,0)==="FORM"?`XOB: версия ${$n(t,8)} не поддерживается`:"Не модель Enfusion (нет FORM/XOB9)");const e=Math.min(t.length,t0(t,4)+8),n=new Map;let s=12;for(;s+8<=e;){const o=$n(t,s),r=t0(t,s+4);n.set(o,{off:s+8,size:r}),s+=8+r}return n}function Er(t,e){const n=new DataView(t.buffer,t.byteOffset,t.byteLength),s=n.getFloat32(e+40,!0);let o=e+44;const r=n.getUint16(o,!0),a=n.getUint16(o+2,!0),i=t[o+4],l=t[o+5];n.getUint32(o+8,!0);const c=n.getUint32(o+12,!0);o+=16;const f=new TextDecoder().decode(t.subarray(o,o+c)).replace(/\0$/,"").split("\0");o+=c;const h=m=>f[m]??"",u=[];for(let m=0;m<r;m++,o+=4)u.push({name:h(n.getUint16(o,!0)),path:h(n.getUint16(o+2,!0))});const d=[];for(let m=0;m<a;m++,o+=36){const g=w=>n.getFloat32(o+4+w*4,!0);d.push({name:h(n.getUint32(o,!0)),t:[g(0),g(1),g(2)],q:[g(3),g(4),g(5),g(6)],next:n.getUint16(o+32,!0),child:n.getUint16(o+34,!0)})}o+=l*12;const p=[];for(let m=0;m<i;m++){if($n(t,o)!=="LZO4"&&m===0){for(let x=1;x<=64;x++)if($n(t,o+x)==="LZO4"){o+=x;break}}if($n(t,o)!=="LZO4")throw new Error(`XOB: описание LOD ${m} не найдено`);const g=n.getUint32(o+4,!0),w=n.getFloat32(o+12,!0),y=n.getUint32(o+16,!0),M={factor:w,offset:n.getUint32(o+20,!0),csize:n.getUint32(o+24,!0),size:n.getUint32(o+28,!0),sections:[]};o+=32;for(let x=0;x<g;x++){const v=n.getUint32(o,!0),b=o+44,k=t[b+14],E=t[b+15],A={name:(()=>{const S=h(v&65535);return S.startsWith("{")?"":S})(),format:v,tris:n.getUint16(b,!0)|n.getUint16(b+18,!0)<<16,verts:n.getUint16(b+2,!0),tverts:n.getUint16(b+4,!0),weights:n.getUint16(b+6,!0),material:n.getUint16(b+12,!0),uvSets:[],palette:[]};o=b+20;for(let S=0;S<k;S++,o+=20)A.uvSets.push({u0:n.getFloat32(o,!0),u1:n.getFloat32(o+4,!0),v0:n.getFloat32(o+8,!0),v1:n.getFloat32(o+12,!0)});for(let S=0;S<E;S++)A.palette.push(t[o++]);M.sections.push(A)}o+=y*28,p.push(M)}return{radius:Number.isFinite(s)&&s>0?s:1,strings:f,materials:u,bones:d,lods:p}}function e0(t){const e=(t.format&tl)!==0,n=t.uvSets.length;return t.tris*12+t.verts*((e?16:12)+4+(t.format&el?4:0)+4*n)+t.tverts*(4+8*Math.max(1,n))+(e?t.weights*16:0)}const mv=(t,e,n)=>{let s=(t>>>21)/1023-1,o=(t>>>10&2047)/1023-1,r=(t&1023)/511-1;const a=Math.hypot(s,o,r)||1;s/=a,o/=a,r/=a,e[n]=s,e[n+1]=o,e[n+2]=r};function n0(t,e){const n=new Uint8Array(e.size);Ki(t,e.offset,n,128);const s=new DataView(n.buffer),o=[];let r=0;for(const a of e.sections){const i=r,l=(a.format&tl)!==0,c=a.uvSets.length,f=a.verts,h=new Uint16Array(a.tris*3);for(let y=0;y<h.length;y++)h[y]=s.getUint16(r+y*2,!0);r+=a.tris*12;const u=new Float32Array(f*3),d=l?new Uint32Array(f):null;for(let y=0;y<f;y++)u[y*3]=s.getFloat32(r,!0),u[y*3+1]=s.getFloat32(r+4,!0),u[y*3+2]=s.getFloat32(r+8,!0),d&&(d[y]=s.getUint32(r+12,!0)),r+=l?16:12;const p=new Float32Array(f*3);for(let y=0;y<f;y++,r+=4)mv(s.getUint32(r,!0),p,y*3);a.format&el&&(r+=f*4);const m=a.uvSets.map(()=>new Float32Array(f*2));for(let y=0;y<f;y++)for(let M=0;M<c;M++,r+=4){const x=a.uvSets[M],v=s.getInt16(r,!0)/32767*.5+.5,b=s.getInt16(r+2,!0)/32767*.5+.5;m[M][y*2]=x.u0+(x.u1-x.u0)*v,m[M][y*2+1]=x.v0+(x.v1-x.v0)*b}r+=a.tverts*(4+8*Math.max(1,c));let g,w;if(l){const y=r;r+=a.weights*16,g=new Float32Array(f*4),w=new Float32Array(f*4);for(let M=0;M<f;M++){const x=d[M];if(x>=a.weights){w[M*4]=1;continue}const v=y+x*16,b=s.getFloat32(v+4,!0),k=s.getFloat32(v+8,!0),E=s.getFloat32(v+12,!0),A=[b,k,E,Math.max(0,1-b-k-E)];for(let S=0;S<4;S++)g[M*4+S]=a.palette[n[v+S]]??0,w[M*4+S]=A[S]}}if(r-i!==e0(a))throw new Error("XOB: размер секции не сходится");o.push({pos:u,nor:p,uv:m[0]??new Float32Array(f*2),uv2:m[1],indices:h,bone:g,weight:w})}if(r!==n.length)throw new Error(`XOB: данные LOD ${r} из ${n.length} байт`);return o}async function Ar(t,e,n,s=0){const o=ho(t),r=o.get("HEAD");if(!r)throw new Error("XOB: нет чанка HEAD");const a=Er(t,r.off);if(!a.lods.length)throw new Error("XOB: в модели нет LOD (только коллизии)");const i=a.lods[Math.max(0,Math.min(a.lods.length-1,s))],l=n0(t,i),c=new Map,f=[],h=[],u=[],d=new Set(i.sections.map(p=>p.name.toLowerCase()));for(const[p,m]of i.sections.entries()){const g=l[p],w=a.materials[m.material],y=w&&n?.get(w.name)||w?.path;if(!c.has(m.material)){const b=y?await qp(y,e).catch(()=>null):null;c.set(m.material,b),b?.image||h.push(y?.replace(/^\{[0-9A-F]+\}/i,"")??"?")}const M=c.get(m.material)??null,x=m.verts,v={name:m.name&&m.name!==w?.name?`${m.name} (${w?.name??m.material})`:`${w?.name??"материал "+m.material}`,texture:M?.texturePath??y??"",material:y,vertexCount:x,pos:g.pos,nor:g.nor,uv:g.uv,bone:g.bone??new Float32Array(x*4),weight:g.weight??new Float32Array(x*4).map((b,k)=>k%4?0:1),indices:g.indices,skinned:!!g.bone};M?.uvSet===1&&g.uv2&&(v.uv=g.uv2),M?.image?v.image=M.image:v.color=M?.color??[.7,.7,.7],M?.normal&&(v.normalImage=M.normal),M?.surface&&(v.surface=M.surface),M?.alphaTest&&(v.alphaTest=!0),(M?.hidden||!M?.image&&(/Decal/.test(M?.cls??"")||/decal|graffiti/i.test(y??"")))&&(v.defaultHidden=!0),/_wound$/i.test(m.name)&&d.has(m.name.toLowerCase().replace(/_wound$/,""))&&(v.defaultHidden=!0),f.push(v),u.push(m.name)}return{head:a,meshes:f,noTexture:h,lodCount:a.lods.length,sections:u}}async function gv(t,e){const n=new Uint8Array(await t.arrayBuffer()),{head:s,meshes:o,noTexture:r}=await Ar(n,e),a=[];if(!o.some(f=>!f.defaultHidden))for(const f of o)delete f.defaultHidden;const i=o.filter(f=>!f.defaultHidden);let l=null,c=[];if(s.bones.length&&o.some(f=>f.skinned))try{l=Kp(s),c=nl(e,l,t.path??"")}catch(f){a.push(`Скелет не построен: ${f.message}`)}if(!l)for(const f of o)f.skinned=!1;return a.unshift(`Enfusion XOB: LOD 1 из ${s.lods.length}, секций ${o.length}, вершин ${le(o)}, треугольников ${Ut(o)}${s.bones.length?`, костей ${s.bones.length}`:""}${l?`, анимаций в списке ${c.length}`:""}`),r.length&&a.push(`Без текстуры (цвет материала): ${r.slice(0,6).join(", ")}${r.length>6?"…":""}`),{meshes:o,skeleton:l,motions:c,bounds:l?Jt(l,i.length?i:o):dt(i.length?i:o),flipZ:!0,info:a}}function s0(t,e,n,s){const o=t.slice(t.lastIndexOf("/")+1);return/_add\.anm$/i.test(o)||/_offset\.anm$/i.test(o)?!1:e?/^[wvmi]_/.test(o)?!1:t.startsWith("anims/anm/")?!t.startsWith("anims/anm/animals/"):t.startsWith("assets/")&&!t.startsWith("assets/animals/")&&o.startsWith("p_"):n&&t.startsWith("anims/anm/animals/")?!0:t.startsWith(s)&&!o.startsWith("p_")}function o0(t,e){let n=0,s=0;for(const o of t.tracks){const r=o.name.toLowerCase();r==="scene_root"||r==="entityposition"||r==="collision"||r.endsWith("_ik_target")||(s++,e.has(r)&&n++)}return{hit:n,total:s}}function r0(t,e,n){for(const s of t.tracks){if(s.additive||!s.pos.length)continue;const o=s.name.toLowerCase(),r=n.get(o);if(r===void 0||/^(scene_root|entityposition|hips|root)$|prop|ik|target|camera|collision|weapon/.test(o))continue;const a=e.nodes[r],i=a.local,l=Math.hypot(i[12],i[13],i[14]),c=s.pos[0][1];if(Math.hypot(c[0],c[1],c[2])>Math.max(.5,l*4))return s.name}return null}const wv=/aimspace|breath|_add\.anm$|\/layered\//i;function nl(t,e,n,s){const o=new Set(e.nodes.map(y=>y.name.toLowerCase())),r=o.has("hips")&&o.has("spine1")&&o.has("head"),a=n.toLowerCase().split("/"),i=!r&&a[0]==="assets"&&a[1]==="animals",l=r?"anims/anm/":a.slice(0,Math.min(a.length-1,4)).join("/")+"/";if(!r&&a.length<3)return[];const c=new Map(e.nodes.map((y,M)=>[y.name.toLowerCase(),M])),f=new Set,h=c.get("spine1");h!==void 0&&e.nodes.forEach((y,M)=>{for(let x=M,v=0;x>=0&&v<256;x=e.nodes[x].parent,v++)if(x===h){f.add(M);break}});const u=y=>/aimspace/i.test(y.name)?y.frames===9?{grid:{cols:3,rows:3,x:1,y:1}}:{frame:Math.max(0,Math.floor(((y.frames??1)-1)/2)),mask:f}:/_ik$/i.test(y.name)?{frame:0,fill:!0}:{},d=(y,M,x)=>{const v=(M.path.startsWith("anims/anm/")?M.path.slice(10):M.path).replace(/\.anm$/,"");return{kind:"node",name:v,tps:30,chan:new Map,duration:0,source:M.path,...x?{asLayer:u}:{},load:async()=>{const b=lv(await y.read(M)),k=o0(b,c);if(!k.total||k.hit<k.total*.6)throw new Error(`анимация не для этого скелета: найдено костей ${k.hit} из ${k.total}`);if(r0(b,e,c))throw new Error("это служебная поза для IK движка (точки в координатах модели), а не анимация");const E=fv(b,v,c,e);return x&&(E.asLayer=u),E}}},p=[],m=[],g=new Set;for(const y of t.archives)for(const M of y.entries){if(!M.path.endsWith(".anm")||g.has(M.path))continue;const x=s0(M.path,r,i,l),v=r&&(wv.test(M.path)&&M.path.startsWith("anims/anm/player/")&&!/_offset\.anm$/i.test(M.path)||M.path===s);!x&&!v||(g.add(M.path),x&&p.push(d(y,M,!1)),v&&m.push(d(y,M,!0)))}const w=y=>y.name.startsWith("assets/")?1:0;if(p.sort((y,M)=>w(y)-w(M)||y.name.localeCompare(M.name)),m.sort((y,M)=>y.name.localeCompare(M.name)),m.length){const y=new Map(m.map(x=>[x.name.slice(x.name.lastIndexOf("/")+1).toLowerCase(),x.name])),M=s?m.find(x=>x.source===s)?.name:void 0;for(const x of p){x.layerPool=m,x.layerAuto=[...M?[M]:[],...a0(x.name,y)];const v=x.load;x.load=async()=>{const b=await v();return b.layerPool=x.layerPool,b.layerAuto=x.layerAuto,b}}}return p}function a0(t,e){const n=t.slice(t.lastIndexOf("/")+1).toLowerCase(),s=/^p_([a-z0-9]+)_(erc|cro|pne)_.*?(ras|ads|low)(?:_|$)/.exec(n);if(!s||/aimspace/.test(n))return[];for(const o of[`p_${s[1]}_${s[2]}_aimspace_body_${s[3]}`,`p_${s[1]}_${s[2]}_aimspace_${s[3]}`]){const r=e.get(o);if(r)return[r]}return[]}const i0=Object.freeze(Object.defineProperty({__proto__:null,XOB_COLORS:el,XOB_SKINNED:tl,anmCoverage:o0,anmFits:s0,anmIsModelSpace:r0,autoAimLayer:a0,listAnms:nl,loadXob:gv,parseXobHead:Er,readXobLod:n0,sectionSize:e0,xobChunks:ho,xobMeshes:Ar},Symbol.toStringTag,{value:"Module"}));function yv(t){const e=[];let n=0;const s=t.length;for(;n<s;){const o=t[n];if(o===`
|
|
202
|
+
`){e.push(null),n++;continue}if(o===" "||o===" "||o==="\r"){n++;continue}if(o==="/"&&t[n+1]==="/"){for(;n<s&&t[n]!==`
|
|
203
|
+
`;)n++;continue}if(o==='"'){let a=n+1,i="";for(;a<s&&t[a]!=='"';)t[a]==="\\"&&a+1<s&&a++,i+=t[a],a++;e.push({t:i,q:!0}),n=a+1;continue}if(o==="{"||o==="}"||o===":"){e.push({t:o,q:!1}),n++;continue}let r=n;for(;r<s&&!/[\s{}"]/.test(t[r])&&!(t[r]===":"&&r>n&&/[\s"]/.test(t[r+1]??" "));)r++;e.push({t:t.slice(n,r),q:!1}),n=r}return e}function l0(t){const e=yv(t);let n=0;const s=r=>{for(;;){for(;n<e.length&&e[n]===null;)n++;if(n>=e.length)return;if(e[n].t==="}"&&!e[n].q){n++;return}const a=[];let i=!1;for(;n<e.length&&e[n]!==null;){const l=e[n];if(!l.q&&l.t==="{"){i=!0,n++;break}if(!l.q&&l.t==="}")break;a.push(l),n++}if(i){const l=a.findIndex(h=>!h.q&&h.t===":"),f={header:(l>=0?a.slice(0,l):a).filter(h=>h.q||h.t!=="+").map(h=>h.t),props:new Map,items:[],children:[]};(l>=0?a.slice(0,l):a).some(h=>!h.q&&h.t==="+")&&(f.append=!0),l>=0&&a[l+1]&&(f.parent=a[l+1].t),r.children.push(f),s(f);continue}if(a.length){if(a[0].q){a.length>1&&!a[1].q?r.props.set(a[0].t,a.slice(1).map(l=>l.t)):r.items.push(...a.map(l=>l.t));continue}r.props.set(a[0].t,a.slice(1).map(l=>l.t))}}},o={header:[],props:new Map,items:[],children:[]};return s(o),o}const c0=t=>t.header.join(" ");function sl(t,e){const n={header:e.header.length?e.header:t.header,parent:e.parent??t.parent,props:new Map(t.props),items:e.append?[...t.items,...e.items]:e.items.length?e.items:t.items,children:[]};e.append&&t.append&&(n.append=!0);for(const[o,r]of e.props)n.props.set(o,r);const s=new Set;for(const o of t.children){const r=e.children.find(a=>!s.has(a)&&c0(a)===c0(o));r?(s.add(r),n.children.push(sl(o,r))):n.children.push(o)}for(const o of e.children)s.has(o)||n.children.push(o);return n}const Fs=(t,e)=>t?.children.find(n=>n.header[0]===e||n.header[1]===e),Ft=(t,e)=>t?.props.get(e)?.join(" "),$r=(t,e)=>{const n=t?.props.get(e)?.map(Number);return n&&n.length>=3&&n.every(Number.isFinite)?[n[0],n[1],n[2]]:null},Sn=()=>{const t=new Float64Array(16);return t[0]=t[5]=t[10]=t[15]=1,t},qn=(t,e)=>{const n=new Float64Array(16);for(let s=0;s<4;s++)for(let o=0;o<4;o++){let r=0;for(let a=0;a<4;a++)r+=t[a*4+o]*e[s*4+a];n[s*4+o]=r}return n};function ol(t,e){const[n,s,o,r]=t,a=Sn();return a[0]=1-2*(s*s+o*o),a[1]=2*(n*s+o*r),a[2]=2*(n*o-s*r),a[4]=2*(n*s-o*r),a[5]=1-2*(n*n+o*o),a[6]=2*(s*o+n*r),a[8]=2*(n*o+s*r),a[9]=2*(s*o-n*r),a[10]=1-2*(n*n+s*s),a[12]=e[0],a[13]=e[1],a[14]=e[2],a}function Sr(t,e){const n=(e??[0,0,0]).map(d=>d*Math.PI/180),s=Math.cos(n[0]),o=Math.sin(n[0]),r=Math.cos(n[1]),a=Math.sin(n[1]),i=Math.cos(n[2]),l=Math.sin(n[2]),c=Sn(),f=Sn(),h=Sn();c[5]=s,c[6]=o,c[9]=-o,c[10]=s,f[0]=r,f[2]=-a,f[8]=a,f[10]=r,h[0]=i,h[1]=l,h[4]=-l,h[5]=i;const u=qn(f,qn(c,h));return t&&(u[12]=t[0],u[13]=t[1],u[14]=t[2]),u}function f0(t){const e=new Map,n=65535,s=(o,r,a)=>{for(let i=0;o!==n&&o<t.bones.length&&i<4096&&a<128;i++){const l=t.bones[o],c=qn(r,ol(l.q,l.t));e.has(l.name)||e.set(l.name,c),l.child!==n&&s(l.child,c,a+1),o=l.next}};return t.bones.length&&s(0,Sn(),0),e}let Tr={key:null,prefabs:new Map};function po(t,e,n=0){const s=$t(t),o=e.archives[0]??null;Tr.key!==o&&(Tr={key:o,prefabs:new Map});let r=Tr.prefabs.get(s);return r||(r=(async()=>{const a=await e.readPath(s);if(!a)return null;const i=l0(new TextDecoder().decode(a)).children[0];if(!i)return null;if(i.parent&&n<16){const l=await po(i.parent,e,n+1);if(l)return sl(l,i)}return i})().catch(()=>null),Tr.prefabs.set(s,r)),r}const u0=/SlotInfo$|StorageSlot$/,bv=/Emissive|Light|Compartment|Salvage|EquipmentStorage|Probe/,vv=/^(SupplyStorage|ShadowAO|Shadow)|Shadow/i,xv=/^(SlotManagerComponent|BaseLoadoutManagerComponent|BaseLoadoutClothComponent|AttachmentSlotComponent|WeaponComponent|MuzzleComponent|SightsComponent|.*AttachmentSlot.*|components|Slots)$/;async function rl(t,e,n={},s=[],o,r=!0){const a={out:[],warn:s,ctx:e,hide:new Set,bodyMap:null,weapons:r};if(await al(t,a,n),o)for(const i of a.hide)for(const l of a.bodyMap?.get(i.toLowerCase())??[`basebody_${i}`,`basebody_${i}_wound`])o.add(l.toLowerCase());return a.out}const Mv=t=>{const e=Sn();for(let n=0;n<3;n++)for(let s=0;s<3;s++)e[s*4+n]=t[n*4+s];for(let n=0;n<3;n++)e[12+n]=-(e[n]*t[12]+e[4+n]*t[13]+e[8+n]*t[14]);return e};async function al(t,e,n){if((n.depth??0)>8)return;const s=await po(t,e.ctx);if(!s){e.warn.push(`нет префаба ${$t(t)}`);return}await h0(s,t,e,n)}async function h0(t,e,n,s){const o=s.depth??0;let r=s.world??Sn();const a=Fs(t,"components"),i=a?.children.find(k=>k.header[0]==="MeshObject"),l=a?.children.find(k=>k.header[0]==="BaseLoadoutClothComponent"),c=Fs(l,"OverrideMeshesToHide");for(const k of c?c.items:s.slotHide??[])n.hide.add(k);const f=Ft(l,"WornModel")||Ft(i,"Object"),h=new Map;for(const k of Fs(i,"Materials")?.children??[]){const E=Ft(k,"SourceMaterial"),A=Ft(k,"AssignedMaterial");E&&A&&h.set(E,A)}const u=s.label||$t(e).replace(/^.*\//,"").replace(/\.et$/,"");let d=new Map;const p=!!f&&!/common\/models\/default\.xob$/i.test($t(f));if(p&&(d=new Map(await Ev(f,n.ctx))),s.childPivot){const k=d.get(s.childPivot)??[...d].find(([E])=>E.toLowerCase()===s.childPivot.toLowerCase())?.[1];k&&(r=qn(r,Mv(k)))}const m=/· тело$/.test(u),g=p?n.out.find(k=>$t(k.xob)===$t(f)&&k.world.every((E,A)=>Math.abs(E-r[A])<1e-6)):void 0;if(g?m&&(g.body=!0):p&&n.out.push({name:u,xob:f,world:r,remap:h,hidden:!!s.hidden,bindBone:s.bindBone,body:m}),s.parentBones)for(const[k,E]of s.parentBones)d.has(k)||d.set(k,E);const w=k=>d.get(k)??[...d].find(([E])=>E.toLowerCase()===k.toLowerCase())?.[1],y=d0(a,k=>k.header.includes("VisualIdentity")),M=y?.children.find(k=>k.header[0]==="BodyMeshesConfig");M?.parent&&!n.bodyMap&&(n.bodyMap=await kv(M.parent,n.ctx));for(const k of["Body","Head"]){const E=Ft(y,k);E&&await al(E,n,{world:r,label:`${u} · ${k==="Head"?"голова":"тело"}`,depth:o+1,hidden:s.hidden,parentBones:d,bindBone:s.bindBone})}const x=[],v=(k,E)=>{for(const A of k?.children??[])(u0.test(A.header[0]??"")||u0.test(A.header[1]??"")||A.header[0]==="AttachmentSlot")&&!bv.test(A.header.join(" "))?x.push({s:A,host:k}):E<6&&xv.test(A.header[0]??"")&&v(A,E+1)};v(a,0);const b=async(k,E,A,S={})=>{const $=Ft(E,"PivotID")??Ft(E.children.find(R=>R.props.has("PivotID")),"PivotID"),T=$?w($):void 0;$&&!T&&n.warn.push(`${u}: нет кости ${$}`);const U=qn(r,qn(T??Sn(),Sr($r(E,"Offset"),$r(E,"Angles")))),_=s.bindBone??(o===0&&T?$:void 0);await al(k,n,{world:U,label:`${u} · ${A}`,depth:o+1,hidden:!!s.hidden||vv.test(A),parentBones:T?void 0:d,bindBone:_,childPivot:Ft(E,"ChildPivotID"),...S})};for(const{s:k,host:E}of x){const A=Ft(k,"Prefab")||(k.header[0]==="MagazinePosition"?Ft(E,"MagazineTemplate"):void 0);A&&await b(A,k,k.header[0]==="MagazinePosition"?"магазин":k.header[k.header.length-1]??"слот",{slotHide:Fs(k,"MeshesToHide")?.items})}for(const k of t.children)if(!k.header.length)for(const E of k.children){if(o>=8)break;if(/Probe|Light|Sound|Trigger|Decal/i.test(E.header[0]??""))continue;let A=E;if(E.parent){const $=await po(E.parent,n.ctx);A=$?sl($,E):null}if(!A||!Fs(A,"components"))continue;const S=qn(r,Sr($r(E,"coords"),$r(E,"angles")));await h0(A,E.parent??e,n,{world:S,label:`${u} · ${E.header[0]??"дочерняя"}`,depth:o+1,hidden:s.hidden,bindBone:s.bindBone})}if(n.weapons&&a){const k=a.children.filter(A=>A.header[0]==="CharacterWeaponSlotComponent"&&Ft(A,"WeaponTemplate")).sort((A,S)=>+(Ft(A,"WeaponSlotIndex")??9)-+(Ft(S,"WeaponSlotIndex")??9)),E=Fs(a.children.find(A=>A.header[0]?.endsWith("CharacterControllerComponent")),"RightHandPosition");for(const[A,S]of k.entries()){const $=S.children.find(U=>U.header[0]==="AttachType");if(A>0&&(Ft(S,"HolsteredHidden")==="1"||!$))continue;const T=A===0?E??{props:new Map([["PivotID",["RightHandProp"]]]),children:[]}:$;A===0&&!Ft(T,"PivotID")&&T.props.set("PivotID",["RightHandProp"]),await b(Ft(S,"WeaponTemplate"),T,A===0?"оружие в руке":"оружие за спиной")}}}async function kv(t,e){const n=await e.readPath($t(t)).catch(()=>null);if(!n)return null;const s=new Map,o=r=>{const a=Ft(r,"PartType");a&&s.set(a.toLowerCase(),[Ft(r,"HealthyVersion"),Ft(r,"WoundVersion")].filter(i=>!!i)),r.children.forEach(o)};return o(l0(new TextDecoder().decode(n))),s.size?s:null}function d0(t,e,n=0){for(const s of t?.children??[]){if(e(s))return s;const o=n<6?d0(s,e,n+1):void 0;if(o)return o}}const Ur=new Map;async function Ev(t,e){const n=$t(t);let s=Ur.get(n);return s||(s=(async()=>{const o=await e.readPath(n);if(!o)return new Map;const{parseXobHead:r,xobChunks:a}=await Promise.resolve().then(()=>i0),i=a(o).get("HEAD");return i?f0(r(o,i.off)):new Map})().catch(()=>new Map),Ur.size>256&&Ur.clear(),Ur.set(n,s)),s}function Av(t,e){const n=new Float32Array(t.pos.length),s=new Float32Array(t.nor.length);for(let o=0;o<t.pos.length;o+=3){const r=t.pos[o],a=t.pos[o+1],i=t.pos[o+2];n[o]=e[0]*r+e[4]*a+e[8]*i+e[12],n[o+1]=e[1]*r+e[5]*a+e[9]*i+e[13],n[o+2]=e[2]*r+e[6]*a+e[10]*i+e[14];const l=t.nor[o],c=t.nor[o+1],f=t.nor[o+2];s[o]=e[0]*l+e[4]*c+e[8]*f,s[o+1]=e[1]*l+e[5]*c+e[9]*f,s[o+2]=e[2]*l+e[6]*c+e[10]*f}return{...t,pos:n,nor:s}}async function $v(t,e){const n=t.path??t.name,s=[],o=new Set,r=await rl(n,e,{},s,o);if(!r.length)throw new Error("В префабе нет моделей (MeshObject) — это служебная сущность или конфиг");const a=[],i=new Set,l=new Map,c=[];for(const v of r){const b=$t(v.xob);l.has(b)||l.set(b,await e.readPath(b));const k=l.get(b);if(!k){s.push(`нет модели ${b}`);continue}try{const E=await Ar(k,e,v.remap);E.noTexture.forEach(A=>i.add(A)),c.push({part:v,head:E.head,meshes:E.meshes,sections:E.sections})}catch(E){s.push(`${b}: ${E.message}`)}}const f=v=>v.every((b,k)=>Math.abs(b-(k%5===0?1:0))<1e-6),h=c.filter(v=>f(v.part.world)&&v.meshes.some(b=>b.skinned)).sort((v,b)=>b.head.bones.length-v.head.bones.length)[0]??c.filter(v=>f(v.part.world)&&v.head.bones.length>1).sort((v,b)=>b.head.bones.length-v.head.bones.length)[0];let u=null;if(h)try{u=Kp(h.head)}catch(v){s.push(`скелет: ${v.message}`)}const d=new Map((u?.nodes??[]).map((v,b)=>[v.name.toLowerCase(),b]));let p=0,m=0;for(const{part:v,head:b,meshes:k,sections:E}of c){const A=u&&f(v.world)?b.bones.map($=>d.get($.name.toLowerCase())??-1):null,S=u&&v.bindBone?d.get(v.bindBone.toLowerCase()):void 0;for(const[$,T]of k.entries()){const U=Av(T,v.world);if(U.name=`${v.name} · ${T.name}`,v.hidden&&(U.defaultHidden=!0),v.body&&o.has((E[$]??"").toLowerCase())&&(U.defaultHidden=!0,p++),T.skinned&&A&&A.some(_=>_>=0)){const _=new Float32Array(T.bone.length);for(let R=0;R<_.length;R++){const L=A[T.bone[R]]??-1;_[R]=L>=0?L:0}U.bone=_}else if(S!==void 0){const _=U.vertexCount,R=new Float32Array(_*4),L=new Float32Array(_*4);for(let O=0;O<_;O++)R[O*4]=S,L[O*4]=1;U.bone=R,U.weight=L,U.skinned=!0,m++}else U.skinned=!1;a.push(U)}}if(!a.length)throw new Error(`Модели префаба не загрузились: ${s.slice(0,3).join("; ")}`);if(!a.some(v=>!v.defaultHidden))for(const v of a)delete v.defaultHidden;const g=a.filter(v=>!v.defaultHidden),w=[`Enfusion префаб: частей ${r.length} (моделей ${new Set(r.map(v=>$t(v.xob))).size}), мешей ${a.length}, вершин ${le(g)}, треугольников ${Ut(g)}`,"Части в слотах (двери, колёса, крыша, стёкла, одежда, снаряжение, обвес, магазин, оружие) поставлены по костям родителя; грузы и тень скрыты (включаются в списке мешей)."];p&&w.push(`Под одеждой скрыто частей тела: ${p} (${[...o].filter(v=>!/_wound$/.test(v)).join(", ")})`),m&&w.push(`Прикреплено к костям скелета (следуют анимации): мешей ${m}`),i.size&&w.push(`Без текстуры: ${[...i].slice(0,6).join(", ")}${i.size>6?"…":""}`),s.length&&w.push(`Замечания: ${[...new Set(s)].slice(0,6).join("; ")}`);let y;const M=c.find(v=>v.part.bindBone?.toLowerCase()==="righthandprop");if(M){const v=$t(M.part.xob).replace(/[^/]*$/,"");y=e.archives.flatMap(k=>k.entries.filter(E=>E.path.startsWith(v)&&/\/p_[^/]*_ik\.anm$/.test(E.path)).map(E=>E.path)).sort((k,E)=>k.length-E.length)[0]}const x=u&&h?nl(e,u,$t(h.part.xob),y):[];return y&&w.push(`Хват оружия: ${y.replace(/^.*\//,"")} — слоем поверх анимаций тела`),u&&w.push(`Скелет: ${u.n} костей (${h.part.xob.replace(/^\{[0-9A-F]+\}/i,"")}), анимаций в списке ${x.length}`),{meshes:a,skeleton:u,motions:x,bounds:u?Jt(u,g):dt(g),flipZ:!0,info:w}}const Sv=Object.freeze(Object.defineProperty({__proto__:null,boneWorld:f0,collectParts:rl,loadPrefabModel:$v,quatMat:ol,resolvePrefab:po,slotMat:Sr},Symbol.toStringTag,{value:"Module"})),wn=t=>{if(!(t instanceof Uint8Array)||t.length!==12)return null;const e=new DataView(t.buffer,t.byteOffset,12);return[e.getFloat32(0,!0),e.getFloat32(4,!0),e.getFloat32(8,!0)]},Tn=t=>t instanceof Uint8Array&&t.length===4?new DataView(t.buffer,t.byteOffset,4).getFloat32(0,!0):null,Fr=t=>{if(!(t instanceof Uint8Array))return null;const e=new DataView(t.buffer,t.byteOffset,t.length);return t.length===1?t[0]:t.length===2?e.getUint16(0,!0):t.length===4?e.getUint32(0,!0):null},_r=t=>Array.isArray(t)?t:[],il=(t,e)=>[t[0]*e[0]+t[4]*e[1]+t[8]*e[2]+t[12],t[1]*e[0]+t[5]*e[1]+t[9]*e[2]+t[13],t[2]*e[0]+t[6]*e[1]+t[10]*e[2]+t[14]],p0=(t,e)=>[t[0]*e[0]+t[4]*e[1]+t[8]*e[2],t[1]*e[0]+t[5]*e[1]+t[9]*e[2],t[2]*e[0]+t[6]*e[1]+t[10]*e[2]],_s=(t,e)=>[t[0]-e[0],t[1]-e[1],t[2]-e[2]],ll=t=>Math.hypot(t[0],t[1],t[2]);function m0(t,e){return t.map(n=>{const s=wn(n.props.get("Position"))??[0,0,0],o={p:il(e,s)};for(const r of _r(n.props.get("Data"))){const a=wn(r.props.get("InTangent")),i=wn(r.props.get("OutTangent"));if(a&&(o.tin=p0(e,a)),i&&(o.tout=p0(e,i)),Fr(r.props.get("WidthOverride"))){const l=Tn(r.props.get("FixedWidth"));l&&l>0&&(o.width=l)}}return o})}function cl(t,e,n=2){const s=t.length,o=[];if(s<2)return t.map(l=>({p:l.p,w:l.width}));const r=l=>t[e?(l+s)%s:Math.max(0,Math.min(s-1,l))],a=e?s:s-1;for(let l=0;l<a;l++){const c=r(l),f=r(l+1),h=c.tout??_s(r(l+1).p,r(l-1).p).map(p=>p/2),u=f.tin??_s(r(l+2).p,r(l).p).map(p=>p/2),d=Math.max(1,Math.min(64,Math.ceil(ll(_s(f.p,c.p))/n)));for(let p=0;p<d;p++){const m=p/d,g=m*m,w=g*m,y=2*w-3*g+1,M=w-2*g+m,x=-2*w+3*g,v=w-g,b=c.width!==void 0||f.width!==void 0?(c.width??f.width)*(1-m)+(f.width??c.width)*m:void 0;o.push({p:[0,1,2].map(k=>y*c.p[k]+M*h[k]+x*f.p[k]+v*u[k]),w:b})}}const i=r(e?0:s-1);return o.push({p:i.p,w:i.width}),o}class Tv{constructor(e,n,s){this.name=e,this.material=n,this.color=s}name;material;color;pos=[];uv=[];idx=[];ribbon(e,n,s,o){if(e.length<2)return;const r=this.pos.length/3;let a=0;for(let i=0;i<e.length;i++){const l=e[Math.max(0,i-1)].p,c=e[Math.min(e.length-1,i+1)].p,f=e[i].p;i&&(a+=ll(_s(f,e[i-1].p)));let h=c[0]-l[0],u=c[2]-l[2];const d=Math.hypot(h,u)||1;h/=d,u/=d;const p=(e[i].w??n)/2;this.pos.push(f[0]-u*p,f[1]+s,f[2]+h*p,f[0]+u*p,f[1]+s,f[2]-h*p);const m=a*o/Math.max(.1,e[i].w??n);this.uv.push(0,m,1,m)}for(let i=0;i+1<e.length;i++){const l=r+i*2,c=l+1,f=l+2,h=l+3;this.idx.push(l,f,c,c,f,h,l,c,f,c,h,f)}}cable(e,n,s){const o=ll(_s(n,e)),r=.02*o+.2,a=Math.max(2,Math.min(16,Math.ceil(o/4))),i=[];for(let u=0;u<=a;u++){const d=u/a;i.push([e[0]+(n[0]-e[0])*d,e[1]+(n[1]-e[1])*d-r*4*d*(1-d),e[2]+(n[2]-e[2])*d])}const l=_s(n,e),c=Math.hypot(l[0],l[2])||1,f=[-l[2]/c*s,0,l[0]/c*s],h=[0,s,0];for(const u of[f,h]){const d=this.pos.length/3;for(const p of i)this.pos.push(p[0]-u[0],p[1]-u[1],p[2]-u[2],p[0]+u[0],p[1]+u[1],p[2]+u[2]),this.uv.push(0,0,1,0);for(let p=0;p<a;p++){const m=d+p*2;this.idx.push(m,m+2,m+1,m+1,m+2,m+3,m,m+1,m+2,m+1,m+3,m+2)}}}polygon(e,n){const s=this.pos.length/3,o=e.map(r=>[r[0],r[2]]);for(const r of e)this.pos.push(r[0],n,r[2]),this.uv.push(r[0]/8,r[2]/8);for(const[r,a,i]of Uv(o))this.idx.push(s+r,s+i,s+a,s+r,s+a,s+i)}mesh(){if(!this.idx.length)return null;const e=new Float32Array(this.pos),n=e.length/3,s=new Float32Array(n*3);for(let o=0;o<n;o++)s[o*3+1]=1;return{name:this.name,texture:this.material,material:this.material,vertexCount:n,pos:e,nor:s,uv:new Float32Array(this.uv),bone:new Float32Array(0),weight:new Float32Array(0),indices:new Uint32Array(this.idx),skinned:!1,color:this.color}}}function Uv(t){const e=t.length,n=[];if(e<3)return n;let s=0;for(let l=0;l<e;l++){const c=t[l],f=t[(l+1)%e];s+=c[0]*f[1]-f[0]*c[1]}const o=Array.from({length:e},(l,c)=>s>0?c:e-1-c),r=(l,c,f)=>(c[0]-l[0])*(f[1]-l[1])-(c[1]-l[1])*(f[0]-l[0]),a=(l,c,f,h)=>r(c,f,l)>=0&&r(f,h,l)>=0&&r(h,c,l)>=0;let i=0;for(;o.length>3&&i++<e*e;){let l=!1;for(let c=0;c<o.length;c++){const f=o[(c+o.length-1)%o.length],h=o[c],u=o[(c+1)%o.length],d=t[f],p=t[h],m=t[u];if(!(r(d,p,m)<=0)&&!o.some(g=>g!==f&&g!==h&&g!==u&&a(t[g],d,p,m))){n.push([f,h,u]),o.splice(c,1),l=!0;break}}if(!l)break}for(let l=1;l+1<o.length;l++)n.push([o[0],o[l],o[l+1]]);return n}async function Fv(t,e,n,s){const o=m=>{if(s)for(const g of m){const w=s(g.p[0],g.p[2]);w!==null&&w>g.p[1]&&(g.p=[g.p[0],w,g.p[2]])}return m},r=new Map,a={roads:0,rivers:0,lakes:0,cables:0},i=new Map,l=(m,g,w,y)=>{let M=r.get(m);return M||r.set(m,M=new Tv(g,w,y)),M},c=(m,g)=>{const w=Fr(m.props.get(g));return w===null?void 0:e(w)},f=new Map,h=m=>{let g=f.get(m);return g||(g=po(m,n).then(w=>w?.children.find(y=>!y.header.length)?.children.find(y=>/Road|River/.test(y.header[0]??""))??(w&&/River/.test(w.header[0]??"")?w:null)),f.set(m,g)),g},u=new Map;t.entities.forEach((m,g)=>{if(m.parent>=0){const w=u.get(m.parent)??[];w.push(g),u.set(m.parent,w)}});for(const[m,g]of t.entities.entries())if(g.cls==="RoadEntity"){const w=g.parent>=0?t.entities[g.parent]:null,y=w?.ref!==void 0&&w.ref>=0?e(w.ref):void 0,M=y?await h(y):null,x=Tn(g.props.get("Width"))??Number(Ft(M??void 0,"Width")??4),v=Tn(g.props.get("VScale"))??Number(Ft(M??void 0,"VScale")??1),b=c(g,"Material")??Ft(M??void 0,"Material")??"",k=m0(_r(g.props.get("SplinePoints")),g.world);if(k.length<2)continue;l(`road ${b}`,`дороги · ${b.replace(/^.*\//,"").replace(/\.emat$/i,"")||"без материала"}`,b,[.42,.4,.36]).ribbon(o(cl(k,Fr(g.props.get("IsClosedSpline"))===1)),x||4,.12,v||1),a.roads++}else if(g.cls==="SplineShapeEntity"||g.cls==="PolylineShapeEntity"){const w=(u.get(m)??[]).map(v=>t.entities[v]),y=m0(_r(g.props.get("Points")),g.world);if(y.length<2)continue;const M=w.find(v=>v.cls==="RiverEntity"),x=w.find(v=>v.cls==="LakeGeneratorEntity");if(M){const v=M.ref>=0?e(M.ref):void 0,b=v?await h(v):null,k=Tn(M.props.get("Width"))??Number(Ft(b??void 0,"Width")??5),E=c(M,"Material")??Ft(b??void 0,"Material")??"",A=Tn(M.props.get("SplineOffsetUp"))??Number(Ft(b??void 0,"SplineOffsetUp")??.1);l("water","вода · реки и озёра",E,[.16,.28,.33]).ribbon(o(cl(y,!1,3)),k,A+.05,.2),a.rivers++}else if(x){const v=y.map(k=>k.p[1]).sort((k,E)=>k-E),b=v[Math.floor(v.length/2)]+(Tn(x.props.get("m_fWaterSurfaceOffset"))??0);l("water","вода · реки и озёра",c(x,"m_MaterialName")??"",[.16,.28,.33]).polygon(g.cls==="SplineShapeEntity"?cl(y,!0,4).map(k=>k.p):y.map(k=>k.p),b),a.lakes++}}else if(g.cls==="PowerlineEntity")for(const w of _r(g.props.get("Cables"))){const y=wn(w.props.get("StartPoint")),M=wn(w.props.get("EndPoint"));!y||!M||(l("cable","провода ЛЭП","",[.1,.1,.1]).cable(il(g.world,y),il(g.world,M),.03),a.cables++)}const d=[],p={...ne};ne.limit=256,ne.lite=!0;try{for(const m of r.values()){const g=m.mesh();if(g){if(m.material&&m.name.startsWith("дороги")){let w=i.get(m.material);w||(w=qp(m.material,n).then(M=>M?{image:M.image,alphaTest:M.alphaTest}:null).catch(()=>null),i.set(m.material,w));const y=await w;y?.image&&(g.image=y.image,delete g.color,g.texture=y.image.name??m.material,y.alphaTest&&(g.alphaTest=!0))}d.push(g)}}}finally{ne.limit=p.limit,ne.lite=p.lite}return{meshes:d,stats:a}}const se=(t,e)=>String.fromCharCode(t[e],t[e+1],t[e+2],t[e+3]),Ze=(t,e)=>(t[e]<<24|t[e+1]<<16|t[e+2]<<8|t[e+3])>>>0,mo={1:4,2:8,3:1,4:2,5:2,6:4,7:8,8:12,9:16,10:8,11:1},_v=(t,e)=>{const n=new Float64Array(16);for(let s=0;s<4;s++)for(let o=0;o<4;o++){let r=0;for(let a=0;a<4;a++)r+=t[a*4+o]*e[s*4+a];n[s*4+o]=r}return n},Cv=()=>{const t=new Float64Array(16);return t[0]=t[5]=t[10]=t[15]=1,t};function fl(t){if(se(t,0)!=="EBIN")throw new Error("Не мир Enfusion (нет EBIN)");const e=new DataView(t.buffer,t.byteOffset,t.byteLength),n=new TextDecoder("latin1"),s=A=>e.getUint16(A,!0),o=A=>e.getUint32(A,!0),r=s(8),a=s(10);let i=12;const l=[];for(let A=0;A<r;A++){const S=s(i);l.push(n.decode(t.subarray(i+2,i+2+S))),i+=2+S}const c=[];for(let A=0;A<a;A++,i+=8)c.push(e.getBigUint64(i,!0));const f=A=>l[A]??`#${A}`,h=l.indexOf("$grp"),u=l.indexOf(":"),d=A=>u>=0&&t[A]===13&&s(A+1)===u&&t[A+3]===5,p=[],m=(A,S)=>{throw new Error(`EBIN: ${S} @${A}`)},g=(A,S)=>{const $=t[A];if($===67){const U=s(A+1);S=n.decode(t.subarray(A+3,A+3+U)),A+=3+U}else A+=$===68?9:$===69?3:$===14?0:m(A,`объект ${$}`);if(d(A)&&(A+=6),t[A]===14&&t[A+5]>=128)return{obj:{cls:S,props:new Map},end:A+1+o(A+1)};if(t[A]!==14)return{obj:{cls:S,props:new Map},end:A};const T=w(A,-1);return{obj:{cls:S,props:T.props},end:T.end}},w=(A,S)=>{t[A]!==14&&m(A,"нет тела");const $=A+1+o(A+1),T=new Map;let U=A+8;for(;U<$;){if(t[U]===14){const L=U+1+o(U+1);let O=U+5;for(;O<L;)O=v(O,S);U=L;continue}t[U]!==13&&m(U,"свойство");const _=f(s(U+1)),R=t[U+3];if(U+=4,mo[R])T.set(_,t.subarray(U,U+mo[R])),U+=mo[R];else if(R>=128){const L=o(U),O=R&127;if(U+=4,mo[O])U+=L*mo[O];else if(O===12||O===67)for(let N=0;N<L;N++)U+=2+s(U);else m(U,`массив типа ${O}`)}else if(R===12||R===67){const L=s(U);T.set(_,t.subarray(U+2,U+2+L)),U+=2+L}else if(R===14){const L=U+o(U);let O=U+4;if(O+4<=L&&t[O]===13&&(t[O+3]===68||t[O+3]===69||t[O+3]===67||t[O+3]===14)){const N=[];for(;O<L;){t[O]!==13&&m(O,"элемент");const D=g(O+3,f(s(O+1)));N.push(D.obj),O=D.end}T.set(_,N)}else T.set(_,t.subarray(U+4,L));U=L}else if(R===13){const L=g(U+2,f(s(U)));T.set(_,L.obj),U=L.end}else if(R===68||R===69){const L=g(U-1,"");T.set(_,L.obj),U=L.end}else m(U,`тип ${R} (${_})`)}return{props:T,end:$}},y=(A,S)=>{const $=Sr(wn(A.get("coords")),wn(A.get("angles"))),T=Tn(A.get("scale"));if(T&&Number.isFinite(T)&&T>0&&T!==1)for(let U=0;U<3;U++)for(let _=0;_<3;_++)$[U*4+_]=$[U*4+_]*T;return S>=0?_v(p[S].world,$):$},M=(A,S,$,T,U)=>{const _=p.length;p.push({cls:S,name:$,ref:T,props:new Map,parent:U,world:Cv()});const R=w(A,_),L=p[_];L.props=R.props,L.world=y(R.props,U);for(let O=_+1;O<p.length;O++)p[O].parent===_&&x(O);return R.end},x=A=>{const S=p[A];S.world=y(S.props,S.parent);for(let $=A+1;$<p.length;$++)p[$].parent===A&&x($)},v=(A,S)=>{t[A]!==13&&m(A,"сущность");let $=s(A+1);if(A+=3,$===h){$=s(A+1),A+=3;let _=-1;d(A)&&(_=s(A+4),A+=6);const R=A+1+o(A+1);for(A+=5;A<R;){let L="",O=_;if(t[A]===67){const N=s(A+1);L=n.decode(t.subarray(A+3,A+3+N)),A+=3+N}d(A)&&(O=s(A+4),A+=6),A=M(A,f($),L,O,S)}return R}let T="",U=-1;if(t[A]===67){const _=s(A+1);T=n.decode(t.subarray(A+3,A+3+_)),A+=3+_}return d(A)&&(U=s(A+4),A+=6),M(A,f($),T,U,S)};let b,k=0;try{for(;i<t.length;)i=v(i,-1),k=p.length}catch(A){if(!k)throw A;b=A.message,p.length=k}const E=[];return p.forEach((A,S)=>{if(A.ref<0||A.ref>=c.length)return;const $=Tn(A.props.get("scale"));E.push({ref:A.ref,coords:wn(A.props.get("coords")),angles:wn(A.props.get("angles")),scale:$&&$>0&&Number.isFinite($)?$:1,world:A.world,entity:S})}),{strings:l,guids:c,entities:p,instances:E,error:b}}function g0(t,e=new Map){const n=new DataView(t.buffer,t.byteOffset,t.byteLength),s=new TextDecoder("latin1");for(let o=8;o+15<t.length;o++){if(t[o]!==0||t[o+1]!==6||t[o+2]!==0||t[o+3]!==0||t[o+4]!==0||t[o+5]!==0||t[o+6]!==0)continue;let r=o-1;for(;r>0&&t[r-1]!==0&&o-r<300;)r--;for(let a=r;a<o-1;a++)if(a>=4&&n.getUint32(a-4,!0)===o-a+1){e.set(n.getBigUint64(o+7,!0),s.decode(t.subarray(a,o)));break}o+=14}return e}function Lv(t){if(se(t,0)!=="FORM"||se(t,8)!=="TERR")throw new Error("Не ландшафт Enfusion (нет FORM/TERR)");const e=new DataView(t.buffer,t.byteOffset,t.byteLength);for(let n=12;n+8<=t.length;n+=8+Ze(t,n+4))if(se(t,n)==="HEAD")return{verts:e.getUint32(n+8,!0),cell:e.getFloat32(n+24,!0),hScale:e.getFloat32(n+28,!0),hOffset:e.getFloat32(n+32,!0)};throw new Error("TERR: нет HEAD")}function w0(t){if(se(t,0)!=="FORM"||se(t,8)!=="TERR")return null;for(let e=12;e+8<=t.length;e+=8+Ze(t,e+4))if(se(t,e)==="HGHT"){const n=Ze(t,e+4),s=Math.round(Math.sqrt(n/2)),o=new Uint16Array(s*s),r=new DataView(t.buffer,t.byteOffset+e+8,n);for(let a=0;a<s*s;a++)o[a]=r.getUint16(a*2,!0);return{n:s,h:o}}return null}function y0(t){if(se(t,0)!=="FORM"||se(t,8)!=="TERR")return[];const e=new DataView(t.buffer,t.byteOffset,t.byteLength),n=new TextDecoder("latin1"),s=[];for(let o=12;o+8<=t.length;o+=8+Ze(t,o+4))if(se(t,o)==="MATS"){const r=o+8+Ze(t,o+4);for(let a=o+10;a+4<=r;){const i=e.getUint32(a,!0);if(a+4+i>r)break;s.push(n.decode(t.subarray(a+4,a+4+i)).replace(/\0+$/,"")),a+=4+i}}return s}let Cr=null;const Rv=t=>{Cr=t};function ul(t,e){if(se(t,0)!=="FORM"||se(t,8)!=="TERR")return;const n=new DataView(t.buffer,t.byteOffset,t.byteLength);let s=-1,o=0;for(let r=12;r+8<=t.length;r+=8+Ze(t,r+4))se(t,r)==="TMAT"&&(s=r+8,o=s+Ze(t,r+4));if(!(s<0))for(let r=s;r+8<=o&&se(t,r)==="BMAT";){const a=Ze(t,r+4),i=r+8;r=i+a;const l=n.getUint16(i,!0),c=n.getUint16(i+2,!0),f=n.getUint16(i+4,!0),h=[];for(let d=0;d<f;d++)h.push(n.getUint16(i+6+d*2,!0));const u=new Uint8Array(4096).fill(h[0]??0);if(f>1)try{let d=i+6+f*2;const p=n.getUint16(d,!0),m=n.getUint16(d+2,!0),g=d+4;d=g+p*8;const w=d+6<=r?n.getUint16(d,!0):0,y=d+6,M=Math.ceil(Math.log2(f)),x=[],v=(k,E,A,S,$)=>{if(k&32768){const T=h[k&32767]??h[0];for(let U=0;U<S;U++)u.fill(T,(A+U)*64+E,(A+U)*64+E+S)}else if(k&16384)x[k&16383]=[E,A,S];else if(k<p&&$<7&&S>1){const T=S>>1;for(let U=0;U<4;U++)v(n.getUint16(g+(k*4+U)*2,!0),E+(U&1)*T,A+(U>>1)*T,T,$+1)}};v(m,0,0,64,0);let b=0;for(let k=0;k<w;k++){const E=x[k];if(!E)break;const[A,S,$]=E;for(let T=0;T<$;T++)for(let U=0;U<$;U++){let _=0;for(let R=0;R<M;R++,b++){const L=y+(b>>3);L<r&&(_|=(t[L]>>(b&7)&1)<<R)}u[(S+T)*64+A+U]=h[_]??h[0]}}}catch(d){Cr&&Cr(l,c,f,d.message)}e(l,c,u)}}const Ov=(t,e)=>{for(const n of t.archives)for(const s of n.entries)if(e(s.path))return{a:n,e:s};return null};async function Iv(t,e,n){const r=[],a=[],i=[],l=[],c=[],f=[],h=[],u=[],d=[],p=[];let m=0,g=0,w=0,y=0;const M=(x,v,b)=>{const k=new Uint8Array(v*v*4),E=[0,0,0];if(!x?.rgba){for(let A=0;A<k.length;A+=4)k.set(b,A);return null}for(let A=0;A<v;A++)for(let S=0;S<v;S++){const $=(Math.floor(A*x.height/v)*x.width+Math.floor(S*x.width/v))*4,T=(A*v+S)*4;k[T]=x.rgba[$],k[T+1]=x.rgba[$+1],k[T+2]=x.rgba[$+2],k[T+3]=x.rgba[$+3],E[0]+=k[T],E[1]+=k[T+1],E[2]+=k[T+2]}for(let A=0;A<3;A++)E[A]=E[A]/(v*v*255);return{img:{width:v,height:v,rgba:k,name:x.name},mean:E}};for(const x of e){let v=new Map,b=x;for(let T=0;b&&T<4;T++){const U=await t.readPath($t(b)).catch(()=>null);if(!U)break;const _=Yp(new TextDecoder().decode(U));v=new Map([..._.props,...v]),b=v.has("BCRMap")?void 0:_.parent}const k=async(T,U)=>v.get(T)?await gn(v.get(T),t,U).catch(()=>null):null,E=M(await k("BCRMap",512),512,[128,128,128,255]);E?(E.img.rgba.forEach((T,U,_)=>{U%4===3&&(_[U]=255)}),r.push(E.img),f.push(E.mean)):(g++,r.push({width:512,height:512,rgba:new Uint8Array(1048576).fill(128)}),f.push([.5,.5,.5]));const A=M(await k("NHOMap",512),512,[128,128,128,255]);A&&w++,a.push(A?.img??{width:512,height:512,rgba:new Uint8Array(1048576).map((T,U)=>U%4===3?255:128)});const S=Math.min(1,Math.max(0,Number(v.get("MiddleBCRBlend")??0)*.5)),$=S>0?M(await k("BCRMiddleMap",256),256,[128,128,128,255]):null;$&&y++,i.push($?.img??{width:256,height:256,rgba:new Uint8Array(262144).fill(128)}),p.push($?.mean??[.5,.5,.5]),d.push($?S:0),u.push(Number(v.get("MiddleScaleUV")??75)||75),l.push(Number(v.get("ScaleUV")??4)||4),c.push(Math.min(1,Math.max(0,Number(v.get("SatMapBlend")??.5)))),h.push(x.replace(/^.*\//,"").replace(/\.emat$/i,"")),m=Math.max(m,Number(v.get("DetailMaxDistance")??0)||0)}if(g&&n.push(`Поверхности ландшафта: у ${g} слоёв не загрузилась текстура`),!!r.length)return n.push(`Слои ландшафта: нормали у ${w}, средний масштаб у ${y} из ${r.length}`),{images:r,period:l,tint:c,mean:f,fade:Math.max(60,m||150),names:h,normals:w?a:void 0,middle:y?i:void 0,midPeriod:u,midBlend:d,midMean:p}}async function Dv(t,e,n,s,o){const r=Ov(t,D=>D.startsWith(e)&&D.endsWith(".terr"));if(!r)return s.push("Ландшафт: .terr не найден"),[];const a=Lv(await r.a.read(r.e));if(!n)for(n=1;2*((a.verts-1)/n)**2>12e5&&n<64;)n*=2;const i=r.e.path.replace(/[^/]*$/,"")+".data/",l=r.e.path.replace(/^.*\//,"").replace(/\.terr$/,""),c=await r.a.read(r.e),f=y0(c),h=r.a.lookup(`${i}${l}_0.ttile`),u=h?await r.a.read(h):null,d=u?w0(u):null;if(!d)return s.push("Ландшафт: тайлы .ttile не найдены"),[];const p=d.n-1,m=Math.max(1,Math.round((a.verts-1)/p)),g=Math.floor(p/n)+1,w=m*(g-1)+1;let y=256;for(;y>32&&m*m*y*y>16777216;)y>>=1;const M=Math.max(1,Math.ceil(m/16)),x=Math.ceil(m/M),v=new Float32Array(w*w).fill(NaN),b=new Array(x*x).fill(null);let k=0;if(u&&f.length){let D=0;ul(u,()=>{D++}),k=Math.round(Math.sqrt(D))}const E=k*64;let A=1;for(;E&&m*m*(E/A)**2>33554432&&A<16;)A*=2;const S=E?M*E/A:0,$=new Array(x*x).fill(null),T=new Map,U=[];let _=1/0,R=0;for(let D=0;D<m*m;D++){const F=D?r.a.lookup(`${i}${l}_${D}.ttile`):h,C=F?D?await r.a.read(F):u:null,P=C?D?w0(C):d:null;if(!P||P.n!==d.n)continue;const I=D%m,B=Math.floor(D/m);E&&C&&ul(C,(j,X,J)=>{const G=j*64,H=X*64,K=Math.floor(X/k/M)*x+Math.floor(j/k/M);if(K<0||K>=x*x)return;let Q=$[K];Q||($[K]=Q=new Uint8Array(S*S));const V=G-Math.floor(j/k/M)*M*E,W=H-Math.floor(X/k/M)*M*E;for(let Y=0;Y<64;Y+=A)for(let tt=0;tt<64;tt+=A){const nt=J[Y*64+tt];let et=T.get(nt);et===void 0&&(et=U.length<32?U.length:0,U.length<32&&(T.set(nt,et),U.push(nt))),Q[(W+Y)/A*S+(V+tt)/A]=et}});for(let j=0;j<g;j++)for(let X=0;X<g;X++){const J=P.h[Math.min(p,j*n)*P.n+Math.min(p,X*n)]*a.hScale+a.hOffset;v[(B*(g-1)+j)*w+I*(g-1)+X]=J,J<_&&(_=J)}R++;const z=r.a.lookup(`${i}${l}_${D}_supertexture.edds`);if(z)try{const j=Yi((await r.a.read(z)).slice().buffer,y),X=j.rgba;if(!X)continue;const J=Math.floor(B/M)*x+Math.floor(I/M),G=M*y;let H=b[J];if(!H){H=new Uint8Array(G*G*4);for(let V=0;V<H.length;V+=4)H[V]=107,H[V+1]=115,H[V+2]=84,H[V+3]=255;b[J]=H}const K=I%M*y,Q=B%M*y;for(let V=0;V<y;V++)for(let W=0;W<y;W++){const Y=Math.min(j.width-1,Math.floor(W*j.width/y)),nt=(Math.min(j.height-1,Math.floor(V*j.height/y))*j.width+Y)*4,et=((Q+V)*G+K+W)*4;H[et]=X[nt],H[et+1]=X[nt+1],H[et+2]=X[nt+2],H[et+3]=255}}catch{}}const{computeNormals:L}=await Promise.resolve().then(()=>oa),O=[],N=n*a.cell;for(let D=0;D<x;D++)for(let F=0;F<x;F++){const C=F*M,P=D*M,I=Math.min(M,m-C),B=Math.min(M,m-P),z=I*(g-1)+1,j=B*(g-1)+1,X=C*(g-1),J=P*(g-1),G=new Float32Array(z*j*3),H=new Float32Array(z*j*2),K=[];for(let tt=0;tt<j;tt++)for(let nt=0;nt<z;nt++){const et=tt*z+nt,rt=v[(J+tt)*w+X+nt];G[et*3]=(X+nt)*N,G[et*3+1]=Number.isFinite(rt)?rt:0,G[et*3+2]=(J+tt)*N,H[et*2]=nt/((g-1)*M),H[et*2+1]=tt/((g-1)*M)}for(let tt=0;tt<j-1;tt++)for(let nt=0;nt<z-1;nt++){if(!Number.isFinite(v[(J+tt)*w+X+nt]))continue;const et=tt*z+nt,rt=et+1,mt=et+z,xt=mt+1;K.push(et,mt,rt,rt,mt,xt)}if(!K.length)continue;const Q=new Uint32Array(K),V=b[D*x+F],W={name:`Ландшафт · блок ${F},${D}`,texture:"",vertexCount:z*j,pos:G,nor:L(G,Q),uv:H,bone:new Float32Array(0),weight:new Float32Array(0),indices:Q,skinned:!1,color:[.42,.45,.33]};if(V){const tt=M*y;W.image={width:tt,height:tt,rgba:V,name:`${l} supertexture ${F},${D}`},W.texture=W.image.name,delete W.color}const Y=$[D*x+F];Y&&(W.terrainIndex={width:S,height:S,data:Y}),O.push(W)}if(o&&U.length){const D=Date.now();o.layers=await Iv(t,U.map(F=>f[F]??""),s),s.push(`Поверхности ландшафта: ${U.length} слоёв, карта ${E/A||0} текселей на тайл (${((d.n-1)*a.cell*A/E).toFixed(1)} м на тексель), ${((Date.now()-D)/1e3).toFixed(1)} с`)}if(o){const D=v,F=w;o.heightAt=(C,P)=>{const I=C/N,B=P/N,z=Math.floor(I),j=Math.floor(B);if(z<0||j<0||z>=F-1||j>=F-1)return null;const X=D[j*F+z],J=D[j*F+z+1],G=D[(j+1)*F+z],H=D[(j+1)*F+z+1],K=I-z,Q=B-j,V=(X*(1-K)+J*K)*(1-Q)+(G*(1-K)+H*K)*Q;return Number.isFinite(V)?V:null},o.min=_,o.size=(F-1)*N}return s.push(`Ландшафт: ${a.verts}×${a.verts} вершин, шаг ${a.cell} м (${((a.verts-1)*a.cell/1e3).toFixed(1)} км), тайлов ${R}, показан с шагом ${N} м, цвет — ${y} px на тайл, блоков ${O.length}`),O}const Pv=/prefabs\/(vegetation|rocks|clutter)\/|\/vegetation\/|weeds|debris|stump|brickpile|rubble|grass/i;function b0(t){if(se(t,0)!=="FORM"||se(t,8)!=="WSTR")throw new Error("Не .wst (нет FORM/WSTR)");const e=new DataView(t.buffer,t.byteOffset,t.byteLength),n=new TextDecoder("latin1"),s=new Map;for(let u=12;u+8<=t.length;u+=8+Ze(t,u+4))s.set(se(t,u),{off:u+8,size:Ze(t,u+4)});const o=s.get("PREF"),r=s.get("HEAD");if(!o||!r)throw new Error("WST: нет PREF/HEAD");const a=[];for(let u=o.off;u+4<=o.off+o.size;){const d=e.getUint32(u,!0);a.push(n.decode(t.subarray(u+4,u+4+d)).replace(/\0+$/,"")),u+=4+d}const i=[];let l=0;for(let u=r.off+24;u+16<=r.off+r.size;u+=16){const d=e.getUint32(u+4,!0),p=e.getUint32(u+8,!0),m=e.getUint32(u+12,!0);m+d*8+p*36>t.length||(i.push([d,p,m]),l+=p)}const c=new Float32Array(l*8),f=new Uint16Array(l);let h=0;for(const[u,d,p]of i){let m=0;const g=p+u*8;for(let w=0;w<u;w++){const y=Math.min(d,e.getUint32(p+w*8,!0)),M=e.getUint32(p+w*8+4,!0);for(let x=m;x<y;x++,h++){const v=g+x*36;for(let b=0;b<8;b++)c[h*8+b]=e.getFloat32(v+b*4,!0);f[h]=M}m=y}}return{prefabs:a,data:c.subarray(0,h*8),prefab:f.subarray(0,h)}}function Nv(t,e){const n=e*8,s=t[n+3]>0?t[n+3]:1,o=ol([t[n+4],t[n+5],t[n+6],t[n+7]],[t[n],t[n+1],t[n+2]]);for(let r=0;r<3;r++)for(let a=0;a<3;a++)o[r*4+a]=o[r*4+a]*s;return o}async function zv(t,e){const n=new Uint8Array(await t.arrayBuffer()),s=[],o=(t.path??t.name).toLowerCase();let r=o.replace(/[^/]*$/,"");const a=fl(n),i=new Map;for(const V of e.archives)for(const W of V.entries)/resourcedatabase\.rdb$/i.test(W.path)&&g0(await V.read(W),i);const l=[a];for(let V=a,W=0;W<4;W++){const Y=V.entities[0],tt=Y?.cls==="SubScene"?Fr(Y.props.get("Parent")):null,nt=tt!==null?i.get(V.guids[tt]):void 0;if(!nt)break;const et=await e.readPath(nt.toLowerCase()).catch(()=>null);if(!et){s.push(`Родительский мир ${nt} не найден — показана только надстройка`);break}V=fl(et),l.unshift(V),r=nt.toLowerCase().replace(/[^/]*$/,""),s.push(`Надстройка над миром ${nt}: показаны оба`)}for(const V of l)V.error&&s.push(`Разбор мира остановлен на неизвестной конструкции (${V.error}) — показано разобранное до неё`);const c={};let f=Date.now();const h=await Dv(e,r,0,s,c);f=Date.now()-f,i.size||s.push("Объекты не расставлены: нет resourceDatabase.rdb (откройте «Папку игры» Arma Reforger целиком)");const u=new Map;let d=0,p=0;for(const V of l){d+=V.entities.length,p+=V.instances.length;for(const W of V.instances){if(!W.coords&&V.entities[W.entity].parent<0)continue;const Y=i.get(V.guids[W.ref]);if(!Y||!/\.et$/i.test(Y))continue;let tt=u.get(Y);tt||u.set(Y,tt=[]),tt.push(W.world)}}let m=null;const g=o.replace(/\.ent$/,".wst"),w=l.length>1?null:g;for(const V of[w,r+r.replace(/\/$/,"").replace(/^.*\//,"")+".wst"].filter(W=>!!W)){const W=await e.readPath(V).catch(()=>null);if(W){try{m=b0(W);for(let Y=0;Y<m.prefab.length;Y++){const tt=$t(m.prefabs[m.prefab[Y]]??"");if(!/\.et$/i.test(tt))continue;let nt=u.get(tt);nt||u.set(tt,nt=[]),nt.push(Y)}p+=m.prefab.length,s.push(`Сгенерированная растительность (${V.replace(/^.*\//,"")}): ${m.prefab.length} экземпляров ${m.prefabs.length} префабов`)}catch(Y){s.push(`${V}: ${Y.message}`)}break}}const y=new vs,M={...ne};ne.limit=256,ne.lite=!0;let x=0,v=0,b=0;const k=((c.size??0)>8e3?7e6:45e5)-Ut(h),E=[],A=[],S=new Map,$=new Map,T=async V=>{const W=$t(V);if(S.has(W))return S.get(W);let Y=null;for(const tt of e.archives){const nt=tt.kind==="enfusion-pak"?tt.lookup(W):null;if(nt){try{let et=await Vp(nt,262144),rt=ho(et).get("HEAD");if((!rt||rt.off+rt.size>et.length)&&(et=await Us(nt),rt=ho(et).get("HEAD")),rt){const mt=Er(et,rt.off);Y=mt.lods.map(xt=>xt.sections.reduce((St,Pt)=>St+Pt.tris,0)),$.set(W,mt.radius)}}catch{Y=null}break}}if(Y===null&&!S.has(W)){const tt=await e.readPath(W).catch(()=>null),nt=tt?ho(tt).get("HEAD"):void 0,et=tt&&nt?Er(tt,nt.off):null;Y=et?et.lods.map(rt=>rt.sections.reduce((mt,xt)=>mt+xt.tris,0)):null,et&&$.set(W,et.radius)}return S.set(W,Y),Y},U={t:Date.now(),plan:0,mesh:0,splines:0,terrain:f};for(const V of u.keys()){const W=V;try{const Y=[];for(const nt of await rl(W,e,{},[],void 0,!1)){if(nt.hidden)continue;const et=await T(nt.xob);et?.length&&Y.push({xob:nt.xob,world:nt.world,remap:nt.remap,lods:et})}const tt=Math.max(.1,...Y.map(nt=>$.get($t(nt.xob))??1));Y.length&&A.push({ref:V,path:W,veg:Pv.test(W),n:u.get(V).length,parts:Y,r:tt})}catch{b++}}U.plan=Date.now()-U.t,U.t=Date.now();const _=(V,W)=>{for(let Y=0;Y<V.length;Y++)if(V[Y]<=W)return Y;return V.length-1},R=k*.7,L=(V,W)=>Math.min(8e3,Math.max(20,W*V.r*V.r)),O=V=>A.reduce((W,Y)=>W+(Y.veg?0:Y.n*Y.parts.reduce((tt,nt)=>tt+nt.lods[_(nt.lods,L(Y,V))],0)),0);let N=Math.log(.001),D=Math.log(1e5);if(O(Math.exp(D))>R)for(let V=0;V<24;V++){const W=(N+D)/2;O(Math.exp(W))>R?D=W:N=W}else N=D;const F=Math.exp(N),C=V=>L(V,F);s.push(`Предел на экземпляр — по размеру: ${Math.round(L({r:10},F))} треугольников у дома (радиус 10 м), ${Math.round(L({r:2},F))} у предмета 2 м (LOD подобраны по бюджету ${Math.round(k/1e6)} млн)`);const P=new Map;try{for(const V of A){const W=[];try{for(const et of V.parts){const rt=V.veg?_(et.lods,300):_(et.lods,C(V)),mt=`${$t(et.xob)}|${rt}|${[...et.remap].join(",")}`;let xt=P.get(mt);if(!xt){const St=await e.readPath($t(et.xob));if(!St)continue;xt=(await Ar(St,e,et.remap,rt)).meshes.filter(Pt=>!Pt.defaultHidden),P.set(mt,xt)}for(const St of xt)W.push(Bv(St,et.world))}}catch{b++;continue}if(!W.length)continue;const Y=V.path.replace(/^.*\//,"").replace(/\.et$/i,""),tt=V.veg,nt=y.proto(Y,W,tt?"veg":"obj",et=>(tt?"растительность · ":"объекты · ")+et.texture.replace(/^.*\//,""));E.push({ref:V.ref,path:Y,veg:tt,pi:nt,parts:W,tris:W.reduce((et,rt)=>et+rt.indices.length/3,0),r:V.r})}}finally{ne.limit=M.limit,ne.lite=M.lite}const I=/prefabs\/structures\//i,B=V=>V.veg?3:I.test(V.ref)&&!/furniture|interior/i.test(V.ref)?0:V.r>=3?1:2,z=[0,0,0,0];for(const V of E)z[B(V)]+=V.tris*u.get(V.ref).length;const j=[1,1,1,1];let X=k;for(const V of[0,1,2,3]){const W=V===3?X:X*(V===0?.8:.6);j[V]=z[V]>W&&W>0?Math.ceil(z[V]/W):z[V]>0&&W<=0?1e3:1,X=Math.max(k*.05,X-z[V]/j[V])}const J=j[3];for(const V of E){const W=u.get(V.ref),Y=j[B(V)];for(let tt=0;tt<W.length;tt+=Y){const nt=W[tt];y.place(V.pi,V.parts,typeof nt=="number"?Nv(m.data,nt):nt,`${V.path} #${tt}`,V.veg?"растительность":"объекты"),x++}v+=W.length-Math.ceil(W.length/Y)}(j[0]>1||j[1]>1||j[2]>1)&&s.push(`Не влезло в бюджет: строения прорежены в ${j[0]} раз(а), крупные предметы — в ${j[1]}, мелкие — в ${j[2]}`);const G=E.map(V=>({n:V.path,t:V.tris*u.get(V.ref).length/j[B(V)]})).sort((V,W)=>W.t-V.t).slice(0,5);s.push(`Больше всего треугольников: ${G.map(V=>`${V.n} ${Math.round(V.t/1e3)} тыс.`).join(", ")}`),U.mesh=Date.now()-U.t,U.t=Date.now();const H=y.build();let K=[];try{const V={roads:0,rivers:0,lakes:0,cables:0};for(const W of l){const Y=await Fv(W,tt=>i.get(W.guids[tt]),e,c.heightAt);K.push(...Y.meshes),V.roads+=Y.stats.roads,V.rivers+=Y.stats.rivers,V.lakes+=Y.stats.lakes,V.cables+=Y.stats.cables}U.splines=Date.now()-U.t,s.push(`Дороги: ${V.roads} сплайнов, реки: ${V.rivers}, озёра: ${V.lakes}, провода ЛЭП: ${V.cables} пролётов`)}catch(V){s.push(`Дороги и вода не построены: ${V.message}`)}if(c.size&&c.min!==void 0&&c.min<-1){const V=c.size,W=V*.5;K.push({name:"вода · море (уровень 0)",texture:"",vertexCount:4,pos:new Float32Array([-W,0,-W,V+W,0,-W,-W,0,V+W,V+W,0,V+W]),nor:new Float32Array([0,1,0,0,1,0,0,1,0,0,1,0]),uv:new Float32Array(8),bone:new Float32Array(0),weight:new Float32Array(0),indices:new Uint32Array([0,2,1,1,2,3,0,1,2,1,3,2]),skinned:!1,color:[.13,.24,.3]})}const Q=[...h,...K,...H];if(!Q.length)throw new Error("В мире не найдено ни ландшафта, ни объектов");return s.unshift(`Enfusion мир ${t.name}: сущностей ${d}, экземпляров префабов ${p} (префабов ${u.size}), поставлено ${x}${v?`, растительность прорежена в ${J} раз(а) — не поставлено ${v}`:""}${b?`, не загрузились ${b} префабов`:""}; треугольников ${Ut(Q)}`),s.push("Объекты — по LOD попроще и с текстурами до 256 px; отдельные префабы открываются в полном качестве."),s.push(`Время: ландшафт ${(U.terrain/1e3).toFixed(1)} с, префабы ${(U.plan/1e3).toFixed(1)} с, модели ${(U.mesh/1e3).toFixed(1)} с, дороги и вода ${(U.splines/1e3).toFixed(1)} с`),{meshes:Q,skeleton:null,motions:[],bounds:dt(h.length?h:Q),flipZ:!0,info:s,instances:y.scene,terrainLayers:c.layers}}function Bv(t,e){if(Array.from(e).every((o,r)=>Math.abs(o-(r%5===0?1:0))<1e-9))return t;const n=new Float32Array(t.pos.length),s=new Float32Array(t.nor.length);for(let o=0;o<t.pos.length;o+=3){const r=t.pos[o],a=t.pos[o+1],i=t.pos[o+2];n[o]=e[0]*r+e[4]*a+e[8]*i+e[12],n[o+1]=e[1]*r+e[5]*a+e[9]*i+e[13],n[o+2]=e[2]*r+e[6]*a+e[10]*i+e[14];const l=t.nor[o],c=t.nor[o+1],f=t.nor[o+2];s[o]=e[0]*l+e[4]*c+e[8]*f,s[o+1]=e[1]*l+e[5]*c+e[9]*f,s[o+2]=e[2]*l+e[6]*c+e[10]*f}return{...t,pos:n,nor:s}}const Vv=Object.freeze(Object.defineProperty({__proto__:null,decodeTmat:ul,loadEnfusionWorld:zv,parseEbin:fl,parseRdb:g0,parseTerrMats:y0,parseWst:b0,setTmatDebug:Rv,get tmatDebug(){return Cr}},Symbol.toStringTag,{value:"Module"}));let v0=class{constructor(e,n,s){this.u=e,this.p=n,this.end=s,this.dv=new DataView(e.buffer,e.byteOffset,e.byteLength)}u;p;end;dv;need(e){if(this.p+e>this.end)throw new Error("Анимация SDK обрезана")}u8(){return this.need(1),this.u[this.p++]}u16(){this.need(2);const e=this.dv.getUint16(this.p,!0);return this.p+=2,e}u32(){this.need(4);const e=this.dv.getUint32(this.p,!0);return this.p+=4,e}f32(){this.need(4);const e=this.dv.getFloat32(this.p,!0);return this.p+=4,e}q16(){return-32+this.u16()*64/65535}sz(){const e=this.u.indexOf(0,this.p);if(e<0||e>=this.end)throw new Error("Анимация SDK: строка не закрыта");const n=new TextDecoder("latin1").decode(this.u.subarray(this.p,e));return this.p=e+1,n}line(){for(;this.p<this.end&&this.u[this.p]!==10;)this.p++;this.p++}};function jv(t){const e=[t.u8(),t.u8()],n=t.u16(),s=[];for(let o=0;o<n;o++){const r=t.f32(),a=t.f32(),i=t.u8(),l={value:r,time:a,shape:i,tension:0,continuity:0,bias:0,param:[0,0,0,0]};i!==4&&(l.tension=t.q16(),l.continuity=t.q16(),l.bias=t.q16(),l.param=[t.q16(),t.q16(),t.q16(),t.q16()]),s.push(l)}return{behavior:e,keys:s}}function x0(t){const e=t.sz(),n=t.u32(),s=t.u32(),o=t.f32(),r=t.u16();if(r<6)throw new Error(`Анимация SDK версии ${r} не поддерживается (нужна 6–7)`);const a=t.u8(),i=t.u16(),l=t.f32();t.f32(),t.f32(),t.f32();const c=t.u16(),f=[];for(let h=0;h<c;h++){const u=t.sz(),d=t.u8(),p=[];for(let m=0;m<6;m++)p.push(jv(t));f.push({name:u,flags:d,envs:p})}if(r>=7){const h=t.u32();for(let u=0;u<h;u++){t.line();const d=t.u32();t.p+=d*8}}return{name:e,frameStart:n,frameEnd:s,fps:o,version:r,flags:a,boneOrPart:i,speed:l,bones:f}}function Gv(t){const e=new v0(t,0,t.length),n=e.u32(),s=[];for(let o=0;o<n;o++)s.push(x0(e));return s}function Xv(t){const e=new DataView(t.buffer,t.byteOffset,t.byteLength);if(t.length<8||e.getUint32(0,!0)!==4608)throw new Error("Не .skl (нет чанка 0x1200)");return x0(new v0(t,8,Math.min(t.length,8+e.getUint32(4,!0))))}function M0(t,e){const n=t[e],s=t[e+1],o=t[e-1];switch(n.shape){case 0:{const r=(1-n.tension)*(1+n.continuity)*(1+n.bias),a=(1-n.tension)*(1-n.continuity)*(1-n.bias),i=s.value-n.value;return o?(s.time-n.time)/(s.time-o.time)*(r*(n.value-o.value)+a*i):a*i}case 3:{const r=s.value-n.value;return o?(s.time-n.time)/(s.time-o.time)*(n.value-o.value+r):r}case 1:case 2:return o?n.param[1]*((s.time-n.time)/(s.time-o.time)):n.param[1];case 5:{const r=n.param[3]*(s.time-n.time);return Math.abs(n.param[2])>1e-5?r/n.param[2]:r*1e5}default:return 0}}function k0(t,e){const n=t[e-1],s=t[e],o=t[e+1];switch(s.shape){case 0:{const r=(1-s.tension)*(1-s.continuity)*(1+s.bias),a=(1-s.tension)*(1+s.continuity)*(1-s.bias),i=s.value-n.value;return o?(s.time-n.time)/(o.time-n.time)*(a*(o.value-s.value)+r*i):r*i}case 3:{const r=s.value-n.value;return o?(s.time-n.time)/(o.time-n.time)*(o.value-s.value+r):r}case 1:case 2:return o?s.param[0]*((s.time-n.time)/(o.time-n.time)):s.param[0];case 5:{const r=s.param[1]*(s.time-n.time);return Math.abs(s.param[0])>1e-5?r/s.param[0]:r*1e5}default:return 0}}function Hv(t,e,n){const s=t.time,o=t.time+(t.shape===5?t.param[2]:(e.time-t.time)/3),r=e.time,a=e.time+(e.shape===5?e.param[0]:-(e.time-t.time)/3),i=t.value,l=t.value+(t.shape===5?t.param[3]:M0([t,e],0)/3),c=e.value,f=e.value+(e.shape===5?e.param[1]:-k0([t,e],1)/3),h=(m,g,w,y,M)=>{const x=1-M;return x*x*x*m+3*x*x*M*g+3*x*M*M*w+M*M*M*y};let u=0,d=1,p=.5;for(let m=0;m<30;m++)p=(u+d)/2,h(s,o,a,r,p)<n?u=p:d=p;return h(i,l,f,c,p)}function Qn(t,e){const n=t.keys;if(!n.length)return 0;if(n.length===1)return n[0].value;const s=n[0],o=n[n.length-1];if(e<s.time){if(t.behavior[0]===0)return 0;if(t.behavior[0]===2&&o.time>s.time)e=s.time+((e-s.time)%(o.time-s.time)+(o.time-s.time))%(o.time-s.time);else return s.value}else if(e>o.time){if(t.behavior[1]===0)return 0;if(t.behavior[1]===2&&o.time>s.time)e=s.time+(e-s.time)%(o.time-s.time);else return o.value}let r=0;for(;r+2<n.length&&e>n[r+1].time;)r++;const a=n[r],i=n[r+1];if(e<=a.time)return a.value;if(e>=i.time)return i.value;const l=(e-a.time)/(i.time-a.time);switch(i.shape){case 0:case 1:case 2:{const c=M0(n,r),f=k0(n,r+1),h=l*l,u=h*l,d=3*h-2*u,p=1-d,m=u-h,g=m-h+l;return p*a.value+d*i.value+g*c+m*f}case 5:return Hv(a,i,e);case 3:return a.value+l*(i.value-a.value);case 4:return a.value;default:return 0}}function E0(t){const e=t[0]+t[5]+t[10];let n,s,o,r;if(e>0){const a=Math.sqrt(e+1)*2;r=a/4,n=(t[9]-t[6])/a,s=(t[2]-t[8])/a,o=(t[4]-t[1])/a}else if(t[0]>t[5]&&t[0]>t[10]){const a=Math.sqrt(1+t[0]-t[5]-t[10])*2;n=a/4,r=(t[9]-t[6])/a,s=(t[1]+t[4])/a,o=(t[2]+t[8])/a}else if(t[5]>t[10]){const a=Math.sqrt(1+t[5]-t[0]-t[10])*2;s=a/4,r=(t[2]-t[8])/a,n=(t[1]+t[4])/a,o=(t[6]+t[9])/a}else{const a=Math.sqrt(1+t[10]-t[0]-t[5])*2;o=a/4,r=(t[4]-t[1])/a,n=(t[2]+t[8])/a,s=(t[6]+t[9])/a}return[n,s,o,r]}function Wv(t,e,n,s=[]){const o=Math.max(1,t.frameEnd-t.frameStart),r=t.fps>0?t.fps:30,a=new Map(e.map((f,h)=>[f.toLowerCase(),h])),i=[],l=new Float32Array(16);for(const f of t.bones){const h=a.get(f.name.toLowerCase());if(h===void 0)continue;const u=new Int16Array(o*4),d=new Float32Array(o*3);let p=0,m=0,g=0,w=1;for(let y=0;y<o;y++){const M=(t.frameStart+y)/r,x=f.envs,v=[Qn(x[0],M),Qn(x[1],M),Qn(x[2],M)];n([Qn(x[4],M),Qn(x[3],M),Qn(x[5],M)],v,l);let[b,k,E,A]=E0(l);b*p+k*m+E*g+A*w<0&&(b=-b,k=-k,E=-E,A=-A),[p,m,g,w]=[b,k,E,A],u[y*4]=Math.round(b*32767),u[y*4+1]=Math.round(k*32767),u[y*4+2]=Math.round(E*32767),u[y*4+3]=Math.round(A*32767),d.set(v,y*3)}i[h]={rot:u,trans:d}}const c={kind:"xray",name:t.name,frames:o,duration:o/r,tracks:i};if(t.flags&1){const f=i[t.boneOrPart];f&&(c.tracks=[],c.tracks[t.boneOrPart]=f,c.part=ec)}else if(t.boneOrPart!==65535&&s.length>1&&s[t.boneOrPart]){const f=s[t.boneOrPart],h=[];for(const u of f.bones){const d=a.get(u.toLowerCase());d!==void 0&&i[d]&&(h[d]=i[d])}c.tracks=h,c.part=f.name}return c}const A0=Object.freeze(Object.defineProperty({__proto__:null,evalEnvelope:Qn,parseSMotions:Gv,parseSkl:Xv,quatOfRows:E0,smotionToXray:Wv},Symbol.toStringTag,{value:"Module"}));function $0(t){let e=t>>>0;return n=>(e=Math.imul(134775813,e)+1>>>0,n===256?e>>>24:Math.floor(e*n/4294967296))}function hl(t,e){const n=new Uint8Array(256);let s=t>>>0;for(let r=0;r<256;r++)n[r]=r;for(let r=0;r<e;r++){s=Math.imul(134775813,s)+1>>>0;const a=s>>>24;let i;do s=Math.imul(134775813,s)+1>>>0,i=s>>>24;while(a===i);const l=n[a];n[a]=n[i],n[i]=l}const o=new Uint8Array(256);for(let r=0;r<256;r++)o[n[r]]=r;return o}function Lr(t,e,n=hl(t.tableSeed,t.iterations)){const s=new Uint8Array(e.length),o=$0(t.encryptSeed);for(let r=0;r<e.length;r++)s[r]=n[e[r]^o(256)&255];return s}function S0(t){const e=new DataView(t.buffer,t.byteOffset,t.byteLength),n=new Set;if(t.length<64||e.getUint16(0,!0)!==23117)return new Uint32Array(0);const s=e.getUint32(60,!0);if(s+24>t.length||e.getUint32(s,!0)!==17744)return new Uint32Array(0);const o=e.getUint16(s+6,!0),r=e.getUint16(s+20,!0),a=Array.from({length:o},(i,l)=>l).sort((i,l)=>{const c=f=>s+24+r+f*40+40<=t.length&&e.getUint32(s+24+r+f*40+36,!0)&2147483648?0:1;return c(i)-c(l)});for(const i of a){const l=s+24+r+i*40;if(l+40>t.length)break;const c=String.fromCharCode(...t.subarray(l,l+8)).replace(/\0+$/,""),f=e.getUint32(l+16,!0),h=e.getUint32(l+20,!0),u=e.getUint32(l+36,!0);if(!(u&536870912||!(u&64)||c===".rsrc"||c===".reloc"))for(let d=h;d+4<=Math.min(t.length,h+f);d+=4){const p=e.getUint32(d,!0);p>65535&&n.add(p)}}return Uint32Array.from(n)}const Kv=[2048,1024,4096,512];function Yv(t,e,n,s){let o=t.encryptSeed>>>0,r=0;for(let a=0;a<4;a++)o=Math.imul(134775813,o)+1>>>0,r+=e[n[a]^o>>>24]*2**(8*a);if(r<16||r>32*n.length+1024)return!1;try{const a=Mn(Lr(t,n.subarray(0,Math.min(n.length,24)),e).subarray(4),Math.min(r,16)),i=a[0]|a[1]<<8;if(i<16||i>520||a.length>14&&(a[14]<32||a[14]>126))return!1;const l=Lr(t,n.subarray(0,Math.min(n.length,512)),e),c=Mn(l.subarray(4),Math.min(r,600)),f=new DataView(c.buffer);let h=0;for(let u=0;u<2&&h+2<=c.length;u++){const d=f.getUint16(h,!0);if(d<16||d>520)return!1;for(let p=h+14;p<Math.min(c.length,h+2+d-4);p++)if(c[p]<32||c[p]>126)return!1;h+=2+d}return s(Mn(Lr(t,n,e)))}catch{return!1}}function Zv(t,e,n){if(e.length<8)return null;const s=S0(t),o=new Map;for(const r of s){const a=$0(r);a(256),a(256),a(256);const i=a(256)&255;let l=o.get(i);l||o.set(i,l=[]),l.push(r)}for(const r of Kv)for(const a of s){const i=hl(a,r);let l=0;for(let c=0;c<256;c++)if(i[c]===0){l=c;break}for(const c of o.get(e[3]^l)??[]){if(c===a)continue;const f={tableSeed:a,encryptSeed:c,iterations:r};if(Yv(f,i,e,n))return f}}return null}const qv=Object.freeze(Object.defineProperty({__proto__:null,findSocKey:Zv,peDataWords:S0,socAlphabetBack:hl,socDecode:Lr},Symbol.toStringTag,{value:"Module"})),T0=30,dl=34e37;let pl=class{constructor(e,n,s){this.pos=n,this.end=s,this.dv=new DataView(e)}pos;end;dv;b(){return this.dv.getUint8(this.pos++)}w(){const e=this.dv.getUint16(this.pos,!0);return this.pos+=2,e}d(){const e=this.dv.getUint32(this.pos,!0);return this.pos+=4,e}i(){const e=this.dv.getInt32(this.pos,!0);return this.pos+=4,e}f(){const e=this.dv.getFloat32(this.pos,!0);return this.pos+=4,e}s(){const e=this.dv.getInt16(this.pos,!0);return this.pos+=2,e}skip(e){this.pos+=e}};const ml=(t,e,n)=>{const s=t.h.blocks[e];return s&&(!n||n.test(s.type))?new pl(t.buf,s.start,s.end):null},Rr=(t,e)=>e>=0&&e<t.strings.length?t.strings[e]:"";function U0(t){const e=[t.f(),t.f(),t.f()],n=[t.f(),t.f(),t.f(),t.f()],s=t.f();return{t:Math.abs(e[0])<dl?e:null,q:Math.abs(n[0])<dl?n:null,s:Math.abs(s)<dl?s:null}}function Qv(t,e,n){const s=Math.cos(t/2),o=Math.sin(t/2),r=Math.cos(e/2),a=Math.sin(e/2),i=Math.cos(n/2),l=Math.sin(n/2);return[s*r*i-o*a*l,o*r*i+s*a*l,s*a*i-o*r*l,s*r*l+o*a*i]}function Or(t){const e=t.d(),n=[];if(!e)return n;const s=t.d();for(let o=0;o<e;o++){const r=t.f(),a=t.f();s===2?t.skip(8):s===3&&t.skip(12),n.push([r,a])}return n}const gl=(t,e)=>{if(!t.length)return 0;if(e<=t[0][0])return t[0][1];for(let n=1;n<t.length;n++)if(e<=t[n][0]){const[s,o]=t[n-1],[r,a]=t[n];return o+(a-o)*(e-s)/(r-s||1)}return t[t.length-1][1]};function Jv(t){let e=null,n=null,s=null;const o=t.d();if(o){const i=t.d();if(i!==4){e=[];for(let l=0;l<o;l++){const c=t.f();e.push([c,[t.f(),t.f(),t.f(),t.f()]]),i===3&&t.skip(12)}}else{const l=[Or(t),Or(t),Or(t)];e=[...new Set(l.flatMap(f=>f.map(h=>h[0])))].sort((f,h)=>f-h).map(f=>[f,Qv(gl(l[0],f),gl(l[1],f),gl(l[2],f))]),e.length||(e=null)}}const r=t.d();if(r){const i=t.d();n=[];for(let l=0;l<r;l++){const c=t.f();n.push([c,[t.f(),t.f(),t.f()]]),i===2?t.skip(24):i===3&&t.skip(12)}}const a=Or(t);return a.length&&(s=a.map(([i,l])=>[i,[l,l,l]])),{pos:n,rot:e,scl:s}}function t4(t,e,n,s){if(e<4){const c=Math.min(Math.max(s,0),1)*Math.max(0,e-1),f=Math.min(Math.floor(c),Math.max(0,e-2)),h=c-f;return Array.from({length:n},(u,d)=>e>1?t(f,d)*(1-h)+t(f+1,d)*h:t(0,d))}const o=e-3,r=Math.min(Math.max(s,0),1)*o,a=c=>Math.min(Math.max(c-3,0),o),i=Math.min(Math.floor(r)+3,e-1),l=[0,1,2,3].map(c=>Array.from({length:n},(f,h)=>t(c+i-3,h)));for(let c=1;c<=3;c++)for(let f=3;f>=c;f--){const h=f+i-3,u=a(h+4-c)-a(h),d=u?(r-a(h))/u:0;for(let p=0;p<n;p++)l[f][p]=(1-d)*l[f-1][p]+d*l[f][p]}return l[3]}function F0(t,e,n,s){const o=e.f(),r=e.f(),a=e.i(),i=e.i(),l=U0(e),c=[e.d(),e.d(),e.d()],f=n?[e.f(),e.f(),e.f(),e.f(),e.f(),e.f()]:null,h=ml(t,a,/^NiBSplineData$/),u=ml(t,i,/^NiBSplineBasisData$/),d={pos:l.t?[[0,l.t]]:null,rot:l.q?[[0,l.q]]:null,scl:l.s!==null?[[0,[l.s,l.s,l.s]]]:null};if(!h||!u)return d;const p=h.d(),m=new Float32Array(p);for(let A=0;A<p;A++)m[A]=h.f();const g=h.d(),w=new Int16Array(g);for(let A=0;A<g;A++)w[A]=h.s();const y=u.d(),M=Math.max(0,r-o),x=Math.max(1,Math.round(M*T0)),v=(A,S)=>{const $=c[A];if($===65535||$===4294967295||!y)return null;const T=n?(_,R)=>f[A*2]+f[A*2+1]*(w[$+_*S+R]??0)/32767:(_,R)=>m[$+_*S+R]??0,U=[];for(let _=0;_<=x;_++)U.push([o-s+M*_/x,t4(T,y,S,_/x)]);return U},b=v(0,3),k=v(1,4),E=v(2,1);return b&&(d.pos=b),k&&(d.rot=k.map(([A,S])=>{const $=Math.hypot(...S)||1;return[A,S.map(T=>T/$)]})),E&&(d.scl=E.map(([A,[S]])=>[A,[S,S,S]])),d}function e4(t,e,n){const s=t.h.blocks[e];if(!s)return null;const o=new pl(t.buf,s.start,s.end);if(s.type==="NiTransformInterpolator"){const r=U0(o),a=o.i(),i=ml(t,a,/^Ni(Transform|Keyframe)Data$/),l=i?Jv(i):{pos:null,rot:null,scl:null},c=f=>f?f.map(([h,u])=>[h-n,u]):null;return l.pos=c(l.pos)??(r.t?[[0,r.t]]:null),l.rot=c(l.rot)??(r.q?[[0,r.q]]:null),l.scl=c(l.scl)??(r.s!==null?[[0,[r.s,r.s,r.s]]]:null),l}return s.type==="NiBSplineCompTransformInterpolator"?F0(t,o,!0,n):s.type==="NiBSplineTransformInterpolator"?F0(t,o,!1,n):null}function _0(t,e,n){let s;try{s=go(t)}catch{return[]}const o={buf:t,h:s},r=[];return s.blocks.forEach(a=>{if(a.type==="NiControllerSequence")try{const i=new pl(t,a.start,a.end),l=Rr(s,i.i()),c=i.d();i.d();const f=[];for(let g=0;g<c;g++){const w=i.i();i.i(),i.b();const y=Rr(s,i.i());i.i();const M=Rr(s,i.i());i.i(),i.i(),f.push({interp:w,node:y,ctrl:M})}i.f(),i.i(),i.d(),i.f();const h=i.f(),u=i.f(),d=new Map;i.i();const p=Rr(s,i.i());for(const g of f){if(g.ctrl&&!/Transform|Keyframe/.test(g.ctrl))continue;const w=e.get(g.node.toLowerCase());if(w===void 0||d.has(w))continue;const y=e4(o,g.interp,h);y&&(y.pos||y.rot||y.scl)&&d.set(w,y)}const m=p?e.get(p.toLowerCase()):void 0;if(d.size&&m!==void 0&&!d.has(m)&&d.set(m,{pos:[[0,[0,0,0]]],rot:[[0,[1,0,0,0]]],scl:null}),d.size){const g=Math.max(0,u-h);r.push({kind:"node",name:l||`sequence ${r.length}`,tps:1,chan:d,duration:g,frames:Math.round(g*T0)+1,source:n})}}catch(i){console.warn("NiControllerSequence:",i)}}),r}class qe{constructor(e,n,s){this.pos=n,this.end=s,this.dv=new DataView(e),this.u8=new Uint8Array(e)}pos;end;dv;u8;b(){return this.dv.getUint8(this.pos++)}w(){const e=this.dv.getUint16(this.pos,!0);return this.pos+=2,e}d(){const e=this.dv.getUint32(this.pos,!0);return this.pos+=4,e}i(){const e=this.dv.getInt32(this.pos,!0);return this.pos+=4,e}f(){const e=this.dv.getFloat32(this.pos,!0);return this.pos+=4,e}skip(e){this.pos+=e}ascii(e){let n="";for(let s=0;s<e;s++)n+=String.fromCharCode(this.u8[this.pos+s]);return this.pos+=e,n}}function go(t){const e=new Uint8Array(t);let n=0;for(;n<128&&n<e.length&&e[n]!==10;)n++;let s="";for(let x=0;x<n;x++)s+=String.fromCharCode(e[x]);const o=/(Gamebryo|NetImmerse).*Version\s+(\d+)\.(\d+)\.(\d+)\.(\d+)/.exec(s);if(!o)throw new Error("Не NIF файл");const r=`${o[2]}.${o[3]}.${o[4]}.${o[5]}`;if((+o[2]<<24|+o[3]<<16|+o[4]<<8|+o[5])>>>0!==335675399)throw new Error(`NIF версии ${r} пока не поддерживается (нужна 20.2.0.7: Fallout 3/NV, Skyrim, Skyrim SE). Oblivion/Morrowind — следующая часть.`);const i=new qe(t,n+1,t.byteLength);if(i.d(),i.b()===0)throw new Error("Big-endian NIF не поддерживается");const l=i.d(),c=i.d();let f=0;l>=3&&(f=i.d(),i.skip(i.b()),f>130&&i.d(),f<131&&i.skip(i.b()),i.skip(i.b()),f>=103&&i.skip(i.b()));const h=i.w(),u=[];for(let x=0;x<h;x++)u.push(i.ascii(i.d()));const d=[];for(let x=0;x<c;x++)d.push(i.w()&32767);const p=[];for(let x=0;x<c;x++)p.push(i.d());const m=i.d();i.d();const g=[];for(let x=0;x<m;x++)g.push(i.ascii(i.d()));i.skip(4*i.d());const w=[];let y=i.pos;for(let x=0;x<c;x++)w.push({type:u[d[x]],start:y,end:y+p[x]}),y+=p[x];let M=[];try{const x=new qe(t,y,t.byteLength),v=x.d();for(let b=0;b<v;b++)M.push(x.i())}catch{}return M.length||(M=[0]),{version:r,bsver:f,userVersion:l,blocks:w,strings:g,roots:M}}function wl(t,e){const n=t.d(),s=t.d();t.skip(s*4),t.i();const o=e.bsver<=26?t.w():t.d(),r=[t.f(),t.f(),t.f()],a=[];for(let c=0;c<9;c++)a.push(t.f());const i=t.f(),l=[];if(e.bsver<=34){const c=t.d();for(let f=0;f<c;f++)l.push(t.i())}return t.i(),{name:n<e.strings.length?e.strings[n]:"",flags:o,t:r,rot:a,scale:i,props:l}}function C0(t,e,n){const s=new qe(t,n.start,n.end),o=wl(s,e),r=s.d(),a=[];for(let i=0;i<r;i++)a.push(s.i());return{...o,kids:a}}function n4(t,e,n){const s=new qe(t,n.start,n.end),o=wl(s,e),r=s.i(),a=s.i(),i=s.d();s.skip(i*8+5);let l=-1;return e.bsver>34&&(l=s.i(),s.i()),{...o,data:r,skin:a,shader:l}}function s4(t,e,n,s,o){try{const r=new qe(t,e.start,e.end);s&&r.d();const a=r.w();if(r.skip(2),!r.b()||a===0)return null;const i=new Float32Array(a*3);for(let p=0;p<a*3;p++)i[p]=r.f();const l=r.w(),c=r.b();let f=null;if(c){f=new Float32Array(a*3);for(let p=0;p<a*3;p++)f[p]=r.f();l&4096&&r.skip(a*24)}r.skip(16),r.b()&&r.skip(a*16);const h=o===0?l&63:l&1,u=new Float32Array(a*2);for(let p=0;p<h;p++)for(let m=0;m<a;m++){const g=r.f(),w=r.f();p===0&&(u[m*2]=g,u[m*2+1]=w)}r.skip(2),r.i();let d=[];if(n){r.w();const p=r.w(),m=[];for(let w=0;w<p;w++)m.push(r.w());if(r.b())for(const w of m){const y=[];for(let M=0;M<w;M++)y.push(r.w());d=d.concat(No(y))}}else{const p=r.w(),m=r.d();if(r.b()){if(m!==p*3)return null;for(let y=0;y<m;y++)d.push(r.w())}const w=r.w();for(let y=0;y<w;y++)r.skip(r.w()*2)}if(r.pos!==e.end)return null;for(const p of d)if(p>=a)return null;return{nv:a,pos:i,nor:f,uv:u,tris:d}}catch{return null}}function o4(t,e,n){for(const s of[!0,!1])for(const o of[0,1]){const r=s4(t,e,n,s,o);if(r)return r}return null}function r4(t,e,n){const s=new qe(t,n.start,n.end),o=wl(s,e);s.skip(16),e.bsver===155&&s.skip(24);const r=s.i(),a=s.i();s.i();const i=s.d(),l=s.d(),c=e.bsver>=130?s.d():s.w(),f=s.w();if(!s.d()||!f)return{...o,skin:r,shader:a,geom:null};const u=(i&15)*4,d=E=>(i>>>4+4*E&15)*4,p=l>>>12&2047,m=e.bsver===100||(p&1024)!==0,g=(p&2)!==0,w=(p&8)!==0,y=s.dv,M=s.pos,x=new Float32Array(f*3),v=new Float32Array(f*3),b=new Float32Array(f*2);for(let E=0;E<f;E++){const A=M+E*u;for(let S=0;S<3;S++)x[E*3+S]=m?y.getFloat32(A+S*4,!0):Fe(y.getUint16(A+S*2,!0));if(g){const S=A+d(1);b[E*2]=Fe(y.getUint16(S,!0)),b[E*2+1]=Fe(y.getUint16(S+2,!0))}if(w){const S=A+d(3);for(let $=0;$<3;$++)v[E*3+$]=y.getUint8(S+$)/255*2-1}}s.pos=M+f*u;const k=[];for(let E=0;E<c*3;E++)k.push(s.w());return{...o,skin:r,shader:a,geom:{nv:f,pos:x,nor:w?v:null,uv:b,tris:k}}}function L0(t){const e=[];for(let o=0;o<9;o++)e.push(t.f());const n=[t.f(),t.f(),t.f()],s=t.f();return wo({t:n,rot:e,scale:s})}function a4(t,e){const n=new qe(t,e.start,e.end),s=L0(n),o=n.d(),r=n.b(),a=[],i=[];for(let l=0;l<o;l++){a.push(L0(n)),n.skip(16);const c=n.w(),f=[];if(r)for(let h=0;h<c;h++){const u=n.w();f.push([u,n.f()])}i.push(f)}if(n.pos!==e.end)throw new Error("NiSkinData: размер не сошёлся");return{overall:s,boneT:a,weights:i}}function R0(t,e,n){try{const s=new qe(t,e.start,e.end),o=s.d(),r=[];for(let a=0;a<o;a++){const i=s.w(),l=s.w(),c=s.w(),f=s.w(),h=s.w(),u=[];for(let y=0;y<c;y++)u.push(s.w());let d=null,p=null,m=null;if(s.b()){d=[];for(let y=0;y<i;y++)d.push(s.w())}if(s.b()){p=new Float32Array(i*h);for(let y=0;y<i*h;y++)p[y]=s.f()}const g=[];for(let y=0;y<f;y++)g.push(s.w());let w=[];if(s.b())if(f)for(const y of g){const M=[];for(let x=0;x<y;x++)M.push(s.w());w=w.concat(No(M))}else for(let y=0;y<l*3;y++)w.push(s.w());if(s.b()&&(m=s.u8.slice(s.pos,s.pos+i*h),s.skip(i*h)),s.skip(n),s.pos>e.end)return null;r.push({nv:i,bones:u,map:d,weights:p,nw:h,tris:w,boneIdx:m})}return s.pos===e.end?r:null}catch{return null}}function O0(t,e,n){const s=e.blocks[n];if(!s||!/SkinInstance$/.test(s.type))throw new Error("нет NiSkinInstance");const o=new qe(t,s.start,s.end),r=o.i(),a=o.i(),i=o.i(),l=o.d(),c=[];for(let m=0;m<l;m++)c.push(o.i());const f=[];if(s.type==="BSDismemberSkinInstance"){const m=o.d();for(let g=0;g<m;g++)o.w(),f.push(o.w())}const h=e.blocks[r];if(!h||h.type!=="NiSkinData")throw new Error("нет NiSkinData");const u=a4(t,h);if(u.boneT.length!==l)throw new Error("число костей в NiSkinData и NiSkinInstance не совпадает");let d=[];const p=e.blocks[a];return p&&p.type==="NiSkinPartition"&&(d=R0(t,p,0)??R0(t,p,2)??[]),{bones:c,root:i,overall:u.overall,boneT:u.boneT,weights:u.weights,partitions:d,bodyParts:f}}function i4(t,e){const n=Array.from({length:e},()=>[]);let s=!1;for(const a of t.partitions){if(!a.weights||!a.boneIdx){s=!1;break}for(let i=0;i<a.nv;i++){const l=a.map?a.map[i]:i;if(!(l>=e)){n[l]=[];for(let c=0;c<a.nw;c++){const f=a.weights[i*a.nw+c];f>0&&n[l].push([a.bones[a.boneIdx[i*a.nw+c]],f])}s=!0}}}if(!s){for(const a of n)a.length=0;t.weights.forEach((a,i)=>{for(const[l,c]of a)l<e&&c>0&&(n[l].push([i,c]),s=!0)})}if(!s)return null;const o=new Uint16Array(e*4),r=new Float32Array(e*4);for(let a=0;a<e;a++){const i=n[a].sort((c,f)=>f[1]-c[1]).slice(0,4),l=i.reduce((c,f)=>c+f[1],0)||1;i.forEach(([c,f],h)=>{o[a*4+h]=c,r[a*4+h]=f/l})}return{bone:o,weight:r}}function l4(t,e){const n=new Map;return e.blocks.forEach((s,o)=>{if(s.type==="BSShaderTextureSet")try{const r=new qe(t,s.start,s.end),a=r.d(),i=[];for(let l=0;l<a;l++)i.push(r.ascii(r.d()));n.set(o,i)}catch{}}),n}function I0(t,e,n,s){const o=new DataView(t);for(const r of s){const a=e.blocks[r];if(a)for(const i of[0,1])for(let l=a.start;l+4<=a.end;l++){if((l-a.start)%4===0!=(i===0))continue;const c=o.getInt32(l,!0);if(n.has(c))return n.get(c)[0]??""}}return""}const c4=t=>t!==void 0&&t>=100&&t<300,yl=/^(editormarker|markerx|collisiongeom)/i,f4=new Set(["NiTriShape","NiTriStrips","BSLODTriShape","BSSegmentedTriShape"]),u4=new Set(["BSTriShape","BSDynamicTriShape","BSMeshLODTriShape","BSSubIndexTriShape"]);function wo(t){const e=nn();for(let n=0;n<3;n++)for(let s=0;s<3;s++)e[s*4+n]=t.rot[n*3+s]*t.scale;return e[12]=t.t[0],e[13]=t.t[1],e[14]=t.t[2],e}function D0(t){const e=go(t),n=[],s=new Set,o=(r,a)=>{const i=e.blocks[r];if(!(!i||s.has(r)||!/Node$/.test(i.type))){s.add(r);try{const l=C0(t,e,i),c=n.push({name:l.name,parent:a,local:wo(l),offset:null})-1;for(const f of l.kids)o(f,c)}catch{}}};for(const r of e.roots)o(r,-1);return n}const h4=(t,e)=>{for(let n=0;n<16;n++)if(Math.abs(t[n]-e[n])>.001*Math.max(1,Math.abs(e[n])))return!1;return!0};function d4(t,e={}){const n=go(t),s=l4(t,n),o=[],r=[],a=new Set,i=new Set,l=new Map,c=[],f=(k,E,A)=>{const S=E.nv,$=E.nor?Float32Array.from(E.nor):ge(E.pos,E.tris);if(E.nor)for(let T=0;T<S;T++){const U=Math.hypot($[T*3],$[T*3+1],$[T*3+2])||1;$[T*3]/=U,$[T*3+1]/=U,$[T*3+2]/=U}return{name:k,texture:A,shader:"",vertexCount:S,pos:E.pos,nor:$,uv:E.uv,bone:new Float32Array(S*4),weight:new Float32Array(S*4),indices:$e(S,E.tris),skinned:!0}},h=(k,E)=>{for(let A=0;A<k.vertexCount;A++)k.bone[A*4]=E,k.weight[A*4]=1;r.push(k)},u=(k,E,A)=>o.push({name:k,parent:E,local:wo(A),offset:null,helper:!0})-1,d=(k,E,A)=>{const S=n.blocks[k];if(!S||a.has(k))return;a.add(k);const $=S.type;try{if(/Node$/.test($)){const T=C0(t,n,S);if(yl.test(T.name))return;const U=o.push({name:T.name,parent:E,local:wo(T),offset:null})-1;l.set(k,U);const _=$==="NiSwitchNode"||$==="NiLODNode"?T.kids.slice(0,1):T.kids;for(const R of _)d(R,U,A||!!(T.flags&1))}else if(f4.has($)){const T=n4(t,n,S);if(A||T.flags&1||yl.test(T.name)||T.data<0)return;const U=n.blocks[T.data],_=U?o4(t,U,$==="NiTriStrips"):null;if(!_){i.add(`блок ${$} #${k}: геометрия не разобрана`);return}const R=I0(t,n,s,n.bsver>34?[T.shader]:T.props);let L=null;if(T.skin>=0)try{L=O0(t,n,T.skin)}catch(D){i.add(`${T.name||$}: скин не разобран (${D.message})`)}if(L){const D=L.partitions.map((F,C)=>c4(L.bodyParts[C]));if(D.some(Boolean)&&L.partitions.every(F=>F.tris.length)&&(_.tris=[]),_.tris.length||L.partitions.forEach((F,C)=>{if(!D[C])for(const P of F.tris)_.tris.push(F.map?F.map[P]:P)}),!_.tris.length&&D.some(Boolean))return}if(!_.tris.length){i.add(`${T.name||$}: нет треугольников`);return}const O=L?i4(L,_.nv):null,N=u(T.name,E,T);L&&O?(c.push({mesh:f(T.name||$,_,R),skin:L,local:O.bone}),c[c.length-1].mesh.weight.set(O.weight)):h(f(T.name||$,_,R),N)}else if(u4.has($)){const T=r4(t,n,S);if(A||T.flags&1||yl.test(T.name))return;if(!T.geom){T.skin>=0&&i.add(`${T.name||$}: вершины в NiSkinPartition (Skyrim SE) — пока не поддержано`);return}h(f(T.name||$,T.geom,I0(t,n,s,[T.shader])),u(T.name,E,T))}}catch(T){i.add(`блок ${$} #${k}: ${T.message}`)}};for(const k of n.roots)d(k,-1,!1);let p=o,m=o.map((k,E)=>E),g="";if(e.skeleton&&c.length)try{const k=D0(e.skeleton),E=new Map;k.forEach((S,$)=>{S.name&&!E.has(S.name.toLowerCase())&&E.set(S.name.toLowerCase(),$)}),p=k.slice(),m=[];let A=0;o.forEach((S,$)=>{const T=!S.helper&&S.name?E.get(S.name.toLowerCase()):void 0;if(T!==void 0){m.push(T),A++;return}m.push(p.push({...S,parent:S.parent>=0?m[S.parent]:-1})-1)}),g=`Скелет: ${e.skeletonName??"skeleton.nif"} (${k.length} узлов, совпало с моделью ${A})`}catch(k){i.add(`skeleton.nif: ${k.message}`),p=o,m=o.map((E,A)=>A)}for(const k of r)for(let E=0;E<k.vertexCount;E++)k.bone[E*4]=m[k.bone[E*4]];let w=0;for(const k of c){const E=k.skin.bones.map((S,$)=>{let T=l.get(S);T===void 0&&(i.add(`кость #${S} вне иерархии`),T=0);const U=m[T],_=Lt(k.skin.boneT[$],k.skin.overall),R=p[U];return R.offset?h4(R.offset,_)?U:p.push({name:`${R.name} (bind)`,parent:U,local:_,offset:null,helper:!0})-1:(R.offset=_,U)}),A=k.mesh;for(let S=0;S<A.vertexCount*4;S++)A.bone[S]=E[k.local[S]]??0;r.push(A),w++}if(!r.length)throw new Error("В NIF не найдено поддерживаемой геометрии"+(i.size?" ("+[...i].join("; ")+")":""));const y=new Kt(p);y.pose(null,0,!1);const M=p4(p),x=_0(t,M,"model"),v={};n.blocks.forEach(k=>{v[k.type]=(v[k.type]??0)+1});const b=[`NIF ${n.version}, BS version ${n.bsver}, блоков: ${n.blocks.length}`,`Мешей: ${r.length} (со скином ${w}), вершин: ${le(r)}, треугольников: ${Ut(r)}`];return g?b.push(g):w&&b.push("skeleton.nif не найден — анимации .kf применяются к костям самой модели."),i.size&&b.push("Замечания:",...[...i].slice(0,8).map(k=>" • "+k)),{meshes:r,skeleton:y,motions:x,bounds:Jt(y,r),flipZ:!1,defaultZUp:!0,cameraYaw:Math.PI,info:b,meta:{types:v},loadMotions:async k=>{const E=_0(await k.arrayBuffer(),M,k.name);if(!E.length)throw new Error(`${k.name}: в файле нет NiControllerSequence с костями этой модели`);return E}}}function p4(t){const e=new Map;return t.forEach((n,s)=>{n.name&&!n.helper&&!e.has(n.name.toLowerCase())&&e.set(n.name.toLowerCase(),s)}),e}function m4(t){try{return go(t).blocks.some(e=>/SkinInstance$/.test(e.type))}catch{return!1}}const g4=Object.freeze(Object.defineProperty({__proto__:null,convertNif:d4,hasSkin:m4,localMatrix:wo,nifNodes:D0,parseHeader:go,parseSkin:O0},Symbol.toStringTag,{value:"Module"})),Un={u8:1,s8:1,bool:1,bool8:1,u16:2,s16:2,u32:4,s32:4,fp32:4,u64:8,s64:8,vec2f:8,vec3f:12,vec4f:16,vec2i:8,vec3i:12,vec4i:16,vec4s16:8,matrix:64,matrix_43:48,stringz:4};function w4(t){const e=new DataView(t.buffer,t.byteOffset,t.byteLength),n=e.getUint32(0,!0),s=[],o=new TextDecoder("windows-1251");let r=4;for(let a=0;a<n&&r<t.length;a++){let i=r;for(;i<t.length&&t[i];)i++;s.push(o.decode(t.subarray(r,i))),r=i+1}return s}function y4(t){const e=new DataView(t.buffer,t.byteOffset,t.byteLength);if(t[0]!==5&&t[0]!==4)throw new Error(`Конфиг 4A: тип ${t[0]} не поддерживается`);let n=1,s=null,o=[];for(;n+8<=t.length;){const d=e.getUint32(n,!0),p=e.getUint32(n+4,!0);if(n+8+p>t.length)throw new Error("Конфиг 4A: чанк за концом файла");d===1?s=[n+8,n+8+p]:d===2&&(o=w4(t.subarray(n+8,n+8+p))),n+=8+p}if(!s)throw new Error("Конфиг 4A: нет чанка данных");const r=o,a=d=>r[d]??`#${d}`,i=[],l=new Map,c=["u16","u32","u8","u64","vec3f","vec4f"],f=(d,p)=>{if(d===p)return!0;if(d+8>p)return!1;const m=e.getUint32(d,!0),g=e.getUint32(d+4,!0);return m<r.length?g<r.length:d+8+g<=p&&(t[d+8]===5||t[d+8]===4)&&e.getUint32(d+9,!0)<r.length},h=(d,p)=>{const m=[];for(;d+8<=p;){const g=e.getUint32(d,!0),w=e.getUint32(d+4,!0);if(g>=r.length){const v=d+8,b=a(e.getUint32(v+1,!0));i.push(b),m.push({name:b,type:"section",value:h(v+5,v+w)}),i.pop(),d=v+w;continue}d+=8;const y=a(g),M=a(w);if(M==="array"&&d+4<=p&&e.getUint32(d,!0)<r.length){m.push({name:y,type:"array-hint",value:null});continue}if(M==="array"){const v=e.getUint32(d+4,!0),b=d+8;m.push({name:y,type:M,value:h(b+5,b+v)}),d=b+v;continue}if(M.endsWith("_array")&&Un[M.slice(0,-6)]){const v=Un[M.slice(0,-6)],b=e.getUint32(d,!0);m.push({name:y,type:M,value:Array.from({length:b},(k,E)=>u(M.slice(0,-6),d+4+E*v))}),d+=4+b*v;continue}let x=Un[M]?M:M.split(",").pop().trim();if(!Un[M]){if(d+4>p||e.getUint32(d,!0)===g){m.push({name:y,type:M,value:null});continue}if(!Un[x]){let v=l.get(M);if(!v){if(v=c.find(b=>f(d+Un[b],p)),!v)throw new Error(`Конфиг 4A: неизвестный тип «${M}» у поля «${y}» (${i.join("/")})`);l.set(M,v)}x=v}}m.push({name:y,type:M,value:u(x,d)}),d+=Un[x]}return m},u=(d,p)=>{switch(d){case"u8":case"bool":case"bool8":return e.getUint8(p);case"s8":return e.getInt8(p);case"u16":return e.getUint16(p,!0);case"s16":return e.getInt16(p,!0);case"u32":return e.getUint32(p,!0);case"s32":return e.getInt32(p,!0);case"fp32":return e.getFloat32(p,!0);case"u64":case"s64":return Number(e.getBigUint64(p,!0));case"stringz":return a(e.getUint32(p,!0));case"vec4s16":return[0,1,2,3].map(m=>e.getInt16(p+m*2,!0));default:{const m=Un[d]/4,g=d.endsWith("i");return Array.from({length:m},(w,y)=>g?e.getInt32(p+y*4,!0):e.getFloat32(p+y*4,!0))}}};return{strings:o,root:h(s[0],s[1])}}function Ir(t,e){return t.find(n=>n.name===e&&n.value!==null)}const Dr=(t,e)=>{const n=Ir(t,e)?.value;return typeof n=="string"?n:""},P0=(t,e)=>{const n=Ir(t,e)?.value;return typeof n=="number"?n:0};function b4(t){const e=Ir(t.root,"entities");if(!e||!Array.isArray(e.value))return[];const n=[];for(const s of e.value){if(s.type!=="section"||!Array.isArray(s.value))continue;const o=s.value,a=(o.filter(i=>i.type==="pose, matrix"&&Array.isArray(i.value)).find(i=>i.name!=="att_offset")??null)?.value;n.push({name:Dr(o,"name")||s.name,cls:Dr(o,"class"),staticData:Dr(o,"static_data_key"),id:P0(o,"id"),parent:Ir(o,"parent_id")?P0(o,"parent_id"):65535,pose:a??null,visual:Dr(o,"visual"),fields:o})}return n}function N0(t){if(t.length>=8&&t[0]===108&&t[1]===101&&t[2]===118&&t[3]===108){const e=new DataView(t.buffer,t.byteOffset,t.byteLength).getUint32(4,!0);return $d(t.subarray(8),e)}return t}const v4=new Set(["PLAYERS_HANDS","PLAYERS_KNIFE","HANDS_FOR_DREZINA","PLAYER"]);async function x4(t,e){const n=b4(y4(N0(t))),s=new Map,o=new Set,r=new Map,a=new Map,i={meshes:[],protos:[],placements:[]},l=m=>[...r.keys()].indexOf(m);let c=0,f=0,h=0;for(const m of n){if(!m.visual||!m.pose||v4.has(m.cls))continue;if(m.parent!==65535){h++;continue}const g=m.visual.toLowerCase().replace(/\\/g,"/");let w=s.get(g);if(w===void 0){const v=await e(g+".model");if(w=null,v)try{w=(await Mi(v,e)).parts}catch{w=null}w?.length||(o.add(g),w=null),s.set(g,w)}if(!w)continue;const y=m.pose;let M=a.get(g);M===void 0&&(M=i.protos.length,a.set(g,M),i.protos.push({name:g,parts:w.map(v=>{const b=`${v.texture}|${v.shader}|${v.material}`;return r.has(b)||r.set(b,{texture:v.texture,shader:v.shader,material:v.material,pos:[],nor:[],uv:[],idx:[]}),{mesh:l(b),pos:Float32Array.from(v.pos),nor:Float32Array.from(v.nor),uv:Float32Array.from(v.uv),indices:Uint32Array.from(v.idx)}})}));const x=[];i.placements.push({proto:M,name:m.name,pose:y.slice(0,16),group:m.cls||"прочее",ranges:x});for(const[v,b]of w.entries()){const k=`${b.texture}|${b.shader}|${b.material}`;let E=r.get(k);E||r.set(k,E={texture:b.texture,shader:b.shader,material:b.material,pos:[],nor:[],uv:[],idx:[]});const A=E.pos.length/3,S=b.pos.length/3;x.push({mesh:l(k),part:v,vStart:A,vCount:S,iStart:E.idx.length,iCount:b.idx.length-b.idx.length%3});for(let T=0;T<S;T++){const U=b.pos[T*3],_=b.pos[T*3+1],R=b.pos[T*3+2];E.pos.push(U*y[0]+_*y[4]+R*y[8]+y[12],U*y[1]+_*y[5]+R*y[9]+y[13],U*y[2]+_*y[6]+R*y[10]+y[14]);const L=b.nor[T*3],O=b.nor[T*3+1],N=b.nor[T*3+2],D=L*y[0]+O*y[4]+N*y[8],F=L*y[1]+O*y[5]+N*y[9],C=L*y[2]+O*y[6]+N*y[10],P=Math.hypot(D,F,C)||1;E.nor.push(D/P,F/P,C/P),E.uv.push(b.uv[T*2],b.uv[T*2+1])}const $=y[0]*(y[5]*y[10]-y[6]*y[9])-y[1]*(y[4]*y[10]-y[6]*y[8])+y[2]*(y[4]*y[9]-y[5]*y[8]);for(let T=0;T+2<b.idx.length;T+=3)$<0?E.idx.push(A+b.idx[T],A+b.idx[T+2],A+b.idx[T+1]):E.idx.push(A+b.idx[T],A+b.idx[T+1],A+b.idx[T+2]);f+=b.idx.length/3}c++}const u=xi([...r.values()],!1,!0).map(m=>({...m,name:`объекты · ${m.texture||"?"}`}));i.meshes=u.map((m,g)=>g);const d=n.filter(m=>m.visual).length,p=[`level.bin: объектов ${n.length}, с моделью ${d}; показано ${c} (моделей ${[...s.values()].filter(Boolean).length}, треугольников ${f})`,...h?[`Прикреплённые к другим объектам (оружие, руки) не показаны: ${h}`]:[],...o.size?[`Модели не найдены или не прочитаны: ${o.size} (${[...o].slice(0,5).join(", ")}${o.size>5?"…":""})`]:[]];return{meshes:u,info:p,entities:n,instances:i}}const M4=Object.freeze(Object.defineProperty({__proto__:null,placeLevelObjects:x4,unpackLevelBin:N0},Symbol.toStringTag,{value:"Module"})),z0=t=>Si(t[0],t[1],t[2]),Cs=(t,e)=>Array.isArray(t)&&t.length>=3?t.slice(0,3).map(n=>typeof n=="number"?n:e):[e,e,e],Pr=t=>Array.isArray(t)?t:t&&typeof t=="object"?Object.values(t):[],Jn=(t,e)=>typeof t=="number"&&Number.isFinite(t)?t:e,he=t=>typeof t=="string"?t:"";function B0(t){const e=mn(t),n=e.render_visuals;if(!Array.isArray(n))throw new Error("client_project: нет render_visuals");const s=[];for(const o of n){const r=qt(o),a=r?.model_name;typeof a!="string"||!a||s.push({model:gt(a),pos:Cs(r.position,0),rot:Cs(r.rotation,0),scale:Cs(r.scale,1)})}return{name:typeof e.project_name=="string"?e.project_name:"",visuals:s}}function V0(t){const e=new DataView(t.buffer,t.byteOffset,t.byteLength);let n=null,s=null;for(let o=0;o+8<=t.length;){const r=e.getUint32(o,!0),a=e.getUint32(o+4,!0),i=o+8;if(i+a>t.length)throw new Error(".veg: чанк выходит за конец файла");if(r===88||r===89){const l=new Float32Array(Math.floor(a/12)*3);for(let c=0;c<l.length;c++)l[c]=e.getFloat32(i+c*4,!0);r===88?n=l:s=l}o=i+a}if(!n)throw new Error(".veg: нет позиций (чанк 88)");return{pos:n,nor:s&&s.length===n.length?s:null}}function j0(t){const e=[];for(const n of t){const s=qt(n);if(!(s?.game_object_type!=="vegetation"||s.is_enabled===0))for(const o of Pr(s.layers)){const r=qt(o);if(!r||!he(r.filename))continue;const a=Pr(r.model_def).map(qt).filter(i=>!!i&&!!he(i.name)).map(i=>({name:gt(he(i.name)),p:Math.max(0,Jn(i.probability,1)),scale:Jn(i.scale,1)}));a.length&&e.push({name:he(r.name)||he(r.filename),file:gt(he(r.filename)),models:a,randomDir:Jn(r.random_dir,1)!==0,randomScale:Jn(r.random_scale,0),faceNormal:Jn(r.use_face_normal,0)!==0,visible:Jn(r.visible,1)!==0})}}return e}function bl(t){let e=t>>>0;return()=>{e=e+1831565813>>>0;let n=e;return n=Math.imul(n^n>>>15,n|1),n^=n+Math.imul(n^n>>>7,n|61),((n^n>>>14)>>>0)/4294967296}}const k4=t=>{let e=2166136261;for(let n=0;n<t.length;n++)e=Math.imul(e^t.charCodeAt(n),16777619);return e>>>0},E4=(t,e)=>[t[0]*e[0]-t[1]*e[1]-t[2]*e[2]-t[3]*e[3],t[0]*e[1]+t[1]*e[0]+t[2]*e[3]-t[3]*e[2],t[0]*e[2]-t[1]*e[3]+t[2]*e[0]+t[3]*e[1],t[0]*e[3]+t[1]*e[2]-t[2]*e[1]+t[3]*e[0]];function A4(t,e,n){const s=Math.hypot(t,e,n);if(!s)return[1,0,0,0];if(t/=s,e/=s,n/=s,e<-.999999)return[0,1,0,0];const o=[1+e,n,0,-t],r=Math.hypot(...o);return o.map(a=>a/r)}function G0(t,e,n){const s=bl(k4(t.file)),o=t.models.reduce((a,i)=>a+i.p,0),r=[];for(let a=0;a<e.length/3;a++){let i=0;if(t.models.length>1)if(o>0){let d=s()*o;for(;i<t.models.length-1&&(d-=t.models[i].p)>=0;)i++;for(;i>0&&t.models[i].p===0;)i--}else i=Math.floor(s()*t.models.length);const l=t.models[i],c=t.randomDir?s()*Math.PI*2:0,f=l.scale*(1+(s()*2-1)*t.randomScale);let h=[Math.cos(c/2),0,Math.sin(c/2),0];t.faceNormal&&n&&(h=E4(A4(n[a*3],n[a*3+1],n[a*3+2]),h));const u=new Float64Array(16);Ae([e[a*3],e[a*3+1],e[a*3+2]],h,[f,f,f],u),r.push({model:i,M:u})}return r}function X0(t,e){if(t.length<2)return null;const n=[],s=[],o=[],r=[],a=Math.max(e,.01)/2;for(const[i,l]of[[a,0],[0,a]]){const c=n.length/3;t.forEach((f,h)=>{if(n.push(f[0]-i,f[1]-l,f[2],f[0]+i,f[1]+l,f[2]),s.push(l?1:0,i?1:0,0,l?1:0,i?1:0,0),o.push(0,h,1,h),h){const u=c+h*2;r.push(u-2,u-1,u,u-1,u+1,u,u-2,u,u-1,u-1,u,u+1)}})}return{name:"wire",texture:"",vertexCount:n.length/3,pos:Float32Array.from(n),nor:Float32Array.from(s),uv:Float32Array.from(o),bone:new Float32Array(0),weight:new Float32Array(0),indices:Uint32Array.from(r),skinned:!1,color:[.08,.08,.08]}}const H0=1,W0=15e5,vl=t=>t.material?.split("/").pop()||t.texture.split("/").pop()||"?";async function $4(t,e,n){const s=gt(t.slice(vr.length).trim()),o=e.archive??n.archives.find(O=>O.lookup(s)),r=o?.lookup(s);if(!o||!r)throw new Error("Уровень Survarium открывается только из архивов resources.*.db");const a=await o.read(r),{name:i,visuals:l}=B0(a),c=Pr(mn(a).client_objects),f=new vs,h=new Set,u=new Set,d=new Map,p=async(O,N,D,F,C=0)=>{const P=`${D}|${C}|${O}`;if(d.has(P))return d.get(P);let I=null;for(const B of N){const z=`models/${O}.${B}`;if(o.lookup(z)){try{const j=await xr(o,z,null,null,C);for(const X of j.missing)u.add(X);j.meshes.length&&(I={pi:f.proto(O,j.meshes,D,F),parts:j.meshes})}catch{}break}}return I||h.add(O),d.set(P,I),I},m=O=>O.split("/").slice(0,-1).join("/")||"models",g=(O,N,D)=>{const F=new Float64Array(16);return Ae(O,z0(N),D,F),F};let w=0;for(const O of l){const N=await p(O.model,["model","skinned_model"],"static",vl);N&&(f.place(N.pi,N.parts,g(O.pos,O.rot,O.scale),O.model.split("/").pop(),m(O.model)),w++)}let y=0,M=0;const x=[],v=[];for(const O of c){const N=qt(O);if(!N||N.is_enabled===0)continue;const D=he(N.game_object_type),F=Cs(N.position,0),C=Cs(N.rotation,0),P=Cs(N.scale,1);if(D==="skeleton_visual"&&he(N.model_name)){const I=gt(he(N.model_name)),B=await p(I,["skinned_model","model"],"static",vl);if(!B)continue;f.place(B.pi,B.parts,g(F,C,P),he(N.name)||I.split("/").pop(),`skeleton_visual/${m(I)}`),y++}else if(D==="light")x.push({name:he(N.name)||"light",pos:F});else if(D==="particle")v.push({name:he(N.particle_name)||he(N.name)||"particle",pos:F});else if(D==="wire_set"){const I=Pr(N.points).filter(j=>Array.isArray(j)&&j.length>=3&&j.every(X=>typeof X=="number")),B=X0(I,Jn(N.wire_width,.03));if(!B)continue;const z=f.proto(`wire ${he(N.name)}`,[B],"wire",()=>"Провода");f.place(z,[B],g(F,C,P),he(N.name)||"wire","wire_set"),M++}}const b=s.split("/").slice(0,-2).join("/"),k=[],E=[];let A=0,S=0;for(const O of j0(c)){if(!O.visible)continue;const N=o.lookup(`${b}/${O.file}`);if(!N){k.push(`${O.file} не найден`);continue}let D;try{D=V0(await o.read(N))}catch(P){k.push(`${O.file}: ${P.message}`);continue}const F=[];for(const P of O.models)F.push(await p(P.name,["model"],"veg",I=>`Трава · ${vl(I)}`,H0));const C=G0(O,D.pos,D.nor);for(const P of C){const I=F[P.model];I&&(A++,S+=I.parts.reduce((B,z)=>B+z.indices.length/3,0))}E.push({L:O,list:C,protos:F})}const $=S>W0?W0/S:1,T=bl(1);let U=0;for(const{L:O,list:N,protos:D}of E)for(const F of N){const C=D[F.model];!C||$<1&&T()>=$||(f.place(C.pi,C.parts,F.M,O.models[F.model].name.split("/").pop(),`vegetation/${O.name}`),U++)}const _=(O,N,D,F)=>{if(!O.length)return;const C=_u(N,.2,D),P=f.proto(N,[C],"marker",()=>N,!0);for(const I of O){const B=new Float64Array(16);Ae(I.pos,[1,0,0,0],[1,1,1],B),f.place(P,[C],B,I.name,F)}};_(x,"Метки · свет",[1,.85,.3],"light"),_(v,"Метки · частицы",[.3,.8,1],"particle");const R=f.build();if(!R.length)throw new Error("В уровне нет найденных моделей");const L=[`Survarium (Vostok): уровень ${i||s}`,`Объектов: ${l.length}, показано ${w} (моделей ${[...d].filter(([O,N])=>N&&O.startsWith("static|")).length}, треугольников ${f.tris})`];return y&&L.push(`Объекты со скелетом (в bind-позе): ${y}`),U&&L.push(`Трава: ${U===A?U:`${U} из ${A} (прорежена)`} экземпляров, LOD${H0} (меши «Трава · …»)`),k.length&&L.push(`Трава пропущена: ${k.slice(0,3).join("; ")}`),M&&L.push(`Провода: ${M}`),(x.length||v.length)&&L.push(`Метки (скрыты): источники света ${x.length}, частицы ${v.length} — эффекты не воспроизводятся`),h.size&&L.push(`Модели не найдены: ${h.size} (${[...h].slice(0,3).join(", ")}${h.size>3?"…":""})`),u.size&&L.push(`Не найдены материалы: ${u.size}`),{meshes:R,instances:f.scene,skeleton:null,motions:[],bounds:dt(R),info:L,flipZ:!0}}const S4=Object.freeze(Object.defineProperty({__proto__:null,levelQuat:z0,levelVisuals:B0,loadVostokLevel:$4,parseVeg:V0,rng:bl,vegInstances:G0,vegLayers:j0,wireMesh:X0},Symbol.toStringTag,{value:"Module"}));return st.DARK_THEME=pa,st.LIGHT_THEME=s2,st.ModelViewer=wf,st.MotionOnlyError=To,st.NodeSkeleton=Kt,st.OrbitCamera=gf,st.Registry=Ll,st.Renderer=mf,st.TEXTURE_RE=Wr,st.TextureLibrary=zs,st.ViewerSession=Bl,st.allEngines=cf,st.analyzeNormalMap=Ol,st.applyPlacement=Zr,st.assimpEngine=ha,st.binary=gm,st.buildDiagnostics=km,st.composeSurface=Pl,st.computeTangents=Il,st.configureAssimp=fa,st.coreEngine=Jr,st.crc32=Do,st.createArchive=Qt,st.createRegistry=tc,st.creation1Engine=fc,st.cryengine1Engine=Pc,st.cryengine3Engine=Nc,st.decodeBC45=Oo,st.decodeBC7=Io,st.decodeDDS=Pn,st.decodeDXT=_e,st.decodePCX=Wl,st.decodeTGA=Kl,st.defineEngine=vt,st.dunia1Engine=ef,st.eagl4Engine=Ic,st.encodeDdsRgba=sa,st.enfusionEngine=nf,st.engineTitle=dm,st.errMsg=In,st.exporterText=pm,st.fceEngine=Oc,st.fourAEngine=Qc,st.gamebryoEngine=lc,st.geometry=oa,st.getLocale=fm,st.goldsrcEngine=Ac,st.hexDump=jl,st.hpl1Engine=hc,st.hpl2Engine=pc,st.idtech2Engine=gc,st.idtech3Engine=wc,st.idtech4Engine=bc,st.inflateRaw=Qr,st.inflateZlib=Lo,st.instanceAt=_m,st.iwEngine=Lc,st.lithtechEngine=Jc,st.loadSameDirMotions=Vl,st.math=Em,st.mount=o2,st.normalCandidates=Rl,st.openFolder=zl,st.openZip=cs,st.pickGames=Cl,st.pickRay=Xl,st.presetText=hm,st.rageEngine=Wc,st.readRange=ht,st.realVirtuality3Engine=jc,st.realVirtuality4Engine=Gc,st.renderwareV3Engine=uc,st.rotatePlacement=Um,st.sceneLeaves=Gl,st.sceneTree=Tm,st.setLocale=cm,st.source1Engine=_c,st.source2Engine=Cc,st.spintiresEngine=qc,st.surfaceCandidates=Hr,st.surfaceMaps=Dl,st.torque3dEngine=zc,st.tr=yt,st.translatePlacement=Fm,st.unreal1Engine=xc,st.unreal2Engine=vc,st.unreal3Engine=Mc,st.unreal4Engine=kc,st.unreal5Engine=Ec,st.visibleIndices=qr,st.vostokEngine=tf,st.writeZip=Xm,st.xrayV1Engine=sc,Object.defineProperty(st,Symbol.toStringTag,{value:"Module"}),st})({});
|
|
204
|
+
//# sourceMappingURL=gmv.iife.js.map
|