@gmvjs/xray-v1 0.1.0 → 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.
Files changed (47) hide show
  1. package/README.md +35 -29
  2. package/dist/db.d.ts +2 -2
  3. package/dist/db.js.map +1 -1
  4. package/dist/details.d.ts +4 -4
  5. package/dist/details.js.map +1 -1
  6. package/dist/export.d.ts +13 -13
  7. package/dist/export.js.map +1 -1
  8. package/dist/games/cop.js.map +1 -1
  9. package/dist/games/cs.js.map +1 -1
  10. package/dist/games/sdk.d.ts +1 -1
  11. package/dist/games/sdk.js.map +1 -1
  12. package/dist/games/soc.d.ts +1 -1
  13. package/dist/games/soc.js.map +1 -1
  14. package/dist/i18n.js.map +1 -1
  15. package/dist/index.d.ts +1 -1
  16. package/dist/index.js +8 -8
  17. package/dist/index.js.map +1 -1
  18. package/dist/level.d.ts +5 -5
  19. package/dist/level.js.map +1 -1
  20. package/dist/load.d.ts +1 -1
  21. package/dist/load.js.map +1 -1
  22. package/dist/lzhuf.d.ts +1 -1
  23. package/dist/lzhuf.js.map +1 -1
  24. package/dist/motion.d.ts +6 -6
  25. package/dist/motion.js.map +1 -1
  26. package/dist/object.d.ts +12 -12
  27. package/dist/object.js.map +1 -1
  28. package/dist/ogf.d.ts +6 -6
  29. package/dist/ogf.js.map +1 -1
  30. package/dist/scene.d.ts +6 -6
  31. package/dist/scene.js.map +1 -1
  32. package/dist/skeleton.d.ts +4 -4
  33. package/dist/skeleton.js +4 -4
  34. package/dist/skeleton.js.map +1 -1
  35. package/dist/smotion.d.ts +5 -5
  36. package/dist/smotion.js.map +1 -1
  37. package/dist/soc-key.d.ts +6 -6
  38. package/dist/soc-key.js.map +1 -1
  39. package/dist/thm.d.ts +1 -1
  40. package/dist/thm.js.map +1 -1
  41. package/dist/write-object.d.ts +7 -7
  42. package/dist/write-object.js +1 -1
  43. package/dist/write-object.js.map +1 -1
  44. package/dist/write-omf.d.ts +9 -9
  45. package/dist/write-scene.d.ts +6 -6
  46. package/dist/write-scene.js.map +1 -1
  47. package/package.json +3 -3
package/dist/smotion.d.ts CHANGED
@@ -27,16 +27,16 @@ export interface SMotion {
27
27
  envs: Envelope[];
28
28
  }>;
29
29
  }
30
- /** Список анимаций: u32 число + анимации (.skls, чанк 0x0916 .object). */
30
+ /** Animation list: u32 count + animations (.skls, .object chunk 0x0916). */
31
31
  export declare function parseSMotions(u: Uint8Array): SMotion[];
32
- /** .skl: чанк 0x1200 с одной анимацией. */
32
+ /** .skl: chunk 0x1200 with one animation. */
33
33
  export declare function parseSkl(u: Uint8Array): SMotion;
34
34
  export declare function evalEnvelope(env: Envelope, time: number): number;
35
- /** Кватернион из матрицы поворота (строки, как в quatLocal скелета X-Ray). */
35
+ /** Quaternion from a rotation matrix (rows, as in the X-Ray skeleton's quatLocal). */
36
36
  export declare function quatOfRows(o: ArrayLike<number>): [number, number, number, number];
37
37
  /**
38
- * Анимация SDK → треки X-Ray по костям модели (по именам): огибающие считаются на каждом кадре, поворот (setXYZi)
39
- * переводится в кватернион. local(rot, pos, out) — матрица кости по углам, как у bind-позы этого скелета.
38
+ * SDK animation → X-Ray tracks per model bone (by name): envelopes are evaluated at every frame, the rotation (setXYZi)
39
+ * is converted to a quaternion. local(rot, pos, out) is the bone matrix from angles, as for this skeleton's bind pose.
40
40
  */
41
41
  export declare function smotionToXray(sm: SMotion, boneNames: readonly string[], local: (rot: number[], pos: number[], o: Float32Array) => void, parts?: Array<{
42
42
  name: string;
@@ -1 +1 @@
1
- {"version":3,"file":"smotion.js","sources":["../src/smotion.ts"],"sourcesContent":["// Анимации X-Ray SDK (ActorEditor): .skl (чанк 0x1200 с одной анимацией), .skls (u32 число + анимации подряд),\n// они же внутри .object (чанк 0x0916). Анимация (CSMotion, версии 6–7): имя, u32 первый и последний кадр, f32 FPS,\n// u16 версия, u8 флаги (1 — эффект на одну кость), u16 кость/часть (0xFFFF — всё тело), f32 скорость, наплыв, спад, сила,\n// u16 число костей; кость: имя, u8 флаги, 6 огибающих (позиция X, Y, Z; повороты H, P, B). Огибающая (как в LightWave):\n// u8×2 поведение до/после, u16 ключей; ключ: f32 значение, f32 время (с), u8 форма (0 TCB, 1 Эрмит, 2 Безье, 3 линия,\n// 4 ступень, 5 Безье-2), кроме ступени — 7 чисел u16 в [−32, 32]: натяжение, непрерывность, смещение, параметры ×4.\n// С версии 7 — метки (имя до «\\r\\n», u32 число, интервалы f32×2). Поза кости: Fmatrix::setXYZi(P, H, B) + позиция.\nimport { XRAY_FX_PART } from './motion';\nimport type { XrayMotion, XrayTrack } from './motion';\n\nexport interface EnvKey { value: number; time: number; shape: number; tension: number; continuity: number; bias: number; param: number[] }\nexport interface Envelope { behavior: [number, number]; keys: EnvKey[] }\nexport interface SMotion {\n name: string; frameStart: number; frameEnd: number; fps: number; version: number;\n flags: number; boneOrPart: number; speed: number;\n bones: Array<{ name: string; flags: number; envs: Envelope[] }>;\n}\n\nclass R {\n readonly dv: DataView;\n constructor(readonly u: Uint8Array, public p: number, readonly end: number) { this.dv = new DataView(u.buffer, u.byteOffset, u.byteLength); }\n need(n: number) { if (this.p + n > this.end) throw new Error('Анимация SDK обрезана'); }\n u8() { this.need(1); return this.u[this.p++]!; }\n u16() { this.need(2); const v = this.dv.getUint16(this.p, true); this.p += 2; return v; }\n u32() { this.need(4); const v = this.dv.getUint32(this.p, true); this.p += 4; return v; }\n f32() { this.need(4); const v = this.dv.getFloat32(this.p, true); this.p += 4; return v; }\n q16() { return -32 + (this.u16() * 64) / 65535; }\n sz() { const e = this.u.indexOf(0, this.p); if (e < 0 || e >= this.end) throw new Error('Анимация SDK: строка не закрыта'); const s = new TextDecoder('latin1').decode(this.u.subarray(this.p, e)); this.p = e + 1; return s; }\n line() { while (this.p < this.end && this.u[this.p] !== 10) this.p++; this.p++; }\n}\n\nfunction readEnvelope(r: R): Envelope {\n const behavior: [number, number] = [r.u8(), r.u8()], n = r.u16(), keys: EnvKey[] = [];\n for (let i = 0; i < n; i++) {\n const value = r.f32(), time = r.f32(), shape = r.u8();\n const k: EnvKey = { value, time, shape, tension: 0, continuity: 0, bias: 0, param: [0, 0, 0, 0] };\n if (shape !== 4) { k.tension = r.q16(); k.continuity = r.q16(); k.bias = r.q16(); k.param = [r.q16(), r.q16(), r.q16(), r.q16()]; }\n keys.push(k);\n }\n return { behavior, keys };\n}\n\nfunction readSMotion(r: R): SMotion {\n const name = r.sz(), frameStart = r.u32(), frameEnd = r.u32(), fps = r.f32(), version = r.u16();\n if (version < 6) throw new Error(`Анимация SDK версии ${version} не поддерживается (нужна 6–7)`);\n const flags = r.u8(), boneOrPart = r.u16(), speed = r.f32();\n r.f32(); r.f32(); r.f32(); // наплыв, спад, сила\n const nb = r.u16(), bones: SMotion['bones'] = [];\n for (let b = 0; b < nb; b++) {\n const bn = r.sz(), bf = r.u8(), envs: Envelope[] = [];\n for (let c = 0; c < 6; c++) envs.push(readEnvelope(r));\n bones.push({ name: bn, flags: bf, envs });\n }\n if (version >= 7) { const nm = r.u32(); for (let i = 0; i < nm; i++) { r.line(); const k = r.u32(); r.p += k * 8; } }\n return { name, frameStart, frameEnd, fps, version, flags, boneOrPart, speed, bones };\n}\n\n/** Список анимаций: u32 число + анимации (.skls, чанк 0x0916 .object). */\nexport function parseSMotions(u: Uint8Array): SMotion[] {\n const r = new R(u, 0, u.length), n = r.u32(), out: SMotion[] = [];\n for (let i = 0; i < n; i++) out.push(readSMotion(r));\n return out;\n}\n\n/** .skl: чанк 0x1200 с одной анимацией. */\nexport function parseSkl(u: Uint8Array): SMotion {\n const dv = new DataView(u.buffer, u.byteOffset, u.byteLength);\n if (u.length < 8 || dv.getUint32(0, true) !== 0x1200) throw new Error('Не .skl (нет чанка 0x1200)');\n return readSMotion(new R(u, 8, Math.min(u.length, 8 + dv.getUint32(4, true))));\n}\n\n// ---- огибающие (evalEnvelope из LightWave SDK, как в xrCore/envelope.cpp) ----\nfunction outgoing(keys: EnvKey[], i: number): number {\n const k0 = keys[i]!, k1 = keys[i + 1]!, prev = keys[i - 1];\n switch (k0.shape) {\n case 0: {\n const a = (1 - k0.tension) * (1 + k0.continuity) * (1 + k0.bias), b = (1 - k0.tension) * (1 - k0.continuity) * (1 - k0.bias), d = k1.value - k0.value;\n return prev ? ((k1.time - k0.time) / (k1.time - prev.time)) * (a * (k0.value - prev.value) + b * d) : b * d;\n }\n case 3: { const d = k1.value - k0.value; return prev ? ((k1.time - k0.time) / (k1.time - prev.time)) * (k0.value - prev.value + d) : d; }\n case 1: case 2: return prev ? k0.param[1]! * ((k1.time - k0.time) / (k1.time - prev.time)) : k0.param[1]!;\n case 5: { const o = k0.param[3]! * (k1.time - k0.time); return Math.abs(k0.param[2]!) > 1e-5 ? o / k0.param[2]! : o * 1e5; }\n default: return 0;\n }\n}\nfunction incoming(keys: EnvKey[], i: number): number {\n const k0 = keys[i - 1]!, k1 = keys[i]!, next = keys[i + 1];\n switch (k1.shape) {\n case 0: {\n const a = (1 - k1.tension) * (1 - k1.continuity) * (1 + k1.bias), b = (1 - k1.tension) * (1 + k1.continuity) * (1 - k1.bias), d = k1.value - k0.value;\n return next ? ((k1.time - k0.time) / (next.time - k0.time)) * (b * (next.value - k1.value) + a * d) : a * d;\n }\n case 3: { const d = k1.value - k0.value; return next ? ((k1.time - k0.time) / (next.time - k0.time)) * (next.value - k1.value + d) : d; }\n case 1: case 2: return next ? k1.param[0]! * ((k1.time - k0.time) / (next.time - k0.time)) : k1.param[0]!;\n case 5: { const o = k1.param[1]! * (k1.time - k0.time); return Math.abs(k1.param[0]!) > 1e-5 ? o / k1.param[0]! : o * 1e5; }\n default: return 0;\n }\n}\n/** Безье-2: кривая по времени — ищем параметр делением отрезка пополам. */\nfunction bez2(k0: EnvKey, k1: EnvKey, time: number): number {\n const x0 = k0.time, x1 = k0.time + (k0.shape === 5 ? k0.param[2]! : (k1.time - k0.time) / 3), x3 = k1.time, x2 = k1.time + (k1.shape === 5 ? k1.param[0]! : -(k1.time - k0.time) / 3);\n const y0 = k0.value, y1 = k0.value + (k0.shape === 5 ? k0.param[3]! : outgoing([k0, k1], 0) / 3), y3 = k1.value, y2 = k1.value + (k1.shape === 5 ? k1.param[1]! : -incoming([k0, k1], 1) / 3);\n const cub = (a: number, b: number, c: number, d: number, t: number) => { const s = 1 - t; return s * s * s * a + 3 * s * s * t * b + 3 * s * t * t * c + t * t * t * d; };\n let lo = 0, hi = 1, t = 0.5;\n for (let i = 0; i < 30; i++) { t = (lo + hi) / 2; if (cub(x0, x1, x2, x3, t) < time) lo = t; else hi = t; }\n return cub(y0, y1, y2, y3, t);\n}\n\nexport function evalEnvelope(env: Envelope, time: number): number {\n const K = env.keys;\n if (!K.length) return 0;\n if (K.length === 1) return K[0]!.value;\n const s = K[0]!, e = K[K.length - 1]!;\n if (time < s.time) {\n if (env.behavior[0] === 0) return 0;\n if (env.behavior[0] === 2 && e.time > s.time) time = s.time + ((((time - s.time) % (e.time - s.time)) + (e.time - s.time)) % (e.time - s.time));\n else return s.value;\n } else if (time > e.time) {\n if (env.behavior[1] === 0) return 0;\n if (env.behavior[1] === 2 && e.time > s.time) time = s.time + ((time - s.time) % (e.time - s.time));\n else return e.value;\n }\n let k = 0;\n while (k + 2 < K.length && time > K[k + 1]!.time) k++;\n const k0 = K[k]!, k1 = K[k + 1]!;\n if (time <= k0.time) return k0.value;\n if (time >= k1.time) return k1.value;\n const t = (time - k0.time) / (k1.time - k0.time);\n switch (k1.shape) {\n case 0: case 1: case 2: {\n const out = outgoing(K, k), inn = incoming(K, k + 1), t2 = t * t, t3 = t2 * t;\n const h2 = 3 * t2 - 2 * t3, h1 = 1 - h2, h4 = t3 - t2, h3 = h4 - t2 + t;\n return h1 * k0.value + h2 * k1.value + h3 * out + h4 * inn;\n }\n case 5: return bez2(k0, k1, time);\n case 3: return k0.value + t * (k1.value - k0.value);\n case 4: return k0.value;\n default: return 0;\n }\n}\n\n/** Кватернион из матрицы поворота (строки, как в quatLocal скелета X-Ray). */\nexport function quatOfRows(o: ArrayLike<number>): [number, number, number, number] {\n const tr = o[0]! + o[5]! + o[10]!;\n let x: number, y: number, z: number, w: number;\n if (tr > 0) { const s = Math.sqrt(tr + 1) * 2; w = s / 4; x = (o[9]! - o[6]!) / s; y = (o[2]! - o[8]!) / s; z = (o[4]! - o[1]!) / s; }\n else if (o[0]! > o[5]! && o[0]! > o[10]!) { const s = Math.sqrt(1 + o[0]! - o[5]! - o[10]!) * 2; x = s / 4; w = (o[9]! - o[6]!) / s; y = (o[1]! + o[4]!) / s; z = (o[2]! + o[8]!) / s; }\n else if (o[5]! > o[10]!) { const s = Math.sqrt(1 + o[5]! - o[0]! - o[10]!) * 2; y = s / 4; w = (o[2]! - o[8]!) / s; x = (o[1]! + o[4]!) / s; z = (o[6]! + o[9]!) / s; }\n else { const s = Math.sqrt(1 + o[10]! - o[0]! - o[5]!) * 2; z = s / 4; w = (o[4]! - o[1]!) / s; x = (o[2]! + o[8]!) / s; y = (o[6]! + o[9]!) / s; }\n return [x, y, z, w];\n}\n\n/**\n * Анимация SDK → треки X-Ray по костям модели (по именам): огибающие считаются на каждом кадре, поворот (setXYZi)\n * переводится в кватернион. local(rot, pos, out) — матрица кости по углам, как у bind-позы этого скелета.\n */\nexport function smotionToXray(sm: SMotion, boneNames: readonly string[], local: (rot: number[], pos: number[], o: Float32Array) => void, parts: Array<{ name: string; bones: string[] }> = []): XrayMotion {\n const frames = Math.max(1, sm.frameEnd - sm.frameStart), fps = sm.fps > 0 ? sm.fps : 30;\n const idx = new Map(boneNames.map((n, i) => [n.toLowerCase(), i]));\n const tracks: XrayTrack[] = [], M = new Float32Array(16);\n for (const b of sm.bones) {\n const bi = idx.get(b.name.toLowerCase());\n if (bi === undefined) continue;\n const rot = new Int16Array(frames * 4), trans = new Float32Array(frames * 3);\n let px = 0, py = 0, pz = 0, pw = 1;\n for (let f = 0; f < frames; f++) {\n const t = (sm.frameStart + f) / fps, E = b.envs;\n const T = [evalEnvelope(E[0]!, t), evalEnvelope(E[1]!, t), evalEnvelope(E[2]!, t)];\n local([evalEnvelope(E[4]!, t), evalEnvelope(E[3]!, t), evalEnvelope(E[5]!, t)], T, M);\n let [x, y, z, w] = quatOfRows(M);\n if (x * px + y * py + z * pz + w * pw < 0) { x = -x; y = -y; z = -z; w = -w; } // без скачков знака между кадрами\n [px, py, pz, pw] = [x, y, z, w];\n rot[f * 4] = Math.round(x * 32767); rot[f * 4 + 1] = Math.round(y * 32767); rot[f * 4 + 2] = Math.round(z * 32767); rot[f * 4 + 3] = Math.round(w * 32767);\n trans.set(T, f * 3);\n }\n tracks[bi] = { rot, trans };\n }\n const m: XrayMotion = { kind: 'xray', name: sm.name, frames, duration: frames / fps, tracks };\n // часть тела или кость-эффект — как у анимаций OGF\n if (sm.flags & 1) {\n const t = tracks[sm.boneOrPart];\n if (t) { m.tracks = []; m.tracks[sm.boneOrPart] = t; m.part = XRAY_FX_PART; }\n } else if (sm.boneOrPart !== 0xffff && parts.length > 1 && parts[sm.boneOrPart]) {\n const p = parts[sm.boneOrPart]!, keep: XrayTrack[] = [];\n for (const n of p.bones) { const i = idx.get(n.toLowerCase()); if (i !== undefined && tracks[i]) keep[i] = tracks[i]!; }\n m.tracks = keep; m.part = p.name;\n }\n return m;\n}\n"],"names":["t"],"mappings":"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"}
1
+ {"version":3,"file":"smotion.js","sources":["../src/smotion.ts"],"sourcesContent":["// X-Ray SDK animations (ActorEditor): .skl (chunk 0x1200 with one animation), .skls (u32 count + animations in sequence),\n// the same inside .object (chunk 0x0916). Animation (CSMotion, versions 6–7): name, u32 first and last frame, f32 FPS,\n// u16 version, u8 flags (1 = an effect on one bone), u16 bone/part (0xFFFF = the whole body), f32 speed, accrue, falloff, power,\n// u16 bone count; bone: name, u8 flags, 6 envelopes (position X, Y, Z; rotations H, P, B). Envelope (as in LightWave):\n// u8×2 pre/post behavior, u16 keys; key: f32 value, f32 time (s), u8 shape (0 TCB, 1 Hermite, 2 Bezier, 3 linear,\n// 4 stepped, 5 Bezier-2), except stepped, 7 u16 numbers in [−32, 32]: tension, continuity, bias, parameters ×4.\n// Since version 7, marks (a name up to \"\\r\\n\", u32 count, f32×2 intervals). Bone pose: Fmatrix::setXYZi(P, H, B) + position.\nimport { XRAY_FX_PART } from './motion';\nimport type { XrayMotion, XrayTrack } from './motion';\n\nexport interface EnvKey { value: number; time: number; shape: number; tension: number; continuity: number; bias: number; param: number[] }\nexport interface Envelope { behavior: [number, number]; keys: EnvKey[] }\nexport interface SMotion {\n name: string; frameStart: number; frameEnd: number; fps: number; version: number;\n flags: number; boneOrPart: number; speed: number;\n bones: Array<{ name: string; flags: number; envs: Envelope[] }>;\n}\n\nclass R {\n readonly dv: DataView;\n constructor(readonly u: Uint8Array, public p: number, readonly end: number) { this.dv = new DataView(u.buffer, u.byteOffset, u.byteLength); }\n need(n: number) { if (this.p + n > this.end) throw new Error('Анимация SDK обрезана'); }\n u8() { this.need(1); return this.u[this.p++]!; }\n u16() { this.need(2); const v = this.dv.getUint16(this.p, true); this.p += 2; return v; }\n u32() { this.need(4); const v = this.dv.getUint32(this.p, true); this.p += 4; return v; }\n f32() { this.need(4); const v = this.dv.getFloat32(this.p, true); this.p += 4; return v; }\n q16() { return -32 + (this.u16() * 64) / 65535; }\n sz() { const e = this.u.indexOf(0, this.p); if (e < 0 || e >= this.end) throw new Error('Анимация SDK: строка не закрыта'); const s = new TextDecoder('latin1').decode(this.u.subarray(this.p, e)); this.p = e + 1; return s; }\n line() { while (this.p < this.end && this.u[this.p] !== 10) this.p++; this.p++; }\n}\n\nfunction readEnvelope(r: R): Envelope {\n const behavior: [number, number] = [r.u8(), r.u8()], n = r.u16(), keys: EnvKey[] = [];\n for (let i = 0; i < n; i++) {\n const value = r.f32(), time = r.f32(), shape = r.u8();\n const k: EnvKey = { value, time, shape, tension: 0, continuity: 0, bias: 0, param: [0, 0, 0, 0] };\n if (shape !== 4) { k.tension = r.q16(); k.continuity = r.q16(); k.bias = r.q16(); k.param = [r.q16(), r.q16(), r.q16(), r.q16()]; }\n keys.push(k);\n }\n return { behavior, keys };\n}\n\nfunction readSMotion(r: R): SMotion {\n const name = r.sz(), frameStart = r.u32(), frameEnd = r.u32(), fps = r.f32(), version = r.u16();\n if (version < 6) throw new Error(`Анимация SDK версии ${version} не поддерживается (нужна 6–7)`);\n const flags = r.u8(), boneOrPart = r.u16(), speed = r.f32();\n r.f32(); r.f32(); r.f32(); // accrue, falloff, power\n const nb = r.u16(), bones: SMotion['bones'] = [];\n for (let b = 0; b < nb; b++) {\n const bn = r.sz(), bf = r.u8(), envs: Envelope[] = [];\n for (let c = 0; c < 6; c++) envs.push(readEnvelope(r));\n bones.push({ name: bn, flags: bf, envs });\n }\n if (version >= 7) { const nm = r.u32(); for (let i = 0; i < nm; i++) { r.line(); const k = r.u32(); r.p += k * 8; } }\n return { name, frameStart, frameEnd, fps, version, flags, boneOrPart, speed, bones };\n}\n\n/** Animation list: u32 count + animations (.skls, .object chunk 0x0916). */\nexport function parseSMotions(u: Uint8Array): SMotion[] {\n const r = new R(u, 0, u.length), n = r.u32(), out: SMotion[] = [];\n for (let i = 0; i < n; i++) out.push(readSMotion(r));\n return out;\n}\n\n/** .skl: chunk 0x1200 with one animation. */\nexport function parseSkl(u: Uint8Array): SMotion {\n const dv = new DataView(u.buffer, u.byteOffset, u.byteLength);\n if (u.length < 8 || dv.getUint32(0, true) !== 0x1200) throw new Error('Не .skl (нет чанка 0x1200)');\n return readSMotion(new R(u, 8, Math.min(u.length, 8 + dv.getUint32(4, true))));\n}\n\n// ---- envelopes (evalEnvelope from the LightWave SDK, as in xrCore/envelope.cpp) ----\nfunction outgoing(keys: EnvKey[], i: number): number {\n const k0 = keys[i]!, k1 = keys[i + 1]!, prev = keys[i - 1];\n switch (k0.shape) {\n case 0: {\n const a = (1 - k0.tension) * (1 + k0.continuity) * (1 + k0.bias), b = (1 - k0.tension) * (1 - k0.continuity) * (1 - k0.bias), d = k1.value - k0.value;\n return prev ? ((k1.time - k0.time) / (k1.time - prev.time)) * (a * (k0.value - prev.value) + b * d) : b * d;\n }\n case 3: { const d = k1.value - k0.value; return prev ? ((k1.time - k0.time) / (k1.time - prev.time)) * (k0.value - prev.value + d) : d; }\n case 1: case 2: return prev ? k0.param[1]! * ((k1.time - k0.time) / (k1.time - prev.time)) : k0.param[1]!;\n case 5: { const o = k0.param[3]! * (k1.time - k0.time); return Math.abs(k0.param[2]!) > 1e-5 ? o / k0.param[2]! : o * 1e5; }\n default: return 0;\n }\n}\nfunction incoming(keys: EnvKey[], i: number): number {\n const k0 = keys[i - 1]!, k1 = keys[i]!, next = keys[i + 1];\n switch (k1.shape) {\n case 0: {\n const a = (1 - k1.tension) * (1 - k1.continuity) * (1 + k1.bias), b = (1 - k1.tension) * (1 + k1.continuity) * (1 - k1.bias), d = k1.value - k0.value;\n return next ? ((k1.time - k0.time) / (next.time - k0.time)) * (b * (next.value - k1.value) + a * d) : a * d;\n }\n case 3: { const d = k1.value - k0.value; return next ? ((k1.time - k0.time) / (next.time - k0.time)) * (next.value - k1.value + d) : d; }\n case 1: case 2: return next ? k1.param[0]! * ((k1.time - k0.time) / (next.time - k0.time)) : k1.param[0]!;\n case 5: { const o = k1.param[1]! * (k1.time - k0.time); return Math.abs(k1.param[0]!) > 1e-5 ? o / k1.param[0]! : o * 1e5; }\n default: return 0;\n }\n}\n/** Bezier-2: a curve over time, the parameter is found by bisection. */\nfunction bez2(k0: EnvKey, k1: EnvKey, time: number): number {\n const x0 = k0.time, x1 = k0.time + (k0.shape === 5 ? k0.param[2]! : (k1.time - k0.time) / 3), x3 = k1.time, x2 = k1.time + (k1.shape === 5 ? k1.param[0]! : -(k1.time - k0.time) / 3);\n const y0 = k0.value, y1 = k0.value + (k0.shape === 5 ? k0.param[3]! : outgoing([k0, k1], 0) / 3), y3 = k1.value, y2 = k1.value + (k1.shape === 5 ? k1.param[1]! : -incoming([k0, k1], 1) / 3);\n const cub = (a: number, b: number, c: number, d: number, t: number) => { const s = 1 - t; return s * s * s * a + 3 * s * s * t * b + 3 * s * t * t * c + t * t * t * d; };\n let lo = 0, hi = 1, t = 0.5;\n for (let i = 0; i < 30; i++) { t = (lo + hi) / 2; if (cub(x0, x1, x2, x3, t) < time) lo = t; else hi = t; }\n return cub(y0, y1, y2, y3, t);\n}\n\nexport function evalEnvelope(env: Envelope, time: number): number {\n const K = env.keys;\n if (!K.length) return 0;\n if (K.length === 1) return K[0]!.value;\n const s = K[0]!, e = K[K.length - 1]!;\n if (time < s.time) {\n if (env.behavior[0] === 0) return 0;\n if (env.behavior[0] === 2 && e.time > s.time) time = s.time + ((((time - s.time) % (e.time - s.time)) + (e.time - s.time)) % (e.time - s.time));\n else return s.value;\n } else if (time > e.time) {\n if (env.behavior[1] === 0) return 0;\n if (env.behavior[1] === 2 && e.time > s.time) time = s.time + ((time - s.time) % (e.time - s.time));\n else return e.value;\n }\n let k = 0;\n while (k + 2 < K.length && time > K[k + 1]!.time) k++;\n const k0 = K[k]!, k1 = K[k + 1]!;\n if (time <= k0.time) return k0.value;\n if (time >= k1.time) return k1.value;\n const t = (time - k0.time) / (k1.time - k0.time);\n switch (k1.shape) {\n case 0: case 1: case 2: {\n const out = outgoing(K, k), inn = incoming(K, k + 1), t2 = t * t, t3 = t2 * t;\n const h2 = 3 * t2 - 2 * t3, h1 = 1 - h2, h4 = t3 - t2, h3 = h4 - t2 + t;\n return h1 * k0.value + h2 * k1.value + h3 * out + h4 * inn;\n }\n case 5: return bez2(k0, k1, time);\n case 3: return k0.value + t * (k1.value - k0.value);\n case 4: return k0.value;\n default: return 0;\n }\n}\n\n/** Quaternion from a rotation matrix (rows, as in the X-Ray skeleton's quatLocal). */\nexport function quatOfRows(o: ArrayLike<number>): [number, number, number, number] {\n const tr = o[0]! + o[5]! + o[10]!;\n let x: number, y: number, z: number, w: number;\n if (tr > 0) { const s = Math.sqrt(tr + 1) * 2; w = s / 4; x = (o[9]! - o[6]!) / s; y = (o[2]! - o[8]!) / s; z = (o[4]! - o[1]!) / s; }\n else if (o[0]! > o[5]! && o[0]! > o[10]!) { const s = Math.sqrt(1 + o[0]! - o[5]! - o[10]!) * 2; x = s / 4; w = (o[9]! - o[6]!) / s; y = (o[1]! + o[4]!) / s; z = (o[2]! + o[8]!) / s; }\n else if (o[5]! > o[10]!) { const s = Math.sqrt(1 + o[5]! - o[0]! - o[10]!) * 2; y = s / 4; w = (o[2]! - o[8]!) / s; x = (o[1]! + o[4]!) / s; z = (o[6]! + o[9]!) / s; }\n else { const s = Math.sqrt(1 + o[10]! - o[0]! - o[5]!) * 2; z = s / 4; w = (o[4]! - o[1]!) / s; x = (o[2]! + o[8]!) / s; y = (o[6]! + o[9]!) / s; }\n return [x, y, z, w];\n}\n\n/**\n * SDK animation → X-Ray tracks per model bone (by name): envelopes are evaluated at every frame, the rotation (setXYZi)\n * is converted to a quaternion. local(rot, pos, out) is the bone matrix from angles, as for this skeleton's bind pose.\n */\nexport function smotionToXray(sm: SMotion, boneNames: readonly string[], local: (rot: number[], pos: number[], o: Float32Array) => void, parts: Array<{ name: string; bones: string[] }> = []): XrayMotion {\n const frames = Math.max(1, sm.frameEnd - sm.frameStart), fps = sm.fps > 0 ? sm.fps : 30;\n const idx = new Map(boneNames.map((n, i) => [n.toLowerCase(), i]));\n const tracks: XrayTrack[] = [], M = new Float32Array(16);\n for (const b of sm.bones) {\n const bi = idx.get(b.name.toLowerCase());\n if (bi === undefined) continue;\n const rot = new Int16Array(frames * 4), trans = new Float32Array(frames * 3);\n let px = 0, py = 0, pz = 0, pw = 1;\n for (let f = 0; f < frames; f++) {\n const t = (sm.frameStart + f) / fps, E = b.envs;\n const T = [evalEnvelope(E[0]!, t), evalEnvelope(E[1]!, t), evalEnvelope(E[2]!, t)];\n local([evalEnvelope(E[4]!, t), evalEnvelope(E[3]!, t), evalEnvelope(E[5]!, t)], T, M);\n let [x, y, z, w] = quatOfRows(M);\n if (x * px + y * py + z * pz + w * pw < 0) { x = -x; y = -y; z = -z; w = -w; } // no sign jumps between frames\n [px, py, pz, pw] = [x, y, z, w];\n rot[f * 4] = Math.round(x * 32767); rot[f * 4 + 1] = Math.round(y * 32767); rot[f * 4 + 2] = Math.round(z * 32767); rot[f * 4 + 3] = Math.round(w * 32767);\n trans.set(T, f * 3);\n }\n tracks[bi] = { rot, trans };\n }\n const m: XrayMotion = { kind: 'xray', name: sm.name, frames, duration: frames / fps, tracks };\n // body part or effect bone, as for OGF animations\n if (sm.flags & 1) {\n const t = tracks[sm.boneOrPart];\n if (t) { m.tracks = []; m.tracks[sm.boneOrPart] = t; m.part = XRAY_FX_PART; }\n } else if (sm.boneOrPart !== 0xffff && parts.length > 1 && parts[sm.boneOrPart]) {\n const p = parts[sm.boneOrPart]!, keep: XrayTrack[] = [];\n for (const n of p.bones) { const i = idx.get(n.toLowerCase()); if (i !== undefined && tracks[i]) keep[i] = tracks[i]!; }\n m.tracks = keep; m.part = p.name;\n }\n return m;\n}\n"],"names":["t"],"mappings":"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"}
package/dist/soc-key.d.ts CHANGED
@@ -3,16 +3,16 @@ export interface SocKey {
3
3
  encryptSeed: number;
4
4
  iterations: number;
5
5
  }
6
- /** Обратная таблица подстановки (alphabet_back) для зерна и числа перестановок. */
6
+ /** Inverse substitution table (alphabet_back) for a seed and permutation count. */
7
7
  export declare function socAlphabetBack(tableSeed: number, iterations: number): Uint8Array;
8
8
  export declare function socDecode(key: SocKey, src: Uint8Array, back?: Uint8Array<ArrayBufferLike>): Uint8Array;
9
- /** Обратное к socDecode (для тестов: синтетические зашифрованные архивы). */
9
+ /** The inverse of socDecode (for tests: synthetic encrypted archives). */
10
10
  export declare function socEncode(key: SocKey, src: Uint8Array): Uint8Array;
11
- /** 32-битные значения (с шагом 4) из неисполняемых разделов PE-файла: кандидаты в зёрна. */
11
+ /** 32-bit values (step 4) from the PE file's non-executable sections: seed candidates. */
12
12
  export declare function peDataWords(exe: Uint8Array): Uint32Array;
13
13
  /**
14
- * Найти ключ в исполняемом файле игры. probe — зашифрованная таблица файлов (чанк 1) любого архива, check — проверка
15
- * распакованной таблицы. Поиск: для каждого кандидата в зерно таблицы и числа перестановок известно, каким должен быть
16
- * 4-й байт потока (старший байт размера таблицы = 0), — кандидаты в зерно потока отбираются по нему и проверяются целиком.
14
+ * Find the key in the game executable. probe is an encrypted file table (chunk 1) of any archive, check verifies
15
+ * the unpacked table. Search: for every table seed candidate and permutation count the expected 4th stream byte is known
16
+ * (the high byte of the table size = 0); stream seed candidates are filtered by it and then checked in full.
17
17
  */
18
18
  export declare function findSocKey(exe: Uint8Array, probe: Uint8Array, check: (table: Uint8Array) => boolean): SocKey | null;
@@ -1 +1 @@
1
- {"version":3,"file":"soc-key.js","sources":["../src/soc-key.ts"],"sourcesContent":["// Таблицы файлов архивов «Тени Чернобыля» (gamedata.db*) зашифрованы простым шифром движка (trivial_encryptor xrCore):\n// подстановка байта по таблице 256 + XOR с потоком генератора random32 (seed = 0x08088405·seed + 1, значение — старшие\n// биты произведения на диапазон). Таблица подстановки строится перестановками от своего зерна, поток — от другого.\n// Ключи (два зерна) в просмотрщик не встроены: они ищутся в исполняемом файле игры пользователя (XR_3DA.exe) —\n// перебираются 32-битные значения его разделов данных, а верный ключ узнаётся по тому, что таблица файлов архива после\n// расшифровки распаковывается LZHUF и разбирается без ошибок. Число перестановок — из небольшого набора степеней двойки.\nimport { lzhufDecompress } from './lzhuf';\n\nexport interface SocKey { tableSeed: number; encryptSeed: number; iterations: number }\n\n/** Генератор random32 движка. */\nfunction random32(seed: number): (range: number) => number {\n let s = seed >>> 0;\n return (range) => { s = (Math.imul(0x08088405, s) + 1) >>> 0; return range === 256 ? s >>> 24 : Math.floor((s * range) / 4294967296); };\n}\n\n/** Обратная таблица подстановки (alphabet_back) для зерна и числа перестановок. */\nexport function socAlphabetBack(tableSeed: number, iterations: number): Uint8Array {\n // random(256) = старший байт нового состояния (u64(seed)·256 >> 32)\n const a = new Uint8Array(256);\n let s = tableSeed >>> 0;\n for (let i = 0; i < 256; i++) a[i] = i;\n for (let i = 0; i < iterations; i++) {\n s = (Math.imul(0x08088405, s) + 1) >>> 0;\n const j = s >>> 24;\n let k: number;\n do { s = (Math.imul(0x08088405, s) + 1) >>> 0; k = s >>> 24; } while (j === k);\n const t = a[j]!; a[j] = a[k]!; a[k] = t;\n }\n const b = new Uint8Array(256);\n for (let i = 0; i < 256; i++) b[a[i]!] = i;\n return b;\n}\n\nexport function socDecode(key: SocKey, src: Uint8Array, back = socAlphabetBack(key.tableSeed, key.iterations)): Uint8Array {\n const out = new Uint8Array(src.length), r = random32(key.encryptSeed);\n for (let i = 0; i < src.length; i++) out[i] = back[src[i]! ^ (r(256) & 255)]!;\n return out;\n}\n\n/** Обратное к socDecode (для тестов: синтетические зашифрованные архивы). */\nexport function socEncode(key: SocKey, src: Uint8Array): Uint8Array {\n const back = socAlphabetBack(key.tableSeed, key.iterations), a = new Uint8Array(256), r = random32(key.encryptSeed), out = new Uint8Array(src.length);\n for (let i = 0; i < 256; i++) a[back[i]!] = i;\n for (let i = 0; i < src.length; i++) out[i] = a[src[i]!]! ^ (r(256) & 255);\n return out;\n}\n\n/** 32-битные значения (с шагом 4) из неисполняемых разделов PE-файла: кандидаты в зёрна. */\nexport function peDataWords(exe: Uint8Array): Uint32Array {\n const dv = new DataView(exe.buffer, exe.byteOffset, exe.byteLength), out = new Set<number>();\n if (exe.length < 0x40 || dv.getUint16(0, true) !== 0x5a4d) return new Uint32Array(0);\n const pe = dv.getUint32(0x3c, true);\n if (pe + 24 > exe.length || dv.getUint32(pe, true) !== 0x4550) return new Uint32Array(0);\n const ns = dv.getUint16(pe + 6, true), opt = dv.getUint16(pe + 20, true);\n // сначала изменяемые данные (.data — там глобальные объекты), затем константы (.rdata)\n const order = Array.from({ length: ns }, (_, k) => k).sort((a, b) => {\n const w = (k: number) => (pe + 24 + opt + k * 40 + 40 <= exe.length && dv.getUint32(pe + 24 + opt + k * 40 + 36, true) & 0x80000000 ? 0 : 1);\n return w(a) - w(b);\n });\n for (const k of order) {\n const o = pe + 24 + opt + k * 40;\n if (o + 40 > exe.length) break;\n const name = String.fromCharCode(...exe.subarray(o, o + 8)).replace(/\\0+$/, ''), raw = dv.getUint32(o + 16, true), ptr = dv.getUint32(o + 20, true), ch = dv.getUint32(o + 36, true);\n // данные: инициализированные (0x40), не код (0x20000000 — исполняемый); ресурсы и релокации пропускаются\n if (ch & 0x20000000 || !(ch & 0x40) || name === '.rsrc' || name === '.reloc') continue;\n for (let p = ptr; p + 4 <= Math.min(exe.length, ptr + raw); p += 4) { const v = dv.getUint32(p, true); if (v > 0xffff) out.add(v); }\n }\n return Uint32Array.from(out);\n}\n\nconst ITERATIONS = [2048, 1024, 4096, 512];\n\n/**\n * Проверка ключа: сначала дёшево — первые байты (размер правдоподобен, первая запись таблицы: длина записи и начало имени),\n * затем целиком (check — разбор распакованной таблицы и границы записей).\n */\nfunction tryKey(key: SocKey, back: Uint8Array, probe: Uint8Array, check: (table: Uint8Array) => boolean): boolean {\n // размер (первые 4 байта) — без выделения памяти\n let st = key.encryptSeed >>> 0, size = 0;\n for (let i = 0; i < 4; i++) { st = (Math.imul(0x08088405, st) + 1) >>> 0; size += back[probe[i]! ^ (st >>> 24)]! * 2 ** (8 * i); }\n if (size < 16 || size > 32 * probe.length + 1024) return false;\n try {\n // длина первой записи — по первым 16 распакованным байтам\n const pre = lzhufDecompress(socDecode(key, probe.subarray(0, Math.min(probe.length, 24)), back).subarray(4), Math.min(size, 16)), bs0 = pre[0]! | (pre[1]! << 8);\n if (bs0 < 16 || bs0 > 520 || (pre.length > 14 && (pre[14]! < 0x20 || pre[14]! > 0x7e))) return false;\n const head = socDecode(key, probe.subarray(0, Math.min(probe.length, 512)), back);\n // первые записи таблицы: u16 длина (16..520), имя из печатных символов; вторая запись — тоже правдоподобной длины\n const first = lzhufDecompress(head.subarray(4), Math.min(size, 600)), dv = new DataView(first.buffer);\n let q = 0;\n for (let rec = 0; rec < 2 && q + 2 <= first.length; rec++) {\n const bs = dv.getUint16(q, true);\n if (bs < 16 || bs > 520) return false;\n for (let k = q + 14; k < Math.min(first.length, q + 2 + bs - 4); k++) if (first[k]! < 0x20 || first[k]! > 0x7e) return false;\n q += 2 + bs;\n }\n return check(lzhufDecompress(socDecode(key, probe, back)));\n } catch { return false; }\n}\n\n/**\n * Найти ключ в исполняемом файле игры. probe — зашифрованная таблица файлов (чанк 1) любого архива, check — проверка\n * распакованной таблицы. Поиск: для каждого кандидата в зерно таблицы и числа перестановок известно, каким должен быть\n * 4-й байт потока (старший байт размера таблицы = 0), — кандидаты в зерно потока отбираются по нему и проверяются целиком.\n */\nexport function findSocKey(exe: Uint8Array, probe: Uint8Array, check: (table: Uint8Array) => boolean): SocKey | null {\n if (probe.length < 8) return null;\n const words = peDataWords(exe);\n // первые 4 байта потока для каждого кандидата в зерно потока: по 4-му — индекс\n const byK3 = new Map<number, number[]>();\n for (const w of words) {\n const r = random32(w);\n r(256); r(256); r(256);\n const k3 = r(256) & 255;\n let l = byK3.get(k3);\n if (!l) byK3.set(k3, (l = []));\n l.push(w);\n }\n for (const iterations of ITERATIONS) {\n for (const tableSeed of words) {\n const back = socAlphabetBack(tableSeed, iterations);\n // back[c3 ^ k3] = 0 → k3 = c3 ^ alphabet[0]\n let a0 = 0;\n for (let i = 0; i < 256; i++) if (back[i] === 0) { a0 = i; break; }\n for (const encryptSeed of byK3.get(probe[3]! ^ a0) ?? []) {\n if (encryptSeed === tableSeed) continue;\n const key = { tableSeed, encryptSeed, iterations };\n if (tryKey(key, back, probe, check)) return key;\n }\n }\n }\n return null;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"soc-key.js","sources":["../src/soc-key.ts"],"sourcesContent":["// The file tables of Shadow of Chernobyl archives (gamedata.db*) are encrypted with the engine's simple cipher (xrCore trivial_encryptor):\n// byte substitution via a 256-entry table + XOR with the random32 generator stream (seed = 0x08088405·seed + 1, the value is the high\n// bits of the product with the range). The substitution table is built by permutations from its own seed, the stream from another.\n// The keys (two seeds) are not built into the viewer: they are searched for in the user's game executable (XR_3DA.exe):\n// the 32-bit values of its data sections are tried, and the right key is recognized because the archive's file table, after\n// decryption, unpacks with LZHUF and parses without errors. The permutation count comes from a small set of powers of two.\nimport { lzhufDecompress } from './lzhuf';\n\nexport interface SocKey { tableSeed: number; encryptSeed: number; iterations: number }\n\n/** The engine's random32 generator. */\nfunction random32(seed: number): (range: number) => number {\n let s = seed >>> 0;\n return (range) => { s = (Math.imul(0x08088405, s) + 1) >>> 0; return range === 256 ? s >>> 24 : Math.floor((s * range) / 4294967296); };\n}\n\n/** Inverse substitution table (alphabet_back) for a seed and permutation count. */\nexport function socAlphabetBack(tableSeed: number, iterations: number): Uint8Array {\n // random(256) = the high byte of the new state (u64(seed)·256 >> 32)\n const a = new Uint8Array(256);\n let s = tableSeed >>> 0;\n for (let i = 0; i < 256; i++) a[i] = i;\n for (let i = 0; i < iterations; i++) {\n s = (Math.imul(0x08088405, s) + 1) >>> 0;\n const j = s >>> 24;\n let k: number;\n do { s = (Math.imul(0x08088405, s) + 1) >>> 0; k = s >>> 24; } while (j === k);\n const t = a[j]!; a[j] = a[k]!; a[k] = t;\n }\n const b = new Uint8Array(256);\n for (let i = 0; i < 256; i++) b[a[i]!] = i;\n return b;\n}\n\nexport function socDecode(key: SocKey, src: Uint8Array, back = socAlphabetBack(key.tableSeed, key.iterations)): Uint8Array {\n const out = new Uint8Array(src.length), r = random32(key.encryptSeed);\n for (let i = 0; i < src.length; i++) out[i] = back[src[i]! ^ (r(256) & 255)]!;\n return out;\n}\n\n/** The inverse of socDecode (for tests: synthetic encrypted archives). */\nexport function socEncode(key: SocKey, src: Uint8Array): Uint8Array {\n const back = socAlphabetBack(key.tableSeed, key.iterations), a = new Uint8Array(256), r = random32(key.encryptSeed), out = new Uint8Array(src.length);\n for (let i = 0; i < 256; i++) a[back[i]!] = i;\n for (let i = 0; i < src.length; i++) out[i] = a[src[i]!]! ^ (r(256) & 255);\n return out;\n}\n\n/** 32-bit values (step 4) from the PE file's non-executable sections: seed candidates. */\nexport function peDataWords(exe: Uint8Array): Uint32Array {\n const dv = new DataView(exe.buffer, exe.byteOffset, exe.byteLength), out = new Set<number>();\n if (exe.length < 0x40 || dv.getUint16(0, true) !== 0x5a4d) return new Uint32Array(0);\n const pe = dv.getUint32(0x3c, true);\n if (pe + 24 > exe.length || dv.getUint32(pe, true) !== 0x4550) return new Uint32Array(0);\n const ns = dv.getUint16(pe + 6, true), opt = dv.getUint16(pe + 20, true);\n // writable data first (.data holds the global objects), then constants (.rdata)\n const order = Array.from({ length: ns }, (_, k) => k).sort((a, b) => {\n const w = (k: number) => (pe + 24 + opt + k * 40 + 40 <= exe.length && dv.getUint32(pe + 24 + opt + k * 40 + 36, true) & 0x80000000 ? 0 : 1);\n return w(a) - w(b);\n });\n for (const k of order) {\n const o = pe + 24 + opt + k * 40;\n if (o + 40 > exe.length) break;\n const name = String.fromCharCode(...exe.subarray(o, o + 8)).replace(/\\0+$/, ''), raw = dv.getUint32(o + 16, true), ptr = dv.getUint32(o + 20, true), ch = dv.getUint32(o + 36, true);\n // data: initialized (0x40), not code (0x20000000 = executable); resources and relocations are skipped\n if (ch & 0x20000000 || !(ch & 0x40) || name === '.rsrc' || name === '.reloc') continue;\n for (let p = ptr; p + 4 <= Math.min(exe.length, ptr + raw); p += 4) { const v = dv.getUint32(p, true); if (v > 0xffff) out.add(v); }\n }\n return Uint32Array.from(out);\n}\n\nconst ITERATIONS = [2048, 1024, 4096, 512];\n\n/**\n * Key check: cheap first, the first bytes (a plausible size, the first table record: record length and the start of the name),\n * then in full (check = parsing the unpacked table and the record bounds).\n */\nfunction tryKey(key: SocKey, back: Uint8Array, probe: Uint8Array, check: (table: Uint8Array) => boolean): boolean {\n // size (the first 4 bytes) without allocating memory\n let st = key.encryptSeed >>> 0, size = 0;\n for (let i = 0; i < 4; i++) { st = (Math.imul(0x08088405, st) + 1) >>> 0; size += back[probe[i]! ^ (st >>> 24)]! * 2 ** (8 * i); }\n if (size < 16 || size > 32 * probe.length + 1024) return false;\n try {\n // the first record length from the first 16 unpacked bytes\n const pre = lzhufDecompress(socDecode(key, probe.subarray(0, Math.min(probe.length, 24)), back).subarray(4), Math.min(size, 16)), bs0 = pre[0]! | (pre[1]! << 8);\n if (bs0 < 16 || bs0 > 520 || (pre.length > 14 && (pre[14]! < 0x20 || pre[14]! > 0x7e))) return false;\n const head = socDecode(key, probe.subarray(0, Math.min(probe.length, 512)), back);\n // the first table records: u16 length (16..520), a name of printable characters; the second record also has a plausible length\n const first = lzhufDecompress(head.subarray(4), Math.min(size, 600)), dv = new DataView(first.buffer);\n let q = 0;\n for (let rec = 0; rec < 2 && q + 2 <= first.length; rec++) {\n const bs = dv.getUint16(q, true);\n if (bs < 16 || bs > 520) return false;\n for (let k = q + 14; k < Math.min(first.length, q + 2 + bs - 4); k++) if (first[k]! < 0x20 || first[k]! > 0x7e) return false;\n q += 2 + bs;\n }\n return check(lzhufDecompress(socDecode(key, probe, back)));\n } catch { return false; }\n}\n\n/**\n * Find the key in the game executable. probe is an encrypted file table (chunk 1) of any archive, check verifies\n * the unpacked table. Search: for every table seed candidate and permutation count the expected 4th stream byte is known\n * (the high byte of the table size = 0); stream seed candidates are filtered by it and then checked in full.\n */\nexport function findSocKey(exe: Uint8Array, probe: Uint8Array, check: (table: Uint8Array) => boolean): SocKey | null {\n if (probe.length < 8) return null;\n const words = peDataWords(exe);\n // the first 4 stream bytes for every stream seed candidate: indexed by the 4th\n const byK3 = new Map<number, number[]>();\n for (const w of words) {\n const r = random32(w);\n r(256); r(256); r(256);\n const k3 = r(256) & 255;\n let l = byK3.get(k3);\n if (!l) byK3.set(k3, (l = []));\n l.push(w);\n }\n for (const iterations of ITERATIONS) {\n for (const tableSeed of words) {\n const back = socAlphabetBack(tableSeed, iterations);\n // back[c3 ^ k3] = 0 → k3 = c3 ^ alphabet[0]\n let a0 = 0;\n for (let i = 0; i < 256; i++) if (back[i] === 0) { a0 = i; break; }\n for (const encryptSeed of byK3.get(probe[3]! ^ a0) ?? []) {\n if (encryptSeed === tableSeed) continue;\n const key = { tableSeed, encryptSeed, iterations };\n if (tryKey(key, back, probe, check)) return key;\n }\n }\n }\n return null;\n}\n"],"names":[],"mappings":"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"}
package/dist/thm.d.ts CHANGED
@@ -6,7 +6,7 @@ export declare const THM_FLAG: {
6
6
  readonly HAS_ALPHA: number;
7
7
  readonly BUMP_DETAIL: number;
8
8
  };
9
- /** Форматы сжатия, которыми SDK пересохраняет текстуру: DXT1, DXT1 с альфой, DXT3, DXT5, без сжатия. */
9
+ /** Compression formats the SDK re-saves the texture with: DXT1, DXT1 with alpha, DXT3, DXT5, uncompressed. */
10
10
  export declare const THM_FMT: {
11
11
  readonly DXT1: 0;
12
12
  readonly ADXT1: 1;
package/dist/thm.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"thm.js","sources":["../src/thm.ts"],"sourcesContent":["// Параметры текстуры X-Ray (.thm рядом с .dds в gamedata/textures): нужны SDK и компилятору xrLC.\n// Чанки: 0x810 версия, 0x813 тип (картинка), 0x812 формат/флаги/размер, 0x814 вид (обычная, бамп, ландшафт),\n// 0x815 детальная текстура, 0x816 материал, 0x817 бамп, 0x818 внешняя карта нормалей, 0x819 задержка затухания.\nexport const THM_FLAG = { MIPMAPS: 1, DITHER: 1 << 8, DIFFUSE_DETAIL: 1 << 23, IMPLICIT_LIGHTED: 1 << 24, HAS_ALPHA: 1 << 25, BUMP_DETAIL: 1 << 26 } as const;\n/** Форматы сжатия, которыми SDK пересохраняет текстуру: DXT1, DXT1 с альфой, DXT3, DXT5, без сжатия. */\nexport const THM_FMT = { DXT1: 0, ADXT1: 1, DXT3: 2, DXT5: 3, RGBA: 6 } as const;\n\nexport interface ThmIn { width: number; height: number; alpha?: boolean; format?: number; detail?: string; detailScale?: number; bump?: string }\n\nexport function writeThm(t: ThmIn): Uint8Array {\n const parts: number[] = [];\n const u8 = (v: number) => parts.push(v & 0xff);\n const u16 = (v: number) => { u8(v); u8(v >> 8); };\n const u32 = (v: number) => { u16(v & 0xffff); u16((v >>> 16) & 0xffff); };\n const f32 = (v: number) => { const b = new Uint8Array(new Float32Array([v]).buffer); for (const x of b) u8(x); };\n const sz = (s: string) => { for (let i = 0; i < s.length; i++) u8(s.charCodeAt(i)); u8(0); };\n const chunk = (id: number, body: () => void) => { u32(id); const at = parts.length; u32(0); body(); const n = parts.length - at - 4; parts[at] = n & 0xff; parts[at + 1] = (n >> 8) & 0xff; parts[at + 2] = (n >> 16) & 0xff; parts[at + 3] = (n >>> 24) & 0xff; };\n const flags = THM_FLAG.MIPMAPS | (t.alpha ? THM_FLAG.HAS_ALPHA | THM_FLAG.DITHER : 0) | (t.detail ? THM_FLAG.DIFFUSE_DETAIL : 0);\n chunk(0x810, () => u16(0x12));\n chunk(0x813, () => u32(1));\n chunk(0x812, () => { u32(t.format ?? (t.alpha ? THM_FMT.DXT5 : THM_FMT.DXT1)); u32(flags); u32(0); u32(0); u32(0); u32(0); u32(t.width); u32(t.height); });\n chunk(0x814, () => u32(0));\n chunk(0x815, () => { sz(t.detail ?? ''); f32(t.detailScale ?? 1); });\n chunk(0x816, () => { u32(1); f32(0); });\n chunk(0x817, () => { f32(0.05); u32(t.bump ? 2 : 1); sz(t.bump ?? ''); });\n chunk(0x818, () => sz(''));\n chunk(0x819, () => u8(0));\n return Uint8Array.from(parts);\n}\n"],"names":[],"mappings":"AAGO,MAAM,WAAW,EAAE,SAAS,GAAG,QAAQ,KAAK,GAAG,gBAAgB,KAAK,IAA+B,WAAW,KAAK,GAAyB;AAE5I,MAAM,UAAU,EAAE,MAAM,GAAsB,MAAM,EAAW;AAI/D,SAAS,SAAS,GAAsB;AAC7C,QAAM,QAAkB,CAAA;AACxB,QAAM,KAAK,CAAC,MAAc,MAAM,KAAK,IAAI,GAAI;AAC7C,QAAM,MAAM,CAAC,MAAc;AAAE,OAAG,CAAC;AAAG,OAAG,KAAK,CAAC;AAAA,EAAG;AAChD,QAAM,MAAM,CAAC,MAAc;AAAE,QAAI,IAAI,KAAM;AAAG,QAAK,MAAM,KAAM,KAAM;AAAA,EAAG;AACxE,QAAM,MAAM,CAAC,MAAc;AAAE,UAAM,IAAI,IAAI,WAAW,IAAI,aAAa,CAAC,CAAC,CAAC,EAAE,MAAM;AAAG,eAAW,KAAK,EAAG,IAAG,CAAC;AAAA,EAAG;AAC/G,QAAM,KAAK,CAAC,MAAc;AAAE,aAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,IAAK,IAAG,EAAE,WAAW,CAAC,CAAC;AAAG,OAAG,CAAC;AAAA,EAAG;AAC3F,QAAM,QAAQ,CAAC,IAAY,SAAqB;AAAE,QAAI,EAAE;AAAG,UAAM,KAAK,MAAM;AAAQ,QAAI,CAAC;AAAG,SAAA;AAAQ,UAAM,IAAI,MAAM,SAAS,KAAK;AAAG,UAAM,EAAE,IAAI,IAAI;AAAM,UAAM,KAAK,CAAC,IAAK,KAAK,IAAK;AAAM,UAAM,KAAK,CAAC,IAAK,KAAK,KAAM;AAAM,UAAM,KAAK,CAAC,IAAK,MAAM,KAAM;AAAA,EAAM;AACjQ,QAAM,QAAQ,SAAS,WAAW,EAAE,QAAQ,SAAS,YAAY,SAAS,SAAS,MAAM,EAAE,SAAS,SAAS,iBAAiB;AAC9H,QAAM,MAAO,MAAM,IAAI,EAAI,CAAC;AAC5B,QAAM,MAAO,MAAM,IAAI,CAAC,CAAC;AACzB,QAAM,MAAO,MAAM;AAAE,QAAI,EAAE,WAAW,EAAE,QAAQ,QAAQ,OAAO,QAAQ,KAAK;AAAG,QAAI,KAAK;AAAG,QAAI,CAAC;AAAG,QAAI,CAAC;AAAG,QAAI,CAAC;AAAG,QAAI,CAAC;AAAG,QAAI,EAAE,KAAK;AAAG,QAAI,EAAE,MAAM;AAAA,EAAG,CAAC;AACzJ,QAAM,MAAO,MAAM,IAAI,CAAC,CAAC;AACzB,QAAM,MAAO,MAAM;AAAE,OAAG,EAAE,UAAU,EAAE;AAAG,QAAI,EAAE,eAAe,CAAC;AAAA,EAAG,CAAC;AACnE,QAAM,MAAO,MAAM;AAAE,QAAI,CAAC;AAAG,QAAI,CAAC;AAAA,EAAG,CAAC;AACtC,QAAM,MAAO,MAAM;AAAE,QAAI,IAAI;AAAG,QAAI,EAAE,OAAO,IAAI,CAAC;AAAG,OAAG,EAAE,QAAQ,EAAE;AAAA,EAAG,CAAC;AACxE,QAAM,MAAO,MAAM,GAAG,EAAE,CAAC;AACzB,QAAM,MAAO,MAAM,GAAG,CAAC,CAAC;AACxB,SAAO,WAAW,KAAK,KAAK;AAC9B;"}
1
+ {"version":3,"file":"thm.js","sources":["../src/thm.ts"],"sourcesContent":["// X-Ray texture parameters (.thm next to .dds in gamedata/textures): needed by the SDK and the xrLC compiler.\n// Chunks: 0x810 version, 0x813 type (image), 0x812 format/flags/size, 0x814 kind (regular, bump, terrain),\n// 0x815 detail texture, 0x816 material, 0x817 bump, 0x818 external normal map, 0x819 fade delay.\nexport const THM_FLAG = { MIPMAPS: 1, DITHER: 1 << 8, DIFFUSE_DETAIL: 1 << 23, IMPLICIT_LIGHTED: 1 << 24, HAS_ALPHA: 1 << 25, BUMP_DETAIL: 1 << 26 } as const;\n/** Compression formats the SDK re-saves the texture with: DXT1, DXT1 with alpha, DXT3, DXT5, uncompressed. */\nexport const THM_FMT = { DXT1: 0, ADXT1: 1, DXT3: 2, DXT5: 3, RGBA: 6 } as const;\n\nexport interface ThmIn { width: number; height: number; alpha?: boolean; format?: number; detail?: string; detailScale?: number; bump?: string }\n\nexport function writeThm(t: ThmIn): Uint8Array {\n const parts: number[] = [];\n const u8 = (v: number) => parts.push(v & 0xff);\n const u16 = (v: number) => { u8(v); u8(v >> 8); };\n const u32 = (v: number) => { u16(v & 0xffff); u16((v >>> 16) & 0xffff); };\n const f32 = (v: number) => { const b = new Uint8Array(new Float32Array([v]).buffer); for (const x of b) u8(x); };\n const sz = (s: string) => { for (let i = 0; i < s.length; i++) u8(s.charCodeAt(i)); u8(0); };\n const chunk = (id: number, body: () => void) => { u32(id); const at = parts.length; u32(0); body(); const n = parts.length - at - 4; parts[at] = n & 0xff; parts[at + 1] = (n >> 8) & 0xff; parts[at + 2] = (n >> 16) & 0xff; parts[at + 3] = (n >>> 24) & 0xff; };\n const flags = THM_FLAG.MIPMAPS | (t.alpha ? THM_FLAG.HAS_ALPHA | THM_FLAG.DITHER : 0) | (t.detail ? THM_FLAG.DIFFUSE_DETAIL : 0);\n chunk(0x810, () => u16(0x12));\n chunk(0x813, () => u32(1));\n chunk(0x812, () => { u32(t.format ?? (t.alpha ? THM_FMT.DXT5 : THM_FMT.DXT1)); u32(flags); u32(0); u32(0); u32(0); u32(0); u32(t.width); u32(t.height); });\n chunk(0x814, () => u32(0));\n chunk(0x815, () => { sz(t.detail ?? ''); f32(t.detailScale ?? 1); });\n chunk(0x816, () => { u32(1); f32(0); });\n chunk(0x817, () => { f32(0.05); u32(t.bump ? 2 : 1); sz(t.bump ?? ''); });\n chunk(0x818, () => sz(''));\n chunk(0x819, () => u8(0));\n return Uint8Array.from(parts);\n}\n"],"names":[],"mappings":"AAGO,MAAM,WAAW,EAAE,SAAS,GAAG,QAAQ,KAAK,GAAG,gBAAgB,KAAK,IAA+B,WAAW,KAAK,GAAyB;AAE5I,MAAM,UAAU,EAAE,MAAM,GAAsB,MAAM,EAAW;AAI/D,SAAS,SAAS,GAAsB;AAC7C,QAAM,QAAkB,CAAA;AACxB,QAAM,KAAK,CAAC,MAAc,MAAM,KAAK,IAAI,GAAI;AAC7C,QAAM,MAAM,CAAC,MAAc;AAAE,OAAG,CAAC;AAAG,OAAG,KAAK,CAAC;AAAA,EAAG;AAChD,QAAM,MAAM,CAAC,MAAc;AAAE,QAAI,IAAI,KAAM;AAAG,QAAK,MAAM,KAAM,KAAM;AAAA,EAAG;AACxE,QAAM,MAAM,CAAC,MAAc;AAAE,UAAM,IAAI,IAAI,WAAW,IAAI,aAAa,CAAC,CAAC,CAAC,EAAE,MAAM;AAAG,eAAW,KAAK,EAAG,IAAG,CAAC;AAAA,EAAG;AAC/G,QAAM,KAAK,CAAC,MAAc;AAAE,aAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,IAAK,IAAG,EAAE,WAAW,CAAC,CAAC;AAAG,OAAG,CAAC;AAAA,EAAG;AAC3F,QAAM,QAAQ,CAAC,IAAY,SAAqB;AAAE,QAAI,EAAE;AAAG,UAAM,KAAK,MAAM;AAAQ,QAAI,CAAC;AAAG,SAAA;AAAQ,UAAM,IAAI,MAAM,SAAS,KAAK;AAAG,UAAM,EAAE,IAAI,IAAI;AAAM,UAAM,KAAK,CAAC,IAAK,KAAK,IAAK;AAAM,UAAM,KAAK,CAAC,IAAK,KAAK,KAAM;AAAM,UAAM,KAAK,CAAC,IAAK,MAAM,KAAM;AAAA,EAAM;AACjQ,QAAM,QAAQ,SAAS,WAAW,EAAE,QAAQ,SAAS,YAAY,SAAS,SAAS,MAAM,EAAE,SAAS,SAAS,iBAAiB;AAC9H,QAAM,MAAO,MAAM,IAAI,EAAI,CAAC;AAC5B,QAAM,MAAO,MAAM,IAAI,CAAC,CAAC;AACzB,QAAM,MAAO,MAAM;AAAE,QAAI,EAAE,WAAW,EAAE,QAAQ,QAAQ,OAAO,QAAQ,KAAK;AAAG,QAAI,KAAK;AAAG,QAAI,CAAC;AAAG,QAAI,CAAC;AAAG,QAAI,CAAC;AAAG,QAAI,CAAC;AAAG,QAAI,EAAE,KAAK;AAAG,QAAI,EAAE,MAAM;AAAA,EAAG,CAAC;AACzJ,QAAM,MAAO,MAAM,IAAI,CAAC,CAAC;AACzB,QAAM,MAAO,MAAM;AAAE,OAAG,EAAE,UAAU,EAAE;AAAG,QAAI,EAAE,eAAe,CAAC;AAAA,EAAG,CAAC;AACnE,QAAM,MAAO,MAAM;AAAE,QAAI,CAAC;AAAG,QAAI,CAAC;AAAA,EAAG,CAAC;AACtC,QAAM,MAAO,MAAM;AAAE,QAAI,IAAI;AAAG,QAAI,EAAE,OAAO,IAAI,CAAC;AAAG,OAAG,EAAE,QAAQ,EAAE;AAAA,EAAG,CAAC;AACxE,QAAM,MAAO,MAAM,GAAG,EAAE,CAAC;AACzB,QAAM,MAAO,MAAM,GAAG,CAAC,CAAC;AACxB,SAAO,WAAW,KAAK,KAAK;AAC9B;"}
@@ -1,23 +1,23 @@
1
1
  export interface ObjectSurfaceIn {
2
2
  name: string;
3
- /** Текстура без расширения относительно gamedata/textures, например «wall\\wall_stucco_05». */
3
+ /** Texture without extension relative to gamedata/textures, for example "wall\\wall_stucco_05". */
4
4
  texture: string;
5
- /** Шейдер движка (eshader), по умолчанию «default». */
5
+ /** Engine shader (eshader), "default" by default. */
6
6
  eshader?: string;
7
- /** Шейдер компилятора (cshader), по умолчанию «default». */
7
+ /** Compiler shader (cshader), "default" by default. */
8
8
  cshader?: string;
9
- /** Игровой материал, по умолчанию «default». */
9
+ /** Game material, "default" by default. */
10
10
  gamemtl?: string;
11
11
  twoSided?: boolean;
12
12
  }
13
13
  export interface ObjectMeshIn {
14
14
  name: string;
15
15
  pos: ArrayLike<number>;
16
- /** Нормали вершин: по их разрывам находятся жёсткие рёбра. Нет — жёсткие рёбра по углу. */
16
+ /** Vertex normals: hard edges are found from their discontinuities. Without them, hard edges by angle. */
17
17
  nor?: ArrayLike<number> | null;
18
18
  uv: ArrayLike<number>;
19
19
  indices: ArrayLike<number>;
20
- /** Номер поверхности для каждого треугольника или одна на весь меш. */
20
+ /** Surface number for every triangle, or one for the whole mesh. */
21
21
  surface: number | ArrayLike<number>;
22
22
  }
23
23
  export interface ObjectIn {
@@ -27,5 +27,5 @@ export interface ObjectIn {
27
27
  dynamic?: boolean;
28
28
  hardAngleDeg?: number;
29
29
  }
30
- /** Записать .object. Возвращает байты файла. */
30
+ /** Write a .object. Returns the file bytes. */
31
31
  export declare function writeObject(o: ObjectIn): Uint8Array;
@@ -48,7 +48,7 @@ class W {
48
48
  this.pos += u.length;
49
49
  return this;
50
50
  }
51
- /** Чанк: id, затем размер, заполняемый после тела. */
51
+ /** Chunk: id, then a size filled in after the body. */
52
52
  chunk(id, body) {
53
53
  this.d(id);
54
54
  const at = this.pos;
@@ -1 +1 @@
1
- {"version":3,"file":"write-object.js","sources":["../src/write-object.ts"],"sourcesContent":["// Запись исходного объекта X-Ray SDK (.object, версия 0x10, сглаживание рёбрами как в SDK 0.7 ЗП).\n// Вход — треугольники в координатах X-Ray (левосторонние, Y вверх, внешняя нормаль = (b − a) × (c − a)).\nimport { EMESH, EOBJ } from './object';\n\nexport interface ObjectSurfaceIn {\n name: string;\n /** Текстура без расширения относительно gamedata/textures, например «wall\\\\wall_stucco_05». */\n texture: string;\n /** Шейдер движка (eshader), по умолчанию «default». */\n eshader?: string;\n /** Шейдер компилятора (cshader), по умолчанию «default». */\n cshader?: string;\n /** Игровой материал, по умолчанию «default». */\n gamemtl?: string;\n twoSided?: boolean;\n}\nexport interface ObjectMeshIn {\n name: string;\n pos: ArrayLike<number>;\n /** Нормали вершин: по их разрывам находятся жёсткие рёбра. Нет — жёсткие рёбра по углу. */\n nor?: ArrayLike<number> | null;\n uv: ArrayLike<number>;\n indices: ArrayLike<number>;\n /** Номер поверхности для каждого треугольника или одна на весь меш. */\n surface: number | ArrayLike<number>;\n}\nexport interface ObjectIn { meshes: ObjectMeshIn[]; surfaces: ObjectSurfaceIn[]; owner?: string; dynamic?: boolean; hardAngleDeg?: number }\n\nclass W {\n private buf = new Uint8Array(1 << 16);\n private dv = new DataView(this.buf.buffer);\n pos = 0;\n private need(n: number) {\n if (this.pos + n <= this.buf.length) return;\n let s = this.buf.length * 2;\n while (s < this.pos + n) s *= 2;\n const b = new Uint8Array(s);\n b.set(this.buf);\n this.buf = b; this.dv = new DataView(b.buffer);\n }\n b(v: number) { this.need(1); this.dv.setUint8(this.pos++, v); return this; }\n w(v: number) { this.need(2); this.dv.setUint16(this.pos, v, true); this.pos += 2; return this; }\n d(v: number) { this.need(4); this.dv.setUint32(this.pos, v >>> 0, true); this.pos += 4; return this; }\n f(v: number) { this.need(4); this.dv.setFloat32(this.pos, v, true); this.pos += 4; return this; }\n sz(s: string) { const e = encode1251(s); this.need(e.length + 1); this.buf.set(e, this.pos); this.pos += e.length; return this.b(0); }\n bytes(u: Uint8Array) { this.need(u.length); this.buf.set(u, this.pos); this.pos += u.length; return this; }\n /** Чанк: id, затем размер, заполняемый после тела. */\n chunk(id: number, body: (w: W) => void) {\n this.d(id);\n const at = this.pos;\n this.d(0);\n body(this);\n this.dv.setUint32(at, this.pos - at - 4, true);\n return this;\n }\n done() { return this.buf.slice(0, this.pos); }\n}\n\n/** Строки SDK — в кодировке Windows-1251 (имена текстур и материалов бывают русскими). */\nfunction encode1251(s: string): Uint8Array {\n const out = new Uint8Array(s.length);\n for (let i = 0; i < s.length; i++) {\n const c = s.charCodeAt(i);\n out[i] = c < 128 ? c : c >= 0x410 && c <= 0x44f ? c - 0x350 : c === 0x401 ? 0xa8 : c === 0x451 ? 0xb8 : 0x3f;\n }\n return out;\n}\n\ninterface Built {\n points: number[]; faces: number[]; sg: number[]; vmrefs: Array<[number, number]>;\n base: number[]; poly: Array<{ u: number; v: number; point: number; face: number }>;\n sfaces: Map<number, number[]>; bbox: [number[], number[]];\n}\n\nfunction build(m: ObjectMeshIn, hardCos: number): Built {\n const P = m.pos, I = m.indices, nt = Math.floor(I.length / 3);\n // сварка вершин: одна точка .object на позицию и нормаль. Углы с разными нормалями остаются разными точками,\n // поэтому SDK не сгладит то, что в исходнике было жёстким (и не потянет сглаживание через шов в соседний «веер»)\n const pointOf = new Map<string, number>(), vpoint = new Int32Array(P.length / 3), points: number[] = [], N0 = m.nor;\n const q = (x: number) => Math.round(x * 1000);\n for (let v = 0; v < P.length / 3; v++) {\n const k = `${P[v * 3]}|${P[v * 3 + 1]}|${P[v * 3 + 2]}` + (N0 ? `|${q(N0[v * 3]!)}|${q(N0[v * 3 + 1]!)}|${q(N0[v * 3 + 2]!)}` : '');\n let p = pointOf.get(k);\n if (p === undefined) { p = points.length / 3; pointOf.set(k, p); points.push(P[v * 3]!, P[v * 3 + 1]!, P[v * 3 + 2]!); }\n vpoint[v] = p;\n }\n const np = points.length / 3, base = new Array<number>(np * 2).fill(NaN), poly: Built['poly'] = [];\n const vmrefs: Array<[number, number]> = [], vmrefOf = new Map<string, number>(), faces: number[] = [];\n const ref = (vm: number, idx: number) => { const k = `${vm}|${idx}`; let r = vmrefOf.get(k); if (r === undefined) { r = vmrefs.length; vmrefOf.set(k, r); vmrefs.push([vm, idx]); } return r; };\n const sfaces = new Map<number, number[]>();\n const surf = (t: number) => (typeof m.surface === 'number' ? m.surface : m.surface[t] ?? 0);\n // базовая UV точки — из первого угла; отличающиеся углы — в «поли»-карту (разрывы UV)\n for (let t = 0; t < nt; t++) for (let k = 0; k < 3; k++) { const v = I[t * 3 + k]!, p = vpoint[v]!; if (Number.isNaN(base[p * 2])) { base[p * 2] = m.uv[v * 2]!; base[p * 2 + 1] = m.uv[v * 2 + 1]!; } }\n for (let t = 0; t < nt; t++) {\n for (let k = 0; k < 3; k++) {\n const v = I[t * 3 + k]!, p = vpoint[v]!, u = m.uv[v * 2]!, w = m.uv[v * 2 + 1]!;\n let r: number;\n if (u === base[p * 2] && w === base[p * 2 + 1]) r = ref(0, p);\n else { r = ref(1, poly.length); poly.push({ u, v: w, point: p, face: t }); }\n faces.push(p, r);\n }\n const s = surf(t);\n (sfaces.get(s) ?? sfaces.set(s, []).get(s)!).push(t);\n }\n // жёсткие рёбра (бит k — ребро k→k+1): нормали вершин по разные стороны ребра различаются, или угол граней больше порога\n const fn = (t: number) => {\n const a = vpoint[I[t * 3]!]! * 3, b = vpoint[I[t * 3 + 1]!]! * 3, c = vpoint[I[t * 3 + 2]!]! * 3;\n const ux = points[b]! - points[a]!, uy = points[b + 1]! - points[a + 1]!, uz = points[b + 2]! - points[a + 2]!, vx = points[c]! - points[a]!, vy = points[c + 1]! - points[a + 1]!, vz = points[c + 2]! - points[a + 2]!;\n const n = [uy * vz - uz * vy, uz * vx - ux * vz, ux * vy - uy * vx], l = Math.hypot(n[0]!, n[1]!, n[2]!) || 1;\n return [n[0]! / l, n[1]! / l, n[2]! / l];\n };\n const edgeFaces = new Map<string, Array<[number, number]>>();\n for (let t = 0; t < nt; t++) for (let k = 0; k < 3; k++) {\n const a = vpoint[I[t * 3 + k]!]!, b = vpoint[I[t * 3 + ((k + 1) % 3)]!]!, key = a < b ? `${a}_${b}` : `${b}_${a}`;\n (edgeFaces.get(key) ?? edgeFaces.set(key, []).get(key)!).push([t, k]);\n }\n const sg = new Array<number>(nt).fill(0);\n const N = m.nor;\n const vnor = (t: number, k: number) => { const v = I[t * 3 + k]!; return [N![v * 3]!, N![v * 3 + 1]!, N![v * 3 + 2]!]; };\n const dot = (a: number[], b: number[]) => a[0]! * b[0]! + a[1]! * b[1]! + a[2]! * b[2]!;\n for (const list of edgeFaces.values()) {\n if (list.length !== 2) { for (const [t, k] of list) sg[t]! |= 1 << k; continue; } // край или неманифолд — жёстко\n const [[t1, k1], [t2, k2]] = list as [[number, number], [number, number]];\n let hard: boolean;\n if (N) {\n // концы ребра: в грани t1 — углы k1 и k1+1, в t2 те же точки\n const p1 = vpoint[I[t1 * 3 + k1]!]!, cornerIn = (t: number, p: number) => [0, 1, 2].find((k) => vpoint[I[t * 3 + k]!] === p)!;\n const q1 = vpoint[I[t1 * 3 + ((k1 + 1) % 3)]!]!;\n hard = dot(vnor(t1, k1), vnor(t2, cornerIn(t2, p1))) < 0.999 || dot(vnor(t1, (k1 + 1) % 3), vnor(t2, cornerIn(t2, q1))) < 0.999;\n } else hard = dot(fn(t1), fn(t2)) < hardCos;\n if (hard) { sg[t1]! |= 1 << k1; sg[t2]! |= 1 << k2; }\n }\n const lo = [Infinity, Infinity, Infinity], hi = [-Infinity, -Infinity, -Infinity];\n for (let i = 0; i < points.length; i++) { lo[i % 3] = Math.min(lo[i % 3]!, points[i]!); hi[i % 3] = Math.max(hi[i % 3]!, points[i]!); }\n return { points, faces, sg, vmrefs, base, poly, sfaces, bbox: [lo, hi] };\n}\n\n/** Записать .object. Возвращает байты файла. */\nexport function writeObject(o: ObjectIn): Uint8Array {\n const hardCos = Math.cos(((o.hardAngleDeg ?? 60) * Math.PI) / 180), now = Math.floor(Date.now() / 1000), w = new W();\n const sname = (i: number) => o.surfaces[i]?.name ?? `surface_${i}`;\n w.chunk(EOBJ.MAIN, (w) => {\n w.chunk(EOBJ.VERSION, (w) => w.w(0x10));\n w.chunk(EOBJ.USERDATA, (w) => w.sz(''));\n w.chunk(EOBJ.LOD_REF, (w) => w.sz(''));\n w.chunk(EOBJ.FLAGS, (w) => w.d(o.dynamic ? 1 : 0));\n w.chunk(EOBJ.MESHES, (w) => {\n o.meshes.forEach((m, mi) => w.chunk(mi, (w) => {\n const b = build(m, hardCos), np = b.points.length / 3, nt = b.faces.length / 6;\n w.chunk(EMESH.VERSION, (w) => w.w(0x11));\n w.chunk(EMESH.NAME, (w) => w.sz(m.name || `mesh_${mi}`));\n w.chunk(EMESH.BBOX, (w) => { for (const v of [...b.bbox[0], ...b.bbox[1]]) w.f(np ? v : 0); });\n w.chunk(EMESH.FLAGS, (w) => w.b(1)); // виден\n w.chunk(EMESH.OPTIONS, (w) => w.d(0).d(0));\n w.chunk(EMESH.VERTS, (w) => { w.d(np); for (const v of b.points) w.f(v); });\n w.chunk(EMESH.FACES, (w) => { w.d(nt); for (const v of b.faces) w.d(v); });\n w.chunk(EMESH.SG, (w) => { for (const v of b.sg) w.d(v); });\n w.chunk(EMESH.VMREFS, (w) => { w.d(b.vmrefs.length); for (const [vm, i] of b.vmrefs) w.b(1).d(vm).d(i); });\n w.chunk(EMESH.SFACE, (w) => {\n w.w(b.sfaces.size);\n for (const [s, list] of b.sfaces) { w.sz(sname(s)).d(list.length); for (const f of list) w.d(f); }\n });\n w.chunk(EMESH.VMAPS2, (w) => {\n w.d(b.poly.length ? 2 : 1);\n w.sz('Texture').b(2).b(0).b(0).d(np);\n for (let p = 0; p < np; p++) w.f(b.base[p * 2]!).f(b.base[p * 2 + 1]!);\n for (let p = 0; p < np; p++) w.d(p);\n if (b.poly.length) {\n w.sz('Texture').b(2).b(1).b(0).d(b.poly.length);\n for (const e of b.poly) w.f(e.u).f(e.v);\n for (const e of b.poly) w.d(e.point);\n for (const e of b.poly) w.d(e.face);\n }\n });\n }));\n });\n w.chunk(EOBJ.SURFACES3, (w) => {\n w.d(o.surfaces.length);\n for (const s of o.surfaces) w.sz(s.name).sz(s.eshader ?? 'default').sz(s.cshader ?? 'default').sz(s.gamemtl ?? 'default').sz(s.texture).sz('Texture').d(s.twoSided ? 1 : 0).d(0x112).d(1);\n });\n w.chunk(EOBJ.REVISION, (w) => w.sz(o.owner ?? 'gmv').d(now).sz(o.owner ?? 'gmv').d(now));\n });\n return w.done();\n}\n"],"names":["w"],"mappings":"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"}
1
+ {"version":3,"file":"write-object.js","sources":["../src/write-object.ts"],"sourcesContent":["// Writing an X-Ray SDK source object (.object, version 0x10, edge smoothing as in SDK 0.7 CoP).\n// Input: triangles in X-Ray coordinates (left-handed, Y up, outward normal = (b − a) × (c − a)).\nimport { EMESH, EOBJ } from './object';\n\nexport interface ObjectSurfaceIn {\n name: string;\n /** Texture without extension relative to gamedata/textures, for example \"wall\\\\wall_stucco_05\". */\n texture: string;\n /** Engine shader (eshader), \"default\" by default. */\n eshader?: string;\n /** Compiler shader (cshader), \"default\" by default. */\n cshader?: string;\n /** Game material, \"default\" by default. */\n gamemtl?: string;\n twoSided?: boolean;\n}\nexport interface ObjectMeshIn {\n name: string;\n pos: ArrayLike<number>;\n /** Vertex normals: hard edges are found from their discontinuities. Without them, hard edges by angle. */\n nor?: ArrayLike<number> | null;\n uv: ArrayLike<number>;\n indices: ArrayLike<number>;\n /** Surface number for every triangle, or one for the whole mesh. */\n surface: number | ArrayLike<number>;\n}\nexport interface ObjectIn { meshes: ObjectMeshIn[]; surfaces: ObjectSurfaceIn[]; owner?: string; dynamic?: boolean; hardAngleDeg?: number }\n\nclass W {\n private buf = new Uint8Array(1 << 16);\n private dv = new DataView(this.buf.buffer);\n pos = 0;\n private need(n: number) {\n if (this.pos + n <= this.buf.length) return;\n let s = this.buf.length * 2;\n while (s < this.pos + n) s *= 2;\n const b = new Uint8Array(s);\n b.set(this.buf);\n this.buf = b; this.dv = new DataView(b.buffer);\n }\n b(v: number) { this.need(1); this.dv.setUint8(this.pos++, v); return this; }\n w(v: number) { this.need(2); this.dv.setUint16(this.pos, v, true); this.pos += 2; return this; }\n d(v: number) { this.need(4); this.dv.setUint32(this.pos, v >>> 0, true); this.pos += 4; return this; }\n f(v: number) { this.need(4); this.dv.setFloat32(this.pos, v, true); this.pos += 4; return this; }\n sz(s: string) { const e = encode1251(s); this.need(e.length + 1); this.buf.set(e, this.pos); this.pos += e.length; return this.b(0); }\n bytes(u: Uint8Array) { this.need(u.length); this.buf.set(u, this.pos); this.pos += u.length; return this; }\n /** Chunk: id, then a size filled in after the body. */\n chunk(id: number, body: (w: W) => void) {\n this.d(id);\n const at = this.pos;\n this.d(0);\n body(this);\n this.dv.setUint32(at, this.pos - at - 4, true);\n return this;\n }\n done() { return this.buf.slice(0, this.pos); }\n}\n\n/** SDK strings are in Windows-1251 (texture and material names may be in Russian). */\nfunction encode1251(s: string): Uint8Array {\n const out = new Uint8Array(s.length);\n for (let i = 0; i < s.length; i++) {\n const c = s.charCodeAt(i);\n out[i] = c < 128 ? c : c >= 0x410 && c <= 0x44f ? c - 0x350 : c === 0x401 ? 0xa8 : c === 0x451 ? 0xb8 : 0x3f;\n }\n return out;\n}\n\ninterface Built {\n points: number[]; faces: number[]; sg: number[]; vmrefs: Array<[number, number]>;\n base: number[]; poly: Array<{ u: number; v: number; point: number; face: number }>;\n sfaces: Map<number, number[]>; bbox: [number[], number[]];\n}\n\nfunction build(m: ObjectMeshIn, hardCos: number): Built {\n const P = m.pos, I = m.indices, nt = Math.floor(I.length / 3);\n // vertex welding: one .object point per position and normal. Corners with different normals stay different points,\n // so the SDK does not smooth what was hard in the source (and does not drag smoothing across a seam into the neighbouring \"fan\")\n const pointOf = new Map<string, number>(), vpoint = new Int32Array(P.length / 3), points: number[] = [], N0 = m.nor;\n const q = (x: number) => Math.round(x * 1000);\n for (let v = 0; v < P.length / 3; v++) {\n const k = `${P[v * 3]}|${P[v * 3 + 1]}|${P[v * 3 + 2]}` + (N0 ? `|${q(N0[v * 3]!)}|${q(N0[v * 3 + 1]!)}|${q(N0[v * 3 + 2]!)}` : '');\n let p = pointOf.get(k);\n if (p === undefined) { p = points.length / 3; pointOf.set(k, p); points.push(P[v * 3]!, P[v * 3 + 1]!, P[v * 3 + 2]!); }\n vpoint[v] = p;\n }\n const np = points.length / 3, base = new Array<number>(np * 2).fill(NaN), poly: Built['poly'] = [];\n const vmrefs: Array<[number, number]> = [], vmrefOf = new Map<string, number>(), faces: number[] = [];\n const ref = (vm: number, idx: number) => { const k = `${vm}|${idx}`; let r = vmrefOf.get(k); if (r === undefined) { r = vmrefs.length; vmrefOf.set(k, r); vmrefs.push([vm, idx]); } return r; };\n const sfaces = new Map<number, number[]>();\n const surf = (t: number) => (typeof m.surface === 'number' ? m.surface : m.surface[t] ?? 0);\n // a point's base UV comes from its first corner; differing corners go to the \"poly\" map (UV seams)\n for (let t = 0; t < nt; t++) for (let k = 0; k < 3; k++) { const v = I[t * 3 + k]!, p = vpoint[v]!; if (Number.isNaN(base[p * 2])) { base[p * 2] = m.uv[v * 2]!; base[p * 2 + 1] = m.uv[v * 2 + 1]!; } }\n for (let t = 0; t < nt; t++) {\n for (let k = 0; k < 3; k++) {\n const v = I[t * 3 + k]!, p = vpoint[v]!, u = m.uv[v * 2]!, w = m.uv[v * 2 + 1]!;\n let r: number;\n if (u === base[p * 2] && w === base[p * 2 + 1]) r = ref(0, p);\n else { r = ref(1, poly.length); poly.push({ u, v: w, point: p, face: t }); }\n faces.push(p, r);\n }\n const s = surf(t);\n (sfaces.get(s) ?? sfaces.set(s, []).get(s)!).push(t);\n }\n // hard edges (bit k = edge k→k+1): vertex normals differ on the two sides of the edge, or the face angle exceeds the threshold\n const fn = (t: number) => {\n const a = vpoint[I[t * 3]!]! * 3, b = vpoint[I[t * 3 + 1]!]! * 3, c = vpoint[I[t * 3 + 2]!]! * 3;\n const ux = points[b]! - points[a]!, uy = points[b + 1]! - points[a + 1]!, uz = points[b + 2]! - points[a + 2]!, vx = points[c]! - points[a]!, vy = points[c + 1]! - points[a + 1]!, vz = points[c + 2]! - points[a + 2]!;\n const n = [uy * vz - uz * vy, uz * vx - ux * vz, ux * vy - uy * vx], l = Math.hypot(n[0]!, n[1]!, n[2]!) || 1;\n return [n[0]! / l, n[1]! / l, n[2]! / l];\n };\n const edgeFaces = new Map<string, Array<[number, number]>>();\n for (let t = 0; t < nt; t++) for (let k = 0; k < 3; k++) {\n const a = vpoint[I[t * 3 + k]!]!, b = vpoint[I[t * 3 + ((k + 1) % 3)]!]!, key = a < b ? `${a}_${b}` : `${b}_${a}`;\n (edgeFaces.get(key) ?? edgeFaces.set(key, []).get(key)!).push([t, k]);\n }\n const sg = new Array<number>(nt).fill(0);\n const N = m.nor;\n const vnor = (t: number, k: number) => { const v = I[t * 3 + k]!; return [N![v * 3]!, N![v * 3 + 1]!, N![v * 3 + 2]!]; };\n const dot = (a: number[], b: number[]) => a[0]! * b[0]! + a[1]! * b[1]! + a[2]! * b[2]!;\n for (const list of edgeFaces.values()) {\n if (list.length !== 2) { for (const [t, k] of list) sg[t]! |= 1 << k; continue; } // a border or non-manifold edge: hard\n const [[t1, k1], [t2, k2]] = list as [[number, number], [number, number]];\n let hard: boolean;\n if (N) {\n // edge ends: corners k1 and k1+1 in face t1, the same points in t2\n const p1 = vpoint[I[t1 * 3 + k1]!]!, cornerIn = (t: number, p: number) => [0, 1, 2].find((k) => vpoint[I[t * 3 + k]!] === p)!;\n const q1 = vpoint[I[t1 * 3 + ((k1 + 1) % 3)]!]!;\n hard = dot(vnor(t1, k1), vnor(t2, cornerIn(t2, p1))) < 0.999 || dot(vnor(t1, (k1 + 1) % 3), vnor(t2, cornerIn(t2, q1))) < 0.999;\n } else hard = dot(fn(t1), fn(t2)) < hardCos;\n if (hard) { sg[t1]! |= 1 << k1; sg[t2]! |= 1 << k2; }\n }\n const lo = [Infinity, Infinity, Infinity], hi = [-Infinity, -Infinity, -Infinity];\n for (let i = 0; i < points.length; i++) { lo[i % 3] = Math.min(lo[i % 3]!, points[i]!); hi[i % 3] = Math.max(hi[i % 3]!, points[i]!); }\n return { points, faces, sg, vmrefs, base, poly, sfaces, bbox: [lo, hi] };\n}\n\n/** Write a .object. Returns the file bytes. */\nexport function writeObject(o: ObjectIn): Uint8Array {\n const hardCos = Math.cos(((o.hardAngleDeg ?? 60) * Math.PI) / 180), now = Math.floor(Date.now() / 1000), w = new W();\n const sname = (i: number) => o.surfaces[i]?.name ?? `surface_${i}`;\n w.chunk(EOBJ.MAIN, (w) => {\n w.chunk(EOBJ.VERSION, (w) => w.w(0x10));\n w.chunk(EOBJ.USERDATA, (w) => w.sz(''));\n w.chunk(EOBJ.LOD_REF, (w) => w.sz(''));\n w.chunk(EOBJ.FLAGS, (w) => w.d(o.dynamic ? 1 : 0));\n w.chunk(EOBJ.MESHES, (w) => {\n o.meshes.forEach((m, mi) => w.chunk(mi, (w) => {\n const b = build(m, hardCos), np = b.points.length / 3, nt = b.faces.length / 6;\n w.chunk(EMESH.VERSION, (w) => w.w(0x11));\n w.chunk(EMESH.NAME, (w) => w.sz(m.name || `mesh_${mi}`));\n w.chunk(EMESH.BBOX, (w) => { for (const v of [...b.bbox[0], ...b.bbox[1]]) w.f(np ? v : 0); });\n w.chunk(EMESH.FLAGS, (w) => w.b(1)); // visible\n w.chunk(EMESH.OPTIONS, (w) => w.d(0).d(0));\n w.chunk(EMESH.VERTS, (w) => { w.d(np); for (const v of b.points) w.f(v); });\n w.chunk(EMESH.FACES, (w) => { w.d(nt); for (const v of b.faces) w.d(v); });\n w.chunk(EMESH.SG, (w) => { for (const v of b.sg) w.d(v); });\n w.chunk(EMESH.VMREFS, (w) => { w.d(b.vmrefs.length); for (const [vm, i] of b.vmrefs) w.b(1).d(vm).d(i); });\n w.chunk(EMESH.SFACE, (w) => {\n w.w(b.sfaces.size);\n for (const [s, list] of b.sfaces) { w.sz(sname(s)).d(list.length); for (const f of list) w.d(f); }\n });\n w.chunk(EMESH.VMAPS2, (w) => {\n w.d(b.poly.length ? 2 : 1);\n w.sz('Texture').b(2).b(0).b(0).d(np);\n for (let p = 0; p < np; p++) w.f(b.base[p * 2]!).f(b.base[p * 2 + 1]!);\n for (let p = 0; p < np; p++) w.d(p);\n if (b.poly.length) {\n w.sz('Texture').b(2).b(1).b(0).d(b.poly.length);\n for (const e of b.poly) w.f(e.u).f(e.v);\n for (const e of b.poly) w.d(e.point);\n for (const e of b.poly) w.d(e.face);\n }\n });\n }));\n });\n w.chunk(EOBJ.SURFACES3, (w) => {\n w.d(o.surfaces.length);\n for (const s of o.surfaces) w.sz(s.name).sz(s.eshader ?? 'default').sz(s.cshader ?? 'default').sz(s.gamemtl ?? 'default').sz(s.texture).sz('Texture').d(s.twoSided ? 1 : 0).d(0x112).d(1);\n });\n w.chunk(EOBJ.REVISION, (w) => w.sz(o.owner ?? 'gmv').d(now).sz(o.owner ?? 'gmv').d(now));\n });\n return w.done();\n}\n"],"names":["w"],"mappings":"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"}
@@ -10,28 +10,28 @@ export declare const ESM: {
10
10
  readonly USE_WEAPON_BONE: 128;
11
11
  };
12
12
  export interface OmfMotionDef {
13
- /** Флаги esm*. */
13
+ /** esm* flags. */
14
14
  flags?: number;
15
- /** Часть тела (номер в parts) для циклов или кость для FX. */
15
+ /** Body part (an index in parts) for cycles, or a bone for FX. */
16
16
  boneOrPart?: number;
17
17
  speed?: number;
18
18
  power?: number;
19
19
  accrue?: number;
20
20
  falloff?: number;
21
- /** Метки (шаги и т. п.): имя и интервалы [начало, конец] в секундах. */
21
+ /** Marks (footsteps and the like): a name and [start, end] intervals in seconds. */
22
22
  marks?: Array<{
23
23
  name: string;
24
24
  intervals: Array<[number, number]>;
25
25
  }>;
26
- /** Дополнительные имена того же движения. Движок X-Ray нумерует движения по порядку описаний (`m_cycle[name] = mot_i`,
27
- * поле «номер движения» описания не читается), поэтому писатель разворачивает псевдонимы в полные копии движения
28
- * (ключи дублируются в файле); описание-ссылка на чужие ключи движок читать не умеет (net1198). */
26
+ /** Extra names of the same motion. The X-Ray engine numbers motions in description order (`m_cycle[name] = mot_i`,
27
+ * the description's "motion number" field is not read), so the writer expands aliases into full copies of the motion
28
+ * (keys are duplicated in the file); the engine cannot read a description pointing at another motion's keys (net1198). */
29
29
  aliases?: string[];
30
30
  }
31
31
  export interface OmfInput {
32
- /** Имена костей в порядке треков (порядок костей модели). */
32
+ /** Bone names in track order (the model bone order). */
33
33
  bones: readonly string[];
34
- /** Части тела; пусто - одна часть «default» со всеми костями. */
34
+ /** Body parts; empty = one "default" part with all bones. */
35
35
  parts?: Array<{
36
36
  name: string;
37
37
  bones: string[];
@@ -43,5 +43,5 @@ export interface OmfInput {
43
43
  }
44
44
  export declare function crc32(u: Uint8Array): number;
45
45
  export declare function writeOMF(input0: OmfInput): Uint8Array;
46
- /** Квантованный поворот из float-кватерниона (x, y, z, w) - для треков, построенных не из OMF. */
46
+ /** Quantized rotation from a float quaternion (x, y, z, w), for tracks not built from OMF. */
47
47
  export declare function quantRot(q: readonly number[]): Int16Array;
@@ -1,10 +1,10 @@
1
1
  export interface SceneObjectOut {
2
- /** Имя экземпляра в сцене. */
2
+ /** Instance name in the scene. */
3
3
  name: string;
4
- /** Путь объекта в rawdata/objects без расширения, например «gmv\\metro\\tunnel_01». */
4
+ /** Object path in rawdata/objects without extension, for example "gmv\\metro\\tunnel_01". */
5
5
  reference: string;
6
6
  position: number[];
7
- /** Углы, как в part-файле (см. sceneRotation), либо матрица поворота 3×3 (строки-векторы). */
7
+ /** Angles as in the part file (see sceneRotation), or a 3×3 rotation matrix (row vectors). */
8
8
  rotation?: number[];
9
9
  matrix?: number[];
10
10
  scale?: number[];
@@ -18,9 +18,9 @@ export interface SceneOut {
18
18
  hpb: number[];
19
19
  };
20
20
  }
21
- /** Углы part-файла по матрице поворота (обратное к sceneRotation). */
21
+ /** Part-file angles from a rotation matrix (the inverse of sceneRotation). */
22
22
  export declare function sceneEuler(R: ArrayLike<number>): number[];
23
- /** ini в стиле SDK: секции по алфавиту, ключи по алфавиту, «ключ = значение» с выравниванием, пустая строка из пробела. */
23
+ /** SDK-style ini: sections and keys sorted alphabetically, "key = value" aligned, an empty line made of a space. */
24
24
  export declare function writeIni(sections: Record<string, Record<string, string>>): string;
25
- /** Файлы сцены: путь (относительно rawdata/levels) -> байты. */
25
+ /** Scene files: path (relative to rawdata/levels) -> bytes. */
26
26
  export declare function writeScene(sc: SceneOut): Map<string, Uint8Array>;
@@ -1 +1 @@
1
- {"version":3,"file":"write-scene.js","sources":["../src/write-scene.ts"],"sourcesContent":["// Запись сцены уровня X-Ray SDK 0.7 (ЗП): <уровень>.level и <уровень>/scene_object.part (ini, CRLF, Windows-1251).\n// Остальные части (свет, секторы, порталы, спавн, AI-сетка) добавляются в LevelEditor.\n\nexport interface SceneObjectOut {\n /** Имя экземпляра в сцене. */\n name: string;\n /** Путь объекта в rawdata/objects без расширения, например «gmv\\\\metro\\\\tunnel_01». */\n reference: string;\n position: number[];\n /** Углы, как в part-файле (см. sceneRotation), либо матрица поворота 3×3 (строки-векторы). */\n rotation?: number[];\n matrix?: number[];\n scale?: number[];\n}\nexport interface SceneOut { name: string; objects: SceneObjectOut[]; owner?: string; camera?: { pos: number[]; hpb: number[] } }\n\n/** Углы part-файла по матрице поворота (обратное к sceneRotation). */\nexport function sceneEuler(R: ArrayLike<number>): number[] {\n // sceneRotation = transpose(setHPB(y, x, z)) -> m = transpose(R)\n const m = [R[0]!, R[3]!, R[6]!, R[1]!, R[4]!, R[7]!, R[2]!, R[5]!, R[8]!];\n const p = Math.asin(Math.max(-1, Math.min(1, m[5]!)));\n let h: number, b: number;\n if (Math.abs(m[5]!) < 0.99999) { h = Math.atan2(-m[2]!, m[8]!); b = Math.atan2(-m[3]!, m[4]!); }\n else { h = 0; b = Math.atan2(m[1]!, m[0]!); } // вырожденный случай (тангаж ±90°)\n return [p, h, b];\n}\n\nconst f6 = (v: number) => (Math.abs(v) < 5e-7 ? 0 : v).toFixed(6);\nconst vec = (a: number[]) => a.map(f6).join(', ');\n\n/** ini в стиле SDK: секции по алфавиту, ключи по алфавиту, «ключ = значение» с выравниванием, пустая строка из пробела. */\nexport function writeIni(sections: Record<string, Record<string, string>>): string {\n let s = '';\n for (const name of Object.keys(sections).sort()) {\n s += `[${name}]\\r\\n`;\n const sec = sections[name]!;\n for (const k of Object.keys(sec).sort()) { const v = sec[k]!; s += ` ${k.padEnd(33)}=${v === '' ? '' : ' ' + v}\\r\\n`; }\n s += ' \\r\\n';\n }\n return s;\n}\n\nfunction encode1251(s: string): Uint8Array {\n const out = new Uint8Array(s.length);\n for (let i = 0; i < s.length; i++) { const c = s.charCodeAt(i); out[i] = c < 128 ? c : c >= 0x410 && c <= 0x44f ? c - 0x350 : c === 0x401 ? 0xa8 : c === 0x451 ? 0xb8 : 0x3f; }\n return out;\n}\n\nconst rnd64 = () => { let v = 0n; for (let i = 0; i < 8; i++) v = (v << 8n) | BigInt(Math.floor(Math.random() * 256)); return v.toString(); };\n\n/** Файлы сцены: путь (относительно rawdata/levels) -> байты. */\nexport function writeScene(sc: SceneOut): Map<string, Uint8Array> {\n const g0 = rnd64(), g1 = rnd64(), now = Math.floor(Date.now() / 1000);\n const objects: Record<string, Record<string, string>> = {};\n sc.objects.forEach((o, i) => {\n const rot = o.rotation ?? (o.matrix ? sceneEuler(o.matrix) : [0, 0, 0]);\n objects[`object_${i}`] = {\n clsid: '2', co_flags: '0', flags: '0', name: o.name, position: vec(o.position), reference_name: o.reference,\n rotation: vec(rot), scale: vec(o.scale ?? [1, 1, 1]), version: '18',\n };\n });\n const part = writeIni({\n appendrandom: { AppendRandomMaxRotation: vec([0, 0, 0]), AppendRandomMaxScale: vec([1, 1, 1]), AppendRandomMinRotation: vec([0, 0, 0]), AppendRandomMinScale: vec([1, 1, 1]), AppendRandomObjects_size: '0' },\n guid: { guid_g0: g0, guid_g1: g1 },\n main: { flags: '0', objects_count: String(sc.objects.length), version: '0' },\n modif: { name: '', time: '0' },\n ...objects,\n });\n // камера: над центром сцены\n let cam = sc.camera;\n if (!cam) {\n const c = [0, 0, 0];\n for (const o of sc.objects) for (let k = 0; k < 3; k++) c[k] += o.position[k]! / Math.max(1, sc.objects.length);\n cam = { pos: [c[0]!, c[1]! + 20, c[2]! - 30], hpb: [0, -0.5, 0] };\n }\n const level = writeIni({\n build_params: {\n light_jitter_samples: '9', light_pixel_per_meter: f6(20), light_quality: '2', light_quality_reserved: '29795', light_rms: '4', light_rms_zero: '4',\n reserved_0: f6(0), reserved_1: f6(0), reserved_2: f6(0), reserved_3: f6(0), reserved_4: f6(0), reserved_5: f6(0), smooth_angle: f6(75), weld_distance: f6(0.005),\n },\n camera: { hpb: vec(cam.hpb), pos: vec(cam.pos) },\n guid: { guid_g0: g0, guid_g1: g1 },\n level_options: {\n bop: '\"\"', game_type: '1', level_path: sc.name, level_prefix: sc.name, light_hemi_quality: '3', light_sun_quality: '0', map_version: '1.0', version: '12', version_bp: '9',\n },\n level_tag: { create_time: String(now), owner: sc.owner ?? 'gmv' },\n version: { value: '5' },\n });\n return new Map([[`${sc.name}.level`, encode1251(level)], [`${sc.name}/scene_object.part`, encode1251(part)]]);\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"write-scene.js","sources":["../src/write-scene.ts"],"sourcesContent":["// Writing an X-Ray SDK 0.7 level scene (CoP): <level>.level and <level>/scene_object.part (ini, CRLF, Windows-1251).\n// The other parts (lights, sectors, portals, spawn, AI grid) are added in LevelEditor.\n\nexport interface SceneObjectOut {\n /** Instance name in the scene. */\n name: string;\n /** Object path in rawdata/objects without extension, for example \"gmv\\\\metro\\\\tunnel_01\". */\n reference: string;\n position: number[];\n /** Angles as in the part file (see sceneRotation), or a 3×3 rotation matrix (row vectors). */\n rotation?: number[];\n matrix?: number[];\n scale?: number[];\n}\nexport interface SceneOut { name: string; objects: SceneObjectOut[]; owner?: string; camera?: { pos: number[]; hpb: number[] } }\n\n/** Part-file angles from a rotation matrix (the inverse of sceneRotation). */\nexport function sceneEuler(R: ArrayLike<number>): number[] {\n // sceneRotation = transpose(setHPB(y, x, z)) -> m = transpose(R)\n const m = [R[0]!, R[3]!, R[6]!, R[1]!, R[4]!, R[7]!, R[2]!, R[5]!, R[8]!];\n const p = Math.asin(Math.max(-1, Math.min(1, m[5]!)));\n let h: number, b: number;\n if (Math.abs(m[5]!) < 0.99999) { h = Math.atan2(-m[2]!, m[8]!); b = Math.atan2(-m[3]!, m[4]!); }\n else { h = 0; b = Math.atan2(m[1]!, m[0]!); } // degenerate case (pitch ±90°)\n return [p, h, b];\n}\n\nconst f6 = (v: number) => (Math.abs(v) < 5e-7 ? 0 : v).toFixed(6);\nconst vec = (a: number[]) => a.map(f6).join(', ');\n\n/** SDK-style ini: sections and keys sorted alphabetically, \"key = value\" aligned, an empty line made of a space. */\nexport function writeIni(sections: Record<string, Record<string, string>>): string {\n let s = '';\n for (const name of Object.keys(sections).sort()) {\n s += `[${name}]\\r\\n`;\n const sec = sections[name]!;\n for (const k of Object.keys(sec).sort()) { const v = sec[k]!; s += ` ${k.padEnd(33)}=${v === '' ? '' : ' ' + v}\\r\\n`; }\n s += ' \\r\\n';\n }\n return s;\n}\n\nfunction encode1251(s: string): Uint8Array {\n const out = new Uint8Array(s.length);\n for (let i = 0; i < s.length; i++) { const c = s.charCodeAt(i); out[i] = c < 128 ? c : c >= 0x410 && c <= 0x44f ? c - 0x350 : c === 0x401 ? 0xa8 : c === 0x451 ? 0xb8 : 0x3f; }\n return out;\n}\n\nconst rnd64 = () => { let v = 0n; for (let i = 0; i < 8; i++) v = (v << 8n) | BigInt(Math.floor(Math.random() * 256)); return v.toString(); };\n\n/** Scene files: path (relative to rawdata/levels) -> bytes. */\nexport function writeScene(sc: SceneOut): Map<string, Uint8Array> {\n const g0 = rnd64(), g1 = rnd64(), now = Math.floor(Date.now() / 1000);\n const objects: Record<string, Record<string, string>> = {};\n sc.objects.forEach((o, i) => {\n const rot = o.rotation ?? (o.matrix ? sceneEuler(o.matrix) : [0, 0, 0]);\n objects[`object_${i}`] = {\n clsid: '2', co_flags: '0', flags: '0', name: o.name, position: vec(o.position), reference_name: o.reference,\n rotation: vec(rot), scale: vec(o.scale ?? [1, 1, 1]), version: '18',\n };\n });\n const part = writeIni({\n appendrandom: { AppendRandomMaxRotation: vec([0, 0, 0]), AppendRandomMaxScale: vec([1, 1, 1]), AppendRandomMinRotation: vec([0, 0, 0]), AppendRandomMinScale: vec([1, 1, 1]), AppendRandomObjects_size: '0' },\n guid: { guid_g0: g0, guid_g1: g1 },\n main: { flags: '0', objects_count: String(sc.objects.length), version: '0' },\n modif: { name: '', time: '0' },\n ...objects,\n });\n // camera: above the scene centre\n let cam = sc.camera;\n if (!cam) {\n const c = [0, 0, 0];\n for (const o of sc.objects) for (let k = 0; k < 3; k++) c[k] += o.position[k]! / Math.max(1, sc.objects.length);\n cam = { pos: [c[0]!, c[1]! + 20, c[2]! - 30], hpb: [0, -0.5, 0] };\n }\n const level = writeIni({\n build_params: {\n light_jitter_samples: '9', light_pixel_per_meter: f6(20), light_quality: '2', light_quality_reserved: '29795', light_rms: '4', light_rms_zero: '4',\n reserved_0: f6(0), reserved_1: f6(0), reserved_2: f6(0), reserved_3: f6(0), reserved_4: f6(0), reserved_5: f6(0), smooth_angle: f6(75), weld_distance: f6(0.005),\n },\n camera: { hpb: vec(cam.hpb), pos: vec(cam.pos) },\n guid: { guid_g0: g0, guid_g1: g1 },\n level_options: {\n bop: '\"\"', game_type: '1', level_path: sc.name, level_prefix: sc.name, light_hemi_quality: '3', light_sun_quality: '0', map_version: '1.0', version: '12', version_bp: '9',\n },\n level_tag: { create_time: String(now), owner: sc.owner ?? 'gmv' },\n version: { value: '5' },\n });\n return new Map([[`${sc.name}.level`, encode1251(level)], [`${sc.name}/scene_object.part`, encode1251(part)]]);\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gmvjs/xray-v1",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "gmv: X-Ray Engine 1.x",
5
5
  "keywords": [
6
6
  "gmv",
@@ -60,8 +60,8 @@
60
60
  "build": "node ../../scripts/build-package.mjs"
61
61
  },
62
62
  "dependencies": {
63
- "@gmvjs/codecs": "^0.1.0",
64
- "@gmvjs/core": "^0.1.0"
63
+ "@gmvjs/codecs": "^0.1.1",
64
+ "@gmvjs/core": "^0.1.1"
65
65
  },
66
66
  "devDependencies": {
67
67
  "@gmvjs/test-utils": "^0.1.0"