#include <api/api_main.h>
#include <extension.h>
#include <vm.h>

extern struct DISASM_TGT target;
extern struct DISASM_ENV disasm_env;
extern struct DISASM_PREFS disasm_prefs;

/* API sections:
 *     5. Addresses            :: Creating/manipulating ADDRESS objects
 * /


/* ---------------------------------------------------------------Addresses */
int IsValidAddr( long rva ){
   struct section s;
   int cont;

   /* foreach section: if code, the see if addr is inside it */
   cont = DBTableFirst(SECTION, &s);
   while (cont) {
      if ( rva >= s.rva && rva < s.rva + s.size)
         return(1);
      cont = DBTableNext(SECTION, &s);
   }
   return(0);
}

int IsValidCodeAddr( long rva ){
   struct section s;
   int cont;

   /* foreach section: if code, the see if addr is inside it */
   cont = DBTableFirst(SECTION, &s);
   while (cont) {
      if (s.flags == SECTION_CODE) {
         if ( rva >= s.rva && rva < s.rva + s.size)
            return(1);
      }
      cont = DBTableNext(SECTION, &s);
   }
   return(0);
}

long FindClosestAddr(long rva) {
/* Finds the closest existing address object to 'rva'
 * Note that this only searches backwards from rva; the
 * intent is to find the address object containing rva.
 *
 * A side effect of 'keyfind' is that a subsequent call to
 * 'keynext' returns the next sorted record in the index -- 
 * so doing a 'keynext' followed by a 'keyprev' sets the current
 * record to the ADDRESS object before the rva being searhced for.
 */
   struct address address;

   if (d_keyfind(ADDRESS_RVA, &rva) != S_NOTFOUND) 
      return(0);
   if (d_keynext(ADDRESS_RVA) != S_OKAY)
      return(0);      
   if (d_keyprev(ADDRESS_RVA) != S_OKAY)      
      return(0);      
   d_recread(&address);
   if ( address.rva + address.size >= rva )
   /* ADDRESS object exists containing rva */
      return(address.rva);
/* else no ADDRESS object containing rva; go ahead and make one */
   return(0);   
}

/* These 'new' functions look more reliable but don't work :( */
int MergeAddress(struct address *a, struct address *a2) {
/* 'a' is existing address [start of merge], 'a2' addr to be created */
   struct address address;
   int size = a2->size; /* bytes we need to absorb */
   int cont, diff;
   
   //UndefineCode(a->rva); /* a is getting whacked, no matter what */
   cont = DBIndexFind(ADDRESS_RVA, &a->rva, &address);

   if ( a2->rva >  a->rva) {
      /* a is to be resized to make room for a2 */
      diff = (a->rva + a->size) - a2->rva;
      size -= diff;
      address.size -= diff;
      d_recwrite(&address);
   } else {
      /* a is to be deleted and absorbed by a2 */
      size -= ((a->rva - a2->rva) + a->size);
      d_delete();
   }
      
   while (cont && size){
      /* remove all addresses that will be inside of new, bigger address */
      cont = DBIndexNext(ADDRESS_RVA, &address);
      if (address.size > size) {
         /* ohcrap this addr is larger than the remaining bytes to absorb */
         /* if a code object is associated with the old addr, delete it */
         //UndefineCode(address.rva);
         /* make address smaller */   
         address.size -= size;
         address.rva  += size;
         address.pa   += size;
         d_recwrite(&address);

         cont = 0;
      } else {
         /* this address object is <= the remaining bytes to absorb */
         size -= address.size;
         d_delete();
      }   
   }
   d_fillnew(ADDRESS, a);
   if (db_status != S_OKAY) {
      return(SetLastError(dbError()) );
   }
   return(1);
}

int SplitAddress(struct address *a, struct address *a2) {
/* note: a is the existing addr; a2 is the to-be-created addr */
   struct address address;
   
   /* modify existing addr so it is now address *after* rva */
   //UndefineCode(a->rva);
   d_keyfind(ADDRESS_RVA, &a->rva);
   if (a2->rva >= a->rva) {
      address.rva += a->size;
      address.pa += a->size;
   }
   address.size -= a->size;
   d_recwrite(&address);
   
   /* create address object for rva */
   d_fillnew(ADDRESS, a2);
   if (db_status != S_OKAY) {
      return(SetLastError(dbError()) );
   }
   return(1);
}

int DefineAddr(long rva, int size, long pa, int flags){
/* If ADDRESS object 'rva' exists, update its flags and
 * size, resize if necessary. Otherwise, create ADDRESS object
 * for rva, resizing other ADDRESS objects as needed.
 */
   struct address *a, addr_old, address;
   struct section sec;
   long temp, end_addr;
   void *state;
   int maxaddr, done = 0;
   
   /* ENTER -- save DB state and such */
   if (!rva ) return(SetLastError(4021));
   if (! size ) {
      /* size = NextAddr - curr Addr */
      if (DBFindClosestNext(ADDRESS_RVA, &rva, &address)) {
         if ( address.rva != rva ) {
            size = address.rva - rva;
         } else size = address.size; /* shouldn't happen */
      } else size = 1; /* must eb the last addr in prog? */
   }
   state = DBSaveState();
   disasm_env.flags |= DB_MOD;

   /* fix variables that are blank */      
   if (! flags) flags = ADDR_DATA;
   if (! pa || pa > target.size){
      /* this is somewhat redundant with GetPA -- need a common func  */
      if (GetSection(rva, &sec) && (a = GetAddressObject(sec.rva)) ){
         pa = a->pa + (rva - sec.rva);
         free(a);
      } else return(0); /* we will NOT have empty PA's! */   
   }
   
   /* fill address structure */
   address.rva = rva;
   address.pa = pa;
   address.flags = flags;
   address.size = size;
   
   
   if (d_keyfind(ADDRESS_RVA, &rva) == S_OKAY) {
   /* Possibility 1 : Address already exists */
      d_recread(&addr_old);
      if (addr_old.size == size){
      /* Possibility 1a : Address exists and is the same size */
         done = 1;
      } else if (addr_old.size < size) {
      /* Possibility 1b : Address exists and is smaller */
         done = MergeAddress( &addr_old, &address );
         //done = MergeOld( &address );
      } else {
      /* Possibility 1c : Address exists and is larger */
         done = SplitAddress( &addr_old, &address );
      }
      
   } else if (db_status == S_NOTFOUND) {
   /* Possibility 2 : RVA contained in previous address  */
      if ( DBFindClosestPrev(ADDRESS_RVA, &rva, &addr_old) &&
            GetSection(rva, &sec) && addr_old.rva >= sec.rva &&
            address.rva < (addr_old.rva + addr_old.size)) {
         if ( addr_old.rva + size > address.rva + address.size) {
         /* 2a : Previous address contains entire ADDRESS: split it */ 
            done = SplitAddress(&addr_old, &address);
         /* 2b : ADDRESS spans prev & next addrs; merge them */
         } else done = MergeAddress( &addr_old, &address );
         
   /* Possibility 3 : RVA extends into subsequent address  */
      } else if ( DBFindClosestNext(ADDRESS_RVA, &rva, &addr_old) &&
            (rva + size) > addr_old.rva ) {
         if ( addr_old.rva + size > address.rva + address.size) {
         /* 3a : Next address contains entire 'missing' addr: split it */
            done = SplitAddress(&addr_old, &address);
         /* 3b : ADDRESS spans multiple next addrs; merge them */
         } else { 
            done = MergeAddress( &addr_old, &address );
         }

   /* Possibility 4: RVA touches no existing addresses */
      } else {
         /* OK then, just fill it */
         d_fillnew(ADDRESS, &address);
         if (db_status == S_OKAY) done=1;
      }
   } else return(SetLastError(dbError()) );
   
   
   /* LEAVE -- Restore DB state and all that */
   if (state) DBRestoreState(state);
   return(done);
}


int UndefineAddr(long rva) {
/* if keyfind(addr) remove from DB.
 * else if in other addr get rva but do not delete
 * TODO: if code, if name, if string, etc ... delete
 */
if ( rva == 0x80489AC ) printf("undef 80489AC\n");
   if ( !DBRecordDelete(ADDRESS_RVA, &rva) )
      return(0);
   UndefineCode(rva);
   disasm_env.flags |= DB_MOD;
   return(1);
}

int AddrExists(long rva){
   if ( d_keyfind(ADDRESS_RVA, &rva) == S_OKAY) 
      return(TRUE);
   return(FALSE);
}   

int MakeCode(long rva){
   struct address *a;
   struct code c;
   int retval = 0;

   if( a = GetAddressObject( rva ) ) {
      c.rva = rva;
      retval = DisAddr(a->pa, &c, c.rva);
      free(a);
   }
   return(retval);
}

int MakeData(long rva){
   struct address *a;
// if already_code 
//       remove code object, etc
//      defineaddr with new flags
   UndefineCode(rva);
   if ( a = GetAddressObject( rva) ) {
      a->flags |= ADDR_DATA;
      a->flags ^= ADDR_CODE;
      d_recwrite(a);
      disasm_env.flags |= DB_MOD;
      free(a);
      return(-1);
   }
   return(0);
}

int GetFlags(long rva){
   struct address *a;
   int flags;

   if ( a= GetAddressObject(rva)){
      flags = a->flags;
      free(a);
      return(flags);
   }
   return(0);
}

int SetFlags(long rva, int flags){
   struct address *a;

   if ( a= GetAddressObject(rva)){
      a->flags = flags;
      d_recwrite(a);
      disasm_env.flags |= DB_MOD;
      free(a);
      return(1);
   }
   return(0);
}

/* ---------------------------------------------------------- sprintf's */
#define MAX_CMT_APPEND 64
int sprint_seg(char *buf, int seg) {
   seg = seg >> 16;
   snprintf(buf, 64, "%s%s:", ext_asm->reg_pre, 
            GetRegMnemonic(ext_arch->reg_seg + seg - 1));
   return(strlen(buf));
}


int sprint_op(char *buf, char *cmt, long op, int flg, 
               struct address *a, int count)
{
   char op_sign = '+', tmp[64];
   struct name n;
   struct string *str;
   struct constant c;
   int op_type = flg & OP_TYPE_MASK;
   int op_perm = flg & OP_PERM_MASK;
   int op_flag = flg & OP_MOD_MASK;
   int seg = flg & OP_SEG_MASK;
   int rv = 0; 
   
   /* switch OPERAND TYPE */
   switch (op_type) {

      case OP_PTR : 
      case OP_ADDR : 
      /* check for string pointer */
         if(str = GetStringObject(op)){
            snprintf(cmt, MAX_CMT_APPEND, "%s StrRef(%08X):\"%s\"", cmt, 
                            op, str->text) ;
            cmt[strlen(cmt) - 1] = '"';  /* just inn case */
            free(str);
         } else if (GetName( op, n.name ) ){
         /*    check if name */
            strncat(tmp, n.name, 64);
         } else if ( GetFuncName( op, n.name )) {
            /* this will handle imports too ! */
            strncat(tmp, n.name, 64);
         } else
            snprintf(tmp, 64, "%08X", op);
         break;   
      case OP_REG:
         //strncpy(tmp, GetRegMnemonic(op), 64);
         if (seg) rv = sprint_seg(tmp, seg);
         snprintf(tmp+rv, 64-rv, "%s%s", ext_asm->reg_pre, GetRegMnemonic(op));
         break;
      case OP_EXPR:
         if (seg) rv = sprint_seg(tmp, seg);
         else  rv = sprint_seg(tmp, OP_DATASEG);
         if (! GetAddrExprString(op, tmp + rv, 64 - rv))
            strcpy(tmp, "invalid addr_expr");
         break;
      case OP_REL :
         if ( op < 0 ) op_sign = '-';
         snprintf(tmp, 64, "%c%X", op_sign, op);
         break;
      case OP_OFF:
         /* add checking for names etc here */
         if (seg) rv = sprint_seg(tmp, seg);
         else  rv = sprint_seg(tmp, OP_DATASEG);
         snprintf(tmp+rv, 64-rv, "%08X", op);
         break;
      case OP_IMM :
         /*  TODO:  check if constant */
         snprintf(tmp, 64, "%s%X", ext_asm->imm_pre, op);
         break;
      default:
         snprintf(tmp, 64, "%X", op);
         break;
   }
   strncat(buf, tmp, (count ? count : 24));
   return(strlen(buf));
}

/* TODO Notes:
 *      - Get Comment Char from assembler 
 */
int asmsprintf( char *buf, char *format, struct address *addr){
/* This takes a printf-style format string and uses it to
 * format information about an address to a buffer. Format
 * specifiers are:
 *    %a - rva               %p - pa
 *    %m - mnemonic          %n - name
 *    %d - dest operand      %c - comment
 *    %s - source operand    %x - xrefs
 *    %t - third operand :)
 *    %b - raw bytes         %S - section
 *
 *    %, - cond. comma       %^ - cond. newline
 *    %; - cond. semicolon   %: - cond. colon
 * Notes:
 *      buf will be cleared in this function.
 *      'cond comma' will only be printed between two params
 *      'cond colon' and 'cond. newline' will only be printed 
 *       when text precedes them in the string.
 *      'cond. semicolon' will only be printed when text follows it.
 * These format specifiers are not meant to make sense; they are
 * meant for the convenience of disassembler scripts ;)
 *  TODO : for at&t syntax and such:
 * %l -- instr[/op] size letter %L -- inst/op size full [e.g. 'dword']
 */
   int i, j, m, count, prev_fmt = 0;
   char *d, *d2;
   char cmt_append[MAX_CMT_APPEND] = {0};
   char xref_types[5] = { 0, 'r', 'w', 0, 'x'};
   char delim[] = ", ", delim2[] = "; ", none[] = "\0";
   struct code *c;   struct xref x ={0};
   struct name n = {0};   struct comment cmt= {0};
   struct section *s;

   if (! buf || ! format || ! addr) 
      return(SetLastError(9010));
   c = (struct code *) calloc(sizeof(struct code), 1);
   if (!c) return(SetLastError(9050));
   DBIndexFind( CODE_RVA, &addr->rva, c);
   //if ( addr->flags & ADDR_CODE && ! DBIndexFind( CODE_RVA, &addr->rva, c))
   //   return(SetLastError(4022));

   buf[0] = 0; //just in case
   d = d2 = none;
   for (i = 0; i < strlen(format); i++){
      if (format[i] != '%' ) {
         sprintf(buf, "%s%c", buf, format[i]);
      } 
      else {
         i++;
         count = 0;
         while (format[i] >= '0' && format[i] <= '9'){
            count *= 10;
            count += (format[i] - '0');
            i++;
         }
         switch (format[i]){
            case 'a':
               strncat(buf, d, 2); 
               sprintf(buf, "%s%08X", buf, addr->rva);
               prev_fmt = 1;
               d = none;
               break;
            case 'b':
               strncat(buf, d, 2); 
               for ( j = 0; j < (count ? count : 4); j++){
                  strncat(buf, d2, 2); 
                  if ( j < addr->size )
                     sprintf(buf, "%s%02X ", buf, (unsigned char) 
                     target.image[addr->pa + j]);
                  else
                     strcat(buf, "   ");
               } 
               if (addr->size > j+1) {
                  /* if '+', add a comment stating the true size */
                  strcat(buf, "+");
                  snprintf(cmt_append, MAX_CMT_APPEND,"%s(Addr of %d bytes)",
                           cmt_append, addr->size);
               } else strcat(buf, " ");
               prev_fmt = 1;
               d = none;
               break;
            case 'm':
               if (c->mnemonic[0]) {
                  strncat(buf, d, 2); 
                  strncat(buf, c->mnemonic, (count ? count : 8));
                  prev_fmt = 1;
               }  else prev_fmt = 0;
               d = none;
               break;
            case 's':
               if (c->srcType){
                  strncat(buf, d, 2); 
                  sprint_op(buf, cmt_append, c->src, c->srcType,addr,count);
                  prev_fmt = 1;
               }  else prev_fmt = 0;
               d = none;
               break;
            case 'd':
               if (c->destType){
                  strncat(buf, d, 2); 
                  sprint_op(buf, cmt_append,c->dest,c->destType,addr,count);
                  prev_fmt = 1;
               }  else prev_fmt = 0;
               d = none;
               break;
            case 't':
               if (c->auxType){
                  strncat(buf, d, 2); 
                  sprint_op(buf, cmt_append, c->aux, c->auxType,addr,count);
                  prev_fmt = 1;
               }  else prev_fmt = 0;
               d = none;
               break;
            case 'p':
               strncat(buf, d, 2); 
               sprintf(buf, "%s%08X", buf, addr->pa);
               prev_fmt = 1;
               d = none;
               break;
            case 'n':
               strncat(buf, d, 2); 
               if (GetName( addr->rva, n.name)){
                  strncat(buf, d2, 2); 
                  strncat(buf, n.name, (count ? count : 32));
                  prev_fmt = 1;
                    d2 = none;
               }  else prev_fmt = 0;
               d = none;
               break;
            case 'c':
               strncat(buf, d, 2); 
               if ( addr->comment && 
                     DBIndexFind(COMMENT_ID, &addr->comment, &cmt) ){
                  strncat(buf, d2, 2); 
                  strcat(buf, ext_asm->comment);
                  strncat(buf, cmt.text, (count ? count : 64));
                  if (cmt_append[0]) 
                     strncat(buf, cmt_append, (count ? count : 64)); 
                     //strncat(buf, cmt_append, (count ? count : 64) - 
                     //                          strlen(cmt.text) ); 
                  prev_fmt = 1;
                  d2 = none;
               } else if (cmt_append[0]){
                  strcat(buf, ext_asm->comment);
                  strncat(buf, cmt_append, (count ? count : 64)); 
                  prev_fmt = 1;
                  d2 = none;
               } else prev_fmt = 0;
               d = none;
               break;
            case 'x':
               m = count ? count : 4;
               strncat(buf, d, 2); 
               if (DBIndexFind(FROM_RVA, &addr->rva, &x)){
                  strncat(buf, d2, 2); 
                  strcat(buf, " xrefs:");
                  while( m > 0) {
                     sprintf(buf, "%s >%08X[%c]", buf, 
                                     x.to_rva, xref_types[x.type]);
                     if  (DBIndexNext(FROM_RVA, &x)  &&  
                                     x.from_rva == addr->rva) 
                        m--;
                     else m = 0;
                  }
               }
               if (DBIndexFind(TO_RVA, &addr->rva, &x)){
                  if (m) {
                     strncat(buf, d2, 2); 
                     strcat(buf, " xrefs:");
                  }
                  m = count ? count : 4;
                  while( m > 0) {
                     sprintf(buf, "%s <%08X[%c]", buf, 
                                     x.from_rva, xref_types[x.type]);
                     if  (DBIndexNext(TO_RVA, &x)  &&  
                                     x.to_rva == addr->rva) 
                        m--;
                     else m = 0;
                  }
               }
               if (!m) {
                  d2 = none;
                  prev_fmt = 1;
               } else  prev_fmt = 0;
               d = none;
               break;
            case 'S':
            //if ( s = GetSectionObject( addr->rva ) ){
            //   strncat(buf, d2, 2); 
            //   strncat(buf, s->name, (count ? count : 16));
            //   free(s);
            //}
               break;
            case ',':
            /* insert a comma only if a field follows it */
               d = delim;
               break;
            case ';':
            /* insert a comment_char only if a field follows it */
               d2 = delim2;
               break;
            case ':':
            /* insert a colon only when text precedes it */
               if (prev_fmt){
                  strncat(buf, ":", 1);
                  prev_fmt = 1;
               } else prev_fmt = 0;
               break;
            case '^':
            /* insert a newline only when text precedes it */
               if (prev_fmt){
                  strncat(buf, "\n", 1);
                  prev_fmt = 1;
               } else prev_fmt = 0;
               break;
            default:
            /* not a format metachar */
               strncat(buf, &format[i], 1);
               break;
         }
      }
   }
   free(c);
   return(1);
}

/* -------------------------------------------------------------------- */
/* this is intended as a generic printline function -- given an address, it
 * will print using the default code or data format, and will show function
 * names, comments, etc if the user has requested it. In light of asmsprintf,
 * this is probably pretty useless */
int PrintAddress(long rva){
   char *line;
   void *state;

   if (! (line = calloc(2048,1)) ) return(SetLastError(9050));

   if (! ext_sprint_code(rva, line, 2048, disasm_env.output)){
         printf("!! error in address %x: %s", rva, GetLastErrorString() );
         free(line);
         return(0);
   } 
   printf("%s\n", line);
   free(line);

   return(1);
}

long GetPA(long rva){
   struct address *a;
   struct section s;
   void *state;
   int retval = 0;

   state = DBSaveState();
   if ( a = GetAddressObject( rva ) ){
      retval = a->pa;
      free(a);
   } else if (GetSection(rva, &s)){
   /* no address object exists : try to guess the pa */
      /* use PA of section containing this addr as a base */
      if (a = GetAddressObject(s.rva)) {
         retval = a->pa;
         /* add the difference of section start and  rva */
         retval += (rva - a->rva); 
         free(a);
      }
   } 
   if (! retval)   {
      /* make one last attempt */
      a = GetAddressObject( FindClosestAddr(rva));
      if (a) {
         retval = a->pa - (rva - a->rva);
         free(a);
      }
   }
   DBRestoreState(state);
   return(retval);
}

long GetRVA(long pa){
   struct address a;

   if ( d_keyfind(PA, &pa) != S_OKAY) 
      return(SetLastError(4030));
   d_recread(&a);
   return(a.rva);
}

int GetAddrType(long rva){
/* If addr exists, return type flag.
 * Else, get section containing address return its type 
 */
   struct address addr;
   struct section sec;
   char secname[32];

   if ( d_keyfind(ADDRESS_RVA, &rva) == S_OKAY) {
      d_recread(&addr); 
      return(addr.flags);
   } 
   else if (db_status == S_NOTFOUND) {
      if (GetSectionName(rva, secname)== -1) 
         return(-1);
      if (d_keyfind(SECTION_NAME, &secname) != S_OKAY) 
         return(-1);
      d_recread(&sec);
      //may be able to remove this:
      d_keyfind(ADDRESS_RVA, &rva);
      if (sec.flags & SECTION_CODE || sec.flags & SECTION_EXECUTE)
         return(ADDR_CODE);
      else 
         return(ADDR_DATA);
   } 
   else 
      return(SetLastError(dbError()));
}

int GetAddrSize(long rva){
   struct address *a;
   int retval = 0;

   if ( a = GetAddressObject(rva) ) {
      retval = a->size;
      free(a);
   }
   return(retval);
}

long GetNextAddr(long rva){
   struct address a;

   if ( d_keyfind(ADDRESS_RVA, &rva) == S_OKAY &&
   d_keynext(ADDRESS_RVA) == S_OKAY){
      d_recread(&a);
      return(a.rva);
   } 
   else 
      return(0);
}

long GetPrevAddr(long rva){
   struct address a;

   if ( d_keyfind(ADDRESS_RVA, &rva) == S_OKAY &&
   d_keyprev(ADDRESS_RVA) == S_OKAY){
      d_recread(&a);
      return(a.rva);
   } 
   else 
      return(0);
}

int GetAddrBytes(long rva, char *buf){
   struct address a;
   if ( d_keyfind(ADDRESS_RVA, &rva) != S_OKAY) 
      return(SetLastError(4020));
   d_recread(&a);
   memcpy(buf, target.image + a.pa, a.size);
   return(a.size);
}
int DefineAddrComment( long rva, char *buf){
   return(SetAddrComment(rva, DefineComment(buf, CMT_USER)) );
}

int GetAddrComment( long rva) {
   struct address a;

   if (d_keyfind(ADDRESS_RVA, &rva) == S_OKAY) {
      d_recread(&a);
      return(a.comment);
   }
   return(SetLastError(4020));
}

int GetAddrConstant( long rva) {
   struct address *a;
   int retval = -1;

   if ( a = GetAddressObject( rva )){
      retval = a->dataConst;
      free(a);
   }
   return(SetLastError(4020));
}

int GetAddrStruct( long rva) {
   struct address a;

   if (d_keyfind(ADDRESS_RVA, &rva) == S_OKAY) {
      d_recread(&a);
      return(a.structure);
   }
   return(SetLastError(4020));
}

int SetAddrComment( long rva, int id) {
   struct address a;

   if (! rva || ! id) 
      return(-1);
   if (d_keyfind(ADDRESS_RVA, &rva) == S_OKAY) {
      d_recread(&a);
      a.comment = id;
      d_recwrite(&a);
      return(1);
   }
   return(SetLastError(4020));
}

int SetAddrConstant( long rva, int id){
   /* if ADDRESS is a data object, the constant refers to
    * its value; if ADDRESS is a code object, the constant
    * refers to the operand with an OP_CONST type. */
   struct address a;

   if (! rva || ! id) 
      return(-1);
   if (d_keyfind(ADDRESS_RVA, &rva) == S_OKAY) {
      if (a.flags & ADDR_CODE) return(SetLastError(4023));
      d_recread(&a);
      a.dataConst = id; /* id of the constant */
      d_recwrite(&a);
      return(1);
   }
   return(SetLastError(4020));
}

int SetAddrStruct( long rva, int id){
   struct address a;

   if (! rva || ! id) 
      return(-1);
   if (d_keyfind(ADDRESS_RVA, &rva) == S_OKAY) {
      d_recread(&a);
      a.structure = id;
      d_recwrite(&a);
      return(1);
   }
   return(SetLastError(4020));
}



