Ejemplo n.º 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;
}
Ejemplo n.º 2
0
/* 04SW - found in Driver: Parallel Lines (Wii) */
VGMSTREAM * init_vgmstream_wii_04sw(STREAMFILE *streamFile) {
    VGMSTREAM * vgmstream = NULL;
    off_t start_offset;
    int loop_flag, channel_count;
    size_t file_size, data_size;


    /* checks */
    /* ".04sw" is just the ID, the real filename inside the file uses .XA */
    if (!check_extensions(streamFile,"xa,04sw"))
        goto fail;
    if (read_32bitBE(0x00,streamFile) != 0x30345357) /* "04SW" */
        goto fail;

    /* after the ID goes a semi-standard DSP header */
    if (read_32bitBE(0x10,streamFile) != 0) goto fail; /* should be non looping */
    loop_flag = 0;
    /* not in header it seems so just dual header check */
    channel_count = (read_32bitBE(0x04,streamFile) == read_32bitBE(0x64,streamFile)) ? 2 : 1;

    start_offset = read_32bitBE(0x04 + 0x60*channel_count,streamFile);

    file_size = get_streamfile_size(streamFile);
    data_size = read_32bitBE(0x04 + 0x60*channel_count + 0x04,streamFile);
    if (data_size+start_offset != file_size) goto fail;


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

    vgmstream->sample_rate = read_32bitBE(0x0c,streamFile);
    vgmstream->num_samples = read_32bitBE(0x04,streamFile);

    vgmstream->coding_type = coding_NGC_DSP;
    vgmstream->layout_type = channel_count == 1 ? layout_none : layout_interleave;
    vgmstream->interleave_block_size = 0x8000;
    vgmstream->interleave_last_block_size = (read_32bitBE(0x08,streamFile) / 2 % vgmstream->interleave_block_size + 7) / 8 * 8;

    dsp_read_coefs_be(vgmstream,streamFile,0x20, 0x60);
    /* the initial history offset seems different thatn standard DSP and possibly always zero */

    vgmstream->meta_type = meta_WII_04SW;
    /* the rest of the header has unknown values (several repeats) and the filename */


    if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
        goto fail;
    return vgmstream;

fail:
    close_vgmstream(vgmstream);
    return NULL;
}
Ejemplo n.º 3
0
/* MSA (from Psyvariar -Complete Edition-) */
VGMSTREAM * init_vgmstream_ps2_msa(STREAMFILE *streamFile) {
    VGMSTREAM * vgmstream = NULL;
    off_t start_offset, datasize, filesize;
    int loop_flag, channel_count;

    /* check extension, case insensitive */
    if (!check_extensions(streamFile, "msa")) goto fail;

    /* check header */
    if (read_32bitBE(0x00,streamFile) != 0x00000000)
        goto fail;

    loop_flag = 0;
    channel_count = 2;

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

    start_offset = 0x14;
    datasize = read_32bitLE(0x4,streamFile);
    filesize = get_streamfile_size(streamFile);
    vgmstream->channels = channel_count;
    vgmstream->sample_rate = read_32bitLE(0x10,streamFile);
    vgmstream->num_samples = datasize*28/(16*channel_count);
    vgmstream->coding_type = coding_PSX;
    vgmstream->layout_type = layout_interleave;
    vgmstream->interleave_block_size = 0x4000;
    vgmstream->meta_type = meta_PS2_MSA;

    /* MSAs are strangely truncated, so manually calculate samples
     *  data after last usable block is always silence or garbage */
    if (datasize > filesize) {
        off_t usable_size = filesize - start_offset;
        usable_size -= usable_size % (vgmstream->interleave_block_size*channel_count);/* block-aligned */
        vgmstream->num_samples = usable_size * 28 / (16*channel_count);
    }


    /* open the file for reading */
    if (!vgmstream_open_stream(vgmstream, streamFile, start_offset))
        goto fail;
    return vgmstream;

fail:
    close_vgmstream(vgmstream);
    return NULL;
}
Ejemplo n.º 4
0
/* .ASS - from Dai Senryaku VII: Exceed (PS2) */
VGMSTREAM * init_vgmstream_ps2_ass(STREAMFILE *streamFile) {
    VGMSTREAM * vgmstream = NULL;
    off_t start_offset;
    size_t channel_size, interleave;
    int loop_flag, channel_count, sample_rate;
    int32_t num_samples, loop_start = 0, loop_end = 0;


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

    start_offset = 0x800;
    channel_count = read_32bitLE(0x00,streamFile); /* assumed */
    if (channel_count != 2) goto fail;
    sample_rate = read_32bitLE(0x04,streamFile);
    channel_size = read_32bitLE(0x08,streamFile);
    interleave = read_32bitLE(0x0c,streamFile);
    num_samples = ps_bytes_to_samples(channel_size,1);

    loop_flag = ps_find_loop_offsets(streamFile, start_offset, channel_size*channel_count, channel_count, interleave, &loop_start, &loop_end);
    loop_flag = loop_flag && (num_samples > 10*sample_rate); /* disable looping for smaller files (in seconds) */


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

    vgmstream->meta_type = meta_PS2_ASS;
    vgmstream->sample_rate = sample_rate;
    vgmstream->num_samples = num_samples;
    vgmstream->loop_start_sample = loop_start;
    vgmstream->loop_end_sample = loop_end;

    vgmstream->coding_type = coding_PSX;
    vgmstream->layout_type = layout_interleave;
    vgmstream->interleave_block_size = interleave;

    if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
        goto fail;
    return vgmstream;

fail:
    close_vgmstream(vgmstream);
    return NULL;
}
Ejemplo n.º 5
0
/* .SMV - from Cho Aniki Zero (PSP) */
VGMSTREAM * init_vgmstream_smv(STREAMFILE *streamFile) {
    VGMSTREAM * vgmstream = NULL;
    off_t start_offset;
    int loop_flag, channel_count;
    size_t channel_size, loop_start;


    /* check extension */
    if (!check_extensions(streamFile, "smv"))
        goto fail;

    channel_size = read_32bitLE(0x00,streamFile);
    /* 0x08: number of full interleave blocks */
    channel_count = read_16bitLE(0x0a,streamFile);
    loop_start = read_32bitLE(0x18,streamFile);
    loop_flag = (loop_start != -1);
    start_offset = 0x800;

    if (channel_size * channel_count + start_offset != get_streamfile_size(streamFile))
        goto fail;

    channel_size -= 0x10; /* last value has SPU end frame without flag 0x7 as it should */

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

    vgmstream->sample_rate = read_32bitLE(0x10, streamFile);
    vgmstream->num_samples = ps_bytes_to_samples(channel_size*channel_count, channel_count);
    vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start*channel_count, channel_count);
    vgmstream->loop_end_sample = vgmstream->num_samples;

    vgmstream->meta_type = meta_SMV;
    vgmstream->coding_type = coding_PSX;
    vgmstream->layout_type = layout_interleave;
    vgmstream->interleave_block_size = read_32bitLE(0x04, streamFile);
    vgmstream->interleave_last_block_size = read_32bitLE(0x0c, streamFile);

    if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
        goto fail;
    return vgmstream;

fail:
    close_vgmstream(vgmstream);
    return NULL;
}
Ejemplo n.º 6
0
/* NXAP - Nex Entertainment header [Time Crisis 4 (PS3), Time Crisis Razing Storm (PS3)] */
VGMSTREAM * init_vgmstream_nxap(STREAMFILE *streamFile) {
    VGMSTREAM * vgmstream = NULL;
    off_t start_offset;
    int loop_flag, channel_count;


    /* checks */
    if (!check_extensions(streamFile, "adp"))
        goto fail;
    if (read_32bitBE(0x00,streamFile) != 0x4E584150) /* "NXAP" */
        goto fail;
    if (read_32bitLE(0x14,streamFile) != 0x40 ||    /* expected frame size? */
        read_32bitLE(0x18,streamFile) != 0x40)      /* expected interleave? */
        goto fail;

    start_offset = read_32bitLE(0x04,streamFile);
    channel_count = read_32bitLE(0x0c,streamFile);
    loop_flag = 0; //(read_32bitLE(0x24,streamFile) > 0); //todo


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

    vgmstream->sample_rate = read_32bitLE(0x10, streamFile);
    vgmstream->num_samples = read_32bitLE(0x1c,streamFile) * (0x40-0x04)*2 / channel_count; /* number of frames */

    /* unknown loop format, also 0x28/2c values seem related */
    //vgmstream->loop_start_sample = read_32bitLE(0x20,streamFile) * (0x40-0x04)*2 / channel_count;
    //vgmstream->loop_end_sample = read_32bitLE(0x24,streamFile) * (0x40-0x04)*2 / channel_count;
    //vgmstream->loop_end_sample = vgmstream->loop_start_sample + vgmstream->loop_end_sample;

    vgmstream->meta_type = meta_NXAP;
    vgmstream->coding_type = coding_NXAP;
    vgmstream->layout_type = layout_interleave;
    vgmstream->interleave_block_size = 0x40;

    if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
        goto fail;
    return vgmstream;

fail:
    close_vgmstream(vgmstream);
    return NULL;
}
Ejemplo n.º 7
0
/* .RXW - legacy fake ext/header for poorly split XWH+XWB files generated by old tools (incorrect header/chunk sizes) */
VGMSTREAM * init_vgmstream_ps2_rxw(STREAMFILE *streamFile) {
    VGMSTREAM * vgmstream = NULL;
    int loop_flag=0, channel_count;
    off_t start_offset;

    /* check extension, case insensitive */
    if (!check_extensions(streamFile,"rxw")) goto fail;

    /* check RXWS/FORM Header */
    if (!((read_32bitBE(0x00,streamFile) == 0x52585753) && 
          (read_32bitBE(0x10,streamFile) == 0x464F524D)))
        goto fail;

    loop_flag = (read_32bitLE(0x3C,streamFile)!=0xFFFFFFFF);
    channel_count=2; /* Always stereo files */
    
    /* build the VGMSTREAM */
    vgmstream = allocate_vgmstream(channel_count,loop_flag);
    if (!vgmstream) goto fail;

    vgmstream->sample_rate = read_32bitLE(0x2E,streamFile);
    vgmstream->num_samples = (read_32bitLE(0x38,streamFile)*28/16)/2;

    /* Get loop point values */
    if(vgmstream->loop_flag) {
        vgmstream->loop_start_sample = read_32bitLE(0x3C,streamFile)/16*14;
        vgmstream->loop_end_sample = read_32bitLE(0x38,streamFile)/16*14;
    }

    vgmstream->interleave_block_size = read_32bitLE(0x1c,streamFile)+0x10;
    vgmstream->coding_type = coding_PSX;
    vgmstream->layout_type = layout_interleave;
    vgmstream->meta_type = meta_PS2_RXWS;
    start_offset = 0x40;

    /* open the file for reading */
    if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
        goto fail;

    return vgmstream;

fail:
    close_vgmstream(vgmstream);
    return NULL;
}
Ejemplo n.º 8
0
/* STRM - from Final Fantasy Tactics A2 (NDS) */
VGMSTREAM * init_vgmstream_nds_strm_ffta2(STREAMFILE *streamFile) {
    VGMSTREAM * vgmstream = NULL;
    off_t start_offset;
    int loop_flag, channel_count;


    /* checks*/
    /* .bin: actual extension
     * .strm: header id */
    if (!check_extensions(streamFile,"bin,strm"))
        goto fail;

    /* check header */
    if (read_32bitBE(0x00,streamFile) != 0x52494646 ||  /* "RIFF" */
        read_32bitBE(0x08,streamFile) != 0x494D4120)    /* "IMA " */
        goto fail;

    loop_flag = (read_32bitLE(0x20,streamFile) !=0);
    channel_count = read_32bitLE(0x24,streamFile);
    start_offset = 0x2C;

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

    vgmstream->channels = channel_count;
    vgmstream->sample_rate = read_32bitLE(0x0C,streamFile);
    vgmstream->num_samples = (read_32bitLE(0x04,streamFile)-start_offset);
    vgmstream->loop_start_sample = read_32bitLE(0x20,streamFile);
    vgmstream->loop_end_sample = read_32bitLE(0x28,streamFile);

    vgmstream->meta_type = meta_NDS_STRM_FFTA2;

    vgmstream->coding_type = coding_FFTA2_IMA;
    vgmstream->layout_type = layout_interleave;
    vgmstream->interleave_block_size = 0x80;

    if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
        goto fail;
    return vgmstream;

fail:
    close_vgmstream(vgmstream);
    return NULL;
}
Ejemplo n.º 9
0
/* .PCM - KCE Japan East PS2 games (Ephemeral Fantasia, Yu-Gi-Oh! The Duelists of the Roses, 7 Blades) */
VGMSTREAM * init_vgmstream_ps2_pcm(STREAMFILE *streamFile) {
    VGMSTREAM * vgmstream = NULL;
    off_t start_offset;
    int loop_flag, channel_count;

    /* check extension */
    if ( !check_extensions(streamFile,"pcm") )
        goto fail;

    /* check header (data_size vs num_samples) */
    if (pcm_bytes_to_samples(read_32bitLE(0x00,streamFile), 2, 16) != read_32bitLE(0x04,streamFile))
        goto fail;
    /* should work too */
    //if (read_32bitLE(0x00,streamFile)+0x800 != get_streamfile_size(streamFile))
    //    goto fail;

    loop_flag = (read_32bitLE(0x0C,streamFile) != 0x00);
    channel_count = 2;
    start_offset = 0x800;

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

    vgmstream->channels = channel_count;
    vgmstream->sample_rate = 24000;
    vgmstream->num_samples = read_32bitLE(0x04,streamFile);
    vgmstream->loop_start_sample = read_32bitLE(0x08,streamFile);
    vgmstream->loop_end_sample = read_32bitLE(0x0C,streamFile);

    vgmstream->coding_type = coding_PCM16LE;
    vgmstream->layout_type = layout_interleave;
    vgmstream->interleave_block_size = 0x2;
    vgmstream->meta_type = meta_PS2_PCM;

    /* open the file for reading */
    if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
        goto fail;
    return vgmstream;

fail:
    if (vgmstream) close_vgmstream(vgmstream);
    return NULL;
}
Ejemplo n.º 10
0
/* MUSC - from Krome's PS2 games (The Legend of Spyro, Ty the Tasmanian Tiger) */
VGMSTREAM * init_vgmstream_musc(STREAMFILE *streamFile) {
    VGMSTREAM * vgmstream = NULL;
    int loop_flag, channel_count;
    off_t start_offset;
    size_t data_size;

    /* .mus is the real extension, .musc is the header ID */
    if (!check_extensions(streamFile,"mus,musc"))
        goto fail;
    if (read_32bitBE(0x00,streamFile) != 0x4D555343) /* "MUSC" */
        goto fail;

    start_offset = read_32bitLE(0x10,streamFile);
    data_size    = read_32bitLE(0x14,streamFile);
    if (start_offset + data_size != get_streamfile_size(streamFile))
        goto fail;
    /* always does full loops unless it ends in silence */
    loop_flag = read_32bitBE(get_streamfile_size(streamFile) - 0x10,streamFile) != 0x0C000000;
    channel_count = 2;


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

    vgmstream->sample_rate = (uint16_t)read_16bitLE(0x06,streamFile);
    vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count);
    vgmstream->loop_start_sample = 0;
    vgmstream->loop_end_sample = vgmstream->num_samples;

    vgmstream->meta_type = meta_MUSC;
    vgmstream->coding_type = coding_PSX;
    vgmstream->layout_type = layout_interleave;
    vgmstream->interleave_block_size = read_32bitLE(0x18,streamFile) / 2;

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

    return vgmstream;

fail:
    close_vgmstream(vgmstream);
    return NULL;
}
Ejemplo n.º 11
0
/* .208 - from Ocean game(s?) [Last Rites (PC)] */
VGMSTREAM * init_vgmstream_208(STREAMFILE *streamFile) {
    VGMSTREAM * vgmstream = NULL;
    off_t start_offset;
    int loop_flag, channel_count, sample_rate;
    size_t data_size;


    /* checks */
    if (!check_extensions(streamFile, "208"))
        goto fail;
    /* possible validation: (0x04 == 0 and 0xcc == 0x1F7D984D) or 0x04 == 0xf0 and 0xcc == 0) */
    if (!((read_32bitLE(0x04,streamFile) == 0x00 && read_32bitBE(0xcc,streamFile) == 0x1F7D984D) ||
          (read_32bitLE(0x04,streamFile) == 0xF0 && read_32bitBE(0xcc,streamFile) == 0x00000000)))
        goto fail;

    start_offset    = read_32bitLE(0x00,streamFile);
    data_size       = read_32bitLE(0x0c,streamFile);
    sample_rate     = read_32bitLE(0x34,streamFile);
    channel_count   = read_32bitLE(0x3C,streamFile); /* assumed */
    loop_flag = 0;

    if (start_offset + data_size != get_streamfile_size(streamFile))
        goto fail;


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

    vgmstream->meta_type = meta_208;
    vgmstream->sample_rate = sample_rate;
    vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, 8);
    vgmstream->coding_type = coding_PCM8_U;
    vgmstream->layout_type = layout_interleave;
    vgmstream->interleave_block_size = 0x1;

    if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
        goto fail;
    return vgmstream;

fail:
    close_vgmstream(vgmstream);
    return NULL;
}
Ejemplo n.º 12
0
/* .VAI - from Asobo Studio games [Ratatouille (GC)] */
VGMSTREAM * init_vgmstream_vai(STREAMFILE *streamFile) {
    VGMSTREAM * vgmstream = NULL;
    off_t start_offset;
    size_t data_size;
    int loop_flag, channel_count;


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

    start_offset = 0x4060;
    data_size = get_streamfile_size(streamFile) - start_offset;
    if (read_32bitBE(0x04,streamFile) != data_size)
        goto fail;

    channel_count = 2;
    loop_flag = 0;


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

    vgmstream->meta_type = meta_VAI;
    vgmstream->sample_rate = read_32bitBE(0x00,streamFile);
    vgmstream->num_samples = dsp_bytes_to_samples(data_size,channel_count);

    vgmstream->coding_type = coding_NGC_DSP;
    vgmstream->layout_type = layout_interleave;
    vgmstream->interleave_block_size = 0x4000;
    dsp_read_coefs_be(vgmstream,streamFile,0x0c,0x20);

    if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
        goto fail;
    return vgmstream;

fail:
    close_vgmstream(vgmstream);
    return NULL;
}
Ejemplo n.º 13
0
VGMSTREAM * init_vgmstream_ngc_adpdtk(STREAMFILE *streamFile) {
    VGMSTREAM * vgmstream = NULL;
    off_t start_offset = 0;
    int channel_count = 2, loop_flag = 0; /* always stereo, no loop */

    /* check extension, case insensitive */
    if ( !check_extensions(streamFile,"dtk,adp"))
        goto fail;

    /* .adp files have no header, and the ext is common, so all we can do is look for valid first frames */
    if (check_extensions(streamFile,"adp")) {
        int i;
        for (i = 0; i < 10; i++) { /* try a bunch of frames */
            if (read_8bit(0x00 + i*0x20,streamFile) != read_8bit(0x02 + i*0x20,streamFile) ||
                read_8bit(0x01 + i*0x20,streamFile) != read_8bit(0x03 + i*0x20,streamFile))
                goto fail;
        }
    }


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

    vgmstream->num_samples = get_streamfile_size(streamFile) / 32 * 28;
    vgmstream->sample_rate = 48000;
    vgmstream->coding_type = coding_NGC_DTK;
    vgmstream->layout_type = layout_none;
    vgmstream->meta_type = meta_NGC_ADPDTK;


    /* open the file for reading */
    if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
        goto fail;

    return vgmstream;

fail:
    close_vgmstream(vgmstream);
    return NULL;
}
Ejemplo n.º 14
0
/* A2M - from Artificial Mind & Movement games [Scooby-Doo! Unmasked (PS2)] */
VGMSTREAM * init_vgmstream_a2m(STREAMFILE *streamFile) {
    VGMSTREAM * vgmstream = NULL;
    off_t start_offset;
    size_t data_size;
    int loop_flag, channel_count;


    /* checks */
    if ( !check_extensions(streamFile,"int") )
        goto fail;
    if (read_32bitBE(0x00,streamFile) != 0x41324D00) /* "A2M\0" */
        goto fail;
    if (read_32bitBE(0x04,streamFile) != 0x50533200) /* "PS2\0" */
        goto fail;

    start_offset = 0x30;
    data_size = get_streamfile_size(streamFile) - start_offset;
    channel_count = 2;
    loop_flag = 0;


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

    vgmstream->meta_type = meta_A2M;
    vgmstream->sample_rate = read_32bitBE(0x10,streamFile);
    vgmstream->num_samples = ps_bytes_to_samples(data_size,channel_count);

    vgmstream->coding_type = coding_PSX;
    vgmstream->layout_type = layout_interleave;
    vgmstream->interleave_block_size = 0x6000;

    if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
        goto fail;
    return vgmstream;

fail:
    close_vgmstream(vgmstream);
    return NULL;
}
Ejemplo n.º 15
0
/* ASTL - found in Dead Rising (PC) */
VGMSTREAM * init_vgmstream_pc_ast(STREAMFILE *streamFile) {
    VGMSTREAM * vgmstream = NULL;
	off_t start_offset, data_size;
    int loop_flag, channel_count;

    /* check extension, case insensitive */
    if ( !check_extensions(streamFile,"ast"))
        goto fail;

    if (read_32bitBE(0x00,streamFile) != 0x4153544C) /* "ASTL" */
        goto fail;


    loop_flag = 0; //TODO - Find hidden loop point calc and flag
	channel_count = read_8bit(0x32, streamFile);
	data_size = read_32bitLE(0x20,streamFile);


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

	/* TODO - Find non-obvious loop points and flag (if any) */
    start_offset = read_32bitLE(0x10,streamFile);
    vgmstream->sample_rate = read_32bitLE(0x34,streamFile);
	vgmstream->coding_type = coding_PCM16LE;
    vgmstream->num_samples = data_size/(channel_count*2);
	vgmstream->layout_type = layout_interleave;
	vgmstream->interleave_block_size = 0x2;
    vgmstream->meta_type = meta_PC_AST;

    /* open the file for reading */
    if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
        goto fail;
    return vgmstream;

fail:
    close_vgmstream(vgmstream);
    return NULL;
}
Ejemplo n.º 16
0
/* Maxis XA - found in Sim City 3000 (PC) */
VGMSTREAM * init_vgmstream_maxis_xa(STREAMFILE *streamFile) {
    VGMSTREAM * vgmstream = NULL;
    off_t start_offset;
    int loop_flag, channel_count;

    /* check extension, case insensitive */
    if (!check_extensions(streamFile,"xa"))
        goto fail;

    /* check header */
    if ((read_32bitBE(0x00,streamFile) != 0x58414900) && /* "XAI\0" */
        (read_32bitBE(0x00,streamFile) != 0x58414A00))   /* "XAJ\0" (some odd song uses this, no apparent diffs) */
        goto fail;

    loop_flag = 0;
    channel_count = read_16bitLE(0x0A,streamFile);
    start_offset = 0x18;

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

    vgmstream->sample_rate = read_32bitLE(0x0C,streamFile);
    vgmstream->num_samples = read_32bitLE(0x04,streamFile)/2/channel_count;

    vgmstream->meta_type = meta_MAXIS_XA;
    vgmstream->coding_type = coding_MAXIS_XA;
    vgmstream->layout_type = layout_none;

    /* open streams */
    if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
        goto fail;
    return vgmstream;

fail:
    close_vgmstream(vgmstream);
    return NULL;
}
Ejemplo n.º 17
0
/* AL" - headerless a-law, found in Conquest of Elysium 3 (PC) */
VGMSTREAM * init_vgmstream_pc_al2(STREAMFILE *streamFile) {
    VGMSTREAM * vgmstream = NULL;
    off_t start_offset;
    int loop_flag = 0, channel_count;


    if ( !check_extensions(streamFile,"al2"))
        goto fail;

    channel_count = 2;

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

    vgmstream->sample_rate = 22050;
    vgmstream->coding_type = coding_ALAW;
    vgmstream->layout_type = layout_interleave;
    vgmstream->interleave_block_size = 0x01;
    vgmstream->meta_type = meta_PC_AL2;
    vgmstream->num_samples = pcm_bytes_to_samples(get_streamfile_size(streamFile), channel_count, 8);
    if (loop_flag) {
        vgmstream->loop_start_sample = 0;
        vgmstream->loop_end_sample = vgmstream->num_samples;
    }

    start_offset = 0;

    if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
        goto fail;
    return vgmstream;

fail:
    close_vgmstream(vgmstream);
    return NULL;
}
Ejemplo n.º 18
0
/* .XWC - Starbreeze games [Chronicles of Riddick: Assault on Dark Athena, Syndicate] */
VGMSTREAM * init_vgmstream_xwc(STREAMFILE *streamFile) {
    VGMSTREAM * vgmstream = NULL;
	off_t start_offset, extra_offset;
	size_t data_size;
    int loop_flag, channel_count, codec, num_samples;


    /* checks */
    /* .xwc: extension of the bigfile, individual files don't have one */
    if ( !check_extensions(streamFile,"xwc"))
        goto fail;


    /* version */
    if (read_32bitBE(0x00,streamFile) == 0x00030000 &&
        read_32bitBE(0x04,streamFile) == 0x00900000) { /* The Darkness */
        data_size = read_32bitLE(0x08, streamFile) + 0x1c; /* not including subheader */
        channel_count = read_32bitLE(0x0c, streamFile);
        /* 0x10: num_samples */
        /* 0x14: 0x8000? */
        /* 0x18: null */
        codec = read_32bitBE(0x1c, streamFile);
        num_samples = read_32bitLE(0x20, streamFile);
        /* 0x24: config data >> 2? (0x00(1): channels; 0x01(2): ?, 0x03(2): sample_rate) */
        extra_offset = 0x28;
    }
    else if (read_32bitBE(0x00,streamFile) == 0x00040000 &&
             read_32bitBE(0x04,streamFile) == 0x00900000) { /* Riddick, Syndicate */
        data_size = read_32bitLE(0x08, streamFile) + 0x24; /* not including subheader */
        channel_count = read_32bitLE(0x0c, streamFile);
        /* 0x10: num_samples */
        /* 0x14: 0x8000? */
        codec = read_32bitBE(0x24, streamFile);
        num_samples = read_32bitLE(0x28, streamFile);
        /* 0x2c: config data >> 2? (0x00(1): channels; 0x01(2): ?, 0x03(2): sample_rate) */
        /* 0x30+: codec dependant */
        extra_offset = 0x30;
    }
    else {
        goto fail;
    }

    loop_flag = 0;


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

    vgmstream->meta_type = meta_XWC;
    vgmstream->num_samples = num_samples;

    switch(codec) {
#ifdef VGM_USE_MPEG
        case 0x4D504547: { /* "MPEG" (PS3) */
            mpeg_custom_config cfg = {0};

            start_offset = 0x800;
            vgmstream->num_samples = read_32bitLE(extra_offset+0x00, streamFile); /* with encoder delay */ //todo improve
            cfg.data_size = read_32bitLE(extra_offset+0x04, streamFile); /* without padding */

            vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg);
            if (!vgmstream->codec_data) goto fail;
            vgmstream->layout_type = layout_none;

            vgmstream->sample_rate = ((mpeg_codec_data*)vgmstream->codec_data)->sample_rate_per_frame;
            break;
        }
#endif
#ifdef VGM_USE_FFMPEG
        case 0x584D4100: { /* "XMA\0" (X360) */
            uint8_t buf[0x100];
            int32_t bytes, seek_size, block_size, block_count, sample_rate;

            seek_size = read_32bitLE(extra_offset+0x00, streamFile);
            start_offset = extra_offset+0x04 + seek_size + read_32bitLE(extra_offset+0x04+seek_size, streamFile) + 0x08;
            start_offset += (start_offset % 0x800) ? 0x800 - (start_offset % 0x800) : 0; /* padded */
            data_size = data_size - start_offset;

            sample_rate = read_32bitBE(extra_offset+0x04+seek_size+0x10, streamFile);
            block_size  = read_32bitBE(extra_offset+0x04+seek_size+0x1c, streamFile);
            block_count = read_32bitBE(extra_offset+0x04+seek_size+0x28, streamFile);
            /* others: scrambled RIFF fmt BE values */

            bytes = ffmpeg_make_riff_xma2(buf,0x100, vgmstream->num_samples, data_size, vgmstream->channels, sample_rate, block_count, block_size);
            vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size);
            if (!vgmstream->codec_data) goto fail;
            vgmstream->coding_type = coding_FFmpeg;
            vgmstream->layout_type = layout_none;

            vgmstream->sample_rate = sample_rate;

            xma_fix_raw_samples(vgmstream, streamFile, start_offset,data_size, 0, 0,0); /* samples are ok, fix delay */
            break;
        }

        case 0x564F5242: { /* "VORB" (PC) */
            start_offset = 0x30;
            data_size = data_size - start_offset;

            vgmstream->codec_data = init_ffmpeg_offset(streamFile, start_offset,data_size);
            if ( !vgmstream->codec_data ) goto fail;
            vgmstream->coding_type = coding_FFmpeg;
            vgmstream->layout_type = layout_none;

            vgmstream->sample_rate = read_32bitLE(start_offset + 0x28, streamFile);
            break;
        }
#endif
        default:
            goto fail;
    }


    if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
        goto fail;
    return vgmstream;

fail:
    close_vgmstream(vgmstream);
    return NULL;
}
Ejemplo n.º 19
0
/* EA SCHl with fixed header - from EA games (~1997? ex. NHL 97 PC) */
VGMSTREAM * init_vgmstream_ea_schl_fixed(STREAMFILE *streamFile) {
    VGMSTREAM * vgmstream = NULL;
    off_t start_offset;
    size_t header_size;
    ea_header ea = {0};


    /* check extension */
    if (!check_extensions(streamFile,"asf"))
        goto fail;

    /* check header (see ea_schl.c for more info about blocks) */
    if (read_32bitBE(0x00,streamFile) != 0x5343486C) /* "SCHl" */
        goto fail;

    header_size = read_32bitLE(0x04,streamFile);

    if (!parse_fixed_header(streamFile,&ea, 0x08))
        goto fail;

    start_offset = header_size;


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

    vgmstream->sample_rate = ea.sample_rate;
    vgmstream->num_samples = ea.num_samples;
    //vgmstream->loop_start_sample = ea.loop_start;
    //vgmstream->loop_end_sample = ea.loop_end;

    vgmstream->codec_endian = ea.big_endian;

    vgmstream->meta_type = meta_EA_SCHL_fixed;

    vgmstream->layout_type = layout_blocked_ea_schl;

    switch (ea.codec) {
        case EA_CODEC_PCM:
            vgmstream->coding_type = ea.bps==8 ? coding_PCM8 : (ea.big_endian ? coding_PCM16BE : coding_PCM16LE);
            break;

        case EA_CODEC_IMA:
            vgmstream->coding_type = coding_DVI_IMA; /* stereo/mono, high nibble first */
            break;

        default:
            VGM_LOG("EA: unknown codec 0x%02x\n", ea.codec);
            goto fail;
    }


    if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
        goto fail;
    return vgmstream;

fail:
    close_vgmstream(vgmstream);
    return NULL;
}
Ejemplo n.º 20
0
/* .OPUS - from Switch games (Lego City Undercover, Ultra SF II, Disgaea 5) */
VGMSTREAM * init_vgmstream_nsw_opus(STREAMFILE *streamFile) {
    VGMSTREAM * vgmstream = NULL;
    off_t start_offset;
    int loop_flag = 0, channel_count;
    int num_samples = 0, loop_start = 0, loop_end = 0;
    off_t offset = 0;

    /* check extension, case insensitive */
    if ( !check_extensions(streamFile,"opus,lopus")) /* no relation to Ogg Opus */
        goto fail;

    /* variations, maybe custom */
    if (read_32bitBE(0x00,streamFile) == 0x01000080) { /* Lego City Undercover */
        offset = 0x00;
    }
    else if ((read_32bitBE(0x04,streamFile) == 0x00000000 && read_32bitBE(0x0c,streamFile) == 0x00000000) ||
             (read_32bitBE(0x04,streamFile) == 0xFFFFFFFF && read_32bitBE(0x0c,streamFile) == 0xFFFFFFFF)) { /* Disgaea 5 */
        offset = 0x10;

        loop_start = read_32bitLE(0x00,streamFile);
        loop_end = read_32bitLE(0x08,streamFile);
    }
    else if (read_32bitLE(0x04,streamFile) == 0x02) { /* Ultra Street Fighter II */
        offset = read_32bitLE(0x1c,streamFile);

        num_samples = read_32bitLE(0x00,streamFile);
        loop_start = read_32bitLE(0x08,streamFile);
        loop_end = read_32bitLE(0x0c,streamFile);
    }
    else {
        offset = 0x00;
    }

    if (read_32bitBE(offset + 0x00,streamFile) != 0x01000080)
        goto fail;

    start_offset = offset + 0x28;
    channel_count = read_8bit(offset + 0x09,streamFile); /* assumed */
    /* 0x0a: packet size if CBR?, other values: no idea */

    loop_flag = (loop_end > 0); /* -1 when not set */


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

    vgmstream->sample_rate = read_32bitLE(offset + 0x0c,streamFile);
    vgmstream->meta_type = meta_NSW_OPUS;

    vgmstream->num_samples = num_samples;
    vgmstream->loop_start_sample = loop_start;
    vgmstream->loop_end_sample = loop_end;

#ifdef VGM_USE_FFMPEG
    {
        uint8_t buf[0x100];
        size_t bytes, skip, data_size;
        ffmpeg_custom_config cfg;

        data_size = get_streamfile_size(streamFile) - start_offset;
        skip = 0; //todo

        bytes = ffmpeg_make_opus_header(buf,0x100, vgmstream->channels, skip, vgmstream->sample_rate);
        if (bytes <= 0) goto fail;

        memset(&cfg, 0, sizeof(ffmpeg_custom_config));
        cfg.type = FFMPEG_SWITCH_OPUS;

        vgmstream->codec_data = init_ffmpeg_config(streamFile, buf,bytes, start_offset,data_size, &cfg);
        if (!vgmstream->codec_data) goto fail;

        vgmstream->coding_type = coding_FFmpeg;
        vgmstream->layout_type = layout_none;

        if (vgmstream->num_samples == 0)
            vgmstream->num_samples = switch_opus_get_samples(start_offset, data_size, vgmstream->sample_rate, streamFile);
    }
#else
    goto fail;
#endif

    /* open the file for reading */
    if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
        goto fail;
    return vgmstream;

fail:
    close_vgmstream(vgmstream);
    return NULL;
}
Ejemplo n.º 21
0
/* Namco NUB xma - from Tekken 6, Galaga Legions DX */
VGMSTREAM * init_vgmstream_nub_xma(STREAMFILE *streamFile) {
    VGMSTREAM * vgmstream = NULL;
    off_t start_offset, chunk_offset;
    size_t data_size, chunk_size;
    int loop_flag, channel_count, sample_rate, chunk_type;
    int num_samples, loop_start_sample, loop_end_sample;


    /* checks */
    if ( !check_extensions(streamFile,"xma")) /* (probably meant to be .nub) */
        goto fail;
    if (read_32bitBE(0x00,streamFile) != 0x786D6100)   /* "xma\0" */
        goto fail;


    /* custom header with a "XMA2" or "fmt " chunk inside; most other values are unknown */
    chunk_type   = read_32bitBE(0xC,streamFile);
    start_offset = 0x100;
    data_size    = read_32bitBE(0x14,streamFile);
    chunk_offset = 0xBC;
    chunk_size   = read_32bitBE(0x24,streamFile);
    if (chunk_type == 0x4) { /* "XMA2" */
        xma2_parse_xma2_chunk(streamFile, chunk_offset, &channel_count,&sample_rate, &loop_flag, &num_samples, &loop_start_sample, &loop_end_sample);
    } else if (chunk_type == 0x8) { /* "fmt " */
        channel_count = read_16bitBE(chunk_offset+0x02,streamFile);
        sample_rate   = read_32bitBE(chunk_offset+0x04,streamFile);
        xma2_parse_fmt_chunk_extra(streamFile, chunk_offset, &loop_flag, &num_samples, &loop_start_sample, &loop_end_sample, 1);
    } else {
        goto fail;
    }


    /* build the VGMSTREAM */
    vgmstream = allocate_vgmstream(channel_count,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->meta_type = meta_NUB_XMA;

#ifdef VGM_USE_FFMPEG
    {
        uint8_t buf[0x100];
        size_t bytes;

        if (chunk_type == 0x4) { /* "XMA2" */
            bytes = ffmpeg_make_riff_xma2_from_xma2_chunk(buf,0x100, chunk_offset,chunk_size, data_size, streamFile);
        } else { /* "fmt " */
            bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,0x100, chunk_offset,chunk_size, data_size, streamFile, 1);
        }
        vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size);
        if ( !vgmstream->codec_data ) goto fail;
        vgmstream->coding_type = coding_FFmpeg;
        vgmstream->layout_type = layout_none;

        xma_fix_raw_samples(vgmstream, streamFile, start_offset, data_size, chunk_offset, 1,1); /* samples needs adjustment */
    }
#else
    goto fail;
#endif

    if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
        goto fail;
    return vgmstream;

fail:
    close_vgmstream(vgmstream);
    return NULL;
}
Ejemplo n.º 22
0
/* .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;
}
Ejemplo n.º 23
0
/* XPCM - from Circus games [Eternal Fantasy (PC), D.C. White Season (PC)] */
VGMSTREAM * init_vgmstream_xpcm(STREAMFILE *streamFile) {
    VGMSTREAM * vgmstream = NULL;
    off_t start_offset;
    size_t decompressed_size;
    int loop_flag, channel_count, codec, subcodec, sample_rate;


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

    if (read_32bitBE(0x00,streamFile) != 0x5850434D) /* "XPCM" "*/
        goto fail;

    decompressed_size = read_32bitLE(0x04,streamFile); /* (data_size for PCM) */
    codec    = read_8bit(0x08, streamFile);
    subcodec = read_8bit(0x09, streamFile);
    /* 0x0a: always null */
    /* 0x0c: always 0x01 (PCM codec) */
    channel_count = read_16bitLE(0x0e,streamFile);
    sample_rate = read_32bitLE(0x10,streamFile);
    /* 0x14: average bitrate */
    /* 0x18: block size */
    /* 0x1a: output bits (16) */
    start_offset = 0x1c; /* compressed size in codec 0x01/03 */

    loop_flag  = 0;


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

    vgmstream->meta_type = meta_XPCM;
    vgmstream->sample_rate = sample_rate;
    vgmstream->num_samples = decompressed_size / sizeof(int16_t) / channel_count;

    switch(codec) {
        case 0x00:
            if (subcodec != 0) goto fail;
            vgmstream->coding_type = coding_PCM16LE;
            vgmstream->layout_type = layout_interleave;
            vgmstream->interleave_block_size = 0x02;
            break;
        case 0x02:
            if (subcodec != 0) goto fail;
            vgmstream->coding_type = coding_CIRCUS_ADPCM;
            vgmstream->layout_type = layout_interleave;
            vgmstream->interleave_block_size = 0x01;
            break;

        case 0x01: /* LZSS + VQ */
        case 0x03: /* unknown */
        default:
            goto fail;
    }

    if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
        goto fail;
    return vgmstream;

fail:
    close_vgmstream(vgmstream);
    return NULL;
}
Ejemplo n.º 24
0
/* MTA2 - found in Metal Gear Solid 4 (PS3) */
VGMSTREAM * init_vgmstream_mta2(STREAMFILE *streamFile) {
    VGMSTREAM * vgmstream = NULL;
    off_t start_offset;
    int loop_flag, channel_count, sample_rate;
    int32_t loop_start, loop_end;
    uint32_t sample_rate_int;


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

    if (read_32bitBE(0x00,streamFile) != 0x4d544132) /* "MTA2" */
        goto fail;
    /* allow truncated files for now? */
    //if (read_32bitBE(0x04, streamFile) + 0x08 != get_streamfile_size(streamFile))
    //    goto fail;

    /* base header (everything is very similar to MGS3's MTAF but BE) */
    /* 0x08(4): version? (1),  0x0c(52): null */

    /* HEAD chunk */
    if (read_32bitBE(0x40, streamFile) != 0x48454144) /* "HEAD" */
        goto fail;
    if (read_32bitBE(0x44, streamFile) != 0xB0) /* HEAD size */
        goto fail;

    /* 0x48(4): null,  0x4c: ? (0x10),   0x50(4): 0x7F (vol?),  0x54(2): 0x40 (pan?) */
    channel_count = read_16bitBE(0x56, streamFile); /* counting all tracks */
    /* 0x60(4): full block size (0x110 * channels), indirectly channels_per_track = channels / (block_size / 0x110) */
    /* 0x80 .. 0xf8: null */

    loop_start = read_32bitBE(0x58, streamFile);
    loop_end   = read_32bitBE(0x5c, streamFile);
    loop_flag = (loop_start != loop_end); /* also flag possibly @ 0x73 */
#if 0
    /* those values look like some kind of loop offsets */
    if (loop_start/0x100 != read_32bitBE(0x68, streamFile) ||
        loop_end  /0x100 != read_32bitBE(0x6C, streamFile) ) {
        VGM_LOG("MTA2: wrong loop points\n");
        goto fail;
    }
#endif

    sample_rate_int = read_32bitBE(0x7c, streamFile);
    if (sample_rate_int) { /* sample rate in 32b float (WHY?) typically 48000.0 */
        float* sample_float = (float*)&sample_rate_int;
        sample_rate = (int)*sample_float;
    } else { /* default when not specified (most of the time) */
        sample_rate = 48000;
    }


    /* TRKP chunks (x16) */
    /* just seem to contain pan/vol stuff (0x7f/0x40), TRKP per track (sometimes +1 main track?) */
    /* there is channel layout bitmask at 0x0f (ex. 1ch = 0x04, 3ch = 0x07, 4ch = 0x33, 6ch = 0x3f), surely:
     * FL 0x01, FR 0x02, FC = 0x04, BL = 0x08, BR = 0x10, BC = 0x20 */

    start_offset = 0x800;

    /* DATA chunk */
    if (read_32bitBE(0x7f8, streamFile) != 0x44415441) // "DATA"
        goto fail;
    //if (read_32bitBE(0x7fc, streamFile) + start_offset != get_streamfile_size(streamFile))
    //    goto fail;


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

    vgmstream->sample_rate = sample_rate;
    vgmstream->num_samples = loop_end;
    vgmstream->loop_start_sample = loop_start;
    vgmstream->loop_end_sample = loop_end;

    vgmstream->coding_type = coding_MTA2;
    vgmstream->layout_type = layout_none;
    vgmstream->meta_type = meta_MTA2;

    /* open the file for reading */
    if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
        goto fail;
    return vgmstream;

fail:
    close_vgmstream(vgmstream);
    return NULL;
}
Ejemplo n.º 25
0
/* GTD - found in Knights Contract (X360, PS3), Valhalla Knights 3 (PSV) */
VGMSTREAM * init_vgmstream_gtd(STREAMFILE *streamFile) {
    VGMSTREAM * vgmstream = NULL;
    off_t start_offset, chunk_offset, stpr_offset, name_offset = 0, loop_start_offset, loop_end_offset;
    size_t data_size, chunk_size;
    int loop_flag, channel_count, sample_rate;
    int num_samples, loop_start_sample, loop_end_sample;
    uint32_t at9_config_data;
    gtd_codec codec;


    /* check extension, case insensitive */
    if ( !check_extensions(streamFile,"gtd"))
        goto fail;

    if (read_32bitBE(0x00,streamFile) != 0x47485320)   /* "GHS " */
        goto fail;

    /* header type, not formally specified */
    if (read_32bitBE(0x04,streamFile) == 1 && read_16bitBE(0x0C,streamFile) == 0x0166) { /* XMA2 */
        /* 0x08(4): seek table size */
        chunk_offset = 0x0c; /* custom header with a "fmt " data chunk inside */
        chunk_size = 0x34;

        channel_count = read_16bitBE(chunk_offset+0x02,streamFile);
        sample_rate   = read_32bitBE(chunk_offset+0x04,streamFile);
        xma2_parse_fmt_chunk_extra(streamFile, chunk_offset, &loop_flag, &num_samples, &loop_start_sample, &loop_end_sample, 1);

        start_offset = read_32bitBE(0x58,streamFile); /* always 0x800 */
        data_size = read_32bitBE(0x5c,streamFile);
        /* 0x34(18): null,  0x54(4): seek table offset, 0x58(4): seek table size, 0x5c(8): null, 0x64: seek table */

        stpr_offset = read_32bitBE(chunk_offset+0x54,streamFile) + read_32bitBE(chunk_offset+0x58,streamFile);
        if (read_32bitBE(stpr_offset,streamFile) == 0x53545052) { /* "STPR" */
            name_offset = stpr_offset + 0xB8; /* there are offsets fields but seems to work */
        }

        codec = XMA2;
    }
    else if (0x34 + read_32bitLE(0x30,streamFile) + read_32bitLE(0x0c,streamFile) == get_streamfile_size(streamFile)) { /* ATRAC9 */

        data_size = read_32bitLE(0x0c,streamFile);
        start_offset = 0x34 + read_32bitLE(0x30,streamFile);
        channel_count   = read_32bitLE(0x10,streamFile);
        sample_rate     = read_32bitLE(0x14,streamFile);
        loop_start_offset = read_32bitLE(0x1c, streamFile);
        loop_end_offset = read_32bitLE(0x20, streamFile);
        loop_flag = loop_end_offset > loop_start_offset;
        at9_config_data = read_32bitBE(0x28,streamFile);
        /* 0x18-0x28: fixed/unknown values */

        stpr_offset = 0x2c;
        if (read_32bitBE(stpr_offset,streamFile) == 0x53545052) { /* "STPR" */
            name_offset = stpr_offset + 0xE8; /* there are offsets fields but seems to work */
        }

        codec = ATRAC9;
    }
    else {
        /* apparently there is a PS3 variation (MSF inside?) */
        goto fail;
    }



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

    vgmstream->sample_rate = sample_rate;
    vgmstream->loop_start_sample = loop_start_sample;
    vgmstream->loop_end_sample   = loop_end_sample;
    vgmstream->meta_type = meta_GTD;
    if (name_offset) //encoding is Shift-Jis in some PSV files
        read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamFile);

    switch(codec) {
#ifdef VGM_USE_FFMPEG
        case XMA2: {
            uint8_t buf[0x100];
            size_t bytes;

            bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,0x100, chunk_offset,chunk_size, data_size, streamFile, 1);
            if (bytes <= 0) goto fail;

            vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size);
            if ( !vgmstream->codec_data ) goto fail;
            vgmstream->coding_type = coding_FFmpeg;
            vgmstream->layout_type = layout_none;

            vgmstream->num_samples = num_samples;

            xma_fix_raw_samples(vgmstream, streamFile, start_offset, data_size, chunk_offset, 1,1);
            break;
        }
#endif
#ifdef VGM_USE_ATRAC9
        case ATRAC9: {
            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;
            if (loop_flag) {
                vgmstream->loop_start_sample = atrac9_bytes_to_samples(loop_start_offset - start_offset, vgmstream->codec_data);
                vgmstream->loop_end_sample = atrac9_bytes_to_samples(loop_end_offset - start_offset, vgmstream->codec_data);
            }
            vgmstream->num_samples = atrac9_bytes_to_samples(data_size, vgmstream->codec_data);
            break;
        }

#endif

        default:
            goto fail;
    }


    /* open the file for reading */
    if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
        goto fail;
    return vgmstream;

fail:
    close_vgmstream(vgmstream);
    return NULL;
}
Ejemplo n.º 26
0
/* 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;
}
Ejemplo n.º 27
0
/* MTAF - found in Metal Gear Solid 3: Snake Eater (PS2), Subsistence and HD too */
VGMSTREAM * init_vgmstream_mtaf(STREAMFILE *streamFile) {
    VGMSTREAM * vgmstream = NULL;
    off_t start_offset;
    int loop_flag, channel_count;
    int32_t loop_start, loop_end;


    /* checks */
    if ( !check_extensions(streamFile,"mtaf"))
        goto fail;
    if (read_32bitBE(0x00, streamFile) != 0x4d544146) /* "MTAF" */
        goto fail;
    /* 0x04(4): pseudo file size (close but smaller) */
    /* 0x08(4): version? (0),  0x0c(20): null,  0x30(32): some kind of id or config? */


    /* HEAD chunk */
    if (read_32bitBE(0x40, streamFile) != 0x48454144) /* "HEAD" */
        goto fail;
    if (read_32bitLE(0x44, streamFile) != 0xB0) /* HEAD size */
        goto fail;


    /* 0x48(4): null,  0x4c: usually channel count (sometimes 0x10 with 2ch),  0x50(4): 0x7F (vol?),  0x54(2): 0x40 (pan?)  */
    channel_count = 2 * read_8bit(0x61, streamFile); /* 0x60(4): full block size (0x110 * channels), but this works */
    /* 0x70(4): ? (00/05/07),  0x80 .. 0xf8: null */

    loop_start = read_32bitLE(0x58, streamFile);
    loop_end   = read_32bitLE(0x5c, streamFile);
    loop_flag  = read_32bitLE(0x70, streamFile) & 1;

    /* check loop points vs frame counts */
    if (loop_start/0x100 != read_32bitLE(0x64, streamFile) ||
        loop_end  /0x100 != read_32bitLE(0x68, streamFile) ) {
        VGM_LOG("MTAF: wrong loop points\n");
        goto fail;
    }

    /* TRKP chunks (x16) */
    /* just seem to contain pan/vol stuff (0x7f/0x40), one TRKP with data per channel and the rest fixed values */

    /* DATA chunk */
    if (read_32bitBE(0x7f8, streamFile) != 0x44415441) /* "DATA" */
        goto fail;
    /* 0x7fc: data size (without blocks in case of blocked layout) */

    start_offset = 0x800;


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

    vgmstream->sample_rate = 48000; /* always */
    vgmstream->num_samples = loop_end;
    vgmstream->loop_start_sample = loop_start;
    vgmstream->loop_end_sample = loop_end;

    vgmstream->coding_type = coding_MTAF;
    vgmstream->layout_type = layout_interleave;
    vgmstream->interleave_block_size = 0x110 / 2; /* kinda hacky for MTAF (stereo codec) track layout */
    vgmstream->meta_type = meta_MTAF;

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

    return vgmstream;

fail:
    close_vgmstream(vgmstream);
    return NULL;
}
Ejemplo n.º 28
0
/* SAB - from Worms 4: Mayhem (PC/Xbox/PS2) */
VGMSTREAM * init_vgmstream_sab(STREAMFILE *streamFile) {
    VGMSTREAM * vgmstream = NULL;
    off_t start_offset;
    int loop_flag, channel_count = 0, is_stream, align, codec, sample_rate, stream_size, loop_start, loop_end;
    int total_subsongs, target_subsong  = streamFile->stream_index;

    /* .sab: main, .sob: config/names */
    if (!check_extensions(streamFile,"sab"))
        goto fail;
    if (read_32bitBE(0x00,streamFile) != 0x43535732 &&  /* "CSW2" (Windows) */
        read_32bitBE(0x00,streamFile) != 0x43535032 &&  /* "CSP2" (PS2) */
        read_32bitBE(0x00,streamFile) != 0x43535832)    /* "CSX2" (Xbox) */
        goto fail;

    is_stream = read_32bitLE(0x04,streamFile) & 0x04; /* other flags don't seem to matter */
    total_subsongs = is_stream ? 1 : read_32bitLE(0x08,streamFile);
    if (target_subsong == 0) target_subsong = 1;
    if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;

    align = read_32bitLE(0x0c,streamFile); /* doubles as interleave */

    /* stream config */
    codec         = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x00,streamFile);
    channel_count = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x04,streamFile);
    sample_rate   = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x08,streamFile);
    stream_size   = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x0c,streamFile);
    loop_start    = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x10,streamFile);
    loop_end      = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x14,streamFile);
    loop_flag     = (loop_end > 0);

    start_offset  = 0x18 + 0x1c*total_subsongs;
    if (start_offset % align)
        start_offset += align - (start_offset % align);
    start_offset += read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x18,streamFile);

    if (is_stream) {
        channel_count = read_32bitLE(0x08,streamFile); /* uncommon, but non-stream stereo exists */
        stream_size *= channel_count;
    }

    /* 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_SAB;

    switch(codec) {
        case 0x01: /* PC */
            vgmstream->coding_type = coding_PCM16LE;
            vgmstream->layout_type = layout_interleave;
            vgmstream->interleave_block_size = is_stream ? align : 0x02;

            vgmstream->num_samples = pcm_bytes_to_samples(stream_size, vgmstream->channels, 16);
            vgmstream->loop_start_sample = pcm_bytes_to_samples(loop_start, vgmstream->channels, 16);
            vgmstream->loop_end_sample = pcm_bytes_to_samples(loop_end, vgmstream->channels, 16);

            break;

        case 0x04: /* PS2 */
            vgmstream->coding_type = coding_PSX;
            vgmstream->layout_type = layout_interleave;
            vgmstream->interleave_block_size = is_stream ? align : 0x10;

            vgmstream->num_samples = ps_bytes_to_samples(stream_size, vgmstream->channels);
            vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, vgmstream->channels);
            vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end, vgmstream->channels);
            break;

        case 0x08: /* Xbox */
            vgmstream->coding_type = is_stream ? coding_XBOX_IMA_int : coding_XBOX_IMA;
            vgmstream->layout_type = is_stream ? layout_interleave : layout_none;
            vgmstream->interleave_block_size = is_stream ? align : 0x00;

            vgmstream->num_samples = xbox_ima_bytes_to_samples(stream_size, vgmstream->channels);
            vgmstream->loop_start_sample = xbox_ima_bytes_to_samples(loop_start, vgmstream->channels);
            vgmstream->loop_end_sample = xbox_ima_bytes_to_samples(loop_end, vgmstream->channels);
            break;

        default:
            VGM_LOG("SAB: unknown codec\n");
            goto fail;
    }

    get_stream_name(vgmstream->stream_name, streamFile, target_subsong);

    if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
        goto fail;
    return vgmstream;

fail:
    close_vgmstream(vgmstream);
    return NULL;
}
Ejemplo n.º 29
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;
}
Ejemplo n.º 30
0
/* .ADS - Sony's "Audio Stream" format [Edit Racing (PS2), Evergrace II (PS2), Pri-Saga! Portable (PSP)] */
VGMSTREAM * init_vgmstream_ps2_ads(STREAMFILE *streamFile) {
    VGMSTREAM * vgmstream = NULL;
    off_t start_offset;
    int loop_flag, channel_count, sample_rate, interleave, is_loop_samples = 0;
    size_t body_size, stream_size, file_size;
    uint32_t codec, loop_start_sample = 0, loop_end_sample = 0, loop_start_offset = 0, loop_end_offset = 0;
    coding_t coding_type;
    int ignore_silent_frame_cavia = 0, ignore_silent_frame_capcom = 0;


    /* checks */
    /* .ads: actual extension
     * .ss2: demuxed videos (fake?)
     * .pcm: Taisho Mononoke Ibunroku (PS2)
     * .adx: Armored Core 3 (PS2)
     * [no actual extension]: MotoGP (PS2)
     * .800: Mobile Suit Gundam: The One Year War (PS2) */
    if (!check_extensions(streamFile, "ads,ss2,pcm,adx,,800"))
        goto fail;

    if (read_32bitBE(0x00,streamFile) != 0x53536864 &&  /* "SShd" */
        read_32bitBE(0x20,streamFile) != 0x53536264)    /* "SSbd" */
        goto fail;
    if (read_32bitLE(0x04,streamFile) != 0x18 && /* standard header size */
        read_32bitLE(0x04,streamFile) != 0x20)   /* True Fortune (PS2) */
        goto fail;


    /* base values (a bit unorderly since devs hack ADS too much and detection is messy) */
    {
        codec = read_32bitLE(0x08,streamFile);
        sample_rate = read_32bitLE(0x0C,streamFile);
        channel_count = read_32bitLE(0x10,streamFile); /* up to 4 [Eve of Extinction (PS2)]*/
        interleave = read_32bitLE(0x14,streamFile); /* set even when mono */


        switch(codec) {
            case 0x01: /* official definition */
            case 0x80000001: /* [Evergrace II (PS2), but not other From Soft games] */
                coding_type = coding_PCM16LE;

                /* Angel Studios/Rockstar San Diego videos codec hijack [Red Dead Revolver (PS2), Spy Hunter 2 (PS2)] */
                if (sample_rate == 12000 && interleave == 0x200) {
                    sample_rate = 48000;
                    interleave = 0x40;
                    coding_type = coding_DVI_IMA_int;
                    /* should try to detect IMA data but it's not so easy, this works ok since
                     * no known games use these settings, videos normally are 48000/24000hz */
                }
                break;

            case 0x10: /* official definition */
            case 0x02: /* Capcom games extension, stereo only [Megaman X7 (PS2), Breath of Fire V (PS2), Clock Tower 3 (PS2)] */
                coding_type = coding_PSX;
                break;

            case 0x00: /* PCM16BE from official docs, probably never used */
            default:
                VGM_LOG("ADS: unknown codec\n");
                goto fail;
        }
    }


    /* sizes */
    {
        file_size = get_streamfile_size(streamFile);
        body_size = read_32bitLE(0x24,streamFile);

        /* bigger than file_size in rare cases, even if containing all data (ex. Megaman X7's SY04.ADS) */
        if (body_size + 0x28 > file_size) {
            body_size = file_size - 0x28;
        }

        /* True Fortune: weird stream size */
        if (body_size * 2 == file_size - 0x18) {
            body_size = (body_size * 2) - 0x10;
        }

        stream_size = body_size;
    }


    /* offset */
    {
        start_offset = 0x28;

        /* start padding (body size is ok, may have end padding) [Evergrace II (PS2), Armored Core 3 (PS2)] */
        /*  detection depends on files being properly ripped, so broken/cut files won't play ok */
        if (file_size - body_size >= 0x800) {
            start_offset = 0x800; /* aligned to sector */

            /* too much end padding, happens in Super Galdelic Hour's SEL.ADS, maybe in bad rips too */
            VGM_ASSERT(file_size - body_size > 0x8000, "ADS: big end padding %x\n", file_size - body_size);
        }

        /* "ADSC" container */
        if (coding_type == coding_PSX
                && read_32bitLE(0x28,streamFile) == 0x1000 /* real start */
                && read_32bitLE(0x2c,streamFile) == 0
                && read_32bitLE(0x1008,streamFile) != 0) {
            int i;
            int is_adsc = 1;

            /* should be empty up to data start */
            for (i = 0; i < 0xFDC/4; i++) {
                if (read_32bitLE(0x2c+(i*4),streamFile) != 0) {
                    is_adsc = 0;
                    break;
                }
            }

            if (is_adsc) {
                start_offset = 0x1000 - 0x08; /* remove "ADSC" alignment */
                /* stream_size doesn't count start offset padding */
            }
        }
    }


    /* loops */
    {
        uint32_t loop_start, loop_end;

        loop_start = read_32bitLE(0x18,streamFile);
        loop_end = read_32bitLE(0x1C,streamFile);

        loop_flag = 0;

        /* detect loops the best we can; docs say those are loop block addresses,
         * but each maker does whatever (no games seem to use PS-ADPCM loop flags though) */


        if (loop_start != 0xFFFFFFFF && loop_end == 0xFFFFFFFF) {

            if (codec == 0x02) { /* Capcom codec */
                /* Capcom games: loop_start is address * 0x10 [Mega Man X7, Breath of Fire V, Clock Tower 3] */
                loop_flag = ((loop_start * 0x10) + 0x200 < body_size); /* near the end (+0x20~80) means no loop */
                loop_start_offset = loop_start * 0x10;
                ignore_silent_frame_capcom = 1;
            }
            else if (read_32bitBE(0x28,streamFile) == 0x50414421) { /* "PAD!" padding until 0x800 */
                /* Super Galdelic Hour: loop_start is PCM bytes */
                loop_flag = 1;
                loop_start_sample = loop_start / 2 / channel_count;
                is_loop_samples = 1;
            }
            else if ((loop_start % 0x800 == 0) && loop_start > 0) {/* sector-aligned, min/0 is 0x800 */
                /* cavia games: loop_start is offset [Drakengard 1/2, GITS: Stand Alone Complex] */
                /* offset is absolute from the "cavia stream format" container that adjusts ADS start */
                loop_flag = 1;
                loop_start_offset = loop_start - 0x800;
                ignore_silent_frame_cavia = 1;
            }
            else if (loop_start % 0x800 != 0 || loop_start == 0) { /* not sector aligned */
                /* Katakamuna: loop_start is address * 0x10 */
                loop_flag = 1;
                loop_start_offset = loop_start * 0x10;
            }
        }
        else if (loop_start != 0xFFFFFFFF && loop_end != 0xFFFFFFFF
                && loop_end > 0) { /* ignore Kamen Rider Blade and others */
#if 0
            //todo improve detection to avoid clashing with address*0x20
            if (loop_end == body_size / 0x10) { /* always body_size? but not all files should loop */
                /* Akane Iro ni Somaru Saka - Parallel: loops is address * 0x10 */
                loop_flag = 1;
                loop_start_offset = loop_start * 0x10;
                loop_end_offset = loop_end * 0x10;
            }
#endif
            if (loop_end <= body_size / 0x70 && coding_type == coding_PCM16LE) { /* close to body_size */
                /* Armored Core - Nexus: loops is address * 0x70 */
                loop_flag = 1;
                loop_start_offset = loop_start * 0x70;
                loop_end_offset = loop_end * 0x70;
            }
            else if (loop_end <= body_size / 0x20 && coding_type == coding_PCM16LE) { /* close to body_size */
                /* Armored Core - Nine Breaker: loops is address * 0x20 */
                loop_flag = 1;
                loop_start_offset = loop_start * 0x20;
                loop_end_offset = loop_end * 0x20;
            }
            else if (loop_end <= body_size / 0x20 && coding_type == coding_PSX) { /* close to body_size */
                /* various games: loops is address * 0x20 [Fire Pro Wrestling Returns, A.C.E. - Another Century's Episode] */
                loop_flag = 1;
                loop_start_offset = loop_start * 0x20;
                loop_end_offset = loop_end * 0x20;
            }
            else if ((loop_end > body_size / 0x20 && coding_type == coding_PSX) ||
                     (loop_end > body_size / 0x70 && coding_type == coding_PCM16LE)) {
                /* various games: loops in samples [Eve of Extinction, Culdcept, WWE Smackdown! 3] */
                loop_flag = 1;
                loop_start_sample = loop_start;
                loop_end_sample = loop_end;
                is_loop_samples = 1;
            }
        }

        //todo Jet Ion Grand Prix seems to have some loop-like values at 0x28
        //todo Yoake mae yori Ruriiro na has loops in unknown format
    }


    /* most games have empty PS-ADPCM frames in the last interleave block that should be skipped for smooth looping */
    if (coding_type == coding_PSX) {
        off_t offset, min_offset;

        offset = start_offset + stream_size;
        min_offset = offset - interleave;

        do {
            offset -= 0x10;

            if (read_8bit(offset+0x01,streamFile) == 0x07) {
                stream_size -= 0x10*channel_count;/* ignore don't decode flag/padding frame (most common) [ex. Capcom games] */
            }
            else if (read_32bitBE(offset+0x00,streamFile) == 0x00000000 && read_32bitBE(offset+0x04,streamFile) == 0x00000000 &&
                     read_32bitBE(offset+0x08,streamFile) == 0x00000000 && read_32bitBE(offset+0x0c,streamFile) == 0x00000000) {
                stream_size -= 0x10*channel_count; /* ignore null frame [ex. A.C.E. Another Century Episode 1/2/3] */
            }
            else if (read_32bitBE(offset+0x00,streamFile) == 0x00007777 && read_32bitBE(offset+0x04,streamFile) == 0x77777777 &&
                     read_32bitBE(offset+0x08,streamFile) == 0x77777777 && read_32bitBE(offset+0x0c,streamFile) == 0x77777777) {
                stream_size -= 0x10*channel_count; /* ignore padding frame [ex. Akane Iro ni Somaru Saka - Parallel]  */
            }
            else if (read_32bitBE(offset+0x00,streamFile) == 0x0C020000 && read_32bitBE(offset+0x04,streamFile) == 0x00000000 &&
                     read_32bitBE(offset+0x08,streamFile) == 0x00000000 && read_32bitBE(offset+0x0c,streamFile) == 0x00000000 &&
                     ignore_silent_frame_cavia) {
                stream_size -= 0x10*channel_count; /* ignore silent frame [ex. cavia games]  */
            }
            else if (read_32bitBE(offset+0x00,streamFile) == 0x0C010000 && read_32bitBE(offset+0x04,streamFile) == 0x00000000 &&
                     read_32bitBE(offset+0x08,streamFile) == 0x00000000 && read_32bitBE(offset+0x0c,streamFile) == 0x00000000 &&
                     ignore_silent_frame_capcom) {
                stream_size -= 0x10*channel_count; /* ignore silent frame [ex. Capcom games]  */
            }
            else {
                break; /* standard frame */
            }
        }
        while(offset > min_offset);

        /* don't bother fixing loop_end_offset since will be adjusted to num_samples later, if needed */
    }


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

    vgmstream->sample_rate = sample_rate;
    vgmstream->coding_type = coding_type;
    vgmstream->interleave_block_size = interleave;
    vgmstream->layout_type = layout_interleave;
    vgmstream->meta_type = meta_PS2_SShd;

    switch(coding_type) {
        case coding_PCM16LE:
            vgmstream->num_samples = pcm_bytes_to_samples(stream_size, channel_count, 16);
            break;
        case coding_PSX:
            vgmstream->num_samples = ps_bytes_to_samples(stream_size, channel_count);
            break;
        case coding_DVI_IMA_int:
            vgmstream->num_samples = ima_bytes_to_samples(stream_size, channel_count);
            break;
        default:
            goto fail;
    }

    if (vgmstream->loop_flag) {
        if (is_loop_samples) {
            vgmstream->loop_start_sample = loop_start_sample;
            vgmstream->loop_end_sample = loop_end_sample;
        }
        else {
            switch(vgmstream->coding_type) {
                case coding_PCM16LE:
                    vgmstream->loop_start_sample = pcm_bytes_to_samples(loop_start_offset,channel_count,16);
                    vgmstream->loop_end_sample = pcm_bytes_to_samples(loop_end_offset,channel_count,16);
                    break;
                case coding_PSX:
                    vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start_offset,channel_count);
                    vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end_offset,channel_count);
                    break;
                default:
                    goto fail;
            }
        }

        /* when loop_end = 0xFFFFFFFF */
        if (vgmstream->loop_end_sample == 0)
            vgmstream->loop_end_sample = vgmstream->num_samples;

        /* happens even when loops are directly samples, loops sound fine (ex. Culdcept) */
        if (vgmstream->loop_end_sample > vgmstream->num_samples)
            vgmstream->loop_end_sample = vgmstream->num_samples;
    }


    if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
        goto fail;
    return vgmstream;

fail:
    close_vgmstream(vgmstream);
    return NULL;
}