Diff of /2da.cpp [000000] .. [516d32] Maximize Restore

  Switch to side-by-side view

--- a
+++ b/2da.cpp
@@ -0,0 +1,2982 @@
+// 2da.cpp: implementation of the C2da class.
+//
+//////////////////////////////////////////////////////////////////////
+
+#include "stdafx.h"
+
+#include <errno.h>
+#include "2da.h"
+#include "options.h"
+
+#ifdef _DEBUG
+#undef THIS_FILE
+static char THIS_FILE[]=__FILE__;
+#define new DEBUG_NEW
+#endif
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+CStringMapArray tooltipnumbers;
+CStringMapCompiler action_data;
+CStringMapCompiler trigger_data;
+
+static int position=0;
+static int xorflag=0;
+static int maxlength=0;
+
+char external[MAXBUFFSIZE];
+
+int mygetc(FILE *fpoi)
+{
+  int ch;
+
+  ch=fgetc(fpoi);
+  if((ch==EOF) && feof(fpoi))
+  {
+    return EOF;
+  }
+  if((ch==255) && !position)
+  {      
+    xorflag=1;
+    fgetc(fpoi);
+    ch=fgetc(fpoi);
+    position=0;
+    if(maxlength>=2) maxlength-=2;
+  }
+  if(xorflag)
+  {
+    ch^=xorblock[position&63];
+  }
+  position++;
+  if(maxlength>=0)
+  {
+    if(position>maxlength) return EOF;
+  }
+  if(ch=='\t') ch=' '; //hack for allowing tabs
+  return ch&255;
+}
+
+int skip_string(FILE *fpoi, char chr)
+{
+  int ch;
+
+  do
+  {
+    ch=mygetc(fpoi);
+    if(ch==EOF) return -1;
+    if((char) ch!=chr)
+    {
+      position--;
+      if(xorflag)
+      {
+        ungetc(ch^xorblock[position&63],fpoi); //stuffing it back into the can
+      }
+      else
+      {
+        ungetc(ch,fpoi); //stuffing it back into the can
+      }
+      break;
+    }      
+  }
+  while(1);
+  return 0;
+}
+
+int read_string(FILE *fpoi, char *pattern, char *tmpbuff=NULL, int maxlength=0)
+{
+  int i,j,k;
+  char ch;
+
+  i=0;
+  k=0;
+  while(pattern[i])
+  {
+    ch=(char) mygetc(fpoi);
+    if(ch==EOF)
+    {
+      if(tmpbuff) tmpbuff[k]=0;
+      if(k)
+      { //incomplete last line
+        return 2;
+      }
+      else return 0;
+    }
+    if(ch!=pattern[i])
+    {
+      for(j=0;j<i;j++)
+      {
+        if(tmpbuff)
+        {
+          tmpbuff[k++]=pattern[j];
+          if(k>=maxlength)
+          {
+            return 3; //buffer overflow
+          }
+        }
+      }
+      i=0;
+      if(tmpbuff && ch!='\r')
+      {
+        tmpbuff[k++]=ch; //don't add \r we would remove it later anyway
+        if(k>=maxlength)
+        {
+          return 3; //buffer overflow
+        }
+      }
+    }
+    else i++;
+  }
+  if(tmpbuff) tmpbuff[k]=0;
+  return 1;
+}
+
+//// reads an array of integers from ids (ordered)
+int ReadIdsFromFile(int fhandle, CStringList &refs, int add, int length)
+{
+  char tmpref[MAXIDSIZE];
+  CString ref;
+  int cnt, prev;
+  int ret;
+  FILE *fpoi;
+
+  if(length<0) maxlength=filelength(fhandle);
+  else maxlength=length;
+  fpoi=fdopen(fhandle,"rb");
+  if(!fpoi)
+  {
+    close(fhandle);
+    return -1;
+  }
+  switch(add)
+  {
+  case -1:
+    prev=-1;
+    break;
+  case 0:
+    read_string(fpoi, "\n"); //skipping crap
+    prev=-1;
+    break;
+  case 1:
+   refs.AddTail("Default");//0
+   read_string(fpoi, "\n"); //skipping crap
+   prev=0;
+   break;
+  case 2:
+    refs.AddTail("Default");//0
+    refs.AddTail("None");   //1
+    read_string(fpoi, "\n"); //skipping crap
+    prev=0;
+    break;
+  default:    
+    return -2; //internal error
+  }
+  do
+  {
+    ret=read_string(fpoi, " ",tmpref,sizeof(tmpref));
+    if(ret!=1) break;
+    tmpref[MAXIDSIZE-1]=0;
+    cnt=strtonum(tmpref);
+    ret=read_string(fpoi,"\n",tmpref,sizeof(tmpref));
+    if(ret!=1 && ret!=2) break;
+    tmpref[MAXIDSIZE-1]=0;
+    while(cnt>prev)
+    {
+      prev++;
+      if(cnt==prev)
+      {
+        ref=tmpref;
+        ref.TrimRight();
+        refs.AddTail(ref);
+      }
+      else
+      {
+        refs.AddTail("");
+      }
+    }
+    if(cnt>2000) break; //cut it out at 2000!
+  }
+  while(1);
+  fclose(fpoi);
+  return 0;
+}
+
+int ReadSongIdsFromFile(int fhandle, CString2List &refs, int length)
+{
+  char tmpref[MAXIDSIZE];
+  char musfile[MAXIDSIZE];
+  CString2 ref;
+  int i;
+  int cnt, prev;
+  int ret;
+  FILE *fpoi;
+  FILE *fpoi2;
+
+  if(length<0) maxlength=filelength(fhandle);
+  else maxlength=length;
+  fpoi2=fopen(bgfolder+"/music/songlist.txt","rb"); //pst style songlist
+  fpoi=fdopen(fhandle,"rb");
+  if(!fpoi)
+  {
+    close(fhandle);
+    return -1;
+  }
+  read_string(fpoi, "\n"); //skipping crap
+  prev=-1;
+  do
+  {
+    ret=read_string(fpoi, " ",tmpref,sizeof(tmpref));
+    if(ret!=1) break;
+    tmpref[MAXIDSIZE-1]=0;
+    cnt=strtonum(tmpref);
+    ret=read_string(fpoi,"\n",tmpref,sizeof(tmpref));
+    if(ret!=1 && ret!=2) break;
+    tmpref[MAXIDSIZE-1]=0;
+    while(cnt>prev)
+    {
+      fgets(musfile,MAXIDSIZE,fpoi2);
+      for(i=0;i<MAXIDSIZE;i++) if(musfile[i]==' ') musfile[i]=0;
+      ref.b=musfile;
+      ref.b.MakeUpper();
+      if(ref.b.Right(4)==".MUS") ref.b=ref.b.Left(ref.b.GetLength()-4);
+      prev++;      
+      if(cnt==prev)
+      {
+        ref.a=tmpref;
+        ref.a.TrimRight();
+      }
+      else
+      {
+        ref.a.Empty();
+      }
+      refs.AddTail(ref);
+    }
+    if(cnt>2000) break; //cut it out at 2000!
+  }
+  while(1);
+  fclose(fpoi);
+  return 0;
+}
+
+//pst song ids are separated from filenames
+int ReadSongIds(loc_entry entry, CString2List &refs)
+{
+  int fhandle;
+
+  refs.RemoveAll();
+  xorflag=position=0;
+  fhandle=locate_file(entry, 0);
+  if(fhandle<1) return -1;
+  return ReadSongIdsFromFile(fhandle, refs, entry.size);
+}
+
+int ReadIds(loc_entry entry, CStringList &refs, int add)
+{
+  int fhandle;
+
+  refs.RemoveAll();
+  xorflag=position=0;
+  fhandle=locate_file(entry, 0);
+  if(fhandle<1) return -1;
+  return ReadIdsFromFile(fhandle, refs, add, entry.size);
+}
+
+//// reads an array of integers from ids (ordered), fix number
+int ReadIdsFromFile2(int fhandle, CString *refs, int maxindex, int mask, int length)
+{
+  char tmpref[MAXIDSIZE];
+  CString ref;
+  int cnt;
+  int ret;
+  FILE *fpoi;
+
+  for(cnt=0;cnt<maxindex;cnt++) refs[cnt].Format("Unknown (%d)",cnt);
+  if(length<0) maxlength=filelength(fhandle);
+  else maxlength=length;
+  fpoi=fdopen(fhandle,"rb");
+  if(!fpoi)
+  {
+    close(fhandle);
+    return -1;
+  }
+  if(mask)
+  { //skipping crap, don't skip for slots.ids
+    read_string(fpoi, "\n");
+  }
+  do
+  {
+    ret=read_string(fpoi, " ",tmpref,sizeof(tmpref));
+    if(ret!=1) break;
+    tmpref[MAXIDSIZE-1]=0;
+    cnt=strtonum(tmpref);
+    if(mask) cnt&=mask;
+    ret=read_string(fpoi, "\n",tmpref,sizeof(tmpref));
+    if(!ret) break;
+    tmpref[MAXIDSIZE-1]=0;
+    if(cnt>=maxindex) break;
+    ref=tmpref;
+    ref.TrimLeft();
+    ref.TrimRight();
+    refs[cnt]=ref;
+  }
+  while(1);
+  fclose(fpoi);
+  return 0;
+}
+
+int ReadIds2(loc_entry entry, CString *refs, int maxindex)
+{
+  int fhandle;
+
+  xorflag=position=0;
+  fhandle=locate_file(entry, 0);
+  if(fhandle<1) return -1;
+  return ReadIdsFromFile2(fhandle, refs, maxindex, 0, entry.size);
+}
+
+//reads an array of compiler definitions (trigger or action)
+int ReadIdsFromFile3(int fhandle, CString *refs, int maxindex, int length, CStringMapCompiler &atdata, int trigger_or_action)
+{
+  POSITION pos;
+  char tmpref[MAXIDSIZE];
+  CString ref;
+  compiler_data compiler_data;
+  int cnt;
+  int ret;
+  FILE *fpoi;
+
+  pos=atdata.GetStartPosition();
+  while(pos)
+  {
+    atdata.GetNextAssoc(pos,ref,compiler_data);
+    delete [] compiler_data.parameters;
+  }
+  atdata.RemoveAll();
+  atdata.InitHashTable(get_hash_size(maxindex));
+  for(cnt=0;cnt<maxindex;cnt++) refs[cnt].Format("Unknown (%d)",cnt);
+  if(length<0) maxlength=filelength(fhandle);
+  else maxlength=length;
+  fpoi=fdopen(fhandle,"rb");
+  if(!fpoi)
+  {
+    close(fhandle);
+    return -1;
+  }
+  read_string(fpoi, "\n");
+  ret=0;
+  do
+  {
+    ret=read_string(fpoi, " ",tmpref,sizeof(tmpref));
+    if(ret!=1)
+    {
+      break;
+    }
+    tmpref[MAXIDSIZE-1]=0;
+    cnt=strtonum(tmpref);
+    ret=read_string(fpoi, "\n",tmpref,sizeof(tmpref));
+    if(ret!=1 && ret!=2)
+    {
+      break;
+    }
+    tmpref[MAXIDSIZE-1]=0;
+    ref=tmpref;
+    ref.TrimLeft();
+    ref.TrimRight();
+    ref.MakeUpper();
+    ret=add_compiler_data(ref, cnt, atdata, trigger_or_action);
+    if(ret)
+    {
+      break;
+    }
+    refs[cnt%maxindex]=tmpref;
+  }
+  while(1);
+  fclose(fpoi);
+  return ret;
+}
+
+int ReadIds3(loc_entry entry, CString *refs, int maxindex, CStringMapCompiler &atdata, int trigger_or_action)
+{
+  int fhandle;
+
+  xorflag=position=0;
+  fhandle=locate_file(entry, 0);
+  if(fhandle<1) return -1;
+  return ReadIdsFromFile3(fhandle, refs, maxindex, entry.size, atdata, trigger_or_action);
+}
+//reads string mapped to int ids style
+int ReadIdsFromFile4(int fhandle, CStringMapInt &refs, int length)
+{
+  char tmpref[MAXIDSIZE];
+  CString ref;
+  int cnt;
+  int ret;
+  FILE *fpoi;
+  int p1;
+  int maxindex;
+
+  if(length<0) maxlength=filelength(fhandle);
+  else maxlength=length;
+  maxindex=maxlength/20;
+  refs.RemoveAll();
+  refs.InitHashTable(get_hash_size(maxindex));
+  fpoi=fdopen(fhandle,"rb");
+  if(!fpoi)
+  {
+    close(fhandle);
+    return -1;
+  }
+  do
+  {
+    ret=read_string(fpoi, "\n",tmpref,sizeof(tmpref));
+    if(ret<1) break;
+    tmpref[MAXIDSIZE-1]=0;
+    cnt=strtonum(tmpref);
+    for(p1=0;(p1<MAXIDSIZE) && (tmpref[p1]!=' ') && tmpref[p1];p1++)
+    {
+      tmpref[p1]=' '; //hacking off number
+    }
+    tmpref[MAXIDSIZE-1]=0;
+    ref=tmpref;
+    ref.TrimLeft();
+    ref.TrimRight();
+    ref.MakeLower();
+    if(!ref.IsEmpty())
+    {
+      refs[ref]=cnt;
+    }
+  }
+  while(1);
+  fclose(fpoi);
+  return 0;
+}
+
+//another bloody hack, this function is called from inside another
+//readids function, therefore it must save the ugly globals
+//a wiser programmer would have created these functions as a class
+int ReadIds4(loc_entry entry, CStringMapInt &refs)
+{
+  int fhandle;
+  int ret;
+  int sxor, spos, smax;
+
+  fhandle=locate_file(entry, 0);
+  if(fhandle<1) return -1;
+  sxor=xorflag;
+  spos=position;
+  smax=maxlength;
+  xorflag=position=0;
+  ret=ReadIdsFromFile4(fhandle, refs, entry.size);
+  xorflag=sxor;
+  position=spos;
+  maxlength=smax;
+  return ret;
+}
+
+///////////////////////2da files
+int Read2daFromFile(int fhandle, CStringList &refs, int length)
+{
+  char tmpref[MAXIDSIZE];
+  CString ref;
+  FILE *fpoi;
+
+  refs.RemoveAll();
+  if(length<0) maxlength=filelength(fhandle);
+  else maxlength=length;
+  fpoi=fdopen(fhandle,"rb");
+  if(!fpoi)
+  {
+    close(fhandle);
+    return -1;
+  }
+  read_string(fpoi, "\n"); //skipping crap
+  read_string(fpoi, "\n"); //skipping crap
+  read_string(fpoi, "\n"); //skipping crap
+  do
+  {
+    if(read_string(fpoi, " ",tmpref,sizeof(tmpref)) )
+    {
+      tmpref[MAXIDSIZE-1]=0;
+      ref=tmpref;
+      ref.TrimRight();
+      ref.MakeUpper();
+      if(!ref.IsEmpty())
+      {
+        refs.AddTail(ref);
+      }
+    }
+  }
+  while(read_string(fpoi,"\n")==1 );
+  fclose(fpoi);
+  return 0;
+}
+
+int Read2daIntFromFile(int fhandle, CStringMapInt &refs, int length)
+{
+  char tmpref[MAXIDSIZE];
+  CString ref;
+  FILE *fpoi;
+  int ret, val;
+
+  if(length<0) maxlength=filelength(fhandle);
+  else maxlength=length;
+  fpoi=fdopen(fhandle,"rb");
+  if(!fpoi)
+  {
+    close(fhandle);
+    return -1;
+  }
+  read_string(fpoi, "\n"); //skipping crap
+  read_string(fpoi, "\n"); //skipping crap
+  read_string(fpoi, "\n"); //skipping crap
+  do
+  {
+    if(read_string(fpoi, " ",tmpref,sizeof(tmpref)) )
+    {
+      tmpref[MAXIDSIZE-1]=0;
+      ref=tmpref;
+      ret=read_string(fpoi,"\n", tmpref,sizeof(tmpref));
+      if(!ret) break;
+      tmpref[MAXIDSIZE-1]=0;
+      val=atoi(tmpref);
+      ref.MakeUpper();
+      refs[ref]=val;
+    }
+    else break;
+  }
+  while(1);
+  fclose(fpoi);
+  return 0;
+}
+
+int Read2daInt(loc_entry entry, CStringMapInt &refs, int removeall)
+{
+  int fhandle;
+
+  if(removeall) refs.RemoveAll();
+  xorflag=position=0;
+  fhandle=locate_file(entry, 0);
+  if(fhandle<1) return -1;
+  return Read2daIntFromFile(fhandle, refs, entry.size);
+}
+
+int Read2daField(CString daname, int line, int column)
+{
+  char tmpref[MAXIDSIZE];
+  loc_entry entry;
+  CString *columns;
+  int fhandle;
+  FILE *fpoi;
+  int defval;
+  int count1, count2;
+  
+  if(line<0 || column<0) return -1;
+  xorflag=position=0;
+  if(!darefs.Lookup(daname,entry)) return -1;
+  fhandle=locate_file(entry, 0);
+  if(fhandle<1) return -1;
+  if(entry.size<0) maxlength=filelength(fhandle);
+  else maxlength=entry.size;
+  fpoi=fdopen(fhandle,"rb");
+  if(!fpoi)
+  {
+    close(fhandle);
+    return -1;
+  }
+  read_string(fpoi, "\n"); //skipping crap
+  read_string(fpoi, "\n", tmpref,sizeof(tmpref)); //default value
+  defval=strtonum(tmpref);
+  read_string(fpoi, "\n", tmpref,sizeof(tmpref)); //columns
+  columns=explode(tmpref,' ',count1);
+  if(columns)
+  {
+    delete [] columns;
+    columns=0;
+  }
+  line++; //just to make it easier
+  while(line--)
+  {
+    if(!read_string(fpoi, "\n",tmpref,sizeof(tmpref))) return defval; //line not found
+  }
+  columns=explode(tmpref,' ',count2);
+  if((count1==count2) && (count1>column))
+  {
+    defval=strtonum(columns[column]);
+  }
+  if(columns) delete [] columns;
+  return defval;
+}
+
+int ReadWeapProfs(loc_entry entry)
+{
+  CString tmpweaprofs[NUM_WEAPROF];
+  CStringMapArray tmpmap;
+  CString label;
+  tooltip_data value;
+  POSITION pos;
+  int i;
+
+  Read2daArray(entry,tmpmap,1);
+  pos=tmpmap.GetStartPosition();
+  i=0;
+  while(pos && (i<NUM_WEAPROF))
+  {
+    tmpmap.GetNextAssoc(pos, label, value);
+    tmpweaprofs[i]=format_label(label);
+    weaprofidx[i]=(value.data[0]<<16)|i;
+    i++;
+  }
+  weaprofs[i]="Unknown";
+  weaprofidx[i]=255;
+  act_num_weaprof=i;
+  ///order weaprofs in a very obscure way
+  qsort(weaprofidx,act_num_weaprof,4,longsortb);
+  for(i=0;i<act_num_weaprof;i++)
+  {
+    weaprofs[i]=tmpweaprofs[weaprofidx[i]&65535];
+    weaprofidx[i]>>=16;
+  }
+  return false;
+}
+
+int Read2da(loc_entry entry, CStringList &refs)
+{
+  int fhandle;
+
+  xorflag=position=0;
+  fhandle=locate_file(entry, 0);
+  if(fhandle<1) return -1;
+  return Read2daFromFile(fhandle, refs, entry.size);
+}
+
+//2 strings (for songlist)
+int Read2daStringFromFile(int fhandle, CString2List &refs, int length, int column)
+{
+  CString *tmparray;
+  int count;
+  CString2 ref, ref2;
+  int cnt, prev;
+  FILE *fpoi;
+  int res, ret;
+
+  ref2.a="";
+  ref2.b="";
+  refs.RemoveAll();
+  if(length<0) maxlength=filelength(fhandle);
+  else maxlength=length;
+  fpoi=fdopen(fhandle,"rb");
+  if(!fpoi)
+  {
+    close(fhandle);
+    return -1;
+  }// sprintf
+  read_string(fpoi, "\n"); //skipping crap
+  read_string(fpoi, "\n"); //skipping crap
+  read_string(fpoi, "\n"); //skipping crap
+  cnt=prev=-1;
+  res=0;
+  tmparray=NULL;
+  do
+  {
+    ret=read_string(fpoi, "\n",external,sizeof(external));   
+    if(ret==1)
+    {
+      if(column) cnt=strtonum(external);
+      else cnt++;
+      if(tmparray) delete [] tmparray;
+      tmparray=explode(external,' ',count);
+      if(!tmparray)
+      {
+        ret=2;
+        break;
+      }
+      ref.a.Empty();
+      ref.b.Empty();
+      if(column<count)
+      {
+        ref.a=tmparray[column];
+        ref.a.MakeUpper();
+      }
+      if(column+1<count)
+      {
+        ref.b=tmparray[column+1];
+        ref.b.MakeUpper();
+        if(ref.b.Right(4)==".MUS") ref.b=ref.b.Left(ref.b.GetLength()-4);
+      }
+      prev++;
+      if(prev!=cnt) res=1;
+      refs.AddTail(ref);
+    }
+  }
+  while(ret==1);
+  if(tmparray) delete [] tmparray;
+  fclose(fpoi);
+  return res;
+}
+
+int Read2daString(loc_entry entry, CString2List &refs, int startcolumn)
+{
+  int fhandle;
+
+  xorflag=position=0;
+  fhandle=locate_file(entry, 0);
+  if(fhandle<1) return -1;
+  return Read2daStringFromFile(fhandle, refs, entry.size, startcolumn);
+}
+
+int Read2daFromFile4(int fhandle, CStringList &refs, int length, int first)
+{
+  CString *args;
+  CString ref, item;
+  FILE *fpoi;
+  int count;
+  int i;
+
+  refs.RemoveAll();
+  if(length<0) maxlength=filelength(fhandle);
+  else maxlength=length;
+  fpoi=fdopen(fhandle,"rb");
+  if(!fpoi)
+  {
+    close(fhandle);
+    return -1;
+  }
+  read_string(fpoi, "\n"); //skipping crap
+  read_string(fpoi, "\n"); //skipping crap
+  read_string(fpoi, "\n", external,sizeof(external)); //reading columns
+  args=explode(external,' ',count);
+  if(args)
+  {
+    for(i=first;i<count;i++)
+    {
+      item=args[i];
+      item.MakeUpper();
+      refs.AddTail(item);
+    }
+    delete [] args;
+  }
+  fclose(fpoi);
+  return 0;
+}
+
+int Read2daRow(loc_entry entry, CStringList &refs, int first)
+{
+  int fhandle;
+
+  xorflag=position=0;
+  fhandle=locate_file(entry, 0);
+  if(fhandle<1) return -1;
+  return Read2daFromFile4(fhandle, refs, entry.size, first);
+}
+
+int Read2daColumnFromFile(int fhandle, CStringList &refs, int length, int column, bool caps)
+{
+  CString *args;
+  CString ref, item;
+  FILE *fpoi;
+  int count;
+  int ret;
+
+  refs.RemoveAll();
+  if(length<0) maxlength=filelength(fhandle);
+  else maxlength=length;
+  fpoi=fdopen(fhandle,"rb");
+  if(!fpoi)
+  {
+    close(fhandle);
+    return -1;
+  }
+  read_string(fpoi, "\n"); //skipping crap
+  read_string(fpoi, "\n"); //skipping crap
+  read_string(fpoi, "\n"); //skipping crap
+  do
+  {
+    ret=read_string(fpoi, "\n", external,sizeof(external)); //reading columns
+    args=explode(external,' ',count);
+    if(args)
+    {
+      if(count>column)
+      {
+        item=args[column];
+        if(caps) item.MakeUpper();
+        refs.AddTail(item);
+        delete [] args;
+      }
+    }
+  }
+  while(ret==1);
+  fclose(fpoi);
+  return 0;
+}
+
+int Read2daColumn(loc_entry entry, CStringList &refs, int column, bool caps)
+{
+  int fhandle;
+
+  xorflag=position=0;
+  fhandle=locate_file(entry, 0);
+  if(fhandle<1) return -1;
+  return Read2daColumnFromFile(fhandle, refs, entry.size, column, caps);
+}
+
+//this is mostly for itemdial.2da (watch for the repeated key)
+//CString defaultvalue ?
+int Write2daMapToFile(CString daname, CStringMapInt &refs, CString columname)
+{
+  CString filename;
+  CString key;
+  int value;
+  CString tmpstr;
+  FILE *fpoi;
+  int ret;
+  POSITION pos;
+
+  filename=bgfolder+CString("override\\")+daname+".2da";
+  fpoi=fopen(filename,"wb");
+  if(!fpoi) return -2;
+  ret=WriteString(fpoi,"2DA V1.0\r\n-1\r\n");
+  if(ret) return ret;
+  pos=refs.GetStartPosition();
+  tmpstr.Format("%-10s %s\r\n"," ",columname);
+  ret=WriteString(fpoi, tmpstr);
+
+  while(pos)
+  {
+    refs.GetNextAssoc(pos,key,value);
+    tmpstr.Format("%-10s %d  %s\r\n",key,value,key);
+    //don't write -1 back
+    if(value!=-1) ret=WriteString(fpoi,tmpstr);
+    if(ret) return ret;
+  }
+  fclose(fpoi);
+  return 0;
+}
+
+//CString defaultvalue ?
+int Write2daListToFile(CString daname, CStringList &refs, CString columname)
+{
+  CString filename;
+  CString key;
+  CString tmpstr;
+  FILE *fpoi;
+  int ret;
+  POSITION pos;
+
+  filename=bgfolder+CString("override\\")+daname+".2da";
+  fpoi=fopen(filename,"wb");
+  if(!fpoi) return -2;
+  ret=WriteString(fpoi,"2DA V1.0\r\n0\r\n");
+  if(ret) return ret;
+  pos=refs.GetHeadPosition();
+  tmpstr.Format("%-10s %s\r\n"," ",columname);
+  ret=WriteString(fpoi, tmpstr);
+
+  while(pos)
+  {
+    key=refs.GetNext(pos);
+    tmpstr.Format("%-10s 1\r\n",key);
+    ret=WriteString(fpoi,tmpstr);
+    if(ret) return ret;
+  }
+  fclose(fpoi);
+  return 0;
+}
+
+//reads a 2da with first column (index) and another numeric column (strref)
+int Read2daFromFile2(int fhandle, CIntMapInt &refs, int length, int column)
+{
+  char tmpref[MAXIDSIZE];
+  CString *row;
+  CString ref;
+  int item;
+  int ret;
+  int mycols;
+  FILE *fpoi;
+  int num;
+
+  if(length<0) maxlength=filelength(fhandle);
+  else maxlength=length;
+  fpoi=fdopen(fhandle,"rb");
+  if(!fpoi)
+  {
+    close(fhandle);
+    return -1;
+  }
+  read_string(fpoi, "\n"); //skipping crap
+  read_string(fpoi, "\n"); //skipping crap
+  read_string(fpoi, "\n"); //skipping crap
+  do
+  {
+    num=0;
+    ret=read_string(fpoi, " ", tmpref,sizeof(tmpref));
+    if(ret!=1) break;
+    tmpref[MAXIDSIZE-1]=0;
+    item=strtonum(tmpref);
+    skip_string(fpoi,' ');
+    ret=read_string(fpoi, "\n", tmpref,sizeof(tmpref));
+    if(!ret) break;
+    tmpref[MAXIDSIZE-1]=0;
+
+    row=explode(tmpref,' ',mycols);
+    if(column>=mycols)
+    {
+      ret|=1;
+    }
+    else
+    {
+      refs[item]=strtonum(row[column]);
+    }
+    if(row) delete [] row;
+  }
+  while(1);
+  fclose(fpoi);
+  return 0;
+}
+
+int Read2daStrref(loc_entry entry, CIntMapInt &refs, int column)
+{
+  int fhandle;
+
+  refs.RemoveAll();
+  xorflag=position=0;
+  fhandle=locate_file(entry, 0);
+  if(fhandle<1) return -1;
+  return Read2daFromFile2(fhandle, refs, entry.size, column);
+}
+
+//////////////////
+int Read2daFromFile3(int fhandle, CStringMapArray &refs, int length, int columns)
+{
+  char tmpref[MAXIDSIZE];
+  CString ref, item;
+  int cnt;
+  int ret;
+  FILE *fpoi;
+  int num;
+  int i;
+
+  refs.RemoveAll();
+  if(length<0) maxlength=filelength(fhandle);
+  else maxlength=length;
+  fpoi=fdopen(fhandle,"rb");
+  if(!fpoi)
+  {
+    close(fhandle);
+    return -1;
+  }
+  read_string(fpoi, "\n"); //skipping crap
+  read_string(fpoi, "\n"); //skipping crap
+  read_string(fpoi, "\n"); //skipping crap
+  do
+  {
+    num=0;
+    ret=read_string(fpoi, " ", tmpref,sizeof(tmpref));
+    if(ret!=1) break;
+    tmpref[MAXIDSIZE-1]=0;
+    item=tmpref;
+    item.MakeUpper();
+    for(i=0;i<columns;i++)
+    {
+      skip_string(fpoi,' ');
+      if(i==columns-1)
+      {
+        ret=read_string(fpoi, "\n", tmpref,sizeof(tmpref));
+        if(ret==3)
+        {
+          read_string(fpoi,"\n");
+          ret=1; //overflow        
+        }
+      }
+      else ret=read_string(fpoi, " ", tmpref,sizeof(tmpref));
+      if(ret!=1) break;
+      tmpref[MAXIDSIZE-1]=0;
+      cnt=strtonum(tmpref);
+      refs[item].data[i]=cnt;
+    }
+  }
+  while(1);
+  fclose(fpoi);
+  return 0;
+}
+
+int Read2daArray(loc_entry entry, CStringMapArray &refs, int columns)
+{
+  int fhandle;
+
+  xorflag=position=0;
+  fhandle=locate_file(entry, 0);
+  if(fhandle<1) return -1;
+  return Read2daFromFile3(fhandle, refs, entry.size, columns);
+}
+
+//int defaultvalue
+int Write2daArrayToFile(CString daname, CStringMapArray &refs, CString *columnames, int columns)
+{
+  CString filename;
+  CString key;
+  CString tmpstr;
+  FILE *fpoi;
+  int ret;
+  POSITION pos;
+  tooltip_data value;
+  int i;
+  int namelen;
+
+  namelen=10;
+  pos=refs.GetStartPosition();
+  while(pos)
+  {
+    refs.GetNextAssoc(pos, key, value);
+    if(namelen<key.GetLength() )
+    {
+      namelen=key.GetLength();
+      if(namelen>30)
+      {
+        namelen=30;
+        break;
+      }
+    }
+  }
+  filename=bgfolder+CString("override\\")+daname+".2da";
+  fpoi=fopen(filename,"wb");
+  if(!fpoi) return -2;
+  ret=WriteString(fpoi,"2DA V1.0\r\n-1\r\n");
+  if(ret) return ret;
+  tmpstr.Format("%-*s",namelen," ");
+  ret=WriteString(fpoi, tmpstr);
+
+  for(i=0;i<columns;i++)
+  {
+    tmpstr.Format(" %-10s",columnames[i]);
+    ret=WriteString(fpoi, tmpstr);
+  }
+  tmpstr="\r\n";
+  ret=WriteString(fpoi, tmpstr);
+
+  pos=refs.GetStartPosition();
+  while(pos)
+  {
+    refs.GetNextAssoc(pos, key, value);
+    tmpstr.Format("%-*s",namelen,key);
+    ret=WriteString(fpoi,tmpstr);
+    if(ret) return ret;
+    for(i=0;i<columns;i++)
+    {
+      tmpstr.Format(" %-10d",value.data[i]);
+      ret=WriteString(fpoi,tmpstr);
+      if(ret) return ret;
+    }
+    tmpstr="\r\n";
+    ret=WriteString(fpoi, tmpstr);
+  }
+  fclose(fpoi);
+  return 0;
+}
+
+int read_until(char c, FILE *fpoi, CString &ret, int maxlength=65535)
+{
+  int stop, act;
+  char *poi;
+  int chr;
+  bool flg;
+
+  stop=0;
+  poi=ret.GetBuffer(act=MAXIDSIZE);
+  flg=true;
+  do
+  {
+    chr=mygetc(fpoi);
+    if(chr==EOF) break;
+    if(chr==c) break;
+    if(flg && (chr=='\n'))
+    {
+      ungetc('\n',fpoi);
+      chr='\r';
+      flg=false;
+    }
+    else flg=true;
+    if(stop<maxlength)
+    {
+      poi[stop++]=(char) chr;
+    }
+    if(stop>act) return -1;
+    if(stop==act )
+    {
+      ret.ReleaseBuffer(act);
+      poi=ret.GetBuffer(act=act+MAXIDSIZE);
+    }
+  }
+  while( (stop<=maxlength) && poi[stop]!=EOF);
+  ret.ReleaseBuffer(stop);
+  return stop;
+}
+
+static CString validtypes="AIOPS0";
+//GG = sf(ADD_GLOBAL,ADD_GLOBAL)
+static int merged[]={0x0303,-1};
+
+int add_compiler_data(CString prototype, int cnt, CStringMapCompiler &at_data, int trigger_or_action)
+{
+  CString name;
+  CString parlist;
+  CString subtype;
+  compiler_data compiler_data;
+  CString *params;
+  parameter_data *parpoi;
+  int ret;
+  int j;
+  int p1,p2;
+  int objectcnt;
+  int pointcnt;
+  int intcnt;
+  int actioncnt;
+  int speccnt;
+  int stringcnt;
+  int chkflag, flg;
+
+  p1=prototype.Find('(');
+  if(p1<1)
+  {
+    return -1; //no name
+  }
+  p2=prototype.Find(')',p1);
+  if(p2<0)
+  {
+    return -1;
+  }
+  name=prototype.Left(p1);
+  name.MakeLower();
+
+  if(at_data.Lookup(name,compiler_data))
+  {
+    delete [] compiler_data.parameters;
+  }
+  compiler_data.parameters=NULL;
+  compiler_data.parnum=0;
+  compiler_data.opcode=cnt;
+  //hardcoded information
+  if(trigger_or_action) flg=chkflag=handle_trigger(cnt);
+  else flg=chkflag=handle_action(cnt);
+  if((flg&0xff00)==0xff00) flg=0x104; //special
+
+  parlist=prototype.Mid(p1+1, prototype.GetLength()-p1-2);
+  params=explode(parlist, ',', compiler_data.parnum);
+  if(!params)
+  {
+    if(compiler_data.parnum)
+    {
+      return -1;
+    }
+  }
+  else
+  {
+    compiler_data.parameters=new parameter_data[compiler_data.parnum];
+    if(!compiler_data.parameters)
+    {
+      delete [] params;
+      return -3;
+    }
+    objectcnt=0;
+    pointcnt=0;
+    intcnt=0;
+    actioncnt=0;
+    stringcnt=0;
+    speccnt=0;
+    for(j=0;j<compiler_data.parnum;j++)
+    {
+      switch(params[j].GetAt(0))
+      {
+      case 'A': actioncnt++; break;
+      case 'I': intcnt++; break;
+      case 'P': pointcnt++; break;
+      case 'O': objectcnt++; break;
+      case 'S': stringcnt++; break;
+      }
+    }
+    if(actioncnt>1 || intcnt>3 || pointcnt>1 || objectcnt>2 || stringcnt>4)
+    {
+      goto endofquest;
+    }
+
+    //special merged strings
+    if(member_array(chkflag,merged)==-1) speccnt=1;
+
+    objectcnt=0;
+    pointcnt=0;
+    intcnt=0;
+    actioncnt=0;
+    stringcnt=0;
+    for(j=0;j<compiler_data.parnum;j++)
+    {
+      parpoi=&compiler_data.parameters[j];
+      p1=params[j].Find(':');
+      if(p1!=1) //type should be 1 character
+      {
+        goto endofquest;
+      }
+      p2=params[j].Find('*',p1);
+      if(p2==-1)
+      {
+        p2=params[j].GetLength(); //missing * hack        
+      }
+      else
+      {
+        parpoi->idsfile=params[j].Mid(p2+1);
+        parpoi->idsfile.MakeUpper();
+      }
+      if(p2<=p1)
+      {
+        goto endofquest;
+      }
+      subtype=params[j].Mid(p1+1,p2-p1-1);
+      subtype.MakeLower();
+      parpoi->type=params[j].GetAt(0);
+      if(validtypes.Find((char) parpoi->type)<0) //invalid type
+      {
+        goto endofquest;
+      }
+      switch(parpoi->type)
+      {
+      case 'P': //only one P
+        if(pointcnt)
+        {
+          goto endofquest;
+        }
+        if(parpoi->idsfile.GetLength() )
+        {
+          goto endofquest;
+        }
+        pointcnt++;
+        break;
+      case '0': //zero object
+        break;
+      case 'O': //one or two objects (2./3. object position)
+        if(parpoi->idsfile.GetLength() )
+        {
+          goto endofquest;
+        }
+        switch(objectcnt)
+        {
+        case 0: break;
+        case 1:
+          parpoi->type=SPT_SECONDOB; //second object (3. place)
+          break;
+        default:
+          goto endofquest;
+        }
+        objectcnt++;
+        break;
+      case 'A': //one action (slips in 1. object)
+        if(actioncnt)
+        {
+          goto endofquest;
+        }
+        if(parpoi->idsfile.GetLength() )
+        {
+          goto endofquest;
+        }
+        actioncnt++;
+        if(compiler_data.parameters[0].type=='O')
+        {//the only way of slipping in the first object
+         //the parsing of 'A' will resolve the 2/3. objects
+          compiler_data.parameters[0].type=SPT_OVERRIDE;
+        }
+        break;
+      case 'S': //if args > 2 then first 2 strings are merged
+        if(stringcnt>3) //we allow only 3 strings
+        {
+          goto endofquest;
+        }
+        if(parpoi->idsfile.GetLength() )
+        {
+          goto endofquest;
+        }
+        //special strings
+        if(stringcnt)
+        {
+          if((flg&0xff)==ADD_VAR)
+          {
+            if(stringcnt==1)
+            {
+              parpoi->type=SPT_AREA1;
+            }
+            else if(stringcnt==3)
+            {
+              parpoi->type=SPT_AREA2;
+            }
+            goto endofcase;
+          }
+          switch(flg&0xff)
+          {
+          case IS_VAR:
+            nop();
+            break;
+          case ADD_GLOBAL: case ADD_LOCAL:
+          case ADD_VAR2: case ADD_VAR: parpoi->type=SPT_VAR2; break;
+          case CHECK_DEAD: parpoi->type=SPT_DEAD2; break;
+          case CHECK_NUM: parpoi->type=SPT_NUMLIST2; break;
+          case VALID_TAG:
+            parpoi->type=SPT_TOKEN2; break; //only tokens are allowed
+          case CHECK_XPLIST: parpoi->type=SPT_COLUMN2; break; //column in xplist
+          default: parpoi->type=SPT_RESREF2; break;
+          }
+          /*
+          if(subtype=="token")
+          {
+//            parpoi->type=SPT_TOKEN2;
+          }
+          else if(subtype=="resref" || subtype=="scriptname" || subtype=="newobject")
+          {
+            parpoi->type=SPT_RESREF2; //max 8 characters long
+          }
+          else if(subtype=="column" || subtype=="vartableentry")
+          {
+//            parpoi->type=SPT_COLUMN2; //column in xplist
+          }
+          else
+          {
+            if((stringcnt==1) && speccnt )
+            {
+              parpoi->type=SPT_AREA1;
+            }
+            else if(stringcnt==3)
+            {
+              parpoi->type=SPT_AREA2;
+            }
+            else parpoi->type=SPT_VAR2;
+          }
+          */
+        }
+        else
+        {
+          switch(flg&0xff)
+          {
+          case IS_VAR:
+            nop();
+            break;
+          case ADD_GLOBAL: case ADD_LOCAL:
+          case ADD_VAR2: case ADD_VAR: parpoi->type=SPT_VAR1; flg<<=8; break;
+          case ADD_DEAD: case CHECK_DEAD: parpoi->type=SPT_DEAD1; break;
+          case CHECK_NUM: parpoi->type=SPT_NUMLIST1; break;
+          case VALID_TAG:
+            parpoi->type=SPT_TOKEN1; break; //only tokens are allowed
+          case CHECK_XPLIST: parpoi->type=SPT_COLUMN1; break; //column in xplist
+          default: parpoi->type=SPT_RESREF1; break;
+          }
+        }
+endofcase:
+        flg>>=8;
+        stringcnt++;
+        break;
+      case 'I':
+        if(intcnt>2)
+        {
+          goto endofquest;
+        }
+        if(parpoi->idsfile.GetLength() )
+        {
+          ret=get_idsfile(parpoi->idsfile, false); //not forced
+          switch(ret)
+          {
+          case -3:
+            MessageBox(0,"Out of memory","Warning",MB_ICONWARNING|MB_OK);
+            break;
+          case 0:
+            break;
+          default:
+            MessageBox(0,"Can't load "+parpoi->idsfile+".ids!","Warning",MB_ICONWARNING|MB_OK);
+            break;
+          }
+        }
+        switch(intcnt)
+        {
+        case 0:
+          //additional check/parsing an embedded string reference
+          if(chkflag&0x10000)
+          {
+            parpoi->type=SPT_STRREF;
+            break;
+          }
+          if(subtype=="0")
+          {
+            parpoi->type=SPT_ZERO;
+            break;
+          }
+          parpoi->type=SPT_INTEGER;
+          break;
+        case 1:
+          if(subtype=="0")
+          {
+            parpoi->type=SPT_ZERO;
+            break;
+          }
+          if(subtype=="strref")
+          {
+            parpoi->type=SPT_STRREF2;
+            break;
+          }
+          parpoi->type=SPT_INTEGER2;
+          break;
+        case 2:
+          if(subtype=="0")
+          {
+            parpoi->type=SPT_ZERO;
+            break;
+          }
+          if(subtype=="strref")
+          {
+            parpoi->type=SPT_STRREF3;
+            break;
+          }
+          parpoi->type=SPT_INTEGER3;
+          break;
+        }
+        intcnt++;
+        break;
+      default: //no other argument types are allowed
+        goto endofquest;
+      }
+    }
+    delete [] params;
+  }
+  at_data[name]=compiler_data;   //store it
+  return 0;
+endofquest:
+  delete [] params;
+  return -1;
+}
+
+int lookup_id(CString filename, CString token, int &value)
+{
+  CStringMapInt *idsfile;
+  int x, len, hex;
+  int ch;
+
+  if(token.IsEmpty()) return -12;
+  if( isdigit(token[0]) ||
+    ( (token.GetLength()>1) && (token[0]=='-') && (isdigit(token[1]) ) )
+    )//accept negative values
+  {
+    len=token.GetLength();
+    hex=0;
+    for(x=1;x<len;x++)
+    {
+      ch=token[x];
+      if(ch>='0' && ch<='9') continue;
+      if(ch>='a' && ch<='f' && hex) continue;
+      if(ch!='x' || x!=1 || hex || token[0]!='0') return -12;
+      hex=1;
+    }
+    value=strtonum(token);
+    return 0; //no resolve needed
+  }
+  if(filename.IsEmpty()) return -17; //no ids file allowed
+  if(!idsmaps.Lookup(filename,idsfile)) return -18; //no such ids file
+  if(!idsfile) return -99;   //internal error (no file loaded)
+  if(idsfile->Lookup(token, value)) return 1; //resolved successfully
+  return -12; //not an IDS lookup value
+}
+
+int convert_strref(CString string)
+{
+  if(string.Left(1)!="~") return -13; //not a string
+  if(string.Right(1)!="~") return -13; //not a string
+  return 0;
+}
+
+int convert_string(CString string, char *poi, int maxlength, int longerr)
+{
+  int i;
+  char ch;
+
+  if(string.GetLength()>maxlength+2) return longerr; //string too long
+  if(string.Left(1)!="\"") return -13; //not a string
+  if(string.Right(1)!="\"") return -13; //not a string
+  for(i=0;i<=maxlength;i++)
+  {
+    ch=string[i+1];
+    if(ch=='"')
+    {
+      poi[i]=0;
+      break;
+    }
+    poi[i]=(char) toupper(ch);
+  }
+  return 0;
+}
+
+int convert_point(CString token, int &value1, int &value2)
+{
+  int p1;
+  CString tmp;
+
+  if(token.Left(1)!="[") return -11; //not a point
+  if(token.Right(1)!="]") return -11; //not a point
+  p1=token.Find('.');
+  if(p1<1) return -11;
+  tmp=token.Mid(1,p1-1);
+  value1=strtonum(tmp);
+  if(!value1)
+  {
+    if((tmp.GetLength()!=1) || (tmp[0]!='0')) return -11;
+  }
+  tmp=token.Mid(p1+1,token.GetLength()-p1-2);
+  value2=strtonum(tmp);
+  if(!value2)
+  {
+    if((tmp.GetLength()!=1) || (tmp[0]!='0')) return -11;
+  }
+  return 0;
+}
+
+int convert_object(CString line, object &ob, int recursion_level)
+{
+  CString *values;
+  CString name;
+  int p1,p2;
+  int ret;
+
+  if(line.IsEmpty() )
+  {
+    if(recursion_level) return 0;
+    else return -10;
+  }
+  if(recursion_level>5) return -31;
+  ob.bytes[0]=recursion_level+1;
+
+  switch(line[0])
+  {
+  case '"': //death variable (string);
+    /* sadly we do not know if this is a creature
+    if(pst_compatible_var()) return convert_string(line, ob.var, 32, -10);
+    else return convert_string(line, ob.var, 18, -10);
+    */
+    return convert_string(line, ob.var, 32, -10);
+    break;
+  case '[': //IDS descriptor (ea,general,race,class,gender,alignment,specific)
+    p2=line.ReverseFind(']');
+    if(p2<0) return -2;
+    values=explode(line.Mid(1,p2-1),'.',p1);
+    if(!p1 || !values) return -10;
+    for(p2=0;p2<p1;p2++)
+    {
+      ret=lookup_id(IDSName(p2,false),values[p2],ob.bytes[p2]);
+      if(ret<0)
+      {
+        delete [] values;
+        return ret;
+      }
+    }
+    delete [] values;
+    break;
+  default:  //object (using object.ids)
+    p1=line.Find('(');
+    if(p1<1)
+    {
+      //accept Myself without brackets for now
+      ret=lookup_id("OBJECT", line, ob.bytes[recursion_level+1]);
+      if(ret==-12) ret=-22;
+      return ret;
+    }
+    p2=line.ReverseFind(')');
+    if(p2<p1)
+    {
+      return -2; //no closing bracket
+    }
+    name=line.Left(p1);
+    ret=lookup_id("OBJECT", name, ob.bytes[recursion_level]);
+    if(ret<0) return ret;
+    if(ret==0) return -10; // don't accept numbers
+    return convert_object(line.Mid(p1+1,p2-p1-1), ob, recursion_level+1);
+    break;
+  }
+  return 0;
+}
+
+int compile_trigger(CString line, trigger &trigger)
+{
+  CString name;
+  CString tmpstr;
+  CString *args;
+  int argnum;
+  int j;
+  int p1,p2;
+  compiler_data compiler_data;
+//  int chkflag;
+  char tmparea[8];
+  int ret;
+
+  trigger.Reset();
+  p1=line.Find("//");              //it may contain remarks but we simply cut them
+  if(p1>=0) line=line.Left(p1);
+  line.TrimRight();                //cut any remaining spaces too
+  if(line.IsEmpty()) return 0;     //not important
+  line.MakeLower();
+  p1=line.Find('(');
+  if(p1<1)
+  {
+    return -1; //no name
+  }
+  p2=line.ReverseFind(')');
+  if(p2<p1)
+  {
+    return -2; //no closing bracket
+  }
+  if(p2+1!=line.GetLength() )
+  {
+    return -9; //crap after closing bracket
+  }
+  name=line.Left(p1);
+  if(name.GetAt(0)=='!') name=name.Mid(1);
+  if(!trigger_data.Lookup(name, compiler_data) )
+  {
+    return -3; //invalid trigger
+  }
+  trigger.opcode=compiler_data.opcode;
+//  chkflag=handle_trigger(trigger.opcode); //information for variable grouping (apparently needed)
+  tmpstr=line.Mid(p1+1, p2-p1-1);
+  args=explode2(tmpstr, argnum);
+  if(argnum<0) return argnum; //direct error from the argument explosion level
+  if(argnum>4)
+  {
+    delete [] args;
+    return -8; //too many arguments
+  }
+  if(argnum!=compiler_data.parnum)
+  {
+    delete [] args;
+    return -4;
+  }
+  for(j=0;j<argnum;j++)
+  {
+    switch(compiler_data.parameters[j].type)
+    {
+    case SPT_ACTION:   //actionoverride in trigger, no way!
+    case SPT_OVERRIDE: //actionoverrided object
+    case SPT_SECONDOB: //second object in trigger
+    case SPT_POINT:    //apparently no points in triggers
+    case SPT_STRREF:   //no strref in triggers
+    case SPT_STRREF2:
+    case SPT_STRREF3:
+      delete [] args;
+      return -30;
+    case '0':  //null argument, simply skip it (not in the original compiler)
+      break;
+    default:
+      delete [] args;
+      return -99;
+    case SPT_INTEGER:
+      //lookup IDS, or simple number
+      ret=lookup_id(compiler_data.parameters[j].idsfile, args[j], trigger.bytes[0]);
+      if(ret<0)
+      {
+        delete [] args;
+        return ret;
+      }
+      break;
+    case SPT_INTEGER2:
+      ret=lookup_id(compiler_data.parameters[j].idsfile, args[j], trigger.bytes[2]);
+      if(ret<0)
+      {
+        delete [] args;
+        return ret;
+      }
+      break;
+    case SPT_DEAD1:
+    case SPT_VAR1: 
+      ret=convert_string(args[j],trigger.var1,32,-15);
+      if(ret<0)
+      {
+        delete [] args;
+        return ret;
+      }
+//check variable
+      break;
+    case SPT_COLUMN1:
+      ret=convert_string(args[j],trigger.var1,32,-15);
+      if(ret<0)
+      {
+        delete [] args;
+        return ret;
+      }
+//check column ???
+      break;
+    case SPT_TOKEN1:
+      ret=convert_string(args[j],trigger.var1,32,-15);
+      if(ret<0)
+      {
+        delete [] args;
+        return ret;
+      }
+//check tokens ???
+      break;
+    case SPT_RESREF1: //resource
+      ret=convert_string(args[j],trigger.var1,8,-15);
+      if(ret<0)
+      {
+        delete [] args;
+        return ret;
+      }
+      break;
+    case SPT_AREA1: //area (only for var1)
+      ret=convert_string(args[j],tmparea,6,-14);
+      if(ret<0)
+      {
+        delete [] args;
+        return ret;
+      }
+      if(strlen(tmparea)!=6)
+      {
+        delete [] args;
+        return -14;
+      }
+      memmove(trigger.var1+6,trigger.var1,32);
+      memcpy(trigger.var1,tmparea,6);
+      break;
+    case SPT_VAR2:
+      ret=convert_string(args[j],trigger.var2,32,-15);
+      if(ret<0)
+      {
+        delete [] args;
+        return ret;
+      }
+//check variable
+      break;
+    case SPT_COLUMN2:
+      ret=convert_string(args[j],trigger.var2,32,-15);
+      if(ret<0)
+      {
+        delete [] args;
+        return ret;
+      }
+//check column ???
+      break;
+    case SPT_TOKEN2:
+      ret=convert_string(args[j],trigger.var2,32,-15);
+      if(ret<0)
+      {
+        delete [] args;
+        return ret;
+      }
+//check tokens ???
+      break;
+    case SPT_RESREF2: //resource
+      ret=convert_string(args[j],trigger.var2,8,-15);
+      if(ret<0)
+      {
+        delete [] args;
+        return ret;
+      }
+      break;
+    case SPT_AREA2: //area (only for var1)
+      ret=convert_string(args[j],tmparea,6,-14);
+      if(ret<0)
+      {
+        delete [] args;
+        return ret;
+      }
+      if(strlen(tmparea)!=6)
+      {
+        delete [] args;
+        return -14;
+      }
+      memmove(trigger.var2+6,trigger.var2,32);
+      memcpy(trigger.var2,tmparea,6);
+      break;
+    case SPT_OBJECT: //only O exists in triggers
+      //object (may be string)
+      ret=convert_object(args[j],trigger.trobj,0);
+      if(ret<0)
+      {
+        delete [] args;
+        return ret;
+      }
+      break;
+    }
+  }
+  delete [] args;
+  return 0;
+}
+
+int compile_action(CString line, action &action, bool inoverride)
+{
+  CString name;
+  CString tmpstr;
+  CString *args;
+  int argnum;
+  int j;
+  int p1,p2;
+  compiler_data compiler_data;
+  int ret;
+//  int chkflag;
+  char tmparea[8];
+
+  if(!inoverride) action.Reset();
+  p1=line.Find("//");              //it may contain remarks but we simply cut them
+  if(p1>=0) line=line.Left(p1);
+  line.TrimRight();                //cut any remaining spaces too
+  if(line.IsEmpty()) return 0;
+  line.MakeLower();
+  p1=line.Find('(');
+  if(p1<1)
+  {
+    return -1; //no name
+  }
+  p2=line.ReverseFind(')');
+  if(p2<p1)
+  {
+    return -2; //no closing bracket
+  }
+  if(p2+1!=line.GetLength() )
+  {
+    return -9; //crap after closing bracket
+  }
+  name=line.Left(p1);
+  //no ! needed in actions
+  if(!action_data.Lookup(name, compiler_data) )
+  {
+    return -3; //invalid action
+  }
+  action.opcode=compiler_data.opcode;
+//  chkflag=handle_action(action.opcode); //information for variable grouping (apparently needed)
+  tmpstr=line.Mid(p1+1, p2-p1-1);
+  args=explode2(tmpstr, argnum);
+  if(argnum<0) return argnum; //direct error from the argument explosion level
+  if(argnum>6)
+  {
+    delete [] args;
+    return -8; //too many arguments
+  }
+  if(argnum!=compiler_data.parnum)
+  {
+    delete [] args;
+    return -4;
+  }
+  for(j=0;j<argnum;j++)
+  {
+    switch(compiler_data.parameters[j].type)
+    {
+    case 0:  //null argument
+      break;
+    default:
+      delete [] args;
+      return -99;
+    case SPT_ACTION:
+      //actionoverride, we have to recompile this, only one level of recursion is allowed
+      if(inoverride)
+      {
+        delete [] args;
+        return -30;
+      }
+      ret=compile_action(args[j],action,true);
+      if(ret<0)
+      {
+        delete [] args;
+        return ret;
+      }
+      break;
+    case SPT_INTEGER:
+      //lookup IDS
+      ret=lookup_id(compiler_data.parameters[j].idsfile, args[j], action.bytes[0]);
+      if(ret<0)
+      {
+        delete [] args;
+        return ret;
+      }
+      break;
+    case SPT_STRREF:
+      if(!(editflg&WEIDUSTRING))
+      {
+        ret=convert_strref(args[j]);
+        if(!ret) break;
+      }
+      //lookup IDS
+      ret=lookup_id(compiler_data.parameters[j].idsfile, args[j], action.bytes[0]);
+      if(ret<0)
+      {
+        delete [] args;
+        return ret;
+      }
+      if(action.bytes[0]>=tlk_headerinfo.entrynum)
+      {
+        delete [] args;
+        return -20;
+      }
+      break;
+    case SPT_INTEGER2:
+      //2. integer
+      ret=lookup_id(compiler_data.parameters[j].idsfile, args[j], action.bytes[3]);
+      if(ret<0)
+      {
+        delete [] args;
+        return ret;
+      }
+      break;
+    case SPT_STRREF2:
+      //2. integer
+      ret=lookup_id(compiler_data.parameters[j].idsfile, args[j], action.bytes[3]);
+      if(ret<0)
+      {
+        delete [] args;
+        return ret;
+      }
+      if(action.bytes[3]>=tlk_headerinfo.entrynum)
+      {
+        delete [] args;
+        return -20;
+      }
+      break;
+    case SPT_INTEGER3:
+      //3. integer
+      ret=lookup_id(compiler_data.parameters[j].idsfile, args[j], action.bytes[4]);
+      if(ret<0)
+      {
+        delete [] args;
+        return ret;
+      }
+      break;
+    case SPT_STRREF3:
+      //3. integer
+      ret=lookup_id(compiler_data.parameters[j].idsfile, args[j], action.bytes[4]);
+      if(ret<0)
+      {
+        delete [] args;
+        return ret;
+      }
+      if(action.bytes[4]>=tlk_headerinfo.entrynum)
+      {
+        delete [] args;
+        return -20;
+      }
+      break;
+    case SPT_VAR1:
+      ret=convert_string(args[j],action.var1,32,-15);
+      if(ret<0)
+      {
+        delete [] args;
+        return ret;
+      }
+//check variable
+      break;
+    case SPT_COLUMN1:
+      ret=convert_string(args[j],action.var1,32,-15);
+      if(ret<0)
+      {
+        delete [] args;
+        return ret;
+      }
+//check column ???
+      break;
+    case SPT_TOKEN1:
+      ret=convert_string(args[j],action.var1,32,-15);
+      if(ret<0)
+      {
+        delete [] args;
+        return ret;
+      }
+//check tokens ???
+      break;
+    case SPT_RESREF1: //resource
+      ret=convert_string(args[j],action.var1,8,-15);
+      if(ret<0)
+      {
+        delete [] args;
+        return ret;
+      }
+      break;
+    case SPT_AREA1: //area (only for var1)
+      ret=convert_string(args[j],tmparea,6,-14);
+      if(ret<0)
+      {
+        delete [] args;
+        return ret;
+      }
+      if(strlen(tmparea)!=6)
+      {
+        delete [] args;
+        return -14;
+      }
+      memmove(action.var1+6,action.var1,32);
+      memcpy(action.var1,tmparea,6);
+      break;
+    case SPT_VAR2:
+      ret=convert_string(args[j],action.var2,32,-15);
+      if(ret<0)
+      {
+        delete [] args;
+        return ret;
+      }
+//check variable
+      break;
+    case SPT_COLUMN2:
+      ret=convert_string(args[j],action.var2,32,-15);
+      if(ret<0)
+      {
+        delete [] args;
+        return ret;
+      }
+//check column ???
+      break;
+    case SPT_TOKEN2:
+      ret=convert_string(args[j],action.var2,32,-15);
+      if(ret<0)
+      {
+        delete [] args;
+        return ret;
+      }
+//check tokens ???
+      break;
+    case SPT_RESREF2: //resource
+      ret=convert_string(args[j],action.var2,8,-15);
+      if(ret<0)
+      {
+        delete [] args;
+        return ret;
+      }
+      break;
+    case SPT_AREA2: //area (only for var1)
+      ret=convert_string(args[j],tmparea,6,-14);
+      if(ret<0)
+      {
+        delete [] args;
+        return ret;
+      }
+      if(strlen(tmparea)!=6)
+      {
+        delete [] args;
+        return -14;
+      }
+      memmove(action.var2+6,action.var2,32);
+      memcpy(action.var2,tmparea,6);
+      break;
+    case SPT_POINT: //only one point allowed
+      ret=convert_point(args[j], action.bytes[1], action.bytes[2]);
+      if(ret<0)
+      {
+        delete [] args;
+        return ret;
+      }
+      //point
+      break;
+    case SPT_SECONDOB: //param2 object (3. place)
+      ret=convert_object(args[j],action.obj[2],0);
+      if(ret<0)
+      {
+        delete [] args;
+        return ret;
+      }
+      break;
+    case SPT_OBJECT: //regular param1 object (2. place)
+      ret=convert_object(args[j],action.obj[1],0);
+      if(ret<0)
+      {
+        delete [] args;
+        return ret;
+      }
+      break;
+    case SPT_OVERRIDE: //actionoverride object (1. place)
+      ret=convert_object(args[j],action.obj[0],0);
+      if(ret<0)
+      {
+        delete [] args;
+        return ret;
+      }
+      break;
+    }
+  }
+  delete [] args;
+  return 0;
+}
+
+int read_next_desc(FILE *fpoi)
+{
+  CString tmpstr;
+  CString tmp;
+  int len;
+  int index;  
+
+  len=read_until(',',fpoi,tmp);
+  if(!len)
+  {
+    return 2;
+  }
+  index=atoi(tmp); //only dec
+  if(index<0 || index>=NUM_FEATS) return -1;
+
+  len=read_until(',',fpoi,tmp);
+  if(!len)
+  {
+    return -1;
+  }
+  fgetc(fpoi); //skip
+  len=read_until('"',fpoi,tmp);
+  if(!len)
+  {
+    return -1;
+  }
+  if(opcodes[index].opcodetext.IsEmpty())
+  {
+    opcodes[index].opcodetext=tmp;
+  }
+
+  read_until('"',fpoi,tmp);
+//  skip_string(fpoi,',');
+//  skip_string(fpoi,'"');
+  len=read_until('"',fpoi,tmp);
+  if(opcodes[index].par1.IsEmpty())
+  {
+    opcodes[index].par1=tmp;
+  }
+
+  read_until('"',fpoi,tmp);
+//  skip_string(fpoi,',');
+//  skip_string(fpoi,'"');
+  len=read_until('"',fpoi,tmp);
+  if(!len)
+  {
+    return -1;
+  }
+  if(opcodes[index].par2.IsEmpty())
+  {
+    opcodes[index].par2=tmp;
+  }
+
+  read_until('"',fpoi,tmp);
+//  skip_string(fpoi,',');
+//  skip_string(fpoi,'"');
+  len=read_until('"',fpoi,tmp);
+  if(!len)
+  {
+    return -1;
+  }
+  if(opcodes[index].opcodedesc.IsEmpty())
+  {
+    opcodes[index].opcodedesc=tmp;  
+  }
+
+  skip_string(fpoi,'\n');
+  return 0;
+}
+
+int read_effect_descs()
+{
+  FILE *fpoi;
+  int i;
+  int ret;
+
+  for(i=0;i<NUM_FEATS;i++)
+  {
+    opcodes[i].opcodetext.Empty();
+    opcodes[i].opcodedesc.Empty();
+    opcodes[i].par1.Empty();
+    opcodes[i].par2.Empty();
+  }
+  if(descpath.IsEmpty())
+  {
+    return 1;
+  }
+  
+  maxlength=-1;
+  xorflag=0;
+  position=0;
+  fpoi=fopen(descpath,"rt");
+  if(fpoi<=0) return -1;
+  ret=0;
+  do
+  {
+    ret=read_next_desc(fpoi);
+    if(ret)
+    {
+      if(ret==2) ret=0;
+      break;
+    }
+  }
+  while(1);
+  fclose(fpoi);
+  return ret;  
+}
+
+int get_idsfile(CString idsname, bool forced)
+{
+  loc_entry tmploc;
+  CStringMapInt *idsfile;
+  int val;
+
+  if(idsname.GetLength()>8) idsname=idsname.Left(8);
+  if(forced)
+  {
+    if(idsmaps.Lookup(idsname, idsfile))
+    {
+      delete idsfile;
+      idsmaps.RemoveKey(idsname);
+    }
+  }
+  if(!idsmaps.Lookup(idsname, idsfile) ) //reading idsfile if it hasn't been read yet
+  {
+    if(!idrefs.Lookup(idsname,tmploc) ) return -1;
+    idsfile=new CStringMapInt;
+    if(!idsfile) return -3;              //this may have crashed something
+    val=ReadIds4(tmploc, *idsfile);
+    if(val<0)
+    {
+      delete idsfile;
+      return val;
+    }
+    idsmaps[idsname]=idsfile;
+    return val;
+  }
+  return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////
+//2da class (editor)
+
+void C2da::new_2da()
+{
+  POSITION pos;
+  CString tmpstr;
+  CString *tmppoi;
+
+  header="2DA V1.0";
+  defvalue="*";
+  if(collabels)
+  {
+    delete [] collabels;
+    collabels=NULL;
+  }
+  pos=data.GetHeadPosition();
+  while(pos)
+  {
+    tmppoi=(CString *) data.GetNext(pos);
+    delete [] tmppoi;
+  }
+  data.RemoveAll();
+  rows=0;
+  cols=1;
+  killcopyrow();
+  killcopycol();
+}
+
+C2da::C2da()
+{
+
+}
+
+C2da::~C2da()
+{
+  new_2da();
+}
+
+int C2da::RemoveColumn(int col)
+{
+  POSITION pos;
+  CString *add, *row;
+  int i,j;
+
+  if(cols<2) return 0;  //don't remove the row label column
+  if(col>=cols) return 0;
+  add=new CString[cols-1];
+  if(!add) return 0;
+  j=0;
+  for(i=0;i<cols;i++)
+  {
+    if(i!=col) add[j++]=collabels[i];
+  }
+  delete [] collabels;
+  collabels=add;
+  pos=data.GetHeadPosition();
+  while(pos)
+  {
+    add=new CString[cols-1];
+    if(!add) return 0;
+    row=(CString *) data.GetAt(pos);
+    j=0;
+    for(i=0;i<cols;i++)
+    {
+      if(i!=col) add[j++]=row[i];
+    }
+    delete [] row;
+    data.SetAt(pos, add);
+    data.GetNext(pos);
+  }
+  cols--;
+  return 1;
+}
+
+int C2da::CopyColumn(int col)
+{
+  POSITION pos;
+  CString *tmppoi;
+  int row, i;
+
+  if(col>=cols) return 0;
+  killcopycol();
+  copycol=new CString [rows+1];
+  if(!copycol) return 0;
+  crows=rows+1;
+  copycol[0]=collabels[col];
+  i=1;
+  for(row=0;row<rows;row++)
+  {
+    pos=data.FindIndex(row);
+    if(pos)
+    {
+      tmppoi=(CString *) data.GetAt(pos);
+      copycol[i++]=tmppoi[col];
+    }
+  }
+  return 1;
+}
+
+int C2da::PasteColumn(int col)
+{
+  POSITION pos;
+  CString *tmppoi;
+  int row, i;
+
+  if(col>=cols) return 0;
+  if(!copycol) return 0;
+  crows=rows+1;
+  collabels[col]=copycol[0];
+  i=1;
+  for(row=0;row<rows && row<crows;row++)
+  {
+    pos=data.FindIndex(row);
+    if(pos)
+    {
+      tmppoi=(CString *) data.GetAt(pos);
+      tmppoi[col]=copycol[i++];
+    }
+  }
+  for(;row<rows;row++)
+  {
+    pos=data.FindIndex(row);
+    if(pos)
+    {
+      tmppoi=(CString *) data.GetAt(pos);
+      tmppoi[col]=defvalue;
+    }
+  }
+  return 1;
+}
+
+int C2da::CopyRow(int row)
+{
+  POSITION pos;
+  CString *tmppoi;
+  int i;
+
+  if(row>=rows) return 0;
+  killcopyrow();
+  copyrow=new CString [cols];
+  if(!copyrow) return 0;
+  ccols=cols;
+  pos=data.FindIndex(row);
+  if(pos)
+  {
+    tmppoi=(CString *) data.GetAt(pos);
+    for(i=0;i<cols;i++)
+    {
+      copyrow[i]=tmppoi[i];
+    }
+  }
+  return 1;
+}
+
+int C2da::PasteRow(int row)
+{
+  POSITION pos;
+  CString *tmppoi;
+  int i;
+
+  if(row>=rows) return 0;
+  if(!copyrow) return 0;
+  pos=data.FindIndex(row);
+  if(pos)
+  {
+    tmppoi=(CString *) data.GetAt(pos);
+    for(i=0;i<cols && i<ccols;i++)
+    {
+      tmppoi[i]=copyrow[i];
+    }
+    for(;i<cols;i++)
+    {
+      tmppoi[i]=defvalue;
+    }
+  }
+  return 1;
+}
+
+int C2da::RemoveRow(int row)
+{
+  POSITION pos;
+  CString *tmppoi;
+
+  pos=data.FindIndex(row);
+  if(pos)
+  {
+    tmppoi=(CString *) data.GetAt(pos);
+    delete [] tmppoi;
+    data.RemoveAt(pos);
+    rows--;
+    return 1;
+  }
+  return 0;
+}
+
+//adds a column
+int C2da::AddColumn(int col, CString label, CString def)
+{
+  POSITION pos;
+  CString *add, *row;
+  int i,j;
+
+  if(col>cols) return 0;
+  add=new CString[cols+1];
+  if(!add) return 0;
+  j=0;
+  for(i=0;i<=cols;i++)
+  {
+    if(i==col) add[j++]=label;
+    if(i<cols) add[j++]=collabels[i];
+  }
+  delete [] collabels;
+  collabels=add;
+  pos=data.GetHeadPosition();
+  while(pos)
+  {
+    add=new CString[cols+1];
+    if(!add) return 0;
+    row=(CString *) data.GetAt(pos);
+    j=0;
+    for(i=0;i<=cols;i++)
+    {
+      if(i==col) add[j++]=def;
+      if(i<cols) add[j++]=row[i];
+    }
+    delete [] row;
+    data.SetAt(pos, add);
+    data.GetNext(pos);
+  }
+  cols++;
+  return 1;
+}
+
+void C2da::AddRow(int row, CString label, CString def)
+{
+  POSITION pos;
+  CString *add;
+  int i;
+
+  if(!cols) return;
+  add=new CString[cols];
+  if(!add) return;
+  add[0]=label;
+  for(i=1;i<cols;i++)
+  {
+    add[i]=def;
+  }
+  pos=data.FindIndex(row);
+  if(pos)
+  {
+    data.InsertBefore(pos,add);
+  }
+  else
+  {
+    data.AddTail(add);
+  }
+  rows++;
+}
+
+CString C2da::GetValue(int row, int col)
+{
+  CString *tmppoi;
+
+  if(rows<=row) return defvalue;
+  if(cols<=col) return defvalue;
+  tmppoi=(CString *) the_2da.data.GetAt(the_2da.data.FindIndex(row));
+  return tmppoi[col];
+}
+
+int C2da::Write2DAToFile(int fhandle)
+{
+  POSITION pos;
+  CString *tmppoi;
+  FILE *fpoi;
+  CString tmpstr;
+  CString format;
+  int i,j;
+  int width;
+  int namelen;
+
+  namelen=10;
+  pos=data.GetHeadPosition();
+  while(pos)
+  {
+    tmppoi=(CString *) data.GetNext(pos);
+    if(namelen<tmppoi[0].GetLength() )
+    {
+      namelen=tmppoi[0].GetLength();
+      if(namelen>30)
+      {
+        namelen=30;
+        break;
+      }
+    }
+  }
+  fpoi=fdopen(fhandle,"wb");
+  if(!fpoi)
+  {
+    close(fhandle);
+    return -1;
+  }
+  tmpstr.Format("%s\r\n%s\r\n",header,defvalue);
+  WriteString(fpoi,tmpstr);
+  tmpstr.Format("%-*s ",namelen,collabels[0]);
+  WriteString(fpoi,tmpstr);
+  for(i=1;i<cols;i++)
+  {
+    tmpstr.Format("%-10s ",collabels[i]);
+    WriteString(fpoi,tmpstr);
+  }
+  WriteString(fpoi,"\r\n");
+  
+  pos=data.GetHeadPosition();
+  for(i=0;i<rows;i++)
+  {
+    tmppoi=(CString *) data.GetNext(pos);
+    for(j=0;j<cols;j++)
+    {
+      width=collabels[j].GetLength();
+      
+      if(j)
+      {
+        if(width<10) width=10;
+      }
+      else width=namelen;
+      format.Format("%%-%ds ",width);
+      tmpstr.Format(format,tmppoi[j]);
+      WriteString(fpoi,tmpstr);
+    }
+    WriteString(fpoi,"\r\n");
+  }
+  fclose(fpoi);
+  return 0;
+}
+
+int C2da::Read2DAFromFile(int fhandle, int length)
+{
+  CString ref;
+  CString *row;
+  CString *add;
+  FILE *fpoi;
+  int mycols;
+  int ret;
+  int res;
+  int i;
+
+  xorflag=position=0;
+  new_2da();
+  if(length<0) maxlength=filelength(fhandle);
+  else maxlength=length;
+  fpoi=fdopen(fhandle,"rb");
+  if(!fpoi)
+  {
+    close(fhandle);
+    return -1;
+  }
+  read_string(fpoi, "\n", external,sizeof(external)); //header
+  header=external;
+  read_string(fpoi, "\n", external,sizeof(external)); //default
+  defvalue=external;
+  read_string(fpoi, "\n", external,sizeof(external)); //columns
+  external[MAXBUFFSIZE-1]=0;
+  collabels=explode(external,' ',cols);
+  if(cols<1)
+  {
+    fclose(fpoi);
+    return -1;
+  }
+  ret=0;
+  do
+  {
+    external[0]=0;
+    res=read_string(fpoi, "\n",external,sizeof(external));
+    external[MAXBUFFSIZE-1]=0;
+    ref=external;
+    ref.TrimRight();
+    ref.MakeUpper();
+    if(!ref.GetLength()) continue; //skipping empty rows
+    row=explode(external,' ',mycols);
+    if(mycols!=cols)
+    {
+      ret|=1;
+    }
+    add=new CString [cols];
+    if(!add)
+    {
+      if(row) delete [] row;
+      return -3;
+    }
+    for(i=0;i<mycols && i<cols;i++)
+    {
+      add[i]=row[i];
+    }
+    while(i<cols)
+    {
+      add[i]="*"; //default
+      i++;
+    }
+    if(row) delete [] row;
+    data.AddTail(add);
+  }
+  while(res==1 );
+  fclose(fpoi);
+  rows=data.GetCount();
+  return ret;
+}
+
+///////////////////////////////////////////////////////////////////////////
+//ids class (editor)
+
+void Cids::new_ids()
+{
+  POSITION pos;
+  CString tmpstr;
+  CString *tmppoi;
+
+  if(!data) return;
+  header="IDS V1.0";
+  pos=data->GetHeadPosition();
+  while(pos)
+  {
+    tmppoi=(CString *) data->GetNext(pos);
+    delete [] tmppoi;
+  }
+  data->RemoveAll();
+  rows=0;
+  label=true;
+}
+
+Cids::Cids()
+{
+  data=new CPtrList;
+}
+
+Cids::~Cids()
+{
+  new_ids();
+  if(data)
+  {
+    delete data;
+    data=NULL;
+  }
+}
+
+int Cids::RemoveRow(int row)
+{
+  POSITION pos;
+  CString *tmppoi;
+
+  pos=data->FindIndex(row);
+  if(pos)
+  {
+    tmppoi=(CString *) data->GetAt(pos);
+    delete [] tmppoi;
+    data->RemoveAt(pos);
+    rows--;
+    return 1;
+  }
+  return 0;
+}
+
+void Cids::AddRow(int row, CString label, CString def)
+{
+  POSITION pos;
+  CString *add;
+
+  add=new CString[2]; //we let it crash here *shrug*
+  add[0]=label;
+  add[1]=def;
+  pos=data->FindIndex(row);
+  if(pos)
+  {
+    data->InsertBefore(pos,add);
+  }
+  else
+  {
+    data->AddTail(add);
+  }
+  rows++;
+}
+
+int Cids::WriteIDSToFile(int fhandle)
+{
+  POSITION pos;
+  CString *tmppoi;
+  FILE *fpoi;
+  CString tmpstr;
+  CString format;
+  int i;
+
+  fpoi=fdopen(fhandle,"wb");
+  if(!fpoi)
+  {
+    close(fhandle);
+    return -1;
+  }
+  tmpstr.Format("%s\r\n",header);
+  WriteString(fpoi,tmpstr);
+  
+  pos=data->GetHeadPosition();
+  for(i=0;i<rows;i++)
+  {
+    tmppoi=(CString *) data->GetNext(pos);
+    tmpstr.Format("%s %s\r\n",tmppoi[0], tmppoi[1]); //do not format first value
+    WriteString(fpoi,tmpstr);
+  }
+  fclose(fpoi);
+  return 0;
+}
+
+int Cids::ReadIDSFromFile(int fhandle, int length)
+{
+  char tmpref[MAXIDSIZE];
+  CString ref;
+  CString *add;
+  FILE *fpoi;
+  int ret;
+  int res;
+  int pos;
+
+  xorflag=position=0;
+  new_ids();
+  if(length<0) maxlength=filelength(fhandle);
+  else maxlength=length;
+  fpoi=fdopen(fhandle,"rb");
+  if(!fpoi)
+  {
+    close(fhandle);
+    return -1;
+  }
+  read_string(fpoi, "\n", tmpref,sizeof(tmpref)); //skipping crap
+  header=tmpref;
+  label=true;
+  ret=0;
+  do
+  {
+    tmpref[0]=0;
+    res=read_string(fpoi, "\n",tmpref,sizeof(tmpref));
+    tmpref[MAXIDSIZE-1]=0;
+    ref=tmpref;
+    if(!ref.GetLength()) continue; //skipping empty rows
+    pos=ref.FindOneOf(" \t");
+    if(pos<1)
+    {
+      ret|=1;
+      continue;
+    }
+    add=new CString[2]; //we let it crash here
+    add[0]=ref.Left(pos);
+    if(ref.FindOneOf(" ()")==-1)
+    {
+      ref.MakeUpper();
+      label=false;
+    }
+    add[1]=ref.Mid(pos);
+    add[1].TrimLeft();
+    add[1].TrimRight();
+
+    data->AddTail(add);
+  }
+  while(res==1 );
+  fclose(fpoi);
+  rows=data->GetCount();
+  return ret;
+}
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+void Cmus::new_mus()
+{
+  POSITION pos;
+  CString tmpstr;
+  CString *tmppoi;
+
+  if(!data) return;
+  pos=data->GetHeadPosition();
+  while(pos)
+  {
+    tmppoi=(CString *) data->GetNext(pos);
+    delete [] tmppoi;
+  }
+  data->RemoveAll();
+  rows=0;
+}
+
+Cmus::Cmus()
+{
+  data=new CPtrList; //cannot really do anything
+}
+
+Cmus::~Cmus()
+{
+  new_mus();
+  if(data)
+  {
+    delete data;
+    data=NULL;
+  }
+}
+
+int Cmus::RemoveRow(int row)
+{
+  POSITION pos;
+  CString *tmppoi;
+
+  pos=data->FindIndex(row);
+  if(pos)
+  {
+    tmppoi=(CString *) data->GetAt(pos);
+    delete [] tmppoi;
+    data->RemoveAt(pos);
+    rows--;
+    return 1;
+  }
+  return 0;
+}
+
+void Cmus::AddRow(int row, CString arg1, CString arg2, CString arg3)
+{
+  POSITION pos;
+  CString *add;
+
+  add=new CString[MUSCOLUMN]; //won't check
+  add[0]=arg1;
+  add[1]=arg2;
+  add[2]=arg3;
+  pos=data->FindIndex(row);
+  if(pos)
+  {
+    data->InsertBefore(pos,add);
+  }
+  else
+  {
+    data->AddTail(add);
+  }
+  rows++;
+}
+
+int Cmus::WriteMusToFile(int fhandle)
+{
+  POSITION pos;
+  CString *tmppoi;
+  FILE *fpoi;
+  CString tmpstr;
+  CString format;
+  int i;
+
+  fpoi=fdopen(fhandle,"wb");
+  if(!fpoi)
+  {
+    close(fhandle);
+    return -1;
+  }
+  tmpstr.Format("%s\r\n%d\r\n",internal,data->GetCount());
+  WriteString(fpoi,tmpstr);
+  
+  pos=data->GetHeadPosition();
+  for(i=0;i<rows;i++)
+  {
+    tmppoi=(CString *) data->GetNext(pos);
+    if(tmppoi[2].IsEmpty())
+    {
+      tmpstr.Format("%-9s%s\r\n",tmppoi[0], tmppoi[1]); 
+    }
+    else
+    {
+      tmpstr.Format("%-9s %-9s @TAG %s\r\n",tmppoi[0], tmppoi[1], tmppoi[2]); 
+    }
+    WriteString(fpoi,tmpstr);
+  }
+  fclose(fpoi);
+  return 0;
+}
+
+int Cmus::ReadMusFromFile(int fhandle, int length)
+{
+  char tmpref[MAXIDSIZE];
+  CString ref;
+  CString *add;
+  FILE *fpoi;
+  int ret;
+  int res;
+  int pos1, pos2;
+
+  xorflag=position=0;
+  new_mus();
+  if(length<0) maxlength=filelength(fhandle);
+  else maxlength=length;
+  fpoi=fdopen(fhandle,"rb");
+  if(!fpoi)
+  {
+    close(fhandle);
+    return -1;
+  }
+  read_string(fpoi, "\n", tmpref,sizeof(tmpref)); //skipping crap
+  internal=tmpref;
+  internal.TrimLeft();
+  internal.TrimRight();
+  internal.MakeUpper();
+  read_string(fpoi, "\n", tmpref,sizeof(tmpref)); //skipping crap
+  rows=strtonum(tmpref);
+  ret=0;
+  do
+  {
+    tmpref[0]=0;
+    res=read_string(fpoi, "\n",tmpref,sizeof(tmpref));
+    tmpref[MAXIDSIZE-1]=0;
+    ref=tmpref;
+    if(!ref.GetLength()) continue; //skipping empty rows
+    ref.MakeUpper();
+    ref.TrimLeft();
+    ref.TrimRight();
+    add=new CString[MUSCOLUMN]; //won't check
+    pos1=ref.FindOneOf(" \t");
+    if(pos1<1)
+    {
+      add[0]=ref;
+      add[1]="";
+      add[2]="";
+    }
+    else
+    {
+      add[0]=ref.Left(pos1);
+      pos2=ref.Find("@TAG",pos1);
+      if(pos2<1)
+      {
+        add[1]=ref.Mid(pos1);
+        add[1].TrimLeft();
+        add[1].TrimRight();
+        add[2]="";
+      }
+      else
+      {
+        add[1]=ref.Mid(pos1,pos2-pos1);
+        add[1].TrimLeft();
+        add[1].TrimRight();
+        add[2]=ref.Mid(pos2+4);
+        add[2].TrimLeft();
+        add[2].TrimRight();
+      }
+    }
+    data->AddTail(add);
+    if(!--rows) break;
+  }
+  while(res==1 );
+  fclose(fpoi);
+  rows=data->GetCount();
+  return ret;
+}