1514 lines
54 KiB
C
1514 lines
54 KiB
C
// This code contains NVIDIA Confidential Information and is disclosed to you
|
|
// under a form of NVIDIA software license agreement provided separately to you.
|
|
//
|
|
// Notice
|
|
// NVIDIA Corporation and its licensors retain all intellectual property and
|
|
// proprietary rights in and to this software and related documentation and
|
|
// any modifications thereto. Any use, reproduction, disclosure, or
|
|
// distribution of this software and related documentation without an express
|
|
// license agreement from NVIDIA Corporation is strictly prohibited.
|
|
//
|
|
// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES
|
|
// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO
|
|
// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT,
|
|
// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE.
|
|
//
|
|
// Information and code furnished is believed to be accurate and reliable.
|
|
// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such
|
|
// information or for any infringement of patents or other rights of third parties that may
|
|
// result from its use. No license is granted by implication or otherwise under any patent
|
|
// or patent rights of NVIDIA Corporation. Details are subject to change without notice.
|
|
// This code supersedes and replaces all information previously supplied.
|
|
// NVIDIA Corporation products are not authorized for use as critical
|
|
// components in life support devices or systems without express written approval of
|
|
// NVIDIA Corporation.
|
|
//
|
|
// Copyright (c) 2020-2021 NVIDIA Corporation. All rights reserved.
|
|
|
|
|
|
#include "GfnRuntimeSdk_Wrapper.h"
|
|
#ifdef _WIN32
|
|
#include "GfnSdk_SecureLoadLibrary.h"
|
|
#endif
|
|
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <wchar.h>
|
|
|
|
#ifdef _WIN32
|
|
# include <ShlObj.h>
|
|
# include <shlwapi.h>
|
|
# ifdef _WIN64
|
|
# define GFN_DLL L"GFN_V2.dll"
|
|
# else
|
|
# define GFN_DLL L"GFN32_V2.dll"
|
|
# endif
|
|
# define GFN_CLIENT_SHARED_LIBRARY L"GfnRuntimeSdk.dll"
|
|
# define GFN_DLL_SUBPATH L"\\NVIDIA Corporation\\GeForceNOW\\SDK\\" GFN_DLL
|
|
# define PLATFORM_MAX_PATH MAX_PATH
|
|
HMODULE g_gfnSdkModule;
|
|
CHAR_TYPE g_cloudLibraryPath[PLATFORM_MAX_PATH];
|
|
#elif __linux__
|
|
# include <time.h> // time functions for log prints
|
|
# include <limits.h> // PATH_MAX
|
|
# include <sys/types.h> // stat
|
|
# include <sys/stat.h> // stat
|
|
# include <dlfcn.h> // dlopen
|
|
# include <libgen.h> // dirname
|
|
# include <unistd.h> // readlink
|
|
# define GFN_SHARED_OBJECT "GfnSdk.so"
|
|
# define GFN_CLIENT_SHARED_LIBRARY "GfnRuntimeSdk.so"
|
|
# define GFN_SHARED_OBJECT_PATH "/opt/nvidia/GfnSdk/" GFN_SHARED_OBJECT
|
|
# define PLATFORM_MAX_PATH PATH_MAX
|
|
|
|
void* g_gfnSdkModule = NULL;
|
|
CHAR_TYPE g_cloudLibraryPath[] = GFN_SHARED_OBJECT_PATH;
|
|
#else
|
|
# error "Unsupported platform"
|
|
#endif
|
|
|
|
# define GFN_SDK_INIT_LOGGING() gfnInitLogging();
|
|
# define GFN_SDK_DEINIT_LOGGING() gfnDeinitLogging();
|
|
# define GFN_SDK_LOG(fmt, ...) gfnLog(__FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)
|
|
# define kGfnLogBufLen 1024
|
|
typedef struct gfnLogData
|
|
{
|
|
char buffer[kGfnLogBufLen];
|
|
#if _WIN32
|
|
SYSTEMTIME timeBuffer;
|
|
#elif __linux__
|
|
struct tm timeBuffer;
|
|
#endif
|
|
} gfnLogData;
|
|
static gfnLogData s_logData;
|
|
static FILE* s_logfile = NULL;
|
|
static void gfnLog(char const* func, int line, char const* format, ...);
|
|
static void gfnInitLogging();
|
|
static void gfnDeinitLogging();
|
|
bool g_LoggingInitialized = false;
|
|
|
|
// Function declarations
|
|
GfnRuntimeError GfnInitializeSdkFromPathDefault(GfnDisplayLanguage language, const CHAR_TYPE* sdkLibraryPath);
|
|
|
|
// Generic callback function pointer used to wrap the typed callbacks
|
|
typedef void (GFN_CALLBACK* _cb)(int, void* pOptionalData, void* pContext);
|
|
|
|
// Library export function definitions
|
|
typedef GfnRuntimeError(*gfnInitializeRuntimeSdkFn)(GfnDisplayLanguage language);
|
|
typedef void(*gfnShutdownRuntimeSdkFn)(void);
|
|
typedef bool (*gfnIsInitializedFn)();
|
|
typedef GfnRuntimeError(*gfnCloudInitializeRuntimeSdkFn)(float libVersion); // Old Initialization method. Deprecate when all libraries have updated to 1.7.1 or greater.
|
|
typedef GfnRuntimeError(*gfnCloudInitializeRuntimeSdkV3Fn)(const char* strLibVersion);
|
|
typedef void(*gfnCloudShutdownRuntimeSdkFn)(void);
|
|
typedef bool(*gfnIsRunningInCloudFn)(void);
|
|
typedef GfnRuntimeError(*gfnIsRunningInCloudSecureFn)(GfnIsRunningInCloudAssurance* assurance);
|
|
typedef GfnRuntimeError(*gfnCloudCheckFn)(const GfnCloudCheckChallenge* challenge, GfnCloudCheckResponse* response, bool* isCloudEnvironment);
|
|
typedef GfnRuntimeError(*gfnGetClientIpFn)(const char** cientIp);
|
|
typedef GfnRuntimeError(*gfnGetClientLanguageCodeFn)(const char** clientLanguageCode);
|
|
typedef GfnRuntimeError(*gfnGetClientCountryCodeFn)(char* clientCountryCode, unsigned int length);
|
|
|
|
typedef GfnRuntimeError(*gfnGetClientInfoFn)(GfnClientInfo* clientInfo);
|
|
typedef GfnRuntimeError(*gfnRegisterClientInfoCallbackFn)(ClientInfoCallbackSig clientInfoCallback, void* pUserContext);
|
|
typedef GfnRuntimeError(*gfnRegisterNetworkStatusCallbackFn)(NetworkStatusCallbackSig networkStatusCallback, unsigned int updateRateMs, void* pUserContext);
|
|
typedef GfnRuntimeError(*gfnRegisterMessageCallbackFn)(MessageCallbackSig messageCallback, void* pUserContext);
|
|
|
|
typedef GfnRuntimeError(*gfnGetSessionInfoFn)(GfnSessionInfo* sessionInfo);
|
|
|
|
typedef GfnRuntimeError(*gfnGetPartnerDataFn)(const char** partnerData);
|
|
typedef GfnRuntimeError(*gfnGetPartnerSecureDataFn)(const char** partnerSecureData);
|
|
|
|
typedef bool (*gfnIsTitleAvailableFn)(const char* platformAppId);
|
|
typedef GfnRuntimeError(*gfnGetTitlesAvailableFn)(const char** platformAppIds);
|
|
|
|
typedef GfnRuntimeError(*gfnFreeFn)(const char** data);
|
|
|
|
typedef GfnRuntimeError(*gfnRegisterStreamStatusCallbackFn)(StreamStatusCallbackSig streamStatusCallback, void* pUserContext);
|
|
typedef GfnRuntimeError(*gfnStartStreamFn)(StartStreamInput * startStreamInput, StartStreamResponse* response);
|
|
typedef void(*gfnStartStreamAsyncFn)(const StartStreamInput* startStreamInput, StartStreamCallbackSig cb, void* context, unsigned int timeoutMs);
|
|
|
|
typedef GfnRuntimeError(*gfnStopStreamFn)(void);
|
|
typedef void(*gfnStopStreamAsyncFn)(StopStreamCallbackSig cb, void* context, unsigned int timeoutMs);
|
|
|
|
typedef GfnRuntimeError(*gfnSetupTitleFn)(const char* platformAppId);
|
|
typedef GfnRuntimeError(*gfnTitleExitedFn)(const char* platformId, const char* platformAppId);
|
|
typedef GfnRuntimeError(*gfnRegisterCallbackFn)(_cb callback, void* userContext);
|
|
typedef GfnRuntimeError(*gfnRegisteCallbackFnWithUIntParam)(_cb callback, unsigned int param, void* userContext);
|
|
typedef GfnRuntimeError(*gfnAppReadyFn)(bool success, const char* status);
|
|
typedef GfnRuntimeError (*gfnSetActionZoneFn)(GfnActionType type, unsigned int id, GfnRect* zone);
|
|
typedef GfnRuntimeError(*gfnSendMessageFn)(const char* pchMessage, unsigned int length);
|
|
typedef GfnRuntimeError(*gfnOpenURLOnClientFn)(const char* pchUrl);
|
|
|
|
typedef struct GfnSdkCloudLibrary_t
|
|
{
|
|
void* handle;
|
|
gfnCloudInitializeRuntimeSdkFn InitializeRuntimeSdk; // Old Initialization method. Deprecate when all libraries have updated to 1.7.1 or greater.
|
|
gfnCloudInitializeRuntimeSdkV3Fn InitializeRuntimeSdkV3;
|
|
gfnCloudShutdownRuntimeSdkFn ShutdownRuntimeSdk;
|
|
gfnIsInitializedFn IsInitialized;
|
|
gfnIsRunningInCloudFn IsRunningInCloud;
|
|
gfnIsRunningInCloudSecureFn IsRunningInCloudSecure;
|
|
gfnCloudCheckFn CloudCheck;
|
|
gfnRegisterCallbackFn RegisterExitCallback;
|
|
gfnRegisterCallbackFn RegisterSaveCallback;
|
|
gfnRegisterCallbackFn RegisterSessionInitCallback;
|
|
gfnRegisterCallbackFn RegisterPauseCallback;
|
|
gfnRegisterCallbackFn RegisterInstallCallback;
|
|
gfnIsTitleAvailableFn IsTitleAvailable;
|
|
gfnGetTitlesAvailableFn GetTitlesAvailable;
|
|
gfnSetupTitleFn SetupTitle;
|
|
gfnTitleExitedFn TitleExited;
|
|
gfnGetClientIpFn GetClientIp;
|
|
gfnGetClientLanguageCodeFn GetClientLanguageCode;
|
|
gfnGetClientCountryCodeFn GetClientCountryCode;
|
|
gfnGetPartnerDataFn GetPartnerData;
|
|
gfnGetPartnerSecureDataFn GetPartnerSecureData;
|
|
gfnFreeFn Free;
|
|
gfnAppReadyFn AppReady;
|
|
gfnSetActionZoneFn SetActionZone;
|
|
gfnSendMessageFn SendMessage;
|
|
|
|
gfnGetClientInfoFn GetClientInfo;
|
|
gfnRegisterCallbackFn RegisterClientInfoCallback;
|
|
gfnRegisteCallbackFnWithUIntParam RegisterNetworkStatusCallback;
|
|
gfnRegisterCallbackFn RegisterMessageCallback;
|
|
gfnOpenURLOnClientFn OpenURLOnClient;
|
|
|
|
gfnGetSessionInfoFn GetSessionInfo;
|
|
} GfnSdkCloudLibrary;
|
|
GfnSdkCloudLibrary* g_pCloudLibrary = NULL;
|
|
GfnRuntimeError g_cloudLibraryStatus = gfnAPINotInit;
|
|
|
|
inline bool GfnUtf8ToWide(const char* in, wchar_t* out, int outSize)
|
|
{
|
|
#ifdef _WIN32
|
|
int result = MultiByteToWideChar(CP_UTF8, 0, in, -1, NULL, 0);
|
|
if (result <= 0 || outSize < result)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
result = MultiByteToWideChar(CP_UTF8, 0, in, -1, out, outSize);
|
|
if (result <= 0)
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
#elif __linux__
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
inline bool GfnWideToUtf8(const wchar_t* in, char* out, int outSize)
|
|
{
|
|
#ifdef _WIN32
|
|
int length = WideCharToMultiByte(CP_UTF8, 0, in, -1, NULL, 0, NULL, NULL);
|
|
if (length <= 0 || outSize < length)
|
|
{
|
|
return false;
|
|
}
|
|
length = WideCharToMultiByte(CP_UTF8, 0, in, -1, out, outSize, NULL, NULL);
|
|
if (length <= 0)
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
#elif __linux__
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
static void gfnFreeLibrary(void* library)
|
|
{
|
|
#ifdef _WIN32
|
|
FreeLibrary((HMODULE)library);
|
|
#elif __linux__
|
|
dlclose(library);
|
|
#endif
|
|
}
|
|
|
|
static void gfnFreeCloudLibrary(GfnSdkCloudLibrary* pCloudLibrary)
|
|
{
|
|
if (pCloudLibrary != NULL)
|
|
{
|
|
if (pCloudLibrary->handle)
|
|
{
|
|
gfnFreeLibrary(pCloudLibrary->handle);
|
|
}
|
|
free(pCloudLibrary);
|
|
}
|
|
}
|
|
|
|
static GfnRuntimeError gfnTranslateCloudStatus(int status)
|
|
{
|
|
return (GfnRuntimeError)status;
|
|
}
|
|
|
|
GfnRuntimeError gfnShutDownCloudSdk(void)
|
|
{
|
|
if (g_pCloudLibrary != NULL)
|
|
{
|
|
if (g_pCloudLibrary->ShutdownRuntimeSdk != NULL)
|
|
{
|
|
g_pCloudLibrary->ShutdownRuntimeSdk();
|
|
}
|
|
|
|
gfnFreeCloudLibrary(g_pCloudLibrary);
|
|
g_pCloudLibrary = NULL;
|
|
g_cloudLibraryStatus = gfnAPINotInit;
|
|
}
|
|
|
|
return gfnSuccess;
|
|
}
|
|
|
|
bool gfnPathExists(const CHAR_TYPE* path)
|
|
{
|
|
#ifdef _WIN32
|
|
return (PathFileExistsW(path) != FALSE);
|
|
#elif __linux__
|
|
struct stat buffer;
|
|
return (stat(path, &buffer) == 0);
|
|
#endif
|
|
}
|
|
|
|
const CHAR_TYPE* gfnGetFilenameFromPath(const CHAR_TYPE* path)
|
|
{
|
|
#ifdef _WIN32
|
|
const CHAR_TYPE *lastBackSepPos = wcsrchr(path, L'/');
|
|
const CHAR_TYPE *lastForeSepPos = wcsrchr(path, L'\\');
|
|
#elif __linux__
|
|
const CHAR_TYPE *lastBackSepPos = strrchr(path, '/');
|
|
const CHAR_TYPE *lastForeSepPos = strrchr(path, '\\');
|
|
#endif
|
|
|
|
const CHAR_TYPE* lastSepPos = (lastBackSepPos != NULL && lastBackSepPos > lastForeSepPos) ? lastBackSepPos : lastForeSepPos;
|
|
|
|
if (lastSepPos == NULL)
|
|
{
|
|
return path;
|
|
}
|
|
else
|
|
{
|
|
return lastSepPos + 1;
|
|
}
|
|
}
|
|
|
|
// This is case sensitive for POSIX, insensitive for WIN32
|
|
bool gfnPathEqual(const CHAR_TYPE* path1, const CHAR_TYPE* path2)
|
|
{
|
|
#ifdef _WIN32
|
|
return _wcsicmp(path1, path2) == 0;
|
|
#elif __linux__
|
|
return strcmp(path1, path2) == 0;
|
|
#endif
|
|
}
|
|
|
|
void* gfnLoadLibrary(const CHAR_TYPE* path)
|
|
{
|
|
// For security reasons, it is preferred to check the digital signature before loading the DLL.
|
|
// Such code is not provided here to reduce code complexity and library size, and in favor of
|
|
// any internal libraries built for this purpose.
|
|
#ifdef _WIN32
|
|
# ifdef _DEBUG
|
|
return LoadLibraryW(path);
|
|
# else
|
|
return gfnSecureLoadCloudLibraryW(path, 0);
|
|
# endif
|
|
#elif __linux__
|
|
return dlopen(path, RTLD_LAZY);
|
|
#endif
|
|
}
|
|
|
|
void* gfnGetSymbol(void* library, const char* name)
|
|
{
|
|
#ifdef _WIN32
|
|
return (void*)GetProcAddress((HMODULE)library, name);
|
|
#elif __linux__
|
|
return dlsym(library, name);
|
|
#endif
|
|
}
|
|
|
|
GfnRuntimeError gfnGetDefaultClientLibraryPath(CHAR_TYPE* path)
|
|
{
|
|
#ifdef _WIN32
|
|
wchar_t* filename = (wchar_t*)malloc(sizeof(wchar_t) * PLATFORM_MAX_PATH);
|
|
DWORD pathSize = GetModuleFileNameW(NULL, filename, PLATFORM_MAX_PATH);
|
|
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
free(filename);
|
|
filename = (wchar_t*)malloc(sizeof(wchar_t) * (pathSize + 1)); //Room for \0
|
|
if (!filename)
|
|
{
|
|
return gfnInternalError;
|
|
}
|
|
pathSize = GetModuleFileNameW(NULL, filename, pathSize + 1);
|
|
}
|
|
|
|
wchar_t* lastSepPos = wcsrchr(filename, L'\\');
|
|
size_t pathLength = lastSepPos - filename + 1;
|
|
size_t dllPathLength = pathLength + 18; // "GfnRuntimeSdk.dll\0"
|
|
|
|
if (!path)
|
|
{
|
|
free(filename);
|
|
return gfnInternalError;
|
|
}
|
|
wcsncpy_s(path, dllPathLength, filename, _TRUNCATE);
|
|
|
|
path[pathLength] = L'\0';
|
|
free(filename);
|
|
wcsncat_s(path, dllPathLength, L"GfnRuntimeSdk.dll", 17);
|
|
#elif __linux__
|
|
char buffer[PLATFORM_MAX_PATH];
|
|
ssize_t len;
|
|
|
|
// Get the absolute path of the executable
|
|
len = readlink("/proc/self/exe", buffer, sizeof(buffer) - 1);
|
|
|
|
// Check for errors
|
|
if (len == -1) {
|
|
return gfnInternalError;
|
|
}
|
|
|
|
// Null-terminate the string
|
|
buffer[len] = '\0';
|
|
|
|
// Get the directory path, excluding the executable name
|
|
char* directory = dirname(buffer);
|
|
strncpy(path, directory, PLATFORM_MAX_PATH);
|
|
|
|
// Append "GfnRuntimeSdk.so" to the directory path
|
|
if (strlen(path) + strlen("/" GFN_CLIENT_SHARED_LIBRARY) + 1 > PLATFORM_MAX_PATH) {
|
|
GFN_SDK_LOG("ERROR: Could not get default client library path name: Path too long");
|
|
return gfnInternalError;
|
|
}
|
|
|
|
strcat(path, "/GfnRuntimeSdk.so");
|
|
#endif
|
|
return gfnSuccess;
|
|
}
|
|
|
|
// Places the null terminator where the path ends
|
|
GfnRuntimeError gfnStripFilename(char* path)
|
|
{
|
|
char *lastBackSepPos = strrchr(path, '/');
|
|
char *lastForeSepPos = strrchr(path, '\\');
|
|
char* lastSepPos = (lastBackSepPos != NULL && lastBackSepPos > lastForeSepPos) ? lastBackSepPos : lastForeSepPos;
|
|
if (lastSepPos)
|
|
{
|
|
*lastSepPos = '\0';
|
|
}
|
|
else
|
|
{
|
|
return gfnInvalidParameter;
|
|
}
|
|
return gfnSuccess;
|
|
}
|
|
|
|
GfnRuntimeError gfnLoadCloudLibrary(GfnSdkCloudLibrary** ppCloudLibrary)
|
|
{
|
|
// If we've already attempted to load this, return the previous results and library
|
|
if (g_cloudLibraryStatus != gfnAPINotInit)
|
|
{
|
|
*ppCloudLibrary = g_pCloudLibrary;
|
|
return g_cloudLibraryStatus;
|
|
}
|
|
|
|
*ppCloudLibrary = NULL;
|
|
|
|
#ifdef _WIN32
|
|
if (SHGetSpecialFolderPathW(NULL, g_cloudLibraryPath, CSIDL_PROGRAM_FILES, false) == TRUE)
|
|
{
|
|
if (wcscat_s(g_cloudLibraryPath, PLATFORM_MAX_PATH, GFN_DLL_SUBPATH) != 0)
|
|
{
|
|
GFN_SDK_LOG("FAIL: Unable to concatenate path to Runtime SDK binaries");
|
|
return gfnInitFailure;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
GFN_SDK_LOG("FAIL: Unable to get path to Runtime SDK binaries");
|
|
return gfnInitFailure;
|
|
}
|
|
#endif // _WIN32
|
|
|
|
if (!gfnPathExists(g_cloudLibraryPath))
|
|
{
|
|
GFN_SDK_LOG("SUCCESS: Cloud library does not exist, this is running on the user client");
|
|
return gfnCloudLibraryNotFound;
|
|
}
|
|
|
|
void* library = gfnLoadLibrary(g_cloudLibraryPath);
|
|
if (!library)
|
|
{
|
|
#ifdef _WIN32
|
|
DWORD lastError = GetLastError();
|
|
if (lastError == CRYPT_E_NO_MATCH)
|
|
{
|
|
GFN_SDK_LOG("ERROR: GFN library failed to load due to invalid signature");
|
|
return gfnBinarySignatureInvalid;
|
|
}
|
|
else
|
|
{
|
|
GFN_SDK_LOG("ERROR: GFN library is present but unable to be loaded! LastError=0x%08X", lastError);
|
|
return gfnInitFailure;
|
|
}
|
|
#elif __linux__
|
|
GFN_SDK_LOG("GFN library is present but unable to be loaded! dlerror=%s", dlerror());
|
|
return gfnInitFailure;
|
|
#endif
|
|
}
|
|
|
|
GfnSdkCloudLibrary* pCloudLibrary = (GfnSdkCloudLibrary*)malloc(sizeof(GfnSdkCloudLibrary));
|
|
if (pCloudLibrary == NULL)
|
|
{
|
|
GFN_SDK_LOG("ERROR: Unable to allocate memory to hold GFN function pointers");
|
|
gfnFreeLibrary(library);
|
|
return gfnUnableToAllocateMemory;
|
|
}
|
|
|
|
pCloudLibrary->handle = library;
|
|
// Old Initialization method. Deprecate when all libraries have updated to 1.7.1 or greater.
|
|
pCloudLibrary->InitializeRuntimeSdk = (gfnCloudInitializeRuntimeSdkFn)gfnGetSymbol(pCloudLibrary->handle, "gfnInitializeRuntimeSdk2");
|
|
pCloudLibrary->InitializeRuntimeSdkV3 = (gfnCloudInitializeRuntimeSdkV3Fn)gfnGetSymbol(pCloudLibrary->handle, "gfnInitializeRuntimeSdk3");
|
|
pCloudLibrary->ShutdownRuntimeSdk = (gfnCloudShutdownRuntimeSdkFn)gfnGetSymbol(pCloudLibrary->handle, "gfnShutdownRuntimeSdk2");
|
|
pCloudLibrary->IsInitialized = (gfnIsInitializedFn)gfnGetSymbol(pCloudLibrary->handle, "gfnIsInitialized");
|
|
pCloudLibrary->IsRunningInCloud = (gfnIsRunningInCloudFn)gfnGetSymbol(pCloudLibrary->handle, "gfnIsRunningInCloud");
|
|
pCloudLibrary->IsRunningInCloudSecure = (gfnIsRunningInCloudSecureFn)gfnGetSymbol(pCloudLibrary->handle, "gfnIsRunningInCloudSecure");
|
|
pCloudLibrary->CloudCheck = (gfnCloudCheckFn)gfnGetSymbol(pCloudLibrary->handle, "gfnCloudCheck");
|
|
pCloudLibrary->IsTitleAvailable = (gfnIsTitleAvailableFn)gfnGetSymbol(pCloudLibrary->handle, "gfnIsTitleAvailable");
|
|
pCloudLibrary->GetTitlesAvailable = (gfnGetTitlesAvailableFn)gfnGetSymbol(pCloudLibrary->handle, "gfnGetTitlesAvailable");
|
|
pCloudLibrary->SetupTitle = (gfnSetupTitleFn)gfnGetSymbol(pCloudLibrary->handle, "gfnSetupTitle");
|
|
pCloudLibrary->TitleExited = (gfnTitleExitedFn)gfnGetSymbol(pCloudLibrary->handle, "gfnTitleExited");
|
|
pCloudLibrary->GetClientIp = (gfnGetClientIpFn)gfnGetSymbol(pCloudLibrary->handle, "gfnGetClientIp");
|
|
pCloudLibrary->GetClientLanguageCode = (gfnGetClientLanguageCodeFn)gfnGetSymbol(pCloudLibrary->handle, "gfnGetClientLanguageCode");
|
|
pCloudLibrary->GetClientCountryCode = (gfnGetClientCountryCodeFn)gfnGetSymbol(pCloudLibrary->handle, "gfnGetClientCountryCode");
|
|
pCloudLibrary->GetPartnerData = (gfnGetPartnerDataFn)gfnGetSymbol(pCloudLibrary->handle, "gfnGetPartnerData");
|
|
pCloudLibrary->GetPartnerSecureData = (gfnGetPartnerSecureDataFn)gfnGetSymbol(pCloudLibrary->handle, "gfnGetPartnerSecureData");
|
|
pCloudLibrary->Free = (gfnFreeFn)gfnGetSymbol(pCloudLibrary->handle, "gfnFree");
|
|
pCloudLibrary->AppReady = (gfnAppReadyFn)gfnGetSymbol(pCloudLibrary->handle, "gfnAppReady");
|
|
pCloudLibrary->SetActionZone = (gfnSetActionZoneFn)gfnGetSymbol(pCloudLibrary->handle, "gfnSetActionZone");
|
|
pCloudLibrary->SendMessage = (gfnSendMessageFn)gfnGetSymbol(pCloudLibrary->handle, "gfnSendCustomMessageToClient");
|
|
pCloudLibrary->RegisterExitCallback = (gfnRegisterCallbackFn)gfnGetSymbol(pCloudLibrary->handle, "gfnRegisterExitCallback");
|
|
pCloudLibrary->RegisterPauseCallback = (gfnRegisterCallbackFn)gfnGetSymbol(pCloudLibrary->handle, "gfnRegisterPauseCallback");
|
|
pCloudLibrary->RegisterInstallCallback = (gfnRegisterCallbackFn)gfnGetSymbol(pCloudLibrary->handle, "gfnRegisterInstallCallback");
|
|
pCloudLibrary->RegisterSaveCallback = (gfnRegisterCallbackFn)gfnGetSymbol(pCloudLibrary->handle, "gfnRegisterSaveCallback");
|
|
pCloudLibrary->RegisterSessionInitCallback = (gfnRegisterCallbackFn)gfnGetSymbol(pCloudLibrary->handle, "gfnRegisterSessionInitCallback");
|
|
|
|
pCloudLibrary->GetClientInfo = (gfnGetClientInfoFn)gfnGetSymbol(pCloudLibrary->handle, "gfnGetClientInfo");
|
|
pCloudLibrary->RegisterClientInfoCallback = (gfnRegisterCallbackFn)gfnGetSymbol(pCloudLibrary->handle, "gfnRegisterClientInfoCallback");
|
|
pCloudLibrary->RegisterNetworkStatusCallback = (gfnRegisteCallbackFnWithUIntParam)gfnGetSymbol(pCloudLibrary->handle, "gfnRegisterNetworkStatusCallback");
|
|
pCloudLibrary->RegisterMessageCallback = (gfnRegisterCallbackFn)gfnGetSymbol(pCloudLibrary->handle, "gfnRegisterCustomMessageCallback");
|
|
pCloudLibrary->OpenURLOnClient = (gfnOpenURLOnClientFn)gfnGetSymbol(pCloudLibrary->handle, "gfnOpenURLOnClient");
|
|
pCloudLibrary->GetSessionInfo = (gfnGetSessionInfoFn)gfnGetSymbol(pCloudLibrary->handle, "gfnGetSessionInfo");
|
|
|
|
GFN_SDK_LOG("Successfully loaded cloud libary");
|
|
|
|
if (pCloudLibrary->InitializeRuntimeSdk == NULL && pCloudLibrary->InitializeRuntimeSdkV3 == NULL)
|
|
{
|
|
GFN_SDK_LOG("Unable to find initialize function pointer");
|
|
gfnFreeCloudLibrary(pCloudLibrary);
|
|
return gfnAPINotFound;
|
|
}
|
|
|
|
*ppCloudLibrary = pCloudLibrary;
|
|
|
|
return gfnSuccess;
|
|
}
|
|
|
|
GfnRuntimeError gfnInitializeCloudSdk(void)
|
|
{
|
|
// Already initialized, no need to re-initialize
|
|
if (g_pCloudLibrary != NULL)
|
|
{
|
|
return g_cloudLibraryStatus;
|
|
}
|
|
|
|
g_cloudLibraryStatus = gfnLoadCloudLibrary(&g_pCloudLibrary);
|
|
if (g_cloudLibraryStatus != gfnSuccess)
|
|
{
|
|
return g_cloudLibraryStatus;
|
|
}
|
|
|
|
if (g_pCloudLibrary->InitializeRuntimeSdkV3)
|
|
{
|
|
g_cloudLibraryStatus = g_pCloudLibrary->InitializeRuntimeSdkV3(NVGFNSDK_VERSION_STR);
|
|
}
|
|
// Old Initialization method. Deprecate when all libraries have updated to 1.7.1 or greater.
|
|
else if (g_pCloudLibrary->InitializeRuntimeSdk)
|
|
{
|
|
g_cloudLibraryStatus = g_pCloudLibrary->InitializeRuntimeSdk((float)(NVGFNSDK_VERSION_SHORT));
|
|
}
|
|
if (GFNSDK_FAILED(g_cloudLibraryStatus))
|
|
{
|
|
GFN_SDK_LOG("Call to cloud InitializeRuntimeSdk failed: %d", g_cloudLibraryStatus);
|
|
// If init fails, we shouldn't force the host application to hold a loaded reference to the cloud DLL.
|
|
// Instead we will unload to make sure SDK is in a clean state in case the application tried to call
|
|
// the Initialize API again.
|
|
gfnFreeCloudLibrary(g_pCloudLibrary);
|
|
g_pCloudLibrary = NULL;
|
|
g_cloudLibraryStatus = gfnAPINotInit;
|
|
}
|
|
return g_cloudLibraryStatus;
|
|
}
|
|
|
|
typedef struct _gfnUserContextCallbackWrapper
|
|
{
|
|
void* fnCallback;
|
|
void* pOrigUserContext;
|
|
} _gfnUserContextCallbackWrapper;
|
|
|
|
enum IsCloud{IsCloud_Unknown,IsCloud_Yes,IsCloud_No};
|
|
static enum IsCloud g_isCloud = IsCloud_Unknown;
|
|
|
|
#define CHECK_NULL_PARAM(param) \
|
|
if (!param) \
|
|
{ \
|
|
return gfnInvalidParameter; \
|
|
}
|
|
#define CHECK_CLOUD_ENVIRONMENT_IMPL(bUseCache) \
|
|
if(g_isCloud == IsCloud_Unknown || !bUseCache) \
|
|
{ \
|
|
if (!g_pCloudLibrary && !g_gfnSdkModule) \
|
|
{ \
|
|
return gfnAPINotInit; \
|
|
} \
|
|
if (!g_pCloudLibrary || !g_pCloudLibrary->IsRunningInCloud) \
|
|
{ \
|
|
GFN_SDK_LOG("Cannot call cloud function: Wrong environment"); \
|
|
return gfnCallWrongEnvironment; \
|
|
} \
|
|
g_isCloud = ((bool)g_pCloudLibrary->IsRunningInCloud()) ? IsCloud_Yes : IsCloud_No; \
|
|
} \
|
|
if (g_isCloud == IsCloud_No) \
|
|
{ \
|
|
GFN_SDK_LOG("Cannot call cloud function: Wrong environment"); \
|
|
return gfnCallWrongEnvironment; \
|
|
}
|
|
#define CHECK_CLOUD_ENVIRONMENT() CHECK_CLOUD_ENVIRONMENT_IMPL(true)
|
|
#define CHECK_CLOUD_API_AVAILABLE(Fn) \
|
|
if (g_pCloudLibrary->Fn == NULL) \
|
|
{ \
|
|
GFN_SDK_LOG("Cannot call cloud function %s: API not found", #Fn); \
|
|
return gfnAPINotFound; \
|
|
}
|
|
#define DELEGATE_TO_CLOUD_LIBRARY(Fn, ...) \
|
|
CHECK_CLOUD_API_AVAILABLE(Fn); \
|
|
return gfnTranslateCloudStatus(g_pCloudLibrary->Fn(__VA_ARGS__));
|
|
|
|
GfnRuntimeError GfnInitializeSdk(GfnDisplayLanguage language)
|
|
{
|
|
// If "client" SDK is already initialized, then we're good to go.
|
|
// "server" SDK may or may not be initialized depending on mode.
|
|
GfnRuntimeError clientStatus = gfnSuccess;
|
|
|
|
if (!g_LoggingInitialized)
|
|
{
|
|
GFN_SDK_INIT_LOGGING();
|
|
g_LoggingInitialized = true;
|
|
}
|
|
|
|
|
|
if (g_gfnSdkModule != NULL)
|
|
{
|
|
GFN_SDK_LOG("Client library already initialized, no need to initialize again");
|
|
}
|
|
else
|
|
{
|
|
CHAR_TYPE* filename = (CHAR_TYPE*)malloc(sizeof(CHAR_TYPE) * PLATFORM_MAX_PATH);
|
|
if (!filename)
|
|
{
|
|
return gfnInternalError;
|
|
}
|
|
|
|
GfnRuntimeError err = gfnGetDefaultClientLibraryPath(filename);
|
|
if (GFNSDK_FAILED(err))
|
|
{
|
|
free(filename);
|
|
return err;
|
|
}
|
|
|
|
clientStatus = GfnInitializeSdkFromPathDefault(language, filename);
|
|
free(filename);
|
|
}
|
|
|
|
if (GFNSDK_FAILED(clientStatus))
|
|
{
|
|
GFN_SDK_LOG("Initialization failed: %d", clientStatus);
|
|
GfnShutdownSdk();
|
|
}
|
|
|
|
return clientStatus;
|
|
}
|
|
|
|
// On WIN32, this accepts a wide char string.
|
|
// On all other platforms, this accepts a UTF-8 string.
|
|
GfnRuntimeError GfnInitializeSdkFromPathDefault(GfnDisplayLanguage language, const CHAR_TYPE* sdkLibraryPath)
|
|
{
|
|
// If "client" library is already initialized, then we're good to go.
|
|
if (g_gfnSdkModule != NULL)
|
|
{
|
|
GFN_SDK_LOG("Client library already initialized, no need to initialize again");
|
|
return gfnSuccess;
|
|
}
|
|
|
|
GfnRuntimeError clientStatus = gfnSuccess;
|
|
if (!g_LoggingInitialized)
|
|
{
|
|
GFN_SDK_INIT_LOGGING();
|
|
g_LoggingInitialized = true;
|
|
}
|
|
|
|
const CHAR_TYPE* filename = gfnGetFilenameFromPath(sdkLibraryPath);
|
|
if (!filename || !gfnPathEqual(filename, GFN_CLIENT_SHARED_LIBRARY))
|
|
{
|
|
GFN_SDK_LOG("Invalid SDK library name");
|
|
return gfnInvalidParameter;
|
|
}
|
|
|
|
if (!gfnPathExists(sdkLibraryPath))
|
|
{
|
|
clientStatus = gfnClientLibraryNotFound;
|
|
}
|
|
else
|
|
{
|
|
|
|
GFN_SDK_LOG("Initializing the GfnSdk");
|
|
// For security reasons, it is preferred to check the digital signature before loading the DLL.
|
|
// Such code is not provided here to reduce code complexity and library size, and in favor of
|
|
// any internal libraries built for this purpose.
|
|
g_gfnSdkModule = (HMODULE)gfnLoadLibrary(sdkLibraryPath);
|
|
if (g_gfnSdkModule == NULL)
|
|
{
|
|
clientStatus = gfnClientLibraryNotFound;
|
|
#ifdef _WIN32
|
|
DWORD lastError = GetLastError();
|
|
if (lastError == CRYPT_E_NO_MATCH)
|
|
{
|
|
GFN_SDK_LOG("ERROR: GFN library failed to load due to invalid signature");
|
|
clientStatus = gfnBinarySignatureInvalid;
|
|
}
|
|
#elif __linux__
|
|
GFN_SDK_LOG("GFN client library is present but unable to be loaded! dlerror=%s", dlerror());
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
gfnInitializeRuntimeSdkFn fnGfnInitializeRuntimeSdk = (gfnInitializeRuntimeSdkFn)gfnGetSymbol(g_gfnSdkModule, "gfnInitializeRuntimeSdk");
|
|
if (fnGfnInitializeRuntimeSdk == NULL)
|
|
{
|
|
clientStatus = gfnAPINotFound;
|
|
}
|
|
else
|
|
{
|
|
clientStatus = (fnGfnInitializeRuntimeSdk)(language);
|
|
}
|
|
}
|
|
}
|
|
// The gfnClientLibraryNotFound error means client library was not present.
|
|
// This is allowed in the GFN cloud environment for robustness reasons as all API
|
|
// calls are deferred to the cloud library, although this is not a recommended use case.
|
|
// Any other error, including presence of a client library that couldn't be validated, is fatal.
|
|
if (GFNSDK_FAILED(clientStatus) && clientStatus != gfnClientLibraryNotFound)
|
|
{
|
|
GFN_SDK_LOG("Client SDK library init failed: %d", clientStatus);
|
|
GfnShutdownSdk();
|
|
return clientStatus;
|
|
}
|
|
|
|
// With the client library loaded attempt to load the cloud Sdk library if available (inside GFN only) in order to use it
|
|
// directly for cloud API calls.
|
|
GfnRuntimeError cloudStatus = gfnInitializeCloudSdk();
|
|
// gfnCloudLibraryNotFound is allowed, indicating that this is not running in a cloud environment.
|
|
// All other errors are fatal.
|
|
if (GFNSDK_FAILED(cloudStatus) && (cloudStatus != gfnCloudLibraryNotFound))
|
|
{
|
|
GFN_SDK_LOG("Cloud library init failed: %d", cloudStatus);
|
|
GfnShutdownSdk();
|
|
return cloudStatus;
|
|
}
|
|
|
|
// If we could find either SDK library, then this is a fatal condition.
|
|
if (clientStatus == gfnClientLibraryNotFound && cloudStatus == gfnCloudLibraryNotFound)
|
|
{
|
|
GFN_SDK_LOG("Failed to find any valid SDK libraries");
|
|
return clientStatus;
|
|
}
|
|
|
|
GFN_SDK_LOG("Initialization successful");
|
|
|
|
if (GFNSDK_SUCCEEDED(cloudStatus) && clientStatus == gfnClientLibraryNotFound)
|
|
{
|
|
return gfnInitSuccessCloudOnly;
|
|
}
|
|
if (GFNSDK_SUCCEEDED(clientStatus) && cloudStatus == gfnCloudLibraryNotFound)
|
|
{
|
|
return gfnInitSuccessClientOnly;
|
|
}
|
|
|
|
return gfnSuccess;
|
|
}
|
|
|
|
GfnRuntimeError GfnInitializeSdkFromPathA(GfnDisplayLanguage language, const char* utf8SdkLibraryPath)
|
|
{
|
|
if (utf8SdkLibraryPath == NULL)
|
|
{
|
|
GFN_SDK_LOG("Invalid SDK library path");
|
|
return gfnInvalidParameter;
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
// On windows, first convert to Unicode-16, then call the implementation.
|
|
size_t libPathSize = strlen(utf8SdkLibraryPath) + 1;
|
|
wchar_t* wSdkLibraryPath = (wchar_t*)malloc(libPathSize * sizeof(wchar_t));
|
|
if (!wSdkLibraryPath)
|
|
{
|
|
GFN_SDK_LOG("Failed to allocate for SDK library path");
|
|
return gfnUnableToAllocateMemory;
|
|
}
|
|
int outSize = (int)libPathSize * sizeof(wchar_t);
|
|
if (!GfnUtf8ToWide(utf8SdkLibraryPath, wSdkLibraryPath, outSize))
|
|
{
|
|
GFN_SDK_LOG("Failed to convert SDK library path");
|
|
free(wSdkLibraryPath);
|
|
return gfnInternalError;
|
|
}
|
|
|
|
GfnError status = GfnInitializeSdkFromPathDefault(language, wSdkLibraryPath);
|
|
free(wSdkLibraryPath);
|
|
return status;
|
|
#elif __linux__
|
|
// On linux, this is the default encoding. Directly call the implementation.
|
|
return GfnInitializeSdkFromPathDefault(language, utf8SdkLibraryPath);
|
|
#endif
|
|
}
|
|
|
|
GfnRuntimeError GfnInitializeSdkFromPathW(GfnDisplayLanguage language, const wchar_t* wSdkLibraryPath)
|
|
{
|
|
if (wSdkLibraryPath == NULL)
|
|
{
|
|
GFN_SDK_LOG("Invalid SDK library path");
|
|
return gfnInvalidParameter;
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
// On windows, this is the default encoding. Directly call the implementation.
|
|
return GfnInitializeSdkFromPathDefault(language, wSdkLibraryPath);
|
|
#elif __linux__
|
|
GFN_SDK_LOG("GfnInitializeSdkFromPathW is unsupported on linux");
|
|
return gfnInvalidParameter;
|
|
#endif
|
|
}
|
|
|
|
GfnRuntimeError GfnShutdownSdk(void)
|
|
{
|
|
gfnShutDownCloudSdk();
|
|
|
|
if (g_gfnSdkModule == NULL)
|
|
{
|
|
// Not initialized, no need to shutdown
|
|
return gfnSuccess;
|
|
}
|
|
|
|
gfnShutdownRuntimeSdkFn fnGfnShutdownRuntimeSdk = (gfnShutdownRuntimeSdkFn)gfnGetSymbol(g_gfnSdkModule, "gfnShutdownRuntimeSdk");
|
|
if (fnGfnShutdownRuntimeSdk == NULL)
|
|
{
|
|
return gfnAPINotFound;
|
|
}
|
|
|
|
fnGfnShutdownRuntimeSdk();
|
|
|
|
gfnFreeLibrary(g_gfnSdkModule);
|
|
g_gfnSdkModule = NULL;
|
|
|
|
GFN_SDK_DEINIT_LOGGING();
|
|
return gfnSuccess;
|
|
}
|
|
|
|
GfnRuntimeError GfnIsRunningInCloud(bool* runningInCloud)
|
|
{
|
|
CHECK_NULL_PARAM(runningInCloud);
|
|
*runningInCloud = false;
|
|
|
|
if (g_pCloudLibrary == NULL && g_gfnSdkModule == NULL)
|
|
{
|
|
return gfnAPINotInit;
|
|
}
|
|
|
|
if (g_pCloudLibrary == NULL)
|
|
{
|
|
GFN_SDK_LOG("No cloud library present, call succeeds");
|
|
return gfnSuccess;
|
|
}
|
|
|
|
if (g_pCloudLibrary->IsRunningInCloud == NULL)
|
|
{
|
|
GFN_SDK_LOG("API Not Found");
|
|
return gfnAPINotFound;
|
|
}
|
|
|
|
*runningInCloud = (bool)g_pCloudLibrary->IsRunningInCloud();
|
|
|
|
GFN_SDK_LOG("Success: %d", *runningInCloud);
|
|
return gfnSuccess;
|
|
}
|
|
|
|
GfnRuntimeError GfnIsRunningInCloudSecure(GfnIsRunningInCloudAssurance* assurance)
|
|
{
|
|
CHECK_NULL_PARAM(assurance);
|
|
*assurance = gfnNotCloud;
|
|
|
|
if (g_pCloudLibrary == NULL && g_gfnSdkModule == NULL)
|
|
{
|
|
return gfnAPINotInit;
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
if (wcslen(g_cloudLibraryPath) == 0)
|
|
#elif __linux__
|
|
if (strlen(g_cloudLibraryPath) == 0)
|
|
#endif
|
|
{
|
|
GFN_SDK_LOG("Cloud library path not defined, which denotes an API call without Initialize succeeding, treating as not in cloud");
|
|
return gfnSuccess;
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
if (gfnCheckLibraryGfnSignatureW(g_cloudLibraryPath) == FALSE)
|
|
{
|
|
GFN_SDK_LOG("Cloud library path does not have valid GFN signing, treating as not in cloud");
|
|
return gfnSuccess;
|
|
}
|
|
#endif
|
|
|
|
if (g_pCloudLibrary == NULL)
|
|
{
|
|
GFN_SDK_LOG("No cloud library present, call succeeds");
|
|
return gfnSuccess;
|
|
}
|
|
|
|
if (g_pCloudLibrary->IsRunningInCloudSecure == NULL)
|
|
{
|
|
GFN_SDK_LOG("API Not Found");
|
|
return gfnAPINotFound;
|
|
}
|
|
|
|
GfnRuntimeError status = gfnTranslateCloudStatus(g_pCloudLibrary->IsRunningInCloudSecure(assurance));
|
|
GFN_SDK_LOG("status=%d assurance=%d", status, *assurance);
|
|
|
|
return status;
|
|
}
|
|
|
|
GfnRuntimeError GfnCloudCheck(const GfnCloudCheckChallenge* challenge, GfnCloudCheckResponse* response, bool* isCloudEnvironment)
|
|
{
|
|
*isCloudEnvironment = false;
|
|
|
|
if (g_pCloudLibrary == NULL && g_gfnSdkModule == NULL)
|
|
{
|
|
return gfnAPINotInit;
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
if (wcslen(g_cloudLibraryPath) == 0)
|
|
#elif __linux__
|
|
if (strlen(g_cloudLibraryPath) == 0)
|
|
#endif
|
|
{
|
|
GFN_SDK_LOG("Cloud library path not defined, which denotes an API call without Initialize succeeding, treating as not in cloud");
|
|
return gfnSuccess;
|
|
}
|
|
|
|
if (g_pCloudLibrary == NULL)
|
|
{
|
|
GFN_SDK_LOG("No cloud library present, call succeeds");
|
|
return gfnSuccess;
|
|
}
|
|
|
|
if (g_pCloudLibrary->CloudCheck == NULL)
|
|
{
|
|
GFN_SDK_LOG("API Not Found");
|
|
return gfnAPINotFound;
|
|
}
|
|
|
|
GfnRuntimeError status = gfnTranslateCloudStatus(g_pCloudLibrary->CloudCheck(challenge, response, isCloudEnvironment));
|
|
GFN_SDK_LOG("status=%d isCloudEnvironment=%d", status, *isCloudEnvironment);
|
|
|
|
return status;
|
|
}
|
|
|
|
#define TESTME(lib, fn) lib->fn()
|
|
|
|
GfnRuntimeError GfnFree(const char** data)
|
|
{
|
|
CHECK_NULL_PARAM(data);
|
|
CHECK_CLOUD_ENVIRONMENT();
|
|
DELEGATE_TO_CLOUD_LIBRARY(Free, data);
|
|
}
|
|
|
|
GfnRuntimeError GfnGetClientIpV4(const char ** clientIp)
|
|
{
|
|
CHECK_NULL_PARAM(clientIp);
|
|
CHECK_CLOUD_ENVIRONMENT();
|
|
DELEGATE_TO_CLOUD_LIBRARY(GetClientIp, clientIp);
|
|
}
|
|
|
|
GfnRuntimeError GfnGetClientLanguageCode(const char** languageCode)
|
|
{
|
|
CHECK_NULL_PARAM(languageCode);
|
|
CHECK_CLOUD_ENVIRONMENT();
|
|
DELEGATE_TO_CLOUD_LIBRARY(GetClientLanguageCode, languageCode);
|
|
}
|
|
|
|
GfnRuntimeError GfnGetClientCountryCode(char* countryCode, unsigned int length)
|
|
{
|
|
CHECK_NULL_PARAM(countryCode);
|
|
CHECK_CLOUD_ENVIRONMENT();
|
|
DELEGATE_TO_CLOUD_LIBRARY(GetClientCountryCode, countryCode, length);
|
|
}
|
|
|
|
GfnRuntimeError GfnGetPartnerData(const char** partnerData)
|
|
{
|
|
CHECK_NULL_PARAM(partnerData);
|
|
CHECK_CLOUD_ENVIRONMENT();
|
|
DELEGATE_TO_CLOUD_LIBRARY(GetPartnerData, partnerData);
|
|
}
|
|
|
|
GfnRuntimeError GfnGetPartnerSecureData(const char** partnerSecureData)
|
|
{
|
|
CHECK_NULL_PARAM(partnerSecureData);
|
|
CHECK_CLOUD_ENVIRONMENT();
|
|
DELEGATE_TO_CLOUD_LIBRARY(GetPartnerSecureData, partnerSecureData);
|
|
}
|
|
|
|
GfnRuntimeError GfnIsTitleAvailable(const char* platformAppId, bool* isAvailable)
|
|
{
|
|
CHECK_NULL_PARAM(isAvailable);
|
|
*isAvailable = false;
|
|
|
|
CHECK_NULL_PARAM(platformAppId);
|
|
CHECK_CLOUD_ENVIRONMENT();
|
|
CHECK_CLOUD_API_AVAILABLE(IsTitleAvailable);
|
|
*isAvailable = (bool)g_pCloudLibrary->IsTitleAvailable(platformAppId);
|
|
|
|
return gfnSuccess;
|
|
}
|
|
|
|
GfnRuntimeError GfnGetTitlesAvailable(const char** platformAppIds)
|
|
{
|
|
CHECK_NULL_PARAM(platformAppIds);
|
|
CHECK_CLOUD_ENVIRONMENT();
|
|
DELEGATE_TO_CLOUD_LIBRARY(GetTitlesAvailable, platformAppIds);
|
|
}
|
|
|
|
|
|
GfnRuntimeError GfnGetClientInfo(GfnClientInfo* clientInfo)
|
|
{
|
|
GFN_SDK_LOG("Calling GfnGetClientInfo");
|
|
CHECK_NULL_PARAM(clientInfo);
|
|
CHECK_CLOUD_ENVIRONMENT();
|
|
DELEGATE_TO_CLOUD_LIBRARY(GetClientInfo, clientInfo);
|
|
}
|
|
|
|
static void GFN_CALLBACK _gfnClientInfoCallbackWrapper(int status, void* updateData, void* pData)
|
|
{
|
|
(void)status;
|
|
GFN_SDK_LOG("ClientInfo update received");
|
|
|
|
_gfnUserContextCallbackWrapper* pWrappedContext = (_gfnUserContextCallbackWrapper*)(pData);
|
|
if (pWrappedContext == NULL || pWrappedContext->fnCallback == NULL)
|
|
{
|
|
GFN_SDK_LOG("Wrapped context was null or had no callback. Ignoring");
|
|
return;
|
|
}
|
|
ClientInfoCallbackSig cb = (ClientInfoCallbackSig)(pWrappedContext->fnCallback);
|
|
if (cb == NULL)
|
|
{
|
|
GFN_SDK_LOG("Callback was NULL, ignoring");
|
|
return;
|
|
}
|
|
cb((GfnClientInfoUpdateData *)updateData, pWrappedContext->pOrigUserContext);
|
|
}
|
|
|
|
GfnRuntimeError GfnRegisterClientInfoCallback(ClientInfoCallbackSig clientInfoCallback, void* pUserContext)
|
|
{
|
|
CHECK_NULL_PARAM(clientInfoCallback);
|
|
CHECK_CLOUD_ENVIRONMENT();
|
|
GFN_SDK_LOG("Registering for ClientInfo updates");
|
|
_gfnUserContextCallbackWrapper* pWrappedContext = (_gfnUserContextCallbackWrapper*)malloc(sizeof(_gfnUserContextCallbackWrapper));
|
|
pWrappedContext->fnCallback = (void*)clientInfoCallback;
|
|
pWrappedContext->pOrigUserContext = pUserContext;
|
|
|
|
// Suppress CppCheck warning about memory leak. The cloud DLL takes ownership of the memory and will free on call to gfnShutdownSdk
|
|
// cppcheck-suppress memleak
|
|
DELEGATE_TO_CLOUD_LIBRARY(RegisterClientInfoCallback, &_gfnClientInfoCallbackWrapper, (void*)(pWrappedContext));
|
|
}
|
|
|
|
GfnRuntimeError GfnGetSessionInfo(GfnSessionInfo* sessionInfo)
|
|
{
|
|
GFN_SDK_LOG("Calling GfnGetSessionInfo");
|
|
CHECK_NULL_PARAM(sessionInfo);
|
|
CHECK_CLOUD_ENVIRONMENT();
|
|
DELEGATE_TO_CLOUD_LIBRARY(GetSessionInfo, sessionInfo);
|
|
}
|
|
|
|
|
|
static void GFN_CALLBACK _gfnNetworkStatusCallbackWrapper(int status, void* updateData, void* pData)
|
|
{
|
|
(void)status;
|
|
GFN_SDK_LOG("Network performance update received");
|
|
|
|
_gfnUserContextCallbackWrapper* pWrappedContext = (_gfnUserContextCallbackWrapper*)(pData);
|
|
if (pWrappedContext == NULL || pWrappedContext->fnCallback == NULL)
|
|
{
|
|
GFN_SDK_LOG("Wrapped context was null or had no callback. Ignoring");
|
|
return;
|
|
}
|
|
NetworkStatusCallbackSig cb = (NetworkStatusCallbackSig)(pWrappedContext->fnCallback);
|
|
if (cb == NULL)
|
|
{
|
|
GFN_SDK_LOG("Callback was NULL, ignoring");
|
|
return;
|
|
}
|
|
cb((GfnNetworkStatusUpdateData *)updateData, pWrappedContext->pOrigUserContext);
|
|
}
|
|
|
|
GfnRuntimeError GfnRegisterNetworkStatusCallback(NetworkStatusCallbackSig networkStatusCallback, unsigned int updateRateMs, void* pUserContext)
|
|
{
|
|
CHECK_NULL_PARAM(networkStatusCallback);
|
|
CHECK_CLOUD_ENVIRONMENT();
|
|
GFN_SDK_LOG("Registering for NetworkStatus updates");
|
|
_gfnUserContextCallbackWrapper* pWrappedContext = (_gfnUserContextCallbackWrapper*)malloc(sizeof(_gfnUserContextCallbackWrapper));
|
|
pWrappedContext->fnCallback = (void*)networkStatusCallback;
|
|
pWrappedContext->pOrigUserContext = pUserContext;
|
|
|
|
// Suppress CppCheck warning about memory leak. The cloud DLL takes ownership of the memory and will free on call to gfnShutdownSdk
|
|
// cppcheck-suppress memleak
|
|
DELEGATE_TO_CLOUD_LIBRARY(RegisterNetworkStatusCallback, &_gfnNetworkStatusCallbackWrapper, updateRateMs, (void*)(pWrappedContext));
|
|
}
|
|
|
|
GfnRuntimeError GfnRegisterStreamStatusCallback(StreamStatusCallbackSig streamStatusCallback, void* userContext)
|
|
{
|
|
if (g_gfnSdkModule == NULL)
|
|
{
|
|
return gfnAPINotInit;
|
|
}
|
|
|
|
gfnRegisterStreamStatusCallbackFn fnRegisterStreamStatusCallback = (gfnRegisterStreamStatusCallbackFn)gfnGetSymbol(g_gfnSdkModule, "gfnRegisterStreamStatusCallback");
|
|
|
|
if (fnRegisterStreamStatusCallback == NULL)
|
|
{
|
|
return gfnAPINotFound;
|
|
}
|
|
|
|
return fnRegisterStreamStatusCallback(streamStatusCallback, userContext);
|
|
}
|
|
|
|
GfnRuntimeError GfnStartStream(StartStreamInput * startStreamInput, StartStreamResponse* response)
|
|
{
|
|
if (g_gfnSdkModule == NULL)
|
|
{
|
|
return gfnAPINotInit;
|
|
}
|
|
|
|
gfnStartStreamFn fnGfnStartStream = (gfnStartStreamFn)gfnGetSymbol(g_gfnSdkModule, "gfnStartStream");
|
|
|
|
if (fnGfnStartStream == NULL)
|
|
{
|
|
return gfnAPINotFound;
|
|
}
|
|
|
|
return fnGfnStartStream(startStreamInput, response);
|
|
}
|
|
|
|
GfnRuntimeError GfnStartStreamAsync(const StartStreamInput* startStreamInput, StartStreamCallbackSig cb, void* context, unsigned int timeoutMs)
|
|
{
|
|
if (g_gfnSdkModule == NULL)
|
|
{
|
|
return gfnAPINotInit;
|
|
}
|
|
|
|
gfnStartStreamAsyncFn fnGfnStartStreamAsync = (gfnStartStreamAsyncFn)gfnGetSymbol(g_gfnSdkModule, "gfnStartStreamAsync");
|
|
|
|
if (fnGfnStartStreamAsync == NULL)
|
|
{
|
|
return gfnAPINotFound;
|
|
}
|
|
|
|
fnGfnStartStreamAsync(startStreamInput, cb, context, timeoutMs);
|
|
|
|
return gfnSuccess;
|
|
}
|
|
|
|
GfnRuntimeError GfnStopStream(void)
|
|
{
|
|
if (g_gfnSdkModule == NULL)
|
|
{
|
|
return gfnAPINotInit;
|
|
}
|
|
|
|
gfnStopStreamFn fnGfnStopStream = (gfnStopStreamFn)gfnGetSymbol(g_gfnSdkModule, "gfnStopStream");
|
|
|
|
if (fnGfnStopStream == NULL)
|
|
{
|
|
return gfnAPINotFound;
|
|
}
|
|
|
|
return fnGfnStopStream();
|
|
}
|
|
|
|
GfnRuntimeError GfnStopStreamAsync(StopStreamCallbackSig cb, void* context, unsigned int timeoutMs)
|
|
{
|
|
if (g_gfnSdkModule == NULL)
|
|
{
|
|
return gfnAPINotInit;
|
|
}
|
|
|
|
gfnStopStreamAsyncFn fnGfnStopStreamAsync = (gfnStopStreamAsyncFn)gfnGetSymbol(g_gfnSdkModule, "gfnStopStreamAsync");
|
|
|
|
if (fnGfnStopStreamAsync == NULL)
|
|
{
|
|
return gfnAPINotFound;
|
|
}
|
|
|
|
fnGfnStopStreamAsync(cb, context, timeoutMs);
|
|
|
|
return gfnSuccess;
|
|
}
|
|
|
|
GfnRuntimeError GfnSetupTitle(const char* platformAppId)
|
|
{
|
|
CHECK_NULL_PARAM(platformAppId);
|
|
CHECK_CLOUD_ENVIRONMENT();
|
|
DELEGATE_TO_CLOUD_LIBRARY(SetupTitle, platformAppId);
|
|
}
|
|
|
|
GfnRuntimeError GfnTitleExited(const char* platformId, const char* platformAppId)
|
|
{
|
|
CHECK_NULL_PARAM(platformId);
|
|
CHECK_NULL_PARAM(platformAppId);
|
|
CHECK_CLOUD_ENVIRONMENT();
|
|
DELEGATE_TO_CLOUD_LIBRARY(TitleExited, platformId, platformAppId);
|
|
}
|
|
|
|
GfnRuntimeError GfnAppReady(bool success, const char* status)
|
|
{
|
|
CHECK_CLOUD_ENVIRONMENT();
|
|
DELEGATE_TO_CLOUD_LIBRARY(AppReady, success, status);
|
|
}
|
|
|
|
GfnRuntimeError GfnSetActionZone(GfnActionType type, unsigned int id, GfnRect* zone)
|
|
{
|
|
CHECK_CLOUD_ENVIRONMENT();
|
|
DELEGATE_TO_CLOUD_LIBRARY(SetActionZone, type, id, zone);
|
|
}
|
|
|
|
GfnRuntimeError GfnSendMessage(const char* pchMessage, unsigned int length) {
|
|
if (g_pCloudLibrary != NULL && g_pCloudLibrary->SendMessage != NULL) \
|
|
{ \
|
|
DELEGATE_TO_CLOUD_LIBRARY(SendMessage, pchMessage, length); \
|
|
}
|
|
else
|
|
{
|
|
if (g_gfnSdkModule == NULL)
|
|
{
|
|
return gfnAPINotInit;
|
|
}
|
|
|
|
gfnSendMessageFn fnSendMessage = (gfnSendMessageFn)gfnGetSymbol(g_gfnSdkModule, "gfnSendMessage");
|
|
|
|
if (fnSendMessage == NULL)
|
|
{
|
|
return gfnAPINotFound;
|
|
}
|
|
|
|
return fnSendMessage(pchMessage, length);
|
|
}
|
|
}
|
|
|
|
GfnRuntimeError GfnOpenURLOnClient(const char* pchUrl) {
|
|
CHECK_CLOUD_ENVIRONMENT();
|
|
DELEGATE_TO_CLOUD_LIBRARY(OpenURLOnClient, pchUrl);
|
|
}
|
|
|
|
static void GFN_CALLBACK _gfnExitCallbackWrapper(int status, void* pUnused, void* pContext)
|
|
{
|
|
(void)status;
|
|
(void)pUnused;
|
|
_gfnUserContextCallbackWrapper* pWrappedContext = (_gfnUserContextCallbackWrapper*)(pContext);
|
|
if (pWrappedContext == NULL || pWrappedContext->fnCallback == NULL)
|
|
{
|
|
return;
|
|
}
|
|
ExitCallbackSig cb = (ExitCallbackSig)(pWrappedContext->fnCallback);
|
|
cb(pWrappedContext->pOrigUserContext);
|
|
}
|
|
|
|
GfnRuntimeError GfnRegisterExitCallback(ExitCallbackSig exitCallback, void* pUserContext)
|
|
{
|
|
CHECK_NULL_PARAM(exitCallback);
|
|
CHECK_CLOUD_ENVIRONMENT();
|
|
|
|
GFN_SDK_LOG("Registering for Exit Callback updates");
|
|
|
|
_gfnUserContextCallbackWrapper* pWrappedContext = (_gfnUserContextCallbackWrapper*)malloc(sizeof(_gfnUserContextCallbackWrapper));
|
|
pWrappedContext->fnCallback = (void*)exitCallback;
|
|
pWrappedContext->pOrigUserContext = pUserContext;
|
|
|
|
// Suppress CppCheck warning about memory leak. The cloud DLL takes ownership of the memory and will free on call to gfnShutdownSdk
|
|
// cppcheck-suppress memleak
|
|
DELEGATE_TO_CLOUD_LIBRARY(RegisterExitCallback, &_gfnExitCallbackWrapper, pWrappedContext);
|
|
}
|
|
|
|
static void GFN_CALLBACK _gfnPauseCallbackWrapper(int status, void* pUnused, void* pContext)
|
|
{
|
|
(void)status;
|
|
(void)pUnused;
|
|
_gfnUserContextCallbackWrapper* pWrappedContext = (_gfnUserContextCallbackWrapper*)(pContext);
|
|
if (pWrappedContext == NULL || pWrappedContext->fnCallback == NULL)
|
|
{
|
|
return;
|
|
}
|
|
PauseCallbackSig cb = (PauseCallbackSig)(pWrappedContext->fnCallback);
|
|
cb(pWrappedContext->pOrigUserContext);
|
|
}
|
|
|
|
GfnRuntimeError GfnRegisterPauseCallback(PauseCallbackSig pauseCallback, void* pUserContext)
|
|
{
|
|
CHECK_NULL_PARAM(pauseCallback);
|
|
CHECK_CLOUD_ENVIRONMENT();
|
|
|
|
GFN_SDK_LOG("Registering for Pause Callback updates");
|
|
|
|
_gfnUserContextCallbackWrapper* pWrappedContext = (_gfnUserContextCallbackWrapper*)malloc(sizeof(_gfnUserContextCallbackWrapper));
|
|
pWrappedContext->fnCallback = (void*)pauseCallback;
|
|
pWrappedContext->pOrigUserContext = pUserContext;
|
|
|
|
// Suppress CppCheck warning about memory leak. The cloud DLL takes ownership of the memory and will free on call to gfnShutdownSdk
|
|
// cppcheck-suppress memleak
|
|
DELEGATE_TO_CLOUD_LIBRARY(RegisterPauseCallback, &_gfnPauseCallbackWrapper, pWrappedContext);
|
|
}
|
|
|
|
static void GFN_CALLBACK _gfnInstallCallbackWrapper(int status, void* pTitleInstallationInformation, void* pContext)
|
|
{
|
|
(void)status;
|
|
_gfnUserContextCallbackWrapper* pWrappedContext = (_gfnUserContextCallbackWrapper*)(pContext);
|
|
if (pWrappedContext == NULL || pWrappedContext->fnCallback == NULL)
|
|
{
|
|
return;
|
|
}
|
|
InstallCallbackSig cb = (InstallCallbackSig)(pWrappedContext->fnCallback);
|
|
cb((TitleInstallationInformation*)pTitleInstallationInformation, pWrappedContext->pOrigUserContext);
|
|
}
|
|
|
|
GfnRuntimeError GfnRegisterInstallCallback(InstallCallbackSig installCallback, void* pUserContext)
|
|
{
|
|
CHECK_NULL_PARAM(installCallback);
|
|
CHECK_CLOUD_ENVIRONMENT();
|
|
|
|
GFN_SDK_LOG("Registering for Install Callback updates");
|
|
|
|
_gfnUserContextCallbackWrapper* pWrappedContext = (_gfnUserContextCallbackWrapper*)malloc(sizeof(_gfnUserContextCallbackWrapper));
|
|
pWrappedContext->fnCallback = (void*)installCallback;
|
|
pWrappedContext->pOrigUserContext = pUserContext;
|
|
|
|
// Suppress CppCheck warning about memory leak. The cloud DLL takes ownership of the memory and will free on call to gfnShutdownSdk
|
|
// cppcheck-suppress memleak
|
|
DELEGATE_TO_CLOUD_LIBRARY(RegisterInstallCallback, &_gfnInstallCallbackWrapper, pWrappedContext);
|
|
}
|
|
|
|
static void GFN_CALLBACK _gfnSaveCallbackWrapper(int status, void* pUnused, void* pContext)
|
|
{
|
|
(void)status;
|
|
(void)pUnused;
|
|
_gfnUserContextCallbackWrapper* pWrappedContext = (_gfnUserContextCallbackWrapper*)(pContext);
|
|
if (pWrappedContext == NULL || pWrappedContext->fnCallback == NULL)
|
|
{
|
|
return;
|
|
}
|
|
SaveCallbackSig cb = (SaveCallbackSig)(pWrappedContext->fnCallback);
|
|
cb(pWrappedContext->pOrigUserContext);
|
|
}
|
|
|
|
GfnRuntimeError GfnRegisterSaveCallback(SaveCallbackSig saveCallback, void* pUserContext)
|
|
{
|
|
CHECK_NULL_PARAM(saveCallback);
|
|
CHECK_CLOUD_ENVIRONMENT();
|
|
|
|
GFN_SDK_LOG("Registering for Save Callback updates");
|
|
|
|
_gfnUserContextCallbackWrapper* pWrappedContext = (_gfnUserContextCallbackWrapper*)malloc(sizeof(_gfnUserContextCallbackWrapper));
|
|
pWrappedContext->fnCallback = (void*)saveCallback;
|
|
pWrappedContext->pOrigUserContext = pUserContext;
|
|
|
|
// Suppress CppCheck warning about memory leak. The cloud DLL takes ownership of the memory and will free on call to gfnShutdownSdk
|
|
// cppcheck-suppress memleak
|
|
DELEGATE_TO_CLOUD_LIBRARY(RegisterSaveCallback, &_gfnSaveCallbackWrapper, pWrappedContext);
|
|
}
|
|
|
|
static void GFN_CALLBACK _gfnSessionInitCallbackWrapper(int status, void* pCString, void* pContext)
|
|
{
|
|
(void)status;
|
|
_gfnUserContextCallbackWrapper* pWrappedContext = (_gfnUserContextCallbackWrapper*)(pContext);
|
|
if (pWrappedContext == NULL || pWrappedContext->fnCallback == NULL)
|
|
{
|
|
return;
|
|
}
|
|
SessionInitCallbackSig cb = (SessionInitCallbackSig)(pWrappedContext->fnCallback);
|
|
cb((const char *)pCString, pWrappedContext->pOrigUserContext);
|
|
}
|
|
|
|
GfnRuntimeError GfnRegisterSessionInitCallback(SessionInitCallbackSig sessionInitCallback, void* pUserContext)
|
|
{
|
|
CHECK_NULL_PARAM(sessionInitCallback);
|
|
CHECK_CLOUD_ENVIRONMENT();
|
|
|
|
GFN_SDK_LOG("Registering for SessionInit Callback updates");
|
|
|
|
_gfnUserContextCallbackWrapper* pWrappedContext = (_gfnUserContextCallbackWrapper*)malloc(sizeof(_gfnUserContextCallbackWrapper));
|
|
pWrappedContext->fnCallback = (void*)sessionInitCallback;
|
|
pWrappedContext->pOrigUserContext = pUserContext;
|
|
|
|
// Suppress CppCheck warning about memory leak. The cloud DLL takes ownership of the memory and will free on call to gfnShutdownSdk
|
|
// cppcheck-suppress memleak
|
|
DELEGATE_TO_CLOUD_LIBRARY(RegisterSessionInitCallback, &_gfnSessionInitCallbackWrapper, pWrappedContext)
|
|
}
|
|
|
|
static void GFN_CALLBACK _gfnMessageCallbackWrapper(int status, void* pMessage, void* pContext)
|
|
{
|
|
(void)status;
|
|
_gfnUserContextCallbackWrapper* pWrappedContext = (_gfnUserContextCallbackWrapper*)(pContext);
|
|
if (pWrappedContext == NULL || pWrappedContext->fnCallback == NULL)
|
|
{
|
|
return;
|
|
}
|
|
MessageCallbackSig cb = (MessageCallbackSig)(pWrappedContext->fnCallback);
|
|
cb((GfnString*)pMessage, pWrappedContext->pOrigUserContext);
|
|
}
|
|
|
|
GfnRuntimeError GfnRegisterMessageCallback(MessageCallbackSig messageCallback, void* pUserContext)
|
|
{
|
|
CHECK_NULL_PARAM(messageCallback);
|
|
|
|
if (g_pCloudLibrary != NULL && g_pCloudLibrary->RegisterMessageCallback != NULL)
|
|
{
|
|
_gfnUserContextCallbackWrapper* pWrappedContext = (_gfnUserContextCallbackWrapper*)malloc(sizeof(_gfnUserContextCallbackWrapper));
|
|
pWrappedContext->fnCallback = (void*)messageCallback;
|
|
pWrappedContext->pOrigUserContext = pUserContext;
|
|
|
|
// Suppress CppCheck warning about memory leak. The cloud DLL takes ownership of the memory and will free on call to gfnShutdownSdk
|
|
// cppcheck-suppress memleak
|
|
DELEGATE_TO_CLOUD_LIBRARY(RegisterMessageCallback, &_gfnMessageCallbackWrapper, pWrappedContext);
|
|
}
|
|
else
|
|
{
|
|
if (g_gfnSdkModule == NULL)
|
|
{
|
|
return gfnAPINotInit;
|
|
}
|
|
|
|
gfnRegisterMessageCallbackFn fnRegisterMessageCallback = (gfnRegisterMessageCallbackFn)gfnGetSymbol(g_gfnSdkModule, "gfnRegisterMessageCallback");
|
|
if (fnRegisterMessageCallback == NULL)
|
|
{
|
|
return gfnAPINotFound;
|
|
}
|
|
|
|
return fnRegisterMessageCallback(messageCallback, pUserContext);
|
|
}
|
|
}
|
|
|
|
|
|
void gfnInitLogging()
|
|
{
|
|
#ifdef _WIN32
|
|
wchar_t localAppDataPath[1024] = { L"" };
|
|
if (SHGetSpecialFolderPathW(NULL, localAppDataPath, CSIDL_COMMON_APPDATA, false) == FALSE)
|
|
{
|
|
GFN_SDK_LOG("Could not get path to LOCALAPPDATA: %d", GetLastError());
|
|
return;
|
|
}
|
|
wcscat_s(localAppDataPath, 1024, L"\\NVIDIA Corporation\\GfnRuntimeSdk");
|
|
int createDirResult = SHCreateDirectoryExW(NULL, localAppDataPath, NULL);
|
|
if (createDirResult != ERROR_SUCCESS && createDirResult != ERROR_FILE_EXISTS && createDirResult != ERROR_ALREADY_EXISTS)
|
|
{
|
|
return;
|
|
}
|
|
wcscat_s(localAppDataPath, 1024, L"\\GfnRuntimeSdkWrapper.log");
|
|
_wfopen_s(&s_logfile, localAppDataPath, L"w+");
|
|
#elif __linux__
|
|
const char* logPath = "~/.nvidia/GfnRuntimeSdk/GfnRuntimeSdkWrapper.log";
|
|
s_logfile = fopen(logPath, "r");
|
|
#endif
|
|
}
|
|
|
|
void gfnDeinitLogging()
|
|
{
|
|
if (s_logfile)
|
|
{
|
|
fclose(s_logfile);
|
|
s_logfile = NULL;
|
|
}
|
|
}
|
|
|
|
void gfnLog(char const* func, int line, char const* format, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, format);
|
|
|
|
#ifdef _WIN32
|
|
// Format date and time
|
|
GetLocalTime(&s_logData.timeBuffer);
|
|
size_t n = sprintf_s(s_logData.buffer, 24, "%04d-%02d-%02dT%02d:%02d:%02d.%03d", s_logData.timeBuffer.wYear, s_logData.timeBuffer.wMonth, s_logData.timeBuffer.wDay,
|
|
s_logData.timeBuffer.wHour, s_logData.timeBuffer.wMinute, s_logData.timeBuffer.wSecond, s_logData.timeBuffer.wMilliseconds);
|
|
|
|
// Format function, line number
|
|
n += _snprintf_s(s_logData.buffer + n, kGfnLogBufLen - n, kGfnLogBufLen - n, " %24.24s:%-5d", func, line);
|
|
|
|
// Format the actual message/format
|
|
n += vsnprintf(s_logData.buffer + n, kGfnLogBufLen - n - 2, format, args); // -1 leave room for linebreak and terminator
|
|
if (kGfnLogBufLen - 2 < n) // returns amount it WOULD have written if buffer were big enough
|
|
{
|
|
n = kGfnLogBufLen - 2;
|
|
}
|
|
_snprintf_s(s_logData.buffer + n, kGfnLogBufLen - n, kGfnLogBufLen - n, "\n"); // Add linebreak at end
|
|
#elif __linux__
|
|
// Format date and time
|
|
time_t t = time(NULL);
|
|
localtime_r(&t, &s_logData.timeBuffer);
|
|
|
|
size_t n = snprintf(s_logData.buffer, 24, "%04d-%02d-%02dT%02d:%02d:%02d.%03d",
|
|
s_logData.timeBuffer.tm_year + 1900,
|
|
s_logData.timeBuffer.tm_mon + 1,
|
|
s_logData.timeBuffer.tm_mday,
|
|
s_logData.timeBuffer.tm_hour,
|
|
s_logData.timeBuffer.tm_min,
|
|
s_logData.timeBuffer.tm_sec,
|
|
0);
|
|
|
|
// Format function, line number
|
|
n += snprintf(s_logData.buffer + n, kGfnLogBufLen - n, " %24.24s:%-5d", func, line);
|
|
|
|
// Format the actual message/format
|
|
n += vsnprintf(s_logData.buffer + n, kGfnLogBufLen - n - 2, format, args);
|
|
if (kGfnLogBufLen - 2 < n) // returns amount it WOULD have written if buffer were big enough
|
|
{
|
|
n = kGfnLogBufLen - 2;
|
|
}
|
|
|
|
// Add linebreak at end
|
|
snprintf(s_logData.buffer + n, kGfnLogBufLen - n, "\n");
|
|
#endif
|
|
|
|
if (s_logfile)
|
|
{
|
|
fputs(s_logData.buffer, s_logfile);
|
|
fflush(s_logfile);
|
|
}
|
|
else
|
|
{
|
|
fputs(s_logData.buffer, stderr);
|
|
fflush(stderr);
|
|
}
|
|
va_end(args);
|
|
}
|