mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-02-23 05:40:08 +01:00
Add cavia .hd2+bd [Drakengard 1/2 (PS2), Ghost in the Shell: SAC (PS2)]
This commit is contained in:
parent
6ad192ec39
commit
b215eb26fa
@ -109,12 +109,13 @@ int32_t pcm8_bytes_to_samples(size_t bytes, int channels);
|
||||
void decode_psx(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int is_badflags, int config);
|
||||
void decode_psx_configurable(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int frame_size, int config);
|
||||
void decode_psx_pivotal(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int frame_size);
|
||||
int ps_find_loop_offsets(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t* out_loop_start, int32_t* out_loop_end);
|
||||
int ps_find_loop_offsets_full(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t* out_loop_start, int32_t* out_loop_end);
|
||||
bool ps_find_loop_offsets(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t* out_loop_start, int32_t* out_loop_end);
|
||||
bool ps_find_loop_offsets_full(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t* out_loop_start, int32_t* out_loop_end);
|
||||
bool ps_find_stream_info(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t* p_loop_start, int32_t* p_loop_end, uint32_t* p_stream_size);
|
||||
size_t ps_find_padding(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, size_t interleave, int discard_empty);
|
||||
size_t ps_bytes_to_samples(size_t bytes, int channels);
|
||||
size_t ps_cfg_bytes_to_samples(size_t bytes, size_t frame_size, int channels);
|
||||
int ps_check_format(STREAMFILE* sf, off_t offset, size_t max);
|
||||
bool ps_check_format(STREAMFILE* sf, off_t offset, size_t max);
|
||||
|
||||
|
||||
/* psv_decoder */
|
||||
|
@ -250,39 +250,43 @@ void decode_psx_pivotal(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channels
|
||||
* - 0x7 (0111): End marker and don't decode
|
||||
* - 0x8+(1NNN): Not valid
|
||||
*/
|
||||
static int ps_find_loop_offsets_internal(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t * p_loop_start, int32_t * p_loop_end, int config) {
|
||||
static int ps_find_stream_info_internal(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t* p_loop_start, int32_t* p_loop_end, uint32_t* p_stream_size, int config) {
|
||||
int num_samples = 0, loop_start = 0, loop_end = 0;
|
||||
int loop_start_found = 0, loop_end_found = 0;
|
||||
bool loop_start_found = false, loop_end_found = false;
|
||||
off_t offset = start_offset;
|
||||
off_t max_offset = start_offset + data_size;
|
||||
size_t interleave_consumed = 0;
|
||||
int detect_full_loops = config & 1;
|
||||
bool detect_full_loops = config & 1;
|
||||
bool stop_on_null = config & 2;
|
||||
int frames = 0;
|
||||
|
||||
|
||||
if (data_size == 0 || channels == 0 || (channels > 1 && interleave == 0))
|
||||
return 0;
|
||||
|
||||
while (offset < max_offset) {
|
||||
uint8_t flag = read_u8(offset+0x01, sf) & 0x0F; /* lower nibble only (for HEVAG) */
|
||||
uint16_t header = read_u16be(offset+0x00, sf);
|
||||
uint8_t flag = header & 0x0F; /* lower nibble only (for HEVAG) */;
|
||||
frames++;
|
||||
|
||||
/* theoretically possible and would use last 0x06 */
|
||||
VGM_ASSERT_ONCE(loop_start_found && flag == 0x06, "PS LOOPS: multiple loop start found at %x\n", (uint32_t)offset);
|
||||
|
||||
if (flag == 0x06 && !loop_start_found) {
|
||||
loop_start = num_samples; /* loop start before this frame */
|
||||
loop_start_found = 1;
|
||||
loop_start_found = true;
|
||||
}
|
||||
|
||||
if (flag == 0x03 && !loop_end) {
|
||||
loop_end = num_samples + 28; /* loop end after this frame */
|
||||
loop_end_found = 1;
|
||||
loop_end_found = true;
|
||||
|
||||
/* ignore strange case in Commandos (PS2), has many loop starts and ends */
|
||||
if (channels == 1
|
||||
&& offset + 0x10 < max_offset
|
||||
&& (read_u8(offset + 0x11, sf) & 0x0F) == 0x06) {
|
||||
loop_end = 0;
|
||||
loop_end_found = 0;
|
||||
loop_end_found = false;
|
||||
}
|
||||
|
||||
if (loop_start_found && loop_end_found)
|
||||
@ -296,7 +300,7 @@ static int ps_find_loop_offsets_internal(STREAMFILE* sf, off_t start_offset, siz
|
||||
if (flag == 0x01 && detect_full_loops) {
|
||||
static const uint8_t eof[0x10] = {0xFF,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
|
||||
uint8_t buf[0x10];
|
||||
uint8_t hdr = read_u8(offset + 0x00, sf);
|
||||
uint8_t hdr = (header >> 8) & 0xFF;
|
||||
|
||||
int read = read_streamfile(buf, offset+0x10, sizeof(buf), sf);
|
||||
if (read > 0
|
||||
@ -310,8 +314,8 @@ static int ps_find_loop_offsets_internal(STREAMFILE* sf, off_t start_offset, siz
|
||||
if (hdr == buf[0] && memcmp(buf+1, eof+1, sizeof(buf) - 1) == 0) {
|
||||
loop_start = 28; /* skip first frame as it's null in PS-ADPCM */
|
||||
loop_end = num_samples + 28; /* loop end after this frame */
|
||||
loop_start_found = 1;
|
||||
loop_end_found = 1;
|
||||
loop_start_found = true;
|
||||
loop_end_found = true;
|
||||
//;VGM_LOG("PS LOOPS: full loop found\n");
|
||||
break;
|
||||
}
|
||||
@ -326,8 +330,19 @@ static int ps_find_loop_offsets_internal(STREAMFILE* sf, off_t start_offset, siz
|
||||
interleave_consumed += 0x10;
|
||||
if (interleave_consumed == interleave) {
|
||||
interleave_consumed = 0;
|
||||
offset += interleave*(channels - 1);
|
||||
offset += interleave * (channels - 1);
|
||||
}
|
||||
|
||||
// stream done flag
|
||||
if (stop_on_null && offset > start_offset && (flag & 0x01)) {
|
||||
frames++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (p_stream_size) {
|
||||
// uses frames rather than offsets to take interleave into account
|
||||
*p_stream_size = frames * 0x10 * channels;
|
||||
}
|
||||
|
||||
VGM_ASSERT(loop_start_found && !loop_end_found, "PS LOOPS: found loop start but not loop end\n");
|
||||
@ -341,15 +356,21 @@ static int ps_find_loop_offsets_internal(STREAMFILE* sf, off_t start_offset, siz
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
return 0; /* no loop */
|
||||
}
|
||||
|
||||
int ps_find_loop_offsets(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t* p_loop_start, int32_t* p_loop_end) {
|
||||
return ps_find_loop_offsets_internal(sf, start_offset, data_size, channels, interleave, p_loop_start, p_loop_end, 0);
|
||||
//TODO: rename as it returns samples
|
||||
bool ps_find_loop_offsets(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t* p_loop_start, int32_t* p_loop_end) {
|
||||
return ps_find_stream_info_internal(sf, start_offset, data_size, channels, interleave, p_loop_start, p_loop_end, NULL, 0x00);
|
||||
}
|
||||
|
||||
int ps_find_loop_offsets_full(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t* p_loop_start, int32_t* p_loop_end) {
|
||||
return ps_find_loop_offsets_internal(sf, start_offset, data_size, channels, interleave, p_loop_start, p_loop_end, 1);
|
||||
bool ps_find_loop_offsets_full(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t* p_loop_start, int32_t* p_loop_end) {
|
||||
return ps_find_stream_info_internal(sf, start_offset, data_size, channels, interleave, p_loop_start, p_loop_end, NULL, 0x01);
|
||||
}
|
||||
|
||||
bool ps_find_stream_info(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t* p_loop_start, int32_t* p_loop_end, uint32_t* p_stream_size) {
|
||||
return ps_find_stream_info_internal(sf, start_offset, data_size, channels, interleave, p_loop_start, p_loop_end, p_stream_size, 0x02);
|
||||
}
|
||||
|
||||
size_t ps_find_padding(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, size_t interleave, int discard_empty) {
|
||||
@ -440,7 +461,7 @@ size_t ps_cfg_bytes_to_samples(size_t bytes, size_t frame_size, int channels) {
|
||||
}
|
||||
|
||||
/* test PS-ADPCM frames for correctness */
|
||||
int ps_check_format(STREAMFILE* sf, off_t offset, size_t max) {
|
||||
bool ps_check_format(STREAMFILE* sf, off_t offset, size_t max) {
|
||||
off_t max_offset = offset + max;
|
||||
if (max_offset > get_streamfile_size(sf))
|
||||
max_offset = get_streamfile_size(sf);
|
||||
@ -450,10 +471,10 @@ int ps_check_format(STREAMFILE* sf, off_t offset, size_t max) {
|
||||
uint8_t flags = read_8bit(offset+0x01,sf);
|
||||
|
||||
if (predictor > 5 || flags > 7) {
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
offset += 0x10;
|
||||
}
|
||||
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
@ -221,9 +221,10 @@ static const char* extension_list[] = {
|
||||
|
||||
"h4m",
|
||||
"hab",
|
||||
"hbd",
|
||||
"hca",
|
||||
"hd",
|
||||
"hbd",
|
||||
"hd2",
|
||||
"hd3",
|
||||
"hdr",
|
||||
"hdt",
|
||||
@ -1457,6 +1458,7 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_KA1A, "Koei Tecmo KA1A header"},
|
||||
{meta_HD_BD, "Sony HD+BD header"},
|
||||
{meta_PPHD, "Sony PPHD header"},
|
||||
{meta_XABP, "cavia XABp header"},
|
||||
};
|
||||
|
||||
void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t out_size) {
|
||||
|
@ -776,6 +776,7 @@
|
||||
<ClCompile Include="meta\wxd_wxh.c" />
|
||||
<ClCompile Include="meta\xa.c" />
|
||||
<ClCompile Include="meta\xa2_acclaim.c" />
|
||||
<ClCompile Include="meta\xabp.c" />
|
||||
<ClCompile Include="meta\xau.c" />
|
||||
<ClCompile Include="meta\xau_konami.c" />
|
||||
<ClCompile Include="meta\xavs.c" />
|
||||
|
@ -2158,6 +2158,9 @@
|
||||
<ClCompile Include="meta\xa2_acclaim.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\xabp.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\xau.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
@ -1019,4 +1019,6 @@ VGMSTREAM* init_vgmstream_hd_bd(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_pphd(STREAMFILE* sf);
|
||||
|
||||
#endif /*_META_H*/
|
||||
VGMSTREAM* init_vgmstream_xabp(STREAMFILE* sf);
|
||||
|
||||
#endif
|
||||
|
62
src/meta/xabp.c
Normal file
62
src/meta/xabp.c
Normal file
@ -0,0 +1,62 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "../util/meta_utils.h"
|
||||
|
||||
|
||||
/* XABp - cavia PS2 bank format [Drakengard 1/2 (PS2), Ghost in the Shell: SAC (PS2)] */
|
||||
VGMSTREAM* init_vgmstream_xabp(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!is_id32be(0x00,sf, "pBAX"))
|
||||
return NULL;
|
||||
|
||||
// .hd2+bd: from bigfiles
|
||||
if (!check_extensions(sf, "hd2"))
|
||||
return NULL;
|
||||
|
||||
meta_header_t h = {
|
||||
.meta = meta_XABP,
|
||||
};
|
||||
h.target_subsong = sf->stream_index;
|
||||
if (h.target_subsong == 0)
|
||||
h.target_subsong = 1;
|
||||
|
||||
// cavia's bank format inspired by .hd+bd
|
||||
uint32_t bd_size = read_u32le(0x04,sf);
|
||||
// 0x08: null
|
||||
h.total_subsongs = read_s16le(0x0c,sf);
|
||||
// 0x0e: always 0x0010?
|
||||
|
||||
uint32_t head_offset = 0x10 + 0x20 * (h.target_subsong - 1);
|
||||
// 00: file id?
|
||||
h.sample_rate = read_s16le(head_offset + 0x16,sf);
|
||||
h.stream_offset = read_u32le(head_offset + 0x18, sf);
|
||||
// others: config? (can't make sense of them, don't seem quite like sizes/flags/etc)
|
||||
|
||||
h.channels = 1;
|
||||
|
||||
h.coding = coding_PSX;
|
||||
h.layout = layout_none;
|
||||
h.open_stream = true;
|
||||
|
||||
h.sf_head = sf;
|
||||
h.sf_body = open_streamfile_by_ext(sf,"bd");
|
||||
if (!h.sf_body) goto fail;
|
||||
|
||||
// Entries/offsets aren't ordered .bd not it seems to have sizes (maybe mixes notes+streams into one)
|
||||
// Since PS-ADPCM is wired to play until end frame end or loop, it's probably designed like that.
|
||||
// It also repeats entries (different ID but same config) but for now just prints it as is.
|
||||
h.loop_flag = ps_find_stream_info(h.sf_body, h.stream_offset, bd_size - h.stream_offset, h.channels, h.interleave, &h.loop_start, &h.loop_end, &h.stream_size);
|
||||
h.num_samples = ps_bytes_to_samples(h.stream_size, h.channels);
|
||||
VGM_LOG("s=%x, %x\n", h.stream_offset, h.stream_size);
|
||||
|
||||
vgmstream = alloc_metastream(&h);
|
||||
close_streamfile(h.sf_body);
|
||||
return vgmstream;
|
||||
fail:
|
||||
close_streamfile(h.sf_body);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
@ -513,6 +513,7 @@ init_vgmstream_t init_vgmstream_functions[] = {
|
||||
init_vgmstream_ka1a,
|
||||
init_vgmstream_hd_bd,
|
||||
init_vgmstream_pphd,
|
||||
init_vgmstream_xabp,
|
||||
|
||||
/* lower priority metas (no clean header identity, somewhat ambiguous, or need extension/companion file to identify) */
|
||||
init_vgmstream_agsc,
|
||||
|
@ -714,6 +714,7 @@ typedef enum {
|
||||
meta_KA1A,
|
||||
meta_HD_BD,
|
||||
meta_PPHD,
|
||||
meta_XABP,
|
||||
|
||||
} meta_t;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user