This commit is contained in:
watrabi
2025-09-18 17:55:52 -04:00
commit 977f1ff4b8
15030 changed files with 17324420 additions and 0 deletions
+61
View File
@@ -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);
}
+16
View File
@@ -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);
};
+70
View File
@@ -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;
}
+5
View File
@@ -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
+73
View File
@@ -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;
};
}
+234
View File
@@ -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;
}
+29
View File
@@ -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);
};
+43
View File
@@ -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;
}
+55
View File
@@ -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();
}
};
+862
View File
@@ -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;
}
+159
View File
@@ -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();
};
+36
View File
@@ -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 &pi; }
~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;
}
};
+126
View File
@@ -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;
}
+18
View File
@@ -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);
};
+631
View File
@@ -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;
}
}
+69
View File
@@ -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);
}
+11
View File
@@ -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
+622
View File
@@ -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;
}
+73
View File
@@ -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);
};
+260
View File
@@ -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;
}
+84
View File
@@ -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__
+124
View File
@@ -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);
}
}
}
+81
View File
@@ -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(); }
};
}
+166
View File
@@ -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")));
}
+138
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+760
View File
@@ -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;
};