mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-12-23 20:04:50 +01:00
215 lines
7.6 KiB
C
215 lines
7.6 KiB
C
#include "meta.h"
|
|
#include "../coding/coding.h"
|
|
|
|
/* PDT - Hudson's stream container [Adventure Island (GC), Muscle Champion (GC), Mario Party series (GC)] */
|
|
VGMSTREAM * init_vgmstream_ngc_pdt(STREAMFILE *streamFile) {
|
|
VGMSTREAM * vgmstream = NULL;
|
|
int loop_flag, channel_count, sample_rate;
|
|
size_t entries, nibble_size, loop_start;
|
|
off_t entries_offset, coefs_offset, header_offset;
|
|
off_t channel1_offset = 0, channel2_offset = 0, coef_offset = 0;
|
|
int total_subsongs, target_subsong = streamFile->stream_index;
|
|
|
|
|
|
/* checks */
|
|
if (!check_extensions(streamFile, "pdt"))
|
|
goto fail;
|
|
|
|
if (read_16bitBE(0x00,streamFile) != 0x01) /* version? */
|
|
goto fail;
|
|
if (read_32bitBE(0x04,streamFile) != 0x02 && /* Mario Party 4 (GC) */
|
|
read_32bitBE(0x04,streamFile) != 0x04) /* Cubic Lode Runner (GC) */
|
|
goto fail;
|
|
if (read_32bitBE(0x08,streamFile) != 0x7d00) /* not-sample rate? */
|
|
goto fail;
|
|
if (read_32bitBE(0x0c,streamFile) != 0x02 && /* not-channels? */
|
|
read_32bitBE(0x0c,streamFile) != 0x04)
|
|
goto fail;
|
|
|
|
entries = read_16bitBE(0x02,streamFile);
|
|
entries_offset = read_32bitBE(0x10,streamFile);
|
|
coefs_offset = read_32bitBE(0x14,streamFile);
|
|
//headers_offset = read_32bitBE(0x18,streamFile); /* we'll have pointers to those two */
|
|
//streams_offset = read_32bitBE(0x1c,streamFile);
|
|
|
|
/* find subsongs and target header, as entries can be empty/repeated */
|
|
{
|
|
/* tables to cache reads as it can be kinda slow with so many loops */
|
|
uint32_t data_offsets[0x2000];
|
|
uint32_t entry_offset, data_offset;
|
|
int i,j;
|
|
|
|
if (entries > 0x2000)
|
|
goto fail;
|
|
|
|
total_subsongs = 0;
|
|
if (target_subsong == 0) target_subsong = 1;
|
|
|
|
header_offset = 0;
|
|
for (i = 0; i < entries; i++) {
|
|
int is_unique = 1;
|
|
|
|
entry_offset = read_32bitBE(entries_offset + i*0x04,streamFile);
|
|
if (entry_offset == 0x00)
|
|
continue;
|
|
data_offset = read_32bitBE(entry_offset+0x10,streamFile);
|
|
|
|
/* check if current entry header was repeated (same file offset, difference in flags only) */
|
|
for (j = 0; j < total_subsongs; j++) {
|
|
if (data_offsets[j] == data_offset) {
|
|
is_unique = 0;
|
|
break;
|
|
}
|
|
}
|
|
if (!is_unique)
|
|
continue;
|
|
|
|
data_offsets[total_subsongs] = data_offset;
|
|
total_subsongs++;
|
|
|
|
/* target GET, but keep going to count subsongs */
|
|
if (!header_offset && target_subsong == total_subsongs) {
|
|
header_offset = entry_offset;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* parse header */
|
|
{
|
|
uint8_t flags;
|
|
size_t coef1_entry;
|
|
off_t coef1_offset;
|
|
|
|
flags = read_8bit(header_offset+0x00,streamFile);
|
|
sample_rate = read_32bitBE(header_offset+0x04,streamFile);
|
|
/* 0x01: unknown + 0x4000 */
|
|
sample_rate = read_32bitBE(header_offset+0x04,streamFile);
|
|
nibble_size = read_32bitBE(header_offset+0x08,streamFile);
|
|
loop_start = read_32bitBE(header_offset+0x0c,streamFile);
|
|
|
|
channel1_offset = read_32bitBE(header_offset+0x10,streamFile);
|
|
coef1_entry = read_16bitBE(header_offset+0x14,streamFile);
|
|
coef1_offset = coefs_offset + coef1_entry*0x20;
|
|
|
|
if (flags & 0x01) {
|
|
//size_t coef2_entry;
|
|
//off_t coef2_offset;
|
|
|
|
channel2_offset = read_32bitBE(header_offset+0x18,streamFile);
|
|
/* always after coef1 in practice */
|
|
//coef2_entry = read_16bitBE(header_offset+0x1c,streamFile);
|
|
//coef2_offset = coefs_offset + coef2_entry*0x20;
|
|
//if (coef1_offset + 0x20 != coef2_offset)
|
|
// goto fail;
|
|
}
|
|
|
|
coef_offset = coef1_offset;
|
|
loop_flag = (flags & 0x02);
|
|
channel_count = (flags & 0x01) ? 2 : 1;
|
|
}
|
|
|
|
|
|
/* build the VGMSTREAM */
|
|
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
|
if (!vgmstream) goto fail;
|
|
|
|
vgmstream->sample_rate = sample_rate;
|
|
//vgmstream->num_samples = dsp_bytes_to_samples(data_size, channel_count);//todo remove
|
|
vgmstream->num_samples = dsp_nibbles_to_samples(nibble_size);
|
|
//vgmstream->loop_start_sample = dsp_bytes_to_samples(loop_start, channel_count);//todo remove
|
|
vgmstream->loop_start_sample = dsp_nibbles_to_samples(loop_start);
|
|
vgmstream->loop_end_sample = vgmstream->num_samples;
|
|
|
|
vgmstream->meta_type = meta_NGC_PDT;
|
|
vgmstream->coding_type = coding_NGC_DSP;
|
|
vgmstream->layout_type = layout_none;
|
|
dsp_read_coefs_be(vgmstream, streamFile, coef_offset, 0x20);
|
|
|
|
vgmstream->num_streams = total_subsongs;
|
|
vgmstream->stream_size = nibble_size / 2 * channel_count;
|
|
|
|
if (!vgmstream_open_stream(vgmstream,streamFile,channel1_offset))
|
|
goto fail;
|
|
|
|
/* channels may start at slightly separated offsets */
|
|
if (channel_count == 2) {
|
|
vgmstream->ch[1].channel_start_offset =
|
|
vgmstream->ch[1].offset = channel2_offset;
|
|
}
|
|
|
|
|
|
return vgmstream;
|
|
|
|
fail:
|
|
close_vgmstream(vgmstream);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* PDT - custom fake header for split (PDTExt) .ptd [Mario Party (GC)] */
|
|
VGMSTREAM * init_vgmstream_ngc_pdt_split(STREAMFILE *streamFile) {
|
|
VGMSTREAM * vgmstream = NULL;
|
|
off_t start_offset;
|
|
int loop_flag, channel_count;
|
|
|
|
|
|
/* checks */
|
|
if (!check_extensions(streamFile, "pdt"))
|
|
goto fail;
|
|
|
|
/* 0x10 fake header + chunks of the original header / data pasted together */
|
|
if (read_32bitBE(0x00,streamFile) != 0x50445420 && /* "PDT " */
|
|
read_32bitBE(0x04,streamFile) != 0x44535020 && /* "DSP " */
|
|
read_32bitBE(0x08,streamFile) != 0x48454144 && /* "HEAD " */
|
|
read_16bitBE(0x0C,streamFile) != 0x4552) /* "ER " */
|
|
goto fail;
|
|
|
|
start_offset = 0x800;
|
|
channel_count = (uint16_t)(read_16bitLE(0x0E,streamFile));
|
|
loop_flag = (read_32bitBE(0x1C,streamFile) != 2);
|
|
|
|
|
|
/* build the VGMSTREAM */
|
|
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
|
if (!vgmstream) goto fail;
|
|
|
|
vgmstream->sample_rate = read_32bitBE(0x14,streamFile);
|
|
|
|
if (channel_count == 1) {
|
|
vgmstream->num_samples = read_32bitBE(0x18,streamFile)*14/8/channel_count/2;
|
|
if (loop_flag) {
|
|
vgmstream->loop_start_sample = read_32bitBE(0x1C,streamFile)*14/8/channel_count/2;
|
|
vgmstream->loop_end_sample = read_32bitBE(0x18,streamFile)*14/8/channel_count/2;
|
|
}
|
|
}
|
|
else if (channel_count == 2) {
|
|
vgmstream->num_samples = read_32bitBE(0x18,streamFile)*14/8/channel_count;
|
|
if (loop_flag) {
|
|
vgmstream->loop_start_sample = read_32bitBE(0x1C,streamFile)*14/8/channel_count;
|
|
vgmstream->loop_end_sample = read_32bitBE(0x18,streamFile)*14/8/channel_count;
|
|
}
|
|
}
|
|
else {
|
|
goto fail;
|
|
}
|
|
|
|
vgmstream->meta_type = meta_NGC_PDT;
|
|
vgmstream->coding_type = coding_NGC_DSP;
|
|
vgmstream->layout_type = layout_none;
|
|
dsp_read_coefs_be(vgmstream, streamFile, 0x50, 0x20);
|
|
|
|
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
|
goto fail;
|
|
|
|
if (channel_count == 2) {
|
|
vgmstream->ch[1].channel_start_offset =
|
|
vgmstream->ch[1].offset = ((get_streamfile_size(streamFile)+start_offset) / channel_count);
|
|
}
|
|
|
|
return vgmstream;
|
|
|
|
fail:
|
|
close_vgmstream(vgmstream);
|
|
return NULL;
|
|
}
|