|
Windows 程序開發會用到資源文件,開發時得準備原始文件,資源腳本,還有資源編譯器,然後開發時得把生成的 .res 文件和中間文件鏈接起來。
Linux下程序開發時,要在生成的程序中直接包含一個文件。若是用 C/C++ 的話,大多人的做法是用工具轉換成 .h 文件,作為頭文件包含。這是個不錯的方法,缺點是轉換出的 .h 文件往往比原來的文件要大很多,真的是非常的多 。而且這個 .h “資源文件” 不能在其他語言上使用。
我覺得,更加好的方法是 Windows 程序開發的方法。那麼,資源編譯器,資源腳本語法如何選擇呢?生成的資源文件又如何應用?
用不着選工具啦。標準工具 GNU binutils 中的 as(GNU assembler as) 就可以。那豈不是說,要用匯編語言來寫資源腳本?是啊,不過只是用到 as 中一些平台無關語法,很簡單的。生成的资源文件在使用上也很简单。
下面給出例子:
首先是一個資源文件:
echo HelloWorld > t.bin
這樣得到一個 t.bin 文件。不過,這樣產生的文件的末尾是 0xa 換行符,所以
dd bs=1 count=1 conv=notrunc seek=10 if=/dev/zero of=t.bin
把最后的 0xa 改成了 0x0 这样就可以直接在 C 语言中作为字符串来使用。
然後寫一個資源腳本,也就是匯編語言程序- .section .data
- .balign 0x10, 0x0
- .global res_tbin
- .global res_size_tbin
- res_tbin:
- .incbin "t.bin"
- res_size_tbin:
- .long res_size_tbin - res_tbin
复制代码
保存為 res_tbin.s
執行
as res_tbin.s -o res_tbin.o
這樣就得到了資源文件了。
解釋:
.section .data 是為了告訴鏈接器,把以下數據放入 .data 段
.balign 0x10, 0x0 是用來對齊數據的,有兩個參數,第一個參數是對齊的邊界,單位為字節。這個值越小,浪費的空間就越小,但它必須大於對應平台的最小地址單位,比如,如果這個資源文件用在 ARM 平台上,這個值必須是4的倍數,如果在 i386 上,則隨便了。如果為了追求速度,則必須是這個平台上“字”的大小的倍數。在 ARM 上,還是4.在 i386 上,是4,在 amd64 上,則是8。第二個值為填充所用的數據,既然是數據,則用0填充沒錯的。這兩個參數在一般情況下都不需要改動。
跳過一空行,後面的6行么,一個資源就需要這麼6行。
頭兩行是使得兩個標號為鏈接器 ld 可見,相當與聲明了兩個外部可見的全局變量啦,和 C 語言中的聲明沒什麼區別。
接下來, res_tbin: 則是這個變量的定義開始了。
.incbin "t.bin" 則是這個變量的值了,告訴 as 包含原始的文件。這裡,原始文件就是 t.bin
然後緊接着 res_size_tbin: 又是一個變量,這個變量為這個文件的大小
.long res_size_tbin - res_tbin res_size_tbin 的地址減去這個資源開頭的地址,也就是這個文件的大小了, as 會計算它的,不用我們擔心。開頭的 .long 告訴 as 這裡寫入一個數據。 .long 是個很有用的指示, as 會根據平台自動選擇它的大小。在 i386 上,會產生出一個 4 字節的數據,amd64 上,則是 8 字節。
所以,如果還要一個圖像資源的,比如一個 dog.bmp 文件,就在 t.s 後面再加上- .global res_dogbmp
- .global res_size_dogbmp
- res_dogbmp:
- .incbin "dog.bmp"
- res_size_dogbmp:
- .long res_size_dogbmp - res_dogbmp
复制代码
接下來是使用這個文件,首先是個 C 語言的例子:- /*author: Kandu(張道遠)*/
- #include<stdio.h>
- extern char res_tbin[];
- extern int res_size_tbin;
- int main(void)
- {
- printf("the size of res_tbin: %d\n", res_size_tbin);
- printf("the data of it: %s\n", res_tbin);
- return 0;
- }
复制代码
extern char res_tbin[];
extern int res_size_tbin;
聲明了兩個外部變量,然後就可以直接使用它了。
這個 C 語言文件編譯後運行:
[~]$ gcc res_tbin.o t.c -o t
[~]$ ./t
the size of res_tbin: 11
the data of it: HelloWorld
另一個最重要的編譯器是 FPC ,雖然只支持 Pascal 語言和不同平台的匯編語言,但它實在是很好用,例子:- {author: Kandu(張道遠)}
- program t;
- {$link res_tbin.o}
- var
- res_tbin:char;external;
- res_size_tbin:integer;external;
- begin
- writeln('the size of res_tbin: ',res_size_tbin);
- writeln('the date of it: ',pchar(@res_tbin));
- end.
复制代码
{$link res_tbin.o} 編譯指示告訴 FPC 編譯器鏈接資源文件
然後是
res_tbin:char;external;
res_size_tbin:integer;external;
和 C 語言的聲明很像吧。
編譯及運行:
[~]$ fpc t.pas
Free Pascal Compiler version 2.4.0 [2010/01/01] for i386
Copyright (c) 1993-2009 by Florian Klaempfl
Target OS: Linux for i386
Compiling t.pas
Linking t
/usr/bin/ld: warning: link.res contains output sections; did you forget -T?
9 lines compiled, 0.2 sec
[~]$ ./t
the size of res_tbin: 11
the data of it: HelloWorld
兩個屏顯結果一模一樣。
--------------------------------------------------------------------------------------------
以上只是演示了字符資源的使用,同理可包含其他的聲音影響圖片文件。
我在為一個文件系統寫作 mkfs 的時候,就把這個文件系統的 BootBlock 用這樣的方式包含入 mkfs 裡面。在一個小系統裡面,也是通過這樣的方法,把所有資源,字庫,圖片直接包含入內核裡面。
--------------------------------------------------------------------------------------------
tips:
如果是 GCC 的話寫好 res_tbin.s 和 t.c 後,直接
gcc res_tbin.s t.c -o t
即可生成可執行文件了,不需要
as res_tbin.s -o res_tbin.o
如果你知道原始文件的確切大小的話,而且這個原始文件不會改動,那麼可以省略
res_size_資源名的定義和使用;同理,你也可以增加其他的屬性,比如
res_type_資源名:
.long 類型值
來說明這個資源的類型:圖片,聲音,影像等等。
但所有增加的值必須跟在 res_size_資源名 後面,不然 res_size_資源名 的值會不準確(變大)。
在 C 語言中,聲明是
extern char res_tbin[];
extern int res_size_tbin;
而在 Pascal 語言中,聲明是
res_tbin:char;external;
res_size_tbin:integer;external;
在這裡
int res_size_tbin; /* C */
和
res_size_tbin:integer; {Pascal}
一樣,直接就是聲明了整數。
但
char res_tbin[]; /* C */
和
res_tbin:char; {Pascal}
則不同了,在 C 中,它可以被聲明成數組而正常使用, Pascal中被仍然被聲明為整數。這是 C 語言設計上的一個缺陷,C語言內部幾個不統一設計之一。要優美地寫作的話,可以把程序改成- /*author: Kandu(張道遠)*/
- #include<stdio.h>
- extern char res_tbin;
- extern int res_size_tbin;
- int main(void)
- {
- printf("the size of res_tbin: %d\n", res_size_tbin);
- printf("the data of it: %s\n", &res_tbin);
- return 0;
- }
复制代码 把 char res_tbin[] 改成 char res_bin 。然後在 printf 處傳地址。
|
|