UNPKG

ts-ritofile

Version:

TypeScript library for reading and writing League of Legends game file formats

193 lines (154 loc) 5.29 kB
/** * WPK (Wwise Package) format implementation * Handles Wwise audio package files containing WEM audio data */ import { BinStream } from '../stream/bin-stream'; import { JsonSerializable } from '../core/json-encoder'; import * as fs from 'fs'; export class WPKWem implements JsonSerializable { public id?: number; public offset?: number; public size?: number; constructor(id?: number, offset?: number, size?: number) { this.id = id; this.offset = offset; this.size = size; } toJSON(): any { return { id: this.id, offset: this.offset, size: this.size }; } } export class WPK implements JsonSerializable { public signature?: string; public version?: number; public wems: WPKWem[] = []; constructor(signature?: string, version?: number, wems?: WPKWem[]) { this.signature = signature; this.version = version; this.wems = wems || []; } toJSON(): any { return { signature: this.signature, version: this.version, wems: this.wems }; } private stream(path?: string, mode?: string, raw?: Buffer | boolean): BinStream { if (raw !== undefined && raw !== null) { if (raw === true) { return BinStream.create(); } else if (Buffer.isBuffer(raw)) { return BinStream.fromBuffer(raw); } } if (!path) { throw new Error('Path is required when raw is not provided'); } return BinStream.fromFile(path, mode || 'rb'); } read(path?: string, raw?: Buffer | boolean): void { const bs = this.stream(path, 'rb', raw); // Read signature this.signature = bs.readString(4); if (this.signature !== 'r3d2') { throw new Error(`pyRitoFile: Failed: Read WPK ${path}: Wrong signature file: ${this.signature}`); } // Read version this.version = bs.readUInt32()[0]; // Read WEM count and offsets const wemCount = bs.readUInt32()[0]; this.wems = []; // Read WEM offsets const wemOffsets: number[] = []; for (let i = 0; i < wemCount; i++) { wemOffsets.push(bs.readUInt32()[0]); } // Filter out WEMs with offset 0 const validOffsets = wemOffsets.filter(offset => offset !== 0); // Read WEM info for each valid offset for (const offset of validOffsets) { bs.seek(offset); const wem = new WPKWem(); // Read data offset and size wem.offset = bs.readUInt32()[0]; wem.size = bs.readUInt32()[0]; // Read WEM ID string const idLength = bs.readUInt32()[0]; const idString = bs.readString(idLength - 1); // -1 for null terminator bs.readUInt8(); // Skip null terminator // Convert ID string to number (remove .wem extension) wem.id = parseInt(idString.replace('.wem', '')); this.wems.push(wem); } } write(path?: string, wemDatas?: Buffer[], raw?: Buffer | boolean): Buffer | null { const bs = this.stream(path, 'wb', raw); // Write signature and version bs.writeString('r3d2'); bs.writeUInt32(1); // Write WEM count const wemCount = this.wems.length; bs.writeUInt32(wemCount); // Reserve space for WEM offsets (will be filled later) const offsetsPosition = bs.tell(); for (let i = 0; i < wemCount; i++) { bs.writeUInt32(0); // Placeholder } // Write WEM info sections const wemOffsets: number[] = []; const wemDataOffsets: number[] = []; for (let i = 0; i < this.wems.length; i++) { const wem = this.wems[i]; // Align to 4-byte boundary bs.pad(4); wemOffsets.push(bs.tell()); // Update size from provided data if (wemDatas && wemDatas[i]) { wem.size = wemDatas[i].length; } // Reserve space for data offset (will be filled later) const dataOffsetPosition = bs.tell(); wemDataOffsets.push(dataOffsetPosition); bs.writeUInt32(0); // Placeholder for data offset // Write size bs.writeUInt32(wem.size || 0); // Write WEM ID string const wemId = `${wem.id}.wem`; bs.writeUInt32(wemId.length + 1); // +1 for null terminator bs.writeString(wemId); bs.writeUInt8(0); // Null terminator } // Write WEM data if (wemDatas) { for (let i = 0; i < wemDatas.length; i++) { const dataOffset = bs.tell(); // Update the data offset in the WEM info section const currentPos = bs.tell(); bs.seek(wemDataOffsets[i]); bs.writeUInt32(dataOffset); bs.seek(currentPos); // Write the actual WEM data bs.writeBuffer(wemDatas[i]); } } // Update WEM offsets in header const finalPos = bs.tell(); bs.seek(offsetsPosition); for (const wemOffset of wemOffsets) { bs.writeUInt32(wemOffset); } bs.seek(finalPos); if (raw !== undefined) { return bs.getBuffer(); } if (path) { fs.writeFileSync(path, bs.getBuffer()); } return null; } }