mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-02-25 22:38:11 +01:00
Fix some Ongakukan .adp + cleanup
This commit is contained in:
parent
b916d2685e
commit
33f01354e6
@ -49,7 +49,7 @@ struct ongakukan_adp_t
|
||||
|
||||
/* filter table consisting of 16 numbers each. */
|
||||
|
||||
const short int ongakukan_adpcm_filter[16] = { 233, 549, 453, 375, 310, 233, 233, 233, 233, 233, 233, 233, 310, 375, 453, 549 };
|
||||
static const short int ongakukan_adpcm_filter[16] = { 233, 549, 453, 375, 310, 233, 233, 233, 233, 233, 233, 233, 310, 375, 453, 549 };
|
||||
|
||||
/* streamfile read function declararion, more may be added in the future. */
|
||||
|
||||
@ -63,13 +63,15 @@ 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* init_ongakukan_adpcm(STREAMFILE* sf, long int data_offset, long int data_size,
|
||||
bool sound_is_adpcm)
|
||||
ongakukan_adp_t* ongakukan_adpcm_init(STREAMFILE* sf, long int data_offset, long int data_size, bool sound_is_adpcm)
|
||||
{
|
||||
ongakukan_adp_t* handle = NULL;
|
||||
|
||||
/* allocate handle using malloc. */
|
||||
handle = malloc(sizeof(ongakukan_adp_t));
|
||||
if (!sound_is_adpcm)
|
||||
return NULL;
|
||||
|
||||
/* allocate handle. */
|
||||
handle = calloc(1, sizeof(ongakukan_adp_t));
|
||||
if (!handle) goto fail;
|
||||
|
||||
/* now, to set up the rest of the handle with the data we have... */
|
||||
@ -134,16 +136,16 @@ void ongakukan_adpcm_seek(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 get_num_samples_from_ongakukan_adpcm(ongakukan_adp_t* handle)
|
||||
long int ongakukan_adpcm_get_num_samples(ongakukan_adp_t* handle)
|
||||
{
|
||||
if (!handle) return 0;
|
||||
return handle->sample_work;
|
||||
}
|
||||
|
||||
void* get_sample_hist_from_ongakukan_adpcm(ongakukan_adp_t* handle)
|
||||
short* ongakukan_adpcm_get_sample_hist(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. */
|
||||
@ -163,7 +165,7 @@ static bool set_up_sample_startpoint(ongakukan_adp_t* handle)
|
||||
return true;
|
||||
}
|
||||
|
||||
void decode_ongakukan_adpcm_data(ongakukan_adp_t* handle)
|
||||
void ongakukan_adpcm_decode_data(ongakukan_adp_t* handle)
|
||||
{
|
||||
/* set samples_filled to 0 and have our decoder go through every sample that exists in the sound data.*/
|
||||
decode_ongakukan_adpcm_samples(handle);
|
||||
|
@ -9,7 +9,7 @@
|
||||
typedef struct ongakukan_adp_t ongakukan_adp_t;
|
||||
|
||||
/* function declaration for we need to set up the codec data. */
|
||||
ongakukan_adp_t* init_ongakukan_adpcm(STREAMFILE* sf, long int data_offset, long int data_size,
|
||||
ongakukan_adp_t* ongakukan_adpcm_init(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. */
|
||||
@ -18,10 +18,10 @@ 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 get_num_samples_from_ongakukan_adpcm(ongakukan_adp_t* handle);
|
||||
void* get_sample_hist_from_ongakukan_adpcm(ongakukan_adp_t* handle);
|
||||
long int ongakukan_adpcm_get_num_samples(ongakukan_adp_t* handle);
|
||||
short* ongakukan_adpcm_get_sample_hist(ongakukan_adp_t* handle);
|
||||
|
||||
/* function declaration for actually decoding samples, can't be that hard, right? */
|
||||
void decode_ongakukan_adpcm_data(ongakukan_adp_t* handle);
|
||||
void ongakukan_adpcm_decode_data(ongakukan_adp_t* handle);
|
||||
|
||||
#endif // _ONGAKUKAN_ADP_LIB_H_
|
||||
#endif
|
||||
|
@ -2,8 +2,7 @@
|
||||
#include "coding.h"
|
||||
#include "libs/ongakukan_adp_lib.h"
|
||||
|
||||
struct ongakukan_adp_data
|
||||
{
|
||||
struct ongakukan_adp_data {
|
||||
void* handle;
|
||||
int16_t* samples;
|
||||
int32_t samples_done;
|
||||
@ -12,9 +11,7 @@ struct ongakukan_adp_data
|
||||
STREAMFILE* sf;
|
||||
};
|
||||
|
||||
ongakukan_adp_data* init_ongakukan_adp(STREAMFILE* sf, int32_t data_offset, int32_t data_size,
|
||||
bool sound_is_adpcm)
|
||||
{
|
||||
ongakukan_adp_data* init_ongakukan_adp(STREAMFILE* sf, int32_t data_offset, int32_t data_size, bool sound_is_adpcm) {
|
||||
ongakukan_adp_data* data = NULL;
|
||||
|
||||
data = calloc(1, sizeof(ongakukan_adp_data));
|
||||
@ -23,8 +20,8 @@ ongakukan_adp_data* init_ongakukan_adp(STREAMFILE* sf, int32_t data_offset, int3
|
||||
/* 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);
|
||||
|
||||
data->handle = ongakukan_adpcm_init(data->sf, (long int)(data_offset), (long int)(data_size), sound_is_adpcm);
|
||||
if (!data->handle) goto fail;
|
||||
|
||||
return data;
|
||||
@ -33,57 +30,47 @@ fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void decode_ongakukan_adp(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do)
|
||||
{
|
||||
void decode_ongakukan_adp(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do) {
|
||||
ongakukan_adp_data* data = vgmstream->codec_data;
|
||||
|
||||
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. */
|
||||
|
||||
while (data->samples_done < samples_to_do)
|
||||
{
|
||||
if (data->samples_filled)
|
||||
{
|
||||
memcpy(outbuf + data->samples_done,
|
||||
data->samples,
|
||||
data->getting_samples * sizeof(int16_t));
|
||||
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;
|
||||
|
||||
data->samples_filled = false;
|
||||
}
|
||||
else { decode_ongakukan_adpcm_data(data->handle);
|
||||
else {
|
||||
ongakukan_adpcm_decode_data(data->handle);
|
||||
|
||||
data->samples_filled = true;
|
||||
data->samples = (int16_t*)get_sample_hist_from_ongakukan_adpcm(data->handle); }
|
||||
data->samples = ongakukan_adpcm_get_sample_hist(data->handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void reset_ongakukan_adp(ongakukan_adp_data* data)
|
||||
{
|
||||
void reset_ongakukan_adp(ongakukan_adp_data* data) {
|
||||
if (!data) return;
|
||||
ongakukan_adpcm_reset(data->handle);
|
||||
}
|
||||
|
||||
void seek_ongakukan_adp(ongakukan_adp_data* data, int32_t current_sample)
|
||||
{
|
||||
void seek_ongakukan_adp(ongakukan_adp_data* data, int32_t current_sample) {
|
||||
if (!data) return;
|
||||
ongakukan_adpcm_seek(data->handle, current_sample);
|
||||
}
|
||||
|
||||
void free_ongakukan_adp(ongakukan_adp_data* data)
|
||||
{
|
||||
void free_ongakukan_adp(ongakukan_adp_data* data) {
|
||||
if (!data) return;
|
||||
close_streamfile(data->sf);
|
||||
ongakukan_adpcm_free(data->handle);
|
||||
free(data);
|
||||
}
|
||||
|
||||
int32_t ongakukan_adp_get_samples(ongakukan_adp_data* data)
|
||||
{
|
||||
int32_t ongakukan_adp_get_samples(ongakukan_adp_data* data) {
|
||||
if (!data) return 0;
|
||||
return (int32_t)get_num_samples_from_ongakukan_adpcm(data->handle);
|
||||
return (int32_t)ongakukan_adpcm_get_num_samples(data->handle);
|
||||
}
|
||||
|
@ -1,89 +1,70 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* Ongakukan RIFF with "ADP" extension [Train Simulator - Midousuji-sen (PS2)] */
|
||||
VGMSTREAM* init_vgmstream_ongakukan_adp(STREAMFILE* sf)
|
||||
{
|
||||
/* Ongakukan RIFF with "ADP" extension [Train Simulator: Midousuji-sen (PS2), Mobile Train Simulator (PSP)] */
|
||||
VGMSTREAM* init_vgmstream_ongakukan_adp(STREAMFILE* sf) {
|
||||
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 fmt_size, fmt_offset;
|
||||
int32_t sample_rate, data_size;
|
||||
int16_t num_channels, block_size;
|
||||
int16_t channels;
|
||||
|
||||
/* 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);
|
||||
/* checks */
|
||||
if (!is_id32be(0x00, sf, "RIFF"))
|
||||
return NULL;
|
||||
if (!check_extensions(sf, "adp"))
|
||||
return NULL;
|
||||
|
||||
/* 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;
|
||||
/* Format starts like RIFF but doesn't have valid chunks beyond fmt (ADPCM data overwrites anything after 0x2c). */
|
||||
|
||||
start_offset = 0x2c; /* fixed values, basically how Ongakukan does it */
|
||||
data_size = get_streamfile_size(sf) - start_offset;
|
||||
|
||||
/* RIFF size seem to match original PCM .wav, while encoded .adp data equals or is slightly smaller that that */
|
||||
uint32_t expected_size = (read_u32le(0x04, sf) - 0x24);
|
||||
uint32_t pcm_size = data_size * 2 * sizeof(short); // * channels
|
||||
if (pcm_size > expected_size)
|
||||
return NULL;
|
||||
|
||||
if (!is_id32be(0x08, sf, "WAVE"))
|
||||
return NULL;
|
||||
if (!is_id32be(0x0c, sf, "fmt "))
|
||||
return NULL;
|
||||
|
||||
/* 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 {
|
||||
/* depending on the adp, fmt_size alternates between 0x10 and 0x12 */
|
||||
if (fmt_size < 0x10 || fmt_size > 0x12)
|
||||
goto fail;
|
||||
}
|
||||
fmt_offset = 0x14;
|
||||
|
||||
/* now calc the var so we can read either "data" or "fact" chunk; */
|
||||
offset_of_supposed_last_chunk = fmt_offset + fmt_size;
|
||||
if (read_s16le(fmt_offset + 0x00, sf) != 0x0001) /* PCM format */
|
||||
goto fail;
|
||||
channels = read_s16le(fmt_offset + 0x02, sf);
|
||||
if (channels != 1) /* not seen (decoder can't handle it) */
|
||||
goto fail;
|
||||
sample_rate = read_s32le(fmt_offset + 0x04, sf);
|
||||
if (read_s16le(fmt_offset + 0x0e, sf) != 16) /* PCM bit depth */
|
||||
goto fail;
|
||||
/* 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 may be is an additional s16 field that's always zero, but not always. */
|
||||
|
||||
/* 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;
|
||||
/* next chunk is at fixed offset, regardless of fmt_size (fmt_size 0x12 with "data" at 0x24 is possible).
|
||||
* "data" has chunk size (does not match ADP size but original WAV) and "fact" chunk size 0x04 cut off) */
|
||||
if (!is_id32be(0x24, sf, "data") && !is_id32be(0x26, sf, "fact"))
|
||||
goto fail;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
/* Ongagukan games using this format just read it by checking "ADP" extension in an provided file name of a programmer's own choosing,
|
||||
/* Ongagukan games using this format just read it by checking "ADP" extension in a provided file name,
|
||||
* 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. */
|
||||
|
||||
/* our custom decoder needs at least one flag set. */
|
||||
sound_is_adpcm = true;
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(num_channels, loop_flag);
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_ONGAKUKAN_RIFF_ADP;
|
||||
|
Loading…
x
Reference in New Issue
Block a user