mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-28 00:20:47 +01:00
Fix some .agsc [Metroid Prime (GC)]
This commit is contained in:
parent
9631e02689
commit
f6b2c6fab3
195
src/meta/agsc.c
195
src/meta/agsc.c
@ -1,74 +1,145 @@
|
||||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "../util/reader_text.h"
|
||||
#include "../util/meta_utils.h"
|
||||
|
||||
/* .agsc - from Metroid Prime 2 */
|
||||
static bool parse_agsc(meta_header_t* hdr, STREAMFILE* sf, int version);
|
||||
|
||||
VGMSTREAM * init_vgmstream_agsc(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
char filename[PATH_LIMIT];
|
||||
|
||||
off_t header_offset;
|
||||
off_t start_offset;
|
||||
int channel_count;
|
||||
int i;
|
||||
/* .agsc - from Retro Studios games [Metroid Prime (GC), Metroid Prime 2 (GC)] */
|
||||
VGMSTREAM* init_vgmstream_agsc(STREAMFILE* sf) {
|
||||
|
||||
/* check extension, case insensitive */
|
||||
streamFile->get_name(streamFile,filename,sizeof(filename));
|
||||
if (strcasecmp("agsc",filename_extension(filename))) goto fail;
|
||||
/* checks */
|
||||
int version;
|
||||
if (is_id32be(0x00, sf, "Audi"))
|
||||
version = 1;
|
||||
else if (read_u32be(0x00, sf) == 0x00000001)
|
||||
version = 2;
|
||||
else
|
||||
return NULL;
|
||||
|
||||
/* check header */
|
||||
if ((uint32_t)read_32bitBE(0,streamFile)!=0x00000001)
|
||||
goto fail;
|
||||
/* .agsc: 'class' type in .pak */
|
||||
if (!check_extensions(sf, "agsc"))
|
||||
return NULL;
|
||||
|
||||
/* count length of name, including terminating 0 */
|
||||
for (header_offset=4;header_offset < get_streamfile_size(streamFile) && read_8bit(header_offset,streamFile)!='\0';header_offset++);
|
||||
|
||||
header_offset ++;
|
||||
meta_header_t hdr = {0};
|
||||
if (!parse_agsc(&hdr, sf, version))
|
||||
return NULL;
|
||||
|
||||
channel_count = 1;
|
||||
hdr.meta = meta_AGSC;
|
||||
hdr.coding = coding_NGC_DSP;
|
||||
hdr.layout = layout_none;
|
||||
hdr.big_endian = true;
|
||||
hdr.allow_dual_stereo = true;
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
hdr.sf = sf;
|
||||
hdr.open_stream = true;
|
||||
|
||||
vgmstream = allocate_vgmstream(1,1);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
vgmstream->num_samples = read_32bitBE(header_offset+0xda,streamFile);
|
||||
vgmstream->sample_rate = (uint16_t)read_16bitBE(header_offset+0xd8,streamFile);
|
||||
|
||||
vgmstream->loop_start_sample = read_32bitBE(header_offset+0xde,streamFile);
|
||||
/* this is cute, we actually have a "loop length" */
|
||||
vgmstream->loop_end_sample = (vgmstream->loop_start_sample + read_32bitBE(header_offset+0xe2,streamFile))-1;
|
||||
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->meta_type = meta_AGSC;
|
||||
vgmstream->allow_dual_stereo = 1;
|
||||
|
||||
for (i=0;i<16;i++) {
|
||||
vgmstream->ch[0].adpcm_coef[i]=read_16bitBE(header_offset+0xf6+i*2,streamFile);
|
||||
}
|
||||
|
||||
start_offset = header_offset+0x116;
|
||||
|
||||
/* open the file for reading by each channel */
|
||||
{
|
||||
int i;
|
||||
for (i=0;i<channel_count;i++) {
|
||||
vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
|
||||
if (!vgmstream->ch[i].streamfile) goto fail;
|
||||
|
||||
vgmstream->ch[i].channel_start_offset=
|
||||
vgmstream->ch[i].offset=
|
||||
start_offset;
|
||||
}
|
||||
}
|
||||
|
||||
return vgmstream;
|
||||
|
||||
/* clean up anything we may have opened */
|
||||
fail:
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
return alloc_metastream(&hdr);
|
||||
}
|
||||
|
||||
|
||||
static bool parse_agsc(meta_header_t* hdr, STREAMFILE* sf, int version) {
|
||||
uint32_t offset;
|
||||
int name_size;
|
||||
|
||||
switch(version) {
|
||||
case 1:
|
||||
// usually "Audio/" but rarely "Audio//"
|
||||
name_size = read_string(NULL, 0x20, 0x00, sf);
|
||||
if (name_size == 0) // not a string
|
||||
return false;
|
||||
offset = name_size + 1;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
/* after fixed ID */
|
||||
offset = 0x04;
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
/* after id starts with name + null */
|
||||
hdr->name_offset = offset;
|
||||
name_size = read_string(NULL, 0x20, offset, sf);
|
||||
if (name_size == 0) // not a string
|
||||
return false;
|
||||
offset += name_size + 1;
|
||||
|
||||
uint32_t head_offset, data_offset;
|
||||
uint32_t unk1_size, unk2_size, head_size, data_size;
|
||||
switch(version) {
|
||||
case 1:
|
||||
/* per chunk: chunk size + chunk data */
|
||||
unk1_size = read_u32be(offset, sf);
|
||||
offset += 0x04 + unk1_size;
|
||||
|
||||
unk2_size = read_u32be(offset, sf);
|
||||
offset += 0x04 + unk2_size;
|
||||
|
||||
data_offset = offset + 0x04; // data chunk goes before headers...
|
||||
data_size = read_u32be(offset, sf);
|
||||
offset += 0x04 + data_size;
|
||||
|
||||
head_offset = offset + 0x04;
|
||||
head_size = read_u32be(offset, sf);
|
||||
offset += 0x04 + head_size;
|
||||
|
||||
break;
|
||||
|
||||
case 2:
|
||||
/* chunk sizes per chunk + chunk data per chunk */
|
||||
offset += 0x02; // song id?
|
||||
unk1_size = read_u32be(offset + 0x00, sf);
|
||||
unk2_size = read_u32be(offset + 0x04, sf);
|
||||
head_size = read_u32be(offset + 0x08, sf);
|
||||
data_size = read_u32be(offset + 0x0c, sf);
|
||||
|
||||
head_offset = offset + 0x10 + unk1_size + unk2_size;
|
||||
data_offset = head_offset + head_size;
|
||||
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
// offsets/values/data aren't 32b aligned but file is (0xFF padding)
|
||||
|
||||
// possible in some test banks
|
||||
if (data_size == 0 || head_size < 0x20 + 0x28) {
|
||||
vgm_logi("AGSC: bank has no subsongs (ignore)\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* header chunk has 0x20 headers per subsong + 0xFFFFFFFF (end marker) + 0x28 coefs per subsongs,
|
||||
* no apparent count even in other chunks */
|
||||
hdr->total_subsongs = (head_size - 0x04) / (0x20 + 0x28);
|
||||
hdr->target_subsong = sf->stream_index;
|
||||
if (!check_subsongs(&hdr->target_subsong, hdr->total_subsongs))
|
||||
return false;
|
||||
|
||||
uint32_t entry_offset = head_offset + 0x20 * (hdr->target_subsong - 1);
|
||||
|
||||
// 00: id?
|
||||
hdr->stream_offset = read_u32be(entry_offset + 0x04,sf) + data_offset;
|
||||
// 08: null?
|
||||
// 0c: always 0x3c00?
|
||||
hdr->sample_rate = read_u16be(entry_offset + 0x0e,sf);
|
||||
hdr->num_samples = read_s32be(entry_offset + 0x10,sf);
|
||||
hdr->loop_start = read_s32be(entry_offset + 0x14,sf);
|
||||
hdr->loop_end = read_s32be(entry_offset + 0x18,sf); // loop length
|
||||
hdr->coefs_offset = read_s32be(entry_offset + 0x1c,sf);
|
||||
|
||||
if (hdr->loop_end)
|
||||
hdr->loop_end = hdr->loop_end + hdr->loop_start - 1;
|
||||
hdr->loop_flag = hdr->loop_end != 0;
|
||||
|
||||
hdr->coefs_offset += head_offset + 0x08; // skip unknown hist/loop ps-like values
|
||||
|
||||
hdr->channels = 1; // MP2 uses dual stereo for title track
|
||||
hdr->stream_size = hdr->num_samples / 14 * 8 * hdr->channels; // meh
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "../vgmstream.h"
|
||||
#include "meta_utils.h"
|
||||
#include "reader_text.h"
|
||||
|
||||
|
||||
/* Allocate memory and setup a VGMSTREAM */
|
||||
@ -10,7 +11,7 @@ VGMSTREAM* alloc_metastream(meta_header_t* h) {
|
||||
return NULL;
|
||||
}
|
||||
if (h->num_samples <= 0 || h->num_samples > VGMSTREAM_MAX_NUM_SAMPLES) {
|
||||
VGM_LOG("meta: wrong samples %i\n", h->sample_rate);
|
||||
VGM_LOG("meta: wrong samples %i\n", h->num_samples);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -30,7 +31,10 @@ VGMSTREAM* alloc_metastream(meta_header_t* h) {
|
||||
vgmstream->num_streams = h->total_subsongs;
|
||||
vgmstream->stream_size = h->stream_size;
|
||||
vgmstream->interleave_block_size = h->interleave;
|
||||
vgmstream->allow_dual_stereo = h->allow_dual_stereo;
|
||||
|
||||
if (h->name_offset)
|
||||
read_string(vgmstream->stream_name, sizeof(vgmstream->stream_name), h->name_offset, h->sf ? h->sf : h->sf_head);
|
||||
|
||||
if (h->coding == coding_NGC_DSP && (h->sf || h->sf_head)) {
|
||||
if (h->coefs_offset || h->coefs_spacing)
|
||||
|
@ -39,7 +39,9 @@ typedef struct {
|
||||
uint32_t hists_offset;
|
||||
uint32_t hists_spacing;
|
||||
|
||||
/* optional but can be used for some actions */
|
||||
uint32_t name_offset;
|
||||
|
||||
/* optional but can be used for some actions (such as DSP coefs) */
|
||||
bool big_endian;
|
||||
coding_t coding;
|
||||
layout_t layout;
|
||||
@ -51,6 +53,8 @@ typedef struct {
|
||||
STREAMFILE* sf_body;
|
||||
|
||||
bool open_stream;
|
||||
|
||||
bool allow_dual_stereo;
|
||||
} meta_header_t;
|
||||
|
||||
VGMSTREAM* alloc_metastream(meta_header_t* h);
|
||||
|
Loading…
Reference in New Issue
Block a user