Merge pull request #480 from bnnm/tests

tests
This commit is contained in:
bnnm 2019-09-30 01:04:01 +02:00 committed by GitHub
commit 60d9895f00
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 397 additions and 218 deletions

View File

@ -380,6 +380,21 @@ int main(int argc, char ** argv) {
res = validate_config(&cfg);
if (!res) goto fail;
#if 0
/* CLI has no need to check */
{
int valid;
vgmstream_ctx_valid_cfg cfg2 = {0};
cfg2.skip_standard = 1;
cfg2.reject_extensionless = 0;
cfg2.accept_unknown = 0;
cfg2.accept_common = 1;
valid = vgmstream_ctx_is_valid(cfg.infilename, &cfg2);
if (!valid) goto fail;
}
#endif
/* open streamfile and pass subsong */
{
@ -435,6 +450,10 @@ int main(int argc, char ** argv) {
fprintf(stderr,"failed to open %s for output\n",cfg.outfilename);
goto fail;
}
/* no improvement */
//setvbuf(outfile, NULL, _IOFBF, SAMPLE_BUFFER_SIZE * sizeof(sample_t) * input_channels);
//setvbuf(outfile, NULL, _IONBF, 0);
}
@ -550,7 +569,7 @@ int main(int argc, char ** argv) {
fwrite(buf + j*channels + (cfg.only_stereo*2), sizeof(sample_t), 2, outfile);
}
} else {
fwrite(buf, sizeof(sample_t) * channels, to_get, outfile);
fwrite(buf, sizeof(sample_t), to_get * channels, outfile);
}
}

View File

@ -22,10 +22,24 @@ There are no hard coding rules but for consistency one could follow the style us
But other styles may be found, this isn't very important as most files are isolated.
Some of the code may be a bit inefficient or duplicated at places, but it isn't much of a problem if gives clarity. vgmstream's performance is fast enough (as it mainly deals with playing songs in real time) so that favors clarity over optimization. Similarly, some code may segfault or even cause infinite loops on bad data, but it's fixed as encountered rather than worrying too much about improbable cases.
### Code quality
There is quite a bit of code that could be improved overall, but given how niche the project is priority is given to adding and improving formats. Some of the code can be inefficient or duplicated at places, but it isn't that much of a problem if gives clarity. vgmstream's performance is fast enough (as it mainly deals with playing songs in real time) so that favors clarity over optimization.
Similarly, parts may segfault or even cause infinite loops on bad data, but it's fixed as encountered rather than worrying too much about improbable cases. There isn't an automated test suite at the moment, so tests are manually done as needed.
For regression testing there is a simple script that compares output of a previous version of vgmstream_cli with current. Some bugs may drastically change output when fixed (for example adjusting loops or decoding) so it could be hard to automate and maintain.
Code is checked for leaks at times using detection tools, but most of vgmstream formats are quite simple and don't need to manage memory. It's mainly useful for files using external decoders or complex segmented/layered layout combos.
```
# recommended to compile with debug info, for example:
make vgmstream_cli EXTRA_CFLAGS="-g" STRIP=echo
# find leaks
drmemory -- vgmstream_cli -o file.ext
```
## Structure
## Source structure
```
./ scripts

View File

@ -52,6 +52,11 @@ BEGIN
CONTROL "Disable tagfile",IDC_TAGFILE_DISABLE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,131,87,10
CONTROL "Override title",IDC_OVERRIDE_TITLE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,147,97,10
CONTROL "Enable unknown exts",IDC_EXTS_UNKNOWN_ON,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,163,87,10
CONTROL "Enable common exts",IDC_EXTS_COMMON_ON,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,179,87,10
END

View File

@ -14,29 +14,20 @@ extern "C" {
#include "foo_vgmstream.h"
static const GUID guid_cfg_LoopForever = { 0xa19e36eb, 0x72a0, 0x4077, { 0x91, 0x43, 0x38, 0xb4, 0x5, 0xfc, 0x91, 0xc5 } };
static const GUID guid_cfg_IgnoreLoop = { 0xddda7ab6, 0x7bb6, 0x4abe, { 0xb9, 0x66, 0x2d, 0xb7, 0x8f, 0xe4, 0xcc, 0xab } };
static const GUID guid_cfg_LoopCount = { 0xfc8dfd72, 0xfae8, 0x44cc, { 0xbe, 0x99, 0x1c, 0x7b, 0x27, 0x7a, 0xb6, 0xb9 } };
static const GUID guid_cfg_FadeLength = { 0x61da7ef1, 0x56a5, 0x4368, { 0xae, 0x6, 0xec, 0x6f, 0xd7, 0xe6, 0x15, 0x5d } };
static const GUID guid_cfg_FadeDelay = { 0x73907787, 0xaf49, 0x4659, { 0x96, 0x8e, 0x9f, 0x70, 0xa1, 0x62, 0x49, 0xc4 } };
static const GUID guid_cfg_DisableSubsongs = { 0xa8cdd664, 0xb32b, 0x4a36, { 0x83, 0x07, 0xa0, 0x4c, 0xcd, 0x52, 0xa3, 0x7c } };
static const GUID guid_cfg_DownmixChannels = { 0x5a0e65dd, 0xeb37, 0x4c67, { 0x9a, 0xb1, 0x3f, 0xb0, 0xc9, 0x7e, 0xb0, 0xe0 } };
static const GUID guid_cfg_TagfileDisable = { 0xc1971eb7, 0xa930, 0x4bae, { 0x9e, 0x7f, 0xa9, 0x50, 0x36, 0x32, 0x41, 0xb3 } };
static const GUID guid_cfg_OverrideTitle = { 0xe794831f, 0xd067, 0x4337, { 0x97, 0x85, 0x10, 0x57, 0x39, 0x4b, 0x1b, 0x97 } };
static cfg_bool cfg_LoopForever(guid_cfg_LoopForever, DEFAULT_LOOP_FOREVER);
static cfg_bool cfg_IgnoreLoop(guid_cfg_IgnoreLoop, DEFAULT_IGNORE_LOOP);
static cfg_string cfg_LoopCount(guid_cfg_LoopCount, DEFAULT_LOOP_COUNT);
static cfg_string cfg_FadeLength(guid_cfg_FadeLength, DEFAULT_FADE_SECONDS);
static cfg_string cfg_FadeDelay(guid_cfg_FadeDelay, DEFAULT_FADE_DELAY_SECONDS);
static cfg_bool cfg_DisableSubsongs(guid_cfg_DisableSubsongs, DEFAULT_DISABLE_SUBSONGS);
static cfg_string cfg_DownmixChannels(guid_cfg_DownmixChannels, DEFAULT_DOWNMIX_CHANNELS);
static cfg_bool cfg_TagfileDisable(guid_cfg_TagfileDisable, DEFAULT_TAGFILE_DISABLE);
static cfg_bool cfg_OverrideTitle(guid_cfg_OverrideTitle, DEFAULT_OVERRIDE_TITLE);
static cfg_bool cfg_LoopForever ({0xa19e36eb,0x72a0,0x4077,{0x91,0x43,0x38,0xb4,0x05,0xfc,0x91,0xc5}}, DEFAULT_LOOP_FOREVER);
static cfg_bool cfg_IgnoreLoop ({0xddda7ab6,0x7bb6,0x4abe,{0xb9,0x66,0x2d,0xb7,0x8f,0xe4,0xcc,0xab}}, DEFAULT_IGNORE_LOOP);
static cfg_string cfg_LoopCount ({0xfc8dfd72,0xfae8,0x44cc,{0xbe,0x99,0x1c,0x7b,0x27,0x7a,0xb6,0xb9}}, DEFAULT_LOOP_COUNT);
static cfg_string cfg_FadeLength ({0x61da7ef1,0x56a5,0x4368,{0xae,0x06,0xec,0x6f,0xd7,0xe6,0x15,0x5d}}, DEFAULT_FADE_SECONDS);
static cfg_string cfg_FadeDelay ({0x73907787,0xaf49,0x4659,{0x96,0x8e,0x9f,0x70,0xa1,0x62,0x49,0xc4}}, DEFAULT_FADE_DELAY_SECONDS);
static cfg_bool cfg_DisableSubsongs ({0xa8cdd664,0xb32b,0x4a36,{0x83,0x07,0xa0,0x4c,0xcd,0x52,0xa3,0x7c}}, DEFAULT_DISABLE_SUBSONGS);
static cfg_string cfg_DownmixChannels ({0x5a0e65dd,0xeb37,0x4c67,{0x9a,0xb1,0x3f,0xb0,0xc9,0x7e,0xb0,0xe0}}, DEFAULT_DOWNMIX_CHANNELS);
static cfg_bool cfg_TagfileDisable ({0xc1971eb7,0xa930,0x4bae,{0x9e,0x7f,0xa9,0x50,0x36,0x32,0x41,0xb3}}, DEFAULT_TAGFILE_DISABLE);
static cfg_bool cfg_OverrideTitle ({0xe794831f,0xd067,0x4337,{0x97,0x85,0x10,0x57,0x39,0x4b,0x1b,0x97}}, DEFAULT_OVERRIDE_TITLE);
static cfg_bool cfg_ExtsUnknownOn ({0xd92dc6a2,0x9683,0x422d,{0x8e,0xd1,0x59,0x46,0xd5,0xbf,0x01,0x6f}}, DEFAULT_EXTS_UNKNOWN_ON);
static cfg_bool cfg_ExtsCommonOn ({0x405af423,0x5037,0x4eae,{0xa6,0xe3,0x72,0xd0,0x12,0x7d,0x84,0x6c}}, DEFAULT_EXTS_COMMON_ON);
// Needs to be here in rder to access the static config
void input_vgmstream::load_settings()
{
void input_vgmstream::load_settings() {
// no verification needed here, as it is done below
sscanf(cfg_FadeLength.get_ptr(),"%lf",&fade_seconds);
sscanf(cfg_LoopCount.get_ptr(),"%lf",&loop_count);
@ -47,28 +38,28 @@ void input_vgmstream::load_settings()
sscanf(cfg_DownmixChannels.get_ptr(),"%d",&downmix_channels);
tagfile_disable = cfg_TagfileDisable;
override_title = cfg_OverrideTitle;
//exts_unknown_on = cfg_ExtsUnknownOn;
//exts_common_on = cfg_ExtsCommonOn;
}
void input_vgmstream::g_load_cfg(int *accept_unknown, int *accept_common) {
//todo improve
*accept_unknown = cfg_ExtsUnknownOn ? 1 : 0;
*accept_common = cfg_ExtsCommonOn ? 1 : 0;
}
const char * vgmstream_prefs::get_name()
{
const char * vgmstream_prefs::get_name() {
return input_vgmstream::g_get_name();
}
GUID vgmstream_prefs::get_guid()
{
GUID vgmstream_prefs::get_guid() {
return input_vgmstream::g_get_preferences_guid();
}
GUID vgmstream_prefs::get_parent_guid()
{
GUID vgmstream_prefs::get_parent_guid() {
return guid_input;
}
BOOL vgmstreamPreferences::OnInitDialog(CWindow, LPARAM)
{
BOOL vgmstreamPreferences::OnInitDialog(CWindow, LPARAM) {
CheckDlgButton(IDC_LOOP_FOREVER, cfg_LoopForever?BST_CHECKED:BST_UNCHECKED);
CheckDlgButton(IDC_IGNORE_LOOP, cfg_IgnoreLoop?BST_CHECKED:BST_UNCHECKED);
CheckDlgButton(IDC_LOOP_NORMALLY, (!cfg_IgnoreLoop && !cfg_LoopForever)?BST_CHECKED:BST_UNCHECKED);
@ -83,21 +74,19 @@ BOOL vgmstreamPreferences::OnInitDialog(CWindow, LPARAM)
CheckDlgButton(IDC_TAGFILE_DISABLE, cfg_TagfileDisable?BST_CHECKED:BST_UNCHECKED);
CheckDlgButton(IDC_OVERRIDE_TITLE, cfg_OverrideTitle?BST_CHECKED:BST_UNCHECKED);
CheckDlgButton(IDC_EXTS_UNKNOWN_ON, cfg_ExtsUnknownOn?BST_CHECKED:BST_UNCHECKED);
CheckDlgButton(IDC_EXTS_COMMON_ON, cfg_ExtsCommonOn?BST_CHECKED:BST_UNCHECKED);
return TRUE;
}
t_uint32 vgmstreamPreferences::get_state()
{
t_uint32 vgmstreamPreferences::get_state() {
t_uint32 state = preferences_state::resettable;
if (HasChanged()) state |= preferences_state::changed;
return state;
}
void vgmstreamPreferences::reset()
{
void vgmstreamPreferences::reset() {
CheckDlgButton(IDC_LOOP_FOREVER, DEFAULT_LOOP_FOREVER?BST_CHECKED:BST_UNCHECKED);
CheckDlgButton(IDC_IGNORE_LOOP, DEFAULT_IGNORE_LOOP?BST_CHECKED:BST_UNCHECKED);
CheckDlgButton(IDC_LOOP_NORMALLY, (!DEFAULT_IGNORE_LOOP && !DEFAULT_LOOP_FOREVER)?BST_CHECKED:BST_UNCHECKED);
@ -112,17 +101,19 @@ void vgmstreamPreferences::reset()
CheckDlgButton(IDC_TAGFILE_DISABLE, DEFAULT_TAGFILE_DISABLE?BST_CHECKED:BST_UNCHECKED);
CheckDlgButton(IDC_OVERRIDE_TITLE, DEFAULT_OVERRIDE_TITLE?BST_CHECKED:BST_UNCHECKED);
CheckDlgButton(IDC_EXTS_UNKNOWN_ON, DEFAULT_EXTS_UNKNOWN_ON?BST_CHECKED:BST_UNCHECKED);
CheckDlgButton(IDC_EXTS_COMMON_ON, DEFAULT_EXTS_COMMON_ON?BST_CHECKED:BST_UNCHECKED);
}
void vgmstreamPreferences::apply()
{
void vgmstreamPreferences::apply() {
cfg_LoopForever = IsDlgButtonChecked(IDC_LOOP_FOREVER)?true:false;
cfg_IgnoreLoop = IsDlgButtonChecked(IDC_IGNORE_LOOP)?true:false;
cfg_DisableSubsongs = IsDlgButtonChecked(IDC_DISABLE_SUBSONGS)?true:false;
cfg_TagfileDisable = IsDlgButtonChecked(IDC_TAGFILE_DISABLE)?true:false;
cfg_OverrideTitle = IsDlgButtonChecked(IDC_OVERRIDE_TITLE)?true:false;
cfg_ExtsUnknownOn = IsDlgButtonChecked(IDC_EXTS_UNKNOWN_ON)?true:false;
cfg_ExtsCommonOn = IsDlgButtonChecked(IDC_EXTS_COMMON_ON)?true:false;
double temp_fade_seconds;
double temp_fade_delay_seconds;
@ -178,8 +169,8 @@ void vgmstreamPreferences::apply()
}
bool vgmstreamPreferences::HasChanged()
{
bool vgmstreamPreferences::HasChanged() {
if(IsDlgButtonChecked(IDC_LOOP_FOREVER))
if(cfg_LoopForever != true) return true;
@ -189,14 +180,22 @@ bool vgmstreamPreferences::HasChanged()
if(IsDlgButtonChecked(IDC_LOOP_NORMALLY))
if(cfg_IgnoreLoop != false || cfg_LoopForever != false) return true;
bool current_cfg_DisableSubsongs = IsDlgButtonChecked(IDC_DISABLE_SUBSONGS)?true:false;
if(cfg_DisableSubsongs != current_cfg_DisableSubsongs) return true;
bool current;
bool current_cfg_TagfileDisable = IsDlgButtonChecked(IDC_TAGFILE_DISABLE)?true:false;
if(cfg_TagfileDisable != current_cfg_TagfileDisable) return true;
current = IsDlgButtonChecked(IDC_DISABLE_SUBSONGS)?true:false;
if(cfg_DisableSubsongs != current) return true;
bool current_cfg_OverrideTitle = IsDlgButtonChecked(IDC_OVERRIDE_TITLE)?true:false;
if(cfg_OverrideTitle != current_cfg_OverrideTitle) return true;
current = IsDlgButtonChecked(IDC_TAGFILE_DISABLE)?true:false;
if(cfg_TagfileDisable != current) return true;
current = IsDlgButtonChecked(IDC_OVERRIDE_TITLE)?true:false;
if(cfg_OverrideTitle != current) return true;
current = IsDlgButtonChecked(IDC_EXTS_UNKNOWN_ON)?true:false;
if(cfg_ExtsUnknownOn != current) return true;
current = IsDlgButtonChecked(IDC_EXTS_COMMON_ON)?true:false;
if(cfg_ExtsCommonOn != current) return true;
pfc::string FadeLength(cfg_FadeLength);
pfc::string FadeDelay(cfg_FadeDelay);
@ -213,8 +212,7 @@ bool vgmstreamPreferences::HasChanged()
}
void vgmstreamPreferences::OnEditChange(UINT, int, CWindow)
{
void vgmstreamPreferences::OnEditChange(UINT, int, CWindow) {
m_callback->on_state_changed();
}

View File

@ -18,14 +18,18 @@
#define DEFAULT_DOWNMIX_CHANNELS "8"
#define DEFAULT_TAGFILE_DISABLE false
#define DEFAULT_OVERRIDE_TITLE false
#define DEFAULT_EXTS_UNKNOWN_ON false
#define DEFAULT_EXTS_COMMON_ON false
class vgmstreamPreferences : public CDialogImpl<vgmstreamPreferences>, public preferences_page_instance {
public:
//Constructor - invoked by preferences_page_impl helpers - don't do Create() in here, preferences_page_impl does this for us
//Constructor - invoked by preferences_page_impl helpers -
//don't do Create() in here, preferences_page_impl does this for us
vgmstreamPreferences(preferences_page_callback::ptr callback) : m_callback(callback) {}
//Note that we don't bother doing anything regarding destruction of our class.
//The host ensures that our dialog is destroyed first, then the last reference to our preferences_page_instance object is released, causing our object to be deleted.
//The host ensures that our dialog is destroyed first, then the last reference to our
//preferences_page_instance object is released, causing our object to be deleted.
//dialog resource ID
@ -48,6 +52,8 @@ public:
COMMAND_HANDLER_EX(IDC_DOWNMIX_CHANNELS, EN_CHANGE, OnEditChange)
COMMAND_HANDLER_EX(IDC_TAGFILE_DISABLE, BN_CLICKED, OnEditChange)
COMMAND_HANDLER_EX(IDC_OVERRIDE_TITLE, BN_CLICKED, OnEditChange)
COMMAND_HANDLER_EX(IDC_EXTS_UNKNOWN_ON, BN_CLICKED, OnEditChange)
COMMAND_HANDLER_EX(IDC_EXTS_COMMON_ON, BN_CLICKED, OnEditChange)
END_MSG_MAP()
private:
BOOL OnInitDialog(CWindow, LPARAM);
@ -60,8 +66,7 @@ private:
class vgmstream_prefs : public preferences_page_impl<vgmstreamPreferences>
{
class vgmstream_prefs : public preferences_page_impl<vgmstreamPreferences> {
public:
const char * get_name();

View File

@ -384,24 +384,12 @@ void input_vgmstream::retag_commit(abort_callback & p_abort) { /*throw exception
bool input_vgmstream::g_is_our_content_type(const char * p_content_type) {return false;}
// called to check if file can be processed by the plugin
bool input_vgmstream::g_is_our_path(const char * p_path,const char * p_extension) {
const char ** ext_list;
size_t ext_list_len;
int i;
bool input_vgmstream::g_is_our_path(const char * p_path, const char * p_extension) {
vgmstream_ctx_valid_cfg cfg = {0};
ext_list = vgmstream_get_formats(&ext_list_len);
for (i=0; i < ext_list_len; i++) {
if (!stricmp_utf8(p_extension, ext_list[i]))
return 1;
}
/* some extensionless files can be handled by vgmstream, try to play */
if (strlen(p_extension) <= 0) {
return 1;
}
return 0;
cfg.is_extension = 1;
input_vgmstream::g_load_cfg(&cfg.accept_unknown, &cfg.accept_common);
return vgmstream_ctx_is_valid(p_extension, &cfg) > 0 ? true : false;
}
// internal util to create a VGMSTREAM

View File

@ -78,6 +78,8 @@ class input_vgmstream : public input_stubs {
bool tagfile_disable;
pfc::string8 tagfile_name;
bool override_title;
//bool exts_common_on;
//bool exts_unknown_on;
/* song config */
foobar_song_config config;
@ -90,6 +92,8 @@ class input_vgmstream : public input_stubs {
bool get_description_tag(pfc::string_base & temp, pfc::string_base const& description, const char *tag, char delimiter = '\n');
void set_config_defaults(foobar_song_config *current);
void apply_config(VGMSTREAM * vgmstream, foobar_song_config *current);
static void g_load_cfg(int *accept_unknown, int *accept_common);
};
/* foo_streamfile.cpp */

View File

@ -17,6 +17,8 @@
#define IDC_DOWNMIX_CHANNELS 1010
#define IDC_TAGFILE_DISABLE 1011
#define IDC_OVERRIDE_TITLE 1012
#define IDC_EXTS_UNKNOWN_ON 1015
#define IDC_EXTS_COMMON_ON 1016
// Next default values for new objects
//

View File

@ -49,7 +49,8 @@ atrac9_codec_data *init_atrac9(atrac9_config *cfg) {
/* must hold at least one superframe and its samples */
data->data_buffer_size = data->info.superframeSize;
data->data_buffer = calloc(sizeof(uint8_t), data->data_buffer_size);
/* extra leeway as Atrac9Decode seems to overread ~2 bytes (doesn't affect decoding though) */
data->data_buffer = calloc(sizeof(uint8_t), data->data_buffer_size + 0x10);
data->sample_buffer = calloc(sizeof(sample_t), data->info.channels * data->info.frameSamples * data->info.framesInSuperframe);
data->samples_to_discard = cfg->encoder_delay;

View File

@ -256,11 +256,6 @@ static int ffmpeg_read(void *opaque, uint8_t *buf, int read_size) {
return bytes + max_to_copy;
}
/* AVIO callback: write stream not needed */
static int ffmpeg_write(void *opaque, uint8_t *buf, int buf_size) {
return -1;
}
/* AVIO callback: seek stream, handling custom data */
static int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) {
ffmpeg_codec_data *data = (ffmpeg_codec_data *) opaque;
@ -488,7 +483,7 @@ static int init_ffmpeg_config(ffmpeg_codec_data * data, int target_subsong, int
data->buffer = av_malloc(FFMPEG_DEFAULT_IO_BUFFER_SIZE);
if (!data->buffer) goto fail;
data->ioCtx = avio_alloc_context(data->buffer, FFMPEG_DEFAULT_IO_BUFFER_SIZE, 0, data, ffmpeg_read, ffmpeg_write, ffmpeg_seek);
data->ioCtx = avio_alloc_context(data->buffer, FFMPEG_DEFAULT_IO_BUFFER_SIZE, 0, data, ffmpeg_read, 0, ffmpeg_seek);
if (!data->ioCtx) goto fail;
data->formatCtx = avformat_alloc_context();
@ -496,8 +491,10 @@ static int init_ffmpeg_config(ffmpeg_codec_data * data, int target_subsong, int
data->formatCtx->pb = data->ioCtx;
//on reset could use AVFormatContext.iformat to reload old format
errcode = avformat_open_input(&data->formatCtx, "", NULL, NULL);
//data->inputFormatCtx = av_find_input_format("h264"); /* set directly? */
/* on reset could use AVFormatContext.iformat to reload old format too */
errcode = avformat_open_input(&data->formatCtx, NULL /*""*/, NULL, NULL);
if (errcode < 0) goto fail;
errcode = avformat_find_stream_info(data->formatCtx, NULL);
@ -554,9 +551,10 @@ static int init_ffmpeg_config(ffmpeg_codec_data * data, int target_subsong, int
if (!data->lastDecodedFrame) goto fail;
av_frame_unref(data->lastDecodedFrame);
data->lastReadPacket = malloc(sizeof(AVPacket));
data->lastReadPacket = av_malloc(sizeof(AVPacket)); /* av_packet_alloc? */
if (!data->lastReadPacket) goto fail;
av_new_packet(data->lastReadPacket, 0);
//av_packet_unref?
return 0;
fail:
@ -823,33 +821,38 @@ static void free_ffmpeg_config(ffmpeg_codec_data *data) {
if (data->lastReadPacket) {
av_packet_unref(data->lastReadPacket);
free(data->lastReadPacket);
av_free(data->lastReadPacket);
data->lastReadPacket = NULL;
}
if (data->lastDecodedFrame) {
av_frame_unref(data->lastDecodedFrame);
av_free(data->lastDecodedFrame);
data->lastDecodedFrame = NULL;
}
if (data->codecCtx) {
avcodec_close(data->codecCtx);
avcodec_free_context(&(data->codecCtx));
avcodec_free_context(&data->codecCtx);
data->codecCtx = NULL;
}
if (data->formatCtx) {
avformat_close_input(&(data->formatCtx));
avformat_close_input(&data->formatCtx);
//avformat_free_context(data->formatCtx); /* done in close_input */
data->formatCtx = NULL;
}
if (data->ioCtx) {
// buffer passed in is occasionally freed and replaced.
// the replacement must be freed as well.
/* buffer passed in is occasionally freed and replaced.
// the replacement must be free'd as well (below) */
data->buffer = data->ioCtx->buffer;
av_free(data->ioCtx);
avio_context_free(&data->ioCtx);
//av_free(data->ioCtx); /* done in context_free (same thing) */
data->ioCtx = NULL;
}
if (data->buffer) {
av_free(data->buffer);
data->buffer = NULL;
}
//todo avformat_find_stream_info may cause some Win Handle leaks? related to certain option (not happening in gcc builds)
}
void free_ffmpeg(ffmpeg_codec_data *data) {

View File

@ -71,6 +71,7 @@ ffmpeg_codec_data * init_ffmpeg_atrac3_raw(STREAMFILE *sf, off_t offset, size_t
return ffmpeg_data;
fail:
free_ffmpeg(ffmpeg_data);
return NULL;
}
@ -181,9 +182,8 @@ ffmpeg_codec_data * init_ffmpeg_atrac3_riff(STREAMFILE *sf, off_t offset, int* o
return ffmpeg_data;
fail:
free_ffmpeg(ffmpeg_data);
return NULL;
}
#endif

View File

@ -234,18 +234,19 @@ void decode_mpeg(VGMSTREAM * vgmstream, sample_t * outbuf, int32_t samples_to_do
*/
static void decode_mpeg_standard(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, sample_t * outbuf, int32_t samples_to_do, int channels) {
int samples_done = 0;
mpg123_handle *m = data->m;
unsigned char *outbytes = (unsigned char *)outbuf;
while (samples_done < samples_to_do) {
size_t bytes_done;
int rc;
int rc, bytes_to_do;
/* read more raw data */
if (!data->buffer_full) {
data->bytes_in_buffer = read_streamfile(data->buffer,stream->offset,data->buffer_size,stream->streamfile);
/* end of stream, fill rest with 0s */
if (!data->bytes_in_buffer) {
if (data->bytes_in_buffer <= 0) {
VGM_ASSERT(samples_to_do < samples_done, "MPEG: end of stream, filling %i\n", (samples_to_do - samples_done));
memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * channels * sizeof(sample));
break;
}
@ -256,30 +257,26 @@ static void decode_mpeg_standard(VGMSTREAMCHANNEL *stream, mpeg_codec_data * dat
stream->offset += data->bytes_in_buffer;
}
bytes_to_do = (samples_to_do-samples_done)*sizeof(sample)*channels;
/* feed new raw data to the decoder if needed, copy decoded results to output */
if (!data->buffer_used) {
rc = mpg123_decode(m,
data->buffer,data->bytes_in_buffer,
(unsigned char *)(outbuf+samples_done*channels),
(samples_to_do-samples_done)*sizeof(sample)*channels,
&bytes_done);
rc = mpg123_decode(data->m, data->buffer,data->bytes_in_buffer, outbytes, bytes_to_do, &bytes_done);
data->buffer_used = 1;
}
else {
rc = mpg123_decode(m,
NULL,0,
(unsigned char *)(outbuf+samples_done*channels),
(samples_to_do-samples_done)*sizeof(sample)*channels,
&bytes_done);
rc = mpg123_decode(data->m, NULL,0, outbytes, bytes_to_do, &bytes_done);
}
/* not enough raw data, request more */
if (rc == MPG123_NEED_MORE) {
data->buffer_full = 0;
}
VGM_ASSERT(rc != MPG123_NEED_MORE && rc != MPG123_OK, "MPEG: error %i\n", rc);
/* update copied samples */
samples_done += bytes_done/sizeof(sample)/channels;
outbytes += bytes_done;
}
}

View File

@ -195,10 +195,11 @@ void free_vorbis_custom(vorbis_custom_codec_data * data) {
if (!data)
return;
/* internal decoder cleanp */
vorbis_info_clear(&data->vi);
vorbis_comment_clear(&data->vc);
/* internal decoder cleanup */
vorbis_block_clear(&data->vb);
vorbis_dsp_clear(&data->vd);
vorbis_comment_clear(&data->vc);
vorbis_info_clear(&data->vi);
free(data->buffer);
free(data);

View File

@ -94,7 +94,7 @@ static const char* extension_list[] = {
"bik",
"bika",
"bik2",
//"bin", //common
//"bin", //common
"bk2",
"bmdx",
"bms",
@ -552,7 +552,7 @@ static const char* extension_list[] = {
"xss",
"xvag",
"xvas",
"xwav",//fake extension for .wav (renamed, to be removed)
"xwav", //fake extension for .wav (renamed, to be removed)
"xwb",
"xmd",
"xopus",
@ -576,12 +576,36 @@ static const char* extension_list[] = {
//, NULL //end mark
};
static const char* common_extension_list[] = {
"aac", //common
"ac3", //common, FFmpeg/not parsed (AC3)
"aif", //common
"aiff", //common
"bin", //common
"flac", //common
"gsf", //conflicts with GBA gsf plugins?
"mp2", //common
"mp3", //common
"mp4", //common
"mpc", //common
"ogg", //common
"opus", //common
"stm", //common
"wav", //common
};
/* List supported formats and return elements in the list, for plugins that need to know. */
const char ** vgmstream_get_formats(size_t * size) {
*size = sizeof(extension_list) / sizeof(char*);
return extension_list;
}
const char ** vgmstream_get_common_formats(size_t * size) {
*size = sizeof(common_extension_list) / sizeof(char*);
return common_extension_list;
}
/* internal description info */

View File

@ -152,6 +152,7 @@ void free_layout_layered(layered_layout_data *data) {
}
free(data->layers);
}
free(data->buffer);
free(data);
}

View File

@ -224,6 +224,7 @@ void free_layout_segmented(segmented_layout_data *data) {
}
free(data->segments);
}
free(data->buffer);
free(data);
}

View File

@ -552,7 +552,33 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
vgmstream->sample_rate = fmt.sample_rate;
vgmstream->channel_layout = fmt.channel_layout;
/* init, samples */
/* coding, layout, interleave */
vgmstream->coding_type = fmt.coding_type;
switch (fmt.coding_type) {
case coding_MSADPCM:
case coding_MS_IMA:
case coding_AICA:
case coding_XBOX_IMA:
case coding_IMA:
#ifdef VGM_USE_FFMPEG
case coding_FFmpeg:
#endif
#ifdef VGM_USE_MAIATRAC3PLUS
case coding_AT3plus:
#endif
#ifdef VGM_USE_ATRAC9
case coding_ATRAC9:
#endif
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = fmt.block_size;
break;
default:
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = fmt.interleave;
break;
}
/* samples, codec init (after setting coding to ensure proper close on failure) */
switch (fmt.coding_type) {
case coding_PCM16LE:
vgmstream->num_samples = pcm_bytes_to_samples(data_size, fmt.channel_count, 16);
@ -672,32 +698,6 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
goto fail;
}
/* coding, layout, interleave */
vgmstream->coding_type = fmt.coding_type;
switch (fmt.coding_type) {
case coding_MSADPCM:
case coding_MS_IMA:
case coding_AICA:
case coding_XBOX_IMA:
case coding_IMA:
#ifdef VGM_USE_FFMPEG
case coding_FFmpeg:
#endif
#ifdef VGM_USE_MAIATRAC3PLUS
case coding_AT3plus:
#endif
#ifdef VGM_USE_ATRAC9
case coding_ATRAC9:
#endif
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = fmt.block_size;
break;
default:
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = fmt.interleave;
break;
}
/* Dynasty Warriors 5 (Xbox) 6ch interleaves stereo frames, probably not official */
if (vgmstream->coding_type == coding_XBOX_IMA && vgmstream->channels > 2) {
vgmstream->layout_type = layout_interleave;

View File

@ -756,14 +756,14 @@ static int get_fade(const char * config, txtp_mix_data *mix, int *out_n) {
char type, separator;
m = sscanf(config, " %d %c%n", &mix->ch_dst, &type, &n);
if (n == 0 || m != 2) goto fail;
if (m != 2 || n == 0) goto fail;
config += n;
tn += n;
if (type == '^') {
/* full definition */
m = sscanf(config, " %lf ~ %lf = %c @%n", &mix->vol_start, &mix->vol_end, &mix->shape, &n);
if (n == 0 || m != 3) goto fail;
if (m != 3 || n == 0) goto fail;
config += n;
tn += n;
@ -773,7 +773,7 @@ static int get_fade(const char * config, txtp_mix_data *mix, int *out_n) {
tn += n;
m = sscanf(config, " %c%n", &separator, &n);
if (n == 0 || m != 1 || separator != '~') goto fail;
if ( m != 1 || n == 0 || separator != '~') goto fail;
config += n;
tn += n;
@ -783,7 +783,7 @@ static int get_fade(const char * config, txtp_mix_data *mix, int *out_n) {
tn += n;
m = sscanf(config, " %c%n", &separator, &n);
if (n == 0 || m != 1 || separator != '+') goto fail;
if (m != 1 || n == 0 || separator != '+') goto fail;
config += n;
tn += n;
@ -793,7 +793,7 @@ static int get_fade(const char * config, txtp_mix_data *mix, int *out_n) {
tn += n;
m = sscanf(config, " %c%n", &separator, &n);
if (n == 0 || m != 1 || separator != '~') goto fail;
if (m != 1 || n == 0 || separator != '~') goto fail;
config += n;
tn += n;
@ -832,7 +832,7 @@ static int get_fade(const char * config, txtp_mix_data *mix, int *out_n) {
tn += n;
m = sscanf(config, " %c%n", &separator, &n);
if (n == 0 || m != 1 || separator != '+') goto fail;
if (m != 1 || n == 0 || separator != '+') goto fail;
config += n;
tn += n;

View File

@ -7,7 +7,7 @@ VGMSTREAM* init_vgmstream_xmv_valve(STREAMFILE* streamFile) {
VGMSTREAM* vgmstream = NULL;
int32_t loop_start;
uint32_t start_offset, data_size, sample_rate, num_samples;
uint16_t loop_block, loop_start_skip, loop_end_skip;
uint16_t /*loop_block, loop_start_skip,*/ loop_end_skip;
uint8_t format, freq_mode, channels;
int loop_flag;
@ -29,8 +29,8 @@ VGMSTREAM* init_vgmstream_xmv_valve(STREAMFILE* streamFile) {
loop_start = read_32bitBE(0x1c, streamFile);
/* XMA only */
loop_block = read_16bitBE(0x20, streamFile);
loop_start_skip = read_16bitBE(0x22, streamFile);
//loop_block = read_16bitBE(0x20, streamFile);
//loop_start_skip = read_16bitBE(0x22, streamFile);
loop_end_skip = read_16bitBE(0x24, streamFile);
format = read_8bit(0x28, streamFile);

View File

@ -307,15 +307,14 @@ static layered_layout_data* build_layered_xvag(STREAMFILE *streamFile, xvag_head
goto fail;
}
if ( !vgmstream_open_stream(data->layers[i], temp_streamFile, 0x00) ) {
if ( !vgmstream_open_stream(data->layers[i], temp_streamFile, 0x00) )
goto fail;
}
close_streamfile(temp_streamFile);
}
/* setup layered VGMSTREAMs */
if (!setup_layout_layered(data))
goto fail;
close_streamfile(temp_streamFile);
return data;
fail:

View File

@ -2,6 +2,71 @@
#include "plugins.h"
#include "mixing.h"
/* ****************************************** */
/* CONTEXT: simplifies plugin code */
/* ****************************************** */
int vgmstream_ctx_is_valid(const char* filename, vgmstream_ctx_valid_cfg *cfg) {
const char ** extension_list;
size_t extension_list_len;
const char *extension;
int i;
if (cfg->is_extension) {
extension = filename;
} else {
extension = filename_extension(filename);
}
/* some metas accept extensionless files */
if (strlen(extension) <= 0) {
return !cfg->reject_extensionless;
}
/* try in default list */
if (!cfg->skip_standard) {
extension_list = vgmstream_get_formats(&extension_list_len);
for (i = 0; i < extension_list_len; i++) {
if (strcasecmp(extension, extension_list[i]) == 0) {
return 1;
}
}
}
/* try in common extensions */
if (cfg->accept_common) {
extension_list = vgmstream_get_common_formats(&extension_list_len);
for (i = 0; i < extension_list_len; i++) {
if (strcasecmp(extension, extension_list[i]) == 0)
return 1;
}
}
/* allow anything not in the normal list but not in common extensions */
if (cfg->accept_unknown) {
int is_common = 0;
extension_list = vgmstream_get_common_formats(&extension_list_len);
for (i = 0; i < extension_list_len; i++) {
if (strcasecmp(extension, extension_list[i]) == 0) {
is_common = 1;
break;
}
}
if (!is_common)
return 1;
}
return 0;
}
/* ****************************************** */
/* TAGS: loads key=val tags from a file */
/* ****************************************** */
#define VGMSTREAM_TAGS_LINE_MAX 2048
/* opaque tag state */
@ -253,6 +318,10 @@ void vgmstream_tags_reset(VGMSTREAM_TAGS* tags, const char* target_filename) {
tags->targetname_len = strlen(tags->targetname);
}
/* ****************************************** */
/* MIXING: modifies vgmstream output */
/* ****************************************** */
void vgmstream_mixing_enable(VGMSTREAM* vgmstream, int32_t max_sample_count, int *input_channels, int *output_channels) {
mixing_setup(vgmstream, max_sample_count);
mixing_info(vgmstream, input_channels, output_channels);

View File

@ -6,46 +6,57 @@
#include "streamfile.h"
/* ****************************************** */
/* CONTEXT: simplifies plugin code */
/* ****************************************** */
typedef struct {
int is_extension; /* set if filename is already an extension */
int skip_standard; /* set if shouldn't check standard formats */
int reject_extensionless; /* set if player can't play extensionless files */
int accept_unknown; /* set to allow any extension (for txth) */
int accept_common; /* set to allow known-but-common extension (when player has plugin priority) */
} vgmstream_ctx_valid_cfg;
/* returns if vgmstream can parse file by extension */
int vgmstream_ctx_is_valid(const char* filename, vgmstream_ctx_valid_cfg *cfg);
#if 0
/* ****************************************** */
/* PLAYER: simplifies plugin code */
/* ****************************************** */
/* opaque player state */
typedef struct VGMSTREAM_PLAYER VGMSTREAM_PLAYER;
typedef struct VGMSTREAM_CTX VGMSTREAM_CTX;
typedef struct {
//...
} VGMSTREAM_PLAYER_INFO;
} VGMSTREAM_CTX_INFO;
VGMSTREAM_PLAYER* vgmstream_player_init(...);
VGMSTREAM_CTX* vgmstream_ctx_init(...);
VGMSTREAM_PLAYER* vgmstream_player_format_check(...);
VGMSTREAM_PLAYER* vgmstream_player_set_format_whilelist(...);
VGMSTREAM_PLAYER* vgmstream_player_set_format_blacklist(...);
VGMSTREAM_CTX* vgmstream_ctx_format_check(...);
VGMSTREAM_CTX* vgmstream_ctx_set_format_whilelist(...);
VGMSTREAM_CTX* vgmstream_ctx_set_format_blacklist(...);
VGMSTREAM_PLAYER* vgmstream_player_set_file(...);
VGMSTREAM_CTX* vgmstream_ctx_set_file(...);
VGMSTREAM_PLAYER* vgmstream_player_get_config(...);
VGMSTREAM_CTX* vgmstream_ctx_get_config(...);
VGMSTREAM_PLAYER* vgmstream_player_set_config(...);
VGMSTREAM_CTX* vgmstream_ctx_set_config(...);
VGMSTREAM_PLAYER* vgmstream_player_get_buffer(...);
VGMSTREAM_CTX* vgmstream_ctx_get_buffer(...);
VGMSTREAM_PLAYER* vgmstream_player_get_info(...);
VGMSTREAM_CTX* vgmstream_ctx_get_info(...);
VGMSTREAM_PLAYER* vgmstream_player_describe(...);
VGMSTREAM_CTX* vgmstream_ctx_describe(...);
VGMSTREAM_PLAYER* vgmstream_player_get_title(...);
VGMSTREAM_CTX* vgmstream_ctx_get_title(...);
VGMSTREAM_PLAYER* vgmstream_player_get_tagfile(...);
VGMSTREAM_CTX* vgmstream_ctx_get_tagfile(...);
VGMSTREAM_PLAYER* vgmstream_player_play(...);
VGMSTREAM_CTX* vgmstream_ctx_play(...);
VGMSTREAM_PLAYER* vgmstream_player_seek(...);
VGMSTREAM_CTX* vgmstream_ctx_seek(...);
VGMSTREAM_PLAYER* vgmstream_player_close(...);
VGMSTREAM_CTX* vgmstream_ctx_close(...);
#endif

View File

@ -18,15 +18,15 @@ typedef struct {
size_t buffersize; /* max buffer size */
size_t validsize; /* current buffer size */
size_t filesize; /* buffered file size */
} STDIOSTREAMFILE;
} STDIO_STREAMFILE;
static STREAMFILE * open_stdio_streamfile_buffer(const char * const filename, size_t buffersize);
static STREAMFILE * open_stdio_streamfile_buffer_by_file(FILE *infile,const char * const filename, size_t buffersize);
static size_t read_stdio(STDIOSTREAMFILE *streamfile,uint8_t * dest, off_t offset, size_t length) {
static size_t read_stdio(STDIO_STREAMFILE *streamfile,uint8_t * dest, off_t offset, size_t length) {
size_t length_read_total = 0;
if (!streamfile || !streamfile->infile || !dest || length <= 0 || offset < 0)
if (!streamfile->infile || !dest || length <= 0 || offset < 0)
return 0;
/* is the part of the requested length in the buffer? */
@ -45,6 +45,11 @@ static size_t read_stdio(STDIOSTREAMFILE *streamfile,uint8_t * dest, off_t offse
dest += length_to_read;
}
#ifdef VGM_DEBUG_OUTPUT
if (offset < streamfile->buffer_offset) {
VGM_LOG("STDIO: rebuffer, requested %lx vs %lx (sf %x)\n", offset, streamfile->buffer_offset, (uint32_t)streamfile);
}
#endif
/* read the rest of the requested length */
while (length > 0) {
@ -99,36 +104,35 @@ static size_t read_stdio(STDIOSTREAMFILE *streamfile,uint8_t * dest, off_t offse
streamfile->offset = offset; /* last fread offset */
return length_read_total;
}
static size_t get_size_stdio(STDIOSTREAMFILE * streamfile) {
static size_t get_size_stdio(STDIO_STREAMFILE * streamfile) {
return streamfile->filesize;
}
static off_t get_offset_stdio(STDIOSTREAMFILE *streamfile) {
static off_t get_offset_stdio(STDIO_STREAMFILE *streamfile) {
return streamfile->offset;
}
static void get_name_stdio(STDIOSTREAMFILE *streamfile,char *buffer,size_t length) {
static void get_name_stdio(STDIO_STREAMFILE *streamfile,char *buffer,size_t length) {
strncpy(buffer,streamfile->name,length);
buffer[length-1]='\0';
}
static void close_stdio(STDIOSTREAMFILE * streamfile) {
static void close_stdio(STDIO_STREAMFILE * streamfile) {
if (streamfile->infile)
fclose(streamfile->infile);
free(streamfile->buffer);
free(streamfile);
}
static STREAMFILE *open_stdio(STDIOSTREAMFILE *streamFile,const char * const filename,size_t buffersize) {
int newfd;
FILE *newfile;
STREAMFILE *newstreamFile;
static STREAMFILE *open_stdio(STDIO_STREAMFILE *streamFile,const char * const filename,size_t buffersize) {
if (!filename)
return NULL;
#if !defined (__ANDROID__)
// if same name, duplicate the file pointer we already have open
if (streamFile->infile && !strcmp(streamFile->name,filename)) {
if (((newfd = dup(fileno(streamFile->infile))) >= 0) &&
(newfile = fdopen( newfd, "rb" )))
{
int newfd;
FILE *newfile;
STREAMFILE *newstreamFile;
if ( ((newfd = dup(fileno(streamFile->infile))) >= 0) && (newfile = fdopen( newfd, "rb")) ) {
newstreamFile = open_stdio_streamfile_buffer_by_file(newfile,filename,buffersize);
if (newstreamFile) {
return newstreamFile;
@ -144,12 +148,12 @@ static STREAMFILE *open_stdio(STDIOSTREAMFILE *streamFile,const char * const fil
static STREAMFILE * open_stdio_streamfile_buffer_by_file(FILE *infile, const char * const filename, size_t buffersize) {
uint8_t * buffer = NULL;
STDIOSTREAMFILE * streamfile = NULL;
STDIO_STREAMFILE * streamfile = NULL;
buffer = calloc(buffersize,1);
if (!buffer) goto fail;
streamfile = calloc(1,sizeof(STDIOSTREAMFILE));
streamfile = calloc(1,sizeof(STDIO_STREAMFILE));
if (!streamfile) goto fail;
streamfile->sf.read = (void*)read_stdio;
@ -237,7 +241,7 @@ typedef struct {
static size_t buffer_read(BUFFER_STREAMFILE *streamfile, uint8_t * dest, off_t offset, size_t length) {
size_t length_read_total = 0;
if (!streamfile || !dest || length <= 0 || offset < 0)
if (!dest || length <= 0 || offset < 0)
return 0;
/* is the part of the requested length in the buffer? */
@ -256,6 +260,11 @@ static size_t buffer_read(BUFFER_STREAMFILE *streamfile, uint8_t * dest, off_t o
dest += length_to_read;
}
#ifdef VGM_DEBUG_OUTPUT
if (offset < streamfile->buffer_offset) {
VGM_LOG("BUFFER: rebuffer, requested %lx vs %lx (sf %x)\n", offset, streamfile->buffer_offset, (uint32_t)streamfile);
}
#endif
/* read the rest of the requested length */
while (length > 0) {
@ -704,6 +713,7 @@ static STREAMFILE *multifile_open(MULTIFILE_STREAMFILE *streamfile, const char *
new_sf = open_multifile_streamfile(new_inner_sfs, streamfile->inner_sfs_size);
if (!new_sf) goto fail;
free(new_inner_sfs);
return new_sf;
}
else {

View File

@ -101,14 +101,22 @@ void put_32bitBE(uint8_t * buf, int32_t i) {
}
void swap_samples_le(sample_t *buf, int count) {
/* Windows can't be BE... I think */
#if !defined(_WIN32)
#if !defined(__BYTE_ORDER__) || __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
int i;
for (i = 0; i < count; i++) {
/* 16b sample in memory: aabb where aa=MSB, bb=LSB */
uint8_t b0 = buf[i] & 0xff;
uint8_t b1 = buf[i] >> 8;
uint8_t *p = (uint8_t*)&(buf[i]);
/* 16b sample in buffer: bbaa where bb=LSB, aa=MSB */
p[0] = b0;
p[1] = b1;
/* when endianness is LE, buffer has bbaa already so this function can be skipped */
}
#endif
#endif
}
/* length is maximum length of dst. dst will always be null-terminated if

View File

@ -79,7 +79,8 @@ static inline int round10(int val) {
* extension in the original filename or the ending null byte if no extension */
const char * filename_extension(const char * filename);
void swap_samples_le(sample *buf, int count);
/* swap samples in machine endianness to little endian (useful to write .wav) */
void swap_samples_le(sample_t *buf, int count);
void concatn(int length, char * dst, const char * src);

View File

@ -2498,7 +2498,7 @@ static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *strea
return;
extension = (char *)filename_extension(new_filename);
if (extension - new_filename >= 1 && extension[-1] == '.')
if (extension - new_filename >= 1 && extension[-1] == '.') /* [-1] is ok, yeah */
extension--; /* must include "." */
extension_len = strlen(extension);
@ -2514,7 +2514,7 @@ static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *strea
//;VGM_LOG("DFS: l=%s, r=%s\n", this_suffix,that_suffix);
/* is suffix matches paste opposite suffix (+ terminator) to extension pointer, thus to new_filename */
/* if suffix matches paste opposite suffix (+ terminator) to extension pointer, thus to new_filename */
if (this_suffix[0] == '.' && extension_len == this_suffix_len) { /* same extension */
//;VGM_LOG("DFS: same ext %s vs %s len %i\n", extension, this_suffix, this_suffix_len);
if (memcmp(extension,this_suffix,this_suffix_len) == 0) {
@ -2522,7 +2522,7 @@ static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *strea
memcpy (extension, that_suffix,that_suffix_len+1);
}
}
else if (filename_len > this_suffix_len) { /* same suffix (without extension) */
else if (filename_len - extension_len > this_suffix_len) { /* same suffix (without extension) */
//;VGM_LOG("DFS: same suf %s vs %s len %i\n", extension - this_suffix_len, this_suffix, this_suffix_len);
if (memcmp(extension - this_suffix_len, this_suffix,this_suffix_len) == 0) {
dfs_pair = j;
@ -2623,6 +2623,8 @@ static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *strea
opened_vgmstream->channels = 2;
/* discard the second VGMSTREAM */
mixing_close(new_vgmstream);
free(new_vgmstream->start_vgmstream);
free(new_vgmstream);
mixing_update_channel(opened_vgmstream); /* notify of new channel hacked-in */

View File

@ -1329,6 +1329,9 @@ int get_vgmstream_average_bitrate(VGMSTREAM * vgmstream);
* The list disables some common formats that may conflict (.wav, .ogg, etc). */
const char ** vgmstream_get_formats(size_t * size);
/* same, but for common-but-disabled formats in the above list. */
const char ** vgmstream_get_common_formats(size_t * size);
/* Force enable/disable internal looping. Should be done before playing anything (or after reset),
* and not all codecs support arbitrary loop values ATM. */
void vgmstream_force_loop(VGMSTREAM* vgmstream, int loop_flag, int loop_start_sample, int loop_end_sample);

View File

@ -69,6 +69,8 @@ typedef struct {
int disable_subsongs;
int downmix_channels;
int tagfile_disable;
int exts_unknown_on;
int exts_common_on;
ReplayGainType gain_type;
ReplayGainType clip_type;
} winamp_settings;
@ -384,6 +386,8 @@ static void cfg_char_to_wchar(TCHAR *wdst, size_t wdstsize, const char *src) {
#define DEFAULT_DISABLE_SUBSONGS 0
#define DEFAULT_DOWNMIX_CHANNELS 0
#define DEFAULT_TAGFILE_DISABLE 0
#define DEFAULT_EXTS_UNKNOWN_ON 0
#define DEFAULT_EXTS_COMMON_ON 0
#define DEFAULT_GAIN_TYPE 1
#define DEFAULT_CLIP_TYPE 2
@ -396,6 +400,8 @@ static void cfg_char_to_wchar(TCHAR *wdst, size_t wdstsize, const char *src) {
#define INI_ENTRY_DISABLE_SUBSONGS TEXT("disable_subsongs")
#define INI_ENTRY_DOWNMIX_CHANNELS TEXT("downmix_channels")
#define INI_ENTRY_TAGFILE_DISABLE TEXT("tagfile_disable")
#define INI_ENTRY_EXTS_UNKNOWN_ON TEXT("exts_unknown_on")
#define INI_ENTRY_EXTS_COMMON_ON TEXT("exts_common_on")
#define INI_ENTRY_GAIN_TYPE TEXT("gain_type")
#define INI_ENTRY_CLIP_TYPE TEXT("clip_type")
@ -517,6 +523,8 @@ static void load_config() {
}
settings.tagfile_disable = GetPrivateProfileInt(CONFIG_APP_NAME,INI_ENTRY_TAGFILE_DISABLE,DEFAULT_TAGFILE_DISABLE,iniFile);
settings.exts_unknown_on = GetPrivateProfileInt(CONFIG_APP_NAME,INI_ENTRY_EXTS_UNKNOWN_ON,DEFAULT_EXTS_UNKNOWN_ON,iniFile);
settings.exts_common_on = GetPrivateProfileInt(CONFIG_APP_NAME,INI_ENTRY_EXTS_COMMON_ON,DEFAULT_EXTS_COMMON_ON,iniFile);
settings.gain_type = GetPrivateProfileInt(CONFIG_APP_NAME, INI_ENTRY_GAIN_TYPE, DEFAULT_GAIN_TYPE, iniFile);
settings.clip_type = GetPrivateProfileInt(CONFIG_APP_NAME, INI_ENTRY_CLIP_TYPE, DEFAULT_CLIP_TYPE, iniFile);
@ -584,6 +592,10 @@ INT_PTR CALLBACK configDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lPara
if (settings.tagfile_disable)
CheckDlgButton(hDlg,IDC_TAGFILE_DISABLE,BST_CHECKED);
if (settings.exts_unknown_on)
CheckDlgButton(hDlg,IDC_EXTS_UNKNOWN_ON,BST_CHECKED);
if (settings.exts_common_on)
CheckDlgButton(hDlg,IDC_EXTS_COMMON_ON,BST_CHECKED);
break;
@ -680,6 +692,14 @@ INT_PTR CALLBACK configDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lPara
cfg_sprintf(buf, TEXT("%d"),settings.tagfile_disable);
WritePrivateProfileString(CONFIG_APP_NAME,INI_ENTRY_TAGFILE_DISABLE,buf,iniFile);
settings.exts_unknown_on = (IsDlgButtonChecked(hDlg,IDC_EXTS_UNKNOWN_ON) == BST_CHECKED);
cfg_sprintf(buf, TEXT("%d"),settings.exts_unknown_on);
WritePrivateProfileString(CONFIG_APP_NAME,INI_ENTRY_EXTS_UNKNOWN_ON,buf,iniFile);
settings.exts_common_on = (IsDlgButtonChecked(hDlg,IDC_EXTS_COMMON_ON) == BST_CHECKED);
cfg_sprintf(buf, TEXT("%d"),settings.exts_common_on);
WritePrivateProfileString(CONFIG_APP_NAME,INI_ENTRY_EXTS_COMMON_ON,buf,iniFile);
hReplayGain = GetDlgItem(hDlg, IDC_REPLAYGAIN);
settings.gain_type = SendMessage(hReplayGain, CB_GETCURSEL, 0, 0);
cfg_sprintf(buf, TEXT("%d"), settings.gain_type);
@ -725,6 +745,8 @@ INT_PTR CALLBACK configDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lPara
CheckDlgButton(hDlg,IDC_DISABLE_SUBSONGS,BST_UNCHECKED);
SetDlgItemText(hDlg,IDC_DOWNMIX_CHANNELS,DEFAULT_DOWNMIX_CHANNELS);
CheckDlgButton(hDlg,IDC_TAGFILE_DISABLE,BST_UNCHECKED);
CheckDlgButton(hDlg,IDC_EXTS_UNKNOWN_ON,BST_UNCHECKED);
CheckDlgButton(hDlg,IDC_EXTS_COMMON_ON,BST_UNCHECKED);
break;
default:
@ -1079,34 +1101,16 @@ void winamp_Quit() {
/* called before extension checks, to allow detection of mms://, etc */
int winamp_IsOurFile(const in_char *fn) {
const in_char *filename;
const in_char *extension;
vgmstream_ctx_valid_cfg cfg = {0};
char filename_utf8[PATH_LIMIT];
/* favor strrchr (optimized/aligned) rather than homemade loops */
/* find possible separator first to avoid misdetecting folders with dots + extensionless files
* (allow both slashes as plugin could pass normalized '/') */
filename = wa_strrchr(fn, wa_L('\\'));
if (filename != NULL)
filename++; /* skip separator */
else {
filename = wa_strrchr(fn, wa_L('/'));
if (filename != NULL)
filename++; /* skip separator */
else
filename = fn; /* pathname has no separators (single filename) */
}
extension = wa_strrchr(filename,'.');
if (extension != NULL)
extension++; /* skip dot */
else
return 1; /* extensionless, try to play it */
wa_ichar_to_char(filename_utf8, PATH_LIMIT, fn);
cfg.skip_standard = 1; /* validated by Winamp */
cfg.accept_unknown = settings.exts_unknown_on;
cfg.accept_common = settings.exts_common_on;
/* returning 0 here means it only accepts the extensions in working_extension_list */
/* it's possible to ignore the list and manually accept extensions, like foobar's g_is_our_path */
return 0;
return vgmstream_ctx_is_valid(filename_utf8, &cfg);
}

View File

@ -13,3 +13,5 @@
#define IDC_TAGFILE_DISABLE 1012
#define IDC_REPLAYGAIN 1013
#define IDC_CLIPPROTECT 1014
#define IDC_EXTS_UNKNOWN_ON 1015
#define IDC_EXTS_COMMON_ON 1016

View File

@ -4,7 +4,7 @@
#define IDC_STATIC -1
//elements: text, id, x, y, width, height [, style [, extended-style]]
IDD_CONFIG DIALOGEX 0, 0, 187, 164
IDD_CONFIG DIALOGEX 0, 0, 187, 196
STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "in_vgmstream configuration"
FONT 8, "MS Sans Serif", 0, 0, 0x0
@ -45,4 +45,11 @@ BEGIN
EDITTEXT IDC_DOWNMIX_CHANNELS,52,112,37,14,ES_AUTOHSCROLL
CONTROL "Disable tagfile",IDC_TAGFILE_DISABLE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,131,87,10
//CONTROL "Override title",IDC_OVERRIDE_TITLE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,147,97,10
CONTROL "Enable unknown exts",IDC_EXTS_UNKNOWN_ON,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,163,87,10
CONTROL "Enable common exts",IDC_EXTS_COMMON_ON,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,179,87,10
END