mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-24 23:10:10 +01:00
Merge pull request #838 from bnnm/exst-ogv
- Add EXTS .sts_cp3+int_cp3 [Shadow of the Colossus (PS3)] - Fix some .ogg looping from Astronauts games - Add encrypted .bgm [Nanami to Konomi no Oshiete ABC (PC), Oyatsu no Jikan (PC)] - Improve bitrate calculations for complex .txtp - Add .ogv [Bloody Rondo (PC)] - Add .wav/lwav extension for .gcub [Sega Soccer Slam (GC)]
This commit is contained in:
commit
37cc12295c
@ -369,6 +369,7 @@ static const char* extension_list[] = {
|
||||
|
||||
//"ogg", //common
|
||||
"ogl",
|
||||
"ogv",
|
||||
"oma", //FFmpeg/not parsed (ATRAC3/ATRAC3PLUS/MP3/LPCM/WMA)
|
||||
"omu",
|
||||
//"opus", //common
|
||||
@ -499,6 +500,7 @@ static const char* extension_list[] = {
|
||||
"stream",
|
||||
"strm",
|
||||
"sts",
|
||||
"sts_cp3",
|
||||
"stx",
|
||||
"svag",
|
||||
"svs",
|
||||
@ -930,7 +932,7 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_RAW_INT, "PS2 .int raw header"},
|
||||
{meta_PS2_OMU, "Alter Echo OMU Header"},
|
||||
{meta_DSP_STM, "Intelligent Systems STM header"},
|
||||
{meta_PS2_EXST, "Sony EXST header"},
|
||||
{meta_EXST, "Sony EXST header"},
|
||||
{meta_SVAG_KCET, "Konami SVAG header"},
|
||||
{meta_PS_HEADERLESS, "Headerless PS-ADPCM raw header"},
|
||||
{meta_MIB_MIH, "Sony MultiStream MIH+MIB header"},
|
||||
@ -1093,7 +1095,7 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_2DX9, "beatmania IIDX 2DX9 header"},
|
||||
{meta_DSP_YGO, "Konami custom DSP Header"},
|
||||
{meta_PS2_VGV, "Rune: Viking Warlord VGV Header"},
|
||||
{meta_NGC_GCUB, "GCub Header"},
|
||||
{meta_GCUB, "Sega GCub header"},
|
||||
{meta_NGC_SCK_DSP, "The Scorpion King SCK Header"},
|
||||
{meta_CAFF, "Apple Core Audio Format File header"},
|
||||
{meta_PC_MXST, "Lego Island MxSt Header"},
|
||||
@ -1347,6 +1349,7 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_TAC, "tri-Ace Codec header"},
|
||||
{meta_IDSP_TOSE, "TOSE .IDSP header"},
|
||||
{meta_DSP_KWA, "Kuju London .KWA header"},
|
||||
{meta_OGV_3RDEYE, "3rdEye .OGV header"},
|
||||
};
|
||||
|
||||
void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t out_size) {
|
||||
|
@ -292,6 +292,10 @@
|
||||
RelativePath=".\meta\ea_schl_streamfile.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\encrypted_bgm_streamfile.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\fsb_encrypted_streamfile.h"
|
||||
>
|
||||
@ -1063,7 +1067,7 @@
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\ngc_gcub.c"
|
||||
RelativePath=".\meta\gcub.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
@ -1150,6 +1154,10 @@
|
||||
RelativePath=".\meta\ogl.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\ogv_3rdeye.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\omu.c"
|
||||
>
|
||||
@ -1271,7 +1279,7 @@
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\ps2_exst.c"
|
||||
RelativePath=".\meta\exst.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
|
@ -115,6 +115,7 @@
|
||||
<ClInclude Include="meta\ea_eaac_streamfile.h" />
|
||||
<ClInclude Include="meta\ea_eaac_opus_streamfile.h" />
|
||||
<ClInclude Include="meta\ea_schl_streamfile.h" />
|
||||
<ClInclude Include="meta\encrypted_bgm_streamfile.h" />
|
||||
<ClInclude Include="meta\fsb_encrypted_streamfile.h" />
|
||||
<ClInclude Include="meta\fsb_interleave_streamfile.h" />
|
||||
<ClInclude Include="meta\fsb5_streamfile.h" />
|
||||
@ -400,7 +401,7 @@
|
||||
<ClCompile Include="meta\ngc_dsp_std.c" />
|
||||
<ClCompile Include="meta\ngc_dsp_ygo.c" />
|
||||
<ClCompile Include="meta\ngc_ffcc_str.c" />
|
||||
<ClCompile Include="meta\ngc_gcub.c" />
|
||||
<ClCompile Include="meta\gcub.c" />
|
||||
<ClCompile Include="meta\ngc_lps.c" />
|
||||
<ClCompile Include="meta\ngc_nst_dsp.c" />
|
||||
<ClCompile Include="meta\ngc_pdt.c" />
|
||||
@ -420,6 +421,7 @@
|
||||
<ClCompile Include="meta\ogg_opus.c" />
|
||||
<ClCompile Include="meta\ogg_vorbis.c" />
|
||||
<ClCompile Include="meta\ogl.c" />
|
||||
<ClCompile Include="meta\ogv_3rdeye.c" />
|
||||
<ClCompile Include="meta\omu.c" />
|
||||
<ClCompile Include="meta\otm.c" />
|
||||
<ClCompile Include="meta\p3d.c" />
|
||||
@ -445,7 +447,7 @@
|
||||
<ClCompile Include="meta\ps2_ccc.c" />
|
||||
<ClCompile Include="meta\ps2_dxh.c" />
|
||||
<ClCompile Include="meta\ps2_enth.c" />
|
||||
<ClCompile Include="meta\ps2_exst.c" />
|
||||
<ClCompile Include="meta\exst.c" />
|
||||
<ClCompile Include="meta\ps2_filp.c" />
|
||||
<ClCompile Include="meta\ps2_gbts.c" />
|
||||
<ClCompile Include="meta\ps2_gcm.c" />
|
||||
|
@ -110,6 +110,9 @@
|
||||
<ClInclude Include="meta\ea_schl_streamfile.h">
|
||||
<Filter>meta\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="meta\encrypted_bgm_streamfile">
|
||||
<Filter>meta\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="meta\fsb_encrypted_streamfile.h">
|
||||
<Filter>meta\Header Files</Filter>
|
||||
</ClInclude>
|
||||
@ -697,7 +700,7 @@
|
||||
<ClCompile Include="meta\ngc_ffcc_str.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ngc_gcub.c">
|
||||
<ClCompile Include="meta\gcub.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ngc_lps.c">
|
||||
@ -757,6 +760,9 @@
|
||||
<ClCompile Include="meta\ogl.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ogv_3rdeye.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\omu.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -832,7 +838,7 @@
|
||||
<ClCompile Include="meta\ps2_enth.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ps2_exst.c">
|
||||
<ClCompile Include="meta\exst.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ps2_filp.c">
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "ogg_vorbis_streamfile.h"
|
||||
#include "encrypted_bgm_streamfile.h"
|
||||
|
||||
//todo fuse ogg encryptions and use generic names
|
||||
|
||||
@ -80,6 +81,32 @@ VGMSTREAM* init_vgmstream_encrypted(STREAMFILE* sf) {
|
||||
return vgmstream;
|
||||
}
|
||||
|
||||
if (check_extensions(sf,"bgm")) {
|
||||
uint8_t keybuf[0x100];
|
||||
size_t key_size;
|
||||
off_t start;
|
||||
|
||||
/* Studio Ring games [Nanami to Konomi no Oshiete ABC (PC), Oyatsu no Jikan (PC)] */
|
||||
if (id != get_id32be("RIFF"))
|
||||
goto fail;
|
||||
|
||||
/* Standard RIFF xor'd past "data", sometimes including extra chunks like JUNK or smpl.
|
||||
* If .bgm is added to riff.c this needs to be reworked so detection goes first, or bgm+bgmkey is
|
||||
* rejected in riff.c (most files are rejected due to the xor'd extra chunks though). */
|
||||
key_size = read_key_file(keybuf, sizeof(keybuf), sf);
|
||||
if (key_size <= 0) goto fail;
|
||||
|
||||
if (!find_chunk_le(sf, get_id32be("data"), 0x0c, 0, &start, NULL))
|
||||
goto fail;
|
||||
|
||||
temp_sf = setup_bgm_streamfile(sf, start, keybuf, key_size);
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_riff(temp_sf);
|
||||
close_streamfile(temp_sf);
|
||||
return vgmstream;
|
||||
}
|
||||
|
||||
|
||||
fail:
|
||||
return NULL;
|
||||
|
52
src/meta/encrypted_bgm_streamfile.h
Normal file
52
src/meta/encrypted_bgm_streamfile.h
Normal file
@ -0,0 +1,52 @@
|
||||
#ifndef _BGM_STREAMFILE_H_
|
||||
#define _BGM_STREAMFILE_H_
|
||||
#include "../streamfile.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint8_t key[0x100];
|
||||
size_t key_len;
|
||||
off_t start;
|
||||
} bgm_io_data;
|
||||
|
||||
static size_t bgm_io_read(STREAMFILE *sf, uint8_t *dest, off_t offset, size_t length, bgm_io_data* data) {
|
||||
int i, begin, pos;
|
||||
size_t bytes = read_streamfile(dest, offset, length, sf);
|
||||
|
||||
/* decrypt data (xor) */
|
||||
if (offset + length > data->start) {
|
||||
if (offset < data->start) {
|
||||
begin = data->start - offset;
|
||||
pos = 0;
|
||||
}
|
||||
else {
|
||||
begin = 0;
|
||||
pos = offset - data->start;
|
||||
}
|
||||
|
||||
for (i = begin; i < bytes; i++) {
|
||||
dest[i] ^= data->key[(pos++) % data->key_len];
|
||||
}
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/* decrypts BGM stream */
|
||||
static STREAMFILE* setup_bgm_streamfile(STREAMFILE *sf, off_t start, uint8_t* key, int key_len) {
|
||||
STREAMFILE *new_sf = NULL;
|
||||
bgm_io_data io_data = {0};
|
||||
|
||||
io_data.start = start;
|
||||
io_data.key_len = key_len;
|
||||
if (key_len > sizeof(io_data.key))
|
||||
return NULL;
|
||||
memcpy(io_data.key, key, key_len);
|
||||
|
||||
new_sf = open_wrap_streamfile(sf);
|
||||
new_sf = open_io_streamfile_f(new_sf, &io_data, sizeof(bgm_io_data), bgm_io_read, NULL);
|
||||
new_sf = open_fakename_streamfile_f(new_sf, NULL, "wav");
|
||||
return new_sf;
|
||||
}
|
||||
|
||||
#endif /* _BGM_STREAMFILE_H_ */
|
103
src/meta/exst.c
Normal file
103
src/meta/exst.c
Normal file
@ -0,0 +1,103 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* EXST - from Sony games [Shadow of the Colossus (PS2), Gacha Mecha Stadium Saru Battle (PS2)] */
|
||||
VGMSTREAM* init_vgmstream_exst(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* sf_body = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channels, sample_rate;
|
||||
int32_t interleave, num_samples, loop_start, loop_end;
|
||||
size_t data_size;
|
||||
int is_cp3 = 0;
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .sts+int: standard [Shadow of the Colossus (PS2)] (some fake .sts have manually joined header+body)
|
||||
* .x: header+body [Ape Escape 3 (PS2)]
|
||||
* .sts_cp3+int_cp3: Shadow of the Colossus (PS3) */
|
||||
if (!check_extensions(sf, "sts,sts_cp3,x"))
|
||||
goto fail;
|
||||
if (!is_id32be(0x00,sf, "EXST"))
|
||||
goto fail;
|
||||
|
||||
/* also detectable since PS2 .sts uses blocks and PS3 offsets */
|
||||
is_cp3 = check_extensions(sf, "sts_cp3");
|
||||
|
||||
if (is_cp3)
|
||||
sf_body = open_streamfile_by_ext(sf,"int_cp3");
|
||||
else
|
||||
sf_body = open_streamfile_by_ext(sf,"int");
|
||||
|
||||
if (sf_body) {
|
||||
/* separate header+body (header is 0x78) */
|
||||
start_offset = 0x00;
|
||||
data_size = get_streamfile_size(sf_body);
|
||||
}
|
||||
else {
|
||||
/* joint header+body (.x and assumed .sts) */
|
||||
start_offset = 0x78;
|
||||
data_size = get_streamfile_size(sf);
|
||||
/* Gacharoku 2 has header+data but padded header (ELF has pointers + size to SOUND.PCK, and
|
||||
* treats them as single files, no extension but there are Sg2ExStAdpcm* calls in the ELF) */
|
||||
if ((data_size % 0x10) == 0)
|
||||
start_offset = 0x80;
|
||||
|
||||
if (data_size <= start_offset)
|
||||
goto fail;
|
||||
|
||||
data_size = data_size - start_offset;
|
||||
}
|
||||
|
||||
channels = read_u16le(0x06,sf);
|
||||
sample_rate = read_u32le(0x08,sf);
|
||||
loop_flag = read_u32le(0x0C,sf);
|
||||
loop_start = read_u32le(0x10,sf);
|
||||
loop_end = read_u32le(0x14,sf);
|
||||
/* 0x18: 0x24 config per channel? (volume+panning+etc?) */
|
||||
/* rest is padding up to 0x78 */
|
||||
|
||||
if (!is_cp3) {
|
||||
interleave = 0x400;
|
||||
loop_flag = (loop_flag == 1);
|
||||
|
||||
num_samples = ps_bytes_to_samples(data_size, channels); /* same or very close to loop end */
|
||||
loop_start = ps_bytes_to_samples(loop_start * interleave * channels, channels); /* blocks */
|
||||
loop_end = ps_bytes_to_samples(loop_end * interleave * channels, channels); /* blocks */
|
||||
}
|
||||
else {
|
||||
interleave = 0x10;
|
||||
loop_flag = !(loop_start == 0 && loop_end == data_size); /* flag always set even for jingles */
|
||||
|
||||
num_samples = ps_bytes_to_samples(data_size, channels); /* not same as loop end */
|
||||
loop_start = ps_bytes_to_samples(loop_start, channels); /* offset */
|
||||
loop_end = ps_bytes_to_samples(loop_end, channels); /* offset */
|
||||
}
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_EXST;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = interleave;
|
||||
|
||||
vgmstream->num_samples = num_samples;
|
||||
vgmstream->loop_start_sample = loop_start;
|
||||
vgmstream->loop_end_sample = loop_end;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf_body ? sf_body : sf, start_offset))
|
||||
goto fail;
|
||||
close_streamfile(sf_body);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(sf_body);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
53
src/meta/gcub.c
Normal file
53
src/meta/gcub.c
Normal file
@ -0,0 +1,53 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* GCub - found in Sega Soccer Slam (GC) */
|
||||
VGMSTREAM* init_vgmstream_gcub(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int channels, loop_flag, sample_rate;
|
||||
size_t data_size;
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .wav: extension found in bigfile
|
||||
* .gcub: header id */
|
||||
if (!check_extensions(sf, "wav,lwav,gcub"))
|
||||
goto fail;
|
||||
if (!is_id32be(0x00,sf, "GCub"))
|
||||
goto fail;
|
||||
|
||||
loop_flag = 0;
|
||||
channels = read_u32be(0x04,sf);
|
||||
sample_rate = read_u32be(0x08,sf);
|
||||
data_size = read_u32be(0x0c,sf);
|
||||
|
||||
if (is_id32be(0x60,sf, "GCxx")) /* seen in sfx */
|
||||
start_offset = 0x88;
|
||||
else
|
||||
start_offset = 0x60;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_GCUB;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = dsp_bytes_to_samples(data_size, channels);
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x8000;
|
||||
|
||||
dsp_read_coefs_be(vgmstream, sf, 0x10, 0x20);
|
||||
/* 0x50: initial ps for ch1/2 (16b) */
|
||||
/* 0x54: hist? (always blank) */
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
@ -84,7 +84,7 @@ VGMSTREAM * init_vgmstream_ps2_rxw(STREAMFILE *streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_raw_int(STREAMFILE *streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ps2_exst(STREAMFILE *streamFile);
|
||||
VGMSTREAM * init_vgmstream_exst(STREAMFILE *streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_svag_kcet(STREAMFILE *streamFile);
|
||||
|
||||
@ -416,7 +416,7 @@ VGMSTREAM * init_vgmstream_dsp_ygo(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ps2_vgv(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ngc_gcub(STREAMFILE * streamFile);
|
||||
VGMSTREAM * init_vgmstream_gcub(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_maxis_xa(STREAMFILE * streamFile);
|
||||
|
||||
@ -949,4 +949,6 @@ VGMSTREAM* init_vgmstream_mjb_mjh(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_tac(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_ogv_3rdeye(STREAMFILE* sf);
|
||||
|
||||
#endif /*_META_H*/
|
||||
|
@ -1,103 +0,0 @@
|
||||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
|
||||
/* GCUB - found in 'Sega Soccer Slam' */
|
||||
VGMSTREAM * init_vgmstream_ngc_gcub(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
char filename[PATH_LIMIT];
|
||||
off_t start_offset;
|
||||
int loop_flag;
|
||||
int channel_count;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
streamFile->get_name(streamFile,filename,sizeof(filename));
|
||||
if (strcasecmp("gcub",filename_extension(filename))) goto fail;
|
||||
|
||||
/* check header */
|
||||
if (read_32bitBE(0x00,streamFile) != 0x47437562) /* "GCub" */
|
||||
goto fail;
|
||||
|
||||
loop_flag = 0;
|
||||
channel_count = read_32bitBE(0x04,streamFile);
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
if (read_32bitBE(0x60,streamFile) == 0x47437878) /* "GCxx" */
|
||||
{
|
||||
start_offset = 0x88;
|
||||
}
|
||||
else
|
||||
{
|
||||
start_offset = 0x60;
|
||||
}
|
||||
|
||||
vgmstream->channels = channel_count;
|
||||
vgmstream->sample_rate = read_32bitBE(0x08,streamFile);
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->num_samples = (read_32bitBE(0x0C,streamFile)-start_offset)/8/channel_count*14;
|
||||
if (loop_flag) {
|
||||
vgmstream->loop_start_sample = 0;
|
||||
vgmstream->loop_end_sample = (read_32bitBE(0x0C,streamFile)-start_offset)/8/channel_count*14;
|
||||
}
|
||||
|
||||
|
||||
if (channel_count == 1)
|
||||
{
|
||||
vgmstream->layout_type = layout_none;
|
||||
}
|
||||
else
|
||||
{
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x8000; // read_32bitBE(0x04,streamFile);
|
||||
}
|
||||
|
||||
vgmstream->meta_type = meta_NGC_GCUB;
|
||||
|
||||
|
||||
if (vgmstream->coding_type == coding_NGC_DSP) {
|
||||
int i;
|
||||
for (i=0;i<16;i++) {
|
||||
vgmstream->ch[0].adpcm_coef[i] = read_16bitBE(0x10+i*2,streamFile);
|
||||
}
|
||||
if (vgmstream->channels == 2) {
|
||||
for (i=0;i<16;i++) {
|
||||
vgmstream->ch[1].adpcm_coef[i] = read_16bitBE(0x30+i*2,streamFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
/* 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);
|
||||
|
||||
if (!vgmstream->ch[1].streamfile) goto fail;
|
||||
|
||||
vgmstream->ch[1].channel_start_offset=
|
||||
vgmstream->ch[1].offset=start_offset+vgmstream->interleave_block_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return vgmstream;
|
||||
|
||||
/* clean up anything we may have opened */
|
||||
fail:
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
@ -544,14 +544,15 @@ VGMSTREAM* init_vgmstream_ogg_vorbis_callbacks(STREAMFILE* sf, ov_callbacks* cal
|
||||
loop_start = atol(strrchr(comment,'=')+1) * sample_rate / 1000; /* ms to samples */
|
||||
loop_flag = (loop_start >= 0);
|
||||
}
|
||||
else if (strstr(comment,"COMMENT=- loopTime ") == comment) { /* Aristear Remain (PC) */
|
||||
loop_start = atol(strrchr(comment,' ')+1) / 1000.0f * sample_rate; /* ms to samples */
|
||||
else if (strstr(comment,"COMMENT=- loopTime ") == comment || /* Aristear Remain (PC) */
|
||||
strstr(comment,"COMMENT=-loopTime ") == comment) { /* Hyakki Ryouran no Yakata x Kawarazaki-ke no Ichizoku (PC) */
|
||||
loop_start = atol(strrchr(comment,'l')) / 1000.0f * sample_rate; /* ms to samples */
|
||||
loop_flag = (loop_start >= 0);
|
||||
|
||||
/* files have all page granule positions -1 except a few close to loop. This throws off
|
||||
* libvorbis seeking (that uses granules), so we need manual fix = slower. Could be detected
|
||||
* by checking granules in the first new OggS pages (other games from same dev don't use
|
||||
* loopTime not have wrong granules though) */
|
||||
* by checking granules in the first new OggS pages (other games from the same dev don't use
|
||||
* loopTime nor have wrong granules though) */
|
||||
force_seek = 1;
|
||||
}
|
||||
|
||||
@ -582,6 +583,8 @@ VGMSTREAM* init_vgmstream_ogg_vorbis_callbacks(STREAMFILE* sf, ov_callbacks* cal
|
||||
vgmstream->coding_type = coding_OGG_VORBIS;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->meta_type = ovmi->meta_type;
|
||||
if (!vgmstream->meta_type)
|
||||
vgmstream->meta_type = meta_OGG_VORBIS;
|
||||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->stream_size = stream_size;
|
||||
|
40
src/meta/ogv_3rdeye.c
Normal file
40
src/meta/ogv_3rdeye.c
Normal file
@ -0,0 +1,40 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* OGV - .ogg container (not related to ogv video) [Bloody Rondo (PC)] */
|
||||
VGMSTREAM* init_vgmstream_ogv_3rdeye(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t subfile_offset, subfile_size;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf,"ogv"))
|
||||
goto fail;
|
||||
if (!is_id32be(0x00,sf, "OGV\0"))
|
||||
goto fail;
|
||||
|
||||
/* 0x04: PCM size */
|
||||
subfile_size = read_u32le(0x08, sf);
|
||||
/* 0x0c: "fmt" + RIFF fmt + "data" (w/ PCM size too) */
|
||||
subfile_offset = 0x2c;
|
||||
|
||||
/* no loops (files bgm does full loops but sfx doesn't) */
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
{
|
||||
ogg_vorbis_meta_info_t ovmi = {0};
|
||||
|
||||
ovmi.meta_type = meta_OGV_3RDEYE;
|
||||
ovmi.stream_size = subfile_size;
|
||||
|
||||
vgmstream = init_vgmstream_ogg_vorbis_callbacks(sf, NULL, subfile_offset, &ovmi);
|
||||
}
|
||||
#else
|
||||
goto fail;
|
||||
#endif
|
||||
|
||||
return vgmstream;
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* EXST - from Sony games [Shadow of the Colossus (PS2), Gacha Mecha Stadium Saru Battle (PS2)] */
|
||||
VGMSTREAM* init_vgmstream_ps2_exst(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* sf_body = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channels, sample_rate;
|
||||
size_t block_size, num_blocks, loop_start_block;
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .sts+int: standard [Shadow of the Colossus (PS2)] (some fake .sts have manually joined header+body)
|
||||
* .x: header+body [Ape Escape 3 (PS2)] */
|
||||
if (!check_extensions(sf, "sts,x"))
|
||||
goto fail;
|
||||
if (!is_id32be(0x00,sf, "EXST"))
|
||||
goto fail;
|
||||
|
||||
sf_body = open_streamfile_by_ext(sf,"int");
|
||||
if (sf_body) {
|
||||
/* separate header+body (header is 0x78) */
|
||||
start_offset = 0x00;
|
||||
}
|
||||
else {
|
||||
/* joint header+body */
|
||||
start_offset = 0x78;
|
||||
/* Gacharoku 2 has header+data but padded header (ELF has pointers + size to SOUND.PCK, and
|
||||
* treats them as single files, no extension but there are Sg2ExStAdpcm* calls in the ELF) */
|
||||
if ((get_streamfile_size(sf) % 0x10) == 0)
|
||||
start_offset = 0x80;
|
||||
|
||||
if (get_streamfile_size(sf) < start_offset)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
channels = read_u16le(0x06,sf);
|
||||
sample_rate = read_u32le(0x08,sf);
|
||||
loop_flag = read_u32le(0x0C,sf) == 1;
|
||||
loop_start_block = read_u32le(0x10,sf);
|
||||
num_blocks = read_u32le(0x14,sf);
|
||||
/* 0x18: 0x24 config per channel? (volume+panning+etc?) */
|
||||
/* rest is padding up to 0x78 */
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_PS2_EXST;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x400;
|
||||
|
||||
block_size = vgmstream->interleave_block_size * vgmstream->channels;
|
||||
vgmstream->num_samples = ps_bytes_to_samples(num_blocks * block_size, channels);
|
||||
vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start_block * block_size, channels);
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf_body ? sf_body : sf, start_offset))
|
||||
goto fail;
|
||||
close_streamfile(sf_body);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(sf_body);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
127
src/vgmstream.c
127
src/vgmstream.c
@ -5,6 +5,7 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <ctype.h>
|
||||
#include "vgmstream.h"
|
||||
#include "meta/meta.h"
|
||||
#include "layout/layout.h"
|
||||
@ -46,7 +47,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
|
||||
init_vgmstream_ps2_rxws,
|
||||
init_vgmstream_ps2_rxw,
|
||||
init_vgmstream_ngc_dsp_stm,
|
||||
init_vgmstream_ps2_exst,
|
||||
init_vgmstream_exst,
|
||||
init_vgmstream_svag_kcet,
|
||||
init_vgmstream_mib_mih,
|
||||
init_vgmstream_ngc_mpdsp,
|
||||
@ -211,7 +212,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
|
||||
init_vgmstream_2dx9,
|
||||
init_vgmstream_dsp_ygo,
|
||||
init_vgmstream_ps2_vgv,
|
||||
init_vgmstream_ngc_gcub,
|
||||
init_vgmstream_gcub,
|
||||
init_vgmstream_maxis_xa,
|
||||
init_vgmstream_ngc_sck_dsp,
|
||||
init_vgmstream_apple_caff,
|
||||
@ -525,6 +526,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
|
||||
init_vgmstream_tac,
|
||||
init_vgmstream_idsp_tose,
|
||||
init_vgmstream_dsp_kwa,
|
||||
init_vgmstream_ogv_3rdeye,
|
||||
|
||||
/* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */
|
||||
init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */
|
||||
@ -1330,6 +1332,33 @@ fail:
|
||||
return;
|
||||
}
|
||||
|
||||
/*******************************************************************************/
|
||||
/* BITRATE */
|
||||
/*******************************************************************************/
|
||||
#define BITRATE_FILES_MAX 128 /* arbitrary max, but +100 segments have been observed */
|
||||
typedef struct {
|
||||
uint32_t hash[BITRATE_FILES_MAX]; /* already used streamfiles */
|
||||
int subsong[BITRATE_FILES_MAX]; /* subsongs of those streamfiles (could be incorporated to the hash?) */
|
||||
int count;
|
||||
int count_max;
|
||||
} bitrate_info_t;
|
||||
|
||||
static uint32_t hash_sf(STREAMFILE* sf) {
|
||||
int i;
|
||||
char path[PATH_LIMIT];
|
||||
uint32_t hash = 2166136261;
|
||||
|
||||
get_streamfile_name(sf, path, sizeof(path));
|
||||
|
||||
/* our favorite garbo hash a.k.a FNV-1 32b */
|
||||
for (i = 0; i < strlen(path); i++) {
|
||||
char c = tolower(path[i]);
|
||||
hash = (hash * 16777619) ^ (uint8_t)c;
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
/* average bitrate helper to get STREAMFILE for a channel, since some codecs may use their own */
|
||||
static STREAMFILE* get_vgmstream_average_bitrate_channel_streamfile(VGMSTREAM* vgmstream, int channel) {
|
||||
|
||||
@ -1368,18 +1397,18 @@ static STREAMFILE* get_vgmstream_average_bitrate_channel_streamfile(VGMSTREAM* v
|
||||
return vgmstream->ch[channel].streamfile;
|
||||
}
|
||||
|
||||
static int get_vgmstream_file_bitrate_from_size(size_t size, int sample_rate, int length_samples) {
|
||||
static int get_vgmstream_file_bitrate_from_size(size_t size, int sample_rate, int32_t length_samples) {
|
||||
if (sample_rate == 0 || length_samples == 0) return 0;
|
||||
if (length_samples < 100) return 0; /* ignore stupid bitrates caused by some segments */
|
||||
return (int)((int64_t)size * 8 * sample_rate / length_samples);
|
||||
}
|
||||
static int get_vgmstream_file_bitrate_from_streamfile(STREAMFILE* streamfile, int sample_rate, int length_samples) {
|
||||
if (streamfile == NULL) return 0;
|
||||
return get_vgmstream_file_bitrate_from_size(get_streamfile_size(streamfile), sample_rate, length_samples);
|
||||
static int get_vgmstream_file_bitrate_from_streamfile(STREAMFILE* sf, int sample_rate, int32_t length_samples) {
|
||||
if (sf == NULL) return 0;
|
||||
return get_vgmstream_file_bitrate_from_size(get_streamfile_size(sf), sample_rate, length_samples);
|
||||
}
|
||||
|
||||
static int get_vgmstream_file_bitrate_main(VGMSTREAM* vgmstream, STREAMFILE** streamfile_pointers, int *pointers_count, int pointers_max) {
|
||||
int sub, ch;
|
||||
static int get_vgmstream_file_bitrate_main(VGMSTREAM* vgmstream, bitrate_info_t* br) {
|
||||
int i, ch;
|
||||
int bitrate = 0;
|
||||
|
||||
/* Recursively get bitrate and fill the list of streamfiles if needed (to filter),
|
||||
@ -1387,58 +1416,65 @@ static int get_vgmstream_file_bitrate_main(VGMSTREAM* vgmstream, STREAMFILE** st
|
||||
*
|
||||
* Because of how data, layers and segments can be combined it's possible to
|
||||
* fool this in various ways; metas should report stream_size in complex cases
|
||||
* to get accurate bitrates (particularly for subsongs). */
|
||||
* to get accurate bitrates (particularly for subsongs). An edge case is when
|
||||
* segments use only a few samples from a full file (like Wwise transitions), bitrates
|
||||
* become a bit high since its hard to detect only part of the file is needed. */
|
||||
|
||||
if (vgmstream->stream_size) {
|
||||
bitrate = get_vgmstream_file_bitrate_from_size(vgmstream->stream_size, vgmstream->sample_rate, vgmstream->num_samples);
|
||||
}
|
||||
else if (vgmstream->layout_type == layout_segmented) {
|
||||
if (vgmstream->layout_type == layout_segmented) {
|
||||
segmented_layout_data *data = (segmented_layout_data *) vgmstream->layout_data;
|
||||
for (sub = 0; sub < data->segment_count; sub++) {
|
||||
bitrate += get_vgmstream_file_bitrate_main(data->segments[sub], streamfile_pointers, pointers_count, pointers_max);
|
||||
for (i = 0; i < data->segment_count; i++) {
|
||||
bitrate += get_vgmstream_file_bitrate_main(data->segments[i], br);
|
||||
}
|
||||
bitrate = bitrate / data->segment_count;
|
||||
}
|
||||
else if (vgmstream->layout_type == layout_layered) {
|
||||
layered_layout_data *data = vgmstream->layout_data;
|
||||
for (sub = 0; sub < data->layer_count; sub++) {
|
||||
bitrate += get_vgmstream_file_bitrate_main(data->layers[sub], streamfile_pointers, pointers_count, pointers_max);
|
||||
for (i = 0; i < data->layer_count; i++) {
|
||||
bitrate += get_vgmstream_file_bitrate_main(data->layers[i], br);
|
||||
}
|
||||
bitrate = bitrate / data->layer_count;
|
||||
}
|
||||
else {
|
||||
/* Add channel bitrate if streamfile hasn't been used before (comparing files
|
||||
* by absolute paths), so bitrate doesn't multiply when the same STREAMFILE is
|
||||
* reopened per channel, also skipping repeated pointers. */
|
||||
char path_current[PATH_LIMIT];
|
||||
char path_compare[PATH_LIMIT];
|
||||
int is_unique = 1;
|
||||
|
||||
/* Add channel bitrate if streamfile hasn't been used before, so bitrate doesn't count repeats
|
||||
* (like same STREAMFILE reopened per channel, also considering SFs may be wrapped). */
|
||||
for (ch = 0; ch < vgmstream->channels; ch++) {
|
||||
STREAMFILE* sf_cur = get_vgmstream_average_bitrate_channel_streamfile(vgmstream, ch);
|
||||
if (!sf_cur) continue;
|
||||
get_streamfile_name(sf_cur, path_current, sizeof(path_current));
|
||||
uint32_t hash_cur;
|
||||
int subsong_cur;
|
||||
STREAMFILE* sf_cur;
|
||||
int is_unique = 1; /* default to "no other SFs exist" */
|
||||
|
||||
for (sub = 0; sub < *pointers_count; sub++) {
|
||||
STREAMFILE* sf_cmp = streamfile_pointers[sub];
|
||||
if (!sf_cmp) continue;
|
||||
if (sf_cur == sf_cmp) {
|
||||
is_unique = 0;
|
||||
break;
|
||||
}
|
||||
get_streamfile_name(sf_cmp, path_compare, sizeof(path_compare));
|
||||
if (strcmp(path_current, path_compare) == 0) {
|
||||
/* compares paths (hashes for faster compares) + subsongs (same file + different subsong = "different" file) */
|
||||
sf_cur = get_vgmstream_average_bitrate_channel_streamfile(vgmstream, ch);
|
||||
if (!sf_cur) continue;
|
||||
|
||||
hash_cur = hash_sf(sf_cur);
|
||||
subsong_cur = vgmstream->stream_index;
|
||||
|
||||
for (i = 0; i < br->count; i++) {
|
||||
uint32_t hash_cmp = br->hash[i];
|
||||
int subsong_cmp = br->subsong[i];
|
||||
|
||||
if (hash_cur == hash_cmp && subsong_cur == subsong_cmp) {
|
||||
is_unique = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_unique) {
|
||||
if (*pointers_count >= pointers_max) goto fail;
|
||||
streamfile_pointers[*pointers_count] = sf_cur;
|
||||
(*pointers_count)++;
|
||||
if (br->count >= br->count_max) goto fail;
|
||||
|
||||
bitrate += get_vgmstream_file_bitrate_from_streamfile(sf_cur, vgmstream->sample_rate, vgmstream->num_samples);
|
||||
br->hash[br->count] = hash_cur;
|
||||
br->subsong[br->count] = subsong_cur;
|
||||
|
||||
br->count++;
|
||||
|
||||
if (vgmstream->stream_size) {
|
||||
/* stream_size applies to both channels but should add once and detect repeats (for current subsong) */
|
||||
bitrate += get_vgmstream_file_bitrate_from_size(vgmstream->stream_size, vgmstream->sample_rate, vgmstream->num_samples);
|
||||
}
|
||||
else {
|
||||
bitrate += get_vgmstream_file_bitrate_from_streamfile(sf_cur, vgmstream->sample_rate, vgmstream->num_samples);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1453,11 +1489,10 @@ fail:
|
||||
* it counts extra data like block headers and padding. While this can be surprising
|
||||
* sometimes (as it's often higher than common codec bitrates) it isn't wrong per se. */
|
||||
int get_vgmstream_average_bitrate(VGMSTREAM* vgmstream) {
|
||||
const size_t pointers_max = 128; /* arbitrary max, but +100 segments have been observed */
|
||||
STREAMFILE* streamfile_pointers[128]; /* list already used streamfiles */
|
||||
int pointers_count = 0;
|
||||
bitrate_info_t br = {0};
|
||||
br.count_max = BITRATE_FILES_MAX;
|
||||
|
||||
return get_vgmstream_file_bitrate_main(vgmstream, streamfile_pointers, &pointers_count, pointers_max);
|
||||
return get_vgmstream_file_bitrate_main(vgmstream, &br);
|
||||
}
|
||||
|
||||
|
||||
|
@ -361,7 +361,7 @@ typedef enum {
|
||||
meta_NPS,
|
||||
meta_PS2_RXWS, /* Sony games (Genji, Okage Shadow King, Arc The Lad Twilight of Spirits) */
|
||||
meta_RAW_INT,
|
||||
meta_PS2_EXST, /* Shadow of Colossus EXST */
|
||||
meta_EXST,
|
||||
meta_SVAG_KCET,
|
||||
meta_PS_HEADERLESS, /* headerless PS-ADPCM */
|
||||
meta_MIB_MIH,
|
||||
@ -515,7 +515,7 @@ typedef enum {
|
||||
meta_SD9, /* beatmaniaIIDX16 - EMPRESS (Arcade) */
|
||||
meta_2DX9, /* beatmaniaIIDX16 - EMPRESS (Arcade) */
|
||||
meta_PS2_VGV, /* Rune: Viking Warlord */
|
||||
meta_NGC_GCUB, /* Sega Soccer Slam */
|
||||
meta_GCUB,
|
||||
meta_MAXIS_XA, /* Sim City 3000 (PC) */
|
||||
meta_NGC_SCK_DSP, /* Scorpion King (NGC) */
|
||||
meta_CAFF, /* iPhone .caf */
|
||||
@ -759,6 +759,7 @@ typedef enum {
|
||||
meta_TAC,
|
||||
meta_IDSP_TOSE,
|
||||
meta_DSP_KWA,
|
||||
meta_OGV_3RDEYE,
|
||||
} meta_t;
|
||||
|
||||
/* standard WAVEFORMATEXTENSIBLE speaker positions */
|
||||
|
Loading…
Reference in New Issue
Block a user