2008-12-02 21:11:42 +01:00
|
|
|
#include "meta.h"
|
2024-04-08 22:29:54 +02:00
|
|
|
#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;
|
2008-12-02 21:11:42 +01:00
|
|
|
}
|
|
|
|
|
2024-04-08 22:29:54 +02:00
|
|
|
/* 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;
|
2008-12-02 21:11:42 +01:00
|
|
|
|
2024-04-08 22:29:54 +02:00
|
|
|
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;
|
2008-12-02 21:11:42 +01:00
|
|
|
|
2024-04-08 22:29:54 +02:00
|
|
|
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;
|
2008-12-02 21:11:42 +01:00
|
|
|
|
2024-04-08 22:29:54 +02:00
|
|
|
h.coding = coding_NGC_DSP;
|
|
|
|
h.layout = layout_interleave; //TODO layout flat + channel offset may be more appropriate
|
|
|
|
h.meta = meta_SSM;
|
2008-12-02 21:11:42 +01:00
|
|
|
|
2024-04-08 22:29:54 +02:00
|
|
|
h.sf = sf;
|
|
|
|
h.big_endian = true;
|
|
|
|
h.open_stream = true;
|
2008-12-02 21:11:42 +01:00
|
|
|
|
2024-04-08 22:29:54 +02:00
|
|
|
return alloc_metastream(&h);
|
2008-12-02 21:11:42 +01:00
|
|
|
}
|