/* api.c : part of The Bastard disassembly environment ========================G
 *
 * Contains  the bulk of the internal code -- specifically, the API routines
 * provided for internal and scripting use. Functions should be in the same
 * order as in api.h
 */
#include <api/api_main.h>

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

/* API sections:
 *    10. Structures            :: User-defined data structures
 *    13. Constants            :: User-defined constants/enums
 */


/* ---------------------------------------------------------------Data Types */

int DefineDataType(char *name, int size, int flags) {
   /* this is essentially a typedef */
   struct data_type d;
   
   /* 1. get id from sequence */
   d.id = ++seq.data_type;
   /* 2. add new data_type record */
   d.flags = flags;
   d.size = size;
   strncpy( d.name, name, 32);
   disasm_env.flags |= DB_MOD;
   if (DBRecordInsert(DATA_TYPE, &d))
      return(d.id);
   return(0);
}

int DeleteDataType(char *name){
   struct data_type d;

   disasm_env.flags |= DB_MOD;
   if (DBIndexFind(DATA_TYPE_NAME, name, &d))
      return(DBRecordDelete(DATA_TYPE, &d.id));
   return(0);
}

int CopyDataType(char *name, char *new_name);
int GetDataType(char *name) {
   struct data_type d;

   if ( DBIndexFind(DATA_TYPE_NAME, name, &d))
      return(d.id);
   return(0);
}
   

/* ---------------------------------------------------------------Structures */
int DefineStruct( char *name, short size ){
   struct structure s;
   
   s.id =  ++seq.structure;
   s.size = size;
   strncpy(s.name, name, 32);
   disasm_env.flags |= DB_MOD;
   if(DBRecordInsert(STRUCTURE, &s))
      return(s.id);
   return(0);
}

int DefineStructMember( int struct_id, long type, int size,int order, char *name ){
   /* type = id of data type     order = field # in structure */
   /* size = # of data types [e.g. >1 == array ] */
   struct struct_member s;
   
   s.id =  ++seq.struct_member;
   s.structure = struct_id;
   s.type = type;
   s.size = size;
   s.order = order;
   strncpy(s.name, name, 32);
   disasm_env.flags |= DB_MOD;
   if (DBRecordInsert(STRUCT_MEMBER, &s))
      return(s.id);
   return(-1);
}
int GetStruct( unsigned long id, struct structure *s) {
// fills structure 
   return(0);
}
unsigned long GetStructID(char *name){
   return(0);
}
int GetStructMember( int struct_id, int order, struct struct_member *m) {
// m.id and m.order must be filled before calling this
   return(0);
}

/* this is considered an "internal" function */
int apply_struct_member( long rva, struct structure *s, 
                         struct struct_member *m) {
   struct data_type d;
   char buf[64];
   int size;

   if (! DBIndexFind( DATA_TYPE_ID, &m->type, &d)) return(SetLastError(0));
   /* get total size of structure member, create ADDRESS for it */
   size = m->size * d.size;
   DefineAddr(rva, size, 0, ADDR_DATA); 
   /* If address has a name, get it; else autogenerate one */
   if (GetName(rva, buf)) {
     strcat(buf, ".");
   } else {
      sprintf(buf, "struct_%08X.", rva);
   }
   strncat(buf, m->name, 63 - strlen(buf));
   SetName(rva, buf, NAME_USER | NAME_STRUCT);

   return(size);
}

int ApplyStructure(long rva, unsigned long id) {
// This basically just sets addr.structre to s
   struct address a;
   struct structure *s;
   struct struct_member *m;
   int cont;

   /* Get Structure Object */
   if( !(s = GetStructureObject(id)) ) return(SetLastError(0));
   m = calloc(sizeof(struct struct_member), 1);

   /* Force ADDRESS to structure.size, get new ADDRESS object */
   DefineAddr(rva, s->size, 0, ADDR_DATA);
   DBIndexFind(ADDRESS_RVA, &id, &a); /* this gets PA :) */

   /* Set up address for this structure */
   a.flags |= ADDR_STRUCT;
   a.structure = id;
   DBRecordUpdate(ADDRESS_RVA, &rva, &a);

   /* for each structure member, create/name an ADDRESS object */
   cont = DBIndexFind(STRUCT_MEMBER_STRUCTURE, &id, m);
   /* TODO: find out why m is zeroed out here */
   while (cont && m->structure == id) {
      rva += apply_struct_member(rva, s, m);
      cont = DBIndexNext(STRUCT_MEMBER_STRUCTURE, m);
   }
   
   free(m);
   free(s);
   disasm_env.flags |= DB_MOD;
   
   return(1);
}
int DelStruct( unsigned long id){
   disasm_env.flags |= DB_MOD;
// s = structure ID 
   return(0);
}
int DelStructMember( unsigned long id){
   disasm_env.flags |= DB_MOD;
// s = struct_member id
   return(0);
}
/* ---------------------------------------------------------------Constants */
int DefineConstant( char *name, int value){
/* redefines existing */
   struct constant c;
   int exists = 0;

   if ( d_keyfind(CONSTANT_NAME, name) == S_OKAY) {
      d_recread(&c);
      exists = 1;
   } else {
      c.id = ++seq.constant;
      strncpy(c.name, name, 31);
      c.name[31] = 0;
   }
   c.value = value;
   if (exists) d_recwrite(&c);
   else (d_fillnew(CONSTANT, &c));

   if (db_status != S_OKAY) return(SetLastError(dbError()) );
   return(1);
}

int GetConstant(int value, char buf[32]){
   struct constant c;
   
   if ( d_keyfind(VALUE, &value) == S_OKAY) {
      d_recread(&c);
      strcpy(buf, c.name);
      return(c.id);
   }
   return(SetLastError(5120));
}

int GetConstantNext(char buf[32]){
   struct constant c;
   
   if ( d_keynext(VALUE) == S_OKAY) {
      d_recread(&c);
      strcpy(buf, c.name);
      return(c.id);
   }
   return(0);
}
   

int GetConstantName( unsigned long id, char buf[32]){
// get name into buf[32]
   struct constant c;
   
   if ( d_keyfind(CONSTANT_ID, &id) == S_OKAY) {
      d_recread(&c);
      strcpy(buf, c.name);
      return(strlen(c.name));
   } 
   return(SetLastError(5100));
}

unsigned long GetConstantID( char *name){
   /* constants are unique by name/id, but not by value */
   struct constant c;
   
   if ( d_keyfind(CONSTANT_NAME, name) == S_OKAY) {
      d_recread(&c);
      return(c.id);
   }
   return(SetLastError(5110));
}

int DelConstant( unsigned long id){
   if (! DBRecordDelete(CONSTANT_ID, &id) )
      return(SetLastError(5100)); 
   disasm_env.flags |= DB_MOD;
   return(1);
}
