//#define __debug__
/*
 *      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 <loader.hpp>
#include <entry.hpp>
#include <diskio.hpp>
#include <srarea.hpp>
#include "classfil.hpp"
//----------------------------------------------------------------------
#define CURRENT_IDP_VERSION 19
//----------------------------------------------------------------------
ClassInfo  curClass;
SegInfo    curSeg;
FieldInfo  curField;

FILE       *myFile;
ConstOpis  curConst;
char       *addonce_name;
//-----------------------------------------------------------------------
void destroyed(void)
{
  static char dst[] = "Database is corrupted!";
  error(dst);
}
//-----------------------------------------------------------------------
void not_compat(void)
{
  static char ncmp[] = "Internal error (compatibility)";
  error(ncmp);
}
//-----------------------------------------------------------------------
static void int_error(void)
{
  static char ie[] = "Internal error (idp)";
  error(ie);
}
//-----------------------------------------------------------------------
static void modifyNameChars(int mode)
{
  static char noname_sym[] =  "./"            // this for "compress" only
                              "[]();,\\\"";   //\\ This correct after...

  register char *e = NameChars + sizeof(NameChars);
  if(mode >= 0) {
    register char *p = NameChars;

    while(*p) if((uchar)*p <= ' ' || strchr(noname_sym, *p)) qstrncpy(p, p+1, e-p);
              else                                           ++p;
  }
  addonce_name = NameChars + strlen(NameChars);
  qstrncpy(addonce_name, mode ? ".+" : ".()", e-addonce_name);
}

//-----------------------------------------------------------------------
static netnode ConstantNode;
static char    constant_pool[] = "$ Constant Pool ~";
static uchar   firstpass = 0;
static uchar   loadMode;
//----------------------------------------------------------------------
void myBase(const char *arg)
{
  char str[MAXSTR - 20];
  register ClassInfo *p;
  register int i;
  register char *ps;

  if(!arg) {
    if(ConstantNode.create(constant_pool) ||
       (p = (ClassInfo *)ConstantNode.supval(0)) == NULL) destroyed();
    if(ConstantNode.altval(-1) != CURRENT_IDP_VERSION) {
      static char olp[] = "Can't convert database format";
      error(olp);
    }
    curClass = *p;
deb_error((!curClass.ClassNode), "sv");
    modifyNameChars(1);
  } else {
    if(!ConstantNode) int_error();
    ps = str;
    if((i = strlen((char *)arg)) >= (sizeof(str) - 1)) {
      ps = (char *)arg + i - (sizeof(str) - 1 - 3);
      for(i = sizeof(str) - 1 - 3; i; i--) if(*--ps == '/') break;
      if(!i) {
        static char ilgc[] = "notify: illegal file name parameter";
        error(ilgc);
      }
      arg = ps;
      ps = qstpncpy(str, "...", sizeof(str));
      i += 3;
    }
    qstrncpy(ps, (char *)arg, sizeof(str)-(ps-str));
    ConstantNode.supset(-2, str, i);
  }

}
//----------------------------------------------------------------------
segment_t *getMySeg(ea_t ea)
{
  register segment_t *s;
  register SegInfo *p;

  if((s = getseg(ea)) == NULL) not_compat();

  if(curSeg.startEA != s->startEA)
    if(!s->orgbase) {
      if(s->type != SEG_IMP && s->type != SEG_XTRN) not_compat();
      curSeg.startEA = s->startEA;
    } else {
      if((p= (SegInfo *)netnode(curClass.ClassNode).supval(s->orgbase))
          == NULL)                                               destroyed();
      if(-s->orgbase != p->id.Number ||
         s->startEA != (s->type == SEG_DATA ? p->DataBase : p->startEA))
                                                                 not_compat();
      curSeg = *p;
    }

  return(s);
}
//-----------------------------------------------------------------------
char *DecompName(void)
{
  register char *p;

  if((p = ConstantNode.supval(-2)) == NULL) destroyed();
  return(p);
}
//-----------------------------------------------------------------------
inline void StoreOpis(ushort index, ConstOpis *opis)
{
  ConstantNode.supset(index, opis, sizeof(*opis));
}
//-----------------------------------------------------------------------
static ushort no_prmpt(void);
ConstOpis *LoadOpis(ushort index, uchar type)
{
  register ConstOpis *p;

  if(!index || index > curClass.maxCPindex ||
     (p = (ConstOpis *)ConstantNode.supval(index)) == NULL) return(NULL);
  if(!firstpass) return(p);

  if(!(p->flag & _REF)) {
    p->flag |= _REF;
    StoreOpis(index, p);
  }
  if(type & 0xE0) {
    static char illrf[] = "Illegal Reference Type to CONSTANT_Utf8#%u\n";
    if(p->type != CONSTANT_Utf8) return(NULL);
    switch(type) {
      default:
//      case 0x80:                                //FREE
        break;
      case 0x40:                                //CheckFieldSign
        if(p->flag & HAS_SIGNATURE) return(p);
        break;
      case 0xC0:                                //CheckCallSign
        if(p->flag & HAS_PARAMSIGN) return(p);
        break;
      case 0x20:                                //CheckAnySign
        if(p->flag & (HAS_SIGNATURE | HAS_PARAMSIGN)) return(p);
        break;
      case 0x60:                                //CheckFieldName
        if(p->flag & HAS_FM_NAME) return(p);
        break;
      case 0xA0:                                //CheckClass
        if(p->flag & (HAS_SIGNATURE | HAS_CLASS_NAME)) return(p);
        break;
      case 0xE0:                               //CheckClassName
        if(p->flag & HAS_CLASS_NAME) return(p);
        break;
    }
    msg(illrf, index);
    ++curClass.errload;
    return(p);
  }
  return((type && type != p->type) ? NULL : p);
}

#if MAX_CONSTANT_TYPE >= 0x20
#error
#endif
//#define Check   (index)       LoadOpis(index, 0x80)
#define CheckFieldSign(index) LoadOpis(index, 0x40)
#define CheckCallSign(index)  LoadOpis(index, 0xC0)
#define CheckAnySign(index)   LoadOpis(index, 0x20)
#define CheckFieldName(index) LoadOpis(index, 0x60)
#define CheckClass(index)     LoadOpis(index, 0xA0)
#define CheckClassName(index) LoadOpis(index, 0xE0)
//-----------------------------------------------------------------------
TXS tp_decl[] = {
                  TXS_DECLARE("boolean"),
                  TXS_DECLARE("char"),
                  TXS_DECLARE("float"),
                  TXS_DECLARE("double"),
                  TXS_DECLARE("byte"),
                  TXS_DECLARE("short"),
                  TXS_DECLARE("int"),
                  TXS_DECLARE("long")
                };
static char tp_type[]= { j_bool, j_char, j_float, j_double,
                         j_byte, j_short, j_int, j_long, 0 };
//-----------------------------------------------------------------------
typedef struct {
  ushort size;
  ushort flags;
}_STROP_;
#define _OP_PARBEG_   0x0001  //temporary for loader (begin '()')
#define _OP_PAREND_   0x0002  //temporary for loader (end '()')
#define _OP_CLSBEG_   0x0004  //temporary for loader (begin 'L...;'))
#define _OP_ARRBEG_   0x0008  //temporary for loader (begin '[')
#define _OP_FRSPRM_   0x0010  //temporary for loader (not first signature)
#define _OP_VOID_     0x0020  // call void function
#define _OP_ONECLS    0x0040  // signature has class reference
#define _OP_FULLNM    0x0080  // field have '.' or '/' => no FM name
#define _OP_NOFNM     0x0100  // can only signature. Not name
#define _OP_VALPOS    0x0200  // has posit for call signature
#define _OP_NOSIGN    0x0400  // not valid signature
#define _OP_NULL_     0x0800  // has simbols 0
#define _OP_NAT0_     0x1000  // has simbols disabled in Xlat-table
#define _OP_WIDE_     0x2000  // has simbols >= 0x100
#define _OP_UTF8_     0x4000  //  Utf8 String
#ifndef REMOVE_UNICODE
#define _OP_UNICODE_  0x8000  //  Unicode String  (remove from standrt)
#endif
#define _OP_NONAME   (_OP_WIDE_ | _OP_NULL_ | _OP_NAT0_)
#define _OP_SGNDFR   (_OP_NOSIGN | _OP_VALPOS | _OP_NOFNM | _OP_FULLNM | _OP_ONECLS | _OP_VOID_)
//-----------------------------------------------------------------------
int fmtString(ushort index, short size, fmt_t mode, _OUTPRC_ putproc)
{
  char     data[MAXSPECSIZE];
  _STROP_  _opstr;
  register ushort *tp, i;
  register char   *p;
  ushort   cw, ostsize, off = 0, dimcnt = 0, strcnt = 0;
  uchar    f_prm = 0, _incast = 0;
  char     extrflag = (mode == fmt_signclass || mode == fmt_retclass);
  uval_t   ind = index << 16;

  if(!ind ||
     (*((ulong *)&_opstr) = ConstantNode.altval(ind++)) == 0) destroyed();

  if(mode >= fmt_retsign && mode <= fmt_paramstr) {
    if((ostsize = (ushort)ConstantNode.altval(ind)) == 0) destroyed();
    if(mode != fmt_paramstr) {
      ind += (ostsize / (MAXSPECSIZE / 2));
      off = ostsize % (MAXSPECSIZE / 2);
      ostsize = _opstr.size - ostsize;
    }
  } else {
    ostsize = _opstr.size;
    if(mode == fmt_string) {
      OutChar('"');
      --size;
    }
  }

  if(ostsize) goto forced;
deb_error((mode != fmt_string), "f:size");
  while(ostsize) {
    if(!size)
     if((size = putproc()) == 0) return(-1);
     else                        ++strcnt;
    if(!off) {
forced:
      if((tp = (ushort *)ConstantNode.supval(ind++)) == NULL) destroyed();
      tp = (ushort *)memcpy(data, tp, MAXSPECSIZE) + off;
      off = (MAXSPECSIZE / 2) - off;
    }

    cw = *tp++;
    --off;
    --ostsize;

    if(cw >= 0x100) {
deb_error((mode != fmt_string), "f:big1");
dumps:
      static char fmt_val[] = "\\u%04X";   //UNICODE-format
      if(size < 6)
        if((size = putproc()) == 0) return(-1);
        else                    ++strcnt;
      size -= out_snprintf(fmt_val, cw);
      continue;
    }

    if(mode == fmt_string) {
      if(!cw) {
        cw = '0';
        goto sputs;
      }
      if(cw == '\\' || cw == '"') {
sputs:
        if(size < 2)
          if((size = putproc()) == 0) return(-1);
          else                    ++strcnt;
        OutChar('\\');
        --size;
      } else if(!strchr(AsciiStringChars, cw)) {
        if(cw >= 7 && cw <= 0xD) {
          static char casc[] = "abtnvfr";

          cw = casc[cw - 7];
          goto sputs;
        } else goto dumps;
      }
puts:
      OutChar((char)cw);
      --size;
      continue;
    }

deb_error((cw <= ' '), "f:min");
    if ( jasmin() ) goto puts;
//    if ( jasmin() && (cw == '/' || cw == '.') ) goto puts;
    if(cw == '/') cw = '.';
    if(cw == '.') {
deb_error((!_incast && mode != fmt_fullname), "f:/");
      goto puts;
    }

    if(_incast) {
      if(cw != ';') goto puts;
      _incast = 0;
      if(extrflag) {
deb_error(ostsize, "f:extract");
#ifdef __debug__
        extrflag = -1;
#endif
        break;
      }
      goto endcast;
    }

    if(cw == ')' || cw == '(') {
deb_error((mode != fmt_paramstr || dimcnt), "f()");
      goto puts;
    }

    if(f_prm == 1) {
deb_error((mode != fmt_paramstr || dimcnt), "f:2");
      OutChar(',');
      ++f_prm;
      if(!--size)
        if((size = putproc()) == 0) return(-1);
        else                    ++strcnt;
      OutChar(' ');
      if(!--size)
        if((size = putproc()) == 0) return(-1);
        else                    ++strcnt;
    }

    if(cw == j_array) {
deb_error((!ostsize || mode >= fmt_fullname), "f:[");
      ++dimcnt;
      continue;
    }

    if(mode == fmt_cast && !dimcnt) mode = fmt_fullname;
    if(mode >= fmt_fullname) goto puts;

    if(cw == j_class) {
deb_error((!ostsize), "f:cls");
      ++_incast;
      continue;
    }

    if(cw == j_void_ret) {
      static char vd[] = "void";

deb_error((ostsize || mode != fmt_retsign), "f:void");
      p = vd;
      i = sizeof(vd);
    } else {
      if((p = strchr(tp_type, cw)) == NULL) int_error();
      i = (ushort)(p - tp_type);
      p = tp_decl[i].str;
      i = tp_decl[i].size;
    }
    if(size < i)
      if((size = putproc()) == 0) return(-1);
      else                    ++strcnt;
    OutLine(p);
    size -= i;
endcast:
deb_error(((!ostsize) != (mode != fmt_paramstr)), "f:mode");
    f_prm = 1;
    while(dimcnt) {
      if(size < 2)
        if((size = putproc()) == 0) return(-1);
        else                    ++strcnt;
      OutChar('[');
      OutChar(']');
      size -= 2;
      --dimcnt;
    }
  }
deb_error((extrflag > 0), "f:extrend");

  if(mode <= fmt_retsign) OutChar((mode == fmt_string) ? '"' : ' ');
  *get_output_ptr() = '\0';
  return(strcnt);
}

//-----------------------------------------------------------------------
int dmpString(ushort index, _DMPPRC_ dmpproc)
{
  _STROP_  _opstr;
  register ushort i, ostsize;
  register char   *p;
  int      cur, summa = 0;
  ulong    ind = ((ulong)index) << 16;

deb_error((!index), "dmp");
  if((*((ulong *)&_opstr) = ConstantNode.altval(ind++)) == 0) destroyed();

  for(ostsize = _opstr.size; ostsize; ostsize -= i) {
    if((p = ConstantNode.supval(ind++)) == NULL) destroyed();
    i = (ostsize > (MAXSPECSIZE / 2)) ? (MAXSPECSIZE / 2) : ostsize;
    if((cur = dmpproc(p, i)) == 0) return(-1);
    summa += cur;
  }
  return(summa);
}

//-----------------------------------------------------------------------
static int CmpString(ushort index1, ushort index2)
{
  register char   *p1, *p2;
  register ushort i;
  ushort          siz;
  uval_t          tmp1, tmp2, ind1, ind2;

deb_error((!index1 || !index2), "cs-ind");
  if(index1 == index2) return(0);
  ind1 = index1 << 16;
  ind2 = index2 << 16;
  if((tmp1 = ConstantNode.altval(ind1++)) == 0 ||
     (tmp2 = ConstantNode.altval(ind2++)) == 0) destroyed();

  if(tmp1 != tmp2) return(1);
  if((siz = (ushort)tmp1) == 0) return(-1);
  i = MAXSPECSIZE / 2;
  do {
    if((p1 = ConstantNode.supval(ind1++)) == NULL ||
       (p2 = ConstantNode.supval(ind2++)) == NULL) destroyed();
    if(i > siz) i = siz;
    if(memcmp(p1, p2, i * 2)) return(1);
  }while((siz -= i) != 0);

  return(0);
}

//----------------------------------------------------------------------
uval_t SearchFM(ushort name, ushort sign, short *naprN)
{
  sval_t          pos;
  sval_t          sav_start, sav_size;
  short           napr = *naprN;
  register void   *p;
  register ushort sav_sign, sav_num;

  pos =  (napr > 0) ? curClass.FieldCnt : -(uval_t)curClass.MethodCnt;
  for( ; pos; pos -= (sval_t)napr) {

    if((p = netnode(curClass.ClassNode).supval(pos)) == NULL) destroyed();
    if(((_FMid_ *)p)->flag & 3) continue;
    sav_sign = ((_FMid_ *)p)->sign;
    sav_num = ((_FMid_ *)p)->Number;
    if(napr < 0) {
      sav_start = ((SegInfo *)p)->startEA;
      sav_size = ((SegInfo *)p)->CodeSize;
    }
    if((name == ((_FMid_ *)p)->name && sign == sav_sign) ||
       (!CmpString(name, ((_FMid_ *)p)->name) && !CmpString(sign, sav_sign)))
      if(napr < 0) {
        if(!sav_size) *naprN = 0;
        return(sav_start);
      } else return(curClass.startEA + sav_num);
  }
  return(BADADDR);
}

//-----------------------------------------------------------------------
static int cmpSignString(ushort index1, int mt, ushort index2, int flag)
{
  register ushort *p1, *p2;
  uval_t          tmp1, tmp2, ind1, ind2;
  ushort          i0, i1, i2, siz1, siz2;
  char            flgM = 1;

  ind1 = ((uval_t)index1) << 16;
  ind2 = ((uval_t)index2) << 16;
  if((tmp1 = ConstantNode.altval(ind1++)) == 0 ||
     (tmp2 = ConstantNode.altval(ind2++)) == 0 ||
     (siz1 = (ushort)tmp1) == 0 || (siz2 = (ushort)tmp2) == 0) int_error();

  if(((ushort)((tmp1 ^ tmp2) >> 16)) & ~_OP_SGNDFR) return(1);
  if(mt && !(tmp1 & ((uval_t)_OP_ONECLS << 16))) return(1);  //xtrnRef_sign

  i1 = i2 = 0;
  for( ; ; ) {
    if(!i1) {
      if((p1 = (ushort *)ConstantNode.supval(ind1)) == NULL) int_error();
      i1 = MAXSPECSIZE / 2;
      if(mt < 0) {
        register ushort offs = (ushort)ConstantNode.altval(ind1);
        if(!offs || offs >= siz1) int_error();
        siz1 -= offs;
        ind1 += (offs / (MAXSPECSIZE / 2));
        offs %= (MAXSPECSIZE / 2);
        i1 -= offs;
        p1 += offs;
        mt = 0;
      }
      ++ind1;
    }
    if(!i2) {
      if((p2 = (ushort *)ConstantNode.supval(ind2++)) == NULL) int_error();
      i2 = MAXSPECSIZE / 2;
    }
    if(flag >= 0) {
      if(flag) {
        do {
          if(*p2 != j_array) break;
          ++p2;
          if(!--siz2) int_error();
        } while(--i2);
        if(!i2) continue;
        if(siz2 <= 2) int_error();
        siz2 -= 2;
      }
      ++p2;
      --flag;
      if(!--i2) continue;
      --flag;
    }
    if(flgM >= 0) {
      if(flgM) {
        do {
          if(*p1 != j_array) break;
          ++p1;
          if(!--siz1) int_error();
        } while(--i1);
        if(!i1) continue;
        if(siz1 <= 2) int_error();
        siz1 -= 2;
        if(siz1 != siz2) return(1);
      }
      ++p1;
      --flgM;
      if(!--i1) continue;
      --flgM;
    }
    if((i0 = i1) > i2) i0 = i2;
    if(i0 > siz1) i0 = siz1;
    if(memcmp(p1, p2, i0 * 2)) return(1);
    if(!(siz1 -= i0)) break;
    i1 -= i0;
    i2 -= i0;
  }

  return(0);
}

//-----------------------------------------------------------------------
static ushort xtrnSignSearch(ushort name, int mode)
{
  register ConstOpis *cr;
  register ushort    j;

  if(curClass.ThisClass &&
     !cmpSignString(name, mode, curClass.ThisName, -1)) return(0xFFFF);
  for(j = curClass.xtrnLQE; j;
      j = (ushort)(netnode(curClass.xtrnNode).altval(cr->ref_ip) >> 16)) {
    if((cr = (ConstOpis *)ConstantNode.supval(j)) == NULL ||
       (cr->type != CONSTANT_Class) || (j = cr->ref_ip) == 0) int_error();
    if(!cmpSignString(name, mode, cr->_name, cr->_sign - 1)) return(j);
  }
  return(0);
}

//-----------------------------------------------------------------------
static ulong FileSize;
//-----------------------------------------------------------------------
static void errtrunc(void)
{
  static char trunc[] = "Premature end of file";
  error(trunc);
}
//-----------------------------------------------------------------------
static ushort read2(void)
{
  ushort data;
  if(FileSize < 2) errtrunc();
  FileSize -= 2;
  fread2bytes(myFile, &data, 1);
  return(data);
}
//-----------------------------------------------------------------------
static ulong read4(void)
{
  ulong data;
  if(FileSize < 4) errtrunc();
  FileSize -= 4;
  fread4bytes(myFile, &data, 1);
  return(data);
}
//-----------------------------------------------------------------------
static uchar read1(void)
{
  uchar data;
  if(!FileSize) errtrunc();
  --FileSize;
  eread(myFile, &data, 1);
  return(data);
}
//-----------------------------------------------------------------------
#ifndef REMOVE_UNICODE
static void readData(void *data, ushort size)
{
  if(FileSize < (ulong)size) errtrunc();
  FileSize -= size;
  eread(myFile, data, size);
}
#endif
//-----------------------------------------------------------------------
static void skipData(ushort size)
{
  if(FileSize < (ulong)size) errtrunc();
  FileSize -= size;
  qfseek(myFile, size, SEEK_CUR);
}

//-----------------------------------------------------------------------
#ifndef REMOVE_UNICODE
static void uni_warn(void)
{
  static char first = 0;
  if(!first) {
    static char uni_fmt[] =
        "File contain CONSTANT_Unicode, but it is removed from the standard";
    ++first;
    warning(uni_fmt);
  }
}
#endif

//-----------------------------------------------------------------------
static uchar LoadString(ushort index, ushort size)
{
  char     str[MAXSPECSIZE];
  _STROP_  _opstr;
  ushort   cursize;
#ifndef REMOVE_UNICODE
  short    curpage;
#endif
  uchar    clssym = 0;   //init супротив warning
  char     clsflg = 0;
  ulong    posit = 0, ind = (((ulong)index) << 16) + 1;

  _opstr.size = size;

#ifndef REMOVE_UNICODE
  if(curConst.type == CONSTANT_Unicode) {
    uni_warn();
    _opstr.flags = _OP_UNICODE_;
    curpage = -1;
    if(!size) {
      static char cc3[] = "information: empty unicode string (%u)\n";
      msg(cc3, index);
//      ++curClass.errload;
    }
  } else
#endif
         _opstr.flags = _OP_UTF8_;

  while(size) {
#ifndef REMOVE_UNICODE
    if(_opstr.flags & _OP_UNICODE_) {
      register char *p;
      register ushort i = size;

      if(i > (MAXSPECSIZE / 2)) i = MAXSPECSIZE / 2;
      size -= (cursize = i);
      readData(str, i * 2);
      if(curpage > -2) {
        p = &str[1];
        if(curpage == -1) curpage = (ushort)(uchar)*p;
        while(i--) if(curpage == *p++) ++p;
                   else {
                          curpage = -2;
                          break;
                        }
      }
    } else {
#endif
      char            c1;
      register ushort cw;
      register ushort *po = (ushort *)str;
      register char   c;

      cursize = 0;
      do {
        --size;
        if((cw = (uchar)(c = read1())) == 0 || cw >= 0xf0) goto errcoding;
        if(c < 0) {
          if(!size) goto errchar;
          cw = (cw & 0x1F) << 6;
          --size;
          --_opstr.size;
          if(((c1 = read1()) & 0xC0) != 0x80) goto errchar;
          cw |= (c1 & 0x3F);
          if((c & 0xE0) != 0xC0) {
            if(!size || ((c & 0xF0) != 0xE0) ||
               (((c = read1()) & 0xC0) != 0x80)) {
errchar:
                static char errc[] = "Illegal byte in CONSTANT_Utf8 (%u)";
                error(errc, index);
            }
            --size;
            --_opstr.size;
            if((cw = (cw << 6) | (c & 0x3F)) < 0x800) goto errcoding;
          } else if(cw < 0x80 && cw) {
errcoding:
              static char errc[] =
                           "information: non-standard symbol encoding (%u)\n";
              msg(errc, index);
              ++curClass.errload;
          }
        }

        if(!size && _opstr.size == 1 && (loadMode & 0x20) &&
           (cw >= 0x80 || !strchr(tp_type, cw))) {
//Symantec error (strip) #3
          static char sfm[] = "_?_%04X";
          char *ps;

          _opstr.size = cursize = qsnprintf(ps=&str[20], sizeof(str)-20, sfm, cw);
          do *po++ = *ps++; while(*ps);
          break;
        }

        if(cw >= 0x100)                         _opstr.flags |= _OP_WIDE_;
        else if(!cw)                            _opstr.flags |= _OP_NULL_;
             else {
               if((c = XlatAsciiName[cw]) == 0) _opstr.flags |= _OP_NAT0_;
               else cw = (uchar)c;
               c = !strchr(NameChars, cw);
             }

        *po++ = cw;

        if(_opstr.flags & _OP_NONAME) continue;

        if(cw == '/' || cw == '.') {
          c = 0;
          _opstr.flags |= _OP_FULLNM;
        }
        if(c) _opstr.flags |= (_OP_NOFNM | _OP_FULLNM);
        if(_opstr.flags & _OP_NOSIGN) continue;
        if(cw == '(') {
          if(cursize || (ushort)ind != 1) goto nosign;
          _opstr.flags |= _OP_PARBEG_;
          --clsflg;  //-1
          continue;
        }
        if(cw == ')') {
          if(posit ||
             (_opstr.flags & (_OP_PARBEG_ | _OP_ARRBEG_ | _OP_CLSBEG_)) !=
                                                    _OP_PARBEG_) goto nosign;
          _opstr.flags ^= (_OP_PARBEG_ | _OP_PAREND_);
          continue;
        }
        if(_opstr.flags & _OP_CLSBEG_) {
          if(cw == ';') {
            if(!clssym) goto nosign;        //\\ ??? empty class name
            _opstr.flags &= ~_OP_CLSBEG_;
            clsflg = clsflg ? -1 : 1;
            continue;
          }
          if(c) goto nosign;
          clssym = 1;
          continue;
        }
        if(_opstr.flags & _OP_PAREND_) {
          posit = ((((ushort)ind) - 1) * (MAXSPECSIZE / 2)) +
                   (ushort)(po-(ushort *)str) - 1;
          _opstr.flags &= ~(_OP_PAREND_ | _OP_FRSPRM_);
          if(cw == j_void_ret) {
            if(size) goto nosign;
            _opstr.flags |= _OP_VOID_;
            continue;
          }
          clsflg = 0;
        }
//chksign
        if((_opstr.flags & (_OP_PARBEG_ | _OP_FRSPRM_)) == _OP_FRSPRM_)
                                                                goto nosign;
        if(cw == j_array) {
          _opstr.flags |= _OP_ARRBEG_;
          continue;
        }
        _opstr.flags = (_opstr.flags & ~_OP_ARRBEG_) | _OP_FRSPRM_;
        if(cw == j_class) {
          _opstr.flags |= _OP_CLSBEG_;
          clssym = 0;
          continue;
        }
        clsflg = -1;
        if(!strchr(tp_type, cw)) {
nosign:
          _opstr.flags |= _OP_NOSIGN;
          posit = 0;
        }
      }while(++cursize < (MAXSPECSIZE / 2) && size);
#ifndef REMOVE_UNICODE
    }
#endif
    ConstantNode.supset(ind++, str, cursize * 2);
  }

  if(_opstr.flags & (_OP_PARBEG_ | _OP_PAREND_ | _OP_CLSBEG_ | _OP_ARRBEG_))
                                                  _opstr.flags |= _OP_NOSIGN;
  if((_opstr.flags & (_OP_UTF8_ | _OP_NONAME | _OP_NOSIGN)) == _OP_UTF8_) {
    if(posit)       _opstr.flags |= _OP_VALPOS;
    if(clsflg == 1) _opstr.flags |= _OP_ONECLS;
  }

  ConstantNode.altset((ind &= 0xFFFF0000ul), *((ulong *)&_opstr));


  if(_opstr.flags & _OP_VALPOS) {
    ConstantNode.altset(++ind, posit);
    return(HAS_PARAMSIGN);
  }

#ifndef REMOVE_UNICODE
  if(_opstr.flags & _OP_UNICODE_) return((uchar)curpage);
#endif

  if(!_opstr.size || (_opstr.flags & _OP_NONAME)) return(0);

  return(((_opstr.flags & _OP_NOSIGN) ? 0 : HAS_SIGNATURE)  |
         ((_opstr.flags & _OP_NOFNM)  ? 0 : HAS_CLASS_NAME) |
         ((_opstr.flags & _OP_FULLNM) ? 0 : HAS_FM_NAME));
}

//-----------------------------------------------------------------------
static ushort no_prmpt(void)
{
  *get_output_ptr() = '\0';
  return(0);
}

//-----------------------------------------------------------------------
static ushort attrStr(ushort index, ushort mask, char *str, size_t strsize)
{
  static char name0[] = "ConstantValue",
              name1[] = "Code",
              name2[] = "Exceptions",
              name3[] = "SourceFile",
              name4[] = "LineNumberTable",
              name5[] = "LocalVariableTable",    //max sizeof!
             *name[] = {name0, name1, name2, name3, name4, name5, NULL};
  register char  **p;
  register short i;
  register ConstOpis *opis = LoadOpis(index, CONSTANT_Utf8);

  str[0] = '\0';
  init_output_buffer(str, strsize);
  if(!opis || !(opis->flag & HAS_FM_NAME)) return(0xFFFFu);
  if(fmtString(index,sizeof(name5)+1,fmt_name,no_prmpt) < 0) return(0xFFFEu);

  for(i = 1, p = name; *p; p++, i <<= 1)
    if((mask & i) != 0 && !strcmp(*p, str)) return(i);

  return(0xFFFDu);
}

//-----------------------------------------------------------------------
static ushort LoadPool(void)
{
  static char f_p1[] = "Loading constant pool...",
              f_p2[] = "loaded...",
              f_p3[] = "checked...",
              f_p4[] = "OK\n";
  ushort   k, ui;
  register ushort i;
  register ConstOpis *co = &curConst;

  msg(f_p1);
  for(i = 1; i <= curClass.maxCPindex; i++) {
    co->flag = 0;
    switch(co->type = read1()) {
      case CONSTANT_Long:
      case CONSTANT_Double:
        co->value2 = read4();
      case CONSTANT_Integer:
      case CONSTANT_Float:
        co->value = read4();
        break;

      case CONSTANT_NameAndType:
      case CONSTANT_Fieldref:
      case CONSTANT_Methodref:
      case CONSTANT_InterfaceMethodref:
        if((k = read2()) == 0 || k > curClass.maxCPindex) {
badindex:
          static char cc1[] =
                        "Bad reference in constant pool (%u) %u %u, %lX";
          error(cc1, i, k, curClass.maxCPindex, qftell(myFile));//i);
        }
        co->_class = k;     // _subnam for name & type
      case CONSTANT_Class:
        co->ref_ip = 0;
      case CONSTANT_String:
        if((k = read2()) == 0 || k > curClass.maxCPindex) goto badindex;
        co->_name = k;      // _sign for name & type
        break;

      case CONSTANT_Utf8:
#ifndef REMOVE_UNICODE
      case CONSTANT_Unicode:
#endif
        co->_name = i;      // for xtrnRef_sign
        {
//size for MAP
          register uchar n = LoadString(i, co->_Ssize = read2());
          if(co->type == CONSTANT_Utf8) co->flag |= n;
          else co->_Spage = (short)(char)n; //page
        }
        break;

      default:
        {
          static char badtype[] = "Bad constant type 0x%x (%u)";
          error(badtype, co->type, i);
          break;
        }
    }  // end switch
    StoreOpis(i, co);
    if(co->type == CONSTANT_Long || co->type == CONSTANT_Double) {
      if(curClass.maxCPindex == i) {
        static char icp[] = "Premature end of constant pool";
        error(icp);
      }
      ++i;
    }
  } // end for

  msg(f_p2);
  for(i = 1; i <= curClass.maxCPindex; i++) {
    register ConstOpis *cr;
    if((co = (ConstOpis *)ConstantNode.supval(i)) == NULL) continue;
    switch(co->type) {
      case CONSTANT_String:
        if(!LoadOpis(co->_name, CONSTANT_Utf8)) goto badindex;
      default:
        continue;

      case CONSTANT_NameAndType:
        if((cr = CheckFieldName(co->_class)) == NULL)  goto badindex;
        co->flag |= ((cr->flag & HAS_FM_NAME) << SUB_SHIFT);
        if((cr = CheckAnySign(co->_name)) == NULL)     goto badindex;
        co->flag |= ((cr->flag<<SUB_SHIFT) & (SUB_SIGNATURE | SUB_PARAMSIGN));
        break;

      case CONSTANT_Class:
        if((cr = CheckClass(co->_name)) == NULL)       goto badindex;
        co->flag |=
                 (cr->flag & (HAS_FM_NAME | HAS_SIGNATURE | HAS_CLASS_NAME));
        co->_sign = co->_subnam = 0;
        break;
    } // end switch
    StoreOpis(i, co);
    if((loadMode & 1) &&
       (co->type == CONSTANT_Class) && (co->flag & HAS_CLASS_NAME)) {
      ulong    rfc;
      register ushort j;

      curConst = *co;
      for(j = 1; j <= curClass.xtrnCnt; j++)
        if(!CmpString(curConst._name,
               (ushort)(rfc = netnode(curClass.xtrnNode).altval(j)))) {

        curConst._subnam = (ushort)(rfc >> 16);
        goto fnd;
      }
      netnode(curClass.xtrnNode).altset(j, (i<<16) | curConst._name);
      ++curClass.xtrnCnt;
      curConst._subnam = i;
fnd:
      StoreOpis(i, &curConst);
    }
  }  // end for
  if(loadMode & 1) netnode(curClass.xtrnNode).altdel();  // delete all

  msg(f_p3);
  for(ui = 0, i = 1; i <= curClass.maxCPindex; i++) {
    register ConstOpis *cr;
    ulong    sav;

    if((co = (ConstOpis *)ConstantNode.supval(i)) == NULL) continue;
    switch(co->type) {
      default:
        continue;

      case CONSTANT_Class:
        if(!(loadMode & 1)) break;
        if((co->flag & (HAS_SIGNATURE | HAS_CLASS_NAME)) == HAS_SIGNATURE &&
           !co->_subnam && (ConstantNode.altval((ulong)co->_name << 16) &
                                                ((ulong)_OP_ONECLS << 16))) {
          co->_sign = 2; //ATTENTION cmpSign
          ++curClass.xtrnCnt;
        }
        break;

      case CONSTANT_InterfaceMethodref:
      case CONSTANT_Fieldref:
      case CONSTANT_Methodref:
        if((cr = LoadOpis(co->_class, CONSTANT_Class)) == NULL) goto badindex;
//\\VALID NULL ??? go twos if any... (reorder cur ind to null)
        co->flag |= (cr->flag & HAS_CLASS_NAME);
        k = co->_name;
        sav = curClass.errload;
        co->ref_ip = cr->_subnam;
        CheckClassName(co->_name = cr->_name);
        if((cr = LoadOpis(k, CONSTANT_NameAndType)) == NULL)    goto badindex;
        co->_sign = cr->_name;
        co->_subnam = cr->_class;
        co->flag |=(cr->flag & (SUB_FM_NAME | SUB_SIGNATURE | SUB_PARAMSIGN));
        if(co->type != CONSTANT_Fieldref) CheckCallSign(co->_sign);
        else                              CheckFieldSign(co->_sign);
        if((loadMode & 1) && (sav == curClass.errload)) {
          netnode(curClass.xtrnNode).altset(++ui, i, '0');
          ++curClass.xtrnCnt;
        } else co->ref_ip = 0;
        break;
    } // end switch
    StoreOpis(i, co);
  }  // end for

  msg(f_p4);
  return(ui);
}

//-----------------------------------------------------------------------
static void trunc_name(ushort num, ushort type)
{
  static char fnam[]   = "...(Field_%u)",
              metnam[] = "...(Method_%u)",
              locnam[] = "...(locvar_%u)",
              xtrn[]   = "...(extern_%u)",
              clsnam[] = "...",
              *add_nam[5] = { xtrn, fnam, metnam, locnam, clsnam };

  *addonce_name = '.';
  size_t s = (sizeof(metnam) - 2 + 5 + 1);
  qsnprintf(get_output_ptr()-s, s, add_nam[type], num);
}

//-----------------------------------------------------------------------
static void xtrnSet(ushort cin, register ConstOpis *co, ushort xip,
                                         char *str, size_t strsize, fmt_t mode)
{
  static   ushort endcls;
  register uval_t rfa = cin;
  register ushort js = MAXNAMELEN - 1;

  co->ref_ip = xip;
  StoreOpis(cin, co);
  init_output_buffer(str, strsize);
  if(mode != fmt_name) {
    if(fmtString(co->_name, js, mode, no_prmpt) < 0) {
      endcls = MAXNAMELEN;
trnc:
      trunc_name(xip, 0);
    } else if(mode == fmt_fullname) endcls = strlen(str);
    rfa |= ((uval_t)curClass.xtrnLQE << 16);
    curClass.xtrnLQE = cin;
  } else {
    if(endcls >= MAXNAMELEN - 2) {set_output_ptr(&str[MAXNAMELEN-1]); goto trnc;}
    str[endcls] = '.';
    set_output_ptr(get_output_ptr() + (endcls + 1));
    js -= (endcls + 1);
    if(fmtString(co->_subnam, js, mode, no_prmpt) < 0) goto trnc;
  }
  netnode(curClass.xtrnNode).altset(xip, rfa);
  doByte(rfa = curClass.xtrnEA + xip, 1);
  *addonce_name = '.';
  do_name_anyway(rfa, str);
  *addonce_name = '\0';
  hide_name(rfa);

}

//-----------------------------------------------------------------------
static void deltry(ushort bg, ushort ic, ushort ui)
{

  register ushort i, j;
  register ConstOpis *co;

  for(i = bg; i <= curClass.xtrnCnt; i++) {
    if((j = (ushort)netnode(curClass.xtrnNode).altval(i, '0')) ==0) continue;
    co = (ConstOpis *)ConstantNode.supval(j);
    if(co->type != curConst.type || co->flag != curConst.flag ||
       co->ref_ip != ic) continue;
    if(co->_subnam != curConst._subnam || co->_sign != curConst._sign) {
      ushort sg = co->_sign;
      if(CmpString(co->_subnam, curConst._subnam) ||
         CmpString(sg, curConst._sign)) continue;
    }
    (co = (ConstOpis *)ConstantNode.supval(j))->ref_ip = ui;
    StoreOpis(j, co);
    netnode(curClass.xtrnNode).altdel(i, '0');
  }
}

//-----------------------------------------------------------------------
static void setPoolReference(void)
{
  static char sort[] = "Sorting external references...",
              oksr[] = "OK\n";
  char str[MAXNAMELEN];
  register ConstOpis *co;
  ushort             i, ui, ii, ic;
  register ushort    j;

  msg(sort);
  for(ui = 0, i = 1; i <= curClass.xtrnCnt; i++) {
    if((j = (ushort)netnode(curClass.xtrnNode).altval(i, '0')) ==0) continue;
    showAddr(curClass.xtrnCnt - i);
    co = (ConstOpis *)ConstantNode.supval(j);
    if(co->_class == curClass.ThisClass) {
      co->ref_ip = 0;
      StoreOpis(j, co);
      continue;
    }
    curConst = *co;
    co = (ConstOpis *)ConstantNode.supval(ic = curConst.ref_ip);
    xtrnSet(ic, co, ++ui, str, sizeof(str), fmt_fullname);
    xtrnSet(j, &curConst, ++ui, str, sizeof(str), fmt_name);
    deltry(ii = i + 1, ic, ui);
    for( ; ii <= curClass.xtrnCnt; ii++) {
      if((j = (ushort)netnode(curClass.xtrnNode).altval(ii, '0')) ==0)
                                                                   continue;
      if((co = (ConstOpis *)ConstantNode.supval(j))->ref_ip != ic) continue;
      xtrnSet(j, co, ++ui, str, sizeof(str), fmt_name);
      netnode(curClass.xtrnNode).altdel(ii, '0');
      deltry(ii + 1, ic, ui);
    }
  }
  netnode(curClass.xtrnNode).altdel_all('0');

  for(i = 1; i <= curClass.maxCPindex; i++) {
    if((co = (ConstOpis *)ConstantNode.supval(i)) == NULL) continue;
    if(co->type != CONSTANT_Class || co->_subnam != i || co->ref_ip ||
       i == curClass.ThisClass) continue;
    xtrnSet(i, co, ++ui, str, sizeof(str), fmt_fullname);
  }

  for(i = 1; i <= curClass.maxCPindex; i++) {
    if((co = (ConstOpis *)ConstantNode.supval(i)) == NULL) continue;
    if(co->type != CONSTANT_Class) continue;
    if(co->_subnam) {
      if(co->_subnam == i) continue;
      co->ref_ip = ((ConstOpis *)ConstantNode.supval(co->_subnam))->ref_ip;
    } else {
      if(!co->_sign) continue;
      curConst = *co;
      if((curConst.ref_ip = xtrnSignSearch(curConst._name, 0)) == 0) {
        xtrnSet(i, &curConst, ++ui, str, sizeof(str), fmt_signclass);
        continue;
      }
      co = &curConst;
    }
    StoreOpis(i, co);
  }

  if((curClass.xtrnCnt = ui) != 0) {
    set_segm_end(curClass.xtrnEA, curClass.xtrnEA + curClass.xtrnCnt + 1, 1);
    doByte(curClass.xtrnEA, 1);
  } else {
    netnode(curClass.xtrnNode).kill();
    curClass.xtrnNode = 0;
    del_segm(curClass.xtrnEA, 1);
    curClass.xtrnEA = 0;
  }
  msg(oksr);
}

//-----------------------------------------------------------------------
static void CheckPoolReference(int pass)
{
  static char chk[] = "Checking references, pass %d...\n",
              emc[] = "Number of unused CONSTANT_%s: %lu\n";
  char str[40];
  register ConstOpis *co;
  uval_t    k1, k2, k3;
  ushort   i, mask = a_FULL_attr;
  uchar    lvar = 1;

  msg(chk, pass + 1);
  for(k1 = k2 = k3 = 0, i = 1; i <= curClass.maxCPindex; i++)
    if((co = (ConstOpis *)ConstantNode.supval(i)) != NULL &&
       !(co->flag & _REF)) switch(co->type) {

      case CONSTANT_Utf8:
        if(!pass) {
          static char lv[] = "LocalVariables";
          register ushort j;

          ++k2;
          if((co->flag & HAS_FM_NAME) && (mask || lvar) &&
             (j = attrStr(i, mask, str, sizeof(str))) <= 0xFFFDu)
            if(mask == 0xFFFDu) {
              if(lvar && !strcmp(str, lv)) {
                lvar = 0;
                break;
              }
            } else if(mask & j) {
              mask ^= j;
              break;
            }
          ++k3;
        }
        break;

      case CONSTANT_NameAndType:
        if(!pass) ++k1;
        break;

      case CONSTANT_Class:
        if(pass){
          ++k2;
          if(!(co->flag & HAS_CLASS_NAME)) ++k3;
        }
        break;

      default:
        if(pass)  ++k1;
        break;
    }

  if(k1) {
    static char n_t[] = "NameAndType",
                oth[] = " records";

    msg(emc, pass ? oth : n_t, k1);
    ++curClass.errload;
  }
  if(k2) {
    static char utf[] = "Utf8",
                cls[] = "Class";

    msg(emc, pass ? cls : utf, k2);
    if(k3) ++curClass.errload;
  }

}

//-----------------------------------------------------------------------
static void locDecl(ushort iName, ushort iSign, ushort bo,
                                                      uval_t eo, ushort slot)

{
  static char begs[] = "Scope slot",
              bege[] = "Scope",
              fmbn[] = "/*%s%03u*/ %s%s",
              fmbe[] = "%s%03u - %s",
              endf[] = "%s %s",
              ends[] = "End Scope",
              badf[] = "%s slot%u - indexes %u/%u";
  char str[MAXSTR];
  register ushort size;
  register char   *p;
  register ea_t bas = curSeg.startEA + bo;

  if(eo + 3 > curSeg.CodeSize) eo = 0;
  else eo += curSeg.startEA;

  init_output_buffer(str, sizeof(str));
  if(!fmtString(iName, MAXNAMELEN - 1, fmt_name, no_prmpt)) {
    do_name_anyway(curSeg.DataBase + slot, str);
    if(eo) add_long_cmt(eo, 0, endf, ends, str);
    p = get_output_ptr() + 1;
    set_output_ptr(p);
#if (MAXNAMELEN + 2) >= MAXSTR
#error
#endif
    size = (sizeof(str) - 1) - (get_output_ptr() - str);
    if(size >= 3 && !fmtString(iSign, size, fmt_sign, no_prmpt))
         describe(bas, 1, fmbn, begs, slot, p, str);
    else add_long_cmt(bas, 1, fmbe, begs, slot, str);
  } else {
    if(eo) add_long_cmt(eo, 0, badf, ends, slot, iName, iSign);
    add_long_cmt(bas, 1, badf, bege, slot, iName, iSign);
  }
}

//-----------------------------------------------------------------------
static segment_t *_add_seg(int caller)
{
  static char _csg[] = "met_",
              _dsg[] = "_var",
              _hsg[] = "head",
              _esg[] = "xtrn",
             *_cls[4] = { _esg, _csg, _dsg, _hsg },
              fmtc[] = "met%03u",
              fmtd[] = "var%03u",
              fmth[] = "_Class",
              fmti[] = "import",
              *fm[4] = { fmti, fmtc, fmtd, fmth };
  static ea_t      startEA = 0;
  static ushort    cursel = 1;
  ushort           sel;
  uval_t           end, top, size;
  register segment_t *S;
  uchar            type;

  switch(caller) {
    case 1: // method
      curSeg.startEA = startEA;
    case -1: //code
      startEA = curSeg.startEA;
      type = SEG_CODE;
      size = curSeg.CodeSize;
      break;

    case 2:  //data
      curSeg.DataBase = startEA;
      size = curSeg.DataSize;
      type = SEG_DATA;
      break;

    case 3: // class
      curClass.startEA = startEA;
      size = curClass.FieldCnt + 1;
      type = SEG_IMP;
      break;

    case 0: // header
      curClass.xtrnEA = startEA = toEA(inf.baseaddr, 0);
      if(!curClass.xtrnCnt) return(NULL);
      size = curClass.xtrnCnt;
      type = SEG_XTRN;
    default:
      break;
  }
  end = ((top = startEA + size) + (0xF + 1)) & ~0xF;

  if(caller < 0) {
    if((S = getseg(startEA)) == NULL ||
       !set_segm_end(curSeg.startEA, end, 1)) qexit(1);
    ulong pos = qftell(myFile);
    linput_t *li = make_linput(myFile);
    file2base(li, pos, startEA, top, FILEREG_PATCHABLE);
    unmake_linput(li);
    qfseek(myFile, pos + curSeg.CodeSize, SEEK_SET);
  } else {
    if(startEA > 0x100000l) set_selector(sel = cursel++, startEA>>4);
    else                    sel = (ushort)(startEA >> 4);
    if(!add_segm(sel, startEA, end, NULL, _cls[caller])) qexit(1);
    S = getseg(startEA);
    S->orgbase = -(uval_t)curSeg.id.Number;
    S->type = type;
    set_segm_name(S, fm[caller], curSeg.id.Number);

    if(caller > 1) for( ; startEA < top; startEA++) {
      doByte(startEA, 1);
      if(caller == 2) set_dummy_name(BADADDR, startEA); //data
    }
  }

  if((unsigned)caller > 1) doByte(top, end - top);
  startEA = end;
  return(S);
}

//-----------------------------------------------------------------------
static void SetName(ushort name, ea_t ea, ushort access, uval_t number)
{
  char str[MAXNAMELEN];

  init_output_buffer(str, sizeof(str));
  if(fmtString(name, sizeof(str) - 1, fmt_name, no_prmpt) < 0)
    if(!number) trunc_name(curSeg.id.Number, 3 + (curSeg.id.Number == 0));
    else if(number <= (uval_t)curClass.FieldCnt)
                trunc_name((ushort)number, 1);
         else   trunc_name((ushort)number - curClass.FieldCnt, 2);

  if(access & (ACC_PRIVATE | ACC_PROTECTED)) do_name_anyway(ea, str);
  else    add_entry((access & ACC_PUBLIC) ? number : ea, ea, str, 0);
  *addonce_name = '\0';

}

//-----------------------------------------------------------------------
static void xtrnRef(ea_t ea, register ConstOpis *opis)
{
  if((loadMode & 1) && opis->ref_ip)
    add_dref(ea, opis->ref_ip == 0xFFFF ? curClass.startEA :
                                      curClass.xtrnEA + opis->ref_ip, dr_I);
}

//-----------------------------------------------------------------------
static void xtrnRef_sign(ea_t ea, register ConstOpis *opis, int mode)
{
  if(mode >= 0) {
    if(!(loadMode & 2) || (opis->flag & HAS_CLASS_NAME)) return;
  } else if(!(loadMode & 4)) return;
  curConst = *opis;
  opis = &curConst;
  opis->ref_ip = xtrnSignSearch(opis->_name, mode);
  xtrnRef(ea, opis);
}

//-----------------------------------------------------------------------
static void LoadAttrib(ushort flag, int caller)
{

  static long savesize = -1;
  static char  diag0[] = "Field",
               diag1[] = "Method",
               diag2[] = "Code",
               diag3[] = "Additional Attributes",
              *diag[] = {diag0, diag1, diag2, diag3};
  char astr[40];
  ushort    i, k, r;
  ulong     dopsize;
  ConstOpis *opis;
  netnode   temp;
  segment_t   *S;
  ushort    cnt = read2();

  for(i = 0; i < cnt; i++) {
    if(savesize >= 0 && (savesize -= 6) < 0) {
recurserr:
      static char recr[] = "Illegal size of declaration";
      error(recr);
    }
    k = read2();
    dopsize = read4();
    if(savesize >= 0 && (savesize -= dopsize) < 0) goto recurserr;

    switch(attrStr(k, flag, &astr[1], sizeof(astr)-1)) {
      case 0xFFFF:
        {
          static char illi[] = "with index (%u)";
          qsnprintf(astr, sizeof(astr), illi, k);
        }
        goto skip;
      case 0xFFFE:
        OutLine("...");
      case 0xFFFD:
        OutChar(astr[0] = '"');
        *get_output_ptr() = '\0';
skip:
        {
          static char dgf[]  = "Attribute %s for %s is ignored\n";
          msg(dgf, astr, diag[caller]);
        }
        ++curClass.errload;
      default:                          //!!!???
skipAttr:
        if(dopsize) skipData((ushort)dopsize);
        continue;

      case a_ConstantValue:
        if(dopsize != 2 || (opis = LoadOpis(k = read2())) == NULL) {
declerr:
          static char dg2[] = "Illegal declaration of %s";
          error(dg2, &astr[1]);
        }

        if(!(temp = curField.valNode)) {
          temp.create();
          curField.valNode = temp;
          r = 0;
        } else r = (ushort)temp.altval(0);
        if((opis->type != CONSTANT_Integer && opis->type != CONSTANT_Long &&
           opis->type != CONSTANT_Float && opis->type != CONSTANT_Double &&
           opis->type != CONSTANT_String)) {

          static char ilv[] = "Illegal reference to value of field#%u\n";
          msg(ilv, curField.id.Number);
          QueueMark(Q_att, curClass.startEA + curField.id.Number);
          ++curClass.errload;
          temp.altset(++r, (k << 16) | 0xFFFF);
        } else temp.supset(++r, opis, sizeof(*opis));
        temp.altset(0, r);
        break;

      case a_Code:
        if(curSeg.CodeSize) {
duplerr:
          static char tmc[] = "Duplicate '%s' attribute declaration";
          error(tmc, &astr[1]);
        }
        k = (curClass.MinVers >= 3) ? 1 : 0;
        if(dopsize < (k ? 12 : 8)) goto declerr;        // !!!
        dopsize -= (k ? 12 : 8);                               // !!!
        curSeg.stacks = k ? read2() : read1();                 // !!!
        curSeg.DataSize = k ? read2() : read1();     //max_locals !!!
        if((curSeg.CodeSize = k ? read4() : read2()) != 0) {   // !!!
          if(dopsize < curSeg.CodeSize) goto declerr;
          dopsize -= curSeg.CodeSize;
          if(FileSize < curSeg.CodeSize) errtrunc();
          FileSize -= curSeg.CodeSize;
          S = _add_seg(-1);      // expand size
          if(curSeg.DataSize)
            SetDefaultRegisterValue(S, rVds, _add_seg(2)->sel); //dataSeg
        }

        if((k = read2()) != 0) {               //except. table
          ea_t ea = curSeg.startEA + curSeg.CodeSize;

          if((8ul * k) > dopsize) goto declerr;
          dopsize -= (8ul * k);
          temp.create();
          curSeg.excNode = temp;
          temp.altset(0, k);
          r = 1;
          do {
            Exception exc;

            exc.start_pc = read2();
            exc.end_pc = read2();
            exc.handler_pc = read2();

            if(exc.start_pc >= curSeg.CodeSize ||
               exc.end_pc > curSeg.CodeSize ||
               exc.handler_pc >= curSeg.CodeSize) {
              QueueMark(Q_noValid, ea);
              ++curClass.errload;
            }
            if((exc.catchInd = read2()) == 0) exc.catchName = 0;
            else if((opis = LoadOpis(exc.catchInd, CONSTANT_Class)) == NULL
                    || !(opis->flag & HAS_CLASS_NAME)) {
                   exc.catchName = 0xFFFF;  // for out
                   QueueMark(Q_att, ea);
                 } else {
                   exc.catchName = opis->_name;
                   xtrnRef(curSeg.startEA + curSeg.CodeSize, opis);
                 }
            temp.supset(r++, &exc, sizeof(exc));
            if(exc.start_pc < curSeg.CodeSize)
              add_dref(ea, curSeg.startEA + exc.start_pc, dr_O);
            if(exc.end_pc <= curSeg.CodeSize)
              add_dref(ea, curSeg.startEA + exc.end_pc, dr_O);
            if(exc.handler_pc < curSeg.CodeSize)
              add_dref(ea, curSeg.startEA + exc.handler_pc, dr_O);
          } while(r <= k);
        }

        savesize = dopsize;
        LoadAttrib(a_Code_ATTR, 2);  //Additional attr
        savesize = -1;
        break;

      case a_Exceptions:
        if(dopsize < 4) goto declerr;
        dopsize -= 2;
        if((dopsize % 2) || (dopsize /= 2) != (ulong)read2()) goto declerr;
        if(curSeg.thrNode) goto duplerr;
        temp.create();
        curSeg.thrNode = temp;
        r = 0;
        while(dopsize--) if((k = read2()) != 0) {
          ulong refd = k << 16;
          if((opis = LoadOpis(k, CONSTANT_Class)) == NULL ||
             !(opis->flag & HAS_CLASS_NAME)) {
            static char ilt[] =
                   "Illegal reference (%u) to 'throws'-class in method %u\n";
            msg(ilt, k, curSeg.id.Number);
            QueueMark(Q_att, curSeg.startEA);
            ++curClass.errload;
          } else {
            refd |= opis->_name;
            xtrnRef(curSeg.startEA, opis);
          }
          temp.altset(++r, refd);
        }
        temp.altset(0, r);
        break;

      case a_LineNumberTable:
        if(dopsize < 2) goto declerr;
        dopsize -= 2;
        if((dopsize % 4) || (dopsize /= 4) != (ulong)read2()) goto declerr;
        if(!dopsize) {
//Symantec error (strip) #1
          static char edc[]="Stripped declaration of LineNumber table\n";
          deb(IDA_DEBUG_IDP, edc);
        }
        while(dopsize--) {
          if((ulong)(k = read2()) >= curSeg.CodeSize) {
            static char lne[]= "Illegal address (%u) of source line %u\n";
            msg(lne, k, read2());
            ++curClass.errload;
          } else set_source_linnum(curSeg.startEA + k, read2());
        }
        break;

      case a_LocalVariableTable:
        if(dopsize < 2) goto declerr;
        if(!(loadMode & 0x40)) goto skipAttr;
        dopsize -= 2;
        if((dopsize % 10) || (dopsize /= 10) != (ulong)read2()) goto declerr;
        while(dopsize--) {
          ushort  o1, o2, i1, i2;
          o1 = read2();
          o2 = read2();
          i1 = read2();
          i2 = read2();
          k  = read2();    // slot
          if((!k && !i1 && !i2 && !o1 && o2 <= 1) || // symantec strip
              (short)o1 == -1) { // microsoft ERROR
//Symantec error (strip) #2
//Microsoft VisualJ++ error (purge?) #1
            static char strp[]="Stripped declaration of local variables";
            deb(IDA_DEBUG_IDP, strp);
          }
          else if(o1 >= curSeg.CodeSize ||
             (o1 + o2) > curSeg.CodeSize ||
//             ((~opis->flag) & (HAS_FM_NAME | HAS_SIGNATURE)) != 0 ||
             (opis = CheckFieldName(i1)) == NULL ||
             (opis = CheckFieldSign(i2)) == NULL ||
             k >= curSeg.DataSize) {
            static char illv[] =
              "Illegal declaration of local variable "
              "slot%03u, n&t=%u/%u, address %u(+%u)\n";
            msg(illv, k, i1, i2, o1, o2);
            ++curClass.errload;
          } else {
            xtrnRef_sign(curSeg.startEA + o1, opis, 1);
            locDecl(i1, i2, o1, o1 + o2, k);
          }
        }
        break;

      case a_SourceFile:
        if(dopsize != 2) goto declerr;
        if(!LoadOpis(k = read2(), CONSTANT_Utf8)) {
          static char isc[] = "Illegal reference (%u) to source file name\n";
          msg(isc, k);
          ++curClass.errload;
        }
        else curClass.SourceName = k;
        break;
    }  //switch
  }  // for

}

//-----------------------------------------------------------------------
//
// This function should read the input file (it is opened in binary mode)
// analyze it and:
//      - loading segment and offset are in inf.BaseAddr &
//      - load it into the database using file2base(),mem2base()
//        or allocate addresses by FlagsEnable() and fill them
//        with values using putByte(), putWord(), putLong()
//      - (if createsegs) create segments using
//          add_segm(segment_t *,const char *name,const char *sclass,int flags)
//        or
//          add_segm(unsigned short,ea_t,ea_t,char *,char *)
//        see segment.hpp for explanations
//      - set up inf.startIP,startCS to the starting address
//      - set up inf.minEA,inf.maxEA
//
//
void loader(FILE *fp, bool manual)
{
  static char illa[] = "Illegal access bits (0x%X)";
  char astr[60];
  ushort i, j, r;
  ConstOpis *opis;
  netnode   temp;

  memset(&curClass, 0, sizeof(curClass));
  eseek(fp, 0);   //rewind
  FileSize = efilelength(myFile = fp);


  if(read4() != MAGICNUMBER) {
    static char magnum[] = "Illegal magic number";
    error(magnum);
  }

  curClass.MinVers   = read2();
  if(   (i = read2()) != CURRENT_MAJOR_VERSION
     && (i != (CURRENT_MAJOR_VERSION+1) || curClass.MinVers)) {
    static char vers[] = "Unsupported file format (version %u.%u)";
    error(vers, i, curClass.MinVers);
  }
  if(curClass.MinVers & 0x8000) {
    error("Internal error: out of structures!");
  }
  if(i != CURRENT_MAJOR_VERSION) curClass.MinVers |= 0x8000;
  else if(curClass.MinVers < 2 || curClass.MinVers > 3) {
    static char ver[] =
       "ATTENTION! Class file format (version %u.%u) is not tested!";
    info(ver, i, curClass.MinVers & ~0x8000);
  }
  modifyNameChars(0);
//--
  if((curClass.maxCPindex = read2()) <= 2) {
    static char cps[] = "Empty constant pool!";
    error(cps);
  }
  loadMode = loadDialog(manual);
  ConstantNode.create(constant_pool);
  --curClass.maxCPindex;  // last valid number
  firstpass = 1; // witch internal xref
  temp.create();
  curClass.xtrnNode = temp;
  r = LoadPool();
  if(_add_seg(0)) curClass.xtrnCnt = r;
  else {
    temp.kill();
    curClass.xtrnNode = 0;
  }
//--
  if((curClass.AccessFlag = read2()) & ~ACC_FULL_MASK) {
    static char ia[] = "Illegal class access bits (%0x%X)\n";
    msg(ia, curClass.AccessFlag);
    ++curClass.errload;
  }
  if((opis= LoadOpis(curClass.ThisClass = read2(), CONSTANT_Class)) ==NULL ||
     !(opis->flag & HAS_CLASS_NAME)) {
    static char ccfi[] = "Illegal reference (%u) to 'this' class/n";
    msg(ccfi, curClass.ThisClass);
    ++curClass.errload;
    curClass.ThisName = curClass.ThisClass;
    curClass.ThisClass = 0;
  } else curClass.ThisName = opis->_name;
//--
  if(curClass.xtrnNode) setPoolReference();
  curClass.SuperClass = read2();
  i = read2();                    // interface counter
  if(FileSize < i * 2) errtrunc();
  qfseek(fp, i * 2, SEEK_CUR);
  curClass.FieldCnt = read2();
  qfseek(fp, -((sval_t)i * 2 + 2), SEEK_CUR);
  _add_seg(3);          // class segment
  if(curClass.ThisClass) {
    *addonce_name = '.';
    curSeg.id.Number = 0;
    SetName(curClass.ThisName, curClass.startEA, curClass.AccessFlag, 0);
  }

  if(curClass.SuperClass) {
    if((opis = LoadOpis(curClass.SuperClass, CONSTANT_Class)) == NULL ||
       !(opis->flag & HAS_CLASS_NAME)) {
      static char ccfs[] = "Illegal reference (%u) to 'super' class\n";
      msg(ccfs, curClass.SuperClass);
      ++curClass.errload;
      curClass.SuperName = curClass.SuperClass;
      curClass.SuperClass = 0xFFFF;
    } else {
      curClass.SuperName = opis->_name;
      xtrnRef(curClass.startEA, opis);
    }
  }
//--
  for(r = 0; i; i--) { //InterfaceCount
    if((j = read2()) == 0) {
      static char int0[] = "Interface index has value 0, ignored";
      msg(int0);
      ++curClass.errload;
    } else {
      uval_t refd = j << 16;
      if(!curClass.impNode) {
        temp.create();
        curClass.impNode = temp;
      }
      if((opis = LoadOpis(j, CONSTANT_Class)) == NULL ||
         !(opis->flag & HAS_CLASS_NAME)) {
        static char int1[] = "Illegal reference (%u) to interface %u\n";
        msg(int1, j, i);
        ++curClass.errload;
      } else {
        refd |= opis->_name;
        xtrnRef(curClass.startEA, opis);
      }
      temp.altset(++r, refd);
    }
  }
  if(r) temp.altset(0, r);
//---
  temp.create();
  curClass.ClassNode = temp;
  qfseek(fp, 2, SEEK_CUR);
  if(curClass.errload) {
    QueueMark(Q_att, curClass.startEA);          //\\- visible?
    if(curClass.AccessFlag & ~ACC_FULL_MASK) {
      qsnprintf(astr, sizeof(astr), illa, curClass.AccessFlag);
      set_cmt_if_none(curClass.startEA, astr, 0);
    }
  }
//---
  for(i = 1; i <= curClass.FieldCnt; i++) {
    memset(&curField, 0, sizeof(curField));
    curField.id.Number = i;
    if(((curField.id.access = read2()) & ~(FIELD_ACC_MASK | ACC_MASK_ONE))
       != 0 || ((j = (curField.id.access & ACC_MASK_ONE)) != 0 &&
                j != ACC_PUBLIC && j != ACC_PROTECTED && j != ACC_PRIVATE)) {
      static char accs[] = "Illegal Field#%u Attribute 0x%04x\n";
      msg(accs, i, curField.id.access);
      ++curClass.errload;
      curField.id.flag |= 4;
      QueueMark(Q_att, curClass.startEA + i);
      qsnprintf(astr, sizeof(astr), illa, curField.id.access);
      set_cmt_if_none(curClass.startEA + i, astr, 0);
    }

    if((opis = CheckFieldName(curField.id.name = read2())) == NULL ||
       !(opis->flag & HAS_FM_NAME))   curField.id.flag |= 1;
    if((opis = CheckFieldSign(curField.id.sign = read2())) == NULL ||
       !(opis->flag & HAS_SIGNATURE)) curField.id.flag |= 2;
    else xtrnRef_sign(curClass.startEA + curField.id.Number, opis, 1);
    if(curField.id.flag & 3) {
      static char fnm[] = "Illegal NameAndType of field %u\n";
      msg(fnm, i);
      ++curClass.errload;
      if((curField.id.flag & 3) == 3) ++curClass.errload;
    }
    LoadAttrib(a_Field_ATTR, 0);
    temp.supset(i, &curField, sizeof(curField));
    if(!(curField.id.flag & 1))
      SetName(curField.id.name, curClass.startEA + i, curField.id.access, i);
  }
//--
  curClass.MethodCnt = read2();
  for(i = 1; i <= curClass.MethodCnt; i++) {
    memset(&curSeg, 0, sizeof(curSeg));
    curSeg.id.Number = i;
    if(((curSeg.id.access = read2()) & ~(METHOD_ACC_MASK | ACC_MASK_ONE))
       != 0 || ((j = (curSeg.id.access & ACC_MASK_ONE)) != 0 &&
                j != ACC_PUBLIC && j != ACC_PROTECTED &&
                j != ACC_PRIVATE)) {
      static char accm[] = "Illegal Method#%u Attribute 0x%04x\n";
      msg(accm, i, curSeg.id.access);
      ++curClass.errload;
      qsnprintf(astr, sizeof(astr), illa, curSeg.id.access);
      curSeg.id.flag |= 4;
    }

    _add_seg(1);  //codeSeg create         // this for strnRef_sign
    if((opis = CheckFieldName(curSeg.id.name = read2())) == NULL ||
       !(opis->flag & HAS_FM_NAME))   curSeg.id.flag |= 1;
    if((opis = CheckCallSign(curSeg.id.sign = read2())) == NULL ||
       !(opis->flag & HAS_PARAMSIGN)) curSeg.id.flag |= 2;
    else xtrnRef_sign(curSeg.startEA, opis, -1);
    if(curSeg.id.flag & 3) {
      static char mnm[] = "Illegal NameAndType of method %u\n";
      msg(mnm, i);
      ++curClass.errload;
      if((curSeg.id.flag & 3) == 3) ++curClass.errload;
    }
    if(curSeg.id.flag & 4) {
      QueueMark(Q_att, curSeg.startEA);
      set_cmt_if_none(curSeg.startEA, astr, 0);
    }
    LoadAttrib(a_Method_ATTR, 1);
    temp.supset(-((uval_t)i), &curSeg, sizeof(curSeg));
  }
//--
  LoadAttrib(a_Additional_ATTR, 3); // Source File
  if(FileSize) {
    static char adi[] = "This file has extra information (pos=0x%lx)";
    warning(adi, qftell(fp));
  }
//---
  firstpass = 0;  // Not set references!
  CheckPoolReference(0);
  firstpass = 1;  // Normal mode
  ConstantNode.supset(0, &curClass, sizeof(curClass));   // load end!
  modifyNameChars(-1);
//--
  debugmode = 1; // full pass...
  for(i = 1; i <= curClass.MethodCnt; i++) {
    static char aut[] = "Analysing method %u...\n";
    ea_t  ea, end;

    msg(aut, i);
    curSeg = *(SegInfo *)temp.supval(-((sval_t)i));
    showAddr(ea = curSeg.startEA);
    if(!curSeg.CodeSize) doByte(end = curSeg.startEA, 0x10);
    else for(end=ea+curSeg.CodeSize;ea<end;ea+=j) if((j=ua_code(ea))==0) ++j;
    if(!(curSeg.id.flag & 1))
      SetName(curSeg.id.name, curSeg.startEA, curSeg.id.access,
                                              curClass.FieldCnt + i);
    add_func(curSeg.startEA, end + 1);
  }

  firstpass = debugmode = 0;  // all references setting...
  CheckPoolReference(1);
  if(curClass.errload) {
    static char nit[] = "There were warning message(s)!\n"
                    "\3 Please look at messages window or create a map file.";
    warning(nit);
  }

  ConstantNode.altset(-1, CURRENT_IDP_VERSION);
  create_filename_cmt();
}

//--------------------------------------------------------------------------


