This commit is contained in:
watrabi
2025-09-18 17:55:52 -04:00
commit 977f1ff4b8
15030 changed files with 17324420 additions and 0 deletions
+7
View File
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Settings>
<BaseUrl>http://www.watrbx.wtf/</BaseUrl>
<ContentFolder>..\..\..\content</ContentFolder>
<SilentCrashReport>0</SilentCrashReport>
<HideChatWindow>0</HideChatWindow>
</Settings>
File diff suppressed because it is too large Load Diff
+209
View File
@@ -0,0 +1,209 @@
#pragma once
#include "LogManager.h"
#include "Teleporter.h"
#include "SharedLauncher.h"
#include "rbx/atomic.h"
#include "v8datamodel/FastLogSettings.h"
#include "Util/HttpAsync.h"
#include "Util/Analytics.h"
// forward declarations
namespace po = boost::program_options;
class CProcessPerfCounter;
class DumpErrorUploader;
class RbxWebView;
namespace RBX {
// more forward declarations
class Game;
class UserInput;
class RenderJob;
class LeaveGameVerb;
class RecordToggleVerb;
class ScreenshotVerb;
class ToggleFullscreenVerb;
class ProfanityFilter;
class RecordToggleVerb;
class FunctionMarshaller;
class Document;
struct StandardOutMessage;
class View;
namespace Tasks { class Sequence; }
class Application {
enum RequestPlaceInfoResult
{
SUCCESS,
FAILED,
RETRY,
GAME_FULL,
USER_LEFT,
};
RBX::Analytics::InfluxDb::Points analyticsPoints;
public:
Application();
~Application();
// Initializes the application.
bool Initialize(HWND hWnd, HINSTANCE hInstance);
// Load AppSettings.xml
bool LoadAppSettings(HINSTANCE hInstance);
// Notification that Shutdown will be happening soon
void AboutToShutdown();
// Free resources and perform cleanup operations before Application
// destructor is called.
void Shutdown();
// Parses command line arguments.
bool ParseArguments(const char* argv);
// Posts messages the document or view care about (mouse, keyboard, window
// activation, etc).
void HandleWindowsMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
// Resizes the viewport when the window is resized.
void OnResize(WPARAM wParam, int cx, int cy);
void Teleport(const std::string& authenticationUrl,
const std::string& ticket,
const std::string& scriptUrl);
// Creates a crash dump which results in an upload to the server the next
// time the process runs.
void UploadSessionLogs();
// Show help wiki
void OnHelp();
// Named event to wait for before showing the window
std::string WaitEventName() { return waitEventName; }
static void OnGetMinMaxInfo(MINMAXINFO* lpMMI);
private:
boost::scoped_ptr<boost::thread> launchPlaceThread;
boost::scoped_ptr<boost::thread> reportingThread;
void LaunchPlaceThreadImpl(const std::string& placeLauncherUrl);
void InferredCrashReportingThreadImpl();
void InitializeNewGame(HWND hWnd);
void StartNewGame(HWND hWnd, HttpFuture& scriptResult, bool isTeleport);
void initializeLogger();
void setWindowFrame();
void initializeCrashReporter();
void uploadCrashData(bool userRequested);
void handleError(const std::exception& e);
bool requestPlaceInfo(int placeId, std::string& authenticationUrl,
std::string& ticket,
std::string& scriptUrl) const;
RequestPlaceInfoResult requestPlaceInfo(const std::string url, std::string& authenticationUrl,
std::string& ticket,
std::string& scriptUrl) const;
void renewLogin(const std::string& authenticationUrl,
const std::string& ticket) const;
HttpFuture renewLoginAsync(const std::string& authenticationUrl,
const std::string& ticket) const;
HttpFuture loginAsync(const std::string& userName, const std::string& passWord) const;
void shareHwnd(HWND hWnd);
const char* getVRDeviceName();
rbx::atomic<int> enteredShutdown;
HANDLE processLocal_stopPreventMultipleJobsThread;
HANDLE processLocal_stopWaitForVideoPrerollThread;
HWND mainWindow;
HANDLE mapFileForWnd;
LPCTSTR bufForWnd;
LONG stopLogsCleanup;
ATL::CEvent clenupFinishedEvent;
boost::scoped_ptr<boost::thread> logsCleanUpThread;
void logsCleanUpHelper();
// Thread to prevent multiple instances of WindowsPlayer from running
// simultaneously
void waitForNewPlayerProcess(HWND hWnd);
void waitForShowWindow(int delay);
void validateBootstrapperVersion();
// Sends messages to the debugger for display.
static void onMessageOut(const StandardOutMessage& message);
// Gets version number from .rc and returns display-friendly representation
std::string getversionNumber();
// determines what kind of game mode we are in
SharedLauncher::LaunchMode launchMode;
// Application owns the views
boost::scoped_ptr<View> mainView;
boost::scoped_ptr<RbxWebView> webView;
// Command-line arguments
po::variables_map vm;
// Filename and path
std::string moduleFilename;
// Settings (e.g. GlobalBasicSettings_10.xml)
std::string globalBasicSettingsPath;
// If not empty string this is the name of a named event the
std::string waitEventName;
// Application owns the Document
boost::scoped_ptr<Document> currentDocument;
// The crash report components.
bool crashReportEnabled, hideChat;
MainLogManager logManager;
boost::scoped_ptr<DumpErrorUploader> dumpErrorUploader;
boost::shared_ptr<CProcessPerfCounter> processPerfCounter;
boost::shared_ptr<ProfanityFilter> profanityFilter;
boost::scoped_ptr<boost::thread> singleRunningInstance;
boost::scoped_ptr<boost::thread> showWindowAfterEvent;
boost::scoped_ptr<boost::thread> validateBootstrapperVersionThread;
Teleporter teleporter;
FunctionMarshaller* marshaller;
bool spoofMD5; // Only used in DEBUG / NOOPT builds
boost::scoped_ptr<ToggleFullscreenVerb> toggleFullscreenVerb;
boost::scoped_ptr<LeaveGameVerb> leaveGameVerb;
boost::scoped_ptr<RecordToggleVerb> recordToggleVerb;
boost::scoped_ptr<ScreenshotVerb> screenshotVerb;
void initVerbs();
void shutdownVerbs();
void openUrlInBrowserApp(const std::string url);
void closeBrowser();
void doOpenUrl(const std::string url);
void doCloseBrowser();
void onDocumentStarted(bool isTeleport);
};
} // namespace RBX
+988
View File
@@ -0,0 +1,988 @@
#include "stdafx.h"
#include "Crypt.h"
#include "Util/MD5Hasher.h"
#include "shlwapi.h"
#pragma comment(lib, "crypt32.lib")
#pragma comment (lib, "wintrust")
bool VerifyEmbeddedSignature(LPCWSTR pwszSourceFile)
{
LONG lStatus;
DWORD dwLastError;
// Initialize the WINTRUST_FILE_INFO structure.
WINTRUST_FILE_INFO FileData;
memset(&FileData, 0, sizeof(FileData));
FileData.cbStruct = sizeof(WINTRUST_FILE_INFO);
FileData.pcwszFilePath = pwszSourceFile;
FileData.hFile = NULL;
FileData.pgKnownSubject = NULL;
/*
WVTPolicyGUID specifies the policy to apply on the file
WINTRUST_ACTION_GENERIC_VERIFY_V2 policy checks:
1) The certificate used to sign the file chains up to a root
certificate located in the trusted root certificate store. This
implies that the identity of the publisher has been verified by
a certification authority.
2) In cases where user interface is displayed (which this example
does not do), WinVerifyTrust will check for whether the
end entity certificate is stored in the trusted publisher store,
implying that the user trusts content from this publisher.
3) The end entity certificate has sufficient permission to sign
code, as indicated by the presence of a code signing EKU or no
EKU.
*/
GUID WVTPolicyGUID = WINTRUST_ACTION_GENERIC_VERIFY_V2;
WINTRUST_DATA WinTrustData;
// Initialize the WinVerifyTrust input data structure.
// Default all fields to 0.
memset(&WinTrustData, 0, sizeof(WinTrustData));
WinTrustData.cbStruct = sizeof(WinTrustData);
// Use default code signing EKU.
WinTrustData.pPolicyCallbackData = NULL;
// No data to pass to SIP.
WinTrustData.pSIPClientData = NULL;
// Disable WVT UI.
WinTrustData.dwUIChoice = WTD_UI_NONE;
// No revocation checking.
WinTrustData.fdwRevocationChecks = WTD_REVOKE_NONE;
// Verify an embedded signature on a file.
WinTrustData.dwUnionChoice = WTD_CHOICE_FILE;
// Default verification.
WinTrustData.dwStateAction = 0;
// Not applicable for default verification of embedded signature.
WinTrustData.hWVTStateData = NULL;
// Not used.
WinTrustData.pwszURLReference = NULL;
// Default.
WinTrustData.dwProvFlags = WTD_SAFER_FLAG;
// This is not applicable if there is no UI because it changes
// the UI to accommodate running applications instead of
// installing applications.
WinTrustData.dwUIContext = 0;
// Set pFile.
WinTrustData.pFile = &FileData;
// WinVerifyTrust verifies signatures as specified by the GUID
// and Wintrust_Data.
lStatus = WinVerifyTrust(
NULL,
&WVTPolicyGUID,
&WinTrustData);
switch (lStatus)
{
case ERROR_SUCCESS:
/*
Signed file:
- Hash that represents the subject is trusted.
- Trusted publisher without any verification errors.
- UI was disabled in dwUIChoice. No publisher or
time stamp chain errors.
- UI was enabled in dwUIChoice and the user clicked
"Yes" when asked to install and run the signed
subject.
*/
//wprintf_s(L"The file \"%s\" is signed and the signature "
// L"was verified.\n",
// pwszSourceFile);
return true;
case TRUST_E_NOSIGNATURE:
// The file was not signed or had a signature
// that was not valid.
// Get the reason for no signature.
dwLastError = GetLastError();
if (TRUST_E_NOSIGNATURE == dwLastError ||
TRUST_E_SUBJECT_FORM_UNKNOWN == dwLastError ||
TRUST_E_PROVIDER_UNKNOWN == dwLastError)
{
// The file was not signed.
//wprintf_s(L"The file \"%s\" is not signed.\n",
// pwszSourceFile);
return false;
}
else
{
// The signature was not valid or there was an error
// opening the file.
//wprintf_s(L"An unknown error occurred trying to "
// L"verify the signature of the \"%s\" file.\n",
// pwszSourceFile);
return false;
}
break;
case TRUST_E_EXPLICIT_DISTRUST:
// The hash that represents the subject or the publisher
// is not allowed by the admin or user.
//wprintf_s(L"The signature is present, but specifically "
// L"disallowed.\n");
return true;
case TRUST_E_SUBJECT_NOT_TRUSTED:
// The user clicked "No" when asked to install and run.
//wprintf_s(L"The signature is present, but not "
// L"trusted.\n");
return true;
case CRYPT_E_SECURITY_SETTINGS:
/*
The hash that represents the subject or the publisher
was not explicitly trusted by the admin and the
admin policy has disabled user trust. No signature,
publisher or time stamp errors.
*/
//wprintf_s(L"CRYPT_E_SECURITY_SETTINGS - The hash "
// L"representing the subject or the publisher wasn't "
// L"explicitly trusted by the admin and admin policy "
// L"has disabled user trust. No signature, publisher "
// L"or timestamp errors.\n");
return true;
default:
// The UI was disabled in dwUIChoice or the admin policy
// has disabled user trust. lStatus contains the
// publisher or time stamp chain error.
//wprintf_s(L"Error is: 0x%x.\n",
// lStatus);
return true;
}
return true;
}
#define ENCODING (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING)
typedef struct {
LPWSTR lpszProgramName;
LPWSTR lpszPublisherLink;
LPWSTR lpszMoreInfoLink;
} SPROG_PUBLISHERINFO, *PSPROG_PUBLISHERINFO;
BOOL GetProgAndPublisherInfo(PCMSG_SIGNER_INFO pSignerInfo,
PSPROG_PUBLISHERINFO Info);
BOOL GetDateOfTimeStamp(PCMSG_SIGNER_INFO pSignerInfo, SYSTEMTIME *st);
bool VerifyCertificateInfo(PCCERT_CONTEXT pCertContext);
BOOL GetTimeStampSignerInfo(PCMSG_SIGNER_INFO pSignerInfo,
PCMSG_SIGNER_INFO *pCounterSignerInfo);
bool VerifyCryptSignature(const std::wstring& fileName)
{
WCHAR szFileName[MAX_PATH];
HCERTSTORE hStore = NULL;
HCRYPTMSG hMsg = NULL;
PCCERT_CONTEXT pCertContext = NULL;
BOOL fResult;
DWORD dwEncoding, dwContentType, dwFormatType;
PCMSG_SIGNER_INFO pSignerInfo = NULL;
PCMSG_SIGNER_INFO pCounterSignerInfo = NULL;
DWORD dwSignerInfo;
CERT_INFO CertInfo;
SPROG_PUBLISHERINFO ProgPubInfo;
//SYSTEMTIME st;
bool result = false;
ZeroMemory(&ProgPubInfo, sizeof(ProgPubInfo));
__try
{
lstrcpynW(szFileName, fileName.c_str(), MAX_PATH);
if(!VerifyEmbeddedSignature(szFileName)){
//The signature is not valid
result = false;
__leave;
}
// Get message handle and store handle from the signed file.
fResult = CryptQueryObject(CERT_QUERY_OBJECT_FILE,
szFileName,
CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
CERT_QUERY_FORMAT_FLAG_BINARY,
0,
&dwEncoding,
&dwContentType,
&dwFormatType,
&hStore,
&hMsg,
NULL);
if (!fResult)
{
//_tprintf(_T("CryptQueryObject failed with %x\n"), GetLastError());
result = false;
__leave;
}
// Get signer information size.
fResult = CryptMsgGetParam(hMsg,
CMSG_SIGNER_INFO_PARAM,
0,
NULL,
&dwSignerInfo);
if (!fResult)
{
//_tprintf(_T("CryptMsgGetParam failed with %x\n"), GetLastError());
result = false;
__leave;
}
// Allocate memory for signer information.
pSignerInfo = (PCMSG_SIGNER_INFO)LocalAlloc(LPTR, dwSignerInfo);
if (!pSignerInfo)
{
//_tprintf(_T("Unable to allocate memory for Signer Info.\n"));
result = false;
__leave;
}
// Get Signer Information.
fResult = CryptMsgGetParam(hMsg,
CMSG_SIGNER_INFO_PARAM,
0,
(PVOID)pSignerInfo,
&dwSignerInfo);
if (!fResult)
{
//_tprintf(_T("CryptMsgGetParam failed with %x\n"), GetLastError());
result = false;
__leave;
}
// Get program name and publisher information from
// signer info structure.
if (GetProgAndPublisherInfo(pSignerInfo, &ProgPubInfo))
{
if (StrCmpW(ProgPubInfo.lpszProgramName, L"Roblox Application") != 0)
{
result = false;
__leave;
//wprintf(L"Program Name : %s\n", ProgPubInfo.lpszProgramName);
}
//if (ProgPubInfo.lpszPublisherLink != NULL)
//{
// wprintf(L"Publisher Link : %s\n", ProgPubInfo.lpszPublisherLink);
//}
if (StrCmpW(ProgPubInfo.lpszMoreInfoLink, L"http://www.roblox.com ") != 0)
{
result = false;
__leave;
//wprintf(L"MoreInfo Link : %s\n", ProgPubInfo.lpszMoreInfoLink);
}
}
// Search for the signer certificate in the temporary
// certificate store.
CertInfo.Issuer = pSignerInfo->Issuer;
CertInfo.SerialNumber = pSignerInfo->SerialNumber;
pCertContext = CertFindCertificateInStore(hStore,
ENCODING,
0,
CERT_FIND_SUBJECT_CERT,
(PVOID)&CertInfo,
NULL);
if (!pCertContext)
{
//_tprintf(_T("CertFindCertificateInStore failed with %x\n"), GetLastError());
result = false;
__leave;
}
// Print Signer certificate information.
//_tprintf(_T("Signer Certificate:\n\n"));
if(!VerifyCertificateInfo(pCertContext)){
result = false;
__leave;
}
//_tprintf(_T("\n"));
result = true;
// Get the timestamp certificate signerinfo structure.
/*if (GetTimeStampSignerInfo(pSignerInfo, &pCounterSignerInfo))
{
// Search for Timestamp certificate in the temporary
// certificate store.
CertInfo.Issuer = pCounterSignerInfo->Issuer;
CertInfo.SerialNumber = pCounterSignerInfo->SerialNumber;
pCertContext = CertFindCertificateInStore(hStore,
ENCODING,
0,
CERT_FIND_SUBJECT_CERT,
(PVOID)&CertInfo,
NULL);
if (!pCertContext)
{
_tprintf(_T("CertFindCertificateInStore failed with %x\n"),
GetLastError());
__leave;
}
// Print timestamp certificate information.
_tprintf(_T("TimeStamp Certificate:\n\n"));
PrintCertificateInfo(pCertContext);
_tprintf(_T("\n"));
// Find Date of timestamp.
if (GetDateOfTimeStamp(pCounterSignerInfo, &st))
{
_tprintf(_T("Date of TimeStamp : %02d/%02d/%04d %02d:%02d\n"),
st.wMonth,
st.wDay,
st.wYear,
st.wHour,
st.wMinute);
}
_tprintf(_T("\n"));
}*/
}
__finally
{
// Clean up.
if (ProgPubInfo.lpszProgramName != NULL)
LocalFree(ProgPubInfo.lpszProgramName);
if (ProgPubInfo.lpszPublisherLink != NULL)
LocalFree(ProgPubInfo.lpszPublisherLink);
if (ProgPubInfo.lpszMoreInfoLink != NULL)
LocalFree(ProgPubInfo.lpszMoreInfoLink);
if (pSignerInfo != NULL) LocalFree(pSignerInfo);
if (pCounterSignerInfo != NULL) LocalFree(pCounterSignerInfo);
if (pCertContext != NULL) CertFreeCertificateContext(pCertContext);
if (hStore != NULL) CertCloseStore(hStore, 0);
if (hMsg != NULL) CryptMsgClose(hMsg);
}
return result;
}
/*int _tmain(int argc, TCHAR *argv[])
{
WCHAR szFileName[MAX_PATH];
HCERTSTORE hStore = NULL;
HCRYPTMSG hMsg = NULL;
PCCERT_CONTEXT pCertContext = NULL;
BOOL fResult;
DWORD dwEncoding, dwContentType, dwFormatType;
PCMSG_SIGNER_INFO pSignerInfo = NULL;
PCMSG_SIGNER_INFO pCounterSignerInfo = NULL;
DWORD dwSignerInfo;
CERT_INFO CertInfo;
SPROG_PUBLISHERINFO ProgPubInfo;
SYSTEMTIME st;
ZeroMemory(&ProgPubInfo, sizeof(ProgPubInfo));
__try
{
if (argc != 2)
{
_tprintf(_T("Usage: SignedFileInfo <filename>\n"));
return 0;
}
#ifdef UNICODE
lstrcpynW(szFileName, argv[1], MAX_PATH);
#else
if (mbstowcs(szFileName, argv[1], MAX_PATH) == -1)
{
printf("Unable to convert to unicode.\n");
__leave;
}
#endif
// Get message handle and store handle from the signed file.
fResult = CryptQueryObject(CERT_QUERY_OBJECT_FILE,
szFileName,
CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
CERT_QUERY_FORMAT_FLAG_BINARY,
0,
&dwEncoding,
&dwContentType,
&dwFormatType,
&hStore,
&hMsg,
NULL);
if (!fResult)
{
_tprintf(_T("CryptQueryObject failed with %x\n"), GetLastError());
__leave;
}
// Get signer information size.
fResult = CryptMsgGetParam(hMsg,
CMSG_SIGNER_INFO_PARAM,
0,
NULL,
&dwSignerInfo);
if (!fResult)
{
_tprintf(_T("CryptMsgGetParam failed with %x\n"), GetLastError());
__leave;
}
// Allocate memory for signer information.
pSignerInfo = (PCMSG_SIGNER_INFO)LocalAlloc(LPTR, dwSignerInfo);
if (!pSignerInfo)
{
_tprintf(_T("Unable to allocate memory for Signer Info.\n"));
__leave;
}
// Get Signer Information.
fResult = CryptMsgGetParam(hMsg,
CMSG_SIGNER_INFO_PARAM,
0,
(PVOID)pSignerInfo,
&dwSignerInfo);
if (!fResult)
{
_tprintf(_T("CryptMsgGetParam failed with %x\n"), GetLastError());
__leave;
}
// Get program name and publisher information from
// signer info structure.
if (GetProgAndPublisherInfo(pSignerInfo, &ProgPubInfo))
{
if (ProgPubInfo.lpszProgramName != NULL)
{
wprintf(L"Program Name : %s\n",
ProgPubInfo.lpszProgramName);
}
if (ProgPubInfo.lpszPublisherLink != NULL)
{
wprintf(L"Publisher Link : %s\n",
ProgPubInfo.lpszPublisherLink);
}
if (ProgPubInfo.lpszMoreInfoLink != NULL)
{
wprintf(L"MoreInfo Link : %s\n",
ProgPubInfo.lpszMoreInfoLink);
}
}
_tprintf(_T("\n"));
// Search for the signer certificate in the temporary
// certificate store.
CertInfo.Issuer = pSignerInfo->Issuer;
CertInfo.SerialNumber = pSignerInfo->SerialNumber;
pCertContext = CertFindCertificateInStore(hStore,
ENCODING,
0,
CERT_FIND_SUBJECT_CERT,
(PVOID)&CertInfo,
NULL);
if (!pCertContext)
{
_tprintf(_T("CertFindCertificateInStore failed with %x\n"),
GetLastError());
__leave;
}
// Print Signer certificate information.
_tprintf(_T("Signer Certificate:\n\n"));
PrintCertificateInfo(pCertContext);
_tprintf(_T("\n"));
// Get the timestamp certificate signerinfo structure.
if (GetTimeStampSignerInfo(pSignerInfo, &pCounterSignerInfo))
{
// Search for Timestamp certificate in the temporary
// certificate store.
CertInfo.Issuer = pCounterSignerInfo->Issuer;
CertInfo.SerialNumber = pCounterSignerInfo->SerialNumber;
pCertContext = CertFindCertificateInStore(hStore,
ENCODING,
0,
CERT_FIND_SUBJECT_CERT,
(PVOID)&CertInfo,
NULL);
if (!pCertContext)
{
_tprintf(_T("CertFindCertificateInStore failed with %x\n"),
GetLastError());
__leave;
}
// Print timestamp certificate information.
_tprintf(_T("TimeStamp Certificate:\n\n"));
PrintCertificateInfo(pCertContext);
_tprintf(_T("\n"));
// Find Date of timestamp.
if (GetDateOfTimeStamp(pCounterSignerInfo, &st))
{
_tprintf(_T("Date of TimeStamp : %02d/%02d/%04d %02d:%02d\n"),
st.wMonth,
st.wDay,
st.wYear,
st.wHour,
st.wMinute);
}
_tprintf(_T("\n"));
}
}
__finally
{
// Clean up.
if (ProgPubInfo.lpszProgramName != NULL)
LocalFree(ProgPubInfo.lpszProgramName);
if (ProgPubInfo.lpszPublisherLink != NULL)
LocalFree(ProgPubInfo.lpszPublisherLink);
if (ProgPubInfo.lpszMoreInfoLink != NULL)
LocalFree(ProgPubInfo.lpszMoreInfoLink);
if (pSignerInfo != NULL) LocalFree(pSignerInfo);
if (pCounterSignerInfo != NULL) LocalFree(pCounterSignerInfo);
if (pCertContext != NULL) CertFreeCertificateContext(pCertContext);
if (hStore != NULL) CertCloseStore(hStore, 0);
if (hMsg != NULL) CryptMsgClose(hMsg);
}
return 0;
}*/
bool VerifyCertificateInfo(PCCERT_CONTEXT pCertContext)
{
bool result = true;
LPTSTR szName = NULL;
DWORD dwData;
static std::string issuerNameToCheck; // Symantec Class 3 SHA256 Code Signing CA
issuerNameToCheck = "";
issuerNameToCheck = "Sym";
issuerNameToCheck += "ant";
issuerNameToCheck += "ec ";
issuerNameToCheck += "Cla";
issuerNameToCheck += "ss ";
issuerNameToCheck += "3 S";
issuerNameToCheck += "HA2";
issuerNameToCheck += "56 ";
issuerNameToCheck += "Cod";
issuerNameToCheck += "e S";
issuerNameToCheck += "ign";
issuerNameToCheck += "ing";
issuerNameToCheck += " CA";
static std::string nameToCheck;
nameToCheck += "R";
nameToCheck += "O";
nameToCheck += "B";
nameToCheck += "L";
nameToCheck += "O";
nameToCheck += "X";
nameToCheck += " C";
nameToCheck += "or";
nameToCheck += "por";
nameToCheck += "at";
nameToCheck += "io";
nameToCheck += "n";
// Print Serial Number.
//_tprintf(_T("Serial Number: "));
dwData = pCertContext->pCertInfo->SerialNumber.cbData;
if(dwData != 16){
return false;
}
// signature is taken from current version of RobloxApp file
// (the value displayed in windows is shown here. In memory it is arranged like this)
if( pCertContext->pCertInfo->SerialNumber.pbData[15] != 0x1B ||
pCertContext->pCertInfo->SerialNumber.pbData[14] != 0x81 ||
pCertContext->pCertInfo->SerialNumber.pbData[13] != 0x59 ||
pCertContext->pCertInfo->SerialNumber.pbData[12] != 0xFA ||
pCertContext->pCertInfo->SerialNumber.pbData[11] != 0xF8 ||
pCertContext->pCertInfo->SerialNumber.pbData[10] != 0x22 ||
pCertContext->pCertInfo->SerialNumber.pbData[ 9] != 0x8B ||
pCertContext->pCertInfo->SerialNumber.pbData[ 8] != 0x39 ||
pCertContext->pCertInfo->SerialNumber.pbData[ 7] != 0xAB ||
pCertContext->pCertInfo->SerialNumber.pbData[ 6] != 0xC0 ||
pCertContext->pCertInfo->SerialNumber.pbData[ 5] != 0x0E ||
pCertContext->pCertInfo->SerialNumber.pbData[ 4] != 0x31 ||
pCertContext->pCertInfo->SerialNumber.pbData[ 3] != 0xBB ||
pCertContext->pCertInfo->SerialNumber.pbData[ 2] != 0xAD ||
pCertContext->pCertInfo->SerialNumber.pbData[ 1] != 0x43 ||
pCertContext->pCertInfo->SerialNumber.pbData[ 0] != 0x09){
return false;
}
//for (DWORD n = 0; n < dwData; n++)
//{
// _tprintf(_T("%02x "), pCertContext->pCertInfo->SerialNumber.pbData[dwData - (n + 1)]);
//}
//_tprintf(_T("\n"));
// Get Issuer name size.
if (!(dwData = CertGetNameString(pCertContext,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
CERT_NAME_ISSUER_FLAG,
NULL,
NULL,
0)))
{
//_tprintf(_T("CertGetNameString failed.\n"));
return false;
}
// Allocate memory for Issuer name.
szName = (LPTSTR)LocalAlloc(LPTR, dwData * sizeof(TCHAR));
if (!szName)
{
//_tprintf(_T("Unable to allocate memory for issuer name.\n"));
return false;
}
// Get Issuer name.
if (!(CertGetNameString(pCertContext,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
CERT_NAME_ISSUER_FLAG,
NULL,
szName,
dwData)))
{
//_tprintf(_T("CertGetNameString failed.\n"));
if (szName != NULL) LocalFree(szName);
return false;
}
// print Issuer name.
if(szName != issuerNameToCheck){
if (szName != NULL) LocalFree(szName);
return false;
}
//_tprintf(_T("Issuer Name: %s\n"), szName);
LocalFree(szName);
szName = NULL;
// Get Subject name size.
if (!(dwData = CertGetNameString(pCertContext,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
0,
NULL,
NULL,
0)))
{
//_tprintf(_T("CertGetNameString failed.\n"));
return false;
}
// Allocate memory for subject name.
szName = (LPTSTR)LocalAlloc(LPTR, dwData * sizeof(TCHAR));
if (!szName)
{
//_tprintf(_T("Unable to allocate memory for subject name.\n"));
return false;
}
// Get subject name.
if (!(CertGetNameString(pCertContext,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
0,
NULL,
szName,
dwData)))
{
//_tprintf(_T("CertGetNameString failed.\n"));
if (szName != NULL) LocalFree(szName);
return false;
}
// Print Subject Name.
if(szName != nameToCheck){
if (szName != NULL) LocalFree(szName);
return false;
}
//_tprintf(_T("Subject Name: %s\n"), szName); //"ROBLOX Corporation"
return true;
}
LPWSTR AllocateAndCopyWideString(LPCWSTR inputString)
{
LPWSTR outputString = NULL;
outputString = (LPWSTR)LocalAlloc(LPTR,
(wcslen(inputString) + 1) * sizeof(WCHAR));
if (outputString != NULL)
{
lstrcpyW(outputString, inputString);
}
return outputString;
}
BOOL GetProgAndPublisherInfo(PCMSG_SIGNER_INFO pSignerInfo,
PSPROG_PUBLISHERINFO Info)
{
BOOL fReturn = FALSE;
PSPC_SP_OPUS_INFO OpusInfo = NULL;
DWORD dwData;
BOOL fResult;
__try
{
// Loop through authenticated attributes and find
// SPC_SP_OPUS_INFO_OBJID OID.
for (DWORD n = 0; n < pSignerInfo->AuthAttrs.cAttr; n++)
{
if (lstrcmpA(SPC_SP_OPUS_INFO_OBJID,
pSignerInfo->AuthAttrs.rgAttr[n].pszObjId) == 0)
{
// Get Size of SPC_SP_OPUS_INFO structure.
fResult = CryptDecodeObject(ENCODING,
SPC_SP_OPUS_INFO_OBJID,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData,
0,
NULL,
&dwData);
if (!fResult)
{
_tprintf(_T("CryptDecodeObject failed with %x\n"),
GetLastError());
__leave;
}
// Allocate memory for SPC_SP_OPUS_INFO structure.
OpusInfo = (PSPC_SP_OPUS_INFO)LocalAlloc(LPTR, dwData);
if (!OpusInfo)
{
_tprintf(_T("Unable to allocate memory for Publisher Info.\n"));
__leave;
}
// Decode and get SPC_SP_OPUS_INFO structure.
fResult = CryptDecodeObject(ENCODING,
SPC_SP_OPUS_INFO_OBJID,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData,
0,
OpusInfo,
&dwData);
if (!fResult)
{
_tprintf(_T("CryptDecodeObject failed with %x\n"),
GetLastError());
__leave;
}
// Fill in Program Name if present.
if (OpusInfo->pwszProgramName)
{
Info->lpszProgramName =
AllocateAndCopyWideString(OpusInfo->pwszProgramName);
}
else
Info->lpszProgramName = NULL;
// Fill in Publisher Information if present.
if (OpusInfo->pPublisherInfo)
{
switch (OpusInfo->pPublisherInfo->dwLinkChoice)
{
case SPC_URL_LINK_CHOICE:
Info->lpszPublisherLink =
AllocateAndCopyWideString(OpusInfo->pPublisherInfo->pwszUrl);
break;
case SPC_FILE_LINK_CHOICE:
Info->lpszPublisherLink =
AllocateAndCopyWideString(OpusInfo->pPublisherInfo->pwszFile);
break;
default:
Info->lpszPublisherLink = NULL;
break;
}
}
else
{
Info->lpszPublisherLink = NULL;
}
// Fill in More Info if present.
if (OpusInfo->pMoreInfo)
{
switch (OpusInfo->pMoreInfo->dwLinkChoice)
{
case SPC_URL_LINK_CHOICE:
Info->lpszMoreInfoLink =
AllocateAndCopyWideString(OpusInfo->pMoreInfo->pwszUrl);
break;
case SPC_FILE_LINK_CHOICE:
Info->lpszMoreInfoLink =
AllocateAndCopyWideString(OpusInfo->pMoreInfo->pwszFile);
break;
default:
Info->lpszMoreInfoLink = NULL;
break;
}
}
else
{
Info->lpszMoreInfoLink = NULL;
}
fReturn = TRUE;
break; // Break from for loop.
} // lstrcmp SPC_SP_OPUS_INFO_OBJID
} // for
}
__finally
{
if (OpusInfo != NULL) LocalFree(OpusInfo);
}
return fReturn;
}
BOOL GetDateOfTimeStamp(PCMSG_SIGNER_INFO pSignerInfo, SYSTEMTIME *st)
{
BOOL fResult;
FILETIME lft, ft;
DWORD dwData;
BOOL fReturn = FALSE;
// Loop through authenticated attributes and find
// szOID_RSA_signingTime OID.
for (DWORD n = 0; n < pSignerInfo->AuthAttrs.cAttr; n++)
{
if (lstrcmpA(szOID_RSA_signingTime,
pSignerInfo->AuthAttrs.rgAttr[n].pszObjId) == 0)
{
// Decode and get FILETIME structure.
dwData = sizeof(ft);
fResult = CryptDecodeObject(ENCODING,
szOID_RSA_signingTime,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData,
0,
(PVOID)&ft,
&dwData);
if (!fResult)
{
_tprintf(_T("CryptDecodeObject failed with %x\n"),
GetLastError());
break;
}
// Convert to local time.
FileTimeToLocalFileTime(&ft, &lft);
FileTimeToSystemTime(&lft, st);
fReturn = TRUE;
break; // Break from for loop.
} //lstrcmp szOID_RSA_signingTime
} // for
return fReturn;
}
BOOL GetTimeStampSignerInfo(PCMSG_SIGNER_INFO pSignerInfo, PCMSG_SIGNER_INFO *pCounterSignerInfo)
{
PCCERT_CONTEXT pCertContext = NULL;
BOOL fReturn = FALSE;
BOOL fResult;
DWORD dwSize;
__try
{
*pCounterSignerInfo = NULL;
// Loop through unathenticated attributes for
// szOID_RSA_counterSign OID.
for (DWORD n = 0; n < pSignerInfo->UnauthAttrs.cAttr; n++)
{
if (lstrcmpA(pSignerInfo->UnauthAttrs.rgAttr[n].pszObjId,
szOID_RSA_counterSign) == 0)
{
// Get size of CMSG_SIGNER_INFO structure.
fResult = CryptDecodeObject(ENCODING,
PKCS7_SIGNER_INFO,
pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].pbData,
pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].cbData,
0,
NULL,
&dwSize);
if (!fResult)
{
_tprintf(_T("CryptDecodeObject failed with %x\n"),
GetLastError());
__leave;
}
// Allocate memory for CMSG_SIGNER_INFO.
*pCounterSignerInfo = (PCMSG_SIGNER_INFO)LocalAlloc(LPTR, dwSize);
if (!*pCounterSignerInfo)
{
_tprintf(_T("Unable to allocate memory for timestamp info.\n"));
__leave;
}
// Decode and get CMSG_SIGNER_INFO structure
// for timestamp certificate.
fResult = CryptDecodeObject(ENCODING,
PKCS7_SIGNER_INFO,
pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].pbData,
pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].cbData,
0,
(PVOID)*pCounterSignerInfo,
&dwSize);
if (!fResult)
{
_tprintf(_T("CryptDecodeObject failed with %x\n"),
GetLastError());
__leave;
}
fReturn = TRUE;
break; // Break from for loop.
}
}
}
__finally
{
// Clean up.
if (pCertContext != NULL) CertFreeCertificateContext(pCertContext);
}
return fReturn;
}
+4
View File
@@ -0,0 +1,4 @@
#pragma once
#include <string>
bool VerifyCryptSignature(const std::wstring& fileName);
+223
View File
@@ -0,0 +1,223 @@
#include "stdafx.h"
#include "Document.h"
#include "FunctionMarshaller.h"
#include "GameVerbs.h"
#include "LogManager.h"
#include "resource.h"
#include "script/ScriptContext.h"
#include "util/Statistics.h"
#include "util/RobloxGoogleAnalytics.h"
#include "v8datamodel/ContentProvider.h"
#include "v8datamodel/DataModel.h"
#include "v8datamodel/DebugSettings.h"
#include "v8datamodel/Game.h"
#include "V8DataModel/GuiService.h"
#include "V8DataModel/HackDefines.h"
#include "V8DataModel/UserInputService.h"
#include "V8DataModel/UserController.h"
#include "Network/Api.h"
#include "InitializationError.h"
#include "View.h"
#include "RbxWebView.h"
#include "VMProtectSDK.h"
LOGGROUP(PlayerShutdownLuaTimeoutSeconds)
namespace RBX {
Document::Document() : marshaller(NULL)
{
}
Document::~Document()
{
}
void Document::Start(HttpFuture& scriptResult, const SharedLauncher::LaunchMode launchMode, bool isTeleport, const char* vrDevice)
{
startedSignal(isTeleport);
LogManager::ReportEvent(EVENTLOG_INFORMATION_TYPE, "Starting script");
SetUiMessage(""); // clear UI message
// TODO: VMProtect this section
executeScript(scriptResult, launchMode, vrDevice);
if (!isTeleport)
RobloxGoogleAnalytics::trackUserTiming(GA_CATEGORY_GAME, GA_CLIENT_START, Time::nowFast().timestampSeconds() * 1000, "Join script executed");
}
static void setUiMessageImpl(shared_ptr<DataModel> dm, const std::string& message)
{
if (message.length() > 0)
{
dm->setUiMessage(message);
}
else
{
dm->clearUiMessage();
}
if (GuiService*gs = dm->create<GuiService>())
gs->setUiMessage(GuiService::UIMESSAGE_INFO, message);
}
void Document::SetUiMessage(const std::string& message)
{
if (shared_ptr<DataModel> dm = game->getDataModel())
{
dm->submitTask(boost::bind(setUiMessageImpl, dm, message), DataModelJob::Write);
}
}
void Document::PrepareShutdown()
{
// give scripts a deadline to finish
if (game && game->getDataModel())
if (FLog::PlayerShutdownLuaTimeoutSeconds > 0)
if (ScriptContext* scriptContext = game->getDataModel()->find<ScriptContext>())
scriptContext->setTimeout(FLog::PlayerShutdownLuaTimeoutSeconds);
}
void Document::Shutdown()
{
if (marshaller)
FunctionMarshaller::ReleaseWindow(marshaller);
if (game)
{
game->shutdown();
game.reset();
}
}
void Document::configureDataModelServices(bool useChat, RBX::DataModel* dataModel)
{
if(!dataModel)
return;
DataModel::LegacyLock lock(dataModel, DataModelJob::Write);
// Inform the UserInputService what kind of input we are providing (this may have to change if we use this with windows 8 touch devices)
if(UserInputService* userInputService = dataModel->find<UserInputService>())
{
userInputService->setKeyboardEnabled(true);
userInputService->setMouseEnabled(true);
}
}
void Document::Initialize(HWND hWnd, bool useChat)
{
marshaller = FunctionMarshaller::GetWindow();
game.reset(new RBX::SecurePlayerGame(NULL, GetBaseURL().c_str()));
configureDataModelServices(useChat, game->getDataModel().get());
DataModel::LegacyLock lock(game->getDataModel().get(), DataModelJob::Write);
if (FLog::PlayerShutdownLuaTimeoutSeconds > 0)
game->getDataModel()->create<ScriptContext>();
game->getDataModel().get()->gameLoadedSignal.connect(boost::bind(&Document::gameIsLoaded,this));
}
void Document::gameIsLoaded()
{
MainLogManager::getMainLogManager()->setGameLoaded();
}
// Executes the 'script' as part of the game initialization.
void Document::executeScript(HttpFuture& scriptResult, const SharedLauncher::LaunchMode launchMode, const char* vrDevice) const
{
shared_ptr<RBX::DataModel> dataModel = game->getDataModel();
#if !defined(LOVE_ALL_ACCESS) && !defined(_NOOPT) && !defined(_DEBUG) && !defined(RBX_STUDIO_BUILD)
dataModel->addHackFlag(HATE_DEBUGGER *
VMProtectIsDebuggerPresent(true /*check for kernel debuggers too*/));
#endif
Security::Impersonator impersonate(Security::COM);
std::string data;
try
{
data = scriptResult.get();
}
catch(const std::exception& e)
{
std::string err = RBX::format("Exception occured in Document::executeScript: %s", e.what());
LogManager::ReportEvent(EVENTLOG_ERROR_TYPE, err.c_str());
if (GuiService*gs = dataModel->create<GuiService>())
gs->setUiMessage(GuiService::UIMESSAGE_INFO, "Unable to join game. Please try again later.");
return;
}
#if !defined(LOVE_ALL_ACCESS) && !defined(_NOOPT) && !defined(_DEBUG) && !defined(RBX_STUDIO_BUILD)
dataModel->addHackFlag(HATE_DEBUGGER *
VMProtectIsDebuggerPresent(true /*check for kernel debuggers too*/));
#endif
ProtectedString verifiedSource;
try
{
verifiedSource = ProtectedString::fromTrustedSource(data);
ContentProvider::verifyScriptSignature(verifiedSource, true);
}
catch(std::bad_alloc& e)
{
std::string err = RBX::format("Exception occured in Document::executeScript: %s", e.what());
LogManager::ReportEvent(EVENTLOG_ERROR_TYPE, err.c_str());
throw;
}
catch(std::exception& e)
{
std::string err = RBX::format("Exception occured in Document::executeScript: %s", e.what());
LogManager::ReportEvent(EVENTLOG_ERROR_TYPE, err.c_str());
return;
}
if (dataModel->isClosed())
return;
int firstNewLineIndex = data.find("\r\n");
if (data[firstNewLineIndex+2] == '{')
{
game->configurePlayer(Security::COM, data.substr(firstNewLineIndex+2), launchMode, vrDevice);
}
else
{
ScriptContext* context = dataModel->create<ScriptContext>();
context->executeInNewThread(Security::COM, verifiedSource, "Start Game");
}
}
FunctionMarshaller* Document::GetMarshaller() const
{
return marshaller;
}
std::string Document::GetSEOStr() const
{
boost::shared_ptr<DataModel> dataModel = game->getDataModel();
std::string seo;
if (dataModel)
{
std::string seo = dataModel->getScreenshotSEOInfo();
if (!seo.empty())
return seo;
}
const int MAX_LOAD_STRING = 100;
char defaultImageInfo[MAX_LOAD_STRING];
LoadStringA(GetModuleHandle(NULL), IDS_DEFAULT_IMAGE_INFO, defaultImageInfo, MAX_LOAD_STRING);
return std::string(defaultImageInfo);
}
} // namespace RBX
+54
View File
@@ -0,0 +1,54 @@
#pragma once
#include "rbx/signal.h"
#include "SharedLauncher.h"
#include "RbxWebView.h"
#include "util/HttpAsync.h"
namespace RBX {
// Forward declarations
class FunctionMarshaller;
class Game;
class View;
class PlayerConfigurer;
// Class responsible for the game state
class Document
{
public:
rbx::signal<void(bool)> startedSignal;
Document();
~Document();
void Initialize(HWND hWnd, bool useChat);
void Start(HttpFuture& scriptResult, const SharedLauncher::LaunchMode launchMode, bool isTelport, const char* vrDevice);
void Shutdown();
void SetUiMessage(const std::string& message);
void PrepareShutdown(); // call before destroying the view
FunctionMarshaller* GetMarshaller() const;
std::string GetSEOStr() const;
boost::shared_ptr<Game> getGame()
{
return game;
}
private:
FunctionMarshaller* marshaller;
// The game to run.
boost::shared_ptr<Game> game;
// Executes the 'script' as part of the game initialization.
void executeScript(HttpFuture& scriptResult, const SharedLauncher::LaunchMode launchMode, const char* vrDevice) const;
void configureDataModelServices(bool useChat, RBX::DataModel* dataModel);
void dataModelDidRestart();
void dataModelWillShutdown();
void gameIsLoaded();
};
} // namespace RBX
+172
View File
@@ -0,0 +1,172 @@
#include "stdafx.h"
#include "FunctionMarshaller.h"
#undef min
#undef max
#include "util/StandardOut.h"
#include "rbx/boost.hpp"
namespace RBX {
FunctionMarshaller::FunctionMarshaller(DWORD threadID)
: refCount(0)
, postedAsyncMessage(0)
{
this->threadID = threadID;
HWND hWnd = this->Create(NULL, 0, 0, WS_POPUP);
ATLASSERT(hWnd!=NULL);
}
FunctionMarshaller::~FunctionMarshaller()
{
boost::function<void()>* f;
while (asyncCalls.pop_if_present(f))
delete f;
ATLASSERT(threadID == GetCurrentThreadId());
#ifdef _DEBUG
{
boost::recursive_mutex::scoped_lock lock(
staticData().windowsCriticalSection);
ATLASSERT (refCount==0);
// Nobody is using this window
ATLASSERT (staticData().windows.find(threadID) == \
staticData().windows.end());
}
#endif
}
FunctionMarshaller* FunctionMarshaller::GetWindow()
{
// Share a common FunctionMarshaller in a given Thread
boost::recursive_mutex::scoped_lock lock(
staticData().windowsCriticalSection);
DWORD threadID = GetCurrentThreadId();
std::map<DWORD, FunctionMarshaller*>::iterator find =
staticData().windows.find(threadID);
if (find != staticData().windows.end())
{
// We already created a window, so use it again
find->second->refCount++;
return find->second;
}
else
{
// Create a new window
FunctionMarshaller* window = new FunctionMarshaller(threadID);
staticData().windows[threadID] = window;
window->refCount++;
return window;
}
}
void FunctionMarshaller::ReleaseWindow(FunctionMarshaller* window)
{
boost::recursive_mutex::scoped_lock lock(
staticData().windowsCriticalSection);
window->refCount--;
if (window->refCount==0)
{
// Nobody is using this window
staticData().windows.erase(window->threadID);
window->DestroyWindow();
}
}
LRESULT FunctionMarshaller::OnAsyncEvent(UINT uMsg, WPARAM wParam,
LPARAM lParam, BOOL& bHandled)
{
bHandled = TRUE;
postedAsyncMessage.swap(0);
boost::function<void()>* f;
while (asyncCalls.pop_if_present(f))
{
try
{
(*f)();
}
catch (std::exception& e)
{
delete f;
StandardOut::singleton()->print(RBX::MESSAGE_ERROR, e);
throw;
}
delete f;
}
return S_OK;
}
LRESULT FunctionMarshaller::OnEvent(UINT uMsg, WPARAM wParam, LPARAM lParam,
BOOL& bHandled)
{
bHandled = TRUE;
Closure* closure = (Closure*)lParam;
try
{
(*closure->f)();
}
catch (std::exception& e)
{
StandardOut::singleton()->print(RBX::MESSAGE_ERROR, e);
throw;
}
return S_OK;
}
void FunctionMarshaller::Execute(boost::function<void()> job)
{
if (threadID == GetCurrentThreadId())
job();
else
{
Closure closure;
closure.f = &job;
if (S_OK != SendMessage(WM_EVENT, 0, (LPARAM)&closure))
throw std::runtime_error(closure.errorMessage);
}
}
void FunctionMarshaller::Submit(boost::function<void()> job)
{
boost::function<void()>* f = new boost::function<void()>(job);
asyncCalls.push(f);
if (postedAsyncMessage.swap(1) == 0)
PostMessage(WM_ASYNCEVENT, 0, 0);
}
void FunctionMarshaller::ProcessMessages()
{
MSG stMsg = { 0 };
while(::PeekMessage(&stMsg, this->m_hWnd, WM_ASYNCEVENT, WM_ASYNCEVENT,
PM_REMOVE))
{
TranslateMessage(&stMsg);
DispatchMessage(&stMsg);
}
}
void FunctionMarshaller::OnFinalMessage(HWND hWnd)
{
delete this;
}
FunctionMarshaller::StaticData::~StaticData()
{
for (std::map<DWORD, FunctionMarshaller*>::iterator iter =
windows.begin(); iter != windows.end(); ++iter)
{
iter->second->DestroyWindow();
}
}
} // namespace RBX
+80
View File
@@ -0,0 +1,80 @@
#pragma once
// TODO: refactor this class so it doesn't use ATL
#ifndef _WIN32
// This code is platform-specific
#error
#endif
#include "atlbase.h"
#include "AtlWin.h"
#include <map>
#include "rbx/threadsafe.h"
#include "rbx/atomic.h"
namespace RBX {
// A very handy class for marshaling a function across Windows
// threads (sync and async)
class FunctionMarshaller
: public ATL::CWindowImpl<FunctionMarshaller>
{
const static int WM_EVENT = WM_USER + 101; // See Q196026
const static int WM_ASYNCEVENT = WM_USER + 102; // See Q196026
LRESULT OnEvent(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
LRESULT OnAsyncEvent(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
struct StaticData
{
std::map<DWORD, FunctionMarshaller*> windows;
// TODO: Would non-recursive be safe here?
boost::recursive_mutex windowsCriticalSection;
~StaticData();
};
SAFE_STATIC(StaticData, staticData)
rbx::safe_queue<boost::function<void()>*> asyncCalls;
rbx::atomic<LONG> postedAsyncMessage;
int refCount;
DWORD threadID;
FunctionMarshaller(DWORD threadID);
~FunctionMarshaller();
public:
// see Q196026
DECLARE_WND_CLASS("Roblox.FunctionMarshaller")
// TODO: Wrap with a reference counter and then remove ~StaticData()
// cleanup code and remove ReleaseWindow()
static FunctionMarshaller* GetWindow();
static void ReleaseWindow(FunctionMarshaller* window);
struct Closure
{
boost::function<void()>* f;
std::string errorMessage;
};
// Executes the given function.
void Execute(boost::function<void()> job);
// Submits a function to be executed by a separate thread.
void Submit(boost::function<void()> job);
// Call this only from the Window's thread.
void ProcessMessages();
BEGIN_MSG_MAP(MarshaledListener)
MESSAGE_HANDLER(WM_EVENT, OnEvent)
MESSAGE_HANDLER(WM_ASYNCEVENT, OnAsyncEvent)
END_MSG_MAP()
virtual void OnFinalMessage(HWND hWnd);
};
} // namespace RBX
+332
View File
@@ -0,0 +1,332 @@
#include "stdafx.h"
#include "GameVerbs.h"
#include "Document.h"
#include "DSVideoCaptureEngine.h"
#include "FunctionMarshaller.h"
#include "VideoControl.h"
#include "DSVideoCaptureEngine.h"
#include "RenderSettingsItem.h"
#include "DSVideoCaptureEngine.h"
#include "Resource.h"
#include "util/FileSystem.h"
#include "util/Http.h"
#include "util/Statistics.h"
#include "v8datamodel/Game.h"
#include "v8datamodel/GameBasicSettings.h"
#include "VideoControl.h"
#include "View.h"
#include "Application.h"
#include "LogManager.h"
#include "WebBrowserAxDialog.h"
namespace RBX {
LeaveGameVerb::LeaveGameVerb(View& view, VerbContainer* container)
: Verb(container, "Exit")
, v(view)
{
}
void LeaveGameVerb::doIt(IDataState* dataState)
{
MainLogManager::getMainLogManager()->setLeaveGame();
v.CloseWindow();
}
ScreenshotVerb::ScreenshotVerb(const Document& doc,
ViewBase* view,
Game* game)
: Verb(game->getDataModel().get(), "Screenshot")
, doc(doc)
, game(game)
, view(view)
{
boost::shared_ptr<DataModel> dataModel = game->getDataModel();
dataModel->screenshotReadySignal.connect(
boost::bind(&ScreenshotVerb::screenshotFinished, this, _1));
}
void ScreenshotVerb::doIt(IDataState* dataState)
{
boost::shared_ptr<DataModel> dataModel = game->getDataModel();
if (!dataModel)
return;
dataModel->submitTask(boost::bind(&DataModel::TakeScreenshotTask,
boost::weak_ptr<DataModel>(dataModel)), DataModelJob::Write);
}
// TODO: Why Facebook?
// TODO: Make non-static
static void PostImageFinished(std::string *response, std::exception *ex, weak_ptr<RBX::DataModel> weakDataModel)
{
if(shared_ptr<RBX::DataModel> dataModel = weakDataModel.lock())
{
if ((ex == NULL) && (response->compare("ok") == 0))
dataModel->submitTask(boost::bind(&RBX::DataModel::ShowMessage, weak_ptr<RBX::DataModel>(dataModel), 1, "Image uploaded to Facebook", 2), RBX::DataModelJob::Write);
else
{
dataModel->submitTask(boost::bind(&RBX::DataModel::ShowMessage, weak_ptr<RBX::DataModel>(dataModel), 1, "Failed to upload image", 2), RBX::DataModelJob::Write);
RBX::GameSettings::singleton().setPostImageSetting(RBX::GameSettings::ASK);
}
dataModel->submitTask(boost::bind(&RBX::DataModel::ScreenshotUploadTask, weak_ptr<RBX::DataModel>(dataModel), true), RBX::DataModelJob::Write);
}
}
void ScreenshotVerb::screenshotFinished(const std::string &filename)
{
view->getAndClearDoScreenshot();
boost::shared_ptr<DataModel> dataModel = game->getDataModel();
dataModel->submitTask(boost::bind(&DataModel::ShowMessage,
weak_ptr<DataModel>(dataModel), 0, "Screenshot saved", 2.0),
DataModelJob::Write);
switch (RBX::GameSettings::singleton().getPostImageSetting()) {
case RBX::GameSettings::ASK:
{
// TODO We should ASK user here from UI thread.
// image upload is disabled anyway on web site, so it looks OK just to file bug to fix this later
// deffect id - DE3515
doc.GetMarshaller()->Submit(boost::bind(&ScreenshotVerb::askUploadScreenshot, this, filename));
break;
}
case RBX::GameSettings::ALWAYS:
{
uploadScreenshot(filename);
break;
}
case RBX::GameSettings::NEVER:
break;
}
}
void ScreenshotVerb::askUploadScreenshot(std::string filename)
{
// Random parameter to force refresh
char n[16];
itoa(rand(), n, 10);
std::string pictureDir = RBX::FileSystem::getUserDirectory(true, RBX::DirPicture).string();
std::string url = RBX::format("%s/UploadMedia/PostImage.aspx?seostr=%s&filename=%s&screenshotdir=%s&from=client&rand=",
GetBaseURL().c_str(),
doc.GetSEOStr().c_str(),
filename.c_str(),
pictureDir.c_str());
url += n;
WebBrowserAxDialog dlg(url, game->getDataModel());
dlg.DoModal();
}
void ScreenshotVerb::uploadScreenshot(const std::string& filename)
{
boost::shared_ptr<DataModel> dataModel = game->getDataModel();
if (!dataModel)
return;
try
{
std::string seostr = doc.GetSEOStr();
std::string url = RBX::format("%s/UploadMedia/DoPostImage.ashx?from=client", GetBaseURL().c_str());
RBX::Http http(url);
// in case the seo info contains nothing but whitespaces, add a line break to prevent facebook from returning errors
http.additionalHeaders[seostr] = seostr + "%0D%0A";
shared_ptr<std::ifstream> in(new std::ifstream);
in->open(filename.c_str(), std::ios::binary);
if (in->fail())
{
dataModel->submitTask(boost::bind(&RBX::DataModel::ShowMessage, weak_ptr<RBX::DataModel>(dataModel), 1, "Failed to upload image", 2), RBX::DataModelJob::Write);
}
else
{
dataModel->submitTask(boost::bind(&RBX::DataModel::ShowMessage, weak_ptr<RBX::DataModel>(dataModel), 1, "Uploading image ...", 0), RBX::DataModelJob::Write);
dataModel->submitTask(boost::bind(&RBX::DataModel::ScreenshotUploadTask, weak_ptr<RBX::DataModel>(dataModel), false), RBX::DataModelJob::Write);
http.post(in, Http::kContentTypeDefaultUnspecified, false,
boost::bind(&PostImageFinished, _1, _2, weak_ptr<RBX::DataModel>(dataModel)));
}
}
catch (...)
{
dataModel->submitTask(boost::bind(&RBX::DataModel::ShowMessage, weak_ptr<RBX::DataModel>(dataModel), 1, "Failed to upload image", 2), RBX::DataModelJob::Write);
dataModel->submitTask(boost::bind(&RBX::DataModel::ScreenshotUploadTask, weak_ptr<RBX::DataModel>(dataModel), true), RBX::DataModelJob::Write);
}
}
void RecordToggleVerb::action()
{
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
while(!stop)
{
jobWait.Wait();
if (!stop)
{
{
DataModel::scoped_write_transfer request(game->getDataModel().get());
job();
}
jobDone.Set();
}
}
CoUninitialize();
threadDone.Set();
}
RecordToggleVerb::RecordToggleVerb(const Document& doc,
View* view,
Game* game)
: Verb(game->getDataModel().get(), "RecordToggle")
, doc(doc)
, view(view)
, game(game)
, stop(false)
, jobWait(false)
, jobDone(false)
, threadDone(false)
, videoUploadingEnabled(true)
{
helper.reset(new boost::thread(boost::bind(&RecordToggleVerb::action,
this)));
DSVideoCaptureEngine* videoCaptureEngine = new DSVideoCaptureEngine();
VideoControl* videoControlPtr = new VideoControl(videoCaptureEngine,
view->GetGfxView(), view->GetGfxView()->getFrameRateManager(), this);
videoControl.reset(videoControlPtr);
}
RecordToggleVerb::~RecordToggleVerb()
{
if( isSelected() )
abortCapture();
stop = true;
jobWait.Set();
threadDone.Wait();
}
bool RecordToggleVerb::isEnabled() const
{
return GameSettings::singleton().videoCaptureEnabled
&& isUploadingVideo();
}
bool RecordToggleVerb::isChecked() const
{
return videoControl->isVideoRecording();
}
bool RecordToggleVerb::isSelected() const
{
return videoControl->isVideoRecording();
}
void RecordToggleVerb::startAction()
{
Soundscape::SoundService* soundService =
ServiceProvider::create<Soundscape::SoundService>(
game->getDataModel().get());
videoControl->startRecording(soundService);
fileName = videoControl->getFileName();
RBX::GameSettings::singleton().videoRecordingSignal(true);
}
void RecordToggleVerb::stopAction()
{
videoControl->stopRecording();
GameBasicSettings& settings = GameBasicSettings::singleton();
RBX::GameSettings::singleton().videoRecordingSignal(false);
if (settings.getUploadVideoSetting() == GameSettings::NEVER)
return;
doc.GetMarshaller()->Submit(boost::bind(&RecordToggleVerb::uploadVideo, this));
}
void RecordToggleVerb::abortCapture()
{
if( videoControl->isVideoRecording())
{
videoControl->stopRecording();
}
}
void RecordToggleVerb::uploadVideo()
{
std::string videoDir = RBX::FileSystem::getUserDirectory(true, RBX::DirVideo).string();
std::string url = RBX::format("%s/UploadMedia/UploadVideo.aspx?from=client&videodir=%s&rand=",
GetBaseURL().c_str(),
videoDir.c_str());
// Random parameter to force refresh
char n[16];
itoa(rand(), n, 10);
url += n;
WebBrowserAxDialog browser(url, game->getDataModel(), boost::bind(&RecordToggleVerb::EnableVideUpload, this, _1));
browser.SetFileName(fileName);
browser.DoModal(view->GetHWnd());
}
void RecordToggleVerb::doIt(IDataState* dataState)
{
OutputDebugString("RecordToggleVerb::doIt");
if (videoControl->isVideoRecording())
{
job = boost::bind(&RecordToggleVerb::stopAction, this);
jobWait.Set();
jobDone.Wait();
return;
}
job = boost::bind(&RecordToggleVerb::startAction, this);
jobWait.Set();
jobDone.Wait();
}
VideoControl* RecordToggleVerb::GetVideoControl()
{
return videoControl.get();
}
ToggleFullscreenVerb::ToggleFullscreenVerb(View& view, VerbContainer* container, VideoControl* videoControl)
: Verb(container, "ToggleFullScreen")
, videoControl(videoControl)
, view(view)
{}
bool ToggleFullscreenVerb::isChecked() const
{
return view.IsFullscreen();
}
bool ToggleFullscreenVerb::isEnabled() const
{
return true;
}
void ToggleFullscreenVerb::doIt(RBX::IDataState* dataState)
{
FASTLOG(FLog::Verbs, "Gui:ToggleFullscreen");
view.SetFullscreen(!view.IsFullscreen());
}
} // namespace RBX
+110
View File
@@ -0,0 +1,110 @@
#pragma once
#include "v8tree/Verb.h"
#include "rbx/CEvent.h"
namespace RBX {
class Game;
class View;
class ViewBase;
class Document;
class VideoControl;
class WebBrowserDialog;
class Application;
// Request to leave the game. Results in process shutdown.
class LeaveGameVerb : public Verb
{
View& v;
public:
LeaveGameVerb(View& v, VerbContainer* container);
virtual ~LeaveGameVerb(){}
virtual void doIt(IDataState* dataState);
};
// Request to take screenshot of current game
class ScreenshotVerb : public RBX::Verb
{
Game* game;
ViewBase* view;
const Document& doc;
void screenshotFinished(const std::string &filename);
void askUploadScreenshot(std::string filename);
void uploadScreenshot(const std::string &filename);
public:
ScreenshotVerb(const Document& doc, ViewBase* view, Game* game);
virtual void doIt(IDataState* dataState);
virtual bool isEnabled() const { return true; }
};
// Request to record gameplay video
class RecordToggleVerb : public Verb
{
const Document& doc;
View* view;
Game* game;
boost::scoped_ptr<VideoControl> videoControl;
// Path to where the file video is being saved.
std::string fileName;
bool videoUploadingEnabled;
bool stop;
boost::scoped_ptr<boost::thread> helper;
boost::function<void()> job;
CEvent jobWait;
CEvent jobDone;
CEvent threadDone;
void action();
// Indicates whether the a video is being uploaded.
bool isUploadingVideo() const { return videoUploadingEnabled; }
void EnableVideUpload(bool enable) { videoUploadingEnabled = enable; }
// Uploads a video to the interwebs.
void uploadVideo();
// stops the recording, does not prompt or signal
void abortCapture();
public:
RecordToggleVerb(const Document& doc, View* view, Game* game);
~RecordToggleVerb();
virtual bool isEnabled() const;
virtual bool isChecked() const;
virtual bool isSelected() const;
void startAction();
void stopAction();
virtual void doIt(IDataState* dataState);
VideoControl* GetVideoControl();
};
// Request to toggle fullscreen
class ToggleFullscreenVerb : public RBX::Verb
{
private:
// Needed because toggle fullscreen is disabled while recording
VideoControl* videoControl;
View& view;
public:
ToggleFullscreenVerb(View& view, VerbContainer* container, VideoControl* videoControl);
virtual bool isChecked() const;
virtual bool isEnabled() const;
virtual void doIt(RBX::IDataState* dataState);
};
} // namespace RBX
+13
View File
@@ -0,0 +1,13 @@
#pragma once
#include <stdexcept>
namespace RBX {
class initialization_error : public std::runtime_error {
public:
initialization_error(const char* const errorMessage) :
std::runtime_error(errorMessage) {}
};
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 326 B

+25
View File
@@ -0,0 +1,25 @@
// We moved to non-ASLR at some point to get consistent hashes.
// Exploiters quickly moved to hardcoding fixed addresses each week.
// This is only here to move the code around a little bit each week.
#include "stdafx.h"
#include "Security/RandomConstant.h"
#include "Security/JunkCode.h"
template <int N> __forceinline void useless()
{
junk<(N*(RBX_BUILDSEED%16) + __LINE__*N*(RBX_BUILDSEED%15) + N*N) % 17>();
useless<N-1>();
useless<N-1>();
}
template<> __forceinline void useless<0>() {}
// VS2012 really doesn't get why a function that is unused should exist.
extern "C"
{
#pragma comment (linker, "/export:_unusedPadding")
void unusedPadding()
{
useless<9>();
}
};
+345
View File
@@ -0,0 +1,345 @@
#include "stdafx.h"
#include "RbxWebView.h"
#include <Exdispid.h> // platform SDK header
#include "util/FileSystem.h"
#include "util/Statistics.h"
#include "util/Http.h"
#include "format_string.h"
#include <atltypes.h>
#include "v8datamodel/GuiService.h"
#include "v8datamodel/DataModel.h"
FASTSTRING(ClientExternalBrowserUserAgent)
RbxWebView::RbxWebView(const std::string& url, shared_ptr<RBX::Game> newGame)
: CAxDialogImpl()
, url(url)
, m_cRef(1)
, game(newGame)
, dialogActive(false)
{
}
HRESULT RbxWebView::QueryInterface(REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject)
{
*ppvObject = 0; // this line pleases Raymond Chen
if (IID_IUnknown == riid)
{
*ppvObject = (LPUNKNOWN)(IDispatch*)this;
AddRef();
return S_OK;
}
else if (IID_IDispatch == riid)
{
*ppvObject = (IDispatch*)this;
AddRef();
return S_OK;
}
else
{
return E_NOINTERFACE;
}
}
ULONG RbxWebView::AddRef(void)
{
return InterlockedIncrement(&m_cRef);
}
ULONG RbxWebView::Release(void)
{
return InterlockedDecrement(&m_cRef);
}
HRESULT RbxWebView::ShowContextMenu(DWORD dwID, POINT *ppt, IUnknown *pcmdtReserved, IDispatch *pdispReserved)
{
return E_NOTIMPL;
}
#define ROBLOX_BROWSERFLAGS \
DOCHOSTUIFLAG_DISABLE_HELP_MENU |\
DOCHOSTUIFLAG_ENABLE_FORMS_AUTOCOMPLETE |\
DOCHOSTUIFLAG_THEME |\
DOCHOSTUIFLAG_DISABLE_SCRIPT_INACTIVE |\
DOCHOSTUIFLAG_LOCAL_MACHINE_ACCESS_CHECK |\
DOCHOSTUIFLAG_DISABLE_UNTRUSTEDPROTOCOL |\
0
HRESULT RbxWebView::GetHostInfo(DOCHOSTUIINFO *pInfo)
{
pInfo->dwFlags |=
DOCHOSTUIFLAG_NO3DBORDER |
ROBLOX_BROWSERFLAGS |
0;
return S_OK;
}
HRESULT RbxWebView::ShowUI(DWORD dwID, IOleInPlaceActiveObject *pActiveObject, IOleCommandTarget *pCommandTarget, IOleInPlaceFrame *pFrame, IOleInPlaceUIWindow *pDoc)
{
return E_NOTIMPL;
}
HRESULT RbxWebView::HideUI(void)
{
return S_OK;
}
HRESULT RbxWebView::UpdateUI(void)
{
return E_NOTIMPL;
}
HRESULT RbxWebView::EnableModeless(BOOL fEnable)
{
return E_NOTIMPL;
}
HRESULT RbxWebView::OnDocWindowActivate(BOOL fActivate)
{
return E_NOTIMPL;
}
HRESULT RbxWebView::OnFrameWindowActivate(BOOL fActivate)
{
return E_NOTIMPL;
}
HRESULT RbxWebView::ResizeBorder(LPCRECT prcBorder, IOleInPlaceUIWindow *pUIWindow, BOOL fRameWindow)
{
return E_NOTIMPL;
}
HRESULT RbxWebView::TranslateAccelerator(LPMSG lpMsg, const GUID *pguidCmdGroup, DWORD nCmdID)
{
return E_NOTIMPL;
}
HRESULT RbxWebView::GetOptionKeyPath(LPOLESTR *pchKey, DWORD dw)
{
return E_NOTIMPL;
}
HRESULT RbxWebView::GetDropTarget(IDropTarget *pDropTarget, IDropTarget **ppDropTarget)
{
return E_NOTIMPL;
}
HRESULT RbxWebView::GetExternal(IDispatch **ppDispatch)
{
*ppDispatch = this;
return S_OK;
}
HRESULT RbxWebView::TranslateUrl(DWORD dwTranslate, OLECHAR *pchURLIn, OLECHAR **ppchURLOut)
{
return E_NOTIMPL;
}
HRESULT RbxWebView::FilterDataObject(IDataObject *pDO, IDataObject **ppDORet)
{
return E_NOTIMPL;
}
HRESULT RbxWebView::GetTypeInfoCount(UINT* pctinfo)
{
if (pctinfo == NULL)
return E_POINTER;
*pctinfo = 1;
return S_OK;
}
HRESULT RbxWebView::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
{
return S_OK;
}
HRESULT RbxWebView::GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId)
{
return S_OK;
}
HRESULT RbxWebView::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS __RPC_FAR *pDispParams, VARIANT __RPC_FAR *pVarResult, EXCEPINFO __RPC_FAR *pExcepInfo, UINT __RPC_FAR *puArgErr)
{
return S_OK;
}
SHDocVw::IWebBrowserAppPtr RbxWebView::getWebBrowser()
{
SHDocVw::IWebBrowserAppPtr pWebBrowser = NULL;
GetDlgControl(IDC_RBXEXPLORER, __uuidof(SHDocVw::IWebBrowserAppPtr), (void**)&pWebBrowser);
return pWebBrowser;
}
LRESULT RbxWebView::OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
CenterWindow();
webBrowserEvents.SetRbxWebView(this);
// Load the Roblox Icon
m_hIcon = ::LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_WINDOW_ICON));
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
SHDocVw::IWebBrowserAppPtr pWebBrowser = NULL;
HRESULT hr = GetDlgControl(IDC_RBXEXPLORER, __uuidof(SHDocVw::IWebBrowserAppPtr), (void**)&pWebBrowser);
if (SUCCEEDED(hr))
{
// setup hooks into browser events
CComQIPtr<IConnectionPointContainer, &IID_IConnectionPointContainer> cpc(pWebBrowser);
CComPtr<IConnectionPoint> cp1;
hr = cpc->FindConnectionPoint(__uuidof(SHDocVw::DWebBrowserEventsPtr), &cp1);
DWORD dwCookie;
hr = cp1->Advise((LPUNKNOWN)&webBrowserEvents, &dwCookie);
CComPtr<IConnectionPoint> cp2;
hr = cpc->FindConnectionPoint(__uuidof(SHDocVw::DWebBrowserEvents2Ptr), &cp2);
hr = cp2->Advise((LPUNKNOWN)&webBrowserEvents, &dwCookie);
// now set the header before the request
std::string userAgent = "Mozilla/4.0 (compatible; MSIE 7.0) ";
userAgent.append(FString::ClientExternalBrowserUserAgent);
std::vector<char> userAgentChars(userAgent.c_str(), userAgent.c_str() + userAgent.size() + 1u);
::UrlMkSetSessionOption(URLMON_OPTION_USERAGENT, &userAgentChars[0], userAgent.length(), 0);
// navigate to default page
pWebBrowser->Navigate(_bstr_t(url.c_str()),NULL,NULL,NULL,NULL);
}
else
{
MessageBox("Failed to open web browser", "Error", MB_OK);
return FALSE;
}
dialogActive = true;
return TRUE; // let the system set the focus
}
LRESULT RbxWebView::OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
if(dialogActive)
webBrowserEvents.WindowClosing();
return 0;
}
void RbxWebView::closeDialog()
{
if(dialogActive)
webBrowserEvents.WindowClosing();
}
LRESULT RbxWebView::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
SHDocVw::IWebBrowserAppPtr pWebBrowser = NULL;
HRESULT hr = GetDlgControl(IDC_RBXEXPLORER, __uuidof(SHDocVw::IWebBrowserAppPtr), (void**)&pWebBrowser);
if (SUCCEEDED(hr))
{
CRect rect;
GetWindowRect(&rect);
pWebBrowser->put_Width(rect.Width() - ::GetSystemMetrics(SM_CXVSCROLL));
pWebBrowser->put_Height(rect.Height() - ::GetSystemMetrics(SM_CXHSCROLL) - ::GetSystemMetrics(SM_CYSIZE));
}
return 0;
}
HRESULT __stdcall WebBrowserEvents::QueryInterface(REFIID riid, LPVOID* ppv)
{
*ppv = NULL;
if (IID_IUnknown == riid || __uuidof(SHDocVw::DWebBrowserEventsPtr) == riid)
{
*ppv = (LPUNKNOWN)(SHDocVw::DWebBrowserEventsPtr*)this;
AddRef();
return NOERROR;
}
else if (IID_IOleClientSite == riid)
{
*ppv = (IOleClientSite*)this;
AddRef();
return NOERROR;
}
else if (IID_IDispatch == riid)
{
*ppv = (IDispatch*)this;
AddRef();
return NOERROR;
}
else
{
return E_NOTIMPL;
}
}
ULONG __stdcall WebBrowserEvents::AddRef() { return 1;}
ULONG __stdcall WebBrowserEvents::Release() { return 0;}
// IDispatch methods
HRESULT __stdcall WebBrowserEvents::GetTypeInfoCount(UINT* pctinfo)
{
return E_NOTIMPL;
}
HRESULT __stdcall WebBrowserEvents::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
{
return E_NOTIMPL;
}
HRESULT __stdcall WebBrowserEvents::GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId)
{
return E_NOTIMPL;
}
HRESULT __stdcall WebBrowserEvents::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS __RPC_FAR *pDispParams, VARIANT __RPC_FAR *pVarResult, EXCEPINFO __RPC_FAR *pExcepInfo, UINT __RPC_FAR *puArgErr)
{
if (dispIdMember == DISPID_WINDOWCLOSING)
WindowClosing(pDispParams);
return S_OK;
}
static void doSignalGuiServiceUrlWindowClose(RBX::DataModel* dataModel)
{
if (RBX::GuiService* guiService = dataModel->find<RBX::GuiService>())
guiService->urlWindowClosed();
}
static void signalGuiServiceUrlWindowClosed(RBX::DataModel* dataModel)
{
if(dataModel)
dataModel->submitTask(boost::bind(&doSignalGuiServiceUrlWindowClose,dataModel), RBX::DataModelJob::Write);
}
HRESULT WebBrowserEvents::WindowClosing(DISPPARAMS __RPC_FAR *pDispParams)
{
if (pDispParams)
*((VARIANT_BOOL*)pDispParams->rgvarg[0].byref) = VARIANT_TRUE;
rbxWebView->EndDialog(IDCANCEL);
rbxWebView->setDialogActive(false);
if(shared_ptr<RBX::Game> game = rbxWebView->getGame().lock())
{
signalGuiServiceUrlWindowClosed(game->getDataModel().get());
}
rbxWebView = NULL;
return S_OK;
}
HRESULT WebBrowserEvents::BeforeNavigate2(_bstr_t URL, long Flags, _bstr_t TargetFrameName, VARIANT * PostData, _bstr_t Headers, VARIANT_BOOL * Cancel)
{
std::string ulrStr = convert_w2s(std::wstring((wchar_t*)URL));
return RBX::Http::trustCheckBrowser(ulrStr.c_str()) ? S_OK : E_FAIL;
}
+100
View File
@@ -0,0 +1,100 @@
#pragma once
#include "resource.h"
#include <atlcom.h>
#include <Exdispid.h> // platform SDK header
#include "boost/shared_ptr.hpp"
#include "boost/function.hpp"
#include "v8datamodel/Game.h"
class RbxWebView; // forward declaration
class WebBrowserEvents : public DWebBrowserEvents
{
// IUnknown methods
STDMETHOD(QueryInterface)(REFIID riid, LPVOID* ppv);
STDMETHOD_(ULONG, AddRef)();
STDMETHOD_(ULONG, Release)();
// IDispatch methods
STDMETHOD(GetTypeInfoCount)(UINT* pctinfo);
STDMETHOD(GetTypeInfo)(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo);
STDMETHOD(GetIDsOfNames)(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId);
STDMETHOD(Invoke)(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS __RPC_FAR *pDispParams, VARIANT __RPC_FAR *pVarResult, EXCEPINFO __RPC_FAR *pExcepInfo, UINT __RPC_FAR *puArgErr);
// Methods:
HRESULT BeforeNavigate(_bstr_t URL, long Flags, _bstr_t TargetFrameName, VARIANT * PostData, _bstr_t Headers, VARIANT_BOOL * Cancel);
HRESULT BeforeNavigate2(_bstr_t URL, long Flags, _bstr_t TargetFrameName, VARIANT * PostData, _bstr_t Headers, VARIANT_BOOL * Cancel);
// members
RbxWebView *rbxWebView; // any time a IWebBrowser instance is needed
public:
void SetRbxWebView(RbxWebView *newView) { rbxWebView = newView; }
HRESULT WindowClosing(DISPPARAMS __RPC_FAR *pDispParams = NULL);
};
class RbxWebView :
public CAxDialogImpl<RbxWebView>,
public IDocHostUIHandler,
public IDispatch
{
ULONG m_cRef;
HICON m_hIcon;
std::string url;
weak_ptr<RBX::Game> game;
bool dialogActive;
WebBrowserEvents webBrowserEvents;
public:
enum { IDD = IDD_RBXWEBVIEW };
BEGIN_MSG_MAP(RbxWebView)
MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
MESSAGE_HANDLER(WM_CLOSE, OnClose)
MESSAGE_HANDLER(WM_SIZE, OnSize)
END_MSG_MAP()
RbxWebView(const std::string& url, shared_ptr<RBX::Game> game);
weak_ptr<RBX::Game> getGame() {return game;}
SHDocVw::IWebBrowserAppPtr RbxWebView::getWebBrowser();
LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
LRESULT OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
void closeDialog();
void PostNcDestroy();
void setDialogActive(bool active) { dialogActive = active; }
// IUnknown
STDMETHOD(QueryInterface(REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject));
ULONG STDMETHODCALLTYPE AddRef(void);
ULONG STDMETHODCALLTYPE Release(void);
// IDispatch methods
STDMETHOD(GetTypeInfoCount)(UINT* pctinfo);
STDMETHOD(GetTypeInfo)(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo);
STDMETHOD(GetIDsOfNames)(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId);
STDMETHOD(Invoke)(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS __RPC_FAR *pDispParams, VARIANT __RPC_FAR *pVarResult, EXCEPINFO __RPC_FAR *pExcepInfo, UINT __RPC_FAR *puArgErr);
// IDocHostUIHandler
STDMETHOD(ShowContextMenu(DWORD dwID, POINT *ppt, IUnknown *pcmdtReserved, IDispatch *pdispReserved));
STDMETHOD(GetHostInfo(DOCHOSTUIINFO *pInfo));
STDMETHOD(ShowUI(DWORD dwID, IOleInPlaceActiveObject *pActiveObject, IOleCommandTarget *pCommandTarget, IOleInPlaceFrame *pFrame, IOleInPlaceUIWindow *pDoc));
STDMETHOD(HideUI(void));
STDMETHOD(UpdateUI(void));
STDMETHOD(EnableModeless(BOOL fEnable));
STDMETHOD(OnDocWindowActivate(BOOL fActivate));
STDMETHOD(OnFrameWindowActivate(BOOL fActivate));
STDMETHOD(ResizeBorder(LPCRECT prcBorder, IOleInPlaceUIWindow *pUIWindow, BOOL fRameWindow));
STDMETHOD(TranslateAccelerator(LPMSG lpMsg, const GUID *pguidCmdGroup, DWORD nCmdID));
STDMETHOD(GetOptionKeyPath(LPOLESTR *pchKey, DWORD dw));
STDMETHOD(GetDropTarget(IDropTarget *pDropTarget, IDropTarget **ppDropTarget));
STDMETHOD(GetExternal(IDispatch **ppDispatch));
STDMETHOD(TranslateUrl(DWORD dwTranslate, OLECHAR *pchURLIn, OLECHAR **ppchURLOut));
STDMETHOD(FilterDataObject(IDataObject *pDO, IDataObject **ppDORet));
};
+72
View File
@@ -0,0 +1,72 @@
============================================================================
Helpful info:
Run the player from the command-line with the --help parameter to learn the
command-line parameters. To easily connect to a place use -id followed by a
place id (such as 1818 for crossroads). To spoof MD5 hash use -md5, please note
this will NOT work on release builds.
To test local scripts use -adminScripts, also will not work on release builds.
============================================================================
Security
Continually review and implement any missing security code:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ietechcol/cols/dnexpie/activex_security.asp
============================================================================
Settings
----------------------------------------------------------------------------
Default locations:
AppSettings.xml: <<Module directory>> (e.g. sibling file to %SystemDrive%/Program Files (x86)/Roblox/Versions/virsion-[####]/RobloxApp.exe)
Content Folder: <<Module directory>>\Content
Cache Folder: CSIDL_COMMON_APPDATA\Roblox\Cache\
Log files: CSIDL_LOCAL_APPDATA\Roblox\logs\
Alternate log files: CSIDL_COMMON_APPDATA\Roblox\logs\
User IDESettings.xml: CSIDL_LOCAL_APPDATA\Roblox\
----------------------------------------------------------------------------
Determining BaseUrl (in order of priority):
1) AppSettings.xml: Settings->BaseUrl
----------------------------------------------------------------------------
Determining the content folder (in order of priority):
1) Command-line (--content)
2) AppSettings.xml: Settings->ContentFolder (might be a relative path w.r.t. Module)
3) <<Default location>> (see "Default Location" section above)
----------------------------------------------------------
Determining CrashReport (in order of priority):
1) HKEY_CURRENT_USER\Software\ROBLOX Corporation\Roblox CrashReport (REG_DWORD)
2) TRUE
----------------------------------------------------------
Determining SilentCrashReport (in order of priority):
1) HKEY_CURRENT_USER\Software\ROBLOX Corporation\Roblox SilentCrashReport (REG_DWORD)
2) AppSettings.xml: Settings->SilentCrashReport
3) FALSE
+721
View File
@@ -0,0 +1,721 @@
// This is version 2 of the golden hash patcher. Version 1 was an external program.
// This new version has the advantage that I know the locations of all of the data.
// I can also call functions from my program.
// This makes this approach much more convenient.
//
// Todo: determine if the PE can be modified after write to remove .zero entirely.
#include "stdafx.h"
#include <windows.h>
#include <psapi.h>
#include <vector>
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <algorithm>
#include "Util/ProgramMemoryChecker.h"
#include "Util/CheatEngine.h"
#include "ReleasePatcher.h"
#include "Security/ApiSecurity.h"
#include "Network/NetPmc.h"
LOGVARIABLE(Zero,1)
#pragma optimize("s", on)
namespace {
struct MemRange
{
uintptr_t base;
size_t size;
MemRange() : base(0), size(0) {}
MemRange(uintptr_t base, size_t size) : base(base), size(size) {}
static bool sizeCmpGreater(const MemRange& a, const MemRange& b)
{
return b.size < a.size;
}
};
enum VmpRangeIdx
{
kVmpPlain = 0,
kVmp0Misc = 1,
kVmpMutant = 2,
kVmp1Misc = 3
};
// PE tools
typedef std::vector<IMAGE_SECTION_HEADER*> SectionPtrVector;
__declspec(code_seg(".zero")) bool getSectionInfo(const SectionPtrVector& sections, const char* name, uintptr_t& baseAddr, size_t& size)
{
const size_t kPeSectionNameLimit = 9;
for (size_t i = 0; i < sections.size(); ++i)
{
if (strncmp(reinterpret_cast<const char*>(sections[i]->Name), name, kPeSectionNameLimit) == 0) //
{
baseAddr = sections[i]->VirtualAddress + reinterpret_cast<size_t>(GetModuleHandle(NULL));
size = sections[i]->Misc.VirtualSize; // Virtual size ignores file alignment padding.
return true;
}
}
return false;
}
__declspec(code_seg(".zero")) int getSections(void* pImageBase, SectionPtrVector& sections)
{
IMAGE_DOS_HEADER *pDosHdr = nullptr;
IMAGE_SECTION_HEADER *pSection = nullptr;
// Get DOS header
pDosHdr = reinterpret_cast<IMAGE_DOS_HEADER*>(pImageBase);
// File not a valid PE file
if (pDosHdr->e_magic != IMAGE_DOS_SIGNATURE)
return -1;
// Get image header
IMAGE_NT_HEADERS32* pImageHdr32 = reinterpret_cast<IMAGE_NT_HEADERS32*>(
reinterpret_cast<BYTE*>(pDosHdr) + pDosHdr->e_lfanew);
// File not a valid PE file
if (pImageHdr32->Signature != IMAGE_NT_SIGNATURE)
return -2;
pSection = reinterpret_cast<IMAGE_SECTION_HEADER*>(pImageHdr32 + 1);
// Sections
for (int i = 0; i < pImageHdr32->FileHeader.NumberOfSections; ++i, ++pSection)
sections.push_back( pSection );
return 0;
}
// This is a heuristic -- checks for 32 0's at end of the last page.
__declspec(code_seg(".zero")) bool isStartOfSection(uintptr_t addr)
{
static const unsigned char kZeros[32] = {};
for (int i = 1; i <= 2; ++i)
{
if (0 == memcmp(reinterpret_cast<const char*>(addr) - i*sizeof(kZeros), &kZeros, sizeof(kZeros)))
{
return true;
}
}
return false;
}
__declspec(code_seg(".zero")) bool getVmpSections(const SectionPtrVector& sections, std::vector<MemRange>& ranges, uintptr_t& vmpBase, size_t& vmpSize)
{
// get the .vmp0 section
if (!getSectionInfo(sections, ".vmp0", vmpBase, vmpSize))
{
FASTLOG(FLog::Zero, "couldn't find .vmp0");
return false;
}
// Find the first gold section
// The section starts @ vmp base and ends at a section that starts with .idata. I find this
// by looking for "EncodePointer" near the beginning of that section (heuristic)
ranges.resize(4);
std::string strBuffer;
uintptr_t vmpAddr = vmpBase;
uintptr_t vmpMiscBeginAddr = vmpAddr;
bool endFound = false;
while (vmpAddr < (vmpBase + vmpSize - 4096))
{
strBuffer = std::string(reinterpret_cast<const char*>(vmpAddr), 1024);
if (strBuffer.find("EncodePointer") != strBuffer.npos)
{
ranges[kVmpPlain] = MemRange(vmpBase, vmpAddr-vmpBase);
vmpMiscBeginAddr = vmpAddr;
endFound = true;
break;
}
vmpAddr += 4096;
}
if (!endFound)
{
FASTLOG(FLog::Zero, "failed first split.");
ranges[kVmpPlain] = MemRange(vmpBase, 4);
}
// get the vmp1 section
uintptr_t vmp1Base;
size_t vmp1Size;
if (!getSectionInfo(sections, ".vmp1", vmp1Base, vmp1Size))
{
FASTLOG(FLog::Zero, "couldn't find .vmp1");
return false;
}
vmpSize = (vmp1Base+vmp1Size) - vmpBase;
// Get the second gold section.
// This is done by looking at the end of the vmp0 section to find the end of that
// iat/rdata/rsrc/??? section from above. the section will end with 32 aligned zeros
// near the end of the page.
uintptr_t vmpMiscEndAddr = vmp1Base - 4096;
vmpAddr = vmpMiscEndAddr;
endFound = false;
while ( vmpAddr > (vmpBase+4096))
{
if (isStartOfSection(vmpAddr))
{
vmpMiscEndAddr = vmpAddr;
endFound = true;
break;
}
vmpAddr -= 4096;
}
if (endFound)
{
// add the misc section, then the gold mutant section
ranges[kVmp0Misc] = MemRange(vmpMiscBeginAddr, vmpMiscEndAddr - vmpMiscBeginAddr);
ranges[kVmpMutant] = MemRange(vmpMiscEndAddr, vmp1Base - vmpMiscEndAddr);
}
else
{
// this could be an error, but for now I'll allow it.
FASTLOG(FLog::Zero, "failed second split.");
ranges[kVmp0Misc] = MemRange(vmpMiscBeginAddr, vmp1Base - vmpMiscBeginAddr);
ranges[kVmpMutant] = MemRange(vmp1Base, 4);
}
// The second misc section.
ranges[kVmp1Misc] = MemRange(vmp1Base, vmp1Size);
return true;
}
// File IO
__declspec(code_seg(".zero")) int readFile(const char* fileName, std::vector<char>& buffer)
{
std::ifstream file(fileName, std::ifstream::binary);
if (file.is_open())
{
file.seekg(0,std::ios::end);
std::streampos length = file.tellg();
file.seekg(0,std::ios::beg);
#pragma warning(disable : 4244)
buffer.resize(length);
#pragma warning(default : 4244)
file.read(&buffer[0],length);
return buffer.size();
}
else
{
return 0;
}
}
__declspec(code_seg(".zero")) int writeFile(const char* fileName, const std::vector<char>& buffer)
{
std::ofstream file(fileName, std::ofstream::binary);
if (file.is_open())
{
file.write(&buffer[0],buffer.size());
}
else
{
return -1;
}
return buffer.size();
}
__declspec(code_seg(".zero")) uintptr_t getFileOffsetOfVa(const SectionPtrVector& sections, uintptr_t addr)
{
for (size_t i = 0; i < sections.size(); ++i)
{
if (addr - sections[i]->VirtualAddress < sections[i]->SizeOfRawData)
{
return (uintptr_t)(
(addr - sections[i]->VirtualAddress) // relative to section
+ sections[i]->PointerToRawData); // relative to buffer
}
}
return 0;
}
__declspec(code_seg(".zero")) bool getImportThunkSection(const SectionPtrVector& sections, uintptr_t& iatBase, size_t& iatSize)
{
iatBase = 0;
iatSize = 0;
uintptr_t rdataVa = 0;
uintptr_t rdataRva = 0;
size_t rdataSize = 0;
for (size_t i = 0; i < sections.size(); ++i)
{
if (strncmp(reinterpret_cast<const char*>(sections[i]->Name), ".rdata", 7) == 0)
{
rdataVa = sections[i]->PointerToRawData;
rdataRva = sections[i]->VirtualAddress + reinterpret_cast<uintptr_t>(GetModuleHandle(NULL));
rdataSize = sections[i]->SizeOfRawData;
break;
}
}
// need to open the _original_ file (not the one modified by VMProtect)
TCHAR name[MAX_PATH]; // assumed to be RobloxPlayerBeta.exe
GetModuleFileName(GetModuleHandle(NULL), name, sizeof(name)/sizeof(TCHAR));
std::string pathName(name);
std::stringstream modifiedName;
if (pathName.substr(pathName.size()-7) != "Raw.exe")
{
modifiedName << pathName.substr(0, pathName.size()-4) << "Raw.exe";
}
else
{
modifiedName << pathName;
}
std::vector<char> rawBinary;
readFile(modifiedName.str().c_str(), rawBinary);
if (rawBinary.empty())
{
return false;
}
// need to read RobloxPlayerBetaRaw to get the original table.
// assume the .text and .rdata sections are in the same locations.
// change ".rdata" to point to the locations in RobloxPlayerBetaRaw.exe
DWORD base = reinterpret_cast<DWORD>(&rawBinary[0]);
rdataVa = rdataVa + base;
std::vector<BYTE> buffer(rdataSize);
// get the import descriptor table -- the list of (non-delay) loaded dll's.
// see https://msdn.microsoft.com/en-us/library/ms809762.aspx "PE File Imports"
// Visual Studio 2012 seems to place the address table at the start of .rdata.
DWORD* loc = 0;
PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)(&rawBinary[0]);
PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)(dosHeader->e_lfanew + (char *)dosHeader);
IMAGE_DATA_DIRECTORY* imageDataDir = ntHeader->OptionalHeader.DataDirectory;
DWORD importVa = imageDataDir[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
IMAGE_IMPORT_DESCRIPTOR* idtEntry = reinterpret_cast<IMAGE_IMPORT_DESCRIPTOR*>(
getFileOffsetOfVa(sections, importVa) + base);
// iterate over the import directory table (until null entry found)
for(;idtEntry->Name; ++idtEntry)
{
// iterate over thunks until null entry found
DWORD* thunk = reinterpret_cast<DWORD*>(getFileOffsetOfVa(sections, idtEntry->FirstThunk) + base);
do
{
if (reinterpret_cast<uintptr_t>(thunk) - rdataVa < rdataSize)
{
size_t idx = reinterpret_cast<uintptr_t>(thunk) - rdataVa;
memset(&buffer[idx], 0xFF, sizeof(void*));
}
} while(*(thunk++));
}
// Check that there is a single contiguous section
uintptr_t firstThunkByte = rdataSize;
uintptr_t lastThunkByte = rdataSize;
size_t thunkSize = 0;
for (size_t i = 0; i < rdataSize; ++i)
{
if (buffer[i])
{
firstThunkByte = (firstThunkByte == rdataSize) ? i : firstThunkByte;
lastThunkByte = i;
++thunkSize;
}
}
if (lastThunkByte+1 - firstThunkByte == thunkSize)
{
iatBase = rdataRva + firstThunkByte;
iatSize = thunkSize;
return true;
}
return false;
}
// Convenient ways to set values
class SectionMapping
{
size_t loadedRva;
size_t fileOffset;
public:
__declspec(code_seg(".zero")) SectionMapping(size_t loadedRva, size_t fileOffset) : loadedRva(loadedRva), fileOffset(fileOffset) {};
template<typename T> __declspec(code_seg(".zero")) void set(const volatile T* virtualAddr, T& value)
{
// Set in the PE file in RAM
uintptr_t modAddr = (reinterpret_cast<uintptr_t>(virtualAddr) - loadedRva) // location relative to .rdata after loaded
+ fileOffset; // location of the filemapped .rdata
T* ptr = reinterpret_cast<T*>(modAddr);
*ptr = value;
// Set in the current process
T* evilAddr = const_cast<T*>(virtualAddr);
*evilAddr = value;
}
};
__declspec(code_seg(".zero")) bool updateNetPmcPartial(uintptr_t origBase, size_t origSize, RBX::Security::NetPmcChallenge* resultArray)
{
uintptr_t base = origBase;
size_t term = origBase+origSize;
size_t stepAvg = (term-base+31)/32;
for (int i = 0; i < 32; ++i)
{
size_t thisTerm = 32*((base+stepAvg+31)/32);
size_t thisBase = (thisTerm < term) ? base : (base - (thisTerm - term));
resultArray[i].base = thisBase;
resultArray[i].size = (thisTerm < term) ? (thisTerm - thisBase) : (term - thisBase);
resultArray[i].seed = i;
resultArray[i].result = netPmcHashCheck(resultArray[i]);
base += (resultArray[i].size);
}
return true;
}
__declspec(code_seg(".zero")) bool updateNetPmcResult(uint32_t key, RBX::Security::NetPmcChallenge* result)
{
uint64_t msb = static_cast<uint64_t>(key) << 32;
result->result = RBX::Security::teaEncrypt(msb | result->result);
return true;
}
// this function modifies the code caves in the .text section to add references to a specific peice of code.
__declspec(code_seg(".zero")) bool addRefsToWcPage(uintptr_t textBase, size_t textSize, uintptr_t textFileBase)
{
static const unsigned char kMov = 0xA3;
static const unsigned char kInt3 = 0xCC;
static const unsigned char kRet = 0xC3;
static const unsigned char kRetPop = 0xC2;
static const unsigned char kHotpatchFirst = 0x8B;
static const unsigned char kPadding[8] = {kInt3, kInt3, kInt3, kInt3, kInt3, kInt3, kInt3, kInt3 };
std::string textCopy(reinterpret_cast<char*>(textBase), textSize);
size_t pos = 0;
while ((pos = textCopy.find(reinterpret_cast<const char*>(kPadding), pos, sizeof(kPadding))) && pos != std::string::npos)
{
if ((pos > 3) // next checks are safe
&& ( (static_cast<unsigned char>(textCopy[pos-3]) == kRetPop) || (static_cast<unsigned char>(textCopy[pos-1]) == kRet) ) // end of function
&& (pos + sizeof(kPadding) < textCopy.size()) // not at end of file
&& (static_cast<unsigned char>(textCopy[pos+sizeof(kPadding)]) != kHotpatchFirst) ) // not part of hotpatch
{
while( (pos < textCopy.size()) && (static_cast<unsigned char>(textCopy[pos]) == kInt3) && pos++ );
pos -= 5;
// update the file in memory
*reinterpret_cast<unsigned char*>(textFileBase + pos) = kMov;
*reinterpret_cast<uint32_t*>(textFileBase + pos + 1) = reinterpret_cast<uint32_t>(&RBX::writecopyTrap);
// update the currently running program
*reinterpret_cast<unsigned char*>(textBase + pos) = kMov;
*reinterpret_cast<uint32_t*>(textBase + pos + 1) = reinterpret_cast<uint32_t>(&RBX::writecopyTrap);
pos += 5;
}
else
{
pos+= sizeof(kPadding);
}
}
return true;
}
__declspec(code_seg(".zero")) bool createUpdatedExe(HANDLE hChild)
{
// get section info
SectionPtrVector procSections;
getSections(GetModuleHandleA(NULL), procSections);
uintptr_t textBase;
size_t textSize;
if (!getSectionInfo(procSections, ".text", textBase, textSize))
{
FASTLOG(FLog::Zero, "couldn't find .text");
return false;
}
uintptr_t rdataBase;
size_t rdataSize;
if (!getSectionInfo(procSections, ".rdata", rdataBase, rdataSize))
{
FASTLOG(FLog::Zero, "couldn't find .rdata");
return false;
}
// generate some values to allow comparison to rdata section in a less obvious way
uintptr_t textEndNeg = (~(textBase+textSize))+1;
size_t textSizeNeg = (~textSize)+1;
uintptr_t vmpBase;
size_t vmpSize;
std::vector<MemRange> vmpRanges;
if (!getVmpSections(procSections, vmpRanges, vmpBase, vmpSize))
{
FASTLOG(FLog::Zero, "couldn't find .vmp");
return false;
}
// Get a copy of the .text
DWORD amount = 0;
std::vector<BYTE> buffer;
buffer.resize(textSize);
if (!ReadProcessMemory(hChild, reinterpret_cast<void*>(textBase), &buffer[0], textSize, &amount))
{
FASTLOG1(FLog::Zero, "RPM failed: 0x%08X", GetLastError());
return false;
}
// Compare to mine
// WARNING -- software breakpoints in .text will affect this.
BYTE* mapOfText = reinterpret_cast<BYTE*>(textBase);
int errCnt = 0;
uintptr_t errLoc = 0;
uintptr_t patchLoc;
for (size_t i = 0; i < textSize; ++i)
{
if ((i-errLoc > 4) && (buffer[i] != mapOfText[i]))
{
++errCnt;
patchLoc = textBase + i;
errLoc = i;
}
}
if (errCnt == 0)
{
// This could actually happen and should cause a retry.
FASTLOG(FLog::Zero, ".text is the same");
return false;
}
else if (errCnt > 1)
{
FASTLOG1(FLog::Zero, ".text has %d differences", errCnt);
return false;
}
// split the .text as needed
// This allows some bytes past the end of where I think the DWORD difference is,
// as well as some bytes before where I think the difference is.
patchLoc &= ~3; // ignore 2 lsb (difference could be on any/all bytes)
const size_t kUpperSlack = 8;
const size_t kLowerSlack = 4;
uintptr_t textUpperBase = patchLoc + kUpperSlack;
size_t textUpperSize = textBase + textSize - textUpperBase;
uintptr_t textLowerBase = textBase;
size_t textLowerSize = textSize - (textUpperSize + kUpperSlack) - kLowerSlack;
// read exe file into RAM
TCHAR name[MAX_PATH];
GetModuleFileName(GetModuleHandle(NULL), name, sizeof(name)/sizeof(TCHAR));
std::vector<char> fileBuffer;
int fileSize = readFile(name, fileBuffer);
if (0 == fileSize)
{
FASTLOG(FLog::Zero, "can't read file");
return false;
}
// get the file offset of the .rdata and .text sections
SectionPtrVector fileSections;
getSections(&fileBuffer[0], fileSections);
uintptr_t rdataFileBase = 0;
uintptr_t rdataFileRva = 0;
uintptr_t textFileBase = 0;
uintptr_t textFileRva = 0;
for (size_t i = 0; i < fileSections.size(); ++i)
{
if (strncmp(reinterpret_cast<const char*>(fileSections[i]->Name), ".rdata", 7) == 0)
{
rdataFileBase = reinterpret_cast<uintptr_t>(fileSections[i]->PointerToRawData + &fileBuffer[0]);
rdataFileRva = fileSections[i]->VirtualAddress + reinterpret_cast<size_t>(GetModuleHandle(NULL));
}
else if (strncmp(reinterpret_cast<const char*>(fileSections[i]->Name), ".text", 6) == 0)
{
textFileBase = reinterpret_cast<uintptr_t>(fileSections[i]->PointerToRawData + &fileBuffer[0]);
textFileRva = fileSections[i]->VirtualAddress + reinterpret_cast<size_t>(GetModuleHandle(NULL));
}
}
FASTLOG1(FLog::Zero, "rdata filebase = 0x%08X", rdataFileBase);
FASTLOG1(FLog::Zero, "rdata filerva = 0x%08X", rdataFileRva);
FASTLOG1(FLog::Zero, "text filebase = 0x%08X", textFileBase);
FASTLOG1(FLog::Zero, "text filerva = 0x%08X", textFileRva);
// Get the import address table
uintptr_t iatBase = 0;
uintptr_t iatSize = 0;
if (!getImportThunkSection(fileSections, iatBase, iatSize))
{
// This part was speculative when I first added it. Fail gracefully.
iatBase = rdataBase;
FASTLOG(FLog::Zero, "Cannot find Import Address Table");
}
uintptr_t rdataNoIatBase = rdataBase + iatSize;
size_t rdataNoIatSize = rdataSize - iatSize;
// update all of the values in .rdata
// Section mapping updates the current process as well.
// The IAT will not be modified, so it is safe to use the modified rdataBase/rdataSize.
DWORD unused = 0; // not an optional argument.
VirtualProtect(reinterpret_cast<void*>(rdataBase), rdataSize, PAGE_READWRITE, &unused);
SectionMapping rdataMapping(rdataFileRva, rdataFileBase);
rdataMapping.set(&RBX::Security::rbxTextBase, textBase);
rdataMapping.set(&RBX::Security::rbxTextSize, textSize);
rdataMapping.set(&RBX::Security::rbxLowerBase, textLowerBase);
rdataMapping.set(&RBX::Security::rbxLowerSize, textLowerSize);
rdataMapping.set(&RBX::Security::rbxUpperBase, textUpperBase);
rdataMapping.set(&RBX::Security::rbxUpperSize, textUpperSize);
rdataMapping.set(&RBX::Security::rbxRdataBase, rdataBase);
rdataMapping.set(&RBX::Security::rbxRdataSize, rdataSize);
rdataMapping.set(&RBX::Security::rbxIatBase, iatBase);
rdataMapping.set(&RBX::Security::rbxIatSize, iatSize);
rdataMapping.set(&RBX::Security::rbxRdataNoIatBase, rdataNoIatBase);
rdataMapping.set(&RBX::Security::rbxRdataNoIatSize, rdataNoIatSize);
rdataMapping.set(&RBX::Security::rbxVmpBase, vmpBase);
rdataMapping.set(&RBX::Security::rbxVmpSize, vmpSize);
rdataMapping.set(&RBX::Security::rbxVmpPlainBase, vmpRanges[kVmpPlain].base);
rdataMapping.set(&RBX::Security::rbxVmpPlainSize, vmpRanges[kVmpPlain].size);
rdataMapping.set(&RBX::Security::rbxVmpMutantBase, vmpRanges[kVmpMutant].base);
rdataMapping.set(&RBX::Security::rbxVmpMutantSize, vmpRanges[kVmpMutant].size);
rdataMapping.set(&RBX::Security::rbxVmp0MiscBase, vmpRanges[kVmp0Misc].base);
rdataMapping.set(&RBX::Security::rbxVmp0MiscSize, vmpRanges[kVmp0Misc].size);
rdataMapping.set(&RBX::Security::rbxVmp1MiscBase, vmpRanges[kVmp1Misc].base);
rdataMapping.set(&RBX::Security::rbxVmp1MiscSize, vmpRanges[kVmp1Misc].size);
rdataMapping.set(&RBX::Security::rbxTextEndNeg, textEndNeg);
rdataMapping.set(&RBX::Security::rbxTextSizeNeg, textSizeNeg);
// update the .text section padding here.
VirtualProtect(reinterpret_cast<void*>(textBase), textSize, PAGE_EXECUTE_READWRITE, &unused);
addRefsToWcPage(textBase, textSize, textFileBase);
// Update the NetPmc values here.
std::vector<RBX::Security::NetPmcChallenge> netPmcChallenges;
netPmcChallenges.resize(RBX::Security::kNumChallenges);
updateNetPmcPartial(textLowerBase, textLowerSize, &netPmcChallenges[ 0]);
updateNetPmcPartial(textUpperBase, textUpperSize, &netPmcChallenges[32]);
updateNetPmcPartial(vmpRanges[kVmpPlain].base, vmpRanges[kVmpPlain].size, &netPmcChallenges[64]);
updateNetPmcPartial(vmpRanges[kVmpMutant].base, vmpRanges[kVmpMutant].size, &netPmcChallenges[96]);
// shuffle them
std::vector<unsigned char> randIdx;
randIdx.resize(RBX::Security::kNumChallenges);
for (unsigned char i = 0;i < RBX::Security::kNumChallenges; ++i)
{
randIdx[i] = i;
}
std::random_shuffle(randIdx.begin(), randIdx.end());
// Stream Encryption
std::vector<RBX::Security::NetPmcChallenge> salsaKey = RBX::Security::generateNetPmcKeys();
for (unsigned char i = 0; i < RBX::Security::kNumChallenges; ++i)
{
updateNetPmcResult(randIdx[i], &netPmcChallenges[i]);
netPmcChallenges[i] ^= salsaKey[randIdx[i]];
rdataMapping.set(&RBX::Security::kChallenges[randIdx[i]], netPmcChallenges[i]);
}
// hash is auto computed in constructor
RBX::ProgramMemoryChecker pmc;
size_t newHash = pmc.getLastGoldenHash();
rdataMapping.set(&RBX::Security::rbxGoldHash, newHash);
// This has affected .rdata, so regenerate the hash. This has updated the pmcHash global.
RBX::ProgramMemoryChecker pmcForFile;
// Write items of checkIdx,value,failMask for each check for this version of the client.
// checkIdx is the index of the hash within the pmcHash.hashes vector.
// value is the value that it should be.
// failMask is the bitmask that will be or'd into the result.
std::ofstream goldMemHashWriter("goldMemHash.txt", std::ofstream::binary);
goldMemHashWriter << RBX::Hasher::kGoldHashStart << "," << RBX::pmcHash.hash[RBX::Hasher::kGoldHashStart] << "," << RBX::Hasher::kGoldHashStartFail << ";";
goldMemHashWriter << RBX::Hasher::kGoldHashEnd << "," << RBX::pmcHash.hash[RBX::Hasher::kGoldHashEnd] << "," << RBX::Hasher::kGoldHashEndFail<< ";";
goldMemHashWriter << RBX::Hasher::kRdataHash << "," << RBX::pmcHash.hash[RBX::Hasher::kRdataHash] << "," << RBX::Hasher::kRdataHashFail << ";";
goldMemHashWriter << RBX::Hasher::kVmpPlainHash << "," << RBX::pmcHash.hash[RBX::Hasher::kVmpPlainHash] << "," << RBX::Hasher::kVmpPlainHashFail << ";";
goldMemHashWriter << RBX::Hasher::kVmpMutantHash << "," << RBX::pmcHash.hash[RBX::Hasher::kVmpMutantHash] << "," << RBX::Hasher::kVmpMutantHashFail << ";";
goldMemHashWriter << RBX::Hasher::kGoldHashStruct << "," << RBX::pmcHash.hash[RBX::Hasher::kGoldHashStruct] << "," << RBX::Hasher::kGoldHashStructFail;
goldMemHashWriter.close();
// Remove the .zero section from the final exe
for (size_t i = 0; i < fileSections.size(); ++i)
{
if (strncmp(reinterpret_cast<const char*>(fileSections[i]->Name), ".zero", 5) == 0)
{
uintptr_t zeroFileBase = reinterpret_cast<uintptr_t>(fileSections[i]->PointerToRawData + &fileBuffer[0]);
memset(reinterpret_cast<void*>(zeroFileBase), 0, fileSections[i]->SizeOfRawData); // SizeOfRawData is size in file.
fileSections[i]->Characteristics &= ~(
IMAGE_SCN_CNT_CODE
| IMAGE_SCN_CNT_INITIALIZED_DATA
| IMAGE_SCN_CNT_UNINITIALIZED_DATA
| IMAGE_SCN_MEM_READ
| IMAGE_SCN_MEM_WRITE
| IMAGE_SCN_MEM_EXECUTE);
break;
}
}
// Write file back to disk.
size_t nameSize = strnlen(name,MAX_PATH);
strcpy_s(&name[nameSize-4], nameSize, ".tmp");
FASTLOG1(FLog::Zero, "final file: %s", name);
if (!writeFile(name, fileBuffer))
{
FASTLOG(FLog::Zero, "can't write file");
return false;
}
return true;
}
} // namespace
namespace RBX { namespace Security {
// look into if the VMP section can be got from the create_suspended child.
__declspec(code_seg(".zero")) bool patchMain()
{
TCHAR name[MAX_PATH];
GetModuleFileName(GetModuleHandle(NULL), name, sizeof(name)/sizeof(TCHAR));
FASTLOG1(FLog::Zero, "exe = %s", name);
char* cli = " -w 10558381 ";
FASTLOG1(FLog::Zero, "cli = %s", cli);
int rerunCount = 2;
// Call the EXE a second time
do
{
PROCESS_INFORMATION childInfo;
STARTUPINFO startInfo;
ZeroMemory( &startInfo, sizeof(startInfo) );
startInfo.cb = sizeof(startInfo);
ZeroMemory( &childInfo, sizeof(childInfo) );
if (!CreateProcess(name,
cli,
NULL,
NULL,
false,
CREATE_SUSPENDED,
NULL,
NULL,
&startInfo,
&childInfo))
{
FASTLOG1(FLog::Zero, "child failed to start = %s", cli);
return false;
}
if (createUpdatedExe(childInfo.hProcess))
{
rerunCount = 0;
}
else
{
--rerunCount;
}
ResumeThread(childInfo.hThread);
WaitForSingleObject(childInfo.hProcess, INFINITE);
} while (rerunCount > 0);
return true;
}
} // ::Security
} // ::RBX
#pragma optimize("", on)
+8
View File
@@ -0,0 +1,8 @@
#pragma once
namespace RBX{ namespace Security {
__declspec(code_seg(".zero")) bool patchMain();
}
}
+220
View File
@@ -0,0 +1,220 @@
#include "stdafx.h"
#include "RenderJob.h"
#include "FunctionMarshaller.h"
#include "GfxBase/ViewBase.h"
#include "GfxBase/FrameRateManager.h"
#include "network/api.h"
#include "network/players.h"
#include "rbx/Log.h"
#include "rbx/SystemUtil.h"
#include "RenderSettingsItem.h"
#include "v8datamodel/HackDefines.h"
#include "v8datamodel/ModelInstance.h"
#include "v8datamodel/Workspace.h"
#include "View.h"
#include "VMProtectSDK.h"
FASTFLAG(RenderLowLatencyLoop)
namespace RBX {
RenderJob::RenderJob(View* robloxView,
FunctionMarshaller* marshaller,
boost::shared_ptr<DataModel> dataModel)
: BaseRenderJob(CRenderSettingsItem::singleton().getMinFrameRate(), CRenderSettingsItem::singleton().getMaxFrameRate(), dataModel)
, robloxView(robloxView)
, marshaller(marshaller)
, stopped(0)
, prepareBeginEvent(false)
, prepareEndEvent(false)
{
}
void RenderJob::stop()
{
stopped = 1;
}
Time::Interval RenderJob::timeSinceLastRender() const
{
return Time::now<Time::Fast>() - lastRenderTime;
}
Time::Interval RenderJob::sleepTime(const Stats& stats)
{
if (isAwake)
return computeStandardSleepTime(stats, maxFrameRate);
else
return RBX::Time::Interval::max();
}
static void remoteCheatHelper(boost::weak_ptr<DataModel> weakDataModel)
{
boost::shared_ptr<DataModel> dataModel = weakDataModel.lock();
if (dataModel)
{
// this will send special item to server and server will kick user off
Network::getSystemUrlLocal(dataModel.get());
}
}
static void reportHacker(boost::weak_ptr<DataModel> weakDataModel, const char* stat)
{
if (boost::shared_ptr<DataModel> dataModel = weakDataModel.lock())
{
if (Network::Players* players = dataModel->find<Network::Players>())
{
if (Network::Player* player = players->getLocalPlayer())
{
player->reportStat(stat);
}
}
}
}
void RenderJob::scheduleRender(weak_ptr<RenderJob> selfWeak, ViewBase* view, double timeJobStart)
{
shared_ptr<RenderJob> self = selfWeak.lock();
if (!self) return;
self->prepareBeginEvent.Wait();
view->renderPrepare(self.get());
self->prepareEndEvent.Set();
view->renderPerform(timeJobStart);
self->wake();
}
static void scheduleRenderPerform(const weak_ptr<RenderJob>& selfWeak, ViewBase* view, double timeJobStart)
{
if (shared_ptr<RenderJob> self = selfWeak.lock())
{
view->renderPerform(timeJobStart);
self->wake();
}
}
TaskScheduler::StepResult RenderJob::stepDataModelJob(const Stats& stats)
{
shared_ptr<DataModel> dm = robloxView->getDataModel();
if (!dm || stopped)
return TaskScheduler::Done;
// Enable security checks for speedhack and attached debugger in release mode
#if !defined(LOVE_ALL_ACCESS) && !defined(RBX_STUDIO_BUILD) && !defined(_NOOPT) && !defined(DEBUG)
VMProtectBeginMutation("34");
if (Time::isSpeedCheater())
{
dm->submitTask(boost::bind(&reportHacker, boost::weak_ptr<DataModel>(dm),
"richard"), DataModelJob::Write);
}
if (Time::isDebugged())
{
dm->submitTask(boost::bind(&reportHacker, boost::weak_ptr<DataModel>(dm),
"suzanne"), DataModelJob::Write);
}
VMProtectEnd();
#endif
double timeJobStart = Time::nowFastSec();
ViewBase* view = robloxView->GetGfxView();
if (FFlag::RenderLowLatencyLoop)
{
RBX::DataModel::scoped_write_request request(dm.get());
const double renderDelta = timeSinceLastRender().seconds();
lastRenderTime = RBX::Time::now<RBX::Time::Fast>();
isAwake = false;
marshaller->Submit(boost::bind(&scheduleRender, weak_from(this), view, timeJobStart));
view->updateVR();
dm->renderStep(renderDelta);
prepareBeginEvent.Set();
prepareEndEvent.Wait();
}
else
{
{
DataModel::scoped_write_request request(robloxView->getDataModel().get());
view->updateVR();
float secondsElapsed = robloxView->GetGfxView()->getFrameRateManager()->GetFrameTimeStats().getLatest() / 1000.f;
dm->renderStep(secondsElapsed);
isAwake = false;
marshaller->Execute(boost::bind(&ViewBase::renderPrepare, view, this));
lastRenderTime = Time::now<Time::Fast>();
}
{
marshaller->Submit(boost::bind(&scheduleRenderPerform, weak_from(this), view, timeJobStart));
}
}
return TaskScheduler::Stepped;
}
// Return information (via IMetric interface, probable asker was DataModel)
std::string RenderJob::getMetric(const std::string& metric) const
{
if (metric == "Graphics Mode")
return RBX::Reflection::EnumDesc<CRenderSettings::GraphicsMode>::singleton().convertToString(robloxView->GetLatchedGraphicsMode());
if (metric == "Render") {
boost::format fmt("%.1f/s %d%%");
fmt % averageStepsPerSecond() % (int)(100.0 * averageDutyCycle());
return fmt.str();
}
ViewBase* view = robloxView->GetGfxView();
RBX::FrameRateManager* frm = view ? view->getFrameRateManager() : 0;
if(frm) {
if (metric == "FRM") return (frm && frm->IsBlockCullingEnabled()) ? "On" : "Off";
if (metric == "Anti-Aliasing") return (frm && frm->getAntialiasingMode() == CRenderSettings::AntialiasingOn) ? "On" : "Off";
}
// If we got here it means we were explicitly asked for information we do
// not have.
RBXASSERT(0);
return "?";
}
// Return information (via IMetric interface, probable asker was DataModel)
double RenderJob::getMetricValue(const std::string& metric) const
{
ViewBase* view = robloxView->GetGfxView();
if (metric == "Render Duty") return averageDutyCycle();
if (metric == "Render FPS") return averageStepsPerSecond();
if (metric == "Render Job Time") return averageStepTime();
if (metric == "Render Nominal FPS") return 1000.0 / view->getFrameRateManager()->GetRenderTimeAverage();
if (metric == "Delta Between Renders") return view->getMetricValue(metric);
if (metric == "Total Render") return view->getMetricValue(metric);
if (metric == "Present Time") return view->getMetricValue(metric);
if (metric == "GPU Delay") return view->getMetricValue(metric);
if (metric == "Video Memory") return SystemUtil::getVideoMemory();
// If we got here it means we were explicitly asked for information we do
// not have.
RBXASSERT(0);
return 0.0;
}
} // namespace RBX
+49
View File
@@ -0,0 +1,49 @@
#pragma once
#include <boost/weak_ptr.hpp>
#include "v8datamodel/BaseRenderJob.h"
#include "v8datamodel/DataModel.h"
#include "rbx/rbxTime.h"
#include "rbx/TaskScheduler.h"
#include "rbx/TaskScheduler.Job.h"
#include "util/IMetric.h"
namespace RBX {
class FunctionMarshaller;
class View;
class ViewBase;
// This job calls ViewBase::render(), which needs to be done exclusive to the
// DataModel. This is why it has the DataModelJob::Render enum, which
// prevents concurrent writes to DataModel. It also needs to run in the view's
// thread for OpenGL.
// TODO: Can Ogre be modified to not require the thread?
class RenderJob : public BaseRenderJob, public IMetric
{
FunctionMarshaller* marshaller;
View* robloxView;
volatile int stopped;
CEvent prepareBeginEvent;
CEvent prepareEndEvent;
static void scheduleRender(weak_ptr<RenderJob> selfWeak, ViewBase* view, double timeJobStart);
public:
RenderJob(View* robloxView, FunctionMarshaller* marshaller,
boost::shared_ptr<DataModel> dataModel);
Time::Interval timeSinceLastRender() const;
Time::Interval sleepTime(const Stats& stats);
virtual TaskScheduler::StepResult stepDataModelJob(const Stats& stats);
virtual std::string getMetric(const std::string& metric) const;
virtual double getMetricValue(const std::string& metric) const;
void stop();
};
} // namespace RBX
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

@@ -0,0 +1,20 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2012
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RobloxGoldenHashPatcher", "RobloxGoldenHashPatcher.vcxproj", "{7AB9F250-6F9E-45A1-BBD5-378E9425A5BB}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{7AB9F250-6F9E-45A1-BBD5-378E9425A5BB}.Debug|Win32.ActiveCfg = Debug|Win32
{7AB9F250-6F9E-45A1-BBD5-378E9425A5BB}.Debug|Win32.Build.0 = Debug|Win32
{7AB9F250-6F9E-45A1-BBD5-378E9425A5BB}.Release|Win32.ActiveCfg = Release|Win32
{7AB9F250-6F9E-45A1-BBD5-378E9425A5BB}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{7AB9F250-6F9E-45A1-BBD5-378E9425A5BB}</ProjectGuid>
<RootNamespace>RobloxGoldenHashPatcher</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v110</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v110</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="main.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>
@@ -0,0 +1,317 @@
#include <fstream>
#include <iostream>
#include <sstream>
#include <iterator>
#include <vector>
#include <algorithm>
#include <stdio.h>
#include <string.h>
int readFile(const char* fileName, std::vector<char>& buffer)
{
std::ifstream file(fileName, std::ifstream::binary);
if (file)
{
/*
* Get the size of the file
*/
file.seekg(0,std::ios::end);
std::streampos length = file.tellg();
file.seekg(0,std::ios::beg);
/*
* Use a vector as the buffer.
* It is exception safe and will be tidied up correctly.
* This constructor creates a buffer of the correct length.
*
* Then read the whole file into the buffer.
*/
buffer.resize(length);
file.read(&buffer[0],length);
}
else
{
return -1;
}
return buffer.size();
}
int writeFile(const char* fileName, const std::vector<char>& buffer)
{
std::ofstream file(fileName, std::ifstream::binary);
if (file)
{
file.write(&buffer[0],buffer.size());
}
else
{
return -1;
}
return buffer.size();
}
std::string getFileName(const std::string& sysPath, const std::string& fileName)
{
return sysPath + '\\' + fileName;
}
std::string setCommand(const std::string& sysPath, const std::string& sysExe, const std::string& fileName)
{
return getFileName(sysPath, sysExe) + " -w 195936478 --globalBasicSettingsPath " + fileName;
}
size_t findAndSetOffset(const std::vector<char>& exeBuffer, size_t idx, const char* search, size_t& location)
{
if (strcmp(&exeBuffer[idx], search) == 0)
{
if (location != 0)
{
return -1;
}
location = idx;
}
return 0;
}
int main(int argc, char** argv)
{
if (argc != 3)
{
std::cerr << "Patcher <Dir of exe> <Name of exe>\n";
return 1;
}
std::vector<char> cmpBufferOne;
std::vector<char> cmpBufferTwo;
std::vector<char> cmpBufferGold;
std::vector<char> exeBuffer;
// Step 1: Run the program with the secret options.
// "195936478" is the magic number = 0x0BADC0DE
std::string sysPath(argv[1]);
std::string sysExe(argv[2]);
std::string sysCommand;
std::string relFile; // filename with path
std::string baseFile; // filename without path
std::string exeFile = getFileName(sysPath, sysExe);
// Run player with special options to dump the .text section to "buffer1.bin"
// The dump will write:
// .text
// 32b int = Golden Hash, calculated
// 32b int = Golden Hash to compare.
baseFile = "buffer1.bin";
relFile = getFileName(sysPath, baseFile);
sysCommand = setCommand(sysPath, sysExe, baseFile);
std::cout << sysCommand << std::endl;
system(sysCommand.c_str());
int fileErrorCode = 0;
fileErrorCode = readFile(relFile.c_str(), cmpBufferOne);
if (fileErrorCode < 0)
{
std::cerr << " couldn't read buffer1.bin.\n";
return 1;
}
const int kPadLen = sizeof(int) + sizeof(int);
int codeLen = cmpBufferOne.size() - kPadLen;
int bufferGhOffset = codeLen; // calculated goldHash offset in buffer (what app calculates)
int bufferRhOffset = codeLen + sizeof(int); // referenced goldHash offset in buffer (what app checks against)
std::cout << "codeLen = " << codeLen << std::endl;
// Step 2: find the location that is different.
// VM protect has a pointer that is different in .text. This will find the
// location. Only run 3 times to find it.
int maxIters = 3;
int diffLocation = -1;
baseFile = "buffer2.bin";
relFile = getFileName(sysPath, baseFile);
sysCommand = setCommand(sysPath, sysExe, baseFile);
while (maxIters)
{
system(sysCommand.c_str());
fileErrorCode = readFile(relFile.c_str(), cmpBufferTwo);
if (fileErrorCode < 0)
{
std::cerr << "couldn't read buffer2.bin.\n";
return 1;
}
// make sure only one place in the file is different.
// if multiple are different, make sure ASLR is disabled
// (note that .rdata also has vtbls from some dll's...)
for (int i = 0; i < codeLen; ++i)
{
if(cmpBufferOne[i] != cmpBufferTwo[i])
{
if ((diffLocation >= 0) && ((i-diffLocation) > 4))
{
std::cerr << "Multiple places have differences.\n";
return -1;
}
else
{
diffLocation = i & 0xFFFFFFFC; // ignore 2 lsb
}
}
}
if (diffLocation >= 0)
{
break;
}
else
{
--maxIters;
}
}
if (maxIters == 0)
{
std::cerr << "Could not find differences between runs.\n";
return -1;
}
// Step 3: Find the locations to modify in the exe
fileErrorCode = readFile(exeFile.c_str(), exeBuffer);
if (fileErrorCode < 0)
{
std::cerr << "couldn't read executable.\n";
return 1;
}
int exeLen = exeBuffer.size();
// There is a common, unique prefix. Check to ensure there is only
// one for maskAddr and one for goldHash
size_t maskAddrOffset = 0;
size_t goldHashOffset = 0;
const char* magicMaskAddrCstr = "1(m$n9.[?y`z+f : a&Rn5<d*nGD9.@93Dr7&";
const char* magicGoldHashCstr = "1N9S6,*%D-&m8sH%/~m _qvZ=&be*db elI^s";
// Added the base/size to the patcher.
const size_t imageBase = 0x00401000;
const char magicImageBaseCstr[24] = {
0x26, 0x53, 0x25, 0x58, 0x64, 0x40, 0x6E, 0x42, 0x34, 0x58, 0x30, 0x20,
0x63, 0x18, 0x0A, 0x34, 0x75, 0x44, 0x26, 0x46, 0x35, 0x29, 0x68, 0x00};
const char magicImageSizeCstr[24] = {
0x35, 0x10, 0x0F, 0x72, 0x38, 0x52, 0x57, 0x43, 0x47, 0x20, 0x39, 0x33,
0x23, 0x42, 0x66, 0x2A, 0x2E, 0x3C, 0x3B, 0x22, 0x5C, 0x73, 0x45, 0x00};
size_t imageBaseOffset = 0;
size_t imageSizeOffset = 0;
bool useImageInfo = true;
// I realize there are some better string matching algorithms out there.
// the exe isn't that large.
for (int i = 0; i < exeLen; ++i)
{
if (findAndSetOffset(exeBuffer, i, magicMaskAddrCstr, maskAddrOffset))
{
std::cerr << "maskAddrOffset is not unique!\n";
return 1;
}
if (findAndSetOffset(exeBuffer, i, magicGoldHashCstr, goldHashOffset))
{
std::cerr << "goldHashOffset is not unique!\n";
return 1;
}
if (findAndSetOffset(exeBuffer, i, magicImageBaseCstr, imageBaseOffset))
{
std::cerr << "imageBaseOffset is not unique!\n";
useImageInfo = false;
}
if (findAndSetOffset(exeBuffer, i, magicImageSizeCstr, imageSizeOffset))
{
std::cerr << "imageSizeOffset is not unique!\n";
useImageInfo = false;
}
}
if (maskAddrOffset == 0)
{
std::cerr << "Unable to find maskAddr.\n";
return 1;
}
if (goldHashOffset == 0)
{
std::cerr << "Unable to find goldHash.\n";
return 1;
}
if (imageBaseOffset == 0)
{
std::cerr << "Unable to find imageBase.\n";
useImageInfo = false;
}
if (imageSizeOffset == 0)
{
std::cerr << "Unable to find imageSize.\n";
useImageInfo = false;
}
size_t alignedMaskAddrOffset = (maskAddrOffset | 7) + 1;
size_t alignedGoldHashOffset = (goldHashOffset | 7) + 1;
imageBaseOffset -= sizeof(size_t); // found byte 4 (on 32b)
imageSizeOffset -= sizeof(size_t);
std::cout << "diffLocation = " << std::hex << diffLocation << std::endl;
std::cout << "maskAddr = " << std::hex << maskAddrOffset << std::endl;
std::cout << "goldHash = " << std::hex << goldHashOffset << std::endl;
std::cout << "imageBase = " << std::hex << imageBaseOffset << std::endl;
std::cout << "imageSize = " << std::hex << imageSizeOffset << std::endl;
// Step 4: Modify the maskAddr
// The aligned versions are used in the application and place the values
// on 8B boundaries. I've noticed that it VC++ doesn't align strings in
// all cases.
//
// Also update the imageBase and imageSize here.
int* maskAddrPtr = (int*) &exeBuffer[alignedMaskAddrOffset];
*maskAddrPtr = diffLocation;
if (useImageInfo)
{
*((size_t*) &exeBuffer[imageBaseOffset]) = imageBase;
*((size_t*) &exeBuffer[imageSizeOffset]) = codeLen;
}
fileErrorCode = writeFile(exeFile.c_str(), exeBuffer);
if (fileErrorCode < 0)
{
std::cerr << "Failed to write first.\n";
return -1;
}
// Step 5: Run the program to get the golden hash
baseFile = "buffer2.bin";
relFile = getFileName(sysPath, baseFile);
sysCommand = setCommand(sysPath, sysExe, baseFile);
system(sysCommand.c_str());
fileErrorCode = readFile(relFile.c_str(), cmpBufferTwo);
if (fileErrorCode < 0)
{
std::cerr << "couldn't read buffer2.bin.\n";
return 1;
}
int goldHashValue = *((int*) (&cmpBufferTwo[bufferGhOffset]));
// Step 6: Modify the goldHash and confirm the hash doesn't change
int* goldHashPtr = (int*) &exeBuffer[alignedGoldHashOffset];
*goldHashPtr = goldHashValue;
fileErrorCode = writeFile(exeFile.c_str(), exeBuffer);
if (fileErrorCode < 0)
{
std::cerr << "Failed to write first.\n";
return -1;
}
baseFile = "buffer3.bin";
relFile = getFileName(sysPath, baseFile);
sysCommand = setCommand(sysPath, sysExe, baseFile);
system(sysCommand.c_str());
fileErrorCode = readFile(relFile.c_str(), cmpBufferGold);
if (fileErrorCode < 0)
{
std::cerr << "couldn't read buffer3.bin.\n";
return 1;
}
int goldHashCheck = *((int*) (&cmpBufferGold[bufferGhOffset])); // the value reported by the 2nd run of the hash check.
int goldHashRCheck = *((int*) (&cmpBufferGold[bufferRhOffset])); // the value reported as the golden hash in the code.
std::cout << std::hex << goldHashCheck << " == " << std::hex << goldHashValue << " : " << std::hex << goldHashRCheck << std::endl;
// This is the check to see if the previously recorded goldHash matches
// both the calculated hash from the new run AND the goldHash that our app will check against.
if ((goldHashValue != goldHashCheck) || (goldHashValue != goldHashRCheck))
{
std::cerr << "Error, golden hash didn't work!\n";
return 1;
}
return 0;
}
+13
View File
@@ -0,0 +1,13 @@
#pragma once
namespace RBX
{
typedef BOOLEAN (NTAPI *RtlDispatchExceptionPfn)(PEXCEPTION_RECORD exRec, PCONTEXT ctx);
// extern RtlDispatchExceptionPfn vehHookContinue; // moved to App because this needs to be checked.
extern DWORD* vehHookLocation;
BOOLEAN RtlDispatchExceptionHook(PEXCEPTION_RECORD exRec, PCONTEXT ctx);
void hookApi();
void unhookApi();
bool hookPreVeh();
}
+29
View File
@@ -0,0 +1,29 @@
#include "stdafx.h"
#include "Teleporter.h"
#include "Application.h"
#include "FunctionMarshaller.h"
#include "util/Statistics.h"
#include "v8datamodel/TeleportService.h"
namespace RBX {
static boost::thread releaseGameThread;
void Teleporter::initialize(Application* app,
FunctionMarshaller* marshaller)
{
this->app = app;
this->marshaller = marshaller;
TeleportService::SetCallback(this);
TeleportService::SetBaseUrl(GetBaseURL().c_str());
}
void Teleporter::doTeleport(const std::string& url, const std::string& ticket,
const std::string& script)
{
marshaller->Submit(boost::bind(&Application::Teleport, app, url, ticket, script));
}
} // namespace RBX
+26
View File
@@ -0,0 +1,26 @@
#pragma once
#include "v8datamodel/TeleportCallback.h"
namespace RBX {
class Application;
class FunctionMarshaller;
// The Teleporter is responsible for teleporting the player across places.
class Teleporter : public TeleportCallback
{
Application* app;
FunctionMarshaller* marshaller;
public:
void initialize(Application* app, FunctionMarshaller* marshaller);
// Submits a callback to the function marshaller to teleport the player.
virtual void doTeleport(const std::string& url, const std::string& ticket,
const std::string& script);
virtual bool isTeleportEnabled() const { return true; }
};
} // namespace RBX
File diff suppressed because it is too large Load Diff
+185
View File
@@ -0,0 +1,185 @@
#pragma once
#include "stdafx.h"
#include "UserInputUtil.h"
#include "Util/UserInputBase.h"
#include "util/standardout.h"
#include "SDLGameController.h"
#pragma comment(lib, "dxguid.lib")
#pragma comment(lib, "dxerr9.lib")
#pragma comment(lib, "dinput8.lib")
namespace RBX {
class Game;
class DataModel;
class RunService;
class View;
class RobloxCriticalSection
{
public:
int callers;
std::string recentCaller;
std::string olderCaller;
ATL::CCriticalSection diSection;
RobloxCriticalSection() : callers(0)
{
}
};
class UserInput : public UserInputBase
{
enum MouseButtonType
{
MBUTTON_LEFT = 0,
MBUTTON_RIGHT = 1,
MBUTTON_MIDDLE = 2
};
rbx::signals::scoped_connection steppedConnection;
mutable RobloxCriticalSection diSection;
// Mouse Stuff
bool isMouseCaptured; // poor man's tracker of button state
Vector2 wrapMousePosition; // in normalized coordinates (center is 0,0. radius is getWrapRadius)
bool wrapping;
bool rightMouseDown;
bool autoMouseMove;
bool mouseButtonSwap; // used for left hand mouse
// Keyboard Stuff
BYTE diKeys[256]; // regular key state
int externallyForcedKeyDown; // + this from external source (like a gui button)
HKL layout;
BYTE keyboardState[256];
std::vector<ACCEL> accelerators;
// DirectInput fields
HWND wnd;
CComPtr<IDirectInput8> diPtr;
CComPtr<IDirectInputDevice8> diMousePtr;
CComPtr<IDirectInputDevice8> diKeyboardPtr;
// InputObject stuff
boost::unordered_map<RBX::InputObject::UserInputType,shared_ptr<RBX::InputObject> > inputObjectMap;
// Gamepad stuff
shared_ptr<SDLGameController> sdlGameController;
shared_ptr<RunService> runService;
View* parentView;
G3D::Vector2 posToWrapTo;
G3D::Vector2 previousCursorPosFraction;
bool leftMouseButtonDown;
HCURSOR hArrow;
////////////////////////////////
//
// Events
void sendEvent(shared_ptr<InputObject> event);
void sendMouseEvent(InputObject::UserInputType mouseEventType,
InputObject::UserInputState mouseEventState,
Vector3& position,
Vector3& delta);
// helper for mouse events
void processMouseButtonEvent(DIDEVICEOBJECTDATA mouseData,
MouseButtonType mouseButton, bool &leftMouseUp);
////////////////////////////////////
//
// Keyboard Mouse
bool isMouseInside;
bool desireKeyboardAcquired;
bool isMouseAcquired; // goes false if data read fails
bool isKeyboardAcquired; // goes false if data read fails
void createMouse();
void createKeyboard();
void createAccelerators();
void updateMouse();
void updateKeyboard();
void acquireMouseInternal();
void acquireMouseInternalBase(const Vector2& pos);
void acquireKeyboard();
void unAcquireMouse();
void unAcquireKeyboard();
bool readBufferedData(LPDIDEVICEOBJECTDATA didod, DWORD& dwElements,
IDirectInputDevice8* device);
void readBufferedMouseData();
void readBufferedKeyboardData();
// window stuff
Vector2int16 getWindowSize() const;
G3D::Rect2D getWindowRect() const;
bool isFullScreenMode() const;
bool movementKeysDown();
bool keyDownInternal(KeyCode code) const;
void doWrapMouse(const G3D::Vector2& delta, G3D::Vector2& wrapMouseDelta);
void setKeyboardDesiredInternal(bool set); // keyboard set by having focus;
// todo: no longer used remove this
void doWrapHybrid(bool cursorMoved, bool leftMouseUp, G3D::Vector2& wrapMouseDelta, G3D::Vector2& wrapMousePosition, G3D::Vector2& posToWrapTo);
G3D::Vector2 getGameCursorPositionInternal();
G3D::Vector2 getGameCursorPositionExpandedInternal(); // prevent hysteresis
G3D::Vector2 getWindowsCursorPositionInternal();
Vector2 getCursorPositionInternal();
void doDiagnostics();
void postProcessUserInput(bool cursorMoved, bool leftMouseUp, RBX::Vector2 wrapMouseDelta, RBX::Vector2 mouseDelta);
public:
shared_ptr<RBX::Game> game;
const static int WM_CALL_SETFOCUS = WM_USER + 187;
HCURSOR hInvisibleCursor;
////////////////////////////////
//
// UserInputBase
/*implement*/ Vector2 getCursorPosition();
/*implement*/ bool keyDown(KeyCode code) const;
/*implement*/ void setKeyState(RBX::KeyCode code, RBX::ModCode modCode, char modifiedKey, bool isDown);
/*implement*/ void centerCursor() { wrapMousePosition = Vector2::zero(); }
/*override*/ TextureProxyBaseRef getGameCursor(Adorn* adorn);
void postUserInputMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
// Call this only within a DataModel lock:
void processUserInputMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
UserInput(HWND wnd, shared_ptr<RBX::Game> game, View* view);
~UserInput();
void setGame(shared_ptr<RBX::Game> game);
void setKeyboardDesired(bool set); // keyboard set by having focus;
void onMouseInside();
void onMouseLeave();
bool getIsKeyboardAcquired() const { return isKeyboardAcquired; }
bool getIsMouseAcquired() const { return isMouseAcquired; }
void reacquireKeyboard();
void processInput();
};
} // namespace RBX
+723
View File
@@ -0,0 +1,723 @@
#include "stdafx.h"
#include <atltypes.h>
#include "v8datamodel/DebugSettings.h"
#include "v8datamodel/GameBasicSettings.h"
#include "script/ScriptContext.h"
#include "FunctionMarshaller.h"
#include "GameVerbs.h"
#include "LogManager.h"
#include "rbx/Tasks/Coordinator.h"
#include "RenderJob.h"
#include "RenderSettingsItem.h"
#include "util/ScopedAssign.h"
#include "v8datamodel/Game.h"
#include "v8datamodel/UserController.h"
#include "InitializationError.h"
#include "View.h"
#include "format_string.h"
#include "util/RobloxGoogleAnalytics.h"
#include "rbx/SystemUtil.h"
LOGGROUP(PlayerShutdownLuaTimeoutSeconds)
LOGGROUP(RobloxWndInit)
FASTFLAGVARIABLE(DirectX11Enable, false)
FASTFLAGVARIABLE(GraphicsReportingInitErrorsToGAEnabled,true)
FASTFLAGVARIABLE(UseNewAppBridgeInputWindows, false)
DYNAMIC_FASTFLAGVARIABLE(FullscreenRefocusingFix, false)
namespace {
HWND SetFocusWrapper(HWND hwnd)
{
return ::SetFocus(hwnd);
}
}
namespace RBX {
static const char* kSavedScreenSizeRegistryKey =
"HKEY_CURRENT_USER\\Software\\ROBLOX Corporation\\Roblox\\Settings\\RobloxPlayerV4WindowSizeAndPosition";
View::View(HWND h)
: fullscreen(false)
, desireFullscreen(false)
, changedResolution(false)
, changingResolution(false)
, hMonitor(NULL)
, marshaller(NULL)
, windowSettingsValid(false)
, windowSettingsMaximized(false)
, restoreWindowStyle(NULL)
{
ZeroMemory(&nonFullscreenPlacement, sizeof(nonFullscreenPlacement));
desireFullscreen = RBX::GameBasicSettings::singleton().getFullScreen();
context.hWnd = h;
marshaller = FunctionMarshaller::GetWindow();
initializeView();
}
View::~View()
{
RBXASSERT(!this->game && "Call Stop() before shutting down!");
view.reset();
if (marshaller)
FunctionMarshaller::ReleaseWindow(marshaller);
}
void View::AboutToShutdown()
{
rememberWindowSettings();
}
void View::rememberWindowSettings()
{
// try to save window size and position
if (HWND hWnd = GetHWnd())
{
WINDOWPLACEMENT placement;
placement.length = sizeof(WINDOWPLACEMENT);
bool foundPlacement = false;
if (!fullscreen)
foundPlacement = GetWindowPlacement(hWnd, &placement) != 0;
else
{
foundPlacement = true;
placement = nonFullscreenPlacement;
}
if (foundPlacement)
{
RECT rect;
GetWindowRect(hWnd, &rect);
RECT windowTaskRect;
HWND taskBar = FindWindow("Shell_traywnd", NULL);
if(taskBar && GetWindowRect(taskBar, &windowTaskRect))
{
// need to do some adjustment depending on where the task bar is living
const int newLeft = rect.left - (windowTaskRect.right - windowTaskRect.left);
if (newLeft >= 0)
{
const int leftDiff = rect.left - newLeft;
rect.left = newLeft;
rect.right -= leftDiff;
}
const int newTop = rect.top - (windowTaskRect.bottom - windowTaskRect.top);
if (newTop >= 0)
{
const int topDiff = rect.top - newTop;
rect.top = newTop;
rect.bottom -= topDiff;
}
placement.rcNormalPosition = rect;
}
else
{
placement.rcNormalPosition = rect;
}
Vector4 lastRect(placement.rcNormalPosition.left,placement.rcNormalPosition.top,
placement.rcNormalPosition.right - placement.rcNormalPosition.left,
placement.rcNormalPosition.bottom - placement.rcNormalPosition.top);
windowSettingsValid = true;
windowSettingsRectangle = lastRect;
windowSettingsMaximized = (placement.showCmd == SW_SHOWMAXIMIZED);
}
}
}
void View::saveWindowSettings()
{
if (windowSettingsValid)
{
DataModel::LegacyLock lock(game->getDataModel(), DataModelJob::Write);
RBX::GameBasicSettings::singleton().setStartScreenSize(windowSettingsRectangle.zw());
RBX::GameBasicSettings::singleton().setStartScreenPos(windowSettingsRectangle.xy());
RBX::GameBasicSettings::singleton().setStartMaximized(windowSettingsMaximized);
}
}
void View::initializeView()
{
ViewBase::InitPluginModules();
LPCSTR rgLogSuffix[5];
memset(rgLogSuffix, 0, sizeof(rgLogSuffix));
rgLogSuffix[(size_t)CRenderSettings::Direct3D9] = "gfx_d3d9";
rgLogSuffix[(size_t)CRenderSettings::Direct3D11] = "gfx_d3d11";
rgLogSuffix[(size_t)CRenderSettings::OpenGL] = "gfx_gl";
std::vector<CRenderSettings::GraphicsMode> modes;
RBX::CRenderSettings::GraphicsMode graphicsMode = CRenderSettingsItem::singleton().getLatchedGraphicsMode();
switch(graphicsMode)
{
case CRenderSettings::NoGraphics:
break;
case CRenderSettings::OpenGL:
case CRenderSettings::Direct3D9:
case CRenderSettings::Direct3D11:
modes.push_back(graphicsMode);
break;
default:
if (FFlag::DirectX11Enable)
modes.push_back(CRenderSettings::Direct3D11);
modes.push_back(CRenderSettings::Direct3D9);
modes.push_back(CRenderSettings::OpenGL);
break;
}
std::string lastMessage;
bool success = false;
size_t modei = 0;
while(!success && modei < modes.size())
{
graphicsMode = modes[modei];
try
{
view.reset(ViewBase::CreateView(graphicsMode, &context, &CRenderSettingsItem::singleton()));
view->initResources();
success = true;
}
catch (std::exception& e)
{
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_WARNING, "Mode %d failed: \"%s\"", graphicsMode, e.what());
lastMessage += e.what();
lastMessage += " | ";
modei++;
if(modei < modes.size())
{
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_WARNING, "Trying mode %d...", modes[modei]);
}
}
}
if(!success)
{
if (FFlag::GraphicsReportingInitErrorsToGAEnabled)
{{{
lastMessage += SystemUtil::getGPUMake() + " | ";
lastMessage += SystemUtil::osVer();
const char* label = modes.size()? "GraphicsInitError" : "GraphicsInitErrorNoModes";
RobloxGoogleAnalytics::trackEventWithoutThrottling( GA_CATEGORY_GAME, label , lastMessage.c_str(), 0 );
}}}
::WriteProfileString("Settings", "lastGFXMode", "-1");
throw initialization_error(
"Your graphics drivers seem to be too old for Roblox to use.\n\n"
"Visit http://www.roblox.com/drivers for info on how to perform a driver upgrade.");
}
RBXASSERT( view );
::WriteProfileString("Settings", "lastGFXMode", format_string("%d", (int)graphicsMode).c_str());
initializeSizes();
}
void View::resetScheduler()
{
TaskScheduler& taskScheduler = TaskScheduler::singleton();
taskScheduler.add(renderJob);
}
void View::HandleWindowsMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (WM_ACTIVATE == uMsg)
{
// We become active, need to restore window if fullscreen
if ((fullscreen || (!fullscreen && desireFullscreen)) && !changingResolution && (WA_ACTIVE == wParam || WA_CLICKACTIVE == wParam))
{
changeResolution();
HWND hWnd = GetHWnd();
::ShowWindow(hWnd, SW_RESTORE);
SetFocus(hWnd);
SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
}
// We become inactive, need to minimize if fullscreen
else if ( fullscreen && !changingResolution && WA_INACTIVE == wParam
&& ::GetParent((HWND)lParam) != GetHWnd() )
{
HWND hWnd = GetHWnd();
SetWindowLongPtr(hWnd, GWL_STYLE, WS_VISIBLE | WS_POPUP | WS_MINIMIZEBOX | WS_MAXIMIZEBOX| WS_CLIPSIBLINGS | WS_CLIPCHILDREN);
SetFocus(hWnd);
SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
}
}
// Other messages that made it to here are meant for UserInput
else if (!FFlag::UseNewAppBridgeInputWindows && userInput)
{
userInput->postUserInputMessage(uMsg, wParam, lParam);
}
}
void View::initializeJobs()
{
shared_ptr<DataModel> dataModel = game->getDataModel();
renderJob.reset(new RenderJob(this, marshaller, dataModel));
}
void View::initializeInput()
{
userInput.reset(new UserInput(GetHWnd(), game, this));
if (userInput)
{
DataModel::LegacyLock lock(game->getDataModel(), DataModelJob::Write);
ControllerService* service =
ServiceProvider::create<ControllerService>(game->getDataModel().get());
service->setHardwareDevice(userInput.get());
}
}
void View::RemoveJobs()
{
if (renderJob)
{
boost::function<void()> callback =
boost::bind(&FunctionMarshaller::ProcessMessages, marshaller);
TaskScheduler::singleton().removeBlocking(renderJob, callback);
}
// RenderJob is sure to be completed at this point, since removeBlocking returned - but it might have marshalled
// renderPerform asynchronously before exiting, which means that we might still have a callback that uses this view
// in the marshaller queue.
// This makes sure that all pending marshalled events are processed to avoid a use after free.
marshaller->ProcessMessages();
// All render processing is complete; it's safe to reset job pointers now
renderJob.reset();
}
shared_ptr<DataModel> View::getDataModel()
{
return game ? game->getDataModel() : shared_ptr<DataModel>();
}
CRenderSettings::GraphicsMode View::GetLatchedGraphicsMode()
{
return CRenderSettingsItem::singleton().getLatchedGraphicsMode();
}
bool View::IsFullscreen()
{
return fullscreen;
}
bool View::findBestMonitorMatch(LPCTSTR szDevice, int desiredX, int desiredY, bool resolutionAuto, DEVMODE& dmBest)
{
DEVMODE dm;
ZeroMemory(&dm, sizeof(dm));
dm.dmSize = sizeof(dm);
LONG result = EnumDisplaySettingsEx(szDevice, ENUM_CURRENT_SETTINGS, &dm, 0);
if (result==0)
{
LogManager::ReportEvent(EVENTLOG_ERROR_TYPE,
RBX::format("In view::findBestMonitorMatch EnumDisplaySettings failed. "
"GetLastError() == %d", GetLastError()).c_str());
return false;
}
FASTLOG2(FLog::RobloxWndInit, "Current screen resolution - %d x %d", dm.dmPelsWidth, dm.dmPelsHeight);
// If current user mode is smaller than the desired resolution, just keep it
if (dm.dmPelsHeight <= (DWORD)desiredY && resolutionAuto)
return true;
int desiredPixelCount = desiredX * desiredY;
const double currentAspectRatio = double(dm.dmPelsWidth)/double(dm.dmPelsHeight);
dmBest = dm;
bool match = true;
DWORD iModeNum = 0;
while (EnumDisplaySettingsEx(szDevice, iModeNum++, &dm, 0)!=0)
{
const double aspectRatio = double(dm.dmPelsWidth)/double(dm.dmPelsHeight);
if (std::abs(currentAspectRatio-aspectRatio) / currentAspectRatio <= 0.1)
{
if (dmBest.dmBitsPerPel==dm.dmBitsPerPel && dmBest.dmDisplayFrequency==dm.dmDisplayFrequency)
{
int a = desiredPixelCount-dm.dmPelsWidth*dm.dmPelsHeight;
int b = desiredPixelCount-dmBest.dmPelsWidth*dmBest.dmPelsHeight;
if (std::abs(a) < std::abs(b))
{
dmBest = dm;
match = false;
}
}
}
}
// Only change resolution
dmBest.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
return match;
}
void View::restoreResolution()
{
fullscreen = false;
FASTLOG(FLog::RobloxWndInit, "Start View::restoreResolution");
RBX::ScopedAssign<bool> assign(changingResolution, true);
if (hMonitor == NULL) {
LogManager::ReportEvent(EVENTLOG_ERROR_TYPE, "Unable to restore resolution, no handle to monitor");
return;
}
if (changedResolution) {
MONITORINFOEX mi;
mi.cbSize = sizeof(mi);
if (GetMonitorInfo(hMonitor, &mi)) {
// For some reason we need to hide the window before restoring the resolution,
// otherwise ChangeDisplaySettings will return DISP_CHANGE_SUCCESSFUL
::ShowWindow(GetHWnd(), SW_HIDE);
LONG result = ChangeDisplaySettingsEx(mi.szDevice, NULL, NULL, 0, NULL);
if (result != DISP_CHANGE_SUCCESSFUL) {
LogManager::ReportEvent(EVENTLOG_ERROR_TYPE,
RBX::format("Unable to restore resolution, ChangeDisplaySettingsEx returned %d", result).c_str());
}
::ShowWindow(GetHWnd(), SW_SHOWNORMAL);
}
changedResolution = false;
}
if (!fullscreen)
{
if (restoreWindowStyle) {
SetWindowLongPtr(GetHWnd(), GWL_STYLE, restoreWindowStyle);
} else {
SetWindowLongPtr(GetHWnd(), GWL_STYLE, WS_OVERLAPPEDWINDOW);
}
}
SetWindowPlacement(GetHWnd(), &nonFullscreenPlacement);
FASTLOG(FLog::RobloxWndInit, "Done Vew::restoreResolution");
}
void View::SetFullscreen(bool value)
{
if (fullscreen != value)
{
if (value)
{
restoreWindowStyle = GetWindowLong(GetHWnd(), GWL_STYLE);
changeResolution();
}
else
restoreResolution();
}
desireFullscreen = value;
RBX::GameBasicSettings::singleton().setFullScreen(value);
}
void View::initializeSizes()
{
if (GetHWnd() == NULL) {
LogManager::ReportEvent(EVENTLOG_WARNING_TYPE, "Attempt to initialize monitor sizes without valid HWND");
return;
}
G3D::Vector2int16 currentDisplaySize = getCurrentDesktopResolution();
G3D::Vector2int16 fullscreenSize, windowSize;
RBX::CRenderSettings::ResolutionPreset preference = CRenderSettingsItem::singleton().getResolutionPreference();
if (preference == RBX::CRenderSettings::ResolutionAuto) {
fullscreenSize = currentDisplaySize;
} else {
const RBX::CRenderSettings::RESOLUTIONENTRY& res =
CRenderSettingsItem::singleton().getResolutionPreset(preference);
fullscreenSize.x = res.width;
fullscreenSize.y = res.height;
}
// validate mode
windowSize = fullscreenSize;
windowSize.x = std::min((int)windowSize.x, (int)currentDisplaySize.x);
windowSize.y = std::min((int)windowSize.y, (int)currentDisplaySize.y);
CRenderSettingsItem::singleton().setWindowSize(windowSize);
CRenderSettingsItem::singleton().setFullscreenSize(fullscreenSize);
}
void View::modifyWindow(DWORD argMask, const RECT& area)
{
SetWindowLongPtr(GetHWnd(), GWL_STYLE, argMask);
SetWindowPos(GetHWnd(), NULL, area.left, area.top, area.right - area.left, area.bottom - area.top, 0);
}
void View::changeResolution()
{
// Save current rect so we can restore to it, but only if we're not already fullscreen
if (!DFFlag::FullscreenRefocusingFix || !fullscreen)
{
ZeroMemory(&nonFullscreenPlacement, sizeof(nonFullscreenPlacement));
nonFullscreenPlacement.length = sizeof(nonFullscreenPlacement);
GetWindowPlacement(GetHWnd(), &nonFullscreenPlacement);
}
fullscreen = true;
// Get monitor information
hMonitor = MonitorFromWindow(GetHWnd(), MONITOR_DEFAULTTONEAREST);
if (hMonitor == NULL) {
LogManager::ReportEvent(EVENTLOG_ERROR_TYPE, "Cannot changeResolution, MonitorFromWindow returned NULL");
return;
}
// We need to initialize sizes every time we toggle fullscreen in case user
// has modified desktop settings during program run
initializeSizes();
RBX::ScopedAssign<bool> assign(changingResolution, true);
MONITORINFOEX mi;
mi.cbSize = sizeof(mi);
if (!GetMonitorInfo(hMonitor, &mi)) {
LogManager::ReportEvent(EVENTLOG_ERROR_TYPE, "Cannot changeResolution, GetMonitorInfo failed");
return;
}
G3D::Vector2int16 size = CRenderSettingsItem::singleton().getFullscreenSize();
DEVMODE dm;
bool matches = findBestMonitorMatch(mi.szDevice, size.x, size.y,
CRenderSettingsItem::singleton().getResolutionPreference() == RBX::CRenderSettings::ResolutionAuto, dm);
LONG result = matches ? DISP_CHANGE_SUCCESSFUL : ChangeDisplaySettingsEx(mi.szDevice, &dm,
NULL, CDS_FULLSCREEN, NULL);
if (result == DISP_CHANGE_SUCCESSFUL) {
if (!changedResolution)
changedResolution = !matches;
if (!matches)
FASTLOG2(FLog::RobloxWndInit, "Changed screen resolution to %d x %d", dm.dmPelsWidth, dm.dmPelsHeight);
// Now resize the window to the monitor's (potentially) new resolution
MONITORINFOEX monitorInfo;
monitorInfo.cbSize = sizeof(monitorInfo);
if (!GetMonitorInfo(hMonitor, &monitorInfo)) {
LogManager::ReportEvent(EVENTLOG_ERROR_TYPE, "Cannot changeResolution, GetMonitorInfo failed");
return;
}
int cxBorder = ::GetSystemMetrics(SM_CXBORDER);
int cyBorder = ::GetSystemMetrics(SM_CYBORDER);
cxBorder = cyBorder = 0;
G3D::Vector2int16 newResolution = getCurrentDesktopResolution();
RECT area = monitorInfo.rcMonitor;
area.left -= cxBorder;
area.right += cxBorder * 2;
area.top -= cyBorder;
area.bottom += cyBorder * 2;
modifyWindow(WS_VISIBLE | WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, area);
} else {
LogManager::ReportEvent(EVENTLOG_ERROR_TYPE, RBX::format("ChangeDisplaySettings returned %d", result).c_str());
}
FASTLOG(FLog::RobloxWndInit, "Done View::changeResolution");
}
G3D::Vector2int16 View::calcDefaultResolution(float aspect_XdivY)
{
// It's ok to return approximate fullscreen size since fullscreen transition
// code will filter it through the mode list
int numlines = 600;
int videomem = RBX::DebugSettings::singleton().videoMemory();
std::string videocard = RBX::DebugSettings::singleton().gfxcard();
if (videocard.find("Intel") != std::string::npos) {
// Intel videocards report random amount of video memory, so use patterns to find good resolution
// 828xx cards mean 810/815, no shaders, really slow
if (videocard.find("828") != std::string::npos) {
numlines = 600;
} else {
numlines = 1200;
}
} else if (videomem <= 32) {
numlines = 600;
} else if (videomem <= 64) {
numlines = 1024;
} else if (videomem <= 128) {
numlines = 1200;
} else if (videomem <= 256) {
numlines = 1280;
} else {
numlines = 1600;
}
int numrows = (int)(numlines * aspect_XdivY);
return G3D::Vector2int16(numrows, numlines);
}
G3D::Vector2int16 View::getCurrentDesktopResolution()
{
G3D::Vector2int16 defaultr(800,600);
if (NULL == hMonitor) {
hMonitor = MonitorFromWindow(GetHWnd(), MONITOR_DEFAULTTONEAREST);
if (!hMonitor)
return defaultr;
}
MONITORINFOEX mi;
mi.cbSize = sizeof(mi);
if (!GetMonitorInfo(hMonitor, &mi)) {
LogManager::ReportEvent(EVENTLOG_ERROR_TYPE,
RBX::format("view::geCurrentDesctopResolution GetMonitorInfo failed, GetLastError returned %d",
GetLastError()).c_str());
return defaultr;
}
DEVMODE dm;
ZeroMemory(&dm, sizeof(dm));
dm.dmSize = sizeof(dm);
DWORD iModeNum = 0;
if (EnumDisplaySettingsEx(mi.szDevice, ENUM_CURRENT_SETTINGS, &dm, 0)!=0) {
return G3D::Vector2int16(dm.dmPelsWidth, dm.dmPelsHeight);
} else {
return defaultr;
}
}
void View::OnResize(WPARAM wParam, int cx, int cy)
{
view->onResize(cx, cy);
}
void View::ShowWindow()
{
// This needs to be done fairly late in initialization. It assumes that it
// is safe to trigger a window resize event, for example.
HWND hWnd = GetHWnd();
if (RBX::GameBasicSettings::singleton().getStartMaximized())
{
::ShowWindow(hWnd, SW_SHOWMAXIMIZED);
}
else
{
// Read window info from xml, if present.
Vector4 startScreenRect(RBX::GameBasicSettings::singleton().getStartScreenPos(), RBX::GameBasicSettings::singleton().getStartScreenSize());
::ShowWindow(hWnd, SW_SHOWNORMAL);
if ( !fullscreen && (startScreenRect != Vector4::zero()) )
{
WINDOWPLACEMENT p = nonFullscreenPlacement;
RECT normalRect;
normalRect.left = startScreenRect.x;
normalRect.top = startScreenRect.y;
normalRect.right = startScreenRect.x + startScreenRect.z;
normalRect.bottom = startScreenRect.y + startScreenRect.w;
p.rcNormalPosition = normalRect;
p.showCmd = SW_SHOWNOACTIVATE;
p.length = sizeof(WINDOWPLACEMENT);
SetWindowPlacement(hWnd, &p);
}
}
// Bring window to foreground and give it focus.
// SetFocus can only do this from specific threads.
marshaller->Submit(boost::bind(&::SetFocusWrapper,hWnd));
// Enable then immediately disable the "always on top" bit to bring
// this window to the foreground (this is more reliable than HWND_TOP).
SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
SetWindowPos(hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
if (RBX::GameBasicSettings::singleton().getFullScreen())
{
SetFullscreen(true);
}
}
void View::unbindWorkspace()
{
shared_ptr<DataModel> dm = getDataModel();
DataModel::LegacyLock lock( dm, DataModelJob::Write);
view->bindWorkspace(boost::shared_ptr<DataModel>());
}
void View::bindWorkspace()
{
shared_ptr<DataModel> dm = getDataModel();
DataModel::LegacyLock lock( dm, DataModelJob::Write);
view->bindWorkspace(game->getDataModel());
view->buildGui();
}
void View::Start(const shared_ptr<Game>& game)
{
RBXASSERT(!this->game);
this->game = game;
bindWorkspace();
initializeJobs();
initializeInput(); // NOTE: have to do this here, Input requires datamodel access
resetScheduler();
// ensure keyboard is in focus (DE6272)
if(userInput)
userInput->setKeyboardDesired(true);
}
void View::Stop()
{
RBXASSERT(this->game);
this->RemoveJobs();
if (game && game->getDataModel())
if (RBX::ControllerService* service = RBX::ServiceProvider::create<RBX::ControllerService>(game->getDataModel().get()))
service->setHardwareDevice(NULL);
if (userInput)
{
userInput->removeJobs();
userInput.reset();
}
unbindWorkspace();
saveWindowSettings();
game.reset();
}
void View::CloseWindow()
{
PostMessage(GetHWnd(), WM_CLOSE, 0, 0);
}
} // namespace RBX
+114
View File
@@ -0,0 +1,114 @@
#pragma once
#include "GfxBase/ViewBase.h"
#include "UserInput.h"
namespace RBX {
// Forward declarations
class FunctionMarshaller;
class Game;
struct OSContext;
class RenderJob;
class ViewBase;
namespace Tasks { class Sequence; }
// Class responsible for the game view
class View
{
public:
View(HWND h);
~View();
void AboutToShutdown();
void Start(const shared_ptr<Game>& game);
void Stop();
void OnResize(WPARAM wParam, int cx, int cy);
void ShowWindow();
void CloseWindow();
void HandleWindowsMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
HWND GetHWnd() const { return static_cast<HWND>(context.hWnd); }
// TODO: refactor verbs so this isn't needed
ViewBase* GetGfxView() const { return view.get(); }
CRenderSettings::GraphicsMode GetLatchedGraphicsMode();
bool IsFullscreen();
void SetFullscreen(bool value);
shared_ptr<DataModel> getDataModel();
private:
// View references, but doesn't own the game
shared_ptr<Game> game;
// The OS context used by the view.
OSContext context;
// Are we currently fullscreen?
bool fullscreen;
// Do we want to become fullscreen at first available opportunity?
bool desireFullscreen;
// When fullscreen this is true if and only if an actual screen resolution change occured
bool changedResolution;
// Enumerated fullscreen sizes. Prone to change as user drags window from
// player to player
std::vector<G3D::Vector2int16> fullScreenSizes;
// The area of the window when not fullscreen (used for exit fullscreen).
WINDOWPLACEMENT nonFullscreenPlacement;
// The style of the window before it went fullscreen
DWORD restoreWindowStyle;
// Are we currently changing resolution?
bool changingResolution;
// Handle to monitor game is running on
HMONITOR hMonitor;
// The view into the game world.
boost::scoped_ptr<RBX::ViewBase> view;
FunctionMarshaller* marshaller;
boost::scoped_ptr<UserInput> userInput;
boost::shared_ptr<Tasks::Sequence> sequence;
boost::shared_ptr<RenderJob> renderJob;
// Window settings
bool windowSettingsValid;
Vector4 windowSettingsRectangle;
bool windowSettingsMaximized;
// Used to enable toggling fullscreen support
void modifyWindow(DWORD argMask, const RECT& area);
bool findBestMonitorMatch(LPCTSTR szDevice, int desiredX, int desiredY, bool resolutionAuto, DEVMODE& dmBest);
void changeResolution();
void restoreResolution();
G3D::Vector2int16 calcDefaultResolution(float aspect_XdivY);
G3D::Vector2int16 getCurrentDesktopResolution();
void initializeSizes();
void bindWorkspace();
void unbindWorkspace();
void initializeView();
void initializeInput();
void resetScheduler();
void initializeJobs();
void RemoveJobs();
void rememberWindowSettings();
void saveWindowSettings();
};
} // namespace RBX
+575
View File
@@ -0,0 +1,575 @@
#include "stdafx.h"
#include "WebBrowserAxDialog.h"
#include <Exdispid.h> // platform SDK header
#include "util/FileSystem.h"
#include "util/Statistics.h"
#include "util/Http.h"
#include "v8datamodel/DataModel.h"
#include "V8DataModel/GameSettings.h"
#include "V8DataModel/GameBasicSettings.h"
#include "v8datamodel/PlayerGui.h"
#include "format_string.h"
static const std::string titlePrefix("<media:title type=\"plain\">");
static const std::string titlePostfix("</media:title>");
WebBrowserAxDialog::WebBrowserAxDialog(const std::string& url, boost::shared_ptr<RBX::DataModel> dataModel, boost::function<void(bool)> enableUpload)
: CAxDialogImpl()
, url(url)
, dataModel(dataModel)
, m_cRef(1)
, siteSEO(false)
, enableUpload(enableUpload)
{
}
WebBrowserAxDialog::WebBrowserAxDialog(const std::string& url, boost::shared_ptr<RBX::DataModel> dataModel)
: CAxDialogImpl()
, url(url)
, dataModel(dataModel)
, m_cRef(1)
, siteSEO(false)
{
}
HRESULT WebBrowserAxDialog::QueryInterface(REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject)
{
if (IID_IUnknown == riid)
{
*ppvObject = (LPUNKNOWN)(IDispatch*)this;
AddRef();
return S_OK;
}
else if (IID_IDispatch == riid)
{
*ppvObject = (IDispatch*)this;
AddRef();
return S_OK;
}
else
{
return E_NOTIMPL;
}
}
ULONG WebBrowserAxDialog::AddRef(void)
{
return InterlockedIncrement(&m_cRef);
}
ULONG WebBrowserAxDialog::Release(void)
{
return InterlockedDecrement(&m_cRef);
}
HRESULT WebBrowserAxDialog::ShowContextMenu(DWORD dwID, POINT *ppt, IUnknown *pcmdtReserved, IDispatch *pdispReserved)
{
return E_NOTIMPL;
}
#define ROBLOX_BROWSERFLAGS \
DOCHOSTUIFLAG_DISABLE_HELP_MENU |\
DOCHOSTUIFLAG_ENABLE_FORMS_AUTOCOMPLETE |\
DOCHOSTUIFLAG_THEME |\
DOCHOSTUIFLAG_DISABLE_SCRIPT_INACTIVE |\
DOCHOSTUIFLAG_LOCAL_MACHINE_ACCESS_CHECK |\
DOCHOSTUIFLAG_DISABLE_UNTRUSTEDPROTOCOL |\
0
HRESULT WebBrowserAxDialog::GetHostInfo(DOCHOSTUIINFO *pInfo)
{
pInfo->dwFlags |=
DOCHOSTUIFLAG_NO3DBORDER |
ROBLOX_BROWSERFLAGS |
0;
return S_OK;
}
HRESULT WebBrowserAxDialog::ShowUI(DWORD dwID, IOleInPlaceActiveObject *pActiveObject, IOleCommandTarget *pCommandTarget, IOleInPlaceFrame *pFrame, IOleInPlaceUIWindow *pDoc)
{
return E_NOTIMPL;
}
HRESULT WebBrowserAxDialog::HideUI(void)
{
return S_OK;
}
HRESULT WebBrowserAxDialog::UpdateUI(void)
{
return E_NOTIMPL;
}
HRESULT WebBrowserAxDialog::EnableModeless(BOOL fEnable)
{
return E_NOTIMPL;
}
HRESULT WebBrowserAxDialog::OnDocWindowActivate(BOOL fActivate)
{
return E_NOTIMPL;
}
HRESULT WebBrowserAxDialog::OnFrameWindowActivate(BOOL fActivate)
{
return E_NOTIMPL;
}
HRESULT WebBrowserAxDialog::ResizeBorder(LPCRECT prcBorder, IOleInPlaceUIWindow *pUIWindow, BOOL fRameWindow)
{
return E_NOTIMPL;
}
HRESULT WebBrowserAxDialog::TranslateAccelerator(LPMSG lpMsg, const GUID *pguidCmdGroup, DWORD nCmdID)
{
return E_NOTIMPL;
}
HRESULT WebBrowserAxDialog::GetOptionKeyPath(LPOLESTR *pchKey, DWORD dw)
{
return E_NOTIMPL;
}
HRESULT WebBrowserAxDialog::GetDropTarget(IDropTarget *pDropTarget, IDropTarget **ppDropTarget)
{
return E_NOTIMPL;
}
HRESULT WebBrowserAxDialog::GetExternal(IDispatch **ppDispatch)
{
*ppDispatch = this;
return S_OK;
}
HRESULT WebBrowserAxDialog::TranslateUrl(DWORD dwTranslate, OLECHAR *pchURLIn, OLECHAR **ppchURLOut)
{
return E_NOTIMPL;
}
HRESULT WebBrowserAxDialog::FilterDataObject(IDataObject *pDO, IDataObject **ppDORet)
{
return E_NOTIMPL;
}
HRESULT WebBrowserAxDialog::GetTypeInfoCount(UINT* pctinfo)
{
if (pctinfo == NULL)
return E_POINTER;
*pctinfo = 1;
return S_OK;
}
HRESULT WebBrowserAxDialog::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
{
return S_OK;
}
HRESULT WebBrowserAxDialog::GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId)
{
if (wcscmp(*rgszNames, L"CheckAppHost") == 0)
{
rgDispId[0] = 0;
}
else if (wcscmp(*rgszNames, L"AppHostOpenVideoFolder") == 0)
{
rgDispId[0] = 1;
}
else if (wcscmp(*rgszNames, L"AppHostUploadVideo") == 0)
{
rgDispId[0] = 2;
}
else if (wcscmp(*rgszNames, L"AppHostOpenPicFolder") == 0)
{
rgDispId[0] = 3;
}
else if (wcscmp(*rgszNames, L"AppHostPostImage") == 0)
{
rgDispId[0] = 4;
}
return S_OK;
}
HRESULT WebBrowserAxDialog::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS __RPC_FAR *pDispParams, VARIANT __RPC_FAR *pVarResult, EXCEPINFO __RPC_FAR *pExcepInfo, UINT __RPC_FAR *puArgErr)
{
if (wFlags & DISPATCH_METHOD)
{
if (dispIdMember == 1)
{
ShellExecuteW(NULL, L"open", RBX::FileSystem::getUserDirectory(true, RBX::DirVideo).native().c_str(), NULL, NULL, SW_SHOWNORMAL);
}
else if (dispIdMember == 2)
{
BSTR title_ = pDispParams->rgvarg[0].bstrVal;
SHORT postSetting = pDispParams->rgvarg[1].iVal;
SHORT doPost = pDispParams->rgvarg[2].iVal;
BSTR token_ = pDispParams->rgvarg[3].bstrVal;
std::string tile = convert_w2s(title_);
std::string token = convert_w2s(token_);
UploadVideo(token, doPost, postSetting, tile);
}
else if (dispIdMember == 3)
{
ShellExecuteW(NULL, L"open", RBX::FileSystem::getUserDirectory(true, RBX::DirPicture).native().c_str(), NULL, NULL, SW_SHOWNORMAL);
}
else if (dispIdMember == 4)
{
// This should only happen when the user clicks the "Do not show this window again" button
RBX::GameSettings::singleton().setPostImageSetting(RBX::GameSettings::NEVER);
}
}
return S_OK;
}
static void PostImageFinished(std::string *response, std::exception *ex, weak_ptr<RBX::DataModel> weakDataModel)
{
if(shared_ptr<RBX::DataModel> dataModel = weakDataModel.lock())
{
if ((ex == NULL) && (response->compare("ok") == 0))
{
dataModel->submitTask(boost::bind(&RBX::DataModel::ShowMessage, weak_ptr<RBX::DataModel>(dataModel), 1, "Image uploaded to Facebook", 2), RBX::DataModelJob::Write);
}
else
{
dataModel->submitTask(boost::bind(&RBX::DataModel::ShowMessage, weak_ptr<RBX::DataModel>(dataModel), 1, "Failed to upload image", 2), RBX::DataModelJob::Write);
RBX::GameSettings::singleton().setPostImageSetting(RBX::GameSettings::ASK);
}
dataModel->submitTask(boost::bind(&RBX::DataModel::ScreenshotUploadTask, weak_ptr<RBX::DataModel>(dataModel), true), RBX::DataModelJob::Write);
}
}
void WebBrowserAxDialog::DoPostImage(std::string filename, std::string seostr)
{
try
{
std::string url = RBX::format("%s/UploadMedia/DoPostImage.ashx?from=client", ::GetBaseURL().c_str());
RBX::Http http(url);
// in case the seo info contains nothing but whitespaces, add a line break to prevent facebook from returning errors
http.additionalHeaders[seostr] = seostr + "%0D%0A";
shared_ptr<std::ifstream> in(new std::ifstream);
in->open(filename.c_str(), std::ios::binary);
if (in->fail())
{
dataModel->submitTask(boost::bind(&RBX::DataModel::ShowMessage, weak_ptr<RBX::DataModel>(dataModel), 1, "Failed to upload image", 2), RBX::DataModelJob::Write);
}
else
{
dataModel->submitTask(boost::bind(&RBX::DataModel::ShowMessage, weak_ptr<RBX::DataModel>(dataModel), 1, "Uploading image ...", 0), RBX::DataModelJob::Write);
dataModel->submitTask(boost::bind(&RBX::DataModel::ScreenshotUploadTask, weak_ptr<RBX::DataModel>(dataModel), false), RBX::DataModelJob::Write);
http.post(in, RBX::Http::kContentTypeDefaultUnspecified, false,
boost::bind(&PostImageFinished, _1, _2, weak_ptr<RBX::DataModel>(dataModel)));
}
}
catch (...)
{
dataModel->submitTask(boost::bind(&RBX::DataModel::ShowMessage, weak_ptr<RBX::DataModel>(dataModel), 1, "Failed to upload image", 2), RBX::DataModelJob::Write);
dataModel->submitTask(boost::bind(&RBX::DataModel::ScreenshotUploadTask, weak_ptr<RBX::DataModel>(dataModel), true), RBX::DataModelJob::Write);
}
}
DWORD ThreadDoUploadVideo(shared_ptr<RBX::DataModel> dataModel, bool siteSEO, std::string videoTitle, std::string videoSEOInfo, std::string fileName, std::string youtubeToken, boost::function<void(bool)> enableUpload)
{
shared_ptr<RBX::CoreGuiService> coreGuiService;
if(dataModel)
coreGuiService = shared_from(dataModel->find<RBX::CoreGuiService>());
if(coreGuiService)
{
dataModel->submitTask(boost::bind(&RBX::CoreGuiService::displayOnScreenMessage, coreGuiService, 1, "Uploading video ...", 0), RBX::DataModelJob::Write);
}
RBX::Http http(RBX::format("http://uploads.gdata.youtube.com/feeds/api/users/default/uploads"));
try{
std::string request1;
if (!siteSEO)
{
std::string requestTmp(
"--f93dcbA3\r\n"
"Content-Type: application/atom+xml; charset=UTF-8\r\n"
"\r\n"
"<?xml version=\"1.0\"?>\r\n"
"<entry xmlns=\"http://www.w3.org/2005/Atom\"\r\n"
"xmlns:media=\"http://search.yahoo.com/mrss/\"\r\n"
"xmlns:yt=\"http://gdata.youtube.com/schemas/2007\">\r\n"
"<media:group>\r\n"
"<media:title type=\"plain\">" + videoTitle + "</media:title>\r\n"
"<media:description type=\"plain\">\r\n"
"" + videoSEOInfo + "\r\n"
"For more games visit http://www.roblox.com\r\n"
"</media:description>\r\n"
"<media:category\r\n"
"scheme=\"http://gdata.youtube.com/schemas/2007/categories.cat\">Games\r\n"
"</media:category>\r\n"
"<media:keywords>ROBLOX, video, free game, online virtual world</media:keywords>\r\n"
"</media:group>\r\n"
"</entry>\r\n"
"--f93dcbA3\r\n"
"Content-Type: video/avi\r\n"
"Content-Transfer-Encoding: binary\r\n"
"\r\n"
);
request1 = requestTmp;
}
else
{
int s = videoSEOInfo.find(titlePrefix) + titlePrefix.length();
int e = videoSEOInfo.find(titlePostfix);
videoSEOInfo = videoSEOInfo.replace(s, e - s, videoTitle);
request1 = RBX::format("--f93dcbA3\r\n"
"Content-Type: application/atom+xml; charset=UTF-8\r\n"
"\r\n"
"%s\r\n"
"--f93dcbA3\r\n"
"Content-Type: video/avi\r\n"
"Content-Transfer-Encoding: binary\r\n"
"\r\n", videoSEOInfo.c_str());
}
std::stringstream buffer;
buffer << request1;
RBXASSERT(!fileName.empty());
std::ifstream file(fileName.c_str(), std::ios::in | std::ios::binary);
if ( file.is_open() )
{
buffer << file.rdbuf();
file.close();
}
else
{
if(coreGuiService)
dataModel->submitTask(boost::bind(&RBX::CoreGuiService::displayOnScreenMessage, coreGuiService, 1, "Failed to upload video", 2), RBX::DataModelJob::Write);
enableUpload(true);
return 1;
}
std::string request2("\r\n--f93dcbA3--\r\n");
buffer << request2;
buffer << '\0';
http.additionalHeaders["Authorization"] = "AuthSub token=\"" + youtubeToken + "\"";
http.additionalHeaders["GData-Version"] = "2";
http.additionalHeaders["X-GData-Key"] = "key=AI39si5sZKe6qAobFgnT9UFGXq9bBO7mUCsK3_cWy_LJmgKDtl-GOMHNNV_Bh7Jk7KqDX7vI8D30jFHwnu8RJcDmcJN47yPW7A";
http.additionalHeaders["Slug"] = "roblox.avi";
http.additionalHeaders["Connection"] = "close";
http.additionalHeaders["Content-Length"] = RBX::format("%d", buffer.str().length());
std::string response;
http.post(buffer, "multipart/related; boundary=\"f93dcbA3\"", false, response);
// Check the response to see if the upload succeeded.
// TODO: better way of checking if the upload succeeded from youtube?
int pos = response.find("videoid");
if(coreGuiService){
if (pos == std::string::npos){
dataModel->submitTask(boost::bind(&RBX::CoreGuiService::displayOnScreenMessage, coreGuiService, 1, "Failed to upload video", 2), RBX::DataModelJob::Write);
}
else{
dataModel->submitTask(boost::bind(&RBX::CoreGuiService::displayOnScreenMessage, coreGuiService, 1, "Video uploaded to YouTube", 2), RBX::DataModelJob::Write);;
}
}
}
catch (...) {
if(coreGuiService)
dataModel->submitTask(boost::bind(&RBX::CoreGuiService::displayOnScreenMessage, coreGuiService, 1, "Failed to upload video", 2), RBX::DataModelJob::Write);
}
enableUpload(true);
return 1;
}
void WebBrowserAxDialog::DoUploadVideo(std::string token, std::string title, std::string seostr, int placeId)
{
std::string titelString = title;
if (seostr.empty())
{
siteSEO = false;
if (placeId > 0)
{
videoSEOInfo = format_string("To play this game, please visit: http://www.roblox.com/item.aspx?id=%d&amp;rbx_source=youtube&amp;rbx_medium=uservideo", placeId);
} else {
videoSEOInfo = seostr;
}
}
else
{
videoSEOInfo = seostr;
siteSEO = true;
}
videoTitle = titelString;
if (videoTitle.length() == 0)
videoTitle = "ROBLOX ROCKS!";
youtubeToken = token;
enableUpload(false);
boost::thread(boost::bind(ThreadDoUploadVideo, dataModel, siteSEO, videoTitle, videoSEOInfo, fileName, youtubeToken, enableUpload));
}
void WebBrowserAxDialog::UploadVideo(std::string token, SHORT doPost, SHORT postSetting, std::string title)
{
RBX::GameBasicSettings::singleton().setUploadVideoSetting((RBX::GameSettings::UploadSetting)postSetting);
if (doPost == 1) {
int placeId = dataModel->getPlaceID();
if (dataModel->isVideoSEOInfoSet())
{
DoUploadVideo(token, title, dataModel->getVideoSEOInfo(), placeId);
}
else
{
DoUploadVideo(token, title, "", placeId);
}
}
}
LRESULT WebBrowserAxDialog::OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
CenterWindow();
m_hIcon = ::LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_WINDOW_ICON));
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
m_events.SetParent(this);
SHDocVw::IWebBrowserAppPtr pWebBrowser = NULL;
HRESULT hr = GetDlgControl(IDC_EXPLORER1, __uuidof(SHDocVw::IWebBrowserAppPtr), (void**)&pWebBrowser);
if (SUCCEEDED(hr)) {
CComQIPtr<IConnectionPointContainer, &IID_IConnectionPointContainer> cpc(pWebBrowser);
CComPtr<IConnectionPoint> cp1;
hr = cpc->FindConnectionPoint(__uuidof(SHDocVw::DWebBrowserEventsPtr), &cp1);
DWORD dwCookie;
hr = cp1->Advise((LPUNKNOWN)&m_events, &dwCookie);
CComPtr<IConnectionPoint> cp2;
hr = cpc->FindConnectionPoint(__uuidof(SHDocVw::DWebBrowserEvents2Ptr), &cp2);
hr = cp2->Advise((LPUNKNOWN)&m_events, &dwCookie);
hr = pWebBrowser->Navigate( _bstr_t(url.c_str()) );
} else {
MessageBox("Failed to open web browser", "Error", MB_OK);
return FALSE;
}
return TRUE; // let the system set the focus
}
LRESULT WebBrowserAxDialog::OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
EndDialog(IDCANCEL);
return 0;
}
HRESULT __stdcall DWebBrowserEventsImpl::QueryInterface(REFIID riid, LPVOID* ppv)
{
*ppv = NULL;
if (IID_IUnknown == riid || __uuidof(SHDocVw::DWebBrowserEventsPtr) == riid)
{
*ppv = (LPUNKNOWN)(SHDocVw::DWebBrowserEventsPtr*)this;
AddRef();
return NOERROR;
}
else if (IID_IOleClientSite == riid)
{
*ppv = (IOleClientSite*)this;
AddRef();
return NOERROR;
}
else if (IID_IDispatch == riid)
{
*ppv = (IDispatch*)this;
AddRef();
return NOERROR;
}
else
{
return E_NOTIMPL;
}
}
ULONG __stdcall DWebBrowserEventsImpl::AddRef() { return 1;}
ULONG __stdcall DWebBrowserEventsImpl::Release() { return 0;}
// IDispatch methods
HRESULT __stdcall DWebBrowserEventsImpl::GetTypeInfoCount(UINT* pctinfo)
{
return E_NOTIMPL;
}
HRESULT __stdcall DWebBrowserEventsImpl::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
{
return E_NOTIMPL;
}
HRESULT __stdcall DWebBrowserEventsImpl::GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId)
{
return E_NOTIMPL;
}
HRESULT __stdcall DWebBrowserEventsImpl::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS __RPC_FAR *pDispParams, VARIANT __RPC_FAR *pVarResult, EXCEPINFO __RPC_FAR *pExcepInfo, UINT __RPC_FAR *puArgErr)
{
// proces OnBeforeNavigate
if (dispIdMember == DISPID_BEFORENAVIGATE)
{
BeforeNavigate(_bstr_t(pDispParams->rgvarg[5].bstrVal), 0, _bstr_t(pDispParams->rgvarg[3].bstrVal), NULL, _bstr_t(""), NULL);
}
if (dispIdMember == DISPID_BEFORENAVIGATE2)
{
BeforeNavigate2(_bstr_t(pDispParams->rgvarg[5].bstrVal), 0, _bstr_t(pDispParams->rgvarg[3].bstrVal), NULL, _bstr_t(""), NULL);
}
else if (dispIdMember == DISPID_NAVIGATECOMPLETE)
{
NavigateComplete(_bstr_t(pDispParams->rgvarg[0].bstrVal));
}
else if (dispIdMember == DISPID_WINDOWCLOSING)
{
*((VARIANT_BOOL*)pDispParams->rgvarg[0].byref) = VARIANT_TRUE;
m_cpParent->EndDialog(IDCANCEL);
}
return NOERROR;
}
// Methods:
HRESULT DWebBrowserEventsImpl::BeforeNavigate(_bstr_t URL, long Flags, _bstr_t TargetFrameName, VARIANT * PostData, _bstr_t Headers, VARIANT_BOOL * Cancel)
{
return S_OK;
}
HRESULT DWebBrowserEventsImpl::BeforeNavigate2(_bstr_t URL, long Flags, _bstr_t TargetFrameName, VARIANT * PostData, _bstr_t Headers, VARIANT_BOOL * Cancel)
{
std::string ulrStr = convert_w2s(std::wstring((wchar_t*)URL));
return RBX::Http::trustCheckBrowser(ulrStr.c_str()) ? S_OK : E_FAIL;
}
HRESULT DWebBrowserEventsImpl::NavigateComplete(_bstr_t URL)
{
SHDocVw::IWebBrowserAppPtr pWebBrowser = NULL;
HRESULT hr = m_cpParent->GetDlgControl(IDC_EXPLORER1, __uuidof(SHDocVw::IWebBrowserAppPtr), (void**)&pWebBrowser);
CComPtr<IDispatch> spDoc;
hr = pWebBrowser->get_Document(&spDoc);
CComPtr<ICustomDoc> customDoc = CComQIPtr<ICustomDoc>(spDoc);
customDoc->SetUIHandler(m_cpParent);
return S_OK;
}
+103
View File
@@ -0,0 +1,103 @@
#pragma once
#include "resource.h"
#include <atlcom.h>
#include <Exdispid.h> // platform SDK header
#include "v8datamodel/DataModel.h"
#include "boost/shared_ptr.hpp"
#include "boost/function.hpp"
class WebBrowserAxDialog; // forward declaration
class DWebBrowserEventsImpl : public DWebBrowserEvents
{
// IUnknown methods
STDMETHOD(QueryInterface)(REFIID riid, LPVOID* ppv);
STDMETHOD_(ULONG, AddRef)();
STDMETHOD_(ULONG, Release)();
// IDispatch methods
STDMETHOD(GetTypeInfoCount)(UINT* pctinfo);
STDMETHOD(GetTypeInfo)(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo);
STDMETHOD(GetIDsOfNames)(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId);
STDMETHOD(Invoke)(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS __RPC_FAR *pDispParams, VARIANT __RPC_FAR *pVarResult, EXCEPINFO __RPC_FAR *pExcepInfo, UINT __RPC_FAR *puArgErr);
// Methods:
HRESULT BeforeNavigate(_bstr_t URL, long Flags, _bstr_t TargetFrameName, VARIANT * PostData, _bstr_t Headers, VARIANT_BOOL * Cancel);
HRESULT BeforeNavigate2(_bstr_t URL, long Flags, _bstr_t TargetFrameName, VARIANT * PostData, _bstr_t Headers, VARIANT_BOOL * Cancel);
HRESULT NavigateComplete(_bstr_t URL);
// members
WebBrowserAxDialog *m_cpParent; // any time a IWebBrowser instance is needed
public:
void SetParent(WebBrowserAxDialog *pParent) { m_cpParent = pParent; }
};
class WebBrowserAxDialog :
public CAxDialogImpl<WebBrowserAxDialog>,
public IDocHostUIHandler,
public IDispatch
{
ULONG m_cRef;
HICON m_hIcon;
std::string url;
boost::shared_ptr<RBX::DataModel> dataModel;
boost::function<void(bool)> enableUpload;
void DoPostImage(std::string filename, std::string seostr);
void UploadVideo(std::string token, SHORT doPost, SHORT postSetting, std::string title);
void DoUploadVideo(std::string token, std::string title, std::string seostr, int placeId);
//video upload data
std::string videoSEOInfo;
bool siteSEO;
std::string videoTitle;
std::string youtubeToken;
//captured video file name
std::string fileName;
DWebBrowserEventsImpl m_events;
public:
enum { IDD = IDD_UPLOADVIDEODIALOG };
BEGIN_MSG_MAP(WebBrowserAxDialog)
MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
MESSAGE_HANDLER(WM_CLOSE, OnClose)
END_MSG_MAP()
WebBrowserAxDialog(const std::string& url, boost::shared_ptr<RBX::DataModel> dataModel, boost::function<void(bool)> enableUpload);
WebBrowserAxDialog(const std::string& url, boost::shared_ptr<RBX::DataModel> dataModel);
LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
LRESULT OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
void SetFileName(std::string file) { fileName = file;}
// IUnknown
STDMETHOD(QueryInterface(REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject));
ULONG STDMETHODCALLTYPE AddRef(void);
ULONG STDMETHODCALLTYPE Release(void);
// IDispatch methods
STDMETHOD(GetTypeInfoCount)(UINT* pctinfo);
STDMETHOD(GetTypeInfo)(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo);
STDMETHOD(GetIDsOfNames)(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId);
STDMETHOD(Invoke)(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS __RPC_FAR *pDispParams, VARIANT __RPC_FAR *pVarResult, EXCEPINFO __RPC_FAR *pExcepInfo, UINT __RPC_FAR *puArgErr);
// IDocHostUIHandler
STDMETHOD(ShowContextMenu(DWORD dwID, POINT *ppt, IUnknown *pcmdtReserved, IDispatch *pdispReserved));
STDMETHOD(GetHostInfo(DOCHOSTUIINFO *pInfo));
STDMETHOD(ShowUI(DWORD dwID, IOleInPlaceActiveObject *pActiveObject, IOleCommandTarget *pCommandTarget, IOleInPlaceFrame *pFrame, IOleInPlaceUIWindow *pDoc));
STDMETHOD(HideUI(void));
STDMETHOD(UpdateUI(void));
STDMETHOD(EnableModeless(BOOL fEnable));
STDMETHOD(OnDocWindowActivate(BOOL fActivate));
STDMETHOD(OnFrameWindowActivate(BOOL fActivate));
STDMETHOD(ResizeBorder(LPCRECT prcBorder, IOleInPlaceUIWindow *pUIWindow, BOOL fRameWindow));
STDMETHOD(TranslateAccelerator(LPMSG lpMsg, const GUID *pguidCmdGroup, DWORD nCmdID));
STDMETHOD(GetOptionKeyPath(LPOLESTR *pchKey, DWORD dw));
STDMETHOD(GetDropTarget(IDropTarget *pDropTarget, IDropTarget **ppDropTarget));
STDMETHOD(GetExternal(IDispatch **ppDispatch));
STDMETHOD(TranslateUrl(DWORD dwTranslate, OLECHAR *pchURLIn, OLECHAR **ppchURLOut));
STDMETHOD(FilterDataObject(IDataObject *pDO, IDataObject **ppDORet));
};
+235
View File
@@ -0,0 +1,235 @@
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (United States) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_WINDOW_ICON ICON "Roblox.ico"
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 0,2,0,0
PRODUCTVERSION 0,2,0,0
FILEFLAGSMASK 0x17L
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x4L
FILETYPE 0x1L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "ROBLOX Corporation"
VALUE "FileDescription", "ROBLOX Game Client"
VALUE "FileVersion", "0.2.0.0"
VALUE "InternalName", "RobloxApp.exe"
VALUE "LegalCopyright", " 2013, ROBLOX Corporation. All rights reserved."
VALUE "OriginalFilename", "RobloxApp.exe"
VALUE "ProductName", "ROBLOX"
VALUE "ProductVersion", "0.2.0.0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END
/////////////////////////////////////////////////////////////////////////////
//
// Cursor
//
IDR_INVISIBLECURSOR CURSOR "InvisibleCursor.cur"
/////////////////////////////////////////////////////////////////////////////
//
// Accelerator
//
IDR_GAME_ACCELERATOR ACCELERATORS
BEGIN
VK_F1, ID_LOADWIKI, VIRTKEY, NOINVERT
VK_F8, ID_UPLOADSESSIONLOGS, VIRTKEY, SHIFT, NOINVERT
END
/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//
IDD_UPLOADVIDEODIALOG DIALOGEX 0, 0, 650, 265
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "ROBLOX"
FONT 8, "MS Shell Dlg", 0, 0, 0x0
BEGIN
CONTROL "",IDC_EXPLORER1,"{8856F961-340A-11D0-A96B-00C04FD705A2}",WS_TABSTOP,0,0,649,265
END
IDD_RBXWEBVIEW DIALOGEX 0, 0, 741, 394
STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
CAPTION "ROBLOX"
FONT 8, "MS Shell Dlg", 400, 0, 0x0
BEGIN
CONTROL "",IDC_RBXEXPLORER,
"{8856F961-340A-11D0-A96B-00C04FD705A2}",WS_TABSTOP,0,0,740,394
END
/////////////////////////////////////////////////////////////////////////////
//
// HTML
//
IDR_HTML_CONTACTINGSERVER HTML "html_con.htm"
/////////////////////////////////////////////////////////////////////////////
//
// Dialog Info
//
IDD_UPLOADVIDEODIALOG DLGINIT
BEGIN
IDC_EXPLORER1, 0x376, 160, 0
0x0000, 0x0000, 0x004c, 0x0000, 0x64aa, 0x0000, 0x2c8c, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x004c, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001,
0x0000, 0xd0e0, 0x0057, 0x3573, 0x11cf, 0x69ae, 0x0008, 0x2e2b, 0x6212,
0x0000, 0x0000, 0x0000, 0x0000, 0x004c, 0x0000, 0x1401, 0x0002, 0x0000,
0x0000, 0x00c0, 0x0000, 0x0000, 0x4600, 0x0080, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0
END
IDD_RBXWEBVIEW DLGINIT
BEGIN
IDC_RBXEXPLORER, 0x376, 160, 0
0x0000, 0x0000, 0x004c, 0x0000, 0x72b9, 0x0000, 0x4225, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x004c, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001,
0x0000, 0xd0e0, 0x0057, 0x3573, 0x11cf, 0x69ae, 0x0008, 0x2e2b, 0x6212,
0x000c, 0x0000, 0x0000, 0x0000, 0x004c, 0x0000, 0x1401, 0x0002, 0x0000,
0x0000, 0x00c0, 0x0000, 0x0000, 0x4600, 0x0080, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0
END
/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//
#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO
BEGIN
IDD_UPLOADVIDEODIALOG, DIALOG
BEGIN
END
IDD_RBXWEBVIEW, DIALOG
BEGIN
RIGHTMARGIN, 740
END
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// String Table
//
STRINGTABLE
BEGIN
IDS_APP_TITLE "watrbx"
IDC_WINDOWSCLIENT "WINDOWSCLIENT"
IDS_ERROR_REPORT_PROMPT "ROBLOX crashed on your computer recently! We would like to find out why so that we can make ROBLOX better.\n\nMay ROBLOX send an error report?"
END
STRINGTABLE
BEGIN
IDS_DEFAULT_IMAGE_INFO "A screenshot from Roblox. Learn more at http://www.watrbx.wtf"
END
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED
File diff suppressed because it is too large Load Diff
+294
View File
@@ -0,0 +1,294 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PublishDLLDependency Include="..\VMProtect\VMProtectSDK32.dll">
<Filter>Libraries</Filter>
</PublishDLLDependency>
<PublishDLLDependency Include="..\RbxDebug\bin\Debug\RbxDebug.dll">
<Filter>Libraries\Debug</Filter>
</PublishDLLDependency>
<PublishDLLDependency Include="..\RbxDebug\bin\Debug\RbxDebug.pdb">
<Filter>Libraries\Debug</Filter>
</PublishDLLDependency>
<PublishDLLDependency Include="..\Rendering\ShaderCompiler\d3dcompiler_47.dll">
<Filter>Libraries</Filter>
</PublishDLLDependency>
<PublishDLLDependency Include="..\fmod\win32\fmod.dll">
<Filter>Libraries</Filter>
</PublishDLLDependency>
<PublishDLLDependency Include="..\SDL2\Win\2.0.4\SDL2.dll">
<Filter>Libraries\SDL2.0.4</Filter>
</PublishDLLDependency>
<PublishDLLDependency Include="..\SDL2\Win\2.0.4\SDL2.pdb">
<Filter>Libraries\SDL2.0.4</Filter>
</PublishDLLDependency>
<PublishDLLDependency Include="..\Rendering\OpenVR\bin\win32\openvr_api.dll" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="Application.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\Win\AuthenticationMarshallar.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Crypt.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\ClientShared\DataModelEmptySerialize.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Document.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\Win\DSVideoCaptureEngine.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\Win\DumpErrorUploader.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\Win\ErrorUploader.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="FunctionMarshaller.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="GameVerbs.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\Win\LogManager.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\ClientBase\MachineConfiguration.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\ClientBase\ReflectionMetadata.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="RenderJob.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\ClientBase\RenderSettingsItem.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="stdafx.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\Win\Tracer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Teleporter.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="UserInput.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\Win\UserInputUtil.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\Win\VersionInfo.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\Win\VideoControl.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="View.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\Win\VistaTools.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="WebBrowserAxDialog.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="RbxWebView.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\App\script\LuaVMClient.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="functionHooks.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="robloxHooks.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\ClientShared\SDLGameController.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="RandomPadding.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ReleasePatcher.cpp" />
<ClCompile Include="$(CONTRIB_PATH)\boost_1_56_0\libs\program_options\src\cmdline.cpp" />
<ClCompile Include="$(CONTRIB_PATH)\boost_1_56_0\libs\program_options\src\config_file.cpp" />
<ClCompile Include="$(CONTRIB_PATH)\boost_1_56_0\libs\program_options\src\convert.cpp" />
<ClCompile Include="$(CONTRIB_PATH)\boost_1_56_0\libs\program_options\src\options_description.cpp" />
<ClCompile Include="$(CONTRIB_PATH)\boost_1_56_0\libs\program_options\src\parsers.cpp" />
<ClCompile Include="$(CONTRIB_PATH)\boost_1_56_0\libs\program_options\src\positional_options.cpp" />
<ClCompile Include="$(CONTRIB_PATH)\boost_1_56_0\libs\program_options\src\split.cpp" />
<ClCompile Include="$(CONTRIB_PATH)\boost_1_56_0\libs\program_options\src\utf8_codecvt_facet.cpp" />
<ClCompile Include="$(CONTRIB_PATH)\boost_1_56_0\libs\program_options\src\value_semantic.cpp" />
<ClCompile Include="$(CONTRIB_PATH)\boost_1_56_0\libs\program_options\src\variables_map.cpp" />
<ClCompile Include="$(CONTRIB_PATH)\boost_1_56_0\libs\program_options\src\winmain.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="Application.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\Win\AuthenticationMarshallar.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Crypt.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\Win\dinput.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Document.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\Win\DSVideoCaptureEngine.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\Win\DumpErrorUploader.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\Win\ErrorUploader.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="FunctionMarshaller.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="GameVerbs.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\Win\LogManager.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="RenderJob.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\ClientBase\RenderSettingsItem.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Teleporter.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="UserInput.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\Win\UserInputUtil.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\Win\VideoControl.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\Win\VersionInfo.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="View.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\Win\VistaTools.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="stdafx.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="resource.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="RbxWebView.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="RobloxHooks.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="functionHooks.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\ClientShared\SDLGameController.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ReleasePatcher.h" />
<ClInclude Include="discord_rpc.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="AppSettings.xml">
<Filter>Dev Files</Filter>
</CustomBuild>
<CustomBuild Include="..\ClientBase\ReflectionMetadata.xml">
<Filter>Dev Files</Filter>
</CustomBuild>
</ItemGroup>
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{bf4db90a-e1d5-40f5-abbe-263eae5cf15b}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{d2e108e9-3076-4039-b347-9e02753167f8}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\boost">
<UniqueIdentifier>{d86f7e4c-a526-4b13-82b2-3d86e72f0717}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\boost\program_options">
<UniqueIdentifier>{ba1269d8-1845-4dc5-9a54-23ec9ba67726}</UniqueIdentifier>
</Filter>
<Filter Include="Dev Files">
<UniqueIdentifier>{8e868540-f968-48b1-bc59-356ca2df0496}</UniqueIdentifier>
</Filter>
<Filter Include="Libraries">
<UniqueIdentifier>{bd30601f-c277-4b23-87a6-bc37eb7f6809}</UniqueIdentifier>
</Filter>
<Filter Include="Libraries\Boost">
<UniqueIdentifier>{46e179b5-7746-4216-adab-0fb57f87d6a6}</UniqueIdentifier>
</Filter>
<Filter Include="Libraries\Boost\Debug">
<UniqueIdentifier>{e116ad4c-4648-4cbe-b614-d1ae8e81c8e2}</UniqueIdentifier>
</Filter>
<Filter Include="Libraries\Boost\Release">
<UniqueIdentifier>{a7bb90b2-935a-4353-9191-05f4201f913e}</UniqueIdentifier>
</Filter>
<Filter Include="Libraries\Debug">
<UniqueIdentifier>{2808a1d4-3b0b-4bc8-94ca-f37357995d15}</UniqueIdentifier>
</Filter>
<Filter Include="Libraries\Release">
<UniqueIdentifier>{5d0208f1-e2e6-46be-a460-66686c5c37aa}</UniqueIdentifier>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{69cae9c7-1da1-4bce-98cf-1f048cb42f27}</UniqueIdentifier>
</Filter>
<Filter Include="Libraries\SDL2.0.4">
<UniqueIdentifier>{9af36c0e-206f-4757-b94b-125d58bae25f}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<None Include="InvisibleCursor.cur">
<Filter>Resource Files</Filter>
</None>
<None Include="html_con.htm" />
</ItemGroup>
<ItemGroup>
<Image Include="Roblox.ico">
<Filter>Resource Files</Filter>
</Image>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="WindowsClient.rc">
<Filter>Resource Files</Filter>
</ResourceCompile>
</ItemGroup>
<ItemGroup>
<Text Include="ReadMe.txt" />
</ItemGroup>
<ItemGroup>
<Library Include="..\SDL2\Win\2.0.4\SDL2.lib">
<Filter>Libraries\SDL2.0.4</Filter>
</Library>
</ItemGroup>
</Project>
+140
View File
@@ -0,0 +1,140 @@
#include "stdafx.h"
#include "functionHooks.h"
#include "VMProtectSDK.h"
#include <windows.h>
#include <psapi.h>
namespace{
const DWORD kLenHotpatchNop = 2;
const WORD kHotpatchNop = 0xFF8B; // mov edi,edi (little endian)
const DWORD kLenLongJumpWin32 = 5;
const DWORD kLenPushByte = 2;
const DWORD kLenPushDword = 5;
const BYTE kHotpatchJmp = 0xF9; // -6 + 1 = 5
const BYTE kNop = 0x90;
const BYTE kInt3 = 0xCC;
const BYTE kJmp8 = 0xEB;
const BYTE kJmp32 = 0xE9;
const BYTE kPushByte = 0x6A;
const BYTE kPushDword = 0x68;
const BYTE kCallDword = 0xE8;
bool hasHotpatchProlog(void* pfn)
{
WORD fnProlog = *(reinterpret_cast<WORD*>(pfn));
if (fnProlog != kHotpatchNop)
{
return 0;
}
for (size_t i = 1; i <= kLenLongJumpWin32; ++i)
{
unsigned char thisPatchByte = *(reinterpret_cast<unsigned char*>(pfn) - i);
if( thisPatchByte != kNop && thisPatchByte != kInt3 )
{
return 0;
}
}
return 1;
}
DWORD getLongJmpArg(void* src, void* dst)
{
return reinterpret_cast<DWORD>(dst)
- (reinterpret_cast<DWORD>(src)+kLenLongJumpWin32);
}
}
namespace RBX
{
// Windows API functions often have a "hotpatch" prolog. Basically either
// CC CC CC CC CC*8B FF ... or 90 90 90 90 90*8B FF ... where * is the entry
// to a function. 8B FF is the two byte NOP "mov edi,edi". The five int3 (CC)
// or nop (90) instructions are just enough for a jmp dword. The original idea
// was to allow patches to fix functions without needing a reboot.
//
// http://blogs.msdn.com/b/oldnewthing/archive/2011/09/21/10214405.aspx
//
void* hotpatchHook(void* origFunction, void* hookFunction)
{
// Can patch?
if (!hasHotpatchProlog(origFunction))
{
return 0;
}
void* pfnResume = reinterpret_cast<void*>(
reinterpret_cast<unsigned char*>(origFunction) + kLenHotpatchNop);
// Must patch
void* pfnWriteEntry = reinterpret_cast<void*>(
reinterpret_cast<unsigned char*>(origFunction) - kLenLongJumpWin32);
unsigned char patchBuffer[7] = {kJmp32, 0x00, 0x00, 0x00, 0x00, kJmp8, kHotpatchJmp};
DWORD relativeAddr = getLongJmpArg(pfnWriteEntry, hookFunction);
memcpy(&patchBuffer[1], &relativeAddr, sizeof(DWORD));
DWORD bytesWritten = 0;
BOOL wpmStatus = WriteProcessMemory(GetCurrentProcess(), pfnWriteEntry, patchBuffer, sizeof(patchBuffer), &bytesWritten);
if (bytesWritten != sizeof(patchBuffer) || !wpmStatus)
{
return 0;
}
return (pfnResume);
}
void* hotpatchUnhook(void* pfn)
{
if (pfn)
{
const unsigned char patchBuffer[7] = {0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x8B, 0xFF};
BYTE* writePfn = reinterpret_cast<BYTE*>(pfn);
writePfn -= sizeof(patchBuffer);
DWORD bytesWritten = 0;
// Change the patch jump to be a nop first.
BOOL wpmStatus = WriteProcessMemory(GetCurrentProcess(), writePfn+5, patchBuffer+5, 2, &bytesWritten);
if (bytesWritten != 2 || !wpmStatus)
{
return 0;
}
// Now change the jump
wpmStatus = WriteProcessMemory(GetCurrentProcess(), writePfn, patchBuffer, 5, &bytesWritten);
if (bytesWritten != 5 || !wpmStatus)
{
return 0;
}
return (writePfn+5);
}
return 0;
}
// If any of these fail, it should be ok to just not enable that security feature.
bool hookingApiHooked()
{
// (Hopefully) get handle to Kernel32
HMODULE kernel32 = GetModuleHandle("Kernel32");
MODULEINFO info;
if (!GetModuleInformation(GetCurrentProcess(), kernel32, &info, sizeof(info)))
{
return true;
}
size_t k32Base = reinterpret_cast<size_t>(info.lpBaseOfDll);
size_t k32Size = info.SizeOfImage;
// is WPM hooked via IAT? (this does the IAT lookup, which points to Kernel32 DLL)
size_t wpmBase = reinterpret_cast<size_t>(&WriteProcessMemory);
if (wpmBase - k32Base >= k32Size)
{
return true;
}
// Does hotpatch exist (the first two bytes better be the hotpatch space)
WORD wpmEntry = *reinterpret_cast<short*>(&WriteProcessMemory);
if (wpmEntry != kHotpatchNop)
{
return true;
}
// It's possible HWBP, or access violation hooks could still be used.
return false;
}
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
namespace RBX {
// Returns the new entry point
void* hotpatchHook(void* origFunction, void* hookFunction);
void* hotpatchUnhook(void* pfn);
bool hookingApiHooked();
}
+13
View File
@@ -0,0 +1,13 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
</head>
<body>
<h2>
ROBLOX is unable to connect to the Internet</h2>
<ul>
<li>Do you have an Internet connection? </li>
<li>Is anti-virus software or a firewall preventing ROBLOX from accessing the Internet?</li>
</ul>
</body>
</html>
+5
View File
@@ -0,0 +1,5 @@
<HTML>
<BODY ID="CSaveToRobloxDialog" BGCOLOR="lightgrey">
<P align="center"><FONT style="BACKGROUND-COLOR: #d3d3d3">Contacting the server...</FONT></P>
</BODY>
</HTML>
+19
View File
@@ -0,0 +1,19 @@
??$useless@$00@@YAXXZ
??$useless@$01@@YAXXZ
??$useless@$02@@YAXXZ
??$useless@$03@@YAXXZ
??$useless@$04@@YAXXZ
??$useless@$05@@YAXXZ
??$useless@$06@@YAXXZ
??$useless@$07@@YAXXZ
??$useless@$08@@YAXXZ
??$useless@$09@@YAXXZ
unusedPadding
_unusedPadding
??$useless@$0A@@@YAXXZ
??$junk@$0P@@@YAXXZ
??$junk@$0M@@@YAXXZ
??$junk@$0L@@@YAXXZ
??$junk@$08@@YAXXZ
??$junk@$04@@YAXXZ
??$junk@$03@@YAXXZ
+177
View File
@@ -0,0 +1,177 @@
#include "stdafx.h"
#include "resource.h"
#include "Application.h"
#include "InitializationError.h"
#include "util/ProgramMemoryChecker.h"
#include "v8datamodel/ContentProvider.h"
#define MAX_LOADSTRING 100
#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600
TCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
RBX::Application* appPtr;
LOGGROUP(HangDetection)
LOGGROUP(RobloxWndInit)
// Processes messages for the main window.
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_TIMER:
// Notify log manager that main thread is still responsive
if (MainLogManager* manager = LogManager::getMainLogManager()) {
FASTLOG(FLog::HangDetection, "WindowsPlayer: timer event fired");
manager->NotifyFGThreadAlive();
}
break;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case ID_UPLOADSESSIONLOGS:
appPtr->UploadSessionLogs();
break;
case ID_LOADWIKI:
appPtr->OnHelp();
break;
}
break;
case WM_GETMINMAXINFO:
RBX::Application::OnGetMinMaxInfo((MINMAXINFO*)lParam);
break;
case WM_KEYDOWN:
case WM_MOUSEMOVE:
case WM_MOUSELEAVE:
case WM_MOUSEWHEEL:
case WM_SETFOCUS:
case WM_KILLFOCUS:
case WM_ACTIVATE:
case WM_ACTIVATEAPP:
case WM_CHAR:
case WM_INPUT:
appPtr->HandleWindowsMessage(message, wParam, lParam);
break;
case WM_DESTROY:
appPtr->AboutToShutdown();
PostQuitMessage(0);
break;
case WM_SIZE:
appPtr->OnResize(wParam, LOWORD(lParam), HIWORD(lParam));
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// Registers the window class.
ATOM RegisterWindowClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINDOW_ICON));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCE(IDC_WINDOWSCLIENT);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_WINDOW_ICON));
return RegisterClassEx(&wcex);
}
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow)
{
// This was compiled with sse2 support. This should be first as any floats
// will cause issues.
if (!G3D::System::hasSSE2())
{
MessageBoxA(NULL, "This platform lacks SSE2 support.", "ROBLOX", MB_OK);
return false;
}
UNREFERENCED_PARAMETER(hPrevInstance);
RBX::Application app;
appPtr = &app;
CComModule comModule; // Needed for ActiveX hosting of IWebBrowser2
if (!app.LoadAppSettings(hInstance))
return FALSE;
if (!app.ParseArguments(lpCmdLine))
return FALSE;
HWND hWnd = NULL;
// need client settings here before we create window
std::string clientSettingsString;
FetchClientSettingsData(CLIENT_APP_SETTINGS_STRING, CLIENT_SETTINGS_API_KEY, &clientSettingsString);
// Apply client settings
LoadClientSettingsFromString(CLIENT_APP_SETTINGS_STRING, clientSettingsString, &RBX::ClientAppSettings::singleton());
TCHAR szTitle[MAX_LOADSTRING];
// initialize global strings
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_WINDOWSCLIENT, szWindowClass, MAX_LOADSTRING);
RegisterWindowClass(hInstance);
// perform application initialization:
hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH, WINDOW_HEIGHT, NULL, NULL, hInstance,
NULL);
if (!hWnd)
return FALSE;
try
{
if (!app.Initialize(hWnd, hInstance))
return FALSE;
}
catch (const RBX::initialization_error& e)
{
const char* const errorMessage = e.what();
FASTLOGS(FLog::RobloxWndInit, "Error during initialization. User message = %s", errorMessage);
MessageBoxA(hWnd, errorMessage, "ROBLOX", MB_OK);
app.AboutToShutdown();
app.Shutdown();
return FALSE;
}
// set a "keep alive" timer that periodically puts a message in the queue
// see message handler for WM_TIMER
SetTimer(hWnd, NULL, 10 * 1000 /*once every ten seconds*/, NULL);
// Only show the window if there isn't a named object to wait for before
// displaying it
ShowWindow(hWnd, SW_HIDE);
UpdateWindow(hWnd);
MSG msg;
// main message loop:
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// at this point, threads will still be around.
DWORD unused;
VirtualProtect(reinterpret_cast<void*>(RBX::Security::rbxVmpBase), RBX::Security::rbxVmpSize, PAGE_EXECUTE_READWRITE, &unused);
app.Shutdown();
return static_cast<int>(msg.wParam);
}
+31
View File
@@ -0,0 +1,31 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by WindowsClient.rc
//
#define IDS_APP_TITLE 101
#define IDC_WINDOWSCLIENT 102
#define IDS_ERROR_REPORT_PROMPT 103
#define IDR_INVISIBLECURSOR 105
#define IDR_HTML_CONTACTINGSERVER 114
#define IDI_ICON1 114
#define IDI_WINDOW_ICON 115
#define IDS_DEFAULT_IMAGE_INFO 147
#define IDD_UPLOADVIDEODIALOG 252
#define IDD_RBXWEBVIEW 253
#define IDR_GAME_ACCELERATOR 255
#define IDC_EXPLORER1 1005
#define IDC_BUTTON1 1007
#define IDC_RBXEXPLORER 1008
#define ID_UPLOADSESSIONLOGS 33042
#define ID_LOADWIKI 40002
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 116
#define _APS_NEXT_COMMAND_VALUE 40004
#define _APS_NEXT_CONTROL_VALUE 1009
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
+200
View File
@@ -0,0 +1,200 @@
#include "stdafx.h"
#include "functionHooks.h"
#include "robloxHooks.h"
#include "v8datamodel/HackDefines.h"
#include "security/FuzzyTokens.h"
#include "security/ApiSecurity.h"
#include "util/CheatEngine.h"
#include "v8datamodel/FastLogSettings.h"
#include "VMProtectSDK.h"
#include <windows.h>
#include <psapi.h>
namespace
{
size_t moduleStart = 0;
size_t moduleSize = 0xFFFFFFFF;
typedef HWND (WINAPI *FindWindowSplice)(LPCTSTR, LPCSTR);
// Can call this function.
FindWindowSplice resumeFindWindow = 0;
// asm stub to run when findWindowA is called.
HWND WINAPI findWindowHook(LPCTSTR className, LPCSTR windowName)
{
size_t returnAddress;
size_t argDiff;
static const size_t kHalf = 1 << 23;
static const size_t kFull = 1 << 24;
VMProtectBeginMutation("35");
returnAddress = reinterpret_cast<size_t>(_ReturnAddress());
argDiff = (reinterpret_cast<size_t>(windowName) - returnAddress);
// This is an attempt at a very conservative check for the window. It looks for
// DLLs that called out ROBLOX by name inside their code and not from some scan.
if (windowName // user passed a windowName
&& ((returnAddress - moduleStart) > moduleSize) // but not us
&& ((argDiff+kHalf) < kFull) // Argument is +-8MB from call location, probably .rdata.
&& (_strnicmp(windowName, "ROBLOX", 6) == 0)) // with roblox as argument
{
RBX::hotpatchUnhook(resumeFindWindow);
RBX::Tokens::simpleToken |= HATE_DLL_INJECTION;
}
VMProtectEnd();
return resumeFindWindow(className,windowName);
}
// This will request a kick if a possible access violation hook is found. This will set
// a flag and allow normal exception handling to occur. If the user was not hacking,
// there will be a crash.
void RtlDispatchExceptionCheck(PEXCEPTION_RECORD exRec, PCONTEXT ctx)
{
const DWORD code = exRec->ExceptionCode;
const DWORD codeStart = RBX::Security::rbxTextBase;
const DWORD codeSize = RBX::Security::rbxTextSize;
if( code == EXCEPTION_ACCESS_VIOLATION)
{
const DWORD avAddr = exRec->ExceptionInformation[1];
if ((avAddr - codeStart) <= codeSize)
{
RBX::Security::setHackFlagVs<LINE_RAND1>(RBX::Security::hackFlag6, HATE_VEH_HOOK);
RBX::Tokens::sendStatsToken.addFlagFast(HATE_VEH_HOOK);
}
}
else if ((code == EXCEPTION_BREAKPOINT) ||
(code == EXCEPTION_SINGLE_STEP) ||
(code == EXCEPTION_ILLEGAL_INSTRUCTION) ||
(code == EXCEPTION_PRIV_INSTRUCTION))
{
const DWORD addr = reinterpret_cast<DWORD>(exRec->ExceptionAddress);
if ((addr - codeStart) <= codeSize)
{
RBX::Security::setHackFlagVs<LINE_RAND1>(RBX::Security::hackFlag6, HATE_VEH_HOOK);
RBX::Tokens::sendStatsToken.addFlagFast(HATE_VEH_HOOK);
}
}
}
bool cmpKiUserExceptionDispatcher(const char* inString)
{
const unsigned char cmpString[26] = {129, 254, 37, 162, 27, 133, 129, 157, 225, 138, 46, 157, 191, 244, 244, 153, 75, 12, 70, 220, 152, 104, 186, 244, 94, 43};
if (!inString) return false;
for (int i = 0; i < 26; ++i)
{
if ((unsigned char)((inString[i]+i)*227) != cmpString[i]) return false;
if (!inString[i]) return (i == 25);
};
return false;
}
const unsigned char kiUserExceptionDispatcherProlog[10] =
{0x8B, 0x4C, 0x24, 0x04, // mov ecx,dword ptr [esp+4]
0x8B, 0x1C, 0x24, // mov ebx,dword ptr [esp]
0x51, // push ecx
0x53, // push ebx
0xE8 /* XX XX XX XX */}; // call ntdll!RtlDisapatchException
}
namespace RBX
{
DWORD* vehHookLocation;
// This will prevent our code from appearing on the callstack of the exception functions.
__declspec(naked) BOOLEAN RtlDispatchExceptionHook(PEXCEPTION_RECORD exRec, PCONTEXT ctx)
{
__asm
{
push ebp;
mov ebp, esp;
sub esp, __LOCAL_SIZE;
push ebx;
push ecx;
}
RtlDispatchExceptionCheck(exRec, ctx);
__asm
{
pop ecx;
pop ebx;
mov esp, ebp;
pop ebp;
cld;
jmp vehHookContinue;
}
}
bool hookPreVeh()
{
volatile bool result = false;
VMProtectBeginMutation(NULL);
HMODULE ntdll = GetModuleHandleA("ntdll");
DWORD* loc = reinterpret_cast<DWORD*>(rbxNtdllProcAddress(ntdll, cmpKiUserExceptionDispatcher));
ntdll = 0;
if (loc)
{
// On win7/8 the prolog is preceeded by "cld", a 1 byte instruction. If the prolog isn't found,
// retry. if it is found on the first try, this is winXp.
bool foundWinXp = true;
if (0 != memcmp(loc, kiUserExceptionDispatcherProlog, sizeof(kiUserExceptionDispatcherProlog)))
{
loc = reinterpret_cast<DWORD*>(reinterpret_cast<BYTE*>(loc) + 1);
foundWinXp = false;
}
if (foundWinXp || (0 == memcmp(loc, kiUserExceptionDispatcherProlog, sizeof(kiUserExceptionDispatcherProlog))))
{
// hook location is the argument to the long call, at eip+1
// it will jump to (eip+5)+arg, which is (loc+4)+*loc
// (eip+5)+arg=pfn, so (loc+4)+arg=pfn, arg = pfn-loc-4
vehHookLocation = reinterpret_cast<DWORD*>(reinterpret_cast<BYTE*>(loc) + sizeof(kiUserExceptionDispatcherProlog));
DWORD newOffset = reinterpret_cast<DWORD>(&RtlDispatchExceptionHook) - reinterpret_cast<DWORD>(vehHookLocation) - 4;
vehHookContinue = reinterpret_cast<void*>(reinterpret_cast<DWORD>(vehHookLocation) + 4 + *vehHookLocation);
DWORD bytesWritten;
BOOL hookResult = WriteProcessMemory(GetCurrentProcess(), vehHookLocation, &newOffset, sizeof(DWORD), &bytesWritten);
if (hookResult && bytesWritten == 4)
{
result = true;
}
else
{
Tokens::apiToken.addFlagSafe(kVehWpmFail);
}
}
else
{
Tokens::apiToken.addFlagSafe(kVehPrologFail);
}
}
else
{
Tokens::apiToken.addFlagSafe(kVehNoNtdll);
}
result = result; // vmprotect workaround.
VMProtectEnd();
return result;
}
void hookApi()
{
if (hookingApiHooked())
{
return;
}
MODULEINFO info;
if (GetModuleInformation(GetCurrentProcess(), GetModuleHandle(NULL), &info, sizeof(MODULEINFO)))
{
moduleStart = reinterpret_cast<size_t>(info.lpBaseOfDll);
moduleSize = info.SizeOfImage;
}
resumeFindWindow = reinterpret_cast<FindWindowSplice>(hotpatchHook(&FindWindowA,findWindowHook));
hookPreVeh();
}
// In case something goes wrong or we need to be stealthy
void unhookApi()
{
hotpatchUnhook(resumeFindWindow);
}
}
+1
View File
@@ -0,0 +1 @@
#include "stdafx.h"
+41
View File
@@ -0,0 +1,41 @@
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
#pragma once
// Exclude rarely-used stuff from Windows headers
#define WIN32_LEAN_AND_MEAN
#define _CRT_SECURE_NO_WARNINGS 1 // Microsoft's standard function deprecation crap: 'strcpy': This function or variable may be unsafe. Consider using strcpy_s instead.
// Windows and library header files
#include <windows.h>
#include <atlsync.h>
#include <atlwin.h>
#include <atlbase.h>
#include <Sensapi.h>
#include <Shellapi.h>
#include <Softpub.h>
#include <wincrypt.h>
#include <wintrust.h>
#include <comutil.h>
// C RunTime Header Files
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
// C++ Header files
#include <fstream>
// Boost header files
#include <boost/format.hpp>
#include <boost/bind.hpp>
#include <boost/iostreams/copy.hpp>
#include <boost/program_options.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/bind.hpp>
// Web browser control (needed for ActiveX hosting of IWebBrowser2)
#import "shdocvw.dll" include("OLECMDID", "OLECMDF", "OLECMDEXECOPT", "tagREADYSTATE")