Torque3D/Templates/Empty/web/source/common/webCommon.cpp
2012-09-19 11:29:55 -04:00

727 lines
21 KiB
C++

//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "webConfig.h"
#include "webCommon.h"
#include <string>
#include <vector>
// Platform specific shared library handling
#ifdef WIN32
#pragma warning( disable : 4996)
#define TORQUE_OPEN LoadLibraryA
#define TORQUE_FUNCTION GetProcAddress
#define TORQUE_CLOSE FreeLibrary
#define strncasecmp strnicmp
#define strcasecmp stricmp
#else // Mac
#define TORQUE_OPEN(path) dlopen(path, RTLD_LAZY | RTLD_LOCAL)
#define TORQUE_FUNCTION dlsym
#define TORQUE_CLOSE dlclose
#endif
// C Interface exported from the Torque 3D DLL (or Bundle on Mac)
extern "C"
{
// initialize Torque 3D including argument handling
bool (*torque_engineinit)(int argc, const char **argv) = NULL;
// tick Torque 3D's main loop
int (*torque_enginetick)() = NULL;
// set Torque 3D into web deployment mode (disable fullscreen exlusive mode, etc)
int (*torque_setwebdeployment)() = NULL;
// shutdown the engine
bool (*torque_engineshutdown)() = NULL;
// signal an engine shutdown (as with the quit(); console command)
void (*torque_enginesignalshutdown)() = NULL;
// reset the engine, unloading any current level and returning to the main menu
void (*torque_reset)() = NULL;
// Evaluate arbitrary TorqueScript (ONLY CALL torque_evaluate FROM TRUSTED CODE!!!)
const char* (*torque_evaluate)(const char* code) = NULL;
// Get a console variable
const char* (*torque_getvariable)(const char* name) = NULL;
// Set a console variable
void (*torque_setvariable)(const char* name, const char* value) = NULL;
// Export a function to the Torque 3D console system which matches the StringCallback function prototype
// specify the nameSpace, functionName, usage, min and max arguments
void (*torque_exportstringcallback)(StringCallback cb, const char *nameSpace, const char *funcName, const char* usage, int minArgs, int maxArgs) = NULL;
// Set a TorqueScript console function as secure and available for JavaScript via the callScript plugin method
void (*torque_addsecurefunction)(const char* nameSpace, const char* fname) = NULL;
// Call a TorqueScript console function that has been marked as secure
const char* (*torque_callsecurefunction)(const char* nameSpace, const char* name, int argc, const char ** argv) = NULL;
// resize the Torque 3D child window to the specified width and height
void (*torque_resizewindow)(int width, int height) = NULL;
#ifndef WIN32
// On Mac, handle the parent safari window
void (*torque_setsafariwindow)(NSWindow* window, int32 x, int32 y, int32 width, int32 height) = NULL;
// On Mac, sets the executable path
void (*torque_setexecutablepath)(const char *path) = NULL;
#else
// retrieve the render windows hwnd
void* (*torque_gethwnd)() = NULL;
// directly add a message to the Torque 3D event queue, bypassing the Windows event queue
// this is useful in the case of the IE plugin, where we are hooking into an application
// level message, and posting to the windows queue would cause a hang
void (*torque_directmessage)(unsigned int message, unsigned int wparam, unsigned int lparam) = NULL;
#endif
};
namespace WebCommon
{
std::string gPluginMIMEType;
#ifdef WIN32
HMODULE gTorque3DModule = NULL;
HMODULE gPluginModule = 0;
// bring up a platform specific message box (used for error reporting)
void MessageBox(void* parentWindow, const char* msg, const char* caption )
{
::MessageBoxA( (HWND) parentWindow, msg, caption, MB_OK|MB_ICONWARNING);
}
// retrieve the game directory using the filename (which includes the full path) of the plugin DLL
const char* GetGameDirectory()
{
static char dir[4096];
GetModuleFileNameA(gPluginModule, dir, 4096);
int i = strlen(dir) - 1;
while (i>=0)
{
if (dir[i] == '\\' || dir[i] == '/')
{
dir[i] = 0;
break;
}
i--;
}
return dir;
}
// retrieve the name of our game DLL (includes Torque 3D engine) based on naming convention
const char* GetGameLibrary()
{
char dir[4096];
static char lib[4096];
lib[0] = 0;
GetModuleFileNameA(gPluginModule, dir, 4096);
int i = strlen(dir) - 1;
while (i>=0)
{
if (dir[i] == '\\' || dir[i] == '/')
{
// copy, minus the "NP " or "IE " of plugin name
#ifdef _DEBUG
sprintf(lib, "%s_DEBUG.dll", &dir[i+4]);
#else
sprintf(lib, "%s.dll", &dir[i+4]);
#endif
return lib;
}
// strip off end
if (!strncmp(&dir[i], " Plugin", 7))
dir[i] = 0;
i--;
}
return lib;
}
#else
void* gTorque3DModule = NULL;
NSBundle* gPluginBundle = NULL;
// bring up a platform specific message box (used for error reporting)
void MessageBox(void* parentWindow, const char* msg, const char* caption )
{
// convert title and message to NSStrings
NSString *nsTitle = [NSString stringWithUTF8String:caption];
NSString *nsMessage = [NSString stringWithUTF8String:msg];
NSAlert *alert = [NSAlert alertWithMessageText:nsTitle
defaultButton:@"OK"
alternateButton:nil
otherButton:nil
informativeTextWithFormat:nsMessage];
[alert runModal];
}
NSBundle* GetPluginBundle()
{
if (gPluginBundle)
return gPluginBundle;
NSDictionary *mimeTypes;
NSString *mime;
NSEnumerator *f;
NSArray *bundles = [NSBundle allBundles];
for (int i = 0; i < [bundles count]; i++) {
NSBundle *b = [bundles objectAtIndex:i];
mimeTypes=[b objectForInfoDictionaryKey:@"WebPluginMIMETypes"];
if (!mimeTypes)
continue;
f=[mimeTypes keyEnumerator];
while((mime=[f nextObject]))
{
if (gPluginMIMEType == std::string([mime UTF8String]))
{
gPluginBundle = b;
break;
}
}
}
return gPluginBundle;
}
// retrieve the game's install folder based on entries from our plugin's Info.plist
const char* GetGameDirectory()
{
static char gamePath[2048] = {'\0'};
if (gamePath[0])
return gamePath;
NSBundle* pluginBundle = GetPluginBundle();
if (!pluginBundle)
return NULL;
NSString *gameInstallPathKey = [NSString stringWithUTF8String:"GameInstallPath"];
NSString *gameInstallPath = [pluginBundle objectForInfoDictionaryKey: gameInstallPathKey];
if (!gameInstallPath)
return NULL;
strcpy(gamePath, [gameInstallPath UTF8String]);
if (!gamePath[0])
return NULL;
return gamePath;
}
// retrieve the game bundle (including Torque 3D engine) from the plugin Info.plist
const char* GetGameLibrary()
{
static char libPath[2048] = {'\0'};
if (libPath[0])
return libPath;
const char* gamePath = GetGameDirectory();
if (!gamePath)
return NULL;
NSBundle* pluginBundle = GetPluginBundle();
if (!pluginBundle)
return NULL;
// NSString* bundleIdentifier = [pluginBundle bundleIdentifier];
NSString *gameNameKey = [NSString stringWithUTF8String:"GameName"];
NSString *gameName = [pluginBundle objectForInfoDictionaryKey: gameNameKey];
if (!gameName)
return NULL;
const char* cgameName = [gameName UTF8String];
if (!cgameName[0])
return NULL;
#ifdef DEBUG
sprintf(libPath, "%s%s_DEBUG.app/Contents/Frameworks/%s Bundle.bundle/Contents/MacOS/%s Bundle", gamePath, cgameName, cgameName, cgameName);
#else
sprintf(libPath, "%s%s.app/Contents/Frameworks/%s Bundle.bundle/Contents/MacOS/%s Bundle", gamePath, cgameName, cgameName, cgameName);
#endif
return libPath;
}
#endif
bool ChangeToGameDirectory()
{
const char* gameDir = GetGameDirectory();
if (!gameDir)
return false;
#ifdef WIN32
return SetCurrentDirectoryA(gameDir);
#else
return (chdir(gameDir) == 0);
#endif
}
// loads the Torque 3D shared library, sets web deployment mode, retrieves engine "C" interface
bool InitTorque3D(void* platformWindow, int clipLeft, int clipTop, int clipRight, int clipBottom)
{
const char* gameDir = GetGameDirectory();
const char* gameLib = GetGameLibrary();
if (gTorque3DModule)
{
WebCommon::MessageBox( 0, "This plugin allows only one instance", "Error");
return false;
}
if (!gameDir || !gameLib)
{
WebCommon::MessageBox( 0, "Unable to get game plugin information", "Error");
return false;
}
if (!ChangeToGameDirectory())
return false;
std::string gameLibStr = gameLib;
#ifdef WIN32
// We want to use an absolute path to the game library
gameLibStr = gameDir;
WebCommon::ConvertToWindowsPathSep(gameLibStr);
gameLibStr += "\\";
gameLibStr += gameLib;
#endif
gTorque3DModule = TORQUE_OPEN(gameLibStr.c_str());
if (!gTorque3DModule)
{
char error[4096];
#ifdef WIN32
sprintf(error, "Could not load game library: %s/%s. Please make sure you have the latest DirectX installed.", gameDir, gameLib);
#else
sprintf(error, "Could not load game library: %s/%s. ", gameDir, gameLib);
#endif
WebCommon::MessageBox( 0, error, "Error");
return false;
}
// snag all the exported functions of the "C" interface
torque_engineinit = (bool (*)(int argc, const char **argv))TORQUE_FUNCTION(gTorque3DModule, "torque_engineinit");
torque_enginetick = (int (*)())TORQUE_FUNCTION(gTorque3DModule, "torque_enginetick");
torque_setwebdeployment = (int (*)())TORQUE_FUNCTION(gTorque3DModule, "torque_setwebdeployment");
torque_engineshutdown = (bool (*)())TORQUE_FUNCTION(gTorque3DModule, "torque_engineshutdown");
torque_enginesignalshutdown = (void (*)())TORQUE_FUNCTION(gTorque3DModule, "torque_enginesignalshutdown");
torque_reset = (void (*)())TORQUE_FUNCTION(gTorque3DModule, "torque_reset");
torque_evaluate = (const char* (*)(const char* code))TORQUE_FUNCTION(gTorque3DModule, "torque_evaluate");
torque_getvariable = (const char* (*)(const char* name))TORQUE_FUNCTION(gTorque3DModule, "torque_getvariable");
torque_setvariable = (void (*)(const char* name, const char* value))TORQUE_FUNCTION(gTorque3DModule, "torque_setvariable");
torque_exportstringcallback = (void (*)(StringCallback cb, const char *nameSpace, const char *funcName, const char* usage, int minArgs, int maxArgs))TORQUE_FUNCTION(gTorque3DModule, "torque_exportstringcallback");
torque_addsecurefunction = (void (*)(const char* nameSpace, const char* name))TORQUE_FUNCTION(gTorque3DModule, "torque_addsecurefunction");
torque_callsecurefunction = (const char* (*)(const char* nameSpace, const char* name, int argc, const char ** argv))TORQUE_FUNCTION(gTorque3DModule, "torque_callsecurefunction");
torque_resizewindow = (void (*)(int width, int height))TORQUE_FUNCTION(gTorque3DModule, "torque_resizewindow");
// check that we got them all
if (!torque_engineinit ||
!torque_enginetick ||
!torque_setwebdeployment ||
!torque_engineshutdown ||
!torque_enginesignalshutdown ||
!torque_reset ||
!torque_evaluate ||
!torque_getvariable ||
!torque_setvariable ||
!torque_exportstringcallback ||
!torque_addsecurefunction ||
!torque_callsecurefunction ||
!torque_resizewindow )
{
WebCommon::MessageBox( platformWindow, "The plugin could not be initialized (missing function exports)", "Error");
TORQUE_CLOSE(gTorque3DModule);
gTorque3DModule = NULL;
return false;
}
#ifndef WIN32
torque_setexecutablepath = (void (*)(const char *path)) dlsym(gTorque3DModule, "torque_setexecutablepath");
torque_setsafariwindow = (void (*)(NSWindow* nswnd, int32, int32, int32, int32)) dlsym(gTorque3DModule, "torque_setsafariwindow");
if (!torque_setexecutablepath || !torque_setsafariwindow)
{
WebCommon::MessageBox( platformWindow, "The plugin could not be initialized (missing function exports)", "Error");
TORQUE_CLOSE(gTorque3DModule);
gTorque3DModule = NULL;
return false;
}
#else
torque_gethwnd = (void* (*)())TORQUE_FUNCTION(gTorque3DModule, "torque_gethwnd");
torque_directmessage = (void (*)(unsigned int message, unsigned int wparam, unsigned int lparam))TORQUE_FUNCTION(gTorque3DModule, "torque_directmessage");
if (!torque_gethwnd || !torque_directmessage)
{
WebCommon::MessageBox( platformWindow, "The plugin could not be initialized (missing function exports)", "Error");
TORQUE_CLOSE(gTorque3DModule);
gTorque3DModule = NULL;
return false;
}
#endif
//tell Torque3D that we're a browser plugin
torque_setwebdeployment();
const char* args[3];
int argc;
#ifdef WIN32
// windows uses a command line arg for parent window
char parentWindow[256];
argc = 3;
sprintf(parentWindow, "%I64u", (unsigned __int64)platformWindow);
args[0] = "game.exe"; //just to satisfy command line parsing
args[1] = "-window";
args[2] = parentWindow;
#else
NSWindow* browserWindow = (NSWindow*) platformWindow;
// tell Torque 3D about our parent browser window
// we initialize with zero size as the page hasn't completely loaded yet
// so, the plugin hasn't been resized by the page and it is better to not show
// anything than wrong extents
torque_setsafariwindow( browserWindow, 0, 0, 0, 0);
argc = 1;
args[0] = gameDir; // just to satisfy command line parsing
#endif
// initialize Torque 3D!
if (!torque_engineinit(argc, args))
{
WebCommon::MessageBox( platformWindow, "The plugin could not be initialized (internal initialization error)", "Error");
return false;
}
return true;
}
// unloads the Torque 3D shared library (first signaling a shutdown for clean exit)
void ShutdownTorque3D()
{
if (gTorque3DModule)
{
ChangeToGameDirectory();
torque_enginesignalshutdown();
torque_enginetick();
torque_engineshutdown();
TORQUE_CLOSE(gTorque3DModule);
}
gTorque3DModule = NULL;
}
// checks a given domain against the allowed domains in webConfig.h
bool CheckDomain(const char* url)
{
bool domainCheck = true;
#ifndef WEBDEPLOY_DOMAIN_CHECK
domainCheck = false;
#endif
#ifdef DEBUG
# ifdef WEBDEPLOY_DOMAIN_ALLOW_DEBUG
domainCheck = false;
# endif
#endif
if (!domainCheck)
return true; // gets rid of "unreachable code" warning
if (strlen(url) > 512)
return false;
if (strlen(url) < 5)
return false;
//do not allow file when using domain checking
if (!strncasecmp(url,"file",4))
return false;
char curl[512] = {0};
unsigned int begin = 0;
while(url[begin])
{
if (url[begin] == ':')
{
if (begin + 3 > strlen(url))
return false;
begin+=3; //skip ://
break;
}
begin++;
}
unsigned int end = begin;
while(end < strlen(url))
{
if (url[end] == '/')
{
break;
}
end++;
}
strcpy(curl, &url[begin]);
curl[end-begin] = 0;
// iterate checking against our allowed domains
for (int i = 0; gAllowedDomains[i]; i++)
if (!strcasecmp(curl, gAllowedDomains[i]))
return true;
WebCommon::MessageBox( 0 , "This plugin cannot be executed from the domain specified", "Error");
return false;
}
// exposes TorqueScript functions marked as secure in webConfig.h, these functions can then be called on the page via Javascript
void AddSecureFunctions()
{
char snamespace[256];
char fname[256];
//define secure functions here
for (unsigned int i = 0; gSecureScript[i]; i++)
{
snamespace[0] = 0;
strcpy(fname, gSecureScript[i]);
//scan through looking for namespace
for (unsigned int j = 1; j < strlen(fname)-2; j++)
{
if (fname[j] == ':' && fname[j+1] == ':')
{
strcpy(snamespace, gSecureScript[i]);
snamespace[j] = 0;
strcpy(fname,&gSecureScript[i][j+2]);
break;
}
}
torque_addsecurefunction(snamespace, fname);
}
}
//simple string copy that eats white space
void StringCopy(char* dst, const char* src, int count)
{
int i, j;
bool eat = true;
for (i = 0, j = 0; i < count ; i++)
{
if (src[i] == 0)
{
dst[j] = 0;
return;
}
if (src[i] != '"' && src[i] != '\t' && src[i] != '\n' && src[i] != ')' && src[i] != '(')
{
if (eat && src[i] == ' ')
continue;
if (src[i] == '\'')
{
eat = !eat;
continue;
}
dst[j++] = src[i];
}
}
}
void SetVariable(const char* variable, const char* value)
{
char mvar[1024];
char mvar2[1024];
if (strlen(variable) > 1023)
{
WebCommon::MessageBox( 0, "WebCommon::SetVariable - buffer overrun", "Error");
return;
}
// make local copies stripping off $ decorator is needed
if (variable[0] == '$')
strcpy(mvar, &variable[1]);
else
strcpy(mvar, variable);
const char* js = "javascript::";
if (strncasecmp(js, mvar, 12))
sprintf(mvar2, "Javascript::%s", mvar);
else
strcpy(mvar2, mvar);
torque_setvariable(mvar2, value);
}
const char* GetVariable(const char* variable)
{
char mvar[1024];
char mvar2[1024];
if (strlen(variable) > 1023)
{
WebCommon::MessageBox( 0, "WebCommon::GetVariable - buffer overrun", "Error");
return "0";
}
// make local copies stripping off $ decorator is needed
if (variable[0] == '$')
strcpy(mvar, &variable[1]);
else
strcpy(mvar, variable);
const char* js = "javascript::";
if (strncasecmp(js, mvar, 12))
sprintf(mvar2, "Javascript::%s", mvar);
else
strcpy(mvar2, mvar);
return torque_getvariable(mvar2);
}
#ifdef WIN32
// string conversion to/from wstring and string
std::wstring StringToWString( const std::string& str )
{
size_t size = str.length();
wchar_t* w = new wchar_t[size+1];
memset( w, 0, sizeof(wchar_t) * (size+1) );
MultiByteToWideChar( CP_ACP,
0,
str.c_str(),
size,
w,
size );
std::wstring ws(w);
delete[] w;
return ws;
}
std::string WStringToString( const std::wstring& wstr )
{
size_t size = wstr.length();
char* s = new char[size+1];
memset( s, 0, sizeof(char) * (size+1) );
WideCharToMultiByte( CP_ACP,
0,
wstr.c_str(),
size,
s,
size,
NULL,
NULL );
std::string str(s);
delete[] s;
return str;
}
void ConvertToWindowsPathSep(std::string& path)
{
size_t pos = 0;
while(pos < path.size() || pos != std::string::npos)
{
pos = path.find("/", pos);
if(pos != std::string::npos)
{
path.replace(pos, 1, "\\");
++pos;
}
}
}
#endif
} //namespace WebCommon