/*
 *      Interactive disassembler (IDA).
 *      Version 3.05
 *  Copyright (c) 1990-96 by Ilfak Guilfanov.
 *      ALL RIGHTS RESERVED.
 *                              FIDO:   2:5020/209
 *                              E-mail: ig@estar.msk.su
 *
 */

#include "java.hpp"
#include "classfil.hpp"

//----------------------------------------------------------------------
extern ConstOpis curConst;
extern FILE *myFile;

extern ushort maxpos, curpos;
extern char *bufbeg;
extern int bufsize;

//----------------------------------------------------------------------
static int prompt(void)
{
  register ushort i;

  if(qfputc('\n', myFile) == EOF || ferror(myFile)) return(0);
  for(i = 0; i <= curpos; i++) qfputc(' ', myFile);
  return(1);
}

//----------------------------------------------------------------------
#ifndef REMOVE_UNICODE
static char *dmp_unichar(register char *p, char *pe)
{
  qfputc('[', myFile);
  for( ; p <= pe; p += 2) qfputc(((short)*p >= 0x20 ||
                                 strchr(AsciiStringChars,(uchar)*p)) ?
                                                    (uchar)*p : ' ', myFile);
  qfputc(']', myFile);
  return(p);
}
//----------------------------------------------------------------------
static int dmpstr(register char *p, ushort size)
{
  static char dmp_full[] = " %02X/%02X",
              dmp_page[] = "%02X ";
  register ushort i, j;
  char            *ps;
  ushort          cntpos, strcnt = 0;

  if((short)curConst._Spage < 0) {
    if((cntpos = (maxpos - curpos) / 5) == 0) ++cntpos;
  } else {
    if((cntpos = (maxpos - curpos - 2) / 4) == 0) ++cntpos;
    ps = ++p;
  }
  for(i = 0, j = cntpos; i < size; i++, p += 2) {
    if((short)curConst._Spage < 0)
         qfprintf(myFile, dmp_full, (uchar)*(p+1), (uchar)*p);
    else qfprintf(myFile, dmp_page, (uchar)*p);
    if(--j) continue;
    if((short)curConst._Spage >= 0) ps = dmp_unichar(ps, p);
    if(!prompt()) return(0);
    j = cntpos;
    ++strcnt;
  }

  if(j != cntpos) {
    if((short)curConst._Spage >= 0) {
      j *= 3;
      while(j--) qfputc(' ', myFile);
      dmp_unichar(ps, p - 2);
    }
    if(qfputc('\n', myFile) == EOF || ferror(myFile)) return(0);
    ++strcnt;
  }

  return(strcnt);
}
//----------------------------------------------------------------------
static int unicod(ushort index)
{
  static char page[] = "page-%03u ",
              empty[]= "EMPTY\n";
  register int cnt, sdwig = 0;

  if((short)curConst._Spage >= 0) {
    qfprintf(myFile, page, curConst._Spage);
    curpos += (sdwig = 9);
  }

  cnt = dmpString(index, dmpstr);
  curpos -= sdwig;

  if(cnt == -1) return(0);  //EOF
  if(!cnt) {
    qfprintf(myFile, empty);
    ++cnt;
  }
  return(cnt);
}
#endif

//----------------------------------------------------------------------
static ushort wrtutf(void)
{
  *get_output_ptr() = '\0';
  qfprintf(myFile, "%s", bufbeg);
  init_output_buffer(bufbeg, bufsize);
  if(!prompt()) return(0);
  return(maxpos - 1);
}

//----------------------------------------------------------------------
static int utfstr(ushort index)
{
  register int i;

  init_output_buffer(bufbeg, bufsize);
  i = fmtString(index, maxpos -= curpos, fmt_string, wrtutf);
  maxpos += curpos;
  if(i < 0) return(0);

  qfprintf(myFile, "%s\n", bufbeg);
  return(++i);
}

//----------------------------------------------------------------------
static int outnum(ushort type)
{
  static char val[] = "value = %s\n";
  char str[40];
  op_t x;
  x.value = curConst.value;
  x.addr = curConst.value2;
#ifdef __EA64__
  x.value = make_ulonglong(x.value, x.addr);
#endif
  x.type = o_imm;
  x.dtyp = type - 3;
  x.offb = 0;
  init_output_buffer(str, sizeof(str));
  OutValue(x, OOF_NUMBER | OOF_SIGNED | OOFW_IMM);
  term_output_buffer();
  qfprintf(myFile, val, str);
  return(1);
}

//----------------------------------------------------------------------
static char ind_fmt[] = "%s=%-5u";
//----------------------------------------------------------------------
static int outref(ushort count)
{
  static char cls[] = "Class",
              ref[] = " ref",
              typ[] = " Signature",
              nam[] = " Name",
              ind[] = "Index";

  switch(count) {
    case 3:
      qfprintf(myFile, ind_fmt, cls, curConst._class);
      qfprintf(myFile, ind_fmt, ref, curConst._name);
      qfprintf(myFile, ind_fmt, typ, curConst._sign);
      qfprintf(myFile, ind_fmt, nam, curConst._subnam);
      break;
   case 2:
      qfprintf(myFile, ind_fmt, &typ[1], curConst._name);
      qfprintf(myFile, ind_fmt, nam, curConst._class);
      break;
   case 1:
      qfprintf(myFile, ind_fmt, ind, curConst._name);
      break;
  }
  qfprintf(myFile, "\n");
  return(1);
}

//----------------------------------------------------------------------
static char rfmt[] = "                %5u=> ";
static int refput(ushort index)
{
  register ConstOpis *op = LoadOpis(index, CONSTANT_Utf8);

  if(!op) return(0);
  qfprintf(myFile, rfmt, index);
  return(utfstr(index));
}

//----------------------------------------------------------------------
typedef struct {
          ushort mask;
          ushort skip;
          char name[8];
          int (*proc)(ushort index);
          ushort arg;
        }_DMP_;


long gen_map_file(FILE *fp)
{
  static const char frm[] = "Map format\n\n"
                      "<~W~idth:D:3:::>  (valid 72-254)\n"
                      " Printing\n"
                      "<~A~ll:R>       Included constant types\n"
                      "<~U~nused  :R>>    <Utf~8~:C>\n"
                      " Sorting by<~C~lass:C>\n"
                      "<~T~ype:R><~R~eferences:C>\n"
                      "<U~n~sorted:R>><~N~ameAndType :C>\n"
                      " Number in pool<Num~b~ers:C>\n"
                      "<~D~ecimal :R><~S~tring:C>"
#ifdef REMOVE_UNICODE
                                                     ">"
#endif
                                                         "\n"
                      "<~H~ex:R>>"
#ifndef REMOVE_UNICODE
                                     "<Unicod~e~:C>>"
#endif
                                                     "\n\n";

  static const _DMP_ defr[MAX_CONSTANT_TYPE] = {
                     {0x01, 0, "Utf8   ", utfstr, 0},
#ifndef REMOVE_UNICODE
                     {0x40, 0, "Unicode", unicod, 0},
#endif
                     {0x10, 4, "Integer", outnum, 3 + dt_dword},
                     {0x10, 4, "Float  ", outnum, 3 + dt_float},
                     {0x10, 8, "Long   ", outnum, 3 + dt_qword},
                     {0x10, 8, "Double ", outnum, 3 + dt_double},
                     {0x02, 2, "Class  ", outref, 1},
                     {0x20, 2, "String ", outref, 1},
                     {0x04, 4, "Fld_ref", outref, 3},
                     {0x04, 4, "Met_ref", outref, 3},
                     {0x04, 4, "Int_ref", outref, 3},
                     {0x08, 4, "nam&typ", outref, 2}
                   };

  char   str[MAXSTR];
  uchar  tflag;
  sval_t width;
  long   pos, numstr = 0;
  ushort curbit = 1;
#ifndef REMOVE_UNICODE
#define DEF_TYPE_MASK  0x7F
#else
#define DEF_TYPE_MASK  0x3F
#endif
  short  unusflg = 1, unsortflg = 1, hexnumflg = 0, typemask = DEF_TYPE_MASK;
  register ConstOpis *opis;
  register ushort i, j;
  static char lft_fmt[] = "%08lX %5u%c %s ",
              ctyp_fmt[]= "\n-----CONSTANT-%s-----\n",
              head_fmt[]= "%s\n\n"
                          "   Constant Pool for \"%s\"\n\n"
                          " offset  #(%s)\n",
              head_fmh[]= "This file generated by IDA",
              head_hex[]= "hex",
              head_dec[]= "dec",
              foot_fmt[]= "\nEnd of map\n",
              numv[]= "(numeric values)",
              rft[] = "(program references)";

  uFlag = decflag();  // Decimal OutValue
  width = 80;
  if(!AskUsingForm_c(frm, &width, &unusflg, &unsortflg,
#ifndef REMOVE_UNICODE
                                 &hexnumflg, &typemask
#else
                                 &typemask, &hexnumflg
#endif
                                                      )) return(0);
  if(width < 72) width = 72;
  if(width > 254) width = 254;
  if(!typemask) typemask = DEF_TYPE_MASK;
#undef DEF_TYPE_MASK

  if(hexnumflg) {
    lft_fmt[7] = ind_fmt[5] = rfmt[17] = '4';
    lft_fmt[8] = ind_fmt[6] = rfmt[18] = 'X';
    curpos = 8 + 1 + 4 + 1 + 1 + 0 + 1;
  } else {
    lft_fmt[7] = ind_fmt[5] = rfmt[17] = '5';
    lft_fmt[8] = ind_fmt[6] = rfmt[18] = 'u';
    curpos = 8 + 1 + 5 + 1 + 1 + 0 + 1;
  }
  if(unsortflg) {
    curpos += 7;
    curbit = typemask;
  }
  maxpos = (ushort)width;
  bufbeg = str;
  myFile = fp;

  do {
    while(!(typemask & curbit)) curbit <<= 1;

    for(tflag = unsortflg, pos = 10, i = 1; i <= curClass.maxCPindex; i++) {
      if((opis = LoadOpis(i)) == 0) destroyed();
      j = opis->type - 1;
deb_error((j >= MAX_CONSTANT_TYPE), "map:type");
      if((!unusflg || !(opis->flag & _REF)) && (curbit & defr[j].mask)) {
        curConst = *opis;
        if(!numstr) {
          for(int k = (maxpos - sizeof(head_fmh)) / 2; k; k--) qfputc(' ', fp);
          qfprintf(fp, head_fmt, head_fmh, DecompName(),
                  hexnumflg ? head_hex : head_dec);
          numstr =  5;
        }
        if(!tflag) {
          qfprintf(fp, ctyp_fmt, (defr[j].arg < 3) ? defr[j].name :
                                         ((defr[j].arg == 3) ? rft : numv));
          ++tflag;
          ++numstr;
        }
        qfprintf(fp, lft_fmt, pos, i, (curConst.flag & _REF) ? ' ' : '*',
                       (unsortflg || defr[j].arg >= 3) ? defr[j].name : "");
        numstr += defr[j].proc(defr[j].arg ? defr[j].arg : i);
        if(unusflg && unsortflg && curConst.type >= CONSTANT_Class) {
          numstr += refput(curConst._name);
          if(curConst.type > CONSTANT_String) {
            if(curConst.type == CONSTANT_NameAndType)
              numstr += refput(curConst._class);
            else {
              numstr += refput(curConst._subnam);
              numstr += refput(curConst._sign);
            }
          }
        }
        if(feof(fp) || ferror(fp)) return(EOF);
      }

      ++pos;
      switch(j = defr[j].skip) {
        case 0:
          pos += curConst._Ssize + 2;
          break;
        case 8:
deb_error((i == curClass.maxCPindex), "map:CPend");
          ++i;
        default:
          pos += j;
          break;
      }
    }
  } while((typemask ^= curbit) != 0);

  if(numstr) {
    qfprintf(fp, foot_fmt);
    if(feof(fp) || ferror(fp)) return(EOF);
    ++numstr;
  }
  return(numstr);
}

//----------------------------------------------------------------------
uchar loadDialog(bool manual)
{
  static const char fmt[] =
"HELP\n"
"Java-VM class file loading options Ü\n"
" ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß\n"
"\n"
"Include local variable declarations (if possible)\n"
"\n"
"  This option allows to retain information about local variables\n"
"  (with their scopes) if the input file has this information.\n"
"\n"
"\n"
"Don't create 'import' segment with external-references\n"
"\n"
"  Prohibits the creation of a segment with external names, classes, and\n"
"  methods.\n"
"\n"
"\n"
"Create 'import' segment with ordinary references\n"
"\n"
"  Only regular references to the import segment will be collected.\n"
"  If this option is off, then all references (including simple text\n"
"  references) will be collected.\n"
"\n"
"\n"
"Field/variable declarations are included to references\n"
"\n"
"  Cross references from field declarations are collected in the import\n"
"  segment. For example, a field declaration\n"
"\n"
"    Field borland.ui.AboutDialog about\n"
"\n"
"  creates a reference to class borland.ui.AboutDialog\n"
"\n"
"\n"
"Method declarations are included to references\n"
"\n"
"  Cross references from the arguments of method declarations are collected\n"
"  in the import segment.\n"
"  NOTE: The import segment does not contain classes that appear only in\n"
"        the arguments of method declarations.\n"
"\n"
"Create visible representation for stripped names\n"
"\n"
"  Some java classes compiled by Symantec jit-compiler have their local\n"
"  names stripped. This option allows IDA to recreate such local names.\n"
"  NOTE: If this option is selected than all one-character names with\n"
"        character code >= 0x80 will be treated as stripped names.\n"
"\n"
"ENDHELP\n"
"Java loading options\n"
"\n"
"                  Class File version %D.%D\n"
"\n"
"\n"
"<Include ~l~ocal variable declarations (if possible)      :C>>\n"
"\n"
"\n"
"<~D~on't create 'import' segment with external-references :R>\n"
"<~C~reate 'import' segment with ordinary references       :R>\n"
"<~F~ield/variable declarations are included to references :R>\n"
"<~M~ethod declarations are included to references         :R>>\n"
"\n"
"<Create visible representation for ~s~tripped names       :C>>\n"
"\n"
"\n";

  short lvar = 1, reftyp = 1, symantCrk = 0;
  uval_t maxv = CURRENT_MAJOR_VERSION,
         minv = curClass.MinVers;
  uchar ans  = 0x80;

  if(minv & 0x8000) {
    minv &= ~0x8000;
    ++maxv;
  }
  if(manual &&
     !AskUsingForm_c(fmt, &maxv, &minv, &lvar, &reftyp, &symantCrk)) qexit(1);
  if(lvar)      ans |= 0x40;
  if(symantCrk) ans |= 0x20;
  return((uchar)(ans | ((1 << reftyp) - 1)));
}

//----------------------------------------------------------------------
