Merge pull request #242 from bnnm/str-mus-exts

STR, MUS, exts
This commit is contained in:
Christopher Snowhill 2018-06-30 14:03:42 -07:00 committed by GitHub
commit 869ca7127a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 169 additions and 88 deletions

View File

@ -21,6 +21,7 @@ extern "C" {
#ifndef VERSION
#include "../version.h"
#endif
#ifndef VERSION
#define PLUGIN_VERSION __DATE__
#else
@ -145,6 +146,8 @@ void input_vgmstream::get_info(t_uint32 p_subsong, file_info & p_info, abort_cal
p_info.meta_set("TITLE",temp);
}
p_info.info_set("vgmstream version",PLUGIN_VERSION);
p_info.info_set_int("samplerate", samplerate);
p_info.info_set_int("channels", channels);
p_info.info_set_int("bitspersample",16);

View File

@ -1,13 +1,16 @@
#include "vgmstream.h"
//#define VGM_REGISTER_TYPE(extension) ...
//#define VGM_REGISTER_TYPE_COMMON(extension) ... /* for common extensions like aiff */
/* defines the list of accepted extensions. vgmstream doesn't use it internally so it's here
* to inform plugins that need it. Common extensions are commented out to avoid stealing them. */
/* some extensions could be #ifdef but no really needed */
/* some extensions require external libraries and could be #ifdef, no really needed */
/* some formats marked as "not parsed" mean they'll go through FFmpeg, the header/extension is not parsed */
static const char* extension_list[] = {
//"", /* vgmstream can plays extensionless files too, but plugins must accept them manually */
"04sw",
"2dx9",
"2pfs",
@ -38,6 +41,7 @@ static const char* extension_list[] = {
"al2",
"amts", //fake extension/header id for .stm (to be removed)
"ao", //txth/reserved [Cloudphobia (PC)]
"apc", //txth/reserved [MegaRace 3 (PC)]
"as4",
"asd",
"asf",
@ -367,6 +371,7 @@ static const char* extension_list[] = {
"v0",
//"v1", //dual channel with v0
"vag",
"vai", //txth/reserved [Ratatouille (GC)]
"vas",
"vawx",
"vb",
@ -435,7 +440,7 @@ static const char* extension_list[] = {
"zsd",
"zwdsp",
"vgmstream"
"vgmstream" /* fake extension, catch-all for FFmpeg/txth/etc */
//, NULL //end mark
};
@ -907,7 +912,7 @@ static const meta_info meta_info_list[] = {
{meta_X360_TRA, "Terminal Reality .TRA raw header"},
{meta_PS2_VGS, "Princess Soft VGS header"},
{meta_PS2_IAB, "Runtime .IAB header"},
{meta_PS2_STRLR, "STR L/R header"},
{meta_PS2_STRLR, "The Bouncer STR header"},
{meta_LSF_N1NJ4N, ".lsf !n1nj4n header"},
{meta_VAWX, "feelplus VAWX header"},
{meta_PC_SNDS, "assumed Heavy Iron IMA by .snds extension"},
@ -1023,6 +1028,7 @@ static const meta_info meta_info_list[] = {
{meta_OGG_GWM, "Ogg Vorbis (GWM header)"},
{meta_DSP_SADF, "Procyon Studio SADF header"},
{meta_H4M, "Hudson HVQM4 header"},
{meta_OGG_MUS, "Ogg Vorbis (MUS header)"},
#ifdef VGM_USE_FFMPEG
{meta_FFmpeg, "FFmpeg supported file format"},

View File

@ -1,19 +1,17 @@
#include "layout.h"
#include "../vgmstream.h"
/* set up for the block at the given offset */
/* The Bouncer STRx blocks, one block per channel when stereo */
void block_update_ps2_strlr(off_t block_offset, VGMSTREAM * vgmstream) {
STREAMFILE* streamFile = vgmstream->ch[0].streamfile;
int i;
vgmstream->current_block_offset = block_offset;
vgmstream->current_block_size = read_32bitLE(
vgmstream->current_block_offset+0x4,
vgmstream->ch[0].streamfile)*2;
vgmstream->next_block_offset = vgmstream->current_block_offset+vgmstream->current_block_size+0x40;
//vgmstream->current_block_size/=vgmstream->channels;
vgmstream->current_block_offset = block_offset;
vgmstream->current_block_size = read_32bitLE(block_offset+0x04,streamFile); /* can be smaller than 0x800 */
vgmstream->next_block_offset = block_offset + 0x800*vgmstream->channels;
/* 0x08: number of remaning blocks, 0x10: some id/size? (shared in all blocks) */
for (i=0;i<vgmstream->channels;i++) {
vgmstream->ch[i].offset = vgmstream->current_block_offset+0x20+(0x800*i);
for (i = 0; i < vgmstream->channels; i++) {
vgmstream->ch[i].offset = block_offset + 0x20 + 0x800*i;
}
}

View File

@ -238,7 +238,13 @@ static const hcakey_info hcakey_list[] = {
{3201512}, // 000000000030D9E8
// PriPara: All Idol Perfect Stage (Takara Tomy) [Switch]
{217735759}, // 000000000CFA624F
{217735759}, // 000000000CFA624F
// Space Invaders Extreme (Taito Corporation, Backbone Entertainment) [PC]
{91380310056}, // 0000001546B0E028
// CR Another God Hades Advent (Universal Entertainment Corporation) [iOS/Android]
{64813795}, // 0000000003DCFAE3
};

View File

@ -226,6 +226,28 @@ static void gwm_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, vo
}
}
static void mus_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) {
static const uint8_t key[16] = {
0x21,0x4D,0x6F,0x01,0x20,0x4C,0x6E,0x02,0x1F,0x4B,0x6D,0x03,0x20,0x4C,0x6E,0x02
};
size_t bytes_read = size*nmemb;
ogg_vorbis_streamfile * const ov_streamfile = datasource;
int i;
char *header_id = "OggS";
/* bytes are xor'd with key, first "OggS" is changed */
for (i = 0; i < bytes_read; i++) {
if (ov_streamfile->offset+i < 0x04) { /* if decrypted gives "Mus " */
((uint8_t*)ptr)[i] = (uint8_t)header_id[(ov_streamfile->offset + i) % 4];
}
else {
((uint8_t*)ptr)[i] ^= key[(ov_streamfile->offset + i) % sizeof(key)];
}
}
}
/* Ogg Vorbis, by way of libvorbisfile; may contain loop comments */
VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
ogg_vorbis_meta_info_t ovmi = {0};
@ -239,6 +261,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
int is_rpgmvo = 0;
int is_eno = 0;
int is_gwm = 0;
int is_mus = 0;
/* check extension */
@ -262,6 +285,8 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
is_eno = 1;
} else if (check_extensions(streamFile,"gwm")) { /* .gwm: Adagio: Cloudburst (PC) */
is_gwm = 1;
} else if (check_extensions(streamFile,"mus")) { /* .mus: Redux - Dark Matters (PC) */
is_mus = 1;
} else {
goto fail;
}
@ -355,7 +380,6 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
start_offset = 0x01;
}
/* check GWM [Adagio: Cloudburst (PC)], encrypted */
if (is_gwm) {
ovmi.xor_value = 0x5D;
@ -363,6 +387,12 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
ovmi.meta_type = meta_OGG_GWM;
}
/* check .mus [Redux - Dark Matters (PC)], encrypted */
if (is_mus) {
ovmi.decryption_callback = mus_ogg_decryption_callback;
ovmi.meta_type = meta_OGG_MUS;
}
return init_vgmstream_ogg_vorbis_callbacks(streamFile, NULL, start_offset, &ovmi);

View File

@ -5,17 +5,14 @@
VGMSTREAM * init_vgmstream_ps2_joe(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int channel_count, loop_flag, sample_rate, num_samples;
size_t file_size, data_size, unknown1, unknown2, interleave;
int loop_flag;
int channel_count;
/* checks */
if (!check_extensions(streamFile, "joe"))
goto fail;
loop_flag = 1;
channel_count = 2;
file_size = get_streamfile_size(streamFile);
data_size = read_32bitLE(0x04,streamFile);
unknown1 = read_32bitLE(0x08,streamFile);
@ -48,14 +45,20 @@ VGMSTREAM * init_vgmstream_ps2_joe(STREAMFILE *streamFile) {
start_offset = file_size - data_size;
channel_count = 2;
sample_rate = read_32bitLE(0x00,streamFile);
num_samples = ps_bytes_to_samples(data_size, channel_count);
/* most songs simply repeat except a few jingles (PS-ADPCM flags are always set) */
loop_flag = (num_samples > 20*sample_rate); /* in seconds */
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = read_32bitLE(0x00,streamFile);
vgmstream->coding_type = coding_PSX;
vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count);
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = num_samples;
//todo improve, not working 100% with early .joe
{
@ -103,6 +106,7 @@ VGMSTREAM * init_vgmstream_ps2_joe(STREAMFILE *streamFile) {
}
}
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = interleave;
vgmstream->meta_type = meta_PS2_JOE;

View File

@ -1,80 +1,57 @@
#include "meta.h"
#include "../layout/layout.h"
#include "../util.h"
#include "../coding/coding.h"
/* STR: The Bouncer (PS2) */
/* STR - The Bouncer (PS2) */
VGMSTREAM * init_vgmstream_ps2_strlr(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
int loop_flag = 0;
int channel_count;
int i;
off_t start_offset;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("str",filename_extension(filename))) goto fail;
int channel_count, loop_flag;
off_t start_offset;
/* checks */
/* .vs: real extension (from .nam container) , .str: partial header id */
if (!check_extensions(streamFile, "vs,str"))
goto fail;
#if 0
/* check header */
if (read_32bitBE(0x00,streamFile) != 0x5354524C) /* "STRL" */
if (!(read_32bitBE(0x000,streamFile) == 0x5354524C && /* "STRL" */
read_32bitBE(0x800,streamFile) == 0x53545252) && /* "STRR" */
read_32bitBE(0x00,streamFile) != 0x5354524D) /* "STRM" */
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x53545252) /* "STRR" */
goto fail;
#endif
/* don't hijack Sonic & Sega All Stars Racing X360 (xma) */
if (read_32bitBE(0x00,streamFile) == 0x52494646)
goto fail; /* "RIFF"*/
/* don't hijack Mad Dash Racing (Xbox) */
if (read_32bitLE(0x0c,streamFile) == 1
&& read_32bitLE(0x010,streamFile) == 0
&& read_32bitLE(0x400,streamFile) == 0
&& read_32bitLE(0x7f0,streamFile) == 0)
goto fail;
loop_flag = 0;
channel_count = 2;
/* build the VGMSTREAM */
channel_count = (read_32bitBE(0x00,streamFile) == 0x5354524D) ? 1 : 2; /* "STRM"=mono (voices) */
start_offset = 0x00;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
start_offset = 0x0;
vgmstream->channels = channel_count;
vgmstream->sample_rate = 48000;
vgmstream->sample_rate = 44100;
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_blocked_ps2_strlr;
//vgmstream->interleave_block_size = read_32bitLE(0xC, streamFile);
vgmstream->meta_type = meta_PS2_STRLR;
/* open the file for reading by each channel */
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
/* calc num_samples */
{
for (i=0;i<channel_count;i++)
{
vgmstream->ch[i].streamfile = streamFile->open(streamFile, filename, 0x8000);
if (!vgmstream->ch[i].streamfile) goto fail;
vgmstream->next_block_offset = start_offset;
do {
block_update_ps2_strlr(vgmstream->next_block_offset,vgmstream);
vgmstream->num_samples += ps_bytes_to_samples(vgmstream->current_block_size, 1);
}
while (vgmstream->next_block_offset < get_streamfile_size(streamFile));
}
/* Calc num_samples */
block_update_ps2_strlr(start_offset, vgmstream);
vgmstream->num_samples=0;
do
{
vgmstream->num_samples += vgmstream->current_block_size * 14 / 16;
block_update_ps2_strlr(vgmstream->next_block_offset, vgmstream);
} while (vgmstream->next_block_offset < get_streamfile_size(streamFile));
block_update_ps2_strlr(start_offset, vgmstream);
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -313,6 +313,7 @@ static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile) {
/* ignore non-audio entry (other types seem to have config data) */
if (read_32bit(offset + 0x04, streamFile) != 0x01)
continue;
//;VGM_LOG("SB at %lx\n", offset);
/* weird case when there is no internal substream ID and just seem to rotate every time type changes, joy */
if (sb->has_rotating_ids) { /* assumes certain configs can't happen in this case */
@ -779,6 +780,23 @@ static int config_sb_header_version(ubi_sb_header * sb, STREAMFILE *streamFile)
return 1;
}
#if 0
/* Far cry: Instincts - Evolution (2006)(Xbox) */
if (sb->version == 0x00170000 && is_sb2) {
sb->section1_entry_size = 0x48;
sb->section2_entry_size = 0x6c;
sb->external_flag_offset = 0;
sb->num_samples_offset = 0x28;
sb->stream_id_offset = 0;
sb->sample_rate_offset = 0x3c;
sb->channels_offset = 0x44;
sb->stream_type_offset = 0x48;
sb->extra_name_offset = 0x58;
return 1;
}
#endif
/* Prince of Persia: Rival Swords (2007)(PSP) */
if (sb->version == 0x00180005 && is_sb5) {

View File

@ -2,16 +2,28 @@
#include "util.h"
#include "streamtypes.h"
const char * filename_extension(const char * filename) {
const char * ext;
const char * filename_extension(const char * pathname) {
const char * filename;
const char * extension;
/* You know what would be nice? strrchrnul().
* Instead I have to do it myself. */
ext = strrchr(filename,'.');
if (ext==NULL) ext=filename+strlen(filename); /* point to null, i.e. an empty string for the extension */
else ext=ext+1; /* skip the dot */
/* get basename + extension */
filename = pathname;
#if 0
//must detect empty extensions in folders with . in the name; not too important and DIR_SEPARATOR could improved
filename = strrchr(pathname, DIR_SEPARATOR);
if (filename == NULL)
filename = pathname; /* pathname has no separators (single filename) */
else
filename++; /* skip the separator */
#endif
return ext;
extension = strrchr(filename,'.');
if (extension==NULL)
extension = filename+strlen(filename); /* point to null, i.e. an empty string for the extension */
else
extension++; /* skip the dot */
return extension;
}
/* unused */

View File

@ -679,6 +679,7 @@ typedef enum {
meta_TA_AAC_VITA, /* tri-Ace AAC (Judas Code) */
meta_OGG_GWM, /* Ogg Vorbis with encryption [Metronomicon (PC)] */
meta_H4M, /* Hudson HVQM4 video [Resident Evil 0 (GC), Tales of Symphonia (GC)] */
meta_OGG_MUS, /* Ogg Vorbis with encryption [Redux - Dark Matters (PC)] */
#ifdef VGM_USE_FFMPEG
meta_FFmpeg,

View File

@ -43,7 +43,7 @@
In_Module input_module;
DWORD WINAPI __stdcall decode(void *arg);
/* Winamp Play extension list, needed to accept/play and associate extensions in Windows */
/* Winamp Play extension list, to accept and associate extensions in Windows */
#define EXTENSION_LIST_SIZE (0x2000 * 6)
#define EXT_BUFFER_SIZE 200
char working_extension_list[EXTENSION_LIST_SIZE] = {0};
@ -88,6 +88,7 @@ in_char lastfn[PATH_LIMIT] = {0}; /* name of the currently playing file */
#define wa_strchr wcschr
#define wa_sscanf swscanf
#define wa_snprintf _snwprintf
#define wa_strrchr wcsrchr
#define wa_fileinfo fileinfoW
#define wa_IPC_PE_INSERTFILENAME IPC_PE_INSERTFILENAMEW
#define wa_L(x) L ##x
@ -99,6 +100,7 @@ in_char lastfn[PATH_LIMIT] = {0}; /* name of the currently playing file */
#define wa_strchr strchr
#define wa_sscanf sscanf
#define wa_snprintf snprintf
#define wa_strrchr strrchr
#define wa_fileinfo fileinfo
#define wa_IPC_PE_INSERTFILENAME IPC_PE_INSERTFILENAME
#define wa_L(x) x
@ -777,7 +779,9 @@ static void add_extension(int length, char * dst, const char * ext) {
}
/* Creates Winamp's extension list, a single string that ends with \0\0.
* Each extension must be in this format: "extension\0Description\0" */
* Each extension must be in this format: "extension\0Description\0"
* The list is used to accept extensions by default when IsOurFile returns 0, and to register file types.
* It could be ignored/empty and just detect in IsOurFile instead. */
static void build_extension_list() {
const char ** ext_list;
size_t ext_list_len;
@ -871,7 +875,29 @@ void winamp_Quit() {
/* called before extension checks, to allow detection of mms://, etc */
int winamp_IsOurFile(const in_char *fn) {
return 0; /* we don't recognize protocols */
const in_char *filename;
const in_char *extension;
/* get basename + extension */
filename = fn;
#if 0
//must detect empty extensions in folders with . in the name; doesn't work ok?
filename = wa_strrchr(fn, wa_L('\\'));
if (filename == NULL)
filename = fn;
else
filename++;
#endif
extension = wa_strrchr(filename, wa_L('.'));
if (extension == NULL)
return 1; /* extensionless, try to play it */
else
extension++;
/* returning 0 here means it only accepts the extensions in working_extension_list */
/* it's possible to ignore the list and manually accept extensions, like foobar's g_is_our_path */
return 0;
}
/* request to start playing a file */