本文介绍作者写的一个小工具,简单的代码中包含了C语言对字符串的处理技巧,对文本文件的简单解析,二进制文件的数据复制的方法,以及格式化输出文本文件的示例。
工具的输入是如下内容的配置文件:
;资源管理器配置脚本 ;以行为单位,每行不能超过255个字符 ;空行和以;开头的注释行会被忽略掉 ;每行都关联一个资源文件,资源序号从0开始,依次递增 .\img\img128x128.bin .\snd\start.wav .\img\sheis1.bin .\snd\balloon.wav .\img\sheis2.bin
工具的源代码贴在这里:
#include <ctype.h> #include <string.h> #include <stdlib.h> #include <stdio.h> /* 定义相关文件名 */ #define CONFIG_FILE_NAME ("config.txt") #define RESPAK_FILE_NAME ("resmm.bin") #define ADDRS_C_FILE_NAME ("resmm_addrs.c") /* 定义配置行最大的字符数 */ #define LINE_CHARS (255) /* 定义复制文件数据时的缓冲区大小 */ #define BUF_SIZE (8 * 1024) /* 从配置行提取文件名 */ static char* extract_file_name(const char* line, char* file_name) { /* 过滤配置行左边的空格符 */ while(isspace(*line++)){}; line--; /* 忽略空行和注释行 */ if((*line == '\0') || (*line == ';')) return NULL; /* 提取文件名,并去掉右边的空格符 */ strcpy(file_name, line); { char* p = file_name + strlen(file_name) - 1; while(isspace(*p--)){}; p++; p++; *p = '\0'; } return file_name; } /* 扫描有效文件数 */ static int scan_file_count(FILE* cf) { char line[LINE_CHARS + 1]; char file_name[LINE_CHARS + 1]; int count = 0; while(!feof(cf)) { fgets(line, LINE_CHARS, cf); if(extract_file_name(line, file_name) != NULL) count++; } return count; } /* 复制文件数据 */ static size_t copy_file_datas(FILE* pf, FILE* rf) { unsigned char buf[BUF_SIZE]; size_t total = 0; size_t len; do{ len = fread(buf, sizeof(unsigned char), BUF_SIZE, rf); fwrite(buf, sizeof(unsigned char), len, pf); total += len; }while(len == BUF_SIZE); return total; } /* 主函数 */ int main(int argc, char* argv[]) { FILE* cf; FILE* pf; FILE* rf; int count; size_t* lens; size_t len; unsigned int addr; char line[LINE_CHARS + 1]; char file_name[LINE_CHARS + 1]; int i; /* 打开配置文件,并扫描有效文件数 */ if((cf = fopen(CONFIG_FILE_NAME, "rt")) == NULL) { printf("Can\'t open %s!\n", CONFIG_FILE_NAME); return -1; } count = scan_file_count(cf); fseek(cf, 0L, SEEK_SET); /* 打开资源包文件 */ if((pf = fopen(RESPAK_FILE_NAME, "wb")) == NULL) { printf("Can\'t create %s!\n", RESPAK_FILE_NAME); fclose(cf); return -1; } /* 复制打包资源文件,并统计其大小 */ if((lens = (size_t*)malloc(sizeof(size_t) * count)) == NULL) { printf("No enough memory!\n"); fclose(pf); fclose(cf); return -1; } i = 0; while(!feof(cf)) { fgets(line, LINE_CHARS, cf); if(extract_file_name(line, file_name) != NULL) { if((rf = fopen(file_name, "rb")) == NULL) { printf("Can\'t open %s!\n", file_name); fclose(pf); fclose(cf); return -1; } if((len = copy_file_datas(pf, rf)) == 0) { printf("File %s is empty!\n", file_name); fclose(pf); fclose(cf); return -1; } lens[i++] = len; fclose(rf); } } fclose(pf); fclose(cf); /* 打开地址描述的C语言源文件 */ if((cf = fopen(ADDRS_C_FILE_NAME, "wt")) == NULL) { printf("Can\'t open %s!\n", ADDRS_C_FILE_NAME); return -1; } /* 把各个资源的地址和长度信息写入C语言数组 */ fprintf(cf, "#define RES_COUNT\t(%d)\n\n", count); fprintf(cf, "static const INT32U addrs[RES_COUNT] = \n{\n"); addr = 0; for(i = 0; i < count; i++) { fprintf(cf, "\t\t0x%08x,\n", addr); addr += lens[i]; } fprintf(cf, "};\n\n"); fprintf(cf, "static const INT32U lens[RES_COUNT] = \n{\n"); for(i = 0; i < count; i++) fprintf(cf, "\t\t0x%08x,\n", lens[i]); fprintf(cf, "};"); fclose(cf); free(lens); return 0; }格式化输出的文本文件是这样的:
#define RES_COUNT (5) static const INT32U addrs[RES_COUNT] = { 0x00000000, 0x00008000, 0x0000889a, 0x0001089a, 0x0001219a, }; static const INT32U lens[RES_COUNT] = { 0x00008000, 0x0000089a, 0x00008000, 0x00001900, 0x00008000, };