二进制图像数据如何还原成图片?困扰我三四天不得解了~

zhhf1025 2007-05-02 11:30:25
事情是这样的

我想从一个游戏客户端的bin文件里面提取出该游戏的图片。我已知此bin文件的图像数据存放规则(有固定的数据块)。

但是我读取出指定数据块的数据后,我不懂该怎么把它转换成我们可以在屏幕上看见的图像。

比如下面的这段数据,便是我从bin这个二进制文件中读取出来的二进制数据。用for循环,循环已知图像数据大小的次数,用byte类型接收,一个字节一个字节接收出来。再inttohex转换成16进制字符。得到的数据如下

1F02A77FD03C06A7A79EA77F7FD039087FA7A77FA77EA7A7D037067F9E9E7F5E7F84A7D0340EA7A77F9E5E9E7FA77FA7A7BB7FBBD0311010A7A77F5E7F9E5E7FA7A7BB7FA7A77FBBD02F839E08A77FBDA77EA77FA7839E047F7F7EBBD02C84A70D5E7FA77FA7A7BDA77F5E9E9E5E837F029E9ED029089E7E7EA77FA77FA7837E0DBDA6A79E705EA7A77F5E9E9E7FD027101A9E9EA77E7EA77FA77FA75D7E5D7DBB7CBC5C7E7DA7BDA77FA77FD0240B7C9EBC9E7F9EA77FA77FA7837E0B5D7EBC7DBD7D7CBDA7BCBD83A7027F7FD02110207F9E7CBC9EA69E7FA77F5E9EA77E5D7EBD53BDA77E7CA6A75D7E7EA77FA77EA7D01F04A7A77BBC84A6101ABBBDA77F5E7FA7A77E7EA77FA77E7EBCA77FA76FA77FA77EA77ED01C83A704BD7EBD6E83A61013BC7FA77F5E7FA7BDA7A77FA76FA7BDBC7D9E7F83A7067EA77EBDBBA7D01910127F9EA79EA77DBABA5BBB7CA67F7F9E9E5E7F837E10139E7F7D7E7E5DA67E5EA77E7DBD7E537BBBA77FD01783BC1014BBBDBC7EA67B5BBBBBA67FBD5E5E7FBDBC7D5C7E83A710109EBD5CBDA77FA77E7EBDBD7CBCA7BBA7D014019E83BC07A6BC7D7E7BBBA683BB10209EA7BDA77EA7BD537DBCBDA77F9E9E7DA6BD7EA77EA7A76F9E9EA7BB7FBD7C7FD011839E102A5DBCBC9EBB7EBB7FBCA6BC9EA6A77E7EA77F7EBD7D537EA7BC9EBC9EA77EA76FA7A6BBBBA69EBC9E9EA7839ECF0EA7A7BCBC7F9EBCBC7EBBBDBBBB9E83A60CBD7E7EBDA77F5E9E9EA7A69E83A710129EA79EA79EBBA7BB9EA77EA7A77F9EBC9EA6CC1020A6BB5BBBBBA7A7BDA7BBBBBD7E5A7D5BBD7D5A5C7D53A6A65CBCA79EBBBBBDBB83A783BD10105D7D7BBDA6A77EA79E9E5E9EA6A7BBA7C908BCBCA6BBA6BBA7A7837E1024BB7B7BA6A77F9EA7BC5C7D7E7E7D7DBD9EBC9EBB7EA79E7F9EA77EBDBD537D7E9EA7A7BD839E06A7BB7EBD7FA6C7103A7FBBA67CA6BBA77E7EA7A77EA77FBBBBA7A77F9E7D7D5D7D7E537E7E9E5E9EA7A7BD5E7F5E7FA7BD53BDBD7FBB7F6E7DBC7E7FA7BDA7BB9EA77EC40D9EBBA7BBBC9EBB5BBCA69EA77E83A7102B7FA77EA7BBBD5D7EA77F7F9E9EBCBC7DBBA77F9E7F5E9E7F7F9E9EA79E9EA77A3BA67D7EA77FA77FA7A6BB83A7101500BBA67F7F9E9EBBA7A79C7FA6A7A63D7D7F5E7F5E839E047FA77F5E839E06BCBCBDBD9E5E839E1015BDA75E7FBBA7BBBDBB7EA6A6535D7FA77FA79E7F7F84A71011009EA77F5EA77EA7A77FBCBCA7A6BCBBA6849E102A5E9E7EA7A77FA7A6A69E9EA77E7F9E5F9E9EBD9E7FBBA77EBDA6A75BBB7F7EA77EBB7EA77F5E7FA77FA7C4101AA77F7FA7A77FBC9EBCA6BB5A59A66D7FA7BD9E9E7E7EA7A7BDBB83BC101D9EA7A75E9EA7A7BD7FBBBDBBA75B7FBC9C9EBC7E5B5BBBBD7F5E7F9EA7C705A7A77F9E7F839E0FBBBA59B95CA65CA6A7A76FBD5AA6A683BC10179CBC9EA77FA77F7E9E7FA7BDA7BB7FA6BCBB9E9EBC9E9E83A7047F5E9E9EC9029E5E839E1016BCA65BA6B9B9A6A65C7E9EA77DBCA65BBB7BA6BCA6A6839E0C7EA7A77F5E7FA7A7BB5BBCA684BC849E047F5E7F7FCC059E9E5E9EBC83BB0F7B7DBC3DBDBD7E7D7D53BDBA7BBB5D849E0D5E7F7E7F5E9E7FA7A7BBBBA6A684BC069E9E5F9E5E9ECF1014A7A6BCBBBB5B5BA75E7EA65D7EA77EBDA7A7BCBB839E05A6BC5E709E83A7049E5E9E9E83BB0A5BA69CBC9E5E9E709E7FD011102EA69E5BBB7C7EA5BD7DBD7E7EA77EA77FBD7DBB9EA6A7BBBCBC9EBC9E7F9E9E709EBCBCA6BBBBBABBA69E9E5F9E9ED0141024A69EBCA6BC7EA7A79E7F9E5E7F9EBDB96E6EA7BDBBA69CBCBC9E5E9E9E5EBC9E7C7CA6BB83BC03A65E9ED01705BC9E7FBA9E83A710205EBD7FA77DBA3B7CB97DA7A6A6BCBC5DBCBC9E5F9E9E7FBBBB5BA6A6BCBC9E9ED01910117FA7BDA77F5E7F9EA7A7BDA5A77B7D7C5D839E08A6A6BCBC9E9EA7BD83A704A6A69EA6839ED01C0F7DBDBD9E9E5E9EA79EBD7FA77F7EA6839E83BD0D5B7BA7A79EA77F9E7F9E9EA79ED01F101B7EBD9E9E70A7A6BBA77CA77F9EA79E5F9E5E9E7F9EBB7E7F9E9E5F859ED021101E7F9E5EA77FBBBBA77FA79E5EBD9E9E709E9EBB9EA7BD5E705F9E5E705E9ED024101A7EBBBBA77F9E9E5E7F7FA77E5E9E5EBCA79EA77EA75E9E709E9ED02783BB1015A69E9E7F5EA7A77FBDA7BC7E5ABDA6A7BD5E9E5F9ED02910167BBBBB9E705E7FA77FA77FA7BDBD5BBDBCBCBB7EA7BBD02C0EBC9E9E5E7F7FA77FA7BD7E7EBBB983A601BBD02F10109E9E709E5E7FA7A77E7E7F7CBBA67C7BD0310B7F5E9E709E9EA7A79E7F7D83A6D0340ABC5E5F9E7F9E9EBC9CBCD037839E055E5EBC9EBCD03906BC5E709EA79ED03C029E7FD01F52440110400000002F000000A0060000D0


以上二进制数据应该是一副完整的图像了。

这个游戏客户端图像数据压缩用的是自定义的RLE算法。算法的具体已经有人发文章了,我转过来,如下:

++++++++++++++++++++++++++++++++++
这是JSS自定的一种Run-Length算法,用于StoneAge和CrossGate,下面是说明:首字节(00) 01 02 03 说明
0n String     长度为n的字符串
1n m String   长度为n*0x100+m的字符串
2x y z String 长度为x*0x10000+y*0x100+z的字符串
8n X     填充n个X
9n X m   填充n*0x100+m个X
Ax X y z 填充x*0x10000+y*0x100+z个X
Cn       填充n个背景色
Dn m     填充n*0x100+m个背景色
Ex y z   填充x*0x10000+y*0x100+z个背景色
比如,C9表示填充9个背景色,D1 10表示填充0x110个背景色,12 50表示后面跟着一个长度为0x250的字符串,91 02 30则表示将0x02重复0x130遍。
RLE压缩方式,具体的压缩编码如下:
0a xx xx xx
a 个单独的颜色点
1a bb xx xx xx
abb 个单独的颜色点
2a bb cc xx xx xx
abbcc个单独的点

Ca
a 个透明色
Da bb
abb 个透明色
Ea bb cc
abbcc个透明色
8a xx
a 个颜色是XX的点
9a xx bb
abb 个颜色为xx的点
Aa xx bb cc
abbcc 个颜色为xx的点

+++++++++++++++++++++++++++++++++

这个是RLE算法的解释,可是我看不太懂……。

我不知道这个算法是不是说要我自己写一个解压算法,将我上面提取出来的二进制数据解码?郁闷……,谁能告诉我怎么解码?要不就告诉我怎么理解上面那个RLE算法,它那个说明0N,1N的我无法理解。。。

另外,图像要完整显示,还跟调色板有关系。游戏客户端有调色板文件。

但是我同样因为没有做过这方面的程序,不理解调色板是做什么用的?我只知道,如果没有应用调色板,图像的色彩会失真,有时候会错乱,但是图像的轮廓还是存在的。

希望高手可以告诉我一下,首先就是如何将我上面提取的二进制数据还原成一个图像。
然后就是调色板的应用是怎么一回事~~!

谢谢谢谢~,分不多,只有一百,……多多包涵~,在下实在是被它困扰多日,寝食难安……
...全文
2236 37 打赏 收藏 转发到动态 举报
写回复
用AI写文章
37 条回复
切换为时间正序
请发表友善的回复…
发表回复
zhhf1025 2007-05-30
  • 打赏
  • 举报
回复
procedure uncompress_rle8(instream:PChar;in_len:Integer;outstream:PChar;out_len:Integer);
var
rle_code:Word;
ptr_instream:Word;
ptr_outstream:Word;
i,loopcnt:Word;
c:Char;
begin
if( instream = nil) or (in_len = 0) or (outstream = nil) or (out_len = 0) or (in_len > out_len ) then exit ;
ptr_instream:=0;
ptr_outstream:=0;
i:=0;
loopcnt:=0;
while ptr_instream < in_len do
begin
//c:=instream[ptr_instream];
rle_code := Word(instream[ptr_instream]);
//rle_code:=Integer(c);
Inc(ptr_instream);
case rle_code and $f0 of
$00:
begin
loopcnt := rle_code;
for i := 0 to loopcnt - 1 do
begin
outstream[ptr_outstream] := instream[ptr_instream];
Inc(ptr_instream);
Inc(ptr_outstream);
end;
end;
$10:
begin
loopcnt:= (rle_code and $0f)*$100+Word(instream[ptr_instream]);
Inc(ptr_instream);
for i := 0 to loopcnt - 1 do
begin
outstream[ptr_outstream] := instream[ptr_instream];
Inc(ptr_instream);
Inc(ptr_outstream);
end;
end;
$20:
begin
loopcnt := (rle_code and $0f) * $10000 + Word(instream[ptr_instream]) * $100 + Word(instream[ptr_instream+1]);
Inc(ptr_instream,2);
for i := 0 to loopcnt - 1 do
begin
outstream[ptr_outstream] := instream[ptr_instream];
Inc(ptr_instream);
Inc(ptr_outstream);
end;
end;
$80:
begin
loopcnt := rle_code and $0f;
rle_code := Word(instream[ptr_instream]);
Inc(ptr_instream);
for i := 0 to loopcnt - 1 do
begin
outstream[ptr_outstream] := Char(rle_code);
Inc(ptr_outstream);
end;
end;
$90:
begin
loopcnt := (rle_code and $0f) * $100 + Word(instream[ptr_instream + 1]);
rle_code := Word(instream[ptr_instream]);
Inc(ptr_instream,2);
for i := 0 to loopcnt - 1 do
begin
outstream[ptr_outstream] := Char(rle_code);
Inc(ptr_outstream);
end;
end; //end $90
$a0:
begin
loopcnt := (rle_code and $0f) * $10000 + Word(instream[ptr_instream + 1]) * $100 + Word(instream[ptr_instream + 2]);
rle_code := Word(instream[ptr_instream]);
Inc(ptr_instream,3);
for i := 0 to loopcnt - 1 do
begin
outstream[ptr_outstream] := Char(rle_code);
Inc(ptr_outstream);
end;
end; //end $a0
$c0:
begin
loopcnt := rle_code and $0f;
for i := 0 to loopcnt - 1 do
begin
outstream[ptr_outstream] := #$ff;
Inc(ptr_outstream);
end;
end;
$d0:
begin
loopcnt := (rle_code and $0f) * $100 + Word(instream[ptr_instream]);
Inc(ptr_instream);
for i := 0 to loopcnt - 1 do
begin
outstream[ptr_outstream] := #$ff;
Inc(ptr_outstream);
end;
end;
$e0:
begin
loopcnt := (rle_code and $0f) * $10000 + Word(instream[ptr_instream]) * $100 + Word(instream[ptr_instream+1]);
Inc(ptr_instream,2);
for i := 0 to loopcnt - 1 do
begin
outstream[ptr_outstream] := #$ff;
Inc(ptr_outstream);
end;
end;

end; //end case
Inc(ptr_instream);
end; //end while

end;

找到这一份代码

其中有些不理解的地方希望各位高手能够帮忙解答解答。

其中这一句

rle_code := Word(instream[ptr_instream]);

word()表示什么意思?

强制转换成word类型?

nothing1 2007-05-21
  • 打赏
  • 举报
回复
顶上去一下~

困扰!
zhhf1025 2007-05-12
  • 打赏
  • 举报
回复
哈哈~~

我也有点看出来了…………果然加入D0以后就比较好理解了

我只要从这块数据头移动到数据尾,按照算法解释的一步一步解码出来,至少目前我可以看出规律了~先前被1F02这种开头卡住了,一直糊里糊涂……

多谢兄台的解释。

我试一下自己能否写出解码的算法。还是希望兄台能够继续指点,当你再看到这个帖子的时候,发一下你的解码代码。

位图的也要,哈~~
zhhf1025 2007-05-12
  • 打赏
  • 举报
回复
jiju(UNCC)兄,能否将你的代码复制出来我学习学习呢?

就是RLE解码算法的代码,以及用解码后位图创建位图的代码~~!!

渴求已久……5~~
zhhf1025 2007-05-12
  • 打赏
  • 举报
回复
jiju(UNCC)

谢谢你的提醒………………

我这几天因为课程的原因,所以除了在这边寻求帮助外,我确实不敢花太多时间去搜索资料……我曾搜索过,但是我除了知道这个是RLE算法之外,我便没办法进一步了解如何写一个解码算法。这个帖子我现在主要就是被这个压缩算法卡住了。我看不懂那个算法的解释说明……我郁闷~~

读取文件的程序我一直在写测试代码,但是我因为不知道如何解码,所以写不出个所以然……

而且你说的没错,确实前面应该有一个DO,是我读取时候少算了一位可能。呵呵

希望你能更详细点告诉我如何解码的,谢谢谢谢~~

(本来想说万分感谢……可想一想这个词突然不敢用,哈哈,还请兄台帮帮我)
jiju 2007-05-11
  • 打赏
  • 举报
回复
这么多天了,你好歹也要自己动手试试啊,
看了你后面说的高度,和宽度,还有图形形状,我找了半天哪天写的代码,知道为什么是一个斜放的四方形(菱形吧),
你给的数据有问题,前面应该还有一个D0,也就是说D01F02A77FD03C06... 这样正好解码出来是
3008个数据,解码到52440110400000002F000000A0060000D0停止. 画出来就是一个菱形,
加D0是我自己猜的,至于解码方式就按前面的提示来做.

省得你再问,在说一点,解码出数据后用CreateBitmap 来创建位图,而不是用Bitmap.LoadFromStream(S)来读取,之后做的事情,自己去查帮助.

zhhf1025 2007-05-11
  • 打赏
  • 举报
回复
…………郁闷了~~

静态数组可能是可以的,我没有试。

现在的主要问题是,我不懂怎么将这段压缩过的数据还原出来~~

RLE算法的。

只有哪位能够指点一下这个数据的解码算法,我才能继续下一步…………

神啊!!让高手都现身吧~~~
yagebu1983 2007-05-11
  • 打赏
  • 举报
回复
最好数组定义的大一些!
超过图片的大小
yagebu1983 2007-05-11
  • 打赏
  • 举报
回复
静态数组试了没有??
怎样啊??
yagebu1983 2007-05-11
  • 打赏
  • 举报
回复
动态的也可以
zhhf1025 2007-05-10
  • 打赏
  • 举报
回复
呵呵~谢谢。

有个问题问一下

你用的byte数组是固定长度的,能否用动态数组?

我先前用动态数组接收读取到的数据竟然不行……
zhhf1025 2007-05-09
  • 打赏
  • 举报
回复
jiju(UNCC) ~~~~

你是怎么解出来的!!这个图形是一个斜放的四方形~~如果要说三角形的话也可以,就是两个三角形拼成一四方形
yagebu1983 2007-05-09
  • 打赏
  • 举报
回复
这段代码也许对你有用!
只要把二进制读入入数组中就可以了,其余用流就可以,你试试吧!
我最近也在做这方面的东西,交流一下!
以下是代码:
var
S: TStream;
B: TMemoryStream;
T:TSTREAM;
I: Integer;
C:ARRAY[0..8000] OF Byte;
begin
B := TMemoryStream.Create;
B.LoadFromFile('D:\lfg\手机照片\Image00001.bmp');
B.Position := 0;
S := TMemoryStream.Create;
S.Position :=0;
for I := 0 to B.Size-1 do
begin
B.Read(C[I], 1);
S.Write(C[I],SIZEOF(C[I])) ;
end;
S.Position :=0;
image1.Picture.Bitmap.LoadFromStream(S);
B.Free;
S.Free;
end;
chtlovezj 2007-05-09
  • 打赏
  • 举报
回复
留个脚印,有空试一下
zhhf1025 2007-05-07
  • 打赏
  • 举报
回复
今天依然无解……………………

旺仔说的DFM却是什么东西??
jiju 2007-05-06
  • 打赏
  • 举报
回复
还真是自定义的RLE算法......
不知道是你的数据有问题,还是你表诉的算法有问题,或着是我理解有问题.反正我理解下来,无法解码

1F02A77FD0 3C06A7A79EA7...
按你的表诉, 由1a bb xx xx xx 表示为: abb 个单独的颜色点
则 有连续的 0xF02 个颜色为A77FD0的颜色点
但是,接下来3C06A7A79EA7... 3c 无法解码了.晕死....

况且,这个东西解码出来,没有图形,比如 高,宽等信息,你还是无法画..


至于调色板,可能只有8a 9a Aa 下用,


如果你所有信息的得到 且数据解码了, 创建一个位图,就可以画出来了(这种画图代码,网上随便抓一把都是.).
cncharles 2007-05-06
  • 打赏
  • 举报
回复
好好参考一下DFM文件.
zhhf1025 2007-05-06
  • 打赏
  • 举报
回复
另外……

jiju(UNCC)兄台,你说的『则 有连续的 0xF02 个颜色为A77FD0的颜色点』

这个我有点不好理解啊,A77FDO这个是三个字节,如果合起来表示一个颜色点的话,这个数值不是会很大?颜色的值的范围我知道,应该是256以内的,因为调色板的颜色点就是256色(以内)。而这样怎么能将后面三个字节全部归为颜色点呢?
zhhf1025 2007-05-06
  • 打赏
  • 举报
回复
呵呵,多谢各位的解答,感觉有点眉目了

TO:IDWB() ,确实是有数据块的起始位置和大小的数据,我就是根据起始位置和数据块的大小读取出来的。但我用的比较笨的办法,一个一个字节循环读取。不知道你所说的分块读取是否是指用数据流来?我只能用byte类型来接收。如果不用数据流的话,分块读取该用什么类型来接收读取到的数据呢?我想不到……

TO:jiju(UNCC)

感谢你的回答,我现在才知道原来要把这堆数据还原成图像,还跟高度宽度有关。

图像的高度和宽度数据我也都有的,原先以为没什么用(郁闷……当时不了解),便没有发上来。

关于这个自定义RLE算法的问题,我看了很多数据块,发现有一个共同点,绝大部分的数据块开头都是1F02,数据一般是从第三个字节开始不同的。而结尾数据也绝大部分是为0000D0。

这样是否可以解码?
zhhf1025 2007-05-06
  • 打赏
  • 举报
回复
会不会可能是什么标志位的啊??

总共大小就1600多字节,FO2就3842了…………

宽度跟高度我知道的

这幅图的宽64,高47~~

高手帮忙还原一下?

调色板文件我也可以提供……

就算没调色板,谁能写个代码将其还原出轮廓来
加载更多回复(17)

1,183

社区成员

发帖
与我相关
我的任务
社区描述
Delphi GAME,图形处理/多媒体
社区管理员
  • GAME,图形处理/多媒体社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧