public static CDNConfigFile GetCDNConfig(string url, string hash) { string content; var cdnConfig = new CDNConfigFile(); if (url.StartsWith("http")) { try { content = Encoding.UTF8.GetString(CDN.Get(url + "/config/" + hash[0] + hash[1] + "/" + hash[2] + hash[3] + "/" + hash)); } catch (Exception e) { Logger.WriteLine("Error retrieving CDN config: " + e.Message); return(cdnConfig); } } else { content = File.ReadAllText(Path.Combine(url, "config", "" + hash[0] + hash[1], "" + hash[2] + hash[3], hash)); } var cdnConfigLines = content.Split(new string[] { "\n" }, StringSplitOptions.RemoveEmptyEntries); for (var i = 0; i < cdnConfigLines.Count(); i++) { if (cdnConfigLines[i].StartsWith("#") || cdnConfigLines[i].Length == 0) { continue; } var cols = cdnConfigLines[i].Split(new string[] { " = " }, StringSplitOptions.RemoveEmptyEntries); switch (cols[0]) { case "archives": var archives = cols[1].Split(' '); cdnConfig.archives = new MD5Hash[archives.Length]; for (var j = 0; j < archives.Length; j++) { cdnConfig.archives[j] = archives[j].ToByteArray().ToMD5(); } break; case "archive-group": cdnConfig.archiveGroup = cols[1]; break; case "patch-archives": if (cols.Length > 1) { var patchArchives = cols[1].Split(' '); cdnConfig.patchArchives = new MD5Hash[patchArchives.Length]; for (var j = 0; j < patchArchives.Length; j++) { cdnConfig.patchArchives[j] = patchArchives[j].ToByteArray().ToMD5(); } } break; case "patch-archive-group": cdnConfig.patchArchiveGroup = cols[1]; break; case "builds": var builds = cols[1].Split(' '); cdnConfig.builds = builds; break; case "file-index": cdnConfig.fileIndex = cols[1]; break; case "file-index-size": cdnConfig.fileIndexSize = cols[1]; break; case "patch-file-index": cdnConfig.patchFileIndex = cols[1]; break; case "patch-file-index-size": cdnConfig.patchFileIndexSize = cols[1]; break; case "archives-index-size": case "patch-archives-index-size": break; default: Logger.WriteLine("!!!!!!!! Unknown cdnconfig variable '" + cols[0] + "'"); break; } } return(cdnConfig); }
public static BuildConfigFile GetBuildConfig(string url, string hash) { string content; var buildConfig = new BuildConfigFile(); if (url.StartsWith("http")) { try { content = Encoding.UTF8.GetString(CDN.Get(url + "/config/" + hash[0] + hash[1] + "/" + hash[2] + hash[3] + "/" + hash)); } catch (Exception e) { Console.WriteLine("Error retrieving CDN config: " + e.Message); return(buildConfig); } } else { content = File.ReadAllText(Path.Combine(url, "config", "" + hash[0] + hash[1], "" + hash[2] + hash[3], hash)); } if (string.IsNullOrEmpty(content) || !content.StartsWith("# Build")) { Console.WriteLine("Error reading build config!"); return(buildConfig); } var lines = content.Split(new string[] { "\n" }, StringSplitOptions.RemoveEmptyEntries); for (var i = 0; i < lines.Count(); i++) { if (lines[i].StartsWith("#") || lines[i].Length == 0) { continue; } var cols = lines[i].Split(new string[] { " = " }, StringSplitOptions.RemoveEmptyEntries); switch (cols[0]) { case "root": buildConfig.root = cols[1].ToByteArray().ToMD5(); break; case "download": var downloadSplit = cols[1].Split(' '); buildConfig.download = new MD5Hash[downloadSplit.Length]; buildConfig.download[0] = downloadSplit[0].ToByteArray().ToMD5(); if (downloadSplit.Length > 1) { buildConfig.download[1] = downloadSplit[1].ToByteArray().ToMD5(); } break; case "install": var installSplit = cols[1].Split(' '); buildConfig.install = new MD5Hash[installSplit.Length]; buildConfig.install[0] = installSplit[0].ToByteArray().ToMD5(); if (installSplit.Length > 1) { buildConfig.install[1] = installSplit[1].ToByteArray().ToMD5(); } break; case "encoding": var encodingSplit = cols[1].Split(' '); buildConfig.encoding = new MD5Hash[encodingSplit.Length]; buildConfig.encoding[0] = encodingSplit[0].ToByteArray().ToMD5(); if (encodingSplit.Length > 1) { buildConfig.encoding[1] = encodingSplit[1].ToByteArray().ToMD5(); } break; case "encoding-size": var encodingSize = cols[1].Split(' '); buildConfig.encodingSize = encodingSize; break; case "size": buildConfig.size = cols[1].Split(' '); break; case "size-size": buildConfig.sizeSize = cols[1].Split(' '); break; case "build-name": buildConfig.buildName = cols[1]; break; case "build-playbuild-installer": buildConfig.buildPlaybuildInstaller = cols[1]; break; case "build-product": buildConfig.buildProduct = cols[1]; break; case "build-uid": buildConfig.buildUid = cols[1]; break; case "patch": buildConfig.patch = cols[1].ToByteArray().ToMD5(); break; case "patch-size": buildConfig.patchSize = cols[1]; break; case "patch-config": buildConfig.patchConfig = cols[1].ToByteArray().ToMD5(); break; case "build-branch": // Overwatch buildConfig.buildBranch = cols[1]; break; case "build-num": // Agent case "build-number": // Overwatch case "build-version": // Catalog buildConfig.buildNumber = cols[1]; break; case "build-attributes": // Agent buildConfig.buildAttributes = cols[1]; break; case "build-comments": // D3 buildConfig.buildComments = cols[1]; break; case "build-creator": // D3 buildConfig.buildCreator = cols[1]; break; case "build-fixed-hash": // S2 buildConfig.buildFixedHash = cols[1]; break; case "build-replay-hash": // S2 buildConfig.buildReplayHash = cols[1]; break; case "build-t1-manifest-version": buildConfig.buildManifestVersion = cols[1]; break; case "install-size": buildConfig.installSize = cols[1].Split(' '); break; case "download-size": buildConfig.downloadSize = cols[1].Split(' '); break; case "build-partial-priority": case "partial-priority": buildConfig.partialPriority = cols[1]; break; case "partial-priority-size": buildConfig.partialPrioritySize = cols[1]; break; default: Logger.WriteLine("!!!!!!!! Unknown buildconfig variable '" + cols[0] + "'"); break; } } return(buildConfig); }
public static EncodingFile GetEncoding(string url, string hash, int encodingSize = 0, bool parseTableB = false, bool checkStuff = false) { var encoding = new EncodingFile(); byte[] content; if (url.Substring(0, 4) == "http") { content = CDN.Get(url + "data/" + hash[0] + hash[1] + "/" + hash[2] + hash[3] + "/" + hash); if (encodingSize != 0 && encodingSize != content.Length) { content = CDN.Get(url + "data/" + hash[0] + hash[1] + "/" + hash[2] + hash[3] + "/" + hash, true); if (encodingSize != content.Length && encodingSize != 0) { throw new Exception("File corrupt/not fully downloaded! Remove " + "data / " + hash[0] + hash[1] + " / " + hash[2] + hash[3] + " / " + hash + " from cache."); } } } else { content = File.ReadAllBytes(Path.Combine(url, "data", "" + hash[0] + hash[1], "" + hash[2] + hash[3], hash)); } using (BinaryReader bin = new BinaryReader(new MemoryStream(BLTE.Parse(content)))) { if (Encoding.UTF8.GetString(bin.ReadBytes(2)) != "EN") { throw new Exception("Error while parsing encoding file. Did BLTE header size change?"); } encoding.version = bin.ReadByte(); encoding.cKeyLength = bin.ReadByte(); encoding.eKeyLength = bin.ReadByte(); encoding.cKeyPageSize = bin.ReadUInt16(true); encoding.eKeyPageSize = bin.ReadUInt16(true); encoding.cKeyPageCount = bin.ReadUInt32(true); encoding.eKeyPageCount = bin.ReadUInt32(true); encoding.stringBlockSize = bin.ReadUInt40(true); var headerLength = bin.BaseStream.Position; if (parseTableB) { var stringBlockEntries = new List <string>(); while ((bin.BaseStream.Position - headerLength) != (long)encoding.stringBlockSize) { stringBlockEntries.Add(bin.ReadCString()); } encoding.stringBlockEntries = stringBlockEntries.ToArray(); } else { bin.BaseStream.Position += (long)encoding.stringBlockSize; } /* Table A */ if (checkStuff) { encoding.aHeaders = new EncodingHeaderEntry[encoding.cKeyPageCount]; for (int i = 0; i < encoding.cKeyPageCount; i++) { encoding.aHeaders[i].firstHash = bin.Read <MD5Hash>(); encoding.aHeaders[i].checksum = bin.Read <MD5Hash>(); } } else { bin.BaseStream.Position += encoding.cKeyPageCount * 32; } var tableAstart = bin.BaseStream.Position; Dictionary <MD5Hash, EncodingFileEntry> entries = new Dictionary <MD5Hash, EncodingFileEntry>(new MD5HashComparer()); for (int i = 0; i < encoding.cKeyPageCount; i++) { byte keysCount; while ((keysCount = bin.ReadByte()) != 0) { EncodingFileEntry entry = new EncodingFileEntry() { size = bin.ReadInt40BE() }; var cKey = bin.Read <MD5Hash>(); // @TODO add support for multiple encoding keys for (int key = 0; key < keysCount; key++) { if (key == 0) { entry.eKey = bin.Read <MD5Hash>(); } else { bin.ReadBytes(16); } } entries.Add(cKey, entry); } var remaining = 4096 - ((bin.BaseStream.Position - tableAstart) % 4096); if (remaining > 0) { bin.BaseStream.Position += remaining; } } encoding.aEntries = entries; if (!parseTableB) { return(encoding); } /* Table B */ if (checkStuff) { encoding.bHeaders = new EncodingHeaderEntry[encoding.eKeyPageCount]; for (int i = 0; i < encoding.eKeyPageCount; i++) { encoding.bHeaders[i].firstHash = bin.Read <MD5Hash>(); encoding.bHeaders[i].checksum = bin.Read <MD5Hash>(); } } else { bin.BaseStream.Position += encoding.eKeyPageCount * 32; } var tableBstart = bin.BaseStream.Position; List <EncodingFileDescEntry> b_entries = new List <EncodingFileDescEntry>(); while (bin.BaseStream.Position < tableBstart + 4096 * encoding.eKeyPageCount) { var remaining = 4096 - (bin.BaseStream.Position - tableBstart) % 4096; if (remaining < 25) { bin.BaseStream.Position += remaining; continue; } EncodingFileDescEntry entry = new EncodingFileDescEntry() { key = bin.Read <MD5Hash>(), stringIndex = bin.ReadUInt32(true), compressedSize = bin.ReadUInt40(true) }; if (entry.stringIndex == uint.MaxValue) { break; } b_entries.Add(entry); } encoding.bEntries = b_entries.ToArray(); } return(encoding); }
public static void GetIndexes(string url, MD5Hash[] archives) { Parallel.ForEach(archives, (archive, state, i) => { uint indexID; string indexName = archive.ToHexString().ToLower(); try { cacheLock.EnterUpgradeableReadLock(); if (!CASC.indexNames.Contains(archives[i], new MD5HashComparer())) { try { cacheLock.EnterWriteLock(); CASC.indexNames.Add(archive); indexID = (uint)CASC.indexNames.Count - 1; } finally { cacheLock.ExitWriteLock(); } } else { return; } } finally { cacheLock.ExitUpgradeableReadLock(); } byte[] indexContent; if (url.StartsWith("http")) { indexContent = CDN.Get(url + "data/" + indexName[0] + indexName[1] + "/" + indexName[2] + indexName[3] + "/" + indexName + ".index"); } else { indexContent = File.ReadAllBytes(Path.Combine(url, "data", "" + indexName[0] + indexName[1], "" + indexName[2] + indexName[3], indexName + ".index")); } using (BinaryReader bin = new BinaryReader(new MemoryStream(indexContent))) { bin.BaseStream.Position = bin.BaseStream.Length - 12; var entryCount = bin.ReadUInt32(); bin.BaseStream.Position = 0; int indexEntries = indexContent.Length / 4096; var entriesRead = 0; for (var b = 0; b < indexEntries; b++) { for (var bi = 0; bi < 170; bi++) { var headerHash = bin.Read <MD5Hash>(); var entry = new IndexEntry() { indexID = indexID, size = bin.ReadUInt32(true), offset = bin.ReadUInt32(true) }; entriesRead++; cacheLock.EnterUpgradeableReadLock(); try { if (!CASC.indexDictionary.ContainsKey(headerHash)) { cacheLock.EnterWriteLock(); try { CASC.indexDictionary.Add(headerHash, entry); } finally { cacheLock.ExitWriteLock(); } } } finally { cacheLock.ExitUpgradeableReadLock(); } } if (entriesRead == entryCount) { return; } // 16 bytes padding that rounds the chunk to 4096 bytes (index entry is 24 bytes, 24 * 170 = 4080 bytes so 16 bytes remain) bin.ReadBytes(16); } } }); }
public static RootFile GetRoot(string url, string hash, bool parseIt = false) { var root = new RootFile { entriesLookup = new MultiDictionary <ulong, RootEntry>(), entriesFDID = new MultiDictionary <uint, RootEntry>(), }; byte[] content; if (url.StartsWith("http:")) { content = CDN.Get(url + "data/" + hash[0] + hash[1] + "/" + hash[2] + hash[3] + "/" + hash); } else { content = File.ReadAllBytes(Path.Combine(url, "data", "" + hash[0] + hash[1], "" + hash[2] + hash[3], hash)); } if (!parseIt) { return(root); } var hasher = new Jenkins96(); var namedCount = 0; var unnamedCount = 0; uint totalFiles = 0; uint namedFiles = 0; var newRoot = false; using (BinaryReader bin = new BinaryReader(new MemoryStream(BLTE.Parse(content)))) { var header = bin.ReadUInt32(); if (header == 1296454484) { totalFiles = bin.ReadUInt32(); namedFiles = bin.ReadUInt32(); newRoot = true; } else { bin.BaseStream.Position = 0; } while (bin.BaseStream.Position < bin.BaseStream.Length) { var count = bin.ReadUInt32(); var contentFlags = (ContentFlags)bin.ReadUInt32(); var localeFlags = (LocaleFlags)bin.ReadUInt32(); var entries = new RootEntry[count]; var filedataIds = new int[count]; var fileDataIndex = 0; for (var i = 0; i < count; ++i) { entries[i].localeFlags = localeFlags; entries[i].contentFlags = contentFlags; filedataIds[i] = fileDataIndex + bin.ReadInt32(); entries[i].fileDataID = (uint)filedataIds[i]; fileDataIndex = filedataIds[i] + 1; } if (!newRoot) { for (var i = 0; i < count; ++i) { entries[i].md5 = bin.Read <MD5Hash>(); entries[i].lookup = bin.ReadUInt64(); root.entriesLookup.Add(entries[i].lookup, entries[i]); root.entriesFDID.Add(entries[i].fileDataID, entries[i]); } } else { for (var i = 0; i < count; ++i) { entries[i].md5 = bin.Read <MD5Hash>(); } for (var i = 0; i < count; ++i) { if (contentFlags.HasFlag(ContentFlags.NoNames)) { //entries[i].lookup = hasher.ComputeHash("BY_FDID_" + entries[i].fileDataID); entries[i].lookup = 0; unnamedCount++; } else { entries[i].lookup = bin.ReadUInt64(); namedCount++; root.entriesLookup.Add(entries[i].lookup, entries[i]); } root.entriesFDID.Add(entries[i].fileDataID, entries[i]); } } } } return(root); }