mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-02-20 12:31:00 +01:00
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:
commit
a8513ca672
@ -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();
|
||||
|
@ -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):
|
||||
|
@ -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(¤t_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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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"},
|
||||
|
@ -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);
|
||||
|
||||
|
||||
|
@ -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" />
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)] */
|
||||
|
@ -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;
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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
59
src/meta/va3.c
Normal 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;
|
||||
}
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
174
src/meta/xavs.c
174
src/meta/xavs.c
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user