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

create_kyradat.cpp

/* ScummVM Tools
 * Copyright (C) 2007 The ScummVM project
 *
 * 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: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/tags/release-0-11-1/tools/create_kyradat/create_kyradat.cpp $
 * $Id: create_kyradat.cpp 30944 2008-02-23 22:50:18Z sev $
 *
 */

// HACK to allow building with the SDL backend on MinGW
// see bug #1800764 "TOOLS: MinGW tools building broken"
#ifdef main
#undef main
#endif // main

#include "create_kyradat.h"

#include "md5.h"

enum {
      kKyraDatVersion = 17,
      kIndexSize = 12
};

// tables

#include "misc.h"
#include "eng.h"
#include "esp.h"
#include "fre.h"
#include "ger.h"
#include "towns.h"
#include "amiga.h"

const Game kyra1FanTranslations[] = {
      { kKyra1, IT_ITA, kTalkieVersion, "d0f1752098236083d81b9497bd2b6989", kyra1FreCD },
      GAME_DUMMY_ENTRY
};

bool extractRaw(PAKFile &out, const Game *g, const byte *data, const uint32 size, const char *filename, int fmtPatch = 0);
bool extractStrings(PAKFile &out, const Game *g, const byte *data, const uint32 size, const char *filename, int fmtPatch = 0);
bool extractRooms(PAKFile &out, const Game *g, const byte *data, const uint32 size, const char *filename, int fmtPatch = 0);
bool extractShapes(PAKFile &out, const Game *g, const byte *data, const uint32 size, const char *filename, int fmtPatch = 0);

void createFilename(char *dstFilename, const int lang, const int special, const char *filename);
void createLangFilename(char *dstFilename, const int lang, const int special, const char *filename);

const ExtractType extractTypeTable[] = {
      { kTypeLanguageList, extractStrings, createLangFilename },
      { kTypeStringList, extractStrings, createFilename },
      { kTypeRoomList, extractRooms, createFilename },
      { kTypeShapeList, extractShapes, createFilename },
      { kTypeRawData, extractRaw, createFilename },
      { -1, 0, 0}
};

const ExtractFilename extractFilenames[] = {
      // INTRO / OUTRO sequences
      { kForestSeq, kTypeRawData, "FOREST.SEQ" },
      { kKallakWritingSeq, kTypeRawData, "KALLAK-WRITING.SEQ" },
      { kKyrandiaLogoSeq, kTypeRawData, "KYRANDIA-LOGO.SEQ" },
      { kKallakMalcolmSeq, kTypeRawData, "KALLAK-MALCOLM.SEQ" },
      { kMalcolmTreeSeq, kTypeRawData, "MALCOLM-TREE.SEQ" },
      { kWestwoodLogoSeq, kTypeRawData, "WESTWOOD-LOGO.SEQ" },
      { kDemo1Seq, kTypeRawData, "DEMO1.SEQ" },
      { kDemo2Seq, kTypeRawData, "DEMO2.SEQ" },
      { kDemo3Seq, kTypeRawData, "DEMO3.SEQ" },
      { kDemo4Seq, kTypeRawData, "DEMO4.SEQ" },
      { kOutroReunionSeq, kTypeRawData, "REUNION.SEQ" },

      // INTRO / OUTRO strings
      { kIntroCPSStrings, kTypeStringList, "INTRO-CPS.TXT" },
      { kIntroCOLStrings, kTypeStringList, "INTRO-COL.TXT" },
      { kIntroWSAStrings, kTypeStringList, "INTRO-WSA.TXT" },
      { kIntroStrings, kTypeLanguageList, "INTRO-STRINGS" },
      { kOutroHomeString, kTypeLanguageList, "HOME" },

      // INGAME strings
      { kItemNames, kTypeLanguageList, "ITEMLIST" },
      { kTakenStrings, kTypeLanguageList, "TAKEN" },
      { kPlacedStrings, kTypeLanguageList, "PLACED" },
      { kDroppedStrings, kTypeLanguageList, "DROPPED" },
      { kNoDropStrings, kTypeLanguageList, "NODROP" },
      { kPutDownString, kTypeLanguageList, "PUTDOWN" },
      { kWaitAmuletString, kTypeLanguageList, "WAITAMUL" },
      { kBlackJewelString, kTypeLanguageList, "BLACKJEWEL" },
      { kPoisonGoneString, kTypeLanguageList, "POISONGONE" },
      { kHealingTipString, kTypeLanguageList, "HEALINGTIP" },
      { kThePoisonStrings, kTypeLanguageList, "THEPOISON" },
      { kFluteStrings, kTypeLanguageList, "FLUTE" },
      { kWispJewelStrings, kTypeLanguageList, "WISPJEWEL" },
      { kMagicJewelStrings, kTypeLanguageList, "MAGICJEWEL" },
      { kFlaskFullString, kTypeLanguageList, "FLASKFULL" },
      { kFullFlaskString, kTypeLanguageList, "FULLFLASK" },
      { kVeryCleverString, kTypeLanguageList, "VERYCLEVER" },
      { kNewGameString, kTypeLanguageList, "NEWGAME" },

      // GUI strings table
      { kGUIStrings, kTypeLanguageList, "GUISTRINGS" },
      { kConfigStrings, kTypeLanguageList, "CONFIGSTRINGS" },
      
      // ROOM table/filenames
      { kRoomList, kTypeRoomList, "ROOM-TABLE.ROOM" },
      { kRoomFilenames, kTypeStringList, "ROOM-FILENAMES.TXT" },

      // SHAPE tables
      { kDefaultShapes, kTypeShapeList, "SHAPES-DEFAULT.SHP" },
      { kHealing1Shapes, kTypeShapeList, "HEALING.SHP" },
      { kHealing2Shapes, kTypeShapeList, "HEALING2.SHP" },
      { kPoisonDeathShapes, kTypeShapeList, "POISONDEATH.SHP" },
      { kFluteShapes, kTypeShapeList, "FLUTE.SHP" },
      { kWinter1Shapes, kTypeShapeList, "WINTER1.SHP" },
      { kWinter2Shapes, kTypeShapeList, "WINTER2.SHP" },
      { kWinter3Shapes, kTypeShapeList, "WINTER3.SHP" },
      { kDrinkShapes, kTypeShapeList, "DRINK.SHP" },
      { kWispShapes, kTypeShapeList, "WISP.SHP" },
      { kMagicAnimShapes, kTypeShapeList, "MAGICANIM.SHP" },
      { kBranStoneShapes, kTypeShapeList, "BRANSTONE.SHP" },

      // IMAGE filename table
      { kCharacterImageFilenames, kTypeStringList, "CHAR-IMAGE.TXT" },

      // AMULET anim
      { kAmuleteAnimSeq, kTypeRawData, "AMULETEANIM.SEQ" },

      // PALETTE table
      { kPaletteList1, kTypeRawData, "PALTABLE1.PAL" },
      { kPaletteList2, kTypeRawData, "PALTABLE2.PAL" },
      { kPaletteList3, kTypeRawData, "PALTABLE3.PAL" },
      { kPaletteList4, kTypeRawData, "PALTABLE4.PAL" },
      { kPaletteList5, kTypeRawData, "PALTABLE5.PAL" },
      { kPaletteList6, kTypeRawData, "PALTABLE6.PAL" },
      { kPaletteList7, kTypeRawData, "PALTABLE7.PAL" },
      { kPaletteList8, kTypeRawData, "PALTABLE8.PAL" },
      { kPaletteList9, kTypeRawData, "PALTABLE9.PAL" },
      { kPaletteList10, kTypeRawData, "PALTABLE10.PAL" },
      { kPaletteList11, kTypeRawData, "PALTABLE11.PAL" },
      { kPaletteList12, kTypeRawData, "PALTABLE12.PAL" },
      { kPaletteList13, kTypeRawData, "PALTABLE13.PAL" },
      { kPaletteList14, kTypeRawData, "PALTABLE14.PAL" },
      { kPaletteList15, kTypeRawData, "PALTABLE15.PAL" },
      { kPaletteList16, kTypeRawData, "PALTABLE16.PAL" },
      { kPaletteList17, kTypeRawData, "PALTABLE17.PAL" },
      { kPaletteList18, kTypeRawData, "PALTABLE18.PAL" },
      { kPaletteList19, kTypeRawData, "PALTABLE19.PAL" },
      { kPaletteList20, kTypeRawData, "PALTABLE20.PAL" },
      { kPaletteList21, kTypeRawData, "PALTABLE21.PAL" },
      { kPaletteList22, kTypeRawData, "PALTABLE22.PAL" },
      { kPaletteList23, kTypeRawData, "PALTABLE23.PAL" },
      { kPaletteList24, kTypeRawData, "PALTABLE24.PAL" },
      { kPaletteList25, kTypeRawData, "PALTABLE25.PAL" },
      { kPaletteList26, kTypeRawData, "PALTABLE26.PAL" },
      { kPaletteList27, kTypeRawData, "PALTABLE27.PAL" },
      { kPaletteList28, kTypeRawData, "PALTABLE28.PAL" },
      { kPaletteList29, kTypeRawData, "PALTABLE29.PAL" },
      { kPaletteList30, kTypeRawData, "PALTABLE30.PAL" },
      { kPaletteList31, kTypeRawData, "PALTABLE31.PAL" },
      { kPaletteList32, kTypeRawData, "PALTABLE32.PAL" },
      { kPaletteList33, kTypeRawData, "PALTABLE33.PAL" },
      
      // FM-TOWNS specific
      { kKyra1TownsSFXTable, kTypeRawData, "SFXTABLE" },
      { kCreditsStrings, kTypeRawData, "CREDITS" },
      { kMenuSKB, kTypeStringList, "MENUSKB" },
      { kSjisVTable, kTypeRawData, "SJISTABLE" },

      { -1, 0, 0 }
};

const ExtractFilename *getFilenameDesc(const int id) {
      for (const ExtractFilename *i = extractFilenames; i->id != -1; ++i) {
            if (i->id == id)
                  return i;
      }
      return 0;
}

// type processing

const ExtractType *findExtractType(const int type) {
      for (const ExtractType *i = extractTypeTable; i->type != -1; ++i) {
            if (i->type == type)
                  return i;
      }
      return 0;
}

// filename processing

bool getFilename(char *dstFilename, const Game *g, const int id) {
      const ExtractFilename *i = getFilenameDesc(id);

      if (!i)
            return false;

      const ExtractType *type = findExtractType(i->type);
      type->createFilename(dstFilename, g->lang, g->special, i->filename);
      return true;
}

void createFilename(char *dstFilename, const int lang, const int special, const char *filename) {
      strcpy(dstFilename, filename);
      
      for (const SpecialExtension *specialE = specialTable; specialE->special != -1; ++specialE) {
            if (specialE->special == special) {
                  strcat(dstFilename, ".");
                  strcat(dstFilename, specialE->ext);
                  break;
            }
      }
}

void createLangFilename(char *dstFilename, const int lang, const int special, const char *filename) {
      strcpy(dstFilename, filename);
      
      for (const Language *langE = languageTable; langE->lang != -1; ++langE) {
            if (langE->lang == lang) {
                  strcat(dstFilename, ".");
                  strcat(dstFilename, langE->ext);
                  break;
            }
      }
      
      for (const SpecialExtension *specialE = specialTable; specialE->special != -1; ++specialE) {
            if (specialE->special == special) {
                  strcat(dstFilename, ".");
                  strcat(dstFilename, specialE->ext);
                  break;
            }
      }
}

// entry checking

int hashEntries(const int *entries) {
      int hash = 0;
      for (const int *i = entries; *i != -1; ++i) {
            hash += *i;
      }
      return hash;
}

bool hasEntry(const ExtractEntry *entries, const int id) {
      for (const ExtractEntry *i = entries; i->id != -1; ++i) {
            if (i->id == id)
                  return true;
      }
      return false;
}

int hashEntries(const Game *game, const GameNeed *need, const PAKFile *file) {
      int hash = 0;
      char filename[128];
      for (const int *i = need->entries; *i != -1; ++i) {
            if (hasEntry(game->entries, *i)) {
                  hash += *i;
                  continue;
            }
            
            if (file) {
                  filename[0] = 0;

                  if (!getFilename(filename, game, *i))
                        error("couldn't find filename for id %d", *i);
                  
                  PAKFile::cFileList *list = file->getFileList();
                  if (list && list->findEntry(filename) != 0)
                        hash += *i;
            }
      }

      return hash;
} 

bool hasNeededEntries(const Game *game, const PAKFile *file) {
      for (const GameNeed *need = gameNeedTable; need->game != -1; ++need) {
            if (need->game == game->game && need->special == game->special) {
                  if (hashEntries(need->entries) == hashEntries(game, need, file))
                        return true;
            }
      }

      return false;
}

// extraction

bool extractRaw(PAKFile &out, const Game *g, const byte *data, const uint32 size, const char *filename, int fmtPatch) {
      uint8 *buffer = 0;

      if (fmtPatch == 2) {
            buffer = new uint8[0x12602];
            assert(buffer);
            memcpy(buffer, data, 0x7EE5);
            memcpy(buffer + 0x7EE5, data + 0x7EE7, 0x7FFF);
            memcpy(buffer + 0xFEE4, data + 0xFEE8, 0x271E);
      } else {
            buffer = new uint8[size];
            assert(buffer);
            memcpy(buffer, data, size);
      }

      return out.addFile(filename, buffer, size);
}

bool extractStrings(PAKFile &out, const Game *g, const byte *data, const uint32 size, const char *filename, int fmtPatch) {
      uint32 entries = 0;
      uint32 targetsize = size + 4;
      for (uint32 i = 0; i < size; ++i) {
            if (!data[i]) {
                  if (g->special == kAmigaVersion) {
                        if (!((i + 1) & 0x1))
                              ++entries;
                  } else {
                        ++entries;
                  }

                  if (g->special == kFMTownsVersionE || g->special == kFMTownsVersionJ) {
                        // prevents creation of empty entries (which we have mostly between all strings in the fm-towns version)
                        while (!data[++i]) {
                              if (i == size)
                                    break;
                              targetsize--;
                        }
                        if (fmtPatch == 1) {
                              // Here is the first step of the extra treatment for all fm-towns string arrays that 
                              // contain more than one string and which the original code
                              // addresses via stringname[boolJapanese].
                              // We simply skip every other string
                              if (i == size)
                                    continue;
                              uint32 len = strlen((const char*) data + i);
                              i += len;
#if 1
                              // FIXME: Not sure whether this correct; the original code was ambiguious, see below
                              targetsize = targetsize - 1 - len;
#else
                              targetsize = --targetsize - len;    // FIXME: This operation is undefined
#endif
                              while (!data[++i]) {
                                    if (i == len)
                                          break;
                                    targetsize--;
                              }
                        }
                  }
            }
      }
      
      if (fmtPatch == 2) {
            if (g->special == kFMTownsVersionE)
                  targetsize--;
            if (g->special == kFMTownsVersionJ)
                  targetsize += 2;        
            entries += (g->special - 1);
      }
      
      uint8 *buffer = new uint8[targetsize];
      assert(buffer);
      uint8 *output = buffer;
      const uint8 *input = (const uint8*) data;

      WRITE_BE_UINT32(output, entries); output += 4;
      if (g->special == kFMTownsVersionE || g->special == kFMTownsVersionJ) {
            const byte * c = data + size;
            do {
                  if (fmtPatch == 2 && input - data == 0x3C0 && input[0x10] == 0x32) {
                        memcpy(output, input, 0x0F);
                        input += 0x11; output += 0x0F;
                  }
                  strcpy((char*) output, (const char*) input);
                  uint32 stringsize = strlen((const char*)output) + 1;
                  input += stringsize; output += stringsize;
                  // skip empty entries
                  while (!*input) {
                        // Write one empty string into intro strings file
                        if (fmtPatch == 2) {
                              if ((g->special == kFMTownsVersionE && input - data == 0x260) ||
                                    (g->special == kFMTownsVersionJ && input - data == 0x2BD) ||
                                    (g->special == kFMTownsVersionJ && input - data == 0x2BE))
                                          *output++ = *input;
                        }

                        if (++input == c)
                              break;
                  }

                  if (fmtPatch == 1) {
                        // Here is the extra treatment for all fm-towns string arrays that 
                        // contain more than one string and which the original code
                        // addresses via stringname[boolJapanese].
                        // We simply skip every other string
                        if (input == c)
                              continue;
                        input += strlen((const char*)input);
                        while (!*input) {
                              if (++input == c)
                                    break;
                        }
                  }

            } while (input < c);
      } else if (g->special == kAmigaVersion) {
            // we need to strip some aligment zeros out here
            int dstPos = 0;
            for (uint32 i = 0; i < size; ++i) {
                  if (!data[i] && ((i+1) & 0x1))
                        continue;
                  *output++ = data[i];
                  ++dstPos;
            }
            targetsize = dstPos + 4;
      } else {
            memcpy(output, data, size);
      }

      return out.addFile(filename, buffer, targetsize);
}

bool extractRooms(PAKFile &out, const Game *g, const byte *data, const uint32 size, const char *filename, int fmtPatch) {
      // different entry size for the fm-towns version
      const int roomEntrySize = (g->special == kFMTownsVersionE || g->special == kFMTownsVersionJ) ? (0x69) : ((g->special == kAmigaVersion) ? 0x52 : 0x51);
      const int countRooms = size / roomEntrySize;

      uint8 *buffer = new uint8[countRooms * 9 + 4];
      assert(buffer);
      uint8 *output = buffer;

      WRITE_BE_UINT32(output, countRooms); output += 4;

      const byte *src = data;
      if (g->special == kAmigaVersion) {
            for (int i = 0; i < countRooms; ++i) {
                  *output++ = *src++; assert(*src == 0); ++src;
                  memcpy(output, src, 8); output += 0x8;
                  src += roomEntrySize - 0x2;
            }
      } else {
            for (int i = 0; i < countRooms; ++i) {
                  *output++ = *src++;
                  WRITE_BE_UINT16(output, READ_LE_UINT16(src)); output += 2; src += 2;
                  WRITE_BE_UINT16(output, READ_LE_UINT16(src)); output += 2; src += 2;
                  WRITE_BE_UINT16(output, READ_LE_UINT16(src)); output += 2; src += 2;
                  WRITE_BE_UINT16(output, READ_LE_UINT16(src)); output += 2; src += 2;
                  src += roomEntrySize - 0x9;
            }
      }

      return out.addFile(filename, buffer, countRooms * 9 + 4);
}

bool extractShapes(PAKFile &out, const Game *g, const byte *data, const uint32 size, const char *filename, int fmtPatch) {
      byte *buffer = new byte[size + 1 * 4];
      assert(buffer);
      byte *output = buffer;

      const int count = size / 0x07;
      WRITE_BE_UINT32(output, count); output += 4;
      memcpy(output, data, size);

      return out.addFile(filename, buffer, size + 1 * 4);
}

// index generation

enum {
      GF_FLOPPY   = 1 <<  0,
      GF_TALKIE   = 1 <<  1,
      GF_FMTOWNS  = 1 <<  2,
      GF_DEMO           = 1 <<  3,
      GF_ENGLISH  = 1 <<  4,
      GF_FRENCH   = 1 <<  5,
      GF_GERMAN   = 1 <<  6,
      GF_SPANISH  = 1 <<  7,
      GF_ITALIAN  = 1 <<  8,
      GF_JAPANESE = 1 <<  9,
      // ...
      GF_LNGUNK   = 1 << 16,
      GF_AMIGA    = 1 << 17
};

uint32 getFeatures(const Game *g) {
      uint32 features = 0;

      if (g->special == kTalkieVersion)
            features |= GF_TALKIE;
      else if (g->special == kDemoVersion)
            features |= GF_DEMO;
      else if (g->special == kFMTownsVersionE || g->special == kFMTownsVersionJ)
            features |= GF_FMTOWNS;
      else if (g->special == kAmigaVersion)
            features |= GF_AMIGA;
      else
            features |= GF_FLOPPY;

      if (g->lang == EN_ANY)
            features |= GF_ENGLISH;
      else if (g->lang == DE_DEU)
            features |= GF_GERMAN;
      else if (g->lang == FR_FRA)
            features |= GF_FRENCH;
      else if (g->lang == ES_ESP)
            features |= GF_SPANISH;
      else if (g->lang == IT_ITA)
            features |= GF_ITALIAN;
      else if (g->lang == JA_JPN)
            features |= GF_JAPANESE;
      
      return features;
}

bool updateIndex(byte *dst, const int dstSize, const Game *g) {
      if ((size_t)dstSize < kIndexSize)
            return false;

      WRITE_BE_UINT32(dst, kKyraDatVersion); dst += 4;
      WRITE_BE_UINT32(dst, g->game); dst += 4;
      uint32 features = READ_BE_UINT32(dst);
      features |= getFeatures(g);
      WRITE_BE_UINT32(dst, features); dst += 4;

      return true;
}

bool checkIndex(const byte *s, const int srcSize) {
      if ((size_t)srcSize < sizeof(uint32))
            return false;     
      uint32 version = READ_BE_UINT32(s);
      return (version == kKyraDatVersion);
}

bool updateIndex(PAKFile &out, const Game *g) {
      char filename[32];
      createFilename(filename, -1, g->special, "INDEX");
      
      byte *index = new byte[kIndexSize];
      assert(index);
      memset(index, 0, kIndexSize);
      
      uint32 size = 0;
      const uint8 *data = out.getFileData(filename, &size);
      if (data)
            memcpy(index, data, size);
      
      if (!updateIndex(index, kIndexSize, g)) {
            delete [] index;
            return false;
      }
      
      out.removeFile(filename);
      if (!out.addFile(filename, index, kIndexSize)) {
            fprintf(stderr, "ERROR: couldn't update %s file", filename);
            delete [] index;
            return false;
      }
      
      return true;
}

bool checkIndex(PAKFile &out, const Game *g) {
      char filename[32];
      createFilename(filename, -1, g->special, "INDEX");

      uint32 size = 0;
      const uint8 *data = out.getFileData(filename, &size);
      if (!data)
            return true;

      return checkIndex(data, size);
}

// main processing

void printHelp(const char *f) {
      printf("Usage:\n");
      printf("%s output inputfiles ...", f);
}

bool process(PAKFile &out, const Game *g, const byte *data, const uint32 size);
const Game *findGame(const byte *buffer, const uint32 size);

int main(int argc, char *argv[]) {
      if (argc < 3) {
            printHelp(argv[0]);
            return -1;
      }
      
      PAKFile out;
      out.loadFile(argv[1], false);

      for (int i = 2; i < argc; ++i) {
            FILE *input = fopen(argv[i], "rb");

            if (!input) {
                  warning("skipping missing file '%s'", argv[i]);
                  continue;
            }

            uint32 size = fileSize(input);
            fseek(input, 0, SEEK_SET);
            
            byte *buffer = new uint8[size];
            assert(buffer);
            
            if (fread(buffer, 1, size, input) != size) {
                  warning("couldn't read from file '%s', skipping it", argv[i]);
                  delete [] buffer;
                  fclose(input);
                  continue;
            }
            fclose(input);
            
            const Game *g = findGame(buffer, size);
            if (!g) {
                  warning("skipping unknown file '%s'", argv[i]);
                  delete [] buffer;
                  continue;
            }
            
            if (!hasNeededEntries(g, &out)) {
                  warning("file '%s' is missing offset entries and thus can't be processed", argv[i]);
                  delete [] buffer;
                  continue;
            }
            
            if (!process(out, g, buffer, size))
                  fprintf(stderr, "ERROR: couldn't process file '%s'", argv[i]);
            
            if (g->special == kFMTownsVersionE) {
                  // The English and non language specific data has now been extracted
                  // so we switch to Japanese and extract the rest
                  if (!hasNeededEntries(++g, &out)) {
                        warning("file '%s' is missing offset entries and thus can't be processed", argv[i]);
                        delete [] buffer;
                        continue;
                  }
                  if (!process(out, g, buffer, size))
                        fprintf(stderr, "ERROR: couldn't process file '%s'", argv[i]);
            }
            
            delete [] buffer;
      }

      if (!out.saveFile(argv[1]))
            error("couldn't save changes to '%s'", argv[1]);

      uint8 digest[16];
      if (!md5_file(argv[1], digest, 0))
            error("couldn't calc. md5 for file '%s'", argv[1]);
      FILE *f = fopen(argv[1], "ab");
      if (!f)
            error("couldn't open file '%s'", argv[1]);
      if (fwrite(digest, 1, 16, f) != 16)
            error("couldn't write md5sum to file '%s'", argv[1]);
      fclose(f);
      
      return 0;
}

bool process(PAKFile &out, const Game *g, const byte *data, const uint32 size) {
      char filename[128];

      if (!checkIndex(out, g)) {
            fprintf(stderr, "ERROR: corrupted INDEX file\n");
            return false;
      }

      for (const ExtractEntry *i = g->entries; i->id != -1; ++i) {
            if (!getFilename(filename, g, i->id)) {
                  fprintf(stderr, "ERROR: couldn't get filename for id %d\n", i->id);
                  return false;
            }
            
            const ExtractFilename *fDesc = getFilenameDesc(i->id);

            if (!fDesc) {
                  fprintf(stderr, "ERROR: couldn't find file description for id %d\n", i->id);
                  return false;
            }

            const ExtractType *tDesc = findExtractType(fDesc->type);
            
            if (!tDesc) {
                  fprintf(stderr, "ERROR: couldn't find type description for id %d\n", i->id);
                  return false;
            }
            
            PAKFile::cFileList *list = out.getFileList();
            if (list && list->findEntry(filename) != 0)
                  continue;
            
            int patch = 0;
            if (g->special == kFMTownsVersionE || g->special == kFMTownsVersionJ) {
                  // FM Towns files that need addional patches
                  if (i->id == kTakenStrings || i->id == kNoDropStrings || i->id == kPoisonGoneString ||
                        i->id == kThePoisonStrings || i->id == kFluteStrings || i->id == kWispJewelStrings)
                        patch = 1;
                  else if (i->id == kIntroStrings || i->id == kKyra1TownsSFXTable)
                        patch = 2;                                
            }
            
            if (!tDesc->extract(out, g, data + i->startOff, i->endOff - i->startOff, filename, patch)) {
                  fprintf(stderr, "ERROR: couldn't extract id %d\n", i->id);
                  return false;
            }
      }

      if (!updateIndex(out, g)) {
            error("couldn't update INDEX file, stop processing of all files");
            return false;
      }

      return true;
}

// game data detection

const Game *gameDescs[] = {
      kyra1EngGames,
      kyra1EspGames,
      kyra1FreGames,
      kyra1GerGames,
      kyra1TownsGames,
      kyra1AmigaGames,
      kyra1FanTranslations,
      0
};

const Game *findGame(const byte *buffer, const uint32 size) {
      md5_context ctx;
      uint8 digest[16];
      char md5str[33];

      md5_starts(&ctx);
      md5_update(&ctx, buffer, size);
      md5_finish(&ctx, digest);
      
      for (int j = 0; j < 16; ++j) {
            sprintf(md5str + j*2, "%02x", (int)digest[j]);
      }
      
      for (const Game **i = gameDescs; *i != 0; ++i) {
            for (const Game *p = *i; p->game != -1; ++p) {
                  if (strcmp(md5str, p->md5) == 0)
                        return p;
            }
      }

      printf("file is not supported (unknown md5 \"%s\")\n", md5str);
      return 0;
}



Generated by  Doxygen 1.6.0   Back to index