Fix some .agsc [Metroid Prime (GC)]

This commit is contained in:
bnnm 2024-07-21 09:50:28 +02:00
parent 9631e02689
commit f6b2c6fab3
3 changed files with 143 additions and 64 deletions

View File

@ -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;
}

View File

@ -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)

View File

@ -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);