private static ArchiveFile OpenArchiveFile(string fileName) { Console.WriteLine("Inside OpenArchiveFile function.."); string m_archiveLocation = FilePath.GetDirectoryName(fileName); //string m_archiveLocation = "C:\\Program Files\\openPDC\\Archive\\"; Console.WriteLine("This is the m_archiveLocation: {0}", m_archiveLocation); string instance = fileName.Substring(0, fileName.LastIndexOf('_')); ArchiveFile file = new ArchiveFile(); file.FileName = fileName; file.FileAccessMode = FileAccess.Read; file.StateFile = new StateFile(); file.StateFile.FileAccessMode = FileAccess.Read; file.StateFile.FileName = string.Format("{0}_startup.dat", instance); file.IntercomFile = new IntercomFile(); file.IntercomFile.FileAccessMode = FileAccess.Read; file.IntercomFile.FileName = string.Format("{0}scratch.dat", m_archiveLocation); file.MetadataFile = new MetadataFile(); file.MetadataFile.FileAccessMode = FileAccess.Read; file.MetadataFile.FileName = string.Format("{0}_dbase.dat", instance); file.MetadataFile.LoadOnOpen = true; Console.WriteLine("OpenArchiveFile has finished"); file.Open(); return file; }
/// <summary> /// Initializes a new instance of the <see cref="ArchiveDataBlock"/> class. /// </summary> /// <param name="parent">An <see cref="ArchiveFile"/> object.</param> /// <param name="index">0-based index of the <see cref="ArchiveDataBlock"/>.</param> /// <param name="historianID">Historian identifier whose <see cref="ArchiveData"/> is stored in the <see cref="ArchiveDataBlock"/>.</param> /// <param name="reset">true if the <see cref="ArchiveDataBlock"/> is to be <see cref="Reset()"/>; otherwise false.</param> internal ArchiveDataBlock(ArchiveFile parent, int index, int historianID, bool reset) { m_parent = parent; m_index = index; m_historianID = historianID; m_writeCursor = Location; m_lastActivityTime = DateTime.Now; if (reset) Reset(); // Clear existing data. else foreach (ArchiveData dataPoint in Read()) { } // Read existing data. }
private TimeTag m_searchEndTime; // <=| #endregion #region [ Constructors ] /// <summary> /// Initializes a new instance of the <see cref="ArchiveFileAllocationTable"/> class. /// </summary> /// <param name="parent">An <see cref="ArchiveFile"/> object.</param> internal ArchiveFileAllocationTable(ArchiveFile parent) { m_parent = parent; m_dataBlockPointers = new List<ArchiveDataBlockPointer>(); if (m_parent.FileData.Length == 0) { // File is brand new. m_fileStartTime = TimeTag.MinValue; m_fileEndTime = TimeTag.MinValue; m_dataBlockSize = m_parent.DataBlockSize; m_dataBlockCount = ArchiveFile.MaximumDataBlocks(m_parent.FileSize, m_parent.DataBlockSize); for (int i = 0; i < m_dataBlockCount; i++) { m_dataBlockPointers.Add(new ArchiveDataBlockPointer(m_parent, i)); } } else { // File was created previously. byte[] fixedFatData = new byte[FixedBinaryLength]; m_parent.FileData.Seek(-fixedFatData.Length, SeekOrigin.End); m_parent.FileData.Read(fixedFatData, 0, fixedFatData.Length); FileStartTime = new TimeTag(EndianOrder.LittleEndian.ToDouble(fixedFatData, 0)); FileEndTime = new TimeTag(EndianOrder.LittleEndian.ToDouble(fixedFatData, 8)); DataPointsReceived = EndianOrder.LittleEndian.ToInt32(fixedFatData, 16); DataPointsArchived = EndianOrder.LittleEndian.ToInt32(fixedFatData, 20); DataBlockSize = EndianOrder.LittleEndian.ToInt32(fixedFatData, 24); DataBlockCount = EndianOrder.LittleEndian.ToInt32(fixedFatData, 28); byte[] variableFatData = new byte[m_dataBlockCount * ArchiveDataBlockPointer.ByteCount]; m_parent.FileData.Seek(-(variableFatData.Length + FixedBinaryLength), SeekOrigin.End); m_parent.FileData.Read(variableFatData, 0, variableFatData.Length); for (int i = 0; i < m_dataBlockCount; i++) { m_dataBlockPointers.Add(new ArchiveDataBlockPointer(m_parent, i, variableFatData, i * ArchiveDataBlockPointer.ByteCount, variableFatData.Length)); } } }
private void WriteToHistoricArchiveFile(ArchiveData[] items) { if (m_buildHistoricFileListThread.IsAlive) // Wait until the historic file list has been built. m_buildHistoricFileListThread.Join(); OnHistoricDataWriteStart(); Dictionary<int, List<ArchiveData>> sortedPointData = new Dictionary<int, List<ArchiveData>>(); // First we'll seperate all point data by ID. for (int i = 0; i < items.Length; i++) { if (!sortedPointData.ContainsKey(items[i].HistorianID)) { sortedPointData.Add(items[i].HistorianID, new List<ArchiveData>()); } sortedPointData[items[i].HistorianID].Add(items[i]); } ProcessProgress<int> historicWriteProgress = new ProcessProgress<int>("HistoricWrite"); historicWriteProgress.Total = items.Length; foreach (int pointID in sortedPointData.Keys) { // We'll sort the point data for the current point ID by time. sortedPointData[pointID].Sort(); ArchiveFile historicFile = null; ArchiveDataBlock historicFileBlock = null; try { for (int i = 0; i < sortedPointData[pointID].Count; i++) { if (historicFile == null) { // We'll try to find a historic file when the current point data belongs. Info historicFileInfo; m_writeSearchTimeTag = sortedPointData[pointID][i].Time; lock (m_historicArchiveFiles) { historicFileInfo = m_historicArchiveFiles.Find(FindHistoricArchiveFileForWrite); } if (historicFileInfo != null) { // Found a historic file where the data can be written. historicFile = new ArchiveFile(); historicFile.FileName = historicFileInfo.FileName; historicFile.StateFile = m_stateFile; historicFile.IntercomFile = m_intercomFile; historicFile.MetadataFile = m_metadataFile; historicFile.Open(); } } if (historicFile != null) { if (sortedPointData[pointID][i].Time.CompareTo(historicFile.Fat.FileStartTime) >= 0 && sortedPointData[pointID][i].Time.CompareTo(historicFile.Fat.FileEndTime) <= 0) { // The current point data belongs to the current historic archive file. if (historicFileBlock == null || historicFileBlock.SlotsAvailable == 0) { // Request a new or previously used data block for point data. historicFileBlock = historicFile.Fat.RequestDataBlock(pointID, sortedPointData[pointID][i].Time, -1); } historicFileBlock.Write(sortedPointData[pointID][i]); historicFile.Fat.DataPointsReceived++; historicFile.Fat.DataPointsArchived++; if (i == sortedPointData[pointID].Count() - 1) { // Last piece of data for the point, so we close the currently open file. historicFile.Save(); historicFile.Dispose(); historicFile = null; historicFileBlock = null; } historicWriteProgress.Complete++; } else { // The current point data doesn't belong to the current historic archive file, so we have // to write all the point data we have so far for the current historic archive file to it. i--; historicFile.Dispose(); historicFile = null; historicFileBlock = null; } } } // Notify of progress per point. historicWriteProgress.ProgressMessage = string.Format("Wrote historic data for point id {0} ({1} of {2}).", pointID, "{0}", "{1}"); OnHistoricDataWriteProgress(historicWriteProgress); } catch (Exception ex) { // Free-up used memory. if (historicFile != null) { try { historicFile.Dispose(); historicFile = null; } catch { } } // Notify of the exception. OnHistoricDataWriteException(ex); } } OnHistoricDataWriteComplete(); }
private Info GetHistoricFileInfo(string fileName) { Info fileInfo = null; try { if (File.Exists(fileName)) { // We'll open the file and get relevant information about it. ArchiveFile historicArchiveFile = new ArchiveFile(); historicArchiveFile.FileName = fileName; historicArchiveFile.StateFile = m_stateFile; historicArchiveFile.IntercomFile = m_intercomFile; historicArchiveFile.MetadataFile = m_metadataFile; try { historicArchiveFile.Open(); fileInfo = new Info(); fileInfo.FileName = fileName; fileInfo.StartTimeTag = historicArchiveFile.Fat.FileStartTime; fileInfo.EndTimeTag = historicArchiveFile.Fat.FileEndTime; } catch (Exception) { } finally { historicArchiveFile.Dispose(); historicArchiveFile = null; } } else { // We'll resolve to getting the file information from its name only if the file no longer exists // at the location. This will be the case when file is moved to a different location. In this // case the file information we provide is only as good as the file name. string datesString = FilePath.GetFileNameWithoutExtension(fileName).Substring((FilePath.GetFileNameWithoutExtension(m_fileName) + "_").Length); string[] fileStartEndDates = datesString.Split(new string[] { "_to_" }, StringSplitOptions.None); fileInfo = new Info(); fileInfo.FileName = fileName; if (fileStartEndDates.Length == 2) { fileInfo.StartTimeTag = new TimeTag(Convert.ToDateTime(fileStartEndDates[0].Replace('!', ':'))); fileInfo.EndTimeTag = new TimeTag(Convert.ToDateTime(fileStartEndDates[1].Replace('!', ':'))); } } } catch (Exception) { } return fileInfo; }
private void PrepareForRollover() { try { DriveInfo archiveDrive = new DriveInfo(Path.GetPathRoot(m_fileName)); if (archiveDrive.AvailableFreeSpace < archiveDrive.TotalSize * (1 - ((double)m_fileOffloadThreshold / 100))) { // We'll start offloading historic files if we've reached the offload threshold. OffloadHistoricFiles(); } OnRolloverPreparationStart(); // Opening and closing a new archive file in "standby" mode will create a "standby" archive file. ArchiveFile standbyArchiveFile = new ArchiveFile(); standbyArchiveFile.FileName = StandbyArchiveFileName; standbyArchiveFile.FileSize = m_fileSize; standbyArchiveFile.DataBlockSize = m_dataBlockSize; standbyArchiveFile.StateFile = m_stateFile; standbyArchiveFile.IntercomFile = m_intercomFile; standbyArchiveFile.MetadataFile = m_metadataFile; try { standbyArchiveFile.Open(); } catch (Exception) { string standbyFileName = standbyArchiveFile.FileName; standbyArchiveFile.Close(); // We didn't succeed in creating a "standby" archive file, so we'll delete it if it was created // partially (might happen if there isn't enough disk space or thread is aborted). This is to // ensure that this preparation processes is kicked off again until a valid "standby" archive // file is successfully created. if (File.Exists(standbyFileName)) { File.Delete(standbyFileName); } throw; // Rethrow the exception so the appropriate action is taken. } finally { standbyArchiveFile.Dispose(); } OnRolloverPreparationComplete(); } catch (ThreadAbortException) { // This thread must die now... } catch (Exception ex) { OnRolloverPreparationException(ex); } }
/// <summary> /// Reads <see cref="ArchiveData"/> points. /// </summary> /// <param name="historianID">Historian identifier for which <see cref="ArchiveData"/> points are to be retrieved.</param> /// <param name="startTime">Start <see cref="TimeTag"/> (in GMT) for the <see cref="ArchiveData"/> points to be retrieved.</param> /// <param name="endTime">End <see cref="TimeTag"/> (in GMT) for the <see cref="ArchiveData"/> points to be retrieved.</param> /// <returns><see cref="IEnumerable{T}"/> object containing zero or more <see cref="ArchiveData"/> points.</returns> public IEnumerable<IDataPoint> ReadData(int historianID, TimeTag startTime, TimeTag endTime) { // Ensure that the current file is open. if (!IsOpen) throw new InvalidOperationException(string.Format("\"{0}\" file is not open.", m_fileName)); // Ensure that the current file is active. if (m_fileType != ArchiveFileType.Active) throw new InvalidOperationException("Data can only be directly read from files that are Active."); // Ensure that the start and end time are valid. if (startTime > endTime) throw new ArgumentException("End Time preceeds Start Time in the specified timespan."); // Yeild to the rollover process if it is in progress. m_rolloverWaitHandle.WaitOne(); List<Info> dataFiles = new List<Info>(); if (startTime < m_fat.FileStartTime) { // Data is to be read from historic file(s). if (m_buildHistoricFileListThread.IsAlive) m_buildHistoricFileListThread.Join(); m_readSearchStartTimeTag = startTime; m_readSearchEndTimeTag = endTime; lock (m_historicArchiveFiles) { dataFiles.AddRange(m_historicArchiveFiles.FindAll(FindHistoricArchiveFileForRead)); } } if (endTime >= m_fat.FileStartTime) { // Data is to be read from the active file. Info activeFileInfo = new Info(); activeFileInfo.FileName = m_fileName; activeFileInfo.StartTimeTag = m_fat.FileStartTime; activeFileInfo.EndTimeTag = m_fat.FileEndTime; dataFiles.Add(activeFileInfo); } // Read data from all qualifying files. foreach (Info dataFile in dataFiles) { ArchiveFile file = new ArchiveFile(); IList<ArchiveDataBlock> dataBlocks; try { file.FileName = dataFile.FileName; file.StateFile = m_stateFile; file.IntercomFile = m_intercomFile; file.MetadataFile = m_metadataFile; file.Open(); dataBlocks = file.Fat.FindDataBlocks(historianID, startTime, endTime); if (dataBlocks.Count > 0) { // Data block before the first data block matching the search criteria might contain some data // for the specified search criteria, so look for such a data block and process its data. lock (file.Fat.DataBlockPointers) { for (int i = dataBlocks[0].Index - 1; i >= 0; i--) { if (file.Fat.DataBlockPointers[i].HistorianID == historianID) { foreach (ArchiveData data in file.Fat.DataBlockPointers[i].DataBlock.Read()) { if (data.Time >= startTime) yield return data; } break; } } } // Read data from rest of the data blocks and scan the last data block for data matching the // the search criteria as it may contain data beyond the timespan specified in the search. for (int i = 0; i < dataBlocks.Count; i++) { if (i < dataBlocks.Count - 1) { // Read all the data. foreach (ArchiveData data in dataBlocks[i].Read()) { yield return data; } } else { // Scan through the data block. foreach (ArchiveData data in dataBlocks[i].Read()) { if (data.Time <= endTime) yield return data; else yield break; } } } } } finally { if (file.IsOpen) { file.Close(); } } } }
/// <summary> /// Initializes a new instance of the <see cref="ArchiveDataBlockPointer"/> class. /// </summary> /// <param name="parent">An <see cref="ArchiveFile"/> object.</param> /// <param name="index">0-based index of the <see cref="ArchiveDataBlockPointer"/>.</param> /// <param name="binaryImage">Binary image to be used for initializing <see cref="ArchiveDataBlockPointer"/>.</param> /// <param name="startIndex">0-based starting index of initialization data in the <paramref name="binaryImage"/>.</param> /// <param name="length">Valid number of bytes in <paramref name="binaryImage"/> from <paramref name="startIndex"/>.</param> internal ArchiveDataBlockPointer(ArchiveFile parent, int index, byte[] binaryImage, int startIndex, int length) : this(parent, index) { Initialize(binaryImage, startIndex, length); }
/// <summary> /// Initializes a new instance of the <see cref="ArchiveDataBlockPointer"/> class. /// </summary> /// <param name="parent">An <see cref="ArchiveFile"/> object.</param> /// <param name="index">0-based index of the <see cref="ArchiveDataBlockPointer"/>.</param> internal ArchiveDataBlockPointer(ArchiveFile parent, int index) { m_parent = parent; m_index = index; Reset(); }
/// <summary> /// Initializes a new instance of the <see cref="ArchiveDataBlock"/> class. /// </summary> /// <param name="parent">An <see cref="ArchiveFile"/> object.</param> /// <param name="index">0-based index of the <see cref="ArchiveDataBlock"/>.</param> /// <param name="historianID">Historian identifier whose <see cref="ArchiveDataPoint"/> is stored in the <see cref="ArchiveDataBlock"/>.</param> /// <param name="reset">true if the <see cref="ArchiveDataBlock"/> is to be <see cref="Reset()"/>; otherwise false.</param> /// <param name="preRead">true to pre-read data to locate write cursor.</param> internal ArchiveDataBlock(ArchiveFile parent, int index, int historianID, bool reset, bool preRead = true) { m_parent = parent; m_index = index; m_historianID = historianID; m_readBuffer = new byte[ArchiveDataPoint.FixedLength]; m_writeCursor = Location; m_lastActivityTime = DateTime.Now; if (reset) { // Clear existing data. Reset(); } else if (preRead) { // Scan through existing data to locate write cursor foreach (ArchiveDataPoint dataPoint in Read()) { } } }
private void CloseArchiveFile() { if ((object)m_archiveFile != null) { m_archiveFile.DataReadException -= file_DataReadException; m_archiveFile.HistoricFileListBuildComplete -= file_HistoricFileListBuildComplete; m_archiveFile.HistoricFileListBuildException -= file_HistoricFileListBuildException; m_archiveFile.HistoricFileListBuildStart -= file_HistoricFileListBuildStart; m_archiveFile.HistoricFileListUpdated -= file_HistoricFileListUpdated; m_archiveFile.RolloverComplete -= file_RolloverComplete; m_archiveFile.RolloverStart -= file_RolloverStart; m_archiveFile.Dispose(); } m_archiveFile = null; }
// Opens an archive file as read-only and returns the ArchiveFile object. private ArchiveFile OpenArchiveFile(string fileName) { const string metadataFileName = "{0}{1}_dbase.dat"; const string stateFileName = "{0}{1}_startup.dat"; const string intercomFileName = "{0}scratch.dat"; string archiveLocation = FilePath.GetDirectoryName(fileName); string archiveName = FilePath.GetFileName(fileName); string instance = archiveName.Substring(0, archiveName.LastIndexOf('_')); ArchiveFile file = new ArchiveFile { FileName = fileName, FileAccessMode = FileAccess.Read, MonitorNewArchiveFiles = true, PersistSettings = false, StateFile = new StateFile { FileAccessMode = FileAccess.Read, FileName = string.Format(stateFileName, archiveLocation, instance) }, IntercomFile = new IntercomFile { FileAccessMode = FileAccess.Read, FileName = string.Format(intercomFileName, archiveLocation) }, MetadataFile = new MetadataFile { FileAccessMode = FileAccess.Read, FileName = string.Format(metadataFileName, archiveLocation, instance), LoadOnOpen = true } }; file.DataReadException += file_DataReadException; file.HistoricFileListBuildComplete += file_HistoricFileListBuildComplete; file.HistoricFileListBuildException += file_HistoricFileListBuildException; file.HistoricFileListBuildStart += file_HistoricFileListBuildStart; file.HistoricFileListUpdated += file_HistoricFileListUpdated; file.RolloverComplete += file_RolloverComplete; file.RolloverStart += file_RolloverStart; // Initialize the archive file (starts file watchers) file.Initialize(); // Open the active archive file file.Open(); // Start the roll-over watch timer m_rolloverWatcher.Start(); return file; }
/// <summary> /// Opens the <see cref="ArchiveFile"/> for use. /// </summary> /// <param name="fileName">Archive .D archive file name.</param> public void Open(string fileName) { CloseArchiveFile(); m_archiveFile = OpenArchiveFile(fileName); }
private Info GetHistoricFileInfo(string fileName) { Info fileInfo = null; try { if (File.Exists(fileName)) { // We'll open the file and get relevant information about it. ArchiveFile historicArchiveFile = new ArchiveFile(); historicArchiveFile.FileName = fileName; historicArchiveFile.StateFile = m_stateFile; historicArchiveFile.IntercomFile = m_intercomFile; historicArchiveFile.MetadataFile = m_metadataFile; historicArchiveFile.FileAccessMode = FileAccess.Read; try { historicArchiveFile.Open(); fileInfo = new Info(); fileInfo.FileName = fileName; fileInfo.StartTimeTag = historicArchiveFile.Fat.FileStartTime; fileInfo.EndTimeTag = historicArchiveFile.Fat.FileEndTime; } catch (Exception ex) { OnHistoricFileListBuildException(new InvalidOperationException(string.Format("Failed to access historic data file \"{0}\" due to exception: {1}", FilePath.GetFileName(fileName), ex.Message), ex)); } finally { historicArchiveFile.Dispose(); } } //else //{ // // We'll resolve to getting the file information from its name only if the file no longer exists // // at the location. This will be the case when file is moved to a different location. In this // // case the file information we provide is only as good as the file name. // string datesString = FilePath.GetFileNameWithoutExtension(fileName).Substring((FilePath.GetFileNameWithoutExtension(m_fileName) + "_").Length); // string[] fileStartEndDates = datesString.Split(new string[] { "_to_" }, StringSplitOptions.None); // fileInfo = new Info(); // fileInfo.FileName = fileName; // if (fileStartEndDates.Length == 2) // { // fileInfo.StartTimeTag = new TimeTag(Convert.ToDateTime(fileStartEndDates[0].Replace('!', ':'))); // fileInfo.EndTimeTag = new TimeTag(Convert.ToDateTime(fileStartEndDates[1].Replace('!', ':'))); // } //} } catch { } return fileInfo; }
// Read data implementation private IEnumerable<IDataPoint> ReadData(IEnumerable<int> historianIDs, TimeTag startTime, TimeTag endTime, int lastHistorianID) { // Yield to archive rollover process. m_rolloverWaitHandle.WaitOne(); // Ensure that the current file is open. if (!IsOpen) throw new InvalidOperationException(string.Format("\"{0}\" file is not open", m_fileName)); // Ensure that the current file is active. if (m_fileType != ArchiveFileType.Active) throw new InvalidOperationException("Data can only be directly read from files that are Active"); // Ensure that the start and end time are valid. if (startTime.CompareTo(endTime) > 0) throw new ArgumentException("End Time preceeds Start Time in the specified timespan"); List<Info> dataFiles = new List<Info>(); bool pendingRollover = false; bool usingActiveFile = false; if (startTime.CompareTo(m_fat.FileStartTime) < 0) { // Data is to be read from historic file(s) - make sure that the list has been built if (m_buildHistoricFileListThread.IsAlive) m_buildHistoricFileListThread.Join(); lock (m_historicArchiveFiles) { dataFiles.AddRange(m_historicArchiveFiles.FindAll(info => FindHistoricArchiveFileForRead(info, startTime, endTime))); } } if (endTime.CompareTo(m_fat.FileStartTime) >= 0) { // Data is to be read from the active file. Info activeFileInfo = new Info(); activeFileInfo.FileName = m_fileName; activeFileInfo.StartTimeTag = m_fat.FileStartTime; activeFileInfo.EndTimeTag = m_fat.FileEndTime; dataFiles.Add(activeFileInfo); } // Read data from all qualifying files. foreach (Info dataFile in dataFiles) { ArchiveFile file = null; try { if (string.Compare(dataFile.FileName, m_fileName, true) == 0) { // Read data from current file. usingActiveFile = true; file = this; // Atomically increment total number of readers for active file Interlocked.Increment(ref m_activeFileReaders); } else { // Read data from historic file. usingActiveFile = false; file = new ArchiveFile(); file.FileName = dataFile.FileName; file.StateFile = m_stateFile; file.IntercomFile = m_intercomFile; file.MetadataFile = m_metadataFile; file.FileAccessMode = FileAccess.Read; file.Open(); } // Create new time sorted data point scanner for the desired points in this file and given time range TimeSortedDataPointScanner scanner = new TimeSortedDataPointScanner(file.Fat, historianIDs, startTime, endTime, lastHistorianID, (sender, e) => OnDataReadException(e.Argument)); // Reset last historian ID to scan from beginning after picking up where left off from roll over lastHistorianID = -1; // Return data points foreach (IDataPoint dataPoint in scanner.Read()) { yield return dataPoint; // If a rollover needs to happen, we need to relinquish read lock and close file if (m_rolloverInProgress) { startTime = dataPoint.Time; lastHistorianID = dataPoint.HistorianID; pendingRollover = true; break; } } } finally { if (usingActiveFile) { // Atomically decrement active file reader count to signal in-process code that read is complete or yielded Interlocked.Decrement(ref m_activeFileReaders); } else if ((object)file != null && file.IsOpen) { file.Close(); } } if (pendingRollover) break; } if (pendingRollover) { // Recurse into this function with an updated start time and last read point ID so that read can // resume right where it left off - recursed function call will wait until rollover is complete foreach (IDataPoint dataPoint in ReadData(historianIDs, startTime, endTime, lastHistorianID)) { yield return dataPoint; } } }
/// <summary> /// Initializes a new instance of the <see cref="ArchiveFileAllocationTable"/> class. /// </summary> /// <param name="parent">An <see cref="ArchiveFile"/> object.</param> internal ArchiveFileAllocationTable(ArchiveFile parent) { m_parent = parent; m_dataBlockPointers = new List<ArchiveDataBlockPointer>(); m_fixedTableRegion = new FixedTableRegion(this); m_variableTableRegion = new VariableTableRegion(this); if (m_parent.FileData.Length == 0) { // File is brand new. m_fileStartTime = TimeTag.MinValue; m_fileEndTime = TimeTag.MinValue; m_dataBlockSize = m_parent.DataBlockSize; m_dataBlockCount = ArchiveFile.MaximumDataBlocks(m_parent.FileSize, m_parent.DataBlockSize); for (int i = 0; i < m_dataBlockCount; i++) { m_dataBlockPointers.Add(new ArchiveDataBlockPointer(m_parent, i)); } } else { // Existing file, read table regions: // Seek to beginning of fixed table region m_parent.FileData.Seek(-m_fixedTableRegion.BinaryLength, SeekOrigin.End); // Parse fixed table region m_fixedTableRegion.ParseBinaryImageFromStream(m_parent.FileData); // Seek to beginning of variable table region (above fixed from bottom of file) m_parent.FileData.Seek(-(m_fixedTableRegion.BinaryLength + m_variableTableRegion.BinaryLength), SeekOrigin.End); // Parse variable table region m_variableTableRegion.ParseBinaryImageFromStream(m_parent.FileData); } }