单exe软件开发方案
一直很喜欢单文件的绿色软件,自己开发的软件也希望做成自己喜欢的样子,但软件又经常需要一些资源文件校图片待二进制文件, 那就不能做成单文件的程序了,为了解决这些问题网上也有解决方案,自己也想到自己更喜欢的解决方案。
一、自解压方案
网上许多单EXE就是用的这个方案,可以实现,方法简单,但自解压过程让我不太喜欢。这个方案的优点是可以把别人的软件二次打包成单EXE的程序。
二、资源文件中zip文件资源方案
duilib就有用的这个方法,简单看源码,实现方法也简单,只需一个个cpp文件就可以解决问题。这个方案已经很好了,但linux好象没有资源的东西,跨平台不太好吧。
三、我的方案,zip非压缩c数组
方案灵感来源于duilib的方法,不用资源文件,而是用bin2c变成c语言文件,这个可以跨平台;非大压缩,没有解压过程,速度应该更快,源代码也更简单,不需要解压代码,自己实现了一下很简单,源文件才200行,实现有效代码更少。
四、简单源码
struct LOCAL_FILE_HEADER {
uint32_t signature; // local file header signature 4 bytes(0x04034b50)
uint16_t extract_version; // version needed to extract 2 bytes
uint16_t flag; // general purpose bit flag 2 bytes
uint16_t compression_method; // compression method 2 bytes
uint16_t last_mod_file_time;// last mod file time 2 bytes
uint16_t last_mod_file_date;// last mod file date 2 bytes
uint32_t crc_32; // crc - 32 4 bytes
uint32_t compressed_size; // compressed size 4 bytes
uint32_t uncompressed_size; // uncompressed size 4 bytes
uint16_t file_name_length; // file name length 2 bytes
uint16_t extra_field_length;// extra field length 2 bytes
char filename_extrafield[0];
};
typedef struct LOCAL_FILE_HEADER FILE_HEADER;
struct DATA_DESCRIPTOR {
uint32_t crc_32; // crc - 32 4 bytes
uint32_t compressed_size; // compressed size 4 bytes
uint32_t uncompressed_size; // uncompressed size 4 bytes
};
//每条记录头对应一个文件或目录
struct CENTRAL_DIRECTORY_HEADER {
uint32_t header_signature; //central file header signature 4 bytes(0x02014b50)
uint16_t version_made_by; //version made by 2 bytes
uint16_t version_needed_extract;//version needed to extract 2 bytes
uint16_t general_purpose_flag; //general purpose bit flag 2 bytes
uint16_t compression_method; //compression method 2 bytes
uint16_t last_mod_filetime; //last mod file time 2 bytes
uint16_t last_mod_filedate; //last mod file date 2 bytes
uint32_t crc_32; //crc - 32 4 bytes
uint32_t compressed_size; //compressed size 4 bytes
uint32_t uncompressed_size; //uncompressed size 4 bytes
uint16_t filename_length; //file name length 2 bytes
uint16_t extra_field_length; //extra field length 2 bytes
uint16_t file_comment_length; //file comment length 2 bytes
uint16_t disk_number_start; //disk number start 2 bytes
uint16_t internal_file_attributes;//internal file attributes 2 bytes
uint32_t external_file_attributes;//external file attributes 4 bytes
uint32_t offset_local_header; //relative offset of local header 4 bytes
char filename[0]; //file name(variable size)
//extra field(variable size)
//file comment(variable size)
};
typedef struct CENTRAL_DIRECTORY_HEADER DIR_ENTER;
struct DIGITAL_SIGNATURE {
uint32_t header_signature; //header signature 4 bytes(0x05054b50)
uint16_t size_data; //size of data 2 bytes
uint8_t signature_data[0]; //signature data(variable size)
};
struct END_CENTRAL_DIRECTORY_RECORD {
uint32_t signature; // end of central dir signature 4 bytes(0x06054b50)
uint16_t number_disk; // number of this disk 2 bytes
// number of the disk with the
uint16_t unused1; // start of the central directory 2 bytes
// total number of entries in the
uint16_t unused2; // central directory on this disk 2 bytes
// total number of entries in
uint16_t number_central_directory; // the central directory 2 bytes zip压缩包中的文件总数
uint32_t size_central_directory; // size of the central directory 4 bytes
// offset of start of central
// directory with respect to
uint32_t offset_central; // the starting disk number 4 bytes
uint16_t comment_lengt; // .ZIP file comment length 2 bytes
// .ZIP file comment(variable size)
};
#pragma pack(pop)
typedef struct MZIP_HANDLE {
uint8_t* buf;
int buf_len;
uint8_t b_need_free; //如果是打开文件自己申请的内存,需要free
struct END_CENTRAL_DIRECTORY_RECORD* end_dir;
struct CENTRAL_DIRECTORY_HEADER* central_dir;
} MZIP_T;
static int find_central_directory(MZIP_T * pzip)
{
int find = 0;
for (int i = 20; i < 2048 && i< pzip->buf_len; i++)
{
uint32_t* signature = (uint32_t*)(pzip->buf + pzip->buf_len - i);
if (*signature == END_CENTRAL_DIRECTORY_SIGNATURE) {
pzip->end_dir = (struct END_CENTRAL_DIRECTORY_RECORD*)signature;
find = 1;
break;
}
}
if (find == 0)
return -1;
int offset = pzip->end_dir->offset_central;
pzip->central_dir = (struct CENTRAL_DIRECTORY_HEADER*)(pzip->buf + offset);
if (pzip->central_dir->header_signature != CENTRAL_DIRECTORY_HEADER_SIGNATURE) {
return -1;
}
return 0;
}
static inline DIR_ENTER *next_dirrectory_header(DIR_ENTER * dir_entr)
{
int offset = dir_entr->filename_length + dir_entr->file_comment_length
+ dir_entr->extra_field_length + sizeof(DIR_ENTER);
DIR_ENTER* next_enter = (DIR_ENTER*)(((uint8_t*)dir_entr) + offset);
if (next_enter->header_signature == CENTRAL_DIRECTORY_HEADER_SIGNATURE)
return next_enter;
else
return NULL;
}
static inline int is_need_enter(DIR_ENTER* dir_enter, const char* filename)
{
int name_len = dir_enter->filename_length;
if (filename[name_len] != 0)//文件名长度都不一致,退出
return 0;
for (int i = 0; i < name_len; i++) {
if (dir_enter->filename[i] != filename[i])
return 0;
}
return 1;
}
void* mzip_getfile(MZIP_T* pzip, const char* filename, int* file_size)
{
struct CENTRAL_DIRECTORY_HEADER* dir_enter = pzip->central_dir;
while (dir_enter != NULL)
{
int need = is_need_enter(dir_enter, filename);
if (need) {
FILE_HEADER* file_header = (FILE_HEADER*)(pzip->buf + dir_enter->offset_local_header);
if (file_header->signature != LOCAL_FILE_HEADER_SIGNATURE)
return NULL;
void* data = (uint8_t *)file_header + sizeof(FILE_HEADER) + file_header->file_name_length
+ file_header->extra_field_length;
*file_size = file_header->uncompressed_size;
return data;
}
dir_enter = next_dirrectory_header(dir_enter);
}
return NULL;
}
MZIP_T* muzip_mopen(uint8_t* buf, int buf_len)
{
int ret = 0;
MZIP_T * pzip = malloc(sizeof(MZIP_T));
pzip->buf = buf;
pzip->buf_len = buf_len;
ret = find_central_directory(pzip);
//ret = find_central_directory_header(pzip);
return pzip;
}
MZIP_T* muzip_fopen(const char * filename)
{
FILE* pf = fopen(filename, "rb");
if (pf == NULL)
return NULL;
fseek(pf, 0, SEEK_END);
int fsize = ftell(pf);
fseek(pf, 0, SEEK_SET);
uint8_t* buf = malloc(fsize);
if (buf == NULL) {
fclose(pf);
return NULL;
}
fread(buf, 1, fsize, pf);
fclose(pf);
MZIP_T * pzip = muzip_mopen(buf, fsize);
pzip->b_need_free = 1;
return pzip;
}
int muzip_free(MZIP_T* pzip)
{
if (pzip != NULL) {
if (pzip->buf != NULL)
free(pzip->buf);
}
return 0;
}