mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-06 21:57:47 +00:00
GEEKING
This commit is contained in:
@@ -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
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
|
||||
bool VerifyCryptSignature(const std::wstring& fileName);
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 |
@@ -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>();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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));
|
||||
};
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
namespace RBX{ namespace Security {
|
||||
|
||||
__declspec(code_seg(".zero")) bool patchMain();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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&rbx_source=youtube&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;
|
||||
}
|
||||
@@ -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));
|
||||
};
|
||||
@@ -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
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
#include "stdafx.h"
|
||||
@@ -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")
|
||||
Reference in New Issue
Block a user