|  | /************************************************ | 
|  | * | 
|  | * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | 
|  | */ | 
|  |  | 
|  | #include "config.h" | 
|  | #include "wine/port.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* 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"; | 
|  |  | 
|  | void usage(void) | 
|  | { | 
|  | printf(help); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | int hex2bin(char c) | 
|  | { | 
|  | if (!isxdigit(c)) return -1024; | 
|  | if (isdigit(c)) return c - '0'; | 
|  | return toupper(c) - 'A' + 10; | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  | 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 (!(fres = fopen(res_file_name, inserting ? "rb" : "wb"))) break; | 
|  | if (inserting) | 
|  | { | 
|  | if (!insert_hexdump(ftmp, fres)) break; | 
|  | while ( (c = fgetc(fin)) != EOF && c != '}') /**/; | 
|  | } | 
|  | else | 
|  | { | 
|  | if (!extract_hexdump(fres, fin)) break; | 
|  | } | 
|  | fclose(fres); | 
|  | } | 
|  |  | 
|  | 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) | 
|  | { | 
|  | unlink(tmp_file_name); | 
|  | return 0; | 
|  | } | 
|  | } | 
|  | } | 
|  | else unlink(tmp_file_name); | 
|  | } | 
|  |  | 
|  | 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; | 
|  |  | 
|  | 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; | 
|  | } |