vgmstream/src/meta/ngc_ssm.c

86 lines
3.1 KiB
C
Raw Normal View History

#include "meta.h"
#include "../util/meta_utils.h"
#include "../coding/coding.h"
/* .SSM - from Hal Laboratory games [Smash Bros Melee! (GC), Konjiki no Gashbell: YnTB Full Power (GC), Kururin Squash! (GC)] */
VGMSTREAM* init_vgmstream_ssm(STREAMFILE* sf) {
meta_header_t h = {0};
/* checks */
h.head_size = read_u32be(0x00,sf);
h.data_size = read_u32be(0x04,sf);
h.total_subsongs = read_s32be(0x08,sf);
int file_id = read_u32be(0x0c,sf);
/* extra tests + arbitrary maxes since no good header ID and values aren't exact */
if (0x10 + h.head_size + h.data_size > get_streamfile_size(sf))
return NULL;
if (h.head_size < h.total_subsongs * 0x48 || h.total_subsongs <= 0 || h.total_subsongs > 0x1000 || file_id > 0x1000)
return NULL;
if (!check_extensions(sf, "ssm"))
return NULL;
h.target_subsong = sf->stream_index;
if (!check_subsongs(&h.target_subsong, h.total_subsongs))
return NULL;
/* sometimes there is padding after head_size, DSP's start ps matches this */
h.data_offset = get_streamfile_size(sf) - h.data_size; //0x10 + h.head_size;
uint32_t offset = 0x10;
for (int i = 0; i < h.total_subsongs; i++) {
int channels = read_u32be(offset + 0x00,sf);
if (channels < 1 || channels > 2) return NULL;
if (i + 1 == h.target_subsong) {
h.channels = read_u32be(offset + 0x00,sf);
h.sample_rate = read_s32be(offset + 0x04,sf);
/* use first channel as base */
h.loop_flag = read_s16be(offset + 0x08,sf);
h.loop_start = read_u32be(offset + 0x0c,sf);
h.chan_size = read_u32be(offset + 0x10,sf);
h.stream_offset = read_s32be(offset + 0x14,sf);
h.coefs_offset = offset + 0x18;
h.coefs_spacing = 0x40;
h.hists_offset = h.coefs_offset + 0x24;
h.hists_spacing = h.coefs_spacing;
if (h.channels >= 2) {
h.interleave = read_s32be(offset + 0x54,sf); /* use 2nd channel offset as interleave */
}
break;
}
offset += 0x08 + channels * 0x40;
}
/* oddly enough values are in absolute nibbles within the stream, adjust them here
* rarely may even point to a nibble after the header one (ex. 0x1005), but it's adjusted to 0x00 here */
h.loop_start -= h.stream_offset;
h.chan_size -= h.stream_offset;
if (h.interleave)
h.interleave -= h.stream_offset;
h.loop_start = dsp_nibbles_to_samples(h.loop_start);
h.loop_end = dsp_nibbles_to_samples(h.chan_size);
h.num_samples = h.loop_end;
h.stream_offset = (h.stream_offset / 0x10 * 0x08) + h.data_offset;
h.stream_size = (h.chan_size / 0x10 * 0x08 + (h.chan_size % 0x08 ? 0x08 : 0x00)) * h.channels;
h.interleave = h.interleave / 0x10 * 0x08;
h.coding = coding_NGC_DSP;
h.layout = layout_interleave; //TODO layout flat + channel offset may be more appropriate
h.meta = meta_SSM;
h.sf = sf;
h.big_endian = true;
h.open_stream = true;
return alloc_metastream(&h);
}