Clean Ogg Vorbis code and IO/decryption callbacks for future changes

This commit is contained in:
bnnm 2018-01-10 21:12:23 +01:00
parent bcad321b6d
commit 7134610495
3 changed files with 231 additions and 327 deletions

View File

@ -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;

View File

@ -1,100 +1,23 @@
#include "../vgmstream.h"
#ifdef VGM_USE_VORBIS
#include <stdio.h>
#include <string.h>
#include "meta.h"
#include "../util.h"
#include <vorbis/vorbisfile.h>
#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;i<num_crypt;i++)
((uint8_t*)ptr)[i] ^= 0xff;
}
ov_streamfile->offset += 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;i<bytes_read;i++)
((uint8_t*)ptr)[i] += 0x23;
items_read = bytes_read / size;
ov_streamfile->offset += 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;i<comment->comments;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);

View File

@ -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;
}
}
}