/* MTA2 in containers */ VGMSTREAM * init_vgmstream_mta2_container(STREAMFILE *streamFile) { VGMSTREAM *vgmstream = NULL; STREAMFILE *temp_streamFile = NULL; off_t subfile_offset; /* checks */ /* .dbm: iPod metadata + mta2 with KCEJ blocks, .bgm: mta2 with KCEJ blocks (fake?) */ if ( !check_extensions(streamFile,"dbm,bgm,mta2")) goto fail; if (read_32bitBE(0x00,streamFile) == 0x444C424D) { /* "DLBM" */ subfile_offset = 0x800; } else if (read_32bitBE(0x00,streamFile) == 0x00000010) { subfile_offset = 0x00; } else { goto fail; } /* subfile size is implicit in KCEJ blocks */ temp_streamFile = setup_mta2_streamfile(streamFile, subfile_offset, 1, "mta2"); if (!temp_streamFile) goto fail; vgmstream = init_vgmstream_mta2(temp_streamFile); close_streamfile(temp_streamFile); return vgmstream; fail: close_streamfile(temp_streamFile); close_vgmstream(vgmstream); return NULL; }
/* MIB+MIH - SCEE MultiStream interleaved bank (header+data) [namCollection: Ace Combat 2 (PS2), Rampage: Total Destruction (PS2)] */ VGMSTREAM * init_vgmstream_mib_mih(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; STREAMFILE * streamHeader = NULL; off_t start_offset; size_t data_size, frame_size, frame_last, frame_count; int channel_count, loop_flag; /* check extension */ if (!check_extensions(streamFile, "mib")) goto fail; streamHeader = open_streamfile_by_ext(streamFile,"mih"); if (!streamHeader) goto fail; if (read_32bitBE(0x00,streamHeader) != 0x40000000) /* header size */ goto fail; loop_flag = 0; /* MIB+MIH don't PS-ADPCM loop flags */ channel_count = read_32bitLE(0x08,streamHeader); start_offset = 0x00; /* 0x04: padding (0x20, MIH header must be multiple of 0x40) */ frame_last = (uint16_t)read_16bitLE(0x05,streamHeader); frame_size = read_32bitLE(0x10,streamHeader); frame_count = read_32bitLE(0x14,streamHeader); if (frame_count == 0) { /* rarely [Gladius (PS2)] */ frame_count = get_streamfile_size(streamFile) / (frame_size * channel_count); } data_size = frame_count * frame_size; data_size -= frame_last ? (frame_size-frame_last) : 0; /* last frame has less usable data */ data_size *= channel_count; /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; vgmstream->sample_rate = read_32bitLE(0x0C,streamHeader); vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count); vgmstream->meta_type = meta_PS2_MIB_MIH; vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = frame_size; if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; close_streamfile(streamHeader); return vgmstream; fail: close_streamfile(streamHeader); close_vgmstream(vgmstream); return NULL; }
VGMSTREAM * init_vgmstream_pos(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; STREAMFILE * streamFileWAV = NULL; char filename[1024]; char filenameWAV[1024]; int i; /* check extension, case insensitive */ streamFile->get_name(streamFile,filename,sizeof(filename)); if (strcasecmp("pos",filename_extension(filename))) goto fail; /* check for .WAV file */ strcpy(filenameWAV,filename); strcpy(filenameWAV+strlen(filenameWAV)-3,"wav"); streamFileWAV = streamFile->open(streamFile,filenameWAV,STREAMFILE_DEFAULT_BUFFER_SIZE); if (!streamFileWAV) { /* try again, ucase */ for (i=strlen(filenameWAV);i>=0&&filenameWAV[i]!=DIRSEP;i--) filenameWAV[i]=toupper(filenameWAV[i]); streamFileWAV = streamFile->open(streamFile,filenameWAV,STREAMFILE_DEFAULT_BUFFER_SIZE); if (!streamFileWAV) goto fail; } /* let the real initer do the parsing */ vgmstream = init_vgmstream_riff(streamFileWAV); if (!vgmstream) goto fail; close_streamfile(streamFileWAV); streamFileWAV = NULL; /* install loops */ if (!vgmstream->loop_flag) { vgmstream->loop_flag = 1; vgmstream->loop_ch = calloc(vgmstream->channels, sizeof(VGMSTREAMCHANNEL)); if (!vgmstream->loop_ch) goto fail; } vgmstream->loop_start_sample = read_32bitLE(0,streamFile); vgmstream->loop_end_sample = read_32bitLE(4,streamFile); vgmstream->meta_type = meta_RIFF_WAVE_POS; return vgmstream; /* clean up anything we may have opened */ fail: if (streamFileWAV) close_streamfile(streamFileWAV); if (vgmstream) close_vgmstream(vgmstream); return NULL; }
static STREAMFILE* setup_bgw_atrac3_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, size_t frame_size, int channels) { STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; bgw_decryption_data io_data = {0}; size_t io_data_size = sizeof(bgw_decryption_data); int ch; /* setup decryption with key (first frame + modified channel header) */ if (frame_size*channels == 0 || frame_size*channels > BGW_KEY_MAX) goto fail; io_data.key_size = read_streamfile(io_data.key, subfile_offset, frame_size*channels, streamFile); for (ch = 0; ch < channels; ch++) { uint32_t xor = get_32bitBE(io_data.key + frame_size*ch); put_32bitBE(io_data.key + frame_size*ch, xor ^ 0xA0024E9F); } /* setup subfile */ new_streamFile = open_wrap_streamfile(streamFile); if (!new_streamFile) goto fail; temp_streamFile = new_streamFile; new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size); if (!new_streamFile) goto fail; temp_streamFile = new_streamFile; new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, bgw_decryption_read,NULL); if (!new_streamFile) goto fail; temp_streamFile = new_streamFile; return temp_streamFile; fail: close_streamfile(temp_streamFile); return NULL; }
/* Prepares custom IO for custom Opus, that is converted to Ogg Opus on the fly */ static STREAMFILE* setup_opus_streamfile(STREAMFILE *streamFile, int channels, int skip, int sample_rate, off_t stream_offset, size_t stream_size, opus_type_t type) { STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; opus_io_data io_data = {0}; size_t io_data_size = sizeof(opus_io_data); io_data.type = type; io_data.stream_offset = stream_offset; io_data.stream_size = stream_size; io_data.physical_offset = stream_offset; io_data.head_size = make_oggs_first(io_data.head_buffer, sizeof(io_data.head_buffer), channels, skip, sample_rate); if (!io_data.head_size) goto fail; io_data.sequence = 2; io_data.logical_size = opus_io_size(streamFile, &io_data); /* force init */ /* setup subfile */ new_streamFile = open_wrap_streamfile(streamFile); if (!new_streamFile) goto fail; temp_streamFile = new_streamFile; new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, opus_io_read,opus_io_size); if (!new_streamFile) goto fail; temp_streamFile = new_streamFile; new_streamFile = open_buffer_streamfile(new_streamFile,0); if (!new_streamFile) goto fail; temp_streamFile = new_streamFile; return temp_streamFile; fail: close_streamfile(temp_streamFile); return NULL; }
static void multifile_close(MULTIFILE_STREAMFILE *streamfile) { int i; for (i = 0; i < streamfile->inner_sfs_size; i++) { for (i = 0; i < streamfile->inner_sfs_size; i++) { close_streamfile(streamfile->inner_sfs[i]); } } free(streamfile->inner_sfs); free(streamfile->sizes); free(streamfile); }
/* ADS in containers */ VGMSTREAM * init_vgmstream_ps2_ads_container(STREAMFILE *streamFile) { VGMSTREAM *vgmstream = NULL; STREAMFILE *temp_streamFile = NULL; off_t subfile_offset; size_t subfile_size; /* checks */ if (!check_extensions(streamFile, "ads")) goto fail; if (read_32bitBE(0x00,streamFile) == 0x41445343 && /* "ADSC" */ read_32bitBE(0x04,streamFile) == 0x01000000) { /* Kenka Bancho 2, Kamen Rider Hibiki/Kabuto, Shinjuku no Okami */ subfile_offset = 0x08; } else if (read_32bitBE(0x00,streamFile) == 0x63617669 && /* "cavi" */ read_32bitBE(0x04,streamFile) == 0x61207374 && /* "a st" */ read_32bitBE(0x08,streamFile) == 0x7265616D) { /* "ream" */ /* cavia games: Drakengard 1/2, Dragon Quest Yangus, GITS: Stand Alone Complex */ subfile_offset = 0x7d8; } else { goto fail; } subfile_size = get_streamfile_size(streamFile) - subfile_offset; temp_streamFile = setup_subfile_streamfile(streamFile, subfile_offset,subfile_size, NULL); if (!temp_streamFile) goto fail; vgmstream = init_vgmstream_ps2_ads(temp_streamFile); close_streamfile(temp_streamFile); return vgmstream; fail: close_streamfile(temp_streamFile); close_vgmstream(vgmstream); return NULL; }
/* multiple streams may share a name, also sometimes won't a the name */ static void get_stream_name(char * stream_name, STREAMFILE *streamFile, int target_stream) { STREAMFILE * streamInfo = NULL; int i, j, total_cues, num_cue = -1; size_t name_size = 0; off_t name_offset = 0x10; streamInfo = open_streamfile_by_ext(streamFile, "sob"); if (!streamInfo) goto end; if (read_32bitBE(0x00,streamInfo) != 0x43544632) /* "CTF2" */ goto end; total_cues = read_32bitLE(0x08,streamInfo); for (i = 0; i < total_cues; i++) { uint32_t flags, num_subsections, subsection_1_size, subsection_2_size; flags = (uint32_t)read_32bitLE(name_offset + 0x00,streamInfo); num_subsections = (uint32_t)read_32bitLE(name_offset + 0x20,streamInfo); subsection_1_size = (flags & 0x00000001) ? 0x40 : 0x00; subsection_1_size +=(flags & 0x00000040) ? 0x20 : 0x00; subsection_2_size = (flags & 0x00000100) ? 0x1c : 0x10; for (j = 0; j < num_subsections; j++) { int num_stream = read_32bitLE(name_offset + 0x2c + subsection_1_size + j*subsection_2_size + 0x08,streamInfo); if (target_stream-1 == num_stream) num_cue = i; } name_offset += 0x2c + subsection_1_size + subsection_2_size * num_subsections; } if (num_cue < 0) goto end; for (i = 0; i < total_cues; i++) { /* 0x00: id */ name_size = read_32bitLE(name_offset + 0x04,streamInfo); /* non null-terminated */ if (i == num_cue) { name_offset += 0x08; break; } name_offset += 0x08 + name_size; } if (name_size > STREAM_NAME_SIZE-1) name_size = STREAM_NAME_SIZE-1; read_string(stream_name,name_size+1, name_offset,streamInfo); end: if (streamInfo) close_streamfile(streamInfo); }
/** * open file containing looping data and copy to buffer * * returns true if found and copied */ int read_pos_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile) { char posname[PATH_LIMIT]; char filename[PATH_LIMIT]; /*size_t bytes_read;*/ STREAMFILE * streamFilePos= NULL; streamFile->get_name(streamFile,filename,sizeof(filename)); if (strlen(filename)+4 > sizeof(posname)) goto fail; /* try to open a posfile using variations: "(name.ext).pos" */ { strcpy(posname, filename); strcat(posname, ".pos"); streamFilePos = streamFile->open(streamFile,posname,STREAMFILE_DEFAULT_BUFFER_SIZE); if (streamFilePos) goto found; goto fail; } found: //if (get_streamfile_size(streamFilePos) != bufsize) goto fail; /* allow pos files to be of different sizes in case of new features, just fill all we can */ memset(buf, 0, bufsize); read_streamfile(buf, 0, bufsize, streamFilePos); close_streamfile(streamFilePos); return 1; fail: if (streamFilePos) close_streamfile(streamFilePos); return 0; }
static STREAMFILE *multifile_open(MULTIFILE_STREAMFILE *streamfile, const char * const filename, size_t buffersize) { char original_filename[PATH_LIMIT]; STREAMFILE *new_sf = NULL; STREAMFILE **new_inner_sfs = NULL; int i; streamfile->inner_sfs[0]->get_name(streamfile->inner_sfs[0], original_filename, PATH_LIMIT); /* detect re-opening the file */ if (strcmp(filename, original_filename) == 0) { /* same multifile */ new_inner_sfs = calloc(streamfile->inner_sfs_size, sizeof(STREAMFILE*)); if (!new_inner_sfs) goto fail; for (i = 0; i < streamfile->inner_sfs_size; i++) { streamfile->inner_sfs[i]->get_name(streamfile->inner_sfs[i], original_filename, PATH_LIMIT); new_inner_sfs[i] = streamfile->inner_sfs[i]->open(streamfile->inner_sfs[i], original_filename, buffersize); if (!new_inner_sfs[i]) goto fail; } new_sf = open_multifile_streamfile(new_inner_sfs, streamfile->inner_sfs_size); if (!new_sf) goto fail; return new_sf; } else { return streamfile->inner_sfs[0]->open(streamfile->inner_sfs[0], filename, buffersize); /* regular file */ } fail: if (new_inner_sfs) { for (i = 0; i < streamfile->inner_sfs_size; i++) close_streamfile(new_inner_sfs[i]); } free(new_inner_sfs); return NULL; }
/* SXD - Sony/SCE's SNDX lib format (cousin of SGXD) [Gravity Rush, Freedom Wars, Soul Sacrifice PSV] */ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; STREAMFILE * streamHeader = NULL, *streamData = NULL; off_t start_offset, chunk_offset, first_offset = 0x60, name_offset = 0; size_t chunk_size, stream_size = 0; int is_dual = 0, is_external = 0; int loop_flag, channels, codec, flags; int sample_rate, num_samples, loop_start_sample, loop_end_sample; uint32_t at9_config_data = 0; int total_subsongs, target_subsong = streamFile->stream_index; /* check extension, case insensitive */ /* .sxd: header+data (SXDF), .sxd1: header (SXDF) + .sxd2 = data (SXDS) */ if (!check_extensions(streamFile,"sxd,sxd2")) goto fail; is_dual = !check_extensions(streamFile,"sxd"); /* sxd1+sxd2: use sxd1 as header; otherwise use the current file as header */ if (is_dual) { if (read_32bitBE(0x00,streamFile) != 0x53584453) /* "SXDS" */ goto fail; streamHeader = open_streamfile_by_ext(streamFile, "sxd1"); if (!streamHeader) goto fail; } else { streamHeader = streamFile; } if (read_32bitBE(0x00,streamHeader) != 0x53584446) /* "SXDF" */ goto fail; /* typical chunks: NAME, WAVE and many control chunks (SXDs don't need to contain any sound data) */ if (!find_chunk_le(streamHeader, 0x57415645,first_offset,0, &chunk_offset,&chunk_size)) /* "WAVE" */ goto fail; /* check multi-streams (usually only in SFX containers) */ total_subsongs = read_32bitLE(chunk_offset+0x04,streamHeader); if (target_subsong == 0) target_subsong = 1; if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; // todo rarely a WAVE subsong may point to a repeated data offset, with different tags only /* read stream header */ { off_t table_offset, header_offset, stream_offset; /* get target offset using table of relative offsets within WAVE */ table_offset = chunk_offset + 0x08 + 4*(target_subsong-1); header_offset = table_offset + read_32bitLE(table_offset,streamHeader); flags = read_32bitLE(header_offset+0x00,streamHeader); codec = read_8bit (header_offset+0x04,streamHeader); channels = read_8bit (header_offset+0x05,streamHeader); /* 0x06(2): unknown, rarely 0xFF */ sample_rate = read_32bitLE(header_offset+0x08,streamHeader); /* 0x0c(4): unknown size? (0x4000/0x3999/0x3333/etc, not related to extra data) */ /* 0x10(4): ? + volume? + pan? (can be 0 for music) */ num_samples = read_32bitLE(header_offset+0x14,streamHeader); loop_start_sample = read_32bitLE(header_offset+0x18,streamHeader); loop_end_sample = read_32bitLE(header_offset+0x1c,streamHeader); stream_size = read_32bitLE(header_offset+0x20,streamHeader); stream_offset = read_32bitLE(header_offset+0x24,streamHeader); loop_flag = loop_start_sample != -1 && loop_end_sample != -1; /* known flag combos: * 0x00: Chaos Rings 2 sfx (RAM + no tags) * 0x01: common (RAM + tags) * 0x02: Chaos Rings 3 sfx (stream + no tags) * 0x03: common (stream + tags) * 0x05: Gravity Rush 2 sfx (RAM + tags) */ //has_tags = flags & 1; is_external = flags & 2; //unknown = flags & 4; /* no apparent differences with/without it? */ /* flag 1 signals TLV-like extra data. Format appears to be 0x00(1)=tag?, 0x01(1)=extra size*32b?, 0x02(2)=config? * but not always (ex. size=0x07 but actually=0), perhaps only some bits are used or varies with tag, or are subflags. * A tag may appear with or without extra data (even 0x0a), 0x01/03/04/06 are common at the beginnig (imply number of tags?), * 0x64/7F are common at the end (but not always), 0x0A=ATRAC9 config, 0x0B/0C appear with RAM preloading data * (most variable in Soul Sacrifice; total TLVs size isn't plainly found in the SXD header AFAIK). */ /* manually try to find ATRAC9 tag */ if (codec == 0x42) { off_t extra_offset = header_offset+0x28; off_t max_offset = chunk_offset + chunk_size; if (!(flags & 1)) goto fail; while (extra_offset < max_offset) { uint32_t tag = read_32bitBE(extra_offset, streamHeader); if (tag == 0x0A010000 || tag == 0x0A010600) { at9_config_data = read_32bitLE(extra_offset+0x04,streamHeader); /* yes, LE */ break; } extra_offset += 0x04; } if (!at9_config_data) goto fail; } /* usually .sxd=header+data and .sxd1=header + .sxd2=data, but rarely sxd1 may contain data [The Last Guardian (PS4)] */ if (is_external) { start_offset = stream_offset; /* absolute if external */ } else { start_offset = header_offset+0x24 + stream_offset; /* from current entry offset if internal */ } } /* get stream name (NAME is tied to REQD/cues, and SFX cues repeat WAVEs, but should work ok for streams) */ if (is_dual && find_chunk_le(streamHeader, 0x4E414D45,first_offset,0, &chunk_offset,NULL)) { /* "NAME" */ /* table: relative offset (32b) + hash? (32b) + cue index (32b) */ int i; int num_entries = read_16bitLE(chunk_offset+0x04,streamHeader); /* can be bigger than streams */ for (i = 0; i < num_entries; i++) { uint32_t index = (uint32_t)read_32bitLE(chunk_offset+0x08 + 0x08 + i*0x0c,streamHeader); if (index+1 == target_subsong) { name_offset = chunk_offset+0x08 + 0x00 + i*0x0c + read_32bitLE(chunk_offset+0x08 + 0x00 + i*0x0c,streamHeader); break; } } } if (is_external && !is_dual) { VGM_LOG("SXD: found single sxd with external data\n"); goto fail; } if (is_external) { streamData = streamFile; } else { streamData = streamHeader; } if (start_offset > get_streamfile_size(streamData)) { VGM_LOG("SXD: wrong location?\n"); goto fail; } /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channels,loop_flag); if (!vgmstream) goto fail; vgmstream->sample_rate = sample_rate; vgmstream->num_samples = num_samples; vgmstream->loop_start_sample = loop_start_sample; vgmstream->loop_end_sample = loop_end_sample; vgmstream->num_streams = total_subsongs; vgmstream->stream_size = stream_size; vgmstream->meta_type = meta_SXD; if (name_offset) read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader); switch (codec) { case 0x20: /* PS-ADPCM [Hot Shots Golf: World Invitational (Vita) sfx] */ vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x10; break; case 0x21: /* HEVAG [Gravity Rush (Vita) sfx] */ vgmstream->coding_type = coding_HEVAG; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x10; break; #ifdef VGM_USE_ATRAC9 case 0x42: { /* ATRAC9 [Soul Sacrifice (Vita), Freedom Wars (Vita), Gravity Rush 2 (PS4)] */ atrac9_config cfg = {0}; cfg.channels = vgmstream->channels; cfg.config_data = at9_config_data; vgmstream->codec_data = init_atrac9(&cfg); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_ATRAC9; vgmstream->layout_type = layout_none; break; } #endif //case 0x28: /* dummy codec? (found with 0 samples) [Hot Shots Golf: World Invitational (Vita) sfx] */ default: VGM_LOG("SXD: unknown codec 0x%x\n", codec); goto fail; } /* open the file for reading */ if (!vgmstream_open_stream(vgmstream,streamData,start_offset)) goto fail; if (is_dual) close_streamfile(streamHeader); return vgmstream; fail: if (is_dual) close_streamfile(streamHeader); close_vgmstream(vgmstream); return NULL; }
VGMSTREAM * init_vgmstream_ish_isd(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; STREAMFILE * streamFileISH = NULL; char filename[260]; char filenameISH[260]; int i; int channel_count; int loop_flag; /* check extension, case insensitive */ streamFile->get_name(streamFile,filename,sizeof(filename)); if (strcasecmp("isd",filename_extension(filename))) goto fail; strcpy(filenameISH,filename); strcpy(filenameISH+strlen(filenameISH)-3,"ish"); streamFileISH = streamFile->open(streamFile,filenameISH,STREAMFILE_DEFAULT_BUFFER_SIZE); if (!streamFileISH) goto fail; /* check header */ if (read_32bitBE(0x00,streamFileISH) != 0x495F5346) /* "I_SF" */ goto fail; channel_count = read_32bitBE(0x14,streamFileISH); loop_flag = (read_32bitBE(0x1C,streamFileISH) !=0); /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; /* fill in the vital statistics */ vgmstream->channels = channel_count; vgmstream->sample_rate = read_32bitBE(0x08,streamFileISH); vgmstream->num_samples=read_32bitBE(0x0C,streamFileISH); vgmstream->coding_type = coding_NGC_DSP; if(loop_flag) { vgmstream->loop_start_sample = read_32bitBE(0x20,streamFileISH)*14/8/channel_count; vgmstream->loop_end_sample = read_32bitBE(0x24,streamFileISH)*14/8/channel_count; } if (channel_count == 1) { vgmstream->layout_type = layout_none; } else if (channel_count == 2) { vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = read_32bitBE(0x18,streamFileISH); } vgmstream->meta_type = meta_ISH_ISD; /* open the file for reading by each channel */ { for (i=0;i<channel_count;i++) { vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,vgmstream->interleave_block_size); if (!vgmstream->ch[i].streamfile) goto fail; vgmstream->ch[i].channel_start_offset= vgmstream->ch[i].offset=i*vgmstream->interleave_block_size; } } if (vgmstream->coding_type == coding_NGC_DSP) { int i; for (i=0;i<16;i++) { vgmstream->ch[0].adpcm_coef[i] = read_16bitBE(0x40+i*2,streamFileISH); } if (vgmstream->channels == 2) { for (i=0;i<16;i++) { vgmstream->ch[1].adpcm_coef[i] = read_16bitBE(0x80+i*2,streamFileISH); } } } close_streamfile(streamFileISH); streamFileISH=NULL; return vgmstream; /* clean up anything we may have opened */ fail: if (streamFileISH) close_streamfile(streamFileISH); if (vgmstream) close_vgmstream(vgmstream); return NULL; }
VGMSTREAM * init_vgmstream_ps2_str(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; STREAMFILE * infileSTH = NULL; char filename[260]; char * filenameSTH = NULL; int i, channel_count, loop_flag; /* check extension, case insensitive */ streamFile->get_name(streamFile,filename,sizeof(filename)); if (strcasecmp("str",filename_extension(filename))) goto fail; /* check for .MIH file */ filenameSTH=(char *)malloc(strlen(filename)+1); if (!filenameSTH) goto fail; strcpy(filenameSTH,filename); strcpy(filenameSTH+strlen(filenameSTH)-3,"STH"); infileSTH = streamFile->open(streamFile,filenameSTH,STREAMFILE_DEFAULT_BUFFER_SIZE); /* STH File is necessary, so we can't confuse those file */ /* with others .STR file as it is a very common extension */ if (!infileSTH) goto fail; if(read_32bitLE(0x2C,infileSTH)==0) goto fail; if((read_32bitLE(0x2C,infileSTH)==0x07) || (read_32bitLE(0x2C,infileSTH)==0x06)) channel_count=2; if(read_32bitLE(0x2C,infileSTH)==0x05) channel_count=1; loop_flag = read_32bitLE(0x2C,infileSTH) & 0x01; /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; /* fill in the vital statistics */ vgmstream->channels = channel_count; vgmstream->sample_rate = read_32bitLE(0x24,infileSTH); vgmstream->interleave_block_size=0x4000; if(read_32bitLE(0x40,infileSTH)==0x01) vgmstream->interleave_block_size = 0x8000; vgmstream->num_samples=read_32bitLE(0x20,infileSTH); vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_interleave; vgmstream->meta_type = meta_PS2_STR; if(loop_flag) { vgmstream->loop_start_sample = 0; vgmstream->loop_end_sample = read_32bitLE(0x20,infileSTH); } close_streamfile(infileSTH); infileSTH=NULL; /* open the file for reading by each channel */ { for (i=0;i<channel_count;i++) { vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,vgmstream->interleave_block_size); if (!vgmstream->ch[i].streamfile) goto fail; vgmstream->ch[i].channel_start_offset= vgmstream->ch[i].offset+=(off_t)(vgmstream->interleave_block_size*i); } } return vgmstream; /* clean up anything we may have opened */ fail: if (infileSTH) close_streamfile(infileSTH); if (filenameSTH) {free(filenameSTH); filenameSTH=NULL;} if (vgmstream) close_vgmstream(vgmstream); return NULL; }
/** * open file containing decryption keys and copy to buffer * tries combinations of keynames based on the original filename * * returns true if found and copied */ int read_key_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile) { char keyname[PATH_LIMIT]; char filename[PATH_LIMIT]; const char *path, *ext; STREAMFILE * streamFileKey = NULL; streamFile->get_name(streamFile,filename,sizeof(filename)); if (strlen(filename)+4 > sizeof(keyname)) goto fail; /* try to open a keyfile using variations: * "(name.ext)key" (per song), "(.ext)key" (per folder) */ { ext = strrchr(filename,'.'); if (ext!=NULL) ext = ext+1; path = strrchr(filename,DIR_SEPARATOR); if (path!=NULL) path = path+1; /* "(name.ext)key" */ strcpy(keyname, filename); strcat(keyname, "key"); streamFileKey = streamFile->open(streamFile,keyname,STREAMFILE_DEFAULT_BUFFER_SIZE); if (streamFileKey) goto found; /* "(name.ext)KEY" */ /* strcpy(keyname+strlen(keyname)-3,"KEY"); streamFileKey = streamFile->open(streamFile,keyname,STREAMFILE_DEFAULT_BUFFER_SIZE); if (streamFileKey) goto found; */ /* "(.ext)key" */ if (path) { strcpy(keyname, filename); keyname[path-filename] = '\0'; strcat(keyname, "."); } else { strcpy(keyname, "."); } if (ext) strcat(keyname, ext); strcat(keyname, "key"); streamFileKey = streamFile->open(streamFile,keyname,STREAMFILE_DEFAULT_BUFFER_SIZE); if (streamFileKey) goto found; /* "(.ext)KEY" */ /* strcpy(keyname+strlen(keyname)-3,"KEY"); streamFileKey = streamFile->open(streamFile,keyname,STREAMFILE_DEFAULT_BUFFER_SIZE); if (streamFileKey) goto found; */ goto fail; } found: if (get_streamfile_size(streamFileKey) != bufsize) goto fail; if (read_streamfile(buf, 0, bufsize, streamFileKey)!=bufsize) goto fail; close_streamfile(streamFileKey); return 1; fail: if (streamFileKey) close_streamfile(streamFileKey); return 0; }
/* SCD - Square-Enix games (FF XIII, XIV) */ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset, tables_offset, meta_offset, extradata_offset, name_offset = 0; int32_t stream_size, extradata_size, loop_start, loop_end; int loop_flag = 0, channel_count, codec, sample_rate; int version, target_entry, aux_chunk_count; int total_subsongs, target_subsong = streamFile->stream_index; int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; /* check extension, case insensitive */ if ( !check_extensions(streamFile, "scd") ) goto fail; /** main header **/ if (read_32bitBE(0x00,streamFile) != 0x53454442 && /* "SEDB" */ read_32bitBE(0x04,streamFile) != 0x53534346) /* "SSCF" */ goto fail; if (read_8bit(0x0c,streamFile) == 0x01) { /* big endian flag */ //size_offset = 0x14; read_32bit = read_32bitBE; read_16bit = read_16bitBE; } else { //size_offset = 0x10; read_32bit = read_32bitLE; read_16bit = read_16bitLE; } /* SSCF version? (older SSCFs from Crisis Core/FFXI X360 seem to be V3/2) */ if (read_8bit(0x0d,streamFile) != 0x04) goto fail; /* v2: FFXIII demo (PS3), FFT0 test files (PC); v3: common; v4: Kingdom Hearts 2.8 (PS4) */ version = read_32bit(0x08,streamFile); if (version != 2 && version != 3 && version != 4) goto fail; tables_offset = read_16bit(0x0e,streamFile); /* usually 0x30 or 0x20 */ #if 0 /* never mind, FFXIII music_68tak.ps3.scd is 0x80 shorter */ /* check file size with header value */ if (read_32bit(size_offset,streamFile) != get_streamfile_size(streamFile)) goto fail; #endif /** offset tables **/ /* 0x00(2): table1/4 (unknown) entries */ /* 0x02(2): table2 (unknown) entries */ /* 0x04(2): table3 (headers) entries */ /* 0x06(2): unknown, varies even for clone files */ /* (implicit: table1 starts at 0x20) */ /* 0x08: table2 (unknown) start offset */ /* 0x0c: table3 (headers) start offset */ /* 0x10: table4 (unknown) start offset */ /* 0x14: always null? */ /* 0x18: table5? (unknown) start offset? */ /* 0x1c: unknown, often null */ /* each table entry is an uint32_t offset; after entries there is padding */ /* if a table isn't present entries is 0 and offset points to next table */ /* find meta_offset in table3 (headers) and total subsongs */ { int i; int headers_entries = read_16bit(tables_offset+0x04,streamFile); off_t headers_offset = read_32bit(tables_offset+0x0c,streamFile); if (target_subsong == 0) target_subsong = 1; total_subsongs = 0; meta_offset = 0; /* manually find subsongs as entries can be dummy (ex. sfx banks in FF XIV or FF Type-0) */ for (i = 0; i < headers_entries; i++) { off_t entry_offset = read_32bit(headers_offset + i*0x04,streamFile); if (read_32bit(entry_offset+0x0c,streamFile) == -1) continue; /* codec -1 when dummy */ total_subsongs++; if (!meta_offset && total_subsongs == target_subsong) { meta_offset = entry_offset; target_entry = i; } } if (meta_offset == 0) goto fail; /* SCD can contain 0 entries too */ } /** stream header **/ stream_size = read_32bit(meta_offset+0x00,streamFile); channel_count = read_32bit(meta_offset+0x04,streamFile); sample_rate = read_32bit(meta_offset+0x08,streamFile); codec = read_32bit(meta_offset+0x0c,streamFile); loop_start = read_32bit(meta_offset+0x10,streamFile); loop_end = read_32bit(meta_offset+0x14,streamFile); extradata_size = read_32bit(meta_offset+0x18,streamFile); aux_chunk_count = read_32bit(meta_offset+0x1c,streamFile); /* 0x01e(2): unknown, seen in some FF XIV sfx (MSADPCM) */ loop_flag = (loop_end > 0); extradata_offset = meta_offset + 0x20; start_offset = extradata_offset + extradata_size; /* only "MARK" chunk is known (some FF XIV PS3 have "STBL" but it's not counted) */ if (aux_chunk_count > 1 && aux_chunk_count < 0xFFFF) { /* some FF XIV Heavensward IMA sfx have 0x01000000 */ VGM_LOG("SCD: unknown aux chunk count %i\n", aux_chunk_count); goto fail; } /* skips aux chunks, sometimes needed (Lightning Returns X360, FF XIV PC) */ if (aux_chunk_count && read_32bitBE(extradata_offset, streamFile) == 0x4D41524B) { /* "MARK" */ extradata_offset += read_32bit(extradata_offset+0x04, streamFile); } /* find name if possible */ if (version == 4) { int info_entries = read_16bit(tables_offset+0x00,streamFile); int headers_entries = read_16bit(tables_offset+0x04,streamFile); off_t info_offset = tables_offset+0x20; /* not very exact as table1 and table3 entries may differ in V3, not sure about V4 */ if (info_entries == headers_entries) { off_t entry_offset = read_16bit(info_offset + 0x04*target_entry,streamFile); name_offset = entry_offset+0x30; } } #ifdef VGM_USE_VORBIS /* special case using init_vgmstream_ogg_vorbis */ if (codec == 0x06) { VGMSTREAM *ogg_vgmstream; uint8_t ogg_version, ogg_byte; ogg_vorbis_meta_info_t ovmi = {0}; ovmi.meta_type = meta_SQEX_SCD; ovmi.total_subsongs = total_subsongs; /* loop values are in bytes, let init_vgmstream_ogg_vorbis find loop comments instead */ ogg_version = read_8bit(extradata_offset + 0x00, streamFile); /* 0x01(1): 0x20 in v2/3, this ogg miniheader size? */ ogg_byte = read_8bit(extradata_offset + 0x02, streamFile); /* 0x03(1): ? in v3 */ if (ogg_version == 0) { /* 0x10? header, then custom Vorbis header before regular Ogg (FF XIV PC v1) */ ovmi.stream_size = stream_size; } else { /* 0x20 header, then seek table */ size_t seek_table_size = read_32bit(extradata_offset+0x10, streamFile); size_t vorb_header_size = read_32bit(extradata_offset+0x14, streamFile); /* 0x18(4): ? (can be 0) */ if ((extradata_offset-meta_offset) + seek_table_size + vorb_header_size != extradata_size) goto fail; ovmi.stream_size = vorb_header_size + stream_size; start_offset = extradata_offset + 0x20 + seek_table_size; /* extradata_size skips vorb_header */ if (ogg_version == 2) { /* header is XOR'ed using byte (FF XIV PC) */ ovmi.decryption_callback = scd_ogg_v2_decryption_callback; ovmi.scd_xor = ogg_byte; ovmi.scd_xor_length = vorb_header_size; } else if (ogg_version == 3) { /* file is XOR'ed using table (FF XIV Heavensward PC) */ ovmi.decryption_callback = scd_ogg_v3_decryption_callback; ovmi.scd_xor = stream_size & 0xFF; /* ogg_byte not used? */ ovmi.scd_xor_length = vorb_header_size + stream_size; } else { VGM_LOG("SCD: unknown ogg_version 0x%x\n", ogg_version); } } /* actual Ogg init */ ogg_vgmstream = init_vgmstream_ogg_vorbis_callbacks(streamFile, NULL, start_offset, &ovmi); if (ogg_vgmstream && name_offset) read_string(ogg_vgmstream->stream_name, PATH_LIMIT, name_offset, streamFile); return ogg_vgmstream; } #endif /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; vgmstream->sample_rate = sample_rate; vgmstream->num_streams = total_subsongs; vgmstream->stream_size = stream_size; vgmstream->meta_type = meta_SQEX_SCD; if (name_offset) read_string(vgmstream->stream_name, PATH_LIMIT, name_offset, streamFile); switch (codec) { case 0x01: /* PCM */ vgmstream->coding_type = coding_PCM16LE; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x02; vgmstream->num_samples = pcm_bytes_to_samples(stream_size, channel_count, 16); if (loop_flag) { vgmstream->loop_start_sample = pcm_bytes_to_samples(loop_start, channel_count, 16); vgmstream->loop_end_sample = pcm_bytes_to_samples(loop_end, channel_count, 16); } break; case 0x03: /* PS-ADPCM [Final Fantasy Type-0] */ vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x10; vgmstream->num_samples = ps_bytes_to_samples(stream_size, channel_count); if (loop_flag) { vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, channel_count); vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end, channel_count); } break; case 0x06: /* OGG [Final Fantasy XIII-2 (PC), Final Fantasy XIV (PC)] */ goto fail; /* handled above */ #ifdef VGM_USE_MPEG case 0x07: { /* MPEG [Final Fantasy XIII (PS3)] */ mpeg_codec_data *mpeg_data = NULL; mpeg_custom_config cfg = {0}; cfg.interleave = 0x800; /* for multistream [Final Fantasy XIII-2 (PS3)], otherwise ignored */ cfg.data_size = stream_size; mpeg_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_SCD, &cfg); if (!mpeg_data) goto fail; vgmstream->codec_data = mpeg_data; vgmstream->layout_type = layout_none; /* some Drakengard 3, Kingdom Hearts HD have adjusted sample rate (47999, 44099), for looping? */ vgmstream->num_samples = mpeg_bytes_to_samples(stream_size, mpeg_data); vgmstream->loop_start_sample = mpeg_bytes_to_samples(loop_start, mpeg_data); vgmstream->loop_end_sample = mpeg_bytes_to_samples(loop_end, mpeg_data); /* somehow loops offsets aren't always frame-aligned, and the code below supposedly helped, * but there isn't much difference since MPEG loops are rough (1152-aligned). Seems it * would help more loop_start - ~1000, loop_end + ~1000 (ex. FFXIII-2 music_SunMizu.ps3.scd) */ //vgmstream->num_samples -= vgmstream->num_samples % 576; //vgmstream->loop_start_sample -= vgmstream->loop_start_sample % 576; //vgmstream->loop_end_sample -= vgmstream->loop_end_sample % 576; break; } #endif case 0x0C: /* MS ADPCM [Final Fantasy XIV (PC) sfx] */ vgmstream->coding_type = coding_MSADPCM; vgmstream->layout_type = layout_none; vgmstream->interleave_block_size = read_16bit(extradata_offset+0x0c,streamFile); /* in extradata_offset is a WAVEFORMATEX (including coefs and all) */ vgmstream->num_samples = msadpcm_bytes_to_samples(stream_size, vgmstream->interleave_block_size, vgmstream->channels); if (loop_flag) { vgmstream->loop_start_sample = msadpcm_bytes_to_samples(loop_start, vgmstream->interleave_block_size, vgmstream->channels); vgmstream->loop_end_sample = msadpcm_bytes_to_samples(loop_end, vgmstream->interleave_block_size, vgmstream->channels); } break; case 0x0A: /* DSP ADPCM [Dragon Quest X (Wii)] */ case 0x15: { /* DSP ADPCM [Dragon Quest X (Wii U)] (no apparent differences except higher sample rate) */ const off_t interleave_size = 0x800; const off_t stride_size = interleave_size * channel_count; int i; size_t total_size; layered_layout_data * data = NULL; /* interleaved DSPs including the header (so the first 0x800 is 0x60 header + 0x740 data) * so interleave layout can't used; we'll setup de-interleaving streamfiles as layers/channels instead */ //todo this could be simplified using a block layout or adding interleave_first_block vgmstream->coding_type = coding_NGC_DSP; vgmstream->layout_type = layout_layered; /* read from the first DSP header and verify other channel headers */ { total_size = (read_32bitBE(start_offset+0x04,streamFile)+1)/2; /* rounded nibbles / 2 */ vgmstream->num_samples = read_32bitBE(start_offset+0x00,streamFile); if (loop_flag) { vgmstream->loop_start_sample = loop_start; vgmstream->loop_end_sample = loop_end+1; } for (i = 1; i < channel_count; i++) { if ((read_32bitBE(start_offset+4,streamFile)+1)/2 != total_size || read_32bitBE(start_offset+interleave_size*i+0x00,streamFile) != vgmstream->num_samples) { goto fail; } } } /* init layout */ data = init_layout_layered(channel_count); if (!data) goto fail; vgmstream->layout_data = data; /* open each layer subfile */ for (i = 0; i < channel_count; i++) { STREAMFILE* temp_streamFile = setup_scd_dsp_streamfile(streamFile, start_offset+interleave_size*i, interleave_size, stride_size, total_size); if (!temp_streamFile) goto fail; data->layers[i] = init_vgmstream_ngc_dsp_std(temp_streamFile); close_streamfile(temp_streamFile); if (!data->layers[i]) goto fail; } /* setup layered VGMSTREAMs */ if (!setup_layout_layered(data)) goto fail; break; } #ifdef VGM_USE_FFMPEG case 0x0B: { /* XMA2 [Final Fantasy (X360), Lightning Returns (X360) sfx, Kingdom Hearts 2.8 (X1)] */ ffmpeg_codec_data *ffmpeg_data = NULL; uint8_t buf[200]; int32_t bytes; /* extradata_offset+0x00: fmt0x166 header (BE), extradata_offset+0x34: seek table */ bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,200, extradata_offset,0x34, stream_size, streamFile, 1); ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,stream_size); if (!ffmpeg_data) goto fail; vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; vgmstream->num_samples = ffmpeg_data->totalSamples; vgmstream->loop_start_sample = loop_start; vgmstream->loop_end_sample = loop_end; xma_fix_raw_samples(vgmstream, streamFile, start_offset,stream_size, 0, 0,0); /* samples are ok, loops? */ break; } case 0x0E: { /* ATRAC3/ATRAC3plus [Lord of Arcana (PSP), Final Fantasy Type-0] */ ffmpeg_codec_data *ffmpeg_data = NULL; /* full RIFF header at start_offset/extradata_offset (same) */ ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,stream_size); if (!ffmpeg_data) goto fail; vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; vgmstream->num_samples = ffmpeg_data->totalSamples; /* fact samples */ vgmstream->loop_start_sample = loop_start; vgmstream->loop_end_sample = loop_end; if (ffmpeg_data->skipSamples <= 0) /* in case FFmpeg didn't get them */ ffmpeg_set_skip_samples(ffmpeg_data, riff_get_fact_skip_samples(streamFile, start_offset)); /* SCD loop/sample values are relative (without skip samples) vs RIFF (with skip samples), no need to adjust */ break; } #endif #ifdef VGM_USE_ATRAC9 case 0x16: { /* ATRAC9 [Kingdom Hearts 2.8 (PS4)] */ atrac9_config cfg = {0}; /* post header has various typical ATRAC9 values */ cfg.channels = vgmstream->channels; cfg.config_data = read_32bit(extradata_offset+0x0c,streamFile); cfg.encoder_delay = read_32bit(extradata_offset+0x18,streamFile); vgmstream->codec_data = init_atrac9(&cfg); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_ATRAC9; vgmstream->layout_type = layout_none; vgmstream->num_samples = read_32bit(extradata_offset+0x10,streamFile); /* loop values above are also weird and ignored */ vgmstream->loop_start_sample = read_32bit(extradata_offset+0x20, streamFile) - (loop_flag ? cfg.encoder_delay : 0); //loop_start vgmstream->loop_end_sample = read_32bit(extradata_offset+0x24, streamFile) - (loop_flag ? cfg.encoder_delay : 0); //loop_end break; } #endif case -1: /* used for dummy entries */ default: VGM_LOG("SCD: unknown codec 0x%x\n", codec); goto fail; } if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) goto fail; return vgmstream; fail: close_vgmstream(vgmstream); return NULL; }
/* BGW - from Final Fantasy XI (PC) music files */ VGMSTREAM * init_vgmstream_bgw(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; STREAMFILE *temp_streamFile = NULL; uint32_t codec, file_size, block_size, sample_rate, block_align; int32_t loop_start; off_t start_offset; int channel_count, loop_flag = 0; /* check extensions */ if ( !check_extensions(streamFile, "bgw") ) goto fail; /* check header */ if (read_32bitBE(0x00,streamFile) != 0x42474d53 || /* "BGMS" */ read_32bitBE(0x04,streamFile) != 0x74726561 || /* "trea" */ read_32bitBE(0x08,streamFile) != 0x6d000000 ) /* "m\0\0\0" */ goto fail; codec = read_32bitLE(0x0c,streamFile); file_size = read_32bitLE(0x10,streamFile); /*file_id = read_32bitLE(0x14,streamFile);*/ block_size = read_32bitLE(0x18,streamFile); loop_start = read_32bitLE(0x1c,streamFile); sample_rate = (read_32bitLE(0x20,streamFile) + read_32bitLE(0x24,streamFile)) & 0x7FFFFFFF; /* bizarrely obfuscated sample rate */ start_offset = read_32bitLE(0x28,streamFile); /*0x2c: unk (vol?) */ /*0x2d: unk (0x10?) */ channel_count = read_8bit(0x2e,streamFile); block_align = (uint8_t)read_8bit(0x2f,streamFile); if (file_size != get_streamfile_size(streamFile)) goto fail; loop_flag = (loop_start > 0); /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; vgmstream->meta_type = meta_FFXI_BGW; vgmstream->sample_rate = sample_rate; switch (codec) { case 0: /* PS ADPCM */ vgmstream->coding_type = coding_PSX_cfg; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = (block_align / 2) + 1; /* half, even if channels = 1 */ vgmstream->num_samples = block_size * block_align; if (loop_flag) { vgmstream->loop_start_sample = (loop_start-1) * block_align; vgmstream->loop_end_sample = vgmstream->num_samples; } break; #ifdef VGM_USE_FFMPEG case 3: { /* ATRAC3 (encrypted) */ uint8_t buf[0x100]; int bytes, joint_stereo, skip_samples; size_t data_size = file_size - start_offset; vgmstream->num_samples = block_size; /* atrac3_bytes_to_samples gives the same value */ if (loop_flag) { vgmstream->loop_start_sample = loop_start; vgmstream->loop_end_sample = vgmstream->num_samples; } block_align = 0xC0 * vgmstream->channels; /* 0x00 in header */ joint_stereo = 0; skip_samples = 0; bytes = ffmpeg_make_riff_atrac3(buf, 0x100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_align, joint_stereo, skip_samples); if (bytes <= 0) goto fail; temp_streamFile = setup_bgw_atrac3_streamfile(streamFile, start_offset,data_size, 0xC0,channel_count); if (!temp_streamFile) goto fail; vgmstream->codec_data = init_ffmpeg_header_offset(temp_streamFile, buf,bytes, 0,data_size); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; close_streamfile(temp_streamFile); break; } #endif default: goto fail; } if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) goto fail; return vgmstream; fail: close_streamfile(temp_streamFile); close_vgmstream(vgmstream); return NULL; }
VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, const char * filename, ov_callbacks *callbacks_p, off_t other_header_bytes, const vgm_vorbis_info_t *vgm_inf) { VGMSTREAM * vgmstream = NULL; OggVorbis_File temp_ovf; ogg_vorbis_streamfile temp_streamfile; ogg_vorbis_codec_data * data = NULL; OggVorbis_File *ovf; int inited_ovf = 0; vorbis_info *info; int loop_flag = vgm_inf->loop_flag; int32_t loop_start = vgm_inf->loop_start; int loop_length_found = vgm_inf->loop_length_found; int32_t loop_length = vgm_inf->loop_length; int loop_end_found = vgm_inf->loop_end_found; int32_t loop_end = vgm_inf->loop_end; ov_callbacks default_callbacks; if (!callbacks_p) { default_callbacks.read_func = read_func; default_callbacks.seek_func = seek_func; default_callbacks.close_func = close_func; default_callbacks.tell_func = tell_func; if (vgm_inf->scd_xor != 0) { default_callbacks.read_func = read_func_scd; } callbacks_p = &default_callbacks; } temp_streamfile.streamfile = streamFile; temp_streamfile.offset = 0; temp_streamfile.size = get_streamfile_size(temp_streamfile.streamfile); temp_streamfile.other_header_bytes = other_header_bytes; temp_streamfile.scd_xor = vgm_inf->scd_xor; temp_streamfile.scd_xor_len = vgm_inf->scd_xor_len; /* can we open this as a proper ogg vorbis file? */ memset(&temp_ovf, 0, sizeof(temp_ovf)); if (ov_test_callbacks(&temp_streamfile, &temp_ovf, NULL, 0, *callbacks_p)) goto fail; /* we have to close this as it has the init_vgmstream meta-reading STREAMFILE */ ov_clear(&temp_ovf); /* proceed to open a STREAMFILE just for this stream */ data = calloc(1,sizeof(ogg_vorbis_codec_data)); if (!data) goto fail; data->ov_streamfile.streamfile = streamFile->open(streamFile,filename, STREAMFILE_DEFAULT_BUFFER_SIZE); if (!data->ov_streamfile.streamfile) goto fail; data->ov_streamfile.offset = 0; data->ov_streamfile.size = get_streamfile_size(data->ov_streamfile.streamfile); data->ov_streamfile.other_header_bytes = other_header_bytes; data->ov_streamfile.scd_xor = vgm_inf->scd_xor; data->ov_streamfile.scd_xor_len = vgm_inf->scd_xor_len; /* open the ogg vorbis file for real */ if (ov_open_callbacks(&data->ov_streamfile, &data->ogg_vorbis_file, NULL, 0, *callbacks_p)) goto fail; ovf = &data->ogg_vorbis_file; inited_ovf = 1; data->bitstream = DEFAULT_BITSTREAM; info = ov_info(ovf,DEFAULT_BITSTREAM); /* grab the comments */ { int i; vorbis_comment *comment; comment = ov_comment(ovf,DEFAULT_BITSTREAM); /* search for a "loop_start" comment */ for (i=0;i<comment->comments;i++) { if (strstr(comment->user_comments[i],"loop_start=")== comment->user_comments[i] || strstr(comment->user_comments[i],"LOOP_START=")== comment->user_comments[i] || strstr(comment->user_comments[i],"COMMENT=LOOPPOINT=")== comment->user_comments[i] || strstr(comment->user_comments[i],"LOOPSTART=")== comment->user_comments[i] || strstr(comment->user_comments[i],"um3.stream.looppoint.start=")== comment->user_comments[i] || strstr(comment->user_comments[i],"LOOP_BEGIN=")== comment->user_comments[i] || strstr(comment->user_comments[i],"LoopStart=")== comment->user_comments[i] ) { loop_start=atol(strrchr(comment->user_comments[i],'=')+1); if (loop_start >= 0) loop_flag=1; } else if (strstr(comment->user_comments[i],"LOOPLENGTH=")== comment->user_comments[i]) { loop_length=atol(strrchr(comment->user_comments[i],'=')+1); loop_length_found=1; } else if (strstr(comment->user_comments[i],"title=-lps")== comment->user_comments[i]) { loop_start=atol(comment->user_comments[i]+10); if (loop_start >= 0) loop_flag=1; } else if (strstr(comment->user_comments[i],"album=-lpe")== comment->user_comments[i]) { loop_end=atol(comment->user_comments[i]+10); loop_flag=1; loop_end_found=1; } else if (strstr(comment->user_comments[i],"LoopEnd=")== comment->user_comments[i]) { if(loop_flag) { loop_length=atol(strrchr(comment->user_comments[i],'=')+1)-loop_start; loop_length_found=1; } } else if (strstr(comment->user_comments[i],"LOOP_END=")== comment->user_comments[i]) { if(loop_flag) { loop_length=atol(strrchr(comment->user_comments[i],'=')+1)-loop_start; loop_length_found=1; } } else if (strstr(comment->user_comments[i],"lp=")== comment->user_comments[i]) { sscanf(strrchr(comment->user_comments[i],'=')+1,"%d,%d", &loop_start,&loop_end); loop_flag=1; loop_end_found=1; } } } /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(info->channels,loop_flag); if (!vgmstream) goto fail; /* store our fun extra datas */ vgmstream->codec_data = data; /* fill in the vital statistics */ vgmstream->channels = info->channels; vgmstream->sample_rate = info->rate; /* let's play the whole file */ vgmstream->num_samples = ov_pcm_total(ovf,-1); if (loop_flag) { vgmstream->loop_start_sample = loop_start; if (loop_length_found) vgmstream->loop_end_sample = loop_start+loop_length; else if (loop_end_found) vgmstream->loop_end_sample = loop_end; else vgmstream->loop_end_sample = vgmstream->num_samples; vgmstream->loop_flag = loop_flag; if (vgmstream->loop_end_sample > vgmstream->num_samples) vgmstream->loop_end_sample = vgmstream->num_samples; } vgmstream->coding_type = coding_ogg_vorbis; vgmstream->layout_type = vgm_inf->layout_type; vgmstream->meta_type = vgm_inf->meta_type; return vgmstream; /* clean up anything we may have opened */ fail: if (data) { if (inited_ovf) ov_clear(&data->ogg_vorbis_file); if (data->ov_streamfile.streamfile) close_streamfile(data->ov_streamfile.streamfile); free(data); } if (vgmstream) { vgmstream->codec_data = NULL; close_vgmstream(vgmstream); } return NULL; }
/* GSP+GSB - 2008-11-28 - manakoAT Super Swing Golf 1 & 2 (WII) */ VGMSTREAM * init_vgmstream_gsp_gsb(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; STREAMFILE * streamFileGSP = NULL; char filename[PATH_LIMIT]; char filenameGSP[PATH_LIMIT]; int channel_count; int loop_flag; int header_len; int i; off_t coef1_start; off_t coef2_start; off_t start_offset; /* check extension, case insensitive */ streamFile->get_name(streamFile,filename,sizeof(filename)); if (strcasecmp("gsb",filename_extension(filename))) goto fail; strcpy(filenameGSP,filename); strcpy(filenameGSP+strlen(filenameGSP)-3,"gsp"); streamFileGSP = streamFile->open(streamFile,filenameGSP,STREAMFILE_DEFAULT_BUFFER_SIZE); if (!streamFileGSP) goto fail; /* check header */ if (read_32bitBE(0x00,streamFileGSP) != 0x47534E44) /* "GSND" */ goto fail; channel_count = (uint16_t)read_16bitBE(0x3A,streamFileGSP); loop_flag = (read_32bitBE(0x64,streamFileGSP) != 0xFFFFFFFF); header_len = read_32bitBE(0x1C,streamFileGSP); coef1_start = header_len-0x4C; coef2_start = header_len-0x1C; /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; /* fill in the vital statistics */ vgmstream->channels = channel_count; vgmstream->sample_rate = read_32bitBE(0x34,streamFileGSP); vgmstream->coding_type = coding_NGC_DSP; if(loop_flag) { vgmstream->loop_start_sample = read_32bitBE(0x64,streamFileGSP); vgmstream->loop_end_sample = read_32bitBE(0x68,streamFileGSP); } if (channel_count == 1) { vgmstream->layout_type = layout_gsb_blocked; } else if (channel_count > 1) { vgmstream->layout_type = layout_gsb_blocked; vgmstream->interleave_block_size = read_32bitBE(header_len-0x64,streamFileGSP); } vgmstream->meta_type = meta_GSP_GSB; /* open the file for reading by each channel */ { for (i=0;i<channel_count;i++) { vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,vgmstream->interleave_block_size); if (!vgmstream->ch[i].streamfile) goto fail; vgmstream->ch[i].channel_start_offset= vgmstream->ch[i].offset=i*vgmstream->interleave_block_size; } } if (vgmstream->coding_type == coding_NGC_DSP) { int i; for (i=0;i<16;i++) { vgmstream->ch[0].adpcm_coef[i] = read_16bitBE(coef1_start+i*2,streamFileGSP); } if (vgmstream->channels == 2) { for (i=0;i<16;i++) { vgmstream->ch[1].adpcm_coef[i] = read_16bitBE(coef2_start+i*2,streamFileGSP); } } } /* Calc num_samples */ start_offset = 0x0; gsb_block_update(start_offset,vgmstream); vgmstream->num_samples=0; do { vgmstream->num_samples += vgmstream->current_block_size*14/8; gsb_block_update(vgmstream->next_block_offset,vgmstream); } while (vgmstream->next_block_offset<get_streamfile_size(streamFile)); gsb_block_update(start_offset,vgmstream); close_streamfile(streamFileGSP); streamFileGSP=NULL; return vgmstream; /* clean up anything we may have opened */ fail: if (streamFileGSP) close_streamfile(streamFileGSP); if (vgmstream) close_vgmstream(vgmstream); return NULL; }
VGMSTREAM * init_vgmstream_sab(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; STREAMFILE* sob = NULL; char filename[1024]; int i; int loop_flag, channel_count, numSounds; /* check extension, case insensitive */ streamFile->get_name(streamFile,filename,sizeof(filename)); if (strcasecmp("sab",filename_extension(filename))) goto fail; /* read information from sob file*/ filename[strlen(filename)-2]='o'; //change extension in sob of file sob = open_stdio_streamfile(filename); if(!sob) goto fail; filename[strlen(filename)-2]='a';//change back to original file if (read_32bitBE(0,streamFile)!=0x43535732)//CSW2 header sab file { goto fail; } if (read_32bitBE(0,sob)!=0x43544632)//CTF2 header sob file { goto fail; } numSounds = read_32bitLE(8,sob); if(numSounds==1) {//it means it's a single stream and not a voice bank loop_flag = 1; }else { loop_flag = 0; } /* Read channels */ channel_count = read_32bitLE(0x30,sob); if( (channel_count>2)||(numSounds>1))/* dirty hack for number of channels*/ channel_count = 1; /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; /* fill in the vital statistics */ //is offset OK. sab files can contain more audio files, but without the sob it's just a long stream of sound effects vgmstream->current_block_offset=8+32*numSounds; vgmstream->channels = channel_count; vgmstream->sample_rate = read_32bitLE(0x20,streamFile); vgmstream->coding_type = coding_PCM16LE_int; vgmstream->num_samples = (int32_t)((get_streamfile_size(streamFile)-vgmstream->current_block_offset)/2/channel_count); if(loop_flag) { vgmstream->loop_start_sample = 0; vgmstream->loop_end_sample = vgmstream->num_samples; } vgmstream->layout_type = layout_none; vgmstream->meta_type = meta_PC_SOB_SAB; { for (i=0;i<channel_count;i++) { vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); if (!vgmstream->ch[i].streamfile) goto fail; vgmstream->ch[i].channel_start_offset= vgmstream->ch[i].offset=vgmstream->current_block_offset+2*i; } } close_streamfile(sob); return vgmstream; /* clean up anything we may have opened */ fail: if (sob) close_streamfile(sob); if (vgmstream) close_vgmstream(vgmstream); return NULL; }
/* .VSV - from Square Enix games [Dawn of Mana: Seiken Densetsu 4 (PS2), Kingdom Hearts Re:Chain of Memories (PS2)] */ VGMSTREAM * init_vgmstream_vsv(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; STREAMFILE *temp_streamFile = NULL; off_t start_offset; int loop_flag, channel_count, flags, sample_rate, is_rs; size_t loop_start, adjust, data_size, interleave; /* checks */ /* .vsv: extension from internal filenames [KH Re:CoM (PS2), DoM (PS2), KH HD I.5 + II.5 ReMIX (PS4)] * .psh: fake */ if (!check_extensions(streamFile, "vsv,psh")) goto fail; /* 0x00(1x4): flags/config? */ if ((uint8_t)read_8bit(0x03,streamFile) > 0x64) /* possibly volume */ goto fail; if ((uint8_t)read_8bit(0x0a,streamFile) != 0) /* not seen */ goto fail; /* Romancing SaGa (PS2) uses an earlier? version, this seems to work */ is_rs = ((uint16_t)read_16bitLE(0x00,streamFile) == 0); start_offset = 0x00; /* correct, but needs some tricks to fix sound (see below) */ interleave = 0x800; adjust = (uint16_t)read_16bitLE(0x04,streamFile); loop_start = ((uint16_t)read_16bitLE(0x06,streamFile) & 0x7FFF) * interleave; loop_flag = (uint16_t)read_16bitLE(0x06,streamFile) & 0x8000; /* loop_start != 0 works too, no files loop from beginning to end */ sample_rate = (uint16_t)read_16bitLE(0x08,streamFile); flags = (uint8_t)read_8bit (0x0b,streamFile); /* values: 0x01=stereo, 0x10=mono */ data_size = (uint16_t)read_16bitLE(0x0c,streamFile) * interleave; /* 0x0e: ? (may be a low-ish value) */ channel_count = (flags & 1) ? 2 : 1; /* must discard to avoid wrong loops and unwanted data (easier to see in voices) */ if (!is_rs) { /* RS doesn't do this */ /* adjust & 0xF800 is unknown (values=0x0000|0x0800|0xF800, can be mono/stereo, loop/no, adjust/no) */ size_t discard = adjust & 0x07FF; /* at (file_end - 0x800 + discard) is a 0x03 PS flag to check this (adjust 0 does discard full block) */ data_size -= (0x800 - discard) * channel_count; } /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count, loop_flag); if (!vgmstream) goto fail; vgmstream->meta_type = meta_VSV; vgmstream->sample_rate = sample_rate; vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count); vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, channel_count); vgmstream->loop_end_sample = vgmstream->num_samples; /* these loops are odd, but comparing the audio wave with the OSTs values seem correct */ if (is_rs) { vgmstream->loop_start_sample -= ps_bytes_to_samples(channel_count*interleave,channel_count); /* maybe *before* loop block? */ vgmstream->loop_start_sample -= ps_bytes_to_samples(0x200*channel_count,channel_count); /* maybe default adjust? */ } vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = interleave; temp_streamFile = setup_vsv_streamfile(streamFile, start_offset, data_size); if (!temp_streamFile) goto fail; if (!vgmstream_open_stream(vgmstream, temp_streamFile, start_offset)) goto fail; close_streamfile(temp_streamFile); return vgmstream; fail: close_streamfile(temp_streamFile); close_vgmstream(vgmstream); return NULL; }
/* SGXD - Sony/SCEI's format (SGB+SGH / SGD / SGX) */ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; STREAMFILE * streamHeader = NULL; off_t start_offset, data_offset, chunk_offset, name_offset = 0; size_t stream_size; int is_sgx, is_sgb = 0; int loop_flag, channels, type; int sample_rate, num_samples, loop_start_sample, loop_end_sample; int total_subsongs, target_subsong = streamFile->stream_index; /* check extension, case insensitive */ /* .sgx: header+data (Genji), .sgd: header+data, .sgh/sgd: header/data */ if (!check_extensions(streamFile,"sgx,sgd,sgb")) goto fail; is_sgx = check_extensions(streamFile,"sgx"); is_sgb = check_extensions(streamFile,"sgb"); /* SGB+SGH: use SGH as header; otherwise use the current file as header */ if (is_sgb) { streamHeader = open_stream_ext(streamFile, "sgh"); if (!streamHeader) goto fail; } else { streamHeader = streamFile; } /* SGXD base (size 0x10) */ if (read_32bitBE(0x00,streamHeader) != 0x53475844) /* "SGXD" */ goto fail; /* 0x04 SGX: full header_size; SGD/SGH: unknown header_size (counting from 0x0/0x8/0x10, varies) */ /* 0x08 SGX: first chunk offset? (0x10); SGD/SGH: full header_size */ /* 0x0c SGX/SGH: full data size with padding; SGD: full data size + 0x80000000 with padding */ if (is_sgb) { data_offset = 0x00; } else if ( is_sgx ) { data_offset = read_32bitLE(0x04,streamHeader); } else { data_offset = read_32bitLE(0x08,streamHeader); } /* typical chunks: WAVE, RGND, NAME (strings for WAVE or RGND), SEQD (related to SFX), WSUR, WMKR, BUSS */ /* WAVE chunk (size 0x10 + files * 0x38 + optional padding) */ if (is_sgx) { /* position after chunk+size */ if (read_32bitBE(0x10,streamHeader) != 0x57415645) goto fail; /* "WAVE" */ chunk_offset = 0x18; } else { if (!find_chunk_le(streamHeader, 0x57415645,0x10,0, &chunk_offset,NULL)) goto fail; /* "WAVE" */ } /* 0x04 SGX: unknown; SGD/SGH: chunk length, 0x08 null */ /* check multi-streams (usually only SE containers; Puppeteer) */ total_subsongs = read_32bitLE(chunk_offset+0x04,streamHeader); if (target_subsong == 0) target_subsong = 1; if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; /* read stream header */ { off_t stream_offset; chunk_offset += 0x08 + 0x38 * (target_subsong-1); /* position in target header*/ /* 0x00 ? (00/01/02) */ if (!is_sgx) /* meaning unknown in .sgx; offset 0 = not a stream (a RGND sample) */ name_offset = read_32bitLE(chunk_offset+0x04,streamHeader); type = read_8bit(chunk_offset+0x08,streamHeader); channels = read_8bit(chunk_offset+0x09,streamHeader); /* 0x0a null */ sample_rate = read_32bitLE(chunk_offset+0x0c,streamHeader); /* 0x10 info_type: meaning of the next value * (00=null, 30/40=data size without padding (ADPCM, ATRAC3plus), 80/A0=block size (AC3) */ /* 0x14 info_value (see above) */ /* 0x18 unknown (ex. 0x0008/0010/3307/CC02/etc)x2 */ /* 0x1c null */ num_samples = read_32bitLE(chunk_offset+0x20,streamHeader); loop_start_sample = read_32bitLE(chunk_offset+0x24,streamHeader); loop_end_sample = read_32bitLE(chunk_offset+0x28,streamHeader); stream_size = read_32bitLE(chunk_offset+0x2c,streamHeader); /* stream size (without padding) / interleave (for type3) */ if (is_sgx) { stream_offset = 0x0; } else{ stream_offset = read_32bitLE(chunk_offset+0x30,streamHeader); } /* 0x34 SGX: unknown; SGD/SGH: stream size (with padding) / interleave */ loop_flag = loop_start_sample!=0xffffffff && loop_end_sample!=0xffffffff; start_offset = data_offset + stream_offset; } /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channels,loop_flag); if (!vgmstream) goto fail; vgmstream->sample_rate = sample_rate; vgmstream->num_samples = num_samples; vgmstream->loop_start_sample = loop_start_sample; vgmstream->loop_end_sample = loop_end_sample; vgmstream->num_streams = total_subsongs; vgmstream->stream_size = stream_size; vgmstream->meta_type = meta_SGXD; if (name_offset) read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader); /* needs -1 to match RIFF AT3's loop chunk * (maybe SGXD = "loop before this sample" rather than "loop after this sample" as expected by vgmstream) */ if (vgmstream->loop_end_sample > 0) vgmstream->loop_end_sample -= 1; switch (type) { case 0x03: /* PS-ADPCM [Genji (PS3), Ape Escape Move (PS3)]*/ vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_interleave; if (is_sgx || is_sgb) { vgmstream->interleave_block_size = 0x10; } else { /* this only seems to happen with SFX */ vgmstream->interleave_block_size = stream_size; } break; #ifdef VGM_USE_FFMPEG case 0x04: { /* ATRAC3plus [Kurohyo 1/2 (PSP), BraveStory (PSP)] */ ffmpeg_codec_data *ffmpeg_data; /* internally has a RIFF header; but the SGXD header / sample rate has priority over it (may not match) */ ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, stream_size); if ( !ffmpeg_data ) goto fail; vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; /* manually read skip_samples if FFmpeg didn't do it */ if (ffmpeg_data->skipSamples <= 0) { off_t chunk_offset; size_t chunk_size, fact_skip_samples = 0; if (!find_chunk_le(streamFile, 0x66616374,start_offset+0xc,0, &chunk_offset,&chunk_size)) /* find "fact" */ goto fail; if (chunk_size == 0x8) { fact_skip_samples = read_32bitLE(chunk_offset+0x4, streamFile); } else if (chunk_size == 0xc) { fact_skip_samples = read_32bitLE(chunk_offset+0x8, streamFile); } ffmpeg_set_skip_samples(ffmpeg_data, fact_skip_samples); } /* SGXD loop/sample values are relative (without skip samples) vs RIFF (with skip samples), no need to adjust */ break; } #endif case 0x05: /* Short PS-ADPCM [Afrika (PS3)] */ vgmstream->coding_type = coding_PSX_cfg; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x4; break; #ifdef VGM_USE_FFMPEG case 0x06: { /* AC3 [Tokyo Jungle (PS3), Afrika (PS3)] */ ffmpeg_codec_data *ffmpeg_data; ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, stream_size); if ( !ffmpeg_data ) goto fail; vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; /* manually set skip_samples if FFmpeg didn't do it */ if (ffmpeg_data->skipSamples <= 0) { /* PS3 AC3 consistently has 256 encoder delay samples, and there are ~1000-2000 samples after num_samples. * Skipping them marginally improves full loops in some Tokyo Jungle tracks (ex. a_1.sgd). */ ffmpeg_set_skip_samples(ffmpeg_data, 256); } /* SGXD loop/sample values are relative (without skip samples), no need to adjust */ break; } #endif default: goto fail; } /* open the file for reading */ if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; if (is_sgb && streamHeader) close_streamfile(streamHeader); return vgmstream; fail: if (is_sgb && streamHeader) close_streamfile(streamHeader); close_vgmstream(vgmstream); return NULL; }
/* SGH+SGB (from Folklore) */ VGMSTREAM * init_vgmstream_ps3_sgh_sgb(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset = 0; STREAMFILE * streamFileSGH = NULL; char filename[260]; char filenameSGH[260]; int channel_count; int loop_flag; /* check extension, case insensitive */ streamFile->get_name(streamFile,filename,sizeof(filename)); if (strcasecmp("sgb",filename_extension(filename))) goto fail; strcpy(filenameSGH,filename); strcpy(filenameSGH+strlen(filenameSGH)-3,"sgh"); streamFileSGH = streamFile->open(streamFile,filenameSGH,STREAMFILE_DEFAULT_BUFFER_SIZE); if (!streamFileSGH) goto fail; /* check header */ if (read_32bitBE(0x00,streamFileSGH) != 0x53475844) /* "SGXD" */ goto fail; channel_count = read_8bit(0x29,streamFileSGH); if (read_32bitBE(0x44,streamFileSGH)==0xFFFFFFFF) loop_flag = 0; else loop_flag = 1; /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; /* fill in the vital statistics */ vgmstream->channels = channel_count; vgmstream->sample_rate = read_32bitLE(0x2C,streamFileSGH); vgmstream->num_samples = read_32bitLE(0xC,streamFileSGH)*28/32; vgmstream->coding_type = coding_PSX; if(loop_flag) { vgmstream->loop_start_sample = read_32bitLE(0x44,streamFileSGH); vgmstream->loop_end_sample = read_32bitLE(0x48,streamFileSGH); } vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x10; vgmstream->meta_type = meta_PS3_SGH_SGB; /* open the file for reading */ { int i; STREAMFILE * file; file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); if (!file) goto fail; for (i=0;i<channel_count;i++) { vgmstream->ch[i].streamfile = file; vgmstream->ch[i].channel_start_offset= vgmstream->ch[i].offset=start_offset+ vgmstream->interleave_block_size*i; } } return vgmstream; /* clean up anything we may have opened */ fail: if (streamFileSGH) close_streamfile(streamFileSGH); if (vgmstream) close_vgmstream(vgmstream); return NULL; }
static int play_vgmstream(const char *filename, struct params *par) { int ret = 0; STREAMFILE *sf; VGMSTREAM *vgms; FILE *save_fps[4]; int loop_count; int64_t total_samples; size_t buffer_size; int64_t buffer_samples; int64_t fade_time_samples, fade_start; int time_total_min; double time_total_sec; int64_t s; int i; sf = open_stdio_streamfile(filename); if (!sf) { fprintf(stderr, "%s: cannot open file\n", filename); return -1; } sf->stream_index = par->stream_index; vgms = init_vgmstream_from_STREAMFILE(sf); close_streamfile(sf); if (!vgms) { fprintf(stderr, "%s: error opening stream\n", filename); return -1; } printf("Playing stream: %s\n", filename); /* Print metadata in verbose mode */ if (verbose) { char description[4096] = { '\0' }; describe_vgmstream(vgms, description, sizeof(description)); puts(description); putchar('\n'); } /* If the audio device hasn't been opened yet, then describe it */ if (!device) { ao_info *info = ao_driver_info(driver_id); printf("Audio device: %s\n", info->name); printf("Comment: %s\n", info->comment); putchar('\n'); } /* Stupid hack to hang onto a few low-numbered file descriptors * so that play_compressed_file() doesn't break, due to POSIX * wackiness like https://bugs.debian.org/590920 */ for (i = 0; i < 4; i++) save_fps[i] = fopen("/dev/null", "r"); ret = set_sample_format(vgms); if (ret) goto fail; loop_count = par->loop_count; if (vgms->loop_flag && loop_count < 0) { /* * Calculate how many loops are needed to achieve a minimum * playback time. Note: This calculation is derived from the * logic in get_vgmstream_play_samples(). */ double intro = (double)vgms->loop_start_sample / vgms->sample_rate; double loop = (double)(vgms->loop_end_sample - vgms->loop_start_sample) / vgms->sample_rate; double end = par->fade_time + par->fade_delay; if (loop < 1.0) loop = 1.0; loop_count = (int)((par->min_time - intro - end) / loop + 0.99); if (loop_count < 1) loop_count = 1; } total_samples = get_vgmstream_play_samples(loop_count, par->fade_time, par->fade_delay, vgms); { double total = (double)total_samples / vgms->sample_rate; time_total_min = (int)total / 60; time_total_sec = total - 60 * time_total_min; } /* Buffer size in bytes */ buffer_size = 1024 * buffer_size_kb; if (!buffer) { if (buffer_size_kb < 1) { fprintf(stderr, "Invalid buffer size '%d'\n", buffer_size_kb); return -1; } buffer = malloc(buffer_size); if (!buffer) goto fail; } buffer_samples = buffer_size / (vgms->channels * sizeof(sample)); fade_time_samples = (int64_t)(par->fade_time * vgms->sample_rate); fade_start = total_samples - fade_time_samples; if (fade_start < 0) fade_start = total_samples; for (s = 0; s < total_samples && !interrupted; s += buffer_samples) { int64_t buffer_used_samples = MIN(buffer_samples, total_samples - s); char *suffix = ""; render_vgmstream(buffer, buffer_used_samples, vgms); #ifdef LITTLE_ENDIAN_OUTPUT swap_samples_le(buffer, vgms->channels * buffer_used_samples); #endif if (vgms->loop_flag && fade_time_samples > 0 && s >= fade_start) { /* Got to do the fade-out ourselves :p */ int64_t b; for (b = 0; b < buffer_used_samples; b++) { double factor = 1.0 - (double)(s + b - fade_start) / fade_time_samples; int c; for (c = 0; c < vgms->channels; c++) buffer[vgms->channels * b + c] *= factor; } suffix = " (fading)"; } if (verbose && !out_filename) { double played = (double)s / vgms->sample_rate; double remain = (double)(total_samples - s) / vgms->sample_rate; int time_played_min = (int)played / 60; double time_played_sec = played - 60 * time_played_min; int time_remain_min = (int)remain / 60; double time_remain_sec = remain - 60 * time_remain_min; /* Time: 01:02.34 [08:57.66] of 10:00.00 */ printf("\rTime: %02d:%05.2f [%02d:%05.2f] of %02d:%05.2f%s ", time_played_min, time_played_sec, time_remain_min, time_remain_sec, time_total_min, time_total_sec, suffix); fflush(stdout); } if (!ao_play(device, (char *)buffer, buffer_used_samples * vgms->channels * sizeof(sample))) { fputs("\nAudio playback error\n", stderr); ao_close(device); device = NULL; ret = -1; break; } } if (verbose && !ret) { /* Clear time status line */ putchar('\r'); for (i = 0; i < 64; i++) putchar(' '); putchar('\r'); fflush(stdout); } if (out_filename && !ret) printf("Wrote %02d:%05.2f of audio to %s\n\n", time_total_min, time_total_sec, out_filename); if (interrupted) { fputs("Playback terminated.\n\n", stdout); ret = record_interrupt(); if (ret) fputs("Exiting...\n", stdout); } fail: close_vgmstream(vgms); for (i = 0; i < 4; i++) fclose(save_fps[i]); return ret; }
VGMSTREAM * init_vgmstream_ngc_sck_dsp(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; STREAMFILE * streamFileDSP = NULL; char filename[1024]; char filenameDSP[1024]; int i; int channel_count; int loop_flag; /* check extension, case insensitive */ streamFile->get_name(streamFile,filename,sizeof(filename)); if (strcasecmp("sck",filename_extension(filename))) goto fail; strcpy(filenameDSP,filename); strcpy(filenameDSP+strlen(filenameDSP)-3,"dsp"); streamFileDSP = streamFile->open(streamFile,filenameDSP,STREAMFILE_DEFAULT_BUFFER_SIZE); if (read_32bitBE(0x5C,streamFile) != 0x60A94000) goto fail; if (!streamFile) goto fail; channel_count = 2; loop_flag = 0; /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; /* fill in the vital statistics */ vgmstream->channels = channel_count; vgmstream->sample_rate = read_32bitBE(0x18,streamFile); vgmstream->num_samples=read_32bitBE(0x14,streamFile)/8/channel_count*14; vgmstream->coding_type = coding_NGC_DSP; if(loop_flag) { vgmstream->loop_start_sample = 0; vgmstream->loop_end_sample = read_32bitBE(0x10,streamFile)/8/channel_count*14; } if (channel_count == 1) { vgmstream->layout_type = layout_none; } else if (channel_count == 2) { vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size=read_32bitBE(0xC,streamFile); } vgmstream->meta_type = meta_NGC_SCK_DSP; /* open the file for reading */ { for (i=0;i<channel_count;i++) { /* Not sure, i'll put a fake value here for now */ vgmstream->ch[i].streamfile = streamFile->open(streamFileDSP,filenameDSP,0x8000); vgmstream->ch[i].offset = 0; if (!vgmstream->ch[i].streamfile) goto fail; } } if (vgmstream->coding_type == coding_NGC_DSP) { int i; for (i=0;i<16;i++) { vgmstream->ch[0].adpcm_coef[i] = read_16bitBE(0x2C+i*2,streamFile); } if (vgmstream->channels == 2) { for (i=0;i<16;i++) { vgmstream->ch[1].adpcm_coef[i] = read_16bitBE(0x2C+i*2,streamFile); } } } close_streamfile(streamFileDSP); streamFileDSP=NULL; return vgmstream; /* clean up anything we may have opened */ fail: if (streamFileDSP) close_streamfile(streamFileDSP); if (vgmstream) close_vgmstream(vgmstream); return NULL; }
VGMSTREAM * init_vgmstream_ngc_dsp_sth_str1(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; STREAMFILE * streamFileSTR = NULL; char filename[260]; char filenameSTR[260]; int i, j; int channel_count; int loop_flag; off_t coef_table[8] = {0x12C,0x18C,0x1EC,0x24C,0x2AC,0x30C,0x36C,0x3CC}; /* check extension, case insensitive */ streamFile->get_name(streamFile,filename,sizeof(filename)); if (strcasecmp("sth",filename_extension(filename))) goto fail; strcpy(filenameSTR,filename); strcpy(filenameSTR+strlen(filenameSTR)-3,"str"); streamFileSTR = streamFile->open(streamFile,filenameSTR,STREAMFILE_DEFAULT_BUFFER_SIZE); if (!streamFileSTR) goto fail; if (read_32bitBE(0x0,streamFile) != 0x0) { goto fail; } if (read_32bitBE(0x4,streamFile) != 0x800) { goto fail; } /* Not really channel_count, just 'included tracks * channels per track */ loop_flag = (read_32bitBE(0xD8,streamFile) != 0xFFFFFFFF); channel_count = (read_32bitBE(0x70,streamFile)) * (read_32bitBE(0x88,streamFile)); if (channel_count > 8) { goto fail; } /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; /* fill in the vital statistics */ vgmstream->channels = channel_count; vgmstream->sample_rate = read_32bitBE(0x24,streamFile); vgmstream->num_samples=get_streamfile_size(streamFileSTR)/8/channel_count*14; vgmstream->coding_type = coding_NGC_DSP; if(loop_flag) { vgmstream->loop_start_sample = read_32bitBE(0xD8,streamFile); vgmstream->loop_end_sample = read_32bitBE(0xDC,streamFile); } if (channel_count == 1) { vgmstream->layout_type = layout_none; } else { vgmstream->layout_type = layout_interleave; if (channel_count == 2) { vgmstream->interleave_block_size=0x10000; } else { vgmstream->interleave_block_size=0x8000; } } vgmstream->meta_type = meta_NGC_DSP_STH_STR; /* open the file for reading */ for (i=0;i<channel_count;i++) { vgmstream->ch[i].streamfile = streamFileSTR->open(streamFileSTR,filenameSTR,0x8000); if (!vgmstream->ch[i].streamfile) goto fail; vgmstream->ch[i].channel_start_offset=vgmstream->ch[i].offset=i*vgmstream->interleave_block_size; } // COEFFS for (j=0;j<vgmstream->channels;j++) { for (i=0;i<16;i++) { vgmstream->ch[j].adpcm_coef[i] = read_16bitBE(coef_table[j]+i*2,streamFile); } } close_streamfile(streamFileSTR); streamFileSTR=NULL; return vgmstream; /* clean up anything we may have opened */ fail: if (streamFileSTR) close_streamfile(streamFileSTR); if (vgmstream) close_vgmstream(vgmstream); return NULL; }
VGMSTREAM * init_vgmstream_ps2_wmus(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; char filename[260]; int loop_flag = 1; int channel_count; off_t start_offset; int i; int blockCount; int shortBlockSize; int lastBlockLocation; char filenameWHED[260]; STREAMFILE * streamFileWHED = NULL; //_TCHAR szBuffer[100]; /* check extension, case insensitive */ streamFile->get_name(streamFile,filename,sizeof(filename)); if (strcasecmp("wmus",filename_extension(filename))) { goto fail; } /* check for .WHED file */ strcpy(filenameWHED, filename); strcpy(filenameWHED + strlen(filenameWHED) - 4, "WHED"); streamFileWHED = streamFile->open(streamFile, filenameWHED, STREAMFILE_DEFAULT_BUFFER_SIZE); if (!streamFileWHED) { goto fail; } /* check loopand channel */ loop_flag = 1; channel_count = read_32bitLE(0x14, streamFileWHED); /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count, loop_flag); if (!vgmstream) { goto fail; } /* fill in the vital statistics */ vgmstream->channels = channel_count; vgmstream->sample_rate = read_32bitLE(0x04, streamFileWHED); vgmstream->coding_type = coding_PSX; vgmstream->interleave_block_size = read_32bitLE(0x18, streamFileWHED); blockCount = read_32bitLE(0x1C, streamFileWHED) * channel_count; shortBlockSize = read_32bitLE(0x20, streamFileWHED); vgmstream->num_samples = (vgmstream->interleave_block_size * blockCount) / 16 / channel_count * 28; vgmstream->loop_start_sample = 0; lastBlockLocation = (vgmstream->interleave_block_size * blockCount) - (vgmstream->interleave_block_size - shortBlockSize); vgmstream->loop_end_sample = lastBlockLocation / 16 / channel_count * 28; //_stprintf(szBuffer, _T("%x"), lastBlockLocation); //MessageBox(NULL, szBuffer, _T("Foo"), MB_OK); vgmstream->layout_type = layout_interleave; vgmstream->meta_type = meta_PS2_WMUS; start_offset = 0; /* open the file for reading by each channel */ { for (i=0;i<channel_count;i++) { vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,vgmstream->interleave_block_size); if (!vgmstream->ch[i].streamfile) goto fail; vgmstream->ch[i].channel_start_offset= vgmstream->ch[i].offset= (off_t)(start_offset+vgmstream->interleave_block_size*i); } } return vgmstream; /* clean up anything we may have opened */ fail: if (streamFileWHED) close_streamfile(streamFileWHED); if (vgmstream) close_vgmstream(vgmstream); return NULL; }
/* Actual AAX init fcn */ VGMSTREAM * init_vgmstream_aax(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; STREAMFILE * streamFileAAX = NULL; STREAMFILE * streamFileADX = NULL; char filename[1024]; off_t *segment_offset = NULL; off_t *segment_size = NULL; int32_t sample_count; int table_error = 0; int loop_flag = 0; int32_t loop_start_sample=0; int32_t loop_end_sample=0; int loop_segment = 0; aax_codec_data *data = NULL; const long AAX_offset = 0; int channel_count = 0, segment_count; int sample_rate = 0; int i; long aax_data_offset; /* check extension, case insensitive */ streamFile->get_name(streamFile,filename,sizeof(filename)); if (strcasecmp("aax",filename_extension(filename))) goto fail; /* get AAX entry count, data offset */ { struct utf_query_result result; long aax_string_table_offset; long aax_string_table_size; result = query_utf_nofail(streamFile, AAX_offset, NULL, &table_error); if (table_error) goto fail; segment_count = result.rows; aax_string_table_offset = AAX_offset + 8 + result.string_table_offset; aax_data_offset = AAX_offset + 8 + result.data_offset; aax_string_table_size = aax_data_offset - aax_string_table_offset; if (result.name_offset+4 > aax_string_table_size) goto fail; if (read_32bitBE(aax_string_table_offset + result.name_offset, streamFile) != 0x41415800) /* "AAX\0" */ goto fail; } segment_offset = calloc(segment_count,sizeof(off_t)); if (!segment_offset) goto fail; segment_size = calloc(segment_count,sizeof(off_t)); if (!segment_size) goto fail; /* get offsets of constituent ADXs */ for (i = 0; i < segment_count; i++) { struct offset_size_pair offset_size; offset_size = query_utf_data(streamFile, AAX_offset, i, "data", &table_error); if (table_error) goto fail; segment_offset[i] = aax_data_offset + offset_size.offset; segment_size[i] = offset_size.size; } streamFileAAX = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); if (!streamFileAAX) goto fail; data = malloc(sizeof(aax_codec_data)); if (!data) goto fail; data->segment_count = segment_count; data->adxs = malloc(sizeof(STREAMFILE *)*segment_count); if (!data->adxs) goto fail; for (i=0;i<segment_count;i++) { data->adxs[i] = NULL; } data->sample_counts = calloc(segment_count,sizeof(int32_t)); if (!data->sample_counts) goto fail; /* for each segment */ for (i = 0; i < segment_count; i++) { VGMSTREAM *adx; /*printf("try opening segment %d/%d %x\n",i,segment_count,segment_offset[i]);*/ streamFileADX = open_aax_with_STREAMFILE(streamFileAAX,segment_offset[i],segment_size[i]); if (!streamFileADX) goto fail; adx = data->adxs[i] = init_vgmstream_adx(streamFileADX); if (!adx) goto fail; data->sample_counts[i] = adx->num_samples; close_streamfile(streamFileADX); streamFileADX = NULL; if (i == 0) { channel_count = adx->channels; sample_rate = adx->sample_rate; } else { if (channel_count != adx->channels) goto fail; if (sample_rate != adx->sample_rate) goto fail; } if (adx->loop_flag != 0) goto fail; /* save start things so we can restart for seeking/looping */ /* copy the channels */ memcpy(adx->start_ch,adx->ch,sizeof(VGMSTREAMCHANNEL)*adx->channels); /* copy the whole VGMSTREAM */ memcpy(adx->start_vgmstream,adx,sizeof(VGMSTREAM)); } sample_count = 0; loop_flag = 0; for (i = 0; i < segment_count; i++) { int segment_loop_flag = query_utf_1byte(streamFile, AAX_offset, i, "lpflg", &table_error); if (table_error) segment_loop_flag = 0; if (!loop_flag && segment_loop_flag) { loop_start_sample = sample_count; loop_segment = i; } sample_count += data->sample_counts[i]; if (!loop_flag && segment_loop_flag) { loop_end_sample = sample_count; loop_flag = 1; } } vgmstream = allocate_vgmstream(channel_count,loop_flag); vgmstream->num_samples = sample_count; vgmstream->sample_rate = sample_rate; vgmstream->loop_start_sample = loop_start_sample; vgmstream->loop_end_sample = loop_end_sample; vgmstream->coding_type = data->adxs[0]->coding_type; vgmstream->layout_type = layout_aax; vgmstream->meta_type = meta_AAX; vgmstream->ch[0].streamfile = streamFileAAX; data->current_segment = 0; data->loop_segment = loop_segment; vgmstream->codec_data = data; free(segment_offset); free(segment_size); return vgmstream; /* clean up anything we may have opened */ fail: if (streamFileAAX) close_streamfile(streamFileAAX); if (streamFileADX) close_streamfile(streamFileADX); if (vgmstream) close_vgmstream(vgmstream); if (segment_offset) free(segment_offset); if (segment_size) free(segment_size); if (data) { if (data->adxs) { int i; for (i=0;i<data->segment_count;i++) if (data->adxs) close_vgmstream(data->adxs[i]); free(data->adxs); } if (data->sample_counts) { free(data->sample_counts); } free(data); } return NULL; }
VGMSTREAM * init_vgmstream_wii_str(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; STREAMFILE * infileSTH = NULL; char filename[PATH_LIMIT]; char * filenameSTH = NULL; int i, j, channel_count, loop_flag; /* check extension, case insensitive */ streamFile->get_name(streamFile,filename,sizeof(filename)); if (strcasecmp("str",filename_extension(filename))) goto fail; /* check for .MIH file */ filenameSTH=(char *)malloc(strlen(filename)+1); if (!filenameSTH) goto fail; strcpy(filenameSTH,filename); strcpy(filenameSTH+strlen(filenameSTH)-3,"sth"); infileSTH = streamFile->open(streamFile,filenameSTH,STREAMFILE_DEFAULT_BUFFER_SIZE); /* STH File is necessary, so we can't confuse those file */ /* with others .STR file as it is a very common extension */ if (!infileSTH) goto fail; if(read_32bitLE(0x2C,infileSTH)!=0) goto fail; channel_count = read_32bitBE(0x70,infileSTH); if(channel_count==1) loop_flag = (read_32bitBE(0xD4,infileSTH)==0x00740000); else loop_flag = (read_32bitBE(0x124,infileSTH)==0x00740000); /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; /* fill in the vital statistics */ vgmstream->channels = channel_count; vgmstream->sample_rate = read_32bitBE(0x38,infileSTH); vgmstream->interleave_block_size=0x8000; vgmstream->num_samples=read_32bitBE(0x34,infileSTH); vgmstream->coding_type = coding_NGC_DSP; vgmstream->layout_type = layout_interleave; vgmstream->meta_type = meta_WII_STR; if(loop_flag) { vgmstream->loop_start_sample = 0; vgmstream->loop_end_sample = vgmstream->num_samples; } /* open the file for reading by each channel */ { for (i=0;i<channel_count;i++) { vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,vgmstream->interleave_block_size); if (!vgmstream->ch[i].streamfile) goto fail; vgmstream->ch[i].channel_start_offset= vgmstream->ch[i].offset+=(off_t)(vgmstream->interleave_block_size*i); for(j=0; j<16; j++) { vgmstream->ch[i].adpcm_coef[j]=read_16bitBE(0xAC+(j*2)+(i*0x50),infileSTH); } } } close_streamfile(infileSTH); infileSTH=NULL; return vgmstream; /* clean up anything we may have opened */ fail: if (infileSTH) close_streamfile(infileSTH); if (filenameSTH) {free(filenameSTH); filenameSTH=NULL;} if (vgmstream) close_vgmstream(vgmstream); return NULL; }
VGMSTREAM * init_vgmstream_sli_ogg(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; STREAMFILE * streamFileOGG = NULL; char filename[PATH_LIMIT]; char filenameOGG[PATH_LIMIT]; char linebuffer[PATH_LIMIT]; off_t bytes_read; off_t sli_offset; int done; int32_t loop_start = -1; int32_t loop_length = -1; int32_t loop_from = -1; int32_t loop_to = -1; /* check extension, case insensitive */ streamFile->get_name(streamFile,filename,sizeof(filename)); if (strcasecmp("sli",filename_extension(filename))) goto fail; /* check for .OGG file */ strcpy(filenameOGG,filename); /* strip off .sli */ filenameOGG[strlen(filenameOGG)-4]='\0'; streamFileOGG = streamFile->open(streamFile,filenameOGG,STREAMFILE_DEFAULT_BUFFER_SIZE); if (!streamFileOGG) { goto fail; } /* let the real initer do the parsing */ vgmstream = init_vgmstream_ogg_vorbis(streamFileOGG); if (!vgmstream) goto fail; close_streamfile(streamFileOGG); streamFileOGG = NULL; sli_offset = 0; while ((loop_start == -1 || loop_length == -1) && sli_offset < get_streamfile_size(streamFile)) { char *endptr; char *foundptr; bytes_read=get_streamfile_dos_line(sizeof(linebuffer),linebuffer,sli_offset,streamFile,&done); if (!done) goto fail; if (!memcmp("LoopStart=",linebuffer,10) && linebuffer[10]!='\0') { loop_start = strtol(linebuffer+10,&endptr,10); if (*endptr != '\0') { /* if it didn't parse cleanly */ loop_start = -1; } } else if (!memcmp("LoopLength=",linebuffer,11) && linebuffer[11]!='\0') { loop_length = strtol(linebuffer+11,&endptr,10); if (*endptr != '\0') { /* if it didn't parse cleanly */ loop_length = -1; } } /* a completely different format, also with .sli extension and can be handled similarly */ if ((foundptr=strstr(linebuffer,"To="))!=NULL && isdigit(foundptr[3])) { loop_to = strtol(foundptr+3,&endptr,10); if (*endptr != ';') { loop_to = -1; } } if ((foundptr=strstr(linebuffer,"From="))!=NULL && isdigit(foundptr[5])) { loop_from = strtol(foundptr+5,&endptr,10); if (*endptr != ';') { loop_from = -1; } } sli_offset += bytes_read; } if ((loop_start != -1 && loop_length != -1) || (loop_to != -1 && loop_from != -1)) { /* install loops */ if (!vgmstream->loop_flag) { vgmstream->loop_flag = 1; vgmstream->loop_ch = calloc(vgmstream->channels, sizeof(VGMSTREAMCHANNEL)); if (!vgmstream->loop_ch) goto fail; } if (loop_to != -1 && loop_from != -1) { vgmstream->loop_start_sample = loop_to; vgmstream->loop_end_sample = loop_from; vgmstream->meta_type = meta_OGG_SLI2; } else { vgmstream->loop_start_sample = loop_start; vgmstream->loop_end_sample = loop_start+loop_length; vgmstream->meta_type = meta_OGG_SLI; } } else goto fail; /* if there's no loop points the .sli wasn't valid */ return vgmstream; /* clean up anything we may have opened */ fail: if (streamFileOGG) close_streamfile(streamFileOGG); if (vgmstream) close_vgmstream(vgmstream); return NULL; }
VGMSTREAM * init_vgmstream_aix(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; STREAMFILE * streamFileAIX = NULL; STREAMFILE * streamFileADX = NULL; char filename[260]; off_t *segment_offset = NULL; int32_t *samples_in_segment = NULL; int32_t sample_count; int loop_flag = 0; int32_t loop_start_sample=0; int32_t loop_end_sample=0; aix_codec_data *data = NULL; off_t first_AIXP; off_t stream_list_offset; off_t stream_list_end; const int segment_list_entry_size = 0x10; const off_t segment_list_offset = 0x20; int stream_count,channel_count,segment_count; int sample_rate; int i; /* check extension, case insensitive */ streamFile->get_name(streamFile,filename,sizeof(filename)); if (strcasecmp("aix",filename_extension(filename))) goto fail; if (read_32bitBE(0x0,streamFile) != 0x41495846 || /* "AIXF" */ read_32bitBE(0x08,streamFile) != 0x01000014 || read_32bitBE(0x0c,streamFile) != 0x00000800) goto fail; first_AIXP = read_32bitBE(0x4,streamFile)+8; segment_count = (uint16_t)read_16bitBE(0x18,streamFile); stream_list_offset = segment_list_offset+segment_list_entry_size*segment_count+0x10; if (stream_list_offset >= first_AIXP) goto fail; if (segment_count < 1) goto fail; sample_rate = read_32bitBE(stream_list_offset+8,streamFile); if (!check_sample_rate(sample_rate)) goto fail; samples_in_segment = calloc(segment_count,sizeof(int32_t)); if (!samples_in_segment) goto fail; segment_offset = calloc(segment_count,sizeof(off_t)); if (!segment_offset) goto fail; for (i = 0; i < segment_count; i++) { segment_offset[i] = read_32bitBE(segment_list_offset+segment_list_entry_size*i+0,streamFile); samples_in_segment[i] = read_32bitBE(segment_list_offset+segment_list_entry_size*i+0x08,streamFile); /*printf("samples_in_segment[%d]=%d\n",i,samples_in_segment[i]);*/ /* all segments must have equal sample rate */ if (read_32bitBE(segment_list_offset+segment_list_entry_size*i+0x0c,streamFile) != sample_rate) { /* segments > 0 can have 0 sample rate (Ryu ga gotoku: Kenzan! tenkei_sng1.aix), seems to indicate same sample rate as first */ if (!(i > 0 && read_32bitBE(segment_list_offset+segment_list_entry_size*i+0x0c,streamFile) == 0)) goto fail; } } if (segment_offset[0] != first_AIXP) goto fail; stream_count = (uint8_t)read_8bit(stream_list_offset,streamFile); if (stream_count < 1) goto fail; stream_list_end = stream_list_offset + 0x8 + stream_count * 8; if (stream_list_end >= first_AIXP) goto fail; channel_count = 0; for (i = 0; i < stream_count; i++) { /* all streams must have same samplerate as segments */ if (read_32bitBE(stream_list_offset+8+i*8,streamFile)!=sample_rate) goto fail; channel_count += read_8bit(stream_list_offset+8+i*8+4,streamFile); } /* check for existence of segments */ for (i = 0; i < segment_count; i++) { int j; off_t AIXP_offset = segment_offset[i]; for (j = 0; j < stream_count; j++) { if (read_32bitBE(AIXP_offset,streamFile)!=0x41495850) /* "AIXP" */ goto fail; if (read_8bit(AIXP_offset+8,streamFile)!=j) goto fail; AIXP_offset += read_32bitBE(AIXP_offset+4,streamFile)+8; } } /*streamFileAIX = streamFile->open(streamFile,filename,sample_rate*0.0375*2/32*18segment_count);*/ streamFileAIX = streamFile->open(streamFile,filename,sample_rate*0.1*segment_count); if (!streamFileAIX) goto fail; data = malloc(sizeof(aix_codec_data)); if (!data) goto fail; data->segment_count = segment_count; data->stream_count = stream_count; data->adxs = malloc(sizeof(STREAMFILE *)*segment_count*stream_count); if (!data->adxs) goto fail; for (i=0;i<segment_count*stream_count;i++) { data->adxs[i] = NULL; } data->sample_counts = calloc(segment_count,sizeof(int32_t)); if (!data->sample_counts) goto fail; memcpy(data->sample_counts,samples_in_segment,segment_count*sizeof(int32_t)); /* for each segment */ for (i = 0; i < segment_count; i++) { int j; /* for each stream */ for (j = 0; j < stream_count; j++) { VGMSTREAM *adx; /*printf("try opening segment %d/%d stream %d/%d %x\n",i,segment_count,j,stream_count,segment_offset[i]);*/ streamFileADX = open_aix_with_STREAMFILE(streamFileAIX,segment_offset[i],j); if (!streamFileADX) goto fail; adx = data->adxs[i*stream_count+j] = init_vgmstream_adx(streamFileADX); if (!adx) goto fail; close_streamfile(streamFileADX); streamFileADX = NULL; if (adx->num_samples != data->sample_counts[i] || adx->loop_flag != 0) goto fail; /* save start things so we can restart for seeking/looping */ /* copy the channels */ memcpy(adx->start_ch,adx->ch,sizeof(VGMSTREAMCHANNEL)*adx->channels); /* copy the whole VGMSTREAM */ memcpy(adx->start_vgmstream,adx,sizeof(VGMSTREAM)); } } if (segment_count > 1) { loop_flag = 1; } sample_count = 0; for (i = 0; i < segment_count; i++) { sample_count += data->sample_counts[i]; if (i == 0) loop_start_sample = sample_count; if (i == 1) loop_end_sample = sample_count; } vgmstream = allocate_vgmstream(channel_count,loop_flag); vgmstream->num_samples = sample_count; vgmstream->sample_rate = sample_rate; vgmstream->loop_start_sample = loop_start_sample; vgmstream->loop_end_sample = loop_end_sample; vgmstream->coding_type = data->adxs[0]->coding_type; vgmstream->layout_type = layout_aix; vgmstream->meta_type = meta_AIX; vgmstream->ch[0].streamfile = streamFileAIX; data->current_segment = 0; vgmstream->codec_data = data; free(segment_offset); free(samples_in_segment); return vgmstream; /* clean up anything we may have opened */ fail: if (streamFileAIX) close_streamfile(streamFileAIX); if (streamFileADX) close_streamfile(streamFileADX); if (vgmstream) close_vgmstream(vgmstream); if (samples_in_segment) free(samples_in_segment); if (segment_offset) free(segment_offset); if (data) { if (data->adxs) { int i; for (i=0;i<data->segment_count*data->stream_count;i++) if (data->adxs) close_vgmstream(data->adxs[i]); free(data->adxs); } if (data->sample_counts) { free(data->sample_counts); } free(data); } return NULL; }