673 lines
19 KiB
C++
Executable File
673 lines
19 KiB
C++
Executable File
//-----------------------------------------------------------------------------
|
|
// Torque Game Engine
|
|
// Copyright (C) GarageGames.com, Inc.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "platform/platformVideo.h"
|
|
#include "gui/core/guiCanvas.h"
|
|
#include "console/console.h"
|
|
#include "platform/gameInterface.h"
|
|
|
|
extern void GameDeactivate( bool noRender );
|
|
extern void GameReactivate();
|
|
|
|
// Static class data:
|
|
Vector<DisplayDevice*> Video::smDeviceList;
|
|
DisplayDevice* Video::smCurrentDevice;
|
|
bool Video::smCritical = false;
|
|
bool Video::smNeedResurrect = false;
|
|
|
|
Resolution DisplayDevice::smCurrentRes;
|
|
bool DisplayDevice::smIsFullScreen;
|
|
|
|
|
|
ConsoleFunctionGroupBegin(Video, "Video control functions.");
|
|
|
|
//--------------------------------------------------------------------------
|
|
ConsoleFunction( setDisplayDevice, bool, 2, 6, "( string deviceName, int width, int height=NULL, int bpp=NULL, bool fullScreen=NULL )"
|
|
"Attempt to set the screen mode using as much information as is provided.")
|
|
{
|
|
Resolution currentRes = Video::getResolution();
|
|
|
|
U32 width = ( argc > 2 ) ? dAtoi( argv[2] ) : currentRes.w;
|
|
U32 height = ( argc > 3 ) ? dAtoi( argv[3] ) : currentRes.h;
|
|
U32 bpp = ( argc > 4 ) ? dAtoi( argv[4] ) : currentRes.bpp;
|
|
bool fullScreen = ( argc > 5 ) ? dAtob( argv[5] ) : Video::isFullScreen();
|
|
|
|
return( Video::setDevice( argv[1], width, height, bpp, fullScreen ) );
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
ConsoleFunction( setScreenMode, bool, 5, 5, "( int width, int height, int bpp, bool fullScreen )" )
|
|
{
|
|
return( Video::setScreenMode( dAtoi( argv[1] ), dAtoi( argv[2] ), dAtoi( argv[3] ), dAtob( argv[4] ) ) );
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
ConsoleFunction( toggleFullScreen, bool, 1, 1, "")
|
|
{
|
|
return( Video::toggleFullScreen() );
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
ConsoleFunction( isFullScreen, bool, 1, 1, "Is the game running full-screen?")
|
|
{
|
|
return( Video::isFullScreen() );
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
ConsoleFunction( switchBitDepth, bool, 1, 1, "Switch between 16 or 32 bits. Only works in full screen.")
|
|
{
|
|
if ( !Video::isFullScreen() )
|
|
{
|
|
Con::warnf( ConsoleLogEntry::General, "Can only switch bit depth in full-screen mode!" );
|
|
return( false );
|
|
}
|
|
|
|
Resolution res = Video::getResolution();
|
|
return( Video::setResolution( res.w, res.h, ( res.bpp == 16 ? 32 : 16 ) ) );
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
ConsoleFunction( prevResolution, bool, 1, 1, "Switch to previous resolution.")
|
|
{
|
|
return( Video::prevRes() );
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
ConsoleFunction( nextResolution, bool, 1, 1, "Switch to next resolution.")
|
|
{
|
|
return( Video::nextRes() );
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
ConsoleFunction( getRes, const char*, 1, 1, "Get the width, height, and bitdepth of the screen.")
|
|
{
|
|
static char resBuf[16];
|
|
Resolution res = Video::getResolution();
|
|
dSprintf( resBuf, sizeof(resBuf), "%d %d %d", res.w, res.h, res.bpp );
|
|
return( resBuf );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
ConsoleFunction( setRes, bool, 3, 4, "( int width, int height, int bpp=NULL )")
|
|
{
|
|
U32 width = dAtoi( argv[1] );
|
|
U32 height = dAtoi( argv[2] );
|
|
U32 bpp = 0;
|
|
if ( argc == 4 )
|
|
{
|
|
bpp = dAtoi( argv[3] );
|
|
if ( bpp != 16 && bpp != 32 )
|
|
bpp = 0;
|
|
}
|
|
|
|
return( Video::setResolution( width, height, bpp ) );
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
ConsoleFunction( getDesktopResolution, const char*, 1, 1, "Get the width, height, and bitdepth of the screen.")
|
|
{
|
|
static char resBuf[32];
|
|
Resolution res = Video::getDesktopResolution();
|
|
dSprintf( resBuf, sizeof(resBuf), "%d %d %d", res.w, res.h, res.bpp );
|
|
return( resBuf );
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
ConsoleFunction( getDisplayDeviceList, const char*, 1, 1, "")
|
|
{
|
|
return( Video::getDeviceList() );
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
ConsoleFunction( getResolutionList, const char*, 2, 2, "")
|
|
{
|
|
DisplayDevice* device = Video::getDevice( argv[1] );
|
|
if ( !device )
|
|
{
|
|
Con::warnf( ConsoleLogEntry::General, "\"%s\" display device not found!", argv[1] );
|
|
return( NULL );
|
|
}
|
|
|
|
return( device->getResolutionList() );
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
ConsoleFunction( getVideoDriverInfo, const char*, 1, 1, "")
|
|
{
|
|
return( Video::getDriverInfo() );
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
ConsoleFunction( isDeviceFullScreenOnly, bool, 2, 2, "( string deviceName )")
|
|
{
|
|
DisplayDevice* device = Video::getDevice( argv[1] );
|
|
if ( !device )
|
|
{
|
|
Con::warnf( ConsoleLogEntry::General, "\"%s\" display device not found!", argv[1] );
|
|
return( false );
|
|
}
|
|
|
|
return( device->isFullScreenOnly() );
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
static F32 sgOriginalGamma = -1.0;
|
|
static F32 sgGammaCorrection = 0.0;
|
|
|
|
ConsoleFunction(videoSetGammaCorrection, void, 2, 2, "setGammaCorrection(gamma);")
|
|
{
|
|
argc;
|
|
F32 g = mClampF(dAtof(argv[1]),0.0,1.0);
|
|
F32 d = -(g - 0.5);
|
|
|
|
if (d != sgGammaCorrection &&
|
|
(sgOriginalGamma != -1.0 || Video::getGammaCorrection(sgOriginalGamma)))
|
|
Video::setGammaCorrection(sgOriginalGamma+d);
|
|
sgGammaCorrection = d;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void Video::init()
|
|
{
|
|
destroy();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void Video::destroy()
|
|
{
|
|
if ( smCurrentDevice )
|
|
{
|
|
smCritical = true;
|
|
smCurrentDevice->shutdown();
|
|
smCritical = false;
|
|
}
|
|
|
|
smCurrentDevice = NULL;
|
|
|
|
for ( U32 i = 0; i < smDeviceList.size(); i++ )
|
|
delete smDeviceList[i];
|
|
|
|
smDeviceList.clear();
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool Video::installDevice( DisplayDevice *dev )
|
|
{
|
|
if ( dev )
|
|
{
|
|
smDeviceList.push_back( dev );
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool Video::setDevice( const char *renderName, U32 width, U32 height, U32 bpp, bool fullScreen )
|
|
{
|
|
S32 deviceIndex = NO_DEVICE;
|
|
S32 iOpenGL = -1;
|
|
S32 iD3D = -1;
|
|
|
|
for ( S32 i = 0; i < smDeviceList.size(); i++ )
|
|
{
|
|
if ( dStrcmp( smDeviceList[i]->mDeviceName, renderName ) == 0 )
|
|
deviceIndex = i;
|
|
|
|
if ( dStrcmp( smDeviceList[i]->mDeviceName, "OpenGL" ) == 0 )
|
|
iOpenGL = i;
|
|
if ( dStrcmp( smDeviceList[i]->mDeviceName, "D3D" ) == 0 )
|
|
iD3D = i;
|
|
}
|
|
|
|
if ( deviceIndex == NO_DEVICE )
|
|
{
|
|
Con::warnf( ConsoleLogEntry::General, "\"%s\" display device not found!", renderName );
|
|
return false;
|
|
}
|
|
|
|
// Change the display device:
|
|
if ( smDeviceList[deviceIndex] != NULL )
|
|
{
|
|
if (smCurrentDevice && smCurrentDevice != smDeviceList[deviceIndex])
|
|
{
|
|
Con::printf( "Deactivating the previous display device..." );
|
|
Game->textureKill();
|
|
smNeedResurrect = true;
|
|
smCurrentDevice->shutdown();
|
|
}
|
|
|
|
if (iOpenGL != -1 && !Con::getBoolVariable("$pref::Video::allowOpenGL"))
|
|
{
|
|
// change to D3D, delete OpenGL in the recursive call
|
|
if (dStrcmp(renderName,"OpenGL") == 0)
|
|
{
|
|
U32 w, h, d;
|
|
|
|
dSscanf(Con::getVariable("$pref::Video::resolution"), "%d %d %d", &w, &h, &d);
|
|
|
|
return setDevice("D3D",w,h,d,Con::getBoolVariable("$pref::Video::fullScreen",true));
|
|
}
|
|
else
|
|
{
|
|
delete smDeviceList[iOpenGL];
|
|
smDeviceList.erase(iOpenGL);
|
|
}
|
|
}
|
|
else if (iD3D != -1 && !Con::getBoolVariable("$pref::Video::allowD3D"))
|
|
{
|
|
// change to OpenGL, delete D3D in the recursive call
|
|
if (dStrcmp(renderName,"D3D") == 0)
|
|
{
|
|
U32 w, h, d;
|
|
|
|
dSscanf(Con::getVariable("$pref::Video::resolution"), "%d %d %d", &w, &h, &d);
|
|
|
|
return setDevice("OpenGL",w,h,d,Con::getBoolVariable("$pref::Video::fullScreen",true));
|
|
}
|
|
else
|
|
{
|
|
delete smDeviceList[iD3D];
|
|
smDeviceList.erase(iD3D);
|
|
}
|
|
}
|
|
else if (iD3D != -1 &&
|
|
dStrcmp(renderName,"OpenGL") == 0 &&
|
|
!Con::getBoolVariable("$pref::Video::preferOpenGL") &&
|
|
!Con::getBoolVariable("$pref::Video::appliedPref"))
|
|
{
|
|
U32 w, h, d;
|
|
|
|
dSscanf(Con::getVariable("$pref::Video::resolution"), "%d %d %d", &w, &h, &d);
|
|
Con::setBoolVariable("$pref::Video::appliedPref", true);
|
|
|
|
return setDevice("D3D",w,h,d,Con::getBoolVariable("$pref::Video::fullScreen",true));
|
|
}
|
|
else
|
|
Con::setBoolVariable("$pref::Video::appliedPref", true);
|
|
|
|
Con::printf( "Activating the %s display device...", renderName );
|
|
smCurrentDevice = smDeviceList[deviceIndex];
|
|
|
|
smCritical = true;
|
|
bool result = smCurrentDevice->activate( width, height, bpp, fullScreen );
|
|
smCritical = false;
|
|
|
|
if ( result )
|
|
{
|
|
if (smNeedResurrect)
|
|
{
|
|
Game->textureResurrect();
|
|
smNeedResurrect = false;
|
|
}
|
|
if (sgOriginalGamma != -1.0 || Video::getGammaCorrection(sgOriginalGamma))
|
|
Video::setGammaCorrection(sgOriginalGamma + sgGammaCorrection);
|
|
Con::evaluate("resetCanvas();");
|
|
}
|
|
|
|
// The video mode activate may have failed above, return that status
|
|
return( result );
|
|
}
|
|
|
|
return( false );
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool Video::setScreenMode( U32 width, U32 height, U32 bpp, bool fullScreen )
|
|
{
|
|
if ( smCurrentDevice )
|
|
{
|
|
smCritical = true;
|
|
bool result = smCurrentDevice->setScreenMode( width, height, bpp, fullScreen );
|
|
smCritical = false;
|
|
|
|
return( result );
|
|
}
|
|
|
|
return( false );
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
void Video::deactivate()
|
|
{
|
|
if ( smCritical ) return;
|
|
|
|
GameDeactivate( DisplayDevice::isFullScreen() );
|
|
if ( smCurrentDevice && DisplayDevice::isFullScreen() )
|
|
{
|
|
smCritical = true;
|
|
|
|
Game->textureKill();
|
|
smCurrentDevice->shutdown();
|
|
|
|
Platform::minimizeWindow();
|
|
smCritical = false;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void Video::reactivate()
|
|
{
|
|
if ( smCritical ) return;
|
|
|
|
if ( smCurrentDevice && DisplayDevice::isFullScreen() )
|
|
{
|
|
Resolution res = DisplayDevice::getResolution();
|
|
|
|
smCritical = true;
|
|
smCurrentDevice->activate(res.w,res.h,res.bpp,DisplayDevice::isFullScreen());
|
|
Game->textureResurrect();
|
|
|
|
smCritical = false;
|
|
if (sgOriginalGamma != -1.0)
|
|
Video::setGammaCorrection(sgOriginalGamma + sgGammaCorrection);
|
|
}
|
|
GameReactivate();
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool Video::setResolution( U32 width, U32 height, U32 bpp )
|
|
{
|
|
if ( smCurrentDevice )
|
|
{
|
|
if ( bpp == 0 )
|
|
bpp = DisplayDevice::getResolution().bpp;
|
|
|
|
smCritical = true;
|
|
bool result = smCurrentDevice->setResolution( width, height, bpp );
|
|
smCritical = false;
|
|
|
|
return( result );
|
|
}
|
|
return( false );
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool Video::toggleFullScreen()
|
|
{
|
|
if ( smCurrentDevice )
|
|
{
|
|
smCritical = true;
|
|
bool result = smCurrentDevice->toggleFullScreen();
|
|
smCritical = false;
|
|
|
|
return( result );
|
|
}
|
|
return( false );
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
DisplayDevice* Video::getDevice( const char* renderName )
|
|
{
|
|
for ( S32 i = 0; i < smDeviceList.size(); i++ )
|
|
{
|
|
if ( dStrcmp( smDeviceList[i]->mDeviceName, renderName ) == 0 )
|
|
return( smDeviceList[i] );
|
|
}
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool Video::prevRes()
|
|
{
|
|
if ( smCurrentDevice )
|
|
{
|
|
smCritical = true;
|
|
bool result = smCurrentDevice->prevRes();
|
|
smCritical = false;
|
|
|
|
return( result );
|
|
}
|
|
return( false );
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool Video::nextRes()
|
|
{
|
|
if ( smCurrentDevice )
|
|
{
|
|
smCritical = true;
|
|
bool result = smCurrentDevice->nextRes();
|
|
smCritical = false;
|
|
|
|
return( result );
|
|
}
|
|
return( false );
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
Resolution Video::getResolution()
|
|
{
|
|
return DisplayDevice::getResolution();
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
const char* Video::getDeviceList()
|
|
{
|
|
U32 deviceCount = smDeviceList.size();
|
|
if ( deviceCount > 0 ) // It better be...
|
|
{
|
|
U32 strLen = 0, i;
|
|
for ( i = 0; i < deviceCount; i++ )
|
|
strLen += ( dStrlen( smDeviceList[i]->mDeviceName ) + 1 );
|
|
|
|
char* returnString = Con::getReturnBuffer( strLen );
|
|
dStrcpy( returnString, smDeviceList[0]->mDeviceName );
|
|
for ( i = 1; i < deviceCount; i++ )
|
|
{
|
|
dStrcat( returnString, "\t" );
|
|
dStrcat( returnString, smDeviceList[i]->mDeviceName );
|
|
}
|
|
|
|
return( returnString );
|
|
}
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
const char* Video::getResolutionList()
|
|
{
|
|
if ( smCurrentDevice )
|
|
return smCurrentDevice->getResolutionList();
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
const char* Video::getDriverInfo()
|
|
{
|
|
if ( smCurrentDevice )
|
|
return smCurrentDevice->getDriverInfo();
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool Video::isFullScreen()
|
|
{
|
|
return DisplayDevice::isFullScreen();
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
void Video::swapBuffers()
|
|
{
|
|
if ( smCurrentDevice )
|
|
smCurrentDevice->swapBuffers();
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool Video::getGammaCorrection(F32 &g)
|
|
{
|
|
if (smCurrentDevice)
|
|
return smCurrentDevice->getGammaCorrection(g);
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool Video::setGammaCorrection(F32 g)
|
|
{
|
|
if (smCurrentDevice)
|
|
return smCurrentDevice->setGammaCorrection(g);
|
|
|
|
return false;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool Video::setVerticalSync( bool on )
|
|
{
|
|
if ( smCurrentDevice )
|
|
return( smCurrentDevice->setVerticalSync( on ) );
|
|
|
|
return( false );
|
|
}
|
|
|
|
ConsoleFunction( setVerticalSync, bool, 2, 2, "setVerticalSync( bool f )" )
|
|
{
|
|
argc;
|
|
return( Video::setVerticalSync( dAtob( argv[1] ) ) );
|
|
}
|
|
|
|
ConsoleFunction( minimizeWindow, void, 1, 1, "minimizeWindow() - Minimize the game window" )
|
|
{
|
|
Platform::minimizeWindow();
|
|
}
|
|
|
|
ConsoleFunctionGroupEnd(Video);
|
|
|
|
//------------------------------------------------------------------------------
|
|
DisplayDevice::DisplayDevice()
|
|
{
|
|
mDeviceName = NULL;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
void DisplayDevice::init()
|
|
{
|
|
smCurrentRes = Resolution( 0, 0, 0 );
|
|
smIsFullScreen = false;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool DisplayDevice::prevRes()
|
|
{
|
|
U32 resIndex;
|
|
for ( resIndex = mResolutionList.size() - 1; resIndex > 0; resIndex-- )
|
|
{
|
|
if ( mResolutionList[resIndex].bpp == smCurrentRes.bpp
|
|
&& mResolutionList[resIndex].w <= smCurrentRes.w
|
|
&& mResolutionList[resIndex].h != smCurrentRes.h )
|
|
break;
|
|
}
|
|
|
|
if ( mResolutionList[resIndex].bpp == smCurrentRes.bpp )
|
|
return( Video::setResolution( mResolutionList[resIndex].w, mResolutionList[resIndex].h, mResolutionList[resIndex].bpp ) );
|
|
|
|
return( false );
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool DisplayDevice::nextRes()
|
|
{
|
|
U32 resIndex;
|
|
for ( resIndex = 0; resIndex < mResolutionList.size() - 1; resIndex++ )
|
|
{
|
|
if ( mResolutionList[resIndex].bpp == smCurrentRes.bpp
|
|
&& mResolutionList[resIndex].w >= smCurrentRes.w
|
|
&& mResolutionList[resIndex].h != smCurrentRes.h )
|
|
break;
|
|
}
|
|
|
|
if ( mResolutionList[resIndex].bpp == smCurrentRes.bpp )
|
|
return( Video::setResolution( mResolutionList[resIndex].w, mResolutionList[resIndex].h, mResolutionList[resIndex].bpp ) );
|
|
|
|
return( false );
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// This function returns a string containing all of the available resolutions for this device
|
|
// in the format "<bit depth> <width> <height>", separated by tabs.
|
|
//
|
|
const char* DisplayDevice::getResolutionList()
|
|
{
|
|
if (Con::getBoolVariable("$pref::Video::clipHigh", false))
|
|
for (S32 i = mResolutionList.size()-1; i >= 0; --i)
|
|
if (mResolutionList[i].w > 1152 || mResolutionList[i].h > 864)
|
|
mResolutionList.erase(i);
|
|
|
|
if (Con::getBoolVariable("$pref::Video::only16", false))
|
|
for (S32 i = mResolutionList.size()-1; i >= 0; --i)
|
|
if (mResolutionList[i].bpp == 32)
|
|
mResolutionList.erase(i);
|
|
|
|
U32 resCount = mResolutionList.size();
|
|
if ( resCount > 0 )
|
|
{
|
|
char* tempBuffer = new char[resCount * 15];
|
|
tempBuffer[0] = 0;
|
|
for ( U32 i = 0; i < resCount; i++ )
|
|
{
|
|
char newString[15];
|
|
dSprintf( newString, sizeof( newString ), "%d %d %d\t", mResolutionList[i].w, mResolutionList[i].h, mResolutionList[i].bpp );
|
|
dStrcat( tempBuffer, newString );
|
|
}
|
|
tempBuffer[dStrlen( tempBuffer ) - 1] = 0;
|
|
|
|
char* returnString = Con::getReturnBuffer( dStrlen( tempBuffer ) + 1 );
|
|
dStrcpy( returnString, tempBuffer );
|
|
delete [] tempBuffer;
|
|
|
|
return returnString;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#define checkstr(a) a = a ? a : "unknown"
|
|
|
|
void Video::setVideo(const char * gapi, const char * vendor, const char * renderer, const char * platform, const char * os, const char * arch)
|
|
{
|
|
checkstr(gapi);
|
|
checkstr(vendor);
|
|
checkstr(renderer);
|
|
checkstr(platform);
|
|
checkstr(os);
|
|
checkstr(arch);
|
|
|
|
Con::executef(7, "setVideo",gapi,vendor,renderer,platform,os,arch);
|
|
}
|