diff --git a/cli/txtp_maker.py b/cli/txtp_maker.py index 96fa6b61..a1431454 100644 --- a/cli/txtp_maker.py +++ b/cli/txtp_maker.py @@ -46,6 +46,7 @@ class Cli(object): p.add_argument('-m', dest='mini_txtp', help="Create mini-txtp", action='store_true') p.add_argument('-o', dest='overwrite', help="Overwrite existing .txtp\n(beware when using with internal names alone)", action='store_true') p.add_argument('-O', dest='overwrite_rename', help="Rename rather than overwriting", action='store_true') + p.add_argument('-Os', dest='overwrite_suffix', help="Rename with a suffix") p.add_argument('-l', dest='layers', help="Create .txtp per subsong layers, every N channels", type=int) p.add_argument('-fd', dest='test_dupes', help="Skip .txtp that point to duplicate streams (slower)", action='store_true') p.add_argument('-fcm', dest='min_channels', help="Filter by min channels", type=int) @@ -254,13 +255,19 @@ class TxtpMaker(object): outname += '.txtp' cfg = self.cfg - if cfg.overwrite_rename and os.path.exists(outname): - if outname in self.rename_map: - rename_count = self.rename_map[outname] - else: - rename_count = 0 - self.rename_map[outname] = rename_count + 1 - outname = outname.replace(".txtp", "_%08i.txtp" % (rename_count)) + if (cfg.overwrite_rename or cfg.overwrite_suffix) and os.path.exists(outname): + must_rename = True + if cfg.overwrite_suffix: + outname = outname.replace(".txtp", "%s.txtp" % (cfg.overwrite_suffix)) + must_rename = os.path.exists(outname) + + if must_rename: + if outname in self.rename_map: + rename_count = self.rename_map[outname] + else: + rename_count = 0 + self.rename_map[outname] = rename_count + 1 + outname = outname.replace(".txtp", "_%08i.txtp" % (rename_count)) if not cfg.overwrite and os.path.exists(outname): raise ValueError('TXTP exists in path: ' + outname) diff --git a/src/meta/fsb_keys.h b/src/meta/fsb_keys.h index 2d626c1d..2084f70b 100644 --- a/src/meta/fsb_keys.h +++ b/src/meta/fsb_keys.h @@ -26,8 +26,8 @@ static const uint8_t key_rev[] = { 0x31,0x5E,0x37,0x25,0x38,0x32,0x23,0x26,0x35, /* Dark Souls 3 (PC) */ //"FDPrVuT4fAFvdHJYAgyMzRF4EcBAnKg" static const uint8_t key_ds3[] = { 0x46,0x44,0x50,0x72,0x56,0x75,0x54,0x34,0x66,0x41,0x46,0x76,0x64,0x48,0x4A,0x59,0x41,0x67,0x79,0x4D,0x7A,0x52,0x46,0x34,0x45,0x63,0x42,0x41,0x6E,0x4B,0x67 }; -/* Mortal Kombat X */ -static const uint8_t key_mkx[] = { 0x99,0x61,0x64,0xB5,0xFC,0x0F,0x40,0x29,0x83,0xF6,0x1F,0x22,0x0B,0xB5,0x1D,0xC6 }; +/* Mortal Kombat X/XL (PC) */ //"996164B5FC0F402983F61F220BB51DC6" +static const uint8_t key_mkx[] = { 0x39,0x39,0x36,0x31,0x36,0x34,0x42,0x35,0x46,0x43,0x30,0x46,0x34,0x30,0x32,0x39,0x38,0x33,0x46,0x36,0x31,0x46,0x32,0x32,0x30,0x42,0x42,0x35,0x31,0x44,0x43,0x36 }; /* Xian Xia Chuan (PC) */ //"gat@tcqs2010" static const uint8_t key_xxc[] = { 0x67,0x61,0x74,0x40,0x74,0x63,0x71,0x73,0x32,0x30,0x31,0x30 }; @@ -107,8 +107,7 @@ static const fsbkey_info fsbkey_list[] = { { 1,0, sizeof(key_rev),key_rev },//FSB5 { 1,0, sizeof(key_ds3),key_ds3 },//untested { 1,1, sizeof(key_ds3),key_ds3 }, - { 1,0, sizeof(key_mkx),key_mkx },//untested - { 1,1, sizeof(key_mkx),key_mkx },//untested + { 1,0, sizeof(key_mkx),key_mkx },//FSB5 { 0,0, sizeof(key_xxc),key_xxc },//untested { 0,1, sizeof(key_xxc),key_xxc },//untested { 1,0, sizeof(key_xxc),key_xxc },//untested diff --git a/src/meta/opus.c b/src/meta/opus.c index 0f5f3a42..7edf7459 100644 --- a/src/meta/opus.c +++ b/src/meta/opus.c @@ -9,31 +9,54 @@ static VGMSTREAM* init_vgmstream_opus(STREAMFILE* sf, meta_t meta_type, off_t of VGMSTREAM* vgmstream = NULL; off_t start_offset; int loop_flag = 0, channel_count; - off_t data_offset, multichannel_offset = 0; + off_t data_offset, samples_offset, multichannel_offset = 0; size_t data_size, skip = 0; - + /* header chunk */ if (read_u32le(offset + 0x00,sf) != 0x80000001) goto fail; + /* 0x04: chunk size */ + /* 0x08: null */ channel_count = read_u8(offset + 0x09, sf); /* 0x0a: packet size if CBR, 0 if VBR */ - data_offset = offset + read_u32le(offset + 0x10, sf); + data_offset = read_u32le(offset + 0x10, sf); + /* 0x14: null/reserved? */ + samples_offset = read_u32le(offset + 0x18, sf); skip = read_u16le(offset + 0x1c, sf); /* 0x1e: ? (seen in Lego Movie 2 (Switch)) */ - /* recent >2ch info [Clannad (Switch)] */ + /* samples chunk, rare [Famicom Detective Club (Switch)] */ + if (samples_offset && read_u32le(offset + samples_offset, sf) == 0x80000003) { + /* maybe should give priority to external info? */ + samples_offset += offset; + /* 0x08: null*/ + loop_flag = read_u8 (samples_offset + 0x09, sf); + num_samples = read_s32le(samples_offset + 0x0c, sf); /* slightly smaller than manual count */ + loop_start = read_s32le(samples_offset + 0x10, sf); + loop_end = read_s32le(samples_offset + 0x14, sf); + /* rest (~0x38) reserved/alignment? */ + /* values seem to take encoder delay into account */ + } + else { + loop_flag = (loop_end > 0); /* -1 when not set */ + } + + + /* multichannel chunk, rare [Clannad (Switch)] */ if (read_u32le(offset + 0x20, sf) == 0x80000005) { multichannel_offset = offset + 0x20; } + + /* data chunk */ + data_offset += offset; if (read_u32le(data_offset, sf) != 0x80000004) goto fail; data_size = read_u32le(data_offset + 0x04, sf); start_offset = data_offset + 0x08; - loop_flag = (loop_end > 0); /* -1 when not set */ /* build the VGMSTREAM */ diff --git a/src/meta/xvag.c b/src/meta/xvag.c index a0e6f370..4236c978 100644 --- a/src/meta/xvag.c +++ b/src/meta/xvag.c @@ -20,6 +20,8 @@ typedef struct { int subsongs; int layers; + int target_subsong; + size_t data_size; off_t stream_offset; } xvag_header; @@ -43,7 +45,7 @@ VGMSTREAM* init_vgmstream_xvag(STREAMFILE* sf) { * (extensionless): The Last Of Us (PS3) speech files */ if (!check_extensions(sf,"xvag,")) goto fail; - if (read_32bitBE(0x00,sf) != 0x58564147) /* "XVAG" */ + if (!is_id32be(0x00,sf, "XVAG")) goto fail; /* endian flag (XVAGs of the same game can use BE or LE, usually when reusing from other platforms) */ @@ -89,6 +91,7 @@ VGMSTREAM* init_vgmstream_xvag(STREAMFILE* sf) { total_subsongs = xvag.subsongs; if (target_subsong == 0) target_subsong = 1; if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + xvag.target_subsong = target_subsong; /* other chunks: */ @@ -117,7 +120,7 @@ VGMSTREAM* init_vgmstream_xvag(STREAMFILE* sf) { /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(xvag.channels,xvag.loop_flag); + vgmstream = allocate_vgmstream(xvag.channels, xvag.loop_flag); if (!vgmstream) goto fail; vgmstream->meta_type = meta_XVAG; @@ -203,7 +206,6 @@ VGMSTREAM* init_vgmstream_xvag(STREAMFILE* sf) { #ifdef VGM_USE_ATRAC9 case 0x09: { /* ATRAC9: Sly Cooper and the Thievius Raccoonus (Vita), The Last of Us Remastered (PS4) */ - if (xvag.subsongs > 1 && xvag.layers > 1) goto fail; /* "a9in": ATRAC9 info */ /* 0x00: frame size, 0x04: samples per frame, 0x0c: fact num_samples (no change), 0x10: encoder delay1 */ @@ -257,8 +259,13 @@ static int init_xvag_atrac9(STREAMFILE* sf, VGMSTREAM* vgmstream, xvag_header * atrac9_config cfg = {0}; cfg.channels = vgmstream->channels; + /* 0x00: frame size */ + /* 0x04: frame samples */ cfg.config_data = read_32bitBE(chunk_offset+0x08,sf); + /* 0x08: data size (layer only) */ + /* 0x10: decoder delay? */ cfg.encoder_delay = read_32bit(chunk_offset+0x14,sf); + /* sometimes ATRAC9 data starts with a fake RIFF, that has total channels rather than layer channels */ vgmstream->codec_data = init_atrac9(&cfg); if (!vgmstream->codec_data) goto fail; @@ -276,6 +283,7 @@ static layered_layout_data* build_layered_xvag(STREAMFILE* sf, xvag_header * xva STREAMFILE* temp_sf = NULL; int32_t (*read_32bit)(off_t,STREAMFILE*) = xvag->big_endian ? read_32bitBE : read_32bitLE; int i, layers = xvag->layers; + int chunk, chunks = layers * xvag->subsongs; /* init layout */ @@ -300,7 +308,14 @@ static layered_layout_data* build_layered_xvag(STREAMFILE* sf, xvag_header * xva if (!init_xvag_atrac9(sf, data->layers[i], xvag, chunk_offset)) goto fail; - temp_sf = setup_xvag_streamfile(sf, start_offset, frame_size*xvag->factor,frame_size, i, layers); + + /* interleaves N layers for custom multichannel, may rarely use subsongs [Days Gone (PS4) multilayer test] + * ex. 2 layers, 1 subsong : [L1][L2][L1][L2] + * ex. 2 layers, 2 subsongs: [L1S1][L2S1][L1S2][L2S2] (assumed, could be [L1S1][L1S2][L2S1][L2S2]) */ + chunk = i + xvag->subsongs * (xvag->target_subsong - 1); /* [L1S1][L2S1][L1S2][L2S2] */ + //chunk = i * xvag->subsongs + (xvag->target_subsong - 1); /* [L1S1][L1S2][L2S1][L2S2] */ + + temp_sf = setup_xvag_streamfile(sf, start_offset, frame_size*xvag->factor, frame_size, chunk, chunks); if (!temp_sf) goto fail; break; }