mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-07 13:57:48 +00:00
GEEKING
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
#include "StdAfx.h"
|
||||
#include "AuthenticationMarshallar.h"
|
||||
#include "util/http.h"
|
||||
#include "rbx/boost.hpp"
|
||||
|
||||
#undef min
|
||||
#undef max
|
||||
|
||||
#include "v8datamodel/contentprovider.h"
|
||||
#include <sstream>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
AuthenticationMarshallar::AuthenticationMarshallar(const char* domain)
|
||||
:domain(domain)
|
||||
{
|
||||
}
|
||||
|
||||
AuthenticationMarshallar::~AuthenticationMarshallar(void)
|
||||
{
|
||||
}
|
||||
|
||||
std::string buildUrl(const char* url, const char* ticket)
|
||||
{
|
||||
std::string compound = url;
|
||||
if (ticket)
|
||||
{
|
||||
compound += "?suggest=";
|
||||
compound += ticket;
|
||||
}
|
||||
|
||||
return compound;
|
||||
}
|
||||
|
||||
std::string AuthenticationMarshallar::Authenticate(const char* url, const char* ticket)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Post our ticket back to Roblox to suggest a re-authentication
|
||||
std::string result;
|
||||
{
|
||||
RBX::Http http(buildUrl(url, ticket).c_str());
|
||||
http.setAuthDomain(domain);
|
||||
http.get(result);
|
||||
}
|
||||
|
||||
// The http content is the new ticket
|
||||
return result;
|
||||
}
|
||||
catch (std::exception&)
|
||||
{
|
||||
//Report Error!
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
RBX::HttpFuture AuthenticationMarshallar::AuthenticateAsync(const char* url, const char* ticket)
|
||||
{
|
||||
RBX::HttpOptions options;
|
||||
options.addHeader(RBX::Http::kRBXAuthenticationNegotiation, domain);
|
||||
return RBX::HttpAsync::get(buildUrl(url, ticket), options);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/function.hpp>
|
||||
|
||||
#include "util/HttpAsync.h"
|
||||
|
||||
/// Keeps authentication cookies in sync between Protected Model IE and unprotected IE
|
||||
class AuthenticationMarshallar
|
||||
{
|
||||
std::string domain;
|
||||
public:
|
||||
AuthenticationMarshallar(const char* domain);
|
||||
~AuthenticationMarshallar(void);
|
||||
std::string Authenticate(const char* url, const char* ticket);
|
||||
RBX::HttpFuture AuthenticateAsync(const char* url, const char* ticket);
|
||||
};
|
||||
@@ -0,0 +1,70 @@
|
||||
#include "stdafx.h"
|
||||
#include "CheckDbg.h"
|
||||
|
||||
#include <boost/functional/hash/hash.hpp>
|
||||
|
||||
static volatile int g_counter = 0;
|
||||
|
||||
static __forceinline DWORD GetFS()
|
||||
{
|
||||
DWORD dw = 0;
|
||||
__asm
|
||||
{
|
||||
push eax // Preserve the registers
|
||||
mov eax, fs:[0x18] // Get the TIB's linear address
|
||||
mov dw, eax
|
||||
pop eax
|
||||
}
|
||||
return dw;
|
||||
}
|
||||
|
||||
static __forceinline DWORD GetFlag(DWORD f)
|
||||
{
|
||||
DWORD dw = 0;
|
||||
__asm
|
||||
{
|
||||
push eax // Preserve the registers
|
||||
push ecx
|
||||
|
||||
mov eax, f
|
||||
mov eax, dword ptr [eax + 0x30]
|
||||
mov ecx, dword ptr [eax] // Get the whole DWORD
|
||||
|
||||
mov dw, ecx
|
||||
|
||||
pop eax
|
||||
pop ecx
|
||||
}
|
||||
|
||||
return dw;
|
||||
}
|
||||
|
||||
__declspec(noinline) bool isDbg1()
|
||||
{
|
||||
DWORD fs = GetFS();
|
||||
g_counter += boost::hash_value(fs);
|
||||
g_counter += boost::hash_value(fs + g_counter);
|
||||
DWORD flag = GetFlag(fs);
|
||||
g_counter += boost::hash_value(fs + flag);
|
||||
return (flag & 0x00010000) ? true : false;
|
||||
}
|
||||
|
||||
__declspec(noinline) bool isDbg2()
|
||||
{
|
||||
DWORD fs = GetFS();
|
||||
g_counter += boost::hash_value(rand() + fs);
|
||||
DWORD flag = GetFlag(fs);
|
||||
g_counter += boost::hash_value((fs << 3) + flag);
|
||||
bool v = (flag & 0x00010000) ? true : false;
|
||||
g_counter += boost::hash_value((fs << 2) + flag + g_counter);
|
||||
return v;
|
||||
}
|
||||
|
||||
__declspec(noinline) bool isDbg3()
|
||||
{
|
||||
DWORD fs = GetFS();
|
||||
g_counter += boost::hash_value(rand() + g_counter);
|
||||
DWORD flag = GetFlag(fs);
|
||||
g_counter += boost::hash_value(rand() + g_counter + (rand() << 3));
|
||||
return (flag & 0x00010000) ? true : false;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
__declspec(noinline) bool isDbg1();
|
||||
__declspec(noinline) bool isDbg2();
|
||||
__declspec(noinline) bool isDbg3();
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* DSVideoCaptureEngine.h
|
||||
* Copyright (c) 2013 ROBLOX Corp. All Rights Reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "VideoControl.h"
|
||||
|
||||
// System Headers
|
||||
#ifndef RBX_PLATFORM_DURANGO
|
||||
#include <atlcomcli.h>
|
||||
#endif // RBX_PLATFORM_DURANGO
|
||||
|
||||
struct IMediaControl;
|
||||
struct IGraphBuilder;
|
||||
|
||||
namespace RBX {
|
||||
|
||||
namespace DS {
|
||||
class CVideoStreamFilter;
|
||||
class CAudioStreamFilter;
|
||||
|
||||
class IAudioTime
|
||||
{
|
||||
public:
|
||||
virtual LONG GetTime() = 0;
|
||||
virtual LONG GetAbsoluteTime() = 0;
|
||||
};
|
||||
}
|
||||
|
||||
class DSVideoCaptureEngine : public IVideoCapture, public DS::IAudioTime
|
||||
{
|
||||
public:
|
||||
DSVideoCaptureEngine();
|
||||
virtual ~DSVideoCaptureEngine();
|
||||
virtual bool start(int cx, int cy, SoundState *s);
|
||||
virtual bool stop();
|
||||
virtual bool isRunning();
|
||||
virtual void setVideoQuality(int vq);
|
||||
virtual void pushNextFrame(void* device, Verb *cancelAction);
|
||||
virtual std::string & getFileName() { return fullFileName; };
|
||||
|
||||
virtual LONG GetTime();
|
||||
virtual LONG GetAbsoluteTime();
|
||||
private:
|
||||
const double defNx;
|
||||
const double defNy;
|
||||
const LONG MaxRecordTime;
|
||||
|
||||
HRESULT BuildCaptureGraph(int cx, int cy, bool forceNoAudio);
|
||||
HRESULT BuildCaptureGraphNoThrow(int cx, int cy, bool forceNoAudio);
|
||||
|
||||
void DestroyCaptureGgaph();
|
||||
int GenerateFileName(LPWSTR fileName);
|
||||
void GetSquareSizes(int cx, int cy, int &ncx, int &ncy);
|
||||
|
||||
DWORD startTime;
|
||||
|
||||
IGraphBuilder *graph;
|
||||
SoundState *soundState;
|
||||
IMediaControl *mediaControl;
|
||||
DS::CVideoStreamFilter *videoSource;
|
||||
DS::CAudioStreamFilter *audioSource;
|
||||
|
||||
int framesPushed;
|
||||
LONG lastPushedVideoFrameTime;
|
||||
std::string fullFileName; // TODO: replace with boost::filesystem::path
|
||||
|
||||
unsigned char* frameData;
|
||||
unsigned frameDataSize;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
/* Copyright 2003-2007 ROBLOX Corporation, All Rights Reserved */
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#undef min
|
||||
#undef max
|
||||
|
||||
#include "rbx/Log.h"
|
||||
|
||||
#include "DumpErrorUploader.h"
|
||||
#include "LogManager.h"
|
||||
#include "rbx/rbxTime.h"
|
||||
|
||||
#include "util/http.h"
|
||||
#include "util/StandardOut.h"
|
||||
#include "util/MemoryStats.h"
|
||||
#include "v8datamodel/Stats.h"
|
||||
|
||||
namespace io = boost::iostreams;
|
||||
|
||||
boost::scoped_ptr<RBX::Http> DumpErrorUploader::crashEventRequest;
|
||||
std::istringstream DumpErrorUploader::crashEventData("Crash happened!");
|
||||
std::string DumpErrorUploader::crashEventResponse;
|
||||
std::string DumpErrorUploader::crashCounterNamePrefix;
|
||||
|
||||
DYNAMIC_FASTINTVARIABLE(RCCInfluxHundredthsPercentage, 1000)
|
||||
DYNAMIC_FASTFLAGVARIABLE(ExtendedCrashInfluxReporting, false)
|
||||
|
||||
DumpErrorUploader::DumpErrorUploader(bool backgroundUpload, const std::string& crashCounterNamePrefix)
|
||||
{
|
||||
if (backgroundUpload)
|
||||
thread.reset(new RBX::worker_thread(boost::bind(&DumpErrorUploader::run, _data), "ErrorUploader"));
|
||||
|
||||
this->crashCounterNamePrefix = crashCounterNamePrefix;
|
||||
}
|
||||
|
||||
void DumpErrorUploader::InitCrashEvent(const std::string& url, const std::string& crashEventFileName)
|
||||
{
|
||||
// Setup a HTTP object for crashEvent
|
||||
if(!crashEventRequest)
|
||||
{
|
||||
std::string finalUrl = url + "?filename=" + RBX::Http::urlEncode(crashEventFileName);
|
||||
RBX::Log::current()->writeEntry(RBX::Log::Information, RBX::format("Initializing CrashEvent request, url: %s", finalUrl.c_str()).c_str());
|
||||
crashEventRequest.reset(new RBX::Http(finalUrl));
|
||||
crashEventResponse.reserve(MAX_PATH);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DumpErrorUploader::Upload(const std::string& url)
|
||||
{
|
||||
HANDLE hMutex;
|
||||
|
||||
hMutex = CreateMutex(
|
||||
NULL, // default security descriptor
|
||||
TRUE, // own the mutex
|
||||
TEXT("RobloxCrashDumpUploaderMutex")); // object name
|
||||
|
||||
if (hMutex == NULL)
|
||||
{
|
||||
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_ERROR, "CreateMutex error: %d\n", GetLastError() );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( GetLastError() == ERROR_ALREADY_EXISTS )
|
||||
{
|
||||
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_INFO, "RobloxCrashDumpUploaderMutex already exists. Not uploading logs.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> f = MainLogManager::getMainLogManager()->gatherCrashLogs();
|
||||
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(_data->sync);
|
||||
_data->url = url;
|
||||
_data->hInterprocessMutex = hMutex;
|
||||
for (size_t i = 0; i<f.size(); ++i)
|
||||
_data->files.push(f[i]);
|
||||
}
|
||||
|
||||
if (thread)
|
||||
{
|
||||
thread->wake();
|
||||
}
|
||||
else
|
||||
{
|
||||
while (run(_data)!=RBX::worker_thread::done)
|
||||
{}
|
||||
}
|
||||
}
|
||||
|
||||
int LogFilter(unsigned int code, struct _EXCEPTION_POINTERS *ep) {
|
||||
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_ERROR, "Exception code: %u", code);
|
||||
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
}
|
||||
|
||||
void DumpErrorUploader::UploadCrashEventFile(struct _EXCEPTION_POINTERS *excInfo)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(crashEventRequest)
|
||||
{
|
||||
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_INFO, "Crash Event post");
|
||||
crashEventRequest->post(crashEventData, RBX::Http::kContentTypeDefaultUnspecified, true, crashEventResponse);
|
||||
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_INFO, "Crash Event posted, response: %s", crashEventResponse.c_str());
|
||||
|
||||
RBX::Analytics::EphemeralCounter::reportCounter(crashCounterNamePrefix+"CrashEvent", 1, true);
|
||||
|
||||
RBX::Analytics::InfluxDb::Points points;
|
||||
if (DFFlag::ExtendedCrashInfluxReporting)
|
||||
{
|
||||
points.addPoint("SessionReport", "AppStatusCrash");
|
||||
points.addPoint("PlayTime", RBX::Time::nowFastSec());
|
||||
points.addPoint("UsedMemoryKB", RBX::MemoryStats::usedMemoryBytes());
|
||||
|
||||
MainLogManager::GameState gamestate = MainLogManager::getMainLogManager()->getGameState();
|
||||
std::string gameStateString = gamestate == MainLogManager::UN_INITIALIZED ? "uninitialized" : (gamestate == MainLogManager::IN_GAME ? "inGame" : "leaveGame");
|
||||
points.addPoint("GameState", gameStateString.c_str());
|
||||
|
||||
if(excInfo)
|
||||
{
|
||||
points.addPoint("ExceptionCode" , (uint32_t)excInfo->ExceptionRecord->ExceptionCode);
|
||||
std::stringstream excepAddress;
|
||||
excepAddress << excInfo->ExceptionRecord->ExceptionAddress;
|
||||
points.addPoint("ExceptionAddress", excepAddress.str().c_str());
|
||||
|
||||
for (uint32_t i = 0 ; i < excInfo->ExceptionRecord->NumberParameters ; ++i)
|
||||
{
|
||||
std::ostringstream paramName;
|
||||
paramName << "ExceptionParameter-" << i;
|
||||
std::stringstream paramValue;
|
||||
paramValue << excInfo->ExceptionRecord->ExceptionInformation[i];
|
||||
points.addPoint(paramName.str(), paramValue.str().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
points.addPoint("SessionID", MainLogManager::getMainLogManager()->getSessionId().c_str());
|
||||
points.report(crashCounterNamePrefix+"CrashEvent", DFInt::RCCInfluxHundredthsPercentage);
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_ERROR, "Exception during upload crash event: %s", e.what());
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_ERROR, "Exception during upload crash event");
|
||||
}
|
||||
}
|
||||
|
||||
RBX::worker_thread::work_result DumpErrorUploader::run(shared_ptr<data> _data)
|
||||
{
|
||||
std::string file;
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(_data->sync);
|
||||
if (_data->files.empty())
|
||||
{
|
||||
if(_data->hInterprocessMutex)
|
||||
{
|
||||
CloseHandle(_data->hInterprocessMutex);
|
||||
_data->hInterprocessMutex = NULL;
|
||||
}
|
||||
|
||||
if (_data->dmpFileCount > 0)
|
||||
{
|
||||
// report number of dmps uploaded
|
||||
RBX::Analytics::EphemeralCounter::reportCounter(crashCounterNamePrefix+"Crash", _data->dmpFileCount, true);
|
||||
}
|
||||
|
||||
return RBX::worker_thread::done;
|
||||
}
|
||||
file = _data->files.front();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
printf("Uploading %s\n", file.c_str());
|
||||
bool isDmpFile = file.substr(file.size()-4)==".dmp";
|
||||
if (isDmpFile)
|
||||
_data->dmpFileCount++;
|
||||
|
||||
bool isFullDmp = file.find(".Full.") != std::string::npos;
|
||||
|
||||
// TODO: put the filename in the header rather than the query string??
|
||||
std::string url = _data->url;
|
||||
url += "?filename=";
|
||||
url += RBX::Http::urlEncode(file);
|
||||
|
||||
if (isDmpFile && _data->dmpFileCount>3)
|
||||
{
|
||||
std::stringstream data("Too many dmp files");
|
||||
std::string response;
|
||||
RBX::Http(url).post(data, RBX::Http::kContentTypeDefaultUnspecified, true, response);
|
||||
}
|
||||
else if (!isFullDmp)
|
||||
{
|
||||
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_INFO, "Uploading %s\n", file.c_str() );
|
||||
std::fstream data(file.c_str(), std::ios_base::in | std::ios_base::binary);
|
||||
std::streamoff begin = data.tellg();
|
||||
data.seekg (0, std::ios::end);
|
||||
std::streamoff end = data.tellg();
|
||||
if (end > begin)
|
||||
{
|
||||
data.seekg (0, std::ios::beg);
|
||||
std::string response;
|
||||
RBX::Http(url).post(data, RBX::Http::kContentTypeDefaultUnspecified, true, response);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Some dmp files are empty. Post it anyway so that we can report it
|
||||
std::stringstream data("Empty!!!");
|
||||
std::string response;
|
||||
RBX::Http(url).post(data, RBX::Http::kContentTypeDefaultUnspecified, true, response);
|
||||
}
|
||||
}
|
||||
|
||||
// done uploading, move to archive.
|
||||
// nb: if upload cuts right after uploading .dmp file, we will never upload the other log files associated.
|
||||
// we think this is acceptable to keep this code simple at this time.
|
||||
ErrorUploader::MoveRelative(file.c_str(), "archive\\");
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
RBX::StandardOut::singleton()->print(RBX::MESSAGE_ERROR, e);
|
||||
}
|
||||
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(_data->sync);
|
||||
_data->files.pop();
|
||||
}
|
||||
return RBX::worker_thread::more;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "ErrorUploader.h"
|
||||
|
||||
|
||||
namespace RBX
|
||||
{
|
||||
class Http;
|
||||
}
|
||||
|
||||
class DumpErrorUploader : public ErrorUploader
|
||||
{
|
||||
public:
|
||||
DumpErrorUploader(bool backgroundUpload, const std::string& crashCounterNamePrefix);
|
||||
void Upload(const std::string& url);
|
||||
void InitCrashEvent(const std::string& url, const std::string& crashEventName);
|
||||
|
||||
static void UploadCrashEventFile(struct _EXCEPTION_POINTERS *info = NULL);
|
||||
|
||||
// Really trying to minimize heap allocations on crash event upload
|
||||
static boost::scoped_ptr<RBX::Http> crashEventRequest;
|
||||
static std::istringstream crashEventData;
|
||||
static std::string crashEventResponse;
|
||||
static std::string crashCounterNamePrefix;
|
||||
private:
|
||||
static RBX::worker_thread::work_result run(shared_ptr<data> _data);
|
||||
|
||||
};
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/* Copyright 2003-2007 ROBLOX Corporation, All Rights Reserved */
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#undef min
|
||||
#undef max
|
||||
|
||||
#include "ErrorUploader.h"
|
||||
#include "LogManager.h"
|
||||
|
||||
#include <boost/iostreams/device/file.hpp>
|
||||
#include <boost/iostreams/stream.hpp>
|
||||
#include <boost/iostreams/copy.hpp>
|
||||
#include <boost/iostreams/concepts.hpp> // source
|
||||
|
||||
#include "util/http.h"
|
||||
#include "util/StandardOut.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
// For mkdir
|
||||
#include <direct.h>
|
||||
#include <io.h>
|
||||
#else
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include "_FindFirst.h"
|
||||
#endif
|
||||
#include "errno.h"
|
||||
|
||||
std::string ErrorUploader::MoveRelative(LPCTSTR fileName, std::string path)
|
||||
{
|
||||
ATL::CPath dir = fileName;
|
||||
dir.RemoveFileSpec();
|
||||
dir.Append(path.c_str());
|
||||
if (!dir.FileExists())
|
||||
::_mkdir(dir);
|
||||
ATL::CPath file = fileName;
|
||||
file.StripPath();
|
||||
dir.Append(file);
|
||||
if (!::MoveFile(fileName, dir))
|
||||
::DeleteFile(fileName); // just in case!
|
||||
return (LPCTSTR) dir;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include <queue>
|
||||
#include "rbx/boost.hpp"
|
||||
#include "rbx/Thread.hpp"
|
||||
|
||||
#include <boost/iostreams/device/file.hpp>
|
||||
#include <boost/iostreams/stream.hpp>
|
||||
#include <boost/iostreams/copy.hpp>
|
||||
#include <boost/iostreams/concepts.hpp> // source
|
||||
|
||||
#ifdef _WIN32
|
||||
// For mkdir
|
||||
#include <direct.h>
|
||||
#include <io.h>
|
||||
#else
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include "_FindFirst.h"
|
||||
#endif
|
||||
#include "errno.h"
|
||||
|
||||
class ErrorUploader
|
||||
{
|
||||
protected:
|
||||
struct data
|
||||
{
|
||||
std::queue<std::string> files;
|
||||
boost::recursive_mutex sync; // TODO: Would non-recursive be safe here?
|
||||
std::string url;
|
||||
int dmpFileCount;
|
||||
HANDLE hInterprocessMutex;
|
||||
|
||||
data():dmpFileCount(0), hInterprocessMutex(NULL) {}
|
||||
};
|
||||
shared_ptr<data> _data;
|
||||
boost::scoped_ptr<RBX::worker_thread> thread;
|
||||
|
||||
static std::string MoveRelative(LPCTSTR fileName, std::string path);
|
||||
public:
|
||||
ErrorUploader()
|
||||
:_data(new data())
|
||||
{}
|
||||
void Cancel()
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(_data->sync);
|
||||
while (!_data->files.empty())
|
||||
_data->files.pop();
|
||||
}
|
||||
bool IsUploading()
|
||||
{
|
||||
return !_data->files.empty();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,862 @@
|
||||
/* Copyright 2003-2007 ROBLOX Corporation, All Rights Reserved */
|
||||
#include "stdafx.h"
|
||||
|
||||
#undef min
|
||||
#undef max
|
||||
|
||||
|
||||
#include "LogManager.h"
|
||||
#include "RbxFormat.h"
|
||||
#include "rbx/Debug.h"
|
||||
#include "rbx/boost.hpp"
|
||||
#include "util/StandardOut.h"
|
||||
#include "util/FileSystem.h"
|
||||
#include "util/Guid.h"
|
||||
#include "util/Http.h"
|
||||
#include "util/Statistics.h"
|
||||
|
||||
#include "G3D/debugAssert.h"
|
||||
#include <direct.h>
|
||||
|
||||
#include "atltime.h"
|
||||
#include "atlfile.h"
|
||||
|
||||
#include "versioninfo.h"
|
||||
#include "rbx/TaskScheduler.h"
|
||||
#include "VistaTools.h"
|
||||
#include "DumpErrorUploader.h"
|
||||
#include "rbx/Log.h"
|
||||
#include "FastLog.h"
|
||||
|
||||
LOGGROUP(CrashReporterInit)
|
||||
|
||||
bool LogManager::logsEnabled = true;
|
||||
|
||||
MainLogManager* LogManager::mainLogManager = NULL;
|
||||
|
||||
RBX::mutex MainLogManager::fastLogChannelsLock;
|
||||
|
||||
#pragma comment(lib, "shell32.lib")
|
||||
|
||||
static const ATL::CPath& DoGetPath()
|
||||
{
|
||||
// DO NOT REMOVE the CString conversion, this is to preserve the old behavior of losing unicodeness.
|
||||
static ATL::CPath path = CString( RBX::FileSystem::getUserDirectory(true, RBX::DirAppData, "logs").native().c_str() );
|
||||
return path;
|
||||
}
|
||||
|
||||
void InitPath()
|
||||
{
|
||||
DoGetPath();
|
||||
}
|
||||
|
||||
std::string GetAppVersion()
|
||||
{
|
||||
CVersionInfo vi;
|
||||
FASTLOG1(FLog::CrashReporterInit, "Getting app version, module handle: %p", _AtlBaseModule.m_hInst);
|
||||
vi.Load(_AtlBaseModule.m_hInst);
|
||||
return vi.GetFileVersionAsString();
|
||||
}
|
||||
|
||||
const ATL::CPath& LogManager::GetLogPath() const
|
||||
{
|
||||
static boost::once_flag flag = BOOST_ONCE_INIT;
|
||||
boost::call_once(&InitPath, flag);
|
||||
return DoGetPath();
|
||||
}
|
||||
|
||||
void MainLogManager::fastLogMessage(FLog::Channel id, const char* message)
|
||||
{
|
||||
RBX::mutex::scoped_lock lock(fastLogChannelsLock);
|
||||
|
||||
if(mainLogManager)
|
||||
{
|
||||
if(id >= mainLogManager->fastLogChannels.size())
|
||||
mainLogManager->fastLogChannels.resize(id+1, NULL);
|
||||
|
||||
if(mainLogManager->fastLogChannels[id] == NULL)
|
||||
{
|
||||
|
||||
mainLogManager->fastLogChannels[id] = new RBX::Log(mainLogManager->getFastLogFileName(id).c_str(), "Log Channel");
|
||||
}
|
||||
|
||||
mainLogManager->fastLogChannels[id]->writeEntry(RBX::Log::Information, message);
|
||||
}
|
||||
}
|
||||
|
||||
std::string MainLogManager::getSessionId()
|
||||
{
|
||||
std::string id = guid;
|
||||
// trim part of the guid for readability
|
||||
id.erase(8);
|
||||
id.erase(0,3);
|
||||
return id;
|
||||
}
|
||||
|
||||
std::string MainLogManager::getCrashEventName()
|
||||
{
|
||||
FASTLOG(FLog::CrashReporterInit, "Getting crash event name");
|
||||
ATL::CPath path = GetLogPath();
|
||||
|
||||
std::string fileName = "log_";
|
||||
fileName += getSessionId();
|
||||
fileName += " ";
|
||||
|
||||
fileName += GetAppVersion();
|
||||
fileName += crashEventExtention;
|
||||
|
||||
path.Append(fileName.c_str());
|
||||
|
||||
return (LPCTSTR)path;
|
||||
}
|
||||
|
||||
std::string MainLogManager::getLogFileName()
|
||||
{
|
||||
ATL::CPath path = GetLogPath();
|
||||
|
||||
std::string fileName = "log_";
|
||||
fileName += getSessionId();
|
||||
fileName += ".txt";
|
||||
|
||||
path.Append(fileName.c_str());
|
||||
|
||||
return (LPCTSTR) path;
|
||||
}
|
||||
|
||||
std::string MainLogManager::getFastLogFileName(FLog::Channel channelId)
|
||||
{
|
||||
ATL::CPath path = GetLogPath();
|
||||
|
||||
CString filename;
|
||||
filename.Format("log_%s_%d.txt", getSessionId().c_str(), channelId);
|
||||
|
||||
path.Append(filename);
|
||||
|
||||
return (LPCTSTR) path;
|
||||
}
|
||||
|
||||
|
||||
std::string MainLogManager::MakeLogFileName(const char* postfix)
|
||||
{
|
||||
ATL::CPath path = GetLogPath();
|
||||
|
||||
std::string fileName = "log_";
|
||||
fileName += getSessionId();
|
||||
fileName += postfix;
|
||||
fileName += ".txt";
|
||||
|
||||
path.Append(fileName.c_str());
|
||||
|
||||
return (LPCTSTR) path;
|
||||
}
|
||||
|
||||
std::string ThreadLogManager::getLogFileName()
|
||||
{
|
||||
std::string fileName = mainLogManager->getLogFileName();
|
||||
CString id;
|
||||
id.Format("_%s_%d", name.c_str(), threadID);
|
||||
fileName.insert(fileName.size()-4, id);
|
||||
return fileName;
|
||||
}
|
||||
|
||||
RBX::Log* LogManager::getLog()
|
||||
{
|
||||
if (!logsEnabled)
|
||||
return NULL;
|
||||
if (log==NULL)
|
||||
{
|
||||
log = new RBX::Log(getLogFileName().c_str(), name.c_str());
|
||||
// TODO: delete an old log that isn't in use
|
||||
}
|
||||
return log;
|
||||
}
|
||||
|
||||
|
||||
RBX::Log* MainLogManager::provideLog()
|
||||
{
|
||||
if (GetCurrentThreadId()==threadID)
|
||||
return this->getLog();
|
||||
|
||||
return ThreadLogManager::getCurrent()->getLog();
|
||||
}
|
||||
|
||||
|
||||
#include < process.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
|
||||
#define MAX_CONSOLE_LINES 250;
|
||||
|
||||
HANDLE g_hConsoleOut; // Handle to debug console
|
||||
|
||||
|
||||
|
||||
RobloxCrashReporter::RobloxCrashReporter(const char* outputPath, const char* appName, const char* crashExtention)
|
||||
{
|
||||
controls.minidumpType=MiniDumpWithDataSegs;
|
||||
|
||||
if(IsVistaPlus())
|
||||
controls.minidumpType |= MiniDumpWithIndirectlyReferencedMemory;
|
||||
|
||||
strcpy(controls.pathToMinidump, outputPath);
|
||||
|
||||
strcpy(controls.appName, appName);
|
||||
|
||||
strncpy(controls.appVersion, GetAppVersion().c_str(), 128);
|
||||
strncpy(controls.crashExtention, crashExtention, sizeof(controls.crashExtention));
|
||||
}
|
||||
|
||||
bool RobloxCrashReporter::silent;
|
||||
|
||||
LONG RobloxCrashReporter::ProcessException(struct _EXCEPTION_POINTERS *info, bool noMsg)
|
||||
{
|
||||
LogManager::ReportEvent(EVENTLOG_INFORMATION_TYPE, "StartProcessException...");
|
||||
|
||||
LONG result = __super::ProcessException(info, noMsg);
|
||||
static bool showedMessage = silent;
|
||||
if (!showedMessage && !noMsg)
|
||||
{
|
||||
showedMessage = true;
|
||||
::MessageBox( NULL, "An unexpected error occurred and ROBLOX needs to quit. We're sorry!", "ROBLOX Crash", MB_OK );
|
||||
}
|
||||
|
||||
LogManager::ReportEvent(EVENTLOG_INFORMATION_TYPE, "DoneProcessException");
|
||||
|
||||
LogManager::ReportEvent(EVENTLOG_INFORMATION_TYPE, "Uploading .crashevent...");
|
||||
DumpErrorUploader::UploadCrashEventFile(info);
|
||||
LogManager::ReportEvent(EVENTLOG_INFORMATION_TYPE, "Done uploading .crashevent...");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void RobloxCrashReporter::logEvent(const char* msg)
|
||||
{
|
||||
LogManager::ReportEvent(EVENTLOG_INFORMATION_TYPE, msg);
|
||||
}
|
||||
|
||||
void MainLogManager::WriteCrashDump()
|
||||
{
|
||||
std::string appName = "log_";
|
||||
appName += getSessionId();
|
||||
crashReporter.reset(new RobloxCrashReporter((LPCTSTR) GetLogPath(), appName.c_str(), crashExtention));
|
||||
crashReporter->Start();
|
||||
CString eventMessage;
|
||||
eventMessage.Format("CrashReporter Start");
|
||||
RBX::Log::current()->writeEntry(RBX::Log::Information, eventMessage);
|
||||
};
|
||||
|
||||
bool MainLogManager::CreateFakeCrashDump()
|
||||
{
|
||||
if(!crashReporter)
|
||||
{
|
||||
// start the service if not started.
|
||||
WriteCrashDump();
|
||||
}
|
||||
|
||||
// First, write FastLog
|
||||
char dumpFilepath[_MAX_PATH];
|
||||
if(FAILED(crashReporter->GenerateDmpFileName(dumpFilepath, _MAX_PATH, true)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
FLog::WriteFastLogDump(dumpFilepath, 2000);
|
||||
|
||||
if(FAILED(crashReporter->GenerateDmpFileName(dumpFilepath, _MAX_PATH)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
HANDLE hFile = CreateFile(dumpFilepath,GENERIC_WRITE, FILE_SHARE_READ,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
|
||||
if (hFile==INVALID_HANDLE_VALUE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD cb;
|
||||
WriteFile(hFile, "Fake", 5, &cb, NULL);
|
||||
|
||||
CloseHandle(hFile);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MainLogManager::EnableImmediateCrashUpload(bool enabled)
|
||||
{
|
||||
if(crashReporter)
|
||||
{
|
||||
crashReporter->EnableImmediateUpload(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
void MainLogManager::DisableHangReporting()
|
||||
{
|
||||
if(crashReporter)
|
||||
{
|
||||
crashReporter->DisableHangReporting();
|
||||
}
|
||||
}
|
||||
|
||||
void MainLogManager::NotifyFGThreadAlive()
|
||||
{
|
||||
if(crashReporter)
|
||||
{
|
||||
#if 0
|
||||
// for debugging only:
|
||||
static int alivecount = 0;
|
||||
if(alivecount++ % 60 == 0)
|
||||
{
|
||||
CString eventMessage;
|
||||
eventMessage.Format("FGAlive %d", alivecount);
|
||||
LogManager::ReportEvent(EVENTLOG_INFORMATION_TYPE, eventMessage);
|
||||
}
|
||||
#endif
|
||||
|
||||
crashReporter->NotifyAlive();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void purecallHandler(void)
|
||||
{
|
||||
CString eventMessage;
|
||||
eventMessage.Format("Pure Call Error");
|
||||
LogManager::ReportEvent(EVENTLOG_ERROR_TYPE, eventMessage);
|
||||
#ifdef _DEBUG
|
||||
_CrtDbgBreak();
|
||||
#endif
|
||||
// Cause a crash
|
||||
RBXCRASH();
|
||||
}
|
||||
|
||||
MainLogManager::MainLogManager(LPCTSTR productName, const char* crashExtention, const char* crashEventExtention)
|
||||
:LogManager(productName),
|
||||
crashExtention(crashExtention),
|
||||
crashEventExtention(crashEventExtention),
|
||||
gameState(MainLogManager::GameState::UN_INITIALIZED)
|
||||
{
|
||||
RBX::Guid::generateRBXGUID(guid);
|
||||
CullLogs("\\", 1024);
|
||||
CullLogs("archive\\", 1024);
|
||||
|
||||
RBXASSERT(mainLogManager == NULL);
|
||||
mainLogManager = this;
|
||||
|
||||
RBX::Log::setLogProvider(this);
|
||||
|
||||
RBX::setAssertionHook(&MainLogManager::handleDebugAssert);
|
||||
RBX::setFailureHook(&MainLogManager::handleFailure);
|
||||
|
||||
_set_purecall_handler(purecallHandler);
|
||||
|
||||
FLog::SetExternalLogFunc(fastLogMessage);
|
||||
}
|
||||
|
||||
MainLogManager* LogManager::getMainLogManager() {
|
||||
return mainLogManager;
|
||||
}
|
||||
|
||||
|
||||
ThreadLogManager::ThreadLogManager()
|
||||
:LogManager(RBX::get_thread_name())
|
||||
{
|
||||
}
|
||||
|
||||
ThreadLogManager::~ThreadLogManager()
|
||||
{
|
||||
}
|
||||
|
||||
static float getThisYearTimeInMinutes(SYSTEMTIME time)
|
||||
{
|
||||
return (time.wMonth * 43829.0639f) + (time.wDay * 1440) + (time.wHour * 60) + time.wMinute;
|
||||
}
|
||||
|
||||
std::vector<std::string> MainLogManager::getRecentCseFiles()
|
||||
{
|
||||
std::vector<std::string> archiveResult;
|
||||
|
||||
ATL::CPath archivePath = GetLogPath();
|
||||
archivePath.AddBackslash();
|
||||
archivePath.Append("archive//");
|
||||
|
||||
WIN32_FIND_DATA FindArchiveData;
|
||||
HANDLE archiveCseFind = FindFirstFile(archivePath + "*.cse", &FindArchiveData);
|
||||
|
||||
SYSTEMTIME curTime;
|
||||
::GetSystemTime(&curTime);
|
||||
float curTimeInMinutes = getThisYearTimeInMinutes(curTime);
|
||||
|
||||
if(archiveCseFind!=INVALID_HANDLE_VALUE)
|
||||
{
|
||||
do
|
||||
{
|
||||
if ((FindArchiveData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)==0)
|
||||
{
|
||||
SYSTEMTIME fileTime;
|
||||
// get our filetime synced up to UTC
|
||||
::FileTimeToSystemTime(&FindArchiveData.ftLastWriteTime,&fileTime);
|
||||
|
||||
// we check to see if we have uploaded this error (or a similar one) within the last hour
|
||||
// if we have, we need to check this file against any new cse files, in case we are uploading
|
||||
// a similar error within an hour of doing so previously
|
||||
float minuteDiff = curTimeInMinutes - getThisYearTimeInMinutes(fileTime);
|
||||
if((curTime.wYear == fileTime.wYear && minuteDiff <= 60) || curTime.wYear > fileTime.wYear)
|
||||
{
|
||||
std::string archiveFilename = FindArchiveData.cFileName;
|
||||
archiveFilename = archiveFilename.substr(0,archiveFilename.size() - 9); // get rid of sessionId, file extension (don't need these for comparison)
|
||||
archiveResult.push_back((LPCTSTR)(archiveFilename.c_str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
while (FindNextFile(archiveCseFind, &FindArchiveData));
|
||||
}
|
||||
|
||||
return archiveResult;
|
||||
|
||||
}
|
||||
|
||||
std::vector<std::string> MainLogManager::gatherScriptCrashLogs()
|
||||
{
|
||||
|
||||
std::vector<std::string> result;
|
||||
|
||||
ATL::CPath path = GetLogPath();
|
||||
path.AddBackslash();
|
||||
|
||||
WIN32_FIND_DATA FindCseData;
|
||||
HANDLE cseFind = FindFirstFile(path + "*.cse", &FindCseData);
|
||||
|
||||
std::vector<std::string> archiveResult = getRecentCseFiles();
|
||||
|
||||
// look for cse files. cse files are simply logs of core script errors
|
||||
if (cseFind!=INVALID_HANDLE_VALUE)
|
||||
{
|
||||
do
|
||||
{
|
||||
if ((FindCseData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)==0)
|
||||
{
|
||||
std::string filename = FindCseData.cFileName;
|
||||
bool deleted = false;
|
||||
|
||||
for(std::vector<std::string>::iterator iter = archiveResult.begin(); iter != archiveResult.end() && !deleted; ++iter)
|
||||
{
|
||||
// we have uploaded this error recently, just delete this log (we will eventually reupload this anyway if it reoccurs)
|
||||
if(filename.find((*iter)) != std::string::npos)
|
||||
{
|
||||
::DeleteFile(path + filename.c_str());
|
||||
deleted = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!deleted)
|
||||
{
|
||||
// attach our sessionId number to the filename (this won't be the same sessionId as when this occurred, but helps with uniquing script errors)
|
||||
filename = filename.substr(0,filename.size() - 4);
|
||||
std::string id = MainLogManager::getMainLogManager()->getSessionId();
|
||||
filename.append(MainLogManager::getMainLogManager()->getSessionId()).append(".cse");
|
||||
rename(path + FindCseData.cFileName,path + filename.c_str());
|
||||
result.push_back((LPCTSTR)(path + filename.c_str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
while (FindNextFile(cseFind, &FindCseData));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Gather all dmp files (and associated log files)
|
||||
std::vector<std::string> MainLogManager::gatherCrashLogs()
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
ATL::CPath path = GetLogPath();
|
||||
path.AddBackslash();
|
||||
|
||||
WIN32_FIND_DATA FindDmpData;
|
||||
HANDLE hFind = FindFirstFile(path + "*.dmp", &FindDmpData);
|
||||
|
||||
// look for regular dmp files
|
||||
if (hFind!=INVALID_HANDLE_VALUE)
|
||||
{
|
||||
do
|
||||
{
|
||||
if ((FindDmpData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)==0)
|
||||
{
|
||||
// archive our dump file now.
|
||||
result.push_back((LPCTSTR)(path + FindDmpData.cFileName));
|
||||
|
||||
// We got a dmp file. archive associated log files
|
||||
std::string wildCard = FindDmpData.cFileName;
|
||||
wildCard = wildCard.substr(0, 9) + "*.*";
|
||||
std::vector<std::string> logs = gatherAssociatedLogs(wildCard);
|
||||
std::copy(logs.begin(), logs.end(), std::back_inserter(result));
|
||||
|
||||
}
|
||||
}
|
||||
while (FindNextFile(hFind, &FindDmpData));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Gather all files associated with a sessionId
|
||||
std::vector<std::string> MainLogManager::gatherAssociatedLogs(const std::string& filenamepattern)
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
ATL::CPath path = GetLogPath();
|
||||
path.AddBackslash();
|
||||
|
||||
// We got a dmp file. Now find associated log files
|
||||
WIN32_FIND_DATA FindOtherFileData;
|
||||
HANDLE hFindOther = FindFirstFile(path + filenamepattern.c_str(), &FindOtherFileData);
|
||||
if (hFindOther!=INVALID_HANDLE_VALUE)
|
||||
{
|
||||
do
|
||||
{
|
||||
if ((FindOtherFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)==0)
|
||||
{
|
||||
if(std::string(FindOtherFileData.cFileName).find(".dmp") == std::string::npos)
|
||||
result.push_back((LPCTSTR)(path + FindOtherFileData.cFileName));
|
||||
}
|
||||
}
|
||||
while (FindNextFile(hFindOther, &FindOtherFileData));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool MainLogManager::hasCrashLogs(std::string extension) const
|
||||
{
|
||||
ATL::CPath path = GetLogPath();
|
||||
path.AddBackslash();
|
||||
WIN32_FIND_DATA FindFileData;
|
||||
HANDLE hFind = FindFirstFile(path + "*" + extension.c_str(), &FindFileData);
|
||||
if(hFind!=INVALID_HANDLE_VALUE)
|
||||
{
|
||||
do
|
||||
{
|
||||
if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)==0)
|
||||
return true;
|
||||
}
|
||||
while (FindNextFile(hFind, &FindFileData));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MainLogManager::CullLogs(const char* folder, int filesRemaining)
|
||||
{
|
||||
ATL::CPath path = GetLogPath();
|
||||
path.Append(folder);
|
||||
path.AddBackslash();
|
||||
|
||||
std::list<WIN32_FIND_DATA> files;
|
||||
WIN32_FIND_DATA FindFileData;
|
||||
HANDLE hFind = FindFirstFile(path + "*.*", &FindFileData);
|
||||
if (hFind!=INVALID_HANDLE_VALUE)
|
||||
{
|
||||
do
|
||||
{
|
||||
if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)==0)
|
||||
{
|
||||
CString f = FindFileData.cFileName;
|
||||
files.push_back(FindFileData);
|
||||
}
|
||||
}
|
||||
while (FindNextFile(hFind, &FindFileData));
|
||||
}
|
||||
|
||||
struct olderthan
|
||||
{
|
||||
bool operator()(const WIN32_FIND_DATA& lhs, const WIN32_FIND_DATA& rhs)
|
||||
{
|
||||
if(lhs.ftCreationTime.dwHighDateTime == rhs.ftCreationTime.dwHighDateTime)
|
||||
return lhs.ftCreationTime.dwLowDateTime < rhs.ftCreationTime.dwLowDateTime;
|
||||
|
||||
return lhs.ftCreationTime.dwHighDateTime < rhs.ftCreationTime.dwHighDateTime;
|
||||
}
|
||||
};
|
||||
|
||||
files.sort(olderthan());
|
||||
|
||||
std::list<WIN32_FIND_DATA>::iterator iter = files.begin();
|
||||
|
||||
for (size_t count = filesRemaining; count<files.size(); iter++, count++)
|
||||
{
|
||||
::DeleteFile(path + (*iter).cFileName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
MainLogManager::~MainLogManager()
|
||||
{
|
||||
RBX::mutex::scoped_lock lock(fastLogChannelsLock);
|
||||
|
||||
FLog::SetExternalLogFunc(NULL);
|
||||
|
||||
for(std::size_t i = 0; i < fastLogChannels.size(); i++)
|
||||
delete fastLogChannels[i];
|
||||
|
||||
mainLogManager = NULL;
|
||||
}
|
||||
|
||||
|
||||
LogManager::~LogManager()
|
||||
{
|
||||
if (log != NULL)
|
||||
{
|
||||
std::string logFile = log->logFile;
|
||||
delete log; // this will close the file so that we can move it
|
||||
log = NULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
inline HRESULT WINAPI RbxReportError(const CLSID& clsid, LPCSTR lpszDesc,
|
||||
DWORD dwHelpID, LPCSTR lpszHelpFile, const IID& iid = GUID_NULL, HRESULT hRes = 0)
|
||||
{
|
||||
ATLASSERT(lpszDesc != NULL);
|
||||
if (lpszDesc == NULL)
|
||||
return E_POINTER;
|
||||
USES_CONVERSION_EX;
|
||||
CComBSTR desc = CString(lpszDesc);
|
||||
if(desc == NULL)
|
||||
return E_OUTOFMEMORY;
|
||||
|
||||
CComBSTR helpFile = NULL;
|
||||
if(lpszHelpFile != NULL)
|
||||
{
|
||||
helpFile = CString(lpszHelpFile);
|
||||
if(helpFile == NULL)
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
return AtlSetErrorInfo(clsid, desc.Detach(), dwHelpID, helpFile.Detach(), iid, hRes, NULL);
|
||||
}
|
||||
|
||||
|
||||
inline HRESULT WINAPI RbxReportError(const CLSID& clsid, UINT nID, const IID& iid = GUID_NULL,
|
||||
HRESULT hRes = 0, HINSTANCE hInst = _AtlBaseModule.GetResourceInstance())
|
||||
{
|
||||
return AtlSetErrorInfo(clsid, (LPCOLESTR)MAKEINTRESOURCE(nID), 0, NULL, iid, hRes, hInst);
|
||||
}
|
||||
|
||||
inline HRESULT WINAPI RbxReportError(const CLSID& clsid, UINT nID, DWORD dwHelpID,
|
||||
LPCOLESTR lpszHelpFile, const IID& iid = GUID_NULL, HRESULT hRes = 0,
|
||||
HINSTANCE hInst = _AtlBaseModule.GetResourceInstance())
|
||||
{
|
||||
return AtlSetErrorInfo(clsid, (LPCOLESTR)MAKEINTRESOURCE(nID), dwHelpID,
|
||||
lpszHelpFile, iid, hRes, hInst);
|
||||
}
|
||||
|
||||
inline HRESULT WINAPI RbxReportError(const CLSID& clsid, LPCSTR lpszDesc,
|
||||
const IID& iid = GUID_NULL, HRESULT hRes = 0)
|
||||
{
|
||||
return RbxReportError(clsid, lpszDesc, 0, NULL, iid, hRes);
|
||||
}
|
||||
|
||||
inline HRESULT WINAPI RbxReportError(const CLSID& clsid, LPCOLESTR lpszDesc,
|
||||
const IID& iid = GUID_NULL, HRESULT hRes = 0)
|
||||
{
|
||||
return AtlSetErrorInfo(clsid, lpszDesc, 0, NULL, iid, hRes, NULL);
|
||||
}
|
||||
|
||||
inline HRESULT WINAPI RbxReportError(const CLSID& clsid, LPCOLESTR lpszDesc, DWORD dwHelpID,
|
||||
LPCOLESTR lpszHelpFile, const IID& iid = GUID_NULL, HRESULT hRes = 0)
|
||||
{
|
||||
return AtlSetErrorInfo(clsid, lpszDesc, dwHelpID, lpszHelpFile, iid, hRes, NULL);
|
||||
}
|
||||
|
||||
HRESULT LogManager::ReportCOMError(const CLSID& clsid, LPCOLESTR lpszDesc, HRESULT hRes)
|
||||
{
|
||||
LogManager::ReportEvent(EVENTLOG_ERROR_TYPE, CString(lpszDesc));
|
||||
return RbxReportError(clsid, lpszDesc, GUID_NULL, hRes);
|
||||
}
|
||||
|
||||
HRESULT LogManager::ReportCOMError(const CLSID& clsid, LPCSTR lpszDesc, HRESULT hRes)
|
||||
{
|
||||
LogManager::ReportEvent(EVENTLOG_ERROR_TYPE, lpszDesc);
|
||||
return RbxReportError(clsid, lpszDesc, GUID_NULL, hRes);
|
||||
}
|
||||
|
||||
HRESULT LogManager::ReportCOMError(const CLSID& clsid, HRESULT hRes)
|
||||
{
|
||||
std::string message = RBX::format("HRESULT 0x%X", hRes);
|
||||
LogManager::ReportEvent(EVENTLOG_ERROR_TYPE, message.c_str());
|
||||
return RbxReportError(clsid, message.c_str(), GUID_NULL, hRes);
|
||||
}
|
||||
|
||||
#ifdef _MFC_VER
|
||||
HRESULT LogManager::ReportCOMError(const CLSID& clsid, CException* exception)
|
||||
{
|
||||
CString fullError;
|
||||
HRESULT hr = COleException::Process(exception);
|
||||
CString sError;
|
||||
if (exception->GetErrorMessage(sError.GetBuffer(1024), 1023))
|
||||
{
|
||||
sError.ReleaseBuffer();
|
||||
fullError.Format("%s (0x%X)", sError, hr);
|
||||
}
|
||||
else
|
||||
fullError.Format("Error 0x%X", hr);
|
||||
|
||||
LogManager::ReportEvent(EVENTLOG_ERROR_TYPE, fullError);
|
||||
return RbxReportError(clsid, fullError, GUID_NULL, hr);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool MainLogManager::handleG3DDebugAssert(
|
||||
const char* _expression,
|
||||
const std::string& message,
|
||||
const char* filename,
|
||||
int lineNumber,
|
||||
bool useGuiPrompt)
|
||||
{
|
||||
return handleDebugAssert(_expression,filename,lineNumber);
|
||||
}
|
||||
|
||||
bool MainLogManager::handleDebugAssert(
|
||||
const char* expression,
|
||||
const char* filename,
|
||||
int lineNumber
|
||||
)
|
||||
{
|
||||
CString eventMessage;
|
||||
eventMessage.Format("Assertion failed: %s\n%s(%d)", expression, filename, lineNumber);
|
||||
LogManager::ReportEvent(EVENTLOG_WARNING_TYPE, eventMessage);
|
||||
#ifdef _DEBUG
|
||||
RBXCRASH();
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool MainLogManager::handleG3DFailure(
|
||||
const char* _expression,
|
||||
const std::string& message,
|
||||
const char* filename,
|
||||
int lineNumber,
|
||||
bool useGuiPrompt)
|
||||
{
|
||||
return handleFailure(_expression, filename, lineNumber);
|
||||
}
|
||||
bool MainLogManager::handleFailure(
|
||||
const char* expression,
|
||||
const char* filename,
|
||||
int lineNumber
|
||||
)
|
||||
{
|
||||
CString eventMessage;
|
||||
eventMessage.Format("G3D Error: %s\n%s(%d)", expression, filename, lineNumber);
|
||||
LogManager::ReportEvent(EVENTLOG_ERROR_TYPE, eventMessage);
|
||||
#ifdef _DEBUG
|
||||
_CrtDbgBreak();
|
||||
#endif
|
||||
// Cause a crash
|
||||
RBXCRASH();
|
||||
return false;
|
||||
}
|
||||
|
||||
HRESULT LogManager::ReportExceptionAsCOMError(const CLSID& clsid, std::exception const& exp)
|
||||
{
|
||||
return ReportCOMError(clsid, exp.what());
|
||||
}
|
||||
|
||||
void LogManager::ReportException(std::exception const& exp)
|
||||
{
|
||||
RBX::StandardOut::singleton()->print(RBX::MESSAGE_ERROR, exp);
|
||||
}
|
||||
|
||||
void LogManager::ReportLastError(LPCSTR message)
|
||||
{
|
||||
DWORD error = GetLastError();
|
||||
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_ERROR, "%s, GetLastError=%d", message, error);
|
||||
}
|
||||
|
||||
void LogManager::ReportEvent(WORD type, LPCSTR message)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case EVENTLOG_SUCCESS:
|
||||
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_INFO, "%s", message);
|
||||
break;
|
||||
case EVENTLOG_ERROR_TYPE:
|
||||
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_ERROR, "%s", message);
|
||||
break;
|
||||
case EVENTLOG_INFORMATION_TYPE:
|
||||
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_INFO, "%s", message);
|
||||
break;
|
||||
case EVENTLOG_AUDIT_SUCCESS:
|
||||
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_INFO, "%s", message);
|
||||
break;
|
||||
case EVENTLOG_AUDIT_FAILURE:
|
||||
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_ERROR, "%s", message);
|
||||
break;
|
||||
}
|
||||
#ifdef _DEBUG
|
||||
switch (type)
|
||||
{
|
||||
case EVENTLOG_SUCCESS:
|
||||
ATLTRACE("EVENTLOG_SUCCESS %s\n", message);
|
||||
break;
|
||||
case EVENTLOG_ERROR_TYPE:
|
||||
ATLTRACE("EVENTLOG_ERROR_TYPE %s\n", message);
|
||||
break;
|
||||
case EVENTLOG_INFORMATION_TYPE:
|
||||
ATLTRACE("EVENTLOG_INFORMATION_TYPE %s\n", message);
|
||||
break;
|
||||
case EVENTLOG_AUDIT_SUCCESS:
|
||||
ATLTRACE("EVENTLOG_AUDIT_SUCCESS %s\n", message);
|
||||
break;
|
||||
case EVENTLOG_AUDIT_FAILURE:
|
||||
ATLTRACE("EVENTLOG_AUDIT_FAILURE %s\n", message);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void LogManager::ReportEvent(WORD type, LPCSTR message, LPCSTR fileName, int lineNumber)
|
||||
{
|
||||
CString m;
|
||||
m.Format("%s\n%s(%d)", message, fileName, lineNumber);
|
||||
LogManager::ReportEvent(type, m);
|
||||
}
|
||||
|
||||
#ifdef _MFC_VER
|
||||
void LogManager::ReportEvent(WORD type, HRESULT hr, LPCSTR fileName, int lineNumber)
|
||||
{
|
||||
COleException e;
|
||||
e.m_sc = hr;
|
||||
TCHAR s[1024];
|
||||
e.GetErrorMessage(s, 1024);
|
||||
|
||||
CString m;
|
||||
m.Format("HRESULT = %d: %s\n%s(%d)", hr, s, fileName, lineNumber);
|
||||
LogManager::ReportEvent(type, m);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
namespace log_detail
|
||||
{
|
||||
boost::once_flag once_init = BOOST_ONCE_INIT;
|
||||
static boost::thread_specific_ptr<ThreadLogManager>* ts;
|
||||
void init(void)
|
||||
{
|
||||
static boost::thread_specific_ptr<ThreadLogManager> value;
|
||||
ts = &value;
|
||||
}
|
||||
}
|
||||
|
||||
ThreadLogManager* ThreadLogManager::getCurrent()
|
||||
{
|
||||
boost::call_once(log_detail::init, log_detail::once_init);
|
||||
ThreadLogManager* logManager = log_detail::ts->get();
|
||||
if (!logManager)
|
||||
{
|
||||
logManager = new ThreadLogManager();
|
||||
log_detail::ts->reset(logManager);
|
||||
}
|
||||
return logManager;
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
#pragma once
|
||||
|
||||
#include "rbx/Log.h"
|
||||
#include "rbx/boost.hpp"
|
||||
#include "Util/Exception.h"
|
||||
#include "atlpath.h"
|
||||
#include <atlutil.h>
|
||||
#include "network/CrashReporter.h"
|
||||
#include "boost/scoped_ptr.hpp"
|
||||
#include <vector>
|
||||
#include "rbx/threadsafe.h"
|
||||
|
||||
|
||||
|
||||
class LogManager
|
||||
{
|
||||
RBX::Log* log;
|
||||
static bool logsEnabled;
|
||||
protected:
|
||||
const DWORD threadID;
|
||||
std::string name;
|
||||
static class MainLogManager* mainLogManager;
|
||||
public:
|
||||
RBX::Log* getLog();
|
||||
|
||||
static MainLogManager* getMainLogManager();
|
||||
#ifdef _MFC_VER
|
||||
static HRESULT ReportCOMError(const CLSID& clsid, CException* exception);
|
||||
#endif
|
||||
static HRESULT ReportCOMError(const CLSID& clsid, HRESULT hRes);
|
||||
static HRESULT ReportCOMError(const CLSID& clsid, LPCOLESTR lpszDesc, HRESULT hRes = 0);
|
||||
static HRESULT ReportCOMError(const CLSID& clsid, LPCSTR lpszDesc, HRESULT hRes = 0);
|
||||
static HRESULT ReportExceptionAsCOMError(const CLSID& clsid, std::exception const& exp);
|
||||
static void ReportException(std::exception const& exp);
|
||||
static void ReportLastError(LPCSTR message);
|
||||
static void ReportEvent(WORD type, LPCSTR message);
|
||||
static void ReportEvent(WORD type, LPCSTR message, LPCSTR fileName, int lineNumber);
|
||||
static void ReportEvent(WORD type, HRESULT hr, LPCSTR fileName, int lineNumber);
|
||||
|
||||
const ATL::CPath& GetLogPath() const;
|
||||
|
||||
virtual ~LogManager();
|
||||
|
||||
virtual std::string getLogFileName() = 0;
|
||||
|
||||
protected:
|
||||
LogManager(const char* name):log(NULL),name(name),threadID(GetCurrentThreadId()) {};
|
||||
};
|
||||
|
||||
|
||||
class RobloxCrashReporter : public CrashReporter
|
||||
{
|
||||
public:
|
||||
static bool silent;
|
||||
RobloxCrashReporter(const char* outputPath, const char* appName, const char* crashExtention);
|
||||
LONG ProcessException(struct _EXCEPTION_POINTERS *info, bool noMsg);
|
||||
protected:
|
||||
/*override*/ void logEvent(const char* msg);
|
||||
};
|
||||
|
||||
class MainLogManager
|
||||
: public RBX::ILogProvider
|
||||
, public LogManager
|
||||
{
|
||||
boost::scoped_ptr<RobloxCrashReporter> crashReporter;
|
||||
std::vector<RBX::Log*> fastLogChannels;
|
||||
static RBX::mutex fastLogChannelsLock;
|
||||
const char* crashExtention;
|
||||
const char* crashEventExtention;
|
||||
public:
|
||||
MainLogManager(LPCTSTR productName, const char* crashExtention, const char* crashEventExtention); // used for main thread
|
||||
~MainLogManager();
|
||||
|
||||
RBX::Log* provideLog();
|
||||
virtual std::string getLogFileName();
|
||||
std::string getFastLogFileName(FLog::Channel channelId);
|
||||
std::string MakeLogFileName(const char* postfix);
|
||||
|
||||
bool hasErrorLogs() const;
|
||||
bool hasCrashLogs(std::string extension) const;
|
||||
// Move crash logs to the archive and return the file paths
|
||||
std::vector<std::string> gatherCrashLogs();
|
||||
std::vector<std::string> gatherAssociatedLogs(const std::string& filenamepattern);
|
||||
void CullLogs(const char* folder, int filesRemaining);
|
||||
|
||||
std::vector<std::string> gatherScriptCrashLogs();
|
||||
|
||||
void WriteCrashDump();
|
||||
|
||||
// triggers upload of log files on next start.
|
||||
bool CreateFakeCrashDump();
|
||||
|
||||
void NotifyFGThreadAlive(); // for deadlock reporting. call every second.
|
||||
void DisableHangReporting();
|
||||
|
||||
void EnableImmediateCrashUpload(bool enabled);
|
||||
|
||||
// returns HEX string that will be part of all the log/dumps output for this session.
|
||||
std::string getSessionId();
|
||||
|
||||
std::string getCrashEventName();
|
||||
|
||||
static void fastLogMessage(FLog::Channel id, const char* message);
|
||||
|
||||
enum GameState
|
||||
{
|
||||
UN_INITIALIZED = 0,
|
||||
IN_GAME,
|
||||
LEAVE_GAME
|
||||
};
|
||||
GameState getGameState() { return gameState; }
|
||||
void setGameLoaded() { gameState = GameState::IN_GAME; }
|
||||
void setLeaveGame() { gameState = GameState::LEAVE_GAME; };
|
||||
|
||||
private:
|
||||
|
||||
GameState gameState;
|
||||
std::string guid;
|
||||
static bool handleDebugAssert(
|
||||
const char* expression,
|
||||
const char* filename,
|
||||
int lineNumber
|
||||
);
|
||||
static bool handleFailure(
|
||||
const char* expression,
|
||||
const char* filename,
|
||||
int lineNumber
|
||||
);
|
||||
|
||||
static bool handleG3DFailure(
|
||||
const char* _expression,
|
||||
const std::string& message,
|
||||
const char* filename,
|
||||
int lineNumber,
|
||||
/*bool& ignoreAlways,*/
|
||||
bool useGuiPrompt);
|
||||
|
||||
static bool handleG3DDebugAssert(
|
||||
const char* _expression,
|
||||
const std::string& message,
|
||||
const char* filename,
|
||||
int lineNumber,
|
||||
/*bool& ignoreAlways,*/
|
||||
bool useGuiPrompt);
|
||||
|
||||
std::vector<std::string> getRecentCseFiles();
|
||||
};
|
||||
|
||||
class ThreadLogManager
|
||||
: public LogManager
|
||||
{
|
||||
ThreadLogManager();
|
||||
public:
|
||||
static ThreadLogManager* getCurrent();
|
||||
virtual ~ThreadLogManager();
|
||||
protected:
|
||||
virtual std::string getLogFileName();
|
||||
};
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
class CProcessInformation
|
||||
{
|
||||
public:
|
||||
PROCESS_INFORMATION pi;
|
||||
CProcessInformation()
|
||||
{
|
||||
pi.hThread = 0;
|
||||
pi.hProcess = 0;
|
||||
}
|
||||
operator PROCESS_INFORMATION& () { return pi; }
|
||||
operator PROCESS_INFORMATION* () { return π }
|
||||
~CProcessInformation()
|
||||
{
|
||||
CloseProcess();
|
||||
}
|
||||
DWORD WaitForSingleObject(DWORD timeout) { return ::WaitForSingleObject(pi.hProcess, timeout); }
|
||||
bool GetExitCode(DWORD& exitCode) const { return ::GetExitCodeProcess(pi.hProcess, &exitCode)==TRUE; }
|
||||
void CloseProcess()
|
||||
{
|
||||
if(InUse()){
|
||||
CloseHandle(pi.hThread);
|
||||
CloseHandle(pi.hProcess);
|
||||
pi.hThread = 0;
|
||||
pi.hProcess = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool InUse()
|
||||
{
|
||||
return pi.hThread != NULL || pi.hProcess != NULL;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
/* Copyright 2003-2007 ROBLOX Corporation, All Rights Reserved */
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#undef min
|
||||
#undef max
|
||||
|
||||
#include "ScriptErrorUploader.h"
|
||||
#include "LogManager.h"
|
||||
|
||||
#include "util/http.h"
|
||||
#include "util/StandardOut.h"
|
||||
|
||||
namespace io = boost::iostreams;
|
||||
|
||||
void ScriptErrorUploader::Upload(std::string url)
|
||||
{
|
||||
HANDLE hMutex;
|
||||
|
||||
hMutex = CreateMutex(
|
||||
NULL, // default security descriptor
|
||||
TRUE, // own the mutex
|
||||
TEXT("RobloxCrashScriptErrorUploaderMutex")); // object name
|
||||
|
||||
if (hMutex == NULL)
|
||||
{
|
||||
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_ERROR, "CreateMutex error: %d\n", GetLastError() );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( GetLastError() == ERROR_ALREADY_EXISTS )
|
||||
{
|
||||
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_INFO, "RobloxCrashScriptErrorUploaderMutex already exists. Not uploading logs.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> f = MainLogManager::getMainLogManager()->gatherScriptCrashLogs();
|
||||
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(_data->sync);
|
||||
_data->url = url;
|
||||
_data->hInterprocessMutex = hMutex;
|
||||
for (size_t i = 0; i<f.size(); ++i)
|
||||
_data->files.push(f[i]);
|
||||
}
|
||||
if (thread)
|
||||
{
|
||||
thread->wake();
|
||||
}
|
||||
else
|
||||
{
|
||||
while (run(_data)!=RBX::worker_thread::done)
|
||||
{}
|
||||
}
|
||||
}
|
||||
|
||||
RBX::worker_thread::work_result ScriptErrorUploader::run(shared_ptr<data> _cseData)
|
||||
{
|
||||
std::string file;
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(_cseData->sync);
|
||||
if (_cseData->files.empty())
|
||||
{
|
||||
if(_cseData->hInterprocessMutex)
|
||||
{
|
||||
CloseHandle(_cseData->hInterprocessMutex);
|
||||
_cseData->hInterprocessMutex = NULL;
|
||||
}
|
||||
return RBX::worker_thread::done;
|
||||
}
|
||||
file = _cseData->files.front();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
printf("Uploading %s\n", file.c_str());
|
||||
bool isCseFile = file.substr(file.size()-4)==".cse";
|
||||
if (isCseFile)
|
||||
_cseData->dmpFileCount++;
|
||||
|
||||
// TODO: put the filename in the header rather than the query string??
|
||||
std::string url = _cseData->url;
|
||||
url += "?filename=";
|
||||
url += file;
|
||||
|
||||
if (isCseFile && _cseData->dmpFileCount>3)
|
||||
{
|
||||
std::stringstream data("Too many cse files");
|
||||
std::string response;
|
||||
RBX::Http(url).post(data, RBX::Http::kContentTypeDefaultUnspecified, true, response);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::fstream data(file.c_str(), std::ios_base::in | std::ios_base::binary);
|
||||
size_t begin = data.tellg();
|
||||
data.seekg (0, std::ios::end);
|
||||
size_t end = data.tellg();
|
||||
if (end > begin)
|
||||
{
|
||||
data.seekg (0, std::ios::beg);
|
||||
std::string response;
|
||||
RBX::Http(url).post(data, RBX::Http::kContentTypeDefaultUnspecified, true, response);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Some cse files are empty. Post it anyway so that we can report it
|
||||
std::stringstream data("Empty!!!");
|
||||
std::string response;
|
||||
RBX::Http(url).post(data, RBX::Http::kContentTypeDefaultUnspecified, true, response);
|
||||
}
|
||||
}
|
||||
|
||||
ErrorUploader::MoveRelative(file.c_str(), "archive\\");
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
RBX::StandardOut::singleton()->print(RBX::MESSAGE_ERROR, e);
|
||||
}
|
||||
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(_cseData->sync);
|
||||
_cseData->files.pop();
|
||||
}
|
||||
return RBX::worker_thread::more;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "ErrorUploader.h"
|
||||
|
||||
class ScriptErrorUploader : public ErrorUploader
|
||||
{
|
||||
public:
|
||||
ScriptErrorUploader(bool backgroundUpload)
|
||||
{
|
||||
if (backgroundUpload)
|
||||
thread.reset(new RBX::worker_thread(boost::bind(&ScriptErrorUploader::run, _data), "ScriptErrorUploader"));
|
||||
}
|
||||
/*override*/ void Upload(std::string url);
|
||||
private:
|
||||
static RBX::worker_thread::work_result run(shared_ptr<data> _cseData);
|
||||
|
||||
};
|
||||
|
||||
@@ -0,0 +1,631 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "SharedLauncher.h"
|
||||
#include "vistatools.h"
|
||||
#include "atlutil.h"
|
||||
#include "wininet.h"
|
||||
#include "atlsync.h"
|
||||
#include "ProcessInformation.h"
|
||||
#endif
|
||||
|
||||
#define ROBLOXREGKEY "RobloxReg" // Can't use "Roblox" because it is used by the old legacy installer
|
||||
#define STUDIOQTROBLOXREG "StudioQTRobloxReg" // Technical debt we need to make this two names globally unique and shared
|
||||
|
||||
//Pull in the win32 version Library
|
||||
#pragma comment(lib, "version.lib")
|
||||
|
||||
namespace SharedLauncher
|
||||
{
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
static void CheckResult(HRESULT hr)
|
||||
{
|
||||
if(FAILED(hr))
|
||||
AtlThrow(hr);
|
||||
}
|
||||
|
||||
CRegKey GetKey(CString& out_operation, bool isStudioKey, bool is64bits)
|
||||
{
|
||||
CRegKey key;
|
||||
|
||||
// Very important to do KEY_READ - DON'T WRITE TO VIRTUAL REGISTRY!!!!
|
||||
REGSAM regSam = KEY_READ;
|
||||
regSam |= is64bits ? KEY_WOW64_64KEY : KEY_WOW64_32KEY;
|
||||
|
||||
if ( !IsVistaPlus() || !IsElevated() )
|
||||
{
|
||||
// First check HKCU then try HKLM
|
||||
if ( isStudioKey )
|
||||
{
|
||||
out_operation = _T("Open HKEY_CURRENT_USER\\Software\\") _T(STUDIOQTROBLOXREG) _T(" key");
|
||||
if ( SUCCEEDED(key.Open(HKEY_CURRENT_USER, _T("Software\\") _T(STUDIOQTROBLOXREG), regSam)) && key.m_hKey )
|
||||
return key;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_operation = _T("Open HKEY_CURRENT_USER\\Software\\") _T(ROBLOXREGKEY) _T(" key");
|
||||
if ( SUCCEEDED(key.Open(HKEY_CURRENT_USER, _T("Software\\") _T(ROBLOXREGKEY), regSam)) && key.m_hKey )
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
// fallback to HKLM (used by "WinXP SP1" and "UAC off")
|
||||
if ( isStudioKey )
|
||||
{
|
||||
out_operation = _T("Open HKEY_LOCAL_MACHINE\\Software\\") _T(STUDIOQTROBLOXREG) _T(" key");
|
||||
CheckResult(key.Open(HKEY_LOCAL_MACHINE, _T("Software\\") _T(STUDIOQTROBLOXREG), regSam));
|
||||
}
|
||||
else
|
||||
{
|
||||
out_operation = _T("Open HKEY_LOCAL_MACHINE\\Software\\") _T(ROBLOXREGKEY) _T(" key");
|
||||
CheckResult(key.Open(HKEY_LOCAL_MACHINE, _T("Software\\") _T(ROBLOXREGKEY), regSam));
|
||||
}
|
||||
|
||||
if ( !key.m_hKey )
|
||||
AtlThrow(E_FAIL);
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
class CHINTERNET
|
||||
{
|
||||
HINTERNET handle;
|
||||
public:
|
||||
CHINTERNET(HINTERNET handle):handle(handle) {}
|
||||
CHINTERNET():handle(0) {}
|
||||
CHINTERNET& operator = (HINTERNET handle)
|
||||
{
|
||||
::InternetCloseHandle(handle);
|
||||
this->handle = handle;
|
||||
return *this;
|
||||
}
|
||||
operator bool() { return handle!=0; }
|
||||
operator HINTERNET() { return handle; }
|
||||
~CHINTERNET()
|
||||
{
|
||||
::InternetCloseHandle(handle);
|
||||
}
|
||||
};
|
||||
|
||||
static bool IsRunningVistaIE()
|
||||
{
|
||||
//return if not Windows Vista or later
|
||||
OSVERSIONINFO osvi = {0};
|
||||
osvi.dwOSVersionInfoSize=sizeof(osvi);
|
||||
GetVersionEx (&osvi);
|
||||
if(osvi.dwMajorVersion<6)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
static CString authenticate(BSTR authenticationUrl)
|
||||
{
|
||||
if (!IsRunningVistaIE())
|
||||
return _T("");
|
||||
|
||||
//if (!Http::trustCheck(url.c_str()))
|
||||
// throw std::runtime_error(G3D::format("trust check failed for %s", url.c_str()));
|
||||
|
||||
CUrl u;
|
||||
u.CrackUrl(CString(authenticationUrl));
|
||||
|
||||
if (u.GetHostNameLength()==0)
|
||||
return _T("");
|
||||
|
||||
// Initialize the User Agent
|
||||
CHINTERNET session = InternetOpen(_T("RobloxProxy"), PRE_CONFIG_INTERNET_ACCESS, NULL, NULL, 0);
|
||||
CHINTERNET connection = ::InternetConnect(session, u.GetHostName(), u.GetPortNumber(), u.GetUserName(), u.GetPassword(), INTERNET_SERVICE_HTTP, 0, 1);
|
||||
|
||||
// 1. Open HTTP Request (pass method type [get/post/..] and URL path (except server name))
|
||||
CString s = u.GetUrlPath();
|
||||
s += u.GetExtraInfo();
|
||||
CHINTERNET request = ::HttpOpenRequest(
|
||||
connection, _T("GET"), s, NULL, NULL, NULL,
|
||||
INTERNET_FLAG_KEEP_CONNECTION |
|
||||
INTERNET_FLAG_EXISTING_CONNECT |
|
||||
INTERNET_FLAG_NEED_FILE, // ensure that it gets cached
|
||||
1);
|
||||
if (!request)
|
||||
return _T("");
|
||||
|
||||
CString additionalHeaders = _T("RBXAuthenticationNegotiation:");
|
||||
additionalHeaders += u.GetHostName();
|
||||
additionalHeaders += "\r\n";
|
||||
if (!::HttpAddRequestHeaders(request, (LPCTSTR)additionalHeaders, additionalHeaders.GetLength(), HTTP_ADDREQ_FLAG_ADD))
|
||||
return _T("");
|
||||
|
||||
if (!HttpSendRequest(request, NULL, 0, 0, 0))
|
||||
return _T("");
|
||||
|
||||
// Check the return HTTP Status Code
|
||||
DWORD statusCode;
|
||||
{
|
||||
TCHAR szBuffer[80];
|
||||
DWORD dwLen = _countof(szBuffer);
|
||||
if (!HttpQueryInfo(request, HTTP_QUERY_STATUS_CODE, szBuffer, &dwLen, NULL))
|
||||
return _T("");
|
||||
statusCode = (DWORD) _ttol(szBuffer);
|
||||
}
|
||||
|
||||
DWORD numBytes;
|
||||
if (!::InternetQueryDataAvailable(request, &numBytes, 0, 0))
|
||||
numBytes = 0;
|
||||
if (numBytes==0)
|
||||
return _T("");
|
||||
|
||||
if (statusCode!=HTTP_STATUS_OK)
|
||||
return _T("");
|
||||
|
||||
DWORD bytesRead;
|
||||
TCHAR ticket[2048];
|
||||
if (!::InternetReadFile(request, (LPVOID) ticket, 2048, &bytesRead))
|
||||
return _T("");
|
||||
|
||||
ticket[bytesRead] = 0;
|
||||
return ticket;
|
||||
}
|
||||
|
||||
static ATL::CPath loadRobloxPath(CString& out_operation, bool isStudio)
|
||||
{
|
||||
ATL::CPath path;
|
||||
CRegKey key = GetKey(out_operation, isStudio);
|
||||
out_operation = "Query Roblox default key";
|
||||
DWORD length = _MAX_PATH+1;
|
||||
CheckResult(key.QueryStringValue(NULL,path.m_strPath.GetBuffer(length),&length));
|
||||
path.m_strPath.ReleaseBuffer();
|
||||
return path;
|
||||
}
|
||||
|
||||
static void launchRoblox(TCHAR cmd[2048])
|
||||
{
|
||||
CProcessInformation pi;
|
||||
STARTUPINFO si = {0};
|
||||
si.cb = sizeof(si);
|
||||
if (!::CreateProcess(NULL, cmd, NULL, NULL, false, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, pi))
|
||||
AtlThrowLastWin32();
|
||||
}
|
||||
|
||||
template<class CHARTYPE>
|
||||
HRESULT StartGame(simple_logger<CHARTYPE> &logger, BSTR authenTicket, BSTR authenticationUrl, BSTR script, const CLSID& clsid, bool silentMode, TCHAR *guidName, bool startInHiddenMode, TCHAR *unhideEventName, LaunchMode launchMode)
|
||||
{
|
||||
CString operation;
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
try
|
||||
{
|
||||
if ( wcslen(authenTicket) == 0)
|
||||
{
|
||||
operation = "Authenticate";
|
||||
// TODO: Nuke this???
|
||||
CComBSTR authenticationTicket = CComBSTR(authenticate(authenticationUrl));
|
||||
authenTicket = (BSTR)authenticationTicket;
|
||||
}
|
||||
bool isStudioLaunch = launchMode == Edit || launchMode == Build;
|
||||
|
||||
ATL::CPath path;
|
||||
|
||||
try
|
||||
{
|
||||
path = loadRobloxPath(operation, isStudioLaunch);
|
||||
}
|
||||
catch( CAtlException )
|
||||
{
|
||||
}
|
||||
|
||||
// If studio doesn't exist (they've uninstalled), try launching in play mode
|
||||
if ( (path.m_strPath.IsEmpty() || !path.FileExists()) && isStudioLaunch )
|
||||
{
|
||||
path = loadRobloxPath(operation, false);
|
||||
launchMode = Play;
|
||||
}
|
||||
|
||||
operation = "Start Roblox.exe";
|
||||
|
||||
TCHAR cmd[2048] = {0};
|
||||
|
||||
// Edit and build mode both launch studio. Studio handles arguments differently from player
|
||||
// Thus you see the formatting in here different than in the else statement
|
||||
if (launchMode == Edit || launchMode == Build)
|
||||
{
|
||||
EditArgs editArgs;
|
||||
|
||||
// For now all web launching should launch build mode, not edit.
|
||||
// TODO: Have web launch Build vs EDIT ?
|
||||
launchMode = Build;
|
||||
|
||||
editArgs.launchMode = launchMode == Build ? BuildArgument : IDEArgument;
|
||||
editArgs.script = convert_w2s(script);
|
||||
editArgs.authUrl = convert_w2s(authenticationUrl);
|
||||
editArgs.authTicket = convert_w2s(authenTicket);
|
||||
editArgs.avatarMode = launchMode == Build ? AvatarModeArgument : ""; // build and play solo have an avatar, edit does not
|
||||
|
||||
#ifdef UNICODE
|
||||
if ( silentMode )
|
||||
editArgs.readyEvent = convert_w2s(guidName);
|
||||
if ( startInHiddenMode )
|
||||
editArgs.showEventName = convert_w2s(unhideEventName);
|
||||
#else
|
||||
if ( silentMode )
|
||||
editArgs.readyEvent = guidName;
|
||||
if ( startInHiddenMode )
|
||||
editArgs.showEventName = unhideEventName;
|
||||
#endif
|
||||
|
||||
std::wstring editCommandLine = generateEditCommandLine(editArgs);
|
||||
|
||||
#ifdef UNICODE
|
||||
swprintf_s(
|
||||
cmd,
|
||||
2048,
|
||||
_T("\"%s\" %s"),
|
||||
(LPCTSTR)path.m_strPath,
|
||||
(LPCTSTR)editCommandLine.c_str() );
|
||||
#else
|
||||
sprintf_s(
|
||||
cmd,
|
||||
2048,
|
||||
_T("\"%S\" %S"),
|
||||
(LPCTSTR)path.m_strPath,
|
||||
(LPCTSTR)editCommandLine.c_str() );
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
BSTR launchType;
|
||||
launchType = SysAllocString(L"-play");
|
||||
#ifdef UNICODE
|
||||
swprintf(cmd, 2048, _T("\"%s\" %s %s %s %s %s %s"), (LPCTSTR)path.m_strPath, launchType, script, authenticationUrl, authenTicket, silentMode ? guidName : _T("nonSilent"), startInHiddenMode ? unhideEventName : _T("nonHidden"));
|
||||
#else
|
||||
sprintf_s(cmd, 2048, _T("\"%s\" %S %S %S %S %s %s"), (LPCTSTR)path.m_strPath, launchType, script, authenticationUrl, authenTicket, silentMode ? guidName : _T("nonSilent"), startInHiddenMode ? unhideEventName : _T("nonHidden"));
|
||||
#endif
|
||||
}
|
||||
|
||||
logger.write_logentry("Final cmd = %s, unideEventName = %s, hidden = %d", cmd, unhideEventName, startInHiddenMode);
|
||||
|
||||
operation = cmd;
|
||||
launchRoblox(cmd);
|
||||
}
|
||||
catch( CAtlException e )
|
||||
{
|
||||
logger.write_logentry("SharedLauncher::StartGame try failed, in catch");
|
||||
hr = e.m_hr;
|
||||
}
|
||||
if (FAILED(hr))
|
||||
{
|
||||
CString message;
|
||||
message.Format(_T("%s (hr=0x%8.8x). operation: %s"), (LPCTSTR)::AtlGetErrorDescription(hr), hr, (LPCTSTR)operation);
|
||||
logger.write_logentry("SharedLauncher::StartGame try failed, error: %S", message.GetString());
|
||||
return ::AtlReportError(clsid, message, GUID_NULL, hr);
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT StartGame(simple_logger<char> &logger, BSTR authenTicket, BSTR authenticationUrl, BSTR script, const CLSID& clsid, bool silentMode, TCHAR *guidName, bool startInHiddenMode, TCHAR *unhideEventName, LaunchMode launchMode)
|
||||
{
|
||||
return StartGame<char>(logger, authenTicket, authenticationUrl, script, clsid, silentMode, guidName, startInHiddenMode, unhideEventName, launchMode);
|
||||
}
|
||||
|
||||
HRESULT StartGame(simple_logger<wchar_t> &logger, BSTR authenTicket, BSTR authenticationUrl, BSTR script, const CLSID& clsid, bool silentMode, TCHAR *guidName, bool startInHiddenMode, TCHAR *unhideEventName, LaunchMode launchMode)
|
||||
{
|
||||
return StartGame<wchar_t>(logger, authenTicket, authenticationUrl, script, clsid, silentMode, guidName, startInHiddenMode, unhideEventName, launchMode);
|
||||
}
|
||||
|
||||
HRESULT PreStartGame(const CLSID& clsid)
|
||||
{
|
||||
CString operation;
|
||||
HRESULT hr = S_OK;
|
||||
try
|
||||
{
|
||||
ATL::CPath path = loadRobloxPath(operation, false);
|
||||
operation = "Start Roblox.exe";
|
||||
|
||||
TCHAR cmd[2048];
|
||||
#ifdef UNICODE
|
||||
swprintf(cmd, 2048, _T("\"%s\" -prePlay"), (LPCTSTR)path.m_strPath);
|
||||
#else
|
||||
sprintf_s(cmd, 2048, "\"%s\" -install", (LPCTSTR)path.m_strPath);
|
||||
#endif
|
||||
operation = cmd;
|
||||
launchRoblox(cmd);
|
||||
}
|
||||
catch( CAtlException e )
|
||||
{
|
||||
hr = e.m_hr;
|
||||
}
|
||||
if (FAILED(hr))
|
||||
{
|
||||
CString message;
|
||||
message.Format(_T("%s (hr=0x%8.8x). operation: %s"), (LPCTSTR)::AtlGetErrorDescription(hr), hr, (LPCTSTR)operation);
|
||||
return ::AtlReportError(clsid, message, GUID_NULL, hr);
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT get_InstallHost(BSTR* pVal, const CLSID& clsid)
|
||||
{
|
||||
CString operation;
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
try
|
||||
{
|
||||
CRegKey key = GetKey(operation, false);
|
||||
operation = _T("Query Software\\") _T(ROBLOXREGKEY) _T(" key");
|
||||
DWORD length = 256;
|
||||
CString host;
|
||||
CheckResult(key.QueryStringValue(_T("install host"), host.GetBuffer(length), &length));
|
||||
host.ReleaseBuffer();
|
||||
CComBSTR result(host);
|
||||
*pVal = result.Detach();
|
||||
}
|
||||
catch( CAtlException e )
|
||||
{
|
||||
hr = e.m_hr;
|
||||
}
|
||||
if (FAILED(hr))
|
||||
{
|
||||
CString message;
|
||||
message.Format(_T("%s (hr=0x%8.8x). operation: %s"), (LPCTSTR)::AtlGetErrorDescription(hr), hr, (LPCTSTR)operation);
|
||||
return ::AtlReportError(clsid, message, GUID_NULL, hr);
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT get_Version(BSTR* pVal, const CLSID& clsid)
|
||||
{
|
||||
|
||||
CString operation;
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
try
|
||||
{
|
||||
CRegKey key = GetKey(operation, false);
|
||||
operation = _T("Query Software\\") _T(ROBLOXREGKEY) _T(" key");
|
||||
DWORD length = 256;
|
||||
CString host;
|
||||
CheckResult(key.QueryStringValue(_T("Plug-in version"), host.GetBuffer(length), &length));
|
||||
host.ReleaseBuffer();
|
||||
if (host.GetLength() == 0)
|
||||
host = "-1";
|
||||
CComBSTR result(host);
|
||||
*pVal = result.Detach();
|
||||
}
|
||||
catch( CAtlException e )
|
||||
{
|
||||
hr = e.m_hr;
|
||||
}
|
||||
if (FAILED(hr))
|
||||
{
|
||||
// return -1 on errors
|
||||
CString sVersion = CString(_T("-1"));
|
||||
CComBSTR result(sVersion);
|
||||
*pVal = result.Detach();
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT Update(const CLSID& clsid)
|
||||
{
|
||||
CString operation;
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
try
|
||||
{
|
||||
operation = "Update Roblox.exe";
|
||||
|
||||
ATL::CPath path;
|
||||
{
|
||||
CRegKey key = GetKey(operation, false);
|
||||
operation = "Query Roblox default key";
|
||||
DWORD length = _MAX_PATH+1;
|
||||
CheckResult(key.QueryStringValue(NULL, path.m_strPath.GetBuffer(length), &length));
|
||||
path.m_strPath.ReleaseBuffer();
|
||||
}
|
||||
|
||||
TCHAR cmd[2048];
|
||||
#ifdef UNICODE
|
||||
swprintf(cmd, 2048, _T("\"%s\" -install"), (LPCTSTR)path.m_strPath);
|
||||
#else
|
||||
sprintf_s(cmd, 2048, _T("\"%s\" -install"), (LPCTSTR)path.m_strPath);
|
||||
#endif
|
||||
operation = cmd;
|
||||
|
||||
CProcessInformation pi;
|
||||
STARTUPINFO si = {0};
|
||||
si.cb = sizeof(si);
|
||||
if (!::CreateProcess(NULL, cmd, NULL, NULL, false, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, pi))
|
||||
AtlThrowLastWin32();
|
||||
}
|
||||
catch( CAtlException e )
|
||||
{
|
||||
hr = e.m_hr;
|
||||
}
|
||||
if (FAILED(hr))
|
||||
{
|
||||
CString message;
|
||||
message.Format(_T("Error code: 0x%x, %s. operation: %s"), hr, (LPCTSTR)::AtlGetErrorDescription(hr), (LPCTSTR)operation);
|
||||
return ::AtlReportError(clsid, message, GUID_NULL, hr);
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
HRESULT get_IsUpToDate(simple_logger<wchar_t> &logger, VARIANT_BOOL* pVal, CProcessInformation& isUpToDateProcessInfo, const CLSID& clsid)
|
||||
{
|
||||
CString operation;
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
(*pVal) = VARIANT_FALSE;
|
||||
try
|
||||
{
|
||||
operation = "IsUpToDate Roblox.exe";
|
||||
|
||||
ATL::CPath path;
|
||||
{
|
||||
CRegKey key = GetKey(operation, false);
|
||||
operation = "Query Roblox default key";
|
||||
DWORD length = _MAX_PATH+1;
|
||||
CheckResult(key.QueryStringValue(NULL, path.m_strPath.GetBuffer(length), &length));
|
||||
path.m_strPath.ReleaseBuffer();
|
||||
}
|
||||
|
||||
TCHAR cmd[2048];
|
||||
#ifdef UNICODE
|
||||
swprintf(cmd, 2048, _T("\"%s\" -failIfNotUpToDate"), (LPCTSTR)path.m_strPath);
|
||||
#else
|
||||
sprintf_s(cmd, 2048, _T("\"%s\" -failIfNotUpToDate"), (LPCTSTR)path.m_strPath);
|
||||
#endif
|
||||
operation = cmd;
|
||||
|
||||
if(!isUpToDateProcessInfo.InUse()){
|
||||
STARTUPINFO si = {0};
|
||||
si.cb = sizeof(si);
|
||||
if (!::CreateProcess(NULL, cmd, NULL, NULL, false, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, isUpToDateProcessInfo))
|
||||
AtlThrowLastWin32();
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.write_logentry("SharedLauncher::get_IsUpToDate process in use");
|
||||
}
|
||||
switch(isUpToDateProcessInfo.WaitForSingleObject(1000)){
|
||||
case WAIT_TIMEOUT:
|
||||
{
|
||||
(*pVal) = VARIANT_FALSE;
|
||||
logger.write_logentry("SharedLauncher::get_IsUpToDate time out");
|
||||
break;
|
||||
}
|
||||
case WAIT_ABANDONED:
|
||||
{
|
||||
(*pVal) = VARIANT_FALSE;
|
||||
isUpToDateProcessInfo.CloseProcess();
|
||||
|
||||
logger.write_logentry("SharedLauncher::get_IsUpToDate abandoned");
|
||||
break;
|
||||
|
||||
}
|
||||
case WAIT_FAILED:
|
||||
{
|
||||
(*pVal) = VARIANT_FALSE;
|
||||
isUpToDateProcessInfo.CloseProcess();
|
||||
|
||||
logger.write_logentry("SharedLauncher::get_IsUpToDate wait failed");
|
||||
//CString message;
|
||||
//message.Format("Failed error=%d\n",GetLastError());
|
||||
//::MessageBox(NULL, message, "Error", MB_OK | MB_ICONEXCLAMATION);
|
||||
|
||||
break;
|
||||
}
|
||||
case WAIT_OBJECT_0:
|
||||
{
|
||||
//Non-zero return value means it is *not* up to date
|
||||
DWORD exitCode;
|
||||
if(isUpToDateProcessInfo.GetExitCode(exitCode)){
|
||||
//CString message;
|
||||
//message.Format("ExitCode = %d\n",exitCode);
|
||||
//::MessageBox(NULL, message, "Error", MB_OK | MB_ICONEXCLAMATION);
|
||||
|
||||
(*pVal) = (exitCode == 0) ? VARIANT_TRUE : VARIANT_FALSE;
|
||||
logger.write_logentry("SharedLauncher::get_IsUpToDate process exit code %d", exitCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.write_logentry("SharedLauncher::get_IsUpToDate failed to get process exit code");
|
||||
}
|
||||
|
||||
isUpToDateProcessInfo.CloseProcess();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( CAtlException e )
|
||||
{
|
||||
hr = e.m_hr;
|
||||
}
|
||||
if (FAILED(hr))
|
||||
{
|
||||
CString message;
|
||||
message.Format(_T("Error code: 0x%x, %s. operation: %s"), hr, (LPCTSTR)::AtlGetErrorDescription(hr), (LPCTSTR)operation);
|
||||
//::MessageBox(NULL, message, "Error", MB_OK | MB_ICONEXCLAMATION);
|
||||
return ::AtlReportError(clsid, message, GUID_NULL, hr);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
std::wstring generateEditCommandLine(const EditArgs& editArgs)
|
||||
{
|
||||
std::string args = editArgs.launchMode;
|
||||
|
||||
if ( !editArgs.authUrl.empty() )
|
||||
args += std::string(" ") + SharedLauncher::AuthUrlArgument + " " + editArgs.authUrl;
|
||||
if ( !editArgs.authTicket.empty() )
|
||||
args += std::string(" ") + SharedLauncher::AuthTicketArgument + " " + editArgs.authTicket;
|
||||
if ( !editArgs.script.empty() )
|
||||
args += std::string(" ") + SharedLauncher::ScriptArgument + " " + editArgs.script;
|
||||
if ( !editArgs.readyEvent.empty() )
|
||||
args += std::string(" ") + SharedLauncher::ReadyEventArgument + " " + editArgs.readyEvent;
|
||||
if ( !editArgs.showEventName.empty() )
|
||||
args += std::string(" ") + SharedLauncher::ShowEventArgument + " " + editArgs.showEventName;
|
||||
if ( !editArgs.avatarMode.empty() )
|
||||
args += std::string(" ") + SharedLauncher::AvatarModeArgument;
|
||||
if ( !editArgs.browserTrackerId.empty() )
|
||||
args += std::string(" ") + SharedLauncher::BrowserTrackerId + " " + editArgs.browserTrackerId;
|
||||
|
||||
return convert_s2w(args);
|
||||
}
|
||||
|
||||
bool parseEditCommandArg(wchar_t** args,int& index,int count,EditArgs& editArgs)
|
||||
{
|
||||
std::string arg = convert_w2s(args[index]);
|
||||
std::string nextArg;
|
||||
if ( (index + 1) < count )
|
||||
nextArg = convert_w2s(args[index + 1]);
|
||||
|
||||
if ( arg == BuildArgument )
|
||||
editArgs.launchMode = BuildArgument;
|
||||
else if ( arg == IDEArgument )
|
||||
editArgs.launchMode = IDEArgument;
|
||||
else if ( arg == AvatarModeArgument )
|
||||
editArgs.avatarMode = AvatarModeArgument;
|
||||
else if ( arg == FileLocationArgument )
|
||||
{
|
||||
editArgs.fileName = nextArg;
|
||||
index++;
|
||||
}
|
||||
else if ( arg == ScriptArgument )
|
||||
{
|
||||
editArgs.script = nextArg;
|
||||
index++;
|
||||
}
|
||||
else if ( arg == AuthUrlArgument )
|
||||
{
|
||||
editArgs.authUrl = nextArg;
|
||||
index++;
|
||||
}
|
||||
else if ( arg == AuthTicketArgument )
|
||||
{
|
||||
editArgs.authTicket = nextArg;
|
||||
index++;
|
||||
}
|
||||
else if ( arg == ReadyEventArgument )
|
||||
{
|
||||
editArgs.readyEvent = nextArg;
|
||||
index++;
|
||||
}
|
||||
else if ( arg == ShowEventArgument )
|
||||
{
|
||||
editArgs.showEventName = nextArg;
|
||||
index++;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* SharedLauncher.h
|
||||
* Copyright (c) 2013 ROBLOX Corp. All Rights Reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(_WIN32) && !defined(RBX_PLATFORM_DURANGO)
|
||||
#include "atlutil.h"
|
||||
#include "ProcessInformation.h"
|
||||
#endif
|
||||
|
||||
#include "format_string.h"
|
||||
|
||||
#define LAUNCHER_STARTED_EVENT_NAME _T("rbxLauncherStarted")
|
||||
|
||||
namespace SharedLauncher
|
||||
{
|
||||
static const char* FileLocationArgument = "-fileLocation"; // File to open
|
||||
static const char* ScriptArgument = "-script"; // Script to execute (LUA)
|
||||
static const char* AuthUrlArgument = "-url"; // Url to hit to authenticate (passing auth from website)
|
||||
static const char* AuthTicketArgument = "-ticket"; // Ticket to tack onto url to authenticate (passing auth from website)
|
||||
static const char* StartEventArgument = "-startEvent"; // Callback to notify launcher of start of application
|
||||
static const char* ReadyEventArgument = "-readyEvent"; // Callback to notify launcher that application is loaded and ready
|
||||
static const char* ShowEventArgument = "-showEvent"; // Event to wait for before showing the window
|
||||
static const char* TestModeArgument = "-testMode"; // Play solo, Start Server, and Start Player
|
||||
static const char* IDEArgument = "-ide"; // Indicates launching studio in advanced mode (full IDE)
|
||||
static const char* BuildArgument = "-build"; // Indicates launching studio in build mode (limited IDE docks)
|
||||
static const char* DebuggerArgument = "-debugger"; // Causes the program to wait for a debugger to attach on startup
|
||||
static const char* AvatarModeArgument = "-avatar"; // Avatar Mode sets up the in-game GUI with an avatar to run around with
|
||||
static const char* RbxDevArgument = "-rbxdev"; // RbxDev starts the mobile development deployer (currently in development as of 1/17/2014), allows mobile devices to connect to a game
|
||||
static const char* BrowserTrackerId = "-browserTrackerId"; // Passed in from website used to log launch status
|
||||
|
||||
enum LaunchMode
|
||||
{
|
||||
Play,
|
||||
Play_Protocol,
|
||||
Build,
|
||||
Edit
|
||||
};
|
||||
|
||||
#if defined(_WIN32) && !defined(RBX_PLATFORM_DURANGO)
|
||||
CRegKey GetKey(CString& out_operation, bool isStudioKey, bool is64bits = false);
|
||||
HRESULT PreStartGame(const CLSID& clsid);
|
||||
__declspec(dllexport) HRESULT StartGame(simple_logger<wchar_t> &logger, BSTR authenTicket, BSTR authenticationUrl, BSTR script, const CLSID& clsid, bool silentMode, TCHAR *guidName, bool startInHiddenMode, TCHAR *unhideEventName, LaunchMode launchMode);
|
||||
HRESULT StartGame(simple_logger<char> &logger, BSTR authenTicket, BSTR authenticationUrl, BSTR script, const CLSID& clsid, bool silentMode, TCHAR *guidName, bool startInHiddenMode, TCHAR *unhideEventName, LaunchMode editMode);
|
||||
HRESULT get_InstallHost(BSTR* pVal, const CLSID& clsid);
|
||||
HRESULT get_Version(BSTR* pVal, const CLSID& clsid);
|
||||
|
||||
HRESULT get_IsUpToDate(simple_logger<wchar_t> &logger, VARIANT_BOOL* pVal, CProcessInformation& isUpToDateProcessInfo, const CLSID& clsid);
|
||||
HRESULT Update(const CLSID& clsid);
|
||||
#endif
|
||||
|
||||
struct EditArgs
|
||||
{
|
||||
std::string fileName;
|
||||
std::string authUrl;
|
||||
std::string authTicket;
|
||||
std::string script;
|
||||
std::string readyEvent;
|
||||
std::string showEventName;
|
||||
std::string launchMode;
|
||||
std::string avatarMode;
|
||||
std::string browserTrackerId;
|
||||
};
|
||||
|
||||
std::wstring generateEditCommandLine(const EditArgs& editArgs);
|
||||
bool parseEditCommandArg(wchar_t** args,int& index,int count,EditArgs& editArgs);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Tracer.cpp
|
||||
* Copyright (c) 2013 ROBLOX Corp. All Rights Reserved.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "rbx/Debug.h"
|
||||
|
||||
namespace RBX {
|
||||
|
||||
} // namespace RBX
|
||||
@@ -0,0 +1,622 @@
|
||||
/**
|
||||
* UserInputUtil.cpp
|
||||
* Copyright (c) 2013 ROBLOX Corp. All Rights Reserved.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "UserInputUtil.h"
|
||||
#include "Util/Rect.h"
|
||||
#include "util/standardout.h"
|
||||
#include "V8DataModel/GameBasicSettings.h"
|
||||
|
||||
#include <dinput.h>
|
||||
|
||||
using RBX::Vector2;
|
||||
using RBX::Rect;
|
||||
|
||||
|
||||
const float UserInputUtil::HybridSensitivity = 15.0f;
|
||||
const float UserInputUtil::MouseTug = 20.0f;
|
||||
|
||||
bool UserInputUtil::isCtrlDown(RBX::ModCode modCode)
|
||||
{
|
||||
return (modCode == RBX::KMOD_LCTRL || modCode == RBX::KMOD_RCTRL);
|
||||
}
|
||||
|
||||
|
||||
// horizontal - will keep doing deltas
|
||||
// vertical - will peg inset 2 pixels
|
||||
//
|
||||
void UserInputUtil::wrapFullScreen(const Vector2& delta,
|
||||
Vector2& wrapMouseDelta,
|
||||
Vector2& wrapMousePosition,
|
||||
const Vector2& windowSize)
|
||||
{
|
||||
float wrapPositionY = wrapMousePosition.y + delta.y;
|
||||
wrapMouseBorderLock(delta, wrapMouseDelta, wrapMousePosition, windowSize);
|
||||
wrapMouseDelta.y = 0.0;
|
||||
wrapMousePosition.y = wrapPositionY;
|
||||
float halfHeight = (windowSize.y * 0.5);
|
||||
|
||||
float wrapPositionX = wrapMousePosition.x;
|
||||
float halfWidth = (windowSize.x * 0.5);
|
||||
wrapMousePosition.x = G3D::clamp(wrapPositionX,-halfWidth,halfWidth);
|
||||
|
||||
// This is changed so we can pop out into the chat area below
|
||||
wrapMousePosition.y = G3D::clamp(wrapMousePosition.y, -halfHeight, halfHeight);
|
||||
//wrapMousePosition.y = std::max(-halfHeight, wrapMousePosition.y);
|
||||
|
||||
// Setting this to zero prevents the camera from panning automatically near the extents of the screen.
|
||||
wrapMouseDelta = Vector2::zero();
|
||||
}
|
||||
|
||||
void UserInputUtil::wrapMouseHorizontalTransition(const Vector2& delta,
|
||||
Vector2& wrapMouseDelta,
|
||||
Vector2& wrapMousePosition,
|
||||
const Vector2& windowSize)
|
||||
{
|
||||
float wrapPositionY = wrapMousePosition.y + delta.y;
|
||||
wrapMouseBorderTransition(delta, wrapMouseDelta, wrapMousePosition, windowSize);
|
||||
wrapMouseDelta.y = 0.0;
|
||||
wrapMousePosition.y = wrapPositionY;
|
||||
}
|
||||
|
||||
|
||||
void UserInputUtil::wrapMouseBorder(const Vector2& delta,
|
||||
Vector2& wrapMouseDelta,
|
||||
Vector2& wrapMousePosition,
|
||||
const Vector2& windowSize,
|
||||
const int borderWidth,
|
||||
const float creepFactor)
|
||||
|
||||
{
|
||||
Vector2 halfSize = windowSize * 0.5;
|
||||
Rect inner = Rect(-halfSize, halfSize).inset(borderWidth); // in Wrap Coordinates
|
||||
Vector2 oldPosition = wrapMousePosition;
|
||||
inner.unionWith(oldPosition); // now union of the border and old position - ratchet
|
||||
|
||||
Vector2 newPositionUnclamped = oldPosition + delta;
|
||||
Vector2 newPositionClamped = inner.clamp(newPositionUnclamped);
|
||||
|
||||
Vector2 positiveDistanceInBorder = newPositionUnclamped - newPositionClamped;
|
||||
|
||||
|
||||
if(!RBX::GameBasicSettings::singleton().inMousepanMode())
|
||||
wrapMousePosition = newPositionClamped + (positiveDistanceInBorder * creepFactor);
|
||||
wrapMouseDelta += positiveDistanceInBorder;
|
||||
}
|
||||
|
||||
void UserInputUtil::wrapMouseBorderLock(const Vector2& delta,
|
||||
Vector2& wrapMouseDelta,
|
||||
Vector2& wrapMousePosition,
|
||||
const Vector2& windowSize)
|
||||
{
|
||||
wrapMouseBorder(
|
||||
delta,
|
||||
wrapMouseDelta,
|
||||
wrapMousePosition,
|
||||
windowSize,
|
||||
6,
|
||||
0.0f);
|
||||
}
|
||||
|
||||
void UserInputUtil::wrapMouseBorderTransition(const Vector2& delta,
|
||||
Vector2& wrapMouseDelta,
|
||||
Vector2& wrapMousePosition,
|
||||
const Vector2& windowSize)
|
||||
{
|
||||
wrapMouseBorder(
|
||||
delta,
|
||||
wrapMouseDelta,
|
||||
wrapMousePosition,
|
||||
windowSize,
|
||||
20,
|
||||
0.05f);
|
||||
}
|
||||
|
||||
void UserInputUtil::wrapMouseNone(const Vector2& delta,
|
||||
Vector2& wrapMouseDelta,
|
||||
Vector2& wrapMousePosition)
|
||||
{
|
||||
wrapMouseDelta = Vector2::zero();
|
||||
wrapMousePosition += delta;
|
||||
}
|
||||
|
||||
void UserInputUtil::wrapMouseCenter(const Vector2& delta,
|
||||
Vector2& wrapMouseDelta,
|
||||
Vector2& wrapMousePosition)
|
||||
{
|
||||
wrapMouseDelta += delta;
|
||||
// don't move the cursor....
|
||||
// wrapMousePosition = G3D::Vector2::zero();
|
||||
|
||||
}
|
||||
|
||||
void UserInputUtil::wrapMousePos(const Vector2& delta,
|
||||
Vector2& wrapMouseDelta,
|
||||
Vector2& wrapMousePosition,
|
||||
const Vector2& windowSize,
|
||||
Vector2& posToWrapTo,
|
||||
bool autoMoveMouse)
|
||||
{
|
||||
if(posToWrapTo.length() > 2)
|
||||
{
|
||||
Vector2 windowDelta = posToWrapTo/windowSize;
|
||||
wrapMouseDelta = windowDelta * HybridSensitivity;
|
||||
posToWrapTo -= (wrapMouseDelta * HybridSensitivity * 0.266f); // 0.266 is a tuning constant
|
||||
|
||||
float xDiff = std::abs(wrapMousePosition.x/wrapMousePosition.length()) * 0.3f;
|
||||
float yDiff = std::abs(wrapMousePosition.y/wrapMousePosition.length()) * 0.4f;
|
||||
|
||||
if(autoMoveMouse)
|
||||
{
|
||||
if(wrapMousePosition.x < 0)
|
||||
{
|
||||
wrapMousePosition.x += xDiff * wrapMouseDelta.length() * MouseTug;
|
||||
if(wrapMousePosition.x > 0)
|
||||
wrapMousePosition.x = 0;
|
||||
}
|
||||
else if (wrapMousePosition.x > 0)
|
||||
{
|
||||
wrapMousePosition.x -= xDiff * wrapMouseDelta.length()* MouseTug;
|
||||
if(wrapMousePosition.x < 0)
|
||||
wrapMousePosition.x = 0;
|
||||
}
|
||||
|
||||
|
||||
if(wrapMousePosition.y < 0)
|
||||
{
|
||||
wrapMousePosition.y += yDiff * wrapMouseDelta.length() * MouseTug;
|
||||
if(wrapMousePosition.y > 0)
|
||||
wrapMousePosition.y = 0;
|
||||
}
|
||||
else if (wrapMousePosition.y > 0)
|
||||
{
|
||||
wrapMousePosition.y -= yDiff * wrapMouseDelta.length() * MouseTug;
|
||||
if(wrapMousePosition.y < 0)
|
||||
wrapMousePosition.y = 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
wrapMousePosition += delta;
|
||||
}
|
||||
|
||||
|
||||
void UserInputUtil::wrapMouseHorizontalCenter(const Vector2& delta,
|
||||
Vector2& wrapMouseDelta,
|
||||
Vector2& wrapMousePosition)
|
||||
{
|
||||
wrapMouseDelta.x += delta.x;
|
||||
// wrapMousePosition = G3D::Vector2::zero();
|
||||
}
|
||||
|
||||
|
||||
|
||||
G3D::Vector2 UserInputUtil::didodToVector2(const DIDEVICEOBJECTDATA& didod)
|
||||
{
|
||||
G3D::Vector2 answer(0,0);
|
||||
float data = (float) ((int)didod.dwData);
|
||||
|
||||
if (didod.dwOfs==DIMOFS_X) {
|
||||
answer.x = data;
|
||||
}
|
||||
else {
|
||||
answer.y = data;
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
|
||||
// Maps DIK_* to RBX::SDLK_*
|
||||
RBX::KeyCode UserInputUtil::directInputToKeyCode(DWORD diKey)
|
||||
{
|
||||
RBXASSERT(diKey>=0);
|
||||
RBXASSERT(diKey<256);
|
||||
|
||||
static RBX::KeyCode keymap[256];
|
||||
static bool initialized = false;
|
||||
if (!initialized)
|
||||
{
|
||||
for ( int i=0; i<256; ++i )
|
||||
keymap[i] = RBX::SDLK_UNKNOWN;
|
||||
|
||||
keymap[DIK_ESCAPE] = RBX::SDLK_ESCAPE;
|
||||
keymap[DIK_1] = RBX::SDLK_1;
|
||||
keymap[DIK_2] = RBX::SDLK_2;
|
||||
keymap[DIK_3] = RBX::SDLK_3;
|
||||
keymap[DIK_4] = RBX::SDLK_4;
|
||||
keymap[DIK_5] = RBX::SDLK_5;
|
||||
keymap[DIK_6] = RBX::SDLK_6;
|
||||
keymap[DIK_7] = RBX::SDLK_7;
|
||||
keymap[DIK_8] = RBX::SDLK_8;
|
||||
keymap[DIK_9] = RBX::SDLK_9;
|
||||
keymap[DIK_0] = RBX::SDLK_0;
|
||||
keymap[DIK_MINUS] = RBX::SDLK_MINUS;
|
||||
keymap[DIK_EQUALS] = RBX::SDLK_EQUALS;
|
||||
keymap[DIK_BACK] = RBX::SDLK_BACKSPACE;
|
||||
keymap[DIK_TAB] = RBX::SDLK_TAB;
|
||||
keymap[DIK_Q] = RBX::SDLK_q;
|
||||
keymap[DIK_W] = RBX::SDLK_w;
|
||||
keymap[DIK_E] = RBX::SDLK_e;
|
||||
keymap[DIK_R] = RBX::SDLK_r;
|
||||
keymap[DIK_T] = RBX::SDLK_t;
|
||||
keymap[DIK_Y] = RBX::SDLK_y;
|
||||
keymap[DIK_U] = RBX::SDLK_u;
|
||||
keymap[DIK_I] = RBX::SDLK_i;
|
||||
keymap[DIK_O] = RBX::SDLK_o;
|
||||
keymap[DIK_P] = RBX::SDLK_p;
|
||||
|
||||
keymap[DIK_LBRACKET] = RBX::SDLK_LEFTBRACKET;
|
||||
keymap[DIK_AT] = RBX::SDLK_AT;
|
||||
keymap[DIK_RBRACKET] = RBX::SDLK_RIGHTBRACKET;
|
||||
keymap[DIK_PREVTRACK] = RBX::SDLK_EQUALS;
|
||||
keymap[DIK_COLON] = RBX::SDLK_COLON;
|
||||
keymap[DIK_KANJI] = RBX::SDLK_BACKQUOTE; // weird key mapping....
|
||||
|
||||
keymap[DIK_RETURN] = RBX::SDLK_RETURN;
|
||||
keymap[DIK_LCONTROL] = RBX::SDLK_LCTRL;
|
||||
keymap[DIK_A] = RBX::SDLK_a;
|
||||
keymap[DIK_S] = RBX::SDLK_s;
|
||||
keymap[DIK_D] = RBX::SDLK_d;
|
||||
keymap[DIK_F] = RBX::SDLK_f;
|
||||
keymap[DIK_G] = RBX::SDLK_g;
|
||||
keymap[DIK_H] = RBX::SDLK_h;
|
||||
keymap[DIK_J] = RBX::SDLK_j;
|
||||
keymap[DIK_K] = RBX::SDLK_k;
|
||||
keymap[DIK_L] = RBX::SDLK_l;
|
||||
keymap[DIK_SEMICOLON] = RBX::SDLK_SEMICOLON;
|
||||
keymap[DIK_APOSTROPHE] = RBX::SDLK_QUOTE;
|
||||
keymap[DIK_GRAVE] = RBX::SDLK_BACKQUOTE;
|
||||
keymap[DIK_LSHIFT] = RBX::SDLK_LSHIFT;
|
||||
keymap[DIK_BACKSLASH] = RBX::SDLK_BACKSLASH;
|
||||
keymap[DIK_OEM_102] = RBX::SDLK_BACKSLASH;
|
||||
keymap[DIK_Z] = RBX::SDLK_z;
|
||||
keymap[DIK_X] = RBX::SDLK_x;
|
||||
keymap[DIK_C] = RBX::SDLK_c;
|
||||
keymap[DIK_V] = RBX::SDLK_v;
|
||||
keymap[DIK_B] = RBX::SDLK_b;
|
||||
keymap[DIK_N] = RBX::SDLK_n;
|
||||
keymap[DIK_M] = RBX::SDLK_m;
|
||||
keymap[DIK_COMMA] = RBX::SDLK_COMMA;
|
||||
keymap[DIK_PERIOD] = RBX::SDLK_PERIOD;
|
||||
keymap[DIK_SLASH] = RBX::SDLK_SLASH;
|
||||
keymap[DIK_RSHIFT] = RBX::SDLK_RSHIFT;
|
||||
keymap[DIK_MULTIPLY] = RBX::SDLK_KP_MULTIPLY;
|
||||
keymap[DIK_LMENU] = RBX::SDLK_LALT;
|
||||
keymap[DIK_SPACE] = RBX::SDLK_SPACE;
|
||||
keymap[DIK_CAPITAL] = RBX::SDLK_CAPSLOCK;
|
||||
keymap[DIK_F1] = RBX::SDLK_F1;
|
||||
keymap[DIK_F2] = RBX::SDLK_F2;
|
||||
keymap[DIK_F3] = RBX::SDLK_F3;
|
||||
keymap[DIK_F4] = RBX::SDLK_F4;
|
||||
keymap[DIK_F5] = RBX::SDLK_F5;
|
||||
keymap[DIK_F6] = RBX::SDLK_F6;
|
||||
keymap[DIK_F7] = RBX::SDLK_F7;
|
||||
keymap[DIK_F8] = RBX::SDLK_F8;
|
||||
keymap[DIK_F9] = RBX::SDLK_F9;
|
||||
keymap[DIK_F10] = RBX::SDLK_F10;
|
||||
keymap[DIK_NUMLOCK] = RBX::SDLK_NUMLOCK;
|
||||
keymap[DIK_SCROLL] = RBX::SDLK_SCROLLOCK;
|
||||
keymap[DIK_NUMPAD7] = RBX::SDLK_KP7;
|
||||
keymap[DIK_NUMPAD8] = RBX::SDLK_KP8;
|
||||
keymap[DIK_NUMPAD9] = RBX::SDLK_KP9;
|
||||
keymap[DIK_SUBTRACT] = RBX::SDLK_KP_MINUS;
|
||||
keymap[DIK_NUMPAD4] = RBX::SDLK_KP4;
|
||||
keymap[DIK_NUMPAD5] = RBX::SDLK_KP5;
|
||||
keymap[DIK_NUMPAD6] = RBX::SDLK_KP6;
|
||||
keymap[DIK_ADD] = RBX::SDLK_KP_PLUS;
|
||||
keymap[DIK_NUMPAD1] = RBX::SDLK_KP1;
|
||||
keymap[DIK_NUMPAD2] = RBX::SDLK_KP2;
|
||||
keymap[DIK_NUMPAD3] = RBX::SDLK_KP3;
|
||||
keymap[DIK_NUMPAD0] = RBX::SDLK_KP0;
|
||||
keymap[DIK_DECIMAL] = RBX::SDLK_KP_PERIOD;
|
||||
keymap[DIK_F11] = RBX::SDLK_F11;
|
||||
keymap[DIK_F12] = RBX::SDLK_F12;
|
||||
keymap[DIK_F13] = RBX::SDLK_F13;
|
||||
keymap[DIK_F14] = RBX::SDLK_F14;
|
||||
keymap[DIK_F15] = RBX::SDLK_F15;
|
||||
keymap[DIK_NUMPADEQUALS] = RBX::SDLK_KP_EQUALS;
|
||||
keymap[DIK_NUMPADENTER] = RBX::SDLK_KP_ENTER;
|
||||
keymap[DIK_RCONTROL] = RBX::SDLK_RCTRL;
|
||||
keymap[DIK_DIVIDE] = RBX::SDLK_KP_DIVIDE;
|
||||
keymap[DIK_SYSRQ] = RBX::SDLK_SYSREQ;
|
||||
keymap[DIK_RMENU] = RBX::SDLK_RALT;
|
||||
keymap[DIK_PAUSE] = RBX::SDLK_PAUSE;
|
||||
keymap[DIK_HOME] = RBX::SDLK_HOME;
|
||||
keymap[DIK_UP] = RBX::SDLK_UP;
|
||||
keymap[DIK_PRIOR] = RBX::SDLK_PAGEUP;
|
||||
keymap[DIK_LEFT] = RBX::SDLK_LEFT;
|
||||
keymap[DIK_RIGHT] = RBX::SDLK_RIGHT;
|
||||
keymap[DIK_END] = RBX::SDLK_END;
|
||||
keymap[DIK_DOWN] = RBX::SDLK_DOWN;
|
||||
keymap[DIK_NEXT] = RBX::SDLK_PAGEDOWN;
|
||||
keymap[DIK_INSERT] = RBX::SDLK_INSERT;
|
||||
keymap[DIK_DELETE] = RBX::SDLK_DELETE;
|
||||
keymap[DIK_LWIN] = RBX::SDLK_LMETA;
|
||||
keymap[DIK_RWIN] = RBX::SDLK_RMETA;
|
||||
keymap[DIK_APPS] = RBX::SDLK_MENU;
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
return keymap[diKey];
|
||||
}
|
||||
|
||||
// Maps RBX::RBX::SDLK_* to DIK_*
|
||||
DWORD UserInputUtil::keyCodeToDirectInput(RBX::KeyCode keyCode)
|
||||
{
|
||||
static DWORD keymap[RBX::SDLK_LAST];
|
||||
static bool initialized = false;
|
||||
if (!initialized)
|
||||
{
|
||||
for ( int i=0; i<RBX::SDLK_LAST; ++i )
|
||||
keymap[i] = 0;
|
||||
|
||||
keymap[RBX::SDLK_ESCAPE] = DIK_ESCAPE;
|
||||
keymap[RBX::SDLK_1] = DIK_1;
|
||||
keymap[RBX::SDLK_2] = DIK_2;
|
||||
keymap[RBX::SDLK_3] = DIK_3;
|
||||
keymap[RBX::SDLK_4] = DIK_4;
|
||||
keymap[RBX::SDLK_5] = DIK_5;
|
||||
keymap[RBX::SDLK_6] = DIK_6;
|
||||
keymap[RBX::SDLK_7] = DIK_7;
|
||||
keymap[RBX::SDLK_8] = DIK_8;
|
||||
keymap[RBX::SDLK_9] = DIK_9;
|
||||
keymap[RBX::SDLK_0] = DIK_0;
|
||||
keymap[RBX::SDLK_MINUS] = DIK_MINUS;
|
||||
keymap[RBX::SDLK_EQUALS] = DIK_EQUALS;
|
||||
keymap[RBX::SDLK_BACKSPACE] = DIK_BACK;
|
||||
keymap[RBX::SDLK_TAB] = DIK_TAB;
|
||||
keymap[RBX::SDLK_q] = DIK_Q;
|
||||
keymap[RBX::SDLK_w] = DIK_W;
|
||||
keymap[RBX::SDLK_e] = DIK_E;
|
||||
keymap[RBX::SDLK_r] = DIK_R;
|
||||
keymap[RBX::SDLK_t] = DIK_T;
|
||||
keymap[RBX::SDLK_y] = DIK_Y;
|
||||
keymap[RBX::SDLK_u] = DIK_U;
|
||||
keymap[RBX::SDLK_i] = DIK_I;
|
||||
keymap[RBX::SDLK_o] = DIK_O;
|
||||
keymap[RBX::SDLK_p] = DIK_P;
|
||||
keymap[RBX::SDLK_LEFTBRACKET] = DIK_LBRACKET;
|
||||
keymap[RBX::SDLK_RIGHTBRACKET] = DIK_RBRACKET;
|
||||
keymap[RBX::SDLK_RETURN] = DIK_RETURN;
|
||||
keymap[RBX::SDLK_LCTRL] = DIK_LCONTROL;
|
||||
keymap[RBX::SDLK_a] = DIK_A;
|
||||
keymap[RBX::SDLK_s] = DIK_S;
|
||||
keymap[RBX::SDLK_d] = DIK_D;
|
||||
keymap[RBX::SDLK_f] = DIK_F;
|
||||
keymap[RBX::SDLK_g] = DIK_G;
|
||||
keymap[RBX::SDLK_h] = DIK_H;
|
||||
keymap[RBX::SDLK_j] = DIK_J;
|
||||
keymap[RBX::SDLK_k] = DIK_K;
|
||||
keymap[RBX::SDLK_l] = DIK_L;
|
||||
keymap[RBX::SDLK_SEMICOLON] = DIK_SEMICOLON;
|
||||
keymap[RBX::SDLK_QUOTE] = DIK_APOSTROPHE;
|
||||
keymap[RBX::SDLK_BACKQUOTE] = DIK_GRAVE;
|
||||
keymap[RBX::SDLK_LSHIFT] = DIK_LSHIFT;
|
||||
keymap[RBX::SDLK_BACKSLASH] = DIK_BACKSLASH;
|
||||
keymap[RBX::SDLK_BACKSLASH] = DIK_OEM_102;
|
||||
keymap[RBX::SDLK_z] = DIK_Z;
|
||||
keymap[RBX::SDLK_x] = DIK_X;
|
||||
keymap[RBX::SDLK_c] = DIK_C;
|
||||
keymap[RBX::SDLK_v] = DIK_V;
|
||||
keymap[RBX::SDLK_b] = DIK_B;
|
||||
keymap[RBX::SDLK_n] = DIK_N;
|
||||
keymap[RBX::SDLK_m] = DIK_M;
|
||||
keymap[RBX::SDLK_COMMA] = DIK_COMMA;
|
||||
keymap[RBX::SDLK_PERIOD] = DIK_PERIOD;
|
||||
keymap[RBX::SDLK_SLASH] = DIK_SLASH;
|
||||
keymap[RBX::SDLK_RSHIFT] = DIK_RSHIFT;
|
||||
keymap[RBX::SDLK_KP_MULTIPLY] = DIK_MULTIPLY;
|
||||
keymap[RBX::SDLK_LALT] = DIK_LMENU;
|
||||
keymap[RBX::SDLK_SPACE] = DIK_SPACE;
|
||||
keymap[RBX::SDLK_CAPSLOCK] = DIK_CAPITAL;
|
||||
keymap[RBX::SDLK_F1] = DIK_F1;
|
||||
keymap[RBX::SDLK_F2] = DIK_F2;
|
||||
keymap[RBX::SDLK_F3] = DIK_F3;
|
||||
keymap[RBX::SDLK_F4] = DIK_F4;
|
||||
keymap[RBX::SDLK_F5] = DIK_F5;
|
||||
keymap[RBX::SDLK_F6] = DIK_F6;
|
||||
keymap[RBX::SDLK_F7] = DIK_F7;
|
||||
keymap[RBX::SDLK_F8] = DIK_F8;
|
||||
keymap[RBX::SDLK_F9] = DIK_F9;
|
||||
keymap[RBX::SDLK_F10] = DIK_F10;
|
||||
keymap[RBX::SDLK_NUMLOCK] = DIK_NUMLOCK;
|
||||
keymap[RBX::SDLK_SCROLLOCK] = DIK_SCROLL;
|
||||
keymap[RBX::SDLK_KP7] = DIK_NUMPAD7;
|
||||
keymap[RBX::SDLK_KP8] = DIK_NUMPAD8;
|
||||
keymap[RBX::SDLK_KP9] = DIK_NUMPAD9;
|
||||
keymap[RBX::SDLK_KP_MINUS] = DIK_SUBTRACT;
|
||||
keymap[RBX::SDLK_KP4] = DIK_NUMPAD4;
|
||||
keymap[RBX::SDLK_KP5] = DIK_NUMPAD5;
|
||||
keymap[RBX::SDLK_KP6] = DIK_NUMPAD6;
|
||||
keymap[RBX::SDLK_KP_PLUS] = DIK_ADD;
|
||||
keymap[RBX::SDLK_KP1] = DIK_NUMPAD1;
|
||||
keymap[RBX::SDLK_KP2] = DIK_NUMPAD2;
|
||||
keymap[RBX::SDLK_KP3] = DIK_NUMPAD3;
|
||||
keymap[RBX::SDLK_KP0] = DIK_NUMPAD0;
|
||||
keymap[RBX::SDLK_KP_PERIOD] = DIK_DECIMAL;
|
||||
keymap[RBX::SDLK_F11] = DIK_F11;
|
||||
keymap[RBX::SDLK_F12] = DIK_F12;
|
||||
keymap[RBX::SDLK_F13] = DIK_F13;
|
||||
keymap[RBX::SDLK_F14] = DIK_F14;
|
||||
keymap[RBX::SDLK_F15] = DIK_F15;
|
||||
keymap[RBX::SDLK_KP_EQUALS] = DIK_NUMPADEQUALS;
|
||||
keymap[RBX::SDLK_KP_ENTER] = DIK_NUMPADENTER;
|
||||
keymap[RBX::SDLK_RCTRL] = DIK_RCONTROL;
|
||||
keymap[RBX::SDLK_KP_DIVIDE] = DIK_DIVIDE;
|
||||
keymap[RBX::SDLK_SYSREQ] = DIK_SYSRQ;
|
||||
keymap[RBX::SDLK_RALT] = DIK_RMENU;
|
||||
keymap[RBX::SDLK_PAUSE] = DIK_PAUSE;
|
||||
keymap[RBX::SDLK_HOME] = DIK_HOME;
|
||||
keymap[RBX::SDLK_UP] = DIK_UP;
|
||||
keymap[RBX::SDLK_PAGEUP] = DIK_PRIOR;
|
||||
keymap[RBX::SDLK_LEFT] = DIK_LEFT;
|
||||
keymap[RBX::SDLK_RIGHT] = DIK_RIGHT;
|
||||
keymap[RBX::SDLK_END] = DIK_END;
|
||||
keymap[RBX::SDLK_DOWN] = DIK_DOWN;
|
||||
keymap[RBX::SDLK_PAGEDOWN] = DIK_NEXT;
|
||||
keymap[RBX::SDLK_INSERT] = DIK_INSERT;
|
||||
keymap[RBX::SDLK_DELETE] = DIK_DELETE;
|
||||
keymap[RBX::SDLK_LMETA] = DIK_LWIN;
|
||||
keymap[RBX::SDLK_RMETA] = DIK_RWIN;
|
||||
keymap[RBX::SDLK_MENU] = DIK_APPS;
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
return keymap[keyCode];
|
||||
}
|
||||
|
||||
|
||||
// Maps RBX::RBX::SDLK_* to VK_*
|
||||
DWORD UserInputUtil::keyCodeToVK(RBX::KeyCode keyCode)
|
||||
{
|
||||
static DWORD keymap[RBX::SDLK_LAST];
|
||||
static bool initialized = false;
|
||||
if (!initialized)
|
||||
{
|
||||
for ( int i=0; i<RBX::SDLK_LAST; ++i )
|
||||
keymap[i] = 0;
|
||||
|
||||
keymap[RBX::SDLK_PRINT] = VK_PRINT;
|
||||
keymap[RBX::SDLK_SYSREQ] = VK_SNAPSHOT;
|
||||
keymap[RBX::SDLK_ESCAPE] = VK_ESCAPE;
|
||||
keymap[RBX::SDLK_BACKSPACE] = VK_BACK;
|
||||
keymap[RBX::SDLK_TAB] = VK_TAB;
|
||||
keymap[RBX::SDLK_RETURN] = VK_RETURN;
|
||||
keymap[RBX::SDLK_LCTRL] = VK_LCONTROL;
|
||||
keymap[RBX::SDLK_LSHIFT] = VK_LSHIFT;
|
||||
keymap[RBX::SDLK_BACKSLASH] = VK_OEM_102;
|
||||
keymap[RBX::SDLK_RSHIFT] = VK_RSHIFT;
|
||||
keymap[RBX::SDLK_KP_MULTIPLY] = VK_MULTIPLY;
|
||||
keymap[RBX::SDLK_LALT] = VK_LMENU;
|
||||
keymap[RBX::SDLK_SPACE] = VK_SPACE;
|
||||
keymap[RBX::SDLK_CAPSLOCK] = VK_CAPITAL;
|
||||
keymap[RBX::SDLK_F1] = VK_F1;
|
||||
keymap[RBX::SDLK_F2] = VK_F2;
|
||||
keymap[RBX::SDLK_F3] = VK_F3;
|
||||
keymap[RBX::SDLK_F4] = VK_F4;
|
||||
keymap[RBX::SDLK_F5] = VK_F5;
|
||||
keymap[RBX::SDLK_F6] = VK_F6;
|
||||
keymap[RBX::SDLK_F7] = VK_F7;
|
||||
keymap[RBX::SDLK_F8] = VK_F8;
|
||||
keymap[RBX::SDLK_F9] = VK_F9;
|
||||
keymap[RBX::SDLK_F10] = VK_F10;
|
||||
keymap[RBX::SDLK_NUMLOCK] = VK_NUMLOCK;
|
||||
keymap[RBX::SDLK_SCROLLOCK] = VK_SCROLL;
|
||||
keymap[RBX::SDLK_KP7] = VK_NUMPAD7;
|
||||
keymap[RBX::SDLK_KP8] = VK_NUMPAD8;
|
||||
keymap[RBX::SDLK_KP9] = VK_NUMPAD9;
|
||||
keymap[RBX::SDLK_KP_MINUS] = VK_SUBTRACT;
|
||||
keymap[RBX::SDLK_KP4] = VK_NUMPAD4;
|
||||
keymap[RBX::SDLK_KP5] = VK_NUMPAD5;
|
||||
keymap[RBX::SDLK_KP6] = VK_NUMPAD6;
|
||||
keymap[RBX::SDLK_KP_PLUS] = VK_ADD;
|
||||
keymap[RBX::SDLK_KP1] = VK_NUMPAD1;
|
||||
keymap[RBX::SDLK_KP2] = VK_NUMPAD2;
|
||||
keymap[RBX::SDLK_KP3] = VK_NUMPAD3;
|
||||
keymap[RBX::SDLK_KP0] = VK_NUMPAD0;
|
||||
keymap[RBX::SDLK_KP_PERIOD] = VK_DECIMAL;
|
||||
keymap[RBX::SDLK_F11] = VK_F11;
|
||||
keymap[RBX::SDLK_F12] = VK_F12;
|
||||
keymap[RBX::SDLK_F13] = VK_F13;
|
||||
keymap[RBX::SDLK_F14] = VK_F14;
|
||||
keymap[RBX::SDLK_F15] = VK_F15;
|
||||
keymap[RBX::SDLK_KP_ENTER] = VK_RETURN;
|
||||
keymap[RBX::SDLK_RCTRL] = VK_RCONTROL;
|
||||
keymap[RBX::SDLK_KP_DIVIDE] = VK_DIVIDE;
|
||||
keymap[RBX::SDLK_RALT] = VK_RMENU;
|
||||
keymap[RBX::SDLK_HOME] = VK_HOME;
|
||||
keymap[RBX::SDLK_UP] = VK_UP;
|
||||
keymap[RBX::SDLK_PAGEUP] = VK_PRIOR;
|
||||
keymap[RBX::SDLK_LEFT] = VK_LEFT;
|
||||
keymap[RBX::SDLK_RIGHT] = VK_RIGHT;
|
||||
keymap[RBX::SDLK_END] = VK_END;
|
||||
keymap[RBX::SDLK_DOWN] = VK_DOWN;
|
||||
keymap[RBX::SDLK_PAGEDOWN] = VK_NEXT;
|
||||
keymap[RBX::SDLK_INSERT] = VK_INSERT;
|
||||
keymap[RBX::SDLK_DELETE] = VK_DELETE;
|
||||
keymap[RBX::SDLK_LMETA] = VK_LWIN;
|
||||
keymap[RBX::SDLK_RMETA] = VK_RWIN;
|
||||
keymap[RBX::SDLK_MENU] = VK_APPS;
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
return keymap[keyCode];
|
||||
}
|
||||
|
||||
RBX::InputObject::UserInputState UserInputUtil::msgToEventState(UINT uMsg)
|
||||
{
|
||||
switch (uMsg)
|
||||
{
|
||||
case WM_MOUSEMOVE:
|
||||
return RBX::InputObject::INPUT_STATE_CHANGE;
|
||||
case WM_LBUTTONDOWN:
|
||||
case WM_RBUTTONDOWN: // intentional fall thru
|
||||
return RBX::InputObject::INPUT_STATE_BEGIN;
|
||||
case WM_LBUTTONUP:
|
||||
case WM_RBUTTONUP: // intentional fall thru
|
||||
return RBX::InputObject::INPUT_STATE_END;
|
||||
default:
|
||||
return RBX::InputObject::INPUT_STATE_NONE;
|
||||
}
|
||||
}
|
||||
RBX::InputObject::UserInputType UserInputUtil::msgToEventType(UINT uMsg)
|
||||
{
|
||||
switch (uMsg)
|
||||
{
|
||||
case WM_MOUSEMOVE:
|
||||
return RBX::InputObject::TYPE_MOUSEMOVEMENT;
|
||||
case WM_LBUTTONDOWN:
|
||||
case WM_LBUTTONUP: // intentional fall thru
|
||||
return RBX::InputObject::TYPE_MOUSEBUTTON1;
|
||||
case WM_RBUTTONDOWN:
|
||||
case WM_RBUTTONUP: // intentional fall thru
|
||||
return RBX::InputObject::TYPE_MOUSEBUTTON2;
|
||||
default:
|
||||
return RBX::InputObject::TYPE_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
RBX::ModCode UserInputUtil::createModCode(const DiKeys& diKeys)
|
||||
{
|
||||
unsigned int modCode = 0;
|
||||
|
||||
if (diKeys[DIK_LSHIFT] & 0x80)
|
||||
{
|
||||
modCode = modCode | RBX::KMOD_LSHIFT;
|
||||
}
|
||||
if (diKeys[DIK_RSHIFT] & 0x80)
|
||||
{
|
||||
modCode = modCode | RBX::KMOD_RSHIFT;
|
||||
}
|
||||
if (diKeys[DIK_LCONTROL] & 0x80)
|
||||
{
|
||||
modCode = modCode | RBX::KMOD_LCTRL;
|
||||
}
|
||||
if (diKeys[DIK_RCONTROL] & 0x80)
|
||||
{
|
||||
modCode = modCode | RBX::KMOD_RCTRL;
|
||||
}
|
||||
if (diKeys[DIK_LMENU] & 0x80)
|
||||
{
|
||||
modCode = modCode | RBX::KMOD_LALT;
|
||||
}
|
||||
if (diKeys[DIK_LMENU] & 0x80)
|
||||
{
|
||||
modCode = modCode | RBX::KMOD_RALT;
|
||||
}
|
||||
/*if (diKeys[DIK_CAPSLOCK] & 0x80)
|
||||
{
|
||||
modCode = modCode | RBX::KMOD_CAPS;
|
||||
}*/
|
||||
if(::GetKeyState(VK_CAPITAL))
|
||||
{
|
||||
modCode = modCode | RBX::KMOD_CAPS;
|
||||
}
|
||||
|
||||
return (RBX::ModCode)modCode;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
|
||||
#include "V8DataModel/InputObject.h"
|
||||
#include "G3D/Vector2.h"
|
||||
|
||||
#define DIRECTINPUT_VERSION 0x0800
|
||||
|
||||
#include <dinput.h>
|
||||
|
||||
class UserInputUtil
|
||||
{
|
||||
private:
|
||||
static void wrapMouseBorder(const G3D::Vector2& delta,
|
||||
G3D::Vector2& wrapMouseDelta,
|
||||
G3D::Vector2& wrapMousePosition,
|
||||
const G3D::Vector2& windowSize,
|
||||
const int borderWidth,
|
||||
const float creepFactor);
|
||||
|
||||
public:
|
||||
typedef BYTE DiKeys[256];
|
||||
|
||||
static const float HybridSensitivity;
|
||||
static const float MouseTug;
|
||||
|
||||
static RBX::ModCode createModCode(const DiKeys& diKeys);
|
||||
static RBX::InputObject::UserInputState msgToEventState(UINT uMsg);
|
||||
static RBX::InputObject::UserInputType msgToEventType(UINT uMsg);
|
||||
static DWORD keyCodeToDirectInput(RBX::KeyCode keyCode);
|
||||
static RBX::KeyCode directInputToKeyCode(DWORD diKey);
|
||||
static DWORD keyCodeToVK(RBX::KeyCode diKey);
|
||||
static G3D::Vector2 didodToVector2(const DIDEVICEOBJECTDATA& didod);
|
||||
static bool isCtrlDown(RBX::ModCode modCode);
|
||||
|
||||
static void wrapMouseNone(const G3D::Vector2& delta,
|
||||
G3D::Vector2& wrapMouseDelta,
|
||||
G3D::Vector2& wrapMousePosition);
|
||||
|
||||
static void wrapFullScreen(const G3D::Vector2& delta,
|
||||
G3D::Vector2& wrapMouseDelta,
|
||||
G3D::Vector2& wrapMousePosition,
|
||||
const G3D::Vector2& windowSize);
|
||||
|
||||
static void wrapMouseHorizontalTransition(const G3D::Vector2& delta,
|
||||
G3D::Vector2& wrapMouseDelta,
|
||||
G3D::Vector2& wrapMousePosition,
|
||||
const G3D::Vector2& windowSize);
|
||||
|
||||
static void wrapMouseBorderLock(const G3D::Vector2& delta,
|
||||
G3D::Vector2& wrapMouseDelta,
|
||||
G3D::Vector2& wrapMousePosition,
|
||||
const G3D::Vector2& windowSize);
|
||||
|
||||
static void wrapMouseBorderTransition(const G3D::Vector2& delta,
|
||||
G3D::Vector2& wrapMouseDelta,
|
||||
G3D::Vector2& wrapMousePosition,
|
||||
const G3D::Vector2& windowSize);
|
||||
|
||||
static void wrapMouseCenter(const G3D::Vector2& delta,
|
||||
G3D::Vector2& wrapMouseDelta,
|
||||
G3D::Vector2& wrapMousePosition);
|
||||
|
||||
static void wrapMousePos(const G3D::Vector2& delta,
|
||||
G3D::Vector2& wrapMouseDelta,
|
||||
G3D::Vector2& wrapMousePosition,
|
||||
const G3D::Vector2& windowSize,
|
||||
G3D::Vector2& posToWrapTo,
|
||||
bool autoMoveMouse);
|
||||
|
||||
static void wrapMouseHorizontalCenter(const G3D::Vector2& delta,
|
||||
G3D::Vector2& wrapMouseDelta,
|
||||
G3D::Vector2& wrapMousePosition);
|
||||
};
|
||||
@@ -0,0 +1,260 @@
|
||||
/*
|
||||
Module : VersionInfo.CPP
|
||||
Purpose: Implementation for a MFC class encapsulation of Version Infos
|
||||
Created: PJN / 10-04-2000
|
||||
History: None
|
||||
|
||||
|
||||
Copyright (c) 2000 by PJ Naughter.
|
||||
All rights reserved.
|
||||
|
||||
*/
|
||||
|
||||
//////////////// Includes ////////////////////////////////////////////
|
||||
#include "stdafx.h"
|
||||
#include "VersionInfo.h"
|
||||
|
||||
#include "boost/tokenizer.hpp"
|
||||
#include "format_string.h"
|
||||
#include "StringConv.h"
|
||||
//#include "FastLog.h" Disable to get compiler working - RWM
|
||||
|
||||
//LOGGROUP(CrashReporterInit)
|
||||
|
||||
//////////////// Implementation //////////////////////////////////////
|
||||
|
||||
using RBX::SysPathString;
|
||||
using RBX::utf8_decode;
|
||||
using RBX::utf8_encode;
|
||||
|
||||
CVersionInfo::CVersionInfo()
|
||||
{
|
||||
m_pVerData = NULL;
|
||||
m_pffi = NULL;
|
||||
m_wLangID = 0;
|
||||
m_wCharset = 1252; //Use the ANSI code page as a default
|
||||
m_pTranslations = NULL;
|
||||
m_nTranslations = 0;
|
||||
}
|
||||
|
||||
CVersionInfo::~CVersionInfo()
|
||||
{
|
||||
Unload();
|
||||
}
|
||||
|
||||
void CVersionInfo::Unload()
|
||||
{
|
||||
m_pffi = NULL;
|
||||
if (m_pVerData)
|
||||
{
|
||||
delete [] m_pVerData;
|
||||
m_pVerData = NULL;
|
||||
}
|
||||
m_wLangID = 0;
|
||||
m_wCharset = 1252; //Use the ANSI code page as a default
|
||||
m_pTranslations = NULL;
|
||||
m_nTranslations = 0;
|
||||
}
|
||||
|
||||
BOOL CVersionInfo::Load(HMODULE module)
|
||||
{
|
||||
WCHAR name[500];
|
||||
::GetModuleFileNameW(module, name, 500);
|
||||
|
||||
WCHAR path[_MAX_PATH];
|
||||
::GetShortPathNameW(name, path, _MAX_PATH);
|
||||
|
||||
return Load(SysPathString(path));
|
||||
}
|
||||
|
||||
BOOL CVersionInfo::Load(const std::wstring& fileName)
|
||||
{
|
||||
//Free up any previous memory lying around
|
||||
Unload();
|
||||
|
||||
BOOL bSuccess = FALSE;
|
||||
DWORD dwHandle = 0;
|
||||
DWORD dwSize = GetFileVersionInfoSizeW(fileName.c_str(), &dwHandle);
|
||||
if (dwSize)
|
||||
{
|
||||
// FASTLOGS(FLog::CrashReporterInit, "Loading module name: %s", fileName);
|
||||
// FASTLOG2(FLog::CrashReporterInit, "Handle: %p, Size: %u", dwHandle, dwSize);
|
||||
m_pVerData = new BYTE[dwSize];
|
||||
if (GetFileVersionInfoW(fileName.c_str(), dwHandle, dwSize, m_pVerData))
|
||||
{
|
||||
//Get the fixed size version info data
|
||||
UINT nLen = 0;
|
||||
if (VerQueryValue(m_pVerData, _T("\\"), (LPVOID*) &m_pffi, &nLen))
|
||||
{
|
||||
//Retrieve the Lang ID and Character set ID
|
||||
if (VerQueryValue(m_pVerData, _T("\\VarFileInfo\\Translation"), (LPVOID*) &m_pTranslations, &nLen) && nLen >= sizeof(TRANSLATION))
|
||||
{
|
||||
m_nTranslations = nLen / sizeof(TRANSLATION);
|
||||
m_wLangID = m_pTranslations[0].m_wLangID;
|
||||
m_wCharset = m_pTranslations[0].m_wCodePage;
|
||||
}
|
||||
bSuccess = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Free up the memory we used
|
||||
if (!bSuccess)
|
||||
{
|
||||
if (m_pVerData)
|
||||
{
|
||||
delete [] m_pVerData;
|
||||
m_pVerData = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return bSuccess;
|
||||
}
|
||||
|
||||
VS_FIXEDFILEINFO* CVersionInfo::GetFixedFileInfo()
|
||||
{
|
||||
return m_pffi;
|
||||
}
|
||||
|
||||
DWORD CVersionInfo::GetFileFlagsMask()
|
||||
{
|
||||
return m_pffi->dwFileFlagsMask;
|
||||
}
|
||||
|
||||
DWORD CVersionInfo::GetFileFlags()
|
||||
{
|
||||
return m_pffi->dwFileFlags;
|
||||
}
|
||||
|
||||
DWORD CVersionInfo::GetOS()
|
||||
{
|
||||
return m_pffi->dwFileOS;
|
||||
}
|
||||
|
||||
DWORD CVersionInfo::GetFileType()
|
||||
{
|
||||
return m_pffi->dwFileType;
|
||||
}
|
||||
|
||||
DWORD CVersionInfo::GetFileSubType()
|
||||
{
|
||||
return m_pffi->dwFileSubtype;
|
||||
}
|
||||
|
||||
FILETIME CVersionInfo::GetCreationTime()
|
||||
{
|
||||
FILETIME CreationTime;
|
||||
CreationTime.dwHighDateTime = m_pffi->dwFileDateMS;
|
||||
CreationTime.dwLowDateTime = m_pffi->dwFileDateLS;
|
||||
return CreationTime;
|
||||
}
|
||||
|
||||
|
||||
std::string CVersionInfo::GetValue(const std::string& sKey)
|
||||
{
|
||||
|
||||
//For the string to query with
|
||||
std::string sVal;
|
||||
std::string sQueryValue = format_string("\\StringFileInfo\\%04x%04x\\%s",
|
||||
m_wLangID, m_wCharset, sKey.c_str());
|
||||
|
||||
//Do the query
|
||||
LPCTSTR pVal = NULL;
|
||||
UINT nLen = 0;
|
||||
if (VerQueryValue(m_pVerData, CVTS2W(sQueryValue).c_str(), (LPVOID*)&pVal, &nLen))
|
||||
sVal = CVTW2S(pVal);
|
||||
|
||||
return sVal;
|
||||
}
|
||||
|
||||
std::string CVersionInfo::GetCompanyName()
|
||||
{
|
||||
return GetValue(CVTW2S(_T("CompanyName")));
|
||||
}
|
||||
|
||||
std::string CVersionInfo::GetFileDescription()
|
||||
{
|
||||
return GetValue(CVTW2S(_T("FileDescription")));
|
||||
}
|
||||
|
||||
std::string CVersionInfo::GetFileVersionAsDotString()
|
||||
{
|
||||
std::string v = GetFileVersionAsString();
|
||||
boost::tokenizer<boost::char_separator<char> > tokens(v, boost::char_separator<char>(" ,."));
|
||||
std::string dotVersion;
|
||||
for (boost::tokenizer<boost::char_separator<char> >::iterator tok_iter = tokens.begin();
|
||||
tok_iter != tokens.end(); ++tok_iter)
|
||||
{
|
||||
if (tok_iter!=tokens.begin())
|
||||
dotVersion += ".";
|
||||
dotVersion += *tok_iter;
|
||||
}
|
||||
return dotVersion;
|
||||
}
|
||||
|
||||
std::string CVersionInfo::GetFileVersionAsString()
|
||||
{
|
||||
return GetValue(CVTW2S(_T("FileVersion")));
|
||||
}
|
||||
|
||||
std::string CVersionInfo::GetInternalName()
|
||||
{
|
||||
return GetValue(CVTW2S(_T("InternalName")));
|
||||
}
|
||||
|
||||
std::string CVersionInfo::GetLegalCopyright()
|
||||
{
|
||||
return GetValue(CVTW2S(_T("LegalCopyright")));
|
||||
}
|
||||
|
||||
std::string CVersionInfo::GetOriginalFilename()
|
||||
{
|
||||
return GetValue(CVTW2S(_T("OriginalFilename")));
|
||||
}
|
||||
|
||||
std::string CVersionInfo::GetProductName()
|
||||
{
|
||||
return GetValue(CVTW2S(_T("Productname")));
|
||||
}
|
||||
|
||||
std::string CVersionInfo::GetProductVersionAsString()
|
||||
{
|
||||
return GetValue(CVTW2S(_T("ProductVersion")));
|
||||
}
|
||||
|
||||
int CVersionInfo::GetNumberOfTranslations()
|
||||
{
|
||||
return m_nTranslations;
|
||||
}
|
||||
|
||||
std::string CVersionInfo::GetComments()
|
||||
{
|
||||
return GetValue(CVTW2S(_T("Comments")));
|
||||
}
|
||||
|
||||
std::string CVersionInfo::GetLegalTrademarks()
|
||||
{
|
||||
return GetValue(CVTW2S(_T("LegalTrademarks")));
|
||||
}
|
||||
|
||||
std::string CVersionInfo::GetPrivateBuild()
|
||||
{
|
||||
return GetValue(CVTW2S(_T("PrivateBuild")));
|
||||
}
|
||||
|
||||
std::string CVersionInfo::GetSpecialBuild()
|
||||
{
|
||||
return GetValue(CVTW2S(_T("SpecialBuild")));
|
||||
}
|
||||
|
||||
CVersionInfo::TRANSLATION* CVersionInfo::GetTranslation(int nIndex)
|
||||
{
|
||||
return &m_pTranslations[nIndex];
|
||||
}
|
||||
|
||||
void CVersionInfo::SetTranslation(int nIndex)
|
||||
{
|
||||
TRANSLATION* pTranslation = GetTranslation(nIndex);
|
||||
m_wLangID = pTranslation->m_wLangID;
|
||||
m_wCharset = pTranslation->m_wCodePage;
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
Module : VersionInfo.H
|
||||
Purpose: Interface for an MFC class encapsulation of Version Infos
|
||||
Created: PJN / 10-04-2000
|
||||
|
||||
Copyright (c) 2000 by PJ Naughter.
|
||||
All rights reserved.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/////////////////////////////// Defines ///////////////////////////////////////
|
||||
#ifndef __VERSIONINFO_H__
|
||||
#define __VERSIONINFO_H__
|
||||
|
||||
// NOTE: This class was refactored to use std::string instead of CString
|
||||
#include <string>
|
||||
|
||||
//Pull in the win32 version Library
|
||||
#pragma comment(lib, "version.lib")
|
||||
|
||||
|
||||
/////////////////////////////// Classes ///////////////////////////////////////
|
||||
|
||||
|
||||
class CVersionInfo
|
||||
{
|
||||
public:
|
||||
struct TRANSLATION
|
||||
{
|
||||
WORD m_wLangID; //e.g. 0x0409 LANG_ENGLISH, SUBLANG_ENGLISH_USA
|
||||
WORD m_wCodePage; //e.g. 1252 Codepage for Windows:Multilingual
|
||||
};
|
||||
|
||||
//Constructors / Destructors
|
||||
CVersionInfo();
|
||||
~CVersionInfo();
|
||||
|
||||
//methods:
|
||||
BOOL Load(HMODULE module);
|
||||
BOOL Load(const std::wstring& sFileName);
|
||||
VS_FIXEDFILEINFO* GetFixedFileInfo();
|
||||
DWORD GetFileFlagsMask();
|
||||
DWORD GetFileFlags();
|
||||
DWORD GetOS();
|
||||
DWORD GetFileType();
|
||||
DWORD GetFileSubType();
|
||||
FILETIME GetCreationTime();
|
||||
unsigned __int64 GetFileVersion();
|
||||
unsigned __int64 GetProductVersion();
|
||||
std::string GetValue(const std::string& sKeyName);
|
||||
std::string GetComments();
|
||||
std::string GetCompanyName();
|
||||
std::string GetFileDescription();
|
||||
std::string GetFileVersionAsString();
|
||||
std::string GetFileVersionAsDotString();
|
||||
std::string GetInternalName();
|
||||
std::string GetLegalCopyright();
|
||||
std::string GetLegalTrademarks();
|
||||
std::string GetOriginalFilename();
|
||||
std::string GetPrivateBuild();
|
||||
std::string GetProductName();
|
||||
std::string GetProductVersionAsString();
|
||||
std::string GetSpecialBuild();
|
||||
int GetNumberOfTranslations();
|
||||
TRANSLATION* GetTranslation(int nIndex);
|
||||
void SetTranslation(int nIndex);
|
||||
|
||||
protected:
|
||||
//Methods
|
||||
void Unload();
|
||||
|
||||
//Data
|
||||
WORD m_wLangID; //The current language ID of the resource
|
||||
WORD m_wCharset; //The current Character set ID of the resource
|
||||
LPVOID m_pVerData; //Pointer to the Version info blob
|
||||
TRANSLATION* m_pTranslations; //Pointer to the "\\VarFileInfo\\Translation" version info
|
||||
int m_nTranslations; //The number of translated version infos in the resource
|
||||
VS_FIXEDFILEINFO* m_pffi; //Pointer to the fixed size version info data
|
||||
};
|
||||
|
||||
|
||||
#endif //__VERSIONINFO_H__
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* VideoControl.cpp
|
||||
* Copyright (c) 2013 ROBLOX Corp. All Rights Reserved.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "VideoControl.h"
|
||||
|
||||
// System Headers
|
||||
#define NOMINMAX // need to define this before windows.h
|
||||
#include <d3d9.h>
|
||||
#include <d3dx9.h>
|
||||
|
||||
// Roblox Headers
|
||||
#include "v8datamodel/ContentProvider.h"
|
||||
#include "v8datamodel/GameSettings.h"
|
||||
#include "util/FileSystem.h"
|
||||
#include "util/standardout.h"
|
||||
#include "GfxBase/FrameRateManager.h"
|
||||
#include "GfxBase/ViewBase.h"
|
||||
|
||||
using namespace RBX;
|
||||
|
||||
namespace RBX{
|
||||
|
||||
|
||||
static void logError(std::string errorString)
|
||||
{
|
||||
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_ERROR, "%s\r\n", errorString.c_str());
|
||||
}
|
||||
|
||||
VideoControl::VideoControl(IVideoCapture *capture, RBX::ViewBase *rbxView, FrameRateManager *frameRateManager, Verb *verb)
|
||||
{
|
||||
this->capture.reset(capture);
|
||||
RBXASSERT(verb);
|
||||
this->verb = verb;
|
||||
|
||||
RBXASSERT(rbxView);
|
||||
this->rbxView = rbxView;
|
||||
this->frameRateManager = frameRateManager;
|
||||
|
||||
recorded = false;
|
||||
videoQuality = -1;
|
||||
setVideoQuality(GameSettings::singleton().getVideoQualitySetting() );
|
||||
}
|
||||
|
||||
bool VideoControl::isVideoRecordingStopped()
|
||||
{
|
||||
return !capture->isRunning();
|
||||
}
|
||||
|
||||
bool VideoControl::isVideoRecording()
|
||||
{
|
||||
return capture->isRunning();
|
||||
}
|
||||
|
||||
bool VideoControl::isVideoPaused()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VideoControl::isReadyToUpload()
|
||||
{
|
||||
return recorded && !capture->isRunning();
|
||||
}
|
||||
|
||||
void VideoControl::startRecording(RBX::Soundscape::SoundService *soundservice)
|
||||
{
|
||||
setVideoQuality(GameSettings::singleton().getVideoQualitySetting());
|
||||
RBXASSERT(soundservice);
|
||||
|
||||
if(soundservice)
|
||||
{
|
||||
soundState.reset(new SoundState());
|
||||
|
||||
soundState->createDSPFunction = boost::bind(&RBX::Soundscape::SoundService::createDSP, soundservice, _1);
|
||||
soundState->getSampleRateFunction = boost::bind(&RBX::Soundscape::SoundService::getSampleRate, soundservice);
|
||||
soundState->enabledFunction = boost::bind(&RBX::Soundscape::SoundService::enabled, soundservice);
|
||||
}
|
||||
|
||||
std::pair<unsigned, unsigned> dimensions = rbxView->setFrameDataCallback(boost::bind(&VideoControl::onFrameData, this, _1));
|
||||
|
||||
bool captureStarted = dimensions.first && dimensions.second && capture->start(dimensions.first, dimensions.second, soundState.get());
|
||||
RBXASSERT(captureStarted);
|
||||
|
||||
if (captureStarted)
|
||||
{
|
||||
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_INFO, "Video recording started");
|
||||
frameRateManager->PauseAutoAdjustment();
|
||||
recorded = false;
|
||||
}
|
||||
}
|
||||
|
||||
void VideoControl::stopRecording()
|
||||
{
|
||||
recorded = true;
|
||||
|
||||
capture->stop();
|
||||
frameRateManager->ResumeAutoAdjustment();
|
||||
|
||||
rbxView->setFrameDataCallback(boost::function<void(void*)>());
|
||||
}
|
||||
|
||||
void VideoControl::pause()
|
||||
{
|
||||
}
|
||||
|
||||
void VideoControl::unPause()
|
||||
{
|
||||
}
|
||||
|
||||
void VideoControl::setVideoQuality(int vq)
|
||||
{
|
||||
capture->setVideoQuality(vq);
|
||||
}
|
||||
|
||||
void VideoControl::onFrameData(void* device)
|
||||
{
|
||||
if (capture->isRunning())
|
||||
{
|
||||
capture->pushNextFrame(device, verb);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* VideoControl.h
|
||||
* Copyright (c) 2013 ROBLOX Corp. All Rights Reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// Standard C/C++ Headers
|
||||
#include <string>
|
||||
|
||||
// Roblox Headers
|
||||
#include "util/SoundService.h"
|
||||
|
||||
struct IDirect3DDevice9;
|
||||
struct IDirect3DSwapChain9;
|
||||
|
||||
namespace RBX {
|
||||
|
||||
class FrameRateManager;
|
||||
class ViewBase;
|
||||
|
||||
/*
|
||||
* Stores functions required by the recording loop to perform audio recording
|
||||
* Also stores whether sound recording is to be done or not.
|
||||
*/
|
||||
struct SoundState
|
||||
{
|
||||
|
||||
public:
|
||||
SoundState()
|
||||
{
|
||||
}
|
||||
~SoundState() {}
|
||||
|
||||
boost::function<FMOD::DSP*(FMOD_DSP_DESCRIPTION&)> createDSPFunction;
|
||||
boost::function<int()> getSampleRateFunction;
|
||||
boost::function<bool()> enabledFunction;
|
||||
};
|
||||
|
||||
class IVideoCapture
|
||||
{
|
||||
public:
|
||||
virtual ~IVideoCapture() {};
|
||||
virtual bool start(int cx, int cy, SoundState *s) = 0;
|
||||
virtual bool stop() = 0;
|
||||
virtual bool isRunning() = 0;
|
||||
virtual void setVideoQuality(int vq) = 0;
|
||||
virtual void pushNextFrame(void* device, Verb *cancelAction) = 0;
|
||||
virtual std::string &getFileName() = 0;
|
||||
};
|
||||
|
||||
class VideoControl
|
||||
{
|
||||
private:
|
||||
bool recorded;
|
||||
|
||||
int videoQuality;
|
||||
|
||||
boost::scoped_ptr<IVideoCapture> capture;
|
||||
boost::scoped_ptr<SoundState> soundState;
|
||||
RBX::ViewBase *rbxView;
|
||||
FrameRateManager *frameRateManager;
|
||||
Verb *verb;
|
||||
|
||||
void onFrameData(void* device);
|
||||
public:
|
||||
VideoControl(IVideoCapture *capture, RBX::ViewBase *rbxView, FrameRateManager *frameRateManager, Verb *verb);
|
||||
|
||||
void unPause();
|
||||
void pause();
|
||||
void stopRecording();
|
||||
void startRecording(RBX::Soundscape::SoundService *soundservice);
|
||||
bool isReadyToUpload();
|
||||
bool isVideoPaused();
|
||||
bool isVideoRecording();
|
||||
bool isVideoRecordingStopped();
|
||||
void setVideoQuality(int vq);
|
||||
std::string &getFileName() { return capture->getFileName(); }
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
///////////////////////////////
|
||||
/* VistaTools.cxx - version 1.0
|
||||
|
||||
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
||||
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
||||
PARTICULAR PURPOSE.
|
||||
|
||||
Copyright (C) 2006. WinAbility Software Corporation. All rights reserved.
|
||||
|
||||
Author: Andrei Belogortseff [ http://www.tweak-uac.com ]
|
||||
|
||||
TERMS OF USE: You are free to use this file in any way you like,
|
||||
for both the commercial and non-commercial purposes, royalty-free,
|
||||
AS LONG AS you agree with the warranty disclaimer above,
|
||||
EXCEPT that you may not remove or modify this or any of the
|
||||
preceeding paragraphs. If you make any changes, please document
|
||||
them in the MODIFICATIONS section below. If the changes are of general
|
||||
interest, please let us know and we will consider incorporating them in
|
||||
this file, as well.
|
||||
|
||||
If you use this file in your own project, an acknowledgement will be appreciated,
|
||||
although it's not required.
|
||||
|
||||
SUMMARY:
|
||||
|
||||
This file contains several Vista-specific functions helpful when dealing with the
|
||||
"elevation" features of Windows Vista. See the descriptions of the functions below
|
||||
for information on what each function does and how to use it.
|
||||
|
||||
This file contains the Win32 stuff only, it can be used with or without other frameworks,
|
||||
such as MFC, ATL, etc.
|
||||
|
||||
HOW TO USE THIS FILE:
|
||||
|
||||
Make sure you have the latest Windows SDK (see msdn.microsoft.com for more information)
|
||||
or this file may not compile!
|
||||
|
||||
(The above should be done once and only once per project).
|
||||
|
||||
The file VistaTools.cxx can be included in the VisualStudio projects, but it should be
|
||||
excluded from the build process (because its contents is compiled when it is included
|
||||
in another .cpp file with IMPLEMENT_VISTA_TOOLS defined, as shown above.)
|
||||
|
||||
MODIFICATIONS:
|
||||
v.1.0 (2006-Dec-16) created by Andrei Belogortseff.
|
||||
v.2.0 (2008-Aug-04) Erik Cassel: Removed most of the APIs Roblox doesn't need. Query functions remain
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
|
||||
|
||||
|
||||
#include <assert.h>
|
||||
#include <string>
|
||||
#include <comdef.h>
|
||||
#include <taskschd.h>
|
||||
|
||||
|
||||
bool IsVistaPlus()
|
||||
{
|
||||
OSVERSIONINFO osver = {0};
|
||||
|
||||
osver.dwOSVersionInfoSize = sizeof( OSVERSIONINFO );
|
||||
|
||||
if ( ::GetVersionEx( &osver ) &&
|
||||
osver.dwPlatformId == VER_PLATFORM_WIN32_NT &&
|
||||
(osver.dwMajorVersion >= 6 ) )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Is64BitWindows()
|
||||
{
|
||||
#if defined(_WIN64)
|
||||
return true; // 64-bit programs run only on Win64
|
||||
#elif defined(_WIN32)
|
||||
// 32-bit programs run on both 32-bit and 64-bit Windows
|
||||
// so must sniff
|
||||
BOOL f64 = FALSE;
|
||||
return IsWow64Process(GetCurrentProcess(), &f64) && f64;
|
||||
#else
|
||||
return false; // Win64 does not support Win16
|
||||
#endif
|
||||
}
|
||||
|
||||
void GetElevationType( __out TOKEN_ELEVATION_TYPE * ptet )
|
||||
{
|
||||
assert( IsVistaPlus() );
|
||||
assert( ptet );
|
||||
|
||||
HRESULT hResult = E_FAIL; // assume an error occured
|
||||
CHandle hToken;
|
||||
|
||||
if ( !::OpenProcessToken(
|
||||
::GetCurrentProcess(),
|
||||
TOKEN_QUERY,
|
||||
&hToken.m_h ) )
|
||||
{
|
||||
throw std::runtime_error("GetElevationType OpenProcessToken failed");
|
||||
}
|
||||
|
||||
DWORD dwReturnLength = 0;
|
||||
|
||||
if ( !::GetTokenInformation(
|
||||
hToken.m_h,
|
||||
TokenElevationType,
|
||||
ptet,
|
||||
sizeof( *ptet ),
|
||||
&dwReturnLength ) )
|
||||
{
|
||||
throw std::runtime_error("GetElevationType GetTokenInformation failed");
|
||||
}
|
||||
else
|
||||
{
|
||||
assert( dwReturnLength == sizeof( *ptet ) );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool IsElevated()
|
||||
{
|
||||
assert( IsVistaPlus() );
|
||||
|
||||
bool result = false;
|
||||
CHandle hToken;
|
||||
|
||||
if ( !::OpenProcessToken(
|
||||
::GetCurrentProcess(),
|
||||
TOKEN_QUERY,
|
||||
&hToken.m_h ) )
|
||||
{
|
||||
throw std::runtime_error("IsElevated OpenProcessToken failed");
|
||||
}
|
||||
|
||||
TOKEN_ELEVATION te = { 0 };
|
||||
DWORD dwReturnLength = 0;
|
||||
|
||||
if ( !::GetTokenInformation(
|
||||
hToken.m_h,
|
||||
TokenElevation,
|
||||
&te,
|
||||
sizeof( te ),
|
||||
&dwReturnLength ) )
|
||||
{
|
||||
throw std::runtime_error("IsElevated GetTokenInformation failed");
|
||||
}
|
||||
else
|
||||
{
|
||||
assert( dwReturnLength == sizeof( te ) );
|
||||
|
||||
result = (te.TokenIsElevated != 0);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool IsUacEnabled()
|
||||
{
|
||||
CRegKey k;
|
||||
return FAILED(k.Open(HKEY_LOCAL_MACHINE, _T("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System\\EnableLUA")));
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
#pragma once
|
||||
#include "shlobj.h"
|
||||
|
||||
bool IsVistaPlus();
|
||||
|
||||
bool Is64BitWindows();
|
||||
|
||||
/*
|
||||
Use IsVistaPlus() to determine whether the current process is running under Windows Vista or
|
||||
(or a later version of Windows, whatever it will be)
|
||||
|
||||
Return Values:
|
||||
If the function succeeds, and the current version of Windows is Vista or later,
|
||||
the return value is TRUE.
|
||||
If the function fails, or if the current version of Windows is older than Vista
|
||||
(that is, if it is Windows XP, Windows 2000, Windows Server 2003, Windows 98, etc.)
|
||||
the return value is FALSE.
|
||||
*/
|
||||
|
||||
void GetElevationType( __out TOKEN_ELEVATION_TYPE * ptet );
|
||||
|
||||
/*
|
||||
Use GetElevationType() to determine the elevation type of the current process.
|
||||
|
||||
Parameters:
|
||||
|
||||
ptet
|
||||
[out] Pointer to a variable that receives the elevation type of the current process.
|
||||
|
||||
The possible values are:
|
||||
|
||||
TokenElevationTypeDefault - User is not using a "split" token.
|
||||
This value indicates that either UAC is disabled, or the process is started
|
||||
by a standard user (not a member of the Administrators group).
|
||||
|
||||
The following two values can be returned only if both the UAC is enabled and
|
||||
the user is a member of the Administrator's group (that is, the user has a "split" token):
|
||||
|
||||
TokenElevationTypeFull - the process is running elevated.
|
||||
|
||||
TokenElevationTypeLimited - the process is not running elevated.
|
||||
|
||||
Return Values:
|
||||
If the function succeeds, the return value is S_OK.
|
||||
If the function fails, the return value is E_FAIL. To get extended error information,
|
||||
call GetLastError().
|
||||
*/
|
||||
|
||||
bool IsElevated();
|
||||
|
||||
/*
|
||||
Use IsElevated() to determine whether the current process is elevated or not.
|
||||
|
||||
Parameters:
|
||||
|
||||
pbElevated
|
||||
[out] [optional] Pointer to a BOOL variable that, if non-NULL, receives the result.
|
||||
|
||||
The possible values are:
|
||||
|
||||
TRUE - the current process is elevated.
|
||||
This value indicates that either UAC is enabled, and the process was elevated by
|
||||
the administrator, or that UAC is disabled and the process was started by a user
|
||||
who is a member of the Administrators group.
|
||||
|
||||
FALSE - the current process is not elevated (limited).
|
||||
This value indicates that either UAC is enabled, and the process was started normally,
|
||||
without the elevation, or that UAC is disabled and the process was started by a standard user.
|
||||
|
||||
Return Values
|
||||
If the function succeeds, and the current process is elevated, the return value is S_OK.
|
||||
If the function succeeds, and the current process is not elevated, the return value is S_FALSE.
|
||||
If the function fails, the return value is E_FAIL. To get extended error information,
|
||||
call GetLastError().
|
||||
*/
|
||||
|
||||
|
||||
bool IsUacEnabled();
|
||||
|
||||
/*
|
||||
Use IsUacEnabled() to determine whether UAC is enabled or not.
|
||||
*/
|
||||
|
||||
#ifndef FOLDERID_LocalAppDataLow
|
||||
DEFINE_KNOWN_FOLDER(FOLDERID_LocalAppDataLow, 0xA520A1A4, 0x1780, 0x4FF6, 0xBD, 0x18, 0x16, 0x73, 0x43, 0xC5, 0xAF, 0x16);
|
||||
DEFINE_KNOWN_FOLDER(FOLDERID_LocalAppData, 0xF1B32785, 0x6FBA, 0x4FCF, 0x9D, 0x55, 0x7B, 0x8E, 0x7F, 0x15, 0x70, 0x91);
|
||||
DEFINE_KNOWN_FOLDER(FOLDERID_Programs, 0xA77F5D77, 0x2E2B, 0x44C3, 0xA6, 0xA2, 0xAB, 0xA6, 0x01, 0x05, 0x4A, 0x51);
|
||||
DEFINE_KNOWN_FOLDER(FOLDERID_ProgramData, 0x62AB5D82, 0xFDC1, 0x4DC3, 0xA9, 0xDD, 0x07, 0x0D, 0x1D, 0x49, 0x5D, 0x97);
|
||||
#endif
|
||||
|
||||
#ifndef KF_FLAG_CREATE
|
||||
#define KF_FLAG_CREATE 0x00008000
|
||||
#endif
|
||||
|
||||
class VistaAPIs
|
||||
{
|
||||
typedef HRESULT (WINAPI* GetKnownFolderPathPtr)(const GUID& rfid,
|
||||
DWORD dwFlags,
|
||||
HANDLE hToken,
|
||||
PWSTR *ppszPath);
|
||||
GetKnownFolderPathPtr getKnownFolderPath;
|
||||
HMODULE gShell32DLLInst;
|
||||
|
||||
public:
|
||||
VistaAPIs()
|
||||
:getKnownFolderPath(NULL)
|
||||
{
|
||||
gShell32DLLInst = LoadLibrary(TEXT("Shell32.dll"));
|
||||
if(gShell32DLLInst)
|
||||
{
|
||||
getKnownFolderPath = (GetKnownFolderPathPtr) GetProcAddress(gShell32DLLInst, "SHGetKnownFolderPath");
|
||||
}
|
||||
}
|
||||
|
||||
~VistaAPIs()
|
||||
{
|
||||
if (gShell32DLLInst)
|
||||
::FreeLibrary(gShell32DLLInst);
|
||||
}
|
||||
|
||||
HRESULT SHGetKnownFolderPath(const GUID& rfid, DWORD dwFlags, HANDLE hToken, PWSTR *ppszPath)
|
||||
{
|
||||
if (getKnownFolderPath==NULL)
|
||||
return E_NOTIMPL;
|
||||
return getKnownFolderPath(rfid, dwFlags, hToken, ppszPath);
|
||||
}
|
||||
|
||||
bool isVistaOrBetter()
|
||||
{
|
||||
//return if not Windows Vista or later
|
||||
OSVERSIONINFO osvi = {0};
|
||||
osvi.dwOSVersionInfoSize=sizeof(osvi);
|
||||
GetVersionEx (&osvi);
|
||||
if(osvi.dwMajorVersion<6)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
+4417
File diff suppressed because it is too large
Load Diff
+760
@@ -0,0 +1,760 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// SiteLock 1.14
|
||||
// ATL sample code for restricting activation of ActiveX controls.
|
||||
// Copyright Microsoft Corporation. All rights reserved.
|
||||
// Last updated: July 19 2007
|
||||
|
||||
// Includes
|
||||
#include <atlctl.h>
|
||||
#include <comdef.h>
|
||||
#include <shlguid.h>
|
||||
#include <wininet.h>
|
||||
|
||||
// Pragmas
|
||||
#pragma once
|
||||
|
||||
// Version information
|
||||
#define SITELOCK_VERSION 0x00010014 // 1.14
|
||||
|
||||
#ifdef SITELOCK_SUPPORT_DOWNLOAD
|
||||
#pragma message("sitelock.h : This version of SiteLock does not support downloading.")
|
||||
#endif
|
||||
|
||||
// Overview:
|
||||
// To enable scripting, developers must declare their ActiveX controls as "safe for scripting".
|
||||
// This is done by implementing interface IObjectSafety, which comes with very important safety
|
||||
// assumptions. Once marked as "safe for scripting", ActiveX controls may be activated by untrusted
|
||||
// web sites. Therefore "safe for scripting" controls must guarantee all their methods are safe
|
||||
// regardless of the activation context. Practically however, it may not be possible for an ActiveX
|
||||
// control to guarantee safety in all activation contexts. The site lock framework allows developers
|
||||
// to specify which zones and domains can instantiate ActiveX controls. For example, this may allow
|
||||
// a developer to implement methods that can only be called if the activation context is the
|
||||
// intranet zone.
|
||||
|
||||
// Usage:
|
||||
// 1/ Include current header file sitelock.h (after including ATL header files).
|
||||
//
|
||||
// 2/ Derive from "public IObjectSafetySiteLockImpl <CYourClass, INTERFACESAFE_FOR...>,".
|
||||
// This replaces the default IObjectSafetyImpl interface implementation.
|
||||
//
|
||||
// 3/ Add the following to your control's COM map:
|
||||
// COM_INTERFACE_ENTRY(IObjectSafety)
|
||||
// COM_INTERFACE_ENTRY(IObjectSafetySiteLock)
|
||||
//
|
||||
// 4/ Add one of the following to specify allowed activation contexts:
|
||||
// a) A public (or friend) member variable, for example:
|
||||
// const CYourClass::SiteList CYourClass::rgslTrustedSites[6] =
|
||||
// {{ SiteList::Deny, L"http", L"users.microsoft.com" },
|
||||
// { SiteList::Allow, L"http", L"microsoft com" },
|
||||
// { SiteList::Allow, L"http", SITELOCK_INTRANET_ZONE },
|
||||
// { SiteList::Deny, L"https", L"users.microsoft.com" },
|
||||
// { SiteList::Allow, L"https", L"microsoft.com" },
|
||||
// { SiteList::Allow, L"https", SITELOCK_INTRANET_ZONE }};
|
||||
//
|
||||
// b) A set of site lock macros, for example:
|
||||
// #define SITELOCK_USE_MAP (prior to including sitelock.h)
|
||||
// BEGIN_SITELOCK_MAP()
|
||||
// SITELOCK_DENYHTTP ( L"users.microsoft.com" )
|
||||
// SITELOCK_ALLOWHTTP ( L"microsoft com" )
|
||||
// SITELOCK_ALLOWHTTP ( SITELOCK_INTRANET_ZONE )
|
||||
// SITELOCK_DENYHTTPS ( L"users.microsoft.com" )
|
||||
// SITELOCK_ALLOWHTTPS ( L"microsoft.com" )
|
||||
// SITELOCK_ALLOWHTTPS ( SITELOCK_INTRANET_ZONE )
|
||||
// END_SITELOCK_MAP()
|
||||
//
|
||||
// The examples above block "*.users.microsoft.com" sites (http and https).
|
||||
// The examples above allow "*.microsoft.com" sites (http and https).
|
||||
// The examples above allow intranet sites (http and https).
|
||||
//
|
||||
// 5/ Choose an expiry lifespan:
|
||||
// You can specify the lifespan of your control one of two ways.
|
||||
// By declaring an enumeration (slightly more efficient):
|
||||
// enum { dwControlLifespan = (lifespan in days) };
|
||||
// By declaring a member variable:
|
||||
// static const DWORD dwControlLifespan = (lifespan in days);
|
||||
// When in doubt, choose a shorter duration rather than a longer one. Expiration can be
|
||||
// disabled by adding #define SITELOCK_NO_EXPIRY before including sitelock.h.
|
||||
//
|
||||
// 6/ Implement IObjectWithSite or IOleObject:
|
||||
// IObjectWithSite is a lightweight interface able to indicate the activation URL to site lock.
|
||||
// IOleObject is a heavier interface providing additional OLE capabilities.
|
||||
// If you need IOleObject, add #define SITELOCK_USE_IOLEOBJECT before including sitelock.h.
|
||||
// Otherwise, simply implement IObjectWithSite:
|
||||
// - Derive from "IObjectWithSiteImpl<CYourClass>".
|
||||
// - Add COM_INTERFACE_ENTRY(IObjectWithSite) to your control's COM map.
|
||||
// You should never implement both IObjectWithSite and IOleObject.
|
||||
//
|
||||
// 7/ Link with urlmon.lib.
|
||||
|
||||
// Detailed usage:
|
||||
// --- Entries ---:
|
||||
// Site lock entries are defined by the following elements:
|
||||
// iAllowType is:
|
||||
// SiteList::Allow: allowed location
|
||||
// SiteList::Deny: blocked location
|
||||
// szScheme is:
|
||||
// L"http": non-SSL location
|
||||
// L"https": SSL-enabled location
|
||||
// Other: in rare cases, the scheme may be outlook:, ms-help:, etc.
|
||||
// szDomain is:
|
||||
// Doman: a string defining a domain
|
||||
// Zone: a constant specifying a zone
|
||||
//
|
||||
// --- Ordering ---:
|
||||
// Entries are matched in the order they appear in.
|
||||
// The first entry that matches will be accepted.
|
||||
// Deny entries should therefore be placed before allow entries.
|
||||
//
|
||||
// --- Protocols ---:
|
||||
// To support multiple protocols (http and https), define separate entries.
|
||||
//
|
||||
// --- Domain names ---:
|
||||
// This sample code performs a case-sensitive comparison after domain normalization.
|
||||
// Whether domain normalization converts strings to lower case depends on the scheme provider.
|
||||
//
|
||||
// If a domain does not contain any special indicator, only domains with the right suffix will
|
||||
// match. For example:
|
||||
// An entry of "microsoft.com" will match "microsoft.com".
|
||||
// An entry of "microsoft.com" will match "office.microsoft.com"
|
||||
// An entry of "microsoft.com" will not match "mymicrosoft.com"
|
||||
// An entry of "microsoft.com" will not match "www.microsoft.com.hacker.com"
|
||||
//
|
||||
// If a domain begins with "*.", only child domains will match.
|
||||
// For example:
|
||||
// An entry of "*.microsoft.com" will match "foo.microsoft.com".
|
||||
// An entry of "*.microsoft.com" will not match "microsoft.com".
|
||||
//
|
||||
// If a domain begins with "=", only the specified domain will match.
|
||||
// For example:
|
||||
// An entry of "=microsoft.com" will match "microsoft.com".
|
||||
// An entry of "=microsoft.com" will not match "foo.microsoft.com".
|
||||
//
|
||||
// If a domain is set to "*", all domains will match.
|
||||
// This is useful to only restrict to specific schemes (ex: http vs. https).
|
||||
//
|
||||
// If a domain name is NULL, then the scheme provider should return an error when asking for the
|
||||
// domain. This is appropriate for protocols (outlook: or ms-help:) that do not use server names.
|
||||
//
|
||||
// If a domain name is SITELOCK_INTRANET_ZONE, then any server in the Intranet zone will match.
|
||||
// Due to a zone limitation, sites in the user's Trusted Sites list will also match. However,
|
||||
// since Trusted Sites typically permit downloading and running of unsigned, unsafe controls,
|
||||
// security is limited for those sites anyway.
|
||||
//
|
||||
// If a domain name is SITELOCK_MYCOMPUTER_ZONE, then any page residing on the user's local
|
||||
// machine will match.
|
||||
//
|
||||
// If a domain name is SITELOCK_TRUSTED_ZONE, then any page residing in the user's Trusted
|
||||
// Sites list will match.
|
||||
|
||||
|
||||
// Language checks
|
||||
#ifndef __cplusplus
|
||||
#error ATL Requires C++
|
||||
#endif
|
||||
|
||||
// Windows constants
|
||||
#if (WINVER < 0x0600)
|
||||
#define IDN_USE_STD3_ASCII_RULES 0x02 // Enforce STD3 ASCII restrictions
|
||||
#endif
|
||||
|
||||
// Function prototypes
|
||||
typedef int (WINAPI * PFN_IdnToAscii)(DWORD, LPCWSTR, int, LPWSTR, int);
|
||||
|
||||
// Macros
|
||||
#ifndef cElements
|
||||
template<typename T> static char cElementsVerify(void const *, T) throw() { return 0; }
|
||||
template<typename T> static void cElementsVerify(T *const, T *const *) throw() {};
|
||||
#define cElements(arr) (sizeof(cElementsVerify(arr,&(arr))) * (sizeof(arr)/sizeof(*(arr))))
|
||||
#endif
|
||||
|
||||
// Restrictions
|
||||
#define SITELOCK_INTRANET_ZONE ((const OLECHAR *)-1)
|
||||
#define SITELOCK_MYCOMPUTER_ZONE ((const OLECHAR *)-2)
|
||||
#define SITELOCK_TRUSTED_ZONE ((const OLECHAR *)-3)
|
||||
|
||||
#ifndef SITELOCK_NO_EXPIRY
|
||||
// Helper functions for expiry
|
||||
#if defined(_WIN64) && defined(_M_IA64)
|
||||
#pragma section(".base", long, read, write)
|
||||
extern "C" __declspec(allocate(".base")) extern IMAGE_DOS_HEADER __ImageBase;
|
||||
#else
|
||||
extern "C" IMAGE_DOS_HEADER __ImageBase;
|
||||
#endif
|
||||
#define ImageNtHeaders(pBase) ((PIMAGE_NT_HEADERS)((PCHAR)(pBase) + ((PIMAGE_DOS_HEADER)(pBase))->e_lfanew))
|
||||
|
||||
#define LODWORD(_qw) ((DWORD)(_qw))
|
||||
#define HIDWORD(_qw) ((DWORD)(((_qw) >> 32) & 0xffffffff))
|
||||
inline void _UNIXTimeToFILETIME(time_t t, LPFILETIME ft)
|
||||
{
|
||||
// The time_t is a 32-bit value for the number of seconds since January 1, 1970.
|
||||
// A FILETIME is a 64-bit for the number of 100-nanosecond periods since January 1, 1601.
|
||||
// Convert by multiplying the time_t value by 1e+7 to get to the same base granularity,
|
||||
// then add the numeric equivalent of January 1, 1970 as FILETIME.
|
||||
|
||||
ULONGLONG qw = ((ULONGLONG)t * 10000000ui64) + 116444736000000000ui64;
|
||||
ft->dwHighDateTime = HIDWORD(qw);
|
||||
ft->dwLowDateTime = LODWORD(qw);
|
||||
}
|
||||
inline time_t _FILETIMEToUNIXTime(LPFILETIME ft)
|
||||
{
|
||||
ULONGLONG qw = (((ULONGLONG)ft->dwHighDateTime)<<32) + ft->dwLowDateTime;
|
||||
return (time_t)((qw - 116444736000000000ui64) / 10000000ui64);
|
||||
}
|
||||
#endif // SITELOCK_NO_EXPIRY
|
||||
|
||||
// Interface declaring "safe for scripting" methods with additional site lock capabilities
|
||||
class __declspec(uuid("7FEB54AE-E3F9-40FC-AB5A-28A545C0F193")) ATL_NO_VTABLE IObjectSafetySiteLock : public IObjectSafety
|
||||
{
|
||||
public:
|
||||
// Site lock entry definition
|
||||
struct SiteList {
|
||||
enum SiteListCategory {
|
||||
Allow, // permit
|
||||
Deny, // disallow
|
||||
Download // OBSOLETE, do not use
|
||||
} iAllowType;
|
||||
const OLECHAR * szScheme; // scheme (http or https)
|
||||
const OLECHAR * szDomain; // domain
|
||||
};
|
||||
|
||||
// Capability definition
|
||||
enum Capability {
|
||||
CanDownload = 0x00000001, // OBSOLETE. Here for backwards compatibility only.
|
||||
UsesIOleObject = 0x00000002, // Use IOleObject instead of IObjectWithSite.
|
||||
HasExpiry = 0x00000004, // Control will expire when lifespan elapsed.
|
||||
};
|
||||
|
||||
// Returns capabilities (this can be used by testing tools to query for custom capabilities or version information)
|
||||
STDMETHOD (GetCapabilities) (DWORD * pdwCapability) = 0;
|
||||
|
||||
// Returns site lock entries controlling activation
|
||||
STDMETHOD (GetApprovedSites) (const SiteList ** pSiteList, DWORD * cSites) = 0;
|
||||
|
||||
// Returns lifespan as number of days and date (version 1.05 or higher)
|
||||
STDMETHOD (GetExpiryDate) (DWORD * pdwLifespan, FILETIME * pExpiryDate) = 0;
|
||||
};
|
||||
|
||||
|
||||
#ifdef SITELOCK_USE_MAP
|
||||
// Site lock actual map entry macro
|
||||
#define SITELOCK_ALLOWHTTPS(domain) {IObjectSafetySiteLock::SiteList::Allow, L"https", domain},
|
||||
#define SITELOCK_DENYHTTPS(domain) {IObjectSafetySiteLock::SiteList::Deny, L"https", domain},
|
||||
#define SITELOCK_ALLOWHTTP(domain) {IObjectSafetySiteLock::SiteList::Allow, L"http", domain},
|
||||
#define SITELOCK_DENYHTTP(domain) {IObjectSafetySiteLock::SiteList::Deny, L"http", domain},
|
||||
|
||||
// Site lock begin map entry macro
|
||||
#define BEGIN_SITELOCK_MAP() \
|
||||
static const IObjectSafetySiteLock::SiteList * GetSiteLockMapAndCount(DWORD * dwCount ) \
|
||||
{ \
|
||||
static IObjectSafetySiteLock::SiteList rgslTrustedSites[] = { \
|
||||
|
||||
// Site lock end map entry macro
|
||||
#define END_SITELOCK_MAP() \
|
||||
{(IObjectSafetySiteLock::SiteList::SiteListCategory)0,0,0}}; \
|
||||
*dwCount = cElements(rgslTrustedSites) - 1; \
|
||||
return rgslTrustedSites; \
|
||||
} \
|
||||
static const IObjectSafetySiteLock::SiteList * GetSiteLockMap() \
|
||||
{ \
|
||||
DWORD dwCount = 0; \
|
||||
return GetSiteLockMapAndCount(&dwCount); \
|
||||
} \
|
||||
static DWORD GetSiteLockMapCount() \
|
||||
{ \
|
||||
DWORD dwCount = 0; \
|
||||
GetSiteLockMapAndCount(&dwCount); \
|
||||
return dwCount; \
|
||||
} \
|
||||
|
||||
#endif // SITELOCK_USE_MAP
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// CSiteLock - Site lock templated class
|
||||
template <typename T>
|
||||
class ATL_NO_VTABLE CSiteLock
|
||||
{
|
||||
public:
|
||||
#ifdef SITELOCK_NO_EXPIRY
|
||||
bool ControlExpired(DWORD = 0) { return false; }
|
||||
#else
|
||||
bool ControlExpired(DWORD dwExpiresDays = T::dwControlLifespan)
|
||||
{
|
||||
SYSTEMTIME st = {0};
|
||||
FILETIME ft = {0};
|
||||
|
||||
GetSystemTime(&st);
|
||||
if (!SystemTimeToFileTime(&st, &ft))
|
||||
return true;
|
||||
|
||||
time_t ttTime = _FILETIMEToUNIXTime(&ft);
|
||||
time_t ttExpire = ImageNtHeaders(&__ImageBase)->FileHeader.TimeDateStamp;
|
||||
ttExpire += dwExpiresDays*86400;
|
||||
|
||||
return (ttTime > ttExpire);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SITELOCK_USE_MAP
|
||||
// Checks if the activation URL is in an allowed domain / zone
|
||||
bool InApprovedDomain(const IObjectSafetySiteLock::SiteList * rgslTrustedSites = T::GetSiteLockMap(), int cTrustedSites = T::GetSiteLockMapCount())
|
||||
#else
|
||||
// Checks if the activation URL is in an allowed domain / zone
|
||||
bool InApprovedDomain(const IObjectSafetySiteLock::SiteList * rgslTrustedSites = T::rgslTrustedSites, int cTrustedSites = cElements(T::rgslTrustedSites))
|
||||
#endif
|
||||
{
|
||||
// Retrieve the activation URL
|
||||
CComBSTR bstrUrl;
|
||||
DWORD dwZone = URLZONE_UNTRUSTED;
|
||||
if (!GetOurUrl(bstrUrl, dwZone))
|
||||
return false;
|
||||
|
||||
// Check if the activation URL is in an allowed domain / zone
|
||||
return FApprovedDomain(bstrUrl, dwZone, rgslTrustedSites, cTrustedSites);
|
||||
}
|
||||
|
||||
// Retrieves the activation URL
|
||||
bool GetOurUrl(CComBSTR &bstrURL, DWORD &dwZone)
|
||||
{
|
||||
// Declarations
|
||||
HRESULT hr = S_OK;
|
||||
CComPtr<IServiceProvider> spSrvProv;
|
||||
CComPtr<IInternetSecurityManager> spInetSecMgr;
|
||||
CComPtr<IWebBrowser2> spWebBrowser;
|
||||
|
||||
// Get the current pointer as an instance of the template class
|
||||
T * pT = static_cast<T*>(this);
|
||||
|
||||
// Retrieve the activation site
|
||||
CComPtr<IOleClientSite> spClientSite;
|
||||
#ifdef SITELOCK_USE_IOLEOBJECT
|
||||
hr = pT->GetClientSite((IOleClientSite **)&spClientSite);
|
||||
if (FAILED(hr) || spClientSite == NULL)
|
||||
return false;
|
||||
hr = spClientSite->QueryInterface(IID_IServiceProvider, (void **)&spSrvProv);
|
||||
#else
|
||||
hr = pT->GetSite(IID_IServiceProvider, (void**)&spSrvProv);
|
||||
#endif
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
// Query the site for a web browser object
|
||||
hr = spSrvProv->QueryService(SID_SWebBrowserApp, IID_IWebBrowser2, (void **)&spWebBrowser);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
// Local declarations
|
||||
CComPtr<IHTMLDocument2> spDoc;
|
||||
CComPtr<IOleContainer> spContainer;
|
||||
|
||||
// Reinitialize
|
||||
spSrvProv = NULL;
|
||||
spClientSite = NULL;
|
||||
|
||||
// Get the client site, container, and provider, etc.
|
||||
hr = pT->GetSite(IID_IOleClientSite, (void**)&spClientSite);
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
hr = spClientSite->GetContainer(&spContainer);
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
hr = spContainer->QueryInterface(IID_IHTMLDocument2, (void **)&spDoc);
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
if (FAILED(spDoc->get_URL(&bstrURL)))
|
||||
return false;
|
||||
hr = spClientSite->QueryInterface(IID_IServiceProvider, (void **)&spSrvProv);
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Query the web browser object for the activation URL
|
||||
hr = spWebBrowser->get_LocationURL(&bstrURL);
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Query the site for its associated security manager
|
||||
hr = spSrvProv->QueryService(SID_SInternetSecurityManager, IID_IInternetSecurityManager, (void **)&spInetSecMgr);
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
// Query the security manager for the zone the activation URL belongs to
|
||||
hr = spInetSecMgr->MapUrlToZone(bstrURL, &dwZone, 0);
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
// Checks if an activation URL is in an allowed domain / zone
|
||||
bool FApprovedDomain(const OLECHAR * wzUrl, DWORD dwZone, const IObjectSafetySiteLock::SiteList * rgslTrustedSites, int cTrustedSites)
|
||||
{
|
||||
// Declarations
|
||||
HRESULT hr = S_OK;
|
||||
OLECHAR wzDomain[INTERNET_MAX_HOST_NAME_LENGTH + 1] = {0};
|
||||
OLECHAR wzScheme[INTERNET_MAX_SCHEME_LENGTH + 1] = {0};
|
||||
|
||||
// Retrieve the normalized domain and scheme
|
||||
hr = GetDomainAndScheme(wzUrl, wzScheme, cElements(wzScheme), wzDomain, cElements(wzDomain));
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
// Try to match the activation URL with each entry in order
|
||||
DWORD cbScheme = (::lstrlenW(wzScheme) + 1) * sizeof(OLECHAR);
|
||||
for (int i=0; i < cTrustedSites; i++)
|
||||
{
|
||||
// Try to match by scheme
|
||||
DWORD cbSiteScheme = (::lstrlenW(rgslTrustedSites[i].szScheme) + 1) * sizeof(OLECHAR);
|
||||
if (cbScheme != cbSiteScheme)
|
||||
continue;
|
||||
if (0 != ::memcmp(wzScheme, rgslTrustedSites[i].szScheme, cbScheme))
|
||||
continue;
|
||||
|
||||
// Try to match by zone
|
||||
if (rgslTrustedSites[i].szDomain == SITELOCK_INTRANET_ZONE)
|
||||
{
|
||||
if ((dwZone == URLZONE_INTRANET) || (dwZone == URLZONE_TRUSTED))
|
||||
return rgslTrustedSites[i].iAllowType == IObjectSafetySiteLock::SiteList::Allow;
|
||||
}
|
||||
else if (rgslTrustedSites[i].szDomain == SITELOCK_MYCOMPUTER_ZONE)
|
||||
{
|
||||
if (dwZone == URLZONE_LOCAL_MACHINE)
|
||||
return rgslTrustedSites[i].iAllowType == IObjectSafetySiteLock::SiteList::Allow;
|
||||
}
|
||||
else if (rgslTrustedSites[i].szDomain == SITELOCK_TRUSTED_ZONE)
|
||||
{
|
||||
if (dwZone == URLZONE_TRUSTED)
|
||||
return rgslTrustedSites[i].iAllowType == IObjectSafetySiteLock::SiteList::Allow;
|
||||
}
|
||||
|
||||
// Try to match by domain name
|
||||
else if (MatchDomains(rgslTrustedSites[i].szDomain, wzDomain))
|
||||
{
|
||||
return rgslTrustedSites[i].iAllowType == IObjectSafetySiteLock::SiteList::Allow;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// Normalizes an international domain name
|
||||
HRESULT NormalizeDomain(OLECHAR * wzDomain, int cchDomain)
|
||||
{
|
||||
// Data validation
|
||||
if (!wzDomain)
|
||||
return E_POINTER;
|
||||
|
||||
// If the domain is only 7-bit ASCII, normalization is not required
|
||||
bool fFoundUnicode = false;
|
||||
for (const OLECHAR * wz = wzDomain; *wz != 0; wz++)
|
||||
{
|
||||
if (0x80 <= *wz)
|
||||
{
|
||||
fFoundUnicode = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!fFoundUnicode)
|
||||
return S_OK;
|
||||
|
||||
// Construct a fully qualified path to the Windows system directory
|
||||
static const WCHAR wzNormaliz[] = L"normaliz.dll";
|
||||
static const int cchNormaliz = cElements(wzNormaliz);
|
||||
WCHAR wzDllPath[MAX_PATH + 1] = {0};
|
||||
if (!::GetSystemDirectoryW(wzDllPath, cElements(wzDllPath) - cchNormaliz - 1))
|
||||
return E_FAIL;
|
||||
int cchDllPath = ::lstrlenW(wzDllPath);
|
||||
if (!cchDllPath)
|
||||
return E_FAIL;
|
||||
if (wzDllPath[cchDllPath-1] != L'\\')
|
||||
wzDllPath[cchDllPath++] = L'\\';
|
||||
::CopyMemory(wzDllPath + cchDllPath, wzNormaliz, cchNormaliz * sizeof(WCHAR));
|
||||
|
||||
// Load the DLL used for domain normalization
|
||||
HMODULE hNormaliz = ::LoadLibraryExW(wzDllPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
|
||||
if (!hNormaliz)
|
||||
return E_FAIL;
|
||||
|
||||
HRESULT hr = E_FAIL;
|
||||
|
||||
// Locate the entry point used for domain normalization
|
||||
PFN_IdnToAscii pfnIdnToAscii = (PFN_IdnToAscii)::GetProcAddress(hNormaliz, "IdnToAscii");
|
||||
if (!pfnIdnToAscii)
|
||||
goto cleanup;
|
||||
|
||||
// Normalize the domain name
|
||||
WCHAR wzEncoded[INTERNET_MAX_HOST_NAME_LENGTH + 1];
|
||||
int cchEncode = pfnIdnToAscii(IDN_USE_STD3_ASCII_RULES, wzDomain, ::lstrlenW(wzDomain), wzEncoded, cElements(wzEncoded));
|
||||
if (0 == cchEncode)
|
||||
{
|
||||
hr = HRESULT_FROM_WIN32(::GetLastError());
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Copy results to the input buffer
|
||||
if (cchEncode >= cchDomain)
|
||||
{
|
||||
hr = E_OUTOFMEMORY;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
::CopyMemory(wzDomain, wzEncoded, cchEncode * sizeof(WCHAR));
|
||||
hr = S_OK;
|
||||
|
||||
cleanup:
|
||||
if (hNormaliz)
|
||||
::CloseHandle(hNormaliz);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
// Extracts a normalized domain and scheme from an activation URL
|
||||
HRESULT GetDomainAndScheme(const OLECHAR * wzUrl, OLECHAR * wzScheme, DWORD cchScheme, OLECHAR * wzDomain, DWORD cchDomain)
|
||||
{
|
||||
// Data validation
|
||||
if (!wzDomain || !wzScheme)
|
||||
return E_POINTER;
|
||||
|
||||
// Extract the scheme
|
||||
HRESULT hr = ::UrlGetPartW(wzUrl, wzScheme, &cchScheme, URL_PART_SCHEME, 0);
|
||||
if (FAILED(hr))
|
||||
return E_FAIL;
|
||||
|
||||
// Extract the host name
|
||||
DWORD cchDomain2 = cchDomain;
|
||||
hr = ::UrlGetPartW(wzUrl, wzDomain, &cchDomain2, URL_PART_HOSTNAME, 0);
|
||||
if (FAILED(hr))
|
||||
*wzDomain = 0;
|
||||
|
||||
// Exclude any URL specifying a user name or password
|
||||
if ((0 == ::_wcsicmp(wzScheme, L"http")) || (0 == ::_wcsicmp(wzScheme, L"https")))
|
||||
{
|
||||
DWORD cch = 1;
|
||||
WCHAR wzTemp[1] = {0};
|
||||
::UrlGetPartW(wzUrl, wzTemp, &cch, URL_PART_USERNAME, 0);
|
||||
if (1 < cch)
|
||||
return E_FAIL;
|
||||
::UrlGetPartW(wzUrl, wzTemp, &cch, URL_PART_PASSWORD, 0);
|
||||
if (1 < cch)
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
// Normalize the domain name
|
||||
return NormalizeDomain(wzDomain, cchDomain);
|
||||
}
|
||||
|
||||
// Attempts to match an activation URL with a domain name
|
||||
bool MatchDomains(const OLECHAR * wzTrustedDomain, const OLECHAR * wzOurDomain)
|
||||
{
|
||||
// Data validation
|
||||
if (!wzTrustedDomain)
|
||||
return (0 == *wzOurDomain); // match only if empty
|
||||
|
||||
// Declarations
|
||||
int cchTrusted = ::lstrlenW(wzTrustedDomain);
|
||||
int cchOur = ::lstrlenW(wzOurDomain);
|
||||
bool fForcePrefix = false;
|
||||
bool fDenyPrefix = false;
|
||||
|
||||
// Check if all activation URLs should be matched
|
||||
if (0 == ::wcscmp(wzTrustedDomain, L"*"))
|
||||
return true;
|
||||
|
||||
// Check if the entry is like *. and setup the comparison range
|
||||
if ((2 < cchTrusted) && (L'*' == wzTrustedDomain[0]) && (L'.' == wzTrustedDomain[1]))
|
||||
{
|
||||
fForcePrefix = true;
|
||||
wzTrustedDomain += 2;
|
||||
cchTrusted -= 2;
|
||||
}
|
||||
|
||||
// Check if the entry is like = and setup the comparison range
|
||||
else if ((1 < cchTrusted) && (L'=' == wzTrustedDomain[0]))
|
||||
{
|
||||
fDenyPrefix = true;
|
||||
wzTrustedDomain++;
|
||||
cchTrusted--;
|
||||
};
|
||||
|
||||
// Check if there is a count mismatch
|
||||
if (cchTrusted > cchOur)
|
||||
return false;
|
||||
|
||||
// Compare URLs on the desired character range
|
||||
if (0 != ::memcmp(wzOurDomain + cchOur - cchTrusted, wzTrustedDomain, cchTrusted * sizeof(OLECHAR)))
|
||||
return false;
|
||||
|
||||
// Compare URLs without allowing child domains
|
||||
if (!fForcePrefix && (cchTrusted == cchOur))
|
||||
return true;
|
||||
|
||||
// Compare URLs requiring child domains
|
||||
if (!fDenyPrefix && (wzOurDomain[cchOur - cchTrusted - 1] == L'.'))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// IObjectSafetySiteLockImpl - "Safe for scripting" template
|
||||
template <typename T, DWORD dwSupportedSafety>
|
||||
class ATL_NO_VTABLE IObjectSafetySiteLockImpl : public IObjectSafetySiteLock, public CSiteLock<T>
|
||||
{
|
||||
public:
|
||||
// Constructor
|
||||
IObjectSafetySiteLockImpl(): m_dwCurrentSafety(0) {}
|
||||
|
||||
// Returns safety options
|
||||
STDMETHOD(GetInterfaceSafetyOptions)(REFIID riid, DWORD * pdwSupportedOptions, DWORD * pdwEnabledOptions)
|
||||
{
|
||||
// Data validation
|
||||
if (!pdwSupportedOptions || !pdwEnabledOptions)
|
||||
return E_POINTER;
|
||||
|
||||
// Declarations
|
||||
HRESULT hr = S_OK;
|
||||
IUnknown * pUnk = NULL;
|
||||
|
||||
// Get the current pointer as an instance of the template class
|
||||
T * pT = static_cast<T*>(this);
|
||||
|
||||
// Check if the requested COM interface is supported
|
||||
hr = pT->GetUnknown()->QueryInterface(riid, (void**)&pUnk);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
*pdwSupportedOptions = 0;
|
||||
*pdwEnabledOptions = 0;
|
||||
return hr;
|
||||
}
|
||||
|
||||
// Release the interface
|
||||
pUnk->Release();
|
||||
|
||||
// Check expiry and if the activation URL is allowed
|
||||
if (!ControlExpired() && InApprovedDomain())
|
||||
{
|
||||
*pdwSupportedOptions = dwSupportedSafety;
|
||||
*pdwEnabledOptions = m_dwCurrentSafety;
|
||||
}
|
||||
else
|
||||
{
|
||||
*pdwSupportedOptions = dwSupportedSafety;
|
||||
*pdwEnabledOptions = 0;
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Sets safety options
|
||||
STDMETHOD(SetInterfaceSafetyOptions)(REFIID riid, DWORD dwOptionSetMask, DWORD dwEnabledOptions)
|
||||
{
|
||||
// Declarations
|
||||
HRESULT hr = S_OK;
|
||||
IUnknown * pUnk = NULL;
|
||||
|
||||
// Get the current pointer as an instance of the template class
|
||||
T * pT = static_cast<T*>(this);
|
||||
|
||||
// Check if we support the interface and return E_NOINTERFACE if we don't
|
||||
// Check if the requested COM interface is supported
|
||||
hr = pT->GetUnknown()->QueryInterface(riid, (void**)&pUnk);
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
// Release the interface
|
||||
pUnk->Release();
|
||||
|
||||
// Reject unsupported requests
|
||||
if (dwOptionSetMask & ~dwSupportedSafety)
|
||||
return E_FAIL;
|
||||
|
||||
// Calculate safety options
|
||||
DWORD dwNewSafety = (m_dwCurrentSafety & ~dwOptionSetMask) | (dwOptionSetMask & dwEnabledOptions);
|
||||
if (m_dwCurrentSafety != dwNewSafety)
|
||||
{
|
||||
// Check expiry and if the activation URL is allowed
|
||||
if (ControlExpired() || !InApprovedDomain())
|
||||
return E_FAIL;
|
||||
|
||||
// Set safety options
|
||||
m_dwCurrentSafety = dwNewSafety;
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Returns capabilities (this can be used by testing tools to query for custom capabilities or version information)
|
||||
STDMETHOD(GetCapabilities)(DWORD * pdwCapability)
|
||||
{
|
||||
// Data validation
|
||||
if (!pdwCapability)
|
||||
return E_POINTER;
|
||||
|
||||
// Return the version if 0 is passed in
|
||||
if (0 == *pdwCapability)
|
||||
{
|
||||
*pdwCapability = SITELOCK_VERSION;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Return the options if 1 is passed in
|
||||
if (1 == *pdwCapability)
|
||||
{
|
||||
*pdwCapability =
|
||||
#ifdef SITELOCK_USE_IOLEOBJECT
|
||||
Capability::UsesIOleObject |
|
||||
#endif
|
||||
#ifndef SITELOCK_NO_EXPIRY
|
||||
Capability::HasExpiry |
|
||||
#endif
|
||||
0;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Return not implemented otherwise
|
||||
*pdwCapability = 0;
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
// Returns site lock entries controlling activation
|
||||
STDMETHOD(GetApprovedSites)(const SiteList ** pSiteList, DWORD * pcEntries)
|
||||
{
|
||||
// Data validation
|
||||
if (!pSiteList || !pcEntries)
|
||||
return E_POINTER;
|
||||
|
||||
// Return specified site lock entries
|
||||
#ifdef SITELOCK_USE_MAP
|
||||
// Use the site lock map
|
||||
*pSiteList = T::GetSiteLockMapAndCount(*pcEntries);
|
||||
#else
|
||||
// Use the static member
|
||||
*pSiteList = T::rgslTrustedSites;
|
||||
*pcEntries = cElements(T::rgslTrustedSites);
|
||||
#endif
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHOD(GetExpiryDate)(DWORD * pdwLifespan, FILETIME * pExpiryDate)
|
||||
{
|
||||
if (!pdwLifespan || !pExpiryDate)
|
||||
return E_POINTER;
|
||||
|
||||
#ifdef SITELOCK_NO_EXPIRY
|
||||
*pdwLifespan = 0;
|
||||
::ZeroMemory((void*)pExpiryDate, sizeof(FILETIME));
|
||||
return E_NOTIMPL;
|
||||
#else
|
||||
*pdwLifespan = T::dwControlLifespan;
|
||||
|
||||
// Calculate expiry date from life span
|
||||
time_t ttExpire = ImageNtHeaders(&__ImageBase)->FileHeader.TimeDateStamp;
|
||||
ttExpire += T::dwControlLifespan*86400; // seconds per day
|
||||
_UNIXTimeToFILETIME(ttExpire, pExpiryDate);
|
||||
|
||||
return S_OK;
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
// Current safety
|
||||
DWORD m_dwCurrentSafety;
|
||||
};
|
||||
Reference in New Issue
Block a user