This page covers how a DWG file is put together: the file header at the start, and how a reader finds the header variables, classes, object map and objects. There are three layouts. R13 to R2000 files hold their sections directly, found through a table of section locators. R2004, R2010, R2013 and R2018 files are split into pages, with an encrypted header, page and section maps, and LZ77 compression. R2007 files use pages too, but with a different header, Reed-Solomon error correction and another compression scheme. It follows the Open Design Alliance’s Open Design Specification for .dwg files, version 5.4.1, chapters 3 to 8. All values are little-endian unless stated. Type codes such as RL and BS are explained on Bit codes and data types.
- Version strings
- R13 to R2000: layout, file header, section locators, worked example
- R2004 and later: file header, encrypted header data, page checksum, system pages, page map, section map, sections, data page headers, compression
- R2007: meta data, file header, page map, section map, sections, system and data pages, compression, CRC random encoding, 64-bit CRCs, Reed-Solomon coding
- R2010, R2013 and R2018
Version strings
The first six bytes are an ASCII version string. The ODA specification lists these; the code in the third column is the one the auxiliary header stores for the released (non-beta) format.
| Bytes | Release (ODA) | Version code | Layout |
|---|---|---|---|
AC1012 | R13 | 19 | R13 to R2000 |
AC1014 | R14 | 21 | R13 to R2000 |
AC1015 | R2000 (R15) | 23 | R13 to R2000 |
AC1018 | R2004 (R18) | 25 | R2004 |
AC1021 | R2007 (R21) | 27 | R2007 |
AC1024 | R2010 (R24) | 29 | R2004, with R2007 bit coding |
AC1027 | R2013 (R27) | 31 | as R2010 |
AC1032 | R2018 | 33 | as R2013 |
The auxiliary header’s list also gives codes for AC1010 (17),
AC1011 (18) and AC1013 (20), and a beta code one below
each released code from AC1015 on. LibreDWG’s version table names
AC1010 and AC1011 as R13 betas and AC1013 as
Release 13c3. The specification notes that the layout changed between R13 C2 and
R13 C3; a file is in the C3 layout when it has four or more section locators.
R13 to R2000 (AC1012, AC1014, AC1015)
Layout
The parts of the file, in order:
- File header (below), ending in a CRC and a sentinel
- Header variables, then a CRC
- Class definitions
- Template (R13 only, optional)
- Padding (R13 C3 and later; see Not confirmed for its size)
- Image data (before R13 C3)
- Object data: all entities, table entries, dictionary entries and other objects
- Object map
- Object free space (optional)
- Template (R14 and R2000, optional)
- Second header
- Image data (R13 C3 and later)
File header
| Offset | Size | Type | Meaning |
|---|---|---|---|
| 0x00 | 6 | ASCII | Version string, e.g. AC1014 |
| 0x06 | 6 | RC | Six zero bytes; in R14, five zeros and then the ACADMAINTVER variable. Other values have been seen, of unclear meaning |
| 0x0C | 1 | RC | 1 |
| 0x0D | 4 | RL | Image seeker: absolute address of the beginning sentinel of the image data |
| 0x11 | 2 | – | Not described by the specification (zero in its example) |
| 0x13 | 2 | RS | DWGCODEPAGE: the drawing’s code page |
| 0x15 | 4 | RL | Number of section locator records that follow |
| 0x19 | 9 each | RC, RL, RL | Section locator records: record number, seeker (absolute address), size |
| after the records | 2 | RS | CRC of everything from the start of the file to here (below) |
| then | 16 | SN | Sentinel 95 A0 4E 28 99 82 1A E5 5E 41 E0 5F 9D 3A 4D 00 |
The CRC is the 8-bit CRC with initial value 0, and the result is XORed with a value that depends on the number of locator records:
| Records | XOR with |
|---|---|
| 3 | 0xA598 |
| 4 | 0x8101 |
| 5 | 0x3CC4 |
| 6 | 0x8461 |
Section locators
| Record | Section |
|---|---|
| 0 | Header variables, including their beginning and ending sentinels |
| 1 | Class section |
| 2 | Object map |
| 3 | R13 C3 and later: a special table without sentinels, the unknown section. Its presence marks the C3 layout |
| 4 | A place where data may be stored; only the MEASUREMENT variable has been seen there (the template). Optional |
| 5 | Seen in some files, meaning unknown. The ODA’s toolkit writes only records 0 to 4 |
Worked example
The specification prints the start of an empty R14 drawing,
empty14.dwg. These are its first 0x58 bytes and the 16 that follow:
00000 41 43 31 30 31 34 00 00 00 00 00 00 01 3F 0C 00
00010 00 00 00 1E 00 05 00 00 00 00 58 00 00 00 ED 01
00020 00 00 01 45 02 00 00 26 00 00 00 02 27 0B 00 00
00030 50 00 00 00 03 77 0B 00 00 35 00 00 00 04 3B 0C
00040 00 00 04 00 00 00 2D 5C 95 A0 4E 28 99 82 1A E5
00050 5E 41 E0 5F 9D 3A 4D 00 CF 7B 1F 23 FD DE 38 A9
00060 5F 7C 68 B8 4E 6D 33 5F C7 01 00 00 00 00 07 00
empty14.dwg, as printed in chapter 9 of
the ODA specification (regrouped 16 bytes to a line).| Offset | Bytes | Meaning |
|---|---|---|
| 0x00 | 41 43 31 30 31 34 | AC1014: R14 |
| 0x06 | 00 00 00 00 00 00 | Five zeros and ACADMAINTVER = 0 |
| 0x0C | 01 | 1 |
| 0x0D | 3F 0C 00 00 | Image data at 0xC3F |
| 0x13 | 1E 00 | Code page 30 |
| 0x15 | 05 00 00 00 | Five locator records |
| 0x19 | 00 58 00 00 00 ED 01 00 00 | Record 0: header variables at 0x58, 0x1ED bytes |
| 0x22 | 01 45 02 00 00 26 00 00 00 | Record 1: classes at 0x245, 0x26 bytes |
| 0x2B | 02 27 0B 00 00 50 00 00 00 | Record 2: object map at 0xB27, 0x50 bytes |
| 0x34 | 03 77 0B 00 00 35 00 00 00 | Record 3: unknown section at 0xB77, 0x35 (53) bytes |
| 0x3D | 04 3B 0C 00 00 04 00 00 00 | Record 4: 4 bytes at 0xC3B |
| 0x46 | 2D 5C | CRC 0x5C2D (already XORed with 0x3CC4, for five records) |
| 0x48 | 95 A0 … 4D 00 | File header sentinel |
| 0x58 | CF 7B … 33 5F | Header variables beginning sentinel |
| 0x68 | C7 01 00 00 | Header variables data size, 0x1C7 bytes |
The sizes agree: 16 (sentinel) + 4 (size) + 0x1C7 (data) + 2 (CRC) + 16 (sentinel) is 0x1ED, and the class section starts at 0x58 + 0x1ED = 0x245.
R2004 and later (AC1018, AC1024, AC1027, AC1032)
After the file header, the file is a sequence of pages. A system section is one page; a data section is one or more pages. Two system sections, the page map and the section map, act as the table of contents; once they are read, every other section can be reached directly. R2010, R2013 and R2018 keep this layout (see below).
File header
| Offset | Size | Meaning |
|---|---|---|
| 0x00 | 6 | Version string, AC1018 for R2004 |
| 0x06 | 5 | Five zero bytes |
| 0x0B | 1 | Maintenance release version |
| 0x0C | 1 | 0x00, 0x01 or 0x03 |
| 0x0D | 4 | Preview address: the image page plus the page header size (0x20) |
| 0x11 | 1 | Application version: the AutoCAD version that wrote the file |
| 0x12 | 1 | Application maintenance release version |
| 0x13 | 2 | Code page |
| 0x15 | 3 | Three zero bytes |
| 0x18 | 4 | Security flags (below), default 0 |
| 0x1C | 4 | Unknown (the ODA writes 0) |
| 0x20 | 4 | Summary info address: its page plus the page header size (0x20) |
| 0x24 | 4 | VBA project address, 0 if there is none |
| 0x28 | 4 | 0x00000080 |
| 0x2C | 0x54 | Zero bytes |
| 0x80 | 0x6C | Encrypted header data (below) |
Security flags (offset 0x18)
| Bit | Meaning |
|---|---|
| 0x0001 | Encrypt data: all data sections except AcDb:Preview and AcDb:SummaryInfo |
| 0x0002 | Encrypt properties: AcDb:Preview and AcDb:SummaryInfo |
| 0x0010 | Sign data |
| 0x0020 | Add timestamp |
When the value is above 0, the file has an AcDb:Security section.
Encrypted header data
The 0x6C bytes at 0x80 are XORed with a fixed sequence. The sequence comes from this generator, which the specification gives:
int randseed = 1;
for (int i = 0; i < 0x6c; i++) {
randseed *= 0x343fd;
randseed += 0x269ec3;
data[i] = (unsigned char)(randseed >> 0x10);
}
29 23 BE 84 E1 6C D6 AE 52 90 49 F1
F1 BB E9 EB and ends … 6B C4 30 B7.Decrypted, the bytes are the “2004 file header data”. The same data is repeated near the end of the file (the second header data).
| Offset | Size | Meaning |
|---|---|---|
| 0x00 | 12 | File ID string AcFssFcAJMB (11 characters in a 12-byte field) |
| 0x0C | 4 | 0x00 |
| 0x10 | 4 | 0x6C |
| 0x14 | 4 | 0x04 |
| 0x18 | 4 | Root tree node gap |
| 0x1C | 4 | Lowermost left tree node gap |
| 0x20 | 4 | Lowermost right tree node gap |
| 0x24 | 4 | Unknown (the ODA writes 1) |
| 0x28 | 4 | Last section page ID |
| 0x2C | 8 | Last section page end address |
| 0x34 | 8 | Address of the second header data at the end of the file |
| 0x3C | 4 | Gap amount |
| 0x40 | 4 | Section page amount |
| 0x44 | 4 | 0x20 |
| 0x48 | 4 | 0x80 |
| 0x4C | 4 | 0x40 |
| 0x50 | 4 | Section page map ID |
| 0x54 | 8 | Section page map address; add 0x100 |
| 0x5C | 4 | Section map ID |
| 0x60 | 4 | Section page array size |
| 0x64 | 4 | Gap array size |
| 0x68 | 4 | CRC-32 (32-bit CRC, seed 0) over all 0x6C bytes, computed with this field set to zero |
The next 0x14 bytes of the file header, at 0xEC, are copied from the magic sequence starting at its offset 0x100 − 0x14. They appear in the header at the start of the file but not in the copy at the end.
Page checksum
System and data page headers carry a 32-bit checksum computed by this function:
uint32_t checksum(uint32_t seed, const uint8_t *data, uint32_t size)
{
uint32_t sum1 = seed & 0xffff;
uint32_t sum2 = seed >> 0x10;
while (size != 0) {
uint32_t chunk = size < 0x15b0 ? size : 0x15b0;
size -= chunk;
for (uint32_t i = 0; i < chunk; i++) {
sum1 += *data++;
sum2 += sum1;
}
sum1 %= 0xFFF1;
sum2 %= 0xFFF1;
}
return (sum2 << 0x10) | (sum1 & 0xffff);
}
System section pages
A system page starts with a 0x14-byte header:
| Offset | Size | Meaning |
|---|---|---|
| 0x00 | 4 | Page type: 0x41630E3B section page map; 0x4163003B section map |
| 0x04 | 4 | Decompressed size of the data that follows |
| 0x08 | 4 | Compressed size of the data that follows (CompDataSize) |
| 0x0C | 4 | Compression type, 0x02 |
| 0x10 | 4 | Section page checksum |
CompDataSize bytes of compressed data follow, and then a second copy of the header with the sizes and checksum set to zero. The checksum is computed in two stages: first over the header (with its checksum field 0) with seed 0, then over the compressed data using the first result as the seed. Every page starts on a 0x20-byte boundary; the gap before it is filled with bytes from the magic sequence.
Section page map
Decompressed, the page map is a list of entries, one per page in the file:
| Offset | Size | Meaning |
|---|---|---|
| 0x00 | 4 | Page number, from 1, unique in the file |
| 0x04 | 4 | Page size |
A negative page number marks a gap (unused space), and four more fields follow it:
| Offset | Size | Meaning |
|---|---|---|
| 0x00 | 4 | Parent |
| 0x04 | 4 | Left |
| 0x08 | 4 | Right |
| 0x0C | 4 | 0x00 |
The first page is at address 0x100; each later page’s address is the previous address plus the previous size. Pages are numbered consecutively, the system pages last, with a gap of one between the data page numbers and the system page numbers. The list, indexed from 1, is called the SectionPageMap.
Data section map
The section map describes the data sections only. Decompressed, it starts:
| Offset | Size | Meaning |
|---|---|---|
| 0x00 | 4 | Number of section descriptions (NumDescriptions) |
| 0x04 | 4 | 0x02 |
| 0x08 | 4 | 0x00007400 |
| 0x0C | 4 | 0x00 |
| 0x10 | 4 | Unknown; the ODA writes NumDescriptions |
Then each section description:
| Offset | Size | Meaning |
|---|---|---|
| 0x00 | 8 | Size of the section |
| 0x08 | 4 | Page count: pages actually written. Pages of only zeros are left out |
| 0x0C | 4 | Maximum decompressed size of one page of this section (normally 0x7400) |
| 0x10 | 4 | Unknown |
| 0x14 | 4 | Compressed: 1 no, 2 yes (normally 2) |
| 0x18 | 4 | Section ID. The empty first section is 0; the others count down from the number of sections − 1 to 1 |
| 0x1C | 4 | Encrypted: 0 no, 1 yes, 2 unknown |
| 0x20 | 64 | Section name |
And then, page count times:
| Offset | Size | Meaning |
|---|---|---|
| 0x00 | 4 | Page number (index into the SectionPageMap) |
| 0x04 | 4 | Data size: the payload bytes stored after the page header. For a compressed section, the compressed length; for an uncompressed one, the full page including padding |
| 0x08 | 8 | Start offset of this page within the decompressed section |
Rules for assembling a section from its pages:
- If a page’s start offset is greater than the total decompressed size of the pages before it, zero pages (each of the section’s page size) were left out and must be inserted until the totals match. A smaller start offset means the file is malformed.
- After the last page, if the total is still short of the section size, add zero pages until it is reached.
- The order in the map is authoritative; pages must not be reordered. Page i starts at i times the page size.
- Every stored page is a whole page, padded to the page size, and the padding is covered by its data checksum. Take only the logical part: the lesser of the page size and the section size minus the page’s start offset.
- The page size must be read from each section description, not assumed: AcDb:FileDepList, AcDb:AppInfo, AcDb:Preview and AcDb:SummaryInfo use smaller pages.
Sections
Sections seen in R2004 files, in section map order:
| Name | Contents | Compressed | Page size |
|---|---|---|---|
| (empty name) | Empty section | yes | 0x7400 |
AcDb:Security | Password and encryption information; optional | no | 0x7400 |
AcDb:FileDepList | File dependencies such as images and fonts | no | 0x80 |
AcDb:VBAProject | VBA project; optional | no | data size + 0x80 + padding |
AcDb:AppInfo | The application that wrote the file (encrypted = 2) | no | 0x80 |
AcDb:Preview | Bitmap preview | no | 0x400 |
AcDb:SummaryInfo | Title, subject, author and so on | no | 0x100 |
AcDb:RevHistory | Revision history | yes | 0x7400 |
AcDb:AcDbObjects | Database objects | yes | 0x7400 |
AcDb:ObjFreeSpace | Object free space | yes | 0x7400 |
AcDb:Template | Template: the MEASUREMENT variable only | yes | 0x7400 |
AcDb:Handles | Object map: handles and offsets into AcDb:AcDbObjects | yes | 0x7400 |
AcDb:Classes | Custom classes | yes | 0x7400 |
AcDb:AuxHeader | Auxiliary header | yes | 0x7400 |
AcDb:Header | Header variables | yes | 0x7400 |
AcDb:Signature | Not written by the ODA | – | – |
The order in the file is different: file header, empty section, AcDb:SummaryInfo, AcDb:Preview, AcDb:VBAProject, AcDb:AppInfo, AcDb:FileDepList, AcDb:RevHistory, AcDb:Security, AcDb:AcDbObjects, AcDb:ObjFreeSpace, AcDb:Template, AcDb:Handles, AcDb:Classes, AcDb:AuxHeader, AcDb:Header, section map, section page map. R2013 adds the AcDb:AcDsPrototype_1b data storage section.
Data section page headers
Each data page starts with a 32-byte header masked with a fixed XOR (unrelated to
password encryption). Treat the header as eight 32-bit little-endian words and XOR
each with 0x4164536B ^ offset, where offset is the low 32
bits of the header’s absolute address in the file, exactly as the SectionPageMap
gives it (0x100 plus the sizes of the pages before it).
| Offset | Size | Meaning |
|---|---|---|
| 0x00 | 4 | Page type, always 0x4163043B for a data page |
| 0x04 | 4 | Section number |
| 0x08 | 4 | Data size: payload bytes after this header, over which the data checksum runs (compressed length, or the whole padded page if uncompressed) |
| 0x0C | 4 | Page size: the page’s extent in the file, at least 32 + data size rounded up to 0x20; may be larger when the page reuses a bigger gap |
| 0x10 | 8 | Start offset within the decompressed section |
| 0x18 | 4 | Page header checksum: over the decoded header with this field zeroed, seeded with the data checksum |
| 0x1C | 4 | Data checksum: over the stored (compressed) data, seed 0 |
For R2004 and later, the decompressed AcDb:AcDbObjects data starts with an RL of 0x0DCA, of unknown meaning.
Compression
R2004 compression is a variant of LZ77: back-references (offset and length) to data already decompressed, mixed with runs of literal bytes. A length may be greater than its offset. A compressed stream starts with a literal length, giving the first run of literal bytes. Then come opcode sets, each giving three values: compressedBytes to copy from earlier output, compOffset backwards from the current position, and litCount literal bytes to copy from the input after them.
| Opcode 1 | compressedBytes | compOffset | litCount |
|---|---|---|---|
| 0x00–0x0F | Not used, since they would be taken for a literal length | ||
| 0x10 | Next long compression offset + 9 | Next two-byte offset + 0x3FFF | From the two-byte offset; if 0, read a literal length |
| 0x11 | End of the input stream | ||
| 0x12–0x1F | (opcode1 & 0x0F) + 2 | Next two-byte offset + 0x3FFF | From the two-byte offset; if 0, read a literal length |
| 0x20 | Next long compression offset + 0x21 | Next two-byte offset | From the two-byte offset; if 0, read a literal length |
| 0x21–0x3F | opcode1 − 0x1E | Next two-byte offset | From the two-byte offset; if 0, read a literal length |
| 0x40–0xFF | ((opcode1 & 0xF0) >> 4) − 1 | Read opcode2; (opcode2 << 2) | ((opcode1 & 0x0C) >> 2) | opcode1 & 0x03: 0 read a literal length, otherwise that value (1 to 3) |
| Field | Encoding |
|---|---|
| Literal length | A byte 0x01–0x0E means that value + 3. A byte with any bit set in the high nibble means the literal length is 0 and the byte is the next opcode 1. A byte 0x00 starts a running total of 0x0F; each further 0x00 adds 0xFF, and the first non-zero byte adds its value and ends it; then add 3. Examples: 05 is 8; 00 02 is 0x14 |
| Two-byte offset | firstByte, secondByte: offset = (firstByte >> 2) | (secondByte << 6); litCount = firstByte & 0x03 |
| Long compression offset | A byte 0x01–0xFF is the value. A byte 0x00 starts a running total of 0xFF; each further 0x00 adds 0xFF, and the first non-zero byte adds its value and ends it. Examples: DD is 0xDD; 00 00 34 is 0x232 |
LibreDWG’s decoder (decompress_R2004_section) reads these opcodes a
little differently, which matters to anyone writing a decoder: it adds 1 to every
back-reference offset, so the offsets above are the distance minus one; for opcodes
0x10 to 0x1F it takes the length from the low three bits (0 meaning an extended
length, read like a long compression offset and added to 7, then 2 added) and uses
bit 3 of the opcode as offset bit 14, adding 0x4000; and its literal length accepts
0x01 to 0x0F.
R2007 (AC1021)
R2007 also has system and data sections made of pages, but every part is coded differently from R2004. The order in the file:
| Part | Size | Meaning |
|---|---|---|
| Meta data | 0x80 | Version and addresses (below) |
| File header | 0x400 | Page map and section map addresses, sizes and CRCs |
| Page map 1 | 0x400 or more | The data page map |
| Page map 2 | 0x400 or more | A copy of the page map |
| Data sections | – | AcDb:SummaryInfo, AcDb:Preview, AcDb:VBAProject, AcDb:AppInfo, AcDb:FileDepList, AcDb:RevHistory, AcDb:Security, AcDb:AcDbObjects, AcDb:ObjFreeSpace, AcDb:Template, AcDb:Handles, AcDb:Classes, AcDb:AuxHeader, AcDb:Header |
| Section map 1 | – | The section map |
| Section map 2 | – | A copy of the section map |
Meta data
| Offset | Size | Meaning |
|---|---|---|
| 0x00 | 6 | AC1021 |
| 0x06 | 5 | Five zero bytes |
| 0x0B | 1 | Maintenance release version |
| 0x0C | 1 | 0x00, 0x01 or 0x03 |
| 0x0D | 4 | Preview address |
| 0x11 | 1 | DWG version: the AutoCAD version that wrote the file |
| 0x12 | 1 | Maintenance release version of that AutoCAD |
| 0x13 | 2 | Code page |
| 0x15 | 3 | Unknown (the ODA writes zeros) |
| 0x18 | 4 | Security type, as the R2004 security flags |
| 0x1C | 4 | Unknown |
| 0x20 | 4 | Summary info address |
| 0x24 | 4 | VBA project address (0 if none) |
| 0x28 | 4 | 0x00000080 |
| 0x2C | 4 | Application info address |
File header
At 0x80 is a 0x400-byte block. Its last 0x28 bytes, from 0x3D8, are check data: five 64-bit values (the normal CRC, the mirrored CRC, two random values and the encoded CRC seed, in that order in the file). The first 0x3D8 bytes are Reed-Solomon (255, 239) coded, interleaved with a factor of 3. Decoded, they hold:
| Offset | Size | Meaning |
|---|---|---|
| 0x00 | 8 | CRC |
| 0x08 | 8 | Unknown key |
| 0x10 | 8 | Compressed data CRC |
| 0x18 | 4 | ComprLen. Negative: the data is stored uncompressed and its length is the absolute value. Positive: the data is compressed and decompresses to 0x110 bytes |
| 0x1C | 4 | Length2 |
| 0x20 | ComprLen | Data |
The decompressed 0x110 bytes are the file header proper:
| Offset | Size | Meaning |
|---|---|---|
| 0x00 | 8 | Header size (normally 0x70) |
| 0x08 | 8 | File size |
| 0x10 | 8 | PagesMapCrcCompressed |
| 0x18 | 8 | PagesMapCorrectionFactor |
| 0x20 | 8 | PagesMapCrcSeed |
| 0x28 | 8 | PagesMap2Offset, relative to page map 1; add 0x480 for the file position |
| 0x30 | 8 | PagesMap2Id |
| 0x38 | 8 | PagesMapOffset, relative to page map 1; add 0x480 |
| 0x40 | 8 | PagesMapId |
| 0x48 | 8 | Header2Offset, relative to page map 1; add 0x480 |
| 0x50 | 8 | PagesMapSizeCompressed |
| 0x58 | 8 | PagesMapSizeUncompressed |
| 0x60 | 8 | PagesAmount |
| 0x68 | 8 | PagesMaxId: the largest page index used |
| 0x70 | 8 | Unknown (normally 0x20) |
| 0x78 | 8 | Unknown (normally 0x40) |
| 0x80 | 8 | PagesMapCrcUncompressed |
| 0x88 | 8 | Unknown (normally 0xF800) |
| 0x90 | 8 | Unknown (normally 4) |
| 0x98 | 8 | Unknown (normally 1) |
| 0xA0 | 8 | SectionsAmount: number of sections + 1 |
| 0xA8 | 8 | SectionsMapCrcUncompressed |
| 0xB0 | 8 | SectionsMapSizeCompressed |
| 0xB8 | 8 | SectionsMap2Id |
| 0xC0 | 8 | SectionsMapId |
| 0xC8 | 8 | SectionsMapSizeUncompressed |
| 0xD0 | 8 | SectionsMapCrcCompressed |
| 0xD8 | 8 | SectionsMapCorrectionFactor |
| 0xE0 | 8 | SectionsMapCrcSeed |
| 0xE8 | 8 | StreamVersion (normally 0x60100) |
| 0xF0 | 8 | CrcSeed (the ODA writes 0) |
| 0xF8 | 8 | CrcSeedEncoded |
| 0x100 | 8 | RandomSeed |
| 0x108 | 8 | Header CRC64 |
The file header is written at 0x80 and again at the end of the file. To write it,
the specification computes the CRCs and check data, compresses the header,
builds a buffer of 3 × 239 bytes holding a checking sequence, the compressed CRC,
the compressed size and the data, repeated as often as it fits and padded with
bytes from the random encoder, Reed-Solomon codes it to
3 × 255 bytes, pads the 0x400-byte page with random bytes and puts the check data
in its last bytes. The CRC seeds are themselves encoded with the random encoder.
The check data uses a 64-bit rotate, Encode(value, control), which
rotates value left by control & 0x1F bits; the
specification gives the full order of operations in its section 5.2.1.
Page map
The page map is one system page at PagesMapOffset, decoded as a system page. Its data is pairs of 64-bit values, a size and an ID; a page’s offset is the sum of the sizes before it:
int64_t offset = 0;
while (!eof) {
int64_t size = read_int64();
int64_t id = read_int64();
int64_t index = id > 0 ? id : -id;
pages[index].id = id;
pages[index].size = size;
pages[index].offset = offset;
offset += size;
}
Section map
The section map is at the offset of page SectionsMapId, decoded as a system page with the SectionsMap sizes, CRCs and correction factor from the file header. For each section it holds:
| Offset | Size | Meaning |
|---|---|---|
| 0x00 | 8 | Data size |
| 0x08 | 8 | Maximum page size |
| 0x10 | 8 | Encryption |
| 0x18 | 8 | Hash code |
| 0x20 | 8 | Section name length |
| 0x28 | 8 | Unknown |
| 0x30 | 8 | Encoding |
| 0x38 | 8 | Number of pages present (pages of only zeros are left out, as in R2004) |
| 0x40 | length × 2 [+ 2] | Section name in UTF-16, followed by two zero bytes if the length is above 0 |
Then, for each page:
| Size | Meaning |
|---|---|
| 8 | Page data offset. If it is greater than the sum of the earlier pages’ decompressed sizes, a zero page of the difference precedes it |
| 8 | Page size |
| 8 | Page ID |
| 8 | Uncompressed size |
| 8 | Compressed size |
| 8 | Page checksum |
| 8 | Page CRC |
Each page may be Reed-Solomon coded, compressed or encrypted. By default nothing is encrypted; the specification says encryption is still to be described.
Sections
| Name | Hash code | Page size | Encryption | Encoding | Compressed |
|---|---|---|---|---|---|
AcDb:Security | 0x4A0204EA | 0xF800 | 0 | 1 | – |
AcDb:FileDepList | 0x6C4205CA | 0x100 if no entries, otherwise 0x80 × (entries + entries / 2) | 2 | 1 | – |
AcDb:VBAProject | 0x586E0544 | VBA data size + 0x80, rounded up to 0x20 | 2 | 1 | – |
AcDb:AppInfo | 0x3FA0043E | 0x300 | 0 | 1 | – |
AcDb:Preview | 0x40AA0473 | 0x400 by default; with an image, the preview size rounded up to 0x20 | 1 if properties are encrypted, else 0 | 1 | – |
AcDb:SummaryInfo | 0x717A060F | 0x80 | 1 if properties are encrypted, else 0 | 1 | – |
AcDb:RevHistory | 0x60A205B3 | 0x1000 | 0 | 4 | yes |
AcDb:AcDbObjects | 0x674C05A9 | 0xF800 | 1 if data is encrypted, else 0 | 4 | yes |
AcDb:ObjFreeSpace | 0x77E2061F | 0xF800 | 0 | 4 | yes |
AcDb:Template | 0x4A1404CE | 0x400 | 0 | 4 | yes |
AcDb:Handles | 0x3F6E0450 | 0xF800 | 1 if data is encrypted, else 0 | 4 | yes |
AcDb:Classes | 0x3F54045F | 0xF800 | 1 if data is encrypted, else 0 | 4 | yes |
AcDb:AuxHeader | 0x54F0050A | 0x800 | 0 | 4 | yes |
AcDb:Header | 0x32B803D9 | 0x800 | 1 if data is encrypted, else 0 | 4 | yes |
AcDb:Signature | Not written by the ODA | ||||
Encoding 1 means the Reed-Solomon blocks are not interleaved; 4 means they are. In R2007 the AcDb:Classes and AcDb:Header data keep their strings in a separate string stream, and the header’s handles in a separate handle stream, as objects do (see the string stream).
System and data pages
System pages (the page map and section map) take the data and a 64-bit CRC seed. The specification computes a mirrored 64-bit CRC of the data (seed updated with UpdateSeed1), compresses it if that makes it shorter, computes another CRC of the result, pads it with zeros to a multiple of 8, repeats it as many times as fits, Reed-Solomon codes it (255, 239), interleaved, and fills the rest of the page with random bytes. The number of repeats is the correction factor stored in the file header. The page size is at least 0x400:
uint64_t system_page_size(uint64_t data_size)
{
uint64_t aligned = (data_size + 7) & ~7ULL; /* CRC block size 8 */
uint64_t size = (aligned * 2 + 239 - 1) / 239 * 255; /* fit the data twice */
if (size < 0x400)
return 0x400;
return (size + 0x1F) & ~0x1FULL; /* page alignment 0x20 */
}
GetSystemPageSize in section 5.3.1
of the specification.Data pages hold all other sections, split into pages of the section’s maximum size. For each page: a 32-bit checksum of the data (below), then optional compression (kept only if shorter), optional encryption, a mirrored 64-bit CRC (seed: the file’s CRC seed updated with UpdateSeed1), zero padding to a multiple of 8, and Reed-Solomon (255, 251) coding, interleaved when the section’s encoding is 4. Pages start on 0x20-byte boundaries and are padded with zeros.
The data page checksum seeds the R2004 checksum with
seed = (seed + length) * 0x343FD + 0x269EC3, then adds bytes in the
same way, except that within each 8-byte group the bytes are taken in the order
6, 7, 4, 5, 2, 3, 0, 1, and a remainder of 1 to 7 bytes has its own order
(section 5.4.1 of the specification lists each case).
Compression
R2007 uses another LZ77 variant. The source contains opcodes, offsets and
lengths; a literal run is followed by one or more back-references. If the first
opcode’s high nibble is 2, the source pointer moves 2 bytes, a length is read from
the next byte and masked with 0x07, and the pointer moves one more byte. A literal
length is read as opcode + 8; if that is 0x17, the next byte is added,
and if that byte was 0xFF, 16-bit little-endian values are added until one is not
0xFFFF. Literal bytes are copied in 32-byte blocks, with the bytes of each block in
a shuffled order that the specification tables for counts 1 to 32.
Back-references are read by opcode high nibble:
| opcode >> 4 | Length | Source offset |
|---|---|---|
| 0 | (opcode & 0x0F) + 0x13, plus ((next opcode >> 3) & 0x10) | Read a byte b, then the next opcode; ((opcode & 0x78) << 5) + 1 + b |
| 1 | (opcode & 0x0F) + 3 | Read a byte b, then the next opcode; ((opcode & 0xF8) << 5) + 1 + b |
| 2 | opcode & 7; if opcode bit 3 is clear, read the next opcode and add (opcode & 0xF8); if set, add (next byte << 3), read the next opcode, add ((opcode & 0xF8) << 8) and 0x100 | A 16-bit little-endian value; plus 1 if opcode bit 3 is set |
| 3 to 15 | opcode >> 4 | opcode & 15, then read the next opcode; ((opcode & 0xF8) << 1) + that + 1 |
After each copy, opcode & 7 gives the next literal length; if it
is 0 and input remains, the next byte is read as an opcode: high nibble 0 ends the
run of back-references, high nibble 15 is masked to its low nibble.
CRC random encoding
Some CRCs are stored as 64-bit values carrying only 10 data bits, the rest filled from a pseudo-random table of 0x270 32-bit values made from one 64-bit seed:
table[0] = (uint32_t)seed * 0x343fd + 0x269ec3;
table[1] = (uint32_t)(seed >> 32) * 0x343fd + 0x269ec3;
uint32_t value = table[1];
for (uint32_t i = 2; i < 0x270; i++) {
value = ((value >> 0x1e) ^ value) * 0x6c078965 + i;
table[i] = value;
}
To encode, take the next 64-bit random value (the index moves on by 2), mask its low word with 0xDF7DF7DF and high word with 0xF7DF7DF7, and set one bit for each of the 10 data bits:
| Data bit | Word | Bit set |
|---|---|---|
| 0x200 | low | 0x20 |
| 0x100 | low | 0x800 |
| 0x80 | low | 0x20000 |
| 0x40 | low | 0x800000 |
| 0x20 | low | 0x20000000 |
| 0x10 | high | 0x08 |
| 0x08 | high | 0x200 |
| 0x04 | high | 0x8000 |
| 0x02 | high | 0x200000 |
| 0x01 | high | 0x8000000 |
Decoding tests the same bits.
64-bit CRCs
R2007 uses two 64-bit CRCs, each with its own 256-entry table printed in the
specification. The normal CRC follows ECMA-182:
table[(byte ^ (crc >> 56)) & 0xFF] ^ (crc << 8) per
byte, with the result inverted at the end; its table starts 0x0000000000000000,
0x42F0E1EBA9EA3693. The mirrored CRC shifts the other way:
table[(crc ^ byte) & 0xFF] ^ (crc >> 8), not inverted; its
table starts 0x0000000000000000, 0x7AD870C830358979. Bytes are fed in groups of 8
in a fixed shuffled order. Two functions prepare the seed:
seed1 = (seed + length) * 0x343fd + 0x269ec3;
seed1 |= seed1 * (0x343fdULL << 32) + (0x269ec3ULL << 32);
seed1 = ~seed1; /* UpdateSeed1 */
seed2 = (seed + length) * 0x343fd + 0x269ec3;
seed2 = seed2 * ((1ULL << 32) + 0x343fd) + (length + 0x269ec3);
seed2 = ~seed2; /* UpdateSeed2 */
Reed-Solomon coding
| Used for | (n, k) | Primitive polynomial coefficients | Corrects |
|---|---|---|---|
| Data pages | (255, 251) | 1, 0, 1, 1, 1, 0, 0, 0 | 2 bytes per 255-byte block |
| System pages | (255, 239) | 1, 0, 0, 1, 0, 1, 1, 0 | 8 bytes per block |
Not interleaved: all data blocks come first, then the parity blocks. Interleaved: with n blocks, the bytes of block j (data and parity) go to positions n × i + j. A last block that is not full is padded with random bytes.
R2010, R2013 and R2018
- R2010 (AC1024) uses the R2004 file header, page map, section map and compression, and the R2007 bit coding, except that the object type is coded differently. As in R2007, data, strings and handles are in separate streams in the header and objects.
- R2013 (AC1027) is mostly R2010. The header section gains two fields, and a new section, AcDb:AcDsPrototype_1b, holds data storage such as ACIS data for regions and solids. Known valid maintenance versions in March 2013 were 6 and 8; the ODA writes 8.
- R2018 (AC1032) is structurally identical to R2013. The auxiliary header gains three zero shorts at its end; the header section’s extra unknown 32-bit value after the size is always present; ACAD_PROXY_ENTITY, ATTRIB, ATTDEF and MTEXT gain fields; and MLINESTYLE refers to line types by handle instead of index.
Not confirmed
- The R13 C3 padding is “200 bytes” in chapter 3 and 0x200 bytes in chapter 11 of the specification.
- The two bytes at 0x11 and 0x12 of the R13 to R2000 file header are not described.
- The meaning of the sixth section locator record, and of the RL 0x0DCA at the start of R2004 object data, is unknown.
- The R2007 check data is five 64-bit values (0x28 bytes from 0x3D8), but section 5.2.1.7 of the specification says the last 0x20 bytes of the page are overwritten with it.
- R2007 encryption is “still to be described” in the specification, and the R2004 literal length range is given as 0x01–0x0E with results 4 to 0x12, which only fits 0x01–0x0F (LibreDWG accepts 0x0F).
- The R2004 opcode table differs from LibreDWG’s decoder for opcodes 0x10 to 0x1F and in the +1 on offsets, as described under Compression; neither reading has been tested against files for this page.
Sources
- Open Design Specification for .dwg files, version 5.4.1 – Open Design Alliance; chapters 3 (R13 to R15), 4 (R2004), 5 (R2007), 6 to 8 (R2010, R2013, R2018), the header dump in chapter 9 and the version list in chapter 27
- LibreDWG include/dwg.h and src/common.c – the version table naming the pre-release strings
- LibreDWG src/decode.c –
decompress_R2004_section, the R2004 decompressor