Clean mib and add PS-ADPCM check

This commit is contained in:
bnnm 2018-04-29 21:05:30 +02:00
parent d292531654
commit fb63ad4aae

View File

@ -1,362 +1,338 @@
#include "meta.h"
#include "../util.h"
/* MIB
static int check_psadpcm(STREAMFILE *streamFile);
PS2 MIB format is a headerless format.
The interleave value can be found by checking the body of the data.
The interleave start allways at offset 0 with a int value (which can have
many values : 0x0000, 0x0002, 0x0006 etc...) follow by 12 empty (zero) values.
The interleave value is the offset where you found the same 16 bytes.
The n° of channels can be found by checking each time you found this 16 bytes.
The interleave value can be very "large" (up to 0x20000 found so far) and is allways
a 0x10 multiply value.
The loop values can be found by checking the 'tags' offset (found @ 0x02 each 0x10 bytes).
06 = start of the loop point (can be found for each channel)
03 - end of the loop point (can be found for each channel)
The .MIH header contains all informations about frequency, numbers of channels, interleave
but has, afaik, no loop values.
known extensions : MIB (MIH for the header) MIC (concatenation of MIB+MIH)
Nota : the MIC stuff is not supported here as there is
another MIC format which can be found in Koei Games.
2008-05-14 - Fastelbja : First version ...
2008-05-20 - Fastelbja : Fix loop value when loopEnd==0
*/
/* MIB+MIH - from namCollection: Ace Combat 2 (PS2), Rampage - Total Destruction (PS2) */
/* headerless PS-ADPCM - from Katamary Damacy (PS2), Air (PS2), Aladdin: Nasira's Revenge (PS1)
* (guesses interleave and channels by testing data and using the file extension, and finds
* loops in PS-ADPCM flags; this is a crutch for convenience, consider using GENH/TXTH instead). */
VGMSTREAM * init_vgmstream_ps2_mib(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
VGMSTREAM * vgmstream = NULL;
STREAMFILE * streamFileMIH = NULL;
off_t start_offset = 0x00;
char filename[PATH_LIMIT];
uint8_t mibBuffer[0x10];
uint8_t testBuffer[0x10];
uint8_t doChannelUpdate=1;
uint8_t bDoUpdateInterleave=1;
size_t fileLength;
off_t loopStart = 0;
off_t loopEnd = 0;
uint8_t mibBuffer[0x10];
uint8_t testBuffer[0x10];
off_t interleave = 0;
size_t fileLength;
off_t loopStart = 0;
off_t loopEnd = 0;
off_t interleave = 0;
off_t readOffset = 0;
off_t readOffset = 0;
char filenameMIH[PATH_LIMIT];
off_t loopStartPoints[0x10];
int loopStartPointsCount=0;
off_t loopStartPoints[0x10] = {0};
int loopStartPointsCount=0;
off_t loopEndPoints[0x10] = {0};
int loopEndPointsCount=0;
int loopToEnd=0;
int forceNoLoop=0;
int gotEmptyLine=0;
off_t loopEndPoints[0x10];
int loopEndPointsCount=0;
uint8_t gotMIH=0;
int loopToEnd=0;
int forceNoLoop=0;
int gotEmptyLine=0;
int i, channel_count=0;
uint8_t gotMIH=0;
int i, channel_count=0;
// Initialize loop point to 0
for(i=0; i<0x10; i++) {
loopStartPoints[i]=0;
loopEndPoints[i]=0;
}
/* check extension, case insensitive */
/* checks
* .mib: common, but many ext-less files are renamed to this.
* .mi4: fake .mib to force another sample rate
* .cvs: Aladdin - Nasira's Revenge (PS1)
* .snds: The Incredibles (PS2)
* .vb: Tantei Jinguuji Saburo - Mikan no Rupo (PS1)
* .xag: Hagane no Renkinjutsushi - Dream Carnival (PS2)
* */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("cvs",filename_extension(filename)) &&
strcasecmp("mib",filename_extension(filename)) &&
strcasecmp("mi4",filename_extension(filename)) &&
strcasecmp("snds",filename_extension(filename))&&
strcasecmp("vb",filename_extension(filename)) &&
strcasecmp("xag",filename_extension(filename))) goto fail;
strcasecmp("xag",filename_extension(filename)))
goto fail;
/* check for .MIH file */
strcpy(filenameMIH,filename);
strcpy(filenameMIH+strlen(filenameMIH)-3,"MIH");
/* test if raw PS-ADPCM */
if (!check_psadpcm(streamFile))
goto fail;
streamFileMIH = streamFile->open(streamFile,filenameMIH,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (streamFileMIH) gotMIH = 1;
/* Search for interleave value & loop points */
/* Get the first 16 values */
fileLength = get_streamfile_size(streamFile);
readOffset+=(off_t)read_streamfile(mibBuffer,0,0x10,streamFile);
readOffset=0;
mibBuffer[0]=0;
/* .MIB may come with a .MIH header file */
if (strcasecmp("mib",filename_extension(filename))==0) {
streamFileMIH = open_streamfile_by_ext(streamFile,"mih");
if (streamFileMIH)
gotMIH = 1;
}
do {
readOffset+=(off_t)read_streamfile(testBuffer,readOffset,0x10,streamFile);
// be sure to point to an interleave value
if(readOffset<(int32_t)(fileLength*0.5)) {
if(memcmp(testBuffer+2, mibBuffer+2,0x0e)) {
if(doChannelUpdate) {
doChannelUpdate=0;
channel_count++;
}
if(channel_count<2)
bDoUpdateInterleave=1;
}
fileLength = get_streamfile_size(streamFile);
testBuffer[0]=0;
if(!memcmp(testBuffer,mibBuffer,0x10)) {
gotEmptyLine=1;
if(bDoUpdateInterleave) {
bDoUpdateInterleave=0;
interleave=readOffset-0x10;
}
if(((readOffset-0x10)==(channel_count*interleave))) {
doChannelUpdate=1;
}
}
}
// Loop Start ...
if(testBuffer[0x01]==0x06)
{
if(loopStartPointsCount<0x10)
{
loopStartPoints[loopStartPointsCount] = readOffset-0x10;
loopStartPointsCount++;
}
}
// Loop End ...
if((testBuffer[0x01]==0x03) && (testBuffer[0x03]!=0x77)) {
if(loopEndPointsCount<0x10)
{
loopEndPoints[loopEndPointsCount] = readOffset;
loopEndPointsCount++;
}
}
if(testBuffer[0x01]==0x04)
{
// 0x04 loop points flag can't be with a 0x03 loop points flag
if(loopStartPointsCount<0x10)
{
loopStartPoints[loopStartPointsCount] = readOffset-0x10;
loopStartPointsCount++;
// Loop end value is not set by flags ...
// go until end of file
loopToEnd=1;
}
}
} while (streamFile->get_offset(streamFile)<((int32_t)fileLength));
if((testBuffer[0]==0x0c) && (testBuffer[1]==0))
forceNoLoop=1;
if(channel_count==0)
channel_count=1;
if(gotMIH)
channel_count=read_32bitLE(0x08,streamFileMIH);
// force no loop
if(!strcasecmp("vb",filename_extension(filename)))
loopStart=0;
if(!strcasecmp("xag",filename_extension(filename)))
channel_count=2;
// Calc Loop Points & Interleave ...
if(loopStartPointsCount>=2)
{
// can't get more then 0x10 loop point !
if(loopStartPointsCount<=0x0F) {
// Always took the first 2 loop points
interleave=loopStartPoints[1]-loopStartPoints[0];
loopStart=loopStartPoints[1];
// Can't be one channel .mib with interleave values
if((interleave>0) && (channel_count==1))
channel_count=2;
} else
loopStart=0;
}
if(loopEndPointsCount>=2)
{
// can't get more then 0x10 loop point !
if(loopEndPointsCount<=0x0F) {
// No need to recalculate interleave value ...
loopEnd=loopEndPoints[loopEndPointsCount-1];
// Can't be one channel .mib with interleave values
if(channel_count==1) channel_count=2;
} else {
loopToEnd=0;
loopEnd=0;
}
}
if (loopToEnd)
loopEnd=fileLength;
// force no loop
if(forceNoLoop)
loopEnd=0;
if((interleave>0x10) && (channel_count==1))
channel_count=2;
if(interleave==0) interleave=0x10;
// further check on channel_count ...
if(gotEmptyLine)
{
int newChannelCount = 0;
readOffset=0;
do
{
newChannelCount++;
read_streamfile(testBuffer,readOffset,0x10,streamFile);
readOffset+=interleave;
} while(!memcmp(testBuffer,mibBuffer,16));
newChannelCount--;
if(newChannelCount>channel_count)
channel_count=newChannelCount;
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,(loopEnd!=0));
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
if(gotMIH) {
// Read stuff from the MIH file
vgmstream->channels = read_32bitLE(0x08,streamFileMIH);
vgmstream->sample_rate = read_32bitLE(0x0C,streamFileMIH);
vgmstream->interleave_block_size = read_32bitLE(0x10,streamFileMIH);
vgmstream->num_samples=((read_32bitLE(0x10,streamFileMIH)*
(read_32bitLE(0x14,streamFileMIH)-1)*2)+
((read_32bitLE(0x04,streamFileMIH)>>8)*2))/16*28/2;
} else {
vgmstream->channels = channel_count;
vgmstream->interleave_block_size = interleave;
if(!strcasecmp("mib",filename_extension(filename)))
vgmstream->sample_rate = 44100;
if(!strcasecmp("mi4",filename_extension(filename)))
vgmstream->sample_rate = 48000;
//Heavy Iron Studios SNDS (The Incredibles)
//Do a bogus check to avoid clashing with PC_SNDS IMA
if(!strcasecmp("snds", filename_extension(filename)) &&
(read_32bitBE(0x0, streamFile) == 0x00000000))
vgmstream->sample_rate = 48000;
if(!strcasecmp("xag",filename_extension(filename))) {
vgmstream->channels=2;
vgmstream->sample_rate = 44100;
}
if (!strcasecmp("cvs", filename_extension(filename)) ||
!strcasecmp("vb",filename_extension(filename)))
{
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size=0;
vgmstream->sample_rate = 22050;
vgmstream->channels = 1;
}
vgmstream->num_samples = (int32_t)(fileLength/16/channel_count*28);
}
if(loopEnd!=0) {
if(vgmstream->channels==1) {
vgmstream->loop_start_sample = loopStart/16*18;
vgmstream->loop_end_sample = loopEnd/16*28;
} else {
vgmstream->loop_start_sample = ((((loopStart/vgmstream->interleave_block_size)-1)*vgmstream->interleave_block_size)/16*14*channel_count)/channel_count;
if(loopStart%vgmstream->interleave_block_size) {
vgmstream->loop_start_sample += (((loopStart%vgmstream->interleave_block_size)-1)/16*14*channel_count);
}
if(loopEnd==fileLength)
{
vgmstream->loop_end_sample=(loopEnd/16*28)/channel_count;
} else {
vgmstream->loop_end_sample = ((((loopEnd/vgmstream->interleave_block_size)-1)*vgmstream->interleave_block_size)/16*14*channel_count)/channel_count;
if(loopEnd%vgmstream->interleave_block_size) {
vgmstream->loop_end_sample += (((loopEnd%vgmstream->interleave_block_size)-1)/16*14*channel_count);
}
}
}
}
if(loopToEnd)
{
// try to find if there's no empty line ...
int emptySamples=0;
for(i=0; i<16;i++) {
mibBuffer[i]=0;
}
readOffset=fileLength-0x10;
do {
read_streamfile(testBuffer,readOffset,0x10,streamFile);
if(!memcmp(mibBuffer,testBuffer,16))
{
emptySamples+=28;
}
readOffset-=0x10;
} while(!memcmp(testBuffer,mibBuffer,16));
vgmstream->loop_end_sample-=(emptySamples*channel_count);
}
vgmstream->meta_type = meta_PS2_MIB;
if (gotMIH) {
vgmstream->meta_type = meta_PS2_MIB_MIH;
close_streamfile(streamFileMIH); streamFileMIH=NULL;
}
/* open the file for reading by each channel */
/* Search for interleave value (checking channel starts) and loop points (using PS-ADPCM flags).
* Channel start will by 0x0000, 0x0002, 0x0006 followed by 12 zero values.
* Interleave value is the offset where those repeat, and channels the number of times.
* Loop flags in second byte are: 0x06 = start, 0x03 = end (per channel).
* Interleave can be large (up to 0x20000 found so far) and is always a 0x10 multiple value. */
readOffset+=(off_t)read_streamfile(mibBuffer,0,0x10,streamFile);
mibBuffer[0]=0;
{
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,0x8000);
uint8_t doChannelUpdate=1;
uint8_t bDoUpdateInterleave=1;
if (!vgmstream->ch[i].streamfile) goto fail;
readOffset=0;
do {
readOffset+=(off_t)read_streamfile(testBuffer,readOffset,0x10,streamFile);
// be sure to point to an interleave value
if(readOffset<(int32_t)(fileLength*0.5)) {
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=i*vgmstream->interleave_block_size;
if(memcmp(testBuffer+2, mibBuffer+2,0x0e)) {
if(doChannelUpdate) {
doChannelUpdate=0;
channel_count++;
}
if(channel_count<2)
bDoUpdateInterleave=1;
}
testBuffer[0]=0;
if(!memcmp(testBuffer,mibBuffer,0x10)) {
gotEmptyLine=1;
if(bDoUpdateInterleave) {
bDoUpdateInterleave=0;
interleave=readOffset-0x10;
}
if(readOffset-0x10 == channel_count*interleave) {
doChannelUpdate=1;
}
}
}
// Loop Start ...
if(testBuffer[0x01]==0x06) {
if(loopStartPointsCount<0x10) {
loopStartPoints[loopStartPointsCount] = readOffset-0x10;
loopStartPointsCount++;
}
}
// Loop End ...
if(testBuffer[0x01]==0x03 && testBuffer[0x03]!=0x77) {
if(loopEndPointsCount<0x10) {
loopEndPoints[loopEndPointsCount] = readOffset;
loopEndPointsCount++;
}
}
if(testBuffer[0x01]==0x04) {
// 0x04 loop points flag can't be with a 0x03 loop points flag
if(loopStartPointsCount<0x10) {
loopStartPoints[loopStartPointsCount] = readOffset-0x10;
loopStartPointsCount++;
// Loop end value is not set by flags ...
// go until end of file
loopToEnd=1;
}
}
} while (streamFile->get_offset(streamFile)<((int32_t)fileLength));
}
if(testBuffer[0]==0x0c && testBuffer[1]==0)
forceNoLoop=1;
if(channel_count==0)
channel_count=1;
if(gotMIH)
channel_count=read_32bitLE(0x08,streamFileMIH);
// force no loop
if(!strcasecmp("vb",filename_extension(filename)))
loopStart=0;
if(!strcasecmp("xag",filename_extension(filename)))
channel_count=2;
// Calc Loop Points & Interleave ...
if(loopStartPointsCount>=2) {
// can't get more then 0x10 loop point !
if(loopStartPointsCount<=0x0F) {
// Always took the first 2 loop points
interleave=loopStartPoints[1]-loopStartPoints[0];
loopStart=loopStartPoints[1];
// Can't be one channel .mib with interleave values
if(interleave>0 && channel_count==1)
channel_count=2;
} else {
loopStart=0;
}
}
if(loopEndPointsCount>=2) {
// can't get more then 0x10 loop point !
if(loopEndPointsCount<=0x0F) {
// No need to recalculate interleave value ...
loopEnd=loopEndPoints[loopEndPointsCount-1];
// Can't be one channel .mib with interleave values
if(channel_count==1)
channel_count=2;
} else {
loopToEnd=0;
loopEnd=0;
}
}
if (loopToEnd)
loopEnd=fileLength;
if(forceNoLoop)
loopEnd=0;
if(interleave>0x10 && channel_count==1)
channel_count=2;
if(interleave==0)
interleave=0x10;
// further check on channel_count ...
if(gotEmptyLine) {
int newChannelCount = 0;
readOffset=0;
/* count empty lines at interleave = channels */
do {
newChannelCount++;
read_streamfile(testBuffer,readOffset,0x10,streamFile);
readOffset+=interleave;
} while(!memcmp(testBuffer,mibBuffer,16));
newChannelCount--;
if(newChannelCount>channel_count)
channel_count=newChannelCount;
}
if (!strcasecmp("cvs", filename_extension(filename)) ||
!strcasecmp("vb",filename_extension(filename)))
channel_count=1;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,(loopEnd!=0));
if (!vgmstream) goto fail;
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = (channel_count == 1) ? layout_none : layout_interleave;
if(gotMIH) {
// Read stuff from the MIH file
vgmstream->sample_rate = read_32bitLE(0x0C,streamFileMIH);
vgmstream->interleave_block_size = read_32bitLE(0x10,streamFileMIH);
vgmstream->num_samples=((read_32bitLE(0x10,streamFileMIH)*
(read_32bitLE(0x14,streamFileMIH)-1)*2)+
((read_32bitLE(0x04,streamFileMIH)>>8)*2))/16*28/2;
} else {
vgmstream->interleave_block_size = interleave;
if(!strcasecmp("mib",filename_extension(filename)))
vgmstream->sample_rate = 44100;
if(!strcasecmp("mi4",filename_extension(filename)))
vgmstream->sample_rate = 48000;
if(!strcasecmp("snds", filename_extension(filename)))
vgmstream->sample_rate = 48000;
if(!strcasecmp("xag",filename_extension(filename)))
vgmstream->sample_rate = 44100;
if (!strcasecmp("cvs", filename_extension(filename)) ||
!strcasecmp("vb",filename_extension(filename)))
vgmstream->sample_rate = 22050;
vgmstream->num_samples = (int32_t)(fileLength/16/channel_count*28);
}
if(loopEnd!=0) {
if(vgmstream->channels==1) {
vgmstream->loop_start_sample = loopStart/16*18; //todo 18 instead of 28 probably a bug
vgmstream->loop_end_sample = loopEnd/16*28;
} else {
vgmstream->loop_start_sample = ((((loopStart/vgmstream->interleave_block_size)-1)*vgmstream->interleave_block_size)/16*14*channel_count)/channel_count;
if(loopStart%vgmstream->interleave_block_size) {
vgmstream->loop_start_sample += (((loopStart%vgmstream->interleave_block_size)-1)/16*14*channel_count);
}
if(loopEnd==fileLength) {
vgmstream->loop_end_sample=(loopEnd/16*28)/channel_count;
} else {
vgmstream->loop_end_sample = ((((loopEnd/vgmstream->interleave_block_size)-1)*vgmstream->interleave_block_size)/16*14*channel_count)/channel_count;
if(loopEnd%vgmstream->interleave_block_size) {
vgmstream->loop_end_sample += (((loopEnd%vgmstream->interleave_block_size)-1)/16*14*channel_count);
}
}
}
}
if(loopToEnd) {
// try to find if there's no empty line ...
int emptySamples=0;
for(i=0; i<16;i++) {
mibBuffer[i]=0; //memset
}
readOffset=fileLength-0x10;
do {
read_streamfile(testBuffer,readOffset,0x10,streamFile);
if(!memcmp(mibBuffer,testBuffer,16)) {
emptySamples+=28;
}
readOffset-=0x10;
} while(!memcmp(testBuffer,mibBuffer,16));
vgmstream->loop_end_sample-=(emptySamples*channel_count);
}
vgmstream->meta_type = gotMIH ? meta_PS2_MIB_MIH : meta_PS2_MIB;
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
close_streamfile(streamFileMIH);
return vgmstream;
/* clean up anything we may have opened */
fail:
if (streamFileMIH) close_streamfile(streamFileMIH);
if (vgmstream) close_vgmstream(vgmstream);
close_streamfile(streamFileMIH);
close_vgmstream(vgmstream);
return NULL;
}
/* tests some PS-ADPCM frames */
static int check_psadpcm(STREAMFILE *streamFile) {
off_t offset, max_offset;
max_offset = get_streamfile_size(streamFile);
if (max_offset > 0x2000)
max_offset = 0x2000;
offset = 0x00;
while (offset < max_offset) {
uint8_t predictor = (read_8bit(offset+0x00,streamFile) >> 4) & 0x0f;
uint8_t flags = read_8bit(offset+0x01,streamFile);
if (predictor > 5 || flags > 7)
goto fail;
offset += 0x10;
}
return 1;
fail:
return 0;
}