Ongakukan ADPCM code improvements

significant improvements include:
- char flags in ongakukan_adp_lib.c have all, with one exception, been made to bools.
-- ATM, sample_mode will remain a char.
-- several other variables have been removed. they did not affect decoding in any significant way.
-- init_ongakukan_adpcm now accepts at least one bool as an argument for a total of four arguments.
-- many function from lib renamed to follow external lib function naming standard.

- simplified ongakukan_adp_decoder.c
-- decode_ongakukan_adp in particular benefited from this code simpification; loop is now re-done so all samples are copied to outbuf when they're made available.
-- as a consequence of this, at least one var was removed entirely, while others have been redefined into different purposes; samples_filled is now a bool var that flags whether or not all available samples were decoded.
-- init_ongakukan_adp has four arguments as well, and accepts at least one bool as an argument of its own.
-- streamfile is now close there as well instead of in the external decoder lib.

- adp_ongakukan.c saw much simpified code as well; meta checks have been relaxed significantly, which meant the removal of several var and at least three vars repurposed into bools.
-- supposed_size is introduced as an entirely new var; it now calculates the supposed file size of an ADP file with several 32-bit fields from the first byte as a base.
-- consequently, file_size checks are now made against supposed_size.

- as a consequence of all of the above, riff.c has now lost an additional RIFF size check; initially if RIFF chunk reported a big-enough size than RIFF file realistically has, it would be enough for vgmstream to stop working with said file.
This commit is contained in:
modusc896d352 2024-07-17 21:58:06 -03:00
parent 2eef6b4535
commit edde33489b
6 changed files with 194 additions and 285 deletions

View File

@ -298,7 +298,7 @@ void free_imuse(imuse_codec_data* data);
typedef struct ongakukan_adp_data ongakukan_adp_data;
ongakukan_adp_data* init_ongakukan_adp(STREAMFILE* sf, int32_t data_offset, int32_t data_size,
char sound_is_adpcm, char sample_has_base_setup_from_the_start);
bool sound_is_adpcm);
void decode_ongakukan_adp(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do);
void reset_ongakukan_adp(ongakukan_adp_data* data);
void seek_ongakukan_adp(ongakukan_adp_data* data, int32_t current_sample);

View File

@ -14,6 +14,7 @@
* -- However, much of that support is reliant on a flag that's set to either one of the two codecs depending on the opened file extension.
* Basically, how it works is: if sound data is "PCM16" (available to "wav" and "ads" files), set flag to 0.
* If sound data is "ADPCM" (available to "adp" files), set it to 1.
* Code handles this flag as a boolean var; 0 is "false" and 1 is "true".
* -- As vgmstream has built-in support for the former codec (and the many metas that use it) however, despite being fairly easy to add here,
* re-implementing one from scratch would be a wasted effort regardless; it is consequentially not included. */
@ -36,15 +37,14 @@ struct ongakukan_adp_t
long int samples_filled; /* how many samples were filled to vgmstream buffer. */
long int samples_consumed; /* how many samples vgmstream buffer had to consume. */
char sound_is_adpcm; /* 0 = no (see "additional notes" above) , 1 = yes */
char sample_startpoint_present; /* -1 = failed to make startpoint, 1 = startpoint present */
bool sound_is_adpcm; /* false = no (see "additional notes" above) , true = yes */
bool sample_startpoint_present; /* false = failed to make startpoint, true = startpoint present */
char sample_mode; /* 0 = creates decoding setup, 1 = continue decoding data with setup in place */
char sample_has_base_setup_from_the_start; /* 0 = no, 1 = yes */
char sample_pair_is_decoded; /* 0 = no, 1 = yes */
bool sample_pair_is_decoded; /* false = no, true = yes */
unsigned char base_pair; /* represents a read byte from ADPCM data, consisting of two 4-bit nibbles each.*/
long int base_scale; /* how loud should this sample be. */
void* sample_hist; /* two pairs of signed 16-bit data, representing samples. yes, it is void. */
short int sample_hist[2]; /* two pairs of signed 16-bit data, representing samples. yes, it is void. */
};
/* filter table consisting of 16 numbers each. */
@ -53,18 +53,18 @@ const short int ongakukan_adpcm_filter[16] = { 233, 549, 453, 375, 310, 233, 233
/* streamfile read function declararion, more may be added in the future. */
uint8_t read_u8_wrapper(ongakukan_adp_t* handle);
static uint8_t read_u8_wrapper(ongakukan_adp_t* handle);
/* function declarations for the inner workings of codec data. */
char set_up_sample_startpoint(ongakukan_adp_t* handle);
void decode_ongakukan_adpcm_sample(ongakukan_adp_t* handle, short int* sample_hist);
static bool set_up_sample_startpoint(ongakukan_adp_t* handle);
static void decode_ongakukan_adpcm_samples(ongakukan_adp_t* handle);
/* codec management functions, meant to oversee and supervise ADP data from the top-down.
* in layman terms, they control how ADP data should be handled and when. */
ongakukan_adp_t* boot_ongakukan_adpcm(STREAMFILE* sf, long int data_offset, long int data_size,
char sound_is_adpcm, char sample_has_base_setup_from_the_start)
ongakukan_adp_t* init_ongakukan_adpcm(STREAMFILE* sf, long int data_offset, long int data_size,
bool sound_is_adpcm)
{
ongakukan_adp_t* handle = NULL;
@ -77,50 +77,45 @@ ongakukan_adp_t* boot_ongakukan_adpcm(STREAMFILE* sf, long int data_offset, long
handle->data_offset = data_offset;
handle->start_offset = data_offset;
handle->data_size = data_size;
handle->samples_filled = 0;
handle->samples_consumed = 0;
handle->sample_mode = 0;
handle->sound_is_adpcm = sound_is_adpcm;
handle->sample_has_base_setup_from_the_start = sample_has_base_setup_from_the_start;
handle->sample_startpoint_present = set_up_sample_startpoint(handle);
/* if we failed in planting up the seeds for an ADPCM decoder, we simply throw in the towel and take a walk in the park. */
if (handle->sample_startpoint_present == -1) { goto fail; }
if (handle->sample_startpoint_present == false) { goto fail; }
return handle;
fail:
free_all_ongakukan_adpcm(handle);
ongakukan_adpcm_free(handle);
return NULL;
}
void free_all_ongakukan_adpcm(ongakukan_adp_t* handle)
void ongakukan_adpcm_free(ongakukan_adp_t* handle)
{
if (!handle) return;
close_streamfile(handle->sf);
free(handle->sample_hist);
free(handle);
}
void reset_all_ongakukan_adpcm(ongakukan_adp_t* handle)
void ongakukan_adpcm_reset(ongakukan_adp_t* handle)
{
if (!handle) return;
/* wipe out certain values from handle so we can start over. */
handle->data_offset = handle->start_offset;
handle->sample_pair_is_decoded = 0;
handle->sample_pair_is_decoded = false;
handle->sample_mode = 0;
handle->sample_has_base_setup_from_the_start = 1;
handle->alt_sample_work1 = 0;
handle->alt_sample_work2 = handle->sample_work;
}
void seek_ongakukan_adpcm_pos(ongakukan_adp_t* handle, long int target_sample)
void ongakukan_adpcm_seek(ongakukan_adp_t* handle, long int target_sample)
{
if (!handle) return;
char ts_modulus = 0; /* ts_modulus is here to ensure target_sample gets rounded to a multiple of 2. */
long int ts_data_offset = 0; /* ts_data_offset is basically data_offset but with (left(if PCM)/right(if ADPCM))-shifted target_sample calc by 1. */
if (handle->sound_is_adpcm == 0) { ts_data_offset = target_sample << 1; }
else { ts_data_offset = target_sample >> 1; ts_modulus = target_sample % 2; target_sample = target_sample - ts_modulus; }
ts_data_offset = target_sample >> 1;
ts_modulus = target_sample % 2;
target_sample = target_sample - ts_modulus;
/* if ADPCM, right-shift the former first then have ts_modulus calc remainder of target_sample by 2 so we can subtract it with ts_modulus.
* this is needed for the two counters that the decoder has that can both add and subtract with 2, respectively
* (and in order, too; meaning one counter does "plus 2" while the other does "minus 2",
@ -130,9 +125,8 @@ void seek_ongakukan_adpcm_pos(ongakukan_adp_t* handle, long int target_sample)
* so we'll have data_offset reposition itself to where sound data for that sample ought to be
* and (as of now) reset basically all decode state up to this point so we can continue to decode all sample pairs without issue. */
handle->data_offset = handle->start_offset + ts_data_offset;
handle->sample_pair_is_decoded = 0;
handle->sample_pair_is_decoded = false;
handle->sample_mode = 0;
handle->sample_has_base_setup_from_the_start = 1;
handle->alt_sample_work1 = target_sample;
handle->alt_sample_work2 = handle->sample_work - target_sample;
@ -140,139 +134,88 @@ void seek_ongakukan_adpcm_pos(ongakukan_adp_t* handle, long int target_sample)
* seek_ongakukan_adpcm_pos in its current state is a bit more involved than the above, but works. */
}
long int grab_num_samples_from_ongakukan_adp(ongakukan_adp_t* handle)
long int get_num_samples_from_ongakukan_adpcm(ongakukan_adp_t* handle)
{
if (!handle) return 0;
return handle->sample_work;
}
long int grab_samples_filled_from_ongakukan_adp(ongakukan_adp_t* handle)
void* get_sample_hist_from_ongakukan_adpcm(ongakukan_adp_t* handle)
{
if (!handle) return 0;
return handle->samples_filled;
}
void send_samples_filled_to_ongakukan_adp(long int samples_filled, ongakukan_adp_t* handle)
{
if (!handle) return;
handle->samples_filled = samples_filled;
}
long int grab_samples_consumed_from_ongakukan_adp(ongakukan_adp_t* handle)
{
if (!handle) return 0;
return handle->samples_consumed;
}
void send_samples_consumed_to_ongakukan_adp(long int samples_consumed, ongakukan_adp_t* handle)
{
if (!handle) return;
handle->samples_consumed = samples_consumed;
}
void* grab_sample_hist_from_ongakukan_adp(ongakukan_adp_t* handle)
{
if (!handle) return 0;
return handle->sample_hist;
return &handle->sample_hist;
}
/* function definitions for the inner workings of codec data. */
char set_up_sample_startpoint(ongakukan_adp_t* handle)
static bool set_up_sample_startpoint(ongakukan_adp_t* handle)
{
/* malloc "sample hist" to 2 short ints */
handle->sample_hist = malloc(2 * sizeof(short int));
/* make decoder fail hard if streamfile object isn't opened or downright useless. */
if (!handle->sf) return false;
/* and make decoder fail hard if they don't have "sample hist" and opened streamfile object. */
if (!handle->sample_hist) return -1;
if (!handle->sf) return -1;
if (handle->sound_is_adpcm == 0) {
/* num_samples but for PCM16 sound data. */
handle->sample_work = handle->data_size >> 1;
}
else {
/* num_samples but for Ongakukan ADPCM sound data. */
handle->sample_work = handle->data_size << 1;
}
if (handle->sound_is_adpcm == 0) { return false; }
else { /* num_samples but for Ongakukan ADPCM sound data. */ handle->sample_work = handle->data_size << 1; }
/* set "beginning" and "end" sample vars and send a "message" that we went through no sample yet.*/
handle->alt_sample_work1 = 0;
handle->alt_sample_work2 = handle->sample_work;
handle->sample_pair_is_decoded = 0;
handle->sample_pair_is_decoded = false;
return 1;
return true;
}
void decode_ongakukan_adp_data(ongakukan_adp_t* handle)
void decode_ongakukan_adpcm_data(ongakukan_adp_t* handle)
{
/* set samples_filled to 0 and have our decoder go through every sample that exists in the sound data.*/
handle->samples_filled = 0;
if (handle->sample_pair_is_decoded == 0)
{
handle->samples_filled = 0;
handle->samples_consumed = 0;
}
decode_ongakukan_adpcm_sample(handle, handle->sample_hist);
decode_ongakukan_adpcm_samples(handle);
/* if setup is established for further decoding, switch gears and have the decoder use that setup for as long as possible. */
if (handle->sample_has_base_setup_from_the_start == 1)
{
handle->sample_has_base_setup_from_the_start = 0;
handle->sample_mode = 1;
}
/* if sample pair is decoded, advance to next byte, tell our handle that we went through 2 samples and make decoder go through next available data again. */
if (handle->sample_pair_is_decoded == 1)
if (handle->sample_pair_is_decoded == true)
{
handle->data_offset++;
handle->alt_sample_work1 += 2;
handle->alt_sample_work2 -= 2;
handle->samples_filled = 2;
handle->sample_pair_is_decoded = 0;
handle->sample_pair_is_decoded = false;
}
}
void decode_ongakukan_adpcm_sample(ongakukan_adp_t* handle, short int* sample_hist)
static void decode_ongakukan_adpcm_samples(ongakukan_adp_t* handle)
{
unsigned char nibble1 = 0, nibble2 = 0; /* two chars representing a 4-bit nibble. */
long int nibble1_1 = 0, nibble2_1 = 0; /* two long ints representing pure sample data. */
if (handle->sample_pair_is_decoded == 0)
if (handle->sample_pair_is_decoded == false)
{
/* sample_mode being 0 means we can just do a setup for future sample decoding so we have nothing to worry about in the future. */
if (handle->sample_mode == 0)
{
/* set "base scale", two "sample hist"s, and "base pair", respectively. */
handle->base_scale = 0x10; /* yes, this is how "base scale" is set; to 16. */
*(sample_hist+0) = 0; /* dereference first sample hist pos to something we can use. */
*(sample_hist+1) = 0; /* dereference second sample hist pos to something we can use. */
handle->base_pair = 0; /* set representing byte to 0 while we're at it. */
handle->base_scale = 0x10;
handle->sample_hist[0] = 0;
handle->sample_hist[1] = 0;
handle->base_pair = 0;
handle->sample_mode = 1; /* indicates we have the setup we need to decode samples. */
}
/* "pinch off" of a single-byte read. remember that we need two nibbles. */
handle->base_pair = (uint8_t)read_u8_wrapper(handle);
/* now pick two nibbles, subtract them, reverse the order of said nibbles so we can calc them one-by-one,
* and finally tell handle that we're done with two samples and would like to take a break, please. */
nibble1 = handle->base_pair & 0xf;
nibble1_1 = nibble1 + -8;
nibble2 = (handle->base_pair >> 4) & 0xf;
nibble2_1 = nibble2 + -8;
nibble2_1 = nibble2_1 * handle->base_scale;
*(sample_hist+0) = *(sample_hist+1) + nibble2_1;
handle->sample_hist[0] = handle->sample_hist[1] + nibble2_1;
handle->base_scale = (handle->base_scale * (ongakukan_adpcm_filter[nibble2])) >> 8;
nibble1_1 = nibble1_1 * handle->base_scale;
*(sample_hist+1) = *(sample_hist+0) + nibble1_1;
handle->sample_hist[1] = handle->sample_hist[0] + nibble1_1;
handle->base_scale = (handle->base_scale * (ongakukan_adpcm_filter[nibble1])) >> 8;
handle->sample_pair_is_decoded = 1;
handle->sample_pair_is_decoded = true;
}
}
/* streamfile read function definitions at the very bottom. */
uint8_t read_u8_wrapper(ongakukan_adp_t* handle)
static uint8_t read_u8_wrapper(ongakukan_adp_t* handle)
{
/* if data_offset minus start_offset goes beyond data_size, return 0. */
if ((handle->data_offset - handle->start_offset) > handle->data_size) return 0;
/* if data_offset minus start_offset goes below 0, same. */
if ((handle->data_offset - handle->start_offset) < 0) return 0;
/* otherwise, read the byte from data_offset and see history get made. */
return read_u8((off_t)(handle->data_offset), handle->sf);
}

View File

@ -1,7 +1,7 @@
#ifndef _ONGAKUKAN_ADP_LIB_
#define _ONGAKUKAN_ADP_LIB_
#ifndef _ONGAKUKAN_ADP_LIB_H_
#define _ONGAKUKAN_ADP_LIB_H_
/* Ongakukan ADP codec, found in PS2 and PSP games. */
/* Ongakukan ADPCM codec, found in PS2 and PSP games. */
#include "../../util/reader_sf.h"
@ -9,23 +9,19 @@
typedef struct ongakukan_adp_t ongakukan_adp_t;
/* function declaration for we need to set up the codec data. */
ongakukan_adp_t* boot_ongakukan_adpcm(STREAMFILE* sf, long int data_offset, long int data_size,
char sample_needs_setup, char sample_has_base_setup_from_the_start);
ongakukan_adp_t* init_ongakukan_adpcm(STREAMFILE* sf, long int data_offset, long int data_size,
bool sound_is_adpcm);
/* function declaration for freeing all memory related to ongakukan_adp_t struct var. */
void free_all_ongakukan_adpcm(ongakukan_adp_t* handle);
void reset_all_ongakukan_adpcm(ongakukan_adp_t* handle);
void seek_ongakukan_adpcm_pos(ongakukan_adp_t* handle, long int target_sample);
void ongakukan_adpcm_free(ongakukan_adp_t* handle);
void ongakukan_adpcm_reset(ongakukan_adp_t* handle);
void ongakukan_adpcm_seek(ongakukan_adp_t* handle, long int target_sample);
/* function declaration for when we need to get (and send) certain values from ongakukan_adp_t handle */
long int grab_num_samples_from_ongakukan_adp(ongakukan_adp_t* handle);
long int grab_samples_filled_from_ongakukan_adp(ongakukan_adp_t* handle);
void send_samples_filled_to_ongakukan_adp(long int samples_filled, ongakukan_adp_t* handle);
long int grab_samples_consumed_from_ongakukan_adp(ongakukan_adp_t* handle);
void send_samples_consumed_to_ongakukan_adp(long int samples_consumed, ongakukan_adp_t* handle);
void* grab_sample_hist_from_ongakukan_adp(ongakukan_adp_t* handle);
long int get_num_samples_from_ongakukan_adpcm(ongakukan_adp_t* handle);
void* get_sample_hist_from_ongakukan_adpcm(ongakukan_adp_t* handle);
/* function declaration for actually decoding samples, can't be that hard, right? */
void decode_ongakukan_adp_data(ongakukan_adp_t* handle);
void decode_ongakukan_adpcm_data(ongakukan_adp_t* handle);
#endif /* _ONGAKUKAN_ADP_LIB_ */
#endif // _ONGAKUKAN_ADP_LIB_H_

View File

@ -4,94 +4,87 @@
struct ongakukan_adp_data
{
void* handle;
int16_t* samples;
int32_t samples_done;
int32_t samples_filled;
int32_t samples_consumed;
int32_t getting_samples;
STREAMFILE* sf;
void* handle;
int16_t* samples;
int32_t samples_done;
bool samples_filled; /* false - no, true - yes */
int32_t getting_samples; /* initialized to 2 on decode_ongakukan_adp. i mean, we literally get two decoded samples here. */
STREAMFILE* sf;
};
ongakukan_adp_data* init_ongakukan_adp(STREAMFILE* sf, int32_t data_offset, int32_t data_size,
char sample_needs_setup, char sample_has_base_setup_from_the_start)
bool sound_is_adpcm)
{
ongakukan_adp_data* data = NULL;
ongakukan_adp_data* data = NULL;
data = calloc(1, sizeof(ongakukan_adp_data));
if (!data) goto fail;
data = calloc(1, sizeof(ongakukan_adp_data));
if (!data) goto fail;
/* reopen STREAMFILE from here, then pass it as an argument for our init function. we need to be able to read the file directly. */
data->sf = reopen_streamfile(sf, 0);
if (!data->sf) goto fail;
data->handle = boot_ongakukan_adpcm(data->sf, (long int)(data_offset), (long int)(data_size),
sample_needs_setup, sample_has_base_setup_from_the_start);
if (!data->handle) goto fail;
/* reopen STREAMFILE from here, then pass it as an argument for our init function. */
data->sf = reopen_streamfile(sf, 0);
if (!data->sf) goto fail;
data->handle = init_ongakukan_adpcm(data->sf, (long int)(data_offset), (long int)(data_size),
sound_is_adpcm);
if (!data->handle) goto fail;
printf(" return data; \n");
return data;
return data;
fail:
free_ongakukan_adp(data);
return NULL;
free_ongakukan_adp(data);
return NULL;
}
void decode_ongakukan_adp(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do)
{
ongakukan_adp_data* data = vgmstream->codec_data;
ongakukan_adp_data* data = vgmstream->codec_data;
data->samples_done = 0;
data->samples_filled = 0;
data->samples_consumed = 0;
data->samples = (int16_t*)grab_sample_hist_from_ongakukan_adp(data->handle); /* this'll return a pointer from the handle containing the samples themselves. */
while (data->samples_done < samples_to_do)
{
if (data->samples_filled)
{
data->getting_samples = data->samples_filled;
if (data->getting_samples > samples_to_do - data->samples_done)
data->getting_samples = samples_to_do - data->samples_done;
data->getting_samples = 2;
data->samples_done = 0;
data->samples_filled = false;
/* ^ samples_filled is boolean here because we need to simplify how decoding will work here.
* so, rather than making samples_filled into a long int counter,
* we make it into a boolean flag instead so as to let data->samples_done shine as a counter
* and the decoder to do its job without worry. */
memcpy(outbuf + data->samples_done,
data->samples + data->samples_consumed,
data->getting_samples * sizeof(int16_t));
data->samples_done += data->getting_samples;
while (data->samples_done < samples_to_do)
{
if (data->samples_filled)
{
memcpy(outbuf + data->samples_done,
data->samples,
data->getting_samples * sizeof(int16_t));
data->samples_done += data->getting_samples;
/* mark consumed samples. */
data->samples_consumed += data->getting_samples;
data->samples_filled -= data->getting_samples;
/* and keep the lib updated while doing so. */
send_samples_consumed_to_ongakukan_adp((long int)(data->samples_consumed), data->handle);
send_samples_filled_to_ongakukan_adp((long int)(data->samples_filled), data->handle);
}
else { decode_ongakukan_adp_data(data->handle);
data->samples_filled = (int32_t)grab_samples_filled_from_ongakukan_adp(data->handle);
data->samples_consumed = (int32_t)grab_samples_consumed_from_ongakukan_adp(data->handle);
data->samples = (int16_t*)grab_sample_hist_from_ongakukan_adp(data->handle); }
}
data->samples_filled = false;
}
else { decode_ongakukan_adpcm_data(data->handle);
data->samples_filled = true;
data->samples = (int16_t*)get_sample_hist_from_ongakukan_adpcm(data->handle); }
}
}
void reset_ongakukan_adp(ongakukan_adp_data* data)
{
if (!data) return;
reset_all_ongakukan_adpcm(data->handle);
if (!data) return;
ongakukan_adpcm_reset(data->handle);
}
void seek_ongakukan_adp(ongakukan_adp_data* data, int32_t current_sample)
{
if (!data) return;
seek_ongakukan_adpcm_pos(data->handle, current_sample);
if (!data) return;
ongakukan_adpcm_seek(data->handle, current_sample);
}
void free_ongakukan_adp(ongakukan_adp_data* data)
{
if (!data) return;
free_all_ongakukan_adpcm(data->handle);
free(data);
if (!data) return;
close_streamfile(data->sf);
ongakukan_adpcm_free(data->handle);
free(data->samples);
free(data);
}
int32_t ongakukan_adp_get_samples(ongakukan_adp_data* data)
{
if (!data) return 0;
return (int32_t)(grab_num_samples_from_ongakukan_adp(data->handle));
if (!data) return 0;
return (int32_t)get_num_samples_from_ongakukan_adpcm(data->handle);
}

View File

@ -4,113 +4,100 @@
/* Ongakukan RIFF with "ADP" extension [Train Simulator - Midousuji-sen (PS2)] */
VGMSTREAM* init_vgmstream_ongakukan_adp(STREAMFILE* sf)
{
VGMSTREAM* vgmstream = NULL;
off_t start_offset;
size_t file_size;
int has_data_chunk = 0, has_fact_chunk = 0, found_start_offset = 0;
int loop_flag = 0;
int riff_wave_header_size = 0x2c;
char sound_is_adpcm = 0, sample_has_base_setup_from_the_start = 0;
/* ^ the entire RIFF WAVE header size, set to this fixed number
* because *surprise* this is also how sound data begins. */
int32_t fmt_size, fmt_offset, offset_of_supposed_last_chunk;
int32_t sample_rate, bitrate, data_size;
int16_t num_channels, block_size;
VGMSTREAM* vgmstream = NULL;
off_t start_offset;
size_t file_size;
bool has_data_chunk = false, has_fact_chunk = false;
int loop_flag = 0;
int riff_wave_header_size = 0x2c;
/* ^ where sound data begins, as a consequence their tools couldn't even write full RIFF WAVE header to file beyond that point.. */
bool sound_is_adpcm = false;
int32_t supposed_size, fmt_size, fmt_offset, offset_of_supposed_last_chunk;
int32_t sample_rate, data_size;
int16_t num_channels, block_size;
/* RIFF+WAVE checks */
if (!is_id32be(0x00, sf, "RIFF")) goto fail;
if (!is_id32be(0x08, sf, "WAVE")) goto fail;
/* WAVE "fmt " check */
if (!is_id32be(0x0c, sf, "fmt ")) goto fail;
/* "adp" extension check (literally only one) */
if (!check_extensions(sf, "adp")) goto fail;
/* catch adp file size from here and use it whenever needed. */
file_size = get_streamfile_size(sf);
/* RIFF+WAVE checks */
if (!is_id32be(0x00, sf, "RIFF")) goto fail;
if (!is_id32be(0x08, sf, "WAVE")) goto fail;
/* WAVE "fmt " check */
if (!is_id32be(0x0c, sf, "fmt ")) goto fail;
/* "adp" extension check (literally only one) */
if (!check_extensions(sf, "adp")) goto fail;
/* RIFF size from adp file (e.g: 10MB) can go beyond actual adp file size (e.g: 2MB),
* have vgmstream call it quits if the former is reported to be less than the latter. */
if (read_s32le(0x04, sf) < file_size) goto fail;
/* catch adp file size from here and use it whenever needed. */
file_size = get_streamfile_size(sf);
/* read entire WAVE "fmt " chunk. we start by reading fmt_size from yours truly and setting fmt_offset. */
fmt_size = read_s32le(0x10, sf);
fmt_offset = 0x14;
if ((fmt_size >= 0x10) && (fmt_size <= 0x12)) /* fmt_size is mostly 0x10, rarely 0x12 */
{
if (read_s16le(fmt_offset + 0, sf) != 1) goto fail; /* chunk reports codec number as signed little-endian PCM, couldn't be more wrong. */
num_channels = read_s16le(fmt_offset + 2, sf);
sample_rate = read_s32le(fmt_offset + 4, sf);
bitrate = read_s32le(fmt_offset + 8, sf);
/* ^ yes, this is technically correct, tho does not reflect actual data.
* chunk reports bitrate as if it was a 16-bit PCM file. */
block_size = read_s16le(fmt_offset + 12, sf); /* mostly 2, rarely 4. */
if (read_s16le(fmt_offset + 14, sf) != 0x10) goto fail; /* bit depth as chunk reports it. */
/* additional checks, this time with bitrate field in the chunk. */
if (bitrate != (sample_rate * block_size)) goto fail;
/* if fmt_size == 0x12 there is an additional s16 field that's always zero. */
}
else {
goto fail;
}
/* RIFF size from adp file can go beyond actual size (e.g: reported 10MB vs 2MB). do quick calcs around this. */
supposed_size = ((read_s32le(0x04, sf) - 0x24) >> 2) + 0x2c;
if (file_size != supposed_size) goto fail;
/* now calc the var so we can read either "data" or "fact" chunk; */
offset_of_supposed_last_chunk = fmt_offset + fmt_size;
/* read entire WAVE "fmt " chunk. we start by reading fmt_size from yours truly and setting fmt_offset. */
fmt_size = read_s32le(0x10, sf);
fmt_offset = 0x14;
if ((fmt_size >= 0x10) && (fmt_size <= 0x12)) /* depending on the adp, fmt_size alternates between 0x10 and 0x12 */
{
if (read_s16le(fmt_offset + 0, sf) != 1) goto fail; /* chunk reports codec number as signed little-endian PCM, couldn't be more wrong. */
num_channels = read_s16le(fmt_offset + 2, sf);
sample_rate = read_s32le(fmt_offset + 4, sf);
if (read_s16le(fmt_offset + 14, sf) != 0x10) goto fail; /* bit depth as chunk reports it. */
/* rest of fmt header is the usual header for 16-bit PCM wav files: bitrate, block size, and the like (see riff.c) */
/* if fmt_size == 0x12 there is an additional s16 field that's always zero. */
}
else {
goto fail;
}
/* then read either one of the two chunks, both cannot co-exist it seems.
* while there, we set start_offset by themselves instead of relying on both chunks to do so.
* see comments for code handling both chunks below for more info. */
if (is_id32be(offset_of_supposed_last_chunk + 0, sf, "data")) has_data_chunk = 1;
if (is_id32be(offset_of_supposed_last_chunk + 0, sf, "fact")) has_fact_chunk = 1;
/* now calc the var so we can read either "data" or "fact" chunk; */
offset_of_supposed_last_chunk = fmt_offset + fmt_size;
/* and because sound data *must* start at 0x2c, they have to bork both chunks too, so they're now essentially useless.
* well, except for trying to deduct how many samples a sound actually has*/
if (has_data_chunk)
{
/* RIFF adp files have borked "data" chunk so much it's not even remotely useful for... well, *anything*, really.
* it doesn't report actual data size as RIFF WAVE files usually do, instead we're left with basically RIFF size with ~50 less numbers now. */
if (read_s32le(offset_of_supposed_last_chunk + 4, sf) < file_size) goto fail;
/* ^ reported data size is meant to be bigger than actual adp size, have vgmstream throw out the towel if it isn't. */
}
/* we need to get to the last WAVE chunk manually, and that means the calc below. */
offset_of_supposed_last_chunk = fmt_offset + fmt_size;
if (is_id32be(offset_of_supposed_last_chunk + 0, sf, "data")) has_data_chunk = true;
if (is_id32be(offset_of_supposed_last_chunk + 0, sf, "fact")) has_fact_chunk = true;
if (has_fact_chunk)
{
/* RIFF adp files also borked "fact" chunk so it no longer reports useful info.
* instead it just leaves out a s16 field containing a static number. */
if (read_s16le(offset_of_supposed_last_chunk + 4, sf) != 4) goto fail;
/* ^ this number is supposed to be 4, have vgmstream ragequit if it isn't. */
}
/* and because sound data *must* start at 0x2c, they have to bork both chunks too, so they're now essentially useless.
* they're basically leftovers from original (lossless) WAV files at this point. */
if (has_data_chunk)
{
/* RIFF adp files have leftover "data" chunk size... that does NOT match the ADP file size at hand. */
supposed_size = (read_s32le(offset_of_supposed_last_chunk + 4, sf) >> 2) + 0x2c;
if (file_size != supposed_size) goto fail;
}
/* set start_offset value to riff_wave_header_size
* and calculate data_size by ourselves */
start_offset = riff_wave_header_size;
data_size = (int32_t)(file_size) - riff_wave_header_size;
if (has_fact_chunk)
{
/* RIFF adp files have also cut off "fact" chunk so we're just left with a useless number now. */
if (read_s16le(offset_of_supposed_last_chunk + 4, sf) != 4) goto fail;
}
/* Ongagukan games using this format just read it by checking "ADP" extension
* in an provided file name of a programmer's own choosing,
* and if it's there they just read the reported "number of samples" and sample_rate from RIFF WAVE "fmt " chunk
* based on an already-opened file with that same name.
* they also calculate start_offset and data_size in much the same manner. */
/* set start_offset value to riff_wave_header_size and calculate data_size by ourselves, basically how Ongakukan does it also. */
start_offset = riff_wave_header_size;
data_size = (int32_t)(file_size) - riff_wave_header_size;
/* silly flags, needed to init our custom decoder. */
sound_is_adpcm = 1;
sample_has_base_setup_from_the_start = 1;
/* Ongagukan games using this format just read it by checking "ADP" extension in an provided file name of a programmer's own choosing,
* and if extension is there they just read the reported "number of samples" and "sample_rate" vars
* from RIFF WAVE "fmt " chunk based on an already-opened file with that same name.
* and they don't even read RIFF chunks, they just pick these two vars and that's basically it. */
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(num_channels, loop_flag);
if (!vgmstream) goto fail;
/* our custom decoder needs at least one flag set. */
sound_is_adpcm = true;
vgmstream->meta_type = meta_ONGAKUKAN_RIFF_ADP;
vgmstream->sample_rate = sample_rate;
vgmstream->codec_data = init_ongakukan_adp(sf, start_offset, data_size, sound_is_adpcm, sample_has_base_setup_from_the_start);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_ONGAKUKAN_ADPCM;
vgmstream->layout_type = layout_none;
vgmstream->num_samples = ongakukan_adp_get_samples(vgmstream->codec_data);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(num_channels, loop_flag);
if (!vgmstream) goto fail;
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;
vgmstream->meta_type = meta_ONGAKUKAN_RIFF_ADP;
vgmstream->sample_rate = sample_rate;
vgmstream->codec_data = init_ongakukan_adp(sf, start_offset, data_size, sound_is_adpcm);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_ONGAKUKAN_ADPCM;
vgmstream->layout_type = layout_none;
vgmstream->num_samples = ongakukan_adp_get_samples(vgmstream->codec_data);
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -350,7 +350,6 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) {
off_t mwv_pflt_offset = 0;
off_t mwv_ctrl_offset = 0;
int ignore_riff_size = 0;
int riff_size_bigger_than_pcm_file = 0;
/* checks*/
@ -474,17 +473,8 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) {
file_size -= 0x40; /* [Megami no Etsubo (PSP)] (has extra padding in all files) */
else if (codec == 0x0011 && file_size - riff_size - 0x08 <= 0x900 && is_id32be(riff_size + 0x08, sf, "cont"))
riff_size = file_size - 0x08; /* [Shin Megami Tensei: Imagine (PC)] (extra "cont" info 0x800/0x900 chunk) */
riff_size = file_size - 0x08; /* [Shin Megami Tensei: Imagine (PC)] (extra "cont" info 0x800/0x900 chunk) */
else if (codec == 0x0001 && riff_size > file_size + 0x8000)
riff_size_bigger_than_pcm_file = 1; /* [Train Simulator: Midousuji-hen (PS2)] (reports bigger RIFF sizes than should be possible) */
}
/* throw out when RIFF size is bigger than actual file size */
if (riff_size_bigger_than_pcm_file)
{
/* vgm_logi("RIFF: reported size (%d) bigger than actual file (%d)\n", riff_size, file_size); */
goto fail;
}
/* check for truncated RIFF */
@ -1205,7 +1195,7 @@ VGMSTREAM* init_vgmstream_rifx(STREAMFILE* sf) {
/* end must add +1, but check in case of faulty tools */
if (vgmstream->loop_end_sample - 1 == vgmstream->num_samples)
vgmstream->loop_end_sample--;
vgmstream->meta_type = meta_RIFX_WAVE_smpl;
}
}