• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

DomCR / ACadSharp / 12907528789

22 Jan 2025 11:47AM UTC coverage: 76.371% (+74.4%) from 2.0%
12907528789

Pull #283

github

web-flow
Merge 705d6c608 into 5c1c15065
Pull Request #283: Issue 200 async Read

5397 of 7806 branches covered (69.14%)

Branch coverage included in aggregate %.

785 of 817 new or added lines in 11 files covered. (96.08%)

2 existing lines in 1 file now uncovered.

21569 of 27503 relevant lines covered (78.42%)

39686.13 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.26
/src/ACadSharp/IO/DWG/DwgStreamReaders/DwgFileHeaderReader.cs
1
using ACadSharp.Exceptions;
2
using CSUtilities.Converters;
3
using CSUtilities.IO;
4
using CSUtilities.Text;
5
using System;
6
using System.Collections.Generic;
7
using System.IO;
8
using System.Linq;
9
using System.Text;
10
using System.Threading;
11
using System.Threading.Tasks;
12

13
namespace ACadSharp.IO.DWG
14
{
15
        internal class DwgFileHeaderReader : DwgSectionIO
16
        {
17
                private const int _metaDataSize = 0x7A;
18

19
                private const int _encriptedDataSize = 0x6C;
20

NEW
21
                public override string SectionName { get { return string.Empty; } }
×
22

23
                private StreamIO _fileStream;
24

25
                public DwgFileHeaderReader(Stream stream) : base(ACadVersion.Unknown)
136✔
26
                {
136✔
27
                        this._fileStream = new StreamIO(stream);
136✔
28
                }
136✔
29

30
                public DwgFileHeader Read()
31
                {
129✔
32
                        this._fileStream.Position = 0;
129✔
33

34
                        //0x00        6        “ACXXXX” version string
35
                        byte[] buffer = new byte[6];
129✔
36
                        this._fileStream.Stream.Read(buffer, 0, buffer.Length);
129✔
37
                        this.updateFileVersion(buffer);
129✔
38

39
                        DwgFileHeader fileHeader = DwgFileHeader.CreateFileHeader(this._version);
129✔
40

41
                        //Get the stream reader
42
                        IDwgStreamReader sreader = DwgStreamReaderBase.GetStreamHandler(this._version, _fileStream.Stream);
129✔
43

44
                        //Read the file header
45
                        switch (fileHeader.AcadVersion)
129!
46
                        {
47
                                case ACadVersion.Unknown:
NEW
48
                                        throw new CadNotSupportedException();
×
49
                                case ACadVersion.MC0_0:
50
                                case ACadVersion.AC1_2:
51
                                case ACadVersion.AC1_4:
52
                                case ACadVersion.AC1_50:
53
                                case ACadVersion.AC2_10:
54
                                case ACadVersion.AC1002:
55
                                case ACadVersion.AC1003:
56
                                case ACadVersion.AC1004:
57
                                case ACadVersion.AC1006:
58
                                case ACadVersion.AC1009:
NEW
59
                                        throw new CadNotSupportedException(this._version);
×
60
                                case ACadVersion.AC1012:
61
                                case ACadVersion.AC1014:
62
                                case ACadVersion.AC1015:
63
                                        this.readFileHeaderAC15(fileHeader as DwgFileHeaderAC15, sreader);
35✔
64
                                        break;
35✔
65
                                case ACadVersion.AC1018:
66
                                        this.readFileHeaderAC18(fileHeader as DwgFileHeaderAC18, sreader);
19✔
67
                                        break;
19✔
68
                                case ACadVersion.AC1021:
69
                                        this.readFileHeaderAC21(fileHeader as DwgFileHeaderAC21, sreader);
14✔
70
                                        break;
14✔
71
                                case ACadVersion.AC1024:
72
                                case ACadVersion.AC1027:
73
                                case ACadVersion.AC1032:
74
                                        //Check if it works...
75
                                        this.readFileHeaderAC18(fileHeader as DwgFileHeaderAC18, sreader);
61✔
76
                                        break;
61✔
77
                        }
78

79
                        return fileHeader;
129✔
80
                }
129✔
81

82
                public async Task<DwgFileHeader> ReadAsync(CancellationToken cancellationToken = default)
83
                {
7✔
84
                        this._fileStream.Position = 0;
7✔
85

86
                        //0x00        6        “ACXXXX” version string
87
                        byte[] buffer = new byte[6];
7✔
88
                        await this._fileStream.Stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken);
7✔
89
                        this.updateFileVersion(buffer);
7✔
90

91
                        DwgFileHeader fileHeader = DwgFileHeader.CreateFileHeader(this._version);
7✔
92

93
                        //Read the file header
94
                        switch (fileHeader.AcadVersion)
7!
95
                        {
96
                                case ACadVersion.Unknown:
NEW
97
                                        throw new CadNotSupportedException();
×
98
                                case ACadVersion.MC0_0:
99
                                case ACadVersion.AC1_2:
100
                                case ACadVersion.AC1_4:
101
                                case ACadVersion.AC1_50:
102
                                case ACadVersion.AC2_10:
103
                                case ACadVersion.AC1002:
104
                                case ACadVersion.AC1003:
105
                                case ACadVersion.AC1004:
106
                                case ACadVersion.AC1006:
107
                                case ACadVersion.AC1009:
NEW
108
                                        throw new CadNotSupportedException(this._version);
×
109
                                case ACadVersion.AC1012:
110
                                case ACadVersion.AC1014:
111
                                case ACadVersion.AC1015:
112
                                        IDwgStreamReader sreader = DwgStreamReaderBase.GetStreamHandler(_version, await getHeaderAC15Stream(cancellationToken));
2✔
113
                                        this.readFileHeaderAC15(fileHeader as DwgFileHeaderAC15, sreader);
2✔
114
                                        break;
2✔
115
                                case ACadVersion.AC1018:
116
                                        await this.readFileHeaderAC18Async(fileHeader as DwgFileHeaderAC18, cancellationToken);
1✔
117
                                        break;
1✔
118
                                case ACadVersion.AC1021:
119
                                        await this.readFileHeaderAC21Async(fileHeader as DwgFileHeaderAC21, cancellationToken);
1✔
120
                                        break;
1✔
121
                                case ACadVersion.AC1024:
122
                                case ACadVersion.AC1027:
123
                                case ACadVersion.AC1032:
124
                                        await this.readFileHeaderAC18Async(fileHeader as DwgFileHeaderAC18, cancellationToken);
3✔
125
                                        break;
3✔
126
                        }
127

128
                        return fileHeader;
7✔
129
                }
7✔
130

131
                private void readFileHeaderAC15(DwgFileHeaderAC15 fileheader, IDwgStreamReader sreader)
132
                {
37✔
133
                        //The next 7 starting at offset 0x06 are to be six bytes of 0 
134
                        //(in R14, 5 0’s and the ACADMAINTVER variable) and a byte of 1.
135
                        sreader.ReadBytes(7);
37✔
136
                        //At 0x0D is a seeker (4 byte long absolute address) for the beginning sentinel of the image data.
137
                        fileheader.PreviewAddress = sreader.ReadInt();
37✔
138

139
                        //Undocumented Bytes at 0x11 and 0x12
140
                        sreader.ReadBytes(2);
37✔
141

142
                        //Bytes at 0x13 and 0x14 are a raw short indicating the value of the code page for this drawing file.
143
                        fileheader.DrawingCodePage = CadUtils.GetCodePage(sreader.ReadShort());
37✔
144

145
                        //At 0x15 is a long that tells how many sets of recno/seeker/length records follow.
146
                        int nRecords = (int)sreader.ReadRawLong();
37✔
147
                        for (int i = 0; i < nRecords; ++i)
488✔
148
                        {
207✔
149
                                //Record number (raw byte) | Seeker (raw long) | Size (raw long)
150
                                DwgSectionLocatorRecord record = new DwgSectionLocatorRecord();
207✔
151
                                record.Number = sreader.ReadByte();
207✔
152
                                record.Seeker = sreader.ReadRawLong();
207✔
153
                                record.Size = sreader.ReadRawLong();
207✔
154

155
                                fileheader.Records.Add(record.Number.Value, record);
207✔
156
                        }
207✔
157

158
                        //RS : CRC for BOF to this point.
159
                        sreader.ReadCRC();
37✔
160

161
                        var sn = sreader.ReadSentinel();
37✔
162
                        if (!CheckSentinel(sn, DwgFileHeaderAC15.EndSentinel))
37!
NEW
163
                        {
×
NEW
164
                                this.notify($"Invalid section sentinel found in FileHeader", NotificationType.Warning);
×
NEW
165
                        }
×
166
                }
37✔
167

168
                private void readFileHeaderAC18(DwgFileHeaderAC18 fileheader, IDwgStreamReader sreader)
169
                {
80✔
170
                        this.readFileMetaData(fileheader, sreader);
80✔
171

172
                        //0x80        0x6C        Encrypted Data
173
                        //Metadata:
174
                        //The encrypted data at 0x80 can be decrypted by exclusive or’ing the 0x6c bytes of data 
175
                        //from the file with the following magic number sequence:
176

177
                        //29 23 BE 84 E1 6C D6 AE 52 90 49 F1 F1 BB E9 EB
178
                        //B3 A6 DB 3C 87 0C 3E 99 24 5E 0D 1C 06 B7 47 DE
179
                        //B3 12 4D C8 43 BB 8B A6 1F 03 5A 7D 09 38 25 1F
180
                        //5D D4 CB FC 96 F5 45 3B 13 0D 89 0A 1C DB AE 32
181
                        //20 9A 50 EE 40 78 36 FD 12 49 32 F6 9E 7D 49 DC
182
                        //AD 4F 14 F2 44 40 66 D0 6B C4 30 B7
183

184
                        StreamIO headerStream = new StreamIO(new CRC32StreamHandler(sreader.ReadBytes(0x6C), 0U)); //108
80✔
185
                        headerStream.Encoding = TextEncoding.GetListedEncoding(CodePage.Windows1252);
80✔
186

187
                        #region Read header encrypted data
188

189
                        //0x00        12        “AcFssFcAJMB” file ID string
190
                        string fileId = headerStream.ReadString(12);
80✔
191
                        if (fileId != "AcFssFcAJMB\0")
80!
NEW
192
                        {
×
NEW
193
                                this.notify($"File validation failed, id should be : AcFssFcAJMB\0, but is : {fileId}", NotificationType.Warning);
×
NEW
194
                        }
×
195

196
                        //0x0C        4        0x00(long)
197
                        headerStream.ReadInt<LittleEndianConverter>();
80✔
198
                        //0x10        4        0x6c(long)
199
                        headerStream.ReadInt<LittleEndianConverter>();
80✔
200
                        //0x14        4        0x04(long)
201
                        headerStream.ReadInt<LittleEndianConverter>();
80✔
202
                        //0x18        4        Root tree node gap
203
                        fileheader.RootTreeNodeGap = headerStream.ReadInt<LittleEndianConverter>();
80✔
204
                        //0x1C        4        Lowermost left tree node gap
205
                        fileheader.LeftGap = headerStream.ReadInt<LittleEndianConverter>();
80✔
206
                        //0x20        4        Lowermost right tree node gap
207
                        fileheader.RigthGap = headerStream.ReadInt<LittleEndianConverter>();
80✔
208
                        //0x24        4        Unknown long(ODA writes 1)        
209
                        headerStream.ReadInt<LittleEndianConverter>();
80✔
210
                        //0x28        4        Last section page Id
211
                        fileheader.LastPageId = headerStream.ReadInt<LittleEndianConverter>();
80✔
212

213
                        //0x2C        8        Last section page end address
214
                        fileheader.LastSectionAddr = headerStream.ReadULong<LittleEndianConverter>();
80✔
215
                        //0x34        8        Second header data address pointing to the repeated header data at the end of the file
216
                        fileheader.SecondHeaderAddr = headerStream.ReadULong<LittleEndianConverter>();
80✔
217

218
                        //0x3C        4        Gap amount
219
                        fileheader.GapAmount = headerStream.ReadUInt<LittleEndianConverter>();
80✔
220
                        //0x40        4        Section page amount
221
                        fileheader.SectionAmount = headerStream.ReadUInt<LittleEndianConverter>();
80✔
222
                        //0x44        4        0x20(long)
223
                        headerStream.ReadInt<LittleEndianConverter>();
80✔
224
                        //0x48        4        0x80(long)
225
                        headerStream.ReadInt<LittleEndianConverter>();
80✔
226
                        //0x4C        4        0x40(long)
227
                        headerStream.ReadInt<LittleEndianConverter>();
80✔
228
                        //0x50        4        Section Page Map Id
229
                        fileheader.SectionPageMapId = headerStream.ReadUInt<LittleEndianConverter>();
80✔
230
                        //0x54        8        Section Page Map address(add 0x100 to this value)
231
                        fileheader.PageMapAddress = headerStream.ReadULong<LittleEndianConverter>() + 256UL;
80✔
232
                        //0x5C        4        Section Map Id
233
                        fileheader.SectionMapId = headerStream.ReadUInt<LittleEndianConverter>();
80✔
234
                        //0x60        4        Section page array size
235
                        fileheader.SectionArrayPageSize = headerStream.ReadUInt<LittleEndianConverter>();
80✔
236
                        //0x64        4        Gap array size
237
                        fileheader.GapArraySize = headerStream.ReadUInt<LittleEndianConverter>();
80✔
238
                        //0x68        4        CRC32(long).See paragraph 2.14.2 for the 32 - bit CRC calculation, 
239
                        //                        the seed is zero. Note that the CRC 
240
                        //                        calculation is done including the 4 CRC bytes that are 
241
                        //                        initially zero! So the CRC calculation takes into account 
242
                        //                        all of the 0x6c bytes of the data in this table.
243
                        fileheader.CRCSeed = headerStream.ReadUInt();
80✔
244
                        #endregion
245

246
                        #region Read page map of the file
247
                        sreader.Position = (long)fileheader.PageMapAddress;
80✔
248

249
                        //Get the page size
250
                        this.getPageHeaderData(sreader, out _, out long decompressedSize, out _, out _, out _);
80✔
251
                        //Get the descompressed stream to read the records
252
                        StreamIO decompressed = new StreamIO(DwgLZ77AC18Decompressor.Decompress(sreader.Stream, decompressedSize));
80✔
253

254
                        //Section size
255
                        int total = 0x100;
80✔
256
                        while (decompressed.Position < decompressed.Length)
1,711✔
257
                        {
1,631✔
258
                                DwgSectionLocatorRecord record = new DwgSectionLocatorRecord();
1,631✔
259
                                //0x00        4        Section page number, starts at 1, page numbers are unique per file.
260
                                record.Number = decompressed.ReadInt();
1,631✔
261
                                //0x04        4        Section size
262
                                record.Size = decompressed.ReadInt();
1,631✔
263

264
                                if (record.Number >= 0)
1,631✔
265
                                {
1,582✔
266
                                        record.Seeker = total;
1,582✔
267
                                        fileheader.Records.Add(record.Number.Value, record);
1,582✔
268
                                }
1,582✔
269
                                else
270
                                {
49✔
271
                                        //If the section number is negative, this represents a gap in the sections (unused data). 
272
                                        //For a negative section number, the following data will be present after the section size:
273

274
                                        //0x00        4        Parent
275
                                        decompressed.ReadInt();
49✔
276
                                        //0x04        4        Left
277
                                        decompressed.ReadInt();
49✔
278
                                        //0x08        4        Right
279
                                        decompressed.ReadInt();
49✔
280
                                        //0x0C        4        0x00
281
                                        decompressed.ReadInt();
49✔
282
                                }
49✔
283

284
                                total += (int)record.Size;
1,631✔
285
                        }
1,631✔
286
                        #endregion
287

288
                        #region Read the data section map
289
                        //Set the positon of the map
290
                        sreader.Position = fileheader.Records[(int)fileheader.SectionMapId].Seeker;
80✔
291
                        //84244
292
                        //Get the page size
293
                        this.getPageHeaderData(sreader, out _, out decompressedSize, out _, out _, out _);
80✔
294
                        //1556
295
                        StreamIO decompressedStream = new StreamIO(DwgLZ77AC18Decompressor.Decompress(sreader.Stream, decompressedSize));
80✔
296
                        decompressedStream.Encoding = TextEncoding.GetListedEncoding(CodePage.Windows1252);
80✔
297

298
                        //0x00        4        Number of section descriptions(NumDescriptions)
299
                        int ndescriptions = decompressedStream.ReadInt<LittleEndianConverter>();
80✔
300
                        //0x04        4        0x02 (long)
301
                        decompressedStream.ReadInt<LittleEndianConverter>();
80✔
302
                        //0x08        4        0x00007400 (long)
303
                        decompressedStream.ReadInt<LittleEndianConverter>();
80✔
304
                        //0x0C        4        0x00 (long)
305
                        decompressedStream.ReadInt<LittleEndianConverter>();
80✔
306
                        //0x10        4        Unknown (long), ODA writes NumDescriptions here.
307
                        decompressedStream.ReadInt<LittleEndianConverter>();
80✔
308

309
                        for (int i = 0; i < ndescriptions; ++i)
2,278✔
310
                        {
1,059✔
311
                                DwgSectionDescriptor descriptor = new DwgSectionDescriptor();
1,059✔
312
                                //0x00        8        Size of section(OdUInt64)
313
                                descriptor.CompressedSize = decompressedStream.ReadULong();
1,059✔
314
                                /*0x08        4        Page count(PageCount). Note that there can be more pages than PageCount,
315
                                                        as PageCount is just the number of pages written to file.
316
                                                        If a page contains zeroes only, that page is not written to file.
317
                                                        These “zero pages” can be detected by checking if the page’s start 
318
                                                        offset is bigger than it should be based on the sum of previously read pages 
319
                                                        decompressed size(including zero pages).After reading all pages, if the total 
320
                                                        decompressed size of the pages is not equal to the section’s size, add more zero 
321
                                                        pages to the section until this condition is met.
322
                                */
323
                                descriptor.PageCount = decompressedStream.ReadInt<LittleEndianConverter>();
1,059✔
324
                                //0x0C        4        Max Decompressed Size of a section page of this type(normally 0x7400)
325
                                descriptor.DecompressedSize = (ulong)decompressedStream.ReadInt<LittleEndianConverter>();
1,059✔
326
                                //0x10        4        Unknown(long)
327
                                decompressedStream.ReadInt<LittleEndianConverter>();
1,059✔
328
                                //0x14        4        Compressed(1 = no, 2 = yes, normally 2)
329
                                descriptor.CompressedCode = decompressedStream.ReadInt<LittleEndianConverter>();
1,059✔
330
                                //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.
331
                                descriptor.SectionId = decompressedStream.ReadInt<LittleEndianConverter>();
1,059✔
332
                                //0x1C        4        Encrypted(0 = no, 1 = yes, 2 = unknown)
333
                                descriptor.Encrypted = decompressedStream.ReadInt<LittleEndianConverter>();
1,059✔
334
                                //0x20        64        Section Name(string)
335
                                descriptor.Name = decompressedStream.ReadString(64).Split('\0')[0];
1,059✔
336

337
                                //Following this, the following (local) section page map data will be present
338
                                for (int j = 0; j < descriptor.PageCount; ++j)
4,962✔
339
                                {
1,422✔
340
                                        DwgLocalSectionMap localmap = new DwgLocalSectionMap();
1,422✔
341
                                        //0x00        4        Page number(index into SectionPageMap), starts at 1
342
                                        localmap.PageNumber = decompressedStream.ReadInt<LittleEndianConverter>();
1,422✔
343
                                        //0x04        4        Data size for this page(compressed size).
344
                                        localmap.CompressedSize = (ulong)decompressedStream.ReadInt<LittleEndianConverter>();
1,422✔
345
                                        //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.
346
                                        localmap.Offset = decompressedStream.ReadULong();
1,422✔
347

348
                                        //same decompressed size and seeker (temporal values)
349
                                        localmap.DecompressedSize = descriptor.DecompressedSize;
1,422✔
350
                                        localmap.Seeker = fileheader.Records[localmap.PageNumber].Seeker;
1,422✔
351

352
                                        //Maximum section page size appears to be 0x7400 bytes in the normal case.
353
                                        //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.
354

355
                                        descriptor.LocalSections.Add(localmap);
1,422✔
356
                                }
1,422✔
357

358
                                //Get the final size for the local section
359
                                uint sizeLeft = (uint)(descriptor.CompressedSize % descriptor.DecompressedSize);
1,059✔
360
                                if (sizeLeft > 0U && descriptor.LocalSections.Count > 0)
1,059✔
361
                                        descriptor.LocalSections[descriptor.LocalSections.Count - 1].DecompressedSize = sizeLeft;
948✔
362

363
                                fileheader.Descriptors.Add(descriptor.Name, descriptor);
1,059✔
364
                        }
1,059✔
365
                        #endregion
366
                }
80✔
367

368
                private async Task readFileHeaderAC18Async(DwgFileHeaderAC18 fileheader, CancellationToken cancellationToken = default)
369
                {
4✔
370
                        this.readFileMetaData(fileheader, await this.getStreamChunkAsync(_metaDataSize, cancellationToken));
4✔
371

372
                        this.readEncriptedData(fileheader, await this.getStreamChunkAsync(_encriptedDataSize, cancellationToken));
4✔
373

374
                        _fileStream.Position = (long)fileheader.PageMapAddress;
4✔
375

376
                        //Get the page size
377
                        this.getPageHeaderData(await this.getStreamChunkAsync(0x14, cancellationToken)
4✔
378
                                , out _, out long decompressedSize, out long compressedSize, out _, out _);
4✔
379

380
                        //Get the descompressed stream to read the records
381
                        IDwgStreamReader compressed = await this.getStreamChunkAsync((int)compressedSize, cancellationToken);
4✔
382

383
                        this.readRecords(fileheader, compressed.Stream, decompressedSize);
4✔
384

385
                        this._fileStream.Position = fileheader.Records[(int)fileheader.SectionMapId].Seeker;
4✔
386
                        //Get the page size
387
                        this.getPageHeaderData(await this.getStreamChunkAsync(0x14, cancellationToken)
4✔
388
                                , out _, out decompressedSize, out compressedSize, out _, out _);
4✔
389

390
                        compressed = await this.getStreamChunkAsync((int)compressedSize, cancellationToken);
4✔
391

392
                        this.readDescriptors(fileheader, compressed.Stream, decompressedSize);
4✔
393
                }
4✔
394

395
                private void readRecords(DwgFileHeaderAC18 fileheader, Stream compressed, long decompressedSize)
396
                {
4✔
397
                        StreamIO decompressed = new StreamIO(DwgLZ77AC18Decompressor.Decompress(compressed, decompressedSize));
4✔
398

399
                        //Section size
400
                        int total = 0x100;
4✔
401
                        while (decompressed.Position < decompressed.Length)
95✔
402
                        {
91✔
403
                                DwgSectionLocatorRecord record = new DwgSectionLocatorRecord();
91✔
404
                                //0x00        4        Section page number, starts at 1, page numbers are unique per file.
405
                                record.Number = decompressed.ReadInt();
91✔
406
                                //0x04        4        Section size
407
                                record.Size = decompressed.ReadInt();
91✔
408

409
                                if (record.Number >= 0)
91✔
410
                                {
88✔
411
                                        record.Seeker = total;
88✔
412
                                        fileheader.Records.Add(record.Number.Value, record);
88✔
413
                                }
88✔
414
                                else
415
                                {
3✔
416
                                        //If the section number is negative, this represents a gap in the sections (unused data). 
417
                                        //For a negative section number, the following data will be present after the section size:
418

419
                                        //0x00        4        Parent
420
                                        decompressed.ReadInt();
3✔
421
                                        //0x04        4        Left
422
                                        decompressed.ReadInt();
3✔
423
                                        //0x08        4        Right
424
                                        decompressed.ReadInt();
3✔
425
                                        //0x0C        4        0x00
426
                                        decompressed.ReadInt();
3✔
427
                                }
3✔
428

429
                                total += (int)record.Size;
91✔
430
                        }
91✔
431
                }
4✔
432

433
                private void readDescriptors(DwgFileHeaderAC18 fileheader, Stream compressed, long decompressedSize)
434
                {
4✔
435
                        StreamIO decompressed = new StreamIO(DwgLZ77AC18Decompressor.Decompress(compressed, decompressedSize));
4✔
436
                        decompressed.Encoding = TextEncoding.GetListedEncoding(CodePage.Windows1252);
4✔
437

438
                        //0x00        4        Number of section descriptions(NumDescriptions)
439
                        int ndescriptions = decompressed.ReadInt<LittleEndianConverter>();
4✔
440
                        //0x04        4        0x02 (long)
441
                        decompressed.ReadInt<LittleEndianConverter>();
4✔
442
                        //0x08        4        0x00007400 (long)
443
                        decompressed.ReadInt<LittleEndianConverter>();
4✔
444
                        //0x0C        4        0x00 (long)
445
                        decompressed.ReadInt<LittleEndianConverter>();
4✔
446
                        //0x10        4        Unknown (long), ODA writes NumDescriptions here.
447
                        decompressed.ReadInt<LittleEndianConverter>();
4✔
448

449
                        for (int i = 0; i < ndescriptions; ++i)
116✔
450
                        {
54✔
451
                                DwgSectionDescriptor descriptor = new DwgSectionDescriptor();
54✔
452
                                //0x00        8        Size of section(OdUInt64)
453
                                descriptor.CompressedSize = decompressed.ReadULong();
54✔
454
                                /*0x08        4        Page count(PageCount). Note that there can be more pages than PageCount,
455
                                                        as PageCount is just the number of pages written to file.
456
                                                        If a page contains zeroes only, that page is not written to file.
457
                                                        These “zero pages” can be detected by checking if the page’s start 
458
                                                        offset is bigger than it should be based on the sum of previously read pages 
459
                                                        decompressed size(including zero pages).After reading all pages, if the total 
460
                                                        decompressed size of the pages is not equal to the section’s size, add more zero 
461
                                                        pages to the section until this condition is met.
462
                                */
463
                                descriptor.PageCount = decompressed.ReadInt<LittleEndianConverter>();
54✔
464
                                //0x0C        4        Max Decompressed Size of a section page of this type(normally 0x7400)
465
                                descriptor.DecompressedSize = (ulong)decompressed.ReadInt<LittleEndianConverter>();
54✔
466
                                //0x10        4        Unknown(long)
467
                                decompressed.ReadInt<LittleEndianConverter>();
54✔
468
                                //0x14        4        Compressed(1 = no, 2 = yes, normally 2)
469
                                descriptor.CompressedCode = decompressed.ReadInt<LittleEndianConverter>();
54✔
470
                                //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.
471
                                descriptor.SectionId = decompressed.ReadInt<LittleEndianConverter>();
54✔
472
                                //0x1C        4        Encrypted(0 = no, 1 = yes, 2 = unknown)
473
                                descriptor.Encrypted = decompressed.ReadInt<LittleEndianConverter>();
54✔
474
                                //0x20        64        Section Name(string)
475
                                descriptor.Name = decompressed.ReadString(64).Split('\0')[0];
54✔
476

477
                                //Following this, the following (local) section page map data will be present
478
                                for (int j = 0; j < descriptor.PageCount; ++j)
268✔
479
                                {
80✔
480
                                        DwgLocalSectionMap localmap = new DwgLocalSectionMap();
80✔
481
                                        //0x00        4        Page number(index into SectionPageMap), starts at 1
482
                                        localmap.PageNumber = decompressed.ReadInt<LittleEndianConverter>();
80✔
483
                                        //0x04        4        Data size for this page(compressed size).
484
                                        localmap.CompressedSize = (ulong)decompressed.ReadInt<LittleEndianConverter>();
80✔
485
                                        //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.
486
                                        localmap.Offset = decompressed.ReadULong();
80✔
487

488
                                        //same decompressed size and seeker (temporal values)
489
                                        localmap.DecompressedSize = descriptor.DecompressedSize;
80✔
490
                                        localmap.Seeker = fileheader.Records[localmap.PageNumber].Seeker;
80✔
491

492
                                        //Maximum section page size appears to be 0x7400 bytes in the normal case.
493
                                        //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.
494

495
                                        descriptor.LocalSections.Add(localmap);
80✔
496
                                }
80✔
497

498
                                //Get the final size for the local section
499
                                uint sizeLeft = (uint)(descriptor.CompressedSize % descriptor.DecompressedSize);
54✔
500
                                if (sizeLeft > 0U && descriptor.LocalSections.Count > 0)
54✔
501
                                        descriptor.LocalSections[descriptor.LocalSections.Count - 1].DecompressedSize = sizeLeft;
49✔
502

503
                                fileheader.Descriptors.Add(descriptor.Name, descriptor);
54✔
504
                        }
54✔
505
                }
4✔
506

507
                private void readFileHeaderAC21(DwgFileHeaderAC21 fileheader, IDwgStreamReader sreader)
508
                {
14✔
509
                        this.readFileMetaData(fileheader, sreader);
14✔
510

511
                        //The last 0x28 bytes of this section consists of check data, 
512
                        //containing 5 Int64 values representing CRC’s and related numbers 
513
                        //(starting from 0x3D8 until the end). The first 0x3D8 bytes 
514
                        //should be decoded using Reed-Solomon (255, 239) decoding, with a factor of 3.
515
                        byte[] compressedData = sreader.ReadBytes(0x400);
14✔
516
                        byte[] decodedData = new byte[3 * 239]; //factor * blockSize
14✔
517
                        this.reedSolomonDecoding(compressedData, decodedData, 3, 239);
14✔
518

519
                        //0x00        8        CRC
520
                        long crc = LittleEndianConverter.Instance.ToInt64(decodedData, 0);
14✔
521
                        //0x08        8        Unknown key
522
                        long unknownKey = LittleEndianConverter.Instance.ToInt64(decodedData, 8);
14✔
523
                        //0x10        8        Compressed Data CRC
524
                        long compressedDataCRC = LittleEndianConverter.Instance.ToInt64(decodedData, 16);
14✔
525
                        //0x18        4        ComprLen
526
                        int comprLen = LittleEndianConverter.Instance.ToInt32(decodedData, 24);
14✔
527
                        //0x1C        4        Length2
528
                        int length2 = LittleEndianConverter.Instance.ToInt32(decodedData, 28);
14✔
529

530
                        //The decompressed size is a fixed 0x110.
531
                        byte[] buffer = new byte[0x110];
14✔
532
                        //If ComprLen is negative, then Data is not compressed (and data length is ComprLen).
533
                        if (comprLen < 0)
14!
NEW
534
                        {
×
535
                                //buffer = decodedData
NEW
536
                                throw new NotImplementedException();
×
537
                        }
538
                        //If ComprLen is positive, the ComprLen bytes of data are compressed
539
                        else
540
                        {
14✔
541
                                DwgLZ77AC21Decompressor.Decompress(decodedData, 32U, (uint)comprLen, buffer);
14✔
542
                        }
14✔
543

544
                        //Get the descompressed stream to read the records
545
                        StreamIO decompressed = new StreamIO(buffer);
14✔
546

547
                        //Read the compressed data
548
                        fileheader.CompressedMetadata = new Dwg21CompressedMetadata()
14✔
549
                        {
14✔
550
                                //0x00        8        Header size (normally 0x70)
14✔
551
                                HeaderSize = decompressed.ReadULong(),
14✔
552
                                //0x08        8        File size
14✔
553
                                FileSize = decompressed.ReadULong(),
14✔
554
                                //0x10        8        PagesMapCrcCompressed
14✔
555
                                PagesMapCrcCompressed = decompressed.ReadULong(),
14✔
556
                                //0x18        8        PagesMapCorrectionFactor
14✔
557
                                PagesMapCorrectionFactor = decompressed.ReadULong(),
14✔
558
                                //0x20        8        PagesMapCrcSeed
14✔
559
                                PagesMapCrcSeed = decompressed.ReadULong(),
14✔
560
                                //0x28        8        Pages map2offset(relative to data page map 1, add 0x480 to get stream position)
14✔
561
                                Map2Offset = decompressed.ReadULong(),
14✔
562
                                //0x30        8        Pages map2Id
14✔
563
                                Map2Id = decompressed.ReadULong(),
14✔
564
                                //0x38        8        PagesMapOffset(relative to data page map 1, add 0x480 to get stream position)
14✔
565
                                PagesMapOffset = decompressed.ReadULong(),
14✔
566
                                //0x40        8        PagesMapId
14✔
567
                                PagesMapId = decompressed.ReadULong(),
14✔
568
                                //0x48        8        Header2offset(relative to page map 1 address, add 0x480 to get stream position)
14✔
569
                                Header2offset = decompressed.ReadULong(),
14✔
570
                                //0x50        8        PagesMapSizeCompressed
14✔
571
                                PagesMapSizeCompressed = decompressed.ReadULong(),
14✔
572
                                //0x58        8        PagesMapSizeUncompressed
14✔
573
                                PagesMapSizeUncompressed = decompressed.ReadULong(),
14✔
574
                                //0x60        8        PagesAmount
14✔
575
                                PagesAmount = decompressed.ReadULong(),
14✔
576
                                //0x68        8        PagesMaxId
14✔
577
                                PagesMaxId = decompressed.ReadULong(),
14✔
578
                                //0x70        8        Unknown(normally 0x20, 32)
14✔
579
                                Unknow0x20 = decompressed.ReadULong(),
14✔
580
                                //0x78        8        Unknown(normally 0x40, 64)
14✔
581
                                Unknow0x40 = decompressed.ReadULong(),
14✔
582
                                //0x80        8        PagesMapCrcUncompressed
14✔
583
                                PagesMapCrcUncompressed = decompressed.ReadULong(),
14✔
584
                                //0x88        8        Unknown(normally 0xf800, 63488)
14✔
585
                                Unknown0xF800 = decompressed.ReadULong(),
14✔
586
                                //0x90        8        Unknown(normally 4)
14✔
587
                                Unknown4 = decompressed.ReadULong(),
14✔
588
                                //0x98        8        Unknown(normally 1)
14✔
589
                                Unknown1 = decompressed.ReadULong(),
14✔
590
                                //0xA0        8        SectionsAmount(number of sections + 1)
14✔
591
                                SectionsAmount = decompressed.ReadULong(),
14✔
592
                                //0xA8        8        SectionsMapCrcUncompressed
14✔
593
                                SectionsMapCrcUncompressed = decompressed.ReadULong(),
14✔
594
                                //0xB0        8        SectionsMapSizeCompressed
14✔
595
                                SectionsMapSizeCompressed = decompressed.ReadULong(),
14✔
596
                                //0xB8        8        SectionsMap2Id
14✔
597
                                SectionsMap2Id = decompressed.ReadULong(),
14✔
598
                                //0xC0        8        SectionsMapId
14✔
599
                                SectionsMapId = decompressed.ReadULong(),
14✔
600
                                //0xC8        8        SectionsMapSizeUncompressed
14✔
601
                                SectionsMapSizeUncompressed = decompressed.ReadULong(),
14✔
602
                                //0xD0        8        SectionsMapCrcCompressed
14✔
603
                                SectionsMapCrcCompressed = decompressed.ReadULong(),
14✔
604
                                //0xD8        8        SectionsMapCorrectionFactor
14✔
605
                                SectionsMapCorrectionFactor = decompressed.ReadULong(),
14✔
606
                                //0xE0        8        SectionsMapCrcSeed
14✔
607
                                SectionsMapCrcSeed = decompressed.ReadULong(),
14✔
608
                                //0xE8        8        StreamVersion(normally 0x60100)
14✔
609
                                StreamVersion = decompressed.ReadULong(),
14✔
610
                                //0xF0        8        CrcSeed
14✔
611
                                CrcSeed = decompressed.ReadULong(),
14✔
612
                                //0xF8        8        CrcSeedEncoded
14✔
613
                                CrcSeedEncoded = decompressed.ReadULong(),
14✔
614
                                //0x100        8        RandomSeed
14✔
615
                                RandomSeed = decompressed.ReadULong(),
14✔
616
                                //0x108        8        Header CRC64
14✔
617
                                HeaderCRC64 = decompressed.ReadULong()
14✔
618
                        };
14✔
619

620
                        //Prepare the page data stream to read
621
                        byte[] arr = this.getPageBuffer(
14✔
622
                                fileheader.CompressedMetadata.PagesMapOffset,
14✔
623
                                fileheader.CompressedMetadata.PagesMapSizeCompressed,
14✔
624
                                fileheader.CompressedMetadata.PagesMapSizeUncompressed,
14✔
625
                                fileheader.CompressedMetadata.PagesMapCorrectionFactor,
14✔
626
                                0xEF, sreader.Stream);
14✔
627

628
                        //Read the page data
629
                        StreamIO pageDataStream = new StreamIO(arr);
14✔
630

631
                        long offset = 0;
14✔
632
                        while (pageDataStream.Position < pageDataStream.Length)
293✔
633
                        {
279✔
634
                                long size = pageDataStream.ReadLong();
279✔
635
                                long id = Math.Abs(pageDataStream.ReadLong());
279✔
636
                                fileheader.Records.Add((int)id, new DwgSectionLocatorRecord((int)id, (int)offset, (int)size));
279✔
637

638
                                //Add the size to the current offset
639
                                offset += size;
279✔
640
                        }
279✔
641

642
                        //Prepare the section map data stream to read
643
                        arr = this.getPageBuffer(
14✔
644
                                (ulong)fileheader.Records[(int)fileheader.CompressedMetadata.SectionsMapId].Seeker,
14✔
645
                                fileheader.CompressedMetadata.SectionsMapSizeCompressed,
14✔
646
                                fileheader.CompressedMetadata.SectionsMapSizeUncompressed,
14✔
647
                                fileheader.CompressedMetadata.SectionsMapCorrectionFactor,
14✔
648
                                239, sreader.Stream);
14✔
649

650
                        //Section map stream
651
                        StreamIO sectionMapStream = new StreamIO(arr);
14✔
652

653
                        while (sectionMapStream.Position < sectionMapStream.Length)
196✔
654
                        {
182✔
655
                                DwgSectionDescriptor section = new DwgSectionDescriptor();
182✔
656
                                //0x00        8        Data size
657
                                section.CompressedSize = sectionMapStream.ReadULong<LittleEndianConverter>();
182✔
658
                                //0x08        8        Max size
659
                                section.DecompressedSize = sectionMapStream.ReadULong<LittleEndianConverter>();
182✔
660
                                //0x10        8        Encryption
661
                                section.Encrypted = (int)sectionMapStream.ReadULong<LittleEndianConverter>();
182✔
662
                                //0x18        8        HashCode
663
                                section.HashCode = sectionMapStream.ReadULong<LittleEndianConverter>();
182✔
664
                                //0x20        8        SectionNameLength
665
                                int sectionNameLength = (int)sectionMapStream.ReadLong<LittleEndianConverter>();
182✔
666
                                //0x28        8        Unknown
667
                                sectionMapStream.ReadULong<LittleEndianConverter>();
182✔
668
                                //0x30        8        Encoding
669
                                section.Encoding = sectionMapStream.ReadULong<LittleEndianConverter>();
182✔
670
                                //0x38        8        NumPages.This is the number of pages present 
671
                                //                        in the file for the section, but this does not include 
672
                                //                        pages that contain zeroes only.A page that contains zeroes 
673
                                //                        only is not written to file.If a page’s data offset is 
674
                                //                        smaller than the sum of the decompressed size of all previous 
675
                                //                        pages, then it is to be preceded by a zero page with a size 
676
                                //                        that is equal to the difference between these two numbers.
677
                                section.PageCount = (int)sectionMapStream.ReadULong<LittleEndianConverter>();
182✔
678

679
                                //Read the name
680
                                if (sectionNameLength > 0)
182✔
681
                                {
168✔
682
                                        section.Name = sectionMapStream.ReadString(sectionNameLength, Encoding.Unicode);
168✔
683
                                        //Remove the empty characters
684
                                        section.Name = section.Name.Replace("\0", "");
168✔
685
                                }
168✔
686

687
                                ulong currentOffset = 0;
182✔
688
                                for (int i = 0; i < section.PageCount; ++i)
810✔
689
                                {
223✔
690
                                        DwgLocalSectionMap page = new DwgLocalSectionMap();
223✔
691
                                        //8        Page data offset.If a page’s data offset is 
692
                                        //        smaller than the sum of the decompressed size
693
                                        //        of all previous pages, then it is to be preceded 
694
                                        //        by a zero page with a size that is equal to the 
695
                                        //        difference between these two numbers.
696
                                        page.Offset = sectionMapStream.ReadULong<LittleEndianConverter>();
223✔
697
                                        //8        Page Size
698
                                        page.Size = sectionMapStream.ReadLong<LittleEndianConverter>();
223✔
699
                                        //8        Page Id
700
                                        page.PageNumber = (int)sectionMapStream.ReadLong<LittleEndianConverter>();
223✔
701
                                        //8        Page Uncompressed Size
702
                                        page.DecompressedSize = sectionMapStream.ReadULong<LittleEndianConverter>();
223✔
703
                                        //8        Page Compressed Size
704
                                        page.CompressedSize = sectionMapStream.ReadULong<LittleEndianConverter>();
223✔
705
                                        //8        Page Checksum
706
                                        page.Checksum = sectionMapStream.ReadULong<LittleEndianConverter>();
223✔
707
                                        //8        Page CRC
708
                                        page.CRC = sectionMapStream.ReadULong<LittleEndianConverter>();
223✔
709

710
                                        //Add the page to the section
711
                                        section.LocalSections.Add(page);
223✔
712
                                        //Move the offset
713
                                        currentOffset = page.Offset + page.DecompressedSize;
223✔
714
                                }
223✔
715
                                if (sectionNameLength > 0)
182✔
716
                                        fileheader.Descriptors.Add(section.Name, section);
168✔
717
                        }
182✔
718
                }
14✔
719

720
                private async Task readFileHeaderAC21Async(DwgFileHeaderAC21 fileheader, CancellationToken cancellationToken = default)
721
                {
1✔
722
                        this.readFileMetaData(fileheader, await this.getStreamChunkAsync(_metaDataSize, cancellationToken));
1✔
723

724
                        //The last 0x28 bytes of this section consists of check data, 
725
                        //containing 5 Int64 values representing CRC’s and related numbers 
726
                        //(starting from 0x3D8 until the end). The first 0x3D8 bytes 
727
                        //should be decoded using Reed-Solomon (255, 239) decoding, with a factor of 3.
728
                        byte[] compressedData = await _fileStream.ReadBytesAsync(0x400);
1✔
729
                        byte[] decodedData = new byte[3 * 239]; //factor * blockSize
1✔
730
                        this.reedSolomonDecoding(compressedData, decodedData, 3, 239);
1✔
731

732
                        //0x00        8        CRC
733
                        long crc = LittleEndianConverter.Instance.ToInt64(decodedData, 0);
1✔
734
                        //0x08        8        Unknown key
735
                        long unknownKey = LittleEndianConverter.Instance.ToInt64(decodedData, 8);
1✔
736
                        //0x10        8        Compressed Data CRC
737
                        long compressedDataCRC = LittleEndianConverter.Instance.ToInt64(decodedData, 16);
1✔
738
                        //0x18        4        ComprLen
739
                        int comprLen = LittleEndianConverter.Instance.ToInt32(decodedData, 24);
1✔
740
                        //0x1C        4        Length2
741
                        int length2 = LittleEndianConverter.Instance.ToInt32(decodedData, 28);
1✔
742

743
                        //The decompressed size is a fixed 0x110.
744
                        byte[] buffer = new byte[0x110];
1✔
745
                        //If ComprLen is negative, then Data is not compressed (and data length is ComprLen).
746
                        if (comprLen < 0)
1!
NEW
747
                        {
×
748
                                //buffer = decodedData
NEW
749
                                throw new NotImplementedException();
×
750
                        }
751
                        //If ComprLen is positive, the ComprLen bytes of data are compressed
752
                        else
753
                        {
1✔
754
                                DwgLZ77AC21Decompressor.Decompress(decodedData, 32U, (uint)comprLen, buffer);
1✔
755
                        }
1✔
756

757
                        //Get the descompressed stream to read the records
758
                        StreamIO decompressed = new StreamIO(buffer);
1✔
759

760
                        //Read the compressed data
761
                        fileheader.CompressedMetadata = new Dwg21CompressedMetadata()
1✔
762
                        {
1✔
763
                                //0x00        8        Header size (normally 0x70)
1✔
764
                                HeaderSize = decompressed.ReadULong(),
1✔
765
                                //0x08        8        File size
1✔
766
                                FileSize = decompressed.ReadULong(),
1✔
767
                                //0x10        8        PagesMapCrcCompressed
1✔
768
                                PagesMapCrcCompressed = decompressed.ReadULong(),
1✔
769
                                //0x18        8        PagesMapCorrectionFactor
1✔
770
                                PagesMapCorrectionFactor = decompressed.ReadULong(),
1✔
771
                                //0x20        8        PagesMapCrcSeed
1✔
772
                                PagesMapCrcSeed = decompressed.ReadULong(),
1✔
773
                                //0x28        8        Pages map2offset(relative to data page map 1, add 0x480 to get stream position)
1✔
774
                                Map2Offset = decompressed.ReadULong(),
1✔
775
                                //0x30        8        Pages map2Id
1✔
776
                                Map2Id = decompressed.ReadULong(),
1✔
777
                                //0x38        8        PagesMapOffset(relative to data page map 1, add 0x480 to get stream position)
1✔
778
                                PagesMapOffset = decompressed.ReadULong(),
1✔
779
                                //0x40        8        PagesMapId
1✔
780
                                PagesMapId = decompressed.ReadULong(),
1✔
781
                                //0x48        8        Header2offset(relative to page map 1 address, add 0x480 to get stream position)
1✔
782
                                Header2offset = decompressed.ReadULong(),
1✔
783
                                //0x50        8        PagesMapSizeCompressed
1✔
784
                                PagesMapSizeCompressed = decompressed.ReadULong(),
1✔
785
                                //0x58        8        PagesMapSizeUncompressed
1✔
786
                                PagesMapSizeUncompressed = decompressed.ReadULong(),
1✔
787
                                //0x60        8        PagesAmount
1✔
788
                                PagesAmount = decompressed.ReadULong(),
1✔
789
                                //0x68        8        PagesMaxId
1✔
790
                                PagesMaxId = decompressed.ReadULong(),
1✔
791
                                //0x70        8        Unknown(normally 0x20, 32)
1✔
792
                                Unknow0x20 = decompressed.ReadULong(),
1✔
793
                                //0x78        8        Unknown(normally 0x40, 64)
1✔
794
                                Unknow0x40 = decompressed.ReadULong(),
1✔
795
                                //0x80        8        PagesMapCrcUncompressed
1✔
796
                                PagesMapCrcUncompressed = decompressed.ReadULong(),
1✔
797
                                //0x88        8        Unknown(normally 0xf800, 63488)
1✔
798
                                Unknown0xF800 = decompressed.ReadULong(),
1✔
799
                                //0x90        8        Unknown(normally 4)
1✔
800
                                Unknown4 = decompressed.ReadULong(),
1✔
801
                                //0x98        8        Unknown(normally 1)
1✔
802
                                Unknown1 = decompressed.ReadULong(),
1✔
803
                                //0xA0        8        SectionsAmount(number of sections + 1)
1✔
804
                                SectionsAmount = decompressed.ReadULong(),
1✔
805
                                //0xA8        8        SectionsMapCrcUncompressed
1✔
806
                                SectionsMapCrcUncompressed = decompressed.ReadULong(),
1✔
807
                                //0xB0        8        SectionsMapSizeCompressed
1✔
808
                                SectionsMapSizeCompressed = decompressed.ReadULong(),
1✔
809
                                //0xB8        8        SectionsMap2Id
1✔
810
                                SectionsMap2Id = decompressed.ReadULong(),
1✔
811
                                //0xC0        8        SectionsMapId
1✔
812
                                SectionsMapId = decompressed.ReadULong(),
1✔
813
                                //0xC8        8        SectionsMapSizeUncompressed
1✔
814
                                SectionsMapSizeUncompressed = decompressed.ReadULong(),
1✔
815
                                //0xD0        8        SectionsMapCrcCompressed
1✔
816
                                SectionsMapCrcCompressed = decompressed.ReadULong(),
1✔
817
                                //0xD8        8        SectionsMapCorrectionFactor
1✔
818
                                SectionsMapCorrectionFactor = decompressed.ReadULong(),
1✔
819
                                //0xE0        8        SectionsMapCrcSeed
1✔
820
                                SectionsMapCrcSeed = decompressed.ReadULong(),
1✔
821
                                //0xE8        8        StreamVersion(normally 0x60100)
1✔
822
                                StreamVersion = decompressed.ReadULong(),
1✔
823
                                //0xF0        8        CrcSeed
1✔
824
                                CrcSeed = decompressed.ReadULong(),
1✔
825
                                //0xF8        8        CrcSeedEncoded
1✔
826
                                CrcSeedEncoded = decompressed.ReadULong(),
1✔
827
                                //0x100        8        RandomSeed
1✔
828
                                RandomSeed = decompressed.ReadULong(),
1✔
829
                                //0x108        8        Header CRC64
1✔
830
                                HeaderCRC64 = decompressed.ReadULong()
1✔
831
                        };
1✔
832

833
                        //Prepare the page data stream to read
834
                        byte[] arr = await this.getPageBufferAsync(
1✔
835
                                fileheader.CompressedMetadata.PagesMapOffset,
1✔
836
                                fileheader.CompressedMetadata.PagesMapSizeCompressed,
1✔
837
                                fileheader.CompressedMetadata.PagesMapSizeUncompressed,
1✔
838
                                fileheader.CompressedMetadata.PagesMapCorrectionFactor,
1✔
839
                                0xEF, _fileStream.Stream);
1✔
840

841
                        //Read the page data
842
                        StreamIO pageDataStream = new StreamIO(arr);
1✔
843

844
                        long offset = 0;
1✔
845
                        while (pageDataStream.Position < pageDataStream.Length)
21✔
846
                        {
20✔
847
                                long size = pageDataStream.ReadLong();
20✔
848
                                long id = Math.Abs(pageDataStream.ReadLong());
20✔
849
                                fileheader.Records.Add((int)id, new DwgSectionLocatorRecord((int)id, (int)offset, (int)size));
20✔
850

851
                                //Add the size to the current offset
852
                                offset += size;
20✔
853
                        }
20✔
854

855
                        //Prepare the section map data stream to read
856
                        arr = await this.getPageBufferAsync(
1✔
857
                                (ulong)fileheader.Records[(int)fileheader.CompressedMetadata.SectionsMapId].Seeker,
1✔
858
                                fileheader.CompressedMetadata.SectionsMapSizeCompressed,
1✔
859
                                fileheader.CompressedMetadata.SectionsMapSizeUncompressed,
1✔
860
                                fileheader.CompressedMetadata.SectionsMapCorrectionFactor,
1✔
861
                                239, _fileStream.Stream);
1✔
862

863
                        //Section map stream
864
                        StreamIO sectionMapStream = new StreamIO(arr);
1✔
865

866
                        while (sectionMapStream.Position < sectionMapStream.Length)
14✔
867
                        {
13✔
868
                                DwgSectionDescriptor section = new DwgSectionDescriptor();
13✔
869
                                //0x00        8        Data size
870
                                section.CompressedSize = sectionMapStream.ReadULong<LittleEndianConverter>();
13✔
871
                                //0x08        8        Max size
872
                                section.DecompressedSize = sectionMapStream.ReadULong<LittleEndianConverter>();
13✔
873
                                //0x10        8        Encryption
874
                                section.Encrypted = (int)sectionMapStream.ReadULong<LittleEndianConverter>();
13✔
875
                                //0x18        8        HashCode
876
                                section.HashCode = sectionMapStream.ReadULong<LittleEndianConverter>();
13✔
877
                                //0x20        8        SectionNameLength
878
                                int sectionNameLength = (int)sectionMapStream.ReadLong<LittleEndianConverter>();
13✔
879
                                //0x28        8        Unknown
880
                                sectionMapStream.ReadULong<LittleEndianConverter>();
13✔
881
                                //0x30        8        Encoding
882
                                section.Encoding = sectionMapStream.ReadULong<LittleEndianConverter>();
13✔
883
                                //0x38        8        NumPages.This is the number of pages present 
884
                                //                        in the file for the section, but this does not include 
885
                                //                        pages that contain zeroes only.A page that contains zeroes 
886
                                //                        only is not written to file.If a page’s data offset is 
887
                                //                        smaller than the sum of the decompressed size of all previous 
888
                                //                        pages, then it is to be preceded by a zero page with a size 
889
                                //                        that is equal to the difference between these two numbers.
890
                                section.PageCount = (int)sectionMapStream.ReadULong<LittleEndianConverter>();
13✔
891

892
                                //Read the name
893
                                if (sectionNameLength > 0)
13✔
894
                                {
12✔
895
                                        section.Name = sectionMapStream.ReadString(sectionNameLength, Encoding.Unicode);
12✔
896
                                        //Remove the empty characters
897
                                        section.Name = section.Name.Replace("\0", "");
12✔
898
                                }
12✔
899

900
                                ulong currentOffset = 0;
13✔
901
                                for (int i = 0; i < section.PageCount; ++i)
58✔
902
                                {
16✔
903
                                        DwgLocalSectionMap page = new DwgLocalSectionMap();
16✔
904
                                        //8        Page data offset.If a page’s data offset is 
905
                                        //        smaller than the sum of the decompressed size
906
                                        //        of all previous pages, then it is to be preceded 
907
                                        //        by a zero page with a size that is equal to the 
908
                                        //        difference between these two numbers.
909
                                        page.Offset = sectionMapStream.ReadULong<LittleEndianConverter>();
16✔
910
                                        //8        Page Size
911
                                        page.Size = sectionMapStream.ReadLong<LittleEndianConverter>();
16✔
912
                                        //8        Page Id
913
                                        page.PageNumber = (int)sectionMapStream.ReadLong<LittleEndianConverter>();
16✔
914
                                        //8        Page Uncompressed Size
915
                                        page.DecompressedSize = sectionMapStream.ReadULong<LittleEndianConverter>();
16✔
916
                                        //8        Page Compressed Size
917
                                        page.CompressedSize = sectionMapStream.ReadULong<LittleEndianConverter>();
16✔
918
                                        //8        Page Checksum
919
                                        page.Checksum = sectionMapStream.ReadULong<LittleEndianConverter>();
16✔
920
                                        //8        Page CRC
921
                                        page.CRC = sectionMapStream.ReadULong<LittleEndianConverter>();
16✔
922

923
                                        //Add the page to the section
924
                                        section.LocalSections.Add(page);
16✔
925
                                        //Move the offset
926
                                        currentOffset = page.Offset + page.DecompressedSize;
16✔
927
                                }
16✔
928
                                if (sectionNameLength > 0)
13✔
929
                                        fileheader.Descriptors.Add(section.Name, section);
12✔
930
                        }
13✔
931
                }
1✔
932

933
                private void readFileMetaData(DwgFileHeaderAC18 fileheader, IDwgStreamReader sreader)
934
                {
99✔
935
                        //5 bytes of 0x00 
936
                        sreader.Advance(5);
99✔
937

938
                        //0x0B        1        Maintenance release version
939
                        fileheader.AcadMaintenanceVersion = sreader.ReadByte();
99✔
940
                        //0x0C        1        Byte 0x00, 0x01, or 0x03
941
                        sreader.Advance(1);
99✔
942
                        //0x0D        4        Preview address(long), points to the image page + page header size(0x20).
943
                        fileheader.PreviewAddress = sreader.ReadRawLong();
99✔
944
                        //0x11        1        Dwg version (Acad version that writes the file)
945
                        fileheader.DwgVersion = sreader.ReadByte();
99✔
946
                        //0x12        1        Application maintenance release version(Acad maintenance version that writes the file)
947
                        fileheader.AppReleaseVersion = sreader.ReadByte();
99✔
948

949
                        //0x13        2        Codepage
950
                        fileheader.DrawingCodePage = CadUtils.GetCodePage(sreader.ReadShort());
99✔
951

952
                        //Advance empty bytes 
953
                        //0x15        3        3 0x00 bytes
954
                        sreader.Advance(3);
99✔
955

956
                        //0x18        4        SecurityType (long), see R2004 meta data, the definition is the same, paragraph 4.1.
957
                        fileheader.SecurityType = sreader.ReadRawLong();
99✔
958
                        //0x1C        4        Unknown long
959
                        sreader.ReadRawLong();
99✔
960
                        //0x20        4        Summary info Address in stream
961
                        fileheader.SummaryInfoAddr = sreader.ReadRawLong();
99✔
962
                        //0x24        4        VBA Project Addr(0 if not present)
963
                        fileheader.VbaProjectAddr = sreader.ReadRawLong();
99✔
964

965
                        //0x28        4        0x00000080
966
                        sreader.ReadRawLong();
99✔
967

968
                        //0x2C        4        App info Address in stream
969
                        sreader.ReadRawLong();
99✔
970

971
                        //Get to offset 0x80
972
                        //0x30        0x80        0x00 bytes
973
                        sreader.Advance(80);
99✔
974
                }
99✔
975

976
                private void readEncriptedData(DwgFileHeaderAC18 fileHeader, IDwgStreamReader sreader)
977
                {
4✔
978
                        //0x80        0x6C        Encrypted Data
979
                        //Metadata:
980
                        //The encrypted data at 0x80 can be decrypted by exclusive or’ing the 0x6c bytes of data 
981
                        //from the file with the following magic number sequence:
982

983
                        //29 23 BE 84 E1 6C D6 AE 52 90 49 F1 F1 BB E9 EB
984
                        //B3 A6 DB 3C 87 0C 3E 99 24 5E 0D 1C 06 B7 47 DE
985
                        //B3 12 4D C8 43 BB 8B A6 1F 03 5A 7D 09 38 25 1F
986
                        //5D D4 CB FC 96 F5 45 3B 13 0D 89 0A 1C DB AE 32
987
                        //20 9A 50 EE 40 78 36 FD 12 49 32 F6 9E 7D 49 DC
988
                        //AD 4F 14 F2 44 40 66 D0 6B C4 30 B7
989

990
                        StreamIO headerStream = new StreamIO(new CRC32StreamHandler(sreader.ReadBytes(0x6C), 0U)); //108
4✔
991
                        headerStream.Encoding = TextEncoding.GetListedEncoding(CodePage.Windows1252);
4✔
992

993
                        //0x00        12        “AcFssFcAJMB” file ID string
994
                        string fileId = headerStream.ReadString(12);
4✔
995
                        if (fileId != "AcFssFcAJMB\0")
4!
NEW
996
                        {
×
NEW
997
                                this.notify($"File validation failed, id should be : AcFssFcAJMB\0, but is : {fileId}", NotificationType.Warning);
×
NEW
998
                        }
×
999

1000
                        //0x0C        4        0x00(long)
1001
                        headerStream.ReadInt<LittleEndianConverter>();
4✔
1002
                        //0x10        4        0x6c(long)
1003
                        headerStream.ReadInt<LittleEndianConverter>();
4✔
1004
                        //0x14        4        0x04(long)
1005
                        headerStream.ReadInt<LittleEndianConverter>();
4✔
1006
                        //0x18        4        Root tree node gap
1007
                        fileHeader.RootTreeNodeGap = headerStream.ReadInt<LittleEndianConverter>();
4✔
1008
                        //0x1C        4        Lowermost left tree node gap
1009
                        fileHeader.LeftGap = headerStream.ReadInt<LittleEndianConverter>();
4✔
1010
                        //0x20        4        Lowermost right tree node gap
1011
                        fileHeader.RigthGap = headerStream.ReadInt<LittleEndianConverter>();
4✔
1012
                        //0x24        4        Unknown long(ODA writes 1)        
1013
                        headerStream.ReadInt<LittleEndianConverter>();
4✔
1014
                        //0x28        4        Last section page Id
1015
                        fileHeader.LastPageId = headerStream.ReadInt<LittleEndianConverter>();
4✔
1016

1017
                        //0x2C        8        Last section page end address
1018
                        fileHeader.LastSectionAddr = headerStream.ReadULong<LittleEndianConverter>();
4✔
1019
                        //0x34        8        Second header data address pointing to the repeated header data at the end of the file
1020
                        fileHeader.SecondHeaderAddr = headerStream.ReadULong<LittleEndianConverter>();
4✔
1021

1022
                        //0x3C        4        Gap amount
1023
                        fileHeader.GapAmount = headerStream.ReadUInt<LittleEndianConverter>();
4✔
1024
                        //0x40        4        Section page amount
1025
                        fileHeader.SectionAmount = headerStream.ReadUInt<LittleEndianConverter>();
4✔
1026
                        //0x44        4        0x20(long)
1027
                        headerStream.ReadInt<LittleEndianConverter>();
4✔
1028
                        //0x48        4        0x80(long)
1029
                        headerStream.ReadInt<LittleEndianConverter>();
4✔
1030
                        //0x4C        4        0x40(long)
1031
                        headerStream.ReadInt<LittleEndianConverter>();
4✔
1032
                        //0x50        4        Section Page Map Id
1033
                        fileHeader.SectionPageMapId = headerStream.ReadUInt<LittleEndianConverter>();
4✔
1034
                        //0x54        8        Section Page Map address(add 0x100 to this value)
1035
                        fileHeader.PageMapAddress = headerStream.ReadULong<LittleEndianConverter>() + 256UL;
4✔
1036
                        //0x5C        4        Section Map Id
1037
                        fileHeader.SectionMapId = headerStream.ReadUInt<LittleEndianConverter>();
4✔
1038
                        //0x60        4        Section page array size
1039
                        fileHeader.SectionArrayPageSize = headerStream.ReadUInt<LittleEndianConverter>();
4✔
1040
                        //0x64        4        Gap array size
1041
                        fileHeader.GapArraySize = headerStream.ReadUInt<LittleEndianConverter>();
4✔
1042
                        //0x68        4        CRC32(long).See paragraph 2.14.2 for the 32 - bit CRC calculation, 
1043
                        //                        the seed is zero. Note that the CRC 
1044
                        //                        calculation is done including the 4 CRC bytes that are 
1045
                        //                        initially zero! So the CRC calculation takes into account 
1046
                        //                        all of the 0x6c bytes of the data in this table.
1047
                        fileHeader.CRCSeed = headerStream.ReadUInt();
4✔
1048
                }
4✔
1049

1050
                private async Task<byte[]> getPageBufferAsync(ulong pageOffset, ulong compressedSize, ulong uncompressedSize, ulong correctionFactor, int blockSize, Stream stream)
1051
                {
2✔
1052
                        //Avoid shifted bits
1053
                        ulong v = compressedSize + 7L;
2✔
1054
                        ulong v1 = v & 0b11111111_11111111_11111111_11111000L;
2✔
1055

1056
                        uint totalSize = (uint)(v1 * correctionFactor);
2✔
1057

1058
                        int factor = (int)(totalSize + blockSize - 1L) / blockSize;
2✔
1059
                        int lenght = factor * byte.MaxValue;
2✔
1060

1061
                        byte[] buffer = new byte[lenght];
2✔
1062

1063
                        //Relative to data page map 1, add 0x480 to get stream position
1064
                        stream.Position = (long)(0x480 + pageOffset);
2✔
1065
                        await stream.ReadAsync(buffer, 0, lenght);
2✔
1066

1067
                        byte[] compressedData = new byte[(int)totalSize];
2✔
1068
                        this.reedSolomonDecoding(buffer, compressedData, factor, blockSize);
2✔
1069

1070
                        byte[] decompressedData = new byte[uncompressedSize];
2✔
1071

1072
                        DwgLZ77AC21Decompressor.Decompress(compressedData, 0U, (uint)compressedSize, decompressedData);
2✔
1073

1074
                        return decompressedData;
2✔
1075
                }
2✔
1076

1077
                private byte[] getPageBuffer(ulong pageOffset, ulong compressedSize, ulong uncompressedSize, ulong correctionFactor, int blockSize, Stream stream)
1078
                {
28✔
1079
                        //Avoid shifted bits
1080
                        ulong v = compressedSize + 7L;
28✔
1081
                        ulong v1 = v & 0b11111111_11111111_11111111_11111000L;
28✔
1082

1083
                        uint totalSize = (uint)(v1 * correctionFactor);
28✔
1084

1085
                        int factor = (int)(totalSize + blockSize - 1L) / blockSize;
28✔
1086
                        int lenght = factor * byte.MaxValue;
28✔
1087

1088
                        byte[] buffer = new byte[lenght];
28✔
1089

1090
                        //Relative to data page map 1, add 0x480 to get stream position
1091
                        stream.Position = (long)(0x480 + pageOffset);
28✔
1092
                        stream.Read(buffer, 0, lenght);
28✔
1093

1094
                        byte[] compressedData = new byte[(int)totalSize];
28✔
1095
                        this.reedSolomonDecoding(buffer, compressedData, factor, blockSize);
28✔
1096

1097
                        byte[] decompressedData = new byte[uncompressedSize];
28✔
1098

1099
                        DwgLZ77AC21Decompressor.Decompress(compressedData, 0U, (uint)compressedSize, decompressedData);
28✔
1100

1101
                        return decompressedData;
28✔
1102
                }
28✔
1103

1104
                private void reedSolomonDecoding(byte[] encoded, byte[] buffer, int factor, int blockSize)
1105
                {
45✔
1106
                        int index = 0;
45✔
1107
                        int n = 0;
45✔
1108
                        int length = buffer.Length;
45✔
1109
                        for (int i = 0; i < factor; ++i)
756✔
1110
                        {
333✔
1111
                                int cindex = n;
333✔
1112
                                if (n < encoded.Length)
333✔
1113
                                {
333✔
1114
                                        int size = Math.Min(length, blockSize);
333✔
1115
                                        length -= size;
333✔
1116
                                        int offset = index + size;
333✔
1117
                                        while (index < offset)
77,224✔
1118
                                        {
76,891✔
1119
                                                buffer[index] = encoded[cindex];
76,891✔
1120
                                                ++index;
76,891✔
1121
                                                cindex += factor;
76,891✔
1122
                                        }
76,891✔
1123
                                }
333✔
1124
                                ++n;
333✔
1125
                        }
333✔
1126
                }
45✔
1127

1128
                private void getPageHeaderData(IDwgStreamReader sreader,
1129
                        out long sectionType,
1130
                        out long decompressedSize,
1131
                        out long compressedSize,
1132
                        out long compressionType,
1133
                        out long checksum
1134
                        )
1135
                {
168✔
1136
                        //0x00        4        Section page type:
1137
                        //Section page map: 0x41630e3b
1138
                        //Section map: 0x4163003b
1139
                        sectionType = sreader.ReadRawLong();
168✔
1140
                        //0x04        4        Decompressed size of the data that follows
1141
                        decompressedSize = sreader.ReadRawLong();
168✔
1142
                        //0x08        4        Compressed size of the data that follows(CompDataSize)
1143
                        compressedSize = sreader.ReadRawLong();
168✔
1144

1145
                        //0x0C        4        Compression type(0x02)
1146
                        compressionType = sreader.ReadRawLong();
168✔
1147
                        //0x10        4        Section page checksum
1148
                        checksum = sreader.ReadRawLong();
168✔
1149
                }
168✔
1150

1151
                private async Task<Stream> getHeaderAC15Stream(CancellationToken cancellationToken = default)
1152
                {
2✔
1153
                        bool match = false;
2✔
1154
                        List<byte> buffer = new List<byte>();
2✔
1155
                        buffer.AddRange(await this._fileStream.ReadBytesAsync(16, cancellationToken));
2✔
1156

1157
                        do
1158
                        {
143✔
1159
                                byte[] sn = buffer.Skip(Math.Max(0, buffer.Count() - 16)).ToArray();
143✔
1160
                                if (CheckSentinel(sn, DwgFileHeaderAC15.EndSentinel))
143✔
1161
                                {
2✔
1162
                                        match = true;
2✔
1163
                                }
2✔
1164
                                else
1165
                                {
141✔
1166
                                        buffer.Add(await this._fileStream.ReadByteAsync(cancellationToken));
141✔
1167
                                }
141✔
1168
                        }
143✔
1169
                        while (!match);
143✔
1170

1171
                        return new MemoryStream(buffer.ToArray());
2✔
1172
                }
2✔
1173

1174
                private async Task<IDwgStreamReader> getStreamChunkAsync(int length, CancellationToken cancellationToken = default)
1175
                {
25✔
1176
                        MemoryStream ms = new MemoryStream(await this._fileStream.ReadBytesAsync(length, cancellationToken));
25✔
1177
                        return DwgStreamReaderBase.GetStreamHandler(this._version, ms);
25✔
1178
                }
25✔
1179

1180
                private void updateFileVersion(byte[] buffer)
1181
                {
136✔
1182
                        ACadVersion version = CadUtils.GetVersionFromName(Encoding.ASCII.GetString(buffer));
136✔
1183
                        this.setVersion(version);
136✔
1184
                }
136✔
1185
        }
1186
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc