1 /* md5table - Convert a MD5 table to either PHP or C++ code
2 * Copyright (C) 2003-2006 The ScummVM Team
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
30 void error(const char *s
, ...) {
35 vsnprintf(buf
, 1024, s
, va
);
38 fprintf(stderr
, "ERROR: %s!\n", buf
);
43 void warning(const char *s
, ...) {
48 vsnprintf(buf
, 1024, s
, va
);
51 fprintf(stderr
, "WARNING: %s!\n", buf
);
68 const char *infoSource
;
71 /* Map MD5 table platform names to ScummVM constant names.
72 * Note: Currently not many constants are defined within ScummVM. However, more
73 * will probably be added eventually (see also commented out constants in
76 static const StringMap platformMap
[] = {
77 { "3DO", "kPlatform3DO" },
78 { "Amiga", "kPlatformAmiga" },
79 { "Atari", "kPlatformAtariST" },
80 { "C64", "kPlatformC64" },
81 { "DOS", "kPlatformPC" },
82 { "FM-TOWNS", "kPlatformFMTowns" },
83 { "Mac", "kPlatformMacintosh" },
84 { "NES", "kPlatformNES" },
85 { "PC-Engine", "kPlatformPCEngine" },
86 { "SEGA", "kPlatformSegaCD" },
87 { "Windows", "kPlatformWindows" },
88 { "Wii", "kPlatformWii" },
90 { "All?", "kPlatformUnknown" },
91 { "All", "kPlatformUnknown" },
93 { 0, "kPlatformUnknown" }
96 static const StringMap langMap
[] = {
117 { "All", "UNK_LANG" },
118 { "All?", "UNK_LANG" },
123 static const char *php_header
=
125 " This file was generated by the md5table tool on %s"
126 " DO NOT EDIT MANUALLY!\n"
131 static const char *c_header
=
133 " This file was generated by the md5table tool on %s"
134 " DO NOT EDIT MANUALLY!\n"
137 "struct MD5Table {\n"
138 " const char *md5;\n"
139 " const char *gameid;\n"
140 " const char *variant;\n"
141 " const char *extra;\n"
143 " Common::Language language;\n"
144 " Common::Platform platform;\n"
147 "static const MD5Table md5table[] = {\n";
149 static const char *c_footer
=
150 " { 0, 0, 0, 0, 0, Common::UNK_LANG, Common::kPlatformUnknown }\n"
153 static void parseEntry(Entry
*entry
, char *line
) {
157 /* Split at the tabs */
158 entry
->md5
= strtok(line
, "\t\n\r");
159 entry
->size
= strtok(NULL
, "\t\n\r");
160 entry
->language
= strtok(NULL
, "\t\n\r");
161 entry
->platform
= strtok(NULL
, "\t\n\r");
162 entry
->variant
= strtok(NULL
, "\t\n\r");
163 entry
->extra
= strtok(NULL
, "\t\n\r");
164 entry
->desc
= strtok(NULL
, "\t\n\r");
165 entry
->infoSource
= strtok(NULL
, "\t\n\r");
168 static int isEmptyLine(const char *line
) {
169 const char *whitespace
= " \t\n\r";
171 if (!strchr(whitespace
, *line
))
178 static const char *mapStr(const char *str
, const StringMap
*map
) {
182 if (0 == strcmp(map
->key
, str
))
186 warning("mapStr: unknown string '%s', defaulting to '%s'", str
, map
->value
);
190 void showhelp(const char *exename
)
192 printf("\nUsage: %s <params>\n", exename
);
193 printf("\nParams:\n");
194 printf(" --c++ output C++ code for inclusion in ScummVM (default)\n");
195 printf(" --php output PHP code for the web site\n");
196 printf(" --txt output TXT file (should be identical to input file)\n");
200 /* needed to call from qsort */
201 int strcmp_wrapper(const void *s1
, const void *s2
)
203 return strcmp((const char *)s1
, (const char *)s2
);
206 int main(int argc
, char *argv
[])
208 FILE *inFile
= stdin
;
209 FILE *outFile
= stdout
;
217 const char *generationDate
;
219 const int entrySize
= 256;
220 int numEntries
= 0, maxEntries
= 1;
221 char *entriesBuffer
= malloc(maxEntries
* entrySize
);
229 OutputMode outputMode
= kCPPOutput
;
233 if (strcmp(argv
[1], "--c++") == 0) {
234 outputMode
= kCPPOutput
;
235 } else if (strcmp(argv
[1], "--php") == 0) {
236 outputMode
= kPHPOutput
;
237 } else if (strcmp(argv
[1], "--txt") == 0) {
238 outputMode
= kTXTOutput
;
244 generationDate
= strdup(asctime(gmtime(&theTime
)));
246 if (outputMode
== kPHPOutput
)
247 fprintf(outFile
, php_header
, generationDate
);
251 while ((line
= fgets(buffer
, sizeof(buffer
), inFile
))) {
253 if (line
[0] == '#' || isEmptyLine(line
)) {
254 if (outputMode
== kTXTOutput
)
255 fprintf(outFile
, "%s", line
);
256 continue; /* Skip comments & empty lines */
258 if (line
[0] == '\t') {
261 parseEntry(&entry
, line
+1);
262 if (outputMode
== kPHPOutput
) {
263 fprintf(outFile
, "\taddEntry(");
265 // Print the description string
266 fprintf(outFile
, "\"");
267 if (entry
.extra
&& strcmp(entry
.extra
, "-")) {
268 fprintf(outFile
, "%s", entry
.extra
);
269 if (entry
.desc
&& strcmp(entry
.desc
, "-"))
270 fprintf(outFile
, " (%s)", entry
.desc
);
272 fprintf(outFile
, "\", ");
274 fprintf(outFile
, "\"%s\", ", entry
.platform
);
275 fprintf(outFile
, "\"%s\", ", entry
.language
);
276 fprintf(outFile
, "\"%s\"", entry
.md5
);
277 if (entry
.infoSource
)
278 fprintf(outFile
, ", \"%s\"", entry
.infoSource
);
279 fprintf(outFile
, ");\n");
280 } else if (outputMode
== kTXTOutput
) {
281 fprintf(outFile
, "\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
289 entry
.infoSource
? entry
.infoSource
: ""
291 } else if (entry
.md5
) {
292 if (numEntries
>= maxEntries
) {
294 entriesBuffer
= realloc(entriesBuffer
, maxEntries
* entrySize
);
296 if (0 == strcmp(entry
.variant
, "-"))
298 if (0 == strcmp(entry
.extra
, "-"))
300 snprintf(entriesBuffer
+ numEntries
* entrySize
, entrySize
,
301 "\t{ \"%s\", \"%s\", \"%s\", \"%s\", %s, Common::%s, Common::%s },\n",
307 mapStr(entry
.language
, langMap
),
308 mapStr(entry
.platform
, platformMap
));
312 if (outputMode
== kPHPOutput
&& gameid
[0] != 0) {
313 // If there is an active section, close it now
314 fprintf(outFile
, "endSection();\n");
316 // Read the gameid, followed by a tab
317 for (i
= 0; *line
&& *line
!= '\t'; ++i
)
324 // Read the section header (usually the full game name)
325 for (i
= 0; *line
&& *line
!= '\n'; ++i
)
326 section
[i
] = *line
++;
330 // If in PHP or TXT mode, we write the output immediately
331 if (outputMode
== kPHPOutput
) {
332 fprintf(outFile
, "beginSection(\"%s\", \"%s\");\n", section
, gameid
);
333 } else if (outputMode
== kTXTOutput
) {
334 fprintf(outFile
, "%s\t%s\n", gameid
, section
);
339 err
= ferror(inFile
);
341 error("Failed reading from input file, error %d", err
);
343 if (outputMode
== kPHPOutput
) {
344 if (gameid
[0] != 0) // If there is an active section, close it now
345 fprintf(outFile
, "endSection();\n");
347 fprintf(outFile
, "?>\n");
350 if (outputMode
== kCPPOutput
) {
352 fprintf(outFile
, c_header
, generationDate
);
353 /* Now sort the MD5 table (this allows for binary searches) */
354 qsort(entriesBuffer
, numEntries
, entrySize
, strcmp_wrapper
);
355 /* Output the table and emit warnings if duplicate md5s are found */
357 for (i
= 0; i
< numEntries
; ++i
) {
358 const char *currentEntry
= entriesBuffer
+ i
* entrySize
;
359 fprintf(outFile
, "%s", currentEntry
);
360 if (strncmp(currentEntry
+ 4, buffer
, 32) == 0) {
361 warning("Duplicate MD5 found '%.32s'", buffer
);
363 strncpy(buffer
, currentEntry
+ 4, 32);
366 /* Finally, print the footer */
367 fprintf(outFile
, "%s", c_footer
);