Logo Search packages:      
Sourcecode: scummvm version File versions  Download package

md5table.c

/* md5table - Convert a MD5 table to either PHP or C++ code
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.

 * This program 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 General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * $URL$
 * $Id$
 *
 */

#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

void error(const char *s, ...) {
      char buf[1024];
      va_list va;

      va_start(va, s);
      vsnprintf(buf, 1024, s, va);
      va_end(va);

      fprintf(stderr, "ERROR: %s!\n", buf);

      exit(1);
}

void warning(const char *s, ...) {
      char buf[1024];
      va_list va;

      va_start(va, s);
      vsnprintf(buf, 1024, s, va);
      va_end(va);

      fprintf(stderr, "WARNING: %s!\n", buf);
}

00057 typedef struct {
      const char *key;
      const char *value;
} StringMap;

00062 typedef struct {
      const char *md5;
      const char *language;
      const char *platform;
      const char *variant;
      const char *extra;
      const char *size;
      const char *desc;
      const char *comments;
      const char *infoSource;
} Entry;

/* Map MD5 table platform names to ScummVM constant names.
 * Note: Currently not many constants are defined within ScummVM. However, more
 * will probably be added eventually (see also commented out constants in
 * common/util.h).
 */
static const StringMap platformMap[] = {
      { "2gs",          "kPlatformApple2GS" },
      { "3DO",          "kPlatform3DO" },
      { "Amiga",        "kPlatformAmiga" },
      { "Atari",        "kPlatformAtariST" },
      { "C64",          "kPlatformC64" },
      { "DOS",          "kPlatformPC" },
      { "FM-TOWNS",           "kPlatformFMTowns" },
      { "Mac",          "kPlatformMacintosh" },
      { "NES",          "kPlatformNES" },
      { "PC-Engine",          "kPlatformPCEngine" },
      { "SEGA",         "kPlatformSegaCD" },
      { "Windows",            "kPlatformWindows" },
      { "Wii",          "kPlatformWii" },

      { "All?",         "kPlatformUnknown" },
      { "All",          "kPlatformUnknown" },

      { 0,              "kPlatformUnknown" }
};

static const StringMap langMap[] = {
      { "en",           "EN_ANY" },
      { "us",           "EN_USA" },
      { "gb",           "EN_GRB" },
      { "de",           "DE_DEU" },
      { "fr",           "FR_FRA" },
      { "it",           "IT_ITA" },
      { "br",           "PT_BRA" },
      { "es",           "ES_ESP" },
      { "jp",           "JA_JPN" },
      { "zh",           "ZH_TWN" },
      { "ko",           "KO_KOR" },
      { "se",           "SE_SWE" },
      { "en",           "EN_GRB" },
      { "he",           "HE_ISR" },
      { "ru",           "RU_RUS" },
      { "cz",           "CZ_CZE" },
      { "nl",           "NL_NLD" },
      { "nb",           "NB_NOR" },
      { "pl",           "PL_POL" },

      { "All",    "UNK_LANG" },
      { "All?",   "UNK_LANG" },

      { 0,        "UNK_LANG" }
};

static const char *php_header =
      "<!--\n"
      "  This file was generated by the md5table tool on %s"
      "  DO NOT EDIT MANUALLY!\n"
      " -->\n"
      "<?php\n"
      "\n";

static const char *c_header =
      "/*\n"
      "  This file was generated by the md5table tool on %s"
      "  DO NOT EDIT MANUALLY!\n"
      " */\n"
      "\n"
      "struct MD5Table {\n"
      "     const char *md5;\n"
      "     const char *gameid;\n"
      "     const char *variant;\n"
      "     const char *extra;\n"
      "     int32 filesize;\n"
      "     Common::Language language;\n"
      "     Common::Platform platform;\n"
      "};\n"
      "\n"
      "static const MD5Table md5table[] = {\n";

static const char *c_footer =
      "     { 0, 0, 0, 0, 0, Common::UNK_LANG, Common::kPlatformUnknown }\n"
      "};\n";

static void parseEntry(Entry *entry, char *line) {
      assert(entry);
      assert(line);

      /* Split at the tabs */
      entry->md5 = strtok(line, "\t\n\r");
      entry->size = strtok(NULL, "\t\n\r");
      entry->language = strtok(NULL, "\t\n\r");
      entry->platform = strtok(NULL, "\t\n\r");
      entry->variant = strtok(NULL, "\t\n\r");
      entry->extra = strtok(NULL, "\t\n\r");
      entry->desc = strtok(NULL, "\t\n\r");
      entry->infoSource = strtok(NULL, "\t\n\r");
}

static int isEmptyLine(const char *line) {
      const char *whitespace = " \t\n\r";
      while (*line) {
            if (!strchr(whitespace, *line))
                  return 0;
            line++;
      }
      return 1;
}

static const char *mapStr(const char *str, const StringMap *map) {
      assert(str);
      assert(map);
      while (map->key) {
            if (0 == strcmp(map->key, str))
                  return map->value;
            map++;
      }
      warning("mapStr: unknown string '%s', defaulting to '%s'", str, map->value);
      return map->value;
}

void showhelp(const char *exename)
{
      printf("\nUsage: %s <params>\n", exename);
      printf("\nParams:\n");
      printf(" --c++   output C++ code for inclusion in ScummVM (default)\n");
      printf(" --php   output PHP code for the web site\n");
      printf(" --txt   output TXT file (should be identical to input file)\n");
      exit(2);
}

/* needed to call from qsort */
int strcmp_wrapper(const void *s1, const void *s2)
{
      return strcmp((const char *)s1, (const char *)s2);
}

int main(int argc, char *argv[])
{
      FILE *inFile = stdin;
      FILE *outFile = stdout;
      char buffer[1024];
      char section[256];
      char gameid[32];
      char *line;
      int err;
      int i;
      time_t theTime;
      char *generationDate;

      const int entrySize = 256;
      int numEntries = 0, maxEntries = 1;
      char *entriesBuffer = (char *)malloc(maxEntries * entrySize);

      typedef enum {
            kCPPOutput,
            kPHPOutput,
            kTXTOutput
      } OutputMode;

      OutputMode outputMode = kCPPOutput;

      if (argc != 2)
            showhelp(argv[0]);
      if (strcmp(argv[1], "--c++") == 0) {
            outputMode = kCPPOutput;
      } else if (strcmp(argv[1], "--php") == 0) {
            outputMode = kPHPOutput;
      } else if (strcmp(argv[1], "--txt") == 0) {
            outputMode = kTXTOutput;
      } else {
            showhelp(argv[0]);
      }

      time(&theTime);
      generationDate = strdup(asctime(gmtime(&theTime)));

      if (outputMode == kPHPOutput)
            fprintf(outFile, php_header, generationDate);

      section[0] = 0;
      gameid[0] = 0;
      while ((line = fgets(buffer, sizeof(buffer), inFile))) {
            /* Parse line */
            if (line[0] == '#' || isEmptyLine(line)) {
                  if (outputMode == kTXTOutput)
                        fprintf(outFile, "%s", line);
                  continue;   /* Skip comments & empty lines */
            }
            if (line[0] == '\t') {
                  Entry entry;
                  assert(section[0]);
                  parseEntry(&entry, line+1);
                  if (outputMode == kPHPOutput) {
                        fprintf(outFile, "\taddEntry(");

                        // Print the description string
                        fprintf(outFile, "\"");
                        if (entry.extra && strcmp(entry.extra, "-")) {
                              fprintf(outFile, "%s", entry.extra);
                              if (entry.desc && strcmp(entry.desc, "-"))
                                    fprintf(outFile, " (%s)", entry.desc);
                        }
                        fprintf(outFile, "\", ");

                        fprintf(outFile, "\"%s\", ", entry.platform);
                        fprintf(outFile, "\"%s\", ", entry.language);
                        fprintf(outFile, "\"%s\"", entry.md5);
                        if (entry.infoSource)
                              fprintf(outFile, ", \"%s\"", entry.infoSource);
                        fprintf(outFile, ");\n");
                  } else if (outputMode == kTXTOutput) {
                        fprintf(outFile, "\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
                              entry.md5,
                              entry.size,
                              entry.language,
                              entry.platform,
                              entry.variant,
                              entry.extra,
                              entry.desc,
                              entry.infoSource ? entry.infoSource : ""
                              );
                  } else if (entry.md5) {
                        if (numEntries >= maxEntries) {
                              maxEntries *= 2;
                              entriesBuffer = (char *)realloc(entriesBuffer, maxEntries * entrySize);
                        }
                        if (0 == strcmp(entry.variant, "-"))
                              entry.variant = "";
                        if (0 == strcmp(entry.extra, "-"))
                              entry.extra = "";
                        snprintf(entriesBuffer + numEntries * entrySize, entrySize,
                              "\t{ \"%s\", \"%s\", \"%s\", \"%s\", %s, Common::%s, Common::%s },\n",
                              entry.md5,
                              gameid,
                              entry.variant,
                              entry.extra,
                              entry.size,
                              mapStr(entry.language, langMap),
                              mapStr(entry.platform, platformMap));
                        numEntries++;
                  }
            } else {
                  if (outputMode == kPHPOutput && gameid[0] != 0) {
                        // If there is an active section, close it now
                        fprintf(outFile, "endSection();\n");
                  }
                  // Read the gameid, followed by a tab
                  for (i = 0; *line && *line != '\t'; ++i)
                        gameid[i] = *line++;
                  assert(i > 0);
                  gameid[i] = 0;
                  assert(*line != 0);
                  line++;

                  // Read the section header (usually the full game name)
                  for (i = 0; *line && *line != '\n'; ++i)
                        section[i] = *line++;
                  assert(i > 0);
                  section[i] = 0;

                  // If in PHP or TXT mode, we write the output immediately
                  if (outputMode == kPHPOutput) {
                        fprintf(outFile, "beginSection(\"%s\", \"%s\");\n", section, gameid);
                  } else if (outputMode == kTXTOutput) {
                        fprintf(outFile, "%s\t%s\n", gameid, section);
                  }
            }
      }

      err = ferror(inFile);
      if (err)
            error("Failed reading from input file, error %d", err);

      if (outputMode == kPHPOutput) {
            if (gameid[0] != 0)           // If there is an active section, close it now
                  fprintf(outFile, "endSection();\n");

            fprintf(outFile, "?>\n");
      }

      if (outputMode == kCPPOutput) {
            /* Printf header */
            fprintf(outFile, c_header, generationDate);
            /* Now sort the MD5 table (this allows for binary searches) */
            qsort(entriesBuffer, numEntries, entrySize, strcmp_wrapper);
            /* Output the table and emit warnings if duplicate md5s are found */
            buffer[0] = '\0';
            for (i = 0; i < numEntries; ++i) {
                  const char *currentEntry = entriesBuffer + i * entrySize;
                  fprintf(outFile, "%s", currentEntry);
                  if (strncmp(currentEntry + 4, buffer, 32) == 0) {
                        warning("Duplicate MD5 found '%.32s'", buffer);
                  } else {
                        strncpy(buffer, currentEntry + 4, 32);
                  }
            }
            /* Finally, print the footer */
            fprintf(outFile, "%s", c_footer);
      }

      free(entriesBuffer);
      free(generationDate);

      return 0;
}

Generated by  Doxygen 1.6.0   Back to index