unit search;
interface
uses sysutils,windows,common,menus,schedule,datas,disasm,disio;
{
  >* These are the functions which handle searching. I still have more     *
  >* work to do on searching (particularly regarding strings - unicode,    *
  >* etc), but the basic functionality is there. Other ideas for the       *
  >* future include wildcard searching. (Byte wildcard searching would be  *
  >* nice :))                                                              *
}
type
  search_type=(search_0,SEARCH_STR_,SEARCH_HEX_,SEARCH_DEC_,SEARCH_BYTES_);

const
  oldsearchtext:array[0..200] of char='';
  lastsearchtype:search_type=SEARCH_STR_;
  lastfromstart:boolean=FALSE;

  procedure searchmore;
  procedure searchengine;

implementation

// searchingbox                                                          *
// - this is simply a small dialog box with only the text message        *
//   'searching' which is displayed while the search takes place. Note   *
//   that while a search is being done we are in the primary thread and  *
//   that the secondary thread is stopped, so nothing else happens       *
//   within Borg at all.                                                 *
function searchingbox(hdwnd,msg,wParam,lParam:dword):dword;stdcall;
const
  brush:HBRUSH=0;
  drect:tRECT=();
begin
  result:=0;
  case msg of
   WM_INITDIALOG:
    begin
      brush:=CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
      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);
      exit;
    end;
   WM_DESTROY:
    begin
      DeleteObject(brush); exit;
    end;
   WM_CTLCOLORSTATIC,
   WM_CTLCOLORDLG,
   WM_SYSCOLORCHANGE:
    begin
      SetBkColor(HDC(wParam),GetSysColor(COLOR_BTNFACE));
      result:=brush;
    end;
  end;
end;

// searchbox                                                             *
// - this is the main search dialog box.                                 *
// - it performs the search when we press ok, and the state of controls  *
//   is saved to global variables                                        *
function searchbox(hdwnd,msg,wParam,lParam:dword):dword;stdcall;
const brush:HBRUSH=0;
var
  searchtype:search_type;
  srchtext:array[0..200] of char;
  match:array[0..200] of byte;
  srchseg:pdsegitem;
  i,matchlen:integer;
  tmpbyte:byte;
  segmtch:pchar;
  mtch:dword;
  s_seg,ssold:lptr;
  drect:tRECT;
  found:boolean;
  sbox:hwnd;
  fromstart:boolean;  // added plus code, bug fix build 14
begin
  result:=0;
  case msg of
   WM_COMMAND:
    case wParam of
     IDOK:
      begin
        if SendDlgItemMessage(hdwnd,search_string,BM_GETCHECK,0,0)<>0 then
          searchtype:=SEARCH_STR_
        else if SendDlgItemMessage(hdwnd,search_hex,BM_GETCHECK,0,0)<>0 then
          searchtype:=SEARCH_HEX_
        else if SendDlgItemMessage(hdwnd,search_decimal,BM_GETCHECK,0,0)<>0 then
          searchtype:=SEARCH_DEC_
        else searchtype:=SEARCH_BYTES_;
        if SendDlgItemMessage(hdwnd,search_fromstart,BM_GETCHECK,0,0)<>0 then begin
          s_seg:=options.loadaddr;
          fromstart:=TRUE;
        end else begin
          dio.findcurrentaddr(s_seg);
          inc(s_seg.o,dsm.getlength(s_seg));
          fromstart:=FALSE;
        end;
        sbox:=CreateDialog(Inst,MAKEINTRESOURCE(S_Box),hdwnd,@searchingbox);
        // get string,
        SendDlgItemMessage(hdwnd,search_edit,WM_GETTEXT,18,dword(@srchtext));
        strcopy(oldsearchtext,srchtext);
        matchlen:=0;
        case searchtype of
         SEARCH_STR_:
          begin
            matchlen:=strlen(srchtext);
            strcopy(@match,srchtext);
          end;
         SEARCH_HEX_:
          begin
            mtch:=strtoint('$'+strpas(srchtext));
            if mtch<256 then begin
              matchlen:=1;
              match[0]:=mtch;
            end else if mtch<65536 then begin
              matchlen:=2;
              match[0]:=mtch and $ff;
              match[1]:=mtch div $100;
            end else begin
              matchlen:=4;
              match[0]:=mtch and $ff;
              match[1]:=mtch div $100;
              match[2]:=mtch div $10000;
              match[3]:=mtch div $1000000;
            end
          end;
         SEARCH_DEC_:
          begin
            mtch:=strtoint(strpas(srchtext));
            if mtch<256 then begin
              matchlen:=1;
              match[0]:=mtch;
            end else if mtch<65536 then begin
              matchlen:=2;
              match[0]:=mtch and $ff;
              match[1]:=mtch div $100;
            end else begin
              matchlen:=4;
              match[0]:=mtch and $ff;
              match[1]:=mtch div $100;
              match[2]:=mtch div $10000;
              match[3]:=mtch div $1000000;
            end;
          end;
         SEARCH_BYTES_:
          begin
            matchlen:=strlen(srchtext) div 2;
            for i:=0 to matchlen-1 do begin
              if srchtext[i*2]>='a' then tmpbyte:=(byte(srchtext[i*2])-byte('a')+10)
              else tmpbyte:=(byte(srchtext[i*2])-byte('0'));
              if srchtext[i*2+1]>='a' then tmpbyte:=tmpbyte*16+(byte(srchtext[i*2+1])-byte('a')+10)
              else tmpbyte:=tmpbyte*16+(byte(srchtext[i*2+1])-byte('0'));
              match[i]:=tmpbyte;
            end;
          end;
        end;
        // string.data,
        // for each seg do .....
        found:=FALSE;
        if fromstart then begin
          dta.resetiterator;
          srchseg:=pdsegitem(dta.nextiterator);
          dta.resetiterator;
        end else srchseg:=dta.findseg(s_seg);
        lastfromstart:=fromstart;
        while srchseg<>nil do begin
          s_seg.s:=srchseg.addr.s;
          if fromstart then s_seg:=srchseg.addr;
          fromstart:=FALSE;
          if s_seg.o<srchseg.addr.o then s_seg:=srchseg.addr;
          while s_seg.o<srchseg.addr.o+srchseg.size-matchlen do begin
            segmtch:=srchseg.data+(s_seg.o-srchseg.addr.o);
            found:=TRUE;
            for i:=0 to matchlen-1 do begin
              if segmtch[i]<>chr(match[i]) then begin
                found:=FALSE; break;
              end;
            end;
            if found then break;
            inc(s_seg.o);
          end;
          if found then break;
          s_seg.o:=0;
          ssold:=srchseg.addr;
          srchseg:=pdsegitem(dta.nextiterator);
          // try not to do first segment twice.........
          if srchseg<>nil then if eq(ssold,srchseg.addr) then
            srchseg:=pdsegitem(dta.nextiterator);
        end;
        lastsearchtype:=searchtype;
        if found then
          scheduler.addtask(user_jumptoaddr,priority_userrequest,s_seg,0,nil);
        DestroyWindow(sbox);
        EndDialog(hdwnd,0);
        result:=1; exit;
     end;
   IDCANCEL:
    begin
      EndDialog(hdwnd,0);
      result:=1; exit;
    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);
      brush:=CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
      fromstart:=lastfromstart;
      searchtype:=lastsearchtype;
      if searchtype=SEARCH_STR_ then
        SendDlgItemMessage(hdwnd,search_string,BM_SETCHECK,1,0)
      else if searchtype=SEARCH_HEX_ then
        SendDlgItemMessage(hdwnd,search_hex,BM_SETCHECK,1,0)
      else if searchtype=SEARCH_DEC_ then
        SendDlgItemMessage(hdwnd,search_decimal,BM_SETCHECK,1,0)
      else
        SendDlgItemMessage(hdwnd,search_bytes,BM_SETCHECK,1,0);
      if fromstart then
        SendDlgItemMessage(hdwnd,search_fromstart,BM_SETCHECK,1,0)
      else
        SendDlgItemMessage(hdwnd,search_fromcurr,BM_SETCHECK,1,0);
      SendDlgItemMessage(hdwnd,search_edit,WM_SETTEXT,0,dword(@oldsearchtext));
      SetFocus(GetDlgItem(hdwnd,search_edit));
      exit;
     end;
   WM_DESTROY:
    begin
      DeleteObject(brush); exit;
    end;
   WM_CTLCOLORSTATIC,
   WM_CTLCOLORBTN,
   WM_CTLCOLORDLG,
   WM_SYSCOLORCHANGE:
    begin
      SetBkColor(HDC(wParam),GetSysColor(COLOR_BTNFACE));
      result:=brush;
    end;
  end;
end;

// searchengine                                                          *
// - simply stops the secondary thread, puts up the search dialog box,   *
//   and restarts the thread again at the end                            *
procedure searchengine;
begin
  scheduler.stopthread;
  DialogBox(Inst,MAKEINTRESOURCE(Search_Dialog),mainwindow,@searchbox);
  scheduler.continuethread;
end;

// searchmore                                                            *
// - I basically ripped out the main search code from the original       *
//   dialog box, and created a searchmore function from it, which just   *
//   allows the user to press a key and search again (F3).               *
// - The function handles stopping and starting the secondary thread     *
procedure searchmore;
var
  searchtype:search_type;
  s_seg,ssold:lptr;
  srchtext:array[0..200] of char;
  match:array[0..200] of byte;
  segmtch:pchar;
  tmpbyte:byte;
  matchlen,i:integer;
  mtch:dword;
  found:boolean;
  sbox:HWND;
  srchseg:pdsegitem;
begin
  if strlen(oldsearchtext)=0 then begin
    searchengine; exit;
  end;
  scheduler.stopthread;
  searchtype:=lastsearchtype;
  dio.findcurrentaddr(s_seg);
  inc(s_seg.o,dsm.getlength(s_seg));
  sbox:=CreateDialog(Inst,MAKEINTRESOURCE(S_Box),mainwindow,@searchingbox);
  // get string,
  strcopy(srchtext,oldsearchtext);
  matchlen:=0;
  case searchtype of
   SEARCH_STR_:
    begin
      matchlen:=strlen(srchtext);
      strcopy(@match,srchtext);
    end;
   SEARCH_HEX_:
    begin
      mtch:=strtoint('$'+strpas(srchtext));
      if mtch<256 then begin
        matchlen:=1;
        match[0]:=byte(mtch);
      end else if mtch<65536 then begin
       matchlen:=2;
        match[0]:=byte(mtch and $ff);
        match[1]:=byte(mtch div $100);
      end else begin
        matchlen:=4;
        match[0]:=byte(mtch and $ff);
        match[1]:=byte(mtch div $100);
        match[2]:=byte(mtch div $10000);
        match[3]:=byte(mtch div $1000000);
      end;
    end;
   SEARCH_DEC_:
    begin
      mtch:=strtoint(strpas(srchtext));
      if mtch<256 then begin
        matchlen:=1;
        match[0]:=byte(mtch);
      end else if mtch<65536 then begin
        matchlen:=2;
        match[0]:=byte(mtch and $ff);
        match[1]:=byte(mtch div $100);
      end else begin
        matchlen:=4;
        match[0]:=byte(mtch and $ff);
        match[1]:=byte(mtch and $100);
        match[2]:=byte(mtch and $10000);
        match[3]:=byte(mtch and $1000000);
      end;
    end;
   SEARCH_BYTES_:
    begin
      matchlen:=strlen(srchtext) div 2;
      for i:=0 to matchlen-1 do begin
        if srchtext[i*2]>='a' then tmpbyte:=byte(srchtext[i*2])-byte('a')+10
        else tmpbyte:=byte(srchtext[i*2])-byte('0');
        if srchtext[i*2+1]>='a' then tmpbyte:=byte(tmpbyte*16+(byte(srchtext[i*2+1])-byte('a')+10))
        else tmpbyte:=tmpbyte*16+(byte(srchtext[i*2+1])-byte('0'));
        match[i]:=tmpbyte;
      end;
    end;
  end;
  // string.data,
  // for each seg do .....
  found:=FALSE;
  srchseg:=dta.findseg(s_seg);
  while srchseg<>nil do begin
    s_seg.s:=srchseg.addr.s;
    if le(s_seg,srchseg.addr) then s_seg:=srchseg.addr;
    while s_seg.o<srchseg.addr.o+srchseg.size-matchlen do begin
      segmtch:=srchseg.data+(s_seg.o-srchseg.addr.o);
      found:=TRUE;
      for i:=0 to matchlen-1 do begin
        if byte(segmtch[i])<>match[i] then begin
          found:=FALSE;
          break;
        end;
      end;
      if found then break;
      inc(s_seg.o);
    end;
    if found then break;
    s_seg.o:=0;
    ssold:=srchseg.addr;
    srchseg:=pdsegitem(dta.nextiterator);
    // try not to do first segment twice.........
    if srchseg<>nil then if eq(ssold,srchseg.addr) then
      srchseg:=pdsegitem(dta.nextiterator);
  end;
  if found then scheduler.addtask(user_jumptoaddr,priority_userrequest,s_seg,0,nil);
  DestroyWindow(sbox);
  scheduler.continuethread;
end;

begin
end.

