/// <summary> /// Run this code at launch /// </summary> void Start() { UserPreferences.Load(); Settings.ApplicationPath = Application.streamingAssetsPath; Settings.Load(); SettingsInit(); ADT.Initialize(CASC.GetComponent <CascHandler>().cascHandler); if (Settings.GetSection("path").GetString("wowsource") == null || Settings.GetSection("path").GetString("wowsource") == "") { } else { CASCConfig config = null; if (Settings.GetSection("misc").GetString("wowsource") == "game") { config = CASCConfig.LoadLocalStorageConfig(Settings.GetSection("path").GetString("selectedpath"), Settings.GetSection("misc").GetString("localproduct")); } else if (Settings.GetSection("misc").GetString("wowsource") == "online") { config = CASCConfig.LoadOnlineStorageConfig(Settings.GetSection("misc").GetString("onlineproduct"), "us", true); } CASC.GetComponent <CascHandler>().InitCasc(config, firstInstalledLocale); } }
static async Task Main(string[] args) { CASCConfig.LoadFlags |= LoadFlags.Install; var pcConfig = CASCConfig.LoadOnlineStorageConfig("hsb", "eu"); var androidConfig = CASCConfig.LoadOnlineStorageConfig("hsb", "android_google"); bool updateFile = true; if (File.Exists(Settings.FilePath)) { var text = File.ReadAllLines(Settings.FilePath); bool pc = pcConfig.VersionName != text[0]; bool android = androidConfig.VersionName != text[1]; updateFile = pc || android; if (updateFile) { var tgClient = new TelegramBotClient(Settings.TgToken); var dsClient = new DiscordWebhookClient(Settings.DsWebhookId, Settings.DsWebhookToken); if (pc) { var message = $"🆕New version released\n⚔️Platform: PC🖥️\n#️⃣Version: {pcConfig.VersionName}\n🆔BuildId: {pcConfig.BuildId}"; var tasks = new List <Task>(); tasks.Add(tgClient.SendTextMessageAsync(new Telegram.Bot.Types.ChatId(Settings.TgChatId), message)); tasks.Add(dsClient.SendMessageAsync(message)); var cascHandler = CASCHandler.OpenStorage(pcConfig); cascHandler.Root.SetFlags(LocaleFlags.ruRU, false); cascHandler.Root.MergeInstall(cascHandler.Install); cascHandler.SaveFileTo(Settings.FileName, Settings.SavePath); await Task.WhenAll(tasks); } if (android) { var message = $"🆕New version released\n⚔️Platform: Android📱\n#️⃣Version: {androidConfig.VersionName}\n🆔BuildId: {androidConfig.BuildId}"; var tasks = new List <Task>(); tasks.Add(tgClient.SendTextMessageAsync(new Telegram.Bot.Types.ChatId(Settings.TgChatId), message)); tasks.Add(dsClient.SendMessageAsync(message)); await Task.WhenAll(tasks); } } } if (updateFile) { File.WriteAllText(Settings.FilePath, $"{pcConfig.VersionName}{Environment.NewLine}{androidConfig.VersionName}"); } }
public void Ok() { if (ToggleGame.isOn) { Settings.GetSection("misc").SetValueOfKey("wowsource", "game"); Settings.GetSection("path").SetValueOfKey("selectedpath", WoWPath.text); if (CheckValidWoWPath(WoWPath.text)) { // start Initialize casc thread // _gameType = DropdownProduct.options[DropdownProduct.value].text; new Thread(() => { var config = CASCConfig.LoadLocalStorageConfig(Settings.GetSection("path").GetString("selectedpath"), _gameType); Casc.InitCasc(config, firstInstalledLocale); }).Start(); // Save Settings // Settings.GetSection("misc").SetValueOfKey("localproduct", _gameType); Settings.Save(); gameObject.SetActive(false); } else { Debug.Log("ERROR: Incorrect WoW Path..."); } } else if (ToggleOnline.isOn) { Settings.GetSection("misc").SetValueOfKey("wowsource", "online"); // Initializes CASC Thread // _gameType = DropdownOnline.options[DropdownOnline.value].text; new Thread(() => { var config = CASCConfig.LoadOnlineStorageConfig(_gameType, "us", true); Casc.InitCasc(config, firstInstalledLocale); }).Start(); // Save Settings // Settings.GetSection("misc").SetValueOfKey("onlineproduct", _gameType); Settings.Save(); gameObject.SetActive(false); } }
public static void InitCasc(BackgroundWorkerEx worker = null, string basedir = null, string program = "wowt") { CASCConfig.LoadFlags &= ~(LoadFlags.Download | LoadFlags.Install); if (basedir == null) { CASCConfig config = CASCConfig.LoadOnlineStorageConfig(program, "us", true); Console.WriteLine("Initializing CASC from web with program " + program + " and build " + config.BuildName); cascHandler = CASCHandler.OpenStorage(config, worker); } else { Console.WriteLine("Initializing CASC from local disk with basedir " + basedir); cascHandler = CASCHandler.OpenLocalStorage(basedir, worker); } cascHandler.Root.SetFlags(LocaleFlags.enUS, ContentFlags.None, false); fIsCASCInit = true; }
private static void Main() { Console.OutputEncoding = Encoding.UTF8; Files = new Dictionary <ulong, PackageRecord>(); TrackedFiles = new Dictionary <ushort, HashSet <ulong> >(); #region Tool Detection HashSet <Type> tools = new HashSet <Type>(); { Assembly asm = typeof(ITool).Assembly; Type t = typeof(ITool); List <Type> types = asm.GetTypes().Where(tt => tt != t && t.IsAssignableFrom(tt)).ToList(); foreach (Type tt in types) { ToolAttribute attrib = tt.GetCustomAttribute <ToolAttribute>(); if (tt.IsInterface || attrib == null) { continue; } tools.Add(tt); if (attrib.TrackTypes == null) { continue; } foreach (ushort type in attrib.TrackTypes) { if (!TrackedFiles.ContainsKey(type)) { TrackedFiles[type] = new HashSet <ulong>(); } } } } #endregion Flags = FlagParser.Parse <ToolFlags>(() => PrintHelp(tools)); if (Flags == null) { return; } Logger.EXIT = !Flags.GracefulExit; ITool targetTool = null; ICLIFlags targetToolFlags = null; #region Tool Activation foreach (Type type in tools) { ToolAttribute attrib = type.GetCustomAttribute <ToolAttribute>(); if (!string.Equals(attrib.Keyword, Flags.Mode, StringComparison.InvariantCultureIgnoreCase)) { continue; } targetTool = Activator.CreateInstance(type) as ITool; if (attrib.CustomFlags != null) { Type flags = attrib.CustomFlags; if (typeof(ICLIFlags).IsAssignableFrom(flags)) { targetToolFlags = typeof(FlagParser).GetMethod("Parse", new Type[] { }).MakeGenericMethod(flags).Invoke(null, null) as ICLIFlags; } } break; } #endregion if (targetTool == null) { FlagParser.Help <ToolFlags>(false); PrintHelp(tools); if (Debugger.IsAttached) { Debugger.Break(); } return; } #region Initialize CASC Log("{0} v{1}", Assembly.GetExecutingAssembly().GetName().Name, Util.GetVersion()); Log("Initializing CASC..."); Log("Set language to {0}", Flags.Language); CDNIndexHandler.Cache.Enabled = Flags.UseCache; CDNIndexHandler.Cache.CacheData = Flags.CacheData; CDNIndexHandler.Cache.Validate = Flags.ValidateCache; // ngdp:us:pro // http:us:pro:us.patch.battle.net:1119 if (Flags.OverwatchDirectory.ToLowerInvariant().Substring(0, 5) == "ngdp:") { string cdn = Flags.OverwatchDirectory.Substring(5, 4); string[] parts = Flags.OverwatchDirectory.Substring(5).Split(':'); string region = "us"; string product = "pro"; if (parts.Length > 1) { region = parts[1]; } if (parts.Length > 2) { product = parts[2]; } if (cdn == "bnet") { Config = CASCConfig.LoadOnlineStorageConfig(product, region); } else { if (cdn == "http") { string host = string.Join(":", parts.Skip(3)); Config = CASCConfig.LoadOnlineStorageConfig(host, product, region, true, true, true); } } } else { Config = CASCConfig.LoadLocalStorageConfig(Flags.OverwatchDirectory, !Flags.SkipKeys, false); } Config.Languages = new HashSet <string>(new[] { Flags.Language }); #endregion foreach (Dictionary <string, string> build in Config.BuildInfo) { if (!build.ContainsKey("Tags")) { continue; } if (build["Tags"].Contains("XX?")) { IsPTR = true; } // us ptr region is known as XX, so just look for it in the tags. // this should work... untested for Asia } BuildVersion = uint.Parse(Config.BuildName.Split('.').Last()); if (Flags.SkipKeys) { Log("Disabling Key auto-detection..."); } Log("Using Overwatch Version {0}", Config.BuildName); CASC = CASCHandler.OpenStorage(Config); Root = CASC.Root as OwRootHandler; if (Root == null) { ErrorLog("Not a valid overwatch installation"); return; } // Fail when trying to extract data from a specified language with 2 or less files found. if (!Root.APMFiles.Any()) { ErrorLog("Could not find the files for language {0}. Please confirm that you have that language installed, and are using the names from the target language.", Flags.Language); if (!Flags.GracefulExit) { return; } } Log("Mapping..."); TrackedFiles[0x90] = new HashSet <ulong>(); IO.MapCMF(); IO.LoadGUIDTable(); Sound.WwiseBank.GetReady(); #region Key Detection if (!Flags.SkipKeys) { Log("Adding Encryption Keys..."); foreach (ulong key in TrackedFiles[0x90]) { if (!ValidKey(key)) { continue; } using (Stream stream = IO.OpenFile(Files[key])) { if (stream == null) { continue; } STUEncryptionKey encryptionKey = GetInstance <STUEncryptionKey>(key); if (encryptionKey != null && encryptionKey.LongKey != 0 && !KeyService.keys.ContainsKey(encryptionKey.LongRevKey)) { KeyService.keys.Add(encryptionKey.LongRevKey, encryptionKey.KeyValue); Log("Added Encryption Key {0}, Value: {1}", encryptionKey.KeyNameProper, encryptionKey.Key); } } } } #endregion Log("Tooling..."); targetTool.Parse(targetToolFlags); if (Debugger.IsAttached) { Debugger.Break(); } }
static void Main(string[] args) { if (args.Length != 4) { Console.WriteLine("Invalid arguments count!"); Console.WriteLine("Usage: CASCConsole <pattern> <destination> <localeFlags> <contentFlags>"); return; } Console.WriteLine("Settings:"); Console.WriteLine(" WowPath: {0}", Settings.Default.StoragePath); Console.WriteLine(" OnlineMode: {0}", Settings.Default.OnlineMode); Console.WriteLine("Loading..."); BackgroundWorkerEx bgLoader = new BackgroundWorkerEx(); bgLoader.ProgressChanged += BgLoader_ProgressChanged; CASCConfig config = Settings.Default.OnlineMode ? CASCConfig.LoadOnlineStorageConfig(Settings.Default.Product, "us") : CASCConfig.LoadLocalStorageConfig(Settings.Default.StoragePath); CASCHandler cascHandler = CASCHandler.OpenStorage(config, bgLoader); string pattern = args[0]; string dest = args[1]; LocaleFlags locale = (LocaleFlags)Enum.Parse(typeof(LocaleFlags), args[2]); ContentFlags content = (ContentFlags)Enum.Parse(typeof(ContentFlags), args[3]); cascHandler.Root.LoadListFile(Path.Combine(Environment.CurrentDirectory, "listfile.txt"), bgLoader); CASCFolder root = cascHandler.Root.SetFlags(locale, content); Console.WriteLine("Loaded."); Console.WriteLine("Extract params:", pattern, dest, locale); Console.WriteLine(" Pattern: {0}", pattern); Console.WriteLine(" Destination: {0}", dest); Console.WriteLine(" LocaleFlags: {0}", locale); Console.WriteLine(" ContentFlags: {0}", content); Wildcard wildcard = new Wildcard(pattern, true, RegexOptions.IgnoreCase); foreach (var file in root.GetFiles()) { if (wildcard.IsMatch(file.FullName)) { Console.Write("Extracting '{0}'...", file.FullName); try { cascHandler.SaveFileTo(file.FullName, dest); Console.WriteLine(" Ok!"); } catch (Exception exc) { Console.WriteLine(" Error!"); Logger.WriteLine(exc.Message); } } } Console.WriteLine("Extracted."); }
static void Main(string[] args) { Console.OutputEncoding = Encoding.UTF8; List <IOvertool> tools = new List <IOvertool>(); { Assembly asm = typeof(IOvertool).Assembly; Type t = typeof(IOvertool); List <Type> types = asm.GetTypes().Where(tt => tt != t && t.IsAssignableFrom(tt)).ToList(); foreach (Type tt in types) { if (tt.IsInterface) { continue; } IOvertool toolinst = (IOvertool)Activator.CreateInstance(tt); if (toolinst.Display) { tools.Add(toolinst); } } } OverToolFlags flags = FlagParser.Parse <OverToolFlags>(() => PrintHelp(tools)); if (flags == null) { return; } bool quiet = flags.Quiet; string root = flags.OverwatchDirectory; char opt = flags.Mode[0]; IOvertool tool = null; Dictionary <ushort, List <ulong> > track = new Dictionary <ushort, List <ulong> >(); track[0x90] = new List <ulong>(); // internal requirements toolsMap = new Dictionary <char, IOvertool>(); foreach (IOvertool t in tools) { if (t.Opt == opt) { tool = t; } foreach (ushort tr in t.Track) { if (!track.ContainsKey(tr)) { track[tr] = new List <ulong>(); } } if (toolsMap.ContainsKey(t.Opt)) { Console.Out.WriteLine("Duplicate opt! {0} conflicts with {1}", t.Title, toolsMap[t.Opt].Title); } toolsMap[t.Opt] = t; } if (tool == null || flags.Positionals.Length - 2 < tool.MinimumArgs) { PrintHelp(tools); return; } Dictionary <ulong, Record> map = new Dictionary <ulong, Record>(); Console.Out.WriteLine("{0} v{1}", Assembly.GetExecutingAssembly().GetName().Name, OWLib.Util.GetVersion()); Console.Out.WriteLine("Initializing CASC..."); Console.Out.WriteLine("Set language to {0}", flags.Language); CDNIndexHandler.Cache.Enabled = flags.UseCache; CDNIndexHandler.Cache.CacheData = flags.CacheData; CDNIndexHandler.Cache.Validate = flags.ValidateCache; CASCConfig config = null; // ngdp:us:pro // http:us:pro:us.patch.battle.net:1119 if (root.ToLowerInvariant().Substring(0, 5) == "ngdp:") { string cdn = root.Substring(5, 4); string[] parts = root.Substring(5).Split(':'); string region = "us"; string product = "pro"; if (parts.Length > 1) { region = parts[1]; } if (parts.Length > 2) { product = parts[2]; } if (cdn == "bnet") { config = CASCConfig.LoadOnlineStorageConfig(product, region); } else { if (cdn == "http") { string host = string.Join(":", parts.Skip(3)); config = CASCConfig.LoadOnlineStorageConfig(host, product, region, true, true, true); } } } else { config = CASCConfig.LoadLocalStorageConfig(root, !flags.SkipKeys, false); } config.Languages = new HashSet <string>(new string[1] { flags.Language }); if (flags.SkipKeys) { Console.Out.WriteLine("Disabling Key auto-detection..."); } Console.Out.WriteLine("Using Overwatch Version {0}", config.BuildName); CASCHandler handler = CASCHandler.OpenStorage(config); OwRootHandler ow = handler.Root as OwRootHandler; if (ow == null) { Console.Error.WriteLine("Not a valid overwatch installation"); return; } // Fail when trying to extract data from a specified language with 2 or less files found. if (ow.APMFiles.Count() == 0) { Console.Error.WriteLine("Could not find the files for language {0}. Please confirm that you have that language installed, and are using the names from the target language.", flags.Language); return; } Console.Out.WriteLine("Mapping..."); Util.MapCMF(ow, handler, map, track, flags); if (!flags.SkipKeys) { Console.Out.WriteLine("Adding Encryption Keys..."); foreach (ulong key in track[0x90]) { if (!map.ContainsKey(key)) { continue; } using (Stream stream = Util.OpenFile(map[key], handler)) { if (stream == null) { continue; } STUD stud = new STUD(stream); if (stud.Instances[0].Name != stud.Manager.GetName(typeof(EncryptionKey))) { continue; } EncryptionKey ek = (EncryptionKey)stud.Instances[0]; if (!KeyService.keys.ContainsKey(ek.KeyNameLong)) { KeyService.keys.Add(ek.KeyNameLong, ek.KeyValueText.ToByteArray()); Console.Out.WriteLine("Added Encryption Key {0}, Value: {1}", ek.KeyNameText, ek.KeyValueText); } } } } Console.Out.WriteLine("Tooling..."); tool.Parse(track, map, handler, quiet, flags); if (System.Diagnostics.Debugger.IsAttached) { System.Diagnostics.Debugger.Break(); } }
static void Main(string[] args) { Console.OutputEncoding = Encoding.UTF8; List <IOvertool> tools = new List <IOvertool>(); { Assembly asm = typeof(IOvertool).Assembly; Type t = typeof(IOvertool); List <Type> types = asm.GetTypes().Where(tt => tt != t && t.IsAssignableFrom(tt)).ToList(); foreach (Type tt in types) { if (tt.IsInterface) { continue; } IOvertool toolinst = (IOvertool)Activator.CreateInstance(tt); if (toolinst.Display) { tools.Add(toolinst); } } } OverToolFlags flags = FlagParser.Parse <OverToolFlags>(() => PrintHelp(tools)); if (flags == null) { return; } Logger.EXIT = !flags.GracefulExit; bool quiet = flags.Quiet; string root = flags.OverwatchDirectory; string opt = flags.Mode; IOvertool tool = null; Dictionary <ushort, List <ulong> > track = new Dictionary <ushort, List <ulong> > { [0x90] = new List <ulong>() // internal requirements }; toolsMap = new Dictionary <string, IOvertool>(); foreach (IOvertool t in tools) { if (t.FullOpt == opt || (opt.Length == 1 && t.Opt != (char)0 && t.Opt == opt[0])) { tool = t; } if (t.Track != null) { foreach (ushort tr in t.Track) { if (!track.ContainsKey(tr)) { track[tr] = new List <ulong>(); } } } if (toolsMap.ContainsKey(t.FullOpt)) { Console.Out.WriteLine("Duplicate opt! {0} conflicts with {1}", t.Title, toolsMap[t.FullOpt].Title); } if (t.FullOpt.Length == 1) { Console.Out.WriteLine("FullOpt should not be length 1 for {0}", t.Title); } toolsMap[t.FullOpt] = t; } if (tool == null || flags.Positionals.Length - 2 < tool.MinimumArgs) { PrintHelp(tools); return; } Dictionary <ulong, Record> map = new Dictionary <ulong, Record>(); Console.Out.WriteLine("{0} v{1}", Assembly.GetExecutingAssembly().GetName().Name, OWLib.Util.GetVersion()); Console.Out.WriteLine("Initializing CASC..."); Console.Out.WriteLine("Set language to {0}", flags.Language); CDNIndexHandler.Cache.Enabled = flags.UseCache; CDNIndexHandler.Cache.CacheData = flags.CacheData; CDNIndexHandler.Cache.Validate = flags.ValidateCache; CASCConfig config = null; // ngdp:us:pro // http:us:pro:us.patch.battle.net:1119 if (root.ToLowerInvariant().Substring(0, 5) == "ngdp:") { string cdn = root.Substring(5, 4); string[] parts = root.Substring(5).Split(':'); string region = "us"; string product = "pro"; if (parts.Length > 1) { region = parts[1]; } if (parts.Length > 2) { product = parts[2]; } if (cdn == "bnet") { config = CASCConfig.LoadOnlineStorageConfig(product, region); } else { if (cdn == "http") { string host = string.Join(":", parts.Skip(3)); config = CASCConfig.LoadOnlineStorageConfig(host, product, region, true, true, true); } } } else { config = CASCConfig.LoadLocalStorageConfig(root, !flags.SkipKeys, false); } config.Languages = new HashSet <string>(new string[1] { flags.Language }); if (flags.SkipKeys) { Console.Out.WriteLine("Disabling Key auto-detection..."); } Regex versionRegex = new Regex(@"\d+\.\d+"); Match versionMatch = versionRegex.Match(config.BuildName); if (versionMatch.Success) { float version = float.Parse(versionMatch.Value); if (version > 1.13) { Console.ForegroundColor = ConsoleColor.Red; Console.Out.WriteLine("==========\nWARNING: Overtool only works with Overwatch version 1.13 and below! You are using {0}!", config.BuildName); Console.Out.WriteLine("You must use DataTool for Overwatch 1.14 and above!\n=========="); Console.ResetColor(); } } Console.Out.WriteLine("Using Overwatch Version {0}", config.BuildName); CASCHandler handler = CASCHandler.OpenStorage(config); OwRootHandler ow = handler.Root as OwRootHandler; if (ow == null) { Console.Error.WriteLine("Not a valid overwatch installation"); return; } // Fail when trying to extract data from a specified language with 2 or less files found. if (ow.APMFiles.Count() == 0) { Console.Error.WriteLine("Could not find the files for language {0}. Please confirm that you have that language installed, and are using the names from the target language.", flags.Language); if (!flags.GracefulExit) { return; } } Console.Out.WriteLine("Mapping..."); Util.MapCMF(ow, handler, map, track, flags.Language); if (!flags.SkipKeys) { Console.Out.WriteLine("Adding Encryption Keys..."); foreach (ulong key in track[0x90]) { if (!map.ContainsKey(key)) { continue; } using (Stream stream = Util.OpenFile(map[key], handler)) { if (stream == null) { continue; } ISTU stu = ISTU.NewInstance(stream, UInt32.Parse(config.BuildName.Split('.').Last())); if (!(stu.Instances.FirstOrDefault() is STUEncryptionKey)) { continue; } STUEncryptionKey ek = stu.Instances.FirstOrDefault() as STUEncryptionKey; if (ek != null && !KeyService.keys.ContainsKey(ek.LongRevKey)) { KeyService.keys.Add(ek.LongRevKey, ek.KeyValue); Console.Out.WriteLine("Added Encryption Key {0}, Value: {1}", ek.KeyNameProper, ek.Key); } } } } Console.Out.WriteLine("Tooling..."); tool.Parse(track, map, handler, quiet, flags); if (System.Diagnostics.Debugger.IsAttached) { System.Diagnostics.Debugger.Break(); } }
//class Hashes //{ // public string[] install; // public string[] encoding; //} static void Main(string[] args) { //HashSet<string> data = new HashSet<string>(); //HashSet<string> installs = new HashSet<string>(); //using (StreamReader sr = new StreamReader("list.txt")) //{ // string line1; // while((line1 = sr.ReadLine()) != null) // { // data.Add(line1.Substring(19, 32)); // if (line1.Contains("install")) // { // installs.Add(line1.Substring(93, 32)); // } // } //} ////foreach (var cfg in data) ////{ //// string url = string.Format("https://bnet.marlam.in/tpr/wow/config/{0}/{1}/{2}", cfg.Substring(0, 2), cfg.Substring(2, 2), cfg); //// var stream = CDNIndexHandler.OpenFileDirect(url); //// using (var fileStream = File.Create("builds\\" + cfg)) //// { //// stream.CopyTo(fileStream); //// } ////} //Dictionary<string, Hashes> data2 = new Dictionary<string, Hashes>(); //foreach (var file in Directory.GetFiles("builds")) //{ // using(var sr = new StreamReader(file)) // { // string line; // while ((line = sr.ReadLine()) != null) // { // if (string.IsNullOrWhiteSpace(line) || line.StartsWith("#")) // skip empty lines and comments // continue; // string[] tokens = line.Split(new char[] { '=' }, 2, StringSplitOptions.RemoveEmptyEntries); // if (tokens.Length != 2) // throw new Exception("KeyValueConfig: tokens.Length != 2"); // var values = tokens[1].Trim().Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); // var valuesList = values.ToList(); // if (!data2.ContainsKey(file)) // data2[file] = new Hashes(); // if (values[0] == "install") // { // data2[file].install = values; // } // if (values[0] == "encoding") // { // data2[file].encoding = values; // } // //sr.Data.Add(tokens[0].Trim(), valuesList); // } // } //} //Dictionary<string, string> realInstalls = new Dictionary<string, string>(); //foreach(var kv in data2) //{ // for(int i = 6; i < kv.Value.install.Length; i++) // { // if (installs.Contains(kv.Value.install[i])) // { // //Console.WriteLine("{0} {1}", kv.Value.install[i], kv.Value.encoding[i]); // realInstalls[kv.Value.install[i]] = kv.Value.encoding[i]; // } // } //} //CASCConfig.ValidateData = false; //int buildIndex = 0; //foreach (var kvinstall in realInstalls) //{ // string url1 = string.Format("http://bnet.marlamin.com/tpr/wow/data/{0}/{1}/{2}", kvinstall.Key.Substring(0, 2), kvinstall.Key.Substring(2, 2), kvinstall.Key); // BLTEStream instFile = new BLTEStream(CDNIndexHandler.OpenFileDirect(url1), new MD5Hash()); // InstallHandler install = new InstallHandler(new BinaryReader(instFile), null); // //foreach(var ent in install.GetEntries().Where(e => e.Name.Contains("MacOS"))) // //{ // // Console.WriteLine(ent.Name); // //} // //continue; // string url2 = string.Format("http://bnet.marlamin.com/tpr/wow/data/{0}/{1}/{2}", kvinstall.Value.Substring(0, 2), kvinstall.Value.Substring(2, 2), kvinstall.Value); // BLTEStream encFile = new BLTEStream(CDNIndexHandler.OpenFileDirect(url2), new MD5Hash()); // EncodingHandler encoding = new EncodingHandler(new BinaryReader(encFile), null); // string[] files = new string[] { "WowB.exe", "WowT.exe", "Wow.exe", "WowB-64.exe", "WowT-64.exe", "Wow-64.exe", "RenderService.exe", "RenderService-64.exe", // @"World of Warcraft Public Test.app\Contents\MacOS\World of Warcraft", // @"World of Warcraft Public Test.app\Contents\MacOS\World of Warcraft 64", // @"World of Warcraft Beta.app\Contents\MacOS\World of Warcraft", // @"World of Warcraft Beta.app\Contents\MacOS\World of Warcraft 64", // @"World of Warcraft Retail.app\Contents\MacOS\World of Warcraft", // @"World of Warcraft Retail.app\Contents\MacOS\World of Warcraft 64", // @"World of Warcraft Test.app\Contents\MacOS\World of Warcraft", // @"World of Warcraft.app\Contents\MacOS\World of Warcraft" // }; // string outFolder = "build_" + buildIndex++; // foreach (var file in files) // { // var entries = install.GetEntriesByName(file); // foreach (var entry in entries) // { // bool ok = encoding.GetEntry(entry.MD5, out var encEntry); // if (ok) // { // string chash = encEntry.Key.ToHexString().ToLower(); // Console.WriteLine("http://bnet.marlamin.com/tpr/wow/data/{0}/{1}/{2} {3}", chash.Substring(0, 2), chash.Substring(2, 2), chash, file); // string url = string.Format("http://blzddist1-a.akamaihd.net/tpr/wow/data/{0}/{1}/{2}", chash.Substring(0, 2), chash.Substring(2, 2), chash); // //string url = string.Format("http://bnet.marlamin.com/tpr/wow/data/{0}/{1}/{2}", chash.Substring(0, 2), chash.Substring(2, 2), chash); // try // { // var stream = CDNIndexHandler.OpenFileDirect(url); // BLTEStream blte3 = new BLTEStream(stream, new MD5Hash()); // string outFile = Path.Combine(outFolder, chash + "_" + file); // string outDir = Path.GetDirectoryName(outFile); // if (!Directory.Exists(outDir)) // Directory.CreateDirectory(outDir); // using (var fileStream = File.Create(outFile)) // { // blte3.CopyTo(fileStream); // } // } // catch(Exception exc) // { // Console.WriteLine(exc.Message); // } // } // } // } //} //ArmadilloCrypt crypt = new ArmadilloCrypt("sc1Dev"); //var decr = crypt.DecryptFile(@"c:\Users\TOM_RUS\Downloads\e32f46c7245bfc154e43924555a5cf9f"); //File.WriteAllBytes("test", decr); //byte[] keyBytes = new byte[16]; //ArmadilloCrypt crypt = new ArmadilloCrypt(keyBytes); //string buildconfigfile = "9f6048f8bd01f38ec0be83f4a9fe5a10"; //byte[] data = File.ReadAllBytes(buildconfigfile); //byte[] IV = buildconfigfile.Substring(16).ToByteArray(); //unsafe //{ // fixed (byte* ptr = keyBytes) // { // for (ulong i = 0; i < ulong.MaxValue; i++) // { // for (ulong j = 0; j < ulong.MaxValue; j++) // { // byte[] decrypted = crypt.DecryptFile(IV, data); // if (decrypted[0] == 0x23 && decrypted[1] == 0x20 && decrypted[2] == 0x42 && decrypted[3] == 0x75) // { // Console.WriteLine("key found: {0} {1} ?", i, j); // } // *(ulong*)ptr = j; // if (j % 1000000 == 0) // Console.WriteLine("{0}/{1}", j, ulong.MaxValue); // } // *(ulong*)(ptr + 8) = i; // } // } //} if (args.Length != 5) { Console.WriteLine("Invalid arguments count!"); Console.WriteLine("Usage: CASCConsole <mode> <pattern|listfile> <destination> <localeFlags> <overrideArchive>"); return; } Console.WriteLine("Settings:"); Console.WriteLine(" WowPath: {0}", Settings.Default.StoragePath); Console.WriteLine(" OnlineMode: {0}", Settings.Default.OnlineMode); Console.WriteLine("Loading..."); BackgroundWorkerEx bgLoader = new BackgroundWorkerEx(); bgLoader.ProgressChanged += BgLoader_ProgressChanged; //CASCConfig.LoadFlags |= LoadFlags.Install; CASCConfig config = Settings.Default.OnlineMode ? CASCConfig.LoadOnlineStorageConfig(Settings.Default.Product, "us") : CASCConfig.LoadLocalStorageConfig(Settings.Default.StoragePath, Settings.Default.Product); CASCHandler cascHandler = CASCHandler.OpenStorage(config, bgLoader); string mode = args[0]; string pattern = args[1]; string dest = args[2]; LocaleFlags locale = (LocaleFlags)Enum.Parse(typeof(LocaleFlags), args[3]); bool overrideArchive = bool.Parse(args[4]); cascHandler.Root.LoadListFile(Path.Combine(Environment.CurrentDirectory, "listfile.csv"), bgLoader); CASCFolder root = cascHandler.Root.SetFlags(locale, overrideArchive); //cascHandler.Root.MergeInstall(cascHandler.Install); Console.WriteLine("Loaded."); Console.WriteLine("Extract params:"); Console.WriteLine(" Mode: {0}", mode); Console.WriteLine(" Pattern/Listfile: {0}", pattern); Console.WriteLine(" Destination: {0}", dest); Console.WriteLine(" LocaleFlags: {0}", locale); Console.WriteLine(" OverrideArchive: {0}", overrideArchive); if (mode == "pattern") { Wildcard wildcard = new Wildcard(pattern, true, RegexOptions.IgnoreCase); foreach (var file in CASCFolder.GetFiles(root.Entries.Select(kv => kv.Value))) { if (wildcard.IsMatch(file.FullName)) { ExtractFile(cascHandler, file.Hash, file.FullName, dest); } } } else if (mode == "listfile") { if (cascHandler.Root is WowRootHandler wowRoot) { char[] splitChar = new char[] { ';' }; var names = File.ReadLines(pattern).Select(s => s.Split(splitChar, 2)).Select(s => new { id = int.Parse(s[0]), name = s[1] }); foreach (var file in names) { ExtractFile(cascHandler, wowRoot.GetHashByFileDataId(file.id), file.name, dest); } } else { var names = File.ReadLines(pattern); foreach (var file in names) { ExtractFile(cascHandler, 0, file, dest); } } } Console.WriteLine("Extracted."); }
static void Main(string[] args) { //byte[] keyBytes = new byte[16]; //ArmadilloCrypt crypt = new ArmadilloCrypt(keyBytes); //string buildconfigfile = "9f6048f8bd01f38ec0be83f4a9fe5a10"; //byte[] data = File.ReadAllBytes(buildconfigfile); //byte[] IV = buildconfigfile.Substring(16).ToByteArray(); //unsafe //{ // fixed (byte* ptr = keyBytes) // { // for (ulong i = 0; i < ulong.MaxValue; i++) // { // for (ulong j = 0; j < ulong.MaxValue; j++) // { // byte[] decrypted = crypt.DecryptFile(IV, data); // if (decrypted[0] == 0x23 && decrypted[1] == 0x20 && decrypted[2] == 0x42 && decrypted[3] == 0x75) // { // Console.WriteLine("key found: {0} {1} ?", i, j); // } // *(ulong*)ptr = j; // if (j % 1000000 == 0) // Console.WriteLine("{0}/{1}", j, ulong.MaxValue); // } // *(ulong*)(ptr + 8) = i; // } // } //} if (args.Length != 4) { Console.WriteLine("Invalid arguments count!"); Console.WriteLine("Usage: CASCConsole <pattern> <destination> <localeFlags> <contentFlags>"); return; } Console.WriteLine("Settings:"); Console.WriteLine(" WowPath: {0}", Settings.Default.StoragePath); Console.WriteLine(" OnlineMode: {0}", Settings.Default.OnlineMode); Console.WriteLine("Loading..."); BackgroundWorkerEx bgLoader = new BackgroundWorkerEx(); bgLoader.ProgressChanged += BgLoader_ProgressChanged; //CASCConfig.LoadFlags |= LoadFlags.Install; CASCConfig config = Settings.Default.OnlineMode ? CASCConfig.LoadOnlineStorageConfig(Settings.Default.Product, "us") : CASCConfig.LoadLocalStorageConfig(Settings.Default.StoragePath); CASCHandler cascHandler = CASCHandler.OpenStorage(config, bgLoader); string pattern = args[0]; string dest = args[1]; LocaleFlags locale = (LocaleFlags)Enum.Parse(typeof(LocaleFlags), args[2]); ContentFlags content = (ContentFlags)Enum.Parse(typeof(ContentFlags), args[3]); cascHandler.Root.LoadListFile(Path.Combine(Environment.CurrentDirectory, "listfile.txt"), bgLoader); CASCFolder root = cascHandler.Root.SetFlags(locale, content); //cascHandler.Root.MergeInstall(cascHandler.Install); Console.WriteLine("Loaded."); Console.WriteLine("Extract params:"); Console.WriteLine(" Pattern: {0}", pattern); Console.WriteLine(" Destination: {0}", dest); Console.WriteLine(" LocaleFlags: {0}", locale); Console.WriteLine(" ContentFlags: {0}", content); Wildcard wildcard = new Wildcard(pattern, true, RegexOptions.IgnoreCase); foreach (var file in CASCFolder.GetFiles(root.Entries.Select(kv => kv.Value))) { if (wildcard.IsMatch(file.FullName)) { Console.Write("Extracting '{0}'...", file.FullName); try { cascHandler.SaveFileTo(file.FullName, dest); Console.WriteLine(" Ok!"); } catch (Exception exc) { Console.WriteLine(" Error!"); Logger.WriteLine(exc.Message); } } } Console.WriteLine("Extracted."); }
static void Main(string[] args) { // APMTool.exe "root" file flag [query] if (args.Length < 2) { Console.Out.WriteLine("Usage: APMTool.exe \"root directory\" op args"); Console.Out.WriteLine("OP f: Find files in APM. args: query... query: a[APM NAME] i[INDEX HEX] t[TYPE HEX] s[SIZE LESS THAN] S[SIZE GREATER THAN] N[INDEX WITHOUT IDENTIFIER]"); Console.Out.WriteLine("OP l: List files in package. args: query query: p[PACKAGE KEY HEX] i[CONTENT KEY HEX]"); Console.Out.WriteLine("OP a: Output all APM names"); Console.Out.WriteLine("OP t: Output all types"); Console.Out.WriteLine(""); Console.Out.WriteLine("Examples:"); Console.Out.WriteLine("APMTool.exe overwatch l iDFEF49BEE7E66774E46DA9EEA750A552"); Console.Out.WriteLine("APMTool.exe overwatch fa i11CE t00C"); Console.Out.WriteLine("APMTool.exe overwatch fa i4F2"); return; } string root = args[0]; string flag = args[1]; Console.Out.WriteLine("{0} v{1}", Assembly.GetExecutingAssembly().GetName().Name, OWLib.Util.GetVersion()); OwRootHandler.LOAD_PACKAGES = true; CASCConfig config = null; // ngdp:us:pro // http:us:pro:us.patch.battle.net:1119 if (root.ToLowerInvariant().Substring(0, 5) == "ngdp:") { string cdn = root.Substring(5, 4); string[] parts = root.Substring(5).Split(':'); string region = "us"; string product = "pro"; if (parts.Length > 1) { region = parts[1]; } if (parts.Length > 2) { product = parts[2]; } if (cdn == "bnet") { config = CASCConfig.LoadOnlineStorageConfig(product, region); } else { if (cdn == "http") { string host = string.Join(":", parts.Skip(3)); config = CASCConfig.LoadOnlineStorageConfig(host, product, region, true, true, true); } } } else { config = CASCConfig.LoadLocalStorageConfig(root, true, true); } object[] query = null; if (flag[0] == 'f' || flag[0] == 'C') { object[] t = new object[6] { null, null, null, null, null, null }; List <ulong> id = new List <ulong>(); List <ulong> type = new List <ulong>(); List <string> apms = new List <string>(); List <ulong> idi = new List <ulong>(); for (int i = 2; i < args.Length; ++i) { string arg = args[i]; try { switch (arg[0]) { case 'I': case 'i': id.Add(ulong.Parse(arg.Substring(1), NumberStyles.HexNumber)); break; case 'T': case 't': type.Add(ulong.Parse(arg.Substring(1), NumberStyles.HexNumber)); break; case 'n': case 'N': idi.Add(ulong.Parse(arg.Substring(1), NumberStyles.HexNumber)); break; case 'a': apms.Add(arg.Substring(1).ToLowerInvariant()); break; case 's': { int v = int.Parse(arg.Substring(1), NumberStyles.Number); if (t[2] == null) { t[2] = v; } else if (v > (int)t[2]) { t[2] = v; } } break; case 'S': { int v = int.Parse(arg.Substring(1), NumberStyles.Number); if (t[3] == null) { t[3] = v; } else if (v < (int)t[3]) { t[3] = v; } } break; } } finally { t[0] = id; t[1] = type; t[4] = apms; t[5] = idi; query = t; } } } else if (flag[0] == 'l') { object[] t = new object[2] { null, null }; List <ulong> pkg = new List <ulong>(); List <string> content = new List <string>(); for (int i = 2; i < args.Length; ++i) { string arg = args[i]; try { switch (arg[0]) { case 'p': pkg.Add(ulong.Parse(arg.Substring(1), NumberStyles.HexNumber)); break; case 'P': pkg.Add(ulong.Parse(arg.Substring(1), NumberStyles.Number)); break; case 'i': content.Add(arg.Substring(1).ToUpperInvariant()); break; } } finally { t[0] = pkg; t[1] = content; query = t; } } } else if (flag[0] == 'a') { config.Languages = null; } else if (flag[0] == 't') { query = new object[1] { new HashSet <ulong>() }; } else { Console.Error.WriteLine("Unsupported operand {0}", flag[0]); return; } CASCHandler handler = CASCHandler.OpenStorage(config); OwRootHandler ow = handler.Root as OwRootHandler; if (ow == null) { Console.Error.WriteLine("Not a valid Overwatch installation"); return; } if (flag[0] == 'a') { foreach (string name in ow.APMList) { Console.Out.WriteLine(name); } return; } foreach (APMFile apm in ow.APMFiles) { string apmName = System.IO.Path.GetFileName(apm.Name); if (flag[0] == 'f' && query[4] != null && ((List <string>)query[4]).Count > 0 && !((List <string>)query[4]).Contains(apmName.ToLowerInvariant())) { continue; } if (flag[0] == 'C') { foreach (ulong key in apm.CMFMap.Keys) { ulong rtype = GUID.Type(key); ulong rindex = GUID.LongKey(key); ulong rindex2 = GUID.Index(key); bool check1 = ((List <ulong>)query[0]).Count == 0; bool check2 = ((List <ulong>)query[1]).Count == 0; bool check5 = ((List <ulong>)query[5]).Count == 0; if (((List <ulong>)query[0]).Count > 0 && ((List <ulong>)query[0]).Contains(rindex)) // if index is not in i { check1 = true; } if (((List <ulong>)query[1]).Count > 0 && ((List <ulong>)query[1]).Contains(rtype)) // if type is not in t { check2 = true; } if (((List <ulong>)query[5]).Count > 0 && ((List <ulong>)query[5]).Contains(rindex2)) // if type is not in t { check5 = true; } bool check = check1 && check2 && check5; if (check) { Console.Out.WriteLine("Found {0:X12}.{1:X3} in APM {2}", rindex, rtype, apm.Name); } } } else if (flag[0] == 't') { foreach (ulong key in apm.CMFMap.Keys) { ((HashSet <ulong>)query[0]).Add(key >> 48); } } if (flag[0] == 'C' || flag[0] == 't') { continue; } for (long i = 0; i < apm.Packages.LongLength; ++i) { APMPackage package = apm.Packages[i]; PackageIndexRecord[] records = apm.Records[i]; if (flag[0] == 'l') { if (((List <ulong>)query[0]).Count + ((List <string>)query[1]).Count == 0) { return; } bool ret = true; if (((List <ulong>)query[0]).Count > 0 && ((List <ulong>)query[0]).Contains(package.packageKey)) { ret = false; } if (ret && ((List <string>)query[1]).Count > 0 && ((List <string>)query[1]).Contains(package.indexContentKey.ToHexString().ToUpperInvariant())) { ret = false; } if (ret) { continue; } ((List <ulong>)query[0]).Remove(package.packageKey); ((List <string>)query[1]).Remove(package.indexContentKey.ToHexString().ToUpperInvariant()); Console.Out.WriteLine("Dump for package i{0} / p{1:X} in APM {2}", package.indexContentKey.ToHexString().ToUpperInvariant(), package.packageKey, apm.Name); } for (long j = 0; j < records.LongLength; ++j) { PackageIndexRecord record = records[j]; ulong rtype = GUID.Type(record.Key); ulong rindex = GUID.LongKey(record.Key); ulong rindex2 = GUID.Index(record.Key); if (flag[0] == 'f') { bool check1 = ((List <ulong>)query[0]).Count == 0; bool check2 = ((List <ulong>)query[1]).Count == 0; bool check3 = query[2] == null; bool check4 = query[3] == null; bool check5 = ((List <ulong>)query[5]).Count == 0; if (((List <ulong>)query[0]).Count > 0 && ((List <ulong>)query[0]).Contains(rindex)) // if index is not in i { check1 = true; } if (((List <ulong>)query[1]).Count > 0 && ((List <ulong>)query[1]).Contains(rtype)) // if type is not in t { check2 = true; } if (query[2] != null && (int)query[2] > record.Size) // if size is less than s[lt] { check3 = true; } if (query[3] != null && (int)query[3] < record.Size) // if size is greater than s[gt] { check4 = true; } if (((List <ulong>)query[5]).Count > 0 && ((List <ulong>)query[5]).Contains(rindex2)) // if type is not in t { check5 = true; } bool check = check1 && check2 && check3 && check4 && check5; if (check) { Console.Out.WriteLine("Found {0:X12}.{1:X3} in package p{2:X} in APM {3}", rindex, rtype, package.packageKey, apm.Name); } } else if (flag[0] == 'l') { Console.Out.WriteLine("\t{0:X12}.{1:X3} ({2} bytes) - {3:X}", rindex, rtype, record.Size, record.Key); } } } } if (flag[0] == 't') { foreach (ulong type in (HashSet <ulong>)query[0]) { byte[] be = BitConverter.GetBytes((ushort)type); Array.Reverse(be); Console.Out.WriteLine("{2:X4} : {0:X4} : {1:X3}", (ushort)type, GUID.Type(type << 48), BitConverter.ToUInt16(be, 0)); } } }
static void Main(string[] args) { if (args.Length < 3) { Console.Out.WriteLine("Usage: PackageTool.exe [-LLang] \"overwatch_folder\" \"output_folder\" <keys...>"); Console.Out.WriteLine("Keys must start with 'i' for content keys, 'p' for package keys, 'n' for package indexes, 't' for package indexes + indices"); Console.Out.WriteLine("Keys must start with 'a' for specific APMs"); Console.Out.WriteLine("If any key starts with Q it will dump all files."); Console.Out.WriteLine("If any key starts with D it will only output filenames to console, but not write files"); return; } string root = args[0]; OwRootHandler.LOAD_PACKAGES = true; CASCConfig config = null; // ngdp:us:pro // http:us:pro:us.patch.battle.net:1119 if (root.ToLowerInvariant().Substring(0, 5) == "ngdp:") { string cdn = root.Substring(5, 4); string[] parts = root.Substring(5).Split(':'); string region = "us"; string product = "pro"; if (parts.Length > 1) { region = parts[1]; } if (parts.Length > 2) { product = parts[2]; } if (cdn == "bnet") { config = CASCConfig.LoadOnlineStorageConfig(product, region); } else { if (cdn == "http") { string host = string.Join(":", parts.Skip(3)); config = CASCConfig.LoadOnlineStorageConfig(host, product, region, true, true, true); } } } else { config = CASCConfig.LoadLocalStorageConfig(root, true, true); } if (args[0][0] == '-' && args[0][1] == 'L') { string lang = args[0].Substring(2); config.Languages = new HashSet <string>(new string[1] { lang }); args = args.Skip(1).ToArray(); } string output = args[1] + Path.DirectorySeparatorChar; Console.Out.WriteLine("{0} v{1}", Assembly.GetExecutingAssembly().GetName().Name, OWLib.Util.GetVersion()); HashSet <ulong> packageKeys = new HashSet <ulong>(); HashSet <ulong> packageIndices = new HashSet <ulong>(); HashSet <ulong> packageIndent = new HashSet <ulong>(); HashSet <string> contentKeys = new HashSet <string>(); HashSet <ulong> dumped = new HashSet <ulong>(); HashSet <ulong> fileKeys = new HashSet <ulong>(); HashSet <ulong> types = new HashSet <ulong>(); string apmName = null; bool dumpAll = false; bool dry = false; for (int i = 2; i < args.Length; ++i) { string arg = args[i]; switch (arg[0]) { case 'q': case 'Q': dumpAll = true; break; case 'd': case 'D': dry = true; break; case 'p': packageKeys.Add(ulong.Parse(arg.Substring(1), NumberStyles.HexNumber)); break; case 'P': packageKeys.Add(ulong.Parse(arg.Substring(1), NumberStyles.Number)); break; case 'n': packageIndices.Add(ulong.Parse(arg.Substring(1), NumberStyles.HexNumber)); break; case 'N': packageIndices.Add(ulong.Parse(arg.Substring(1), NumberStyles.Number)); break; case 't': packageIndent.Add(ulong.Parse(arg.Substring(1), NumberStyles.HexNumber)); break; case 'T': packageIndent.Add(ulong.Parse(arg.Substring(1), NumberStyles.Number)); break; case 'a': case 'A': apmName = arg.Substring(1).ToLowerInvariant(); break; case 'i': case 'I': contentKeys.Add(arg.Substring(1).ToUpperInvariant()); break; case 'f': fileKeys.Add(ulong.Parse(arg.Substring(1), NumberStyles.HexNumber)); break; case 'F': fileKeys.Add(ulong.Parse(arg.Substring(1), NumberStyles.Number)); break; case 'M': types.Add(ulong.Parse(arg.Substring(1), NumberStyles.HexNumber)); break; } } if (contentKeys.Count + packageKeys.Count + packageIndices.Count + packageIndent.Count + fileKeys.Count + types.Count == 0 && !dumpAll) { Console.Error.WriteLine("Must have at least 1 query"); return; } CASCHandler handler = CASCHandler.OpenStorage(config); OwRootHandler ow = handler.Root as OwRootHandler; if (ow == null) { Console.Error.WriteLine("Not a valid Overwatch installation"); return; } Console.Out.WriteLine("Extracting..."); HashSet <ulong> indicesExtracted = new HashSet <ulong>(); HashSet <ulong> CMFExtracted = new HashSet <ulong>(); foreach (APMFile apm in ow.APMFiles) { if (apmName != null && !Path.GetFileName(apm.Name).ToLowerInvariant().Contains(apmName)) { continue; } Console.Out.WriteLine("Iterating {0}", Path.GetFileName(apm.Name)); HashSet <ulong> removed = new HashSet <ulong>(); foreach (ulong key in fileKeys) { if (apm.CMFMap.ContainsKey(key)) { ulong rtype = GUID.Type(key); ulong rindex = GUID.LongKey(key); string ofn = $"{output}{Path.DirectorySeparatorChar}cmf{Path.DirectorySeparatorChar}{rtype:X3}{Path.DirectorySeparatorChar}"; if (!dry && !Directory.Exists(ofn)) { Console.Out.WriteLine("Created directory {0}", ofn); Directory.CreateDirectory(ofn); } ofn = $"{ofn}{rindex:X12}.{rtype:X3}"; if (!dry) { using (Stream outputStream = File.Open(ofn, FileMode.Create, FileAccess.Write)) { EncodingEntry recordEncoding; if (!handler.Encoding.GetEntry(apm.CMFMap[key].HashKey, out recordEncoding)) { Console.Error.WriteLine("Cannot open file {0} -- malformed CMF?", ofn); continue; } try { using (Stream recordStream = handler.OpenFile(recordEncoding.Key)) { CopyBytes(recordStream, outputStream, recordEncoding.Size); } Console.Out.WriteLine("Saved file {0}", ofn); removed.Add(key); } catch { Console.Error.WriteLine("Cannot open file {0} -- encryption", ofn); } } } } } foreach (ulong key in removed) { fileKeys.Remove(key); } if (types.Count > 0 || dumpAll) { foreach (ulong key in apm.CMFMap.Keys) { if (types.Contains(GUID.Type(key)) || dumpAll) { ulong rtype = GUID.Type(key); ulong rindex = GUID.LongKey(key); string ofn = $"{output}{Path.DirectorySeparatorChar}cmf{Path.DirectorySeparatorChar}{rtype:X3}{Path.DirectorySeparatorChar}"; if (!dry && !Directory.Exists(ofn)) { Console.Out.WriteLine("Created directory {0}", ofn); Directory.CreateDirectory(ofn); } ofn = $"{ofn}{rindex:X12}.{rtype:X3}"; if (!dry) { using (Stream outputStream = File.Open(ofn, FileMode.Create, FileAccess.Write)) { EncodingEntry recordEncoding; if (!handler.Encoding.GetEntry(apm.CMFMap[key].HashKey, out recordEncoding)) { Console.Error.WriteLine("Cannot open file {0} -- malformed CMF?", ofn); continue; } try { using (Stream recordStream = handler.OpenFile(recordEncoding.Key)) { CopyBytes(recordStream, outputStream, recordEncoding.Size); } Console.Out.WriteLine("Saved file {0}", ofn); } catch { Console.Error.WriteLine("Cannot open file {0} -- encryption", ofn); } } } } } } if (dumpAll) { continue; } if (contentKeys.Count + packageKeys.Count + packageIndices.Count + packageIndent.Count + fileKeys.Count > 0) { for (long i = 0; i < apm.Packages.LongLength; ++i) { if (contentKeys.Count + packageKeys.Count + packageIndices.Count + packageIndent.Count + fileKeys.Count == 0) { break; } APMPackage package = apm.Packages[i]; if (!dumpAll) { bool ret = true; if (packageKeys.Count > 0 && packageKeys.Contains(package.packageKey)) { ret = false; } if (ret && contentKeys.Count > 0 && contentKeys.Contains(package.indexContentKey.ToHexString().ToUpperInvariant())) { ret = false; } if (ret && packageIndices.Count > 0 && packageIndices.Contains(GUID.Index(package.packageKey)) && !indicesExtracted.Contains(package.packageKey)) { ret = false; } if (ret && packageIndent.Count > 0 && packageIndent.Contains(GUID.LongKey(package.packageKey))) { ret = false; } if (ret) { continue; } } packageKeys.Remove(package.packageKey); indicesExtracted.Add(package.packageKey); packageIndent.Remove(GUID.LongKey(package.packageKey)); contentKeys.Remove(package.indexContentKey.ToHexString().ToUpperInvariant()); PackageIndex index = apm.Indexes[i]; PackageIndexRecord[] records = apm.Records[i]; string o = null; if (dumpAll) { o = output; } else { o = $"{output}{GUID.LongKey(package.packageKey):X12}{Path.DirectorySeparatorChar}"; } EncodingEntry bundleEncoding; bool allowBundle = handler.Encoding.GetEntry(index.bundleContentKey, out bundleEncoding); Stream bundleStream = null; if (allowBundle) { try { bundleStream = handler.OpenFile(bundleEncoding.Key); } catch { Console.Error.WriteLine("Cannot open bundle {0:X16} -- encryption", index.bundleKey); continue; } } foreach (PackageIndexRecord record in records) { if (dumpAll && !dumped.Add(record.Key)) { continue; } ulong rtype = GUID.Type(record.Key); ulong rindex = GUID.LongKey(record.Key); string ofn = $"{o}{rtype:X3}{Path.DirectorySeparatorChar}"; if (!dry && !Directory.Exists(ofn)) { Console.Out.WriteLine("Created directory {0}", ofn); Directory.CreateDirectory(ofn); } ofn = $"{ofn}{rindex:X12}.{rtype:X3}"; if (!dry) { using (Stream outputStream = File.Open(ofn, FileMode.Create, FileAccess.Write)) { if (((ContentFlags)record.Flags & ContentFlags.Bundle) == ContentFlags.Bundle) { if (allowBundle) { bundleStream.Position = record.Offset; CopyBytes(bundleStream, outputStream, record.Size); } else { Console.Error.WriteLine("Cannot open file {0} -- can't open bundle", ofn); continue; } } else { EncodingEntry recordEncoding; if (!handler.Encoding.GetEntry(record.ContentKey, out recordEncoding)) { Console.Error.WriteLine("Cannot open file {0} -- doesn't have bundle flags", ofn); continue; } try { using (Stream recordStream = handler.OpenFile(recordEncoding.Key)) { CopyBytes(recordStream, outputStream, record.Size); } } catch { Console.Error.WriteLine("Cannot open file {0} -- encryption", ofn); } } } } Console.Out.WriteLine("Saved file {0}", ofn); } if (allowBundle) { bundleStream.Dispose(); } } } } }
static void Main(string[] args) { Console.OutputEncoding = Encoding.UTF8; Files = new Dictionary <ulong, PackageRecord>(); TrackedFiles = new Dictionary <ushort, HashSet <ulong> >(); Flags = FlagParser.Parse <ToolFlags>(); if (Flags == null) { return; } #region Initialize CASC Log("{0} v{1}", Assembly.GetExecutingAssembly().GetName().Name, Util.GetVersion()); Log("Initializing CASC..."); Log("Set language to {0}", Flags.Language); CDNIndexHandler.Cache.Enabled = Flags.UseCache; CDNIndexHandler.Cache.CacheData = Flags.CacheData; CDNIndexHandler.Cache.Validate = Flags.ValidateCache; // ngdp:us:pro // http:us:pro:us.patch.battle.net:1119 if (Flags.OverwatchDirectory.ToLowerInvariant().Substring(0, 5) == "ngdp:") { string cdn = Flags.OverwatchDirectory.Substring(5, 4); string[] parts = Flags.OverwatchDirectory.Substring(5).Split(':'); string region = "us"; string product = "pro"; if (parts.Length > 1) { region = parts[1]; } if (parts.Length > 2) { product = parts[2]; } if (cdn == "bnet") { Config = CASCConfig.LoadOnlineStorageConfig(product, region); } else { if (cdn == "http") { string host = string.Join(":", parts.Skip(3)); Config = CASCConfig.LoadOnlineStorageConfig(host, product, region, true, true, true); } } } else { Config = CASCConfig.LoadLocalStorageConfig(Flags.OverwatchDirectory, !Flags.SkipKeys, false); } Config.Languages = new HashSet <string>(new[] { Flags.Language }); #endregion BuildVersion = uint.Parse(Config.BuildName.Split('.').Last()); if (Flags.SkipKeys) { Log("Disabling Key auto-detection..."); } Log("Using Overwatch Version {0}", Config.BuildName); CASC = CASCHandler.OpenStorage(Config); Root = CASC.Root as OwRootHandler; if (Root == null) { ErrorLog("Not a valid overwatch installation"); return; } if (!Root.APMFiles.Any()) { ErrorLog("Could not find the files for language {0}. Please confirm that you have that language installed, and are using the names from the target language.", Flags.Language); if (!Flags.GracefulExit) { return; } } string[] modeArgs = Flags.Positionals.Skip(2).ToArray(); switch (Flags.Mode.ToLower()) { case "extract": Extract(modeArgs); break; case "search": Search(modeArgs); break; case "search-type": SearchType(modeArgs); break; case "info": Info(modeArgs); break; case "convert": Convert(modeArgs); break; case "types": Types(modeArgs); break; default: Console.Out.WriteLine("Available modes: extract, search, search-type, info"); break; } }