mirror of
git://source.winehq.org/git/wine.git
synced 2024-10-31 11:43:31 +00:00
283 lines
7.5 KiB
C
283 lines
7.5 KiB
C
/************************************************
|
|
*
|
|
* Converting binary resources from/to *.rc files
|
|
*
|
|
* Copyright 1999 Juergen Schmied
|
|
* Copyright 2003 Dimitrie O. Paun
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "wine/port.h"
|
|
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <limits.h>
|
|
#ifdef HAVE_SYS_PARAM_H
|
|
# include <sys/param.h>
|
|
#endif
|
|
|
|
static const char *clean_file;
|
|
|
|
static const char* help =
|
|
"Usage: bin2res [OPTIONS] <rsrc.rc>\n"
|
|
" -a archive binaries into the <rsrc.rc> file\n"
|
|
" -x extract binaries from the <rsrc.rc> file\n"
|
|
" -i <filename> archive the named file into the <rsrc.rc> file\n"
|
|
" -o <filename> extract the named file from the <rsrc.rc> file\n"
|
|
" -f force processing of older resources\n"
|
|
" -v causes the command to be verbous during processing\n"
|
|
" -h print this help screen and exit\n"
|
|
"\n"
|
|
"This tool allows the insertion/extractions of embedded binary\n"
|
|
"resources to/from .rc files, for storage within the cvs tree.\n"
|
|
"This is accomplished by placing a magic marker in a comment\n"
|
|
"just above the resource. The marker consists of the BINRES\n"
|
|
"string followed by the file name. For example, to insert a\n"
|
|
"brand new binary resource in a .rc file, place the marker\n"
|
|
"above empty brackets:\n"
|
|
" /* BINRES idb_std_small.bmp */\n"
|
|
" IDB_STD_SMALL BITMAP idb_std_small.bmp\n"
|
|
" /* {\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"
|
|
"To process a particular file, use the -i/-o options.\n";
|
|
|
|
static void usage(void)
|
|
{
|
|
printf(help);
|
|
exit(1);
|
|
}
|
|
|
|
static void cleanup_files(void)
|
|
{
|
|
if (clean_file) unlink( clean_file );
|
|
}
|
|
|
|
static void exit_on_signal( int sig )
|
|
{
|
|
exit(1); /* this will call the atexit functions */
|
|
}
|
|
|
|
static int insert_hexdump (FILE* outfile, FILE* infile)
|
|
{
|
|
int i, c;
|
|
|
|
fprintf (outfile, "{\n '");
|
|
for (i = 0; (c = fgetc(infile)) != EOF; i++)
|
|
{
|
|
if (i && (i % 16) == 0) fprintf (outfile, "'\n '");
|
|
if (i % 16) fprintf (outfile, " ");
|
|
fprintf(outfile, "%02X", c);
|
|
}
|
|
fprintf (outfile, "'\n}");
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int hex2bin(char c)
|
|
{
|
|
if (!isxdigit(c)) return -1024;
|
|
if (isdigit(c)) return c - '0';
|
|
return toupper(c) - 'A' + 10;
|
|
}
|
|
|
|
static int extract_hexdump (FILE* outfile, FILE* infile)
|
|
{
|
|
int byte, c;
|
|
|
|
while ( (c = fgetc(infile)) != EOF && c != '}')
|
|
{
|
|
if (isspace(c) || c == '\'') continue;
|
|
byte = 16 * hex2bin(c);
|
|
c = fgetc(infile);
|
|
if (c == EOF) return 0;
|
|
byte += hex2bin(c);
|
|
if (byte < 0) return 0;
|
|
fputc(byte, outfile);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static const char* parse_marker(const char *line, time_t* last_updated)
|
|
{
|
|
static char res_file_name[PATH_MAX], *rpos, *wpos;
|
|
struct stat st;
|
|
|
|
if (!(rpos = strstr(line, "BINRES"))) return 0;
|
|
for (rpos += 6; *rpos && isspace(*rpos); rpos++) /**/;
|
|
for (wpos = res_file_name; *rpos && !isspace(*rpos); ) *wpos++ = *rpos++;
|
|
*wpos = 0;
|
|
|
|
*last_updated = (stat(res_file_name, &st) < 0) ? 0 : st.st_mtime;
|
|
|
|
return res_file_name;
|
|
}
|
|
|
|
static int process_resources(const char* input_file_name, const char* specific_file_name,
|
|
int inserting, int force_processing, int verbose)
|
|
{
|
|
char buffer[2048], tmp_file_name[PATH_MAX];
|
|
const char *res_file_name;
|
|
time_t rc_last_update, res_last_update;
|
|
FILE *fin, *fres, *ftmp = 0;
|
|
struct stat st;
|
|
int fd, c;
|
|
|
|
if (!(fin = fopen(input_file_name, "r"))) return 0;
|
|
if (fstat(fileno(fin), &st) < 0) {
|
|
fclose (fin);
|
|
return 0;
|
|
}
|
|
rc_last_update = st.st_mtime;
|
|
|
|
if (inserting)
|
|
{
|
|
strcpy(tmp_file_name, input_file_name);
|
|
strcat(tmp_file_name, "-XXXXXX.temp");
|
|
if ((fd = mkstemps(tmp_file_name, 5)) == -1)
|
|
{
|
|
strcpy(tmp_file_name, "/tmp/bin2res-XXXXXX.temp");
|
|
if ((fd = mkstemps(tmp_file_name, 5)) == -1) return 0;
|
|
}
|
|
clean_file = tmp_file_name;
|
|
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 ( (specific_file_name && strcmp(specific_file_name, res_file_name)) ||
|
|
(!force_processing && ((rc_last_update < res_last_update) == !inserting)) )
|
|
{
|
|
if (verbose) printf("skipping '%s'\n", res_file_name);
|
|
continue;
|
|
}
|
|
|
|
if (verbose) printf("processing '%s'\n", res_file_name);
|
|
while ( (c = fgetc(fin)) != EOF && c != '{')
|
|
if (inserting) fputc(c, ftmp);
|
|
if (c == EOF) break;
|
|
|
|
if (inserting)
|
|
{
|
|
if (!(fres = fopen(res_file_name, "rb"))) break;
|
|
if (!insert_hexdump(ftmp, fres)) break;
|
|
while ( (c = fgetc(fin)) != EOF && c != '}') /**/;
|
|
fclose(fres);
|
|
}
|
|
else
|
|
{
|
|
clean_file = res_file_name;
|
|
if (!(fres = fopen(res_file_name, "wb"))) break;
|
|
if (!extract_hexdump(fres, fin)) break;
|
|
fclose(fres);
|
|
clean_file = NULL;
|
|
}
|
|
}
|
|
|
|
fclose(fin);
|
|
|
|
if (inserting)
|
|
{
|
|
fclose(ftmp);
|
|
if (c == EOF)
|
|
{
|
|
if (rename(tmp_file_name, input_file_name) < 0)
|
|
{
|
|
/* try unlinking first, Windows rename is brain-damaged */
|
|
if (unlink(input_file_name) < 0 || rename(tmp_file_name, input_file_name) < 0)
|
|
return 0;
|
|
}
|
|
clean_file = NULL;
|
|
}
|
|
}
|
|
|
|
return c == EOF;
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
int convert_dir = 0, optc;
|
|
int force_overwrite = 0, verbose = 0;
|
|
const char* input_file_name = 0;
|
|
const char* specific_file_name = 0;
|
|
|
|
atexit( cleanup_files );
|
|
signal( SIGTERM, exit_on_signal );
|
|
signal( SIGINT, exit_on_signal );
|
|
#ifdef SIGHUP
|
|
signal( SIGHUP, exit_on_signal );
|
|
#endif
|
|
|
|
while((optc = getopt(argc, argv, "axi:o:fhv")) != EOF)
|
|
{
|
|
switch(optc)
|
|
{
|
|
case 'a':
|
|
case 'x':
|
|
if (convert_dir) usage();
|
|
convert_dir = optc;
|
|
break;
|
|
case 'i':
|
|
case 'o':
|
|
if (specific_file_name) usage();
|
|
specific_file_name = optarg;
|
|
optc = ((optc == 'i') ? 'a' : 'x');
|
|
if (convert_dir && convert_dir != optc) usage();
|
|
convert_dir = optc;
|
|
break;
|
|
case 'f':
|
|
force_overwrite = 1;
|
|
break;
|
|
case 'v':
|
|
verbose = 1;
|
|
break;
|
|
case 'h':
|
|
printf(help);
|
|
exit(0);
|
|
break;
|
|
default:
|
|
usage();
|
|
}
|
|
}
|
|
|
|
if (optind + 1 != argc) usage();
|
|
input_file_name = argv[optind];
|
|
|
|
if (!convert_dir) usage();
|
|
|
|
if (!process_resources(input_file_name, specific_file_name,
|
|
convert_dir == 'a', force_overwrite, verbose))
|
|
{
|
|
perror("Processing failed");
|
|
exit(1);
|
|
}
|
|
|
|
return 0;
|
|
}
|