diff --git a/src/meta/meta.h b/src/meta/meta.h index cd7a78c7..55c4d26a 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -99,9 +99,8 @@ typedef struct { meta_t meta_type; layout_t layout_type; - /* XOR setup (SCD) */ - int decryption_enabled; - void (*decryption_callback)(void *ptr, size_t size, size_t nmemb, void *datasource, int bytes_read); + /* decryption setup */ + void (*decryption_callback)(void *ptr, size_t size, size_t nmemb, void *datasource); uint8_t scd_xor; off_t scd_xor_length; diff --git a/src/meta/ogg_vorbis_file.c b/src/meta/ogg_vorbis_file.c index 02aaade4..64b2a122 100644 --- a/src/meta/ogg_vorbis_file.c +++ b/src/meta/ogg_vorbis_file.c @@ -1,100 +1,23 @@ #include "../vgmstream.h" #ifdef VGM_USE_VORBIS - #include #include #include "meta.h" -#include "../util.h" #include +#define OGG_DEFAULT_BITSTREAM 0 -#define DEFAULT_BITSTREAM 0 - -static size_t read_func(void *ptr, size_t size, size_t nmemb, void * datasource) -{ +static size_t ov_read_func(void *ptr, size_t size, size_t nmemb, void * datasource) { ogg_vorbis_streamfile * const ov_streamfile = datasource; - size_t items_read; - - size_t bytes_read; - - bytes_read = read_streamfile(ptr, ov_streamfile->offset + ov_streamfile->other_header_bytes, size * nmemb, - ov_streamfile->streamfile); - - items_read = bytes_read / size; - - ov_streamfile->offset += items_read * size; - - return items_read; -} - -static size_t read_func_um3(void *ptr, size_t size, size_t nmemb, void * datasource) -{ - ogg_vorbis_streamfile * const ov_streamfile = datasource; - size_t items_read; - - size_t bytes_read; - - bytes_read = read_streamfile(ptr, ov_streamfile->offset + ov_streamfile->other_header_bytes, size * nmemb, - ov_streamfile->streamfile); - - items_read = bytes_read / size; - - /* first 0x800 bytes of um3 are xor'd with 0xff */ - if (ov_streamfile->offset < 0x800) { - int num_crypt = 0x800-ov_streamfile->offset; - int i; - - if (num_crypt > bytes_read) num_crypt=bytes_read; - for (i=0;ioffset += items_read * size; - - return items_read; -} - -static size_t read_func_kovs(void *ptr, size_t size, size_t nmemb, void * datasource) -{ - ogg_vorbis_streamfile * const ov_streamfile = datasource; - size_t items_read; - - size_t bytes_read; - - bytes_read = read_streamfile(ptr, ov_streamfile->offset+ov_streamfile->other_header_bytes, size * nmemb, - ov_streamfile->streamfile); - - items_read = bytes_read / size; - - /* first 0x100 bytes of KOVS are xor'd with offset */ - if (ov_streamfile->offset < 0x100) { - int i; - - for (i=ov_streamfile->offset;i<0x100;i++) - ((uint8_t*)ptr)[i-ov_streamfile->offset] ^= i; - } - - ov_streamfile->offset += items_read * size; - - return items_read; -} - -static size_t read_func_scd(void *ptr, size_t size, size_t nmemb, void * datasource) -{ - ogg_vorbis_streamfile * const ov_streamfile = datasource; - size_t items_read; - - size_t bytes_read; - - bytes_read = read_streamfile(ptr, ov_streamfile->offset + ov_streamfile->other_header_bytes, size * nmemb, - ov_streamfile->streamfile); + size_t bytes_read, items_read; + bytes_read = read_streamfile(ptr, ov_streamfile->offset + ov_streamfile->other_header_bytes, size * nmemb, ov_streamfile->streamfile); items_read = bytes_read / size; /* may be encrypted */ - if (ov_streamfile->decryption_enabled) { - ov_streamfile->decryption_callback(ptr, size, nmemb, ov_streamfile, bytes_read); + if (ov_streamfile->decryption_callback) { + ov_streamfile->decryption_callback(ptr, size, items_read, ov_streamfile); } ov_streamfile->offset += items_read * size; @@ -102,32 +25,9 @@ static size_t read_func_scd(void *ptr, size_t size, size_t nmemb, void * datasou return items_read; } -static size_t read_func_psych(void *ptr, size_t size, size_t nmemb, void * datasource) -{ +static int ov_seek_func(void *datasource, ogg_int64_t offset, int whence) { ogg_vorbis_streamfile * const ov_streamfile = datasource; - size_t items_read; - - size_t bytes_read; - size_t i; - - bytes_read = read_streamfile(ptr, ov_streamfile->offset + ov_streamfile->other_header_bytes, size * nmemb, - ov_streamfile->streamfile); - - /* add 0x23 ('#') */ - for (i=0;ioffset += items_read * size; - - return items_read; -} - -static int seek_func(void *datasource, ogg_int64_t offset, int whence) { - ogg_vorbis_streamfile * const ov_streamfile = datasource; - ogg_int64_t base_offset; - ogg_int64_t new_offset; + ogg_int64_t base_offset, new_offset; switch (whence) { case SEEK_SET: @@ -144,107 +44,141 @@ static int seek_func(void *datasource, ogg_int64_t offset, int whence) { break; } + new_offset = base_offset + offset; if (new_offset < 0 || new_offset > (ov_streamfile->size - ov_streamfile->other_header_bytes)) { - return -1; + return -1; /* *must* return -1 if stream is unseekable */ } else { ov_streamfile->offset = new_offset; return 0; } } -static long tell_func(void * datasource) { +static long ov_tell_func(void * datasource) { ogg_vorbis_streamfile * const ov_streamfile = datasource; return ov_streamfile->offset; } -/* setting close_func in ov_callbacks to NULL doesn't seem to work */ -static int close_func(void * datasource) { +static int ov_close_func(void * datasource) { + /* needed as setting ov_close_func in ov_callbacks to NULL doesn't seem to work + * (closing the streamfile is done in free_ogg_vorbis) */ return 0; } -/* Ogg Vorbis, by way of libvorbisfile */ +static void um3_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) { + size_t bytes_read = size*nmemb; + ogg_vorbis_streamfile * const ov_streamfile = datasource; + int i; + + /* first 0x800 bytes are xor'd with 0xff */ + if (ov_streamfile->offset < 0x800) { + int num_crypt = 0x800 - ov_streamfile->offset; + if (num_crypt > bytes_read) + num_crypt = bytes_read; + + for (i = 0; i < num_crypt; i++) + ((uint8_t*)ptr)[i] ^= 0xff; + } +} + +static void kovs_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) { + size_t bytes_read = size*nmemb; + ogg_vorbis_streamfile * const ov_streamfile = datasource; + int i; + + /* first 0x100 bytes are xor'd with offset */ + if (ov_streamfile->offset < 0x100) { + int max_offset = ov_streamfile->offset + bytes_read; + if (max_offset > 0x100) + max_offset = 0x100; + + for (i = ov_streamfile->offset; i < max_offset; i++) { + ((uint8_t*)ptr)[i-ov_streamfile->offset] ^= i; + } + } +} + +static void psychic_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) { + size_t bytes_read = size*nmemb; + int i; + + /* add 0x23 ('#') */ + { + for (i = 0; i < bytes_read; i++) + ((uint8_t*)ptr)[i] += 0x23; + } +} + + +/* Ogg Vorbis, by way of libvorbisfile; may contain loop comments */ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { char filename[PATH_LIMIT]; + vgm_vorbis_info_t inf = {0}; + off_t start_offset = 0; - ov_callbacks callbacks; + int is_ogg = 0; + int is_um3 = 0; + int is_kovs = 0; + int is_psychic = 0; - off_t other_header_bytes = 0; - int um3_ogg = 0; - int kovs_ogg = 0; - int psych_ogg = 0; - vgm_vorbis_info_t inf; - memset(&inf, 0, sizeof(inf)); - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - - /* It is only interesting to use oggs with vgmstream if they are looped. - To prevent such files from being played by other plugins and such they - may be renamed to .logg. This meta reader should still support .ogg, - though. */ - if (strcasecmp("logg",filename_extension(filename)) && - strcasecmp("ogg",filename_extension(filename))) { - if (!strcasecmp("um3",filename_extension(filename))) { - um3_ogg = 1; - } else if (check_extensions(streamFile,"kvs,kovs")) { /* .kvs: Atelier Sophie, kovs: header id only? */ - kovs_ogg = 1; - } else { - goto fail; - } - } - - /* not all um3-ogg are crypted */ - if (um3_ogg && read_32bitBE(0x0,streamFile)==0x4f676753) { - um3_ogg = 0; - } - - /* use KOVS header */ - if (kovs_ogg) { - if (read_32bitBE(0x0,streamFile)!=0x4b4f5653) { /* "KOVS" */ - goto fail; - } - if (read_32bitLE(0x8,streamFile)!=0) { - inf.loop_start = read_32bitLE(0x8,streamFile); - inf.loop_flag = 1; - } - - other_header_bytes = 0x20; - } - - /* detect Psychic Software obfuscation (as seen in "Darkwind") */ - if (read_32bitBE(0x0,streamFile)==0x2c444430) { - psych_ogg = 1; - } - - if (um3_ogg) { - callbacks.read_func = read_func_um3; - } else if (kovs_ogg) { - callbacks.read_func = read_func_kovs; - } else if (psych_ogg) { - callbacks.read_func = read_func_psych; + /* check extension */ + if (check_extensions(streamFile,"ogg,logg")) { /* .ogg: standard/psychic, .logg: renamed for plugins */ + is_ogg = 1; + } else if (check_extensions(streamFile,"um3")) { + is_um3 = 1; + } else if (check_extensions(streamFile,"kvs,kovs")) { /* .kvs: Atelier Sophie (PC), kovs: header id only? */ + is_kovs = 1; } else { - callbacks.read_func = read_func; + goto fail; } - callbacks.seek_func = seek_func; - callbacks.close_func = close_func; - callbacks.tell_func = tell_func; + streamFile->get_name(streamFile,filename,sizeof(filename)); - if (um3_ogg) { + /* check standard Ogg Vorbis */ + if (is_ogg) { + + /* check Psychic Software obfuscation (Darkwind: War on Wheels PC) */ + if (read_32bitBE(0x00,streamFile) == 0x2c444430) { + is_psychic = 1; + inf.decryption_callback = psychic_ogg_decryption_callback; + } + else if (read_32bitBE(0x00,streamFile) != 0x4f676753) { /* "OggS" */ + goto fail; /* not known (ex. Wwise) */ + } + } + + /* check "Ultramarine3" (???), may be encrypted */ + if (is_um3) { + if (read_32bitBE(0x00,streamFile) != 0x4f676753) { /* "OggS" */ + inf.decryption_callback = um3_ogg_decryption_callback; + } + } + + /* check KOVS (Koei Tecmo games), encrypted and has an actual header */ + if (is_kovs) { + if (read_32bitBE(0x00,streamFile) != 0x4b4f5653) { /* "KOVS" */ + goto fail; + } + inf.loop_start = read_32bitLE(0x08,streamFile); + inf.loop_flag = (inf.loop_start != 0); + inf.decryption_callback = kovs_ogg_decryption_callback; + + start_offset = 0x20; + } + + if (is_um3) { inf.meta_type = meta_OGG_UM3; - } else if (kovs_ogg) { + } else if (is_kovs) { inf.meta_type = meta_OGG_KOVS; - } else if (psych_ogg) { + } else if (is_psychic) { inf.meta_type = meta_OGG_PSYCH; } else { inf.meta_type = meta_OGG_VORBIS; } - inf.layout_type = layout_ogg_vorbis; - return init_vgmstream_ogg_vorbis_callbacks(streamFile, filename, &callbacks, other_header_bytes, &inf); + return init_vgmstream_ogg_vorbis_callbacks(streamFile, filename, NULL, start_offset, &inf); fail: return NULL; @@ -252,14 +186,9 @@ fail: VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, const char * filename, ov_callbacks *callbacks_p, off_t other_header_bytes, const vgm_vorbis_info_t *vgm_inf) { VGMSTREAM * vgmstream = NULL; - - OggVorbis_File temp_ovf; - ogg_vorbis_streamfile temp_streamfile; - ogg_vorbis_codec_data * data = NULL; - OggVorbis_File *ovf; - int inited_ovf = 0; - vorbis_info *info; + OggVorbis_File *ovf = NULL; + vorbis_info *vi; int loop_flag = vgm_inf->loop_flag; int32_t loop_start = vgm_inf->loop_start; @@ -271,158 +200,132 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, const ch ov_callbacks default_callbacks; if (!callbacks_p) { - default_callbacks.read_func = read_func; - default_callbacks.seek_func = seek_func; - default_callbacks.close_func = close_func; - default_callbacks.tell_func = tell_func; - - if (vgm_inf->decryption_enabled) { - default_callbacks.read_func = read_func_scd; - } + default_callbacks.read_func = ov_read_func; + default_callbacks.seek_func = ov_seek_func; + default_callbacks.close_func = ov_close_func; + default_callbacks.tell_func = ov_tell_func; callbacks_p = &default_callbacks; } - temp_streamfile.streamfile = streamFile; - temp_streamfile.offset = 0; - temp_streamfile.size = get_streamfile_size(temp_streamfile.streamfile); - temp_streamfile.other_header_bytes = other_header_bytes; - temp_streamfile.decryption_enabled = vgm_inf->decryption_enabled; - temp_streamfile.decryption_callback = vgm_inf->decryption_callback; - temp_streamfile.scd_xor = vgm_inf->scd_xor; - temp_streamfile.scd_xor_length = vgm_inf->scd_xor_length; + /* test if this is a proper Ogg Vorbis file, with the current (from init_x) STREAMFILE */ + { + OggVorbis_File temp_ovf; + ogg_vorbis_streamfile temp_streamfile; - /* can we open this as a proper ogg vorbis file? */ - memset(&temp_ovf, 0, sizeof(temp_ovf)); - if (ov_test_callbacks(&temp_streamfile, &temp_ovf, NULL, - 0, *callbacks_p)) goto fail; + temp_streamfile.streamfile = streamFile; + temp_streamfile.offset = 0; + temp_streamfile.size = get_streamfile_size(temp_streamfile.streamfile); + temp_streamfile.other_header_bytes = other_header_bytes; + temp_streamfile.decryption_callback = vgm_inf->decryption_callback; + temp_streamfile.scd_xor = vgm_inf->scd_xor; + temp_streamfile.scd_xor_length = vgm_inf->scd_xor_length; - /* we have to close this as it has the init_vgmstream meta-reading - STREAMFILE */ - ov_clear(&temp_ovf); + /* open the ogg vorbis file for testing */ + memset(&temp_ovf, 0, sizeof(temp_ovf)); + if (ov_test_callbacks(&temp_streamfile, &temp_ovf, NULL, 0, *callbacks_p)) + goto fail; - /* proceed to open a STREAMFILE just for this stream */ - data = calloc(1,sizeof(ogg_vorbis_codec_data)); - if (!data) goto fail; + /* we have to close this as it has the init_vgmstream meta-reading STREAMFILE */ + ov_clear(&temp_ovf); + } - data->ov_streamfile.streamfile = streamFile->open(streamFile,filename, - STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!data->ov_streamfile.streamfile) goto fail; - data->ov_streamfile.offset = 0; - data->ov_streamfile.size = get_streamfile_size(data->ov_streamfile.streamfile); - data->ov_streamfile.other_header_bytes = other_header_bytes; - data->ov_streamfile.decryption_enabled = vgm_inf->decryption_enabled; - data->ov_streamfile.decryption_callback = vgm_inf->decryption_callback; - data->ov_streamfile.scd_xor = vgm_inf->scd_xor; - data->ov_streamfile.scd_xor_length = vgm_inf->scd_xor_length; - /* open the ogg vorbis file for real */ - if (ov_open_callbacks(&data->ov_streamfile, &data->ogg_vorbis_file, NULL, - 0, *callbacks_p)) goto fail; - ovf = &data->ogg_vorbis_file; - inited_ovf = 1; + /* proceed to init codec_data and reopen a STREAMFILE for this stream */ + { + data = calloc(1,sizeof(ogg_vorbis_codec_data)); + if (!data) goto fail; - data->bitstream = DEFAULT_BITSTREAM; + data->ov_streamfile.streamfile = streamFile->open(streamFile,filename, STREAMFILE_DEFAULT_BUFFER_SIZE); + if (!data->ov_streamfile.streamfile) goto fail; - info = ov_info(ovf,DEFAULT_BITSTREAM); + data->ov_streamfile.offset = 0; + data->ov_streamfile.size = get_streamfile_size(data->ov_streamfile.streamfile); + data->ov_streamfile.other_header_bytes = other_header_bytes; + data->ov_streamfile.decryption_callback = vgm_inf->decryption_callback; + data->ov_streamfile.scd_xor = vgm_inf->scd_xor; + data->ov_streamfile.scd_xor_length = vgm_inf->scd_xor_length; - /* grab the comments */ + /* open the ogg vorbis file for real */ + if (ov_open_callbacks(&data->ov_streamfile, &data->ogg_vorbis_file, NULL, 0, *callbacks_p)) + goto fail; + ovf = &data->ogg_vorbis_file; + } + + /* get info from bitstream 0 */ + data->bitstream = OGG_DEFAULT_BITSTREAM; + vi = ov_info(ovf,OGG_DEFAULT_BITSTREAM); + + /* search for loop comments */ { int i; - vorbis_comment *comment; + vorbis_comment *comment = ov_comment(ovf,OGG_DEFAULT_BITSTREAM); - comment = ov_comment(ovf,DEFAULT_BITSTREAM); - - /* search for a "loop_start" comment */ - for (i=0;icomments;i++) { - if (strstr(comment->user_comments[i],"loop_start=")== - comment->user_comments[i] || - strstr(comment->user_comments[i],"LOOP_START=")== - comment->user_comments[i] || - strstr(comment->user_comments[i],"COMMENT=LOOPPOINT=")== - comment->user_comments[i] || - strstr(comment->user_comments[i],"LOOPSTART=")== - comment->user_comments[i] || - strstr(comment->user_comments[i],"um3.stream.looppoint.start=")== - comment->user_comments[i] || - strstr(comment->user_comments[i],"LOOP_BEGIN=")== - comment->user_comments[i] || - strstr(comment->user_comments[i],"LoopStart=")== - comment->user_comments[i] - ) { - loop_start=atol(strrchr(comment->user_comments[i],'=')+1); - if (loop_start >= 0) - loop_flag=1; + for (i = 0; i < comment->comments; i++) { + const char * user_comment = comment->user_comments[i]; + if (strstr(user_comment,"loop_start=")==user_comment || /* PSO4 */ + strstr(user_comment,"LOOP_START=")==user_comment || /* PSO4 */ + strstr(user_comment,"COMMENT=LOOPPOINT=")==user_comment || + strstr(user_comment,"LOOPSTART=")==user_comment || + strstr(user_comment,"um3.stream.looppoint.start=")==user_comment || + strstr(user_comment,"LOOP_BEGIN=")==user_comment || /* Hatsune Miku: Project Diva F (PS3) */ + strstr(user_comment,"LoopStart=")==user_comment) { /* Devil May Cry 4 (PC) */ + loop_start = atol(strrchr(user_comment,'=')+1); + loop_flag = (loop_start >= 0); } - else if (strstr(comment->user_comments[i],"LOOPLENGTH=")== - comment->user_comments[i]) { - loop_length=atol(strrchr(comment->user_comments[i],'=')+1); - loop_length_found=1; + else if (strstr(user_comment,"LOOPLENGTH=")==user_comment) {/* (LOOPSTART pair) */ + loop_length = atol(strrchr(user_comment,'=')+1); + loop_length_found = 1; } - else if (strstr(comment->user_comments[i],"title=-lps")== - comment->user_comments[i]) { - loop_start=atol(comment->user_comments[i]+10); - if (loop_start >= 0) - loop_flag=1; + else if (strstr(user_comment,"title=-lps")==user_comment) { /* Memories Off #5 (PC) */ + loop_start = atol(user_comment+10); + loop_flag = (loop_start >= 0); } - else if (strstr(comment->user_comments[i],"album=-lpe")== - comment->user_comments[i]) { - loop_end=atol(comment->user_comments[i]+10); - loop_flag=1; - loop_end_found=1; + else if (strstr(user_comment,"album=-lpe")==user_comment) { /* (title=-lps pair) */ + loop_end = atol(user_comment+10); + loop_flag = 1; + loop_end_found = 1; } - else if (strstr(comment->user_comments[i],"LoopEnd=")== - comment->user_comments[i]) { - if(loop_flag) { - loop_length=atol(strrchr(comment->user_comments[i],'=')+1)-loop_start; - loop_length_found=1; - } + else if (strstr(user_comment,"LoopEnd=")==user_comment) { /* (LoopStart pair) */ + if(loop_flag) { + loop_length = atol(strrchr(user_comment,'=')+1)-loop_start; + loop_length_found = 1; + } } - else if (strstr(comment->user_comments[i],"LOOP_END=")== - comment->user_comments[i]) { - if(loop_flag) { - loop_length=atol(strrchr(comment->user_comments[i],'=')+1)-loop_start; - loop_length_found=1; - } + else if (strstr(user_comment,"LOOP_END=")==user_comment) { /* (LOOP_BEGIN pair) */ + if(loop_flag) { + loop_length = atol(strrchr(user_comment,'=')+1)-loop_start; + loop_length_found = 1; + } } - else if (strstr(comment->user_comments[i],"lp=")== - comment->user_comments[i]) { - sscanf(strrchr(comment->user_comments[i],'=')+1,"%d,%d", - &loop_start,&loop_end); - loop_flag=1; - loop_end_found=1; + else if (strstr(user_comment,"lp=")==user_comment) { + sscanf(strrchr(user_comment,'=')+1,"%d,%d", &loop_start,&loop_end); + loop_flag = 1; + loop_end_found = 1; } - else if (strstr(comment->user_comments[i],"LOOPDEFS=")== - comment->user_comments[i]) { - sscanf(strrchr(comment->user_comments[i],'=')+1,"%d,%d", - &loop_start,&loop_end); - loop_flag=1; - loop_end_found=1; + else if (strstr(user_comment,"LOOPDEFS=")==user_comment) { /* Fairy Fencer F: Advent Dark Force */ + sscanf(strrchr(user_comment,'=')+1,"%d,%d", &loop_start,&loop_end); + loop_flag = 1; + loop_end_found = 1; } - else if (strstr(comment->user_comments[i],"COMMENT=loop(")== - comment->user_comments[i]) { - sscanf(strrchr(comment->user_comments[i],'(')+1,"%d,%d", - &loop_start,&loop_end); - loop_flag=1; - loop_end_found=1; + else if (strstr(user_comment,"COMMENT=loop(")==user_comment) { /* Zero Time Dilemma (PC) */ + sscanf(strrchr(user_comment,'(')+1,"%d,%d", &loop_start,&loop_end); + loop_flag = 1; + loop_end_found = 1; } } } + /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(info->channels,loop_flag); + vgmstream = allocate_vgmstream(vi->channels,loop_flag); if (!vgmstream) goto fail; - /* store our fun extra datas */ - vgmstream->codec_data = data; - - /* fill in the vital statistics */ - vgmstream->channels = info->channels; - vgmstream->sample_rate = info->rate; - - /* let's play the whole file */ - vgmstream->num_samples = ov_pcm_total(ovf,-1); + vgmstream->codec_data = data; /* store our fun extra datas */ + vgmstream->channels = vi->channels; + vgmstream->sample_rate = vi->rate; + vgmstream->num_samples = ov_pcm_total(ovf,-1); /* let libvorbisfile find total samples */ if (loop_flag) { vgmstream->loop_start_sample = loop_start; if (loop_length_found) @@ -436,17 +339,18 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, const ch if (vgmstream->loop_end_sample > vgmstream->num_samples) vgmstream->loop_end_sample = vgmstream->num_samples; } + vgmstream->coding_type = coding_ogg_vorbis; vgmstream->layout_type = vgm_inf->layout_type; vgmstream->meta_type = vgm_inf->meta_type; return vgmstream; - /* clean up anything we may have opened */ fail: + /* clean up anything we may have opened */ if (data) { - if (inited_ovf) - ov_clear(&data->ogg_vorbis_file); + if (ovf) + ov_clear(&data->ogg_vorbis_file);//same as ovf if (data->ov_streamfile.streamfile) close_streamfile(data->ov_streamfile.streamfile); free(data); diff --git a/src/meta/sqex_scd.c b/src/meta/sqex_scd.c index c0df0613..ffe632f5 100644 --- a/src/meta/sqex_scd.c +++ b/src/meta/sqex_scd.c @@ -4,8 +4,8 @@ #ifdef VGM_USE_VORBIS -static void scd_ogg_decrypt_v2_callback(void *ptr, size_t size, size_t nmemb, void *datasource, int bytes_read); -static void scd_ogg_decrypt_v3_callback(void *ptr, size_t size, size_t nmemb, void *datasource, int bytes_read); +static void scd_ogg_v2_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource); +static void scd_ogg_v3_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource); #endif /* SCD - Square-Enix games (FF XIII, XIV) */ @@ -178,14 +178,12 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { return NULL; /* not actually encrypted, happens but should be handled above */ if (xor_version == 2) { /* header is XOR'ed using byte */ - inf.decryption_enabled = 1; - inf.decryption_callback = scd_ogg_decrypt_v2_callback; + inf.decryption_callback = scd_ogg_v2_decryption_callback; inf.scd_xor = xor_byte; inf.scd_xor_length = vorb_header_size; } else if (xor_version == 3) { /* full file is XOR'ed using table */ - inf.decryption_enabled = 1; - inf.decryption_callback = scd_ogg_decrypt_v3_callback; + inf.decryption_callback = scd_ogg_v3_decryption_callback; inf.scd_xor = stream_size & 0xFF; /* xor_byte is not used? (also there is data at +0x03) */ inf.scd_xor_length = stream_size; } @@ -412,7 +410,8 @@ fail: #ifdef VGM_USE_VORBIS -static void scd_ogg_decrypt_v2_callback(void *ptr, size_t size, size_t nmemb, void *datasource, int bytes_read) { +static void scd_ogg_v2_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) { + size_t bytes_read = size*nmemb; ogg_vorbis_streamfile * ov_streamfile = (ogg_vorbis_streamfile*)datasource; /* header is XOR'd with a constant byte */ @@ -429,9 +428,9 @@ static void scd_ogg_decrypt_v2_callback(void *ptr, size_t size, size_t nmemb, vo } } -static void scd_ogg_decrypt_v3_callback(void *ptr, size_t size, size_t nmemb, void *datasource, int bytes_read) { - /* V3 decryption table found in the .exe */ - static const uint8_t scd_ogg_v3_lookuptable[256] = { /* FF XIV Heavensward */ +static void scd_ogg_v3_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) { + /* V3 decryption table found in the .exe of FF XIV Heavensward */ + static const uint8_t scd_ogg_v3_lookuptable[256] = { 0x3A, 0x32, 0x32, 0x32, 0x03, 0x7E, 0x12, 0xF7, 0xB2, 0xE2, 0xA2, 0x67, 0x32, 0x32, 0x22, 0x32, // 00-0F 0x32, 0x52, 0x16, 0x1B, 0x3C, 0xA1, 0x54, 0x7B, 0x1B, 0x97, 0xA6, 0x93, 0x1A, 0x4B, 0xAA, 0xA6, // 10-1F 0x7A, 0x7B, 0x1B, 0x97, 0xA6, 0xF7, 0x02, 0xBB, 0xAA, 0xA6, 0xBB, 0xF7, 0x2A, 0x51, 0xBE, 0x03, // 20-2F @@ -449,23 +448,25 @@ static void scd_ogg_decrypt_v3_callback(void *ptr, size_t size, size_t nmemb, vo 0xE2, 0xA2, 0x67, 0x32, 0x32, 0x12, 0x32, 0xB2, 0x32, 0x32, 0x32, 0x32, 0x75, 0xA3, 0x26, 0x7B, // E0-EF 0x83, 0x26, 0xF9, 0x83, 0x2E, 0xFF, 0xE3, 0x16, 0x7D, 0xC0, 0x1E, 0x63, 0x21, 0x07, 0xE3, 0x01, // F0-FF }; + + size_t bytes_read = size*nmemb; ogg_vorbis_streamfile *ov_streamfile = (ogg_vorbis_streamfile*)datasource; /* file is XOR'd with a table (algorithm and table by Ioncannon) */ if (ov_streamfile->offset < ov_streamfile->scd_xor_length) { int i, num_crypt; - uint8_t byte1, byte2, xorByte; + uint8_t byte1, byte2, xor_byte; num_crypt = bytes_read; byte1 = ov_streamfile->scd_xor & 0x7F; byte2 = ov_streamfile->scd_xor & 0x3F; for (i = 0; i < num_crypt; i++) { - xorByte = scd_ogg_v3_lookuptable[(byte2 + ov_streamfile->offset + i) & 0xFF]; - xorByte &= 0xFF; - xorByte ^= ((uint8_t*)ptr)[i]; - xorByte ^= byte1; - ((uint8_t*)ptr)[i] = (uint8_t)xorByte; + xor_byte = scd_ogg_v3_lookuptable[(byte2 + ov_streamfile->offset + i) & 0xFF]; + xor_byte &= 0xFF; + xor_byte ^= ((uint8_t*)ptr)[i]; + xor_byte ^= byte1; + ((uint8_t*)ptr)[i] = (uint8_t)xor_byte; } } }