You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
316 lines
8.8 KiB
316 lines
8.8 KiB
//========= Copyright Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Defines the entry point for the console application
|
|
//
|
|
//===============================================================================
|
|
|
|
//#include <sys/stat.h>
|
|
//#include <time.h>
|
|
#include <winsock2.h>
|
|
#include <ws2tcpip.h>
|
|
#include <conio.h >
|
|
#include "tier1/utlvector.h"
|
|
#include "tier0/icommandline.h"
|
|
#include "tier2/tier2.h"
|
|
#include "tier2/fileutils.h"
|
|
#include "../../dx9sdk/include/d3d9.h"
|
|
#include "../../dx9sdk/include/d3dx9.h"
|
|
|
|
#define DEFAULT_PORT "20000"
|
|
#define DEFAULT_SEND_BUF_LEN 40000
|
|
#define DEFAULT_RECV_BUF_LEN 40000
|
|
|
|
char g_pPathBase[MAX_PATH];
|
|
bool g_bPrintDisassembly;
|
|
|
|
// This guy just spins and compiles when a command comes in from the game
|
|
void ServerThread( void * )
|
|
{
|
|
WSADATA wsaData;
|
|
if( WSAStartup( 0x101, &wsaData ) != 0 )
|
|
return;
|
|
|
|
struct addrinfo *result = NULL, hints;
|
|
|
|
ZeroMemory( &hints, sizeof(hints) );
|
|
hints.ai_family = AF_INET;
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
hints.ai_protocol = IPPROTO_TCP;
|
|
hints.ai_flags = AI_PASSIVE;
|
|
|
|
// Resolve the server address and port
|
|
int nResult = getaddrinfo( NULL, DEFAULT_PORT, &hints, &result );
|
|
if ( nResult != 0 )
|
|
{
|
|
printf( "getaddrinfo failed: %d\n", nResult );
|
|
WSACleanup();
|
|
return;
|
|
}
|
|
|
|
// Create a SOCKET for connecting to server
|
|
SOCKET ListenSocket = socket( result->ai_family, result->ai_socktype, result->ai_protocol );
|
|
if (ListenSocket == INVALID_SOCKET) {
|
|
printf("socket failed: %ld\n", WSAGetLastError());
|
|
freeaddrinfo(result);
|
|
WSACleanup();
|
|
return;
|
|
}
|
|
|
|
// Setup the TCP listening socket
|
|
nResult = bind( ListenSocket, result->ai_addr, (int)result->ai_addrlen );
|
|
if (nResult == SOCKET_ERROR)
|
|
{
|
|
printf("bind failed: %d\n", WSAGetLastError());
|
|
freeaddrinfo(result);
|
|
closesocket(ListenSocket);
|
|
WSACleanup();
|
|
return;
|
|
}
|
|
|
|
freeaddrinfo(result);
|
|
|
|
|
|
nResult = listen( ListenSocket, SOMAXCONN );
|
|
if ( nResult == SOCKET_ERROR )
|
|
{
|
|
printf( "listen failed: %d\n", WSAGetLastError() );
|
|
closesocket( ListenSocket );
|
|
WSACleanup();
|
|
return;
|
|
}
|
|
|
|
printf( "Waiting for initial connection...\n" );
|
|
|
|
// Accept a client socket
|
|
SOCKET ClientSocket = accept( ListenSocket, NULL, NULL );
|
|
if ( ClientSocket == INVALID_SOCKET )
|
|
{
|
|
printf( "accept failed: %d\n", WSAGetLastError() );
|
|
closesocket( ListenSocket );
|
|
WSACleanup();
|
|
return;
|
|
}
|
|
|
|
// First connection
|
|
printf( "Game connected\n" );
|
|
|
|
char pRecbuf[DEFAULT_RECV_BUF_LEN]; // Text in command file from game
|
|
uint32 pSendbuf[DEFAULT_SEND_BUF_LEN]; // Error string or binary shader blob in reply
|
|
|
|
while ( true )
|
|
{
|
|
nResult = recv( ClientSocket, pRecbuf, DEFAULT_RECV_BUF_LEN, 0 );
|
|
|
|
if ( nResult > 0 )
|
|
{
|
|
char *pShaderFilename = strtok ( pRecbuf, "\n");
|
|
char pFullFilename[MAX_PATH];
|
|
|
|
// If we took in a path on the commandline, we concatenate the incoming filenames with it
|
|
if ( V_strlen( g_pPathBase ) > 0 )
|
|
{
|
|
V_strncpy( pFullFilename, g_pPathBase, MAX_PATH ); // base path
|
|
V_strncat( pFullFilename, pShaderFilename, MAX_PATH );
|
|
pShaderFilename = pFullFilename;
|
|
}
|
|
char *pShaderModel = strtok ( NULL, "\n");
|
|
int nSendBufLen = 0;
|
|
|
|
// Only try to compile if we have a recognized profile
|
|
if ( !stricmp( pShaderModel, "vs_2_0" ) || !stricmp( pShaderModel, "ps_2_0" ) || !stricmp( pShaderModel, "ps_2_b" ) )
|
|
{
|
|
char *pNumMacros = strtok ( NULL, "\n");
|
|
int nNumMacros = atoi( pNumMacros );
|
|
|
|
// Read macros from the command file
|
|
CUtlVector<D3DXMACRO> macros;
|
|
D3DXMACRO macro;
|
|
for ( int i=0; i<nNumMacros-1; i++ ) // The last one is the (null) one, so don't bother reading it
|
|
{
|
|
// Allocate and populate strings
|
|
macro.Name = strtok( NULL, "\n");
|
|
macro.Definition = strtok( NULL, "\n");
|
|
macros.AddToTail( macro );
|
|
}
|
|
|
|
// Null macro at the end
|
|
macro.Name = NULL;
|
|
macro.Definition = NULL;
|
|
macros.AddToTail( macro );
|
|
|
|
LPD3DXBUFFER pShader, pErrorMessages;
|
|
|
|
// This is the shader compiler we use for pre-ps30 shaders.
|
|
// This utility needs to change if we want to do ps30 shaders (see logic in vertexshaderdx8.cpp)
|
|
HRESULT hr = D3DXCompileShaderFromFile( pShaderFilename, macros.Base(), NULL /* LPD3DXINCLUDE */, "main",
|
|
pShaderModel, 0, &pShader, &pErrorMessages,
|
|
NULL /* LPD3DXCONSTANTTABLE *ppConstantTable */ );
|
|
if ( hr != D3D_OK )
|
|
{
|
|
pSendbuf[0] = 0;
|
|
|
|
printf( "Compilation error in %s\n", pShaderFilename );
|
|
|
|
if ( pErrorMessages )
|
|
{
|
|
memcpy( pSendbuf+1, pErrorMessages->GetBufferPointer(), pErrorMessages->GetBufferSize() ); // Null-terminated string
|
|
printf("%s\n", (const char*)(pSendbuf + 1 ));
|
|
nSendBufLen = pErrorMessages->GetBufferSize() + 4; // account for uint32 at front of the buffer
|
|
}
|
|
else
|
|
{
|
|
((uint8*)(pSendbuf+1))[0] = '?';
|
|
((uint8*)(pSendbuf+1))[1] = '\0';
|
|
nSendBufLen = 2 + 4; // account for uint32 at front of the buffer
|
|
}
|
|
|
|
|
|
}
|
|
else // Success!
|
|
{
|
|
// printf( "Compiled %s\n", pShaderFilename );
|
|
pSendbuf[0] = pShader->GetBufferSize();
|
|
memcpy( pSendbuf+1, pShader->GetBufferPointer(), pShader->GetBufferSize() );
|
|
nSendBufLen = pShader->GetBufferSize() + 4; // account for uint32 at front of the buffer
|
|
|
|
if ( g_bPrintDisassembly )
|
|
{
|
|
printf( "Filename: %s\n", pShaderFilename );
|
|
printf( "Shader model: %s\n", pShaderModel );
|
|
printf( "Macros: " );
|
|
for ( int i = 0; i < nNumMacros - 1; i++ )
|
|
printf( " %s\n", macros[i].Name );
|
|
LPD3DXBUFFER pDisassembly = NULL;
|
|
D3DXDisassembleShader( (const DWORD*)pShader->GetBufferPointer(), FALSE, "", &pDisassembly );
|
|
if ( pDisassembly )
|
|
{
|
|
printf( "Disassembled shader:\n");
|
|
puts( (const char*)pDisassembly->GetBufferPointer() );
|
|
printf("\n");
|
|
|
|
pDisassembly->Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pErrorMessages)
|
|
{
|
|
pErrorMessages->Release();
|
|
}
|
|
|
|
if (pShader)
|
|
{
|
|
pShader->Release();
|
|
}
|
|
|
|
// Purge the macro buffer
|
|
macros.RemoveMultipleFromTail( nNumMacros );
|
|
}
|
|
else // Not a supported shader profile
|
|
{
|
|
pSendbuf[0] = 0;
|
|
char *pCharSendbuff = (char *) (pSendbuf+1);
|
|
V_snprintf( pCharSendbuff, DEFAULT_SEND_BUF_LEN, "Unsupported shader profile: %s\n", pShaderModel );
|
|
nSendBufLen = strlen( pCharSendbuff ) + 4; // account for uint32 at front of the buffer
|
|
}
|
|
|
|
// Send the compiled shader back to the game
|
|
int nSendResult = send( ClientSocket, (const char *)pSendbuf, nSendBufLen, 0 );
|
|
if ( nSendResult == SOCKET_ERROR )
|
|
{
|
|
printf( "send failed: %d\n", WSAGetLastError() );
|
|
closesocket( ClientSocket );
|
|
WSACleanup();
|
|
return;
|
|
}
|
|
}
|
|
else // We had a game talking to us but it went away
|
|
{
|
|
printf( "Game went away, waiting for new connection...\n" );
|
|
|
|
// Block again waiting to accept a connection
|
|
ClientSocket = accept( ListenSocket, NULL, NULL );
|
|
|
|
printf( "Game connected\n" );
|
|
|
|
if ( ClientSocket == INVALID_SOCKET )
|
|
{
|
|
printf( "accept failed: %d\n", WSAGetLastError() );
|
|
Assert( 0 );
|
|
closesocket( ListenSocket );
|
|
WSACleanup();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
nResult = shutdown( ClientSocket, SD_SEND );
|
|
if ( nResult == SOCKET_ERROR )
|
|
{
|
|
printf("shutdown failed: %d\n", WSAGetLastError());
|
|
closesocket( ClientSocket );
|
|
WSACleanup();
|
|
return;
|
|
}
|
|
|
|
// cleanup
|
|
closesocket( ClientSocket );
|
|
WSACleanup();
|
|
}
|
|
|
|
|
|
void CheckPath( char *pPath )
|
|
{
|
|
int len = V_strlen( pPath );
|
|
|
|
// If we don't have a path separator at the end of the path, put one there
|
|
if ( ( pPath[len-1] != '\\' ) && ( pPath[len-1] != '/' ) )
|
|
{
|
|
V_strncat( pPath, CORRECT_PATH_SEPARATOR_S, MAX_PATH );
|
|
}
|
|
}
|
|
|
|
|
|
int main(int argc, char* argv[])
|
|
{
|
|
if ( argc < 2 )
|
|
{
|
|
printf( "============================================================\n" );
|
|
printf( " Please provide full path to shader directory. For example:\n" );
|
|
printf( " U:\\piston\\staging\\src\\materialsystem\\stdshaders\\ \n");
|
|
printf( "============================================================\n" );
|
|
printf( " remoteshadercompiler will now exit!!! \n" );
|
|
printf( "============================================================\n" );
|
|
return 0;
|
|
}
|
|
|
|
printf( "========================================================\n");
|
|
printf( "Remote shader compiler is running. Press ESCAPE to exit\n" );
|
|
printf( "========================================================\n");
|
|
|
|
// If we have a path specified on the commandline, we expect
|
|
// that the remote machine is going to send base filenames only
|
|
// and that we'll want to strcat this path onto the filename from the worker.
|
|
//
|
|
// For example, if you have your shader source on your Windows machine, you can use something like this:
|
|
//
|
|
// U:\piston\staging\src\materialsystem\stdshaders\
|
|
//
|
|
strcpy( g_pPathBase, argv[1] );
|
|
|
|
if ( argc == 3 )
|
|
{
|
|
g_bPrintDisassembly = true;
|
|
}
|
|
|
|
CheckPath( g_pPathBase );
|
|
|
|
// Kick off compile server thread
|
|
_beginthread( ServerThread, 0, NULL );
|
|
|
|
// Spin until escape
|
|
while( _getch() != 27 )
|
|
;
|
|
|
|
return 0;
|
|
}
|