Extract WINE resources at compile-time
[reactos.git] / reactos / tools / bin2res / bin2res.c
1 /************************************************
2 *
3 * Converting binary resources from/to *.rc files
4 *
5 * Copyright 1999 Juergen Schmied
6 * Copyright 2003 Dimitrie O. Paun
7 *
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.
12 *
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.
17 *
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23 #include "config.h"
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <ctype.h>
28 #include <string.h>
29 #include <sys/stat.h>
30 #include <limits.h>
31 #ifdef HAVE_SYS_PARAM_H
32 # include <sys/param.h>
33 #endif
34 #ifdef HAVE_UNISTD_H
35 # include <unistd.h>
36 #endif
37
38 #if defined(WIN32)
39 #define DIR_SEPARATOR "\\"
40 #else
41 #define DIR_SEPARATOR "/"
42 #endif
43
44 extern int mkstemps(char *template, int suffix_len);
45
46 int process_resources(const char* input_file_name, const char* specific_file_name,
47 const char* relative_path,
48 int inserting, int force_processing, int verbose);
49
50 static const char* help =
51 "Usage: bin2res [OPTIONS] <rsrc.rc>\n"
52 " -a archive binaries into the <rsrc.rc> file\n"
53 " -x extract binaries from the <rsrc.rc> file\n"
54 " -i <filename> archive the named file into the <rsrc.rc> file\n"
55 " -o <filename> extract the named file from the <rsrc.rc> file\n"
56 " -b <path> assume resources are relative to this base path\n"
57 " -f force processing of older resources\n"
58 " -v causes the command to be verbous during processing\n"
59 " -h print this help screen and exit\n"
60 "\n"
61 "This tool allows the insertion/extractions of embedded binary\n"
62 "resources to/from .rc files, for storage within the cvs tree.\n"
63 "This is accomplished by placing a magic marker in a comment\n"
64 "just above the resource. The marker consists of the BINRES\n"
65 "string followed by the file name. For example, to insert a\n"
66 "brand new binary resource in a .rc file, place the marker\n"
67 "above empty brackets:\n"
68 " /* BINRES idb_std_small.bmp */\n"
69 " {}\n"
70 "To merge the binary resources into the .rc file, run:\n"
71 " bin2res -a myrsrc.rc\n"
72 "Only resources that are newer than the .rc are processed.\n"
73 "To extract the binary resources from the .rc file, run:\n"
74 " bin2res -x myrsrc.rc\n"
75 "Binary files newer than the .rc file are not overwritten.\n"
76 "\n"
77 "To force processing of all resources, use the -f flag.\n"
78 "To process a particular file, use the -i/-o options.\n";
79
80 void usage(void)
81 {
82 printf(help);
83 exit(1);
84 }
85
86 int insert_hexdump (FILE* outfile, FILE* infile)
87 {
88 int i, c;
89
90 fprintf (outfile, "{\n '");
91 for (i = 0; (c = fgetc(infile)) != EOF; i++)
92 {
93 if (i && (i % 16) == 0) fprintf (outfile, "'\n '");
94 if (i % 16) fprintf (outfile, " ");
95 fprintf(outfile, "%02X", c);
96 }
97 fprintf (outfile, "'\n}");
98
99 return 1;
100 }
101
102 int hex2bin(char c)
103 {
104 if (!isxdigit(c)) return -1024;
105 if (isdigit(c)) return c - '0';
106 return toupper(c) - 'A' + 10;
107 }
108
109 int extract_hexdump (FILE* outfile, FILE* infile)
110 {
111 int byte, c;
112
113 while ( (c = fgetc(infile)) != EOF && c != '}')
114 {
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);
122 }
123 return 1;
124 }
125
126 const char* parse_marker(const char *line, time_t* last_updated)
127 {
128 static char res_file_name[PATH_MAX], *rpos, *wpos;
129 struct stat st;
130
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;
135
136 *last_updated = (stat(res_file_name, &st) < 0) ? 0 : st.st_mtime;
137
138 return res_file_name;
139 }
140
141 const char* parse_include(const char *line)
142 {
143 static char include_filename[PATH_MAX], *rpos, *wpos;
144
145 if (!(rpos = strstr(line, "#"))) return 0;
146 for (rpos += 1; *rpos && isspace(*rpos); rpos++) /**/;
147 if (!(rpos = strstr(line, "include"))) return 0;
148 for (rpos += 7; *rpos && isspace(*rpos); rpos++) /**/;
149 for (; *rpos && (*rpos == '"' || *rpos == '<'); rpos++) /**/;
150 for (wpos = include_filename; *rpos && !(*rpos == '"' || *rpos == '<'); ) *wpos++ = *rpos++;
151 if (!(rpos = strstr(include_filename, ".rc"))) return 0;
152 *wpos = 0;
153
154 return include_filename;
155 }
156
157 char* get_filename_with_full_path(char* output, const char* filename, const char* relative_path)
158 {
159 if (relative_path != NULL && relative_path[0] != 0)
160 {
161 strcpy(output, relative_path);
162 strcat(output, DIR_SEPARATOR);
163 strcat(output, filename);
164 }
165 else
166 strcpy(output, filename);
167 return output;
168 }
169
170 void process_includes(const char* input_file_name, const char* specific_file_name,
171 const char* relative_path,
172 int inserting, int force_processing, int verbose)
173 {
174 char filename[PATH_MAX];
175 char buffer[2048];
176 const char *include_file_name;
177 FILE *fin;
178 int c;
179
180 if (!(fin = fopen(input_file_name, "r"))) return;
181 for (c = EOF; fgets(buffer, sizeof(buffer), fin); c = EOF)
182 {
183 if (!(include_file_name = parse_include(buffer))) continue;
184 if ( verbose ) printf ( "Processing included file %s\n", include_file_name);
185 process_resources(get_filename_with_full_path(filename, include_file_name, relative_path),
186 specific_file_name, relative_path,
187 inserting, force_processing, verbose);
188 }
189 fclose(fin);
190 }
191
192 int process_resources(const char* input_file_name, const char* specific_file_name,
193 const char* relative_path,
194 int inserting, int force_processing, int verbose)
195 {
196 char filename[PATH_MAX];
197 char buffer[2048], tmp_file_name[PATH_MAX];
198 const char *res_file_name;
199 time_t rc_last_update, res_last_update;
200 FILE *fin, *fres, *ftmp = 0;
201 struct stat st;
202 int fd, c;
203
204 if (!(fin = fopen(input_file_name, "r"))) return 0;
205 if (stat(input_file_name, &st) < 0) return 0;
206 rc_last_update = st.st_mtime;
207
208 if (inserting)
209 {
210 strcpy(tmp_file_name, input_file_name);
211 strcat(tmp_file_name, "-XXXXXX.temp");
212 if ((fd = mkstemps(tmp_file_name, 5)) == -1)
213 {
214 strcpy(tmp_file_name, "/tmp/bin2res-XXXXXX.temp");
215 if ((fd = mkstemps(tmp_file_name, 5)) == -1) return 0;
216 }
217 if (!(ftmp = fdopen(fd, "w"))) return 0;
218 }
219
220 for (c = EOF; fgets(buffer, sizeof(buffer), fin); c = EOF)
221 {
222 if (inserting) fprintf(ftmp, "%s", buffer);
223 if (!(res_file_name = parse_marker(buffer, &res_last_update))) continue;
224 if ( (specific_file_name && strcmp(specific_file_name, res_file_name)) ||
225 (!force_processing && ((rc_last_update < res_last_update) == !inserting)) )
226 {
227 if (verbose) printf("skipping '%s'\n", res_file_name);
228 continue;
229 }
230
231 if (verbose) printf("processing '%s'\n", res_file_name);
232 while ( (c = fgetc(fin)) != EOF && c != '{')
233 if (inserting) fputc(c, ftmp);
234 if (c == EOF) break;
235
236 if (!(fres = fopen(get_filename_with_full_path(filename, res_file_name, relative_path),
237 inserting ? "rb" : "wb"))) break;
238 if (inserting)
239 {
240 if (!insert_hexdump(ftmp, fres)) break;
241 while ( (c = fgetc(fin)) != EOF && c != '}') /**/;
242 }
243 else
244 {
245 if (!extract_hexdump(fres, fin)) break;
246 }
247 fclose(fres);
248 }
249
250 fclose(fin);
251
252 if (inserting)
253 {
254 fclose(ftmp);
255 if (c == EOF)
256 {
257 if (rename(tmp_file_name, input_file_name) < 0)
258 {
259 /* try unlinking first, Windows rename is brain-damaged */
260 if (unlink(input_file_name) < 0 || rename(tmp_file_name, input_file_name) < 0)
261 {
262 unlink(tmp_file_name);
263 return 0;
264 }
265 }
266 }
267 else unlink(tmp_file_name);
268 }
269 else
270 {
271 process_includes(input_file_name, specific_file_name, relative_path,
272 inserting, force_processing, verbose);
273 }
274
275 return c == EOF;
276 }
277
278 int main(int argc, char **argv)
279 {
280 int convert_dir = 0, optc;
281 int force_overwrite = 0, verbose = 0;
282 const char* input_file_name = 0;
283 const char* specific_file_name = 0;
284 const char* relative_path = 0;
285
286 while((optc = getopt(argc, argv, "axi:o:b:fhv")) != EOF)
287 {
288 switch(optc)
289 {
290 case 'a':
291 case 'x':
292 if (convert_dir) usage();
293 convert_dir = optc;
294 break;
295 case 'i':
296 case 'o':
297 if (specific_file_name) usage();
298 specific_file_name = optarg;
299 optc = ((optc == 'i') ? 'a' : 'x');
300 if (convert_dir && convert_dir != optc) usage();
301 convert_dir = optc;
302 break;
303 case 'b':
304 if (relative_path) usage();
305 relative_path = optarg;
306 break;
307 case 'f':
308 force_overwrite = 1;
309 break;
310 case 'v':
311 verbose = 1;
312 break;
313 case 'h':
314 printf(help);
315 exit(0);
316 break;
317 default:
318 usage();
319 }
320 }
321
322 if (optind + 1 != argc) usage();
323 input_file_name = argv[optind];
324
325 if (!convert_dir) usage();
326
327 if (!process_resources(input_file_name, specific_file_name, relative_path,
328 convert_dir == 'a', force_overwrite, verbose))
329 {
330 perror("Processing failed");
331 exit(1);
332 }
333
334 return 0;
335 }