编写GOM引擎登录器,直接启动GOM客户端DAT文件不掉线

        最近遇到一个逆向类课题,是关于GOM20151108版本对应登录器研究。刚接触传奇的时候是2002年,那时候网吧玩SF,都是手动用IP直接连接,当时的一款 联创传奇 很好玩,有传送戒子,木域戒指,土域戒指,麻痹戒子,护手戒子,法师的圣言术可以直接把2000厚血的教主直接秒掉,星星战甲这个衣服更是拉风,穿上直接是一个双头精钢。

         现在市场上主流微端模式的技术架构,到处都易语言写的登录器生成器,各种各样的版本。不过对于其中的技术,大部分都是一样的,只是有的插入了自己写的DLL。Delphi程式内存数据结构也比较特殊,这款GOM引擎客户端其实很小,只有3.5M左右。在注册账号、修改密码的功能上目前已经实现,接下来是是做客户端文件的启动,即 Clent.dat这个文件的启动,这个文件很多人被忽视,其实这个文件才是GOM引擎的核心精髓,所谓的引擎也主要是指这个文件,这个文件里面包含了游戏的所有逻辑代码。但是要调用起这个文件可没有那么容易,难点也是在这里。这个文件很多商业登录器生成器上直接隐藏的,被嵌入了登录器里面,dat文件其实也是和EXE结构一样的PE文件,即他们的做法是易语言写的EXE登录器和Delphi写的GOM20151108版本的Client.dat文件合并在一起,玩家点击登录器的时候就显示出登录器,玩家点击登录器界面上的启动游戏按钮后登录器是会创建一个和登录器本身一样的进程,不过这个进程启动界面是游戏画面的客户端程序,即Client.dat在内存中被创建,里面用到了PELoad的技术(也可以说是傀儡进程技术、僵尸进程技术,只是这次这种技术没有将系统进程作为宿主而已,而是将登录器作为了宿主启动了Client.dat)。此刻我们看到任务管理器上有两个登录器进程,其中一个其实不是登录器而是 Client.dat。小小一个登录器原来这么复杂,真是够隐蔽的,技术上其实已经超越了官方的登录器了。

         对于GOM的背景历史暂时不知道,只知道这款引擎非常的出色。但是在2015年11月8日被停止了更新,GOM官方解散了。网上有早期版本的源代码,都是delphi语言写的。但是网上应该没有人拿到2015年11月8日停更版本的源代码。只能靠逆向技术来给这款引擎加上额外的功能和优化了。

    这客户端可不是随随便便都可以调用的,各种校验。经过研究后掉线问题解决了。如下

 

unit Main;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, jpeg, ExtCtrls, RzBmpBtn, StdCtrls, RzLstBox, RzButton, RzRadChk,
  OleCtrls, SHDocVw, ComCtrls, ShlObj, ComObj, ActiveX, Grobal2, JSocket,
  Registry, winsock, WinInet, ShellApi, IniFiles, GameShare, IdBaseComponent,
  IdComponent, IdTCPConnection, IdTCPClient, IdHTTP, {ZLib,} tlHelp32, RzForms;

type
  TSearchThread = class(TThread)
  protected
    procedure Execute; override;
  public
    constructor Create(CreateSuspended: Boolean);
    destructor Destroy; override;
  end;

  TfrmMain = class(TForm)
    RzBmpButtonHomePage: TRzBmpButton;
    RzBmpButtonEditGameList: TRzBmpButton;
    RzBmpButtonNewAccount: TRzBmpButton;
    RzBmpButtonGetBakPassWord: TRzBmpButton;
    RzBmpButtonChgPassWord: TRzBmpButton;
    RzBmpButtonAutoLogin: TRzBmpButton;
    RzBmpButtonFullScreenStart: TRzBmpButton;
    RzBmpButtonClose: TRzBmpButton;
    Timer: TTimer;
    ClientSocket: TClientSocket;
    TimerStart: TTimer;
    CloseTimer: TTimer;
    RzBmpButtonMin: TRzBmpButton;
    TreeView: TTreeView;
    RzBmpButtonStart: TRzBmpButton;
    RzBmpButtonExitGame: TRzBmpButton;
    RzBmpButtonUpgrade: TRzBmpButton;
    Image: TRzFormShape;
    LabelStatus: TLabel;
    TimerReakSkin: TTimer;
    ComboBox: TComboBox;
    procedure TimerTimer(Sender: TObject);
    procedure ClientSocketConnect(Sender: TObject;
      Socket: TCustomWinSocket);
    procedure ClientSocketConnecting(Sender: TObject;
      Socket: TCustomWinSocket);
    procedure ClientSocketDisconnect(Sender: TObject;
      Socket: TCustomWinSocket);
    procedure ClientSocketError(Sender: TObject; Socket: TCustomWinSocket;
      ErrorEvent: TErrorEvent; var ErrorCode: Integer);
    procedure ClientSocketRead(Sender: TObject; Socket: TCustomWinSocket);
    procedure FormCreate(Sender: TObject);
    procedure RzBmpButtonCloseClick(Sender: TObject);
    procedure RzBmpButtonHomePageClick(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure RzBmpButtonFullScreenStartClick(Sender: TObject);
    procedure RzBmpButtonEditGameListClick(Sender: TObject);
    procedure TimerStartTimer(Sender: TObject);
    procedure RzBmpButtonNewAccountClick(Sender: TObject);
    procedure RzBmpButtonGetBakPassWordClick(Sender: TObject);
    procedure RzBmpButtonChgPassWordClick(Sender: TObject);
    procedure RzBmpButtonAutoLoginClick(Sender: TObject);
    procedure CloseTimerTimer(Sender: TObject);
    procedure ImageMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure RzBmpButtonMinClick(Sender: TObject);
    procedure TreeViewClick(Sender: TObject);
    procedure TimerReakSkinTimer(Sender: TObject);
    procedure RzBmpButtonUpgradeClick(Sender: TObject);
  private
    { Private declarations }
    procedure CreateUlr(sCreateUlrName: string);
    procedure DecodeMessagePacket(datablock: string);
    procedure ChgButtonStatus(btStatus: Byte);
    procedure ReleaseClient(sDirectory: string; GameZone: TGameZone);
//    function RunApp(AppName: string; I: Integer): Integer;
    procedure OnProgramException(Sender: TObject; E: Exception);
    function GetProcesses: Boolean;
    function GetModules(ProcessID: DWORD): Boolean;
    procedure ReadSkin;
    function SearchMirClient(Path: string): Boolean;

    procedure LoadUserConfig;
    procedure SaveUserConfig;
  public
    { Public declarations }
    procedure SendGetRandomCode;
    procedure SendUpdateAccount(ue: TUserEntry; ua: TUserEntryAdd; nRandomCode: Integer);
    procedure SendGetBackPassword(sAccount, sQuest1, sAnswer1,
      sQuest2, sAnswer2, sBirthDay: string);
    procedure SendChgPw(sAccount, sPasswd, sNewPasswd: string);
    procedure SendCSocket(sendstr: string);
    procedure LoadListToBox();
    procedure ProcessMessage(var Msg: TMsg; var Handled: Boolean);
    procedure MyMessage(var MsgData: TWmCopyData); message WM_COPYDATA;
  end;

var
  frmMain: TfrmMain;
  MirClient: TMemoryStream;
  WebBrowser: TWebBrowser;
function CheckLegendPath(Path: string): Boolean;
function CheckMirPath(Path: string): Boolean;
function CheckFullPath(Path: string): Boolean;
procedure LoopFiles(Path, Mask: string; SubDir: TStrings);
function GetDrives: string;
function RunApp(AppName: string): Integer;
function SearchPath: Boolean;
implementation
uses EncryptUnit, HUtil32, LNewAccount, LChgPassword, LGetBackPassword {, SecrchInfoMain},
  LEditGame, LUpgrade, { CMain, NPCDialog, NPCMain,} CheckPrevious, ZLibEx;
const
  R_MyRootKey = HKEY_LOCAL_MACHINE; //注册表根键
  R_MySubKey = '\SOFTWARE\cqfir\Legend of mir'; //注册表子键
  R_SndaSubKey = '\SOFTWARE\snda\Legend of mir';
  R_Key = 'Path';
{$R *.dfm}

procedure TSearchThread.Execute;
var
  DriverList: string;
  I, Len: Integer;
begin
  DriverList := GetDrives; //得到可写的磁盘列表 //遍历每个磁盘驱动器
  Len := Length(DriverList);
  for I := 1 to Len do begin
    g_SearchList.Add(DriverList[I] + ':\');
  end;
  while (not Terminated) and (not SearchPath) and (not g_boClose) do begin

  end;
end;

constructor TSearchThread.Create(CreateSuspended: Boolean);
begin
  inherited;

end;

destructor TSearchThread.Destroy;
begin
  inherited;
end;

function ReadRegKey(const iMode: Integer; const sPath,
  sKeyName: string; var sResult: string): Boolean;
var
  rRegObject: TRegistry;
begin
  rRegObject := TRegistry.Create;
  Result := False;
  try
    with rRegObject do begin
      RootKey := R_MyRootKey;
      if OpenKey(sPath, True) then begin
        case iMode of
          1: sResult := Trim(ReadString(sKeyName));
          2: sResult := IntToStr(ReadInteger(sKeyName));
          //3: sResult := ReadBinaryData(sKeyName, Buffer, BufSize);
        end;
        if sResult = '' then Result := False else Result := True;
      end
      else
        Result := False;
      CloseKey;
    end;
  finally
    rRegObject.Free;
  end;
end;
//_____________________________________________________________________//

function WriteRegKey(const iMode: Integer; const sPath, sKeyName,
  sKeyValue: string): Boolean;
var
  rRegObject: TRegistry;
  bData: Byte;
begin
  rRegObject := TRegistry.Create;
  try
    with rRegObject do begin
      RootKey := R_MyRootKey;
      if OpenKey(sPath, True) then begin
        case iMode of
          1: WriteString(sKeyName, sKeyValue);
          2: WriteInteger(sKeyName, StrToInt(sKeyValue));
          3: WriteBinaryData(sKeyName, bData, 1);
        end;
        Result := True;
      end
      else
        Result := False;
      CloseKey;
    end;
  finally
    rRegObject.Free;
  end;
end;
//_____________________________________________________________________//

function SelectDirCB(Wnd: Hwnd; uMsg: UINT; LPARAM, lpData: LPARAM): Integer stdcall;
begin
  if (uMsg = BFFM_INITIALIZED) and (lpData <> 0) then
    SendMessage(Wnd, BFFM_SETSELECTION, Integer(True), lpData);
  Result := 0;
end;

function SelectDirectory(const Caption: string; const Root: WideString;
  var Directory: string; Owner: THandle): Boolean;
var
  WindowList: Pointer;
  BrowseInfo: TBrowseInfo;
  Buffer: PChar;
  RootItemIDList, ItemIDList: PItemIDList;
  ShellMalloc: IMalloc;
  IDesktopFolder: IShellFolder;
  Eaten, Flags: LongWord;
begin
  Result := False;
  if not DirectoryExists(Directory) then
    Directory := '';
  FillChar(BrowseInfo, SizeOf(BrowseInfo), 0);
  if (ShGetMalloc(ShellMalloc) = S_OK) and (ShellMalloc <> nil) then begin
    Buffer := ShellMalloc.Alloc(MAX_PATH);
    try
      RootItemIDList := nil;
      if Root <> '' then begin
        SHGetDesktopFolder(IDesktopFolder);
        IDesktopFolder.ParseDisplayName(Application.Handle, nil,
          POleStr(Root), Eaten, RootItemIDList, Flags);
      end;
      with BrowseInfo do begin
        hwndOwner := Owner;
        pidlRoot := RootItemIDList;
        pszDisplayName := Buffer;
        lpszTitle := PChar(Caption);
        ulFlags := BIF_RETURNONLYFSDIRS;
        if Directory <> '' then begin
          lpfn := SelectDirCB;
          LPARAM := Integer(PChar(Directory));
        end;
      end;
      WindowList := DisableTaskWindows(0);
      try
        ItemIDList := ShBrowseForFolder(BrowseInfo);
      finally
        EnableTaskWindows(WindowList);
      end;
      Result := ItemIDList <> nil;
      if Result then begin
        ShGetPathFromIDList(ItemIDList, Buffer);
        ShellMalloc.Free(ItemIDList);
        Directory := Buffer;
      end;
    finally
      ShellMalloc.Free(Buffer);
    end;
  end;
end;
//_____________________________________________________________________//

function AddString(S: string): string;
begin
  Result := S;
  if S[Length(S)] <> '\' then Result := S + '\';
end;

//获取当前的硬盘所有的盘符
{

Lbl_DriveType:Tlabel;
  DriveType:WORD; //定义驱动器类型变量
  DriveType:=GetDriveType(RootPathName); //获得RootPathName所对应的磁盘驱动器信息
  case DriveType of
  DRIVE_REMOVABLE:Lbl_DriveType.Caption:= '软盘驱动器';
  DRIVE_FIXED : Lbl_DriveType.Caption:= '硬盘驱动器';
  DRIVE_REMOTE: Lbl_DriveType.Caption:= '网络驱动器';
  DRIVE_CDROM: Lbl_DriveType.Caption:= '光盘驱动器';
  DRIVE_RAMDISK: Lbl_DriveType.Caption:= '内存虚拟盘';
  end; //将该磁盘信息显示在Lbl_DriveType中

}

function GetDrives: string;
var
  DiskType: Word;
  D: Char;
  Str: string;
  I: Integer;
begin
  for I := 0 to 25 do //遍历26个字母
  begin
    D := Chr(I + 65);
    Str := D + ':';
    DiskType := GetDriveType(PChar(Str));
    //得到本地磁盘和网络盘
    if (DiskType = DRIVE_FIXED) {or (DiskType = DRIVE_REMOTE)} then
      Result := Result + D;
  end;
end;

{ 遍历目录 }
{
procedure LoopFiles(Path, Mask: string);
var
  I, Count: Integer;
  Fn, Ext: string;
  SubDir: TStrings;
  SearchRec: TSearchRec;
  Msg: TMsg;
  function IsValidDir(SearchRec: TSearchRec): Integer;
  begin
    if (SearchRec.Attr <> 16) and (SearchRec.Name <> '.') and
      (SearchRec.Name <> '..') then
      Result := 0 //不是目录
    else if (SearchRec.Attr = 16) and (SearchRec.Name <> '.') and
      (SearchRec.Name <> '..') then
      Result := 1 //不是根目录
    else Result := 2; //是根目录
  end;
begin
  SubDir := TStringList.Create;
  if (FindFirst(Path + '*.*', faDirectory, SearchRec) = 0) then
  begin
    repeat
      if g_boClose then break;
      Application.ProcessMessages;
      if IsValidDir(SearchRec) = 1 then begin
        if not CheckMirPath(Path + SearchRec.Name + '\') then begin
          SubDir.Add(SearchRec.Name);
          FrmMain.LabelStatus.Caption := Path + SearchRec.Name;
        end else begin
          g_sMirPath := Path + SearchRec.Name + '\';
          WriteRegKey(1, R_MySubKey, R_Key, g_sMirPath);
          Break;
        end;
      end;
      Sleep(1);

    until (FindNext(SearchRec) <> 0);
  end;
  FindClose(SearchRec);

  if not CheckMirPath(g_sMirPath) then begin
    Count := SubDir.Count - 1;
    for I := 0 to Count do begin
      if g_boClose then break;
      LoopFiles(Path + SubDir.Strings[I] + '\', Mask);
    end;
  end;

  FreeAndNil(SubDir);
end;

}

//缩短显示不下的长路径名

function FormatPath(APath: string; Width: Integer): string;
var
  SLen: Integer;
  i, j: Integer;
  TString: string;
begin
  SLen := Length(APath);
  if (SLen <= Width) or (Width <= 6) then
  begin
    Result := APath;
    Exit
  end
  else
  begin
    i := SLen;
    TString := APath;
    for j := 1 to 2 do
    begin
      while (TString[i] <> '\') and (SLen - i < Width - 8) do
        i := i - 1;
      i := i - 1;
    end;
    for j := SLen - i - 1 downto 0 do
      TString[Width - j] := TString[SLen - j];
    for j := SLen - i to SLen - i + 2 do
      TString[Width - j] := '.';
    Delete(TString, Width + 1, 255);
    Result := TString;
  end;
end;

procedure LoopFiles(Path, Mask: string; SubDir: TStrings);
var
  I, Count: Integer;
  Fn, Ext, FullPath: string;
  SearchRec: TSearchRec;
  Msg: TMsg;
  function IsValidDir(SearchRec: TSearchRec): Integer;
  begin
    if (SearchRec.Attr <> 16) and (SearchRec.Name <> '.') and
      (SearchRec.Name <> '..') then
      Result := 0 //不是目录
    else if (SearchRec.Attr = 16) and (SearchRec.Name <> '.') and
      (SearchRec.Name <> '..') then
      Result := 1 //不是根目录
    else Result := 2; //是根目录
  end;
begin
  if (FindFirst(Path + '*.*', faDirectory, SearchRec) = 0) then begin
    repeat
      if g_boClose then break;
      Application.ProcessMessages;
      if IsValidDir(SearchRec) = 1 then begin
        FullPath := Path + SearchRec.Name + '\';
        if not CheckFullPath(FullPath) then begin
          //if CheckLegendPath(FullPath) then
          if CheckLegendPath(FullPath) then
            g_LegendPathList.Add(FullPath);

          SubDir.Add(FullPath);

          FrmMain.LabelStatus.Caption := FormatPath(FullPath, 40);
          {if Length(FullPath) > 30 then begin
            FrmMain.LabelStatus.Caption := ExtractShortPathName(FullPath);
          end else begin
            FrmMain.LabelStatus.Caption := FullPath;
          end; }
        end else begin
          g_sMirPath := FullPath;
          WriteRegKey(1, R_MySubKey, R_Key, g_sMirPath);
          Break;
        end;
      end;
      Sleep(1);

    until (FindNext(SearchRec) <> 0);
  end;
  FindClose(SearchRec);
end;

function CheckLegendPath(Path: string): Boolean;
begin
  Result := (DirectoryExists(Path + 'Data')) and
    (DirectoryExists(Path + 'Map')) and
    (DirectoryExists(Path + 'Wav'));
end;

function CheckFullPath(Path: string): Boolean;
begin
  Result := (DirectoryExists(Path + 'Data')) and
    (DirectoryExists(Path + 'Map')) and
    (DirectoryExists(Path + 'Wav')) and
    (not FileExists(Path + 'Data\ChrSel_16.wil')) and
    (not FileExists(Path + 'Data\Prguse_16.wil')) and
    (not FileExists(Path + 'Data\Prguse2_16.wil')) and
    (not FileExists(Path + 'Data\Prguse3_16.wil'));
end;

function CheckMirPath(Path: string): Boolean;
var
  sKeyValue: string;
begin
  if CheckLegendPath(Path) then begin
    g_sMirPath := Path;
    Result := True;
  end else begin
    if ReadRegKey(1, R_MySubKey, R_Key, sKeyValue) then begin
      if CheckLegendPath(sKeyValue) then begin
        g_sMirPath := sKeyValue;
        Result := True;
        Exit;
      end;
    end;

    if ReadRegKey(1, R_SndaSubKey, R_Key, sKeyValue) then begin
      if CheckLegendPath(sKeyValue) then begin
        g_sMirPath := sKeyValue;
        WriteRegKey(1, R_MySubKey, R_Key, g_sMirPath);
        Result := True;
        Exit;
      end;
    end;
    Result := False;
  end;
end;

function SearchPath: Boolean;
var
  I: Integer;
  SubDir: TStrings;
begin
  Result := False;
  SubDir := TStringList.Create;
  for I := 0 to g_SearchList.Count - 1 do begin
    LoopFiles(g_SearchList[I], '*.*', SubDir);
    if g_boClose or CheckFullPath(g_sMirPath) then begin
      break;
    end;
  end;
  if not CheckFullPath(g_sMirPath) then begin
    g_SearchList.Clear;
    g_SearchList.AddStrings(SubDir);
  end else Result := True;
  FreeAndNil(SubDir);
end;

function TFrmMain.SearchMirClient(Path: string): Boolean;
var
  DriverList: string;
  sKeyValue: string;
  I, Len: Integer;
begin
  Result := False;
  if not CheckLegendPath(Path) then begin
    if ReadRegKey(1, R_MySubKey, R_Key, sKeyValue) then begin
      if CheckLegendPath(sKeyValue) then begin
        g_sMirPath := sKeyValue;
        Result := True;
        Exit;
      end;
    end;

    if ReadRegKey(1, R_SndaSubKey, R_Key, sKeyValue) then begin
      if CheckLegendPath(sKeyValue) then begin
        g_sMirPath := sKeyValue;
        WriteRegKey(1, R_MySubKey, R_Key, g_sMirPath);
        Result := True;
        Exit;
      end;
    end;

    DriverList := GetDrives; //得到可写的磁盘列表 //遍历每个磁盘驱动器
    Len := Length(DriverList);
    for I := 1 to Len do begin
      g_SearchList.Add(DriverList[I] + ':\');
    end;

    while (not SearchPath) and (not g_boClose) do begin
    end;

    if (not CheckMirPath(g_sMirPath)) and (g_LegendPathList.Count > 0) then begin
      g_sMirPath := g_LegendPathList.Strings[0];
    end;

    Result := CheckLegendPath(g_sMirPath);

    {DriverList := GetDrives; //得到可写的磁盘列表
    Len := Length(DriverList);
    for I := Len downto 1 do begin //遍历每个磁盘驱动器
      try
        LoopFiles(DriverList[I] + ':\', '*.*');
        if CheckMirPath(g_sMirPath) then begin
          Result := True;
          Break;
        end;
      except

      end;
      if g_boClose then break;
    end;
    }
  end else begin
    g_sMirPath := Path;
    if not ReadRegKey(1, R_MySubKey, R_Key, sKeyValue) then begin
      WriteRegKey(1, R_MySubKey, R_Key, g_sMirPath);
    end else begin
      if not CheckLegendPath(sKeyValue) then begin
        WriteRegKey(1, R_MySubKey, R_Key, g_sMirPath);
      end;
    end;
    Result := True;
  end;
end;


procedure TfrmMain.ProcessMessage(var Msg: TMsg; var Handled: Boolean);
begin
  if Msg.message = WM_SENDPROCMSG then begin
    //    ShowMessage('asfd');
    Handled := True;
  end;
end;

procedure TfrmMain.MyMessage(var MsgData: TWmCopyData);
var
  I, nHandle: Integer;
  sData: string;
  wIdent, wRecog: Word;
begin
  if not g_boClose then begin
    wIdent := HiWord(MsgData.From);
    wRecog := LoWord(MsgData.From);
    sData := StrPas(MsgData.CopyDataStruct^.lpData);
    case wIdent of
      CM_HANDLE: begin
          nHandle := Str_ToInt(sData, 0);
          for I := 0 to g_ClientHandleList.Count - 1 do begin
            if nHandle = Integer(g_ClientHandleList.Items[I]) then begin
              Exit;
            end;
          end;
          g_ClientHandleList.Add(Pointer(nHandle));
        //Showmessage('CM_HANDLE:'+IntToStr(nHandle));
        end;
      CM_QUIT: begin
          nHandle := Str_ToInt(sData, 0);
        //Showmessage('CM_QUIT:'+IntToStr(nHandle));
          for I := 0 to g_ClientHandleList.Count - 1 do begin
            if nHandle = Integer(g_ClientHandleList.Items[I]) then begin
              g_ClientHandleList.Delete(I);
              Break;
            end;
          end;
          if IsIconic(Application.Handle) then
            ShowWindow(Application.Handle, SW_RESTORE);
          SetForegroundWindow(Application.Handle);
        end;
    end;
  end;
end;

procedure TfrmMain.CreateUlr(sCreateUlrName: string); //创建快捷方式
var
  ShLink: IShellLink;
  PFile: IPersistFile;
  FileName: string;
  WFileName: WideString;
  Reg: TRegIniFile;
  AnObj: IUnknown;
  UrlName: string;
begin
  UrlName := Trim(sCreateUlrName);
  if UrlName <> '' then begin
    AnObj := CreateComObject(CLSID_ShellLink);
    ShLink := AnObj as IShellLink;
    PFile := AnObj as IPersistFile;
    FileName := ParamStr(0);
    ShLink.SetPath(PChar(FileName));
    ShLink.SetWorkingDirectory(PChar(ExtractFilePath(FileName)));
    Reg := TRegIniFile.Create('Software\MicroSoft\Windows\CurrentVersion\Explorer');
    WFileName := Reg.ReadString('Shell Folders', 'Desktop', '') + '\' + UrlName + '.lnk';
    PFile.Save(PWChar(WFileName), True);
  end;
end;

procedure TfrmMain.SendCSocket(sendstr: string);
var
  sSendText: string;
begin
  if ClientSocket.Socket.Connected then begin  //是否被连接
    sSendText := '#' + IntToStr(btCode) + sendstr + '!'; //btcode =1  #+btcode+sendstr+!
    ClientSocket.Socket.SendText('#' + IntToStr(btCode) + sendstr + '!');
    Inc(btCode);//+1
    if btCode >= 10 then btCode := 1;//>=10 则 赋值1
  end;
end;

procedure TfrmMain.SendChgPw(sAccount, sPasswd, sNewPasswd: string); //发送修改密码
var
  Msg: TDefaultMessage;
begin
  Msg := MakeDefaultMsg(CM_CHANGEPASSWORD, 0, 0, 0, 0);//修改密码
  SendCSocket(EncodeMessage(Msg) + EncodeString(sAccount + #9 + sPasswd + #9 + sNewPasswd));
end;

procedure TfrmMain.SendGetBackPassword(sAccount, sQuest1, sAnswer1,
  sQuest2, sAnswer2, sBirthDay: string); //发送找回密码
var
  Msg: TDefaultMessage;
begin
  Msg := MakeDefaultMsg(CM_GETBACKPASSWORD, 0, 0, 0, 0);//知足找回密码消息
  SendCSocket(EncodeMessage(Msg) + EncodeString(sAccount + #9 + sQuest1 + #9 + sAnswer1 + #9 + sQuest2 + #9 + sAnswer2 + #9 + sBirthDay));
  //消息代码 + 编码字符串 tab分割参数
end;

procedure TfrmMain.SendGetRandomCode;
var
  Msg: TDefaultMessage;
begin
  Msg := MakeDefaultMsg(CM_RANDOMCODE, 0, 0, 0, 0); //make 一个默认消息
  SendCSocket(EncodeMessage(Msg)); //加密消息 发送消息
end;

procedure TfrmMain.SendUpdateAccount(ue: TUserEntry; ua: TUserEntryAdd; nRandomCode: Integer); //发送新建账号
var
  Msg: TDefaultMessage;  //发送更新用户 新建用户
begin
  sMakeNewAccount := ue.sAccount;//全局变量
  Msg := MakeDefaultMsg(CM_ADDNEWUSER, nRandomCode, 0, 0, 0);//发送消息
  SendCSocket(EncodeMessage(Msg) + EncodeBuffer(@ue, SizeOf(TUserEntry)) + EncodeBuffer(@ua, SizeOf(TUserEntryAdd))); //329
  //发送 消息码+编码缓冲区ue+编码缓冲区ua
end;

procedure TfrmMain.TimerTimer(Sender: TObject);
var
  Str, data: string;
  len, I, n, mcnt: Integer;
begin
  if boBusy then Exit;
  boBusy := True;
  try
    sBufferStr := sBufferStr + sSocStr;
    sSocStr := '';
    if sBufferStr <> '' then begin
      mcnt := 0;
      while Length(sBufferStr) >= 2 do begin
        if Pos('!', sBufferStr) <= 0 then Break;
        sBufferStr := ArrestStringEx(sBufferStr, '#', '!', data);
        if data <> '' then begin
          DecodeMessagePacket(data);
        end else
          if Pos('!', sBufferStr) = 0 then
          Break;
      end;
    end;
    {if frmNewAccount.Visible then begin
      frmNewAccount.LabelRandomCode.Caption := g_sRandomCode;
    end;}
  finally
    boBusy := False;
  end;
end;

procedure TfrmMain.TreeViewClick(Sender: TObject);
var
  TreeNode: TTreeNode;
  GameZone: TGameZone;
  sServerAddr: string;
  sServerPort: string;
begin
  g_SelGameZone := nil;
  TreeNode := TreeView.Selected;
  if (TreeNode = nil) or (TreeNode.Parent = nil) then begin
    //Showmessage('TreeNode.Parent = nil');
    Exit;
  end;
  g_SelGameZone := TGameZone(TreeNode.Data);
  try
    if not g_SelGameZone.Connected then begin
      ClientSocket.Active := False;
      ClientSocket.Host := '';
      ClientSocket.Address := '';

      if g_SelGameZone.Encrypt then begin
        sServerAddr := DeCodeString(g_SelGameZone.GameHost);
        sServerPort := DeCodeString(g_SelGameZone.GameIPPort);
      end else begin
        sServerAddr := g_SelGameZone.GameHost;
        sServerPort := g_SelGameZone.GameIPPort;
      end;

      if IsIpAddr(sServerAddr) then begin
        ClientSocket.Address := sServerAddr;
      end else begin
        ClientSocket.Host := sServerAddr;
      end;
      ClientSocket.Port := Str_ToInt(sServerPort, 7000);

      ClientSocket.Active := True;
    end else begin
      g_boClientSocketConnect := True;
      LabelStatus.Font.Color := g_GameLoginConfig.LabelConnectColor;
      LabelStatus.Caption := sClientConnect;
      RzBmpButtonFullScreenStart.Enabled := True;
      RzBmpButtonStart.Enabled := True;
      RzBmpButtonAutoLogin.Enabled := True;
      RzBmpButtonNewAccount.Enabled := True;
      RzBmpButtonGetBakPassWord.Enabled := True;
      RzBmpButtonChgPassWord.Enabled := True;
    end;
    //WebBrowser.Height := g_nWebHeiht;
    //WebBrowser.Width := g_WebBrowser.Width;
    WebBrowser.Navigate(g_SelGameZone.NoticeUrl);
    //WebBrowser.Width := g_WebBrowser.Width;
    {Showmessage(IntToStr(WebBrowser.Width));
    Showmessage(IntToStr(WebBrowser.Height));}
  except

  end;
end;

procedure TfrmMain.DecodeMessagePacket(datablock: string);
var
  head, body, body2, tagstr, data, rdstr, Str: string;
  Msg: TDefaultMessage;
  smsg: TShortMessage;
  mbw: TMessageBodyW;
  desc: TCharDesc;
  wl: TMessageBodyWL;
  featureEx: word;
  L, I, j, n, BLKSize, param, sound, cltime, svtime: Integer;
  tempb: Boolean;
begin
  if datablock[1] = '+' then Exit;
  if Length(datablock) < DEFBLOCKSIZE then Exit;
  head := Copy(datablock, 1, DEFBLOCKSIZE);
  body := Copy(datablock, DEFBLOCKSIZE + 1, Length(datablock) - DEFBLOCKSIZE);
  Msg := DecodeMessage(head);
  case Msg.Ident of
    SM_NEWID_SUCCESS: begin
        Application.MessageBox('您的帐号创建成功。' + #13 +
          '请妥善保管您的帐号和密码,' + #13 + '并且不要因任何原因把帐号和密码告诉任何其他人。' + #13 +
          '如果忘记了密码,你可以通过我们的主页重新找回。', '提示信息', MB_OK);
        frmNewAccount.Close;
      end;
    SM_NEWID_FAIL: begin
        case Msg.Recog of
          0: begin
              Application.MessageBox(PChar('帐号 "' + sMakeNewAccount + '" 已被其他的玩家使用了。' + #13 +
                '请选择其它帐号名注册。'), '提示信息', MB_OK);
            end;
          1: begin
              Application.MessageBox('验证码输入错误,请重新输入!!!', '提示信息', MB_OK);
              //frmNewAccount.EditRandomCode.SetFocus;
            end;
          -2: Application.MessageBox('此帐号名被禁止使用!', '提示信息', MB_OK);
        else Application.MessageBox(PChar('帐号创建失败,请确认帐号是否包括空格、及非法字符!Code: ' + IntToStr(Msg.Recog)), '提示信息', MB_OK);
        end;
        frmNewAccount.ButtonOK.Enabled := True;
        Exit;
      end;
    SM_RANDOMCODE: begin

      end;
    
    SM_CHGPASSWD_SUCCESS: begin
        Application.MessageBox('密码修改成功。', '提示信息', MB_OK);
        {frmChangePassword.ChgEditAccount.Text:='';
        frmChangePassword.ChgEditPassword.Text:='';
        frmChangePassword.ChgEditConfirm.Text:='';
        frmChangePassword.ChgEditNewPassword.Text:='';}
        frmChangePassword.ButtonOK.Enabled := False;
        //frmNewAccount.Close;
        Exit;
      end;
    SM_CHGPASSWD_FAIL: begin
        case Msg.Recog of
          0: Application.MessageBox('输入的帐号不存在!!!', '提示信息', MB_OK);
          -1: Application.MessageBox('输入的原始密码不正确!', '提示信息', MB_OK);
          -2: Application.MessageBox('此帐号被锁定!', '提示信息', MB_OK);
        else Application.MessageBox('输入的新密码长度小于四位!', '提示信息', MB_OK);
        end;
        frmChangePassword.ButtonOK.Enabled := True;
        Exit;
      end;
    SM_GETBACKPASSWD_SUCCESS: begin
        frmGetBackPassword.EditPassword.Text := DecodeString(body);
        Application.MessageBox(PChar('密码找回成功。'), '提示信息', MB_OK);
        Exit;
      end;
    SM_GETBACKPASSWD_FAIL: begin
        case Msg.Recog of
          0: Application.MessageBox('输入的帐号不存在!!!', '提示信息', MB_OK + MB_ICONERROR);
          -1: Application.MessageBox('问题答案不正确!!!', '提示信息', MB_OK + MB_ICONERROR);
          -2: Application.MessageBox(PChar('此帐号被锁定!!!' + #13 + '请稍候三分钟再重新找回。'), '提示信息', MB_OK + MB_ICONERROR);
          -3: Application.MessageBox('答案输入不正确!!!', '提示信息', MB_OK + MB_ICONERROR);
        else Application.MessageBox('未知错误!', '提示信息', MB_OK + MB_ICONERROR);
        end;
        frmGetBackPassword.ButtonOK.Enabled := True;
        Exit;
      end;
  end;
end;

procedure TfrmMain.ClientSocketConnect(Sender: TObject;
  Socket: TCustomWinSocket);
begin
  if g_SelGameZone <> nil then begin
    g_SelGameZone.GameIPaddr := Socket.RemoteAddress;
    g_SelGameZone.Connected := True;
  end;
  //ChgButtonStatus(0);
  g_boClientSocketConnect := True;
  LabelStatus.Font.Color := g_GameLoginConfig.LabelConnectColor;
  //LabelStatus.Font.Color := clLime;
  LabelStatus.Caption := sClientConnect;
  //frmGetBackPassword.LabelStatus.Caption := sClientConnect;
  //frmNewAccount.LabelStatus.Caption := sClientConnect;
  //frmChangePassword.LabelStatus.Caption := sClientConnect;

  RzBmpButtonFullScreenStart.Enabled := True;
  RzBmpButtonStart.Enabled := True;
  RzBmpButtonAutoLogin.Enabled := True;
  RzBmpButtonNewAccount.Enabled := True;
  RzBmpButtonGetBakPassWord.Enabled := True;
  RzBmpButtonChgPassWord.Enabled := True;
end;

procedure TfrmMain.ClientSocketConnecting(Sender: TObject;
  Socket: TCustomWinSocket);
begin
  //ChgButtonStatus(1);
  g_boClientSocketConnect := False;
  LabelStatus.Font.Color := g_GameLoginConfig.LabelConnectingColor; //clFuchsia;
  LabelStatus.Caption := sClientConnecting;
  frmGetBackPassword.LabelStatus.Caption := sClientConnecting;
  //frmNewAccount.LabelStatus.Caption := sClientConnecting;
  //frmChangePassword.LabelStatus.Caption := sClientConnecting;

  RzBmpButtonFullScreenStart.Enabled := False;
  RzBmpButtonStart.Enabled := False;
  RzBmpButtonAutoLogin.Enabled := False;
  RzBmpButtonNewAccount.Enabled := False;
  RzBmpButtonGetBakPassWord.Enabled := False;
  RzBmpButtonChgPassWord.Enabled := False;
end;

procedure TfrmMain.ClientSocketDisconnect(Sender: TObject;
  Socket: TCustomWinSocket);
begin
  //ChgButtonStatus(2);
  g_boClientSocketConnect := False;
  if not g_SelGameZone.Connected then begin
    LabelStatus.Font.Color := g_GameLoginConfig.LabelDisconnectColor; //clYellow;
    LabelStatus.Caption := sClientDisconnect;

  //frmGetBackPassword.LabelStatus.Caption := sClientDisconnect;
  //frmNewAccount.LabelStatus.Caption := sClientDisconnect;
  //frmChangePassword.LabelStatus.Caption := sClientDisconnect;

    RzBmpButtonFullScreenStart.Enabled := False;
    RzBmpButtonStart.Enabled := False;
    RzBmpButtonAutoLogin.Enabled := False;
    RzBmpButtonNewAccount.Enabled := False;
    RzBmpButtonGetBakPassWord.Enabled := False;
    RzBmpButtonChgPassWord.Enabled := False;
  end;
end;

procedure TfrmMain.ClientSocketError(Sender: TObject;
  Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
  var ErrorCode: Integer);
begin
  //ChgButtonStatus(2);
  ErrorCode := 0;
  Socket.Close;
end;

procedure TfrmMain.ClientSocketRead(Sender: TObject;
  Socket: TCustomWinSocket);
var
  n: Integer;
  data, data2: string;
begin
  data := Socket.ReceiveText;
  n := Pos('*', data);
  if n > 0 then begin
    data2 := Copy(data, 1, n - 1);
    data := data2 + Copy(data, n + 1, Length(data));
    ClientSocket.Socket.SendText('*');
  end;
  sSocStr := sSocStr + data;
end;

procedure TfrmMain.OnProgramException(Sender: TObject; E: Exception);
begin
  DebugOutStr(E.Message);
end;

procedure TFrmMain.LoadUserConfig;
var
  Conf: TIniFile;
begin
  Conf := TIniFile.Create(g_sSelfFilePath + 'User.ini');
  //RzCheckBoxWindowMode.Checked := Conf.ReadBool('Settings', 'WindowMode', False);
  //ComboBoxScreenMode.ItemIndex := Conf.ReadInteger('Settings', 'ScreenMode', 0);
  ComboBox.ItemIndex := Conf.ReadInteger('Settings', 'ScreenMode', 0);
  Conf.Free;
end;

procedure TFrmMain.SaveUserConfig;
var
  Conf: TIniFile;
begin
  Conf := TIniFile.Create(g_sSelfFilePath + 'User.ini');
  //Conf.WriteBool('Settings', 'WindowMode', RzCheckBoxWindowMode.Checked);
  //Conf.WriteInteger('Settings', 'ScreenMode', ComboBoxScreenMode.ItemIndex);
  Conf.WriteInteger('Settings', 'ScreenMode', ComboBox.ItemIndex);
  Conf.Free;
end;


procedure TfrmMain.FormCreate(Sender: TObject);

var
  BatchFileName: string;
  sCopyFile: string;
  MemoryStream: TMemoryStream;
  //FileStream: TMemoryStream;
  PlugStream: TMemoryStream;
  Config: TConfig;
  sText: string;
  nCount: Integer;
  nCount1: Integer;
  Buffer: Pointer;
  OutBuf: Pointer;
  OutBytes: Integer;
  SrcP: Pointer;
  nLen: Integer;
  //JPE: TJpegImage;
  Bitmap: TBitmap;
  I: Integer;
begin
  Application.ShowMainForm := False;//隐藏主窗口
  g_sSelfFileName := ExtractFileName(Application.ExeName);//自身文件名
  g_sSelfFilePath := ExtractFilePath(Application.ExeName);//自身路径

  g_SearchList := TStringList.Create;//搜索列表
  g_LegendPathList := TStringList.Create;//传奇客户端路径
  g_ClientHandleList := TList.Create;//客户端句柄

  WebBrowser := TWebBrowser.Create(Self);//游览器
  g_ComponentList.Add(RzBmpButtonFullScreenStart);//全屏启动
  g_ComponentList.Add(RzBmpButtonEditGameList);//编辑游戏
  g_ComponentList.Add(RzBmpButtonHomePage);//官方主页
  g_ComponentList.Add(RzBmpButtonAutoLogin);//脱机登陆
  g_ComponentList.Add(RzBmpButtonUpgrade);//游戏更新
  g_ComponentList.Add(RzBmpButtonStart);//窗口登陆
  g_ComponentList.Add(RzBmpButtonNewAccount);//帐号注册
  g_ComponentList.Add(RzBmpButtonGetBakPassWord);//密码找回
  g_ComponentList.Add(RzBmpButtonChgPassWord);//修改密码
  g_ComponentList.Add(RzBmpButtonExitGame);//退出游戏
  g_ComponentList.Add(RzBmpButtonMin);//最小化
  g_ComponentList.Add(RzBmpButtonClose);//关闭
  g_ComponentList.Add(TreeView);//列表
  g_ComponentList.Add(WebBrowser);//游览器
  g_ComponentList.Add(LabelStatus);//状态
  g_ComponentList.Add(ComboBox);//分辨率

  MirClient := nil;//内存变量
{------------------------------------------------------------------------------}
{------------------------------------------------------------------------------}
{------------------------------------------------------------------------------}
  MirClient := TMemoryStream.Create;//创建
  MemoryStream := TMemoryStream.Create; //创建
  try
    nLen := SizeOf(TGameLoginConfig);//获取游戏配置大小
    MemoryStream.LoadFromFile(Application.ExeName);//载入exe

    MemoryStream.Seek(-nLen, soFromEnd);//移动底部 倒退配置大小
    MemoryStream.Read(g_GameLoginConfig, nLen);//读取到内存  配置文件中

    for I := 0 to Length(g_GameLoginConfig.ComponentImages) - 1 do begin
      Inc(nLen, g_GameLoginConfig.ComponentImages[I].UpSize); //图片 大小
      Inc(nLen, g_GameLoginConfig.ComponentImages[I].HotSize); //图片大小
      Inc(nLen, g_GameLoginConfig.ComponentImages[I].DownSize);//大小
      Inc(nLen, g_GameLoginConfig.ComponentImages[I].Disabled);//大小
    end;

{------------------------------------------------------------------------------}
{------------------------------------------------------------------------------}
{------------------------------------------------------------------------------}
 //读取界面数据
    MemoryStream.Seek(-nLen, soFromEnd);//移动
    GetMem(g_Buffer, nLen - SizeOf(TGameLoginConfig));//分配内存
    MemoryStream.Read(g_Buffer^, nLen - SizeOf(TGameLoginConfig));//读取
{------------------------------------------------------------------------------}
{------------------------------------------------------------------------------}
{------------------------------------------------------------------------------}
    MemoryStream.Seek(-(nLen + SizeOf(Integer)), soFromEnd); //移动
    MemoryStream.Read(nCount, SizeOf(Integer));//读取整形count
    MemoryStream.Seek(-(nLen + SizeOf(Integer) + nCount), soFromEnd);//再移动

    GetMem(Buffer, nCount);//分配内存
    try
      MemoryStream.Read(Buffer^, nCount); //读到buffer中
      DecompressBuf(Buffer, nCount, 0, OutBuf, OutBytes);//解压指针
    finally
      FreeMem(Buffer);//释放内存
    end;
    Move(OutBuf^, Config, OutBytes);//将解压后的配置 移动到 Config
    FreeMem(OutBuf);//释放内存

    GetMem(Buffer, Config.nClientSize);//分配内存
    try
      MemoryStream.Seek(Config.nClientPos, soFromBeginning);//客户端的位置
      MemoryStream.Read(Buffer^, Config.nClientSize);//读取到buffer
      MirClient.Write(Buffer^, Config.nClientSize);//将读取到的buffer写入到mirclient内存流中
    finally
      FreeMem(Buffer);
    end;

  except

  end;
  MemoryStream.Free;


{------------------------------------------------------------------------------}
{------------------------------------------------------------------------------}
{------------------------------------------------------------------------------}
  g_CreateUlrName := EncodeString(Config.sUlrName);//解码快捷方式名称
  g_sHomePage := EncodeString(Config.sHomePage);//解码地址
  g_sRemoteConfigAddress := EncodeString(Config.sConfigAddress);//解码配置地址
  g_sGameRemoteAddress := EncodeString(Config.sGameAddress);//解码游戏地址

  g_boClose := False;
  CreateUlr(DeCodeString(g_CreateUlrName));//创建快捷方式
  g_AllGameList := TGameZone.Create;//所有游戏列表
  g_GameList := TGameZone.Create();//游戏列表
  g_LocalGameList := TGameZone.Create();//本地列表

  //g_MessageDlg := TMessageDlg.Create(Owner);
  //g_NpcDlg := TNpcDlg.Create(Owner);

  //TimerReakSkin.Enabled := TRUE;
  ReadSkin;//读取皮肤

  if CheckMirPath(g_sSelfFilePath) then begin  //检查客户端路径
   { g_MirsClient.sDirectory := g_sMirPath;
    g_MirsClient.sDirectory := g_sMirPath; }
    frmUpgradeDownload := TfrmUpgradeDownload.Create(Owner); //创建更新窗口
    frmUpgradeDownload.Open;//打开
    frmUpgradeDownload.Free;//释放
    {Self.Repaint;
    Self.Refresh; }
  end;

  LoadUserConfig;//载入配置

  if g_boNeedUpgradeSelf then begin
    CloseTimer.Enabled := True;
  end else begin
    TimerStart.Enabled := True;
    Application.ShowMainForm := True;
  end;

  //Image.RecreateRegion;

  {if g_boClose then begin
    Application.ShowMainForm := False;
    CloseTimer.Enabled := True;
  end else begin
    TimerStart.Enabled := True;
  end;}
end;

procedure TfrmMain.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
var
  I: Integer;
  nHandle: Integer;
begin
  g_boClose := True;
  for I := 0 to g_ClientHandleList.Count - 1 do begin
    nHandle := Integer(g_ClientHandleList.Items[I]);
    SendProgramMsg(nHandle, GL_QUIT, '');
  end;

  try
    if not g_boClose then
      g_LocalGameList.SaveToFile(g_sLocalGameListFileName);

    g_GameList.Free;
    g_LocalGameList.Free;

    g_AllGameList.Free;
    WebBrowser.Free;
    g_ClientHandleList.Free;
    if MirClient <> nil then MirClient.Free;

    g_SearchList.Free;
    g_LegendPathList.Free;
  except

  end;
end;

function TfrmMain.GetProcesses: Boolean;
var
  hSnap: THandle;
  ProcessEntry: TProcessEntry32;
  Proceed: Boolean;
begin
  Result := False;
  hSnap := CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0); //创建系统快照
  if hSnap <> -1 then
  begin
    ProcessEntry.dwSize := SizeOf(TProcessEntry32); //先初始化 FProcessEntry32 的大小
    Proceed := Process32First(hSnap, ProcessEntry);
    while Proceed do begin
      with ProcessEntry do
        if (CompareText(StrPas(szEXEFile), 'JS.UCU') = 0) then begin
          //CloseProcess(Th32ProcessID);
          Result := True;
          Break;
        end else begin
          if GetModules(Th32ProcessID) then begin
            Result := True;
            Break;
          end;
        end;
       { with listview_pro.Items.Add do
        begin
          Caption := szEXEFile;
          subitems.Add(IntToStr(Th32ProcessID));
          subitems.Add(IntToStr(th32ParentProcessID));
          subitems.Add(IntToStr(Th32ModuleID));
          subitems.Add(IntToStr(cntUsage));
          subitems.Add(IntToStr(cntThreads));
          subitems.Add(IntToStr(pcPriClassBase));
        end; }
      Proceed := Process32Next(hSnap, ProcessEntry);
    end;
    CloseHandle(hSnap);
  end;
     {else
     ShowMessage( 'Oops...' + SysErrorMessage(GetLastError)); }
end;

function TfrmMain.GetModules(ProcessID: DWORD): Boolean;
var hSnap: THandle;
  ModuleEntry: TModuleEntry32;
  Proceed: Boolean;
begin
  Result := False;
  hSnap := CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, ProcessID);
  if hSnap <> -1 then
  begin
    ModuleEntry.dwSize := SizeOf(TModuleEntry32);
    Proceed := Module32First(hSnap, ModuleEntry);
    while Proceed do
    begin
      with ModuleEntry do
        if (CompareText(szModule, 'WINIO.DLL') = 0) or (CompareText(szModule, 'JSHJ.DLL') = 0) then begin
          //CloseProcess(ProcessID);
          Result := True;
          Break;
        end;
        {with listview_mod.Items.Add do
        begin
          Caption := szModule;
          subitems.Add(ExtractFilePath(szEXEPath));
          subitems.Add(IntToStr(Th32ModuleID));
          subitems.Add(FloatToStr(ModBaseSize / 1024));
          subitems.Add(IntToStr(GlblCntUsage));
        end; }
      Proceed := Module32Next(hSnap, ModuleEntry);
    end;
    CloseHandle(hSnap);
  end;
     {else
     ShowMessage( 'Oops...' + SysErrorMessage(GetLastError));  }
end;

function RunProgram(GameZone: TGameZone; sDirectory, sProgramFile, sHandle: string; dwWaitTime: LongWord; nScreenWidth, nScreenHegiht: Integer): LongWord;
var
  StartupInfo: TStartupInfo;
  sCommandLine: string;
  sCurDirectory: string;
  ProcessInfo: TProcessInformation;
  dwTick: LongWord;
  MirServer: TMirServer;
begin
  Result := 0;
  FillChar(StartupInfo, SizeOf(TStartupInfo), #0);
  {
  StartupInfo.cb:=SizeOf(TStartupInfo);
  StartupInfo.lpReserved:=nil;
  StartupInfo.lpDesktop:=nil;
  StartupInfo.lpTitle:=nil;
  StartupInfo.dwFillAttribute:=0;
  StartupInfo.cbReserved2:=0;
  StartupInfo.lpReserved2:=nil;
  }

  if GameZone.Encrypt then begin
    MirServer.sServeraddr := DeCodeString(GameZone.GameHost);
    MirServer.nServerPort := Str_ToInt(DeCodeString(GameZone.GameIPPort), 0);
  end else begin
    MirServer.sServeraddr := GameZone.GameHost;
    MirServer.nServerPort := Str_ToInt(GameZone.GameIPPort, 0);
  end;
  MirServer.sServerName := GameZone.ServerName;
  MirServer.boFullScreen := g_boFullScreen;

  MirServer.nScreenWidth := nScreenWidth;
  MirServer.nScreenHegiht := nScreenHegiht;

  GetStartupInfo(StartupInfo);
  StartupInfo.wShowWindow := SW_SHOW;
  StartupInfo.dwFlags := STARTF_USEFILLATTRIBUTE;
  StartupInfo.dwFillAttribute := FOREGROUND_INTENSITY or BACKGROUND_BLUE;

  sCommandLine := Format('%s%s %s %s', [sDirectory, sProgramFile, sHandle, EncodeBuffer(@MirServer, SizeOf(TMirServer))]);

  dwTick := GetTickCount;
  sCurDirectory := sDirectory;
  if not CreateProcess(nil, //lpApplicationName,
    PChar(sCommandLine), //lpCommandLine,
    nil, //lpProcessAttributes,
    nil, //lpThreadAttributes,
    True, //bInheritHandles,
    0, //dwCreationFlags,
    nil, //lpEnvironment,
    PChar(sCurDirectory), //lpCurrentDirectory,
    StartupInfo, //lpStartupInfo,
    ProcessInfo) then begin //lpProcessInformation

    Result := GetLastError();
  end;
  //Sleep(dwWaitTime);
end;


procedure TfrmMain.RzBmpButtonFullScreenStartClick(Sender: TObject);
var
  I: Integer;
  nScreenWidth: Integer;
  nScreenHegiht: Integer;
begin
  if g_SelGameZone <> nil then begin
    g_boFullScreen := Sender = RzBmpButtonFullScreenStart;
    FileSetAttr(g_sMirPath + g_sProgamFile, 0);
    try
      MirClient.SaveToFile(g_sMirPath + g_sProgamFile);  // 释放 MirClient.dat 客户端文件
    except

    end;

    Application.Minimize; //自身最小化窗口
    //RunApp(g_sMirPath + g_sProgamFile); //启动客户端
    nScreenWidth := 800;
    nScreenHegiht := 600;
    case ComboBox.ItemIndex of
      0: begin
          nScreenWidth := 800;
          nScreenHegiht := 600;
        end;
      1: begin
          nScreenWidth := 1024;
          nScreenHegiht := 768;
        end;
    end;
    SaveUserConfig;
    RunProgram(g_SelGameZone, g_sMirPath, g_sProgamFile, IntToStr(Handle), 0, nScreenWidth, nScreenHegiht);
  end;
end;

procedure TfrmMain.ChgButtonStatus(btStatus: Byte);
begin
  //RzBmpButtonStatus.Bitmaps.Up.LoadFromResourceName(HInstance, 'Connect');
  {case btStatus of
    0: begin
        ConnectRes := TResourceStream.Create(HInstance, 'Connect', PChar('Bmp'));
        RzBmpButtonStatus.Bitmaps.Up.LoadFromStream(ConnectRes);
        ConnectRes.Free;
      end;
    1: begin
        ConnectingRes := TResourceStream.Create(HInstance, 'Connecting', PChar('Bmp'));
        RzBmpButtonStatus.Bitmaps.Up.LoadFromStream(ConnectingRes);
        ConnectingRes.Free;
      end;
    2: begin
        DisconnectRes := TResourceStream.Create(HInstance, 'Disconnect', PChar('Bmp'));
        RzBmpButtonStatus.Bitmaps.Up.LoadFromStream(DisconnectRes);
        DisconnectRes.Free;
      end;
  end;}
end;

procedure TfrmMain.RzBmpButtonCloseClick(Sender: TObject);
begin
  Close;
end;

procedure TfrmMain.RzBmpButtonHomePageClick(Sender: TObject);
begin
  if g_SelGameZone = nil then Exit;
  ShellExecute(0, 'Open', PChar(g_SelGameZone.HomePage), nil, nil, SW_SHOWNORMAL);//打开网页
end;

procedure TfrmMain.RzBmpButtonNewAccountClick(Sender: TObject);
begin
  frmMain.SendGetRandomCode;
  //frmNewAccount.LabelStatus.Caption := MsgLabel.Caption;
  frmNewAccount.Open;
end;

procedure TfrmMain.RzBmpButtonUpgradeClick(Sender: TObject);
begin
//升级代码也是假的
end;

procedure TfrmMain.RzBmpButtonGetBakPassWordClick(Sender: TObject);
begin
  //frmGetBackPassword.LabelStatus.Caption := MsgLabel.Caption;
  frmGetBackPassword.Open;
end;

procedure TfrmMain.RzBmpButtonChgPassWordClick(Sender: TObject);
begin
  //frmChangePassword.LabelStatus.Caption := MsgLabel.Caption;
  frmChangePassword.Open;
end;

procedure TfrmMain.ReleaseClient(sDirectory: string; GameZone: TGameZone);
  {function HostToIP(Name: string; var Ip: string): Boolean;
  var
    wsdata: TWSAData;
    hostName: array[0..255] of char;
    hostEnt: PHostEnt;
    addr: PChar;
  begin
    WSAStartup($0101, wsdata);
    try
      gethostname(hostName, SizeOf(hostName));
      StrPCopy(hostName, Name);
      hostEnt := gethostbyname(hostName);
      if Assigned(hostEnt) then
        if Assigned(hostEnt^.h_addr_list) then begin
          addr := hostEnt^.h_addr_list^;
          if Assigned(addr) then begin
            Ip := Format('%d.%d.%d.%d', [Byte(addr[0]),
              Byte(addr[1]), Byte(addr[2]), Byte(addr[3])]);
            Result := True;
          end
          else
            Result := FALSE;
        end
        else
          Result := FALSE
      else begin
        Result := FALSE;
      end;
    finally
      WSACleanup;
    end
  end; }
var
  Res: TResourceStream;
  //sIpAddr: string;
  Myinifile: TIniFile;
  sServerAddr: string;
  sServerPort: string;
begin
  FileSetAttr(sDirectory + g_sProgamFile, 0);
  try
    MirClient.SaveToFile(sDirectory + g_sProgamFile);
  except

  end;
  {  Res := TResourceStream.Create(HInstance, 'MirClient', PChar('exe'));
  try
    FileSetAttr(sDirectory + g_sProgamFile, 0);
    Res.SaveToFile(sDirectory + g_sProgamFile);
  except

  end;
  Res.Free;}
 { FileSetAttr(sDirectory + g_sClientFile, 0);
  Res := TResourceStream.Create(HInstance, 'FirClient', PChar('dll'));
  try
    Res.SaveToFile(sDirectory + g_sClientFile);
    Res.Free;
    Res := nil;
  except
    Res.Free;
    Res := nil;
  end;  }

  if GameZone.Encrypt then begin
    sServerAddr := DeCodeString(GameZone.GameHost);
    sServerPort := DeCodeString(GameZone.GameIPPort);
  end else begin
    sServerAddr := GameZone.GameHost;
    sServerPort := GameZone.GameIPPort;
  end;
  FileSetAttr(sDirectory + 'mir.ini', 0);
  Myinifile := TIniFile.Create(sDirectory + 'mir.ini');
  if Myinifile <> nil then begin
    Myinifile.WriteString('Setup', 'FontName', '宋体');
    Myinifile.WriteBool('Setup', 'FullScreen', g_boFullScreen);
    Myinifile.WriteString('Setup', 'Serveraddr', GameZone.GameIPaddr); //IP地址
    Myinifile.WriteString('Setup', 'ServerPort', sServerPort); //端口
    Myinifile.Free;
  end;
 { FileSetAttr(sDirectory + 'ftp.ini', 0);
  Myinifile := TIniFile.Create(sDirectory + 'ftp.ini');
  if Myinifile <> nil then begin
    Myinifile.WriteInteger('Server', 'Servercount', 1);
    Myinifile.WriteString('Server', 'server1caption', GameZone.ServerName); //开门名称
    Myinifile.WriteString('Server', 'server1name', GameZone.ServerName); //服务器名称
    Myinifile.Free;
  end;   }

  {FileSetAttr(sDirectory + g_sProgamFile, 2);
  FileSetAttr(sDirectory + g_sClientFile, 2); }
end;

function RunApp(AppName: string): Integer;
var
  Sti: TStartupInfo;
  ProcessInfo: TProcessInformation;
begin
  FillMemory(@Sti, SizeOf(Sti), 0);
  Sti.wShowWindow := SW_SHOW;
  Sti.dwFlags := STARTF_USEFILLATTRIBUTE;
  Sti.dwFillAttribute := FOREGROUND_INTENSITY or BACKGROUND_BLUE;
  if CreateProcess(PChar(AppName), nil,
    nil, nil, False,
    0, nil, PChar(ExtractFilePath(AppName)),
    Sti, ProcessInfo) then begin
    Result := ProcessInfo.dwProcessId;
  end else Result := -1;
end;

{procedure CopyFile(const Sour, Dest: string);
var
  FileOp: TSHFileOpStruct;
begin
  with FileOp do begin
    Wnd := FrmMain.HandLe;
    wFunc := FO_Copy; //更换此参数可实现拷贝和更名
    pFrom := PChar(Sour);
    pTo := PChar(Dest);
    fFlags := FOF_NoConfirmation;
    fAnyOperationsAborted := False;
    hNameMappings := nil;
    lpszProgressTitle := nil
  end;
  SHFileOperation(FileOp);
end;}

//判断文件是否正在使用

function IsFileInUse(FName: string): Boolean;
var
  HFileRes: HFILE;
begin
  Result := False;
  if not FileExists(FName) then
    Exit;
  HFileRes := CreateFile(PChar(FName), GENERIC_READ or GENERIC_WRITE, 0,
    nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  Result := (HFileRes = INVALID_HANDLE_VALUE);
  if not Result then
    CloseHandle(HFileRes);
end;


procedure TfrmMain.TimerStartTimer(Sender: TObject);
var
  I, n01: Integer;
  GameZone: TGameZone;
  TreeNode: TTreeNode;
  ChildTreeNode: TTreeNode;
  sFileName: string;
  //GameArea: pTGameArea;
begin
  TimerStart.Enabled := False;
  //ParamStr(1);

  if not CheckMirPath(g_sSelfFilePath) then begin
    if Application.MessageBox('目录不正确,是否自动搜索热血传奇客户端?',
      '提示信息',
      MB_YESNO + MB_ICONQUESTION) = IDYES then begin
      if SearchMirClient(g_sSelfFilePath) then begin

        if IsFileInUse(g_sMirPath + g_sSelfFileName) then begin
          if Pos('.', g_sSelfFileName) > 0 then begin
            g_sSelfFileName := Copy(g_sSelfFileName, 1, Pos('.', g_sSelfFileName) - 1);
          end;
          //g_sSelfFileName := Copy(g_sSelfFileName, 1, Length(g_sSelfFileName) - 4);
          //ChangeFileExt(ExtractFileName(sFileName),'');
          n01 := 0;
          while TRUE do begin
            sFileName := g_sMirPath + g_sSelfFileName + IntToStr(n01) + '.exe';
            if not FileExists(sFileName) then Break;
          end;
          g_sSelfFileName := g_sSelfFileName + IntToStr(n01) + '.exe';
        end else begin
          FileSetAttr(g_sMirPath + g_sSelfFileName, 0);
          DeleteFile(g_sMirPath + g_sSelfFileName);
        end;

        CopyFile(PChar(Application.ExeName), PChar(g_sMirPath + g_sSelfFileName), True); //复制自己
        RunApp(g_sMirPath + g_sSelfFileName); //启动
        Application.Terminate;
        Exit;
      end;
    end else begin
      if not SelectDirectory('请选择您的热血传奇客户端“Legend of mir2”目录', '', g_sMirPath, Handle) then begin
        Close;
        Exit;
      end;
      if (g_sMirPath <> '') and (g_sMirPath[Length(g_sMirPath)] <> '\') then g_sMirPath := g_sMirPath + '\';
      if not CheckMirPath(g_sMirPath) then begin
        Close;
        Exit;
      end else begin
        WriteRegKey(1, R_MySubKey, R_Key, g_sMirPath);

        if IsFileInUse(g_sMirPath + g_sSelfFileName) then begin
          if Pos('.', g_sSelfFileName) > 0 then begin
            g_sSelfFileName := Copy(g_sSelfFileName, 1, Pos('.', g_sSelfFileName) - 1);
          end;
          //g_sSelfFileName := Copy(g_sSelfFileName, 1, Length(g_sSelfFileName) - 4);
          //ChangeFileExt(ExtractFileName(sFileName),'');
          n01 := 0;
          while TRUE do begin
            sFileName := g_sMirPath + g_sSelfFileName + IntToStr(n01) + '.exe';
            if not FileExists(sFileName) then Break;
          end;
          g_sSelfFileName := g_sSelfFileName + IntToStr(n01) + '.exe';
        end else begin
          FileSetAttr(g_sMirPath + g_sSelfFileName, 0);
          DeleteFile(g_sMirPath + g_sSelfFileName);
        end;

        CopyFile(PChar(Application.ExeName), PChar(g_sMirPath + g_sSelfFileName), True); //复制自己
        RunApp(g_sMirPath + g_sSelfFileName); //启动
        Application.Terminate;
        Exit;
      end;
    end;
  end;

  g_sLocalGameListFileName := g_sMirPath + 'LocalCqfir.Dat';

  Timer.Enabled := True;

  //g_GameList.LoadFromFile(g_sGameListFileName);
  g_LocalGameList.LoadFromFile(g_sLocalGameListFileName);
  LoadListToBox();
  TreeNode := TreeView.Items.GetFirstNode;
  if TreeNode <> nil then begin
    ChildTreeNode := TreeNode.getFirstChild;
    if ChildTreeNode <> nil then begin
      TreeView.SetFocus;
      ChildTreeNode.Selected := True;
      ChildTreeNode.Focused := True;
      TreeViewClick(TreeView);
    end;
  end;
end;

procedure TfrmMain.LoadListToBox();
var
  I, II: Integer;
  GameZone: TGameZone;
  ChildGameZone: TGameZone;
  TreeNode: TTreeNode;
  ChildTreeNode: TTreeNode;
begin
  TreeView.Items.Clear;
  g_AllGameList.Clear;
  g_AllGameList.Strings.AddStrings(g_GameList.Strings);
  g_AllGameList.Strings.AddStrings(g_LocalGameList.Strings);
  for I := 0 to g_AllGameList.Count - 1 do begin
    GameZone := g_AllGameList.Items[I];
    if GameZone.Count > 0 then begin
      TreeNode := TreeView.Items.AddObject(nil, GameZone.Caption, GameZone);
      TreeNode.HasChildren := True;
      //TreeNode.EndEdit(False);
      for II := 0 to GameZone.Count - 1 do begin
        ChildGameZone := GameZone.Items[II];
        ChildTreeNode := TreeView.Items.AddChildObject(TreeNode, ChildGameZone.Caption, ChildGameZone);
        //TreeNode.Expand(ChildGameZone.Expand);
        //ChildTreeNode.EndEdit(False);
        //Showmessage(ChildGameZone.Caption);
      end;
      ChildTreeNode.Expand(ChildGameZone.Expand);
    end;
  end;
end;

procedure TfrmMain.RzBmpButtonEditGameListClick(Sender: TObject);
begin
  frmEditGame := TfrmEditGame.Create(Owner);//打开编辑列表
  frmEditGame.Open();
  frmEditGame.Free;
end;

procedure TfrmMain.RzBmpButtonAutoLoginClick(Sender: TObject);
begin   //脱机登陆
  {try
    if g_SelGameZone <> nil then begin
      frmCMain := TfrmCMain.Create(Application);
      frmCMain.Open;
      Application.Minimize;
    end;
  except
  end; }
end;

procedure TfrmMain.CloseTimerTimer(Sender: TObject);
var
  BatchFile: TextFile;
  BatchFileName: string;
  ProcessInfo: TProcessInformation;
  StartUpInfo: TStartupInfo;
begin
  CloseTimer.Enabled := False;
  BatchFileName := ExtractFilePath(ParamStr(0)) + '$$s$$.bat';
  FileSetAttr(BatchFileName, 0);
  DeleteFile(BatchFileName);
  AssignFile(BatchFile, BatchFileName);
  Rewrite(BatchFile);
  Writeln(BatchFile, ':tryn');
  Writeln(BatchFile, 'ren ' + g_sSelfFileName + ' ' + g_sSelfFileName + '.Del');
  Writeln(BatchFile, 'if exist "' + g_sSelfFileName + '.Del' + '"' + ' goto try');
  Writeln(BatchFile, 'if exist "' + g_sSelfFileName + '"' + ' goto tryn');
  Writeln(BatchFile, ':try');
  Writeln(BatchFile, 'del "' + g_sSelfFileName + '.Del' + '"');
  Writeln(BatchFile, 'if exist "' + g_sSelfFileName + '.Del' + '"' + ' goto try');

  Writeln(BatchFile, ':refren');
  Writeln(BatchFile, 'ren ' + g_sUpgradeSelfFileName + ' ' + g_sSelfFileName);
  Writeln(BatchFile, 'if not exist "' + g_sSelfFileName + '"' + ' goto refren');

  Writeln(BatchFile, 'start ' + g_sSelfFileName);
  Writeln(BatchFile, 'del %0');
  Writeln(BatchFile, 'exit');
  CloseFile(BatchFile);
    //FileSetAttr(BatchFileName, 2);
  FileSetAttr(ExtractFilePath(ParamStr(0)) + g_sSelfFileName, 0);
  FillChar(StartUpInfo, SizeOf(StartUpInfo), $00);
  StartUpInfo.dwFlags := STARTF_USESHOWWINDOW;
  StartUpInfo.wShowWindow := SW_Hide;
  if CreateProcess(nil, PChar(BatchFileName), nil, nil,
    False, IDLE_PRIORITY_CLASS, nil, nil, StartUpInfo,
    ProcessInfo) then begin
    CloseHandle(ProcessInfo.hThread);
    CloseHandle(ProcessInfo.hProcess);
  end;
  Close;
end;

procedure TfrmMain.ImageMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if Button = mbLeft then begin
    ReleaseCapture;
    Perform(WM_SYSCOMMAND, $F012, 0);
  end;
end;


procedure TfrmMain.RzBmpButtonMinClick(Sender: TObject);
begin
  Application.Minimize;
end;


procedure TfrmMain.ReadSkin;
  function IncPointer(Buffer: Pointer; Size: Integer): Pointer;
  begin
    Result := Pointer(Integer(Buffer) + Size);
  end;

var
  I: Integer;
  nLen: Integer;
  Buffer: Pointer;
  ReadBuffer: Pointer;
  OutBuf: Pointer;
  OutBytes: Integer;
  Bitmap: TBitmap;
  FileStream: TMemoryStream;
begin
  Buffer := g_Buffer;
  FileStream := TMemoryStream.Create;

  Application.ProcessMessages;
  if g_GameLoginConfig.ComponentImages[0].UpSize > 0 then begin
    DecompressBuf(Buffer, g_GameLoginConfig.ComponentImages[0].UpSize, 0, OutBuf, OutBytes);
    Buffer := IncPointer(Buffer, g_GameLoginConfig.ComponentImages[0].UpSize);

    FileStream.Clear;
    FileStream.Write(OutBuf^, OutBytes);
    FileStream.Position := 0;
    FreeMem(OutBuf);
    //设置背景
    Bitmap := TBitmap.Create;
    Bitmap.LoadFromStream(FileStream);
    FileStream.Position := 0;
    //Bitmap.SaveToFile('Bitmap.bmp');
    ClientHeight := Bitmap.Height;
    ClientWidth := Bitmap.Width;
    Image.Picture.Bitmap.Assign(Bitmap);
    Image.Transparent := g_GameLoginConfig.Transparent;
    //Image.Picture.Bitmap.LoadFromStream(FileStream);
    //Image.Picture.Bitmap.TransparentColor := clBlack;
    //Image.Picture.Bitmap.Transparent := True;
    if Image.Transparent then
      Image.RecreateRegion;

    Bitmap.Free;
  end;
  Application.ProcessMessages;
{------------------------------------------------------------------------------}
  //设置其他控件位置
  for I := 0 to Length(g_GameLoginConfig.ComponentConfigs) - 1 do begin
    Application.ProcessMessages;
    if WebBrowser = g_ComponentList.Items[I] then begin
      g_nWebHeiht := g_GameLoginConfig.ComponentConfigs[I].Height;
      g_WebBrowser := g_GameLoginConfig.ComponentConfigs[I];
    end else begin
      TWinControl(g_ComponentList.Items[I]).Left := g_GameLoginConfig.ComponentConfigs[I].Left;
      TWinControl(g_ComponentList.Items[I]).Top := g_GameLoginConfig.ComponentConfigs[I].Top;
      TWinControl(g_ComponentList.Items[I]).Width := g_GameLoginConfig.ComponentConfigs[I].Width;
      TWinControl(g_ComponentList.Items[I]).Height := g_GameLoginConfig.ComponentConfigs[I].Height;
      if TWinControl(g_ComponentList.Items[I]).Visible then
        with TWinControl(g_ComponentList.Items[I]) do
          SetBounds(Left, Top, Width, Height);
    end;
    TWinControl(g_ComponentList.Items[I]).Visible := g_GameLoginConfig.ComponentConfigs[I].Visible;
  end;
  //设置游览器位置
  WebBrowser.Left := g_WebBrowser.Left;
  WebBrowser.Top := g_WebBrowser.Top;
  WebBrowser.Width := g_WebBrowser.Width;
  WebBrowser.Height := g_WebBrowser.Height;

  WebBrowser.ParentWindow := Handle;
  with g_WebBrowser do
    WebBrowser.SetBounds(Left, Top, Width, Height);
//设置按钮图片
  for I := 1 to Length(g_GameLoginConfig.ComponentImages) - 1 do begin
    Application.ProcessMessages;
    if TRzBmpButton(g_ComponentList.Items[I - 1]).Visible then begin
      if (g_GameLoginConfig.ComponentImages[I].UpSize > 0) and (TRzBmpButton(g_ComponentList.Items[I - 1]).Bitmaps.Up <> nil) then begin
        FileStream.Clear;
        FileStream.Write(Buffer^, g_GameLoginConfig.ComponentImages[I].UpSize);
        Buffer := IncPointer(Buffer, g_GameLoginConfig.ComponentImages[I].UpSize);
        FileStream.Position := 0;
        TRzBmpButton(g_ComponentList.Items[I - 1]).Bitmaps.Up.LoadFromStream(FileStream);
      end;
      if (g_GameLoginConfig.ComponentImages[I].HotSize > 0) and (TRzBmpButton(g_ComponentList.Items[I - 1]).Bitmaps.Hot <> nil) then begin
        FileStream.Clear;
        FileStream.Write(Buffer^, g_GameLoginConfig.ComponentImages[I].HotSize);
        Buffer := IncPointer(Buffer, g_GameLoginConfig.ComponentImages[I].HotSize);
        FileStream.Position := 0;
        TRzBmpButton(g_ComponentList.Items[I - 1]).Bitmaps.Hot.LoadFromStream(FileStream);
      end;
      if (g_GameLoginConfig.ComponentImages[I].DownSize > 0) and (TRzBmpButton(g_ComponentList.Items[I - 1]).Bitmaps.Down <> nil) then begin
        FileStream.Clear;
        FileStream.Write(Buffer^, g_GameLoginConfig.ComponentImages[I].DownSize);
        Buffer := IncPointer(Buffer, g_GameLoginConfig.ComponentImages[I].DownSize);
        FileStream.Position := 0;
        TRzBmpButton(g_ComponentList.Items[I - 1]).Bitmaps.Down.LoadFromStream(FileStream);
      end;
      if (g_GameLoginConfig.ComponentImages[I].Disabled > 0) and (TRzBmpButton(g_ComponentList.Items[I - 1]).Bitmaps.Disabled <> nil) then begin
        FileStream.Clear;
        FileStream.Write(Buffer^, g_GameLoginConfig.ComponentImages[I].Disabled);
        Buffer := IncPointer(Buffer, g_GameLoginConfig.ComponentImages[I].Disabled);
        FileStream.Position := 0;
        TRzBmpButton(g_ComponentList.Items[I - 1]).Bitmaps.Disabled.LoadFromStream(FileStream);
      end;
    end;
  end;
  FreeMem(g_Buffer);

  FileStream.Free;
  //设置列表
  TreeView.Color := g_GameLoginConfig.ViewBColor;
  TreeView.Font.Color := g_GameLoginConfig.ViewFColor;
  g_boReadSkin := True;
end;

procedure TfrmMain.TimerReakSkinTimer(Sender: TObject);
begin
  TimerReakSkin.Enabled := False;
  ReadSkin;
end;

initialization
  begin
    g_ComponentList := TList.Create;
  end;
finalization
  begin
    g_ComponentList.Free;
  end;
end.

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

侠客软件开发

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值