UNPKG

patchwork-mapconverter

Version:

Executable wrapper for https://github.com/ChiefOfGxBxL/WC3MapTranslator

228 lines (192 loc) 7.19 kB
/* eslint-disable @typescript-eslint/strict-boolean-expressions */ import { HexBuffer } from '../HexBuffer' import { W3Buffer } from '../W3Buffer' import { type WarResult, type JsonResult } from '../CommonInterfaces' import { type Translator } from './Translator' import { type Sound } from '../data/Sound' export class SoundsTranslator implements Translator<Sound[]> { private static instance: SoundsTranslator private constructor () {} public static getInstance (): SoundsTranslator { if (this.instance == null) { this.instance = new this() } return this.instance } public static jsonToWar (sounds: Sound[]): WarResult { return this.getInstance().jsonToWar(sounds) } public static warToJson (buffer: Buffer): JsonResult<Sound[]> { return this.getInstance().warToJson(buffer) } public jsonToWar (soundsJson: Sound[]): WarResult { const outBufferToWar = new HexBuffer() /* * Header */ outBufferToWar.addInt(3) // file version outBufferToWar.addInt(soundsJson?.length || 0) // number of sounds /* * Body */ soundsJson?.forEach((sound) => { outBufferToWar.addString(sound.name) // e.g. gg_snd_HumanGlueScreenLoop1 outBufferToWar.addString(sound.path) // e.g. Sound\Ambient\HumanGlueScreenLoop1.wav // EAX effects enum (e.g. missiles, speech, etc) /* default = DefaultEAXON combat = CombatSoundsEAX drums = KotoDrumsEAX spells = SpellsEAX missiles = MissilesEAX hero speech = HeroAcksEAX doodads = DoodadsEAX */ outBufferToWar.addString(sound.eax != null ? sound.eax : 'DefaultEAXON') // defaults to "DefaultEAXON" // Flags, if present (optional) let flags = 0 if (sound.flags != null) { if (sound.flags.looping) flags |= 0x1 if (sound.flags['3dSound']) flags |= 0x2 if (sound.flags.stopOutOfRange) flags |= 0x4 if (sound.flags.music) flags |= 0x8 } outBufferToWar.addInt(flags) // Fade in and out rate (optional) outBufferToWar.addInt(sound.fadeRate != null ? sound.fadeRate.in != null ? sound.fadeRate.in : 10 : 10) // default to 10 outBufferToWar.addInt(sound.fadeRate != null ? sound.fadeRate.out != null ? sound.fadeRate.out : 10 : 10) // default to 10 // Volume (optional) outBufferToWar.addInt(sound.volume != null ? sound.volume : -1) // default to -1 (for normal volume) // Pitch (optional) outBufferToWar.addFloat(sound.pitch != null ? sound.pitch : 1.0) // default to 1.0 for normal pitch // Mystery numbers... their use is unknown by the w3x documentation, but they must be present outBufferToWar.addFloat(0) outBufferToWar.addInt(8) // or -1? // Which channel to use? Use the lookup table for more details (optional) /* 0=General 1=Unit Selection 2=Unit Acknowledgement 3=Unit Movement 4=Unit Ready 5=Combat 6=Error 7=Music 8=User Interface 9=Looping Movement 10=Looping Ambient 11=Animations 12=Constructions 13=Birth 14=Fire */ outBufferToWar.addInt(sound.channel != null ? sound.channel : 0) // default to 0 // Distance fields outBufferToWar.addFloat(sound.distance.min) outBufferToWar.addFloat(sound.distance.max) outBufferToWar.addFloat(sound.distance.cutoff) // More mystery numbers... outBufferToWar.addFloat(0) outBufferToWar.addFloat(0) outBufferToWar.addFloat(127) // or -1? outBufferToWar.addFloat(0) outBufferToWar.addFloat(0) outBufferToWar.addFloat(0) outBufferToWar.addString(sound.variableName) outBufferToWar.addString('') outBufferToWar.addString(sound.path) // More unknowns outBufferToWar.addFloat(0) outBufferToWar.addByte(0) outBufferToWar.addFloat(0) outBufferToWar.addFloat(0) outBufferToWar.addFloat(0) outBufferToWar.addByte(0) outBufferToWar.addFloat(0) }) return { errors: [], buffer: outBufferToWar.getBuffer() } } public warToJson (buffer: Buffer): JsonResult<Sound[]> { const result: Sound[] = [] const outBufferToJSON = new W3Buffer(buffer) const fileVersion = outBufferToJSON.readInt() // File version const numSounds = outBufferToJSON.readInt() // # of sounds for (let i = 0; i < numSounds; i++) { const sound: Sound = { name: '', variableName: '', path: '', eax: '', volume: 0, pitch: 0, channel: 0, flags: { looping: true, // 0x00000001=looping '3dSound': true, // 0x00000002=3D sound stopOutOfRange: true, // 0x00000004=stop when out of range music: true // 0x00000008=music}, }, fadeRate: { in: 0, out: 0 }, distance: { min: 0, max: 0, cutoff: 0 } } sound.name = outBufferToJSON.readString() sound.path = outBufferToJSON.readString() sound.eax = outBufferToJSON.readString() const flags = outBufferToJSON.readInt() sound.flags = { looping: !!(flags & 0b1), // 0x00000001=looping '3dSound': !!(flags & 0b10), // 0x00000002=3D sound stopOutOfRange: !!(flags & 0b100), // 0x00000004=stop when out of range music: !!(flags & 0b1000) // 0x00000008=music } sound.fadeRate = { in: outBufferToJSON.readInt(), out: outBufferToJSON.readInt() } sound.volume = outBufferToJSON.readInt() sound.pitch = outBufferToJSON.readFloat() // Unknown values outBufferToJSON.readFloat() outBufferToJSON.readInt() sound.channel = outBufferToJSON.readInt() sound.distance = { min: outBufferToJSON.readFloat(), max: outBufferToJSON.readFloat(), cutoff: outBufferToJSON.readFloat() } // Unknown values outBufferToJSON.readFloat() outBufferToJSON.readFloat() outBufferToJSON.readFloat() outBufferToJSON.readFloat() outBufferToJSON.readFloat() outBufferToJSON.readFloat() sound.variableName = outBufferToJSON.readString() // Unknown values outBufferToJSON.readString() outBufferToJSON.readString() outBufferToJSON.readChars(4) outBufferToJSON.readChars(1) outBufferToJSON.readChars(4) outBufferToJSON.readChars(4) outBufferToJSON.readChars(4) outBufferToJSON.readChars(1) outBufferToJSON.readChars(4) result.push(sound) } return { errors: [], json: result } } }