diff --git a/Plugins/vgmstream/vgmstream/VGMContainer.m b/Plugins/vgmstream/vgmstream/VGMContainer.m index 52b0f3b19..0ec8d3ede 100644 --- a/Plugins/vgmstream/vgmstream/VGMContainer.m +++ b/Plugins/vgmstream/vgmstream/VGMContainer.m @@ -47,14 +47,13 @@ url = [NSURL fileURLWithPath:[path stringByRemovingPercentEncoding]]; } - VGMSTREAM *stream = init_vgmstream_from_cogfile([path UTF8String], 0); - if(!stream) { - ALog(@"Open failed for file: %@", [url absoluteString]); - return @[]; - } + libstreamfile_t* sf = open_vfs([path UTF8String]); + if(!sf) return @[]; - if(stream->stream_index > 0) { - close_vgmstream(stream); + libvgmstream_config_t vcfg = {0}; + + libvgmstream_t* infostream = libvgmstream_create(sf, 0, &vcfg); + if(!infostream) { return @[]; } @@ -64,30 +63,33 @@ NSURL *trackurl; int i; - int subsongs = stream->num_streams; + int subsongs = infostream->format->subsong_count; if(subsongs == 0) subsongs = 1; + if(infostream->format->subsong_index > 0) + subsongs = 1; { trackurl = [NSURL URLWithString:[[url absoluteString] stringByAppendingString:@"#1"]]; - [sharedMyCache stuffURL:trackurl stream:stream]; + [sharedMyCache stuffURL:trackurl stream:infostream]; [tracks addObject:trackurl]; } for(i = 2; i <= subsongs; ++i) { - close_vgmstream(stream); + libvgmstream_close_stream(infostream); - stream = init_vgmstream_from_cogfile([path UTF8String], i); + infostream = libvgmstream_create(sf, i, &vcfg); - if(!stream) + if(!infostream) return @[]; trackurl = [NSURL URLWithString:[[url absoluteString] stringByAppendingFormat:@"#%i", i]]; - [sharedMyCache stuffURL:trackurl stream:stream]; + [sharedMyCache stuffURL:trackurl stream:infostream]; [tracks addObject:trackurl]; } - close_vgmstream(stream); + libvgmstream_close_stream(infostream); + libstreamfile_close(sf); return tracks; } diff --git a/Plugins/vgmstream/vgmstream/VGMDecoder.h b/Plugins/vgmstream/vgmstream/VGMDecoder.h index dd57e9698..9eebd047c 100644 --- a/Plugins/vgmstream/vgmstream/VGMDecoder.h +++ b/Plugins/vgmstream/vgmstream/VGMDecoder.h @@ -8,8 +8,7 @@ #import -#import -#import +#import #import "Plugin.h" @@ -19,14 +18,16 @@ + (id)sharedCache; -- (void)stuffURL:(NSURL *)url stream:(VGMSTREAM *)stream; +- (void)stuffURL:(NSURL *)url stream:(libvgmstream_t *)stream; - (NSDictionary *)getPropertiesForURL:(NSURL *)url; - (NSDictionary *)getMetadataForURL:(NSURL *)url; @end @interface VGMDecoder : NSObject { - VGMSTREAM *stream; + libvgmstream_t *stream; + + BOOL formatFloat; BOOL playForever; BOOL canPlayForever; @@ -37,8 +38,5 @@ int bitrate; long totalFrames; long framesRead; - - void *sample_buf; - void *sample_buf_temp; } @end diff --git a/Plugins/vgmstream/vgmstream/VGMDecoder.m b/Plugins/vgmstream/vgmstream/VGMDecoder.m index 5f1290616..ac0beedf1 100644 --- a/Plugins/vgmstream/vgmstream/VGMDecoder.m +++ b/Plugins/vgmstream/vgmstream/VGMDecoder.m @@ -9,7 +9,7 @@ #import "VGMDecoder.h" #import "VGMInterface.h" -#import +#import #import "PlaylistController.h" @@ -52,33 +52,16 @@ static NSString *get_description_tag(const char *description, const char *tag, c return self; } -- (void)stuffURL:(NSURL *)url stream:(VGMSTREAM *)stream { - vgmstream_cfg_t vcfg = { 0 }; - - vcfg.allow_play_forever = 1; - vcfg.play_forever = 0; - vcfg.loop_count = 2; - vcfg.fade_time = 10; - vcfg.fade_delay = 0; - vcfg.ignore_loop = 0; - - vgmstream_apply_config(stream, &vcfg); - - int output_channels = stream->channels; - - vgmstream_mixing_autodownmix(stream, 6); - vgmstream_mixing_enable(stream, MAX_BUFFER_SAMPLES, NULL, &output_channels); +- (void)stuffURL:(NSURL *)url stream:(libvgmstream_t *)stream { + int output_channels = stream->format->channels; int track_num = [[url fragment] intValue]; - int sampleRate = stream->sample_rate; + int sampleRate = stream->format->sample_rate; int channels = output_channels; - long totalFrames = vgmstream_get_samples(stream); + long totalFrames = stream->format->play_samples; - int bitrate = get_vgmstream_average_bitrate(stream); - - char description[1024]; - describe_vgmstream(stream, description, 1024); + int bitrate = stream->format->stream_bitrate; NSString *path = [url absoluteString]; NSRange fragmentRange = [path rangeOfString:@"#" options:NSBackwardsSearch]; @@ -107,16 +90,17 @@ static NSString *get_description_tag(const char *description, const char *tag, c NSNumber *rgAlbumGain = @(0); NSNumber *rgAlbumPeak = @(0); - codec = get_description_tag(description, "encoding: ", 0); + codec = [NSString stringWithUTF8String:stream->format->codec_name]; - STREAMFILE *tagFile = open_cog_streamfile_from_url(tagurl); - if(tagFile) { - VGMSTREAM_TAGS *tags; - const char *tag_key, *tag_val; + libstreamfile_t* sf_tags = open_vfs([[tagurl absoluteString] UTF8String]); + if(sf_tags) { + libvgmstream_tags_t* tags = NULL; - tags = vgmstream_tags_init(&tag_key, &tag_val); - vgmstream_tags_reset(tags, [filename UTF8String]); - while(vgmstream_tags_next_tag(tags, tagFile)) { + tags = libvgmstream_tags_init(sf_tags); + libvgmstream_tags_find(tags, [filename UTF8String]); + while(libvgmstream_tags_next_tag(tags)) { + const char* tag_key = tags->key; + const char* tag_val = tags->val; NSString *value = guess_encoding_of_string(tag_val); if(!strncasecmp(tag_key, "REPLAYGAIN_", strlen("REPLAYGAIN_"))) { if(!strncasecmp(tag_key + strlen("REPLAYGAIN_"), "TRACK_", strlen("TRACK_"))) { @@ -148,15 +132,23 @@ static NSString *get_description_tag(const char *description, const char *tag, c title = value; } } - vgmstream_tags_close(tags); - close_streamfile(tagFile); + libvgmstream_tags_free(tags); + libstreamfile_close(sf_tags); + } + + BOOL formatFloat; + switch(stream->format->sample_format) { + case LIBVGMSTREAM_SFMT_PCM16: formatFloat = NO; break; + case LIBVGMSTREAM_SFMT_FLOAT: formatFloat = YES; break; + default: + return; } NSDictionary *properties = @{ @"bitrate": @(bitrate / 1000), @"sampleRate": @(sampleRate), @"totalFrames": @(totalFrames), - @"bitsPerSample": @(16), - @"floatingPoint": @(NO), + @"bitsPerSample": @(formatFloat ? 32 : 16), + @"floatingPoint": @(formatFloat), @"channels": @(channels), @"seekable": @(YES), @"replaygain_album_gain": rgAlbumGain, @@ -168,8 +160,8 @@ static NSString *get_description_tag(const char *description, const char *tag, c @"encoding": @"lossy/lossless" }; if([title isEqualToString:@""]) { - if(stream->num_streams > 1) { - title = [NSString stringWithFormat:@"%@ - %@", [[urlTrimmed URLByDeletingPathExtension] lastPathComponent], guess_encoding_of_string(stream->stream_name)]; + if(stream->format->subsong_count > 1) { + title = [NSString stringWithFormat:@"%@ - %@", [[urlTrimmed URLByDeletingPathExtension] lastPathComponent], guess_encoding_of_string(stream->format->stream_name)]; } else { title = [[urlTrimmed URLByDeletingPathExtension] lastPathComponent]; } @@ -254,25 +246,9 @@ static NSString *get_description_tag(const char *description, const char *tag, c path = [path substringToIndex:fragmentRange.location]; } - NSLog(@"Opening %@ subsong %d", path, track_num); + playForever = IsRepeatOneSet(); - stream = init_vgmstream_from_cogfile([[path stringByRemovingPercentEncoding] UTF8String], track_num); - if(!stream) - return NO; - - int output_channels = stream->channels; - - vgmstream_mixing_autodownmix(stream, 6); - vgmstream_mixing_enable(stream, MAX_BUFFER_SAMPLES, NULL, &output_channels); - - canPlayForever = stream->loop_flag; - if(canPlayForever) { - playForever = IsRepeatOneSet(); - } else { - playForever = NO; - } - - vgmstream_cfg_t vcfg = { 0 }; + libvgmstream_config_t vcfg = { 0 }; vcfg.allow_play_forever = 1; vcfg.play_forever = playForever; @@ -280,16 +256,35 @@ static NSString *get_description_tag(const char *description, const char *tag, c vcfg.fade_time = fadeTime; vcfg.fade_delay = 0; vcfg.ignore_loop = 0; + vcfg.auto_downmix_channels = 6; - vgmstream_apply_config(stream, &vcfg); + NSLog(@"Opening %@ subsong %d", path, track_num); - sampleRate = stream->sample_rate; + libstreamfile_t* sf = open_vfs([[path stringByRemovingPercentEncoding] UTF8String]); + if(!sf) + return NO; + stream = libvgmstream_create(sf, track_num, &vcfg); + if(!stream) + return NO; + + int output_channels = stream->format->channels; + + sampleRate = stream->format->sample_rate; channels = output_channels; - totalFrames = vgmstream_get_samples(stream); + totalFrames = stream->format->play_samples; framesRead = 0; - bitrate = get_vgmstream_average_bitrate(stream); + bitrate = stream->format->stream_bitrate; + + switch(stream->format->sample_format) { + case LIBVGMSTREAM_SFMT_PCM16: formatFloat = NO; break; + case LIBVGMSTREAM_SFMT_FLOAT: formatFloat = YES; break; + default: + libvgmstream_free(stream); + stream = NULL; + return NO; + } [self willChangeValueForKey:@"properties"]; [self didChangeValueForKey:@"properties"]; @@ -301,8 +296,8 @@ static NSString *get_description_tag(const char *description, const char *tag, c return @{ @"bitrate": @(bitrate / 1000), @"sampleRate": @(sampleRate), @"totalFrames": @(totalFrames), - @"bitsPerSample": @(16), - @"floatingPoint": @(NO), + @"bitsPerSample": @(formatFloat ? 32 : 16), + @"floatingPoint": @(formatFloat), @"channels": @(channels), @"seekable": @(YES), @"endian": @"host", @@ -314,88 +309,44 @@ static NSString *get_description_tag(const char *description, const char *tag, c } - (AudioChunk *)readAudio { - UInt32 frames = 1024; - UInt32 framesMax = frames; - UInt32 framesDone = 0; - - double streamTimestamp = (double)(stream->pstate.play_position) / sampleRate; + double streamTimestamp = (double)(libvgmstream_get_play_position(stream)) / sampleRate; id audioChunkClass = NSClassFromString(@"AudioChunk"); AudioChunk *chunk = [[audioChunkClass alloc] initWithProperties:[self properties]]; + size_t framesDone = 0; - if (!sample_buf) { - sample_buf = malloc(1024 * channels * sizeof(int16_t)); - if (!sample_buf) return nil; - } - void *buf = (void *)sample_buf; + for(;;) { + if(stream->decoder->done) break; - if(canPlayForever) { - BOOL repeatone = IsRepeatOneSet(); + int err = libvgmstream_render(stream); + if(err < 0) + break; - if(repeatone != playForever) { - playForever = repeatone; - vgmstream_set_play_forever(stream, repeatone); - } - } - - if(framesRead + frames > totalFrames && !playForever) - frames = totalFrames - framesRead; - if(frames > framesMax) - frames = 0; // integer overflow? - - while(frames) { - if (!sample_buf_temp) { - sample_buf_temp = malloc(MAX_BUFFER_SAMPLES * VGMSTREAM_MAX_CHANNELS * sizeof(int16_t)); - if (!sample_buf_temp) return nil; + const size_t bytes_done = stream->decoder->buf_bytes; + if(!bytes_done) { + continue; } - UInt32 frames_to_do = frames; - if(frames_to_do > MAX_BUFFER_SAMPLES) - frames_to_do = MAX_BUFFER_SAMPLES; + const size_t bytes_per_sample = stream->format->channels * (formatFloat ? 4 : 2); + framesDone = bytes_done / bytes_per_sample; - memset(sample_buf_temp, 0, frames_to_do * channels * sizeof(float)); - - sbuf_t sbuf = {0}; - sbuf_init(&sbuf, SFMT_S16, sample_buf_temp, frames_to_do, stream->channels); - - int frames_done = render_main(&sbuf, stream); - - if (!frames_done) return nil; - - framesRead += frames_done; - framesDone += frames_done; - - int16_t *obuf = (int16_t *)buf; - - memcpy(obuf, sample_buf_temp, frames_done * channels * sizeof(obuf[0])); - - obuf += frames_done * channels; - - buf = (void *)obuf; - - frames -= frames_done; + framesRead += framesDone; + break; } - [chunk setStreamTimestamp:streamTimestamp]; - [chunk assignSamples:sample_buf frameCount:framesDone]; + if(framesDone) { + [chunk setStreamTimestamp:streamTimestamp]; + [chunk assignSamples:stream->decoder->buf frameCount:framesDone]; + } return chunk; } - (long)seek:(long)frame { - if(canPlayForever) { - BOOL repeatone = IsRepeatOneSet(); - - if(repeatone != playForever) { - playForever = repeatone; - vgmstream_set_play_forever(stream, repeatone); - } - } - if(frame > totalFrames) frame = totalFrames; - seek_vgmstream(stream, frame); + libvgmstream_seek(stream, frame); framesRead = frame; @@ -403,16 +354,8 @@ static NSString *get_description_tag(const char *description, const char *tag, c } - (void)close { - close_vgmstream(stream); + libvgmstream_close_stream(stream); stream = NULL; - if (sample_buf_temp) { - free(sample_buf_temp); - sample_buf_temp = NULL; - } - if (sample_buf) { - free(sample_buf); - sample_buf = NULL; - } } - (void)dealloc { @@ -422,8 +365,8 @@ static NSString *get_description_tag(const char *description, const char *tag, c + (NSArray *)fileTypes { NSMutableArray *array = [[NSMutableArray alloc] init]; - size_t count; - const char **formats = vgmstream_get_formats(&count); + int count; + const char **formats = libvgmstream_get_extensions(&count); for(size_t i = 0; i < count; ++i) { [array addObject:[NSString stringWithUTF8String:formats[i]]]; diff --git a/Plugins/vgmstream/vgmstream/VGMInterface.h b/Plugins/vgmstream/vgmstream/VGMInterface.h index 2d1e2077c..58d6a457a 100644 --- a/Plugins/vgmstream/vgmstream/VGMInterface.h +++ b/Plugins/vgmstream/vgmstream/VGMInterface.h @@ -6,34 +6,8 @@ // Copyright 2017 __LoSnoCo__. All rights reserved. // -#import +#import -#import - -/* a STREAMFILE that operates via standard IO using a buffer */ -typedef struct _COGSTREAMFILE { - STREAMFILE vt; /* callbacks */ - - void* infile; /* CogSource, retained */ - char* name; /* FILE filename */ - int name_len; /* cache */ - - char* archname; /* archive name */ - int archname_len; /* cache */ - int archpath_end; /* where the last / ends before archive name */ - int archfile_end; /* where the last | ends before file name */ - - offv_t offset; /* last read offset (info) */ - offv_t buf_offset; /* current buffer data start */ - uint8_t* buf; /* data buffer */ - size_t buf_size; /* max buffer size */ - size_t valid_size; /* current buffer size */ - size_t file_size; /* buffered file size */ -} COGSTREAMFILE; - -STREAMFILE* open_cog_streamfile_from_url(NSURL* url); -STREAMFILE* open_cog_streamfile(const char* filename); - -VGMSTREAM* init_vgmstream_from_cogfile(const char* path, int subsong); +libstreamfile_t* open_vfs(const char *path); void register_log_callback(); diff --git a/Plugins/vgmstream/vgmstream/VGMInterface.m b/Plugins/vgmstream/vgmstream/VGMInterface.m index 7b584fe79..8da9e21a6 100644 --- a/Plugins/vgmstream/vgmstream/VGMInterface.m +++ b/Plugins/vgmstream/vgmstream/VGMInterface.m @@ -12,301 +12,105 @@ #import "Logging.h" -#import - static void log_callback(int level, const char* str) { - ALog(@"%@", str); + ALog(@"%s", str); } void register_log_callback() { - vgmstream_set_log_callback(VGM_LOG_LEVEL_ALL, &log_callback); + libvgmstream_set_log(LIBVGMSTREAM_LOG_LEVEL_ALL, &log_callback); } -static STREAMFILE* open_cog_streamfile_buffer(const char* const filename, size_t buf_size); -static STREAMFILE* open_cog_streamfile_buffer_by_file(id infile, const char* const filename, size_t buf_size); +typedef struct { + void* infile; + int64_t offset, file_size; + char name[0x4000]; +} vfs_priv_t; -static size_t cogsf_read(COGSTREAMFILE* sf, uint8_t* dst, offv_t offset, size_t length) { +static size_t vfs_read(void* user_data, uint8_t* dst, int64_t offset, int length) { + vfs_priv_t* priv = (vfs_priv_t*)user_data; size_t read_total = 0; - if(!sf || !sf->infile || !dst || length <= 0 || offset < 0) - return 0; - - //;VGM_LOG("cogsf: read %lx + %x (buf %lx + %x)\n", offset, length, sf->buf_offset, sf->valid_size); - - /* is the part of the requested length in the buffer? */ - if(offset >= sf->buf_offset && offset < sf->buf_offset + sf->valid_size) { - size_t buf_limit; - int buf_into = (int)(offset - sf->buf_offset); - - buf_limit = sf->valid_size - buf_into; - if(buf_limit > length) - buf_limit = length; - - //;VGM_LOG("cogsf: copy buf %lx + %x (+ %x) (buf %lx + %x)\n", offset, length_to_read, (length - length_to_read), sf->buf_offset, sf->valid_size); - - memcpy(dst, sf->buf + buf_into, buf_limit); - read_total += buf_limit; - length -= buf_limit; - offset += buf_limit; - dst += buf_limit; - } - -#ifdef VGM_DEBUG_OUTPUT - if(offset < sf->buf_offset && length > 0) { - // VGM_LOG("cogsf: rebuffer, requested %x vs %x (sf %x)\n", (uint32_t)offset, (uint32_t)sf->buf_offset, (uint32_t)sf); - // sf->rebuffer++; - // if (rebuffer > N) ... - } -#endif - NSObject* _file = (__bridge NSObject*)(sf->infile); + NSObject* _file = (__bridge NSObject*)(priv->infile); id __unsafe_unretained file = (id)_file; - /* read the rest of the requested length */ - while(length > 0) { - size_t length_to_read; - - /* ignore requests at EOF */ - if(offset >= sf->file_size) { - // offset = sf->file_size; /* seems fseek doesn't clamp offset */ - VGM_ASSERT_ONCE(offset > sf->file_size, "COGSF: reading over file_size 0x%x @ 0x%x + 0x%x\n", sf->file_size, (uint32_t)offset, length); - break; - } - - /* position to new offset */ - if(![file seek:offset whence:SEEK_SET]) { - break; /* this shouldn't happen in our code */ - } - - /* fill the buffer (offset now is beyond buf_offset) */ - sf->buf_offset = offset; - sf->valid_size = [file read:sf->buf amount:sf->buf_size]; - //;VGM_LOG("cogsf: read buf %lx + %x\n", sf->buf_offset, sf->valid_size); - - /* decide how much must be read this time */ - if(length > sf->buf_size) - length_to_read = sf->buf_size; - else - length_to_read = length; - - /* give up on partial reads (EOF) */ - if(sf->valid_size < length_to_read) { - memcpy(dst, sf->buf, sf->valid_size); - offset += sf->valid_size; - read_total += sf->valid_size; - break; - } - - /* use the new buffer */ - memcpy(dst, sf->buf, length_to_read); - offset += length_to_read; - read_total += length_to_read; - length -= length_to_read; - dst += length_to_read; + if(!dst || length <= 0 || offset < 0) + return 0; + + if(priv->offset != offset) { + BOOL ok = offset <= priv->file_size && [file seek:offset whence:SEEK_SET]; + if(!ok) + return 0; + priv->offset = offset; } - - sf->offset = offset; /* last fread offset */ - return read_total; -} -static size_t cogsf_get_size(COGSTREAMFILE* sf) { - return sf->file_size; -} -static offv_t cogsf_get_offset(COGSTREAMFILE* sf) { - return sf->offset; -} -static void cogsf_get_name(COGSTREAMFILE* sf, char* name, size_t name_size) { - int copy_size = sf->name_len + 1; - if(copy_size > name_size) - copy_size = name_size; - - memcpy(name, sf->name, copy_size); - name[copy_size - 1] = '\0'; + + size_t bytes_read = [file read:dst amount:length]; + priv->offset += bytes_read; + + return bytes_read; } -static STREAMFILE* cogsf_open(COGSTREAMFILE* sf, const char* const filename, size_t buf_size) { +static size_t vfs_get_size(void* user_data) { + vfs_priv_t* priv = (vfs_priv_t*)user_data; + return priv->file_size; +} + +static const char* vfs_get_name(void* user_data) { + vfs_priv_t* priv = (vfs_priv_t*)user_data; + return priv->name; +} + +static libstreamfile_t* vfs_open(void* user_data, const char* filename) { if(!filename) return NULL; - if(sf->archname) { - char finalname[PATH_LIMIT]; - const char* dirsep = NULL; - const char* dirsep2 = NULL; - - // newly open files should be "(current-path)\newfile" or "(current-path)\folder\newfile", so we need to make - // (archive-path = current-path)\(rest = newfile plus new folders) - int filename_len = strlen(filename); - - if(filename_len > sf->archpath_end) { - dirsep = &filename[sf->archpath_end]; - } else { - dirsep = strrchr(filename, '\\'); // vgmstream shouldn't remove paths though - dirsep2 = strrchr(filename, '/'); - if(dirsep2 > dirsep) - dirsep = dirsep2; - if(!dirsep) - dirsep = filename; - else - dirsep += 1; - } - - // TODO improve strops - memcpy(finalname, sf->archname, sf->archfile_end); // copy current path+archive - finalname[sf->archfile_end] = '\0'; - concatn(sizeof(finalname), finalname, dirsep); // paste possible extra dirs and filename - - // subfolders inside archives use "/" (path\archive.ext|subfolder/file.ext) - for(int i = sf->archfile_end; i < sizeof(finalname); i++) { - if(finalname[i] == '\0') - break; - if(finalname[i] == '\\') - finalname[i] = '/'; - } - - // console::formatter() << "finalname: " << finalname; - return open_cog_streamfile_buffer(finalname, buf_size); - } - - // The file is already open, add a reference to existing file - if(sf->infile && !strcmp(sf->name, filename)) { - // Already retained by sf, will be retained again if used - NSObject* _file = (__bridge NSObject*)(sf->infile); - id __unsafe_unretained file = (id)_file; - - STREAMFILE* new_sf = open_cog_streamfile_buffer_by_file(file, filename, buf_size); - - if(new_sf) { - return new_sf; - } - // Failure, try default open method - } - - // a normal open, open a new file - return open_cog_streamfile_buffer(filename, buf_size); + return open_vfs(filename); } -static void cogsf_close(COGSTREAMFILE* sf) { - if(sf->infile) - CFBridgingRelease(sf->infile); - free(sf->name); - free(sf->archname); - free(sf->buf); - free(sf); +static void vfs_close(libstreamfile_t* libsf) { + if(libsf->user_data) { + vfs_priv_t* priv = (vfs_priv_t*)libsf->user_data; + CFBridgingRelease(priv->infile); + } + free(libsf); } -static STREAMFILE* open_cog_streamfile_buffer_by_file(id infile, const char* const filename, size_t buf_size) { - uint8_t* buf = NULL; - COGSTREAMFILE* this_sf = NULL; +static libstreamfile_t* open_vfs_by_cogsource(id file, const char* path) { + vfs_priv_t* priv = NULL; + libstreamfile_t* libsf = (libstreamfile_t*)calloc(1, sizeof(libstreamfile_t)); + if(!libsf) return NULL; - buf = calloc(buf_size, sizeof(uint8_t)); - if(!buf) goto fail; + libsf->read = (int (*)(void*, uint8_t*, int64_t, int)) vfs_read; + libsf->get_size = (int64_t (*)(void*)) vfs_get_size; + libsf->get_name = (const char* (*)(void*)) vfs_get_name; + libsf->open = (libstreamfile_t* (*)(void*, const char* const)) vfs_open; + libsf->close = (void (*)(libstreamfile_t*)) vfs_close; - this_sf = calloc(1, sizeof(COGSTREAMFILE)); - if(!this_sf) goto fail; + libsf->user_data = (vfs_priv_t*)calloc(1, sizeof(vfs_priv_t)); + if(!libsf->user_data) goto fail; - this_sf->vt.read = (void*)cogsf_read; - this_sf->vt.get_size = (void*)cogsf_get_size; - this_sf->vt.get_offset = (void*)cogsf_get_offset; - this_sf->vt.get_name = (void*)cogsf_get_name; - this_sf->vt.open = (void*)cogsf_open; - this_sf->vt.close = (void*)cogsf_close; + priv = (vfs_priv_t*)libsf->user_data; + priv->infile = CFBridgingRetain(file); + priv->offset = 0; + strncpy(priv->name, path, sizeof(priv->name)); + priv->name[sizeof(priv->name) - 1] = '\0'; - if(infile) { - this_sf->infile = (void*)CFBridgingRetain(infile); + if(![file seekable]) { + goto fail; } - this_sf->buf_size = buf_size; - this_sf->buf = buf; + [file seek:0 whence:SEEK_END]; + priv->file_size = [file tell]; + [file seek:0 whence:SEEK_SET]; - this_sf->name = strdup(filename); - if(!this_sf->name) goto fail; - this_sf->name_len = strlen(this_sf->name); - - // Cog supports archives in unpack:// paths, similar to foobar2000 - if(strncmp(filename, "unpack", 6) == 0) { - const char* archfile_ptr = strrchr(this_sf->name, '|'); - char temp_save; - char* temp_null = 0; - if(archfile_ptr) { - this_sf->archfile_end = (intptr_t)archfile_ptr + 1 - (intptr_t)this_sf->name; - - // So we search for the last slash in the source path - temp_null = archfile_ptr; - temp_save = *temp_null; - *temp_null = '\0'; - } - - const char* archpath_ptr = strrchr(this_sf->name, '/'); - if(archpath_ptr) - this_sf->archpath_end = (intptr_t)archpath_ptr + 1 - (intptr_t)this_sf->name; - - if(temp_null) - *temp_null = temp_save; - - if(this_sf->archpath_end <= 0 || this_sf->archfile_end <= 0 || this_sf->archpath_end > this_sf->name_len || this_sf->archfile_end >= PATH_LIMIT) { - // ??? - this_sf->archpath_end = 0; - this_sf->archfile_end = 0; - } else { - this_sf->archname = strdup(filename); - if(!this_sf->archname) goto fail; - this_sf->archname_len = this_sf->name_len; - - // change from "(path)/(archive)|(filename)" to "(path)/(filename)" - this_sf->name[this_sf->archpath_end] = '\0'; - concatn(this_sf->name_len, this_sf->name, &this_sf->archname[this_sf->archfile_end]); - } - } - - /* cache file_size */ - if(infile) { - [infile seek:0 whence:SEEK_END]; - this_sf->file_size = [infile tell]; - [infile seek:0 whence:SEEK_SET]; - } else { - this_sf->file_size = 0; /* allow virtual, non-existing files */ - } - - /* Typically fseek(o)/ftell(o) may only handle up to ~2.14GB, signed 32b = 0x7FFFFFFF - * (happens in banks like FSB, though rarely). Should work if configured properly, log otherwise. */ - if(this_sf->file_size == 0xFFFFFFFF) { /* -1 on error */ - vgm_logi("STREAMFILE: file size too big (report)\n"); - goto fail; /* can be ignored but may result in strange/unexpected behaviors */ - } - - return &this_sf->vt; + return libsf; fail: - if(this_sf) { - if(this_sf->infile) - CFBridgingRelease(this_sf->infile); - free(this_sf->archname); - free(this_sf->name); - } - free(buf); - free(this_sf); + vfs_close(libsf); return NULL; } -static STREAMFILE* open_cog_streamfile_buffer_from_url(NSURL* url, const char* const filename, size_t bufsize) { - id infile; - STREAMFILE* sf = NULL; - - id audioSourceClass = NSClassFromString(@"AudioSource"); - infile = [audioSourceClass audioSourceForURL:url]; - - if(![infile open:url]) { - /* allow non-existing files in some cases */ - if(!vgmstream_is_virtual_filename(filename)) - return NULL; - } - - if(![infile seekable]) - return NULL; - - return open_cog_streamfile_buffer_by_file(infile, filename, bufsize); -} - -static STREAMFILE* open_cog_streamfile_buffer(const char* const filename, size_t bufsize) { - NSString* urlString = [NSString stringWithUTF8String:filename]; +libstreamfile_t* open_vfs(const char* path) { + NSString* urlString = [NSString stringWithUTF8String:path]; NSURL* url = [NSURL URLWithDataRepresentation:[urlString dataUsingEncoding:NSUTF8StringEncoding] relativeToURL:nil]; if([url fragment]) { @@ -319,32 +123,12 @@ static STREAMFILE* open_cog_streamfile_buffer(const char* const filename, size_t } } - return open_cog_streamfile_buffer_from_url(url, filename, bufsize); -} + id audioSourceClass = NSClassFromString(@"AudioSource"); + id infile = [audioSourceClass audioSourceForURL:url]; -STREAMFILE* open_cog_streamfile_from_url(NSURL* url) { - return open_cog_streamfile_buffer_from_url(url, [[[url absoluteString] stringByRemovingPercentEncoding] UTF8String], STREAMFILE_DEFAULT_BUFFER_SIZE); -} - -STREAMFILE* open_cog_streamfile(const char* filename) { - return open_cog_streamfile_buffer(filename, STREAMFILE_DEFAULT_BUFFER_SIZE); -} - -// STREAMFILE* open_cog_streamfile_by_file(id file, const char* filename) { -// return open_cog_streamfile_buffer_by_file(file, filename, STREAMFILE_DEFAULT_BUFFER_SIZE); -// } - -VGMSTREAM* init_vgmstream_from_cogfile(const char* path, int subsong) { - STREAMFILE* sf; - VGMSTREAM* vgm = NULL; - - sf = open_cog_streamfile(path); - - if(sf) { - sf->stream_index = subsong; - vgm = init_vgmstream_from_STREAMFILE(sf); - cogsf_close((COGSTREAMFILE*)sf); + if(![infile open:url]) { + return NULL; } - return vgm; + return open_vfs_by_cogsource(infile, path); } diff --git a/Plugins/vgmstream/vgmstream/VGMMetadataReader.m b/Plugins/vgmstream/vgmstream/VGMMetadataReader.m index cecdc46c3..79803538b 100644 --- a/Plugins/vgmstream/vgmstream/VGMMetadataReader.m +++ b/Plugins/vgmstream/vgmstream/VGMMetadataReader.m @@ -38,13 +38,26 @@ path = [path substringToIndex:fragmentRange.location]; } - VGMSTREAM *stream = init_vgmstream_from_cogfile([[path stringByRemovingPercentEncoding] UTF8String], track_num); + libvgmstream_config_t vcfg = { 0 }; + + vcfg.allow_play_forever = 1; + vcfg.play_forever = 0; + vcfg.loop_count = 2; + vcfg.fade_time = 10; + vcfg.fade_delay = 0; + vcfg.ignore_loop = 0; + + libstreamfile_t* sf = open_vfs([[path stringByRemovingPercentEncoding] UTF8String]); + if(!sf) + return nil; + libvgmstream_t* stream = libvgmstream_create(sf, track_num, &vcfg); + libstreamfile_close(sf); if(!stream) return nil; [sharedMyCache stuffURL:url stream:stream]; - close_vgmstream(stream); + libvgmstream_close_stream(stream); metadata = [sharedMyCache getMetadataForURL:url]; } diff --git a/Plugins/vgmstream/vgmstream/VGMPropertiesReader.m b/Plugins/vgmstream/vgmstream/VGMPropertiesReader.m index c337ead99..d84323270 100644 --- a/Plugins/vgmstream/vgmstream/VGMPropertiesReader.m +++ b/Plugins/vgmstream/vgmstream/VGMPropertiesReader.m @@ -40,13 +40,26 @@ path = [path substringToIndex:fragmentRange.location]; } - VGMSTREAM *stream = init_vgmstream_from_cogfile([[path stringByRemovingPercentEncoding] UTF8String], track_num); + libvgmstream_config_t vcfg = { 0 }; + + vcfg.allow_play_forever = 1; + vcfg.play_forever = 0; + vcfg.loop_count = 2; + vcfg.fade_time = 10; + vcfg.fade_delay = 0; + vcfg.ignore_loop = 0; + + libstreamfile_t* sf = open_vfs([[path stringByRemovingPercentEncoding] UTF8String]); + if(!sf) + return nil; + libvgmstream_t* stream = libvgmstream_create(sf, track_num, &vcfg); + libstreamfile_close(sf); if(!stream) return nil; [sharedMyCache stuffURL:url stream:stream]; - close_vgmstream(stream); + libvgmstream_close_stream(stream); properties = [sharedMyCache getPropertiesForURL:url]; }