本文介绍作者写的一个小工具,简单的代码中包含了C语言对字符串的处理技巧,对文本文件的简单解析,二进制文件的数据复制的方法,以及格式化输出文本文件的示例。
工具的输入是如下内容的配置文件:
;资源管理器配置脚本
;以行为单位,每行不能超过255个字符
;空行和以;开头的注释行会被忽略掉
;每行都关联一个资源文件,资源序号从0开始,依次递增
.\img\img128x128.bin
.\snd\start.wav
.\img\sheis1.bin
.\snd\balloon.wav
.\img\sheis2.bin
工具的源代码贴在这里:
#include
#include
#include
#include
/* 定义相关文件名 */
#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,
};