public static void CopyToStream(this Stream src, Stream dst, long len, BackgroundWorkerEx progressReporter = null) { long done = 0; #if NET5_0_OR_GREATER Span <byte> buf = stackalloc byte[0x1000]; #else byte[] buf = new byte[0x1000]; #endif int count; do { if (progressReporter != null && progressReporter.CancellationPending) { return; } #if NET5_0_OR_GREATER count = src.Read(buf); dst.Write(buf.Slice(0, count)); #else count = src.Read(buf, 0, buf.Length); dst.Write(buf, 0, count); #endif done += count; progressReporter?.ReportProgress((int)(done / (float)len * 100)); } while (count > 0); }
private bool LoadPreHashedListFile(string pathbin, string pathtext, BackgroundWorkerEx worker = null) { using (var _ = new PerfCounter("WowRootHandler::LoadPreHashedListFile()")) { worker?.ReportProgress(0, "Loading \"listfile\"..."); if (!File.Exists(pathbin)) { return(false); } var timebin = File.GetLastWriteTime(pathbin); var timetext = File.GetLastWriteTime(pathtext); if (timebin != timetext) // text has been modified, recreate crehashed file { return(false); } Logger.WriteLine("WowRootHandler: loading file names..."); using (var fs = new FileStream(pathbin, FileMode.Open)) using (var br = new BinaryReader(fs)) { int numFolders = br.ReadInt32(); for (int i = 0; i < numFolders; i++) { string dirName = br.ReadString(); Logger.WriteLine(dirName); int numFiles = br.ReadInt32(); for (int j = 0; j < numFiles; j++) { ulong fileHash = br.ReadUInt64(); string fileName = br.ReadString(); string fileNameFull = dirName != String.Empty ? dirName + "\\" + fileName : fileName; // skip invalid names if (!RootData.ContainsKey(fileHash)) { Logger.WriteLine("Invalid file name: {0}", fileNameFull); continue; } CASCFile.Files[fileHash] = new CASCFile(fileHash, fileNameFull); } worker?.ReportProgress((int)(fs.Position / (float)fs.Length * 100)); } Logger.WriteLine("WowRootHandler: loaded {0} valid file names", CASCFile.Files.Count); } } return(true); }
private static CASCHandlerLite Open(LocaleFlags locale, BackgroundWorkerEx worker, CASCConfig config) { using (var _ = new PerfCounter("new CASCHandlerLite()")) { return(new CASCHandlerLite(config, locale, worker)); } }
public CASCHandlerBase(CASCConfig config, BackgroundWorkerEx worker) { Config = config; Logger.WriteLine("CASCHandlerBase: loading CDN indices..."); using (var _ = new PerfCounter("CDNIndexHandler.Initialize()")) { CDNIndex = CDNIndexHandler.Initialize(config, worker); } Logger.WriteLine("CASCHandlerBase: loaded {0} CDN indexes", CDNIndex.Count); if (!config.OnlineMode) { CDNCache.Enabled = false; Logger.WriteLine("CASCHandlerBase: loading local indices..."); using (var _ = new PerfCounter("LocalIndexHandler.Initialize()")) { LocalIndex = LocalIndexHandler.Initialize(config, worker); } Logger.WriteLine("CASCHandlerBase: loaded {0} local indexes", LocalIndex.Count); } }
private CASCHandlerLite(CASCConfig config, LocaleFlags locale, BackgroundWorkerEx worker) : base(config, worker) { if (config.GameType != CASCGameType.WoW) { throw new Exception("Unsupported game " + config.BuildUID); } Logger.WriteLine("CASCHandlerLite: loading encoding data..."); EncodingHandler EncodingHandler; using (var _ = new PerfCounter("new EncodingHandler()")) { using (var fs = OpenEncodingFile(this)) EncodingHandler = new EncodingHandler(fs, worker); } Logger.WriteLine("CASCHandlerLite: loaded {0} encoding data", EncodingHandler.Count); Logger.WriteLine("CASCHandlerLite: loading root data..."); WowRootHandler RootHandler; using (var _ = new PerfCounter("new RootHandler()")) { using (var fs = OpenRootFile(EncodingHandler, this)) RootHandler = new WowRootHandler(fs, worker); } Logger.WriteLine("CASCHandlerLite: loaded {0} root data", RootHandler.Count); RootHandler.SetFlags(locale, false, false); RootEntry rootEntry; foreach (var entry in RootHandler.GetAllEntries()) { rootEntry = entry.Value; if ((rootEntry.LocaleFlags == locale || (rootEntry.LocaleFlags & locale) != LocaleFlags.None) && (rootEntry.ContentFlags & ContentFlags.Alternate) == ContentFlags.None) { if (EncodingHandler.GetEntry(rootEntry.cKey, out EncodingEntry enc)) { if (!HashToEKey.ContainsKey(entry.Key)) { HashToEKey.Add(entry.Key, enc.Keys[0]); FileDataIdToHash.Add(RootHandler.GetFileDataIdByHash(entry.Key), entry.Key); } } } } RootHandler.Clear(); RootHandler = null; EncodingHandler.Clear(); EncodingHandler = null; GC.Collect(); Logger.WriteLine("CASCHandlerLite: loaded {0} files", HashToEKey.Count); }
private static CASCHandler Open(BackgroundWorkerEx worker, CASCConfig config) { using (var _ = new PerfCounter("new CASCHandler()")) { return(new CASCHandler(config, worker)); } }
public static LocalIndexHandler Initialize(CASCConfig config, BackgroundWorkerEx worker) { var handler = new LocalIndexHandler(); var idxFiles = GetIdxFiles(config); if (idxFiles.Count == 0) { throw new FileNotFoundException("idx files are missing!"); } worker?.ReportProgress(0, "Loading \"local indexes\"..."); int idxIndex = 0; foreach (var idx in idxFiles) { handler.ParseIndex(idx); worker?.ReportProgress((int)(++idxIndex / (float)idxFiles.Count * 100)); } Logger.WriteLine("LocalIndexHandler: loaded {0} indexes", handler.Count); return(handler); }
// copies whole stream public static Stream CopyToMemoryStream(this Stream src, long length, BackgroundWorkerEx worker = null) { MemoryStream ms = new MemoryStream(); src.CopyToStream(ms, length, worker); ms.Position = 0; return(ms); }
public void Clear() { CDNIndexData.Clear(); CDNIndexData = null; config = null; worker = null; }
public DummyRootHandler(BinaryReader stream, BackgroundWorkerEx worker) { worker?.ReportProgress(0, "Loading \"root\"..."); // root file is executable, skip worker?.ReportProgress(100); }
public AgentRootHandler(BinaryReader stream, BackgroundWorkerEx worker) { worker?.ReportProgress(0, "Loading \"root\"..."); string hash = Encoding.ASCII.GetString(stream.ReadBytes((int)stream.BaseStream.Length)); // what is this for? worker?.ReportProgress(100); }
public HSRootHandler(BinaryReader stream, BackgroundWorkerEx worker) { worker?.ReportProgress(0, "Loading \"root\"..."); // Hearthstone root file happened to be game executable! Just ignore it. worker?.ReportProgress(100); }
public Destiny2RootHandler(BinaryReader stream, BackgroundWorkerEx worker) { worker?.ReportProgress(0, "Loading \"root\"..."); // root file for Destiny 2 is game executable... worker?.ReportProgress(100); }
public Wc3RootHandler(BinaryReader stream, BackgroundWorkerEx worker) { worker?.ReportProgress(0, "Loading \"root\"..."); using (StreamReader sr = new StreamReader(stream.BaseStream)) { string line; while ((line = sr.ReadLine()) != null) { string[] tokens = line.Split('|'); if (tokens.Length != 3 && tokens.Length != 4) { throw new InvalidDataException("tokens.Length != 3 && tokens.Length != 4"); } string file; if (tokens[0].IndexOf(':') != -1) { string[] tokens2 = tokens[0].Split(':'); if (tokens2.Length == 2 || tokens2.Length == 3 || tokens2.Length == 4) { file = Path.Combine(tokens2); } else { throw new InvalidDataException("tokens2.Length"); } } else { file = tokens[0]; } if (!Enum.TryParse(tokens[2], out LocaleFlags locale)) { locale = LocaleFlags.All; } ulong fileHash = Hasher.ComputeHash(file); RootData[fileHash] = new RootEntry() { LocaleFlags = locale, ContentFlags = ContentFlags.None, cKey = tokens[1].FromHexString().ToMD5() }; CASCFile.Files[fileHash] = new CASCFile(fileHash, file); } } worker?.ReportProgress(100); }
public OwRootHandler(BinaryReader stream, BackgroundWorkerEx worker, CASCHandler casc) { worker?.ReportProgress(0, "Loading \"root\"..."); string str = Encoding.ASCII.GetString(stream.ReadBytes((int)stream.BaseStream.Length)); string[] array = str.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); List <string> APMNames = new List <string>(); for (int i = 1; i < array.Length; i++) { string[] filedata = array[i].Split('|'); string name = filedata[4]; if (Path.GetExtension(name) == ".apm" && name.Contains("RDEV")) { APMNames.Add(Path.GetFileNameWithoutExtension(name)); if (!name.Contains("L" + LanguageScan)) { continue; } // add apm file for dev purposes ulong apmNameHash = Hasher.ComputeHash(name); MD5Hash apmMD5 = filedata[0].ToByteArray().ToMD5(); _rootData[apmNameHash] = new OWRootEntry() { baseEntry = new RootEntry() { MD5 = apmMD5, LocaleFlags = LocaleFlags.All, ContentFlags = ContentFlags.None } }; CASCFile.Files[apmNameHash] = new CASCFile(apmNameHash, name); if (!casc.Encoding.GetEntry(apmMD5, out EncodingEntry apmEnc)) { continue; } using (Stream apmStream = casc.OpenFile(apmEnc.Key)) { apmFiles.Add(new APMFile(name, apmStream, casc)); } } worker?.ReportProgress((int)(i / (array.Length / 100f))); } APMList = APMNames.ToArray(); APMNames.Clear(); }
public DownloadHandler(BinaryReader stream, BackgroundWorkerEx worker) { worker?.ReportProgress(0, "Loading \"download\"..."); stream.Skip(2); // DL byte b1 = stream.ReadByte(); byte b2 = stream.ReadByte(); byte b3 = stream.ReadByte(); int numFiles = stream.ReadInt32BE(); short numTags = stream.ReadInt16BE(); int numMaskBytes = (numFiles + 7) / 8; for (int i = 0; i < numFiles; i++) { MD5Hash key = stream.Read <MD5Hash>(); //byte[] unk = stream.ReadBytes(0xA); stream.Skip(0xA); //var entry = new DownloadEntry() { Index = i, Unk = unk }; var entry = new DownloadEntry() { Index = i }; DownloadData.Add(key, entry); worker?.ReportProgress((int)((i + 1) / (float)numFiles * 100)); } for (int i = 0; i < numTags; i++) { DownloadTag tag = new DownloadTag(); string name = stream.ReadCString(); tag.Type = stream.ReadInt16BE(); byte[] bits = stream.ReadBytes(numMaskBytes); for (int j = 0; j < numMaskBytes; j++) { bits[j] = (byte)((bits[j] * 0x0202020202 & 0x010884422010) % 1023); } tag.Bits = new BitArray(bits); Tags.Add(name, tag); } }
public InstallHandler(BinaryReader stream, BackgroundWorkerEx worker) { worker?.ReportProgress(0, "Loading \"install\"..."); stream.ReadBytes(2); // IN byte b1 = stream.ReadByte(); byte b2 = stream.ReadByte(); short numTags = stream.ReadInt16BE(); int numFiles = stream.ReadInt32BE(); int numMaskBytes = (numFiles + 7) / 8; List <InstallTag> Tags = new List <InstallTag>(); for (int i = 0; i < numTags; i++) { InstallTag tag = new InstallTag() { Name = stream.ReadCString(), Type = stream.ReadInt16BE() }; byte[] bits = stream.ReadBytes(numMaskBytes); for (int j = 0; j < numMaskBytes; j++) { bits[j] = (byte)((bits[j] * 0x0202020202 & 0x010884422010) % 1023); } tag.Bits = new BitArray(bits); Tags.Add(tag); } for (int i = 0; i < numFiles; i++) { InstallEntry entry = new InstallEntry() { Name = stream.ReadCString(), MD5 = stream.Read <MD5Hash>(), Size = stream.ReadInt32BE() }; InstallData.Add(entry); entry.Tags = Tags.FindAll(tag => tag.Bits[i]); worker?.ReportProgress((int)((i + 1) / (float)numFiles * 100)); } }
public S1RootHandler(BinaryReader stream, BackgroundWorkerEx worker) { worker?.ReportProgress(0, "Loading \"root\"..."); using (StreamReader sr = new StreamReader(stream.BaseStream)) { string line; while ((line = sr.ReadLine()) != null) { string[] tokens = line.Split('|'); string file; LocaleFlags locale = LocaleFlags.All; if (tokens[0].IndexOf(':') != -1) { string[] tokens2 = tokens[0].Split(':'); file = tokens2[0]; locale = (LocaleFlags)Enum.Parse(typeof(LocaleFlags), tokens2[1]); } else { file = tokens[0]; } ulong fileHash = Hasher.ComputeHash(file); RootData[fileHash] = new RootEntry() { LocaleFlags = locale, ContentFlags = ContentFlags.None, MD5 = tokens[1].ToByteArray().ToMD5() }; CASCFile.Files[fileHash] = new CASCFile(fileHash, file); } } worker?.ReportProgress(100); }
public override void LoadListFile(string path, BackgroundWorkerEx worker = null) { worker?.ReportProgress(0, "Loading \"listfile\"..."); Logger.WriteLine("D3RootHandler: loading file names..."); int numFiles = D3RootData.Sum(p => p.Value.Count); int i = 0; foreach (var kv in D3RootData) { foreach (var e in kv.Value) { AddFile(kv.Key, e); worker?.ReportProgress((int)(++i / (float)numFiles * 100)); } } Logger.WriteLine("D3RootHandler: loaded {0} file names", i); }
public static void CopyToStream(this Stream src, Stream dst, long len, BackgroundWorkerEx progressReporter = null) { long done = 0; // TODO: Span<byte>+stackalloc byte[] buf = new byte[0x4000]; int count; do { if (progressReporter != null && progressReporter.CancellationPending) { return; } count = src.Read(buf, 0, buf.Length); dst.Write(buf, 0, count); done += count; progressReporter?.ReportProgress((int)(done / (float)len * 100)); } while (count > 0); }
public static CDNIndexHandler Initialize(CASCConfig config, BackgroundWorkerEx worker) { var handler = new CDNIndexHandler(config, worker); worker?.ReportProgress(0, "Loading \"CDN indexes\"..."); for (int i = 0; i < config.Archives.Count; i++) { string archive = config.Archives[i]; if (config.OnlineMode) { handler.DownloadIndexFile(archive, i); } else { handler.OpenIndexFile(archive, i); } worker?.ReportProgress((int)((i + 1) / (float)config.Archives.Count * 100)); } return(handler); }
public override void LoadListFile(string path, BackgroundWorkerEx worker = null) { }
public static CASCHandler OpenStorage(CASCConfig config, BackgroundWorkerEx worker = null) => Open(worker, config);
public static CASCHandler OpenOnlineStorage(string product, string region = "us", BackgroundWorkerEx worker = null) { CASCConfig config = CASCConfig.LoadOnlineStorageConfig(product, region); return(Open(worker, config)); }
private CDNIndexHandler(CASCConfig cascConfig, BackgroundWorkerEx worker) { config = cascConfig; this.worker = worker; downloader = new SyncDownloader(worker); }
private CASCHandler(CASCConfig config, BackgroundWorkerEx worker) : base(config, worker) { Logger.WriteLine("CASCHandler: loading encoding data..."); using (var _ = new PerfCounter("new EncodingHandler()")) { using (var fs = OpenEncodingFile(this)) EncodingHandler = new EncodingHandler(fs, worker); } Logger.WriteLine("CASCHandler: loaded {0} encoding data", EncodingHandler.Count); if ((CASCConfig.LoadFlags & LoadFlags.Download) != 0) { Logger.WriteLine("CASCHandler: loading download data..."); using (var _ = new PerfCounter("new DownloadHandler()")) { using (var fs = OpenDownloadFile(EncodingHandler, this)) DownloadHandler = new DownloadHandler(fs, worker); } Logger.WriteLine("CASCHandler: loaded {0} download data", EncodingHandler.Count); } Logger.WriteLine("CASCHandler: loading root data..."); using (var _ = new PerfCounter("new RootHandler()")) { using (var fs = OpenRootFile(EncodingHandler, this)) { if (config.GameType == CASCGameType.S2 || config.GameType == CASCGameType.HotS) { RootHandler = new MNDXRootHandler(fs, worker); } else if (config.GameType == CASCGameType.D3) { RootHandler = new D3RootHandler(fs, worker, this); } else if (config.GameType == CASCGameType.WoW) { RootHandler = new WowRootHandler(fs, worker); } else if (config.GameType == CASCGameType.Agent || config.GameType == CASCGameType.Bna || config.GameType == CASCGameType.Client) { RootHandler = new AgentRootHandler(fs, worker); } else if (config.GameType == CASCGameType.S1) { RootHandler = new S1RootHandler(fs, worker); } else if (config.GameType == CASCGameType.WC3) { RootHandler = new WC3RootHandler(fs, worker); } else if (config.GameType == CASCGameType.Hearthstone) { RootHandler = new HSRootHandler(fs, worker); } else if (config.GameType == CASCGameType.Overwatch) { RootHandler = new OwRootHandler(fs, worker, this); } else if (config.GameType == CASCGameType.Destiny2) { RootHandler = new Destiny2RootHandler(fs, worker); } else { using (var ufs = new FileStream("unk_root", FileMode.Create)) fs.BaseStream.CopyTo(ufs); throw new Exception("Unsupported game " + config.BuildUID); } } } Logger.WriteLine("CASCHandler: loaded {0} root data", RootHandler.Count); if ((CASCConfig.LoadFlags & LoadFlags.Install) != 0) { Logger.WriteLine("CASCHandler: loading install data..."); using (var _ = new PerfCounter("new InstallHandler()")) { using (var fs = OpenInstallFile(EncodingHandler, this)) InstallHandler = new InstallHandler(fs, worker); InstallHandler.Print(); } Logger.WriteLine("CASCHandler: loaded {0} install data", InstallHandler.Count); } }
public D3RootHandler(BinaryReader stream, BackgroundWorkerEx worker, CASCHandler casc) { worker?.ReportProgress(0, "Loading \"root\"..."); byte b1 = stream.ReadByte(); byte b2 = stream.ReadByte(); byte b3 = stream.ReadByte(); byte b4 = stream.ReadByte(); int count = stream.ReadInt32(); for (int j = 0; j < count; j++) { MD5Hash md5 = stream.Read <MD5Hash>(); string name = stream.ReadCString(); var entries = new List <D3RootEntry>(); D3RootData[name] = entries; if (!casc.Encoding.GetEntry(md5, out EncodingEntry enc)) { continue; } using (BinaryReader s = new BinaryReader(casc.OpenFile(enc.Key))) { uint magic = s.ReadUInt32(); int nEntries0 = s.ReadInt32(); for (int i = 0; i < nEntries0; i++) { entries.Add(D3RootEntry.Read(0, s)); } int nEntries1 = s.ReadInt32(); for (int i = 0; i < nEntries1; i++) { entries.Add(D3RootEntry.Read(1, s)); } int nNamedEntries = s.ReadInt32(); for (int i = 0; i < nNamedEntries; i++) { entries.Add(D3RootEntry.Read(2, s)); } } worker?.ReportProgress((int)((j + 1) / (float)(count + 2) * 100)); } // Parse CoreTOC.dat var coreTocEntry = D3RootData["Base"].Find(e => e.Name == "CoreTOC.dat"); casc.Encoding.GetEntry(coreTocEntry.MD5, out EncodingEntry enc1); using (var file = casc.OpenFile(enc1.Key)) tocParser = new CoreTOCParser(file); worker?.ReportProgress((int)((count + 1) / (float)(count + 2) * 100)); // Parse Packages.dat var pkgEntry = D3RootData["Base"].Find(e => e.Name == "Data_D3\\PC\\Misc\\Packages.dat"); casc.Encoding.GetEntry(pkgEntry.MD5, out EncodingEntry enc2); using (var file = casc.OpenFile(enc2.Key)) pkgParser = new PackagesParser(file); worker?.ReportProgress(100); }
public override void LoadListFile(string path, BackgroundWorkerEx worker = null) { //CASCFile.Files.Clear(); using (var _ = new PerfCounter("WowRootHandler::LoadListFile()")) { worker?.ReportProgress(0, "Loading \"listfile\"..."); if (!File.Exists(path)) { Logger.WriteLine("WowRootHandler: list file missing!"); return; } bool isCsv = Path.GetExtension(path) == ".csv"; Logger.WriteLine($"WowRootHandler: loading listfile {path}..."); using (var fs2 = File.Open(path, FileMode.Open)) using (var sr = new StreamReader(fs2)) { string line; char[] splitChar = isCsv ? new char[] { ';' } : new char[] { ' ' }; while ((line = sr.ReadLine()) != null) { string[] tokens = line.Split(splitChar, 2); if (tokens.Length != 2) { Logger.WriteLine($"Invalid line in listfile: {line}"); continue; } if (!int.TryParse(tokens[0], out int fileDataId)) { Logger.WriteLine($"Invalid line in listfile: {line}"); continue; } // skip invalid names if (!RootData.ContainsKey(fileDataId)) { Logger.WriteLine($"Invalid fileDataId in listfile: {line}"); continue; } string file = tokens[1]; ulong fileHash = FileDataStore[fileDataId]; if (!CASCFile.Files.ContainsKey(fileHash)) { CASCFile.Files.Add(fileHash, new CASCFile(fileHash, file)); } else { Logger.WriteLine($"Duplicate fileDataId {fileDataId} detected: {line}"); } worker?.ReportProgress((int)(sr.BaseStream.Position / (float)sr.BaseStream.Length * 100)); } } Logger.WriteLine($"WowRootHandler: loaded {CASCFile.Files.Count} valid file names"); } }
public static CASCHandler OpenLocalStorage(string basePath, BackgroundWorkerEx worker = null) { CASCConfig config = CASCConfig.LoadLocalStorageConfig(basePath); return(Open(worker, config)); }
public WowRootHandler(BinaryReader stream, BackgroundWorkerEx worker) { worker?.ReportProgress(0, "Loading \"root\"..."); int magic = stream.ReadInt32(); int numFilesTotal = 0, numFilesWithNameHash = 0, numFilesRead = 0; const int TSFMMagic = 0x4D465354; if (magic == TSFMMagic) { numFilesTotal = stream.ReadInt32(); numFilesWithNameHash = stream.ReadInt32(); } else { stream.BaseStream.Position -= 4; } while (stream.BaseStream.Position < stream.BaseStream.Length) { int count = stream.ReadInt32(); numFilesRead += count; ContentFlags contentFlags = (ContentFlags)stream.ReadUInt32(); LocaleFlags localeFlags = (LocaleFlags)stream.ReadUInt32(); if (localeFlags == LocaleFlags.None) { throw new InvalidDataException("block.LocaleFlags == LocaleFlags.None"); } if (contentFlags != ContentFlags.None && (contentFlags & (ContentFlags.F00000001 | ContentFlags.Windows | ContentFlags.MacOS | ContentFlags.Alternate | ContentFlags.F00020000 | ContentFlags.F00080000 | ContentFlags.F00100000 | ContentFlags.F00200000 | ContentFlags.F00400000 | ContentFlags.F02000000 | ContentFlags.NotCompressed | ContentFlags.NoNameHash | ContentFlags.F20000000)) == 0) { throw new InvalidDataException("block.ContentFlags != ContentFlags.None"); } RootEntry[] entries = new RootEntry[count]; int[] filedataIds = new int[count]; int fileDataIndex = 0; for (var i = 0; i < count; ++i) { entries[i].LocaleFlags = localeFlags; entries[i].ContentFlags = contentFlags; filedataIds[i] = fileDataIndex + stream.ReadInt32(); fileDataIndex = filedataIds[i] + 1; } //Console.WriteLine("Block: {0} {1} (size {2})", block.ContentFlags, block.LocaleFlags, count); ulong[] nameHashes = null; if (magic == TSFMMagic) { for (var i = 0; i < count; ++i) { entries[i].cKey = stream.Read <MD5Hash>(); } if ((contentFlags & ContentFlags.NoNameHash) == 0) { nameHashes = new ulong[count]; for (var i = 0; i < count; ++i) { nameHashes[i] = stream.ReadUInt64(); } } } else { nameHashes = new ulong[count]; for (var i = 0; i < count; ++i) { entries[i].cKey = stream.Read <MD5Hash>(); nameHashes[i] = stream.ReadUInt64(); } } for (var i = 0; i < count; ++i) { int fileDataId = filedataIds[i]; //Logger.WriteLine("filedataid {0}", fileDataId); ulong hash; if (nameHashes == null) { hash = FileDataHash.ComputeHash(fileDataId); } else { hash = nameHashes[i]; } RootData.Add(fileDataId, entries[i]); //Console.WriteLine("File: {0:X8} {1:X16} {2}", entries[i].FileDataId, hash, entries[i].MD5.ToHexString()); if (FileDataStore.TryGetValue(fileDataId, out ulong hash2)) { if (hash2 == hash) { // duplicate, skipping } else { Logger.WriteLine("ERROR: got miltiple hashes for filedataid {0}", fileDataId); } continue; } FileDataStore.Add(fileDataId, hash); FileDataStoreReverse.Add(hash, fileDataId); if (nameHashes != null) { // generate our custom hash as well so we can still find file without calling GetHashByFileDataId in some weird cases ulong fileDataHash = FileDataHash.ComputeHash(fileDataId); FileDataStoreReverse.Add(fileDataHash, fileDataId); } } worker?.ReportProgress((int)(stream.BaseStream.Position / (float)stream.BaseStream.Length * 100)); } }