From da91dc3b6f4f6e79d9ed55d0e731886834c13111 Mon Sep 17 00:00:00 2001 From: RD42 <42702181+dashr9230@users.noreply.github.com> Date: Mon, 18 Dec 2023 23:35:22 +0800 Subject: [PATCH] [exgui] Implement Main procedures and functions --- exgui/EliRT.obj | Bin 0 -> 2513 bytes exgui/Main.dfm | Bin 87089 -> 43550 bytes exgui/Main.pas | 1974 ++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 1878 insertions(+), 96 deletions(-) create mode 100644 exgui/EliRT.obj diff --git a/exgui/EliRT.obj b/exgui/EliRT.obj new file mode 100644 index 0000000000000000000000000000000000000000..983ba2d011e949c1767a656fbef9869c0bbbf7e4 GIT binary patch literal 2513 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 Z2ly#{NvnlKEAd<5ZUITES9IOh{{yy9m3{yK literal 87089 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 DeM*GW diff --git a/exgui/Main.pas b/exgui/Main.pas index c2b86e9..25887b8 100644 --- a/exgui/Main.pas +++ b/exgui/Main.pas @@ -3,9 +3,19 @@ unit Main; interface uses - Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, - Dialogs, ExtCtrls, XPMan, ImgList, Menus, ComCtrls, StdCtrls, TeEngine, - Series, TeeProcs, Chart, Tabs, ToolWin, GIFImage; + Windows, Messages, Forms, SysUtils, Registry, WinSock, ComObj, ShellAPI, ShlObj, hh, + FindSort, Dialogs, IdHTTP, MMSystem, ExtCtrls, XPMan, ImgList, Controls, + Menus, ComCtrls, StdCtrls, TeEngine, Series, TeeProcs, Chart, Tabs, Graphics, + GIFImage, ToolWin, Classes, TlHelp32, ClipBrd, ActiveX; + +{$L EliRT.obj} + +function RT_GetVersion(pReserved:Pointer): LongWord; stdcall; external; +function xVirtualAllocEx(hProcess: LongWord; lpAddress: Pointer; dwSize: LongWord; flAllocationType: LongWord; flProtect: LongWord): Pointer; stdcall; external; +function xCreateRemoteThread(hProcess :LongWord; lpThreadAttributes :Pointer; dwStackSize: LongWord; lpStartAddress: Pointer; lpParameter: Pointer; dwCreationFlags: LongWord; var lpThreadId: Cardinal): LongWord; stdcall; external; + +const + WM_RECV = WM_USER+1; type TfmMain = class(TForm) @@ -116,6 +126,11 @@ type tmrServerListUpdate: TTimer; ToolButton1: TToolButton; + function GetToken(TokenData: String; ItemIndex: Integer; TokenDelimiter: String): String; + function GetClipBoardStr: String; + procedure SetClipBoardStr(Str: String); + + procedure GetGTAExe(Owner: HWND); procedure FormCreate(Sender: TObject); procedure lbServersDrawItem(Control: TWinControl; Index: Integer; Rect: TRect; State: TOwnerDrawState); @@ -127,41 +142,38 @@ type procedure pnBreakableResize(Sender: TObject); procedure hcServersDrawSection(HeaderControl: THeaderControl; Section: THeaderSection; const Rect: TRect; Pressed: Boolean); - procedure tmrServerListUpdateTimer(Sender: TObject); procedure tmSIPingUpdateTimer(Sender: TObject); procedure lbServersClick(Sender: TObject); procedure FilterChange(Sender: TObject); procedure UpdateServers; procedure lbServersContextPopup(Sender: TObject; MousePos: TPoint; var Handled: Boolean); - procedure gbInfoContextPopup(Sender: TObject; MousePos: TPoint; - var Handled: Boolean); + procedure AddServer(Server: String); procedure ImportFavoritesClick(Sender: TObject); - procedure ImportFavorites; + procedure ImportFavorites(FileName: String; AddToFavs: Boolean); procedure ExportFavoritesClick(Sender: TObject); - procedure ExportFavorites; + procedure ExportFavorites(FileName: String; ExportPasswords: Boolean); procedure ExitClick(Sender: TObject); procedure miViewClick(Sender: TObject); procedure ToggleFilterServerInfo(Sender: TObject); procedure ToggleStatusBar(Sender: TObject); procedure ConnectClick(Sender: TObject); procedure AddServerClick(Sender: TObject); - procedure AddServer; procedure DeleteServerClick(Sender: TObject); procedure RefreshServerClick(Sender: TObject); procedure MasterServerUpdateClick(Sender: TObject); procedure CopyServerInfoClick(Sender: TObject); procedure ServerPropertiesClick(Sender: TObject); - procedure RemoteConsoleClick(Sender: TObject); procedure SettingsClick(Sender: TObject); + procedure RemoteConsoleClick(Sender: TObject); procedure HelpTopicsClick(Sender: TObject); procedure AboutClick(Sender: TObject); procedure tsServerListsChange(Sender: TObject; NewTab: Integer; var AllowChange: Boolean); - procedure QueryServerInfoParse; - procedure QueryServerInfoError; - procedure QueryServerInfo; - procedure ServerConnect; + procedure QueryServerInfoParse(SrcIP: String; SrcPort: Word; Buf: PAnsiChar; DataLen: Integer); + procedure QueryServerInfoError(SocketError: Integer); + procedure QueryServerInfo(Server: String; bPing: Boolean; bInfo: Boolean; bPlayers: Boolean; bRules: Boolean); + procedure ServerConnect(Server: String; Port: String; Password: String); procedure piCopyClick(Sender: TObject); procedure pmCopyPopup(Sender: TObject); procedure lbPlayersDrawItem(Control: TWinControl; Index: Integer; @@ -176,332 +188,2102 @@ type const Rect: TRect); procedure lbPlayersExit(Sender: TObject); procedure lbRulesExit(Sender: TObject); - procedure WMRecv; + procedure WMRecv(var Message: TMessage); message WM_RECV; procedure FormDestroy(Sender: TObject); procedure FormShow(Sender: TObject); - procedure BrowseForFolder; + function BrowseForFolder(Owner: HWND; var Directory: String; StartDir, Title: String): Boolean; procedure miSampClick(Sender: TObject); procedure tmrQueryQueueProcessTimer(Sender: TObject); - procedure label_urlClick(Sender: TObject); - procedure CreateFASTDesktoplink1Click; procedure FormResize(Sender: TObject); + procedure tmServerListUpdate(Sender: TObject); + procedure label_urlClick(Sender: TObject); procedure imLogoClick(Sender: TObject); + procedure CreateFASTDesktoplink1Click(Sender: TObject); + private { Private declarations } public { Public declarations } end; + TPlayerInfo = record + Name: String; + Score: Integer; + end; + + TRuleInfo = record + Rule: String; + Value: String; + end; + + TServerInfo = record + Address: String; + DottedAddress: String; + HasAddress: Boolean; + Port: Integer; + Tag: Word; + + HostName: String; + Passworded: Boolean; + Players: Integer; + MaxPlayers: Integer; + Ping: Integer; + Mode: String; + Map: String; + + ServerPassword: String; + RconPassword: String; + + aPlayers: Array of TPlayerInfo; + aRules: Array of TRuleInfo; + end; + + TSortMode = (smHostName, smPlayers, smPing, smMode, smMap); + + TSortDir = (sdUp, sdDown); + +const + FavoritesFileVersion = 1; + var fmMain: TfmMain; + Servers: Array of TServerInfo; + Filtered: Boolean = true; + SortMode: TSortMode = smHostName; + OldSortMode: TSortMode = smHostName; + SortDir: TSortDir = sdUp; + OldSortDir: TSortDir = sdUp; + QuerySocket: Integer; + FileTag: Array[0..3] of Char = ('S','A','M','P'); + SelServer: Integer = -1; + PingCounter: Integer = 0; + MasterFile: Integer = 1; + gta_sa_exe: String; + QueryQueue: TStringList; + ServersTopIndex: Integer = -1; + byte_4ED6C4: Boolean = false; + dword_4EF08C: TStringList; + FavoritesChanged: Boolean; implementation +uses + ImportFavorites, ExportFavorites, ServerProperties, + RconConfig, Settings, About, Rcon, MasterUpdate, + unit_webrunform; + {$R *.dfm} -procedure TfmMain.FormCreate(Sender: TObject); +procedure sub_4E1CEC(); begin - // TODO: TfmMain.FormCreate + if byte_4ED6C4 <> true then begin + CreateMutex(nil, true, 'kyeman and spookie woz ''ere, innit.'); + if GetLastError = ERROR_ALREADY_EXISTS then begin + MessageBox(0, 'SA:MP is already running.'#10#10'You can only run one instance at a time.', 'SA:MP Error', MB_ICONERROR); + ExitProcess(0); + end; + byte_4ED6C4:= true; + end; +end; + +function sub_4E1DA8(): String; +var + szPath: Array[0..MAX_PATH] Of Char; +begin + if SHGetSpecialFolderPath(0, szPath, CSIDL_PERSONAL, False) <> FALSE then + Result:= szPath + '\GTA San Andreas User Files\SAMP\' + else + Result:= ''; +end; + +procedure sub_4E1E6C(a1, a2: String); +var + v16: IUnknown; + v15: IShellLink; + v14: IPersistFile; + v13, v12: String; + v11: Array[0..264] Of Char; + szPath: Array[0..MAX_PATH] Of Char; + v10: WideString; +begin + v12:= Application.ExeName; + + v16 := CreateComObject(CLSID_ShellLink); + v15 := v16 as IShellLink; + v14 := v16 as IPersistFile; + + v15.SetPath(PChar(v12)); + v15.SetWorkingDirectory(PChar(ExtractFilePath(v12))); + v15.SetArguments(PChar(a1)); + + SHGetSpecialFolderPath(0, szPath, CSIDL_DESKTOP, False); + + v13:= szPath + String('\') + a2 + '.lnk'; + + v14.Save(PWideChar(v13), false); +end; + +function CompareHostName(a, b: Pointer): Integer; +begin + Result:= CompareText(TServerInfo(a^).HostName, TServerInfo(b^).HostName); +end; + +function ComparePlayers(a, b: Pointer): Integer; +begin + if TServerInfo(a^).Players > TServerInfo(b^).Players then + Result:= 1 + else if TServerInfo(a^).Players = TServerInfo(b^).Players then + Result:= 0 + else + Result:= -1; +end; + +function ComparePing(a, b: Pointer): Integer; +begin + if TServerInfo(a^).Ping > TServerInfo(b^).Ping then + Result:= 1 + else if TServerInfo(a^).Ping = TServerInfo(b^).Ping then + Result:= 0 + else + Result:= -1; +end; + +function CompareMode(a, b: Pointer): Integer; +begin + Result:= CompareText(TServerInfo(a^).Mode, TServerInfo(b^).Mode); +end; + +function CompareMap(a, b: Pointer): Integer; +begin + Result:= CompareText(TServerInfo(a^).Map, TServerInfo(b^).Map); +end; + +procedure sub_4E220C(a1: String); +var + v10: String; + v9: Integer; + + function sub_4E2140(a1, a2: string): Integer; + var + v13: Integer; + begin + Result:= 0; + for v13:= (Length(a2) - Length(a1) + 1) downto 1 do begin + if Copy(a2, v13, Length(a1)) = a1 then begin + Result:= v13; + Exit; + end; + end; + end; + +begin + if (Copy(a1, 2, 1) <> ':') then begin + if (Copy(a1, 3, 1) <> '\') then begin + if (Copy(a1, 1, 1) = '\') then begin + a1:= 'C:' + a1; + end else begin + a1:= 'C:\' + a1; + end; + end else begin + a1 := 'C:' + a1; + end; + end; + if not DirectoryExists(a1) then begin + v9:= sub_4E2140('\', a1); + v10:= Copy(a1, 1, v9 - 1); + if not DirectoryExists(v10) then + sub_4E220C(v10); + CreateDir(a1); + end; +end; + +function TfmMain.GetToken(TokenData: String; ItemIndex: Integer; TokenDelimiter: String): String; +var + i, Len, DelimLen, TokenCount: Integer; + TokenString: String; +begin + TokenCount:= 0; + i:= 0; + TokenString:= ''; + Len:= Length(TokenData); + DelimLen := Length(TokenDelimiter); + if (Len > 0) then begin + while i <= Len do begin + if (TokenCount = (ItemIndex - 1)) then begin + if (Copy(TokenData,i+1,DelimLen) = TokenDelimiter) then + break; + TokenString := TokenString + Copy(TokenData,i+1,1); + end; + if (Copy(TokenData,i+1,DelimLen) = TokenDelimiter) then begin + Inc(TokenCount); + Inc(i, DelimLen-1); + end; + Inc(i); + end; + end; + Result:= TokenString; +end; + +function GetIPFromHost(const HostName: string): string; +type + TaPInAddr = array[0..10] of PInAddr; + PaPInAddr = ^TaPInAddr; +var + phe: PHostEnt; + pptr: PaPInAddr; + i: Integer; + GInitData: TWSAData; +begin + if dword_4EF08C.Values[HostName] <> '' then begin + Result := ''; + Exit; + end; + WSAStartup($101, GInitData); + Result := ''; + phe := GetHostByName(PChar(HostName)); + if phe = nil then begin + Result:= ''; + dword_4EF08C.Values[HostName]:= '1'; + Exit; + end; + pPtr := PaPInAddr(phe^.h_addr_list); + i := 0; + while pPtr^[i] <> nil do + begin + Result := inet_ntoa(pptr^[i]^); + Inc(i); + end; + WSACleanup; +end; + +function sub_4E2628(a1: TColor): TColor; +begin + Result:= a1; + if GetRValue(ColorToRGB(a1)) > 16 then begin + if GetGValue(ColorToRGB(a1)) > 16 then begin + if GetBValue(ColorToRGB(a1)) > 16 then begin + Result:= RGB( + GetRValue(ColorToRGB(a1))-16, + GetGValue(ColorToRGB(a1))-16, + GetBValue(ColorToRGB(a1))-16); + end; + end; + end; +end; + +function TfmMain.GetClipBoardStr: String; +begin + Result:= Clipboard.AsText; +end; + +procedure TfmMain.SetClipBoardStr(Str: String); +begin + Clipboard.AsText:= Str; +end; + +procedure TfmMain.GetGTAExe(Owner: HWND); +var + Reg: TRegistry; + TmpStr, BrowseExe: String; +begin + Reg:= TRegistry.Create; + Reg.RootKey:= HKEY_CURRENT_USER; + Reg.OpenKey('SOFTWARE\Rockstar Games\GTA San Andreas\Installation', false); + if Reg.ValueExists('ExePath') then + TmpStr:= Reg.ReadString('ExePath') + else begin + Reg.RootKey:= HKEY_LOCAL_MACHINE; + Reg.OpenKey('SOFTWARE\Rockstar Games\GTA San Andreas\Installation', false); + if Reg.ValueExists('ExePath') then + TmpStr:= Reg.ReadString('ExePath'); + end; + Reg.CloseKey; + + TmpStr:= ExtractFilePath(Copy(TmpStr, 2, Length(TmpStr)-2)); + + if BrowseForFolder(Owner, BrowseExe, TmpStr, 'Please locate your GTA: San Andreas installtion...') then begin + gta_sa_exe:= BrowseExe + '\gta_sa.exe'; + + Reg.RootKey:= HKEY_CURRENT_USER; + Reg.OpenKey('SOFTWARE\SAMP', true); + Reg.WriteString('gta_sa_exe', gta_sa_exe); + Reg.CloseKey; + Reg.Free; + end; +end; + +procedure TfmMain.FormCreate(Sender: TObject); +var + Dummy: Boolean; + WSData: WSADATA; + s_in: sockaddr_in; + Reg: TRegistry; +begin + dword_4EF08C:= TStringList.Create; + FavoritesChanged:= false; + + Reg:= TRegistry.Create; + Reg.RootKey:= HKEY_CURRENT_USER; + Reg.OpenKey('SOFTWARE\SAMP', true); + + if Reg.ValueExists('gta_sa_exe') then + gta_sa_exe:= Reg.ReadString('gta_sa_exe') + else + GetGTAExe(Handle); + + if Reg.ValueExists('PlayerName') then + edName.Text:= Reg.ReadString('PlayerName'); + Reg.CloseKey; + Reg.Free; + + Randomize; + + WSAStartup($0202, WSData); + + QuerySocket:= Socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); + ZeroMemory(@s_in, sizeof(s_in)); + s_in.sin_addr.S_addr:= INADDR_ANY; + s_in.sin_family:= AF_INET; + s_in.sin_port:= 0; + bind(QuerySocket, s_in, sizeof(s_in)); + WSAAsyncSelect(QuerySocket, Handle, WM_RECV, FD_READ); + + QueryQueue:= TStringList.Create; + tmrQueryQueueProcess.Enabled:= true; + tmrServerListUpdate.Enabled:= true; + + sub_4E220C(sub_4E1DA8); + + if FileExists(sub_4E1DA8 + 'USERDATA.DAT') then + ImportFavorites(sub_4E1DA8 + 'USERDATA.DAT', false); + + tsServerListsChange(Self, 0, Dummy); + lbServersClick(Self); + + UpdateServers; end; procedure TfmMain.lbServersDrawItem(Control: TWinControl; Index: Integer; Rect: TRect; State: TOwnerDrawState); +var + i, Idx: Integer; + TempRect: TRect; begin - // TODO: TfmMain.lbServersDrawItem + with (Control as TListBox) do begin + Idx:= StrToIntDef(Items.Strings[Index], 0); + if Idx >= Length(Servers) then Exit; + + Canvas.Pen.Color:= clBtnHighlight; + Canvas.Pen.Style:= psClear; + + if odSelected in State then begin + Canvas.Font.Color:= clHighlightText; + Canvas.Brush.Color:= clHighlight; + end else begin + Canvas.Font.Color:= clWindowText; + if (Index mod 2) = 1 then + Canvas.Brush.Color:= clWindow + else + Canvas.Brush.Color:= sub_4E2628(clWindow); + end; + + Inc(Rect.Right); + Canvas.Rectangle(Rect); + Dec(Rect.Right); + Canvas.Pen.Style:= psSolid; + Canvas.PenPos:= Point(Rect.Right, Rect.Bottom-1); + Canvas.LineTo(Rect.Left, Rect.Bottom-1); + for i:= 0 to 5 do begin + Canvas.PenPos:= Point(hcServers.Sections.Items[i].Right-1, Rect.Top); + Canvas.LineTo(hcServers.Sections.Items[i].Right-1, Rect.Bottom); + end; + + if Servers[Idx].Passworded then + BitBlt(Canvas.Handle, 7, Rect.Top + 1, 13, 14, imPadlocked.Canvas.Handle, 0, 0, SRCCOPY) + else + BitBlt(Canvas.Handle, 7, Rect.Top + 1, 13, 14, imPadlock.Canvas.Handle, 0, 0, SRCCOPY); + + TempRect:= Classes.Rect(hcServers.Sections.Items[1].Left + 2, Rect.Top + 2, hcServers.Sections.Items[1].Right - 2, Rect.Bottom - 2); + DrawText(Canvas.Handle, PChar(Servers[Idx].HostName), -1, TempRect, DT_LEFT); + + TempRect:= Classes.Rect(hcServers.Sections.Items[2].Left + 2, Rect.Top + 2, hcServers.Sections.Items[2].Right - 2, Rect.Bottom - 2); + DrawText(Canvas.Handle, PChar(IntToStr(Servers[Idx].Players) + ' / ' + IntToStr(Servers[Idx].MaxPlayers)), -1, TempRect, DT_LEFT); + + TempRect:= Classes.Rect(hcServers.Sections.Items[3].Left + 2, Rect.Top + 2, hcServers.Sections.Items[3].Right - 2, Rect.Bottom - 2); + if Servers[Idx].Ping = 9999 then + DrawText(Canvas.Handle, '-', -1, TempRect, DT_LEFT) + else + DrawText(Canvas.Handle, PChar(IntToStr(Servers[Idx].Ping)), -1, TempRect, DT_LEFT); + + TempRect:= Classes.Rect(hcServers.Sections.Items[4].Left + 2, Rect.Top + 2, hcServers.Sections.Items[4].Right - 2, Rect.Bottom - 2); + DrawText(Canvas.Handle, PChar(Servers[Idx].Mode), -1, TempRect, DT_LEFT); + + TempRect:= Classes.Rect(hcServers.Sections.Items[5].Left + 2, Rect.Top + 2, hcServers.Sections.Items[5].Right - 2, Rect.Bottom - 2); + DrawText(Canvas.Handle, PChar(Servers[Idx].Map), -1, TempRect, DT_LEFT); + end; end; procedure TfmMain.hcServersSectionResize(HeaderControl: THeaderControl; Section: THeaderSection); begin - // TODO: TfmMain.hcServersSectionResize + lbServers.Repaint; end; procedure TfmMain.tbMainResize(Sender: TObject); begin - // TODO: TfmMain.tbMainResize + ToolButton1.Width:= ((Sender as TToolBar).Width-ToolButton1.Left)-imLogo.Width; + imLogo.Repaint; end; procedure TfmMain.hcServersSectionClick(HeaderControl: THeaderControl; Section: THeaderSection); begin - // TODO: TfmMain.hcServersSectionClick + case Section.Index of + 1: + begin + if SortMode = smHostName then begin + if SortDir = sdUp then + SortDir:= sdDown + else + SortDir:= sdUp; + end else begin + SortMode:= smHostName; + SortDir:= sdUp; + end; + end; + 2: + begin + if SortMode = smPlayers then begin + if SortDir = sdUp then + SortDir:= sdDown + else + SortDir:= sdUp; + end else begin + SortMode:= smPlayers; + SortDir:= sdDown; + end; + end; + 3: + begin + if SortMode = smPing then begin + if SortDir = sdUp then + SortDir:= sdDown + else + SortDir:= sdUp; + end else begin + SortMode:= smPing; + SortDir:= sdUp; + end; + end; + 4: + begin + if SortMode = smMode then begin + if SortDir = sdUp then + SortDir:= sdDown + else + SortDir:= sdUp; + end else begin + SortMode:= smMode; + SortDir:= sdUp; + end; + end; + 5: + begin + if SortMode = smMap then begin + if SortDir = sdUp then + SortDir:= sdDown + else + SortDir:= sdUp; + end else begin + SortMode:= smMap; + SortDir:= sdUp; + end; + end; + end; + + UpdateServers; end; procedure TfmMain.pnBreakableResize(Sender: TObject); begin - // TODO: TfmMain.pnBreakableResize + gbInfo.Width:= pnBreakable.Width - gbFilter.Width + 1; + chSIPingChart.Width:= gbInfo.Width - 272; + if chSIPingChart.Width > 50 then + chSIPingChart.Visible:= true + else + chSIPingChart.Visible:= false; end; +var + ReRender: Boolean; procedure TfmMain.hcServersDrawSection(HeaderControl: THeaderControl; Section: THeaderSection; const Rect: TRect; Pressed: Boolean); +var + DoIt: Boolean; + TempRect: TRect; begin - // TODO: TfmMain.hcServersDrawSection + DoIt:= false; + + Inc(TempRect.Left, 2); + Inc(TempRect.Top, 2); + Dec(TempRect.Right, 2); + Dec(TempRect.Bottom, 2); + + if (Section.Index = 1) and (SortMode = smHostName) then begin + DoIt:= true; + end else if (Section.Index = 2) and (SortMode = smPlayers) then begin + DoIt:= true; + end else if (Section.Index = 3) and (SortMode = smPing) then begin + DoIt:= true; + end else if (Section.Index = 4) and (SortMode = smMode) then begin + DoIt:= true; + end else if (Section.Index = 5) and (SortMode = smMap) then begin + DoIt:= true; + end; + + TempRect:= Rect; + Inc(TempRect.Left, 2); + Inc(TempRect.Top, 1); + + if DoIt then begin + if SortDir = sdDown then + BitBlt(HeaderControl.Canvas.Handle, Rect.Left + 2, Rect.Top + 2, 9, 12, imDownArrow.Canvas.Handle, 0, 0, SRCCOPY) + else + BitBlt(HeaderControl.Canvas.Handle, Rect.Left + 2, Rect.Top + 2, 9, 12, imUpArrow.Canvas.Handle, 0, 0, SRCCOPY); + Inc(TempRect.Left, 10); + end; + + DrawText(HeaderControl.Canvas.Handle, PChar(Section.Text), -1, TempRect, DT_LEFT); + + if not ReRender then begin + ReRender:= true; + HeaderControl.Repaint; + ReRender:= false; + end; end; -procedure TfmMain.tmrServerListUpdateTimer(Sender: TObject); +procedure TfmMain.tmServerListUpdate(Sender: TObject); begin - // TODO: TfmMain.tmrServerListUpdateTimer + if QueryQueue.Count > 0 then + UpdateServers; + lbPlayers.Invalidate; + lbRules.Invalidate; end; procedure TfmMain.tmSIPingUpdateTimer(Sender: TObject); +var + Idx: Integer; + PingOnly: Boolean; begin - // TODO: TfmMain.tmSIPingUpdateTimer + if lbServers.ItemIndex = -1 then Exit; + + if GetForegroundWindow <> Handle then Exit; + + Idx:= StrToInt(lbServers.Items.Strings[lbServers.ItemIndex]); + if Idx >= Length(Servers) then Exit; + + if PingCounter = 5 then begin + PingCounter:= 0; + PingOnly:= false; + end else begin + Inc(PingCounter); + PingOnly:= true; + end; + + if PingOnly then + QueryServerInfo(Servers[Idx].Address + ':' + IntToStr(Servers[Idx].Port) + '#' + IntToStr(Servers[Idx].Tag), true, false, false, false) + else + QueryServerInfo(Servers[Idx].Address + ':' + IntToStr(Servers[Idx].Port) + '#' + IntToStr(Servers[Idx].Tag), true, true, true, true); end; procedure TfmMain.lbServersClick(Sender: TObject); +var + Enabled: Boolean; begin - // TODO: TfmMain.lbServersClick + if SelServer <> lbServers.ItemIndex then begin + SelServer:= lbServers.ItemIndex; + chSIPingChart.Series[0].Clear; + end; + lbPlayers.Clear; + lbRules.Clear; + + Enabled:= lbServers.ItemIndex <> -1; + + tbDeleteServer.Enabled:= MasterFile = 0; + miDeleteServer.Enabled:= tbDeleteServer.Enabled; + + tbConnect.Enabled:= Enabled; + miConnect.Enabled:= Enabled; + tbRefreshServer.Enabled:= Enabled; + miRefreshServer.Enabled:= Enabled; + tbCopyServerInfo.Enabled:= Enabled; + miCopyServerInfo.Enabled:= Enabled; + tbServerProperties.Enabled:= Enabled; + miServerProperties.Enabled:= Enabled; + + if lbServers.ItemIndex = -1 then begin + edSIAddress.Text:= '- - -'; + lbSIPlayers.Caption:= '- - -'; + lbSIPing.Caption:= '- - -'; + lbSIMode.Caption:= '- - -'; + lbSIMap.Caption:= '- - -'; + gbInfo.Caption:= ' Server Info '; + Exit; + end; + + RefreshServerClick(Self); end; procedure TfmMain.FilterChange(Sender: TObject); begin - // TODO: TfmMain.FilterChange + UpdateServers; end; procedure TfmMain.UpdateServers; +var + ActualIdx, Idx, i: Integer; + OldServ: String; + FilterList: TStringList; + ItemFiltered: Boolean; + Sorted: Boolean; + TrackingChanges: Boolean; + TotServers, TotSlots, TotPlayers: Integer; + NewServs: TStringList; + TopIndexes: Array[1..3] of Integer; + TopIndexSaved: Integer; begin - // TODO: TfmMain.UpdateServers + if QueryQueue.Count > 0 then begin + lbServers.Items.BeginUpdate; + TrackingChanges:= true; + end; + + TopIndexSaved:= lbServers.TopIndex; + + NewServs:= TStringList.Create; + + ServersTopIndex:= lbServers.TopIndex; + TopIndexes[1]:= lbServers.TopIndex; + TopIndexes[2]:= lbPlayers.TopIndex; + TopIndexes[3]:= lbRules.TopIndex; + + Idx:= -1; + ActualIdx:= lbServers.ItemIndex; + if ActualIdx <> -1 then begin + Idx:= StrToInt(lbServers.Items.Strings[ActualIdx]); + if Idx < Length(Servers) then + OldServ:= Servers[Idx].Address + ':' + IntToStr(Servers[Idx].Port); + end; + + Sorted:= false; + if (OldSortMode <> SortMode) or (OldSortDir <> SortDir) then begin + OldSortMode:= SortMode; + OldSortDir:= SortDir; + Sorted:= true; + case SortMode of + smHostName: SortArray(Servers[0], 0, SizeOf(TServerInfo), 0, Length(Servers)-1, CompareHostName); + smPlayers : SortArray(Servers[0], 0, SizeOf(TServerInfo), 0, Length(Servers)-1, ComparePlayers); + smPing : SortArray(Servers[0], 0, SizeOf(TServerInfo), 0, Length(Servers)-1, ComparePing); + smMode : SortArray(Servers[0], 0, SizeOf(TServerInfo), 0, Length(Servers)-1, CompareMode); + smMap : SortArray(Servers[0], 0, SizeOf(TServerInfo), 0, Length(Servers)-1, CompareMap); + end; + end; + + if Filtered then begin + FilterList:= TStringList.Create; + for i:= 0 to Length(Servers) -1 do begin + ItemFiltered:= false; + // Check the mode filter... + if edFilterMode.Text <> '' then + if Pos(Lowercase(edFilterMode.Text), Lowercase(Servers[i].Mode)) = 0 then + ItemFiltered:= true; + // Check the mode filter... + if edFilterMap.Text <> '' then + if Pos(Lowercase(edFilterMap.Text), Lowercase(Servers[i].Map)) = 0 then + ItemFiltered:= true; + // Check the "Not Full" filter... + if cbFilterFull.Checked then + if Servers[i].Players = Servers[i].MaxPlayers then + ItemFiltered:= true; + // Check the "Not Empty" filter... + if cbFilterEmpty.Checked then + if Servers[i].Players = 0 then + ItemFiltered:= true; + // Check the "Not Passworded" filter... + if cbFilterPassworded.Checked then + if Servers[i].Passworded then + ItemFiltered:= true; + // Server hasn't responded yet. + if (Servers[i].MaxPlayers < 1) and (MasterFile <> 0) then + ItemFiltered:= true; + // If this server hasn't been filtered... + if not ItemFiltered then + FilterList.Add(IntToStr(i)); + end; + + if SortDir = sdDown then begin + for i:= FilterList.Count -1 downto 0 do + NewServs.Add(FilterList.Strings[i]); + end else + NewServs.Text:= FilterList.Text; + FilterList.Free; + end else begin + if SortDir = sdDown then begin + for i:= Length(Servers) -1 downto 0 do + if (Servers[i].MaxPlayers > 0) or (MasterFile = 0) then + NewServs.Add(IntToStr(i)); + end else begin + for i:= 0 to Length(Servers) -1 do + if (Servers[i].MaxPlayers > 0) or (MasterFile = 0) then + NewServs.Add(IntToStr(i)); + end; + end; + + if lbServers.Items.Text <> NewServs.Text then begin + lbServers.Items.Text:= NewServs.Text; + lbServers.TopIndex:= TopIndexes[1]; + end; + if (ActualIdx <> -1) and (Idx <> -1) and (Idx < Length(Servers)) then begin + NewServs.Clear; + for i:= 0 to Length(Servers[Idx].aPlayers)-1 do + NewServs.Add(IntToStr(i)); + if lbPlayers.Items.Text <> NewServs.Text then begin + lbPlayers.Items.Text:= NewServs.Text; + lbPlayers.TopIndex:= TopIndexes[2]; + end; + NewServs.Clear; + + label_url.Caption:= ''; + + for i:= 0 to Length(Servers[Idx].aRules)-1 do begin + NewServs.Add(IntToStr(i)); + if Servers[Idx].aRules[i].Rule = 'weburl' then + label_url.Caption:= Trim(Servers[Idx].aRules[i].Value); + end; + if lbRules.Items.Text <> NewServs.Text then begin + lbRules.Items.Text:= NewServs.Text; + lbRules.TopIndex:= TopIndexes[3]; + end; + end; + NewServs.Free; + + if Sorted then begin + for i:= 0 to lbServers.Count -1 do begin + Idx:= StrToInt(lbServers.Items.Strings[i]); + if Servers[Idx].Address + ':' + IntToStr(Servers[Idx].Port) = OldServ then begin + lbServers.ItemIndex:= i; + //lbServersClick(Self); + break; + end; + end; + end else + if ActualIdx <> -1 then + if ActualIdx < lbServers.Items.Count then + lbServers.ItemIndex:= ActualIdx; + + if Filtered then begin + if lbServers.ItemIndex <> -1 then begin + Idx:= StrToInt(lbServers.Items.Strings[lbServers.ItemIndex]); + if Idx < Length(Servers) then begin + edSIAddress.Text:= Servers[Idx].Address + ':' + IntToStr(Servers[Idx].Port); + lbSIPlayers.Caption:= IntToStr(Servers[Idx].Players) + ' / ' + IntToStr(Servers[Idx].MaxPlayers); + lbSIPing.Caption:= IntToStr(Servers[Idx].Ping); + lbSIMode.Caption:= Servers[Idx].Mode; + lbSIMap.Caption:= Servers[Idx].Map; + gbInfo.Caption:= ' Server Info: ' + Servers[Idx].HostName + ' '; + end; + end; + end; + + TotServers:= lbServers.Items.Count; + TotSlots:= 0; + TotPlayers:= 0; + for i:= 0 to Length(Servers) -1 do begin + Inc(TotSlots, Servers[i].MaxPlayers); + Inc(TotPlayers, Servers[i].Players); + end; + sbMain.SimpleText:= 'Servers: '+IntToStr(TotPlayers)+' players, playing on '+IntToStr(TotServers)+' servers. ('+IntToStr(TotSlots)+' player slots available)'; + + if lbServers.ItemIndex = -1 then + lbServers.ItemIndex:= 0; + if lbServers.Items.Count >= TopIndexSaved then + lbServers.TopIndex:= TopIndexSaved; + if TrackingChanges = true then + lbServers.Items.EndUpdate; + Application.ProcessMessages; end; procedure TfmMain.lbServersContextPopup(Sender: TObject; MousePos: TPoint; var Handled: Boolean); begin - // TODO: TfmMain.lbServersContextPopup -end; - -procedure TfmMain.gbInfoContextPopup(Sender: TObject; MousePos: TPoint; - var Handled: Boolean); -begin - // TODO: TfmMain.gbInfoContextPopup + Handled:= lbServers.ItemIndex = -1; end; procedure TfmMain.ImportFavoritesClick(Sender: TObject); +var + OD: TOpenDialog; + fmImportFavorites: TfmImportFavorites; + AddToFavs: Boolean; begin - // TODO: TfmMain.ImportFavoritesClick + tsServerLists.TabIndex:= 0; + + OD:= TOpenDialog.Create(Self); + OD.DefaultExt:= 'fav'; + OD.Filter:= 'SA-MP Favorites List (*.fav)|*.fav'; + OD.Options:= [ofEnableSizing,ofFileMustExist]; + OD.Title:= 'Import Favorites'; + if not OD.Execute then begin + OD.Free; + Exit; + end; + + fmImportFavorites:= TfmImportFavorites.Create(Application); + fmImportFavorites.ShowModal; + AddToFavs:= fmImportFavorites.rbAddToCurrent.Checked; + fmImportFavorites.Free; + + ImportFavorites(OD.FileName, AddToFavs); + OD.Free; + + ExportFavorites(sub_4E1DA8 + 'USERDATA.DAT', true); end; -procedure TfmMain.ImportFavorites; +procedure TfmMain.ImportFavorites(FileName: String; AddToFavs: Boolean); +var + ImportFile: File; + Temp, Temp2, i, j, x: Integer; + Dubble: Boolean; + ThisTag: Array[0..3] of Char; begin - // TODO: TfmMain.ImportFavorites + AssignFile(ImportFile, FileName); + FileMode:= 0; + Reset(ImportFile, 1); + BlockRead(ImportFile, ThisTag[0], 4); + if ThisTag <> FileTag then begin + MessageDlg('Invalid SA-MP file.', mtError, [mbOk], 0); + CloseFile(ImportFile); + Exit; + end; + BlockRead(ImportFile, Temp, 4); + if Temp <> FavoritesFileVersion then begin + MessageDlg('Bad SA-MP favorites file version.'#13#10#13#10'Your client may need updating.', mtError, [mbOk], 0); + CloseFile(ImportFile); + Exit; + end; + + // Number of servers in file + BlockRead(ImportFile, Temp2, 4); + + if not AddToFavs then + SetLength(Servers, 0); + + for j:= 1 to Temp2 do begin + Dubble:= false; + i:= Length(Servers); + SetLength(Servers, i+1); + + // Address + BlockRead(ImportFile, Temp, 4); + SetLength(Servers[i].Address, Temp); + if Temp <> 0 then + BlockRead(ImportFile, Servers[i].Address[1], Temp); + + // Port + BlockRead(ImportFile, Servers[i].Port, 4); + + for x:= 0 to Length(Servers)-2 do begin + if (Servers[x].Address = Servers[i].Address) and (Servers[x].Port = Servers[i].Port) then begin + Dubble:= true; + Break; + end; + end; + + // Hostname + BlockRead(ImportFile, Temp, 4); + SetLength(Servers[i].HostName, Temp); + BlockRead(ImportFile, Servers[i].HostName[1], Temp); + // Server password + BlockRead(ImportFile, Temp, 4); + SetLength(Servers[i].ServerPassword, Temp); + if Temp <> 0 then + BlockRead(ImportFile, Servers[i].ServerPassword[1], Temp); + // Rcon password + BlockRead(ImportFile, Temp, 4); + SetLength(Servers[i].RconPassword, Temp); + if Temp <> 0 then + BlockRead(ImportFile, Servers[i].RconPassword[1], Temp); + + Servers[i].Ping:= 9999; + Servers[i].Tag:= Random($FFFF); + + if Dubble then + SetLength(Servers, i) + else + QueryQueue.Add(Servers[i].Address + ':' + IntToStr(Servers[i].Port) + '#' + IntToStr(Servers[i].Tag)); + //QueryServerInfo(Servers[i].Address + ':' + IntToStr(Servers[i].Port), true, true, false, false); + end; + CloseFile(ImportFile); + + UpdateServers; end; - - - procedure TfmMain.ExportFavoritesClick(Sender: TObject); +var + SD: TSaveDialog; + fmExportFavorites: TfmExportFavorites; + ExportPasswords: Boolean; begin - // TODO: TfmMain.ExportFavoritesClick + tsServerLists.TabIndex:= 0; + + SD:= TSaveDialog.Create(Self); + SD.DefaultExt:= 'fav'; + SD.Filter:= 'SA-MP Favorites List (*.fav)|*.fav'; + SD.Options:= [ofHideReadOnly,ofEnableSizing]; + SD.Title:= 'Export Favorites'; + if not SD.Execute then begin + SD.Free; + Exit; + end else begin + if FileExists(SD.FileName) then begin + if MessageDlg('File '''+SD.FileName+''' already exists. Overwrite?', mtConfirmation, [mbYes, mbNo], 0) = mrNo then begin + SD.Free; + Exit; + end; + end; + end; + + fmExportFavorites:= TfmExportFavorites.Create(Application); + fmExportFavorites.ShowModal; + ExportPasswords:= fmExportFavorites.cbIncludeSavedPasswords.Checked; + fmExportFavorites.Free; + + ExportFavorites(SD.FileName, ExportPasswords); + SD.Free; end; -procedure TfmMain.ExportFavorites; +procedure TfmMain.ExportFavorites(FileName: String; ExportPasswords: Boolean); +var + ExportFile: File; + Temp, i: Integer; begin - // TODO: TfmMain.ExportFavorites + AssignFile(ExportFile, FileName); + FileMode:= 1; + Rewrite(ExportFile, 1); + BlockWrite(ExportFile, FileTag[0], 4); + Temp:= FavoritesFileVersion; + BlockWrite(ExportFile, Temp, 4); + Temp:= Length(Servers); + BlockWrite(ExportFile, Temp, 4); + for i:= 0 to Length(Servers)-1 do begin + Temp:= Length(Servers[i].Address); + BlockWrite(ExportFile, Temp, 4); + if Temp <> 0 then + BlockWrite(ExportFile, Servers[i].Address[1], Temp); + BlockWrite(ExportFile, Servers[i].Port, 4); + Temp:= Length(Servers[i].HostName); + BlockWrite(ExportFile, Temp, 4); + BlockWrite(ExportFile, Servers[i].HostName[1], Temp); + if ExportPasswords then begin + Temp:= Length(Servers[i].ServerPassword); + BlockWrite(ExportFile, Temp, 4); + if Temp <> 0 then + BlockWrite(ExportFile, Servers[i].ServerPassword[1], Temp); + Temp:= Length(Servers[i].RconPassword); + BlockWrite(ExportFile, Temp, 4); + if Temp <> 0 then + BlockWrite(ExportFile, Servers[i].RconPassword[1], Temp); + end else begin + Temp:= 0; + BlockWrite(ExportFile, Temp, 4); + BlockWrite(ExportFile, Temp, 4); + end; + end; + CloseFile(ExportFile); end; procedure TfmMain.ExitClick(Sender: TObject); begin - // TODO: TfmMain.ExitClick + Close; end; procedure TfmMain.miViewClick(Sender: TObject); begin - // TODO: TfmMain.miViewClick + miFilterServerInfo.Checked:= Filtered; + miStatusBar.Checked:= sbMain.Visible; end; procedure TfmMain.ToggleFilterServerInfo(Sender: TObject); begin - // TODO: TfmMain.ToggleFilterServerInfo + if Filtered then begin + Filtered:= false; + pnBreakable.Height:= 16; + gbInfo.Caption:= ' Server Info '; + end else begin + Filtered:= true; + pnBreakable.Height:= 100; + pnBreakable.Top:= 0; + if lbServers.ItemIndex <> -1 then + gbInfo.Caption:= ' Server Info: ' + Servers[StrToInt(lbServers.Items.Strings[lbServers.ItemIndex])].HostName + ' '; + end; + UpdateServers; end; procedure TfmMain.ToggleStatusBar(Sender: TObject); begin - // TODO: TfmMain.ToggleStatusBar + sbMain.Visible:= not sbMain.Visible; + sbMain.Top:= 10000; end; procedure TfmMain.ConnectClick(Sender: TObject); +var + Idx: Integer; + SrvPwd: String; + NickName: String; begin - // TODO: TfmMain.ConnectClick + if lbServers.ItemIndex = -1 then Exit; + Idx:= StrToInt(lbServers.Items.Strings[lbServers.ItemIndex]); + if Idx >= Length(Servers) then Exit; + + SrvPwd:= Servers[Idx].ServerPassword; + if Servers[Idx].Passworded then + if not InputQuery('Server Password', 'This server requires a password...', SrvPwd) then + Exit; + if edName.Text = '' then begin + if not InputQuery('Who are you?', 'Enter your nickname/handle...', NickName) then + Exit; + if NickName = '' then + Exit; + edName.Text:= NickName; + end; + + ServerConnect(Servers[Idx].Address, IntToStr(Servers[Idx].Port), SrvPwd); end; procedure TfmMain.AddServerClick(Sender: TObject); +var + Server: String; + Dummy: Boolean; begin - // TODO: TfmMain.AddServerClick + Server:= GetClipBoardStr; + if (MasterFile <> 0) and (lbServers.ItemIndex <> -1) then begin + Server:= Servers[StrToIntDef(lbServers.Items.Strings[lbServers.ItemIndex], 0)].Address + ':' + + IntToStr(Servers[StrToIntDef(lbServers.Items.Strings[lbServers.ItemIndex], 0)].Port); + + tsServerLists.TabIndex:= 0; + tsServerListsChange(tsServerLists, 0, Dummy); + end; + if InputQuery('Add Server', 'Enter new server HOST:PORT...', Server) then + if Server <> '' then begin + AddServer(Server); + end; end; -procedure TfmMain.AddServer; +procedure TfmMain.AddServer(Server: String); +var + i, j: Integer; + Dupe: Boolean; begin - // TODO: TfmMain.AddServer + Server:= Trim(Server); + i:= Length(Servers); + SetLength(Servers, i + 1); + if Pos(':', Server) <> 0 then begin + Servers[i].Address:= Copy(Server, 1, Pos(':', Server)-1); + Servers[i].Port:= StrToIntDef(Copy(Server, Pos(':', Server)+1, 5), 7777); + end else begin + Servers[i].Address:= Server; + Servers[i].Port:= 7777; + end; + Servers[i].HostName:= '(Retrieving info...) ' + Servers[i].Address + ':' + IntToStr(Servers[i].Port); + Dupe:= False; + for j:= 0 to i-1 do + if (Servers[j].Address = Servers[i].Address) and (Servers[j].Port = Servers[i].Port) then begin + SetLength(Servers, i); + MessageDlg('This server is already on your list.', mtError, [mbOk], 0); + Dupe:= True; + end; + if not Dupe then begin + //QueryServerInfo(Server, true, true, false, false); + QueryQueue.Add(Server); + ExportFavorites(sub_4E1DA8 + 'USERDATA.DAT', true); + FavoritesChanged:= true; + end; + UpdateServers; end; procedure TfmMain.DeleteServerClick(Sender: TObject); +var + Idx, i: Integer; begin - // TODO: TfmMain.DeleteServerClick + if lbServers.ItemIndex = -1 then Exit; + if tsServerLists.TabIndex <> 0 then Exit; + + Idx:= StrToInt(lbServers.Items.Strings[lbServers.ItemIndex]); + lbServers.Items.Delete(lbServers.ItemIndex); + for i:= Idx to Length(Servers)-2 do + Servers[i]:= Servers[i+1]; + SetLength(Servers, Length(Servers)-1); + UpdateServers; + + ExportFavorites(sub_4E1DA8 + 'USERDATA.DAT', true); + FavoritesChanged:= true; end; procedure TfmMain.RefreshServerClick(Sender: TObject); +var + Idx: Integer; begin - // TODO: TfmMain.RefreshServerClick + if lbServers.ItemIndex = -1 then Exit; + Idx:= StrToInt(lbServers.Items.Strings[lbServers.ItemIndex]); + if Idx >= Length(Servers) then Exit; + + QueryServerInfo(Servers[Idx].Address + ':' + IntToStr(Servers[Idx].Port) + '#' + IntToStr(Servers[Idx].Tag), true, true, true, true); end; procedure TfmMain.MasterServerUpdateClick(Sender: TObject); +var + SL: TStringList; + i: Integer; + IdHTTP: TIdHTTP; + fmMasterUpdate: TfmMasterUpdate; begin - // TODO: TfmMain.MasterServerUpdateClick + fmMasterUpdate:= TfmMasterUpdate.Create(Application); + fmMasterUpdate.Show; + Enabled:= false; + Application.ProcessMessages; + + IdHTTP:= TIdHTTP.Create(Application); + with IdHTTP do begin + ReadTimeout:= 20000; // 20secs + AllowCookies:= true; + + ProxyParams.BasicAuthentication:= False; + ProxyParams.ProxyPort:= 0; + + Port:= 80; + Host:= 'lists.sa-mp.com'; + Request.Host:= IdHTTP.Host; + + Request.ContentLength:= -1; + Request.ContentRangeEnd:= 0; + Request.ContentRangeStart:= 0; + Request.ContentType:= 'text/html'; + Request.Accept:= 'text/html, */*'; + Request.BasicAuthentication:= False; + Request.UserAgent:= 'Mozilla/3.0 (compatible; SA:MP v0.3.7)'; + + HTTPOptions:= [hoForceEncodeParams]; + end; + + SL:= TStringList.Create; + try + if MasterFile = 1 then + SL.Text:= IdHTTP.Get('/0.3.7/internet') + else if MasterFile = 2 then + SL.Text:= IdHTTP.Get('/0.3.7/hosted'); + except + // STATUS: BAD RESPONCE FROM MASTER SERVER + end; + IdHTTP.Free; + lbServers.Clear; + lbPlayers.Clear; + lbRules.Clear; + SetLength(Servers, 0); + tmrQueryQueueProcess.Enabled := false; + tmrServerListUpdate.Enabled := false; + SetLength(Servers, SL.Count); + + Enabled:= true; + fmMasterUpdate.Close; + fmMasterUpdate.Free; + + for i:= 0 to SL.Count -1 do begin + //Application.ProcessMessages; + Servers[i].HostName:= '(Retrieving info...) ' + SL.Strings[i]; + Servers[i].Address:= Copy(SL.Strings[i], 1, Pos(':', SL.Strings[i])-1); + Servers[i].Port:= StrToIntDef(Copy(SL.Strings[i], Pos(':', SL.Strings[i])+1, 5), 7777); + Servers[i].Ping:= 9999; + Servers[i].Tag:= Random($FFFF); + //QueryServerInfo(SL.Strings[i], true, true, false, false); + //Sleep(10); + QueryQueue.Add(SL.Strings[i] + '#' + IntToStr(Servers[i].Tag)); + end; + + tmrQueryQueueProcess.Enabled := true; + tmrServerListUpdate.Enabled := true; + SL.Free; + dword_4EF08C.Clear; + + //UpdateServers; + end; procedure TfmMain.CopyServerInfoClick(Sender: TObject); +var + Idx: Integer; + Str: String; begin - // TODO: TfmMain.CopyServerInfoClick + if lbServers.ItemIndex = -1 then Exit; + Idx:= StrToInt(lbServers.Items.Strings[lbServers.ItemIndex]); + if Idx >= Length(Servers) then Exit; + + Str:= 'HostName: ' + Servers[Idx].HostName + #13#10 + + 'Address: ' + Servers[Idx].Address + ':' + IntToStr(Servers[Idx].Port) + #13#10 + + 'Players: ' + IntToStr(Servers[Idx].Players) + ' / ' + IntToStr(Servers[Idx].MaxPlayers) + #13#10 + + 'Ping: ' + IntToStr(Servers[Idx].Ping) + #13#10 + + 'Mode: ' + Servers[Idx].Mode + #13#10 + + 'Language: ' + Servers[Idx].Map; + SetClipBoardStr(Str); end; procedure TfmMain.ServerPropertiesClick(Sender: TObject); +var + fmServerProperties: TfmServerProperties; + Idx: Integer; begin - // TODO: TfmMain.ServerPropertiesClick + if lbServers.ItemIndex = -1 then Exit; + + Idx:= StrToInt(lbServers.Items.Strings[lbServers.ItemIndex]); + if Idx >= Length(Servers) then Exit; + + fmServerProperties:= TfmServerProperties.Create(Application); + with fmServerProperties do begin + lbHostName.Caption:= Servers[Idx].HostName; + edAddress.Text:= Servers[Idx].Address + ':' + IntToStr(Servers[Idx].Port); + lbPlayers.Caption:= IntToStr(Servers[Idx].Players) + ' / ' + IntToStr(Servers[Idx].MaxPlayers); + lbPing.Caption:= IntToStr(Servers[Idx].Ping); + lbMode.Caption:= Servers[Idx].Mode; + lbMap.Caption:= Servers[Idx].Map; + edServerPassword.Text:= Servers[Idx].ServerPassword; + edRconPassword.Text:= Servers[Idx].RconPassword; + edServerPassword.Enabled:= Servers[Idx].Passworded; + if not Servers[Idx].Passworded then + edServerPassword.Color:= clBtnFace; + ShowModal; + Free; + end; end; procedure TfmMain.RemoteConsoleClick(Sender: TObject); +var + fmRconConfig: TfmRconConfig; + Idx: Integer; begin - // TODO: TfmMain.RemoteConsoleClick + fmRconConfig:= TfmRconConfig.Create(Application); + if lbServers.ItemIndex <> -1 then begin + Idx:= StrToInt(lbServers.Items.Strings[lbServers.ItemIndex]); + fmRconConfig.edHost.Text:= Servers[Idx].Address + ':' + IntToStr(Servers[Idx].Port); + fmRconConfig.edPassword.Text:= Servers[Idx].RconPassword; + end; + fmRconConfig.ShowModal; + fmRconConfig.Free; end; procedure TfmMain.SettingsClick(Sender: TObject); +var + fmSettings: TfmSettings; begin - // TODO: TfmMain.SettingsClick + fmSettings:= TfmSettings.Create(Application); + fmSettings.ShowModal; + fmSettings.Free; end; procedure TfmMain.HelpTopicsClick(Sender: TObject); begin - // TODO: TfmMain.HelpTopicsClick + ShellExecute(Handle, 'open', 'http://wiki.sa-mp.com/', nil, nil, SW_SHOWNORMAL); end; procedure TfmMain.AboutClick(Sender: TObject); begin - // TODO: TfmMain.AboutClick + fmAbout.ShowModal; end; procedure TfmMain.tsServerListsChange(Sender: TObject; NewTab: Integer; var AllowChange: Boolean); begin - // TODO: TfmMain.tsServerListsChange + if (tsServerLists.TabIndex = 0) and (FavoritesChanged = True) then begin + ExportFavorites(sub_4E1DA8 + 'USERDATA.DAT', true); + FavoritesChanged:= False; + end; + + QueryQueue.Clear; + lbServers.Clear; + lbPlayers.Clear; + lbRules.Clear; + SetLength(Servers, 0); + + if NewTab = 0 then begin + if FileExists(sub_4E1DA8 + 'USERDATA.DAT') then + ImportFavorites(sub_4E1DA8 + 'USERDATA.DAT', false); + end; + + { + if NewTab <> 0 then begin + lbServers.Clear; + SetLength(Servers, 0); + end; + } + + UpdateServers; + + tbMasterServerUpdate.Enabled:= NewTab <> 0; + miMasterServerUpdate.Enabled:= NewTab <> 0; + + //tbAddServer.Enabled:= NewTab = 0; + miAddServer.Enabled:= NewTab = 0; + tbDeleteServer.Enabled:= NewTab = 0; + miDeleteServer.Enabled:= NewTab = 0; + piDeleteServer.Visible:= NewTab = 0; + + if lbServers.Count > 0 then + lbServers.ItemIndex:= 0; + MasterFile:= NewTab; + lbServersClick(Self); + if NewTab <> 0 then + MasterServerUpdateClick(Self); + dword_4EF08C.Clear; end; -procedure TfmMain.QueryServerInfoParse; +procedure TfmMain.QueryServerInfoParse(SrcIP: String; SrcPort: Word; Buf: PAnsiChar; DataLen: Integer); +var + StrIP: String; + Tag: Word; + i, j, Idx: Integer; + Magic: array[0..3] of char; + ping: Cardinal; + TempWord: Word; + TempCard: Cardinal; + TempByte: Byte; + BufPos: Integer; + Value: Double; + RepaintServerList, + RepaintPlayerList, + RepaintRulesList: Boolean; + TempInt: Integer; + Port: Word; begin - // TODO: TfmMain.QueryServerInfoParse + + + if DataLen < 11 then Exit; // 10b is min size: 4b magic, 4b ip, 2b port, 1b id + + Move(Buf[0], Magic[0], 4); + if Magic <> 'SAMP' then Exit; + + StrIP:= IntToStr(Byte(Buf[4]))+'.'+IntToStr(Byte(Buf[5]))+'.'+ + IntToStr(Byte(Buf[6]))+'.'+IntToStr(Byte(Buf[7])); + + Move(Buf[8], Port, 2); + + Tag:= SrcPort; + + if SrcIP <> StrIP then Exit; + + Idx:= -1; + for i:= 0 to Length(Servers)-1 do begin + if Servers[i].DottedAddress = '' then begin + if not Servers[i].HasAddress then begin + Servers[i].DottedAddress:= GetIPFromHost(Servers[i].Address); + Servers[i].HasAddress:= true; + end; + end; + if (Servers[i].Address = SrcIP) or (Servers[i].DottedAddress = SrcIP) then begin + if Servers[i].Port = SrcPort then begin + Idx:= i; + break; + end; + end; + if (Servers[i].Address = SrcIP) and (Servers[i].Port = SrcPort) then begin + Idx:= i; + break; + end; + end; + + if Idx = -1 then begin + //fmMain.Caption:= 'Server not found ' + StrIP; + Exit; + end; + + if (Servers[i].Tag <> 0) and (Servers[i].Tag <> Port) then begin + //fmMain.Caption:= 'Invalid tag for ' + StrIP; + Exit; + end; + + RepaintServerList:= false; + RepaintPlayerList:= false; + RepaintRulesList:= false; + + case Buf[10] of // PacketID + 'p': // Ping + begin + //OutputDebugString(PChar('[P] Packet from ' + Servers[Idx].Address + ':' + IntToStr(Servers[Idx].Port))); + if DataLen = 15 then begin + Move(Buf[11], ping, 4); + timeBeginPeriod(5); + Servers[Idx].Ping:= timeGetTime - ping; + + Value:= Servers[Idx].Ping; + if Value < 1 then Value:= 1; + i:= chSIPingChart.Series[0].AddY(Value, '', clBlue); + if i > 60 then begin + for j:= 1 to 61 do begin + chSIPingChart.Series[0].YValue[j-1]:= chSIPingChart.Series[0].YValue[j]; + end; + chSIPingChart.Series[0].Delete(61); + end; + + chSIPingChart.LeftAxis.Maximum:= chSIPingChart.Series[0].MaxYValue / 2 * 3; + + timeEndPeriod(5); + + RepaintServerList:= true; + end; + end; + + 'i': // Info + begin + BufPos:= 11; + + //OutputDebugString(PChar('[I] Packet from ' + Servers[Idx].Address + ':' + IntToStr(Servers[Idx].Port))); + + Move(Buf[BufPos], TempByte, 1); + Inc(BufPos, 1); + Servers[Idx].Passworded:= TempByte <> 0; + + Move(Buf[BufPos], TempWord, 2); + if TempWord > 1000 then TempWord:= 1000; + Inc(BufPos, 2); + Servers[Idx].Players:= TempWord; + + Move(Buf[BufPos], TempWord, 2); + if TempWord > 1000 then TempWord:= 1000; + Inc(BufPos, 2); + Servers[Idx].MaxPlayers:= TempWord; + + if Servers[Idx].Players > Servers[Idx].MaxPlayers then + Servers[Idx].Players:= Servers[Idx].MaxPlayers; + + Servers[Idx].HostName:= '-'; + Servers[Idx].Mode:= '-'; + Servers[Idx].Map:= '-'; + + Move(Buf[BufPos], TempCard, 4); + if (TempCard > 0) and (TempCard < 64) then begin + Inc(BufPos, 4); + SetLength(Servers[Idx].HostName, TempCard); + Move(Buf[BufPos], Servers[Idx].HostName[1], TempCard); + Inc(BufPos, TempCard); + + Move(Buf[BufPos], TempCard, 4); + if (TempCard > 0) and (TempCard < 40) then begin + Inc(BufPos, 4); + SetLength(Servers[Idx].Mode, TempCard); + Move(Buf[BufPos], Servers[Idx].Mode[1], TempCard); + Inc(BufPos, TempCard); + + Move(Buf[BufPos], TempCard, 4); + if (TempCard > 0) and (TempCard < 40) then begin + Inc(BufPos, 4); + SetLength(Servers[Idx].Map, TempCard); + Move(Buf[BufPos], Servers[Idx].Map[1], TempCard); + //Inc(BufPos, TempCard); + end; + end; + end; + + RepaintServerList:= true; + + QueryServerInfo(Servers[Idx].Address+':'+IntToStr(Servers[Idx].Port)+'#'+IntToStr(Servers[Idx].Tag), true, false, false, false); + end; + + 'c': // Players + begin + BufPos:= 11; + //OutputDebugString(PChar('[C] Packet from ' + Servers[Idx].Address + ':' + IntToStr(Servers[Idx].Port))); + Move(Buf[BufPos], TempWord, 2); + if TempWord > 100 then TempWord:= 100; + Inc(BufPos, 2); + Servers[Idx].Players:= TempWord; + SetLength(Servers[Idx].aPlayers, TempWord); + + for i:= 0 to TempWord -1 do begin + Move(Buf[BufPos], TempByte, 1); + Inc(BufPos, 1); + if BufPos > DataLen then break; + SetLength(Servers[Idx].aPlayers[i].Name, TempByte); + Move(Buf[BufPos], Servers[Idx].aPlayers[i].Name[1], TempByte); + Inc(BufPos, TempByte); + if BufPos > DataLen then break; + Move(Buf[BufPos], TempInt, 4); + if TempInt > 1000000 then TempInt:= 1000000; + if TempInt < 0 then TempInt:= 0; + Servers[Idx].aPlayers[i].Score:= TempInt; + Inc(BufPos, 4); + if BufPos > DataLen then break; + end; + + RepaintPlayerList:= true; + end; + + 'r': // Rules + begin + BufPos:= 11; + //OutputDebugString(PChar('[R] Packet from ' + Servers[Idx].Address + ':' + IntToStr(Servers[Idx].Port))); + Move(Buf[BufPos], TempWord, 2); + if TempWord > 30 then TempWord:= 30; + Inc(BufPos, 2); + SetLength(Servers[Idx].aRules, TempWord); + + for i:= 0 to TempWord -1 do begin + if BufPos > DataLen then break; + Move(Buf[BufPos], TempByte, 1); + Inc(BufPos, 1); + if BufPos > DataLen then break; + SetLength(Servers[Idx].aRules[i].Rule, TempByte); + Move(Buf[BufPos], Servers[Idx].aRules[i].Rule[1], TempByte); + Inc(BufPos, TempByte); + + if BufPos > DataLen then break; + + Move(Buf[BufPos], TempByte, 1); + Inc(BufPos, 1); + if BufPos > DataLen then break; + SetLength(Servers[Idx].aRules[i].Value, TempByte); + Move(Buf[BufPos], Servers[Idx].aRules[i].Value[1], TempByte); + Inc(BufPos, TempByte); + end; + + RepaintRulesList:= true; + end; + end; + + if QueryQueue.Count <= 0 then + UpdateServers; + + if RepaintServerList then + lbServers.Invalidate; + if RepaintPlayerList then + lbPlayers.Invalidate; + if RepaintRulesList then + lbRules.Invalidate; end; -procedure TfmMain.QueryServerInfoError; +procedure TfmMain.QueryServerInfoError(SocketError: Integer); +var + err: array[0..512] of Char; begin - // TODO: TfmMain.QueryServerInfoError + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nil, SocketError, 0, @err, 512, nil); + MessageDlg(err, mtError, [mbOk], 0); end; -procedure TfmMain.QueryServerInfo; +procedure TfmMain.QueryServerInfo(Server: String; bPing: Boolean; bInfo: Boolean; bPlayers: Boolean; bRules: Boolean); +var + Buf: PByteArray; + Ticks: Cardinal; + + ToAddr: TSockAddr; + ToLen: Integer; + + Host: String; + Port: Word; + Tag: Word; + + ColPos, TagPos: Integer; begin - // TODO: TfmMain.QueryServerInfo + Tag:= 0; + + if Pos(':', Server) <> 0 then begin + if Pos('#', Server) <> 0 then begin + ColPos:= Pos(':', Server); + TagPos:= Pos('#', Server); + + Host:= Copy(Server, 1, ColPos-1); + Port:= StrToIntDef(Copy(Server, ColPos+1, TagPos-(ColPos+1)), 7777); + Tag:= StrToIntDef(Copy(Server, TagPos+1, Length(Server)-TagPos), 0); + end else begin + ColPos:= Pos(':', Server); + + Host:= Copy(Server, 1, ColPos-1); + Port:= StrToIntDef(Copy(Server, ColPos+1, Length(Server)-ColPos+1), 7777); + end; + end else begin + Host:= Server; + Port:= 7777; + end; + + if Tag = 0 then + Tag:= Port; + + Host:= GetIPFromHost(Host); + if (Length(Host) < 7) or (Length(Host) > 15) then + Exit; + + GetMem(Buf, 15); + Buf[0]:= Byte('S'); // Magic + Buf[1]:= Byte('A'); + Buf[2]:= Byte('M'); + Buf[3]:= Byte('P'); + + Buf[4]:= StrToIntDef(GetToken(Host, 1, '.'), 0); // IP + Buf[5]:= StrToIntDef(GetToken(Host, 2, '.'), 0); + Buf[6]:= StrToIntDef(GetToken(Host, 3, '.'), 0); + Buf[7]:= StrToIntDef(GetToken(Host, 4, '.'), 0); + + //Move(Port, Buf[8], 2); // Port + + ZeroMemory(@ToAddr, SizeOf(ToAddr)); + ToAddr.sin_family:= AF_INET; + ToAddr.sin_port:= htons(Port); + ToAddr.sin_addr.S_addr:= inet_addr(PChar(Host)); + ToLen:= SizeOf(ToAddr); + + Move(Tag, Buf[8], 2); // Tag + + if bInfo = true then begin + Buf[10]:= Byte('i'); // Info Packet Id + sendto(QuerySocket, Buf[0], 11, 0, ToAddr, ToLen); + Sleep(1); + end; + + if bPing = true then begin + Buf[10]:= Byte('p'); // Ping Packet Id + timeBeginPeriod(1); + Ticks:= timeGetTime; + timeEndPeriod(1); + Move(Ticks, Buf[11], 4); + sendto(QuerySocket, Buf[0], 15, 0, ToAddr, ToLen); + Sleep(1); + end; + + if bPlayers = true then begin + Buf[10]:= Byte('c'); // Players Packet Id + sendto(QuerySocket, Buf[0], 11, 0, ToAddr, ToLen); + Sleep(1); + end; + + if bRules = true then begin + Buf[10]:= Byte('r'); // Rules Packet Id + sendto(QuerySocket, Buf[0], 11, 0, ToAddr, ToLen); + Sleep(1); + end; + + FreeMem(Buf, 15); end; -procedure TfmMain.ServerConnect; +procedure TfmMain.ServerConnect(Server: String; Port: String; Password: String); +procedure GetDebugPrivs; +var + hToken: THandle; + xTokenPriv: TTokenPrivileges; + iRetLen: DWord; begin - // TODO: TfmMain.ServerConnect + If OpenProcessToken(GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, hToken) Then Begin + LookupPrivilegeValue(nil, 'SeDebugPrivilege', xTokenPriv.Privileges[0].Luid); + xTokenPriv.PrivilegeCount:= 1; + xTokenPriv.Privileges[0].Attributes:= SE_PRIVILEGE_ENABLED; + AdjustTokenPrivileges(hToken, False, xTokenPriv, 0, nil, iRetLen); + End; +end; +const + ACL_REVISION = 2; +var + StartInfo: TStartupInfo; + ProcInfo: TProcessInformation; + Created: Boolean; + hThread: THandle; + pLibRemote: Pointer; + NumBytes, ThreadID: Cardinal; + CmdLine, SAMP_DLL: String; +begin + if not FileExists(gta_sa_exe) then begin + MessageDlg('GTA: San Andreas executable not found.'#13#10'('+gta_sa_exe+')'#13#10#13#10'Please locate it now.', mtError, [mbOk], 0); + GetGTAExe(Handle); + end; + if not FileExists(gta_sa_exe) then begin + MessageDlg('GTA: San Andreas executable STILL not found.'#13#10'('+gta_sa_exe+')'#13#10#13#10'Aborting launch.', mtError, [mbOk], 0); + Exit; + end; + + FillChar(StartInfo, SizeOf(TStartupInfo), 0); + FillChar(ProcInfo, SizeOf(TProcessInformation), 0); + StartInfo.cb:= SizeOf(TStartupInfo); + + CmdLine:= ' -c -n ' + edName.Text + ' -h ' + GetIPFromHost(Server) + ' -p ' + Port; + if Password <> '' then + CmdLine:= CmdLine + ' -z ' + Password; + + Created:= CreateProcess(nil, PChar('"' + gta_sa_exe + '"' + CmdLine), {@SA}nil, nil, false, + CREATE_NEW_PROCESS_GROUP+NORMAL_PRIORITY_CLASS+CREATE_SUSPENDED, + nil, PChar(ExtractFilePath(gta_sa_exe)), StartInfo, ProcInfo); + + if not Created then begin + MessageDlg('Unable to execute.', mtError, [mbOk], 0); + Exit; + end; + + if RT_GetVersion(nil) shr 31 = 0 then + GetDebugPrivs; + + SAMP_DLL:= ExtractFilePath(gta_sa_exe) + 'samp.dll'; + SetLength(SAMP_DLL, Length(SAMP_DLL)+1); + SAMP_DLL[Length(SAMP_DLL)]:= #0; + + pLibRemote:= xVirtualAllocEx(ProcInfo.hProcess, nil, MAX_PATH, MEM_COMMIT, PAGE_READWRITE); + WriteProcessMemory(ProcInfo.hProcess, pLibRemote, PChar(SAMP_DLL), Length(SAMP_DLL), NumBytes); + hThread:= xCreateRemoteThread(ProcInfo.hProcess, nil, 0, GetProcAddress(GetModuleHandle('kernel32'), 'LoadLibraryA'), pLibRemote, 0, ThreadID); + + WaitForSingleObject(hThread, 2000); + CloseHandle(hThread); + VirtualFreeEx(ProcInfo.hProcess, pLibRemote, MAX_PATH, MEM_RELEASE); + ResumeThread(ProcInfo.hThread); + //CloseHandle(ProcInfo.hProcess); end; procedure TfmMain.piCopyClick(Sender: TObject); begin - // TODO: TfmMain.piCopyClick + SetClipBoardStr(edSIAddress.Text); end; procedure TfmMain.pmCopyPopup(Sender: TObject); begin - // TODO: TfmMain.pmCopyPopup + piCopy.Enabled:= edSIAddress.Text <> '- - -'; end; procedure TfmMain.lbPlayersDrawItem(Control: TWinControl; Index: Integer; Rect: TRect; State: TOwnerDrawState); +var + Idx: Integer; + TempRect: TRect; begin - // TODO: TfmMain.lbPlayersDrawItem + if lbServers.ItemIndex = -1 then Exit; + + with (Control as TListBox) do begin + Idx:= StrToIntDef(lbServers.Items.Strings[lbServers.ItemIndex], 0); + if Idx >= Length(Servers) then Exit; + + if Index >= Length(Servers[Idx].aPlayers) then + Exit; + + Canvas.Pen.Color:= clBtnHighlight; + Canvas.Pen.Style:= psClear; + + if odSelected in State then begin + Canvas.Font.Color:= clHighlightText; + Canvas.Brush.Color:= clHighlight; + end else begin + Canvas.Font.Color:= clWindowText; + if (Index mod 2) = 1 then + Canvas.Brush.Color:= clWindow + else + Canvas.Brush.Color:= sub_4E2628(clWindow); + end; + + Inc(Rect.Right); + Canvas.Rectangle(Rect); + Dec(Rect.Right); + Canvas.Pen.Style:= psSolid; + Canvas.PenPos:= Point(Rect.Right, Rect.Bottom-1); + Canvas.LineTo(Rect.Left, Rect.Bottom-1); + + Canvas.PenPos:= Point(hcPlayers.Sections.Items[0].Right-2, Rect.Top); + Canvas.LineTo(hcPlayers.Sections.Items[0].Right-2, Rect.Bottom); + Canvas.PenPos:= Point(hcPlayers.Sections.Items[1].Right-2, Rect.Top); + Canvas.LineTo(hcPlayers.Sections.Items[1].Right-2, Rect.Bottom); + + TempRect:= Classes.Rect(hcPlayers.Sections.Items[0].Left + 2, Rect.Top + 2, hcPlayers.Sections.Items[0].Right - 2, Rect.Bottom - 2); + DrawText(Canvas.Handle, PChar(Servers[Idx].aPlayers[Index].Name), -1, TempRect, DT_LEFT); + + TempRect:= Classes.Rect(hcPlayers.Sections.Items[1].Left + 2, Rect.Top + 2, hcPlayers.Sections.Items[1].Right - 2, Rect.Bottom - 2); + DrawText(Canvas.Handle, PChar(IntToStr(Servers[Idx].aPlayers[Index].Score)), -1, TempRect, DT_LEFT); + end; end; procedure TfmMain.hcPlayersSectionResize(HeaderControl: THeaderControl; Section: THeaderSection); begin - // TODO: TfmMain.hcPlayersSectionResize + lbPlayers.Repaint; end; procedure TfmMain.lbRulesDrawItem(Control: TWinControl; Index: Integer; Rect: TRect; State: TOwnerDrawState); +var + Idx: Integer; + TempRect: TRect; begin - // TODO: TfmMain.lbRulesDrawItem + if lbServers.ItemIndex = -1 then Exit; + + with (Control as TListBox) do begin + Idx:= StrToIntDef(lbServers.Items.Strings[lbServers.ItemIndex], 0); + if Idx >= Length(Servers) then Exit; + + if Index >= Length(Servers[Idx].aRules) then + Exit; + + Canvas.Pen.Color:= clBtnHighlight; + Canvas.Pen.Style:= psClear; + + if odSelected in State then begin + Canvas.Font.Color:= clHighlightText; + Canvas.Brush.Color:= clHighlight; + end else begin + Canvas.Font.Color:= clWindowText; + if (Index mod 2) = 1 then + Canvas.Brush.Color:= clWindow + else + Canvas.Brush.Color:= sub_4E2628(clWindow); + end; + + Inc(Rect.Right); + Canvas.Rectangle(Rect); + Dec(Rect.Right); + Canvas.Pen.Style:= psSolid; + Canvas.PenPos:= Point(Rect.Right, Rect.Bottom-1); + Canvas.LineTo(Rect.Left, Rect.Bottom-1); + + Canvas.PenPos:= Point(hcRules.Sections.Items[0].Right-2, Rect.Top); + Canvas.LineTo(hcRules.Sections.Items[0].Right-2, Rect.Bottom); + Canvas.PenPos:= Point(hcRules.Sections.Items[1].Right-2, Rect.Top); + Canvas.LineTo(hcRules.Sections.Items[1].Right-2, Rect.Bottom); + + TempRect:= Classes.Rect(hcRules.Sections.Items[0].Left + 2, Rect.Top + 2, hcRules.Sections.Items[0].Right - 2, Rect.Bottom - 2); + DrawText(Canvas.Handle, PChar(Servers[Idx].aRules[Index].Rule), -1, TempRect, DT_LEFT); + + TempRect:= Classes.Rect(hcRules.Sections.Items[1].Left + 2, Rect.Top + 2, hcRules.Sections.Items[1].Right - 2, Rect.Bottom - 2); + DrawText(Canvas.Handle, PChar(Servers[Idx].aRules[Index].Value), -1, TempRect, DT_LEFT); + end; end; procedure TfmMain.hcRulesSectionResize(HeaderControl: THeaderControl; Section: THeaderSection); begin - // TODO: TfmMain.hcRulesSectionResize + lbRules.Repaint; end; procedure TfmMain.sbMainDrawPanel(StatusBar: TStatusBar; Panel: TStatusPanel; const Rect: TRect); +var + Pcnt: Integer; begin - // TODO: TfmMain.sbMainDrawPanel + StatusBar.Canvas.Brush.Color:= clBtnFace; + StatusBar.Canvas.Rectangle(Rect); + + StatusBar.Canvas.Brush.Color:= $00804000; + Pcnt:= Round(((Rect.Right-Rect.Left) / 100.0) * 75.0); + StatusBar.Canvas.Rectangle(Rect.Left, Rect.Top, Rect.Left+Pcnt, Rect.Bottom); end; procedure TfmMain.lbPlayersExit(Sender: TObject); begin - // TODO: TfmMain.lbPlayersExit + lbPlayers.ItemIndex:= -1; end; procedure TfmMain.lbRulesExit(Sender: TObject); begin - // TODO: TfmMain.lbRulesExit + lbRules.ItemIndex:= -1; end; -procedure TfmMain.WMRecv; +procedure TfmMain.WMRecv(var Message: TMessage); +var + lpBuffer: Array[0..2048] of Char; + BufLen: Integer; + + FromAddr: TSockAddr; + FromLen: Integer; + + SrcAddr: String; + SrcPort: Word; begin - // TODO: TfmMain.WMRecv + ZeroMemory(@lpBuffer, sizeof(lpBuffer)); + + ZeroMemory(@FromAddr, sizeof(FromAddr)); + FromAddr.sin_family:= AF_INET; + FromLen:= SizeOf(FromAddr); + + BufLen:= recvfrom(QuerySocket, lpBuffer, 2048, 0, FromAddr, FromLen); + SrcAddr:= inet_ntoa(FromAddr.sin_addr); + SrcPort:= htons(FromAddr.sin_port); + + while (BufLen > 0) do begin + //OutputDebugString( PChar('[*] of size ' + IntToStr(BufLen)) ); + QueryServerInfoParse(SrcAddr,SrcPort,lpBuffer,BufLen); + ZeroMemory(@lpBuffer, sizeof(lpBuffer)); + BufLen:= recvfrom(QuerySocket, lpBuffer, 2048, 0, FromAddr, FromLen); + SrcAddr:= inet_ntoa(FromAddr.sin_addr); + SrcPort:= htons(FromAddr.sin_port); + end; + end; procedure TfmMain.FormDestroy(Sender: TObject); +var + Reg: TRegistry; begin - // TODO: TfmMain.FormDestroy + Reg:= TRegistry.Create; + Reg.RootKey:= HKEY_CURRENT_USER; + Reg.OpenKey('SOFTWARE\SAMP', true); + Reg.WriteString('PlayerName', edName.Text); + Reg.CloseKey; + Reg.Free; + + CloseSocket(QuerySocket); + ShutDown(QuerySocket, 0); + WSACleanup; + + QueryQueue.Free; + + if (tsServerLists.TabIndex = 0) and (FavoritesChanged = True) then begin + ExportFavorites(sub_4E1DA8 + 'USERDATA.DAT', True); + FavoritesChanged:= False; + end; + + Sleep(0); end; procedure TfmMain.FormShow(Sender: TObject); +var + ServFull, ServAddr, ServPort, ServPass: String; + NewServer: String; begin - // TODO: TfmMain.FormShow + //SetProcessAffinityMask(GetCurrentProcess(),1); + lbServers.DoubleBuffered:= true; + lbPlayers.DoubleBuffered:= true; + lbRules.DoubleBuffered:= true; + lbServers.SetFocus; + + if ParamCount > 0 then begin + ServFull:= ParamStr(1); + if Copy(ServFull, 0, 7) = 'samp://' then begin + if ParamCount > 1 then + ServPass:= ParamStr(2); + ServFull:= StringReplace(ServFull, 'samp://', '', [rfReplaceAll, rfIgnoreCase]); + ServFull:= StringReplace(ServFull, '/', '', [rfReplaceAll, rfIgnoreCase]); + if Pos(':', ServFull) <> 0 then begin + ServAddr:= Copy(ServFull, 0, Pos(':', ServFull)-1); + /////////////////////////////////////////////// + // + // Delphi 7 compiler bug(?) + // + // With this structure: + // ServPort:= IntToStr(StrToIntDef(Copy(ServFull, Pos(':', ServFull)+1, Length(ServFull)-Pos(':', ServFull)+1), 7777)); + // the compiler seems to gets confused and will not generate + // LStrLAsg call after Sysutils::IntToStr to set ServPort variable. + // Workaround is to break down the structure. + // + /////////////////////////////////////////////// + ServPort:= Copy(ServFull, Pos(':', ServFull)+1, Length(ServFull)-Pos(':', ServFull)+1); + ServPort:= IntToStr(StrToIntDef(ServPort, 7777)); + end else begin + ServAddr:= Copy(ServFull, 0, Length(ServFull)); + ServPort:= '7777'; + end; + wnd_webrunform.Label1.Caption:= 'Do you want to add ' + ServAddr + ':' + ServPort + ' to your favorites ' + #13#10 + 'or play on this server now?'; + Case wnd_webrunform.ShowModal Of + mrOk: begin + ServerConnect(ServAddr, ServPort, ServPass); + Application.Terminate; + end; + mrYes: begin + sub_4E1CEC; + NewServer:= ServAddr + ':' + ServPort; + if InputQuery('Add Server', 'Enter new server HOST:PORT...', NewServer) <> False then + if NewServer <> '' then + AddServer(NewServer); + end; + mrCancel: ; + end; + end else begin + ServFull:= ParamStr(1); + if ParamCount > 1 then + ServPass:= ParamStr(2); + if Pos(':', ServFull) <> 0 then begin + ServAddr:= Copy(ServFull, 1, Pos(':', ServFull)-1); + ServPort:= IntToStr(StrToIntDef(Copy(ServFull, Pos(':', ServFull)+1, 5), 7777)); + end else begin + ServAddr:= ServFull; + ServPort:= '7777'; + end; + ServerConnect(ServAddr, ServPort, ServPass); + Application.Terminate; + end; + end; + sub_4E1CEC; end; -procedure TfmMain.BrowseForFolder; +function BrowseCallbackProc(hwnd: HWND; uMsg: UINT; lParam, lpData: LPARAM): Integer; stdcall; begin - // TODO: TfmMain.BrowseForFolder + case uMsg of + BFFM_INITIALIZED: + Begin + SetWindowText(hWnd, 'GTA: San Andreas Installation'); + SendMessage(hwnd, BFFM_SETSELECTION, 1, lpData); + End; + end; + Result:= 0; +end; + +function TfmMain.BrowseForFolder(Owner: HWND; var Directory: String; StartDir, Title: String): Boolean; + var + BrowseInfo: TBrowseInfo; + DisplayName: Array[0..MAX_PATH] Of Char; + TempPath : Array[0..MAX_PATH] Of Char; +begin + Result:= False; + FillChar(BrowseInfo, SizeOf(TBrowseInfo), #0); + With BrowseInfo Do Begin + hwndOwner:= Owner; + pidlRoot:= nil; + pszDisplayName:= @DisplayName; + lpszTitle:= PChar(Title); + ulFlags:= BIF_RETURNONLYFSDIRS; + lParam:= Integer(PChar(StartDir)); + lpfn:= BrowseCallbackProc; + End; + If SHGetPathFromIDList(SHBrowseForFolder(BrowseInfo), TempPath) Then Begin + Directory:= TempPath; + Result:= True; + End; end; procedure TfmMain.miSampClick(Sender: TObject); begin - // TODO: TfmMain.miSampClick + ShellExecute(Handle, 'open', 'http://www.sa-mp.com/', nil, nil, SW_SHOWNORMAL); end; procedure TfmMain.tmrQueryQueueProcessTimer(Sender: TObject); begin - // TODO: TfmMain.tmrQueryQueueProcessTimer + //Application.ProcessMessages; + if QueryQueue.Count > 0 then begin + QueryServerInfo(QueryQueue.Strings[0], false, true, false, false); + //QueryServerInfo(QueryQueue.Strings[0], true, false, false, false); + //UpdateServers; + QueryQueue.Delete(0); + end; end; procedure TfmMain.label_urlClick(Sender: TObject); begin - // TODO: TfmMain.label_urlClick + ShellExecute(0, 'open', PAnsiChar('http://' + (Sender as TLabel).Caption), '', '', SW_SHOWNORMAL); end; -procedure TfmMain.CreateFASTDesktoplink1Click; +procedure TfmMain.CreateFASTDesktoplink1Click(Sender: TObject); +var + Idx: Integer; begin - // TODO: TfmMain.CreateFASTDesktoplink1Click + if lbServers.ItemIndex = -1 then Exit; + + Idx:= StrToInt(lbServers.Items.Strings[lbServers.ItemIndex]); + if Idx >= Length(Servers) then Exit; + + sub_4E1E6C(Servers[Idx].Address + ':' + IntToStr(Servers[Idx].Port), Servers[Idx].HostName); end; procedure TfmMain.FormResize(Sender: TObject); begin - // TODO: TfmMain.FormResize + imLogo.Left:= fmMain.Width - imLogo.Width; + imLogo.Repaint; end; procedure TfmMain.imLogoClick(Sender: TObject); begin - // TODO: TfmMain.imLogoClick + fmAbout.ShowModal; end; end. +