/************************************************ * * 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 (stat(input_file_name, &st) < 0) 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; }