scummvm/devtools/skycpt/cptcompiler.cpp
2014-02-18 02:39:33 +01:00

564 lines
16 KiB
C++

/* 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 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.
*
*/
#include "stdafx.h"
#include "cpthelp.h"
#include "TextFile.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
uint32 crop(char *line);
uint16 findCptId(char *name, TextFile *cptFile);
#define MAX_CPTS 0xA000
#define MAX_OBJ_SIZE (0x2000 * 2)
#define NUM_DATA_LISTS 9
#define ASCII_SIZE (65536 * 2)
enum CptType {
PTR_NULL = 0,
COMPACT,
TURNTAB,
ANIMSEQ,
MISCBIN,
GETTOTAB,
ROUTEBUF,
MAINLIST
};
void processMainLists(FILE *inf, CptObj *destArr, uint16 *idList) {
char line[1024];
dofgets(line, 1024, inf);
assert(lineMatchSection(line, "MAINLISTS"));
uint16 *resBuf = (uint16 *)malloc(MAX_OBJ_SIZE);
uint32 idNum = 0;
do {
dofgets(line, 1024, inf);
if (!isEndOfSection(line)) {
char cptName[50];
uint16 id = getInfo(line, "MAINLST", cptName);
CptObj *dest = destArr + id;
assertEmpty(dest);
dest->type = MAINLIST;
dest->dbgName = (char *)malloc(strlen(cptName) + 1);
strcpy(dest->dbgName, cptName);
memset(resBuf, 0, MAX_OBJ_SIZE);
uint32 resPos = 0;
idList[idNum] = id;
idNum++;
do {
dofgets(line, 1024, inf);
if (!isEndOfObject(line, "MAINLST", id)) {
assert((line[0] == '\t') && (line[1] == '\t'));
char *stopCh;
uint16 destId = (uint16)strtoul(line + 2, &stopCh, 16);
assert(stopCh == (line + 6));
assert((stopCh[0] == ':') && (stopCh[1] == ':'));
resBuf[resPos] = destId;
resPos++;
} else
break;
} while (1);
assert(resPos < (MAX_OBJ_SIZE / 2));
dest->len = resPos;
dest->data = (uint16 *)malloc(resPos * 2);
memcpy(dest->data, resBuf, resPos * 2);
} else
break;
} while (1);
free(resBuf);
}
void processCpts(FILE *inf, CptObj *destArr) {
char line[1024];
dofgets(line, 1024, inf);
assert(lineMatchSection(line, "COMPACTS"));
uint16 *resBuf = (uint16 *)malloc(MAX_OBJ_SIZE);
do {
dofgets(line, 1024, inf);
if (!isEndOfSection(line)) {
char cptName[50];
uint16 id = getInfo(line, "COMPACT", cptName);
CptObj *dest = destArr + id;
assertEmpty(dest);
dest->dbgName = (char *)malloc(strlen(cptName) + 1);
dest->type = COMPACT;
strcpy(dest->dbgName, cptName);
memset(resBuf, 0, MAX_OBJ_SIZE);
uint32 resPos = 0;
do {
dofgets(line, 1024, inf);
if (!isEndOfObject(line, "COMPACT", id)) {
assert((line[0] == '\t') && (line[1] == '\t'));
char *stopCh;
uint16 destId = (uint16)strtoul(line + 2, &stopCh, 16);
assert(stopCh != (line + 2));
assert((stopCh[0] == '-') && (stopCh[1] == '>'));
if (resPos == 23) { // grafixProg
assert(destId == 0);
resBuf[resPos] = resBuf[resPos + 1] = 0;
resPos += 2;
} else if (resPos == 48) { // turnProg. shouldn't it be 49?
assert(destId == 0);
resBuf[resPos] = resBuf[resPos + 1] = 0;
resPos += 2;
} else {
resBuf[resPos] = destId;
resPos++;
}
} else
break;
} while (1);
assert(resPos < (MAX_OBJ_SIZE / 2));
dest->len = resPos;
dest->data = (uint16 *)malloc(resPos * 2);
memcpy(dest->data, resBuf, resPos * 2);
} else
break;
} while (1);
free(resBuf);
}
void processTurntabs(FILE *inf, CptObj *destArr) {
char line[1024];
dofgets(line, 1024, inf);
assert(lineMatchSection(line, "TURNTABS"));
uint16 *resBuf = (uint16 *)malloc(MAX_OBJ_SIZE);
do {
dofgets(line, 1024, inf);
if (!isEndOfSection(line)) {
char cptName[50];
uint16 id = getInfo(line, "TURNTAB", cptName);
CptObj *dest = destArr + id;
assertEmpty(dest);
dest->dbgName = (char *)malloc(strlen(cptName) + 1);
dest->type = TURNTAB;
strcpy(dest->dbgName, cptName);
memset(resBuf, 0, MAX_OBJ_SIZE);
uint32 resPos = 0;
do {
dofgets(line, 1024, inf);
if (!isEndOfObject(line, "TURNTAB", id)) {
assert((line[0] == '\t') && (line[1] == '\t'));
char *stopCh;
uint16 destId = (uint16)strtoul(line + 2, &stopCh, 16);
assert(stopCh == (line + 6));
assert((stopCh[0] == '-') && (stopCh[1] == '>'));
resBuf[resPos] = destId;
resPos++;
} else
break;
} while (1);
assert(resPos < (MAX_OBJ_SIZE / 2));
dest->len = resPos;
dest->data = (uint16 *)malloc(resPos * 2);
memcpy(dest->data, resBuf, resPos * 2);
} else
break;
} while (1);
free(resBuf);
}
void processBins(FILE *inf, CptObj *destArr, const char *typeName, const char *objName, uint8 cTypeId) {
char line[1024];
dofgets(line, 1024, inf);
assert(lineMatchSection(line, typeName));
uint16 *resBuf = (uint16 *)malloc(MAX_OBJ_SIZE);
do {
dofgets(line, 1024, inf);
if (!isEndOfSection(line)) {
char cptName[50];
uint16 id = getInfo(line, objName, cptName);
CptObj *dest = destArr + id;
assertEmpty(dest);
dest->dbgName = (char *)malloc(strlen(cptName) + 1);
dest->type = cTypeId;
strcpy(dest->dbgName, cptName);
memset(resBuf, 0, MAX_OBJ_SIZE);
uint32 resPos = 0;
do {
dofgets(line, 1024, inf);
if (!isEndOfObject(line, objName, id)) {
assert((line[0] == '\t') && (line[1] == '\t'));
char *stopCh;
uint16 destId = (uint16)strtoul(line + 2, &stopCh, 16);
assert(stopCh == (line + 6));
assert(*stopCh == '\0');
resBuf[resPos] = destId;
resPos++;
} else
break;
} while (1);
assert(resPos < (MAX_OBJ_SIZE / 2));
dest->len = resPos;
dest->data = (uint16 *)malloc(resPos * 2);
memcpy(dest->data, resBuf, resPos * 2);
} else
break;
} while (1);
free(resBuf);
}
uint16 dlinkCount = 0;
static uint16 dlinks[1024];
static char* dlinkNames[512];
void processSymlinks(FILE *inf, CptObj *destArr, uint16 *baseLists) {
char line[1024];
dofgets(line, 1024, inf);
assert(lineMatchSection(line, "SYMLINKS"));
do {
dofgets(line, 1024, inf);
if (!isEndOfSection(line)) {
char cptName[50];
uint16 fromId = getInfo(line, "SYMLINK", cptName);
CptObj *from = destArr + fromId;
assertEmpty(from);
dlinkNames[dlinkCount] = (char *)malloc(strlen(cptName) + 1);
strcpy(dlinkNames[dlinkCount], cptName);
dofgets(line, 1024, inf);
assert((line[0] == '\t') && (line[1] == '\t') && (line[2] == '-') && (line[3] == '>'));
char *stopCh;
uint16 destId = (uint16)strtoul(line + 4, &stopCh, 16);
assert(stopCh == (line + 8));
assert((stopCh[0] == ':') && (stopCh[1] == ':'));
dlinks[dlinkCount * 2 + 0] = fromId;
dlinks[dlinkCount * 2 + 1] = destId;
dlinkCount++;
dofgets(line, 1024, inf);
assert(isEndOfObject(line, "SYMLINK", fromId));
} else
break;
} while (1);
}
void doCompile(FILE *inf, FILE *debOutf, FILE *resOutf, TextFile *cptDef, FILE *sve) {
uint16 maxStrl = 0;
uint16 maxCptl = 0;
printf("Processing...\n");
CptObj *resCpts;
uint16 baseLists[NUM_DATA_LISTS];
memset(baseLists, 0, NUM_DATA_LISTS * 2);
resCpts = (CptObj *)malloc(MAX_CPTS * sizeof(CptObj));
memset(resCpts, 0, MAX_CPTS * sizeof(CptObj));
printf(" MainLists...\n");
processMainLists(inf, resCpts, baseLists);
printf(" Compacts...\n");
processCpts(inf, resCpts);
printf(" Turntables...\n");
processTurntabs(inf, resCpts);
printf(" Animation tables...\n");
processBins(inf, resCpts, "ANIMSEQS", "ANIMSEQ", ANIMSEQ);
printf(" Unknown binaries...\n");
processBins(inf, resCpts, "MISCBINS", "MISCBIN", MISCBIN);
printf(" Get To tables...\n");
processBins(inf, resCpts, "GETTOTAB", "GET_TOS", GETTOTAB);
printf(" Scratch buffers...\n");
processBins(inf, resCpts, "SCRATCHR", "SCRATCH", ROUTEBUF);
printf(" Symbolic links...\n");
processSymlinks(inf, resCpts, baseLists);
printf("Converting to binary data...\n");
uint32 numCpts = 1;
for (uint32 cnt = 1; cnt < MAX_CPTS; cnt++)
if (resCpts[cnt].data || resCpts[cnt].dbgName || resCpts[cnt].len)
numCpts++;
uint16 dataListLen[NUM_DATA_LISTS];
for (uint32 cnt = 0; cnt < NUM_DATA_LISTS; cnt++)
for (uint16 elemCnt = 0; elemCnt < 0x1000; elemCnt++) {
uint32 id = (cnt << 12) | elemCnt;
if (resCpts[id].data || resCpts[id].dbgName || resCpts[id].len)
dataListLen[cnt] = elemCnt + 1;
}
// write the header
uint32 rev = 0;
fwrite(&rev, 2, 1, debOutf);
fwrite(&rev, 2, 1, resOutf);
rev = NUM_DATA_LISTS;
fwrite(&rev, 2, 1, debOutf);
fwrite(&rev, 2, 1, resOutf);
for (uint32 cnt = 0; cnt < NUM_DATA_LISTS; cnt++) {
fwrite(dataListLen + cnt, 2, 1, debOutf);
fwrite(dataListLen + cnt, 2, 1, resOutf);
}
uint32 binSize = 0;
uint32 binDest = ftell(debOutf);
fwrite(&binSize, 1, 4, debOutf);
fwrite(&binSize, 1, 4, resOutf);
fwrite(&binSize, 1, 4, debOutf);
fwrite(&binSize, 1, 4, resOutf);
char *asciiBuf = (char *)malloc(ASCII_SIZE);
char *asciiPos = asciiBuf;
// now process all the compacts
uint32 cptSize[2];
cptSize[0] = ftell(debOutf);
cptSize[1] = ftell(resOutf);
for (uint32 lcnt = 0; lcnt < NUM_DATA_LISTS; lcnt++) {
for (uint32 eCnt = 0; eCnt < dataListLen[lcnt]; eCnt++) {
uint32 cId = (lcnt << 12) | eCnt;
CptObj *cpt = resCpts + cId;
if (resCpts[cId].data || resCpts[cId].dbgName || resCpts[cId].len || resCpts[cId].type) {
strcpy(asciiPos, cpt->dbgName);
asciiPos += strlen(cpt->dbgName) + 1;
assert(cpt->len < 0xFFFF);
uint16 dlen = (uint16)cpt->len;
if (dlen > maxCptl)
maxCptl = dlen;
binSize += dlen;
assert(dlen != 0);
fwrite(&dlen, 2, 1, debOutf);
fwrite(&dlen, 2, 1, resOutf);
uint16 field = resCpts[cId].type;
fwrite(&field, 2, 1, debOutf);
fwrite(cpt->data, 2, dlen, debOutf);
fwrite(cpt->data, 2, dlen, resOutf);
} else {
uint16 tmp = 0;
fwrite(&tmp, 2, 1, debOutf);
fwrite(&tmp, 2, 1, resOutf);
}
}
printf("DEBUG lcnt: %lu Output File Position: 0x%08lX\r\n", lcnt, ftell(debOutf));
}
cptSize[0] = ftell(debOutf) - cptSize[0];
cptSize[1] = ftell(resOutf) - cptSize[1];
assert(!(cptSize[0] & 1));
assert(!(cptSize[1] & 1));
cptSize[0] /= 2;
cptSize[1] /= 2;
for (uint32 cnt = 0; cnt < dlinkCount; cnt++) {
strcpy(asciiPos, dlinkNames[cnt]);
asciiPos += strlen(dlinkNames[cnt]) + 1;
}
uint32 asciiSize = (uint32)(asciiPos - asciiBuf);
fwrite(&asciiSize, 1, 4, debOutf);
fwrite(asciiBuf, 1, asciiSize, debOutf);
free(asciiBuf);
// the direct links...
fwrite(&dlinkCount, 2, 1, debOutf);
fwrite(&dlinkCount, 2, 1, resOutf);
for (uint32 cnt = 0; cnt < dlinkCount; cnt++) {
fwrite(dlinks + cnt * 2 + 0, 2, 1, debOutf);
fwrite(dlinks + cnt * 2 + 0, 2, 1, resOutf);
fwrite(dlinks + cnt * 2 + 1, 2, 1, debOutf);
fwrite(dlinks + cnt * 2 + 1, 2, 1, resOutf);
}
printf("Processing diff data...\n");
printf("DEBUG Output File Position: 0x%08lX\r\n", ftell(debOutf));
// 288 diffdata
FILE *dif = fopen("288diff.txt", "r");
assert(dif);
char line[1024];
uint16 diff[8192];
uint16 diffDest = 0;
uint16 diffNo = 0;
while (fgets(line, 1024, dif)) {
crop(line);
if (line[0] != '$') {
assert(memcmp(line, "data_", 5) == 0);
char *pos = line + 5;
char *stopCh;
uint16 lId = (uint16)strtoul(pos, &stopCh, 10);
assert(*stopCh == '[');
uint16 eId = (uint16)strtoul(stopCh + 1, &stopCh, 10);
assert((stopCh[0] == ']') && (stopCh[1] == '[') && (eId <= 0xFFF) && (lId <= 7));
uint16 id = (lId << 12) | eId;
uint16 elemNo = (uint16)strtoul(stopCh + 2, &stopCh, 10);
assert(*stopCh == ']');
stopCh = strstr(stopCh, "0x") + 2;
uint16 val = (uint16)strtoul(stopCh, &stopCh, 16);
assert(*stopCh == ';');
diff[diffDest++] = id;
diff[diffDest++] = elemNo;
diff[diffDest++] = 1;
diff[diffDest++] = val;
diffNo++;
} else {
char *pos = strchr(line, ' ');
*pos = '\0';
uint16 id = findCptId(line + 1, cptDef);
assert(id);
diff[diffDest++] = id;
diff[diffDest++] = 0;
pos++;
uint16 len = (uint16)strtoul(pos, &pos, 10);
diff[diffDest++] = len;
assert(len);
assert(resCpts[id].len == len);
for (uint16 cnt = 0; cnt < len; cnt++) {
assert(*pos == ' ');
pos++;
diff[diffDest++] = (uint16)strtoul(pos, &pos, 16);
}
assert(diff[diffDest - 1] == 0xFFFF);
diffNo++;
}
}
fclose(dif);
free(resCpts);
assert(diffDest <= 8192);
fwrite(&diffNo, 1, 2, debOutf);
fwrite(&diffDest, 1, 2, debOutf);
fwrite(diff, 2, diffDest, debOutf);
fwrite(&diffNo, 1, 2, resOutf);
fwrite(&diffDest, 1, 2, resOutf);
fwrite(diff, 2, diffDest, resOutf);
printf("Converting Save data...\n");
printf("DEBUG Output File Position: 0x%08lX\r\n", ftell(debOutf));
// the IDs of the compacts to be saved
char cptName[1024];
uint16 saveIds[2048];
uint16 numIds = 0;
while (fgets(cptName, 1024, sve)) {
crop(cptName);
uint16 resId = findCptId(cptName, cptDef);
if (!resId)
printf("ERROR: Can't find definition of %s\n", cptName);
else {
saveIds[numIds] = resId;
numIds++;
}
}
printf("%d saveIds\n", numIds);
fwrite(&numIds, 2, 1, debOutf);
fwrite(saveIds, 2, numIds, debOutf);
fwrite(&numIds, 2, 1, resOutf);
fwrite(saveIds, 2, numIds, resOutf);
printf("Converting Reset data...\n");
// now append the reset data
uint16 gameVers[7] = { 303, 331, 348, 365, 368, 372, 288 };
// make sure all files exist
bool filesExist = true;
char inName[32];
for (int i = 0; i < 7; i++) {
sprintf(inName, "RESET.%03d", gameVers[i]);
FILE *test = fopen(inName, "rb");
if (test)
fclose(test);
else {
filesExist = false;
printf("File %s not found\n", inName);
}
}
if (filesExist) {
FILE *res288 = fopen("RESET.288", "rb");
fseek(res288, 0, SEEK_END);
assert((ftell(res288) / 2) < 65536);
uint16 resSize = (uint16)(ftell(res288) / 2);
fseek(res288, 0, SEEK_SET);
uint16 *buf288 = (uint16 *)malloc(resSize * 2);
fread(buf288, 2, resSize, res288);
fclose(res288);
fwrite(&resSize, 1, 2, debOutf);
fwrite(buf288, 2, resSize, debOutf);
uint16 tmp = 7;
fwrite(&tmp, 2, 1, debOutf);
tmp = 288;
fwrite(&tmp, 2, 1, debOutf);
tmp = 0;
fwrite(&tmp, 2, 1, debOutf);
printf("DEBUG Output File Position: 0x%08lX\r\n", ftell(debOutf));
printf("reset destination: %ld\n", ftell(debOutf));
for (int cnt = 0; cnt < 6; cnt++) {
printf("Processing diff v0.0%03d\n", gameVers[cnt]);
uint16 diffPos = 0;
sprintf(inName, "RESET.%03d", gameVers[cnt]);
FILE *resDiff = fopen(inName, "rb");
fseek(resDiff, 0, SEEK_END);
assert(ftell(resDiff) == (resSize * 2));
fseek(resDiff, 0, SEEK_SET);
uint16 *bufDif = (uint16 *)malloc(resSize *2);
fread(bufDif, 2, resSize, resDiff);
fclose(resDiff);
for (uint16 eCnt = 0; eCnt < resSize; eCnt++)
if (buf288[eCnt] != bufDif[eCnt]) {
diff[diffPos++] = eCnt;
diff[diffPos++] = bufDif[eCnt];
}
free(bufDif);
fwrite(gameVers + cnt, 1, 2, debOutf);
assert(!(diffPos & 1));
diffPos /= 2;
fwrite(&diffPos, 1, 2, debOutf);
fwrite(diff, 2, 2 * diffPos, debOutf);
printf("diff v0.0%03d: 2 * 2 * %d\n", gameVers[cnt], diffPos);
printf("DEBUG Output File Position: 0x%08lX\r\n", ftell(debOutf));
}
free(buf288);
} else {
printf("Creating CPT file with Dummy reset data @ %ld\n", ftell(debOutf));
uint16 resetFields16 = 4;
fwrite(&resetFields16, 2, 1, debOutf);
uint32 blah = 8;
fwrite(&blah, 4, 1, debOutf); // size field: 8 bytes
blah = (uint32)-1;
fwrite(&blah, 4, 1, debOutf); // save file revision. -1 is unknown to scummvm, so it'll refuse to load it.
resetFields16 = 0;
fwrite(&resetFields16, 2, 1, debOutf); // numDiffs: 0, no further reset blocks.
}
// now fill the raw-compact-data-size header field
fseek(resOutf, binDest, SEEK_SET);
fseek(debOutf, binDest, SEEK_SET);
fwrite(&binSize, 1, 4, debOutf);
fwrite(&binSize, 1, 4, resOutf);
fwrite(cptSize + 0, 1, 4, debOutf);
fwrite(cptSize + 1, 1, 4, resOutf);
printf("%d diffs\n", diffNo);
printf("%ld Compacts in total\n", numCpts);
printf("max strlen = %d\n", maxStrl);
printf("raw size = 2 * %ld\n", binSize);
printf("max cptlen = %d\n", maxCptl);
}