From 54384eb5f452e7c052cef1bea3eb4ff7b71f8d4a Mon Sep 17 00:00:00 2001 From: DomCR Date: Thu, 7 Dec 2023 07:47:48 +0100 Subject: [PATCH 01/22] added test --- ACadSharp.Tests/IO/DWG/DwgReaderTests.cs | 13 ++++++ ACadSharp/IO/DWG/DwgReader.cs | 44 +++++++++++-------- .../DwgStreamReaders/DwgFileHeaderReader.cs | 17 +++++++ 3 files changed, 55 insertions(+), 19 deletions(-) create mode 100644 ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs diff --git a/ACadSharp.Tests/IO/DWG/DwgReaderTests.cs b/ACadSharp.Tests/IO/DWG/DwgReaderTests.cs index 241b52f8c..f39ed978c 100644 --- a/ACadSharp.Tests/IO/DWG/DwgReaderTests.cs +++ b/ACadSharp.Tests/IO/DWG/DwgReaderTests.cs @@ -1,4 +1,5 @@ using ACadSharp.IO; +using System.Threading.Tasks; using Xunit; using Xunit.Abstractions; @@ -22,6 +23,18 @@ public override void ReadTest(string test) base.ReadTest(test); } + [Theory] + [MemberData(nameof(DwgFilePaths))] + public async Task ReadAsyncTest(string test) + { + CadDocument doc = null; + using (DwgReader reader = new DwgReader(test)) + { + reader.OnNotification += this.onNotification; + doc = await reader.ReadAsync(); + } + } + [Theory] [MemberData(nameof(DwgFilePaths))] public override void AssertDocumentDefaults(string test) diff --git a/ACadSharp/IO/DWG/DwgReader.cs b/ACadSharp/IO/DWG/DwgReader.cs index d863e16a5..52ae0e938 100644 --- a/ACadSharp/IO/DWG/DwgReader.cs +++ b/ACadSharp/IO/DWG/DwgReader.cs @@ -10,6 +10,8 @@ using System.Text; using ACadSharp.Exceptions; using ACadSharp.IO.DWG; +using System.Threading.Tasks; +using System.Threading; namespace ACadSharp.IO { @@ -97,12 +99,24 @@ public static CadDocument Read(string filename, DwgReaderConfiguration configura return doc; } + public async Task ReadAsync(CancellationToken cancellationToken = default) + { + this.initializeReader(); + + //0x00 6 “ACXXXX” version string + byte[] buffer = new byte[6]; + await this._fileStream.Stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken); + ACadVersion version = CadUtils.GetVersionFromName(Encoding.ASCII.GetString(buffer)); + DwgFileHeader fileHeader = DwgFileHeader.CreateFileHeader(version); + + + throw new NotImplementedException(); + } + /// public override CadDocument Read() { - this._document = new CadDocument(false); - this._builder = new DwgDocumentBuilder(this._document, this.Configuration); - this._builder.OnNotification += this.onNotificationEvent; + this.initializeReader(); //Read the file header this._fileHeader = this.readFileHeader(); @@ -226,10 +240,18 @@ public override CadHeader ReadHeader() return header; } + private void initializeReader() + { + this._document = new CadDocument(false); + this._builder = new DwgDocumentBuilder(this._document, this.Configuration); + this._builder.OnNotification += this.onNotificationEvent; + } + /// /// Read the file header data. /// /// + [Obsolete("Use the class DwgFileHeaderReader")] internal DwgFileHeader readFileHeader() { //Reset the stream position at the begining @@ -881,22 +903,6 @@ private void readFileHeaderAC21(DwgFileHeaderAC21 fileheader, IDwgStreamReader s //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 diff --git a/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs b/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs new file mode 100644 index 000000000..2ac37c89a --- /dev/null +++ b/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ACadSharp.IO.DWG.DwgStreamReaders +{ + internal class DwgFileHeaderReader : DwgSectionIO + { + public override string SectionName { get { return string.Empty; } } + + public DwgFileHeaderReader(ACadVersion version) : base(version) + { + } + } +} From 9a73f981d19dc61bb3da0a597b9068f01b0ea656 Mon Sep 17 00:00:00 2001 From: DomCR Date: Thu, 7 Dec 2023 08:13:09 +0100 Subject: [PATCH 02/22] crc --- ACadSharp/IO/DWG/DwgReader.cs | 11 ++++++++-- .../DWG/DwgStreamReaders/DwgClassesReader.cs | 2 +- .../DwgStreamReaders/DwgFileHeaderReader.cs | 20 +++++++++++++------ .../DWG/DwgStreamReaders/DwgHeaderReader.cs | 2 +- .../DWG/DwgStreamReaders/DwgMergedReader.cs | 4 ++-- .../DwgStreamReaders/DwgStreamReaderBase.cs | 6 +++--- .../DWG/DwgStreamReaders/IDwgStreamReader.cs | 5 +++-- 7 files changed, 33 insertions(+), 17 deletions(-) diff --git a/ACadSharp/IO/DWG/DwgReader.cs b/ACadSharp/IO/DWG/DwgReader.cs index 52ae0e938..6143b5fe2 100644 --- a/ACadSharp/IO/DWG/DwgReader.cs +++ b/ACadSharp/IO/DWG/DwgReader.cs @@ -107,8 +107,10 @@ public async Task ReadAsync(CancellationToken cancellationToken = d byte[] buffer = new byte[6]; await this._fileStream.Stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken); ACadVersion version = CadUtils.GetVersionFromName(Encoding.ASCII.GetString(buffer)); - DwgFileHeader fileHeader = DwgFileHeader.CreateFileHeader(version); + DwgFileHeaderReader fileHeaderReader = new DwgFileHeaderReader(version, this._fileStream.Stream); + + DwgFileHeader fileHeader = await fileHeaderReader.ReadAsync(cancellationToken); throw new NotImplementedException(); } @@ -458,7 +460,7 @@ private void readFileHeaderAC15(DwgFileHeaderAC15 fileheader, IDwgStreamReader s } //RS : CRC for BOF to this point. - sreader.ResetShift(); + sreader.ReadCRC(); var sn = sreader.ReadSentinel(); if (!DwgSectionIO.CheckSentinel(sn, DwgFileHeaderAC15.EndSentinel)) @@ -964,6 +966,11 @@ private void readFileMetaData(DwgFileHeaderAC18 fileheader, IDwgStreamReader sre #endregion + private ACadVersion getFileVersion(byte[] buffer) + { + return CadUtils.GetVersionFromName(Encoding.ASCII.GetString(buffer)); + } + private IDwgStreamReader getSectionStream(string sectionName) { Stream sectionStream = null; diff --git a/ACadSharp/IO/DWG/DwgStreamReaders/DwgClassesReader.cs b/ACadSharp/IO/DWG/DwgStreamReaders/DwgClassesReader.cs index 85fd4c7b5..728e08714 100644 --- a/ACadSharp/IO/DWG/DwgStreamReaders/DwgClassesReader.cs +++ b/ACadSharp/IO/DWG/DwgStreamReaders/DwgClassesReader.cs @@ -128,7 +128,7 @@ public DxfClassCollection Read(IDwgStreamReader sreader) } //RS: CRC - sreader.ResetShift(); + sreader.ReadCRC(); //0x72,0x5E,0x3B,0x47,0x3B,0x56,0x07,0x3A,0x3F,0x23,0x0B,0xA0,0x18,0x30,0x49,0x75 this.checkSentinel(sreader, DwgSectionDefinition.EndSentinels[SectionName]); diff --git a/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs b/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs index 2ac37c89a..9cb5c9661 100644 --- a/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs +++ b/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs @@ -1,17 +1,25 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using CSUtilities.IO; +using System; +using System.IO; +using System.Threading; using System.Threading.Tasks; -namespace ACadSharp.IO.DWG.DwgStreamReaders +namespace ACadSharp.IO.DWG { internal class DwgFileHeaderReader : DwgSectionIO { public override string SectionName { get { return string.Empty; } } - public DwgFileHeaderReader(ACadVersion version) : base(version) + private StreamIO _stream; + + public DwgFileHeaderReader(ACadVersion version, Stream stream) : base(version) + { + this._stream = new StreamIO(stream); + } + + public async Task ReadAsync(CancellationToken cancellationToken = default) { + throw new NotImplementedException(); } } } diff --git a/ACadSharp/IO/DWG/DwgStreamReaders/DwgHeaderReader.cs b/ACadSharp/IO/DWG/DwgStreamReaders/DwgHeaderReader.cs index 1d8ec1dd9..aed635d64 100644 --- a/ACadSharp/IO/DWG/DwgStreamReaders/DwgHeaderReader.cs +++ b/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/ACadSharp/IO/DWG/DwgStreamReaders/DwgMergedReader.cs b/ACadSharp/IO/DWG/DwgStreamReaders/DwgMergedReader.cs index b92dfae51..f779d21f9 100644 --- a/ACadSharp/IO/DWG/DwgStreamReaders/DwgMergedReader.cs +++ b/ACadSharp/IO/DWG/DwgStreamReaders/DwgMergedReader.cs @@ -323,9 +323,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/ACadSharp/IO/DWG/DwgStreamReaders/DwgStreamReaderBase.cs b/ACadSharp/IO/DWG/DwgStreamReaders/DwgStreamReaderBase.cs index 85d86b8c1..d4705d9ca 100644 --- a/ACadSharp/IO/DWG/DwgStreamReaders/DwgStreamReaderBase.cs +++ b/ACadSharp/IO/DWG/DwgStreamReaders/DwgStreamReaderBase.cs @@ -892,17 +892,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/ACadSharp/IO/DWG/DwgStreamReaders/IDwgStreamReader.cs b/ACadSharp/IO/DWG/DwgStreamReaders/IDwgStreamReader.cs index 62d31cbda..36b3d5ab5 100644 --- a/ACadSharp/IO/DWG/DwgStreamReaders/IDwgStreamReader.cs +++ b/ACadSharp/IO/DWG/DwgStreamReaders/IDwgStreamReader.cs @@ -306,10 +306,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 From b1a1075dd36343bd8695182508b9a9eba575cd57 Mon Sep 17 00:00:00 2001 From: DomCR Date: Thu, 7 Dec 2023 09:16:25 +0100 Subject: [PATCH 03/22] dev proj --- ACadSharp.sln | 3 ++ .../DwgStreamReaders/DwgFileHeaderReader.cs | 50 ++++++++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/ACadSharp.sln b/ACadSharp.sln index c27c22ec1..b15139aeb 100644 --- a/ACadSharp.sln +++ b/ACadSharp.sln @@ -45,6 +45,8 @@ Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "CSMath", "CSUtilities\CSMat EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{824FC7E8-4401-428C-87E7-2EA8A78D54BB}" EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "CSUtilities", "..\CSUtilities\CSUtilities\CSUtilities.shproj", "{375A58CE-9BCB-4995-9DCF-0EB6961EA973}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -85,6 +87,7 @@ Global SolutionGuid = {B40C2E48-575E-40D1-88C5-272437A5683F} EndGlobalSection GlobalSection(SharedMSBuildProjectFiles) = preSolution + ..\CSUtilities\CSUtilities\CSUtilities.projitems*{375a58ce-9bcb-4995-9dcf-0eb6961ea973}*SharedItemsImports = 13 CSUtilities\CSMath\CSMath.projitems*{411b6122-5ef6-46db-a8f2-43e9c1841e4a}*SharedItemsImports = 13 CSUtilities\CSMath\CSMath.projitems*{66b978f6-8ed4-4e14-bd43-bbb86664adb3}*SharedItemsImports = 5 CSUtilities\CSUtilities\CSUtilities.projitems*{66b978f6-8ed4-4e14-bd43-bbb86664adb3}*SharedItemsImports = 5 diff --git a/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs b/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs index 9cb5c9661..9e1b383d5 100644 --- a/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs +++ b/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs @@ -1,4 +1,5 @@ -using CSUtilities.IO; +using ACadSharp.Exceptions; +using CSUtilities.IO; using System; using System.IO; using System.Threading; @@ -21,5 +22,52 @@ public async Task ReadAsync(CancellationToken cancellationToken = { throw new NotImplementedException(); } + + public DwgFileHeader Read() + { + DwgFileHeader fileHeader = DwgFileHeader.CreateFileHeader(this._version); + + //Reset the stream position at the begining + this._stream.Position = 0L; + + //Read the file header + switch (fileHeader.AcadVersion) + { + case ACadVersion.Unknown: + throw new DwgNotSupportedException(); + 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 DwgNotSupportedException(this._version); + case ACadVersion.AC1012: + case ACadVersion.AC1014: + case ACadVersion.AC1015: + this.readFileHeaderAC15(fileHeader as DwgFileHeaderAC15, _stream); + break; + case ACadVersion.AC1018: + this.readFileHeaderAC18(fileHeader as DwgFileHeaderAC18, _stream); + break; + case ACadVersion.AC1021: + this.readFileHeaderAC21(fileHeader as DwgFileHeaderAC21, _stream); + break; + case ACadVersion.AC1024: + case ACadVersion.AC1027: + case ACadVersion.AC1032: + //Check if it works... + this.readFileHeaderAC18(fileHeader as DwgFileHeaderAC18, _stream); + break; + default: + break; + } + + return fileHeader; + } } } From 7ad2aab6129881de026237ad032ece5a3e7bc65a Mon Sep 17 00:00:00 2001 From: DomCR Date: Thu, 7 Dec 2023 10:11:25 +0100 Subject: [PATCH 04/22] async submodule --- ACadSharp.sln | 2 +- ACadSharp/ACadSharp.csproj | 2 +- .../DwgStreamReaders/DwgFileHeaderReader.cs | 675 +++++++++++++++++- 3 files changed, 667 insertions(+), 12 deletions(-) diff --git a/ACadSharp.sln b/ACadSharp.sln index b15139aeb..831944a4d 100644 --- a/ACadSharp.sln +++ b/ACadSharp.sln @@ -90,7 +90,7 @@ Global ..\CSUtilities\CSUtilities\CSUtilities.projitems*{375a58ce-9bcb-4995-9dcf-0eb6961ea973}*SharedItemsImports = 13 CSUtilities\CSMath\CSMath.projitems*{411b6122-5ef6-46db-a8f2-43e9c1841e4a}*SharedItemsImports = 13 CSUtilities\CSMath\CSMath.projitems*{66b978f6-8ed4-4e14-bd43-bbb86664adb3}*SharedItemsImports = 5 - CSUtilities\CSUtilities\CSUtilities.projitems*{66b978f6-8ed4-4e14-bd43-bbb86664adb3}*SharedItemsImports = 5 + ..\CSUtilities\CSUtilities\CSUtilities.projitems*{66b978f6-8ed4-4e14-bd43-bbb86664adb3}*SharedItemsImports = 5 CSUtilities\CSUtilities\CSUtilities.projitems*{b4ef345d-52b9-47f1-aa2a-b4ae85f62efc}*SharedItemsImports = 13 EndGlobalSection EndGlobal diff --git a/ACadSharp/ACadSharp.csproj b/ACadSharp/ACadSharp.csproj index 203b3c207..472b1b94a 100644 --- a/ACadSharp/ACadSharp.csproj +++ b/ACadSharp/ACadSharp.csproj @@ -22,7 +22,7 @@ - + diff --git a/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs b/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs index 9e1b383d5..cba207015 100644 --- a/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs +++ b/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs @@ -1,7 +1,14 @@ using ACadSharp.Exceptions; +using CSUtilities.Converters; using CSUtilities.IO; +using CSUtilities.Text; using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; using System.IO; +using System.Linq; +using System.Runtime.InteropServices.ComTypes; +using System.Text; using System.Threading; using System.Threading.Tasks; @@ -11,16 +18,60 @@ internal class DwgFileHeaderReader : DwgSectionIO { public override string SectionName { get { return string.Empty; } } - private StreamIO _stream; + private StreamIO _fileStream; public DwgFileHeaderReader(ACadVersion version, Stream stream) : base(version) { - this._stream = new StreamIO(stream); + this._fileStream = new StreamIO(stream); } public async Task ReadAsync(CancellationToken cancellationToken = default) { - throw new NotImplementedException(); + DwgFileHeader fileHeader = DwgFileHeader.CreateFileHeader(this._version); + + //Reset the stream position at the begining + this._fileStream.Position = 0L; + IDwgStreamReader sreader = null; + + //Read the file header + switch (fileHeader.AcadVersion) + { + case ACadVersion.Unknown: + throw new DwgNotSupportedException(); + 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 DwgNotSupportedException(this._version); + case ACadVersion.AC1012: + case ACadVersion.AC1014: + case ACadVersion.AC1015: + sreader = DwgStreamReaderBase.GetStreamHandler(_version, await getHeaderAC15Stream(cancellationToken)); + this.readFileHeaderAC15(fileHeader as DwgFileHeaderAC15, sreader); + break; + case ACadVersion.AC1018: + sreader = DwgStreamReaderBase.GetStreamHandler(_version, await getHeaderAC18Stream(cancellationToken)); + this.readFileHeaderAC18(fileHeader as DwgFileHeaderAC18, sreader); + break; + case ACadVersion.AC1021: + sreader = DwgStreamReaderBase.GetStreamHandler(_version, await getHeaderAC21Stream(cancellationToken)); + this.readFileHeaderAC21(fileHeader as DwgFileHeaderAC21, sreader); + break; + case ACadVersion.AC1024: + case ACadVersion.AC1027: + case ACadVersion.AC1032: + sreader = DwgStreamReaderBase.GetStreamHandler(_version, await getHeaderAC18Stream(cancellationToken)); + this.readFileHeaderAC18(fileHeader as DwgFileHeaderAC18, sreader); + break; + } + + return fileHeader; } public DwgFileHeader Read() @@ -28,7 +79,10 @@ public DwgFileHeader Read() DwgFileHeader fileHeader = DwgFileHeader.CreateFileHeader(this._version); //Reset the stream position at the begining - this._stream.Position = 0L; + this._fileStream.Position = 0L; + + //Get the stream reader + IDwgStreamReader sreader = DwgStreamReaderBase.GetStreamHandler(_version, _fileStream.Stream); //Read the file header switch (fileHeader.AcadVersion) @@ -49,25 +103,626 @@ public DwgFileHeader Read() case ACadVersion.AC1012: case ACadVersion.AC1014: case ACadVersion.AC1015: - this.readFileHeaderAC15(fileHeader as DwgFileHeaderAC15, _stream); + this.readFileHeaderAC15(fileHeader as DwgFileHeaderAC15, sreader); break; case ACadVersion.AC1018: - this.readFileHeaderAC18(fileHeader as DwgFileHeaderAC18, _stream); + this.readFileHeaderAC18(fileHeader as DwgFileHeaderAC18, sreader); break; case ACadVersion.AC1021: - this.readFileHeaderAC21(fileHeader as DwgFileHeaderAC21, _stream); + 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, _stream); - break; - default: + this.readFileHeaderAC18(fileHeader as DwgFileHeaderAC18, sreader); 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); + + 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") + { + 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; + //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); + } + + //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 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 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 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 getHeaderAC18Stream(CancellationToken cancellationToken = default) + { + ulong metadataSize = 0; + + throw new NotImplementedException(); + } + + private async Task getHeaderAC21Stream(CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } } } From 8e5f429267204ec7d9d611deb6bf0ee53a11039c Mon Sep 17 00:00:00 2001 From: DomCR Date: Thu, 7 Dec 2023 10:22:34 +0100 Subject: [PATCH 05/22] DwgFileHeaderReader sync --- .../Internal/DwgFileHeaderExploration.cs | 4 ++-- ACadSharp/IO/DWG/DwgReader.cs | 15 ++++++++------- .../DWG/DwgStreamReaders/DwgFileHeaderReader.cs | 5 ----- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/ACadSharp.Tests/Internal/DwgFileHeaderExploration.cs b/ACadSharp.Tests/Internal/DwgFileHeaderExploration.cs index f82043d03..0c755463a 100644 --- a/ACadSharp.Tests/Internal/DwgFileHeaderExploration.cs +++ b/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/ACadSharp/IO/DWG/DwgReader.cs b/ACadSharp/IO/DWG/DwgReader.cs index 6143b5fe2..c050dc75a 100644 --- a/ACadSharp/IO/DWG/DwgReader.cs +++ b/ACadSharp/IO/DWG/DwgReader.cs @@ -106,7 +106,7 @@ public async Task ReadAsync(CancellationToken cancellationToken = d //0x00 6 “ACXXXX” version string byte[] buffer = new byte[6]; await this._fileStream.Stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken); - ACadVersion version = CadUtils.GetVersionFromName(Encoding.ASCII.GetString(buffer)); + ACadVersion version = this.getFileVersion(buffer); DwgFileHeaderReader fileHeaderReader = new DwgFileHeaderReader(version, this._fileStream.Stream); @@ -249,13 +249,14 @@ private void initializeReader() this._builder.OnNotification += this.onNotificationEvent; } - /// - /// Read the file header data. - /// - /// - [Obsolete("Use the class DwgFileHeaderReader")] - internal DwgFileHeader readFileHeader() + private DwgFileHeader readFileHeader() { + DwgFileHeaderReader reader = new DwgFileHeaderReader(this.getFileVersion(this._fileStream.ReadBytes(6)), this._fileStream.Stream); + this._fileHeader = reader.Read(); + this._encoding = getListedEncoding((int)_fileHeader.DrawingCodePage); + + return this._fileHeader; + //Reset the stream position at the begining this._fileStream.Position = 0L; diff --git a/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs b/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs index cba207015..ca67fc339 100644 --- a/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs +++ b/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs @@ -4,10 +4,8 @@ using CSUtilities.Text; using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.IO; using System.Linq; -using System.Runtime.InteropServices.ComTypes; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -78,9 +76,6 @@ public DwgFileHeader Read() { DwgFileHeader fileHeader = DwgFileHeader.CreateFileHeader(this._version); - //Reset the stream position at the begining - this._fileStream.Position = 0L; - //Get the stream reader IDwgStreamReader sreader = DwgStreamReaderBase.GetStreamHandler(_version, _fileStream.Stream); From 83dd6442d2640a5dc42fb1b2eb3acdbcdf95aafd Mon Sep 17 00:00:00 2001 From: DomCR Date: Thu, 7 Dec 2023 10:25:40 +0100 Subject: [PATCH 06/22] cleanup --- ACadSharp/IO/DWG/DwgReader.cs | 631 ---------------------------------- 1 file changed, 631 deletions(-) diff --git a/ACadSharp/IO/DWG/DwgReader.cs b/ACadSharp/IO/DWG/DwgReader.cs index c050dc75a..f8340b5db 100644 --- a/ACadSharp/IO/DWG/DwgReader.cs +++ b/ACadSharp/IO/DWG/DwgReader.cs @@ -1,8 +1,5 @@ using ACadSharp.Classes; using ACadSharp.Header; -using CSUtilities.IO; -using CSUtilities.Converters; -using CSUtilities.Text; using System; using System.Collections.Generic; using System.IO; @@ -256,55 +253,6 @@ private DwgFileHeader readFileHeader() this._encoding = getListedEncoding((int)_fileHeader.DrawingCodePage); return this._fileHeader; - - //Reset the stream position at the begining - this._fileStream.Position = 0L; - - //0x00 6 “ACXXXX” version string - ACadVersion version = CadUtils.GetVersionFromName(this._fileStream.ReadString(6, Encoding.ASCII)); - DwgFileHeader fileHeader = DwgFileHeader.CreateFileHeader(version); - - //Get the stream reader - IDwgStreamReader sreader = DwgStreamReaderBase.GetStreamHandler(fileHeader.AcadVersion, this._fileStream.Stream); - - //Read the file header - switch (fileHeader.AcadVersion) - { - case ACadVersion.Unknown: - throw new DwgNotSupportedException(); - 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 DwgNotSupportedException(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; - } - - return fileHeader; } /// @@ -425,548 +373,6 @@ 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) - { - //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.ReadCRC(); - - var sn = sreader.ReadSentinel(); - if (!DwgSectionIO.CheckSentinel(sn, DwgFileHeaderAC15.EndSentinel)) - { - this.triggerNotification($"Invalid section sentinel found in FileHeader", NotificationType.Warning); - } - } - - /// - /// 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") - { - this.triggerNotification($"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; - //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); - } - - //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 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(); - } - - /// - /// Read the file header for the AC1021 (2007-2009) version of the header. - /// - /// File header to read - /// - 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); - } - } - - /// - /// 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); - } - - #endregion - private ACadVersion getFileVersion(byte[] buffer) { return CadUtils.GetVersionFromName(Encoding.ASCII.GetString(buffer)); @@ -1216,42 +622,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; - } } } From d08f3166b7632f26b69452141d5e4c0cb3f0769f Mon Sep 17 00:00:00 2001 From: DomCR Date: Thu, 7 Dec 2023 19:42:20 +0100 Subject: [PATCH 07/22] read file header async AC1018 --- .../DwgStreamReaders/DwgFileHeaderReader.cs | 255 ++++++++++++++++-- 1 file changed, 239 insertions(+), 16 deletions(-) diff --git a/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs b/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs index ca67fc339..4e36104f6 100644 --- a/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs +++ b/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs @@ -14,6 +14,10 @@ namespace ACadSharp.IO.DWG { internal class DwgFileHeaderReader : DwgSectionIO { + private const int _metaDataSize = 0x80; + + private const int _encriptedDataSize = 0x6C; + public override string SectionName { get { return string.Empty; } } private StreamIO _fileStream; @@ -23,13 +27,12 @@ public DwgFileHeaderReader(ACadVersion version, Stream stream) : base(version) this._fileStream = new StreamIO(stream); } - public async Task ReadAsync(CancellationToken cancellationToken = default) + public DwgFileHeader Read() { DwgFileHeader fileHeader = DwgFileHeader.CreateFileHeader(this._version); - //Reset the stream position at the begining - this._fileStream.Position = 0L; - IDwgStreamReader sreader = null; + //Get the stream reader + IDwgStreamReader sreader = DwgStreamReaderBase.GetStreamHandler(_version, _fileStream.Stream); //Read the file header switch (fileHeader.AcadVersion) @@ -50,21 +53,18 @@ public async Task ReadAsync(CancellationToken cancellationToken = case ACadVersion.AC1012: case ACadVersion.AC1014: case ACadVersion.AC1015: - sreader = DwgStreamReaderBase.GetStreamHandler(_version, await getHeaderAC15Stream(cancellationToken)); this.readFileHeaderAC15(fileHeader as DwgFileHeaderAC15, sreader); break; case ACadVersion.AC1018: - sreader = DwgStreamReaderBase.GetStreamHandler(_version, await getHeaderAC18Stream(cancellationToken)); this.readFileHeaderAC18(fileHeader as DwgFileHeaderAC18, sreader); break; case ACadVersion.AC1021: - sreader = DwgStreamReaderBase.GetStreamHandler(_version, await getHeaderAC21Stream(cancellationToken)); this.readFileHeaderAC21(fileHeader as DwgFileHeaderAC21, sreader); break; case ACadVersion.AC1024: case ACadVersion.AC1027: case ACadVersion.AC1032: - sreader = DwgStreamReaderBase.GetStreamHandler(_version, await getHeaderAC18Stream(cancellationToken)); + //Check if it works... this.readFileHeaderAC18(fileHeader as DwgFileHeaderAC18, sreader); break; } @@ -72,12 +72,13 @@ public async Task ReadAsync(CancellationToken cancellationToken = return fileHeader; } - public DwgFileHeader Read() + public async Task ReadAsync(CancellationToken cancellationToken = default) { DwgFileHeader fileHeader = DwgFileHeader.CreateFileHeader(this._version); - //Get the stream reader - IDwgStreamReader sreader = DwgStreamReaderBase.GetStreamHandler(_version, _fileStream.Stream); + //Reset the stream position at the begining + this._fileStream.Position = 0L; + IDwgStreamReader sreader = null; //Read the file header switch (fileHeader.AcadVersion) @@ -98,18 +99,20 @@ public DwgFileHeader Read() case ACadVersion.AC1012: case ACadVersion.AC1014: case ACadVersion.AC1015: + sreader = DwgStreamReaderBase.GetStreamHandler(_version, await getHeaderAC15Stream(cancellationToken)); this.readFileHeaderAC15(fileHeader as DwgFileHeaderAC15, sreader); break; case ACadVersion.AC1018: - this.readFileHeaderAC18(fileHeader as DwgFileHeaderAC18, sreader); + await this.readFileHeaderAC18Async(fileHeader as DwgFileHeaderAC18, cancellationToken); break; case ACadVersion.AC1021: + sreader = DwgStreamReaderBase.GetStreamHandler(_version, await getHeaderAC21Stream(cancellationToken)); this.readFileHeaderAC21(fileHeader as DwgFileHeaderAC21, sreader); break; case ACadVersion.AC1024: case ACadVersion.AC1027: case ACadVersion.AC1032: - //Check if it works... + sreader = DwgStreamReaderBase.GetStreamHandler(_version, await getHeaderAC18Stream(cancellationToken)); this.readFileHeaderAC18(fileHeader as DwgFileHeaderAC18, sreader); break; } @@ -173,8 +176,6 @@ private void readFileHeaderAC18(DwgFileHeaderAC18 fileheader, IDwgStreamReader s 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 @@ -279,8 +280,10 @@ private void readFileHeaderAC18(DwgFileHeaderAC18 fileheader, IDwgStreamReader s #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); @@ -354,6 +357,145 @@ pages to the section until this condition is met. #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); @@ -610,6 +752,80 @@ private void readFileMetaData(DwgFileHeaderAC18 fileheader, IDwgStreamReader sre 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 byte[] getPageBuffer(ulong pageOffset, ulong compressedSize, ulong uncompressedSize, ulong correctionFactor, int blockSize, Stream stream) { //Avoid shifted bits @@ -708,9 +924,16 @@ private async Task getHeaderAC15Stream(CancellationToken cancellationTok 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 async Task getHeaderAC18Stream(CancellationToken cancellationToken = default) { - ulong metadataSize = 0; + ulong metadataSize = 0x80; + ulong fileHeaderSize = 0x400; throw new NotImplementedException(); } From 90da53514c2792718f5dda1509fb4ed881dd8f45 Mon Sep 17 00:00:00 2001 From: DomCR Date: Fri, 8 Dec 2023 10:48:18 +0100 Subject: [PATCH 08/22] AC1021 start --- .../DwgStreamReaders/DwgFileHeaderReader.cs | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs b/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs index 4e36104f6..ee6067304 100644 --- a/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs +++ b/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs @@ -106,13 +106,11 @@ public async Task ReadAsync(CancellationToken cancellationToken = await this.readFileHeaderAC18Async(fileHeader as DwgFileHeaderAC18, cancellationToken); break; case ACadVersion.AC1021: - sreader = DwgStreamReaderBase.GetStreamHandler(_version, await getHeaderAC21Stream(cancellationToken)); - this.readFileHeaderAC21(fileHeader as DwgFileHeaderAC21, sreader); + await this.readFileHeaderAC21Async(fileHeader as DwgFileHeaderAC21, cancellationToken); break; case ACadVersion.AC1024: case ACadVersion.AC1027: case ACadVersion.AC1032: - sreader = DwgStreamReaderBase.GetStreamHandler(_version, await getHeaderAC18Stream(cancellationToken)); this.readFileHeaderAC18(fileHeader as DwgFileHeaderAC18, sreader); break; } @@ -709,6 +707,20 @@ private void readFileHeaderAC21(DwgFileHeaderAC21 fileheader, IDwgStreamReader s } } + 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); + + } private void readFileMetaData(DwgFileHeaderAC18 fileheader, IDwgStreamReader sreader) { //5 bytes of 0x00 @@ -930,14 +942,6 @@ private async Task getStreamChunkAsync(int length, Cancellatio return DwgStreamReaderBase.GetStreamHandler(this._version, ms); } - private async Task getHeaderAC18Stream(CancellationToken cancellationToken = default) - { - ulong metadataSize = 0x80; - ulong fileHeaderSize = 0x400; - - throw new NotImplementedException(); - } - private async Task getHeaderAC21Stream(CancellationToken cancellationToken = default) { throw new NotImplementedException(); From abd91e894a317b4067bcdf31524c91d725d1bbfc Mon Sep 17 00:00:00 2001 From: DomCR Date: Sun, 10 Dec 2023 19:55:27 +0100 Subject: [PATCH 09/22] getSectionBuffer15Async --- ACadSharp/IO/DWG/DwgReader.cs | 55 +++++++++++++------ .../DwgStreamReaders/DwgFileHeaderReader.cs | 4 +- 2 files changed, 41 insertions(+), 18 deletions(-) diff --git a/ACadSharp/IO/DWG/DwgReader.cs b/ACadSharp/IO/DWG/DwgReader.cs index f8340b5db..ce6725462 100644 --- a/ACadSharp/IO/DWG/DwgReader.cs +++ b/ACadSharp/IO/DWG/DwgReader.cs @@ -96,22 +96,6 @@ public static CadDocument Read(string filename, DwgReaderConfiguration configura return doc; } - public async Task ReadAsync(CancellationToken cancellationToken = default) - { - this.initializeReader(); - - //0x00 6 “ACXXXX” version string - byte[] buffer = new byte[6]; - await this._fileStream.Stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken); - ACadVersion version = this.getFileVersion(buffer); - - DwgFileHeaderReader fileHeaderReader = new DwgFileHeaderReader(version, this._fileStream.Stream); - - DwgFileHeader fileHeader = await fileHeaderReader.ReadAsync(cancellationToken); - - throw new NotImplementedException(); - } - /// public override CadDocument Read() { @@ -133,6 +117,21 @@ public override CadDocument Read() return this._document; } + public async Task ReadAsync(CancellationToken cancellationToken = default) + { + this.initializeReader(); + + //0x00 6 “ACXXXX” version string + byte[] buffer = new byte[6]; + await this._fileStream.Stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken); + ACadVersion version = this.getFileVersion(buffer); + + DwgFileHeaderReader fileHeaderReader = new DwgFileHeaderReader(version, this._fileStream.Stream); + this._fileHeader = await fileHeaderReader.ReadAsync(cancellationToken); + + throw new NotImplementedException(); + } + /// /// Read the summary info of the dwg file. /// @@ -250,7 +249,7 @@ private DwgFileHeader readFileHeader() { DwgFileHeaderReader reader = new DwgFileHeaderReader(this.getFileVersion(this._fileStream.ReadBytes(6)), this._fileStream.Stream); this._fileHeader = reader.Read(); - this._encoding = getListedEncoding((int)_fileHeader.DrawingCodePage); + this._encoding = this.getListedEncoding((int)_fileHeader.DrawingCodePage); return this._fileHeader; } @@ -453,6 +452,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)) diff --git a/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs b/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs index ee6067304..dfd0029e6 100644 --- a/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs +++ b/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs @@ -111,7 +111,7 @@ public async Task ReadAsync(CancellationToken cancellationToken = case ACadVersion.AC1024: case ACadVersion.AC1027: case ACadVersion.AC1032: - this.readFileHeaderAC18(fileHeader as DwgFileHeaderAC18, sreader); + await this.readFileHeaderAC18Async(fileHeader as DwgFileHeaderAC18, cancellationToken); break; } @@ -720,7 +720,9 @@ private async Task readFileHeaderAC21Async(DwgFileHeaderAC21 fileheader, Cancell byte[] decodedData = new byte[3 * 239]; //factor * blockSize this.reedSolomonDecoding(compressedData, decodedData, 3, 239); + throw new NotSupportedException(); } + private void readFileMetaData(DwgFileHeaderAC18 fileheader, IDwgStreamReader sreader) { //5 bytes of 0x00 From 08abdc065a517cbd4e88af9bb2f1f9b4d4323f10 Mon Sep 17 00:00:00 2001 From: DomCR Date: Tue, 12 Dec 2023 15:46:23 +0100 Subject: [PATCH 10/22] summary --- ACadSharp/DwgPreview.cs | 9 +- ACadSharp/IO/DWG/DwgReader.cs | 161 ++++++++++++++++-- ACadSharp/IO/DWG/DwgSectionIO.cs | 29 ++-- .../DwgStreamReaders/DwgFileHeaderReader.cs | 23 ++- 4 files changed, 186 insertions(+), 36 deletions(-) diff --git a/ACadSharp/DwgPreview.cs b/ACadSharp/DwgPreview.cs index 8462bd9aa..b90c9fdb6 100644 --- a/ACadSharp/DwgPreview.cs +++ b/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/ACadSharp/IO/DWG/DwgReader.cs b/ACadSharp/IO/DWG/DwgReader.cs index ce6725462..539e9a5cd 100644 --- a/ACadSharp/IO/DWG/DwgReader.cs +++ b/ACadSharp/IO/DWG/DwgReader.cs @@ -16,6 +16,8 @@ public class DwgReader : CadReaderBase { public DwgReaderConfiguration Configuration { get; set; } = new DwgReaderConfiguration(); + private ACadVersion _version = ACadVersion.Unknown; + private DwgDocumentBuilder _builder; private DwgFileHeader _fileHeader; @@ -121,15 +123,16 @@ public async Task ReadAsync(CancellationToken cancellationToken = d { this.initializeReader(); - //0x00 6 “ACXXXX” version string - byte[] buffer = new byte[6]; - await this._fileStream.Stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken); - ACadVersion version = this.getFileVersion(buffer); + this._fileHeader = await this.readFileHeaderAsync(cancellationToken); - DwgFileHeaderReader fileHeaderReader = new DwgFileHeaderReader(version, this._fileStream.Stream); - this._fileHeader = await fileHeaderReader.ReadAsync(cancellationToken); + if (this._fileHeader.AcadVersion >= ACadVersion.AC1018) + { + this._document.SummaryInfo = this.readSummaryInfo(await this.getSectionStreamAsync(DwgSectionDefinition.SummaryInfo)); + } - throw new NotImplementedException(); + this._document.Header = this.readHeader(await this.getSectionStreamAsync(DwgSectionDefinition.Header)); + + return this._document; } /// @@ -155,6 +158,12 @@ public CadSummaryInfo ReadSummaryInfo() return summaryReader.Read(); } + private CadSummaryInfo readSummaryInfo(IDwgStreamReader reader) + { + DwgSummaryInfoReader summaryReader = new DwgSummaryInfoReader(this._fileHeader.AcadVersion, reader); + return summaryReader.Read(); + } + /// /// Read the preview image of the dwg file. /// @@ -162,6 +171,7 @@ public CadSummaryInfo ReadSummaryInfo() /// Refers to AcDb:Preview data section. /// /// + /// public DwgPreview ReadPreview() { this._fileHeader = this._fileHeader ?? this.readFileHeader(); @@ -238,6 +248,22 @@ public override CadHeader ReadHeader() return header; } + private CadHeader readHeader(IDwgStreamReader sreader) + { + CadHeader header = new CadHeader(); + header.CodePage = CadUtils.GetCodePageName(this._fileHeader.DrawingCodePage); + + DwgHeaderReader hReader = new DwgHeaderReader(this._fileHeader.AcadVersion, sreader, header); + hReader.OnNotification += onNotificationEvent; + + hReader.Read(this._fileHeader.AcadMaintenanceVersion, out DwgHeaderHandlesCollection headerHandles); + + if (this._builder != null) + this._builder.HeaderHandles = headerHandles; + + return header; + } + private void initializeReader() { this._document = new CadDocument(false); @@ -247,9 +273,20 @@ private void initializeReader() private DwgFileHeader readFileHeader() { - DwgFileHeaderReader reader = new DwgFileHeaderReader(this.getFileVersion(this._fileStream.ReadBytes(6)), this._fileStream.Stream); + DwgFileHeaderReader reader = new DwgFileHeaderReader(this._fileStream.Stream); this._fileHeader = reader.Read(); - this._encoding = this.getListedEncoding((int)_fileHeader.DrawingCodePage); + + this.setFileVersion(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.setFileVersion(this._fileHeader); return this._fileHeader; } @@ -372,9 +409,10 @@ private void readObjects() sectionReader.Read(); } - private ACadVersion getFileVersion(byte[] buffer) + private void setFileVersion(DwgFileHeader fileHeader) { - return CadUtils.GetVersionFromName(Encoding.ASCII.GetString(buffer)); + this._version = fileHeader.AcadVersion; + this._encoding = this.getListedEncoding((int)_fileHeader.DrawingCodePage); } private IDwgStreamReader getSectionStream(string sectionName) @@ -401,7 +439,6 @@ private IDwgStreamReader getSectionStream(string sectionName) case ACadVersion.AC1014: case ACadVersion.AC1015: sectionStream = this.getSectionBuffer15(this._fileHeader as DwgFileHeaderAC15, sectionName); - //encoding = TextEncoding.GetListedEncoding((this._fileHeader as DwgFileHeaderAC15).DrawingCodePage); break; case ACadVersion.AC1018: sectionStream = this.getSectionBuffer18(this._fileHeader as DwgFileHeaderAC18, sectionName); @@ -431,6 +468,60 @@ private IDwgStreamReader getSectionStream(string sectionName) return streamHandler; } + private async Task getSectionStreamAsync(string sectionName, CancellationToken cancellationToken = default) + { + Stream sectionStream = null; + + //Get the section buffer + switch (this._fileHeader.AcadVersion) + { + case ACadVersion.Unknown: + throw new DwgNotSupportedException(); + 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 DwgNotSupportedException(this._fileHeader.AcadVersion); + case ACadVersion.AC1012: + case ACadVersion.AC1014: + case ACadVersion.AC1015: + sectionStream = await this.getSectionBuffer15Async(this._fileHeader as DwgFileHeaderAC15, sectionName, cancellationToken); + break; + case ACadVersion.AC1018: + sectionStream = await this.getSectionBuffer18Async(this._fileHeader as DwgFileHeaderAC18, sectionName, cancellationToken); + 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 = await this.getSectionBuffer18Async(this._fileHeader as DwgFileHeaderAC18, sectionName, cancellationToken); + break; + default: + break; + } + + //Section not found + if (sectionStream == null) + return null; + + IDwgStreamReader streamHandler = DwgStreamReaderBase.GetStreamHandler(this._fileHeader.AcadVersion, sectionStream); + + //Set the encoding if needed + streamHandler.Encoding = this._encoding; + + return streamHandler; + } + + private Stream getSectionBuffer15(DwgFileHeaderAC15 fileheader, string sectionName) { Stream stream = null; @@ -520,6 +611,52 @@ private Stream getSectionBuffer18(DwgFileHeaderAC18 fileheader, string sectionNa return memoryStream; } + private async Task getSectionBuffer18Async(DwgFileHeaderAC18 fileheader, string sectionName, CancellationToken cancellationToken = default) + { + 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 + { + this._fileStream.Position = section.Seeker; + byte[] buffer = await this._fileStream.ReadBytesAsync((int)section.CompressedSize); + + //Get the page section header + IDwgStreamReader sreader = DwgStreamReaderBase.GetStreamHandler(fileheader.AcadVersion, new MemoryStream(buffer)); + //Get the header data + this.decryptDataSection(section, sreader); + + if (descriptor.IsCompressed) + { + //Page is compressed + DwgLZ77AC18Decompressor.DecompressToDest(sreader.Stream, memoryStream); + } + else + { + //Read the stream normally + memoryStream.Write(await this._fileStream.ReadBytesAsync((int)section.CompressedSize), 0, (int)section.CompressedSize); + } + } + } + + //Reset the stream + memoryStream.Position = 0L; + return memoryStream; + } + private void decryptDataSection(DwgLocalSectionMap section, IDwgStreamReader sreader) { int secMask = 0x4164536B ^ (int)sreader.Position; diff --git a/ACadSharp/IO/DWG/DwgSectionIO.cs b/ACadSharp/IO/DWG/DwgSectionIO.cs index f11c4dc85..e4ab0b028 100644 --- a/ACadSharp/IO/DWG/DwgSectionIO.cs +++ b/ACadSharp/IO/DWG/DwgSectionIO.cs @@ -45,21 +45,11 @@ internal abstract class DwgSectionIO /// protected bool R2018Plus; - protected readonly ACadVersion _version; + protected ACadVersion _version; public DwgSectionIO(ACadVersion version) { - _version = version; - - R13_14Only = version == ACadVersion.AC1014 || version == ACadVersion.AC1012; - R13_15Only = version >= ACadVersion.AC1012 && version <= ACadVersion.AC1015; - R2000Plus = version >= ACadVersion.AC1015; - R2004Pre = version < ACadVersion.AC1018; - R2004Plus = version >= ACadVersion.AC1018; - R2007Plus = version >= ACadVersion.AC1021; - R2010Plus = version >= ACadVersion.AC1024; - R2013Plus = version >= ACadVersion.AC1027; - R2018Plus = version >= ACadVersion.AC1032; + this.setVersion(version); } public static bool CheckSentinel(byte[] actual, byte[] expected) @@ -78,6 +68,21 @@ public static bool CheckSentinel(byte[] actual, byte[] expected) return true; } + protected void setVersion(ACadVersion version) + { + _version = version; + + R13_14Only = version == ACadVersion.AC1014 || version == ACadVersion.AC1012; + R13_15Only = version >= ACadVersion.AC1012 && version <= ACadVersion.AC1015; + R2000Plus = version >= ACadVersion.AC1015; + R2004Pre = version < ACadVersion.AC1018; + R2004Plus = version >= ACadVersion.AC1018; + R2007Plus = version >= ACadVersion.AC1021; + R2010Plus = version >= ACadVersion.AC1024; + R2013Plus = version >= ACadVersion.AC1027; + R2018Plus = version >= ACadVersion.AC1032; + } + protected void checkSentinel(IDwgStreamReader sreader, byte[] expected) { var sn = sreader.ReadSentinel(); diff --git a/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs b/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs index dfd0029e6..a69ac8f60 100644 --- a/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs +++ b/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs @@ -22,17 +22,24 @@ internal class DwgFileHeaderReader : DwgSectionIO private StreamIO _fileStream; - public DwgFileHeaderReader(ACadVersion version, Stream stream) : base(version) + 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(_version, _fileStream.Stream); + IDwgStreamReader sreader = DwgStreamReaderBase.GetStreamHandler(this._version, _fileStream.Stream); //Read the file header switch (fileHeader.AcadVersion) @@ -74,6 +81,13 @@ public DwgFileHeader Read() 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); //Reset the stream position at the begining @@ -944,9 +958,10 @@ private async Task getStreamChunkAsync(int length, Cancellatio return DwgStreamReaderBase.GetStreamHandler(this._version, ms); } - private async Task getHeaderAC21Stream(CancellationToken cancellationToken = default) + private void updateFileVersion(byte[] buffer) { - throw new NotImplementedException(); + ACadVersion version = CadUtils.GetVersionFromName(Encoding.ASCII.GetString(buffer)); + this.setVersion(version); } } } From 24ceb49d70e6b6c2b6cec86e2d0d27456ed48ca7 Mon Sep 17 00:00:00 2001 From: DomCR Date: Tue, 13 Feb 2024 21:54:50 +0100 Subject: [PATCH 11/22] submodule --- CSUtilities | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CSUtilities b/CSUtilities index bce7847f2..9ba1dc223 160000 --- a/CSUtilities +++ b/CSUtilities @@ -1 +1 @@ -Subproject commit bce7847f206d9cb636644101cf2534370f7594fd +Subproject commit 9ba1dc2233a65c5a8e29e8efdccbb2fd0c4d974d From d989943493ca483f23317516691266077492e1c4 Mon Sep 17 00:00:00 2001 From: DomCR Date: Mon, 19 Feb 2024 18:11:29 +0100 Subject: [PATCH 12/22] async buffer --- ACadSharp/IO/DWG/DwgReader.cs | 19 ++++++++++--------- .../DwgStreamReaders/DwgStreamReaderBase.cs | 2 +- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/ACadSharp/IO/DWG/DwgReader.cs b/ACadSharp/IO/DWG/DwgReader.cs index 5d82cca12..ff3997715 100644 --- a/ACadSharp/IO/DWG/DwgReader.cs +++ b/ACadSharp/IO/DWG/DwgReader.cs @@ -157,8 +157,7 @@ public CadSummaryInfo ReadSummaryInfo() if (reader == null) return null; - DwgSummaryInfoReader summaryReader = new DwgSummaryInfoReader(this._fileHeader.AcadVersion, reader); - return summaryReader.Read(); + return this.readSummaryInfo(reader); } private CadSummaryInfo readSummaryInfo(IDwgStreamReader reader) @@ -596,7 +595,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) @@ -613,7 +611,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) { @@ -655,13 +653,14 @@ private async Task getSectionBuffer18Async(DwgFileHeaderAC18 fileheader, } else { + int dataSecitonSize = 32; this._fileStream.Position = section.Seeker; - byte[] buffer = await this._fileStream.ReadBytesAsync((int)section.CompressedSize); + 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); + this.decryptDataSection(section, sreader, section.Seeker); if (descriptor.IsCompressed) { @@ -671,7 +670,9 @@ private async Task getSectionBuffer18Async(DwgFileHeaderAC18 fileheader, else { //Read the stream normally - memoryStream.Write(await this._fileStream.ReadBytesAsync((int)section.CompressedSize), 0, (int)section.CompressedSize); + byte[] b = new byte[section.CompressedSize]; + sreader.Stream.Read(b, 0, (int)section.CompressedSize); + memoryStream.Write(b, 0, (int)section.CompressedSize); } } } @@ -681,9 +682,9 @@ private async Task getSectionBuffer18Async(DwgFileHeaderAC18 fileheader, return memoryStream; } - private void decryptDataSection(DwgLocalSectionMap section, IDwgStreamReader sreader) + private void decryptDataSection(DwgLocalSectionMap section, IDwgStreamReader sreader, long seeker) { - int secMask = 0x4164536B ^ (int)sreader.Position; + int secMask = 0x4164536B ^ (int)seeker; //0x00 4 Section page type, since it’s always a data section: 0x4163043b var pageType = sreader.ReadRawLong() ^ secMask; diff --git a/ACadSharp/IO/DWG/DwgStreamReaders/DwgStreamReaderBase.cs b/ACadSharp/IO/DWG/DwgStreamReaders/DwgStreamReaderBase.cs index d4705d9ca..d56455cc9 100644 --- a/ACadSharp/IO/DWG/DwgStreamReaders/DwgStreamReaderBase.cs +++ b/ACadSharp/IO/DWG/DwgStreamReaders/DwgStreamReaderBase.cs @@ -369,7 +369,7 @@ public char ReadRawChar() /// public long ReadRawLong() { - return this.ReadInt(); + return this.ReadInt(); } /// From aad1663124a257e3eb6a05cbf902a8c533c9d275 Mon Sep 17 00:00:00 2001 From: DomCR Date: Mon, 19 Feb 2024 18:27:27 +0100 Subject: [PATCH 13/22] submodules --- ACadSharp.sln | 5 +-- ACadSharp/ACadSharp.csproj | 78 +++++++++++++++++++------------------- CSUtilities | 2 +- 3 files changed, 41 insertions(+), 44 deletions(-) diff --git a/ACadSharp.sln b/ACadSharp.sln index 831944a4d..c27c22ec1 100644 --- a/ACadSharp.sln +++ b/ACadSharp.sln @@ -45,8 +45,6 @@ Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "CSMath", "CSUtilities\CSMat EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{824FC7E8-4401-428C-87E7-2EA8A78D54BB}" EndProject -Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "CSUtilities", "..\CSUtilities\CSUtilities\CSUtilities.shproj", "{375A58CE-9BCB-4995-9DCF-0EB6961EA973}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -87,10 +85,9 @@ Global SolutionGuid = {B40C2E48-575E-40D1-88C5-272437A5683F} EndGlobalSection GlobalSection(SharedMSBuildProjectFiles) = preSolution - ..\CSUtilities\CSUtilities\CSUtilities.projitems*{375a58ce-9bcb-4995-9dcf-0eb6961ea973}*SharedItemsImports = 13 CSUtilities\CSMath\CSMath.projitems*{411b6122-5ef6-46db-a8f2-43e9c1841e4a}*SharedItemsImports = 13 CSUtilities\CSMath\CSMath.projitems*{66b978f6-8ed4-4e14-bd43-bbb86664adb3}*SharedItemsImports = 5 - ..\CSUtilities\CSUtilities\CSUtilities.projitems*{66b978f6-8ed4-4e14-bd43-bbb86664adb3}*SharedItemsImports = 5 + CSUtilities\CSUtilities\CSUtilities.projitems*{66b978f6-8ed4-4e14-bd43-bbb86664adb3}*SharedItemsImports = 5 CSUtilities\CSUtilities\CSUtilities.projitems*{b4ef345d-52b9-47f1-aa2a-b4ae85f62efc}*SharedItemsImports = 13 EndGlobalSection EndGlobal diff --git a/ACadSharp/ACadSharp.csproj b/ACadSharp/ACadSharp.csproj index d1cab2f3e..3fd79c15e 100644 --- a/ACadSharp/ACadSharp.csproj +++ b/ACadSharp/ACadSharp.csproj @@ -1,43 +1,43 @@  - - net6.0;net7.0;net5.0;net48;netstandard2.1 - DomCr - ACadSharp - C# Dwg Dxf - https://github.com/DomCR/ACadSharp - MIT - git - https://github.com/DomCR/ACadSharp - Copyright (c) 2024 Albert Domenech - C# library to read/write cad files like dxf/dwg. - True - - - - true - README.md - 2.1.0-beta - - - - - - - - - True - \ - - - - - - - - - - - + + net6.0;net7.0;net5.0;net48;netstandard2.1 + DomCr + ACadSharp + C# Dwg Dxf + https://github.com/DomCR/ACadSharp + MIT + git + https://github.com/DomCR/ACadSharp + Copyright (c) 2024 Albert Domenech + C# library to read/write cad files like dxf/dwg. + True + + + + true + README.md + 2.1.0-beta + + + + + + + + + True + \ + + + + + + + + + + + diff --git a/CSUtilities b/CSUtilities index 9ba1dc223..833d0fa5f 160000 --- a/CSUtilities +++ b/CSUtilities @@ -1 +1 @@ -Subproject commit 9ba1dc2233a65c5a8e29e8efdccbb2fd0c4d974d +Subproject commit 833d0fa5f36d006b09dd9381f5702842519f3d67 From fea50fe3cf64e28424b8baab60c2b1debee17a46 Mon Sep 17 00:00:00 2001 From: DomCR Date: Mon, 19 Feb 2024 19:44:50 +0100 Subject: [PATCH 14/22] metadata read fix --- ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs b/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs index a69ac8f60..d24b1cfba 100644 --- a/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs +++ b/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs @@ -14,7 +14,7 @@ namespace ACadSharp.IO.DWG { internal class DwgFileHeaderReader : DwgSectionIO { - private const int _metaDataSize = 0x80; + private const int _metaDataSize = 0x7A; private const int _encriptedDataSize = 0x6C; @@ -90,10 +90,6 @@ public async Task ReadAsync(CancellationToken cancellationToken = DwgFileHeader fileHeader = DwgFileHeader.CreateFileHeader(this._version); - //Reset the stream position at the begining - this._fileStream.Position = 0L; - IDwgStreamReader sreader = null; - //Read the file header switch (fileHeader.AcadVersion) { @@ -113,7 +109,7 @@ public async Task ReadAsync(CancellationToken cancellationToken = case ACadVersion.AC1012: case ACadVersion.AC1014: case ACadVersion.AC1015: - sreader = DwgStreamReaderBase.GetStreamHandler(_version, await getHeaderAC15Stream(cancellationToken)); + IDwgStreamReader sreader = DwgStreamReaderBase.GetStreamHandler(_version, await getHeaderAC15Stream(cancellationToken)); this.readFileHeaderAC15(fileHeader as DwgFileHeaderAC15, sreader); break; case ACadVersion.AC1018: @@ -932,7 +928,6 @@ private async Task getHeaderAC15Stream(CancellationToken cancellationTok { bool match = false; List buffer = new List(); - buffer.AddRange(await this._fileStream.ReadBytesAsync(16, cancellationToken)); do From 0574a1085303a3427e91f44f9a29247496069a58 Mon Sep 17 00:00:00 2001 From: DomCR Date: Thu, 22 Feb 2024 18:39:56 +0100 Subject: [PATCH 15/22] readobjects --- ACadSharp/IO/DWG/DwgReader.cs | 55 ++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 8 deletions(-) diff --git a/ACadSharp/IO/DWG/DwgReader.cs b/ACadSharp/IO/DWG/DwgReader.cs index ff3997715..ff8e28aaa 100644 --- a/ACadSharp/IO/DWG/DwgReader.cs +++ b/ACadSharp/IO/DWG/DwgReader.cs @@ -109,9 +109,9 @@ public override CadDocument Read() this._document.SummaryInfo = this.ReadSummaryInfo(); this._document.Header = this.ReadHeader(); - this._document.Classes = this.readClasses(); + 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(); @@ -130,10 +130,19 @@ public async Task ReadAsync(CancellationToken cancellationToken = d if (this._fileHeader.AcadVersion >= ACadVersion.AC1018) { - this._document.SummaryInfo = this.readSummaryInfo(await this.getSectionStreamAsync(DwgSectionDefinition.SummaryInfo)); + this._document.SummaryInfo = this.readSummaryInfo(await this.getSectionStreamAsync(DwgSectionDefinition.SummaryInfo, cancellationToken)); } - this._document.Header = this.readHeader(await this.getSectionStreamAsync(DwgSectionDefinition.Header)); + 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; } @@ -300,11 +309,11 @@ private async Task readFileHeaderAsync(CancellationToken cancella /// 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, this._fileHeader); reader.OnNotification += onNotificationEvent; @@ -312,7 +321,7 @@ private DxfClassCollection readClasses() return reader.Read(sreader); } - private void readAppInfo() + private void readAppInfo(IDwgStreamReader sreader) { #if TEST this._fileHeader = this._fileHeader ?? this.readFileHeader(); @@ -403,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) @@ -432,6 +440,37 @@ private void readObjects() sectionReader.Read(); } + private async Task readObjectsAsync() + { + Dictionary handles = this.readHandles(); + + IDwgStreamReader sreader = null; + if (this._fileHeader.AcadVersion <= ACadVersion.AC1015) + { + sreader = DwgStreamReaderBase.GetStreamHandler(this._fileHeader.AcadVersion, this._fileStream.Stream); + //Handles are in absolute offset for this versions + sreader.Position = 0; + } + else + { + sreader = await this.getSectionStreamAsync(DwgSectionDefinition.AcDbObjects); + } + + Queue objectHandles = new Queue(this._builder.HeaderHandles.GetHandles() + .Where(o => o.HasValue) + .Select(a => a.Value)); + + DwgObjectReader sectionReader = new DwgObjectReader( + this._fileHeader.AcadVersion, + this._builder, + sreader, + objectHandles, + handles, + this._document.Classes); + + sectionReader.Read(); + } + private void setFileVersion(DwgFileHeader fileHeader) { this._version = fileHeader.AcadVersion; From 41b54c8ad3d39c2ad3b3ee118a18ef8b9e7b81e9 Mon Sep 17 00:00:00 2001 From: DomCR Date: Thu, 9 May 2024 12:32:21 +0200 Subject: [PATCH 16/22] AC21 --- .../DwgStreamReaders/DwgFileHeaderReader.cs | 228 +++++++++++++++++- 1 file changed, 226 insertions(+), 2 deletions(-) diff --git a/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs b/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs index d24b1cfba..af9acfb27 100644 --- a/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs +++ b/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs @@ -726,11 +726,208 @@ private async Task readFileHeaderAC21Async(DwgFileHeaderAC21 fileheader, Cancell //(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); - throw new NotSupportedException(); + //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) @@ -850,6 +1047,33 @@ private void readEncriptedData(DwgFileHeaderAC18 fileHeader, IDwgStreamReader sr 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 From 00745c0e87039e11077392636dd884452d45d720 Mon Sep 17 00:00:00 2001 From: DomCR Date: Thu, 9 May 2024 12:49:42 +0200 Subject: [PATCH 17/22] version fix --- ACadSharp/IO/DWG/DwgSectionIO.cs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/ACadSharp/IO/DWG/DwgSectionIO.cs b/ACadSharp/IO/DWG/DwgSectionIO.cs index cdcb06284..8e30058cb 100644 --- a/ACadSharp/IO/DWG/DwgSectionIO.cs +++ b/ACadSharp/IO/DWG/DwgSectionIO.cs @@ -74,17 +74,18 @@ public static bool CheckSentinel(byte[] actual, byte[] expected) protected void setVersion(ACadVersion version) { - _version = version; + this._version = version; - R13_14Only = version == ACadVersion.AC1014 || version == ACadVersion.AC1012; - R13_15Only = version >= ACadVersion.AC1012 && version <= ACadVersion.AC1015; - R2000Plus = version >= ACadVersion.AC1015; - R2004Pre = version < ACadVersion.AC1018; - R2004Plus = version >= ACadVersion.AC1018; - R2007Plus = version >= ACadVersion.AC1021; - R2010Plus = version >= ACadVersion.AC1024; - R2013Plus = version >= ACadVersion.AC1027; - R2018Plus = version >= ACadVersion.AC1032; + 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) From 3ac6df46bf0417f521a3cc21585b1efe51e6c6d0 Mon Sep 17 00:00:00 2001 From: DomCR Date: Wed, 22 Jan 2025 12:34:00 +0100 Subject: [PATCH 18/22] build fix --- src/ACadSharp/IO/CadDocumentBuilder.cs | 11 ++++++ src/ACadSharp/IO/CadReaderBase.cs | 5 +++ src/ACadSharp/IO/DWG/DwgDocumentBuilder.cs | 9 +---- src/ACadSharp/IO/DWG/DwgReader.cs | 37 ++++++++----------- .../DWG/DwgStreamReaders/DwgClassesReader.cs | 2 +- .../DwgStreamReaders/DwgFileHeaderReader.cs | 8 ++-- src/ACadSharp/IO/DXF/DxfReader.cs | 8 ++++ src/ACadSharp/IO/ICadReader.cs | 11 +++++- 8 files changed, 57 insertions(+), 34 deletions(-) diff --git a/src/ACadSharp/IO/CadDocumentBuilder.cs b/src/ACadSharp/IO/CadDocumentBuilder.cs index 02a8c84b9..ba80f6e1e 100644 --- a/src/ACadSharp/IO/CadDocumentBuilder.cs +++ b/src/ACadSharp/IO/CadDocumentBuilder.cs @@ -297,4 +297,15 @@ 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; } + + 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..7e9914d03 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(); @@ -23,9 +19,8 @@ internal class DwgDocumentBuilder : CadDocumentBuilder 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 18de34ec8..37329958e 100644 --- a/src/ACadSharp/IO/DWG/DwgReader.cs +++ b/src/ACadSharp/IO/DWG/DwgReader.cs @@ -102,20 +102,15 @@ public static CadDocument Read(string filename, DwgReaderConfiguration configura /// public override CadDocument Read() { - this.initializeReader(); - this._builder = new DwgDocumentBuilder(this._document, this.Configuration); - this._builder.OnNotification += this.onNotificationEvent; - //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.Classes = this.readClasses(); + this._document.Classes = this.readClasses(this.getSectionStream(DwgSectionDefinition.Classes)); this.readAppInfo(this.getSectionStream(DwgSectionDefinition.AppInfo)); @@ -128,12 +123,12 @@ public override CadDocument Read() return this._document; } - public async Task ReadAsync(CancellationToken cancellationToken = default) + public override async Task ReadAsync(CancellationToken cancellationToken = default) { - this.initializeReader(); - 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)); @@ -273,27 +268,28 @@ private CadHeader readHeader(IDwgStreamReader sreader) DwgHeaderReader hReader = new DwgHeaderReader(this._fileHeader.AcadVersion, sreader, header); hReader.OnNotification += onNotificationEvent; + hReader.Read(this._fileHeader.AcadMaintenanceVersion, out DwgHeaderHandlesCollection headerHandles); + if (this._builder != null) this._builder.HeaderHandles = headerHandles; return header; } - private void initializeReader() + private void initializeReader(ACadVersion version) { this._document = new CadDocument(false); - this._builder = new DwgDocumentBuilder(this._document, this.Configuration); + + this._builder = new DwgDocumentBuilder(version, this._document, this.Configuration); this._builder.OnNotification += this.onNotificationEvent; } - break; - } private DwgFileHeader readFileHeader() { DwgFileHeaderReader reader = new DwgFileHeaderReader(this._fileStream.Stream); this._fileHeader = reader.Read(); - this.setFileVersion(this._fileHeader); + this.setFileEncoding(this._fileHeader); return this._fileHeader; } @@ -303,7 +299,7 @@ private async Task readFileHeaderAsync(CancellationToken cancella DwgFileHeaderReader reader = new DwgFileHeaderReader(this._fileStream.Stream); this._fileHeader = await reader.ReadAsync(cancellationToken); - this.setFileVersion(this._fileHeader); + this.setFileEncoding(this._fileHeader); return this._fileHeader; } @@ -477,9 +473,8 @@ private async Task readObjectsAsync() sectionReader.Read(); } - private void setFileVersion(DwgFileHeader fileHeader) + private void setFileEncoding(DwgFileHeader fileHeader) { - this._version = fileHeader.AcadVersion; this._encoding = this.getListedEncoding((int)_fileHeader.DrawingCodePage); } @@ -491,7 +486,7 @@ private IDwgStreamReader getSectionStream(string sectionName) switch (this._fileHeader.AcadVersion) { case ACadVersion.Unknown: - throw new DwgNotSupportedException(); + throw new CadNotSupportedException(); case ACadVersion.MC0_0: case ACadVersion.AC1_2: case ACadVersion.AC1_4: @@ -502,7 +497,7 @@ private IDwgStreamReader getSectionStream(string sectionName) case ACadVersion.AC1004: case ACadVersion.AC1006: case ACadVersion.AC1009: - throw new DwgNotSupportedException(this._fileHeader.AcadVersion); + throw new CadNotSupportedException(this._fileHeader.AcadVersion); case ACadVersion.AC1012: case ACadVersion.AC1014: case ACadVersion.AC1015: 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 index af9acfb27..556d8cb7e 100644 --- a/src/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs +++ b/src/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs @@ -45,7 +45,7 @@ public DwgFileHeader Read() switch (fileHeader.AcadVersion) { case ACadVersion.Unknown: - throw new DwgNotSupportedException(); + throw new CadNotSupportedException(); case ACadVersion.MC0_0: case ACadVersion.AC1_2: case ACadVersion.AC1_4: @@ -56,7 +56,7 @@ public DwgFileHeader Read() case ACadVersion.AC1004: case ACadVersion.AC1006: case ACadVersion.AC1009: - throw new DwgNotSupportedException(this._version); + throw new CadNotSupportedException(this._version); case ACadVersion.AC1012: case ACadVersion.AC1014: case ACadVersion.AC1015: @@ -94,7 +94,7 @@ public async Task ReadAsync(CancellationToken cancellationToken = switch (fileHeader.AcadVersion) { case ACadVersion.Unknown: - throw new DwgNotSupportedException(); + throw new CadNotSupportedException(); case ACadVersion.MC0_0: case ACadVersion.AC1_2: case ACadVersion.AC1_4: @@ -105,7 +105,7 @@ public async Task ReadAsync(CancellationToken cancellationToken = case ACadVersion.AC1004: case ACadVersion.AC1006: case ACadVersion.AC1009: - throw new DwgNotSupportedException(this._version); + throw new CadNotSupportedException(this._version); case ACadVersion.AC1012: case ACadVersion.AC1014: case ACadVersion.AC1015: diff --git a/src/ACadSharp/IO/DXF/DxfReader.cs b/src/ACadSharp/IO/DXF/DxfReader.cs index ca2f7ffc4..17adb8700 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,12 @@ 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); } } From fc6f278164c1547f066635a9f7b9cf2185b40a65 Mon Sep 17 00:00:00 2001 From: DomCR Date: Wed, 22 Jan 2025 12:36:41 +0100 Subject: [PATCH 19/22] test fix --- src/ACadSharp.Tests/IO/DWG/DwgReaderTests.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ACadSharp.Tests/IO/DWG/DwgReaderTests.cs b/src/ACadSharp.Tests/IO/DWG/DwgReaderTests.cs index f2004985f..959edb59a 100644 --- a/src/ACadSharp.Tests/IO/DWG/DwgReaderTests.cs +++ b/src/ACadSharp.Tests/IO/DWG/DwgReaderTests.cs @@ -1,4 +1,6 @@ using ACadSharp.IO; +using ACadSharp.Tests.TestModels; +using System.Threading.Tasks; using Xunit; using Xunit.Abstractions; @@ -27,7 +29,7 @@ public override void ReadTest(FileModel test) public async Task ReadAsyncTest(FileModel test) { CadDocument doc = null; - using (DwgReader reader = new DwgReader(test)) + using (DwgReader reader = new DwgReader(test.Path)) { reader.OnNotification += this.onNotification; doc = await reader.ReadAsync(); From 705d6c608b49ffd72b6d16ab9f297a724cde9948 Mon Sep 17 00:00:00 2001 From: DomCR Date: Wed, 22 Jan 2025 12:47:14 +0100 Subject: [PATCH 20/22] header link --- src/ACadSharp/CadDocument.cs | 12 +++++++++++- src/ACadSharp/IO/DWG/DwgReader.cs | 6 ++---- 2 files changed, 13 insertions(+), 5 deletions(-) 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/IO/DWG/DwgReader.cs b/src/ACadSharp/IO/DWG/DwgReader.cs index 37329958e..c21a3676f 100644 --- a/src/ACadSharp/IO/DWG/DwgReader.cs +++ b/src/ACadSharp/IO/DWG/DwgReader.cs @@ -4,10 +4,8 @@ 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; @@ -18,8 +16,6 @@ namespace ACadSharp.IO /// public class DwgReader : CadReaderBase { - public DwgReaderConfiguration Configuration { get; set; } = new DwgReaderConfiguration(); - private DwgDocumentBuilder _builder; private DwgFileHeader _fileHeader; @@ -255,7 +251,9 @@ public override CadHeader ReadHeader() hReader.Read(this._fileHeader.AcadMaintenanceVersion, out DwgHeaderHandlesCollection headerHandles); if (this._builder != null) + { this._builder.HeaderHandles = headerHandles; + } return header; } From afe41bf604af90268db0b9bb9370f0596ac80b03 Mon Sep 17 00:00:00 2001 From: DomCR Date: Wed, 22 Jan 2025 12:52:17 +0100 Subject: [PATCH 21/22] builder config --- src/ACadSharp/IO/CadDocumentBuilder.cs | 4 ++++ src/ACadSharp/IO/DWG/DwgDocumentBuilder.cs | 4 ---- src/ACadSharp/IO/DXF/DxfDocumentBuilder.cs | 13 +++---------- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/src/ACadSharp/IO/CadDocumentBuilder.cs b/src/ACadSharp/IO/CadDocumentBuilder.cs index ba80f6e1e..39ef8bf1f 100644 --- a/src/ACadSharp/IO/CadDocumentBuilder.cs +++ b/src/ACadSharp/IO/CadDocumentBuilder.cs @@ -303,6 +303,10 @@ internal abstract class CadDocumentBuilder : CadDocumentBuilder { 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/DWG/DwgDocumentBuilder.cs b/src/ACadSharp/IO/DWG/DwgDocumentBuilder.cs index 7e9914d03..68393ceee 100644 --- a/src/ACadSharp/IO/DWG/DwgDocumentBuilder.cs +++ b/src/ACadSharp/IO/DWG/DwgDocumentBuilder.cs @@ -14,10 +14,6 @@ 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, configuration) { diff --git a/src/ACadSharp/IO/DXF/DxfDocumentBuilder.cs b/src/ACadSharp/IO/DXF/DxfDocumentBuilder.cs index 431d95dcf..94bc035fe 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() @@ -37,7 +30,7 @@ public override void BuildDocument() } this.ModelSpaceTemplate.OwnedObjectsHandlers.AddRange(this.ModelSpaceEntities); - + this.RegisterTables(); this.BuildTables(); From b3e7a6d0d5ea684e3b26e46b1e0ad7fe0c8b1d15 Mon Sep 17 00:00:00 2001 From: DomCR Date: Sun, 9 Feb 2025 12:07:06 +0100 Subject: [PATCH 22/22] doc --- src/ACadSharp/IO/DXF/DxfReader.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ACadSharp/IO/DXF/DxfReader.cs b/src/ACadSharp/IO/DXF/DxfReader.cs index 17adb8700..039815b04 100644 --- a/src/ACadSharp/IO/DXF/DxfReader.cs +++ b/src/ACadSharp/IO/DXF/DxfReader.cs @@ -190,6 +190,7 @@ public override CadDocument Read() return this._document; } + /// public override async Task ReadAsync(CancellationToken cancellationToken = default) { await Task.CompletedTask;