diff --git a/bootstrap b/bootstrap index c6a80137..a76e7ee7 100755 --- a/bootstrap +++ b/bootstrap @@ -4,8 +4,8 @@ # gets all files and updates .am scripts to avoid having to do manually (frowned upon by automake, whatevs) # maybe there is a better way or place for this -VGMSTREAM_SRCS=`(cd ./src/ && ls *.c */*.c) | tr '\n' ' '` -VGMSTREAM_HDRS=`(cd ./src/ && ls *.h */*.h) | tr '\n' ' '` +VGMSTREAM_SRCS=`(cd ./src/ && ls *.c */*.c */*/*.c) | tr '\n' ' '` +VGMSTREAM_HDRS=`(cd ./src/ && ls *.h */*.h */*/*.h) | tr '\n' ' '` AUDACIOUS_SRCS=`(cd ./audacious/ && ls *.cc) | tr '\n' ' '` AUDACIOUS_HDRS=`(cd ./audacious/ && ls *.h) | tr '\n' ' '` diff --git a/doc/FORMATS.md b/doc/FORMATS.md index 73a5a2e6..4cec1e23 100644 --- a/doc/FORMATS.md +++ b/doc/FORMATS.md @@ -1944,22 +1944,22 @@ Reminder of some extra formats and helper files vgmstream supports. They are des in detail in USAGE.md. - artificial headers: -- .txth (text header, adds support to lots of extra formats) -- .genh (generic header, deprecated) + - .txth (text header, adds support to lots of extra formats) + - .txtp (text play config, per song segment/layer manipulation) + - .txtm (text map config, for formats that open companion files manually) + - .genh (generic header, deprecated) - loop assists: -- .txtm (text map config, for formats that open companion files manually) -- .txtp (text play config, per song segment/layer manipulation) -- loop assists: -- .mus (playlist for .acm) -- .pos (loop info for .wav) -- .sli (loop info for .ogg) -- .sfl (loop info for .ogg) + - .mus (playlist for .acm) + - .pos (loop info for .wav) + - .sli (loop info for .ogg) + - .sfl (loop info for .ogg) - other: -- .adxkey (decryption key for .adx) -- .ahxkey (decryption key for .ahx) -- .hcakey (decryption key for .hca) -- .fsbkey (decryption key for .fsb) -- .bnsfkey (decryption key for .bnsf) + - .adxkey (decryption key for .adx) + - .ahxkey (decryption key for .ahx) + - .hcakey (decryption key for .hca) + - .fsbkey (decryption key for .fsb) + - .bnsfkey (decryption key for .bnsf) + - .awckey (decryption key for .awc) ## Supported codecs diff --git a/doc/USAGE.md b/doc/USAGE.md index 8b13f365..b57ddb2d 100644 --- a/doc/USAGE.md +++ b/doc/USAGE.md @@ -404,11 +404,11 @@ Regular formats without companion files should work fine in upper/lowercase. For Certain formats have encrypted data, and need a key to decrypt. vgmstream will try to find the correct key from a list, but it can be provided by a companion file: -- `.adx`: `.adxkey` (keystring, 8-byte keycode, or derived 6 byte start/mult/add key) +- `.adx`: `.adxkey` (keystring, or 8-byte keycode, or derived 6 byte start/mult/add key) - `.ahx`: `.ahxkey` (keystring, or derived 6-byte start/mult/add key) -- `.hca`: `.hcakey` (8-byte decryption key, a 64-bit number) - - `.awb`/`.acb` also may use `.hcakey`, and will combine with an internal AWB subkey - - May set a 8-byte key followed a 2-byte AWB subkey for newer HCA +- `.hca`: `.hcakey` (keystring, or 8-byte keycode, a 64-bit number) + - May set 8-byte key followed a 2-byte AWB subkey for newer HCA + - `.awb`/`.acb` also may use `.adxkey`/`.hcakey`, and will combine with an internal AWB subkey - `.fsb`: `.fsbkey` (decryption key in hex, usually between 8-32 bytes) - `.bnsf`: `.bnsfkey` (decryption key, a string up to 24 chars) - `.awc`: `.awckey` (decryption key, 0x10 bytes divided into 4 BE ints) diff --git a/src/meta/adx.c b/src/meta/adx.c index f972923e..014a81c3 100644 --- a/src/meta/adx.c +++ b/src/meta/adx.c @@ -17,7 +17,7 @@ #define ADX_KEY_MAX_TEST_FRAMES 32768 #define ADX_KEY_TEST_BUFFER_SIZE 0x8000 -static int find_adx_key(STREAMFILE* sf, uint8_t type, uint16_t* xor_start, uint16_t* xor_mult, uint16_t* xor_add, uint16_t subkey); +static bool find_adx_key(STREAMFILE* sf, uint8_t type, uint16_t* xor_start, uint16_t* xor_mult, uint16_t* xor_add, uint16_t subkey); VGMSTREAM* init_vgmstream_adx(STREAMFILE* sf) { return init_vgmstream_adx_subkey(sf, 0); @@ -87,7 +87,6 @@ VGMSTREAM* init_vgmstream_adx_subkey(STREAMFILE* sf, uint16_t subkey) { /* encryption */ if (version == 0x0408) { - if (!find_adx_key(sf, 8, &xor_start, &xor_mult, &xor_add, 0)) { vgm_logi("ADX: decryption keystring not found\n"); } @@ -265,7 +264,7 @@ fail: /* ADX key detection works by reading XORed ADPCM scales in frames, and un-XORing with keys in * a list. If resulting values are within the expected range for N scales we accept that key. */ -static int find_adx_key(STREAMFILE* sf, uint8_t type, uint16_t *xor_start, uint16_t *xor_mult, uint16_t *xor_add, uint16_t subkey) { +static bool find_adx_key(STREAMFILE* sf, uint8_t type, uint16_t *xor_start, uint16_t *xor_mult, uint16_t *xor_add, uint16_t subkey) { const int frame_size = 0x12; uint16_t *scales = NULL; uint16_t *prescales = NULL; @@ -280,7 +279,7 @@ static int find_adx_key(STREAMFILE* sf, uint8_t type, uint16_t *xor_start, uint1 size_t key_size; /* handle type8 keystrings, key9 keycodes and derived keys too */ - key_size = read_key_file(keybuf, sizeof(keybuf), sf); + key_size = read_key_file(keybuf, sizeof(keybuf) - 1, sf); if (key_size > 0) { int is_keystring = 0; @@ -288,28 +287,37 @@ static int find_adx_key(STREAMFILE* sf, uint8_t type, uint16_t *xor_start, uint1 if (type == 8) { is_keystring = cri_key8_valid_keystring(keybuf, key_size); } + else if (type == 9) { + is_keystring = cri_key9_valid_keystring(keybuf, key_size); + } if (key_size == 0x06 && !is_keystring) { *xor_start = get_u16be(keybuf + 0x00); *xor_mult = get_u16be(keybuf + 0x02); *xor_add = get_u16be(keybuf + 0x04); - return 1; + return true; } else if (type == 8 && is_keystring) { const char* keystring = (const char*)keybuf; cri_key8_derive(keystring, xor_start, xor_mult, xor_add); - return 1; + return true; + } + else if (type == 9 && is_keystring) { + const char* keystring = (const char*)keybuf; + uint64_t keycode = strtoull(keystring, NULL, 10); + cri_key9_derive(keycode, subkey, xor_start, xor_mult, xor_add); + return true; } else if (type == 9 && key_size == 0x08) { uint64_t keycode = get_u64be(keybuf); cri_key9_derive(keycode, subkey, xor_start, xor_mult, xor_add); - return 1; + return true; } else if (type == 9 && key_size == 0x08+0x02) { uint64_t file_keycode = get_u64be(keybuf+0x00); uint16_t file_subkey = get_u16be(keybuf+0x08); cri_key9_derive(file_keycode, file_subkey, xor_start, xor_mult, xor_add); - return 1; + return true; } } /* no key set or unknown format, try list */ @@ -525,5 +533,5 @@ static int find_adx_key(STREAMFILE* sf, uint8_t type, uint16_t *xor_start, uint1 done: free(scales); free(prescales); - return rc; + return rc != 0; } diff --git a/src/meta/aifc.c b/src/meta/aifc.c index 3dde4523..f07c9dae 100644 --- a/src/meta/aifc.c +++ b/src/meta/aifc.c @@ -86,6 +86,7 @@ VGMSTREAM* init_vgmstream_aifc(STREAMFILE* sf) { /* .aif: common (AIFF or AIFC) * .wav: SimCity 3000 (Mac) (both AIFF and AIFC) + * .aiff: rare and actually AIFC (maybe renamed AIFF too) [Cro-Mag Rally (Mac)] * (extensionless): Doom (3DO) * * .aifc: renamed AIFC? @@ -97,20 +98,19 @@ VGMSTREAM* init_vgmstream_aifc(STREAMFILE* sf) { * .xa: SimCity 3000 (Mac) * .caf: Topple (iOS) * - * .aiff: renamed AIFF? * .acm: Crusader - No Remorse (SAT) * .adp: Sonic Jam (SAT) * .ai: Dragon Force (SAT) * .pcm: Road Rash (SAT) */ - if (check_extensions(sf, "aif,laif,wav,lwav,")) { + if (check_extensions(sf, "aif,laif,wav,lwav,aiff,laiff,")) { is_aifc_ext = 1; is_aiff_ext = 1; } else if (check_extensions(sf, "aifc,laifc,afc,cbd2,bgm,fda,n64,xa,caf")) { is_aifc_ext = 1; } - else if (check_extensions(sf, "aiff,laiff,acm,adp,ai,pcm")) { + else if (check_extensions(sf, "acm,adp,ai,pcm")) { is_aiff_ext = 1; } else { @@ -138,6 +138,8 @@ VGMSTREAM* init_vgmstream_aifc(STREAMFILE* sf) { if (file_size != aifx_size + 0x08) { if (is_aiff && file_size == aifx_size + 0x08 + 0x08) aifx_size += 0x08; /* [Psychic Force Puzzle Taisen CD2 (PS1)] */ + else if (is_aifc && file_size == aifx_size + 0x08 + 0x4c) + aifx_size += 0x4c; /* Cro-Mag Rally (Mac), only one file */ } if (aifx_size + 0x08 != file_size) { @@ -145,7 +147,6 @@ VGMSTREAM* init_vgmstream_aifc(STREAMFILE* sf) { goto fail; } - /* read through chunks to verify format and find metadata */ { off_t offset = 0x0c; /* start with first chunk within FORM */ @@ -164,7 +165,7 @@ VGMSTREAM* init_vgmstream_aifc(STREAMFILE* sf) { goto fail; switch(chunk_type) { - case 0x46564552: /* "FVER" (version info, required) */ + case 0x46564552: /* "FVER" (version info, officially required but some odd game ommits it [Cro-Mag Rally (Mac)]) */ if (fver_found) goto fail; if (is_aiff) goto fail; /* plain AIFF shouldn't have */ fver_found = 1; @@ -315,14 +316,14 @@ VGMSTREAM* init_vgmstream_aifc(STREAMFILE* sf) { } if (is_aifc) { - if (!fver_found || !comm_found || !data_found) + if (/*!fver_found ||*/ !comm_found || !data_found) goto fail; - } else if (is_aiff) { + } + else if (is_aiff) { if (!comm_found || !data_found) goto fail; } - /* read loop points */ if (inst_offset && mark_offset) { int start_marker; diff --git a/src/meta/hca.c b/src/meta/hca.c index 294d71f9..3ee83874 100644 --- a/src/meta/hca.c +++ b/src/meta/hca.c @@ -4,6 +4,7 @@ #include "../coding/hca_decoder_clhca.h" #include "../util/channel_mappings.h" #include "../util/companion_files.h" +#include "../util/cri_keys.h" #ifdef VGM_DEBUG_OUTPUT //#define HCA_BRUTEFORCE @@ -45,14 +46,21 @@ VGMSTREAM* init_vgmstream_hca_subkey(STREAMFILE* sf, uint16_t subkey) { /* find decryption key in external file or preloaded list */ if (hca_info->encryptionEnabled) { uint64_t keycode = 0; - uint8_t keybuf[0x08+0x02]; - size_t keysize; + uint8_t keybuf[20+1] = {0}; /* max keystring 20, +1 extra null */ + size_t key_size; - keysize = read_key_file(keybuf, sizeof(keybuf), sf); - if (keysize == 0x08) { /* standard */ + key_size = read_key_file(keybuf, sizeof(keybuf) - 1, sf); + + bool is_keystring = cri_key9_valid_keystring(keybuf, key_size); + + if (is_keystring) { /* number */ + const char* keystring = (const char*)keybuf; + keycode = strtoull(keystring, NULL, 10); + } + else if (key_size == 0x08) { /* hex */ keycode = get_u64be(keybuf+0x00); } - else if (keysize == 0x08+0x02) { /* seed key + AWB subkey */ + else if (key_size == 0x08+0x02) { /* seed key + AWB subkey */ keycode = get_u64be(keybuf+0x00); subkey = get_u16be(keybuf+0x08); } diff --git a/src/meta/txtp.c b/src/meta/txtp.c index 9db9a49a..64e4c4f8 100644 --- a/src/meta/txtp.c +++ b/src/meta/txtp.c @@ -408,6 +408,23 @@ static int make_group_segment(txtp_header* txtp, txtp_group* grp, int position, } + /* fix loop keep (do it before init'ing as loops/metadata may be disabled for segments) */ + int32_t loop_start_sample = 0, loop_end_sample = 0; + if (loop_flag && txtp->is_loop_keep) { + int32_t current_samples = 0; + for (i = 0; i < count; i++) { + if (loop_start == i+1 /*&& txtp->vgmstream[i + position]->loop_start_sample*/) { + loop_start_sample = current_samples + txtp->vgmstream[i + position]->loop_start_sample; + } + + current_samples += txtp->vgmstream[i + position]->num_samples; + + if (loop_end == i+1 && txtp->vgmstream[i + position]->loop_end_sample) { + loop_end_sample = current_samples - txtp->vgmstream[i + position]->num_samples + txtp->vgmstream[i + position]->loop_end_sample; + } + } + } + /* init layout */ data_s = init_layout_segmented(count); if (!data_s) goto fail; @@ -436,18 +453,8 @@ static int make_group_segment(txtp_header* txtp, txtp_group* grp, int position, /* fix loop keep */ if (loop_flag && txtp->is_loop_keep) { - int32_t current_samples = 0; - for (i = 0; i < count; i++) { - if (loop_start == i+1 /*&& data_s->segments[i]->loop_start_sample*/) { - vgmstream->loop_start_sample = current_samples + data_s->segments[i]->loop_start_sample; - } - - current_samples += data_s->segments[i]->num_samples; - - if (loop_end == i+1 && data_s->segments[i]->loop_end_sample) { - vgmstream->loop_end_sample = current_samples - data_s->segments[i]->num_samples + data_s->segments[i]->loop_end_sample; - } - } + vgmstream->loop_start_sample = loop_start_sample; + vgmstream->loop_end_sample = loop_end_sample; } diff --git a/src/util/cri_keys.c b/src/util/cri_keys.c index dbce900f..a5397be6 100644 --- a/src/util/cri_keys.c +++ b/src/util/cri_keys.c @@ -126,14 +126,28 @@ end: *p_key3 = key3; } -int cri_key8_valid_keystring(uint8_t* buf, int buf_size) { - int i; +bool cri_key8_valid_keystring(uint8_t* buf, int buf_size) { + if (buf_size <= 0) + return false; - for (i = 0; i < buf_size; i++) { + for (int i = 0; i < buf_size; i++) { if (buf[i] < 0x20 || buf[i] > 0x8f) { /* allow 0x8x for (uncommon) cases of SHIFT-JIS */ - return 0; + return false; } } - return 1; + return true; +} + +bool cri_key9_valid_keystring(uint8_t* buf, int buf_size) { + if (buf_size <= 0 || buf_size > 20) /* max u64 */ + return false; + + for (int i = 0; i < buf_size; i++) { + if (buf[i] < 0x30 || buf[i] > 0x39) { /* numbers only */ + return false; + } + } + + return true; } diff --git a/src/util/cri_keys.h b/src/util/cri_keys.h index 7748b9d1..21f8208f 100644 --- a/src/util/cri_keys.h +++ b/src/util/cri_keys.h @@ -2,12 +2,14 @@ #define _CRI_KEYS_H_ #include +#include /* common CRI key helpers */ void cri_key8_derive(const char* key8, uint16_t* p_key1, uint16_t* p_key2, uint16_t* p_key3); void cri_key9_derive(uint64_t key9, uint16_t subkey, uint16_t* p_key1, uint16_t* p_key2, uint16_t* p_key3); -int cri_key8_valid_keystring(uint8_t* buf, int buf_size); +bool cri_key8_valid_keystring(uint8_t* buf, int buf_size); +bool cri_key9_valid_keystring(uint8_t* buf, int buf_size); #endif /* _CRI_KEYS_H_ */