DWG 2004格式解析系列(五) 位流解码基础

本文介绍了如何通过CBitChain类解析DWG文件的位编码模式,包括字符串、句柄和不同版本的处理策略。重点讲解了AcDb:Classes section的实例分析,以及如何计算strchain和句柄流的位置以便正确解码数据。
摘要由CSDN通过智能技术生成

经前面的结构性解码,dwg中的section已经呈现为位编码模式了。从位流的视角看,我们需要按照dwg中各种对象的组织形式,连续从位流中解读位码。但是,我们知道,电脑处理的最小的单位是字节,我们也没有对连续位流的读取能力,所以终究还是要从字节上处理。

1,位流解析类 CBitChain

不妨把我们解析位流的类叫CBitChain,它的功能就是解码位流。

class CBitChain
{
	...
private:
	uint8_t* m_chain; // 位流数据
	uint32_t m_size;  // 数据大小(bytes)
	uint32_t m_byte;  // 读取指针所在字节
	uint8_t  m_bit;   // 读取指针所在字节中的位
}

这里要注意的是,m_bit的值与字节本身的位的值不同,顺序刚好相反,这样定义符合我们从左到右的阅读习惯。

bit of byte in computer:  76543210  76543210  76543210
bit of byte in CBitChain: 01234567  01234567  01234567

此类能够从当前位置处,读取不同编码格式的数据,并更新读取指针的值(即m_byte和m_bit的值)。这里贴一个读取RawChar的代码,其他代码就不贴了,按照编码规则使用位运算,不难实现。

// Read 1 byte (raw char)
BITCODE_RC CBitChain::bit_read_RC()
{
	uint8_t result;
	uint8_t byte = m_chain[m_byte];
	if (m_bit == 0)
		result = byte;
	else
	{
		result = byte << m_bit;
		if (m_byte < m_size - 1)
		{
			byte = m_chain[m_byte + 1];
			result |= byte >> (8 - m_bit);
		}
	}

	bit_advance_position(8);
	return ((uint8_t)result);
}

2,字符串

2007之前的版本,字符串都是Ansi编码且夹杂在数据中间,读取时按照对象规格,遇到字符串就从当前位流处读取。从2007开始,字符串为UCS-2编码,且一个对象的所有字符串都统一集中到对象的尾部存放。这样,如果仍然使用一个CBitChian来读,势必会引起不便。因此,在解码对象时,无论什么版本,都提供2个位流解码器。假设一个叫chain,一个叫strchain,它们的初始值相同,对于2007+的dwg,对象编码数据中会提供参数计算strchain的值。

以AcDb:Classes section 为例,其数据布局为:

SN : 8D A1 C4 B8 C4 A9 F8 C5 C0 DC F4 5F E7 CF B6 8A
RL : bytesize, total bytes of data (data的范围为[bitsize,CRC)), RLL for 2010+
RL : bitsize, total bit size of data(bytesize中有效的bit数)
BL : Maxiumum class number
B  : bool value
X  : Class Data (format described below)
X  : String stream data
B  : bool value (true if string stream data is present)
RS : CRC
SN : 72 5E 3B 47 3B 56 07 3A 3F 23 0B A0 18 30 49 75

看实例:
AcDb:Classes的section map

name: AcDb:Classes
name length: 1a
data size: 529
max size: f800
encrption: 0
hashcode: 3f54045f
encoding: 4
page count: 1
	page id: 13
	offset: 0
	size: ed00
	uncompressed size: 529
	compressed size: 36c
	check sum: be37c4fb
	crc: 27eb3fe9256530aa

AcDb:Classes section的数据

00000000   8D A1 C4 B8 C4 A9 F8 C5   C0 DC F4 5F E7 CF B6 8A 
00000010   FB 04 00 00 D8 27 00 00   3F C0 40 00 27 A0 0C 3C 
00000020   C0 50 14 59 2A A3 D4 06   1E 60 28 0A 2C 95 51 EC 
00000030   03 0F 30 14 0D 16 4A A8   F7 01 3F C3 C7 98 0A 02 
...
00000090   01 07 98 0A 04 8D A0 34   88 A7 80 31 00 35 00 32 
000000A0   80 31 80 3A 00 22 00 21   00 2C 00 10 00 21 80 36 
000000B0   00 30 80 39 80 39 80 32   80 39 80 23 28 20 0C 60 
000000C0   08 80 0C 40 08 80 0D 20   0C 60 0E 80 0D 20 0D E0 
000000D0   0D C0 0C 20 0E 40 0F 20   0A E0 0D 20 0E 80 0D 00 
...
000004E0   60 03 60 02 98 03 A0 03   C8 03 60 03 28 02 68 03 
000004F0   08 03 80 02 18 86 00 8A   00 98 00 98 00 A6 00 A8 
00000500   00 B2 00 98 00 8A 00 9A   00 82 00 A0 01 50 47 1B 
00000510   02 72 5E 3B 47 3B 56 07   3A 3F 23 0B A0 18 30 49 
00000520   75 00 00 00 00 00 00 00   00 

0x10:size=0x4FB
0x14:bitsize=0x27D8
0x14+0x4FB = 0x50F,以byte为边界,class的data到0x50E结束
0x14+0x27D8/8 = 0x50F,以bit为边界,class的data也到0x50E结束
本例中两个边界一致,只是巧合。一般来说,bit的边界小于等于byte的边界。

strchain起始位置的计算,要从bit边界的尾部向前倒推。
将strchain的指针移到尾部(0x50E, 7),并记str_chain的当前位置为p。

unsigned long p = strchain.position();
char has_string = strchain.read_B();
if (has_string == 0) return;
p -= 16;
strchain.bit_set_position(p);
long str_bit_size = strchain.bit_read_RS();
if (str_bit_size && 0x8000)
{
	str_bit_size &= 0x7FFF;
	p -= 16;
	strchain.bit_set_position(p);
	long high = strchain.bit_read_RS();
	str_bit_size |= (high << 15);
}
strchain.bit_set_position(p - str_bit_size);

从代码可知,字符串区的大小是分两步得到的,先看short能否记录其大小,若不能,则再补充一个short组合成long型表示大小,尽可能的压榨存储空间。代码中高位的short值向左移15位,是因为,低位的short其最高位是标志位,要用高位的short的0位替换。

截取实例中0x50A到0x50E的内容来分析:
在这里插入图片描述
最后,计算strchain的起始位置:
字符串区大小 = 0x23A8 bits
bit position = 0x50E*8 + 7 - 0x10 - 0x23A8 = 0x4BF
byte = 0x4BF/8 = 0x97
bit = 0x4BF - 0x97 * 8 = 7

让我们定位到0x97字节,取一些数据加以验证。按照unicode字符串的编码格式解读如下:
在这里插入图片描述
对应的是第一个class的app name:ObjectDBX Classes

number: 500(1F4)
proxyflag: 0
app name: ObjectDBX Classes
cpp name: AcDbDictionaryWithDefault
dxf name: ACDBDICTIONARYWDFLT
class id: 0x1F3
num instance: 1
dwg ver: 22
maint ver: 42

3,句柄

对于Object/Entity除了跟字符串类似,也要计算其字符串区的起始位置(2007+)外,对象引用的句柄也是集中在句柄区,同样要计算其起始位置。不过,句柄区一般都在对象其他属性读完之后,再读取的,因此,不必另外使用一个句柄位流对象,借用chian,再定位到句柄区即可。
句柄区位置的计算比较简单,不详述了。

void CDwgObjectDecoderBase::calc_handle_stream_pos()
{
	SINCE(R2010)
		m_handlestream_pos = m_chain.size() * 8 - m_handlestream_size;
	OTHER_VERSIONS
		m_handlestream_pos = m_data_start * 8 + m_bitsize;
}

今天有点累,就写到这里了。

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值