/* ScummVM - Graphic Adventure Engine
*
* 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 3 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, see .
*
*/
#include "file.h"
#include "cif.h"
#include "tvd_data.h"
#include "nancy1_data.h"
#include "nancy2_data.h"
#include "nancy3_data.h"
#include "nancy4_data.h"
#include "nancy5_data.h"
#include "nancy6_data.h"
#include "nancy7_data.h"
#include "nancy8_data.h"
#define NANCYDAT_MAJOR_VERSION 1
#define NANCYDAT_MINOR_VERSION 1
#define NANCYDAT_NUM_GAMES 9
/**
* Format specifications for nancy.dat:
* 4 bytes Magic string 'NNCY'
* 1 byte Major version number
* 1 byte Minor version number
* 2 bytes Number of games (ignoring multiple languages)
* 4 bytes per game File offsets for every game's data
* Rest of file Game data
*
* Game data contents:
* Various data sections, depending on title;
* e.g.: only nancy1 has a hint section, since later
* titles abandoned the dedicated hint system.
* Each section has the following structure:
* 4 bytes Offset to next section
* 4 bytes Section tag (generated using MKTAG macro)
* variable Section data
*
* Arrays in the game data are variable-size.
* All arrays are preceded by a 2-byte size property.
* 2D arrays with strings (e.g conditional dialogue) are also preceded
* by a list of 4-byte offsets (one per language).
* All offsets are absolute (relative to start of file).
* All data is little endian.
*
* Game order:
* The Vampire Diaries
* Nancy Drew: Secrets Can Kill
* Nancy Drew: Stay Tuned for Danger
* Nancy Drew: Message in a Haunted Mansion
* Nancy Drew: Treasure in the Royal Tower
* Nancy Drew: The Final Scene
* Nancy Drew: Secret of the Scarlet Hand
* Nancy Drew: Ghost Dogs of Moon Lake
* Nancy Drew: The Haunted Carousel
*/
// Add the offset to the next tagged section before the section itself for easier navigation
#define WRAPWITHOFFSET(x) beginOffset = output.pos();\
output.skip(4);\
x;\
endOffset = output.pos();\
output.seek(beginOffset);\
output.writeUint32(endOffset);\
output.seek(endOffset);
void NORETURN_PRE error(const char *s, ...) {
printf("%s\n", s);
exit(1);
}
const uint32 _mapAccessTag = MKTAG('M', 'A', 'P', 'A');
void writeConstants(File &output, const GameConstants &gameConstants) {
output.writeUint32(MKTAG('C', 'O', 'N', 'S'));
output.writeUint16(gameConstants.numItems);
output.writeUint16(gameConstants.numEventFlags);
writeToFile(output, gameConstants.genericEventFlags);
output.writeUint16(gameConstants.numCursorTypes);
output.writeUint32(gameConstants.logoEndAfter);
output.writeUint16(gameConstants.wonGameFlagID);
}
void writeSoundChannels(File &output, const SoundChannelInfo &soundChannelInfo) {
output.writeUint32(MKTAG('S', 'C', 'H', 'N'));
output.writeByte(soundChannelInfo.numChannels);
output.writeByte(soundChannelInfo.numSceneSpecificChannels);
writeToFile(output, soundChannelInfo.speechChannels);
writeToFile(output, soundChannelInfo.musicChannels);
writeToFile(output, soundChannelInfo.sfxChannels);
}
void writeLanguages(File &output, const Common::Array &languages) {
output.writeUint32(MKTAG('L', 'A', 'N', '2'));
writeToFile(output, languages);
}
void writeConditionalDialogue(File &output, const Common::Array> &conditionalDialogue, const Common::Array> &dialogueTexts) {
output.writeUint32(MKTAG('C', 'D', 'L', 'G'));
writeToFile(output, conditionalDialogue);
writeMultilangArray(output, dialogueTexts);
}
// Version without text array, used in nancy6 and up
void writeConditionalDialogue(File &output, const Common::Array> &conditionalDialogue) {
output.writeUint32(MKTAG('C', 'D', 'L', '2'));
writeToFile(output, conditionalDialogue);
}
void writeGoodbyes(File &output, const Common::Array &goodbyes, const Common::Array> &goodbyeTexts) {
output.writeUint32(MKTAG('G', 'D', 'B', 'Y'));
writeToFile(output, goodbyes);
writeMultilangArray(output, goodbyeTexts);
}
// Version without text array, used in nancy6 and up
void writeGoodbyes(File &output, const Common::Array &goodbyes) {
output.writeUint32(MKTAG('G', 'D', 'B', '2'));
writeToFile(output, goodbyes);
}
void writeHints(File &output, const Common::Array> &hints, const SceneChangeDescription &hintSceneChange, const Common::Array> &hintTexts) {
output.writeUint32(MKTAG('H', 'I', 'N', 'T'));
writeToFile(output, hintSceneChange);
writeToFile(output, hints);
writeMultilangArray(output, hintTexts);
}
void writeRingingTexts(File &output, const Common::Array &ringingTexts) {
output.writeUint32(MKTAG('R', 'I', 'N', 'G'));
writeToFile(output, ringingTexts);
}
void writeEmptySaveTexts(File &output, const Common::Array &emptySaveTexts) {
output.writeUint32(MKTAG('E', 'S', 'A', 'V'));
writeToFile(output, emptySaveTexts);
}
void writeEventFlagNames(File &output, const Common::Array &eventFlagNames) {
output.writeUint32(MKTAG('E', 'F', 'L', 'G'));
writeToFile(output, eventFlagNames);
}
void writePatchFile(File &output, uint gameVersion, Common::Array filenames, const char *folderName) {
uint32 size;
byte *b = createCifTree(gameVersion, filenames, folderName, size);
if (!b) {
return;
}
output.writeUint32(MKTAG('P', 'A', 'T', 'C'));
output.write(b, size);
delete[] b;
}
void writePatchAssociations(File &output, const Common::Array &patchAssociations) {
output.writeUint32(MKTAG('P', 'A', 'S', 'S'));
writeToFile(output, patchAssociations);
}
int main(int argc, char *argv[]) {
File output;
if (!output.open("nancy.dat", kFileWriteMode)) {
error("Unable to open nancy.dat");
}
Common::Array gameOffsets;
uint32 beginOffset, endOffset;
// Write header
output.writeByte('N');
output.writeByte('N');
output.writeByte('C');
output.writeByte('Y');
output.writeByte(NANCYDAT_MAJOR_VERSION);
output.writeByte(NANCYDAT_MINOR_VERSION);
output.writeUint16(NANCYDAT_NUM_GAMES);
// Skip game offsets, they'll be written at the end
uint32 offsetsOffset = output.pos();
output.skip(NANCYDAT_NUM_GAMES * 4);
// The Vampire Diaries data
gameOffsets.push_back(output.pos());
WRAPWITHOFFSET(writeConstants(output, _tvdConstants))
WRAPWITHOFFSET(output.writeUint32(_mapAccessTag); writeToFile(output, _tvdMapAccessSceneIDs));
WRAPWITHOFFSET(writeSoundChannels(output, _tvdToNancy2SoundChannelInfo))
WRAPWITHOFFSET(writeLanguages(output, _tvdLanguagesOrder))
WRAPWITHOFFSET(writeConditionalDialogue(output, _tvdConditionalDialogue, _tvdConditionalDialogueTexts))
WRAPWITHOFFSET(writeGoodbyes(output, _tvdGoodbyes, _tvdGoodbyeTexts))
WRAPWITHOFFSET(writeEmptySaveTexts(output, _tvdEmptySaveStrings))
WRAPWITHOFFSET(writeEventFlagNames(output, _tvdEventFlagNames))
// Nancy Drew: Secrets Can Kill data
gameOffsets.push_back(output.pos());
WRAPWITHOFFSET(writeConstants(output, _nancy1Constants))
WRAPWITHOFFSET(output.writeUint32(_mapAccessTag); writeToFile(output, _nancy1MapAccessSceneIDs));
WRAPWITHOFFSET(writeSoundChannels(output, _tvdToNancy2SoundChannelInfo))
WRAPWITHOFFSET(writeLanguages(output, _nancy1LanguagesOrder))
WRAPWITHOFFSET(writeConditionalDialogue(output, _nancy1ConditionalDialogue, _nancy1ConditionalDialogueTexts))
WRAPWITHOFFSET(writeGoodbyes(output, _nancy1Goodbyes, _nancy1GoodbyeTexts))
WRAPWITHOFFSET(writeHints(output, _nancy1Hints, _nancy1HintSceneChange, _nancy1HintTexts))
WRAPWITHOFFSET(writeRingingTexts(output, _nancy1TelephoneRinging))
WRAPWITHOFFSET(writeEmptySaveTexts(output, _nancy1EmptySaveStrings))
WRAPWITHOFFSET(writeEventFlagNames(output, _nancy1EventFlagNames))
// Nancy Drew: Stay Tuned for Danger data
gameOffsets.push_back(output.pos());
WRAPWITHOFFSET(writeConstants(output, _nancy2Constants))
WRAPWITHOFFSET(writeSoundChannels(output, _tvdToNancy2SoundChannelInfo))
WRAPWITHOFFSET(writeLanguages(output, _nancy2LanguagesOrder))
WRAPWITHOFFSET(writeConditionalDialogue(output, _nancy2ConditionalDialogue, _nancy2ConditionalDialogueTexts))
WRAPWITHOFFSET(writeGoodbyes(output, _nancy2Goodbyes, _nancy2GoodbyeTexts))
WRAPWITHOFFSET(writeRingingTexts(output, _nancy2TelephoneRinging))
WRAPWITHOFFSET(writeEmptySaveTexts(output, _nancy2EmptySaveStrings))
WRAPWITHOFFSET(writeEventFlagNames(output, _nancy2EventFlagNames))
WRAPWITHOFFSET(writePatchFile(output, 3, nancy2PatchSrcFiles, "files/nancy2"))
WRAPWITHOFFSET(writePatchAssociations(output, nancy2PatchAssociations))
// Nancy Drew: Message in a Haunted Mansion data
gameOffsets.push_back(output.pos());
WRAPWITHOFFSET(writeConstants(output, _nancy3Constants))
WRAPWITHOFFSET(writeSoundChannels(output, _nancy3andUpSoundChannelInfo))
WRAPWITHOFFSET(writeLanguages(output, _nancy3LanguagesOrder))
WRAPWITHOFFSET(writeConditionalDialogue(output, _nancy3ConditionalDialogue, _nancy3ConditionalDialogueTexts))
WRAPWITHOFFSET(writeGoodbyes(output, _nancy3Goodbyes, _nancy3GoodbyeTexts))
WRAPWITHOFFSET(writeRingingTexts(output, _nancy3TelephoneRinging))
WRAPWITHOFFSET(writeEmptySaveTexts(output, _nancy3EmptySaveStrings))
WRAPWITHOFFSET(writeEventFlagNames(output, _nancy3EventFlagNames))
WRAPWITHOFFSET(writePatchFile(output, 4, nancy3PatchSrcFiles, "files/nancy3"))
WRAPWITHOFFSET(writePatchAssociations(output, nancy3PatchAssociations))
// Nancy Drew: Treasure in the Royal Tower data
gameOffsets.push_back(output.pos());
WRAPWITHOFFSET(writeConstants(output, _nancy4Constants))
WRAPWITHOFFSET(writeSoundChannels(output, _nancy3andUpSoundChannelInfo))
WRAPWITHOFFSET(writeLanguages(output, _nancy4LanguagesOrder))
WRAPWITHOFFSET(writeConditionalDialogue(output, _nancy4ConditionalDialogue, _nancy4ConditionalDialogueTexts))
WRAPWITHOFFSET(writeGoodbyes(output, _nancy4Goodbyes, _nancy4GoodbyeTexts))
WRAPWITHOFFSET(writeRingingTexts(output, _nancy4TelephoneRinging))
WRAPWITHOFFSET(writeEmptySaveTexts(output, _nancy4EmptySaveStrings))
WRAPWITHOFFSET(writeEventFlagNames(output, _nancy4EventFlagNames))
// Nancy Drew: The Final Scene data
gameOffsets.push_back(output.pos());
WRAPWITHOFFSET(writeConstants(output, _nancy5Constants))
WRAPWITHOFFSET(writeSoundChannels(output, _nancy3andUpSoundChannelInfo))
WRAPWITHOFFSET(writeLanguages(output, _nancy5LanguagesOrder))
WRAPWITHOFFSET(writeConditionalDialogue(output, _nancy5ConditionalDialogue, _nancy5ConditionalDialogueTexts))
WRAPWITHOFFSET(writeGoodbyes(output, _nancy5Goodbyes, _nancy5GoodbyeTexts))
WRAPWITHOFFSET(writeRingingTexts(output, _nancy5TelephoneRinging))
WRAPWITHOFFSET(writeEmptySaveTexts(output, _nancy5EmptySaveStrings))
WRAPWITHOFFSET(writeEventFlagNames(output, _nancy5EventFlagNames))
WRAPWITHOFFSET(writePatchFile(output, 6, nancy5PatchSrcFiles, "files/nancy5"))
WRAPWITHOFFSET(writePatchAssociations(output, nancy5PatchAssociations))
// Nancy Drew: Secret of the Scarlet Hand
gameOffsets.push_back(output.pos());
WRAPWITHOFFSET(writeConstants(output, _nancy6Constants))
WRAPWITHOFFSET(writeSoundChannels(output, _nancy3andUpSoundChannelInfo))
WRAPWITHOFFSET(writeLanguages(output, _nancy6LanguagesOrder))
WRAPWITHOFFSET(writeConditionalDialogue(output, _nancy6ConditionalDialogue))
WRAPWITHOFFSET(writeGoodbyes(output, _nancy6Goodbyes))
WRAPWITHOFFSET(writeRingingTexts(output, _nancy6TelephoneRinging))
WRAPWITHOFFSET(writeEmptySaveTexts(output, _nancy6EmptySaveStrings))
WRAPWITHOFFSET(writeEventFlagNames(output, _nancy6EventFlagNames))
WRAPWITHOFFSET(writePatchFile(output, 7, nancy6PatchSrcFiles, "files/nancy6"))
WRAPWITHOFFSET(writePatchAssociations(output, nancy6PatchAssociations))
// Nancy Drew: Ghost Dogs of Moon Lake
gameOffsets.push_back(output.pos());
WRAPWITHOFFSET(writeConstants(output, _nancy7Constants))
WRAPWITHOFFSET(writeSoundChannels(output, _nancy3andUpSoundChannelInfo))
WRAPWITHOFFSET(writeLanguages(output, _nancy7LanguagesOrder))
WRAPWITHOFFSET(writeConditionalDialogue(output, _nancy7ConditionalDialogue))
WRAPWITHOFFSET(writeGoodbyes(output, _nancy7Goodbyes))
WRAPWITHOFFSET(writeRingingTexts(output, _nancy7TelephoneRinging))
WRAPWITHOFFSET(writeEventFlagNames(output, _nancy7EventFlagNames))
WRAPWITHOFFSET(writePatchFile(output, 8, nancy7PatchSrcFiles, "files/nancy7"))
WRAPWITHOFFSET(writePatchAssociations(output, nancy7PatchAssociations))
// Nancy Drew: The Haunted Carousel
gameOffsets.push_back(output.pos());
WRAPWITHOFFSET(writeConstants(output, _nancy8Constants))
WRAPWITHOFFSET(writeSoundChannels(output, _nancy3andUpSoundChannelInfo))
WRAPWITHOFFSET(writeLanguages(output, _nancy8LanguagesOrder))
WRAPWITHOFFSET(writeConditionalDialogue(output, _nancy8ConditionalDialogue))
WRAPWITHOFFSET(writeGoodbyes(output, _nancy8Goodbyes))
WRAPWITHOFFSET(writeRingingTexts(output, _nancy8TelephoneRinging))
WRAPWITHOFFSET(writeEventFlagNames(output, _nancy8EventFlagNames))
// Write the offsets for each game in the header
output.seek(offsetsOffset);
for (uint i = 0; i < gameOffsets.size(); ++i) {
output.writeUint32(gameOffsets[i]);
}
output.close();
return 0;
}