/// <summary> /// Constructor. /// </summary> /// <param name="indexFile"></param> /// <param name="dataFile"></param> protected void Initialize(String indexFile, String dataFile) { logger.Trace("Cache index file: {0}", indexFile); logger.Trace("Cache data file: {0}", dataFile); // Open cache files if BOTH index and data files exist if (File.Exists(indexFile) && File.Exists(dataFile)) { try { logger.Info("Cache files found, attempting to load."); // Open cache files indexStream = new StreamEx(indexFile); dataStream = new StreamEx(dataFile); // Load index file string magicNumber = new String(Encoding.ASCII.GetChars(indexStream.ReadBytes(0x10))); if (magicNumber.Equals(MagicNumber)) { // Calculate the entry count long entryCount = (indexStream.Length - MagicNumber.Length) / IndexAlignment; // Load Data for (int i = 0; i < entryCount; i++) { // Load Address and length long address = indexStream.ReadInt64(); long length = indexStream.ReadInt64(); // Entry is valid only if address is greater or equal than 0 if (address >= 0) { // Read Name string name = new String(Encoding.UTF8.GetChars(indexStream.ReadBytes(IndexAlignment - 0x10))); name = name.TrimEnd('\0'); // Add Entry CacheEntry entry = new CacheEntry { Address = address, Length = length, MetadataAddress = i * IndexAlignment + MagicNumber.Length, Name = name }; entriesMap.Add(entry.Name, entry.Address); entries.Add(entry.Address, entry); } else { // Add the unused entry to the holes indexHoles.Enqueue(i * IndexAlignment + MagicNumber.Length); } } // Scan for data holes //var sortedEntries = entries.Values.OrderBy(t => t.Address).ToArray(); CacheEntry[] sortedEntries = entries.Values.ToArray(); // <-- this should be sorted correctly for (int i = 0; i < entries.Count - 1; i++) { long lastEntryEnd = Align(sortedEntries[i].EndAddress, DataAlignment); long nextEntryStart = sortedEntries[i + 1].Address; long holeSize = nextEntryStart - lastEntryEnd; // Positive hole size means a data hole, negative means corrupted data if (holeSize > 0) { dataHoles.Add(lastEntryEnd, holeSize); } else if (holeSize < 0) { throw new InvalidDataException("Negative hole (overlapping data) detected."); } } } else { throw new InvalidDataException("Invalid magic number!"); } } catch (Exception x) { logger.Warn("Cache file corrupted: [{0}] {1}", x.GetType().FullName, x.Message); indexStream.Dispose(); dataStream.Dispose(); indexStream = null; dataStream = null; } } else { logger.Info("Cache index or data file not found, proceeding to creating empty cache."); } if (indexStream == null && dataStream == null) { // Overwrite BOTH index and data files if one of them is missing // This is also the fallback route if cache files are corrupted indexStream = new StreamEx(indexFile, FileMode.Create); indexStream.WriteBytes(Encoding.ASCII.GetBytes(MagicNumber)); dataStream = new StreamEx(dataFile, FileMode.Create); } }