// 
// rline/rlpc.cpp 
// 
// Copyright (C) 1999-2010 Rockstar Games.  All Rights Reserved. 
// 

#include "rlpc.h"

#if RSG_PC

#include "data/base64.h"
#include "data/certificateverify.h"
#include "diag/seh.h"
#include "grcore/device.h"
#include "grcore/monitor.h"
#include "file/asset.h"
#include "file/device.h"
#include "grcore/device.h"
#include "input/pad.h"
#include "input/eventq.h"
#include "input/mouse.h"
#include "net/netfuncprofiler.h"
#include "rline/livestream/rllivestream.h"
#include "rline/rlpresence.h"
#include "rline/rltitleid.h"
#include "rline/ros/rlroscredtasks.h"
#include "rline/scpresence/rlscpresencemessage.h"
#include "rline/socialclub/rlsocialclub.h"
#include "rline/rlsystemui.h"
#include "ros/rlros.h"
#include "string/stringutil.h"
#include "wolfssl/ssl.h"

// debug DLL in debug, beta, bank release, release, __FINAL_LOGGING
// release DLL final and master
#define USE_DEBUG_DLL !__NO_OUTPUT
#define RLPC_DO_CERTIFICATE_VERIFICATION (!USE_DEBUG_DLL)

#if !__RGSC_DLL
#include "string/unicode.h"
#endif

#pragma warning(push)
#pragma warning(disable: 4668)
#include <ShlObj.h>
#include "system/d3d11.h"
#pragma warning(pop)

using namespace rgsc;

#if !__RGSC_DLL
#pragma comment(lib, "version.lib") 
#pragma comment(lib, "crypt32.lib")
#endif

#if __STEAM_BUILD
#pragma warning(disable: 4265)
#include "../../3rdParty/Steam/public/steam/steam_api.h"
#pragma warning(error: 4265)
#endif

typedef struct {
	LPWSTR lpszProgramName;
	LPWSTR lpszPublisherLink;
	LPWSTR lpszMoreInfoLink;
} SPROG_PUBLISHERINFO, *PSPROG_PUBLISHERINFO;

static const char * sm_TrustedIssuer = "Entrust Code Signing Certification Authority - L1D";
static const char * sm_TrustedIssuer2016 = "Entrust Code Signing CA - OVCS1";
static const char * sm_TrustedIssuer2020 = "DigiCert SHA2 Assured ID Code Signing CA";

static const char * sm_TrustedCommonName = "Take-Two Interactive Software, Inc.";
static const char * sm_TrustedCommonName2016 = "Rockstar Games, Inc.";

#if defined(RSG_LEAN_CLIENT) && __NO_OUTPUT
NOSTRIP_PC_XPARAM(nographics);
#else
XPARAM(nographics);
#endif

XPARAM(usegameservicesauth);

namespace rage
{

XPARAM(unattended);
XPARAM(rldisablessl);
XPARAM(rlenablesslbydefault);

#if __STEAM_BUILD
char rlPc::sm_SteamAuthTicket[rgsc::RGSC_STEAM_TICKET_ENCODED_BUF_SIZE];
u32 rlPc::sm_uSteamAuthTicketLen = 0;
#endif

static void SetTargetArea()
{
	if (PARAM_nographics.Get())
		return;

	const GridMonitor& pMon = GRCDEVICE.GetMonitorConfig().getCentralMonitor();

	unsigned left = pMon.uLeft;
	unsigned top = pMon.uTop;
	unsigned right = pMon.uRight;
	unsigned bottom = pMon.uBottom;

	g_rlPc.GetUiInterface()->SetRenderRect(top, left, bottom, right);
}

#if __STEAM_BUILD
void SteamStoreOpenCallback(u32 appId)
{
	if (SteamFriends())
	{
		SteamFriends()->ActivateGameOverlayToStore(appId, k_EOverlayToStoreFlag_None);
	}
}
#endif

#if defined(RSG_LEAN_CLIENT) || defined(MASTER_NO_SCUI)
#define LEAN_CLIENT_PARAM NOSTRIP_PC_PARAM
#else
#define LEAN_CLIENT_PARAM PARAM
#endif

LEAN_CLIENT_PARAM(noSocialClub, "Disables loading of the Social Club DLL.");
LEAN_CLIENT_PARAM(scDll, "Allows an override of the SC DLL flavor (debug, release, none)");
LEAN_CLIENT_PARAM(takehometest, "Switches on features for the take home test.");
LEAN_CLIENT_PARAM(scemail, "the email to use to sign-in to the social club. Currently only works when -scnoui is specified.");
LEAN_CLIENT_PARAM(scpassword, "the password to use to sign-in to the social club. Currently only works when -scnoui is specified.");
LEAN_CLIENT_PARAM(scnoui, "can be used with -scemail and -scpassword to sign into the social club without a UI");

NOSTRIP_PC_PARAM(scOfflineOnly, "Forces the Social Club UI to load the offline website only.");
NOSTRIP_PC_PARAM(scNoAutologin, "Disable Social Club autologin");
NOSTRIP_PC_PARAM(netUseStagingEnv, "Enables the Social Club staging environment");

PARAM(sclaunchermode, "Puts the Social Club DLL into launcher mode.");

#if !__FINAL
PARAM(noMarshalInput, "Disables marshalled RGSC SDK input");
#endif

#if !__NO_OUTPUT
XPARAM(processinstance);
#endif

RAGE_DEFINE_SUBCHANNEL(rline, pc);
#undef __rage_channel
#define __rage_channel rline_pc

AUTOID_IMPL(rlPcEvent);
AUTOID_IMPL(rlPcEventSignInStateChanged);
AUTOID_IMPL(rlPcEventRosTicketChanged);
AUTOID_IMPL(rlPcEventFriendStatusChanged);
AUTOID_IMPL(rlPcEventSocialClubMessage);
AUTOID_IMPL(rlPcEventGameInviteAccepted);
AUTOID_IMPL(rlPcEventJoinedViaPresence);
AUTOID_IMPL(rlPcEventCloudSavesEnabledUpdate);

rlPc g_rlPc;
static bool s_IsRendering = false;
static bool s_LostDevice = false;

extern const rlTitleId* g_rlTitleId;

class RgscDelegate : public IRgscDelegateLatestVersion
{
public:
	HRESULT RGSC_CALL QueryInterface(RGSC_REFIID riid, void** ppvObject);
	void RGSC_CALL Output(OutputSeverity severity, const char* msg);
	bool RGSC_CALL GetStatsData(char** data);
	void RGSC_CALL FreeStatsData(const char* data);
	void RGSC_CALL SetTextBoxHasFocus(const bool hasFocus);
	void RGSC_CALL SetTextBoxHasFocusV2(const bool hasFocus, const char* prompt, const char* text, const bool isPassword, const unsigned int maxNumChars);
	void RGSC_CALL UpdateSocialClubDll(const char* commandLine);
	void RGSC_CALL HandleNotification(const NotificationType id, const void* param);
};

static RgscDelegate m_Delegate;

HRESULT RgscDelegate::QueryInterface(RGSC_REFIID riid, LPVOID* ppvObj)
{
	IRgscUnknown *pUnknown = NULL;

	if(ppvObj == NULL)
	{
		return RGSC_INVALIDARG;
	}

	if(riid == IID_IRgscUnknown)
	{
		pUnknown = static_cast<IRgscDelegate*>(this);
	}
	else if(riid == IID_IRgscDelegateV1)
	{
		pUnknown = static_cast<IRgscDelegateV1*>(this);
	}
	else if(riid == IID_IRgscDelegateV2)
	{
		pUnknown = static_cast<IRgscDelegateV2*>(this);
	}
	else if(riid == IID_IRgscDelegateV3)
	{
		pUnknown = static_cast<IRgscDelegateV3*>(this);
	}

	*ppvObj = pUnknown;
	if(pUnknown == NULL)
	{
		return RGSC_NOINTERFACE;
	}

	return RGSC_OK;
}

void
RgscDelegate::Output(RgscDelegate::OutputSeverity OUTPUT_ONLY(severity), const char* OUTPUT_ONLY(msg))
{
#if !__NO_OUTPUT
	int msgLen = istrlen(msg);
	if (msgLen >= CHANNEL_MESSAGE_SIZE)
	{
		if(severity == RGSC_OUTPUT_SEVERITY_ASSERT)
		{
			// assert will be truncated, but must be included for uniqueness.
			// Full message will be printed below
			rlAssertf(false, "%s", msg); 
		}
		else if(severity == RGSC_OUTPUT_SEVERITY_ERROR)
		{
			rlError("");
		}
		else if(severity == RGSC_OUTPUT_SEVERITY_WARNING)
		{
			rlWarning("");
		}
		else if(severity == RGSC_OUTPUT_SEVERITY_INFO)
		{
			rlDebug3("");
		}

		diagLoggedPrintLn(msg, msgLen + 1);
	}
	else
	{
		if(severity == RGSC_OUTPUT_SEVERITY_ASSERT)
		{
#if RSG_ASSERT
			rlAssertf(false, "%s", msg);
#else
			rlError("Assert: %s", msg);
#endif
		}
		else if(severity == RGSC_OUTPUT_SEVERITY_ERROR)
		{
			rlError("%s", msg);
		}
		else if(severity == RGSC_OUTPUT_SEVERITY_WARNING)
		{
			rlWarning("%s", msg);
		}
		else if(severity == RGSC_OUTPUT_SEVERITY_INFO)
		{
			rlDebug3("%s", msg);
		}
	}
#endif
}

bool 
RgscDelegate::GetStatsData(char** UNUSED_PARAM(data))
{
	// deprecated
	return false;
}

void 
RgscDelegate::FreeStatsData(const char* UNUSED_PARAM(data))
{
	// deprecated
}

void 
RgscDelegate::SetTextBoxHasFocus(const bool /*hasFocus*/)
{
// 	// show or hide the OnLive virtual keyboard
// 	if(hasFocus)
// 	{
// 		printf("Show OnLive Virtual Keyboard");
// 	}
// 	else
// 	{
// 		printf("Hide OnLive Virtual Keyboard");
// 	}
// 
// 	fflush(stdout);
}

void 
RgscDelegate::SetTextBoxHasFocusV2(const bool hasFocus, const char* prompt, const char* text, 
								   const bool isPassword, const unsigned int maxNumChars)
{
	if (hasFocus)
	{
		g_rlPc.SetTextBoxHasFocus(hasFocus, prompt, text, isPassword, maxNumChars);
	}
}

void 
	RgscDelegate::UpdateSocialClubDll(const char* UNUSED_PARAM(commandLine))
{
	// deprecated
}

void 
RgscDelegate::HandleNotification(const NotificationType id, const void* param)
{
	switch(id)
	{
		case rgsc::NOTIFY_SIGN_IN_STATE_CHANGED:
		{
			rgsc::SignInStateFlags state = *(rgsc::SignInStateFlags*)param;
			rlPcEventSignInStateChanged e(state);
			g_rlPc.DispatchEvent(&e);
		}
		break;

		case rgsc::NOTIFY_ROS_TICKET_CHANGED:
			{
				const char* xml = (const char*)param;
				rlPcEventRosTicketChanged e(xml);
				g_rlPc.DispatchEvent(&e);
			}
			break;

		case rgsc::NOTIFY_FRIEND_STATUS_CHANGED:
		{
			rlPcEventFriendStatusChanged e;
			g_rlPc.DispatchEvent(&e);
		}
		break;

		case rgsc::NOTIFY_SOCIAL_CLUB_EVENT:
		{
			IPresenceMessage* msgInterface = (IPresenceMessage*)param;
			PresenceMessageV1* v1 = NULL;
			HRESULT hr = msgInterface->QueryInterface(IID_IPresenceMessageV1, (void**) &v1);
			if(rlVerify(SUCCEEDED(hr) && (v1 != NULL)))
			{
				rlScPresenceMessageSender sender;
				g_rlPc.GetPresenceMessageSender(msgInterface, &sender);
				rlScPresenceMessage scMsg(v1->GetContents(), v1->GetTimestamp());
				rlPcEventSocialClubMessage e(&scMsg, &sender);
				g_rlPc.DispatchEvent(&e);
			}
		}
		break;

		case rgsc::NOTIFY_GAME_INVITE_ACCEPTED:
		{
			IPresenceMessage* msgInterface = (IPresenceMessage*)param;
			PresenceMessageV1* v1 = NULL;
			HRESULT hr = msgInterface->QueryInterface(IID_IPresenceMessageV1, (void**) &v1);
			if(rlVerify(SUCCEEDED(hr) && (v1 != NULL)))
			{
				rlScPresenceMessageSender sender;
				g_rlPc.GetPresenceMessageSender(msgInterface, &sender);
				rlScPresenceMessage scMsg(v1->GetContents(), v1->GetTimestamp());
				rlPcEventGameInviteAccepted e(&scMsg, &sender);
				g_rlPc.DispatchEvent(&e);
			}
		}
		break;

		case rgsc::NOTIFY_JOINED_VIA_PRESENCE:
		{
			IPresenceMessage* msgInterface = (IPresenceMessage*)param;
			PresenceMessageV1* v1 = NULL;
			HRESULT hr = msgInterface->QueryInterface(IID_IPresenceMessageV1, (void**) &v1);
			if(rlVerify(SUCCEEDED(hr) && (v1 != NULL)))
			{
				rlScPresenceMessageSender sender;
				g_rlPc.GetPresenceMessageSender(msgInterface, &sender);
				rlScPresenceMessage scMsg(v1->GetContents(), v1->GetTimestamp());
				rlPcEventJoinedViaPresence e(&scMsg, &sender);
				g_rlPc.DispatchEvent(&e);
			}
		}
		break;

		case rgsc::NOTIFY_FATAL_ERROR:
		{
			int errCode = *(int*)param;
			g_rlPc.SetInitErrorCode(errCode);
		}
		break;

		case rgsc::NOTIFY_UI_EVENT:
		{
			int eventId = 0;
			const char * jsonEvent = (const char*)param;
			RsonReader rr(jsonEvent, (unsigned)strlen(jsonEvent));
			rr.ReadInt("EventId", eventId);

			if(eventId == 1)
			{
				RsonReader rrData;
				if(rr.GetMember("Data", &rrData))
				{
					RsonReader rrAction;

					char action[64] = {0};
					if(rrData.ReadString("Action", action))
					{
						if(stricmp(action, "Close") == 0)
						{
							rlDisplay("Shutdown requested via the scui");
							g_rlPc.SetUserRequestedShutdownViaScui(true);
						}
					}
				}
			}
		}
		break;

		case rgsc::NOTIFY_EXTERNAL_BROWSER_URL:
		{
			g_SystemUi.ShowWebBrowser(0, (char*)param);
		}
		break;
		case rgsc::NOTIFY_REFRESH_STEAM_AUTH_TICKET:
		{
#if __STEAM_BUILD
			bool bInformScui = param ? *(bool*)param : true;
			g_rlPc.RefreshSteamAuthTicket(bInformScui);
#endif
		}
		break;
		case rgsc::NOTIFY_ENTITLEMENT_EVENT:
		{
			const char * jsonEvent = (const char*)param;
			g_rlPc.HandleEntitlementEvent(jsonEvent);		
		}
		break;
		case rgsc::NOTIFY_CLOUD_SAVE_ENABLED_UPDATED:
		{

			rlPcEventCloudSavesEnabledUpdate e((const char*)param);
			g_rlPc.DispatchEvent(&e);
		}
		break;
		case rgsc::NOTIFY_BROWSER_CREATED:
		{
			// unused
		}
		break;
	}
}

rlPc::rlPc()
: m_Rgsc(NULL)
, m_ProfileManager(NULL)
, m_GamepadManager(NULL)
, m_AchievementManager(NULL)
, m_PlayerManager(NULL)
, m_Telemetry(NULL)
, m_PresenceManager(NULL)
, m_CommerceManager(NULL)
, m_ActivationSystem(NULL)
, m_PatchingSystem(NULL)
, m_FileSystem(NULL)
, m_RgscUi(NULL)
, m_TaskManager(NULL)
, m_NetworkInterface(NULL)
, m_CloudSaveManager(NULL)
, m_GamerPicManager(NULL)
, m_hRgscDll(NULL)
, m_PCPipe(NULL)
, m_InitializationErrorCode(SC_INIT_ERR_NONE)
, m_WaitingForKeyboardResult(false)
, m_KeyboardCompleteTime(0)
, m_bHasScuiRequestedQuit(false)
, m_NumScuiPads(0)
{
	memset(m_SocialClubVersionNum, 0, sizeof(m_SocialClubVersionNum));
	memset(m_SocialClubVersion, 0, sizeof(m_SocialClubVersion));
	memset(m_AdditionalSessionAttr, 0, sizeof(m_AdditionalSessionAttr));
	memset(m_AdditionalJoinAttr, 0, sizeof(m_AdditionalJoinAttr));
	memset(m_MetaDataPath, 0, sizeof(m_MetaDataPath));
	memset(m_ScuiPads, 0, sizeof(m_ScuiPads));
}

rlPc::~rlPc()
{

}

void RgscPresentCallback(grcDeviceHandle* /*device*/,
						 const tagRECT* /*pSourceRect*/,
						 const tagRECT* /*pDestRect*/,
						 HWND__* /*hwndOverride*/,
						 const tagRGNDATA* /*dirtyRegion*/)
{
	s_IsRendering = true;

	if((s_LostDevice == false) && g_rlPc.GetUiInterface())
	{
		g_rlPc.GetUiInterface()->Render();
	}

	s_IsRendering = false;
}

void PostPresentCallback()
{
#if LIVESTREAM_ENABLED
	rlLiveStream::PostPresentCallback();
#endif
}

void RgscDeviceLostCB()
{
#if !__RGSC_DLL
	s_LostDevice = true;
	if(g_rlPc.GetUiInterface())
	{
		g_rlPc.GetUiInterface()->OnLostDevice();
	}
#endif
}

void RgscDeviceResetCB()
{
	// the game calls DeviceReset without calling DeviceLost sometimes, so protect ourselves here.
	if(s_LostDevice == false)
	{
		RgscDeviceLostCB();
	}

#if !__RGSC_DLL
	if(g_rlPc.GetUiInterface())
	{
		SetTargetArea();

#if __D3D11
		{
			IDXGISwapChain* pSwapChain = (IDXGISwapChain*)GRCDEVICE.GetSwapChain();
			DXGI_SWAP_CHAIN_DESC desc;
			pSwapChain->GetDesc(&desc);
			g_rlPc.GetUiInterface()->OnResetDevice(&desc);
		}
#else
		{
			grcPresentParameters *pPresentParams = GRCDEVICE.GetPresentParameters();
			g_rlPc.GetUiInterface()->OnResetDevice(pPresentParams);
		}
#endif
	}

	s_LostDevice = false;
#endif
}

#if !__FINAL
void rlPc::SimluateDeviceLost()
{
	RgscDeviceLostCB();
}

void rlPc::SimluateDeviceReset()
{
	RgscDeviceResetCB();
}
#endif

void rlPc::GetPresenceMessageSender(IPresenceMessage* msg, rlScPresenceMessageSender* sender)
{
	if(msg == nullptr || sender == nullptr)
	{
		return;
	}

	// PresenceMessageV3 implements interface IPresenceMessageV2
	PresenceMessageV3* v3 = NULL;
	HRESULT hr = msg->QueryInterface(IID_IPresenceMessageV2, (void**) &v3);
	if(SUCCEEDED(hr) && (v3 != NULL))
	{
		rlGamerHandle senderGh;
		if(!StringNullOrEmpty(v3->GetSenderGamerHandle()))
		{
			if(!senderGh.FromString(v3->GetSenderGamerHandle()))
			{
				senderGh.Clear();
			}
		}

		*sender = rlScPresenceMessageSender(senderGh, (rlScPresenceMessageSender::Source)v3->GetSource());
	}
}

#if !__FINAL && !__RGSC_DLL
u32 
rlPc::GetDllVersion(const char* fileName)
{
	DWORD handle = 0;
	DWORD size = GetFileVersionInfoSize(fileName, &handle);
	BYTE* versionInfo = rage_new BYTE[size];
	if(!GetFileVersionInfo(fileName, handle, size, versionInfo))
	{
		Warningf("Could not get version number for %s", fileName);
		delete[] versionInfo;
		return 0;
	}

	// we have version information
	UINT len = 0;
	VS_FIXEDFILEINFO* vsfi = NULL;
	VerQueryValue(versionInfo, "\\", (void**)&vsfi, &len);

	int aVersion[4] = {0};
	aVersion[0] = HIWORD(vsfi->dwFileVersionMS);
	aVersion[1] = LOWORD(vsfi->dwFileVersionMS);
	aVersion[2] = HIWORD(vsfi->dwFileVersionLS);
	aVersion[3] = LOWORD(vsfi->dwFileVersionLS);

	delete[] versionInfo;

	// TODO: NS - this only works if we have something like a.b.c.d where a, b, c, and d are 1 digit.
	// Not the best way to do this since we could have version 1.0.1.2813, etc.
	rlAssert(aVersion[0] >= 0 && aVersion[0] <= 9);
	rlAssert(aVersion[1] >= 0 && aVersion[1] <= 9);
	rlAssert(aVersion[2] >= 0 && aVersion[2] <= 9);
	rlAssert(aVersion[3] >= 0 && aVersion[3] <= 9);
	
	Displayf("%s: v%d.%d.%d.%d", fileName, aVersion[0], aVersion[1], aVersion[2], aVersion[3]);

	return aVersion[0] * 1000 + aVersion[1] * 100 + aVersion[2] * 10 + aVersion[3];
}

u32
rlPc::GetDllFileCount(const char* path)
{
	u32 fileCount = 0;
	const u32 maxcount = 255;
	const fiDevice *device = fiDevice::GetDevice(path);

	fiFindData data;
	fiHandle h = device->FindFileBegin(path, data);

	if (fiIsValidHandle(h)) 
	{
		do
		{
			// CEF generates 'debug.log' at runtime in the DLL directory. Exclude this.
			if (stricmp(data.m_Name, "debug.log") != 0)
			{
				fileCount++;
			}

		} while (fileCount < maxcount && device->FindFileNext(h,data));
		device->FindFileEnd(h);
	}

	return fileCount;
}

bool 
rlPc::InstallDlls(char (&loadPath)[MAX_PATH])
{
	/*
		Cases:
		Local Exists	Dest Exists		Local is higher version		Copy?
		0				0				-							0
		0				1				-							0
		1				0				-							1
		1				1				0							0
		1				1				1							1
	*/

	bool success = true;

	rtry
	{
		char localPath[RGSC_MAX_PATH] = {0};
		char localDllPath[RGSC_MAX_PATH] = {0};

		const char* plaformRoot = "update:/x64/";
		const char* plaformPath = "update:/x64/Social Club SDK/Rockstar Games/";
		const fiDevice *device = fiDevice::GetDevice(plaformPath, true);
		if(device)
		{
			device->FixRelativeName(localPath, sizeof(localPath), plaformPath);
		}

		if(strncmp(localPath, plaformRoot, sizeof(plaformRoot) - 1) == 0)
		{
			// failed to resolve platform root
			Warningf("Can't find Social Club SDK to do a version check.");
			return false;
		}
		
		safecpy(localDllPath, localPath);

#if USE_DEBUG_DLL
		strcat_s(localDllPath, "x64/Social Club Debug/");
#else
		strcat_s(localDllPath, "x64/Social Club Debug/");
#endif

		u32 localDllFileCount = GetDllFileCount(localDllPath);

		strcat_s(localDllPath, "socialclub.dll");

		u32 localDllVersion = GetDllVersion(localDllPath);

		char destPath[MAX_PATH] = {0};
		char destDllPath[MAX_PATH] = {0};
		rcheck(SHGetSpecialFolderPath(NULL, destPath, CSIDL_PROGRAM_FILES, false), catchall, );
		strcat_s(destPath, "\\Rockstar Games");
		safecpy(destDllPath, destPath);

#if USE_DEBUG_DLL
		strcat_s(destDllPath, "\\Social Club Debug\\");
#else
		strcat_s(destDllPath, "\\Social Club Debug\\");
#endif

		u32 destDllFileCount = GetDllFileCount(destDllPath);

		strcat_s(destDllPath, "socialclub.dll");

		u32 destDllVersion = GetDllVersion(destDllPath);
		if(destDllVersion == 0)
		{
			Warningf("Could not get Social Club version. Social Club may not be installed or you may not have admin rights. Will attempt to install...");
		}

		bool updateSdk = false;
		if (localDllFileCount > destDllFileCount && localDllVersion == destDllVersion)
		{
			Displayf("SDK file count mismatch (%u/%u) with matching version, will attempt to update.", localDllFileCount, destDllFileCount);
			updateSdk = true;
			success = false;
		}
		else if(localDllVersion > destDllVersion)
		{
			Displayf("Local SDK version greater than destination, will attempt to update.", localDllVersion, destDllVersion);
			updateSdk = true;
			success = false;
		}

		if (updateSdk)
		{
			Displayf("Attempting to update Social Club SDK...");

			char pathToDelete[MAX_PATH] = {0};
			safecpy(pathToDelete, destPath);
			strcat_s(pathToDelete, "\\Social Club");
			fiDevice::DeleteDirectory(pathToDelete, true);

			safecpy(pathToDelete, destPath);
			strcat_s(pathToDelete, "\\Social Club Debug");
			fiDevice::DeleteDirectory(pathToDelete, true);

			safecpy(pathToDelete, destPath);
			strcat_s(pathToDelete, "\\Social Club SDK");
			fiDevice::DeleteDirectory(pathToDelete, true);

			char pathToCopy[MAX_PATH] = {0};
			char pathToCopyTo[MAX_PATH] = {0};
			safecpy(pathToCopy, localPath);
			safecpy(pathToCopyTo, destPath);
			strcat_s(pathToCopyTo, "\\Social Club");

#if RSG_CPU_X64
			strcat_s(pathToCopy, "x64\\Social Club");
#else
			strcat_s(pathToCopy, "x86\\Social Club");
#endif
			ASSET.CreateLeadingPath(pathToCopyTo);
			success = fiDevice::CopyDirectory(pathToCopy, pathToCopyTo);

			safecpy(pathToCopy, localPath);
			safecpy(pathToCopyTo, destPath);
			strcat_s(pathToCopyTo, "\\Social Club Debug");

#if RSG_CPU_X64
			strcat_s(pathToCopy, "x64\\Social Club Debug");
#else
			strcat_s(pathToCopy, "x86\\Social Club Debug");
#endif
			ASSET.CreateLeadingPath(pathToCopyTo);
			success = success && fiDevice::CopyDirectory(pathToCopy, pathToCopyTo);

// 			safecpy(pathToCopy, localPath);
// 			safecpy(pathToCopyTo, destPath);
// 			strcat_s(pathToCopyTo, "\\Social Club SDK");
// 
// 			strcat_s(pathToCopy, "\\Social Club SDK");
// 			ASSET.CreateLeadingPath(pathToCopyTo);
// 			success = success && fiDevice::CopyDirectory(pathToCopy, pathToCopyTo);

			if(success == false)
			{
				Errorf("Failed to copy Social Club SDK. You may not have admin rights or a file is in use. Will load the newer Social Club from '%s'.", localPath);

				// Upper case the drive letter.
				localDllPath[0] = (char)toupper(localDllPath[0]);

				// Copy to loadPath
				safecpy(loadPath, localDllPath); 
			}
		}
	}
	rcatchall
	{

	}

	return success;
}
#endif

void 
rlPc::SetSocialClubVersion(const wchar_t* fileName)
{
	DWORD handle = 0;
	DWORD size = GetFileVersionInfoSizeW(fileName, &handle);
	BYTE* versionInfo = rage_new BYTE[size];
	if(!GetFileVersionInfoW(fileName, handle, size, versionInfo))
	{
		delete[] versionInfo;
		return;
	}

	// we have version information
	UINT len = 0;
	VS_FIXEDFILEINFO* vsfi = NULL;
	VerQueryValue(versionInfo, "\\", (void**)&vsfi, &len);

	memset(m_SocialClubVersionNum, 0, sizeof(m_SocialClubVersionNum));
	m_SocialClubVersionNum[0] = HIWORD(vsfi->dwFileVersionMS);
	m_SocialClubVersionNum[1] = LOWORD(vsfi->dwFileVersionMS);
	m_SocialClubVersionNum[2] = HIWORD(vsfi->dwFileVersionLS);
	m_SocialClubVersionNum[3] = LOWORD(vsfi->dwFileVersionLS);

	delete[] versionInfo;

	formatf(m_SocialClubVersion, "%d.%d.%d.%d", m_SocialClubVersionNum[0], m_SocialClubVersionNum[1], m_SocialClubVersionNum[2], m_SocialClubVersionNum[3]);
}

const char* 
rlPc::GetSocialClubVersion()
{
	return m_SocialClubVersion;
}

bool
rlPc::IsSocialClubVersion(int major, int minor, int build, int revision)
{
	return major == m_SocialClubVersionNum[0] && minor == m_SocialClubVersionNum[1] && build == m_SocialClubVersionNum[2] && revision == m_SocialClubVersionNum[3];
}

bool 
rlPc::IsAtLeastSocialClubVersion(int major, int minor, int build, int revision)
{
	// If the specified major number is more recent, immediately fail. If it is less recent, immediately succeed.
	if (m_SocialClubVersionNum[0] < major)
		return false;
	else if (m_SocialClubVersionNum[0] > major)
		return true;

	// If the specified minor number is more recent, immediately fail. If it is less recent, immediately succeed.
	if (m_SocialClubVersionNum[1] < minor)
		return false;
	else if (m_SocialClubVersionNum[1] > minor)
		return true;

	// If the specified build number is more recent, immediately fail. If it is less recent, immediately succeed.
	if (m_SocialClubVersionNum[2] < build)
		return false;
	else if (m_SocialClubVersionNum[2] > build)
		return true;

	// If the specified revision number is more recent, immediately fail. If it is less recent, immediately succeed.
	if (m_SocialClubVersionNum[3] < revision)
		return false;
	else if (m_SocialClubVersionNum[3] > revision)
		return true;
	
	// At this point, we know all 4 numbers are equal, so return true;
	return true;
}

void
rlPc::SetTextBoxHasFocus(const bool hasFocus, const char* STEAMBUILD_ONLY(prompt), const char* text, const bool isPassword, const unsigned int maxNumChars)
{
	rlDebug3("Trace: SetTextBoxHasFocus");

#if __STEAM_BUILD
	rtry
	{
		if (hasFocus && 
			g_rlPc.GetUiInterface()->GetInputMethod() == RGSC_IM_CONTROLLER &&
			!m_ScuiVirtualKeyboard.IsPending())
		{
			USES_CONVERSION;

			ioVirtualKeyboard::Params params;
			params.m_InitialValue = UTF8_TO_WIDE(text);
			params.m_KeyboardType = isPassword ? ioVirtualKeyboard::kTextType_PASSWORD : ioVirtualKeyboard::kTextType_DEFAULT;
			params.m_Description = UTF8_TO_WIDE(prompt);
			params.m_Title = UTF8_TO_WIDE(prompt);
			if (maxNumChars < 0 || maxNumChars > ioVirtualKeyboard::MAX_STRING_LENGTH_FOR_VIRTUAL_KEYBOARD)
			{
				params.m_MaxLength = ioVirtualKeyboard::MAX_STRING_LENGTH_FOR_VIRTUAL_KEYBOARD;
			}
			else
			{
				params.m_MaxLength = maxNumChars;
			}
			params.m_PlayerIndex = 0;

			bool bRet = m_ScuiVirtualKeyboard.Show(params);
			rlDebug3("SteamUtils()->ioVirtualKeyboard::Show : %s", bRet ? "true" : "False");
			rcheck(bRet, catchall, );

			m_KeyboardCompleteTime = 0;
			g_rlPc.GetUiInterface()->EnableClosingUi(false);
			m_WaitingForKeyboardResult = true;
		}
		else if (!hasFocus)
		{
			m_WaitingForKeyboardResult = false;
		}
	}
	rcatchall
#else
	if (hasFocus && g_rlPc.GetUiInterface()->GetInputMethod() == RGSC_IM_CONTROLLER)
#endif
	{
		g_rlPc.GetUiInterface()->ShowVirtualKeyboard(text, isPassword, maxNumChars);
	}
}

void rlPc::RefreshSteamAuthTicket(bool STEAMBUILD_ONLY(bNotifyRgsc))
{
#if __STEAM_BUILD
	if (!SteamUser())
		return;

	// refresh the auth session ticket
	u8 authTicketU8[rgsc::RGSC_STEAM_TICKET_BUF_SIZE] = {0};
	SteamUser()->GetAuthSessionTicket(authTicketU8, sizeof(authTicketU8), &sm_uSteamAuthTicketLen);

	// clear the previous ticket
	sysMemSet(sm_SteamAuthTicket, 0, sizeof(sm_SteamAuthTicket));
	
	// hex-encode the steam auth ticket
	for(u32 i = 0; i < sm_uSteamAuthTicketLen; i++)
	{
		safecatf(sm_SteamAuthTicket, "%02X", authTicketU8[i]);
	}

	// verify that the length of the encoded ticket
	rlAssert(strlen(sm_SteamAuthTicket) == (sm_uSteamAuthTicketLen * 2));

	// debug log the refreshed ticket
	rlDebug("Steam ticket: %s", sm_SteamAuthTicket);
	
	if (bNotifyRgsc && m_Rgsc != NULL)
	{
		m_Rgsc->SetSteamAuthTicket(sm_SteamAuthTicket);
	}
#endif
}

u32 rlPc::GetSteamAuthTicketLength()
{
#if __STEAM_BUILD
	return sm_uSteamAuthTicketLen;
#else
	return 0;
#endif
}

const char* rlPc::GetSteamAuthTicket()
{
#if __STEAM_BUILD
	return sm_SteamAuthTicket;
#else
	return NULL;
#endif
}

void rlPc::HandleEntitlementEvent(const char* jsonEvent)
{
	RsonReader rr(jsonEvent, (unsigned)strlen(jsonEvent));
	char action[64] = {0};
	if(rlVerify(rr.ReadString("Action", action)))
	{
		atString productId;
		if (rlVerify(rr.ReadString("ProductId", productId)))
		{
			if (stricmp(action, "Download") == 0)
			{
				if (g_rlPc.GetDownloaderPipe() && g_rlPc.GetDownloaderPipe()->IsConnected())
				{
					g_rlPc.GetDownloaderPipe()->StartDownloadWithToken(productId.c_str(), "");
				}
			}
		}
	}	
}

unsigned rlPc::GetNumScuiPads() const
{
	return m_NumScuiPads;
}

rgsc::RgscGamepad* rlPc::GetScuiPad(const unsigned index) const
{
	if (!rlVerify(index < m_NumScuiPads && index < COUNTOF(m_ScuiPads)))
		return nullptr;

	return m_ScuiPads[index];
}

bool RockstarCertificateVerification(CertificateDetails & detail)
{
	/* 
		<loadingCertificate>
			I really wanted to parse these fields out of the certificate. I couldn't figure out
			how to do it with CyaSSL. The methods I used on the launcher (utilising OpenSSL)
			just don't exist within CyaSSL. I've spent 6-8 hours trying to figure out the right 
			methods to call within CyaSSL, but I haven't had any luck. I can continue to grind
			but at some point I need to draw the line and move on with lockdown coming up.

			In the mean time, I'm settling for damaging repairing the constant strings
			provided in the static const buffers above.

		</loadingCertificate>
	 */
	// Declare our local variables
	HCRYPTPROV hProv;
	HCERTSTORE hStore = NULL;
	HCRYPTMSG hMsg = NULL; 
	PCCERT_CONTEXT pCertContext = NULL;
	BOOL fResult;   
	DWORD dwEncoding, dwContentType, dwFormatType;
	PCMSG_SIGNER_INFO pSignerInfo = NULL;
	DWORD dwSignerInfo;
	CERT_INFO CertInfo;     
	PSPROG_PUBLISHERINFO ProgPubInfo;
	
	// Unfortunately these only work if they're in UNICODE.
	// Spent two hours debugging the m_fileOfCertificate not working
	// because it wasn't in unicode format (for the call to 
	// CryptQueryObject)
	WCHAR szFileName[MAX_PATH]; 
	//@@: location ROCKSTARCERTIFICATEVERIFICATION_CONVERT_TO_UNICODE
	safecpy(szFileName, detail.m_fileOfCertificate, MAX_PATH);
	
	// Start by creating a crypto provider.
	//@@: range ROCKSTARCERTIFICATEVERIFICATION_DOWORK {
	if(!CryptAcquireContext(
		&hProv, 
		NULL, 
		NULL, 
		PROV_RSA_FULL, 
		0)) 
	{
		rlDebug("Failed to create a crypto provider.");
	}
	// Zero out the memory we intend on using
	ZeroMemory(&ProgPubInfo, sizeof(ProgPubInfo));
	// Next lets query the object
	//@@: location ROCKSTARCERTIFICATEVERIFICATION_QUERY_OBJECT
	fResult = CryptQueryObject(CERT_QUERY_OBJECT_FILE,
		szFileName,
		CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
		CERT_QUERY_FORMAT_FLAG_BINARY,
		0,
		&dwEncoding,
		&dwContentType,
		&dwFormatType,
		&hStore,
		&hMsg,
		NULL);
	if (!fResult)
	{
		rlDebug("CryptQueryObject failed with %x\n", GetLastError());
		return false;
	}
	// Fetch signer information size.
	fResult = CryptMsgGetParam(hMsg, 
		CMSG_SIGNER_INFO_PARAM, 
		0, 
		NULL, 
		&dwSignerInfo);
	if (!fResult)
	{
		rlDebug("CryptMsgGetParam failed with %x\n", GetLastError());
	}

	// Allocate memory for signer information.
	//@@: location ROCKSTARCERTIFICATEVERIFICATION_LOCAL_ALLOC_SIGNER
	pSignerInfo = (PCMSG_SIGNER_INFO)LocalAlloc(LPTR, dwSignerInfo);
	if (!pSignerInfo)
	{
		rlDebug("Unable to allocate memory for Signer Info.\n");
		return false;
	}

	// Fetch Signer Information.
	fResult = CryptMsgGetParam(hMsg, 
		CMSG_SIGNER_INFO_PARAM, 
		0, 
		(PVOID)pSignerInfo, 
		&dwSignerInfo);
	if (!fResult)
	{
		rlDebug("CryptMsgGetParam failed with %x\n", GetLastError());
		return false;
	}

	CertInfo.Issuer = pSignerInfo->Issuer;
	CertInfo.SerialNumber = pSignerInfo->SerialNumber;

	pCertContext = CertFindCertificateInStore(hStore,
		(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING),
		0,
		CERT_FIND_SUBJECT_CERT,
		(PVOID)&CertInfo,
		NULL);
	if (!pCertContext)
	{
		rlDebug("CertFindCertificateInStore failed with %x\n", GetLastError());
		return false;
	}

	DWORD dwData;

	// Get Issuer name size.
	dwData = CertGetNameString(pCertContext, 
		CERT_NAME_SIMPLE_DISPLAY_TYPE,
		CERT_NAME_ISSUER_FLAG,
		NULL,
		NULL,
		0);
	if(!dwData)
	{
		rlDebug("CertGetNameString failed.\n");
	}

	// Allocate memory for Issuer name.
	char *szName = rage_new char[dwData * sizeof(TCHAR)];
	if (!szName)
	{
		rlDebug("Unable to allocate memory for issuer name.\n");
		return false;
	}

	// Get Issuer name.
	if (!(CertGetNameString(pCertContext, 
		CERT_NAME_SIMPLE_DISPLAY_TYPE,
		CERT_NAME_ISSUER_FLAG,
		NULL,
		szName,
		dwData)))
	{
		rlDebug("CertGetNameString failed attempting to get size of issuer name.\n");
		return false;
	}

	
	if(strcmp(sm_TrustedIssuer,szName)!=0 && strcmp(sm_TrustedIssuer2016, szName)!=0 && strcmp(sm_TrustedIssuer2020, szName)!=0)
	{
		rlDebug("Comparison of Issuer Failed. This is catastrophically bad [%s] - Could be [%s] or [%s] or [%s].\n",szName,sm_TrustedIssuer,sm_TrustedIssuer2016,sm_TrustedIssuer2020);
		delete szName;
		return false;
	}


	delete szName;

	// Get subject name size.
	dwData = CertGetNameString(pCertContext, 
		CERT_NAME_SIMPLE_DISPLAY_TYPE,
		NULL,
		NULL,
		NULL,
		0);
	if(!dwData)
	{
		rlDebug("CertGetNameString failed attempting to get size of subject name.\n");
		return false;
	}

	// Allocate memory for subject name.
	szName = rage_new char[dwData * sizeof(TCHAR)];

	if (!szName)
	{
		rlDebug("Unable to allocate memory for subject name.\n");
		return false;
	}

	// Get subject name.
	//@@: location ROCKSTARCERTIFICATEVERIFICATION_GET_SUBJECT_NAME
	if (!(CertGetNameString(pCertContext, 
		CERT_NAME_SIMPLE_DISPLAY_TYPE,
		NULL,
		NULL,
		szName,
		dwData)))
	{
		rlDebug("CertGetNameString failed.\n");
		return false;
	}


	// Now lets check our subject name
	if(strcmp(sm_TrustedCommonName ,szName)!=0 && strcmp(sm_TrustedCommonName2016, szName) !=0)
	{
		rlDebug("Comparison of Subject Failed. This is catastrophically bad [%s] - Could be [%s] or [%s].\n",szName,sm_TrustedCommonName, sm_TrustedCommonName2016);
		delete szName;
		return false;
	}

	delete szName;
	return true;
	//@@: } ROCKSTARCERTIFICATEVERIFICATION_DOWORK

}

bool 
rlPc::LoadDll()
{
	bool success = false;

#if !__RGSC_DLL
	rtry
	{
		//@@: range RLPC_LOADDLL {
		// use Unicode since users can map their Program Files directories to a path with Unicode characters
		wchar_t socialClubDllPath[MAX_PATH];
		socialClubDllPath[0] = L'\0';

#if !__FINAL
		// -takehometest allows the SC to load from the same directory as the exe
		if(PARAM_takehometest.Get())
		{
			GetCurrentDirectoryW(COUNTOF(socialClubDllPath), socialClubDllPath);
		}
		else
#endif
		{
			rverify(SHGetFolderPathW(NULL, CSIDL_PROGRAM_FILES, NULL, SHGFP_TYPE_CURRENT, socialClubDllPath) == S_OK, catchall, SetInitErrorCode(SC_INIT_ERR_PROGRAM_FILES_NOT_FOUND));
		}

#if USE_DEBUG_DLL
		safecat(socialClubDllPath, L"\\Rockstar Games\\Social Club Debug\\socialclub.dll");
#else
		safecat(socialClubDllPath, L"\\Rockstar Games\\Social Club Debug\\socialclub.dll");
#endif

#if !__FINAL
		if(!PARAM_takehometest.Get())
		{
			char path[MAX_PATH] = {0};
			WideCharToMultiByte(CP_UTF8, 0, socialClubDllPath, -1, path, COUNTOF(path), 0, 0);
			InstallDlls(path);

			// InstallDlls can modify the path, convert back to wide
			MultiByteToWideChar(CP_UTF8 , NULL , path, -1, socialClubDllPath, COUNTOF(socialClubDllPath));
		}
#endif

		// check if the DLL exists and we can access it
		//@@: location RLPC_LOADVERIFY_CHECK_PATH
		rverify(GetFileAttributesW(socialClubDllPath) != INVALID_FILE_ATTRIBUTES, catchall, SetInitErrorCode(SC_INIT_ERR_NO_DLL));

#if RLPC_DO_CERTIFICATE_VERIFICATION
		// do authenticode verification before loading the release DLL
		rverify(CertificateVerify::Verify(socialClubDllPath, RockstarCertificateVerification), catchall, SetInitErrorCode(SC_INIT_ERR_SIGNATURE_VERIFICATION_FAILED));
#else
		if(!CertificateVerify::Verify(socialClubDllPath, RockstarCertificateVerification))	{ rlWarning("SC.DLL Certificate Verification failed.");	}
#endif // RLPC_DO_CERTIFICATE_VERIFICATION

		typedef IRgsc* (*GetRgscInstancePtr)();
		const unsigned int kFunctionOrdinal = 1;

		// get the version of the DLL we are about to load, and store it for showing in error message if it's out of date.
		SetSocialClubVersion(socialClubDllPath);
		//@@: location RLPC_LOADVERIFY_LOAD_LIBRARY
		m_hRgscDll = LoadLibraryExW(socialClubDllPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);

		rcheck(m_hRgscDll, catchall, SetInitErrorCode(SC_INIT_ERR_LOAD_LIBRARY_FAILED));

		GetRgscInstancePtr func = (GetRgscInstancePtr)GetProcAddress(m_hRgscDll, (LPCSTR)kFunctionOrdinal);

		rcheck(func, catchall, SetInitErrorCode(SC_INIT_ERR_GET_PROC_ADDRESS_FAILED));

		IRgsc* rgsc = func();
		rcheck(rgsc, catchall, SetInitErrorCode(SC_INIT_ERR_CALL_EXPORTED_FUNC_FAILED));

		rlAssert(m_Rgsc == NULL);

		//@@: } RLPC_LOADDLL

		// if this fails, then the DLL on the local computer doesn't match the headers
		HRESULT hr = rgsc->QueryInterface(IID_IRgscV10, (void**) &m_Rgsc);
		rverify((hr == RGSC_OK) && m_Rgsc, catchall, SetInitErrorCode(SC_INIT_ERR_QUERY_INTERFACE_FAILED));

		// convert language enums
		RgscLanguage rgscLang = RGSC_LANGUAGE_INVALID;

		switch(rlGetLanguage())
		{
			case LANGUAGE_ENGLISH:
				rgscLang = RGSC_LANGUAGE_ENGLISH;
				break;
			case LANGUAGE_SPANISH:
				rgscLang = RGSC_LANGUAGE_SPANISH;
				break;
			case LANGUAGE_FRENCH:
				rgscLang = RGSC_LANGUAGE_FRENCH;
				break;
			case LANGUAGE_GERMAN:
				rgscLang = RGSC_LANGUAGE_GERMAN;
				break;
			case LANGUAGE_ITALIAN:
				rgscLang = RGSC_LANGUAGE_ITALIAN;
				break;
			case LANGUAGE_JAPANESE:
				rgscLang = RGSC_LANGUAGE_JAPANESE;
				break;
			case LANGUAGE_RUSSIAN:
				rgscLang = RGSC_LANGUAGE_RUSSIAN;
				break;
			case LANGUAGE_PORTUGUESE:
				rgscLang = RGSC_LANGUAGE_PORTUGUESE;
				break;
			case LANGUAGE_POLISH:
				rgscLang = RGSC_LANGUAGE_POLISH;
				break;
			case LANGUAGE_KOREAN:
				rgscLang = RGSC_LANGUAGE_KOREAN;
				break;
			case LANGUAGE_CHINESE_TRADITIONAL:
				rgscLang = RGSC_LANGUAGE_CHINESE_TRADITIONAL;
				break;
			case LANGUAGE_CHINESE_SIMPLIFIED:
				rgscLang = RGSC_LANGUAGE_CHINESE_SIMPLIFIED;
				break;
			case LANGUAGE_MEXICAN:
				rgscLang = RGSC_LANGUAGE_MEXICAN;
				break;
			default:
				rlAssertf(false, "unknown language specified: %d", rlGetLanguage());
				rgscLang = RGSC_LANGUAGE_ENGLISH;
				break;
		}

		// convert env enums
		ITitleId::RosEnvironment rgscEnvironment = ITitleId::RLROS_ENV_UNKNOWN;
		//@@: location RLPC_LOADVERIFY_GET_ENVIRONMENT
		switch(g_rlTitleId->m_RosTitleId.GetEnvironment())
		{
			case RLROS_ENV_RAGE:
				rgscEnvironment = ITitleId::RLROS_ENV_RAGE;
				break;
			case RLROS_ENV_CI_RAGE:
				rgscEnvironment = ITitleId::RLROS_ENV_CI_RAGE;
				break;
			case RLROS_ENV_DEV:
				rgscEnvironment = ITitleId::RLROS_ENV_DEV;
				break;
			case RLROS_ENV_CERT:
				rgscEnvironment = ITitleId::RLROS_ENV_CERT;
				break;
			case RLROS_ENV_CERT_2:
				rgscEnvironment = ITitleId::RLROS_ENV_CERT_2;
				break;
			case RLROS_ENV_PROD:
				rgscEnvironment = ITitleId::RLROS_ENV_PROD;
				break;
			default:
				rgscEnvironment = ITitleId::RLROS_ENV_UNKNOWN;
				break;
		}

		rverify(rgscEnvironment != RLROS_ENV_UNKNOWN, catchall, SetInitErrorCode(SC_INIT_ERR_UNKNOWN_ENV));

		rgsc::Configuration rgscConfig;
		rgscConfig.SetCommandLineParams(sysParam::GetArgCount(), sysParam::GetArgArray());
		rgscConfig.SetOfflineOnlyMode(PARAM_scOfflineOnly.Get() NOTFINAL_ONLY(|| PARAM_unattended.Get()));

		// this option is deprecated. All patching must be done while the
		// Social Club DLL isn't loaded (i.e. via the game's launcher)
		rgscConfig.SetPatchingEnabled(false);

		rgscConfig.SetHandleAutoSignIn(!PARAM_scNoAutologin.Get());

		// Set the additional session attr (i.e. 'tsinfo' in GTAV) and MetaData paths
		rgscConfig.SetAdditionalSessionAttr(m_AdditionalSessionAttr);
		rgscConfig.SetAdditionalJoinAttr(m_AdditionalJoinAttr);
		rgscConfig.SetMetadataPath(m_MetaDataPath); // NOTE: the path *must* be UTF8 encoded

		if (!PARAM_rldisablessl.Get() && PARAM_rlenablesslbydefault.Get())
		{
			rgscConfig.SetUseHttpsForRosServices(true);
		}

		rgsc::IConfigurationV6::GamepadSupport gamepadSupport = rgsc::IConfigurationV6::GAMEPADS_MARSHALLED;
#if !RSG_FINAL
		if (PARAM_noMarshalInput.Get())
		{
			gamepadSupport = rgsc::IConfigurationV6::GAMEPADS_SUPPORTED;
		}
#endif
		rgscConfig.SetGamepadSupport(gamepadSupport);
		rgscConfig.SetIsUiEnabled(!PARAM_scnoui.Get());
		
		if(PARAM_sclaunchermode.Get())
		{
			rgscConfig.SetIsLauncher(true);
		}

#if __DEV
		// for the sample app, which can simulate running other titles
		if(strcmp(g_rlTitleId->m_RosTitleId.GetTitleName(), "lanoire") == 0)
		{
			rgscConfig.SetLocalProfilesEnabled(true);
		}
		else
#endif
		{
			rgscConfig.SetLocalProfilesEnabled(false);
		}

		rgsc::TitleId rgscTitleId;
		rgscTitleId.SetRosTitleName(g_rlTitleId->m_RosTitleId.GetTitleName());
		rgscTitleId.SetRosEnvironment(rgscEnvironment);
		rgscTitleId.SetRosTitleSecrets(g_rlTitleId->m_RosTitleId.GetTitleSecretsBase64());
		rgscTitleId.SetUseStagingEnvironment(rlIsUsingStagingEnvironment());

		const u8* rsaKey = NULL;
		unsigned rsaKeyLen;
		//@@: location RLPC_LOAD_GET_PUBLIC_RSA_KEY
		g_rlTitleId->m_RosTitleId.GetPublicRsaKey(&rsaKey, &rsaKeyLen);
		rgscTitleId.SetPublicRsaKey(rsaKey, rsaKeyLen);

#if __STEAM_BUILD
		rgscTitleId.SetPlatform(ITitleId::PLATFORM_STEAM);
		rgscTitleId.SetSteamAuthTicket(g_rlTitleId->m_RosTitleId.GetSteamAuthTicket());
		rgscTitleId.SetSteamAppId(g_rlTitleId->m_RosTitleId.GetSteamAppId());

		if (SteamFriends())
		{
			rgscTitleId.SetSteamPersona(SteamFriends()->GetPersonaName());
		}

		if (SteamUser())
		{
			CSteamID steamId = SteamUser()->GetSteamID();
			rgscTitleId.SetSteamId(steamId.ConvertToUint64());
			rlDebug("Steam id: %I64u", steamId.ConvertToUint64());
		}

		// Steam Offline Mode
		bool bSteamOffLineMode = false;
		if (SteamUser() && !SteamUser()->BLoggedOn())
		{
			bSteamOffLineMode = true;
			rgscConfig.SetOfflineOnlyMode(true);
		}

		rlDebug("Steam offline: %s", bSteamOffLineMode ? "TRUE" : "FALSE");
#elif __MAC_APPSTORE_BUILD
		rgscTitleId.SetPlatform(ITitleId::PLATFORM_MAC_APP_STORE);
#else
		printf("Based windows chad.");
#endif
		rgscTitleId.SetRosTitleVersion(g_rlTitleId->m_RosTitleId.GetTitleVersion());
		rgscTitleId.SetScVersion(g_rlTitleId->m_RosTitleId.GetScVersion());
		rgscTitleId.SetTitleDirectoryName(g_rlTitleId->m_RosTitleId.GetTitleDirectoryName());

#if __MAC_APPSTORE_BUILD
		rgscTitleId.SetMacAppStoreReceipt((rgsc::u8*)g_rlTitleId->m_RosTitleId.GetMacAppStoreReceipt(), g_rlTitleId->m_RosTitleId.GetMacAppstoreReceiptLength());
#endif

		//rgscTitleId.SetRootDataDirectory("C:\\OnLiveRootDir\\"); // for OnLive

#if !__NO_OUTPUT
		u32 processInstance = 0;
		if(PARAM_processinstance.Get(processInstance))
		{
			if(rlVerify(processInstance > 0))
			{
				wchar_t rootDir[RGSC_MAX_PATH] = {0};

				if(SHGetFolderPathW(NULL, CSIDL_MYDOCUMENTS, NULL, SHGFP_TYPE_CURRENT, rootDir) == RGSC_OK)
				{
					USES_CONVERSION;
					safecatf(rootDir, L"\\Rockstar Games %u\\", processInstance);
					rgscTitleId.SetRootDataDirectory(WIDE_TO_UTF8(reinterpret_cast<const char16*>(rootDir)));
				}
			}

#if !__FINAL
			ioPad::sm_IgnoreControllerInput = true;
#endif
		}
#endif

		rverify(m_Rgsc->Init(&rgscConfig, &rgscTitleId, rgscLang, &m_Delegate) == RGSC_OK, catchall, SetInitErrorCode(SC_INIT_ERR_INIT_CALL_FAILED));

		IProfileManager* profileManager = m_Rgsc->GetProfileManager();
		rverify(profileManager, catchall, SetInitErrorCode(SC_INIT_ERR_GET_SUBSYSTEM_FAILED));
		rlAssert(m_ProfileManager == NULL);

		// if this fails, then the DLL on the local computer doesn't match the headers
		hr = profileManager->QueryInterface(IID_IProfileManagerV2, (void**) &m_ProfileManager);
		rverify((hr == RGSC_OK) && m_ProfileManager, catchall, SetInitErrorCode(SC_INIT_ERR_QUERY_INTERFACE_FAILED));

		IAchievementManager* achievementManager = m_Rgsc->GetAchievementManager();
		rverify(achievementManager, catchall, SetInitErrorCode(SC_INIT_ERR_GET_SUBSYSTEM_FAILED));
		rlAssert(m_AchievementManager == NULL);

		// if this fails, then the DLL on the local computer doesn't match the headers
		hr = achievementManager->QueryInterface(IID_IAchievementManagerV3, (void**) &m_AchievementManager);
		rverify((hr == RGSC_OK) && m_AchievementManager, catchall, SetInitErrorCode(SC_INIT_ERR_QUERY_INTERFACE_FAILED));

		IGamepadManager* gamepadManager = m_Rgsc->GetGamepadMgr();
		rverify(gamepadManager, catchall, SetInitErrorCode(SC_INIT_ERR_GET_SUBSYSTEM_FAILED));
		rlAssert(m_GamepadManager == NULL);

		// if this fails, then the DLL on the local computer doesn't match the headers
		hr = gamepadManager->QueryInterface(IID_IGamepadManagerV3, (void**) &m_GamepadManager);
		rverify((hr == RGSC_OK) && m_GamepadManager, catchall, SetInitErrorCode(SC_INIT_ERR_QUERY_INTERFACE_FAILED));

		IPlayerManager* playerManager = m_Rgsc->GetPlayerManager();
		rverify(playerManager, catchall, SetInitErrorCode(SC_INIT_ERR_GET_SUBSYSTEM_FAILED));
		rlAssert(m_PlayerManager == NULL);

		// if this fails, then the DLL on the local computer doesn't match the headers
		hr = playerManager->QueryInterface(IID_IPlayerManagerV2, (void**) &m_PlayerManager);
		rverify((hr == RGSC_OK) && m_PlayerManager, catchall, SetInitErrorCode(SC_INIT_ERR_QUERY_INTERFACE_FAILED));

		IPresenceManager* presenceManager = m_Rgsc->GetPresenceManager();
		rverify(presenceManager, catchall, SetInitErrorCode(SC_INIT_ERR_GET_SUBSYSTEM_FAILED));
		rlAssert(m_PresenceManager == NULL);

		// if this fails, then the DLL on the local computer doesn't match the headers
		hr = presenceManager->QueryInterface(IID_IPresenceManagerV4, (void**) &m_PresenceManager);
		rverify((hr == RGSC_OK) && m_PresenceManager, catchall, SetInitErrorCode(SC_INIT_ERR_QUERY_INTERFACE_FAILED));

		ICommerceManager* commerceManager = m_Rgsc->GetCommerceManager();
		rverify(commerceManager, catchall, );
		rlAssert(m_CommerceManager == NULL);

		// if this fails, then the DLL on the local computer doesn't match the headers
		hr = commerceManager->QueryInterface(IID_ICommerceManagerV4, (void**) &m_CommerceManager);
		rverify((hr == RGSC_OK) && m_CommerceManager, catchall, SetInitErrorCode(SC_INIT_ERR_QUERY_INTERFACE_FAILED));

#if __STEAM_BUILD
		m_CommerceManager->SetSteamStoreOpenCallback(SteamStoreOpenCallback);
#endif

		IFileSystem* fileSystem = m_Rgsc->GetFileSystem();
		rverify(fileSystem, catchall, SetInitErrorCode(SC_INIT_ERR_GET_SUBSYSTEM_FAILED));
		rlAssert(m_FileSystem == NULL);

		// if this fails, then the DLL on the local computer doesn't match the headers
		hr = fileSystem->QueryInterface(IID_IFileSystemV3, (void**) &m_FileSystem);
		rverify((hr == RGSC_OK) && m_FileSystem, catchall, SetInitErrorCode(SC_INIT_ERR_QUERY_INTERFACE_FAILED));

		IRgscUi* rgscUi = m_Rgsc->GetUiInterface();
		rverify(rgscUi, catchall, SetInitErrorCode(SC_INIT_ERR_GET_SUBSYSTEM_FAILED));
		rlAssert(m_RgscUi == NULL);

		// if this fails, then the DLL on the local computer doesn't match the headers
		hr = rgscUi->QueryInterface(IID_IRgscUiV7, (void**) &m_RgscUi);
		rcheck((hr == RGSC_OK) && m_RgscUi, catchall, SetInitErrorCode(SC_INIT_ERR_QUERY_INTERFACE_FAILED));
		m_RgscUi->ForwardExternalUrls(true);

		IActivation* activationSystem = m_RgscUi->GetActivationSystem();
		rverify(activationSystem, catchall, SetInitErrorCode(SC_INIT_ERR_GET_SUBSYSTEM_FAILED));
		rlAssert(m_ActivationSystem == NULL);

		// if this fails, then the DLL on the local computer doesn't match the headers
		hr = activationSystem->QueryInterface(IID_IActivationV2, (void**) &m_ActivationSystem);
		rverify((hr == RGSC_OK) && m_ActivationSystem, catchall, SetInitErrorCode(SC_INIT_ERR_QUERY_INTERFACE_FAILED));

		IPatching* patchingSystem = m_RgscUi->GetPatchingSystem();
		rverify(patchingSystem, catchall, SetInitErrorCode(SC_INIT_ERR_GET_SUBSYSTEM_FAILED));
		rlAssert(m_PatchingSystem == NULL);

		// if this fails, then the DLL on the local computer doesn't match the headers
		hr = patchingSystem->QueryInterface(IID_IPatchingV1, (void**) &m_PatchingSystem);
		rverify((hr == RGSC_OK) && m_PatchingSystem, catchall, SetInitErrorCode(SC_INIT_ERR_QUERY_INTERFACE_FAILED));

		ITaskManager* taskManager = m_Rgsc->GetTaskManager();
		rverify(taskManager, catchall, SetInitErrorCode(SC_INIT_ERR_GET_SUBSYSTEM_FAILED));
		rlAssert(m_TaskManager == NULL);

		// if this fails, then the DLL on the local computer doesn't match the headers
		hr = taskManager->QueryInterface(IID_ITaskManagerV1, (void**) &m_TaskManager);
		rverify((hr == RGSC_OK) && m_TaskManager, catchall, SetInitErrorCode(SC_INIT_ERR_QUERY_INTERFACE_FAILED));

		ITelemetry* telemetry = m_Rgsc->GetTelemetry();
		rverify(telemetry, catchall, SetInitErrorCode(SC_INIT_ERR_GET_SUBSYSTEM_FAILED));
		
		// if this fails, then the DLL on the local computer doesn't match the headers
		hr = telemetry->QueryInterface(IID_ITelemetryV3, (void**) &m_Telemetry);
		rverify((hr == RGSC_OK) && m_Telemetry, catchall, SetInitErrorCode(SC_INIT_ERR_QUERY_INTERFACE_FAILED));

		INetwork* network = m_Rgsc->GetNetworkInterface();
		rverify(network, catchall, SetInitErrorCode(SC_INIT_ERR_GET_SUBSYSTEM_FAILED));

		// if this fails, then the DLL on the local computer doesn't match the headers
		hr = network->QueryInterface(IID_INetworkV1, (void**) &m_NetworkInterface);
		rverify((hr == RGSC_OK) && m_NetworkInterface, catchall, SetInitErrorCode(SC_INIT_ERR_QUERY_INTERFACE_FAILED));

		ICloudSaveManager* cloudSaveManager = m_Rgsc->GetCloudSaveManager();
		rverify(cloudSaveManager, catchall, SetInitErrorCode(SC_INIT_ERR_GET_SUBSYSTEM_FAILED));

		// if this fails, then the DLL on the local computer doesn't match the headers
		hr = cloudSaveManager->QueryInterface(IID_ICloudSaveManagerV3, (void**) &m_CloudSaveManager);
		rverify((hr == RGSC_OK) && m_CloudSaveManager, catchall, SetInitErrorCode(SC_INIT_ERR_QUERY_INTERFACE_FAILED));

		IGamerPicManager* gamerPicManager = m_Rgsc->GetGamerPicManager();
		rverify(gamerPicManager, catchall, SetInitErrorCode(SC_INIT_ERR_GET_SUBSYSTEM_FAILED));

		// if this fails, then the DLL on the local computer doesn't match the headers
		hr = gamerPicManager->QueryInterface(IID_IGamerPicManagerV1, (void**) &m_GamerPicManager);
		rverify((hr == RGSC_OK) && m_GamerPicManager, catchall, SetInitErrorCode(SC_INIT_ERR_QUERY_INTERFACE_FAILED));

		// setup marshaled gamepads if necessary
		if (gamepadSupport == IConfigurationV6::GAMEPADS_MARSHALLED)
		{
			for (u32 i = 0; i < COUNTOF(m_ScuiPads); i++)
			{
				m_ScuiPads[m_NumScuiPads] = rage_new rgsc::RgscGamepad();
				if (m_ScuiPads[m_NumScuiPads])
				{
					m_ScuiPads[m_NumScuiPads]->ClearInputs();
					m_ScuiPads[m_NumScuiPads]->SetPadIndex(i);
					m_NumScuiPads++;
				}
			}

			m_GamepadManager->SetMarshalledGamepads((rgsc::IGamepad**)&m_ScuiPads[0], m_NumScuiPads);
		}

		success = true;
	}
	rcatchall
	{
		m_Rgsc = NULL;
		m_ProfileManager = NULL;
		m_AchievementManager = NULL;
		m_PlayerManager = NULL;
		m_Telemetry = NULL;
		m_GamepadManager = NULL;
		m_PresenceManager = NULL;
		m_CommerceManager = NULL;
		m_ActivationSystem = NULL;
		m_PatchingSystem = NULL;
		m_FileSystem = NULL;
		m_TaskManager = NULL;
		m_NetworkInterface = NULL;
		m_CloudSaveManager = NULL;
		m_GamerPicManager = NULL;

		m_RgscUi = NULL;
		m_hRgscDll = NULL;
	}
#endif // ! __RGSC_DLL

	return success;
}

#if __BANK	// Stub function for ioEventQueue
static bool is_ui_showing()
{
	return g_rlPc.IsUiShowing();
}
#endif

#if !__NO_OUTPUT || defined(MASTER_NO_SCUI)
static bool SignInWithoutScDll(const char* email, const char* password)
{
	bool success = false;

	rlRosSignInWithoutScUiTask* task = NULL;

	rtry
	{
		rverify(rlGetTaskManager()->CreateTask(&task),catchall,);

		static netStatus s_MyStatus;
		rverify(rlTaskBase::Configure(task,
			email,
			password,
			&s_MyStatus), catchall,);

		rverify(rlGetTaskManager()->AddParallelTask(task), catchall,);

		success = true;
	}
	rcatchall
	{
		if(task)
		{
			rlGetTaskManager()->DestroyTask(task);
		}
	}

	return success;
}
#endif

bool
rlPc::Init()
{
	bool success = false;

	SetInitErrorCode(SC_INIT_ERR_NONE);

#if !__RGSC_DLL
#if __BANK
	ioEventQueue::ShouldPostMessages = is_ui_showing;
#endif

#if !__FINAL
	if (PARAM_unattended.Get())
	{
		PARAM_noSocialClub.Set("");
	}
#endif

#if __ASSERT
	if (PARAM_nographics.Get())
	{
		rlAssertf(PARAM_noSocialClub.Get() || PARAM_scnoui.Get(), "When -nographics is specified, either -noSocialClub or -scNoUi must be specified.");
	}
#endif

	rtry
	{
#if !__NO_OUTPUT || defined(RSG_LEAN_CLIENT) || defined(MASTER_NO_SCUI)
		const char* scEmail;
		const char* scPassword;
		if(PARAM_scemail.Get(scEmail) && PARAM_scpassword.Get(scPassword))
		{
			const char* scDll;
			if(PARAM_scDll.Get(scDll))
			{
				if(!strcmp(scDll, "none"))
				{
					SignInWithoutScDll(scEmail, scPassword);
					rcheck(false, noDll, );
				}
			}
		}
#endif
		rcheck(!PARAM_noSocialClub.Get(), noDll, SetInitErrorCode(SC_INIT_ERR_NO_SC_COMMAND_LINE));

		rcheck(LoadDll(), noDll, );
		rcheck(m_Rgsc != NULL, catchall, SetInitErrorCode(SC_INIT_ERR_RGSC_NULL_AFTER_LOAD));

		grcDevice::sm_PresentCallback = RgscPresentCallback;
#if LIVESTREAM_ENABLED
		grcDevice::sm_PostPresentCallback = PostPresentCallback;
#endif

		Functor0 lostCb	 = MakeFunctor(RgscDeviceLostCB);
		Functor0 resetCb = MakeFunctor(RgscDeviceResetCB);

		if (!PARAM_nographics.Get())
		{
			GRCDEVICE.RegisterDeviceLostCallbacks(lostCb, resetCb);

			SetTargetArea();

#if __D3D11
			{
				IDXGISwapChain* pSwapChain = (IDXGISwapChain*)GRCDEVICE.GetSwapChain();
				DXGI_SWAP_CHAIN_DESC desc;
				pSwapChain->GetDesc(&desc);

				rcheck(m_RgscUi->OnCreateDevice((IUnknown*)GRCDEVICE.GetCurrent(), &desc) == RGSC_OK, catchall, SetInitErrorCode(SC_INIT_ERR_CREATE_DEVICE_DX11));
			}
#else
			{
				grcPresentParameters *pPresentParams = GRCDEVICE.GetPresentParameters();
				rcheck(m_RgscUi->OnCreateDevice((IUnknown*)GRCDEVICE.GetCurrent(),pPresentParams) == RGSC_OK, catchall, SetInitErrorCode(SC_INIT_ERR_CREATE_DEVICE_DX9));
			}
#endif
		}

		m_RelayDelegate.Bind(this, &rlPc::OnRelayEvent);
		netRelay::AddDelegate(NET_RELAYEVENT_MESSAGE_RECEIVED, &m_RelayDelegate);

		// Create PC Pipe		
		m_PCPipe = rage_new rlPCPipe();
		rcheck(m_PCPipe != NULL, catchall, SetInitErrorCode(SC_INIT_ERR_NULL_PIPE));
		
		success = true;
	}
	rcatch(noDll)
	{
		success = true;
	}
	rcatchall
	{

	}
#endif

	return success;
}

void
rlPc::Shutdown()
{
	while(s_IsRendering)
	{
		// I don't want to use a critical section here if I can get away without one.
	}


	m_Delegator.Clear();

	m_RgscUi = NULL;
	m_ProfileManager = NULL;
	m_AchievementManager = NULL;
	m_PlayerManager = NULL;
	m_Telemetry = NULL;
	m_GamepadManager = NULL;
	m_PresenceManager = NULL;
	m_CommerceManager = NULL;
	m_ActivationSystem = NULL;
	m_PatchingSystem = NULL;
	m_FileSystem = NULL;
	m_TaskManager = NULL;
	m_NetworkInterface = NULL;
	m_CloudSaveManager = NULL;
	m_GamerPicManager = NULL;

	SetInitErrorCode(SC_INIT_ERR_NONE);

	if(m_RelayDelegate.IsRegistered())
	{
		netRelay::RemoveDelegate(NET_RELAYEVENT_MESSAGE_RECEIVED, &m_RelayDelegate);
	}
	
	if (m_PCPipe)
	{
		if (m_PCPipe->IsConnected())
		{
			m_PCPipe->Disconnect();
		}

		delete m_PCPipe;
		m_PCPipe = NULL;
	}

	if(m_Rgsc)
	{
		IRgscV7* rgsc = m_Rgsc;
		m_Rgsc = NULL;
		rgsc->Shutdown();
		FreeLibrary(m_hRgscDll);
		rgsc = NULL;
		m_hRgscDll = NULL;
	}

	for (int i = 0; i < COUNTOF(m_ScuiPads); i++)
	{
		if (m_ScuiPads[i] != nullptr)
		{
			delete m_ScuiPads[i];
		}

		m_ScuiPads[i] = nullptr;
	}

	m_NumScuiPads = 0;
}

void
rlPc::Update()
{
#if 0
	// TODO: NS
	MOUSE.SetExclusive(IsUiShowing() == false);
#endif

	if(m_PCPipe && m_PCPipe->IsConnected())
	{
		NPROFILE(m_PCPipe->Update();)
	}

	if(m_Rgsc)
	{
		NPROFILE(m_Rgsc->Update();)
	}

#if !__RGSC_DLL
	NPROFILE(m_ScuiVirtualKeyboard.Update();)
	if (m_WaitingForKeyboardResult && !m_ScuiVirtualKeyboard.IsPending())
	{
		if (m_ScuiVirtualKeyboard.Succeeded())
		{
			if (GetUiInterface())
			{
				rlDebug3("Sending Virtual Keyboard Result: %ls", m_ScuiVirtualKeyboard.GetResult());
				NPROFILE(GetUiInterface()->SendVirtualKeyboardResult((const wchar_t*)m_ScuiVirtualKeyboard.GetResult());)
			}
		}

		m_KeyboardCompleteTime = sysTimer::GetSystemMsTime();
		m_WaitingForKeyboardResult = false;
	}

	// We don't want to enable closing the SCUI until ~1 second after the virtual keyboard is closed
	// to prevent users from accidentally backing all the way out of the SCUI
	const int SCUI_CLOSE_KEYBOARD_DELAY_MS = 1000;
	if (m_KeyboardCompleteTime > 0 && 
		sysTimer::HasElapsedIntervalMs(m_KeyboardCompleteTime, SCUI_CLOSE_KEYBOARD_DELAY_MS))
	{
		NPROFILE(g_rlPc.GetUiInterface()->EnableClosingUi(true);)
		m_KeyboardCompleteTime = 0;
	}
#endif
}

void
rlPc::AddDelegate(rlPcEventDelegate* dlgt)
{
	m_Delegator.AddDelegate(dlgt);
}

void
rlPc::RemoveDelegate(rlPcEventDelegate* dlgt)
{
	m_Delegator.RemoveDelegate(dlgt);
}

void
rlPc::DispatchEvent(const rlPcEvent *e)
{
	m_Delegator.Dispatch(this, e);
}

#if __RGSC_DLL
void rlPc::OnRelayEvent(const netRelayEvent& /*event*/)
#else
void rlPc::OnRelayEvent(const netRelayEvent& event)
#endif
{
#if !__RGSC_DLL
	// TODO: NS - this is a hack to tell the Social Club DLL to check for new
	//			  presence messages when a message is received over the relay
	//			  server. Only the game connects to the relay server (not the
	//			  DLL), but the relay and presence servers can optionally
	//			  be the same server.
	rtry
	{
		rverify(NET_RELAYEVENT_MESSAGE_RECEIVED == event.m_EventType,
			catchall,);

		const netRelayEventMessageReceived& msgRecvd =
			(const netRelayEventMessageReceived&) event;

#if !__NO_OUTPUT
        char msg[1024];
        unsigned msgLen = sizeof(msg)-1;
        if(msgLen > msgRecvd.m_Length) msgLen = msgRecvd.m_Length;
        sysMemCpy(msg, msgRecvd.m_Message, msgLen);
        msg[msgLen] = '\0';
        rlDebug("Received message: %s", msg);
#endif

		RsonReader rr(msgRecvd.m_Message, msgRecvd.m_Length);

		if(rr.CheckName("ros.msgs"))
		{
			PresenceMessageV1 v1;
			v1.SetContents(msgRecvd.m_Message);

			// hack to send a message to ourselves without having to 
			// add or change an existing DLL interface.
			g_rlPc.GetPresenceManager()->PostMessage(0,
													 NULL,
													 0,
													 &v1,
													 0,
													 NULL);
		}
	}
	rcatchall
	{

	}
#endif
}

bool 
rlPc::IsInitialized()
{
	return m_Rgsc != NULL;
}

IRgscV10*
rlPc::GetRgscInterface()
{
	return m_Rgsc;
}

IAchievementManagerV3* 
rlPc::GetAchievementManager()
{
	return m_AchievementManager;
}

IPlayerManagerV2* 
rlPc::GetPlayerManager()
{
	return m_PlayerManager;
}

IPresenceManagerV4* 
rlPc::GetPresenceManager()
{
	return m_PresenceManager;
}

ICommerceManagerV4* 
rlPc::GetCommerceManager()
{
	return m_CommerceManager;
}

IProfileManagerV2* 
rlPc::GetProfileManager()
{
	return m_ProfileManager;
}

IGamepadManagerV3* 
rlPc::GetGamepadManager()
{
	return m_GamepadManager;
}

IFileSystemV3* 
rlPc::GetFileSystem()
{
	return m_FileSystem;
}

ITaskManagerV1* 
rlPc::GetTaskManager()
{
	return m_TaskManager;
}

ITelemetryV3*
rlPc::GetTelemetry()
{
	return m_Telemetry;
}

INetworkV1* 
rlPc::GetNetworkInterface()
{
	return m_NetworkInterface;
}

ICloudSaveManagerV3*
rlPc::GetCloudSaveManager()
{
	return m_CloudSaveManager;
}

IGamerPicManagerV1* 
rlPc::GetGamerPicManager()
{
	return m_GamerPicManager;
}

IRgscUiV7* 
rlPc::GetUiInterface()
{
	return m_RgscUi;
}

IActivationV2* 
rlPc::GetActivationInterface()
{
	return m_ActivationSystem;
}

IPatchingV1* 
rlPc::GetPatchingInterface()
{
	return m_PatchingSystem;
}

bool
rlPc::IsUiShowing() const
{
	return m_RgscUi && m_RgscUi->IsVisible();
}

bool
rlPc::ShowUi()
{
	rlAssert(!IsUiShowing());
	return m_RgscUi && m_RgscUi->ShowUi() == RGSC_OK;
}

bool
rlPc::ShowSigninUi()
{
	rlAssert(!IsUiShowing());
	return m_RgscUi && m_RgscUi->ShowSignInUi() == RGSC_OK;
}

bool
rlPc::ShowGamerProfileUi(const rlGamerHandle& target)
{
	rlAssert(!IsUiShowing());
	return m_PlayerManager && m_PlayerManager->ShowPlayerProfileUi(target.GetRockstarId()) == RGSC_OK;
}

bool
rlPc::ShowCommerceUi(const char* url)
{
	rlAssert(!IsUiShowing());
	return m_CommerceManager && m_CommerceManager->ShowCommerceUi(url) == RGSC_OK;
}

bool
rlPc::IsUiAcceptingCommands()
{
	return m_RgscUi && m_RgscUi->IsReadyToAcceptCommands();
}

bool rlPc::HasUserRequestedShutdownViaScui()
{
	return m_bHasScuiRequestedQuit;
}

void rlPc::SetUserRequestedShutdownViaScui(bool bShutdownRequested)
{
	rlDebug1("User has requested a shutdown via the SCUI");
	m_bHasScuiRequestedQuit = bShutdownRequested;
}

void rlPc::SetInitErrorCode(int errorCode) 
{
	if(m_InitializationErrorCode == SC_INIT_ERR_NONE) 
	{
		if (errorCode != SC_INIT_ERR_NONE)
			rlError("rlPc::SetInitErrorCode - %d", errorCode);

		m_InitializationErrorCode = errorCode;
	}
}

bool rlPc::ConnectDownloaderPipe(const char * pipeName)
{
	if(m_PCPipe == NULL)
	{
		rlWarning("PC Downloader Pipe not initialized");
		return false;
	}

	m_PCPipe->Connect(pipeName);
	
#if RSG_LAUNCHER_CHECK
	// verify that the pipe could be connected.
	if (!m_PCPipe->IsConnected())
	{
		SetInitErrorCode(SC_INIT_ERR_PIPE_CONNECT_FAILED);
		return false;
	}
#endif

	return true;
}

} //namespace rage

#endif
