2007-02-03 14:52:02 +00:00
|
|
|
/* 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$
|
|
|
|
* $Id$
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2007-09-29 11:40:31 +00:00
|
|
|
// HACK to allow building with the SDL backend on MinGW
|
|
|
|
// see bug #1800764 "TOOLS: MinGW tools building broken"
|
|
|
|
#ifdef main
|
|
|
|
#undef main
|
|
|
|
#endif // main
|
|
|
|
|
2007-02-03 14:52:02 +00:00
|
|
|
#include "create_kyradat.h"
|
|
|
|
|
|
|
|
#include "md5.h"
|
|
|
|
|
|
|
|
enum {
|
2008-05-12 13:35:58 +00:00
|
|
|
kKyraDatVersion = 25,
|
2007-02-03 14:52:02 +00:00
|
|
|
kIndexSize = 12
|
|
|
|
};
|
|
|
|
|
|
|
|
// tables
|
|
|
|
|
|
|
|
#include "misc.h"
|
|
|
|
#include "eng.h"
|
|
|
|
#include "esp.h"
|
|
|
|
#include "fre.h"
|
|
|
|
#include "ger.h"
|
2007-02-07 19:29:15 +00:00
|
|
|
#include "towns.h"
|
2007-03-12 20:42:28 +00:00
|
|
|
#include "amiga.h"
|
2007-02-03 14:52:02 +00:00
|
|
|
|
2008-01-10 23:24:43 +00:00
|
|
|
#include "hof_floppy.h"
|
|
|
|
#include "hof_towns.h"
|
|
|
|
#include "hof_cd.h"
|
|
|
|
#include "hof_demo.h"
|
|
|
|
|
2008-05-12 13:35:58 +00:00
|
|
|
#include "malcolm.h"
|
|
|
|
|
2007-07-10 13:03:11 +00:00
|
|
|
const Game kyra1FanTranslations[] = {
|
2007-09-16 13:51:57 +00:00
|
|
|
{ kKyra1, IT_ITA, kTalkieVersion, "d0f1752098236083d81b9497bd2b6989", kyra1FreCD },
|
2007-07-10 13:03:11 +00:00
|
|
|
GAME_DUMMY_ENTRY
|
|
|
|
};
|
|
|
|
|
2007-02-07 19:29:15 +00:00
|
|
|
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);
|
2008-01-10 23:24:43 +00:00
|
|
|
bool extractHofSeqData(PAKFile &out, const Game *g, const byte *data, const uint32 size, const char *filename, int fmtPatch = 0);
|
2008-04-06 14:54:14 +00:00
|
|
|
bool extractHofShapeAnimDataV1(PAKFile &out, const Game *g, const byte *data, const uint32 size, const char *filename, int fmtPatch = 0);
|
|
|
|
bool extractHofShapeAnimDataV2(PAKFile &out, const Game *g, const byte *data, const uint32 size, const char *filename, int fmtPatch = 0);
|
|
|
|
|
2008-05-12 13:35:58 +00:00
|
|
|
bool extractStringsWoSuffix(PAKFile &out, const Game *g, const byte *data, const uint32 size, const char *filename, int fmtPatch = 0);
|
|
|
|
bool extractRaw16to8(PAKFile &out, const Game *g, const byte *data, const uint32 size, const char *filename, int fmtPatch = 0);
|
|
|
|
bool extractMrShapeAnimData(PAKFile &out, const Game *g, const byte *data, const uint32 size, const char *filename, int fmtPatch = 0);
|
|
|
|
|
2008-01-10 23:24:43 +00:00
|
|
|
int extractHofSeqData_checkString(const void *ptr, uint8 checkSize);
|
|
|
|
int extractHofSeqData_isSequence(const void *ptr, const Game *g, uint32 maxCheckSize);
|
|
|
|
int extractHofSeqData_isControl(const void *ptr, uint32 size);
|
2007-02-03 14:52:02 +00:00
|
|
|
|
2008-01-10 23:24:43 +00:00
|
|
|
void createFilename(char *dstFilename, const int gid, const int lang, const int special, const char *filename);
|
|
|
|
void createLangFilename(char *dstFilename, const int gid, const int lang, const int special, const char *filename);
|
2007-02-03 14:52:02 +00:00
|
|
|
|
|
|
|
const ExtractType extractTypeTable[] = {
|
|
|
|
{ kTypeLanguageList, extractStrings, createLangFilename },
|
|
|
|
{ kTypeStringList, extractStrings, createFilename },
|
|
|
|
{ kTypeRoomList, extractRooms, createFilename },
|
|
|
|
{ kTypeShapeList, extractShapes, createFilename },
|
|
|
|
{ kTypeRawData, extractRaw, createFilename },
|
2008-01-10 23:24:43 +00:00
|
|
|
|
|
|
|
{ k2TypeSeqData, extractHofSeqData, createFilename },
|
2008-04-06 14:54:14 +00:00
|
|
|
{ k2TypeShpDataV1, extractHofShapeAnimDataV1, createFilename },
|
|
|
|
{ k2TypeShpDataV2, extractHofShapeAnimDataV2, createFilename },
|
2008-01-10 23:24:43 +00:00
|
|
|
|
2008-05-12 13:35:58 +00:00
|
|
|
{ k2TypeSoundList, extractStringsWoSuffix, createFilename },
|
|
|
|
{ k3TypeRaw16to8, extractRaw16to8, createFilename },
|
|
|
|
{ k3TypeShpData, extractMrShapeAnimData, createFilename },
|
|
|
|
|
2007-09-22 07:32:28 +00:00
|
|
|
{ -1, 0, 0}
|
2007-02-03 14:52:02 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
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" },
|
2008-01-27 19:47:41 +00:00
|
|
|
|
2007-02-03 14:52:02 +00:00
|
|
|
// 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" },
|
|
|
|
|
2008-01-10 23:24:43 +00:00
|
|
|
// AUDIO filename table
|
|
|
|
{ kAudioTracks, kTypeStringList, "TRACKS.TXT" },
|
|
|
|
{ kAudioTracksIntro, kTypeStringList, "TRACKSINT.TXT" },
|
|
|
|
|
2007-02-03 14:52:02 +00:00
|
|
|
// 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" },
|
2008-01-27 19:47:41 +00:00
|
|
|
|
2007-02-03 14:52:02 +00:00
|
|
|
// FM-TOWNS specific
|
2008-01-10 23:24:43 +00:00
|
|
|
{ kKyra1TownsSFXwdTable, kTypeRawData, "SFXWDTABLE" },
|
|
|
|
{ kKyra1TownsSFXbtTable, kTypeRawData, "SFXBTTABLE" },
|
|
|
|
{ kKyra1TownsCDATable, kTypeRawData, "CDATABLE" },
|
2007-02-07 19:29:15 +00:00
|
|
|
{ kCreditsStrings, kTypeRawData, "CREDITS" },
|
2008-01-10 23:24:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
// HAND OF FATE
|
|
|
|
|
|
|
|
// Sequence Player
|
|
|
|
{ k2SeqplayPakFiles, kTypeStringList, "S_PAKFILES.TXT" },
|
|
|
|
{ k2SeqplayCredits, kTypeRawData, "S_CREDITS.TXT" },
|
2008-01-27 02:06:59 +00:00
|
|
|
{ k2SeqplayCreditsSpecial, kTypeStringList, "S_CREDITS2.TXT" },
|
2008-01-10 23:24:43 +00:00
|
|
|
{ k2SeqplayStrings, kTypeLanguageList, "S_STRINGS" },
|
|
|
|
{ k2SeqplaySfxFiles, kTypeStringList, "S_SFXFILES.TXT" },
|
|
|
|
{ k2SeqplayTlkFiles, kTypeLanguageList, "S_TLKFILES" },
|
|
|
|
{ k2SeqplaySeqData, k2TypeSeqData, "S_DATA.SEQ" },
|
|
|
|
{ k2SeqplayIntroTracks, kTypeStringList, "S_INTRO.TRA" },
|
2008-01-27 19:47:41 +00:00
|
|
|
{ k2SeqplayFinaleTracks, kTypeStringList, "S_FINALE.TRA" },
|
2008-01-10 23:24:43 +00:00
|
|
|
{ k2SeqplayIntroCDA, kTypeRawData, "S_INTRO.CDA" },
|
|
|
|
{ k2SeqplayFinaleCDA, kTypeRawData, "S_FINALE.CDA" },
|
2008-04-06 14:54:14 +00:00
|
|
|
{ k2SeqplayShapeAnimData, k2TypeShpDataV1, "S_DEMO.SHP" },
|
2008-01-10 23:24:43 +00:00
|
|
|
|
|
|
|
// Ingame
|
|
|
|
{ k2IngamePakFiles, kTypeStringList, "I_PAKFILES.TXT" },
|
2008-02-16 15:45:15 +00:00
|
|
|
{ k2IngameSfxFiles, kTypeStringList, "I_SFXFILES.TRA" },
|
|
|
|
{ k2IngameSfxIndex, kTypeRawData, "I_SFXINDEX.MAP" },
|
2008-01-10 23:24:43 +00:00
|
|
|
{ k2IngameTracks, kTypeStringList, "I_TRACKS.TRA" },
|
|
|
|
{ k2IngameCDA, kTypeRawData, "I_TRACKS.CDA" },
|
2008-02-16 15:45:15 +00:00
|
|
|
{ k2IngameTalkObjIndex, kTypeRawData, "I_TALKOBJECTS.MAP" },
|
|
|
|
{ k2IngameTimJpStrings, kTypeStringList, "I_TIMJPSTR.TXT" },
|
2008-04-06 14:54:14 +00:00
|
|
|
{ k2IngameItemAnimData, k2TypeShpDataV2, "I_INVANIM.SHP" },
|
2008-05-12 13:35:58 +00:00
|
|
|
{ k2IngameTlkDemoStrings, kTypeLanguageList, "I_TLKDEMO.TXT" },
|
|
|
|
|
|
|
|
|
|
|
|
// MALCOLM'S REVENGE
|
|
|
|
{ k3MainMenuStrings, kTypeStringList, "MAINMENU.TXT" },
|
|
|
|
{ k3MusicFiles, k2TypeSoundList, "SCORE.TRA" },
|
|
|
|
{ k3ScoreTable, kTypeRawData, "SCORE.MAP" },
|
|
|
|
{ k3SfxFiles, kTypeStringList, "SFXFILES.TRA" },
|
|
|
|
{ k3SfxMap, k3TypeRaw16to8, "SFXINDEX.MAP" },
|
|
|
|
{ k3ItemAnimData, k3TypeShpData, "INVANIM.SHP" },
|
|
|
|
{ k3ItemMagicTable, k3TypeRaw16to8, "ITEMMAGIC.MAP" },
|
|
|
|
{ k3ItemStringMap, kTypeRawData, "ITEMSTRINGS.MAP" },
|
|
|
|
|
2007-02-03 14:52:02 +00:00
|
|
|
{ -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);
|
2008-01-10 23:24:43 +00:00
|
|
|
type->createFilename(dstFilename, g->game, g->lang, g->special, i->filename);
|
2007-02-03 14:52:02 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2008-01-10 23:24:43 +00:00
|
|
|
void createFilename(char *dstFilename, const int gid, const int lang, const int special, const char *filename) {
|
2007-02-03 14:52:02 +00:00
|
|
|
strcpy(dstFilename, filename);
|
2008-01-10 23:24:43 +00:00
|
|
|
|
2008-02-16 15:45:15 +00:00
|
|
|
static const char *gidExtensions[] = { "", ".K2", ".K3" };
|
2008-01-10 23:24:43 +00:00
|
|
|
strcat(dstFilename, gidExtensions[gid]);
|
2008-01-27 19:47:41 +00:00
|
|
|
|
2007-02-03 14:52:02 +00:00
|
|
|
for (const SpecialExtension *specialE = specialTable; specialE->special != -1; ++specialE) {
|
|
|
|
if (specialE->special == special) {
|
|
|
|
strcat(dstFilename, ".");
|
|
|
|
strcat(dstFilename, specialE->ext);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-01-10 23:24:43 +00:00
|
|
|
void createLangFilename(char *dstFilename, const int gid, const int lang, const int special, const char *filename) {
|
2007-02-03 14:52:02 +00:00
|
|
|
strcpy(dstFilename, filename);
|
2008-01-10 23:24:43 +00:00
|
|
|
|
2007-02-03 14:52:02 +00:00
|
|
|
for (const Language *langE = languageTable; langE->lang != -1; ++langE) {
|
|
|
|
if (langE->lang == lang) {
|
|
|
|
strcat(dstFilename, ".");
|
|
|
|
strcat(dstFilename, langE->ext);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2008-01-10 23:24:43 +00:00
|
|
|
|
2008-02-16 15:45:15 +00:00
|
|
|
static const char *gidExtensions[] = { "", ".K2", ".K3" };
|
2008-01-10 23:24:43 +00:00
|
|
|
strcat(dstFilename, gidExtensions[gid]);
|
2008-01-27 19:47:41 +00:00
|
|
|
|
2007-02-03 14:52:02 +00:00
|
|
|
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;
|
|
|
|
}
|
2008-01-27 19:47:41 +00:00
|
|
|
|
2007-02-03 14:52:02 +00:00
|
|
|
if (file) {
|
|
|
|
filename[0] = 0;
|
|
|
|
|
|
|
|
if (!getFilename(filename, game, *i))
|
|
|
|
error("couldn't find filename for id %d", *i);
|
2008-01-27 19:47:41 +00:00
|
|
|
|
2007-02-03 14:52:02 +00:00
|
|
|
PAKFile::cFileList *list = file->getFileList();
|
|
|
|
if (list && list->findEntry(filename) != 0)
|
|
|
|
hash += *i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return hash;
|
2008-01-27 19:47:41 +00:00
|
|
|
}
|
2007-02-03 14:52:02 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2007-02-07 19:29:15 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2007-02-03 14:52:02 +00:00
|
|
|
return out.addFile(filename, buffer, size);
|
|
|
|
}
|
|
|
|
|
2007-02-07 19:29:15 +00:00
|
|
|
bool extractStrings(PAKFile &out, const Game *g, const byte *data, const uint32 size, const char *filename, int fmtPatch) {
|
2007-02-03 14:52:02 +00:00
|
|
|
uint32 entries = 0;
|
2007-02-07 19:29:15 +00:00
|
|
|
uint32 targetsize = size + 4;
|
2007-02-03 14:52:02 +00:00
|
|
|
for (uint32 i = 0; i < size; ++i) {
|
2007-02-07 19:29:15 +00:00
|
|
|
if (!data[i]) {
|
2007-03-12 20:42:28 +00:00
|
|
|
if (g->special == kAmigaVersion) {
|
|
|
|
if (!((i + 1) & 0x1))
|
|
|
|
++entries;
|
|
|
|
} else {
|
|
|
|
++entries;
|
|
|
|
}
|
|
|
|
|
2008-01-10 23:24:43 +00:00
|
|
|
if (g->special == kFMTownsVersionE || g->special == kFMTownsVersionJ ||
|
|
|
|
g->special == k2TownsFile1E || g->special == k2TownsFile1J ||
|
2008-01-27 02:06:59 +00:00
|
|
|
g->special == k2TownsFile2E || g->special == k2TownsFile2J || fmtPatch == 5) {
|
2007-02-07 19:29:15 +00:00
|
|
|
// 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) {
|
2008-01-27 19:47:41 +00:00
|
|
|
// Here is the first step of the extra treatment for all fm-towns string arrays that
|
2007-02-07 19:29:15 +00:00
|
|
|
// contain more than one string and which the original code
|
|
|
|
// addresses via stringname[boolJapanese].
|
|
|
|
// We simply skip every other string
|
|
|
|
if (i == size)
|
|
|
|
continue;
|
2007-09-22 07:32:28 +00:00
|
|
|
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
|
2007-02-07 19:29:15 +00:00
|
|
|
while (!data[++i]) {
|
2007-09-22 07:32:28 +00:00
|
|
|
if (i == len)
|
2007-02-07 19:29:15 +00:00
|
|
|
break;
|
|
|
|
targetsize--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-01-27 19:47:41 +00:00
|
|
|
|
2007-02-07 19:29:15 +00:00
|
|
|
if (fmtPatch == 2) {
|
2007-03-04 15:05:17 +00:00
|
|
|
if (g->special == kFMTownsVersionE)
|
|
|
|
targetsize--;
|
|
|
|
if (g->special == kFMTownsVersionJ)
|
2008-01-10 23:24:43 +00:00
|
|
|
targetsize += 2;
|
2007-02-12 17:59:47 +00:00
|
|
|
entries += (g->special - 1);
|
2007-02-03 14:52:02 +00:00
|
|
|
}
|
2008-01-10 23:24:43 +00:00
|
|
|
|
|
|
|
if (fmtPatch == 3) {
|
|
|
|
entries++;
|
|
|
|
targetsize++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fmtPatch == 4) {
|
|
|
|
targetsize -= 9;
|
|
|
|
}
|
2008-01-27 19:47:41 +00:00
|
|
|
|
2007-02-07 19:29:15 +00:00
|
|
|
uint8 *buffer = new uint8[targetsize];
|
2007-02-03 14:52:02 +00:00
|
|
|
assert(buffer);
|
|
|
|
uint8 *output = buffer;
|
2007-09-22 11:47:51 +00:00
|
|
|
const uint8 *input = (const uint8*) data;
|
2007-02-03 14:52:02 +00:00
|
|
|
|
|
|
|
WRITE_BE_UINT32(output, entries); output += 4;
|
2008-01-10 23:24:43 +00:00
|
|
|
if (g->special == kFMTownsVersionE || g->special == kFMTownsVersionJ ||
|
|
|
|
g->special == k2TownsFile1E || g->special == k2TownsFile1J ||
|
2008-01-27 02:06:59 +00:00
|
|
|
g->special == k2TownsFile2E || g->special == k2TownsFile2J || fmtPatch == 5) {
|
2007-02-07 19:29:15 +00:00
|
|
|
const byte * c = data + size;
|
|
|
|
do {
|
2007-03-04 15:05:17 +00:00
|
|
|
if (fmtPatch == 2 && input - data == 0x3C0 && input[0x10] == 0x32) {
|
|
|
|
memcpy(output, input, 0x0F);
|
|
|
|
input += 0x11; output += 0x0F;
|
|
|
|
}
|
2008-01-10 23:24:43 +00:00
|
|
|
|
2007-02-07 19:29:15 +00:00
|
|
|
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) ||
|
2007-02-12 17:59:47 +00:00
|
|
|
(g->special == kFMTownsVersionJ && input - data == 0x2BD) ||
|
|
|
|
(g->special == kFMTownsVersionJ && input - data == 0x2BE))
|
2007-02-07 19:29:15 +00:00
|
|
|
*output++ = *input;
|
|
|
|
}
|
|
|
|
|
2008-01-10 23:24:43 +00:00
|
|
|
// insert one dummy string at hof sequence strings position 59
|
|
|
|
if (fmtPatch == 3) {
|
|
|
|
if ((g->special == k2TownsFile1E && input - data == 0x695) ||
|
|
|
|
(g->special == k2TownsFile1J && input - data == 0x598))
|
|
|
|
*output++ = *input;
|
|
|
|
}
|
|
|
|
|
2007-02-07 19:29:15 +00:00
|
|
|
if (++input == c)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fmtPatch == 1) {
|
2008-01-27 19:47:41 +00:00
|
|
|
// Here is the extra treatment for all fm-towns string arrays that
|
2007-02-07 19:29:15 +00:00
|
|
|
// 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);
|
2007-03-12 20:42:28 +00:00
|
|
|
} else if (g->special == kAmigaVersion) {
|
|
|
|
// we need to strip some aligment zeros out here
|
|
|
|
int dstPos = 0;
|
2007-09-22 07:32:28 +00:00
|
|
|
for (uint32 i = 0; i < size; ++i) {
|
2007-03-12 20:42:28 +00:00
|
|
|
if (!data[i] && ((i+1) & 0x1))
|
|
|
|
continue;
|
|
|
|
*output++ = data[i];
|
|
|
|
++dstPos;
|
|
|
|
}
|
|
|
|
targetsize = dstPos + 4;
|
2007-02-07 19:29:15 +00:00
|
|
|
} else {
|
2008-01-10 23:24:43 +00:00
|
|
|
uint32 copySize = size;
|
|
|
|
if (fmtPatch == 4) {
|
|
|
|
memcpy(output, data, 44);
|
|
|
|
output += 44;
|
|
|
|
data += 44;
|
|
|
|
for (int t = 1; t != 10; t++) {
|
|
|
|
sprintf((char*) output, "COST%d_SH.PAK", t);
|
|
|
|
output += 13;
|
|
|
|
}
|
|
|
|
data += 126;
|
|
|
|
copySize -= 170;
|
|
|
|
}
|
|
|
|
memcpy(output, data, copySize);
|
2007-02-07 19:29:15 +00:00
|
|
|
}
|
2007-02-03 14:52:02 +00:00
|
|
|
|
2007-02-07 19:29:15 +00:00
|
|
|
return out.addFile(filename, buffer, targetsize);
|
2007-02-03 14:52:02 +00:00
|
|
|
}
|
|
|
|
|
2007-02-07 19:29:15 +00:00
|
|
|
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
|
2007-03-12 20:42:28 +00:00
|
|
|
const int roomEntrySize = (g->special == kFMTownsVersionE || g->special == kFMTownsVersionJ) ? (0x69) : ((g->special == kAmigaVersion) ? 0x52 : 0x51);
|
|
|
|
const int countRooms = size / roomEntrySize;
|
2007-02-03 14:52:02 +00:00
|
|
|
|
|
|
|
uint8 *buffer = new uint8[countRooms * 9 + 4];
|
|
|
|
assert(buffer);
|
|
|
|
uint8 *output = buffer;
|
|
|
|
|
|
|
|
WRITE_BE_UINT32(output, countRooms); output += 4;
|
|
|
|
|
|
|
|
const byte *src = data;
|
2007-03-12 20:42:28 +00:00
|
|
|
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;
|
|
|
|
}
|
2007-02-03 14:52:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return out.addFile(filename, buffer, countRooms * 9 + 4);
|
|
|
|
}
|
|
|
|
|
2007-02-07 19:29:15 +00:00
|
|
|
bool extractShapes(PAKFile &out, const Game *g, const byte *data, const uint32 size, const char *filename, int fmtPatch) {
|
2007-02-03 14:52:02 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2008-01-10 23:24:43 +00:00
|
|
|
bool extractHofSeqData(PAKFile &out, const Game *g, const byte *data, const uint32 size, const char *filename, int fmtPatch) {
|
|
|
|
int numSequences = 0;
|
|
|
|
int numNestedSequences = 0;
|
|
|
|
|
|
|
|
uint16 headerSize = 50 * sizeof(uint16);
|
|
|
|
uint16 bufferSize = size + headerSize;
|
|
|
|
byte *buffer = new byte[bufferSize];
|
|
|
|
assert(buffer);
|
|
|
|
memset(buffer, 0, bufferSize );
|
|
|
|
uint16 *header = (uint16*) buffer;
|
|
|
|
byte *output = buffer + headerSize;
|
|
|
|
uint16 *hdout = header;
|
|
|
|
|
|
|
|
//debug(1, "\nProcessing Hand of Fate sequence data:\n--------------------------------------\n");
|
|
|
|
for (int cycle = 0; cycle < 2; cycle++) {
|
|
|
|
const byte *ptr = data;
|
|
|
|
hdout++;
|
|
|
|
|
|
|
|
const byte * endOffs = (const byte *)(data + size);
|
|
|
|
|
|
|
|
// detect sequence structs
|
|
|
|
while (ptr < endOffs) {
|
|
|
|
if (ptr[1]) {
|
|
|
|
error("invalid sequence data encountered");
|
2008-05-07 14:31:45 +00:00
|
|
|
delete[] buffer;
|
2008-01-10 23:24:43 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
int v = extractHofSeqData_isSequence(ptr, g, endOffs - ptr);
|
|
|
|
|
|
|
|
if (cycle == 0 && v == 1) {
|
2008-01-27 02:06:59 +00:00
|
|
|
if ((g->special == k2FloppyFile1 && *ptr == 5) || (g->special == k2DemoVersion && (ptr - data == 312))) {
|
|
|
|
// patch for floppy version: skips invalid ferb sequence
|
|
|
|
// patch for demo: skips invalid title sequence
|
2008-01-10 23:24:43 +00:00
|
|
|
ptr += 54;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
numSequences++;
|
|
|
|
uint16 relOffs = (uint16) (output - buffer);
|
2008-04-06 14:54:14 +00:00
|
|
|
WRITE_BE_UINT16(hdout, relOffs);
|
2008-01-10 23:24:43 +00:00
|
|
|
hdout++;
|
2008-04-06 14:54:14 +00:00
|
|
|
|
|
|
|
WRITE_BE_UINT16(output, READ_LE_UINT16(ptr)); // flags
|
|
|
|
ptr += 2;
|
|
|
|
output += 2;
|
2008-01-10 23:24:43 +00:00
|
|
|
|
2008-04-06 14:54:14 +00:00
|
|
|
memcpy(output, ptr, 28); // wsa and cps file names
|
|
|
|
ptr += 28;
|
|
|
|
output += 28;
|
|
|
|
|
|
|
|
if (g->special == k2TownsFile1E) { // startupCommand + finalCommand
|
2008-01-10 23:24:43 +00:00
|
|
|
memcpy(output , ptr, 2);
|
|
|
|
ptr += 2;
|
|
|
|
output += 2;
|
|
|
|
} else {
|
|
|
|
*output++ = READ_LE_UINT16(ptr) & 0xff;
|
|
|
|
ptr += 2;
|
|
|
|
*output++ = READ_LE_UINT16(ptr) & 0xff;
|
|
|
|
ptr += 2;
|
2008-01-27 19:47:41 +00:00
|
|
|
}
|
2008-01-10 23:24:43 +00:00
|
|
|
|
2008-04-06 14:54:14 +00:00
|
|
|
for (int w = 0; w < 7; w++) { //stringIndex1 to yPos
|
|
|
|
WRITE_BE_UINT16(output, READ_LE_UINT16(ptr));
|
|
|
|
ptr += 2;
|
|
|
|
output += 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
ptr += 4;
|
|
|
|
WRITE_BE_UINT16(output, READ_LE_UINT16(ptr)); // duration
|
2008-01-10 23:24:43 +00:00
|
|
|
ptr += 2;
|
|
|
|
output+= 2;
|
|
|
|
|
|
|
|
} else if (cycle == 1 && v != 1 && v != -2) {
|
|
|
|
uint16 controlOffs = 0;
|
2008-05-12 13:35:58 +00:00
|
|
|
uint16 ctrSize = 0;
|
2008-01-10 23:24:43 +00:00
|
|
|
if (v) {
|
|
|
|
const byte *ctrStart = ptr;
|
|
|
|
while (v && v != -2) {
|
|
|
|
ptr++;
|
|
|
|
v = extractHofSeqData_isSequence(ptr, g, endOffs - ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (v == -2)
|
|
|
|
break;
|
|
|
|
|
2008-05-12 13:35:58 +00:00
|
|
|
ctrSize = (uint16)(ptr - ctrStart);
|
2008-01-10 23:24:43 +00:00
|
|
|
|
|
|
|
if (g->special != k2DemoVersion &&
|
|
|
|
extractHofSeqData_isControl(ctrStart, ctrSize)) {
|
|
|
|
controlOffs = (uint16) (output - buffer);
|
2008-04-06 14:54:14 +00:00
|
|
|
*output++ = ctrSize >> 2;
|
|
|
|
|
|
|
|
for (int cc = 0; cc < ctrSize; cc += 2)
|
|
|
|
WRITE_BE_UINT16(output + cc, READ_LE_UINT16(ctrStart + cc)); // frame control
|
2008-01-10 23:24:43 +00:00
|
|
|
output += ctrSize;
|
|
|
|
}
|
2008-01-27 19:47:41 +00:00
|
|
|
}
|
2008-01-10 23:24:43 +00:00
|
|
|
|
|
|
|
numNestedSequences++;
|
|
|
|
uint16 relOffs = (uint16) (output - buffer);
|
2008-04-06 14:54:14 +00:00
|
|
|
WRITE_BE_UINT16(hdout, relOffs);
|
2008-01-10 23:24:43 +00:00
|
|
|
hdout++;
|
|
|
|
|
2008-04-06 14:54:14 +00:00
|
|
|
WRITE_BE_UINT16(output, READ_LE_UINT16(ptr)); // flags
|
|
|
|
ptr += 2;
|
|
|
|
output += 2;
|
|
|
|
|
|
|
|
memcpy(output, ptr, 14); // wsa file name
|
|
|
|
ptr += 14;
|
|
|
|
output += 14;
|
2008-01-10 23:24:43 +00:00
|
|
|
|
2008-05-12 13:35:58 +00:00
|
|
|
// startframe
|
|
|
|
WRITE_BE_UINT16(output, READ_LE_UINT16(ptr));
|
|
|
|
ptr += 2;
|
|
|
|
output += 2;
|
|
|
|
|
|
|
|
// endFrame
|
|
|
|
WRITE_BE_UINT16(output, (ctrSize && ((ctrSize >> 2) < READ_LE_UINT16(ptr))) ? (ctrSize >> 2) : READ_LE_UINT16(ptr));
|
|
|
|
ptr += 2;
|
|
|
|
output += 2;
|
|
|
|
|
|
|
|
// frameDelay
|
|
|
|
WRITE_BE_UINT16(output, READ_LE_UINT16(ptr));
|
|
|
|
ptr += 2;
|
|
|
|
output += 2;
|
2008-04-06 14:54:14 +00:00
|
|
|
|
2008-01-10 23:24:43 +00:00
|
|
|
ptr += 4;
|
2008-04-06 14:54:14 +00:00
|
|
|
|
|
|
|
for (int w = 0; w < 2; w++) { //x, y
|
|
|
|
WRITE_BE_UINT16(output, READ_LE_UINT16(ptr));
|
|
|
|
ptr += 2;
|
|
|
|
output += 2;
|
|
|
|
}
|
2008-01-10 23:24:43 +00:00
|
|
|
|
|
|
|
if (!READ_LE_UINT32(ptr))
|
|
|
|
controlOffs = 0;
|
2008-01-27 19:47:41 +00:00
|
|
|
|
2008-04-06 14:54:14 +00:00
|
|
|
WRITE_BE_UINT16(output, controlOffs);
|
2008-01-10 23:24:43 +00:00
|
|
|
if (g->special != k2DemoVersion)
|
|
|
|
ptr += 4;
|
|
|
|
output += 2;
|
|
|
|
|
|
|
|
if (g->special != k2DemoVersion) {
|
2008-04-06 14:54:14 +00:00
|
|
|
for (int w = 0; w < 2; w++) { //startupCommand, finalCommand
|
|
|
|
WRITE_BE_UINT16(output, READ_LE_UINT16(ptr));
|
|
|
|
ptr += 2;
|
|
|
|
output += 2;
|
|
|
|
}
|
2008-01-10 23:24:43 +00:00
|
|
|
} else {
|
2008-04-06 14:54:14 +00:00
|
|
|
memset(output, 0, 4);
|
|
|
|
output += 4;
|
2008-01-10 23:24:43 +00:00
|
|
|
}
|
2008-04-06 14:54:14 +00:00
|
|
|
|
2008-01-10 23:24:43 +00:00
|
|
|
if (g->special == k2TownsFile1E)
|
|
|
|
ptr += 2;
|
|
|
|
|
|
|
|
} else if (cycle == 0) {
|
|
|
|
while (v != 1 && v != -2) {
|
|
|
|
ptr++;
|
|
|
|
v = extractHofSeqData_isSequence(ptr, g, endOffs - ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (v == -2)
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
} else if (cycle == 1) {
|
|
|
|
while (v == 1 && v != -2) {
|
|
|
|
ptr++;
|
|
|
|
v = extractHofSeqData_isSequence(ptr, g, endOffs - ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (v == -2)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16 finHeaderSize = (2 + numSequences + numNestedSequences) * sizeof(uint16);
|
|
|
|
uint16 finBufferSize = ((output - buffer) - headerSize) + finHeaderSize;
|
|
|
|
byte *finBuffer = new byte[finBufferSize];
|
|
|
|
assert(finBuffer);
|
|
|
|
uint16 diff = headerSize - finHeaderSize;
|
|
|
|
uint16 *finHeader = (uint16*) finBuffer;
|
2008-01-27 19:47:41 +00:00
|
|
|
|
2008-01-10 23:24:43 +00:00
|
|
|
for (int i = 1; i < finHeaderSize; i++)
|
2008-04-06 14:54:14 +00:00
|
|
|
WRITE_BE_UINT16(&finHeader[i], (READ_BE_UINT16(&header[i]) - diff));
|
|
|
|
WRITE_BE_UINT16(finHeader, numSequences);
|
|
|
|
WRITE_BE_UINT16(&finHeader[numSequences + 1], numNestedSequences);
|
2008-01-10 23:24:43 +00:00
|
|
|
memcpy (finBuffer + finHeaderSize, buffer + headerSize, finBufferSize - finHeaderSize);
|
2008-05-07 14:31:45 +00:00
|
|
|
delete[] buffer;
|
2008-01-10 23:24:43 +00:00
|
|
|
|
|
|
|
finHeader = (uint16*) (finBuffer + ((numSequences + 2) * sizeof(uint16)));
|
|
|
|
for (int i = 0; i < numNestedSequences; i++) {
|
2008-04-06 14:54:14 +00:00
|
|
|
uint8 * offs = finBuffer + READ_BE_UINT16(finHeader++) + 26;
|
|
|
|
uint16 ctrl = READ_BE_UINT16(offs);
|
2008-01-10 23:24:43 +00:00
|
|
|
if (ctrl)
|
|
|
|
ctrl -= diff;
|
2008-04-06 14:54:14 +00:00
|
|
|
WRITE_BE_UINT16(offs, ctrl);
|
2008-01-10 23:24:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return out.addFile(filename, finBuffer, finBufferSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
int extractHofSeqData_checkString(const void *ptr, uint8 checkSize) {
|
|
|
|
// return values: 1 = text; 0 = zero string; -1 = other
|
|
|
|
|
|
|
|
int t = 0;
|
|
|
|
int c = checkSize;
|
|
|
|
const uint8 *s = (const uint8*)ptr;
|
|
|
|
|
|
|
|
// check for character string
|
|
|
|
while (c--) {
|
|
|
|
if (*s > 31 && *s < 123)
|
|
|
|
t++;
|
2008-01-27 19:47:41 +00:00
|
|
|
s++;
|
2008-01-10 23:24:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (t == checkSize)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
// check for zero string
|
|
|
|
c = checkSize;
|
|
|
|
uint32 sum = 0;
|
|
|
|
s = (const uint8*)ptr;
|
|
|
|
while (c--)
|
|
|
|
sum += *s++;
|
|
|
|
|
|
|
|
return (sum) ? -1 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int extractHofSeqData_isSequence(const void *ptr, const Game *g, uint32 maxCheckSize) {
|
|
|
|
// return values: 1 = Sequence; 0 = Nested Sequence; -1 = other; -2 = overflow
|
|
|
|
|
|
|
|
if (maxCheckSize < 30)
|
|
|
|
return -2;
|
2008-01-27 19:47:41 +00:00
|
|
|
|
2008-01-10 23:24:43 +00:00
|
|
|
const uint8 * s = (const uint8*)ptr;
|
|
|
|
int c1 = extractHofSeqData_checkString(s + 2, 6);
|
|
|
|
int c2 = extractHofSeqData_checkString(s + 16, 6);
|
|
|
|
int c3 = extractHofSeqData_checkString(s + 2, 14);
|
|
|
|
int c4 = extractHofSeqData_checkString(s + 16, 14);
|
|
|
|
int c0 = s[1];
|
|
|
|
int c5 = s[0];
|
|
|
|
|
|
|
|
if (c0 == 0 && c5 && ((c1 + c2) >= 1) && (!(c3 == 0 && c2 != 1)) && (!(c4 == 0 && c1 != 1))) {
|
|
|
|
if (maxCheckSize < 41)
|
|
|
|
return -2;
|
|
|
|
|
|
|
|
if (g->special == k2TownsFile1E) {
|
|
|
|
if (!(s[37] | s[39]) && s[38] > s[36])
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
if (!(s[39] | s[41]) && s[40] > s[38])
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (c0 == 0 && c5 == 4 && c3 == 0 && c4 == 0) {
|
|
|
|
if (maxCheckSize >= 41 && READ_LE_UINT32(s + 34) && !(s[39] | s[41]) && s[40] > s[38])
|
|
|
|
return 1;
|
2008-01-27 19:47:41 +00:00
|
|
|
}
|
2008-01-10 23:24:43 +00:00
|
|
|
|
|
|
|
if (c0 == 0 && c5 && c1 == 1 && c4 == -1 && s[20])
|
|
|
|
return 0;
|
2008-01-27 19:47:41 +00:00
|
|
|
|
2008-01-10 23:24:43 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int extractHofSeqData_isControl(const void *ptr, uint32 size) {
|
|
|
|
// return values: 1 = possible frame control data; 0 = definitely not frame control data
|
|
|
|
|
|
|
|
const uint8 *s = (const uint8*)ptr;
|
2008-01-27 02:06:59 +00:00
|
|
|
for (uint32 i = 2; i < size; i += 4) {
|
2008-01-10 23:24:43 +00:00
|
|
|
if (!s[i])
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-01-27 02:06:59 +00:00
|
|
|
for (uint32 i = 1; i < size; i += 2) {
|
2008-01-10 23:24:43 +00:00
|
|
|
if (s[i])
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2008-04-06 14:54:14 +00:00
|
|
|
bool extractHofShapeAnimDataV1(PAKFile &out, const Game *g, const byte *data, const uint32 size, const char *filename, int fmtPatch) {
|
2008-05-12 13:35:58 +00:00
|
|
|
int outsize = 1;
|
|
|
|
uint8 *buffer = new uint8[size + 1];
|
2008-04-06 14:54:14 +00:00
|
|
|
const uint8 *src = data;
|
|
|
|
uint8 *dst = buffer + 1;
|
|
|
|
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
|
|
WRITE_BE_UINT16(dst, READ_LE_UINT16(src));
|
|
|
|
src += 2;
|
|
|
|
dst += 2;
|
|
|
|
WRITE_BE_UINT16(dst, READ_LE_UINT16(src));
|
|
|
|
src += 4;
|
|
|
|
dst += 2;
|
2008-05-12 13:35:58 +00:00
|
|
|
outsize += 4;
|
2008-04-06 14:54:14 +00:00
|
|
|
|
2008-04-10 15:05:59 +00:00
|
|
|
for (int j = 0; j < 20; j++) {
|
2008-04-06 14:54:14 +00:00
|
|
|
WRITE_BE_UINT16(dst, READ_LE_UINT16(src));
|
|
|
|
src += 2;
|
|
|
|
dst += 2;
|
2008-05-12 13:35:58 +00:00
|
|
|
outsize += 2;
|
2008-04-06 14:54:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
*buffer = 4; // number of items
|
|
|
|
|
|
|
|
return out.addFile(filename, buffer, outsize);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool extractHofShapeAnimDataV2(PAKFile &out, const Game *g, const byte *data, const uint32 size, const char *filename, int fmtPatch) {
|
2008-05-12 13:35:58 +00:00
|
|
|
int outsize = 1;
|
|
|
|
uint8 *buffer = new uint8[size + 1];
|
2008-04-06 14:54:14 +00:00
|
|
|
const uint8 *src = data;
|
|
|
|
uint8 *dst = buffer + 1;
|
2008-05-12 13:35:58 +00:00
|
|
|
const uint8 *fin = data + size;
|
2008-04-06 14:54:14 +00:00
|
|
|
int count = 0;
|
|
|
|
|
|
|
|
do {
|
|
|
|
if (READ_LE_UINT16(src) == 0xffff)
|
|
|
|
break;
|
|
|
|
|
|
|
|
count++;
|
|
|
|
|
|
|
|
WRITE_BE_UINT16(dst, READ_LE_UINT16(src));
|
|
|
|
src += 2;
|
|
|
|
dst += 2;
|
|
|
|
|
|
|
|
uint8 numFrames = *src;
|
|
|
|
*dst++ = numFrames;
|
|
|
|
src += 6;
|
2008-05-12 13:35:58 +00:00
|
|
|
outsize += 3;
|
2008-04-06 14:54:14 +00:00
|
|
|
|
|
|
|
for (int i = 0; i < (numFrames << 1); i++) {
|
|
|
|
WRITE_BE_UINT16(dst, READ_LE_UINT16(src));
|
|
|
|
src += 2;
|
|
|
|
dst += 2;
|
2008-05-12 13:35:58 +00:00
|
|
|
outsize += 2;
|
2008-04-06 14:54:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
src += (48 - (numFrames << 2));
|
|
|
|
|
2008-05-12 13:35:58 +00:00
|
|
|
} while (src < fin);
|
|
|
|
|
|
|
|
*buffer = count; // number of items
|
|
|
|
|
|
|
|
return out.addFile(filename, buffer, outsize);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool extractStringsWoSuffix(PAKFile &out, const Game *g, const byte *data, const uint32 size, const char *filename, int fmtPatch) {
|
|
|
|
int outsize = size + 4;
|
|
|
|
uint8 *buffer = new uint8[outsize];
|
|
|
|
const uint8 *src = data;
|
|
|
|
uint8 *dst = buffer + 4;
|
|
|
|
const uint8 *fin = src + size;
|
|
|
|
int entries = 0;
|
|
|
|
|
|
|
|
while (src < fin) {
|
|
|
|
while (!*src && src < fin)
|
|
|
|
src++;
|
|
|
|
while (*src && *src != '.' && src < fin)
|
|
|
|
*dst++ = *src++;
|
|
|
|
|
|
|
|
*dst++ = '\0';
|
|
|
|
entries++;
|
|
|
|
|
|
|
|
if (*src == '.') {
|
|
|
|
while (*src && src < fin)
|
|
|
|
src++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
WRITE_BE_UINT32(buffer, entries);
|
|
|
|
outsize = dst - buffer;
|
|
|
|
|
|
|
|
return out.addFile(filename, buffer, outsize);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool extractRaw16to8(PAKFile &out, const Game *g, const byte *data, const uint32 size, const char *filename, int fmtPatch) {
|
|
|
|
int outsize = size >> 1;
|
|
|
|
uint8 *buffer = new uint8[outsize];
|
|
|
|
const uint8 *src = data;
|
|
|
|
uint8 *dst = buffer;
|
|
|
|
|
|
|
|
for (int i = 0; i < outsize; i++) {
|
|
|
|
*dst++ = *src++;
|
|
|
|
*src++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return out.addFile(filename, buffer, outsize);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool extractMrShapeAnimData(PAKFile &out, const Game *g, const byte *data, const uint32 size, const char *filename, int fmtPatch) {
|
|
|
|
int outsize = 1;
|
|
|
|
uint8 *buffer = new uint8[size + 1];
|
|
|
|
const uint8 *src2 = data;
|
|
|
|
const uint8 *src1 = data + 324;
|
|
|
|
uint8 *dst = buffer + 1;
|
|
|
|
const uint8 *fin = data + size;
|
|
|
|
int count = 0;
|
|
|
|
|
|
|
|
do {
|
|
|
|
if (READ_LE_UINT16(src1) == 0xffff)
|
|
|
|
break;
|
|
|
|
|
|
|
|
count++;
|
|
|
|
|
|
|
|
WRITE_BE_UINT16(dst, READ_LE_UINT16(src1));
|
|
|
|
src1 += 2;
|
|
|
|
dst += 2;
|
|
|
|
|
|
|
|
uint8 numFrames = *src1;
|
|
|
|
*dst++ = numFrames;
|
|
|
|
src1 += 10;
|
|
|
|
outsize += 3;
|
|
|
|
|
|
|
|
for (int i = 0; i < (numFrames << 1); i++) {
|
|
|
|
WRITE_BE_UINT16(dst, READ_LE_UINT16(src2));
|
|
|
|
src2 += 2;
|
|
|
|
dst += 2;
|
|
|
|
outsize += 2;
|
|
|
|
}
|
|
|
|
} while (src1 < fin);
|
2008-04-06 14:54:14 +00:00
|
|
|
|
|
|
|
*buffer = count; // number of items
|
|
|
|
|
|
|
|
return out.addFile(filename, buffer, outsize);
|
|
|
|
}
|
|
|
|
|
2007-02-03 14:52:02 +00:00
|
|
|
// 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;
|
|
|
|
|
2008-05-12 13:35:58 +00:00
|
|
|
if (g->special == kTalkieVersion || g->special == k2CDFile1E || g->special == k2CDFile1F || g->special == k2CDFile1G || g->special == k2CDFile2E || g->special == k2CDFile2F || g->special == k2CDFile2G || g->game == kKyra3)
|
2007-02-03 14:52:02 +00:00
|
|
|
features |= GF_TALKIE;
|
2008-01-10 23:24:43 +00:00
|
|
|
else if (g->special == kDemoVersion || g->special == k2DemoVersion)
|
2007-02-03 14:52:02 +00:00
|
|
|
features |= GF_DEMO;
|
2008-01-10 23:24:43 +00:00
|
|
|
else if (g->special == kFMTownsVersionE || g->special == kFMTownsVersionJ ||
|
|
|
|
g->special == k2TownsFile1E || g->special == k2TownsFile1J ||
|
|
|
|
g->special == k2TownsFile2E || g->special == k2TownsFile2J)
|
2007-02-03 14:52:02 +00:00
|
|
|
features |= GF_FMTOWNS;
|
2007-03-12 20:42:28 +00:00
|
|
|
else if (g->special == kAmigaVersion)
|
|
|
|
features |= GF_AMIGA;
|
2007-02-03 14:52:02 +00:00
|
|
|
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;
|
2007-02-07 19:29:15 +00:00
|
|
|
else if (g->lang == JA_JPN)
|
|
|
|
features |= GF_JAPANESE;
|
2008-01-27 19:47:41 +00:00
|
|
|
|
2007-02-03 14:52:02 +00:00
|
|
|
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))
|
2008-01-27 19:47:41 +00:00
|
|
|
return false;
|
2007-02-03 14:52:02 +00:00
|
|
|
uint32 version = READ_BE_UINT32(s);
|
|
|
|
return (version == kKyraDatVersion);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool updateIndex(PAKFile &out, const Game *g) {
|
|
|
|
char filename[32];
|
2008-01-10 23:24:43 +00:00
|
|
|
createFilename(filename, g->game, -1, g->special, "INDEX");
|
2008-01-27 19:47:41 +00:00
|
|
|
|
2007-02-03 14:52:02 +00:00
|
|
|
byte *index = new byte[kIndexSize];
|
|
|
|
assert(index);
|
|
|
|
memset(index, 0, kIndexSize);
|
2008-01-27 19:47:41 +00:00
|
|
|
|
2007-02-03 14:52:02 +00:00
|
|
|
uint32 size = 0;
|
|
|
|
const uint8 *data = out.getFileData(filename, &size);
|
|
|
|
if (data)
|
|
|
|
memcpy(index, data, size);
|
2008-01-27 19:47:41 +00:00
|
|
|
|
2007-02-03 14:52:02 +00:00
|
|
|
if (!updateIndex(index, kIndexSize, g)) {
|
2008-05-07 14:31:45 +00:00
|
|
|
delete[] index;
|
2007-02-03 14:52:02 +00:00
|
|
|
return false;
|
|
|
|
}
|
2008-01-27 19:47:41 +00:00
|
|
|
|
2007-02-03 14:52:02 +00:00
|
|
|
out.removeFile(filename);
|
|
|
|
if (!out.addFile(filename, index, kIndexSize)) {
|
|
|
|
fprintf(stderr, "ERROR: couldn't update %s file", filename);
|
2008-05-07 14:31:45 +00:00
|
|
|
delete[] index;
|
2007-02-03 14:52:02 +00:00
|
|
|
return false;
|
|
|
|
}
|
2008-01-27 19:47:41 +00:00
|
|
|
|
2007-02-03 14:52:02 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool checkIndex(PAKFile &out, const Game *g) {
|
|
|
|
char filename[32];
|
2008-01-10 23:24:43 +00:00
|
|
|
createFilename(filename, g->game, -1, g->special, "INDEX");
|
2007-02-03 14:52:02 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2008-01-27 19:47:41 +00:00
|
|
|
|
2007-02-03 14:52:02 +00:00
|
|
|
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);
|
2008-01-27 19:47:41 +00:00
|
|
|
|
2007-02-03 14:52:02 +00:00
|
|
|
byte *buffer = new uint8[size];
|
|
|
|
assert(buffer);
|
2008-01-27 19:47:41 +00:00
|
|
|
|
2007-02-03 14:52:02 +00:00
|
|
|
if (fread(buffer, 1, size, input) != size) {
|
|
|
|
warning("couldn't read from file '%s', skipping it", argv[i]);
|
2008-05-07 14:31:45 +00:00
|
|
|
delete[] buffer;
|
2007-02-03 14:52:02 +00:00
|
|
|
fclose(input);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
fclose(input);
|
2008-01-27 19:47:41 +00:00
|
|
|
|
2007-02-03 14:52:02 +00:00
|
|
|
const Game *g = findGame(buffer, size);
|
|
|
|
if (!g) {
|
|
|
|
warning("skipping unknown file '%s'", argv[i]);
|
2008-05-07 14:31:45 +00:00
|
|
|
delete[] buffer;
|
2007-02-03 14:52:02 +00:00
|
|
|
continue;
|
|
|
|
}
|
2008-01-27 19:47:41 +00:00
|
|
|
|
2007-02-03 14:52:02 +00:00
|
|
|
if (!hasNeededEntries(g, &out)) {
|
|
|
|
warning("file '%s' is missing offset entries and thus can't be processed", argv[i]);
|
2008-05-07 14:31:45 +00:00
|
|
|
delete[] buffer;
|
2007-02-03 14:52:02 +00:00
|
|
|
continue;
|
|
|
|
}
|
2008-01-27 19:47:41 +00:00
|
|
|
|
2007-02-03 14:52:02 +00:00
|
|
|
if (!process(out, g, buffer, size))
|
|
|
|
fprintf(stderr, "ERROR: couldn't process file '%s'", argv[i]);
|
2008-01-27 19:47:41 +00:00
|
|
|
|
2008-01-10 23:24:43 +00:00
|
|
|
if (g->special == kFMTownsVersionE || g->special == k2TownsFile1E || g->special == k2TownsFile2E ||
|
|
|
|
g->special == k2CDFile1E || g->special == k2CDFile2E) {
|
|
|
|
// This is for executables which contain support for at least 2 languages
|
|
|
|
// The English and non language specific data has now been extracted.
|
|
|
|
// We switch to the second language and continue extraction.
|
|
|
|
if (!hasNeededEntries(++g, &out)) {
|
|
|
|
warning("file '%s' is missing offset entries and thus can't be processed", argv[i]);
|
2008-05-07 14:31:45 +00:00
|
|
|
delete[] buffer;
|
2008-01-10 23:24:43 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!process(out, g, buffer, size))
|
|
|
|
fprintf(stderr, "ERROR: couldn't process file '%s'", argv[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g->special == k2CDFile1F || g->special == k2CDFile2F) {
|
|
|
|
// This is for executables which contain support for 3 languages.
|
|
|
|
// We switch to the third language and continue extraction.
|
2007-02-07 19:29:15 +00:00
|
|
|
if (!hasNeededEntries(++g, &out)) {
|
|
|
|
warning("file '%s' is missing offset entries and thus can't be processed", argv[i]);
|
2008-05-07 14:31:45 +00:00
|
|
|
delete[] buffer;
|
2007-02-07 19:29:15 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!process(out, g, buffer, size))
|
|
|
|
fprintf(stderr, "ERROR: couldn't process file '%s'", argv[i]);
|
|
|
|
}
|
2008-01-27 19:47:41 +00:00
|
|
|
|
2008-05-07 14:31:45 +00:00
|
|
|
delete[] buffer;
|
2007-02-03 14:52:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2008-01-27 19:47:41 +00:00
|
|
|
|
2007-02-03 14:52:02 +00:00
|
|
|
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;
|
|
|
|
}
|
2008-01-27 19:47:41 +00:00
|
|
|
|
2007-02-03 14:52:02 +00:00
|
|
|
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);
|
2008-01-27 19:47:41 +00:00
|
|
|
|
2007-02-03 14:52:02 +00:00
|
|
|
if (!tDesc) {
|
|
|
|
fprintf(stderr, "ERROR: couldn't find type description for id %d\n", i->id);
|
|
|
|
return false;
|
|
|
|
}
|
2008-01-27 19:47:41 +00:00
|
|
|
|
2007-02-03 14:52:02 +00:00
|
|
|
PAKFile::cFileList *list = out.getFileList();
|
|
|
|
if (list && list->findEntry(filename) != 0)
|
|
|
|
continue;
|
2008-01-27 19:47:41 +00:00
|
|
|
|
2007-02-07 19:29:15 +00:00
|
|
|
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;
|
2008-01-10 23:24:43 +00:00
|
|
|
else if (i->id == kIntroStrings || i->id == kKyra1TownsSFXwdTable)
|
2008-01-27 19:47:41 +00:00
|
|
|
patch = 2;
|
2007-02-07 19:29:15 +00:00
|
|
|
}
|
2008-01-10 23:24:43 +00:00
|
|
|
|
|
|
|
if (g->special == k2TownsFile1E || g->special == k2TownsFile1J) {
|
|
|
|
if (i->id == k2SeqplayStrings)
|
|
|
|
patch = 3;
|
|
|
|
}
|
2008-01-27 19:47:41 +00:00
|
|
|
|
2008-01-10 23:24:43 +00:00
|
|
|
if (g->special == k2FloppyFile2) {
|
|
|
|
if (i->id == k2IngamePakFiles)
|
|
|
|
patch = 4;
|
|
|
|
}
|
2008-01-27 02:06:59 +00:00
|
|
|
|
|
|
|
if (g->special == k2FloppyFile2 || g->special == k2CDFile2E) {
|
|
|
|
if (i->id == k2IngameSfxFiles)
|
|
|
|
patch = 5;
|
2008-01-27 19:47:41 +00:00
|
|
|
}
|
|
|
|
|
2007-02-07 19:29:15 +00:00
|
|
|
if (!tDesc->extract(out, g, data + i->startOff, i->endOff - i->startOff, filename, patch)) {
|
2007-02-03 14:52:02 +00:00
|
|
|
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,
|
2007-02-07 19:29:15 +00:00
|
|
|
kyra1TownsGames,
|
2007-03-12 20:42:28 +00:00
|
|
|
kyra1AmigaGames,
|
2007-07-10 13:03:11 +00:00
|
|
|
kyra1FanTranslations,
|
2008-01-10 23:24:43 +00:00
|
|
|
|
|
|
|
kyra2FloppyGames,
|
|
|
|
kyra2TalkieGames,
|
|
|
|
kyra2TownsGames,
|
|
|
|
kyra2Demos,
|
|
|
|
|
2008-05-12 13:35:58 +00:00
|
|
|
kyra3Games,
|
|
|
|
|
2007-02-03 14:52:02 +00:00
|
|
|
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);
|
2008-01-27 19:47:41 +00:00
|
|
|
|
2007-02-03 14:52:02 +00:00
|
|
|
for (int j = 0; j < 16; ++j) {
|
|
|
|
sprintf(md5str + j*2, "%02x", (int)digest[j]);
|
|
|
|
}
|
2008-01-27 19:47:41 +00:00
|
|
|
|
2007-02-03 14:52:02 +00:00
|
|
|
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;
|
|
|
|
}
|