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); } } }); }