Complete rewrite of bin2res, for a cleaner codebase.
Add online help describing how the program works. Sanitize command line options.
This commit is contained in:
parent
59b2ad62db
commit
fb9c47d522
421
tools/bin2res.c
421
tools/bin2res.c
@ -3,8 +3,7 @@
|
|||||||
* Converting binary resources from/to *.rc files
|
* Converting binary resources from/to *.rc files
|
||||||
*
|
*
|
||||||
* Copyright 1999 Juergen Schmied
|
* Copyright 1999 Juergen Schmied
|
||||||
*
|
* Copyright 2003 Dimitrie O. Paun
|
||||||
* 11/99 first release
|
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
@ -22,342 +21,206 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "wine/port.h"
|
||||||
#ifdef HAVE_SYS_PARAM_H
|
|
||||||
# include <sys/param.h>
|
|
||||||
#endif
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdarg.h>
|
#include <ctype.h>
|
||||||
|
#include <string.h>
|
||||||
#include <fcntl.h>
|
#include <sys/stat.h>
|
||||||
#ifdef HAVE_UNISTD_H
|
#ifdef HAVE_SYS_PARAM_H
|
||||||
# include <unistd.h>
|
# include <sys/param.h>
|
||||||
#endif
|
|
||||||
#ifdef HAVE_SYS_MMAN_H
|
|
||||||
# include <sys/mman.h>
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "windef.h"
|
static const char* help =
|
||||||
#include "winbase.h"
|
"Usage: bin2res [-x] | [-a] [-f] [-h] <rsrc.rc>\n"
|
||||||
#include "wingdi.h"
|
" -a archive binaries into the <rsrc.rc> file\n"
|
||||||
#include "winuser.h"
|
" -x extract binaries from the <rsrc.rc> file\n"
|
||||||
|
" -f force processing of older resources\n"
|
||||||
extern char* g_lpstrFileName;
|
" -h print this help screen and exit\n"
|
||||||
|
"\n"
|
||||||
/* global options */
|
"This tool allows the insertion/extractions of embedded binary\n"
|
||||||
|
"resources to/from .rc files, for storage within the cvs tree.\n"
|
||||||
char* g_lpstrFileName = NULL;
|
"This is accomplished by placing a magic marker in a comment\n"
|
||||||
char* g_lpstrInputFile = NULL;
|
"just above the resource. The marker consists of the BINRES\n"
|
||||||
int b_to_binary = 0;
|
"string followed by the file name. For example, to insert a\n"
|
||||||
int b_force_overwrite = 0;
|
"brand new binary resource in a .rc file, place the marker\n"
|
||||||
|
"above empty brackets:\n"
|
||||||
static char* errorOpenFile = "Unable to open file.\n";
|
" /* BINRES idb_std_small.bmp */\n"
|
||||||
static char* errorRCFormat = "Unexpexted syntax in rc file line %i\n";
|
" {}\n"
|
||||||
|
"To merge the binary resources into the .rc file, run:\n"
|
||||||
|
" bin2res -a myrsrc.rc\n"
|
||||||
|
"Only resources that are newer than the .rc are processed.\n"
|
||||||
|
"To extract the binary resources from the .rc file, run:\n"
|
||||||
|
" bin2res -x myrsrc.rc\n"
|
||||||
|
"Binary files newer than the .rc file are not overwritten.\n"
|
||||||
|
"\n"
|
||||||
|
"To force processing of all resources, use the -f flag.\n";
|
||||||
|
|
||||||
void usage(void)
|
void usage(void)
|
||||||
{
|
{
|
||||||
printf("Usage: bin2res [-d bin] [input file]\n");
|
printf(help);
|
||||||
printf(" -d bin convert a *.res back to a binary\n");
|
|
||||||
printf(" -f force overwriting newer files\n");
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void parse_options(int argc, char **argv)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
switch( argc )
|
|
||||||
{
|
|
||||||
case 2:
|
|
||||||
g_lpstrInputFile = argv[1];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 3:
|
|
||||||
case 4:
|
|
||||||
case 5:
|
|
||||||
for( i = 1; i < argc - 1; i++ )
|
|
||||||
{
|
|
||||||
if( argv[i][0] != '-' ||
|
|
||||||
strlen(argv[i]) != 2 ) break;
|
|
||||||
|
|
||||||
if( argv[i][1] == 'd')
|
|
||||||
{
|
|
||||||
if (strcmp ("bin", argv[i+1])==0)
|
|
||||||
{
|
|
||||||
b_to_binary =1;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
usage();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
else if ( argv[i][1] == 'f')
|
|
||||||
{
|
|
||||||
b_force_overwrite = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
usage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if( i == argc - 1 )
|
|
||||||
{
|
|
||||||
g_lpstrInputFile = argv[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: usage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int insert_hex (char * infile, FILE * outfile)
|
|
||||||
{
|
|
||||||
#ifdef HAVE_MMAP
|
|
||||||
unsigned int i;
|
|
||||||
int fd;
|
|
||||||
struct stat st;
|
|
||||||
LPBYTE p_in_file = NULL;
|
|
||||||
|
|
||||||
if( (fd = open( infile, O_RDONLY))==-1 )
|
|
||||||
{
|
|
||||||
fprintf(stderr, errorOpenFile );
|
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
if ((fstat(fd, &st) == -1) || (p_in_file = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == (void *)-1)
|
|
||||||
|
int insert_hexdump (FILE* outfile, FILE* infile)
|
||||||
{
|
{
|
||||||
fprintf(stderr, errorOpenFile );
|
int i, c;
|
||||||
close(fd);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf (outfile, "{\n '");
|
fprintf (outfile, "{\n '");
|
||||||
i = 0;
|
for (i = 0; (c = fgetc(infile)) != EOF; i++)
|
||||||
while (1)
|
|
||||||
{
|
{
|
||||||
fprintf(outfile, "%02X", p_in_file[i]);
|
if (i && (i % 16) == 0) fprintf (outfile, "'\n '");
|
||||||
if (++i >= st.st_size) break;
|
if (i % 16) fprintf (outfile, " ");
|
||||||
fprintf(outfile, "%s", (i == (i & 0xfffffff0)) ? "'\n '" :" ");
|
|
||||||
}
|
|
||||||
fprintf (outfile, "'\n}");
|
|
||||||
munmap(p_in_file, st.st_size);
|
|
||||||
close(fd);
|
|
||||||
return 1;
|
|
||||||
#else /* HAVE_MMAP */
|
|
||||||
FILE* fp;
|
|
||||||
struct stat st;
|
|
||||||
unsigned int i;
|
|
||||||
int c;
|
|
||||||
|
|
||||||
fp = fopen( infile, "r" );
|
|
||||||
if ( fp == NULL )
|
|
||||||
{
|
|
||||||
fprintf(stderr, errorOpenFile );
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
if (fstat(fileno(fp), &st) == -1)
|
|
||||||
{
|
|
||||||
fprintf(stderr, errorOpenFile );
|
|
||||||
fclose(fp);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf (outfile, "{\n '");
|
|
||||||
i = 0;
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
c = fgetc(fp);
|
|
||||||
if ( c == EOF )
|
|
||||||
{
|
|
||||||
fprintf(stderr, errorOpenFile );
|
|
||||||
fclose(fp);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
fprintf(outfile, "%02X", c);
|
fprintf(outfile, "%02X", c);
|
||||||
if (++i >= st.st_size) break;
|
|
||||||
fprintf(outfile, "%s", (i == (i & 0xfffffff0)) ? "'\n '" :" ");
|
|
||||||
}
|
}
|
||||||
fprintf (outfile, "'\n}");
|
fprintf (outfile, "'\n}");
|
||||||
|
|
||||||
fclose(fp);
|
|
||||||
return 1;
|
return 1;
|
||||||
#endif /* HAVE_MMAP */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int convert_to_res ()
|
int hex2bin(char c)
|
||||||
{
|
{
|
||||||
FILE *fin, *ftemp;
|
if (!isxdigit(c)) return -1024;
|
||||||
char buffer[255];
|
if (isdigit(c)) return c - '0';
|
||||||
char infile[255];
|
return toupper(c) - 'A' + 10;
|
||||||
char tmpfile[255];
|
|
||||||
char *pos;
|
|
||||||
int c, len;
|
|
||||||
struct stat st;
|
|
||||||
int line = 0;
|
|
||||||
time_t tinput;
|
|
||||||
long startpos, endpos;
|
|
||||||
|
|
||||||
strcpy( tmpfile, g_lpstrInputFile );
|
|
||||||
strcat( tmpfile, "-tmp" );
|
|
||||||
/* FIXME: should use better tmp name and create with O_EXCL */
|
|
||||||
if( (ftemp = fopen( tmpfile, "w")) == NULL ) goto error_open_file;
|
|
||||||
|
|
||||||
if( (fin = fopen( g_lpstrInputFile, "r")) == NULL || stat(g_lpstrInputFile, &st)) goto error_open_file;
|
|
||||||
tinput = st.st_ctime;
|
|
||||||
|
|
||||||
while ( NULL != fgets(buffer, 255, fin))
|
|
||||||
{
|
|
||||||
fputs(buffer, ftemp);
|
|
||||||
line++;
|
|
||||||
if ( (pos = strstr(buffer, "BINRES")) != NULL)
|
|
||||||
{
|
|
||||||
/* get the out-file name */
|
|
||||||
len = 0; pos += 6; startpos=0; endpos=0;
|
|
||||||
while ( *pos == ' ') pos++;
|
|
||||||
while ( pos[len] != ' ') len++;
|
|
||||||
strncpy(infile, pos, len);
|
|
||||||
infile[len]=0;
|
|
||||||
|
|
||||||
if ( (!stat(infile, &st) && st.st_ctime > tinput) || b_force_overwrite)
|
|
||||||
{
|
|
||||||
/* write a output file */
|
|
||||||
printf("[%s:c]", infile);
|
|
||||||
while((c = fgetc(fin))!='{' && c != EOF) fputc(c, ftemp);
|
|
||||||
if (c == EOF ) goto error_rc_format;
|
|
||||||
while((c = fgetc(fin))!='}' && c != EOF);
|
|
||||||
if (c == EOF ) goto error_rc_format;
|
|
||||||
|
|
||||||
insert_hex(infile, ftemp);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf("[%s:s]", infile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(fin);
|
int extract_hexdump (FILE* outfile, FILE* infile)
|
||||||
fclose(ftemp);
|
{
|
||||||
|
int byte, c;
|
||||||
|
|
||||||
if (unlink(g_lpstrInputFile) == -1)
|
while ( (c = fgetc(infile)) != EOF && c != '}')
|
||||||
{
|
{
|
||||||
perror("unlink");
|
if (isspace(c) || c == '\'') continue;
|
||||||
unlink(tmpfile);
|
byte = 16 * hex2bin(c);
|
||||||
return 0;
|
c = fgetc(infile);
|
||||||
}
|
if (c == EOF) return 0;
|
||||||
if (rename(tmpfile, g_lpstrInputFile) == -1)
|
byte += hex2bin(c);
|
||||||
{
|
if (byte < 0) return 0;
|
||||||
perror("rename");
|
fputc(byte, outfile);
|
||||||
unlink(tmpfile);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
error_open_file:
|
|
||||||
fprintf(stderr, errorOpenFile );
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
error_rc_format:
|
|
||||||
fprintf(stderr, errorRCFormat, line);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int convert_to_bin()
|
const char* parse_marker(const char *line, time_t* last_updated)
|
||||||
{
|
{
|
||||||
FILE *fin, *fout;
|
static char res_file_name[PATH_MAX], *rpos, *wpos;
|
||||||
char buffer[255];
|
|
||||||
char outfile[255];
|
|
||||||
char *pos;
|
|
||||||
int len, index, in_resource;
|
|
||||||
unsigned int byte;
|
|
||||||
struct stat st;
|
struct stat st;
|
||||||
int line = 0;
|
|
||||||
time_t tinput;
|
|
||||||
|
|
||||||
if( (fin = fopen( g_lpstrInputFile, "r")) == NULL || stat(g_lpstrInputFile, &st)) goto error_open_file;
|
if (!(rpos = strstr(line, "BINRES"))) return 0;
|
||||||
tinput = st.st_ctime;
|
for (rpos += 6; *rpos && isspace(*rpos); rpos++) /**/;
|
||||||
|
for (wpos = res_file_name; *rpos && !isspace(*rpos); ) *wpos++ = *rpos++;
|
||||||
|
*wpos = 0;
|
||||||
|
|
||||||
while ( NULL != fgets(buffer, 255, fin))
|
*last_updated = (stat(res_file_name, &st) < 0) ? 0 : st.st_mtime;
|
||||||
{
|
|
||||||
line++;
|
|
||||||
if ( (pos = strstr(buffer, "BINRES")) != NULL)
|
|
||||||
{
|
|
||||||
/* get the out-file name */
|
|
||||||
len = 0; pos += 6;
|
|
||||||
while ( *pos == ' ') pos++;
|
|
||||||
while ( pos[len] != ' ') len++;
|
|
||||||
strncpy(outfile, pos, len);
|
|
||||||
outfile[len]=0;
|
|
||||||
|
|
||||||
if ( stat(outfile, &st) || st.st_ctime < tinput || b_force_overwrite)
|
return res_file_name;
|
||||||
{
|
}
|
||||||
/* write a output file */
|
|
||||||
printf("[%s:c]", outfile);
|
|
||||||
if ( (fout = fopen( outfile, "w")) == NULL) goto error_open_file;
|
|
||||||
|
|
||||||
in_resource = 0;
|
int process_resources(const char* input_file_name, int inserting, int force_processing)
|
||||||
while (1)
|
|
||||||
{
|
{
|
||||||
if ( NULL == fgets(buffer, 255, fin)) goto error_rc_format;
|
char buffer[2048], tmp_file_name[PATH_MAX];
|
||||||
line++;
|
const char *res_file_name;
|
||||||
|
time_t rc_last_update, res_last_update;
|
||||||
|
FILE *fin, *fres, *ftmp = 0;
|
||||||
|
struct stat st;
|
||||||
|
int fd, c;
|
||||||
|
|
||||||
/* parse a line */
|
if (!(fin = fopen(input_file_name, "r"))) return 0;
|
||||||
for ( index = 0; buffer[index] != 0; index++ )
|
if (stat(input_file_name, &st) < 0) return 0;
|
||||||
|
rc_last_update = st.st_mtime;
|
||||||
|
|
||||||
|
if (inserting)
|
||||||
{
|
{
|
||||||
if ( ! in_resource )
|
strcpy(tmp_file_name, input_file_name);
|
||||||
|
strcat(tmp_file_name, "-XXXXXX.temp");
|
||||||
|
if ((fd = mkstemps(tmp_file_name, 5)) == -1)
|
||||||
{
|
{
|
||||||
if ( buffer[index] == '{' ) in_resource = 1;
|
strcpy(tmp_file_name, "/tmp/bin2res-XXXXXX.temp");
|
||||||
|
if ((fd = mkstemps(tmp_file_name, 5)) == -1) return 0;
|
||||||
|
}
|
||||||
|
if (!(ftmp = fdopen(fd, "w"))) return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (c = EOF; fgets(buffer, sizeof(buffer), fin); c = EOF)
|
||||||
|
{
|
||||||
|
if (inserting) fprintf(ftmp, "%s", buffer);
|
||||||
|
if (!(res_file_name = parse_marker(buffer, &res_last_update))) continue;
|
||||||
|
if (!force_processing && ((rc_last_update < res_last_update) == !inserting))
|
||||||
|
{
|
||||||
|
printf("skipping '%s'\n", res_file_name);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( buffer[index] == ' ' || buffer[index] == '\''|| buffer[index] == '\n' ) continue;
|
printf("processing '%s'\n", res_file_name);
|
||||||
if ( buffer[index] == '}' ) goto end_of_resource;
|
while ( (c = fgetc(fin)) != EOF && c != '{')
|
||||||
if ( ! isxdigit(buffer[index])) goto error_rc_format;
|
if (inserting) fputc(c, ftmp);
|
||||||
index += sscanf(&buffer[index], "%02x", &byte);
|
if (c == EOF) break;
|
||||||
fputc(byte, fout);
|
|
||||||
}
|
if (!(fres = fopen(res_file_name, inserting ? "r" : "w"))) break;
|
||||||
}
|
if (inserting)
|
||||||
fclose(fout);
|
{
|
||||||
|
if (!insert_hexdump(ftmp, fres)) break;
|
||||||
|
while ( (c = fgetc(fin)) != EOF && c != '}') /**/;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
printf("[%s:s]", outfile);
|
if (!extract_hexdump(fres, fin)) break;
|
||||||
}
|
|
||||||
end_of_resource: ;
|
|
||||||
}
|
}
|
||||||
|
fclose(fres);
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(fin);
|
fclose(fin);
|
||||||
return 1;
|
|
||||||
|
|
||||||
error_open_file:
|
if (inserting)
|
||||||
fprintf(stderr, errorOpenFile );
|
{
|
||||||
return 0;
|
fclose(ftmp);
|
||||||
|
if (c == EOF && rename(tmp_file_name, input_file_name) < 0)
|
||||||
|
c = '.'; /* force an error */
|
||||||
|
else unlink(tmp_file_name);
|
||||||
|
}
|
||||||
|
|
||||||
error_rc_format:
|
return c == EOF;
|
||||||
fprintf(stderr, errorRCFormat, line);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
parse_options( argc, argv);
|
int convert_dir = 0, optc;
|
||||||
|
int force_overwrite = 0;
|
||||||
|
const char* input_file_name;
|
||||||
|
|
||||||
if (b_to_binary == 0)
|
while((optc = getopt(argc, argv, "axfh")) != EOF)
|
||||||
{
|
{
|
||||||
convert_to_res();
|
switch(optc)
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
convert_to_bin();
|
case 'a':
|
||||||
|
case 'x':
|
||||||
|
if (convert_dir) usage();
|
||||||
|
convert_dir = optc;
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
force_overwrite = 1;
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
printf(help);
|
||||||
|
exit(0);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
usage();
|
||||||
}
|
}
|
||||||
printf("\n");
|
}
|
||||||
|
|
||||||
|
if (optind + 1 != argc) usage();
|
||||||
|
input_file_name = argv[optind];
|
||||||
|
|
||||||
|
if (!convert_dir) usage();
|
||||||
|
|
||||||
|
if (!process_resources(input_file_name, convert_dir == 'a', force_overwrite))
|
||||||
|
{
|
||||||
|
perror("Processing failed");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user