单exe软件开发方案


源文最早发布于我的博客 为学网 https://vxue.net/index.php/archives/29/

一直很喜欢单文件的绿色软件,自己开发的软件也希望做成自己喜欢的样子,但软件又经常需要一些资源文件校图片待二进制文件, 那就不能做成单文件的程序了,为了解决这些问题网上也有解决方案,自己也想到自己更喜欢的解决方案。

一、自解压方案

网上许多单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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值