/* * QT AGI Studio :: Copyright (C) 2000 Helen Zommer * * Almost all of this code was adapted from the Windows AGI Studio * developed by Peter Kelly. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include "game.h" #include "logedit.h" #include "words.h" #include "object.h" #include "menu.h" #include "agicommands.h" #include #include static int EncryptionStart; static int MessageSectionStart,MessageSectionEnd; static bool MessageUsed[256],MessageExists[256]; static int ResPos; static byte CurByte; static int NumMessages; static string Messages[MaxMessages]; static byte ThisCommand; bool ShowArgTypes = true; bool ShowNonExistingValues = true; // Uses the number of an object, word or message instead of the text if it does not exist byte SpecialSyntaxType = 1; // 0 for v30 = v30 + 4;, 1 for v30 += 4; static byte BlockDepth; static short BlockEnd[MaxBlockDepth+1]; static short BlockLength[MaxBlockDepth+1]; static bool BlockIsIf[MaxBlockDepth+1]; static short TempBlockLength,CurBlock; static byte CurArg; static unsigned int ArgsStart; static TResource LabelIndex; static int LabelLoc,NumLabels; static bool DoGoto; static string ThisLine; static bool ErrorOccured; static bool FirstCommand, OROn , NOTOn; static byte NumSaidArgs; static byte IndentPos; //*************************************************** static byte ReadByte(void) { if(ResPos < ResourceData.Size){ return ResourceData.Data[ResPos++]; } return 0; } //*************************************************** static short ReadLSMSWord(void) { byte MSbyte,LSbyte; LSbyte = ReadByte(); MSbyte = ReadByte(); return ((MSbyte<<8)|LSbyte); } //*************************************************** static byte ReadEncByte(void) { return (ReadByte() ^ EncryptionKey[(ResPos-EncryptionStart+10)%11]) ; } //*************************************************** void Logic::ReadMessages(void) { int MessageStart[256]; string ThisMessage; int i; // NOTE: There is no message 0 (this is not supported by the file format). for(i=0;i 0){ MessageSectionEnd = ReadLSMSWord() + MessageSectionStart; for (i = 1;i <= NumMessages;i++){ MessageStart[i] = ReadLSMSWord(); } EncryptionStart = ResPos; for (i = 1;i <= NumMessages;i++){ if (MessageStart[i] > 0){ ThisMessage = ""; ResPos = MessageSectionStart + MessageStart[i] + 1; do{ CurByte = ReadEncByte(); if (CurByte == 0 || ResPos >= ResourceData.Size)break; if(CurByte == 0x0a)ThisMessage += "\\n"; else if (CurByte == 0x22)ThisMessage += "\\\""; else if (CurByte == 0x5c)ThisMessage += "\\\\"; else ThisMessage += CurByte; }while(true); Messages[i]=ThisMessage; MessageExists[i] = true; } } } } //*************************************************** void Logic::DisplayMessages(void) { int i; if(game->show_all_messages){ OutputText.append("// Messages\n"); for(i=1;i<=255;i++){ if (MessageExists[i]){ OutputText.append("#message " + IntToStr(i) + " \""+Messages[i]+"\"\n"); } } } else{ //(only those not used elsewhere in the logic are here) OutputText.append("// Messages\n"); for(i=1;i<=255;i++){ if (MessageExists[i] && !MessageUsed[i]){ OutputText.append("#message " + IntToStr(i)+" \""+Messages[i]+"\"\n"); } } } } //*************************************************** int Logic::FindLabels_ReadIfs(void) { byte NumSaidArgs; do{ CurByte = ReadByte(); if(CurByte == 0xFC)CurByte = ReadByte(); if(CurByte == 0xFC)CurByte = ReadByte(); // we may have 2 0xFCs in a row, e.g. (a || b) && (c || d) if(CurByte == 0xFD)CurByte = ReadByte(); if (CurByte > 0 && CurByte <= NumTestCommands){ ThisCommand = CurByte; if (ThisCommand == 14){ // said command NumSaidArgs = ReadByte(); ResPos += NumSaidArgs*2; } else{ ResPos += TestCommand[ThisCommand].NumArgs; } } else if(CurByte == 0xFF){ if (BlockDepth >= MaxBlockDepth - 1){ sprintf(tmp,"Too many nested blocks (%d)\n",BlockDepth); ErrorList.append(tmp); ErrorOccured=true; break; } BlockDepth++; BlockIsIf[BlockDepth] = true; BlockLength[BlockDepth] = ReadLSMSWord(); BlockEnd[BlockDepth] = BlockLength[BlockDepth] + ResPos; if (BlockEnd[BlockDepth] > BlockEnd[BlockDepth-1]){ sprintf(tmp,"Block too long (%d bytes longer than rest of previous block)",BlockEnd[BlockDepth]-BlockEnd[BlockDepth-1]); ErrorOccured=true; ErrorList.append(string(tmp)+"\n"); } break; } else{ sprintf(tmp,"Unknown test command (%d)",CurByte); break; } }while(true); return 0; } //*************************************************** void Logic::AddBlockEnds(void) { for(int CurBlock = BlockDepth;CurBlock>=1;CurBlock--){ if (BlockEnd[CurBlock] <= ResPos){ OutputText.append(MultStr(" ",CurBlock-1)+"}\n"); BlockDepth--; } } } //*************************************************** int Logic::FindLabels(void) { LabelIndex.Size = ResourceData.Size; LabelIndex.Data = (byte *)calloc(LabelIndex.Size,1); BlockDepth = 0; NumLabels = 0; do{ for( CurBlock = BlockDepth;CurBlock>=1;CurBlock--){ if (BlockEnd[CurBlock] <= ResPos)BlockDepth--; } CurByte = ReadByte(); if (CurByte == 0xFF)FindLabels_ReadIfs(); else if(CurByte <= NumAGICommands){ ResPos += AGICommand[CurByte].NumArgs; } else if(CurByte == 0xFE){ DoGoto = false; TempBlockLength = ReadLSMSWord(); if ((BlockEnd[BlockDepth] == ResPos) && (BlockIsIf[BlockDepth]) && (BlockDepth > 0) && (!game->show_elses_as_gotos)){ BlockIsIf[BlockDepth] = false; if (TempBlockLength + ResPos > BlockEnd[BlockDepth-1] || (TempBlockLength & 0x8000) || BlockLength[BlockDepth] <= 3){ DoGoto = true; } else{ BlockLength[BlockDepth] = TempBlockLength; BlockEnd[BlockDepth] = BlockLength[BlockDepth] + ResPos; } } else{ DoGoto = true; } //goto if (DoGoto){ LabelLoc = TempBlockLength + ResPos; if (LabelLoc > LabelIndex.Size - 1){ sprintf(tmp,"Label past end of logic (%x %x)\n ",LabelLoc,LabelIndex.Size); ErrorList.append(tmp); ErrorOccured=true; break; } if (LabelIndex.Data[LabelLoc] == 0){ NumLabels++; LabelIndex.Data[LabelLoc] = NumLabels; } } } else{ sprintf(tmp,"Unknown command (%d)",CurByte); break; } }while(ResPos < MessageSectionStart); return 0; } //*************************************************** void Logic::AddArg(byte Arg, byte ArgType) { int NumCharsToDisplay; string ThisMessage; if(ShowArgTypes){ switch(ArgType){ case atMsg: if (MessageExists[Arg]){ string ThisMessage = Messages[Arg]; do{ if(ThisMessage.length() + ThisLine.length() > maxcol){ NumCharsToDisplay = maxcol - ThisLine.length(); do{ NumCharsToDisplay--; }while(!(NumCharsToDisplay <= 0 || ThisMessage[NumCharsToDisplay]==' ')); if (NumCharsToDisplay <= 0) NumCharsToDisplay = maxcol-ThisLine.length(); if (NumCharsToDisplay <= 0) NumCharsToDisplay = ThisMessage.length(); ThisLine += "\"" + ThisMessage.substr(0,NumCharsToDisplay) + "\""; if(NumCharsToDisplay < (int)ThisMessage.length()){ ThisMessage = ThisMessage.substr(NumCharsToDisplay+1); OutputText.append(ThisLine+"\n"); } else{ ThisMessage = ""; OutputText.append(ThisLine); } if (ArgsStart >= maxcol - 20)ArgsStart = maxcol - 20; ThisLine = MultStr(" ",ArgsStart); } else{ ThisLine += "\"" + ThisMessage + "\"" ; ThisMessage = ""; } }while(ThisMessage.length()>0); } else if(ShowNonExistingValues){ ThisLine += ArgTypePrefix[atMsg] + IntToStr(Arg); } else{ sprintf(tmp,"Unknown message (%d)\n",Arg); ErrorList.append(tmp); ErrorOccured=true; } MessageUsed[Arg] = true; break; case atIObj: if (Arg <= objlist->ItemNames.num - 1){ ThisLine += "\"" + objlist->ItemNames.at(Arg) + "\""; } else if(ShowNonExistingValues){ ThisLine += ArgTypePrefix[atIObj] + IntToStr(Arg); } else{ sprintf(tmp,"Unknown inventory item (%d)\n",Arg); ErrorList.append(tmp); ErrorOccured=true; } break; default: if(ArgType != 0){ ThisLine += ArgTypePrefix[ArgType] + IntToStr(Arg); } else{ ThisLine += IntToStr(Arg); } break; } } else{ ThisLine += IntToStr(Arg); } } //*************************************************** void Logic::AddSpecialSyntaxCommand(void) { int arg1; arg1 = ReadByte(); switch(ThisCommand){ // increment case 0x01: ThisLine += "v"+IntToStr(arg1)+"++"; break; // decrement case 0x02: ThisLine += "v"+IntToStr(arg1)+"--"; break; // assignn case 0x03: ThisLine += "v"+IntToStr(arg1)+" = "+IntToStr(ReadByte()); break; // assignv case 0x04: ThisLine += "v"+IntToStr(arg1)+" = v"+IntToStr(ReadByte()); break; // addn case 0x05: if (SpecialSyntaxType == 0) { ThisLine += "v"+IntToStr(arg1)+" = v"+IntToStr(arg1)+" + "+IntToStr(ReadByte()); } else { ThisLine += "v"+IntToStr(arg1)+" += "+IntToStr(ReadByte()); } break; // addv case 0x06: if (SpecialSyntaxType == 0) ThisLine += "v"+IntToStr(arg1)+" = v"+IntToStr(arg1)+" + v"+IntToStr(ReadByte()); else { ThisLine += "v"+IntToStr(arg1)+" += v"+IntToStr(ReadByte()); } break; // subn case 0x07: if (SpecialSyntaxType == 0) { ThisLine += "v"+IntToStr(arg1)+" = v"+IntToStr(arg1)+" - "+IntToStr(ReadByte()); } else { ThisLine += "v"+IntToStr(arg1)+" -= "+IntToStr(ReadByte()); } break; // subv case 0x08: if (SpecialSyntaxType == 0) { ThisLine += "v"+IntToStr(arg1)+" = v"+IntToStr(arg1)+" - v"+IntToStr(ReadByte()); } else { ThisLine += "v"+IntToStr(arg1)+" -= v"+IntToStr(ReadByte()); } break; // lindirectv case 0x09: ThisLine += "*v"+IntToStr(arg1)+" = v"+IntToStr(ReadByte()); break; // rindirect case 0x0A: ThisLine += "v"+IntToStr(arg1)+" = *v"+IntToStr(ReadByte()); break; // lindirectn case 0x0B: ThisLine += "*v"+IntToStr(arg1)+" = "+IntToStr(ReadByte()); break; // mul.n case 0xA5: if (SpecialSyntaxType == 0) { ThisLine += "v"+IntToStr(arg1)+" = v"+IntToStr(arg1)+" * "+IntToStr(ReadByte()); } else { ThisLine += "v"+IntToStr(arg1)+" *= "+IntToStr(ReadByte()); } break; // mul.v case 0xA6: if (SpecialSyntaxType == 0) { ThisLine += "v"+IntToStr(arg1)+" = v"+IntToStr(arg1)+" * v"+IntToStr(ReadByte()); } else { ThisLine += "v"+IntToStr(arg1)+" *= v"+IntToStr(ReadByte()); } break; // div.n case 0xA7: if (SpecialSyntaxType == 0) { ThisLine += "v"+IntToStr(arg1)+" = v"+IntToStr(arg1)+" / "+IntToStr(ReadByte()); } else { ThisLine += "v"+IntToStr(arg1)+" /= "+IntToStr(ReadByte()); } break; // div.v case 0xA8: if (SpecialSyntaxType == 0) { ThisLine += "v"+IntToStr(arg1)+" = v"+IntToStr(arg1)+" / v"+IntToStr(ReadByte()); } else { ThisLine += "v"+IntToStr(arg1)+" /= v"+IntToStr(ReadByte()); } break; } } //*************************************************** void Logic::AddSpecialIFSyntaxCommand(void) { switch(ThisCommand){ case 1: // equaln ThisLine += 'v' + IntToStr(ReadByte()); if (NOTOn) ThisLine += " != "; else ThisLine += " == "; ThisLine += IntToStr(ReadByte()); break; case 2: // equalv ThisLine += 'v' + IntToStr(ReadByte()); if (NOTOn) ThisLine += " != v"; else ThisLine += " == v"; ThisLine += IntToStr(ReadByte()); break; case 3: // lessn ThisLine += 'v' + IntToStr(ReadByte()); if (NOTOn) ThisLine += " >= "; else ThisLine += " < "; ThisLine += IntToStr(ReadByte()); break; case 4: // lessv ThisLine += 'v' + IntToStr(ReadByte()); if (NOTOn) ThisLine += " >= v"; else ThisLine += " < v"; ThisLine += IntToStr(ReadByte()); break; case 5: // greatern ThisLine += 'v' + IntToStr(ReadByte()); if (NOTOn) ThisLine += " <= "; else ThisLine += " > "; ThisLine += IntToStr(ReadByte()); break; case 6: // greaterv ThisLine += 'v' + IntToStr(ReadByte()); if (NOTOn) ThisLine += " <= v"; else ThisLine += " > v"; ThisLine += IntToStr(ReadByte()); break; } } //*************************************************** void Logic::ReadIfs(void) { int ThisWordGroupIndex, ThisWordGroupNum; FirstCommand = true; OROn = false; ThisLine = MultStr(" ",BlockDepth)+"if ("; do{ NOTOn = false; CurByte = ReadByte(); if (CurByte == 0xFC){ OROn = !OROn; if (OROn){ if (!FirstCommand){ ThisLine += " &&"; OutputText.append(ThisLine+"\n"); ThisLine = MultStr(" ",BlockDepth)+" "; FirstCommand = true; } ThisLine += '('; } else ThisLine += ')'; CurByte = ReadByte(); } if(CurByte == 0xFC && !OROn){ // we may have 2 0xFCs in a row, e.g. (a || b) && (c || d) ThisLine += " &&"; OutputText.append(ThisLine+"\n"); ThisLine = MultStr(" ",BlockDepth)+" "; FirstCommand = true; ThisLine += "("; OROn = true; CurByte = ReadByte(); } if (CurByte == 0xFD){ // NOT NOTOn = true; CurByte = ReadByte(); } if (CurByte > 0 && CurByte <= NumTestCommands){ if(!FirstCommand){ if (OROn) ThisLine += " ||"; else ThisLine += " &&"; OutputText.append(ThisLine+"\n"); ThisLine = MultStr(" ",BlockDepth)+" "; } ThisCommand = CurByte; if (game->show_special_syntax && ThisCommand >=1 && ThisCommand <=6) AddSpecialIFSyntaxCommand(); else{ if (NOTOn) ThisLine += '!'; ThisLine += string(TestCommand[ThisCommand].Name) + '('; ArgsStart = ThisLine.length(); if (ThisCommand == 14){ // said command NumSaidArgs = ReadByte(); for (CurArg = 1;CurArg<=NumSaidArgs; CurArg++){ ThisWordGroupNum = ReadLSMSWord(); ThisWordGroupIndex = wordlist->GetWordGroupIndex(ThisWordGroupNum); if (ThisWordGroupIndex < 0){ if (ShowNonExistingValues){ ThisLine += IntToStr(ThisWordGroupNum); } else{ sprintf(tmp,"Unknown word group (%d)\n",ThisWordGroupNum); ErrorList.append(tmp); ErrorOccured=true; break; } } else{ ThisLine += '"' + string(wordlist->WordGroup[ThisWordGroupIndex].Words.at(0)) + '"'; if (CurArg < NumSaidArgs)ThisLine += ','; } } } else{ for (CurArg = 0; CurArg < TestCommand[ThisCommand].NumArgs; CurArg++){ CurByte = ReadByte(); AddArg(CurByte,TestCommand[ThisCommand].argTypes[CurArg]); if (CurArg < TestCommand[ThisCommand].NumArgs-1)ThisLine += ','; } } // if ThisCommand != 14 ThisLine += ')'; } FirstCommand = false; }//if (CurByte > 0) && (CurByte <= NumTestCommands) else if (CurByte == 0xff){ ThisLine += ") {"; if (BlockDepth >= MaxBlockDepth - 1){ sprintf(tmp,"Too many nested blocks (%d)\n",BlockDepth); ErrorList.append(tmp); ErrorOccured=true; break; } else{ BlockDepth++; BlockIsIf[BlockDepth] = true; BlockLength[BlockDepth] = ReadLSMSWord(); BlockEnd[BlockDepth] = BlockLength[BlockDepth] + ResPos; if (BlockEnd[BlockDepth] > BlockEnd[BlockDepth-1]){ sprintf(tmp,"Block too long (%d bytes longer than rest of previous block)\n",BlockEnd[BlockDepth]-BlockEnd[BlockDepth-1]); ErrorList.append(tmp); ErrorOccured=true; break; } } OutputText.append(ThisLine+"\n"); ThisLine = MultStr(" ",BlockDepth); break; }// if CurByte = 0xFF else{ sprintf(tmp,"Unknown test command (%d)\n",CurByte); ErrorList.append(tmp); ErrorOccured=true; break; } }while(!ErrorOccured); } //*************************************************** int Logic::decode(int ResNum) { int ret=0,i,j; OutputText = ""; sprintf(tmp,"%s/words.tok",game->dir.c_str()); ret = wordlist->read(tmp); if(ret)return 1; sprintf(tmp,"%s/object",game->dir.c_str()); ret = objlist->read(tmp,false); if(ret)return 1; objlist->ItemNames.toLower(); // words already in lower case in file so we don't need to convert them for(i=0;iItemNames.num;i++){ if(objlist->ItemNames.at(i).find_first_of("\"")==string::npos)continue; //replace " with \" char *ptr=(char *)objlist->ItemNames.at(i).c_str(); for(j=0;*ptr;ptr++){ if(*ptr=='"'){ tmp[j++]='\\'; tmp[j++]='"'; } else tmp[j++]=*ptr; } tmp[j]=0; objlist->ItemNames.replace(i,tmp); } ret = game->ReadResource(LOGIC,ResNum); if(ret)return 1; ErrorList=""; ResPos = 0; MessageSectionStart = ReadLSMSWord() + 2; if (MessageSectionStart > ResourceData.Size - 1){ sprintf(tmp,"Error: Message section start %x is beyond end of resource\n", MessageSectionStart); ErrorList.append(tmp); return 1; } ErrorOccured=false; ReadMessages(); ResPos = 2; BlockEnd[0] = MessageSectionStart; BlockIsIf[0] = false; memset(BlockIsIf,0,sizeof(BlockIsIf)); FindLabels(); BlockDepth = 0; ResPos = 2; do{ AddBlockEnds(); if (LabelIndex.Data[ResPos] > 0){ OutputText.append("Label" + IntToStr(LabelIndex.Data[ResPos]) + ":\n"); } CurByte = ReadByte(); if(CurByte == 0xFF)ReadIfs(); else if(CurByte <= NumAGICommands){ ThisCommand = CurByte; ThisLine = MultStr(" ",BlockDepth); if (game->show_special_syntax && ((ThisCommand>=0x01 && ThisCommand<=0x0B)||(ThisCommand>=0xA5&&ThisCommand<=0xA8)))AddSpecialSyntaxCommand(); else{ ThisLine+=(string(AGICommand[ThisCommand].Name) + "("); ArgsStart = ThisLine.length(); IndentPos = ThisLine.length(); for(CurArg = 1;CurArg<=AGICommand[ThisCommand].NumArgs;CurArg++){ CurByte = ReadByte(); AddArg(CurByte,AGICommand[ThisCommand].argTypes[CurArg-1]); if(CurArg < AGICommand[ThisCommand].NumArgs)ThisLine+=","; } ThisLine+=")"; } ThisLine+=";"; OutputText.append(ThisLine+"\n"); } else if(CurByte == 0xfe){ DoGoto = false; TempBlockLength = ReadLSMSWord(); if (BlockEnd[BlockDepth] == ResPos && (BlockIsIf[BlockDepth]) && BlockDepth > 0 && (!game->show_elses_as_gotos)){ //else BlockIsIf[BlockDepth] = false; if (TempBlockLength + ResPos > BlockEnd[BlockDepth-1] || TempBlockLength & 0x8000 || BlockLength[BlockDepth] <= 3){ DoGoto = true; } else{ OutputText.append(MultStr(" ",BlockDepth-1) + "}\n"); OutputText.append(MultStr(" ",BlockDepth-1) + "else {\n"); BlockLength[BlockDepth] = TempBlockLength; BlockEnd[BlockDepth] = BlockLength[BlockDepth] + ResPos; } } else DoGoto = true; // goto if (DoGoto){ LabelLoc = TempBlockLength + ResPos; if (LabelLoc > LabelIndex.Size - 1){ sprintf(tmp,"Label past end of logic (%x %x)\n ",LabelLoc,LabelIndex.Size); ErrorList.append(tmp); ErrorOccured=true; break; } else{ OutputText.append(MultStr(" ",BlockDepth) + "goto(Label"+IntToStr(LabelIndex.Data[LabelLoc])+");\n"); } } } else{ sprintf(tmp,"Unknown action command (%d)\n",CurByte); ErrorList.append(tmp); ErrorOccured = true; break; } }while(ResPos < MessageSectionStart); if(!ErrorOccured)AddBlockEnds(); free(LabelIndex.Data); OutputText.append("\n"); DisplayMessages(); return (ErrorOccured)?1:0; }