diff --git a/src/ACadSharp.Tests/IO/DWG/DwgReaderTests.cs b/src/ACadSharp.Tests/IO/DWG/DwgReaderTests.cs
index 997e6ae80..b4ae85bfa 100644
--- a/src/ACadSharp.Tests/IO/DWG/DwgReaderTests.cs
+++ b/src/ACadSharp.Tests/IO/DWG/DwgReaderTests.cs
@@ -1,5 +1,6 @@
using ACadSharp.IO;
using ACadSharp.Tests.TestModels;
+using System.Threading.Tasks;
using Xunit;
using Xunit.Abstractions;
@@ -23,6 +24,18 @@ public override void ReadTest(FileModel test)
base.ReadTest(test);
}
+ [Theory]
+ [MemberData(nameof(DwgFilePaths))]
+ public async Task ReadAsyncTest(FileModel test)
+ {
+ CadDocument doc = null;
+ using (DwgReader reader = new DwgReader(test.Path))
+ {
+ reader.OnNotification += this.onNotification;
+ doc = await reader.ReadAsync();
+ }
+ }
+
[Theory]
[MemberData(nameof(DwgFilePaths))]
public override void AssertDocumentDefaults(FileModel test)
diff --git a/src/ACadSharp.Tests/Internal/DwgFileHeaderExploration.cs b/src/ACadSharp.Tests/Internal/DwgFileHeaderExploration.cs
index f82043d03..0c755463a 100644
--- a/src/ACadSharp.Tests/Internal/DwgFileHeaderExploration.cs
+++ b/src/ACadSharp.Tests/Internal/DwgFileHeaderExploration.cs
@@ -32,10 +32,10 @@ public DwgFileHeaderExploration(ITestOutputHelper output) : base()
[MemberData(nameof(DwgFilePaths))]
public void PrintFileHeaderInfo(string test)
{
- DwgFileHeader fh;
+ DwgFileHeader fh = null;
using (DwgReader reader = new DwgReader(test))
{
- fh = reader.readFileHeader();
+ //fh = reader.readFileHeader();
}
printHeader(fh);
diff --git a/src/ACadSharp/CadDocument.cs b/src/ACadSharp/CadDocument.cs
index bc5be50ea..ce0078308 100644
--- a/src/ACadSharp/CadDocument.cs
+++ b/src/ACadSharp/CadDocument.cs
@@ -24,7 +24,15 @@ public class CadDocument : IHandledCadObject
///
/// Contains all the header variables for this document.
///
- public CadHeader Header { get; internal set; }
+ public CadHeader Header
+ {
+ get { return this._header; }
+ internal set
+ {
+ this._header = value;
+ this._header.Document = this;
+ }
+ }
///
/// Accesses drawing properties such as the Title, Subject, Author, and Keywords properties
@@ -166,6 +174,8 @@ internal set
///
public BlockRecord PaperSpace { get { return this.BlockRecords[BlockRecord.PaperSpaceName]; } }
+ private CadHeader _header = null;
+
private CadDictionary _rootDictionary = null;
//Contains all the objects in the document
diff --git a/src/ACadSharp/DwgPreview.cs b/src/ACadSharp/DwgPreview.cs
index 8462bd9aa..b90c9fdb6 100644
--- a/src/ACadSharp/DwgPreview.cs
+++ b/src/ACadSharp/DwgPreview.cs
@@ -1,13 +1,6 @@
-#region copyright
-//Copyright 2021, Albert Domenech.
-//All rights reserved.
-//This source code is licensed under the MIT license.
-//See LICENSE file in the project root for full license information.
-#endregion
-namespace ACadSharp
+namespace ACadSharp
{
public class DwgPreview
{
-
}
}
diff --git a/src/ACadSharp/IO/CadDocumentBuilder.cs b/src/ACadSharp/IO/CadDocumentBuilder.cs
index 02a8c84b9..39ef8bf1f 100644
--- a/src/ACadSharp/IO/CadDocumentBuilder.cs
+++ b/src/ACadSharp/IO/CadDocumentBuilder.cs
@@ -297,4 +297,19 @@ private void addToMap(ICadObjectTemplate template)
this.cadObjects.Add(template.CadObject.Handle, template.CadObject);
}
}
+
+ internal abstract class CadDocumentBuilder : CadDocumentBuilder
+ where T : CadReaderConfiguration
+ {
+ public T Configuration { get; }
+
+ public override bool KeepUnknownEntities => this.Configuration.KeepUnknownEntities;
+
+ public override bool KeepUnknownNonGraphicalObjects => this.Configuration.KeepUnknownNonGraphicalObjects;
+
+ protected CadDocumentBuilder(ACadVersion version, CadDocument document, T configuration) : base(version, document)
+ {
+ this.Configuration = configuration;
+ }
+ }
}
diff --git a/src/ACadSharp/IO/CadReaderBase.cs b/src/ACadSharp/IO/CadReaderBase.cs
index 888ef66e6..aaf045711 100644
--- a/src/ACadSharp/IO/CadReaderBase.cs
+++ b/src/ACadSharp/IO/CadReaderBase.cs
@@ -4,6 +4,8 @@
using System;
using System.IO;
using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
namespace ACadSharp.IO
{
@@ -45,6 +47,9 @@ protected CadReaderBase(Stream stream, NotificationEventHandler notification = n
///
public abstract CadDocument Read();
+ ///
+ public abstract Task ReadAsync(CancellationToken cancellationToken = default);
+
///
public abstract CadHeader ReadHeader();
diff --git a/src/ACadSharp/IO/DWG/DwgDocumentBuilder.cs b/src/ACadSharp/IO/DWG/DwgDocumentBuilder.cs
index eda63f5e9..68393ceee 100644
--- a/src/ACadSharp/IO/DWG/DwgDocumentBuilder.cs
+++ b/src/ACadSharp/IO/DWG/DwgDocumentBuilder.cs
@@ -1,15 +1,11 @@
using ACadSharp.Entities;
using ACadSharp.IO.Templates;
-using ACadSharp.Objects;
-using System;
using System.Collections.Generic;
namespace ACadSharp.IO.DWG
{
- internal class DwgDocumentBuilder : CadDocumentBuilder
+ internal class DwgDocumentBuilder : CadDocumentBuilder
{
- public DwgReaderConfiguration Configuration { get; }
-
public DwgHeaderHandlesCollection HeaderHandles { get; set; } = new();
public List BlockRecordTemplates { get; set; } = new();
@@ -18,14 +14,9 @@ internal class DwgDocumentBuilder : CadDocumentBuilder
public List ModelSpaceEntities { get; } = new();
- public override bool KeepUnknownEntities => this.Configuration.KeepUnknownEntities;
-
- public override bool KeepUnknownNonGraphicalObjects => this.Configuration.KeepUnknownNonGraphicalObjects;
-
public DwgDocumentBuilder(ACadVersion version, CadDocument document, DwgReaderConfiguration configuration)
- : base(version, document)
+ : base(version, document, configuration)
{
- this.Configuration = configuration;
}
public override void BuildDocument()
diff --git a/src/ACadSharp/IO/DWG/DwgReader.cs b/src/ACadSharp/IO/DWG/DwgReader.cs
index d92c6a62f..c21a3676f 100644
--- a/src/ACadSharp/IO/DWG/DwgReader.cs
+++ b/src/ACadSharp/IO/DWG/DwgReader.cs
@@ -1,16 +1,13 @@
using ACadSharp.Classes;
using ACadSharp.Header;
-using CSUtilities.IO;
-using CSUtilities.Converters;
-using CSUtilities.Text;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using System.Text;
using ACadSharp.Exceptions;
using ACadSharp.IO.DWG;
-using ACadSharp.IO.DWG.DwgStreamReaders;
+using System.Threading.Tasks;
+using System.Threading;
namespace ACadSharp.IO
{
@@ -101,22 +98,17 @@ public static CadDocument Read(string filename, DwgReaderConfiguration configura
///
public override CadDocument Read()
{
- this._document = new CadDocument(false);
-
//Read the file header
this._fileHeader = this.readFileHeader();
- this._builder = new DwgDocumentBuilder(this._fileHeader.AcadVersion, this._document, this.Configuration);
- this._builder.OnNotification += this.onNotificationEvent;
+ this.initializeReader(this._fileHeader.AcadVersion);
this._document.SummaryInfo = this.ReadSummaryInfo();
-
- this._document.Header = this.ReadHeader();
- this._document.Header.Document = this._document;
- this._document.Classes = this.readClasses();
+ this._document.Header = this.ReadHeader();
+ this._document.Classes = this.readClasses(this.getSectionStream(DwgSectionDefinition.Classes));
- this.readAppInfo();
+ this.readAppInfo(this.getSectionStream(DwgSectionDefinition.AppInfo));
//Read all the objects in the file
this.readObjects();
@@ -127,6 +119,31 @@ public override CadDocument Read()
return this._document;
}
+ public override async Task ReadAsync(CancellationToken cancellationToken = default)
+ {
+ this._fileHeader = await this.readFileHeaderAsync(cancellationToken);
+
+ this.initializeReader(this._fileHeader.AcadVersion);
+
+ if (this._fileHeader.AcadVersion >= ACadVersion.AC1018)
+ {
+ this._document.SummaryInfo = this.readSummaryInfo(await this.getSectionStreamAsync(DwgSectionDefinition.SummaryInfo, cancellationToken));
+ }
+
+ this._document.Header = this.readHeader(await this.getSectionStreamAsync(DwgSectionDefinition.Header, cancellationToken));
+ this._document.Classes = this.readClasses(await this.getSectionStreamAsync(DwgSectionDefinition.Classes, cancellationToken));
+
+ this.readAppInfo(await this.getSectionStreamAsync(DwgSectionDefinition.AppInfo, cancellationToken));
+
+ //Read all the objects in the file
+ await this.readObjectsAsync();
+
+ //Build the document
+ this._builder.BuildDocument();
+
+ return this._document;
+ }
+
///
/// Read the summary info of the dwg file.
///
@@ -146,6 +163,11 @@ public CadSummaryInfo ReadSummaryInfo()
if (reader == null)
return new CadSummaryInfo();
+ return this.readSummaryInfo(reader);
+ }
+
+ private CadSummaryInfo readSummaryInfo(IDwgStreamReader reader)
+ {
DwgSummaryInfoReader summaryReader = new DwgSummaryInfoReader(this._fileHeader.AcadVersion, reader);
return summaryReader.Read();
}
@@ -157,6 +179,7 @@ public CadSummaryInfo ReadSummaryInfo()
/// Refers to AcDb:Preview data section.
///
///
+ ///
public DwgPreview ReadPreview()
{
this._fileHeader = this._fileHeader ?? this.readFileHeader();
@@ -228,65 +251,55 @@ public override CadHeader ReadHeader()
hReader.Read(this._fileHeader.AcadMaintenanceVersion, out DwgHeaderHandlesCollection headerHandles);
if (this._builder != null)
+ {
this._builder.HeaderHandles = headerHandles;
+ }
return header;
}
- ///
- /// Read the file header data.
- ///
- ///
- internal DwgFileHeader readFileHeader()
+ private CadHeader readHeader(IDwgStreamReader sreader)
{
- //Reset the stream position at the begining
- this._fileStream.Position = 0L;
+ CadHeader header = new CadHeader();
+ header.CodePage = CadUtils.GetCodePageName(this._fileHeader.DrawingCodePage);
- //0x00 6 “ACXXXX” version string
- ACadVersion version = CadUtils.GetVersionFromName(this._fileStream.ReadString(6, Encoding.ASCII));
- DwgFileHeader fileHeader = DwgFileHeader.CreateFileHeader(version);
+ DwgHeaderReader hReader = new DwgHeaderReader(this._fileHeader.AcadVersion, sreader, header);
+ hReader.OnNotification += onNotificationEvent;
- //Get the stream reader
- IDwgStreamReader sreader = DwgStreamReaderBase.GetStreamHandler(fileHeader.AcadVersion, this._fileStream.Stream);
+ hReader.Read(this._fileHeader.AcadMaintenanceVersion, out DwgHeaderHandlesCollection headerHandles);
- //Read the file header
- switch (fileHeader.AcadVersion)
- {
- case ACadVersion.Unknown:
- throw new CadNotSupportedException();
- case ACadVersion.MC0_0:
- case ACadVersion.AC1_2:
- case ACadVersion.AC1_4:
- case ACadVersion.AC1_50:
- case ACadVersion.AC2_10:
- case ACadVersion.AC1002:
- case ACadVersion.AC1003:
- case ACadVersion.AC1004:
- case ACadVersion.AC1006:
- case ACadVersion.AC1009:
- throw new CadNotSupportedException(this._fileHeader.AcadVersion);
- case ACadVersion.AC1012:
- case ACadVersion.AC1014:
- case ACadVersion.AC1015:
- this.readFileHeaderAC15(fileHeader as DwgFileHeaderAC15, sreader);
- break;
- case ACadVersion.AC1018:
- this.readFileHeaderAC18(fileHeader as DwgFileHeaderAC18, sreader);
- break;
- case ACadVersion.AC1021:
- this.readFileHeaderAC21(fileHeader as DwgFileHeaderAC21, sreader);
- break;
- case ACadVersion.AC1024:
- case ACadVersion.AC1027:
- case ACadVersion.AC1032:
- //Check if it works...
- this.readFileHeaderAC18(fileHeader as DwgFileHeaderAC18, sreader);
- break;
- default:
- break;
- }
+ if (this._builder != null)
+ this._builder.HeaderHandles = headerHandles;
+
+ return header;
+ }
- return fileHeader;
+ private void initializeReader(ACadVersion version)
+ {
+ this._document = new CadDocument(false);
+
+ this._builder = new DwgDocumentBuilder(version, this._document, this.Configuration);
+ this._builder.OnNotification += this.onNotificationEvent;
+ }
+
+ private DwgFileHeader readFileHeader()
+ {
+ DwgFileHeaderReader reader = new DwgFileHeaderReader(this._fileStream.Stream);
+ this._fileHeader = reader.Read();
+
+ this.setFileEncoding(this._fileHeader);
+
+ return this._fileHeader;
+ }
+
+ private async Task readFileHeaderAsync(CancellationToken cancellationToken = default)
+ {
+ DwgFileHeaderReader reader = new DwgFileHeaderReader(this._fileStream.Stream);
+ this._fileHeader = await reader.ReadAsync(cancellationToken);
+
+ this.setFileEncoding(this._fileHeader);
+
+ return this._fileHeader;
}
///
@@ -296,11 +309,11 @@ internal DwgFileHeader readFileHeader()
/// Refers to AcDb:Classes data section.
///
///
- private DxfClassCollection readClasses()
+ private DxfClassCollection readClasses(IDwgStreamReader sreader)
{
this._fileHeader = this._fileHeader ?? this.readFileHeader();
- IDwgStreamReader sreader = this.getSectionStream(DwgSectionDefinition.Classes);
+ sreader = this.getSectionStream(DwgSectionDefinition.Classes);
var reader = new DwgClassesReader(this._fileHeader.AcadVersion, sreader, this._fileHeader);
reader.OnNotification += onNotificationEvent;
@@ -308,7 +321,7 @@ private DxfClassCollection readClasses()
return reader.Read();
}
- private void readAppInfo()
+ private void readAppInfo(IDwgStreamReader sreader)
{
#if TEST
this._fileHeader = this._fileHeader ?? this.readFileHeader();
@@ -399,7 +412,6 @@ private void readTemplate()
private void readObjects()
{
Dictionary handles = this.readHandles();
- this._document.Classes = this.readClasses();
IDwgStreamReader sreader = null;
if (this._fileHeader.AcadVersion <= ACadVersion.AC1015)
@@ -428,565 +440,96 @@ private void readObjects()
sectionReader.Read();
}
- #region File Header reading methods
-
- ///
- /// Read the file header for the AC1012 to AC1015 (R13-R15) versions of the header.
- ///
- /// File header to read
- ///
- private void readFileHeaderAC15(DwgFileHeaderAC15 fileheader, IDwgStreamReader sreader)
+ private async Task readObjectsAsync()
{
- //The next 7 starting at offset 0x06 are to be six bytes of 0
- //(in R14, 5 0’s and the ACADMAINTVER variable) and a byte of 1.
- sreader.ReadBytes(7);
- //At 0x0D is a seeker (4 byte long absolute address) for the beginning sentinel of the image data.
- fileheader.PreviewAddress = sreader.ReadInt();
-
- //Undocumented Bytes at 0x11 and 0x12
- sreader.ReadBytes(2);
-
- //Bytes at 0x13 and 0x14 are a raw short indicating the value of the code page for this drawing file.
- fileheader.DrawingCodePage = CadUtils.GetCodePage(sreader.ReadShort());
- this._encoding = getListedEncoding((int)fileheader.DrawingCodePage);
-
- //At 0x15 is a long that tells how many sets of recno/seeker/length records follow.
- int nRecords = (int)sreader.ReadRawLong();
- for (int i = 0; i < nRecords; ++i)
- {
- //Record number (raw byte) | Seeker (raw long) | Size (raw long)
- DwgSectionLocatorRecord record = new DwgSectionLocatorRecord();
- record.Number = sreader.ReadByte();
- record.Seeker = sreader.ReadRawLong();
- record.Size = sreader.ReadRawLong();
-
- fileheader.Records.Add(record.Number.Value, record);
- }
-
- //RS : CRC for BOF to this point.
- sreader.ResetShift();
+ Dictionary handles = this.readHandles();
- var sn = sreader.ReadSentinel();
- if (!DwgSectionIO.CheckSentinel(sn, DwgFileHeaderAC15.EndSentinel))
+ IDwgStreamReader sreader = null;
+ if (this._fileHeader.AcadVersion <= ACadVersion.AC1015)
{
- this.triggerNotification($"Invalid section sentinel found in FileHeader", NotificationType.Warning);
+ sreader = DwgStreamReaderBase.GetStreamHandler(this._fileHeader.AcadVersion, this._fileStream.Stream);
+ //Handles are in absolute offset for this versions
+ sreader.Position = 0;
}
- }
-
- ///
- /// Read the file header for the AC1018 (2004-2006) version of the header.
- ///
- /// File header to read
- ///
- private void readFileHeaderAC18(DwgFileHeaderAC18 fileheader, IDwgStreamReader sreader)
- {
- this.readFileMetaData(fileheader, sreader);
-
- //0x80 0x6C Encrypted Data
- //Metadata:
- //The encrypted data at 0x80 can be decrypted by exclusive or’ing the 0x6c bytes of data
- //from the file with the following magic number sequence:
-
- //29 23 BE 84 E1 6C D6 AE 52 90 49 F1 F1 BB E9 EB
- //B3 A6 DB 3C 87 0C 3E 99 24 5E 0D 1C 06 B7 47 DE
- //B3 12 4D C8 43 BB 8B A6 1F 03 5A 7D 09 38 25 1F
- //5D D4 CB FC 96 F5 45 3B 13 0D 89 0A 1C DB AE 32
- //20 9A 50 EE 40 78 36 FD 12 49 32 F6 9E 7D 49 DC
- //AD 4F 14 F2 44 40 66 D0 6B C4 30 B7
-
- StreamIO headerStream = new StreamIO(new CRC32StreamHandler(sreader.ReadBytes(0x6C), 0U)); //108
- headerStream.Encoding = TextEncoding.GetListedEncoding(CodePage.Windows1252);
-
- sreader.ReadBytes(20); //CHECK IF IS USEFUL
-
- #region Read header encrypted data
-
- //0x00 12 “AcFssFcAJMB” file ID string
- string fileId = headerStream.ReadString(12);
- if (fileId != "AcFssFcAJMB\0")
+ else
{
- this.triggerNotification($"File validation failed, id should be : AcFssFcAJMB\0, but is : {fileId}", NotificationType.Warning);
+ sreader = await this.getSectionStreamAsync(DwgSectionDefinition.AcDbObjects);
}
- //0x0C 4 0x00(long)
- headerStream.ReadInt();
- //0x10 4 0x6c(long)
- headerStream.ReadInt();
- //0x14 4 0x04(long)
- headerStream.ReadInt();
- //0x18 4 Root tree node gap
- fileheader.RootTreeNodeGap = headerStream.ReadInt();
- //0x1C 4 Lowermost left tree node gap
- fileheader.LeftGap = headerStream.ReadInt();
- //0x20 4 Lowermost right tree node gap
- fileheader.RigthGap = headerStream.ReadInt();
- //0x24 4 Unknown long(ODA writes 1)
- headerStream.ReadInt();
- //0x28 4 Last section page Id
- fileheader.LastPageId = headerStream.ReadInt();
-
- //0x2C 8 Last section page end address
- fileheader.LastSectionAddr = headerStream.ReadULong();
- //0x34 8 Second header data address pointing to the repeated header data at the end of the file
- fileheader.SecondHeaderAddr = headerStream.ReadULong();
-
- //0x3C 4 Gap amount
- fileheader.GapAmount = headerStream.ReadUInt();
- //0x40 4 Section page amount
- fileheader.SectionAmount = headerStream.ReadUInt();
- //0x44 4 0x20(long)
- headerStream.ReadInt();
- //0x48 4 0x80(long)
- headerStream.ReadInt();
- //0x4C 4 0x40(long)
- headerStream.ReadInt();
- //0x50 4 Section Page Map Id
- fileheader.SectionPageMapId = headerStream.ReadUInt();
- //0x54 8 Section Page Map address(add 0x100 to this value)
- fileheader.PageMapAddress = headerStream.ReadULong() + 256UL;
- //0x5C 4 Section Map Id
- fileheader.SectionMapId = headerStream.ReadUInt();
- //0x60 4 Section page array size
- fileheader.SectionArrayPageSize = headerStream.ReadUInt();
- //0x64 4 Gap array size
- fileheader.GapArraySize = headerStream.ReadUInt();
- //0x68 4 CRC32(long).See paragraph 2.14.2 for the 32 - bit CRC calculation,
- // the seed is zero. Note that the CRC
- // calculation is done including the 4 CRC bytes that are
- // initially zero! So the CRC calculation takes into account
- // all of the 0x6c bytes of the data in this table.
- fileheader.CRCSeed = headerStream.ReadUInt();
- #endregion
-
- #region Read page map of the file
- sreader.Position = (long)fileheader.PageMapAddress;
-
- //Get the page size
- this.getPageHeaderData(sreader, out _, out long decompressedSize, out _, out _, out _);
- //Get the descompressed stream to read the records
- StreamIO decompressed = new StreamIO(DwgLZ77AC18Decompressor.Decompress(sreader.Stream, decompressedSize));
-
- //Section size
- int total = 0x100;
- while (decompressed.Position < decompressed.Length)
- {
- DwgSectionLocatorRecord record = new DwgSectionLocatorRecord();
- //0x00 4 Section page number, starts at 1, page numbers are unique per file.
- record.Number = decompressed.ReadInt();
- //0x04 4 Section size
- record.Size = decompressed.ReadInt();
-
- if (record.Number >= 0)
- {
- record.Seeker = total;
- fileheader.Records.Add(record.Number.Value, record);
- }
- else
- {
- //If the section number is negative, this represents a gap in the sections (unused data).
- //For a negative section number, the following data will be present after the section size:
-
- //0x00 4 Parent
- decompressed.ReadInt();
- //0x04 4 Left
- decompressed.ReadInt();
- //0x08 4 Right
- decompressed.ReadInt();
- //0x0C 4 0x00
- decompressed.ReadInt();
- }
-
- total += (int)record.Size;
- }
- #endregion
-
- #region Read the data section map
- //Set the positon of the map
- sreader.Position = fileheader.Records[(int)fileheader.SectionMapId].Seeker;
- //Get the page size
- this.getPageHeaderData(sreader, out _, out decompressedSize, out _, out _, out _);
- StreamIO decompressedStream = new StreamIO(DwgLZ77AC18Decompressor.Decompress(sreader.Stream, decompressedSize));
- decompressedStream.Encoding = TextEncoding.GetListedEncoding(CodePage.Windows1252);
-
- //0x00 4 Number of section descriptions(NumDescriptions)
- int ndescriptions = decompressedStream.ReadInt();
- //0x04 4 0x02 (long)
- decompressedStream.ReadInt();
- //0x08 4 0x00007400 (long)
- decompressedStream.ReadInt();
- //0x0C 4 0x00 (long)
- decompressedStream.ReadInt();
- //0x10 4 Unknown (long), ODA writes NumDescriptions here.
- decompressedStream.ReadInt();
-
- for (int i = 0; i < ndescriptions; ++i)
- {
- DwgSectionDescriptor descriptor = new DwgSectionDescriptor();
- //0x00 8 Size of section(OdUInt64)
- descriptor.CompressedSize = decompressedStream.ReadULong();
- /*0x08 4 Page count(PageCount). Note that there can be more pages than PageCount,
- as PageCount is just the number of pages written to file.
- If a page contains zeroes only, that page is not written to file.
- These “zero pages” can be detected by checking if the page’s start
- offset is bigger than it should be based on the sum of previously read pages
- decompressed size(including zero pages).After reading all pages, if the total
- decompressed size of the pages is not equal to the section’s size, add more zero
- pages to the section until this condition is met.
- */
- descriptor.PageCount = decompressedStream.ReadInt();
- //0x0C 4 Max Decompressed Size of a section page of this type(normally 0x7400)
- descriptor.DecompressedSize = (ulong)decompressedStream.ReadInt();
- //0x10 4 Unknown(long)
- decompressedStream.ReadInt();
- //0x14 4 Compressed(1 = no, 2 = yes, normally 2)
- descriptor.CompressedCode = decompressedStream.ReadInt();
- //0x18 4 Section Id(starts at 0). The first section(empty section) is numbered 0, consecutive sections are numbered descending from(the number of sections – 1) down to 1.
- descriptor.SectionId = decompressedStream.ReadInt();
- //0x1C 4 Encrypted(0 = no, 1 = yes, 2 = unknown)
- descriptor.Encrypted = decompressedStream.ReadInt();
- //0x20 64 Section Name(string)
- descriptor.Name = decompressedStream.ReadString(64).Split('\0')[0];
-
- //Following this, the following (local) section page map data will be present
- for (int j = 0; j < descriptor.PageCount; ++j)
- {
- DwgLocalSectionMap localmap = new DwgLocalSectionMap();
- //0x00 4 Page number(index into SectionPageMap), starts at 1
- localmap.PageNumber = decompressedStream.ReadInt();
- //0x04 4 Data size for this page(compressed size).
- localmap.CompressedSize = (ulong)decompressedStream.ReadInt();
- //0x08 8 Start offset for this page(OdUInt64).If this start offset is smaller than the sum of the decompressed size of all previous pages, then this page is to be preceded by zero pages until this condition is met.
- localmap.Offset = decompressedStream.ReadULong();
-
- //same decompressed size and seeker (temporal values)
- localmap.DecompressedSize = descriptor.DecompressedSize;
- localmap.Seeker = fileheader.Records[localmap.PageNumber].Seeker;
-
- //Maximum section page size appears to be 0x7400 bytes in the normal case.
- //If a logical section of the file (the database objects, for example) exceeds this size, then it is broken up into pages of size 0x7400.
-
- descriptor.LocalSections.Add(localmap);
- }
+ Queue objectHandles = new Queue(this._builder.HeaderHandles.GetHandles()
+ .Where(o => o.HasValue)
+ .Select(a => a.Value));
- //Get the final size for the local section
- uint sizeLeft = (uint)(descriptor.CompressedSize % descriptor.DecompressedSize);
- if (sizeLeft > 0U && descriptor.LocalSections.Count > 0)
- descriptor.LocalSections[descriptor.LocalSections.Count - 1].DecompressedSize = sizeLeft;
+ DwgObjectReader sectionReader = new DwgObjectReader(
+ this._fileHeader.AcadVersion,
+ this._builder,
+ sreader,
+ objectHandles,
+ handles,
+ this._document.Classes);
- fileheader.Descriptors.Add(descriptor.Name, descriptor);
- }
- #endregion
+ sectionReader.Read();
}
- private void getPageHeaderData(IDwgStreamReader sreader,
- out long sectionType,
- out long decompressedSize,
- out long compressedSize,
- out long compressionType,
- out long checksum
- )
+ private void setFileEncoding(DwgFileHeader fileHeader)
{
- //0x00 4 Section page type:
- //Section page map: 0x41630e3b
- //Section map: 0x4163003b
- sectionType = sreader.ReadRawLong();
- //0x04 4 Decompressed size of the data that follows
- decompressedSize = sreader.ReadRawLong();
- //0x08 4 Compressed size of the data that follows(CompDataSize)
- compressedSize = sreader.ReadRawLong();
-
- //0x0C 4 Compression type(0x02)
- compressionType = sreader.ReadRawLong();
- //0x10 4 Section page checksum
- checksum = sreader.ReadRawLong();
+ this._encoding = this.getListedEncoding((int)_fileHeader.DrawingCodePage);
}
- ///
- /// Read the file header for the AC1021 (2007-2009) version of the header.
- ///
- /// File header to read
- ///
- private void readFileHeaderAC21(DwgFileHeaderAC21 fileheader, IDwgStreamReader sreader)
+ private IDwgStreamReader getSectionStream(string sectionName)
{
- this.readFileMetaData(fileheader, sreader);
-
- //The last 0x28 bytes of this section consists of check data,
- //containing 5 Int64 values representing CRC’s and related numbers
- //(starting from 0x3D8 until the end). The first 0x3D8 bytes
- //should be decoded using Reed-Solomon (255, 239) decoding, with a factor of 3.
- byte[] compressedData = sreader.ReadBytes(0x400);
- byte[] decodedData = new byte[3 * 239]; //factor * blockSize
- this.reedSolomonDecoding(compressedData, decodedData, 3, 239);
-
- //0x00 8 CRC
- long crc = LittleEndianConverter.Instance.ToInt64(decodedData, 0);
- //0x08 8 Unknown key
- long unknownKey = LittleEndianConverter.Instance.ToInt64(decodedData, 8);
- //0x10 8 Compressed Data CRC
- long compressedDataCRC = LittleEndianConverter.Instance.ToInt64(decodedData, 16);
- //0x18 4 ComprLen
- int comprLen = LittleEndianConverter.Instance.ToInt32(decodedData, 24);
- //0x1C 4 Length2
- int length2 = LittleEndianConverter.Instance.ToInt32(decodedData, 28);
-
- //The decompressed size is a fixed 0x110.
- byte[] buffer = new byte[0x110];
- //If ComprLen is negative, then Data is not compressed (and data length is ComprLen).
- if (comprLen < 0)
- {
- //buffer = decodedData
- throw new NotImplementedException();
- }
- //If ComprLen is positive, the ComprLen bytes of data are compressed
- else
- {
- DwgLZ77AC21Decompressor.Decompress(decodedData, 32U, (uint)comprLen, buffer);
- }
-
- //Get the descompressed stream to read the records
- StreamIO decompressed = new StreamIO(buffer);
+ Stream sectionStream = null;
- //Read the compressed data
- fileheader.CompressedMetadata = new Dwg21CompressedMetadata()
- {
- //0x00 8 Header size (normally 0x70)
- HeaderSize = decompressed.ReadULong(),
- //0x08 8 File size
- FileSize = decompressed.ReadULong(),
- //0x10 8 PagesMapCrcCompressed
- PagesMapCrcCompressed = decompressed.ReadULong(),
- //0x18 8 PagesMapCorrectionFactor
- PagesMapCorrectionFactor = decompressed.ReadULong(),
- //0x20 8 PagesMapCrcSeed
- PagesMapCrcSeed = decompressed.ReadULong(),
- //0x28 8 Pages map2offset(relative to data page map 1, add 0x480 to get stream position)
- Map2Offset = decompressed.ReadULong(),
- //0x30 8 Pages map2Id
- Map2Id = decompressed.ReadULong(),
- //0x38 8 PagesMapOffset(relative to data page map 1, add 0x480 to get stream position)
- PagesMapOffset = decompressed.ReadULong(),
- //0x40 8 PagesMapId
- PagesMapId = decompressed.ReadULong(),
- //0x48 8 Header2offset(relative to page map 1 address, add 0x480 to get stream position)
- Header2offset = decompressed.ReadULong(),
- //0x50 8 PagesMapSizeCompressed
- PagesMapSizeCompressed = decompressed.ReadULong(),
- //0x58 8 PagesMapSizeUncompressed
- PagesMapSizeUncompressed = decompressed.ReadULong(),
- //0x60 8 PagesAmount
- PagesAmount = decompressed.ReadULong(),
- //0x68 8 PagesMaxId
- PagesMaxId = decompressed.ReadULong(),
- //0x70 8 Unknown(normally 0x20, 32)
- Unknow0x20 = decompressed.ReadULong(),
- //0x78 8 Unknown(normally 0x40, 64)
- Unknow0x40 = decompressed.ReadULong(),
- //0x80 8 PagesMapCrcUncompressed
- PagesMapCrcUncompressed = decompressed.ReadULong(),
- //0x88 8 Unknown(normally 0xf800, 63488)
- Unknown0xF800 = decompressed.ReadULong(),
- //0x90 8 Unknown(normally 4)
- Unknown4 = decompressed.ReadULong(),
- //0x98 8 Unknown(normally 1)
- Unknown1 = decompressed.ReadULong(),
- //0xA0 8 SectionsAmount(number of sections + 1)
- SectionsAmount = decompressed.ReadULong(),
- //0xA8 8 SectionsMapCrcUncompressed
- SectionsMapCrcUncompressed = decompressed.ReadULong(),
- //0xB0 8 SectionsMapSizeCompressed
- SectionsMapSizeCompressed = decompressed.ReadULong(),
- //0xB8 8 SectionsMap2Id
- SectionsMap2Id = decompressed.ReadULong(),
- //0xC0 8 SectionsMapId
- SectionsMapId = decompressed.ReadULong(),
- //0xC8 8 SectionsMapSizeUncompressed
- SectionsMapSizeUncompressed = decompressed.ReadULong(),
- //0xD0 8 SectionsMapCrcCompressed
- SectionsMapCrcCompressed = decompressed.ReadULong(),
- //0xD8 8 SectionsMapCorrectionFactor
- SectionsMapCorrectionFactor = decompressed.ReadULong(),
- //0xE0 8 SectionsMapCrcSeed
- SectionsMapCrcSeed = decompressed.ReadULong(),
- //0xE8 8 StreamVersion(normally 0x60100)
- StreamVersion = decompressed.ReadULong(),
- //0xF0 8 CrcSeed
- CrcSeed = decompressed.ReadULong(),
- //0xF8 8 CrcSeedEncoded
- CrcSeedEncoded = decompressed.ReadULong(),
- //0x100 8 RandomSeed
- RandomSeed = decompressed.ReadULong(),
- //0x108 8 Header CRC64
- HeaderCRC64 = decompressed.ReadULong()
- };
-
- //Prepare the page data stream to read
- byte[] arr = this.getPageBuffer(
- fileheader.CompressedMetadata.PagesMapOffset,
- fileheader.CompressedMetadata.PagesMapSizeCompressed,
- fileheader.CompressedMetadata.PagesMapSizeUncompressed,
- fileheader.CompressedMetadata.PagesMapCorrectionFactor,
- 0xEF, sreader.Stream);
-
- //Read the page data
- StreamIO pageDataStream = new StreamIO(arr);
-
- long offset = 0;
- while (pageDataStream.Position < pageDataStream.Length)
+ //Get the section buffer
+ switch (this._fileHeader.AcadVersion)
{
- long size = pageDataStream.ReadLong();
- long id = Math.Abs(pageDataStream.ReadLong());
- fileheader.Records.Add((int)id, new DwgSectionLocatorRecord((int)id, (int)offset, (int)size));
-
- //Add the size to the current offset
- offset += size;
+ case ACadVersion.Unknown:
+ throw new CadNotSupportedException();
+ case ACadVersion.MC0_0:
+ case ACadVersion.AC1_2:
+ case ACadVersion.AC1_4:
+ case ACadVersion.AC1_50:
+ case ACadVersion.AC2_10:
+ case ACadVersion.AC1002:
+ case ACadVersion.AC1003:
+ case ACadVersion.AC1004:
+ case ACadVersion.AC1006:
+ case ACadVersion.AC1009:
+ throw new CadNotSupportedException(this._fileHeader.AcadVersion);
+ case ACadVersion.AC1012:
+ case ACadVersion.AC1014:
+ case ACadVersion.AC1015:
+ sectionStream = this.getSectionBuffer15(this._fileHeader as DwgFileHeaderAC15, sectionName);
+ break;
+ case ACadVersion.AC1018:
+ sectionStream = this.getSectionBuffer18(this._fileHeader as DwgFileHeaderAC18, sectionName);
+ break;
+ case ACadVersion.AC1021:
+ sectionStream = this.getSectionBuffer21(this._fileHeader as DwgFileHeaderAC21, sectionName);
+ break;
+ case ACadVersion.AC1024:
+ case ACadVersion.AC1027:
+ case ACadVersion.AC1032:
+ //Check if it works...
+ sectionStream = this.getSectionBuffer18(this._fileHeader as DwgFileHeaderAC18, sectionName);
+ break;
+ default:
+ break;
}
- //Prepare the section map data stream to read
- arr = this.getPageBuffer(
- (ulong)fileheader.Records[(int)fileheader.CompressedMetadata.SectionsMapId].Seeker,
- fileheader.CompressedMetadata.SectionsMapSizeCompressed,
- fileheader.CompressedMetadata.SectionsMapSizeUncompressed,
- fileheader.CompressedMetadata.SectionsMapCorrectionFactor,
- 239, sreader.Stream);
-
- //Section map stream
- StreamIO sectionMapStream = new StreamIO(arr);
+ //Section not found
+ if (sectionStream == null)
+ return null;
- while (sectionMapStream.Position < sectionMapStream.Length)
- {
- DwgSectionDescriptor section = new DwgSectionDescriptor();
- //0x00 8 Data size
- section.CompressedSize = sectionMapStream.ReadULong();
- //0x08 8 Max size
- section.DecompressedSize = sectionMapStream.ReadULong();
- //0x10 8 Encryption
- section.Encrypted = (int)sectionMapStream.ReadULong();
- //0x18 8 HashCode
- section.HashCode = sectionMapStream.ReadULong();
- //0x20 8 SectionNameLength
- int sectionNameLength = (int)sectionMapStream.ReadLong();
- //0x28 8 Unknown
- sectionMapStream.ReadULong();
- //0x30 8 Encoding
- section.Encoding = sectionMapStream.ReadULong();
- //0x38 8 NumPages.This is the number of pages present
- // in the file for the section, but this does not include
- // pages that contain zeroes only.A page that contains zeroes
- // only is not written to file.If a page’s data offset is
- // smaller than the sum of the decompressed size of all previous
- // pages, then it is to be preceded by a zero page with a size
- // that is equal to the difference between these two numbers.
- section.PageCount = (int)sectionMapStream.ReadULong();
-
- //Read the name
- if (sectionNameLength > 0)
- {
- section.Name = sectionMapStream.ReadString(sectionNameLength, Encoding.Unicode);
- //Remove the empty characters
- section.Name = section.Name.Replace("\0", "");
- }
+ IDwgStreamReader streamHandler = DwgStreamReaderBase.GetStreamHandler(this._fileHeader.AcadVersion, sectionStream);
- ulong currentOffset = 0;
- for (int i = 0; i < section.PageCount; ++i)
- {
- DwgLocalSectionMap page = new DwgLocalSectionMap();
- //8 Page data offset.If a page’s data offset is
- // smaller than the sum of the decompressed size
- // of all previous pages, then it is to be preceded
- // by a zero page with a size that is equal to the
- // difference between these two numbers.
- page.Offset = sectionMapStream.ReadULong();
- //8 Page Size
- page.Size = sectionMapStream.ReadLong();
- //8 Page Id
- page.PageNumber = (int)sectionMapStream.ReadLong();
- //8 Page Uncompressed Size
- page.DecompressedSize = sectionMapStream.ReadULong();
- //8 Page Compressed Size
- page.CompressedSize = sectionMapStream.ReadULong();
- //8 Page Checksum
- page.Checksum = sectionMapStream.ReadULong();
- //8 Page CRC
- page.CRC = sectionMapStream.ReadULong();
-
-#if false
-//this code it doesn't take any effect on the reading
- //Create an empty page to fill the gap
- if (currentOffset < page.Offset)
- {
- ulong decompressedSize = page.Offset - currentOffset;
- DwgLocalSectionMap emptyPage = new DwgLocalSectionMap();
- emptyPage.IsEmpty = true;
- emptyPage.Offset = currentOffset;
- emptyPage.CompressedSize = 0;
- emptyPage.DecompressedSize = decompressedSize;
-
- //Add the empty local section to the current descriptor
- section.LocalSections.Add(emptyPage);
- }
-#endif
- //Add the page to the section
- section.LocalSections.Add(page);
- //Move the offset
- currentOffset = page.Offset + page.DecompressedSize;
- }
- if (sectionNameLength > 0)
- fileheader.Descriptors.Add(section.Name, section);
- }
- }
+ //Set the encoding if needed
+ streamHandler.Encoding = this._encoding;
- ///
- /// Read the metadata from the file.
- ///
- /// File header where the data will be stored
- ///
- private void readFileMetaData(DwgFileHeaderAC18 fileheader, IDwgStreamReader sreader)
- {
- //5 bytes of 0x00
- sreader.Advance(5);
-
- //0x0B 1 Maintenance release version
- fileheader.AcadMaintenanceVersion = sreader.ReadByte();
- //0x0C 1 Byte 0x00, 0x01, or 0x03
- sreader.Advance(1);
- //0x0D 4 Preview address(long), points to the image page + page header size(0x20).
- fileheader.PreviewAddress = sreader.ReadRawLong();
- //0x11 1 Dwg version (Acad version that writes the file)
- fileheader.DwgVersion = sreader.ReadByte();
- //0x12 1 Application maintenance release version(Acad maintenance version that writes the file)
- fileheader.AppReleaseVersion = sreader.ReadByte();
-
- //0x13 2 Codepage
- fileheader.DrawingCodePage = CadUtils.GetCodePage(sreader.ReadShort());
- this._encoding = sreader.Encoding = getListedEncoding((int)fileheader.DrawingCodePage);
-
- //Advance empty bytes
- //0x15 3 3 0x00 bytes
- sreader.Advance(3);
-
- //0x18 4 SecurityType (long), see R2004 meta data, the definition is the same, paragraph 4.1.
- fileheader.SecurityType = sreader.ReadRawLong();
- //0x1C 4 Unknown long
- sreader.ReadRawLong();
- //0x20 4 Summary info Address in stream
- fileheader.SummaryInfoAddr = sreader.ReadRawLong();
- //0x24 4 VBA Project Addr(0 if not present)
- fileheader.VbaProjectAddr = sreader.ReadRawLong();
-
- //0x28 4 0x00000080
- sreader.ReadRawLong();
-
- //0x2C 4 App info Address in stream
- sreader.ReadRawLong();
-
- //Get to offset 0x80
- //0x30 0x80 0x00 bytes
- sreader.Advance(80);
+ return streamHandler;
}
- #endregion
-
- private IDwgStreamReader getSectionStream(string sectionName)
+ private async Task getSectionStreamAsync(string sectionName, CancellationToken cancellationToken = default)
{
Stream sectionStream = null;
@@ -1009,11 +552,10 @@ private IDwgStreamReader getSectionStream(string sectionName)
case ACadVersion.AC1012:
case ACadVersion.AC1014:
case ACadVersion.AC1015:
- sectionStream = this.getSectionBuffer15(this._fileHeader as DwgFileHeaderAC15, sectionName);
- //encoding = TextEncoding.GetListedEncoding((this._fileHeader as DwgFileHeaderAC15).DrawingCodePage);
+ sectionStream = await this.getSectionBuffer15Async(this._fileHeader as DwgFileHeaderAC15, sectionName, cancellationToken);
break;
case ACadVersion.AC1018:
- sectionStream = this.getSectionBuffer18(this._fileHeader as DwgFileHeaderAC18, sectionName);
+ sectionStream = await this.getSectionBuffer18Async(this._fileHeader as DwgFileHeaderAC18, sectionName, cancellationToken);
break;
case ACadVersion.AC1021:
sectionStream = this.getSectionBuffer21(this._fileHeader as DwgFileHeaderAC21, sectionName);
@@ -1022,7 +564,7 @@ private IDwgStreamReader getSectionStream(string sectionName)
case ACadVersion.AC1027:
case ACadVersion.AC1032:
//Check if it works...
- sectionStream = this.getSectionBuffer18(this._fileHeader as DwgFileHeaderAC18, sectionName);
+ sectionStream = await this.getSectionBuffer18Async(this._fileHeader as DwgFileHeaderAC18, sectionName, cancellationToken);
break;
default:
break;
@@ -1040,6 +582,7 @@ private IDwgStreamReader getSectionStream(string sectionName)
return streamHandler;
}
+
private Stream getSectionBuffer15(DwgFileHeaderAC15 fileheader, string sectionName)
{
Stream stream = null;
@@ -1061,6 +604,28 @@ private Stream getSectionBuffer15(DwgFileHeaderAC15 fileheader, string sectionNa
return stream;
}
+ private async Task getSectionBuffer15Async(DwgFileHeaderAC15 fileheader, string sectionName, CancellationToken cancellationToken = default)
+ {
+ Stream stream = null;
+
+ //Get the section locator
+ var sectionLocator = DwgSectionDefinition.GetSectionLocatorByName(sectionName);
+
+ if (!sectionLocator.HasValue)
+ //There is no section for this version
+ return null;
+
+ if (fileheader.Records.TryGetValue(sectionLocator.Value, out DwgSectionLocatorRecord record))
+ {
+ //set the stream position
+ this._fileStream.Stream.Position = record.Seeker;
+ byte[] buffer = await this._fileStream.ReadBytesAsync((int)record.Size, cancellationToken);
+ stream = new MemoryStream(buffer);
+ }
+
+ return stream;
+ }
+
private Stream getSectionBuffer18(DwgFileHeaderAC18 fileheader, string sectionName)
{
if (!fileheader.Descriptors.TryGetValue(sectionName, out DwgSectionDescriptor descriptor))
@@ -1068,7 +633,6 @@ private Stream getSectionBuffer18(DwgFileHeaderAC18 fileheader, string sectionNa
//get the total size of the page
MemoryStream memoryStream = new MemoryStream((int)descriptor.DecompressedSize * descriptor.LocalSections.Count);
-
foreach (DwgLocalSectionMap section in descriptor.LocalSections)
{
if (section.IsEmpty)
@@ -1085,7 +649,7 @@ private Stream getSectionBuffer18(DwgFileHeaderAC18 fileheader, string sectionNa
IDwgStreamReader sreader = DwgStreamReaderBase.GetStreamHandler(fileheader.AcadVersion, this._fileStream.Stream);
sreader.Position = section.Seeker;
//Get the header data
- this.decryptDataSection(section, sreader);
+ this.decryptDataSection(section, sreader, section.Seeker);
if (descriptor.IsCompressed)
{
@@ -1107,9 +671,58 @@ private Stream getSectionBuffer18(DwgFileHeaderAC18 fileheader, string sectionNa
return memoryStream;
}
- private void decryptDataSection(DwgLocalSectionMap section, IDwgStreamReader sreader)
+ private async Task getSectionBuffer18Async(DwgFileHeaderAC18 fileheader, string sectionName, CancellationToken cancellationToken = default)
{
- int secMask = 0x4164536B ^ (int)sreader.Position;
+ if (!fileheader.Descriptors.TryGetValue(sectionName, out DwgSectionDescriptor descriptor))
+ return null;
+
+ //get the total size of the page
+ MemoryStream memoryStream = new MemoryStream((int)descriptor.DecompressedSize * descriptor.LocalSections.Count);
+
+ foreach (DwgLocalSectionMap section in descriptor.LocalSections)
+ {
+ if (section.IsEmpty)
+ {
+ //Page is empty, fill the gap with 0s
+ for (int index = 0; index < (int)section.DecompressedSize; ++index)
+ {
+ memoryStream.WriteByte(0);
+ }
+ }
+ else
+ {
+ int dataSecitonSize = 32;
+ this._fileStream.Position = section.Seeker;
+ byte[] buffer = await this._fileStream.ReadBytesAsync((int)section.CompressedSize + dataSecitonSize);
+
+ //Get the page section header
+ IDwgStreamReader sreader = DwgStreamReaderBase.GetStreamHandler(fileheader.AcadVersion, new MemoryStream(buffer));
+ //Get the header data
+ this.decryptDataSection(section, sreader, section.Seeker);
+
+ if (descriptor.IsCompressed)
+ {
+ //Page is compressed
+ DwgLZ77AC18Decompressor.DecompressToDest(sreader.Stream, memoryStream);
+ }
+ else
+ {
+ //Read the stream normally
+ byte[] b = new byte[section.CompressedSize];
+ sreader.Stream.Read(b, 0, (int)section.CompressedSize);
+ memoryStream.Write(b, 0, (int)section.CompressedSize);
+ }
+ }
+ }
+
+ //Reset the stream
+ memoryStream.Position = 0L;
+ return memoryStream;
+ }
+
+ private void decryptDataSection(DwgLocalSectionMap section, IDwgStreamReader sreader, long seeker)
+ {
+ int secMask = 0x4164536B ^ (int)seeker;
//0x00 4 Section page type, since it’s always a data section: 0x4163043b
var pageType = sreader.ReadRawLong() ^ secMask;
@@ -1230,42 +843,5 @@ private void reedSolomonDecoding(byte[] encoded, byte[] buffer, int factor, int
++n;
}
}
-
- ///
- /// Get the buffer to read the dwg page.
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- private byte[] getPageBuffer(ulong pageOffset, ulong compressedSize, ulong uncompressedSize, ulong correctionFactor, int blockSize, Stream stream)
- {
- //Avoid shifted bits
- ulong v = compressedSize + 7L;
- ulong v1 = v & 0b11111111_11111111_11111111_11111000L;
-
- uint totalSize = (uint)(v1 * correctionFactor);
-
- int factor = (int)(totalSize + blockSize - 1L) / blockSize;
- int lenght = factor * byte.MaxValue;
-
- byte[] buffer = new byte[lenght];
-
- //Relative to data page map 1, add 0x480 to get stream position
- stream.Position = (long)(0x480 + pageOffset);
- stream.Read(buffer, 0, lenght);
-
- byte[] compressedData = new byte[(int)totalSize];
- this.reedSolomonDecoding(buffer, compressedData, factor, blockSize);
-
- byte[] decompressedData = new byte[uncompressedSize];
-
- DwgLZ77AC21Decompressor.Decompress(compressedData, 0U, (uint)compressedSize, decompressedData);
-
- return decompressedData;
- }
}
}
diff --git a/src/ACadSharp/IO/DWG/DwgSectionIO.cs b/src/ACadSharp/IO/DWG/DwgSectionIO.cs
index d0c015244..8e30058cb 100644
--- a/src/ACadSharp/IO/DWG/DwgSectionIO.cs
+++ b/src/ACadSharp/IO/DWG/DwgSectionIO.cs
@@ -49,22 +49,11 @@ internal abstract class DwgSectionIO
///
protected bool R2018Plus;
- protected readonly ACadVersion _version;
+ protected ACadVersion _version;
public DwgSectionIO(ACadVersion version)
{
- this._version = version;
-
- this.R13_14Only = version == ACadVersion.AC1014 || version == ACadVersion.AC1012;
- this.R13_15Only = version >= ACadVersion.AC1012 && version <= ACadVersion.AC1015;
- this.R2000Plus = version >= ACadVersion.AC1015;
- this.R2004Pre = version < ACadVersion.AC1018;
- this.R2007Pre = version <= ACadVersion.AC1021;
- this.R2004Plus = version >= ACadVersion.AC1018;
- this.R2007Plus = version >= ACadVersion.AC1021;
- this.R2010Plus = version >= ACadVersion.AC1024;
- this.R2013Plus = version >= ACadVersion.AC1027;
- this.R2018Plus = version >= ACadVersion.AC1032;
+ this.setVersion(version);
}
public static bool CheckSentinel(byte[] actual, byte[] expected)
@@ -83,6 +72,22 @@ public static bool CheckSentinel(byte[] actual, byte[] expected)
return true;
}
+ protected void setVersion(ACadVersion version)
+ {
+ this._version = version;
+
+ this.R13_14Only = version == ACadVersion.AC1014 || version == ACadVersion.AC1012;
+ this.R13_15Only = version >= ACadVersion.AC1012 && version <= ACadVersion.AC1015;
+ this.R2000Plus = version >= ACadVersion.AC1015;
+ this.R2004Pre = version < ACadVersion.AC1018;
+ this.R2007Pre = version <= ACadVersion.AC1021;
+ this.R2004Plus = version >= ACadVersion.AC1018;
+ this.R2007Plus = version >= ACadVersion.AC1021;
+ this.R2010Plus = version >= ACadVersion.AC1024;
+ this.R2013Plus = version >= ACadVersion.AC1027;
+ this.R2018Plus = version >= ACadVersion.AC1032;
+ }
+
protected void checkSentinel(IDwgStreamReader sreader, byte[] expected)
{
var sn = sreader.ReadSentinel();
diff --git a/src/ACadSharp/IO/DWG/DwgStreamReaders/DwgClassesReader.cs b/src/ACadSharp/IO/DWG/DwgStreamReaders/DwgClassesReader.cs
index c8588a0eb..7ca867200 100644
--- a/src/ACadSharp/IO/DWG/DwgStreamReaders/DwgClassesReader.cs
+++ b/src/ACadSharp/IO/DWG/DwgStreamReaders/DwgClassesReader.cs
@@ -143,7 +143,7 @@ public DxfClassCollection Read()
}
//RS: CRC
- this._sreader.ResetShift();
+ this._sreader.ReadCRC();
//0x72,0x5E,0x3B,0x47,0x3B,0x56,0x07,0x3A,0x3F,0x23,0x0B,0xA0,0x18,0x30,0x49,0x75
this.checkSentinel(this._sreader, DwgSectionDefinition.EndSentinels[this.SectionName]);
diff --git a/src/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs b/src/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs
new file mode 100644
index 000000000..556d8cb7e
--- /dev/null
+++ b/src/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs
@@ -0,0 +1,1186 @@
+using ACadSharp.Exceptions;
+using CSUtilities.Converters;
+using CSUtilities.IO;
+using CSUtilities.Text;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace ACadSharp.IO.DWG
+{
+ internal class DwgFileHeaderReader : DwgSectionIO
+ {
+ private const int _metaDataSize = 0x7A;
+
+ private const int _encriptedDataSize = 0x6C;
+
+ public override string SectionName { get { return string.Empty; } }
+
+ private StreamIO _fileStream;
+
+ public DwgFileHeaderReader(Stream stream) : base(ACadVersion.Unknown)
+ {
+ this._fileStream = new StreamIO(stream);
+ }
+
+ public DwgFileHeader Read()
+ {
+ this._fileStream.Position = 0;
+
+ //0x00 6 “ACXXXX” version string
+ byte[] buffer = new byte[6];
+ this._fileStream.Stream.Read(buffer, 0, buffer.Length);
+ this.updateFileVersion(buffer);
+
+ DwgFileHeader fileHeader = DwgFileHeader.CreateFileHeader(this._version);
+
+ //Get the stream reader
+ IDwgStreamReader sreader = DwgStreamReaderBase.GetStreamHandler(this._version, _fileStream.Stream);
+
+ //Read the file header
+ switch (fileHeader.AcadVersion)
+ {
+ case ACadVersion.Unknown:
+ throw new CadNotSupportedException();
+ case ACadVersion.MC0_0:
+ case ACadVersion.AC1_2:
+ case ACadVersion.AC1_4:
+ case ACadVersion.AC1_50:
+ case ACadVersion.AC2_10:
+ case ACadVersion.AC1002:
+ case ACadVersion.AC1003:
+ case ACadVersion.AC1004:
+ case ACadVersion.AC1006:
+ case ACadVersion.AC1009:
+ throw new CadNotSupportedException(this._version);
+ case ACadVersion.AC1012:
+ case ACadVersion.AC1014:
+ case ACadVersion.AC1015:
+ this.readFileHeaderAC15(fileHeader as DwgFileHeaderAC15, sreader);
+ break;
+ case ACadVersion.AC1018:
+ this.readFileHeaderAC18(fileHeader as DwgFileHeaderAC18, sreader);
+ break;
+ case ACadVersion.AC1021:
+ this.readFileHeaderAC21(fileHeader as DwgFileHeaderAC21, sreader);
+ break;
+ case ACadVersion.AC1024:
+ case ACadVersion.AC1027:
+ case ACadVersion.AC1032:
+ //Check if it works...
+ this.readFileHeaderAC18(fileHeader as DwgFileHeaderAC18, sreader);
+ break;
+ }
+
+ return fileHeader;
+ }
+
+ public async Task ReadAsync(CancellationToken cancellationToken = default)
+ {
+ this._fileStream.Position = 0;
+
+ //0x00 6 “ACXXXX” version string
+ byte[] buffer = new byte[6];
+ await this._fileStream.Stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken);
+ this.updateFileVersion(buffer);
+
+ DwgFileHeader fileHeader = DwgFileHeader.CreateFileHeader(this._version);
+
+ //Read the file header
+ switch (fileHeader.AcadVersion)
+ {
+ case ACadVersion.Unknown:
+ throw new CadNotSupportedException();
+ case ACadVersion.MC0_0:
+ case ACadVersion.AC1_2:
+ case ACadVersion.AC1_4:
+ case ACadVersion.AC1_50:
+ case ACadVersion.AC2_10:
+ case ACadVersion.AC1002:
+ case ACadVersion.AC1003:
+ case ACadVersion.AC1004:
+ case ACadVersion.AC1006:
+ case ACadVersion.AC1009:
+ throw new CadNotSupportedException(this._version);
+ case ACadVersion.AC1012:
+ case ACadVersion.AC1014:
+ case ACadVersion.AC1015:
+ IDwgStreamReader sreader = DwgStreamReaderBase.GetStreamHandler(_version, await getHeaderAC15Stream(cancellationToken));
+ this.readFileHeaderAC15(fileHeader as DwgFileHeaderAC15, sreader);
+ break;
+ case ACadVersion.AC1018:
+ await this.readFileHeaderAC18Async(fileHeader as DwgFileHeaderAC18, cancellationToken);
+ break;
+ case ACadVersion.AC1021:
+ await this.readFileHeaderAC21Async(fileHeader as DwgFileHeaderAC21, cancellationToken);
+ break;
+ case ACadVersion.AC1024:
+ case ACadVersion.AC1027:
+ case ACadVersion.AC1032:
+ await this.readFileHeaderAC18Async(fileHeader as DwgFileHeaderAC18, cancellationToken);
+ break;
+ }
+
+ return fileHeader;
+ }
+
+ private void readFileHeaderAC15(DwgFileHeaderAC15 fileheader, IDwgStreamReader sreader)
+ {
+ //The next 7 starting at offset 0x06 are to be six bytes of 0
+ //(in R14, 5 0’s and the ACADMAINTVER variable) and a byte of 1.
+ sreader.ReadBytes(7);
+ //At 0x0D is a seeker (4 byte long absolute address) for the beginning sentinel of the image data.
+ fileheader.PreviewAddress = sreader.ReadInt();
+
+ //Undocumented Bytes at 0x11 and 0x12
+ sreader.ReadBytes(2);
+
+ //Bytes at 0x13 and 0x14 are a raw short indicating the value of the code page for this drawing file.
+ fileheader.DrawingCodePage = CadUtils.GetCodePage(sreader.ReadShort());
+
+ //At 0x15 is a long that tells how many sets of recno/seeker/length records follow.
+ int nRecords = (int)sreader.ReadRawLong();
+ for (int i = 0; i < nRecords; ++i)
+ {
+ //Record number (raw byte) | Seeker (raw long) | Size (raw long)
+ DwgSectionLocatorRecord record = new DwgSectionLocatorRecord();
+ record.Number = sreader.ReadByte();
+ record.Seeker = sreader.ReadRawLong();
+ record.Size = sreader.ReadRawLong();
+
+ fileheader.Records.Add(record.Number.Value, record);
+ }
+
+ //RS : CRC for BOF to this point.
+ sreader.ReadCRC();
+
+ var sn = sreader.ReadSentinel();
+ if (!CheckSentinel(sn, DwgFileHeaderAC15.EndSentinel))
+ {
+ this.notify($"Invalid section sentinel found in FileHeader", NotificationType.Warning);
+ }
+ }
+
+ private void readFileHeaderAC18(DwgFileHeaderAC18 fileheader, IDwgStreamReader sreader)
+ {
+ this.readFileMetaData(fileheader, sreader);
+
+ //0x80 0x6C Encrypted Data
+ //Metadata:
+ //The encrypted data at 0x80 can be decrypted by exclusive or’ing the 0x6c bytes of data
+ //from the file with the following magic number sequence:
+
+ //29 23 BE 84 E1 6C D6 AE 52 90 49 F1 F1 BB E9 EB
+ //B3 A6 DB 3C 87 0C 3E 99 24 5E 0D 1C 06 B7 47 DE
+ //B3 12 4D C8 43 BB 8B A6 1F 03 5A 7D 09 38 25 1F
+ //5D D4 CB FC 96 F5 45 3B 13 0D 89 0A 1C DB AE 32
+ //20 9A 50 EE 40 78 36 FD 12 49 32 F6 9E 7D 49 DC
+ //AD 4F 14 F2 44 40 66 D0 6B C4 30 B7
+
+ StreamIO headerStream = new StreamIO(new CRC32StreamHandler(sreader.ReadBytes(0x6C), 0U)); //108
+ headerStream.Encoding = TextEncoding.GetListedEncoding(CodePage.Windows1252);
+
+ #region Read header encrypted data
+
+ //0x00 12 “AcFssFcAJMB” file ID string
+ string fileId = headerStream.ReadString(12);
+ if (fileId != "AcFssFcAJMB\0")
+ {
+ this.notify($"File validation failed, id should be : AcFssFcAJMB\0, but is : {fileId}", NotificationType.Warning);
+ }
+
+ //0x0C 4 0x00(long)
+ headerStream.ReadInt();
+ //0x10 4 0x6c(long)
+ headerStream.ReadInt();
+ //0x14 4 0x04(long)
+ headerStream.ReadInt();
+ //0x18 4 Root tree node gap
+ fileheader.RootTreeNodeGap = headerStream.ReadInt();
+ //0x1C 4 Lowermost left tree node gap
+ fileheader.LeftGap = headerStream.ReadInt();
+ //0x20 4 Lowermost right tree node gap
+ fileheader.RigthGap = headerStream.ReadInt();
+ //0x24 4 Unknown long(ODA writes 1)
+ headerStream.ReadInt();
+ //0x28 4 Last section page Id
+ fileheader.LastPageId = headerStream.ReadInt();
+
+ //0x2C 8 Last section page end address
+ fileheader.LastSectionAddr = headerStream.ReadULong();
+ //0x34 8 Second header data address pointing to the repeated header data at the end of the file
+ fileheader.SecondHeaderAddr = headerStream.ReadULong();
+
+ //0x3C 4 Gap amount
+ fileheader.GapAmount = headerStream.ReadUInt();
+ //0x40 4 Section page amount
+ fileheader.SectionAmount = headerStream.ReadUInt();
+ //0x44 4 0x20(long)
+ headerStream.ReadInt();
+ //0x48 4 0x80(long)
+ headerStream.ReadInt();
+ //0x4C 4 0x40(long)
+ headerStream.ReadInt();
+ //0x50 4 Section Page Map Id
+ fileheader.SectionPageMapId = headerStream.ReadUInt();
+ //0x54 8 Section Page Map address(add 0x100 to this value)
+ fileheader.PageMapAddress = headerStream.ReadULong() + 256UL;
+ //0x5C 4 Section Map Id
+ fileheader.SectionMapId = headerStream.ReadUInt();
+ //0x60 4 Section page array size
+ fileheader.SectionArrayPageSize = headerStream.ReadUInt();
+ //0x64 4 Gap array size
+ fileheader.GapArraySize = headerStream.ReadUInt();
+ //0x68 4 CRC32(long).See paragraph 2.14.2 for the 32 - bit CRC calculation,
+ // the seed is zero. Note that the CRC
+ // calculation is done including the 4 CRC bytes that are
+ // initially zero! So the CRC calculation takes into account
+ // all of the 0x6c bytes of the data in this table.
+ fileheader.CRCSeed = headerStream.ReadUInt();
+ #endregion
+
+ #region Read page map of the file
+ sreader.Position = (long)fileheader.PageMapAddress;
+
+ //Get the page size
+ this.getPageHeaderData(sreader, out _, out long decompressedSize, out _, out _, out _);
+ //Get the descompressed stream to read the records
+ StreamIO decompressed = new StreamIO(DwgLZ77AC18Decompressor.Decompress(sreader.Stream, decompressedSize));
+
+ //Section size
+ int total = 0x100;
+ while (decompressed.Position < decompressed.Length)
+ {
+ DwgSectionLocatorRecord record = new DwgSectionLocatorRecord();
+ //0x00 4 Section page number, starts at 1, page numbers are unique per file.
+ record.Number = decompressed.ReadInt();
+ //0x04 4 Section size
+ record.Size = decompressed.ReadInt();
+
+ if (record.Number >= 0)
+ {
+ record.Seeker = total;
+ fileheader.Records.Add(record.Number.Value, record);
+ }
+ else
+ {
+ //If the section number is negative, this represents a gap in the sections (unused data).
+ //For a negative section number, the following data will be present after the section size:
+
+ //0x00 4 Parent
+ decompressed.ReadInt();
+ //0x04 4 Left
+ decompressed.ReadInt();
+ //0x08 4 Right
+ decompressed.ReadInt();
+ //0x0C 4 0x00
+ decompressed.ReadInt();
+ }
+
+ total += (int)record.Size;
+ }
+ #endregion
+
+ #region Read the data section map
+ //Set the positon of the map
+ sreader.Position = fileheader.Records[(int)fileheader.SectionMapId].Seeker;
+ //84244
+ //Get the page size
+ this.getPageHeaderData(sreader, out _, out decompressedSize, out _, out _, out _);
+ //1556
+ StreamIO decompressedStream = new StreamIO(DwgLZ77AC18Decompressor.Decompress(sreader.Stream, decompressedSize));
+ decompressedStream.Encoding = TextEncoding.GetListedEncoding(CodePage.Windows1252);
+
+ //0x00 4 Number of section descriptions(NumDescriptions)
+ int ndescriptions = decompressedStream.ReadInt();
+ //0x04 4 0x02 (long)
+ decompressedStream.ReadInt();
+ //0x08 4 0x00007400 (long)
+ decompressedStream.ReadInt();
+ //0x0C 4 0x00 (long)
+ decompressedStream.ReadInt();
+ //0x10 4 Unknown (long), ODA writes NumDescriptions here.
+ decompressedStream.ReadInt();
+
+ for (int i = 0; i < ndescriptions; ++i)
+ {
+ DwgSectionDescriptor descriptor = new DwgSectionDescriptor();
+ //0x00 8 Size of section(OdUInt64)
+ descriptor.CompressedSize = decompressedStream.ReadULong();
+ /*0x08 4 Page count(PageCount). Note that there can be more pages than PageCount,
+ as PageCount is just the number of pages written to file.
+ If a page contains zeroes only, that page is not written to file.
+ These “zero pages” can be detected by checking if the page’s start
+ offset is bigger than it should be based on the sum of previously read pages
+ decompressed size(including zero pages).After reading all pages, if the total
+ decompressed size of the pages is not equal to the section’s size, add more zero
+ pages to the section until this condition is met.
+ */
+ descriptor.PageCount = decompressedStream.ReadInt();
+ //0x0C 4 Max Decompressed Size of a section page of this type(normally 0x7400)
+ descriptor.DecompressedSize = (ulong)decompressedStream.ReadInt();
+ //0x10 4 Unknown(long)
+ decompressedStream.ReadInt();
+ //0x14 4 Compressed(1 = no, 2 = yes, normally 2)
+ descriptor.CompressedCode = decompressedStream.ReadInt();
+ //0x18 4 Section Id(starts at 0). The first section(empty section) is numbered 0, consecutive sections are numbered descending from(the number of sections – 1) down to 1.
+ descriptor.SectionId = decompressedStream.ReadInt();
+ //0x1C 4 Encrypted(0 = no, 1 = yes, 2 = unknown)
+ descriptor.Encrypted = decompressedStream.ReadInt();
+ //0x20 64 Section Name(string)
+ descriptor.Name = decompressedStream.ReadString(64).Split('\0')[0];
+
+ //Following this, the following (local) section page map data will be present
+ for (int j = 0; j < descriptor.PageCount; ++j)
+ {
+ DwgLocalSectionMap localmap = new DwgLocalSectionMap();
+ //0x00 4 Page number(index into SectionPageMap), starts at 1
+ localmap.PageNumber = decompressedStream.ReadInt();
+ //0x04 4 Data size for this page(compressed size).
+ localmap.CompressedSize = (ulong)decompressedStream.ReadInt();
+ //0x08 8 Start offset for this page(OdUInt64).If this start offset is smaller than the sum of the decompressed size of all previous pages, then this page is to be preceded by zero pages until this condition is met.
+ localmap.Offset = decompressedStream.ReadULong();
+
+ //same decompressed size and seeker (temporal values)
+ localmap.DecompressedSize = descriptor.DecompressedSize;
+ localmap.Seeker = fileheader.Records[localmap.PageNumber].Seeker;
+
+ //Maximum section page size appears to be 0x7400 bytes in the normal case.
+ //If a logical section of the file (the database objects, for example) exceeds this size, then it is broken up into pages of size 0x7400.
+
+ descriptor.LocalSections.Add(localmap);
+ }
+
+ //Get the final size for the local section
+ uint sizeLeft = (uint)(descriptor.CompressedSize % descriptor.DecompressedSize);
+ if (sizeLeft > 0U && descriptor.LocalSections.Count > 0)
+ descriptor.LocalSections[descriptor.LocalSections.Count - 1].DecompressedSize = sizeLeft;
+
+ fileheader.Descriptors.Add(descriptor.Name, descriptor);
+ }
+ #endregion
+ }
+
+ private async Task readFileHeaderAC18Async(DwgFileHeaderAC18 fileheader, CancellationToken cancellationToken = default)
+ {
+ this.readFileMetaData(fileheader, await this.getStreamChunkAsync(_metaDataSize, cancellationToken));
+
+ this.readEncriptedData(fileheader, await this.getStreamChunkAsync(_encriptedDataSize, cancellationToken));
+
+ _fileStream.Position = (long)fileheader.PageMapAddress;
+
+ //Get the page size
+ this.getPageHeaderData(await this.getStreamChunkAsync(0x14, cancellationToken)
+ , out _, out long decompressedSize, out long compressedSize, out _, out _);
+
+ //Get the descompressed stream to read the records
+ IDwgStreamReader compressed = await this.getStreamChunkAsync((int)compressedSize, cancellationToken);
+
+ this.readRecords(fileheader, compressed.Stream, decompressedSize);
+
+ this._fileStream.Position = fileheader.Records[(int)fileheader.SectionMapId].Seeker;
+ //Get the page size
+ this.getPageHeaderData(await this.getStreamChunkAsync(0x14, cancellationToken)
+ , out _, out decompressedSize, out compressedSize, out _, out _);
+
+ compressed = await this.getStreamChunkAsync((int)compressedSize, cancellationToken);
+
+ this.readDescriptors(fileheader, compressed.Stream, decompressedSize);
+ }
+
+ private void readRecords(DwgFileHeaderAC18 fileheader, Stream compressed, long decompressedSize)
+ {
+ StreamIO decompressed = new StreamIO(DwgLZ77AC18Decompressor.Decompress(compressed, decompressedSize));
+
+ //Section size
+ int total = 0x100;
+ while (decompressed.Position < decompressed.Length)
+ {
+ DwgSectionLocatorRecord record = new DwgSectionLocatorRecord();
+ //0x00 4 Section page number, starts at 1, page numbers are unique per file.
+ record.Number = decompressed.ReadInt();
+ //0x04 4 Section size
+ record.Size = decompressed.ReadInt();
+
+ if (record.Number >= 0)
+ {
+ record.Seeker = total;
+ fileheader.Records.Add(record.Number.Value, record);
+ }
+ else
+ {
+ //If the section number is negative, this represents a gap in the sections (unused data).
+ //For a negative section number, the following data will be present after the section size:
+
+ //0x00 4 Parent
+ decompressed.ReadInt();
+ //0x04 4 Left
+ decompressed.ReadInt();
+ //0x08 4 Right
+ decompressed.ReadInt();
+ //0x0C 4 0x00
+ decompressed.ReadInt();
+ }
+
+ total += (int)record.Size;
+ }
+ }
+
+ private void readDescriptors(DwgFileHeaderAC18 fileheader, Stream compressed, long decompressedSize)
+ {
+ StreamIO decompressed = new StreamIO(DwgLZ77AC18Decompressor.Decompress(compressed, decompressedSize));
+ decompressed.Encoding = TextEncoding.GetListedEncoding(CodePage.Windows1252);
+
+ //0x00 4 Number of section descriptions(NumDescriptions)
+ int ndescriptions = decompressed.ReadInt();
+ //0x04 4 0x02 (long)
+ decompressed.ReadInt();
+ //0x08 4 0x00007400 (long)
+ decompressed.ReadInt();
+ //0x0C 4 0x00 (long)
+ decompressed.ReadInt();
+ //0x10 4 Unknown (long), ODA writes NumDescriptions here.
+ decompressed.ReadInt();
+
+ for (int i = 0; i < ndescriptions; ++i)
+ {
+ DwgSectionDescriptor descriptor = new DwgSectionDescriptor();
+ //0x00 8 Size of section(OdUInt64)
+ descriptor.CompressedSize = decompressed.ReadULong();
+ /*0x08 4 Page count(PageCount). Note that there can be more pages than PageCount,
+ as PageCount is just the number of pages written to file.
+ If a page contains zeroes only, that page is not written to file.
+ These “zero pages” can be detected by checking if the page’s start
+ offset is bigger than it should be based on the sum of previously read pages
+ decompressed size(including zero pages).After reading all pages, if the total
+ decompressed size of the pages is not equal to the section’s size, add more zero
+ pages to the section until this condition is met.
+ */
+ descriptor.PageCount = decompressed.ReadInt();
+ //0x0C 4 Max Decompressed Size of a section page of this type(normally 0x7400)
+ descriptor.DecompressedSize = (ulong)decompressed.ReadInt();
+ //0x10 4 Unknown(long)
+ decompressed.ReadInt();
+ //0x14 4 Compressed(1 = no, 2 = yes, normally 2)
+ descriptor.CompressedCode = decompressed.ReadInt();
+ //0x18 4 Section Id(starts at 0). The first section(empty section) is numbered 0, consecutive sections are numbered descending from(the number of sections – 1) down to 1.
+ descriptor.SectionId = decompressed.ReadInt();
+ //0x1C 4 Encrypted(0 = no, 1 = yes, 2 = unknown)
+ descriptor.Encrypted = decompressed.ReadInt();
+ //0x20 64 Section Name(string)
+ descriptor.Name = decompressed.ReadString(64).Split('\0')[0];
+
+ //Following this, the following (local) section page map data will be present
+ for (int j = 0; j < descriptor.PageCount; ++j)
+ {
+ DwgLocalSectionMap localmap = new DwgLocalSectionMap();
+ //0x00 4 Page number(index into SectionPageMap), starts at 1
+ localmap.PageNumber = decompressed.ReadInt();
+ //0x04 4 Data size for this page(compressed size).
+ localmap.CompressedSize = (ulong)decompressed.ReadInt();
+ //0x08 8 Start offset for this page(OdUInt64).If this start offset is smaller than the sum of the decompressed size of all previous pages, then this page is to be preceded by zero pages until this condition is met.
+ localmap.Offset = decompressed.ReadULong();
+
+ //same decompressed size and seeker (temporal values)
+ localmap.DecompressedSize = descriptor.DecompressedSize;
+ localmap.Seeker = fileheader.Records[localmap.PageNumber].Seeker;
+
+ //Maximum section page size appears to be 0x7400 bytes in the normal case.
+ //If a logical section of the file (the database objects, for example) exceeds this size, then it is broken up into pages of size 0x7400.
+
+ descriptor.LocalSections.Add(localmap);
+ }
+
+ //Get the final size for the local section
+ uint sizeLeft = (uint)(descriptor.CompressedSize % descriptor.DecompressedSize);
+ if (sizeLeft > 0U && descriptor.LocalSections.Count > 0)
+ descriptor.LocalSections[descriptor.LocalSections.Count - 1].DecompressedSize = sizeLeft;
+
+ fileheader.Descriptors.Add(descriptor.Name, descriptor);
+ }
+ }
+
+ private void readFileHeaderAC21(DwgFileHeaderAC21 fileheader, IDwgStreamReader sreader)
+ {
+ this.readFileMetaData(fileheader, sreader);
+
+ //The last 0x28 bytes of this section consists of check data,
+ //containing 5 Int64 values representing CRC’s and related numbers
+ //(starting from 0x3D8 until the end). The first 0x3D8 bytes
+ //should be decoded using Reed-Solomon (255, 239) decoding, with a factor of 3.
+ byte[] compressedData = sreader.ReadBytes(0x400);
+ byte[] decodedData = new byte[3 * 239]; //factor * blockSize
+ this.reedSolomonDecoding(compressedData, decodedData, 3, 239);
+
+ //0x00 8 CRC
+ long crc = LittleEndianConverter.Instance.ToInt64(decodedData, 0);
+ //0x08 8 Unknown key
+ long unknownKey = LittleEndianConverter.Instance.ToInt64(decodedData, 8);
+ //0x10 8 Compressed Data CRC
+ long compressedDataCRC = LittleEndianConverter.Instance.ToInt64(decodedData, 16);
+ //0x18 4 ComprLen
+ int comprLen = LittleEndianConverter.Instance.ToInt32(decodedData, 24);
+ //0x1C 4 Length2
+ int length2 = LittleEndianConverter.Instance.ToInt32(decodedData, 28);
+
+ //The decompressed size is a fixed 0x110.
+ byte[] buffer = new byte[0x110];
+ //If ComprLen is negative, then Data is not compressed (and data length is ComprLen).
+ if (comprLen < 0)
+ {
+ //buffer = decodedData
+ throw new NotImplementedException();
+ }
+ //If ComprLen is positive, the ComprLen bytes of data are compressed
+ else
+ {
+ DwgLZ77AC21Decompressor.Decompress(decodedData, 32U, (uint)comprLen, buffer);
+ }
+
+ //Get the descompressed stream to read the records
+ StreamIO decompressed = new StreamIO(buffer);
+
+ //Read the compressed data
+ fileheader.CompressedMetadata = new Dwg21CompressedMetadata()
+ {
+ //0x00 8 Header size (normally 0x70)
+ HeaderSize = decompressed.ReadULong(),
+ //0x08 8 File size
+ FileSize = decompressed.ReadULong(),
+ //0x10 8 PagesMapCrcCompressed
+ PagesMapCrcCompressed = decompressed.ReadULong(),
+ //0x18 8 PagesMapCorrectionFactor
+ PagesMapCorrectionFactor = decompressed.ReadULong(),
+ //0x20 8 PagesMapCrcSeed
+ PagesMapCrcSeed = decompressed.ReadULong(),
+ //0x28 8 Pages map2offset(relative to data page map 1, add 0x480 to get stream position)
+ Map2Offset = decompressed.ReadULong(),
+ //0x30 8 Pages map2Id
+ Map2Id = decompressed.ReadULong(),
+ //0x38 8 PagesMapOffset(relative to data page map 1, add 0x480 to get stream position)
+ PagesMapOffset = decompressed.ReadULong(),
+ //0x40 8 PagesMapId
+ PagesMapId = decompressed.ReadULong(),
+ //0x48 8 Header2offset(relative to page map 1 address, add 0x480 to get stream position)
+ Header2offset = decompressed.ReadULong(),
+ //0x50 8 PagesMapSizeCompressed
+ PagesMapSizeCompressed = decompressed.ReadULong(),
+ //0x58 8 PagesMapSizeUncompressed
+ PagesMapSizeUncompressed = decompressed.ReadULong(),
+ //0x60 8 PagesAmount
+ PagesAmount = decompressed.ReadULong(),
+ //0x68 8 PagesMaxId
+ PagesMaxId = decompressed.ReadULong(),
+ //0x70 8 Unknown(normally 0x20, 32)
+ Unknow0x20 = decompressed.ReadULong(),
+ //0x78 8 Unknown(normally 0x40, 64)
+ Unknow0x40 = decompressed.ReadULong(),
+ //0x80 8 PagesMapCrcUncompressed
+ PagesMapCrcUncompressed = decompressed.ReadULong(),
+ //0x88 8 Unknown(normally 0xf800, 63488)
+ Unknown0xF800 = decompressed.ReadULong(),
+ //0x90 8 Unknown(normally 4)
+ Unknown4 = decompressed.ReadULong(),
+ //0x98 8 Unknown(normally 1)
+ Unknown1 = decompressed.ReadULong(),
+ //0xA0 8 SectionsAmount(number of sections + 1)
+ SectionsAmount = decompressed.ReadULong(),
+ //0xA8 8 SectionsMapCrcUncompressed
+ SectionsMapCrcUncompressed = decompressed.ReadULong(),
+ //0xB0 8 SectionsMapSizeCompressed
+ SectionsMapSizeCompressed = decompressed.ReadULong(),
+ //0xB8 8 SectionsMap2Id
+ SectionsMap2Id = decompressed.ReadULong(),
+ //0xC0 8 SectionsMapId
+ SectionsMapId = decompressed.ReadULong(),
+ //0xC8 8 SectionsMapSizeUncompressed
+ SectionsMapSizeUncompressed = decompressed.ReadULong(),
+ //0xD0 8 SectionsMapCrcCompressed
+ SectionsMapCrcCompressed = decompressed.ReadULong(),
+ //0xD8 8 SectionsMapCorrectionFactor
+ SectionsMapCorrectionFactor = decompressed.ReadULong(),
+ //0xE0 8 SectionsMapCrcSeed
+ SectionsMapCrcSeed = decompressed.ReadULong(),
+ //0xE8 8 StreamVersion(normally 0x60100)
+ StreamVersion = decompressed.ReadULong(),
+ //0xF0 8 CrcSeed
+ CrcSeed = decompressed.ReadULong(),
+ //0xF8 8 CrcSeedEncoded
+ CrcSeedEncoded = decompressed.ReadULong(),
+ //0x100 8 RandomSeed
+ RandomSeed = decompressed.ReadULong(),
+ //0x108 8 Header CRC64
+ HeaderCRC64 = decompressed.ReadULong()
+ };
+
+ //Prepare the page data stream to read
+ byte[] arr = this.getPageBuffer(
+ fileheader.CompressedMetadata.PagesMapOffset,
+ fileheader.CompressedMetadata.PagesMapSizeCompressed,
+ fileheader.CompressedMetadata.PagesMapSizeUncompressed,
+ fileheader.CompressedMetadata.PagesMapCorrectionFactor,
+ 0xEF, sreader.Stream);
+
+ //Read the page data
+ StreamIO pageDataStream = new StreamIO(arr);
+
+ long offset = 0;
+ while (pageDataStream.Position < pageDataStream.Length)
+ {
+ long size = pageDataStream.ReadLong();
+ long id = Math.Abs(pageDataStream.ReadLong());
+ fileheader.Records.Add((int)id, new DwgSectionLocatorRecord((int)id, (int)offset, (int)size));
+
+ //Add the size to the current offset
+ offset += size;
+ }
+
+ //Prepare the section map data stream to read
+ arr = this.getPageBuffer(
+ (ulong)fileheader.Records[(int)fileheader.CompressedMetadata.SectionsMapId].Seeker,
+ fileheader.CompressedMetadata.SectionsMapSizeCompressed,
+ fileheader.CompressedMetadata.SectionsMapSizeUncompressed,
+ fileheader.CompressedMetadata.SectionsMapCorrectionFactor,
+ 239, sreader.Stream);
+
+ //Section map stream
+ StreamIO sectionMapStream = new StreamIO(arr);
+
+ while (sectionMapStream.Position < sectionMapStream.Length)
+ {
+ DwgSectionDescriptor section = new DwgSectionDescriptor();
+ //0x00 8 Data size
+ section.CompressedSize = sectionMapStream.ReadULong();
+ //0x08 8 Max size
+ section.DecompressedSize = sectionMapStream.ReadULong();
+ //0x10 8 Encryption
+ section.Encrypted = (int)sectionMapStream.ReadULong();
+ //0x18 8 HashCode
+ section.HashCode = sectionMapStream.ReadULong();
+ //0x20 8 SectionNameLength
+ int sectionNameLength = (int)sectionMapStream.ReadLong();
+ //0x28 8 Unknown
+ sectionMapStream.ReadULong();
+ //0x30 8 Encoding
+ section.Encoding = sectionMapStream.ReadULong();
+ //0x38 8 NumPages.This is the number of pages present
+ // in the file for the section, but this does not include
+ // pages that contain zeroes only.A page that contains zeroes
+ // only is not written to file.If a page’s data offset is
+ // smaller than the sum of the decompressed size of all previous
+ // pages, then it is to be preceded by a zero page with a size
+ // that is equal to the difference between these two numbers.
+ section.PageCount = (int)sectionMapStream.ReadULong();
+
+ //Read the name
+ if (sectionNameLength > 0)
+ {
+ section.Name = sectionMapStream.ReadString(sectionNameLength, Encoding.Unicode);
+ //Remove the empty characters
+ section.Name = section.Name.Replace("\0", "");
+ }
+
+ ulong currentOffset = 0;
+ for (int i = 0; i < section.PageCount; ++i)
+ {
+ DwgLocalSectionMap page = new DwgLocalSectionMap();
+ //8 Page data offset.If a page’s data offset is
+ // smaller than the sum of the decompressed size
+ // of all previous pages, then it is to be preceded
+ // by a zero page with a size that is equal to the
+ // difference between these two numbers.
+ page.Offset = sectionMapStream.ReadULong();
+ //8 Page Size
+ page.Size = sectionMapStream.ReadLong();
+ //8 Page Id
+ page.PageNumber = (int)sectionMapStream.ReadLong();
+ //8 Page Uncompressed Size
+ page.DecompressedSize = sectionMapStream.ReadULong();
+ //8 Page Compressed Size
+ page.CompressedSize = sectionMapStream.ReadULong();
+ //8 Page Checksum
+ page.Checksum = sectionMapStream.ReadULong();
+ //8 Page CRC
+ page.CRC = sectionMapStream.ReadULong();
+
+ //Add the page to the section
+ section.LocalSections.Add(page);
+ //Move the offset
+ currentOffset = page.Offset + page.DecompressedSize;
+ }
+ if (sectionNameLength > 0)
+ fileheader.Descriptors.Add(section.Name, section);
+ }
+ }
+
+ private async Task readFileHeaderAC21Async(DwgFileHeaderAC21 fileheader, CancellationToken cancellationToken = default)
+ {
+ this.readFileMetaData(fileheader, await this.getStreamChunkAsync(_metaDataSize, cancellationToken));
+
+ //The last 0x28 bytes of this section consists of check data,
+ //containing 5 Int64 values representing CRC’s and related numbers
+ //(starting from 0x3D8 until the end). The first 0x3D8 bytes
+ //should be decoded using Reed-Solomon (255, 239) decoding, with a factor of 3.
+ byte[] compressedData = await _fileStream.ReadBytesAsync(0x400);
+ byte[] decodedData = new byte[3 * 239]; //factor * blockSize
+ this.reedSolomonDecoding(compressedData, decodedData, 3, 239);
+
+ //0x00 8 CRC
+ long crc = LittleEndianConverter.Instance.ToInt64(decodedData, 0);
+ //0x08 8 Unknown key
+ long unknownKey = LittleEndianConverter.Instance.ToInt64(decodedData, 8);
+ //0x10 8 Compressed Data CRC
+ long compressedDataCRC = LittleEndianConverter.Instance.ToInt64(decodedData, 16);
+ //0x18 4 ComprLen
+ int comprLen = LittleEndianConverter.Instance.ToInt32(decodedData, 24);
+ //0x1C 4 Length2
+ int length2 = LittleEndianConverter.Instance.ToInt32(decodedData, 28);
+
+ //The decompressed size is a fixed 0x110.
+ byte[] buffer = new byte[0x110];
+ //If ComprLen is negative, then Data is not compressed (and data length is ComprLen).
+ if (comprLen < 0)
+ {
+ //buffer = decodedData
+ throw new NotImplementedException();
+ }
+ //If ComprLen is positive, the ComprLen bytes of data are compressed
+ else
+ {
+ DwgLZ77AC21Decompressor.Decompress(decodedData, 32U, (uint)comprLen, buffer);
+ }
+
+ //Get the descompressed stream to read the records
+ StreamIO decompressed = new StreamIO(buffer);
+
+ //Read the compressed data
+ fileheader.CompressedMetadata = new Dwg21CompressedMetadata()
+ {
+ //0x00 8 Header size (normally 0x70)
+ HeaderSize = decompressed.ReadULong(),
+ //0x08 8 File size
+ FileSize = decompressed.ReadULong(),
+ //0x10 8 PagesMapCrcCompressed
+ PagesMapCrcCompressed = decompressed.ReadULong(),
+ //0x18 8 PagesMapCorrectionFactor
+ PagesMapCorrectionFactor = decompressed.ReadULong(),
+ //0x20 8 PagesMapCrcSeed
+ PagesMapCrcSeed = decompressed.ReadULong(),
+ //0x28 8 Pages map2offset(relative to data page map 1, add 0x480 to get stream position)
+ Map2Offset = decompressed.ReadULong(),
+ //0x30 8 Pages map2Id
+ Map2Id = decompressed.ReadULong(),
+ //0x38 8 PagesMapOffset(relative to data page map 1, add 0x480 to get stream position)
+ PagesMapOffset = decompressed.ReadULong(),
+ //0x40 8 PagesMapId
+ PagesMapId = decompressed.ReadULong(),
+ //0x48 8 Header2offset(relative to page map 1 address, add 0x480 to get stream position)
+ Header2offset = decompressed.ReadULong(),
+ //0x50 8 PagesMapSizeCompressed
+ PagesMapSizeCompressed = decompressed.ReadULong(),
+ //0x58 8 PagesMapSizeUncompressed
+ PagesMapSizeUncompressed = decompressed.ReadULong(),
+ //0x60 8 PagesAmount
+ PagesAmount = decompressed.ReadULong(),
+ //0x68 8 PagesMaxId
+ PagesMaxId = decompressed.ReadULong(),
+ //0x70 8 Unknown(normally 0x20, 32)
+ Unknow0x20 = decompressed.ReadULong(),
+ //0x78 8 Unknown(normally 0x40, 64)
+ Unknow0x40 = decompressed.ReadULong(),
+ //0x80 8 PagesMapCrcUncompressed
+ PagesMapCrcUncompressed = decompressed.ReadULong(),
+ //0x88 8 Unknown(normally 0xf800, 63488)
+ Unknown0xF800 = decompressed.ReadULong(),
+ //0x90 8 Unknown(normally 4)
+ Unknown4 = decompressed.ReadULong(),
+ //0x98 8 Unknown(normally 1)
+ Unknown1 = decompressed.ReadULong(),
+ //0xA0 8 SectionsAmount(number of sections + 1)
+ SectionsAmount = decompressed.ReadULong(),
+ //0xA8 8 SectionsMapCrcUncompressed
+ SectionsMapCrcUncompressed = decompressed.ReadULong(),
+ //0xB0 8 SectionsMapSizeCompressed
+ SectionsMapSizeCompressed = decompressed.ReadULong(),
+ //0xB8 8 SectionsMap2Id
+ SectionsMap2Id = decompressed.ReadULong(),
+ //0xC0 8 SectionsMapId
+ SectionsMapId = decompressed.ReadULong(),
+ //0xC8 8 SectionsMapSizeUncompressed
+ SectionsMapSizeUncompressed = decompressed.ReadULong(),
+ //0xD0 8 SectionsMapCrcCompressed
+ SectionsMapCrcCompressed = decompressed.ReadULong(),
+ //0xD8 8 SectionsMapCorrectionFactor
+ SectionsMapCorrectionFactor = decompressed.ReadULong(),
+ //0xE0 8 SectionsMapCrcSeed
+ SectionsMapCrcSeed = decompressed.ReadULong(),
+ //0xE8 8 StreamVersion(normally 0x60100)
+ StreamVersion = decompressed.ReadULong(),
+ //0xF0 8 CrcSeed
+ CrcSeed = decompressed.ReadULong(),
+ //0xF8 8 CrcSeedEncoded
+ CrcSeedEncoded = decompressed.ReadULong(),
+ //0x100 8 RandomSeed
+ RandomSeed = decompressed.ReadULong(),
+ //0x108 8 Header CRC64
+ HeaderCRC64 = decompressed.ReadULong()
+ };
+
+ //Prepare the page data stream to read
+ byte[] arr = await this.getPageBufferAsync(
+ fileheader.CompressedMetadata.PagesMapOffset,
+ fileheader.CompressedMetadata.PagesMapSizeCompressed,
+ fileheader.CompressedMetadata.PagesMapSizeUncompressed,
+ fileheader.CompressedMetadata.PagesMapCorrectionFactor,
+ 0xEF, _fileStream.Stream);
+
+ //Read the page data
+ StreamIO pageDataStream = new StreamIO(arr);
+
+ long offset = 0;
+ while (pageDataStream.Position < pageDataStream.Length)
+ {
+ long size = pageDataStream.ReadLong();
+ long id = Math.Abs(pageDataStream.ReadLong());
+ fileheader.Records.Add((int)id, new DwgSectionLocatorRecord((int)id, (int)offset, (int)size));
+
+ //Add the size to the current offset
+ offset += size;
+ }
+
+ //Prepare the section map data stream to read
+ arr = await this.getPageBufferAsync(
+ (ulong)fileheader.Records[(int)fileheader.CompressedMetadata.SectionsMapId].Seeker,
+ fileheader.CompressedMetadata.SectionsMapSizeCompressed,
+ fileheader.CompressedMetadata.SectionsMapSizeUncompressed,
+ fileheader.CompressedMetadata.SectionsMapCorrectionFactor,
+ 239, _fileStream.Stream);
+
+ //Section map stream
+ StreamIO sectionMapStream = new StreamIO(arr);
+
+ while (sectionMapStream.Position < sectionMapStream.Length)
+ {
+ DwgSectionDescriptor section = new DwgSectionDescriptor();
+ //0x00 8 Data size
+ section.CompressedSize = sectionMapStream.ReadULong();
+ //0x08 8 Max size
+ section.DecompressedSize = sectionMapStream.ReadULong();
+ //0x10 8 Encryption
+ section.Encrypted = (int)sectionMapStream.ReadULong();
+ //0x18 8 HashCode
+ section.HashCode = sectionMapStream.ReadULong();
+ //0x20 8 SectionNameLength
+ int sectionNameLength = (int)sectionMapStream.ReadLong();
+ //0x28 8 Unknown
+ sectionMapStream.ReadULong();
+ //0x30 8 Encoding
+ section.Encoding = sectionMapStream.ReadULong();
+ //0x38 8 NumPages.This is the number of pages present
+ // in the file for the section, but this does not include
+ // pages that contain zeroes only.A page that contains zeroes
+ // only is not written to file.If a page’s data offset is
+ // smaller than the sum of the decompressed size of all previous
+ // pages, then it is to be preceded by a zero page with a size
+ // that is equal to the difference between these two numbers.
+ section.PageCount = (int)sectionMapStream.ReadULong();
+
+ //Read the name
+ if (sectionNameLength > 0)
+ {
+ section.Name = sectionMapStream.ReadString(sectionNameLength, Encoding.Unicode);
+ //Remove the empty characters
+ section.Name = section.Name.Replace("\0", "");
+ }
+
+ ulong currentOffset = 0;
+ for (int i = 0; i < section.PageCount; ++i)
+ {
+ DwgLocalSectionMap page = new DwgLocalSectionMap();
+ //8 Page data offset.If a page’s data offset is
+ // smaller than the sum of the decompressed size
+ // of all previous pages, then it is to be preceded
+ // by a zero page with a size that is equal to the
+ // difference between these two numbers.
+ page.Offset = sectionMapStream.ReadULong();
+ //8 Page Size
+ page.Size = sectionMapStream.ReadLong();
+ //8 Page Id
+ page.PageNumber = (int)sectionMapStream.ReadLong();
+ //8 Page Uncompressed Size
+ page.DecompressedSize = sectionMapStream.ReadULong();
+ //8 Page Compressed Size
+ page.CompressedSize = sectionMapStream.ReadULong();
+ //8 Page Checksum
+ page.Checksum = sectionMapStream.ReadULong();
+ //8 Page CRC
+ page.CRC = sectionMapStream.ReadULong();
+
+ //Add the page to the section
+ section.LocalSections.Add(page);
+ //Move the offset
+ currentOffset = page.Offset + page.DecompressedSize;
+ }
+ if (sectionNameLength > 0)
+ fileheader.Descriptors.Add(section.Name, section);
+ }
+ }
+
+ private void readFileMetaData(DwgFileHeaderAC18 fileheader, IDwgStreamReader sreader)
+ {
+ //5 bytes of 0x00
+ sreader.Advance(5);
+
+ //0x0B 1 Maintenance release version
+ fileheader.AcadMaintenanceVersion = sreader.ReadByte();
+ //0x0C 1 Byte 0x00, 0x01, or 0x03
+ sreader.Advance(1);
+ //0x0D 4 Preview address(long), points to the image page + page header size(0x20).
+ fileheader.PreviewAddress = sreader.ReadRawLong();
+ //0x11 1 Dwg version (Acad version that writes the file)
+ fileheader.DwgVersion = sreader.ReadByte();
+ //0x12 1 Application maintenance release version(Acad maintenance version that writes the file)
+ fileheader.AppReleaseVersion = sreader.ReadByte();
+
+ //0x13 2 Codepage
+ fileheader.DrawingCodePage = CadUtils.GetCodePage(sreader.ReadShort());
+
+ //Advance empty bytes
+ //0x15 3 3 0x00 bytes
+ sreader.Advance(3);
+
+ //0x18 4 SecurityType (long), see R2004 meta data, the definition is the same, paragraph 4.1.
+ fileheader.SecurityType = sreader.ReadRawLong();
+ //0x1C 4 Unknown long
+ sreader.ReadRawLong();
+ //0x20 4 Summary info Address in stream
+ fileheader.SummaryInfoAddr = sreader.ReadRawLong();
+ //0x24 4 VBA Project Addr(0 if not present)
+ fileheader.VbaProjectAddr = sreader.ReadRawLong();
+
+ //0x28 4 0x00000080
+ sreader.ReadRawLong();
+
+ //0x2C 4 App info Address in stream
+ sreader.ReadRawLong();
+
+ //Get to offset 0x80
+ //0x30 0x80 0x00 bytes
+ sreader.Advance(80);
+ }
+
+ private void readEncriptedData(DwgFileHeaderAC18 fileHeader, IDwgStreamReader sreader)
+ {
+ //0x80 0x6C Encrypted Data
+ //Metadata:
+ //The encrypted data at 0x80 can be decrypted by exclusive or’ing the 0x6c bytes of data
+ //from the file with the following magic number sequence:
+
+ //29 23 BE 84 E1 6C D6 AE 52 90 49 F1 F1 BB E9 EB
+ //B3 A6 DB 3C 87 0C 3E 99 24 5E 0D 1C 06 B7 47 DE
+ //B3 12 4D C8 43 BB 8B A6 1F 03 5A 7D 09 38 25 1F
+ //5D D4 CB FC 96 F5 45 3B 13 0D 89 0A 1C DB AE 32
+ //20 9A 50 EE 40 78 36 FD 12 49 32 F6 9E 7D 49 DC
+ //AD 4F 14 F2 44 40 66 D0 6B C4 30 B7
+
+ StreamIO headerStream = new StreamIO(new CRC32StreamHandler(sreader.ReadBytes(0x6C), 0U)); //108
+ headerStream.Encoding = TextEncoding.GetListedEncoding(CodePage.Windows1252);
+
+ //0x00 12 “AcFssFcAJMB” file ID string
+ string fileId = headerStream.ReadString(12);
+ if (fileId != "AcFssFcAJMB\0")
+ {
+ this.notify($"File validation failed, id should be : AcFssFcAJMB\0, but is : {fileId}", NotificationType.Warning);
+ }
+
+ //0x0C 4 0x00(long)
+ headerStream.ReadInt();
+ //0x10 4 0x6c(long)
+ headerStream.ReadInt();
+ //0x14 4 0x04(long)
+ headerStream.ReadInt();
+ //0x18 4 Root tree node gap
+ fileHeader.RootTreeNodeGap = headerStream.ReadInt();
+ //0x1C 4 Lowermost left tree node gap
+ fileHeader.LeftGap = headerStream.ReadInt();
+ //0x20 4 Lowermost right tree node gap
+ fileHeader.RigthGap = headerStream.ReadInt();
+ //0x24 4 Unknown long(ODA writes 1)
+ headerStream.ReadInt();
+ //0x28 4 Last section page Id
+ fileHeader.LastPageId = headerStream.ReadInt();
+
+ //0x2C 8 Last section page end address
+ fileHeader.LastSectionAddr = headerStream.ReadULong();
+ //0x34 8 Second header data address pointing to the repeated header data at the end of the file
+ fileHeader.SecondHeaderAddr = headerStream.ReadULong();
+
+ //0x3C 4 Gap amount
+ fileHeader.GapAmount = headerStream.ReadUInt();
+ //0x40 4 Section page amount
+ fileHeader.SectionAmount = headerStream.ReadUInt();
+ //0x44 4 0x20(long)
+ headerStream.ReadInt();
+ //0x48 4 0x80(long)
+ headerStream.ReadInt();
+ //0x4C 4 0x40(long)
+ headerStream.ReadInt();
+ //0x50 4 Section Page Map Id
+ fileHeader.SectionPageMapId = headerStream.ReadUInt();
+ //0x54 8 Section Page Map address(add 0x100 to this value)
+ fileHeader.PageMapAddress = headerStream.ReadULong() + 256UL;
+ //0x5C 4 Section Map Id
+ fileHeader.SectionMapId = headerStream.ReadUInt();
+ //0x60 4 Section page array size
+ fileHeader.SectionArrayPageSize = headerStream.ReadUInt();
+ //0x64 4 Gap array size
+ fileHeader.GapArraySize = headerStream.ReadUInt();
+ //0x68 4 CRC32(long).See paragraph 2.14.2 for the 32 - bit CRC calculation,
+ // the seed is zero. Note that the CRC
+ // calculation is done including the 4 CRC bytes that are
+ // initially zero! So the CRC calculation takes into account
+ // all of the 0x6c bytes of the data in this table.
+ fileHeader.CRCSeed = headerStream.ReadUInt();
+ }
+
+ private async Task getPageBufferAsync(ulong pageOffset, ulong compressedSize, ulong uncompressedSize, ulong correctionFactor, int blockSize, Stream stream)
+ {
+ //Avoid shifted bits
+ ulong v = compressedSize + 7L;
+ ulong v1 = v & 0b11111111_11111111_11111111_11111000L;
+
+ uint totalSize = (uint)(v1 * correctionFactor);
+
+ int factor = (int)(totalSize + blockSize - 1L) / blockSize;
+ int lenght = factor * byte.MaxValue;
+
+ byte[] buffer = new byte[lenght];
+
+ //Relative to data page map 1, add 0x480 to get stream position
+ stream.Position = (long)(0x480 + pageOffset);
+ await stream.ReadAsync(buffer, 0, lenght);
+
+ byte[] compressedData = new byte[(int)totalSize];
+ this.reedSolomonDecoding(buffer, compressedData, factor, blockSize);
+
+ byte[] decompressedData = new byte[uncompressedSize];
+
+ DwgLZ77AC21Decompressor.Decompress(compressedData, 0U, (uint)compressedSize, decompressedData);
+
+ return decompressedData;
+ }
+
+ private byte[] getPageBuffer(ulong pageOffset, ulong compressedSize, ulong uncompressedSize, ulong correctionFactor, int blockSize, Stream stream)
+ {
+ //Avoid shifted bits
+ ulong v = compressedSize + 7L;
+ ulong v1 = v & 0b11111111_11111111_11111111_11111000L;
+
+ uint totalSize = (uint)(v1 * correctionFactor);
+
+ int factor = (int)(totalSize + blockSize - 1L) / blockSize;
+ int lenght = factor * byte.MaxValue;
+
+ byte[] buffer = new byte[lenght];
+
+ //Relative to data page map 1, add 0x480 to get stream position
+ stream.Position = (long)(0x480 + pageOffset);
+ stream.Read(buffer, 0, lenght);
+
+ byte[] compressedData = new byte[(int)totalSize];
+ this.reedSolomonDecoding(buffer, compressedData, factor, blockSize);
+
+ byte[] decompressedData = new byte[uncompressedSize];
+
+ DwgLZ77AC21Decompressor.Decompress(compressedData, 0U, (uint)compressedSize, decompressedData);
+
+ return decompressedData;
+ }
+
+ private void reedSolomonDecoding(byte[] encoded, byte[] buffer, int factor, int blockSize)
+ {
+ int index = 0;
+ int n = 0;
+ int length = buffer.Length;
+ for (int i = 0; i < factor; ++i)
+ {
+ int cindex = n;
+ if (n < encoded.Length)
+ {
+ int size = Math.Min(length, blockSize);
+ length -= size;
+ int offset = index + size;
+ while (index < offset)
+ {
+ buffer[index] = encoded[cindex];
+ ++index;
+ cindex += factor;
+ }
+ }
+ ++n;
+ }
+ }
+
+ private void getPageHeaderData(IDwgStreamReader sreader,
+ out long sectionType,
+ out long decompressedSize,
+ out long compressedSize,
+ out long compressionType,
+ out long checksum
+ )
+ {
+ //0x00 4 Section page type:
+ //Section page map: 0x41630e3b
+ //Section map: 0x4163003b
+ sectionType = sreader.ReadRawLong();
+ //0x04 4 Decompressed size of the data that follows
+ decompressedSize = sreader.ReadRawLong();
+ //0x08 4 Compressed size of the data that follows(CompDataSize)
+ compressedSize = sreader.ReadRawLong();
+
+ //0x0C 4 Compression type(0x02)
+ compressionType = sreader.ReadRawLong();
+ //0x10 4 Section page checksum
+ checksum = sreader.ReadRawLong();
+ }
+
+ private async Task getHeaderAC15Stream(CancellationToken cancellationToken = default)
+ {
+ bool match = false;
+ List buffer = new List();
+ buffer.AddRange(await this._fileStream.ReadBytesAsync(16, cancellationToken));
+
+ do
+ {
+ byte[] sn = buffer.Skip(Math.Max(0, buffer.Count() - 16)).ToArray();
+ if (CheckSentinel(sn, DwgFileHeaderAC15.EndSentinel))
+ {
+ match = true;
+ }
+ else
+ {
+ buffer.Add(await this._fileStream.ReadByteAsync(cancellationToken));
+ }
+ }
+ while (!match);
+
+ return new MemoryStream(buffer.ToArray());
+ }
+
+ private async Task getStreamChunkAsync(int length, CancellationToken cancellationToken = default)
+ {
+ MemoryStream ms = new MemoryStream(await this._fileStream.ReadBytesAsync(length, cancellationToken));
+ return DwgStreamReaderBase.GetStreamHandler(this._version, ms);
+ }
+
+ private void updateFileVersion(byte[] buffer)
+ {
+ ACadVersion version = CadUtils.GetVersionFromName(Encoding.ASCII.GetString(buffer));
+ this.setVersion(version);
+ }
+ }
+}
diff --git a/src/ACadSharp/IO/DWG/DwgStreamReaders/DwgHeaderReader.cs b/src/ACadSharp/IO/DWG/DwgStreamReaders/DwgHeaderReader.cs
index 30571e412..f9f13b542 100644
--- a/src/ACadSharp/IO/DWG/DwgStreamReaders/DwgHeaderReader.cs
+++ b/src/ACadSharp/IO/DWG/DwgStreamReaders/DwgHeaderReader.cs
@@ -1034,7 +1034,7 @@ public void Read(int acadMaintenanceVersion, out DwgHeaderHandlesCollection obje
//Set the position at the end of the section
mainreader.SetPositionInBits(initialPos + size * 8);
- mainreader.ResetShift();
+ mainreader.ReadCRC();
//Ending sentinel: 0x30,0x84,0xE0,0xDC,0x02,0x21,0xC7,0x56,0xA0,0x83,0x97,0x47,0xB1,0x92,0xCC,0xA0
this.checkSentinel(this._reader, DwgSectionDefinition.EndSentinels[SectionName]);
diff --git a/src/ACadSharp/IO/DWG/DwgStreamReaders/DwgMergedReader.cs b/src/ACadSharp/IO/DWG/DwgStreamReaders/DwgMergedReader.cs
index 7d59bead0..5dbe8b677 100644
--- a/src/ACadSharp/IO/DWG/DwgStreamReaders/DwgMergedReader.cs
+++ b/src/ACadSharp/IO/DWG/DwgStreamReaders/DwgMergedReader.cs
@@ -327,9 +327,9 @@ public string ReadVariableText()
return this._textReader.ReadVariableText();
}
- public ushort ResetShift()
+ public ushort ReadCRC()
{
- return this._mainReader.ResetShift();
+ return this._mainReader.ReadCRC();
}
public void SetPositionInBits(long positon)
diff --git a/src/ACadSharp/IO/DWG/DwgStreamReaders/DwgStreamReaderBase.cs b/src/ACadSharp/IO/DWG/DwgStreamReaders/DwgStreamReaderBase.cs
index 25320c838..4ae482471 100644
--- a/src/ACadSharp/IO/DWG/DwgStreamReaders/DwgStreamReaderBase.cs
+++ b/src/ACadSharp/IO/DWG/DwgStreamReaders/DwgStreamReaderBase.cs
@@ -906,17 +906,17 @@ public void Advance(int offset)
}
///
- public ushort ResetShift()
+ public ushort ReadCRC()
{
//Reset the shift value
if ((uint)this.BitShift > 0U)
this.BitShift = 0;
this.AdvanceByte();
- ushort num = this._lastByte;
+ ushort crc = this._lastByte;
this.AdvanceByte();
- return (ushort)(num | (uint)(ushort)((uint)this._lastByte << 8));
+ return (ushort)(crc | (uint)(ushort)((uint)this._lastByte << 8));
}
#endregion Stream pointer control
diff --git a/src/ACadSharp/IO/DWG/DwgStreamReaders/IDwgStreamReader.cs b/src/ACadSharp/IO/DWG/DwgStreamReaders/IDwgStreamReader.cs
index 38206c867..bdad135d8 100644
--- a/src/ACadSharp/IO/DWG/DwgStreamReaders/IDwgStreamReader.cs
+++ b/src/ACadSharp/IO/DWG/DwgStreamReaders/IDwgStreamReader.cs
@@ -316,10 +316,11 @@ internal interface IDwgStreamReader
///
///
void Advance(int offset);
+
///
- /// Sets the shift displacement to 0.
+ /// CRC: raw short (not compressed)
///
///
- ushort ResetShift();
+ ushort ReadCRC();
}
}
\ No newline at end of file
diff --git a/src/ACadSharp/IO/DXF/DxfDocumentBuilder.cs b/src/ACadSharp/IO/DXF/DxfDocumentBuilder.cs
index aad459e5b..77f0cf519 100644
--- a/src/ACadSharp/IO/DXF/DxfDocumentBuilder.cs
+++ b/src/ACadSharp/IO/DXF/DxfDocumentBuilder.cs
@@ -7,21 +7,14 @@
namespace ACadSharp.IO.DXF
{
- internal class DxfDocumentBuilder : CadDocumentBuilder
+ internal class DxfDocumentBuilder : CadDocumentBuilder
{
- public DxfReaderConfiguration Configuration { get; }
-
public CadBlockRecordTemplate ModelSpaceTemplate { get; set; }
public HashSet ModelSpaceEntities { get; } = new();
- public override bool KeepUnknownEntities => this.Configuration.KeepUnknownEntities;
-
- public override bool KeepUnknownNonGraphicalObjects => this.Configuration.KeepUnknownNonGraphicalObjects;
-
- public DxfDocumentBuilder(ACadVersion version, CadDocument document, DxfReaderConfiguration configuration) : base(version, document)
+ public DxfDocumentBuilder(ACadVersion version, CadDocument document, DxfReaderConfiguration configuration) : base(version, document, configuration)
{
- this.Configuration = configuration;
}
public override void BuildDocument()
diff --git a/src/ACadSharp/IO/DXF/DxfReader.cs b/src/ACadSharp/IO/DXF/DxfReader.cs
index ca2f7ffc4..039815b04 100644
--- a/src/ACadSharp/IO/DXF/DxfReader.cs
+++ b/src/ACadSharp/IO/DXF/DxfReader.cs
@@ -11,6 +11,8 @@
using System.IO;
using System.Linq;
using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
namespace ACadSharp.IO
{
@@ -188,6 +190,13 @@ public override CadDocument Read()
return this._document;
}
+ ///
+ public override async Task ReadAsync(CancellationToken cancellationToken = default)
+ {
+ await Task.CompletedTask;
+ throw new NotImplementedException();
+ }
+
///
public override CadHeader ReadHeader()
{
diff --git a/src/ACadSharp/IO/ICadReader.cs b/src/ACadSharp/IO/ICadReader.cs
index c452d15ba..48ba69ca5 100644
--- a/src/ACadSharp/IO/ICadReader.cs
+++ b/src/ACadSharp/IO/ICadReader.cs
@@ -1,5 +1,7 @@
using ACadSharp.Header;
using System;
+using System.Threading.Tasks;
+using System.Threading;
namespace ACadSharp.IO
{
@@ -22,8 +24,15 @@ public interface ICadReader : IDisposable
CadHeader ReadHeader();
///
- /// Read the cad document.
+ /// Read a cad document.
///
CadDocument Read();
+
+ ///
+ /// Read a cad document asynchronously.
+ ///
+ ///
+ ///
+ Task ReadAsync(CancellationToken cancellationToken = default);
}
}