wine.inf: Add the SystemDrive environment variable.
[wine/multimedia.git] / tools / bin2res.c
blobd52aebcedf758a7bfb7666c124528fde24f24c71
1 /************************************************
3 * Converting binary resources from/to *.rc files
5 * Copyright 1999 Juergen Schmied
6 * Copyright 2003 Dimitrie O. Paun
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #include "config.h"
24 #include "wine/port.h"
26 #include <signal.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <ctype.h>
30 #include <string.h>
31 #include <sys/stat.h>
32 #include <limits.h>
33 #ifdef HAVE_SYS_PARAM_H
34 # include <sys/param.h>
35 #endif
37 static const char *clean_file;
39 static const char* help =
40 "Usage: bin2res [OPTIONS] <rsrc.rc>\n"
41 " -a archive binaries into the <rsrc.rc> file\n"
42 " -x extract binaries from the <rsrc.rc> file\n"
43 " -i <filename> archive the named file into the <rsrc.rc> file\n"
44 " -o <filename> extract the named file from the <rsrc.rc> file\n"
45 " -f force processing of older resources\n"
46 " -v causes the command to be verbous during processing\n"
47 " -h print this help screen and exit\n"
48 "\n"
49 "This tool allows the insertion/extractions of embedded binary\n"
50 "resources to/from .rc files, for storage within the cvs tree.\n"
51 "This is accomplished by placing a magic marker in a comment\n"
52 "just above the resource. The marker consists of the BINRES\n"
53 "string followed by the file name. For example, to insert a\n"
54 "brand new binary resource in a .rc file, place the marker\n"
55 "above empty brackets:\n"
56 " /* BINRES idb_std_small.bmp */\n"
57 " IDB_STD_SMALL BITMAP idb_std_small.bmp\n"
58 " /* {\n"
59 " } */\n"
60 "To merge the binary resources into the .rc file, run:\n"
61 " bin2res -a myrsrc.rc\n"
62 "Only resources that are newer than the .rc are processed.\n"
63 "To extract the binary resources from the .rc file, run:\n"
64 " bin2res -x myrsrc.rc\n"
65 "Binary files newer than the .rc file are not overwritten.\n"
66 "\n"
67 "To force processing of all resources, use the -f flag.\n"
68 "To process a particular file, use the -i/-o options.\n";
70 static void usage(void)
72 printf(help);
73 exit(1);
76 static void cleanup_files(void)
78 if (clean_file) unlink( clean_file );
81 static void exit_on_signal( int sig )
83 exit(1); /* this will call the atexit functions */
86 static int insert_hexdump (FILE* outfile, FILE* infile)
88 int i, c;
90 fprintf (outfile, "{\n '");
91 for (i = 0; (c = fgetc(infile)) != EOF; i++)
93 if (i && (i % 16) == 0) fprintf (outfile, "'\n '");
94 if (i % 16) fprintf (outfile, " ");
95 fprintf(outfile, "%02X", c);
97 fprintf (outfile, "'\n}");
99 return 1;
102 static int hex2bin(char c)
104 if (!isxdigit(c)) return -1024;
105 if (isdigit(c)) return c - '0';
106 return toupper(c) - 'A' + 10;
109 static int extract_hexdump (FILE* outfile, FILE* infile)
111 int byte, c;
113 while ( (c = fgetc(infile)) != EOF && c != '}')
115 if (isspace(c) || c == '\'') continue;
116 byte = 16 * hex2bin(c);
117 c = fgetc(infile);
118 if (c == EOF) return 0;
119 byte += hex2bin(c);
120 if (byte < 0) return 0;
121 fputc(byte, outfile);
123 return 1;
126 static const char* parse_marker(const char *line, time_t* last_updated)
128 static char res_file_name[PATH_MAX], *rpos, *wpos;
129 struct stat st;
131 if (!(rpos = strstr(line, "BINRES"))) return 0;
132 for (rpos += 6; *rpos && isspace(*rpos); rpos++) /**/;
133 for (wpos = res_file_name; *rpos && !isspace(*rpos); ) *wpos++ = *rpos++;
134 *wpos = 0;
136 *last_updated = (stat(res_file_name, &st) < 0) ? 0 : st.st_mtime;
138 return res_file_name;
141 static int process_resources(const char* input_file_name, const char* specific_file_name,
142 int inserting, int force_processing, int verbose)
144 char buffer[2048], tmp_file_name[PATH_MAX];
145 const char *res_file_name;
146 time_t rc_last_update, res_last_update;
147 FILE *fin, *fres, *ftmp = 0;
148 struct stat st;
149 int fd, c;
151 if (!(fin = fopen(input_file_name, "r"))) return 0;
152 if (fstat(fileno(fin), &st) < 0) {
153 fclose (fin);
154 return 0;
156 rc_last_update = st.st_mtime;
158 if (inserting)
160 strcpy(tmp_file_name, input_file_name);
161 strcat(tmp_file_name, "-XXXXXX.temp");
162 if ((fd = mkstemps(tmp_file_name, 5)) == -1)
164 strcpy(tmp_file_name, "/tmp/bin2res-XXXXXX.temp");
165 if ((fd = mkstemps(tmp_file_name, 5)) == -1) return 0;
167 clean_file = tmp_file_name;
168 if (!(ftmp = fdopen(fd, "w"))) return 0;
171 for (c = EOF; fgets(buffer, sizeof(buffer), fin); c = EOF)
173 if (inserting) fprintf(ftmp, "%s", buffer);
174 if (!(res_file_name = parse_marker(buffer, &res_last_update))) continue;
175 if ( (specific_file_name && strcmp(specific_file_name, res_file_name)) ||
176 (!force_processing && ((rc_last_update < res_last_update) == !inserting)) )
178 if (verbose) printf("skipping '%s'\n", res_file_name);
179 continue;
182 if (verbose) printf("processing '%s'\n", res_file_name);
183 while ( (c = fgetc(fin)) != EOF && c != '{')
184 if (inserting) fputc(c, ftmp);
185 if (c == EOF) break;
187 if (inserting)
189 if (!(fres = fopen(res_file_name, "rb"))) break;
190 if (!insert_hexdump(ftmp, fres)) break;
191 while ( (c = fgetc(fin)) != EOF && c != '}') /**/;
192 fclose(fres);
194 else
196 clean_file = res_file_name;
197 if (!(fres = fopen(res_file_name, "wb"))) break;
198 if (!extract_hexdump(fres, fin)) break;
199 fclose(fres);
200 clean_file = NULL;
204 fclose(fin);
206 if (inserting)
208 fclose(ftmp);
209 if (c == EOF)
211 if (rename(tmp_file_name, input_file_name) < 0)
213 /* try unlinking first, Windows rename is brain-damaged */
214 if (unlink(input_file_name) < 0 || rename(tmp_file_name, input_file_name) < 0)
215 return 0;
217 clean_file = NULL;
221 return c == EOF;
224 int main(int argc, char **argv)
226 int convert_dir = 0, optc;
227 int force_overwrite = 0, verbose = 0;
228 const char* input_file_name = 0;
229 const char* specific_file_name = 0;
231 atexit( cleanup_files );
232 signal( SIGTERM, exit_on_signal );
233 signal( SIGINT, exit_on_signal );
234 #ifdef SIGHUP
235 signal( SIGHUP, exit_on_signal );
236 #endif
238 while((optc = getopt(argc, argv, "axi:o:fhv")) != EOF)
240 switch(optc)
242 case 'a':
243 case 'x':
244 if (convert_dir) usage();
245 convert_dir = optc;
246 break;
247 case 'i':
248 case 'o':
249 if (specific_file_name) usage();
250 specific_file_name = optarg;
251 optc = ((optc == 'i') ? 'a' : 'x');
252 if (convert_dir && convert_dir != optc) usage();
253 convert_dir = optc;
254 break;
255 case 'f':
256 force_overwrite = 1;
257 break;
258 case 'v':
259 verbose = 1;
260 break;
261 case 'h':
262 printf(help);
263 exit(0);
264 break;
265 default:
266 usage();
270 if (optind + 1 != argc) usage();
271 input_file_name = argv[optind];
273 if (!convert_dir) usage();
275 if (!process_resources(input_file_name, specific_file_name,
276 convert_dir == 'a', force_overwrite, verbose))
278 perror("Processing failed");
279 exit(1);
282 return 0;