// ScriptEdit.cpp : implementation file
//
#include "stdafx.h"
#include <fcntl.h>
#include <sys/stat.h>
#include <direct.h>
#include "2da.h"
#include "chitemDlg.h"
#include "ScriptEdit.h"
#include "2daEdit.h"
#include "options.h"
#include "compat.h"
#include "WeiduLog.h"
#include "StrRefDlg.h"
#include "Decompiler.h"
#include "Compiler.h"
#include "MyFileDialog.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define SHOW_MESSAGES 1
#define FORCED_CHECK 2
#define WHOLE_CHECK 4
#define MAX_SCRIPT_LEN 6553599
#define LINES 30
//static function
//displays compiler data in human readable format
CString format_parameters(compiler_data &compiler_data)
{
CString ret, add;
int i;
for(i=0;i<compiler_data.parnum;i++)
{
switch(compiler_data.parameters[i].type)
{
case SPT_ZERO: add="0"; break;
case SPT_1AREA: case SPT_2AREA:
case SPT_AREA1: case SPT_AREA2: case SPT_AREA3: case SPT_AREA4: add="qualifier"; break;
case SPT_VAR1: case SPT_VAR2: case SPT_VAR3: case SPT_VAR4: add="variable"; break;
case SPT_OBJECT:case SPT_OVERRIDE: case SPT_SECONDOB: add="object"; break;
case SPT_INTEGER:case SPT_INTEGER2:case SPT_INTEGER3:
if(compiler_data.parameters[i].idsfile.IsEmpty()) add="integer";
else add="ids:"+compiler_data.parameters[i].idsfile;
break;
/*case SPT_STRING:*/case SPT_STRING1: case SPT_STRING2: add="string"; break;
case SPT_STRREF:case SPT_STRREF2: case SPT_STRREF3: add="strref"; break;
case SPT_ACTION: add="action"; break;
case SPT_POINT: add="point"; break;
case SPT_RES1:case SPT_RES2:case SPT_RES3:case SPT_RES4:
case SPT_RESREF1: case SPT_RESREF2: add="resource"; break;
case SPT_DEAD1: case SPT_DEAD2: add="deathvar"; break;
case SPT_COLUMN1: case SPT_COLUMN2: add="xplist"; break;
case SPT_TOKEN1: case SPT_TOKEN2: add="token"; break;
case SPT_NUMLIST1: case SPT_NUMLIST2: add="spellist"; break;
default: add="unknown";
}
if(i) ret+=", "+add;
else ret+=add;
}
return ret;
}
//a rather lame windows-style way of outputting a richedit control
unsigned long __stdcall writestream_callback(unsigned long cookie, LPBYTE buffer, long cb, long *pcb)
{
int res;
res=write(cookie,buffer,cb)!=cb;
*pcb=cb;
return res;
}
int writestream(int fhandle, CRichEditCtrl &ctrl)
{
EDITSTREAM es;
es.dwCookie=fhandle;
es.dwError=0;
es.pfnCallback=writestream_callback;
ctrl.StreamOut(SF_TEXT,es);
return es.dwError;
}
/////////////////////////////////////////////////////////////////////////////
// CScriptEdit dialog
CScriptEdit::CScriptEdit(CWnd* pParent /*=NULL*/)
: CDialog(CScriptEdit::IDD, pParent)
{
//{{AFX_DATA_INIT(CScriptEdit)
//}}AFX_DATA_INIT
m_errors=NULL;
m_count=0;
m_firsterror=0;
m_bcs=1;
m_searchdlg=NULL;
}
void CScriptEdit::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CScriptEdit)
DDX_Control(pDX, IDC_TEXT, m_text_control);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CScriptEdit, CDialog)
//{{AFX_MSG_MAP(CScriptEdit)
ON_BN_CLICKED(IDC_LOAD, OnLoad)
ON_BN_CLICKED(IDC_LOADEX, OnLoadex)
ON_BN_CLICKED(IDC_SAVEAS, OnSaveas)
ON_BN_CLICKED(IDC_NEW, OnNew)
ON_EN_SETFOCUS(IDC_TEXT, OnSetfocusText)
ON_BN_CLICKED(IDC_CHECK, OnCheck)
ON_COMMAND(ID_FILE_SAVESCRIPT, OnSaveAsBAF)
ON_COMMAND(ID_FILE_SAVECOMPILESCRIPT, OnSaveAsBCS)
ON_COMMAND(ID_FILE_LOADSOURCE, OnFileLoadsource)
ON_COMMAND(ID_SEARCH, OnSearch)
ON_EN_KILLFOCUS(IDC_TEXT, OnKillfocusText)
ON_EN_MAXTEXT(IDC_TEXT, OnMaxtextText)
ON_COMMAND(ID_OPTIONS_AUTOCHECK, OnOptionsAutocheck)
ON_COMMAND(ID_OPTIONS_LARGEINDENT, OnOptionsLargeindent)
ON_COMMAND(ID_EDIT_SELECTALL, OnEditSelectall)
ON_COMMAND(ID_EDIT_UNDO, OnEditUndo)
ON_COMMAND(ID_EDIT_CUT, OnEditCut)
ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
ON_COMMAND(ID_EDIT_PASTE, OnEditPaste)
ON_COMMAND(ID_EDIT_DELETE, OnEditDelete)
ON_WM_CONTEXTMENU()
ON_COMMAND(ID_TOOLS_IDSBROWSER, OnIDS)
ON_WM_HELPINFO()
ON_EN_CHANGE(IDC_TEXT, OnChangeText)
ON_EN_UPDATE(IDC_TEXT, OnUpdateText)
ON_COMMAND(ID_TOOLS_LOOKUPSTRREF, OnToolsLookupstrref)
ON_COMMAND(ID_OPTIONS_USEINTERNALCOMPILER, OnOptionsUseinternalcompiler)
ON_COMMAND(ID_OPTIONS_USEINTERNALDECOMPILER, OnOptionsUseinternaldecompiler)
ON_COMMAND(ID_FILE_NEW, OnNew)
ON_COMMAND(ID_FILE_LOAD, OnLoad)
ON_COMMAND(ID_FILE_SAVEAS, OnSaveas)
ON_COMMAND(ID_FILE_LOADEXTERNALSCRIPT, OnLoadex)
ON_COMMAND(ID_CHECK, OnCheck)
ON_COMMAND(ID_OPTIONS_LOGGING, OnOptionsLogging)
//}}AFX_MSG_MAP
ON_REGISTERED_MESSAGE( WM_FINDREPLACE, OnFindReplace )
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CScriptEdit message handlers
BOOL CScriptEdit::OnInitDialog()
{
CFont Font;
CString tmpstr,tmpstr1,tmpstr2;
the_ids.new_ids();
m_idsname="";
if(editflg&INDENT) m_indent=2;
else m_indent=4;
CDialog::OnInitDialog();
m_text_control.LimitText(MAX_SCRIPT_LEN);
m_text_control.ModifyStyle(0,ES_SAVESEL);
m_text_control.SetOptions(ECOOP_OR,ECO_AUTOWORDSELECTION|ECO_SAVESEL|ECO_WANTRETURN|ECO_NOHIDESEL);
//enables the update event for a rich edit control
m_text_control.SetEventMask(ENM_CHANGE|ENM_UPDATE|ENM_MOUSEEVENTS|ENM_KEYEVENTS|ENM_SCROLL|ENM_SELCHANGE);
Font.CreateFont(15,0,0,0,0,0,0,0,0,0,0,0,0,"Courier New");
m_text_control.SetFont(&Font);
Font.DeleteObject();
// UpdateData(UD_DISPLAY);
CheckScript(WHOLE_CHECK|FORCED_CHECK);
RefreshDialog();
RefreshMenu();
m_infind=0;
//tooltips
{
m_tooltip.Create(this,TTS_NOPREFIX);
m_tooltip.SetMaxTipWidth(200);
m_tooltip.SetTipBkColor(RGB(240,224,160));
m_tooltip.AddTool(GetDlgItem(IDCANCEL), IDS_CANCEL);
tmpstr1.LoadString(IDS_LOAD);
tmpstr2.LoadString(IDS_SCRIPT);
tmpstr.Format(tmpstr1, tmpstr2);
m_tooltip.AddTool(GetDlgItem(IDC_LOAD), tmpstr);
tmpstr1.LoadString(IDS_LOADEX);
tmpstr.Format(tmpstr1, tmpstr2);
m_tooltip.AddTool(GetDlgItem(IDC_LOADEX), tmpstr);
tmpstr1.LoadString(IDS_SAVE);
tmpstr.Format(tmpstr1, tmpstr2);
m_tooltip.AddTool(GetDlgItem(IDC_SAVEAS), tmpstr);
tmpstr1.LoadString(IDS_NEW);
tmpstr.Format(tmpstr1, tmpstr2);
m_tooltip.AddTool(GetDlgItem(IDC_NEW), tmpstr);
tmpstr1.LoadString(IDS_CHECK);
tmpstr.Format(tmpstr1, tmpstr2);
m_tooltip.AddTool(GetDlgItem(IDC_CHECK), tmpstr);
}
return TRUE;
}
int CScriptEdit::RunWeidu(CString syscommand)
{
CString tmpstr;
int res;
//flushing dialog.tlk
if(global_changed[0]==true)
{
((CChitemDlg *) AfxGetMainWnd())->write_file_progress(0);
}
if(global_changed[1]==true)
{
((CChitemDlg *) AfxGetMainWnd())->write_file_progress(1);
}
if(weiduflg&WEI_LOGGING)
{
unlink(WEIDU_LOG);
}
res=my_system(syscommand);
if(weiduflg&WEI_LOGGING)
{
if(file_exists(WEIDU_LOG) )
{
CWeiduLog dlg;
dlg.DoModal();
}
else MessageBox("No logfile has been created by WeiDU.","Script editor",MB_OK);
}
if(res)
{
tmpstr.Format("[%s] returned: %d",syscommand, res);
MessageBox(tmpstr,"Script editor",MB_OK|MB_ICONSTOP);
}
return res;
}
int CScriptEdit::compile(CString filepath)
{
CString tmpstr;
CString outpath;
CString syscommand;
CString tmpname;
int res;
res=0;
if(editflg&INTERNALCOMPILER)
{
if(weidupath.IsEmpty())
{
MessageBox("Please set up WeiDU before use.","Script editor",MB_OK|MB_ICONSTOP);
return -1;
}
if(!file_exists(weidupath) )
{
MessageBox("WeiDU executable not found.","Script editor",MB_OK|MB_ICONSTOP);
return -1;
}
}
chdir(bgfolder);
outpath.Format("override");
if(!assure_dir_exists(bgfolder+outpath))
{
tmpstr.Format("%s cannot be created as output path.",outpath);
MessageBox(tmpstr,"Dialog editor",MB_OK|MB_ICONSTOP);
return -1;
}
if(editflg&INTERNALCOMPILER)
{
syscommand=AssembleWeiduCommandLine(filepath,outpath, false); //import (compile)
res=RunWeidu(syscommand);
((CChitemDlg *) AfxGetMainWnd())->rescan_dialog(true);
((CChitemDlg *) AfxGetMainWnd())->scan_override();
}
else
{
Compiler *scmp = new Compiler(weiduflg&WEI_LOGGING);
int x = filepath.ReverseFind('\\');
int y = filepath.ReverseFind('/');
if (x>y) y=x;
if (y>=0)
{
tmpname = filepath.Mid(y+1);
}
else
{
tmpname = filepath;
}
y = tmpname.ReverseFind('.');
if (y>0)
{
tmpname=tmpname.Left(y);
}
//setting up compiler with options
res = scmp->Compile(filepath, bgfolder+"\\"+outpath+"\\"+tmpname+".bcs");
//actually this is not needed, delete will close all anyway
delete scmp;
if (res)
{
MessageBox("Compiler error.","Script editor",MB_OK|MB_ICONSTOP);
res = 0;
}
}
return res;
}
//decompiles file and returns the decompiled filename
//filepath is the long path of the compiled file
int CScriptEdit::decompile(CString &filepath, CString tmpname)
{
CString tmpstr;
CString syscommand;
int res;
unlink(bgfolder+weidudecompiled+"\\"+tmpname+".baf");
res=0;
//actually, if the bit is set, it means weidu is used
if(editflg&INTERNALDECOMP)
{
if(weidupath.IsEmpty())
{
MessageBox("Please set up WeiDU before use.","Script editor",MB_OK|MB_ICONSTOP);
return -1;
}
if(!file_exists(weidupath) )
{
MessageBox("WeiDU executable not found.","Script editor",MB_OK|MB_ICONSTOP);
return -1;
}
}
chdir(bgfolder);
if(!assure_dir_exists(bgfolder+weidudecompiled))
{
tmpstr.Format("%s cannot be created as output path.",weidudecompiled);
MessageBox(tmpstr,"Dialog editor",MB_OK|MB_ICONSTOP);
return -1;
}
if(global_changed[0]==true)
{
((CChitemDlg *) AfxGetMainWnd())->write_file_progress(0);
}
if(global_changed[1]==true)
{
((CChitemDlg *) AfxGetMainWnd())->write_file_progress(1);
}
if(editflg&INTERNALDECOMP)
{
syscommand=AssembleWeiduCommandLine(filepath,weidudecompiled, true); //export
res=RunWeidu(syscommand);
}
else
{
Decompiler *sdec = new Decompiler(weiduflg&WEI_LOGGING);
//setting up decompiler with options
res = sdec->Decompile(filepath, bgfolder+weidudecompiled+"\\"+tmpname+".baf");
//actually this is not needed, delete will close all anyway
sdec->CloseAll();
delete sdec;
if (res)
{
MessageBox("Decompiler error.","Script editor",MB_OK|MB_ICONSTOP);
res = 0;
}
}
filepath=bgfolder+weidudecompiled+"\\"+tmpname+".baf";
return res;
}
void CScriptEdit::OnLoad()
{
CString filepath;
CString m_text;
int fhandle;
int res;
char *pos;
pickerdlg.m_restype=REF_BCS;
pickerdlg.m_picked=itemname;
res=pickerdlg.DoModal();
if(res==IDOK)
{
filepath=pickerdlg.m_picked+".bcs";
itemname=pickerdlg.m_picked;
m_bcs=1;
res=decompile(filepath, itemname); //this will decompile the bcs
if(res)
{
MessageBox("Cannot decompile file!","Error",MB_OK);
}
else
{
fhandle=open(filepath, O_RDONLY|O_BINARY);
if(!fhandle)
{
MessageBox("Cannot open file!","Error",MB_OK);
res=-1;
}
if(!res)
{//fhandle will always get initialized if res was 0
res=filelength(fhandle);
if(res>=0)
{
pos=m_text.GetBufferSetLength(res);
if(pos)
{
if(read(fhandle,pos,res)!=res) res=-1;
else res=0;
}
else res=-1;
m_text.ReleaseBuffer();
m_text_control.SetWindowText(m_text);
}
close(fhandle);
}
}
switch(res)
{
case 0:
if((m_bcs==1) && (editflg&REMBAF) )
{//removing decompiled baf
unlink(filepath);
}
break;
default:
MessageBox("Cannot read script!","Error",MB_OK);
OnNew();
return;
}
}
UpdateData(UD_DISPLAY);
the_script.m_changed=false;
CheckScript(WHOLE_CHECK|FORCED_CHECK);
RefreshDialog();
}
//static char BASED_CODE szFiltera[] = "Script files (*.bcs)|*.bcs|Script Source files (*.baf)|*.baf|All files (*.*)|*.*||";
static char BASED_CODE szFilterb[] = "Script Source files (*.baf)|*.baf|Script files (*.bcs)|*.bcs|All files (*.*)|*.*||";
void CScriptEdit::OnLoadex()
{
char BASED_CODE *szFilter;
CString m_text;
CString fpath;
int fhandle;
int res;
char *pos;
res=OFN_FILEMUSTEXIST|OFN_ENABLESIZING|OFN_EXPLORER;
if(readonly) res|=OFN_READONLY;
szFilter=szFilterb;
CMyFileDialog m_getfiledlg(TRUE, m_bcs?"bcs":"baf", m_bcs ? makeitemname(".bcs",0): makeitemname(".baf",1), res, szFilter);
m_getfiledlg.m_ofn.nFilterIndex = m_bcs+1;
if( m_getfiledlg.DoModal() == IDOK )
{
filepath = fpath = m_getfiledlg.GetPathName();
if(checkfile(fpath,"SC") )
{
m_bcs=1;
res=decompile(filepath, m_getfiledlg.GetFileTitle()); //decompile first
if(res)
{
MessageBox("Cannot decompile file: "+fpath+"!","Error",MB_OK);
return;
}
lastopenedoverride=fpath.Left(filepath.ReverseFind('\\'));
}
else
{ //remember the path when opening a script source
m_bcs=0;
lastopenedscript=fpath.Left(filepath.ReverseFind('\\'));
}
fhandle=open(filepath, O_RDONLY|O_BINARY);
if(fhandle<1)
{
MessageBox("Cannot open file!","Error",MB_OK);
return;
}
readonly=m_getfiledlg.GetReadOnlyPref();
res=filelength(fhandle);
if(res>=0)
{
pos=m_text.GetBufferSetLength(res);
if(pos)
{
if(read(fhandle,pos,res)!=res) res=-9;
else res=0;
}
else res=-9;
}
m_text.ReleaseBuffer();
close(fhandle);
m_text_control.SetWindowText(m_text);
switch(res)
{
case 0:
if((m_bcs==1) && (editflg&REMBAF) )
{//removing decompiled baf
unlink(filepath);
}
itemname=m_getfiledlg.GetFileTitle();
itemname.MakeUpper();
break;
case -9: //special error: cannot read decompiled script
MessageBox("Cannot read script!","Error",MB_OK);
default: //that error was already handled elsewhere
OnNew();
return;
}
}
UpdateData(UD_DISPLAY);
the_script.m_changed=false;
CheckScript(WHOLE_CHECK|FORCED_CHECK);
RefreshDialog();
}
void CScriptEdit::OnSaveas()
{
OnSaveasGeneral(-1);
}
void CScriptEdit::OnSaveAsBCS()
{
OnSaveasGeneral(1);
}
void CScriptEdit::OnSaveAsBAF()
{
OnSaveasGeneral(0);
}
void CScriptEdit::OnSaveasGeneral(int type)
{
char BASED_CODE *szFilter;
CString tmpstr;
CString newname;
int fhandle;
int res;
if(readonly)
{
MessageBox("You opened it read only!","Warning",MB_ICONEXCLAMATION|MB_OK);
return;
}
res=OFN_HIDEREADONLY|OFN_ENABLESIZING|OFN_EXPLORER;
if(type!=-1) m_bcs=type;
szFilter=szFilterb;
if(m_bcs)
{
//szFilter=szFiltera;
tmpstr=bgfolder+"override\\"+itemname;
}
else
{
//szFilter=szFilterb;
tmpstr=makeitemname(".baf",1);
}
CMyFileDialog m_getfiledlg(FALSE, m_bcs?"bcs":"baf", tmpstr, res, szFilter);
m_getfiledlg.m_ofn.nFilterIndex = m_bcs+1;
restart:
if( m_getfiledlg.DoModal() == IDOK )
{
filepath=m_getfiledlg.GetPathName();
filepath.MakeLower();
if(filepath.Right(4)==".baf") m_bcs=0;
else if(filepath.Right(4)==".bcs") m_bcs=1; //this is the default
if(m_bcs)
{
if(filepath.Right(4)!=".bcs") filepath+=".bcs";
lastopenedoverride=filepath.Left(filepath.ReverseFind('\\'));
}
else
{
if(filepath.Right(4)!=".baf") filepath+=".baf";
lastopenedscript=filepath.Left(filepath.ReverseFind('\\'));
}
newname=m_getfiledlg.GetFileName();
newname.MakeUpper();
if(newname.Right(4)==".BCS") newname=newname.Left(newname.GetLength()-4);
else if(newname.Right(4)==".BAF") newname=newname.Left(newname.GetLength()-4);
if(newname.GetLength()>8 || newname.GetLength()<1 || newname.Find(" ",0)!=-1)
{
tmpstr.Format("The resource name '%s' is wrong, it should be 8 characters long and without spaces.",newname);
MessageBox(tmpstr,"Warning",MB_ICONEXCLAMATION|MB_OK);
goto restart;
}
if(newname!=itemname && file_exists(filepath) )
{
res=MessageBox("Do you want to overwrite "+newname+"?","Warning",MB_ICONQUESTION|MB_YESNO);
if(res==IDNO) goto restart;
}
itemname=newname;
res=0;
if(filepath.Right(4)==".bcs")
{
chdir(bgfolder);
tmpstr=bgfolder+weidudecompiled;
assure_dir_exists(tmpstr);
if(!dir_exists(tmpstr) )
{
res=-3;
}
else tmpstr+="\\"+newname+".baf";
}
if(!res)
{
fhandle=creat(tmpstr,S_IWRITE);
if(fhandle>0)
{
//this is the intended way of extracting info from a richedit control
res=writestream(fhandle,m_text_control);
close(fhandle);
}
else res=-2;
if(m_bcs)
{
if(!res) res=compile(tmpstr);
}
}
switch(res)
{
case -3:
newname.Format("Cannot create output path:%s",tmpstr);
MessageBox(tmpstr,"Script editor",MB_ICONSTOP|MB_OK);
break;
case -2:
newname.Format("Cannot create file:%s",tmpstr);
MessageBox(tmpstr,"Script editor",MB_ICONSTOP|MB_OK);
break;
case 0:
if(m_bcs) MessageBox("Script saved and compiled...","Script editor",MB_ICONINFORMATION|MB_OK);
else MessageBox("Script saved...","Script editor",MB_ICONINFORMATION|MB_OK);
break;
default:
MessageBox("Error during save.","Script editor",MB_ICONSTOP|MB_OK);
}
}
UpdateData(UD_DISPLAY);
the_script.m_changed=false;
CheckScript(0);
RefreshDialog();
}
void CScriptEdit::NewScript()
{
itemname="new script";
if(m_errors) delete [] m_errors;
m_errors=NULL;
m_count=0;
m_firsterror=0;
m_bcs=1;
the_script.m_changed=false;
}
void CScriptEdit::OnNew()
{
NewScript();
m_text_control.SetWindowText("");
UpdateData(UD_DISPLAY);
CheckScript(FORCED_CHECK|WHOLE_CHECK);
RefreshDialog();
}
void format_line(CString &line, int indent)
{
if(line.IsEmpty()) return;
//line.SetAt(0,(char) toupper(line.GetAt(0)));
line=CString(TCHAR(' '),indent)+line;
}
int match_string(CString &line, CString token, int indent, int error)
{
int maxi, maxj;
int i,j;
maxi=line.GetLength();
if(!maxi) return 0;
maxj=token.GetLength();
i=0;
j=0;
while(j<maxj)
{
if(token[j]=='%')
{
switch(token[++j])
{
case 'd': //number
if(i>=maxi) return error;
while((i<maxi) && (line[i]>='0') && (line[i]<='9') ) i++;
break;
}
j++;
continue;
}
if(i>=maxi) return error; //end of line without full parse
if(toupper(line[i])!=token[j]) return error; //difference
i++;
j++;
}
if(i<maxi) return error; //excess on line
line.MakeUpper();
line=CString(TCHAR(' '),indent)+line;
return 0;
}
void CScriptEdit::RefreshMenu()
{
CMenu *pMenu;
pMenu=GetMenu();
if(pMenu)
{
pMenu->CheckMenuItem(ID_OPTIONS_AUTOCHECK, MF_SET(!(editflg&NOCHECK)));
pMenu->CheckMenuItem(ID_OPTIONS_LARGEINDENT, MF_SET(!(editflg&INDENT)));
pMenu->CheckMenuItem(ID_OPTIONS_USEINTERNALDECOMPILER, MF_SET(!(editflg&INTERNALDECOMP)));
pMenu->CheckMenuItem(ID_OPTIONS_USEINTERNALCOMPILER, MF_SET(!(editflg&INTERNALCOMPILER)));
pMenu->CheckMenuItem(ID_OPTIONS_LOGGING, MF_SET(weiduflg&WEI_LOGGING));
}
}
void CScriptEdit::RefreshDialog()
{
CString tmpstr;
CButton *cb;
int topline;
int i;
if(m_firsterror>=0)
{
topline=m_text_control.GetFirstVisibleLine();
for(i=0;i<LINES;i++)
{
cb=(CButton *) GetDlgItem(IDC_U1+i);
if(topline+i>=m_count)
{
cb->ShowWindow(false);
}
else
{
cb->ShowWindow(true);
tmpstr.Format("%d",topline+i+1);
cb->SetWindowText(tmpstr);
if(m_errors[topline+i]&0xffff) cb->SetCheck(false);
else cb->SetCheck(true);
}
}
}
else
{
topline=m_text_control.GetFirstVisibleLine();
for(i=0;i<LINES;i++)
{
cb=(CButton *) GetDlgItem(IDC_U1+i);
if(topline+i>=m_count)
{
cb->ShowWindow(false);
}
else
{
cb->ShowWindow(true);
tmpstr.Format("%d",topline+i+1);
cb->SetWindowText(tmpstr);
cb->SetCheck(true);
}
}
}
}
CString CScriptEdit::GetLine(int idx)
{
int len;
CString line;
len=m_text_control.LineLength(m_text_control.LineIndex(idx));
m_text_control.GetLine(idx,line.GetBufferSetLength(len+4),len);
line.ReleaseBuffer(len);
return line;
}
void CScriptEdit::CheckScript(int messages)
{
CString tmpstr;
CString linestr;
CString line;
trigger trigger;
action action;
int i;
int triggeroraction;
int startline, endline;
int num_or;
bool tr_override;
int commentpos;
if(m_bcs)
{
tmpstr.Format("Edit script: %s%s",itemname, the_script.m_changed?"*":"");
}
else
{
tmpstr.Format("Edit script source: %s%s",itemname, the_script.m_changed?"*":"");
}
SetWindowText(tmpstr);
if(!(messages&FORCED_CHECK) )
{
if(editflg&NOCHECK) return;
if(!m_text_control.GetModify()) return;
}
m_text_control.SetModify(false);
m_count=m_text_control.GetLineCount();
if(m_errors) delete [] m_errors;
triggeroraction=TA_IF;
if(m_count<0)
{
m_errors=new int[1];
m_errors[0]=m_count;
m_firsterror=0;
endline=m_count=1;
}
else
{
m_firsterror=-1;
m_errors=new int[m_count];
if(messages&WHOLE_CHECK)
{
startline=0;
endline=m_count;
}
else
{
startline=m_text_control.GetFirstVisibleLine();
endline=min(m_count,startline+LINES);
while(startline>0)
{
if(GetLine(startline)=="IF") break;
startline--;
}
}
num_or=0;
tr_override=false;
for(i=startline;i<endline;i++)
{
line=GetLine(i);
commentpos=line.Find("//");
if(commentpos>=0)
{
tmpstr=line.Left(commentpos);
}
else tmpstr=line;
tmpstr.TrimRight();
tmpstr.TrimLeft();
if(!tmpstr.IsEmpty())
{
switch(triggeroraction)
{
case TA_ACTION:
m_errors[i]=match_string(tmpstr, "END",0,-130);
if(!m_errors[i])
{
triggeroraction=TA_IF;
break;
}
m_errors[i]=match_string(tmpstr, "RESPONSE #%d",m_indent,-110);
if(!m_errors[i])
{
triggeroraction=TA_ACTION;
break;
}
m_errors[i]=compile_action(tmpstr, action, 0);
format_line(tmpstr,m_indent*2);
break;
case TA_TRIGGER:
m_errors[i]=match_string(tmpstr, "THEN",0,-120);
if(!m_errors[i])
{
if(num_or)
{
m_errors[i]=CE_INCOMPLETE_OR;
}
if(tr_override)
{
m_errors[i]=CE_INCOMPLETE_TROVERRIDE;
}
triggeroraction=TA_RESPONSE;
break;
}
m_errors[i]=compile_trigger(tmpstr, trigger);
format_line(tmpstr, num_or?m_indent+2:m_indent);
if((trigger.opcode&0x1fff)==TR_OR)
{
if(num_or) m_errors[i]=CE_INCOMPLETE_OR;
num_or=trigger.bytes[0];
}
else
{
if(num_or) num_or--;
}
if((trigger.opcode==TR_OVERRIDE) && is_this_bgee())
{
if(tr_override) m_errors[i]=CE_INCOMPLETE_TROVERRIDE;
else tr_override=true;
}
else
{
//override object???
tr_override=false;
}
break;
case TA_IF:
m_errors[i]=match_string(tmpstr, "IF",0,-100);
triggeroraction=TA_TRIGGER;
num_or=0;
tr_override=false;
break;
case TA_RESPONSE:
m_errors[i]=match_string(tmpstr, "RESPONSE #%d",m_indent,-110);
triggeroraction=TA_ACTION;
break;
}
if(m_errors[i])
{
if(m_firsterror==-1) m_firsterror=i;
}
}
else m_errors[i]=0;
if(commentpos>=0) line=tmpstr+line.Mid(commentpos);
else line=tmpstr;
}
}
if(messages&SHOW_MESSAGES)
{
if(m_firsterror>=0)
{
for(i=m_firsterror;i<endline;i++)
{
if(m_errors[i])
{
tmpstr.Format("Error in script line #%d: %s",i+1,get_compile_error(m_errors[i]) );
if(MessageBox(tmpstr,"Dialog editor",MB_ICONEXCLAMATION|MB_OKCANCEL)==IDCANCEL)
{
m_firsterror=i; //positioning to the cancelled line
break;
}
}
}
}
}
}
void CScriptEdit::OnKillfocusText()
{
UpdateData(UD_RETRIEVE);
}
void CScriptEdit::OnSetfocusText()
{
if(!m_firsterror) CheckScript(0);
RefreshDialog();
}
void CScriptEdit::OnCheck()
{
int startchar, linelength;
CheckScript(WHOLE_CHECK|FORCED_CHECK|SHOW_MESSAGES);
if(m_firsterror>=0)
{
m_text_control.SetFocus();
startchar=m_text_control.LineIndex(m_firsterror);
if(!(editflg&SELECTION))
{
linelength=m_text_control.LineLength(startchar);
m_text_control.SetSel(startchar, startchar+linelength);
}
else
{
m_text_control.SetSel(startchar, startchar);
}
}
else MessageBox("The script appears correct.","Script editor",MB_OK);
}
void CScriptEdit::OnChangeText()
{
CWnd *cwnd;
cwnd=GetFocus();
//make sure that the edit window has the input focus, otherwise this could
//cause various unexpected bugs
// if(!cwnd || (m_text_control.GetSafeHwnd()!=cwnd->GetSafeHwnd()) ) return;
if(!cwnd || (cwnd->GetDlgCtrlID()!=IDC_TEXT) ) return;
UpdateData(UD_RETRIEVE);
if(m_text_control.GetModify())
{
the_script.m_changed=true;
}
CheckScript(FORCED_CHECK);
RefreshDialog();
}
void CScriptEdit::PostNcDestroy()
{
if(m_errors) delete [] m_errors;
m_errors=NULL;
CDialog::PostNcDestroy();
}
void CScriptEdit::OnFileLoadsource()
{
m_bcs=0;
OnLoadex();
}
void CScriptEdit::OnMaxtextText()
{
MessageBox("Script is too long, sorry.","Warning",MB_ICONEXCLAMATION|MB_OK);
}
void CScriptEdit::OnSearch()
{
CString tmpstr;
if(!m_searchdlg)
{
m_searchdlg=new CFindReplaceDialog;
if(!m_searchdlg)
{
MessageBox("Insufficient memory","Error",MB_ICONEXCLAMATION|MB_OK);
return;
}
tmpstr=m_text_control.GetSelText();
tmpstr.Replace("\r\n","\n");
m_searchdlg->Create(readonly,tmpstr,NULL, FR_DOWN | FR_MATCHCASE, this);
}
}
long CScriptEdit::OnFindReplace(WPARAM /*wParam*/, LPARAM /*lParam*/)
{
int direction;
int mode; //0 - search, 1 - replace current, 2 - replace all
int match;
CString search;
CString replace;
if(!m_searchdlg) return 0;
if(m_searchdlg->IsTerminating() )
{
m_searchdlg=NULL;
return 0;
}
if(m_infind) return 0;
m_infind=1;
direction=m_searchdlg->SearchDown();
mode=!!m_searchdlg->ReplaceCurrent()+(!!m_searchdlg->ReplaceAll())*2;
//matchcase is cheaper, therefore it is marked 0
match=!m_searchdlg->MatchCase()+(!!m_searchdlg->MatchWholeWord())*2;
search=m_searchdlg->GetFindString();
replace=m_searchdlg->GetReplaceString();
do_search_and_replace(direction,mode,match,search,replace);
m_infind=0;
OnSetfocusText();
return 0;
}
int CScriptEdit::perform_search_and_replace(int idx, int mode, int match, CString search, CString replace)
{
int ch;
int found;
int len;
int tmplen, tmpstart;
CString tmp2;
CString line;
line=GetLine(idx);
ch=m_text_control.LineIndex(idx);
len=m_text_control.LineLength(ch);
found=false;
switch(mode)
{
case SR_SEARCH:
tmp2=line;
if(match&1) // case insensitive trick
{
tmp2.MakeLower();
}
if(match&2) // full word trick
{
tmp2=" "+line+" "; //in this
}
tmplen=tmp2.Find(search);
if(tmplen!=-1)
{
m_text_control.SetSel(ch+tmplen,ch+tmplen+search.GetLength());
found=true;
}
break;
case SR_REPLACE: case SR_ALL:
tmplen=line.GetLength();
if(match&2)
{
line=" "+line+" ";
}
if(match&1)
{
tmp2=line;
tmp2.MakeLower();
tmpstart=tmp2.Find(search);
if(tmpstart==-1) ch=0;
else
{
line.Delete(tmpstart,search.GetLength());
line.Insert(tmpstart,replace);
found=true;
}
}
else
{
found=line.Replace(search,replace);
}
if(match&2)
{
line=line.Mid(1,tmplen);
}
if(found)
{
m_text_control.SetSel(ch,ch+len);
m_text_control.ReplaceSel(line,true);
found=true;
UpdateData(UD_DISPLAY);
}
break;
}
return found;
}
void CScriptEdit::do_search_and_replace(int direction,int mode,int match,CString search,CString replace)
{
CString what;
CString operation;
int linecount;
int idx, oldidx;
int count;
int found;
idx=m_text_control.LineFromChar(-1);
linecount=m_text_control.GetLineCount();
if(!linecount) return;
if(idx<0 || idx>=linecount)
{
idx=0;
}
oldidx=idx;
if(match&1) //we can do this here
{
search.MakeLower();
}
if(match&2) //this too
{
search=" "+search+" ";
}
found=-1;
count=0;
if(direction)
{
direction=1;
what="bottom";
}
else
{
direction=-1;
what="top";
}
switch(mode)
{
case SR_SEARCH: operation="Search"; break; //0
case SR_REPLACE: operation="Search & replace"; break; //1
case SR_ALL: operation="Replace all"; break; //2
}
if(!mode)
{
idx+=direction;
}
while(direction<1? idx : idx<linecount)
{
if(perform_search_and_replace(idx,mode,match,search,replace))
{
found=idx;
count++;
}
if(found==-1)
{//we found nothing
if(mode==1) mode=0; //couldn't find anything in the actual position, we will search from now
}
else
{//we found something, if it is not replace all then we stop here
if(mode!=2) break;
}
idx+=direction;
}
if((found==-1) || (mode==2)) //replace all should go around even if it has found something
{
if(MessageBox(operation+" hit the "+what+" of the list, do you want to continue ?",operation,MB_SYSTEMMODAL|MB_YESNO)==IDNO)
{
return;
}
if(direction>0)
{
idx=0;
}
else
{
idx=linecount-1;
}
while(idx!=oldidx)
{
if(perform_search_and_replace(idx,mode,match,search,replace))
{
found=idx;
count++;
}
if(found==-1)
{//we found nothing
if(mode==1) mode=0; //couldn't find anything in the actual position, we will search from now
}
else
{//we found something, if it is not replace all then we stop here
if(mode!=2) break;
}
idx+=direction;
}
}
if(found!=-1)
{
if(count>1)
{
operation.Format("Found %d entries.",count);
MessageBox(operation);
}
}
else
{
MessageBox("Couldn't find any entries containing: "+search);
}
return;
}
void CScriptEdit::OnOptionsAutocheck()
{
editflg^=NOCHECK;
RefreshMenu();
}
void CScriptEdit::OnOptionsLargeindent()
{
editflg^=INDENT;
if(editflg&INDENT) m_indent=2;
else m_indent=4;
RefreshMenu();
}
void CScriptEdit::OnOptionsUseinternaldecompiler()
{
editflg^=INTERNALDECOMP;
RefreshMenu();
}
void CScriptEdit::OnOptionsUseinternalcompiler()
{
editflg^=INTERNALCOMPILER;
RefreshMenu();
}
void CScriptEdit::OnOptionsLogging()
{
weiduflg^=WEI_LOGGING;
RefreshMenu();
}
void CScriptEdit::OnEditSelectall()
{
m_text_control.SetSel(0,-1);
}
void CScriptEdit::OnEditUndo()
{
m_text_control.Undo();
}
void CScriptEdit::OnEditCut()
{
m_text_control.Cut();
}
void CScriptEdit::OnEditCopy()
{
m_text_control.Copy();
}
void CScriptEdit::OnEditPaste()
{
m_text_control.Paste();
}
void CScriptEdit::OnEditDelete()
{
m_text_control.Clear();
}
#define EDITMENU 1
void CScriptEdit::OnContextMenu(CWnd* pWnd, CPoint point)
{
if(pWnd->GetDlgCtrlID()==IDC_TEXT)
{
CMenu *menu;
CMenu *popupmenu;
menu=GetMenu();
popupmenu=menu->GetSubMenu(EDITMENU); //this should be better defined
popupmenu->TrackPopupMenu(TPM_CENTERALIGN,point.x,point.y,this);
}
}
void CScriptEdit::OnIDS()
{
CIDSEdit dlg;
CString tmpstr;
dlg.SetReadOnly(true);
tmpstr=itemname;
itemname=m_idsname;
dlg.DoModal();
m_idsname=itemname;
itemname=tmpstr;
}
BOOL CScriptEdit::OnHelpInfo(HELPINFO* /*pHelpInfo*/)
{
CString tmpstr;
CString helpstr;
compiler_data compiler_data;
tmpstr=m_text_control.GetSelText();
tmpstr.MakeLower();
tmpstr.TrimLeft();
tmpstr.TrimRight();
if(action_data.Lookup(tmpstr,compiler_data) )
{
helpstr.Format("%s(%s)",compiler_data.keyword,format_parameters(compiler_data));
MessageBox(helpstr,"Script Action", MB_ICONINFORMATION|MB_OK);
return false;
}
if(trigger_data.Lookup(tmpstr,compiler_data) )
{
helpstr.Format("%s(%s)",compiler_data.keyword,format_parameters(compiler_data));
MessageBox(helpstr,"Script Trigger", MB_ICONINFORMATION|MB_OK);
return false;
}
int tmp = IDSKey("OBJECT",tmpstr);
if (tmp!=-1)
{
helpstr.Format("%s=%d", tmpstr, tmp);
MessageBox(helpstr,"Script Object", MB_ICONINFORMATION|MB_OK);
return false;
}
tmp = IDSKey("SPELL", tmpstr);
if (tmp!=-1)
{
helpstr.Format("%s=%s", tmpstr, format_spell_id(tmp));
MessageBox(helpstr,"Spell", MB_ICONINFORMATION|MB_OK);
return false;
}
return false;
}
void CScriptEdit::OnUpdateText()
{
if(m_text_control.GetModify() )
{
the_script.m_changed=true;
return; //this will be handled in onchange
}
OnChangeText(); //this event is called from scroll
}
void CScriptEdit::OnCancel()
{
CString tmpstr;
if(the_script.m_changed)
{
tmpstr.Format("Changes have been made to the file (%s).\n"
"Do you want to quit without save?\n",itemname);
if(MessageBox(tmpstr,"Warning",MB_YESNO)==IDNO)
{
return;
}
}
CDialog::OnCancel();
}
void CScriptEdit::OnToolsLookupstrref()
{
CStrRefDlg dlg;
dlg.DoModal();
RefreshMenu();
}
BOOL CScriptEdit::PreTranslateMessage(MSG* pMsg)
{
m_tooltip.RelayEvent(pMsg);
return CDialog::PreTranslateMessage(pMsg);
}