mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-17 23:36:41 +01:00
Fix various leaks and issues found with drmemory
This commit is contained in:
parent
2b2b9e0ec8
commit
1b34ef1f01
18
doc/DEV.md
18
doc/DEV.md
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -152,6 +152,7 @@ void free_layout_layered(layered_layout_data *data) {
|
||||
}
|
||||
free(data->layers);
|
||||
}
|
||||
free(data->buffer);
|
||||
free(data);
|
||||
}
|
||||
|
||||
|
@ -224,6 +224,7 @@ void free_layout_segmented(segmented_layout_data *data) {
|
||||
}
|
||||
free(data->segments);
|
||||
}
|
||||
free(data->buffer);
|
||||
free(data);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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:
|
||||
|
@ -117,18 +117,17 @@ static void close_stdio(STDIOSTREAMFILE * streamfile) {
|
||||
}
|
||||
|
||||
static STREAMFILE *open_stdio(STDIOSTREAMFILE *streamFile,const char * const filename,size_t buffersize) {
|
||||
int newfd;
|
||||
FILE *newfile;
|
||||
STREAMFILE *newstreamFile;
|
||||
|
||||
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;
|
||||
@ -704,6 +703,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 {
|
||||
|
@ -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 */
|
||||
|
Loading…
x
Reference in New Issue
Block a user