unit datas;
interface
uses sysutils,windows,common,gname,proctab,schedule,menus,savefile;
{
  >* - this is the set of functions which looks after segments/sections    *
  >* blocks of code or data can be added to the database, where they are   *
  >* kept and ordered and can be interrogated. functions should be segment *
  >* level functions in here. the possibleentrycode function was put here  *
  >* on this basis rather than being a disasm function as it simply        *
  >* looks for likely entry code and adds scheduler items                  *
  >*                                                                       *
  >* NB segment address with segment=0 is treated as a special return      *
  >* value, and segments should not be created with this value             *
}
type
  segtype=(code0,code16,code32,data16,data32,uninitdata,debugdata,resourcedata);

//essentially the data-mapping to the file.
//divides file into code/data segments or objects
//each set of data is then referenced through these sorted headers
  pdsegitem=^tdsegitem;
  tdsegitem=packed record
    addr:lptr;
    size:dword;
    data:pchar;
    typ:segtype;
    nam:pchar;
  end;

  pdsegitemsave=^tdsegitemsave;
  tdsegitemsave=packed record
    addr:lptr;
    size:dword;
    fileoffset:dword;
    typ:segtype;
    isnam:bool;
  end;

  tdataseg=class(tslist)
  public
    constructor create;
    procedure addseg(loc:lptr;size:dword;dataptr:pchar; t:segtype;nam:pchar);
    function findseg(loc:lptr):pdsegitem;
    function beyondseg(loc:lptr):boolean;
    procedure nextseg(var loc:lptr);
    procedure lastseg(var loc:lptr);
    function datagetpos(loc:lptr):dword;
    function getlocpos(pos:dword):lptr;
    procedure possibleentrycode(loc:lptr);
    procedure segviewer;
    procedure segheader(loc:lptr);
    function savedb(sf:tsavefile;filebuff:pchar):boolean;
    function loaddb(sf:tsavefile;filebuff:pchar):boolean;
  end;

var
  dta:tdataseg;
  total_data_size :dword;
  current_data_pos:dword;

implementation
uses disasm;

// compare function                                                      *
// this is the compare function for the inherited list class             *
// it determines how segments are ordered - by start address             *//compare function to order in list
function dsegcfunc(i,j:listitem):integer;
begin
  if eq(pdsegitem(i).addr,pdsegitem(j).addr) then begin result:=0; exit; end;
  if gr(pdsegitem(i).addr,pdsegitem(j).addr) then begin result:=1; exit; end;
  result:=-1;
end;

// deletion function                                                     *
// this function is called for each list item (segment) and is used to   *
// delete the list items (dsegitem)                                      *
// - we delete any name which was created for the segment, and also any  *
//   data which was created for uninitdata types (we generate a memory   *
//   space for uninitdata for simplicity of analysis later)              *
procedure dsegdfunc(i:listitem);
begin
  if pdsegitem(i).nam<> nil then dispose(pdsegitem(i).nam);
  if pdsegitem(i).typ=uninitdata then dispose(pdsegitem(i).data);
  dispose(pdsegitem(i));
end;

// constructor function                                                  *
// - we set up the deletion and compare functions for the data segment   *
//   list - see list.cpp                                                 *
// - we reset the global variables used to track data size and position  *
constructor tdataseg.create;
begin
  inherited create;
  setcomparefunc(dsegcfunc);
  setdelfunc(dsegdfunc);
  total_data_size :=0;
  current_data_pos:=0;
end;

// addseg                                                                *
// this adds a segment to the list of segments. It is called on loading  *
// the file, and segments should be setup before any analysis - here a   *
// segment is just a block of data of a single attribute, which will be  *
// referenced later. It could be a true segment, or just a PE section,   *
// or even just a single resource. Segments should not overlap           *
// the function checks for any overlaps, adds a task to the scheduler    *
// for a segment header comment block, keeps track of total data size    *
procedure tdataseg.addseg(loc:lptr;size:dword;dataptr:pchar;t:segtype;nam:pchar);
var
  addit:pdsegitem;
  chker:pdsegitem;
  tmpnum,tsize:dword;
  warning:string;
begin
  addit:=new(pdsegitem);
  addit.addr.s:=loc.s;
  addit.addr.o:=loc.o;
  addit.size:=size;
  addit.data:=dataptr;
  addit.typ:=t;
  if nam<>nil then begin
    getmem(addit.nam,strlen(nam)+1);
    strcopy(addit.nam,nam);
  end else addit.nam:=nil;
  resetiterator;
  chker:=pdsegitem(nextiterator);
  while chker<>nil do begin
    if (chker.addr.s=addit.addr.s)and(chker.addr.o>=addit.addr.o)
      and(chker.addr.o<addit.addr.o+addit.size)
    then begin
      //need to cut addit short.
      addit.size:=chker.addr.o-addit.addr.o;
      if addit.size=0 then begin
        tmpnum:=loc.s;
        Fmtstr(warning,'Warning : Unable to create segment %4x:%4x size :%4x',
         [tmpnum,loc.o,size]);
        MessageBox(mainwindow,pchar(warning),'Borg Warning',MB_ICONEXCLAMATION or MB_OK);
        exit;
      end else begin
        tmpnum:=loc.s;
        Fmtstr(warning,'Warning : Segment overlap %4x:%4x size :%4x reduced to size :%4x',
         [tmpnum,loc.o,size,addit.size]);
        MessageBox(mainwindow,pchar(warning),'Borg Warning',MB_ICONEXCLAMATION or MB_OK);
      end;
    end;
    if (addit.addr.s=chker.addr.s)and(addit.addr.o>=chker.addr.o)
      and(addit.addr.o<(chker.addr.o+chker.size))
    then begin
      //need to cut chkit short.
      tsize:=chker.size;
      chker.size:=addit.addr.o-chker.addr.o;
      if chker.size=0 then begin
        tmpnum:=chker.addr.s;
        fmtstr(warning,'Warning : Unable to create segment %4x:%4x size :%4x',
         [tmpnum,chker.addr.o,tsize]);
        MessageBox(mainwindow,pchar(warning),'Borg Warning',MB_ICONEXCLAMATION or MB_OK);
        delfrom(listitem(chker));
        resetiterator;
      end else begin
        tmpnum:=chker.addr.s;
        fmtstr(warning,'Warning : Segment overlap %4x:%4x size :%4x reduced to size :%4x',
         [tmpnum,chker.addr.o,tsize,chker.size]);
        MessageBox(mainwindow,pchar(warning),'Borg Warning',MB_ICONEXCLAMATION or MB_OK);
      end;
    end;
    chker:=pdsegitem(nextiterator);
  end;
  addto(listitem(addit));
  scheduler.addtask(dis_segheader,priority_segheader,loc,0,nil);
  inc(total_data_size,size);
end;

// findseg                                                               *
// - the segment locator takes a loc and searches for the segment        *
//   containing the loc. If its found then it returns a pointer to the   *
//   dsegitem. Note the iterator is changed, and can be left pointing to *
//   the next segment, or the found one. So a call to nextiterator could *
//   return the same segment, but might return the next                  *
function tdataseg.findseg(loc:lptr):pdsegitem;
var
  t1:tdsegitem;
  findd:pdsegitem;
begin
  result:=nil;
  t1.addr.s:=loc.s;
  t1.addr.o:=0;
  findd:=pdsegitem(find(listitem(@t1)));
  while findd<>nil do begin
    if (findd.addr.s=loc.s)and(loc.o<findd.addr.o+findd.size)and(loc.o>=findd.addr.o)
    then begin result:=findd; exit; end;
    findd:=pdsegitem(nextiterator);
  end;
end;

// datagetpos                                                            *
// - this simply calculates how far along the total data a loc is, which *
//   is used for the vertical scroll bar to determine how far down it    *
//   should be.                                                          *
function tdataseg.datagetpos(loc:lptr):dword;
var
  findd:pdsegitem;
  ctr:dword;
begin
  resetiterator;
  findd:=pdsegitem(nextiterator);
  ctr:=0;
  while findd<>nil do begin
    if (findd.addr.s=loc.s)and(loc.o>=findd.addr.o)and(loc.o<findd.addr.o+findd.size)
    then begin
      inc(ctr,(loc.o-findd.addr.o)); break;
    end;
    inc(ctr,findd.size);
    findd:=pdsegitem(nextiterator);
  end;
  result:=ctr;
end;

// getlocpos                                                             *
// - for a data position this returns the loc, treating all segments as  *
//   continous. It is the opposite function to datagetpos. It is used    *
//   when the vertical scroll bar is dragged to a position and stopped,  *
//   in order to calculate the new loc.                                  *
function tdataseg.getlocpos(pos:dword):lptr;
var
  findd:pdsegitem;
  ctr:lptr;
begin
   resetiterator;
   findd:=pdsegitem(nextiterator);
   ctr:=findd.addr;
   while findd<>nil do begin
     if pos<findd.size then begin
        ctr.o:=findd.addr.o+pos; break;
     end;
     dec(pos,findd.size);
     ctr:=findd.addr;
     if findd.size<>0 then inc(ctr.o,(findd.size-1)); // last byte in seg in case we dont find the addr
     findd:=pdsegitem(nextiterator);
   end;
   result:=ctr;
end;

// beyondseg                                                             *
// - returns a BOOL value indicating whether the loc is outside a        *
//   segment. Used a lot to determine if we have moved outside a data    *
//   area. If two segments have the same segm value and are contiguous   *
//   then it will return false, not true                                 *
function tdataseg.beyondseg(loc:lptr):boolean;
var
  findd:pdsegitem;
begin
  resetiterator;
  findd:=pdsegitem(nextiterator);
  while findd<>nil do begin
    if (findd.addr.s=loc.s)and(loc.o>=findd.addr.o)and(loc.o<findd.addr.o+findd.size)
    then begin result:=FALSE; exit; end;
    findd:=pdsegitem(nextiterator);
  end;
  result:=TRUE;
end;

// nextseg                                                               *
// - this function takes a loc, and returns the next segment first loc   *
//   or a segm=0 if its not found                                        *
// mainly used in output and display routines                            *
procedure tdataseg.nextseg(var loc:lptr);
var findd:pdsegitem;
begin
  resetiterator;
  findd:=pdsegitem(nextiterator);
  while findd<>nil do begin
    if (findd.addr.s=loc.s)and(loc.o>findd.addr.o)and(loc.o<=findd.addr.o+findd.size+1)
    then begin
      findd:=pdsegitem(nextiterator);
      if findd=nil then loc.s:=0 else loc:=findd.addr;
      exit;
    end;
    findd:=pdsegitem(nextiterator);
  end;
end;

// lastseg                                                               *
// - this function takes a loc, and returns the last segment last loc    *
//   or a segm=0 if its not found                                        *
// mainly used in output and display routines                            *
procedure tdataseg.lastseg(var loc:lptr);
var
  findd:pdsegitem;
  prevd:pdsegitem;
begin
  resetiterator;
  findd:=pdsegitem(nextiterator);
  prevd:=nil;
  while findd<>nil do begin
    if (findd.addr.s=loc.s)and(loc.o+1>=findd.addr.o)and(loc.o+1<findd.addr.o+findd.size)
    then begin if prevd=nil then loc.s:=0
      else begin
        loc.o:=prevd.addr.o+prevd.size-1;
      end; exit;
    end;
    prevd:=findd;
    findd:=pdsegitem(nextiterator);
  end;
end;

// possibleentrycode                                                     *
// - this just scans a whole segment for possible routine entrycode, as  *
// defined by options, and adds scheduler items for proper analysis by   *
// disasm. currently all specific to the 80x86 processor                 *
procedure tdataseg.possibleentrycode(loc:lptr);
var
  t1:tdsegitem;
  findd:pdsegitem;
  length:dword;
begin
  if options.processor=PROC_Z80 then exit;
  t1.addr:=loc;
  findd:=pdsegitem(find(listitem(@t1)));
  if findd=nil then exit;
  length:=findd.size;
  loc.o:=findd.addr.o;
  if options.codedetect and CD_AGGRESSIVE<>0 then
    scheduler.addtask(seek_code,priority_aggressivesearch,loc,0,nil);
  while length<>0 do begin
    if (options.codedetect and CD_PUSHBP<>0)and(length>3) then begin
      if findd.data[loc.o-findd.addr.o]=#$55 then begin // push bp
        // two encodings of mov bp,sp
        if (findd.data[(loc.o-findd.addr.o)+1]=#$8b)and(findd.data[(loc.o-findd.addr.o)+2]=#$ec)
        then scheduler.addtask(dis_code,priority_possiblecode,loc,0,nil);
        if (findd.data[(loc.o-findd.addr.o)+1]=#$89)and(findd.data[(loc.o-findd.addr.o)+2]=#$e5)
        then scheduler.addtask(dis_code,priority_possiblecode,loc,0,nil);
      end;
    end;
    if (options.codedetect and CD_EAXFROMESP<>0)and(length>4) then begin
      if findd.data[loc.o-findd.addr.o]=#$55 then begin  // push bp
        if (findd.data[(loc.o-findd.addr.o)+1]=#$8b)
          and(findd.data[(loc.o-findd.addr.o)+2]=#$44)
          and(findd.data[(loc.o-findd.addr.o)+3]=#$24) // mov ax,[sp+xx]
        then scheduler.addtask(dis_code,priority_possiblecode,loc,0,nil);
       end;
    end;
    if (options.codedetect and CD_MOVEAX<>0)and(length>3) then begin
      if (findd.data[loc.o-findd.addr.o]=#$8b)
        and(findd.data[(loc.o-findd.addr.o)+1]=#$44)
        and(findd.data[(loc.o-findd.addr.o)+2]=#$24) // mov ax,[sp+xx]
      then scheduler.addtask(dis_code,priority_possiblecode,loc,0,nil);
    end;
    if (options.codedetect and CD_ENTER<>0)and(length>4) then begin
      if findd.data[loc.o-findd.addr.o]=#$c8 then begin // enter
        if(findd.data[(loc.o-findd.addr.o)+3]=#$00) // enter xx,00
        then scheduler.addtask(dis_code,priority_possiblecode,loc,0,nil);
       end;
    end;
    if (options.codedetect and CD_MOVBX<>0)and(length>2) then begin // mov bx,sp
      if (findd.data[loc.o-findd.addr.o]=#$8b)
        and(findd.data[(loc.o-findd.addr.o)+1]=#$dc)
      then scheduler.addtask(dis_code,priority_possiblecode,loc,0,nil);
    end;
    inc(loc.o);
    dec(length);
  end;
end;

// segheader                                                             *
// - here we add the segment header as a comment, to the disassembly     *
//  disasm adds the comment, we assemble the information here.           *
procedure tdataseg.segheader(loc:lptr);
var
  tmpc:pchar;
  tmpcs:string;
  t1:tdsegitem;
  findd:pdsegitem;
begin
  t1.addr:=loc;
  findd:=pdsegitem(find(listitem(@t1)));
  if findd=nil then exit;
  getmem(tmpc,80);
  strcopy(tmpc,'-----------------------------------------------------------------------');
  dsm.discomment(loc,dsmsegheader,tmpc);
  getmem(tmpc,80);
  fmtstr(tmpcs,'Segment : %2xh     Offset : %2xh     Size : %2xh',
   [loc.s,loc.o,findd.size]);
  strpcopy(tmpc,tmpcs);
  dsm.discomment(loc,succ(dsmsegheader),tmpc);
  getmem(tmpc,80);
  case findd.typ of
   code16:      strcopy(tmpc,'16-bit Code');
   code32:      strcopy(tmpc,'32-bit Code');
   data16:      strcopy(tmpc,'16-bit Data');
   data32:      strcopy(tmpc,'32-bit Data');
   uninitdata:  strcopy(tmpc,'Uninit Data');
   debugdata:   strcopy(tmpc,'Debug Data');
   resourcedata:
    begin
      strcopy(tmpc,'Resource Data ');
    end;
   else strcopy(tmpc,'Unknown');
  end;
  if findd.nam<>nil then begin
    strcat(tmpc,' : '); strmove(@tmpc[strlen(tmpc)],findd.nam,60);
  end;
  dsm.discomment(loc,succ(succ(dsmsegheader)),tmpc);
  getmem(tmpc,80);
  strcopy(tmpc,'-----------------------------------------------------------------------');
  dsm.discomment(loc,succ(succ(succ(dsmsegheader))),tmpc);
end;

// segbox                                                                *
// - this is the segment viewer dialog box which shows the segments, and *
//   details of them as they are clicked on. It allows jumping to the    *
//   segments as well. Background analysis is halted when calling this   *
//   particularly because iterators are used, and they there is only one *
//   iterator for the segment list                                       *
function segbox(hdwnd:HWND; msg,wParam,lParam:longint):longint;stdcall;
type
  psegtarr=^tsegtarr;
  tsegtarr=array[0..1000] of pchar;
const
  brush:hbrush=0;
  segt :psegtarr=nil;
  sseg  :string='';
  sstart:string='';
  send  :string='';
  ssize :string='';
  stype :string='';
  a:array[0..300] of char=#0;
  t:pdsegitem=nil;
var
  i:integer;
  drect:trect;
begin
  result:=0;
  case msg of
   WM_COMMAND:
    begin
      case wParam of
       IDC_OKBUTTON:
        begin
          EndDialog(hdwnd,0);
          result:=1; exit;
        end;
       IDC_JUMPTOBUTTON:
        begin
          scheduler.addtask(user_jumptoaddr,priority_userrequest,t.addr,0,nil);
          EndDialog(hdwnd,0);
          result:=1; exit;
        end;
      end;
      case HIWORD(wParam) of
       LBN_SELCHANGE:
        begin
          i:=SendDlgItemMessage(hdwnd,IDC_SEGLISTBOX,LB_GETCURSEL,0,0);
          dta.resetiterator;
          t:=pdsegitem(dta.nextiterator);
          while i<>0 do begin
            t:=pdsegitem(dta.nextiterator); dec(i);
          end;
          fmtstr(sstart,'0x%8.8x',[t.addr.o]);
          fmtstr(send,'0x%8.8x',[t.addr.o+t.size-1]);
          fmtstr(ssize,'0x%8.8x',[t.size]);
          case t.typ of
           code16:       stype:='16-bit Code';
           code32:       stype:='32-bit Code';
           data16:       stype:='16-bit Data';
           data32:       stype:='32-bit Data';
           uninitdata:   stype:='Uninit Data';
           debugdata:    stype:='Debug Data';
           resourcedata: stype:='Resource Data';
           else          stype:='Unknown';
          end;
          SendDlgItemMessage(hdwnd,SEG_TEXTSTART,WM_SETTEXT,0,dword(pchar(sstart)));
          SendDlgItemMessage(hdwnd,SEG_TEXTEND,WM_SETTEXT,0,dword(pchar(send)));
          SendDlgItemMessage(hdwnd,SEG_TEXTSIZE,WM_SETTEXT,0,dword(pchar(ssize)));
          SendDlgItemMessage(hdwnd,SEG_TEXTTYPE,WM_SETTEXT,0,dword(pchar(stype)));
          SendDlgItemMessage(hdwnd,IDC_SEGNAMETEXT,WM_SETTEXT,0,dword(@t.nam^));
        end;
      end;
    end;
   WM_INITDIALOG:
    begin
      GetWindowRect(hdwnd,drect);
      MoveWindow(hdwnd,((mainwnd.right+mainwnd.left)-(drect.right-drect.left)) div 2,
        ((mainwnd.bottom+mainwnd.top)-(drect.bottom-drect.top)) div 2,drect.right-drect.left,drect.bottom-drect.top,TRUE);
      getmem(segt,4*dta.numlistitems+1);
      dta.resetiterator;
      for i:=0 to dta.numlistitems-1 do begin
        getmem(segt^[i],20); fillchar(segt^[i]^,20,0);
        t:=pdsegitem(dta.nextiterator);
        fmtstr(sseg,'0x%4.4x:0x%8.8x',[t.addr.s,t.addr.o]);
        strpcopy(segt^[i],sseg);
        SendDlgItemMessage(hdwnd,IDC_SEGLISTBOX,LB_ADDSTRING,0,dword(@segt^[i]^));
      end;
      SendDlgItemMessage(hdwnd,IDC_SEGLISTBOX,LB_SETCURSEL,0,0);
      dta.resetiterator;
      t:=pdsegitem(dta.nextiterator);
      fmtstr(sstart,'0x%8.8x',[t.addr.o]);
      fmtstr(send,'0x%8.8x',[t.addr.o+t.size-1]);
      fmtstr(ssize,'0x%8.8x',[t.size]);
      case t.typ of
       code16:      stype:='16-bit Code';
       code32:      stype:='32-bit Code';
       data16:      stype:='16-bit Data';
       data32:      stype:='32-bit Data';
       uninitdata:  stype:='Uninit Data';
       debugdata:   stype:='Debug Data';
       resourcedata:stype:='Resource Data';
       else         stype:='Unknown';
      end;
      SendDlgItemMessage(hdwnd,SEG_TEXTSTART,WM_SETTEXT,0,dword(pchar(sstart)));
      SendDlgItemMessage(hdwnd,SEG_TEXTEND,WM_SETTEXT,0,dword(pchar(send)));
      SendDlgItemMessage(hdwnd,SEG_TEXTSIZE,WM_SETTEXT,0,dword(pchar(ssize)));
      SendDlgItemMessage(hdwnd,SEG_TEXTTYPE,WM_SETTEXT,0,dword(pchar(stype)));
      SendDlgItemMessage(hdwnd,IDC_SEGNAMETEXT,WM_SETTEXT,0,dword(@t.nam^));
      brush:=CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
      SetFocus(GetDlgItem(hdwnd,IDC_OKBUTTON));
      exit;
    end;
   WM_DESTROY:
    begin
      for i:=0 to dta.numlistitems-1 do begin
        SendDlgItemMessage(hdwnd,IDC_SEGLISTBOX,LB_DELETESTRING,dta.numlistitems-i-1,0);
        freemem(segt^[dta.numlistitems-i-1]);
      end;
      freemem(segt);
      DeleteObject(brush);
      exit;
    end;
  WM_CTLCOLORSTATIC,
  WM_CTLCOLORBTN,
  WM_CTLCOLORDLG,
  WM_SYSCOLORCHANGE:
   begin
     SetBkColor(HDC(wParam),GetSysColor(COLOR_BTNFACE));
     result:=brush;
     result:=0;
   end;
  end;
end;

// segviewer                                                             *
// - stops the secondary thread, and calls the dialog box for viewing    *
//   the segments, then restarts the thread when the dialog box is done. *
procedure tdataseg.segviewer;
begin
  scheduler.stopthread;
  DialogBox(Inst,MAKEINTRESOURCE(Seg_Viewer),mainwindow,@segbox);
  scheduler.continuethread;
end;

// savedb                                                                *
// - we save the segments to the database file. saving in borg is single *
// pass and so stringlengths are saved before strings, etc. we use a     *
// similar structure item for the save (dsegitem - dsegitemsave). As the *
// data item is a pointer it is converted into an offset for saving.     *
function tdataseg.savedb(sf:tsavefile;filebuff:pchar):boolean;
var
  nsegs,nlen:dword;
  structsave:tdsegitemsave;
  currseg:pdsegitem;
begin
  result:=false;
  nsegs:=numlistitems;
  resetiterator;
  writeln(log,'pos=',inttohex(numwriten,8));
  writeln(log,'segs=',nsegs);
  if not sf.swrite(@nsegs,sizeof(dword)) then exit;
  while nsegs<>0 do begin
    currseg:=pdsegitem(nextiterator);
    structsave.addr:=currseg.addr;
    structsave.size:=currseg.size;
    structsave.fileoffset:=currseg.data-filebuff;
    structsave.typ:=currseg.typ;
    if currseg.nam<>nil then structsave.isnam:=TRUE else structsave.isnam:=FALSE;
    with structsave do write(log,format(' %4.4x:%8.8x %8.8x',[addr.s,addr.o,size]));
    with structsave do write(log,format(' %8.8x %d',[fileoffset,word(typ)]));
    dobool(structsave.isnam);
    if not sf.swrite(@structsave,sizeof(tdsegitemsave)) then begin result:=FALSE; exit; end;
    if structsave.isnam<>false then begin
      nlen:=strlen(currseg.nam)+1;
      if not sf.swrite(@nlen,sizeof(dword)) then exit;
      write(log,format(' %d nam=%s',[nlen,currseg.nam]));
      if not sf.swrite(currseg.nam,nlen) then exit;
    end;
    writeln(log);
    dec(nsegs);
  end;
  result:=TRUE;
end;

// loaddb                                                                *
// - loads the segments in, matching the savedb routine. Pointers are    *
//   reconstructed (for data) and segments added to the analysis. Note   *
//   that addseg is not used here, since we have all the information and *
//   just need to reconstruct the dsegitem. Hence we also need to        *
//   recalculate total_data_size in here. Also, for uninit data segments *
//   we add a false data area.                                           *
function tdataseg.loaddb(sf:tsavefile;filebuff:pchar):boolean;
var
  num:integer;
  nsegs,nlen:dword;
  structsave:tdsegitemsave;
  currseg:pdsegitem;
begin
  result:=FALSE;
  if not sf.sread(@nsegs,sizeof(integer),num) then exit;
  while nsegs<>0 do begin
    currseg:=new(pdsegitem);
    if not sf.sread(@structsave,sizeof(tdsegitemsave),num) then exit;
    currseg.addr:=structsave.addr;
    currseg.size:=structsave.size;
    currseg.data:=structsave.fileoffset+filebuff;
    currseg.typ:=segtype(structsave.typ);
    inc(total_data_size,currseg.size);
    if structsave.isnam then begin
      if not sf.sread(@nlen,sizeof(dword),num) then exit;
      getmem(currseg.nam,nlen);
      if not sf.sread(currseg.nam,nlen,num) then exit;
    end else currseg.nam:=nil;
    if currseg.typ=uninitdata then begin
      getmem(currseg.data,currseg.size);
      for nlen:=0 to currseg.size-1 do currseg.data[nlen]:=#0;
    end;
    addto(listitem(currseg));
    dec(nsegs);
  end;
  result:=TRUE;
end;

initialization
  dta:=tdataseg.create;
finalization
  dta.free;
end.

