#include "EntitlementManager.h"

#if RSG_ENTITLEMENT_ENABLED

#include <comdef.h>
#include <WbemIdl.h>
#include "MachineHash.h"

#include "core/app.h"
#include "wolfssl/wolfcrypt/sha.h"
#include "diag/seh.h"
#include "frontend/WarningScreen.h"
#include "fwnet/netchannel.h"
#include "network/Network.h"
#include "network/Sessions/NetworkSession.h"
#include "rline/entitlement/rlrosentitlement.h"
#include "system/param.h"
#include "system/timer.h"
//#include "system/epic.h"

using namespace rage;

// chosen by fair random.org.
// guaranteed to be random.
static volatile u32 g_EntitlementManager_magic_idle = 0x94e90e07;
static volatile u32 g_EntitlementManager_magic_waiting_online = 0x5de4e7e0;
static volatile u32 g_EntitlementManager_magic_check_pending = 0x4d6c7730;
static volatile u32 g_EntitlementManager_magic_ask_for_code_on_main_thread = 0x6b11e8d5;
static volatile u32 g_EntitlementManager_magic_waiting_for_code = 0x327c0a7a;
static volatile u32 g_EntitlementManager_magic_waiting_for_creds = 0x52af4cb3;
static volatile u32 g_EntitlementManager_magic_error = 0xd2cd7276;
static volatile u32 MAGIC_PREORDER_BONUS = 0xc9b4aa31;

#if !__NO_OUTPUT
bool CEntitlementManager::sm_bPassedLegals = false;
bool CEntitlementManager::sm_bPassedInitialization = false;
bool CEntitlementManager::sm_bPassedUIAcceptingCommands = false;
#define ENT_STATE_CHECK(x)	if (!x)	{ x = true; gnetDebug1("Setting "#x" to true"); }
#else
#define ENT_STATE_CHECK(x) 
#endif

// This cannot be a member
void CEntitlementManager_StartCheck(int)
{
	CEntitlementManager::StartCheck();
}

// Neither can this
static volatile u32 g_EntitlementManager_state = g_EntitlementManager_magic_idle;

sysCriticalSectionToken CEntitlementManager::sm_Cs;

bool CEntitlementManager::sm_initialized = false;
bool CEntitlementManager::sm_IsActivationUiShowing = false;
bool CEntitlementManager::sm_bEntitlementWorkComplete = false;
bool CEntitlementManager::sm_bFirstTimeShowSignIn = true;
char CEntitlementManager::sm_PrePopulateKey[MAX_PATH] = {0};
u32 CEntitlementManager::sm_EntitlementCheckStartTime = 0;
bool CEntitlementManager::sm_EntitlementError = false;
u32 CEntitlementManager::sm_EntitlementMsgStartTime = 0;
u32 CEntitlementManager::sm_CheckStartTime = 0;
u32 CEntitlementManager::sm_PreorderBonus = 0;
u32 CEntitlementManager::sm_codeAskStartTime = 0;

#if defined(MASTER_NO_SCUI)
namespace rage 
{
	NOSTRIP_PARAM(noentitlements, "Disable entitlements for Master builds");
	NOSTRIP_XPARAM(noSocialClub);
}
#elif !__FINAL
namespace rage
{
	PARAM(noentitlements, "Disable entitlents for non-Final builds");
	XPARAM(noSocialClub);
}
#endif //!__FINAL

RAGE_DEFINE_SUBCHANNEL(net, entitlement, DIAG_SEVERITY_DEBUG3)
#undef __net_channel
#define __net_channel net_entitlement


static const char * sm_SkuSteam					= "96C261A9C3EF0DA32CBC68A83FB7699CA1D5973D6B4D4EA1F9EF8B4E5C7D85B8";
static const char * sm_SkuMain					= "D962AD7FF354A224C6599EB313EFA50E2E04B3E82274480A2143697681FC5DC6";
static const char * sm_SkuEpic					= "04541FB36991EF2C43B38659B204EC3B941649666404498FB2CAD04B29E64A33";

static const char * sm_PreorderSinglePlayerMoney = "92694D1B738D91C40DA97F68D6946D658C64A420AE2CF5BFF92A819CF8DBAB6E";

// Called once to set up
void CEntitlementManager::Init(unsigned)
{
	gnetDebug1("Init");

	rtry
	{
#if !__FINAL || defined(MASTER_NO_SCUI)
		if (PARAM_noSocialClub.Get() || PARAM_noentitlements.Get())
		{
			gnetDebug1("Entitlements are disabled due to running with -noentitlements");
			g_EntitlementManager_state = g_EntitlementManager_magic_idle;
			sm_initialized = true;
			return;
		}
#endif // !__FINAL || defined(MASTER_NO_SCUI)

		rverify(!sm_initialized, catchall, gnetError("CEntitlementManager already initialized!"));
		sm_initialized = true;
		//@@: location ENTITLEMENTMANAGER_INIT_THREAD_ENTRY
		sysIpcCreateThread(ThreadEntry, NULL, 1024 * 16, PRIO_BELOW_NORMAL, "CEM Thread");

		// Prepopulate the redemption key
		PrePopulateKey();
	}
	rcatchall
	{

	}	
}

void CEntitlementManager::Reset()
{
	SYS_CS_SYNC(sm_Cs);
	gnetDebug1("Reset");

	sm_IsActivationUiShowing = false;
	sm_bEntitlementWorkComplete = false;
	sm_EntitlementError = false;
	sm_EntitlementCheckStartTime = 0;
	sm_EntitlementMsgStartTime = 0;
	sm_PreorderBonus = 0;

	g_EntitlementManager_state = g_EntitlementManager_magic_idle;
}

bool CEntitlementManager::IsPending()
{
#if !__FINAL || defined(MASTER_NO_SCUI)
	if (PARAM_noSocialClub.Get())
	{
		return false;
	}
#endif //!__FINAL || defined(MASTER_NO_SCUI)

	// If we're not signed in, we're always pending regardless of manager state
	if (!g_rlPc.GetProfileManager()->IsSignedIn())
		return true;

	// for UI purposes, if we lagged enough to display this message, keep it on screen to avoid a flicker.
	if (sm_EntitlementMsgStartTime > 0)
		return true;

	return g_EntitlementManager_state != g_EntitlementManager_magic_idle || !sm_initialized;
}

void CEntitlementManager::StartCheck()
{
	SYS_CS_SYNC(sm_Cs);
	gnetDebug1("StartCheck");
	//@@: location CENTITLEMENTMANAGER_STARTCHECK_GET_SYSTEM_TIME
	sm_CheckStartTime = sysTimer::GetSystemMsTime();

	if (g_EntitlementManager_state == g_EntitlementManager_magic_idle)
	{
		gnetDebug1("Update :: StartCheck, setting to Waiting Online");
		//@@: location CENTITLEMENTMANAGER_STARTCHECK_SET_STATE
		g_EntitlementManager_state = g_EntitlementManager_magic_waiting_online;
	}
}

void CEntitlementManager::Shutdown(unsigned)
{
	gnetDebug1("Shutdown");
	sm_initialized = false;
}

// Called to determine ownership of preorder bonus
bool CEntitlementManager::IsEntitledToSpPreorderBonus()
{
	return sm_PreorderBonus == MAGIC_PREORDER_BONUS;
}

bool CEntitlementManager::IsReadyToShutdown()
{
	return g_EntitlementManager_state != g_EntitlementManager_magic_check_pending;
}

void CEntitlementManager::ActivationCallback(const int /*contentId*/, const char* /*activationCode*/, const rgsc::IActivationV1::ActivationAction action)
{
	SYS_CS_SYNC(sm_Cs);
	gnetDebug1("ActivationCallback :: action: %d", (int)action);

	sm_IsActivationUiShowing = false;
	sm_EntitlementError = false;
	sm_EntitlementCheckStartTime = 0;
	sm_EntitlementMsgStartTime = 0;

	if (action == rgsc::IActivationV1::ACTIVATION_ACTIVATED_CONTENT)
	{
		// We need to wait for a credentials changed event
		if (g_EntitlementManager_state == g_EntitlementManager_magic_waiting_for_code)
		{
			gnetDebug1("ACTIVATION_ACTIVATED_CONTENT - We haven't received new creds yet");
			g_EntitlementManager_state = g_EntitlementManager_magic_waiting_for_creds;
		}
	}
	else if (action == rgsc::IActivationV1::ACTIVATION_USER_QUIT)
	{
		g_rlPc.GetUiInterface()->CloseUi();
		g_rlPc.SetUserRequestedShutdownViaScui(true);
	}
	else
	{
		sm_codeAskStartTime = sysTimer::GetSystemMsTime();
		g_EntitlementManager_state = g_EntitlementManager_magic_ask_for_code_on_main_thread;
	}
}

void CEntitlementManager::OnRosEvent(const rlRosEvent& rEvent)
{
	switch(rEvent.GetId())
	{
	case RLROS_EVENT_GET_CREDENTIALS_RESULT:
		{
			if (g_EntitlementManager_state == g_EntitlementManager_magic_waiting_for_code ||
				g_EntitlementManager_state == g_EntitlementManager_magic_waiting_for_creds)
			{
				gnetDebug1("OnRosEvent - RLROS_EVENT_GET_CREDENTIALS_RESULT : Requesting new entitlement data");
				sm_bEntitlementWorkComplete = false;
				g_EntitlementManager_state = g_EntitlementManager_magic_check_pending;
			}
		}
		break;
	default:
		break;
	}
}

// Show activation UI
void CEntitlementManager::ShowActivationUi()
{
	gnetDebug1("Showing Activation Interface");
	sm_IsActivationUiShowing = true;
	g_rlPc.GetActivationInterface()->ShowActivationCodeUi(0, sm_PrePopulateKey, false, &ActivationCallback);
}

void CEntitlementManager::UpdateOnlineState()
{
	rtry
	{
		rcheck(!CLoadingScreens::IsDisplayingLegalScreen(), catchall, );
		rcheck(g_rlPc.IsInitialized(), catchall, );
		rcheck(g_rlPc.IsUiAcceptingCommands(), catchall, );
		rcheck(!CApp::IsScuiDisabledForShutdown(), catchall, );

		if (g_rlPc.IsUiShowing())
		{
			// If the entitlement work is complete, show the activation ui
			if (sm_bEntitlementWorkComplete)
			{
				rcheck(!sm_IsActivationUiShowing, catchall, );
				// more consistant timing and animation of SCUI here. We'll see the online prompt and the
				// homepage return for 1.5 second, and then show the activation UI. Otherwise can appear janky.
				rcheck(sysTimer::HasElapsedIntervalMs(sm_codeAskStartTime, 1500), catchall, );
				ShowActivationUi();
			}
			// Otherwise, request the entitlement work to begin
			else
			{
				g_EntitlementManager_state = g_EntitlementManager_magic_check_pending;
			}
		}
		else
		{
			rlGamertag name;
			rlPresence::GetName(0, name);
			CWarningScreen::SetMessageWithHeader(WARNING_MESSAGE_STANDARD, "WARNING_ACTIVATION", "ACTIVATION_ONLINE", FE_WARNING_OK, false, -1, name);
			eWarningButtonFlags result = CWarningScreen::CheckAllInput();
			if (result == FE_WARNING_OK)
			{
				ShowActivationUi();
			}
		}
	}
	rcatchall
	{

	}
}

void CEntitlementManager::UpdateWaitingForOnlineState()
{
	//@@: range CENTITLEMENTMANAGER_UPDATEWAITINGFORONLINESTATE {
	rtry
	{
		rcheck(!CLoadingScreens::IsDisplayingLegalScreen(), catchall, );
		ENT_STATE_CHECK(sm_bPassedLegals);
		rcheck(g_rlPc.IsInitialized(), catchall, );
		ENT_STATE_CHECK(sm_bPassedInitialization);
		rcheck(g_rlPc.IsUiAcceptingCommands(), catchall, );
		ENT_STATE_CHECK(sm_bPassedUIAcceptingCommands);

		// User is now online, update the state
		if (g_rlPc.GetProfileManager()->IsOnline())
		{
			gnetDebug1("Update :: Online, setting to Magic Check Pending");
			g_EntitlementManager_state = g_EntitlementManager_magic_check_pending;
			CWarningScreen::Remove();
			sm_bFirstTimeShowSignIn = false;
			return;
		}

		// User is signed in but not online, and is thus OFFLINE
		rcheck(!g_rlPc.GetProfileManager()->IsSigningIn(), catchall, sm_bFirstTimeShowSignIn = false);
		rcheck(!g_rlPc.GetProfileManager()->IsSignedIn(), offlineflow, sm_bFirstTimeShowSignIn = false);
		rcheck(!g_rlPc.GetUiInterface()->IsVisible(), catchall, sm_bFirstTimeShowSignIn = false);
		rcheck(!CApp::IsScuiDisabledForShutdown(), catchall, );

		if (sm_bFirstTimeShowSignIn)
		{
			rcheck(!g_rlPc.IsUiShowing(), catchall, );
			sm_bFirstTimeShowSignIn = false;
			gnetDebug1("UpdateWaitingForOnlineState :: Showing Signin UI");
			//@@: location CENTITLEMENTMANAGER_UPDATEWAITINGFORONLINESTATE_SHOWSIGNINUI
			g_rlPc.ShowSigninUi();
		}
		else
		{
			return;
		}
	}
	rcatch(offlineflow)
	{
		if (sm_bEntitlementWorkComplete)
		{
			if (!CApp::IsScuiDisabledForShutdown())
			{
#if __STEAM_BUILD
				if (SteamUser() && !SteamUser()->BLoggedOn())
				{
					CWarningScreen::SetMessageWithHeader(WARNING_MESSAGE_STANDARD, "WARNING_ACTIVATION", "ACTIVATION_OFFLINE_STEAM", FE_WARNING_OK);
				}
				else
#endif
				{
					printf("screaming in public restrooms part 3");
				}

				eWarningButtonFlags result = CWarningScreen::CheckAllInput();
				if (result == FE_WARNING_OK)
				{
					gnetDebug1("Update :: Offline, Shutdown requested");
					CPauseMenu::SetGameWantsToExitToWindows(true);
					NetworkInterface::GetNetworkExitFlow().StartShutdownTasks();
				}
			}
		}
		else if (g_EntitlementManager_state != g_EntitlementManager_magic_check_pending)
		{
			g_EntitlementManager_state = g_EntitlementManager_magic_check_pending;
		}
	}
	rcatchall
	{
		static bool bShowingSocialClubInit = false;
		const int SCUI_INIT_SPINNER_MS = 1000;

		bool bIsShowingLegals = CLoadingScreens::IsDisplayingLegalScreen();
		bool bIsAcceptingCommands = g_rlPc.IsUiAcceptingCommands();
		bool bIsValidTime = sm_CheckStartTime != 0 && sysTimer::HasElapsedIntervalMs(sm_CheckStartTime, SCUI_INIT_SPINNER_MS);

		if (bIsValidTime && !bIsShowingLegals && !bIsAcceptingCommands)
		{
			CLoadingScreens::SetSocialClubLoadingSpinner();
			bShowingSocialClubInit = true;
		}
		else if (bShowingSocialClubInit && g_rlPc.IsUiAcceptingCommands())
		{
			CLoadingScreens::ShutdownSocialClubLoadingSpinner();
			bShowingSocialClubInit = false;
		}
	}
	//@@: } CENTITLEMENTMANAGER_UPDATEWAITINGFORONLINESTATE

}

void CEntitlementManager::Update()
{
	SYS_CS_SYNC(sm_Cs);

	// Check for noSocialClub
#if !__FINAL || defined(MASTER_NO_SCUI)
	if (PARAM_noSocialClub.Get())
	{
		g_EntitlementManager_state = g_EntitlementManager_magic_idle;
		return;
	}

	if (PARAM_noentitlements.Get())
	{
		// Even without -entitlements, a signin is still required
		if (!g_rlPc.GetProfileManager()->IsSignedIn())
		{
			UpdateWaitingForOnlineState();
		}

		g_EntitlementManager_state = g_EntitlementManager_magic_idle;
		return;
	}
#endif // !__FINAL || defined(MASTER_NO_SCUI)

	if (g_EntitlementManager_state == g_EntitlementManager_magic_ask_for_code_on_main_thread)
	{
		gnetDebug1("Update :: Setting state to g_EntitlementManager_magic_waiting_for_code");
		g_EntitlementManager_state = g_EntitlementManager_magic_waiting_for_code;
	}
	else if (g_EntitlementManager_state == g_EntitlementManager_magic_waiting_for_code)
	{
		if (g_rlPc.GetProfileManager()->IsOnline())
		{
			UpdateOnlineState();
		}
		else
		{
			gnetDebug1("Update :: Not Online, setting to Waiting Online");
			//@@: location CENTITLEMENTMANAGER_UPDATE_SET_ONLINE
			g_EntitlementManager_state = g_EntitlementManager_magic_waiting_online;
		}
	}
	else if (g_EntitlementManager_state == g_EntitlementManager_magic_waiting_online)
	{
		UpdateWaitingForOnlineState();
	}
	else if (g_EntitlementManager_state == g_EntitlementManager_magic_check_pending || 
			 g_EntitlementManager_state == g_EntitlementManager_magic_waiting_for_creds)
	{
		const int MIN_TIME_WARNING = 8000;
		//@@: location CENTITLEMENTMANAGER_UPDATE_SET_PENDING
		if (sm_EntitlementCheckStartTime != 0 && !g_rlPc.IsUiShowing() && sysTimer::HasElapsedIntervalMs(sm_EntitlementCheckStartTime, MIN_TIME_WARNING))
		{
			CWarningScreen::SetMessageWithHeader(WARNING_MESSAGE_STANDARD, "ACTIVATION_HEADER", "ACTIVATION_PENDING", FE_WARNING_SPINNER);
			if (sm_EntitlementMsgStartTime == 0)
			{
				sm_EntitlementMsgStartTime = sysTimer::GetSystemMsTime();
				gnetDebug1("Displaying \"activation taking too long\" message at %u", sm_EntitlementMsgStartTime);
			}
		}
	}
	else if (g_EntitlementManager_state == g_EntitlementManager_magic_error)
	{
		CWarningScreen::SetMessageWithHeader(WARNING_MESSAGE_STANDARD, "ACTIVATION_ERROR", "ACTIVATION_ERROR_NO_OFFLINE", FE_WARNING_OK);
		eWarningButtonFlags result = CWarningScreen::CheckAllInput();
		if (result == FE_WARNING_OK)
		{
			gnetDebug1("Update :: Error, Shutdown requested");
			CPauseMenu::SetGameWantsToExitToWindows(true);
			NetworkInterface::GetNetworkExitFlow().StartShutdownTasks();
		}
	}
	// Make sure warning is on screen for at least 1500ms
	else if (sm_EntitlementMsgStartTime != 0)
	{
		const int MIN_DISPLAY_TIME = 1500;
		if (sysTimer::HasElapsedIntervalMs(sm_EntitlementMsgStartTime, MIN_DISPLAY_TIME))
		{
			sm_EntitlementMsgStartTime = 0;
			gnetDebug1("Clearing \"activation taking too long\" message at %u", sm_EntitlementMsgStartTime);
		}
		else
		{
			CWarningScreen::SetMessageWithHeader(WARNING_MESSAGE_STANDARD, "ACTIVATION_HEADER", "ACTIVATION_PENDING", FE_WARNING_NONE);
		}
	}
}

#define CHECKDINPUT_THRESHOLD 1000*30
void CEntitlementManager::ThreadEntry(void*)
{
	while (sm_initialized)
	{
		// dormant until rlPC is initialized
		while(!g_rlPc.IsInitialized())
		{
			sysIpcSleep(100);
			
			if (CApp::IsShutdownConfirmed())
				return;
		}

		// dormant until the main thread requests some work
		while (g_EntitlementManager_state != g_EntitlementManager_magic_check_pending)
		{
			//@@: range CENTITLEMENTMANAGER_THREADENTRY_LOOP {
			sysIpcSleep(100);

			if (CApp::IsShutdownConfirmed())
				return;
			//@@: } CENTITLEMENTMANAGER_THREADENTRY_LOOP 
		}

		sysIpcSleep(30);

		sm_EntitlementCheckStartTime = sysTimer::GetSystemMsTime();

		rage::netStatus asyncStatus;

		// Set up buffer for entitlement block
		const size_t ENTITLEMENT_BLOCK_SIZE = 1024 * 64;
		char* entitlementBlock = rage_new char[ENTITLEMENT_BLOCK_SIZE];
		unsigned int entitlementBlockSize = 0;
		//@@: range CENTITLEMENTMANAGER_THREADENTRY {

		gnetDebug1("ThreadEntry :: Calculating machine hash");

		// Get the machine hash
		char machineHash[HASH_DIGEST_STRING_LENGTH];
		// Get the profile information
		rgsc::Profile profile;
		rgsc::RockstarId profileId = InvalidRockstarId;
		//@@: location CENTITLEMENTMANAGER_THREADENTRY_GET_SIGN_IN_INFO
		if (g_rlPc.GetProfileManager())
		{
			HRESULT hr = g_rlPc.GetProfileManager()->GetSignInInfo(&profile);
			if(hr == ERROR_SUCCESS)
			{
				profileId = profile.GetProfileId();
			}
		}

		//@@: range CENTITLEMENTMANAGER_THREADENTRY_ACTUAL_WORK {
		//@@: location CENTITLEMENTMANAGER_THREADENTRY_CALL_MACHINE_HASH
		CMachineHash::GetMachineHash(machineHash, profileId);

		gnetDebug1("Machine Hash for profile id: %"I64FMT"d: %s", profileId, machineHash);

		if(g_rlPc.GetProfileManager() && g_rlPc.GetProfileManager()->IsOnline())
		{
			// Start an async request for the entitlement block, coerced into being synchronous
			int version = 0;
			gnetDebug1("ThreadEntry :: Getting entitlement block");
			//@@: location CENTITLEMENTMANAGER_THREADENTRY_GET_ENTITLEMENT_BLOCK
			if (rlRosEntitlement::GetEntitlementBlock(0, machineHash, "en-US", entitlementBlock, ENTITLEMENT_BLOCK_SIZE, &version, &entitlementBlockSize, &asyncStatus))
			{
				// Wait for the async operation
				while (asyncStatus.Pending())
				{
					sysIpcSleep(1);
				}
			}
			else
			{
				// Clear the block to signify failure
				delete[] entitlementBlock;
				entitlementBlock = NULL;
			}
		}

#if !__FINAL
		// Offline Entitlement is tied to the launcher in final builds. In !Final builds, there is no launcher...
		if (entitlementBlock && asyncStatus.Succeeded())
		{
			gnetDebug1("ThreadEntry :: Got block :: SaveOfflineEntitlement");
			rlRosEntitlement::SaveOfflineEntitlement((u8*)entitlementBlock, entitlementBlockSize, (rgsc::u8*)machineHash);
			while(rlRosEntitlement::IsEntitlementIOInProgress())
				sysIpcSleep(1);
		}
#endif

		sm_EntitlementError = asyncStatus.Failed();

		//@@: location CENTITLEMENTMANAGER_THREADENTRY_ISINITIALIZED
		if(entitlementBlockSize == 0 && rlRosEntitlement::IsInitialized())
		{
#if SUPPORT_OFFLINE_ENTITLEMENT
			bool retBool = rlRosEntitlement::LoadOfflineEntitlement((rgsc::u8*)machineHash);
			if(retBool)
			{

				//@@: range CENTITLEMENTMANAGER_THREADENTRY_FETCH_OFFLINE_VALUES {
				while(rlRosEntitlement::IsEntitlementIOInProgress())
					sysIpcSleep(1);
				//@@: location CENTITLEMENTMANAGER_THREADENTRY_GET_OFFLINE_VALUES
				entitlementBlock = (char *)rlRosEntitlement::GetOfflineEntitlement();
				entitlementBlockSize = rlRosEntitlement::GetOfflineEntitlementLength();
				//@@: } CENTITLEMENTMANAGER_THREADENTRY_FETCH_OFFLINE_VALUES

			}
#endif
		}

		// Reset the wait, or go round again if another request has come in
		SYS_CS_SYNC(sm_Cs);

		u32 nextState;
		//@@: location CENTITLEMENTMANAGER_THREADENTRY_DOESDATABLOCKGRANTENTITLEMENT

		/////////////////////////////////////// START PREORDER BONUS /////////////////////////////////////////////////
		if (entitlementBlock)
		{
			char* entBlockPreorder = rage_new char[ENTITLEMENT_BLOCK_SIZE];
			if (entBlockPreorder)
			{
				sysMemCpy(entBlockPreorder, entitlementBlock, entitlementBlockSize);

				if (rlRosEntitlement::DoesDataBlockGrantEntitlement(profileId, machineHash, entBlockPreorder, entitlementBlockSize, sm_PreorderSinglePlayerMoney))
				{
					sm_PreorderBonus = MAGIC_PREORDER_BONUS;
				}

				delete[] entBlockPreorder;
				entBlockPreorder = NULL;
			}

			gnetDebug1("Has entitlement for Preorder cash - %s", IsEntitledToSpPreorderBonus() ? "TRUE" : "FALSE");
		}
		/////////////////////////////////////// END PREORDER BONUS /////////////////////////////////////////////////
		//@@: location CENTITLEMENTMANAGER_THREADENTRY_DOESDATABLOCKGRANTENTITLEMENT_GAME

		const char* sku = NULL;

#if __STEAM_BUILD
		sku = sm_SkuSteam;
#else
		//if(g_SysEpic.IsInitialized())
		//{
		//	sku = sm_SkuEpic;
		//}
		/* TODO JRM
		else if(g_sysSteam.IsInitialized())
		{
			sku = sm_SkuSteam;
		}
		*/
		//else
		//{
			sku = sm_SkuMain;
		//}
#endif // __STEAM_BUILD

		if (entitlementBlock && rlRosEntitlement::DoesDataBlockGrantEntitlement(profileId, machineHash, entitlementBlock, entitlementBlockSize, sku))
		{
			gnetDebug1("ThreadEntry :: Updating state to g_EntitlementManager_magic_idle");
			nextState = g_EntitlementManager_magic_idle;
		}
		else
		{
			if (sm_EntitlementError)
			{
				gnetDebug1("ThreadEntry :: Updating state to g_EntitlementManager_magic_error");
				nextState = g_EntitlementManager_magic_error;
			}
			else
			{
				gnetDebug1("ThreadEntry :: Updating state to g_EntitlementManager_magic_ask_for_code_on_main_thread");
				sm_codeAskStartTime = sysTimer::GetSystemMsTime();
				nextState = g_EntitlementManager_magic_ask_for_code_on_main_thread;
			}
		}
		//@@: } CENTITLEMENTMANAGER_THREADENTRY_ACTUAL_WORK


		// If we loaded offline entitlement, clear it. At this point entitlementBlock simply points
		// at the same memory owned by rlRosEntitlement
		
		//@@: location CENTITLEMENTMANAGER_THREADENTRY_GET_OFFLINE_ENTITLEMENT 
		if (rlRosEntitlement::GetOfflineEntitlement())
		{
			rlRosEntitlement::ClearOfflineEntitlement();
		}
		else if (entitlementBlock)
		{
			delete[] entitlementBlock;
			entitlementBlock = NULL;
		}

		// Zero-out the machine hash, as this function is the last to use it
		sysMemSet(machineHash, 0, sizeof(machineHash));
		
		sm_bEntitlementWorkComplete = true;
		g_EntitlementManager_state = nextState;
		//@@: } CENTITLEMENTMANAGER_THREADENTRY

	}
}

bool CEntitlementManager::PrePopulateKey()
{
#if __STEAM_BUILD
	HKEY	hKey = NULL;
	DWORD	dwType = 0;
	DWORD	dwSize = MAX_PATH;
	LRESULT lResult;

	// Location of the registry key where Steam will be putting the serial number.
	char *wSteamKey = "Software\\Rockstar Games";

	// Open the registry key.
	lResult = RegOpenKeyEx(HKEY_CURRENT_USER, wSteamKey, 0, KEY_READ, &hKey);

	// Make sure the key exists.
	if (lResult == ERROR_SUCCESS)
	{
		// Make sure the "CDKey" key value exists, otherwise return an empty serial number.
		lResult = RegQueryValueEx(hKey, "Grand Theft Auto V", 0, &dwType, (BYTE*)sm_PrePopulateKey, &dwSize);
		if (lResult == ERROR_SUCCESS && dwSize < MAX_PATH)
		{
			sm_PrePopulateKey[dwSize] = '\0';
			RegCloseKey(hKey);
			return true;
		}
		else
		{
			RegCloseKey(hKey);
			return false;
		}
	}
	// If the key doesn't even exist then return an empty serial.
	else
	{
		return false;
	}
#elif !__FINAL
	safecpy(sm_PrePopulateKey, "TEST-GTAV-MAIN");
	return true;
#else
	return true;
#endif
}

#endif // RSG_ENTITLEMENT_ENABLED
