mirror of
https://github.com/scummvm/scummvm.git
synced 2025-04-02 10:52:32 -04:00
514 lines
13 KiB
C++
514 lines
13 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 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 <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "common/memstream.h"
|
|
#include "common/system.h"
|
|
|
|
#include "graphics/palette.h"
|
|
#include "graphics/paletteman.h"
|
|
|
|
#include "image/png.h"
|
|
|
|
#include "dgds/ads.h"
|
|
#include "dgds/console.h"
|
|
#include "dgds/dgds.h"
|
|
#include "dgds/globals.h"
|
|
#include "dgds/includes.h"
|
|
#include "dgds/image.h"
|
|
#include "dgds/parser.h"
|
|
#include "dgds/resource.h"
|
|
#include "dgds/scene.h"
|
|
#include "dgds/game_palettes.h"
|
|
#include "gui/debugger.h"
|
|
|
|
namespace Dgds {
|
|
|
|
Console::Console(DgdsEngine *vm) : _vm(vm) {
|
|
registerCmd("fileinfo", WRAP_METHOD(Console, cmdFileInfo));
|
|
registerCmd("filesearch", WRAP_METHOD(Console, cmdFileSearch));
|
|
registerCmd("filedump", WRAP_METHOD(Console, cmdFileDump));
|
|
registerCmd("imagedump", WRAP_METHOD(Console, cmdImageDump));
|
|
registerCmd("imagedumpall", WRAP_METHOD(Console, cmdImageDumpAll));
|
|
registerCmd("global", WRAP_METHOD(Console, cmdGlobal));
|
|
registerCmd("scene", WRAP_METHOD(Console, cmdScene));
|
|
registerCmd("scriptdump", WRAP_METHOD(Console, cmdScriptDump));
|
|
registerCmd("trigger", WRAP_METHOD(Console, cmdTrigger));
|
|
registerCmd("hotarea", WRAP_METHOD(Console, cmdSetHotAreaDebug));
|
|
}
|
|
|
|
bool Console::cmdFileInfo(int argc, const char **argv) {
|
|
if (argc != 2) {
|
|
debugPrintf("Usage: %s <file>\n", argv[0]);
|
|
return true;
|
|
}
|
|
|
|
const Resource &res = _vm->getResourceManager()->getResourceInfo(argv[1]);
|
|
debugPrintf("Resource volume: %d, position: %d, size: %d, checksum: %d\n", res.volume, res.pos, res.size, res.checksum);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Console::cmdFileSearch(int argc, const char **argv) {
|
|
if (argc != 2) {
|
|
debugPrintf("Usage: %s <file to search>\n", argv[0]);
|
|
return true;
|
|
}
|
|
|
|
const ResourceList &resources = _vm->getResourceManager()->getResources();
|
|
for (const auto &resource : resources) {
|
|
if (resource._key.contains(argv[1])) {
|
|
Resource res = resource._value;
|
|
debugPrintf("Resource: %s, volume: %d, position: %d, size: %d, checksum: %d\n", resource._key.c_str(), res.volume, res.pos, res.size, res.checksum);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Console::cmdFileDump(int argc, const char **argv) {
|
|
if (argc < 2) {
|
|
debugPrintf("Usage: %s <file> [ignore patches] [unpack] [outputpath] [chunktype]\n", argv[0]);
|
|
debugPrintf(" eg: %s CLLIVING.ADH 0 1 clliving_unpack.adscript SCR:\n", argv[0]);
|
|
return true;
|
|
}
|
|
|
|
Common::String fileName = argv[1];
|
|
bool ignorePatches = (argc > 2) && (!scumm_stricmp(argv[2], "true") || !strcmp(argv[2], "1"));
|
|
bool unpack = (argc > 3) && (!scumm_stricmp(argv[3], "true") || !strcmp(argv[3], "1"));
|
|
Common::String dstPath = (argc > 4) ? argv[4] : "";
|
|
Common::String chunkType = (argc > 5) ? argv[5] : "";
|
|
Common::SeekableReadStream *resStream = _vm->getResource(fileName, ignorePatches);
|
|
if (resStream == nullptr) {
|
|
debugPrintf("Resource not found\n");
|
|
return true;
|
|
}
|
|
int32 size = resStream->size();
|
|
byte *data;
|
|
|
|
if (!unpack) {
|
|
data = new byte[size];
|
|
resStream->read(data, size);
|
|
} else {
|
|
data = new byte[2000000]; // about 2MB, but maximum container size is around 1.5MB
|
|
byte *ptr = data;
|
|
const char *dot;
|
|
DGDS_EX ex = 0;
|
|
size = 0;
|
|
|
|
if ((dot = strrchr(fileName.c_str(), '.'))) {
|
|
ex = MKTAG24(toupper(dot[1]), toupper(dot[2]), toupper(dot[3]));
|
|
}
|
|
|
|
DgdsChunkReader chunk(resStream);
|
|
while (chunk.readNextHeader(ex, fileName)) {
|
|
if (!chunkType.empty() && !chunkType.equals(chunk.getIdStr())) {
|
|
if (!chunk.isContainer())
|
|
chunk.skipContent();
|
|
continue;
|
|
}
|
|
|
|
bool readSuccess = chunk.readContent(_vm->getDecompressor());
|
|
if (readSuccess) {
|
|
memcpy(ptr, chunk.getIdStr(), 4);
|
|
ptr += 4;
|
|
|
|
chunk.getContent()->read(ptr, chunk.getContent()->size());
|
|
ptr += chunk.getContent()->size();
|
|
|
|
size += 4 + chunk.getContent()->size();
|
|
} else {
|
|
warning("Failed to read content for chunk with id %s", chunk.getIdStr());
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
delete resStream;
|
|
|
|
Common::DumpFile out;
|
|
out.open(Common::Path(dstPath + fileName));
|
|
if (!out.isOpen()) {
|
|
warning("Couldn't open path %s%s", dstPath.c_str(), fileName.c_str());
|
|
} else {
|
|
out.write(data, size);
|
|
out.flush();
|
|
out.close();
|
|
}
|
|
delete[] data;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Console::dumpImageFrame(const char *fname, int frameno, const char *outpath) {
|
|
Image img(_vm->getResourceManager(), _vm->getDecompressor());
|
|
int maxframe = img.frameCount(fname);
|
|
if (frameno > maxframe) {
|
|
debugPrintf("Image only has %d frames\n", maxframe);
|
|
return false;
|
|
}
|
|
GamePalettes pal(_vm->getResourceManager(), _vm->getDecompressor());
|
|
img.loadBitmap(fname);
|
|
int width = img.width(frameno);
|
|
int height = img.height(frameno);
|
|
if (!width || !height) {
|
|
debugPrintf("Image %s:%d not valid\n", fname, frameno);
|
|
return false;
|
|
}
|
|
|
|
Common::DumpFile outf;
|
|
Common::String outfname = Common::String::format("%s-%d.png", fname, frameno);
|
|
|
|
if (outpath) {
|
|
Common::Path path(outpath);
|
|
path.joinInPlace(outfname);
|
|
outf.open(path);
|
|
} else {
|
|
outf.open(Common::Path(outfname));
|
|
}
|
|
if (!outf.isOpen()) {
|
|
debugPrintf("Couldn't open %s\n", outfname.c_str());
|
|
return false;
|
|
}
|
|
|
|
byte palbuf[768];
|
|
g_system->getPaletteManager()->grabPalette(palbuf, 0, 256);
|
|
::Image::writePNG(outf, *(img.getSurface(frameno)->surfacePtr()), palbuf);
|
|
outf.close();
|
|
debugPrintf("wrote %dx%d png to %s\n", width, height, outfname.c_str());
|
|
return true;
|
|
}
|
|
|
|
bool Console::cmdImageDumpAll(int argc, const char **argv) {
|
|
#ifdef USE_PNG
|
|
if (argc < 2) {
|
|
debugPrintf("Usage: %s <imagefilename> [outputdir]\n", argv[0]);
|
|
debugPrintf(" eg: %s CLGAME2.BMP /tmp\n", argv[0]);
|
|
return true;
|
|
}
|
|
|
|
const char *fname = argv[1];
|
|
if (!_vm->getResourceManager()->hasResource(fname)) {
|
|
debugPrintf("Resource %s not found\n", fname);
|
|
return true;
|
|
}
|
|
|
|
Image img(_vm->getResourceManager(), _vm->getDecompressor());
|
|
int maxframe = img.frameCount(fname);
|
|
|
|
const char *outpath = (argc > 2 ? argv[2] : nullptr);
|
|
|
|
for (int i = 0; i < maxframe; i++) {
|
|
if (!dumpImageFrame(fname, i, outpath))
|
|
break;
|
|
}
|
|
return true;
|
|
#else
|
|
warning("dumpimage needs png support");
|
|
return true;
|
|
#endif // USE_PNG
|
|
|
|
}
|
|
|
|
bool Console::cmdImageDump(int argc, const char **argv) {
|
|
#ifdef USE_PNG
|
|
if (argc < 3) {
|
|
debugPrintf("Usage: %s <imagefilename> <frameno> [outputdir]\n", argv[0]);
|
|
debugPrintf(" eg: %s CLGAME2.BMP 2 /tmp\n", argv[0]);
|
|
return true;
|
|
}
|
|
|
|
const char *fname = argv[1];
|
|
int frameno = atoi(argv[2]);
|
|
|
|
if (!_vm->getResourceManager()->hasResource(fname)) {
|
|
debugPrintf("Resource %s not found\n", fname);
|
|
return true;
|
|
}
|
|
|
|
const char *outpath = (argc > 3 ? argv[3] : nullptr);
|
|
|
|
dumpImageFrame(fname, frameno, outpath);
|
|
return true;
|
|
#else
|
|
warning("dumpimage needs png support");
|
|
return true;
|
|
#endif // USE_PNG
|
|
}
|
|
|
|
bool Console::cmdGlobal(int argc, const char **argv) {
|
|
if (argc < 2) {
|
|
debugPrintf("Usage: %s <num> <val>\n", argv[0]);
|
|
debugPrintf("%s <num> returns the value of a global\n", argv[0]);
|
|
debugPrintf("%s <num> <val> sets the value of a global\n", argv[0]);
|
|
return true;
|
|
}
|
|
|
|
GDSScene *scene = _vm->getGDSScene();
|
|
int num = atoi(argv[1]);
|
|
|
|
if (argc == 2) {
|
|
int16 val = scene->getGlobal(num);
|
|
debugPrintf("Global %d is %d\n", num, val);
|
|
} else if (argc == 3) {
|
|
int16 newVal = atoi(argv[2]);
|
|
scene->setGlobal(num, newVal);
|
|
debugPrintf("Global %d set to %d\n", num, newVal);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Console::cmdScene(int argc, const char **argv) {
|
|
if (argc == 1) {
|
|
int curSceneNum = _vm->getScene()->getNum();
|
|
debugPrintf("Current scene is %d\n", curSceneNum);
|
|
debugPrintf("Use %s <num> to change the scene\n", argv[0]);
|
|
} else {
|
|
int num = atoi(argv[1]);
|
|
_vm->changeScene(num);
|
|
debugPrintf("Scene changed to %d\n", num);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Console::cmdTrigger(int argc, const char **argv) {
|
|
if (argc < 2) {
|
|
debugPrintf("Usage: %s <num> <val>\n", argv[0]);
|
|
debugPrintf("%s <num> returns the status of a trigger\n", argv[0]);
|
|
debugPrintf("%s <num> <val> sets the status of a trigger\n", argv[0]);
|
|
return true;
|
|
}
|
|
|
|
SDSScene *scene = _vm->getScene();
|
|
int num = atoi(argv[1]);
|
|
|
|
if (argc == 2) {
|
|
bool val = scene->isTriggerEnabled(num);
|
|
debugPrintf("Trigger %d is %d\n", num, val);
|
|
} else if (argc == 3) {
|
|
bool enable = atoi(argv[2]);
|
|
scene->enableTrigger(0, num, enable);
|
|
debugPrintf("Trigger %d set to %d\n", num, enable);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void Console::printOp(int indent, const char *text) {
|
|
for (int i = 0; i < indent; i++) {
|
|
debugPrintf("\t");
|
|
}
|
|
debugPrintf("%s", text);
|
|
}
|
|
|
|
bool Console::cmdScriptDump(int argc, const char **argv) {
|
|
if (argc < 2) {
|
|
debugPrintf("Usage: %s <filename>\n", argv[0]);
|
|
debugPrintf(" eg: %s CLBATH.ADS\n", argv[0]);
|
|
return true;
|
|
}
|
|
|
|
Common::String fname(argv[1]);
|
|
|
|
Common::SeekableReadStream *resStream = _vm->getResource(fname, false);
|
|
if (resStream == nullptr) {
|
|
debugPrintf("Resource %s not found\n", fname.c_str());
|
|
return true;
|
|
}
|
|
|
|
ADSData adsData;
|
|
ADSInterpreter interp(_vm);
|
|
ADSParser dgds(_vm->getResourceManager(), _vm->getDecompressor());
|
|
dgds.parse(&adsData, fname);
|
|
|
|
adsData.scr->seek(0);
|
|
|
|
uint8 segno = 0;
|
|
uint8 indent = 1;
|
|
uint16 opcode;
|
|
uint16 tag = 1;
|
|
while (!adsData.scr->eos()) {
|
|
opcode = adsData.scr->readUint16LE();
|
|
switch (opcode) {
|
|
case 0xFFFF:
|
|
printOp(indent, "END\n");
|
|
tag = adsData.scr->readUint16LE();
|
|
segno++;
|
|
// Fall through
|
|
case 0x0001:
|
|
if (adsData._tags.contains(tag))
|
|
debugPrintf("\n%d: %s\n", segno, adsData._tags[tag].c_str());
|
|
continue;
|
|
case 0x0005:
|
|
printOp(indent, "init");
|
|
break;
|
|
case 0x1010:
|
|
printOp(indent++, "WHILE runtype");
|
|
break;
|
|
case 0x1020:
|
|
printOp(indent++, "WHILE not runtype");
|
|
break;
|
|
case 0x1030:
|
|
printOp(indent++, "WHILE NOT_PLAYED");
|
|
break;
|
|
case 0x1040:
|
|
printOp(indent++, "WHILE PLAYED");
|
|
break;
|
|
case 0x1050:
|
|
printOp(indent++, "WHILE FINISHED");
|
|
break;
|
|
case 0x1060:
|
|
printOp(indent++, "WHILE NOT_RUNNING");
|
|
break;
|
|
case 0x1070:
|
|
printOp(indent++, "WHILE RUNNING");
|
|
break;
|
|
case 0x1080:
|
|
printOp(indent++, "WHILE count?");
|
|
break;
|
|
case 0x1090:
|
|
printOp(indent++, "WHILE ??");
|
|
break;
|
|
case 0x1310:
|
|
printOp(indent++, "IF PAUSED");
|
|
break;
|
|
case 0x1320:
|
|
printOp(indent++, "IF NOT_PAUSED");
|
|
break;
|
|
case 0x1330:
|
|
printOp(indent++, "IF NOT_PLAYED");
|
|
break;
|
|
case 0x1340:
|
|
printOp(indent++, "IF PLAYED");
|
|
break;
|
|
case 0x1350:
|
|
printOp(indent++, "IF FINISHED");
|
|
break;
|
|
case 0x1360:
|
|
printOp(indent++, "IF NOT_RUNNING");
|
|
break;
|
|
case 0x1370:
|
|
printOp(indent++, "IF RUNNING");
|
|
break;
|
|
case 0x1380:
|
|
printOp(indent++, "IF DETAIL LEVEL <=");
|
|
break;
|
|
case 0x1390:
|
|
printOp(indent++, "IF DETAIL LEVEL >=");
|
|
break;
|
|
case 0x1500:
|
|
printOp(--indent, "ELSE / Skip to end-if");
|
|
break;
|
|
case 0x1510:
|
|
printOp(--indent, "END IF");
|
|
break;
|
|
case 0x1520:
|
|
printOp(--indent, "END WHILE");
|
|
break;
|
|
case 0x2000:
|
|
printOp(indent, "ADD sequence (restart)");
|
|
break;
|
|
case 0x2005:
|
|
printOp(indent, "ADD sequence (continue)");
|
|
break;
|
|
case 0x2010:
|
|
printOp(indent, "STOP SCENE");
|
|
break;
|
|
case 0x2015:
|
|
printOp(indent, "PAUSE SEQ");
|
|
break;
|
|
case 0x2020:
|
|
printOp(indent, "RESET SEQ");
|
|
break;
|
|
case 0x3020:
|
|
printOp(indent, "RANDOM_NOOP");
|
|
break;
|
|
case 0x3010:
|
|
printOp(indent++, "RANDOM_START");
|
|
break;
|
|
case 0x30FF:
|
|
printOp(--indent, "RANDOM_END");
|
|
break;
|
|
case 0x4000:
|
|
printOp(indent, "MOVE SEQ TO BACK");
|
|
break;
|
|
case 0x4010:
|
|
printOp(indent, "MOVE SEQ TO FRONT");
|
|
break;
|
|
case 0xF000:
|
|
printOp(indent, "SET STATE 2");
|
|
break;
|
|
case 0xF010:
|
|
printOp(indent, "FADE_OUT");
|
|
break;
|
|
case 0xF200:
|
|
printOp(indent, "RUN_SCRIPT");
|
|
break;
|
|
case 0xF210:
|
|
printOp(indent, "RESTART_SCRIPT");
|
|
break;
|
|
case 0x1420:
|
|
printOp(--indent, "AND");
|
|
break;
|
|
case 0x1430:
|
|
printOp(--indent, "OR");
|
|
break;
|
|
case 0xFF10:
|
|
printOp(indent, "UNKNOWN");
|
|
break;
|
|
case 0xFFF0:
|
|
printOp(indent, "END_IF");
|
|
break;
|
|
default:
|
|
printOp(indent, "UNKNOWN");
|
|
debugPrintf("(%04x)", opcode);
|
|
break;
|
|
}
|
|
int numArgs = interp.numArgs(opcode);
|
|
for (int i = 0; i < numArgs; i++) {
|
|
debugPrintf(" %d", adsData.scr->readUint16LE());
|
|
}
|
|
debugPrintf("\n");
|
|
}
|
|
|
|
delete resStream;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Console::cmdSetHotAreaDebug(int argc, const char **argv) {
|
|
if (argc > 1) {
|
|
debugPrintf("Usage: %s\n", argv[0]);
|
|
debugPrintf("Toggle whether to show debug boxes around scene hot areas\n");
|
|
return true;
|
|
}
|
|
|
|
DgdsEngine *engine = DgdsEngine::getInstance();
|
|
|
|
bool enabled = engine->getDebugShowHotAreas();
|
|
debugPrintf("Turned %s hot area debug\n", enabled ? "OFF" : "ON");
|
|
engine->setDebugShowHotAreas(!enabled);
|
|
return true;
|
|
}
|
|
|
|
|
|
} // End of namespace Dgds
|