diff --git a/src/Makefile b/src/Makefile index a7f08a26..d4f3f3ff 100644 --- a/src/Makefile +++ b/src/Makefile @@ -96,9 +96,10 @@ META_OBJS=meta/adx_header.o \ meta/dc_kcey.o \ meta/ps2_rstm.o \ meta/acm.o \ - meta/ps2_kces.o \ - meta/ps2_dxh.o \ - meta/ps2_psh.o + meta/mus_acm.o \ + meta/ps2_kces.o \ + meta/ps2_dxh.o \ + meta/ps2_psh.o OBJECTS=vgmstream.o streamfile.o util.o $(CODING_OBJS) $(LAYOUT_OBJS) $(META_OBJS) diff --git a/src/layout/mus_acm.c b/src/layout/mus_acm.c index 8e414dee..6e13519c 100644 --- a/src/layout/mus_acm.c +++ b/src/layout/mus_acm.c @@ -12,31 +12,31 @@ void render_vgmstream_mus_acm(sample * buffer, int32_t sample_count, VGMSTREAM * int samples_to_do; int samples_this_block = acm->total_values / acm->info.channels; -#if 0 if (vgmstream->loop_flag && vgmstream_do_loop(vgmstream)) { + data->current_file = data->loop_start_file; + acm_reset(data->files[data->current_file]); + vgmstream->samples_into_block = 0; continue; } -#endif samples_to_do = vgmstream_samples_to_do(samples_this_block, 1, vgmstream); + /*printf("samples_to_do=%d,samples_this_block=%d,samples_written=%d,sample_count=%d\n",samples_to_do,samples_this_block,samples_written,sample_count);*/ + if (samples_written+samples_to_do > sample_count) samples_to_do=sample_count-samples_written; if (samples_to_do == 0) { data->current_file++; + /*printf("next %d, %d samples\n",data->current_file,data->files[data->current_file]->total_values/data->files[data->current_file]->info.channels);*/ /* check for loop */ - if (vgmstream->loop_flag) { - if (data->current_file == data->loop_end_file) - data->current_file = data->loop_start_file; - } else { - /* */ - } acm_reset(data->files[data->current_file]); + vgmstream->samples_into_block = 0; continue; } + /*printf("decode %d samples file %d\n",samples_to_do,data->current_file);*/ decode_acm(acm, buffer+samples_written*vgmstream->channels, samples_to_do, vgmstream->channels); diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index c0a8220c..4a3c30ef 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -262,6 +262,10 @@ RelativePath=".\meta\ivb.c" > + + diff --git a/src/meta/Makefile.unix.am b/src/meta/Makefile.unix.am index 3e15f175..5bca9dd0 100644 --- a/src/meta/Makefile.unix.am +++ b/src/meta/Makefile.unix.am @@ -75,5 +75,6 @@ libmeta_la_SOURCES += acm.c libmeta_la_SOURCES += ps2_kces.c libmeta_la_SOURCES += ps2_dxh.c libmeta_la_SOURCES += ps2_psh.c +libmeta_la_SOURCES += mus_acm.c EXTRA_DIST = meta.h diff --git a/src/meta/meta.h b/src/meta/meta.h index 0d725343..a579b80a 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -163,4 +163,6 @@ VGMSTREAM * init_vgmstream_ps2_dxh(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ps2_psh(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_mus_acm(STREAMFILE * streamFile); + #endif diff --git a/src/meta/mus_acm.c b/src/meta/mus_acm.c new file mode 100644 index 00000000..d31598ab --- /dev/null +++ b/src/meta/mus_acm.c @@ -0,0 +1,349 @@ +#include "../vgmstream.h" +#include "meta.h" +#include "../util.h" +#include "../streamfile.h" +#include "../coding/acm_decoder.h" +#include +#include +#include + +#ifdef WIN32 +#define DIRSEP '\\' +#else +#define DIRSEP '/' +#endif + +#define NAME_LENGTH 260 + +int exists(char *filename, STREAMFILE *streamfile) { + STREAMFILE * temp = + streamfile->open(streamfile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); + if (!temp) return 0; + + close_streamfile(temp); + return 1; +} + +/* needs the name of a file in the directory to test, as all we can do reliably is attempt to open a file */ +int find_directory_name(char *name_base, char *dir_name, int subdir_name_size, char *subdir_name, char *name, char *file_name, STREAMFILE *streamfile) { + /* find directory name */ + { + char temp_dir_name[NAME_LENGTH]; + + subdir_name[0]='\0'; + concatn(subdir_name_size,subdir_name,name_base); + + if (strlen(subdir_name) >= subdir_name_size-2) goto fail; + subdir_name[strlen(subdir_name)+1]='\0'; + subdir_name[strlen(subdir_name)]=DIRSEP; + + temp_dir_name[0]='\0'; + concatn(sizeof(temp_dir_name),temp_dir_name,dir_name); + concatn(sizeof(temp_dir_name),temp_dir_name,subdir_name); + concatn(sizeof(temp_dir_name),temp_dir_name,name_base); + concatn(sizeof(temp_dir_name),temp_dir_name,name); + concatn(sizeof(temp_dir_name),temp_dir_name,".ACM"); + + if (!exists(temp_dir_name,streamfile)) { + int i; + /* try all lowercase */ + for (i=strlen(subdir_name)-1;i>=0;i--) { + subdir_name[i]=tolower(subdir_name[i]); + } + temp_dir_name[0]='\0'; + concatn(sizeof(temp_dir_name),temp_dir_name,dir_name); + concatn(sizeof(temp_dir_name),temp_dir_name,subdir_name); + concatn(sizeof(temp_dir_name),temp_dir_name,name_base); + concatn(sizeof(temp_dir_name),temp_dir_name,name); + concatn(sizeof(temp_dir_name),temp_dir_name,".ACM"); + + if (!exists(temp_dir_name,streamfile)) { + /* try first uppercase */ + subdir_name[0]=toupper(subdir_name[0]); + temp_dir_name[0]='\0'; + concatn(sizeof(temp_dir_name),temp_dir_name,dir_name); + concatn(sizeof(temp_dir_name),temp_dir_name,subdir_name); + concatn(sizeof(temp_dir_name),temp_dir_name,name_base); + concatn(sizeof(temp_dir_name),temp_dir_name,name); + concatn(sizeof(temp_dir_name),temp_dir_name,".ACM"); + if (!exists(temp_dir_name,streamfile)) { + /* try also 3rd uppercase */ + subdir_name[2]=toupper(subdir_name[2]); + temp_dir_name[0]='\0'; + concatn(sizeof(temp_dir_name),temp_dir_name,dir_name); + concatn(sizeof(temp_dir_name),temp_dir_name,subdir_name); + concatn(sizeof(temp_dir_name),temp_dir_name,name_base); + concatn(sizeof(temp_dir_name),temp_dir_name,name); + concatn(sizeof(temp_dir_name),temp_dir_name,".ACM"); + + if (!exists(temp_dir_name,streamfile)) { + /* ah well, disaster has befallen your party */ + goto fail; + } + } + } + } + } + + return 0; + +fail: + return 1; +} + +/* MUS playlist for InterPlay ACM */ +VGMSTREAM * init_vgmstream_mus_acm(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + ACMStream *acm_stream = NULL; + mus_acm_codec_data *data = NULL; + + char filename[NAME_LENGTH]; + char line_buffer[NAME_LENGTH]; + char * end_ptr; + char name_base[NAME_LENGTH]; + char (*names)[NAME_LENGTH] = NULL; + char dir_name[NAME_LENGTH]; + char subdir_name[NAME_LENGTH]; + + int i; + int loop_flag = 0; + int channel_count; + int file_count; + size_t line_bytes; + int whole_line_read = 0; + off_t mus_offset = 0; + + int loop_end_index = -1; + int loop_start_index = -1; + int32_t loop_start_samples = -1; + int32_t loop_end_samples = -1; + + int32_t total_samples = 0; + + /* check extension, case insensitive */ + streamFile->get_name(streamFile,filename,sizeof(filename)); + if (strcasecmp("mus",filename_extension(filename))) goto fail; + + /* read file name base */ + line_bytes = get_streamfile_dos_line(sizeof(line_buffer),line_buffer, + mus_offset, streamFile, &whole_line_read); + if (!whole_line_read) goto fail; + mus_offset += line_bytes; + memcpy(name_base,line_buffer,sizeof(name_base)); + + /* uppercase name_base */ + { + int i; + for (i=0;name_base[i];i++) name_base[i]=toupper(name_base[i]); + } + + /*printf("name base: %s\n",name_base);*/ + + /* read track entry count */ + line_bytes = get_streamfile_dos_line(sizeof(line_buffer),line_buffer, + mus_offset, streamFile, &whole_line_read); + if (!whole_line_read) goto fail; + if (line_buffer[0] == '\0') goto fail; + mus_offset += line_bytes; + file_count = strtol(line_buffer,&end_ptr,10); + /* didn't parse whole line as an integer (optional opening whitespace) */ + if (*end_ptr != '\0') goto fail; + + /*printf("entries: %d\n",file_count);*/ + + names = calloc(file_count,sizeof(names[0])); + if (!names) goto fail; + + dir_name[0]='\0'; + concatn(sizeof(dir_name),dir_name,filename); + + { + /* find directory name for the directory contianing the MUS */ + char * last_slash; + last_slash = strrchr(dir_name,DIRSEP); + if (last_slash != NULL) { + /* trim off the file name */ + last_slash[1]='\0'; + } else { + /* no dir name? annihilate! */ + dir_name[0] = '\0'; + } + } + + /* can't do this until we have a file name */ + subdir_name[0]='\0'; + + /* parse each entry */ + { + char name[NAME_LENGTH]; + char loop_name_base_temp[NAME_LENGTH]; + char loop_name_temp[NAME_LENGTH]; + char loop_name_base[NAME_LENGTH]; + char loop_name[NAME_LENGTH]; + for (i=0;ifiles = calloc(file_count,sizeof(ACMStream *)); + if (!data->files) { + free(data); data = NULL; + goto fail; + } + + /* open each file... */ + for (i=0;ifiles[i]=acm_stream; + + if (i==loop_start_index) loop_start_samples = total_samples; + if (i==loop_end_index) loop_end_samples = total_samples; + + total_samples += acm_stream->total_values / acm_stream->info.channels; + + if (i>0) { + if (acm_stream->info.channels != data->files[0]->info.channels || + acm_stream->info.rate != data->files[0]->info.rate) goto fail; + } + } + + if (i==loop_end_index) loop_end_samples = total_samples; + + channel_count = data->files[0]->info.channels; + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->channels = channel_count; + vgmstream->sample_rate = data->files[0]->info.rate; + vgmstream->coding_type = coding_ACM; + vgmstream->num_samples = total_samples; + vgmstream->loop_start_sample = loop_start_samples; + vgmstream->loop_end_sample = loop_end_samples; + vgmstream->layout_type = layout_mus_acm; + vgmstream->meta_type = meta_MUS_ACM; + + data->file_count = file_count; + data->current_file = 0; + data->loop_start_file = loop_start_index; + data->loop_end_file = loop_end_index; + /*data->end_file = -1;*/ + + vgmstream->codec_data = data; + + free(names); + + return vgmstream; + + /* clean up anything we may have opened */ +fail: + if (data) { + int i; + for (i=0;ifile_count;i++) { + if (data->files[i]) { + acm_close(data->files[i]); + data->files[i] = NULL; + } + } + } + if (names) free(names); + if (vgmstream) close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/streamfile.c b/src/streamfile.c index fc10cf41..51739551 100644 --- a/src/streamfile.c +++ b/src/streamfile.c @@ -202,3 +202,62 @@ STREAMFILE * open_stdio_streamfile_buffer(const char * const filename, size_t bu return streamFile; } + +/* Read a line into dst. The source files are MS-DOS style, + * separated (not terminated) by CRLF. Return 1 if the full line was + * retrieved (if it could fit in dst), 0 otherwise. In any case the result + * will be properly terminated. The CRLF will be removed if there is one. + * Return the number of bytes read (including CRLF line ending). Note that + * this is not the length of the string, and could be larger than the buffer. + * *line_done_ptr is set to 1 if the complete line was read into dst, + * otherwise it is set to 0. line_done_ptr can be NULL if you aren't + * interested in this info. + */ +size_t get_streamfile_dos_line(int dst_length, char * dst, off_t offset, + STREAMFILE * infile, int *line_done_ptr) +{ + int i; + off_t file_length = get_streamfile_size(infile); + /* how many bytes over those put in the buffer were read */ + int extra_bytes = 0; + + if (line_done_ptr) *line_done_ptr = 0; + + for (i=0;i