UNPKG

arx-convert

Version:

Converts various Arx Fatalis formats to JSON or YAML and back

212 lines 8.81 kB
import { BinaryIO } from '../common/BinaryIO.js'; import { repeat, times } from '../common/helpers.js'; import { TEA_VERSION_NEW, TEA_VERSION_OLD } from './constants.js'; /** * @see https://github.com/arx/ArxLibertatis/blob/1.2.1/src/animation/Animation.h#L131 * @see https://github.com/arx/ArxLibertatis/blob/ArxFatalis-1.21/Sources/Include/EERIETypes.h#L667 */ export var ArxAnimationPlayingFlags; (function (ArxAnimationPlayingFlags) { ArxAnimationPlayingFlags[ArxAnimationPlayingFlags["None"] = 0] = "None"; /** * Must be looped at end (indefinitely...) */ ArxAnimationPlayingFlags[ArxAnimationPlayingFlags["Loop"] = 1] = "Loop"; /** * Is played in reverse (from end to start) */ ArxAnimationPlayingFlags[ArxAnimationPlayingFlags["Reverse"] = 2] = "Reverse"; /** * Is paused */ ArxAnimationPlayingFlags[ArxAnimationPlayingFlags["Paused"] = 4] = "Paused"; /** * Has just finished */ ArxAnimationPlayingFlags[ArxAnimationPlayingFlags["AnimEnd"] = 8] = "AnimEnd"; /** * Is a static Anim (no movement offset returned) */ ArxAnimationPlayingFlags[ArxAnimationPlayingFlags["StaticAnim"] = 16] = "StaticAnim"; /** * Must be stopped at end */ ArxAnimationPlayingFlags[ArxAnimationPlayingFlags["StopEnd"] = 32] = "StopEnd"; /** * User controlled... MUST be played... */ ArxAnimationPlayingFlags[ArxAnimationPlayingFlags["ForcePlay"] = 64] = "ForcePlay"; /** * ctime externally set, no update */ ArxAnimationPlayingFlags[ArxAnimationPlayingFlags["ExControl"] = 128] = "ExControl"; })(ArxAnimationPlayingFlags || (ArxAnimationPlayingFlags = {})); export class KeyFrame { static readFrom(binary, version, numberOfGroups) { const frame = binary.readInt32(); const flags = binary.readInt32(); const isNewerVersion = version >= TEA_VERSION_NEW; if (isNewerVersion) { binary.readString(256); // info_frame - always an empty string } const isMasterKeyFrame = binary.readInt32() !== 0; const isKeyFrame = binary.readInt32() !== 0; const hasTranslateData = binary.readInt32() !== 0; // key_move const hasQuaternionData = binary.readInt32() !== 0; // key_orient const hasMorphData = binary.readInt32() !== 0; // key_morph - 16 bytes of unknown data - ignored by Arx const keyframe = { frame, flags, isMasterKeyFrame, isKeyFrame, timeFrame: binary.readInt32(), groups: [], }; if (hasTranslateData) { const translate = binary.readVector3(); if (translate.x !== 0 || translate.y !== 0 || translate.z !== 0) { keyframe.translate = translate; } } if (hasQuaternionData) { binary.skipBytes(8); // theo angle - no info on the structure - not used by Arx const quaternion = binary.readQuat(); if (quaternion.x !== 0 || quaternion.y !== 0 || quaternion.z !== 0 || quaternion.w !== 1) { keyframe.quaternion = quaternion; } } if (hasMorphData) { binary.skipBytes(16); // thea morph - no info on the structure - not used by Arx } keyframe.groups = times(() => { const isKey = binary.readInt32() !== 0; binary.readString(8); // angle - probably not a string as it holds garbage data, like "Ò?x19" when not empty - not used by Arx const quaternion = binary.readQuat(); const translate = binary.readVector3(); const zoom = binary.readVector3(); const group = { isKey, }; if (quaternion.x !== 0 || quaternion.y !== 0 || quaternion.z !== 0 || quaternion.w !== 1) { group.quaternion = quaternion; } if (translate.x !== 0 || translate.y !== 0 || translate.z !== 0) { group.translate = translate; } if (zoom.x !== 0 || zoom.y !== 0 || zoom.z !== 0) { group.zoom = zoom; } return group; }, numberOfGroups); const hasSample = binary.readInt32() !== -1; if (hasSample) { keyframe.sample = { name: binary.readString(256), sizeInBytes: binary.readInt32(), }; binary.skipBytes(keyframe.sample.sizeInBytes); // contains audio data if sizeInBytes > 0, which is already present in SFX folder of the game * - not used by Arx // * Only 4 audios are not present in the SFX folder of the released game: // - Bell.wav // - porticulis_close.wav // - porticulis_open.wav // - wooddoorclose.wav (same as sfx/door_wood_close.wav) // 2 audios are present in SFX, but sound different: // - LEVER.wav // - Bow_end.wav } binary.readInt32(); // num_sfx - always -1 - not used by Arx return keyframe; } static accumulateFrom(keyframe) { const hasTranslateData = keyframe.translate !== undefined; const hasQuaternionData = keyframe.quaternion !== undefined; const hasSample = keyframe.sample !== undefined; const buffer = new ArrayBuffer(KeyFrame.sizeOf(TEA_VERSION_OLD, hasTranslateData, hasQuaternionData, false, keyframe.groups.length, hasSample, 0)); const binary = new BinaryIO(buffer); binary.writeInt32(keyframe.frame); binary.writeInt32(keyframe.flags ?? -1); if (keyframe.isMasterKeyFrame) { binary.writeInt32(1); } else { binary.writeInt32(0); } if (keyframe.isKeyFrame) { binary.writeInt32(1); } else { binary.writeInt32(0); } // key_move if (hasTranslateData) { binary.writeInt32(1); } else { binary.writeInt32(0); } // key_orient if (hasQuaternionData) { binary.writeInt32(1); } else { binary.writeInt32(0); } binary.writeInt32(0); // key_morph - ignored by Arx binary.writeInt32(keyframe.timeFrame); if (keyframe.translate) { binary.writeVector3(keyframe.translate); } if (keyframe.quaternion) { binary.writeUint8Array(repeat(0, 8)); // theo angle - no info on the structure binary.writeQuat(keyframe.quaternion); } keyframe.groups.forEach(({ isKey, quaternion = { x: 0, y: 0, z: 0, w: 1 }, translate = { x: 0, y: 0, z: 0 }, zoom = { x: 0, y: 0, z: 0 }, }) => { if (isKey) { binary.writeInt32(1); } else { binary.writeInt32(0); } binary.writeString('', 8); // angle binary.writeQuat(quaternion); binary.writeVector3(translate); binary.writeVector3(zoom); }); if (keyframe.sample === undefined) { binary.writeInt32(-1); // num_sample } else { binary.writeInt32(1); // num_sample binary.writeString(keyframe.sample.name, 256); // sample_name binary.writeInt32(0); // sample_size } binary.writeInt32(-1); // num_sfx return buffer; } static sizeOf(version, hasTranslateData, hasQuaternionData, hasMorphData, numberOfGroups, hasSample, sampleSize = 0) { let numberOfBytes = BinaryIO.sizeOfInt32() * 8; const isNewerVersion = version >= TEA_VERSION_NEW; if (isNewerVersion) { numberOfBytes = numberOfBytes + BinaryIO.sizeOfString(256); } if (hasTranslateData) { numberOfBytes = numberOfBytes + BinaryIO.sizeOfVector3(); } if (hasQuaternionData) { numberOfBytes = numberOfBytes + 8 * BinaryIO.sizeOfUint8() + BinaryIO.sizeOfQuat(); } if (hasMorphData) { numberOfBytes = numberOfBytes + 16 * BinaryIO.sizeOfUint8(); } const sizeOfGroup = BinaryIO.sizeOfInt32() + BinaryIO.sizeOfString(8) + BinaryIO.sizeOfQuat() + 2 * BinaryIO.sizeOfVector3(); numberOfBytes = numberOfBytes + numberOfGroups * sizeOfGroup; numberOfBytes = numberOfBytes + BinaryIO.sizeOfInt32(); if (hasSample) { const sizeOfSample = BinaryIO.sizeOfString(256) + BinaryIO.sizeOfInt32() + sampleSize * BinaryIO.sizeOfUint8(); numberOfBytes = numberOfBytes + sizeOfSample; } numberOfBytes = numberOfBytes + BinaryIO.sizeOfInt32(); return numberOfBytes; } } //# sourceMappingURL=KeyFrame.js.map