Exemplo n.º 1
0
/* Decodes samples for segmented streams.
 * Chains together sequential vgmstreams, for data divided into separate sections or files
 * (like one part for intro and other for loop segments, which may even use different codecs). */
void render_vgmstream_segmented(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream) {
    int samples_written = 0;
    segmented_layout_data *data = vgmstream->layout_data;


    while (samples_written < sample_count) {
        int samples_to_do;
        int samples_this_block = data->segments[data->current_segment]->num_samples;


        if (vgmstream->loop_flag && vgmstream_do_loop(vgmstream)) {
            /* handle looping, finding loop segment */
            int loop_segment = 0, samples = 0, loop_samples_skip = 0;
            while (samples < vgmstream->num_samples) {
                int32_t segment_samples = data->segments[loop_segment]->num_samples;
                if (vgmstream->loop_start_sample >= samples && vgmstream->loop_start_sample < samples + segment_samples) {
                    loop_samples_skip = vgmstream->loop_start_sample - samples;
                    break; /* loop_start falls within loop_segment's samples */
                }
                samples += segment_samples;
                loop_segment++;
            }
            if (loop_segment == data->segment_count) {
                VGM_LOG("segmented_layout: can't find loop segment\n");
                loop_segment = 0;
            }
            if (loop_samples_skip > 0) {
                VGM_LOG("segmented_layout: loop starts after %i samples\n", loop_samples_skip);
                //todo skip/fix, but probably won't happen
            }

            data->current_segment = loop_segment;
            reset_vgmstream(data->segments[data->current_segment]);
            vgmstream->samples_into_block = 0;
            continue;
        }

        samples_to_do = vgmstream_samples_to_do(samples_this_block, sample_count, vgmstream);
        if (samples_to_do > sample_count - samples_written)
            samples_to_do = sample_count - samples_written;

        /* detect segment change and restart */
        if (samples_to_do == 0) {
            data->current_segment++;
            reset_vgmstream(data->segments[data->current_segment]);
            vgmstream->samples_into_block = 0;
            continue;
        }

        render_vgmstream(&buffer[samples_written*data->segments[data->current_segment]->channels],
                samples_to_do,data->segments[data->current_segment]);

        samples_written += samples_to_do;
        vgmstream->current_sample += samples_to_do;
        vgmstream->samples_into_block += samples_to_do;
    }
}
Exemplo n.º 2
0
/* debug util, mainly for custom IO testing */
void dump_streamfile(STREAMFILE *streamFile, const char* out) {
#ifdef VGM_DEBUG_OUTPUT
    off_t offset = 0;
    FILE *f = NULL;

    if (out) {
        f = fopen(out,"wb");
        if (!f) return;
    }

    VGM_LOG("dump streamfile, size: %x\n", get_streamfile_size(streamFile));
    while (offset < get_streamfile_size(streamFile)) {
        uint8_t buffer[0x8000];
        size_t read;

        read = read_streamfile(buffer,offset,0x8000,streamFile);
        if (out)
            fwrite(buffer,sizeof(uint8_t),read, f);
        else
            VGM_LOGB(buffer,read,0);
        offset += read;
    }

    if (out) {
        fclose(f);
    }
#endif
}
Exemplo n.º 3
0
int setup_layout_segmented(segmented_layout_data* data) {
    int i;

    /* setup each VGMSTREAM (roughly equivalent to vgmstream.c's init_vgmstream_internal stuff) */
    for (i = 0; i < data->segment_count; i++) {
        if (!data->segments[i])
            goto fail;

        if (data->segments[i]->num_samples <= 0)
            goto fail;

        /* shouldn't happen */
        if (data->segments[i]->loop_flag != 0) {
            VGM_LOG("segmented layout: segment %i is looped\n", i);
            data->segments[i]->loop_flag = 0;
        }

        if (i > 0) {
            if (data->segments[i]->channels != data->segments[i-1]->channels)
                goto fail;

            /* a bit weird, but no matter */
            if (data->segments[i]->sample_rate != data->segments[i-1]->sample_rate) {
                VGM_LOG("segmented layout: segment %i has different sample rate\n", i);
            }

            //if (data->segments[i]->coding_type != data->segments[i-1]->coding_type)
            //    goto fail; /* perfectly acceptable */
        }


        /* save start things so we can restart for seeking/looping */
        memcpy(data->segments[i]->start_ch,data->segments[i]->ch,sizeof(VGMSTREAMCHANNEL)*data->segments[i]->channels);
        memcpy(data->segments[i]->start_vgmstream,data->segments[i],sizeof(VGMSTREAM));
    }


    return 1;
fail:
    return 0; /* caller is expected to free */
}
Exemplo n.º 4
0
static STREAMFILE * open_stdio_streamfile_buffer_by_file(FILE *infile,const char * const filename, size_t buffersize) {
    uint8_t * buffer = NULL;
    STDIOSTREAMFILE * streamfile = NULL;

    buffer = calloc(buffersize,1);
    if (!buffer) goto fail;

    streamfile = calloc(1,sizeof(STDIOSTREAMFILE));
    if (!streamfile) goto fail;

    streamfile->sf.read = (void*)read_stdio;
    streamfile->sf.get_size = (void*)get_size_stdio;
    streamfile->sf.get_offset = (void*)get_offset_stdio;
    streamfile->sf.get_name = (void*)get_name_stdio;
    streamfile->sf.open = (void*)open_stdio;
    streamfile->sf.close = (void*)close_stdio;

    streamfile->infile = infile;
    streamfile->buffersize = buffersize;
    streamfile->buffer = buffer;

    strncpy(streamfile->name,filename,sizeof(streamfile->name));
    streamfile->name[sizeof(streamfile->name)-1] = '\0';

    /* cache filesize */
    fseeko(streamfile->infile,0,SEEK_END);
    streamfile->filesize = ftello(streamfile->infile);

    /* Typically fseek(o)/ftell(o) may only handle up to ~2.14GB, signed 32b = 0x7FFFFFFF
     * (happens in banks like FSB, though rarely). Can be remedied with the
     * preprocessor (-D_FILE_OFFSET_BITS=64 in GCC) but it's not well tested. */
    if (streamfile->filesize == 0xFFFFFFFF) { /* -1 on error */
        VGM_LOG("STREAMFILE: ftell error\n");
        goto fail; /* can be ignored but may result in strange/unexpected behaviors */
    }

    return &streamfile->sf;

fail:
    free(buffer);
    free(streamfile);
    return NULL;
}
Exemplo n.º 5
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;
}
Exemplo n.º 6
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;
}
Exemplo n.º 7
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;
}
Exemplo n.º 8
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;
}
Exemplo n.º 9
0
/* .dsp w/ Cstr header, seen in Star Fox Assault and Donkey Konga */
VGMSTREAM * init_vgmstream_cstr(STREAMFILE *streamFile) {
    VGMSTREAM * vgmstream = NULL;
    char filename[PATH_LIMIT];

    int loop_flag;
    off_t start_offset;
    off_t first_data;
    off_t loop_offset;
    size_t interleave;
    int loop_adjust;
    int double_loop_end = 0;

    /* check extension, case insensitive */
    streamFile->get_name(streamFile,filename,sizeof(filename));
    if (strcasecmp("dsp",filename_extension(filename))) goto fail;

    /* check header */
    if ((uint32_t)read_32bitBE(0,streamFile)!=0x43737472)   /* "Cstr" */
        goto fail;
#ifdef DEBUG
    fprintf(stderr,"header ok\n");
#endif

    if (read_8bit(0x1b,streamFile)==1) {
        /* mono version, much simpler to handle */
        /* Only seen in R Racing Evolution radio sfx */

        start_offset = 0x80;
        loop_flag = read_16bitBE(0x2c,streamFile);

        /* check initial predictor/scale */
        if (read_16bitBE(0x5e,streamFile) != (uint8_t)read_8bit(start_offset,streamFile))
            goto fail;

        /* check type==0 and gain==0 */
        if (read_16bitBE(0x2e,streamFile) || read_16bitBE(0x5c,streamFile))
            goto fail;

        loop_offset = start_offset+read_32bitBE(0x10,streamFile);
        if (loop_flag) {
            if (read_16bitBE(0x64,streamFile) != (uint8_t)read_8bit(loop_offset,streamFile)) goto fail;
        }

        /* build the VGMSTREAM */

        vgmstream = allocate_vgmstream(1,loop_flag);
        if (!vgmstream) goto fail;

        /* fill in the vital statistics */
        vgmstream->sample_rate = read_32bitBE(0x28,streamFile);
        vgmstream->num_samples = read_32bitBE(0x20,streamFile);

        if (loop_flag) {
        vgmstream->loop_start_sample = dsp_nibbles_to_samples(
                read_32bitBE(0x30,streamFile));
        vgmstream->loop_end_sample =  dsp_nibbles_to_samples(
                read_32bitBE(0x34,streamFile))+1;
        }

        vgmstream->coding_type = coding_NGC_DSP;
        vgmstream->layout_type = layout_none;
        vgmstream->meta_type = meta_DSP_CSTR;

        {
            int i;
            for (i=0;i<16;i++)
                vgmstream->ch[0].adpcm_coef[i]=read_16bitBE(0x3c+i*2,streamFile);
        }

        /* open the file for reading by each channel */
        vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);

        if (!vgmstream->ch[0].streamfile) goto fail;

        vgmstream->ch[0].channel_start_offset=
            vgmstream->ch[0].offset=
            start_offset;

        return vgmstream;
    }   /* end mono */

    interleave = read_16bitBE(0x06,streamFile);
    start_offset = 0xe0; 
    first_data = start_offset+read_32bitBE(0x0c,streamFile);
    loop_flag = read_16bitBE(0x2c,streamFile);

    if (!loop_flag) {
        /* Nonlooped tracks seem to follow no discernable pattern
         * with where they actually start.
         * But! with the magic of initial p/s redundancy, we can guess.
         */
        while (first_data<start_offset+0x800 &&
                (read_16bitBE(0x5e,streamFile) != (uint8_t)read_8bit(first_data,streamFile) ||
                read_16bitBE(0xbe,streamFile) != (uint8_t)read_8bit(first_data+interleave,streamFile)))
            first_data+=8;
#ifdef DEBUG
        fprintf(stderr,"guessed first_data at %#x\n",first_data);
#endif
    }

    /* check initial predictor/scale */
    if (read_16bitBE(0x5e,streamFile) != (uint8_t)read_8bit(first_data,streamFile))
        goto fail;
    if (read_16bitBE(0xbe,streamFile) != (uint8_t)read_8bit(first_data+interleave,streamFile))
        goto fail;

#ifdef DEBUG
    fprintf(stderr,"p/s ok\n");
#endif

    /* check type==0 and gain==0 */
    if (read_16bitBE(0x2e,streamFile) || read_16bitBE(0x5c,streamFile))
        goto fail;
    if (read_16bitBE(0x8e,streamFile) || read_16bitBE(0xbc,streamFile))
        goto fail;

#ifdef DEBUG
    fprintf(stderr,"type & gain ok\n");
#endif

    /* check for loop flag agreement */
    if (read_16bitBE(0x2c,streamFile) != read_16bitBE(0x8c,streamFile))
        goto fail;

#ifdef DEBUG
    fprintf(stderr,"loop flags agree\n");
#endif

    loop_offset = start_offset+read_32bitBE(0x10,streamFile)*2;
    if (loop_flag) {
        int loops_ok=0;
        /* check loop predictor/scale */
        /* some fuzz allowed */
        for (loop_adjust=0;loop_adjust>=-0x10;loop_adjust-=8) {
#ifdef DEBUG
            fprintf(stderr,"looking for loop p/s at %#x,%#x\n",loop_offset-interleave+loop_adjust,loop_offset+loop_adjust);
#endif
            if (read_16bitBE(0x64,streamFile) == (uint8_t)read_8bit(loop_offset-interleave+loop_adjust,streamFile) &&
                    read_16bitBE(0xc4,streamFile) == (uint8_t)read_8bit(loop_offset+loop_adjust,streamFile)) {
                loops_ok=1;
                break;
            }
        }
        if (!loops_ok)
            for (loop_adjust=interleave;loop_adjust<=interleave+0x10;loop_adjust+=8) {
#ifdef DEBUG
                fprintf(stderr,"looking for loop p/s at %#x,%#x\n",loop_offset-interleave+loop_adjust,loop_offset+loop_adjust);
#endif
                if (read_16bitBE(0x64,streamFile) == (uint8_t)read_8bit(loop_offset-interleave+loop_adjust,streamFile) &&
                        read_16bitBE(0xc4,streamFile) == (uint8_t)read_8bit(loop_offset+loop_adjust,streamFile)) {
                    loops_ok=1;
                    break;
                }
            }

        if (!loops_ok) goto fail;
#ifdef DEBUG
        fprintf(stderr,"loop p/s ok (with %#4x adjust)\n",loop_adjust);
#endif

        /* check for agreement */
        /* loop end (channel 1 & 2 headers) */
        if (read_32bitBE(0x34,streamFile) != read_32bitBE(0x94,streamFile))
            goto fail;
        
        /* Mr. Driller oddity */
        if (dsp_nibbles_to_samples(read_32bitBE(0x34,streamFile)*2)+1 <= read_32bitBE(0x20,streamFile)) {
#ifdef DEBUG
            fprintf(stderr,"loop end <= half total samples, should be doubled\n");
#endif
            double_loop_end = 1;
        }

        /* loop start (Cstr header and channel 1 header) */
        if (read_32bitBE(0x30,streamFile) != read_32bitBE(0x10,streamFile)
#if 0
                /* this particular glitch only true for SFA, though it
                 * seems like something similar happens in Donkey Konga */
                /* loop start (Cstr, channel 1 & 2 headers) */
                || (read_32bitBE(0x0c,streamFile)+read_32bitLE(0x30,streamFile)) !=
                read_32bitBE(0x90,streamFile)
#endif
           )
            /* alternatively (Donkey Konga) the header loop is 0x0c+0x10 */
            if (
                    /* loop start (Cstr header and channel 1 header) */
                    read_32bitBE(0x30,streamFile) != read_32bitBE(0x10,streamFile)+
                    read_32bitBE(0x0c,streamFile))
                /* further alternatively (Donkey Konga), if we loop back to
                 * the very first frame 0x30 might be 0x00000002 (which
                 * is a *valid* std dsp loop start, imagine that) while 0x10
                 * is 0x00000000 */
                if (!(read_32bitBE(0x30,streamFile) == 2 &&
                            read_32bitBE(0x10,streamFile) == 0))
                    /* lest there be too few alternatives, in Mr. Driller we
                     * find that [0x30] + [0x0c] + 8 = [0x10]*2 */
                    if (!(double_loop_end &&
                            read_32bitBE(0x30,streamFile) +
                            read_32bitBE(0x0c,streamFile) + 8 ==
                            read_32bitBE(0x10,streamFile)*2)) {
                        /* for Mr.Driller Drill Land, no idea about the above but seems to play and loop ok */
                        VGM_LOG("Cstr: bad loop check ignored\n");
                        //goto fail;
                    }

#ifdef DEBUG
        fprintf(stderr,"loop points agree\n");
#endif
    }

    /* assure that sample counts, sample rates agree */
    if (
            /* sample count (channel 1 & 2 headers) */
            read_32bitBE(0x20,streamFile) != read_32bitBE(0x80,streamFile) ||
            /* sample rate (channel 1 & 2 headers) */
            read_32bitBE(0x28,streamFile) != read_32bitBE(0x88,streamFile) ||
            /* sample count (Cstr header and channel 1 header) */
            read_32bitLE(0x14,streamFile) != read_32bitBE(0x20,streamFile) ||
            /* sample rate (Cstr header and channel 1 header) */
            (uint16_t)read_16bitLE(0x18,streamFile) != read_32bitBE(0x28,streamFile))
        goto fail;

    /* build the VGMSTREAM */

    vgmstream = allocate_vgmstream(2,loop_flag);
    if (!vgmstream) goto fail;

    /* fill in the vital statistics */
    vgmstream->sample_rate = read_32bitBE(0x28,streamFile);
    /* This is a slight hack to counteract their hack.
     * All the data is ofset by first_data so that the loop
     * point occurs at a block boundary. However, I always begin decoding
     * right after the header, as that is the start of the first block and
     * my interleave code relies on starting at the beginning of a block.
     * So we decode a few silent samples at the beginning, and here we make up
     * for it by lengthening the track by that much.
     */
    vgmstream->num_samples = read_32bitBE(0x20,streamFile) +
        (first_data-start_offset)/8*14;

    if (loop_flag) {
        off_t loop_start_bytes = loop_offset-start_offset-interleave;
        vgmstream->loop_start_sample = dsp_nibbles_to_samples((loop_start_bytes/(2*interleave)*interleave+loop_start_bytes%(interleave*2))*2);
        /*dsp_nibbles_to_samples(loop_start_bytes);*/
        /*dsp_nibbles_to_samples(read_32bitBE(0x30,streamFile)*2-inter);*/
        vgmstream->loop_end_sample =  dsp_nibbles_to_samples(
                read_32bitBE(0x34,streamFile))+1;

        if (double_loop_end)
            vgmstream->loop_end_sample =
                dsp_nibbles_to_samples(read_32bitBE(0x34,streamFile)*2)+1;

        if (vgmstream->loop_end_sample > vgmstream->num_samples) {
#ifdef DEBUG
            fprintf(stderr,"loop_end_sample > num_samples, adjusting\n");
#endif
            vgmstream->loop_end_sample = vgmstream->num_samples;
        }
    }

    vgmstream->coding_type = coding_NGC_DSP;
    vgmstream->layout_type = layout_interleave;
    vgmstream->interleave_block_size = interleave;
    vgmstream->meta_type = meta_DSP_CSTR;

    {
        int i;
        for (i=0;i<16;i++)
            vgmstream->ch[0].adpcm_coef[i]=read_16bitBE(0x3c+i*2,streamFile);
        for (i=0;i<16;i++)
            vgmstream->ch[1].adpcm_coef[i]=read_16bitBE(0x9c+i*2,streamFile);
    }

    /* open the file for reading by each channel */
    {
        int i;
        for (i=0;i<2;i++) {
            vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);

            if (!vgmstream->ch[i].streamfile) goto fail;

            vgmstream->ch[i].channel_start_offset=
                vgmstream->ch[i].offset=
                start_offset+interleave*i;
        }
    }

    return vgmstream;

    /* clean up anything we may have opened */
fail:
    if (vgmstream) close_vgmstream(vgmstream);
    return NULL;
}
Exemplo n.º 10
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;
}
Exemplo n.º 11
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;
}
Exemplo n.º 12
0
/* Convers custom Opus packets to Ogg Opus, so the resulting data is larger than physical data. */
static size_t opus_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, opus_io_data* data) {
    size_t total_read = 0;

    /* ignore bad reads */
    if (offset < 0 || offset > data->logical_size) {
        return total_read;
    }

    /* previous offset: re-start as we can't map logical<>physical offsets */
    if (offset < data->logical_offset) {
        data->physical_offset = data->stream_offset;
        data->logical_offset = 0x00;
        data->page_size = 0;
        data->samples_done = 0;
        data->sequence = 2; /* appended header is 0/1 */

        if (offset >= data->head_size)
            data->logical_offset = data->head_size;
    }

    /* insert fake header */
    if (offset < data->head_size) {
        size_t bytes_consumed, to_read;

        bytes_consumed = offset - data->logical_offset;
        to_read = data->head_size - bytes_consumed;
        if (to_read > length)
            to_read = length;
        memcpy(dest, data->head_buffer + bytes_consumed, to_read);

        total_read += to_read;
        dest += to_read;
        offset += to_read;
        length -= to_read;
        data->logical_offset += to_read;
    }

    /* read blocks, one at a time */
    while (length > 0) {

        /* ignore EOF */
        if (data->logical_offset >= data->logical_size) {
            break;
        }

        /* process new block */
        if (data->page_size == 0) {
            size_t data_size, skip_size, oggs_size;

            switch(data->type) {
                case OPUS_SWITCH: /* format seem to come from opus_test and not Nintendo-specific */
                    data_size = read_32bitBE(data->physical_offset, streamfile);
                    skip_size = 0x08; /* size + Opus state(?) */
                    break;
                case OPUS_UE4:
                    data_size = (uint16_t)read_16bitLE(data->physical_offset, streamfile);
                    skip_size = 0x02;
                    break;
                case OPUS_EA:
                    data_size = (uint16_t)read_16bitBE(data->physical_offset, streamfile);
                    skip_size = 0x02;
                    break;
                case OPUS_X:
                    data_size = get_xopus_packet_size(data->sequence - 2, streamfile);
                    skip_size = 0;
                    break;
                default:
                    return 0;
            }

            oggs_size = 0x1b + (int)(data_size / 0xFF + 1); /* OggS page: base size + lacing values */

            data->block_size = data_size + skip_size;
            data->page_size = oggs_size + data_size;

            if (data->page_size > sizeof(data->page_buffer)) { /* happens on bad reads/EOF too */
                VGM_LOG("OPUS: buffer can't hold OggS at %x\n", (uint32_t)data->physical_offset);
                data->page_size = 0;
                break;
            }

            /* create fake OggS page (full page for checksums) */
            read_streamfile(data->page_buffer+oggs_size, data->physical_offset + skip_size, data_size, streamfile); /* store page data */
            data->samples_done += opus_get_packet_samples(data->page_buffer+oggs_size, data_size);
            make_oggs_page(data->page_buffer,sizeof(data->page_buffer), data_size, data->sequence, data->samples_done);
            data->sequence++;
        }

        /* move to next block */
        if (offset >= data->logical_offset + data->page_size) {
            data->physical_offset += data->block_size;
            data->logical_offset += data->page_size;
            data->page_size = 0;
            continue;
        }

        /* read data */
        {
            size_t bytes_consumed, to_read;

            bytes_consumed = offset - data->logical_offset;
            to_read = data->page_size - bytes_consumed;
            if (to_read > length)
                to_read = length;
            memcpy(dest, data->page_buffer + bytes_consumed, to_read);

            total_read += to_read;
            dest += to_read;
            offset += to_read;
            length -= to_read;

            if (to_read == 0) {
                break; /* error/EOF */
            }
        }
    }

    return total_read;
}
Exemplo n.º 13
0
static size_t opus_io_size(STREAMFILE *streamfile, opus_io_data* data) {
    off_t physical_offset, max_physical_offset;
    size_t logical_size = 0;
    int packet = 0;

    if (data->logical_size)
        return data->logical_size;

    if (data->stream_offset + data->stream_size > get_streamfile_size(streamfile)) {
        VGM_LOG("OPUS: wrong streamsize %x + %x vs %x\n", (uint32_t)data->stream_offset, data->stream_size, get_streamfile_size(streamfile));
        return 0;
    }

    physical_offset = data->stream_offset;
    max_physical_offset = data->stream_offset + data->stream_size;
    logical_size = data->head_size;

    /* get size of the logical stream */
    while (physical_offset < max_physical_offset) {
        size_t data_size, skip_size, oggs_size;

        switch(data->type) {
            case OPUS_SWITCH:
                data_size = read_32bitBE(physical_offset, streamfile);
                skip_size = 0x08;
                break;
            case OPUS_UE4:
                data_size = (uint16_t)read_16bitLE(physical_offset, streamfile);
                skip_size = 0x02;
                break;
            case OPUS_EA:
                data_size = (uint16_t)read_16bitBE(physical_offset, streamfile);
                skip_size = 0x02;
                break;
            case OPUS_X:
                data_size = get_xopus_packet_size(packet, streamfile);
                skip_size = 0x00;
                break;
            default:
                return 0;
        }

        if (data_size == 0 ) {
            VGM_LOG("OPUS: data_size is 0 at %x\n", (uint32_t)physical_offset);
            return 0; /* bad rip? or could 'break' and truck along */
        }

        oggs_size = 0x1b + (int)(data_size / 0xFF + 1); /* OggS page: base size + lacing values */

        physical_offset += data_size + skip_size;
        logical_size += oggs_size + data_size;
        packet++;
    }

    /* logical size can be bigger though */
    if (physical_offset > get_streamfile_size(streamfile)) {
        VGM_LOG("OPUS: wrong size\n");
        return 0;
    }

    data->logical_size = logical_size;
    return data->logical_size;
}
Exemplo n.º 14
0
/* SCD - Square-Enix games (FF XIII, XIV) */
VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
    VGMSTREAM * vgmstream = NULL;
    off_t start_offset, tables_offset, meta_offset, extradata_offset, name_offset = 0;
    int32_t stream_size, extradata_size, loop_start, loop_end;

    int loop_flag = 0, channel_count, codec, sample_rate;
    int version, target_entry, aux_chunk_count;
    int total_subsongs, target_subsong = streamFile->stream_index;

    int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
    int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;


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

    /** main header **/
    if (read_32bitBE(0x00,streamFile) != 0x53454442 &&  /* "SEDB" */
        read_32bitBE(0x04,streamFile) != 0x53534346)    /* "SSCF" */
        goto fail;

    if (read_8bit(0x0c,streamFile) == 0x01) { /* big endian flag */
        //size_offset = 0x14;
        read_32bit = read_32bitBE;
        read_16bit = read_16bitBE;
    } else {
        //size_offset = 0x10;
        read_32bit = read_32bitLE;
        read_16bit = read_16bitLE;
    }

    /* SSCF version? (older SSCFs from Crisis Core/FFXI X360 seem to be V3/2) */
    if (read_8bit(0x0d,streamFile) != 0x04)
        goto fail;

    /* v2: FFXIII demo (PS3), FFT0 test files (PC); v3: common; v4: Kingdom Hearts 2.8 (PS4) */
    version = read_32bit(0x08,streamFile);
    if (version != 2 && version != 3 && version != 4)
        goto fail;

    tables_offset = read_16bit(0x0e,streamFile); /* usually 0x30 or 0x20 */

#if 0
    /* never mind, FFXIII music_68tak.ps3.scd is 0x80 shorter */
    /* check file size with header value */
    if (read_32bit(size_offset,streamFile) != get_streamfile_size(streamFile))
        goto fail;
#endif


    /** offset tables **/
    /* 0x00(2): table1/4 (unknown) entries */
    /* 0x02(2): table2 (unknown) entries */
    /* 0x04(2): table3 (headers) entries */
    /* 0x06(2): unknown, varies even for clone files */

    /* (implicit: table1 starts at 0x20) */
    /* 0x08: table2 (unknown) start offset */
    /* 0x0c: table3 (headers) start offset */
    /* 0x10: table4 (unknown) start offset */
    /* 0x14: always null? */
    /* 0x18: table5? (unknown) start offset? */
    /* 0x1c: unknown, often null */
    /* each table entry is an uint32_t offset; after entries there is padding */
    /* if a table isn't present entries is 0 and offset points to next table */

    /* find meta_offset in table3 (headers) and total subsongs */
    {
        int i;
        int headers_entries = read_16bit(tables_offset+0x04,streamFile);
        off_t headers_offset = read_32bit(tables_offset+0x0c,streamFile);

        if (target_subsong == 0) target_subsong = 1;
        total_subsongs = 0;
        meta_offset = 0;

        /* manually find subsongs as entries can be dummy (ex. sfx banks in FF XIV or FF Type-0) */
        for (i = 0; i < headers_entries; i++) {
            off_t entry_offset = read_32bit(headers_offset + i*0x04,streamFile);

            if (read_32bit(entry_offset+0x0c,streamFile) == -1)
                continue; /* codec -1 when dummy */

            total_subsongs++;
            if (!meta_offset && total_subsongs == target_subsong) {
                meta_offset = entry_offset;
                target_entry = i;
            }
        }
        if (meta_offset == 0) goto fail;
        /* SCD can contain 0 entries too */
    }

    /** stream header **/
    stream_size     = read_32bit(meta_offset+0x00,streamFile);
    channel_count   = read_32bit(meta_offset+0x04,streamFile);
    sample_rate     = read_32bit(meta_offset+0x08,streamFile);
    codec           = read_32bit(meta_offset+0x0c,streamFile);

    loop_start      = read_32bit(meta_offset+0x10,streamFile);
    loop_end        = read_32bit(meta_offset+0x14,streamFile);
    extradata_size  = read_32bit(meta_offset+0x18,streamFile);
    aux_chunk_count = read_32bit(meta_offset+0x1c,streamFile);
    /* 0x01e(2): unknown, seen in some FF XIV sfx (MSADPCM) */

    loop_flag       = (loop_end > 0);
    extradata_offset = meta_offset + 0x20;
    start_offset = extradata_offset + extradata_size;

    /* only "MARK" chunk is known (some FF XIV PS3 have "STBL" but it's not counted) */
    if (aux_chunk_count > 1 && aux_chunk_count < 0xFFFF) { /* some FF XIV Heavensward IMA sfx have 0x01000000 */
        VGM_LOG("SCD: unknown aux chunk count %i\n", aux_chunk_count);
        goto fail;
    }

    /* skips aux chunks, sometimes needed (Lightning Returns X360, FF XIV PC) */
    if (aux_chunk_count && read_32bitBE(extradata_offset, streamFile) == 0x4D41524B) { /* "MARK" */
        extradata_offset += read_32bit(extradata_offset+0x04, streamFile);
    }

    /* find name if possible */
    if (version == 4) {
        int info_entries    = read_16bit(tables_offset+0x00,streamFile);
        int headers_entries = read_16bit(tables_offset+0x04,streamFile);
        off_t info_offset   = tables_offset+0x20;

        /* not very exact as table1 and table3 entries may differ in V3, not sure about V4 */
        if (info_entries == headers_entries) {
            off_t entry_offset = read_16bit(info_offset + 0x04*target_entry,streamFile);
            name_offset = entry_offset+0x30;
        }
    }


#ifdef VGM_USE_VORBIS
    /* special case using init_vgmstream_ogg_vorbis */
    if (codec == 0x06) {
        VGMSTREAM *ogg_vgmstream;
        uint8_t ogg_version, ogg_byte;
        ogg_vorbis_meta_info_t ovmi = {0};

        ovmi.meta_type = meta_SQEX_SCD;
        ovmi.total_subsongs = total_subsongs;
        /* loop values are in bytes, let init_vgmstream_ogg_vorbis find loop comments instead */

        ogg_version = read_8bit(extradata_offset + 0x00, streamFile);
        /* 0x01(1): 0x20 in v2/3, this ogg miniheader size? */
        ogg_byte    = read_8bit(extradata_offset + 0x02, streamFile);
        /* 0x03(1): ? in v3 */

        if (ogg_version == 0) { /* 0x10? header, then custom Vorbis header before regular Ogg (FF XIV PC v1) */
            ovmi.stream_size = stream_size;
        }
        else { /* 0x20 header, then seek table */
            size_t seek_table_size  = read_32bit(extradata_offset+0x10, streamFile);
            size_t vorb_header_size = read_32bit(extradata_offset+0x14, streamFile);
            /* 0x18(4): ? (can be 0) */

            if ((extradata_offset-meta_offset) + seek_table_size + vorb_header_size != extradata_size)
                goto fail;

            ovmi.stream_size = vorb_header_size + stream_size;
            start_offset = extradata_offset + 0x20 + seek_table_size; /* extradata_size skips vorb_header */

            if (ogg_version == 2) { /* header is XOR'ed using byte (FF XIV PC) */
                ovmi.decryption_callback = scd_ogg_v2_decryption_callback;
                ovmi.scd_xor = ogg_byte;
                ovmi.scd_xor_length = vorb_header_size;
            }
            else if (ogg_version == 3) { /* file is XOR'ed using table (FF XIV Heavensward PC)  */
                ovmi.decryption_callback = scd_ogg_v3_decryption_callback;
                ovmi.scd_xor = stream_size & 0xFF; /* ogg_byte not used? */
                ovmi.scd_xor_length = vorb_header_size + stream_size;
            }
            else {
                VGM_LOG("SCD: unknown ogg_version 0x%x\n", ogg_version);
            }
        }

        /* actual Ogg init */
        ogg_vgmstream = init_vgmstream_ogg_vorbis_callbacks(streamFile, NULL, start_offset, &ovmi);
        if (ogg_vgmstream && name_offset)
            read_string(ogg_vgmstream->stream_name, PATH_LIMIT, name_offset, streamFile);
        return ogg_vgmstream;
    }
#endif


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

    vgmstream->sample_rate = sample_rate;
    vgmstream->num_streams = total_subsongs;
    vgmstream->stream_size = stream_size;
    vgmstream->meta_type = meta_SQEX_SCD;
    if (name_offset)
        read_string(vgmstream->stream_name, PATH_LIMIT, name_offset, streamFile);

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

            vgmstream->num_samples = pcm_bytes_to_samples(stream_size, channel_count, 16);
            if (loop_flag) {
                vgmstream->loop_start_sample = pcm_bytes_to_samples(loop_start, channel_count, 16);
                vgmstream->loop_end_sample = pcm_bytes_to_samples(loop_end, channel_count, 16);
            }
            break;

        case 0x03:      /* PS-ADPCM [Final Fantasy Type-0] */
            vgmstream->coding_type = coding_PSX;
            vgmstream->layout_type = layout_interleave;
            vgmstream->interleave_block_size = 0x10;

            vgmstream->num_samples = ps_bytes_to_samples(stream_size, channel_count);
            if (loop_flag) {
                vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, channel_count);
                vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end, channel_count);
            }
            break;

        case 0x06:      /* OGG [Final Fantasy XIII-2 (PC), Final Fantasy XIV (PC)] */
            goto fail; /* handled above */

#ifdef VGM_USE_MPEG
        case 0x07: {    /* MPEG [Final Fantasy XIII (PS3)] */
            mpeg_codec_data *mpeg_data = NULL;
            mpeg_custom_config cfg = {0};

            cfg.interleave = 0x800; /* for multistream [Final Fantasy XIII-2 (PS3)], otherwise ignored */
            cfg.data_size = stream_size;

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

            /* some Drakengard 3, Kingdom Hearts HD have adjusted sample rate (47999, 44099), for looping? */

            vgmstream->num_samples = mpeg_bytes_to_samples(stream_size, mpeg_data);
            vgmstream->loop_start_sample = mpeg_bytes_to_samples(loop_start, mpeg_data);
            vgmstream->loop_end_sample = mpeg_bytes_to_samples(loop_end, mpeg_data);

            /* somehow loops offsets aren't always frame-aligned, and the code below supposedly helped,
             * but there isn't much difference since MPEG loops are rough (1152-aligned). Seems it
             * would help more loop_start - ~1000, loop_end + ~1000 (ex. FFXIII-2 music_SunMizu.ps3.scd) */
            //vgmstream->num_samples -= vgmstream->num_samples % 576;
            //vgmstream->loop_start_sample -= vgmstream->loop_start_sample % 576;
            //vgmstream->loop_end_sample -= vgmstream->loop_end_sample % 576;
            break;
        }
#endif
        case 0x0C:      /* MS ADPCM [Final Fantasy XIV (PC) sfx] */
            vgmstream->coding_type = coding_MSADPCM;
            vgmstream->layout_type = layout_none;
            vgmstream->interleave_block_size = read_16bit(extradata_offset+0x0c,streamFile);
            /* in extradata_offset is a WAVEFORMATEX (including coefs and all) */

            vgmstream->num_samples = msadpcm_bytes_to_samples(stream_size, vgmstream->interleave_block_size, vgmstream->channels);
            if (loop_flag) {
                vgmstream->loop_start_sample = msadpcm_bytes_to_samples(loop_start, vgmstream->interleave_block_size, vgmstream->channels);
                vgmstream->loop_end_sample = msadpcm_bytes_to_samples(loop_end, vgmstream->interleave_block_size, vgmstream->channels);
            }
            break;

        case 0x0A:      /* DSP ADPCM [Dragon Quest X (Wii)] */
        case 0x15: {    /* DSP ADPCM [Dragon Quest X (Wii U)] (no apparent differences except higher sample rate) */
            const off_t interleave_size = 0x800;
            const off_t stride_size = interleave_size * channel_count;
            int i;
            size_t total_size;
            layered_layout_data * data = NULL;

            /* interleaved DSPs including the header (so the first 0x800 is 0x60 header + 0x740 data)
             * so interleave layout can't used; we'll setup de-interleaving streamfiles as layers/channels instead */
            //todo this could be simplified using a block layout or adding interleave_first_block
            vgmstream->coding_type = coding_NGC_DSP;
            vgmstream->layout_type = layout_layered;

            /* read from the first DSP header and verify other channel headers */
            {
                total_size = (read_32bitBE(start_offset+0x04,streamFile)+1)/2; /* rounded nibbles / 2 */
                vgmstream->num_samples = read_32bitBE(start_offset+0x00,streamFile);
                if (loop_flag) {
                    vgmstream->loop_start_sample = loop_start;
                    vgmstream->loop_end_sample = loop_end+1;
                }

                for (i = 1; i < channel_count; i++) {
                    if ((read_32bitBE(start_offset+4,streamFile)+1)/2 != total_size ||
                        read_32bitBE(start_offset+interleave_size*i+0x00,streamFile) != vgmstream->num_samples) {
                        goto fail;
                    }
                }
            }

            /* init layout */
            data = init_layout_layered(channel_count);
            if (!data) goto fail;
            vgmstream->layout_data = data;

            /* open each layer subfile */
            for (i = 0; i < channel_count; i++) {
                STREAMFILE* temp_streamFile = setup_scd_dsp_streamfile(streamFile, start_offset+interleave_size*i, interleave_size, stride_size, total_size);
                if (!temp_streamFile) goto fail;

                data->layers[i] = init_vgmstream_ngc_dsp_std(temp_streamFile);
                close_streamfile(temp_streamFile);
                if (!data->layers[i]) goto fail;
            }

            /* setup layered VGMSTREAMs */
            if (!setup_layout_layered(data))
                goto fail;

            break;
        }

#ifdef VGM_USE_FFMPEG
        case 0x0B: {    /* XMA2 [Final Fantasy (X360), Lightning Returns (X360) sfx, Kingdom Hearts 2.8 (X1)] */
            ffmpeg_codec_data *ffmpeg_data = NULL;
            uint8_t buf[200];
            int32_t bytes;

            /* extradata_offset+0x00: fmt0x166 header (BE),  extradata_offset+0x34: seek table */
            bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,200, extradata_offset,0x34, stream_size, streamFile, 1);
            ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,stream_size);
            if (!ffmpeg_data) goto fail;
            vgmstream->codec_data = ffmpeg_data;
            vgmstream->coding_type = coding_FFmpeg;
            vgmstream->layout_type = layout_none;

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

            xma_fix_raw_samples(vgmstream, streamFile, start_offset,stream_size, 0, 0,0); /* samples are ok, loops? */
            break;
        }

        case 0x0E: {    /* ATRAC3/ATRAC3plus [Lord of Arcana (PSP), Final Fantasy Type-0] */
            ffmpeg_codec_data *ffmpeg_data = NULL;

            /* full RIFF header at start_offset/extradata_offset (same) */
            ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,stream_size);
            if (!ffmpeg_data) goto fail;
            vgmstream->codec_data = ffmpeg_data;
            vgmstream->coding_type = coding_FFmpeg;
            vgmstream->layout_type = layout_none;

            vgmstream->num_samples = ffmpeg_data->totalSamples; /* fact samples */
            vgmstream->loop_start_sample = loop_start;
            vgmstream->loop_end_sample = loop_end;

            if (ffmpeg_data->skipSamples <= 0) /* in case FFmpeg didn't get them */
                ffmpeg_set_skip_samples(ffmpeg_data, riff_get_fact_skip_samples(streamFile, start_offset));
            /* SCD loop/sample values are relative (without skip samples) vs RIFF (with skip samples), no need to adjust */
            break;
        }
#endif

#ifdef VGM_USE_ATRAC9
        case 0x16: { /* ATRAC9 [Kingdom Hearts 2.8 (PS4)] */
            atrac9_config cfg = {0};

            /* post header has various typical ATRAC9 values */
            cfg.channels = vgmstream->channels;
            cfg.config_data = read_32bit(extradata_offset+0x0c,streamFile);
            cfg.encoder_delay = read_32bit(extradata_offset+0x18,streamFile);

            vgmstream->codec_data = init_atrac9(&cfg);
            if (!vgmstream->codec_data) goto fail;
            vgmstream->coding_type = coding_ATRAC9;
            vgmstream->layout_type = layout_none;

            vgmstream->num_samples = read_32bit(extradata_offset+0x10,streamFile); /* loop values above are also weird and ignored */
            vgmstream->loop_start_sample = read_32bit(extradata_offset+0x20, streamFile) - (loop_flag ? cfg.encoder_delay : 0); //loop_start
            vgmstream->loop_end_sample   = read_32bit(extradata_offset+0x24, streamFile) - (loop_flag ? cfg.encoder_delay : 0); //loop_end
            break;
        }
#endif

        case -1:    /* used for dummy entries */
        default:
            VGM_LOG("SCD: unknown codec 0x%x\n", codec);
            goto fail;
    }


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

    return vgmstream;

fail:
    close_vgmstream(vgmstream);
    return NULL;
}
Exemplo n.º 15
0
/* FLX - from Ultima IX (.FLX is actually an archive format with sometimes sound data, let's support both anyway) */
VGMSTREAM * init_vgmstream_flx(STREAMFILE *streamFile) {
    VGMSTREAM * vgmstream = NULL;
    off_t start_offset, stream_offset = 0;
    size_t data_size;
    int loop_flag, channel_count, codec;
    int total_subsongs = 0, target_subsong = streamFile->stream_index;
    size_t stream_size = 0;


    /* check extensions (.flx: name of archive, files inside don't have extensions) */
    if (!check_extensions(streamFile,"flx"))
        goto fail;

    /* all spaces up to 0x50 = archive FLX */
    if (read_32bitBE(0x00,streamFile) == 0x20202020 && read_32bitBE(0x40,streamFile) == 0x20202020) {
        int i;
        int entries = read_32bitLE(0x50,streamFile);
        off_t offset = 0x80;

        if (read_32bitLE(0x54,streamFile) != 0x02
                || read_32bitLE(0x58,streamFile) != get_streamfile_size(streamFile))
            goto fail;

        if (target_subsong == 0) target_subsong = 1;

        for (i = 0; i < entries; i++) {
            off_t entry_offset = read_32bitLE(offset + 0x00, streamFile);
            size_t entry_size = read_32bitLE(offset + 0x04, streamFile);
            offset += 0x08;

            if (entry_offset != 0x00)
                total_subsongs++; /* many entries are empty */
            if (total_subsongs == target_subsong && stream_offset == 0) {
                stream_offset = entry_offset; /* found but let's keep adding total_streams */
                stream_size = entry_size;
            }
        }
        if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
        if (stream_offset == 0x00) goto fail;
    }
    else {
        stream_offset = 0x00;
        stream_size = get_streamfile_size(streamFile);
    }

    if (read_32bitLE(stream_offset + 0x30,streamFile) != 0x10)
        goto fail;
    data_size = read_32bitLE(stream_offset + 0x28,streamFile);
    channel_count = read_32bitLE(stream_offset + 0x34,streamFile);
    codec = read_32bitLE(stream_offset + 0x38,streamFile);
    loop_flag = (channel_count > 1); /* full seamless repeats in music */
    start_offset = stream_offset + 0x3c;
    /* 0x00: id */

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

    vgmstream->sample_rate = read_32bitLE(stream_offset + 0x2c,streamFile);
    vgmstream->num_streams = total_subsongs;
    vgmstream->stream_size = stream_size;
    vgmstream->meta_type = meta_PC_FLX;

    switch(codec) {
        case 0x00:  /* PCM (sfx) */
            vgmstream->coding_type = coding_PCM16LE;
            vgmstream->layout_type = layout_interleave;
            vgmstream->interleave_block_size = 0x02;

            vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, 16);
            break;

        case 0x01:  /* EA-XA (music, sfx) */
            vgmstream->coding_type = channel_count > 1 ? coding_EA_XA : coding_EA_XA_int;
            vgmstream->layout_type = layout_none;

            vgmstream->num_samples = read_32bitLE(stream_offset + 0x28,streamFile) / 0x0f*channel_count * 28; /* ea_xa_bytes_to_samples */
            vgmstream->loop_start_sample = 0;
            vgmstream->loop_end_sample = vgmstream->num_samples;
            break;

        case 0x02:  /* EA-MT (voices) */
            vgmstream->coding_type = coding_EA_MT;
            vgmstream->codec_data = init_ea_mt(vgmstream->channels, 0);
            if (!vgmstream->codec_data) goto fail;

            vgmstream->num_samples = read_32bitLE(start_offset,streamFile);
            start_offset += 0x04;
            break;

        default:
            VGM_LOG("FLX: unknown codec 0x%x\n", codec);
            goto fail;
    }

    read_string(vgmstream->stream_name,0x20+1, stream_offset + 0x04,streamFile);


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

    return vgmstream;

fail:
    close_vgmstream(vgmstream);
    return NULL;
}