From 7f1641e82065bbc21bb1b9fa9fd5713ccb4a7081 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 13 Apr 2024 20:23:14 +0200 Subject: [PATCH 1/6] Fix 6ch .sps opus [EA Sports FC24 (PC)] --- src/coding/ffmpeg_decoder_custom_opus.c | 30 +++++++++++++++---------- src/meta/ea_eaac.c | 28 ++++++++++++++++------- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/src/coding/ffmpeg_decoder_custom_opus.c b/src/coding/ffmpeg_decoder_custom_opus.c index 36f643ef..4922c7aa 100644 --- a/src/coding/ffmpeg_decoder_custom_opus.c +++ b/src/coding/ffmpeg_decoder_custom_opus.c @@ -514,7 +514,7 @@ static size_t make_opus_header(uint8_t* buf, int buf_size, opus_config *cfg) { /* set mapping family */ if (cfg->channels > 2 || cfg->stream_count > 1) { - mapping_family = 1; //todo test 255 + mapping_family = 1; header_size += 0x01 + 0x01 + cfg->channels; /* table size */ } @@ -525,7 +525,7 @@ static size_t make_opus_header(uint8_t* buf, int buf_size, opus_config *cfg) { if (header_size > buf_size) { VGM_LOG("OPUS: buffer can't hold header\n"); - goto fail; + return 0; } put_u32be(buf+0x00, get_id32be("Opus")); @@ -539,21 +539,29 @@ static size_t make_opus_header(uint8_t* buf, int buf_size, opus_config *cfg) { /* set mapping table */ if (mapping_family > 0) { - int i; + /* test if external mappings are correctly set, as incorrect values result in wrong output + * (ex. all 0s would mean "write channel L in every channel)")*/ + bool mappings_set = false; + for (int i = 0; i < cfg->channels; i++) { + if (cfg->channel_mapping[i]) { + mappings_set = true; + break; + } + } /* total streams (mono/stereo) */ put_u8(buf+0x13, cfg->stream_count); /* stereo streams (6ch can be 2ch+2ch+1ch+1ch = 2 coupled in 4 streams) */ put_u8(buf+0x14, cfg->coupled_count); + /* mapping per channel (order of channels, ex: 00 01 04 05 02 03) */ - for (i = 0; i < cfg->channels; i++) { - put_u8(buf+0x15+i, cfg->channel_mapping[i]); + for (int i = 0; i < cfg->channels; i++) { + uint8_t mapping = (mappings_set) ? cfg->channel_mapping[i] : i; + put_u8(buf+0x15+i, mapping); } } return header_size; -fail: - return 0; } static size_t make_opus_comment(uint8_t* buf, int buf_size) { @@ -568,11 +576,11 @@ static size_t make_opus_comment(uint8_t* buf, int buf_size) { if (comment_size > buf_size) { VGM_LOG("OPUS: buffer can't hold comment\n"); - goto fail; + return 0; } - put_u32be(buf+0x00, 0x4F707573); /* "Opus" header magic */ - put_u32be(buf+0x04, 0x54616773); /* "Tags" header magic */ + put_u32be(buf+0x00, get_id32be("Opus")); + put_u32be(buf+0x04, get_id32be("Tags")); put_u32le(buf+0x08, vendor_string_length); memcpy (buf+0x0c, vendor_string, vendor_string_length); put_u32le(buf+0x0c + vendor_string_length+0x00, 1); /* user_comment_list_length */ @@ -580,8 +588,6 @@ static size_t make_opus_comment(uint8_t* buf, int buf_size) { memcpy (buf+0x0c + vendor_string_length+0x08, user_comment_0_string, user_comment_0_length); return comment_size; -fail: - return 0; } static size_t make_oggs_first(uint8_t* buf, int buf_size, opus_config* cfg) { diff --git a/src/meta/ea_eaac.c b/src/meta/ea_eaac.c index b2681aaa..8ea149d9 100644 --- a/src/meta/ea_eaac.c +++ b/src/meta/ea_eaac.c @@ -251,8 +251,8 @@ fail: static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE* sf_head, STREAMFILE* sf_data, eaac_header_t* eaac); static layered_layout_data* build_layered_eaaudiocore(STREAMFILE* sf, eaac_header_t *eaac, off_t start_offset); -static STREAMFILE *setup_eaac_streamfile(eaac_header_t *ea, STREAMFILE* sf_head, STREAMFILE* sf_data); -static size_t calculate_eaac_size(STREAMFILE* sf, eaac_header_t *ea, uint32_t num_samples, off_t start_offset, int is_ram); +static STREAMFILE* setup_eaac_streamfile(eaac_header_t* ea, STREAMFILE* sf_head, STREAMFILE* sf_data); +static size_t calculate_eaac_size(STREAMFILE* sf, eaac_header_t* ea, uint32_t num_samples, off_t start_offset, int is_ram); static VGMSTREAM* init_vgmstream_eaaudiocore_main(eaac_header_t* eaac, STREAMFILE* sf_head, STREAMFILE* sf_data, off_t header_offset, off_t _start_offset, meta_t meta_type, bool standalone) { @@ -469,18 +469,30 @@ static VGMSTREAM* init_vgmstream_eaaudiocore_main(eaac_header_t* eaac, STREAMFIL } else { switch(eaac->channels) { - //case 8: cfg.coupled_count = 3; break; /* 2ch+2ch+2ch+1ch+1ch, 5 streams */ - //case 6: cfg.coupled_count = 2; break; /* 2ch+2ch+1ch+1ch, 4 streams */ - case 4: cfg.coupled_count = 2; break; /* 2ch+2ch, 2 streams */ - case 2: cfg.coupled_count = 1; break; /* 2ch, 1 stream */ - case 1: cfg.coupled_count = 0; break; /* 1ch, 1 stream [Madden 22 (PC)] */ - default: goto fail; /* possibly: streams = Nch / 2, coupled = Nch % 2 */ + //case 8: cfg.coupled_count = 3; break; /* 2ch+2ch+2ch+1ch+1ch, 5 streams */ + case 6: cfg.coupled_count = 2; break; /* 2ch+2ch+1ch+1ch, 4 streams [FC24 (PC)] */ + case 4: cfg.coupled_count = 2; break; /* 2ch+2ch, 2 streams */ + case 2: cfg.coupled_count = 1; break; /* 2ch, 1 stream */ + case 1: cfg.coupled_count = 0; break; /* 1ch, 1 stream [Madden 22 (PC)] */ + default: + VGM_LOG("EAAC: unknown coupled count for %i\n", eaac->channels); + goto fail; /* possibly: streams = Nch / 2, coupled = Nch % 2 */ } } /* total number internal OPUS streams (should be >0) */ cfg.stream_count = cfg.channels - cfg.coupled_count; + /* observed mapping, basically swaps BL<>LFE (4ch and below are fine with defaults)*/ + if (eaac->channels == 6) { /* FL FR FC LFE BL BR */ + cfg.channel_mapping[0] = 0; + cfg.channel_mapping[1] = 1; + cfg.channel_mapping[2] = 2; + cfg.channel_mapping[3] = 5; + cfg.channel_mapping[4] = 4; + cfg.channel_mapping[5] = 3; + } + /* We *don't* remove EA blocks b/c in Multi Opus 1 block = 1 Opus packet * Regular EAOPUS uses layers to fake multichannel, this is normal multichannel Opus. * This can be used for stereo too, so probably replaces EAOPUS. */ From f25eacdb5289835829a188321bc5c820414ef8a3 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 13 Apr 2024 20:23:24 +0200 Subject: [PATCH 2/6] Fix PCM-only EAMP3 [EA Sports FC24 (PC)] --- src/coding/mpeg_custom_utils_eamp3.c | 85 +++++++++++++++------------- 1 file changed, 46 insertions(+), 39 deletions(-) diff --git a/src/coding/mpeg_custom_utils_eamp3.c b/src/coding/mpeg_custom_utils_eamp3.c index 99efefb5..99542414 100644 --- a/src/coding/mpeg_custom_utils_eamp3.c +++ b/src/coding/mpeg_custom_utils_eamp3.c @@ -15,42 +15,49 @@ typedef struct { uint32_t pcm_size; /* size of the PCM block */ } eamp3_frame_info; -static int eamp3_parse_frame(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, eamp3_frame_info * eaf); -static int eamp3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, eamp3_frame_info * eaf); -static int eamp3_skip_data(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, int at_start); +static bool eamp3_parse_frame(STREAMFILE* sf, uint32_t offset, mpeg_codec_data* data, eamp3_frame_info* eaf); +static int eamp3_write_pcm_block(VGMSTREAMCHANNEL* stream, mpeg_codec_data* data, int num_stream, eamp3_frame_info* eaf); +static int eamp3_skip_data(VGMSTREAMCHANNEL* stream, mpeg_codec_data* data, int num_stream, int at_start); /* init config and validate */ -int mpeg_custom_setup_init_eamp3(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type) { - mpeg_frame_info info; - uint16_t frame_header; - size_t header_size; +int mpeg_custom_setup_init_eamp3(STREAMFILE* sf, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type) { + eamp3_frame_info eaf = {0}; - - /* test unknown stuff */ - frame_header = (uint16_t)read_16bitLE(start_offset+0x00, streamFile); - if (frame_header & 0x2000) { - VGM_LOG("EAMP3: found unknown bit 13\n"); - goto fail; - } - if ((frame_header & 0x8000) && (uint32_t)read_32bitLE(start_offset+0x02, streamFile) > 0xFFFF) { - VGM_LOG("EAMP3: found big PCM block\n"); + /* needed below for rare PCM-only cases (EAMP3 can use multilayers) */ + data->channels_per_frame = data->config.channels >= 2 ? 2 : 1; + bool ok = eamp3_parse_frame(sf, start_offset, data, &eaf); + if (!ok) goto fail; + + /* checks */ + if (eaf.unknown_flag || (eaf.extended_flag && eaf.pcm_number > 0xFFFF) || (!eaf.pcm_number && !eaf.mpeg_size)) { + VGM_LOG("EAMP3: found data found\n"); goto fail; } /* get frame info at offset */ - header_size = (frame_header & 0x8000) ? 0x06 : 0x02; - if (!mpeg_get_frame_info(streamFile, start_offset+header_size, &info)) - goto fail; - switch(info.layer) { - case 1: *coding_type = coding_MPEG_layer1; break; - case 2: *coding_type = coding_MPEG_layer2; break; - case 3: *coding_type = coding_MPEG_layer3; break; - default: goto fail; + if (!eaf.mpeg_size) { + /* rare small pcm-only files, pretend mp3 max though won't be really needed [FC24 (PC)] */ + *coding_type = coding_MPEG_layer3; + data->channels_per_frame = data->config.channels; + data->samples_per_frame = 1152; + data->bitrate_per_frame = 320; + data->sample_rate_per_frame = 48000; + } + else { + mpeg_frame_info info; + if (!mpeg_get_frame_info(sf, start_offset + eaf.pre_size, &info)) + goto fail; + switch(info.layer) { + case 1: *coding_type = coding_MPEG_layer1; break; + case 2: *coding_type = coding_MPEG_layer2; break; + case 3: *coding_type = coding_MPEG_layer3; break; + default: goto fail; + } + data->channels_per_frame = info.channels; + data->samples_per_frame = info.frame_samples; + data->bitrate_per_frame = info.bit_rate; + data->sample_rate_per_frame = info.sample_rate; } - data->channels_per_frame = info.channels; - data->samples_per_frame = info.frame_samples; - data->bitrate_per_frame = info.bit_rate; - data->sample_rate_per_frame = info.sample_rate; return 1; @@ -61,14 +68,14 @@ fail: /* reads custom frame header + MPEG data + (optional) PCM block */ int mpeg_custom_parse_frame_eamp3(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream) { mpeg_custom_stream *ms = data->streams[num_stream]; - eamp3_frame_info eaf; + eamp3_frame_info eaf = {0}; int ok; if (!eamp3_skip_data(stream, data, num_stream, 1)) goto fail; - ok = eamp3_parse_frame(stream, data, &eaf); + ok = eamp3_parse_frame(stream->streamfile, stream->offset, data, &eaf); if (!ok) goto fail; ms->bytes_in_buffer = read_streamfile(ms->buffer, stream->offset + eaf.pre_size, eaf.mpeg_size, stream->streamfile); @@ -87,8 +94,8 @@ fail: } -static int eamp3_parse_frame(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, eamp3_frame_info * eaf) { - uint16_t current_header = (uint16_t)read_16bitLE(stream->offset+0x00, stream->streamfile); +static bool eamp3_parse_frame(STREAMFILE* sf, uint32_t offset, mpeg_codec_data* data, eamp3_frame_info* eaf) { + uint16_t current_header = read_u16le(offset+0x00, sf); eaf->extended_flag = (current_header & 0x8000); eaf->stereo_flag = (current_header & 0x4000); @@ -96,12 +103,12 @@ static int eamp3_parse_frame(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, ea eaf->frame_size = (current_header & 0x1FFF); /* full size including PCM block */ eaf->pcm_number = 0; if (eaf->extended_flag > 0) { - eaf->pcm_number = (uint32_t)read_32bitLE(stream->offset+0x02, stream->streamfile); - eaf->pcm_size = sizeof(sample) * eaf->pcm_number * data->channels_per_frame; + eaf->pcm_number = read_u32le(offset+0x02, sf); + eaf->pcm_size = sizeof(int16_t) * eaf->pcm_number * data->channels_per_frame; eaf->pre_size = 0x06; eaf->mpeg_size = eaf->frame_size - eaf->pre_size - eaf->pcm_size; if (eaf->frame_size < eaf->pre_size + eaf->pcm_size) { - VGM_LOG("EAMP3: bad pcm size at %x\n", (uint32_t)stream->offset); + VGM_LOG("EAMP3: bad pcm size at %x: %x < %x + %x (%x * %x)\n", offset, eaf->frame_size, eaf->pre_size, eaf->pcm_size, eaf->pcm_number, data->channels_per_frame); goto fail; } } @@ -111,9 +118,9 @@ static int eamp3_parse_frame(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, ea eaf->mpeg_size = eaf->frame_size - eaf->pre_size; } - return 1; + return true; fail: - return 0; + return false; } /* write PCM block directly to sample buffer and setup decode discard (see EALayer3). */ @@ -155,12 +162,12 @@ fail: /* Skip EA-frames from other streams for .sns/sps multichannel (see EALayer3). */ static int eamp3_skip_data(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, int at_start) { int ok, i; - eamp3_frame_info eaf; + eamp3_frame_info eaf = {0}; int skips = at_start ? num_stream : data->streams_size - 1 - num_stream; for (i = 0; i < skips; i++) { - ok = eamp3_parse_frame(stream, data, &eaf); + ok = eamp3_parse_frame(stream->streamfile, stream->offset, data, &eaf); if (!ok) goto fail; //;VGM_LOG("s%i: skipping %x, now at %lx\n", num_stream,eaf.frame_size,stream->offset); From dc5fe71c2149e1139cee6ea280b534672c45dfe7 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 13 Apr 2024 20:24:18 +0200 Subject: [PATCH 3/6] Add .bgm extension to SVS [Unlimited Saga (PS2)] --- src/meta/svs.c | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/meta/svs.c b/src/meta/svs.c index 92b366f0..c55d999f 100644 --- a/src/meta/svs.c +++ b/src/meta/svs.c @@ -3,40 +3,41 @@ /* SVS - SeqVagStream from Square games [Unlimited Saga (PS2) music] */ -VGMSTREAM * init_vgmstream_svs(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; +VGMSTREAM* init_vgmstream_svs(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; off_t start_offset; - int channel_count, loop_flag, pitch; + int channels, loop_flag, pitch; /* checks */ - /* .svs: header id (probably ok like The Bouncer's .vs) */ - if (!check_extensions(streamFile, "svs")) - goto fail; - if (read_32bitBE(0x00,streamFile) != 0x53565300) /* "SVS\0" */ - goto fail; + if (!is_id32be(0x00,sf, "SVS\0")) + return NULL; + /* .bgm: from debug strings (music%3.3u.bgm) + * .svs: header id (probably ok like The Bouncer's .vs, there are also refs to "vas") */ + if (!check_extensions(sf, "bgm,svs")) + return NULL; /* 0x04: flags (1=stereo?, 2=loop) */ - pitch = read_32bitLE(0x10,streamFile); /* usually 0x1000 = 48000 */ + pitch = read_s32le(0x10,sf); /* usually 0x1000 = 48000 */ /* 0x14: volume? */ /* 0x18: file id (may be null) */ /* 0x1c: null */ - loop_flag = (read_32bitLE(0x08,streamFile) > 0); /* loop start frame, min is 1 */ - channel_count = 2; + loop_flag = (read_s32le(0x08,sf) > 0); /* loop start frame, min is 1 */ + channels = 2; start_offset = 0x20; /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); + vgmstream = allocate_vgmstream(channels, loop_flag); if (!vgmstream) goto fail; vgmstream->meta_type = meta_SVS; vgmstream->sample_rate = round10((48000 * pitch) / 4096); /* music = ~44100, ambience = 48000 (rounding makes more sense but not sure) */ - vgmstream->num_samples = ps_bytes_to_samples(get_streamfile_size(streamFile) - start_offset, channel_count); + vgmstream->num_samples = ps_bytes_to_samples(get_streamfile_size(sf) - start_offset, channels); if (loop_flag) { - vgmstream->loop_start_sample = read_32bitLE(0x08,streamFile) * 28; /* frame count (0x10*ch) */ - vgmstream->loop_end_sample = read_32bitLE(0x0c,streamFile) * 28; /* frame count, (not exact num_samples when no loop) */ + vgmstream->loop_start_sample = read_s32le(0x08,sf) * 28; /* frame count (0x10*ch) */ + vgmstream->loop_end_sample = read_s32le(0x0c,sf) * 28; /* frame count, (not exact num_samples when no loop) */ /* start/end on the same frame rarely happens too (ex. file_id 63 SVS), perhaps loop should be +1 */ } @@ -44,7 +45,7 @@ VGMSTREAM * init_vgmstream_svs(STREAMFILE *streamFile) { vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x10; - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + if (!vgmstream_open_stream(vgmstream,sf,start_offset)) goto fail; return vgmstream; From 9927887e172e61e230b7f07a55c02a656b1d4062 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 13 Apr 2024 20:24:40 +0200 Subject: [PATCH 4/6] doc --- doc/USAGE.md | 13 ++++++++++--- src/meta/ktsr.c | 23 ++++++++++++++++------- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/doc/USAGE.md b/doc/USAGE.md index ba6ff772..2f76d47b 100644 --- a/doc/USAGE.md +++ b/doc/USAGE.md @@ -123,16 +123,23 @@ Note the above is also affected by vgmstream's options *Enable common exts* (vgm will accept and play common files like `.wav` or `.ogg`), and *Enable unknown exts* (will try to play files outside the known extension list, which is often possible through *TXTH*). -#### Default title +#### Default title and playlist columns By default *vgmstream* auto-generates a `title` tag depending on subsongs, stream name and other details. You can change this by setting *"override title"* in the options, that uses foobar's default (filename without extension) and tweating the display format in *Preferences > Display > Default User Interface* (may need to add some conditionals -to handle files with/out subsongs). *vgmstream* automatically exports these tags: +to handle files with/out subsongs). + +*vgmstream* automatically exports these tags: - `STREAM_INDEX`: current subsong, if file has subsongs, starts from 1 - `STREAM_COUNT`: total subsongs, if file has subsongs - `STREAM_NAME`: internal name, that also exists in some formats without subsongs -For example: `[%artist% - ]%title% [%stream_index%][/ %stream_name%]` +- `LOOP_START`: loop start, if any +- `LOOP_END`: loop end, if any + +Those exported tags can be used as columns as well (*.. > Playlist view > custom columns*). + +Example: `[%artist% - ]%title% [%stream_index%][/ %stream_name%]` You can also set an unique *Destination* pattern when converting to .wav (even without) setting *override title*). For example `[$num(%stream_index%,2)] %filename%[-%stream_name%]` diff --git a/src/meta/ktsr.c b/src/meta/ktsr.c index 94cd7405..237d9156 100644 --- a/src/meta/ktsr.c +++ b/src/meta/ktsr.c @@ -40,7 +40,7 @@ typedef struct { } ktsr_header; static VGMSTREAM* init_vgmstream_ktsr_internal(STREAMFILE* sf, bool is_srsa); -static int parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf); +static bool parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf); static layered_layout_data* build_layered_atrac9(ktsr_header* ktsr, STREAMFILE *sf, uint32_t config_data); static VGMSTREAM* init_vgmstream_ktsr_sub(STREAMFILE* sf_b, ktsr_header* ktsr, VGMSTREAM* (*init_vgmstream)(STREAMFILE* sf), const char* ext); @@ -113,6 +113,11 @@ static VGMSTREAM* init_vgmstream_ktsr_internal(STREAMFILE* sf, bool is_srsa) { if (!parse_ktsr(&ktsr, sf)) goto fail; + if (ktsr.total_subsongs == 0) { + vgm_logi("KTSR: file has no subsongs\n"); + return NULL; + } + /* open companion body */ if (ktsr.is_external) { if (ktsr.is_srsa) { @@ -371,7 +376,7 @@ fail: return 0; } -static int parse_ktsr_subfile(ktsr_header* ktsr, STREAMFILE* sf, uint32_t offset) { +static bool parse_ktsr_subfile(ktsr_header* ktsr, STREAMFILE* sf, uint32_t offset) { uint32_t suboffset, starts_offset, sizes_offset; int i; uint32_t type; @@ -484,10 +489,10 @@ static int parse_ktsr_subfile(ktsr_header* ktsr, STREAMFILE* sf, uint32_t offset if (!parse_codec(ktsr)) goto fail; - return 1; + return true; fail: VGM_LOG("ktsr: error parsing subheader\n"); - return 0; + return false; } /* ktsr engine reads+decrypts in the same func based on passed flag tho (reversed from exe) @@ -583,7 +588,7 @@ static void parse_longname(ktsr_header* ktsr, STREAMFILE* sf) { } } -static int parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf) { +static bool parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf) { uint32_t offset, end, header_offset, name_offset; uint32_t stream_count; @@ -672,6 +677,10 @@ static int parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf) { offset += size; } + if (ktsr->total_subsongs == 0) { + return true; + } + if (ktsr->target_subsong > ktsr->total_subsongs) goto fail; @@ -687,8 +696,8 @@ static int parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf) { ktsr->extra_offset += ktsr->base_offset; /* ? */ } - return 1; + return true; fail: vgm_logi("KTSR: unknown variation (report)\n"); - return 0; + return false; } From 55e6ec3244268e148e01d436fd74fc75171bbff6 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 13 Apr 2024 20:24:55 +0200 Subject: [PATCH 5/6] Add HCA key --- src/meta/hca_keys.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/meta/hca_keys.h b/src/meta/hca_keys.h index cd8fa93a..cf75cf19 100644 --- a/src/meta/hca_keys.h +++ b/src/meta/hca_keys.h @@ -1308,6 +1308,9 @@ static const hcakey_info hcakey_list[] = { // Puyo Puyo Puzzle Pop (iOS) {9999}, // 000000000000270F + // Penny Blood: Hellbound (PC) + {845873498572}, // 00000C4F1FD49CC + }; #endif/*_HCA_KEYS_H_*/ From 1185bf0689ac9fb9823a9e59b10cb9ce08c83b6d Mon Sep 17 00:00:00 2001 From: bnnm Date: Sun, 14 Apr 2024 00:51:42 +0200 Subject: [PATCH 6/6] cleanup: experiment --- src/meta/svs.c | 52 +++++++++++++++++++------------------------------- 1 file changed, 20 insertions(+), 32 deletions(-) diff --git a/src/meta/svs.c b/src/meta/svs.c index c55d999f..3d78f21b 100644 --- a/src/meta/svs.c +++ b/src/meta/svs.c @@ -1,13 +1,10 @@ #include "meta.h" #include "../coding/coding.h" +#include "../util/meta_utils.h" /* SVS - SeqVagStream from Square games [Unlimited Saga (PS2) music] */ VGMSTREAM* init_vgmstream_svs(STREAMFILE* sf) { - VGMSTREAM* vgmstream = NULL; - off_t start_offset; - int channels, loop_flag, pitch; - /* checks */ if (!is_id32be(0x00,sf, "SVS\0")) @@ -17,39 +14,30 @@ VGMSTREAM* init_vgmstream_svs(STREAMFILE* sf) { if (!check_extensions(sf, "bgm,svs")) return NULL; + meta_header_t h = { + .meta = meta_SVS, + }; /* 0x04: flags (1=stereo?, 2=loop) */ - pitch = read_s32le(0x10,sf); /* usually 0x1000 = 48000 */ + //h.loop_start = read_s32le(0x08,sf) * 28; /* frame count (0x10 * ch) */ + h.loop_end = read_s32le(0x0c,sf) * 28; /* frame count (not exact num_samples when no loop) */ + int pitch = read_s32le(0x10,sf); /* usually 0x1000 = 48000 */ /* 0x14: volume? */ /* 0x18: file id (may be null) */ /* 0x1c: null */ + h.stream_offset = 0x20; + h.stream_size = get_streamfile_size(sf) - h.stream_offset; - loop_flag = (read_s32le(0x08,sf) > 0); /* loop start frame, min is 1 */ - channels = 2; - start_offset = 0x20; + h.channels = 2; + h.sample_rate = round10((48000 * pitch) / 4096); /* music = ~44100, ambience = 48000 (rounding makes more sense but not sure) */ + h.num_samples = ps_bytes_to_samples(h.stream_size, h.channels); + /* loop start/end on the same frame rarely happens too (ex. file_id 63 SVS), perhaps loop should be +1 */ + h.loop_flag = (h.loop_start > 0); /* min is 1 */ + h.coding = coding_PSX; + h.layout = layout_interleave; + h.interleave = 0x10; - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channels, loop_flag); - if (!vgmstream) goto fail; - - vgmstream->meta_type = meta_SVS; - vgmstream->sample_rate = round10((48000 * pitch) / 4096); /* music = ~44100, ambience = 48000 (rounding makes more sense but not sure) */ - vgmstream->num_samples = ps_bytes_to_samples(get_streamfile_size(sf) - start_offset, channels); - if (loop_flag) { - vgmstream->loop_start_sample = read_s32le(0x08,sf) * 28; /* frame count (0x10*ch) */ - vgmstream->loop_end_sample = read_s32le(0x0c,sf) * 28; /* frame count, (not exact num_samples when no loop) */ - /* start/end on the same frame rarely happens too (ex. file_id 63 SVS), perhaps loop should be +1 */ - } - - vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x10; - - if (!vgmstream_open_stream(vgmstream,sf,start_offset)) - goto fail; - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; + h.sf = sf; + h.open_stream = true; + return alloc_metastream(&h); }