
#include <bastard.h>
#include <stdarg.h>

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


/* ---------------------------------------------------------------Addresses */
   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);	
   }

   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 address;
      long temp, end_addr;
      disasm_env.flags |= DB_MOD;
      if (! flags) flags |= ADDR_DATA;
      if (! pa || pa > target.size) {
      /* compute from nearest addr */
         if ( d_keyfind(ADDRESS_RVA, &rva) == S_NOTFOUND) {
            if (d_keynext(ADDRESS_RVA) != S_OKAY)
               return(SetLastError(dbError()));		
            if (d_keyprev(ADDRESS_RVA) != S_OKAY)		
               return(SetLastError(dbError()));		
         } 
         else if (db_status != S_OKAY) 
            return(SetLastError(dbError()));
         d_recread(&address);
         pa = address.pa + (rva - address.rva);
      }
      if ( d_keyfind(ADDRESS_RVA, &rva) == S_OKAY) {
       /* if ADDRESS object for RVA already exists */
         d_recread(&address);
         if (address.size == size) {
         /* both are same size */
            address.flags = flags; /* fall thru to fillnew();*/
         } 
         else if ( address.size > size) {
         /* existing ADDRESS is larger */
            address.rva += size;
            address.size -= size;
            d_recwrite(&address);
            address.rva = rva;
            address.size = size;
            address.pa = pa;
            address.flags = flags; /* fall thru to fillnew();*/
         } 
         else {
         /* existing ADDRESS is smaller */
            address.rva = rva;
            address.size = size;
            address.pa = pa;
            address.flags = flags;
            d_recwrite(&address);
            if (FindClosestAddr( rva + size)) {
            /* Remove/modify intervening ADDRESS objects */
               d_recread(&address);
               address.size -= ((rva + size) - address.rva);
               address.pa += ((rva + size) - address.rva);
               address.rva += ((rva + size) - address.rva);
               d_recwrite(&address);
               d_recprev(ADDRESS);
               d_recread(&address);
               while ( address.rva > rva){
                  d_delete();
                  d_recprev(ADDRESS);
                  d_recread(&address);
               }
            }
            return(1);
         }
      } 
      else if (FindClosestAddr(rva)){
      /* Find ADDRESS object containing RVA */ 
         d_recread(&address);
         temp = address.rva;
         if ((address.rva + address.size) < (rva + size)){
            if (FindClosestAddr( rva + size)) {
            /* remove intervening ADDRESS objects */
               d_recread(&address); 
               if ((address.rva + address.size) > (end_addr)){
               /* Resize NEXT ADDR so it starts after CURRENT */
                  address.size -= (end_addr - address.rva);
                  address.pa += (end_addr - address.rva);
                  address.rva += (end_addr - address.rva);
                  d_recwrite(&address);
                  d_recprev(ADDRESS);
                  d_recread(&address);
               }
            /* delete all ADDRESS objects between PREV and NEXT */
               while ( address.rva > rva){
                  d_delete();
                  d_recprev(ADDRESS);
                  d_recread(&address);
               }
            }
            d_keyfind(ADDRESS_RVA, &temp);
            d_recread(&address); 
         }
         address.rva -= rva;
         address.pa -= pa;
         address.size -= size; /* check for overrun! */
         d_recwrite(&address);
         address.rva = rva;
         address.pa = pa;
         address.size = size;
         address.flags = flags;
      } 
      else {
      /* Create an entirely new ADDRESS object at RVA */
         address.rva = rva;
         address.pa = pa;
         address.size = size;
         address.flags = flags;
      }
      d_fillnew(ADDRESS, &address);
      if (db_status != S_OKAY) 
         return(SetLastError(dbError()));
      return(1);
   /* Note: The intended use of this routine, and of the disassembler in
    * general, is to create an ADDRESS object the size of the target 
    * program, then split it into a series of sequential ADDRESS objects
    * representing CODE and DATA contained in the program.
    *       It is possible, however, to simply create ADDRESS objects as
    * needed in order to conserve memory. The resulting database will be
    * non-sequential --kind of like IDA DBs-- and as a result the patch
    * routines of the disassembler will be unreliable [much like the ones
    * in IDA ;) ], since bytes in the original binary which do not have 
    * corresponding ADDRESS objects [e.g. .bss sections, file headers, etc]
    * will be lost ... and a re-linking of the code and data will be 
    * required in order to make a working binary. BFD could of course be 
    * used for this... */
   }

   int UndefineAddr(long addr) {
   /* if keyfind(addr) remove from DB.
    * else if in other addr get rva but do not delete
    * if code, if name, if string, etc ... delete
    */
      if ( d_keyfind(ADDRESS_RVA, &addr) == S_OKAY) d_delete();
      disasm_env.flags |= DB_MOD;
      return(0);
   }

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

   int MakeCode(long addr){
      struct address *a;
      struct code c;
      int retval = 0;
   
      if( a = GetAddressObject( addr ) ) {
         c.rva = addr;
         retval = DisAddr(a->pa, &c, c.rva);
         free(a);
      }
      return(retval);
   }

   int MakeData(long addr){
      struct address *a;
   // if already_code 
   //    	remove code object, etc
   //		defineaddr with new flags
      if (d_keyfind(CODE_RVA, &addr) == S_OKAY) d_delete();
      if ( a = GetAddressObject( addr) ) {
         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 addr){
      struct address *a;
      int flags;
   
      if ( a= GetAddressObject(addr)){
         flags = a->flags;
         free(a);
         return(flags);
      }
      return(0);
   }

   int SetFlags(long addr, int flags){
      struct address *a;
   
      if ( a= GetAddressObject(addr)){
         a->flags = flags;
         d_recwrite(a);
         disasm_env.flags |= DB_MOD;
         free(a);
         return(1);
      }
      return(0);
   }

   int datasprintf( char *buf, char *format, struct address *addr){
   	/*for ( x=0; x < addr.size; x++){
   		for ( y=0; y < 8; y++) 
   			sprintf(asc, "%s%c", asc, target.image + addr.pa + y);
   		sprintf( line, "");
   	}*/
      if (! addr || ! buf) 
         return(SetLastError(9010));
      sprintf(buf, "%08X\t Data region of size %08X", addr->rva, addr->size);
      return(strlen(buf));
   }

   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
    * 	%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 ;)
    */
      int i, j, count;
      char *d, *d2, delim[] = ", ", delim2[] = "; ", none[] = "\0";
      struct code *c;	struct xref x;
      struct name *n;	struct comment *cmt;
      struct section *s;
   
      if (! buf || ! format || ! addr) 
         return(SetLastError(9010));
      if ( ! ( c = GetCodeObject( addr->rva )) ) {
      /* this should really be handled by the caller; this is a failsafe */
         return(datasprintf(buf, format, addr));
      }
   
      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':
                  sprintf(buf, "%s%08X", buf, addr->rva);
                  break;
               case 'b':
                  for ( j = 0; j < (count ? count : 4); j++){
                     strncat(buf, d2, 2); 
                     if ( j >= addr->size )
                        sprintf(buf, "%s   ", buf);
                     else sprintf(buf, "%s%02X ", buf, (unsigned char) 
                           target.image[addr->pa + j]);
                  }
                  break;
               case 'm':
                  if (c->mnemonic[0])
                     strncat(buf, c->mnemonic, (count ? count : 8));
                  break;
               case 's':
						/* these should all call PrintOperand()
						 *  ... if OP_CONST && addr.const_id ) ...
						 *  ... if OP_ADDR && GetAddressName(operand)...
						 */
                  if (c->src[0]){
                     strncat(buf, d, 2); 
                     strncat(buf, c->src, (count ? count : 12));
                  }
                  d = none;
                  break;
               case 'd':
                  if (c->dest[0]){
                     strncat(buf, d, 2); 
                     strncat(buf, c->dest, (count ? count : 12));
                  }
                  d = none;
                  break;
               case 'p':
                  sprintf(buf, "%s%08X", buf, addr->pa);
                  break;
               case 'n':
                  if (n = GetNameObject( addr->rva )){
                     strncat(buf, d2, 2); 
                     strncat(buf, n->name, (count ? count : 32));
                     free(n);
                  }
                  break;
               case 'c':
               //if (d_keyfind(COMMENT_RVA, &addr->rva) == S_OKAY){
               //	d_recread(&cmt);
               //	strncat(buf, d2, 2); 
               //	strncat(buf, cmt.text, (count ? count : 64));
               //}
                  break;
               case 'x':
                  if (d_keyfind(FROM_RVA, &addr->rva) == S_OKAY){
                     d_recread(&x);
                     count ? count : 4;
                  //do this for max 'count' xrefs
                  //not impelemented :)
                     strncat(buf, d2, 2); 
                     sprintf(buf, "%s%08X ", buf, x.to_rva);
                  }
                  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 semicolon only if a field follows it */
                  d2 = delim2;
                  break;
               case ':':
               /* insert a colon only when text precedes it */
                  if (strlen(buf) > 0)
                     strncat(buf, ":", 1);
                  break;
               case '^':
               /* insert a newline only when text precedes it */
                  if (strlen(buf) > 0)
                     strncat(buf, "\n", 1);
                  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){
      struct address *addr;
      struct code inst;
      char *line;
      char hex[32], asc[12];
      int x, y;
   
      line = malloc(2048);
   /* the keyfind is wasteful in disasm functions, but needed for API */
      addr = GetAddressObject( rva);
      if ( ! addr  ) 
         return(SetLastError(9010));
   //if (addr->flags & ADDR_FUNCTION){ }
   //if (addr-?flags & ADDR_INLINE) { }
   
      if ( d_keyfind(CODE_RVA, &rva) == S_OKAY){
         asmsprintf(line, disasm_env.ttyFmt, addr);
      } 
      else {
         datasprintf(line, NULL, addr);
      }
      printf("%s\n", line);
      free(line);
      free(addr);
      return(1);
   }

   long GetPA(long rva){
      struct address *a;
      int retval = 0;
   
      if ( a = GetAddressObject( rva ) ){
         retval = a->pa;
         free(a);
      }
      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));
   }



