一般而言我们都会将一些资源文件(图片、声音啥的)放在程序同级目录下,以此组成我们的应用程序。但是有些时候,我们想将资源文件嵌入到可执行文件中从而实现单文件,为此我结合了一些国外论坛上的方法实验了几下。

Windows下的实现

Windows下实现其实还是很简单的,微软爸爸已经准备好了API,FindResourceLoadResource等实现相当简单,在VS中几乎是“一键式”的,在此不做过多讨论。

Linux下的实现

Linux下就比较麻烦了,没有VS,也没有RC资源之类的概念,因此不太好搞。国外有网友通过研究ELF文件结构然后用汇编加进去,这种方法虽然可以但是比较麻烦(https://stackoverflow.com/a/36295692)。

OSX下的实现

没有玩过水果系统,有知道的可以评论留言。

跨平台实现

暴力编译

这种方法比较暴力,具体来说就是使用xxd等工具把文件转成一个头文件,里面其实就是一个静态字符数组,然后内容就是文件的二进制数据。使用的时候直接调用这个数组就行了,相当于加载到内存……
然而这种方法缺点很明显,稍大一点的文件生成的.h头文件大小非常恐怖,一个小几十M的zip就能搞出几百M的内容(因为头文件里是用文本方式存的,1个字节的内容要变成至少5个字节比如0xFF,,实际还要更多),这样会极大增加编译时间,而且经过测试30M大小的文件生成的数据就会让编译器挂掉了(Heap大小不够)。

附加数据

这种方法个人认为是最好的,利用在可执行文件末尾附加数据的方法把文件嵌进去,而且还不会影响原文件的执行,DuiMini使用的就是这种方法。
例如如下的文件结构:

[可执行文件段]
[固定识别编码"UI"] 0x55, 0x49
[zip文件段大小] 4/8字节
[zip文件段]
[可执行文件段大小] 4/8字节

4/8字节取决于平台long的类型长度!

写个简单的Packer程序即可,首先读取原文件大小和zip文件大小,定位文件指针到可执行程序尾,写入两个识别编码(类似zip的PK),然后写入4/8字节zip大小,zip文件内容,最后加上原文件大小即可。
把原文件大小放最后是为了定位指针方便,因为这样读取时定位到离文件尾-4/-8的位置即可读取出识别编码的位置然后顺序读取即可。
使用就很简单了,原文件里写一个读取自身数据的代码即可把内容装进内存。

Packer程序可以参考(https://github.com/MXWXZ/DuiMini/blob/master/Tools/ResPacker/ResPacker.cpp)