/// <summary> /// Function to open a chunk for reading. /// </summary> /// <param name="chunkId">The ID of the chunk to open.</param> /// <returns>A <see cref="GorgonBinaryWriter" /> that will allow writing within the chunk.</returns> /// <remarks> /// <para> /// Use this to write data to a chunk within the file. This method will add a new chunk to the chunk table represented by the <see cref="GorgonChunkFile{T}.Chunks"/> collection. Note that the <paramref name="chunkId"/> /// is not required to be unique, but must not be the same as the header for the file, or the chunk table identifier. There are constants in the <see cref="GorgonChunkFile{T}"/> type that expose these values. /// </para> /// <note type="Important"> /// This method should always be paired with a call to <see cref="GorgonChunkFile{T}.CloseChunk"/>. Failure to do so will keep the chunk table from being updated properly, and corrupt the file. /// </note> /// </remarks> /// <exception cref="IOException">Thrown if the chunk was opened without calling <see cref="GorgonChunkFile{T}.Open"/> first.</exception> public override GorgonBinaryWriter OpenChunk(ulong chunkId) { if (!IsOpen) { throw new IOException(Resources.GOR_ERR_CHUNK_FILE_NOT_OPEN); } ValidateChunkID(chunkId); if (_activeChunk.ID != 0) { if (_activeChunk.ID == chunkId) { return(_activeWriter); } CloseChunk(); } // Size is 0 for now, we'll update it later. _activeChunk = new GorgonChunk(chunkId, 0, (ulong)(Stream.Position - _headerEnd + sizeof(long))); using (var chunkIDWriter = new GorgonBinaryWriter(Stream, true)) { chunkIDWriter.Write(chunkId); } _activeWriter = new GorgonBinaryWriter(new GorgonStreamWrapper(Stream, 0, 0), true); return(_activeWriter); }
/// <summary> /// Function to open a chunk for reading. /// </summary> /// <param name="chunkId">The ID of the chunk to open.</param> /// <returns>A <see cref="GorgonBinaryReader" /> that will allow reading within the chunk.</returns> /// <remarks> /// <para> /// Use this to read data from a chunk within the file. If the <paramref name="chunkId"/> is not found, then this method will throw an exception. To mitigate this, check for the existence of a chunk in /// the <see cref="GorgonChunkFile{T}.Chunks"/> collection. /// </para> /// <para> /// This method will provide minimal validation for the chunk in that it will only check the <paramref name="chunkId"/> to see if it matches what's in the file, beyond that, the user is responsible for /// validating the data that lives within the chunk. /// </para> /// </remarks> /// <exception cref="IOException">Thrown if the chunk was opened without calling <see cref="GorgonChunkFile{T}.Open"/> first.</exception> /// <exception cref="GorgonException">Thrown when the <paramref name="chunkId" /> does not match the chunk in the file.</exception> /// <exception cref="KeyNotFoundException">Thrown when the <paramref name="chunkId" /> was not found in the chunk table.</exception> public override GorgonBinaryReader OpenChunk(ulong chunkId) { if (!IsOpen) { throw new IOException(Resources.GOR_ERR_CHUNK_FILE_NOT_OPEN); } ValidateChunkID(chunkId); GorgonChunk chunk = Chunks[chunkId]; if (chunk.Equals(GorgonChunk.EmptyChunk)) { throw new KeyNotFoundException(string.Format(Resources.GOR_ERR_CHUNK_NOT_FOUND, chunkId.FormatHex())); } if (_activeChunk.ID != 0) { if (_activeChunk.ID == chunkId) { return(_activeReader); } CloseChunk(); } _activeChunk = chunk; GorgonBinaryReader reader = null; // Validate the chunk ID at the offset. try { reader = new GorgonBinaryReader(Stream, true); Stream.Position = ((long)_activeChunk.FileOffset + _headerEnd) - sizeof(long); ulong fileChunkId = reader.ReadUInt64(); if (fileChunkId != _activeChunk.ID) { throw new GorgonException(GorgonResult.CannotRead, string.Format(Resources.GOR_ERR_CHUNK_FILE_CHUNK_MISMATCH, _activeChunk.FileOffset.FormatHex(), _activeChunk.ID.FormatHex())); } } finally { reader?.Dispose(); } _activeReader = new GorgonBinaryReader(new GorgonStreamWrapper(Stream, 0, _activeChunk.Size), true); return(_activeReader); }
/// <summary> /// Function to close an open chunk. /// </summary> /// <remarks>This will close the active chunk, and reposition the stream to the end of the file header.</remarks> public override void CloseChunk() { if (_activeChunk.ID == 0) { return; } _activeChunk = default; Stream.Position = _headerEnd; _activeReader.Dispose(); _activeReader = null; }
/// <summary> /// Function to close an open chunk. /// </summary> /// <remarks> /// <para> /// This will close the active chunk, and add it to the chunk table list. It will reposition the stream pointer for the stream passed to the constructor of this object to the next position for /// a chunk, or the end of the chunk data. /// </para> /// <para> /// If this method is not called, then the chunk will not be added to the chunk table in the file and the file will lose that chunk. This, however, does not mean the file is necessarily corrupt, /// just that the chunk will not exist. Regardless, this method should always be called when one of the <see cref="O:Gorgon.IO.GorgonChunkFile`1.OpenChunk"/> are called. /// </para> /// </remarks> public override void CloseChunk() { if (_activeChunk.ID == 0) { return; } // Move the stream forward by the amount written. _activeChunk = new GorgonChunk(_activeChunk.ID, (int)(_activeWriter.BaseStream.Length), _activeChunk.FileOffset); Stream.Position += _activeChunk.Size; ChunkList.Add(_activeChunk); _activeChunk = default; _activeWriter.Dispose(); _activeWriter = null; }