/// <summary> /// Serializes the current instance to the passed <paramref name="stream"/>. /// </summary> /// <param name="stream">The stream where the data will be written to.</param> /// <param name="writeData">The action that is being called (when the file header has been written) to now write the actual data.</param> /// <param name="autoAlign">A boolean indicating if all component buffers and pack infos should be aligned automatically.</param> public void Save(Stream stream, Action <BinaryWriter> writeData, bool autoAlign = true) { if (!stream.CanWrite || !stream.CanSeek) { throw new InvalidOperationException("The stream must be writeable and seekable."); } if (autoAlign) { var rawBlock = new FamosFileRawBlock(); this.RawBlocks.Clear(); this.RawBlocks.Add(rawBlock); this.AlignBuffers(rawBlock, FamosFileAlignmentMode.Continuous); } this.Validate(); this.BeforeSerialize(); var codePage = this.LanguageInfo is null ? 1252 : this.LanguageInfo.CodePage; var encoding = Encoding.GetEncoding(codePage); using (var writer = new BinaryWriter(stream, encoding, leaveOpen: true)) { // Serialize header and leave CK key open. this.Serialize(writer); // Serialize data. writeData.Invoke(writer); // Close CK. writer.BaseStream.Seek(20, SeekOrigin.Begin); writer.Write('1'); } this.AfterSerialize(); }
/// <summary> /// Aligns the buffers of all <paramref name="components"/> to the provided <paramref name="rawBlock"/> instance. This is done automatically, if the funcionality is not being disabled during the call to 'famosFile.Save(...)'. /// </summary> /// <param name="rawBlock">The raw block instance, where the actual data is stored.</param> /// <param name="alignmentMode">The buffer alignment mode.</param> /// <param name="components">The list of components to align.</param> public void AlignBuffers(FamosFileRawBlock rawBlock, FamosFileAlignmentMode alignmentMode, List <FamosFileComponent> components) { var actualComponents = this.Fields.SelectMany(field => field.Components); if (!this.RawBlocks.Contains(rawBlock)) { throw new InvalidOperationException("The passed raw block instance is not a member of this instance."); } if (!components.Any(component => actualComponents.Contains(component))) { throw new InvalidOperationException("One or more passed component instances are not a member of this instance."); } switch (alignmentMode) { case FamosFileAlignmentMode.Continuous: var offset = 0; foreach (var component in components) { var packInfo = component.PackInfo; var buffer = packInfo.Buffers.First(); packInfo.GroupSize = 1; packInfo.ByteGapSize = 0; buffer.RawBlock = rawBlock; buffer.RawBlockOffset = offset; offset += buffer.Length; } rawBlock.Length = offset; break; case FamosFileAlignmentMode.Interlaced: if (components.Any()) { var commonSize = components.First().GetSize(); if (components.Skip(1).Any(component => component.GetSize() != commonSize)) { throw new InvalidOperationException("In interlaced mode, all components must be of the same size."); } } else { return; } var valueSizes = components.Select(component => component.PackInfo.ValueSize); var totalSize = valueSizes.Sum(); var currentOffset = 0; var rawBlockLength = 0L; foreach (var component in components) { var packInfo = component.PackInfo; var buffer = packInfo.Buffers.First(); packInfo.Offset = currentOffset; packInfo.GroupSize = 1; packInfo.ByteGapSize = totalSize - packInfo.ValueSize; buffer.RawBlock = rawBlock; buffer.RawBlockOffset = 0; currentOffset += packInfo.ValueSize; rawBlockLength += buffer.Length; } if (rawBlockLength > Math.Pow(10, 9)) { throw new InvalidOperationException("In interlaced mode, all buffers are combined into a single large buffer. This buffer would exceed the maximum allowed length of '10^9' bytes."); } foreach (var component in components) { var buffer = component.PackInfo.Buffers.First(); buffer.Length = (int)rawBlockLength; buffer.ConsumedBytes = (int)rawBlockLength; } rawBlock.Length = rawBlockLength; break; default: throw new InvalidOperationException($"The alignment mode '{alignmentMode}' parameter is unknown."); } }
/// <summary> /// Aligns the buffers of all components of this instance to the provided <paramref name="rawBlock"/> instance. This is done automatically, if the funcionality is not being disabled during the call to 'famosFile.Save(...)'. /// </summary> /// <param name="rawBlock">The raw block instance, where the actual data is stored.</param> /// <param name="alignmentMode">The buffer alignment mode.</param> public void AlignBuffers(FamosFileRawBlock rawBlock, FamosFileAlignmentMode alignmentMode) { this.AlignBuffers(rawBlock, alignmentMode, this.Fields.SelectMany(field => field.Components).ToList()); }