private void CLI(StartupEventArgs e) { List<string> args = e.Args.ToList<string>(); List<string>.Enumerator en = args.GetEnumerator(); debug = false; bool list = false; quiet = false; string gameDir = ""; string backupDir = ""; string outDir = ""; bool decode = true; bool all = false; bool all_hardcore = false; bool nolist = false; bool noassets = false; List<string> inputs = new List<string>(); string location = System.Reflection.Assembly.GetEntryAssembly().Location; string infoFile = location.Remove(location.LastIndexOf("\\")) + "\\Resources\\info.txt"; List<string> filters = new List<string>(); bool PRINT_AND_EXIT = false; int MIN_FZ = 0; int MAX_FZ = int.MaxValue; bool pcm = false; string lookup = ""; List<string> filter_id = new List<string>(); while (en.MoveNext()) { string arg = en.Current; string[] argp = arg.Split('='); switch (argp[0]) { case "-r": case "--nolist": nolist = true; break; case "-R": case "--noassets": noassets = true; break; case "--max": if (argp.Length < 2) { Output("Format: --max=<int>"); return; } MAX_FZ = int.Parse(argp[1]); break; case "-X": if (en.MoveNext()) { MAX_FZ = int.Parse(en.Current); } break; case "--lookup": if (argp.Length < 2) { Output("Format: --lookup=<file>"); } Output("!!WARNING!!"); Output("--lookup is an experimental module"); Output("results so far: everything is messed up"); Output("!!WARNING!!"); lookup = argp[1]; break; case "--min": if (argp.Length < 2) { Output("Format: --min=<int>"); return; } MIN_FZ = int.Parse(argp[1]); break; case "-x": if (en.MoveNext()) { MIN_FZ = int.Parse(en.Current); } break; case "--id": if (argp.Length < 2) { Output("Format: --id=<id>"); return; } if (argp[1].Length != 16) { Output("--id should be 16 characters"); return; } filter_id.Add(argp[1].ToUpper()); break; case "-I": if (en.MoveNext()) { if (en.Current.Length != 16) { Output("-I should be 16 characters"); return; } filter_id.Add(en.Current.ToUpper()); } break; case "--list": case "-l": list = true; break; case "--verbose": case "--debug": case "-D": debug = true; break; case "--quiet": case "-q": quiet = true; break; case "--decode": case "-P": decode = false; break; case "--game-dir": if (argp.Length < 2) { Output("Format: --game-dir=<path>"); break; } gameDir = argp[1]; break; case "-G": if (en.MoveNext()) { gameDir = en.Current; } break; case "--backup-dir": if (argp.Length < 2) { Output("Format: --backup-dir=<path>"); break; } backupDir = argp[1]; break; case "-B": if (en.MoveNext()) { backupDir = en.Current; } break; case "--out-dir": if (argp.Length < 2) { Output("Format: --out-dir=<path>"); break; } outDir = argp[1]; break; case "-O": if (en.MoveNext()) { outDir = en.Current; } break; case "--all": case "-a": all = true; break; case "-A": all = true; all_hardcore = true; break; case "--ext": if (argp.Length < 2) { Output("Format: --ext=<path>"); break; } infoFile = argp[1]; break; case "-e": if (en.MoveNext()) { infoFile = en.Current; } break; case "--pcm": pcm = true; break; case "-f": if (en.MoveNext()) { string[] argf = en.Current.Split(','); for (int i = 0; i < argf.Length; ++i) { filters.Add(argf[i].Trim()); } } break; case "-i": if (en.MoveNext()) { inputs.Add(en.Current); } break; case "-F": PRINT_AND_EXIT = true; break; default: if (arg.ToCharArray()[0] != '-') { inputs.Add(arg); } break; } } Output("Bundai CLI"); Bandaid band = new Bandaid(Debug, Output); Debug("Checking if extension file exists"); if (!File.Exists(infoFile)) { Output("Error! extfile does not exist!"); return; } Debug("Loading extension file", infoFile); Dictionary<string, string[]> exts = Bandaid.Exts(infoFile); if (PRINT_AND_EXIT) { List<string[]> extsL = exts.Values.ToList<string[]>(); foreach (string[] ext in extsL) { Console.Write(ext[0]); if (ext[0] == "stream") Console.Write("; converts into IMA ADPCM (or PCM) .WAV file"); if (ext[0] == "strings") Console.Write("; converts into .CSV file"); Console.Write("\r\n"); } return; } Debug("Checking if game-dir is specified and exists; game-dir =", gameDir); if (gameDir.Length == 0 || !Directory.Exists(gameDir)) { Output("Error! Game-dir does not exist or is not specified"); return; } Debug("Checking for /assets"); if (!Directory.Exists(gameDir + "/assets")) { Output("Error! Could not find assets subdirectory"); return; } Debug("Checking for all.blb"); if (!File.Exists(gameDir + "/assets/all.blb")) { Output("Error! Could not find all.blb"); return; } Debug("Checking if inputs or all; all =", all, "inputs = [" + string.Join(",", inputs.ToArray<string>()) + "]"); if (!all && inputs.Count == 0) { Output("Error! All or inputs are not specified"); return; } Debug("Backup directory specified; backup-dir =", backupDir); if (backupDir.Length > 0 && !list) { Debug("Checking if backup-dir", backupDir, "exists"); if (File.Exists(backupDir)) { Output("Error!", backupDir, "exists but is a file!"); } else if (!Directory.Exists(backupDir)) { Output(backupDir, "does not exist... Making."); Directory.CreateDirectory(backupDir); } else Output("Backup directory", backupDir, "is valid"); } Debug("Checking if list-only mode or out-dir; list =", list, "out-dir =", outDir); if (!list && outDir.Length > 0) { Debug("Checking if out-dir", outDir, "exists"); if (File.Exists(outDir)) { Output("Error!", outDir, "exists but is a file!"); } else if (!Directory.Exists(outDir)) { Output(outDir, "does not exist... Making."); Directory.CreateDirectory(outDir); } else Output("Output directory", outDir, "is valid"); } else if (!list) { Output("Error! out-dir or list are not specified"); return; } else Debug("List-only mode"); Debug("Loading asset filenames"); List<string> assets = Directory.GetFiles(gameDir + "/assets/").ToList<string>(); if (all || all_hardcore) { Debug("Populating inputs with", all_hardcore ? "all bundles" : "all_*.bundle"); inputs.Clear(); List<string>.Enumerator aen = assets.GetEnumerator(); while (aen.MoveNext()) { string asset_file = aen.Current.Substring(aen.Current.LastIndexOf('/')+1); if (asset_file.EndsWith("_h.bundle")) { if (!all_hardcore && !asset_file.StartsWith("all_")) continue; asset_file = asset_file.Split('.')[0].Split(new string[] { "_h" }, StringSplitOptions.None)[0]; inputs.Add(asset_file); Debug("Found", asset_file); } } } else { Debug("Fixing inputs"); List<string> inputsC = new List<string>(); List<string>.Enumerator ienc = inputs.GetEnumerator(); while (ienc.MoveNext()) { string ienC = ienc.Current.Split('.')[0].Split(new string[] { "_h" }, StringSplitOptions.None)[0]; List<string>.Enumerator aen = assets.GetEnumerator(); while (aen.MoveNext()) { if (aen.Current.Contains(ienC + "_h.bundle")) { inputsC.Add(ienC); Debug("Found", ienC); } } } inputs.Clear(); inputs.AddRange(inputsC); } Debug("New inputs", "[" + string.Join(",", inputs) + "]"); string ROOT_DIR = gameDir + "/assets/"; if (backupDir.Length > 0) { Output("Backing files up"); Output("Backup: all.blb"); if (!File.Exists(backupDir + "/all.blb") && File.Exists(ROOT_DIR + "/all.blb")) File.Copy(ROOT_DIR + "/all.blb", backupDir + "/all.blb"); else Debug("Skipping all.blb"); List<string>.Enumerator ben = inputs.GetEnumerator(); while (ben.MoveNext()) { string src = ROOT_DIR + ben.Current; string dst = backupDir + "/" + ben.Current; Output("Backup:", ben.Current); if (!File.Exists(dst + ".bundle") && File.Exists(src + ".bundle")) File.Copy(src + ".bundle", dst + ".bundle"); else Debug("Skipping", ben.Current + ".bundle"); if (!File.Exists(dst + "_h.bundle") && File.Exists(src + "_h.bundle")) File.Copy(src + "_h.bundle", dst + "_h.bundle"); else Debug("Skipping", ben.Current + "_h.bundle"); } ROOT_DIR = backupDir + "/"; } Output("Starting..."); IndexCollection idxCol = new IndexCollection(); if (lookup != null && lookup.Length > 0) { Binboy lbb = new Binboy(lookup); lbb.Open(); idxCol.ParseLookup(lbb); } Binboy bb = new Binboy(ROOT_DIR + "all.blb"); if (!bb.Open()) { Output("Can't open all.blb"); return; } for (int i = 0; i < 8; i++) bb.ReadBytes(4); int files = bb.ReadInt32(); int target = bb.ReadInt32(); bb.Position = (long)target; Debug("Reading file table"); for (int i = 0; i < files; ++i) { idxCol.Add(bb.ReadBytes(8), bb.ReadBytes(8), bb.ReadInt32(), bb.ReadInt32(), bb.ReadInt32(), bb.ReadInt32()); } bb.Dispose(); List<string>.Enumerator ieun = inputs.GetEnumerator(); bool hadFilters = filter_id.Count > 0; while (ieun.MoveNext()) { if (list) outDir = System.IO.Path.GetTempPath(); string dst = outDir + "/unknown/" + ieun.Current + "/"; BundleDataCollection bdc = new BundleDataCollection(); Binboy bbd = new Binboy(ROOT_DIR + ieun.Current + "_h.bundle"); Debug("Opening", ROOT_DIR + ieun.Current + "_h.bundle"); if (!bbd.Open()) continue; Debug("Reading headers"); bbd.Position = 0; for (int i = 0; i < 5; ++i) bdc.AddHeader(bbd.ReadUInt32()); bool flag = false; if ((uint)bdc.Header[4] == 24u) { flag = true; bdc.AddHeader(bbd.ReadUInt32()); bdc.AddHeader(bbd.ReadUInt32()); } for (int i = 0; i < bdc.GetTotal(); i++) { Debug(i+1, "/", bdc.GetTotal()); if (flag) { bdc.Add(new BundleData(bbd.ReadInt32(), bbd.ReadInt32(), bbd.ReadInt32())); } else { bdc.Add(new BundleData(bbd.ReadInt32(), bbd.ReadInt32(), 0)); } if (i > 0 && !flag) { BundleData bhd1 = (BundleData)bdc[i]; BundleData bhd2 = (BundleData)bdc[i - 1]; bhd2.Length = bhd1.Address - bhd2.Address; } } if (bdc.Count - 1 >= 0 && !flag) { BundleData bhd3 = (BundleData)bdc[bdc.Count - 1]; bhd3.Length = -1; } string txt = ""; bbd.Dispose(); bbd = new Binboy(ROOT_DIR + ieun.Current + ".bundle"); Output("Opening", ieun.Current); bbd.Open(); if (bdc.Header.Count == 5) { bbd.ReadBytes(((BundleData)bdc[0]).Address); } System.Collections.IEnumerator bbdc = bdc.GetEnumerator(); bool hasDoneShit = false; if (hadFilters && filter_id.Count == 0) break; while(bbdc.MoveNext()) { if (hadFilters && filter_id.Count == 0) break; BundleData bddd = (BundleData)bbdc.Current; string xex = "bin"; bool lookedup = false; string dstFrom = dst; try { txt = idxCol.FindName(bddd.Index); string[] tin = txt.Split('.'); Debug("tin", string.Join(",", tin)); if (hadFilters) { if (!filter_id.Contains(tin[0].ToUpper())) continue; filter_id.Remove(tin[0].ToUpper()); } xex = tin[2]; if (exts.ContainsKey(xex.ToUpper())) { string[] xx = exts[xex]; Debug("xx", string.Join(",", xx)); if (filters.Count > 0 && !filters.Contains(xx[0])) continue; xex = xx.Length > 1 ? xx[1] : xx[0]; txt = txt.Substring(0, txt.LastIndexOf('.') + 1); } string lookupstr = idxCol.FindLookup(bddd.Index); if (lookupstr != null && lookupstr.Trim().Length > 1) { txt = "."; lookedup = true; dstFrom = outDir + "/" + lookupstr.Trim(); } else { dstFrom = dst; } } catch (Exception epppppp) { if (filters.Count > 0) continue; txt = "unknown_" + string.Format("{0:X8}", bddd.Index) + "."; Debug(epppppp.Message, epppppp.StackTrace); } bbd.Position = (long)bddd.Address; byte[] a; if (bddd.Length == -1) a = bbd.ReadAllBytes(); else a = bbd.ReadBytes(bddd.Length); if (a != null && a.LongLength > MIN_FZ && a.LongLength < MAX_FZ) { string dstFrm = dstFrom; if (lookedup) { dstFrm = dstFrom.Substring(0, dstFrom.LastIndexOf('/') == 0 ? dstFrom.Length : dstFrom.LastIndexOf('/')); } Output(ieun.Current, dstFrom + txt + xex, Binboy.Format(a.LongLength)); if (!list) { byte[] a_cache = a; if (decode) { switch (xex) { case "strings": a = Parsers.Strings(a, out xex); break; case "wav": case "stream": a = Parsers.IMA_ADPCM(a, out xex, pcm); break; } } if (!Directory.Exists(dstFrm)) Directory.CreateDirectory(dstFrm); Bandaid.write(dstFrom + txt + xex, a); if (!noassets) { using (StreamWriter sw = new StreamWriter(new FileStream(dst + "/assets.idx", FileMode.Append))) { sw.WriteLine(txt + xex); } } hasDoneShit = true; } } } if (!nolist && !list && hasDoneShit) { using (StreamWriter sw = new StreamWriter(new FileStream(dst + "/list.csv", FileMode.Create))) { sw.WriteLine(string.Join(",", bdc.Header.ToArray())); } } } }
//Coverted from C (ima_rejigger2) //https://bitbucket.org/anders/wwiseconv/wiki/WWise_format public static byte[] IMA_ADPCM(byte[] a, out string xex, bool pcm) { xex = "stream"; Binboy bb = new Binboy(a); if(!bb.Open()) return a; if (bb.GetEncoding().GetString(bb.ReadBytes(4), 0, 4) != "RIFF") return a; int riffSize = bb.ReadInt32(); if (bb.GetEncoding().GetString(bb.ReadBytes(4), 0, 4) != "WAVE") return a; int waveChunkStart = 12; int waveChunkEnd = 8 + riffSize; int chunkOffset = waveChunkStart; int channels = -1; int blockSize = -1; int samples = -1; int dataSize = -1; int dataPos = -1; bb.SetPosition(0); MemoryStream ms = new MemoryStream(bb.ReadAllBytes()); BinaryWriter bw = new BinaryWriter(ms); while (chunkOffset < waveChunkEnd) { bb.SetPosition((int)chunkOffset); string chunkType = Encoding.ASCII.GetString(bb.ReadBytes(4), 0, 4); int chunkSize = bb.ReadInt32(); if (chunkOffset + 8 + chunkSize < chunkOffset + 8 || chunkOffset + 8 + chunkSize > waveChunkEnd) return a; switch (chunkType) { case "fmt ": Bandaid.Debug("fmt"); if ((int)0xE > chunkSize) return a; Bandaid.Debug("ChunkSize is correct"); ushort codec = bb.ReadUInt16(chunkOffset + 8); if (codec == 0xFFFF) { xex = "stream"; return a; } if (0x2 != codec) return a; Bandaid.Debug("Codec is correct"); channels = bb.ReadUInt16(); Bandaid.Debug("chunkOffset", chunkOffset); samples = bb.ReadInt32(); bb.ReadUInt32(); ushort sblockSize = bb.ReadUInt16(); Bandaid.Debug("Channels", channels, "BlockSize", sblockSize); blockSize = sblockSize; bw.BaseStream.Position = chunkOffset + 8; bw.Write((ushort)0x11); break; case "data": Bandaid.Debug("Data"); if (channels < 1 || blockSize < 1) return a; dataSize = chunkSize; dataPos = chunkOffset + 8; reinterleaveMS_IMA(bw, bb, dataPos, chunkSize, channels, blockSize); break; } chunkOffset += 8 + chunkSize; } if (!pcm) return ms.ToArray(); ms.Position = 0; IMA_ADPCM decode = new IMA_ADPCM(ms); xex = "wav"; return decode.Decode(); }