// NeLNS - MMORPG Framework // 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 . #include "std_afx.h" #include #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 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"); }