#include "meta.h" #include "../coding/coding.h" #include #include static int get_loops_nwainfo_ini(STREAMFILE *sf, int *p_loop_flag, int32_t *p_loop_start); static int get_loops_gameexe_ini(STREAMFILE *sf, int *p_loop_flag, int32_t *p_loop_start, int32_t *p_loop_end); /* NWA - Visual Art's streams [Air (PC), Clannad (PC)] */ VGMSTREAM* init_vgmstream_nwa(STREAMFILE* sf) { VGMSTREAM* vgmstream = NULL; off_t start_offset; int channel_count, loop_flag = 0; int32_t loop_start_sample = 0, loop_end_sample = 0; int nwainfo_ini_found = 0, gameexe_ini_found = 0; int compression_level; /* checks */ if (!check_extensions(sf, "nwa")) goto fail; channel_count = read_16bitLE(0x00,sf); if (channel_count != 1 && channel_count != 2) goto fail; /* check if we're using raw pcm */ if ( read_32bitLE(0x08,sf)==-1 || /* compression level */ read_32bitLE(0x10,sf)==0 || /* block count */ read_32bitLE(0x18,sf)==0 || /* compressed data size */ read_32bitLE(0x20,sf)==0 || /* block size */ read_32bitLE(0x24,sf)==0 ) { /* restsize */ compression_level = -1; } else { compression_level = read_32bitLE(0x08,sf); } /* loop points come from external files */ nwainfo_ini_found = get_loops_nwainfo_ini(sf, &loop_flag, &loop_start_sample); gameexe_ini_found = !nwainfo_ini_found && get_loops_gameexe_ini(sf, &loop_flag, &loop_start_sample, &loop_end_sample); start_offset = 0x2c; /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; vgmstream->sample_rate = read_32bitLE(0x04,sf); vgmstream->num_samples = read_32bitLE(0x1c,sf) / channel_count; switch(compression_level) { case -1: switch (read_16bitLE(0x02,sf)) { case 8: vgmstream->coding_type = coding_PCM8; vgmstream->interleave_block_size = 0x01; break; case 16: vgmstream->coding_type = coding_PCM16LE; vgmstream->interleave_block_size = 0x02; break; default: goto fail; } vgmstream->layout_type = layout_interleave; break; case 0: case 1: case 2: case 3: case 4: case 5: vgmstream->coding_type = coding_NWA; vgmstream->layout_type = layout_none; vgmstream->codec_data = init_nwa(sf); if (!vgmstream->codec_data) goto fail; break; default: goto fail; break; } if (nwainfo_ini_found) { vgmstream->meta_type = meta_NWA_NWAINFOINI; if (loop_flag) { vgmstream->loop_start_sample = loop_start_sample; vgmstream->loop_end_sample = vgmstream->num_samples; } } else if (gameexe_ini_found) { vgmstream->meta_type = meta_NWA_GAMEEXEINI; if (loop_flag) { vgmstream->loop_start_sample = loop_start_sample; vgmstream->loop_end_sample = loop_end_sample; } } else { vgmstream->meta_type = meta_NWA; } if (!vgmstream_open_stream(vgmstream,sf,start_offset)) goto fail; return vgmstream; fail: close_vgmstream(vgmstream); return NULL; } /* try to locate NWAINFO.INI in the same directory */ static int get_loops_nwainfo_ini(STREAMFILE *sf, int *p_loop_flag, int32_t *p_loop_start) { STREAMFILE *sf_loop; char namebase[PATH_LIMIT]; const char * ext; int length; int found; off_t offset; size_t file_size; off_t found_off = -1; int loop_flag = 0; int32_t loop_start_sample = 0; sf_loop = open_streamfile_by_filename(sf, "NWAINFO.INI"); if (!sf_loop) goto fail; get_streamfile_filename(sf,namebase,PATH_LIMIT); /* ini found, try to find our name */ ext = filename_extension(namebase); length = ext - 1 - namebase; file_size = get_streamfile_size(sf_loop); for (found = 0, offset = 0; !found && offset < file_size; offset++) { off_t suboffset; /* Go for an n*m search 'cause it's easier than building an * FSA for the search string. Just wanted to make the point that * I'm not ignorant, just lazy. */ for (suboffset = offset; suboffset < file_size && suboffset-offset < length && read_8bit(suboffset,sf_loop) == namebase[suboffset-offset]; suboffset++) { /* skip */ } if (suboffset-offset==length && read_8bit(suboffset,sf_loop)==0x09) { /* tab */ found = 1; found_off = suboffset+1; } } /* if found file name in INI */ if (found) { char loopstring[9] = {0}; if (read_streamfile((uint8_t*)loopstring,found_off,8,sf_loop) == 8) { loop_start_sample = atol(loopstring); if (loop_start_sample > 0) loop_flag = 1; } } *p_loop_flag = loop_flag; *p_loop_start = loop_start_sample; close_streamfile(sf_loop); return 1; fail: close_streamfile(sf_loop); return 0; } /* try to locate Gameexe.ini in the same directory */ static int get_loops_gameexe_ini(STREAMFILE* sf, int* p_loop_flag, int32_t* p_loop_start, int32_t* p_loop_end) { STREAMFILE*sf_loop; char namebase[PATH_LIMIT]; const char* ext; int length; int found; off_t offset; off_t file_size; off_t found_off = -1; int loop_flag = 0; int32_t loop_start_sample = 0, loop_end_sample = 0; sf_loop = open_streamfile_by_filename(sf, "Gameexe.ini"); if (!sf_loop) goto fail; get_streamfile_filename(sf,namebase,PATH_LIMIT); /* ini found, try to find our name */ ext = filename_extension(namebase); length = ext-1-namebase; file_size = get_streamfile_size(sf_loop); /* According to the official documentation of RealLiveMax (the public version of RealLive), format of line is: * #DSTRACK = 00000000 - eeeeeeee - ssssssss = "filename" = "alias for game script" * ^22 ^33 ^45 ^57? * * Original text from the documentation (written in Japanese) is: * ; ■BGMの登録:DirectSound * ;(※必要ない場合は登録しないで下さい。) * ; 終了位置の設定が 99999999 なら最後まで演奏します。 * ; ※設定値はサンプル数で指定して下さい。(旧システムではバイト指定でしたので注意してください。) * ;========================================================================================================= * ; 開始位置 - 終了位置 - リピート = ファイル名 = 登録名 * #DSTRACK = 00000000 - 01896330 - 00088270 = "b_manuke" = "b_manuke" * #DSTRACK = 00000000 - 01918487 - 00132385 = "c_happy" = "c_happy" */ for (found = 0, offset = 0; !found && offset