Example #1
0
/* 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;
}
Example #2
0
/* 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);
}
Example #3
0
/* 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;
}
Example #4
0
/* .XPS+DAT - From Software games streams [Metal Wolf Chaos (Xbox), Otogi (Xbox)] */
VGMSTREAM * init_vgmstream_xps_dat(STREAMFILE *streamFile) {
    VGMSTREAM * vgmstream = NULL;
    STREAMFILE * streamData = NULL;
    off_t start_offset, header_offset;
    size_t stream_size;
    int loop_flag, channel_count, sample_rate, codec, loop_start_sample, loop_end_sample, file_id;
    int total_subsongs, target_subsong = streamFile->stream_index;


    /* checks */
    if (!check_extensions(streamFile, "xps"))
        goto fail;

    if (read_32bitLE(0x00,streamFile) != get_streamfile_size(streamFile))
        goto fail;
    if (read_32bitBE(0x0c,streamFile) != 0x64696666)  /* "diff" */
        goto fail;

    /* handle .xps+dat (bank .xps are done below) */
    streamData = open_streamfile_by_ext(streamFile, "dat");
    if (!streamData) goto fail;

    /* 0x00: approximate file size */

    total_subsongs = read_32bitLE(0x04,streamData);
    if (target_subsong == 0) target_subsong = 1;
    if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;

    header_offset = 0x20 + 0x94*(target_subsong-1); /* could start at 0x0c too */

    file_id             = read_32bitLE(header_offset+0x00,streamData);
    start_offset        = read_32bitLE(header_offset+0x04,streamData);
    stream_size         = read_32bitLE(header_offset+0x08,streamData);
    /* 0x0c: loop start offset? */
    /* 0x10: loop end offset? */
    /* 0x14: always null? */
    codec               = read_16bitLE(header_offset+0x18,streamData);
    channel_count       = read_16bitLE(header_offset+0x1a,streamData);
    sample_rate         = read_32bitLE(header_offset+0x1c,streamData);
    /* 0x20: average bitrate */
    /* 0x24: block size, bps */
    loop_flag           = read_32bitLE(header_offset+0x5c,streamData);
    loop_start_sample   = read_32bitLE(header_offset+0x6c,streamData);
    loop_end_sample     = read_32bitLE(header_offset+0x70,streamData) + 1; /* a "smpl" chunk basically */


    /* build the VGMSTREAM */
    vgmstream = allocate_vgmstream(channel_count, loop_flag);
    if (!vgmstream) goto fail;

    vgmstream->sample_rate = sample_rate;
    vgmstream->meta_type = meta_XPS_DAT;
    vgmstream->loop_start_sample = loop_start_sample;
    vgmstream->loop_end_sample = loop_end_sample;
    vgmstream->num_streams = total_subsongs;
    vgmstream->stream_size = stream_size;

    switch(codec) {
        case 0x01:
            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);
            break;

        case 0x69:
            vgmstream->coding_type = coding_XBOX_IMA;
            vgmstream->layout_type = layout_none;
            vgmstream->num_samples = xbox_ima_bytes_to_samples(stream_size, channel_count);
            break;

        default:
            goto fail;
    }

    read_xps_name(vgmstream, streamFile, file_id);

    if (!vgmstream_open_stream(vgmstream,streamData,start_offset))
        goto fail;

    close_streamfile(streamData);
    return vgmstream;

fail:
    close_streamfile(streamData);
    close_vgmstream(vgmstream);
    return NULL;
}
Example #5
0
/* .XPS - From Software games banks [Metal Wolf Chaos (Xbox), Otogi (Xbox)] */
VGMSTREAM * init_vgmstream_xps(STREAMFILE *streamFile) {
    VGMSTREAM * vgmstream = NULL;
    STREAMFILE * streamData = NULL;
    int i, entries;
    off_t entry_offset = 0x10;
    int total_subsongs, target_subsong = streamFile->stream_index;


    /* checks */
    if (!check_extensions(streamFile, "xps"))
        goto fail;

    if (read_32bitLE(0x00,streamFile) != get_streamfile_size(streamFile))
        goto fail;
    if (read_32bitBE(0x0c,streamFile) != 0x64696666)  /* "diff" */
        goto fail;

    /* handle .xps alone (stream .xps+data are done above) */
    streamData = open_streamfile_by_ext(streamFile, "dat");
    if (streamData) goto fail;

    /* main section + bank sections (usually same number but not always) */
    entries = read_32bitLE(0x04,streamFile);

    total_subsongs = 0;
    if (target_subsong == 0) target_subsong = 1;
    if (target_subsong < 0 /*|| target_subsong > total_subsongs || total_subsongs < 1*/) goto fail;


    /* parse entries: skip (there is probably a stream/bank flag here) */
    for (i = 0; i < entries; i++) {
        off_t entry_base  = entry_offset;
        size_t entry_size = read_32bitLE(entry_base+0x00,streamFile);
        uint32_t entry_id = read_32bitBE(entry_base+0x04,streamFile);
        size_t entry_pad  = read_32bitLE(entry_base+0x08,streamFile);
        /* 0x0c: always null, rest: entry (format varies) */

        entry_offset += entry_size + entry_pad + 0x10;

        /* sound info entry */
        if (entry_id == 0x73696400) { /* "sid\0" */
            /* keep looking for sound banks */
            continue;
        }

        /* sound bank entry, otherwise no good */
        if (entry_id != 0x75647362) { /* "udsb" */
            goto fail;
        }

        total_subsongs++;

        /* open internal RIFF */
        if (target_subsong == total_subsongs && vgmstream == NULL) {
            STREAMFILE* temp_streamFile;
            off_t subsong_offset = entry_base+0x18;
            size_t subsong_size  = read_32bitLE(entry_base+0x14,streamFile);

            temp_streamFile = setup_subfile_streamfile(streamFile, subsong_offset,subsong_size, "wav");
            if (!temp_streamFile) goto fail;

            vgmstream = init_vgmstream_riff(temp_streamFile);
            close_streamfile(temp_streamFile);
            if (!vgmstream) goto fail;

        }
    }

    /* subsong not found */
    if (!vgmstream)
        goto fail;

    vgmstream->num_streams = total_subsongs;
    return vgmstream;

fail:
    close_streamfile(streamData);
    close_vgmstream(vgmstream);
    return NULL;
}