/* 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 . * */ // Disable symbol overrides so that we can use system headers. #define FORBIDDEN_SYMBOL_ALLOW_ALL #include #include #include #include "util.h" #include "create_teenagent.h" #include "static_tables.h" void writeStringsBlock(FILE *fd, const char **stringArr, uint size) { for (uint i = 0; i < size; i++) { for (uint j = 0; j < strlen(stringArr[i]); j++) { if (stringArr[i][j] == '\n') writeByte(fd, '\0'); else writeByte(fd, stringArr[i][j]); } writeByte(fd, '\0'); writeByte(fd, '\0'); } } void writeCombinations(FILE *fd, Common::Language language) { const char **combineMessages = englishCombineMessages; if (language == CS_CZE) combineMessages = czechCombineMessages; else if (language == PL_POL) combineMessages = polishCombineMessages; else if (language == RU_RUS) combineMessages = russianCombineMessages; for (uint i = 0; i < kNumCombinations; i++) { combiningTable[i].write(fd); for (uint j = 0; j < strlen(combineMessages[i]); j++) { if (combineMessages[i][j] == '\n') writeByte(fd, '\0'); else writeByte(fd, combineMessages[i][j]); } writeByte(fd, '\0'); writeByte(fd, '\0'); } } void writeDialogStacks(FILE *fd, Common::Language language) { const char ***dialogs = englishDialogs; if (language == CS_CZE) dialogs = czechDialogs; else if (language == PL_POL) dialogs = polishDialogs; else if (language == RU_RUS) dialogs = russianDialogs; uint16 offset = 0; uint16 dialogOffsets[kNumDialogs]; for (uint i = 0; i < kNumDialogs; i++) { dialogOffsets[i] = offset; bool dialogEnd = false; uint j = 0; while (!dialogEnd) { offset += strlen(dialogs[i][j]); if (strcmp(dialogs[i][j], END_DIALOG) == 0) dialogEnd = true; j++; } } for (uint i = 0; i < sizeof(dialogStacks) / sizeof(uint16); i++) { if (dialogStacks[i] != 0xffff) { if (i == 0) { // skip ANIM_WAIT (0xff) byte writeUint16LE(fd, dialogOffsets[dialogStacks[i]] + 1); } else if (i == 190) { // There are two extra null bytes // in at the beginning of this dialog. // Skip them. writeUint16LE(fd, dialogOffsets[dialogStacks[i]] + 2); } else { writeUint16LE(fd, dialogOffsets[dialogStacks[i]]); } } else { writeUint16LE(fd, 0xffff); } } } void writeDialogs(FILE *fd, Common::Language language) { const char ***dialogs = englishDialogs; if (language == CS_CZE) dialogs = czechDialogs; else if (language == PL_POL) dialogs = polishDialogs; else if (language == RU_RUS) dialogs = russianDialogs; // Write out dialog string block static const char nulls[6] = "\0\0\0\0\0"; for (uint i = 0; i < kNumDialogs; i++) { //printf("Writing Dialog #%d\n", i); bool dialogEnd = false; uint j = 0; while (!dialogEnd) { uint nullCount = 0; if (strcmp(dialogs[i][j], NEW_LINE) == 0) { nullCount = 1; } else if (strcmp(dialogs[i][j], DISPLAY_MESSAGE) == 0) { nullCount = 2; } else if (strcmp(dialogs[i][j], CHANGE_CHARACTER) == 0) { nullCount = 3; } else if (strcmp(dialogs[i][j], END_DIALOG) == 0) { nullCount = 4; dialogEnd = true; } else { // Deals with normal dialogue and ANIM_WAIT cases if (fwrite(dialogs[i][j], 1, strlen(dialogs[i][j]), fd) != strlen(dialogs[i][j])) { perror("Writing dialog string"); exit(1); } } if (nullCount != 0 && nullCount < 5) { if (fwrite(nulls, 1, nullCount, fd) != nullCount) { perror("Writing dialog string nulls"); exit(1); } } j++; } } } void writeItems(FILE *fd, Common::Language language) { const char ***items = englishItems; if (language == CS_CZE) items = czechItems; else if (language == PL_POL) items = polishItems; else if (language == RU_RUS) items = russianItems; const uint kNumInventoryItems = 92; for (uint i = 0; i < kNumInventoryItems; i++) { // Write item id writeByte(fd, i + 1); // Write animated flag if (i == 6 || i == 13 || i == 47 || i == 49 || i == 67 || i == 91) writeByte(fd, 0x01); else writeByte(fd, 0x00); // Write name and description (if exists) of an item uint j = 0; bool endItem = false; while (!endItem) { if (strcmp(items[i][j], "\n") == 0) { // Separator between name and description writeByte(fd, '\0'); } else if (strcmp(items[i][j], "\n\n") == 0) { writeByte(fd, '\0'); writeByte(fd, '\0'); endItem = true; } else { if (fwrite(items[i][j], 1, strlen(items[i][j]), fd) != strlen(items[i][j])) { perror("Writing item string"); exit(1); } } j++; } } } void writeSceneObjects(FILE *fd, Common::Language language) { Common::Array> *objNamesDescs = &englishSceneObjectNamesDescs; SettableObjectName *settableSceneObjects = englishSettableObjectNames; if (language == CS_CZE) { objNamesDescs = &czechSceneObjectNamesDescs; settableSceneObjects = czechSettableObjectNames; } else if (language == PL_POL) { objNamesDescs = &polishSceneObjectNamesDescs; settableSceneObjects = polishSettableObjectNames; } else if (language == RU_RUS) { objNamesDescs = &russianSceneObjectNamesDescs; settableSceneObjects = russianSettableObjectNames; } uint sceneObjTableAddrsPos = ftell(fd); uint16 sceneObjTableAddrs[42]{}; uint16 curOffset = 0; for (uint i = 0; i < sceneObjects.size(); i++) writeUint16LE(fd, 0); curOffset += 84; // 2 bytes * 42 scenes for (uint i = 0; i < sceneObjects.size(); i++) { sceneObjTableAddrs[i] = curOffset; uint firstObjsAddrFilePos = ftell(fd); Common::Array sceneObjAddrs(sceneObjects[i].size(), 0); // Add blank object to the end sceneObjAddrs.push_back(0); for (uint16 addr : sceneObjAddrs) writeUint16LE(fd, addr); curOffset += sizeof(uint16) * sceneObjAddrs.size(); for (uint j = 0; j < sceneObjects[i].size(); j++) { sceneObjAddrs[j] = curOffset; // Write the object data sceneObjects[i][j].write(fd); curOffset += 19; // Name const char *name = (*objNamesDescs)[i][j]._name; for (uint k = 0; k < strlen(name); k++) { if (name[k] == '\n') writeByte(fd, '\0'); else writeByte(fd, name[k]); } bool nameIsSettable = false; const char *setName = nullptr; for (byte k = 0; k < 4; k++) { if (strcmp(name, settableSceneObjects[k]._initialName) == 0) { nameIsSettable = true; setName = settableSceneObjects[k]._setName; if (strlen(setName) > strlen(settableSceneObjects[k]._initialName)) { uint nameLengthDiff = strlen(setName) - strlen(settableSceneObjects[k]._initialName); for (uint c = 0; c < nameLengthDiff; c++) { writeByte(fd, '\0'); curOffset++; } } break; } } writeByte(fd, '\0'); curOffset += strlen(name) + 1; // Description (if exists) const char *description = (*objNamesDescs)[i][j]._description; if (strlen(description) == 0) { writeByte(fd, '\0'); writeByte(fd, '\0'); curOffset += 2; } else if (strcmp(description, "\001") == 0) { writeByte(fd, '\001'); curOffset++; } else { for (uint k = 0; k < strlen(description); k++) { if (description[k] == '\n') writeByte(fd, '\0'); else writeByte(fd, description[k]); } writeByte(fd, '\0'); writeByte(fd, '\0'); curOffset += strlen(description); curOffset += 2; } if (nameIsSettable) { fwrite(setName, 1, strlen(setName), fd); writeByte(fd, '\0'); writeByte(fd, 0xFF); curOffset += strlen(setName) + 2; } } uint pos = ftell(fd); fseek(fd, firstObjsAddrFilePos, SEEK_SET); fwrite(sceneObjAddrs.data(), sizeof(uint16), sceneObjAddrs.size(), fd); fseek(fd, pos, SEEK_SET); } uint pos = ftell(fd); fseek(fd, sceneObjTableAddrsPos, SEEK_SET); for (uint i = 0; i < sceneObjects.size(); i++) { writeUint16LE(fd, sceneObjTableAddrs[i]); } fseek(fd, pos, SEEK_SET); } uint32 writeResource(FILE *fd, ResourceType resType, Common::Language language) { uint prevFilePos = ftell(fd); switch (resType) { case kResCredits: { const char **credits = englishCredits; if (language == CS_CZE) credits = czechCredits; else if (language == PL_POL) credits = polishCredits; else if (language == RU_RUS) credits = russianCredits; writeStringsBlock(fd, credits, kNumCredits); break; } case kResDialogStacks: writeDialogStacks(fd, language); break; case kResDialogs: writeDialogs(fd, language); break; case kResItems: writeItems(fd, language); break; case kResSceneObjects: writeSceneObjects(fd, language); break; case kResMessages: { const char **messages = englishMessages; if (language == CS_CZE) messages = czechMessages; else if (language == PL_POL) messages = polishMessages; else if (language == RU_RUS) messages = russianMessages; writeStringsBlock(fd, messages, kNumMessages); break; } case kResCombinations: writeCombinations(fd, language); break; }; uint currentFilePos = ftell(fd); uint32 resourceSize = currentFilePos - prevFilePos; return resourceSize; } int main(int argc, char *argv[]) { const char *dat_name = "teenagent.dat"; FILE *fout = fopen(dat_name, "wb"); if (fout == nullptr) { perror("opening output file"); exit(1); } // Write header fwrite("TEENAGENT", 9, 1, fout); writeByte(fout, TEENAGENT_DAT_VERSION); if (fwrite(cseg, CSEG_SIZE, 1, fout) != 1) { perror("Writing code segment"); exit(1); } if (fwrite(dsegStartBlock, DSEG_STARTBLK_SIZE, 1, fout) != 1) { perror("Writing data segment start block"); exit(1); } // Skip messages block // It is written as a resource after the data segment uint msgBlockSize = 11415; // The size of messages block in the English exe fseek(fout, msgBlockSize, SEEK_CUR); if (fwrite(dsegEndBlock, DSEG_ENDBLK_SIZE, 1, fout) != 1) { perror("Writing data segment end block"); exit(1); } uint32 languageOffset = ftell(fout); for (uint lang = 0; lang < NUM_LANGS; lang++) { // Write language ID writeByte(fout, supportedLanguages[lang]); writeUint32LE(fout, 0); } writeByte(fout, (byte)Common::Language::UNK_LANG); for (uint lang = 0; lang < NUM_LANGS; lang++) { // Write offset to data uint32 dataOffset = ftell(fout); fseek(fout, languageOffset + (lang * 5 + 1), SEEK_SET); writeUint32LE(fout, dataOffset); fseek(fout, dataOffset, SEEK_SET); ResourceInfo resourceInfos[NUM_RESOURCES]; uint32 resInfoPos = ftell(fout); fseek(fout, (2 * sizeof(uint32) + sizeof(byte)) * NUM_RESOURCES, SEEK_CUR); for (uint i = 0; i < NUM_RESOURCES; i++) { resourceInfos[i]._id = i; resourceInfos[i]._offset = ftell(fout); uint32 size = writeResource(fout, ResourceType(i), supportedLanguages[lang]); resourceInfos[i]._size = size; } fseek(fout, resInfoPos, SEEK_SET); for (uint i = 0; i < NUM_RESOURCES; i++) { writeByte(fout, resourceInfos[i]._id); if (resourceInfos[i]._id != 0) { // Offsets are stored relative to first resource's offset // NOTE: First resource is kResDialogs(1), not kResDialogStacks(0) // because kResDialogStacks is not stored with the rest of resources. writeUint32LE(fout, resourceInfos[i]._offset - resourceInfos[1]._offset); } else writeUint32LE(fout, resourceInfos[i]._offset); writeUint32LE(fout, resourceInfos[i]._size); } // Go back to current file pos fseek(fout, resourceInfos[NUM_RESOURCES - 1]._offset + resourceInfos[NUM_RESOURCES - 1]._size, SEEK_SET); } fclose(fout); return 0; }