//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //=============================================================================// #include "CDKeyEntryDialog.h" #include "EngineInterface.h" #include "steamcommon.h" // VALVE_AUTH cd key checking code #include "ValidateNewValveCDKeyClient.h" #include "ValveCDKeyGameAndTerritoryCodes.h" #include "vgui/IInput.h" #include "vgui/IPanel.h" #include "vgui/ISystem.h" #include "vgui/ISurface.h" #include "vgui_controls/Button.h" #include "vgui_controls/MessageBox.h" #include "vgui_controls/TextEntry.h" #include "vstdlib/random.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" #include using namespace vgui; #define FAKE_CDKEY_LEN 49 #define FAKE_CDKEY_REGKEY "HKEY_CURRENT_USER\\Software\\Valve\\Source\\Settings\\EncryptedCDKey" //----------------------------------------------------------------------------- // Purpose: hacky class to make all input uppercase //----------------------------------------------------------------------------- class CUpperCaseTextEntry : public vgui::TextEntry { DECLARE_CLASS_SIMPLE( CUpperCaseTextEntry, vgui::TextEntry ); public: CUpperCaseTextEntry(vgui::Panel *parent, const char *name) : TextEntry(parent, name) {} virtual void InsertChar(wchar_t unichar) { // only allow input of valid cdkey characters if (unichar >= 'a' && unichar <= 'z') { // force to be uppercase BaseClass::InsertChar(unichar - 'a' + 'A'); } else if ((unichar >= 'A' && unichar <= 'Z') || (unichar >= '0' && unichar <= '9')) { BaseClass::InsertChar(unichar); } } }; class CloseMessageBox : public vgui::MessageBox { public: CloseMessageBox(const char *title, const char *text, Panel *parent = NULL): MessageBox( title, text, parent) {} virtual void OnClose() { engine->ClientCmd_Unrestricted("quit\n"); } }; //----------------------------------------------------------------------------- // Purpose: Constructor //----------------------------------------------------------------------------- CCDKeyEntryDialog::CCDKeyEntryDialog(vgui::Panel *parent, bool inConnect) : BaseClass(parent, "CDKeyEntryDialog") { // see what type of cdkey we have SetDeleteSelfOnClose(true); m_bInConnect = inConnect; m_iErrCount = 0; m_bEnteredValidCDKey = false; m_pOK = new Button(this, "OKButton", "#GameUI_OK"); m_pQuitGame = new Button(this, "CancelButton", "#GameUI_Quit"); m_pEntry1 = new CUpperCaseTextEntry(this, "Entry1");; m_pEntry2 = new CUpperCaseTextEntry(this, "Entry2"); m_pEntry3 = new CUpperCaseTextEntry(this, "Entry3"); m_pEntry4 = new CUpperCaseTextEntry(this, "Entry4"); m_pEntry5 = new CUpperCaseTextEntry(this, "Entry5"); m_pEntry1->SetMaximumCharCount(5); m_pEntry2->SetMaximumCharCount(5); m_pEntry3->SetMaximumCharCount(5); m_pEntry4->SetMaximumCharCount(5); m_pEntry5->SetMaximumCharCount(5); m_pEntry1->SetAutoProgressOnHittingCharLimit(true); m_pEntry2->SetAutoProgressOnHittingCharLimit(true); m_pEntry3->SetAutoProgressOnHittingCharLimit(true); m_pEntry4->SetAutoProgressOnHittingCharLimit(true); m_pEntry5->SetAutoProgressOnHittingCharLimit(false); SetSizeable(false); SetSize(360, 224); SetTitle("#GameUI_CDKey", true); LoadControlSettings("Resource/ValveCDKeyEntryDialog.res"); // MoveToCenterOfScreen(); SetMinimizeButtonVisible(false); SetMaximizeButtonVisible(false); m_pOK->SetEnabled(false); } //----------------------------------------------------------------------------- // Purpose: Destructor //----------------------------------------------------------------------------- CCDKeyEntryDialog::~CCDKeyEntryDialog() { vgui::surface()->RestrictPaintToSinglePanel(NULL); } //----------------------------------------------------------------------------- // Purpose: returns true if there is a valid weak check key in the registry //----------------------------------------------------------------------------- bool CCDKeyEntryDialog::IsValidWeakCDKeyInRegistry() { char fakekey[FAKE_CDKEY_LEN]; if (vgui::system()->GetRegistryString(FAKE_CDKEY_REGKEY, fakekey, sizeof(fakekey))) { if (strlen(fakekey) == (FAKE_CDKEY_LEN - 1) && fakekey[17] == 'E' && fakekey[31] == 'z' && fakekey[43] == 'n') { return true; } } return false; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCDKeyEntryDialog::OnCommand(const char *command) { if (!stricmp(command, "OK")) { if (IsEnteredKeyValid()) { m_bEnteredValidCDKey = true; // write out fake key and continue char fakekey[FAKE_CDKEY_LEN]; for (int i = 0; i < sizeof(fakekey) - 1; i++) { // pick some random fields for us to check later if (i == 17) { fakekey[i] = 'E'; } else if (i == 31) { fakekey[i] = 'z'; } else if (i == 43) { fakekey[i] = 'n'; } else { fakekey[i] = RandomInt('0', 'z'); } } fakekey[sizeof(fakekey) - 1] = 0; vgui::system()->SetRegistryString(FAKE_CDKEY_REGKEY, fakekey); if ( m_bInConnect ) { engine->ClientCmd_Unrestricted( "retry\n" ); // retry the server connection with this new key... } Close(); } else { m_hErrorBox = new MessageBox("#GameUI_CDKey_Invalid_Title","#GameUI_CDKey_Invalid_Text", this); m_hErrorBox->ShowWindow( this ); } } else if (!stricmp(command, "Cancel") || !stricmp(command, "Close")) { Close(); } else { BaseClass::OnCommand(command); } if (!m_bEnteredValidCDKey) // moved away from the dialog box to make it a little harder to crack... { m_iErrCount++; if( m_iErrCount >= MAX_CDKEY_ERRORS ) { // too many bad entries, make em quit CloseMessageBox *bx = new CloseMessageBox("#GameUI_CDKey_Invalid_Title","#GameUI_CDKey_TooManyTries", this); bx->ShowWindow( this ); } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCDKeyEntryDialog::OnClose() { if (!m_bEnteredValidCDKey) { // if we don't have a valid key we can't continue engine->ClientCmd_Unrestricted("quit\n"); } BaseClass::OnClose(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCDKeyEntryDialog::OnThink() { if (!m_bEnteredValidCDKey) { // force us to be the only thing to draw VPANEL vpanel = m_hErrorBox.Get() ? m_hErrorBox->GetVPanel() : GetVPanel(); vgui::surface()->RestrictPaintToSinglePanel(vpanel); // make sure we're the focus if (!(input()->GetFocus() && ipanel()->HasParent(input()->GetFocus(), GetVPanel()))) { BaseClass::Activate(); } } BaseClass::OnThink(); } //----------------------------------------------------------------------------- // Purpose: returns whether or not the key entered by the user passes the simple check //----------------------------------------------------------------------------- bool CCDKeyEntryDialog::IsEnteredKeyValid() { // get the entered text char cdkey[32]; m_pEntry1->GetText(cdkey, 6); m_pEntry2->GetText(cdkey + 6, 6); m_pEntry3->GetText(cdkey + 12, 6); m_pEntry4->GetText(cdkey + 18, 6); m_pEntry5->GetText(cdkey + 24, 6); // add in the hyphens cdkey[5] = '-'; cdkey[11] = '-'; cdkey[17] = '-'; cdkey[23] = '-'; // verify the entry for (int i = 0; i < 29; i++) { if (cdkey[i] != '-' && !isalnum(cdkey[i])) return false; } #if !defined( NO_STEAM ) uint gameCode, salesTerritoryCode, uniqueSerialNumber; // test key - this key passes they weak check, but not the strong check // "5J5Q3-QCME2-2SMMV-SBHN9-43S2S" if ( SteamWeakVerifyNewValveCDKey(cdkey, &gameCode, &salesTerritoryCode, &uniqueSerialNumber) == eSteamErrorNone ) { // require hl2 retail cdkey (ATI Bundle keys are also in this category) if (gameCode == eHalfLife2) return true; } #endif return false; } //----------------------------------------------------------------------------- // Purpose: handles user entering data in fields //----------------------------------------------------------------------------- void CCDKeyEntryDialog::OnTextChanged(Panel *entry) { char cdkey[32]; m_pEntry1->GetText(cdkey, 6); m_pEntry2->GetText(cdkey + 6, 6); m_pEntry3->GetText(cdkey + 12, 6); m_pEntry4->GetText(cdkey + 18, 6); m_pEntry5->GetText(cdkey + 24, 6); // add in the hyphens cdkey[5] = '-'; cdkey[11] = '-'; cdkey[17] = '-'; cdkey[23] = '-'; if (strlen(cdkey) == 29) { m_pOK->SetEnabled(true); } else { m_pOK->SetEnabled(false); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCDKeyEntryDialog::Activate() { BaseClass::Activate(); m_pEntry1->RequestFocus(); }