/*
 *              Class "Virtual Array of Longs"
 *
 *              Written by Ilfak Guilfanov, 1991
 *              Latest modifications at 1997
 *              Copyright (c), All Rights Reserved.
 */

#ifndef VARRAY_HPP
#define VARRAY_HPP
#pragma pack(push, 1)   // IDA uses 1 byte alignments!

#include <pro.h>
#include <vm.hpp>

implementVM(ulong);

// internal classes
struct vaptr_t                          // Chunk definition
{
  unsigned long start;                  // Start address of the chunk
  unsigned long end;                    // End address of the chunk (not included)
  unsigned long offset;                 // Offset in the file
                                        // Offsets should be in increasing order
  void correct_offset(int pagesize)
  {
    int psize = pagesize / sizeof(ulong);
    offset = offset - offset%pagesize + (start%psize)*sizeof(ulong);
  }
  inline unsigned long size(void) const { return end - start; }
};


#define VA_MAGIC "Va0"

struct vaheader_t                       // First bytes of VA file (header)
{
  char magic[4];                        // Must be VA_MAGIC
  ushort nchunks;                       // Number of chunks
  ushort eof;                           // Number of last used page + 1
};


// Main class declared in the file:
class Varray
{
public:

        Varray(void)    { Pages = NULL; }
        ~Varray(void)   { vclose(); }

        int     linkTo  (const char *file,int Psize,int PoolSize);
                                        // Psize - how many longs
                                        // PoolSize - how many pages in the cache
                                        // if file doesn't exist, it will be
                                        // created
        void    vclose   (void);
//
//      Address space functions
//
        int     enable  (ulong start,ulong end);        // 0-ok,else error
        int     disable (ulong start,ulong end);        // 0-ok,else error
        bool    enabled (const ulong addr)              // is enabled ?
                                { return getoff(addr) != 0; }
        ulong nextaddr  (ulong addr);   // get next enabled addr
                                        // if not exist, returns -1
        ulong prevaddr  (ulong addr);   // get prev enabled addr
                                        // if not exist, returns -1
        ulong prevchunk (ulong addr);   // return prev chunk last addr
                                        // if not exist, returns -1
        ulong nextchunk (ulong addr);   // return next chunk first addr
                                        // if not exist, returns -1
        ulong size      (ulong addr);   // return size of the chunk
                                        // addr may be any address in the chunk
        ulong getstart  (ulong addr);   // get start address of the chunk
                                        // if illegal address given, returns -1
        int movechunk  (ulong from, ulong to, ulong size);
                             // move information from one address to another. returns VAMOVE_...
#define VAMOVE_OK        0   // all ok
#define VAMOVE_BADFROM   -1  // the from address is not enabled
#define VAMOVE_TOOBIG    -2  // the range to move is too big
#define VAMOVE_OVERLAP   -3  // the target address range is already occupied
#define VAMOVE_TOOMANY   -4  // too many chunks are defined, can't move
        int check_move_args(ulong from, ulong to, ulong size); // returns VAMOVE_...

//         
//      Read/Write functions
//
        long    vread   (ulong num)     { return *Raddr(num); }
        void    vwrite  (ulong num,long val) { *Waddr(num)  =  val; }
        void    setbits (ulong num,long bit) { *Waddr(num) |=  bit; }
        void    clrbits (ulong num,long bit) { *Waddr(num) &= ~bit; }
        ulong*  Waddr   (ulong num);    // return address for the 'num', mark page as dirty
        ulong*  Raddr   (ulong num);    // return address for the 'num'
        void    vflush  (void)          { Pages->vflush(); }

        void    memset  (ulong start,ulong size,ulong x);
        void    memcpy  (ulong start,ulong size,Varray &src,ulong srcstart);
        ulong memcmp(ulong start,ulong size,Varray &v2,ulong v2start);
                                                // returns -1 - if equal
                                                // else address where mismatches
        ulong memscn (ulong start,ulong size,ulong x);
        ulong memtst (ulong start,ulong size,bool (idaapi*test)(ulong));
        ulong memtstr(ulong start,ulong size,bool (idaapi*test)(ulong));

        ulong  *vread    (ulong start,      ulong *buffer,ulong size);
        void    vwrite   (ulong start,const ulong *buffer,ulong size);

        void    shright (ulong from);           // Shift right tail of array
        void    shleft  (ulong from);           // Shift left  tail of array

//
//      Sorted array functions
//
        ulong bsearch(ulong ea);        // Binary search
                                        // Returns index to >= ea
                                        // Attention: this func may return
                                        // pointer past array !
        bool addsorted(ulong ea);// Add an element to a sorted array.
                                        // If element exists, return 0
                                        // else 1
        bool delsorted(ulong ea);// Del element from a sorted array
                                        // If doesn't exist, return 0
                                        // else return 1

        void vadump(const char *msg, bool ea_sort_order); // debugging
        char *check(bool ea_sort_order); // check internal consistency

private:
  declareVM(ulong) *Pages;
  vaheader_t *header;                   // Header page
  ulong lastnum;                        // Last accessed address
  vaptr_t *lastvp;                      //  vptr of it
  ulong lastoff;                        //  offset of it
  ushort lastPage;
  ulong *lPage;
  int psize;                            // how many items can be put into a page
  int pagesize;                         // page size in bytes

//
// scan the Varray forward. call 'perform' for each page. If perform()
// returns >=0, then this is an index in the page, return address with this index.
// if perform() < 0, continue...
// Returns the address calculated by the index returned by perform().
//      perform() args: page - pointer to the page
//                      s    - size of the page
// 'change' - will perform() change the pages
//
  ulong vascan(ulong start,ulong size,int (idaapi*perform)(ulong *page,int s),bool change);
  ulong vascanr(ulong start,ulong size,int (idaapi*perform)(ulong *page,int s),bool change);

  ulong getoff(ulong addr);
  int shiftPages(ulong offset, int op, int np, bool changecur);
  void move_vm_chunk(int p1, int p2, int n);
  void swap(int x1, int x2, int n);
  vaptr_t *split(vaptr_t *cvp, ulong from, ulong end);
  void split_page(int kp, ulong from);
  void merge_if_necessary(vaptr_t *vp);

  int add_pages(ulong offset, int npages, bool changecur)
    { return shiftPages(offset, 0, npages, changecur); }
  int del_pages(ulong offset, int npages, bool changecur)
    { return shiftPages(offset, npages, 0, changecur); }

  vaptr_t *getv0(void) const { return (vaptr_t *)(header + 1); }
  int getidx(const vaptr_t *vp) const { return vp - getv0(); }
  bool is_first(const vaptr_t *vp) const { return getidx(vp) == 0; }
  bool is_last(const vaptr_t *vp) const;
};

#pragma pack(pop)
#endif // VARRAY_HPP
