/* 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. * */ // AGOS debug functions #include "common/file.h" #include "common/textconsole.h" #include "agos/debug.h" #include "agos/agos.h" #include "agos/intern.h" #include "agos/vga.h" namespace AGOS { const byte *AGOSEngine::dumpOpcode(const byte *p) { uint16 opcode; const char *s, *st; if (getGameType() == GType_ELVIRA1) { opcode = READ_BE_UINT16(p); p += 2; if (opcode == 10000) return NULL; } else { opcode = *p++; if (opcode == 255) return NULL; } if (getGameType() == GType_PP) { st = s = puzzlepack_opcodeNameTable[opcode]; } else if (getGameType() == GType_FF) { st = s = feeblefiles_opcodeNameTable[opcode]; } else if (getGameType() == GType_SIMON2 && getFeatures() & GF_TALKIE) { st = s = simon2talkie_opcodeNameTable[opcode]; } else if (getGameType() == GType_SIMON2) { st = s = simon2dos_opcodeNameTable[opcode]; } else if (getFeatures() & GF_TALKIE) { st = s = simon1talkie_opcodeNameTable[opcode]; } else if (getGameType() == GType_SIMON1) { st = s = simon1dos_opcodeNameTable[opcode]; } else if (getGameType() == GType_WW) { st = s = waxworks_opcodeNameTable[opcode]; } else if (getGameType() == GType_ELVIRA2) { st = s = elvira2_opcodeNameTable[opcode]; } else { st = s = elvira1_opcodeNameTable[opcode]; } if (s == NULL) { error("dumpOpcode: INVALID OPCODE %d", opcode); } while (*st != '|') st++; debugN("%s ", st + 1); for (;;) { switch (*s++) { case 'x': debugN("\n"); return NULL; case '|': debugN("\n"); return p; case 'B':{ byte b = *p++; if (b == 255) debugN("[%d] ", *p++); else debugN("%d ", b); break; } case 'V':{ byte b = *p++; if (b == 255) debugN("[[%d]] ", *p++); else debugN("[%d] ", b); break; } case 'W':{ uint16 n = READ_BE_UINT16(p); p += 2; if (getGameType() == GType_PP) { if (n >= 60000 && n < 62048) debugN("[%d] ", n - 60000); else debugN("%d ", n); } else { if (n >= 30000 && n < 30512) debugN("[%d] ", n - 30000); else debugN("%d ", n); } break; } case 'w':{ int n = (int16)READ_BE_UINT16(p); p += 2; debugN("%d ", n); break; } case 'I':{ int n = (int16)READ_BE_UINT16(p); p += 2; if (n == -1) debugN("SUBJECT_ITEM "); else if (n == -3) debugN("OBJECT_ITEM "); else if (n == -5) debugN("ME_ITEM "); else if (n == -7) debugN("ACTOR_ITEM "); else if (n == -9) debugN("ITEM_A_PARENT "); else debugN("<%d> ", n); break; } case 'J':{ debugN("-> "); } break; case 'T':{ uint n = READ_BE_UINT16(p); p += 2; if (n != 0xFFFF) debugN("\"%s\"(%d) ", getStringPtrByID(n), n); else debugN("NULL_STRING "); } break; default: break; } } } void AGOSEngine::dumpSubroutineLine(SubroutineLine *sl, Subroutine *sub) { const byte *p; debugN("; ****\n"); p = (byte *)sl + SUBROUTINE_LINE_SMALL_SIZE; if (sub->id == 0) { debugN("; verb=%d, noun1=%d, noun2=%d\n", sl->verb, sl->noun1, sl->noun2); p = (byte *)sl + SUBROUTINE_LINE_BIG_SIZE; } for (;;) { p = dumpOpcode(p); if (p == NULL) break; } } void AGOSEngine::dumpSubroutine(Subroutine *sub) { SubroutineLine *sl; debugN("\n******************************************\n;Subroutine, ID=%d:\nSUB_%d:\n", sub->id, sub->id); sl = (SubroutineLine *)((byte *)sub + sub->first); for (; (byte *)sl != (byte *)sub; sl = (SubroutineLine *)((byte *)sub + sl->next)) { dumpSubroutineLine(sl, sub); } debugN("\nEND ******************************************\n"); } void AGOSEngine::dumpSubroutines() { Subroutine *sub = _subroutineList; for (; sub; sub = sub->next) { dumpSubroutine(sub); } } void AGOSEngine::dumpAllSubroutines() { for (int i = 0; i < 65536; i++) { Subroutine *sub = getSubroutineByID(i); if (sub != NULL) { dumpSubroutine(sub); } } } void AGOSEngine::dumpVideoScript(const byte *src, bool singeOpcode) { uint16 opcode; const char *str, *strn; do { if (getGameType() == GType_SIMON2 || getGameType() == GType_FF || getGameType() == GType_PP) { opcode = *src++; } else { opcode = READ_BE_UINT16(src); src += 2; } if (opcode >= _numVideoOpcodes) { error("dumpVideoScript: Opcode %d out of range (%d)", opcode, _numVideoOpcodes); } if (getGameType() == GType_PP) { strn = str = puzzlepack_videoOpcodeNameTable[opcode]; } else if (getGameType() == GType_FF) { strn = str = feeblefiles_videoOpcodeNameTable[opcode]; } else if (getGameType() == GType_SIMON2) { strn = str = simon2_videoOpcodeNameTable[opcode]; } else if (getGameType() == GType_SIMON1) { strn = str = simon1_videoOpcodeNameTable[opcode]; } else if (getGameType() == GType_WW) { strn = str = ww_videoOpcodeNameTable[opcode]; } else if (getGameType() == GType_ELVIRA2) { strn = str = elvira2_videoOpcodeNameTable[opcode]; } else if (getGameType() == GType_ELVIRA1) { strn = str = elvira1_videoOpcodeNameTable[opcode]; } else { strn = str = pn_videoOpcodeNameTable[opcode]; } if (strn == NULL) { error("dumpVideoScript: Invalid Opcode %d", opcode); } while (*strn != '|') strn++; debugN("%.2d: %s ", opcode, strn + 1); int end = (getGameType() == GType_FF || getGameType() == GType_PP) ? 9999 : 999; for (; *str != '|'; str++) { switch (*str) { case 'x': debugN("\n"); return; case 'b': { debugN("%d ", *src++); break; } case 'w': { int16 v = (int16)readUint16Wrapper(src); src += 2; if (v < 0) debugN("[%d] ", -v); else debugN("%d ", v); break; } case 'd': { debugN("%d ", (int16)readUint16Wrapper(src)); src += 2; break; } case 'v': { debugN("[%d] ", readUint16Wrapper(src)); src += 2; break; } case 'i': { debugN("%d ", (int16)readUint16Wrapper(src)); src += 2; break; } case 'j': { debugN("-> "); break; } case 'q': { while (readUint16Wrapper(src) != end) { debugN("(%d,%d) ", readUint16Wrapper(src), readUint16Wrapper(src + 2)); src += 4; } src += 2; break; } default: error("dumpVideoScript: Invalid fmt string '%c' in decompile VGA", *str); } } debugN("\n"); } while (!singeOpcode); } void AGOSEngine::dumpVgaScript(const byte *ptr, uint16 res, uint16 id) { dumpVgaScriptAlways(ptr, res, id); } void AGOSEngine::dumpVgaScriptAlways(const byte *ptr, uint16 res, uint16 id) { debugN("; address=%x, vgafile=%d vgasprite=%d\n", (unsigned int)(ptr - _vgaBufferPointers[res].vgaFile1), res, id); dumpVideoScript(ptr, false); debugN("; end\n"); } void AGOSEngine::dumpAllVgaImageFiles() { const uint8 start = (getGameType() == GType_PN) ? 0 : 2; for (int z = start; z < _numZone; z++) { loadZone(z, false); dumpVgaBitmaps(z); } } void AGOSEngine::dumpAllVgaScriptFiles() { const uint8 start = (getGameType() == GType_PN) ? 0 : 2; for (int z = start; z < _numZone; z++) { uint16 zoneNum = (getGameType() == GType_PN) ? 0 : z; loadZone(z, false); VgaPointersEntry *vpe = &_vgaBufferPointers[zoneNum]; if (vpe->vgaFile1 != NULL) { _curVgaFile1 = vpe->vgaFile1; dumpVgaFile(_curVgaFile1); } } } #ifdef ENABLE_AGOS2 void AGOSEngine_Feeble::dumpVgaFile(const byte *vga) { const byte *pp; const byte *p; int16 count; pp = vga; p = pp + READ_LE_UINT16(pp + 2); count = READ_LE_UINT16(&((const VgaFile1Header_Feeble *) p)->animationCount); p = pp + READ_LE_UINT16(&((const VgaFile1Header_Feeble *) p)->animationTable); while (--count >= 0) { uint16 id = READ_LE_UINT16(&((const AnimationHeader_Feeble *) p)->id); dumpVgaScriptAlways(vga + READ_LE_UINT16(&((const AnimationHeader_Feeble *) p)->scriptOffs), id / 100, id); p += sizeof(AnimationHeader_Feeble); } pp = vga; p = pp + READ_LE_UINT16(pp + 2); count = READ_LE_UINT16(&((const VgaFile1Header_Feeble *) p)->imageCount); p = pp + READ_LE_UINT16(&((const VgaFile1Header_Feeble *) p)->imageTable); while (--count >= 0) { uint16 id = READ_LE_UINT16(&((const ImageHeader_Feeble *) p)->id); dumpVgaScriptAlways(vga + READ_LE_UINT16(&((const ImageHeader_Feeble *) p)->scriptOffs), id / 100, id); p += sizeof(ImageHeader_Feeble); } } #endif void AGOSEngine_Simon1::dumpVgaFile(const byte *vga) { const byte *pp; const byte *p; int16 count; pp = vga; p = pp + READ_BE_UINT16(pp + 4); count = READ_BE_UINT16(&((const VgaFile1Header_Common *) p)->animationCount); p = pp + READ_BE_UINT16(&((const VgaFile1Header_Common *) p)->animationTable); while (--count >= 0) { uint16 id = READ_BE_UINT16(&((const AnimationHeader_Simon *) p)->id); dumpVgaScriptAlways(vga + READ_BE_UINT16(&((const AnimationHeader_Simon *) p)->scriptOffs), id / 100, id); p += sizeof(AnimationHeader_Simon); } pp = vga; p = pp + READ_BE_UINT16(pp + 4); count = READ_BE_UINT16(&((const VgaFile1Header_Common *) p)->imageCount); p = pp + READ_BE_UINT16(&((const VgaFile1Header_Common *) p)->imageTable); while (--count >= 0) { uint16 id = READ_BE_UINT16(&((const ImageHeader_Simon *) p)->id); dumpVgaScriptAlways(vga + READ_BE_UINT16(&((const ImageHeader_Simon *) p)->scriptOffs), id / 100, id); p += sizeof(ImageHeader_Simon); } } void AGOSEngine::dumpVgaFile(const byte *vga) { const byte *pp; const byte *p; int16 count; pp = vga; p = pp + READ_BE_UINT16(pp + 10) + 20; count = READ_BE_UINT16(&((const VgaFile1Header_Common *) p)->animationCount); p = pp + READ_BE_UINT16(&((const VgaFile1Header_Common *) p)->animationTable); while (--count >= 0) { uint16 id = READ_BE_UINT16(&((const AnimationHeader_WW *) p)->id); dumpVgaScriptAlways(vga + READ_BE_UINT16(&((const AnimationHeader_WW *) p)->scriptOffs), id / 100, id); p += sizeof(AnimationHeader_WW); } pp = vga; p = pp + READ_BE_UINT16(pp + 10) + 20; count = READ_BE_UINT16(&((const VgaFile1Header_Common *) p)->imageCount); p = pp + READ_BE_UINT16(&((const VgaFile1Header_Common *) p)->imageTable); while (--count >= 0) { uint16 id = READ_BE_UINT16(&((const ImageHeader_WW *) p)->id); dumpVgaScriptAlways(vga + READ_BE_UINT16(&((const ImageHeader_WW *) p)->scriptOffs), id / 100, id); p += sizeof(ImageHeader_WW); } } static const byte bmp_hdr[] = { 0x42, 0x4D, 0x9E, 0x14, 0x00, 0x00, /* offset 2, file size */ 0x00, 0x00, 0x00, 0x00, 0x36, 0x04, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, /* image width */ 0x46, 0x00, 0x00, 0x00, /* image height */ 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, }; void dumpBMP(const char *filename, int16 w, int16 h, const byte *bytes, const byte *palette) { Common::DumpFile out; byte my_hdr[sizeof(bmp_hdr)]; int i; out.open(filename); if (!out.isOpen()) return; memcpy(my_hdr, bmp_hdr, sizeof(bmp_hdr)); *(uint32 *)(my_hdr + 2) = w * h + 1024 + sizeof(bmp_hdr); *(uint32 *)(my_hdr + 18) = w; *(uint32 *)(my_hdr + 22) = h; out.write(my_hdr, sizeof(my_hdr)); for (i = 0; i != 256; i++, palette += 3) { byte color[4]; color[0] = palette[2]; color[1] = palette[1]; color[2] = palette[0]; color[3] = 0; out.write(color, 4); } while (--h >= 0) { out.write(bytes + h * ((w + 3) & ~3), ((w + 3) & ~3)); } } void AGOSEngine::dumpBitmap(const char *filename, const byte *offs, uint16 w, uint16 h, int flags, const byte *palette, byte base) { byte *imageBuffer = (byte *)malloc(w * h); assert(imageBuffer); VC10_state state; state.depack_cont = -0x80; state.srcPtr = offs; state.dh = h; state.height = h; state.width = w / 16; if (getFeatures() & GF_PLANAR) { state.srcPtr = convertImage(&state, (getGameType() == GType_PN || (flags & 0x80) != 0)); flags &= ~0x80; } const byte *src = state.srcPtr; byte *dst = imageBuffer; int i, j; if (w > _screenWidth) { for (i = 0; i < w; i += 8) { decodeColumn(dst, src + readUint32Wrapper(src), h, w); dst += 8; src += 4; } } else if (h > _screenHeight) { for (i = 0; i < h; i += 8) { decodeRow(dst, src + readUint32Wrapper(src), w, w); dst += 8 * w; src += 4; } } else if (getGameType() == GType_FF || getGameType() == GType_PP) { if ((flags & 0x80)) { for (i = 0; i != w; i++) { byte *c = vc10_depackColumn(&state); for (j = 0; j != h; j++) { dst[j * w + i] = c[j]; } } } else { for (j = 0; j != h; j++) { for (i = 0; i != w; i++) { dst[i] = src[i]; } } dst += w; src += w; } } else if ((getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2) && w == 320 && (h == 134 || h == 135 || h == 200)) { for (j = 0; j != h; j++) { uint16 count = w / 8; byte *dstPtr = dst; do { uint32 bits = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | (src[3]); dstPtr[0] = (byte)((bits >> (32 - 5)) & 31); dstPtr[1] = (byte)((bits >> (32 - 10)) & 31); dstPtr[2] = (byte)((bits >> (32 - 15)) & 31); dstPtr[3] = (byte)((bits >> (32 - 20)) & 31); dstPtr[4] = (byte)((bits >> (32 - 25)) & 31); dstPtr[5] = (byte)((bits >> (32 - 30)) & 31); bits = (bits << 8) | src[4]; dstPtr[6] = (byte)((bits >> (40 - 35)) & 31); dstPtr[7] = (byte)((bits) & 31); dstPtr += 8; src += 5; } while (--count); dst += w; } } else if (flags & 0x80) { for (i = 0; i != w; i += 2) { byte *c = vc10_depackColumn(&state); for (j = 0; j != h; j++) { byte col = c[j]; dst[j * w + i] = (col >> 4) | base; dst[j * w + i + 1] = (col & 0xF) | base; } } } else { for (j = 0; j != h; j++) { for (i = 0; i != w / 2; i ++) { byte col = src[i]; dst[i * 2] = (col >> 4) | base; dst[i * 2 + 1] = (col & 0xF) | base; } dst += w; src += w / 2; } } dumpBMP(filename, w, h, imageBuffer, palette); free(imageBuffer); } void AGOSEngine::dumpSingleBitmap(int file, int image, const byte *offs, int w, int h, byte base) { char buf[40]; sprintf(buf, "dumps/File%d_Image%d.bmp", file, image); if (Common::File::exists(buf)) return; dumpBitmap(buf, offs, w, h, 0, _displayPalette, base); } void AGOSEngine::palLoad(byte *pal, const byte *vga1, int a, int b) { const byte *src; uint16 num, palSize; byte *palptr = (byte *)&pal[0]; if (getGameType() == GType_FF || getGameType() == GType_PP) { num = 256; palSize = 768; } else { num = 32; palSize = 96; } if (getGameType() == GType_PN && (getFeatures() & GF_EGA)) { memcpy(palptr, _displayPalette, 3 * 16); } else if (getGameType() == GType_PN || getGameType() == GType_ELVIRA1 || getGameType() == GType_ELVIRA2 || getGameType() == GType_WW) { src = vga1 + READ_BE_UINT16(vga1 + 6) + b * 32; do { uint16 color = READ_BE_UINT16(src); palptr[0] = ((color & 0xf00) >> 8) * 32; palptr[1] = ((color & 0x0f0) >> 4) * 32; palptr[2] = ((color & 0x00f) >> 0) * 32; palptr += 3; src += 2; } while (--num); } else { src = vga1 + 6 + b * palSize; do { palptr[0] = src[0] << 2; palptr[1] = src[1] << 2; palptr[2] = src[2] << 2; palptr += 3; src += 3; } while (--num); } } void AGOSEngine::dumpVgaBitmaps(uint16 zoneNum) { uint16 width, height, flags; uint32 offs, offsEnd; const byte *p2; byte pal[768]; uint16 zone = (getGameType() == GType_PN) ? 0 : zoneNum; VgaPointersEntry *vpe = &_vgaBufferPointers[zone]; if (vpe->vgaFile1 == NULL || vpe->vgaFile2 == NULL) return; const byte *vga1 = vpe->vgaFile1; const byte *vga2 = vpe->vgaFile2; uint32 imageBlockSize = vpe->vgaFile2End - vpe->vgaFile2; memset(pal, 0, sizeof(pal)); palLoad(pal, vga1, 0, 0); offsEnd = readUint32Wrapper(vga2 + 8); for (uint i = 1; ; i++) { if ((i * 8) >= offsEnd) break; p2 = vga2 + i * 8; offs = readUint32Wrapper(p2); width = readUint16Wrapper(p2 + 6); if (getGameType() == GType_FF || getGameType() == GType_PP) { height = READ_LE_UINT16(p2 + 4) & 0x7FFF; flags = p2[5]; } else { height = p2[5]; flags = p2[4]; } debug(1, "Zone %d: Image %d. Offs= %d Width=%d, Height=%d, Flags=0x%X", zoneNum, i, offs, width, height, flags); if (offs >= imageBlockSize || width == 0 || height == 0) break; /* dump bitmap */ char buf[40]; sprintf(buf, "dumps/Res%d_Image%d.bmp", zoneNum, i); dumpBitmap(buf, vga2 + offs, width, height, flags, pal, 0); } } } // End of namespace AGOS