khanat-opennel-code/code/nelns/login_system/nel_launcher_windows_ext2/nel_launcher_dlg.cpp

601 lines
15 KiB
C++

// NeLNS - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "std_afx.h"
#include <nel/misc/config_file.h>
#include "nel_launcher.h"
#include "nel_launcher_dlg.h"
#include "connection.h"
#include "patch.h"
/////////////////////////////////////////////////////////////////////////////
using namespace std;
using namespace NLMISC;
CConfigFile ConfigFile;
bool VerboseLog = false;
static CFileDisplayer NLFileDisplayer("nel_launcher.log", true);
static bool DispayerAdded = false;
void quit()
{
if(DispayerAdded)
{
createDebug();
DebugLog->removeDisplayer(&NLFileDisplayer);
InfoLog->removeDisplayer(&NLFileDisplayer);
WarningLog->removeDisplayer(&NLFileDisplayer);
ErrorLog->removeDisplayer(&NLFileDisplayer);
AssertLog->removeDisplayer(&NLFileDisplayer);
}
exit(0);
}
/////////////////////////////////////////////////////////////////////////////
// CNeLLauncherDlg dialog
CNeLLauncherDlg::CNeLLauncherDlg(CWnd* pParent /*=NULL*/)
: CDialog(CNeLLauncherDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CNeLLauncherDlg)
Login = _T("");
Password = _T("");
PatchStatus = _T("");
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
SelectedShardId = 0xFFFFFFFF;
}
void CNeLLauncherDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CNeLLauncherDlg)
DDX_Control(pDX, IDC_PATCH_PROGRESS, PatchProgress);
DDX_Control(pDX, IDC_SHARDS, ShardsList);
DDX_Text(pDX, IDC_LOGIN, Login);
DDX_Text(pDX, IDC_PASSWORD, Password);
DDX_Text(pDX, IDC_PATCH_STATUS, PatchStatus);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CNeLLauncherDlg, CDialog)
//{{AFX_MSG_MAP(CNeLLauncherDlg)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(ID_LOGIN, OnLogin)
ON_WM_SHOWWINDOW()
ON_NOTIFY(NM_DBLCLK, IDC_SHARDS, OnDblclkShards)
ON_BN_CLICKED(ID_CONNECT, OnConnect)
ON_WM_TIMER()
ON_BN_CLICKED(IDC_SIGNUP, OnSignup)
ON_BN_CLICKED(IDC_ATYSNEWS, OnAtysnews)
ON_BN_CLICKED(IDC_PLAYGUIDE, OnPlayguide)
ON_BN_CLICKED(IDC_RELEASENOTE, OnReleasenote)
ON_BN_CLICKED(IDC_LIVESUPPORT, OnLivesupport)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CNeLLauncherDlg message handlers
BOOL CNeLLauncherDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
createDebug();
DebugLog->addDisplayer(&NLFileDisplayer);
InfoLog->addDisplayer(&NLFileDisplayer);
WarningLog->addDisplayer(&NLFileDisplayer);
ErrorLog->addDisplayer(&NLFileDisplayer);
AssertLog->addDisplayer(&NLFileDisplayer);
DispayerAdded = true;
nlinfo("Loading config file");
ConfigFile.load("nel_launcher.cfg");
if(ConfigFile.exists("VerboseLog"))
VerboseLog = (ConfigFile.getVar("VerboseLog").asInt() == 1);
if(VerboseLog) nlinfo("Using verbose log mode");
GetDlgItem(IDC_SHARDS)->EnableWindow(FALSE);
GetDlgItem(ID_CONNECT)->EnableWindow(FALSE);
return TRUE; // return TRUE unless you set the focus to a control
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CNeLLauncherDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
// The system calls this to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CNeLLauncherDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}
void CNeLLauncherDlg::OnLogin()
{
nlinfo("OnLogin called");
UpdateData(TRUE);
string l = (LPCTSTR)Login;
string p = (LPCTSTR)Password;
if(!l.empty())
SetRegKey("Login", l);
GetDlgItem(IDC_LOGIN)->EnableWindow(FALSE);
GetDlgItem(IDC_PASSWORD)->EnableWindow(FALSE);
GetDlgItem(ID_LOGIN)->EnableWindow(FALSE);
// Check the login/pass
string res = checkLogin(l, p, ConfigFile.getVar("Application").asString(0));
if(res.empty())
{
ShardsList.DeleteAllItems();
ShardsList.InsertColumn(0, _T("Shard Name"), LVCFMT_LEFT, -1);
ShardsList.InsertColumn(1, _T("Version"), LVCFMT_LEFT, 70);
ShardsList.InsertColumn(2, _T("Status"), LVCFMT_LEFT, 50);
ShardsList.InsertColumn(3, _T("NbPlayers"), LVCFMT_LEFT, 70);
for(uint i = 0; i < Shards.size(); i++)
{
CString strItem;
LVITEM lvi;
// Insert the first item
lvi.mask = LVIF_TEXT;
strItem = Shards[i].Name.c_str();
lvi.iItem = i;
lvi.iSubItem = 0;
lvi.pszText = (LPTSTR)(LPCTSTR)(strItem);
int idx = ShardsList.InsertItem(&lvi);
lvi.iItem = idx;
strItem = Shards[i].Version.c_str();
lvi.iSubItem = 1;
lvi.pszText = (LPTSTR)(LPCTSTR)(strItem);
ShardsList.SetItem(&lvi);
strItem = Shards[i].Online?"Online":"Offline";
lvi.iSubItem = 2;
lvi.pszText = (LPTSTR)(LPCTSTR)(strItem);
ShardsList.SetItem(&lvi);
strItem = toString(Shards[i].NbPlayers).c_str();
lvi.iSubItem = 3;
lvi.pszText = (LPTSTR)(LPCTSTR)(strItem);
ShardsList.SetItem(&lvi);
ShardsList.SetItemData(i, Shards[i].ShardId);
}
ShardsList.SetColumnWidth(0, LVSCW_AUTOSIZE);
GetDlgItem(IDC_SHARDS)->EnableWindow(TRUE);
GetDlgItem(ID_CONNECT)->EnableWindow(TRUE);
}
else
{
MessageBox(res.c_str(), "Error");
GetDlgItem(IDC_LOGIN)->EnableWindow(TRUE);
GetDlgItem(IDC_PASSWORD)->EnableWindow(TRUE);
GetDlgItem(ID_LOGIN)->EnableWindow(TRUE);
GetDlgItem(IDC_PASSWORD)->SetFocus();
}
}
static const string RootKey = "SOFTWARE\\Nevrax\\Ryzom";
static const uint32 KeyMaxLength = 1024;
string CNeLLauncherDlg::GetRegKeyValue(const string &Entry)
{
HKEY hkey;
string ret;
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, RootKey.c_str(), 0, KEY_READ, &hkey) == ERROR_SUCCESS)
{
DWORD dwType = 0L;
DWORD dwSize = KeyMaxLength;
unsigned char Buffer[KeyMaxLength];
if(RegQueryValueEx(hkey, Entry.c_str(), NULL, &dwType, Buffer, &dwSize) != ERROR_SUCCESS)
{
nlwarning("Can't get the reg key '%s'", Entry.c_str());
}
else
{
ret = (char*)Buffer;
}
RegCloseKey(hkey);
}
else
{
nlwarning("Can't get the reg key '%s'", Entry.c_str());
}
return ret;
}
void CNeLLauncherDlg::SetRegKey(const string &ValueName, const string &Value)
{
HKEY hkey;
DWORD dwDisp;
if(RegCreateKeyEx(HKEY_LOCAL_MACHINE, RootKey.c_str(), 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, &dwDisp) == ERROR_SUCCESS)
{
RegSetValueEx(hkey, ValueName.c_str(), 0L, REG_SZ, (const BYTE *)Value.c_str(), (Value.size())+1);
RegCloseKey(hkey);
}
else
{
nlwarning("Can't set the reg key '%s' '%s'", ValueName.c_str(), Value.c_str());
}
}
void CNeLLauncherDlg::OnShowWindow(BOOL bShow, UINT nStatus)
{
CDialog::OnShowWindow(bShow, nStatus);
string l = GetRegKeyValue("Login").c_str();
if(!l.empty())
{
Login = l.c_str();
UpdateData(FALSE);
GetDlgItem(IDC_PASSWORD)->SetFocus();
}
else
{
GetDlgItem(IDC_LOGIN)->SetFocus();
}
}
void CNeLLauncherDlg::OnDblclkShards(NMHDR* pNMHDR, LRESULT* pResult)
{
OnConnect();
*pResult = 0;
}
void CNeLLauncherDlg::OnConnect()
{
nlinfo("OnConnect called");
UpdateData(TRUE);
string log = (LPCTSTR)Login;
string pass = (LPCTSTR)Password;
GetDlgItem(IDC_SHARDS)->EnableWindow(FALSE);
GetDlgItem(ID_CONNECT)->EnableWindow(FALSE);
POSITION p = ShardsList.GetFirstSelectedItemPosition();
if(!p)
{
MessageBox("Please, select a shard and then press Connect button", "Information");
goto end;
}
while (p)
{
int nSelected = ShardsList.GetNextSelectedItem(p);
// Do something with item nSelected
TCHAR szBuffer[1024];
DWORD cchBuf(1024);
LVITEM lvi;
lvi.iItem = nSelected;
lvi.iSubItem = 0;
lvi.mask = LVIF_TEXT;
lvi.pszText = szBuffer;
lvi.cchTextMax = cchBuf;
ShardsList.GetItem(&lvi);
uint32 shardId = ShardsList.GetItemData(nSelected);
uint i;
for(i = 0; i < Shards.size(); i++)
{
if(Shards[i].ShardId == shardId)
{
break;
}
}
nlassert(i != Shards.size());
if(!Shards[i].Online)
{
MessageBox("You can't connect to an offline shard (error code 15)", "Error");
goto end;
}
SelectedShardId = shardId;
if(!Shards[i].Version.empty() && Shards[i].Version != getVersion())
{
// We need to patch first
if(IDCANCEL == MessageBox("You need to patch to access to this shard", "Information", MB_OKCANCEL))
{
goto end;
}
if(Shards[i].PatchURL.empty())
{
MessageBox("You can't patch because the PatchURL isn't setup in database (error code 16)", "Error");
goto end;
}
string urlok, urlfailed;
startPatchThread(Shards[i].PatchURL, Shards[i].Version, urlok, urlfailed, "\n");
PatchProgress.EnableWindow(TRUE);
PatchProgress.SetRange(0, getTotalFilesToGet());
PatchProgress.SetPos(0);
SetTimer(0, 100, NULL);
}
else
{
// Version is good, connect and launch the client
ConnectToShard();
}
}
return;
end:
GetDlgItem(IDC_SHARDS)->EnableWindow(TRUE);
GetDlgItem(ID_CONNECT)->EnableWindow(TRUE);
}
void CNeLLauncherDlg::ConnectToShard()
{
nlinfo("ConnectToShard called");
nlassert(SelectedShardId != 0xFFFFFFFF);
string cookie, addr;
string res = selectShard(SelectedShardId, cookie, addr);
if(res.empty())
{
LaunchClient(cookie, addr);
}
else
{
MessageBox(res.c_str(), "Error");
}
GetDlgItem(IDC_SHARDS)->EnableWindow(TRUE);
GetDlgItem(ID_CONNECT)->EnableWindow(TRUE);
}
void CNeLLauncherDlg::LaunchClient(const string &cookie, const string &addr)
{
nlinfo("LaunchClient called");
string rapp = ConfigFile.getVar("Application").asString(1);
string dir = ConfigFile.getVar("Application").asString(2);
// vector<string> vargs;
const char *args[50];
// vargs.push_back(cookie);
// vargs.push_back(addr);
// uint i;
args[0] = rapp.c_str();
/*
for(i = 0; i < vargs.size(); i++)
{
args[i+1] = vargs[i].c_str();
}
*/
args[1] = NULL;
MessageBox("We have made changes to the way you gain access to the game:\r\n - this is the last time you are using the nel launcher\r\n - nel launcher is now merged with the game\r\n - next time you want to play SoR you need to click on this shortcut 'Play Ryzom' or click on 'client_ryzom_d.exe in your ryzom folder\r\n - You will now be taken to the new login area", "Please Read!", MB_ICONEXCLAMATION);
const char *SE_TRANSLATOR_IN_MAIN_MODULE = "NEL_SE_TRANS";
TCHAR envBuf [2];
if ( GetEnvironmentVariable( SE_TRANSLATOR_IN_MAIN_MODULE, envBuf, 2 ) != 0)
{
SetEnvironmentVariable( SE_TRANSLATOR_IN_MAIN_MODULE, NULL );
}
if(!dir.empty())
_chdir(dir.c_str());
// execute, should better use CreateProcess()
if(_execvp(rapp.c_str(), args) == -1)
{
MessageBox("Can't execute the game (error code 17)", "Error");
}
else
{
quit();
}
}
void CNeLLauncherDlg::OnTimer(UINT nIDEvent)
{
CDialog::OnTimer(nIDEvent);
PatchProgress.SetRange(0, getTotalFilesToGet());
PatchProgress.SetPos(getCurrentFilesToGet());
string state, log;
if(patchState(state, log))
{
PatchStatus = state.c_str();
UpdateData(FALSE);
}
string url;
bool res;
if(patchEnded(url, res))
{
KillTimer(0);
PatchProgress.EnableWindow(FALSE);
if(res)
{
MessageBox("Patch completed successfully", "Information");
// Version is now good, connect and launch the client
ConnectToShard();
}
else
{
MessageBox(url.c_str(), "Error");
GetDlgItem(IDC_SHARDS)->EnableWindow(TRUE);
GetDlgItem(ID_CONNECT)->EnableWindow(TRUE);
}
}
}
void CNeLLauncherDlg::OnCancel()
{
nlinfo("OnCancel called");
quit();
CDialog::OnCancel();
}
LONG GetRegKey(HKEY key, LPCTSTR subkey, LPTSTR retdata)
{
HKEY hkey;
LONG retval = RegOpenKeyEx(key, subkey, 0, KEY_QUERY_VALUE, &hkey);
if (retval == ERROR_SUCCESS)
{
long datasize = MAX_PATH;
char data[MAX_PATH];
RegQueryValue(hkey, NULL, data, &datasize);
lstrcpy(retdata,data);
RegCloseKey(hkey);
}
return retval;
}
void GotoURL(LPCTSTR url)
{
char key[MAX_PATH + MAX_PATH];
// First try ShellExecute()
HINSTANCE result = ShellExecute(NULL, "open", url, NULL,NULL, SW_SHOW);
// If it failed, get the .htm regkey and lookup the program
if ((UINT)result <= HINSTANCE_ERROR) {
if (GetRegKey(HKEY_CLASSES_ROOT, ".htm", key) == ERROR_SUCCESS)
{
lstrcat(key, "\\shell\\open\\command");
if (GetRegKey(HKEY_CLASSES_ROOT,key,key) == ERROR_SUCCESS)
{
char *pos;
pos = strstr(key, "\"%1\"");
if (pos == NULL)
{ // No quotes found
pos = strstr(key, "%1"); // Check for %1, without quotes
if (pos == NULL) // No parameter at all...
pos = key+lstrlen(key)-1;
else
*pos = '\0'; // Remove the parameter
}
else
*pos = '\0'; // Remove the parameter
lstrcat(pos, " ");
lstrcat(pos, url);
result = (HINSTANCE) WinExec(key, SW_SHOW);
}
}
}
}
void CNeLLauncherDlg::OnSignup()
{
if(ConfigFile.exists("SignUpURL"))
GotoURL(ConfigFile.getVar("SignUpURL").asString().c_str());
else
GotoURL("http://www.ryzom.com");
}
void CNeLLauncherDlg::OnAtysnews()
{
GotoURL("http://atys.ryzom.com");
}
void CNeLLauncherDlg::OnPlayguide()
{
GotoURL("http://atys.ryzom.com/forums/index.php?c=4");
}
void CNeLLauncherDlg::OnReleasenote()
{
GotoURL("http://atys.ryzom.com/?page=update_latest");
}
void CNeLLauncherDlg::OnLivesupport()
{
GotoURL("http://atys.ryzom.com/?page=beta_chat");
}