Fix .ssm subsongs [Kururin Squash! (GC)]

This commit is contained in:
bnnm 2024-04-08 22:29:54 +02:00
parent 4c340d6956
commit 924b381691
9 changed files with 195 additions and 89 deletions

View File

@ -1125,7 +1125,7 @@ static const meta_info meta_info_list[] = {
{meta_ISH_ISD, "ISH+ISD DSP Header"},
{meta_GSND, "Tecmo GSND Header"},
{meta_YDSP, "Yuke's YDSP Header"},
{meta_NGC_SSM, "SSM DSP Header"},
{meta_SSM, "HAL Laboratory .SSM Header"},
{meta_PS2_JOE, "Asobo Studio .JOE header"},
{meta_VGS, "Guitar Hero VGS Header"},
{meta_DCS_WAV, "In Utero DCS+WAV header"},

View File

@ -185,6 +185,7 @@
<ClInclude Include="util\layout_utils.h" />
<ClInclude Include="util\log.h" />
<ClInclude Include="util\m2_psb.h" />
<ClInclude Include="util\meta_utils.h" />
<ClInclude Include="util\miniz.h" />
<ClInclude Include="util\paths.h" />
<ClInclude Include="util\reader_get.h" />
@ -765,6 +766,7 @@
<ClCompile Include="util\layout_utils.c" />
<ClCompile Include="util\log.c" />
<ClCompile Include="util\m2_psb.c" />
<ClCompile Include="util\meta_utils.c" />
<ClCompile Include="util\miniz.c" />
<ClCompile Include="util\paths.c" />
<ClCompile Include="util\reader.c" />

View File

@ -380,6 +380,9 @@
<ClInclude Include="util\m2_psb.h">
<Filter>util\Header Files</Filter>
</ClInclude>
<ClInclude Include="util\meta_utils.h">
<Filter>util\Header Files</Filter>
</ClInclude>
<ClInclude Include="util\miniz.h">
<Filter>util\Header Files</Filter>
</ClInclude>
@ -2116,6 +2119,9 @@
<ClCompile Include="util\m2_psb.c">
<Filter>util\Source Files</Filter>
</ClCompile>
<ClCompile Include="util\meta_utils.c">
<Filter>util\Source Files</Filter>
</ClCompile>
<ClCompile Include="util\miniz.c">
<Filter>util\Source Files</Filter>
</ClCompile>

View File

@ -330,7 +330,7 @@ VGMSTREAM * init_vgmstream_ydsp(STREAMFILE * streamFile);
VGMSTREAM* init_vgmstream_gsnd(STREAMFILE* sf);
VGMSTREAM * init_vgmstream_ngc_ssm(STREAMFILE * streamFile);
VGMSTREAM* init_vgmstream_ssm(STREAMFILE* sf);
VGMSTREAM * init_vgmstream_ps2_joe(STREAMFILE * streamFile);

View File

@ -1,96 +1,85 @@
#include "meta.h"
#include "../util.h"
/* SSM (Golden Gashbell Full Power GC) */
VGMSTREAM * init_vgmstream_ngc_ssm(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset;
int loop_flag;
int channel_count;
int coef1_start;
int coef2_start;
int second_channel_start;
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("ssm",filename_extension(filename)))
goto fail;
/* check header */
#if 0
if (read_32bitBE(0x00,streamFile) != 0x0)
goto fail;
#endif
loop_flag = (uint32_t)read_16bitBE(0x18,streamFile);
channel_count = read_32bitBE(0x10,streamFile);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
start_offset = read_32bitBE(0x0,streamFile);
vgmstream->channels = channel_count;
vgmstream->sample_rate = read_32bitBE(0x14,streamFile);
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->num_samples = read_32bitBE(0x04,streamFile)*14/8/channel_count;
if (loop_flag) {
vgmstream->loop_start_sample = read_32bitBE(0x24,streamFile)*14/8/channel_count;
vgmstream->loop_end_sample = read_32bitBE(0x20,streamFile)*14/8/channel_count;
}
vgmstream->layout_type = layout_none;
vgmstream->meta_type = meta_NGC_SSM;
/* Retrieveing the coef tables and the start of the second channel*/
coef1_start = 0x28;
coef2_start = 0x68;
second_channel_start = (read_32bitBE(0x04,streamFile)/2)+start_offset;
{
int i;
for (i=0;i<16;i++)
vgmstream->ch[0].adpcm_coef[i] = read_16bitBE(coef1_start+i*2,streamFile);
if (channel_count == 2) {
for (i=0;i<16;i++)
vgmstream->ch[1].adpcm_coef[i] = read_16bitBE(coef2_start+i*2,streamFile);
}
}
#include "../util/meta_utils.h"
#include "../coding/coding.h"
/* open the file for reading */
{
int i;
STREAMFILE * file;
file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!file) goto fail;
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = file;
/* .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};
/* The first channel */
vgmstream->ch[0].channel_start_offset=
vgmstream->ch[0].offset=start_offset;
/* The second channel */
if (channel_count == 2) {
vgmstream->ch[1].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
/* 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);
if (!vgmstream->ch[1].streamfile) goto fail;
/* 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;
vgmstream->ch[1].channel_start_offset=
vgmstream->ch[1].offset=second_channel_start;
}
}
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;
}
return vgmstream;
/* 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;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
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);
}

48
src/util/meta_utils.c Normal file
View File

@ -0,0 +1,48 @@
#include "../vgmstream.h"
#include "meta_utils.h"
/* Allocate memory and setup a VGMSTREAM */
VGMSTREAM* alloc_metastream(meta_header_t* h) {
if (h->sample_rate <= 0 || h->sample_rate > VGMSTREAM_MAX_SAMPLE_RATE)
return NULL;
if (h->num_samples <= 0 || h->num_samples > VGMSTREAM_MAX_NUM_SAMPLES)
return NULL;
VGMSTREAM* vgmstream = allocate_vgmstream(h->channels, h->loop_flag);
if (!vgmstream) return NULL;
//vgmstream->channels = h->channels;
vgmstream->sample_rate = h->sample_rate;
vgmstream->num_samples = h->num_samples;
vgmstream->loop_start_sample = h->loop_start;
vgmstream->loop_end_sample = h->loop_end;
vgmstream->coding_type = h->coding;
vgmstream->layout_type = h->layout;
vgmstream->meta_type = h->meta;
vgmstream->num_streams = h->total_subsongs;
vgmstream->stream_size = h->stream_size;
vgmstream->interleave_block_size = h->interleave;
if (h->coding == coding_NGC_DSP && (h->sf || h->sf_head)) {
if (h->coefs_offset || h->coefs_spacing)
dsp_read_coefs(vgmstream, h->sf ? h->sf : h->sf_head, h->coefs_offset, h->coefs_spacing, h->big_endian);
if (h->hists_offset || h->hists_spacing)
dsp_read_hist (vgmstream, h->sf ? h->sf : h->sf_head, h->hists_offset, h->hists_spacing, h->big_endian);
}
if (h->open_stream) {
if (!vgmstream_open_stream(vgmstream, h->sf ? h->sf : h->sf_head, h->stream_offset))
goto fail;
}
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

61
src/util/meta_utils.h Normal file
View File

@ -0,0 +1,61 @@
#ifndef _META_UTILS_H
#define _META_UTILS_H
#include "../streamtypes.h"
#include "reader_get.h"
#include "reader_put.h"
#include "../coding/coding.h"
/* Helper struct for common numbers (no need to use all), to use with helper functions.
* Preferably declare after validating header ID as it's faster (by a minuscule amount). */
typedef struct {
/* should be set */
int channels;
int sample_rate;
int32_t num_samples;
/* optional info */
bool loop_flag;
int32_t loop_start;
int32_t loop_end;
int target_subsong;
int total_subsongs;
int32_t interleave;
/* common helpers */
uint32_t stream_offset; /* where current stream starts */
uint32_t stream_size; /* current stream size */
uint32_t data_offset; /* where data (first stream) starts */
uint32_t data_size; /* data for all streams */
uint32_t head_size; /* size of some header part */
uint32_t chan_offset;
uint32_t chan_size;
uint32_t coefs_offset;
uint32_t coefs_spacing;
uint32_t hists_offset;
uint32_t hists_spacing;
/* optional but can be used for some actions */
bool big_endian;
coding_t coding;
layout_t layout;
meta_t meta;
/* only sf_head is used to read coefs and such */
STREAMFILE* sf;
STREAMFILE* sf_head;
STREAMFILE* sf_body;
bool open_stream;
} meta_header_t;
VGMSTREAM* alloc_metastream(meta_header_t* h);
/* checks max subsongs and setups target */
//bool check_subsongs(int* target_subsong, int total_subsongs);
#endif

View File

@ -142,7 +142,7 @@ init_vgmstream_t init_vgmstream_functions[] = {
init_vgmstream_ish_isd,
init_vgmstream_gsnd,
init_vgmstream_ydsp,
init_vgmstream_ngc_ssm,
init_vgmstream_ssm,
init_vgmstream_ps2_joe,
init_vgmstream_vgs,
init_vgmstream_dcs_wav,

View File

@ -364,7 +364,7 @@ typedef enum {
meta_FFCC_STR, /* Final Fantasy: Crystal Chronicles */
meta_UBI_JADE, /* Beyond Good & Evil, Rayman Raving Rabbids */
meta_GCA, /* Metal Slug Anthology */
meta_NGC_SSM, /* Golden Gashbell Full Power */
meta_SSM,
meta_PS2_JOE, /* Wall-E / Pixar games */
meta_YMF,
meta_SADL,