Example #1
0
        public static void MapCMF(OwRootHandler ow, CASCHandler handler, Dictionary <ulong, PackageRecord> map, Dictionary <ushort, List <ulong> > track, string language)
        {
            if (ow == null || handler == null)
            {
                return;
            }

            foreach (ApplicationPackageManifest apm in ow.APMFiles)
            {
                if (!apm.Name.ToLowerInvariant().Contains("rdev"))
                {
                    continue;
                }
                if (language != null && !apm.Name.ToLowerInvariant().Contains("l" + language.ToLowerInvariant()))
                {
                    continue;
                }
                foreach (KeyValuePair <ulong, PackageRecord> pair in apm.FirstOccurence)
                {
                    ushort id = GUID.Type(pair.Key);
                    if (track != null && track.ContainsKey(id))
                    {
                        track[id].Add(pair.Value.GUID);
                    }

                    if (map.ContainsKey(pair.Key))
                    {
                        continue;
                    }

                    map[pair.Key] = pair.Value;
                }
            }
        }
Example #2
0
        public static void MapCMF(OwRootHandler ow, CASCHandler handler, Dictionary <ulong, Record> map,
                                  Dictionary <ushort, List <ulong> > track, string language)
        {
            if (ow == null || handler == null)
            {
                return;
            }

            foreach (APMFile apm in ow.APMFiles)
            {
                if (!apm.Name.ToLowerInvariant().Contains("rdev"))
                {
                    continue;
                }
                if (language != null && !apm.Name.ToLowerInvariant().Contains("l" + language.ToLowerInvariant()))
                {
                    continue;
                }
                foreach (KeyValuePair <ulong, CMFHashData> pair in apm.CMFMap)
                {
                    ushort id = GUID.Type(pair.Key);
                    if (track != null && track.ContainsKey(id))
                    {
                        track[id].Add(pair.Value.id);
                    }

                    if (map.ContainsKey(pair.Key))
                    {
                        continue;
                    }
                    Record rec = new Record {
                        record = new PackageIndexRecord()
                    };
                    rec.record.Flags      = 0;
                    rec.record.Key        = pair.Value.id;
                    rec.record.ContentKey = pair.Value.HashKey;
                    rec.package           = new APMPackage(new APMPackageItem());
                    rec.index             = new PackageIndex(new PackageIndexItem());

                    EncodingEntry enc;
                    if (handler.Encoding.GetEntry(pair.Value.HashKey, out enc))
                    {
                        rec.record.Size = enc.Size;
                        map.Add(pair.Key, rec);
                    }
                }
            }
        }
Example #3
0
        public Highlight(Stream stream, CASCHandler handler, Dictionary <ulong, Record> records = null)
        {
            if (records != null)
            {
                this.records = records;
            }

            this.handler = handler;
            root         = handler?.Root as OwRootHandler;
            Util.MapCMF(root, handler, records, null, null);

            using (BinaryReader reader = new BinaryReader(stream)) {
                header = reader.Read <HighlightHeader>();
                if (header.Magic != MAGIC_CONSTANT)
                {
                    throw new InvalidDataException("Data stream is not a highlight!");
                }
                MemoryStream replayData = new MemoryStream(header.ReplayLength);
                stream.CopyBytes(replayData, header.ReplayLength);
                embeddedReplay = new Replay(replayData, handler, records);
            }
        }
Example #4
0
        public Replay(Stream stream, CASCHandler handler, Dictionary <ulong, Record> records = null)
        {
            if (records != null)
            {
                this.records = records;
            }

            this.handler = handler;
            root         = handler?.Root as OwRootHandler;
            Util.MapCMF(root, handler, records, null, null);

            using (BinaryReader reader = new BinaryReader(stream)) {
                header = reader.Read <ReplayHeader>();
                if (header.Magic != MAGIC_CONSTANT)
                {
                    throw new InvalidDataException("Data stream is not a replay!");
                }
                using (Decompressor decompressor = new Decompressor()) {
                    byte[] temp = reader.ReadBytes((int)(stream.Length - stream.Position));
                    byte[] dec  = decompressor.Unwrap(temp);
                    decompressedStream = new MemoryStream(dec);
                }
            }
        }
Example #5
0
        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();
            }
        }
Example #6
0
        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();
            }
        }
Example #7
0
        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();
            }
        }
Example #8
0
        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));
                }
            }
        }
Example #9
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();
                        }
                    }
                }
            }
        }