接下来我们看调用 load_bits_by_len
的地方,预计内容比较多,所以会做一些拆分。
本系列已完结,以下是各章节说明,17 之前是 dos 版相关,之后是 2001 版:
右键点击 load_bits_by_len
,我们就可以找到查找引用的功能:
虽然引用的地方很多,但是实际上他们都在一个函数 FUN_1018_3da2
中。
这个函数很长:
void __stdcall16far
FUN_1018_3da2(undefined2 param_1,undefined2 param_2,undefined4 param_3,undefined4 param_4)
{
undefined uVar1;
char cVar2;
int iVar3;
int local_12;
int local_6;
int local_4;
FUN_1008_3cc0(0x4000,0,(int)&DICT_START,0x1050);
BUFFER_2BYTES_AX = 0;
BUFFER_2BYTES_DX = 0;
CURRENT_BUFFER_LEN = 0;
IMAGE_BUFFER_PTR = (undefined2)((ulong)param_4 >> 0x10);
FILE_BUFFER_IDX = (undefined2)param_4;
UK_BUFFER_PTR = (undefined2)((ulong)param_3 >> 0x10);
UK_BUFFER_IDX = (undefined2)param_3;
local_12 = -1;
init_dict();
load_bits_by_len();
local_4 = load_bits_by_len();
FUN_1018_3d00(local_4);
local_6 = load_bits_by_len();
while (local_6 != END_MARK_101) {
...
}
FUN_1008_3d5f(0x4000,0,(int)&DICT_START,0x1050);
(*DAT_1050_25e6)(0x1008,100);
return;
}
所以我估计会拆成几个部分来讲,这次我们只讲到循环开始前。 注意我标记了一些全局变量,有些是之前标记的,有些是这次标记的, 我们还是按顺序去讲函数,顺带分析变量。
FUN_1008_3cc0
→ alloc_ram
第一个函数,传了一个 4000h
,一个 0
,一个字典的地址,
我们很容易猜这是一个初始化函数,不过我们还是看一下代码:
undefined4 __stdcall16far FUN_1008_3cc0(uint param_1,int param_2,undefined2 *param_3)
{
uint uVar1;
int iVar2;
int iVar3;
undefined2 uVar4;
long lVar5;
undefined4 uVar6;
undefined2 uVar7;
undefined2 local_6;
undefined2 local_4;
uVar7 = 0;
lVar5 = GETFREESPACE();
iVar2 = (int)((ulong)lVar5 >> 0x10);
if (CONCAT22(param_2,param_1) < lVar5) {
iVar3 = (int)param_3;
uVar4 = (undefined2)((ulong)param_3 >> 0x10);
if ((param_2 < 0) || ((param_2 < 1 && (param_1 < 0xfff8)))) {
uVar1 = FUN_1048_03a6(uVar7);
if ((param_2 < iVar2) || ((param_2 <= iVar2 && (param_1 < uVar1)))) {
uVar6 = FUN_1048_033e(param_1);
*param_3 = (int)uVar6;
*(undefined2 *)(iVar3 + 2) = (int)((ulong)uVar6 >> 0x10);
}
else {
*param_3 = 0;
*(undefined2 *)(iVar3 + 2) = 0;
}
}
else {
uVar6 = FUN_1010_3d6f(param_1,param_2,2);
*param_3 = (int)uVar6;
*(undefined2 *)(iVar3 + 2) = (int)((ulong)uVar6 >> 0x10);
}
local_6 = *param_3;
local_4 = *(undefined2 *)(iVar3 + 2);
}
else {
local_6 = 0;
local_4 = 0;
}
return CONCAT22(local_4,local_6);
}
感觉这个函数讲完也就差不多了,不过我觉得这个函数并不重要,感觉就是分配内存空间,
我们快速过一下好了。首先调用 GETFREESPACE
,根据 DOS 2.0 API,
这是用来获取磁盘剩余空间。所以最外层的 if
是判断磁盘空间,如果空间不足,
直接返回空指针。
那么空间充足,则又分了两种情况,
不过 (param_2 < 0) || ((param_2 < 1 && (param_1 < 0xfff8)))
这个不太好理解,
我们只能先理解为是判断我们需要的空间有没有超过一定范围。根据传入值的日志,
我们是符合条件的,不过 else
比较简单,
我们先看这个 else
中唯一的函数 FUN_1010_3d6f
好了。
FUN_1010_3d6f
→ alloc_huge_ram
void __stdcall16far FUN_1010_3d6f(undefined2 param_1,undefined2 param_2,undefined2 param_3)
{
undefined2 uVar1;
undefined2 unaff_CS;
uVar1 = GLOBALALLOC(unaff_CS,param_1,param_2);
GLOBALLOCK(0x1060,uVar1,param_3);
return;
}
很简单,GLOBALALLOC
查了一下,
也是用来分配大量内存的,所以这个我们不需要在意,有一点摸不清头脑的是,
前面是检查磁盘空间,而这个好像是申请内存的,不过我们应该不用纠结这个问题,
分配内存不是我们要注意的东西。
else
我们搞清楚了,那其实 if
就是分配少量内存时使用,
由于函数中有调用 RTM::Ordinal_21
,这个我没查到资料,那么我们索性推测,
整个函数 FUN_1008_3cc0
,其实就是为了分配内存。
FUN_1018_3b3f
→ init_dict
void __cdecl16far init_dict(void)
{
undefined2 *puVar1;
undefined2 uVar2;
int local_4;
FUN_1008_3de2(0,0x4000,0,(int)DICT_START,(int)((ulong)DICT_START >> 0x10));
CURRENT_BIT_LEN = 8;
RESET_MARK_100 = 0x100;
END_MARK_101 = 0x101;
LAST_DICT_INDEX = 0x101;
local_4 = 0;
CURRENT_DICT_LEN = RESET_MARK_100;
while( true ) {
uVar2 = (undefined2)((ulong)DICT_START >> 0x10);
puVar1 = (undefined2 *)((int)DICT_START + local_4 * 4);
*puVar1 = 0x7fff;
*(undefined *)(puVar1 + 1) = (undefined)local_4;
if (local_4 == 0x101) break;
local_4 = local_4 + 1;
}
CURRENT_BIT_LEN = CURRENT_BIT_LEN + 1;
CURRENT_DICT_LEN = CURRENT_DICT_LEN << 1;
return;
}
看循环应该是把 0
- 101h
的字典值设为索引,前面 FUN_1008_3de2
推测是全部设为 0
。注意:0
-101h
对应的前两位值都是 7fffh
,
来代表这是一个颜色
FUN_1008_3de2
→ init_ram
void __stdcall16far FUN_1008_3de2(undefined init_val,uint dict_len,int param_3,undefined4 dict_ptr)
{
undefined2 *puVar1;
uint uVar2;
undefined2 *puVar4;
int iVar5;
uint uVar3;
iVar5 = (int)((ulong)dict_ptr >> 0x10);
puVar4 = (undefined2 *)dict_ptr;
while( true ) {
if ((0 < param_3) || (uVar3 = dict_len, CARRY2(dict_len,(uint)puVar4))) {
param_3 = param_3 - (uint)(dict_len < -(int)puVar4 - 1U);
uVar3 = -(int)puVar4 - 1U;
}
for (uVar2 = uVar3 >> 1; uVar2 != 0; uVar2 = uVar2 - 1) {
puVar1 = puVar4;
puVar4 = puVar4 + 1;
*puVar1 = CONCAT11(init_val,init_val);
}
for (uVar2 = (uint)((uVar3 & 1) != 0); uVar2 != 0; uVar2 = uVar2 - 1) {
puVar1 = puVar4;
puVar4 = (undefined2 *)((int)puVar4 + 1);
*(undefined *)puVar1 = init_val;
}
if ((param_3 < 1) && (dict_len - uVar3 == 0)) break;
puVar1 = puVar4;
puVar4 = (undefined2 *)((int)puVar4 + 1);
*(undefined *)puVar1 = init_val;
dict_len = (dict_len - uVar3) - 1;
if ((param_3 < 1) && (dict_len == 0)) {
return;
}
iVar5 = iVar5 + 8;
}
return;
}
while(true)
的第一个 if
比较难看懂,不过我觉得问题不大,
这看上去就是处理了一下越界的问题。
第一个 for
就很容易理解了,相当于 memset
。
第二个 for
,感觉是又做了一次 memset
,感觉可能程序在这里遇到过什么 bug。
后面的处理,似乎也是在处理指针和长度的特殊情况,感觉应该也不是重点。 所以整个函数应该就是用来初始化内存空间。
FUN_1018_3d00
→ convert_to_pix
void __stdcall16far FUN_1018_3d00(int param_1)
{
int dict_start;
undefined2 uVar1;
undefined2 unaff_SS;
int local_1006;
int local_1004;
undefined local_1002 [4096];
uVar1 = (undefined2)((ulong)DICT_START >> 0x10);
dict_start = (int)DICT_START;
local_1002[0] = *(undefined *)(dict_start + param_1 * 4 + 2);
local_1004 = 1;
while (*(int *)(dict_start + param_1 * 4) != 0x7fff) {
param_1 = *(int *)(dict_start + param_1 * 4);
local_1002[local_1004] = *(undefined *)(dict_start + param_1 * 4 + 2);
local_1004 = local_1004 + 1;
}
local_1006 = local_1004 + -1;
if (-1 < local_1006) {
while( true ) {
*_UK_BUFFER_IDX = local_1002[local_1006];
offset_file_buffer(1,(int)&UK_BUFFER_IDX,0x1050);
if (local_1006 == 0) break;
local_1006 = local_1006 + -1;
}
}
return;
}
这段函数做两件事情:
local_1002
这个数组中,然后如果查到的索引并不是一个颜色,也就是前两位不是 7fffh
,那么这个循环会一直继续。local_1002
的颜色倒序输出。这和我们之前的处理就有出入了,还记得我们读到未创建索引时是怎么处理的吗, 我们会把这个索引造出来,但是其实根据这段程序,他只是简单地返回了初始设定值。
基于这一点其实引申了一个事实:在查找索引的过程中,并不会创建新的索引。
3da2
的函数,循环前的内容,我们基本看了一遍,主要是做了这些事:
下一次,我们就要进入循环了。