Merge pull request #1683 from bnnm/api-etc2

- Fix some WIIADPCM .adpcm [Angry Birds: Star Wars (Wii/WiiU)]
- Fix broken MPEG FSB5
- Remove fake format .wmus (use .txth)
- cleanup: misc
- txtp-maker: add option to handle stream names as SHIFT-JIS
This commit is contained in:
bnnm 2025-02-17 00:06:14 +01:00 committed by GitHub
commit a8513ca672
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
36 changed files with 553 additions and 784 deletions

View File

@ -49,18 +49,16 @@ static int api_example(const char* infile) {
//.loop_count = 1.0,
//.fade_time = 10.0,
.ignore_loop = true,
.force_pcm16 = fill_test,
.force_sfmt = LIBVGMSTREAM_SFMT_PCM16,
};
libvgmstream_setup(lib, &cfg);
// open target file
libvgmstream_options_t opt = {
.libsf = get_streamfile(infile)
};
err = libvgmstream_open_stream(lib, &opt);
libstreamfile_t* sf = get_streamfile(infile);
err = libvgmstream_open_stream(lib, sf, 0);
// external SF is not needed after _open
libstreamfile_close(opt.libsf);
libstreamfile_close(sf);
if (err < 0) {
printf("not a valid file\n");
@ -181,7 +179,7 @@ static void test_lib_extensions() {
VGM_STEP();
const char** exts;
size_t size = 0;
int size = 0;
size = 0;
exts = libvgmstream_get_extensions(&size);
@ -383,10 +381,7 @@ static void test_lib_tags() {
int main(int argc, char** argv) {
printf("API v%08x test\n", libvgmstream_get_version());
libvgmstream_log_t cfg = {
.stdout_callback = true
};
libvgmstream_set_log(&cfg);
libvgmstream_set_log(0, NULL);
test_lib_is_valid();
test_lib_extensions();

View File

@ -74,6 +74,7 @@ class Cli(object):
p.add_argument('-fni', dest='include_regex', help="Filter by REGEX including matches of subsong name")
p.add_argument('-fne', dest='exclude_regex', help="Filter by REGEX excluding matches of subsong name")
p.add_argument('-nsc',dest='no_semicolon', help="Remove semicolon names (for songs with multinames)", action='store_true')
p.add_argument('-sj',dest='shift_jis', help="Take extended names as shift-jis", action='store_true')
p.add_argument("-cmd","--command", help="sets any command (free text)")
p.add_argument("-cmdi","--command-inline", help="sets any inline command (free text)")
p.add_argument('-v', dest='log_level', help="Verbose log level (off|debug|info, default: info)", default='info')
@ -158,14 +159,14 @@ class Cr32Helper(object):
#******************************************************************************
class TxtpInfo(object):
def __init__(self, output_b):
def __init__(self, output_b, cfg):
self.output = str(output_b).replace("\\r","").replace("\\n","\n")
self.channels = self._get_value("channels: ")
self.sample_rate = self._get_value("sample rate: ")
self.num_samples = self._get_value("stream total samples: ")
self.stream_count = self._get_value("stream count: ")
self.stream_index = self._get_value("stream index: ")
self.stream_name = self._get_text("stream name: ")
self.stream_name = self._get_text("stream name: ", cfg.shift_jis)
self.encoding = self._get_text("encoding: ")
# in case vgmstream returns error, but output code wasn't EXIT_FAILURE
@ -185,12 +186,16 @@ class TxtpInfo(object):
else:
return str_cut.split()[0].strip()
def _get_text(self, str):
def _get_text(self, str, shift_jis=False):
text = self._get_string(str, full=True)
# stream names in CLI is printed as UTF-8 using '\xNN', so detect and transform
try:
if text and '\\' in text:
return text.encode('ascii').decode('unicode-escape').encode('iso-8859-1').decode('utf-8')
text_bytes = text.encode('ascii').decode('unicode-escape').encode('iso-8859-1')
if shift_jis:
return text_bytes.decode('shift-jis')
else:
return text_bytes.decode('utf-8')
except:
return text #odd/buggy names
return text
@ -215,7 +220,7 @@ class TxtpMaker(object):
return str(self.__dict__)
def parse(self, output_b):
self.info = TxtpInfo(output_b)
self.info = TxtpInfo(output_b, self.cfg)
self.ignorable = self._is_ignorable(self.cfg)
def reset(self):

View File

@ -72,11 +72,11 @@
#define LITTLE_ENDIAN_OUTPUT 1 /* untested in BE */
#define DEFAULT_CONFIG { 0, 0, -1, -1, 2.0, 10.0, 0.0, 0, 0, 0, 0, 0, 0 }
#define DEFAULT_CONFIG { 0, 0, 0, -1, 2.0, 10.0, 0.0, 0, 0, 0, 0, 0, 0 }
typedef struct {
int subsong_index;
int subsong_end;
int only_stereo;
int stereo_track;
double min_time;
double loop_count;
@ -99,7 +99,7 @@ static ao_device *device = NULL;
static ao_option *device_options = NULL;
static ao_sample_format current_sample_format;
static sample_t *buffer = NULL;
static sample_t* buffer = NULL;
/* reportedly 1kb helps Raspberry Pi Zero play FFmpeg formats without stuttering
* (presumably other low powered devices too), plus it's the default in other plugins */
static int buffer_size_kb = 1;
@ -188,21 +188,18 @@ static int set_sample_format(int channels, int sample_rate) {
return 0;
}
static void apply_config(VGMSTREAM* vgmstream, song_config_t* cfg) {
vgmstream_cfg_t vcfg = {0};
static void load_config(vgmstream_cfg_t* vcfg, song_config_t* cfg) {
vcfg.allow_play_forever = 1;
vcfg->allow_play_forever = true;
vcfg.play_forever = cfg->play_forever;
vcfg.fade_time = cfg->fade_time;
vcfg.loop_count = cfg->loop_count;
vcfg.fade_delay = cfg->fade_delay;
vcfg->play_forever = cfg->play_forever;
vcfg->fade_time = cfg->fade_time;
vcfg->loop_count = cfg->loop_count;
vcfg->fade_delay = cfg->fade_delay;
vcfg.ignore_loop = cfg->ignore_loop;
vcfg.force_loop = cfg->force_loop;
vcfg.really_force_loop = cfg->really_force_loop;
vgmstream_apply_config(vgmstream, &vcfg);
vcfg->ignore_loop = cfg->ignore_loop;
vcfg->force_loop = cfg->force_loop;
vcfg->really_force_loop = cfg->really_force_loop;
}
#ifndef WIN32
@ -230,34 +227,39 @@ static int getkey() {
}
#endif
static int play_vgmstream(const char* filename, song_config_t* cfg) {
int ret = 0;
STREAMFILE* sf;
VGMSTREAM* vgmstream;
FILE* save_fps[4];
size_t buffer_size;
int32_t max_buffer_samples;
int i;
int output_channels, input_channels;
static VGMSTREAM* open_vgmstream(const char* filename, song_config_t* cfg) {
sf = open_stdio_streamfile(filename);
STREAMFILE* sf = open_stdio_streamfile(filename);
if (!sf) {
fprintf(stderr, "%s: cannot open file\n", filename);
return -1;
return NULL;
}
vgmstream_set_log_stdout(VGM_LOG_LEVEL_ALL);
sf->stream_index = cfg->subsong_current_index;
vgmstream = init_vgmstream_from_STREAMFILE(sf);
VGMSTREAM* vgmstream = init_vgmstream_from_STREAMFILE(sf);
close_streamfile(sf);
if (!vgmstream) {
fprintf(stderr, "%s: error opening stream\n", filename);
return -1;
return NULL;
}
return vgmstream;
}
static int play_vgmstream(const char* filename, song_config_t* cfg) {
int ret = 0;
FILE* save_fps[4];
size_t buffer_size;
int32_t max_buffer_samples;
VGMSTREAM* vgmstream = open_vgmstream(filename, cfg);
if (!vgmstream)
return -1;
/* force load total subsongs if signalled */
if (cfg->subsong_current_end == -1) {
cfg->subsong_current_end = vgmstream->num_streams;
@ -265,6 +267,35 @@ static int play_vgmstream(const char* filename, song_config_t* cfg) {
return 0;
}
/* Calculate how many loops are needed to achieve a minimum
* playback time. Note: This calculation is derived from the
* logic in get_vgmstream_play_samples().
*/
if (vgmstream->loop_flag && cfg->loop_count < 0) {
double intro = (double)vgmstream->loop_start_sample / vgmstream->sample_rate;
double loop = (double)(vgmstream->loop_end_sample - vgmstream->loop_start_sample) / vgmstream->sample_rate;
double end = cfg->fade_time + cfg->fade_delay;
if (loop < 1.0) loop = 1.0;
cfg->loop_count = ((cfg->min_time - intro - end) / loop + 0.99);
if (cfg->loop_count < 1.0) cfg->loop_count = 1.0;
}
/* Config
*/
vgmstream_cfg_t vcfg = {0};
load_config(&vcfg, cfg);
vgmstream_apply_config(vgmstream, &vcfg);
if (cfg->stereo_track > 0) {
vgmstream_mixing_stereo_only(vgmstream, cfg->stereo_track - 1);
}
int input_channels = vgmstream->channels;
int output_channels = vgmstream->channels;
vgmstream_mixing_enable(vgmstream, 0, &input_channels, &output_channels); /* query */
bool play_forever = vgmstream_get_play_forever(vgmstream);
/* If the audio device hasn't been opened yet, then describe it
*/
if (!device) {
@ -292,7 +323,7 @@ static int play_vgmstream(const char* filename, song_config_t* cfg) {
/* Print metadata in verbose mode
*/
if (verbose) {
char description[4096] = { '\0' };
char description[4096];
describe_vgmstream(vgmstream, description, sizeof(description));
puts(description);
putchar('\n');
@ -303,36 +334,11 @@ static int play_vgmstream(const char* filename, song_config_t* cfg) {
* so that play_compressed_file() doesn't break, due to POSIX
* wackiness like https://bugs.debian.org/590920
*/
for (i = 0; i < 4; i++)
for (int i = 0; i < 4; i++) {
save_fps[i] = fopen("/dev/null", "r");
/* Calculate how many loops are needed to achieve a minimum
* playback time. Note: This calculation is derived from the
* logic in get_vgmstream_play_samples().
*/
if (vgmstream->loop_flag && cfg->loop_count < 0) {
double intro = (double)vgmstream->loop_start_sample / vgmstream->sample_rate;
double loop = (double)(vgmstream->loop_end_sample - vgmstream->loop_start_sample) / vgmstream->sample_rate;
double end = cfg->fade_time + cfg->fade_delay;
if (loop < 1.0) loop = 1.0;
cfg->loop_count = ((cfg->min_time - intro - end) / loop + 0.99);
if (cfg->loop_count < 1.0) cfg->loop_count = 1.0;
}
/* Config
*/
apply_config(vgmstream, cfg);
if (cfg->only_stereo >= 0) {
vgmstream_mixing_stereo_only(vgmstream, cfg->only_stereo);
}
input_channels = vgmstream->channels;
output_channels = vgmstream->channels;
vgmstream_mixing_enable(vgmstream, 0, &input_channels, &output_channels); /* query */
/* Buffer size in bytes (after getting channels)
*/
buffer_size = 1024 * buffer_size_kb;
@ -356,30 +362,22 @@ static int play_vgmstream(const char* filename, song_config_t* cfg) {
ret = set_sample_format(output_channels, vgmstream->sample_rate);
if (ret) goto fail;
if (out_filename && play_forever) {
fprintf(stderr, "%s: cannot play forever and use output filename\n", filename);
ret = -1;
goto fail;
}
/* Decode
*/
{
double total;
int time_total_min;
double time_total_sec;
int play_forever = vgmstream_get_play_forever(vgmstream);
int32_t decode_pos_samples = 0;
int32_t length_samples = vgmstream_get_samples(vgmstream);
if (length_samples <= 0) goto fail;
if (out_filename && play_forever) {
fprintf(stderr, "%s: cannot play forever and use output filename\n", filename);
ret = -1;
goto fail;
}
total = (double)length_samples / vgmstream->sample_rate;
time_total_min = (int)total / 60;
time_total_sec = total - 60 * time_total_min;
int32_t play_position = 0;
int32_t play_samples = vgmstream_get_samples(vgmstream);
double time_total = (double)play_samples / vgmstream->sample_rate;
int time_total_min = (int)time_total / 60;
double time_total_sec = time_total - 60 * time_total_min;
while (!interrupted) {
int to_do;
#ifndef WIN32
int key = getkey();
if (key < 0) {
@ -392,11 +390,11 @@ static int play_vgmstream(const char* filename, song_config_t* cfg) {
}
#endif
if (decode_pos_samples + max_buffer_samples > length_samples && !play_forever)
to_do = length_samples - decode_pos_samples;
int to_do;
if (play_position + max_buffer_samples > play_samples && !play_forever)
to_do = play_samples - play_position;
else
to_do = max_buffer_samples;
if (to_do <= 0) {
break; /* EOF */
}
@ -408,8 +406,8 @@ static int play_vgmstream(const char* filename, song_config_t* cfg) {
#endif
if (verbose && !out_filename) {
double played = (double)decode_pos_samples / vgmstream->sample_rate;
double remain = (double)(length_samples - decode_pos_samples) / vgmstream->sample_rate;
double played = (double)play_position / vgmstream->sample_rate;
double remain = (double)(play_samples - play_position) / vgmstream->sample_rate;
if (remain < 0)
remain = 0; /* possible if play forever is set */
@ -435,22 +433,23 @@ static int play_vgmstream(const char* filename, song_config_t* cfg) {
break;
}
decode_pos_samples += to_do;
play_position += to_do;
}
if (verbose && !ret) {
/* Clear time status line */
putchar('\r');
for (i = 0; i < 64; i++)
for (int i = 0; i < 64; i++) {
putchar(' ');
}
putchar('\r');
fflush(stdout);
}
if (out_filename && !ret)
printf("Wrote %02d:%05.2f of audio to %s\n\n",
time_total_min, time_total_sec, out_filename);
if (out_filename && !ret) {
printf("Wrote %02d:%05.2f of audio to %s\n\n", time_total_min, time_total_sec, out_filename);
}
if (interrupted) {
fputs("Playback terminated.\n\n", stdout);
@ -460,13 +459,14 @@ static int play_vgmstream(const char* filename, song_config_t* cfg) {
}
fail:
fail: //also decode done
close_vgmstream(vgmstream);
for (i = 0; i < 4; i++)
for (int i = 0; i < 4; i++) {
if (save_fps[i]) {
fclose(save_fps[i]);
}
}
return ret;
}
@ -736,7 +736,9 @@ static void print_usage(const char* progname, bool is_help) {
" -B N Use an audio buffer of N kilobytes [%d]\n"
" -@ LSTFILE Read playlist from LSTFILE\n"
"\n"
#ifndef WIN32 //libao uses fopen(..., "w") instead of "wb" so any 0x0a (\n) becomes 0x0d0a (\r\n)...
" -o OUTFILE Set output filename for a file driver specified with -D\n"
#endif
" -m Print metadata and playback progress\n"
" -s N Play subsong N, if the format supports multiple subsongs\n"
" -S N Play up to end subsong N (set 0 for 'all')\n"
@ -779,6 +781,8 @@ int main(int argc, char** argv) {
driver_id = ao_default_driver_id();
memset(&current_sample_format, 0, sizeof(current_sample_format));
vgmstream_set_log_stdout(VGM_LOG_LEVEL_ALL);
if (argc == 1) {
/* We were invoked with no arguments */
print_usage(argv[0], false);
@ -833,7 +837,7 @@ again_opts:
cfg.subsong_index = 1;
break;
case '2':
cfg.only_stereo = atoi(optarg);
cfg.stereo_track = atoi(optarg) + 1;
break;
case 'i':
cfg.ignore_loop = 1;

View File

@ -302,18 +302,8 @@ static void apply_config(VGMSTREAM* vgmstream, cli_config_t* cfg) {
if (cfg->write_lwav) {
vcfg.disable_config_override = true;
cfg->ignore_loop = true;
if (vgmstream->loop_start_sample < vgmstream->loop_end_sample) {
cfg->lwav_loop_start = vgmstream->loop_start_sample;
cfg->lwav_loop_end = vgmstream->loop_end_sample;
cfg->lwav_loop_end--; /* from spec, +1 is added when reading "smpl" */
}
else {
/* reset for subsongs */
cfg->lwav_loop_start = 0;
cfg->lwav_loop_end = 0;
}
}
/* only allowed if manually active */
if (cfg->play_forever) {
vcfg.allow_play_forever = true;
@ -387,10 +377,16 @@ static bool write_file(VGMSTREAM* vgmstream, cli_config_t* cfg) {
.sample_rate = vgmstream->sample_rate,
.channels = channels,
.write_smpl_chunk = cfg->write_lwav,
.loop_start = cfg->lwav_loop_start,
.loop_end = cfg->lwav_loop_end
.sample_size = 0,
.is_float = false
};
if (cfg->write_lwav && vgmstream->loop_start_sample < vgmstream->loop_end_sample) {
wav.loop_start = vgmstream->loop_start_sample;
wav.loop_end = vgmstream->loop_end_sample;
wav.loop_end--; /* from spec, +1 is added when reading "smpl" */
}
bytes_done = wav_make_header(wav_buf, 0x100, &wav);
if (bytes_done == 0) goto fail;
fwrite(wav_buf, sizeof(uint8_t), bytes_done, outfile);

View File

@ -60,8 +60,6 @@ typedef struct {
/* not quite config but eh */
int subsong_current_index;
int subsong_current_end;
int lwav_loop_start;
int lwav_loop_end;
} cli_config_t;

View File

@ -139,7 +139,7 @@ void print_info(VGMSTREAM* vgmstream, cli_config_t* cfg) {
if (!cfg->play_sdtout && !cfg->print_adxencd && !cfg->print_oggenc && !cfg->print_batchvar) {
char description[1024];
describe_vgmstream(vgmstream, description, 1024);
describe_vgmstream(vgmstream, description, sizeof(description));
printf("%s", description);
}
}

View File

@ -617,10 +617,6 @@ different internally (encrypted, different versions, etc) and not always can be
- Midway ADS header [*ADS_MIDWAY*]
- *ads_midway*: `.ads`
- Codecs: NGC_DSP XBOX_IMA_mono
- **ps2_mcg.c**
- Gunvari MCG Header [*PS2_MCG*]
- *ps2_mcg*: `.mcg`
- Codecs: PSX
- **zsd.c**
- Konami ZSD header [*ZSD*]
- *zsd*: `.zsd`
@ -738,9 +734,9 @@ different internally (encrypted, different versions, etc) and not always can be
- Namco Bandai BNSF header [*BNSF*]
- *bnsf*: `.bnsf + .(external)`
- Codecs: G7221C G719
- **ps2_gcm.c**
- Namco GCM header [*PS2_GCM*]
- *ps2_gcm*: `.gcm`
- **mcg.c**
- Namco MCG header [*MCG*]
- *mcg*: `.gcm`
- Codecs: PSX
- **smpl.c**
- Skonec SMPL header [*SMPL*]
@ -1019,7 +1015,7 @@ different internally (encrypted, different versions, etc) and not always can be
- tri-Ace AAC header [*AAC_TRIACE*]
- *aac_triace*: `.aac .laac`
- Codecs: XMA2 ATRAC3 ATRAC9 MSADPCM ASKA OGG_VORBIS
- **ps2_va3.c**
- **va3.c**
- Konami VA3 header [*VA3*]
- *va3*: `.va3`
- Codecs: ATRAC3
@ -1418,7 +1414,7 @@ different internally (encrypted, different versions, etc) and not always can be
- Codecs: FFmpeg(various)
- **nus3audio.c**
- (container)
- *nus3audio*: `.nus3audio`
- *nus3audio*: `.nus3audio .patch3audio`
- Subfiles: *idsp_namco opus_nus3 riff bnsf*
- **imc.c**
- iNiS .IMC header [*IMC*]
@ -1868,10 +1864,6 @@ different internally (encrypted, different versions, etc) and not always can be
- *vas_kceo_container*: `.vas`
- Subfiles: *vas_kceo*
- Codecs: PCM16LE XBOX_IMA PSX NGC_DSP
- **ps2_wmus.c**
- assumed The Warriors Sony ADPCM by .wmus extension [*PS2_WMUS*]
- *ps2_wmus*: `.wmus`
- Codecs: PSX
- **mjb_mjh.c**
- Sony MultiStream MJH+MJB header [*MJB_MJH*]
- *mjb_mjh*: `.mjb + .mjh .mjb`
@ -2095,6 +2087,7 @@ are used in few games.
- Electronic Arts MicroTalk a.k.a. UTK or UMT
- Inti Creates DCT codec
- Circus XPCM VQ
- Koei Tecmo KA1A
- Misc
- SDX2 2:1 Squareroot-Delta-Exact compression DPCM
- CBD2 2:1 Cuberoot-Delta-Exact compression DPCM

View File

@ -48,21 +48,39 @@ LIBVGMSTREAM_API void libvgmstream_free(libvgmstream_t* lib) {
free(lib);
}
// TODO: allow calling after load
LIBVGMSTREAM_API void libvgmstream_setup(libvgmstream_t* lib, libvgmstream_config_t* cfg) {
if (!lib || !lib->priv)
return;
libvgmstream_priv_t* priv = lib->priv;
// Can only apply once b/c some options modify the internal mixing chain and there is no clean way to
// reset the stream when txtp also manipulates it (maybe could add some flag per mixing item)
if (priv->setup_done)
return;
// allow overwritting current config, though will only apply to next load
//if (priv->config_loaded)
// return;
if (!cfg) {
memset(&priv->cfg , 0, sizeof(libvgmstream_config_t));
priv->cfg.loop_count = 1; //TODO: loop 0 means no loop (improve detection)
priv->config_loaded = false;
}
else {
priv->cfg = *cfg;
priv->config_loaded = true;
}
//TODO validate, etc
//TODO validate, etc (for now most incorrect values are ignored)
// apply now if possible to update format info
if (priv->vgmstream) {
api_apply_config(priv);
}
}
@ -81,24 +99,24 @@ void libvgmstream_priv_reset(libvgmstream_priv_t* priv, bool reset_buf) {
priv->decode_done = false;
}
libvgmstream_sample_t api_get_output_sample_type(libvgmstream_priv_t* priv) {
libvgmstream_sfmt_t api_get_output_sample_type(libvgmstream_priv_t* priv) {
VGMSTREAM* v = priv->vgmstream;
sfmt_t format = mixing_get_output_sample_type(v);
switch(format) {
case SFMT_S16: return LIBVGMSTREAM_SAMPLE_PCM16;
case SFMT_FLT: return LIBVGMSTREAM_SAMPLE_FLOAT;
case SFMT_S16: return LIBVGMSTREAM_SFMT_PCM16;
case SFMT_FLT: return LIBVGMSTREAM_SFMT_FLOAT;
default:
return 0x00; //???
}
}
int api_get_sample_size(libvgmstream_sample_t sample_type) {
switch(sample_type) {
case LIBVGMSTREAM_SAMPLE_PCM24:
case LIBVGMSTREAM_SAMPLE_PCM32:
case LIBVGMSTREAM_SAMPLE_FLOAT:
int api_get_sample_size(libvgmstream_sfmt_t sample_format) {
switch(sample_format) {
//case LIBVGMSTREAM_SFMT_PCM24:
//case LIBVGMSTREAM_SFMT_PCM32:
case LIBVGMSTREAM_SFMT_FLOAT:
return 0x04;
case LIBVGMSTREAM_SAMPLE_PCM16:
case LIBVGMSTREAM_SFMT_PCM16:
default:
return 0x02;
}

View File

@ -3,18 +3,6 @@
#include "mixing.h"
static void load_vgmstream(libvgmstream_priv_t* priv, libvgmstream_options_t* opt) {
STREAMFILE* sf_api = open_api_streamfile(opt->libsf);
if (!sf_api)
return;
//TODO: handle internal format_id
sf_api->stream_index = opt->subsong_index;
priv->vgmstream = init_vgmstream_from_STREAMFILE(sf_api);
close_streamfile(sf_api);
}
static void apply_config(libvgmstream_priv_t* priv) {
libvgmstream_config_t* cfg = &priv->cfg;
@ -36,23 +24,37 @@ static void apply_config(libvgmstream_priv_t* priv) {
if (!vcfg.allow_play_forever)
vcfg.play_forever = 0;
// Traditionally in CLI loop_count = 0 removes loops but this is pretty odd for a lib
// (calling _setup with nothing set would remove most audio).
// For now loop_count 0 is set to 1, and loop_count <0 is assumed to be same 0
if (vcfg.loop_count == 0) {
vcfg.loop_count = 1;
} else if (vcfg.loop_count < 0)
vcfg.loop_count = 0;
vgmstream_apply_config(priv->vgmstream, &vcfg);
}
static void prepare_mixing(libvgmstream_priv_t* priv, libvgmstream_options_t* opt) {
static void prepare_mixing(libvgmstream_priv_t* priv) {
libvgmstream_config_t* cfg = &priv->cfg;
/* enable after config but before outbuf */
if (priv->cfg.auto_downmix_channels) {
vgmstream_mixing_autodownmix(priv->vgmstream, priv->cfg.auto_downmix_channels);
if (cfg->auto_downmix_channels) {
vgmstream_mixing_autodownmix(priv->vgmstream, cfg->auto_downmix_channels);
}
else if (opt && opt->stereo_track >= 1) {
vgmstream_mixing_stereo_only(priv->vgmstream, opt->stereo_track - 1);
else if (cfg->stereo_track >= 1) {
vgmstream_mixing_stereo_only(priv->vgmstream, cfg->stereo_track - 1);
}
if (priv->cfg.force_pcm16) {
mixing_macro_output_sample_format(priv->vgmstream, SFMT_S16);
}
else if (priv->cfg.force_float) {
mixing_macro_output_sample_format(priv->vgmstream, SFMT_FLT);
if (cfg->force_sfmt) {
sfmt_t type = SFMT_NONE;
switch(cfg->force_sfmt) {
case LIBVGMSTREAM_SFMT_PCM16: type = SFMT_S16; break;
case LIBVGMSTREAM_SFMT_FLOAT: type = SFMT_F32; break;
default: break;
}
mixing_macro_output_sample_format(priv->vgmstream, type);
}
vgmstream_mixing_enable(priv->vgmstream, INTERNAL_BUF_SAMPLES, NULL /*&input_channels*/, NULL /*&output_channels*/);
@ -76,11 +78,11 @@ static void update_format_info(libvgmstream_priv_t* priv) {
fmt->channels = v->channels;
fmt->input_channels = 0;
vgmstream_mixing_enable(v, 0, &fmt->input_channels, &fmt->channels);
vgmstream_mixing_enable(v, 0, &fmt->input_channels, &fmt->channels); //query
fmt->channel_layout = v->channel_layout;
fmt->sample_type = api_get_output_sample_type(priv);
fmt->sample_size = api_get_sample_size(fmt->sample_type);
fmt->sample_format = api_get_output_sample_type(priv);
fmt->sample_size = api_get_sample_size(fmt->sample_format);
fmt->sample_rate = v->sample_rate;
@ -105,26 +107,58 @@ static void update_format_info(libvgmstream_priv_t* priv) {
}
}
LIBVGMSTREAM_API int libvgmstream_open_stream(libvgmstream_t* lib, libvgmstream_options_t* opt) {
if (!lib ||!lib->priv)
return LIBVGMSTREAM_ERROR_GENERIC;
if (!opt || !opt->libsf || opt->subsong_index < 0)
// apply config if data + config is loaded and not already loaded
void api_apply_config(libvgmstream_priv_t* priv) {
if (priv->setup_done)
return;
if (!priv->vgmstream)
return;
apply_config(priv);
prepare_mixing(priv);
update_position(priv);
update_format_info(priv);
priv->setup_done = true;
}
static void load_vgmstream(libvgmstream_priv_t* priv, libstreamfile_t* libsf, int subsong_index) {
STREAMFILE* sf_api = open_api_streamfile(libsf);
if (!sf_api)
return;
//TODO: handle format_id
sf_api->stream_index = subsong_index;
priv->vgmstream = init_vgmstream_from_STREAMFILE(sf_api);
close_streamfile(sf_api);
}
LIBVGMSTREAM_API int libvgmstream_open_stream(libvgmstream_t* lib, libstreamfile_t* libsf, int subsong_index) {
if (!lib ||!lib->priv || !libsf)
return LIBVGMSTREAM_ERROR_GENERIC;
// close loaded song if any + reset
libvgmstream_close_stream(lib);
libvgmstream_priv_t* priv = lib->priv;
if (subsong_index < 0)
return LIBVGMSTREAM_ERROR_GENERIC;
load_vgmstream(priv, opt);
load_vgmstream(priv, libsf, subsong_index);
if (!priv->vgmstream)
return LIBVGMSTREAM_ERROR_GENERIC;
apply_config(priv);
prepare_mixing(priv, opt);
update_position(priv);
update_format_info(priv);
// apply now if possible to update format info
if (priv->config_loaded) {
api_apply_config(priv);
}
else {
// no config: just update info (apply_config will be called later)
update_position(priv);
update_format_info(priv);
}
return LIBVGMSTREAM_OK;
}
@ -138,6 +172,8 @@ LIBVGMSTREAM_API void libvgmstream_close_stream(libvgmstream_t* lib) {
close_vgmstream(priv->vgmstream);
priv->vgmstream = NULL;
priv->setup_done = false;
//priv->config_loaded = false; // loaded config still applies (_close is also called on _open)
libvgmstream_priv_reset(priv, true);
}

View File

@ -68,6 +68,13 @@ LIBVGMSTREAM_API int libvgmstream_render(libvgmstream_t* lib) {
return LIBVGMSTREAM_ERROR_GENERIC;
libvgmstream_priv_t* priv = lib->priv;
// setup if not called (mainly to make sure mixing is enabled) //TODO: handle internally
// (for cases where _open_stream is called but not _setup)
if (!priv->setup_done) {
api_apply_config(priv);
}
if (priv->decode_done)
return LIBVGMSTREAM_ERROR_GENERIC;

View File

@ -12,28 +12,33 @@ static int get_internal_log_level(libvgmstream_loglevel_t level) {
}
}
LIBVGMSTREAM_API void libvgmstream_set_log(libvgmstream_log_t* cfg) {
if (!cfg)
return;
int ilevel = get_internal_log_level(cfg->level);
if (cfg->stdout_callback) {
//vgmstream_set_log_stdout(ilevel);
vgm_log_set_callback(NULL, ilevel, 1, NULL);
LIBVGMSTREAM_API void libvgmstream_set_log(libvgmstream_loglevel_t level, void (*callback)(int level, const char* str)) {
int ilevel = get_internal_log_level(level);
if (callback) {
vgm_log_set_callback(NULL, ilevel, 0, callback);
}
else {
//vgmstream_set_log_callback(ilevel, cfg->callback);
vgm_log_set_callback(NULL, ilevel, 0, cfg->callback);
vgm_log_set_callback(NULL, ilevel, 1, NULL);
}
}
LIBVGMSTREAM_API const char** libvgmstream_get_extensions(size_t* size) {
return vgmstream_get_formats(size);
LIBVGMSTREAM_API const char** libvgmstream_get_extensions(int* size) {
if (!size)
return NULL;
size_t tmp = 0;
const char** list = vgmstream_get_formats(&tmp);
*size = tmp;
return list;
}
LIBVGMSTREAM_API const char** libvgmstream_get_common_extensions(size_t* size) {
return vgmstream_get_common_formats(size);
LIBVGMSTREAM_API const char** libvgmstream_get_common_extensions(int* size) {
if (!size)
return NULL;
size_t tmp = 0;
const char** list = vgmstream_get_common_formats(&tmp);
*size = tmp;
return list;
}

View File

@ -33,31 +33,36 @@ typedef struct {
} libvgmstream_priv_buf_t;
// used to calculate and stop stream (to be removed once VGMSTREAM can stop on its own)
typedef struct {
int64_t play_forever;
int64_t play_samples;
int64_t current;
} libvgmstream_priv_position_t;
/* vgmstream context/handle */
// vgmstream context/handle
typedef struct {
libvgmstream_format_t fmt; // externally exposed
libvgmstream_decoder_t dec; // externally exposed
// externally exposed to API
libvgmstream_format_t fmt;
libvgmstream_decoder_t dec;
libvgmstream_config_t cfg; // internal copy
// internals
libvgmstream_config_t cfg;
VGMSTREAM* vgmstream;
libvgmstream_priv_buf_t buf;
libvgmstream_priv_position_t pos;
bool config_loaded;
bool setup_done;
bool decode_done;
} libvgmstream_priv_t;
void libvgmstream_priv_reset(libvgmstream_priv_t* priv, bool reset_buf);
libvgmstream_sample_t api_get_output_sample_type(libvgmstream_priv_t* priv);
int api_get_sample_size(libvgmstream_sample_t sample_type);
libvgmstream_sfmt_t api_get_output_sample_type(libvgmstream_priv_t* priv);
int api_get_sample_size(libvgmstream_sfmt_t sample_format);
void api_apply_config(libvgmstream_priv_t* priv);
STREAMFILE* open_api_streamfile(libstreamfile_t* libsf);

View File

@ -5,63 +5,23 @@ static libstreamfile_t* libstreamfile_from_streamfile(STREAMFILE* sf);
/* libstreamfile_t for external use, as a default implementation calling some internal SF */
typedef struct {
int64_t offset;
int64_t size;
STREAMFILE* sf;
char name[PATH_LIMIT];
} libsf_priv_t;
static int libsf_read(void* user_data, uint8_t* dst, int dst_size) {
static int libsf_read(void* user_data, uint8_t* dst, int64_t offset, int length) {
libsf_priv_t* priv = user_data;
if (!priv || !dst)
return 0;
int bytes = priv->sf->read(priv->sf, dst, priv->offset, dst_size);
priv->offset += bytes;
return bytes;
}
static int64_t libsf_seek(void* user_data, int64_t offset, int whence) {
libsf_priv_t* priv = user_data;
if (!priv)
return -1;
switch (whence) {
case LIBSTREAMFILE_SEEK_SET: /* absolute */
break;
case LIBSTREAMFILE_SEEK_CUR: /* relative to current */
offset += priv->offset;
break;
case LIBSTREAMFILE_SEEK_END: /* relative to file end (should be negative) */
offset += priv->size;
break;
default:
break;
}
/* clamp offset like fseek */
if (offset > priv->size)
offset = priv->size;
else if (offset < 0)
offset = 0;
/* main seek */
priv->offset = offset;
return 0;
return priv->sf->read(priv->sf, dst, offset, length);
}
static int64_t libsf_get_size(void* user_data) {
libsf_priv_t* priv = user_data;
if (!priv)
return 0;
return priv->size;
return priv->sf->get_size(priv->sf);
}
static const char* libsf_get_name(void* user_data) {
libsf_priv_t* priv = user_data;
if (!priv)
return NULL;
if (priv->name[0] == '\0') {
priv->sf->get_name(priv->sf, priv->name, sizeof(priv->name));
@ -72,7 +32,8 @@ static const char* libsf_get_name(void* user_data) {
static libstreamfile_t* libsf_open(void* user_data, const char* filename) {
libsf_priv_t* priv = user_data;
if (!priv || !priv->sf || !filename)
if (!filename)
return NULL;
STREAMFILE* sf = priv->sf->open(priv->sf, filename, 0);
@ -111,7 +72,6 @@ static libstreamfile_t* libstreamfile_from_streamfile(STREAMFILE* sf) {
if (!libsf) goto fail;
libsf->read = libsf_read;
libsf->seek = libsf_seek;
libsf->get_size = libsf_get_size;
libsf->get_name = libsf_get_name;
libsf->open = libsf_open;

View File

@ -18,10 +18,10 @@ typedef struct {
char name[PATH_LIMIT];
} cache_priv_t;
static int cache_read(void* user_data, uint8_t* dst, int dst_size) {
static int cache_read(void* user_data, uint8_t* dst, int64_t offset, int length) {
cache_priv_t* priv = user_data;
size_t read_total = 0;
if (!dst || dst_size <= 0)
if (!dst || length <= 0 || offset < 0)
return 0;
/* is the part of the requested length in the buffer? */
@ -30,19 +30,22 @@ static int cache_read(void* user_data, uint8_t* dst, int dst_size) {
int buf_into = (int)(priv->offset - priv->buf_offset);
buf_limit = priv->valid_size - buf_into;
if (buf_limit > dst_size)
buf_limit = dst_size;
if (buf_limit > length)
buf_limit = length;
memcpy(dst, priv->buf + buf_into, buf_limit);
read_total += buf_limit;
dst_size -= buf_limit;
length -= buf_limit;
priv->offset += buf_limit;
dst += buf_limit;
}
/* possible if all data was copied to buf and FD closed */
if (!priv->libsf)
return read_total;
/* read the rest of the requested length */
while (dst_size > 0) {
while (length > 0) {
size_t buf_limit;
/* ignore requests at EOF */
@ -52,18 +55,15 @@ static int cache_read(void* user_data, uint8_t* dst, int dst_size) {
break;
}
/* position to new offset */
priv->libsf->seek(priv, priv->offset, 0);
/* fill the buffer (offset now is beyond buf_offset) */
priv->buf_offset = priv->offset;
priv->valid_size = priv->libsf->read(priv, priv->buf, priv->buf_size);
priv->valid_size = priv->libsf->read(priv, priv->buf, priv->buf_offset, priv->buf_size);
/* decide how much must be read this time */
if (dst_size > priv->buf_size)
if (length > priv->buf_size)
buf_limit = priv->buf_size;
else
buf_limit = dst_size;
buf_limit = length;
/* give up on partial reads (EOF) */
if (priv->valid_size < buf_limit) {
@ -77,40 +77,14 @@ static int cache_read(void* user_data, uint8_t* dst, int dst_size) {
memcpy(dst, priv->buf, buf_limit);
priv->offset += buf_limit;
read_total += buf_limit;
dst_size -= buf_limit;
length -= buf_limit;
dst += buf_limit;
}
priv->offset = offset; /* last fread offset */
return read_total;
}
static int64_t cache_seek(void* user_data, int64_t offset, int whence) {
cache_priv_t* priv = user_data;
switch (whence) {
case LIBSTREAMFILE_SEEK_SET: /* absolute */
break;
case LIBSTREAMFILE_SEEK_CUR: /* relative to current */
offset += priv->offset;
break;
case LIBSTREAMFILE_SEEK_END: /* relative to file end (should be negative) */
offset += priv->file_size;
break;
default:
break;
}
/* clamp offset like fseek */
if (offset > priv->file_size)
offset = priv->file_size;
else if (offset < 0)
offset = 0;
/* main seek */
priv->offset = offset;
return 0;
}
static int64_t cache_get_size(void* user_data) {
cache_priv_t* priv = user_data;
return priv->file_size;
@ -168,7 +142,6 @@ LIBVGMSTREAM_API libstreamfile_t* libstreamfile_open_buffered(libstreamfile_t* e
if (!libsf) goto fail;
libsf->read = cache_read;
libsf->seek = cache_seek;
libsf->get_size = cache_get_size;
libsf->get_name = cache_get_name;
libsf->open = cache_open;

View File

@ -15,8 +15,7 @@ typedef struct {
static size_t api_read(API_STREAMFILE* sf, uint8_t* dst, offv_t offset, size_t length) {
void* user_data = sf->libsf->user_data;
sf->libsf->seek(sf->libsf->user_data, offset, LIBSTREAMFILE_SEEK_SET);
return sf->libsf->read(user_data, dst, length);
return sf->libsf->read(user_data, dst, offset, length);
}
static size_t api_get_size(API_STREAMFILE* sf) {

View File

@ -667,7 +667,6 @@ static const char* extension_list[] = {
"wic", //txth/reserved [Road Rash (SAT)-videos]
"wip", //txth/reserved [Colin McRae DiRT (PC)]
"wlv", //txth/reserved [ToeJam & Earl III: Mission to Earth (DC)]
"wmus", //fake extension (to be removed)
"wp2",
"wpd",
"wsd",
@ -1178,7 +1177,6 @@ static const meta_info meta_info_list[] = {
{meta_PS2_SND, "Might and Magic SSND Header"},
{meta_SMSS, "Treasure SMSS header"},
{meta_ADS_MIDWAY, "Midway ADS header"},
{meta_PS2_MCG, "Gunvari MCG Header"},
{meta_ZSD, "Konami ZSD header"},
{meta_REDSPARK, "RedSpark header"},
{meta_RAGE_AUD, "Rockstar AUD header"},
@ -1214,7 +1212,7 @@ static const meta_info meta_info_list[] = {
{meta_WB, "Triangle Service .WB header"},
{meta_S14, "Namco .S14 raw header"},
{meta_SSS, "Namco .SSS raw header"},
{meta_PS2_GCM, "Namco GCM header"},
{meta_MCG, "Namco MCG header"},
{meta_SMPL, "Skonec SMPL header"},
{meta_MSA, "Success .MSA header"},
{meta_VOI, "Irem .VOI header"},
@ -1250,7 +1248,6 @@ static const meta_info meta_info_list[] = {
{meta_LSF_N1NJ4N, "Gizmondo Studios Helsingborg LSF header"},
{meta_XWAV, "feelplus XWAV header"},
{meta_RAW_SNDS, "PC .snds raw header"},
{meta_PS2_WMUS, "assumed The Warriors Sony ADPCM by .wmus extension"},
{meta_HYPERSCAN_KVAG, "Mattel Hyperscan KVAG"},
{meta_PSND, "Polarbit PSND header"},
{meta_ADP_WILDFIRE, "Wildfire ADP! header"},

View File

@ -18,9 +18,9 @@
* This may make the API a bit odd, will probably improve later. Probably.
*
* Notes:
* - now there is an API, internals (vgmstream.h) may change in the future so avoid accesing them
* - now there is an API, internals (vgmstream.h) will change in the future so avoid accesing them
* - some details described in the API may not happen at the moment (defined for future changes)
* - uses long-winded libvgmstream_* names since internals alredy use the vgmstream_* 'namespace', #define as needed
* - uses long-winded libvgmstream_* names since internals alredy use the vgmstream_* 'namespace', #define if needed
* - c-strings should be in UTF-8
*/
@ -71,20 +71,20 @@ LIBVGMSTREAM_API uint32_t libvgmstream_get_version(void);
/*****************************************************************************/
/* DECODE */
/* interleaved samples: buf[0]=ch0, buf[1]=ch1, buf[2]=ch0, buf[3]=ch0, ... */
typedef enum {
LIBVGMSTREAM_SAMPLE_PCM16 = 1,
LIBVGMSTREAM_SAMPLE_PCM24 = 2,
LIBVGMSTREAM_SAMPLE_PCM32 = 3,
LIBVGMSTREAM_SAMPLE_FLOAT = 4,
} libvgmstream_sample_t;
/* available sample formats, interleaved: buf[0]=ch0, buf[1]=ch1, buf[2]=ch0, buf[3]=ch0, ... */
typedef enum {
LIBVGMSTREAM_SFMT_PCM16 = 1,
//LIBVGMSTREAM_SFMT_PCM24 = 2,
//LIBVGMSTREAM_SFMT_PCM32 = 3,
LIBVGMSTREAM_SFMT_FLOAT = 4,
} libvgmstream_sfmt_t;
/* current song info, may be copied around (values are info-only) */
typedef struct {
/* main (always set) */
int channels; // output channels
int sample_rate; // output sample rate
libvgmstream_sample_t sample_type; // output buffer's sample type
libvgmstream_sfmt_t sample_format; // output buffer's sample type
int sample_size; // derived from sample_type (pcm16=0x02, float=0x04, etc)
/* extra info (may be 0 if not known or not relevant) */
@ -126,8 +126,8 @@ typedef struct {
// query description and since libvgmstream returns its own copy it shouldn't be too much of a problem
// ** (may be separated later)
int format_id; // when reopening subfiles or similar formats without checking other all possible formats
// ** this value WILL change without warning between vgmstream versions/commits, but usually only add
int format_id; // current format's ID (can be set when reopening streams to load a particular format)
// ** this value WILL change without warning between vgmstream versions/commits
} libvgmstream_format_t;
@ -165,6 +165,7 @@ LIBVGMSTREAM_API void libvgmstream_free(libvgmstream_t* lib);
/* configures how vgmstream behaves internally when playing a file */
typedef struct {
bool disable_config_override; // ignore forced (TXTP) config
bool allow_play_forever; // must allow manually as some cases a TXTP may set loop forever but client may not handle it
@ -174,46 +175,39 @@ typedef struct {
bool really_force_loop; // forces full loops (0..samples) even if file has loop points
bool ignore_fade; // don't fade after N loops and play remaning stream (for files with outros)
double loop_count; // target loops (values like 1.5 are ok)
double loop_count; // target loops (values like 1.5 are ok); defaults to 1; set to -1 to remove the loop section
double fade_time; // fade period after target loops
double fade_delay; // fade delay after target loops
int stereo_track; // forces vgmstream to decode one 2ch+2ch+2ch... 'track' and discard other channels, where 0 = disabled, 1..N = Nth track
int auto_downmix_channels; // downmix if vgmstream's channels are higher than value
// ** for players that can only handle N channels
// ** this type of downmixing is very simplistic and not recommended
bool force_pcm16; // forces output buffer to be remixed into PCM16
bool force_float; // forces output buffer to be remixed into float
libvgmstream_sfmt_t force_sfmt; // forces output buffer to be remixed into some sample format
//int format_id; // force a format (for example when loading new subsong of the same archive, for a minuscule speed up)
// // ** only applies when called before _open_stream
} libvgmstream_config_t;
/* pass default config, that will be applied to song on open
* - invalid config or complex cases (ex. some TXTP) may ignore these settings
* - should be called without a song loaded (before _open or after _close)
* - without config vgmstream will decode the current stream once
/* optionally pass config to apply to next _open_stream (or current stream if already loaded and not setup previously)
* - some settings may be ignored in invalid or complex cases (ex. TXTP with pre-configured options)
* - once config is applied to current stream new _setup calls only apply to next _open_stream
* - pass NULL to clear current config
* - remember config may change format info like channels or output format (recheck if calling after loading song)
*/
LIBVGMSTREAM_API void libvgmstream_setup(libvgmstream_t* lib, libvgmstream_config_t* cfg);
/* configures how vgmstream opens the format */
typedef struct {
libstreamfile_t* libsf; // custom IO streamfile that provides reader info for vgmstream
// ** not needed after _open and should be closed, as vgmstream re-opens its own SFs internally as needed
int subsong_index; // target subsong (1..N) or 0 = default/first
// ** to check if a file has subsongs, _open first + check format->total_subsongs (then _open 2nd, 3rd, etc)
int format_id; // force a format (for example when loading new subsong of the same archive)
int stereo_track; // forces vgmstream to decode one 2ch+2ch+2ch... 'track' and discard other channels, where 0 = disabled, 1..N = Nth track
} libvgmstream_options_t;
/* Opens file based on config and prepares it to play if supported.
* - returns < 0 on error (file not recognised, invalid subsong index, etc)
* - will close currently loaded song if needed
* - libsf (custom IO) is not needed after _open and should be closed, as vgmstream re-opens as needed
* - subsong can be 1..N or 0 = default/first
* ** to check if a file has subsongs, _open default + check format->total_subsongs (then _open Nth)
*/
LIBVGMSTREAM_API int libvgmstream_open_stream(libvgmstream_t* lib, libvgmstream_options_t* open_options);
LIBVGMSTREAM_API int libvgmstream_open_stream(libvgmstream_t* lib, libstreamfile_t* libsf, int subsong_index);
/* Closes current song; may still use libvgmstream to open other songs
*/
@ -248,7 +242,6 @@ LIBVGMSTREAM_API void libvgmstream_seek(libvgmstream_t* lib, int64_t sample);
LIBVGMSTREAM_API void libvgmstream_reset(libvgmstream_t* lib);
/*****************************************************************************/
/* HELPERS */
@ -259,29 +252,25 @@ typedef enum {
LIBVGMSTREAM_LOG_LEVEL_NONE = 100,
} libvgmstream_loglevel_t;
typedef struct {
libvgmstream_loglevel_t level; // log level
void (*callback)(int level, const char* str); // log callback
bool stdout_callback; // use default log callback rather than user supplied
} libvgmstream_log_t;
/* Defines a global log callback, as vgmstream sometimes communicates format issues to the user.
* - note that log is currently set globally rather than per libvgmstream_t
* - call with LOG_LEVEL_NONE to disable current callback
* - call with NULL callback to use default stdout callback
*/
LIBVGMSTREAM_API void libvgmstream_set_log(libvgmstream_log_t* cfg);
LIBVGMSTREAM_API void libvgmstream_set_log(libvgmstream_loglevel_t level, void (*callback)(int level, const char* str));
/* Returns a list of supported extensions (WARNING: it's pretty big), such as "adx", "dsp", etc.
* Mainly for plugins that want to know which extensions are supported.
* - returns NULL if no size is provided
*/
LIBVGMSTREAM_API const char** libvgmstream_get_extensions(size_t* size);
LIBVGMSTREAM_API const char** libvgmstream_get_extensions(int* size);
/* Same as above, buf returns a list what vgmstream considers "common" formats (such as "wav", "ogg"),
* which usually one doesn't want to associate to vgmstream.
* - returns NULL if no size is provided
*/
LIBVGMSTREAM_API const char** libvgmstream_get_common_extensions(size_t* size);
LIBVGMSTREAM_API const char** libvgmstream_get_common_extensions(int* size);
typedef struct {
@ -301,7 +290,7 @@ LIBVGMSTREAM_API bool libvgmstream_is_valid(const char* filename, libvgmstream_v
typedef struct {
bool force_title; // TODO: what was this for?
bool force_title; // force stream name as title (by default it may be hiddedn if file has no subsongs)
bool subsong_range; // print a range of possible subsongs after title 'filename#1~N'
bool remove_extension; // remove extension from passed filename
bool remove_archive; // remove '(archive)|(subfile)' format of some plugins
@ -350,11 +339,13 @@ LIBVGMSTREAM_API void libvgmstream_tags_find(libvgmstream_tags_t* tags, const ch
/* Extracts next valid tag in tagfile to key/val.
* - returns false if no more tags are found (meant to be called repeatedly until false)
* - key/values are trimmed of beginning/end whitespaces and values are in UTF-8
* - key/values are trimmed of beginning/end whitespaces and values are UTF-8
*/
LIBVGMSTREAM_API bool libvgmstream_tags_next_tag(libvgmstream_tags_t* tags);
/* Closes tags. */
/* Closes tags.
* - passed libsf is not closed
*/
LIBVGMSTREAM_API void libvgmstream_tags_free(libvgmstream_tags_t* tags);

View File

@ -552,6 +552,7 @@
<ClCompile Include="meta\mattel_hyperscan.c" />
<ClCompile Include="meta\maxis_xa.c" />
<ClCompile Include="meta\mca.c" />
<ClCompile Include="meta\mcg.c" />
<ClCompile Include="meta\mib_mih.c" />
<ClCompile Include="meta\mic_koei.c" />
<ClCompile Include="meta\mjb_mjh.c" />
@ -626,19 +627,15 @@
<ClCompile Include="meta\ps2_adm.c" />
<ClCompile Include="meta\ps2_ass.c" />
<ClCompile Include="meta\ps2_bmdx.c" />
<ClCompile Include="meta\ps2_gcm.c" />
<ClCompile Include="meta\ps2_hsf.c" />
<ClCompile Include="meta\ps2_iab.c" />
<ClCompile Include="meta\ps2_joe.c" />
<ClCompile Include="meta\ps2_mcg.c" />
<ClCompile Include="meta\ps2_rnd.c" />
<ClCompile Include="meta\ps2_snd.c" />
<ClCompile Include="meta\ps2_sps.c" />
<ClCompile Include="meta\ps2_va3.c" />
<ClCompile Include="meta\ps2_vbk.c" />
<ClCompile Include="meta\ps2_vgv.c" />
<ClCompile Include="meta\ps2_vms.c" />
<ClCompile Include="meta\ps2_wmus.c" />
<ClCompile Include="meta\psb.c" />
<ClCompile Include="meta\psf.c" />
<ClCompile Include="meta\psnd.c" />
@ -745,6 +742,7 @@
<ClCompile Include="meta\ue4opus.c" />
<ClCompile Include="meta\undefind.c" />
<ClCompile Include="meta\utk.c" />
<ClCompile Include="meta\va3.c" />
<ClCompile Include="meta\vab.c" />
<ClCompile Include="meta\vag.c" />
<ClCompile Include="meta\vai.c" />

View File

@ -1486,6 +1486,9 @@
<ClCompile Include="meta\mca.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\mcg.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\mib_mih.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
@ -1708,9 +1711,6 @@
<ClCompile Include="meta\ps2_bmdx.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ps2_gcm.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ps2_hsf.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
@ -1720,9 +1720,6 @@
<ClCompile Include="meta\ps2_joe.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ps2_mcg.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ps2_rnd.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
@ -1732,9 +1729,6 @@
<ClCompile Include="meta\ps2_sps.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ps2_va3.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ps2_vbk.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
@ -1744,9 +1738,6 @@
<ClCompile Include="meta\ps2_vms.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ps2_wmus.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\psb.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
@ -2065,6 +2056,9 @@
<ClCompile Include="meta\utk.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\va3.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\vab.c">
<Filter>meta\Source Files</Filter>
</ClCompile>

View File

@ -3,44 +3,33 @@
#include "libvgmstream.h"
/* vgmstream's IO API, defined as a "streamfile" ('SF').
*
* vgmstream mostly assumes there is an underlying filesystem (as usual in games), plus given video game formats are
*
* vgmstream mostly assumes there is an underlying filesystem (as usual in games), plus given video game formats are
* often ill-defined it needs extra ops to handle edge cases: seeking + reading from arbitrary offsets, opening companion
* files, filename/size tests, etc.
*
*
* If your case is too different you may still create a partial streamfile: returning a fake filename, only handling "open"
* that reopens itself (same filename), etc. Simpler formats should work fine.
*/
enum {
LIBSTREAMFILE_SEEK_SET = 0,
LIBSTREAMFILE_SEEK_CUR = 1,
LIBSTREAMFILE_SEEK_END = 2,
//LIBSTREAMFILE_SEEK_GET_OFFSET = 3,
//LIBSTREAMFILE_SEEK_GET_SIZE = 5,
};
// should be "libvgmstream_streamfile_t" but it was getting unwieldly
typedef struct libstreamfile_t {
//uint32_t flags; // info flags for vgmstream
void* user_data; // any internal structure
/* read 'length' data at internal offset to 'dst'
/* read 'length' data at 'offset' to 'dst'
* - assumes 0 = failure/EOF
* - note that vgmstream needs to seek + read from arbitrary offset (non-linearly) fairly often
*/
int (*read)(void* user_data, uint8_t* dst, int dst_size);
/* seek to offset
* - note that vgmstream needs to seek + read fairly often (to be optimized someday)
*/
int64_t (*seek)(void* user_data, int64_t offset, int whence);
int (*read)(void* user_data, uint8_t* dst, int64_t offset, int length);
/* get max offset (typically for checks or sample calculations)
*/
int64_t (*get_size)(void* user_data);
/* get current filename (used to open same or other streamfiles and heuristics; no need to be a real path)
/* get current filename
* - used to open same or other streamfiles and heuristics; no need to be a real path but extension matters
*/
const char* (*get_name)(void* user_data);
@ -49,13 +38,14 @@ typedef struct libstreamfile_t {
*/
struct libstreamfile_t* (*open)(void* user_data, const char* filename);
/* free current SF */
/* free current SF
*/
void (*close)(struct libstreamfile_t* libsf);
} libstreamfile_t;
/* helper */
/* helpers */
static inline void libstreamfile_close(libstreamfile_t* libsf) {
if (!libsf || !libsf->close)
return;

View File

@ -385,6 +385,7 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) {
mpeg_custom_config cfg = {0};
cfg.fsb_padding = (vgmstream->channels > 2 ? 16 : 4); /* observed default */
cfg.data_size = fsb5.stream_offset + fsb5.stream_size;
vgmstream->codec_data = init_mpeg_custom(sb, fsb5.stream_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_FSB, &cfg);
if (!vgmstream->codec_data) goto fail;

View File

@ -1,64 +1,32 @@
#include "meta.h"
#include "../util.h"
#include "../coding/coding.h"
#include "../util/meta_utils.h"
/* KRAW (from Geometry Wars - Galaxies) */
VGMSTREAM * init_vgmstream_kraw(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset;
int loop_flag = 0;
int channel_count;
/* KRAW - from Geometry Wars: Galaxies (Wii) */
VGMSTREAM* init_vgmstream_kraw(STREAMFILE* sf) {
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("kraw",filename_extension(filename))) goto fail;
/* checks */
if (!is_id32be(0x00,sf, "kRAW"))
return NULL;
// .kRAW: actual extension
if (!check_extensions(sf, "kraw"))
return NULL;
/* check header */
if (read_32bitBE(0x00,streamFile) != 0x6B524157) /* "kRAW" */
goto fail;
meta_header_t h = {0};
h.data_size = read_u32be(0x04,sf);
loop_flag = 0;
channel_count = 1;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
h.meta = meta_KRAW;
/* fill in the vital statistics */
start_offset = 0x8;
vgmstream->channels = channel_count;
vgmstream->sample_rate = 32000;
vgmstream->coding_type = coding_PCM16BE;
vgmstream->num_samples = read_32bitBE(0x04,streamFile)/2/channel_count;
if (loop_flag) {
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = read_32bitBE(0x04,streamFile)/2/channel_count;
}
h.stream_offset = 0x08;
h.channels = 1;
h.sample_rate = 32000;
h.num_samples = pcm16_bytes_to_samples(h.data_size, h.channels);
h.allow_dual_stereo = true;
vgmstream->layout_type = layout_none;
vgmstream->meta_type = meta_KRAW;
vgmstream->allow_dual_stereo = 1;
h.coding = coding_PCM16BE;
h.layout = layout_none;
h.open_stream = true;
h.sf = sf;
/* open the file for reading */
{
int i;
STREAMFILE * file;
file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!file) goto fail;
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = file;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=start_offset+
vgmstream->interleave_block_size*i;
}
}
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
return alloc_metastream(&h);
}

View File

@ -512,7 +512,6 @@ static bool parse_ktsr_subfile(ktsr_header_t* ktsr, STREAMFILE* sf, uint32_t off
ktsr->stream_sizes[0] = read_u32le(offset + 0x38, sf);
}
ktsr->is_external = true;
VGM_LOG("k=%x\n", ktsr->codec_value);
break;
case 0x41FDBD4E: /* internal [Attack on Titan: Wings of Freedom (Vita)] */

View File

@ -2,8 +2,8 @@
#include "../util.h"
#include "../coding/coding.h"
/* .GCM - from PS2 Namco games [Gunvari Collection + Time Crisis (PS2), NamCollection (PS2)] */
VGMSTREAM* init_vgmstream_ps2_gcm(STREAMFILE* sf) {
/* MCG - from PS2 Namco games [Gunvari Collection + Time Crisis (PS2), NamCollection (PS2)] */
VGMSTREAM* init_vgmstream_mcg(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
uint32_t start_offset, name_offset;
uint32_t vagp_l_offset, vagp_r_offset, track_size, data_size, channel_size;
@ -12,11 +12,10 @@ VGMSTREAM* init_vgmstream_ps2_gcm(STREAMFILE* sf) {
/* checks */
if (!is_id32be(0x00,sf, "MCG\0"))
goto fail;
/* .gcm: actual extension */
return NULL;
// .gcm: actual extension
if (!check_extensions(sf, "gcm"))
goto fail;
return NULL;
/* format is two v4 "VAGp" headers then interleaved data (even for 6ch files) */
@ -59,7 +58,7 @@ VGMSTREAM* init_vgmstream_ps2_gcm(STREAMFILE* sf) {
vgmstream = allocate_vgmstream(channels, 0);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_PS2_GCM;
vgmstream->meta_type = meta_MCG;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = ps_bytes_to_samples(channel_size, 1);
vgmstream->coding_type = coding_PSX;

View File

@ -366,8 +366,6 @@ VGMSTREAM* init_vgmstream_smss(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_ads_midway(STREAMFILE* sf);
VGMSTREAM * init_vgmstream_ps2_mcg(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_zsd(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_vgs_ps(STREAMFILE *streamFile);
@ -431,7 +429,7 @@ VGMSTREAM* init_vgmstream_wb(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_raw_s14_sss(STREAMFILE* sf);
VGMSTREAM * init_vgmstream_ps2_gcm(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_mcg(STREAMFILE* streamFile);
VGMSTREAM* init_vgmstream_smpl(STREAMFILE* sf);
@ -490,8 +488,6 @@ VGMSTREAM * init_vgmstream_xwav_old(STREAMFILE* sf);
VGMSTREAM * init_vgmstream_raw_snds(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ps2_wmus(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_hyperscan_kvag(STREAMFILE* streamFile);
VGMSTREAM* init_vgmstream_psnd(STREAMFILE* sf);

View File

@ -1446,7 +1446,7 @@ VGMSTREAM* init_vgmstream_dsp_wiivoice(STREAMFILE* sf) {
}
/* WIIADPCM - Exient wrapper [Need for Speed: Hot Pursuit (Wii), Angry Birds: Star Wars (WiiU)] */
/* WIIADPCM - Exient wrapper [Need for Speed: Hot Pursuit (Wii), Angry Birds: Star Wars (Wii/WiiU)] */
VGMSTREAM* init_vgmstream_dsp_wiiadpcm(STREAMFILE* sf) {
dsp_meta dspm = {0};
@ -1456,18 +1456,33 @@ VGMSTREAM* init_vgmstream_dsp_wiiadpcm(STREAMFILE* sf) {
if (!check_extensions(sf, "adpcm"))
return NULL;
dspm.interleave = read_u32be(0x08,sf); /* interleave offset */
/* 0x0c: NFS = 0 when RAM (2 DSP headers), interleave size when stream (2 WIIADPCM headers)
* AB = 0 (2 WIIADPCM headers) */
// no good flag so use v2's loop+type as other values are easy to mistake
int test = read_u32be(0x2c,sf);
if (!(test == 0x00010000 || test == 0x00000000)) {
// V1 (NFSHP)
// 08: ch2 offset
// 0c: real interleave in streams (xN WIIADPCM headers), null in memory audio (xN DSP headers)
dspm.header_offset = 0x10;
dspm.max_channels = 2;
}
else {
// V2 (ABSW)
// 08-18: chN offset
// 1c: real interleave in streams (xN WIIADPCM headers), null in memory audio (xN DSP headers)
// (interleave may be set in mono too)
dspm.header_offset = 0x20;
dspm.max_channels = 6;
}
dspm.channels = (dspm.interleave ? 2 : 1);
dspm.max_channels = 2;
if (read_u32be(0x10,sf) != 0)
dspm.header_offset = 0x10; /* NFSHP */
else
dspm.header_offset = 0x20; /* ABSW */
dspm.channels = 1;
for (int i = 0; i < dspm.max_channels - 1; i++) {
uint32_t offset = read_u32be(0x08 + i * 0x04, sf);
if (!offset)
break;
dspm.channels += 1;
}
dspm.interleave = read_u32be(0x08,sf); // use first channel offset as interleave
if (dspm.interleave)
dspm.interleave -= dspm.header_offset;
dspm.interleave_first_skip = 0x60 + dspm.header_offset;
@ -1476,7 +1491,6 @@ VGMSTREAM* init_vgmstream_dsp_wiiadpcm(STREAMFILE* sf) {
dspm.header_spacing = dspm.interleave;
dspm.start_offset = dspm.header_offset + 0x60;
dspm.meta_type = meta_DSP_WIIADPCM;
return init_vgmstream_dsp_common(sf, &dspm);
}

View File

@ -1,67 +0,0 @@
#include "meta.h"
#include "../util.h"
/* GUN (Gunvari Streams) */
VGMSTREAM * init_vgmstream_ps2_mcg(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset;
int loop_flag = 0;
int channel_count;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("mcg",filename_extension(filename))) goto fail;
/* check header */
if (!((read_32bitBE(0x00,streamFile) == 0x4D434700) &&
(read_32bitBE(0x20,streamFile) == 0x56414770) &&
(read_32bitBE(0x50,streamFile) == 0x56414770)))
goto fail;
loop_flag = (read_32bitLE(0x34,streamFile)!=0);
channel_count = 2;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
start_offset = 0x80;
vgmstream->channels = channel_count;
vgmstream->sample_rate = read_32bitBE(0x30,streamFile);
vgmstream->coding_type = coding_PSX;
vgmstream->num_samples = read_32bitBE(0x2C,streamFile)/16*14*channel_count;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = read_32bitLE(0x14,streamFile);
vgmstream->meta_type = meta_PS2_MCG;
if (vgmstream->loop_flag)
{
vgmstream->loop_start_sample = read_32bitLE(0x34,streamFile);
vgmstream->loop_end_sample = vgmstream->num_samples;
}
/* open the file for reading */
{
int i;
STREAMFILE * file;
file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!file) goto fail;
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = file;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=start_offset+
vgmstream->interleave_block_size*i;
}
}
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}

View File

@ -1,62 +0,0 @@
#include "meta.h"
#include "../coding/coding.h"
/* VA3 - Konami / Sony Atrac3 Container [PS2 DDR Supernova 2 Arcade] */
VGMSTREAM * init_vgmstream_va3(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int loop_flag, channel_count;
uint32_t data_size;
/* check extension, case insensitive */
if (!check_extensions(streamFile, "va3"))
goto fail;
if (read_32bitBE(0x00, streamFile) != 0x21334156) /* "!3AV" */
goto fail;
/* va3 header */
start_offset = 0x800;
data_size = read_32bitLE(0x04, streamFile);// get_streamfile_size(streamFile) - start_offset;
// pretty sure 0x4 LE 32 bit is some sort of filesize...
loop_flag = 0;
//0x18 is 1... what is this?
channel_count = 2;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_VA3;
vgmstream->sample_rate = read_32bitLE(0x14, streamFile);
vgmstream->num_samples = read_32bitLE(0x08, streamFile);
vgmstream->channels = channel_count;
#ifdef VGM_USE_FFMPEG
{
int block_align, encoder_delay;
block_align = 0xC0 * vgmstream->channels;
encoder_delay = 0; //todo
vgmstream->codec_data = init_ffmpeg_atrac3_raw(streamFile, start_offset,data_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
}
#else
goto fail;
#endif
/* open the file for reading */
if (!vgmstream_open_stream(vgmstream, streamFile, start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -1,102 +0,0 @@
#include "meta.h"
#include "../util.h"
//#include <windows.h>
//#include <tchar.h>
/* WMUS - Arbitrary extension chosen for The Warriors (PS2) */
VGMSTREAM * init_vgmstream_ps2_wmus(STREAMFILE *streamFile)
{
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
int loop_flag = 1;
int channel_count;
off_t start_offset;
int i;
int blockCount;
int shortBlockSize;
int lastBlockLocation;
char filenameWHED[PATH_LIMIT];
STREAMFILE * streamFileWHED = NULL;
//_TCHAR szBuffer[100];
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("wmus",filename_extension(filename)))
{
goto fail;
}
/* check for .WHED file */
strcpy(filenameWHED, filename);
strcpy(filenameWHED + strlen(filenameWHED) - 4, "WHED");
streamFileWHED = streamFile->open(streamFile, filenameWHED, STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!streamFileWHED)
{
goto fail;
}
/* check loopand channel */
loop_flag = 1;
channel_count = read_32bitLE(0x14, streamFileWHED);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream)
{
goto fail;
}
/* fill in the vital statistics */
vgmstream->channels = channel_count;
vgmstream->sample_rate = read_32bitLE(0x04, streamFileWHED);
vgmstream->coding_type = coding_PSX;
vgmstream->interleave_block_size = read_32bitLE(0x18, streamFileWHED);
blockCount = read_32bitLE(0x1C, streamFileWHED) * channel_count;
shortBlockSize = read_32bitLE(0x20, streamFileWHED);
vgmstream->num_samples = (vgmstream->interleave_block_size * blockCount) / 16 / channel_count * 28;
vgmstream->loop_start_sample = 0;
lastBlockLocation = (vgmstream->interleave_block_size * blockCount) - (vgmstream->interleave_block_size - shortBlockSize);
vgmstream->loop_end_sample = lastBlockLocation / 16 / channel_count * 28;
//_stprintf(szBuffer, _T("%x"), lastBlockLocation);
//MessageBox(NULL, szBuffer, _T("Foo"), MB_OK);
vgmstream->layout_type = layout_interleave;
vgmstream->meta_type = meta_PS2_WMUS;
start_offset = 0;
/* open the file for reading by each channel */
{
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!vgmstream->ch[i].streamfile) goto fail;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=
(off_t)(start_offset+vgmstream->interleave_block_size*i);
}
}
return vgmstream;
/* clean up anything we may have opened */
fail:
if (streamFileWHED) close_streamfile(streamFileWHED);
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}

59
src/meta/va3.c Normal file
View File

@ -0,0 +1,59 @@
#include "meta.h"
#include "../coding/coding.h"
/* VA3 - from Konami games [Dance Dance Revolution Supernova 2 (Arcade)] */
VGMSTREAM* init_vgmstream_va3(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset;
int loop_flag, channels;
uint32_t data_size;
/* checks */
if (!is_id32be(0x00, sf, "!3AV"))
return NULL;
// .va3: actual extension
if (!check_extensions(sf, "va3"))
return NULL;
start_offset = 0x800;
data_size = read_u32le(0x04, sf);
loop_flag = 0;
channels = 2;
//0c: null (loops?)
//10: null (loops?)
//18: flag? (always 1)
//1c: always 0x0200 (channels?)
//20: always 100 (volume?)
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_VA3;
vgmstream->num_samples = read_s32le(0x08, sf);
vgmstream->sample_rate = read_s32le(0x14, sf);
#ifdef VGM_USE_FFMPEG
{
int block_align = 0xC0 * vgmstream->channels;
int encoder_delay = 0; //TODO
vgmstream->codec_data = init_ffmpeg_atrac3_raw(sf, start_offset,data_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
}
#else
goto fail;
#endif
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -226,8 +226,6 @@ VGMSTREAM* init_vgmstream_vas_kceo_container(STREAMFILE* sf) {
}
}
else if (read_u32le(0x00, sf) == 0x800) { /* Xbox/PC (start?) */
VGM_STEP();
total_subsongs = read_s32le(0x04, sf);
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;

View File

@ -13,7 +13,7 @@ VGMSTREAM* init_vgmstream_vig_kces(STREAMFILE* sf) {
/* checks */
if (read_u32be(0x00,sf) != 0x01006408)
return NULL;
/* .vig: actual extension from DDR exes */
/* .vig: actual extension from AC versions + PS2 exes */
if (!check_extensions(sf, "vig"))
return NULL;

View File

@ -1,87 +1,87 @@
#include "meta.h"
#include "../coding/coding.h"
#include "../layout/layout.h"
#include "xavs_streamfile.h"
/* XAVS - Reflections audio and video+audio container [Stuntman (PS2)] */
VGMSTREAM * init_vgmstream_xavs(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int loop_flag, channel_count;
int total_subsongs, target_subsong = streamFile->stream_index;
STREAMFILE *temp_streamFile = NULL;
/* checks */
if (!check_extensions(streamFile, "xav"))
goto fail;
if (read_32bitBE(0x00, streamFile) != 0x58415653) /* "XAVS" */
goto fail;
loop_flag = 0;
channel_count = 2;
start_offset = 0x00;
/* 0x04: 16b width + height (0 if file has no video) */
/* 0x08: related to video (0 if file has no video) */
total_subsongs = read_16bitLE(0x0c, streamFile);
/* 0x0c: volume? (0x50, 0x4e) */
/* 0x10: biggest video chunk? (0 if file has no video) */
/* 0x14: biggest audio chunk? */
if (target_subsong == 0) target_subsong = 1;
if (target_subsong > total_subsongs || total_subsongs <= 0) goto fail;
/* could use a blocked layout, but this needs interleaved PCM within blocks which can't be done ATM */
temp_streamFile = setup_xavs_streamfile(streamFile, 0x18, target_subsong - 1);
if (!temp_streamFile) goto fail;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_XAVS;
vgmstream->num_streams = total_subsongs;
vgmstream->coding_type = coding_PCM16LE;
vgmstream->layout_type = layout_interleave;
/* no apparent flags, most videos use 0x41 but not all */
{
off_t offset = 0x18;
while (offset < get_streamfile_size(streamFile)) {
uint32_t chunk_id = read_32bitLE(offset+0x00, streamFile) & 0xFF;
uint32_t chunk_size = read_32bitLE(offset+0x00, streamFile) >> 8;
if ((chunk_id & 0xF0) == 0x40) {
vgmstream->sample_rate = 48000;
vgmstream->interleave_block_size = 0x200;
break;
} else if ((chunk_id & 0xF0) == 0x60) {
vgmstream->sample_rate = 24000;
vgmstream->interleave_block_size = 0x100;
break;
} else if (chunk_id == 0x56) {
offset += 0x04 + chunk_size;
} else if (chunk_id == 0x21) {
offset += 0x04;
} else {
goto fail;
}
}
}
vgmstream->num_samples = pcm_bytes_to_samples(get_streamfile_size(temp_streamFile), channel_count, 16);
if (!vgmstream_open_stream(vgmstream,temp_streamFile,start_offset))
goto fail;
close_streamfile(temp_streamFile);
return vgmstream;
fail:
close_streamfile(temp_streamFile);
close_vgmstream(vgmstream);
return NULL;
}
#include "meta.h"
#include "../coding/coding.h"
#include "../layout/layout.h"
#include "xavs_streamfile.h"
/* XAVS - Reflections audio and video+audio container [Stuntman (PS2)] */
VGMSTREAM* init_vgmstream_xavs(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
STREAMFILE* temp_sf = NULL;
off_t start_offset;
int loop_flag, channels;
int total_subsongs, target_subsong = sf->stream_index;
/* checks */
if (!is_id32be(0x00, sf, "XAVS"))
return NULL;
if (!check_extensions(sf, "xav"))
return NULL;
loop_flag = 0;
channels = 2;
start_offset = 0x00;
/* 0x04: 16b width + height (0 if file has no video) */
/* 0x08: related to video (0 if file has no video) */
total_subsongs = read_u16le(0x0c, sf);
/* 0x0c: volume? (0x50, 0x4e) */
/* 0x10: biggest video chunk? (0 if file has no video) */
/* 0x14: biggest audio chunk? */
if (target_subsong == 0) target_subsong = 1;
if (target_subsong > total_subsongs || total_subsongs <= 0) goto fail;
/* could use a blocked layout, but this needs interleaved PCM within blocks which can't be done ATM */
temp_sf = setup_xavs_streamfile(sf, 0x18, target_subsong - 1);
if (!temp_sf) goto fail;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_XAVS;
vgmstream->num_streams = total_subsongs;
vgmstream->coding_type = coding_PCM16LE;
vgmstream->layout_type = layout_interleave;
/* no apparent flags, most videos use 0x41 but not all */
{
off_t offset = 0x18;
while (offset < get_streamfile_size(sf)) {
uint32_t chunk_id = read_u32le(offset+0x00, sf) & 0xFF;
uint32_t chunk_size = read_u32le(offset+0x00, sf) >> 8;
if ((chunk_id & 0xF0) == 0x40) {
vgmstream->sample_rate = 48000;
vgmstream->interleave_block_size = 0x200;
break;
} else if ((chunk_id & 0xF0) == 0x60) {
vgmstream->sample_rate = 24000;
vgmstream->interleave_block_size = 0x100;
break;
} else if (chunk_id == 0x56) {
offset += 0x04 + chunk_size;
} else if (chunk_id == 0x21) {
offset += 0x04;
} else {
goto fail;
}
}
}
vgmstream->num_samples = pcm_bytes_to_samples(get_streamfile_size(temp_sf), channels, 16);
if (!vgmstream_open_stream(vgmstream, temp_sf, start_offset))
goto fail;
close_streamfile(temp_sf);
return vgmstream;
fail:
close_streamfile(temp_sf);
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -15,6 +15,7 @@ logger_t log_impl = {0};
//void* log = &log_impl;
enum {
LOG_LEVEL_NONE = 0,
LOG_LEVEL_INFO = 1,
LOG_LEVEL_DEBUG = 2,
LOG_LEVEL_ALL = 100,
@ -30,6 +31,11 @@ void vgm_log_set_callback(void* ctx_p, int level, int type, void* callback) {
ctx->level = level;
if (level == LOG_LEVEL_NONE) {
ctx->callback = NULL;
return;
}
switch(type) {
case 0:
ctx->callback = callback;

View File

@ -141,7 +141,6 @@ init_vgmstream_t init_vgmstream_functions[] = {
init_vgmstream_nds_rrds,
init_vgmstream_smss,
init_vgmstream_ads_midway,
init_vgmstream_ps2_mcg,
init_vgmstream_zsd,
init_vgmstream_vgs_ps,
init_vgmstream_redspark,
@ -175,7 +174,7 @@ init_vgmstream_t init_vgmstream_functions[] = {
init_vgmstream_ngc_dsp_aaap,
init_vgmstream_wb,
init_vgmstream_bnsf,
init_vgmstream_ps2_gcm,
init_vgmstream_mcg,
init_vgmstream_smpl,
init_vgmstream_msa,
init_vgmstream_voi,
@ -524,7 +523,6 @@ init_vgmstream_t init_vgmstream_functions[] = {
init_vgmstream_scd_pcm,
init_vgmstream_vas_kceo,
init_vgmstream_vas_kceo_container,
init_vgmstream_ps2_wmus,
init_vgmstream_mib_mih,
init_vgmstream_mjb_mjh,
init_vgmstream_mic_koei,

View File

@ -434,7 +434,6 @@ typedef enum {
meta_P2BT_MOVE_VISA,
meta_GBTS,
meta_NGC_DSP_IADP, /* Gamecube Interleave DSP */
meta_PS2_MCG, /* Gunvari MCG Files (was name .GCM on disk) */
meta_ZSD, /* Dragon Booster ZSD */
meta_REDSPARK, /* "RedSpark" RSD (MadWorld) */
meta_RAGE_AUD, /* Rockstar AUD - MC:LA, GTA IV */
@ -460,7 +459,7 @@ typedef enum {
meta_WB,
meta_S14, /* raw Siren 14, 24kbit mono */
meta_SSS, /* raw Siren 14, 48kbit stereo */
meta_PS2_GCM, /* NamCollection */
meta_MCG,
meta_SMPL,
meta_MSA,
meta_VOI,
@ -495,7 +494,6 @@ typedef enum {
meta_LSF_N1NJ4N, /* .lsf n1nj4n Fastlane Street Racing (iPhone) */
meta_XWAV,
meta_RAW_SNDS,
meta_PS2_WMUS, /* The Warriors (PS2) */
meta_HYPERSCAN_KVAG, /* Hyperscan KVAG/BVG */
meta_PSND,
meta_ADP_WILDFIRE,