Skip to main content

siglus_assets/
nwa.rs

1//! NWA audio container support.
2//!
3//! Decoder implementation for the NWA container format.
4//!
5//! Notes:
6//! - `set_read_sample_pos` and `read_samples` treat one *frame* as one sample
7//!   even for stereo (i.e. a frame contains L+R).
8//! - For compressed NWA (`pack_mod != -1`), the current implementation supports
9//!   16-bit PCM output, matching the original decoder.
10
11use anyhow::{Context, Result, bail};
12use std::fs::File;
13use std::io::{Cursor, Read, Seek, SeekFrom};
14use std::path::Path;
15
16#[derive(Debug, Clone)]
17pub struct NwaHeader {
18    pub channels: u16,
19    pub bits_per_sample: u16,
20    pub samples_per_sec: u32,
21    pub pack_mod: i32,
22    pub zero_mod: i32,
23    pub unit_cnt: u32,
24    pub original_size: u32,
25    pub pack_size: u32,
26    pub sample_cnt: u32,
27    pub unit_sample_cnt: u32,
28    pub last_sample_cnt: u32,
29    pub last_sample_pack_size: u32,
30}
31
32impl NwaHeader {
33    pub fn is_uncompressed(&self) -> bool {
34        self.pack_mod == -1
35    }
36
37    pub fn frame_count(&self) -> u32 {
38        if self.channels == 0 {
39            return 0;
40        }
41        self.sample_cnt / (self.channels as u32)
42    }
43}
44
45#[derive(Debug)]
46struct UnitCache {
47    unit_no: i32,
48    unit_sample_cnt: u32,
49    buf: Vec<u8>,
50}
51
52impl UnitCache {
53    fn new() -> Self {
54        Self {
55            unit_no: -1,
56            unit_sample_cnt: 0,
57            buf: Vec::new(),
58        }
59    }
60}
61
62// `Send` so a reader can decode on an audio streaming thread.
63trait ReadSeek: Read + Seek + Send {}
64impl<T: Read + Seek + Send> ReadSeek for T {}
65
66/// NWA reader with random access by frame index.
67pub struct NwaReader {
68    file: Box<dyn ReadSeek>,
69    base_offset: u64,
70    header: NwaHeader,
71    unit_offsets: Vec<u32>,
72    one_sample_byte_size: u32,
73    read_sample_pos: u32,
74    cache: UnitCache,
75}
76
77impl std::fmt::Debug for NwaReader {
78    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79        f.debug_struct("NwaReader")
80            .field("base_offset", &self.base_offset)
81            .field("header", &self.header)
82            .field("unit_offsets_len", &self.unit_offsets.len())
83            .field("one_sample_byte_size", &self.one_sample_byte_size)
84            .field("read_sample_pos", &self.read_sample_pos)
85            .finish()
86    }
87}
88
89impl NwaReader {
90    pub fn open(path: impl AsRef<Path>) -> Result<Self> {
91        Self::open_with_offset(path, 0)
92    }
93
94    pub fn open_with_offset(path: impl AsRef<Path>, base_offset: u64) -> Result<Self> {
95        let mut file =
96            File::open(&path).with_context(|| format!("open NWA: {}", path.as_ref().display()))?;
97        if base_offset != 0 {
98            file.seek(SeekFrom::Start(base_offset))?;
99        }
100        let header = read_header(&mut file)?;
101
102        let mut unit_offsets = Vec::new();
103        if header.pack_mod != -1 {
104            unit_offsets.resize(header.unit_cnt as usize, 0);
105            let mut tmp = vec![0u8; header.unit_cnt as usize * 4];
106            file.read_exact(&mut tmp)?;
107            for i in 0..header.unit_cnt as usize {
108                let o = u32::from_le_bytes([
109                    tmp[i * 4],
110                    tmp[i * 4 + 1],
111                    tmp[i * 4 + 2],
112                    tmp[i * 4 + 3],
113                ]);
114                unit_offsets[i] = o;
115            }
116        }
117
118        let one_sample_byte_size = (header.bits_per_sample as u32) / 8;
119
120        Ok(Self {
121            file: Box::new(file),
122            base_offset,
123            header,
124            unit_offsets,
125            one_sample_byte_size,
126            read_sample_pos: 0,
127            cache: UnitCache::new(),
128        })
129    }
130
131    pub fn open_from_bytes(bytes: Vec<u8>) -> Result<Self> {
132        let mut file = Cursor::new(bytes);
133        let header = read_header_from_reader(&mut file)?;
134
135        let mut unit_offsets = Vec::new();
136        if header.pack_mod != -1 {
137            unit_offsets.resize(header.unit_cnt as usize, 0);
138            let mut tmp = vec![0u8; header.unit_cnt as usize * 4];
139            file.read_exact(&mut tmp)?;
140            for i in 0..header.unit_cnt as usize {
141                let o = u32::from_le_bytes([
142                    tmp[i * 4],
143                    tmp[i * 4 + 1],
144                    tmp[i * 4 + 2],
145                    tmp[i * 4 + 3],
146                ]);
147                unit_offsets[i] = o;
148            }
149        }
150
151        let one_sample_byte_size = (header.bits_per_sample as u32) / 8;
152
153        Ok(Self {
154            file: Box::new(file),
155            base_offset: 0,
156            header,
157            unit_offsets,
158            one_sample_byte_size,
159            read_sample_pos: 0,
160            cache: UnitCache::new(),
161        })
162    }
163
164    pub fn header(&self) -> &NwaHeader {
165        &self.header
166    }
167
168    /// Set current read position in frames.
169    pub fn set_read_sample_pos(&mut self, frame_pos: u32) {
170        self.read_sample_pos = frame_pos.saturating_mul(self.header.channels as u32);
171    }
172
173    /// Get current read position in frames.
174    pub fn get_read_sample_pos(&self) -> u32 {
175        let ch = self.header.channels as u32;
176        self.read_sample_pos.checked_div(ch).unwrap_or(0)
177    }
178
179    /// Read `frame_cnt` frames into an interleaved PCM byte buffer.
180    ///
181    /// Returns exactly `frames_read` frames worth of data.
182    pub fn read_samples(&mut self, frame_cnt: u32) -> Result<Vec<u8>> {
183        if self.header.channels != 1 && self.header.channels != 2 {
184            bail!("unsupported channels: {}", self.header.channels);
185        }
186        if self.header.bits_per_sample != 8 && self.header.bits_per_sample != 16 {
187            bail!(
188                "unsupported bits_per_sample: {}",
189                self.header.bits_per_sample
190            );
191        }
192
193        let need_byte_size =
194            (frame_cnt as u64) * (self.header.channels as u64) * (self.one_sample_byte_size as u64);
195        let mut out = vec![0u8; need_byte_size as usize];
196        let mut dp = 0usize;
197        let mut need = need_byte_size as i64;
198
199        while need > 0 {
200            if self.read_sample_pos >= self.header.sample_cnt {
201                break;
202            }
203
204            let copy_byte_size: usize = if self.header.pack_mod == -1 {
205                // Uncompressed: data starts immediately after the header.
206                self.read_no_pack_data(need as u64, &mut out[dp..])?
207            } else {
208                // Compressed: decode the unit if needed, then copy from cached buffer.
209                let unit_no = self.read_sample_pos / self.header.unit_sample_cnt;
210                self.read_unit(unit_no)?;
211
212                let ofs = ((self.read_sample_pos % self.header.unit_sample_cnt) as usize)
213                    * (self.one_sample_byte_size as usize);
214                let mut cb = (self.cache.unit_sample_cnt as usize
215                    * self.one_sample_byte_size as usize)
216                    .saturating_sub(ofs);
217                if cb > need as usize {
218                    cb = need as usize;
219                }
220                if cb == 0 {
221                    break;
222                }
223                out[dp..dp + cb].copy_from_slice(&self.cache.buf[ofs..ofs + cb]);
224                cb
225            };
226
227            if copy_byte_size == 0 {
228                break;
229            }
230            dp += copy_byte_size;
231            need -= copy_byte_size as i64;
232            self.read_sample_pos += (copy_byte_size as u32) / self.one_sample_byte_size;
233        }
234
235        out.truncate(dp);
236        Ok(out)
237    }
238
239    /// Convert the entire NWA stream into a WAV (RIFF PCM) byte vector.
240    pub fn to_wav_bytes(&mut self) -> Result<Vec<u8>> {
241        let total_frames = self.header.frame_count();
242        self.set_read_sample_pos(0);
243        let pcm = self.read_samples(total_frames)?;
244
245        let fmt_tag: u16 = 1; // PCM
246        let channels = self.header.channels;
247        let sample_rate = self.header.samples_per_sec;
248        let bits = self.header.bits_per_sample;
249        let block_align = (channels as u32 * (bits as u32 / 8)) as u16;
250        let byte_rate = sample_rate * (block_align as u32);
251        let data_len = pcm.len() as u32;
252
253        let mut wav = Vec::with_capacity(44 + pcm.len());
254        wav.extend_from_slice(b"RIFF");
255        wav.extend_from_slice(&(36u32 + data_len).to_le_bytes());
256        wav.extend_from_slice(b"WAVE");
257
258        wav.extend_from_slice(b"fmt ");
259        wav.extend_from_slice(&16u32.to_le_bytes());
260        wav.extend_from_slice(&fmt_tag.to_le_bytes());
261        wav.extend_from_slice(&channels.to_le_bytes());
262        wav.extend_from_slice(&sample_rate.to_le_bytes());
263        wav.extend_from_slice(&byte_rate.to_le_bytes());
264        wav.extend_from_slice(&block_align.to_le_bytes());
265        wav.extend_from_slice(&bits.to_le_bytes());
266
267        wav.extend_from_slice(b"data");
268        wav.extend_from_slice(&data_len.to_le_bytes());
269        wav.extend_from_slice(&pcm);
270        Ok(wav)
271    }
272
273    fn read_no_pack_data(&mut self, need_byte_size: u64, out: &mut [u8]) -> Result<usize> {
274        let data_ofs = (self.read_sample_pos as u64) * (self.one_sample_byte_size as u64);
275        let file_ofs = self.base_offset + 44 + data_ofs; // sizeof(NWA_HEADER_STRUCT)
276
277        self.file.seek(SeekFrom::Start(file_ofs))?;
278
279        // Clamp at end.
280        let max_data_len = (self.header.sample_cnt as u64) * (self.one_sample_byte_size as u64);
281        let remain = max_data_len.saturating_sub(data_ofs);
282        let to_read = std::cmp::min(need_byte_size, std::cmp::min(remain, out.len() as u64));
283        let buf = &mut out[..to_read as usize];
284        self.file.read_exact(buf)?;
285        Ok(to_read as usize)
286    }
287
288    fn read_unit(&mut self, unit_no: u32) -> Result<()> {
289        if self.cache.unit_no == unit_no as i32 {
290            return Ok(());
291        }
292
293        if unit_no >= self.header.unit_cnt {
294            bail!(
295                "unit_no out of range: {} >= {}",
296                unit_no,
297                self.header.unit_cnt
298            );
299        }
300
301        if self.header.pack_mod == -1 {
302            bail!("read_unit called for uncompressed NWA");
303        }
304        if self.header.bits_per_sample != 16 {
305            bail!(
306                "compressed NWA currently requires 16-bit PCM (got {})",
307                self.header.bits_per_sample
308            );
309        }
310
311        let src_ofs = self.base_offset + (self.unit_offsets[unit_no as usize] as u64);
312        self.file.seek(SeekFrom::Start(src_ofs))?;
313
314        // Every block begins with the initial predictor sample(s) followed by
315        // the packed bitstream (see the reference RealLive NWA decoder).
316        let init_byte_cnt = (self.header.channels as usize) * (self.one_sample_byte_size as usize);
317        let mut init_bytes = vec![0u8; init_byte_cnt.max(1)];
318        self.file.read_exact(&mut init_bytes)?;
319        let mut init_l = 0i32;
320        let mut init_r = 0i32;
321        if self.one_sample_byte_size == 2 {
322            init_l = i16::from_le_bytes([init_bytes[0], init_bytes[1]]) as i32;
323            if self.header.channels == 2 {
324                init_r = i16::from_le_bytes([init_bytes[2], init_bytes[3]]) as i32;
325            }
326        } else {
327            init_l = init_bytes[0] as i32;
328            if self.header.channels == 2 {
329                init_r = init_bytes[1] as i32;
330            }
331        }
332
333        let (unit_sample_cnt, src_size) = if unit_no == self.header.unit_cnt - 1 {
334            (
335                self.header.last_sample_cnt,
336                self.header.last_sample_pack_size as usize,
337            )
338        } else {
339            let a = self.unit_offsets[unit_no as usize] as u64;
340            let b = self.unit_offsets[unit_no as usize + 1] as u64;
341            (self.header.unit_sample_cnt, (b.saturating_sub(a)) as usize)
342        };
343        let mut src = vec![0u8; src_size.saturating_sub(init_byte_cnt)];
344        self.file.read_exact(&mut src)?;
345
346        self.cache.buf.resize(unit_sample_cnt as usize * 2, 0);
347        nwa_unpack_unit(
348            &src,
349            self.header.pack_mod,
350            self.header.zero_mod != 0,
351            self.header.channels,
352            init_l,
353            init_r,
354            unit_sample_cnt,
355            &mut self.cache.buf,
356        )?;
357
358        self.cache.unit_no = unit_no as i32;
359        self.cache.unit_sample_cnt = unit_sample_cnt;
360        Ok(())
361    }
362}
363
364fn read_header(file: &mut File) -> Result<NwaHeader> {
365    read_header_from_reader(file)
366}
367
368fn read_header_from_reader<R: Read + ?Sized>(file: &mut R) -> Result<NwaHeader> {
369    let mut buf = [0u8; 44];
370    file.read_exact(&mut buf)?;
371    let mut o = 0usize;
372
373    let channels = u16::from_le_bytes([buf[o], buf[o + 1]]);
374    o += 2;
375    let bits_per_sample = u16::from_le_bytes([buf[o], buf[o + 1]]);
376    o += 2;
377    let samples_per_sec = u32::from_le_bytes([buf[o], buf[o + 1], buf[o + 2], buf[o + 3]]);
378    o += 4;
379    let pack_mod = i32::from_le_bytes([buf[o], buf[o + 1], buf[o + 2], buf[o + 3]]);
380    o += 4;
381    let zero_mod = i32::from_le_bytes([buf[o], buf[o + 1], buf[o + 2], buf[o + 3]]);
382    o += 4;
383    let unit_cnt = u32::from_le_bytes([buf[o], buf[o + 1], buf[o + 2], buf[o + 3]]);
384    o += 4;
385    let original_size = u32::from_le_bytes([buf[o], buf[o + 1], buf[o + 2], buf[o + 3]]);
386    o += 4;
387    let pack_size = u32::from_le_bytes([buf[o], buf[o + 1], buf[o + 2], buf[o + 3]]);
388    o += 4;
389    let sample_cnt = u32::from_le_bytes([buf[o], buf[o + 1], buf[o + 2], buf[o + 3]]);
390    o += 4;
391    let unit_sample_cnt = u32::from_le_bytes([buf[o], buf[o + 1], buf[o + 2], buf[o + 3]]);
392    o += 4;
393    let last_sample_cnt = u32::from_le_bytes([buf[o], buf[o + 1], buf[o + 2], buf[o + 3]]);
394    o += 4;
395    let last_sample_pack_size = u32::from_le_bytes([buf[o], buf[o + 1], buf[o + 2], buf[o + 3]]);
396
397    Ok(NwaHeader {
398        channels,
399        bits_per_sample,
400        samples_per_sec,
401        pack_mod,
402        zero_mod,
403        unit_cnt,
404        original_size,
405        pack_size,
406        sample_cnt,
407        unit_sample_cnt,
408        last_sample_cnt,
409        last_sample_pack_size,
410    })
411}
412
413struct BitReaderLE<'a> {
414    buf: &'a [u8],
415    byte_pos: usize,
416    bit_pos: u8,
417}
418
419impl<'a> BitReaderLE<'a> {
420    fn new(buf: &'a [u8]) -> Self {
421        Self {
422            buf,
423            byte_pos: 0,
424            bit_pos: 0,
425        }
426    }
427
428    fn read_bits(&mut self, n: u8) -> u32 {
429        debug_assert!(n <= 8);
430        let b0 = *self.buf.get(self.byte_pos).unwrap_or(&0);
431        let b1 = *self.buf.get(self.byte_pos + 1).unwrap_or(&0);
432        let w = u16::from_le_bytes([b0, b1]);
433        let v = ((w as u32) >> (self.bit_pos as u32)) & ((1u32 << n) - 1);
434
435        self.bit_pos = self.bit_pos.wrapping_add(n);
436        if self.bit_pos >= 8 {
437            self.byte_pos += (self.bit_pos / 8) as usize;
438            self.bit_pos &= 7;
439        }
440        v
441    }
442}
443
444fn nwa_unpack_unit(
445    src: &[u8],
446    pack_mod: i32,
447    zero_mod: bool,
448    channels: u16,
449    init_l: i32,
450    init_r: i32,
451    src_smp_cnt: u32,
452    dst_le_i16: &mut [u8],
453) -> Result<()> {
454    if dst_le_i16.len() < src_smp_cnt as usize * 2 {
455        bail!("dst buffer too small");
456    }
457
458    let mut br = BitReaderLE::new(src);
459    let mut now_l: i32 = init_l;
460    let mut now_r: i32 = init_r;
461    let mut zero_cnt: u32 = 0;
462    let mono = channels <= 1;
463
464    let mut mod_map = pack_mod;
465    match mod_map {
466        0 => mod_map = 2,
467        1 => mod_map = 1,
468        2 => mod_map = 0,
469        _ => {}
470    }
471
472    match mod_map {
473        0 => unpack16(
474            &mut br,
475            zero_mod,
476            src_smp_cnt,
477            dst_le_i16,
478            3,
479            mono,
480            &mut now_l,
481            &mut now_r,
482            &mut zero_cnt,
483        ),
484        1 => unpack16(
485            &mut br,
486            zero_mod,
487            src_smp_cnt,
488            dst_le_i16,
489            4,
490            mono,
491            &mut now_l,
492            &mut now_r,
493            &mut zero_cnt,
494        ),
495        2 => unpack16(
496            &mut br,
497            zero_mod,
498            src_smp_cnt,
499            dst_le_i16,
500            5,
501            mono,
502            &mut now_l,
503            &mut now_r,
504            &mut zero_cnt,
505        ),
506        3 => unpack16(
507            &mut br,
508            zero_mod,
509            src_smp_cnt,
510            dst_le_i16,
511            6,
512            mono,
513            &mut now_l,
514            &mut now_r,
515            &mut zero_cnt,
516        ),
517        4 => unpack16(
518            &mut br,
519            zero_mod,
520            src_smp_cnt,
521            dst_le_i16,
522            7,
523            mono,
524            &mut now_l,
525            &mut now_r,
526            &mut zero_cnt,
527        ),
528        5 => unpack16(
529            &mut br,
530            zero_mod,
531            src_smp_cnt,
532            dst_le_i16,
533            8,
534            mono,
535            &mut now_l,
536            &mut now_r,
537            &mut zero_cnt,
538        ),
539        _ => bail!("invalid pack_mod: {}", pack_mod),
540    }
541}
542
543fn get_zero_count(br: &mut BitReaderLE) -> u32 {
544    let mut zero_cnt = br.read_bits(1);
545    if zero_cnt == 1 {
546        zero_cnt = br.read_bits(2);
547        if zero_cnt == 3 {
548            zero_cnt = br.read_bits(8);
549        }
550    }
551    zero_cnt
552}
553
554fn apply_delta(br: &mut BitReaderLE, mod_n: u8, data_n: u8, nowsmp: &mut i32) {
555    let (bits, sign_mask, shift): (u8, u32, u8) = match (mod_n, data_n) {
556        (3, 1) => (3, 0x04, 5),
557        (3, 2) => (3, 0x04, 6),
558        (3, 3) => (3, 0x04, 7),
559        (3, 4) => (3, 0x04, 8),
560        (3, 5) => (3, 0x04, 9),
561        (3, 6) => (3, 0x04, 10),
562        (3, 7) => (6, 0x20, 11),
563
564        (4, 1) => (4, 0x08, 4),
565        (4, 2) => (4, 0x08, 5),
566        (4, 3) => (4, 0x08, 6),
567        (4, 4) => (4, 0x08, 7),
568        (4, 5) => (4, 0x08, 8),
569        (4, 6) => (4, 0x08, 9),
570        (4, 7) => (7, 0x40, 10),
571
572        (5, 1) => (5, 0x10, 3),
573        (5, 2) => (5, 0x10, 4),
574        (5, 3) => (5, 0x10, 5),
575        (5, 4) => (5, 0x10, 6),
576        (5, 5) => (5, 0x10, 7),
577        (5, 6) => (5, 0x10, 8),
578        (5, 7) => (8, 0x80, 9),
579
580        (6, 1) => (6, 0x20, 2),
581        (6, 2) => (6, 0x20, 3),
582        (6, 3) => (6, 0x20, 4),
583        (6, 4) => (6, 0x20, 5),
584        (6, 5) => (6, 0x20, 6),
585        (6, 6) => (6, 0x20, 7),
586        (6, 7) => (8, 0x80, 9),
587
588        (7, 1) => (7, 0x40, 2),
589        (7, 2) => (7, 0x40, 3),
590        (7, 3) => (7, 0x40, 4),
591        (7, 4) => (7, 0x40, 5),
592        (7, 5) => (7, 0x40, 6),
593        (7, 6) => (7, 0x40, 7),
594        (7, 7) => (8, 0x80, 9),
595
596        (8, 1) => (8, 0x80, 2),
597        (8, 2) => (8, 0x80, 3),
598        (8, 3) => (8, 0x80, 4),
599        (8, 4) => (8, 0x80, 5),
600        (8, 5) => (8, 0x80, 6),
601        (8, 6) => (8, 0x80, 7),
602        (8, 7) => (8, 0x80, 9),
603        _ => return,
604    };
605
606    let mut dat_code = br.read_bits(bits);
607    if (dat_code & sign_mask) != 0 {
608        dat_code &= !sign_mask;
609        *nowsmp -= (dat_code as i32) << shift;
610    } else {
611        *nowsmp += (dat_code as i32) << shift;
612    }
613}
614
615fn unpack16(
616    br: &mut BitReaderLE,
617    zero_mod: bool,
618    src_smp_cnt: u32,
619    dst_le_i16: &mut [u8],
620    mod_n: u8,
621    mono: bool,
622    now_l: &mut i32,
623    now_r: &mut i32,
624    zero_cnt: &mut u32,
625) -> Result<()> {
626    for i in 0..src_smp_cnt {
627        // Mono streams use a single predictor; alternating accumulators is
628        // only valid for interleaved stereo data.
629        let nowsmp_ref: &mut i32 = if mono || (i & 1) == 0 { now_l } else { now_r };
630
631        if *zero_cnt != 0 {
632            *zero_cnt -= 1;
633        } else {
634            let mod_code = br.read_bits(3) as u8;
635            match mod_code {
636                0 => {
637                    if zero_mod {
638                        *zero_cnt = get_zero_count(br);
639                    }
640                }
641                1 => apply_delta(br, mod_n, 1, nowsmp_ref),
642                2 => apply_delta(br, mod_n, 2, nowsmp_ref),
643                3 => apply_delta(br, mod_n, 3, nowsmp_ref),
644                4 => apply_delta(br, mod_n, 4, nowsmp_ref),
645                5 => apply_delta(br, mod_n, 5, nowsmp_ref),
646                6 => apply_delta(br, mod_n, 6, nowsmp_ref),
647                7 => {
648                    let b = br.read_bits(1);
649                    if b == 0 {
650                        apply_delta(br, mod_n, 7, nowsmp_ref);
651                    } else {
652                        *nowsmp_ref = 0;
653                    }
654                }
655                _ => {}
656            }
657        }
658
659        // Write i16 little-endian.
660        let s = (*nowsmp_ref as i16).to_le_bytes();
661        let o = (i as usize) * 2;
662        dst_le_i16[o] = s[0];
663        dst_le_i16[o + 1] = s[1];
664    }
665    Ok(())
666}