diff --git a/src/layout/blocked_thp.c b/src/layout/blocked_thp.c index abea3e2c..1722e9e3 100644 --- a/src/layout/blocked_thp.c +++ b/src/layout/blocked_thp.c @@ -2,30 +2,40 @@ #include "../vgmstream.h" /* set up for the block at the given offset */ -void block_update_thp(off_t block_offset, VGMSTREAM * vgmstream) { - int i,j; - STREAMFILE *streamFile=vgmstream->ch[0].streamfile; - off_t start_offset; - int32_t nextFrameSize; +void block_update_thp(off_t block_offset, VGMSTREAM *vgmstream) { + int i, j; + STREAMFILE *streamFile = vgmstream->ch[0].streamfile; + off_t audio_offset; + size_t next_block_size, video_size; - vgmstream->current_block_offset = block_offset; - nextFrameSize=read_32bitBE(vgmstream->current_block_offset,streamFile); + next_block_size = read_32bitBE(block_offset + 0x00, streamFile); + /* 0x04: frame size previous */ + video_size = read_32bitBE(block_offset + 0x08,streamFile); + /* 0x0c: audio size */ - vgmstream->next_block_offset = vgmstream->current_block_offset - + vgmstream->full_block_size; - vgmstream->full_block_size = nextFrameSize; + audio_offset = block_offset + 0x10 + video_size; - start_offset=vgmstream->current_block_offset - + read_32bitBE(vgmstream->current_block_offset+0x08,streamFile)+0x10; - vgmstream->current_block_size=read_32bitBE(start_offset,streamFile); - start_offset+=8; + vgmstream->current_block_offset = block_offset; + vgmstream->next_block_offset = block_offset + vgmstream->full_block_size; + vgmstream->full_block_size = next_block_size; - for(i=0;ichannels;i++) { - for(j=0;j<16;j++) { - vgmstream->ch[i].adpcm_coef[j]=read_16bitBE(start_offset+(i*0x20)+(j*2),streamFile); - } - vgmstream->ch[i].adpcm_history1_16=read_16bitBE(start_offset + (0x20*vgmstream->channels) + (i*4),streamFile); - vgmstream->ch[i].adpcm_history2_16=read_16bitBE(start_offset + (0x20*vgmstream->channels) + (i*4) + 2,streamFile); - vgmstream->ch[i].offset = start_offset + (0x24*vgmstream->channels)+(i*vgmstream->current_block_size); - } + /* block samples can be smaller than block size, normally in the last block, + * but num_samples already takes that into account, so there is no real difference */ + vgmstream->current_block_size = read_32bitBE(audio_offset + 0x00, streamFile); + vgmstream->current_block_samples = read_32bitBE(audio_offset + 0x04, streamFile); + + audio_offset += 0x08; + + for (i = 0; i < vgmstream->channels; i++) { + off_t coef_offset = audio_offset + i*0x20; + off_t hist_offset = audio_offset + vgmstream->channels*0x20 + i*0x04; + off_t data_offset = audio_offset + vgmstream->channels*0x24 + i*vgmstream->current_block_size; + + for (j = 0; j < 16; j++) { + vgmstream->ch[i].adpcm_coef[j] = read_16bitBE(coef_offset + (j*0x02),streamFile); + } + vgmstream->ch[i].adpcm_history1_16 = read_16bitBE(hist_offset + 0x00,streamFile); + vgmstream->ch[i].adpcm_history2_16 = read_16bitBE(hist_offset + 0x02,streamFile); + vgmstream->ch[i].offset = data_offset; + } } diff --git a/src/meta/thp.c b/src/meta/thp.c index 593f94ac..b6d71364 100644 --- a/src/meta/thp.c +++ b/src/meta/thp.c @@ -1,103 +1,100 @@ #include "meta.h" #include "../layout/layout.h" -#include "../util.h" -/* THP (Just play audio from .thp movie file) - by fastelbja */ - -VGMSTREAM * init_vgmstream_thp(STREAMFILE *streamFile) { - - VGMSTREAM * vgmstream = NULL; - - char filename[PATH_LIMIT]; - off_t start_offset; - - uint32_t maxAudioSize=0; - - uint32_t numComponents; - off_t componentTypeOffset; - off_t componentDataOffset; - - char thpVersion; - - int loop_flag; - int channel_count=-1; +/* THP - Nintendo movie format found in GC/Wii games */ +VGMSTREAM* init_vgmstream_thp(STREAMFILE *streamFile) { + VGMSTREAM *vgmstream = NULL; + off_t start_offset, component_type_offset, component_data_offset; + uint32_t version, max_audio_size; + int num_components; + int loop_flag, channel_count; int i; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("thp",filename_extension(filename)) && - strcasecmp("dsp",filename_extension(filename))) goto fail; - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x54485000) + /* checks */ + /* .thp: actual extension + * .dsp: fake extension? + * (extensionless): Fragile (Wii) */ + if (!check_extensions(streamFile, "thp,dsp,")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x54485000) /* "THP\0" */ goto fail; - maxAudioSize = read_32bitBE(0x0C,streamFile); - thpVersion = read_8bit(0x06,streamFile); + version = read_32bitBE(0x04,streamFile); /* 16b+16b major/minor */ + /* 0x08: max buffer size */ + max_audio_size = read_32bitBE(0x0C,streamFile); + /* 0x10: fps in float */ + /* 0x14: block count */ + /* 0x18: first block size */ + /* 0x1c: data size */ - if(maxAudioSize==0) // no sound + if (version != 0x00010000 && version != 0x00011000) /* v1.0 (~2002) or v1.1 (rest) */ + goto fail; + if (max_audio_size == 0) /* no sound */ goto fail; - loop_flag = 0; // allways unloop + component_type_offset = read_32bitBE(0x20,streamFile); + /* 0x24: block offsets table offset (optional, for seeking) */ + start_offset = read_32bitBE(0x28,streamFile); + /* 0x2c: last block offset */ - /* fill in the vital statistics */ - if(thpVersion==0x10) { - start_offset = read_32bitBE(0x24,streamFile); - /* No idea what's up with this */ - if (start_offset == 0) - start_offset = read_32bitBE(0x28,streamFile); - } else - start_offset = read_32bitBE(0x28,streamFile); + /* first component "type" x16 then component headers */ + num_components = read_32bitBE(component_type_offset,streamFile); + component_type_offset += 0x04; + component_data_offset = component_type_offset + 0x10; - // Get info from the first block - componentTypeOffset = read_32bitBE(0x20,streamFile); - numComponents = read_32bitBE(componentTypeOffset ,streamFile); - componentDataOffset=componentTypeOffset+0x14; - componentTypeOffset+=4; + /* parse "component" (data that goes into blocks) */ + for (i = 0; i < num_components; i++) { + int type = read_8bit(component_type_offset + i,streamFile); - for(i=0;ichannels=channel_count; - vgmstream->sample_rate=read_32bitBE(componentDataOffset+4,streamFile); - vgmstream->num_samples=read_32bitBE(componentDataOffset+8,streamFile); - break; - } else { - if(thpVersion==0x10) - componentDataOffset+=0x0c; + if (type == 0x00) { /* video */ + if (version == 0x00010000) + component_data_offset += 0x08; /* width + height */ else - componentDataOffset+=0x08; + component_data_offset += 0x0c; /* width + height + format? */ + } + else if (type == 0x01) { /* audio */ + /* parse below */ +#if 0 + if (version == 0x00010000) + component_data_offset += 0x0c; /* channels + sample rate + samples */ + else + component_data_offset += 0x10; /* channels + sample rate + samples + format? */ +#endif + break; + } + else { /* 0xFF / no data (reserved as THP is meant to be extensible) */ + goto fail; } } - /* 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;ich[i].streamfile = file; - } - } + /* official docs remark original's audio is adjusted to match GC's hardware rate + * (48000 > 48043 / 32000 > 32028), not sure if that means ouput sample rate should + * adjusted, but we can't detect Wii (non adjusted) .thp tho */ - vgmstream->full_block_size = read_32bitBE(0x18,streamFile); /* block size of current block, changes every time */ - block_update_thp(start_offset,vgmstream); + loop_flag = 0; + channel_count = read_32bitBE(component_data_offset + 0x00,streamFile); + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = read_32bitBE(component_data_offset + 0x04,streamFile); + vgmstream->num_samples = read_32bitBE(component_data_offset + 0x08,streamFile); + + vgmstream->meta_type = meta_THP; vgmstream->coding_type = coding_NGC_DSP; vgmstream->layout_type = layout_blocked_thp; - vgmstream->meta_type = meta_THP; + /* coefs are in every block */ + vgmstream->full_block_size = read_32bitBE(0x18,streamFile); /* next block size */ + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; }