/*Written By Sm0oth(Wayne Anthony) And Bevets(Steve Boothman)++ with code from AlGonzalez(Alberto Gonzalez)
  Written in IBPro, updates made in EBasic
  Compile As Windows exe*/
AutoDefine "Off"
Declare Import,SetWindowPos(hwnd As Int,hWndInsertAfter As Int,x As Int,y As Int,cx As Int,cy As Int,wFlags As Int),Int
Declare Import,AdjustWindowRectEx(rec As Pointer,style As Int,mflag As Int,ExStyle As Int),Int
Declare Import,GetWindowLongA(hwin As Int,nIndex As Int),Int
Declare Import,SetRect(pRect As Pointer,x1 As Int,y1 As Int,x2 As Int,y2 As Int),Int
Declare Import,PtInRect(pRect As WINRECT, x As Int, y As Int),Int
Declare Import,GetCommandLineA(),String
Declare fnSearch(str As String, start As Int),Int
Const GWL_STYLE = -16
Const GWL_EXSTYLE=-20
Const WS_OVERLAPPED = 0x0
Const SWP_NOMOVE = 0x2
Const WM_MOUSEWHEEL = 0x20A
Dim wndMain,dlgEdit,dlgFind,dlgGoto As Window
Dim hexFile As BFile
Dim Filename As String
Dim position,searchpos As Int
Dim pt As Point
Dim grid[640] As WinRect
Dim x,z,row,hexb As Int
Dim seekpos As Int64
Dim pSearch As UInt
pSearch=&SearchFile
searchpos=0
position=1
hexb=True
Filename=NthField(GetCommandLineA()," ",1,True)
If Filename="" Then Filename=FileRequest("Open a file",0,1)
If Filename<>"" Then OpenFile(hexFile,Filename,"R+") Else End
OpenWindow wndMain,0,0,800,600,@MinBox|@NoAutoDraw|@VScroll,0,"iHex - "+ReturnName(Filename)+" - "+Str$(Len(hexFile))+" bytes",&wndMainProc
CreateDialog dlgEdit,0,0,134,83,0x80C80080,0,"Edit",&dlgEditProc
Control dlgEdit,@Edit,"00",12,5,39,21,0x50810000,1
Control dlgEdit,@ComboBox,"",54,5,70,69,0x50A10643,2
Control dlgEdit,@Button,"Okay",29,54,70,20,0x50010001,3
CreateDialog dlgFind,0,0,224,72,0x80C80080,0,"Find",&dlgFindProc
Control dlgFind,@Edit,"",9,6,141,20,0x50810080,1
Control dlgFind,@Button,"Find",151,6,35,20,0x50010000,2
Control dlgFind,@ComboBox,"",9,28,70,69,0x50A10643,3
Control dlgFind,@Edit,"",80,28,70,21,0x50810000,4
Control dlgFind,@Button,"Byte",151,28,70,21,0x50010000,5
Control dlgFind,@Button,"Next",186,6,35,20,0x50010000,6
Control dlgFind,@RadioButton,"File-based",9,50,70,20,@TabStop,7
Control dlgFind,@RadioButton,"Memory",80,50,70,20,@TabStop,8
CreateDialog dlgGoto,0,0,212,70,0x80C80080,0,"Goto",&dlgGotoProc
Control dlgGoto,@Edit,"",7,10,105,20,0x50810080,1
Control dlgGoto,@Button,"Goto",71,43,70,20,0x50010001,2
Control dlgGoto,@ComboBox,"",115,10,86,61,0x50A10643,3
RasterMode wndMain,@Transparent
SetClientSize(wndMain,790,400)
SetScrollRange(wndMain,-2,0,(Len(hexFile)/32)-1)
SetScrollPos(wndMain,-2,position)
SetFont wndMain,"FixedSys",0,0
WaitUntil wndMain=0
End
Sub dlgGotoProc
	Select @Message
		Case @IDInitDialog
			CenterWindow dlgGoto
			If ControlExists(dlgGoto,2)
				AddString dlgGoto,3,"Hex"
				AddString dlgGoto,3,"Dec"
				AddString dlgGoto,3,"Oct"
				AddString dlgGoto,3,"Bin"
				SetSelected dlgGoto,3,0
			EndIf
		Case @IDControl
			If @ControlID=2 Then
				Select GetControlText(dlgGoto,3)
					Case "Hex"
						SetScrollPos wndMain,-2,(ToInt(GetControlText(dlgGoto,1),16)/32)+1
					Case "Oct"
						SetScrollPos wndMain,-2,(ToInt(GetControlText(dlgGoto,1),8)/32)+1
					Case "Bin"
						SetScrollPos wndMain,-2,(ToInt(GetControlText(dlgGoto,1),2)/32)+1
					Case "Dec"
						SetScrollPos wndMain,-2,(Val(GetControlText(dlgGoto,1))/32)+1
				EndSelect
				position = GetScrollPos(wndMain,-2)
				Paint(GetScrollPos(wndMain,-2))
				CloseDialog dlgGoto
			EndIf
	EndSelect
	Return
EndSub
Sub dlgFindProc
	Dim r As Char
	Select @Message
		Case @IDInitDialog
			CenterWindow dlgFind
			AddString dlgFind,3,"Hex"
			AddString dlgFind,3,"Dec"
			AddString dlgFind,3,"Oct"
			AddString dlgFind,3,"Bin"
			SetSelected dlgFind,3,0
			If pSearch=&SearchFile Then SetState dlgFind,7,1
			If pSearch=&SearchBuffer Then SetState dlgFind,8,1
		Case @IDControl
			Select @ControlID
				Case 2
					searchpos=!<fnSearch>pSearch(GetControlText(dlgFind,1),0)
					If searchpos > 0
						SetScrollPos wndMain,-2,(searchpos / 32) + 1
						position = GetScrollPos(wndMain,-2)
						Paint(GetScrollPos(wndMain,-2))
					EndIf
					CloseDialog dlgFind
				Case 5
					Select GetControlText(dlgFind,3)
						Case "Hex"
							r=ToInt(GetControlText(dlgFind,4),16)
						Case "Oct"
							r=ToInt(GetControlText(dlgFind,4),8)
						Case "Bin"
							r=ToInt(GetControlText(dlgFind,4),2)
						Case "Dec"
							r=Val(GetControlText(dlgFind,4))
					EndSelect
					SetControlText dlgFind,1,GetControlText(dlgFind,1)+r
				Case 6
					searchpos=!<fnSearch>pSearch(GetControlText(dlgFind,1),searchpos)
					If searchpos > 0
						SetScrollPos wndMain,-2,(searchpos / 32) + 1
						position = GetScrollPos(wndMain,-2)
						Paint(GetScrollPos(wndMain,-2))
					EndIf
				Case 7
					pSearch=&SearchFile
				Case 8
					pSearch=&SearchBuffer
		EndSelect
	EndSelect
	Return
EndSub
Sub dlgEditProc
	Dim r As Char
	Dim i As Int
	Select @Message
		Case @IDInitDialog
			CenterWindow dlgEdit
			If ControlExists(dlgEdit,2)
				AddString dlgEdit,2,"Hex"
				AddString dlgEdit,2,"Dec"
				AddString dlgEdit,2,"Oct"
				AddString dlgEdit,2,"Bin"
				AddString dlgEdit,2,"Asc"
				SetSelected dlgEdit,2,0
			EndIf
			If ControlExists(dlgEdit,1)
				r=Read$(1)
				SetControlText dlgEdit,1,Hex$(Int(r))
				If r>37 And r<127 Then SetCaption dlgEdit,"Byte #"+LTrim$(Hex$(seekpos))+" "+r Else SetCaption dlgEdit,"Byte #"+LTrim$(Hex$(seekpos))+" ."
			EndIf
		Case @IDControl
			If @ControlID=3
				Seek(hexFile,seekpos)
				Select GetControlText(dlgEdit,2)
					Case "Hex"
						r=ToInt(GetControlText(dlgEdit,1),16)
					Case "Oct"
						r=ToInt(GetControlText(dlgEdit,1),8)
					Case "Bin"
						r=ToInt(GetControlText(dlgEdit,1),2)
					Case "Dec"
						r=Val(GetControlText(dlgEdit,1))
					Case "Asc"
						r=Asc(Mid$(GetControlText(dlgEdit,1),1,1))
				EndSelect
				Write hexFile,r
				SetCaption wndMain,"iHex - "+ReturnName(Filename)+" - "+Str$(Len(hexFile))+" bytes"
				CloseDialog dlgEdit
			EndIf
	EndSelect
	Return
EndSub
Sub wndMainProc
	Dim delta As Int
	Select @Message
		Case @IDCreate
			AddAccelerator wndMain,@FControl|@FVirtKey,Asc("H"),1
			AddAccelerator wndMain,@FControl|@FVirtKey,Asc("F"),2
			AddAccelerator wndMain,@FControl|@FVirtKey,Asc("G"),3
			AddAccelerator wndMain,@FControl|@FVirtKey,Asc("R"),101
		Case @IDKeyDown
			Select @WParam
				Case 0x21 /*Page Up*/
					'SendMessage wndMain,@IDVScroll,Null,@SBPageUp
					If position > 20
						position-=20
						SetScrollPos wndMain,-2,position
						Paint(GetScrollPos(wndMain,-2))
					EndIf
				Case 0x22 /*Page Down*/
					'SendMessage wndMain,@IDVScroll,Null,@SBPageDown
					If Eof(hexFile)<>True
						position+=20
						SetScrollPos wndMain,-2,position
						Paint(GetScrollPos(wndMain,-2))
					EndIf
				Case 0x23 /*End*/
					position=(Len(hexFile)/32)-1
					SetScrollPos wndMain,-2,position
					Paint(GetScrollPos(wndMain,-2))
				Case 0x24 /*Home*/
					position=1
					SetScrollPos wndMain,-2,position
					Paint(GetScrollPos(wndMain,-2))
				Case 0x26 /*Up Arrow*/
					'SendMessage wndMain,@IDVScroll,Null,@SBLineUp
					If position > 1
						position--
						SetScrollPos wndMain,-2,position
						Paint(GetScrollPos(wndMain,-2))
					EndIf
				Case 0x28 /*Down Arrow*/
					'SendMessage wndMain,@IDVScroll,Null,@SBLineDown
					If Eof(hexFile)<>True
						position++
						SetScrollPos wndMain,-2,position
						Paint(GetScrollPos(wndMain,-2))
					EndIf
			EndSelect
		Case @IDLButtonDN
			For x = 0 To 639
				If PtInRect(grid[x],@MouseX,@MouseY)
					seekpos=(32*(position-1))+x
					Seek(hexFile,seekpos)
					'MessageBox wndMain,"Byte "+Str$(seekpos)+"\n"+Read$(1)+" Selected","IHex"
					DoModal dlgEdit,wndMain
					Paint(GetScrollPos(wndMain,-2))
					BreakFor
				EndIf
			Next x
		Case @IDRButtonUp
			ContextMenu wndMain,@MouseX,@MouseY
				MenuItem "Hex\tCtrl+H",@MenuCheck*hexb,1
				MenuItem "Find\tCtrl+F",0,2
				MenuItem "Goto\tCtrl+G",0,3
				BeginPopup "Info"
					MenuItem "Retrieve full path\tCtrl+R",0,101
				EndPopup
			EndMenu
		Case @IDCloseWindow
			CloseFile hexFile
			CloseWindow wndMain
		Case @IDPaint
			Paint(GetScrollPos(wndMain,-2))
		Case @IDVScroll
			Select @WParam
				Case @SBLineUp
					If position > 1
						position--
						SetScrollPos wndMain,-2,position
						Paint(GetScrollPos(wndMain,-2))
					EndIf
				Case @SBLineDown
					If Eof(hexFile)<>True
						position++
						SetScrollPos wndMain,-2,position
						Paint(GetScrollPos(wndMain,-2))
					EndIf
				Case @SBPageUp
					If position > 20
						position-=20
						SetScrollPos wndMain,-2,position
						Paint(GetScrollPos(wndMain,-2))
					EndIf
				Case @SBPageDown
					If Eof(hexFile)<>True
						position+=20
						SetScrollPos wndMain,-2,position
						Paint(GetScrollPos(wndMain,-2))
					EndIf
				Case @SBThumbPos
					position=GetThumbPos(wndMain,-2)
					If position=0 Then position=1
					SetScrollPos wndMain,-2,position
					Paint(GetScrollPos(wndMain,-2))
				Case @SBThumbTrack
					position=GetThumbPos(wndMain,-2)
					If position=0 Then position=1
					SetScrollPos wndMain,-2,position
					Paint(GetScrollPos(wndMain,-2))
				Case @SBTop
					position=1
					SetScrollPos wndMain,-2,position
					Paint(GetScrollPos(wndMain,-2))
				Case @SBBottom
					position=(Len(hexFile)/32)-1
					SetScrollPos wndMain,-2,position
					Paint(GetScrollPos(wndMain,-2))
			EndSelect
		Case @IDMenuPick
			Select @MenuNum
				Case 1
					hexb=(hexb=False)
					Paint(GetScrollPos(wndMain,-2))
				Case 2
					DoModal dlgFind,wndMain
				Case 3
					DoModal dlgGoto,wndMain
				Case 101
					MessageBox wndMain,"The full path to the file currently open is:\n"+Filename,"Info"
			EndSelect
		Case WM_MOUSEWHEEL
			delta=Int((@code/65536)/120)*15
			If delta>0 Then
				position-=5
				If position<1 Then position=1
				SetScrollPos wndMain,-2,position
				Paint(GetScrollPos(wndMain,-2))
			EndIf
			If delta<0 Then
				position+=5
				SetScrollPos wndMain,-2,position
				Paint(GetScrollPos(wndMain,-2))
			EndIf
	EndSelect
	Return
EndSub
Sub Paint(Opt posi As Int)
	Dim i,j,pos,topo,l,x,flag As Int
	Dim mypos As Int64
	Dim a As Char
	x=0:flag=0
	mypos= Posi
	pos=Posi
	topo=20+pos-2
	Seek(hexFile,(Posi-1)*32)
	For j=pos-1 to topo
		l=Len(Hex$(Seek(hexFile)))
		Move wndMain,14,20*x
		Print wndMain,String$(8-l,"0")+Hex$(Seek(hexFile))
		For i=1 to 32
			a=Read$(1)
			l=Len(Hex$(a))
			Move wndMain,8*14+i*20,20*x
			If Eof(hexFile) = 1 Then flag++
			If flag > 1
				Print wndMain,"??"
			Else
				If hexb
					Print wndMain,String$(2-l,"0")+Hex$(Int(a))
				Else
					If a>37 And a<127
						Print wndMain," "+Chr$(a)
					Else
						Select a
							Case 13
								Print wndMain,"CR"
							Case 10
								Print wndMain,"LF"
							Default
								Print wndMain," ."
						EndSelect
					EndIf
				EndIf
			EndIf
		Next i
		x++
	Next j
	Draw()
	Return
EndSub
Sub Read$(Bytes As Int),String
	Dim tmp As String
	__Read(hexFile,tmp,Bytes)
	Return tmp
EndSub
Sub SetClientSize(win As Window,w As Int,h As Int,Opt fmenu=0 As Int),Int
	 WINRECT rec
	 Int style,exstyle,err
	 style=0:exstyle=0:err=0
	 style=GetWindowLongA(win.hwnd,GWL_STYLE)
	 If style
		style=style & not(WS_OVERLAPPED)
		exstyle=GetWindowLongA(win.hwnd,GWL_EXSTYLE)
		If style=0 | exstyle=0 Then Return 0
	 EndIf
	 err=SetRect(rec,0,0,w,h)
	 If err
		err=AdjustWindowRectEx(rec,style,fmenu,exstyle)
		If err
			err=SetWindowPos(win.hwnd,0,0,0,rec.right+Int(ABS(rec.left)),rec.bottom+Int(ABS(rec.top)),SWP_NOMOVE)
		EndIf
	 EndIf
	Return err
EndSub
Sub Draw()
	Dim cons As Int
	cons=20
	z=14*8
	For x = 0 To 639
	' **
		If x%32=0
			row = x/32
			z = 14*8
		EndIf
		grid[x].top=row*cons
	' **
		z=z+cons
		grid[x].left=z
		grid[x].right=18
		grid[x].bottom=18
		'print the number to the window
		Move wndMain,grid[x].left,grid[x].top
	'	Print wndMain,Hex$(x)
		'draw the rectangle
		'update the winrect for use with PtInRect
		'works differently to Rect
		grid[x].right=grid[x].left+18
		grid[x].bottom=grid[x].top+18
	Next x
	Return
EndSub
Sub ToBase(intValue As Int,Base As Int),String
	Dim result As String
	Dim i As Int
	result = ""
	If (base >= 2) & (base <= 36)
		While intValue > 0
			i = (intValue % base) + 1
			intValue = intValue / base 
			result = Mid$("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ",i,1)+result
		EndWhile
	EndIf
	Return result
EndSub
Sub ToInt(strValue As String, base As Int),Int
	Dim result, i, charValue As Int
	result = 0
	If (base >= 2) & (base <= 36)
		For i = 1 to Len(strValue)
			charValue = InStr("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", UCase$(Mid$(strValue, i, 1)))
			' return -1 if character is not supported by base
			If charValue > base Then Return -1
			result = (result * base) + (charValue - 1)
		Next i
	EndIf
	Return result
EndSub
Sub ReturnName(Path As String),String
	Dim last,late,i As Int
	Dim rString As String
	late=0
	Do
		last=late
		late=Instr(Path,"\\",last+1)
	Until late=0
	rString=Mid$(Path,last+1)
	For i=Len(rString)-1 to 0 Step -1
		If rString[i]="."
			rString[i]=0
			i=0
		EndIf
	Next i
	Return rString
EndSub
Sub NthField(Source As String,Delimiter As Char,fieldNum As Int,Opt quotes=False As Int),String
	Dim rString As Pointer
	Dim pos,field,quote As Int
	field=0
	rString=AllocHeap(Len(Source)+1)
	quote=False
	SetType rString,String
	If InStr(Source,Chr$(Delimiter))=0 Then Return ""
	For pos=0 To Len(Source)-1
		If quotes=True And Source[pos]=34 Then
			quote=(quote=False)
		Else
			If Source[pos]=Delimiter And quote=False Then
				If field=fieldNum Then Return #rString
				field++
				#rString=""
			Else
				#rString+=Source[pos]
			EndIf
		EndIf
	Next pos
	Return #rString
EndSub
Sub SearchBuffer(str:String,Opt start=0:Int),Int
	Memory buffer
	Pointer mypointer
	Int blen,pos
	blen = 0:pos = -1
	If hexFile <> NULL
		blen = Len(hexFile) + 1
		AllocMem(buffer,blen,1)
		Seek hexFile,0
		Read hexFile,buffer
		mypointer = buffer
		mypointer+=start
		pos = INBIN(mypointer,blen,str)
		If buffer <> NULL
			FreeMem(buffer)
		EndIf
		If pos = 0
			Return 0
		EndIf
	EndIf
	Return pos + 1 + start
EndSub
Sub SearchFile(str As String,Opt start=0 As Int),Int
	Dim buffer As String
	Dim blen,pos,inc As Int
	If str="" Then Return 0
	inc=start
	pos=0
	While pos=0 And Eof(hexFile)=0
		Seek hexFile,inc
		buffer=Read$(Len(str))
		pos=INBIN(buffer,Len(str)+1,str)
		inc++
	Wend
	Return (inc-1)*pos
EndSub
Sub INBIN(target:Pointer,blen:Int,search:String,OPT pos = 1:UInt),UInt
	Pointer cp,s1,s2,top
	cp = target
	s2 = search
	top=blen+cp
	If pos < 1 Then pos = 1
	If pos > 1
		target += (pos - 1)
		If blen < pos Then Return 0
	EndIf
	If (#<Char>cp <> 0) & (#<Char>s2 <> 0)
		While top >= s1
			s1 = cp
			s2 = search
			While (s1 <> blen) & (#<Char>s1 = #<Char>s2)
				s1++
				s2++
			EndWhile
			If #<Char>s2 = 0
				'substring found
				s1 = target
				Return (cp - s1)+1
			EndIf
			cp++
		EndWhile
	EndIf
	Return 0
EndSub