mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-30 03:47:30 +01:00
Clean Ogg Vorbis code and IO/decryption callbacks for future changes
This commit is contained in:
parent
bcad321b6d
commit
7134610495
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user