pub mod names {
pub const ACDB_OBJECTS: &str = "AcDb:AcDbObjects";
pub const ACDS_PROTOTYPE: &str = "AcDb:AcDsPrototype_1b";
pub const APP_INFO: &str = "AcDb:AppInfo";
pub const AUX_HEADER: &str = "AcDb:AuxHeader";
pub const HEADER: &str = "AcDb:Header";
pub const CLASSES: &str = "AcDb:Classes";
pub const HANDLES: &str = "AcDb:Handles";
pub const OBJ_FREE_SPACE: &str = "AcDb:ObjFreeSpace";
pub const TEMPLATE: &str = "AcDb:Template";
pub const SUMMARY_INFO: &str = "AcDb:SummaryInfo";
pub const FILE_DEP_LIST: &str = "AcDb:FileDepList";
pub const PREVIEW: &str = "AcDb:Preview";
pub const REV_HISTORY: &str = "AcDb:RevHistory";
pub const SECURITY: &str = "AcDb:Security";
pub const VBA_PROJECT: &str = "AcDb:VBAProject";
pub const SIGNATURE: &str = "AcDb:Signature";
}
pub mod start_sentinels {
pub const HEADER: [u8; 16] = [
0xCF, 0x7B, 0x1F, 0x23, 0xFD, 0xDE, 0x38, 0xA9,
0x5F, 0x7C, 0x68, 0xB8, 0x4E, 0x6D, 0x33, 0x5F,
];
pub const CLASSES: [u8; 16] = [
0x8D, 0xA1, 0xC4, 0xB8, 0xC4, 0xA9, 0xF8, 0xC5,
0xC0, 0xDC, 0xF4, 0x5F, 0xE7, 0xCF, 0xB6, 0x8A,
];
pub const PREVIEW: [u8; 16] = [
0x1F, 0x25, 0x6D, 0x07, 0xD4, 0x36, 0x28, 0x28,
0x9D, 0x57, 0xCA, 0x3F, 0x9D, 0x44, 0x10, 0x2B,
];
}
pub mod end_sentinels {
pub const HEADER: [u8; 16] = [
0x30, 0x84, 0xE0, 0xDC, 0x02, 0x21, 0xC7, 0x56,
0xA0, 0x83, 0x97, 0x47, 0xB1, 0x92, 0xCC, 0xA0,
];
pub const CLASSES: [u8; 16] = [
0x72, 0x5E, 0x3B, 0x47, 0x3B, 0x56, 0x07, 0x3A,
0x3F, 0x23, 0x0B, 0xA0, 0x18, 0x30, 0x49, 0x75,
];
pub const PREVIEW: [u8; 16] = [
0xE0, 0xDA, 0x92, 0xF8, 0x2B, 0xC9, 0xD7, 0xD7,
0x62, 0xA8, 0x35, 0xC0, 0x62, 0xBB, 0xEF, 0xD4,
];
pub const FILE_HEADER: [u8; 16] = [
0x95, 0xA0, 0x4E, 0x28, 0x99, 0x82, 0x1A, 0xE5,
0x5E, 0x41, 0xE0, 0x5F, 0x9D, 0x3A, 0x4D, 0x00,
];
}
#[allow(dead_code)]
#[repr(u32)]
pub enum DwgSectionHash {
Unknown = 0x00000000,
Security = 0x4A0204EA,
FileDepList = 0x6C4205CA,
VbaProject = 0x586E0544,
AppInfo = 0x3FA0043E,
Preview = 0x40AA0473,
SummaryInfo = 0x717A060F,
RevHistory = 0x60A205B3,
AcDbObjects = 0x674C05A9,
ObjFreeSpace = 0x77E2061F,
Template = 0x4A1404CE,
Handles = 0x3F6E0450,
Classes = 0x3F54045F,
AuxHeader = 0x54F0050A,
Header = 0x32B803D9,
}
pub mod ac21_section_info {
use super::names;
pub fn hash_code(name: &str) -> Option<u32> {
match name {
names::HEADER => Some(0x32B803D9),
names::CLASSES => Some(0x3F54045F),
names::HANDLES => Some(0x3F6E0450),
names::ACDB_OBJECTS => Some(0x674C05A9),
names::OBJ_FREE_SPACE => Some(0x77E2061F),
names::TEMPLATE => Some(0x4A1404CE),
names::AUX_HEADER => Some(0x54F0050A),
names::REV_HISTORY => Some(0x60A205B3),
names::SUMMARY_INFO => Some(0x717A060F),
names::PREVIEW => Some(0x40AA0473),
names::APP_INFO => Some(0x3FA0043E),
names::FILE_DEP_LIST => Some(0x6C4205CA),
names::SECURITY => Some(0x4A0204EA),
names::VBA_PROJECT => Some(0x586E0544),
_ => None,
}
}
pub fn page_size(name: &str) -> Option<u64> {
match name {
names::HEADER => Some(0x800),
names::CLASSES => Some(0xF800),
names::HANDLES => Some(0xF800),
names::ACDB_OBJECTS => Some(0xF800),
names::OBJ_FREE_SPACE => Some(0xF800),
names::TEMPLATE => Some(0x400),
names::AUX_HEADER => Some(0x800),
names::REV_HISTORY => Some(0x1000),
names::SUMMARY_INFO => Some(0x80),
names::PREVIEW => Some(0x400),
names::APP_INFO => Some(0x300),
names::FILE_DEP_LIST => Some(0x100),
names::SECURITY => Some(0xF800),
names::VBA_PROJECT => None,
_ => None,
}
}
pub fn encoding(name: &str) -> Option<u64> {
match name {
names::HEADER
| names::CLASSES
| names::HANDLES
| names::ACDB_OBJECTS
| names::OBJ_FREE_SPACE
| names::TEMPLATE
| names::AUX_HEADER
| names::REV_HISTORY => Some(4),
names::SUMMARY_INFO
| names::PREVIEW
| names::APP_INFO
| names::FILE_DEP_LIST
| names::SECURITY
| names::VBA_PROJECT => Some(1),
_ => None,
}
}
pub fn encryption(name: &str) -> Option<u64> {
match name {
names::FILE_DEP_LIST
| names::VBA_PROJECT => Some(2),
names::HEADER
| names::CLASSES
| names::HANDLES
| names::ACDB_OBJECTS
| names::OBJ_FREE_SPACE
| names::TEMPLATE
| names::AUX_HEADER
| names::REV_HISTORY
| names::SUMMARY_INFO
| names::PREVIEW
| names::APP_INFO
| names::SECURITY => Some(0),
_ => None,
}
}
pub const ALL_SECTION_NAMES: &[&str] = &[
names::HEADER,
names::CLASSES,
names::HANDLES,
names::ACDB_OBJECTS,
names::OBJ_FREE_SPACE,
names::TEMPLATE,
names::AUX_HEADER,
names::REV_HISTORY,
names::SUMMARY_INFO,
names::PREVIEW,
names::APP_INFO,
names::FILE_DEP_LIST,
names::SECURITY,
names::VBA_PROJECT,
];
pub const SECTION_MAP_ORDER: &[&str] = &[
names::SECURITY,
names::FILE_DEP_LIST,
names::VBA_PROJECT,
names::APP_INFO,
names::PREVIEW,
names::SUMMARY_INFO,
names::REV_HISTORY,
names::ACDB_OBJECTS,
names::OBJ_FREE_SPACE,
names::TEMPLATE,
names::HANDLES,
names::CLASSES,
names::AUX_HEADER,
names::HEADER,
];
}
pub const PAGE_TYPE_DATA_SECTION: i32 = 0x4163043B;
pub const PAGE_TYPE_SECTION_MAP: i32 = 0x4163003B;
pub const PAGE_TYPE_SECTION_PAGE_MAP: i32 = 0x41630E3B;
pub fn section_locator_number(name: &str) -> Option<u8> {
match name {
names::HEADER => Some(0),
names::CLASSES => Some(1),
names::HANDLES => Some(2),
names::OBJ_FREE_SPACE => Some(3),
names::TEMPLATE => Some(4),
names::AUX_HEADER => Some(5),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_section_locator_numbers() {
assert_eq!(section_locator_number(names::HEADER), Some(0));
assert_eq!(section_locator_number(names::CLASSES), Some(1));
assert_eq!(section_locator_number(names::HANDLES), Some(2));
assert_eq!(section_locator_number(names::OBJ_FREE_SPACE), Some(3));
assert_eq!(section_locator_number(names::TEMPLATE), Some(4));
assert_eq!(section_locator_number(names::AUX_HEADER), Some(5));
assert_eq!(section_locator_number(names::ACDB_OBJECTS), None);
assert_eq!(section_locator_number(names::PREVIEW), None);
}
#[test]
fn test_sentinels_are_complement() {
for i in 0..16 {
assert_eq!(
start_sentinels::HEADER[i] ^ end_sentinels::HEADER[i],
0xFF,
"Header sentinel mismatch at index {i}"
);
assert_eq!(
start_sentinels::CLASSES[i] ^ end_sentinels::CLASSES[i],
0xFF,
"Classes sentinel mismatch at index {i}"
);
assert_eq!(
start_sentinels::PREVIEW[i] ^ end_sentinels::PREVIEW[i],
0xFF,
"Preview sentinel mismatch at index {i}"
);
}
}
#[test]
fn test_hash_code_matches_enum() {
assert_eq!(ac21_section_info::hash_code(names::HEADER), Some(DwgSectionHash::Header as u32));
assert_eq!(ac21_section_info::hash_code(names::CLASSES), Some(DwgSectionHash::Classes as u32));
assert_eq!(ac21_section_info::hash_code(names::HANDLES), Some(DwgSectionHash::Handles as u32));
assert_eq!(ac21_section_info::hash_code(names::ACDB_OBJECTS), Some(DwgSectionHash::AcDbObjects as u32));
assert_eq!(ac21_section_info::hash_code(names::OBJ_FREE_SPACE), Some(DwgSectionHash::ObjFreeSpace as u32));
assert_eq!(ac21_section_info::hash_code(names::TEMPLATE), Some(DwgSectionHash::Template as u32));
assert_eq!(ac21_section_info::hash_code(names::AUX_HEADER), Some(DwgSectionHash::AuxHeader as u32));
assert_eq!(ac21_section_info::hash_code(names::REV_HISTORY), Some(DwgSectionHash::RevHistory as u32));
assert_eq!(ac21_section_info::hash_code(names::SUMMARY_INFO), Some(DwgSectionHash::SummaryInfo as u32));
assert_eq!(ac21_section_info::hash_code(names::PREVIEW), Some(DwgSectionHash::Preview as u32));
assert_eq!(ac21_section_info::hash_code(names::APP_INFO), Some(DwgSectionHash::AppInfo as u32));
assert_eq!(ac21_section_info::hash_code(names::FILE_DEP_LIST), Some(DwgSectionHash::FileDepList as u32));
assert_eq!(ac21_section_info::hash_code(names::SECURITY), Some(DwgSectionHash::Security as u32));
assert_eq!(ac21_section_info::hash_code(names::VBA_PROJECT), Some(DwgSectionHash::VbaProject as u32));
}
#[test]
fn test_hash_code_unknown_returns_none() {
assert_eq!(ac21_section_info::hash_code("AcDb:Unknown"), None);
assert_eq!(ac21_section_info::hash_code(""), None);
assert_eq!(ac21_section_info::hash_code("NotASection"), None);
}
#[test]
fn test_hash_code_exact_values() {
assert_eq!(ac21_section_info::hash_code(names::HEADER), Some(0x32B803D9));
assert_eq!(ac21_section_info::hash_code(names::CLASSES), Some(0x3F54045F));
assert_eq!(ac21_section_info::hash_code(names::HANDLES), Some(0x3F6E0450));
assert_eq!(ac21_section_info::hash_code(names::ACDB_OBJECTS), Some(0x674C05A9));
assert_eq!(ac21_section_info::hash_code(names::OBJ_FREE_SPACE), Some(0x77E2061F));
assert_eq!(ac21_section_info::hash_code(names::TEMPLATE), Some(0x4A1404CE));
assert_eq!(ac21_section_info::hash_code(names::AUX_HEADER), Some(0x54F0050A));
assert_eq!(ac21_section_info::hash_code(names::REV_HISTORY), Some(0x60A205B3));
assert_eq!(ac21_section_info::hash_code(names::SUMMARY_INFO), Some(0x717A060F));
assert_eq!(ac21_section_info::hash_code(names::PREVIEW), Some(0x40AA0473));
assert_eq!(ac21_section_info::hash_code(names::APP_INFO), Some(0x3FA0043E));
assert_eq!(ac21_section_info::hash_code(names::FILE_DEP_LIST), Some(0x6C4205CA));
assert_eq!(ac21_section_info::hash_code(names::SECURITY), Some(0x4A0204EA));
assert_eq!(ac21_section_info::hash_code(names::VBA_PROJECT), Some(0x586E0544));
}
#[test]
fn test_page_size_values() {
assert_eq!(ac21_section_info::page_size(names::HEADER), Some(0x800));
assert_eq!(ac21_section_info::page_size(names::CLASSES), Some(0xF800));
assert_eq!(ac21_section_info::page_size(names::HANDLES), Some(0xF800));
assert_eq!(ac21_section_info::page_size(names::ACDB_OBJECTS), Some(0xF800));
assert_eq!(ac21_section_info::page_size(names::OBJ_FREE_SPACE), Some(0xF800));
assert_eq!(ac21_section_info::page_size(names::TEMPLATE), Some(0x400));
assert_eq!(ac21_section_info::page_size(names::AUX_HEADER), Some(0x800));
assert_eq!(ac21_section_info::page_size(names::REV_HISTORY), Some(0x1000));
assert_eq!(ac21_section_info::page_size(names::SUMMARY_INFO), Some(0x80));
assert_eq!(ac21_section_info::page_size(names::PREVIEW), Some(0x400));
assert_eq!(ac21_section_info::page_size(names::APP_INFO), Some(0x300));
assert_eq!(ac21_section_info::page_size(names::FILE_DEP_LIST), Some(0x100));
assert_eq!(ac21_section_info::page_size(names::SECURITY), Some(0xF800));
assert_eq!(ac21_section_info::page_size(names::VBA_PROJECT), None);
}
#[test]
fn test_page_size_unknown_returns_none() {
assert_eq!(ac21_section_info::page_size("AcDb:Unknown"), None);
}
#[test]
fn test_encoding_compressed_sections() {
for name in &[
names::HEADER,
names::CLASSES,
names::HANDLES,
names::ACDB_OBJECTS,
names::OBJ_FREE_SPACE,
names::TEMPLATE,
names::AUX_HEADER,
names::REV_HISTORY,
] {
assert_eq!(
ac21_section_info::encoding(name), Some(4),
"{name} should be compressed (encoding=4)"
);
}
}
#[test]
fn test_encoding_uncompressed_sections() {
for name in &[
names::SUMMARY_INFO,
names::PREVIEW,
names::APP_INFO,
names::FILE_DEP_LIST,
names::SECURITY,
names::VBA_PROJECT,
] {
assert_eq!(
ac21_section_info::encoding(name), Some(1),
"{name} should be uncompressed (encoding=1)"
);
}
}
#[test]
fn test_encoding_unknown_returns_none() {
assert_eq!(ac21_section_info::encoding("AcDb:Unknown"), None);
}
#[test]
fn test_encryption_values() {
for name in &[
names::HEADER,
names::CLASSES,
names::HANDLES,
names::ACDB_OBJECTS,
names::OBJ_FREE_SPACE,
names::TEMPLATE,
names::AUX_HEADER,
names::REV_HISTORY,
names::SUMMARY_INFO,
names::PREVIEW,
names::APP_INFO,
names::SECURITY,
] {
assert_eq!(
ac21_section_info::encryption(name), Some(0),
"{name} should have encryption=0"
);
}
assert_eq!(ac21_section_info::encryption(names::FILE_DEP_LIST), Some(2));
assert_eq!(ac21_section_info::encryption(names::VBA_PROJECT), Some(2));
}
#[test]
fn test_encryption_unknown_returns_none() {
assert_eq!(ac21_section_info::encryption("AcDb:Unknown"), None);
}
#[test]
fn test_all_section_names_completeness() {
assert_eq!(ac21_section_info::ALL_SECTION_NAMES.len(), 14);
for name in ac21_section_info::ALL_SECTION_NAMES {
assert!(
ac21_section_info::hash_code(name).is_some(),
"Missing hash_code for {name}"
);
assert!(
ac21_section_info::encoding(name).is_some(),
"Missing encoding for {name}"
);
assert!(
ac21_section_info::encryption(name).is_some(),
"Missing encryption for {name}"
);
}
}
#[test]
fn test_all_section_names_have_unique_hashcodes() {
let hashcodes: Vec<u32> = ac21_section_info::ALL_SECTION_NAMES
.iter()
.filter_map(|name| ac21_section_info::hash_code(name))
.collect();
let mut sorted = hashcodes.clone();
sorted.sort();
sorted.dedup();
assert_eq!(
hashcodes.len(),
sorted.len(),
"Duplicate hashcodes detected"
);
}
#[test]
fn test_page_sizes_are_power_of_two_or_known() {
for name in ac21_section_info::ALL_SECTION_NAMES {
if let Some(size) = ac21_section_info::page_size(name) {
assert!(size > 0, "{name} has zero page size");
assert!(size <= 0x10000, "{name} has unexpectedly large page size: {size:#X}");
}
}
}
}