2022-07-01 17:49:38 +02:00
# include "meta.h"
# include "../util.h"
# include "../coding/coding.h"
/* .GCM - from select Namco games released in around the PS2 era [Gunvari Collection + Time Crisis (PS2), NamCollection (PS2)] */
VGMSTREAM * init_vgmstream_ps2_gcm ( STREAMFILE * sf ) {
VGMSTREAM * vgmstream = NULL ;
off_t start_offset ;
2022-07-01 21:34:00 +02:00
uint32_t vagp_l_offset , vagp_r_offset , gcm_samples , data_size , gcm_sample_rate , gcm_channels ;
2022-07-01 17:49:38 +02:00
/* checks */
/* .gcm: actual extension. */
if ( ! check_extensions ( sf , " gcm " ) )
goto fail ;
/* check header */
if ( read_u32be ( 0x00 , sf ) ! = 0x4D434700 ) /* "MCG" */
goto fail ;
/* GCM format as Namco outlined it is a hacked VAGp stereo format that can hold 6-channel audio at will.
2022-07-02 03:49:47 +02:00
* so , first step is to deal with whatever quirks the format might have . */
2022-07-01 21:34:00 +02:00
vagp_l_offset = read_u32le ( 0x04 , sf ) ; /* "left" VAGp header pointer. */
2022-07-01 17:49:38 +02:00
if ( vagp_l_offset ! = 0x20 )
goto fail ;
2022-07-01 21:34:00 +02:00
vagp_r_offset = read_u32le ( 0x08 , sf ) ; /* "right" VAGp header pointer. */
2022-07-01 17:49:38 +02:00
if ( vagp_r_offset ! = 0x50 )
goto fail ;
2022-07-01 21:34:00 +02:00
start_offset = read_u32le ( 0x0c , sf ) ; /* raw "stereo VAG" data pointer. */
2022-07-01 17:49:38 +02:00
if ( start_offset ! = 0x80 )
goto fail ;
2022-07-02 03:49:47 +02:00
gcm_samples = read_u32le ( 0x10 , sf ) ; /* size of raw "stereo VAG" data, not exactly correct for multi-channel files as we'll see below. */
2022-07-01 17:49:38 +02:00
2022-07-02 03:49:47 +02:00
/* second step is to check actual GCM size and decide the "channels" value from there.
* not helping matters is there ' s nothing in the GCM header that indicates whether or not this GCM is actually multi - channel in any way .
2022-07-01 21:34:00 +02:00
* meaning we have to manually support those Klonoa multi - channel files ( KLN3182M . GCM , KLN3191M . GCM , etc ) . */
2022-07-01 17:49:38 +02:00
data_size = get_streamfile_size ( sf ) - start_offset ;
2022-07-01 21:34:00 +02:00
if ( data_size = = ( gcm_samples * 3 ) ) {
/* 6-channel GCM file. */
gcm_channels = 6 ;
2022-07-01 17:49:38 +02:00
}
2022-07-01 21:34:00 +02:00
else if ( data_size = = gcm_samples ) {
/* stereo GCM file. */
gcm_channels = 2 ;
2022-07-01 17:49:38 +02:00
}
2022-07-01 21:34:00 +02:00
else {
/* there is only one known GCM "multi-channel" setup and it's hacky. */
2022-07-01 17:49:38 +02:00
goto fail ;
}
2022-07-02 03:49:47 +02:00
/* what follows are two VAGp headers and raw data, third and final step is to perform sanity checks on them (except the data itself) and go from there. */
2022-07-01 17:49:38 +02:00
2022-07-02 03:49:47 +02:00
/* VAGp signature field, always present. */
2022-07-01 21:34:00 +02:00
uint32_t vagp_l_sig , vagp_r_sig , vagp_sig ;
vagp_l_sig = read_u32be ( vagp_l_offset , sf ) ;
vagp_r_sig = read_u32be ( vagp_r_offset , sf ) ;
if ( vagp_l_sig ! = vagp_r_sig ) {
2022-07-02 03:49:47 +02:00
/* check two identical values against each other. */
2022-07-01 21:34:00 +02:00
goto fail ;
}
else {
2022-07-02 03:49:47 +02:00
/* check for "VAGp" value. */
2022-07-01 21:34:00 +02:00
vagp_sig = vagp_r_sig ;
if ( vagp_sig ! = 0x56414770 ) goto fail ; /* "VAGp" */
}
2022-07-02 03:49:47 +02:00
/* VAGp version field, always 4. */
2022-07-01 21:34:00 +02:00
uint32_t vagp_l_ver , vagp_r_ver , vagp_ver ;
2022-07-01 17:49:38 +02:00
vagp_l_ver = read_u32be ( vagp_l_offset + 4 , sf ) ;
vagp_r_ver = read_u32be ( vagp_r_offset + 4 , sf ) ;
2022-07-01 21:34:00 +02:00
if ( vagp_l_ver ! = vagp_r_ver ) {
2022-07-02 03:49:47 +02:00
/* check two identical values against each other. */
2022-07-01 17:49:38 +02:00
goto fail ;
2022-07-01 21:34:00 +02:00
}
else {
2022-07-02 03:49:47 +02:00
/* check for "version 4" value. */
2022-07-01 21:34:00 +02:00
vagp_ver = vagp_r_ver ;
if ( vagp_ver ! = 4 ) goto fail ;
}
2022-07-02 03:49:47 +02:00
/* VAGp size field, usually separate per channel and does not cover blank frames at all. */
2022-07-01 21:34:00 +02:00
uint32_t vagp_l_size , vagp_r_size , vagp_samples ;
2022-07-01 17:49:38 +02:00
vagp_l_size = read_u32be ( vagp_l_offset + 12 , sf ) ;
vagp_r_size = read_u32be ( vagp_r_offset + 12 , sf ) ;
2022-07-01 21:34:00 +02:00
if ( vagp_l_size ! = vagp_r_size ) {
2022-07-02 03:49:47 +02:00
/* check two identical values against each other. */
2022-07-01 17:49:38 +02:00
goto fail ;
2022-07-01 21:34:00 +02:00
}
else {
2022-07-02 03:49:47 +02:00
/* assign one of the two existing "vag size" values to an entirely new "vag size" variable to be used later. */
2022-07-01 21:34:00 +02:00
vagp_samples = vagp_r_size ;
}
2022-07-02 03:49:47 +02:00
/* VAGp sample rate field, usually separate per channel. */
2022-07-01 17:49:38 +02:00
uint32_t vagp_l_sample_rate , vagp_r_sample_rate ;
vagp_l_sample_rate = read_u32be ( vagp_l_offset + 16 , sf ) ;
vagp_r_sample_rate = read_u32be ( vagp_r_offset + 16 , sf ) ;
if ( vagp_l_sample_rate ! = vagp_r_sample_rate ) {
2022-07-02 03:49:47 +02:00
/* check two identical values against each other. */
2022-07-01 17:49:38 +02:00
goto fail ;
}
else {
2022-07-02 03:49:47 +02:00
/* assign one of the two existing "sample rate" values to an entirely new "sample rate" variable to be used later. */
2022-07-01 17:49:38 +02:00
gcm_sample_rate = vagp_r_sample_rate ;
}
2022-07-01 21:34:00 +02:00
/* build the VGMSTREAM by allocating it to total number of channels and a single loop flag.
* though loop flag is set to 0. GCM files don ' t really " loop " by themselves . */
vgmstream = allocate_vgmstream ( gcm_channels , 0 ) ;
2022-07-01 17:49:38 +02:00
if ( ! vgmstream ) goto fail ;
2022-07-02 03:49:47 +02:00
/* fill in the vital statistics. */
2022-07-01 17:49:38 +02:00
vgmstream - > meta_type = meta_PS2_GCM ;
vgmstream - > sample_rate = gcm_sample_rate ;
2022-07-01 21:34:00 +02:00
vgmstream - > num_samples = ps_bytes_to_samples ( vagp_samples , 1 ) ;
2022-07-01 17:49:38 +02:00
vgmstream - > coding_type = coding_PSX ;
vgmstream - > layout_type = layout_interleave ;
vgmstream - > interleave_block_size = read_u32le ( 0x14 , sf ) ;
2022-07-02 03:49:47 +02:00
/* open the file for reading. */
2022-07-01 17:49:38 +02:00
if ( ! vgmstream_open_stream ( vgmstream , sf , start_offset ) )
goto fail ;
return vgmstream ;
2022-07-02 03:49:47 +02:00
/* clean up anything we may have opened. */
2022-07-01 17:49:38 +02:00
fail :
close_vgmstream ( vgmstream ) ;
return NULL ;
}