From f56bb35301836e56582a575a75864392a0177875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20P=2E=20Tjern=C3=B8?= Date: Mon, 2 Dec 2013 19:31:46 -0800 Subject: Fix line endings. WHAMMY. --- mp/src/utils/common/ISQLDBReplyTarget.h | 58 +- mp/src/utils/common/MySqlDatabase.cpp | 384 +- mp/src/utils/common/MySqlDatabase.h | 208 +- mp/src/utils/common/bsplib.cpp | 10128 +++++++++++++-------------- mp/src/utils/common/bsplib.h | 808 +-- mp/src/utils/common/cmdlib.cpp | 2014 +++--- mp/src/utils/common/cmdlib.h | 354 +- mp/src/utils/common/consolewnd.cpp | 666 +- mp/src/utils/common/consolewnd.h | 90 +- mp/src/utils/common/filesystem_tools.cpp | 418 +- mp/src/utils/common/filesystem_tools.h | 118 +- mp/src/utils/common/map_shared.cpp | 272 +- mp/src/utils/common/map_shared.h | 182 +- mp/src/utils/common/movie.h | 66 +- mp/src/utils/common/mpi_stats.cpp | 1676 ++--- mp/src/utils/common/mpi_stats.h | 118 +- mp/src/utils/common/mstristrip.cpp | 1860 ++--- mp/src/utils/common/mstristrip.h | 86 +- mp/src/utils/common/pacifier.cpp | 126 +- mp/src/utils/common/pacifier.h | 46 +- mp/src/utils/common/physdll.cpp | 62 +- mp/src/utils/common/physdll.h | 60 +- mp/src/utils/common/polylib.cpp | 1830 ++--- mp/src/utils/common/polylib.h | 156 +- mp/src/utils/common/qfiles.h | 84 +- mp/src/utils/common/scratchpad_helpers.cpp | 206 +- mp/src/utils/common/scratchpad_helpers.h | 50 +- mp/src/utils/common/scriplib.cpp | 2698 +++---- mp/src/utils/common/scriplib.h | 192 +- mp/src/utils/common/threads.cpp | 514 +- mp/src/utils/common/threads.h | 130 +- mp/src/utils/common/tools_minidump.cpp | 122 +- mp/src/utils/common/tools_minidump.h | 70 +- mp/src/utils/common/utilmatlib.cpp | 368 +- mp/src/utils/common/utilmatlib.h | 82 +- mp/src/utils/common/vmpi_tools_shared.cpp | 748 +- mp/src/utils/common/vmpi_tools_shared.h | 90 +- mp/src/utils/common/wadlib.c | 668 +- mp/src/utils/common/wadlib.h | 92 +- 39 files changed, 13950 insertions(+), 13950 deletions(-) (limited to 'mp/src/utils/common') diff --git a/mp/src/utils/common/ISQLDBReplyTarget.h b/mp/src/utils/common/ISQLDBReplyTarget.h index 31406368..9049a5c7 100644 --- a/mp/src/utils/common/ISQLDBReplyTarget.h +++ b/mp/src/utils/common/ISQLDBReplyTarget.h @@ -1,29 +1,29 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#ifndef ISQLDLREPLYTARGET_H -#define ISQLDLREPLYTARGET_H -#ifdef _WIN32 -#pragma once -#endif - -//----------------------------------------------------------------------------- -// Purpose: Interface to handle results of SQL queries -//----------------------------------------------------------------------------- -class ISQLDBReplyTarget -{ -public: - // handles a response from the database - virtual void SQLDBResponse(int cmdID, int returnState, int returnVal, void *data) = 0; - - // called from a seperate thread; tells the reply target that a message is waiting for it - virtual void WakeUp() = 0; - -}; - - -#endif // ISQLDLREPLYTARGET_H +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef ISQLDLREPLYTARGET_H +#define ISQLDLREPLYTARGET_H +#ifdef _WIN32 +#pragma once +#endif + +//----------------------------------------------------------------------------- +// Purpose: Interface to handle results of SQL queries +//----------------------------------------------------------------------------- +class ISQLDBReplyTarget +{ +public: + // handles a response from the database + virtual void SQLDBResponse(int cmdID, int returnState, int returnVal, void *data) = 0; + + // called from a seperate thread; tells the reply target that a message is waiting for it + virtual void WakeUp() = 0; + +}; + + +#endif // ISQLDLREPLYTARGET_H diff --git a/mp/src/utils/common/MySqlDatabase.cpp b/mp/src/utils/common/MySqlDatabase.cpp index 46d8a4b9..2558ba08 100644 --- a/mp/src/utils/common/MySqlDatabase.cpp +++ b/mp/src/utils/common/MySqlDatabase.cpp @@ -1,192 +1,192 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include "MySqlDatabase.h" - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -CMySqlDatabase::CMySqlDatabase() -{ -} - -//----------------------------------------------------------------------------- -// Purpose: Destructor -// blocks until db process thread has stopped -//----------------------------------------------------------------------------- -CMySqlDatabase::~CMySqlDatabase() -{ - // flag the thread to stop - m_bRunThread = false; - - // pulse the thread to make it run - ::SetEvent(m_hEvent); - - // make sure it's done - ::EnterCriticalSection(&m_csThread); - ::LeaveCriticalSection(&m_csThread); -} - -//----------------------------------------------------------------------------- -// Purpose: Thread access function -//----------------------------------------------------------------------------- -static DWORD WINAPI staticThreadFunc(void *param) -{ - ((CMySqlDatabase *)param)->RunThread(); - return 0; -} - -//----------------------------------------------------------------------------- -// Purpose: Establishes connection to the database and sets up this object to handle db command -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CMySqlDatabase::Initialize() -{ - // prepare critical sections - //!! need to download SDK and replace these with InitializeCriticalSectionAndSpinCount() calls - ::InitializeCriticalSection(&m_csThread); - ::InitializeCriticalSection(&m_csInQueue); - ::InitializeCriticalSection(&m_csOutQueue); - ::InitializeCriticalSection(&m_csDBAccess); - - // initialize wait calls - m_hEvent = ::CreateEvent(NULL, false, true, NULL); - - // start the DB-access thread - m_bRunThread = true; - - unsigned long threadID; - ::CreateThread(NULL, 0, staticThreadFunc, this, 0, &threadID); - - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: Main thread loop -//----------------------------------------------------------------------------- -void CMySqlDatabase::RunThread() -{ - ::EnterCriticalSection(&m_csThread); - while (m_bRunThread) - { - if (m_InQueue.Count() > 0) - { - // get a dispatched DB request - ::EnterCriticalSection(&m_csInQueue); - - // pop the front of the queue - int headIndex = m_InQueue.Head(); - msg_t msg = m_InQueue[headIndex]; - m_InQueue.Remove(headIndex); - - ::LeaveCriticalSection(&m_csInQueue); - - ::EnterCriticalSection(&m_csDBAccess); - - // run sqldb command - msg.result = msg.cmd->RunCommand(); - - ::LeaveCriticalSection(&m_csDBAccess); - - if (msg.replyTarget) - { - // put the results in the outgoing queue - ::EnterCriticalSection(&m_csOutQueue); - m_OutQueue.AddToTail(msg); - ::LeaveCriticalSection(&m_csOutQueue); - - // wake up out queue - msg.replyTarget->WakeUp(); - } - else - { - // there is no return data from the call, so kill the object now - msg.cmd->deleteThis(); - } - } - else - { - // nothing in incoming queue, so wait until we get the signal - ::WaitForSingleObject(m_hEvent, INFINITE); - } - - // check the size of the outqueue; if it's getting too big, sleep to let the main thread catch up - if (m_OutQueue.Count() > 50) - { - ::Sleep(2); - } - } - ::LeaveCriticalSection(&m_csThread); -} - -//----------------------------------------------------------------------------- -// Purpose: Adds a database command to the queue, and wakes the db thread -//----------------------------------------------------------------------------- -void CMySqlDatabase::AddCommandToQueue(ISQLDBCommand *cmd, ISQLDBReplyTarget *replyTarget, int returnState) -{ - ::EnterCriticalSection(&m_csInQueue); - - // add to the queue - msg_t msg = { cmd, replyTarget, 0, returnState }; - m_InQueue.AddToTail(msg); - - ::LeaveCriticalSection(&m_csInQueue); - - // signal the thread to start running - ::SetEvent(m_hEvent); -} - -//----------------------------------------------------------------------------- -// Purpose: Dispatches responses to SQLDB queries -//----------------------------------------------------------------------------- -bool CMySqlDatabase::RunFrame() -{ - bool doneWork = false; - - while (m_OutQueue.Count() > 0) - { - ::EnterCriticalSection(&m_csOutQueue); - - // pop the first item in the queue - int headIndex = m_OutQueue.Head(); - msg_t msg = m_OutQueue[headIndex]; - m_OutQueue.Remove(headIndex); - - ::LeaveCriticalSection(&m_csOutQueue); - - // run result - if (msg.replyTarget) - { - msg.replyTarget->SQLDBResponse(msg.cmd->GetID(), msg.returnState, msg.result, msg.cmd->GetReturnData()); - - // kill command - // it would be a good optimization to be able to reuse these - msg.cmd->deleteThis(); - } - - doneWork = true; - } - - return doneWork; -} - -//----------------------------------------------------------------------------- -// Purpose: load info - returns the number of sql db queries waiting to be processed -//----------------------------------------------------------------------------- -int CMySqlDatabase::QueriesInOutQueue() -{ - // the queue names are from the DB point of view, not the server - thus the reversal - return m_InQueue.Count(); -} - -//----------------------------------------------------------------------------- -// Purpose: number of queries finished processing, waiting to be responded to -//----------------------------------------------------------------------------- -int CMySqlDatabase::QueriesInFinishedQueue() -{ - return m_OutQueue.Count(); -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "MySqlDatabase.h" + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CMySqlDatabase::CMySqlDatabase() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +// blocks until db process thread has stopped +//----------------------------------------------------------------------------- +CMySqlDatabase::~CMySqlDatabase() +{ + // flag the thread to stop + m_bRunThread = false; + + // pulse the thread to make it run + ::SetEvent(m_hEvent); + + // make sure it's done + ::EnterCriticalSection(&m_csThread); + ::LeaveCriticalSection(&m_csThread); +} + +//----------------------------------------------------------------------------- +// Purpose: Thread access function +//----------------------------------------------------------------------------- +static DWORD WINAPI staticThreadFunc(void *param) +{ + ((CMySqlDatabase *)param)->RunThread(); + return 0; +} + +//----------------------------------------------------------------------------- +// Purpose: Establishes connection to the database and sets up this object to handle db command +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CMySqlDatabase::Initialize() +{ + // prepare critical sections + //!! need to download SDK and replace these with InitializeCriticalSectionAndSpinCount() calls + ::InitializeCriticalSection(&m_csThread); + ::InitializeCriticalSection(&m_csInQueue); + ::InitializeCriticalSection(&m_csOutQueue); + ::InitializeCriticalSection(&m_csDBAccess); + + // initialize wait calls + m_hEvent = ::CreateEvent(NULL, false, true, NULL); + + // start the DB-access thread + m_bRunThread = true; + + unsigned long threadID; + ::CreateThread(NULL, 0, staticThreadFunc, this, 0, &threadID); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Main thread loop +//----------------------------------------------------------------------------- +void CMySqlDatabase::RunThread() +{ + ::EnterCriticalSection(&m_csThread); + while (m_bRunThread) + { + if (m_InQueue.Count() > 0) + { + // get a dispatched DB request + ::EnterCriticalSection(&m_csInQueue); + + // pop the front of the queue + int headIndex = m_InQueue.Head(); + msg_t msg = m_InQueue[headIndex]; + m_InQueue.Remove(headIndex); + + ::LeaveCriticalSection(&m_csInQueue); + + ::EnterCriticalSection(&m_csDBAccess); + + // run sqldb command + msg.result = msg.cmd->RunCommand(); + + ::LeaveCriticalSection(&m_csDBAccess); + + if (msg.replyTarget) + { + // put the results in the outgoing queue + ::EnterCriticalSection(&m_csOutQueue); + m_OutQueue.AddToTail(msg); + ::LeaveCriticalSection(&m_csOutQueue); + + // wake up out queue + msg.replyTarget->WakeUp(); + } + else + { + // there is no return data from the call, so kill the object now + msg.cmd->deleteThis(); + } + } + else + { + // nothing in incoming queue, so wait until we get the signal + ::WaitForSingleObject(m_hEvent, INFINITE); + } + + // check the size of the outqueue; if it's getting too big, sleep to let the main thread catch up + if (m_OutQueue.Count() > 50) + { + ::Sleep(2); + } + } + ::LeaveCriticalSection(&m_csThread); +} + +//----------------------------------------------------------------------------- +// Purpose: Adds a database command to the queue, and wakes the db thread +//----------------------------------------------------------------------------- +void CMySqlDatabase::AddCommandToQueue(ISQLDBCommand *cmd, ISQLDBReplyTarget *replyTarget, int returnState) +{ + ::EnterCriticalSection(&m_csInQueue); + + // add to the queue + msg_t msg = { cmd, replyTarget, 0, returnState }; + m_InQueue.AddToTail(msg); + + ::LeaveCriticalSection(&m_csInQueue); + + // signal the thread to start running + ::SetEvent(m_hEvent); +} + +//----------------------------------------------------------------------------- +// Purpose: Dispatches responses to SQLDB queries +//----------------------------------------------------------------------------- +bool CMySqlDatabase::RunFrame() +{ + bool doneWork = false; + + while (m_OutQueue.Count() > 0) + { + ::EnterCriticalSection(&m_csOutQueue); + + // pop the first item in the queue + int headIndex = m_OutQueue.Head(); + msg_t msg = m_OutQueue[headIndex]; + m_OutQueue.Remove(headIndex); + + ::LeaveCriticalSection(&m_csOutQueue); + + // run result + if (msg.replyTarget) + { + msg.replyTarget->SQLDBResponse(msg.cmd->GetID(), msg.returnState, msg.result, msg.cmd->GetReturnData()); + + // kill command + // it would be a good optimization to be able to reuse these + msg.cmd->deleteThis(); + } + + doneWork = true; + } + + return doneWork; +} + +//----------------------------------------------------------------------------- +// Purpose: load info - returns the number of sql db queries waiting to be processed +//----------------------------------------------------------------------------- +int CMySqlDatabase::QueriesInOutQueue() +{ + // the queue names are from the DB point of view, not the server - thus the reversal + return m_InQueue.Count(); +} + +//----------------------------------------------------------------------------- +// Purpose: number of queries finished processing, waiting to be responded to +//----------------------------------------------------------------------------- +int CMySqlDatabase::QueriesInFinishedQueue() +{ + return m_OutQueue.Count(); +} diff --git a/mp/src/utils/common/MySqlDatabase.h b/mp/src/utils/common/MySqlDatabase.h index caa5855c..52517f6d 100644 --- a/mp/src/utils/common/MySqlDatabase.h +++ b/mp/src/utils/common/MySqlDatabase.h @@ -1,104 +1,104 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#ifndef MYSQLDATABASE_H -#define MYSQLDATABASE_H -#ifdef _WIN32 -#pragma once -#endif - -#include -#include "ISQLDBReplyTarget.h" -#include "utlvector.h" -#include "UtlLinkedList.h" - -class ISQLDBCommand; - -//----------------------------------------------------------------------------- -// Purpose: Generic MySQL accessing database -// Provides threaded I/O queue functionality for accessing a mysql db -//----------------------------------------------------------------------------- -class CMySqlDatabase -{ -public: - // constructor - CMySqlDatabase(); - ~CMySqlDatabase(); - - // initialization - must be called before this object can be used - bool Initialize(); - - // Dispatches responses to SQLDB queries - bool RunFrame(); - - // load info - returns the number of sql db queries waiting to be processed - virtual int QueriesInOutQueue(); - - // number of queries finished processing, waiting to be responded to - virtual int QueriesInFinishedQueue(); - - // activates the thread - void RunThread(); - - // command queues - void AddCommandToQueue(ISQLDBCommand *cmd, ISQLDBReplyTarget *replyTarget, int returnState = 0); - -private: - - // threading data - bool m_bRunThread; - CRITICAL_SECTION m_csThread; - CRITICAL_SECTION m_csInQueue; - CRITICAL_SECTION m_csOutQueue; - CRITICAL_SECTION m_csDBAccess; - - // wait event - HANDLE m_hEvent; - - struct msg_t - { - ISQLDBCommand *cmd; - ISQLDBReplyTarget *replyTarget; - int result; - int returnState; - }; - - // command queues - CUtlLinkedList m_InQueue; - CUtlLinkedList m_OutQueue; -}; - -class Connection; - -//----------------------------------------------------------------------------- -// Purpose: Interface to a command -//----------------------------------------------------------------------------- -class ISQLDBCommand -{ -public: - // makes the command run (blocking), returning the success code - virtual int RunCommand() = 0; - - // return data - virtual void *GetReturnData() { return NULL; } - - // returns the command ID - virtual int GetID() { return 0; } - - // gets information about the command for if it failed - virtual void GetDebugInfo(char *buf, int bufSize) { buf[0] = 0; } - - // use to delete - virtual void deleteThis() = 0; - -protected: - // protected destructor, so that it has to be deleted through deleteThis() - virtual ~ISQLDBCommand() {} -}; - - -#endif // MYSQLDATABASE_H +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef MYSQLDATABASE_H +#define MYSQLDATABASE_H +#ifdef _WIN32 +#pragma once +#endif + +#include +#include "ISQLDBReplyTarget.h" +#include "utlvector.h" +#include "UtlLinkedList.h" + +class ISQLDBCommand; + +//----------------------------------------------------------------------------- +// Purpose: Generic MySQL accessing database +// Provides threaded I/O queue functionality for accessing a mysql db +//----------------------------------------------------------------------------- +class CMySqlDatabase +{ +public: + // constructor + CMySqlDatabase(); + ~CMySqlDatabase(); + + // initialization - must be called before this object can be used + bool Initialize(); + + // Dispatches responses to SQLDB queries + bool RunFrame(); + + // load info - returns the number of sql db queries waiting to be processed + virtual int QueriesInOutQueue(); + + // number of queries finished processing, waiting to be responded to + virtual int QueriesInFinishedQueue(); + + // activates the thread + void RunThread(); + + // command queues + void AddCommandToQueue(ISQLDBCommand *cmd, ISQLDBReplyTarget *replyTarget, int returnState = 0); + +private: + + // threading data + bool m_bRunThread; + CRITICAL_SECTION m_csThread; + CRITICAL_SECTION m_csInQueue; + CRITICAL_SECTION m_csOutQueue; + CRITICAL_SECTION m_csDBAccess; + + // wait event + HANDLE m_hEvent; + + struct msg_t + { + ISQLDBCommand *cmd; + ISQLDBReplyTarget *replyTarget; + int result; + int returnState; + }; + + // command queues + CUtlLinkedList m_InQueue; + CUtlLinkedList m_OutQueue; +}; + +class Connection; + +//----------------------------------------------------------------------------- +// Purpose: Interface to a command +//----------------------------------------------------------------------------- +class ISQLDBCommand +{ +public: + // makes the command run (blocking), returning the success code + virtual int RunCommand() = 0; + + // return data + virtual void *GetReturnData() { return NULL; } + + // returns the command ID + virtual int GetID() { return 0; } + + // gets information about the command for if it failed + virtual void GetDebugInfo(char *buf, int bufSize) { buf[0] = 0; } + + // use to delete + virtual void deleteThis() = 0; + +protected: + // protected destructor, so that it has to be deleted through deleteThis() + virtual ~ISQLDBCommand() {} +}; + + +#endif // MYSQLDATABASE_H diff --git a/mp/src/utils/common/bsplib.cpp b/mp/src/utils/common/bsplib.cpp index 84d1a1d0..c3ad433e 100644 --- a/mp/src/utils/common/bsplib.cpp +++ b/mp/src/utils/common/bsplib.cpp @@ -1,5064 +1,5064 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Revision: $ -// $NoKeywords: $ -//=============================================================================// - -#include "cmdlib.h" -#include "mathlib/mathlib.h" -#include "bsplib.h" -#include "zip_utils.h" -#include "scriplib.h" -#include "utllinkedlist.h" -#include "bsptreedata.h" -#include "cmodel.h" -#include "gamebspfile.h" -#include "materialsystem/imaterial.h" -#include "materialsystem/hardwareverts.h" -#include "utlbuffer.h" -#include "utlrbtree.h" -#include "utlsymbol.h" -#include "utlstring.h" -#include "checksum_crc.h" -#include "physdll.h" -#include "tier0/dbg.h" -#include "lumpfiles.h" -#include "vtf/vtf.h" - -//============================================================================= - -// Boundary each lump should be aligned to -#define LUMP_ALIGNMENT 4 - -// Data descriptions for byte swapping - only needed -// for structures that are written to file for use by the game. -BEGIN_BYTESWAP_DATADESC( dheader_t ) - DEFINE_FIELD( ident, FIELD_INTEGER ), - DEFINE_FIELD( version, FIELD_INTEGER ), - DEFINE_EMBEDDED_ARRAY( lumps, HEADER_LUMPS ), - DEFINE_FIELD( mapRevision, FIELD_INTEGER ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( lump_t ) - DEFINE_FIELD( fileofs, FIELD_INTEGER ), - DEFINE_FIELD( filelen, FIELD_INTEGER ), - DEFINE_FIELD( version, FIELD_INTEGER ), - DEFINE_ARRAY( fourCC, FIELD_CHARACTER, 4 ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( dflagslump_t ) - DEFINE_FIELD( m_LevelFlags, FIELD_INTEGER ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( dplane_t ) - DEFINE_FIELD( normal, FIELD_VECTOR ), - DEFINE_FIELD( dist, FIELD_FLOAT ), - DEFINE_FIELD( type, FIELD_INTEGER ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( dleaf_version_0_t ) - DEFINE_FIELD( contents, FIELD_INTEGER ), - DEFINE_FIELD( cluster, FIELD_SHORT ), - DEFINE_BITFIELD( bf, FIELD_SHORT, 16 ), - DEFINE_ARRAY( mins, FIELD_SHORT, 3 ), - DEFINE_ARRAY( maxs, FIELD_SHORT, 3 ), - DEFINE_FIELD( firstleafface, FIELD_SHORT ), - DEFINE_FIELD( numleaffaces, FIELD_SHORT ), - DEFINE_FIELD( firstleafbrush, FIELD_SHORT ), - DEFINE_FIELD( numleafbrushes, FIELD_SHORT ), - DEFINE_FIELD( leafWaterDataID, FIELD_SHORT ), - DEFINE_EMBEDDED( m_AmbientLighting ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( dleaf_t ) - DEFINE_FIELD( contents, FIELD_INTEGER ), - DEFINE_FIELD( cluster, FIELD_SHORT ), - DEFINE_BITFIELD( bf, FIELD_SHORT, 16 ), - DEFINE_ARRAY( mins, FIELD_SHORT, 3 ), - DEFINE_ARRAY( maxs, FIELD_SHORT, 3 ), - DEFINE_FIELD( firstleafface, FIELD_SHORT ), - DEFINE_FIELD( numleaffaces, FIELD_SHORT ), - DEFINE_FIELD( firstleafbrush, FIELD_SHORT ), - DEFINE_FIELD( numleafbrushes, FIELD_SHORT ), - DEFINE_FIELD( leafWaterDataID, FIELD_SHORT ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( CompressedLightCube ) // array of 6 ColorRGBExp32 (3 bytes and 1 char) - DEFINE_ARRAY( m_Color, FIELD_CHARACTER, 6 * sizeof(ColorRGBExp32) ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( dleafambientindex_t ) - DEFINE_FIELD( ambientSampleCount, FIELD_SHORT ), - DEFINE_FIELD( firstAmbientSample, FIELD_SHORT ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( dleafambientlighting_t ) // array of 6 ColorRGBExp32 (3 bytes and 1 char) - DEFINE_EMBEDDED( cube ), - DEFINE_FIELD( x, FIELD_CHARACTER ), - DEFINE_FIELD( y, FIELD_CHARACTER ), - DEFINE_FIELD( z, FIELD_CHARACTER ), - DEFINE_FIELD( pad, FIELD_CHARACTER ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( dvertex_t ) - DEFINE_FIELD( point, FIELD_VECTOR ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( dnode_t ) - DEFINE_FIELD( planenum, FIELD_INTEGER ), - DEFINE_ARRAY( children, FIELD_INTEGER, 2 ), - DEFINE_ARRAY( mins, FIELD_SHORT, 3 ), - DEFINE_ARRAY( maxs, FIELD_SHORT, 3 ), - DEFINE_FIELD( firstface, FIELD_SHORT ), - DEFINE_FIELD( numfaces, FIELD_SHORT ), - DEFINE_FIELD( area, FIELD_SHORT ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( texinfo_t ) - DEFINE_ARRAY( textureVecsTexelsPerWorldUnits, FIELD_FLOAT, 2 * 4 ), - DEFINE_ARRAY( lightmapVecsLuxelsPerWorldUnits, FIELD_FLOAT, 2 * 4 ), - DEFINE_FIELD( flags, FIELD_INTEGER ), - DEFINE_FIELD( texdata, FIELD_INTEGER ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( dtexdata_t ) - DEFINE_FIELD( reflectivity, FIELD_VECTOR ), - DEFINE_FIELD( nameStringTableID, FIELD_INTEGER ), - DEFINE_FIELD( width, FIELD_INTEGER ), - DEFINE_FIELD( height, FIELD_INTEGER ), - DEFINE_FIELD( view_width, FIELD_INTEGER ), - DEFINE_FIELD( view_height, FIELD_INTEGER ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( ddispinfo_t ) - DEFINE_FIELD( startPosition, FIELD_VECTOR ), - DEFINE_FIELD( m_iDispVertStart, FIELD_INTEGER ), - DEFINE_FIELD( m_iDispTriStart, FIELD_INTEGER ), - DEFINE_FIELD( power, FIELD_INTEGER ), - DEFINE_FIELD( minTess, FIELD_INTEGER ), - DEFINE_FIELD( smoothingAngle, FIELD_FLOAT ), - DEFINE_FIELD( contents, FIELD_INTEGER ), - DEFINE_FIELD( m_iMapFace, FIELD_SHORT ), - DEFINE_FIELD( m_iLightmapAlphaStart, FIELD_INTEGER ), - DEFINE_FIELD( m_iLightmapSamplePositionStart, FIELD_INTEGER ), - DEFINE_EMBEDDED_ARRAY( m_EdgeNeighbors, 4 ), - DEFINE_EMBEDDED_ARRAY( m_CornerNeighbors, 4 ), - DEFINE_ARRAY( m_AllowedVerts, FIELD_INTEGER, ddispinfo_t::ALLOWEDVERTS_SIZE ), // unsigned long -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( CDispNeighbor ) - DEFINE_EMBEDDED_ARRAY( m_SubNeighbors, 2 ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( CDispCornerNeighbors ) - DEFINE_ARRAY( m_Neighbors, FIELD_SHORT, MAX_DISP_CORNER_NEIGHBORS ), - DEFINE_FIELD( m_nNeighbors, FIELD_CHARACTER ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( CDispSubNeighbor ) - DEFINE_FIELD( m_iNeighbor, FIELD_SHORT ), - DEFINE_FIELD( m_NeighborOrientation, FIELD_CHARACTER ), - DEFINE_FIELD( m_Span, FIELD_CHARACTER ), - DEFINE_FIELD( m_NeighborSpan, FIELD_CHARACTER ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( CDispVert ) - DEFINE_FIELD( m_vVector, FIELD_VECTOR ), - DEFINE_FIELD( m_flDist, FIELD_FLOAT ), - DEFINE_FIELD( m_flAlpha, FIELD_FLOAT ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( CDispTri ) - DEFINE_FIELD( m_uiTags, FIELD_SHORT ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( CFaceMacroTextureInfo ) - DEFINE_FIELD( m_MacroTextureNameID, FIELD_SHORT ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( dprimitive_t ) - DEFINE_FIELD( type, FIELD_CHARACTER ), - DEFINE_FIELD( firstIndex, FIELD_SHORT ), - DEFINE_FIELD( indexCount, FIELD_SHORT ), - DEFINE_FIELD( firstVert, FIELD_SHORT ), - DEFINE_FIELD( vertCount, FIELD_SHORT ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( dprimvert_t ) - DEFINE_FIELD( pos, FIELD_VECTOR ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( dface_t ) - DEFINE_FIELD( planenum, FIELD_SHORT ), - DEFINE_FIELD( side, FIELD_CHARACTER ), - DEFINE_FIELD( onNode, FIELD_CHARACTER ), - DEFINE_FIELD( firstedge, FIELD_INTEGER ), - DEFINE_FIELD( numedges, FIELD_SHORT ), - DEFINE_FIELD( texinfo, FIELD_SHORT ), - DEFINE_FIELD( dispinfo, FIELD_SHORT ), - DEFINE_FIELD( surfaceFogVolumeID, FIELD_SHORT ), - DEFINE_ARRAY( styles, FIELD_CHARACTER, MAXLIGHTMAPS ), - DEFINE_FIELD( lightofs, FIELD_INTEGER ), - DEFINE_FIELD( area, FIELD_FLOAT ), - DEFINE_ARRAY( m_LightmapTextureMinsInLuxels, FIELD_INTEGER, 2 ), - DEFINE_ARRAY( m_LightmapTextureSizeInLuxels, FIELD_INTEGER, 2 ), - DEFINE_FIELD( origFace, FIELD_INTEGER ), - DEFINE_FIELD( m_NumPrims, FIELD_SHORT ), - DEFINE_FIELD( firstPrimID, FIELD_SHORT ), - DEFINE_FIELD( smoothingGroups, FIELD_INTEGER ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( dfaceid_t ) - DEFINE_FIELD( hammerfaceid, FIELD_SHORT ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( dbrush_t ) - DEFINE_FIELD( firstside, FIELD_INTEGER ), - DEFINE_FIELD( numsides, FIELD_INTEGER ), - DEFINE_FIELD( contents, FIELD_INTEGER ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( dbrushside_t ) - DEFINE_FIELD( planenum, FIELD_SHORT ), - DEFINE_FIELD( texinfo, FIELD_SHORT ), - DEFINE_FIELD( dispinfo, FIELD_SHORT ), - DEFINE_FIELD( bevel, FIELD_SHORT ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( dedge_t ) - DEFINE_ARRAY( v, FIELD_SHORT, 2 ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( dmodel_t ) - DEFINE_FIELD( mins, FIELD_VECTOR ), - DEFINE_FIELD( maxs, FIELD_VECTOR ), - DEFINE_FIELD( origin, FIELD_VECTOR ), - DEFINE_FIELD( headnode, FIELD_INTEGER ), - DEFINE_FIELD( firstface, FIELD_INTEGER ), - DEFINE_FIELD( numfaces, FIELD_INTEGER ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( dphysmodel_t ) - DEFINE_FIELD( modelIndex, FIELD_INTEGER ), - DEFINE_FIELD( dataSize, FIELD_INTEGER ), - DEFINE_FIELD( keydataSize, FIELD_INTEGER ), - DEFINE_FIELD( solidCount, FIELD_INTEGER ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( dphysdisp_t ) - DEFINE_FIELD( numDisplacements, FIELD_SHORT ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( darea_t ) - DEFINE_FIELD( numareaportals, FIELD_INTEGER ), - DEFINE_FIELD( firstareaportal, FIELD_INTEGER ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( dareaportal_t ) - DEFINE_FIELD( m_PortalKey, FIELD_SHORT ), - DEFINE_FIELD( otherarea, FIELD_SHORT ), - DEFINE_FIELD( m_FirstClipPortalVert, FIELD_SHORT ), - DEFINE_FIELD( m_nClipPortalVerts, FIELD_SHORT ), - DEFINE_FIELD( planenum, FIELD_INTEGER ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( dworldlight_t ) - DEFINE_FIELD( origin, FIELD_VECTOR ), - DEFINE_FIELD( intensity, FIELD_VECTOR ), - DEFINE_FIELD( normal, FIELD_VECTOR ), - DEFINE_FIELD( cluster, FIELD_INTEGER ), - DEFINE_FIELD( type, FIELD_INTEGER ), // enumeration - DEFINE_FIELD( style, FIELD_INTEGER ), - DEFINE_FIELD( stopdot, FIELD_FLOAT ), - DEFINE_FIELD( stopdot2, FIELD_FLOAT ), - DEFINE_FIELD( exponent, FIELD_FLOAT ), - DEFINE_FIELD( radius, FIELD_FLOAT ), - DEFINE_FIELD( constant_attn, FIELD_FLOAT ), - DEFINE_FIELD( linear_attn, FIELD_FLOAT ), - DEFINE_FIELD( quadratic_attn, FIELD_FLOAT ), - DEFINE_FIELD( flags, FIELD_INTEGER ), - DEFINE_FIELD( texinfo, FIELD_INTEGER ), - DEFINE_FIELD( owner, FIELD_INTEGER ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( dleafwaterdata_t ) - DEFINE_FIELD( surfaceZ, FIELD_FLOAT ), - DEFINE_FIELD( minZ, FIELD_FLOAT ), - DEFINE_FIELD( surfaceTexInfoID, FIELD_SHORT ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( doccluderdata_t ) - DEFINE_FIELD( flags, FIELD_INTEGER ), - DEFINE_FIELD( firstpoly, FIELD_INTEGER ), - DEFINE_FIELD( polycount, FIELD_INTEGER ), - DEFINE_FIELD( mins, FIELD_VECTOR ), - DEFINE_FIELD( maxs, FIELD_VECTOR ), - DEFINE_FIELD( area, FIELD_INTEGER ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( doccluderpolydata_t ) - DEFINE_FIELD( firstvertexindex, FIELD_INTEGER ), - DEFINE_FIELD( vertexcount, FIELD_INTEGER ), - DEFINE_FIELD( planenum, FIELD_INTEGER ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( dcubemapsample_t ) - DEFINE_ARRAY( origin, FIELD_INTEGER, 3 ), - DEFINE_FIELD( size, FIELD_CHARACTER ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( doverlay_t ) - DEFINE_FIELD( nId, FIELD_INTEGER ), - DEFINE_FIELD( nTexInfo, FIELD_SHORT ), - DEFINE_FIELD( m_nFaceCountAndRenderOrder, FIELD_SHORT ), - DEFINE_ARRAY( aFaces, FIELD_INTEGER, OVERLAY_BSP_FACE_COUNT ), - DEFINE_ARRAY( flU, FIELD_FLOAT, 2 ), - DEFINE_ARRAY( flV, FIELD_FLOAT, 2 ), - DEFINE_ARRAY( vecUVPoints, FIELD_VECTOR, 4 ), - DEFINE_FIELD( vecOrigin, FIELD_VECTOR ), - DEFINE_FIELD( vecBasisNormal, FIELD_VECTOR ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( dwateroverlay_t ) - DEFINE_FIELD( nId, FIELD_INTEGER ), - DEFINE_FIELD( nTexInfo, FIELD_SHORT ), - DEFINE_FIELD( m_nFaceCountAndRenderOrder, FIELD_SHORT ), - DEFINE_ARRAY( aFaces, FIELD_INTEGER, WATEROVERLAY_BSP_FACE_COUNT ), - DEFINE_ARRAY( flU, FIELD_FLOAT, 2 ), - DEFINE_ARRAY( flV, FIELD_FLOAT, 2 ), - DEFINE_ARRAY( vecUVPoints, FIELD_VECTOR, 4 ), - DEFINE_FIELD( vecOrigin, FIELD_VECTOR ), - DEFINE_FIELD( vecBasisNormal, FIELD_VECTOR ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( doverlayfade_t ) - DEFINE_FIELD( flFadeDistMinSq, FIELD_FLOAT ), - DEFINE_FIELD( flFadeDistMaxSq, FIELD_FLOAT ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( dgamelumpheader_t ) - DEFINE_FIELD( lumpCount, FIELD_INTEGER ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( dgamelump_t ) - DEFINE_FIELD( id, FIELD_INTEGER ), // GameLumpId_t - DEFINE_FIELD( flags, FIELD_SHORT ), - DEFINE_FIELD( version, FIELD_SHORT ), - DEFINE_FIELD( fileofs, FIELD_INTEGER ), - DEFINE_FIELD( filelen, FIELD_INTEGER ), -END_BYTESWAP_DATADESC() - -// From gamebspfile.h -BEGIN_BYTESWAP_DATADESC( StaticPropDictLump_t ) - DEFINE_ARRAY( m_Name, FIELD_CHARACTER, STATIC_PROP_NAME_LENGTH ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( StaticPropLump_t ) - DEFINE_FIELD( m_Origin, FIELD_VECTOR ), - DEFINE_FIELD( m_Angles, FIELD_VECTOR ), // QAngle - DEFINE_FIELD( m_PropType, FIELD_SHORT ), - DEFINE_FIELD( m_FirstLeaf, FIELD_SHORT ), - DEFINE_FIELD( m_LeafCount, FIELD_SHORT ), - DEFINE_FIELD( m_Solid, FIELD_CHARACTER ), - DEFINE_FIELD( m_Flags, FIELD_CHARACTER ), - DEFINE_FIELD( m_Skin, FIELD_INTEGER ), - DEFINE_FIELD( m_FadeMinDist, FIELD_FLOAT ), - DEFINE_FIELD( m_FadeMaxDist, FIELD_FLOAT ), - DEFINE_FIELD( m_LightingOrigin, FIELD_VECTOR ), - DEFINE_FIELD( m_flForcedFadeScale, FIELD_FLOAT ), - DEFINE_FIELD( m_nMinDXLevel, FIELD_SHORT ), - DEFINE_FIELD( m_nMaxDXLevel, FIELD_SHORT ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( StaticPropLumpV4_t ) - DEFINE_FIELD( m_Origin, FIELD_VECTOR ), - DEFINE_FIELD( m_Angles, FIELD_VECTOR ), // QAngle - DEFINE_FIELD( m_PropType, FIELD_SHORT ), - DEFINE_FIELD( m_FirstLeaf, FIELD_SHORT ), - DEFINE_FIELD( m_LeafCount, FIELD_SHORT ), - DEFINE_FIELD( m_Solid, FIELD_CHARACTER ), - DEFINE_FIELD( m_Flags, FIELD_CHARACTER ), - DEFINE_FIELD( m_Skin, FIELD_INTEGER ), - DEFINE_FIELD( m_FadeMinDist, FIELD_FLOAT ), - DEFINE_FIELD( m_FadeMaxDist, FIELD_FLOAT ), - DEFINE_FIELD( m_LightingOrigin, FIELD_VECTOR ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( StaticPropLumpV5_t ) - DEFINE_FIELD( m_Origin, FIELD_VECTOR ), - DEFINE_FIELD( m_Angles, FIELD_VECTOR ), // QAngle - DEFINE_FIELD( m_PropType, FIELD_SHORT ), - DEFINE_FIELD( m_FirstLeaf, FIELD_SHORT ), - DEFINE_FIELD( m_LeafCount, FIELD_SHORT ), - DEFINE_FIELD( m_Solid, FIELD_CHARACTER ), - DEFINE_FIELD( m_Flags, FIELD_CHARACTER ), - DEFINE_FIELD( m_Skin, FIELD_INTEGER ), - DEFINE_FIELD( m_FadeMinDist, FIELD_FLOAT ), - DEFINE_FIELD( m_FadeMaxDist, FIELD_FLOAT ), - DEFINE_FIELD( m_LightingOrigin, FIELD_VECTOR ), - DEFINE_FIELD( m_flForcedFadeScale, FIELD_FLOAT ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( StaticPropLeafLump_t ) - DEFINE_FIELD( m_Leaf, FIELD_SHORT ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( DetailObjectDictLump_t ) - DEFINE_ARRAY( m_Name, FIELD_CHARACTER, DETAIL_NAME_LENGTH ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( DetailObjectLump_t ) - DEFINE_FIELD( m_Origin, FIELD_VECTOR ), - DEFINE_FIELD( m_Angles, FIELD_VECTOR ), // QAngle - DEFINE_FIELD( m_DetailModel, FIELD_SHORT ), - DEFINE_FIELD( m_Leaf, FIELD_SHORT ), - DEFINE_ARRAY( m_Lighting, FIELD_CHARACTER, 4 ), // ColorRGBExp32 - DEFINE_FIELD( m_LightStyles, FIELD_INTEGER ), - DEFINE_FIELD( m_LightStyleCount, FIELD_CHARACTER ), - DEFINE_FIELD( m_SwayAmount, FIELD_CHARACTER ), - DEFINE_FIELD( m_ShapeAngle, FIELD_CHARACTER ), - DEFINE_FIELD( m_ShapeSize, FIELD_CHARACTER ), - DEFINE_FIELD( m_Orientation, FIELD_CHARACTER ), - DEFINE_ARRAY( m_Padding2, FIELD_CHARACTER, 3 ), - DEFINE_FIELD( m_Type, FIELD_CHARACTER ), - DEFINE_ARRAY( m_Padding3, FIELD_CHARACTER, 3 ), - DEFINE_FIELD( m_flScale, FIELD_FLOAT ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( DetailSpriteDictLump_t ) - DEFINE_FIELD( m_UL, FIELD_VECTOR2D ), - DEFINE_FIELD( m_LR, FIELD_VECTOR2D ), - DEFINE_FIELD( m_TexUL, FIELD_VECTOR2D ), - DEFINE_FIELD( m_TexLR, FIELD_VECTOR2D ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( DetailPropLightstylesLump_t ) - DEFINE_ARRAY( m_Lighting, FIELD_CHARACTER, 4 ), // ColorRGBExp32 - DEFINE_FIELD( m_Style, FIELD_CHARACTER ), -END_BYTESWAP_DATADESC() - -// From vradstaticprops.h -namespace HardwareVerts -{ -BEGIN_BYTESWAP_DATADESC( MeshHeader_t ) - DEFINE_FIELD( m_nLod, FIELD_INTEGER ), - DEFINE_FIELD( m_nVertexes, FIELD_INTEGER ), - DEFINE_FIELD( m_nOffset, FIELD_INTEGER ), - DEFINE_ARRAY( m_nUnused, FIELD_INTEGER, 4 ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC( FileHeader_t ) - DEFINE_FIELD( m_nVersion, FIELD_INTEGER ), - DEFINE_FIELD( m_nChecksum, FIELD_INTEGER ), - DEFINE_FIELD( m_nVertexFlags, FIELD_INTEGER ), - DEFINE_FIELD( m_nVertexSize, FIELD_INTEGER ), - DEFINE_FIELD( m_nVertexes, FIELD_INTEGER ), - DEFINE_FIELD( m_nMeshes, FIELD_INTEGER ), - DEFINE_ARRAY( m_nUnused, FIELD_INTEGER, 4 ), -END_BYTESWAP_DATADESC() -} // end namespace - -static const char *s_LumpNames[] = { - "LUMP_ENTITIES", // 0 - "LUMP_PLANES", // 1 - "LUMP_TEXDATA", // 2 - "LUMP_VERTEXES", // 3 - "LUMP_VISIBILITY", // 4 - "LUMP_NODES", // 5 - "LUMP_TEXINFO", // 6 - "LUMP_FACES", // 7 - "LUMP_LIGHTING", // 8 - "LUMP_OCCLUSION", // 9 - "LUMP_LEAFS", // 10 - "LUMP_FACEIDS", // 11 - "LUMP_EDGES", // 12 - "LUMP_SURFEDGES", // 13 - "LUMP_MODELS", // 14 - "LUMP_WORLDLIGHTS", // 15 - "LUMP_LEAFFACES", // 16 - "LUMP_LEAFBRUSHES", // 17 - "LUMP_BRUSHES", // 18 - "LUMP_BRUSHSIDES", // 19 - "LUMP_AREAS", // 20 - "LUMP_AREAPORTALS", // 21 - "LUMP_UNUSED0", // 22 - "LUMP_UNUSED1", // 23 - "LUMP_UNUSED2", // 24 - "LUMP_UNUSED3", // 25 - "LUMP_DISPINFO", // 26 - "LUMP_ORIGINALFACES", // 27 - "LUMP_PHYSDISP", // 28 - "LUMP_PHYSCOLLIDE", // 29 - "LUMP_VERTNORMALS", // 30 - "LUMP_VERTNORMALINDICES", // 31 - "LUMP_DISP_LIGHTMAP_ALPHAS", // 32 - "LUMP_DISP_VERTS", // 33 - "LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS", // 34 - "LUMP_GAME_LUMP", // 35 - "LUMP_LEAFWATERDATA", // 36 - "LUMP_PRIMITIVES", // 37 - "LUMP_PRIMVERTS", // 38 - "LUMP_PRIMINDICES", // 39 - "LUMP_PAKFILE", // 40 - "LUMP_CLIPPORTALVERTS", // 41 - "LUMP_CUBEMAPS", // 42 - "LUMP_TEXDATA_STRING_DATA", // 43 - "LUMP_TEXDATA_STRING_TABLE", // 44 - "LUMP_OVERLAYS", // 45 - "LUMP_LEAFMINDISTTOWATER", // 46 - "LUMP_FACE_MACRO_TEXTURE_INFO", // 47 - "LUMP_DISP_TRIS", // 48 - "LUMP_PHYSCOLLIDESURFACE", // 49 - "LUMP_WATEROVERLAYS", // 50 - "LUMP_LEAF_AMBIENT_INDEX_HDR", // 51 - "LUMP_LEAF_AMBIENT_INDEX", // 52 - "LUMP_LIGHTING_HDR", // 53 - "LUMP_WORLDLIGHTS_HDR", // 54 - "LUMP_LEAF_AMBIENT_LIGHTING_HDR", // 55 - "LUMP_LEAF_AMBIENT_LIGHTING", // 56 - "LUMP_XZIPPAKFILE", // 57 - "LUMP_FACES_HDR", // 58 - "LUMP_MAP_FLAGS", // 59 - "LUMP_OVERLAY_FADES", // 60 -}; - -const char *GetLumpName( unsigned int lumpnum ) -{ - if ( lumpnum >= ARRAYSIZE( s_LumpNames ) ) - { - return "UNKNOWN"; - } - return s_LumpNames[lumpnum]; -} - -// "-hdr" tells us to use the HDR fields (if present) on the light sources. Also, tells us to write -// out the HDR lumps for lightmaps, ambient leaves, and lights sources. -bool g_bHDR = false; - -// Set to true to generate Xbox360 native output files -static bool g_bSwapOnLoad = false; -static bool g_bSwapOnWrite = false; - -VTFConvertFunc_t g_pVTFConvertFunc; -VHVFixupFunc_t g_pVHVFixupFunc; -CompressFunc_t g_pCompressFunc; - -CUtlVector< CUtlString > g_StaticPropNames; -CUtlVector< int > g_StaticPropInstances; - -CByteswap g_Swap; - -uint32 g_LevelFlags = 0; - -int nummodels; -dmodel_t dmodels[MAX_MAP_MODELS]; - -int visdatasize; -byte dvisdata[MAX_MAP_VISIBILITY]; -dvis_t *dvis = (dvis_t *)dvisdata; - -CUtlVector dlightdataHDR; -CUtlVector dlightdataLDR; -CUtlVector *pdlightdata = &dlightdataLDR; - -CUtlVector dentdata; - -int numleafs; -#if !defined( BSP_USE_LESS_MEMORY ) -dleaf_t dleafs[MAX_MAP_LEAFS]; -#else -dleaf_t *dleafs; -#endif - -CUtlVector g_LeafAmbientIndexLDR; -CUtlVector g_LeafAmbientIndexHDR; -CUtlVector *g_pLeafAmbientIndex = NULL; -CUtlVector g_LeafAmbientLightingLDR; -CUtlVector g_LeafAmbientLightingHDR; -CUtlVector *g_pLeafAmbientLighting = NULL; - -unsigned short g_LeafMinDistToWater[MAX_MAP_LEAFS]; - -int numplanes; -dplane_t dplanes[MAX_MAP_PLANES]; - -int numvertexes; -dvertex_t dvertexes[MAX_MAP_VERTS]; - -int g_numvertnormalindices; // dfaces reference these. These index g_vertnormals. -unsigned short g_vertnormalindices[MAX_MAP_VERTNORMALS]; - -int g_numvertnormals; -Vector g_vertnormals[MAX_MAP_VERTNORMALS]; - -int numnodes; -dnode_t dnodes[MAX_MAP_NODES]; - -CUtlVector texinfo( MAX_MAP_TEXINFO ); - -int numtexdata; -dtexdata_t dtexdata[MAX_MAP_TEXDATA]; - -// -// displacement map bsp file info: dispinfo -// -CUtlVector g_dispinfo; -CUtlVector g_DispVerts; -CUtlVector g_DispTris; -CUtlVector g_DispLightmapSamplePositions; // LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS - -int numorigfaces; -dface_t dorigfaces[MAX_MAP_FACES]; - -int g_numprimitives = 0; -dprimitive_t g_primitives[MAX_MAP_PRIMITIVES]; - -int g_numprimverts = 0; -dprimvert_t g_primverts[MAX_MAP_PRIMVERTS]; - -int g_numprimindices = 0; -unsigned short g_primindices[MAX_MAP_PRIMINDICES]; - -int numfaces; -dface_t dfaces[MAX_MAP_FACES]; - -int numfaceids; -CUtlVector dfaceids; - -int numfaces_hdr; -dface_t dfaces_hdr[MAX_MAP_FACES]; - -int numedges; -dedge_t dedges[MAX_MAP_EDGES]; - -int numleaffaces; -unsigned short dleaffaces[MAX_MAP_LEAFFACES]; - -int numleafbrushes; -unsigned short dleafbrushes[MAX_MAP_LEAFBRUSHES]; - -int numsurfedges; -int dsurfedges[MAX_MAP_SURFEDGES]; - -int numbrushes; -dbrush_t dbrushes[MAX_MAP_BRUSHES]; - -int numbrushsides; -dbrushside_t dbrushsides[MAX_MAP_BRUSHSIDES]; - -int numareas; -darea_t dareas[MAX_MAP_AREAS]; - -int numareaportals; -dareaportal_t dareaportals[MAX_MAP_AREAPORTALS]; - -int numworldlightsLDR; -dworldlight_t dworldlightsLDR[MAX_MAP_WORLDLIGHTS]; - -int numworldlightsHDR; -dworldlight_t dworldlightsHDR[MAX_MAP_WORLDLIGHTS]; - -int *pNumworldlights = &numworldlightsLDR; -dworldlight_t *dworldlights = dworldlightsLDR; - -int numleafwaterdata = 0; -dleafwaterdata_t dleafwaterdata[MAX_MAP_LEAFWATERDATA]; - -CUtlVector g_FaceMacroTextureInfos; - -Vector g_ClipPortalVerts[MAX_MAP_PORTALVERTS]; -int g_nClipPortalVerts; - -dcubemapsample_t g_CubemapSamples[MAX_MAP_CUBEMAPSAMPLES]; -int g_nCubemapSamples = 0; - -int g_nOverlayCount; -doverlay_t g_Overlays[MAX_MAP_OVERLAYS]; -doverlayfade_t g_OverlayFades[MAX_MAP_OVERLAYS]; - -int g_nWaterOverlayCount; -dwateroverlay_t g_WaterOverlays[MAX_MAP_WATEROVERLAYS]; - -CUtlVector g_TexDataStringData; -CUtlVector g_TexDataStringTable; - -byte *g_pPhysCollide = NULL; -int g_PhysCollideSize = 0; -int g_MapRevision = 0; - -byte *g_pPhysDisp = NULL; -int g_PhysDispSize = 0; - -CUtlVector g_OccluderData( 256, 256 ); -CUtlVector g_OccluderPolyData( 1024, 1024 ); -CUtlVector g_OccluderVertexIndices( 2048, 2048 ); - -template static void WriteData( T *pData, int count = 1 ); -template static void WriteData( int fieldType, T *pData, int count = 1 ); -template< class T > static void AddLump( int lumpnum, T *pData, int count, int version = 0 ); -template< class T > static void AddLump( int lumpnum, CUtlVector &data, int version = 0 ); - -dheader_t *g_pBSPHeader; -FileHandle_t g_hBSPFile; - -struct Lump_t -{ - void *pLumps[HEADER_LUMPS]; - int size[HEADER_LUMPS]; - bool bLumpParsed[HEADER_LUMPS]; -} g_Lumps; - -CGameLump g_GameLumps; - -static IZip *s_pakFile = 0; - -//----------------------------------------------------------------------------- -// Keep the file position aligned to an arbitrary boundary. -// Returns updated file position. -//----------------------------------------------------------------------------- -static unsigned int AlignFilePosition( FileHandle_t hFile, int alignment ) -{ - unsigned int currPosition = g_pFileSystem->Tell( hFile ); - - if ( alignment >= 2 ) - { - unsigned int newPosition = AlignValue( currPosition, alignment ); - unsigned int count = newPosition - currPosition; - if ( count ) - { - char *pBuffer; - char smallBuffer[4096]; - if ( count > sizeof( smallBuffer ) ) - { - pBuffer = (char *)malloc( count ); - } - else - { - pBuffer = smallBuffer; - } - - memset( pBuffer, 0, count ); - SafeWrite( hFile, pBuffer, count ); - - if ( pBuffer != smallBuffer ) - { - free( pBuffer ); - } - - currPosition = newPosition; - } - } - - return currPosition; -} - -//----------------------------------------------------------------------------- -// Purpose: // Get a pakfile instance -// Output : IZip* -//----------------------------------------------------------------------------- -IZip* GetPakFile( void ) -{ - if ( !s_pakFile ) - { - s_pakFile = IZip::CreateZip(); - } - return s_pakFile; -} - -//----------------------------------------------------------------------------- -// Purpose: Free the pak files -//----------------------------------------------------------------------------- -void ReleasePakFileLumps( void ) -{ - // Release the pak files - IZip::ReleaseZip( s_pakFile ); - s_pakFile = NULL; -} - -//----------------------------------------------------------------------------- -// Purpose: Set the sector alignment for all subsequent zip operations -//----------------------------------------------------------------------------- -void ForceAlignment( IZip *pak, bool bAlign, bool bCompatibleFormat, unsigned int alignmentSize ) -{ - pak->ForceAlignment( bAlign, bCompatibleFormat, alignmentSize ); -} - -//----------------------------------------------------------------------------- -// Purpose: Store data back out to .bsp file -//----------------------------------------------------------------------------- -static void WritePakFileLump( void ) -{ - CUtlBuffer buf( 0, 0 ); - GetPakFile()->ActivateByteSwapping( IsX360() ); - GetPakFile()->SaveToBuffer( buf ); - - // must respect pak file alignment - // pad up and ensure lump starts on same aligned boundary - AlignFilePosition( g_hBSPFile, GetPakFile()->GetAlignment() ); - - // Now store final buffers out to file - AddLump( LUMP_PAKFILE, (byte*)buf.Base(), buf.TellPut() ); -} - -//----------------------------------------------------------------------------- -// Purpose: Remove all entries -//----------------------------------------------------------------------------- -void ClearPakFile( IZip *pak ) -{ - pak->Reset(); -} - -//----------------------------------------------------------------------------- -// Purpose: Add file from disk to .bsp PAK lump -// Input : *relativename - -// *fullpath - -//----------------------------------------------------------------------------- -void AddFileToPak( IZip *pak, const char *relativename, const char *fullpath ) -{ - pak->AddFileToZip( relativename, fullpath ); -} - -//----------------------------------------------------------------------------- -// Purpose: Add buffer to .bsp PAK lump as named file -// Input : *relativename - -// *data - -// length - -//----------------------------------------------------------------------------- -void AddBufferToPak( IZip *pak, const char *pRelativeName, void *data, int length, bool bTextMode ) -{ - pak->AddBufferToZip( pRelativeName, data, length, bTextMode ); -} - -//----------------------------------------------------------------------------- -// Purpose: Check if a file already exists in the pack file. -// Input : *relativename - -//----------------------------------------------------------------------------- -bool FileExistsInPak( IZip *pak, const char *pRelativeName ) -{ - return pak->FileExistsInZip( pRelativeName ); -} - - -//----------------------------------------------------------------------------- -// Read a file from the pack file -//----------------------------------------------------------------------------- -bool ReadFileFromPak( IZip *pak, const char *pRelativeName, bool bTextMode, CUtlBuffer &buf ) -{ - return pak->ReadFileFromZip( pRelativeName, bTextMode, buf ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Remove file from .bsp PAK lump -// Input : *relativename - -//----------------------------------------------------------------------------- -void RemoveFileFromPak( IZip *pak, const char *relativename ) -{ - pak->RemoveFileFromZip( relativename ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Get next filename in directory -// Input : id, -1 to start, returns next id, or -1 at list conclusion -//----------------------------------------------------------------------------- -int GetNextFilename( IZip *pak, int id, char *pBuffer, int bufferSize, int &fileSize ) -{ - return pak->GetNextFilename( id, pBuffer, bufferSize, fileSize ); -} - -//----------------------------------------------------------------------------- -// Convert four-CC code to a handle + back -//----------------------------------------------------------------------------- -GameLumpHandle_t CGameLump::GetGameLumpHandle( GameLumpId_t id ) -{ - // NOTE: I'm also expecting game lump id's to be four-CC codes - Assert( id > HEADER_LUMPS ); - - FOR_EACH_LL(m_GameLumps, i) - { - if (m_GameLumps[i].m_Id == id) - return i; - } - - return InvalidGameLump(); -} - -GameLumpId_t CGameLump::GetGameLumpId( GameLumpHandle_t handle ) -{ - return m_GameLumps[handle].m_Id; -} - -int CGameLump::GetGameLumpFlags( GameLumpHandle_t handle ) -{ - return m_GameLumps[handle].m_Flags; -} - -int CGameLump::GetGameLumpVersion( GameLumpHandle_t handle ) -{ - return m_GameLumps[handle].m_Version; -} - - -//----------------------------------------------------------------------------- -// Game lump accessor methods -//----------------------------------------------------------------------------- - -void* CGameLump::GetGameLump( GameLumpHandle_t id ) -{ - return m_GameLumps[id].m_Memory.Base(); -} - -int CGameLump::GameLumpSize( GameLumpHandle_t id ) -{ - return m_GameLumps[id].m_Memory.NumAllocated(); -} - - -//----------------------------------------------------------------------------- -// Game lump iteration methods -//----------------------------------------------------------------------------- - -GameLumpHandle_t CGameLump::FirstGameLump() -{ - return (m_GameLumps.Count()) ? m_GameLumps.Head() : InvalidGameLump(); -} - -GameLumpHandle_t CGameLump::NextGameLump( GameLumpHandle_t handle ) -{ - - return (m_GameLumps.IsValidIndex(handle)) ? m_GameLumps.Next(handle) : InvalidGameLump(); -} - -GameLumpHandle_t CGameLump::InvalidGameLump() -{ - return 0xFFFF; -} - - -//----------------------------------------------------------------------------- -// Game lump creation/destruction method -//----------------------------------------------------------------------------- - -GameLumpHandle_t CGameLump::CreateGameLump( GameLumpId_t id, int size, int flags, int version ) -{ - Assert( GetGameLumpHandle(id) == InvalidGameLump() ); - GameLumpHandle_t handle = m_GameLumps.AddToTail(); - m_GameLumps[handle].m_Id = id; - m_GameLumps[handle].m_Flags = flags; - m_GameLumps[handle].m_Version = version; - m_GameLumps[handle].m_Memory.EnsureCapacity( size ); - return handle; -} - -void CGameLump::DestroyGameLump( GameLumpHandle_t handle ) -{ - m_GameLumps.Remove( handle ); -} - -void CGameLump::DestroyAllGameLumps() -{ - m_GameLumps.RemoveAll(); -} - -//----------------------------------------------------------------------------- -// Compute file size and clump count -//----------------------------------------------------------------------------- - -void CGameLump::ComputeGameLumpSizeAndCount( int& size, int& clumpCount ) -{ - // Figure out total size of the client lumps - size = 0; - clumpCount = 0; - GameLumpHandle_t h; - for( h = FirstGameLump(); h != InvalidGameLump(); h = NextGameLump( h ) ) - { - ++clumpCount; - size += GameLumpSize( h ); - } - - // Add on headers - size += sizeof( dgamelumpheader_t ) + clumpCount * sizeof( dgamelump_t ); -} - - -void CGameLump::SwapGameLump( GameLumpId_t id, int version, byte *dest, byte *src, int length ) -{ - int count = 0; - switch( id ) - { - case GAMELUMP_STATIC_PROPS: - // Swap the static prop model dict - count = *(int*)src; - g_Swap.SwapBufferToTargetEndian( (int*)dest, (int*)src ); - count = g_bSwapOnLoad ? *(int*)dest : count; - src += sizeof(int); - dest += sizeof(int); - - g_Swap.SwapFieldsToTargetEndian( (StaticPropDictLump_t*)dest, (StaticPropDictLump_t*)src, count ); - src += sizeof(StaticPropDictLump_t) * count; - dest += sizeof(StaticPropDictLump_t) * count; - - // Swap the leaf list - count = *(int*)src; - g_Swap.SwapBufferToTargetEndian( (int*)dest, (int*)src ); - count = g_bSwapOnLoad ? *(int*)dest : count; - src += sizeof(int); - dest += sizeof(int); - - g_Swap.SwapFieldsToTargetEndian( (StaticPropLeafLump_t*)dest, (StaticPropLeafLump_t*)src, count ); - src += sizeof(StaticPropLeafLump_t) * count; - dest += sizeof(StaticPropLeafLump_t) * count; - - // Swap the models - count = *(int*)src; - g_Swap.SwapBufferToTargetEndian( (int*)dest, (int*)src ); - count = g_bSwapOnLoad ? *(int*)dest : count; - src += sizeof(int); - dest += sizeof(int); - - // The one-at-a-time swap is to compensate for these structures - // possibly being misaligned, which crashes the Xbox 360. - if ( version == 4 ) - { - StaticPropLumpV4_t lump; - for ( int i = 0; i < count; ++i ) - { - Q_memcpy( &lump, src, sizeof(StaticPropLumpV4_t) ); - g_Swap.SwapFieldsToTargetEndian( &lump, &lump ); - Q_memcpy( dest, &lump, sizeof(StaticPropLumpV4_t) ); - src += sizeof( StaticPropLumpV4_t ); - dest += sizeof( StaticPropLumpV4_t ); - } - } - else if ( version == 5 ) - { - StaticPropLumpV5_t lump; - for ( int i = 0; i < count; ++i ) - { - Q_memcpy( &lump, src, sizeof(StaticPropLumpV5_t) ); - g_Swap.SwapFieldsToTargetEndian( &lump, &lump ); - Q_memcpy( dest, &lump, sizeof(StaticPropLumpV5_t) ); - src += sizeof( StaticPropLumpV5_t ); - dest += sizeof( StaticPropLumpV5_t ); - } - } - else - { - if ( version != 6 ) - { - Error( "Unknown Static Prop Lump version %d didn't get swapped!\n", version ); - } - - StaticPropLump_t lump; - for ( int i = 0; i < count; ++i ) - { - Q_memcpy( &lump, src, sizeof(StaticPropLump_t) ); - g_Swap.SwapFieldsToTargetEndian( &lump, &lump ); - Q_memcpy( dest, &lump, sizeof(StaticPropLump_t) ); - src += sizeof( StaticPropLump_t ); - dest += sizeof( StaticPropLump_t ); - } - } - break; - - case GAMELUMP_DETAIL_PROPS: - // Swap the detail prop model dict - count = *(int*)src; - g_Swap.SwapBufferToTargetEndian( (int*)dest, (int*)src ); - count = g_bSwapOnLoad ? *(int*)dest : count; - src += sizeof(int); - dest += sizeof(int); - - g_Swap.SwapFieldsToTargetEndian( (DetailObjectDictLump_t*)dest, (DetailObjectDictLump_t*)src, count ); - src += sizeof(DetailObjectDictLump_t) * count; - dest += sizeof(DetailObjectDictLump_t) * count; - - if ( version == 4 ) - { - // Swap the detail sprite dict - count = *(int*)src; - g_Swap.SwapBufferToTargetEndian( (int*)dest, (int*)src ); - count = g_bSwapOnLoad ? *(int*)dest : count; - src += sizeof(int); - dest += sizeof(int); - - DetailSpriteDictLump_t spritelump; - for ( int i = 0; i < count; ++i ) - { - Q_memcpy( &spritelump, src, sizeof(DetailSpriteDictLump_t) ); - g_Swap.SwapFieldsToTargetEndian( &spritelump, &spritelump ); - Q_memcpy( dest, &spritelump, sizeof(DetailSpriteDictLump_t) ); - src += sizeof(DetailSpriteDictLump_t); - dest += sizeof(DetailSpriteDictLump_t); - } - - // Swap the models - count = *(int*)src; - g_Swap.SwapBufferToTargetEndian( (int*)dest, (int*)src ); - count = g_bSwapOnLoad ? *(int*)dest : count; - src += sizeof(int); - dest += sizeof(int); - - DetailObjectLump_t objectlump; - for ( int i = 0; i < count; ++i ) - { - Q_memcpy( &objectlump, src, sizeof(DetailObjectLump_t) ); - g_Swap.SwapFieldsToTargetEndian( &objectlump, &objectlump ); - Q_memcpy( dest, &objectlump, sizeof(DetailObjectLump_t) ); - src += sizeof(DetailObjectLump_t); - dest += sizeof(DetailObjectLump_t); - } - } - break; - - case GAMELUMP_DETAIL_PROP_LIGHTING: - // Swap the LDR light styles - count = *(int*)src; - g_Swap.SwapBufferToTargetEndian( (int*)dest, (int*)src ); - count = g_bSwapOnLoad ? *(int*)dest : count; - src += sizeof(int); - dest += sizeof(int); - - g_Swap.SwapFieldsToTargetEndian( (DetailPropLightstylesLump_t*)dest, (DetailPropLightstylesLump_t*)src, count ); - src += sizeof(DetailObjectDictLump_t) * count; - dest += sizeof(DetailObjectDictLump_t) * count; - break; - - case GAMELUMP_DETAIL_PROP_LIGHTING_HDR: - // Swap the HDR light styles - count = *(int*)src; - g_Swap.SwapBufferToTargetEndian( (int*)dest, (int*)src ); - count = g_bSwapOnLoad ? *(int*)dest : count; - src += sizeof(int); - dest += sizeof(int); - - g_Swap.SwapFieldsToTargetEndian( (DetailPropLightstylesLump_t*)dest, (DetailPropLightstylesLump_t*)src, count ); - src += sizeof(DetailObjectDictLump_t) * count; - dest += sizeof(DetailObjectDictLump_t) * count; - break; - - default: - char idchars[5] = {0}; - Q_memcpy( idchars, &id, 4 ); - Warning( "Unknown game lump '%s' didn't get swapped!\n", idchars ); - memcpy ( dest, src, length); - break; - } -} - -//----------------------------------------------------------------------------- -// Game lump file I/O -//----------------------------------------------------------------------------- -void CGameLump::ParseGameLump( dheader_t* pHeader ) -{ - g_GameLumps.DestroyAllGameLumps(); - - g_Lumps.bLumpParsed[LUMP_GAME_LUMP] = true; - - int length = pHeader->lumps[LUMP_GAME_LUMP].filelen; - int ofs = pHeader->lumps[LUMP_GAME_LUMP].fileofs; - - if (length > 0) - { - // Read dictionary... - dgamelumpheader_t* pGameLumpHeader = (dgamelumpheader_t*)((byte *)pHeader + ofs); - if ( g_bSwapOnLoad ) - { - g_Swap.SwapFieldsToTargetEndian( pGameLumpHeader ); - } - dgamelump_t* pGameLump = (dgamelump_t*)(pGameLumpHeader + 1); - for (int i = 0; i < pGameLumpHeader->lumpCount; ++i ) - { - if ( g_bSwapOnLoad ) - { - g_Swap.SwapFieldsToTargetEndian( &pGameLump[i] ); - } - - int length = pGameLump[i].filelen; - GameLumpHandle_t lump = g_GameLumps.CreateGameLump( pGameLump[i].id, length, pGameLump[i].flags, pGameLump[i].version ); - if ( g_bSwapOnLoad ) - { - SwapGameLump( pGameLump[i].id, pGameLump[i].version, (byte*)g_GameLumps.GetGameLump(lump), (byte *)pHeader + pGameLump[i].fileofs, length ); - } - else - { - memcpy( g_GameLumps.GetGameLump(lump), (byte *)pHeader + pGameLump[i].fileofs, length ); - } - } - } -} - - -//----------------------------------------------------------------------------- -// String table methods -//----------------------------------------------------------------------------- -const char *TexDataStringTable_GetString( int stringID ) -{ - return &g_TexDataStringData[g_TexDataStringTable[stringID]]; -} - -int TexDataStringTable_AddOrFindString( const char *pString ) -{ - int i; - // garymcthack: Make this use an RBTree! - for( i = 0; i < g_TexDataStringTable.Count(); i++ ) - { - if( stricmp( pString, &g_TexDataStringData[g_TexDataStringTable[i]] ) == 0 ) - { - return i; - } - } - - int len = strlen( pString ); - int outOffset = g_TexDataStringData.AddMultipleToTail( len+1, pString ); - int outIndex = g_TexDataStringTable.AddToTail( outOffset ); - return outIndex; -} - -//----------------------------------------------------------------------------- -// Adds all game lumps into one big block -//----------------------------------------------------------------------------- - -static void AddGameLumps( ) -{ - // Figure out total size of the client lumps - int size, clumpCount; - g_GameLumps.ComputeGameLumpSizeAndCount( size, clumpCount ); - - // Set up the main lump dictionary entry - g_Lumps.size[LUMP_GAME_LUMP] = 0; // mark it written - - lump_t* lump = &g_pBSPHeader->lumps[LUMP_GAME_LUMP]; - - lump->fileofs = g_pFileSystem->Tell( g_hBSPFile ); - lump->filelen = size; - - // write header - dgamelumpheader_t header; - header.lumpCount = clumpCount; - WriteData( &header ); - - // write dictionary - dgamelump_t dict; - int offset = lump->fileofs + sizeof(header) + clumpCount * sizeof(dgamelump_t); - GameLumpHandle_t h; - for( h = g_GameLumps.FirstGameLump(); h != g_GameLumps.InvalidGameLump(); h = g_GameLumps.NextGameLump( h ) ) - { - dict.id = g_GameLumps.GetGameLumpId(h); - dict.version = g_GameLumps.GetGameLumpVersion(h); - dict.flags = g_GameLumps.GetGameLumpFlags(h); - dict.fileofs = offset; - dict.filelen = g_GameLumps.GameLumpSize( h ); - offset += dict.filelen; - - WriteData( &dict ); - } - - // write lumps.. - for( h = g_GameLumps.FirstGameLump(); h != g_GameLumps.InvalidGameLump(); h = g_GameLumps.NextGameLump( h ) ) - { - unsigned int lumpsize = g_GameLumps.GameLumpSize(h); - if ( g_bSwapOnWrite ) - { - g_GameLumps.SwapGameLump( g_GameLumps.GetGameLumpId(h), g_GameLumps.GetGameLumpVersion(h), (byte*)g_GameLumps.GetGameLump(h), (byte*)g_GameLumps.GetGameLump(h), lumpsize ); - } - SafeWrite( g_hBSPFile, g_GameLumps.GetGameLump(h), lumpsize ); - } - - // align to doubleword - AlignFilePosition( g_hBSPFile, 4 ); -} - - -//----------------------------------------------------------------------------- -// Adds the occluder lump... -//----------------------------------------------------------------------------- -static void AddOcclusionLump( ) -{ - g_Lumps.size[LUMP_OCCLUSION] = 0; // mark it written - - int nOccluderCount = g_OccluderData.Count(); - int nOccluderPolyDataCount = g_OccluderPolyData.Count(); - int nOccluderVertexIndices = g_OccluderVertexIndices.Count(); - - int nLumpLength = nOccluderCount * sizeof(doccluderdata_t) + - nOccluderPolyDataCount * sizeof(doccluderpolydata_t) + - nOccluderVertexIndices * sizeof(int) + - 3 * sizeof(int); - - lump_t *lump = &g_pBSPHeader->lumps[LUMP_OCCLUSION]; - - lump->fileofs = g_pFileSystem->Tell( g_hBSPFile ); - lump->filelen = nLumpLength; - lump->version = LUMP_OCCLUSION_VERSION; - lump->fourCC[0] = ( char )0; - lump->fourCC[1] = ( char )0; - lump->fourCC[2] = ( char )0; - lump->fourCC[3] = ( char )0; - - // Data is swapped in place, so the 'Count' variables aren't safe to use after they're written - WriteData( FIELD_INTEGER, &nOccluderCount ); - WriteData( (doccluderdata_t*)g_OccluderData.Base(), g_OccluderData.Count() ); - WriteData( FIELD_INTEGER, &nOccluderPolyDataCount ); - WriteData( (doccluderpolydata_t*)g_OccluderPolyData.Base(), g_OccluderPolyData.Count() ); - WriteData( FIELD_INTEGER, &nOccluderVertexIndices ); - WriteData( FIELD_INTEGER, (int*)g_OccluderVertexIndices.Base(), g_OccluderVertexIndices.Count() ); -} - - -//----------------------------------------------------------------------------- -// Loads the occluder lump... -//----------------------------------------------------------------------------- -static void UnserializeOcclusionLumpV2( CUtlBuffer &buf ) -{ - int nCount = buf.GetInt(); - if ( nCount ) - { - g_OccluderData.SetCount( nCount ); - buf.GetObjects( g_OccluderData.Base(), nCount ); - } - - nCount = buf.GetInt(); - if ( nCount ) - { - g_OccluderPolyData.SetCount( nCount ); - buf.GetObjects( g_OccluderPolyData.Base(), nCount ); - } - - nCount = buf.GetInt(); - if ( nCount ) - { - if ( g_bSwapOnLoad ) - { - g_Swap.SwapBufferToTargetEndian( (int*)buf.PeekGet(), (int*)buf.PeekGet(), nCount ); - } - g_OccluderVertexIndices.SetCount( nCount ); - buf.Get( g_OccluderVertexIndices.Base(), nCount * sizeof(g_OccluderVertexIndices[0]) ); - } -} - - -static void LoadOcclusionLump() -{ - g_OccluderData.RemoveAll(); - g_OccluderPolyData.RemoveAll(); - g_OccluderVertexIndices.RemoveAll(); - - int length, ofs; - - g_Lumps.bLumpParsed[LUMP_OCCLUSION] = true; - - length = g_pBSPHeader->lumps[LUMP_OCCLUSION].filelen; - ofs = g_pBSPHeader->lumps[LUMP_OCCLUSION].fileofs; - - CUtlBuffer buf( (byte *)g_pBSPHeader + ofs, length, CUtlBuffer::READ_ONLY ); - buf.ActivateByteSwapping( g_bSwapOnLoad ); - switch ( g_pBSPHeader->lumps[LUMP_OCCLUSION].version ) - { - case 2: - UnserializeOcclusionLumpV2( buf ); - break; - - case 0: - break; - - default: - Error("Unknown occlusion lump version!\n"); - break; - } -} - - -/* -=============== -CompressVis - -=============== -*/ -int CompressVis (byte *vis, byte *dest) -{ - int j; - int rep; - int visrow; - byte *dest_p; - - dest_p = dest; -// visrow = (r_numvisleafs + 7)>>3; - visrow = (dvis->numclusters + 7)>>3; - - for (j=0 ; j>3; - row = (dvis->numclusters+7)>>3; - out = decompressed; - - do - { - if (*in) - { - *out++ = *in++; - continue; - } - - c = in[1]; - if (!c) - Error ("DecompressVis: 0 repeat"); - in += 2; - if ((out - decompressed) + c > row) - { - c = row - (out - decompressed); - Warning( "warning: Vis decompression overrun\n" ); - } - while (c) - { - *out++ = 0; - c--; - } - } while (out - decompressed < row); -} - -//----------------------------------------------------------------------------- -// Lump-specific swap functions -//----------------------------------------------------------------------------- -struct swapcollideheader_t -{ - DECLARE_BYTESWAP_DATADESC(); - int size; - int vphysicsID; - short version; - short modelType; -}; - -struct swapcompactsurfaceheader_t : swapcollideheader_t -{ - DECLARE_BYTESWAP_DATADESC(); - int surfaceSize; - Vector dragAxisAreas; - int axisMapSize; -}; - -struct swapmoppsurfaceheader_t : swapcollideheader_t -{ - DECLARE_BYTESWAP_DATADESC(); - int moppSize; -}; - -BEGIN_BYTESWAP_DATADESC( swapcollideheader_t ) - DEFINE_FIELD( size, FIELD_INTEGER ), - DEFINE_FIELD( vphysicsID, FIELD_INTEGER ), - DEFINE_FIELD( version, FIELD_SHORT ), - DEFINE_FIELD( modelType, FIELD_SHORT ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC_( swapcompactsurfaceheader_t, swapcollideheader_t ) - DEFINE_FIELD( surfaceSize, FIELD_INTEGER ), - DEFINE_FIELD( dragAxisAreas, FIELD_VECTOR ), - DEFINE_FIELD( axisMapSize, FIELD_INTEGER ), -END_BYTESWAP_DATADESC() - -BEGIN_BYTESWAP_DATADESC_( swapmoppsurfaceheader_t, swapcollideheader_t ) - DEFINE_FIELD( moppSize, FIELD_INTEGER ), -END_BYTESWAP_DATADESC() - - -static void SwapPhyscollideLump( byte *pDestBase, byte *pSrcBase, unsigned int &count ) -{ - IPhysicsCollision *physcollision = NULL; - CSysModule *pPhysicsModule = g_pFullFileSystem->LoadModule( "vphysics.dll" ); - if ( pPhysicsModule ) - { - CreateInterfaceFn physicsFactory = Sys_GetFactory( pPhysicsModule ); - if ( physicsFactory ) - { - physcollision = (IPhysicsCollision *)physicsFactory( VPHYSICS_COLLISION_INTERFACE_VERSION, NULL ); - } - } - - if ( !physcollision ) - { - Warning("!!! WARNING: Can't swap the physcollide lump!\n" ); - return; - } - - // physics data is variable length. The last physmodel is a NULL pointer - // with modelIndex -1, dataSize -1 - dphysmodel_t *pPhysModel; - byte *pSrc = pSrcBase; - - // first the src chunks have to be aligned properly - // swap increases size, allocate enough expansion room - byte *pSrcAlignedBase = (byte*)malloc( 2*count ); - byte *basePtr = pSrcAlignedBase; - byte *pSrcAligned = pSrcAlignedBase; - - do - { - if ( g_bSwapOnLoad ) - { - g_Swap.SwapFieldsToTargetEndian( (dphysmodel_t*)pSrcAligned, (dphysmodel_t*)pSrc ); - } - else - { - Q_memcpy( pSrcAligned, pSrc, sizeof(dphysmodel_t) ); - } - pPhysModel = (dphysmodel_t*)pSrcAligned; - - pSrc += sizeof(dphysmodel_t); - pSrcAligned += sizeof(dphysmodel_t); - - if ( pPhysModel->dataSize > 0 ) - { - // Align the collide headers - for ( int i = 0; i < pPhysModel->solidCount; ++i ) - { - // Get data size - int size; - Q_memcpy( &size, pSrc, sizeof(int) ); - if ( g_bSwapOnLoad ) - size = SwapLong( size ); - - // Fixup size - int padBytes = 0; - if ( size % 4 != 0 ) - { - padBytes = ( 4 - size % 4 ); - count += padBytes; - pPhysModel->dataSize += padBytes; - } - - // Copy data and size into alligned buffer - int newsize = size + padBytes; - if ( g_bSwapOnLoad ) - newsize = SwapLong( newsize ); - - Q_memcpy( pSrcAligned, &newsize, sizeof(int) ); - Q_memcpy( pSrcAligned + sizeof(int), pSrc + sizeof(int), size ); - pSrcAligned += size + padBytes + sizeof(int); - pSrc += size + sizeof(int); - } - - int padBytes = 0; - int dataSize = pPhysModel->dataSize + pPhysModel->keydataSize; - Q_memcpy( pSrcAligned, pSrc, pPhysModel->keydataSize ); - pSrc += pPhysModel->keydataSize; - pSrcAligned += pPhysModel->keydataSize; - if ( dataSize % 4 != 0 ) - { - // Next chunk will be unaligned - padBytes = ( 4 - dataSize % 4 ); - pPhysModel->keydataSize += padBytes; - count += padBytes; - Q_memset( pSrcAligned, 0, padBytes ); - pSrcAligned += padBytes; - } - } - } while ( pPhysModel->dataSize > 0 ); - - // Now the data can be swapped properly - pSrcBase = pSrcAlignedBase; - pSrc = pSrcBase; - byte *pDest = pDestBase; - - do - { - // src headers are in native format - pPhysModel = (dphysmodel_t*)pSrc; - if ( g_bSwapOnWrite ) - { - g_Swap.SwapFieldsToTargetEndian( (dphysmodel_t*)pDest, (dphysmodel_t*)pSrc ); - } - else - { - Q_memcpy( pDest, pSrc, sizeof(dphysmodel_t) ); - } - - pSrc += sizeof(dphysmodel_t); - pDest += sizeof(dphysmodel_t); - - pSrcBase = pSrc; - pDestBase = pDest; - - if ( pPhysModel->dataSize > 0 ) - { - vcollide_t collide = {0}; - int dataSize = pPhysModel->dataSize + pPhysModel->keydataSize; - - if ( g_bSwapOnWrite ) - { - // Load the collide data - physcollision->VCollideLoad( &collide, pPhysModel->solidCount, (const char *)pSrc, dataSize, false ); - } - - int *offsets = new int[ pPhysModel->solidCount ]; - - // Swap the collision data headers - for ( int i = 0; i < pPhysModel->solidCount; ++i ) - { - int headerSize = 0; - swapcollideheader_t *baseHdr = (swapcollideheader_t*)pSrc; - short modelType = baseHdr->modelType; - if ( g_bSwapOnLoad ) - { - g_Swap.SwapBufferToTargetEndian( &modelType ); - } - - if ( modelType == 0 ) // COLLIDE_POLY - { - headerSize = sizeof(swapcompactsurfaceheader_t); - swapcompactsurfaceheader_t swapHdr; - Q_memcpy( &swapHdr, pSrc, headerSize ); - g_Swap.SwapFieldsToTargetEndian( &swapHdr, &swapHdr ); - Q_memcpy( pDest, &swapHdr, headerSize ); - } - else if ( modelType == 1 ) // COLLIDE_MOPP - { - // The PC still unserializes these, but we don't support them - if ( g_bSwapOnWrite ) - { - collide.solids[i] = NULL; - } - - headerSize = sizeof(swapmoppsurfaceheader_t); - swapmoppsurfaceheader_t swapHdr; - Q_memcpy( &swapHdr, pSrc, headerSize ); - g_Swap.SwapFieldsToTargetEndian( &swapHdr, &swapHdr ); - Q_memcpy( pDest, &swapHdr, headerSize ); - - } - else - { - // Shouldn't happen - Assert( 0 ); - } - - if ( g_bSwapOnLoad ) - { - // src needs the native header data to load the vcollides - Q_memcpy( pSrc, pDest, headerSize ); - } - // HACK: Need either surfaceSize or moppSize - both sit at the same offset in the structure - swapmoppsurfaceheader_t *hdr = (swapmoppsurfaceheader_t*)pSrc; - pSrc += hdr->size + sizeof(int); - pDest += hdr->size + sizeof(int); - offsets[i] = hdr->size; - } - - pSrc = pSrcBase; - pDest = pDestBase; - if ( g_bSwapOnLoad ) - { - physcollision->VCollideLoad( &collide, pPhysModel->solidCount, (const char *)pSrc, dataSize, true ); - } - - // Write out the ledge tree data - for ( int i = 0; i < pPhysModel->solidCount; ++i ) - { - if ( collide.solids[i] ) - { - // skip over the size member - pSrc += sizeof(int); - pDest += sizeof(int); - int offset = physcollision->CollideWrite( (char*)pDest, collide.solids[i], g_bSwapOnWrite ); - pSrc += offset; - pDest += offset; - } - else - { - pSrc += offsets[i] + sizeof(int); - pDest += offsets[i] + sizeof(int); - } - } - - // copy the keyvalues data - Q_memcpy( pDest, pSrc, pPhysModel->keydataSize ); - pDest += pPhysModel->keydataSize; - pSrc += pPhysModel->keydataSize; - - // Free the memory - physcollision->VCollideUnload( &collide ); - delete [] offsets; - } - - // avoid infinite loop on badly formed file - if ( (pSrc - basePtr) > count ) - break; - - } while ( pPhysModel->dataSize > 0 ); - - free( pSrcAlignedBase ); -} - - -// UNDONE: This code is not yet tested. -static void SwapPhysdispLump( byte *pDest, byte *pSrc, int count ) -{ - // the format of this lump is one unsigned short dispCount, then dispCount unsigned shorts of sizes - // followed by an array of variable length (each element is the length of the corresponding entry in the - // previous table) byte-stream data structure of the displacement collision models - // these byte-stream structs are endian-neutral because each element is byte-sized - unsigned short dispCount = *(unsigned short*)pSrc; - if ( g_bSwapOnLoad ) - { - g_Swap.SwapBufferToTargetEndian( &dispCount ); - } - g_Swap.SwapBufferToTargetEndian( (unsigned short*)pDest, (unsigned short*)pSrc, dispCount + 1 ); - - const int nBytes = (dispCount + 1) * sizeof( unsigned short ); - pSrc += nBytes; - pDest += nBytes; - count -= nBytes; - - g_Swap.SwapBufferToTargetEndian( pDest, pSrc, count ); -} - - -static void SwapVisibilityLump( byte *pDest, byte *pSrc, int count ) -{ - int firstInt = *(int*)pSrc; - if ( g_bSwapOnLoad ) - { - g_Swap.SwapBufferToTargetEndian( &firstInt ); - } - int intCt = firstInt * 2 + 1; - const int hdrSize = intCt * sizeof(int); - g_Swap.SwapBufferToTargetEndian( (int*)pDest, (int*)pSrc, intCt ); - g_Swap.SwapBufferToTargetEndian( pDest + hdrSize, pSrc + hdrSize, count - hdrSize ); -} - -//============================================================================= -void Lumps_Init( void ) -{ - memset( &g_Lumps, 0, sizeof(g_Lumps) ); -} - -int LumpVersion( int lump ) -{ - return g_pBSPHeader->lumps[lump].version; -} - -bool HasLump( int lump ) -{ - return g_pBSPHeader->lumps[lump].filelen > 0; -} - -void ValidateLump( int lump, int length, int size, int forceVersion ) -{ - if ( length % size ) - { - Error( "ValidateLump: odd size for lump %d", lump ); - } - - if ( forceVersion >= 0 && forceVersion != g_pBSPHeader->lumps[lump].version ) - { - Error( "ValidateLump: old version for lump %d in map!", lump ); - } -} - -//----------------------------------------------------------------------------- -// Add Lumps of integral types without datadescs -//----------------------------------------------------------------------------- -template< class T > -int CopyLumpInternal( int fieldType, int lump, T *dest, int forceVersion ) -{ - g_Lumps.bLumpParsed[lump] = true; - - // Vectors are passed in as floats - int fieldSize = ( fieldType == FIELD_VECTOR ) ? sizeof(Vector) : sizeof(T); - unsigned int length = g_pBSPHeader->lumps[lump].filelen; - unsigned int ofs = g_pBSPHeader->lumps[lump].fileofs; - - // count must be of the integral type - unsigned int count = length / sizeof(T); - - ValidateLump( lump, length, fieldSize, forceVersion ); - - if ( g_bSwapOnLoad ) - { - switch( lump ) - { - case LUMP_VISIBILITY: - SwapVisibilityLump( (byte*)dest, ((byte*)g_pBSPHeader + ofs), count ); - break; - - case LUMP_PHYSCOLLIDE: - // SwapPhyscollideLump may change size - SwapPhyscollideLump( (byte*)dest, ((byte*)g_pBSPHeader + ofs), count ); - length = count; - break; - - case LUMP_PHYSDISP: - SwapPhysdispLump( (byte*)dest, ((byte*)g_pBSPHeader + ofs), count ); - break; - - default: - g_Swap.SwapBufferToTargetEndian( dest, (T*)((byte*)g_pBSPHeader + ofs), count ); - break; - } - } - else - { - memcpy( dest, (byte*)g_pBSPHeader + ofs, length ); - } - - // Return actual count of elements - return length / fieldSize; -} - -template< class T > -int CopyLump( int fieldType, int lump, T *dest, int forceVersion = -1 ) -{ - return CopyLumpInternal( fieldType, lump, dest, forceVersion ); -} - -template< class T > -void CopyLump( int fieldType, int lump, CUtlVector &dest, int forceVersion = -1 ) -{ - Assert( fieldType != FIELD_VECTOR ); // TODO: Support this if necessary - dest.SetSize( g_pBSPHeader->lumps[lump].filelen / sizeof(T) ); - CopyLumpInternal( fieldType, lump, dest.Base(), forceVersion ); -} - -template< class T > -void CopyOptionalLump( int fieldType, int lump, CUtlVector &dest, int forceVersion = -1 ) -{ - // not fatal if not present - if ( !HasLump( lump ) ) - return; - - dest.SetSize( g_pBSPHeader->lumps[lump].filelen / sizeof(T) ); - CopyLumpInternal( fieldType, lump, dest.Base(), forceVersion ); -} - -template< class T > -int CopyVariableLump( int fieldType, int lump, void **dest, int forceVersion = -1 ) -{ - int length = g_pBSPHeader->lumps[lump].filelen; - *dest = malloc( length ); - - return CopyLumpInternal( fieldType, lump, (T*)*dest, forceVersion ); -} - -//----------------------------------------------------------------------------- -// Add Lumps of object types with datadescs -//----------------------------------------------------------------------------- -template< class T > -int CopyLumpInternal( int lump, T *dest, int forceVersion ) -{ - g_Lumps.bLumpParsed[lump] = true; - - unsigned int length = g_pBSPHeader->lumps[lump].filelen; - unsigned int ofs = g_pBSPHeader->lumps[lump].fileofs; - unsigned int count = length / sizeof(T); - - ValidateLump( lump, length, sizeof(T), forceVersion ); - - if ( g_bSwapOnLoad ) - { - g_Swap.SwapFieldsToTargetEndian( dest, (T*)((byte*)g_pBSPHeader + ofs), count ); - } - else - { - memcpy( dest, (byte*)g_pBSPHeader + ofs, length ); - } - - return count; -} - -template< class T > -int CopyLump( int lump, T *dest, int forceVersion = -1 ) -{ - return CopyLumpInternal( lump, dest, forceVersion ); -} - -template< class T > -void CopyLump( int lump, CUtlVector &dest, int forceVersion = -1 ) -{ - dest.SetSize( g_pBSPHeader->lumps[lump].filelen / sizeof(T) ); - CopyLumpInternal( lump, dest.Base(), forceVersion ); -} - -template< class T > -void CopyOptionalLump( int lump, CUtlVector &dest, int forceVersion = -1 ) -{ - // not fatal if not present - if ( !HasLump( lump ) ) - return; - - dest.SetSize( g_pBSPHeader->lumps[lump].filelen / sizeof(T) ); - CopyLumpInternal( lump, dest.Base(), forceVersion ); -} - -template< class T > -int CopyVariableLump( int lump, void **dest, int forceVersion = -1 ) -{ - int length = g_pBSPHeader->lumps[lump].filelen; - *dest = malloc( length ); - - return CopyLumpInternal( lump, (T*)*dest, forceVersion ); -} - -//----------------------------------------------------------------------------- -// Add/Write unknown lumps -//----------------------------------------------------------------------------- -void Lumps_Parse( void ) -{ - int i; - - for ( i = 0; i < HEADER_LUMPS; i++ ) - { - if ( !g_Lumps.bLumpParsed[i] && g_pBSPHeader->lumps[i].filelen ) - { - g_Lumps.size[i] = CopyVariableLump( FIELD_CHARACTER, i, &g_Lumps.pLumps[i], -1 ); - Msg( "Reading unknown lump #%d (%d bytes)\n", i, g_Lumps.size[i] ); - } - } -} - -void Lumps_Write( void ) -{ - int i; - - for ( i = 0; i < HEADER_LUMPS; i++ ) - { - if ( g_Lumps.size[i] ) - { - Msg( "Writing unknown lump #%d (%d bytes)\n", i, g_Lumps.size[i] ); - AddLump( i, (byte*)g_Lumps.pLumps[i], g_Lumps.size[i] ); - } - if ( g_Lumps.pLumps[i] ) - { - free( g_Lumps.pLumps[i] ); - g_Lumps.pLumps[i] = NULL; - } - } -} - -int LoadLeafs( void ) -{ -#if defined( BSP_USE_LESS_MEMORY ) - dleafs = (dleaf_t*)malloc( g_pBSPHeader->lumps[LUMP_LEAFS].filelen ); -#endif - - switch ( LumpVersion( LUMP_LEAFS ) ) - { - case 0: - { - g_Lumps.bLumpParsed[LUMP_LEAFS] = true; - int length = g_pBSPHeader->lumps[LUMP_LEAFS].filelen; - int size = sizeof( dleaf_version_0_t ); - if ( length % size ) - { - Error( "odd size for LUMP_LEAFS\n" ); - } - int count = length / size; - - void *pSrcBase = ( ( byte * )g_pBSPHeader + g_pBSPHeader->lumps[LUMP_LEAFS].fileofs ); - dleaf_version_0_t *pSrc = (dleaf_version_0_t *)pSrcBase; - dleaf_t *pDst = dleafs; - - // version 0 predates HDR, build the LDR - g_LeafAmbientLightingLDR.SetCount( count ); - g_LeafAmbientIndexLDR.SetCount( count ); - - dleafambientlighting_t *pDstLeafAmbientLighting = &g_LeafAmbientLightingLDR[0]; - for ( int i = 0; i < count; i++ ) - { - g_LeafAmbientIndexLDR[i].ambientSampleCount = 1; - g_LeafAmbientIndexLDR[i].firstAmbientSample = i; - - if ( g_bSwapOnLoad ) - { - g_Swap.SwapFieldsToTargetEndian( pSrc ); - } - // pDst is a subset of pSrc; - *pDst = *( ( dleaf_t * )( void * )pSrc ); - pDstLeafAmbientLighting->cube = pSrc->m_AmbientLighting; - pDstLeafAmbientLighting->x = pDstLeafAmbientLighting->y = pDstLeafAmbientLighting->z = pDstLeafAmbientLighting->pad = 0; - pDst++; - pSrc++; - pDstLeafAmbientLighting++; - } - return count; - } - - case 1: - return CopyLump( LUMP_LEAFS, dleafs ); - - default: - Assert( 0 ); - Error( "Unknown LUMP_LEAFS version\n" ); - return 0; - } -} - -void LoadLeafAmbientLighting( int numLeafs ) -{ - if ( LumpVersion( LUMP_LEAFS ) == 0 ) - { - // an older leaf version already built the LDR ambient lighting on load - return; - } - - // old BSP with ambient, or new BSP with no lighting, convert ambient light to new format or create dummy ambient - if ( !HasLump( LUMP_LEAF_AMBIENT_INDEX ) ) - { - // a bunch of legacy maps, have these lumps with garbage versions - // expect them to be NOT the current version - if ( HasLump(LUMP_LEAF_AMBIENT_LIGHTING) ) - { - Assert( LumpVersion( LUMP_LEAF_AMBIENT_LIGHTING ) != LUMP_LEAF_AMBIENT_LIGHTING_VERSION ); - } - if ( HasLump(LUMP_LEAF_AMBIENT_LIGHTING_HDR) ) - { - Assert( LumpVersion( LUMP_LEAF_AMBIENT_LIGHTING_HDR ) != LUMP_LEAF_AMBIENT_LIGHTING_VERSION ); - } - - void *pSrcBase = ( ( byte * )g_pBSPHeader + g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_LIGHTING].fileofs ); - CompressedLightCube *pSrc = NULL; - if ( HasLump( LUMP_LEAF_AMBIENT_LIGHTING ) ) - { - pSrc = (CompressedLightCube*)pSrcBase; - } - g_LeafAmbientIndexLDR.SetCount( numLeafs ); - g_LeafAmbientLightingLDR.SetCount( numLeafs ); - - void *pSrcBaseHDR = ( ( byte * )g_pBSPHeader + g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_LIGHTING_HDR].fileofs ); - CompressedLightCube *pSrcHDR = NULL; - if ( HasLump( LUMP_LEAF_AMBIENT_LIGHTING_HDR ) ) - { - pSrcHDR = (CompressedLightCube*)pSrcBaseHDR; - } - g_LeafAmbientIndexHDR.SetCount( numLeafs ); - g_LeafAmbientLightingHDR.SetCount( numLeafs ); - - for ( int i = 0; i < numLeafs; i++ ) - { - g_LeafAmbientIndexLDR[i].ambientSampleCount = 1; - g_LeafAmbientIndexLDR[i].firstAmbientSample = i; - g_LeafAmbientIndexHDR[i].ambientSampleCount = 1; - g_LeafAmbientIndexHDR[i].firstAmbientSample = i; - - Q_memset( &g_LeafAmbientLightingLDR[i], 0, sizeof(g_LeafAmbientLightingLDR[i]) ); - Q_memset( &g_LeafAmbientLightingHDR[i], 0, sizeof(g_LeafAmbientLightingHDR[i]) ); - - if ( pSrc ) - { - if ( g_bSwapOnLoad ) - { - g_Swap.SwapFieldsToTargetEndian( &pSrc[i] ); - } - g_LeafAmbientLightingLDR[i].cube = pSrc[i]; - } - if ( pSrcHDR ) - { - if ( g_bSwapOnLoad ) - { - g_Swap.SwapFieldsToTargetEndian( &pSrcHDR[i] ); - } - g_LeafAmbientLightingHDR[i].cube = pSrcHDR[i]; - } - } - - g_Lumps.bLumpParsed[LUMP_LEAF_AMBIENT_LIGHTING] = true; - g_Lumps.bLumpParsed[LUMP_LEAF_AMBIENT_INDEX] = true; - g_Lumps.bLumpParsed[LUMP_LEAF_AMBIENT_LIGHTING_HDR] = true; - g_Lumps.bLumpParsed[LUMP_LEAF_AMBIENT_INDEX_HDR] = true; - } - else - { - CopyOptionalLump( LUMP_LEAF_AMBIENT_LIGHTING, g_LeafAmbientLightingLDR ); - CopyOptionalLump( LUMP_LEAF_AMBIENT_INDEX, g_LeafAmbientIndexLDR ); - CopyOptionalLump( LUMP_LEAF_AMBIENT_LIGHTING_HDR, g_LeafAmbientLightingHDR ); - CopyOptionalLump( LUMP_LEAF_AMBIENT_INDEX_HDR, g_LeafAmbientIndexHDR ); - } -} - -void ValidateHeader( const char *filename, const dheader_t *pHeader ) -{ - if ( pHeader->ident != IDBSPHEADER ) - { - Error ("%s is not a IBSP file", filename); - } - if ( pHeader->version < MINBSPVERSION || pHeader->version > BSPVERSION ) - { - Error ("%s is version %i, not %i", filename, pHeader->version, BSPVERSION); - } -} - -//----------------------------------------------------------------------------- -// Low level BSP opener for external parsing. Parses headers, but nothing else. -// You must close the BSP, via CloseBSPFile(). -//----------------------------------------------------------------------------- -void OpenBSPFile( const char *filename ) -{ - Lumps_Init(); - - // load the file header - LoadFile( filename, (void **)&g_pBSPHeader ); - - if ( g_bSwapOnLoad ) - { - g_Swap.ActivateByteSwapping( true ); - g_Swap.SwapFieldsToTargetEndian( g_pBSPHeader ); - } - - ValidateHeader( filename, g_pBSPHeader ); - - g_MapRevision = g_pBSPHeader->mapRevision; -} - -//----------------------------------------------------------------------------- -// CloseBSPFile -//----------------------------------------------------------------------------- -void CloseBSPFile( void ) -{ - free( g_pBSPHeader ); - g_pBSPHeader = NULL; -} - -//----------------------------------------------------------------------------- -// LoadBSPFile -//----------------------------------------------------------------------------- -void LoadBSPFile( const char *filename ) -{ - OpenBSPFile( filename ); - - nummodels = CopyLump( LUMP_MODELS, dmodels ); - numvertexes = CopyLump( LUMP_VERTEXES, dvertexes ); - numplanes = CopyLump( LUMP_PLANES, dplanes ); - numleafs = LoadLeafs(); - numnodes = CopyLump( LUMP_NODES, dnodes ); - CopyLump( LUMP_TEXINFO, texinfo ); - numtexdata = CopyLump( LUMP_TEXDATA, dtexdata ); - - CopyLump( LUMP_DISPINFO, g_dispinfo ); - CopyLump( LUMP_DISP_VERTS, g_DispVerts ); - CopyLump( LUMP_DISP_TRIS, g_DispTris ); - CopyLump( FIELD_CHARACTER, LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS, g_DispLightmapSamplePositions ); - CopyLump( LUMP_FACE_MACRO_TEXTURE_INFO, g_FaceMacroTextureInfos ); - - numfaces = CopyLump(LUMP_FACES, dfaces, LUMP_FACES_VERSION); - if ( HasLump( LUMP_FACES_HDR ) ) - numfaces_hdr = CopyLump( LUMP_FACES_HDR, dfaces_hdr, LUMP_FACES_VERSION ); - else - numfaces_hdr = 0; - - CopyOptionalLump( LUMP_FACEIDS, dfaceids ); - - g_numprimitives = CopyLump( LUMP_PRIMITIVES, g_primitives ); - g_numprimverts = CopyLump( LUMP_PRIMVERTS, g_primverts ); - g_numprimindices = CopyLump( FIELD_SHORT, LUMP_PRIMINDICES, g_primindices ); - numorigfaces = CopyLump( LUMP_ORIGINALFACES, dorigfaces ); // original faces - numleaffaces = CopyLump( FIELD_SHORT, LUMP_LEAFFACES, dleaffaces ); - numleafbrushes = CopyLump( FIELD_SHORT, LUMP_LEAFBRUSHES, dleafbrushes ); - numsurfedges = CopyLump( FIELD_INTEGER, LUMP_SURFEDGES, dsurfedges ); - numedges = CopyLump( LUMP_EDGES, dedges ); - numbrushes = CopyLump( LUMP_BRUSHES, dbrushes ); - numbrushsides = CopyLump( LUMP_BRUSHSIDES, dbrushsides ); - numareas = CopyLump( LUMP_AREAS, dareas ); - numareaportals = CopyLump( LUMP_AREAPORTALS, dareaportals ); - - visdatasize = CopyLump ( FIELD_CHARACTER, LUMP_VISIBILITY, dvisdata ); - CopyOptionalLump( FIELD_CHARACTER, LUMP_LIGHTING, dlightdataLDR, LUMP_LIGHTING_VERSION ); - CopyOptionalLump( FIELD_CHARACTER, LUMP_LIGHTING_HDR, dlightdataHDR, LUMP_LIGHTING_VERSION ); - - LoadLeafAmbientLighting( numleafs ); - - CopyLump( FIELD_CHARACTER, LUMP_ENTITIES, dentdata ); - numworldlightsLDR = CopyLump( LUMP_WORLDLIGHTS, dworldlightsLDR ); - numworldlightsHDR = CopyLump( LUMP_WORLDLIGHTS_HDR, dworldlightsHDR ); - - numleafwaterdata = CopyLump( LUMP_LEAFWATERDATA, dleafwaterdata ); - g_PhysCollideSize = CopyVariableLump( FIELD_CHARACTER, LUMP_PHYSCOLLIDE, (void**)&g_pPhysCollide ); - g_PhysDispSize = CopyVariableLump( FIELD_CHARACTER, LUMP_PHYSDISP, (void**)&g_pPhysDisp ); - - g_numvertnormals = CopyLump( FIELD_VECTOR, LUMP_VERTNORMALS, (float*)g_vertnormals ); - g_numvertnormalindices = CopyLump( FIELD_SHORT, LUMP_VERTNORMALINDICES, g_vertnormalindices ); - - g_nClipPortalVerts = CopyLump( FIELD_VECTOR, LUMP_CLIPPORTALVERTS, (float*)g_ClipPortalVerts ); - g_nCubemapSamples = CopyLump( LUMP_CUBEMAPS, g_CubemapSamples ); - - CopyLump( FIELD_CHARACTER, LUMP_TEXDATA_STRING_DATA, g_TexDataStringData ); - CopyLump( FIELD_INTEGER, LUMP_TEXDATA_STRING_TABLE, g_TexDataStringTable ); - - g_nOverlayCount = CopyLump( LUMP_OVERLAYS, g_Overlays ); - g_nWaterOverlayCount = CopyLump( LUMP_WATEROVERLAYS, g_WaterOverlays ); - CopyLump( LUMP_OVERLAY_FADES, g_OverlayFades ); - - dflagslump_t flags_lump; - - if ( HasLump( LUMP_MAP_FLAGS ) ) - CopyLump ( LUMP_MAP_FLAGS, &flags_lump ); - else - memset( &flags_lump, 0, sizeof( flags_lump ) ); // default flags to 0 - - g_LevelFlags = flags_lump.m_LevelFlags; - - LoadOcclusionLump(); - - CopyLump( FIELD_SHORT, LUMP_LEAFMINDISTTOWATER, g_LeafMinDistToWater ); - - /* - int crap; - for( crap = 0; crap < g_nBSPStringTable; crap++ ) - { - Msg( "stringtable %d", ( int )crap ); - Msg( " %d:", ( int )g_BSPStringTable[crap] ); - puts( &g_BSPStringData[g_BSPStringTable[crap]] ); - puts( "\n" ); - } - */ - - // Load PAK file lump into appropriate data structure - byte *pakbuffer = NULL; - int paksize = CopyVariableLump( FIELD_CHARACTER, LUMP_PAKFILE, ( void ** )&pakbuffer ); - if ( paksize > 0 ) - { - GetPakFile()->ActivateByteSwapping( IsX360() ); - GetPakFile()->ParseFromBuffer( pakbuffer, paksize ); - } - else - { - GetPakFile()->Reset(); - } - - free( pakbuffer ); - - g_GameLumps.ParseGameLump( g_pBSPHeader ); - - // NOTE: Do NOT call CopyLump after Lumps_Parse() it parses all un-Copied lumps - // parse any additional lumps - Lumps_Parse(); - - // everything has been copied out - CloseBSPFile(); - - g_Swap.ActivateByteSwapping( false ); -} - -//----------------------------------------------------------------------------- -// Reset any state. -//----------------------------------------------------------------------------- -void UnloadBSPFile() -{ - nummodels = 0; - numvertexes = 0; - numplanes = 0; - - numleafs = 0; -#if defined( BSP_USE_LESS_MEMORY ) - if ( dleafs ) - { - free( dleafs ); - dleafs = NULL; - } -#endif - - numnodes = 0; - texinfo.Purge(); - numtexdata = 0; - - g_dispinfo.Purge(); - g_DispVerts.Purge(); - g_DispTris.Purge(); - - g_DispLightmapSamplePositions.Purge(); - g_FaceMacroTextureInfos.Purge(); - - numfaces = 0; - numfaces_hdr = 0; - - dfaceids.Purge(); - - g_numprimitives = 0; - g_numprimverts = 0; - g_numprimindices = 0; - numorigfaces = 0; - numleaffaces = 0; - numleafbrushes = 0; - numsurfedges = 0; - numedges = 0; - numbrushes = 0; - numbrushsides = 0; - numareas = 0; - numareaportals = 0; - - visdatasize = 0; - dlightdataLDR.Purge(); - dlightdataHDR.Purge(); - - g_LeafAmbientLightingLDR.Purge(); - g_LeafAmbientLightingHDR.Purge(); - g_LeafAmbientIndexHDR.Purge(); - g_LeafAmbientIndexLDR.Purge(); - - dentdata.Purge(); - numworldlightsLDR = 0; - numworldlightsHDR = 0; - - numleafwaterdata = 0; - - if ( g_pPhysCollide ) - { - free( g_pPhysCollide ); - g_pPhysCollide = NULL; - } - g_PhysCollideSize = 0; - - if ( g_pPhysDisp ) - { - free( g_pPhysDisp ); - g_pPhysDisp = NULL; - } - g_PhysDispSize = 0; - - g_numvertnormals = 0; - g_numvertnormalindices = 0; - - g_nClipPortalVerts = 0; - g_nCubemapSamples = 0; - - g_TexDataStringData.Purge(); - g_TexDataStringTable.Purge(); - - g_nOverlayCount = 0; - g_nWaterOverlayCount = 0; - - g_LevelFlags = 0; - - g_OccluderData.Purge(); - g_OccluderPolyData.Purge(); - g_OccluderVertexIndices.Purge(); - - g_GameLumps.DestroyAllGameLumps(); - - for ( int i = 0; i < HEADER_LUMPS; i++ ) - { - if ( g_Lumps.pLumps[i] ) - { - free( g_Lumps.pLumps[i] ); - g_Lumps.pLumps[i] = NULL; - } - } - - ReleasePakFileLumps(); -} - -//----------------------------------------------------------------------------- -// LoadBSPFileFilesystemOnly -//----------------------------------------------------------------------------- -void LoadBSPFile_FileSystemOnly( const char *filename ) -{ - Lumps_Init(); - - // - // load the file header - // - LoadFile( filename, (void **)&g_pBSPHeader ); - - ValidateHeader( filename, g_pBSPHeader ); - - // Load PAK file lump into appropriate data structure - byte *pakbuffer = NULL; - int paksize = CopyVariableLump( FIELD_CHARACTER, LUMP_PAKFILE, ( void ** )&pakbuffer, 1 ); - if ( paksize > 0 ) - { - GetPakFile()->ParseFromBuffer( pakbuffer, paksize ); - } - else - { - GetPakFile()->Reset(); - } - - free( pakbuffer ); - - // everything has been copied out - free( g_pBSPHeader ); - g_pBSPHeader = NULL; -} - -void ExtractZipFileFromBSP( char *pBSPFileName, char *pZipFileName ) -{ - Lumps_Init(); - - // - // load the file header - // - LoadFile( pBSPFileName, (void **)&g_pBSPHeader); - - ValidateHeader( pBSPFileName, g_pBSPHeader ); - - byte *pakbuffer = NULL; - int paksize = CopyVariableLump( FIELD_CHARACTER, LUMP_PAKFILE, ( void ** )&pakbuffer ); - if ( paksize > 0 ) - { - FILE *fp; - fp = fopen( pZipFileName, "wb" ); - if( !fp ) - { - fprintf( stderr, "can't open %s\n", pZipFileName ); - return; - } - - fwrite( pakbuffer, paksize, 1, fp ); - fclose( fp ); - } - else - { - fprintf( stderr, "zip file is zero length!\n" ); - } -} - -/* -============= -LoadBSPFileTexinfo - -Only loads the texinfo lump, so qdata can scan for textures -============= -*/ -void LoadBSPFileTexinfo( const char *filename ) -{ - FILE *f; - int length, ofs; - - g_pBSPHeader = (dheader_t*)malloc( sizeof(dheader_t) ); - - f = fopen( filename, "rb" ); - fread( g_pBSPHeader, sizeof(dheader_t), 1, f); - - ValidateHeader( filename, g_pBSPHeader ); - - length = g_pBSPHeader->lumps[LUMP_TEXINFO].filelen; - ofs = g_pBSPHeader->lumps[LUMP_TEXINFO].fileofs; - - int nCount = length / sizeof(texinfo_t); - - texinfo.Purge(); - texinfo.AddMultipleToTail( nCount ); - - fseek( f, ofs, SEEK_SET ); - fread( texinfo.Base(), length, 1, f ); - fclose( f ); - - // everything has been copied out - free( g_pBSPHeader ); - g_pBSPHeader = NULL; -} - -static void AddLumpInternal( int lumpnum, void *data, int len, int version ) -{ - lump_t *lump; - - g_Lumps.size[lumpnum] = 0; // mark it written - - lump = &g_pBSPHeader->lumps[lumpnum]; - - lump->fileofs = g_pFileSystem->Tell( g_hBSPFile ); - lump->filelen = len; - lump->version = version; - lump->fourCC[0] = ( char )0; - lump->fourCC[1] = ( char )0; - lump->fourCC[2] = ( char )0; - lump->fourCC[3] = ( char )0; - - SafeWrite( g_hBSPFile, data, len ); - - // pad out to the next dword - AlignFilePosition( g_hBSPFile, 4 ); -} - -template< class T > -static void SwapInPlace( T *pData, int count ) -{ - if ( !pData ) - return; - - // use the datadesc to swap the fields in place - g_Swap.SwapFieldsToTargetEndian( (T*)pData, pData, count ); -} - -template< class T > -static void SwapInPlace( int fieldType, T *pData, int count ) -{ - if ( !pData ) - return; - - // swap the data in place - g_Swap.SwapBufferToTargetEndian( (T*)pData, (T*)pData, count ); -} - -//----------------------------------------------------------------------------- -// Add raw data chunk to file (not a lump) -//----------------------------------------------------------------------------- -template< class T > -static void WriteData( int fieldType, T *pData, int count ) -{ - if ( g_bSwapOnWrite ) - { - SwapInPlace( fieldType, pData, count ); - } - SafeWrite( g_hBSPFile, pData, count * sizeof(T) ); -} - -template< class T > -static void WriteData( T *pData, int count ) -{ - if ( g_bSwapOnWrite ) - { - SwapInPlace( pData, count ); - } - SafeWrite( g_hBSPFile, pData, count * sizeof(T) ); -} - -//----------------------------------------------------------------------------- -// Add Lump of object types with datadescs -//----------------------------------------------------------------------------- -template< class T > -static void AddLump( int lumpnum, T *pData, int count, int version ) -{ - AddLumpInternal( lumpnum, pData, count * sizeof(T), version ); -} - -template< class T > -static void AddLump( int lumpnum, CUtlVector &data, int version ) -{ - AddLumpInternal( lumpnum, data.Base(), data.Count() * sizeof(T), version ); -} - -/* -============= -WriteBSPFile - -Swaps the bsp file in place, so it should not be referenced again -============= -*/ -void WriteBSPFile( const char *filename, char *pUnused ) -{ - if ( texinfo.Count() > MAX_MAP_TEXINFO ) - { - Error( "Map has too many texinfos (has %d, can have at most %d)\n", texinfo.Count(), MAX_MAP_TEXINFO ); - return; - } - - dheader_t outHeader; - g_pBSPHeader = &outHeader; - memset( g_pBSPHeader, 0, sizeof( dheader_t ) ); - - g_pBSPHeader->ident = IDBSPHEADER; - g_pBSPHeader->version = BSPVERSION; - g_pBSPHeader->mapRevision = g_MapRevision; - - g_hBSPFile = SafeOpenWrite( filename ); - WriteData( g_pBSPHeader ); // overwritten later - - AddLump( LUMP_PLANES, dplanes, numplanes ); - AddLump( LUMP_LEAFS, dleafs, numleafs, LUMP_LEAFS_VERSION ); - AddLump( LUMP_LEAF_AMBIENT_LIGHTING, g_LeafAmbientLightingLDR, LUMP_LEAF_AMBIENT_LIGHTING_VERSION ); - AddLump( LUMP_LEAF_AMBIENT_INDEX, g_LeafAmbientIndexLDR ); - AddLump( LUMP_LEAF_AMBIENT_INDEX_HDR, g_LeafAmbientIndexHDR ); - AddLump( LUMP_LEAF_AMBIENT_LIGHTING_HDR, g_LeafAmbientLightingHDR, LUMP_LEAF_AMBIENT_LIGHTING_VERSION ); - - AddLump( LUMP_VERTEXES, dvertexes, numvertexes ); - AddLump( LUMP_NODES, dnodes, numnodes ); - AddLump( LUMP_TEXINFO, texinfo ); - AddLump( LUMP_TEXDATA, dtexdata, numtexdata ); - - AddLump( LUMP_DISPINFO, g_dispinfo ); - AddLump( LUMP_DISP_VERTS, g_DispVerts ); - AddLump( LUMP_DISP_TRIS, g_DispTris ); - AddLump( LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS, g_DispLightmapSamplePositions ); - AddLump( LUMP_FACE_MACRO_TEXTURE_INFO, g_FaceMacroTextureInfos ); - - AddLump( LUMP_PRIMITIVES, g_primitives, g_numprimitives ); - AddLump( LUMP_PRIMVERTS, g_primverts, g_numprimverts ); - AddLump( LUMP_PRIMINDICES, g_primindices, g_numprimindices ); - AddLump( LUMP_FACES, dfaces, numfaces, LUMP_FACES_VERSION ); - if (numfaces_hdr) - AddLump( LUMP_FACES_HDR, dfaces_hdr, numfaces_hdr, LUMP_FACES_VERSION ); - AddLump ( LUMP_FACEIDS, dfaceids, numfaceids ); - - AddLump( LUMP_ORIGINALFACES, dorigfaces, numorigfaces ); // original faces lump - AddLump( LUMP_BRUSHES, dbrushes, numbrushes ); - AddLump( LUMP_BRUSHSIDES, dbrushsides, numbrushsides ); - AddLump( LUMP_LEAFFACES, dleaffaces, numleaffaces ); - AddLump( LUMP_LEAFBRUSHES, dleafbrushes, numleafbrushes ); - AddLump( LUMP_SURFEDGES, dsurfedges, numsurfedges ); - AddLump( LUMP_EDGES, dedges, numedges ); - AddLump( LUMP_MODELS, dmodels, nummodels ); - AddLump( LUMP_AREAS, dareas, numareas ); - AddLump( LUMP_AREAPORTALS, dareaportals, numareaportals ); - - AddLump( LUMP_LIGHTING, dlightdataLDR, LUMP_LIGHTING_VERSION ); - AddLump( LUMP_LIGHTING_HDR, dlightdataHDR, LUMP_LIGHTING_VERSION ); - AddLump( LUMP_VISIBILITY, dvisdata, visdatasize ); - AddLump( LUMP_ENTITIES, dentdata ); - AddLump( LUMP_WORLDLIGHTS, dworldlightsLDR, numworldlightsLDR ); - AddLump( LUMP_WORLDLIGHTS_HDR, dworldlightsHDR, numworldlightsHDR ); - AddLump( LUMP_LEAFWATERDATA, dleafwaterdata, numleafwaterdata ); - - AddOcclusionLump(); - - dflagslump_t flags_lump; - flags_lump.m_LevelFlags = g_LevelFlags; - AddLump( LUMP_MAP_FLAGS, &flags_lump, 1 ); - - // NOTE: This is just for debugging, so it is disabled in release maps -#if 0 - // add the vis portals to the BSP for visualization - AddLump( LUMP_PORTALS, dportals, numportals ); - AddLump( LUMP_CLUSTERS, dclusters, numclusters ); - AddLump( LUMP_PORTALVERTS, dportalverts, numportalverts ); - AddLump( LUMP_CLUSTERPORTALS, dclusterportals, numclusterportals ); -#endif - - AddLump( LUMP_CLIPPORTALVERTS, (float*)g_ClipPortalVerts, g_nClipPortalVerts * 3 ); - AddLump( LUMP_CUBEMAPS, g_CubemapSamples, g_nCubemapSamples ); - AddLump( LUMP_TEXDATA_STRING_DATA, g_TexDataStringData ); - AddLump( LUMP_TEXDATA_STRING_TABLE, g_TexDataStringTable ); - AddLump( LUMP_OVERLAYS, g_Overlays, g_nOverlayCount ); - AddLump( LUMP_WATEROVERLAYS, g_WaterOverlays, g_nWaterOverlayCount ); - AddLump( LUMP_OVERLAY_FADES, g_OverlayFades, g_nOverlayCount ); - - if ( g_pPhysCollide ) - { - AddLump( LUMP_PHYSCOLLIDE, g_pPhysCollide, g_PhysCollideSize ); - } - - if ( g_pPhysDisp ) - { - AddLump ( LUMP_PHYSDISP, g_pPhysDisp, g_PhysDispSize ); - } - - AddLump( LUMP_VERTNORMALS, (float*)g_vertnormals, g_numvertnormals * 3 ); - AddLump( LUMP_VERTNORMALINDICES, g_vertnormalindices, g_numvertnormalindices ); - - AddLump( LUMP_LEAFMINDISTTOWATER, g_LeafMinDistToWater, numleafs ); - - AddGameLumps(); - - // Write pakfile lump to disk - WritePakFileLump(); - - // NOTE: Do NOT call AddLump after Lumps_Write() it writes all un-Added lumps - // write any additional lumps - Lumps_Write(); - - g_pFileSystem->Seek( g_hBSPFile, 0, FILESYSTEM_SEEK_HEAD ); - WriteData( g_pBSPHeader ); - g_pFileSystem->Close( g_hBSPFile ); -} - -// Generate the next clear lump filename for the bsp file -bool GenerateNextLumpFileName( const char *bspfilename, char *lumpfilename, int buffsize ) -{ - for (int i = 0; i < MAX_LUMPFILES; i++) - { - GenerateLumpFileName( bspfilename, lumpfilename, buffsize, i ); - - if ( !g_pFileSystem->FileExists( lumpfilename ) ) - return true; - } - - return false; -} - -void WriteLumpToFile( char *filename, int lump ) -{ - if ( !HasLump(lump) ) - return; - - char lumppre[MAX_PATH]; - if ( !GenerateNextLumpFileName( filename, lumppre, MAX_PATH ) ) - { - Warning( "Failed to find valid lump filename for bsp %s.\n", filename ); - return; - } - - // Open the file - FileHandle_t lumpfile = g_pFileSystem->Open(lumppre, "wb"); - if ( !lumpfile ) - { - Error ("Error opening %s! (Check for write enable)\n",filename); - return; - } - - int ofs = g_pBSPHeader->lumps[lump].fileofs; - int length = g_pBSPHeader->lumps[lump].filelen; - - // Write the header - lumpfileheader_t lumpHeader; - lumpHeader.lumpID = lump; - lumpHeader.lumpVersion = LumpVersion(lump); - lumpHeader.lumpLength = length; - lumpHeader.mapRevision = LittleLong( g_MapRevision ); - lumpHeader.lumpOffset = sizeof(lumpfileheader_t); // Lump starts after the header - SafeWrite (lumpfile, &lumpHeader, sizeof(lumpfileheader_t)); - - // Write the lump - SafeWrite (lumpfile, (byte *)g_pBSPHeader + ofs, length); -} - -void WriteLumpToFile( char *filename, int lump, int nLumpVersion, void *pBuffer, size_t nBufLen ) -{ - char lumppre[MAX_PATH]; - if ( !GenerateNextLumpFileName( filename, lumppre, MAX_PATH ) ) - { - Warning( "Failed to find valid lump filename for bsp %s.\n", filename ); - return; - } - - // Open the file - FileHandle_t lumpfile = g_pFileSystem->Open(lumppre, "wb"); - if ( !lumpfile ) - { - Error ("Error opening %s! (Check for write enable)\n",filename); - return; - } - - // Write the header - lumpfileheader_t lumpHeader; - lumpHeader.lumpID = lump; - lumpHeader.lumpVersion = nLumpVersion; - lumpHeader.lumpLength = nBufLen; - lumpHeader.mapRevision = LittleLong( g_MapRevision ); - lumpHeader.lumpOffset = sizeof(lumpfileheader_t); // Lump starts after the header - SafeWrite( lumpfile, &lumpHeader, sizeof(lumpfileheader_t)); - - // Write the lump - SafeWrite( lumpfile, pBuffer, nBufLen ); - - g_pFileSystem->Close( lumpfile ); -} - - -//============================================================================ -#define ENTRIES(a) (sizeof(a)/sizeof(*(a))) -#define ENTRYSIZE(a) (sizeof(*(a))) - -int ArrayUsage( const char *szItem, int items, int maxitems, int itemsize ) -{ - float percentage = maxitems ? items * 100.0 / maxitems : 0.0; - - Msg("%-17.17s %8i/%-8i %8i/%-8i (%4.1f%%) ", - szItem, items, maxitems, items * itemsize, maxitems * itemsize, percentage ); - if ( percentage > 80.0 ) - Msg( "VERY FULL!\n" ); - else if ( percentage > 95.0 ) - Msg( "SIZE DANGER!\n" ); - else if ( percentage > 99.9 ) - Msg( "SIZE OVERFLOW!!!\n" ); - else - Msg( "\n" ); - return items * itemsize; -} - -int GlobUsage( const char *szItem, int itemstorage, int maxstorage ) -{ - float percentage = maxstorage ? itemstorage * 100.0 / maxstorage : 0.0; - Msg("%-17.17s [variable] %8i/%-8i (%4.1f%%) ", - szItem, itemstorage, maxstorage, percentage ); - if ( percentage > 80.0 ) - Msg( "VERY FULL!\n" ); - else if ( percentage > 95.0 ) - Msg( "SIZE DANGER!\n" ); - else if ( percentage > 99.9 ) - Msg( "SIZE OVERFLOW!!!\n" ); - else - Msg( "\n" ); - return itemstorage; -} - -/* -============= -PrintBSPFileSizes - -Dumps info about current file -============= -*/ -void PrintBSPFileSizes (void) -{ - int totalmemory = 0; - -// if (!num_entities) -// ParseEntities (); - - Msg("\n"); - Msg( "%-17s %16s %16s %9s \n", "Object names", "Objects/Maxobjs", "Memory / Maxmem", "Fullness" ); - Msg( "%-17s %16s %16s %9s \n", "------------", "---------------", "---------------", "--------" ); - - totalmemory += ArrayUsage( "models", nummodels, ENTRIES(dmodels), ENTRYSIZE(dmodels) ); - totalmemory += ArrayUsage( "brushes", numbrushes, ENTRIES(dbrushes), ENTRYSIZE(dbrushes) ); - totalmemory += ArrayUsage( "brushsides", numbrushsides, ENTRIES(dbrushsides), ENTRYSIZE(dbrushsides) ); - totalmemory += ArrayUsage( "planes", numplanes, ENTRIES(dplanes), ENTRYSIZE(dplanes) ); - totalmemory += ArrayUsage( "vertexes", numvertexes, ENTRIES(dvertexes), ENTRYSIZE(dvertexes) ); - totalmemory += ArrayUsage( "nodes", numnodes, ENTRIES(dnodes), ENTRYSIZE(dnodes) ); - totalmemory += ArrayUsage( "texinfos", texinfo.Count(),MAX_MAP_TEXINFO, sizeof(texinfo_t) ); - totalmemory += ArrayUsage( "texdata", numtexdata, ENTRIES(dtexdata), ENTRYSIZE(dtexdata) ); - - totalmemory += ArrayUsage( "dispinfos", g_dispinfo.Count(), 0, sizeof( ddispinfo_t ) ); - totalmemory += ArrayUsage( "disp_verts", g_DispVerts.Count(), 0, sizeof( g_DispVerts[0] ) ); - totalmemory += ArrayUsage( "disp_tris", g_DispTris.Count(), 0, sizeof( g_DispTris[0] ) ); - totalmemory += ArrayUsage( "disp_lmsamples",g_DispLightmapSamplePositions.Count(),0,sizeof( g_DispLightmapSamplePositions[0] ) ); - - totalmemory += ArrayUsage( "faces", numfaces, ENTRIES(dfaces), ENTRYSIZE(dfaces) ); - totalmemory += ArrayUsage( "hdr faces", numfaces_hdr, ENTRIES(dfaces_hdr), ENTRYSIZE(dfaces_hdr) ); - totalmemory += ArrayUsage( "origfaces", numorigfaces, ENTRIES(dorigfaces), ENTRYSIZE(dorigfaces) ); // original faces - totalmemory += ArrayUsage( "leaves", numleafs, ENTRIES(dleafs), ENTRYSIZE(dleafs) ); - totalmemory += ArrayUsage( "leaffaces", numleaffaces, ENTRIES(dleaffaces), ENTRYSIZE(dleaffaces) ); - totalmemory += ArrayUsage( "leafbrushes", numleafbrushes, ENTRIES(dleafbrushes), ENTRYSIZE(dleafbrushes) ); - totalmemory += ArrayUsage( "areas", numareas, ENTRIES(dareas), ENTRYSIZE(dareas) ); - totalmemory += ArrayUsage( "surfedges", numsurfedges, ENTRIES(dsurfedges), ENTRYSIZE(dsurfedges) ); - totalmemory += ArrayUsage( "edges", numedges, ENTRIES(dedges), ENTRYSIZE(dedges) ); - totalmemory += ArrayUsage( "LDR worldlights", numworldlightsLDR, ENTRIES(dworldlightsLDR), ENTRYSIZE(dworldlightsLDR) ); - totalmemory += ArrayUsage( "HDR worldlights", numworldlightsHDR, ENTRIES(dworldlightsHDR), ENTRYSIZE(dworldlightsHDR) ); - - totalmemory += ArrayUsage( "leafwaterdata", numleafwaterdata,ENTRIES(dleafwaterdata), ENTRYSIZE(dleafwaterdata) ); - totalmemory += ArrayUsage( "waterstrips", g_numprimitives,ENTRIES(g_primitives), ENTRYSIZE(g_primitives) ); - totalmemory += ArrayUsage( "waterverts", g_numprimverts, ENTRIES(g_primverts), ENTRYSIZE(g_primverts) ); - totalmemory += ArrayUsage( "waterindices", g_numprimindices,ENTRIES(g_primindices),ENTRYSIZE(g_primindices) ); - totalmemory += ArrayUsage( "cubemapsamples", g_nCubemapSamples,ENTRIES(g_CubemapSamples),ENTRYSIZE(g_CubemapSamples) ); - totalmemory += ArrayUsage( "overlays", g_nOverlayCount, ENTRIES(g_Overlays), ENTRYSIZE(g_Overlays) ); - - totalmemory += GlobUsage( "LDR lightdata", dlightdataLDR.Count(), 0 ); - totalmemory += GlobUsage( "HDR lightdata", dlightdataHDR.Count(), 0 ); - totalmemory += GlobUsage( "visdata", visdatasize, sizeof(dvisdata) ); - totalmemory += GlobUsage( "entdata", dentdata.Count(), 384*1024 ); // goal is <384K - - totalmemory += ArrayUsage( "LDR ambient table", g_LeafAmbientIndexLDR.Count(), MAX_MAP_LEAFS, sizeof( g_LeafAmbientIndexLDR[0] ) ); - totalmemory += ArrayUsage( "HDR ambient table", g_LeafAmbientIndexHDR.Count(), MAX_MAP_LEAFS, sizeof( g_LeafAmbientIndexHDR[0] ) ); - totalmemory += ArrayUsage( "LDR leaf ambient lighting", g_LeafAmbientLightingLDR.Count(), MAX_MAP_LEAFS, sizeof( g_LeafAmbientLightingLDR[0] ) ); - totalmemory += ArrayUsage( "HDR leaf ambient lighting", g_LeafAmbientLightingHDR.Count(), MAX_MAP_LEAFS, sizeof( g_LeafAmbientLightingHDR[0] ) ); - - totalmemory += ArrayUsage( "occluders", g_OccluderData.Count(), 0, sizeof( g_OccluderData[0] ) ); - totalmemory += ArrayUsage( "occluder polygons", g_OccluderPolyData.Count(), 0, sizeof( g_OccluderPolyData[0] ) ); - totalmemory += ArrayUsage( "occluder vert ind",g_OccluderVertexIndices.Count(),0, sizeof( g_OccluderVertexIndices[0] ) ); - - GameLumpHandle_t h = g_GameLumps.GetGameLumpHandle( GAMELUMP_DETAIL_PROPS ); - if (h != g_GameLumps.InvalidGameLump()) - totalmemory += GlobUsage( "detail props", 1, g_GameLumps.GameLumpSize(h) ); - h = g_GameLumps.GetGameLumpHandle( GAMELUMP_DETAIL_PROP_LIGHTING ); - if (h != g_GameLumps.InvalidGameLump()) - totalmemory += GlobUsage( "dtl prp lght", 1, g_GameLumps.GameLumpSize(h) ); - h = g_GameLumps.GetGameLumpHandle( GAMELUMP_DETAIL_PROP_LIGHTING_HDR ); - if (h != g_GameLumps.InvalidGameLump()) - totalmemory += GlobUsage( "HDR dtl prp lght", 1, g_GameLumps.GameLumpSize(h) ); - h = g_GameLumps.GetGameLumpHandle( GAMELUMP_STATIC_PROPS ); - if (h != g_GameLumps.InvalidGameLump()) - totalmemory += GlobUsage( "static props", 1, g_GameLumps.GameLumpSize(h) ); - - totalmemory += GlobUsage( "pakfile", GetPakFile()->EstimateSize(), 0 ); - // HACKHACK: Set physics limit at 4MB, in reality this is totally dynamic - totalmemory += GlobUsage( "physics", g_PhysCollideSize, 4*1024*1024 ); - totalmemory += GlobUsage( "physics terrain", g_PhysDispSize, 1*1024*1024 ); - - Msg( "\nLevel flags = %x\n", g_LevelFlags ); - - Msg( "\n" ); - - int triangleCount = 0; - - for ( int i = 0; i < numfaces; i++ ) - { - // face tris = numedges - 2 - triangleCount += dfaces[i].numedges - 2; - } - Msg("Total triangle count: %d\n", triangleCount ); - - // UNDONE: - // areaportals, portals, texdata, clusters, worldlights, portalverts -} - -/* -============= -PrintBSPPackDirectory - -Dumps a list of files stored in the bsp pack. -============= -*/ -void PrintBSPPackDirectory( void ) -{ - GetPakFile()->PrintDirectory(); -} - - -//============================================ - -int num_entities; -entity_t entities[MAX_MAP_ENTITIES]; - -void StripTrailing (char *e) -{ - char *s; - - s = e + strlen(e)-1; - while (s >= e && *s <= 32) - { - *s = 0; - s--; - } -} - -/* -================= -ParseEpair -================= -*/ -epair_t *ParseEpair (void) -{ - epair_t *e; - - e = (epair_t*)malloc (sizeof(epair_t)); - memset (e, 0, sizeof(epair_t)); - - if (strlen(token) >= MAX_KEY-1) - Error ("ParseEpar: token too long"); - e->key = copystring(token); - - GetToken (false); - if (strlen(token) >= MAX_VALUE-1) - Error ("ParseEpar: token too long"); - e->value = copystring(token); - - // strip trailing spaces - StripTrailing (e->key); - StripTrailing (e->value); - - return e; -} - - -/* -================ -ParseEntity -================ -*/ -qboolean ParseEntity (void) -{ - epair_t *e; - entity_t *mapent; - - if (!GetToken (true)) - return false; - - if (Q_stricmp (token, "{") ) - Error ("ParseEntity: { not found"); - - if (num_entities == MAX_MAP_ENTITIES) - Error ("num_entities == MAX_MAP_ENTITIES"); - - mapent = &entities[num_entities]; - num_entities++; - - do - { - if (!GetToken (true)) - Error ("ParseEntity: EOF without closing brace"); - if (!Q_stricmp (token, "}") ) - break; - e = ParseEpair (); - e->next = mapent->epairs; - mapent->epairs = e; - } while (1); - - return true; -} - -/* -================ -ParseEntities - -Parses the dentdata string into entities -================ -*/ -void ParseEntities (void) -{ - num_entities = 0; - ParseFromMemory (dentdata.Base(), dentdata.Count()); - - while (ParseEntity ()) - { - } -} - - -/* -================ -UnparseEntities - -Generates the dentdata string from all the entities -================ -*/ -void UnparseEntities (void) -{ - epair_t *ep; - char line[2048]; - int i; - char key[1024], value[1024]; - - CUtlBuffer buffer( 0, 0, CUtlBuffer::TEXT_BUFFER ); - buffer.EnsureCapacity( 256 * 1024 ); - - for (i=0 ; inext) - { - strcpy (key, ep->key); - StripTrailing (key); - strcpy (value, ep->value); - StripTrailing (value); - - sprintf(line, "\"%s\" \"%s\"\n", key, value); - buffer.PutString( line ); - } - buffer.PutString("}\n"); - } - int entdatasize = buffer.TellPut()+1; - - dentdata.SetSize( entdatasize ); - memcpy( dentdata.Base(), buffer.Base(), entdatasize-1 ); - dentdata[entdatasize-1] = 0; -} - -void PrintEntity (entity_t *ent) -{ - epair_t *ep; - - Msg ("------- entity %p -------\n", ent); - for (ep=ent->epairs ; ep ; ep=ep->next) - { - Msg ("%s = %s\n", ep->key, ep->value); - } - -} - -void SetKeyValue(entity_t *ent, const char *key, const char *value) -{ - epair_t *ep; - - for (ep=ent->epairs ; ep ; ep=ep->next) - if (!Q_stricmp (ep->key, key) ) - { - free (ep->value); - ep->value = copystring(value); - return; - } - ep = (epair_t*)malloc (sizeof(*ep)); - ep->next = ent->epairs; - ent->epairs = ep; - ep->key = copystring(key); - ep->value = copystring(value); -} - -char *ValueForKey (entity_t *ent, char *key) -{ - for (epair_t *ep=ent->epairs ; ep ; ep=ep->next) - if (!Q_stricmp (ep->key, key) ) - return ep->value; - return ""; -} - -vec_t FloatForKey (entity_t *ent, char *key) -{ - char *k = ValueForKey (ent, key); - return atof(k); -} - -vec_t FloatForKeyWithDefault (entity_t *ent, char *key, float default_value) -{ - for (epair_t *ep=ent->epairs ; ep ; ep=ep->next) - if (!Q_stricmp (ep->key, key) ) - return atof( ep->value ); - return default_value; -} - - - -int IntForKey (entity_t *ent, char *key) -{ - char *k = ValueForKey (ent, key); - return atol(k); -} - -int IntForKeyWithDefault(entity_t *ent, char *key, int nDefault ) -{ - char *k = ValueForKey (ent, key); - if ( !k[0] ) - return nDefault; - return atol(k); -} - -void GetVectorForKey (entity_t *ent, char *key, Vector& vec) -{ - - char *k = ValueForKey (ent, key); -// scanf into doubles, then assign, so it is vec_t size independent - double v1, v2, v3; - v1 = v2 = v3 = 0; - sscanf (k, "%lf %lf %lf", &v1, &v2, &v3); - vec[0] = v1; - vec[1] = v2; - vec[2] = v3; -} - -void GetVector2DForKey (entity_t *ent, char *key, Vector2D& vec) -{ - double v1, v2; - - char *k = ValueForKey (ent, key); -// scanf into doubles, then assign, so it is vec_t size independent - v1 = v2 = 0; - sscanf (k, "%lf %lf", &v1, &v2); - vec[0] = v1; - vec[1] = v2; -} - -void GetAnglesForKey (entity_t *ent, char *key, QAngle& angle) -{ - char *k; - double v1, v2, v3; - - k = ValueForKey (ent, key); -// scanf into doubles, then assign, so it is vec_t size independent - v1 = v2 = v3 = 0; - sscanf (k, "%lf %lf %lf", &v1, &v2, &v3); - angle[0] = v1; - angle[1] = v2; - angle[2] = v3; -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void BuildFaceCalcWindingData( dface_t *pFace, int *points ) -{ - for( int i = 0; i < pFace->numedges; i++ ) - { - int eIndex = dsurfedges[pFace->firstedge+i]; - if( eIndex < 0 ) - { - points[i] = dedges[-eIndex].v[1]; - } - else - { - points[i] = dedges[eIndex].v[0]; - } - } -} - - -void TriStripToTriList( - unsigned short const *pTriStripIndices, - int nTriStripIndices, - unsigned short **pTriListIndices, - int *pnTriListIndices ) -{ - int nMaxTriListIndices = (nTriStripIndices - 2) * 3; - *pTriListIndices = new unsigned short[ nMaxTriListIndices ]; - *pnTriListIndices = 0; - - for( int i=0; i < nTriStripIndices - 2; i++ ) - { - if( pTriStripIndices[i] == pTriStripIndices[i+1] || - pTriStripIndices[i] == pTriStripIndices[i+2] || - pTriStripIndices[i+1] == pTriStripIndices[i+2] ) - { - } - else - { - // Flip odd numbered tris.. - if( i & 1 ) - { - (*pTriListIndices)[(*pnTriListIndices)++] = pTriStripIndices[i+2]; - (*pTriListIndices)[(*pnTriListIndices)++] = pTriStripIndices[i+1]; - (*pTriListIndices)[(*pnTriListIndices)++] = pTriStripIndices[i]; - } - else - { - (*pTriListIndices)[(*pnTriListIndices)++] = pTriStripIndices[i]; - (*pTriListIndices)[(*pnTriListIndices)++] = pTriStripIndices[i+1]; - (*pTriListIndices)[(*pnTriListIndices)++] = pTriStripIndices[i+2]; - } - } - } -} - - -void CalcTextureCoordsAtPoints( - float const texelsPerWorldUnits[2][4], - int const subtractOffset[2], - Vector const *pPoints, - int const nPoints, - Vector2D *pCoords ) -{ - for( int i=0; i < nPoints; i++ ) - { - for( int iCoord=0; iCoord < 2; iCoord++ ) - { - float *pDestCoord = &pCoords[i][iCoord]; - - *pDestCoord = 0; - for( int iDot=0; iDot < 3; iDot++ ) - *pDestCoord += pPoints[i][iDot] * texelsPerWorldUnits[iCoord][iDot]; - - *pDestCoord += texelsPerWorldUnits[iCoord][3]; - *pDestCoord -= subtractOffset[iCoord]; - } - } -} - - -/* -================ -CalcFaceExtents - -Fills in s->texmins[] and s->texsize[] -================ -*/ -void CalcFaceExtents(dface_t *s, int lightmapTextureMinsInLuxels[2], int lightmapTextureSizeInLuxels[2]) -{ - vec_t mins[2], maxs[2], val=0; - int i,j, e=0; - dvertex_t *v=NULL; - texinfo_t *tex=NULL; - - mins[0] = mins[1] = 1e24; - maxs[0] = maxs[1] = -1e24; - - tex = &texinfo[s->texinfo]; - - for (i=0 ; inumedges ; i++) - { - e = dsurfedges[s->firstedge+i]; - if (e >= 0) - v = dvertexes + dedges[e].v[0]; - else - v = dvertexes + dedges[-e].v[1]; - - for (j=0 ; j<2 ; j++) - { - val = v->point[0] * tex->lightmapVecsLuxelsPerWorldUnits[j][0] + - v->point[1] * tex->lightmapVecsLuxelsPerWorldUnits[j][1] + - v->point[2] * tex->lightmapVecsLuxelsPerWorldUnits[j][2] + - tex->lightmapVecsLuxelsPerWorldUnits[j][3]; - if (val < mins[j]) - mins[j] = val; - if (val > maxs[j]) - maxs[j] = val; - } - } - - int nMaxLightmapDim = (s->dispinfo == -1) ? MAX_LIGHTMAP_DIM_WITHOUT_BORDER : MAX_DISP_LIGHTMAP_DIM_WITHOUT_BORDER; - for (i=0 ; i<2 ; i++) - { - mins[i] = ( float )floor( mins[i] ); - maxs[i] = ( float )ceil( maxs[i] ); - - lightmapTextureMinsInLuxels[i] = ( int )mins[i]; - lightmapTextureSizeInLuxels[i] = ( int )( maxs[i] - mins[i] ); - if( lightmapTextureSizeInLuxels[i] > nMaxLightmapDim + 1 ) - { - Vector point = vec3_origin; - for (int j=0 ; jnumedges ; j++) - { - e = dsurfedges[s->firstedge+j]; - v = (e<0)?dvertexes + dedges[-e].v[1] : dvertexes + dedges[e].v[0]; - point += v->point; - Warning( "Bad surface extents point: %f %f %f\n", v->point.x, v->point.y, v->point.z ); - } - point *= 1.0f/s->numedges; - Error( "Bad surface extents - surface is too big to have a lightmap\n\tmaterial %s around point (%.1f %.1f %.1f)\n\t(dimension: %d, %d>%d)\n", - TexDataStringTable_GetString( dtexdata[texinfo[s->texinfo].texdata].nameStringTableID ), - point.x, point.y, point.z, - ( int )i, - ( int )lightmapTextureSizeInLuxels[i], - ( int )( nMaxLightmapDim + 1 ) - ); - } - } -} - - -void UpdateAllFaceLightmapExtents() -{ - for( int i=0; i < numfaces; i++ ) - { - dface_t *pFace = &dfaces[i]; - - if ( texinfo[pFace->texinfo].flags & (SURF_SKY|SURF_NOLIGHT) ) - continue; // non-lit texture - - CalcFaceExtents( pFace, pFace->m_LightmapTextureMinsInLuxels, pFace->m_LightmapTextureSizeInLuxels ); - } -} - - -//----------------------------------------------------------------------------- -// -// Helper class to iterate over leaves, used by tools -// -//----------------------------------------------------------------------------- - -#define TEST_EPSILON (0.03125) - - -class CToolBSPTree : public ISpatialQuery -{ -public: - // Returns the number of leaves - int LeafCount() const; - - // Enumerates the leaves along a ray, box, etc. - bool EnumerateLeavesAtPoint( Vector const& pt, ISpatialLeafEnumerator* pEnum, int context ); - bool EnumerateLeavesInBox( Vector const& mins, Vector const& maxs, ISpatialLeafEnumerator* pEnum, int context ); - bool EnumerateLeavesInSphere( Vector const& center, float radius, ISpatialLeafEnumerator* pEnum, int context ); - bool EnumerateLeavesAlongRay( Ray_t const& ray, ISpatialLeafEnumerator* pEnum, int context ); -}; - - -//----------------------------------------------------------------------------- -// Returns the number of leaves -//----------------------------------------------------------------------------- - -int CToolBSPTree::LeafCount() const -{ - return numleafs; -} - - -//----------------------------------------------------------------------------- -// Enumerates the leaves at a point -//----------------------------------------------------------------------------- - -bool CToolBSPTree::EnumerateLeavesAtPoint( Vector const& pt, - ISpatialLeafEnumerator* pEnum, int context ) -{ - int node = 0; - while( node >= 0 ) - { - dnode_t* pNode = &dnodes[node]; - dplane_t* pPlane = &dplanes[pNode->planenum]; - - if (DotProduct( pPlane->normal, pt ) <= pPlane->dist) - { - node = pNode->children[1]; - } - else - { - node = pNode->children[0]; - } - } - - return pEnum->EnumerateLeaf( - node - 1, context ); -} - - -//----------------------------------------------------------------------------- -// Enumerates the leaves in a box -//----------------------------------------------------------------------------- - -static bool EnumerateLeavesInBox_R( int node, Vector const& mins, - Vector const& maxs, ISpatialLeafEnumerator* pEnum, int context ) -{ - Vector cornermin, cornermax; - - while( node >= 0 ) - { - dnode_t* pNode = &dnodes[node]; - dplane_t* pPlane = &dplanes[pNode->planenum]; - - // Arbitrary split plane here - for (int i = 0; i < 3; ++i) - { - if (pPlane->normal[i] >= 0) - { - cornermin[i] = mins[i]; - cornermax[i] = maxs[i]; - } - else - { - cornermin[i] = maxs[i]; - cornermax[i] = mins[i]; - } - } - - if ( (DotProduct( pPlane->normal, cornermax ) - pPlane->dist) <= -TEST_EPSILON ) - { - node = pNode->children[1]; - } - else if ( (DotProduct( pPlane->normal, cornermin ) - pPlane->dist) >= TEST_EPSILON ) - { - node = pNode->children[0]; - } - else - { - if (!EnumerateLeavesInBox_R( pNode->children[0], mins, maxs, pEnum, context )) - { - return false; - } - - return EnumerateLeavesInBox_R( pNode->children[1], mins, maxs, pEnum, context ); - } - } - - return pEnum->EnumerateLeaf( - node - 1, context ); -} - -bool CToolBSPTree::EnumerateLeavesInBox( Vector const& mins, Vector const& maxs, - ISpatialLeafEnumerator* pEnum, int context ) -{ - return EnumerateLeavesInBox_R( 0, mins, maxs, pEnum, context ); -} - -//----------------------------------------------------------------------------- -// Enumerate leaves within a sphere -//----------------------------------------------------------------------------- - -static bool EnumerateLeavesInSphere_R( int node, Vector const& origin, - float radius, ISpatialLeafEnumerator* pEnum, int context ) -{ - while( node >= 0 ) - { - dnode_t* pNode = &dnodes[node]; - dplane_t* pPlane = &dplanes[pNode->planenum]; - - if (DotProduct( pPlane->normal, origin ) + radius - pPlane->dist <= -TEST_EPSILON ) - { - node = pNode->children[1]; - } - else if (DotProduct( pPlane->normal, origin ) - radius - pPlane->dist >= TEST_EPSILON ) - { - node = pNode->children[0]; - } - else - { - if (!EnumerateLeavesInSphere_R( pNode->children[0], - origin, radius, pEnum, context )) - { - return false; - } - - return EnumerateLeavesInSphere_R( pNode->children[1], - origin, radius, pEnum, context ); - } - } - - return pEnum->EnumerateLeaf( - node - 1, context ); -} - -bool CToolBSPTree::EnumerateLeavesInSphere( Vector const& center, float radius, ISpatialLeafEnumerator* pEnum, int context ) -{ - return EnumerateLeavesInSphere_R( 0, center, radius, pEnum, context ); -} - - -//----------------------------------------------------------------------------- -// Enumerate leaves along a ray -//----------------------------------------------------------------------------- - -static bool EnumerateLeavesAlongRay_R( int node, Ray_t const& ray, - Vector const& start, Vector const& end, ISpatialLeafEnumerator* pEnum, int context ) -{ - float front,back; - - while (node >= 0) - { - dnode_t* pNode = &dnodes[node]; - dplane_t* pPlane = &dplanes[pNode->planenum]; - - if ( pPlane->type <= PLANE_Z ) - { - front = start[pPlane->type] - pPlane->dist; - back = end[pPlane->type] - pPlane->dist; - } - else - { - front = DotProduct(start, pPlane->normal) - pPlane->dist; - back = DotProduct(end, pPlane->normal) - pPlane->dist; - } - - if (front <= -TEST_EPSILON && back <= -TEST_EPSILON) - { - node = pNode->children[1]; - } - else if (front >= TEST_EPSILON && back >= TEST_EPSILON) - { - node = pNode->children[0]; - } - else - { - // test the front side first - bool side = front < 0; - - // Compute intersection point based on the original ray - float splitfrac; - float denom = DotProduct( ray.m_Delta, pPlane->normal ); - if ( denom == 0.0f ) - { - splitfrac = 1.0f; - } - else - { - splitfrac = ( pPlane->dist - DotProduct( ray.m_Start, pPlane->normal ) ) / denom; - if (splitfrac < 0) - splitfrac = 0; - else if (splitfrac > 1) - splitfrac = 1; - } - - // Compute the split point - Vector split; - VectorMA( ray.m_Start, splitfrac, ray.m_Delta, split ); - - bool r = EnumerateLeavesAlongRay_R (pNode->children[side], ray, start, split, pEnum, context ); - if (!r) - return r; - return EnumerateLeavesAlongRay_R (pNode->children[!side], ray, split, end, pEnum, context); - } - } - - return pEnum->EnumerateLeaf( - node - 1, context ); -} - -bool CToolBSPTree::EnumerateLeavesAlongRay( Ray_t const& ray, ISpatialLeafEnumerator* pEnum, int context ) -{ - if (!ray.m_IsSwept) - { - Vector mins, maxs; - VectorAdd( ray.m_Start, ray.m_Extents, maxs ); - VectorSubtract( ray.m_Start, ray.m_Extents, mins ); - - return EnumerateLeavesInBox_R( 0, mins, maxs, pEnum, context ); - } - - // FIXME: Extruded ray not implemented yet - Assert( ray.m_IsRay ); - - Vector end; - VectorAdd( ray.m_Start, ray.m_Delta, end ); - return EnumerateLeavesAlongRay_R( 0, ray, ray.m_Start, end, pEnum, context ); -} - - -//----------------------------------------------------------------------------- -// Singleton accessor -//----------------------------------------------------------------------------- - -ISpatialQuery* ToolBSPTree() -{ - static CToolBSPTree s_ToolBSPTree; - return &s_ToolBSPTree; -} - - - -//----------------------------------------------------------------------------- -// Enumerates nodes in front to back order... -//----------------------------------------------------------------------------- - -// FIXME: Do we want this in the IBSPTree interface? - -static bool EnumerateNodesAlongRay_R( int node, Ray_t const& ray, float start, float end, - IBSPNodeEnumerator* pEnum, int context ) -{ - float front, back; - float startDotN, deltaDotN; - - while (node >= 0) - { - dnode_t* pNode = &dnodes[node]; - dplane_t* pPlane = &dplanes[pNode->planenum]; - - if ( pPlane->type <= PLANE_Z ) - { - startDotN = ray.m_Start[pPlane->type]; - deltaDotN = ray.m_Delta[pPlane->type]; - } - else - { - startDotN = DotProduct( ray.m_Start, pPlane->normal ); - deltaDotN = DotProduct( ray.m_Delta, pPlane->normal ); - } - - front = startDotN + start * deltaDotN - pPlane->dist; - back = startDotN + end * deltaDotN - pPlane->dist; - - if (front <= -TEST_EPSILON && back <= -TEST_EPSILON) - { - node = pNode->children[1]; - } - else if (front >= TEST_EPSILON && back >= TEST_EPSILON) - { - node = pNode->children[0]; - } - else - { - // test the front side first - bool side = front < 0; - - // Compute intersection point based on the original ray - float splitfrac; - if ( deltaDotN == 0.0f ) - { - splitfrac = 1.0f; - } - else - { - splitfrac = ( pPlane->dist - startDotN ) / deltaDotN; - if (splitfrac < 0.0f) - splitfrac = 0.0f; - else if (splitfrac > 1.0f) - splitfrac = 1.0f; - } - - bool r = EnumerateNodesAlongRay_R (pNode->children[side], ray, start, splitfrac, pEnum, context ); - if (!r) - return r; - - // Visit the node... - if (!pEnum->EnumerateNode( node, ray, splitfrac, context )) - return false; - - return EnumerateNodesAlongRay_R (pNode->children[!side], ray, splitfrac, end, pEnum, context); - } - } - - // Visit the leaf... - return pEnum->EnumerateLeaf( - node - 1, ray, start, end, context ); -} - - -bool EnumerateNodesAlongRay( Ray_t const& ray, IBSPNodeEnumerator* pEnum, int context ) -{ - Vector end; - VectorAdd( ray.m_Start, ray.m_Delta, end ); - return EnumerateNodesAlongRay_R( 0, ray, 0.0f, 1.0f, pEnum, context ); -} - - -//----------------------------------------------------------------------------- -// Helps us find all leaves associated with a particular cluster -//----------------------------------------------------------------------------- -CUtlVector g_ClusterLeaves; - -void BuildClusterTable( void ) -{ - int i, j; - int leafCount; - int leafList[MAX_MAP_LEAFS]; - - g_ClusterLeaves.SetCount( dvis->numclusters ); - for ( i = 0; i < dvis->numclusters; i++ ) - { - leafCount = 0; - for ( j = 0; j < numleafs; j++ ) - { - if ( dleafs[j].cluster == i ) - { - leafList[ leafCount ] = j; - leafCount++; - } - } - - g_ClusterLeaves[i].leafCount = leafCount; - if ( leafCount ) - { - g_ClusterLeaves[i].leafs.SetCount( leafCount ); - memcpy( g_ClusterLeaves[i].leafs.Base(), leafList, sizeof(int) * leafCount ); - } - } -} - -// There's a version of this in host.cpp!!! Make sure that they match. -void GetPlatformMapPath( const char *pMapPath, char *pPlatformMapPath, int dxlevel, int maxLength ) -{ - Q_StripExtension( pMapPath, pPlatformMapPath, maxLength ); - -// if( dxlevel <= 60 ) -// { -// Q_strncat( pPlatformMapPath, "_dx60", maxLength, COPY_ALL_CHARACTERS ); -// } - - Q_strncat( pPlatformMapPath, ".bsp", maxLength, COPY_ALL_CHARACTERS ); -} - -// There's a version of this in checksum_engine.cpp!!! Make sure that they match. -static bool CRC_MapFile(CRC32_t *crcvalue, const char *pszFileName) -{ - byte chunk[1024]; - lump_t *curLump; - - FileHandle_t fp = g_pFileSystem->Open( pszFileName, "rb" ); - if ( !fp ) - return false; - - // CRC across all lumps except for the Entities lump - for ( int l = 0; l < HEADER_LUMPS; ++l ) - { - if (l == LUMP_ENTITIES) - continue; - - curLump = &g_pBSPHeader->lumps[l]; - unsigned int nSize = curLump->filelen; - - g_pFileSystem->Seek( fp, curLump->fileofs, FILESYSTEM_SEEK_HEAD ); - - // Now read in 1K chunks - while ( nSize > 0 ) - { - int nBytesRead = 0; - - if ( nSize > 1024 ) - nBytesRead = g_pFileSystem->Read( chunk, 1024, fp ); - else - nBytesRead = g_pFileSystem->Read( chunk, nSize, fp ); - - // If any data was received, CRC it. - if ( nBytesRead > 0 ) - { - nSize -= nBytesRead; - CRC32_ProcessBuffer( crcvalue, chunk, nBytesRead ); - } - else - { - g_pFileSystem->Close( fp ); - return false; - } - } - } - - g_pFileSystem->Close( fp ); - return true; -} - - -void SetHDRMode( bool bHDR ) -{ - g_bHDR = bHDR; - if ( bHDR ) - { - pdlightdata = &dlightdataHDR; - g_pLeafAmbientLighting = &g_LeafAmbientLightingHDR; - g_pLeafAmbientIndex = &g_LeafAmbientIndexHDR; - pNumworldlights = &numworldlightsHDR; - dworldlights = dworldlightsHDR; -#ifdef VRAD - extern void VRadDetailProps_SetHDRMode( bool bHDR ); - VRadDetailProps_SetHDRMode( bHDR ); -#endif - } - else - { - pdlightdata = &dlightdataLDR; - g_pLeafAmbientLighting = &g_LeafAmbientLightingLDR; - g_pLeafAmbientIndex = &g_LeafAmbientIndexLDR; - pNumworldlights = &numworldlightsLDR; - dworldlights = dworldlightsLDR; -#ifdef VRAD - extern void VRadDetailProps_SetHDRMode( bool bHDR ); - VRadDetailProps_SetHDRMode( bHDR ); -#endif - } -} - -bool SwapVHV( void *pDestBase, void *pSrcBase ) -{ - byte *pDest = (byte*)pDestBase; - byte *pSrc = (byte*)pSrcBase; - - HardwareVerts::FileHeader_t *pHdr = (HardwareVerts::FileHeader_t*)( g_bSwapOnLoad ? pDest : pSrc ); - g_Swap.SwapFieldsToTargetEndian( (HardwareVerts::FileHeader_t*)pDest, (HardwareVerts::FileHeader_t*)pSrc ); - pSrc += sizeof(HardwareVerts::FileHeader_t); - pDest += sizeof(HardwareVerts::FileHeader_t); - - // This swap is pretty format specific - Assert( pHdr->m_nVersion == VHV_VERSION ); - if ( pHdr->m_nVersion != VHV_VERSION ) - return false; - - HardwareVerts::MeshHeader_t *pSrcMesh = (HardwareVerts::MeshHeader_t*)pSrc; - HardwareVerts::MeshHeader_t *pDestMesh = (HardwareVerts::MeshHeader_t*)pDest; - HardwareVerts::MeshHeader_t *pMesh = (HardwareVerts::MeshHeader_t*)( g_bSwapOnLoad ? pDest : pSrc ); - for ( int i = 0; i < pHdr->m_nMeshes; ++i, ++pMesh, ++pSrcMesh, ++pDestMesh ) - { - g_Swap.SwapFieldsToTargetEndian( pDestMesh, pSrcMesh ); - - pSrc = (byte*)pSrcBase + pMesh->m_nOffset; - pDest = (byte*)pDestBase + pMesh->m_nOffset; - - // Swap as a buffer of integers - // (source is bgra for an Intel swap to argb. PowerPC won't swap, so we need argb source. - g_Swap.SwapBufferToTargetEndian( (int*)pDest, (int*)pSrc, pMesh->m_nVertexes ); - } - return true; -} - -const char *ResolveStaticPropToModel( const char *pPropName ) -{ - // resolve back to static prop - int iProp = -1; - - // filename should be sp_???.vhv or sp_hdr_???.vhv - if ( V_strnicmp( pPropName, "sp_", 3 ) ) - { - return NULL; - } - const char *pPropNumber = V_strrchr( pPropName, '_' ); - if ( pPropNumber ) - { - sscanf( pPropNumber+1, "%d.vhv", &iProp ); - } - else - { - return NULL; - } - - // look up the prop to get to the actual model - if ( iProp < 0 || iProp >= g_StaticPropInstances.Count() ) - { - // prop out of range - return NULL; - } - int iModel = g_StaticPropInstances[iProp]; - if ( iModel < 0 || iModel >= g_StaticPropNames.Count() ) - { - // model out of range - return NULL; - } - - return g_StaticPropNames[iModel].String(); -} - -//----------------------------------------------------------------------------- -// Iterate files in pak file, distribute to converters -// pak file will be ready for serialization upon completion -//----------------------------------------------------------------------------- -void ConvertPakFileContents( const char *pInFilename ) -{ - IZip *newPakFile = IZip::CreateZip( NULL ); - - CUtlBuffer sourceBuf; - CUtlBuffer targetBuf; - bool bConverted; - CUtlVector< CUtlString > hdrFiles; - - int id = -1; - int fileSize; - while ( 1 ) - { - char relativeName[MAX_PATH]; - id = GetNextFilename( GetPakFile(), id, relativeName, sizeof( relativeName ), fileSize ); - if ( id == -1) - break; - - bConverted = false; - sourceBuf.Purge(); - targetBuf.Purge(); - - const char* pExtension = V_GetFileExtension( relativeName ); - const char* pExt = 0; - - bool bOK = ReadFileFromPak( GetPakFile(), relativeName, false, sourceBuf ); - if ( !bOK ) - { - Warning( "Failed to load '%s' from lump pak for conversion or copy in '%s'.\n", relativeName, pInFilename ); - continue; - } - - if ( pExtension && !V_stricmp( pExtension, "vtf" ) ) - { - bOK = g_pVTFConvertFunc( relativeName, sourceBuf, targetBuf, g_pCompressFunc ); - if ( !bOK ) - { - Warning( "Failed to convert '%s' in '%s'.\n", relativeName, pInFilename ); - continue; - } - - bConverted = true; - pExt = ".vtf"; - } - else if ( pExtension && !V_stricmp( pExtension, "vhv" ) ) - { - CUtlBuffer tempBuffer; - if ( g_pVHVFixupFunc ) - { - // caller supplied a fixup - const char *pModelName = ResolveStaticPropToModel( relativeName ); - if ( !pModelName ) - { - Warning( "Static Prop '%s' failed to resolve actual model in '%s'.\n", relativeName, pInFilename ); - continue; - } - - // output temp buffer may shrink, must use TellPut() to determine size - bOK = g_pVHVFixupFunc( relativeName, pModelName, sourceBuf, tempBuffer ); - if ( !bOK ) - { - Warning( "Failed to convert '%s' in '%s'.\n", relativeName, pInFilename ); - continue; - } - } - else - { - // use the source buffer as-is - tempBuffer.EnsureCapacity( sourceBuf.TellMaxPut() ); - tempBuffer.Put( sourceBuf.Base(), sourceBuf.TellMaxPut() ); - } - - // swap the VHV - targetBuf.EnsureCapacity( tempBuffer.TellPut() ); - bOK = SwapVHV( targetBuf.Base(), tempBuffer.Base() ); - if ( !bOK ) - { - Warning( "Failed to swap '%s' in '%s'.\n", relativeName, pInFilename ); - continue; - } - targetBuf.SeekPut( CUtlBuffer::SEEK_HEAD, tempBuffer.TellPut() ); - - if ( g_pCompressFunc ) - { - CUtlBuffer compressedBuffer; - targetBuf.SeekGet( CUtlBuffer::SEEK_HEAD, sizeof( HardwareVerts::FileHeader_t ) ); - bool bCompressed = g_pCompressFunc( targetBuf, compressedBuffer ); - if ( bCompressed ) - { - // copy all the header data off - CUtlBuffer headerBuffer; - headerBuffer.EnsureCapacity( sizeof( HardwareVerts::FileHeader_t ) ); - headerBuffer.Put( targetBuf.Base(), sizeof( HardwareVerts::FileHeader_t ) ); - - // reform the target with the header and then the compressed data - targetBuf.Clear(); - targetBuf.Put( headerBuffer.Base(), sizeof( HardwareVerts::FileHeader_t ) ); - targetBuf.Put( compressedBuffer.Base(), compressedBuffer.TellPut() ); - } - - targetBuf.SeekGet( CUtlBuffer::SEEK_HEAD, 0 ); - } - - bConverted = true; - pExt = ".vhv"; - } - - if ( !bConverted ) - { - // straight copy - AddBufferToPak( newPakFile, relativeName, sourceBuf.Base(), sourceBuf.TellMaxPut(), false ); - } - else - { - // converted filename - V_StripExtension( relativeName, relativeName, sizeof( relativeName ) ); - V_strcat( relativeName, ".360", sizeof( relativeName ) ); - V_strcat( relativeName, pExt, sizeof( relativeName ) ); - AddBufferToPak( newPakFile, relativeName, targetBuf.Base(), targetBuf.TellMaxPut(), false ); - } - - if ( V_stristr( relativeName, ".hdr" ) || V_stristr( relativeName, "_hdr" ) ) - { - hdrFiles.AddToTail( relativeName ); - } - - DevMsg( "Created '%s' in lump pak in '%s'.\n", relativeName, pInFilename ); - } - - // strip ldr version of hdr files - for ( int i=0; iRemoveFileFromZip( ldrFileName ); - } - } - - // discard old pak in favor of new pak - IZip::ReleaseZip( s_pakFile ); - s_pakFile = newPakFile; -} - -void SetAlignedLumpPosition( int lumpnum, int alignment = LUMP_ALIGNMENT ) -{ - g_pBSPHeader->lumps[lumpnum].fileofs = AlignFilePosition( g_hBSPFile, alignment ); -} - -template< class T > -int SwapLumpToDisk( int fieldType, int lumpnum ) -{ - if ( g_pBSPHeader->lumps[lumpnum].filelen == 0 ) - return 0; - - DevMsg( "Swapping %s\n", GetLumpName( lumpnum ) ); - - // lump swap may expand, allocate enough expansion room - void *pBuffer = malloc( 2*g_pBSPHeader->lumps[lumpnum].filelen ); - - // CopyLumpInternal will handle the swap on load case - unsigned int fieldSize = ( fieldType == FIELD_VECTOR ) ? sizeof(Vector) : sizeof(T); - unsigned int count = CopyLumpInternal( fieldType, lumpnum, (T*)pBuffer, g_pBSPHeader->lumps[lumpnum].version ); - g_pBSPHeader->lumps[lumpnum].filelen = count * fieldSize; - - if ( g_bSwapOnWrite ) - { - // Swap the lump in place before writing - switch( lumpnum ) - { - case LUMP_VISIBILITY: - SwapVisibilityLump( (byte*)pBuffer, (byte*)pBuffer, count ); - break; - - case LUMP_PHYSCOLLIDE: - // SwapPhyscollideLump may change size - SwapPhyscollideLump( (byte*)pBuffer, (byte*)pBuffer, count ); - g_pBSPHeader->lumps[lumpnum].filelen = count; - break; - - case LUMP_PHYSDISP: - SwapPhysdispLump( (byte*)pBuffer, (byte*)pBuffer, count ); - break; - - default: - g_Swap.SwapBufferToTargetEndian( (T*)pBuffer, (T*)pBuffer, g_pBSPHeader->lumps[lumpnum].filelen / sizeof(T) ); - break; - } - } - - SetAlignedLumpPosition( lumpnum ); - SafeWrite( g_hBSPFile, pBuffer, g_pBSPHeader->lumps[lumpnum].filelen ); - - free( pBuffer ); - - return g_pBSPHeader->lumps[lumpnum].filelen; -} - -template< class T > -int SwapLumpToDisk( int lumpnum ) -{ - if ( g_pBSPHeader->lumps[lumpnum].filelen == 0 || g_Lumps.bLumpParsed[lumpnum] ) - return 0; - - DevMsg( "Swapping %s\n", GetLumpName( lumpnum ) ); - - // lump swap may expand, allocate enough room - void *pBuffer = malloc( 2*g_pBSPHeader->lumps[lumpnum].filelen ); - - // CopyLumpInternal will handle the swap on load case - int count = CopyLumpInternal( lumpnum, (T*)pBuffer, g_pBSPHeader->lumps[lumpnum].version ); - g_pBSPHeader->lumps[lumpnum].filelen = count * sizeof(T); - - if ( g_bSwapOnWrite ) - { - // Swap the lump in place before writing - g_Swap.SwapFieldsToTargetEndian( (T*)pBuffer, (T*)pBuffer, count ); - } - - SetAlignedLumpPosition( lumpnum ); - SafeWrite( g_hBSPFile, pBuffer, g_pBSPHeader->lumps[lumpnum].filelen ); - free( pBuffer ); - - return g_pBSPHeader->lumps[lumpnum].filelen; -} - -void SwapLeafAmbientLightingLumpToDisk() -{ - if ( HasLump( LUMP_LEAF_AMBIENT_INDEX ) || HasLump( LUMP_LEAF_AMBIENT_INDEX_HDR ) ) - { - // current version, swap in place - if ( HasLump( LUMP_LEAF_AMBIENT_INDEX_HDR ) ) - { - // write HDR - SwapLumpToDisk< dleafambientlighting_t >( LUMP_LEAF_AMBIENT_LIGHTING_HDR ); - SwapLumpToDisk< dleafambientindex_t >( LUMP_LEAF_AMBIENT_INDEX_HDR ); - - // cull LDR - g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_LIGHTING].filelen = 0; - g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_INDEX].filelen = 0; - } - else - { - // no HDR, keep LDR version - SwapLumpToDisk< dleafambientlighting_t >( LUMP_LEAF_AMBIENT_LIGHTING ); - SwapLumpToDisk< dleafambientindex_t >( LUMP_LEAF_AMBIENT_INDEX ); - } - } - else - { - // older ambient lighting version (before index) - // load older ambient lighting into memory and build ambient/index - // an older leaf version would have already built the new LDR leaf ambient/index - int numLeafs = g_pBSPHeader->lumps[LUMP_LEAFS].filelen / sizeof( dleaf_t ); - LoadLeafAmbientLighting( numLeafs ); - - if ( HasLump( LUMP_LEAF_AMBIENT_LIGHTING_HDR ) ) - { - DevMsg( "Swapping %s\n", GetLumpName( LUMP_LEAF_AMBIENT_LIGHTING_HDR ) ); - DevMsg( "Swapping %s\n", GetLumpName( LUMP_LEAF_AMBIENT_INDEX_HDR ) ); - - // write HDR - if ( g_bSwapOnWrite ) - { - g_Swap.SwapFieldsToTargetEndian( g_LeafAmbientLightingHDR.Base(), g_LeafAmbientLightingHDR.Count() ); - g_Swap.SwapFieldsToTargetEndian( g_LeafAmbientIndexHDR.Base(), g_LeafAmbientIndexHDR.Count() ); - } - - SetAlignedLumpPosition( LUMP_LEAF_AMBIENT_LIGHTING_HDR ); - g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_LIGHTING_HDR].version = LUMP_LEAF_AMBIENT_LIGHTING_VERSION; - g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_LIGHTING_HDR].filelen = g_LeafAmbientLightingHDR.Count() * sizeof( dleafambientlighting_t ); - SafeWrite( g_hBSPFile, g_LeafAmbientLightingHDR.Base(), g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_LIGHTING_HDR].filelen ); - - SetAlignedLumpPosition( LUMP_LEAF_AMBIENT_INDEX_HDR ); - g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_INDEX_HDR].filelen = g_LeafAmbientIndexHDR.Count() * sizeof( dleafambientindex_t ); - SafeWrite( g_hBSPFile, g_LeafAmbientIndexHDR.Base(), g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_INDEX_HDR].filelen ); - - // mark as processed - g_Lumps.bLumpParsed[LUMP_LEAF_AMBIENT_LIGHTING_HDR] = true; - g_Lumps.bLumpParsed[LUMP_LEAF_AMBIENT_INDEX_HDR] = true; - - // cull LDR - g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_LIGHTING].filelen = 0; - g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_INDEX].filelen = 0; - } - else - { - // no HDR, keep LDR version - DevMsg( "Swapping %s\n", GetLumpName( LUMP_LEAF_AMBIENT_LIGHTING ) ); - DevMsg( "Swapping %s\n", GetLumpName( LUMP_LEAF_AMBIENT_INDEX ) ); - - if ( g_bSwapOnWrite ) - { - g_Swap.SwapFieldsToTargetEndian( g_LeafAmbientLightingLDR.Base(), g_LeafAmbientLightingLDR.Count() ); - g_Swap.SwapFieldsToTargetEndian( g_LeafAmbientIndexLDR.Base(), g_LeafAmbientIndexLDR.Count() ); - } - - SetAlignedLumpPosition( LUMP_LEAF_AMBIENT_LIGHTING ); - g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_LIGHTING].version = LUMP_LEAF_AMBIENT_LIGHTING_VERSION; - g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_LIGHTING].filelen = g_LeafAmbientLightingLDR.Count() * sizeof( dleafambientlighting_t ); - SafeWrite( g_hBSPFile, g_LeafAmbientLightingLDR.Base(), g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_LIGHTING].filelen ); - - SetAlignedLumpPosition( LUMP_LEAF_AMBIENT_INDEX ); - g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_INDEX].filelen = g_LeafAmbientIndexLDR.Count() * sizeof( dleafambientindex_t ); - SafeWrite( g_hBSPFile, g_LeafAmbientIndexLDR.Base(), g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_INDEX].filelen ); - - // mark as processed - g_Lumps.bLumpParsed[LUMP_LEAF_AMBIENT_LIGHTING] = true; - g_Lumps.bLumpParsed[LUMP_LEAF_AMBIENT_INDEX] = true; - } - - g_LeafAmbientLightingLDR.Purge(); - g_LeafAmbientIndexLDR.Purge(); - g_LeafAmbientLightingHDR.Purge(); - g_LeafAmbientIndexHDR.Purge(); - } -} - -void SwapLeafLumpToDisk( void ) -{ - DevMsg( "Swapping %s\n", GetLumpName( LUMP_LEAFS ) ); - - // load the leafs - int count = LoadLeafs(); - if ( g_bSwapOnWrite ) - { - g_Swap.SwapFieldsToTargetEndian( dleafs, count ); - } - - bool bOldLeafVersion = ( LumpVersion( LUMP_LEAFS ) == 0 ); - if ( bOldLeafVersion ) - { - // version has been converted in the load process - // not updating the version ye, SwapLeafAmbientLightingLumpToDisk() can detect - g_pBSPHeader->lumps[LUMP_LEAFS].filelen = count * sizeof( dleaf_t ); - } - - SetAlignedLumpPosition( LUMP_LEAFS ); - SafeWrite( g_hBSPFile, dleafs, g_pBSPHeader->lumps[LUMP_LEAFS].filelen ); - - SwapLeafAmbientLightingLumpToDisk(); - - if ( bOldLeafVersion ) - { - // version has been converted in the load process - // can now safely change - g_pBSPHeader->lumps[LUMP_LEAFS].version = 1; - } - -#if defined( BSP_USE_LESS_MEMORY ) - if ( dleafs ) - { - free( dleafs ); - dleafs = NULL; - } -#endif -} - -void SwapOcclusionLumpToDisk( void ) -{ - DevMsg( "Swapping %s\n", GetLumpName( LUMP_OCCLUSION ) ); - - LoadOcclusionLump(); - SetAlignedLumpPosition( LUMP_OCCLUSION ); - AddOcclusionLump(); -} - -void SwapPakfileLumpToDisk( const char *pInFilename ) -{ - DevMsg( "Swapping %s\n", GetLumpName( LUMP_PAKFILE ) ); - - byte *pakbuffer = NULL; - int paksize = CopyVariableLump( FIELD_CHARACTER, LUMP_PAKFILE, ( void ** )&pakbuffer ); - if ( paksize > 0 ) - { - GetPakFile()->ActivateByteSwapping( IsX360() ); - GetPakFile()->ParseFromBuffer( pakbuffer, paksize ); - - ConvertPakFileContents( pInFilename ); - } - free( pakbuffer ); - - SetAlignedLumpPosition( LUMP_PAKFILE, XBOX_DVD_SECTORSIZE ); - WritePakFileLump(); - - ReleasePakFileLumps(); -} - -void SwapGameLumpsToDisk( void ) -{ - DevMsg( "Swapping %s\n", GetLumpName( LUMP_GAME_LUMP ) ); - - g_GameLumps.ParseGameLump( g_pBSPHeader ); - SetAlignedLumpPosition( LUMP_GAME_LUMP ); - AddGameLumps(); -} - -//----------------------------------------------------------------------------- -// Generate a table of all static props, used for resolving static prop lighting -// files back to their actual mdl. -//----------------------------------------------------------------------------- -void BuildStaticPropNameTable() -{ - g_StaticPropNames.Purge(); - g_StaticPropInstances.Purge(); - - g_GameLumps.ParseGameLump( g_pBSPHeader ); - - GameLumpHandle_t hGameLump = g_GameLumps.GetGameLumpHandle( GAMELUMP_STATIC_PROPS ); - if ( hGameLump != g_GameLumps.InvalidGameLump() ) - { - int nVersion = g_GameLumps.GetGameLumpVersion( hGameLump ); - if ( nVersion < 4 ) - { - // old unsupported version - return; - } - - if ( nVersion != 4 && nVersion != 5 && nVersion != 6 ) - { - Error( "Unknown Static Prop Lump version %d!\n", nVersion ); - } - - byte *pGameLumpData = (byte *)g_GameLumps.GetGameLump( hGameLump ); - if ( pGameLumpData && g_GameLumps.GameLumpSize( hGameLump ) ) - { - // get the model dictionary - int count = ((int *)pGameLumpData)[0]; - pGameLumpData += sizeof( int ); - StaticPropDictLump_t *pStaticPropDictLump = (StaticPropDictLump_t *)pGameLumpData; - for ( int i = 0; i < count; i++ ) - { - g_StaticPropNames.AddToTail( pStaticPropDictLump[i].m_Name ); - } - pGameLumpData += count * sizeof( StaticPropDictLump_t ); - - // skip the leaf list - count = ((int *)pGameLumpData)[0]; - pGameLumpData += sizeof( int ); - pGameLumpData += count * sizeof( StaticPropLeafLump_t ); - - // get the instances - count = ((int *)pGameLumpData)[0]; - pGameLumpData += sizeof( int ); - for ( int i = 0; i < count; i++ ) - { - int propType; - if ( nVersion == 4 ) - { - propType = ((StaticPropLumpV4_t *)pGameLumpData)->m_PropType; - pGameLumpData += sizeof( StaticPropLumpV4_t ); - } - else if ( nVersion == 5 ) - { - propType = ((StaticPropLumpV5_t *)pGameLumpData)->m_PropType; - pGameLumpData += sizeof( StaticPropLumpV5_t ); - } - else - { - propType = ((StaticPropLump_t *)pGameLumpData)->m_PropType; - pGameLumpData += sizeof( StaticPropLump_t ); - } - g_StaticPropInstances.AddToTail( propType ); - } - } - } - - g_GameLumps.DestroyAllGameLumps(); -} - -int AlignBuffer( CUtlBuffer &buffer, int alignment ) -{ - unsigned int newPosition = AlignValue( buffer.TellPut(), alignment ); - int padLength = newPosition - buffer.TellPut(); - for ( int i = 0; ipLump->fileofs; - int fileOffsetB = pSortedLumpB->pLump->fileofs; - - int fileSizeA = pSortedLumpA->pLump->filelen; - int fileSizeB = pSortedLumpB->pLump->filelen; - - // invalid or empty lumps get sorted together - if ( !fileSizeA ) - { - fileOffsetA = 0; - } - if ( !fileSizeB ) - { - fileOffsetB = 0; - } - - // compare by offset, want ascending - if ( fileOffsetA < fileOffsetB ) - { - return -1; - } - else if ( fileOffsetA > fileOffsetB ) - { - return 1; - } - - return 0; -} - -bool CompressGameLump( dheader_t *pInBSPHeader, dheader_t *pOutBSPHeader, CUtlBuffer &outputBuffer, CompressFunc_t pCompressFunc ) -{ - CByteswap byteSwap; - - dgamelumpheader_t* pInGameLumpHeader = (dgamelumpheader_t*)(((byte *)pInBSPHeader) + pInBSPHeader->lumps[LUMP_GAME_LUMP].fileofs); - dgamelump_t* pInGameLump = (dgamelump_t*)(pInGameLumpHeader + 1); - - byteSwap.ActivateByteSwapping( true ); - byteSwap.SwapFieldsToTargetEndian( pInGameLumpHeader ); - byteSwap.SwapFieldsToTargetEndian( pInGameLump, pInGameLumpHeader->lumpCount ); - - unsigned int newOffset = outputBuffer.TellPut(); - outputBuffer.Put( pInGameLumpHeader, sizeof( dgamelumpheader_t ) ); - outputBuffer.Put( pInGameLump, pInGameLumpHeader->lumpCount * sizeof( dgamelump_t ) ); - - dgamelumpheader_t* pOutGameLumpHeader = (dgamelumpheader_t*)((byte *)outputBuffer.Base() + newOffset); - dgamelump_t* pOutGameLump = (dgamelump_t*)(pOutGameLumpHeader + 1); - - // add a dummy terminal gamelump - // purposely NOT updating the .filelen to reflect the compressed size, but leaving as original size - // callers use the next entry offset to determine compressed size - pOutGameLumpHeader->lumpCount++; - dgamelump_t dummyLump = { 0 }; - outputBuffer.Put( &dummyLump, sizeof( dgamelump_t ) ); - - for ( int i = 0; i < pInGameLumpHeader->lumpCount; i++ ) - { - CUtlBuffer inputBuffer; - CUtlBuffer compressedBuffer; - - pOutGameLump[i].fileofs = AlignBuffer( outputBuffer, 4 ); - - if ( pInGameLump[i].filelen ) - { - inputBuffer.SetExternalBuffer( ((byte *)pInBSPHeader) + pInGameLump[i].fileofs, pInGameLump[i].filelen, pInGameLump[i].filelen ); - - bool bCompressed = pCompressFunc( inputBuffer, compressedBuffer ); - if ( bCompressed ) - { - pOutGameLump[i].flags |= GAMELUMPFLAG_COMPRESSED; - - outputBuffer.Put( compressedBuffer.Base(), compressedBuffer.TellPut() ); - compressedBuffer.Purge(); - } - else - { - // as is - outputBuffer.Put( inputBuffer.Base(), inputBuffer.TellPut() ); - } - } - } - - // fix the dummy terminal lump - int lastLump = pOutGameLumpHeader->lumpCount-1; - pOutGameLump[lastLump].fileofs = outputBuffer.TellPut(); - - // fix the output for 360, swapping it back - byteSwap.SwapFieldsToTargetEndian( pOutGameLump, pOutGameLumpHeader->lumpCount ); - byteSwap.SwapFieldsToTargetEndian( pOutGameLumpHeader ); - - pOutBSPHeader->lumps[LUMP_GAME_LUMP].fileofs = newOffset; - pOutBSPHeader->lumps[LUMP_GAME_LUMP].filelen = outputBuffer.TellPut() - newOffset; - - return true; -} - -bool CompressBSP( CUtlBuffer &inputBuffer, CUtlBuffer &outputBuffer, CompressFunc_t pCompressFunc ) -{ - CByteswap byteSwap; - - dheader_t *pInBSPHeader = (dheader_t *)inputBuffer.Base(); - if ( pInBSPHeader->ident != BigLong( IDBSPHEADER ) || !pCompressFunc ) - { - // only compress 360 bsp's - return false; - } - - // bsp is 360, swap the header back - byteSwap.ActivateByteSwapping( true ); - byteSwap.SwapFieldsToTargetEndian( pInBSPHeader ); - - // output will be smaller, use input size as upper bound - outputBuffer.EnsureCapacity( inputBuffer.TellMaxPut() ); - outputBuffer.Put( pInBSPHeader, sizeof( dheader_t ) ); - - dheader_t *pOutBSPHeader = (dheader_t *)outputBuffer.Base(); - - // must adhere to input lump's offset order and process according to that, NOT lump num - // sort by offset order - CUtlVector< SortedLump_t > sortedLumps; - for ( int i = 0; i < HEADER_LUMPS; i++ ) - { - int iIndex = sortedLumps.AddToTail(); - sortedLumps[iIndex].lumpNum = i; - sortedLumps[iIndex].pLump = &pInBSPHeader->lumps[i]; - } - sortedLumps.Sort( SortLumpsByOffset ); - - // iterate in sorted order - for ( int i = 0; i < HEADER_LUMPS; ++i ) - { - SortedLump_t *pSortedLump = &sortedLumps[i]; - int lumpNum = pSortedLump->lumpNum; - - if ( !pSortedLump->pLump->filelen ) - { - // degenerate - pOutBSPHeader->lumps[lumpNum].fileofs = 0; - } - else - { - int alignment = 4; - if ( lumpNum == LUMP_PAKFILE ) - { - alignment = 2048; - } - unsigned int newOffset = AlignBuffer( outputBuffer, alignment ); - - // only set by compressed lumps, hides the uncompressed size - *((unsigned int *)pOutBSPHeader->lumps[lumpNum].fourCC) = 0; - - CUtlBuffer inputBuffer; - inputBuffer.SetExternalBuffer( ((byte *)pInBSPHeader) + pSortedLump->pLump->fileofs, pSortedLump->pLump->filelen, pSortedLump->pLump->filelen ); - - if ( lumpNum == LUMP_GAME_LUMP ) - { - // the game lump has to have each of its components individually compressed - CompressGameLump( pInBSPHeader, pOutBSPHeader, outputBuffer, pCompressFunc ); - } - else if ( lumpNum == LUMP_PAKFILE ) - { - // add as is - pOutBSPHeader->lumps[lumpNum].fileofs = newOffset; - outputBuffer.Put( inputBuffer.Base(), inputBuffer.TellPut() ); - } - else - { - CUtlBuffer compressedBuffer; - bool bCompressed = pCompressFunc( inputBuffer, compressedBuffer ); - if ( bCompressed ) - { - // placing the uncompressed size in the unused fourCC, will decode at runtime - *((unsigned int *)pOutBSPHeader->lumps[lumpNum].fourCC) = BigLong( inputBuffer.TellPut() ); - pOutBSPHeader->lumps[lumpNum].filelen = compressedBuffer.TellPut(); - pOutBSPHeader->lumps[lumpNum].fileofs = newOffset; - outputBuffer.Put( compressedBuffer.Base(), compressedBuffer.TellPut() ); - compressedBuffer.Purge(); - } - else - { - // add as is - pOutBSPHeader->lumps[lumpNum].fileofs = newOffset; - outputBuffer.Put( inputBuffer.Base(), inputBuffer.TellPut() ); - } - } - } - } - - // fix the output for 360, swapping it back - byteSwap.SetTargetBigEndian( true ); - byteSwap.SwapFieldsToTargetEndian( pOutBSPHeader ); - - return true; -} - -//----------------------------------------------------------------------------- -// For all lumps in a bsp: Loads the lump from file A, swaps it, writes it to file B. -// This limits the memory used for the swap process which helps the Xbox 360. -// -// NOTE: These lumps will be written to the file in exactly the order they appear here, -// so they can be shifted around if desired for file access optimization. -//----------------------------------------------------------------------------- -bool SwapBSPFile( const char *pInFilename, const char *pOutFilename, bool bSwapOnLoad, VTFConvertFunc_t pVTFConvertFunc, VHVFixupFunc_t pVHVFixupFunc, CompressFunc_t pCompressFunc ) -{ - DevMsg( "Creating %s\n", pOutFilename ); - - if ( !g_pFileSystem->FileExists( pInFilename ) ) - { - Warning( "Error! Couldn't open input file %s - BSP swap failed!\n", pInFilename ); - return false; - } - - g_hBSPFile = SafeOpenWrite( pOutFilename ); - if ( !g_hBSPFile ) - { - Warning( "Error! Couldn't open output file %s - BSP swap failed!\n", pOutFilename ); - return false; - } - - if ( !pVTFConvertFunc ) - { - Warning( "Error! Missing VTF Conversion function\n" ); - return false; - } - g_pVTFConvertFunc = pVTFConvertFunc; - - // optional VHV fixup - g_pVHVFixupFunc = pVHVFixupFunc; - - // optional compression callback - g_pCompressFunc = pCompressFunc; - - // These must be mutually exclusive - g_bSwapOnLoad = bSwapOnLoad; - g_bSwapOnWrite = !bSwapOnLoad; - - g_Swap.ActivateByteSwapping( true ); - - OpenBSPFile( pInFilename ); - - // CRC the bsp first - CRC32_t mapCRC; - CRC32_Init(&mapCRC); - if ( !CRC_MapFile( &mapCRC, pInFilename ) ) - { - Warning( "Failed to CRC the bsp\n" ); - return false; - } - - // hold a dictionary of all the static prop names - // this is needed to properly convert any VHV files inside the pak lump - BuildStaticPropNameTable(); - - // Set the output file pointer after the header - dheader_t dummyHeader = { 0 }; - SafeWrite( g_hBSPFile, &dummyHeader, sizeof( dheader_t ) ); - - // To allow for alignment fixups, the lumps will be written to the - // output file in the order they appear in this function. - - // NOTE: Flags for 360 !!!MUST!!! be first - SwapLumpToDisk< dflagslump_t >( LUMP_MAP_FLAGS ); - - // complex lump swaps first or for later contingent data - SwapLeafLumpToDisk(); - SwapOcclusionLumpToDisk(); - SwapGameLumpsToDisk(); - - // Strip dead or non relevant lumps - g_pBSPHeader->lumps[LUMP_DISP_LIGHTMAP_ALPHAS].filelen = 0; - g_pBSPHeader->lumps[LUMP_FACEIDS].filelen = 0; - - // Strip obsolete LDR in favor of HDR - if ( SwapLumpToDisk( LUMP_FACES_HDR ) ) - { - g_pBSPHeader->lumps[LUMP_FACES].filelen = 0; - } - else - { - // no HDR, keep LDR version - SwapLumpToDisk( LUMP_FACES ); - } - - if ( SwapLumpToDisk( LUMP_WORLDLIGHTS_HDR ) ) - { - g_pBSPHeader->lumps[LUMP_WORLDLIGHTS].filelen = 0; - } - else - { - // no HDR, keep LDR version - SwapLumpToDisk( LUMP_WORLDLIGHTS ); - } - - // Simple lump swaps - SwapLumpToDisk( FIELD_CHARACTER, LUMP_PHYSDISP ); - SwapLumpToDisk( FIELD_CHARACTER, LUMP_PHYSCOLLIDE ); - SwapLumpToDisk( FIELD_CHARACTER, LUMP_VISIBILITY ); - SwapLumpToDisk( LUMP_MODELS ); - SwapLumpToDisk( LUMP_VERTEXES ); - SwapLumpToDisk( LUMP_PLANES ); - SwapLumpToDisk( LUMP_NODES ); - SwapLumpToDisk( LUMP_TEXINFO ); - SwapLumpToDisk( LUMP_TEXDATA ); - SwapLumpToDisk( LUMP_DISPINFO ); - SwapLumpToDisk( LUMP_DISP_VERTS ); - SwapLumpToDisk( LUMP_DISP_TRIS ); - SwapLumpToDisk( FIELD_CHARACTER, LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS ); - SwapLumpToDisk( LUMP_FACE_MACRO_TEXTURE_INFO ); - SwapLumpToDisk( LUMP_PRIMITIVES ); - SwapLumpToDisk( LUMP_PRIMVERTS ); - SwapLumpToDisk( FIELD_SHORT, LUMP_PRIMINDICES ); - SwapLumpToDisk( LUMP_ORIGINALFACES ); - SwapLumpToDisk( FIELD_SHORT, LUMP_LEAFFACES ); - SwapLumpToDisk( FIELD_SHORT, LUMP_LEAFBRUSHES ); - SwapLumpToDisk( FIELD_INTEGER, LUMP_SURFEDGES ); - SwapLumpToDisk( LUMP_EDGES ); - SwapLumpToDisk( LUMP_BRUSHES ); - SwapLumpToDisk( LUMP_BRUSHSIDES ); - SwapLumpToDisk( LUMP_AREAS ); - SwapLumpToDisk( LUMP_AREAPORTALS ); - SwapLumpToDisk( FIELD_CHARACTER, LUMP_ENTITIES ); - SwapLumpToDisk( LUMP_LEAFWATERDATA ); - SwapLumpToDisk( FIELD_VECTOR, LUMP_VERTNORMALS ); - SwapLumpToDisk( FIELD_SHORT, LUMP_VERTNORMALINDICES ); - SwapLumpToDisk( FIELD_VECTOR, LUMP_CLIPPORTALVERTS ); - SwapLumpToDisk( LUMP_CUBEMAPS ); - SwapLumpToDisk( FIELD_CHARACTER, LUMP_TEXDATA_STRING_DATA ); - SwapLumpToDisk( FIELD_INTEGER, LUMP_TEXDATA_STRING_TABLE ); - SwapLumpToDisk( LUMP_OVERLAYS ); - SwapLumpToDisk( LUMP_WATEROVERLAYS ); - SwapLumpToDisk( FIELD_SHORT, LUMP_LEAFMINDISTTOWATER ); - SwapLumpToDisk( LUMP_OVERLAY_FADES ); - - - // NOTE: this data placed at the end for the sake of 360: - { - // NOTE: lighting must be the penultimate lump - // (allows 360 to free this memory part-way through map loading) - if ( SwapLumpToDisk( FIELD_CHARACTER, LUMP_LIGHTING_HDR ) ) - { - g_pBSPHeader->lumps[LUMP_LIGHTING].filelen = 0; - } - else - { - // no HDR, keep LDR version - SwapLumpToDisk( FIELD_CHARACTER, LUMP_LIGHTING ); - } - // NOTE: Pakfile for 360 !!!MUST!!! be last - SwapPakfileLumpToDisk( pInFilename ); - } - - - // Store the crc in the flags lump version field - g_pBSPHeader->lumps[LUMP_MAP_FLAGS].version = mapCRC; - - // Pad out the end of the file to a sector boundary for optimal IO - AlignFilePosition( g_hBSPFile, XBOX_DVD_SECTORSIZE ); - - // Warn of any lumps that didn't get swapped - for ( int i = 0; i < HEADER_LUMPS; ++i ) - { - if ( HasLump( i ) && !g_Lumps.bLumpParsed[i] ) - { - // a new lump got added that needs to have a swap function - Warning( "BSP: '%s', %s has no swap or copy function. Discarding!\n", pInFilename, GetLumpName(i) ); - - // the data didn't get copied, so don't reference garbage - g_pBSPHeader->lumps[i].filelen = 0; - } - } - - // Write the updated header - g_pFileSystem->Seek( g_hBSPFile, 0, FILESYSTEM_SEEK_HEAD ); - WriteData( g_pBSPHeader ); - g_pFileSystem->Close( g_hBSPFile ); - g_hBSPFile = 0; - - // Cleanup - g_Swap.ActivateByteSwapping( false ); - - CloseBSPFile(); - - g_StaticPropNames.Purge(); - g_StaticPropInstances.Purge(); - - DevMsg( "Finished BSP Swap\n" ); - - // caller provided compress func will further compress compatible lumps - if ( pCompressFunc ) - { - CUtlBuffer inputBuffer; - if ( !g_pFileSystem->ReadFile( pOutFilename, NULL, inputBuffer ) ) - { - Warning( "Error! Couldn't read file %s - final BSP compression failed!\n", pOutFilename ); - return false; - } - - CUtlBuffer outputBuffer; - if ( !CompressBSP( inputBuffer, outputBuffer, pCompressFunc ) ) - { - Warning( "Error! Failed to compress BSP '%s'!\n", pOutFilename ); - return false; - } - - g_hBSPFile = SafeOpenWrite( pOutFilename ); - if ( !g_hBSPFile ) - { - Warning( "Error! Couldn't open output file %s - BSP swap failed!\n", pOutFilename ); - return false; - } - SafeWrite( g_hBSPFile, outputBuffer.Base(), outputBuffer.TellPut() ); - g_pFileSystem->Close( g_hBSPFile ); - g_hBSPFile = 0; - } - - return true; -} - -//----------------------------------------------------------------------------- -// Get the pak lump from a BSP -//----------------------------------------------------------------------------- -bool GetPakFileLump( const char *pBSPFilename, void **pPakData, int *pPakSize ) -{ - *pPakData = NULL; - *pPakSize = 0; - - if ( !g_pFileSystem->FileExists( pBSPFilename ) ) - { - Warning( "Error! Couldn't open file %s!\n", pBSPFilename ); - return false; - } - - // determine endian nature - dheader_t *pHeader; - LoadFile( pBSPFilename, (void **)&pHeader ); - bool bSwap = ( pHeader->ident == BigLong( IDBSPHEADER ) ); - free( pHeader ); - - g_bSwapOnLoad = bSwap; - g_bSwapOnWrite = !bSwap; - - OpenBSPFile( pBSPFilename ); - - if ( g_pBSPHeader->lumps[LUMP_PAKFILE].filelen ) - { - *pPakSize = CopyVariableLump( FIELD_CHARACTER, LUMP_PAKFILE, pPakData ); - } - - CloseBSPFile(); - - return true; -} - -// compare function for qsort below -static int LumpOffsetCompare( const void *pElem1, const void *pElem2 ) -{ - int lump1 = *(byte *)pElem1; - int lump2 = *(byte *)pElem2; - - if ( lump1 != lump2 ) - { - // force LUMP_MAP_FLAGS to be first, always - if ( lump1 == LUMP_MAP_FLAGS ) - { - return -1; - } - else if ( lump2 == LUMP_MAP_FLAGS ) - { - return 1; - } - - // force LUMP_PAKFILE to be last, always - if ( lump1 == LUMP_PAKFILE ) - { - return 1; - } - else if ( lump2 == LUMP_PAKFILE ) - { - return -1; - } - } - - int fileOffset1 = g_pBSPHeader->lumps[lump1].fileofs; - int fileOffset2 = g_pBSPHeader->lumps[lump2].fileofs; - - // invalid or empty lumps will get sorted together - if ( !g_pBSPHeader->lumps[lump1].filelen ) - { - fileOffset1 = 0; - } - - if ( !g_pBSPHeader->lumps[lump2].filelen ) - { - fileOffset2 = 0; - } - - // compare by offset - if ( fileOffset1 < fileOffset2 ) - { - return -1; - } - else if ( fileOffset1 > fileOffset2 ) - { - return 1; - } - return 0; -} - -//----------------------------------------------------------------------------- -// Replace the pak lump in a BSP -//----------------------------------------------------------------------------- -bool SetPakFileLump( const char *pBSPFilename, const char *pNewFilename, void *pPakData, int pakSize ) -{ - if ( !g_pFileSystem->FileExists( pBSPFilename ) ) - { - Warning( "Error! Couldn't open file %s!\n", pBSPFilename ); - return false; - } - - // determine endian nature - dheader_t *pHeader; - LoadFile( pBSPFilename, (void **)&pHeader ); - bool bSwap = ( pHeader->ident == BigLong( IDBSPHEADER ) ); - free( pHeader ); - - g_bSwapOnLoad = bSwap; - g_bSwapOnWrite = bSwap; - - OpenBSPFile( pBSPFilename ); - - // save a copy of the old header - // generating a new bsp is a destructive operation - dheader_t oldHeader; - oldHeader = *g_pBSPHeader; - - g_hBSPFile = SafeOpenWrite( pNewFilename ); - if ( !g_hBSPFile ) - { - return false; - } - - // placeholder only, reset at conclusion - WriteData( &oldHeader ); - - // lumps must be reserialized in same relative offset order - // build sorted order table - int readOrder[HEADER_LUMPS]; - for ( int i=0; ilumps[lump].filelen; - if ( length ) - { - // save the lump data - int offset = g_pBSPHeader->lumps[lump].fileofs; - SetAlignedLumpPosition( lump ); - SafeWrite( g_hBSPFile, (byte *)g_pBSPHeader + offset, length ); - } - else - { - g_pBSPHeader->lumps[lump].fileofs = 0; - } - } - - // Always write the pak file at the end - // Pad out the end of the file to a sector boundary for optimal IO - g_pBSPHeader->lumps[LUMP_PAKFILE].fileofs = AlignFilePosition( g_hBSPFile, XBOX_DVD_SECTORSIZE ); - g_pBSPHeader->lumps[LUMP_PAKFILE].filelen = pakSize; - SafeWrite( g_hBSPFile, pPakData, pakSize ); - - // Pad out the end of the file to a sector boundary for optimal IO - AlignFilePosition( g_hBSPFile, XBOX_DVD_SECTORSIZE ); - - // Write the updated header - g_pFileSystem->Seek( g_hBSPFile, 0, FILESYSTEM_SEEK_HEAD ); - WriteData( g_pBSPHeader ); - g_pFileSystem->Close( g_hBSPFile ); - - CloseBSPFile(); - - return true; -} - -//----------------------------------------------------------------------------- -// Build a list of files that BSP owns, world/cubemap materials, static props, etc. -//----------------------------------------------------------------------------- -bool GetBSPDependants( const char *pBSPFilename, CUtlVector< CUtlString > *pList ) -{ - if ( !g_pFileSystem->FileExists( pBSPFilename ) ) - { - Warning( "Error! Couldn't open file %s!\n", pBSPFilename ); - return false; - } - - // must be set, but exact hdr not critical for dependant traversal - SetHDRMode( false ); - - LoadBSPFile( pBSPFilename ); - - char szBspName[MAX_PATH]; - V_FileBase( pBSPFilename, szBspName, sizeof( szBspName ) ); - V_SetExtension( szBspName, ".bsp", sizeof( szBspName ) ); - - // get embedded pak files, and internals - char szFilename[MAX_PATH]; - int fileSize; - int fileId = -1; - for ( ;; ) - { - fileId = GetPakFile()->GetNextFilename( fileId, szFilename, sizeof( szFilename ), fileSize ); - if ( fileId == -1 ) - { - break; - } - pList->AddToTail( szFilename ); - } - - // get all the world materials - for ( int i=0; iAddToTail( szFilename ); - } - - // get all the static props - GameLumpHandle_t hGameLump = g_GameLumps.GetGameLumpHandle( GAMELUMP_STATIC_PROPS ); - if ( hGameLump != g_GameLumps.InvalidGameLump() ) - { - byte *pGameLumpData = (byte *)g_GameLumps.GetGameLump( hGameLump ); - if ( pGameLumpData && g_GameLumps.GameLumpSize( hGameLump ) ) - { - int count = ((int *)pGameLumpData)[0]; - pGameLumpData += sizeof( int ); - - StaticPropDictLump_t *pStaticPropDictLump = (StaticPropDictLump_t *)pGameLumpData; - for ( int i=0; iAddToTail( pStaticPropDictLump[i].m_Name ); - } - } - } - - // get all the detail props - hGameLump = g_GameLumps.GetGameLumpHandle( GAMELUMP_DETAIL_PROPS ); - if ( hGameLump != g_GameLumps.InvalidGameLump() ) - { - byte *pGameLumpData = (byte *)g_GameLumps.GetGameLump( hGameLump ); - if ( pGameLumpData && g_GameLumps.GameLumpSize( hGameLump ) ) - { - int count = ((int *)pGameLumpData)[0]; - pGameLumpData += sizeof( int ); - - DetailObjectDictLump_t *pDetailObjectDictLump = (DetailObjectDictLump_t *)pGameLumpData; - for ( int i=0; iAddToTail( pDetailObjectDictLump[i].m_Name ); - } - pGameLumpData += count * sizeof( DetailObjectDictLump_t ); - - if ( g_GameLumps.GetGameLumpVersion( hGameLump ) == 4 ) - { - count = ((int *)pGameLumpData)[0]; - pGameLumpData += sizeof( int ); - if ( count ) - { - // All detail prop sprites must lie in the material detail/detailsprites - pList->AddToTail( "materials/detail/detailsprites.vmt" ); - } - } - } - } - - UnloadBSPFile(); - - return true; -} - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Revision: $ +// $NoKeywords: $ +//=============================================================================// + +#include "cmdlib.h" +#include "mathlib/mathlib.h" +#include "bsplib.h" +#include "zip_utils.h" +#include "scriplib.h" +#include "utllinkedlist.h" +#include "bsptreedata.h" +#include "cmodel.h" +#include "gamebspfile.h" +#include "materialsystem/imaterial.h" +#include "materialsystem/hardwareverts.h" +#include "utlbuffer.h" +#include "utlrbtree.h" +#include "utlsymbol.h" +#include "utlstring.h" +#include "checksum_crc.h" +#include "physdll.h" +#include "tier0/dbg.h" +#include "lumpfiles.h" +#include "vtf/vtf.h" + +//============================================================================= + +// Boundary each lump should be aligned to +#define LUMP_ALIGNMENT 4 + +// Data descriptions for byte swapping - only needed +// for structures that are written to file for use by the game. +BEGIN_BYTESWAP_DATADESC( dheader_t ) + DEFINE_FIELD( ident, FIELD_INTEGER ), + DEFINE_FIELD( version, FIELD_INTEGER ), + DEFINE_EMBEDDED_ARRAY( lumps, HEADER_LUMPS ), + DEFINE_FIELD( mapRevision, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( lump_t ) + DEFINE_FIELD( fileofs, FIELD_INTEGER ), + DEFINE_FIELD( filelen, FIELD_INTEGER ), + DEFINE_FIELD( version, FIELD_INTEGER ), + DEFINE_ARRAY( fourCC, FIELD_CHARACTER, 4 ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( dflagslump_t ) + DEFINE_FIELD( m_LevelFlags, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( dplane_t ) + DEFINE_FIELD( normal, FIELD_VECTOR ), + DEFINE_FIELD( dist, FIELD_FLOAT ), + DEFINE_FIELD( type, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( dleaf_version_0_t ) + DEFINE_FIELD( contents, FIELD_INTEGER ), + DEFINE_FIELD( cluster, FIELD_SHORT ), + DEFINE_BITFIELD( bf, FIELD_SHORT, 16 ), + DEFINE_ARRAY( mins, FIELD_SHORT, 3 ), + DEFINE_ARRAY( maxs, FIELD_SHORT, 3 ), + DEFINE_FIELD( firstleafface, FIELD_SHORT ), + DEFINE_FIELD( numleaffaces, FIELD_SHORT ), + DEFINE_FIELD( firstleafbrush, FIELD_SHORT ), + DEFINE_FIELD( numleafbrushes, FIELD_SHORT ), + DEFINE_FIELD( leafWaterDataID, FIELD_SHORT ), + DEFINE_EMBEDDED( m_AmbientLighting ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( dleaf_t ) + DEFINE_FIELD( contents, FIELD_INTEGER ), + DEFINE_FIELD( cluster, FIELD_SHORT ), + DEFINE_BITFIELD( bf, FIELD_SHORT, 16 ), + DEFINE_ARRAY( mins, FIELD_SHORT, 3 ), + DEFINE_ARRAY( maxs, FIELD_SHORT, 3 ), + DEFINE_FIELD( firstleafface, FIELD_SHORT ), + DEFINE_FIELD( numleaffaces, FIELD_SHORT ), + DEFINE_FIELD( firstleafbrush, FIELD_SHORT ), + DEFINE_FIELD( numleafbrushes, FIELD_SHORT ), + DEFINE_FIELD( leafWaterDataID, FIELD_SHORT ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( CompressedLightCube ) // array of 6 ColorRGBExp32 (3 bytes and 1 char) + DEFINE_ARRAY( m_Color, FIELD_CHARACTER, 6 * sizeof(ColorRGBExp32) ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( dleafambientindex_t ) + DEFINE_FIELD( ambientSampleCount, FIELD_SHORT ), + DEFINE_FIELD( firstAmbientSample, FIELD_SHORT ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( dleafambientlighting_t ) // array of 6 ColorRGBExp32 (3 bytes and 1 char) + DEFINE_EMBEDDED( cube ), + DEFINE_FIELD( x, FIELD_CHARACTER ), + DEFINE_FIELD( y, FIELD_CHARACTER ), + DEFINE_FIELD( z, FIELD_CHARACTER ), + DEFINE_FIELD( pad, FIELD_CHARACTER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( dvertex_t ) + DEFINE_FIELD( point, FIELD_VECTOR ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( dnode_t ) + DEFINE_FIELD( planenum, FIELD_INTEGER ), + DEFINE_ARRAY( children, FIELD_INTEGER, 2 ), + DEFINE_ARRAY( mins, FIELD_SHORT, 3 ), + DEFINE_ARRAY( maxs, FIELD_SHORT, 3 ), + DEFINE_FIELD( firstface, FIELD_SHORT ), + DEFINE_FIELD( numfaces, FIELD_SHORT ), + DEFINE_FIELD( area, FIELD_SHORT ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( texinfo_t ) + DEFINE_ARRAY( textureVecsTexelsPerWorldUnits, FIELD_FLOAT, 2 * 4 ), + DEFINE_ARRAY( lightmapVecsLuxelsPerWorldUnits, FIELD_FLOAT, 2 * 4 ), + DEFINE_FIELD( flags, FIELD_INTEGER ), + DEFINE_FIELD( texdata, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( dtexdata_t ) + DEFINE_FIELD( reflectivity, FIELD_VECTOR ), + DEFINE_FIELD( nameStringTableID, FIELD_INTEGER ), + DEFINE_FIELD( width, FIELD_INTEGER ), + DEFINE_FIELD( height, FIELD_INTEGER ), + DEFINE_FIELD( view_width, FIELD_INTEGER ), + DEFINE_FIELD( view_height, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( ddispinfo_t ) + DEFINE_FIELD( startPosition, FIELD_VECTOR ), + DEFINE_FIELD( m_iDispVertStart, FIELD_INTEGER ), + DEFINE_FIELD( m_iDispTriStart, FIELD_INTEGER ), + DEFINE_FIELD( power, FIELD_INTEGER ), + DEFINE_FIELD( minTess, FIELD_INTEGER ), + DEFINE_FIELD( smoothingAngle, FIELD_FLOAT ), + DEFINE_FIELD( contents, FIELD_INTEGER ), + DEFINE_FIELD( m_iMapFace, FIELD_SHORT ), + DEFINE_FIELD( m_iLightmapAlphaStart, FIELD_INTEGER ), + DEFINE_FIELD( m_iLightmapSamplePositionStart, FIELD_INTEGER ), + DEFINE_EMBEDDED_ARRAY( m_EdgeNeighbors, 4 ), + DEFINE_EMBEDDED_ARRAY( m_CornerNeighbors, 4 ), + DEFINE_ARRAY( m_AllowedVerts, FIELD_INTEGER, ddispinfo_t::ALLOWEDVERTS_SIZE ), // unsigned long +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( CDispNeighbor ) + DEFINE_EMBEDDED_ARRAY( m_SubNeighbors, 2 ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( CDispCornerNeighbors ) + DEFINE_ARRAY( m_Neighbors, FIELD_SHORT, MAX_DISP_CORNER_NEIGHBORS ), + DEFINE_FIELD( m_nNeighbors, FIELD_CHARACTER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( CDispSubNeighbor ) + DEFINE_FIELD( m_iNeighbor, FIELD_SHORT ), + DEFINE_FIELD( m_NeighborOrientation, FIELD_CHARACTER ), + DEFINE_FIELD( m_Span, FIELD_CHARACTER ), + DEFINE_FIELD( m_NeighborSpan, FIELD_CHARACTER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( CDispVert ) + DEFINE_FIELD( m_vVector, FIELD_VECTOR ), + DEFINE_FIELD( m_flDist, FIELD_FLOAT ), + DEFINE_FIELD( m_flAlpha, FIELD_FLOAT ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( CDispTri ) + DEFINE_FIELD( m_uiTags, FIELD_SHORT ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( CFaceMacroTextureInfo ) + DEFINE_FIELD( m_MacroTextureNameID, FIELD_SHORT ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( dprimitive_t ) + DEFINE_FIELD( type, FIELD_CHARACTER ), + DEFINE_FIELD( firstIndex, FIELD_SHORT ), + DEFINE_FIELD( indexCount, FIELD_SHORT ), + DEFINE_FIELD( firstVert, FIELD_SHORT ), + DEFINE_FIELD( vertCount, FIELD_SHORT ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( dprimvert_t ) + DEFINE_FIELD( pos, FIELD_VECTOR ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( dface_t ) + DEFINE_FIELD( planenum, FIELD_SHORT ), + DEFINE_FIELD( side, FIELD_CHARACTER ), + DEFINE_FIELD( onNode, FIELD_CHARACTER ), + DEFINE_FIELD( firstedge, FIELD_INTEGER ), + DEFINE_FIELD( numedges, FIELD_SHORT ), + DEFINE_FIELD( texinfo, FIELD_SHORT ), + DEFINE_FIELD( dispinfo, FIELD_SHORT ), + DEFINE_FIELD( surfaceFogVolumeID, FIELD_SHORT ), + DEFINE_ARRAY( styles, FIELD_CHARACTER, MAXLIGHTMAPS ), + DEFINE_FIELD( lightofs, FIELD_INTEGER ), + DEFINE_FIELD( area, FIELD_FLOAT ), + DEFINE_ARRAY( m_LightmapTextureMinsInLuxels, FIELD_INTEGER, 2 ), + DEFINE_ARRAY( m_LightmapTextureSizeInLuxels, FIELD_INTEGER, 2 ), + DEFINE_FIELD( origFace, FIELD_INTEGER ), + DEFINE_FIELD( m_NumPrims, FIELD_SHORT ), + DEFINE_FIELD( firstPrimID, FIELD_SHORT ), + DEFINE_FIELD( smoothingGroups, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( dfaceid_t ) + DEFINE_FIELD( hammerfaceid, FIELD_SHORT ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( dbrush_t ) + DEFINE_FIELD( firstside, FIELD_INTEGER ), + DEFINE_FIELD( numsides, FIELD_INTEGER ), + DEFINE_FIELD( contents, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( dbrushside_t ) + DEFINE_FIELD( planenum, FIELD_SHORT ), + DEFINE_FIELD( texinfo, FIELD_SHORT ), + DEFINE_FIELD( dispinfo, FIELD_SHORT ), + DEFINE_FIELD( bevel, FIELD_SHORT ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( dedge_t ) + DEFINE_ARRAY( v, FIELD_SHORT, 2 ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( dmodel_t ) + DEFINE_FIELD( mins, FIELD_VECTOR ), + DEFINE_FIELD( maxs, FIELD_VECTOR ), + DEFINE_FIELD( origin, FIELD_VECTOR ), + DEFINE_FIELD( headnode, FIELD_INTEGER ), + DEFINE_FIELD( firstface, FIELD_INTEGER ), + DEFINE_FIELD( numfaces, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( dphysmodel_t ) + DEFINE_FIELD( modelIndex, FIELD_INTEGER ), + DEFINE_FIELD( dataSize, FIELD_INTEGER ), + DEFINE_FIELD( keydataSize, FIELD_INTEGER ), + DEFINE_FIELD( solidCount, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( dphysdisp_t ) + DEFINE_FIELD( numDisplacements, FIELD_SHORT ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( darea_t ) + DEFINE_FIELD( numareaportals, FIELD_INTEGER ), + DEFINE_FIELD( firstareaportal, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( dareaportal_t ) + DEFINE_FIELD( m_PortalKey, FIELD_SHORT ), + DEFINE_FIELD( otherarea, FIELD_SHORT ), + DEFINE_FIELD( m_FirstClipPortalVert, FIELD_SHORT ), + DEFINE_FIELD( m_nClipPortalVerts, FIELD_SHORT ), + DEFINE_FIELD( planenum, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( dworldlight_t ) + DEFINE_FIELD( origin, FIELD_VECTOR ), + DEFINE_FIELD( intensity, FIELD_VECTOR ), + DEFINE_FIELD( normal, FIELD_VECTOR ), + DEFINE_FIELD( cluster, FIELD_INTEGER ), + DEFINE_FIELD( type, FIELD_INTEGER ), // enumeration + DEFINE_FIELD( style, FIELD_INTEGER ), + DEFINE_FIELD( stopdot, FIELD_FLOAT ), + DEFINE_FIELD( stopdot2, FIELD_FLOAT ), + DEFINE_FIELD( exponent, FIELD_FLOAT ), + DEFINE_FIELD( radius, FIELD_FLOAT ), + DEFINE_FIELD( constant_attn, FIELD_FLOAT ), + DEFINE_FIELD( linear_attn, FIELD_FLOAT ), + DEFINE_FIELD( quadratic_attn, FIELD_FLOAT ), + DEFINE_FIELD( flags, FIELD_INTEGER ), + DEFINE_FIELD( texinfo, FIELD_INTEGER ), + DEFINE_FIELD( owner, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( dleafwaterdata_t ) + DEFINE_FIELD( surfaceZ, FIELD_FLOAT ), + DEFINE_FIELD( minZ, FIELD_FLOAT ), + DEFINE_FIELD( surfaceTexInfoID, FIELD_SHORT ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( doccluderdata_t ) + DEFINE_FIELD( flags, FIELD_INTEGER ), + DEFINE_FIELD( firstpoly, FIELD_INTEGER ), + DEFINE_FIELD( polycount, FIELD_INTEGER ), + DEFINE_FIELD( mins, FIELD_VECTOR ), + DEFINE_FIELD( maxs, FIELD_VECTOR ), + DEFINE_FIELD( area, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( doccluderpolydata_t ) + DEFINE_FIELD( firstvertexindex, FIELD_INTEGER ), + DEFINE_FIELD( vertexcount, FIELD_INTEGER ), + DEFINE_FIELD( planenum, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( dcubemapsample_t ) + DEFINE_ARRAY( origin, FIELD_INTEGER, 3 ), + DEFINE_FIELD( size, FIELD_CHARACTER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( doverlay_t ) + DEFINE_FIELD( nId, FIELD_INTEGER ), + DEFINE_FIELD( nTexInfo, FIELD_SHORT ), + DEFINE_FIELD( m_nFaceCountAndRenderOrder, FIELD_SHORT ), + DEFINE_ARRAY( aFaces, FIELD_INTEGER, OVERLAY_BSP_FACE_COUNT ), + DEFINE_ARRAY( flU, FIELD_FLOAT, 2 ), + DEFINE_ARRAY( flV, FIELD_FLOAT, 2 ), + DEFINE_ARRAY( vecUVPoints, FIELD_VECTOR, 4 ), + DEFINE_FIELD( vecOrigin, FIELD_VECTOR ), + DEFINE_FIELD( vecBasisNormal, FIELD_VECTOR ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( dwateroverlay_t ) + DEFINE_FIELD( nId, FIELD_INTEGER ), + DEFINE_FIELD( nTexInfo, FIELD_SHORT ), + DEFINE_FIELD( m_nFaceCountAndRenderOrder, FIELD_SHORT ), + DEFINE_ARRAY( aFaces, FIELD_INTEGER, WATEROVERLAY_BSP_FACE_COUNT ), + DEFINE_ARRAY( flU, FIELD_FLOAT, 2 ), + DEFINE_ARRAY( flV, FIELD_FLOAT, 2 ), + DEFINE_ARRAY( vecUVPoints, FIELD_VECTOR, 4 ), + DEFINE_FIELD( vecOrigin, FIELD_VECTOR ), + DEFINE_FIELD( vecBasisNormal, FIELD_VECTOR ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( doverlayfade_t ) + DEFINE_FIELD( flFadeDistMinSq, FIELD_FLOAT ), + DEFINE_FIELD( flFadeDistMaxSq, FIELD_FLOAT ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( dgamelumpheader_t ) + DEFINE_FIELD( lumpCount, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( dgamelump_t ) + DEFINE_FIELD( id, FIELD_INTEGER ), // GameLumpId_t + DEFINE_FIELD( flags, FIELD_SHORT ), + DEFINE_FIELD( version, FIELD_SHORT ), + DEFINE_FIELD( fileofs, FIELD_INTEGER ), + DEFINE_FIELD( filelen, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +// From gamebspfile.h +BEGIN_BYTESWAP_DATADESC( StaticPropDictLump_t ) + DEFINE_ARRAY( m_Name, FIELD_CHARACTER, STATIC_PROP_NAME_LENGTH ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( StaticPropLump_t ) + DEFINE_FIELD( m_Origin, FIELD_VECTOR ), + DEFINE_FIELD( m_Angles, FIELD_VECTOR ), // QAngle + DEFINE_FIELD( m_PropType, FIELD_SHORT ), + DEFINE_FIELD( m_FirstLeaf, FIELD_SHORT ), + DEFINE_FIELD( m_LeafCount, FIELD_SHORT ), + DEFINE_FIELD( m_Solid, FIELD_CHARACTER ), + DEFINE_FIELD( m_Flags, FIELD_CHARACTER ), + DEFINE_FIELD( m_Skin, FIELD_INTEGER ), + DEFINE_FIELD( m_FadeMinDist, FIELD_FLOAT ), + DEFINE_FIELD( m_FadeMaxDist, FIELD_FLOAT ), + DEFINE_FIELD( m_LightingOrigin, FIELD_VECTOR ), + DEFINE_FIELD( m_flForcedFadeScale, FIELD_FLOAT ), + DEFINE_FIELD( m_nMinDXLevel, FIELD_SHORT ), + DEFINE_FIELD( m_nMaxDXLevel, FIELD_SHORT ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( StaticPropLumpV4_t ) + DEFINE_FIELD( m_Origin, FIELD_VECTOR ), + DEFINE_FIELD( m_Angles, FIELD_VECTOR ), // QAngle + DEFINE_FIELD( m_PropType, FIELD_SHORT ), + DEFINE_FIELD( m_FirstLeaf, FIELD_SHORT ), + DEFINE_FIELD( m_LeafCount, FIELD_SHORT ), + DEFINE_FIELD( m_Solid, FIELD_CHARACTER ), + DEFINE_FIELD( m_Flags, FIELD_CHARACTER ), + DEFINE_FIELD( m_Skin, FIELD_INTEGER ), + DEFINE_FIELD( m_FadeMinDist, FIELD_FLOAT ), + DEFINE_FIELD( m_FadeMaxDist, FIELD_FLOAT ), + DEFINE_FIELD( m_LightingOrigin, FIELD_VECTOR ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( StaticPropLumpV5_t ) + DEFINE_FIELD( m_Origin, FIELD_VECTOR ), + DEFINE_FIELD( m_Angles, FIELD_VECTOR ), // QAngle + DEFINE_FIELD( m_PropType, FIELD_SHORT ), + DEFINE_FIELD( m_FirstLeaf, FIELD_SHORT ), + DEFINE_FIELD( m_LeafCount, FIELD_SHORT ), + DEFINE_FIELD( m_Solid, FIELD_CHARACTER ), + DEFINE_FIELD( m_Flags, FIELD_CHARACTER ), + DEFINE_FIELD( m_Skin, FIELD_INTEGER ), + DEFINE_FIELD( m_FadeMinDist, FIELD_FLOAT ), + DEFINE_FIELD( m_FadeMaxDist, FIELD_FLOAT ), + DEFINE_FIELD( m_LightingOrigin, FIELD_VECTOR ), + DEFINE_FIELD( m_flForcedFadeScale, FIELD_FLOAT ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( StaticPropLeafLump_t ) + DEFINE_FIELD( m_Leaf, FIELD_SHORT ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( DetailObjectDictLump_t ) + DEFINE_ARRAY( m_Name, FIELD_CHARACTER, DETAIL_NAME_LENGTH ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( DetailObjectLump_t ) + DEFINE_FIELD( m_Origin, FIELD_VECTOR ), + DEFINE_FIELD( m_Angles, FIELD_VECTOR ), // QAngle + DEFINE_FIELD( m_DetailModel, FIELD_SHORT ), + DEFINE_FIELD( m_Leaf, FIELD_SHORT ), + DEFINE_ARRAY( m_Lighting, FIELD_CHARACTER, 4 ), // ColorRGBExp32 + DEFINE_FIELD( m_LightStyles, FIELD_INTEGER ), + DEFINE_FIELD( m_LightStyleCount, FIELD_CHARACTER ), + DEFINE_FIELD( m_SwayAmount, FIELD_CHARACTER ), + DEFINE_FIELD( m_ShapeAngle, FIELD_CHARACTER ), + DEFINE_FIELD( m_ShapeSize, FIELD_CHARACTER ), + DEFINE_FIELD( m_Orientation, FIELD_CHARACTER ), + DEFINE_ARRAY( m_Padding2, FIELD_CHARACTER, 3 ), + DEFINE_FIELD( m_Type, FIELD_CHARACTER ), + DEFINE_ARRAY( m_Padding3, FIELD_CHARACTER, 3 ), + DEFINE_FIELD( m_flScale, FIELD_FLOAT ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( DetailSpriteDictLump_t ) + DEFINE_FIELD( m_UL, FIELD_VECTOR2D ), + DEFINE_FIELD( m_LR, FIELD_VECTOR2D ), + DEFINE_FIELD( m_TexUL, FIELD_VECTOR2D ), + DEFINE_FIELD( m_TexLR, FIELD_VECTOR2D ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( DetailPropLightstylesLump_t ) + DEFINE_ARRAY( m_Lighting, FIELD_CHARACTER, 4 ), // ColorRGBExp32 + DEFINE_FIELD( m_Style, FIELD_CHARACTER ), +END_BYTESWAP_DATADESC() + +// From vradstaticprops.h +namespace HardwareVerts +{ +BEGIN_BYTESWAP_DATADESC( MeshHeader_t ) + DEFINE_FIELD( m_nLod, FIELD_INTEGER ), + DEFINE_FIELD( m_nVertexes, FIELD_INTEGER ), + DEFINE_FIELD( m_nOffset, FIELD_INTEGER ), + DEFINE_ARRAY( m_nUnused, FIELD_INTEGER, 4 ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( FileHeader_t ) + DEFINE_FIELD( m_nVersion, FIELD_INTEGER ), + DEFINE_FIELD( m_nChecksum, FIELD_INTEGER ), + DEFINE_FIELD( m_nVertexFlags, FIELD_INTEGER ), + DEFINE_FIELD( m_nVertexSize, FIELD_INTEGER ), + DEFINE_FIELD( m_nVertexes, FIELD_INTEGER ), + DEFINE_FIELD( m_nMeshes, FIELD_INTEGER ), + DEFINE_ARRAY( m_nUnused, FIELD_INTEGER, 4 ), +END_BYTESWAP_DATADESC() +} // end namespace + +static const char *s_LumpNames[] = { + "LUMP_ENTITIES", // 0 + "LUMP_PLANES", // 1 + "LUMP_TEXDATA", // 2 + "LUMP_VERTEXES", // 3 + "LUMP_VISIBILITY", // 4 + "LUMP_NODES", // 5 + "LUMP_TEXINFO", // 6 + "LUMP_FACES", // 7 + "LUMP_LIGHTING", // 8 + "LUMP_OCCLUSION", // 9 + "LUMP_LEAFS", // 10 + "LUMP_FACEIDS", // 11 + "LUMP_EDGES", // 12 + "LUMP_SURFEDGES", // 13 + "LUMP_MODELS", // 14 + "LUMP_WORLDLIGHTS", // 15 + "LUMP_LEAFFACES", // 16 + "LUMP_LEAFBRUSHES", // 17 + "LUMP_BRUSHES", // 18 + "LUMP_BRUSHSIDES", // 19 + "LUMP_AREAS", // 20 + "LUMP_AREAPORTALS", // 21 + "LUMP_UNUSED0", // 22 + "LUMP_UNUSED1", // 23 + "LUMP_UNUSED2", // 24 + "LUMP_UNUSED3", // 25 + "LUMP_DISPINFO", // 26 + "LUMP_ORIGINALFACES", // 27 + "LUMP_PHYSDISP", // 28 + "LUMP_PHYSCOLLIDE", // 29 + "LUMP_VERTNORMALS", // 30 + "LUMP_VERTNORMALINDICES", // 31 + "LUMP_DISP_LIGHTMAP_ALPHAS", // 32 + "LUMP_DISP_VERTS", // 33 + "LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS", // 34 + "LUMP_GAME_LUMP", // 35 + "LUMP_LEAFWATERDATA", // 36 + "LUMP_PRIMITIVES", // 37 + "LUMP_PRIMVERTS", // 38 + "LUMP_PRIMINDICES", // 39 + "LUMP_PAKFILE", // 40 + "LUMP_CLIPPORTALVERTS", // 41 + "LUMP_CUBEMAPS", // 42 + "LUMP_TEXDATA_STRING_DATA", // 43 + "LUMP_TEXDATA_STRING_TABLE", // 44 + "LUMP_OVERLAYS", // 45 + "LUMP_LEAFMINDISTTOWATER", // 46 + "LUMP_FACE_MACRO_TEXTURE_INFO", // 47 + "LUMP_DISP_TRIS", // 48 + "LUMP_PHYSCOLLIDESURFACE", // 49 + "LUMP_WATEROVERLAYS", // 50 + "LUMP_LEAF_AMBIENT_INDEX_HDR", // 51 + "LUMP_LEAF_AMBIENT_INDEX", // 52 + "LUMP_LIGHTING_HDR", // 53 + "LUMP_WORLDLIGHTS_HDR", // 54 + "LUMP_LEAF_AMBIENT_LIGHTING_HDR", // 55 + "LUMP_LEAF_AMBIENT_LIGHTING", // 56 + "LUMP_XZIPPAKFILE", // 57 + "LUMP_FACES_HDR", // 58 + "LUMP_MAP_FLAGS", // 59 + "LUMP_OVERLAY_FADES", // 60 +}; + +const char *GetLumpName( unsigned int lumpnum ) +{ + if ( lumpnum >= ARRAYSIZE( s_LumpNames ) ) + { + return "UNKNOWN"; + } + return s_LumpNames[lumpnum]; +} + +// "-hdr" tells us to use the HDR fields (if present) on the light sources. Also, tells us to write +// out the HDR lumps for lightmaps, ambient leaves, and lights sources. +bool g_bHDR = false; + +// Set to true to generate Xbox360 native output files +static bool g_bSwapOnLoad = false; +static bool g_bSwapOnWrite = false; + +VTFConvertFunc_t g_pVTFConvertFunc; +VHVFixupFunc_t g_pVHVFixupFunc; +CompressFunc_t g_pCompressFunc; + +CUtlVector< CUtlString > g_StaticPropNames; +CUtlVector< int > g_StaticPropInstances; + +CByteswap g_Swap; + +uint32 g_LevelFlags = 0; + +int nummodels; +dmodel_t dmodels[MAX_MAP_MODELS]; + +int visdatasize; +byte dvisdata[MAX_MAP_VISIBILITY]; +dvis_t *dvis = (dvis_t *)dvisdata; + +CUtlVector dlightdataHDR; +CUtlVector dlightdataLDR; +CUtlVector *pdlightdata = &dlightdataLDR; + +CUtlVector dentdata; + +int numleafs; +#if !defined( BSP_USE_LESS_MEMORY ) +dleaf_t dleafs[MAX_MAP_LEAFS]; +#else +dleaf_t *dleafs; +#endif + +CUtlVector g_LeafAmbientIndexLDR; +CUtlVector g_LeafAmbientIndexHDR; +CUtlVector *g_pLeafAmbientIndex = NULL; +CUtlVector g_LeafAmbientLightingLDR; +CUtlVector g_LeafAmbientLightingHDR; +CUtlVector *g_pLeafAmbientLighting = NULL; + +unsigned short g_LeafMinDistToWater[MAX_MAP_LEAFS]; + +int numplanes; +dplane_t dplanes[MAX_MAP_PLANES]; + +int numvertexes; +dvertex_t dvertexes[MAX_MAP_VERTS]; + +int g_numvertnormalindices; // dfaces reference these. These index g_vertnormals. +unsigned short g_vertnormalindices[MAX_MAP_VERTNORMALS]; + +int g_numvertnormals; +Vector g_vertnormals[MAX_MAP_VERTNORMALS]; + +int numnodes; +dnode_t dnodes[MAX_MAP_NODES]; + +CUtlVector texinfo( MAX_MAP_TEXINFO ); + +int numtexdata; +dtexdata_t dtexdata[MAX_MAP_TEXDATA]; + +// +// displacement map bsp file info: dispinfo +// +CUtlVector g_dispinfo; +CUtlVector g_DispVerts; +CUtlVector g_DispTris; +CUtlVector g_DispLightmapSamplePositions; // LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS + +int numorigfaces; +dface_t dorigfaces[MAX_MAP_FACES]; + +int g_numprimitives = 0; +dprimitive_t g_primitives[MAX_MAP_PRIMITIVES]; + +int g_numprimverts = 0; +dprimvert_t g_primverts[MAX_MAP_PRIMVERTS]; + +int g_numprimindices = 0; +unsigned short g_primindices[MAX_MAP_PRIMINDICES]; + +int numfaces; +dface_t dfaces[MAX_MAP_FACES]; + +int numfaceids; +CUtlVector dfaceids; + +int numfaces_hdr; +dface_t dfaces_hdr[MAX_MAP_FACES]; + +int numedges; +dedge_t dedges[MAX_MAP_EDGES]; + +int numleaffaces; +unsigned short dleaffaces[MAX_MAP_LEAFFACES]; + +int numleafbrushes; +unsigned short dleafbrushes[MAX_MAP_LEAFBRUSHES]; + +int numsurfedges; +int dsurfedges[MAX_MAP_SURFEDGES]; + +int numbrushes; +dbrush_t dbrushes[MAX_MAP_BRUSHES]; + +int numbrushsides; +dbrushside_t dbrushsides[MAX_MAP_BRUSHSIDES]; + +int numareas; +darea_t dareas[MAX_MAP_AREAS]; + +int numareaportals; +dareaportal_t dareaportals[MAX_MAP_AREAPORTALS]; + +int numworldlightsLDR; +dworldlight_t dworldlightsLDR[MAX_MAP_WORLDLIGHTS]; + +int numworldlightsHDR; +dworldlight_t dworldlightsHDR[MAX_MAP_WORLDLIGHTS]; + +int *pNumworldlights = &numworldlightsLDR; +dworldlight_t *dworldlights = dworldlightsLDR; + +int numleafwaterdata = 0; +dleafwaterdata_t dleafwaterdata[MAX_MAP_LEAFWATERDATA]; + +CUtlVector g_FaceMacroTextureInfos; + +Vector g_ClipPortalVerts[MAX_MAP_PORTALVERTS]; +int g_nClipPortalVerts; + +dcubemapsample_t g_CubemapSamples[MAX_MAP_CUBEMAPSAMPLES]; +int g_nCubemapSamples = 0; + +int g_nOverlayCount; +doverlay_t g_Overlays[MAX_MAP_OVERLAYS]; +doverlayfade_t g_OverlayFades[MAX_MAP_OVERLAYS]; + +int g_nWaterOverlayCount; +dwateroverlay_t g_WaterOverlays[MAX_MAP_WATEROVERLAYS]; + +CUtlVector g_TexDataStringData; +CUtlVector g_TexDataStringTable; + +byte *g_pPhysCollide = NULL; +int g_PhysCollideSize = 0; +int g_MapRevision = 0; + +byte *g_pPhysDisp = NULL; +int g_PhysDispSize = 0; + +CUtlVector g_OccluderData( 256, 256 ); +CUtlVector g_OccluderPolyData( 1024, 1024 ); +CUtlVector g_OccluderVertexIndices( 2048, 2048 ); + +template static void WriteData( T *pData, int count = 1 ); +template static void WriteData( int fieldType, T *pData, int count = 1 ); +template< class T > static void AddLump( int lumpnum, T *pData, int count, int version = 0 ); +template< class T > static void AddLump( int lumpnum, CUtlVector &data, int version = 0 ); + +dheader_t *g_pBSPHeader; +FileHandle_t g_hBSPFile; + +struct Lump_t +{ + void *pLumps[HEADER_LUMPS]; + int size[HEADER_LUMPS]; + bool bLumpParsed[HEADER_LUMPS]; +} g_Lumps; + +CGameLump g_GameLumps; + +static IZip *s_pakFile = 0; + +//----------------------------------------------------------------------------- +// Keep the file position aligned to an arbitrary boundary. +// Returns updated file position. +//----------------------------------------------------------------------------- +static unsigned int AlignFilePosition( FileHandle_t hFile, int alignment ) +{ + unsigned int currPosition = g_pFileSystem->Tell( hFile ); + + if ( alignment >= 2 ) + { + unsigned int newPosition = AlignValue( currPosition, alignment ); + unsigned int count = newPosition - currPosition; + if ( count ) + { + char *pBuffer; + char smallBuffer[4096]; + if ( count > sizeof( smallBuffer ) ) + { + pBuffer = (char *)malloc( count ); + } + else + { + pBuffer = smallBuffer; + } + + memset( pBuffer, 0, count ); + SafeWrite( hFile, pBuffer, count ); + + if ( pBuffer != smallBuffer ) + { + free( pBuffer ); + } + + currPosition = newPosition; + } + } + + return currPosition; +} + +//----------------------------------------------------------------------------- +// Purpose: // Get a pakfile instance +// Output : IZip* +//----------------------------------------------------------------------------- +IZip* GetPakFile( void ) +{ + if ( !s_pakFile ) + { + s_pakFile = IZip::CreateZip(); + } + return s_pakFile; +} + +//----------------------------------------------------------------------------- +// Purpose: Free the pak files +//----------------------------------------------------------------------------- +void ReleasePakFileLumps( void ) +{ + // Release the pak files + IZip::ReleaseZip( s_pakFile ); + s_pakFile = NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Set the sector alignment for all subsequent zip operations +//----------------------------------------------------------------------------- +void ForceAlignment( IZip *pak, bool bAlign, bool bCompatibleFormat, unsigned int alignmentSize ) +{ + pak->ForceAlignment( bAlign, bCompatibleFormat, alignmentSize ); +} + +//----------------------------------------------------------------------------- +// Purpose: Store data back out to .bsp file +//----------------------------------------------------------------------------- +static void WritePakFileLump( void ) +{ + CUtlBuffer buf( 0, 0 ); + GetPakFile()->ActivateByteSwapping( IsX360() ); + GetPakFile()->SaveToBuffer( buf ); + + // must respect pak file alignment + // pad up and ensure lump starts on same aligned boundary + AlignFilePosition( g_hBSPFile, GetPakFile()->GetAlignment() ); + + // Now store final buffers out to file + AddLump( LUMP_PAKFILE, (byte*)buf.Base(), buf.TellPut() ); +} + +//----------------------------------------------------------------------------- +// Purpose: Remove all entries +//----------------------------------------------------------------------------- +void ClearPakFile( IZip *pak ) +{ + pak->Reset(); +} + +//----------------------------------------------------------------------------- +// Purpose: Add file from disk to .bsp PAK lump +// Input : *relativename - +// *fullpath - +//----------------------------------------------------------------------------- +void AddFileToPak( IZip *pak, const char *relativename, const char *fullpath ) +{ + pak->AddFileToZip( relativename, fullpath ); +} + +//----------------------------------------------------------------------------- +// Purpose: Add buffer to .bsp PAK lump as named file +// Input : *relativename - +// *data - +// length - +//----------------------------------------------------------------------------- +void AddBufferToPak( IZip *pak, const char *pRelativeName, void *data, int length, bool bTextMode ) +{ + pak->AddBufferToZip( pRelativeName, data, length, bTextMode ); +} + +//----------------------------------------------------------------------------- +// Purpose: Check if a file already exists in the pack file. +// Input : *relativename - +//----------------------------------------------------------------------------- +bool FileExistsInPak( IZip *pak, const char *pRelativeName ) +{ + return pak->FileExistsInZip( pRelativeName ); +} + + +//----------------------------------------------------------------------------- +// Read a file from the pack file +//----------------------------------------------------------------------------- +bool ReadFileFromPak( IZip *pak, const char *pRelativeName, bool bTextMode, CUtlBuffer &buf ) +{ + return pak->ReadFileFromZip( pRelativeName, bTextMode, buf ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Remove file from .bsp PAK lump +// Input : *relativename - +//----------------------------------------------------------------------------- +void RemoveFileFromPak( IZip *pak, const char *relativename ) +{ + pak->RemoveFileFromZip( relativename ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Get next filename in directory +// Input : id, -1 to start, returns next id, or -1 at list conclusion +//----------------------------------------------------------------------------- +int GetNextFilename( IZip *pak, int id, char *pBuffer, int bufferSize, int &fileSize ) +{ + return pak->GetNextFilename( id, pBuffer, bufferSize, fileSize ); +} + +//----------------------------------------------------------------------------- +// Convert four-CC code to a handle + back +//----------------------------------------------------------------------------- +GameLumpHandle_t CGameLump::GetGameLumpHandle( GameLumpId_t id ) +{ + // NOTE: I'm also expecting game lump id's to be four-CC codes + Assert( id > HEADER_LUMPS ); + + FOR_EACH_LL(m_GameLumps, i) + { + if (m_GameLumps[i].m_Id == id) + return i; + } + + return InvalidGameLump(); +} + +GameLumpId_t CGameLump::GetGameLumpId( GameLumpHandle_t handle ) +{ + return m_GameLumps[handle].m_Id; +} + +int CGameLump::GetGameLumpFlags( GameLumpHandle_t handle ) +{ + return m_GameLumps[handle].m_Flags; +} + +int CGameLump::GetGameLumpVersion( GameLumpHandle_t handle ) +{ + return m_GameLumps[handle].m_Version; +} + + +//----------------------------------------------------------------------------- +// Game lump accessor methods +//----------------------------------------------------------------------------- + +void* CGameLump::GetGameLump( GameLumpHandle_t id ) +{ + return m_GameLumps[id].m_Memory.Base(); +} + +int CGameLump::GameLumpSize( GameLumpHandle_t id ) +{ + return m_GameLumps[id].m_Memory.NumAllocated(); +} + + +//----------------------------------------------------------------------------- +// Game lump iteration methods +//----------------------------------------------------------------------------- + +GameLumpHandle_t CGameLump::FirstGameLump() +{ + return (m_GameLumps.Count()) ? m_GameLumps.Head() : InvalidGameLump(); +} + +GameLumpHandle_t CGameLump::NextGameLump( GameLumpHandle_t handle ) +{ + + return (m_GameLumps.IsValidIndex(handle)) ? m_GameLumps.Next(handle) : InvalidGameLump(); +} + +GameLumpHandle_t CGameLump::InvalidGameLump() +{ + return 0xFFFF; +} + + +//----------------------------------------------------------------------------- +// Game lump creation/destruction method +//----------------------------------------------------------------------------- + +GameLumpHandle_t CGameLump::CreateGameLump( GameLumpId_t id, int size, int flags, int version ) +{ + Assert( GetGameLumpHandle(id) == InvalidGameLump() ); + GameLumpHandle_t handle = m_GameLumps.AddToTail(); + m_GameLumps[handle].m_Id = id; + m_GameLumps[handle].m_Flags = flags; + m_GameLumps[handle].m_Version = version; + m_GameLumps[handle].m_Memory.EnsureCapacity( size ); + return handle; +} + +void CGameLump::DestroyGameLump( GameLumpHandle_t handle ) +{ + m_GameLumps.Remove( handle ); +} + +void CGameLump::DestroyAllGameLumps() +{ + m_GameLumps.RemoveAll(); +} + +//----------------------------------------------------------------------------- +// Compute file size and clump count +//----------------------------------------------------------------------------- + +void CGameLump::ComputeGameLumpSizeAndCount( int& size, int& clumpCount ) +{ + // Figure out total size of the client lumps + size = 0; + clumpCount = 0; + GameLumpHandle_t h; + for( h = FirstGameLump(); h != InvalidGameLump(); h = NextGameLump( h ) ) + { + ++clumpCount; + size += GameLumpSize( h ); + } + + // Add on headers + size += sizeof( dgamelumpheader_t ) + clumpCount * sizeof( dgamelump_t ); +} + + +void CGameLump::SwapGameLump( GameLumpId_t id, int version, byte *dest, byte *src, int length ) +{ + int count = 0; + switch( id ) + { + case GAMELUMP_STATIC_PROPS: + // Swap the static prop model dict + count = *(int*)src; + g_Swap.SwapBufferToTargetEndian( (int*)dest, (int*)src ); + count = g_bSwapOnLoad ? *(int*)dest : count; + src += sizeof(int); + dest += sizeof(int); + + g_Swap.SwapFieldsToTargetEndian( (StaticPropDictLump_t*)dest, (StaticPropDictLump_t*)src, count ); + src += sizeof(StaticPropDictLump_t) * count; + dest += sizeof(StaticPropDictLump_t) * count; + + // Swap the leaf list + count = *(int*)src; + g_Swap.SwapBufferToTargetEndian( (int*)dest, (int*)src ); + count = g_bSwapOnLoad ? *(int*)dest : count; + src += sizeof(int); + dest += sizeof(int); + + g_Swap.SwapFieldsToTargetEndian( (StaticPropLeafLump_t*)dest, (StaticPropLeafLump_t*)src, count ); + src += sizeof(StaticPropLeafLump_t) * count; + dest += sizeof(StaticPropLeafLump_t) * count; + + // Swap the models + count = *(int*)src; + g_Swap.SwapBufferToTargetEndian( (int*)dest, (int*)src ); + count = g_bSwapOnLoad ? *(int*)dest : count; + src += sizeof(int); + dest += sizeof(int); + + // The one-at-a-time swap is to compensate for these structures + // possibly being misaligned, which crashes the Xbox 360. + if ( version == 4 ) + { + StaticPropLumpV4_t lump; + for ( int i = 0; i < count; ++i ) + { + Q_memcpy( &lump, src, sizeof(StaticPropLumpV4_t) ); + g_Swap.SwapFieldsToTargetEndian( &lump, &lump ); + Q_memcpy( dest, &lump, sizeof(StaticPropLumpV4_t) ); + src += sizeof( StaticPropLumpV4_t ); + dest += sizeof( StaticPropLumpV4_t ); + } + } + else if ( version == 5 ) + { + StaticPropLumpV5_t lump; + for ( int i = 0; i < count; ++i ) + { + Q_memcpy( &lump, src, sizeof(StaticPropLumpV5_t) ); + g_Swap.SwapFieldsToTargetEndian( &lump, &lump ); + Q_memcpy( dest, &lump, sizeof(StaticPropLumpV5_t) ); + src += sizeof( StaticPropLumpV5_t ); + dest += sizeof( StaticPropLumpV5_t ); + } + } + else + { + if ( version != 6 ) + { + Error( "Unknown Static Prop Lump version %d didn't get swapped!\n", version ); + } + + StaticPropLump_t lump; + for ( int i = 0; i < count; ++i ) + { + Q_memcpy( &lump, src, sizeof(StaticPropLump_t) ); + g_Swap.SwapFieldsToTargetEndian( &lump, &lump ); + Q_memcpy( dest, &lump, sizeof(StaticPropLump_t) ); + src += sizeof( StaticPropLump_t ); + dest += sizeof( StaticPropLump_t ); + } + } + break; + + case GAMELUMP_DETAIL_PROPS: + // Swap the detail prop model dict + count = *(int*)src; + g_Swap.SwapBufferToTargetEndian( (int*)dest, (int*)src ); + count = g_bSwapOnLoad ? *(int*)dest : count; + src += sizeof(int); + dest += sizeof(int); + + g_Swap.SwapFieldsToTargetEndian( (DetailObjectDictLump_t*)dest, (DetailObjectDictLump_t*)src, count ); + src += sizeof(DetailObjectDictLump_t) * count; + dest += sizeof(DetailObjectDictLump_t) * count; + + if ( version == 4 ) + { + // Swap the detail sprite dict + count = *(int*)src; + g_Swap.SwapBufferToTargetEndian( (int*)dest, (int*)src ); + count = g_bSwapOnLoad ? *(int*)dest : count; + src += sizeof(int); + dest += sizeof(int); + + DetailSpriteDictLump_t spritelump; + for ( int i = 0; i < count; ++i ) + { + Q_memcpy( &spritelump, src, sizeof(DetailSpriteDictLump_t) ); + g_Swap.SwapFieldsToTargetEndian( &spritelump, &spritelump ); + Q_memcpy( dest, &spritelump, sizeof(DetailSpriteDictLump_t) ); + src += sizeof(DetailSpriteDictLump_t); + dest += sizeof(DetailSpriteDictLump_t); + } + + // Swap the models + count = *(int*)src; + g_Swap.SwapBufferToTargetEndian( (int*)dest, (int*)src ); + count = g_bSwapOnLoad ? *(int*)dest : count; + src += sizeof(int); + dest += sizeof(int); + + DetailObjectLump_t objectlump; + for ( int i = 0; i < count; ++i ) + { + Q_memcpy( &objectlump, src, sizeof(DetailObjectLump_t) ); + g_Swap.SwapFieldsToTargetEndian( &objectlump, &objectlump ); + Q_memcpy( dest, &objectlump, sizeof(DetailObjectLump_t) ); + src += sizeof(DetailObjectLump_t); + dest += sizeof(DetailObjectLump_t); + } + } + break; + + case GAMELUMP_DETAIL_PROP_LIGHTING: + // Swap the LDR light styles + count = *(int*)src; + g_Swap.SwapBufferToTargetEndian( (int*)dest, (int*)src ); + count = g_bSwapOnLoad ? *(int*)dest : count; + src += sizeof(int); + dest += sizeof(int); + + g_Swap.SwapFieldsToTargetEndian( (DetailPropLightstylesLump_t*)dest, (DetailPropLightstylesLump_t*)src, count ); + src += sizeof(DetailObjectDictLump_t) * count; + dest += sizeof(DetailObjectDictLump_t) * count; + break; + + case GAMELUMP_DETAIL_PROP_LIGHTING_HDR: + // Swap the HDR light styles + count = *(int*)src; + g_Swap.SwapBufferToTargetEndian( (int*)dest, (int*)src ); + count = g_bSwapOnLoad ? *(int*)dest : count; + src += sizeof(int); + dest += sizeof(int); + + g_Swap.SwapFieldsToTargetEndian( (DetailPropLightstylesLump_t*)dest, (DetailPropLightstylesLump_t*)src, count ); + src += sizeof(DetailObjectDictLump_t) * count; + dest += sizeof(DetailObjectDictLump_t) * count; + break; + + default: + char idchars[5] = {0}; + Q_memcpy( idchars, &id, 4 ); + Warning( "Unknown game lump '%s' didn't get swapped!\n", idchars ); + memcpy ( dest, src, length); + break; + } +} + +//----------------------------------------------------------------------------- +// Game lump file I/O +//----------------------------------------------------------------------------- +void CGameLump::ParseGameLump( dheader_t* pHeader ) +{ + g_GameLumps.DestroyAllGameLumps(); + + g_Lumps.bLumpParsed[LUMP_GAME_LUMP] = true; + + int length = pHeader->lumps[LUMP_GAME_LUMP].filelen; + int ofs = pHeader->lumps[LUMP_GAME_LUMP].fileofs; + + if (length > 0) + { + // Read dictionary... + dgamelumpheader_t* pGameLumpHeader = (dgamelumpheader_t*)((byte *)pHeader + ofs); + if ( g_bSwapOnLoad ) + { + g_Swap.SwapFieldsToTargetEndian( pGameLumpHeader ); + } + dgamelump_t* pGameLump = (dgamelump_t*)(pGameLumpHeader + 1); + for (int i = 0; i < pGameLumpHeader->lumpCount; ++i ) + { + if ( g_bSwapOnLoad ) + { + g_Swap.SwapFieldsToTargetEndian( &pGameLump[i] ); + } + + int length = pGameLump[i].filelen; + GameLumpHandle_t lump = g_GameLumps.CreateGameLump( pGameLump[i].id, length, pGameLump[i].flags, pGameLump[i].version ); + if ( g_bSwapOnLoad ) + { + SwapGameLump( pGameLump[i].id, pGameLump[i].version, (byte*)g_GameLumps.GetGameLump(lump), (byte *)pHeader + pGameLump[i].fileofs, length ); + } + else + { + memcpy( g_GameLumps.GetGameLump(lump), (byte *)pHeader + pGameLump[i].fileofs, length ); + } + } + } +} + + +//----------------------------------------------------------------------------- +// String table methods +//----------------------------------------------------------------------------- +const char *TexDataStringTable_GetString( int stringID ) +{ + return &g_TexDataStringData[g_TexDataStringTable[stringID]]; +} + +int TexDataStringTable_AddOrFindString( const char *pString ) +{ + int i; + // garymcthack: Make this use an RBTree! + for( i = 0; i < g_TexDataStringTable.Count(); i++ ) + { + if( stricmp( pString, &g_TexDataStringData[g_TexDataStringTable[i]] ) == 0 ) + { + return i; + } + } + + int len = strlen( pString ); + int outOffset = g_TexDataStringData.AddMultipleToTail( len+1, pString ); + int outIndex = g_TexDataStringTable.AddToTail( outOffset ); + return outIndex; +} + +//----------------------------------------------------------------------------- +// Adds all game lumps into one big block +//----------------------------------------------------------------------------- + +static void AddGameLumps( ) +{ + // Figure out total size of the client lumps + int size, clumpCount; + g_GameLumps.ComputeGameLumpSizeAndCount( size, clumpCount ); + + // Set up the main lump dictionary entry + g_Lumps.size[LUMP_GAME_LUMP] = 0; // mark it written + + lump_t* lump = &g_pBSPHeader->lumps[LUMP_GAME_LUMP]; + + lump->fileofs = g_pFileSystem->Tell( g_hBSPFile ); + lump->filelen = size; + + // write header + dgamelumpheader_t header; + header.lumpCount = clumpCount; + WriteData( &header ); + + // write dictionary + dgamelump_t dict; + int offset = lump->fileofs + sizeof(header) + clumpCount * sizeof(dgamelump_t); + GameLumpHandle_t h; + for( h = g_GameLumps.FirstGameLump(); h != g_GameLumps.InvalidGameLump(); h = g_GameLumps.NextGameLump( h ) ) + { + dict.id = g_GameLumps.GetGameLumpId(h); + dict.version = g_GameLumps.GetGameLumpVersion(h); + dict.flags = g_GameLumps.GetGameLumpFlags(h); + dict.fileofs = offset; + dict.filelen = g_GameLumps.GameLumpSize( h ); + offset += dict.filelen; + + WriteData( &dict ); + } + + // write lumps.. + for( h = g_GameLumps.FirstGameLump(); h != g_GameLumps.InvalidGameLump(); h = g_GameLumps.NextGameLump( h ) ) + { + unsigned int lumpsize = g_GameLumps.GameLumpSize(h); + if ( g_bSwapOnWrite ) + { + g_GameLumps.SwapGameLump( g_GameLumps.GetGameLumpId(h), g_GameLumps.GetGameLumpVersion(h), (byte*)g_GameLumps.GetGameLump(h), (byte*)g_GameLumps.GetGameLump(h), lumpsize ); + } + SafeWrite( g_hBSPFile, g_GameLumps.GetGameLump(h), lumpsize ); + } + + // align to doubleword + AlignFilePosition( g_hBSPFile, 4 ); +} + + +//----------------------------------------------------------------------------- +// Adds the occluder lump... +//----------------------------------------------------------------------------- +static void AddOcclusionLump( ) +{ + g_Lumps.size[LUMP_OCCLUSION] = 0; // mark it written + + int nOccluderCount = g_OccluderData.Count(); + int nOccluderPolyDataCount = g_OccluderPolyData.Count(); + int nOccluderVertexIndices = g_OccluderVertexIndices.Count(); + + int nLumpLength = nOccluderCount * sizeof(doccluderdata_t) + + nOccluderPolyDataCount * sizeof(doccluderpolydata_t) + + nOccluderVertexIndices * sizeof(int) + + 3 * sizeof(int); + + lump_t *lump = &g_pBSPHeader->lumps[LUMP_OCCLUSION]; + + lump->fileofs = g_pFileSystem->Tell( g_hBSPFile ); + lump->filelen = nLumpLength; + lump->version = LUMP_OCCLUSION_VERSION; + lump->fourCC[0] = ( char )0; + lump->fourCC[1] = ( char )0; + lump->fourCC[2] = ( char )0; + lump->fourCC[3] = ( char )0; + + // Data is swapped in place, so the 'Count' variables aren't safe to use after they're written + WriteData( FIELD_INTEGER, &nOccluderCount ); + WriteData( (doccluderdata_t*)g_OccluderData.Base(), g_OccluderData.Count() ); + WriteData( FIELD_INTEGER, &nOccluderPolyDataCount ); + WriteData( (doccluderpolydata_t*)g_OccluderPolyData.Base(), g_OccluderPolyData.Count() ); + WriteData( FIELD_INTEGER, &nOccluderVertexIndices ); + WriteData( FIELD_INTEGER, (int*)g_OccluderVertexIndices.Base(), g_OccluderVertexIndices.Count() ); +} + + +//----------------------------------------------------------------------------- +// Loads the occluder lump... +//----------------------------------------------------------------------------- +static void UnserializeOcclusionLumpV2( CUtlBuffer &buf ) +{ + int nCount = buf.GetInt(); + if ( nCount ) + { + g_OccluderData.SetCount( nCount ); + buf.GetObjects( g_OccluderData.Base(), nCount ); + } + + nCount = buf.GetInt(); + if ( nCount ) + { + g_OccluderPolyData.SetCount( nCount ); + buf.GetObjects( g_OccluderPolyData.Base(), nCount ); + } + + nCount = buf.GetInt(); + if ( nCount ) + { + if ( g_bSwapOnLoad ) + { + g_Swap.SwapBufferToTargetEndian( (int*)buf.PeekGet(), (int*)buf.PeekGet(), nCount ); + } + g_OccluderVertexIndices.SetCount( nCount ); + buf.Get( g_OccluderVertexIndices.Base(), nCount * sizeof(g_OccluderVertexIndices[0]) ); + } +} + + +static void LoadOcclusionLump() +{ + g_OccluderData.RemoveAll(); + g_OccluderPolyData.RemoveAll(); + g_OccluderVertexIndices.RemoveAll(); + + int length, ofs; + + g_Lumps.bLumpParsed[LUMP_OCCLUSION] = true; + + length = g_pBSPHeader->lumps[LUMP_OCCLUSION].filelen; + ofs = g_pBSPHeader->lumps[LUMP_OCCLUSION].fileofs; + + CUtlBuffer buf( (byte *)g_pBSPHeader + ofs, length, CUtlBuffer::READ_ONLY ); + buf.ActivateByteSwapping( g_bSwapOnLoad ); + switch ( g_pBSPHeader->lumps[LUMP_OCCLUSION].version ) + { + case 2: + UnserializeOcclusionLumpV2( buf ); + break; + + case 0: + break; + + default: + Error("Unknown occlusion lump version!\n"); + break; + } +} + + +/* +=============== +CompressVis + +=============== +*/ +int CompressVis (byte *vis, byte *dest) +{ + int j; + int rep; + int visrow; + byte *dest_p; + + dest_p = dest; +// visrow = (r_numvisleafs + 7)>>3; + visrow = (dvis->numclusters + 7)>>3; + + for (j=0 ; j>3; + row = (dvis->numclusters+7)>>3; + out = decompressed; + + do + { + if (*in) + { + *out++ = *in++; + continue; + } + + c = in[1]; + if (!c) + Error ("DecompressVis: 0 repeat"); + in += 2; + if ((out - decompressed) + c > row) + { + c = row - (out - decompressed); + Warning( "warning: Vis decompression overrun\n" ); + } + while (c) + { + *out++ = 0; + c--; + } + } while (out - decompressed < row); +} + +//----------------------------------------------------------------------------- +// Lump-specific swap functions +//----------------------------------------------------------------------------- +struct swapcollideheader_t +{ + DECLARE_BYTESWAP_DATADESC(); + int size; + int vphysicsID; + short version; + short modelType; +}; + +struct swapcompactsurfaceheader_t : swapcollideheader_t +{ + DECLARE_BYTESWAP_DATADESC(); + int surfaceSize; + Vector dragAxisAreas; + int axisMapSize; +}; + +struct swapmoppsurfaceheader_t : swapcollideheader_t +{ + DECLARE_BYTESWAP_DATADESC(); + int moppSize; +}; + +BEGIN_BYTESWAP_DATADESC( swapcollideheader_t ) + DEFINE_FIELD( size, FIELD_INTEGER ), + DEFINE_FIELD( vphysicsID, FIELD_INTEGER ), + DEFINE_FIELD( version, FIELD_SHORT ), + DEFINE_FIELD( modelType, FIELD_SHORT ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC_( swapcompactsurfaceheader_t, swapcollideheader_t ) + DEFINE_FIELD( surfaceSize, FIELD_INTEGER ), + DEFINE_FIELD( dragAxisAreas, FIELD_VECTOR ), + DEFINE_FIELD( axisMapSize, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC_( swapmoppsurfaceheader_t, swapcollideheader_t ) + DEFINE_FIELD( moppSize, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + + +static void SwapPhyscollideLump( byte *pDestBase, byte *pSrcBase, unsigned int &count ) +{ + IPhysicsCollision *physcollision = NULL; + CSysModule *pPhysicsModule = g_pFullFileSystem->LoadModule( "vphysics.dll" ); + if ( pPhysicsModule ) + { + CreateInterfaceFn physicsFactory = Sys_GetFactory( pPhysicsModule ); + if ( physicsFactory ) + { + physcollision = (IPhysicsCollision *)physicsFactory( VPHYSICS_COLLISION_INTERFACE_VERSION, NULL ); + } + } + + if ( !physcollision ) + { + Warning("!!! WARNING: Can't swap the physcollide lump!\n" ); + return; + } + + // physics data is variable length. The last physmodel is a NULL pointer + // with modelIndex -1, dataSize -1 + dphysmodel_t *pPhysModel; + byte *pSrc = pSrcBase; + + // first the src chunks have to be aligned properly + // swap increases size, allocate enough expansion room + byte *pSrcAlignedBase = (byte*)malloc( 2*count ); + byte *basePtr = pSrcAlignedBase; + byte *pSrcAligned = pSrcAlignedBase; + + do + { + if ( g_bSwapOnLoad ) + { + g_Swap.SwapFieldsToTargetEndian( (dphysmodel_t*)pSrcAligned, (dphysmodel_t*)pSrc ); + } + else + { + Q_memcpy( pSrcAligned, pSrc, sizeof(dphysmodel_t) ); + } + pPhysModel = (dphysmodel_t*)pSrcAligned; + + pSrc += sizeof(dphysmodel_t); + pSrcAligned += sizeof(dphysmodel_t); + + if ( pPhysModel->dataSize > 0 ) + { + // Align the collide headers + for ( int i = 0; i < pPhysModel->solidCount; ++i ) + { + // Get data size + int size; + Q_memcpy( &size, pSrc, sizeof(int) ); + if ( g_bSwapOnLoad ) + size = SwapLong( size ); + + // Fixup size + int padBytes = 0; + if ( size % 4 != 0 ) + { + padBytes = ( 4 - size % 4 ); + count += padBytes; + pPhysModel->dataSize += padBytes; + } + + // Copy data and size into alligned buffer + int newsize = size + padBytes; + if ( g_bSwapOnLoad ) + newsize = SwapLong( newsize ); + + Q_memcpy( pSrcAligned, &newsize, sizeof(int) ); + Q_memcpy( pSrcAligned + sizeof(int), pSrc + sizeof(int), size ); + pSrcAligned += size + padBytes + sizeof(int); + pSrc += size + sizeof(int); + } + + int padBytes = 0; + int dataSize = pPhysModel->dataSize + pPhysModel->keydataSize; + Q_memcpy( pSrcAligned, pSrc, pPhysModel->keydataSize ); + pSrc += pPhysModel->keydataSize; + pSrcAligned += pPhysModel->keydataSize; + if ( dataSize % 4 != 0 ) + { + // Next chunk will be unaligned + padBytes = ( 4 - dataSize % 4 ); + pPhysModel->keydataSize += padBytes; + count += padBytes; + Q_memset( pSrcAligned, 0, padBytes ); + pSrcAligned += padBytes; + } + } + } while ( pPhysModel->dataSize > 0 ); + + // Now the data can be swapped properly + pSrcBase = pSrcAlignedBase; + pSrc = pSrcBase; + byte *pDest = pDestBase; + + do + { + // src headers are in native format + pPhysModel = (dphysmodel_t*)pSrc; + if ( g_bSwapOnWrite ) + { + g_Swap.SwapFieldsToTargetEndian( (dphysmodel_t*)pDest, (dphysmodel_t*)pSrc ); + } + else + { + Q_memcpy( pDest, pSrc, sizeof(dphysmodel_t) ); + } + + pSrc += sizeof(dphysmodel_t); + pDest += sizeof(dphysmodel_t); + + pSrcBase = pSrc; + pDestBase = pDest; + + if ( pPhysModel->dataSize > 0 ) + { + vcollide_t collide = {0}; + int dataSize = pPhysModel->dataSize + pPhysModel->keydataSize; + + if ( g_bSwapOnWrite ) + { + // Load the collide data + physcollision->VCollideLoad( &collide, pPhysModel->solidCount, (const char *)pSrc, dataSize, false ); + } + + int *offsets = new int[ pPhysModel->solidCount ]; + + // Swap the collision data headers + for ( int i = 0; i < pPhysModel->solidCount; ++i ) + { + int headerSize = 0; + swapcollideheader_t *baseHdr = (swapcollideheader_t*)pSrc; + short modelType = baseHdr->modelType; + if ( g_bSwapOnLoad ) + { + g_Swap.SwapBufferToTargetEndian( &modelType ); + } + + if ( modelType == 0 ) // COLLIDE_POLY + { + headerSize = sizeof(swapcompactsurfaceheader_t); + swapcompactsurfaceheader_t swapHdr; + Q_memcpy( &swapHdr, pSrc, headerSize ); + g_Swap.SwapFieldsToTargetEndian( &swapHdr, &swapHdr ); + Q_memcpy( pDest, &swapHdr, headerSize ); + } + else if ( modelType == 1 ) // COLLIDE_MOPP + { + // The PC still unserializes these, but we don't support them + if ( g_bSwapOnWrite ) + { + collide.solids[i] = NULL; + } + + headerSize = sizeof(swapmoppsurfaceheader_t); + swapmoppsurfaceheader_t swapHdr; + Q_memcpy( &swapHdr, pSrc, headerSize ); + g_Swap.SwapFieldsToTargetEndian( &swapHdr, &swapHdr ); + Q_memcpy( pDest, &swapHdr, headerSize ); + + } + else + { + // Shouldn't happen + Assert( 0 ); + } + + if ( g_bSwapOnLoad ) + { + // src needs the native header data to load the vcollides + Q_memcpy( pSrc, pDest, headerSize ); + } + // HACK: Need either surfaceSize or moppSize - both sit at the same offset in the structure + swapmoppsurfaceheader_t *hdr = (swapmoppsurfaceheader_t*)pSrc; + pSrc += hdr->size + sizeof(int); + pDest += hdr->size + sizeof(int); + offsets[i] = hdr->size; + } + + pSrc = pSrcBase; + pDest = pDestBase; + if ( g_bSwapOnLoad ) + { + physcollision->VCollideLoad( &collide, pPhysModel->solidCount, (const char *)pSrc, dataSize, true ); + } + + // Write out the ledge tree data + for ( int i = 0; i < pPhysModel->solidCount; ++i ) + { + if ( collide.solids[i] ) + { + // skip over the size member + pSrc += sizeof(int); + pDest += sizeof(int); + int offset = physcollision->CollideWrite( (char*)pDest, collide.solids[i], g_bSwapOnWrite ); + pSrc += offset; + pDest += offset; + } + else + { + pSrc += offsets[i] + sizeof(int); + pDest += offsets[i] + sizeof(int); + } + } + + // copy the keyvalues data + Q_memcpy( pDest, pSrc, pPhysModel->keydataSize ); + pDest += pPhysModel->keydataSize; + pSrc += pPhysModel->keydataSize; + + // Free the memory + physcollision->VCollideUnload( &collide ); + delete [] offsets; + } + + // avoid infinite loop on badly formed file + if ( (pSrc - basePtr) > count ) + break; + + } while ( pPhysModel->dataSize > 0 ); + + free( pSrcAlignedBase ); +} + + +// UNDONE: This code is not yet tested. +static void SwapPhysdispLump( byte *pDest, byte *pSrc, int count ) +{ + // the format of this lump is one unsigned short dispCount, then dispCount unsigned shorts of sizes + // followed by an array of variable length (each element is the length of the corresponding entry in the + // previous table) byte-stream data structure of the displacement collision models + // these byte-stream structs are endian-neutral because each element is byte-sized + unsigned short dispCount = *(unsigned short*)pSrc; + if ( g_bSwapOnLoad ) + { + g_Swap.SwapBufferToTargetEndian( &dispCount ); + } + g_Swap.SwapBufferToTargetEndian( (unsigned short*)pDest, (unsigned short*)pSrc, dispCount + 1 ); + + const int nBytes = (dispCount + 1) * sizeof( unsigned short ); + pSrc += nBytes; + pDest += nBytes; + count -= nBytes; + + g_Swap.SwapBufferToTargetEndian( pDest, pSrc, count ); +} + + +static void SwapVisibilityLump( byte *pDest, byte *pSrc, int count ) +{ + int firstInt = *(int*)pSrc; + if ( g_bSwapOnLoad ) + { + g_Swap.SwapBufferToTargetEndian( &firstInt ); + } + int intCt = firstInt * 2 + 1; + const int hdrSize = intCt * sizeof(int); + g_Swap.SwapBufferToTargetEndian( (int*)pDest, (int*)pSrc, intCt ); + g_Swap.SwapBufferToTargetEndian( pDest + hdrSize, pSrc + hdrSize, count - hdrSize ); +} + +//============================================================================= +void Lumps_Init( void ) +{ + memset( &g_Lumps, 0, sizeof(g_Lumps) ); +} + +int LumpVersion( int lump ) +{ + return g_pBSPHeader->lumps[lump].version; +} + +bool HasLump( int lump ) +{ + return g_pBSPHeader->lumps[lump].filelen > 0; +} + +void ValidateLump( int lump, int length, int size, int forceVersion ) +{ + if ( length % size ) + { + Error( "ValidateLump: odd size for lump %d", lump ); + } + + if ( forceVersion >= 0 && forceVersion != g_pBSPHeader->lumps[lump].version ) + { + Error( "ValidateLump: old version for lump %d in map!", lump ); + } +} + +//----------------------------------------------------------------------------- +// Add Lumps of integral types without datadescs +//----------------------------------------------------------------------------- +template< class T > +int CopyLumpInternal( int fieldType, int lump, T *dest, int forceVersion ) +{ + g_Lumps.bLumpParsed[lump] = true; + + // Vectors are passed in as floats + int fieldSize = ( fieldType == FIELD_VECTOR ) ? sizeof(Vector) : sizeof(T); + unsigned int length = g_pBSPHeader->lumps[lump].filelen; + unsigned int ofs = g_pBSPHeader->lumps[lump].fileofs; + + // count must be of the integral type + unsigned int count = length / sizeof(T); + + ValidateLump( lump, length, fieldSize, forceVersion ); + + if ( g_bSwapOnLoad ) + { + switch( lump ) + { + case LUMP_VISIBILITY: + SwapVisibilityLump( (byte*)dest, ((byte*)g_pBSPHeader + ofs), count ); + break; + + case LUMP_PHYSCOLLIDE: + // SwapPhyscollideLump may change size + SwapPhyscollideLump( (byte*)dest, ((byte*)g_pBSPHeader + ofs), count ); + length = count; + break; + + case LUMP_PHYSDISP: + SwapPhysdispLump( (byte*)dest, ((byte*)g_pBSPHeader + ofs), count ); + break; + + default: + g_Swap.SwapBufferToTargetEndian( dest, (T*)((byte*)g_pBSPHeader + ofs), count ); + break; + } + } + else + { + memcpy( dest, (byte*)g_pBSPHeader + ofs, length ); + } + + // Return actual count of elements + return length / fieldSize; +} + +template< class T > +int CopyLump( int fieldType, int lump, T *dest, int forceVersion = -1 ) +{ + return CopyLumpInternal( fieldType, lump, dest, forceVersion ); +} + +template< class T > +void CopyLump( int fieldType, int lump, CUtlVector &dest, int forceVersion = -1 ) +{ + Assert( fieldType != FIELD_VECTOR ); // TODO: Support this if necessary + dest.SetSize( g_pBSPHeader->lumps[lump].filelen / sizeof(T) ); + CopyLumpInternal( fieldType, lump, dest.Base(), forceVersion ); +} + +template< class T > +void CopyOptionalLump( int fieldType, int lump, CUtlVector &dest, int forceVersion = -1 ) +{ + // not fatal if not present + if ( !HasLump( lump ) ) + return; + + dest.SetSize( g_pBSPHeader->lumps[lump].filelen / sizeof(T) ); + CopyLumpInternal( fieldType, lump, dest.Base(), forceVersion ); +} + +template< class T > +int CopyVariableLump( int fieldType, int lump, void **dest, int forceVersion = -1 ) +{ + int length = g_pBSPHeader->lumps[lump].filelen; + *dest = malloc( length ); + + return CopyLumpInternal( fieldType, lump, (T*)*dest, forceVersion ); +} + +//----------------------------------------------------------------------------- +// Add Lumps of object types with datadescs +//----------------------------------------------------------------------------- +template< class T > +int CopyLumpInternal( int lump, T *dest, int forceVersion ) +{ + g_Lumps.bLumpParsed[lump] = true; + + unsigned int length = g_pBSPHeader->lumps[lump].filelen; + unsigned int ofs = g_pBSPHeader->lumps[lump].fileofs; + unsigned int count = length / sizeof(T); + + ValidateLump( lump, length, sizeof(T), forceVersion ); + + if ( g_bSwapOnLoad ) + { + g_Swap.SwapFieldsToTargetEndian( dest, (T*)((byte*)g_pBSPHeader + ofs), count ); + } + else + { + memcpy( dest, (byte*)g_pBSPHeader + ofs, length ); + } + + return count; +} + +template< class T > +int CopyLump( int lump, T *dest, int forceVersion = -1 ) +{ + return CopyLumpInternal( lump, dest, forceVersion ); +} + +template< class T > +void CopyLump( int lump, CUtlVector &dest, int forceVersion = -1 ) +{ + dest.SetSize( g_pBSPHeader->lumps[lump].filelen / sizeof(T) ); + CopyLumpInternal( lump, dest.Base(), forceVersion ); +} + +template< class T > +void CopyOptionalLump( int lump, CUtlVector &dest, int forceVersion = -1 ) +{ + // not fatal if not present + if ( !HasLump( lump ) ) + return; + + dest.SetSize( g_pBSPHeader->lumps[lump].filelen / sizeof(T) ); + CopyLumpInternal( lump, dest.Base(), forceVersion ); +} + +template< class T > +int CopyVariableLump( int lump, void **dest, int forceVersion = -1 ) +{ + int length = g_pBSPHeader->lumps[lump].filelen; + *dest = malloc( length ); + + return CopyLumpInternal( lump, (T*)*dest, forceVersion ); +} + +//----------------------------------------------------------------------------- +// Add/Write unknown lumps +//----------------------------------------------------------------------------- +void Lumps_Parse( void ) +{ + int i; + + for ( i = 0; i < HEADER_LUMPS; i++ ) + { + if ( !g_Lumps.bLumpParsed[i] && g_pBSPHeader->lumps[i].filelen ) + { + g_Lumps.size[i] = CopyVariableLump( FIELD_CHARACTER, i, &g_Lumps.pLumps[i], -1 ); + Msg( "Reading unknown lump #%d (%d bytes)\n", i, g_Lumps.size[i] ); + } + } +} + +void Lumps_Write( void ) +{ + int i; + + for ( i = 0; i < HEADER_LUMPS; i++ ) + { + if ( g_Lumps.size[i] ) + { + Msg( "Writing unknown lump #%d (%d bytes)\n", i, g_Lumps.size[i] ); + AddLump( i, (byte*)g_Lumps.pLumps[i], g_Lumps.size[i] ); + } + if ( g_Lumps.pLumps[i] ) + { + free( g_Lumps.pLumps[i] ); + g_Lumps.pLumps[i] = NULL; + } + } +} + +int LoadLeafs( void ) +{ +#if defined( BSP_USE_LESS_MEMORY ) + dleafs = (dleaf_t*)malloc( g_pBSPHeader->lumps[LUMP_LEAFS].filelen ); +#endif + + switch ( LumpVersion( LUMP_LEAFS ) ) + { + case 0: + { + g_Lumps.bLumpParsed[LUMP_LEAFS] = true; + int length = g_pBSPHeader->lumps[LUMP_LEAFS].filelen; + int size = sizeof( dleaf_version_0_t ); + if ( length % size ) + { + Error( "odd size for LUMP_LEAFS\n" ); + } + int count = length / size; + + void *pSrcBase = ( ( byte * )g_pBSPHeader + g_pBSPHeader->lumps[LUMP_LEAFS].fileofs ); + dleaf_version_0_t *pSrc = (dleaf_version_0_t *)pSrcBase; + dleaf_t *pDst = dleafs; + + // version 0 predates HDR, build the LDR + g_LeafAmbientLightingLDR.SetCount( count ); + g_LeafAmbientIndexLDR.SetCount( count ); + + dleafambientlighting_t *pDstLeafAmbientLighting = &g_LeafAmbientLightingLDR[0]; + for ( int i = 0; i < count; i++ ) + { + g_LeafAmbientIndexLDR[i].ambientSampleCount = 1; + g_LeafAmbientIndexLDR[i].firstAmbientSample = i; + + if ( g_bSwapOnLoad ) + { + g_Swap.SwapFieldsToTargetEndian( pSrc ); + } + // pDst is a subset of pSrc; + *pDst = *( ( dleaf_t * )( void * )pSrc ); + pDstLeafAmbientLighting->cube = pSrc->m_AmbientLighting; + pDstLeafAmbientLighting->x = pDstLeafAmbientLighting->y = pDstLeafAmbientLighting->z = pDstLeafAmbientLighting->pad = 0; + pDst++; + pSrc++; + pDstLeafAmbientLighting++; + } + return count; + } + + case 1: + return CopyLump( LUMP_LEAFS, dleafs ); + + default: + Assert( 0 ); + Error( "Unknown LUMP_LEAFS version\n" ); + return 0; + } +} + +void LoadLeafAmbientLighting( int numLeafs ) +{ + if ( LumpVersion( LUMP_LEAFS ) == 0 ) + { + // an older leaf version already built the LDR ambient lighting on load + return; + } + + // old BSP with ambient, or new BSP with no lighting, convert ambient light to new format or create dummy ambient + if ( !HasLump( LUMP_LEAF_AMBIENT_INDEX ) ) + { + // a bunch of legacy maps, have these lumps with garbage versions + // expect them to be NOT the current version + if ( HasLump(LUMP_LEAF_AMBIENT_LIGHTING) ) + { + Assert( LumpVersion( LUMP_LEAF_AMBIENT_LIGHTING ) != LUMP_LEAF_AMBIENT_LIGHTING_VERSION ); + } + if ( HasLump(LUMP_LEAF_AMBIENT_LIGHTING_HDR) ) + { + Assert( LumpVersion( LUMP_LEAF_AMBIENT_LIGHTING_HDR ) != LUMP_LEAF_AMBIENT_LIGHTING_VERSION ); + } + + void *pSrcBase = ( ( byte * )g_pBSPHeader + g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_LIGHTING].fileofs ); + CompressedLightCube *pSrc = NULL; + if ( HasLump( LUMP_LEAF_AMBIENT_LIGHTING ) ) + { + pSrc = (CompressedLightCube*)pSrcBase; + } + g_LeafAmbientIndexLDR.SetCount( numLeafs ); + g_LeafAmbientLightingLDR.SetCount( numLeafs ); + + void *pSrcBaseHDR = ( ( byte * )g_pBSPHeader + g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_LIGHTING_HDR].fileofs ); + CompressedLightCube *pSrcHDR = NULL; + if ( HasLump( LUMP_LEAF_AMBIENT_LIGHTING_HDR ) ) + { + pSrcHDR = (CompressedLightCube*)pSrcBaseHDR; + } + g_LeafAmbientIndexHDR.SetCount( numLeafs ); + g_LeafAmbientLightingHDR.SetCount( numLeafs ); + + for ( int i = 0; i < numLeafs; i++ ) + { + g_LeafAmbientIndexLDR[i].ambientSampleCount = 1; + g_LeafAmbientIndexLDR[i].firstAmbientSample = i; + g_LeafAmbientIndexHDR[i].ambientSampleCount = 1; + g_LeafAmbientIndexHDR[i].firstAmbientSample = i; + + Q_memset( &g_LeafAmbientLightingLDR[i], 0, sizeof(g_LeafAmbientLightingLDR[i]) ); + Q_memset( &g_LeafAmbientLightingHDR[i], 0, sizeof(g_LeafAmbientLightingHDR[i]) ); + + if ( pSrc ) + { + if ( g_bSwapOnLoad ) + { + g_Swap.SwapFieldsToTargetEndian( &pSrc[i] ); + } + g_LeafAmbientLightingLDR[i].cube = pSrc[i]; + } + if ( pSrcHDR ) + { + if ( g_bSwapOnLoad ) + { + g_Swap.SwapFieldsToTargetEndian( &pSrcHDR[i] ); + } + g_LeafAmbientLightingHDR[i].cube = pSrcHDR[i]; + } + } + + g_Lumps.bLumpParsed[LUMP_LEAF_AMBIENT_LIGHTING] = true; + g_Lumps.bLumpParsed[LUMP_LEAF_AMBIENT_INDEX] = true; + g_Lumps.bLumpParsed[LUMP_LEAF_AMBIENT_LIGHTING_HDR] = true; + g_Lumps.bLumpParsed[LUMP_LEAF_AMBIENT_INDEX_HDR] = true; + } + else + { + CopyOptionalLump( LUMP_LEAF_AMBIENT_LIGHTING, g_LeafAmbientLightingLDR ); + CopyOptionalLump( LUMP_LEAF_AMBIENT_INDEX, g_LeafAmbientIndexLDR ); + CopyOptionalLump( LUMP_LEAF_AMBIENT_LIGHTING_HDR, g_LeafAmbientLightingHDR ); + CopyOptionalLump( LUMP_LEAF_AMBIENT_INDEX_HDR, g_LeafAmbientIndexHDR ); + } +} + +void ValidateHeader( const char *filename, const dheader_t *pHeader ) +{ + if ( pHeader->ident != IDBSPHEADER ) + { + Error ("%s is not a IBSP file", filename); + } + if ( pHeader->version < MINBSPVERSION || pHeader->version > BSPVERSION ) + { + Error ("%s is version %i, not %i", filename, pHeader->version, BSPVERSION); + } +} + +//----------------------------------------------------------------------------- +// Low level BSP opener for external parsing. Parses headers, but nothing else. +// You must close the BSP, via CloseBSPFile(). +//----------------------------------------------------------------------------- +void OpenBSPFile( const char *filename ) +{ + Lumps_Init(); + + // load the file header + LoadFile( filename, (void **)&g_pBSPHeader ); + + if ( g_bSwapOnLoad ) + { + g_Swap.ActivateByteSwapping( true ); + g_Swap.SwapFieldsToTargetEndian( g_pBSPHeader ); + } + + ValidateHeader( filename, g_pBSPHeader ); + + g_MapRevision = g_pBSPHeader->mapRevision; +} + +//----------------------------------------------------------------------------- +// CloseBSPFile +//----------------------------------------------------------------------------- +void CloseBSPFile( void ) +{ + free( g_pBSPHeader ); + g_pBSPHeader = NULL; +} + +//----------------------------------------------------------------------------- +// LoadBSPFile +//----------------------------------------------------------------------------- +void LoadBSPFile( const char *filename ) +{ + OpenBSPFile( filename ); + + nummodels = CopyLump( LUMP_MODELS, dmodels ); + numvertexes = CopyLump( LUMP_VERTEXES, dvertexes ); + numplanes = CopyLump( LUMP_PLANES, dplanes ); + numleafs = LoadLeafs(); + numnodes = CopyLump( LUMP_NODES, dnodes ); + CopyLump( LUMP_TEXINFO, texinfo ); + numtexdata = CopyLump( LUMP_TEXDATA, dtexdata ); + + CopyLump( LUMP_DISPINFO, g_dispinfo ); + CopyLump( LUMP_DISP_VERTS, g_DispVerts ); + CopyLump( LUMP_DISP_TRIS, g_DispTris ); + CopyLump( FIELD_CHARACTER, LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS, g_DispLightmapSamplePositions ); + CopyLump( LUMP_FACE_MACRO_TEXTURE_INFO, g_FaceMacroTextureInfos ); + + numfaces = CopyLump(LUMP_FACES, dfaces, LUMP_FACES_VERSION); + if ( HasLump( LUMP_FACES_HDR ) ) + numfaces_hdr = CopyLump( LUMP_FACES_HDR, dfaces_hdr, LUMP_FACES_VERSION ); + else + numfaces_hdr = 0; + + CopyOptionalLump( LUMP_FACEIDS, dfaceids ); + + g_numprimitives = CopyLump( LUMP_PRIMITIVES, g_primitives ); + g_numprimverts = CopyLump( LUMP_PRIMVERTS, g_primverts ); + g_numprimindices = CopyLump( FIELD_SHORT, LUMP_PRIMINDICES, g_primindices ); + numorigfaces = CopyLump( LUMP_ORIGINALFACES, dorigfaces ); // original faces + numleaffaces = CopyLump( FIELD_SHORT, LUMP_LEAFFACES, dleaffaces ); + numleafbrushes = CopyLump( FIELD_SHORT, LUMP_LEAFBRUSHES, dleafbrushes ); + numsurfedges = CopyLump( FIELD_INTEGER, LUMP_SURFEDGES, dsurfedges ); + numedges = CopyLump( LUMP_EDGES, dedges ); + numbrushes = CopyLump( LUMP_BRUSHES, dbrushes ); + numbrushsides = CopyLump( LUMP_BRUSHSIDES, dbrushsides ); + numareas = CopyLump( LUMP_AREAS, dareas ); + numareaportals = CopyLump( LUMP_AREAPORTALS, dareaportals ); + + visdatasize = CopyLump ( FIELD_CHARACTER, LUMP_VISIBILITY, dvisdata ); + CopyOptionalLump( FIELD_CHARACTER, LUMP_LIGHTING, dlightdataLDR, LUMP_LIGHTING_VERSION ); + CopyOptionalLump( FIELD_CHARACTER, LUMP_LIGHTING_HDR, dlightdataHDR, LUMP_LIGHTING_VERSION ); + + LoadLeafAmbientLighting( numleafs ); + + CopyLump( FIELD_CHARACTER, LUMP_ENTITIES, dentdata ); + numworldlightsLDR = CopyLump( LUMP_WORLDLIGHTS, dworldlightsLDR ); + numworldlightsHDR = CopyLump( LUMP_WORLDLIGHTS_HDR, dworldlightsHDR ); + + numleafwaterdata = CopyLump( LUMP_LEAFWATERDATA, dleafwaterdata ); + g_PhysCollideSize = CopyVariableLump( FIELD_CHARACTER, LUMP_PHYSCOLLIDE, (void**)&g_pPhysCollide ); + g_PhysDispSize = CopyVariableLump( FIELD_CHARACTER, LUMP_PHYSDISP, (void**)&g_pPhysDisp ); + + g_numvertnormals = CopyLump( FIELD_VECTOR, LUMP_VERTNORMALS, (float*)g_vertnormals ); + g_numvertnormalindices = CopyLump( FIELD_SHORT, LUMP_VERTNORMALINDICES, g_vertnormalindices ); + + g_nClipPortalVerts = CopyLump( FIELD_VECTOR, LUMP_CLIPPORTALVERTS, (float*)g_ClipPortalVerts ); + g_nCubemapSamples = CopyLump( LUMP_CUBEMAPS, g_CubemapSamples ); + + CopyLump( FIELD_CHARACTER, LUMP_TEXDATA_STRING_DATA, g_TexDataStringData ); + CopyLump( FIELD_INTEGER, LUMP_TEXDATA_STRING_TABLE, g_TexDataStringTable ); + + g_nOverlayCount = CopyLump( LUMP_OVERLAYS, g_Overlays ); + g_nWaterOverlayCount = CopyLump( LUMP_WATEROVERLAYS, g_WaterOverlays ); + CopyLump( LUMP_OVERLAY_FADES, g_OverlayFades ); + + dflagslump_t flags_lump; + + if ( HasLump( LUMP_MAP_FLAGS ) ) + CopyLump ( LUMP_MAP_FLAGS, &flags_lump ); + else + memset( &flags_lump, 0, sizeof( flags_lump ) ); // default flags to 0 + + g_LevelFlags = flags_lump.m_LevelFlags; + + LoadOcclusionLump(); + + CopyLump( FIELD_SHORT, LUMP_LEAFMINDISTTOWATER, g_LeafMinDistToWater ); + + /* + int crap; + for( crap = 0; crap < g_nBSPStringTable; crap++ ) + { + Msg( "stringtable %d", ( int )crap ); + Msg( " %d:", ( int )g_BSPStringTable[crap] ); + puts( &g_BSPStringData[g_BSPStringTable[crap]] ); + puts( "\n" ); + } + */ + + // Load PAK file lump into appropriate data structure + byte *pakbuffer = NULL; + int paksize = CopyVariableLump( FIELD_CHARACTER, LUMP_PAKFILE, ( void ** )&pakbuffer ); + if ( paksize > 0 ) + { + GetPakFile()->ActivateByteSwapping( IsX360() ); + GetPakFile()->ParseFromBuffer( pakbuffer, paksize ); + } + else + { + GetPakFile()->Reset(); + } + + free( pakbuffer ); + + g_GameLumps.ParseGameLump( g_pBSPHeader ); + + // NOTE: Do NOT call CopyLump after Lumps_Parse() it parses all un-Copied lumps + // parse any additional lumps + Lumps_Parse(); + + // everything has been copied out + CloseBSPFile(); + + g_Swap.ActivateByteSwapping( false ); +} + +//----------------------------------------------------------------------------- +// Reset any state. +//----------------------------------------------------------------------------- +void UnloadBSPFile() +{ + nummodels = 0; + numvertexes = 0; + numplanes = 0; + + numleafs = 0; +#if defined( BSP_USE_LESS_MEMORY ) + if ( dleafs ) + { + free( dleafs ); + dleafs = NULL; + } +#endif + + numnodes = 0; + texinfo.Purge(); + numtexdata = 0; + + g_dispinfo.Purge(); + g_DispVerts.Purge(); + g_DispTris.Purge(); + + g_DispLightmapSamplePositions.Purge(); + g_FaceMacroTextureInfos.Purge(); + + numfaces = 0; + numfaces_hdr = 0; + + dfaceids.Purge(); + + g_numprimitives = 0; + g_numprimverts = 0; + g_numprimindices = 0; + numorigfaces = 0; + numleaffaces = 0; + numleafbrushes = 0; + numsurfedges = 0; + numedges = 0; + numbrushes = 0; + numbrushsides = 0; + numareas = 0; + numareaportals = 0; + + visdatasize = 0; + dlightdataLDR.Purge(); + dlightdataHDR.Purge(); + + g_LeafAmbientLightingLDR.Purge(); + g_LeafAmbientLightingHDR.Purge(); + g_LeafAmbientIndexHDR.Purge(); + g_LeafAmbientIndexLDR.Purge(); + + dentdata.Purge(); + numworldlightsLDR = 0; + numworldlightsHDR = 0; + + numleafwaterdata = 0; + + if ( g_pPhysCollide ) + { + free( g_pPhysCollide ); + g_pPhysCollide = NULL; + } + g_PhysCollideSize = 0; + + if ( g_pPhysDisp ) + { + free( g_pPhysDisp ); + g_pPhysDisp = NULL; + } + g_PhysDispSize = 0; + + g_numvertnormals = 0; + g_numvertnormalindices = 0; + + g_nClipPortalVerts = 0; + g_nCubemapSamples = 0; + + g_TexDataStringData.Purge(); + g_TexDataStringTable.Purge(); + + g_nOverlayCount = 0; + g_nWaterOverlayCount = 0; + + g_LevelFlags = 0; + + g_OccluderData.Purge(); + g_OccluderPolyData.Purge(); + g_OccluderVertexIndices.Purge(); + + g_GameLumps.DestroyAllGameLumps(); + + for ( int i = 0; i < HEADER_LUMPS; i++ ) + { + if ( g_Lumps.pLumps[i] ) + { + free( g_Lumps.pLumps[i] ); + g_Lumps.pLumps[i] = NULL; + } + } + + ReleasePakFileLumps(); +} + +//----------------------------------------------------------------------------- +// LoadBSPFileFilesystemOnly +//----------------------------------------------------------------------------- +void LoadBSPFile_FileSystemOnly( const char *filename ) +{ + Lumps_Init(); + + // + // load the file header + // + LoadFile( filename, (void **)&g_pBSPHeader ); + + ValidateHeader( filename, g_pBSPHeader ); + + // Load PAK file lump into appropriate data structure + byte *pakbuffer = NULL; + int paksize = CopyVariableLump( FIELD_CHARACTER, LUMP_PAKFILE, ( void ** )&pakbuffer, 1 ); + if ( paksize > 0 ) + { + GetPakFile()->ParseFromBuffer( pakbuffer, paksize ); + } + else + { + GetPakFile()->Reset(); + } + + free( pakbuffer ); + + // everything has been copied out + free( g_pBSPHeader ); + g_pBSPHeader = NULL; +} + +void ExtractZipFileFromBSP( char *pBSPFileName, char *pZipFileName ) +{ + Lumps_Init(); + + // + // load the file header + // + LoadFile( pBSPFileName, (void **)&g_pBSPHeader); + + ValidateHeader( pBSPFileName, g_pBSPHeader ); + + byte *pakbuffer = NULL; + int paksize = CopyVariableLump( FIELD_CHARACTER, LUMP_PAKFILE, ( void ** )&pakbuffer ); + if ( paksize > 0 ) + { + FILE *fp; + fp = fopen( pZipFileName, "wb" ); + if( !fp ) + { + fprintf( stderr, "can't open %s\n", pZipFileName ); + return; + } + + fwrite( pakbuffer, paksize, 1, fp ); + fclose( fp ); + } + else + { + fprintf( stderr, "zip file is zero length!\n" ); + } +} + +/* +============= +LoadBSPFileTexinfo + +Only loads the texinfo lump, so qdata can scan for textures +============= +*/ +void LoadBSPFileTexinfo( const char *filename ) +{ + FILE *f; + int length, ofs; + + g_pBSPHeader = (dheader_t*)malloc( sizeof(dheader_t) ); + + f = fopen( filename, "rb" ); + fread( g_pBSPHeader, sizeof(dheader_t), 1, f); + + ValidateHeader( filename, g_pBSPHeader ); + + length = g_pBSPHeader->lumps[LUMP_TEXINFO].filelen; + ofs = g_pBSPHeader->lumps[LUMP_TEXINFO].fileofs; + + int nCount = length / sizeof(texinfo_t); + + texinfo.Purge(); + texinfo.AddMultipleToTail( nCount ); + + fseek( f, ofs, SEEK_SET ); + fread( texinfo.Base(), length, 1, f ); + fclose( f ); + + // everything has been copied out + free( g_pBSPHeader ); + g_pBSPHeader = NULL; +} + +static void AddLumpInternal( int lumpnum, void *data, int len, int version ) +{ + lump_t *lump; + + g_Lumps.size[lumpnum] = 0; // mark it written + + lump = &g_pBSPHeader->lumps[lumpnum]; + + lump->fileofs = g_pFileSystem->Tell( g_hBSPFile ); + lump->filelen = len; + lump->version = version; + lump->fourCC[0] = ( char )0; + lump->fourCC[1] = ( char )0; + lump->fourCC[2] = ( char )0; + lump->fourCC[3] = ( char )0; + + SafeWrite( g_hBSPFile, data, len ); + + // pad out to the next dword + AlignFilePosition( g_hBSPFile, 4 ); +} + +template< class T > +static void SwapInPlace( T *pData, int count ) +{ + if ( !pData ) + return; + + // use the datadesc to swap the fields in place + g_Swap.SwapFieldsToTargetEndian( (T*)pData, pData, count ); +} + +template< class T > +static void SwapInPlace( int fieldType, T *pData, int count ) +{ + if ( !pData ) + return; + + // swap the data in place + g_Swap.SwapBufferToTargetEndian( (T*)pData, (T*)pData, count ); +} + +//----------------------------------------------------------------------------- +// Add raw data chunk to file (not a lump) +//----------------------------------------------------------------------------- +template< class T > +static void WriteData( int fieldType, T *pData, int count ) +{ + if ( g_bSwapOnWrite ) + { + SwapInPlace( fieldType, pData, count ); + } + SafeWrite( g_hBSPFile, pData, count * sizeof(T) ); +} + +template< class T > +static void WriteData( T *pData, int count ) +{ + if ( g_bSwapOnWrite ) + { + SwapInPlace( pData, count ); + } + SafeWrite( g_hBSPFile, pData, count * sizeof(T) ); +} + +//----------------------------------------------------------------------------- +// Add Lump of object types with datadescs +//----------------------------------------------------------------------------- +template< class T > +static void AddLump( int lumpnum, T *pData, int count, int version ) +{ + AddLumpInternal( lumpnum, pData, count * sizeof(T), version ); +} + +template< class T > +static void AddLump( int lumpnum, CUtlVector &data, int version ) +{ + AddLumpInternal( lumpnum, data.Base(), data.Count() * sizeof(T), version ); +} + +/* +============= +WriteBSPFile + +Swaps the bsp file in place, so it should not be referenced again +============= +*/ +void WriteBSPFile( const char *filename, char *pUnused ) +{ + if ( texinfo.Count() > MAX_MAP_TEXINFO ) + { + Error( "Map has too many texinfos (has %d, can have at most %d)\n", texinfo.Count(), MAX_MAP_TEXINFO ); + return; + } + + dheader_t outHeader; + g_pBSPHeader = &outHeader; + memset( g_pBSPHeader, 0, sizeof( dheader_t ) ); + + g_pBSPHeader->ident = IDBSPHEADER; + g_pBSPHeader->version = BSPVERSION; + g_pBSPHeader->mapRevision = g_MapRevision; + + g_hBSPFile = SafeOpenWrite( filename ); + WriteData( g_pBSPHeader ); // overwritten later + + AddLump( LUMP_PLANES, dplanes, numplanes ); + AddLump( LUMP_LEAFS, dleafs, numleafs, LUMP_LEAFS_VERSION ); + AddLump( LUMP_LEAF_AMBIENT_LIGHTING, g_LeafAmbientLightingLDR, LUMP_LEAF_AMBIENT_LIGHTING_VERSION ); + AddLump( LUMP_LEAF_AMBIENT_INDEX, g_LeafAmbientIndexLDR ); + AddLump( LUMP_LEAF_AMBIENT_INDEX_HDR, g_LeafAmbientIndexHDR ); + AddLump( LUMP_LEAF_AMBIENT_LIGHTING_HDR, g_LeafAmbientLightingHDR, LUMP_LEAF_AMBIENT_LIGHTING_VERSION ); + + AddLump( LUMP_VERTEXES, dvertexes, numvertexes ); + AddLump( LUMP_NODES, dnodes, numnodes ); + AddLump( LUMP_TEXINFO, texinfo ); + AddLump( LUMP_TEXDATA, dtexdata, numtexdata ); + + AddLump( LUMP_DISPINFO, g_dispinfo ); + AddLump( LUMP_DISP_VERTS, g_DispVerts ); + AddLump( LUMP_DISP_TRIS, g_DispTris ); + AddLump( LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS, g_DispLightmapSamplePositions ); + AddLump( LUMP_FACE_MACRO_TEXTURE_INFO, g_FaceMacroTextureInfos ); + + AddLump( LUMP_PRIMITIVES, g_primitives, g_numprimitives ); + AddLump( LUMP_PRIMVERTS, g_primverts, g_numprimverts ); + AddLump( LUMP_PRIMINDICES, g_primindices, g_numprimindices ); + AddLump( LUMP_FACES, dfaces, numfaces, LUMP_FACES_VERSION ); + if (numfaces_hdr) + AddLump( LUMP_FACES_HDR, dfaces_hdr, numfaces_hdr, LUMP_FACES_VERSION ); + AddLump ( LUMP_FACEIDS, dfaceids, numfaceids ); + + AddLump( LUMP_ORIGINALFACES, dorigfaces, numorigfaces ); // original faces lump + AddLump( LUMP_BRUSHES, dbrushes, numbrushes ); + AddLump( LUMP_BRUSHSIDES, dbrushsides, numbrushsides ); + AddLump( LUMP_LEAFFACES, dleaffaces, numleaffaces ); + AddLump( LUMP_LEAFBRUSHES, dleafbrushes, numleafbrushes ); + AddLump( LUMP_SURFEDGES, dsurfedges, numsurfedges ); + AddLump( LUMP_EDGES, dedges, numedges ); + AddLump( LUMP_MODELS, dmodels, nummodels ); + AddLump( LUMP_AREAS, dareas, numareas ); + AddLump( LUMP_AREAPORTALS, dareaportals, numareaportals ); + + AddLump( LUMP_LIGHTING, dlightdataLDR, LUMP_LIGHTING_VERSION ); + AddLump( LUMP_LIGHTING_HDR, dlightdataHDR, LUMP_LIGHTING_VERSION ); + AddLump( LUMP_VISIBILITY, dvisdata, visdatasize ); + AddLump( LUMP_ENTITIES, dentdata ); + AddLump( LUMP_WORLDLIGHTS, dworldlightsLDR, numworldlightsLDR ); + AddLump( LUMP_WORLDLIGHTS_HDR, dworldlightsHDR, numworldlightsHDR ); + AddLump( LUMP_LEAFWATERDATA, dleafwaterdata, numleafwaterdata ); + + AddOcclusionLump(); + + dflagslump_t flags_lump; + flags_lump.m_LevelFlags = g_LevelFlags; + AddLump( LUMP_MAP_FLAGS, &flags_lump, 1 ); + + // NOTE: This is just for debugging, so it is disabled in release maps +#if 0 + // add the vis portals to the BSP for visualization + AddLump( LUMP_PORTALS, dportals, numportals ); + AddLump( LUMP_CLUSTERS, dclusters, numclusters ); + AddLump( LUMP_PORTALVERTS, dportalverts, numportalverts ); + AddLump( LUMP_CLUSTERPORTALS, dclusterportals, numclusterportals ); +#endif + + AddLump( LUMP_CLIPPORTALVERTS, (float*)g_ClipPortalVerts, g_nClipPortalVerts * 3 ); + AddLump( LUMP_CUBEMAPS, g_CubemapSamples, g_nCubemapSamples ); + AddLump( LUMP_TEXDATA_STRING_DATA, g_TexDataStringData ); + AddLump( LUMP_TEXDATA_STRING_TABLE, g_TexDataStringTable ); + AddLump( LUMP_OVERLAYS, g_Overlays, g_nOverlayCount ); + AddLump( LUMP_WATEROVERLAYS, g_WaterOverlays, g_nWaterOverlayCount ); + AddLump( LUMP_OVERLAY_FADES, g_OverlayFades, g_nOverlayCount ); + + if ( g_pPhysCollide ) + { + AddLump( LUMP_PHYSCOLLIDE, g_pPhysCollide, g_PhysCollideSize ); + } + + if ( g_pPhysDisp ) + { + AddLump ( LUMP_PHYSDISP, g_pPhysDisp, g_PhysDispSize ); + } + + AddLump( LUMP_VERTNORMALS, (float*)g_vertnormals, g_numvertnormals * 3 ); + AddLump( LUMP_VERTNORMALINDICES, g_vertnormalindices, g_numvertnormalindices ); + + AddLump( LUMP_LEAFMINDISTTOWATER, g_LeafMinDistToWater, numleafs ); + + AddGameLumps(); + + // Write pakfile lump to disk + WritePakFileLump(); + + // NOTE: Do NOT call AddLump after Lumps_Write() it writes all un-Added lumps + // write any additional lumps + Lumps_Write(); + + g_pFileSystem->Seek( g_hBSPFile, 0, FILESYSTEM_SEEK_HEAD ); + WriteData( g_pBSPHeader ); + g_pFileSystem->Close( g_hBSPFile ); +} + +// Generate the next clear lump filename for the bsp file +bool GenerateNextLumpFileName( const char *bspfilename, char *lumpfilename, int buffsize ) +{ + for (int i = 0; i < MAX_LUMPFILES; i++) + { + GenerateLumpFileName( bspfilename, lumpfilename, buffsize, i ); + + if ( !g_pFileSystem->FileExists( lumpfilename ) ) + return true; + } + + return false; +} + +void WriteLumpToFile( char *filename, int lump ) +{ + if ( !HasLump(lump) ) + return; + + char lumppre[MAX_PATH]; + if ( !GenerateNextLumpFileName( filename, lumppre, MAX_PATH ) ) + { + Warning( "Failed to find valid lump filename for bsp %s.\n", filename ); + return; + } + + // Open the file + FileHandle_t lumpfile = g_pFileSystem->Open(lumppre, "wb"); + if ( !lumpfile ) + { + Error ("Error opening %s! (Check for write enable)\n",filename); + return; + } + + int ofs = g_pBSPHeader->lumps[lump].fileofs; + int length = g_pBSPHeader->lumps[lump].filelen; + + // Write the header + lumpfileheader_t lumpHeader; + lumpHeader.lumpID = lump; + lumpHeader.lumpVersion = LumpVersion(lump); + lumpHeader.lumpLength = length; + lumpHeader.mapRevision = LittleLong( g_MapRevision ); + lumpHeader.lumpOffset = sizeof(lumpfileheader_t); // Lump starts after the header + SafeWrite (lumpfile, &lumpHeader, sizeof(lumpfileheader_t)); + + // Write the lump + SafeWrite (lumpfile, (byte *)g_pBSPHeader + ofs, length); +} + +void WriteLumpToFile( char *filename, int lump, int nLumpVersion, void *pBuffer, size_t nBufLen ) +{ + char lumppre[MAX_PATH]; + if ( !GenerateNextLumpFileName( filename, lumppre, MAX_PATH ) ) + { + Warning( "Failed to find valid lump filename for bsp %s.\n", filename ); + return; + } + + // Open the file + FileHandle_t lumpfile = g_pFileSystem->Open(lumppre, "wb"); + if ( !lumpfile ) + { + Error ("Error opening %s! (Check for write enable)\n",filename); + return; + } + + // Write the header + lumpfileheader_t lumpHeader; + lumpHeader.lumpID = lump; + lumpHeader.lumpVersion = nLumpVersion; + lumpHeader.lumpLength = nBufLen; + lumpHeader.mapRevision = LittleLong( g_MapRevision ); + lumpHeader.lumpOffset = sizeof(lumpfileheader_t); // Lump starts after the header + SafeWrite( lumpfile, &lumpHeader, sizeof(lumpfileheader_t)); + + // Write the lump + SafeWrite( lumpfile, pBuffer, nBufLen ); + + g_pFileSystem->Close( lumpfile ); +} + + +//============================================================================ +#define ENTRIES(a) (sizeof(a)/sizeof(*(a))) +#define ENTRYSIZE(a) (sizeof(*(a))) + +int ArrayUsage( const char *szItem, int items, int maxitems, int itemsize ) +{ + float percentage = maxitems ? items * 100.0 / maxitems : 0.0; + + Msg("%-17.17s %8i/%-8i %8i/%-8i (%4.1f%%) ", + szItem, items, maxitems, items * itemsize, maxitems * itemsize, percentage ); + if ( percentage > 80.0 ) + Msg( "VERY FULL!\n" ); + else if ( percentage > 95.0 ) + Msg( "SIZE DANGER!\n" ); + else if ( percentage > 99.9 ) + Msg( "SIZE OVERFLOW!!!\n" ); + else + Msg( "\n" ); + return items * itemsize; +} + +int GlobUsage( const char *szItem, int itemstorage, int maxstorage ) +{ + float percentage = maxstorage ? itemstorage * 100.0 / maxstorage : 0.0; + Msg("%-17.17s [variable] %8i/%-8i (%4.1f%%) ", + szItem, itemstorage, maxstorage, percentage ); + if ( percentage > 80.0 ) + Msg( "VERY FULL!\n" ); + else if ( percentage > 95.0 ) + Msg( "SIZE DANGER!\n" ); + else if ( percentage > 99.9 ) + Msg( "SIZE OVERFLOW!!!\n" ); + else + Msg( "\n" ); + return itemstorage; +} + +/* +============= +PrintBSPFileSizes + +Dumps info about current file +============= +*/ +void PrintBSPFileSizes (void) +{ + int totalmemory = 0; + +// if (!num_entities) +// ParseEntities (); + + Msg("\n"); + Msg( "%-17s %16s %16s %9s \n", "Object names", "Objects/Maxobjs", "Memory / Maxmem", "Fullness" ); + Msg( "%-17s %16s %16s %9s \n", "------------", "---------------", "---------------", "--------" ); + + totalmemory += ArrayUsage( "models", nummodels, ENTRIES(dmodels), ENTRYSIZE(dmodels) ); + totalmemory += ArrayUsage( "brushes", numbrushes, ENTRIES(dbrushes), ENTRYSIZE(dbrushes) ); + totalmemory += ArrayUsage( "brushsides", numbrushsides, ENTRIES(dbrushsides), ENTRYSIZE(dbrushsides) ); + totalmemory += ArrayUsage( "planes", numplanes, ENTRIES(dplanes), ENTRYSIZE(dplanes) ); + totalmemory += ArrayUsage( "vertexes", numvertexes, ENTRIES(dvertexes), ENTRYSIZE(dvertexes) ); + totalmemory += ArrayUsage( "nodes", numnodes, ENTRIES(dnodes), ENTRYSIZE(dnodes) ); + totalmemory += ArrayUsage( "texinfos", texinfo.Count(),MAX_MAP_TEXINFO, sizeof(texinfo_t) ); + totalmemory += ArrayUsage( "texdata", numtexdata, ENTRIES(dtexdata), ENTRYSIZE(dtexdata) ); + + totalmemory += ArrayUsage( "dispinfos", g_dispinfo.Count(), 0, sizeof( ddispinfo_t ) ); + totalmemory += ArrayUsage( "disp_verts", g_DispVerts.Count(), 0, sizeof( g_DispVerts[0] ) ); + totalmemory += ArrayUsage( "disp_tris", g_DispTris.Count(), 0, sizeof( g_DispTris[0] ) ); + totalmemory += ArrayUsage( "disp_lmsamples",g_DispLightmapSamplePositions.Count(),0,sizeof( g_DispLightmapSamplePositions[0] ) ); + + totalmemory += ArrayUsage( "faces", numfaces, ENTRIES(dfaces), ENTRYSIZE(dfaces) ); + totalmemory += ArrayUsage( "hdr faces", numfaces_hdr, ENTRIES(dfaces_hdr), ENTRYSIZE(dfaces_hdr) ); + totalmemory += ArrayUsage( "origfaces", numorigfaces, ENTRIES(dorigfaces), ENTRYSIZE(dorigfaces) ); // original faces + totalmemory += ArrayUsage( "leaves", numleafs, ENTRIES(dleafs), ENTRYSIZE(dleafs) ); + totalmemory += ArrayUsage( "leaffaces", numleaffaces, ENTRIES(dleaffaces), ENTRYSIZE(dleaffaces) ); + totalmemory += ArrayUsage( "leafbrushes", numleafbrushes, ENTRIES(dleafbrushes), ENTRYSIZE(dleafbrushes) ); + totalmemory += ArrayUsage( "areas", numareas, ENTRIES(dareas), ENTRYSIZE(dareas) ); + totalmemory += ArrayUsage( "surfedges", numsurfedges, ENTRIES(dsurfedges), ENTRYSIZE(dsurfedges) ); + totalmemory += ArrayUsage( "edges", numedges, ENTRIES(dedges), ENTRYSIZE(dedges) ); + totalmemory += ArrayUsage( "LDR worldlights", numworldlightsLDR, ENTRIES(dworldlightsLDR), ENTRYSIZE(dworldlightsLDR) ); + totalmemory += ArrayUsage( "HDR worldlights", numworldlightsHDR, ENTRIES(dworldlightsHDR), ENTRYSIZE(dworldlightsHDR) ); + + totalmemory += ArrayUsage( "leafwaterdata", numleafwaterdata,ENTRIES(dleafwaterdata), ENTRYSIZE(dleafwaterdata) ); + totalmemory += ArrayUsage( "waterstrips", g_numprimitives,ENTRIES(g_primitives), ENTRYSIZE(g_primitives) ); + totalmemory += ArrayUsage( "waterverts", g_numprimverts, ENTRIES(g_primverts), ENTRYSIZE(g_primverts) ); + totalmemory += ArrayUsage( "waterindices", g_numprimindices,ENTRIES(g_primindices),ENTRYSIZE(g_primindices) ); + totalmemory += ArrayUsage( "cubemapsamples", g_nCubemapSamples,ENTRIES(g_CubemapSamples),ENTRYSIZE(g_CubemapSamples) ); + totalmemory += ArrayUsage( "overlays", g_nOverlayCount, ENTRIES(g_Overlays), ENTRYSIZE(g_Overlays) ); + + totalmemory += GlobUsage( "LDR lightdata", dlightdataLDR.Count(), 0 ); + totalmemory += GlobUsage( "HDR lightdata", dlightdataHDR.Count(), 0 ); + totalmemory += GlobUsage( "visdata", visdatasize, sizeof(dvisdata) ); + totalmemory += GlobUsage( "entdata", dentdata.Count(), 384*1024 ); // goal is <384K + + totalmemory += ArrayUsage( "LDR ambient table", g_LeafAmbientIndexLDR.Count(), MAX_MAP_LEAFS, sizeof( g_LeafAmbientIndexLDR[0] ) ); + totalmemory += ArrayUsage( "HDR ambient table", g_LeafAmbientIndexHDR.Count(), MAX_MAP_LEAFS, sizeof( g_LeafAmbientIndexHDR[0] ) ); + totalmemory += ArrayUsage( "LDR leaf ambient lighting", g_LeafAmbientLightingLDR.Count(), MAX_MAP_LEAFS, sizeof( g_LeafAmbientLightingLDR[0] ) ); + totalmemory += ArrayUsage( "HDR leaf ambient lighting", g_LeafAmbientLightingHDR.Count(), MAX_MAP_LEAFS, sizeof( g_LeafAmbientLightingHDR[0] ) ); + + totalmemory += ArrayUsage( "occluders", g_OccluderData.Count(), 0, sizeof( g_OccluderData[0] ) ); + totalmemory += ArrayUsage( "occluder polygons", g_OccluderPolyData.Count(), 0, sizeof( g_OccluderPolyData[0] ) ); + totalmemory += ArrayUsage( "occluder vert ind",g_OccluderVertexIndices.Count(),0, sizeof( g_OccluderVertexIndices[0] ) ); + + GameLumpHandle_t h = g_GameLumps.GetGameLumpHandle( GAMELUMP_DETAIL_PROPS ); + if (h != g_GameLumps.InvalidGameLump()) + totalmemory += GlobUsage( "detail props", 1, g_GameLumps.GameLumpSize(h) ); + h = g_GameLumps.GetGameLumpHandle( GAMELUMP_DETAIL_PROP_LIGHTING ); + if (h != g_GameLumps.InvalidGameLump()) + totalmemory += GlobUsage( "dtl prp lght", 1, g_GameLumps.GameLumpSize(h) ); + h = g_GameLumps.GetGameLumpHandle( GAMELUMP_DETAIL_PROP_LIGHTING_HDR ); + if (h != g_GameLumps.InvalidGameLump()) + totalmemory += GlobUsage( "HDR dtl prp lght", 1, g_GameLumps.GameLumpSize(h) ); + h = g_GameLumps.GetGameLumpHandle( GAMELUMP_STATIC_PROPS ); + if (h != g_GameLumps.InvalidGameLump()) + totalmemory += GlobUsage( "static props", 1, g_GameLumps.GameLumpSize(h) ); + + totalmemory += GlobUsage( "pakfile", GetPakFile()->EstimateSize(), 0 ); + // HACKHACK: Set physics limit at 4MB, in reality this is totally dynamic + totalmemory += GlobUsage( "physics", g_PhysCollideSize, 4*1024*1024 ); + totalmemory += GlobUsage( "physics terrain", g_PhysDispSize, 1*1024*1024 ); + + Msg( "\nLevel flags = %x\n", g_LevelFlags ); + + Msg( "\n" ); + + int triangleCount = 0; + + for ( int i = 0; i < numfaces; i++ ) + { + // face tris = numedges - 2 + triangleCount += dfaces[i].numedges - 2; + } + Msg("Total triangle count: %d\n", triangleCount ); + + // UNDONE: + // areaportals, portals, texdata, clusters, worldlights, portalverts +} + +/* +============= +PrintBSPPackDirectory + +Dumps a list of files stored in the bsp pack. +============= +*/ +void PrintBSPPackDirectory( void ) +{ + GetPakFile()->PrintDirectory(); +} + + +//============================================ + +int num_entities; +entity_t entities[MAX_MAP_ENTITIES]; + +void StripTrailing (char *e) +{ + char *s; + + s = e + strlen(e)-1; + while (s >= e && *s <= 32) + { + *s = 0; + s--; + } +} + +/* +================= +ParseEpair +================= +*/ +epair_t *ParseEpair (void) +{ + epair_t *e; + + e = (epair_t*)malloc (sizeof(epair_t)); + memset (e, 0, sizeof(epair_t)); + + if (strlen(token) >= MAX_KEY-1) + Error ("ParseEpar: token too long"); + e->key = copystring(token); + + GetToken (false); + if (strlen(token) >= MAX_VALUE-1) + Error ("ParseEpar: token too long"); + e->value = copystring(token); + + // strip trailing spaces + StripTrailing (e->key); + StripTrailing (e->value); + + return e; +} + + +/* +================ +ParseEntity +================ +*/ +qboolean ParseEntity (void) +{ + epair_t *e; + entity_t *mapent; + + if (!GetToken (true)) + return false; + + if (Q_stricmp (token, "{") ) + Error ("ParseEntity: { not found"); + + if (num_entities == MAX_MAP_ENTITIES) + Error ("num_entities == MAX_MAP_ENTITIES"); + + mapent = &entities[num_entities]; + num_entities++; + + do + { + if (!GetToken (true)) + Error ("ParseEntity: EOF without closing brace"); + if (!Q_stricmp (token, "}") ) + break; + e = ParseEpair (); + e->next = mapent->epairs; + mapent->epairs = e; + } while (1); + + return true; +} + +/* +================ +ParseEntities + +Parses the dentdata string into entities +================ +*/ +void ParseEntities (void) +{ + num_entities = 0; + ParseFromMemory (dentdata.Base(), dentdata.Count()); + + while (ParseEntity ()) + { + } +} + + +/* +================ +UnparseEntities + +Generates the dentdata string from all the entities +================ +*/ +void UnparseEntities (void) +{ + epair_t *ep; + char line[2048]; + int i; + char key[1024], value[1024]; + + CUtlBuffer buffer( 0, 0, CUtlBuffer::TEXT_BUFFER ); + buffer.EnsureCapacity( 256 * 1024 ); + + for (i=0 ; inext) + { + strcpy (key, ep->key); + StripTrailing (key); + strcpy (value, ep->value); + StripTrailing (value); + + sprintf(line, "\"%s\" \"%s\"\n", key, value); + buffer.PutString( line ); + } + buffer.PutString("}\n"); + } + int entdatasize = buffer.TellPut()+1; + + dentdata.SetSize( entdatasize ); + memcpy( dentdata.Base(), buffer.Base(), entdatasize-1 ); + dentdata[entdatasize-1] = 0; +} + +void PrintEntity (entity_t *ent) +{ + epair_t *ep; + + Msg ("------- entity %p -------\n", ent); + for (ep=ent->epairs ; ep ; ep=ep->next) + { + Msg ("%s = %s\n", ep->key, ep->value); + } + +} + +void SetKeyValue(entity_t *ent, const char *key, const char *value) +{ + epair_t *ep; + + for (ep=ent->epairs ; ep ; ep=ep->next) + if (!Q_stricmp (ep->key, key) ) + { + free (ep->value); + ep->value = copystring(value); + return; + } + ep = (epair_t*)malloc (sizeof(*ep)); + ep->next = ent->epairs; + ent->epairs = ep; + ep->key = copystring(key); + ep->value = copystring(value); +} + +char *ValueForKey (entity_t *ent, char *key) +{ + for (epair_t *ep=ent->epairs ; ep ; ep=ep->next) + if (!Q_stricmp (ep->key, key) ) + return ep->value; + return ""; +} + +vec_t FloatForKey (entity_t *ent, char *key) +{ + char *k = ValueForKey (ent, key); + return atof(k); +} + +vec_t FloatForKeyWithDefault (entity_t *ent, char *key, float default_value) +{ + for (epair_t *ep=ent->epairs ; ep ; ep=ep->next) + if (!Q_stricmp (ep->key, key) ) + return atof( ep->value ); + return default_value; +} + + + +int IntForKey (entity_t *ent, char *key) +{ + char *k = ValueForKey (ent, key); + return atol(k); +} + +int IntForKeyWithDefault(entity_t *ent, char *key, int nDefault ) +{ + char *k = ValueForKey (ent, key); + if ( !k[0] ) + return nDefault; + return atol(k); +} + +void GetVectorForKey (entity_t *ent, char *key, Vector& vec) +{ + + char *k = ValueForKey (ent, key); +// scanf into doubles, then assign, so it is vec_t size independent + double v1, v2, v3; + v1 = v2 = v3 = 0; + sscanf (k, "%lf %lf %lf", &v1, &v2, &v3); + vec[0] = v1; + vec[1] = v2; + vec[2] = v3; +} + +void GetVector2DForKey (entity_t *ent, char *key, Vector2D& vec) +{ + double v1, v2; + + char *k = ValueForKey (ent, key); +// scanf into doubles, then assign, so it is vec_t size independent + v1 = v2 = 0; + sscanf (k, "%lf %lf", &v1, &v2); + vec[0] = v1; + vec[1] = v2; +} + +void GetAnglesForKey (entity_t *ent, char *key, QAngle& angle) +{ + char *k; + double v1, v2, v3; + + k = ValueForKey (ent, key); +// scanf into doubles, then assign, so it is vec_t size independent + v1 = v2 = v3 = 0; + sscanf (k, "%lf %lf %lf", &v1, &v2, &v3); + angle[0] = v1; + angle[1] = v2; + angle[2] = v3; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void BuildFaceCalcWindingData( dface_t *pFace, int *points ) +{ + for( int i = 0; i < pFace->numedges; i++ ) + { + int eIndex = dsurfedges[pFace->firstedge+i]; + if( eIndex < 0 ) + { + points[i] = dedges[-eIndex].v[1]; + } + else + { + points[i] = dedges[eIndex].v[0]; + } + } +} + + +void TriStripToTriList( + unsigned short const *pTriStripIndices, + int nTriStripIndices, + unsigned short **pTriListIndices, + int *pnTriListIndices ) +{ + int nMaxTriListIndices = (nTriStripIndices - 2) * 3; + *pTriListIndices = new unsigned short[ nMaxTriListIndices ]; + *pnTriListIndices = 0; + + for( int i=0; i < nTriStripIndices - 2; i++ ) + { + if( pTriStripIndices[i] == pTriStripIndices[i+1] || + pTriStripIndices[i] == pTriStripIndices[i+2] || + pTriStripIndices[i+1] == pTriStripIndices[i+2] ) + { + } + else + { + // Flip odd numbered tris.. + if( i & 1 ) + { + (*pTriListIndices)[(*pnTriListIndices)++] = pTriStripIndices[i+2]; + (*pTriListIndices)[(*pnTriListIndices)++] = pTriStripIndices[i+1]; + (*pTriListIndices)[(*pnTriListIndices)++] = pTriStripIndices[i]; + } + else + { + (*pTriListIndices)[(*pnTriListIndices)++] = pTriStripIndices[i]; + (*pTriListIndices)[(*pnTriListIndices)++] = pTriStripIndices[i+1]; + (*pTriListIndices)[(*pnTriListIndices)++] = pTriStripIndices[i+2]; + } + } + } +} + + +void CalcTextureCoordsAtPoints( + float const texelsPerWorldUnits[2][4], + int const subtractOffset[2], + Vector const *pPoints, + int const nPoints, + Vector2D *pCoords ) +{ + for( int i=0; i < nPoints; i++ ) + { + for( int iCoord=0; iCoord < 2; iCoord++ ) + { + float *pDestCoord = &pCoords[i][iCoord]; + + *pDestCoord = 0; + for( int iDot=0; iDot < 3; iDot++ ) + *pDestCoord += pPoints[i][iDot] * texelsPerWorldUnits[iCoord][iDot]; + + *pDestCoord += texelsPerWorldUnits[iCoord][3]; + *pDestCoord -= subtractOffset[iCoord]; + } + } +} + + +/* +================ +CalcFaceExtents + +Fills in s->texmins[] and s->texsize[] +================ +*/ +void CalcFaceExtents(dface_t *s, int lightmapTextureMinsInLuxels[2], int lightmapTextureSizeInLuxels[2]) +{ + vec_t mins[2], maxs[2], val=0; + int i,j, e=0; + dvertex_t *v=NULL; + texinfo_t *tex=NULL; + + mins[0] = mins[1] = 1e24; + maxs[0] = maxs[1] = -1e24; + + tex = &texinfo[s->texinfo]; + + for (i=0 ; inumedges ; i++) + { + e = dsurfedges[s->firstedge+i]; + if (e >= 0) + v = dvertexes + dedges[e].v[0]; + else + v = dvertexes + dedges[-e].v[1]; + + for (j=0 ; j<2 ; j++) + { + val = v->point[0] * tex->lightmapVecsLuxelsPerWorldUnits[j][0] + + v->point[1] * tex->lightmapVecsLuxelsPerWorldUnits[j][1] + + v->point[2] * tex->lightmapVecsLuxelsPerWorldUnits[j][2] + + tex->lightmapVecsLuxelsPerWorldUnits[j][3]; + if (val < mins[j]) + mins[j] = val; + if (val > maxs[j]) + maxs[j] = val; + } + } + + int nMaxLightmapDim = (s->dispinfo == -1) ? MAX_LIGHTMAP_DIM_WITHOUT_BORDER : MAX_DISP_LIGHTMAP_DIM_WITHOUT_BORDER; + for (i=0 ; i<2 ; i++) + { + mins[i] = ( float )floor( mins[i] ); + maxs[i] = ( float )ceil( maxs[i] ); + + lightmapTextureMinsInLuxels[i] = ( int )mins[i]; + lightmapTextureSizeInLuxels[i] = ( int )( maxs[i] - mins[i] ); + if( lightmapTextureSizeInLuxels[i] > nMaxLightmapDim + 1 ) + { + Vector point = vec3_origin; + for (int j=0 ; jnumedges ; j++) + { + e = dsurfedges[s->firstedge+j]; + v = (e<0)?dvertexes + dedges[-e].v[1] : dvertexes + dedges[e].v[0]; + point += v->point; + Warning( "Bad surface extents point: %f %f %f\n", v->point.x, v->point.y, v->point.z ); + } + point *= 1.0f/s->numedges; + Error( "Bad surface extents - surface is too big to have a lightmap\n\tmaterial %s around point (%.1f %.1f %.1f)\n\t(dimension: %d, %d>%d)\n", + TexDataStringTable_GetString( dtexdata[texinfo[s->texinfo].texdata].nameStringTableID ), + point.x, point.y, point.z, + ( int )i, + ( int )lightmapTextureSizeInLuxels[i], + ( int )( nMaxLightmapDim + 1 ) + ); + } + } +} + + +void UpdateAllFaceLightmapExtents() +{ + for( int i=0; i < numfaces; i++ ) + { + dface_t *pFace = &dfaces[i]; + + if ( texinfo[pFace->texinfo].flags & (SURF_SKY|SURF_NOLIGHT) ) + continue; // non-lit texture + + CalcFaceExtents( pFace, pFace->m_LightmapTextureMinsInLuxels, pFace->m_LightmapTextureSizeInLuxels ); + } +} + + +//----------------------------------------------------------------------------- +// +// Helper class to iterate over leaves, used by tools +// +//----------------------------------------------------------------------------- + +#define TEST_EPSILON (0.03125) + + +class CToolBSPTree : public ISpatialQuery +{ +public: + // Returns the number of leaves + int LeafCount() const; + + // Enumerates the leaves along a ray, box, etc. + bool EnumerateLeavesAtPoint( Vector const& pt, ISpatialLeafEnumerator* pEnum, int context ); + bool EnumerateLeavesInBox( Vector const& mins, Vector const& maxs, ISpatialLeafEnumerator* pEnum, int context ); + bool EnumerateLeavesInSphere( Vector const& center, float radius, ISpatialLeafEnumerator* pEnum, int context ); + bool EnumerateLeavesAlongRay( Ray_t const& ray, ISpatialLeafEnumerator* pEnum, int context ); +}; + + +//----------------------------------------------------------------------------- +// Returns the number of leaves +//----------------------------------------------------------------------------- + +int CToolBSPTree::LeafCount() const +{ + return numleafs; +} + + +//----------------------------------------------------------------------------- +// Enumerates the leaves at a point +//----------------------------------------------------------------------------- + +bool CToolBSPTree::EnumerateLeavesAtPoint( Vector const& pt, + ISpatialLeafEnumerator* pEnum, int context ) +{ + int node = 0; + while( node >= 0 ) + { + dnode_t* pNode = &dnodes[node]; + dplane_t* pPlane = &dplanes[pNode->planenum]; + + if (DotProduct( pPlane->normal, pt ) <= pPlane->dist) + { + node = pNode->children[1]; + } + else + { + node = pNode->children[0]; + } + } + + return pEnum->EnumerateLeaf( - node - 1, context ); +} + + +//----------------------------------------------------------------------------- +// Enumerates the leaves in a box +//----------------------------------------------------------------------------- + +static bool EnumerateLeavesInBox_R( int node, Vector const& mins, + Vector const& maxs, ISpatialLeafEnumerator* pEnum, int context ) +{ + Vector cornermin, cornermax; + + while( node >= 0 ) + { + dnode_t* pNode = &dnodes[node]; + dplane_t* pPlane = &dplanes[pNode->planenum]; + + // Arbitrary split plane here + for (int i = 0; i < 3; ++i) + { + if (pPlane->normal[i] >= 0) + { + cornermin[i] = mins[i]; + cornermax[i] = maxs[i]; + } + else + { + cornermin[i] = maxs[i]; + cornermax[i] = mins[i]; + } + } + + if ( (DotProduct( pPlane->normal, cornermax ) - pPlane->dist) <= -TEST_EPSILON ) + { + node = pNode->children[1]; + } + else if ( (DotProduct( pPlane->normal, cornermin ) - pPlane->dist) >= TEST_EPSILON ) + { + node = pNode->children[0]; + } + else + { + if (!EnumerateLeavesInBox_R( pNode->children[0], mins, maxs, pEnum, context )) + { + return false; + } + + return EnumerateLeavesInBox_R( pNode->children[1], mins, maxs, pEnum, context ); + } + } + + return pEnum->EnumerateLeaf( - node - 1, context ); +} + +bool CToolBSPTree::EnumerateLeavesInBox( Vector const& mins, Vector const& maxs, + ISpatialLeafEnumerator* pEnum, int context ) +{ + return EnumerateLeavesInBox_R( 0, mins, maxs, pEnum, context ); +} + +//----------------------------------------------------------------------------- +// Enumerate leaves within a sphere +//----------------------------------------------------------------------------- + +static bool EnumerateLeavesInSphere_R( int node, Vector const& origin, + float radius, ISpatialLeafEnumerator* pEnum, int context ) +{ + while( node >= 0 ) + { + dnode_t* pNode = &dnodes[node]; + dplane_t* pPlane = &dplanes[pNode->planenum]; + + if (DotProduct( pPlane->normal, origin ) + radius - pPlane->dist <= -TEST_EPSILON ) + { + node = pNode->children[1]; + } + else if (DotProduct( pPlane->normal, origin ) - radius - pPlane->dist >= TEST_EPSILON ) + { + node = pNode->children[0]; + } + else + { + if (!EnumerateLeavesInSphere_R( pNode->children[0], + origin, radius, pEnum, context )) + { + return false; + } + + return EnumerateLeavesInSphere_R( pNode->children[1], + origin, radius, pEnum, context ); + } + } + + return pEnum->EnumerateLeaf( - node - 1, context ); +} + +bool CToolBSPTree::EnumerateLeavesInSphere( Vector const& center, float radius, ISpatialLeafEnumerator* pEnum, int context ) +{ + return EnumerateLeavesInSphere_R( 0, center, radius, pEnum, context ); +} + + +//----------------------------------------------------------------------------- +// Enumerate leaves along a ray +//----------------------------------------------------------------------------- + +static bool EnumerateLeavesAlongRay_R( int node, Ray_t const& ray, + Vector const& start, Vector const& end, ISpatialLeafEnumerator* pEnum, int context ) +{ + float front,back; + + while (node >= 0) + { + dnode_t* pNode = &dnodes[node]; + dplane_t* pPlane = &dplanes[pNode->planenum]; + + if ( pPlane->type <= PLANE_Z ) + { + front = start[pPlane->type] - pPlane->dist; + back = end[pPlane->type] - pPlane->dist; + } + else + { + front = DotProduct(start, pPlane->normal) - pPlane->dist; + back = DotProduct(end, pPlane->normal) - pPlane->dist; + } + + if (front <= -TEST_EPSILON && back <= -TEST_EPSILON) + { + node = pNode->children[1]; + } + else if (front >= TEST_EPSILON && back >= TEST_EPSILON) + { + node = pNode->children[0]; + } + else + { + // test the front side first + bool side = front < 0; + + // Compute intersection point based on the original ray + float splitfrac; + float denom = DotProduct( ray.m_Delta, pPlane->normal ); + if ( denom == 0.0f ) + { + splitfrac = 1.0f; + } + else + { + splitfrac = ( pPlane->dist - DotProduct( ray.m_Start, pPlane->normal ) ) / denom; + if (splitfrac < 0) + splitfrac = 0; + else if (splitfrac > 1) + splitfrac = 1; + } + + // Compute the split point + Vector split; + VectorMA( ray.m_Start, splitfrac, ray.m_Delta, split ); + + bool r = EnumerateLeavesAlongRay_R (pNode->children[side], ray, start, split, pEnum, context ); + if (!r) + return r; + return EnumerateLeavesAlongRay_R (pNode->children[!side], ray, split, end, pEnum, context); + } + } + + return pEnum->EnumerateLeaf( - node - 1, context ); +} + +bool CToolBSPTree::EnumerateLeavesAlongRay( Ray_t const& ray, ISpatialLeafEnumerator* pEnum, int context ) +{ + if (!ray.m_IsSwept) + { + Vector mins, maxs; + VectorAdd( ray.m_Start, ray.m_Extents, maxs ); + VectorSubtract( ray.m_Start, ray.m_Extents, mins ); + + return EnumerateLeavesInBox_R( 0, mins, maxs, pEnum, context ); + } + + // FIXME: Extruded ray not implemented yet + Assert( ray.m_IsRay ); + + Vector end; + VectorAdd( ray.m_Start, ray.m_Delta, end ); + return EnumerateLeavesAlongRay_R( 0, ray, ray.m_Start, end, pEnum, context ); +} + + +//----------------------------------------------------------------------------- +// Singleton accessor +//----------------------------------------------------------------------------- + +ISpatialQuery* ToolBSPTree() +{ + static CToolBSPTree s_ToolBSPTree; + return &s_ToolBSPTree; +} + + + +//----------------------------------------------------------------------------- +// Enumerates nodes in front to back order... +//----------------------------------------------------------------------------- + +// FIXME: Do we want this in the IBSPTree interface? + +static bool EnumerateNodesAlongRay_R( int node, Ray_t const& ray, float start, float end, + IBSPNodeEnumerator* pEnum, int context ) +{ + float front, back; + float startDotN, deltaDotN; + + while (node >= 0) + { + dnode_t* pNode = &dnodes[node]; + dplane_t* pPlane = &dplanes[pNode->planenum]; + + if ( pPlane->type <= PLANE_Z ) + { + startDotN = ray.m_Start[pPlane->type]; + deltaDotN = ray.m_Delta[pPlane->type]; + } + else + { + startDotN = DotProduct( ray.m_Start, pPlane->normal ); + deltaDotN = DotProduct( ray.m_Delta, pPlane->normal ); + } + + front = startDotN + start * deltaDotN - pPlane->dist; + back = startDotN + end * deltaDotN - pPlane->dist; + + if (front <= -TEST_EPSILON && back <= -TEST_EPSILON) + { + node = pNode->children[1]; + } + else if (front >= TEST_EPSILON && back >= TEST_EPSILON) + { + node = pNode->children[0]; + } + else + { + // test the front side first + bool side = front < 0; + + // Compute intersection point based on the original ray + float splitfrac; + if ( deltaDotN == 0.0f ) + { + splitfrac = 1.0f; + } + else + { + splitfrac = ( pPlane->dist - startDotN ) / deltaDotN; + if (splitfrac < 0.0f) + splitfrac = 0.0f; + else if (splitfrac > 1.0f) + splitfrac = 1.0f; + } + + bool r = EnumerateNodesAlongRay_R (pNode->children[side], ray, start, splitfrac, pEnum, context ); + if (!r) + return r; + + // Visit the node... + if (!pEnum->EnumerateNode( node, ray, splitfrac, context )) + return false; + + return EnumerateNodesAlongRay_R (pNode->children[!side], ray, splitfrac, end, pEnum, context); + } + } + + // Visit the leaf... + return pEnum->EnumerateLeaf( - node - 1, ray, start, end, context ); +} + + +bool EnumerateNodesAlongRay( Ray_t const& ray, IBSPNodeEnumerator* pEnum, int context ) +{ + Vector end; + VectorAdd( ray.m_Start, ray.m_Delta, end ); + return EnumerateNodesAlongRay_R( 0, ray, 0.0f, 1.0f, pEnum, context ); +} + + +//----------------------------------------------------------------------------- +// Helps us find all leaves associated with a particular cluster +//----------------------------------------------------------------------------- +CUtlVector g_ClusterLeaves; + +void BuildClusterTable( void ) +{ + int i, j; + int leafCount; + int leafList[MAX_MAP_LEAFS]; + + g_ClusterLeaves.SetCount( dvis->numclusters ); + for ( i = 0; i < dvis->numclusters; i++ ) + { + leafCount = 0; + for ( j = 0; j < numleafs; j++ ) + { + if ( dleafs[j].cluster == i ) + { + leafList[ leafCount ] = j; + leafCount++; + } + } + + g_ClusterLeaves[i].leafCount = leafCount; + if ( leafCount ) + { + g_ClusterLeaves[i].leafs.SetCount( leafCount ); + memcpy( g_ClusterLeaves[i].leafs.Base(), leafList, sizeof(int) * leafCount ); + } + } +} + +// There's a version of this in host.cpp!!! Make sure that they match. +void GetPlatformMapPath( const char *pMapPath, char *pPlatformMapPath, int dxlevel, int maxLength ) +{ + Q_StripExtension( pMapPath, pPlatformMapPath, maxLength ); + +// if( dxlevel <= 60 ) +// { +// Q_strncat( pPlatformMapPath, "_dx60", maxLength, COPY_ALL_CHARACTERS ); +// } + + Q_strncat( pPlatformMapPath, ".bsp", maxLength, COPY_ALL_CHARACTERS ); +} + +// There's a version of this in checksum_engine.cpp!!! Make sure that they match. +static bool CRC_MapFile(CRC32_t *crcvalue, const char *pszFileName) +{ + byte chunk[1024]; + lump_t *curLump; + + FileHandle_t fp = g_pFileSystem->Open( pszFileName, "rb" ); + if ( !fp ) + return false; + + // CRC across all lumps except for the Entities lump + for ( int l = 0; l < HEADER_LUMPS; ++l ) + { + if (l == LUMP_ENTITIES) + continue; + + curLump = &g_pBSPHeader->lumps[l]; + unsigned int nSize = curLump->filelen; + + g_pFileSystem->Seek( fp, curLump->fileofs, FILESYSTEM_SEEK_HEAD ); + + // Now read in 1K chunks + while ( nSize > 0 ) + { + int nBytesRead = 0; + + if ( nSize > 1024 ) + nBytesRead = g_pFileSystem->Read( chunk, 1024, fp ); + else + nBytesRead = g_pFileSystem->Read( chunk, nSize, fp ); + + // If any data was received, CRC it. + if ( nBytesRead > 0 ) + { + nSize -= nBytesRead; + CRC32_ProcessBuffer( crcvalue, chunk, nBytesRead ); + } + else + { + g_pFileSystem->Close( fp ); + return false; + } + } + } + + g_pFileSystem->Close( fp ); + return true; +} + + +void SetHDRMode( bool bHDR ) +{ + g_bHDR = bHDR; + if ( bHDR ) + { + pdlightdata = &dlightdataHDR; + g_pLeafAmbientLighting = &g_LeafAmbientLightingHDR; + g_pLeafAmbientIndex = &g_LeafAmbientIndexHDR; + pNumworldlights = &numworldlightsHDR; + dworldlights = dworldlightsHDR; +#ifdef VRAD + extern void VRadDetailProps_SetHDRMode( bool bHDR ); + VRadDetailProps_SetHDRMode( bHDR ); +#endif + } + else + { + pdlightdata = &dlightdataLDR; + g_pLeafAmbientLighting = &g_LeafAmbientLightingLDR; + g_pLeafAmbientIndex = &g_LeafAmbientIndexLDR; + pNumworldlights = &numworldlightsLDR; + dworldlights = dworldlightsLDR; +#ifdef VRAD + extern void VRadDetailProps_SetHDRMode( bool bHDR ); + VRadDetailProps_SetHDRMode( bHDR ); +#endif + } +} + +bool SwapVHV( void *pDestBase, void *pSrcBase ) +{ + byte *pDest = (byte*)pDestBase; + byte *pSrc = (byte*)pSrcBase; + + HardwareVerts::FileHeader_t *pHdr = (HardwareVerts::FileHeader_t*)( g_bSwapOnLoad ? pDest : pSrc ); + g_Swap.SwapFieldsToTargetEndian( (HardwareVerts::FileHeader_t*)pDest, (HardwareVerts::FileHeader_t*)pSrc ); + pSrc += sizeof(HardwareVerts::FileHeader_t); + pDest += sizeof(HardwareVerts::FileHeader_t); + + // This swap is pretty format specific + Assert( pHdr->m_nVersion == VHV_VERSION ); + if ( pHdr->m_nVersion != VHV_VERSION ) + return false; + + HardwareVerts::MeshHeader_t *pSrcMesh = (HardwareVerts::MeshHeader_t*)pSrc; + HardwareVerts::MeshHeader_t *pDestMesh = (HardwareVerts::MeshHeader_t*)pDest; + HardwareVerts::MeshHeader_t *pMesh = (HardwareVerts::MeshHeader_t*)( g_bSwapOnLoad ? pDest : pSrc ); + for ( int i = 0; i < pHdr->m_nMeshes; ++i, ++pMesh, ++pSrcMesh, ++pDestMesh ) + { + g_Swap.SwapFieldsToTargetEndian( pDestMesh, pSrcMesh ); + + pSrc = (byte*)pSrcBase + pMesh->m_nOffset; + pDest = (byte*)pDestBase + pMesh->m_nOffset; + + // Swap as a buffer of integers + // (source is bgra for an Intel swap to argb. PowerPC won't swap, so we need argb source. + g_Swap.SwapBufferToTargetEndian( (int*)pDest, (int*)pSrc, pMesh->m_nVertexes ); + } + return true; +} + +const char *ResolveStaticPropToModel( const char *pPropName ) +{ + // resolve back to static prop + int iProp = -1; + + // filename should be sp_???.vhv or sp_hdr_???.vhv + if ( V_strnicmp( pPropName, "sp_", 3 ) ) + { + return NULL; + } + const char *pPropNumber = V_strrchr( pPropName, '_' ); + if ( pPropNumber ) + { + sscanf( pPropNumber+1, "%d.vhv", &iProp ); + } + else + { + return NULL; + } + + // look up the prop to get to the actual model + if ( iProp < 0 || iProp >= g_StaticPropInstances.Count() ) + { + // prop out of range + return NULL; + } + int iModel = g_StaticPropInstances[iProp]; + if ( iModel < 0 || iModel >= g_StaticPropNames.Count() ) + { + // model out of range + return NULL; + } + + return g_StaticPropNames[iModel].String(); +} + +//----------------------------------------------------------------------------- +// Iterate files in pak file, distribute to converters +// pak file will be ready for serialization upon completion +//----------------------------------------------------------------------------- +void ConvertPakFileContents( const char *pInFilename ) +{ + IZip *newPakFile = IZip::CreateZip( NULL ); + + CUtlBuffer sourceBuf; + CUtlBuffer targetBuf; + bool bConverted; + CUtlVector< CUtlString > hdrFiles; + + int id = -1; + int fileSize; + while ( 1 ) + { + char relativeName[MAX_PATH]; + id = GetNextFilename( GetPakFile(), id, relativeName, sizeof( relativeName ), fileSize ); + if ( id == -1) + break; + + bConverted = false; + sourceBuf.Purge(); + targetBuf.Purge(); + + const char* pExtension = V_GetFileExtension( relativeName ); + const char* pExt = 0; + + bool bOK = ReadFileFromPak( GetPakFile(), relativeName, false, sourceBuf ); + if ( !bOK ) + { + Warning( "Failed to load '%s' from lump pak for conversion or copy in '%s'.\n", relativeName, pInFilename ); + continue; + } + + if ( pExtension && !V_stricmp( pExtension, "vtf" ) ) + { + bOK = g_pVTFConvertFunc( relativeName, sourceBuf, targetBuf, g_pCompressFunc ); + if ( !bOK ) + { + Warning( "Failed to convert '%s' in '%s'.\n", relativeName, pInFilename ); + continue; + } + + bConverted = true; + pExt = ".vtf"; + } + else if ( pExtension && !V_stricmp( pExtension, "vhv" ) ) + { + CUtlBuffer tempBuffer; + if ( g_pVHVFixupFunc ) + { + // caller supplied a fixup + const char *pModelName = ResolveStaticPropToModel( relativeName ); + if ( !pModelName ) + { + Warning( "Static Prop '%s' failed to resolve actual model in '%s'.\n", relativeName, pInFilename ); + continue; + } + + // output temp buffer may shrink, must use TellPut() to determine size + bOK = g_pVHVFixupFunc( relativeName, pModelName, sourceBuf, tempBuffer ); + if ( !bOK ) + { + Warning( "Failed to convert '%s' in '%s'.\n", relativeName, pInFilename ); + continue; + } + } + else + { + // use the source buffer as-is + tempBuffer.EnsureCapacity( sourceBuf.TellMaxPut() ); + tempBuffer.Put( sourceBuf.Base(), sourceBuf.TellMaxPut() ); + } + + // swap the VHV + targetBuf.EnsureCapacity( tempBuffer.TellPut() ); + bOK = SwapVHV( targetBuf.Base(), tempBuffer.Base() ); + if ( !bOK ) + { + Warning( "Failed to swap '%s' in '%s'.\n", relativeName, pInFilename ); + continue; + } + targetBuf.SeekPut( CUtlBuffer::SEEK_HEAD, tempBuffer.TellPut() ); + + if ( g_pCompressFunc ) + { + CUtlBuffer compressedBuffer; + targetBuf.SeekGet( CUtlBuffer::SEEK_HEAD, sizeof( HardwareVerts::FileHeader_t ) ); + bool bCompressed = g_pCompressFunc( targetBuf, compressedBuffer ); + if ( bCompressed ) + { + // copy all the header data off + CUtlBuffer headerBuffer; + headerBuffer.EnsureCapacity( sizeof( HardwareVerts::FileHeader_t ) ); + headerBuffer.Put( targetBuf.Base(), sizeof( HardwareVerts::FileHeader_t ) ); + + // reform the target with the header and then the compressed data + targetBuf.Clear(); + targetBuf.Put( headerBuffer.Base(), sizeof( HardwareVerts::FileHeader_t ) ); + targetBuf.Put( compressedBuffer.Base(), compressedBuffer.TellPut() ); + } + + targetBuf.SeekGet( CUtlBuffer::SEEK_HEAD, 0 ); + } + + bConverted = true; + pExt = ".vhv"; + } + + if ( !bConverted ) + { + // straight copy + AddBufferToPak( newPakFile, relativeName, sourceBuf.Base(), sourceBuf.TellMaxPut(), false ); + } + else + { + // converted filename + V_StripExtension( relativeName, relativeName, sizeof( relativeName ) ); + V_strcat( relativeName, ".360", sizeof( relativeName ) ); + V_strcat( relativeName, pExt, sizeof( relativeName ) ); + AddBufferToPak( newPakFile, relativeName, targetBuf.Base(), targetBuf.TellMaxPut(), false ); + } + + if ( V_stristr( relativeName, ".hdr" ) || V_stristr( relativeName, "_hdr" ) ) + { + hdrFiles.AddToTail( relativeName ); + } + + DevMsg( "Created '%s' in lump pak in '%s'.\n", relativeName, pInFilename ); + } + + // strip ldr version of hdr files + for ( int i=0; iRemoveFileFromZip( ldrFileName ); + } + } + + // discard old pak in favor of new pak + IZip::ReleaseZip( s_pakFile ); + s_pakFile = newPakFile; +} + +void SetAlignedLumpPosition( int lumpnum, int alignment = LUMP_ALIGNMENT ) +{ + g_pBSPHeader->lumps[lumpnum].fileofs = AlignFilePosition( g_hBSPFile, alignment ); +} + +template< class T > +int SwapLumpToDisk( int fieldType, int lumpnum ) +{ + if ( g_pBSPHeader->lumps[lumpnum].filelen == 0 ) + return 0; + + DevMsg( "Swapping %s\n", GetLumpName( lumpnum ) ); + + // lump swap may expand, allocate enough expansion room + void *pBuffer = malloc( 2*g_pBSPHeader->lumps[lumpnum].filelen ); + + // CopyLumpInternal will handle the swap on load case + unsigned int fieldSize = ( fieldType == FIELD_VECTOR ) ? sizeof(Vector) : sizeof(T); + unsigned int count = CopyLumpInternal( fieldType, lumpnum, (T*)pBuffer, g_pBSPHeader->lumps[lumpnum].version ); + g_pBSPHeader->lumps[lumpnum].filelen = count * fieldSize; + + if ( g_bSwapOnWrite ) + { + // Swap the lump in place before writing + switch( lumpnum ) + { + case LUMP_VISIBILITY: + SwapVisibilityLump( (byte*)pBuffer, (byte*)pBuffer, count ); + break; + + case LUMP_PHYSCOLLIDE: + // SwapPhyscollideLump may change size + SwapPhyscollideLump( (byte*)pBuffer, (byte*)pBuffer, count ); + g_pBSPHeader->lumps[lumpnum].filelen = count; + break; + + case LUMP_PHYSDISP: + SwapPhysdispLump( (byte*)pBuffer, (byte*)pBuffer, count ); + break; + + default: + g_Swap.SwapBufferToTargetEndian( (T*)pBuffer, (T*)pBuffer, g_pBSPHeader->lumps[lumpnum].filelen / sizeof(T) ); + break; + } + } + + SetAlignedLumpPosition( lumpnum ); + SafeWrite( g_hBSPFile, pBuffer, g_pBSPHeader->lumps[lumpnum].filelen ); + + free( pBuffer ); + + return g_pBSPHeader->lumps[lumpnum].filelen; +} + +template< class T > +int SwapLumpToDisk( int lumpnum ) +{ + if ( g_pBSPHeader->lumps[lumpnum].filelen == 0 || g_Lumps.bLumpParsed[lumpnum] ) + return 0; + + DevMsg( "Swapping %s\n", GetLumpName( lumpnum ) ); + + // lump swap may expand, allocate enough room + void *pBuffer = malloc( 2*g_pBSPHeader->lumps[lumpnum].filelen ); + + // CopyLumpInternal will handle the swap on load case + int count = CopyLumpInternal( lumpnum, (T*)pBuffer, g_pBSPHeader->lumps[lumpnum].version ); + g_pBSPHeader->lumps[lumpnum].filelen = count * sizeof(T); + + if ( g_bSwapOnWrite ) + { + // Swap the lump in place before writing + g_Swap.SwapFieldsToTargetEndian( (T*)pBuffer, (T*)pBuffer, count ); + } + + SetAlignedLumpPosition( lumpnum ); + SafeWrite( g_hBSPFile, pBuffer, g_pBSPHeader->lumps[lumpnum].filelen ); + free( pBuffer ); + + return g_pBSPHeader->lumps[lumpnum].filelen; +} + +void SwapLeafAmbientLightingLumpToDisk() +{ + if ( HasLump( LUMP_LEAF_AMBIENT_INDEX ) || HasLump( LUMP_LEAF_AMBIENT_INDEX_HDR ) ) + { + // current version, swap in place + if ( HasLump( LUMP_LEAF_AMBIENT_INDEX_HDR ) ) + { + // write HDR + SwapLumpToDisk< dleafambientlighting_t >( LUMP_LEAF_AMBIENT_LIGHTING_HDR ); + SwapLumpToDisk< dleafambientindex_t >( LUMP_LEAF_AMBIENT_INDEX_HDR ); + + // cull LDR + g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_LIGHTING].filelen = 0; + g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_INDEX].filelen = 0; + } + else + { + // no HDR, keep LDR version + SwapLumpToDisk< dleafambientlighting_t >( LUMP_LEAF_AMBIENT_LIGHTING ); + SwapLumpToDisk< dleafambientindex_t >( LUMP_LEAF_AMBIENT_INDEX ); + } + } + else + { + // older ambient lighting version (before index) + // load older ambient lighting into memory and build ambient/index + // an older leaf version would have already built the new LDR leaf ambient/index + int numLeafs = g_pBSPHeader->lumps[LUMP_LEAFS].filelen / sizeof( dleaf_t ); + LoadLeafAmbientLighting( numLeafs ); + + if ( HasLump( LUMP_LEAF_AMBIENT_LIGHTING_HDR ) ) + { + DevMsg( "Swapping %s\n", GetLumpName( LUMP_LEAF_AMBIENT_LIGHTING_HDR ) ); + DevMsg( "Swapping %s\n", GetLumpName( LUMP_LEAF_AMBIENT_INDEX_HDR ) ); + + // write HDR + if ( g_bSwapOnWrite ) + { + g_Swap.SwapFieldsToTargetEndian( g_LeafAmbientLightingHDR.Base(), g_LeafAmbientLightingHDR.Count() ); + g_Swap.SwapFieldsToTargetEndian( g_LeafAmbientIndexHDR.Base(), g_LeafAmbientIndexHDR.Count() ); + } + + SetAlignedLumpPosition( LUMP_LEAF_AMBIENT_LIGHTING_HDR ); + g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_LIGHTING_HDR].version = LUMP_LEAF_AMBIENT_LIGHTING_VERSION; + g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_LIGHTING_HDR].filelen = g_LeafAmbientLightingHDR.Count() * sizeof( dleafambientlighting_t ); + SafeWrite( g_hBSPFile, g_LeafAmbientLightingHDR.Base(), g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_LIGHTING_HDR].filelen ); + + SetAlignedLumpPosition( LUMP_LEAF_AMBIENT_INDEX_HDR ); + g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_INDEX_HDR].filelen = g_LeafAmbientIndexHDR.Count() * sizeof( dleafambientindex_t ); + SafeWrite( g_hBSPFile, g_LeafAmbientIndexHDR.Base(), g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_INDEX_HDR].filelen ); + + // mark as processed + g_Lumps.bLumpParsed[LUMP_LEAF_AMBIENT_LIGHTING_HDR] = true; + g_Lumps.bLumpParsed[LUMP_LEAF_AMBIENT_INDEX_HDR] = true; + + // cull LDR + g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_LIGHTING].filelen = 0; + g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_INDEX].filelen = 0; + } + else + { + // no HDR, keep LDR version + DevMsg( "Swapping %s\n", GetLumpName( LUMP_LEAF_AMBIENT_LIGHTING ) ); + DevMsg( "Swapping %s\n", GetLumpName( LUMP_LEAF_AMBIENT_INDEX ) ); + + if ( g_bSwapOnWrite ) + { + g_Swap.SwapFieldsToTargetEndian( g_LeafAmbientLightingLDR.Base(), g_LeafAmbientLightingLDR.Count() ); + g_Swap.SwapFieldsToTargetEndian( g_LeafAmbientIndexLDR.Base(), g_LeafAmbientIndexLDR.Count() ); + } + + SetAlignedLumpPosition( LUMP_LEAF_AMBIENT_LIGHTING ); + g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_LIGHTING].version = LUMP_LEAF_AMBIENT_LIGHTING_VERSION; + g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_LIGHTING].filelen = g_LeafAmbientLightingLDR.Count() * sizeof( dleafambientlighting_t ); + SafeWrite( g_hBSPFile, g_LeafAmbientLightingLDR.Base(), g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_LIGHTING].filelen ); + + SetAlignedLumpPosition( LUMP_LEAF_AMBIENT_INDEX ); + g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_INDEX].filelen = g_LeafAmbientIndexLDR.Count() * sizeof( dleafambientindex_t ); + SafeWrite( g_hBSPFile, g_LeafAmbientIndexLDR.Base(), g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_INDEX].filelen ); + + // mark as processed + g_Lumps.bLumpParsed[LUMP_LEAF_AMBIENT_LIGHTING] = true; + g_Lumps.bLumpParsed[LUMP_LEAF_AMBIENT_INDEX] = true; + } + + g_LeafAmbientLightingLDR.Purge(); + g_LeafAmbientIndexLDR.Purge(); + g_LeafAmbientLightingHDR.Purge(); + g_LeafAmbientIndexHDR.Purge(); + } +} + +void SwapLeafLumpToDisk( void ) +{ + DevMsg( "Swapping %s\n", GetLumpName( LUMP_LEAFS ) ); + + // load the leafs + int count = LoadLeafs(); + if ( g_bSwapOnWrite ) + { + g_Swap.SwapFieldsToTargetEndian( dleafs, count ); + } + + bool bOldLeafVersion = ( LumpVersion( LUMP_LEAFS ) == 0 ); + if ( bOldLeafVersion ) + { + // version has been converted in the load process + // not updating the version ye, SwapLeafAmbientLightingLumpToDisk() can detect + g_pBSPHeader->lumps[LUMP_LEAFS].filelen = count * sizeof( dleaf_t ); + } + + SetAlignedLumpPosition( LUMP_LEAFS ); + SafeWrite( g_hBSPFile, dleafs, g_pBSPHeader->lumps[LUMP_LEAFS].filelen ); + + SwapLeafAmbientLightingLumpToDisk(); + + if ( bOldLeafVersion ) + { + // version has been converted in the load process + // can now safely change + g_pBSPHeader->lumps[LUMP_LEAFS].version = 1; + } + +#if defined( BSP_USE_LESS_MEMORY ) + if ( dleafs ) + { + free( dleafs ); + dleafs = NULL; + } +#endif +} + +void SwapOcclusionLumpToDisk( void ) +{ + DevMsg( "Swapping %s\n", GetLumpName( LUMP_OCCLUSION ) ); + + LoadOcclusionLump(); + SetAlignedLumpPosition( LUMP_OCCLUSION ); + AddOcclusionLump(); +} + +void SwapPakfileLumpToDisk( const char *pInFilename ) +{ + DevMsg( "Swapping %s\n", GetLumpName( LUMP_PAKFILE ) ); + + byte *pakbuffer = NULL; + int paksize = CopyVariableLump( FIELD_CHARACTER, LUMP_PAKFILE, ( void ** )&pakbuffer ); + if ( paksize > 0 ) + { + GetPakFile()->ActivateByteSwapping( IsX360() ); + GetPakFile()->ParseFromBuffer( pakbuffer, paksize ); + + ConvertPakFileContents( pInFilename ); + } + free( pakbuffer ); + + SetAlignedLumpPosition( LUMP_PAKFILE, XBOX_DVD_SECTORSIZE ); + WritePakFileLump(); + + ReleasePakFileLumps(); +} + +void SwapGameLumpsToDisk( void ) +{ + DevMsg( "Swapping %s\n", GetLumpName( LUMP_GAME_LUMP ) ); + + g_GameLumps.ParseGameLump( g_pBSPHeader ); + SetAlignedLumpPosition( LUMP_GAME_LUMP ); + AddGameLumps(); +} + +//----------------------------------------------------------------------------- +// Generate a table of all static props, used for resolving static prop lighting +// files back to their actual mdl. +//----------------------------------------------------------------------------- +void BuildStaticPropNameTable() +{ + g_StaticPropNames.Purge(); + g_StaticPropInstances.Purge(); + + g_GameLumps.ParseGameLump( g_pBSPHeader ); + + GameLumpHandle_t hGameLump = g_GameLumps.GetGameLumpHandle( GAMELUMP_STATIC_PROPS ); + if ( hGameLump != g_GameLumps.InvalidGameLump() ) + { + int nVersion = g_GameLumps.GetGameLumpVersion( hGameLump ); + if ( nVersion < 4 ) + { + // old unsupported version + return; + } + + if ( nVersion != 4 && nVersion != 5 && nVersion != 6 ) + { + Error( "Unknown Static Prop Lump version %d!\n", nVersion ); + } + + byte *pGameLumpData = (byte *)g_GameLumps.GetGameLump( hGameLump ); + if ( pGameLumpData && g_GameLumps.GameLumpSize( hGameLump ) ) + { + // get the model dictionary + int count = ((int *)pGameLumpData)[0]; + pGameLumpData += sizeof( int ); + StaticPropDictLump_t *pStaticPropDictLump = (StaticPropDictLump_t *)pGameLumpData; + for ( int i = 0; i < count; i++ ) + { + g_StaticPropNames.AddToTail( pStaticPropDictLump[i].m_Name ); + } + pGameLumpData += count * sizeof( StaticPropDictLump_t ); + + // skip the leaf list + count = ((int *)pGameLumpData)[0]; + pGameLumpData += sizeof( int ); + pGameLumpData += count * sizeof( StaticPropLeafLump_t ); + + // get the instances + count = ((int *)pGameLumpData)[0]; + pGameLumpData += sizeof( int ); + for ( int i = 0; i < count; i++ ) + { + int propType; + if ( nVersion == 4 ) + { + propType = ((StaticPropLumpV4_t *)pGameLumpData)->m_PropType; + pGameLumpData += sizeof( StaticPropLumpV4_t ); + } + else if ( nVersion == 5 ) + { + propType = ((StaticPropLumpV5_t *)pGameLumpData)->m_PropType; + pGameLumpData += sizeof( StaticPropLumpV5_t ); + } + else + { + propType = ((StaticPropLump_t *)pGameLumpData)->m_PropType; + pGameLumpData += sizeof( StaticPropLump_t ); + } + g_StaticPropInstances.AddToTail( propType ); + } + } + } + + g_GameLumps.DestroyAllGameLumps(); +} + +int AlignBuffer( CUtlBuffer &buffer, int alignment ) +{ + unsigned int newPosition = AlignValue( buffer.TellPut(), alignment ); + int padLength = newPosition - buffer.TellPut(); + for ( int i = 0; ipLump->fileofs; + int fileOffsetB = pSortedLumpB->pLump->fileofs; + + int fileSizeA = pSortedLumpA->pLump->filelen; + int fileSizeB = pSortedLumpB->pLump->filelen; + + // invalid or empty lumps get sorted together + if ( !fileSizeA ) + { + fileOffsetA = 0; + } + if ( !fileSizeB ) + { + fileOffsetB = 0; + } + + // compare by offset, want ascending + if ( fileOffsetA < fileOffsetB ) + { + return -1; + } + else if ( fileOffsetA > fileOffsetB ) + { + return 1; + } + + return 0; +} + +bool CompressGameLump( dheader_t *pInBSPHeader, dheader_t *pOutBSPHeader, CUtlBuffer &outputBuffer, CompressFunc_t pCompressFunc ) +{ + CByteswap byteSwap; + + dgamelumpheader_t* pInGameLumpHeader = (dgamelumpheader_t*)(((byte *)pInBSPHeader) + pInBSPHeader->lumps[LUMP_GAME_LUMP].fileofs); + dgamelump_t* pInGameLump = (dgamelump_t*)(pInGameLumpHeader + 1); + + byteSwap.ActivateByteSwapping( true ); + byteSwap.SwapFieldsToTargetEndian( pInGameLumpHeader ); + byteSwap.SwapFieldsToTargetEndian( pInGameLump, pInGameLumpHeader->lumpCount ); + + unsigned int newOffset = outputBuffer.TellPut(); + outputBuffer.Put( pInGameLumpHeader, sizeof( dgamelumpheader_t ) ); + outputBuffer.Put( pInGameLump, pInGameLumpHeader->lumpCount * sizeof( dgamelump_t ) ); + + dgamelumpheader_t* pOutGameLumpHeader = (dgamelumpheader_t*)((byte *)outputBuffer.Base() + newOffset); + dgamelump_t* pOutGameLump = (dgamelump_t*)(pOutGameLumpHeader + 1); + + // add a dummy terminal gamelump + // purposely NOT updating the .filelen to reflect the compressed size, but leaving as original size + // callers use the next entry offset to determine compressed size + pOutGameLumpHeader->lumpCount++; + dgamelump_t dummyLump = { 0 }; + outputBuffer.Put( &dummyLump, sizeof( dgamelump_t ) ); + + for ( int i = 0; i < pInGameLumpHeader->lumpCount; i++ ) + { + CUtlBuffer inputBuffer; + CUtlBuffer compressedBuffer; + + pOutGameLump[i].fileofs = AlignBuffer( outputBuffer, 4 ); + + if ( pInGameLump[i].filelen ) + { + inputBuffer.SetExternalBuffer( ((byte *)pInBSPHeader) + pInGameLump[i].fileofs, pInGameLump[i].filelen, pInGameLump[i].filelen ); + + bool bCompressed = pCompressFunc( inputBuffer, compressedBuffer ); + if ( bCompressed ) + { + pOutGameLump[i].flags |= GAMELUMPFLAG_COMPRESSED; + + outputBuffer.Put( compressedBuffer.Base(), compressedBuffer.TellPut() ); + compressedBuffer.Purge(); + } + else + { + // as is + outputBuffer.Put( inputBuffer.Base(), inputBuffer.TellPut() ); + } + } + } + + // fix the dummy terminal lump + int lastLump = pOutGameLumpHeader->lumpCount-1; + pOutGameLump[lastLump].fileofs = outputBuffer.TellPut(); + + // fix the output for 360, swapping it back + byteSwap.SwapFieldsToTargetEndian( pOutGameLump, pOutGameLumpHeader->lumpCount ); + byteSwap.SwapFieldsToTargetEndian( pOutGameLumpHeader ); + + pOutBSPHeader->lumps[LUMP_GAME_LUMP].fileofs = newOffset; + pOutBSPHeader->lumps[LUMP_GAME_LUMP].filelen = outputBuffer.TellPut() - newOffset; + + return true; +} + +bool CompressBSP( CUtlBuffer &inputBuffer, CUtlBuffer &outputBuffer, CompressFunc_t pCompressFunc ) +{ + CByteswap byteSwap; + + dheader_t *pInBSPHeader = (dheader_t *)inputBuffer.Base(); + if ( pInBSPHeader->ident != BigLong( IDBSPHEADER ) || !pCompressFunc ) + { + // only compress 360 bsp's + return false; + } + + // bsp is 360, swap the header back + byteSwap.ActivateByteSwapping( true ); + byteSwap.SwapFieldsToTargetEndian( pInBSPHeader ); + + // output will be smaller, use input size as upper bound + outputBuffer.EnsureCapacity( inputBuffer.TellMaxPut() ); + outputBuffer.Put( pInBSPHeader, sizeof( dheader_t ) ); + + dheader_t *pOutBSPHeader = (dheader_t *)outputBuffer.Base(); + + // must adhere to input lump's offset order and process according to that, NOT lump num + // sort by offset order + CUtlVector< SortedLump_t > sortedLumps; + for ( int i = 0; i < HEADER_LUMPS; i++ ) + { + int iIndex = sortedLumps.AddToTail(); + sortedLumps[iIndex].lumpNum = i; + sortedLumps[iIndex].pLump = &pInBSPHeader->lumps[i]; + } + sortedLumps.Sort( SortLumpsByOffset ); + + // iterate in sorted order + for ( int i = 0; i < HEADER_LUMPS; ++i ) + { + SortedLump_t *pSortedLump = &sortedLumps[i]; + int lumpNum = pSortedLump->lumpNum; + + if ( !pSortedLump->pLump->filelen ) + { + // degenerate + pOutBSPHeader->lumps[lumpNum].fileofs = 0; + } + else + { + int alignment = 4; + if ( lumpNum == LUMP_PAKFILE ) + { + alignment = 2048; + } + unsigned int newOffset = AlignBuffer( outputBuffer, alignment ); + + // only set by compressed lumps, hides the uncompressed size + *((unsigned int *)pOutBSPHeader->lumps[lumpNum].fourCC) = 0; + + CUtlBuffer inputBuffer; + inputBuffer.SetExternalBuffer( ((byte *)pInBSPHeader) + pSortedLump->pLump->fileofs, pSortedLump->pLump->filelen, pSortedLump->pLump->filelen ); + + if ( lumpNum == LUMP_GAME_LUMP ) + { + // the game lump has to have each of its components individually compressed + CompressGameLump( pInBSPHeader, pOutBSPHeader, outputBuffer, pCompressFunc ); + } + else if ( lumpNum == LUMP_PAKFILE ) + { + // add as is + pOutBSPHeader->lumps[lumpNum].fileofs = newOffset; + outputBuffer.Put( inputBuffer.Base(), inputBuffer.TellPut() ); + } + else + { + CUtlBuffer compressedBuffer; + bool bCompressed = pCompressFunc( inputBuffer, compressedBuffer ); + if ( bCompressed ) + { + // placing the uncompressed size in the unused fourCC, will decode at runtime + *((unsigned int *)pOutBSPHeader->lumps[lumpNum].fourCC) = BigLong( inputBuffer.TellPut() ); + pOutBSPHeader->lumps[lumpNum].filelen = compressedBuffer.TellPut(); + pOutBSPHeader->lumps[lumpNum].fileofs = newOffset; + outputBuffer.Put( compressedBuffer.Base(), compressedBuffer.TellPut() ); + compressedBuffer.Purge(); + } + else + { + // add as is + pOutBSPHeader->lumps[lumpNum].fileofs = newOffset; + outputBuffer.Put( inputBuffer.Base(), inputBuffer.TellPut() ); + } + } + } + } + + // fix the output for 360, swapping it back + byteSwap.SetTargetBigEndian( true ); + byteSwap.SwapFieldsToTargetEndian( pOutBSPHeader ); + + return true; +} + +//----------------------------------------------------------------------------- +// For all lumps in a bsp: Loads the lump from file A, swaps it, writes it to file B. +// This limits the memory used for the swap process which helps the Xbox 360. +// +// NOTE: These lumps will be written to the file in exactly the order they appear here, +// so they can be shifted around if desired for file access optimization. +//----------------------------------------------------------------------------- +bool SwapBSPFile( const char *pInFilename, const char *pOutFilename, bool bSwapOnLoad, VTFConvertFunc_t pVTFConvertFunc, VHVFixupFunc_t pVHVFixupFunc, CompressFunc_t pCompressFunc ) +{ + DevMsg( "Creating %s\n", pOutFilename ); + + if ( !g_pFileSystem->FileExists( pInFilename ) ) + { + Warning( "Error! Couldn't open input file %s - BSP swap failed!\n", pInFilename ); + return false; + } + + g_hBSPFile = SafeOpenWrite( pOutFilename ); + if ( !g_hBSPFile ) + { + Warning( "Error! Couldn't open output file %s - BSP swap failed!\n", pOutFilename ); + return false; + } + + if ( !pVTFConvertFunc ) + { + Warning( "Error! Missing VTF Conversion function\n" ); + return false; + } + g_pVTFConvertFunc = pVTFConvertFunc; + + // optional VHV fixup + g_pVHVFixupFunc = pVHVFixupFunc; + + // optional compression callback + g_pCompressFunc = pCompressFunc; + + // These must be mutually exclusive + g_bSwapOnLoad = bSwapOnLoad; + g_bSwapOnWrite = !bSwapOnLoad; + + g_Swap.ActivateByteSwapping( true ); + + OpenBSPFile( pInFilename ); + + // CRC the bsp first + CRC32_t mapCRC; + CRC32_Init(&mapCRC); + if ( !CRC_MapFile( &mapCRC, pInFilename ) ) + { + Warning( "Failed to CRC the bsp\n" ); + return false; + } + + // hold a dictionary of all the static prop names + // this is needed to properly convert any VHV files inside the pak lump + BuildStaticPropNameTable(); + + // Set the output file pointer after the header + dheader_t dummyHeader = { 0 }; + SafeWrite( g_hBSPFile, &dummyHeader, sizeof( dheader_t ) ); + + // To allow for alignment fixups, the lumps will be written to the + // output file in the order they appear in this function. + + // NOTE: Flags for 360 !!!MUST!!! be first + SwapLumpToDisk< dflagslump_t >( LUMP_MAP_FLAGS ); + + // complex lump swaps first or for later contingent data + SwapLeafLumpToDisk(); + SwapOcclusionLumpToDisk(); + SwapGameLumpsToDisk(); + + // Strip dead or non relevant lumps + g_pBSPHeader->lumps[LUMP_DISP_LIGHTMAP_ALPHAS].filelen = 0; + g_pBSPHeader->lumps[LUMP_FACEIDS].filelen = 0; + + // Strip obsolete LDR in favor of HDR + if ( SwapLumpToDisk( LUMP_FACES_HDR ) ) + { + g_pBSPHeader->lumps[LUMP_FACES].filelen = 0; + } + else + { + // no HDR, keep LDR version + SwapLumpToDisk( LUMP_FACES ); + } + + if ( SwapLumpToDisk( LUMP_WORLDLIGHTS_HDR ) ) + { + g_pBSPHeader->lumps[LUMP_WORLDLIGHTS].filelen = 0; + } + else + { + // no HDR, keep LDR version + SwapLumpToDisk( LUMP_WORLDLIGHTS ); + } + + // Simple lump swaps + SwapLumpToDisk( FIELD_CHARACTER, LUMP_PHYSDISP ); + SwapLumpToDisk( FIELD_CHARACTER, LUMP_PHYSCOLLIDE ); + SwapLumpToDisk( FIELD_CHARACTER, LUMP_VISIBILITY ); + SwapLumpToDisk( LUMP_MODELS ); + SwapLumpToDisk( LUMP_VERTEXES ); + SwapLumpToDisk( LUMP_PLANES ); + SwapLumpToDisk( LUMP_NODES ); + SwapLumpToDisk( LUMP_TEXINFO ); + SwapLumpToDisk( LUMP_TEXDATA ); + SwapLumpToDisk( LUMP_DISPINFO ); + SwapLumpToDisk( LUMP_DISP_VERTS ); + SwapLumpToDisk( LUMP_DISP_TRIS ); + SwapLumpToDisk( FIELD_CHARACTER, LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS ); + SwapLumpToDisk( LUMP_FACE_MACRO_TEXTURE_INFO ); + SwapLumpToDisk( LUMP_PRIMITIVES ); + SwapLumpToDisk( LUMP_PRIMVERTS ); + SwapLumpToDisk( FIELD_SHORT, LUMP_PRIMINDICES ); + SwapLumpToDisk( LUMP_ORIGINALFACES ); + SwapLumpToDisk( FIELD_SHORT, LUMP_LEAFFACES ); + SwapLumpToDisk( FIELD_SHORT, LUMP_LEAFBRUSHES ); + SwapLumpToDisk( FIELD_INTEGER, LUMP_SURFEDGES ); + SwapLumpToDisk( LUMP_EDGES ); + SwapLumpToDisk( LUMP_BRUSHES ); + SwapLumpToDisk( LUMP_BRUSHSIDES ); + SwapLumpToDisk( LUMP_AREAS ); + SwapLumpToDisk( LUMP_AREAPORTALS ); + SwapLumpToDisk( FIELD_CHARACTER, LUMP_ENTITIES ); + SwapLumpToDisk( LUMP_LEAFWATERDATA ); + SwapLumpToDisk( FIELD_VECTOR, LUMP_VERTNORMALS ); + SwapLumpToDisk( FIELD_SHORT, LUMP_VERTNORMALINDICES ); + SwapLumpToDisk( FIELD_VECTOR, LUMP_CLIPPORTALVERTS ); + SwapLumpToDisk( LUMP_CUBEMAPS ); + SwapLumpToDisk( FIELD_CHARACTER, LUMP_TEXDATA_STRING_DATA ); + SwapLumpToDisk( FIELD_INTEGER, LUMP_TEXDATA_STRING_TABLE ); + SwapLumpToDisk( LUMP_OVERLAYS ); + SwapLumpToDisk( LUMP_WATEROVERLAYS ); + SwapLumpToDisk( FIELD_SHORT, LUMP_LEAFMINDISTTOWATER ); + SwapLumpToDisk( LUMP_OVERLAY_FADES ); + + + // NOTE: this data placed at the end for the sake of 360: + { + // NOTE: lighting must be the penultimate lump + // (allows 360 to free this memory part-way through map loading) + if ( SwapLumpToDisk( FIELD_CHARACTER, LUMP_LIGHTING_HDR ) ) + { + g_pBSPHeader->lumps[LUMP_LIGHTING].filelen = 0; + } + else + { + // no HDR, keep LDR version + SwapLumpToDisk( FIELD_CHARACTER, LUMP_LIGHTING ); + } + // NOTE: Pakfile for 360 !!!MUST!!! be last + SwapPakfileLumpToDisk( pInFilename ); + } + + + // Store the crc in the flags lump version field + g_pBSPHeader->lumps[LUMP_MAP_FLAGS].version = mapCRC; + + // Pad out the end of the file to a sector boundary for optimal IO + AlignFilePosition( g_hBSPFile, XBOX_DVD_SECTORSIZE ); + + // Warn of any lumps that didn't get swapped + for ( int i = 0; i < HEADER_LUMPS; ++i ) + { + if ( HasLump( i ) && !g_Lumps.bLumpParsed[i] ) + { + // a new lump got added that needs to have a swap function + Warning( "BSP: '%s', %s has no swap or copy function. Discarding!\n", pInFilename, GetLumpName(i) ); + + // the data didn't get copied, so don't reference garbage + g_pBSPHeader->lumps[i].filelen = 0; + } + } + + // Write the updated header + g_pFileSystem->Seek( g_hBSPFile, 0, FILESYSTEM_SEEK_HEAD ); + WriteData( g_pBSPHeader ); + g_pFileSystem->Close( g_hBSPFile ); + g_hBSPFile = 0; + + // Cleanup + g_Swap.ActivateByteSwapping( false ); + + CloseBSPFile(); + + g_StaticPropNames.Purge(); + g_StaticPropInstances.Purge(); + + DevMsg( "Finished BSP Swap\n" ); + + // caller provided compress func will further compress compatible lumps + if ( pCompressFunc ) + { + CUtlBuffer inputBuffer; + if ( !g_pFileSystem->ReadFile( pOutFilename, NULL, inputBuffer ) ) + { + Warning( "Error! Couldn't read file %s - final BSP compression failed!\n", pOutFilename ); + return false; + } + + CUtlBuffer outputBuffer; + if ( !CompressBSP( inputBuffer, outputBuffer, pCompressFunc ) ) + { + Warning( "Error! Failed to compress BSP '%s'!\n", pOutFilename ); + return false; + } + + g_hBSPFile = SafeOpenWrite( pOutFilename ); + if ( !g_hBSPFile ) + { + Warning( "Error! Couldn't open output file %s - BSP swap failed!\n", pOutFilename ); + return false; + } + SafeWrite( g_hBSPFile, outputBuffer.Base(), outputBuffer.TellPut() ); + g_pFileSystem->Close( g_hBSPFile ); + g_hBSPFile = 0; + } + + return true; +} + +//----------------------------------------------------------------------------- +// Get the pak lump from a BSP +//----------------------------------------------------------------------------- +bool GetPakFileLump( const char *pBSPFilename, void **pPakData, int *pPakSize ) +{ + *pPakData = NULL; + *pPakSize = 0; + + if ( !g_pFileSystem->FileExists( pBSPFilename ) ) + { + Warning( "Error! Couldn't open file %s!\n", pBSPFilename ); + return false; + } + + // determine endian nature + dheader_t *pHeader; + LoadFile( pBSPFilename, (void **)&pHeader ); + bool bSwap = ( pHeader->ident == BigLong( IDBSPHEADER ) ); + free( pHeader ); + + g_bSwapOnLoad = bSwap; + g_bSwapOnWrite = !bSwap; + + OpenBSPFile( pBSPFilename ); + + if ( g_pBSPHeader->lumps[LUMP_PAKFILE].filelen ) + { + *pPakSize = CopyVariableLump( FIELD_CHARACTER, LUMP_PAKFILE, pPakData ); + } + + CloseBSPFile(); + + return true; +} + +// compare function for qsort below +static int LumpOffsetCompare( const void *pElem1, const void *pElem2 ) +{ + int lump1 = *(byte *)pElem1; + int lump2 = *(byte *)pElem2; + + if ( lump1 != lump2 ) + { + // force LUMP_MAP_FLAGS to be first, always + if ( lump1 == LUMP_MAP_FLAGS ) + { + return -1; + } + else if ( lump2 == LUMP_MAP_FLAGS ) + { + return 1; + } + + // force LUMP_PAKFILE to be last, always + if ( lump1 == LUMP_PAKFILE ) + { + return 1; + } + else if ( lump2 == LUMP_PAKFILE ) + { + return -1; + } + } + + int fileOffset1 = g_pBSPHeader->lumps[lump1].fileofs; + int fileOffset2 = g_pBSPHeader->lumps[lump2].fileofs; + + // invalid or empty lumps will get sorted together + if ( !g_pBSPHeader->lumps[lump1].filelen ) + { + fileOffset1 = 0; + } + + if ( !g_pBSPHeader->lumps[lump2].filelen ) + { + fileOffset2 = 0; + } + + // compare by offset + if ( fileOffset1 < fileOffset2 ) + { + return -1; + } + else if ( fileOffset1 > fileOffset2 ) + { + return 1; + } + return 0; +} + +//----------------------------------------------------------------------------- +// Replace the pak lump in a BSP +//----------------------------------------------------------------------------- +bool SetPakFileLump( const char *pBSPFilename, const char *pNewFilename, void *pPakData, int pakSize ) +{ + if ( !g_pFileSystem->FileExists( pBSPFilename ) ) + { + Warning( "Error! Couldn't open file %s!\n", pBSPFilename ); + return false; + } + + // determine endian nature + dheader_t *pHeader; + LoadFile( pBSPFilename, (void **)&pHeader ); + bool bSwap = ( pHeader->ident == BigLong( IDBSPHEADER ) ); + free( pHeader ); + + g_bSwapOnLoad = bSwap; + g_bSwapOnWrite = bSwap; + + OpenBSPFile( pBSPFilename ); + + // save a copy of the old header + // generating a new bsp is a destructive operation + dheader_t oldHeader; + oldHeader = *g_pBSPHeader; + + g_hBSPFile = SafeOpenWrite( pNewFilename ); + if ( !g_hBSPFile ) + { + return false; + } + + // placeholder only, reset at conclusion + WriteData( &oldHeader ); + + // lumps must be reserialized in same relative offset order + // build sorted order table + int readOrder[HEADER_LUMPS]; + for ( int i=0; ilumps[lump].filelen; + if ( length ) + { + // save the lump data + int offset = g_pBSPHeader->lumps[lump].fileofs; + SetAlignedLumpPosition( lump ); + SafeWrite( g_hBSPFile, (byte *)g_pBSPHeader + offset, length ); + } + else + { + g_pBSPHeader->lumps[lump].fileofs = 0; + } + } + + // Always write the pak file at the end + // Pad out the end of the file to a sector boundary for optimal IO + g_pBSPHeader->lumps[LUMP_PAKFILE].fileofs = AlignFilePosition( g_hBSPFile, XBOX_DVD_SECTORSIZE ); + g_pBSPHeader->lumps[LUMP_PAKFILE].filelen = pakSize; + SafeWrite( g_hBSPFile, pPakData, pakSize ); + + // Pad out the end of the file to a sector boundary for optimal IO + AlignFilePosition( g_hBSPFile, XBOX_DVD_SECTORSIZE ); + + // Write the updated header + g_pFileSystem->Seek( g_hBSPFile, 0, FILESYSTEM_SEEK_HEAD ); + WriteData( g_pBSPHeader ); + g_pFileSystem->Close( g_hBSPFile ); + + CloseBSPFile(); + + return true; +} + +//----------------------------------------------------------------------------- +// Build a list of files that BSP owns, world/cubemap materials, static props, etc. +//----------------------------------------------------------------------------- +bool GetBSPDependants( const char *pBSPFilename, CUtlVector< CUtlString > *pList ) +{ + if ( !g_pFileSystem->FileExists( pBSPFilename ) ) + { + Warning( "Error! Couldn't open file %s!\n", pBSPFilename ); + return false; + } + + // must be set, but exact hdr not critical for dependant traversal + SetHDRMode( false ); + + LoadBSPFile( pBSPFilename ); + + char szBspName[MAX_PATH]; + V_FileBase( pBSPFilename, szBspName, sizeof( szBspName ) ); + V_SetExtension( szBspName, ".bsp", sizeof( szBspName ) ); + + // get embedded pak files, and internals + char szFilename[MAX_PATH]; + int fileSize; + int fileId = -1; + for ( ;; ) + { + fileId = GetPakFile()->GetNextFilename( fileId, szFilename, sizeof( szFilename ), fileSize ); + if ( fileId == -1 ) + { + break; + } + pList->AddToTail( szFilename ); + } + + // get all the world materials + for ( int i=0; iAddToTail( szFilename ); + } + + // get all the static props + GameLumpHandle_t hGameLump = g_GameLumps.GetGameLumpHandle( GAMELUMP_STATIC_PROPS ); + if ( hGameLump != g_GameLumps.InvalidGameLump() ) + { + byte *pGameLumpData = (byte *)g_GameLumps.GetGameLump( hGameLump ); + if ( pGameLumpData && g_GameLumps.GameLumpSize( hGameLump ) ) + { + int count = ((int *)pGameLumpData)[0]; + pGameLumpData += sizeof( int ); + + StaticPropDictLump_t *pStaticPropDictLump = (StaticPropDictLump_t *)pGameLumpData; + for ( int i=0; iAddToTail( pStaticPropDictLump[i].m_Name ); + } + } + } + + // get all the detail props + hGameLump = g_GameLumps.GetGameLumpHandle( GAMELUMP_DETAIL_PROPS ); + if ( hGameLump != g_GameLumps.InvalidGameLump() ) + { + byte *pGameLumpData = (byte *)g_GameLumps.GetGameLump( hGameLump ); + if ( pGameLumpData && g_GameLumps.GameLumpSize( hGameLump ) ) + { + int count = ((int *)pGameLumpData)[0]; + pGameLumpData += sizeof( int ); + + DetailObjectDictLump_t *pDetailObjectDictLump = (DetailObjectDictLump_t *)pGameLumpData; + for ( int i=0; iAddToTail( pDetailObjectDictLump[i].m_Name ); + } + pGameLumpData += count * sizeof( DetailObjectDictLump_t ); + + if ( g_GameLumps.GetGameLumpVersion( hGameLump ) == 4 ) + { + count = ((int *)pGameLumpData)[0]; + pGameLumpData += sizeof( int ); + if ( count ) + { + // All detail prop sprites must lie in the material detail/detailsprites + pList->AddToTail( "materials/detail/detailsprites.vmt" ); + } + } + } + } + + UnloadBSPFile(); + + return true; +} + diff --git a/mp/src/utils/common/bsplib.h b/mp/src/utils/common/bsplib.h index 83486e8b..80952ba9 100644 --- a/mp/src/utils/common/bsplib.h +++ b/mp/src/utils/common/bsplib.h @@ -1,404 +1,404 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// $NoKeywords: $ -//=============================================================================// - -#ifndef BSPLIB_H -#define BSPLIB_H - -#ifdef _WIN32 -#pragma once -#endif - - -#include "bspfile.h" -#include "utlvector.h" -#include "utlstring.h" -#include "utllinkedlist.h" -#include "byteswap.h" -#ifdef ENGINE_DLL -#include "zone.h" -#endif - -#ifdef ENGINE_DLL -typedef CUtlVector > CDispLightmapSamplePositions; -#else -typedef CUtlVector CDispLightmapSamplePositions; -#endif - -class ISpatialQuery; -struct Ray_t; -class Vector2D; -struct portal_t; -class CUtlBuffer; -class IZip; - -// this is only true in vrad -extern bool g_bHDR; - -// default width/height of luxels in world units. -#define DEFAULT_LUXEL_SIZE ( 16.0f ) - -#define SINGLE_BRUSH_MAP (MAX_BRUSH_LIGHTMAP_DIM_INCLUDING_BORDER*MAX_BRUSH_LIGHTMAP_DIM_INCLUDING_BORDER) -#define SINGLEMAP (MAX_LIGHTMAP_DIM_INCLUDING_BORDER*MAX_LIGHTMAP_DIM_INCLUDING_BORDER) - -struct entity_t -{ - Vector origin; - int firstbrush; - int numbrushes; - epair_t *epairs; - - // only valid for func_areaportals - int areaportalnum; - int portalareas[2]; - portal_t *m_pPortalsLeadingIntoAreas[2]; // portals leading into portalareas -}; - -extern int num_entities; -extern entity_t entities[MAX_MAP_ENTITIES]; - -extern int nummodels; -extern dmodel_t dmodels[MAX_MAP_MODELS]; - -extern int visdatasize; -extern byte dvisdata[MAX_MAP_VISIBILITY]; -extern dvis_t *dvis; - -extern CUtlVector dlightdataHDR; -extern CUtlVector dlightdataLDR; -extern CUtlVector *pdlightdata; -extern CUtlVector dentdata; - -extern int numleafs; -#if !defined( _X360 ) -extern dleaf_t dleafs[MAX_MAP_LEAFS]; -#else -extern dleaf_t *dleafs; -#endif -extern CUtlVector *g_pLeafAmbientLighting; -extern CUtlVector *g_pLeafAmbientIndex; -extern unsigned short g_LeafMinDistToWater[MAX_MAP_LEAFS]; - -extern int numplanes; -extern dplane_t dplanes[MAX_MAP_PLANES]; - -extern int numvertexes; -extern dvertex_t dvertexes[MAX_MAP_VERTS]; - -extern int g_numvertnormalindices; // dfaces reference these. These index g_vertnormals. -extern unsigned short g_vertnormalindices[MAX_MAP_VERTNORMALS]; - -extern int g_numvertnormals; -extern Vector g_vertnormals[MAX_MAP_VERTNORMALS]; - -extern int numnodes; -extern dnode_t dnodes[MAX_MAP_NODES]; - -extern CUtlVector texinfo; - -extern int numtexdata; -extern dtexdata_t dtexdata[MAX_MAP_TEXDATA]; - -// displacement map .bsp file info -extern CUtlVector g_dispinfo; -extern CUtlVector g_DispVerts; -extern CUtlVector g_DispTris; -extern CDispLightmapSamplePositions g_DispLightmapSamplePositions; // LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS - -extern int numorigfaces; -extern dface_t dorigfaces[MAX_MAP_FACES]; - -extern int g_numprimitives; -extern dprimitive_t g_primitives[MAX_MAP_PRIMITIVES]; - -extern int g_numprimverts; -extern dprimvert_t g_primverts[MAX_MAP_PRIMVERTS]; - -extern int g_numprimindices; -extern unsigned short g_primindices[MAX_MAP_PRIMINDICES]; - -extern int numfaces; -extern dface_t dfaces[MAX_MAP_FACES]; - -extern int numfaceids; -extern CUtlVector dfaceids; - -extern int numfaces_hdr; -extern dface_t dfaces_hdr[MAX_MAP_FACES]; - -extern int numedges; -extern dedge_t dedges[MAX_MAP_EDGES]; - -extern int numleaffaces; -extern unsigned short dleaffaces[MAX_MAP_LEAFFACES]; - -extern int numleafbrushes; -extern unsigned short dleafbrushes[MAX_MAP_LEAFBRUSHES]; - -extern int numsurfedges; -extern int dsurfedges[MAX_MAP_SURFEDGES]; - -extern int numareas; -extern darea_t dareas[MAX_MAP_AREAS]; - -extern int numareaportals; -extern dareaportal_t dareaportals[MAX_MAP_AREAPORTALS]; - -extern int numbrushes; -extern dbrush_t dbrushes[MAX_MAP_BRUSHES]; - -extern int numbrushsides; -extern dbrushside_t dbrushsides[MAX_MAP_BRUSHSIDES]; - -extern int *pNumworldlights; -extern dworldlight_t *dworldlights; - -extern Vector g_ClipPortalVerts[MAX_MAP_PORTALVERTS]; -extern int g_nClipPortalVerts; - -extern dcubemapsample_t g_CubemapSamples[MAX_MAP_CUBEMAPSAMPLES]; -extern int g_nCubemapSamples; - -extern int g_nOverlayCount; -extern doverlay_t g_Overlays[MAX_MAP_OVERLAYS]; -extern doverlayfade_t g_OverlayFades[MAX_MAP_OVERLAYS]; // Parallel array of fade info in a separate lump to avoid breaking backwards compat - -extern int g_nWaterOverlayCount; -extern dwateroverlay_t g_WaterOverlays[MAX_MAP_WATEROVERLAYS]; - -extern CUtlVector g_TexDataStringData; -extern CUtlVector g_TexDataStringTable; - -extern int numleafwaterdata; -extern dleafwaterdata_t dleafwaterdata[MAX_MAP_LEAFWATERDATA]; - -extern CUtlVector g_FaceMacroTextureInfos; - -extern CUtlVector g_OccluderData; -extern CUtlVector g_OccluderPolyData; -extern CUtlVector g_OccluderVertexIndices; - -// level flags - see LVLFLAGS_xxx in bspfile.h -extern uint32 g_LevelFlags; - -// physics collision data -extern byte *g_pPhysCollide; -extern int g_PhysCollideSize; -extern byte *g_pPhysDisp; -extern int g_PhysDispSize; - -// Embedded pack/pak file -IZip *GetPakFile( void ); -IZip *GetSwapPakFile( void ); -void ClearPakFile( IZip *pak ); -void AddFileToPak( IZip *pak, const char *pRelativeName, const char *fullpath ); -void AddBufferToPak( IZip *pak, const char *pRelativeName, void *data, int length, bool bTextMode ); -bool FileExistsInPak( IZip *pak, const char *pRelativeName ); -bool ReadFileFromPak( IZip *pak, const char *pRelativeName, bool bTextMode, CUtlBuffer &buf ); -void RemoveFileFromPak( IZip *pak, const char *pRelativeName ); -int GetNextFilename( IZip *pak, int id, char *pBuffer, int bufferSize, int &fileSize ); -void ForceAlignment( IZip *pak, bool bAlign, bool bCompatibleFormat, unsigned int alignmentSize ); - -typedef bool (*CompressFunc_t)( CUtlBuffer &inputBuffer, CUtlBuffer &outputBuffer ); -typedef bool (*VTFConvertFunc_t)( const char *pDebugName, CUtlBuffer &sourceBuf, CUtlBuffer &targetBuf, CompressFunc_t pCompressFunc ); -typedef bool (*VHVFixupFunc_t)( const char *pVhvFilename, const char *pModelName, CUtlBuffer &sourceBuf, CUtlBuffer &targetBuf ); - -//----------------------------------------------------------------------------- -// Game lump memory storage -//----------------------------------------------------------------------------- -// NOTE: This is not optimal at all; since I expect client lumps to -// not be accessed all that often. - -struct GameLump_t -{ - GameLumpId_t m_Id; - unsigned short m_Flags; - unsigned short m_Version; - CUtlMemory< unsigned char > m_Memory; -}; - -//----------------------------------------------------------------------------- -// Handle to a game lump -//----------------------------------------------------------------------------- -typedef unsigned short GameLumpHandle_t; - -class CGameLump -{ -public: - //----------------------------------------------------------------------------- - // Convert four-CC code to a handle + back - //----------------------------------------------------------------------------- - GameLumpHandle_t GetGameLumpHandle( GameLumpId_t id ); - GameLumpId_t GetGameLumpId( GameLumpHandle_t handle ); - int GetGameLumpFlags( GameLumpHandle_t handle ); - int GetGameLumpVersion( GameLumpHandle_t handle ); - void ComputeGameLumpSizeAndCount( int& size, int& clumpCount ); - void ParseGameLump( dheader_t* pHeader ); - void SwapGameLump( GameLumpId_t id, int version, byte *dest, byte *src, int size ); - - - //----------------------------------------------------------------------------- - // Game lump accessor methods - //----------------------------------------------------------------------------- - void* GetGameLump( GameLumpHandle_t handle ); - int GameLumpSize( GameLumpHandle_t handle ); - - - //----------------------------------------------------------------------------- - // Game lump iteration methods - //----------------------------------------------------------------------------- - GameLumpHandle_t FirstGameLump(); - GameLumpHandle_t NextGameLump( GameLumpHandle_t handle ); - GameLumpHandle_t InvalidGameLump(); - - - //----------------------------------------------------------------------------- - // Game lump creation/destruction method - //----------------------------------------------------------------------------- - GameLumpHandle_t CreateGameLump( GameLumpId_t id, int size, int flags, int version ); - void DestroyGameLump( GameLumpHandle_t handle ); - void DestroyAllGameLumps(); - -private: - CUtlLinkedList< GameLump_t, GameLumpHandle_t > m_GameLumps; -}; - -extern CGameLump g_GameLumps; -extern CByteswap g_Swap; - -//----------------------------------------------------------------------------- -// Helper for the bspzip tool -//----------------------------------------------------------------------------- -void ExtractZipFileFromBSP( char *pBSPFileName, char *pZipFileName ); - - -//----------------------------------------------------------------------------- -// String table methods -//----------------------------------------------------------------------------- -const char * TexDataStringTable_GetString( int stringID ); -int TexDataStringTable_AddOrFindString( const char *pString ); - -void DecompressVis (byte *in, byte *decompressed); -int CompressVis (byte *vis, byte *dest); - -void OpenBSPFile( const char *filename ); -void CloseBSPFile(void); -void LoadBSPFile( const char *filename ); -void LoadBSPFile_FileSystemOnly( const char *filename ); -void LoadBSPFileTexinfo( const char *filename ); -void WriteBSPFile( const char *filename, char *pUnused = NULL ); -void PrintBSPFileSizes(void); -void PrintBSPPackDirectory(void); -void ReleasePakFileLumps(void); -bool SwapBSPFile( const char *filename, const char *swapFilename, bool bSwapOnLoad, VTFConvertFunc_t pVTFConvertFunc, VHVFixupFunc_t pVHVFixupFunc, CompressFunc_t pCompressFunc ); -bool GetPakFileLump( const char *pBSPFilename, void **pPakData, int *pPakSize ); -bool SetPakFileLump( const char *pBSPFilename, const char *pNewFilename, void *pPakData, int pakSize ); -void WriteLumpToFile( char *filename, int lump ); -void WriteLumpToFile( char *filename, int lump, int nLumpVersion, void *pBuffer, size_t nBufLen ); -bool GetBSPDependants( const char *pBSPFilename, CUtlVector< CUtlString > *pList ); -void UnloadBSPFile(); - -void ParseEntities (void); -void UnparseEntities (void); -void PrintEntity (entity_t *ent); - -void SetKeyValue (entity_t *ent, const char *key, const char *value); -char *ValueForKey (entity_t *ent, char *key); -// will return "" if not present -int IntForKey (entity_t *ent, char *key); -int IntForKeyWithDefault(entity_t *ent, char *key, int nDefault ); -vec_t FloatForKey (entity_t *ent, char *key); -vec_t FloatForKeyWithDefault (entity_t *ent, char *key, float default_value); -void GetVectorForKey (entity_t *ent, char *key, Vector& vec); -void GetVector2DForKey (entity_t *ent, char *key, Vector2D& vec); -void GetAnglesForKey (entity_t *ent, char *key, QAngle& vec); -epair_t *ParseEpair (void); -void StripTrailing (char *e); - -// Build a list of the face's vertices (index into dvertexes). -// points must be able to hold pFace->numedges indices. -void BuildFaceCalcWindingData( dface_t *pFace, int *points ); - -// Convert a tristrip to a trilist. -// Removes degenerates. -// Fills in pTriListIndices and pnTriListIndices. -// You must free pTriListIndices with delete[]. -void TriStripToTriList( - unsigned short const *pTriStripIndices, - int nTriStripIndices, - unsigned short **pTriListIndices, - int *pnTriListIndices ); - -// Calculates the lightmap coordinates at a given set of positions given the -// lightmap basis information. -void CalcTextureCoordsAtPoints( - float const texelsPerWorldUnits[2][4], - int const subtractOffset[2], - Vector const *pPoints, - int const nPoints, - Vector2D *pCoords ); - -// Figure out lightmap extents on all (lit) faces. -void UpdateAllFaceLightmapExtents(); - - -//----------------------------------------------------------------------------- -// Gets at an interface for the tree for enumeration of leaves in volumes. -//----------------------------------------------------------------------------- -ISpatialQuery* ToolBSPTree(); - -class IBSPNodeEnumerator -{ -public: - // call back with a node and a context - virtual bool EnumerateNode( int node, Ray_t const& ray, float f, int context ) = 0; - - // call back with a leaf and a context - virtual bool EnumerateLeaf( int leaf, Ray_t const& ray, float start, float end, int context ) = 0; -}; - -//----------------------------------------------------------------------------- -// Enumerates nodes + leafs in front to back order... -//----------------------------------------------------------------------------- -bool EnumerateNodesAlongRay( Ray_t const& ray, IBSPNodeEnumerator* pEnum, int context ); - - -//----------------------------------------------------------------------------- -// Helps us find all leaves associated with a particular cluster -//----------------------------------------------------------------------------- -struct clusterlist_t -{ - int leafCount; - CUtlVector leafs; -}; - -extern CUtlVector g_ClusterLeaves; - -// Call this to build the mapping from cluster to leaves -void BuildClusterTable( ); - -void GetPlatformMapPath( const char *pMapPath, char *pPlatformMapPath, int dxlevel, int maxLength ); - -void SetHDRMode( bool bHDR ); - -// ----------------------------------------------------------------------------- // -// Helper accessors for the various structures. -// ----------------------------------------------------------------------------- // - -inline ColorRGBExp32* dface_AvgLightColor( dface_t *pFace, int nLightStyleIndex ) -{ - return (ColorRGBExp32*)&(*pdlightdata)[pFace->lightofs - (nLightStyleIndex+1) * 4]; -} - -inline const char* TexInfo_TexName( int iTexInfo ) -{ - return TexDataStringTable_GetString( dtexdata[texinfo[iTexInfo].texdata].nameStringTableID ); -} - - -#endif // BSPLIB_H +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//=============================================================================// + +#ifndef BSPLIB_H +#define BSPLIB_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "bspfile.h" +#include "utlvector.h" +#include "utlstring.h" +#include "utllinkedlist.h" +#include "byteswap.h" +#ifdef ENGINE_DLL +#include "zone.h" +#endif + +#ifdef ENGINE_DLL +typedef CUtlVector > CDispLightmapSamplePositions; +#else +typedef CUtlVector CDispLightmapSamplePositions; +#endif + +class ISpatialQuery; +struct Ray_t; +class Vector2D; +struct portal_t; +class CUtlBuffer; +class IZip; + +// this is only true in vrad +extern bool g_bHDR; + +// default width/height of luxels in world units. +#define DEFAULT_LUXEL_SIZE ( 16.0f ) + +#define SINGLE_BRUSH_MAP (MAX_BRUSH_LIGHTMAP_DIM_INCLUDING_BORDER*MAX_BRUSH_LIGHTMAP_DIM_INCLUDING_BORDER) +#define SINGLEMAP (MAX_LIGHTMAP_DIM_INCLUDING_BORDER*MAX_LIGHTMAP_DIM_INCLUDING_BORDER) + +struct entity_t +{ + Vector origin; + int firstbrush; + int numbrushes; + epair_t *epairs; + + // only valid for func_areaportals + int areaportalnum; + int portalareas[2]; + portal_t *m_pPortalsLeadingIntoAreas[2]; // portals leading into portalareas +}; + +extern int num_entities; +extern entity_t entities[MAX_MAP_ENTITIES]; + +extern int nummodels; +extern dmodel_t dmodels[MAX_MAP_MODELS]; + +extern int visdatasize; +extern byte dvisdata[MAX_MAP_VISIBILITY]; +extern dvis_t *dvis; + +extern CUtlVector dlightdataHDR; +extern CUtlVector dlightdataLDR; +extern CUtlVector *pdlightdata; +extern CUtlVector dentdata; + +extern int numleafs; +#if !defined( _X360 ) +extern dleaf_t dleafs[MAX_MAP_LEAFS]; +#else +extern dleaf_t *dleafs; +#endif +extern CUtlVector *g_pLeafAmbientLighting; +extern CUtlVector *g_pLeafAmbientIndex; +extern unsigned short g_LeafMinDistToWater[MAX_MAP_LEAFS]; + +extern int numplanes; +extern dplane_t dplanes[MAX_MAP_PLANES]; + +extern int numvertexes; +extern dvertex_t dvertexes[MAX_MAP_VERTS]; + +extern int g_numvertnormalindices; // dfaces reference these. These index g_vertnormals. +extern unsigned short g_vertnormalindices[MAX_MAP_VERTNORMALS]; + +extern int g_numvertnormals; +extern Vector g_vertnormals[MAX_MAP_VERTNORMALS]; + +extern int numnodes; +extern dnode_t dnodes[MAX_MAP_NODES]; + +extern CUtlVector texinfo; + +extern int numtexdata; +extern dtexdata_t dtexdata[MAX_MAP_TEXDATA]; + +// displacement map .bsp file info +extern CUtlVector g_dispinfo; +extern CUtlVector g_DispVerts; +extern CUtlVector g_DispTris; +extern CDispLightmapSamplePositions g_DispLightmapSamplePositions; // LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS + +extern int numorigfaces; +extern dface_t dorigfaces[MAX_MAP_FACES]; + +extern int g_numprimitives; +extern dprimitive_t g_primitives[MAX_MAP_PRIMITIVES]; + +extern int g_numprimverts; +extern dprimvert_t g_primverts[MAX_MAP_PRIMVERTS]; + +extern int g_numprimindices; +extern unsigned short g_primindices[MAX_MAP_PRIMINDICES]; + +extern int numfaces; +extern dface_t dfaces[MAX_MAP_FACES]; + +extern int numfaceids; +extern CUtlVector dfaceids; + +extern int numfaces_hdr; +extern dface_t dfaces_hdr[MAX_MAP_FACES]; + +extern int numedges; +extern dedge_t dedges[MAX_MAP_EDGES]; + +extern int numleaffaces; +extern unsigned short dleaffaces[MAX_MAP_LEAFFACES]; + +extern int numleafbrushes; +extern unsigned short dleafbrushes[MAX_MAP_LEAFBRUSHES]; + +extern int numsurfedges; +extern int dsurfedges[MAX_MAP_SURFEDGES]; + +extern int numareas; +extern darea_t dareas[MAX_MAP_AREAS]; + +extern int numareaportals; +extern dareaportal_t dareaportals[MAX_MAP_AREAPORTALS]; + +extern int numbrushes; +extern dbrush_t dbrushes[MAX_MAP_BRUSHES]; + +extern int numbrushsides; +extern dbrushside_t dbrushsides[MAX_MAP_BRUSHSIDES]; + +extern int *pNumworldlights; +extern dworldlight_t *dworldlights; + +extern Vector g_ClipPortalVerts[MAX_MAP_PORTALVERTS]; +extern int g_nClipPortalVerts; + +extern dcubemapsample_t g_CubemapSamples[MAX_MAP_CUBEMAPSAMPLES]; +extern int g_nCubemapSamples; + +extern int g_nOverlayCount; +extern doverlay_t g_Overlays[MAX_MAP_OVERLAYS]; +extern doverlayfade_t g_OverlayFades[MAX_MAP_OVERLAYS]; // Parallel array of fade info in a separate lump to avoid breaking backwards compat + +extern int g_nWaterOverlayCount; +extern dwateroverlay_t g_WaterOverlays[MAX_MAP_WATEROVERLAYS]; + +extern CUtlVector g_TexDataStringData; +extern CUtlVector g_TexDataStringTable; + +extern int numleafwaterdata; +extern dleafwaterdata_t dleafwaterdata[MAX_MAP_LEAFWATERDATA]; + +extern CUtlVector g_FaceMacroTextureInfos; + +extern CUtlVector g_OccluderData; +extern CUtlVector g_OccluderPolyData; +extern CUtlVector g_OccluderVertexIndices; + +// level flags - see LVLFLAGS_xxx in bspfile.h +extern uint32 g_LevelFlags; + +// physics collision data +extern byte *g_pPhysCollide; +extern int g_PhysCollideSize; +extern byte *g_pPhysDisp; +extern int g_PhysDispSize; + +// Embedded pack/pak file +IZip *GetPakFile( void ); +IZip *GetSwapPakFile( void ); +void ClearPakFile( IZip *pak ); +void AddFileToPak( IZip *pak, const char *pRelativeName, const char *fullpath ); +void AddBufferToPak( IZip *pak, const char *pRelativeName, void *data, int length, bool bTextMode ); +bool FileExistsInPak( IZip *pak, const char *pRelativeName ); +bool ReadFileFromPak( IZip *pak, const char *pRelativeName, bool bTextMode, CUtlBuffer &buf ); +void RemoveFileFromPak( IZip *pak, const char *pRelativeName ); +int GetNextFilename( IZip *pak, int id, char *pBuffer, int bufferSize, int &fileSize ); +void ForceAlignment( IZip *pak, bool bAlign, bool bCompatibleFormat, unsigned int alignmentSize ); + +typedef bool (*CompressFunc_t)( CUtlBuffer &inputBuffer, CUtlBuffer &outputBuffer ); +typedef bool (*VTFConvertFunc_t)( const char *pDebugName, CUtlBuffer &sourceBuf, CUtlBuffer &targetBuf, CompressFunc_t pCompressFunc ); +typedef bool (*VHVFixupFunc_t)( const char *pVhvFilename, const char *pModelName, CUtlBuffer &sourceBuf, CUtlBuffer &targetBuf ); + +//----------------------------------------------------------------------------- +// Game lump memory storage +//----------------------------------------------------------------------------- +// NOTE: This is not optimal at all; since I expect client lumps to +// not be accessed all that often. + +struct GameLump_t +{ + GameLumpId_t m_Id; + unsigned short m_Flags; + unsigned short m_Version; + CUtlMemory< unsigned char > m_Memory; +}; + +//----------------------------------------------------------------------------- +// Handle to a game lump +//----------------------------------------------------------------------------- +typedef unsigned short GameLumpHandle_t; + +class CGameLump +{ +public: + //----------------------------------------------------------------------------- + // Convert four-CC code to a handle + back + //----------------------------------------------------------------------------- + GameLumpHandle_t GetGameLumpHandle( GameLumpId_t id ); + GameLumpId_t GetGameLumpId( GameLumpHandle_t handle ); + int GetGameLumpFlags( GameLumpHandle_t handle ); + int GetGameLumpVersion( GameLumpHandle_t handle ); + void ComputeGameLumpSizeAndCount( int& size, int& clumpCount ); + void ParseGameLump( dheader_t* pHeader ); + void SwapGameLump( GameLumpId_t id, int version, byte *dest, byte *src, int size ); + + + //----------------------------------------------------------------------------- + // Game lump accessor methods + //----------------------------------------------------------------------------- + void* GetGameLump( GameLumpHandle_t handle ); + int GameLumpSize( GameLumpHandle_t handle ); + + + //----------------------------------------------------------------------------- + // Game lump iteration methods + //----------------------------------------------------------------------------- + GameLumpHandle_t FirstGameLump(); + GameLumpHandle_t NextGameLump( GameLumpHandle_t handle ); + GameLumpHandle_t InvalidGameLump(); + + + //----------------------------------------------------------------------------- + // Game lump creation/destruction method + //----------------------------------------------------------------------------- + GameLumpHandle_t CreateGameLump( GameLumpId_t id, int size, int flags, int version ); + void DestroyGameLump( GameLumpHandle_t handle ); + void DestroyAllGameLumps(); + +private: + CUtlLinkedList< GameLump_t, GameLumpHandle_t > m_GameLumps; +}; + +extern CGameLump g_GameLumps; +extern CByteswap g_Swap; + +//----------------------------------------------------------------------------- +// Helper for the bspzip tool +//----------------------------------------------------------------------------- +void ExtractZipFileFromBSP( char *pBSPFileName, char *pZipFileName ); + + +//----------------------------------------------------------------------------- +// String table methods +//----------------------------------------------------------------------------- +const char * TexDataStringTable_GetString( int stringID ); +int TexDataStringTable_AddOrFindString( const char *pString ); + +void DecompressVis (byte *in, byte *decompressed); +int CompressVis (byte *vis, byte *dest); + +void OpenBSPFile( const char *filename ); +void CloseBSPFile(void); +void LoadBSPFile( const char *filename ); +void LoadBSPFile_FileSystemOnly( const char *filename ); +void LoadBSPFileTexinfo( const char *filename ); +void WriteBSPFile( const char *filename, char *pUnused = NULL ); +void PrintBSPFileSizes(void); +void PrintBSPPackDirectory(void); +void ReleasePakFileLumps(void); +bool SwapBSPFile( const char *filename, const char *swapFilename, bool bSwapOnLoad, VTFConvertFunc_t pVTFConvertFunc, VHVFixupFunc_t pVHVFixupFunc, CompressFunc_t pCompressFunc ); +bool GetPakFileLump( const char *pBSPFilename, void **pPakData, int *pPakSize ); +bool SetPakFileLump( const char *pBSPFilename, const char *pNewFilename, void *pPakData, int pakSize ); +void WriteLumpToFile( char *filename, int lump ); +void WriteLumpToFile( char *filename, int lump, int nLumpVersion, void *pBuffer, size_t nBufLen ); +bool GetBSPDependants( const char *pBSPFilename, CUtlVector< CUtlString > *pList ); +void UnloadBSPFile(); + +void ParseEntities (void); +void UnparseEntities (void); +void PrintEntity (entity_t *ent); + +void SetKeyValue (entity_t *ent, const char *key, const char *value); +char *ValueForKey (entity_t *ent, char *key); +// will return "" if not present +int IntForKey (entity_t *ent, char *key); +int IntForKeyWithDefault(entity_t *ent, char *key, int nDefault ); +vec_t FloatForKey (entity_t *ent, char *key); +vec_t FloatForKeyWithDefault (entity_t *ent, char *key, float default_value); +void GetVectorForKey (entity_t *ent, char *key, Vector& vec); +void GetVector2DForKey (entity_t *ent, char *key, Vector2D& vec); +void GetAnglesForKey (entity_t *ent, char *key, QAngle& vec); +epair_t *ParseEpair (void); +void StripTrailing (char *e); + +// Build a list of the face's vertices (index into dvertexes). +// points must be able to hold pFace->numedges indices. +void BuildFaceCalcWindingData( dface_t *pFace, int *points ); + +// Convert a tristrip to a trilist. +// Removes degenerates. +// Fills in pTriListIndices and pnTriListIndices. +// You must free pTriListIndices with delete[]. +void TriStripToTriList( + unsigned short const *pTriStripIndices, + int nTriStripIndices, + unsigned short **pTriListIndices, + int *pnTriListIndices ); + +// Calculates the lightmap coordinates at a given set of positions given the +// lightmap basis information. +void CalcTextureCoordsAtPoints( + float const texelsPerWorldUnits[2][4], + int const subtractOffset[2], + Vector const *pPoints, + int const nPoints, + Vector2D *pCoords ); + +// Figure out lightmap extents on all (lit) faces. +void UpdateAllFaceLightmapExtents(); + + +//----------------------------------------------------------------------------- +// Gets at an interface for the tree for enumeration of leaves in volumes. +//----------------------------------------------------------------------------- +ISpatialQuery* ToolBSPTree(); + +class IBSPNodeEnumerator +{ +public: + // call back with a node and a context + virtual bool EnumerateNode( int node, Ray_t const& ray, float f, int context ) = 0; + + // call back with a leaf and a context + virtual bool EnumerateLeaf( int leaf, Ray_t const& ray, float start, float end, int context ) = 0; +}; + +//----------------------------------------------------------------------------- +// Enumerates nodes + leafs in front to back order... +//----------------------------------------------------------------------------- +bool EnumerateNodesAlongRay( Ray_t const& ray, IBSPNodeEnumerator* pEnum, int context ); + + +//----------------------------------------------------------------------------- +// Helps us find all leaves associated with a particular cluster +//----------------------------------------------------------------------------- +struct clusterlist_t +{ + int leafCount; + CUtlVector leafs; +}; + +extern CUtlVector g_ClusterLeaves; + +// Call this to build the mapping from cluster to leaves +void BuildClusterTable( ); + +void GetPlatformMapPath( const char *pMapPath, char *pPlatformMapPath, int dxlevel, int maxLength ); + +void SetHDRMode( bool bHDR ); + +// ----------------------------------------------------------------------------- // +// Helper accessors for the various structures. +// ----------------------------------------------------------------------------- // + +inline ColorRGBExp32* dface_AvgLightColor( dface_t *pFace, int nLightStyleIndex ) +{ + return (ColorRGBExp32*)&(*pdlightdata)[pFace->lightofs - (nLightStyleIndex+1) * 4]; +} + +inline const char* TexInfo_TexName( int iTexInfo ) +{ + return TexDataStringTable_GetString( dtexdata[texinfo[iTexInfo].texdata].nameStringTableID ); +} + + +#endif // BSPLIB_H diff --git a/mp/src/utils/common/cmdlib.cpp b/mp/src/utils/common/cmdlib.cpp index a6962380..a5f9b747 100644 --- a/mp/src/utils/common/cmdlib.cpp +++ b/mp/src/utils/common/cmdlib.cpp @@ -1,1007 +1,1007 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -// -//=============================================================================// -// ----------------------- -// cmdlib.c -// ----------------------- -#include "tier0/platform.h" -#ifdef IS_WINDOWS_PC -#include -#endif -#include "cmdlib.h" -#include -#include -#include "tier1/strtools.h" -#ifdef _WIN32 -#include -#endif -#include "utlvector.h" -#include "filesystem_helpers.h" -#include "utllinkedlist.h" -#include "tier0/icommandline.h" -#include "KeyValues.h" -#include "filesystem_tools.h" - -#if defined( MPI ) - - #include "vmpi.h" - #include "vmpi_tools_shared.h" - -#endif - - -#if defined( _WIN32 ) || defined( WIN32 ) -#include -#endif - -#if defined( _X360 ) -#include "xbox/xbox_win32stubs.h" -#endif - -// set these before calling CheckParm -int myargc; -char **myargv; - -char com_token[1024]; - -qboolean archive; -char archivedir[1024]; - -FileHandle_t g_pLogFile = 0; - -CUtlLinkedList g_CleanupFunctions; -CUtlLinkedList g_ExtraSpewHooks; - -bool g_bStopOnExit = false; -void (*g_ExtraSpewHook)(const char*) = NULL; - -#if defined( _WIN32 ) || defined( WIN32 ) - -void CmdLib_FPrintf( FileHandle_t hFile, const char *pFormat, ... ) -{ - static CUtlVector buf; - if ( buf.Count() == 0 ) - buf.SetCount( 1024 ); - - va_list marker; - va_start( marker, pFormat ); - - while ( 1 ) - { - int ret = Q_vsnprintf( buf.Base(), buf.Count(), pFormat, marker ); - if ( ret >= 0 ) - { - // Write the string. - g_pFileSystem->Write( buf.Base(), ret, hFile ); - - break; - } - else - { - // Make the buffer larger. - int newSize = buf.Count() * 2; - buf.SetCount( newSize ); - if ( buf.Count() != newSize ) - { - Error( "CmdLib_FPrintf: can't allocate space for text." ); - } - } - } - - va_end( marker ); -} - -char* CmdLib_FGets( char *pOut, int outSize, FileHandle_t hFile ) -{ - int iCur=0; - for ( ; iCur < (outSize-1); iCur++ ) - { - char c; - if ( !g_pFileSystem->Read( &c, 1, hFile ) ) - { - if ( iCur == 0 ) - return NULL; - else - break; - } - - pOut[iCur] = c; - if ( c == '\n' ) - break; - - if ( c == EOF ) - { - if ( iCur == 0 ) - return NULL; - else - break; - } - } - - pOut[iCur] = 0; - return pOut; -} - -#if !defined( _X360 ) -#include -#endif - -// This pauses before exiting if they use -StopOnExit. Useful for debugging. -class CExitStopper -{ -public: - ~CExitStopper() - { - if ( g_bStopOnExit ) - { - Warning( "\nPress any key to quit.\n" ); - getch(); - } - } -} g_ExitStopper; - - -static unsigned short g_InitialColor = 0xFFFF; -static unsigned short g_LastColor = 0xFFFF; -static unsigned short g_BadColor = 0xFFFF; -static WORD g_BackgroundFlags = 0xFFFF; -static void GetInitialColors( ) -{ -#if !defined( _X360 ) - // Get the old background attributes. - CONSOLE_SCREEN_BUFFER_INFO oldInfo; - GetConsoleScreenBufferInfo( GetStdHandle( STD_OUTPUT_HANDLE ), &oldInfo ); - g_InitialColor = g_LastColor = oldInfo.wAttributes & (FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_INTENSITY); - g_BackgroundFlags = oldInfo.wAttributes & (BACKGROUND_RED|BACKGROUND_GREEN|BACKGROUND_BLUE|BACKGROUND_INTENSITY); - - g_BadColor = 0; - if (g_BackgroundFlags & BACKGROUND_RED) - g_BadColor |= FOREGROUND_RED; - if (g_BackgroundFlags & BACKGROUND_GREEN) - g_BadColor |= FOREGROUND_GREEN; - if (g_BackgroundFlags & BACKGROUND_BLUE) - g_BadColor |= FOREGROUND_BLUE; - if (g_BackgroundFlags & BACKGROUND_INTENSITY) - g_BadColor |= FOREGROUND_INTENSITY; -#endif -} - -WORD SetConsoleTextColor( int red, int green, int blue, int intensity ) -{ - WORD ret = g_LastColor; -#if !defined( _X360 ) - - g_LastColor = 0; - if( red ) g_LastColor |= FOREGROUND_RED; - if( green ) g_LastColor |= FOREGROUND_GREEN; - if( blue ) g_LastColor |= FOREGROUND_BLUE; - if( intensity ) g_LastColor |= FOREGROUND_INTENSITY; - - // Just use the initial color if there's a match... - if (g_LastColor == g_BadColor) - g_LastColor = g_InitialColor; - - SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), g_LastColor | g_BackgroundFlags ); -#endif - return ret; -} - -void RestoreConsoleTextColor( WORD color ) -{ -#if !defined( _X360 ) - SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), color | g_BackgroundFlags ); - g_LastColor = color; -#endif -} - - -#if defined( CMDLIB_NODBGLIB ) - -// This can go away when everything is in bin. -void Error( char const *pMsg, ... ) -{ - va_list marker; - va_start( marker, pMsg ); - vprintf( pMsg, marker ); - va_end( marker ); - - exit( -1 ); -} - -#else - -CRITICAL_SECTION g_SpewCS; -bool g_bSpewCSInitted = false; -bool g_bSuppressPrintfOutput = false; - -SpewRetval_t CmdLib_SpewOutputFunc( SpewType_t type, char const *pMsg ) -{ - // Hopefully two threads won't call this simultaneously right at the start! - if ( !g_bSpewCSInitted ) - { - InitializeCriticalSection( &g_SpewCS ); - g_bSpewCSInitted = true; - } - - WORD old; - SpewRetval_t retVal; - - EnterCriticalSection( &g_SpewCS ); - { - if (( type == SPEW_MESSAGE ) || (type == SPEW_LOG )) - { - Color c = *GetSpewOutputColor(); - if ( c.r() != 255 || c.g() != 255 || c.b() != 255 ) - { - // custom color - old = SetConsoleTextColor( c.r(), c.g(), c.b(), c.a() ); - } - else - { - old = SetConsoleTextColor( 1, 1, 1, 0 ); - } - retVal = SPEW_CONTINUE; - } - else if( type == SPEW_WARNING ) - { - old = SetConsoleTextColor( 1, 1, 0, 1 ); - retVal = SPEW_CONTINUE; - } - else if( type == SPEW_ASSERT ) - { - old = SetConsoleTextColor( 1, 0, 0, 1 ); - retVal = SPEW_DEBUGGER; - -#ifdef MPI - // VMPI workers don't want to bring up dialogs and suchlike. - // They need to have a special function installed to handle - // the exceptions and write the minidumps. - // Install the function after VMPI_Init with a call: - // SetupToolsMinidumpHandler( VMPI_ExceptionFilter ); - if ( g_bUseMPI && !g_bMPIMaster && !Plat_IsInDebugSession() ) - { - // Generating an exception and letting the - // installed handler handle it - ::RaiseException - ( - 0, // dwExceptionCode - EXCEPTION_NONCONTINUABLE, // dwExceptionFlags - 0, // nNumberOfArguments, - NULL // const ULONG_PTR* lpArguments - ); - - // Never get here (non-continuable exception) - - VMPI_HandleCrash( pMsg, NULL, true ); - exit( 0 ); - } -#endif - } - else if( type == SPEW_ERROR ) - { - old = SetConsoleTextColor( 1, 0, 0, 1 ); - retVal = SPEW_ABORT; // doesn't matter.. we exit below so we can return an errorlevel (which dbg.dll doesn't do). - } - else - { - old = SetConsoleTextColor( 1, 1, 1, 1 ); - retVal = SPEW_CONTINUE; - } - - if ( !g_bSuppressPrintfOutput || type == SPEW_ERROR ) - printf( "%s", pMsg ); - - OutputDebugString( pMsg ); - - if ( type == SPEW_ERROR ) - { - printf( "\n" ); - OutputDebugString( "\n" ); - } - - if( g_pLogFile ) - { - CmdLib_FPrintf( g_pLogFile, "%s", pMsg ); - g_pFileSystem->Flush( g_pLogFile ); - } - - // Dispatch to other spew hooks. - FOR_EACH_LL( g_ExtraSpewHooks, i ) - g_ExtraSpewHooks[i]( pMsg ); - - RestoreConsoleTextColor( old ); - } - LeaveCriticalSection( &g_SpewCS ); - - if ( type == SPEW_ERROR ) - { - CmdLib_Exit( 1 ); - } - - return retVal; -} - - -void InstallSpewFunction() -{ - setvbuf( stdout, NULL, _IONBF, 0 ); - setvbuf( stderr, NULL, _IONBF, 0 ); - - SpewOutputFunc( CmdLib_SpewOutputFunc ); - GetInitialColors(); -} - - -void InstallExtraSpewHook( SpewHookFn pFn ) -{ - g_ExtraSpewHooks.AddToTail( pFn ); -} - -#if 0 -void CmdLib_AllocError( unsigned long size ) -{ - Error( "Error trying to allocate %d bytes.\n", size ); -} - - -int CmdLib_NewHandler( size_t size ) -{ - CmdLib_AllocError( size ); - return 0; -} -#endif - -void InstallAllocationFunctions() -{ -// _set_new_mode( 1 ); // so if malloc() fails, we exit. -// _set_new_handler( CmdLib_NewHandler ); -} - -void SetSpewFunctionLogFile( char const *pFilename ) -{ - Assert( (!g_pLogFile) ); - g_pLogFile = g_pFileSystem->Open( pFilename, "a" ); - - Assert( g_pLogFile ); - if (!g_pLogFile) - Error("Can't create LogFile:\"%s\"\n", pFilename ); - - CmdLib_FPrintf( g_pLogFile, "\n\n\n" ); -} - - -void CloseSpewFunctionLogFile() -{ - if ( g_pFileSystem && g_pLogFile ) - { - g_pFileSystem->Close( g_pLogFile ); - g_pLogFile = FILESYSTEM_INVALID_HANDLE; - } -} - - -void CmdLib_AtCleanup( CleanupFn pFn ) -{ - g_CleanupFunctions.AddToTail( pFn ); -} - - -void CmdLib_Cleanup() -{ - CloseSpewFunctionLogFile(); - - CmdLib_TermFileSystem(); - - FOR_EACH_LL( g_CleanupFunctions, i ) - g_CleanupFunctions[i](); - -#if defined( MPI ) - // Unfortunately, when you call exit(), even if you have things registered with atexit(), - // threads go into a seemingly undefined state where GetExitCodeThread gives STILL_ACTIVE - // and WaitForSingleObject will stall forever on the thread. Because of this, we must cleanup - // everything that uses threads before exiting. - VMPI_Finalize(); -#endif -} - - -void CmdLib_Exit( int exitCode ) -{ - TerminateProcess( GetCurrentProcess(), 1 ); -} - - - -#endif - -#endif - - - - -/* -=================== -ExpandWildcards - -Mimic unix command line expansion -=================== -*/ -#define MAX_EX_ARGC 1024 -int ex_argc; -char *ex_argv[MAX_EX_ARGC]; -#if defined( _WIN32 ) && !defined( _X360 ) -#include "io.h" -void ExpandWildcards (int *argc, char ***argv) -{ - struct _finddata_t fileinfo; - int handle; - int i; - char filename[1024]; - char filebase[1024]; - char *path; - - ex_argc = 0; - for (i=0 ; i<*argc ; i++) - { - path = (*argv)[i]; - if ( path[0] == '-' - || ( !strstr(path, "*") && !strstr(path, "?") ) ) - { - ex_argv[ex_argc++] = path; - continue; - } - - handle = _findfirst (path, &fileinfo); - if (handle == -1) - return; - - Q_ExtractFilePath (path, filebase, sizeof( filebase )); - - do - { - sprintf (filename, "%s%s", filebase, fileinfo.name); - ex_argv[ex_argc++] = copystring (filename); - } while (_findnext( handle, &fileinfo ) != -1); - - _findclose (handle); - } - - *argc = ex_argc; - *argv = ex_argv; -} -#else -void ExpandWildcards (int *argc, char ***argv) -{ -} -#endif - - -// only printf if in verbose mode -qboolean verbose = false; -void qprintf (const char *format, ...) -{ - if (!verbose) - return; - - va_list argptr; - va_start (argptr,format); - - char str[2048]; - Q_vsnprintf( str, sizeof(str), format, argptr ); - -#if defined( CMDLIB_NODBGLIB ) - printf( "%s", str ); -#else - Msg( "%s", str ); -#endif - - va_end (argptr); -} - - -// ---------------------------------------------------------------------------------------------------- // -// Helpers. -// ---------------------------------------------------------------------------------------------------- // - -static void CmdLib_getwd( char *out, int outSize ) -{ -#if defined( _WIN32 ) || defined( WIN32 ) - _getcwd( out, outSize ); - Q_strncat( out, "\\", outSize, COPY_ALL_CHARACTERS ); -#else - getcwd(out, outSize); - strcat(out, "/"); -#endif - Q_FixSlashes( out ); -} - -char *ExpandArg (char *path) -{ - static char full[1024]; - - if (path[0] != '/' && path[0] != '\\' && path[1] != ':') - { - CmdLib_getwd (full, sizeof( full )); - Q_strncat (full, path, sizeof( full ), COPY_ALL_CHARACTERS); - } - else - Q_strncpy (full, path, sizeof( full )); - return full; -} - - -char *ExpandPath (char *path) -{ - static char full[1024]; - if (path[0] == '/' || path[0] == '\\' || path[1] == ':') - return path; - sprintf (full, "%s%s", qdir, path); - return full; -} - - - -char *copystring(const char *s) -{ - char *b; - b = (char *)malloc(strlen(s)+1); - strcpy (b, s); - return b; -} - - -void GetHourMinuteSeconds( int nInputSeconds, int &nHours, int &nMinutes, int &nSeconds ) -{ -} - - -void GetHourMinuteSecondsString( int nInputSeconds, char *pOut, int outLen ) -{ - int nMinutes = nInputSeconds / 60; - int nSeconds = nInputSeconds - nMinutes * 60; - int nHours = nMinutes / 60; - nMinutes -= nHours * 60; - - const char *extra[2] = { "", "s" }; - - if ( nHours > 0 ) - Q_snprintf( pOut, outLen, "%d hour%s, %d minute%s, %d second%s", nHours, extra[nHours != 1], nMinutes, extra[nMinutes != 1], nSeconds, extra[nSeconds != 1] ); - else if ( nMinutes > 0 ) - Q_snprintf( pOut, outLen, "%d minute%s, %d second%s", nMinutes, extra[nMinutes != 1], nSeconds, extra[nSeconds != 1] ); - else - Q_snprintf( pOut, outLen, "%d second%s", nSeconds, extra[nSeconds != 1] ); -} - - -void Q_mkdir (char *path) -{ -#if defined( _WIN32 ) || defined( WIN32 ) - if (_mkdir (path) != -1) - return; -#else - if (mkdir (path, 0777) != -1) - return; -#endif -// if (errno != EEXIST) - Error ("mkdir failed %s\n", path ); -} - -void CmdLib_InitFileSystem( const char *pFilename, int maxMemoryUsage ) -{ - FileSystem_Init( pFilename, maxMemoryUsage ); - if ( !g_pFileSystem ) - Error( "CmdLib_InitFileSystem failed." ); -} - -void CmdLib_TermFileSystem() -{ - FileSystem_Term(); -} - -CreateInterfaceFn CmdLib_GetFileSystemFactory() -{ - return FileSystem_GetFactory(); -} - - -/* -============ -FileTime - -returns -1 if not present -============ -*/ -int FileTime (char *path) -{ - struct stat buf; - - if (stat (path,&buf) == -1) - return -1; - - return buf.st_mtime; -} - - - -/* -============== -COM_Parse - -Parse a token out of a string -============== -*/ -char *COM_Parse (char *data) -{ - return (char*)ParseFile( data, com_token, NULL ); -} - - -/* -============================================================================= - - MISC FUNCTIONS - -============================================================================= -*/ - - -/* -================= -CheckParm - -Checks for the given parameter in the program's command line arguments -Returns the argument number (1 to argc-1) or 0 if not present -================= -*/ -int CheckParm (char *check) -{ - int i; - - for (i = 1;iSize( f ); -} - - -FileHandle_t SafeOpenWrite ( const char *filename ) -{ - FileHandle_t f = g_pFileSystem->Open(filename, "wb"); - - if (!f) - { - //Error ("Error opening %s: %s",filename,strerror(errno)); - // BUGBUG: No way to get equivalent of errno from IFileSystem! - Error ("Error opening %s! (Check for write enable)\n",filename); - } - - return f; -} - -#define MAX_CMDLIB_BASE_PATHS 10 -static char g_pBasePaths[MAX_CMDLIB_BASE_PATHS][MAX_PATH]; -static int g_NumBasePaths = 0; - -void CmdLib_AddBasePath( const char *pPath ) -{ -// printf( "CmdLib_AddBasePath( \"%s\" )\n", pPath ); - if( g_NumBasePaths < MAX_CMDLIB_BASE_PATHS ) - { - Q_strncpy( g_pBasePaths[g_NumBasePaths], pPath, MAX_PATH ); - Q_FixSlashes( g_pBasePaths[g_NumBasePaths] ); - g_NumBasePaths++; - } - else - { - Assert( 0 ); - } -} - -bool CmdLib_HasBasePath( const char *pFileName_, int &pathLength ) -{ - char *pFileName = ( char * )_alloca( strlen( pFileName_ ) + 1 ); - strcpy( pFileName, pFileName_ ); - Q_FixSlashes( pFileName ); - pathLength = 0; - int i; - for( i = 0; i < g_NumBasePaths; i++ ) - { - // see if we can rip the base off of the filename. - if( Q_strncasecmp( g_pBasePaths[i], pFileName, strlen( g_pBasePaths[i] ) ) == 0 ) - { - pathLength = strlen( g_pBasePaths[i] ); - return true; - } - } - return false; -} - -int CmdLib_GetNumBasePaths( void ) -{ - return g_NumBasePaths; -} - -const char *CmdLib_GetBasePath( int i ) -{ - Assert( i >= 0 && i < g_NumBasePaths ); - return g_pBasePaths[i]; -} - - -//----------------------------------------------------------------------------- -// Like ExpandPath but expands the path for each base path like SafeOpenRead -//----------------------------------------------------------------------------- -int CmdLib_ExpandWithBasePaths( CUtlVector< CUtlString > &expandedPathList, const char *pszPath ) -{ - int nPathLength = 0; - - pszPath = ExpandPath( const_cast< char * >( pszPath ) ); // Kind of redundant but it's how CmdLib_HasBasePath needs things - - if ( CmdLib_HasBasePath( pszPath, nPathLength ) ) - { - pszPath = pszPath + nPathLength; - for ( int i = 0; i < CmdLib_GetNumBasePaths(); ++i ) - { - CUtlString &expandedPath = expandedPathList[ expandedPathList.AddToTail( CmdLib_GetBasePath( i ) ) ]; - expandedPath += pszPath; - } - } - else - { - expandedPathList.AddToTail( pszPath ); - } - - return expandedPathList.Count(); -} - - -FileHandle_t SafeOpenRead( const char *filename ) -{ - int pathLength; - FileHandle_t f = 0; - if( CmdLib_HasBasePath( filename, pathLength ) ) - { - filename = filename + pathLength; - int i; - for( i = 0; i < g_NumBasePaths; i++ ) - { - char tmp[MAX_PATH]; - strcpy( tmp, g_pBasePaths[i] ); - strcat( tmp, filename ); - f = g_pFileSystem->Open( tmp, "rb" ); - if( f ) - { - return f; - } - } - Error ("Error opening %s\n",filename ); - return f; - } - else - { - f = g_pFileSystem->Open( filename, "rb" ); - if ( !f ) - Error ("Error opening %s",filename ); - - return f; - } -} - -void SafeRead( FileHandle_t f, void *buffer, int count) -{ - if ( g_pFileSystem->Read (buffer, count, f) != (size_t)count) - Error ("File read failure"); -} - - -void SafeWrite ( FileHandle_t f, void *buffer, int count) -{ - if (g_pFileSystem->Write (buffer, count, f) != (size_t)count) - Error ("File write failure"); -} - - -/* -============== -FileExists -============== -*/ -qboolean FileExists ( const char *filename ) -{ - FileHandle_t hFile = g_pFileSystem->Open( filename, "rb" ); - if ( hFile == FILESYSTEM_INVALID_HANDLE ) - { - return false; - } - else - { - g_pFileSystem->Close( hFile ); - return true; - } -} - -/* -============== -LoadFile -============== -*/ -int LoadFile ( const char *filename, void **bufferptr ) -{ - int length = 0; - void *buffer; - - FileHandle_t f = SafeOpenRead (filename); - if ( FILESYSTEM_INVALID_HANDLE != f ) - { - length = Q_filelength (f); - buffer = malloc (length+1); - ((char *)buffer)[length] = 0; - SafeRead (f, buffer, length); - g_pFileSystem->Close (f); - *bufferptr = buffer; - } - else - { - *bufferptr = NULL; - } - return length; -} - - - -/* -============== -SaveFile -============== -*/ -void SaveFile ( const char *filename, void *buffer, int count ) -{ - FileHandle_t f = SafeOpenWrite (filename); - SafeWrite (f, buffer, count); - g_pFileSystem->Close (f); -} - -/* -==================== -Extract file parts -==================== -*/ -// FIXME: should include the slash, otherwise -// backing to an empty path will be wrong when appending a slash - - - -/* -============== -ParseNum / ParseHex -============== -*/ -int ParseHex (char *hex) -{ - char *str; - int num; - - num = 0; - str = hex; - - while (*str) - { - num <<= 4; - if (*str >= '0' && *str <= '9') - num += *str-'0'; - else if (*str >= 'a' && *str <= 'f') - num += 10 + *str-'a'; - else if (*str >= 'A' && *str <= 'F') - num += 10 + *str-'A'; - else - Error ("Bad hex number: %s",hex); - str++; - } - - return num; -} - - -int ParseNum (char *str) -{ - if (str[0] == '$') - return ParseHex (str+1); - if (str[0] == '0' && str[1] == 'x') - return ParseHex (str+2); - return atol (str); -} - -/* -============ -CreatePath -============ -*/ -void CreatePath (char *path) -{ - char *ofs, c; - - // strip the drive - if (path[1] == ':') - path += 2; - - for (ofs = path+1 ; *ofs ; ofs++) - { - c = *ofs; - if (c == '/' || c == '\\') - { // create the directory - *ofs = 0; - Q_mkdir (path); - *ofs = c; - } - } -} - -//----------------------------------------------------------------------------- -// Creates a path, path may already exist -//----------------------------------------------------------------------------- -#if defined( _WIN32 ) || defined( WIN32 ) -void SafeCreatePath( char *path ) -{ - char *ptr; - - // skip past the drive path, but don't strip - if ( path[1] == ':' ) - { - ptr = strchr( path, '\\' ); - } - else - { - ptr = path; - } - while ( ptr ) - { - ptr = strchr( ptr+1, '\\' ); - if ( ptr ) - { - *ptr = '\0'; - _mkdir( path ); - *ptr = '\\'; - } - } -} -#endif - -/* -============ -QCopyFile - - Used to archive source files -============ -*/ -void QCopyFile (char *from, char *to) -{ - void *buffer; - int length; - - length = LoadFile (from, &buffer); - CreatePath (to); - SaveFile (to, buffer, length); - free (buffer); -} - - - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +// ----------------------- +// cmdlib.c +// ----------------------- +#include "tier0/platform.h" +#ifdef IS_WINDOWS_PC +#include +#endif +#include "cmdlib.h" +#include +#include +#include "tier1/strtools.h" +#ifdef _WIN32 +#include +#endif +#include "utlvector.h" +#include "filesystem_helpers.h" +#include "utllinkedlist.h" +#include "tier0/icommandline.h" +#include "KeyValues.h" +#include "filesystem_tools.h" + +#if defined( MPI ) + + #include "vmpi.h" + #include "vmpi_tools_shared.h" + +#endif + + +#if defined( _WIN32 ) || defined( WIN32 ) +#include +#endif + +#if defined( _X360 ) +#include "xbox/xbox_win32stubs.h" +#endif + +// set these before calling CheckParm +int myargc; +char **myargv; + +char com_token[1024]; + +qboolean archive; +char archivedir[1024]; + +FileHandle_t g_pLogFile = 0; + +CUtlLinkedList g_CleanupFunctions; +CUtlLinkedList g_ExtraSpewHooks; + +bool g_bStopOnExit = false; +void (*g_ExtraSpewHook)(const char*) = NULL; + +#if defined( _WIN32 ) || defined( WIN32 ) + +void CmdLib_FPrintf( FileHandle_t hFile, const char *pFormat, ... ) +{ + static CUtlVector buf; + if ( buf.Count() == 0 ) + buf.SetCount( 1024 ); + + va_list marker; + va_start( marker, pFormat ); + + while ( 1 ) + { + int ret = Q_vsnprintf( buf.Base(), buf.Count(), pFormat, marker ); + if ( ret >= 0 ) + { + // Write the string. + g_pFileSystem->Write( buf.Base(), ret, hFile ); + + break; + } + else + { + // Make the buffer larger. + int newSize = buf.Count() * 2; + buf.SetCount( newSize ); + if ( buf.Count() != newSize ) + { + Error( "CmdLib_FPrintf: can't allocate space for text." ); + } + } + } + + va_end( marker ); +} + +char* CmdLib_FGets( char *pOut, int outSize, FileHandle_t hFile ) +{ + int iCur=0; + for ( ; iCur < (outSize-1); iCur++ ) + { + char c; + if ( !g_pFileSystem->Read( &c, 1, hFile ) ) + { + if ( iCur == 0 ) + return NULL; + else + break; + } + + pOut[iCur] = c; + if ( c == '\n' ) + break; + + if ( c == EOF ) + { + if ( iCur == 0 ) + return NULL; + else + break; + } + } + + pOut[iCur] = 0; + return pOut; +} + +#if !defined( _X360 ) +#include +#endif + +// This pauses before exiting if they use -StopOnExit. Useful for debugging. +class CExitStopper +{ +public: + ~CExitStopper() + { + if ( g_bStopOnExit ) + { + Warning( "\nPress any key to quit.\n" ); + getch(); + } + } +} g_ExitStopper; + + +static unsigned short g_InitialColor = 0xFFFF; +static unsigned short g_LastColor = 0xFFFF; +static unsigned short g_BadColor = 0xFFFF; +static WORD g_BackgroundFlags = 0xFFFF; +static void GetInitialColors( ) +{ +#if !defined( _X360 ) + // Get the old background attributes. + CONSOLE_SCREEN_BUFFER_INFO oldInfo; + GetConsoleScreenBufferInfo( GetStdHandle( STD_OUTPUT_HANDLE ), &oldInfo ); + g_InitialColor = g_LastColor = oldInfo.wAttributes & (FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_INTENSITY); + g_BackgroundFlags = oldInfo.wAttributes & (BACKGROUND_RED|BACKGROUND_GREEN|BACKGROUND_BLUE|BACKGROUND_INTENSITY); + + g_BadColor = 0; + if (g_BackgroundFlags & BACKGROUND_RED) + g_BadColor |= FOREGROUND_RED; + if (g_BackgroundFlags & BACKGROUND_GREEN) + g_BadColor |= FOREGROUND_GREEN; + if (g_BackgroundFlags & BACKGROUND_BLUE) + g_BadColor |= FOREGROUND_BLUE; + if (g_BackgroundFlags & BACKGROUND_INTENSITY) + g_BadColor |= FOREGROUND_INTENSITY; +#endif +} + +WORD SetConsoleTextColor( int red, int green, int blue, int intensity ) +{ + WORD ret = g_LastColor; +#if !defined( _X360 ) + + g_LastColor = 0; + if( red ) g_LastColor |= FOREGROUND_RED; + if( green ) g_LastColor |= FOREGROUND_GREEN; + if( blue ) g_LastColor |= FOREGROUND_BLUE; + if( intensity ) g_LastColor |= FOREGROUND_INTENSITY; + + // Just use the initial color if there's a match... + if (g_LastColor == g_BadColor) + g_LastColor = g_InitialColor; + + SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), g_LastColor | g_BackgroundFlags ); +#endif + return ret; +} + +void RestoreConsoleTextColor( WORD color ) +{ +#if !defined( _X360 ) + SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), color | g_BackgroundFlags ); + g_LastColor = color; +#endif +} + + +#if defined( CMDLIB_NODBGLIB ) + +// This can go away when everything is in bin. +void Error( char const *pMsg, ... ) +{ + va_list marker; + va_start( marker, pMsg ); + vprintf( pMsg, marker ); + va_end( marker ); + + exit( -1 ); +} + +#else + +CRITICAL_SECTION g_SpewCS; +bool g_bSpewCSInitted = false; +bool g_bSuppressPrintfOutput = false; + +SpewRetval_t CmdLib_SpewOutputFunc( SpewType_t type, char const *pMsg ) +{ + // Hopefully two threads won't call this simultaneously right at the start! + if ( !g_bSpewCSInitted ) + { + InitializeCriticalSection( &g_SpewCS ); + g_bSpewCSInitted = true; + } + + WORD old; + SpewRetval_t retVal; + + EnterCriticalSection( &g_SpewCS ); + { + if (( type == SPEW_MESSAGE ) || (type == SPEW_LOG )) + { + Color c = *GetSpewOutputColor(); + if ( c.r() != 255 || c.g() != 255 || c.b() != 255 ) + { + // custom color + old = SetConsoleTextColor( c.r(), c.g(), c.b(), c.a() ); + } + else + { + old = SetConsoleTextColor( 1, 1, 1, 0 ); + } + retVal = SPEW_CONTINUE; + } + else if( type == SPEW_WARNING ) + { + old = SetConsoleTextColor( 1, 1, 0, 1 ); + retVal = SPEW_CONTINUE; + } + else if( type == SPEW_ASSERT ) + { + old = SetConsoleTextColor( 1, 0, 0, 1 ); + retVal = SPEW_DEBUGGER; + +#ifdef MPI + // VMPI workers don't want to bring up dialogs and suchlike. + // They need to have a special function installed to handle + // the exceptions and write the minidumps. + // Install the function after VMPI_Init with a call: + // SetupToolsMinidumpHandler( VMPI_ExceptionFilter ); + if ( g_bUseMPI && !g_bMPIMaster && !Plat_IsInDebugSession() ) + { + // Generating an exception and letting the + // installed handler handle it + ::RaiseException + ( + 0, // dwExceptionCode + EXCEPTION_NONCONTINUABLE, // dwExceptionFlags + 0, // nNumberOfArguments, + NULL // const ULONG_PTR* lpArguments + ); + + // Never get here (non-continuable exception) + + VMPI_HandleCrash( pMsg, NULL, true ); + exit( 0 ); + } +#endif + } + else if( type == SPEW_ERROR ) + { + old = SetConsoleTextColor( 1, 0, 0, 1 ); + retVal = SPEW_ABORT; // doesn't matter.. we exit below so we can return an errorlevel (which dbg.dll doesn't do). + } + else + { + old = SetConsoleTextColor( 1, 1, 1, 1 ); + retVal = SPEW_CONTINUE; + } + + if ( !g_bSuppressPrintfOutput || type == SPEW_ERROR ) + printf( "%s", pMsg ); + + OutputDebugString( pMsg ); + + if ( type == SPEW_ERROR ) + { + printf( "\n" ); + OutputDebugString( "\n" ); + } + + if( g_pLogFile ) + { + CmdLib_FPrintf( g_pLogFile, "%s", pMsg ); + g_pFileSystem->Flush( g_pLogFile ); + } + + // Dispatch to other spew hooks. + FOR_EACH_LL( g_ExtraSpewHooks, i ) + g_ExtraSpewHooks[i]( pMsg ); + + RestoreConsoleTextColor( old ); + } + LeaveCriticalSection( &g_SpewCS ); + + if ( type == SPEW_ERROR ) + { + CmdLib_Exit( 1 ); + } + + return retVal; +} + + +void InstallSpewFunction() +{ + setvbuf( stdout, NULL, _IONBF, 0 ); + setvbuf( stderr, NULL, _IONBF, 0 ); + + SpewOutputFunc( CmdLib_SpewOutputFunc ); + GetInitialColors(); +} + + +void InstallExtraSpewHook( SpewHookFn pFn ) +{ + g_ExtraSpewHooks.AddToTail( pFn ); +} + +#if 0 +void CmdLib_AllocError( unsigned long size ) +{ + Error( "Error trying to allocate %d bytes.\n", size ); +} + + +int CmdLib_NewHandler( size_t size ) +{ + CmdLib_AllocError( size ); + return 0; +} +#endif + +void InstallAllocationFunctions() +{ +// _set_new_mode( 1 ); // so if malloc() fails, we exit. +// _set_new_handler( CmdLib_NewHandler ); +} + +void SetSpewFunctionLogFile( char const *pFilename ) +{ + Assert( (!g_pLogFile) ); + g_pLogFile = g_pFileSystem->Open( pFilename, "a" ); + + Assert( g_pLogFile ); + if (!g_pLogFile) + Error("Can't create LogFile:\"%s\"\n", pFilename ); + + CmdLib_FPrintf( g_pLogFile, "\n\n\n" ); +} + + +void CloseSpewFunctionLogFile() +{ + if ( g_pFileSystem && g_pLogFile ) + { + g_pFileSystem->Close( g_pLogFile ); + g_pLogFile = FILESYSTEM_INVALID_HANDLE; + } +} + + +void CmdLib_AtCleanup( CleanupFn pFn ) +{ + g_CleanupFunctions.AddToTail( pFn ); +} + + +void CmdLib_Cleanup() +{ + CloseSpewFunctionLogFile(); + + CmdLib_TermFileSystem(); + + FOR_EACH_LL( g_CleanupFunctions, i ) + g_CleanupFunctions[i](); + +#if defined( MPI ) + // Unfortunately, when you call exit(), even if you have things registered with atexit(), + // threads go into a seemingly undefined state where GetExitCodeThread gives STILL_ACTIVE + // and WaitForSingleObject will stall forever on the thread. Because of this, we must cleanup + // everything that uses threads before exiting. + VMPI_Finalize(); +#endif +} + + +void CmdLib_Exit( int exitCode ) +{ + TerminateProcess( GetCurrentProcess(), 1 ); +} + + + +#endif + +#endif + + + + +/* +=================== +ExpandWildcards + +Mimic unix command line expansion +=================== +*/ +#define MAX_EX_ARGC 1024 +int ex_argc; +char *ex_argv[MAX_EX_ARGC]; +#if defined( _WIN32 ) && !defined( _X360 ) +#include "io.h" +void ExpandWildcards (int *argc, char ***argv) +{ + struct _finddata_t fileinfo; + int handle; + int i; + char filename[1024]; + char filebase[1024]; + char *path; + + ex_argc = 0; + for (i=0 ; i<*argc ; i++) + { + path = (*argv)[i]; + if ( path[0] == '-' + || ( !strstr(path, "*") && !strstr(path, "?") ) ) + { + ex_argv[ex_argc++] = path; + continue; + } + + handle = _findfirst (path, &fileinfo); + if (handle == -1) + return; + + Q_ExtractFilePath (path, filebase, sizeof( filebase )); + + do + { + sprintf (filename, "%s%s", filebase, fileinfo.name); + ex_argv[ex_argc++] = copystring (filename); + } while (_findnext( handle, &fileinfo ) != -1); + + _findclose (handle); + } + + *argc = ex_argc; + *argv = ex_argv; +} +#else +void ExpandWildcards (int *argc, char ***argv) +{ +} +#endif + + +// only printf if in verbose mode +qboolean verbose = false; +void qprintf (const char *format, ...) +{ + if (!verbose) + return; + + va_list argptr; + va_start (argptr,format); + + char str[2048]; + Q_vsnprintf( str, sizeof(str), format, argptr ); + +#if defined( CMDLIB_NODBGLIB ) + printf( "%s", str ); +#else + Msg( "%s", str ); +#endif + + va_end (argptr); +} + + +// ---------------------------------------------------------------------------------------------------- // +// Helpers. +// ---------------------------------------------------------------------------------------------------- // + +static void CmdLib_getwd( char *out, int outSize ) +{ +#if defined( _WIN32 ) || defined( WIN32 ) + _getcwd( out, outSize ); + Q_strncat( out, "\\", outSize, COPY_ALL_CHARACTERS ); +#else + getcwd(out, outSize); + strcat(out, "/"); +#endif + Q_FixSlashes( out ); +} + +char *ExpandArg (char *path) +{ + static char full[1024]; + + if (path[0] != '/' && path[0] != '\\' && path[1] != ':') + { + CmdLib_getwd (full, sizeof( full )); + Q_strncat (full, path, sizeof( full ), COPY_ALL_CHARACTERS); + } + else + Q_strncpy (full, path, sizeof( full )); + return full; +} + + +char *ExpandPath (char *path) +{ + static char full[1024]; + if (path[0] == '/' || path[0] == '\\' || path[1] == ':') + return path; + sprintf (full, "%s%s", qdir, path); + return full; +} + + + +char *copystring(const char *s) +{ + char *b; + b = (char *)malloc(strlen(s)+1); + strcpy (b, s); + return b; +} + + +void GetHourMinuteSeconds( int nInputSeconds, int &nHours, int &nMinutes, int &nSeconds ) +{ +} + + +void GetHourMinuteSecondsString( int nInputSeconds, char *pOut, int outLen ) +{ + int nMinutes = nInputSeconds / 60; + int nSeconds = nInputSeconds - nMinutes * 60; + int nHours = nMinutes / 60; + nMinutes -= nHours * 60; + + const char *extra[2] = { "", "s" }; + + if ( nHours > 0 ) + Q_snprintf( pOut, outLen, "%d hour%s, %d minute%s, %d second%s", nHours, extra[nHours != 1], nMinutes, extra[nMinutes != 1], nSeconds, extra[nSeconds != 1] ); + else if ( nMinutes > 0 ) + Q_snprintf( pOut, outLen, "%d minute%s, %d second%s", nMinutes, extra[nMinutes != 1], nSeconds, extra[nSeconds != 1] ); + else + Q_snprintf( pOut, outLen, "%d second%s", nSeconds, extra[nSeconds != 1] ); +} + + +void Q_mkdir (char *path) +{ +#if defined( _WIN32 ) || defined( WIN32 ) + if (_mkdir (path) != -1) + return; +#else + if (mkdir (path, 0777) != -1) + return; +#endif +// if (errno != EEXIST) + Error ("mkdir failed %s\n", path ); +} + +void CmdLib_InitFileSystem( const char *pFilename, int maxMemoryUsage ) +{ + FileSystem_Init( pFilename, maxMemoryUsage ); + if ( !g_pFileSystem ) + Error( "CmdLib_InitFileSystem failed." ); +} + +void CmdLib_TermFileSystem() +{ + FileSystem_Term(); +} + +CreateInterfaceFn CmdLib_GetFileSystemFactory() +{ + return FileSystem_GetFactory(); +} + + +/* +============ +FileTime + +returns -1 if not present +============ +*/ +int FileTime (char *path) +{ + struct stat buf; + + if (stat (path,&buf) == -1) + return -1; + + return buf.st_mtime; +} + + + +/* +============== +COM_Parse + +Parse a token out of a string +============== +*/ +char *COM_Parse (char *data) +{ + return (char*)ParseFile( data, com_token, NULL ); +} + + +/* +============================================================================= + + MISC FUNCTIONS + +============================================================================= +*/ + + +/* +================= +CheckParm + +Checks for the given parameter in the program's command line arguments +Returns the argument number (1 to argc-1) or 0 if not present +================= +*/ +int CheckParm (char *check) +{ + int i; + + for (i = 1;iSize( f ); +} + + +FileHandle_t SafeOpenWrite ( const char *filename ) +{ + FileHandle_t f = g_pFileSystem->Open(filename, "wb"); + + if (!f) + { + //Error ("Error opening %s: %s",filename,strerror(errno)); + // BUGBUG: No way to get equivalent of errno from IFileSystem! + Error ("Error opening %s! (Check for write enable)\n",filename); + } + + return f; +} + +#define MAX_CMDLIB_BASE_PATHS 10 +static char g_pBasePaths[MAX_CMDLIB_BASE_PATHS][MAX_PATH]; +static int g_NumBasePaths = 0; + +void CmdLib_AddBasePath( const char *pPath ) +{ +// printf( "CmdLib_AddBasePath( \"%s\" )\n", pPath ); + if( g_NumBasePaths < MAX_CMDLIB_BASE_PATHS ) + { + Q_strncpy( g_pBasePaths[g_NumBasePaths], pPath, MAX_PATH ); + Q_FixSlashes( g_pBasePaths[g_NumBasePaths] ); + g_NumBasePaths++; + } + else + { + Assert( 0 ); + } +} + +bool CmdLib_HasBasePath( const char *pFileName_, int &pathLength ) +{ + char *pFileName = ( char * )_alloca( strlen( pFileName_ ) + 1 ); + strcpy( pFileName, pFileName_ ); + Q_FixSlashes( pFileName ); + pathLength = 0; + int i; + for( i = 0; i < g_NumBasePaths; i++ ) + { + // see if we can rip the base off of the filename. + if( Q_strncasecmp( g_pBasePaths[i], pFileName, strlen( g_pBasePaths[i] ) ) == 0 ) + { + pathLength = strlen( g_pBasePaths[i] ); + return true; + } + } + return false; +} + +int CmdLib_GetNumBasePaths( void ) +{ + return g_NumBasePaths; +} + +const char *CmdLib_GetBasePath( int i ) +{ + Assert( i >= 0 && i < g_NumBasePaths ); + return g_pBasePaths[i]; +} + + +//----------------------------------------------------------------------------- +// Like ExpandPath but expands the path for each base path like SafeOpenRead +//----------------------------------------------------------------------------- +int CmdLib_ExpandWithBasePaths( CUtlVector< CUtlString > &expandedPathList, const char *pszPath ) +{ + int nPathLength = 0; + + pszPath = ExpandPath( const_cast< char * >( pszPath ) ); // Kind of redundant but it's how CmdLib_HasBasePath needs things + + if ( CmdLib_HasBasePath( pszPath, nPathLength ) ) + { + pszPath = pszPath + nPathLength; + for ( int i = 0; i < CmdLib_GetNumBasePaths(); ++i ) + { + CUtlString &expandedPath = expandedPathList[ expandedPathList.AddToTail( CmdLib_GetBasePath( i ) ) ]; + expandedPath += pszPath; + } + } + else + { + expandedPathList.AddToTail( pszPath ); + } + + return expandedPathList.Count(); +} + + +FileHandle_t SafeOpenRead( const char *filename ) +{ + int pathLength; + FileHandle_t f = 0; + if( CmdLib_HasBasePath( filename, pathLength ) ) + { + filename = filename + pathLength; + int i; + for( i = 0; i < g_NumBasePaths; i++ ) + { + char tmp[MAX_PATH]; + strcpy( tmp, g_pBasePaths[i] ); + strcat( tmp, filename ); + f = g_pFileSystem->Open( tmp, "rb" ); + if( f ) + { + return f; + } + } + Error ("Error opening %s\n",filename ); + return f; + } + else + { + f = g_pFileSystem->Open( filename, "rb" ); + if ( !f ) + Error ("Error opening %s",filename ); + + return f; + } +} + +void SafeRead( FileHandle_t f, void *buffer, int count) +{ + if ( g_pFileSystem->Read (buffer, count, f) != (size_t)count) + Error ("File read failure"); +} + + +void SafeWrite ( FileHandle_t f, void *buffer, int count) +{ + if (g_pFileSystem->Write (buffer, count, f) != (size_t)count) + Error ("File write failure"); +} + + +/* +============== +FileExists +============== +*/ +qboolean FileExists ( const char *filename ) +{ + FileHandle_t hFile = g_pFileSystem->Open( filename, "rb" ); + if ( hFile == FILESYSTEM_INVALID_HANDLE ) + { + return false; + } + else + { + g_pFileSystem->Close( hFile ); + return true; + } +} + +/* +============== +LoadFile +============== +*/ +int LoadFile ( const char *filename, void **bufferptr ) +{ + int length = 0; + void *buffer; + + FileHandle_t f = SafeOpenRead (filename); + if ( FILESYSTEM_INVALID_HANDLE != f ) + { + length = Q_filelength (f); + buffer = malloc (length+1); + ((char *)buffer)[length] = 0; + SafeRead (f, buffer, length); + g_pFileSystem->Close (f); + *bufferptr = buffer; + } + else + { + *bufferptr = NULL; + } + return length; +} + + + +/* +============== +SaveFile +============== +*/ +void SaveFile ( const char *filename, void *buffer, int count ) +{ + FileHandle_t f = SafeOpenWrite (filename); + SafeWrite (f, buffer, count); + g_pFileSystem->Close (f); +} + +/* +==================== +Extract file parts +==================== +*/ +// FIXME: should include the slash, otherwise +// backing to an empty path will be wrong when appending a slash + + + +/* +============== +ParseNum / ParseHex +============== +*/ +int ParseHex (char *hex) +{ + char *str; + int num; + + num = 0; + str = hex; + + while (*str) + { + num <<= 4; + if (*str >= '0' && *str <= '9') + num += *str-'0'; + else if (*str >= 'a' && *str <= 'f') + num += 10 + *str-'a'; + else if (*str >= 'A' && *str <= 'F') + num += 10 + *str-'A'; + else + Error ("Bad hex number: %s",hex); + str++; + } + + return num; +} + + +int ParseNum (char *str) +{ + if (str[0] == '$') + return ParseHex (str+1); + if (str[0] == '0' && str[1] == 'x') + return ParseHex (str+2); + return atol (str); +} + +/* +============ +CreatePath +============ +*/ +void CreatePath (char *path) +{ + char *ofs, c; + + // strip the drive + if (path[1] == ':') + path += 2; + + for (ofs = path+1 ; *ofs ; ofs++) + { + c = *ofs; + if (c == '/' || c == '\\') + { // create the directory + *ofs = 0; + Q_mkdir (path); + *ofs = c; + } + } +} + +//----------------------------------------------------------------------------- +// Creates a path, path may already exist +//----------------------------------------------------------------------------- +#if defined( _WIN32 ) || defined( WIN32 ) +void SafeCreatePath( char *path ) +{ + char *ptr; + + // skip past the drive path, but don't strip + if ( path[1] == ':' ) + { + ptr = strchr( path, '\\' ); + } + else + { + ptr = path; + } + while ( ptr ) + { + ptr = strchr( ptr+1, '\\' ); + if ( ptr ) + { + *ptr = '\0'; + _mkdir( path ); + *ptr = '\\'; + } + } +} +#endif + +/* +============ +QCopyFile + + Used to archive source files +============ +*/ +void QCopyFile (char *from, char *to) +{ + void *buffer; + int length; + + length = LoadFile (from, &buffer); + CreatePath (to); + SaveFile (to, buffer, length); + free (buffer); +} + + + diff --git a/mp/src/utils/common/cmdlib.h b/mp/src/utils/common/cmdlib.h index 50fa9d20..5b5b2c7c 100644 --- a/mp/src/utils/common/cmdlib.h +++ b/mp/src/utils/common/cmdlib.h @@ -1,178 +1,178 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// $NoKeywords: $ -//=============================================================================// - -#ifndef CMDLIB_H -#define CMDLIB_H - -#ifdef _WIN32 -#pragma once -#endif - -// cmdlib.h - -#include "basetypes.h" - -// This can go away when everything is in bin. -#if defined( CMDLIB_NODBGLIB ) - void Error( PRINTF_FORMAT_STRING char const *pMsg, ... ); -#else - #include "tier0/dbg.h" -#endif - -#include -#include -#include -#include -#include -#include -#include -#include "filesystem.h" -#include "filesystem_tools.h" -#include "tier1/utlstring.h" - - -// Tools should use this as the read path ID. It'll look into the paths specified by gameinfo.txt -#define TOOLS_READ_PATH_ID "GAME" - - -// Tools should use this to fprintf data to files. -void CmdLib_FPrintf( FileHandle_t hFile, PRINTF_FORMAT_STRING const char *pFormat, ... ); -char* CmdLib_FGets( char *pOut, int outSize, FileHandle_t hFile ); - - -// This can be set so Msg() sends output to hook functions (like the VMPI MySQL database), -// but doesn't actually printf the output. -extern bool g_bSuppressPrintfOutput; - -extern IBaseFileSystem *g_pFileSystem; - -// These call right into the functions in filesystem_tools.h -void CmdLib_InitFileSystem( const char *pFilename, int maxMemoryUsage = 0 ); -void CmdLib_TermFileSystem(); // GracefulExit calls this. -CreateInterfaceFn CmdLib_GetFileSystemFactory(); - - -#ifdef _WIN32 -#pragma warning(disable : 4244) // MIPS -#pragma warning(disable : 4136) // X86 -#pragma warning(disable : 4051) // ALPHA - -#pragma warning(disable : 4018) // signed/unsigned mismatch -#pragma warning(disable : 4305) // truncate from double to float - -#pragma warning(disable : 4389) // singned/unsigned mismatch in == -#pragma warning(disable: 4512) // assignment operator could not be generated -#endif - - -// the dec offsetof macro doesnt work very well... -#define myoffsetof(type,identifier) offsetof( type, identifier ) - - -// set these before calling CheckParm -extern int myargc; -extern char **myargv; - -int Q_filelength (FileHandle_t f); -int FileTime (char *path); - -void Q_mkdir( char *path ); - -char *ExpandArg (char *path); // expand relative to CWD -char *ExpandPath (char *path); // expand relative to gamedir - -char *ExpandPathAndArchive (char *path); - -// Fills in pOut with "X hours, Y minutes, Z seconds". Leaves out hours or minutes if they're zero. -void GetHourMinuteSecondsString( int nInputSeconds, char *pOut, int outLen ); - - - -int CheckParm (char *check); - -FileHandle_t SafeOpenWrite ( const char *filename ); -FileHandle_t SafeOpenRead ( const char *filename ); -void SafeRead( FileHandle_t f, void *buffer, int count); -void SafeWrite( FileHandle_t f, void *buffer, int count); - -int LoadFile ( const char *filename, void **bufferptr ); -void SaveFile ( const char *filename, void *buffer, int count ); -qboolean FileExists ( const char *filename ); - -int ParseNum (char *str); - -// Do a printf in the specified color. -#define CP_ERROR stderr, 1, 0, 0, 1 // default colors.. -#define CP_WARNING stderr, 1, 1, 0, 1 -#define CP_STARTUP stdout, 0, 1, 1, 1 -#define CP_NOTIFY stdout, 1, 1, 1, 1 -void ColorPrintf( FILE *pFile, bool red, bool green, bool blue, bool intensity, PRINTF_FORMAT_STRING char const *pFormat, ... ); - -// Initialize spew output. -void InstallSpewFunction(); - -// This registers an extra callback for spew output. -typedef void (*SpewHookFn)( const char * ); -void InstallExtraSpewHook( SpewHookFn pFn ); - -// Install allocation hooks so we error out if an allocation can't happen. -void InstallAllocationFunctions(); - -// This shuts down mgrs that use threads gracefully. If you just call exit(), the threads can -// get in a state where you can't tell if they are shutdown or not, and it can stall forever. -typedef void (*CleanupFn)(); -void CmdLib_AtCleanup( CleanupFn pFn ); // register a callback when Cleanup() is called. -void CmdLib_Cleanup(); -void CmdLib_Exit( int exitCode ); // Use this to cleanup and call exit(). - -// entrypoint if chaining spew functions -SpewRetval_t CmdLib_SpewOutputFunc( SpewType_t type, char const *pMsg ); -unsigned short SetConsoleTextColor( int red, int green, int blue, int intensity ); -void RestoreConsoleTextColor( unsigned short color ); - -// Append all spew output to the specified file. -void SetSpewFunctionLogFile( char const *pFilename ); - -char *COM_Parse (char *data); - -extern char com_token[1024]; - -char *copystring(const char *s); - -void CreatePath( char *path ); -void QCopyFile( char *from, char *to ); -void SafeCreatePath( char *path ); - -extern qboolean archive; -extern char archivedir[1024]; - -extern qboolean verbose; - -void qprintf( PRINTF_FORMAT_STRING const char *format, ... ); - -void ExpandWildcards (int *argc, char ***argv); - -void CmdLib_AddBasePath( const char *pBasePath ); -bool CmdLib_HasBasePath( const char *pFileName, int &pathLength ); -int CmdLib_GetNumBasePaths( void ); -const char *CmdLib_GetBasePath( int i ); -// Like ExpandPath but expands the path for each base path like SafeOpenRead -int CmdLib_ExpandWithBasePaths( CUtlVector< CUtlString > &expandedPathList, const char *pszPath ); - -extern bool g_bStopOnExit; - -// for compression routines -typedef struct -{ - byte *data; - int count; -} cblock_t; - - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//=============================================================================// + +#ifndef CMDLIB_H +#define CMDLIB_H + +#ifdef _WIN32 +#pragma once +#endif + +// cmdlib.h + +#include "basetypes.h" + +// This can go away when everything is in bin. +#if defined( CMDLIB_NODBGLIB ) + void Error( PRINTF_FORMAT_STRING char const *pMsg, ... ); +#else + #include "tier0/dbg.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include "filesystem.h" +#include "filesystem_tools.h" +#include "tier1/utlstring.h" + + +// Tools should use this as the read path ID. It'll look into the paths specified by gameinfo.txt +#define TOOLS_READ_PATH_ID "GAME" + + +// Tools should use this to fprintf data to files. +void CmdLib_FPrintf( FileHandle_t hFile, PRINTF_FORMAT_STRING const char *pFormat, ... ); +char* CmdLib_FGets( char *pOut, int outSize, FileHandle_t hFile ); + + +// This can be set so Msg() sends output to hook functions (like the VMPI MySQL database), +// but doesn't actually printf the output. +extern bool g_bSuppressPrintfOutput; + +extern IBaseFileSystem *g_pFileSystem; + +// These call right into the functions in filesystem_tools.h +void CmdLib_InitFileSystem( const char *pFilename, int maxMemoryUsage = 0 ); +void CmdLib_TermFileSystem(); // GracefulExit calls this. +CreateInterfaceFn CmdLib_GetFileSystemFactory(); + + +#ifdef _WIN32 +#pragma warning(disable : 4244) // MIPS +#pragma warning(disable : 4136) // X86 +#pragma warning(disable : 4051) // ALPHA + +#pragma warning(disable : 4018) // signed/unsigned mismatch +#pragma warning(disable : 4305) // truncate from double to float + +#pragma warning(disable : 4389) // singned/unsigned mismatch in == +#pragma warning(disable: 4512) // assignment operator could not be generated +#endif + + +// the dec offsetof macro doesnt work very well... +#define myoffsetof(type,identifier) offsetof( type, identifier ) + + +// set these before calling CheckParm +extern int myargc; +extern char **myargv; + +int Q_filelength (FileHandle_t f); +int FileTime (char *path); + +void Q_mkdir( char *path ); + +char *ExpandArg (char *path); // expand relative to CWD +char *ExpandPath (char *path); // expand relative to gamedir + +char *ExpandPathAndArchive (char *path); + +// Fills in pOut with "X hours, Y minutes, Z seconds". Leaves out hours or minutes if they're zero. +void GetHourMinuteSecondsString( int nInputSeconds, char *pOut, int outLen ); + + + +int CheckParm (char *check); + +FileHandle_t SafeOpenWrite ( const char *filename ); +FileHandle_t SafeOpenRead ( const char *filename ); +void SafeRead( FileHandle_t f, void *buffer, int count); +void SafeWrite( FileHandle_t f, void *buffer, int count); + +int LoadFile ( const char *filename, void **bufferptr ); +void SaveFile ( const char *filename, void *buffer, int count ); +qboolean FileExists ( const char *filename ); + +int ParseNum (char *str); + +// Do a printf in the specified color. +#define CP_ERROR stderr, 1, 0, 0, 1 // default colors.. +#define CP_WARNING stderr, 1, 1, 0, 1 +#define CP_STARTUP stdout, 0, 1, 1, 1 +#define CP_NOTIFY stdout, 1, 1, 1, 1 +void ColorPrintf( FILE *pFile, bool red, bool green, bool blue, bool intensity, PRINTF_FORMAT_STRING char const *pFormat, ... ); + +// Initialize spew output. +void InstallSpewFunction(); + +// This registers an extra callback for spew output. +typedef void (*SpewHookFn)( const char * ); +void InstallExtraSpewHook( SpewHookFn pFn ); + +// Install allocation hooks so we error out if an allocation can't happen. +void InstallAllocationFunctions(); + +// This shuts down mgrs that use threads gracefully. If you just call exit(), the threads can +// get in a state where you can't tell if they are shutdown or not, and it can stall forever. +typedef void (*CleanupFn)(); +void CmdLib_AtCleanup( CleanupFn pFn ); // register a callback when Cleanup() is called. +void CmdLib_Cleanup(); +void CmdLib_Exit( int exitCode ); // Use this to cleanup and call exit(). + +// entrypoint if chaining spew functions +SpewRetval_t CmdLib_SpewOutputFunc( SpewType_t type, char const *pMsg ); +unsigned short SetConsoleTextColor( int red, int green, int blue, int intensity ); +void RestoreConsoleTextColor( unsigned short color ); + +// Append all spew output to the specified file. +void SetSpewFunctionLogFile( char const *pFilename ); + +char *COM_Parse (char *data); + +extern char com_token[1024]; + +char *copystring(const char *s); + +void CreatePath( char *path ); +void QCopyFile( char *from, char *to ); +void SafeCreatePath( char *path ); + +extern qboolean archive; +extern char archivedir[1024]; + +extern qboolean verbose; + +void qprintf( PRINTF_FORMAT_STRING const char *format, ... ); + +void ExpandWildcards (int *argc, char ***argv); + +void CmdLib_AddBasePath( const char *pBasePath ); +bool CmdLib_HasBasePath( const char *pFileName, int &pathLength ); +int CmdLib_GetNumBasePaths( void ); +const char *CmdLib_GetBasePath( int i ); +// Like ExpandPath but expands the path for each base path like SafeOpenRead +int CmdLib_ExpandWithBasePaths( CUtlVector< CUtlString > &expandedPathList, const char *pszPath ); + +extern bool g_bStopOnExit; + +// for compression routines +typedef struct +{ + byte *data; + int count; +} cblock_t; + + #endif // CMDLIB_H \ No newline at end of file diff --git a/mp/src/utils/common/consolewnd.cpp b/mp/src/utils/common/consolewnd.cpp index 8802e39e..6201409a 100644 --- a/mp/src/utils/common/consolewnd.cpp +++ b/mp/src/utils/common/consolewnd.cpp @@ -1,333 +1,333 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include -#include "consolewnd.h" - - -#pragma warning( disable : 4311 ) // warning C4311: 'reinterpret_cast' : pointer truncation from 'CConsoleWnd *const ' to 'LONG' -#pragma warning( disable : 4312 ) // warning C4312: 'type cast' : conversion from 'LONG' to 'CConsoleWnd *' of greater size - -#define EDITCONTROL_BORDER_SIZE 5 - - -// ------------------------------------------------------------------------------------------------ // -// Functions to manage the console window. -// ------------------------------------------------------------------------------------------------ // - -class CConsoleWnd : public IConsoleWnd -{ -public: - CConsoleWnd(); - ~CConsoleWnd(); - - bool Init( void *hInstance, int dialogResourceID, int editControlID, bool bVisible ); - void Term(); - - virtual void Release(); - - virtual void SetVisible( bool bVisible ); - virtual bool IsVisible() const; - - virtual void PrintToConsole( const char *pMsg ); - virtual void SetTitle( const char *pTitle ); - - virtual void SetDeleteOnClose( bool bDelete ); - - -private: - - int WindowProc( - HWND hwndDlg, // handle to dialog box - UINT uMsg, // message - WPARAM wParam, // first message parameter - LPARAM lParam // second message parameter - ); - - static int CALLBACK StaticWindowProc( - HWND hwndDlg, // handle to dialog box - UINT uMsg, // message - WPARAM wParam, // first message parameter - LPARAM lParam // second message parameter - ); - - void RepositionEditControl(); - - -private: - - HWND m_hWnd; - HWND m_hEditControl; - bool m_bVisible; - bool m_bDeleteOnClose; - int m_nCurrentChars; -}; - - -CConsoleWnd::CConsoleWnd() -{ - m_hWnd = m_hEditControl = NULL; - m_bVisible = false; - m_bDeleteOnClose = false; - m_nCurrentChars = 0; -} - - -CConsoleWnd::~CConsoleWnd() -{ - Term(); -} - -bool CConsoleWnd::Init( void *hInstance, int dialogResourceID, int editControlID, bool bVisible ) -{ - // Create the window. - m_hWnd = CreateDialog( - (HINSTANCE)hInstance, - MAKEINTRESOURCE( dialogResourceID ), - NULL, - &CConsoleWnd::StaticWindowProc ); - - if ( !m_hWnd ) - return false; - - SetWindowLong( m_hWnd, GWL_USERDATA, reinterpret_cast< LONG >( this ) ); - if ( bVisible ) - ShowWindow( m_hWnd, SW_SHOW ); - - // Get a handle to the edit control. - m_hEditControl = GetDlgItem( m_hWnd, editControlID ); - if ( !m_hEditControl ) - return false; - - RepositionEditControl(); - - m_bVisible = bVisible; - return true; -} - - -void CConsoleWnd::Term() -{ - if ( m_hWnd ) - { - DestroyWindow( m_hWnd ); - m_hWnd = NULL; - } -} - - -void CConsoleWnd::Release() -{ - delete this; -} - - -void CConsoleWnd::SetVisible( bool bVisible ) -{ - ShowWindow( m_hWnd, bVisible ? SW_RESTORE : SW_HIDE ); - - if ( bVisible ) - { - ShowWindow( m_hWnd, SW_SHOW ); - SetWindowPos( m_hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE ); - UpdateWindow( m_hWnd ); - - int nLen = (int)SendMessage( m_hEditControl, EM_GETLIMITTEXT, 0, 0 ); - SendMessage( m_hEditControl, EM_SETSEL, nLen, nLen ); - } - else - { - SetWindowPos( m_hWnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_HIDEWINDOW | SWP_NOOWNERZORDER ); - } - - m_bVisible = bVisible; -} - - -bool CConsoleWnd::IsVisible() const -{ - return m_bVisible; -} - - -void CConsoleWnd::PrintToConsole( const char *pMsg ) -{ - if ( m_nCurrentChars >= 16*1024 ) - { - // Clear the edit control otherwise it'll stop outputting anything. - m_nCurrentChars = 0; - - int nLen = (int)SendMessage( m_hEditControl, EM_GETLIMITTEXT, 0, 0 ); - SendMessage( m_hEditControl, EM_SETSEL, 0, nLen ); - SendMessage( m_hEditControl, EM_REPLACESEL, FALSE, (LPARAM)"" ); - } - - FormatAndSendToEditControl( m_hEditControl, pMsg ); - m_nCurrentChars += (int)strlen( pMsg ); -} - - -void CConsoleWnd::SetTitle( const char *pTitle ) -{ - SetWindowText( m_hWnd, pTitle ); -} - - -int CConsoleWnd::WindowProc( - HWND hwndDlg, // handle to dialog box - UINT uMsg, // message - WPARAM wParam, // first message parameter - LPARAM lParam // second message parameter - ) -{ - lParam = lParam; // avoid compiler warning - - if ( hwndDlg != m_hWnd ) - return false; - - switch ( uMsg ) - { - case WM_SYSCOMMAND: - { - if ( wParam == SC_CLOSE ) - { - if ( m_bDeleteOnClose ) - { - Release(); - } - else - { - SetVisible( false ); - return true; - } - } - } - break; - - case WM_SHOWWINDOW: - { - m_bVisible = (wParam != 0); - } - break; - - case WM_SIZE: - case WM_INITDIALOG: - { - RepositionEditControl(); - } - break; - } - - return false; -} - - -int CConsoleWnd::StaticWindowProc( - HWND hwndDlg, // handle to dialog box - UINT uMsg, // message - WPARAM wParam, // first message parameter - LPARAM lParam // second message parameter - ) -{ - CConsoleWnd *pDlg = (CConsoleWnd*)GetWindowLong( hwndDlg, GWL_USERDATA ); - if ( pDlg ) - return pDlg->WindowProc( hwndDlg, uMsg, wParam, lParam ); - else - return false; -} - - -void CConsoleWnd::RepositionEditControl() -{ - RECT rcMain; - GetClientRect( m_hWnd, &rcMain ); - - RECT rcNew; - rcNew.left = rcMain.left + EDITCONTROL_BORDER_SIZE; - rcNew.right = rcMain.right - EDITCONTROL_BORDER_SIZE; - rcNew.top = rcMain.top + EDITCONTROL_BORDER_SIZE; - rcNew.bottom = rcMain.bottom - EDITCONTROL_BORDER_SIZE; - - SetWindowPos( - m_hEditControl, - NULL, - rcNew.left, - rcNew.top, - rcNew.right - rcNew.left, - rcNew.bottom - rcNew.top, - SWP_NOZORDER ); -} - - -void CConsoleWnd::SetDeleteOnClose( bool bDelete ) -{ - m_bDeleteOnClose = bDelete; -} - - -// ------------------------------------------------------------------------------------ // -// Module interface. -// ------------------------------------------------------------------------------------ // - -void SendToEditControl( HWND hEditControl, const char *pText ) -{ - int nLen = (int)SendMessage( hEditControl, EM_GETLIMITTEXT, 0, 0 ); - SendMessage( hEditControl, EM_SETSEL, nLen, nLen ); - SendMessage( hEditControl, EM_REPLACESEL, FALSE, (LPARAM)pText ); -} - - -void FormatAndSendToEditControl( void *hWnd, const char *pText ) -{ - HWND hEditControl = (HWND)hWnd; - - // Translate \n to \r\n. - char outMsg[1024]; - const char *pIn = pText; - char *pOut = outMsg; - while ( *pIn ) - { - if ( *pIn == '\n' ) - { - *pOut = '\r'; - pOut++; - } - *pOut = *pIn; - - ++pIn; - ++pOut; - - if ( pOut - outMsg >= 1020 ) - { - *pOut = 0; - SendToEditControl( hEditControl, outMsg ); - pOut = outMsg; - } - } - *pOut = 0; - SendToEditControl( hEditControl, outMsg ); -} - - -IConsoleWnd* CreateConsoleWnd( void *hInstance, int dialogResourceID, int editControlID, bool bVisible ) -{ - CConsoleWnd *pWnd = new CConsoleWnd; - - if ( pWnd->Init( hInstance, dialogResourceID, editControlID, bVisible ) ) - { - return pWnd; - } - else - { - pWnd->Release(); - return NULL; - } -} - - - - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include +#include "consolewnd.h" + + +#pragma warning( disable : 4311 ) // warning C4311: 'reinterpret_cast' : pointer truncation from 'CConsoleWnd *const ' to 'LONG' +#pragma warning( disable : 4312 ) // warning C4312: 'type cast' : conversion from 'LONG' to 'CConsoleWnd *' of greater size + +#define EDITCONTROL_BORDER_SIZE 5 + + +// ------------------------------------------------------------------------------------------------ // +// Functions to manage the console window. +// ------------------------------------------------------------------------------------------------ // + +class CConsoleWnd : public IConsoleWnd +{ +public: + CConsoleWnd(); + ~CConsoleWnd(); + + bool Init( void *hInstance, int dialogResourceID, int editControlID, bool bVisible ); + void Term(); + + virtual void Release(); + + virtual void SetVisible( bool bVisible ); + virtual bool IsVisible() const; + + virtual void PrintToConsole( const char *pMsg ); + virtual void SetTitle( const char *pTitle ); + + virtual void SetDeleteOnClose( bool bDelete ); + + +private: + + int WindowProc( + HWND hwndDlg, // handle to dialog box + UINT uMsg, // message + WPARAM wParam, // first message parameter + LPARAM lParam // second message parameter + ); + + static int CALLBACK StaticWindowProc( + HWND hwndDlg, // handle to dialog box + UINT uMsg, // message + WPARAM wParam, // first message parameter + LPARAM lParam // second message parameter + ); + + void RepositionEditControl(); + + +private: + + HWND m_hWnd; + HWND m_hEditControl; + bool m_bVisible; + bool m_bDeleteOnClose; + int m_nCurrentChars; +}; + + +CConsoleWnd::CConsoleWnd() +{ + m_hWnd = m_hEditControl = NULL; + m_bVisible = false; + m_bDeleteOnClose = false; + m_nCurrentChars = 0; +} + + +CConsoleWnd::~CConsoleWnd() +{ + Term(); +} + +bool CConsoleWnd::Init( void *hInstance, int dialogResourceID, int editControlID, bool bVisible ) +{ + // Create the window. + m_hWnd = CreateDialog( + (HINSTANCE)hInstance, + MAKEINTRESOURCE( dialogResourceID ), + NULL, + &CConsoleWnd::StaticWindowProc ); + + if ( !m_hWnd ) + return false; + + SetWindowLong( m_hWnd, GWL_USERDATA, reinterpret_cast< LONG >( this ) ); + if ( bVisible ) + ShowWindow( m_hWnd, SW_SHOW ); + + // Get a handle to the edit control. + m_hEditControl = GetDlgItem( m_hWnd, editControlID ); + if ( !m_hEditControl ) + return false; + + RepositionEditControl(); + + m_bVisible = bVisible; + return true; +} + + +void CConsoleWnd::Term() +{ + if ( m_hWnd ) + { + DestroyWindow( m_hWnd ); + m_hWnd = NULL; + } +} + + +void CConsoleWnd::Release() +{ + delete this; +} + + +void CConsoleWnd::SetVisible( bool bVisible ) +{ + ShowWindow( m_hWnd, bVisible ? SW_RESTORE : SW_HIDE ); + + if ( bVisible ) + { + ShowWindow( m_hWnd, SW_SHOW ); + SetWindowPos( m_hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE ); + UpdateWindow( m_hWnd ); + + int nLen = (int)SendMessage( m_hEditControl, EM_GETLIMITTEXT, 0, 0 ); + SendMessage( m_hEditControl, EM_SETSEL, nLen, nLen ); + } + else + { + SetWindowPos( m_hWnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_HIDEWINDOW | SWP_NOOWNERZORDER ); + } + + m_bVisible = bVisible; +} + + +bool CConsoleWnd::IsVisible() const +{ + return m_bVisible; +} + + +void CConsoleWnd::PrintToConsole( const char *pMsg ) +{ + if ( m_nCurrentChars >= 16*1024 ) + { + // Clear the edit control otherwise it'll stop outputting anything. + m_nCurrentChars = 0; + + int nLen = (int)SendMessage( m_hEditControl, EM_GETLIMITTEXT, 0, 0 ); + SendMessage( m_hEditControl, EM_SETSEL, 0, nLen ); + SendMessage( m_hEditControl, EM_REPLACESEL, FALSE, (LPARAM)"" ); + } + + FormatAndSendToEditControl( m_hEditControl, pMsg ); + m_nCurrentChars += (int)strlen( pMsg ); +} + + +void CConsoleWnd::SetTitle( const char *pTitle ) +{ + SetWindowText( m_hWnd, pTitle ); +} + + +int CConsoleWnd::WindowProc( + HWND hwndDlg, // handle to dialog box + UINT uMsg, // message + WPARAM wParam, // first message parameter + LPARAM lParam // second message parameter + ) +{ + lParam = lParam; // avoid compiler warning + + if ( hwndDlg != m_hWnd ) + return false; + + switch ( uMsg ) + { + case WM_SYSCOMMAND: + { + if ( wParam == SC_CLOSE ) + { + if ( m_bDeleteOnClose ) + { + Release(); + } + else + { + SetVisible( false ); + return true; + } + } + } + break; + + case WM_SHOWWINDOW: + { + m_bVisible = (wParam != 0); + } + break; + + case WM_SIZE: + case WM_INITDIALOG: + { + RepositionEditControl(); + } + break; + } + + return false; +} + + +int CConsoleWnd::StaticWindowProc( + HWND hwndDlg, // handle to dialog box + UINT uMsg, // message + WPARAM wParam, // first message parameter + LPARAM lParam // second message parameter + ) +{ + CConsoleWnd *pDlg = (CConsoleWnd*)GetWindowLong( hwndDlg, GWL_USERDATA ); + if ( pDlg ) + return pDlg->WindowProc( hwndDlg, uMsg, wParam, lParam ); + else + return false; +} + + +void CConsoleWnd::RepositionEditControl() +{ + RECT rcMain; + GetClientRect( m_hWnd, &rcMain ); + + RECT rcNew; + rcNew.left = rcMain.left + EDITCONTROL_BORDER_SIZE; + rcNew.right = rcMain.right - EDITCONTROL_BORDER_SIZE; + rcNew.top = rcMain.top + EDITCONTROL_BORDER_SIZE; + rcNew.bottom = rcMain.bottom - EDITCONTROL_BORDER_SIZE; + + SetWindowPos( + m_hEditControl, + NULL, + rcNew.left, + rcNew.top, + rcNew.right - rcNew.left, + rcNew.bottom - rcNew.top, + SWP_NOZORDER ); +} + + +void CConsoleWnd::SetDeleteOnClose( bool bDelete ) +{ + m_bDeleteOnClose = bDelete; +} + + +// ------------------------------------------------------------------------------------ // +// Module interface. +// ------------------------------------------------------------------------------------ // + +void SendToEditControl( HWND hEditControl, const char *pText ) +{ + int nLen = (int)SendMessage( hEditControl, EM_GETLIMITTEXT, 0, 0 ); + SendMessage( hEditControl, EM_SETSEL, nLen, nLen ); + SendMessage( hEditControl, EM_REPLACESEL, FALSE, (LPARAM)pText ); +} + + +void FormatAndSendToEditControl( void *hWnd, const char *pText ) +{ + HWND hEditControl = (HWND)hWnd; + + // Translate \n to \r\n. + char outMsg[1024]; + const char *pIn = pText; + char *pOut = outMsg; + while ( *pIn ) + { + if ( *pIn == '\n' ) + { + *pOut = '\r'; + pOut++; + } + *pOut = *pIn; + + ++pIn; + ++pOut; + + if ( pOut - outMsg >= 1020 ) + { + *pOut = 0; + SendToEditControl( hEditControl, outMsg ); + pOut = outMsg; + } + } + *pOut = 0; + SendToEditControl( hEditControl, outMsg ); +} + + +IConsoleWnd* CreateConsoleWnd( void *hInstance, int dialogResourceID, int editControlID, bool bVisible ) +{ + CConsoleWnd *pWnd = new CConsoleWnd; + + if ( pWnd->Init( hInstance, dialogResourceID, editControlID, bVisible ) ) + { + return pWnd; + } + else + { + pWnd->Release(); + return NULL; + } +} + + + + diff --git a/mp/src/utils/common/consolewnd.h b/mp/src/utils/common/consolewnd.h index 4572ff57..92649e2f 100644 --- a/mp/src/utils/common/consolewnd.h +++ b/mp/src/utils/common/consolewnd.h @@ -1,45 +1,45 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#ifndef CONSOLEWND_H -#define CONSOLEWND_H -#ifdef _WIN32 -#pragma once -#endif - - -class IConsoleWnd -{ -public: - virtual void Release() = 0; - - // Print a message to the console. - virtual void PrintToConsole( const char *pMsg ) = 0; - - // Set the window title. - virtual void SetTitle( const char *pTitle ) = 0; - - // Show and hide the console window. - virtual void SetVisible( bool bVisible ) = 0; - virtual bool IsVisible() const = 0; - - // Normally, the window just hides itself when closed. You can use this to make the window - // automatically go away when they close it. - virtual void SetDeleteOnClose( bool bDelete ) = 0; -}; - - -// Utility functions. - -// This converts adds \r's where necessary and sends the text to the edit control. -void FormatAndSendToEditControl( void *hWnd, const char *pText ); - - -IConsoleWnd* CreateConsoleWnd( void *hInstance, int dialogResourceID, int editControlID, bool bVisible ); - - -#endif // CONSOLEWND_H +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef CONSOLEWND_H +#define CONSOLEWND_H +#ifdef _WIN32 +#pragma once +#endif + + +class IConsoleWnd +{ +public: + virtual void Release() = 0; + + // Print a message to the console. + virtual void PrintToConsole( const char *pMsg ) = 0; + + // Set the window title. + virtual void SetTitle( const char *pTitle ) = 0; + + // Show and hide the console window. + virtual void SetVisible( bool bVisible ) = 0; + virtual bool IsVisible() const = 0; + + // Normally, the window just hides itself when closed. You can use this to make the window + // automatically go away when they close it. + virtual void SetDeleteOnClose( bool bDelete ) = 0; +}; + + +// Utility functions. + +// This converts adds \r's where necessary and sends the text to the edit control. +void FormatAndSendToEditControl( void *hWnd, const char *pText ); + + +IConsoleWnd* CreateConsoleWnd( void *hInstance, int dialogResourceID, int editControlID, bool bVisible ); + + +#endif // CONSOLEWND_H diff --git a/mp/src/utils/common/filesystem_tools.cpp b/mp/src/utils/common/filesystem_tools.cpp index 9714c57a..b1306c59 100644 --- a/mp/src/utils/common/filesystem_tools.cpp +++ b/mp/src/utils/common/filesystem_tools.cpp @@ -1,209 +1,209 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//===========================================================================// - -#if defined( _WIN32 ) && !defined( _X360 ) -#include -#include -#include // _chmod -#elif _LINUX -#include -#endif - -#include -#include -#include "tier1/strtools.h" -#include "filesystem_tools.h" -#include "tier0/icommandline.h" -#include "KeyValues.h" -#include "tier2/tier2.h" - -#ifdef MPI - #include "vmpi.h" - #include "vmpi_tools_shared.h" - #include "vmpi_filesystem.h" -#endif - -// memdbgon must be the last include file in a .cpp file!!! -#include - - -// ---------------------------------------------------------------------------------------------------- // -// Module interface. -// ---------------------------------------------------------------------------------------------------- // - -IBaseFileSystem *g_pFileSystem = NULL; - -// These are only used for tools that need the search paths that the engine's file system provides. -CSysModule *g_pFullFileSystemModule = NULL; - -// --------------------------------------------------------------------------- -// -// These are the base paths that everything will be referenced relative to (textures especially) -// All of these directories include the trailing slash -// -// --------------------------------------------------------------------------- - -// This is the the path of the initial source file (relative to the cwd) -char qdir[1024]; - -// This is the base engine + mod-specific game dir (e.g. "c:\tf2\mytfmod\") -char gamedir[1024]; - -void FileSystem_SetupStandardDirectories( const char *pFilename, const char *pGameInfoPath ) -{ - // Set qdir. - if ( !pFilename ) - { - pFilename = "."; - } - - Q_MakeAbsolutePath( qdir, sizeof( qdir ), pFilename, NULL ); - Q_StripFilename( qdir ); - Q_strlower( qdir ); - if ( qdir[0] != 0 ) - { - Q_AppendSlash( qdir, sizeof( qdir ) ); - } - - // Set gamedir. - Q_MakeAbsolutePath( gamedir, sizeof( gamedir ), pGameInfoPath ); - Q_AppendSlash( gamedir, sizeof( gamedir ) ); -} - - -bool FileSystem_Init_Normal( const char *pFilename, FSInitType_t initType, bool bOnlyUseDirectoryName ) -{ - if ( initType == FS_INIT_FULL ) - { - // First, get the name of the module - char fileSystemDLLName[MAX_PATH]; - bool bSteam; - if ( FileSystem_GetFileSystemDLLName( fileSystemDLLName, MAX_PATH, bSteam ) != FS_OK ) - return false; - - // If we're under Steam we need extra setup to let us find the proper modules - FileSystem_SetupSteamInstallPath(); - - // Next, load the module, call Connect/Init. - CFSLoadModuleInfo loadModuleInfo; - loadModuleInfo.m_pFileSystemDLLName = fileSystemDLLName; - loadModuleInfo.m_pDirectoryName = pFilename; - loadModuleInfo.m_bOnlyUseDirectoryName = bOnlyUseDirectoryName; - loadModuleInfo.m_ConnectFactory = Sys_GetFactoryThis(); - loadModuleInfo.m_bSteam = bSteam; - loadModuleInfo.m_bToolsMode = true; - if ( FileSystem_LoadFileSystemModule( loadModuleInfo ) != FS_OK ) - return false; - - // Next, mount the content - CFSMountContentInfo mountContentInfo; - mountContentInfo.m_pDirectoryName= loadModuleInfo.m_GameInfoPath; - mountContentInfo.m_pFileSystem = loadModuleInfo.m_pFileSystem; - mountContentInfo.m_bToolsMode = true; - if ( FileSystem_MountContent( mountContentInfo ) != FS_OK ) - return false; - - // Finally, load the search paths. - CFSSearchPathsInit searchPathsInit; - searchPathsInit.m_pDirectoryName = loadModuleInfo.m_GameInfoPath; - searchPathsInit.m_pFileSystem = loadModuleInfo.m_pFileSystem; - if ( FileSystem_LoadSearchPaths( searchPathsInit ) != FS_OK ) - return false; - - // Store the data we got from filesystem_init. - g_pFileSystem = g_pFullFileSystem = loadModuleInfo.m_pFileSystem; - g_pFullFileSystemModule = loadModuleInfo.m_pModule; - - FileSystem_AddSearchPath_Platform( g_pFullFileSystem, loadModuleInfo.m_GameInfoPath ); - - FileSystem_SetupStandardDirectories( pFilename, loadModuleInfo.m_GameInfoPath ); - } - else - { - if ( !Sys_LoadInterface( - "filesystem_stdio", - FILESYSTEM_INTERFACE_VERSION, - &g_pFullFileSystemModule, - (void**)&g_pFullFileSystem ) ) - { - return false; - } - - if ( g_pFullFileSystem->Init() != INIT_OK ) - return false; - - g_pFullFileSystem->RemoveAllSearchPaths(); - g_pFullFileSystem->AddSearchPath( "../platform", "PLATFORM" ); - g_pFullFileSystem->AddSearchPath( ".", "GAME" ); - - g_pFileSystem = g_pFullFileSystem; - } - - return true; -} - - -bool FileSystem_Init( const char *pBSPFilename, int maxMemoryUsage, FSInitType_t initType, bool bOnlyUseFilename ) -{ - Assert( CommandLine()->GetCmdLine() != NULL ); // Should have called CreateCmdLine by now. - - // If this app uses VMPI, then let VMPI intercept all filesystem calls. -#if defined( MPI ) - if ( g_bUseMPI ) - { - if ( g_bMPIMaster ) - { - if ( !FileSystem_Init_Normal( pBSPFilename, initType, bOnlyUseFilename ) ) - return false; - - g_pFileSystem = g_pFullFileSystem = VMPI_FileSystem_Init( maxMemoryUsage, g_pFullFileSystem ); - SendQDirInfo(); - } - else - { - g_pFileSystem = g_pFullFileSystem = VMPI_FileSystem_Init( maxMemoryUsage, NULL ); - RecvQDirInfo(); - } - return true; - } -#endif - - return FileSystem_Init_Normal( pBSPFilename, initType, bOnlyUseFilename ); -} - - -void FileSystem_Term() -{ -#if defined( MPI ) - if ( g_bUseMPI ) - { - g_pFileSystem = g_pFullFileSystem = VMPI_FileSystem_Term(); - } -#endif - - if ( g_pFullFileSystem ) - { - g_pFullFileSystem->Shutdown(); - g_pFullFileSystem = NULL; - g_pFileSystem = NULL; - } - - if ( g_pFullFileSystemModule ) - { - Sys_UnloadModule( g_pFullFileSystemModule ); - g_pFullFileSystemModule = NULL; - } -} - - -CreateInterfaceFn FileSystem_GetFactory() -{ -#if defined( MPI ) - if ( g_bUseMPI ) - return VMPI_FileSystem_GetFactory(); -#endif - return Sys_GetFactory( g_pFullFileSystemModule ); -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//===========================================================================// + +#if defined( _WIN32 ) && !defined( _X360 ) +#include +#include +#include // _chmod +#elif _LINUX +#include +#endif + +#include +#include +#include "tier1/strtools.h" +#include "filesystem_tools.h" +#include "tier0/icommandline.h" +#include "KeyValues.h" +#include "tier2/tier2.h" + +#ifdef MPI + #include "vmpi.h" + #include "vmpi_tools_shared.h" + #include "vmpi_filesystem.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include + + +// ---------------------------------------------------------------------------------------------------- // +// Module interface. +// ---------------------------------------------------------------------------------------------------- // + +IBaseFileSystem *g_pFileSystem = NULL; + +// These are only used for tools that need the search paths that the engine's file system provides. +CSysModule *g_pFullFileSystemModule = NULL; + +// --------------------------------------------------------------------------- +// +// These are the base paths that everything will be referenced relative to (textures especially) +// All of these directories include the trailing slash +// +// --------------------------------------------------------------------------- + +// This is the the path of the initial source file (relative to the cwd) +char qdir[1024]; + +// This is the base engine + mod-specific game dir (e.g. "c:\tf2\mytfmod\") +char gamedir[1024]; + +void FileSystem_SetupStandardDirectories( const char *pFilename, const char *pGameInfoPath ) +{ + // Set qdir. + if ( !pFilename ) + { + pFilename = "."; + } + + Q_MakeAbsolutePath( qdir, sizeof( qdir ), pFilename, NULL ); + Q_StripFilename( qdir ); + Q_strlower( qdir ); + if ( qdir[0] != 0 ) + { + Q_AppendSlash( qdir, sizeof( qdir ) ); + } + + // Set gamedir. + Q_MakeAbsolutePath( gamedir, sizeof( gamedir ), pGameInfoPath ); + Q_AppendSlash( gamedir, sizeof( gamedir ) ); +} + + +bool FileSystem_Init_Normal( const char *pFilename, FSInitType_t initType, bool bOnlyUseDirectoryName ) +{ + if ( initType == FS_INIT_FULL ) + { + // First, get the name of the module + char fileSystemDLLName[MAX_PATH]; + bool bSteam; + if ( FileSystem_GetFileSystemDLLName( fileSystemDLLName, MAX_PATH, bSteam ) != FS_OK ) + return false; + + // If we're under Steam we need extra setup to let us find the proper modules + FileSystem_SetupSteamInstallPath(); + + // Next, load the module, call Connect/Init. + CFSLoadModuleInfo loadModuleInfo; + loadModuleInfo.m_pFileSystemDLLName = fileSystemDLLName; + loadModuleInfo.m_pDirectoryName = pFilename; + loadModuleInfo.m_bOnlyUseDirectoryName = bOnlyUseDirectoryName; + loadModuleInfo.m_ConnectFactory = Sys_GetFactoryThis(); + loadModuleInfo.m_bSteam = bSteam; + loadModuleInfo.m_bToolsMode = true; + if ( FileSystem_LoadFileSystemModule( loadModuleInfo ) != FS_OK ) + return false; + + // Next, mount the content + CFSMountContentInfo mountContentInfo; + mountContentInfo.m_pDirectoryName= loadModuleInfo.m_GameInfoPath; + mountContentInfo.m_pFileSystem = loadModuleInfo.m_pFileSystem; + mountContentInfo.m_bToolsMode = true; + if ( FileSystem_MountContent( mountContentInfo ) != FS_OK ) + return false; + + // Finally, load the search paths. + CFSSearchPathsInit searchPathsInit; + searchPathsInit.m_pDirectoryName = loadModuleInfo.m_GameInfoPath; + searchPathsInit.m_pFileSystem = loadModuleInfo.m_pFileSystem; + if ( FileSystem_LoadSearchPaths( searchPathsInit ) != FS_OK ) + return false; + + // Store the data we got from filesystem_init. + g_pFileSystem = g_pFullFileSystem = loadModuleInfo.m_pFileSystem; + g_pFullFileSystemModule = loadModuleInfo.m_pModule; + + FileSystem_AddSearchPath_Platform( g_pFullFileSystem, loadModuleInfo.m_GameInfoPath ); + + FileSystem_SetupStandardDirectories( pFilename, loadModuleInfo.m_GameInfoPath ); + } + else + { + if ( !Sys_LoadInterface( + "filesystem_stdio", + FILESYSTEM_INTERFACE_VERSION, + &g_pFullFileSystemModule, + (void**)&g_pFullFileSystem ) ) + { + return false; + } + + if ( g_pFullFileSystem->Init() != INIT_OK ) + return false; + + g_pFullFileSystem->RemoveAllSearchPaths(); + g_pFullFileSystem->AddSearchPath( "../platform", "PLATFORM" ); + g_pFullFileSystem->AddSearchPath( ".", "GAME" ); + + g_pFileSystem = g_pFullFileSystem; + } + + return true; +} + + +bool FileSystem_Init( const char *pBSPFilename, int maxMemoryUsage, FSInitType_t initType, bool bOnlyUseFilename ) +{ + Assert( CommandLine()->GetCmdLine() != NULL ); // Should have called CreateCmdLine by now. + + // If this app uses VMPI, then let VMPI intercept all filesystem calls. +#if defined( MPI ) + if ( g_bUseMPI ) + { + if ( g_bMPIMaster ) + { + if ( !FileSystem_Init_Normal( pBSPFilename, initType, bOnlyUseFilename ) ) + return false; + + g_pFileSystem = g_pFullFileSystem = VMPI_FileSystem_Init( maxMemoryUsage, g_pFullFileSystem ); + SendQDirInfo(); + } + else + { + g_pFileSystem = g_pFullFileSystem = VMPI_FileSystem_Init( maxMemoryUsage, NULL ); + RecvQDirInfo(); + } + return true; + } +#endif + + return FileSystem_Init_Normal( pBSPFilename, initType, bOnlyUseFilename ); +} + + +void FileSystem_Term() +{ +#if defined( MPI ) + if ( g_bUseMPI ) + { + g_pFileSystem = g_pFullFileSystem = VMPI_FileSystem_Term(); + } +#endif + + if ( g_pFullFileSystem ) + { + g_pFullFileSystem->Shutdown(); + g_pFullFileSystem = NULL; + g_pFileSystem = NULL; + } + + if ( g_pFullFileSystemModule ) + { + Sys_UnloadModule( g_pFullFileSystemModule ); + g_pFullFileSystemModule = NULL; + } +} + + +CreateInterfaceFn FileSystem_GetFactory() +{ +#if defined( MPI ) + if ( g_bUseMPI ) + return VMPI_FileSystem_GetFactory(); +#endif + return Sys_GetFactory( g_pFullFileSystemModule ); +} diff --git a/mp/src/utils/common/filesystem_tools.h b/mp/src/utils/common/filesystem_tools.h index 09db7b3e..ada82ad5 100644 --- a/mp/src/utils/common/filesystem_tools.h +++ b/mp/src/utils/common/filesystem_tools.h @@ -1,59 +1,59 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//===========================================================================// - -#ifndef FILESYSTEM_TOOLS_H -#define FILESYSTEM_TOOLS_H -#ifdef _WIN32 -#pragma once -#endif - - -#include "filesystem.h" -#include "filesystem_init.h" - - -// This is the the path of the initial source file -extern char qdir[1024]; - -// This is the base engine + mod-specific game dir (e.g. "d:\tf2\mytfmod\") -extern char gamedir[1024]; - - -// ---------------------------------------------------------------------------------------- // -// Filesystem initialization. -// ---------------------------------------------------------------------------------------- // - -enum FSInitType_t -{ - FS_INIT_FULL, // Load gameinfo.txt, maybe use filesystem_steam, and setup search paths. - FS_INIT_COMPATIBILITY_MODE // Load filesystem_stdio and that's it. -}; - -// -// Initializes qdir, and gamedir. Also initializes the VMPI filesystem if MPI is defined. -// -// pFilename can be NULL if you want to rely on vproject and qproject. If it's specified, FileSystem_Init -// will go up directories from pFilename looking for gameinfo.txt (if vproject isn't specified). -// -// If bOnlyUseFilename is true, then it won't use any alternative methods of finding the vproject dir -// (ie: it won't use -game or -vproject or the vproject env var or qproject). -// -bool FileSystem_Init( const char *pFilename, int maxMemoryUsage=0, FSInitType_t initType=FS_INIT_FULL, bool bOnlyUseFilename=false ); -void FileSystem_Term(); - -// Used to connect app-framework based console apps to the filesystem tools -void FileSystem_SetupStandardDirectories( const char *pFilename, const char *pGameInfoPath ); - -CreateInterfaceFn FileSystem_GetFactory( void ); - - -extern IBaseFileSystem *g_pFileSystem; -extern IFileSystem *g_pFullFileSystem; // NOTE: this is here when VMPI is being used, but a VMPI app can - // ONLY use LoadModule/UnloadModule. - - -#endif // FILESYSTEM_TOOLS_H +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef FILESYSTEM_TOOLS_H +#define FILESYSTEM_TOOLS_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "filesystem.h" +#include "filesystem_init.h" + + +// This is the the path of the initial source file +extern char qdir[1024]; + +// This is the base engine + mod-specific game dir (e.g. "d:\tf2\mytfmod\") +extern char gamedir[1024]; + + +// ---------------------------------------------------------------------------------------- // +// Filesystem initialization. +// ---------------------------------------------------------------------------------------- // + +enum FSInitType_t +{ + FS_INIT_FULL, // Load gameinfo.txt, maybe use filesystem_steam, and setup search paths. + FS_INIT_COMPATIBILITY_MODE // Load filesystem_stdio and that's it. +}; + +// +// Initializes qdir, and gamedir. Also initializes the VMPI filesystem if MPI is defined. +// +// pFilename can be NULL if you want to rely on vproject and qproject. If it's specified, FileSystem_Init +// will go up directories from pFilename looking for gameinfo.txt (if vproject isn't specified). +// +// If bOnlyUseFilename is true, then it won't use any alternative methods of finding the vproject dir +// (ie: it won't use -game or -vproject or the vproject env var or qproject). +// +bool FileSystem_Init( const char *pFilename, int maxMemoryUsage=0, FSInitType_t initType=FS_INIT_FULL, bool bOnlyUseFilename=false ); +void FileSystem_Term(); + +// Used to connect app-framework based console apps to the filesystem tools +void FileSystem_SetupStandardDirectories( const char *pFilename, const char *pGameInfoPath ); + +CreateInterfaceFn FileSystem_GetFactory( void ); + + +extern IBaseFileSystem *g_pFileSystem; +extern IFileSystem *g_pFullFileSystem; // NOTE: this is here when VMPI is being used, but a VMPI app can + // ONLY use LoadModule/UnloadModule. + + +#endif // FILESYSTEM_TOOLS_H diff --git a/mp/src/utils/common/map_shared.cpp b/mp/src/utils/common/map_shared.cpp index f1e970c0..e33e20cb 100644 --- a/mp/src/utils/common/map_shared.cpp +++ b/mp/src/utils/common/map_shared.cpp @@ -1,136 +1,136 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include "map_shared.h" -#include "bsplib.h" -#include "cmdlib.h" - - -CMapError g_MapError; -int g_nMapFileVersion; - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *szKey - -// *szValue - -// *pLoadEntity - -// Output : ChunkFileResult_t -//----------------------------------------------------------------------------- -ChunkFileResult_t LoadEntityKeyCallback(const char *szKey, const char *szValue, LoadEntity_t *pLoadEntity) -{ - if (!stricmp(szKey, "classname")) - { - if (!stricmp(szValue, "func_detail")) - { - pLoadEntity->nBaseContents = CONTENTS_DETAIL; - } - else if (!stricmp(szValue, "func_ladder")) - { - pLoadEntity->nBaseContents = CONTENTS_LADDER; - } - else if (!stricmp(szValue, "func_water")) - { - pLoadEntity->nBaseContents = CONTENTS_WATER; - } - } - else if (!stricmp(szKey, "id")) - { - // UNDONE: flag entity errors by ID instead of index - //g_MapError.EntityState( atoi( szValue ) ); - // rename this field since DME code uses this name - SetKeyValue( pLoadEntity->pEntity, "hammerid", szValue ); - return(ChunkFile_Ok); - } - else if( !stricmp( szKey, "mapversion" ) ) - { - // .vmf map revision number - g_MapRevision = atoi( szValue ); - SetKeyValue( pLoadEntity->pEntity, szKey, szValue ); - return ( ChunkFile_Ok ); - } - - SetKeyValue( pLoadEntity->pEntity, szKey, szValue ); - - return(ChunkFile_Ok); -} - - -static ChunkFileResult_t LoadEntityCallback( CChunkFile *pFile, int nParam ) -{ - if (num_entities == MAX_MAP_ENTITIES) - { - // Exits. - g_MapError.ReportError ("num_entities == MAX_MAP_ENTITIES"); - } - - entity_t *mapent = &entities[num_entities]; - num_entities++; - memset(mapent, 0, sizeof(*mapent)); - mapent->numbrushes = 0; - - LoadEntity_t LoadEntity; - LoadEntity.pEntity = mapent; - - // No default flags/contents - LoadEntity.nBaseFlags = 0; - LoadEntity.nBaseContents = 0; - - // - // Read the entity chunk. - // - ChunkFileResult_t eResult = pFile->ReadChunk((KeyHandler_t)LoadEntityKeyCallback, &LoadEntity); - - return eResult; -} - - -bool LoadEntsFromMapFile( char const *pFilename ) -{ - // - // Dummy this up for the texture handling. This can be removed when old .MAP file - // support is removed. - // - g_nMapFileVersion = 400; - - // - // Open the file. - // - CChunkFile File; - ChunkFileResult_t eResult = File.Open( pFilename, ChunkFile_Read ); - - if(eResult == ChunkFile_Ok) - { - num_entities = 0; - - // - // Set up handlers for the subchunks that we are interested in. - // - CChunkHandlerMap Handlers; - Handlers.AddHandler("entity", (ChunkHandler_t)LoadEntityCallback, 0); - - File.PushHandlers(&Handlers); - - // - // Read the sub-chunks. We ignore keys in the root of the file. - // - while (eResult == ChunkFile_Ok) - { - eResult = File.ReadChunk(); - } - - File.PopHandlers(); - return true; - } - else - { - Error("Error in LoadEntsFromMapFile (in-memory file): %s.\n", File.GetErrorText(eResult)); - return false; - } -} - - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "map_shared.h" +#include "bsplib.h" +#include "cmdlib.h" + + +CMapError g_MapError; +int g_nMapFileVersion; + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *szKey - +// *szValue - +// *pLoadEntity - +// Output : ChunkFileResult_t +//----------------------------------------------------------------------------- +ChunkFileResult_t LoadEntityKeyCallback(const char *szKey, const char *szValue, LoadEntity_t *pLoadEntity) +{ + if (!stricmp(szKey, "classname")) + { + if (!stricmp(szValue, "func_detail")) + { + pLoadEntity->nBaseContents = CONTENTS_DETAIL; + } + else if (!stricmp(szValue, "func_ladder")) + { + pLoadEntity->nBaseContents = CONTENTS_LADDER; + } + else if (!stricmp(szValue, "func_water")) + { + pLoadEntity->nBaseContents = CONTENTS_WATER; + } + } + else if (!stricmp(szKey, "id")) + { + // UNDONE: flag entity errors by ID instead of index + //g_MapError.EntityState( atoi( szValue ) ); + // rename this field since DME code uses this name + SetKeyValue( pLoadEntity->pEntity, "hammerid", szValue ); + return(ChunkFile_Ok); + } + else if( !stricmp( szKey, "mapversion" ) ) + { + // .vmf map revision number + g_MapRevision = atoi( szValue ); + SetKeyValue( pLoadEntity->pEntity, szKey, szValue ); + return ( ChunkFile_Ok ); + } + + SetKeyValue( pLoadEntity->pEntity, szKey, szValue ); + + return(ChunkFile_Ok); +} + + +static ChunkFileResult_t LoadEntityCallback( CChunkFile *pFile, int nParam ) +{ + if (num_entities == MAX_MAP_ENTITIES) + { + // Exits. + g_MapError.ReportError ("num_entities == MAX_MAP_ENTITIES"); + } + + entity_t *mapent = &entities[num_entities]; + num_entities++; + memset(mapent, 0, sizeof(*mapent)); + mapent->numbrushes = 0; + + LoadEntity_t LoadEntity; + LoadEntity.pEntity = mapent; + + // No default flags/contents + LoadEntity.nBaseFlags = 0; + LoadEntity.nBaseContents = 0; + + // + // Read the entity chunk. + // + ChunkFileResult_t eResult = pFile->ReadChunk((KeyHandler_t)LoadEntityKeyCallback, &LoadEntity); + + return eResult; +} + + +bool LoadEntsFromMapFile( char const *pFilename ) +{ + // + // Dummy this up for the texture handling. This can be removed when old .MAP file + // support is removed. + // + g_nMapFileVersion = 400; + + // + // Open the file. + // + CChunkFile File; + ChunkFileResult_t eResult = File.Open( pFilename, ChunkFile_Read ); + + if(eResult == ChunkFile_Ok) + { + num_entities = 0; + + // + // Set up handlers for the subchunks that we are interested in. + // + CChunkHandlerMap Handlers; + Handlers.AddHandler("entity", (ChunkHandler_t)LoadEntityCallback, 0); + + File.PushHandlers(&Handlers); + + // + // Read the sub-chunks. We ignore keys in the root of the file. + // + while (eResult == ChunkFile_Ok) + { + eResult = File.ReadChunk(); + } + + File.PopHandlers(); + return true; + } + else + { + Error("Error in LoadEntsFromMapFile (in-memory file): %s.\n", File.GetErrorText(eResult)); + return false; + } +} + + diff --git a/mp/src/utils/common/map_shared.h b/mp/src/utils/common/map_shared.h index 08e443a0..5f2b2b62 100644 --- a/mp/src/utils/common/map_shared.h +++ b/mp/src/utils/common/map_shared.h @@ -1,91 +1,91 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// - -#ifndef MAP_SHARED_H -#define MAP_SHARED_H -#ifdef _WIN32 -#pragma once -#endif - - -#include "ChunkFile.h" -#include "bsplib.h" -#include "cmdlib.h" - - -struct LoadEntity_t -{ - entity_t *pEntity; - int nID; - int nBaseFlags; - int nBaseContents; -}; - - -class CMapError -{ -public: - - void BrushState( int brushID ) - { - m_brushID = brushID; - } - - void BrushSide( int side ) - { - m_sideIndex = side; - } - - void TextureState( const char *pTextureName ) - { - Q_strncpy( m_textureName, pTextureName, sizeof( m_textureName ) ); - } - - void ClearState( void ) - { - BrushState( 0 ); - BrushSide( 0 ); - TextureState( "Not a Parse error!" ); - } - - //----------------------------------------------------------------------------- - // Purpose: Hook the map parse errors and report brush/ent/texture state - // Input : *pErrorString - - //----------------------------------------------------------------------------- - void ReportError( const char *pErrorString ) - { - Error( "Brush %i: %s\nSide %i\nTexture: %s\n", m_brushID, pErrorString, m_sideIndex, m_textureName ); - } - - //----------------------------------------------------------------------------- - // Purpose: Hook the map parse errors and report brush/ent/texture state without exiting. - // Input : pWarningString - - //----------------------------------------------------------------------------- - void ReportWarning( const char *pWarningString ) - { - printf( "Brush %i, Side %i: %s\n", m_brushID, m_sideIndex, pWarningString ); - } - -private: - - int m_brushID; - int m_sideIndex; - char m_textureName[80]; -}; - - -extern CMapError g_MapError; -extern int g_nMapFileVersion; - - -// Shared mapload code. -ChunkFileResult_t LoadEntityKeyCallback( const char *szKey, const char *szValue, LoadEntity_t *pLoadEntity ); - -// Used by VRAD incremental lighting - only load ents from the file and -// fill in the global entities/num_entities array. -bool LoadEntsFromMapFile( char const *pFilename ); - -#endif // MAP_SHARED_H +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef MAP_SHARED_H +#define MAP_SHARED_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "ChunkFile.h" +#include "bsplib.h" +#include "cmdlib.h" + + +struct LoadEntity_t +{ + entity_t *pEntity; + int nID; + int nBaseFlags; + int nBaseContents; +}; + + +class CMapError +{ +public: + + void BrushState( int brushID ) + { + m_brushID = brushID; + } + + void BrushSide( int side ) + { + m_sideIndex = side; + } + + void TextureState( const char *pTextureName ) + { + Q_strncpy( m_textureName, pTextureName, sizeof( m_textureName ) ); + } + + void ClearState( void ) + { + BrushState( 0 ); + BrushSide( 0 ); + TextureState( "Not a Parse error!" ); + } + + //----------------------------------------------------------------------------- + // Purpose: Hook the map parse errors and report brush/ent/texture state + // Input : *pErrorString - + //----------------------------------------------------------------------------- + void ReportError( const char *pErrorString ) + { + Error( "Brush %i: %s\nSide %i\nTexture: %s\n", m_brushID, pErrorString, m_sideIndex, m_textureName ); + } + + //----------------------------------------------------------------------------- + // Purpose: Hook the map parse errors and report brush/ent/texture state without exiting. + // Input : pWarningString - + //----------------------------------------------------------------------------- + void ReportWarning( const char *pWarningString ) + { + printf( "Brush %i, Side %i: %s\n", m_brushID, m_sideIndex, pWarningString ); + } + +private: + + int m_brushID; + int m_sideIndex; + char m_textureName[80]; +}; + + +extern CMapError g_MapError; +extern int g_nMapFileVersion; + + +// Shared mapload code. +ChunkFileResult_t LoadEntityKeyCallback( const char *szKey, const char *szValue, LoadEntity_t *pLoadEntity ); + +// Used by VRAD incremental lighting - only load ents from the file and +// fill in the global entities/num_entities array. +bool LoadEntsFromMapFile( char const *pFilename ); + +#endif // MAP_SHARED_H diff --git a/mp/src/utils/common/movie.h b/mp/src/utils/common/movie.h index 78ba92fb..f9055296 100644 --- a/mp/src/utils/common/movie.h +++ b/mp/src/utils/common/movie.h @@ -1,34 +1,34 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -// -//=============================================================================// - -#ifndef _MOVIE_H_ -#define _MOVIE_H_ - -/* - movie.h - - definitions and such for dumping screen shots to make a movie -*/ - -typedef struct -{ - unsigned long tag; - unsigned long size; -} movieblockheader_t; - - -typedef struct -{ - short width; - short height; - short depth; -} movieframe_t; - - - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#ifndef _MOVIE_H_ +#define _MOVIE_H_ + +/* + movie.h + + definitions and such for dumping screen shots to make a movie +*/ + +typedef struct +{ + unsigned long tag; + unsigned long size; +} movieblockheader_t; + + +typedef struct +{ + short width; + short height; + short depth; +} movieframe_t; + + + #endif _MOVIE_H_ \ No newline at end of file diff --git a/mp/src/utils/common/mpi_stats.cpp b/mp/src/utils/common/mpi_stats.cpp index 8d9cc5e7..f5840cea 100644 --- a/mp/src/utils/common/mpi_stats.cpp +++ b/mp/src/utils/common/mpi_stats.cpp @@ -1,839 +1,839 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -// Nasty headers! -#include "MySqlDatabase.h" -#include "tier1/strtools.h" -#include "vmpi.h" -#include "vmpi_dispatch.h" -#include "mpi_stats.h" -#include "cmdlib.h" -#include "imysqlwrapper.h" -#include "threadhelpers.h" -#include "vmpi_tools_shared.h" -#include "tier0/icommandline.h" - -/* - --- MySQL code to create the databases, create the users, and set access privileges. --- You only need to ever run this once. - -create database vrad; - -use mysql; - -create user vrad_worker; -create user vmpi_browser; - --- This updates the "user" table, which is checked when someone tries to connect to the database. -grant select,insert,update on vrad.* to vrad_worker; -grant select on vrad.* to vmpi_browser; -flush privileges; - -/* - --- SQL code to (re)create the tables. - --- Master generates a unique job ID (in job_master_start) and sends it to workers. --- Each worker (and the master) make a job_worker_start, link it to the primary job ID, --- get their own unique ID, which represents that process in that job. --- All JobWorkerID fields link to the JobWorkerID field in job_worker_start. - --- NOTE: do a "use vrad" or "use vvis" first, depending on the DB you want to create. - - -use vrad; - - -drop table job_master_start; -create table job_master_start ( - JobID INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, index id( JobID, MachineName(5) ), - BSPFilename TINYTEXT NOT NULL, - StartTime TIMESTAMP NOT NULL, - MachineName TEXT NOT NULL, - RunningTimeMS INTEGER UNSIGNED NOT NULL, - NumWorkers INTEGER UNSIGNED NOT NULL default 0 - ); - -drop table job_master_end; -create table job_master_end ( - JobID INTEGER UNSIGNED NOT NULL, PRIMARY KEY ( JobID ), - NumWorkersConnected SMALLINT UNSIGNED NOT NULL, - NumWorkersDisconnected SMALLINT UNSIGNED NOT NULL, - ErrorText TEXT NOT NULL - ); - -drop table job_worker_start; -create table job_worker_start ( - JobWorkerID INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, - index index_jobid( JobID ), - index index_jobworkerid( JobWorkerID ), - - JobID INTEGER UNSIGNED NOT NULL, -- links to job_master_start::JobID - IsMaster BOOL NOT NULL, -- Set to 1 if this "worker" is the master process. - RunningTimeMS INTEGER UNSIGNED NOT NULL default 0, - MachineName TEXT NOT NULL, - WorkerState SMALLINT UNSIGNED NOT NULL default 0, -- 0 = disconnected, 1 = connected - NumWorkUnits INTEGER UNSIGNED NOT NULL default 0, -- how many work units this worker has completed - CurrentStage TINYTEXT NOT NULL, -- which compile stage is it on - Thread0WU INTEGER NOT NULL default 0, -- which WU thread 0 is on - Thread1WU INTEGER NOT NULL default 0, -- which WU thread 1 is on - Thread2WU INTEGER NOT NULL default 0, -- which WU thread 2 is on - Thread3WU INTEGER NOT NULL default 0 -- which WU thread 3 is on - ); - -drop table text_messages; -create table text_messages ( - JobWorkerID INTEGER UNSIGNED NOT NULL, index id( JobWorkerID, MessageIndex ), - MessageIndex INTEGER UNSIGNED NOT NULL, - Text TEXT NOT NULL - ); - -drop table graph_entry; -create table graph_entry ( - JobWorkerID INTEGER UNSIGNED NOT NULL, index id( JobWorkerID ), - MSSinceJobStart INTEGER UNSIGNED NOT NULL, - BytesSent INTEGER UNSIGNED NOT NULL, - BytesReceived INTEGER UNSIGNED NOT NULL - ); - -drop table events; -create table events ( - JobWorkerID INTEGER UNSIGNED NOT NULL, index id( JobWorkerID ), - Text TEXT NOT NULL - ); -*/ - - - -// Stats set by the app. -int g_nWorkersConnected = 0; -int g_nWorkersDisconnected = 0; - - -DWORD g_StatsStartTime; - -CMySqlDatabase *g_pDB = NULL; - -IMySQL *g_pSQL = NULL; -CSysModule *g_hMySQLDLL = NULL; - -char g_BSPFilename[256]; - -bool g_bMaster = false; -unsigned long g_JobPrimaryID = 0; // This represents this job, but doesn't link to a particular machine. -unsigned long g_JobWorkerID = 0; // A unique key in the DB that represents this machine in this job. -char g_MachineName[MAX_COMPUTERNAME_LENGTH+1] = {0}; - -unsigned long g_CurrentMessageIndex = 0; - - -HANDLE g_hPerfThread = NULL; -DWORD g_PerfThreadID = 0xFEFEFEFE; -HANDLE g_hPerfThreadExitEvent = NULL; - -// These are set by the app and they go into the database. -extern uint64 g_ThreadWUs[4]; - -extern uint64 VMPI_GetNumWorkUnitsCompleted( int iProc ); - - -// ---------------------------------------------------------------------------------------------------- // -// This is a helper class to build queries like the stream IO. -// ---------------------------------------------------------------------------------------------------- // - -class CMySQLQuery -{ -friend class CMySQL; - -public: - // This is like a sprintf, but it will grow the string as necessary. - void Format( const char *pFormat, ... ); - - int Execute( IMySQL *pDB ); - -private: - CUtlVector m_QueryText; -}; - - -void CMySQLQuery::Format( const char *pFormat, ... ) -{ - #define QUERYTEXT_GROWSIZE 1024 - - // This keeps growing the buffer and calling _vsnprintf until the buffer is - // large enough to hold all the data. - m_QueryText.SetSize( QUERYTEXT_GROWSIZE ); - while ( 1 ) - { - va_list marker; - va_start( marker, pFormat ); - int ret = _vsnprintf( m_QueryText.Base(), m_QueryText.Count(), pFormat, marker ); - va_end( marker ); - - if ( ret < 0 ) - { - m_QueryText.SetSize( m_QueryText.Count() + QUERYTEXT_GROWSIZE ); - } - else - { - m_QueryText[ m_QueryText.Count() - 1 ] = 0; - break; - } - } -} - - -int CMySQLQuery::Execute( IMySQL *pDB ) -{ - int ret = pDB->Execute( m_QueryText.Base() ); - m_QueryText.Purge(); - return ret; -} - - - -// ---------------------------------------------------------------------------------------------------- // -// This inserts the necessary backslashes in front of backslashes or quote characters. -// ---------------------------------------------------------------------------------------------------- // - -char* FormatStringForSQL( const char *pText ) -{ - // First, count the quotes in the string. We need to put a backslash in front of each one. - int nChars = 0; - const char *pCur = pText; - while ( *pCur != 0 ) - { - if ( *pCur == '\"' || *pCur == '\\' ) - ++nChars; - - ++pCur; - ++nChars; - } - - pCur = pText; - char *pRetVal = new char[nChars+1]; - for ( int i=0; i < nChars; ) - { - if ( *pCur == '\"' || *pCur == '\\' ) - pRetVal[i++] = '\\'; - - pRetVal[i++] = *pCur; - ++pCur; - } - pRetVal[nChars] = 0; - - return pRetVal; -} - - - -// -------------------------------------------------------------------------------- // -// Commands to add data to the database. -// -------------------------------------------------------------------------------- // -class CSQLDBCommandBase : public ISQLDBCommand -{ -public: - virtual ~CSQLDBCommandBase() - { - } - - virtual void deleteThis() - { - delete this; - } -}; - -class CSQLDBCommand_WorkerStats : public CSQLDBCommandBase -{ -public: - virtual int RunCommand() - { - int nCurConnections = VMPI_GetCurrentNumberOfConnections(); - - - // Update the NumWorkers entry. - char query[2048]; - Q_snprintf( query, sizeof( query ), "update job_master_start set NumWorkers=%d where JobID=%lu", - nCurConnections, - g_JobPrimaryID ); - g_pSQL->Execute( query ); - - - // Update the job_master_worker_stats stuff. - for ( int i=1; i < nCurConnections; i++ ) - { - unsigned long jobWorkerID = VMPI_GetJobWorkerID( i ); - - if ( jobWorkerID != 0xFFFFFFFF ) - { - Q_snprintf( query, sizeof( query ), "update " - "job_worker_start set WorkerState=%d, NumWorkUnits=%d where JobWorkerID=%lu", - VMPI_IsProcConnected( i ), - (int) VMPI_GetNumWorkUnitsCompleted( i ), - VMPI_GetJobWorkerID( i ) - ); - g_pSQL->Execute( query ); - } - } - return 1; - } -}; - -class CSQLDBCommand_JobMasterEnd : public CSQLDBCommandBase -{ -public: - - virtual int RunCommand() - { - CMySQLQuery query; - query.Format( "insert into job_master_end values ( %lu, %d, %d, \"no errors\" )", g_JobPrimaryID, g_nWorkersConnected, g_nWorkersDisconnected ); - query.Execute( g_pSQL ); - - // Now set RunningTimeMS. - unsigned long runningTimeMS = GetTickCount() - g_StatsStartTime; - query.Format( "update job_master_start set RunningTimeMS=%lu where JobID=%lu", runningTimeMS, g_JobPrimaryID ); - query.Execute( g_pSQL ); - return 1; - } -}; - - -void UpdateJobWorkerRunningTime() -{ - unsigned long runningTimeMS = GetTickCount() - g_StatsStartTime; - - char curStage[256]; - VMPI_GetCurrentStage( curStage, sizeof( curStage ) ); - - CMySQLQuery query; - query.Format( "update job_worker_start set RunningTimeMS=%lu, CurrentStage=\"%s\", " - "Thread0WU=%d, Thread1WU=%d, Thread2WU=%d, Thread3WU=%d where JobWorkerID=%lu", - runningTimeMS, - curStage, - (int) g_ThreadWUs[0], - (int) g_ThreadWUs[1], - (int) g_ThreadWUs[2], - (int) g_ThreadWUs[3], - g_JobWorkerID ); - query.Execute( g_pSQL ); -} - - -class CSQLDBCommand_GraphEntry : public CSQLDBCommandBase -{ -public: - - CSQLDBCommand_GraphEntry( DWORD msTime, DWORD nBytesSent, DWORD nBytesReceived ) - { - m_msTime = msTime; - m_nBytesSent = nBytesSent; - m_nBytesReceived = nBytesReceived; - } - - virtual int RunCommand() - { - CMySQLQuery query; - query.Format( "insert into graph_entry (JobWorkerID, MSSinceJobStart, BytesSent, BytesReceived) " - "values ( %lu, %lu, %lu, %lu )", - g_JobWorkerID, - m_msTime, - m_nBytesSent, - m_nBytesReceived ); - - query.Execute( g_pSQL ); - - UpdateJobWorkerRunningTime(); - - ++g_CurrentMessageIndex; - return 1; - } - - DWORD m_nBytesSent; - DWORD m_nBytesReceived; - DWORD m_msTime; -}; - - - -class CSQLDBCommand_TextMessage : public CSQLDBCommandBase -{ -public: - - CSQLDBCommand_TextMessage( const char *pText ) - { - m_pText = FormatStringForSQL( pText ); - } - - virtual ~CSQLDBCommand_TextMessage() - { - delete [] m_pText; - } - - virtual int RunCommand() - { - CMySQLQuery query; - query.Format( "insert into text_messages (JobWorkerID, MessageIndex, Text) values ( %lu, %lu, \"%s\" )", g_JobWorkerID, g_CurrentMessageIndex, m_pText ); - query.Execute( g_pSQL ); - - ++g_CurrentMessageIndex; - return 1; - } - - char *m_pText; -}; - - -// -------------------------------------------------------------------------------- // -// Internal helpers. -// -------------------------------------------------------------------------------- // - -// This is the spew output before it has connected to the MySQL database. -CCriticalSection g_SpewTextCS; -CUtlVector g_SpewText( 1024 ); - - -void VMPI_Stats_SpewHook( const char *pMsg ) -{ - CCriticalSectionLock csLock( &g_SpewTextCS ); - csLock.Lock(); - - // Queue the text up so we can send it to the DB right away when we connect. - g_SpewText.AddMultipleToTail( strlen( pMsg ), pMsg ); -} - - -void PerfThread_SendSpewText() -{ - // Send the spew text to the database. - CCriticalSectionLock csLock( &g_SpewTextCS ); - csLock.Lock(); - - if ( g_SpewText.Count() > 0 ) - { - g_SpewText.AddToTail( 0 ); - - if ( g_bMPI_StatsTextOutput ) - { - g_pDB->AddCommandToQueue( new CSQLDBCommand_TextMessage( g_SpewText.Base() ), NULL ); - } - else - { - // Just show one message in the vmpi_job_watch window to let them know that they need - // to use a command line option to get the output. - static bool bFirst = true; - if ( bFirst ) - { - char msg[512]; - V_snprintf( msg, sizeof( msg ), "%s not enabled", VMPI_GetParamString( mpi_Stats_TextOutput ) ); - bFirst = false; - g_pDB->AddCommandToQueue( new CSQLDBCommand_TextMessage( msg ), NULL ); - } - } - - g_SpewText.RemoveAll(); - } - - csLock.Unlock(); -} - - -void PerfThread_AddGraphEntry( DWORD startTicks, DWORD &lastSent, DWORD &lastReceived ) -{ - // Send the graph entry with data transmission info. - DWORD curSent = g_nBytesSent + g_nMulticastBytesSent; - DWORD curReceived = g_nBytesReceived + g_nMulticastBytesReceived; - - g_pDB->AddCommandToQueue( - new CSQLDBCommand_GraphEntry( - GetTickCount() - startTicks, - curSent - lastSent, - curReceived - lastReceived ), - NULL ); - - lastSent = curSent; - lastReceived = curReceived; -} - - -// This function adds a graph_entry into the database periodically. -DWORD WINAPI PerfThreadFn( LPVOID pParameter ) -{ - DWORD lastSent = 0; - DWORD lastReceived = 0; - DWORD startTicks = GetTickCount(); - - while ( WaitForSingleObject( g_hPerfThreadExitEvent, 1000 ) != WAIT_OBJECT_0 ) - { - PerfThread_AddGraphEntry( startTicks, lastSent, lastReceived ); - - // Send updates for text output. - PerfThread_SendSpewText(); - - // If we're the master, update all the worker stats. - if ( g_bMaster ) - { - g_pDB->AddCommandToQueue( - new CSQLDBCommand_WorkerStats, - NULL ); - } - } - - // Add the remaining text and one last graph entry (which will include the current stage info). - PerfThread_SendSpewText(); - PerfThread_AddGraphEntry( startTicks, lastSent, lastReceived ); - - SetEvent( g_hPerfThreadExitEvent ); - return 0; -} - - -// -------------------------------------------------------------------------------- // -// VMPI_Stats interface. -// -------------------------------------------------------------------------------- // - -void VMPI_Stats_InstallSpewHook() -{ - InstallExtraSpewHook( VMPI_Stats_SpewHook ); -} - - -void UnloadMySQLWrapper() -{ - if ( g_hMySQLDLL ) - { - if ( g_pSQL ) - { - g_pSQL->Release(); - g_pSQL = NULL; - } - - Sys_UnloadModule( g_hMySQLDLL ); - g_hMySQLDLL = NULL; - } -} - - -bool LoadMySQLWrapper( - const char *pHostName, - const char *pDBName, - const char *pUserName - ) -{ - UnloadMySQLWrapper(); - - // Load the DLL and the interface. - if ( !Sys_LoadInterface( "mysql_wrapper", MYSQL_WRAPPER_VERSION_NAME, &g_hMySQLDLL, (void**)&g_pSQL ) ) - return false; - - // Try to init the database. - if ( !g_pSQL->InitMySQL( pDBName, pHostName, pUserName ) ) - { - UnloadMySQLWrapper(); - return false; - } - - return true; -} - - -bool VMPI_Stats_Init_Master( - const char *pHostName, - const char *pDBName, - const char *pUserName, - const char *pBSPFilename, - unsigned long *pDBJobID ) -{ - Assert( !g_pDB ); - - g_bMaster = true; - - // Connect the database. - g_pDB = new CMySqlDatabase; - if ( !g_pDB || !g_pDB->Initialize() || !LoadMySQLWrapper( pHostName, pDBName, pUserName ) ) - { - delete g_pDB; - g_pDB = NULL; - return false; - } - - DWORD size = sizeof( g_MachineName ); - GetComputerName( g_MachineName, &size ); - - // Create the job_master_start row. - Q_FileBase( pBSPFilename, g_BSPFilename, sizeof( g_BSPFilename ) ); - - g_JobPrimaryID = 0; - CMySQLQuery query; - query.Format( "insert into job_master_start ( BSPFilename, StartTime, MachineName, RunningTimeMS ) values ( \"%s\", null, \"%s\", %lu )", g_BSPFilename, g_MachineName, RUNNINGTIME_MS_SENTINEL ); - query.Execute( g_pSQL ); - - g_JobPrimaryID = g_pSQL->InsertID(); - if ( g_JobPrimaryID == 0 ) - { - delete g_pDB; - g_pDB = NULL; - return false; - } - - - // Now init the worker portion. - *pDBJobID = g_JobPrimaryID; - return VMPI_Stats_Init_Worker( NULL, NULL, NULL, g_JobPrimaryID ); -} - - - -bool VMPI_Stats_Init_Worker( const char *pHostName, const char *pDBName, const char *pUserName, unsigned long DBJobID ) -{ - g_StatsStartTime = GetTickCount(); - - // If pDBServerName is null, then we're the master and we just want to make the job_worker_start entry. - if ( pHostName ) - { - Assert( !g_pDB ); - - // Connect the database. - g_pDB = new CMySqlDatabase; - if ( !g_pDB || !g_pDB->Initialize() || !LoadMySQLWrapper( pHostName, pDBName, pUserName ) ) - { - delete g_pDB; - g_pDB = NULL; - return false; - } - - // Get our machine name to store in the database. - DWORD size = sizeof( g_MachineName ); - GetComputerName( g_MachineName, &size ); - } - - - g_JobPrimaryID = DBJobID; - g_JobWorkerID = 0; - - CMySQLQuery query; - query.Format( "insert into job_worker_start ( JobID, CurrentStage, IsMaster, MachineName ) values ( %lu, \"none\", %d, \"%s\" )", - g_JobPrimaryID, g_bMaster, g_MachineName ); - query.Execute( g_pSQL ); - - g_JobWorkerID = g_pSQL->InsertID(); - if ( g_JobWorkerID == 0 ) - { - delete g_pDB; - g_pDB = NULL; - return false; - } - - // Now create a thread that samples perf data and stores it in the database. - g_hPerfThreadExitEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); - g_hPerfThread = CreateThread( - NULL, - 0, - PerfThreadFn, - NULL, - 0, - &g_PerfThreadID ); - - return true; -} - - -void VMPI_Stats_Term() -{ - if ( !g_pDB ) - return; - - // Stop the thread. - SetEvent( g_hPerfThreadExitEvent ); - WaitForSingleObject( g_hPerfThread, INFINITE ); - - CloseHandle( g_hPerfThreadExitEvent ); - g_hPerfThreadExitEvent = NULL; - - CloseHandle( g_hPerfThread ); - g_hPerfThread = NULL; - - if ( g_bMaster ) - { - // (Write a job_master_end entry here). - g_pDB->AddCommandToQueue( new CSQLDBCommand_JobMasterEnd, NULL ); - } - - // Wait for up to a second for the DB to finish writing its data. - DWORD startTime = GetTickCount(); - while ( GetTickCount() - startTime < 1000 ) - { - if ( g_pDB->QueriesInOutQueue() == 0 ) - break; - } - - delete g_pDB; - g_pDB = NULL; - - UnloadMySQLWrapper(); -} - - -static bool ReadStringFromFile( FILE *fp, char *pStr, int strSize ) -{ - int i=0; - for ( i; i < strSize-2; i++ ) - { - if ( fread( &pStr[i], 1, 1, fp ) != 1 || - pStr[i] == '\n' ) - { - break; - } - } - - pStr[i] = 0; - return i != 0; -} - - -// This looks for pDBInfoFilename in the same path as pBaseExeFilename. -// The file has 3 lines: machine name (with database), database name, username -void GetDBInfo( const char *pDBInfoFilename, CDBInfo *pInfo ) -{ - char baseExeFilename[512]; - if ( !GetModuleFileName( GetModuleHandle( NULL ), baseExeFilename, sizeof( baseExeFilename ) ) ) - Error( "GetModuleFileName failed." ); - - // Look for the info file in the same directory as the exe. - char dbInfoFilename[512]; - Q_strncpy( dbInfoFilename, baseExeFilename, sizeof( dbInfoFilename ) ); - Q_StripFilename( dbInfoFilename ); - - if ( dbInfoFilename[0] == 0 ) - Q_strncpy( dbInfoFilename, ".", sizeof( dbInfoFilename ) ); - - Q_strncat( dbInfoFilename, "/", sizeof( dbInfoFilename ), COPY_ALL_CHARACTERS ); - Q_strncat( dbInfoFilename, pDBInfoFilename, sizeof( dbInfoFilename ), COPY_ALL_CHARACTERS ); - - FILE *fp = fopen( dbInfoFilename, "rt" ); - if ( !fp ) - { - Error( "Can't open %s for database info.\n", dbInfoFilename ); - } - - if ( !ReadStringFromFile( fp, pInfo->m_HostName, sizeof( pInfo->m_HostName ) ) || - !ReadStringFromFile( fp, pInfo->m_DBName, sizeof( pInfo->m_DBName ) ) || - !ReadStringFromFile( fp, pInfo->m_UserName, sizeof( pInfo->m_UserName ) ) - ) - { - Error( "%s is not a valid database info file.\n", dbInfoFilename ); - } - - fclose( fp ); -} - - -void RunJobWatchApp( char *pCmdLine ) -{ - STARTUPINFO si; - memset( &si, 0, sizeof( si ) ); - si.cb = sizeof( si ); - - PROCESS_INFORMATION pi; - memset( &pi, 0, sizeof( pi ) ); - - // Working directory should be the same as our exe's directory. - char dirName[512]; - if ( GetModuleFileName( NULL, dirName, sizeof( dirName ) ) != 0 ) - { - char *s1 = V_strrchr( dirName, '\\' ); - char *s2 = V_strrchr( dirName, '/' ); - if ( s1 || s2 ) - { - // Get rid of the last slash. - s1 = max( s1, s2 ); - s1[0] = 0; - - if ( !CreateProcess( - NULL, - pCmdLine, - NULL, // security - NULL, - TRUE, - 0, // flags - NULL, // environment - dirName, // current directory - &si, - &pi ) ) - { - Warning( "%s - error launching '%s'\n", VMPI_GetParamString( mpi_Job_Watch ), pCmdLine ); - } - } - } -} - - -void StatsDB_InitStatsDatabase( - int argc, - char **argv, - const char *pDBInfoFilename ) -{ - // Did they disable the stats database? - if ( !g_bMPI_Stats && !VMPI_IsParamUsed( mpi_Job_Watch ) ) - return; - - unsigned long jobPrimaryID; - - // Now open the DB. - if ( g_bMPIMaster ) - { - CDBInfo dbInfo; - GetDBInfo( pDBInfoFilename, &dbInfo ); - - if ( !VMPI_Stats_Init_Master( dbInfo.m_HostName, dbInfo.m_DBName, dbInfo.m_UserName, argv[argc-1], &jobPrimaryID ) ) - { - Warning( "VMPI_Stats_Init_Master( %s, %s, %s ) failed.\n", dbInfo.m_HostName, dbInfo.m_DBName, dbInfo.m_UserName ); - - // Tell the workers not to use stats. - dbInfo.m_HostName[0] = 0; - } - - char cmdLine[2048]; - Q_snprintf( cmdLine, sizeof( cmdLine ), "vmpi_job_watch -JobID %d", jobPrimaryID ); - - Msg( "\nTo watch this job, run this command line:\n%s\n\n", cmdLine ); - - if ( VMPI_IsParamUsed( mpi_Job_Watch ) ) - { - // Convenience thing to automatically launch the job watch for this job. - RunJobWatchApp( cmdLine ); - } - - // Send the database info to all the workers. - SendDBInfo( &dbInfo, jobPrimaryID ); - } - else - { - // Wait to get DB info so we can connect to the MySQL database. - CDBInfo dbInfo; - unsigned long jobPrimaryID; - RecvDBInfo( &dbInfo, &jobPrimaryID ); - - if ( dbInfo.m_HostName[0] != 0 ) - { - if ( !VMPI_Stats_Init_Worker( dbInfo.m_HostName, dbInfo.m_DBName, dbInfo.m_UserName, jobPrimaryID ) ) - Error( "VMPI_Stats_Init_Worker( %s, %s, %s, %d ) failed.\n", dbInfo.m_HostName, dbInfo.m_DBName, dbInfo.m_UserName, jobPrimaryID ); - } - } -} - - -unsigned long StatsDB_GetUniqueJobID() -{ - return g_JobPrimaryID; -} - - -unsigned long VMPI_Stats_GetJobWorkerID() -{ - return g_JobWorkerID; +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +// Nasty headers! +#include "MySqlDatabase.h" +#include "tier1/strtools.h" +#include "vmpi.h" +#include "vmpi_dispatch.h" +#include "mpi_stats.h" +#include "cmdlib.h" +#include "imysqlwrapper.h" +#include "threadhelpers.h" +#include "vmpi_tools_shared.h" +#include "tier0/icommandline.h" + +/* + +-- MySQL code to create the databases, create the users, and set access privileges. +-- You only need to ever run this once. + +create database vrad; + +use mysql; + +create user vrad_worker; +create user vmpi_browser; + +-- This updates the "user" table, which is checked when someone tries to connect to the database. +grant select,insert,update on vrad.* to vrad_worker; +grant select on vrad.* to vmpi_browser; +flush privileges; + +/* + +-- SQL code to (re)create the tables. + +-- Master generates a unique job ID (in job_master_start) and sends it to workers. +-- Each worker (and the master) make a job_worker_start, link it to the primary job ID, +-- get their own unique ID, which represents that process in that job. +-- All JobWorkerID fields link to the JobWorkerID field in job_worker_start. + +-- NOTE: do a "use vrad" or "use vvis" first, depending on the DB you want to create. + + +use vrad; + + +drop table job_master_start; +create table job_master_start ( + JobID INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, index id( JobID, MachineName(5) ), + BSPFilename TINYTEXT NOT NULL, + StartTime TIMESTAMP NOT NULL, + MachineName TEXT NOT NULL, + RunningTimeMS INTEGER UNSIGNED NOT NULL, + NumWorkers INTEGER UNSIGNED NOT NULL default 0 + ); + +drop table job_master_end; +create table job_master_end ( + JobID INTEGER UNSIGNED NOT NULL, PRIMARY KEY ( JobID ), + NumWorkersConnected SMALLINT UNSIGNED NOT NULL, + NumWorkersDisconnected SMALLINT UNSIGNED NOT NULL, + ErrorText TEXT NOT NULL + ); + +drop table job_worker_start; +create table job_worker_start ( + JobWorkerID INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + index index_jobid( JobID ), + index index_jobworkerid( JobWorkerID ), + + JobID INTEGER UNSIGNED NOT NULL, -- links to job_master_start::JobID + IsMaster BOOL NOT NULL, -- Set to 1 if this "worker" is the master process. + RunningTimeMS INTEGER UNSIGNED NOT NULL default 0, + MachineName TEXT NOT NULL, + WorkerState SMALLINT UNSIGNED NOT NULL default 0, -- 0 = disconnected, 1 = connected + NumWorkUnits INTEGER UNSIGNED NOT NULL default 0, -- how many work units this worker has completed + CurrentStage TINYTEXT NOT NULL, -- which compile stage is it on + Thread0WU INTEGER NOT NULL default 0, -- which WU thread 0 is on + Thread1WU INTEGER NOT NULL default 0, -- which WU thread 1 is on + Thread2WU INTEGER NOT NULL default 0, -- which WU thread 2 is on + Thread3WU INTEGER NOT NULL default 0 -- which WU thread 3 is on + ); + +drop table text_messages; +create table text_messages ( + JobWorkerID INTEGER UNSIGNED NOT NULL, index id( JobWorkerID, MessageIndex ), + MessageIndex INTEGER UNSIGNED NOT NULL, + Text TEXT NOT NULL + ); + +drop table graph_entry; +create table graph_entry ( + JobWorkerID INTEGER UNSIGNED NOT NULL, index id( JobWorkerID ), + MSSinceJobStart INTEGER UNSIGNED NOT NULL, + BytesSent INTEGER UNSIGNED NOT NULL, + BytesReceived INTEGER UNSIGNED NOT NULL + ); + +drop table events; +create table events ( + JobWorkerID INTEGER UNSIGNED NOT NULL, index id( JobWorkerID ), + Text TEXT NOT NULL + ); +*/ + + + +// Stats set by the app. +int g_nWorkersConnected = 0; +int g_nWorkersDisconnected = 0; + + +DWORD g_StatsStartTime; + +CMySqlDatabase *g_pDB = NULL; + +IMySQL *g_pSQL = NULL; +CSysModule *g_hMySQLDLL = NULL; + +char g_BSPFilename[256]; + +bool g_bMaster = false; +unsigned long g_JobPrimaryID = 0; // This represents this job, but doesn't link to a particular machine. +unsigned long g_JobWorkerID = 0; // A unique key in the DB that represents this machine in this job. +char g_MachineName[MAX_COMPUTERNAME_LENGTH+1] = {0}; + +unsigned long g_CurrentMessageIndex = 0; + + +HANDLE g_hPerfThread = NULL; +DWORD g_PerfThreadID = 0xFEFEFEFE; +HANDLE g_hPerfThreadExitEvent = NULL; + +// These are set by the app and they go into the database. +extern uint64 g_ThreadWUs[4]; + +extern uint64 VMPI_GetNumWorkUnitsCompleted( int iProc ); + + +// ---------------------------------------------------------------------------------------------------- // +// This is a helper class to build queries like the stream IO. +// ---------------------------------------------------------------------------------------------------- // + +class CMySQLQuery +{ +friend class CMySQL; + +public: + // This is like a sprintf, but it will grow the string as necessary. + void Format( const char *pFormat, ... ); + + int Execute( IMySQL *pDB ); + +private: + CUtlVector m_QueryText; +}; + + +void CMySQLQuery::Format( const char *pFormat, ... ) +{ + #define QUERYTEXT_GROWSIZE 1024 + + // This keeps growing the buffer and calling _vsnprintf until the buffer is + // large enough to hold all the data. + m_QueryText.SetSize( QUERYTEXT_GROWSIZE ); + while ( 1 ) + { + va_list marker; + va_start( marker, pFormat ); + int ret = _vsnprintf( m_QueryText.Base(), m_QueryText.Count(), pFormat, marker ); + va_end( marker ); + + if ( ret < 0 ) + { + m_QueryText.SetSize( m_QueryText.Count() + QUERYTEXT_GROWSIZE ); + } + else + { + m_QueryText[ m_QueryText.Count() - 1 ] = 0; + break; + } + } +} + + +int CMySQLQuery::Execute( IMySQL *pDB ) +{ + int ret = pDB->Execute( m_QueryText.Base() ); + m_QueryText.Purge(); + return ret; +} + + + +// ---------------------------------------------------------------------------------------------------- // +// This inserts the necessary backslashes in front of backslashes or quote characters. +// ---------------------------------------------------------------------------------------------------- // + +char* FormatStringForSQL( const char *pText ) +{ + // First, count the quotes in the string. We need to put a backslash in front of each one. + int nChars = 0; + const char *pCur = pText; + while ( *pCur != 0 ) + { + if ( *pCur == '\"' || *pCur == '\\' ) + ++nChars; + + ++pCur; + ++nChars; + } + + pCur = pText; + char *pRetVal = new char[nChars+1]; + for ( int i=0; i < nChars; ) + { + if ( *pCur == '\"' || *pCur == '\\' ) + pRetVal[i++] = '\\'; + + pRetVal[i++] = *pCur; + ++pCur; + } + pRetVal[nChars] = 0; + + return pRetVal; +} + + + +// -------------------------------------------------------------------------------- // +// Commands to add data to the database. +// -------------------------------------------------------------------------------- // +class CSQLDBCommandBase : public ISQLDBCommand +{ +public: + virtual ~CSQLDBCommandBase() + { + } + + virtual void deleteThis() + { + delete this; + } +}; + +class CSQLDBCommand_WorkerStats : public CSQLDBCommandBase +{ +public: + virtual int RunCommand() + { + int nCurConnections = VMPI_GetCurrentNumberOfConnections(); + + + // Update the NumWorkers entry. + char query[2048]; + Q_snprintf( query, sizeof( query ), "update job_master_start set NumWorkers=%d where JobID=%lu", + nCurConnections, + g_JobPrimaryID ); + g_pSQL->Execute( query ); + + + // Update the job_master_worker_stats stuff. + for ( int i=1; i < nCurConnections; i++ ) + { + unsigned long jobWorkerID = VMPI_GetJobWorkerID( i ); + + if ( jobWorkerID != 0xFFFFFFFF ) + { + Q_snprintf( query, sizeof( query ), "update " + "job_worker_start set WorkerState=%d, NumWorkUnits=%d where JobWorkerID=%lu", + VMPI_IsProcConnected( i ), + (int) VMPI_GetNumWorkUnitsCompleted( i ), + VMPI_GetJobWorkerID( i ) + ); + g_pSQL->Execute( query ); + } + } + return 1; + } +}; + +class CSQLDBCommand_JobMasterEnd : public CSQLDBCommandBase +{ +public: + + virtual int RunCommand() + { + CMySQLQuery query; + query.Format( "insert into job_master_end values ( %lu, %d, %d, \"no errors\" )", g_JobPrimaryID, g_nWorkersConnected, g_nWorkersDisconnected ); + query.Execute( g_pSQL ); + + // Now set RunningTimeMS. + unsigned long runningTimeMS = GetTickCount() - g_StatsStartTime; + query.Format( "update job_master_start set RunningTimeMS=%lu where JobID=%lu", runningTimeMS, g_JobPrimaryID ); + query.Execute( g_pSQL ); + return 1; + } +}; + + +void UpdateJobWorkerRunningTime() +{ + unsigned long runningTimeMS = GetTickCount() - g_StatsStartTime; + + char curStage[256]; + VMPI_GetCurrentStage( curStage, sizeof( curStage ) ); + + CMySQLQuery query; + query.Format( "update job_worker_start set RunningTimeMS=%lu, CurrentStage=\"%s\", " + "Thread0WU=%d, Thread1WU=%d, Thread2WU=%d, Thread3WU=%d where JobWorkerID=%lu", + runningTimeMS, + curStage, + (int) g_ThreadWUs[0], + (int) g_ThreadWUs[1], + (int) g_ThreadWUs[2], + (int) g_ThreadWUs[3], + g_JobWorkerID ); + query.Execute( g_pSQL ); +} + + +class CSQLDBCommand_GraphEntry : public CSQLDBCommandBase +{ +public: + + CSQLDBCommand_GraphEntry( DWORD msTime, DWORD nBytesSent, DWORD nBytesReceived ) + { + m_msTime = msTime; + m_nBytesSent = nBytesSent; + m_nBytesReceived = nBytesReceived; + } + + virtual int RunCommand() + { + CMySQLQuery query; + query.Format( "insert into graph_entry (JobWorkerID, MSSinceJobStart, BytesSent, BytesReceived) " + "values ( %lu, %lu, %lu, %lu )", + g_JobWorkerID, + m_msTime, + m_nBytesSent, + m_nBytesReceived ); + + query.Execute( g_pSQL ); + + UpdateJobWorkerRunningTime(); + + ++g_CurrentMessageIndex; + return 1; + } + + DWORD m_nBytesSent; + DWORD m_nBytesReceived; + DWORD m_msTime; +}; + + + +class CSQLDBCommand_TextMessage : public CSQLDBCommandBase +{ +public: + + CSQLDBCommand_TextMessage( const char *pText ) + { + m_pText = FormatStringForSQL( pText ); + } + + virtual ~CSQLDBCommand_TextMessage() + { + delete [] m_pText; + } + + virtual int RunCommand() + { + CMySQLQuery query; + query.Format( "insert into text_messages (JobWorkerID, MessageIndex, Text) values ( %lu, %lu, \"%s\" )", g_JobWorkerID, g_CurrentMessageIndex, m_pText ); + query.Execute( g_pSQL ); + + ++g_CurrentMessageIndex; + return 1; + } + + char *m_pText; +}; + + +// -------------------------------------------------------------------------------- // +// Internal helpers. +// -------------------------------------------------------------------------------- // + +// This is the spew output before it has connected to the MySQL database. +CCriticalSection g_SpewTextCS; +CUtlVector g_SpewText( 1024 ); + + +void VMPI_Stats_SpewHook( const char *pMsg ) +{ + CCriticalSectionLock csLock( &g_SpewTextCS ); + csLock.Lock(); + + // Queue the text up so we can send it to the DB right away when we connect. + g_SpewText.AddMultipleToTail( strlen( pMsg ), pMsg ); +} + + +void PerfThread_SendSpewText() +{ + // Send the spew text to the database. + CCriticalSectionLock csLock( &g_SpewTextCS ); + csLock.Lock(); + + if ( g_SpewText.Count() > 0 ) + { + g_SpewText.AddToTail( 0 ); + + if ( g_bMPI_StatsTextOutput ) + { + g_pDB->AddCommandToQueue( new CSQLDBCommand_TextMessage( g_SpewText.Base() ), NULL ); + } + else + { + // Just show one message in the vmpi_job_watch window to let them know that they need + // to use a command line option to get the output. + static bool bFirst = true; + if ( bFirst ) + { + char msg[512]; + V_snprintf( msg, sizeof( msg ), "%s not enabled", VMPI_GetParamString( mpi_Stats_TextOutput ) ); + bFirst = false; + g_pDB->AddCommandToQueue( new CSQLDBCommand_TextMessage( msg ), NULL ); + } + } + + g_SpewText.RemoveAll(); + } + + csLock.Unlock(); +} + + +void PerfThread_AddGraphEntry( DWORD startTicks, DWORD &lastSent, DWORD &lastReceived ) +{ + // Send the graph entry with data transmission info. + DWORD curSent = g_nBytesSent + g_nMulticastBytesSent; + DWORD curReceived = g_nBytesReceived + g_nMulticastBytesReceived; + + g_pDB->AddCommandToQueue( + new CSQLDBCommand_GraphEntry( + GetTickCount() - startTicks, + curSent - lastSent, + curReceived - lastReceived ), + NULL ); + + lastSent = curSent; + lastReceived = curReceived; +} + + +// This function adds a graph_entry into the database periodically. +DWORD WINAPI PerfThreadFn( LPVOID pParameter ) +{ + DWORD lastSent = 0; + DWORD lastReceived = 0; + DWORD startTicks = GetTickCount(); + + while ( WaitForSingleObject( g_hPerfThreadExitEvent, 1000 ) != WAIT_OBJECT_0 ) + { + PerfThread_AddGraphEntry( startTicks, lastSent, lastReceived ); + + // Send updates for text output. + PerfThread_SendSpewText(); + + // If we're the master, update all the worker stats. + if ( g_bMaster ) + { + g_pDB->AddCommandToQueue( + new CSQLDBCommand_WorkerStats, + NULL ); + } + } + + // Add the remaining text and one last graph entry (which will include the current stage info). + PerfThread_SendSpewText(); + PerfThread_AddGraphEntry( startTicks, lastSent, lastReceived ); + + SetEvent( g_hPerfThreadExitEvent ); + return 0; +} + + +// -------------------------------------------------------------------------------- // +// VMPI_Stats interface. +// -------------------------------------------------------------------------------- // + +void VMPI_Stats_InstallSpewHook() +{ + InstallExtraSpewHook( VMPI_Stats_SpewHook ); +} + + +void UnloadMySQLWrapper() +{ + if ( g_hMySQLDLL ) + { + if ( g_pSQL ) + { + g_pSQL->Release(); + g_pSQL = NULL; + } + + Sys_UnloadModule( g_hMySQLDLL ); + g_hMySQLDLL = NULL; + } +} + + +bool LoadMySQLWrapper( + const char *pHostName, + const char *pDBName, + const char *pUserName + ) +{ + UnloadMySQLWrapper(); + + // Load the DLL and the interface. + if ( !Sys_LoadInterface( "mysql_wrapper", MYSQL_WRAPPER_VERSION_NAME, &g_hMySQLDLL, (void**)&g_pSQL ) ) + return false; + + // Try to init the database. + if ( !g_pSQL->InitMySQL( pDBName, pHostName, pUserName ) ) + { + UnloadMySQLWrapper(); + return false; + } + + return true; +} + + +bool VMPI_Stats_Init_Master( + const char *pHostName, + const char *pDBName, + const char *pUserName, + const char *pBSPFilename, + unsigned long *pDBJobID ) +{ + Assert( !g_pDB ); + + g_bMaster = true; + + // Connect the database. + g_pDB = new CMySqlDatabase; + if ( !g_pDB || !g_pDB->Initialize() || !LoadMySQLWrapper( pHostName, pDBName, pUserName ) ) + { + delete g_pDB; + g_pDB = NULL; + return false; + } + + DWORD size = sizeof( g_MachineName ); + GetComputerName( g_MachineName, &size ); + + // Create the job_master_start row. + Q_FileBase( pBSPFilename, g_BSPFilename, sizeof( g_BSPFilename ) ); + + g_JobPrimaryID = 0; + CMySQLQuery query; + query.Format( "insert into job_master_start ( BSPFilename, StartTime, MachineName, RunningTimeMS ) values ( \"%s\", null, \"%s\", %lu )", g_BSPFilename, g_MachineName, RUNNINGTIME_MS_SENTINEL ); + query.Execute( g_pSQL ); + + g_JobPrimaryID = g_pSQL->InsertID(); + if ( g_JobPrimaryID == 0 ) + { + delete g_pDB; + g_pDB = NULL; + return false; + } + + + // Now init the worker portion. + *pDBJobID = g_JobPrimaryID; + return VMPI_Stats_Init_Worker( NULL, NULL, NULL, g_JobPrimaryID ); +} + + + +bool VMPI_Stats_Init_Worker( const char *pHostName, const char *pDBName, const char *pUserName, unsigned long DBJobID ) +{ + g_StatsStartTime = GetTickCount(); + + // If pDBServerName is null, then we're the master and we just want to make the job_worker_start entry. + if ( pHostName ) + { + Assert( !g_pDB ); + + // Connect the database. + g_pDB = new CMySqlDatabase; + if ( !g_pDB || !g_pDB->Initialize() || !LoadMySQLWrapper( pHostName, pDBName, pUserName ) ) + { + delete g_pDB; + g_pDB = NULL; + return false; + } + + // Get our machine name to store in the database. + DWORD size = sizeof( g_MachineName ); + GetComputerName( g_MachineName, &size ); + } + + + g_JobPrimaryID = DBJobID; + g_JobWorkerID = 0; + + CMySQLQuery query; + query.Format( "insert into job_worker_start ( JobID, CurrentStage, IsMaster, MachineName ) values ( %lu, \"none\", %d, \"%s\" )", + g_JobPrimaryID, g_bMaster, g_MachineName ); + query.Execute( g_pSQL ); + + g_JobWorkerID = g_pSQL->InsertID(); + if ( g_JobWorkerID == 0 ) + { + delete g_pDB; + g_pDB = NULL; + return false; + } + + // Now create a thread that samples perf data and stores it in the database. + g_hPerfThreadExitEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); + g_hPerfThread = CreateThread( + NULL, + 0, + PerfThreadFn, + NULL, + 0, + &g_PerfThreadID ); + + return true; +} + + +void VMPI_Stats_Term() +{ + if ( !g_pDB ) + return; + + // Stop the thread. + SetEvent( g_hPerfThreadExitEvent ); + WaitForSingleObject( g_hPerfThread, INFINITE ); + + CloseHandle( g_hPerfThreadExitEvent ); + g_hPerfThreadExitEvent = NULL; + + CloseHandle( g_hPerfThread ); + g_hPerfThread = NULL; + + if ( g_bMaster ) + { + // (Write a job_master_end entry here). + g_pDB->AddCommandToQueue( new CSQLDBCommand_JobMasterEnd, NULL ); + } + + // Wait for up to a second for the DB to finish writing its data. + DWORD startTime = GetTickCount(); + while ( GetTickCount() - startTime < 1000 ) + { + if ( g_pDB->QueriesInOutQueue() == 0 ) + break; + } + + delete g_pDB; + g_pDB = NULL; + + UnloadMySQLWrapper(); +} + + +static bool ReadStringFromFile( FILE *fp, char *pStr, int strSize ) +{ + int i=0; + for ( i; i < strSize-2; i++ ) + { + if ( fread( &pStr[i], 1, 1, fp ) != 1 || + pStr[i] == '\n' ) + { + break; + } + } + + pStr[i] = 0; + return i != 0; +} + + +// This looks for pDBInfoFilename in the same path as pBaseExeFilename. +// The file has 3 lines: machine name (with database), database name, username +void GetDBInfo( const char *pDBInfoFilename, CDBInfo *pInfo ) +{ + char baseExeFilename[512]; + if ( !GetModuleFileName( GetModuleHandle( NULL ), baseExeFilename, sizeof( baseExeFilename ) ) ) + Error( "GetModuleFileName failed." ); + + // Look for the info file in the same directory as the exe. + char dbInfoFilename[512]; + Q_strncpy( dbInfoFilename, baseExeFilename, sizeof( dbInfoFilename ) ); + Q_StripFilename( dbInfoFilename ); + + if ( dbInfoFilename[0] == 0 ) + Q_strncpy( dbInfoFilename, ".", sizeof( dbInfoFilename ) ); + + Q_strncat( dbInfoFilename, "/", sizeof( dbInfoFilename ), COPY_ALL_CHARACTERS ); + Q_strncat( dbInfoFilename, pDBInfoFilename, sizeof( dbInfoFilename ), COPY_ALL_CHARACTERS ); + + FILE *fp = fopen( dbInfoFilename, "rt" ); + if ( !fp ) + { + Error( "Can't open %s for database info.\n", dbInfoFilename ); + } + + if ( !ReadStringFromFile( fp, pInfo->m_HostName, sizeof( pInfo->m_HostName ) ) || + !ReadStringFromFile( fp, pInfo->m_DBName, sizeof( pInfo->m_DBName ) ) || + !ReadStringFromFile( fp, pInfo->m_UserName, sizeof( pInfo->m_UserName ) ) + ) + { + Error( "%s is not a valid database info file.\n", dbInfoFilename ); + } + + fclose( fp ); +} + + +void RunJobWatchApp( char *pCmdLine ) +{ + STARTUPINFO si; + memset( &si, 0, sizeof( si ) ); + si.cb = sizeof( si ); + + PROCESS_INFORMATION pi; + memset( &pi, 0, sizeof( pi ) ); + + // Working directory should be the same as our exe's directory. + char dirName[512]; + if ( GetModuleFileName( NULL, dirName, sizeof( dirName ) ) != 0 ) + { + char *s1 = V_strrchr( dirName, '\\' ); + char *s2 = V_strrchr( dirName, '/' ); + if ( s1 || s2 ) + { + // Get rid of the last slash. + s1 = max( s1, s2 ); + s1[0] = 0; + + if ( !CreateProcess( + NULL, + pCmdLine, + NULL, // security + NULL, + TRUE, + 0, // flags + NULL, // environment + dirName, // current directory + &si, + &pi ) ) + { + Warning( "%s - error launching '%s'\n", VMPI_GetParamString( mpi_Job_Watch ), pCmdLine ); + } + } + } +} + + +void StatsDB_InitStatsDatabase( + int argc, + char **argv, + const char *pDBInfoFilename ) +{ + // Did they disable the stats database? + if ( !g_bMPI_Stats && !VMPI_IsParamUsed( mpi_Job_Watch ) ) + return; + + unsigned long jobPrimaryID; + + // Now open the DB. + if ( g_bMPIMaster ) + { + CDBInfo dbInfo; + GetDBInfo( pDBInfoFilename, &dbInfo ); + + if ( !VMPI_Stats_Init_Master( dbInfo.m_HostName, dbInfo.m_DBName, dbInfo.m_UserName, argv[argc-1], &jobPrimaryID ) ) + { + Warning( "VMPI_Stats_Init_Master( %s, %s, %s ) failed.\n", dbInfo.m_HostName, dbInfo.m_DBName, dbInfo.m_UserName ); + + // Tell the workers not to use stats. + dbInfo.m_HostName[0] = 0; + } + + char cmdLine[2048]; + Q_snprintf( cmdLine, sizeof( cmdLine ), "vmpi_job_watch -JobID %d", jobPrimaryID ); + + Msg( "\nTo watch this job, run this command line:\n%s\n\n", cmdLine ); + + if ( VMPI_IsParamUsed( mpi_Job_Watch ) ) + { + // Convenience thing to automatically launch the job watch for this job. + RunJobWatchApp( cmdLine ); + } + + // Send the database info to all the workers. + SendDBInfo( &dbInfo, jobPrimaryID ); + } + else + { + // Wait to get DB info so we can connect to the MySQL database. + CDBInfo dbInfo; + unsigned long jobPrimaryID; + RecvDBInfo( &dbInfo, &jobPrimaryID ); + + if ( dbInfo.m_HostName[0] != 0 ) + { + if ( !VMPI_Stats_Init_Worker( dbInfo.m_HostName, dbInfo.m_DBName, dbInfo.m_UserName, jobPrimaryID ) ) + Error( "VMPI_Stats_Init_Worker( %s, %s, %s, %d ) failed.\n", dbInfo.m_HostName, dbInfo.m_DBName, dbInfo.m_UserName, jobPrimaryID ); + } + } +} + + +unsigned long StatsDB_GetUniqueJobID() +{ + return g_JobPrimaryID; +} + + +unsigned long VMPI_Stats_GetJobWorkerID() +{ + return g_JobWorkerID; } \ No newline at end of file diff --git a/mp/src/utils/common/mpi_stats.h b/mp/src/utils/common/mpi_stats.h index 10aa6162..841db3a2 100644 --- a/mp/src/utils/common/mpi_stats.h +++ b/mp/src/utils/common/mpi_stats.h @@ -1,59 +1,59 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#ifndef MPI_STATS_H -#define MPI_STATS_H -#ifdef _WIN32 -#pragma once -#endif - - -// The VMPI stats module reports a bunch of statistics to a MySQL server, and the -// stats can be used to trace and graph a compile session. -// - -// Call this as soon as possible to initialize spew hooks. -void VMPI_Stats_InstallSpewHook(); - -// -// pDBServerName is the hostname (or dotted IP address) of the MySQL server to connect to. -// pBSPFilename is the last argument on the command line. -// pMachineIP is the dotted IP address of this machine. -// jobID is an 8-byte unique identifier for this job. -// -bool VMPI_Stats_Init_Master( const char *pHostName, const char *pDBName, const char *pUserName, const char *pBSPFilename, unsigned long *pDBJobID ); -bool VMPI_Stats_Init_Worker( const char *pHostName, const char *pDBName, const char *pUserName, unsigned long DBJobID ); -void VMPI_Stats_Term(); - -// Add a generic text event to the database. -void VMPI_Stats_AddEventText( const char *pText ); - -class CDBInfo -{ -public: - char m_HostName[128]; - char m_DBName[128]; - char m_UserName[128]; -}; - -// If you're the master, this loads pDBInfoFilename, sends that info to the workers, and -// connects to the database. -// -// If you're a worker, this waits for the DB info, then connects to the database. -void StatsDB_InitStatsDatabase( - int argc, - char **argv, - const char *pDBInfoFilename ); - -// The database gives back a unique ID for the job. -unsigned long StatsDB_GetUniqueJobID(); - -// Get the worker ID (used for the JobWorkerID fields in the database). -unsigned long VMPI_Stats_GetJobWorkerID(); - - -#endif // MPI_STATS_H +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef MPI_STATS_H +#define MPI_STATS_H +#ifdef _WIN32 +#pragma once +#endif + + +// The VMPI stats module reports a bunch of statistics to a MySQL server, and the +// stats can be used to trace and graph a compile session. +// + +// Call this as soon as possible to initialize spew hooks. +void VMPI_Stats_InstallSpewHook(); + +// +// pDBServerName is the hostname (or dotted IP address) of the MySQL server to connect to. +// pBSPFilename is the last argument on the command line. +// pMachineIP is the dotted IP address of this machine. +// jobID is an 8-byte unique identifier for this job. +// +bool VMPI_Stats_Init_Master( const char *pHostName, const char *pDBName, const char *pUserName, const char *pBSPFilename, unsigned long *pDBJobID ); +bool VMPI_Stats_Init_Worker( const char *pHostName, const char *pDBName, const char *pUserName, unsigned long DBJobID ); +void VMPI_Stats_Term(); + +// Add a generic text event to the database. +void VMPI_Stats_AddEventText( const char *pText ); + +class CDBInfo +{ +public: + char m_HostName[128]; + char m_DBName[128]; + char m_UserName[128]; +}; + +// If you're the master, this loads pDBInfoFilename, sends that info to the workers, and +// connects to the database. +// +// If you're a worker, this waits for the DB info, then connects to the database. +void StatsDB_InitStatsDatabase( + int argc, + char **argv, + const char *pDBInfoFilename ); + +// The database gives back a unique ID for the job. +unsigned long StatsDB_GetUniqueJobID(); + +// Get the worker ID (used for the JobWorkerID fields in the database). +unsigned long VMPI_Stats_GetJobWorkerID(); + + +#endif // MPI_STATS_H diff --git a/mp/src/utils/common/mstristrip.cpp b/mp/src/utils/common/mstristrip.cpp index 9e611f94..ad6ebdc8 100644 --- a/mp/src/utils/common/mstristrip.cpp +++ b/mp/src/utils/common/mstristrip.cpp @@ -1,930 +1,930 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -// -//=============================================================================// -//----------------------------------------------------------------------------- -// FILE: TRISTRIP.CPP -// -// Desc: Xbox tristripper -// -// Copyright (c) 1999-2000 Microsoft Corporation. All rights reserved. -//----------------------------------------------------------------------------- - -// identifier was truncated to '255' characters in the debug information -#pragma warning(disable: 4786) -// conversion from 'double' to 'float' -#pragma warning(disable: 4244) -#pragma warning(disable: 4530) - -#include -#include -#include -#include -#include - -#include -#ifdef _DEBUG -#include -#endif - -#include "mstristrip.h" - -using namespace std; - -//========================================================================= -// structs -//========================================================================= -typedef vector STRIPVERTS; -typedef list STRIPLIST; -typedef WORD (*TRIANGLELIST)[3]; - -struct TRIANGLEINFO -{ - int neighbortri[3]; - int neighboredge[3]; -}; - -// return true if strip starts clockwise -inline bool FIsStripCW(const STRIPVERTS &stripvertices) -{ - // last index should have cw/ccw bool - return !!stripvertices[stripvertices.size() - 1]; -} - -// return length of strip -inline int StripLen(const STRIPVERTS &stripvertices) -{ - return (int)stripvertices.size() - 1; -} - -// free all stripverts and clear the striplist -inline void FreeStripListVerts(STRIPLIST *pstriplist) -{ - STRIPLIST::iterator istriplist = pstriplist->begin(); - while(istriplist != pstriplist->end()) - { - STRIPVERTS *pstripverts = *istriplist; - delete pstripverts; - pstriplist->erase(istriplist++); - } -} - -//========================================================================= -// main stripper class -//========================================================================= -class CStripper -{ -public: - // ctors/dtors - CStripper(int numtris, TRIANGLELIST ptriangles); - ~CStripper(); - - // initialize tri info - void InitTriangleInfo(int tri, int vert); - - // get maximum length strip from tri/vert - int CreateStrip(int tri, int vert, int maxlen, int *pswaps, - bool flookahead, bool fstartcw, int *pstriptris, int *pstripverts); - - // stripify entire mesh - void BuildStrips(STRIPLIST *pstriplist, int maxlen, bool flookahead); - - // blast strip indices to ppstripindices - int CreateManyStrips(STRIPLIST *pstriplist, WORD **ppstripindices); - int CreateLongStrip(STRIPLIST *pstriplist, WORD **ppstripindices); - - inline int GetNeighborCount(int tri) - { - int count = 0; - for(int vert = 0; vert < 3; vert++) - { - int neighbortri = m_ptriinfo[tri].neighbortri[vert]; - count += (neighbortri != -1) && !m_pused[neighbortri]; - } - return count; - } - - // from callee - int m_numtris; // # tris - TRIANGLELIST m_ptriangles; // trilist - - TRIANGLEINFO *m_ptriinfo; // tri edge, neighbor info - int *m_pused; // tri used flag -}; - -//========================================================================= -// vertex cache class -//========================================================================= -class CVertCache -{ -public: - CVertCache() - { Reset(); } - ~CVertCache() - {}; - - // reset cache - void Reset() - { - m_iCachePtr = 0; - m_cachehits = 0; - memset(m_rgCache, 0xff, sizeof(m_rgCache)); - } - - // add vertindex to cache - bool Add(int strip, int vertindex); - - int NumCacheHits() const - { return m_cachehits; } - -// enum { CACHE_SIZE = 10 }; - enum { CACHE_SIZE = 18 }; - -private: - int m_cachehits; // current # of cache hits - WORD m_rgCache[CACHE_SIZE]; // vertex cache - int m_rgCacheStrip[CACHE_SIZE]; // strip # which added vert - int m_iCachePtr; // fifo ptr -}; - -//========================================================================= -// Get maximum length of strip starting at tri/vert -//========================================================================= -int CStripper::CreateStrip(int tri, int vert, int maxlen, int *pswaps, - bool flookahead, bool fstartcw, int *pstriptris, int *pstripverts) -{ - *pswaps = 0; - - // this guy has already been used? - if(m_pused[tri]) - return 0; - - // mark tri as used - m_pused[tri] = 1; - - int swaps = 0; - - // add first tri info - pstriptris[0] = tri; - pstriptris[1] = tri; - pstriptris[2] = tri; - - if(fstartcw) - { - pstripverts[0] = (vert) % 3; - pstripverts[1] = (vert + 1) % 3; - pstripverts[2] = (vert + 2) % 3; - } - else - { - pstripverts[0] = (vert + 1) % 3; - pstripverts[1] = (vert + 0) % 3; - pstripverts[2] = (vert + 2) % 3; - } - fstartcw = !fstartcw; - - // get next tri information - int edge = (fstartcw ? vert + 2 : vert + 1) % 3; - int nexttri = m_ptriinfo[tri].neighbortri[edge]; - int nextvert = m_ptriinfo[tri].neighboredge[edge]; - - // start building the strip until we run out of room or indices - int stripcount; - for( stripcount = 3; stripcount < maxlen; stripcount++) - { - // dead end? - if(nexttri == -1 || m_pused[nexttri]) - break; - - // move to next tri - tri = nexttri; - vert = nextvert; - - // toggle orientation - fstartcw = !fstartcw; - - // find the next natural edge - int edge = (fstartcw ? vert + 2 : vert + 1) % 3; - nexttri = m_ptriinfo[tri].neighbortri[edge]; - nextvert = m_ptriinfo[tri].neighboredge[edge]; - - bool fswap = false; - if(nexttri == -1 || m_pused[nexttri]) - { - // if the next tri is a dead end - try swapping orientation - fswap = true; - } - else if(flookahead) - { - // try a swap and see who our new neighbor would be - int edgeswap = (fstartcw ? vert + 1 : vert + 2) % 3; - int nexttriswap = m_ptriinfo[tri].neighbortri[edgeswap]; - int nextvertswap = m_ptriinfo[tri].neighboredge[edgeswap]; - - if(nexttriswap != -1 && !m_pused[nexttriswap]) - { - assert(nexttri != -1); - - // if the swap neighbor has a lower count, change directions - if(GetNeighborCount(nexttriswap) < GetNeighborCount(nexttri)) - { - fswap = true; - } - else if(GetNeighborCount(nexttriswap) == GetNeighborCount(nexttri)) - { - // if they have the same number of neighbors - check their neighbors - edgeswap = (fstartcw ? nextvertswap + 2 : nextvertswap + 1) % 3; - nexttriswap = m_ptriinfo[nexttriswap].neighbortri[edgeswap]; - - int edge1 = (fstartcw ? nextvert + 1 : nextvert + 2) % 3; - int nexttri1 = m_ptriinfo[nexttri].neighbortri[edge1]; - - if(nexttri1 == -1 || m_pused[nexttri1]) - { - // natural winding order leads us to a dead end so turn - fswap = true; - } - else if(nexttriswap != -1 && !m_pused[nexttriswap]) - { - // check neighbor counts on both directions and swap if it's better - if(GetNeighborCount(nexttriswap) < GetNeighborCount(nexttri1)) - fswap = true; - } - } - } - } - - if(fswap) - { - // we've been told to change directions so make sure we actually can - // and then add the swap vertex - int edgeswap = (fstartcw ? vert + 1 : vert + 2) % 3; - nexttri = m_ptriinfo[tri].neighbortri[edgeswap]; - nextvert = m_ptriinfo[tri].neighboredge[edgeswap]; - - if(nexttri != -1 && !m_pused[nexttri]) - { - pstriptris[stripcount] = pstriptris[stripcount - 2]; - pstripverts[stripcount] = pstripverts[stripcount - 2]; - stripcount++; - swaps++; - fstartcw = !fstartcw; - } - } - - // record index information - pstriptris[stripcount] = tri; - pstripverts[stripcount] = (vert + 2) % 3; - - // mark triangle as used - m_pused[tri] = 1; - } - - // clear the used flags - for(int j = 2; j < stripcount; j++) - m_pused[pstriptris[j]] = 0; - - // return swap count and striplen - *pswaps = swaps; - return stripcount; -} - -//========================================================================= -// Given a striplist and current cache state, pick the best next strip -//========================================================================= -STRIPLIST::iterator FindBestCachedStrip(STRIPLIST *pstriplist, - const CVertCache &vertcachestate) -{ - if(pstriplist->empty()) - return pstriplist->end(); - - bool fFlipStrip = false; - int maxcachehits = -1; - STRIPLIST::iterator istriplistbest = pstriplist->begin(); - - int striplen = StripLen(**istriplistbest); - bool fstartcw = FIsStripCW(**istriplistbest); - - // go through all the other strips looking for the best caching - for(STRIPLIST::iterator istriplist = pstriplist->begin(); - istriplist != pstriplist->end(); - ++istriplist) - { - bool fFlip = false; - const STRIPVERTS &stripverts = **istriplist; - int striplennew = StripLen(stripverts); - - // check cache if this strip is the same type as us (ie: cw/odd) - if((FIsStripCW(stripverts) == fstartcw) && - ((striplen & 0x1) == (striplennew & 0x1))) - { - // copy current state of cache - CVertCache vertcachenew = vertcachestate; - - // figure out what this guy would do to our cache - for(int ivert = 0; ivert < striplennew; ivert++) - vertcachenew.Add(2, stripverts[ivert]); - - // even length strip - see if better cache hits reversed - if(!(striplennew & 0x1)) - { - CVertCache vertcacheflipped = vertcachestate; - - for(int ivert = StripLen(stripverts) - 1; ivert >= 0; ivert--) - vertcacheflipped.Add(2, stripverts[ivert]); - - if(vertcacheflipped.NumCacheHits() > vertcachenew.NumCacheHits()) - { - vertcachenew = vertcacheflipped; - fFlip = true; - } - } - - // record the best number of cache hits to date - int numcachehits = vertcachenew.NumCacheHits() - vertcachestate.NumCacheHits(); - if(numcachehits > maxcachehits) - { - maxcachehits = numcachehits; - istriplistbest = istriplist; - fFlipStrip = fFlip; - } - } - } - - if(fFlipStrip) - { - STRIPVERTS &stripverts = **istriplistbest; - STRIPVERTS::iterator vend = stripverts.end(); - - reverse(stripverts.begin(), --vend); - } - - // make sure we keep the list in order and always pull off - // the first dude. - if(istriplistbest != pstriplist->begin()) - swap(*istriplistbest, *pstriplist->begin()); - - return pstriplist->begin(); -} - - -//========================================================================= -// Don't merge the strips - just blast em into the stripbuffer one by one -// (useful for debugging) -//========================================================================= -int CStripper::CreateManyStrips(STRIPLIST *pstriplist, WORD **ppstripindices) -{ - // allow room for each of the strips size plus the final 0 - int indexcount = (int)pstriplist->size() + 1; - - // we're storing the strips in [size1 i1 i2 i3][size2 i4 i5 i6][0] format - STRIPLIST::iterator istriplist; - for( istriplist = pstriplist->begin(); istriplist != pstriplist->end(); ++istriplist) - { - // add striplength plus potential degenerate to swap ccw --> cw - indexcount += StripLen(**istriplist) + 1; - } - - // alloc the space for all this stuff - WORD *pstripindices = new WORD [indexcount]; - assert(pstripindices); - - CVertCache vertcache; - int numstripindices = 0; - - for(istriplist = pstriplist->begin(); - !pstriplist->empty(); - istriplist = FindBestCachedStrip(pstriplist, vertcache)) - { - const STRIPVERTS &stripverts = **istriplist; - - if(!FIsStripCW(stripverts)) - { - // add an extra index if it's ccw - pstripindices[numstripindices++] = StripLen(stripverts) + 1; - pstripindices[numstripindices++] = stripverts[0]; - } - else - { - // add the strip length - pstripindices[numstripindices++] = StripLen(stripverts); - } - - // add all the strip indices - for(int i = 0; i < StripLen(stripverts); i++) - { - pstripindices[numstripindices++] = stripverts[i]; - vertcache.Add(1, stripverts[i]); - } - - // free this guy and pop him off the list - delete &stripverts; - pstriplist->pop_front(); - } - - // add terminating zero - pstripindices[numstripindices++] = 0; - *ppstripindices = pstripindices; - - return numstripindices; -} - -//========================================================================= -// Merge striplist into one big uberlist with (hopefully) optimal caching -//========================================================================= -int CStripper::CreateLongStrip(STRIPLIST *pstriplist, WORD **ppstripindices) -{ - // allow room for one strip length plus a possible 3 extra indices per - // concatenated strip list plus the final 0 - int indexcount = ((int)pstriplist->size() * 3) + 2; - - // we're storing the strips in [size1 i1 i2 i3][size2 i4 i5 i6][0] format - STRIPLIST::iterator istriplist; - for( istriplist = pstriplist->begin(); istriplist != pstriplist->end(); ++istriplist) - { - indexcount += StripLen(**istriplist); - } - - // alloc the space for all this stuff - WORD *pstripindices = new WORD [indexcount]; - assert(pstripindices); - - CVertCache vertcache; - int numstripindices = 0; - - // add first strip - istriplist = pstriplist->begin(); - const STRIPVERTS &stripverts = **istriplist; - - // first strip should be cw - assert(FIsStripCW(stripverts)); - - for(int ivert = 0; ivert < StripLen(stripverts); ivert++) - { - pstripindices[numstripindices++] = stripverts[ivert]; - vertcache.Add(1, stripverts[ivert]); - } - - // kill first dude - delete &stripverts; - pstriplist->erase(istriplist); - - // add all the others - while(pstriplist->size()) - { - istriplist = FindBestCachedStrip(pstriplist, vertcache); - STRIPVERTS &stripverts = **istriplist; - short lastvert = pstripindices[numstripindices - 1]; - short firstvert = stripverts[0]; - - if(firstvert != lastvert) - { - // add degenerate from last strip - pstripindices[numstripindices++] = lastvert; - - // add degenerate from our strip - pstripindices[numstripindices++] = firstvert; - } - - // if we're not orientated correctly, we need to add a degenerate - if(FIsStripCW(stripverts) != !(numstripindices & 0x1)) - { - // This shouldn't happen - we're currently trying very hard - // to keep everything oriented correctly. - assert(false); - pstripindices[numstripindices++] = firstvert; - } - - // add these verts - for(int ivert = 0; ivert < StripLen(stripverts); ivert++) - { - pstripindices[numstripindices++] = stripverts[ivert]; - vertcache.Add(1, stripverts[ivert]); - } - - // free these guys - delete &stripverts; - pstriplist->erase(istriplist); - } - - *ppstripindices = pstripindices; - return numstripindices; -} - -//========================================================================= -// Build a (hopefully) optimal set of strips from a trilist -//========================================================================= -void CStripper::BuildStrips(STRIPLIST *pstriplist, int maxlen, bool flookahead) -{ - // temp indices storage - const int ctmpverts = 1024; - int pstripverts[ctmpverts + 1]; - int pstriptris[ctmpverts + 1]; - - assert(maxlen <= ctmpverts); - - // clear all the used flags for the tris - memset(m_pused, 0, sizeof(m_pused[0]) * m_numtris); - - bool fstartcw = true; - for(;;) - { - int besttri = 0; - int bestvert = 0; - float bestratio = 2.0f; - int bestneighborcount = INT_MAX; - - int tri; - for( tri = 0; tri < m_numtris; tri++) - { - // if used the continue - if(m_pused[tri]) - continue; - - // get the neighbor count - int curneightborcount = GetNeighborCount(tri); - assert(curneightborcount >= 0 && curneightborcount <= 3); - - // push all the singletons to the very end - if(!curneightborcount) - curneightborcount = 4; - - // if this guy has more neighbors than the current best - bail - if(curneightborcount > bestneighborcount) - continue; - - // try starting the strip with each of this tris verts - for(int vert = 0; vert < 3; vert++) - { - int swaps; - int len = CreateStrip(tri, vert, maxlen, &swaps, flookahead, - fstartcw, pstriptris, pstripverts); - assert(len); - - float ratio = (len == 3) ? 1.0f : (float)swaps / len; - - // check if this ratio is better than what we've already got for - // this neighborcount - if((curneightborcount < bestneighborcount) || - ((curneightborcount == bestneighborcount) && (ratio < bestratio))) - { - bestneighborcount = curneightborcount; - - besttri = tri; - bestvert = vert; - bestratio = ratio; - } - - } - } - - // no strips found? - if(bestneighborcount == INT_MAX) - break; - - // recreate this strip - int swaps; - int len = CreateStrip(besttri, bestvert, maxlen, - &swaps, flookahead, fstartcw, pstriptris, pstripverts); - assert(len); - - // mark the tris on the best strip as used - for(tri = 0; tri < len; tri++) - m_pused[pstriptris[tri]] = 1; - - // create a new STRIPVERTS and stuff in the indices - STRIPVERTS *pstripvertices = new STRIPVERTS(len + 1); - assert(pstripvertices); - - // store orientation in first entry - for(tri = 0; tri < len; tri++) - (*pstripvertices)[tri] = m_ptriangles[pstriptris[tri]][pstripverts[tri]]; - (*pstripvertices)[len] = fstartcw; - - // store the STRIPVERTS - pstriplist->push_back(pstripvertices); - - // if strip was odd - swap orientation - if((len & 0x1)) - fstartcw = !fstartcw; - } - -#ifdef _DEBUG - // make sure all tris are used - for(int t = 0; t < m_numtris; t++) - assert(m_pused[t]); -#endif -} - -//========================================================================= -// Guesstimate on the total index count for this list of strips -//========================================================================= -int EstimateStripCost(STRIPLIST *pstriplist) -{ - int count = 0; - - for(STRIPLIST::iterator istriplist = pstriplist->begin(); - istriplist != pstriplist->end(); - ++istriplist) - { - // add count of indices - count += StripLen(**istriplist); - } - - // assume 2 indices per strip to tack all these guys together - return count + ((int)pstriplist->size() - 1) * 2; -} - -//========================================================================= -// Initialize triangle information (edges, #neighbors, etc.) -//========================================================================= -void CStripper::InitTriangleInfo(int tri, int vert) -{ - WORD *ptriverts = &m_ptriangles[tri + 1][0]; - int vert1 = m_ptriangles[tri][(vert + 1) % 3]; - int vert2 = m_ptriangles[tri][vert]; - - for(int itri = tri + 1; itri < m_numtris; itri++, ptriverts += 3) - { - if(m_pused[itri] != 0x7) - { - for(int ivert = 0; ivert < 3; ivert++) - { - if((ptriverts[ivert] == vert1) && - (ptriverts[(ivert + 1) % 3] == vert2)) - { - // add the triangle info - m_ptriinfo[tri].neighbortri[vert] = itri; - m_ptriinfo[tri].neighboredge[vert] = ivert; - m_pused[tri] |= (1 << vert); - - m_ptriinfo[itri].neighbortri[ivert] = tri; - m_ptriinfo[itri].neighboredge[ivert] = vert; - m_pused[itri] |= (1 << ivert); - return; - } - } - } - } -} - -//========================================================================= -// CStripper ctor -//========================================================================= -CStripper::CStripper(int numtris, TRIANGLELIST ptriangles) -{ - // store trilist info - m_numtris = numtris; - m_ptriangles = ptriangles; - - m_pused = new int[numtris]; - assert(m_pused); - m_ptriinfo = new TRIANGLEINFO[numtris]; - assert(m_ptriinfo); - - // init triinfo - int itri; - for( itri = 0; itri < numtris; itri++) - { - m_ptriinfo[itri].neighbortri[0] = -1; - m_ptriinfo[itri].neighbortri[1] = -1; - m_ptriinfo[itri].neighbortri[2] = -1; - } - - // clear the used flag - memset(m_pused, 0, sizeof(m_pused[0]) * m_numtris); - - // go through all the triangles and find edges, neighbor counts - for(itri = 0; itri < numtris; itri++) - { - for(int ivert = 0; ivert < 3; ivert++) - { - if(!(m_pused[itri] & (1 << ivert))) - InitTriangleInfo(itri, ivert); - } - } - - // clear the used flags from InitTriangleInfo - memset(m_pused, 0, sizeof(m_pused[0]) * m_numtris); -} - -//========================================================================= -// CStripper dtor -//========================================================================= -CStripper::~CStripper() -{ - // free stuff - delete [] m_pused; - m_pused = NULL; - - delete [] m_ptriinfo; - m_ptriinfo = NULL; -} - -//========================================================================= -// Add an index to the cache - returns true if it was added, false otherwise -//========================================================================= -bool CVertCache::Add(int strip, int vertindex) -{ - // find index in cache - for(int iCache = 0; iCache < CACHE_SIZE; iCache++) - { - if(vertindex == m_rgCache[iCache]) - { - // if it's in the cache and it's from a different strip - // change the strip to the new one and count the cache hit - if(strip != m_rgCacheStrip[iCache]) - { - m_cachehits++; - m_rgCacheStrip[iCache] = strip; - return true; - } - - // we added this item to the cache earlier - carry on - return false; - } - } - - // not in cache, add vert and strip - m_rgCache[m_iCachePtr] = vertindex; - m_rgCacheStrip[m_iCachePtr] = strip; - m_iCachePtr = (m_iCachePtr + 1) % CACHE_SIZE; - return true; -} - -#ifdef _DEBUG -//========================================================================= -// Turn on c runtime leak checking, etc. -//========================================================================= -void EnableLeakChecking() -{ - int flCrtDbgFlags = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); - - flCrtDbgFlags &= - ~(_CRTDBG_LEAK_CHECK_DF | - _CRTDBG_CHECK_ALWAYS_DF | - _CRTDBG_DELAY_FREE_MEM_DF); - - // always check for memory leaks - flCrtDbgFlags |= _CRTDBG_LEAK_CHECK_DF; - - // others you may / may not want to set - flCrtDbgFlags |= _CRTDBG_CHECK_ALWAYS_DF; - flCrtDbgFlags |= _CRTDBG_DELAY_FREE_MEM_DF; - - _CrtSetDbgFlag(flCrtDbgFlags); - - // all types of reports go via OutputDebugString - _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG); - _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG); - _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG); - - // big errors and asserts get their own assert window - _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_WNDW); - _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_WNDW); - - // _CrtSetBreakAlloc(0); -} -#endif - -//========================================================================= -// Main Stripify routine -//========================================================================= -int Stripify(int numtris, WORD *ptriangles, int *pnumindices, WORD **ppstripindices) -{ - if(!numtris || !ptriangles) - return 0; - -#ifdef _DEBUG -// EnableLeakChecking(); -#endif - - CStripper stripper(numtris, (TRIANGLELIST)ptriangles); - - // map of various args to try stripifying mesh with - struct ARGMAP - { - int maxlen; // maximum length of strips - bool flookahead; // use sgi greedy lookahead (or not) - } rgargmap[] = - { - { 1024, true }, - { 1024, false }, - }; - static const int cargmaps = sizeof(rgargmap) / sizeof(rgargmap[0]); - STRIPLIST striplistbest; - int bestlistcost = 0; - - for(int imap = 0; imap < cargmaps; imap++) - { - STRIPLIST striplist; - - // build the strip with the various args - stripper.BuildStrips(&striplist, rgargmap[imap].maxlen, - rgargmap[imap].flookahead); - - // guesstimate the list cost and store it if it's good - int listcost = EstimateStripCost(&striplist); - if(!bestlistcost || (listcost < bestlistcost)) - { - // free the old best list - FreeStripListVerts(&striplistbest); - - // store the new best list - striplistbest = striplist; - bestlistcost = listcost; - assert(bestlistcost > 0); - } - else - { - FreeStripListVerts(&striplist); - } - } - -#ifdef NEVER - // Return the strips in [size1 i1 i2 i3][size2 i4 i5 i6]...[0] format - // Very useful for debugging... - return stripper.CreateManyStrips(&striplistbest, ppstripindices); -#endif // NEVER - - // return one big long strip - int numindices = stripper.CreateLongStrip(&striplistbest, ppstripindices); - - if(pnumindices) - *pnumindices = numindices; - return numindices; -} - -//========================================================================= -// Class used to vertices for locality of access. -//========================================================================= -struct SortEntry -{ -public: - int iFirstUsed; - int iOrigIndex; - - bool operator<(const SortEntry& rhs) const - { - return iFirstUsed < rhs.iFirstUsed; - } -}; - -//========================================================================= -// Reorder the vertices -//========================================================================= -void ComputeVertexPermutation(int numstripindices, WORD* pstripindices, - int* pnumverts, WORD** ppvertexpermutation) -{ - // Sort verts to maximize locality. - SortEntry* pSortTable = new SortEntry[*pnumverts]; - - // Fill in original index. - int i; - for( i = 0; i < *pnumverts; i++) - { - pSortTable[i].iOrigIndex = i; - pSortTable[i].iFirstUsed = -1; - } - - // Fill in first used flag. - for(i = 0; i < numstripindices; i++) - { - int index = pstripindices[i]; - - if(pSortTable[index].iFirstUsed == -1) - pSortTable[index].iFirstUsed = i; - } - - // Sort the table. - sort(pSortTable, pSortTable + *pnumverts); - - // Copy re-mapped to orignal vertex permutaion into output array. - *ppvertexpermutation = new WORD[*pnumverts]; - - for(i = 0; i < *pnumverts; i++) - { - (*ppvertexpermutation)[i] = pSortTable[i].iOrigIndex; - } - - // Build original to re-mapped permutation. - WORD* pInversePermutation = new WORD[numstripindices]; - - for(i = 0; i < *pnumverts; i++) - { - pInversePermutation[pSortTable[i].iOrigIndex] = i; - } - - // We need to remap indices as well. - for(i = 0; i < numstripindices; i++) - { - pstripindices[i] = pInversePermutation[pstripindices[i]]; - } - - delete[] pSortTable; - delete[] pInversePermutation; -} - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +//----------------------------------------------------------------------------- +// FILE: TRISTRIP.CPP +// +// Desc: Xbox tristripper +// +// Copyright (c) 1999-2000 Microsoft Corporation. All rights reserved. +//----------------------------------------------------------------------------- + +// identifier was truncated to '255' characters in the debug information +#pragma warning(disable: 4786) +// conversion from 'double' to 'float' +#pragma warning(disable: 4244) +#pragma warning(disable: 4530) + +#include +#include +#include +#include +#include + +#include +#ifdef _DEBUG +#include +#endif + +#include "mstristrip.h" + +using namespace std; + +//========================================================================= +// structs +//========================================================================= +typedef vector STRIPVERTS; +typedef list STRIPLIST; +typedef WORD (*TRIANGLELIST)[3]; + +struct TRIANGLEINFO +{ + int neighbortri[3]; + int neighboredge[3]; +}; + +// return true if strip starts clockwise +inline bool FIsStripCW(const STRIPVERTS &stripvertices) +{ + // last index should have cw/ccw bool + return !!stripvertices[stripvertices.size() - 1]; +} + +// return length of strip +inline int StripLen(const STRIPVERTS &stripvertices) +{ + return (int)stripvertices.size() - 1; +} + +// free all stripverts and clear the striplist +inline void FreeStripListVerts(STRIPLIST *pstriplist) +{ + STRIPLIST::iterator istriplist = pstriplist->begin(); + while(istriplist != pstriplist->end()) + { + STRIPVERTS *pstripverts = *istriplist; + delete pstripverts; + pstriplist->erase(istriplist++); + } +} + +//========================================================================= +// main stripper class +//========================================================================= +class CStripper +{ +public: + // ctors/dtors + CStripper(int numtris, TRIANGLELIST ptriangles); + ~CStripper(); + + // initialize tri info + void InitTriangleInfo(int tri, int vert); + + // get maximum length strip from tri/vert + int CreateStrip(int tri, int vert, int maxlen, int *pswaps, + bool flookahead, bool fstartcw, int *pstriptris, int *pstripverts); + + // stripify entire mesh + void BuildStrips(STRIPLIST *pstriplist, int maxlen, bool flookahead); + + // blast strip indices to ppstripindices + int CreateManyStrips(STRIPLIST *pstriplist, WORD **ppstripindices); + int CreateLongStrip(STRIPLIST *pstriplist, WORD **ppstripindices); + + inline int GetNeighborCount(int tri) + { + int count = 0; + for(int vert = 0; vert < 3; vert++) + { + int neighbortri = m_ptriinfo[tri].neighbortri[vert]; + count += (neighbortri != -1) && !m_pused[neighbortri]; + } + return count; + } + + // from callee + int m_numtris; // # tris + TRIANGLELIST m_ptriangles; // trilist + + TRIANGLEINFO *m_ptriinfo; // tri edge, neighbor info + int *m_pused; // tri used flag +}; + +//========================================================================= +// vertex cache class +//========================================================================= +class CVertCache +{ +public: + CVertCache() + { Reset(); } + ~CVertCache() + {}; + + // reset cache + void Reset() + { + m_iCachePtr = 0; + m_cachehits = 0; + memset(m_rgCache, 0xff, sizeof(m_rgCache)); + } + + // add vertindex to cache + bool Add(int strip, int vertindex); + + int NumCacheHits() const + { return m_cachehits; } + +// enum { CACHE_SIZE = 10 }; + enum { CACHE_SIZE = 18 }; + +private: + int m_cachehits; // current # of cache hits + WORD m_rgCache[CACHE_SIZE]; // vertex cache + int m_rgCacheStrip[CACHE_SIZE]; // strip # which added vert + int m_iCachePtr; // fifo ptr +}; + +//========================================================================= +// Get maximum length of strip starting at tri/vert +//========================================================================= +int CStripper::CreateStrip(int tri, int vert, int maxlen, int *pswaps, + bool flookahead, bool fstartcw, int *pstriptris, int *pstripverts) +{ + *pswaps = 0; + + // this guy has already been used? + if(m_pused[tri]) + return 0; + + // mark tri as used + m_pused[tri] = 1; + + int swaps = 0; + + // add first tri info + pstriptris[0] = tri; + pstriptris[1] = tri; + pstriptris[2] = tri; + + if(fstartcw) + { + pstripverts[0] = (vert) % 3; + pstripverts[1] = (vert + 1) % 3; + pstripverts[2] = (vert + 2) % 3; + } + else + { + pstripverts[0] = (vert + 1) % 3; + pstripverts[1] = (vert + 0) % 3; + pstripverts[2] = (vert + 2) % 3; + } + fstartcw = !fstartcw; + + // get next tri information + int edge = (fstartcw ? vert + 2 : vert + 1) % 3; + int nexttri = m_ptriinfo[tri].neighbortri[edge]; + int nextvert = m_ptriinfo[tri].neighboredge[edge]; + + // start building the strip until we run out of room or indices + int stripcount; + for( stripcount = 3; stripcount < maxlen; stripcount++) + { + // dead end? + if(nexttri == -1 || m_pused[nexttri]) + break; + + // move to next tri + tri = nexttri; + vert = nextvert; + + // toggle orientation + fstartcw = !fstartcw; + + // find the next natural edge + int edge = (fstartcw ? vert + 2 : vert + 1) % 3; + nexttri = m_ptriinfo[tri].neighbortri[edge]; + nextvert = m_ptriinfo[tri].neighboredge[edge]; + + bool fswap = false; + if(nexttri == -1 || m_pused[nexttri]) + { + // if the next tri is a dead end - try swapping orientation + fswap = true; + } + else if(flookahead) + { + // try a swap and see who our new neighbor would be + int edgeswap = (fstartcw ? vert + 1 : vert + 2) % 3; + int nexttriswap = m_ptriinfo[tri].neighbortri[edgeswap]; + int nextvertswap = m_ptriinfo[tri].neighboredge[edgeswap]; + + if(nexttriswap != -1 && !m_pused[nexttriswap]) + { + assert(nexttri != -1); + + // if the swap neighbor has a lower count, change directions + if(GetNeighborCount(nexttriswap) < GetNeighborCount(nexttri)) + { + fswap = true; + } + else if(GetNeighborCount(nexttriswap) == GetNeighborCount(nexttri)) + { + // if they have the same number of neighbors - check their neighbors + edgeswap = (fstartcw ? nextvertswap + 2 : nextvertswap + 1) % 3; + nexttriswap = m_ptriinfo[nexttriswap].neighbortri[edgeswap]; + + int edge1 = (fstartcw ? nextvert + 1 : nextvert + 2) % 3; + int nexttri1 = m_ptriinfo[nexttri].neighbortri[edge1]; + + if(nexttri1 == -1 || m_pused[nexttri1]) + { + // natural winding order leads us to a dead end so turn + fswap = true; + } + else if(nexttriswap != -1 && !m_pused[nexttriswap]) + { + // check neighbor counts on both directions and swap if it's better + if(GetNeighborCount(nexttriswap) < GetNeighborCount(nexttri1)) + fswap = true; + } + } + } + } + + if(fswap) + { + // we've been told to change directions so make sure we actually can + // and then add the swap vertex + int edgeswap = (fstartcw ? vert + 1 : vert + 2) % 3; + nexttri = m_ptriinfo[tri].neighbortri[edgeswap]; + nextvert = m_ptriinfo[tri].neighboredge[edgeswap]; + + if(nexttri != -1 && !m_pused[nexttri]) + { + pstriptris[stripcount] = pstriptris[stripcount - 2]; + pstripverts[stripcount] = pstripverts[stripcount - 2]; + stripcount++; + swaps++; + fstartcw = !fstartcw; + } + } + + // record index information + pstriptris[stripcount] = tri; + pstripverts[stripcount] = (vert + 2) % 3; + + // mark triangle as used + m_pused[tri] = 1; + } + + // clear the used flags + for(int j = 2; j < stripcount; j++) + m_pused[pstriptris[j]] = 0; + + // return swap count and striplen + *pswaps = swaps; + return stripcount; +} + +//========================================================================= +// Given a striplist and current cache state, pick the best next strip +//========================================================================= +STRIPLIST::iterator FindBestCachedStrip(STRIPLIST *pstriplist, + const CVertCache &vertcachestate) +{ + if(pstriplist->empty()) + return pstriplist->end(); + + bool fFlipStrip = false; + int maxcachehits = -1; + STRIPLIST::iterator istriplistbest = pstriplist->begin(); + + int striplen = StripLen(**istriplistbest); + bool fstartcw = FIsStripCW(**istriplistbest); + + // go through all the other strips looking for the best caching + for(STRIPLIST::iterator istriplist = pstriplist->begin(); + istriplist != pstriplist->end(); + ++istriplist) + { + bool fFlip = false; + const STRIPVERTS &stripverts = **istriplist; + int striplennew = StripLen(stripverts); + + // check cache if this strip is the same type as us (ie: cw/odd) + if((FIsStripCW(stripverts) == fstartcw) && + ((striplen & 0x1) == (striplennew & 0x1))) + { + // copy current state of cache + CVertCache vertcachenew = vertcachestate; + + // figure out what this guy would do to our cache + for(int ivert = 0; ivert < striplennew; ivert++) + vertcachenew.Add(2, stripverts[ivert]); + + // even length strip - see if better cache hits reversed + if(!(striplennew & 0x1)) + { + CVertCache vertcacheflipped = vertcachestate; + + for(int ivert = StripLen(stripverts) - 1; ivert >= 0; ivert--) + vertcacheflipped.Add(2, stripverts[ivert]); + + if(vertcacheflipped.NumCacheHits() > vertcachenew.NumCacheHits()) + { + vertcachenew = vertcacheflipped; + fFlip = true; + } + } + + // record the best number of cache hits to date + int numcachehits = vertcachenew.NumCacheHits() - vertcachestate.NumCacheHits(); + if(numcachehits > maxcachehits) + { + maxcachehits = numcachehits; + istriplistbest = istriplist; + fFlipStrip = fFlip; + } + } + } + + if(fFlipStrip) + { + STRIPVERTS &stripverts = **istriplistbest; + STRIPVERTS::iterator vend = stripverts.end(); + + reverse(stripverts.begin(), --vend); + } + + // make sure we keep the list in order and always pull off + // the first dude. + if(istriplistbest != pstriplist->begin()) + swap(*istriplistbest, *pstriplist->begin()); + + return pstriplist->begin(); +} + + +//========================================================================= +// Don't merge the strips - just blast em into the stripbuffer one by one +// (useful for debugging) +//========================================================================= +int CStripper::CreateManyStrips(STRIPLIST *pstriplist, WORD **ppstripindices) +{ + // allow room for each of the strips size plus the final 0 + int indexcount = (int)pstriplist->size() + 1; + + // we're storing the strips in [size1 i1 i2 i3][size2 i4 i5 i6][0] format + STRIPLIST::iterator istriplist; + for( istriplist = pstriplist->begin(); istriplist != pstriplist->end(); ++istriplist) + { + // add striplength plus potential degenerate to swap ccw --> cw + indexcount += StripLen(**istriplist) + 1; + } + + // alloc the space for all this stuff + WORD *pstripindices = new WORD [indexcount]; + assert(pstripindices); + + CVertCache vertcache; + int numstripindices = 0; + + for(istriplist = pstriplist->begin(); + !pstriplist->empty(); + istriplist = FindBestCachedStrip(pstriplist, vertcache)) + { + const STRIPVERTS &stripverts = **istriplist; + + if(!FIsStripCW(stripverts)) + { + // add an extra index if it's ccw + pstripindices[numstripindices++] = StripLen(stripverts) + 1; + pstripindices[numstripindices++] = stripverts[0]; + } + else + { + // add the strip length + pstripindices[numstripindices++] = StripLen(stripverts); + } + + // add all the strip indices + for(int i = 0; i < StripLen(stripverts); i++) + { + pstripindices[numstripindices++] = stripverts[i]; + vertcache.Add(1, stripverts[i]); + } + + // free this guy and pop him off the list + delete &stripverts; + pstriplist->pop_front(); + } + + // add terminating zero + pstripindices[numstripindices++] = 0; + *ppstripindices = pstripindices; + + return numstripindices; +} + +//========================================================================= +// Merge striplist into one big uberlist with (hopefully) optimal caching +//========================================================================= +int CStripper::CreateLongStrip(STRIPLIST *pstriplist, WORD **ppstripindices) +{ + // allow room for one strip length plus a possible 3 extra indices per + // concatenated strip list plus the final 0 + int indexcount = ((int)pstriplist->size() * 3) + 2; + + // we're storing the strips in [size1 i1 i2 i3][size2 i4 i5 i6][0] format + STRIPLIST::iterator istriplist; + for( istriplist = pstriplist->begin(); istriplist != pstriplist->end(); ++istriplist) + { + indexcount += StripLen(**istriplist); + } + + // alloc the space for all this stuff + WORD *pstripindices = new WORD [indexcount]; + assert(pstripindices); + + CVertCache vertcache; + int numstripindices = 0; + + // add first strip + istriplist = pstriplist->begin(); + const STRIPVERTS &stripverts = **istriplist; + + // first strip should be cw + assert(FIsStripCW(stripverts)); + + for(int ivert = 0; ivert < StripLen(stripverts); ivert++) + { + pstripindices[numstripindices++] = stripverts[ivert]; + vertcache.Add(1, stripverts[ivert]); + } + + // kill first dude + delete &stripverts; + pstriplist->erase(istriplist); + + // add all the others + while(pstriplist->size()) + { + istriplist = FindBestCachedStrip(pstriplist, vertcache); + STRIPVERTS &stripverts = **istriplist; + short lastvert = pstripindices[numstripindices - 1]; + short firstvert = stripverts[0]; + + if(firstvert != lastvert) + { + // add degenerate from last strip + pstripindices[numstripindices++] = lastvert; + + // add degenerate from our strip + pstripindices[numstripindices++] = firstvert; + } + + // if we're not orientated correctly, we need to add a degenerate + if(FIsStripCW(stripverts) != !(numstripindices & 0x1)) + { + // This shouldn't happen - we're currently trying very hard + // to keep everything oriented correctly. + assert(false); + pstripindices[numstripindices++] = firstvert; + } + + // add these verts + for(int ivert = 0; ivert < StripLen(stripverts); ivert++) + { + pstripindices[numstripindices++] = stripverts[ivert]; + vertcache.Add(1, stripverts[ivert]); + } + + // free these guys + delete &stripverts; + pstriplist->erase(istriplist); + } + + *ppstripindices = pstripindices; + return numstripindices; +} + +//========================================================================= +// Build a (hopefully) optimal set of strips from a trilist +//========================================================================= +void CStripper::BuildStrips(STRIPLIST *pstriplist, int maxlen, bool flookahead) +{ + // temp indices storage + const int ctmpverts = 1024; + int pstripverts[ctmpverts + 1]; + int pstriptris[ctmpverts + 1]; + + assert(maxlen <= ctmpverts); + + // clear all the used flags for the tris + memset(m_pused, 0, sizeof(m_pused[0]) * m_numtris); + + bool fstartcw = true; + for(;;) + { + int besttri = 0; + int bestvert = 0; + float bestratio = 2.0f; + int bestneighborcount = INT_MAX; + + int tri; + for( tri = 0; tri < m_numtris; tri++) + { + // if used the continue + if(m_pused[tri]) + continue; + + // get the neighbor count + int curneightborcount = GetNeighborCount(tri); + assert(curneightborcount >= 0 && curneightborcount <= 3); + + // push all the singletons to the very end + if(!curneightborcount) + curneightborcount = 4; + + // if this guy has more neighbors than the current best - bail + if(curneightborcount > bestneighborcount) + continue; + + // try starting the strip with each of this tris verts + for(int vert = 0; vert < 3; vert++) + { + int swaps; + int len = CreateStrip(tri, vert, maxlen, &swaps, flookahead, + fstartcw, pstriptris, pstripverts); + assert(len); + + float ratio = (len == 3) ? 1.0f : (float)swaps / len; + + // check if this ratio is better than what we've already got for + // this neighborcount + if((curneightborcount < bestneighborcount) || + ((curneightborcount == bestneighborcount) && (ratio < bestratio))) + { + bestneighborcount = curneightborcount; + + besttri = tri; + bestvert = vert; + bestratio = ratio; + } + + } + } + + // no strips found? + if(bestneighborcount == INT_MAX) + break; + + // recreate this strip + int swaps; + int len = CreateStrip(besttri, bestvert, maxlen, + &swaps, flookahead, fstartcw, pstriptris, pstripverts); + assert(len); + + // mark the tris on the best strip as used + for(tri = 0; tri < len; tri++) + m_pused[pstriptris[tri]] = 1; + + // create a new STRIPVERTS and stuff in the indices + STRIPVERTS *pstripvertices = new STRIPVERTS(len + 1); + assert(pstripvertices); + + // store orientation in first entry + for(tri = 0; tri < len; tri++) + (*pstripvertices)[tri] = m_ptriangles[pstriptris[tri]][pstripverts[tri]]; + (*pstripvertices)[len] = fstartcw; + + // store the STRIPVERTS + pstriplist->push_back(pstripvertices); + + // if strip was odd - swap orientation + if((len & 0x1)) + fstartcw = !fstartcw; + } + +#ifdef _DEBUG + // make sure all tris are used + for(int t = 0; t < m_numtris; t++) + assert(m_pused[t]); +#endif +} + +//========================================================================= +// Guesstimate on the total index count for this list of strips +//========================================================================= +int EstimateStripCost(STRIPLIST *pstriplist) +{ + int count = 0; + + for(STRIPLIST::iterator istriplist = pstriplist->begin(); + istriplist != pstriplist->end(); + ++istriplist) + { + // add count of indices + count += StripLen(**istriplist); + } + + // assume 2 indices per strip to tack all these guys together + return count + ((int)pstriplist->size() - 1) * 2; +} + +//========================================================================= +// Initialize triangle information (edges, #neighbors, etc.) +//========================================================================= +void CStripper::InitTriangleInfo(int tri, int vert) +{ + WORD *ptriverts = &m_ptriangles[tri + 1][0]; + int vert1 = m_ptriangles[tri][(vert + 1) % 3]; + int vert2 = m_ptriangles[tri][vert]; + + for(int itri = tri + 1; itri < m_numtris; itri++, ptriverts += 3) + { + if(m_pused[itri] != 0x7) + { + for(int ivert = 0; ivert < 3; ivert++) + { + if((ptriverts[ivert] == vert1) && + (ptriverts[(ivert + 1) % 3] == vert2)) + { + // add the triangle info + m_ptriinfo[tri].neighbortri[vert] = itri; + m_ptriinfo[tri].neighboredge[vert] = ivert; + m_pused[tri] |= (1 << vert); + + m_ptriinfo[itri].neighbortri[ivert] = tri; + m_ptriinfo[itri].neighboredge[ivert] = vert; + m_pused[itri] |= (1 << ivert); + return; + } + } + } + } +} + +//========================================================================= +// CStripper ctor +//========================================================================= +CStripper::CStripper(int numtris, TRIANGLELIST ptriangles) +{ + // store trilist info + m_numtris = numtris; + m_ptriangles = ptriangles; + + m_pused = new int[numtris]; + assert(m_pused); + m_ptriinfo = new TRIANGLEINFO[numtris]; + assert(m_ptriinfo); + + // init triinfo + int itri; + for( itri = 0; itri < numtris; itri++) + { + m_ptriinfo[itri].neighbortri[0] = -1; + m_ptriinfo[itri].neighbortri[1] = -1; + m_ptriinfo[itri].neighbortri[2] = -1; + } + + // clear the used flag + memset(m_pused, 0, sizeof(m_pused[0]) * m_numtris); + + // go through all the triangles and find edges, neighbor counts + for(itri = 0; itri < numtris; itri++) + { + for(int ivert = 0; ivert < 3; ivert++) + { + if(!(m_pused[itri] & (1 << ivert))) + InitTriangleInfo(itri, ivert); + } + } + + // clear the used flags from InitTriangleInfo + memset(m_pused, 0, sizeof(m_pused[0]) * m_numtris); +} + +//========================================================================= +// CStripper dtor +//========================================================================= +CStripper::~CStripper() +{ + // free stuff + delete [] m_pused; + m_pused = NULL; + + delete [] m_ptriinfo; + m_ptriinfo = NULL; +} + +//========================================================================= +// Add an index to the cache - returns true if it was added, false otherwise +//========================================================================= +bool CVertCache::Add(int strip, int vertindex) +{ + // find index in cache + for(int iCache = 0; iCache < CACHE_SIZE; iCache++) + { + if(vertindex == m_rgCache[iCache]) + { + // if it's in the cache and it's from a different strip + // change the strip to the new one and count the cache hit + if(strip != m_rgCacheStrip[iCache]) + { + m_cachehits++; + m_rgCacheStrip[iCache] = strip; + return true; + } + + // we added this item to the cache earlier - carry on + return false; + } + } + + // not in cache, add vert and strip + m_rgCache[m_iCachePtr] = vertindex; + m_rgCacheStrip[m_iCachePtr] = strip; + m_iCachePtr = (m_iCachePtr + 1) % CACHE_SIZE; + return true; +} + +#ifdef _DEBUG +//========================================================================= +// Turn on c runtime leak checking, etc. +//========================================================================= +void EnableLeakChecking() +{ + int flCrtDbgFlags = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + + flCrtDbgFlags &= + ~(_CRTDBG_LEAK_CHECK_DF | + _CRTDBG_CHECK_ALWAYS_DF | + _CRTDBG_DELAY_FREE_MEM_DF); + + // always check for memory leaks + flCrtDbgFlags |= _CRTDBG_LEAK_CHECK_DF; + + // others you may / may not want to set + flCrtDbgFlags |= _CRTDBG_CHECK_ALWAYS_DF; + flCrtDbgFlags |= _CRTDBG_DELAY_FREE_MEM_DF; + + _CrtSetDbgFlag(flCrtDbgFlags); + + // all types of reports go via OutputDebugString + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG); + _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG); + _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG); + + // big errors and asserts get their own assert window + _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_WNDW); + _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_WNDW); + + // _CrtSetBreakAlloc(0); +} +#endif + +//========================================================================= +// Main Stripify routine +//========================================================================= +int Stripify(int numtris, WORD *ptriangles, int *pnumindices, WORD **ppstripindices) +{ + if(!numtris || !ptriangles) + return 0; + +#ifdef _DEBUG +// EnableLeakChecking(); +#endif + + CStripper stripper(numtris, (TRIANGLELIST)ptriangles); + + // map of various args to try stripifying mesh with + struct ARGMAP + { + int maxlen; // maximum length of strips + bool flookahead; // use sgi greedy lookahead (or not) + } rgargmap[] = + { + { 1024, true }, + { 1024, false }, + }; + static const int cargmaps = sizeof(rgargmap) / sizeof(rgargmap[0]); + STRIPLIST striplistbest; + int bestlistcost = 0; + + for(int imap = 0; imap < cargmaps; imap++) + { + STRIPLIST striplist; + + // build the strip with the various args + stripper.BuildStrips(&striplist, rgargmap[imap].maxlen, + rgargmap[imap].flookahead); + + // guesstimate the list cost and store it if it's good + int listcost = EstimateStripCost(&striplist); + if(!bestlistcost || (listcost < bestlistcost)) + { + // free the old best list + FreeStripListVerts(&striplistbest); + + // store the new best list + striplistbest = striplist; + bestlistcost = listcost; + assert(bestlistcost > 0); + } + else + { + FreeStripListVerts(&striplist); + } + } + +#ifdef NEVER + // Return the strips in [size1 i1 i2 i3][size2 i4 i5 i6]...[0] format + // Very useful for debugging... + return stripper.CreateManyStrips(&striplistbest, ppstripindices); +#endif // NEVER + + // return one big long strip + int numindices = stripper.CreateLongStrip(&striplistbest, ppstripindices); + + if(pnumindices) + *pnumindices = numindices; + return numindices; +} + +//========================================================================= +// Class used to vertices for locality of access. +//========================================================================= +struct SortEntry +{ +public: + int iFirstUsed; + int iOrigIndex; + + bool operator<(const SortEntry& rhs) const + { + return iFirstUsed < rhs.iFirstUsed; + } +}; + +//========================================================================= +// Reorder the vertices +//========================================================================= +void ComputeVertexPermutation(int numstripindices, WORD* pstripindices, + int* pnumverts, WORD** ppvertexpermutation) +{ + // Sort verts to maximize locality. + SortEntry* pSortTable = new SortEntry[*pnumverts]; + + // Fill in original index. + int i; + for( i = 0; i < *pnumverts; i++) + { + pSortTable[i].iOrigIndex = i; + pSortTable[i].iFirstUsed = -1; + } + + // Fill in first used flag. + for(i = 0; i < numstripindices; i++) + { + int index = pstripindices[i]; + + if(pSortTable[index].iFirstUsed == -1) + pSortTable[index].iFirstUsed = i; + } + + // Sort the table. + sort(pSortTable, pSortTable + *pnumverts); + + // Copy re-mapped to orignal vertex permutaion into output array. + *ppvertexpermutation = new WORD[*pnumverts]; + + for(i = 0; i < *pnumverts; i++) + { + (*ppvertexpermutation)[i] = pSortTable[i].iOrigIndex; + } + + // Build original to re-mapped permutation. + WORD* pInversePermutation = new WORD[numstripindices]; + + for(i = 0; i < *pnumverts; i++) + { + pInversePermutation[pSortTable[i].iOrigIndex] = i; + } + + // We need to remap indices as well. + for(i = 0; i < numstripindices; i++) + { + pstripindices[i] = pInversePermutation[pstripindices[i]]; + } + + delete[] pSortTable; + delete[] pInversePermutation; +} + diff --git a/mp/src/utils/common/mstristrip.h b/mp/src/utils/common/mstristrip.h index 626a0062..55c93198 100644 --- a/mp/src/utils/common/mstristrip.h +++ b/mp/src/utils/common/mstristrip.h @@ -1,43 +1,43 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -// -//=============================================================================// -//----------------------------------------------------------------------------- -// FILE: TRISTRIP.H -// -// Desc: tristrip header file -// -// Copyright (c) 1999-2000 Microsoft Corporation. All rights reserved. -//----------------------------------------------------------------------------- - -typedef unsigned short WORD; - -// -// Main Stripify routine. Returns number of strip indices contained -// in ppstripindices. Caller must delete [] ppstripindices. -// -int Stripify( - int numtris, // Number of triangles - WORD *ptriangles, // triangle indices pointer - int *pnumindices, // number of indices in ppstripindices (out) - WORD **ppstripindices // triangle strip indices -); - -// -// Re-arrange vertices so that they occur in the order that they are first -// used. This function doesn't actually move vertex data around, it returns -// an array that specifies where in the new vertex array each old vertex -// should go. It also re-maps the strip indices to use the new vertex -// locations. Caller must delete [] pVertexPermutation. -// -void ComputeVertexPermutation -( - int numstripindices, // Number of strip indices - WORD *pstripindices, // Strip indices - int *pnumverts, // Number of verts (in and out) - WORD **ppvertexpermutation // Map from orignal index to remapped index -); - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +//----------------------------------------------------------------------------- +// FILE: TRISTRIP.H +// +// Desc: tristrip header file +// +// Copyright (c) 1999-2000 Microsoft Corporation. All rights reserved. +//----------------------------------------------------------------------------- + +typedef unsigned short WORD; + +// +// Main Stripify routine. Returns number of strip indices contained +// in ppstripindices. Caller must delete [] ppstripindices. +// +int Stripify( + int numtris, // Number of triangles + WORD *ptriangles, // triangle indices pointer + int *pnumindices, // number of indices in ppstripindices (out) + WORD **ppstripindices // triangle strip indices +); + +// +// Re-arrange vertices so that they occur in the order that they are first +// used. This function doesn't actually move vertex data around, it returns +// an array that specifies where in the new vertex array each old vertex +// should go. It also re-maps the strip indices to use the new vertex +// locations. Caller must delete [] pVertexPermutation. +// +void ComputeVertexPermutation +( + int numstripindices, // Number of strip indices + WORD *pstripindices, // Strip indices + int *pnumverts, // Number of verts (in and out) + WORD **ppvertexpermutation // Map from orignal index to remapped index +); + diff --git a/mp/src/utils/common/pacifier.cpp b/mp/src/utils/common/pacifier.cpp index 6d9c73f2..41a10184 100644 --- a/mp/src/utils/common/pacifier.cpp +++ b/mp/src/utils/common/pacifier.cpp @@ -1,63 +1,63 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include -#include "basetypes.h" -#include "pacifier.h" -#include "tier0/dbg.h" - - -static int g_LastPacifierDrawn = -1; -static bool g_bPacifierSuppressed = false; - -#define clamp(a,b,c) ( (a) > (c) ? (c) : ( (a) < (b) ? (b) : (a) ) ) - -void StartPacifier( char const *pPrefix ) -{ - Msg( "%s", pPrefix ); - g_LastPacifierDrawn = -1; - UpdatePacifier( 0.001f ); -} - -void UpdatePacifier( float flPercent ) -{ - int iCur = (int)(flPercent * 40.0f); - iCur = clamp( iCur, g_LastPacifierDrawn, 40 ); - - if( iCur != g_LastPacifierDrawn && !g_bPacifierSuppressed ) - { - for( int i=g_LastPacifierDrawn+1; i <= iCur; i++ ) - { - if ( !( i % 4 ) ) - { - Msg("%d", i/4); - } - else - { - if( i != 40 ) - { - Msg("."); - } - } - } - - g_LastPacifierDrawn = iCur; - } -} - -void EndPacifier( bool bCarriageReturn ) -{ - UpdatePacifier(1); - - if( bCarriageReturn && !g_bPacifierSuppressed ) - Msg("\n"); -} - -void SuppressPacifier( bool bSuppress ) -{ - g_bPacifierSuppressed = bSuppress; -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include +#include "basetypes.h" +#include "pacifier.h" +#include "tier0/dbg.h" + + +static int g_LastPacifierDrawn = -1; +static bool g_bPacifierSuppressed = false; + +#define clamp(a,b,c) ( (a) > (c) ? (c) : ( (a) < (b) ? (b) : (a) ) ) + +void StartPacifier( char const *pPrefix ) +{ + Msg( "%s", pPrefix ); + g_LastPacifierDrawn = -1; + UpdatePacifier( 0.001f ); +} + +void UpdatePacifier( float flPercent ) +{ + int iCur = (int)(flPercent * 40.0f); + iCur = clamp( iCur, g_LastPacifierDrawn, 40 ); + + if( iCur != g_LastPacifierDrawn && !g_bPacifierSuppressed ) + { + for( int i=g_LastPacifierDrawn+1; i <= iCur; i++ ) + { + if ( !( i % 4 ) ) + { + Msg("%d", i/4); + } + else + { + if( i != 40 ) + { + Msg("."); + } + } + } + + g_LastPacifierDrawn = iCur; + } +} + +void EndPacifier( bool bCarriageReturn ) +{ + UpdatePacifier(1); + + if( bCarriageReturn && !g_bPacifierSuppressed ) + Msg("\n"); +} + +void SuppressPacifier( bool bSuppress ) +{ + g_bPacifierSuppressed = bSuppress; +} diff --git a/mp/src/utils/common/pacifier.h b/mp/src/utils/common/pacifier.h index 42ecd6ec..d64d6cda 100644 --- a/mp/src/utils/common/pacifier.h +++ b/mp/src/utils/common/pacifier.h @@ -1,23 +1,23 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#ifndef PACIFIER_H -#define PACIFIER_H -#ifdef _WIN32 -#pragma once -#endif - - -// Use these to display a pacifier like: -// ProcessBlock_Thread: 0...1...2...3...4...5...6...7...8...9... (0) -void StartPacifier( char const *pPrefix ); // Prints the prefix and resets the pacifier -void UpdatePacifier( float flPercent ); // percent value between 0 and 1. -void EndPacifier( bool bCarriageReturn = true ); // Completes pacifier as if 100% was done -void SuppressPacifier( bool bSuppress = true ); // Suppresses pacifier updates if another thread might still be firing them - - -#endif // PACIFIER_H +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef PACIFIER_H +#define PACIFIER_H +#ifdef _WIN32 +#pragma once +#endif + + +// Use these to display a pacifier like: +// ProcessBlock_Thread: 0...1...2...3...4...5...6...7...8...9... (0) +void StartPacifier( char const *pPrefix ); // Prints the prefix and resets the pacifier +void UpdatePacifier( float flPercent ); // percent value between 0 and 1. +void EndPacifier( bool bCarriageReturn = true ); // Completes pacifier as if 100% was done +void SuppressPacifier( bool bSuppress = true ); // Suppresses pacifier updates if another thread might still be firing them + + +#endif // PACIFIER_H diff --git a/mp/src/utils/common/physdll.cpp b/mp/src/utils/common/physdll.cpp index fae9810a..3df4b964 100644 --- a/mp/src/utils/common/physdll.cpp +++ b/mp/src/utils/common/physdll.cpp @@ -1,31 +1,31 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -// -//=============================================================================// -#include -#include "physdll.h" -#include "filesystem_tools.h" - -static CSysModule *pPhysicsModule = NULL; -CreateInterfaceFn GetPhysicsFactory( void ) -{ - if ( !pPhysicsModule ) - { - pPhysicsModule = g_pFullFileSystem->LoadModule( "VPHYSICS.DLL" ); - if ( !pPhysicsModule ) - return NULL; - } - - return Sys_GetFactory( pPhysicsModule ); -} - -void PhysicsDLLPath( const char *pPathname ) -{ - if ( !pPhysicsModule ) - { - pPhysicsModule = g_pFullFileSystem->LoadModule( pPathname ); - } -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#include +#include "physdll.h" +#include "filesystem_tools.h" + +static CSysModule *pPhysicsModule = NULL; +CreateInterfaceFn GetPhysicsFactory( void ) +{ + if ( !pPhysicsModule ) + { + pPhysicsModule = g_pFullFileSystem->LoadModule( "VPHYSICS.DLL" ); + if ( !pPhysicsModule ) + return NULL; + } + + return Sys_GetFactory( pPhysicsModule ); +} + +void PhysicsDLLPath( const char *pPathname ) +{ + if ( !pPhysicsModule ) + { + pPhysicsModule = g_pFullFileSystem->LoadModule( pPathname ); + } +} diff --git a/mp/src/utils/common/physdll.h b/mp/src/utils/common/physdll.h index 151a9e50..f6faf2d8 100644 --- a/mp/src/utils/common/physdll.h +++ b/mp/src/utils/common/physdll.h @@ -1,30 +1,30 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#ifndef PHYSDLL_H -#define PHYSDLL_H -#pragma once - - -#ifdef __cplusplus -#include "vphysics_interface.h" -class IPhysics; -class IPhysicsCollision; - -extern CreateInterfaceFn GetPhysicsFactory( void ); - -extern "C" { -#endif - -// tools need to force the path -void PhysicsDLLPath( const char *pPathname ); - -#ifdef __cplusplus -} -#endif - -#endif // PHYSDLL_H +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef PHYSDLL_H +#define PHYSDLL_H +#pragma once + + +#ifdef __cplusplus +#include "vphysics_interface.h" +class IPhysics; +class IPhysicsCollision; + +extern CreateInterfaceFn GetPhysicsFactory( void ); + +extern "C" { +#endif + +// tools need to force the path +void PhysicsDLLPath( const char *pPathname ); + +#ifdef __cplusplus +} +#endif + +#endif // PHYSDLL_H diff --git a/mp/src/utils/common/polylib.cpp b/mp/src/utils/common/polylib.cpp index 36690a27..63c91449 100644 --- a/mp/src/utils/common/polylib.cpp +++ b/mp/src/utils/common/polylib.cpp @@ -1,915 +1,915 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// $NoKeywords: $ -//=============================================================================// - -#include "cmdlib.h" -#include "mathlib/mathlib.h" -#include "polylib.h" -#include "worldsize.h" -#include "threads.h" -#include "tier0/dbg.h" - -// doesn't seem to need to be here? -- in threads.h -//extern int numthreads; - -// counters are only bumped when running single threaded, -// because they are an awefull coherence problem -int c_active_windings; -int c_peak_windings; -int c_winding_allocs; -int c_winding_points; - -void pw(winding_t *w) -{ - int i; - for (i=0 ; inumpoints ; i++) - printf ("(%5.1f, %5.1f, %5.1f)\n",w->p[i][0], w->p[i][1],w->p[i][2]); -} - -winding_t *winding_pool[MAX_POINTS_ON_WINDING+4]; - -/* -============= -AllocWinding -============= -*/ -winding_t *AllocWinding (int points) -{ - winding_t *w; - - if (numthreads == 1) - { - c_winding_allocs++; - c_winding_points += points; - c_active_windings++; - if (c_active_windings > c_peak_windings) - c_peak_windings = c_active_windings; - } - ThreadLock(); - if (winding_pool[points]) - { - w = winding_pool[points]; - winding_pool[points] = w->next; - } - else - { - w = (winding_t *)malloc(sizeof(*w)); - w->p = (Vector *)calloc( points, sizeof(Vector) ); - } - ThreadUnlock(); - w->numpoints = 0; // None are occupied yet even though allocated. - w->maxpoints = points; - w->next = NULL; - return w; -} - -void FreeWinding (winding_t *w) -{ - if (w->numpoints == 0xdeaddead) - Error ("FreeWinding: freed a freed winding"); - - ThreadLock(); - w->numpoints = 0xdeaddead; // flag as freed - w->next = winding_pool[w->maxpoints]; - winding_pool[w->maxpoints] = w; - ThreadUnlock(); -} - -/* -============ -RemoveColinearPoints -============ -*/ -int c_removed; - -void RemoveColinearPoints (winding_t *w) -{ - int i, j, k; - Vector v1, v2; - int nump; - Vector p[MAX_POINTS_ON_WINDING]; - - nump = 0; - for (i=0 ; inumpoints ; i++) - { - j = (i+1)%w->numpoints; - k = (i+w->numpoints-1)%w->numpoints; - VectorSubtract (w->p[j], w->p[i], v1); - VectorSubtract (w->p[i], w->p[k], v2); - VectorNormalize(v1); - VectorNormalize(v2); - if (DotProduct(v1, v2) < 0.999) - { - VectorCopy (w->p[i], p[nump]); - nump++; - } - } - - if (nump == w->numpoints) - return; - - if (numthreads == 1) - c_removed += w->numpoints - nump; - w->numpoints = nump; - memcpy (w->p, p, nump*sizeof(p[0])); -} - -/* -============ -WindingPlane -============ -*/ -void WindingPlane (winding_t *w, Vector &normal, vec_t *dist) -{ - Vector v1, v2; - - VectorSubtract (w->p[1], w->p[0], v1); - - // HACKHACK: Avoid potentially collinear verts - if ( w->numpoints > 3 ) - { - VectorSubtract (w->p[3], w->p[0], v2); - } - else - { - VectorSubtract (w->p[2], w->p[0], v2); - } - CrossProduct (v2, v1, normal); - VectorNormalize (normal); - *dist = DotProduct (w->p[0], normal); - -} - - -/* -============= -WindingArea -============= -*/ -vec_t WindingArea(winding_t *w) -{ - int i; - Vector d1, d2, cross; - vec_t total; - - total = 0; - for (i=2 ; inumpoints ; i++) - { - VectorSubtract (w->p[i-1], w->p[0], d1); - VectorSubtract (w->p[i], w->p[0], d2); - CrossProduct (d1, d2, cross); - total += VectorLength ( cross ); - } - return total * 0.5; -} - -void WindingBounds (winding_t *w, Vector &mins, Vector &maxs) -{ - vec_t v; - int i,j; - - mins[0] = mins[1] = mins[2] = 99999; - maxs[0] = maxs[1] = maxs[2] = -99999; - - for (i=0 ; inumpoints ; i++) - { - for (j=0 ; j<3 ; j++) - { - v = w->p[i][j]; - if (v < mins[j]) - mins[j] = v; - if (v > maxs[j]) - maxs[j] = v; - } - } -} - -/* -============= -WindingCenter -============= -*/ -void WindingCenter (winding_t *w, Vector ¢er) -{ - int i; - float scale; - - VectorCopy (vec3_origin, center); - for (i=0 ; inumpoints ; i++) - VectorAdd (w->p[i], center, center); - - scale = 1.0/w->numpoints; - VectorScale (center, scale, center); -} - - - -/* -============= -WindingCenter -============= -*/ -vec_t WindingAreaAndBalancePoint( winding_t *w, Vector ¢er ) -{ - int i; - Vector d1, d2, cross; - vec_t total; - - VectorCopy (vec3_origin, center); - if ( !w ) - return 0.0f; - - total = 0; - for (i=2 ; inumpoints ; i++) - { - VectorSubtract (w->p[i-1], w->p[0], d1); - VectorSubtract (w->p[i], w->p[0], d2); - CrossProduct (d1, d2, cross); - float area = VectorLength ( cross ); - total += area; - - // center of triangle, weighed by area - VectorMA( center, area / 3.0, w->p[i-1], center ); - VectorMA( center, area / 3.0, w->p[i], center ); - VectorMA( center, area / 3.0, w->p[0], center ); - } - if (total) - { - VectorScale( center, 1.0 / total, center ); - } - return total * 0.5; -} - -/* -================= -BaseWindingForPlane -================= -*/ -winding_t *BaseWindingForPlane (const Vector &normal, vec_t dist) -{ - int i, x; - vec_t max, v; - Vector org, vright, vup; - winding_t *w; - -// find the major axis - - max = -1; - x = -1; - for (i=0 ; i<3; i++) - { - v = fabs(normal[i]); - if (v > max) - { - x = i; - max = v; - } - } - if (x==-1) - Error ("BaseWindingForPlane: no axis found"); - - VectorCopy (vec3_origin, vup); - switch (x) - { - case 0: - case 1: - vup[2] = 1; - break; - case 2: - vup[0] = 1; - break; - } - - v = DotProduct (vup, normal); - VectorMA (vup, -v, normal, vup); - VectorNormalize (vup); - - VectorScale (normal, dist, org); - - CrossProduct (vup, normal, vright); - - VectorScale (vup, (MAX_COORD_INTEGER*4), vup); - VectorScale (vright, (MAX_COORD_INTEGER*4), vright); - -// project a really big axis aligned box onto the plane - w = AllocWinding (4); - - VectorSubtract (org, vright, w->p[0]); - VectorAdd (w->p[0], vup, w->p[0]); - - VectorAdd (org, vright, w->p[1]); - VectorAdd (w->p[1], vup, w->p[1]); - - VectorAdd (org, vright, w->p[2]); - VectorSubtract (w->p[2], vup, w->p[2]); - - VectorSubtract (org, vright, w->p[3]); - VectorSubtract (w->p[3], vup, w->p[3]); - - w->numpoints = 4; - - return w; -} - -/* -================== -CopyWinding -================== -*/ -winding_t *CopyWinding (winding_t *w) -{ - int size; - winding_t *c; - - c = AllocWinding (w->numpoints); - c->numpoints = w->numpoints; - size = w->numpoints*sizeof(w->p[0]); - memcpy (c->p, w->p, size); - return c; -} - -/* -================== -ReverseWinding -================== -*/ -winding_t *ReverseWinding (winding_t *w) -{ - int i; - winding_t *c; - - c = AllocWinding (w->numpoints); - for (i=0 ; inumpoints ; i++) - { - VectorCopy (w->p[w->numpoints-1-i], c->p[i]); - } - c->numpoints = w->numpoints; - return c; -} - - -// BUGBUG: Hunt this down - it's causing CSG errors -#pragma optimize("g", off) -/* -============= -ClipWindingEpsilon -============= -*/ - -void ClipWindingEpsilon (winding_t *in, const Vector &normal, vec_t dist, - vec_t epsilon, winding_t **front, winding_t **back) -{ - vec_t dists[MAX_POINTS_ON_WINDING+4]; - int sides[MAX_POINTS_ON_WINDING+4]; - int counts[3]; - vec_t dot; - int i, j; - Vector mid = vec3_origin; - winding_t *f, *b; - int maxpts; - - counts[0] = counts[1] = counts[2] = 0; - -// determine sides for each point - for (i=0 ; inumpoints ; i++) - { - dot = DotProduct (in->p[i], normal); - dot -= dist; - dists[i] = dot; - if (dot > epsilon) - sides[i] = SIDE_FRONT; - else if (dot < -epsilon) - sides[i] = SIDE_BACK; - else - { - sides[i] = SIDE_ON; - } - counts[sides[i]]++; - } - sides[i] = sides[0]; - dists[i] = dists[0]; - - *front = *back = NULL; - - if (!counts[0]) - { - *back = CopyWinding (in); - return; - } - if (!counts[1]) - { - *front = CopyWinding (in); - return; - } - - maxpts = in->numpoints+4; // cant use counts[0]+2 because - // of fp grouping errors - - *front = f = AllocWinding (maxpts); - *back = b = AllocWinding (maxpts); - - for (i=0 ; inumpoints ; i++) - { - Vector& p1 = in->p[i]; - - if (sides[i] == SIDE_ON) - { - VectorCopy (p1, f->p[f->numpoints]); - f->numpoints++; - VectorCopy (p1, b->p[b->numpoints]); - b->numpoints++; - continue; - } - - if (sides[i] == SIDE_FRONT) - { - VectorCopy (p1, f->p[f->numpoints]); - f->numpoints++; - } - if (sides[i] == SIDE_BACK) - { - VectorCopy (p1, b->p[b->numpoints]); - b->numpoints++; - } - - if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i]) - continue; - - // generate a split point - Vector& p2 = in->p[(i+1)%in->numpoints]; - - dot = dists[i] / (dists[i]-dists[i+1]); - for (j=0 ; j<3 ; j++) - { // avoid round off error when possible - if (normal[j] == 1) - mid[j] = dist; - else if (normal[j] == -1) - mid[j] = -dist; - else - mid[j] = p1[j] + dot*(p2[j]-p1[j]); - } - - VectorCopy (mid, f->p[f->numpoints]); - f->numpoints++; - VectorCopy (mid, b->p[b->numpoints]); - b->numpoints++; - } - - if (f->numpoints > maxpts || b->numpoints > maxpts) - Error ("ClipWinding: points exceeded estimate"); - if (f->numpoints > MAX_POINTS_ON_WINDING || b->numpoints > MAX_POINTS_ON_WINDING) - Error ("ClipWinding: MAX_POINTS_ON_WINDING"); -} -#pragma optimize("", on) - - -// NOTE: This is identical to ClipWindingEpsilon, but it does a pre/post translation to improve precision -void ClipWindingEpsilon_Offset( winding_t *in, const Vector &normal, vec_t dist, vec_t epsilon, winding_t **front, winding_t **back, const Vector &offset ) -{ - TranslateWinding( in, offset ); - ClipWindingEpsilon( in, normal, dist+DotProduct(offset,normal), epsilon, front, back ); - TranslateWinding( in, -offset ); - if ( front && *front ) - { - TranslateWinding( *front, -offset ); - } - if ( back && *back ) - { - TranslateWinding( *back, -offset ); - } -} - -void ClassifyWindingEpsilon_Offset( winding_t *in, const Vector &normal, vec_t dist, vec_t epsilon, winding_t **front, winding_t **back, winding_t **on, const Vector &offset) -{ - TranslateWinding( in, offset ); - ClassifyWindingEpsilon( in, normal, dist+DotProduct(offset,normal), epsilon, front, back, on ); - TranslateWinding( in, -offset ); - if ( front && *front ) - { - TranslateWinding( *front, -offset ); - } - if ( back && *back ) - { - TranslateWinding( *back, -offset ); - } - if ( on && *on ) - { - TranslateWinding( *on, -offset ); - } -} - -/* -============= -ClassifyWindingEpsilon -============= -*/ -// This version returns the winding as "on" if all verts lie in the plane -void ClassifyWindingEpsilon( winding_t *in, const Vector &normal, vec_t dist, - vec_t epsilon, winding_t **front, winding_t **back, winding_t **on) -{ - vec_t dists[MAX_POINTS_ON_WINDING+4]; - int sides[MAX_POINTS_ON_WINDING+4]; - int counts[3]; - vec_t dot; - int i, j; - Vector mid = vec3_origin; - winding_t *f, *b; - int maxpts; - - counts[0] = counts[1] = counts[2] = 0; - -// determine sides for each point - for (i=0 ; inumpoints ; i++) - { - dot = DotProduct (in->p[i], normal); - dot -= dist; - dists[i] = dot; - if (dot > epsilon) - sides[i] = SIDE_FRONT; - else if (dot < -epsilon) - sides[i] = SIDE_BACK; - else - { - sides[i] = SIDE_ON; - } - counts[sides[i]]++; - } - sides[i] = sides[0]; - dists[i] = dists[0]; - - *front = *back = *on = NULL; - - if ( !counts[0] && !counts[1] ) - { - *on = CopyWinding(in); - return; - } - - if (!counts[0]) - { - *back = CopyWinding(in); - return; - } - if (!counts[1]) - { - *front = CopyWinding(in); - return; - } - - maxpts = in->numpoints+4; // cant use counts[0]+2 because - // of fp grouping errors - - *front = f = AllocWinding (maxpts); - *back = b = AllocWinding (maxpts); - - for (i=0 ; inumpoints ; i++) - { - Vector& p1 = in->p[i]; - - if (sides[i] == SIDE_ON) - { - VectorCopy (p1, f->p[f->numpoints]); - f->numpoints++; - VectorCopy (p1, b->p[b->numpoints]); - b->numpoints++; - continue; - } - - if (sides[i] == SIDE_FRONT) - { - VectorCopy (p1, f->p[f->numpoints]); - f->numpoints++; - } - if (sides[i] == SIDE_BACK) - { - VectorCopy (p1, b->p[b->numpoints]); - b->numpoints++; - } - - if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i]) - continue; - - // generate a split point - Vector& p2 = in->p[(i+1)%in->numpoints]; - - dot = dists[i] / (dists[i]-dists[i+1]); - for (j=0 ; j<3 ; j++) - { // avoid round off error when possible - if (normal[j] == 1) - mid[j] = dist; - else if (normal[j] == -1) - mid[j] = -dist; - else - mid[j] = p1[j] + dot*(p2[j]-p1[j]); - } - - VectorCopy (mid, f->p[f->numpoints]); - f->numpoints++; - VectorCopy (mid, b->p[b->numpoints]); - b->numpoints++; - } - - if (f->numpoints > maxpts || b->numpoints > maxpts) - Error ("ClipWinding: points exceeded estimate"); - if (f->numpoints > MAX_POINTS_ON_WINDING || b->numpoints > MAX_POINTS_ON_WINDING) - Error ("ClipWinding: MAX_POINTS_ON_WINDING"); -} - -/* -============= -ChopWindingInPlace -============= -*/ -void ChopWindingInPlace (winding_t **inout, const Vector &normal, vec_t dist, vec_t epsilon) -{ - winding_t *in; - vec_t dists[MAX_POINTS_ON_WINDING+4]; - int sides[MAX_POINTS_ON_WINDING+4]; - int counts[3]; - vec_t dot; - int i, j; - Vector mid = vec3_origin; - winding_t *f; - int maxpts; - - in = *inout; - counts[0] = counts[1] = counts[2] = 0; -// determine sides for each point - for (i=0 ; inumpoints ; i++) - { - dot = DotProduct (in->p[i], normal); - dot -= dist; - dists[i] = dot; - if (dot > epsilon) - { - sides[i] = SIDE_FRONT; - } - else if (dot < -epsilon) - { - sides[i] = SIDE_BACK; - } - else - { - sides[i] = SIDE_ON; - } - counts[sides[i]]++; - } - sides[i] = sides[0]; - dists[i] = dists[0]; - - if (!counts[0]) - { - FreeWinding (in); - *inout = NULL; - return; - } - if (!counts[1]) - return; // inout stays the same - - maxpts = in->numpoints+4; // cant use counts[0]+2 because - // of fp grouping errors - - f = AllocWinding (maxpts); - - for (i=0 ; inumpoints ; i++) - { - Vector& p1 = in->p[i]; - - if (sides[i] == SIDE_ON) - { - VectorCopy (p1, f->p[f->numpoints]); - f->numpoints++; - continue; - } - - if (sides[i] == SIDE_FRONT) - { - VectorCopy (p1, f->p[f->numpoints]); - f->numpoints++; - } - - if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i]) - continue; - - // generate a split point - Vector& p2 = in->p[(i+1)%in->numpoints]; - - dot = dists[i] / (dists[i]-dists[i+1]); - for (j=0 ; j<3 ; j++) - { // avoid round off error when possible - if (normal[j] == 1) - mid[j] = dist; - else if (normal[j] == -1) - mid[j] = -dist; - else - mid[j] = p1[j] + dot*(p2[j]-p1[j]); - } - - VectorCopy (mid, f->p[f->numpoints]); - f->numpoints++; - } - - if (f->numpoints > maxpts) - Error ("ClipWinding: points exceeded estimate"); - if (f->numpoints > MAX_POINTS_ON_WINDING) - Error ("ClipWinding: MAX_POINTS_ON_WINDING"); - - FreeWinding (in); - *inout = f; -} - - -/* -================= -ChopWinding - -Returns the fragment of in that is on the front side -of the cliping plane. The original is freed. -================= -*/ -winding_t *ChopWinding (winding_t *in, const Vector &normal, vec_t dist) -{ - winding_t *f, *b; - - ClipWindingEpsilon (in, normal, dist, ON_EPSILON, &f, &b); - FreeWinding (in); - if (b) - FreeWinding (b); - return f; -} - - -/* -================= -CheckWinding - -================= -*/ -void CheckWinding (winding_t *w) -{ - int i, j; - vec_t d, edgedist; - Vector dir, edgenormal, facenormal; - vec_t area; - vec_t facedist; - - if (w->numpoints < 3) - Error ("CheckWinding: %i points",w->numpoints); - - area = WindingArea(w); - if (area < 1) - Error ("CheckWinding: %f area", area); - - WindingPlane (w, facenormal, &facedist); - - for (i=0 ; inumpoints ; i++) - { - Vector& p1 = w->p[i]; - - for (j=0 ; j<3 ; j++) - { - if (p1[j] > MAX_COORD_INTEGER || p1[j] < MIN_COORD_INTEGER) - Error ("CheckFace: out of range: %f",p1[j]); - } - - j = i+1 == w->numpoints ? 0 : i+1; - - // check the point is on the face plane - d = DotProduct (p1, facenormal) - facedist; - if (d < -ON_EPSILON || d > ON_EPSILON) - Error ("CheckWinding: point off plane"); - - // check the edge isnt degenerate - Vector& p2 = w->p[j]; - VectorSubtract (p2, p1, dir); - - if (VectorLength (dir) < ON_EPSILON) - Error ("CheckWinding: degenerate edge"); - - CrossProduct (facenormal, dir, edgenormal); - VectorNormalize (edgenormal); - edgedist = DotProduct (p1, edgenormal); - edgedist += ON_EPSILON; - - // all other points must be on front side - for (j=0 ; jnumpoints ; j++) - { - if (j == i) - continue; - d = DotProduct (w->p[j], edgenormal); - if (d > edgedist) - Error ("CheckWinding: non-convex"); - } - } -} - - -/* -============ -WindingOnPlaneSide -============ -*/ -int WindingOnPlaneSide (winding_t *w, const Vector &normal, vec_t dist) -{ - qboolean front, back; - int i; - vec_t d; - - front = false; - back = false; - for (i=0 ; inumpoints ; i++) - { - d = DotProduct (w->p[i], normal) - dist; - if (d < -ON_EPSILON) - { - if (front) - return SIDE_CROSS; - back = true; - continue; - } - if (d > ON_EPSILON) - { - if (back) - return SIDE_CROSS; - front = true; - continue; - } - } - - if (back) - return SIDE_BACK; - if (front) - return SIDE_FRONT; - return SIDE_ON; -} - - -//----------------------------------------------------------------------------- -// Purpose: 2d point inside of winding test (assumes the point resides in the -// winding plane) -//----------------------------------------------------------------------------- -bool PointInWinding( const Vector &pt, winding_t *pWinding ) -{ - if( !pWinding ) - return false; - -#if 0 - // - // NOTE: this will be a quicker way to calculate this, however I don't - // know the trick off hand (post dot product tests??) - // TODO: look in graphics gems!!!! (cab) - // - - Vector edge1, edge2; - for( int ndxPt = 0; ndxPt < pWinding->numpoints; ndxPt++ ) - { - edge1 = pWinding->p[ndxPt] - pt; - edge2 = pWinding->p[(ndxPt+1)%pWinding->numpoints] - pt; - - VectorNormalize( edge1 ); - VectorNormalize( edge2 ); - - if( edge2.Dot( edge1 ) < 0.0f ) - return false; - } - - return true; - -#else - Vector edge, toPt, cross, testCross; - - // - // get the first normal to test - // - toPt = pt - pWinding->p[0]; - edge = pWinding->p[1] - pWinding->p[0]; - testCross = edge.Cross( toPt ); - VectorNormalize( testCross ); - - for( int ndxPt = 1; ndxPt < pWinding->numpoints; ndxPt++ ) - { - toPt = pt - pWinding->p[ndxPt]; - edge = pWinding->p[(ndxPt+1)%pWinding->numpoints] - pWinding->p[ndxPt]; - cross = edge.Cross( toPt ); - VectorNormalize( cross ); - - if( cross.Dot( testCross ) < 0.0f ) - return false; - } - - return true; -#endif -} - -void TranslateWinding( winding_t *pWinding, const Vector &offset ) -{ - for ( int i = 0; i < pWinding->numpoints; i++ ) - { - pWinding->p[i] += offset; - } -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//=============================================================================// + +#include "cmdlib.h" +#include "mathlib/mathlib.h" +#include "polylib.h" +#include "worldsize.h" +#include "threads.h" +#include "tier0/dbg.h" + +// doesn't seem to need to be here? -- in threads.h +//extern int numthreads; + +// counters are only bumped when running single threaded, +// because they are an awefull coherence problem +int c_active_windings; +int c_peak_windings; +int c_winding_allocs; +int c_winding_points; + +void pw(winding_t *w) +{ + int i; + for (i=0 ; inumpoints ; i++) + printf ("(%5.1f, %5.1f, %5.1f)\n",w->p[i][0], w->p[i][1],w->p[i][2]); +} + +winding_t *winding_pool[MAX_POINTS_ON_WINDING+4]; + +/* +============= +AllocWinding +============= +*/ +winding_t *AllocWinding (int points) +{ + winding_t *w; + + if (numthreads == 1) + { + c_winding_allocs++; + c_winding_points += points; + c_active_windings++; + if (c_active_windings > c_peak_windings) + c_peak_windings = c_active_windings; + } + ThreadLock(); + if (winding_pool[points]) + { + w = winding_pool[points]; + winding_pool[points] = w->next; + } + else + { + w = (winding_t *)malloc(sizeof(*w)); + w->p = (Vector *)calloc( points, sizeof(Vector) ); + } + ThreadUnlock(); + w->numpoints = 0; // None are occupied yet even though allocated. + w->maxpoints = points; + w->next = NULL; + return w; +} + +void FreeWinding (winding_t *w) +{ + if (w->numpoints == 0xdeaddead) + Error ("FreeWinding: freed a freed winding"); + + ThreadLock(); + w->numpoints = 0xdeaddead; // flag as freed + w->next = winding_pool[w->maxpoints]; + winding_pool[w->maxpoints] = w; + ThreadUnlock(); +} + +/* +============ +RemoveColinearPoints +============ +*/ +int c_removed; + +void RemoveColinearPoints (winding_t *w) +{ + int i, j, k; + Vector v1, v2; + int nump; + Vector p[MAX_POINTS_ON_WINDING]; + + nump = 0; + for (i=0 ; inumpoints ; i++) + { + j = (i+1)%w->numpoints; + k = (i+w->numpoints-1)%w->numpoints; + VectorSubtract (w->p[j], w->p[i], v1); + VectorSubtract (w->p[i], w->p[k], v2); + VectorNormalize(v1); + VectorNormalize(v2); + if (DotProduct(v1, v2) < 0.999) + { + VectorCopy (w->p[i], p[nump]); + nump++; + } + } + + if (nump == w->numpoints) + return; + + if (numthreads == 1) + c_removed += w->numpoints - nump; + w->numpoints = nump; + memcpy (w->p, p, nump*sizeof(p[0])); +} + +/* +============ +WindingPlane +============ +*/ +void WindingPlane (winding_t *w, Vector &normal, vec_t *dist) +{ + Vector v1, v2; + + VectorSubtract (w->p[1], w->p[0], v1); + + // HACKHACK: Avoid potentially collinear verts + if ( w->numpoints > 3 ) + { + VectorSubtract (w->p[3], w->p[0], v2); + } + else + { + VectorSubtract (w->p[2], w->p[0], v2); + } + CrossProduct (v2, v1, normal); + VectorNormalize (normal); + *dist = DotProduct (w->p[0], normal); + +} + + +/* +============= +WindingArea +============= +*/ +vec_t WindingArea(winding_t *w) +{ + int i; + Vector d1, d2, cross; + vec_t total; + + total = 0; + for (i=2 ; inumpoints ; i++) + { + VectorSubtract (w->p[i-1], w->p[0], d1); + VectorSubtract (w->p[i], w->p[0], d2); + CrossProduct (d1, d2, cross); + total += VectorLength ( cross ); + } + return total * 0.5; +} + +void WindingBounds (winding_t *w, Vector &mins, Vector &maxs) +{ + vec_t v; + int i,j; + + mins[0] = mins[1] = mins[2] = 99999; + maxs[0] = maxs[1] = maxs[2] = -99999; + + for (i=0 ; inumpoints ; i++) + { + for (j=0 ; j<3 ; j++) + { + v = w->p[i][j]; + if (v < mins[j]) + mins[j] = v; + if (v > maxs[j]) + maxs[j] = v; + } + } +} + +/* +============= +WindingCenter +============= +*/ +void WindingCenter (winding_t *w, Vector ¢er) +{ + int i; + float scale; + + VectorCopy (vec3_origin, center); + for (i=0 ; inumpoints ; i++) + VectorAdd (w->p[i], center, center); + + scale = 1.0/w->numpoints; + VectorScale (center, scale, center); +} + + + +/* +============= +WindingCenter +============= +*/ +vec_t WindingAreaAndBalancePoint( winding_t *w, Vector ¢er ) +{ + int i; + Vector d1, d2, cross; + vec_t total; + + VectorCopy (vec3_origin, center); + if ( !w ) + return 0.0f; + + total = 0; + for (i=2 ; inumpoints ; i++) + { + VectorSubtract (w->p[i-1], w->p[0], d1); + VectorSubtract (w->p[i], w->p[0], d2); + CrossProduct (d1, d2, cross); + float area = VectorLength ( cross ); + total += area; + + // center of triangle, weighed by area + VectorMA( center, area / 3.0, w->p[i-1], center ); + VectorMA( center, area / 3.0, w->p[i], center ); + VectorMA( center, area / 3.0, w->p[0], center ); + } + if (total) + { + VectorScale( center, 1.0 / total, center ); + } + return total * 0.5; +} + +/* +================= +BaseWindingForPlane +================= +*/ +winding_t *BaseWindingForPlane (const Vector &normal, vec_t dist) +{ + int i, x; + vec_t max, v; + Vector org, vright, vup; + winding_t *w; + +// find the major axis + + max = -1; + x = -1; + for (i=0 ; i<3; i++) + { + v = fabs(normal[i]); + if (v > max) + { + x = i; + max = v; + } + } + if (x==-1) + Error ("BaseWindingForPlane: no axis found"); + + VectorCopy (vec3_origin, vup); + switch (x) + { + case 0: + case 1: + vup[2] = 1; + break; + case 2: + vup[0] = 1; + break; + } + + v = DotProduct (vup, normal); + VectorMA (vup, -v, normal, vup); + VectorNormalize (vup); + + VectorScale (normal, dist, org); + + CrossProduct (vup, normal, vright); + + VectorScale (vup, (MAX_COORD_INTEGER*4), vup); + VectorScale (vright, (MAX_COORD_INTEGER*4), vright); + +// project a really big axis aligned box onto the plane + w = AllocWinding (4); + + VectorSubtract (org, vright, w->p[0]); + VectorAdd (w->p[0], vup, w->p[0]); + + VectorAdd (org, vright, w->p[1]); + VectorAdd (w->p[1], vup, w->p[1]); + + VectorAdd (org, vright, w->p[2]); + VectorSubtract (w->p[2], vup, w->p[2]); + + VectorSubtract (org, vright, w->p[3]); + VectorSubtract (w->p[3], vup, w->p[3]); + + w->numpoints = 4; + + return w; +} + +/* +================== +CopyWinding +================== +*/ +winding_t *CopyWinding (winding_t *w) +{ + int size; + winding_t *c; + + c = AllocWinding (w->numpoints); + c->numpoints = w->numpoints; + size = w->numpoints*sizeof(w->p[0]); + memcpy (c->p, w->p, size); + return c; +} + +/* +================== +ReverseWinding +================== +*/ +winding_t *ReverseWinding (winding_t *w) +{ + int i; + winding_t *c; + + c = AllocWinding (w->numpoints); + for (i=0 ; inumpoints ; i++) + { + VectorCopy (w->p[w->numpoints-1-i], c->p[i]); + } + c->numpoints = w->numpoints; + return c; +} + + +// BUGBUG: Hunt this down - it's causing CSG errors +#pragma optimize("g", off) +/* +============= +ClipWindingEpsilon +============= +*/ + +void ClipWindingEpsilon (winding_t *in, const Vector &normal, vec_t dist, + vec_t epsilon, winding_t **front, winding_t **back) +{ + vec_t dists[MAX_POINTS_ON_WINDING+4]; + int sides[MAX_POINTS_ON_WINDING+4]; + int counts[3]; + vec_t dot; + int i, j; + Vector mid = vec3_origin; + winding_t *f, *b; + int maxpts; + + counts[0] = counts[1] = counts[2] = 0; + +// determine sides for each point + for (i=0 ; inumpoints ; i++) + { + dot = DotProduct (in->p[i], normal); + dot -= dist; + dists[i] = dot; + if (dot > epsilon) + sides[i] = SIDE_FRONT; + else if (dot < -epsilon) + sides[i] = SIDE_BACK; + else + { + sides[i] = SIDE_ON; + } + counts[sides[i]]++; + } + sides[i] = sides[0]; + dists[i] = dists[0]; + + *front = *back = NULL; + + if (!counts[0]) + { + *back = CopyWinding (in); + return; + } + if (!counts[1]) + { + *front = CopyWinding (in); + return; + } + + maxpts = in->numpoints+4; // cant use counts[0]+2 because + // of fp grouping errors + + *front = f = AllocWinding (maxpts); + *back = b = AllocWinding (maxpts); + + for (i=0 ; inumpoints ; i++) + { + Vector& p1 = in->p[i]; + + if (sides[i] == SIDE_ON) + { + VectorCopy (p1, f->p[f->numpoints]); + f->numpoints++; + VectorCopy (p1, b->p[b->numpoints]); + b->numpoints++; + continue; + } + + if (sides[i] == SIDE_FRONT) + { + VectorCopy (p1, f->p[f->numpoints]); + f->numpoints++; + } + if (sides[i] == SIDE_BACK) + { + VectorCopy (p1, b->p[b->numpoints]); + b->numpoints++; + } + + if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i]) + continue; + + // generate a split point + Vector& p2 = in->p[(i+1)%in->numpoints]; + + dot = dists[i] / (dists[i]-dists[i+1]); + for (j=0 ; j<3 ; j++) + { // avoid round off error when possible + if (normal[j] == 1) + mid[j] = dist; + else if (normal[j] == -1) + mid[j] = -dist; + else + mid[j] = p1[j] + dot*(p2[j]-p1[j]); + } + + VectorCopy (mid, f->p[f->numpoints]); + f->numpoints++; + VectorCopy (mid, b->p[b->numpoints]); + b->numpoints++; + } + + if (f->numpoints > maxpts || b->numpoints > maxpts) + Error ("ClipWinding: points exceeded estimate"); + if (f->numpoints > MAX_POINTS_ON_WINDING || b->numpoints > MAX_POINTS_ON_WINDING) + Error ("ClipWinding: MAX_POINTS_ON_WINDING"); +} +#pragma optimize("", on) + + +// NOTE: This is identical to ClipWindingEpsilon, but it does a pre/post translation to improve precision +void ClipWindingEpsilon_Offset( winding_t *in, const Vector &normal, vec_t dist, vec_t epsilon, winding_t **front, winding_t **back, const Vector &offset ) +{ + TranslateWinding( in, offset ); + ClipWindingEpsilon( in, normal, dist+DotProduct(offset,normal), epsilon, front, back ); + TranslateWinding( in, -offset ); + if ( front && *front ) + { + TranslateWinding( *front, -offset ); + } + if ( back && *back ) + { + TranslateWinding( *back, -offset ); + } +} + +void ClassifyWindingEpsilon_Offset( winding_t *in, const Vector &normal, vec_t dist, vec_t epsilon, winding_t **front, winding_t **back, winding_t **on, const Vector &offset) +{ + TranslateWinding( in, offset ); + ClassifyWindingEpsilon( in, normal, dist+DotProduct(offset,normal), epsilon, front, back, on ); + TranslateWinding( in, -offset ); + if ( front && *front ) + { + TranslateWinding( *front, -offset ); + } + if ( back && *back ) + { + TranslateWinding( *back, -offset ); + } + if ( on && *on ) + { + TranslateWinding( *on, -offset ); + } +} + +/* +============= +ClassifyWindingEpsilon +============= +*/ +// This version returns the winding as "on" if all verts lie in the plane +void ClassifyWindingEpsilon( winding_t *in, const Vector &normal, vec_t dist, + vec_t epsilon, winding_t **front, winding_t **back, winding_t **on) +{ + vec_t dists[MAX_POINTS_ON_WINDING+4]; + int sides[MAX_POINTS_ON_WINDING+4]; + int counts[3]; + vec_t dot; + int i, j; + Vector mid = vec3_origin; + winding_t *f, *b; + int maxpts; + + counts[0] = counts[1] = counts[2] = 0; + +// determine sides for each point + for (i=0 ; inumpoints ; i++) + { + dot = DotProduct (in->p[i], normal); + dot -= dist; + dists[i] = dot; + if (dot > epsilon) + sides[i] = SIDE_FRONT; + else if (dot < -epsilon) + sides[i] = SIDE_BACK; + else + { + sides[i] = SIDE_ON; + } + counts[sides[i]]++; + } + sides[i] = sides[0]; + dists[i] = dists[0]; + + *front = *back = *on = NULL; + + if ( !counts[0] && !counts[1] ) + { + *on = CopyWinding(in); + return; + } + + if (!counts[0]) + { + *back = CopyWinding(in); + return; + } + if (!counts[1]) + { + *front = CopyWinding(in); + return; + } + + maxpts = in->numpoints+4; // cant use counts[0]+2 because + // of fp grouping errors + + *front = f = AllocWinding (maxpts); + *back = b = AllocWinding (maxpts); + + for (i=0 ; inumpoints ; i++) + { + Vector& p1 = in->p[i]; + + if (sides[i] == SIDE_ON) + { + VectorCopy (p1, f->p[f->numpoints]); + f->numpoints++; + VectorCopy (p1, b->p[b->numpoints]); + b->numpoints++; + continue; + } + + if (sides[i] == SIDE_FRONT) + { + VectorCopy (p1, f->p[f->numpoints]); + f->numpoints++; + } + if (sides[i] == SIDE_BACK) + { + VectorCopy (p1, b->p[b->numpoints]); + b->numpoints++; + } + + if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i]) + continue; + + // generate a split point + Vector& p2 = in->p[(i+1)%in->numpoints]; + + dot = dists[i] / (dists[i]-dists[i+1]); + for (j=0 ; j<3 ; j++) + { // avoid round off error when possible + if (normal[j] == 1) + mid[j] = dist; + else if (normal[j] == -1) + mid[j] = -dist; + else + mid[j] = p1[j] + dot*(p2[j]-p1[j]); + } + + VectorCopy (mid, f->p[f->numpoints]); + f->numpoints++; + VectorCopy (mid, b->p[b->numpoints]); + b->numpoints++; + } + + if (f->numpoints > maxpts || b->numpoints > maxpts) + Error ("ClipWinding: points exceeded estimate"); + if (f->numpoints > MAX_POINTS_ON_WINDING || b->numpoints > MAX_POINTS_ON_WINDING) + Error ("ClipWinding: MAX_POINTS_ON_WINDING"); +} + +/* +============= +ChopWindingInPlace +============= +*/ +void ChopWindingInPlace (winding_t **inout, const Vector &normal, vec_t dist, vec_t epsilon) +{ + winding_t *in; + vec_t dists[MAX_POINTS_ON_WINDING+4]; + int sides[MAX_POINTS_ON_WINDING+4]; + int counts[3]; + vec_t dot; + int i, j; + Vector mid = vec3_origin; + winding_t *f; + int maxpts; + + in = *inout; + counts[0] = counts[1] = counts[2] = 0; +// determine sides for each point + for (i=0 ; inumpoints ; i++) + { + dot = DotProduct (in->p[i], normal); + dot -= dist; + dists[i] = dot; + if (dot > epsilon) + { + sides[i] = SIDE_FRONT; + } + else if (dot < -epsilon) + { + sides[i] = SIDE_BACK; + } + else + { + sides[i] = SIDE_ON; + } + counts[sides[i]]++; + } + sides[i] = sides[0]; + dists[i] = dists[0]; + + if (!counts[0]) + { + FreeWinding (in); + *inout = NULL; + return; + } + if (!counts[1]) + return; // inout stays the same + + maxpts = in->numpoints+4; // cant use counts[0]+2 because + // of fp grouping errors + + f = AllocWinding (maxpts); + + for (i=0 ; inumpoints ; i++) + { + Vector& p1 = in->p[i]; + + if (sides[i] == SIDE_ON) + { + VectorCopy (p1, f->p[f->numpoints]); + f->numpoints++; + continue; + } + + if (sides[i] == SIDE_FRONT) + { + VectorCopy (p1, f->p[f->numpoints]); + f->numpoints++; + } + + if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i]) + continue; + + // generate a split point + Vector& p2 = in->p[(i+1)%in->numpoints]; + + dot = dists[i] / (dists[i]-dists[i+1]); + for (j=0 ; j<3 ; j++) + { // avoid round off error when possible + if (normal[j] == 1) + mid[j] = dist; + else if (normal[j] == -1) + mid[j] = -dist; + else + mid[j] = p1[j] + dot*(p2[j]-p1[j]); + } + + VectorCopy (mid, f->p[f->numpoints]); + f->numpoints++; + } + + if (f->numpoints > maxpts) + Error ("ClipWinding: points exceeded estimate"); + if (f->numpoints > MAX_POINTS_ON_WINDING) + Error ("ClipWinding: MAX_POINTS_ON_WINDING"); + + FreeWinding (in); + *inout = f; +} + + +/* +================= +ChopWinding + +Returns the fragment of in that is on the front side +of the cliping plane. The original is freed. +================= +*/ +winding_t *ChopWinding (winding_t *in, const Vector &normal, vec_t dist) +{ + winding_t *f, *b; + + ClipWindingEpsilon (in, normal, dist, ON_EPSILON, &f, &b); + FreeWinding (in); + if (b) + FreeWinding (b); + return f; +} + + +/* +================= +CheckWinding + +================= +*/ +void CheckWinding (winding_t *w) +{ + int i, j; + vec_t d, edgedist; + Vector dir, edgenormal, facenormal; + vec_t area; + vec_t facedist; + + if (w->numpoints < 3) + Error ("CheckWinding: %i points",w->numpoints); + + area = WindingArea(w); + if (area < 1) + Error ("CheckWinding: %f area", area); + + WindingPlane (w, facenormal, &facedist); + + for (i=0 ; inumpoints ; i++) + { + Vector& p1 = w->p[i]; + + for (j=0 ; j<3 ; j++) + { + if (p1[j] > MAX_COORD_INTEGER || p1[j] < MIN_COORD_INTEGER) + Error ("CheckFace: out of range: %f",p1[j]); + } + + j = i+1 == w->numpoints ? 0 : i+1; + + // check the point is on the face plane + d = DotProduct (p1, facenormal) - facedist; + if (d < -ON_EPSILON || d > ON_EPSILON) + Error ("CheckWinding: point off plane"); + + // check the edge isnt degenerate + Vector& p2 = w->p[j]; + VectorSubtract (p2, p1, dir); + + if (VectorLength (dir) < ON_EPSILON) + Error ("CheckWinding: degenerate edge"); + + CrossProduct (facenormal, dir, edgenormal); + VectorNormalize (edgenormal); + edgedist = DotProduct (p1, edgenormal); + edgedist += ON_EPSILON; + + // all other points must be on front side + for (j=0 ; jnumpoints ; j++) + { + if (j == i) + continue; + d = DotProduct (w->p[j], edgenormal); + if (d > edgedist) + Error ("CheckWinding: non-convex"); + } + } +} + + +/* +============ +WindingOnPlaneSide +============ +*/ +int WindingOnPlaneSide (winding_t *w, const Vector &normal, vec_t dist) +{ + qboolean front, back; + int i; + vec_t d; + + front = false; + back = false; + for (i=0 ; inumpoints ; i++) + { + d = DotProduct (w->p[i], normal) - dist; + if (d < -ON_EPSILON) + { + if (front) + return SIDE_CROSS; + back = true; + continue; + } + if (d > ON_EPSILON) + { + if (back) + return SIDE_CROSS; + front = true; + continue; + } + } + + if (back) + return SIDE_BACK; + if (front) + return SIDE_FRONT; + return SIDE_ON; +} + + +//----------------------------------------------------------------------------- +// Purpose: 2d point inside of winding test (assumes the point resides in the +// winding plane) +//----------------------------------------------------------------------------- +bool PointInWinding( const Vector &pt, winding_t *pWinding ) +{ + if( !pWinding ) + return false; + +#if 0 + // + // NOTE: this will be a quicker way to calculate this, however I don't + // know the trick off hand (post dot product tests??) + // TODO: look in graphics gems!!!! (cab) + // + + Vector edge1, edge2; + for( int ndxPt = 0; ndxPt < pWinding->numpoints; ndxPt++ ) + { + edge1 = pWinding->p[ndxPt] - pt; + edge2 = pWinding->p[(ndxPt+1)%pWinding->numpoints] - pt; + + VectorNormalize( edge1 ); + VectorNormalize( edge2 ); + + if( edge2.Dot( edge1 ) < 0.0f ) + return false; + } + + return true; + +#else + Vector edge, toPt, cross, testCross; + + // + // get the first normal to test + // + toPt = pt - pWinding->p[0]; + edge = pWinding->p[1] - pWinding->p[0]; + testCross = edge.Cross( toPt ); + VectorNormalize( testCross ); + + for( int ndxPt = 1; ndxPt < pWinding->numpoints; ndxPt++ ) + { + toPt = pt - pWinding->p[ndxPt]; + edge = pWinding->p[(ndxPt+1)%pWinding->numpoints] - pWinding->p[ndxPt]; + cross = edge.Cross( toPt ); + VectorNormalize( cross ); + + if( cross.Dot( testCross ) < 0.0f ) + return false; + } + + return true; +#endif +} + +void TranslateWinding( winding_t *pWinding, const Vector &offset ) +{ + for ( int i = 0; i < pWinding->numpoints; i++ ) + { + pWinding->p[i] += offset; + } +} diff --git a/mp/src/utils/common/polylib.h b/mp/src/utils/common/polylib.h index 1750a82a..8753dccc 100644 --- a/mp/src/utils/common/polylib.h +++ b/mp/src/utils/common/polylib.h @@ -1,78 +1,78 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// - -#ifndef POLYLIB_H -#define POLYLIB_H -#pragma once - -#ifndef MATHLIB_H -#include "mathlib/mathlib.h" -#endif - -struct winding_t -{ - int numpoints; - Vector *p; // variable sized - int maxpoints; - winding_t *next; -}; - -#define MAX_POINTS_ON_WINDING 64 - -// you can define on_epsilon in the makefile as tighter -// point on plane side epsilon -// todo: need a world-space epsilon, a lightmap-space epsilon, and a texture space epsilon -// or at least convert from a world-space epsilon to lightmap and texture space epsilons -#ifndef ON_EPSILON -#define ON_EPSILON 0.1 -#endif - - -winding_t *AllocWinding (int points); -vec_t WindingArea (winding_t *w); -void WindingCenter (winding_t *w, Vector ¢er); -vec_t WindingAreaAndBalancePoint( winding_t *w, Vector ¢er ); -void ClipWindingEpsilon (winding_t *in, const Vector &normal, vec_t dist, - vec_t epsilon, winding_t **front, winding_t **back); - -// translates everything by offset, then does the clip, then translates back (to keep precision) -void ClipWindingEpsilon_Offset( winding_t *in, const Vector &normal, vec_t dist, vec_t epsilon, winding_t **front, winding_t **back, const Vector &offset ); - -void ClassifyWindingEpsilon( winding_t *in, const Vector &normal, vec_t dist, - vec_t epsilon, winding_t **front, winding_t **back, winding_t **on); -void ClassifyWindingEpsilon_Offset( winding_t *in, const Vector &normal, vec_t dist, - vec_t epsilon, winding_t **front, winding_t **back, winding_t **on, const Vector &offset); - -winding_t *ChopWinding (winding_t *in, const Vector &normal, vec_t dist); -winding_t *CopyWinding (winding_t *w); -winding_t *ReverseWinding (winding_t *w); -winding_t *BaseWindingForPlane (const Vector &normal, vec_t dist); -void CheckWinding (winding_t *w); -void WindingPlane (winding_t *w, Vector &normal, vec_t *dist); -void RemoveColinearPoints (winding_t *w); -int WindingOnPlaneSide (winding_t *w, const Vector &normal, vec_t dist); -void FreeWinding (winding_t *w); -void WindingBounds (winding_t *w, Vector &mins, Vector &maxs); - -void ChopWindingInPlace (winding_t **w, const Vector &normal, vec_t dist, vec_t epsilon); -// frees the original if clipped - -bool PointInWinding( Vector const &pt, winding_t *pWinding ); - -// translates a winding by offset -void TranslateWinding( winding_t *pWinding, const Vector &offset ); - -void pw(winding_t *w); - - -#endif // POLYLIB_H +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef POLYLIB_H +#define POLYLIB_H +#pragma once + +#ifndef MATHLIB_H +#include "mathlib/mathlib.h" +#endif + +struct winding_t +{ + int numpoints; + Vector *p; // variable sized + int maxpoints; + winding_t *next; +}; + +#define MAX_POINTS_ON_WINDING 64 + +// you can define on_epsilon in the makefile as tighter +// point on plane side epsilon +// todo: need a world-space epsilon, a lightmap-space epsilon, and a texture space epsilon +// or at least convert from a world-space epsilon to lightmap and texture space epsilons +#ifndef ON_EPSILON +#define ON_EPSILON 0.1 +#endif + + +winding_t *AllocWinding (int points); +vec_t WindingArea (winding_t *w); +void WindingCenter (winding_t *w, Vector ¢er); +vec_t WindingAreaAndBalancePoint( winding_t *w, Vector ¢er ); +void ClipWindingEpsilon (winding_t *in, const Vector &normal, vec_t dist, + vec_t epsilon, winding_t **front, winding_t **back); + +// translates everything by offset, then does the clip, then translates back (to keep precision) +void ClipWindingEpsilon_Offset( winding_t *in, const Vector &normal, vec_t dist, vec_t epsilon, winding_t **front, winding_t **back, const Vector &offset ); + +void ClassifyWindingEpsilon( winding_t *in, const Vector &normal, vec_t dist, + vec_t epsilon, winding_t **front, winding_t **back, winding_t **on); +void ClassifyWindingEpsilon_Offset( winding_t *in, const Vector &normal, vec_t dist, + vec_t epsilon, winding_t **front, winding_t **back, winding_t **on, const Vector &offset); + +winding_t *ChopWinding (winding_t *in, const Vector &normal, vec_t dist); +winding_t *CopyWinding (winding_t *w); +winding_t *ReverseWinding (winding_t *w); +winding_t *BaseWindingForPlane (const Vector &normal, vec_t dist); +void CheckWinding (winding_t *w); +void WindingPlane (winding_t *w, Vector &normal, vec_t *dist); +void RemoveColinearPoints (winding_t *w); +int WindingOnPlaneSide (winding_t *w, const Vector &normal, vec_t dist); +void FreeWinding (winding_t *w); +void WindingBounds (winding_t *w, Vector &mins, Vector &maxs); + +void ChopWindingInPlace (winding_t **w, const Vector &normal, vec_t dist, vec_t epsilon); +// frees the original if clipped + +bool PointInWinding( Vector const &pt, winding_t *pWinding ); + +// translates a winding by offset +void TranslateWinding( winding_t *pWinding, const Vector &offset ); + +void pw(winding_t *w); + + +#endif // POLYLIB_H diff --git a/mp/src/utils/common/qfiles.h b/mp/src/utils/common/qfiles.h index b1d2232f..fc122c33 100644 --- a/mp/src/utils/common/qfiles.h +++ b/mp/src/utils/common/qfiles.h @@ -1,42 +1,42 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// - -#ifndef QFILES_H -#define QFILES_H -#pragma once - - -// -// qfiles.h: quake file formats -// This file must be identical in the quake and utils directories -// - -#include "basetypes.h" -#include "commonmacros.h" -#include "worldsize.h" -#include "bspfile.h" - -#define MAX_OSPATH 260 -#define MAX_QPATH 64 - -/* -======================================================================== - -The .pak files are just a linear collapse of a directory tree - -======================================================================== -*/ - -#define IDPAKHEADER (('K'<<24)+('C'<<16)+('A'<<8)+'P') - -#endif // QFILES_H +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef QFILES_H +#define QFILES_H +#pragma once + + +// +// qfiles.h: quake file formats +// This file must be identical in the quake and utils directories +// + +#include "basetypes.h" +#include "commonmacros.h" +#include "worldsize.h" +#include "bspfile.h" + +#define MAX_OSPATH 260 +#define MAX_QPATH 64 + +/* +======================================================================== + +The .pak files are just a linear collapse of a directory tree + +======================================================================== +*/ + +#define IDPAKHEADER (('K'<<24)+('C'<<16)+('A'<<8)+'P') + +#endif // QFILES_H diff --git a/mp/src/utils/common/scratchpad_helpers.cpp b/mp/src/utils/common/scratchpad_helpers.cpp index 9a3c2d74..ecab731b 100644 --- a/mp/src/utils/common/scratchpad_helpers.cpp +++ b/mp/src/utils/common/scratchpad_helpers.cpp @@ -1,103 +1,103 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// - -#include "scratchpad_helpers.h" -#include "bspfile.h" -#include "bsplib.h" - - -void ScratchPad_DrawWinding( - IScratchPad3D *pPad, - int nPoints, - Vector *pPoints, - Vector vColor, - Vector vOffset ) -{ - for ( int i=0; i < nPoints; i++ ) - { - pPad->DrawLine( CSPVert( pPoints[i]+vOffset, vColor ), CSPVert( pPoints[(i+1)%nPoints]+vOffset, vColor ) ); - } -} - - -void ScratchPad_DrawFace( IScratchPad3D *pPad, dface_t *f, int iFaceNumber, const CSPColor &faceColor, const Vector &vOffset ) -{ - // Draw the face's outline, then put text for its face index on it too. - CUtlVector points; - for ( int iEdge = 0; iEdge < f->numedges; iEdge++ ) - { - int v; - int se = dsurfedges[f->firstedge + iEdge]; - if ( se < 0 ) - v = dedges[-se].v[1]; - else - v = dedges[se].v[0]; - - dvertex_t *dv = &dvertexes[v]; - points.AddToTail( dv->point ); - } - - // Draw the outline. - Vector vCenter( 0, 0, 0 ); - for ( int iEdge=0; iEdge < points.Count(); iEdge++ ) - { - pPad->DrawLine( CSPVert( points[iEdge]+vOffset, faceColor ), CSPVert( points[(iEdge+1)%points.Count()]+vOffset, faceColor ) ); - vCenter += points[iEdge]; - } - vCenter /= points.Count(); - vCenter += vOffset; - - // Draw the text. - if ( iFaceNumber != -1 ) - { - char str[64]; - Q_snprintf( str, sizeof( str ), "%d", iFaceNumber ); - - CTextParams params; - - params.m_bCentered = true; - params.m_bOutline = true; - params.m_flLetterWidth = 2; - params.m_vColor.Init( 1, 0, 0 ); - - VectorAngles( dplanes[f->planenum].normal, params.m_vAngles ); - params.m_bTwoSided = true; - - params.m_vPos = vCenter; - - pPad->DrawText( str, params ); - } -} - - -void ScratchPad_DrawWorld( IScratchPad3D *pPad, bool bDrawFaceNumbers, const CSPColor &faceColor ) -{ - bool bAutoFlush = pPad->GetAutoFlush(); - pPad->SetAutoFlush( false ); - - for ( int i=0; i < numleafs; i++ ) - { - dleaf_t *l = &dleafs[i]; - if ( l->contents & CONTENTS_DETAIL ) - continue; - - for ( int z=0; z < l->numleaffaces; z++ ) - { - int iFace = dleaffaces[l->firstleafface+z]; - dface_t *f = &dfaces[iFace]; - ScratchPad_DrawFace( pPad, f, bDrawFaceNumbers ? i : -1 ); - } - } - - pPad->SetAutoFlush( bAutoFlush ); -} - - -void ScratchPad_DrawWorld( bool bDrawFaceNumbers, const CSPColor &faceColor ) -{ - IScratchPad3D *pPad = ScratchPad3D_Create(); - ScratchPad_DrawWorld( pPad, bDrawFaceNumbers ); -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "scratchpad_helpers.h" +#include "bspfile.h" +#include "bsplib.h" + + +void ScratchPad_DrawWinding( + IScratchPad3D *pPad, + int nPoints, + Vector *pPoints, + Vector vColor, + Vector vOffset ) +{ + for ( int i=0; i < nPoints; i++ ) + { + pPad->DrawLine( CSPVert( pPoints[i]+vOffset, vColor ), CSPVert( pPoints[(i+1)%nPoints]+vOffset, vColor ) ); + } +} + + +void ScratchPad_DrawFace( IScratchPad3D *pPad, dface_t *f, int iFaceNumber, const CSPColor &faceColor, const Vector &vOffset ) +{ + // Draw the face's outline, then put text for its face index on it too. + CUtlVector points; + for ( int iEdge = 0; iEdge < f->numedges; iEdge++ ) + { + int v; + int se = dsurfedges[f->firstedge + iEdge]; + if ( se < 0 ) + v = dedges[-se].v[1]; + else + v = dedges[se].v[0]; + + dvertex_t *dv = &dvertexes[v]; + points.AddToTail( dv->point ); + } + + // Draw the outline. + Vector vCenter( 0, 0, 0 ); + for ( int iEdge=0; iEdge < points.Count(); iEdge++ ) + { + pPad->DrawLine( CSPVert( points[iEdge]+vOffset, faceColor ), CSPVert( points[(iEdge+1)%points.Count()]+vOffset, faceColor ) ); + vCenter += points[iEdge]; + } + vCenter /= points.Count(); + vCenter += vOffset; + + // Draw the text. + if ( iFaceNumber != -1 ) + { + char str[64]; + Q_snprintf( str, sizeof( str ), "%d", iFaceNumber ); + + CTextParams params; + + params.m_bCentered = true; + params.m_bOutline = true; + params.m_flLetterWidth = 2; + params.m_vColor.Init( 1, 0, 0 ); + + VectorAngles( dplanes[f->planenum].normal, params.m_vAngles ); + params.m_bTwoSided = true; + + params.m_vPos = vCenter; + + pPad->DrawText( str, params ); + } +} + + +void ScratchPad_DrawWorld( IScratchPad3D *pPad, bool bDrawFaceNumbers, const CSPColor &faceColor ) +{ + bool bAutoFlush = pPad->GetAutoFlush(); + pPad->SetAutoFlush( false ); + + for ( int i=0; i < numleafs; i++ ) + { + dleaf_t *l = &dleafs[i]; + if ( l->contents & CONTENTS_DETAIL ) + continue; + + for ( int z=0; z < l->numleaffaces; z++ ) + { + int iFace = dleaffaces[l->firstleafface+z]; + dface_t *f = &dfaces[iFace]; + ScratchPad_DrawFace( pPad, f, bDrawFaceNumbers ? i : -1 ); + } + } + + pPad->SetAutoFlush( bAutoFlush ); +} + + +void ScratchPad_DrawWorld( bool bDrawFaceNumbers, const CSPColor &faceColor ) +{ + IScratchPad3D *pPad = ScratchPad3D_Create(); + ScratchPad_DrawWorld( pPad, bDrawFaceNumbers ); +} diff --git a/mp/src/utils/common/scratchpad_helpers.h b/mp/src/utils/common/scratchpad_helpers.h index 8f409fca..29e5a122 100644 --- a/mp/src/utils/common/scratchpad_helpers.h +++ b/mp/src/utils/common/scratchpad_helpers.h @@ -1,25 +1,25 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// - -#ifndef SCRATCHPAD_HELPERS_H -#define SCRATCHPAD_HELPERS_H -#ifdef _WIN32 -#pragma once -#endif - - -#include "iscratchpad3d.h" -#include "bspfile.h" - - -void ScratchPad_DrawWinding( IScratchPad3D *pPad, int nPoints, Vector *pPoints, Vector vColor, Vector vOffset = Vector(0,0,0) ); - -void ScratchPad_DrawFace( IScratchPad3D *pPad, dface_t *f, int iFaceNumber = -1, const CSPColor &faceColor=CSPColor(1,1,1,1), const Vector &vOffset=Vector(0,0,0) ); -void ScratchPad_DrawWorld( IScratchPad3D *pPad, bool bDrawFaceNumbers, const CSPColor &faceColor=CSPColor(1,1,1,1) ); -void ScratchPad_DrawWorld( bool bDrawFaceNumbers, const CSPColor &faceColor=CSPColor(1,1,1,1) ); - - -#endif // SCRATCHPAD_HELPERS_H +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef SCRATCHPAD_HELPERS_H +#define SCRATCHPAD_HELPERS_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "iscratchpad3d.h" +#include "bspfile.h" + + +void ScratchPad_DrawWinding( IScratchPad3D *pPad, int nPoints, Vector *pPoints, Vector vColor, Vector vOffset = Vector(0,0,0) ); + +void ScratchPad_DrawFace( IScratchPad3D *pPad, dface_t *f, int iFaceNumber = -1, const CSPColor &faceColor=CSPColor(1,1,1,1), const Vector &vOffset=Vector(0,0,0) ); +void ScratchPad_DrawWorld( IScratchPad3D *pPad, bool bDrawFaceNumbers, const CSPColor &faceColor=CSPColor(1,1,1,1) ); +void ScratchPad_DrawWorld( bool bDrawFaceNumbers, const CSPColor &faceColor=CSPColor(1,1,1,1) ); + + +#endif // SCRATCHPAD_HELPERS_H diff --git a/mp/src/utils/common/scriplib.cpp b/mp/src/utils/common/scriplib.cpp index 469c7885..1c8b47f8 100644 --- a/mp/src/utils/common/scriplib.cpp +++ b/mp/src/utils/common/scriplib.cpp @@ -1,1349 +1,1349 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -// -//===========================================================================// - -// scriplib.c - -#include "tier1/strtools.h" -#include "tier2/tier2.h" -#include "cmdlib.h" -#include "scriplib.h" -#if defined( _X360 ) -#include "xbox\xbox_win32stubs.h" -#endif -#if defined(POSIX) -#include "../../filesystem/linux_support.h" -#include -#endif -/* -============================================================================= - - PARSING STUFF - -============================================================================= -*/ - -typedef struct -{ - char filename[1024]; - char *buffer,*script_p,*end_p; - int line; - - char macrobuffer[4096]; - char *macroparam[64]; - char *macrovalue[64]; - int nummacroparams; - -} script_t; - -#define MAX_INCLUDES 64 -script_t scriptstack[MAX_INCLUDES]; -script_t *script = NULL; -int scriptline; - -char token[MAXTOKEN]; -qboolean endofscript; -qboolean tokenready; // only true if UnGetToken was just called - -typedef struct -{ - char *param; - char *value; -} variable_t; - -CUtlVector g_definevariable; - -/* -Callback stuff -*/ - -void DefaultScriptLoadedCallback( char const *pFilenameLoaded, char const *pIncludedFromFileName, int nIncludeLineNumber ) -{ - NULL; -} - -SCRIPT_LOADED_CALLBACK g_pfnCallback = DefaultScriptLoadedCallback; - -SCRIPT_LOADED_CALLBACK SetScriptLoadedCallback( SCRIPT_LOADED_CALLBACK pfnNewScriptLoadedCallback ) -{ - SCRIPT_LOADED_CALLBACK pfnCallback = g_pfnCallback; - g_pfnCallback = pfnNewScriptLoadedCallback; - return pfnCallback; -} - -/* -============== -AddScriptToStack -============== -*/ -void AddScriptToStack (char *filename, ScriptPathMode_t pathMode = SCRIPT_USE_ABSOLUTE_PATH) -{ - int size; - - script++; - if (script == &scriptstack[MAX_INCLUDES]) - Error ("script file exceeded MAX_INCLUDES"); - - if ( pathMode == SCRIPT_USE_RELATIVE_PATH ) - Q_strncpy( script->filename, filename, sizeof( script->filename ) ); - else - Q_strncpy (script->filename, ExpandPath (filename), sizeof( script->filename ) ); - - size = LoadFile (script->filename, (void **)&script->buffer); - - // printf ("entering %s\n", script->filename); - if ( g_pfnCallback ) - { - if ( script == scriptstack + 1 ) - g_pfnCallback( script->filename, NULL, 0 ); - else - g_pfnCallback( script->filename, script[-1].filename, script[-1].line ); - } - - script->line = 1; - - script->script_p = script->buffer; - script->end_p = script->buffer + size; -} - - -/* -============== -LoadScriptFile -============== -*/ -void LoadScriptFile (char *filename, ScriptPathMode_t pathMode) -{ - script = scriptstack; - AddScriptToStack (filename, pathMode); - - endofscript = false; - tokenready = false; -} - - -/* -============== -============== -*/ - -script_t *macrolist[256]; -int nummacros; - -void DefineMacro( char *macroname ) -{ - script_t *pmacro = (script_t *)malloc( sizeof( script_t ) ); - - strcpy( pmacro->filename, macroname ); - pmacro->line = script->line; - pmacro->nummacroparams = 0; - - char *mp = pmacro->macrobuffer; - char *cp = script->script_p; - - while (TokenAvailable( )) - { - GetToken( false ); - - if (token[0] == '\\' && token[1] == '\\') - { - break; - } - cp = script->script_p; - - pmacro->macroparam[pmacro->nummacroparams++] = mp; - - strcpy( mp, token ); - mp += strlen( token ) + 1; - - if (mp >= pmacro->macrobuffer + sizeof( pmacro->macrobuffer )) - Error("Macro buffer overflow\n"); - } - // roll back script_p to previous valid location - script->script_p = cp; - - // find end of macro def - while (*cp && *cp != '\n') - { - //Msg("%d ", *cp ); - if (*cp == '\\' && *(cp+1) == '\\') - { - // skip till end of line - while (*cp && *cp != '\n') - { - *cp = ' '; // replace with spaces - cp++; - } - - if (*cp) - { - cp++; - } - } - else - { - cp++; - } - } - - int size = (cp - script->script_p); - - pmacro->buffer = (char *)malloc( size + 1); - memcpy( pmacro->buffer, script->script_p, size ); - pmacro->buffer[size] = '\0'; - pmacro->end_p = &pmacro->buffer[size]; - - macrolist[nummacros++] = pmacro; - - script->script_p = cp; -} - - -void DefineVariable( char *variablename ) -{ - variable_t v; - - v.param = strdup( variablename ); - - GetToken( false ); - - v.value = strdup( token ); - - g_definevariable.AddToTail( v ); -} - - - -/* -============== -============== -*/ -bool AddMacroToStack( char *macroname ) -{ - // lookup macro - if (macroname[0] != '$') - return false; - - int i; - for (i = 0; i < nummacros; i++) - { - if (strcmpi( macrolist[i]->filename, ¯oname[1] ) == 0) - { - break; - } - } - if (i == nummacros) - return false; - - script_t *pmacro = macrolist[i]; - - // get tokens - script_t *pnext = script + 1; - - pnext++; - if (pnext == &scriptstack[MAX_INCLUDES]) - Error ("script file exceeded MAX_INCLUDES"); - - // get tokens - char *cp = pnext->macrobuffer; - - pnext->nummacroparams = pmacro->nummacroparams; - - for (i = 0; i < pnext->nummacroparams; i++) - { - GetToken(false); - - strcpy( cp, token ); - pnext->macroparam[i] = pmacro->macroparam[i]; - pnext->macrovalue[i] = cp; - - cp += strlen( token ) + 1; - - if (cp >= pnext->macrobuffer + sizeof( pnext->macrobuffer )) - Error("Macro buffer overflow\n"); - } - - script = pnext; - strcpy( script->filename, pmacro->filename ); - - int size = pmacro->end_p - pmacro->buffer; - script->buffer = (char *)malloc( size + 1 ); - memcpy( script->buffer, pmacro->buffer, size ); - pmacro->buffer[size] = '\0'; - script->script_p = script->buffer; - script->end_p = script->buffer + size; - script->line = pmacro->line; - - return true; -} - - - -bool ExpandMacroToken( char *&token_p ) -{ - if ( script->nummacroparams && *script->script_p == '$' ) - { - char *cp = script->script_p + 1; - - while ( *cp > 32 && *cp != '$' ) - { - cp++; - } - - // found a word with $'s on either end? - if (*cp != '$') - return false; - - // get token pointer - char *tp = script->script_p + 1; - int len = (cp - tp); - *(tp + len) = '\0'; - - // lookup macro parameter - int index = 0; - for (index = 0; index < script->nummacroparams; index++) - { - if (stricmp( script->macroparam[index], tp ) == 0) - break; - } - if (index >= script->nummacroparams) - { - Error("unknown macro token \"%s\" in %s\n", tp, script->filename ); - } - - // paste token into - len = strlen( script->macrovalue[index] ); - strcpy( token_p, script->macrovalue[index] ); - token_p += len; - - script->script_p = cp + 1; - - if (script->script_p >= script->end_p) - Error ("Macro expand overflow\n"); - - if (token_p >= &token[MAXTOKEN]) - Error ("Token too large on line %i\n",scriptline); - - return true; - } - return false; -} - - - -/* -============== -============== -*/ -// FIXME: this should create a new script context so the individual tokens in the variable can be parsed -bool ExpandVariableToken( char *&token_p ) -{ - if ( *script->script_p == '$' ) - { - char *cp = script->script_p + 1; - - while ( *cp > 32 && *cp != '$' ) - { - cp++; - } - - // found a word with $'s on either end? - if (*cp != '$') - return false; - - // get token pointer - char *tp = script->script_p + 1; - int len = (cp - tp); - *(tp + len) = '\0'; - - // lookup macro parameter - - int index; - for (index = 0; index < g_definevariable.Count(); index++) - { - if (Q_strnicmp( g_definevariable[index].param, tp, len ) == 0) - break; - } - - if (index >= g_definevariable.Count() ) - { - Error("unknown variable token \"%s\" in %s\n", tp, script->filename ); - } - - // paste token into - len = strlen( g_definevariable[index].value ); - strcpy( token_p, g_definevariable[index].value ); - token_p += len; - - script->script_p = cp + 1; - - if (script->script_p >= script->end_p) - Error ("Macro expand overflow\n"); - - if (token_p >= &token[MAXTOKEN]) - Error ("Token too large on line %i\n",scriptline); - - return true; - } - return false; -} - - - -/* -============== -ParseFromMemory -============== -*/ -void ParseFromMemory (char *buffer, int size) -{ - script = scriptstack; - script++; - if (script == &scriptstack[MAX_INCLUDES]) - Error ("script file exceeded MAX_INCLUDES"); - strcpy (script->filename, "memory buffer" ); - - script->buffer = buffer; - script->line = 1; - script->script_p = script->buffer; - script->end_p = script->buffer + size; - - endofscript = false; - tokenready = false; -} - - -//----------------------------------------------------------------------------- -// Used instead of ParseFromMemory to temporarily add a memory buffer -// to the script stack. ParseFromMemory just blows away the stack. -//----------------------------------------------------------------------------- -void PushMemoryScript( char *pszBuffer, const int nSize ) -{ - if ( script == NULL ) - { - script = scriptstack; - } - script++; - if ( script == &scriptstack[MAX_INCLUDES] ) - { - Error ( "script file exceeded MAX_INCLUDES" ); - } - strcpy (script->filename, "memory buffer" ); - - script->buffer = pszBuffer; - script->line = 1; - script->script_p = script->buffer; - script->end_p = script->buffer + nSize; - - endofscript = false; - tokenready = false; -} - - -//----------------------------------------------------------------------------- -// Used after calling PushMemoryScript to clean up the memory buffer -// added to the script stack. The normal end of script terminates -// all parsing at the end of a memory buffer even if there are more scripts -// remaining on the script stack -//----------------------------------------------------------------------------- -bool PopMemoryScript() -{ - if ( V_stricmp( script->filename, "memory buffer" ) ) - return false; - - if ( script == scriptstack ) - { - endofscript = true; - return false; - } - script--; - scriptline = script->line; - - endofscript = false; - - return true; -} - - -/* -============== -UnGetToken - -Signals that the current token was not used, and should be reported -for the next GetToken. Note that - -GetToken (true); -UnGetToken (); -GetToken (false); - -could cross a line boundary. -============== -*/ -void UnGetToken (void) -{ - tokenready = true; -} - - -qboolean EndOfScript (qboolean crossline) -{ - if (!crossline) - Error ("Line %i is incomplete\n",scriptline); - - if (!strcmp (script->filename, "memory buffer")) - { - endofscript = true; - return false; - } - - free (script->buffer); - script->buffer = NULL; - if (script == scriptstack+1) - { - endofscript = true; - return false; - } - script--; - scriptline = script->line; - // printf ("returning to %s\n", script->filename); - return GetToken (crossline); -} - - -//----------------------------------------------------------------------------- -// Purpose: Given an absolute path, do a find first find next on it and build -// a list of files. Physical file system only -//----------------------------------------------------------------------------- -static void FindFileAbsoluteList( CUtlVector< CUtlString > &outAbsolutePathNames, const char *pszFindName ) -{ - char szPath[MAX_PATH]; - V_strncpy( szPath, pszFindName, sizeof( szPath ) ); - V_StripFilename( szPath ); - - char szResult[MAX_PATH]; - FileFindHandle_t hFile = FILESYSTEM_INVALID_FIND_HANDLE; - - for ( const char *pszFoundFile = g_pFullFileSystem->FindFirst( pszFindName, &hFile ); pszFoundFile && hFile != FILESYSTEM_INVALID_FIND_HANDLE; pszFoundFile = g_pFullFileSystem->FindNext( hFile ) ) - { - V_ComposeFileName( szPath, pszFoundFile, szResult, sizeof( szResult ) ); - outAbsolutePathNames.AddToTail( szResult ); - } - - g_pFullFileSystem->FindClose( hFile ); -} - - -//----------------------------------------------------------------------------- -// Data for checking for single character tokens while parsing -//----------------------------------------------------------------------------- -bool g_bCheckSingleCharTokens = false; -CUtlString g_sSingleCharTokens; - - -//----------------------------------------------------------------------------- -// Sets whether the scriplib parser will do a special check for single -// character tokens. Returns previous state of whether single character -// tokens will be checked. -//----------------------------------------------------------------------------- -bool SetCheckSingleCharTokens( bool bCheck ) -{ - const bool bRetVal = g_bCheckSingleCharTokens; - - g_bCheckSingleCharTokens = bCheck; - - return bRetVal; -} - - -//----------------------------------------------------------------------------- -// Sets the list of single character tokens to check if SetCheckSingleCharTokens -// is turned on. -//----------------------------------------------------------------------------- -CUtlString SetSingleCharTokenList( const char *pszSingleCharTokenList ) -{ - const CUtlString sRetVal = g_sSingleCharTokens; - - if ( pszSingleCharTokenList ) - { - g_sSingleCharTokens = pszSingleCharTokenList; - } - - return sRetVal; -} - - -/* -============== -GetToken -============== -*/ -qboolean GetToken (qboolean crossline) -{ - char *token_p; - - if (tokenready) // is a token allready waiting? - { - tokenready = false; - return true; - } - - // printf("script_p %x (%x)\n", script->script_p, script->end_p ); fflush( stdout ); - - if (script->script_p >= script->end_p) - { - return EndOfScript (crossline); - } - - tokenready = false; - - // skip space, ctrl chars -skipspace: - while (*script->script_p <= 32) - { - if (script->script_p >= script->end_p) - { - return EndOfScript (crossline); - } - if (*(script->script_p++) == '\n') - { - if (!crossline) - { - Error ("Line %i is incomplete\n",scriptline); - } - scriptline = ++script->line; - } - } - - if (script->script_p >= script->end_p) - { - return EndOfScript (crossline); - } - - // strip single line comments - if (*script->script_p == ';' || *script->script_p == '#' || // semicolon and # is comment field - (*script->script_p == '/' && *((script->script_p)+1) == '/')) // also make // a comment field - { - if (!crossline) - Error ("Line %i is incomplete\n",scriptline); - while (*script->script_p++ != '\n') - { - if (script->script_p >= script->end_p) - { - return EndOfScript (crossline); - } - } - scriptline = ++script->line; - goto skipspace; - } - - // strip out matching /* */ comments - if (*script->script_p == '/' && *((script->script_p)+1) == '*') - { - script->script_p += 2; - while (*script->script_p != '*' || *((script->script_p)+1) != '/') - { - if (*script->script_p++ != '\n') - { - if (script->script_p >= script->end_p) - { - return EndOfScript (crossline); - } - - scriptline = ++script->line; - } - } - script->script_p += 2; - goto skipspace; - } - - // copy token to buffer - token_p = token; - - if (*script->script_p == '"') - { - // quoted token - script->script_p++; - while (*script->script_p != '"') - { - *token_p++ = *script->script_p++; - if (script->script_p == script->end_p) - break; - if (token_p == &token[MAXTOKEN]) - Error ("Token too large on line %i\n",scriptline); - } - script->script_p++; - } - else if ( g_bCheckSingleCharTokens && !g_sSingleCharTokens.IsEmpty() && strchr( g_sSingleCharTokens.String(), *script->script_p ) != NULL ) - { - *token_p++ = *script->script_p++; - } - else // regular token - while ( *script->script_p > 32 && *script->script_p != ';') - { - if ( !ExpandMacroToken( token_p ) ) - { - if ( !ExpandVariableToken( token_p ) ) - { - *token_p++ = *script->script_p++; - if (script->script_p == script->end_p) - break; - if (token_p == &token[MAXTOKEN]) - Error ("Token too large on line %i\n",scriptline); - - } - } - } - - // add null to end of token - *token_p = 0; - - // check for other commands - if ( !stricmp( token, "$include" ) ) - { - GetToken( false ); - - bool bFallbackToToken = true; - - CUtlVector< CUtlString > expandedPathList; - - if ( CmdLib_ExpandWithBasePaths( expandedPathList, token ) > 0 ) - { - for ( int i = 0; i < expandedPathList.Count(); ++i ) - { - CUtlVector< CUtlString > findFileList; - FindFileAbsoluteList( findFileList, expandedPathList[i].String() ); - - if ( findFileList.Count() > 0 ) - { - bFallbackToToken = false; - - // Only add the first set of glob matches from the first base path - for ( int j = 0; j < findFileList.Count(); ++j ) - { - AddScriptToStack( const_cast< char * >( findFileList[j].String() ) ); - } - - break; - } - } - } - - if ( bFallbackToToken ) - { - AddScriptToStack( token ); - } - - return GetToken( crossline ); - } - else if (!stricmp (token, "$definemacro")) - { - GetToken (false); - DefineMacro(token); - return GetToken (crossline); - } - else if (!stricmp (token, "$definevariable")) - { - GetToken (false); - DefineVariable(token); - return GetToken (crossline); - } - else if (AddMacroToStack( token )) - { - return GetToken (crossline); - } - - return true; -} - - -/* -============== -GetExprToken - use C mathematical operator parsing rules to split tokens instead of whitespace -============== -*/ -qboolean GetExprToken (qboolean crossline) -{ - char *token_p; - - if (tokenready) // is a token allready waiting? - { - tokenready = false; - return true; - } - - if (script->script_p >= script->end_p) - return EndOfScript (crossline); - - tokenready = false; - -// -// skip space -// -skipspace: - while (*script->script_p <= 32) - { - if (script->script_p >= script->end_p) - return EndOfScript (crossline); - if (*script->script_p++ == '\n') - { - if (!crossline) - Error ("Line %i is incomplete\n",scriptline); - scriptline = ++script->line; - } - } - - if (script->script_p >= script->end_p) - return EndOfScript (crossline); - - if (*script->script_p == ';' || *script->script_p == '#' || // semicolon and # is comment field - (*script->script_p == '/' && *((script->script_p)+1) == '/')) // also make // a comment field - { - if (!crossline) - Error ("Line %i is incomplete\n",scriptline); - while (*script->script_p++ != '\n') - if (script->script_p >= script->end_p) - return EndOfScript (crossline); - goto skipspace; - } - -// -// copy token -// - token_p = token; - - if (*script->script_p == '"') - { - // quoted token - script->script_p++; - while (*script->script_p != '"') - { - *token_p++ = *script->script_p++; - if (script->script_p == script->end_p) - break; - if (token_p == &token[MAXTOKEN]) - Error ("Token too large on line %i\n",scriptline); - } - script->script_p++; - } - else - { - if ( V_isalpha( *script->script_p ) || *script->script_p == '_' ) - { - // regular token - while ( V_isalnum( *script->script_p ) || *script->script_p == '_' ) - { - *token_p++ = *script->script_p++; - if (script->script_p == script->end_p) - break; - if (token_p == &token[MAXTOKEN]) - Error ("Token too large on line %i\n",scriptline); - } - } - else if ( V_isdigit( *script->script_p ) || *script->script_p == '.' ) - { - // regular token - while ( V_isdigit( *script->script_p ) || *script->script_p == '.' ) - { - *token_p++ = *script->script_p++; - if (script->script_p == script->end_p) - break; - if (token_p == &token[MAXTOKEN]) - Error ("Token too large on line %i\n",scriptline); - } - } - else - { - // single char - *token_p++ = *script->script_p++; - } - } - - *token_p = 0; - - if (!stricmp (token, "$include")) - { - GetToken (false); - AddScriptToStack (token); - return GetToken (crossline); - } - - return true; -} - - -/* -============== -TokenAvailable - -Returns true if there is another token on the line -============== -*/ -qboolean TokenAvailable (void) -{ - char *search_p; - - if (tokenready) // is a token allready waiting? - { - return true; - } - - search_p = script->script_p; - - if (search_p >= script->end_p) - return false; - - while ( *search_p <= 32) - { - if (*search_p == '\n') - return false; - search_p++; - if (search_p == script->end_p) - return false; - - } - - if (*search_p == ';' || *search_p == '#' || // semicolon and # is comment field - (*search_p == '/' && *((search_p)+1) == '/')) // also make // a comment field - return false; - - return true; -} - -qboolean GetTokenizerStatus( char **pFilename, int *pLine ) -{ - // is this the default state? - if (!script) - return false; - - if (script->script_p >= script->end_p) - return false; - - if (pFilename) - { - *pFilename = script->filename; - } - if (pLine) - { - *pLine = script->line; - } - return true; -} - - -#include -#include -#ifdef WIN32 -#include -#include -#include -#endif -#include -#include -#include -#include -#include "tier1/utlbuffer.h" - -class CScriptLib : public IScriptLib -{ -public: - virtual bool ReadFileToBuffer( const char *pSourceName, CUtlBuffer &buffer, bool bText = false, bool bNoOpenFailureWarning = false ); - virtual bool WriteBufferToFile( const char *pTargetName, CUtlBuffer &buffer, DiskWriteMode_t writeMode ); - virtual int FindFiles( char* pFileMask, bool bRecurse, CUtlVector &fileList ); - virtual char *MakeTemporaryFilename( char const *pchModPath, char *pPath, int pathSize ); - virtual void DeleteTemporaryFiles( const char *pFileMask ); - virtual int CompareFileTime( const char *pFilenameA, const char *pFilenameB ); - virtual bool DoesFileExist( const char *pFilename ); - -private: - - int GetFileList( const char* pDirPath, const char* pPattern, CUtlVector< fileList_t > &fileList ); - void RecurseFileTree_r( const char* pDirPath, int depth, CUtlVector< CUtlString > &dirList ); -}; - -static CScriptLib g_ScriptLib; -IScriptLib *scriptlib = &g_ScriptLib; -IScriptLib *g_pScriptLib = &g_ScriptLib; - -//----------------------------------------------------------------------------- -// Existence check -//----------------------------------------------------------------------------- -bool CScriptLib::DoesFileExist( const char *pFilename ) -{ - return g_pFullFileSystem->FileExists( pFilename ); -} - -//----------------------------------------------------------------------------- -// Purpose: Helper utility, read file into buffer -//----------------------------------------------------------------------------- -bool CScriptLib::ReadFileToBuffer( const char *pSourceName, CUtlBuffer &buffer, bool bText, bool bNoOpenFailureWarning ) -{ - bool bSuccess = true; - - if ( !g_pFullFileSystem->ReadFile( pSourceName, NULL, buffer ) ) - { - if ( !bNoOpenFailureWarning ) - { - Msg( "ReadFileToBuffer(): Error opening %s: %s\n", pSourceName, strerror( errno ) ); - } - return false; - } - - if ( bText ) - { - // force it into text mode - buffer.SetBufferType( true, true ); - } - else - { - buffer.SetBufferType( false, false ); - } - - return bSuccess; -} - -//----------------------------------------------------------------------------- -// Purpose: Helper utility, Write buffer to file -//----------------------------------------------------------------------------- -bool CScriptLib::WriteBufferToFile( const char *pTargetName, CUtlBuffer &buffer, DiskWriteMode_t writeMode ) -{ - char* ptr; - char dirPath[MAX_PATH]; - - bool bSuccess = true; - - // create path - // prime and skip to first seperator - strcpy( dirPath, pTargetName ); - ptr = strchr( dirPath, '\\' ); - while ( ptr ) - { - ptr = strchr( ptr+1, '\\' ); - if ( ptr ) - { - *ptr = '\0'; - _mkdir( dirPath ); - *ptr = '\\'; - } - } - - bool bDoWrite = false; - if ( writeMode == WRITE_TO_DISK_ALWAYS ) - { - bDoWrite = true; - } - else if ( writeMode == WRITE_TO_DISK_UPDATE ) - { - if ( DoesFileExist( pTargetName ) ) - { - bDoWrite = true; - } - } - - if ( bDoWrite ) - { - bSuccess = g_pFullFileSystem->WriteFile( pTargetName, NULL, buffer ); - } - - return bSuccess; -} - -//----------------------------------------------------------------------------- -// Returns -1, 0, or 1. -//----------------------------------------------------------------------------- -int CScriptLib::CompareFileTime( const char *pFilenameA, const char *pFilenameB ) -{ - int timeA = g_pFullFileSystem->GetFileTime( (char *)pFilenameA ); - int timeB = g_pFullFileSystem->GetFileTime( (char *)pFilenameB ); - - if ( timeA == -1) - { - // file a not exist - timeA = 0; - } - if ( timeB == -1 ) - { - // file b not exist - timeB = 0; - } - - if ( (unsigned int)timeA < (unsigned int)timeB ) - { - return -1; - } - else if ( (unsigned int)timeA > (unsigned int)timeB ) - { - return 1; - } - - return 0; -} - -//----------------------------------------------------------------------------- -// Make a temporary filename -//----------------------------------------------------------------------------- -char *CScriptLib::MakeTemporaryFilename( char const *pchModPath, char *pPath, int pathSize ) -{ - char *pBuffer = _tempnam( pchModPath, "mgd_" ); - if ( pBuffer[0] == '\\' ) - { - pBuffer++; - } - if ( pBuffer[strlen( pBuffer )-1] == '.' ) - { - pBuffer[strlen( pBuffer )-1] = '\0'; - } - V_snprintf( pPath, pathSize, "%s.tmp", pBuffer ); - - free( pBuffer ); - - return pPath; -} - -//----------------------------------------------------------------------------- -// Delete temporary files -//----------------------------------------------------------------------------- -void CScriptLib::DeleteTemporaryFiles( const char *pFileMask ) -{ -#if !defined( _X360 ) - const char *pEnv = getenv( "temp" ); - if ( !pEnv ) - { - pEnv = getenv( "tmp" ); - } - - if ( pEnv ) - { - char tempPath[MAX_PATH]; - strcpy( tempPath, pEnv ); - V_AppendSlash( tempPath, sizeof( tempPath ) ); - strcat( tempPath, pFileMask ); - - CUtlVector fileList; - FindFiles( tempPath, false, fileList ); - for ( int i=0; i &fileList ) -{ - char sourcePath[MAX_PATH]; - char fullPath[MAX_PATH]; - bool bFindDirs; - - fileList.Purge(); - - strcpy( sourcePath, pDirPath ); - int len = (int)strlen( sourcePath ); - if ( !len ) - { - strcpy( sourcePath, ".\\" ); - } - else if ( sourcePath[len-1] != '\\' ) - { - sourcePath[len] = '\\'; - sourcePath[len+1] = '\0'; - } - - strcpy( fullPath, sourcePath ); - if ( pPattern[0] == '\\' && pPattern[1] == '\0' ) - { - // find directories only - bFindDirs = true; - strcat( fullPath, "*" ); - } - else - { - // find files, use provided pattern - bFindDirs = false; - strcat( fullPath, pPattern ); - } - -#ifdef WIN32 - struct _finddata_t findData; - intptr_t h = _findfirst( fullPath, &findData ); - if ( h == -1 ) - { - return 0; - } - - do - { - // dos attribute complexities i.e. _A_NORMAL is 0 - if ( bFindDirs ) - { - // skip non dirs - if ( !( findData.attrib & _A_SUBDIR ) ) - continue; - } - else - { - // skip dirs - if ( findData.attrib & _A_SUBDIR ) - continue; - } - - if ( !stricmp( findData.name, "." ) ) - continue; - - if ( !stricmp( findData.name, ".." ) ) - continue; - - char fileName[MAX_PATH]; - strcpy( fileName, sourcePath ); - strcat( fileName, findData.name ); - - int j = fileList.AddToTail(); - fileList[j].fileName.Set( fileName ); - fileList[j].timeWrite = findData.time_write; - } - while ( !_findnext( h, &findData ) ); - - _findclose( h ); -#elif defined(POSIX) - FIND_DATA findData; - Q_FixSlashes( fullPath ); - void *h = FindFirstFile( fullPath, &findData ); - if ( (int)h == -1 ) - { - return 0; - } - - do - { - // dos attribute complexities i.e. _A_NORMAL is 0 - if ( bFindDirs ) - { - // skip non dirs - if ( !( findData.dwFileAttributes & S_IFDIR ) ) - continue; - } - else - { - // skip dirs - if ( findData.dwFileAttributes & S_IFDIR ) - continue; - } - - if ( !stricmp( findData.cFileName, "." ) ) - continue; - - if ( !stricmp( findData.cFileName, ".." ) ) - continue; - - char fileName[MAX_PATH]; - strcpy( fileName, sourcePath ); - strcat( fileName, findData.cFileName ); - - int j = fileList.AddToTail(); - fileList[j].fileName.Set( fileName ); - struct stat statbuf; - if ( stat( fileName, &statbuf ) ) -#ifdef OSX - fileList[j].timeWrite = statbuf.st_mtimespec.tv_sec; -#else - fileList[j].timeWrite = statbuf.st_mtime; -#endif - else - fileList[j].timeWrite = 0; - } - while ( !FindNextFile( h, &findData ) ); - - FindClose( h ); - -#else -#error -#endif - - - return fileList.Count(); -} - -//----------------------------------------------------------------------------- -// Purpose: Recursively determine directory tree -//----------------------------------------------------------------------------- -void CScriptLib::RecurseFileTree_r( const char* pDirPath, int depth, CUtlVector< CUtlString > &dirList ) -{ - // recurse from source directory, get directories only - CUtlVector< fileList_t > fileList; - int dirCount = GetFileList( pDirPath, "\\", fileList ); - if ( !dirCount ) - { - // add directory name to search tree - int j = dirList.AddToTail(); - dirList[j].Set( pDirPath ); - return; - } - - for ( int i=0; i &fileList ) -{ - char dirPath[MAX_PATH]; - char pattern[MAX_PATH]; - char extension[MAX_PATH]; - - // get path only - strcpy( dirPath, pFileMask ); - V_StripFilename( dirPath ); - - // get pattern only - V_FileBase( pFileMask, pattern, sizeof( pattern ) ); - V_ExtractFileExtension( pFileMask, extension, sizeof( extension ) ); - if ( extension[0] ) - { - strcat( pattern, "." ); - strcat( pattern, extension ); - } - - if ( !bRecurse ) - { - GetFileList( dirPath, pattern, fileList ); - } - else - { - // recurse and get the tree - CUtlVector< fileList_t > tempList; - CUtlVector< CUtlString > dirList; - RecurseFileTree_r( dirPath, 0, dirList ); - for ( int i=0; i +#endif +/* +============================================================================= + + PARSING STUFF + +============================================================================= +*/ + +typedef struct +{ + char filename[1024]; + char *buffer,*script_p,*end_p; + int line; + + char macrobuffer[4096]; + char *macroparam[64]; + char *macrovalue[64]; + int nummacroparams; + +} script_t; + +#define MAX_INCLUDES 64 +script_t scriptstack[MAX_INCLUDES]; +script_t *script = NULL; +int scriptline; + +char token[MAXTOKEN]; +qboolean endofscript; +qboolean tokenready; // only true if UnGetToken was just called + +typedef struct +{ + char *param; + char *value; +} variable_t; + +CUtlVector g_definevariable; + +/* +Callback stuff +*/ + +void DefaultScriptLoadedCallback( char const *pFilenameLoaded, char const *pIncludedFromFileName, int nIncludeLineNumber ) +{ + NULL; +} + +SCRIPT_LOADED_CALLBACK g_pfnCallback = DefaultScriptLoadedCallback; + +SCRIPT_LOADED_CALLBACK SetScriptLoadedCallback( SCRIPT_LOADED_CALLBACK pfnNewScriptLoadedCallback ) +{ + SCRIPT_LOADED_CALLBACK pfnCallback = g_pfnCallback; + g_pfnCallback = pfnNewScriptLoadedCallback; + return pfnCallback; +} + +/* +============== +AddScriptToStack +============== +*/ +void AddScriptToStack (char *filename, ScriptPathMode_t pathMode = SCRIPT_USE_ABSOLUTE_PATH) +{ + int size; + + script++; + if (script == &scriptstack[MAX_INCLUDES]) + Error ("script file exceeded MAX_INCLUDES"); + + if ( pathMode == SCRIPT_USE_RELATIVE_PATH ) + Q_strncpy( script->filename, filename, sizeof( script->filename ) ); + else + Q_strncpy (script->filename, ExpandPath (filename), sizeof( script->filename ) ); + + size = LoadFile (script->filename, (void **)&script->buffer); + + // printf ("entering %s\n", script->filename); + if ( g_pfnCallback ) + { + if ( script == scriptstack + 1 ) + g_pfnCallback( script->filename, NULL, 0 ); + else + g_pfnCallback( script->filename, script[-1].filename, script[-1].line ); + } + + script->line = 1; + + script->script_p = script->buffer; + script->end_p = script->buffer + size; +} + + +/* +============== +LoadScriptFile +============== +*/ +void LoadScriptFile (char *filename, ScriptPathMode_t pathMode) +{ + script = scriptstack; + AddScriptToStack (filename, pathMode); + + endofscript = false; + tokenready = false; +} + + +/* +============== +============== +*/ + +script_t *macrolist[256]; +int nummacros; + +void DefineMacro( char *macroname ) +{ + script_t *pmacro = (script_t *)malloc( sizeof( script_t ) ); + + strcpy( pmacro->filename, macroname ); + pmacro->line = script->line; + pmacro->nummacroparams = 0; + + char *mp = pmacro->macrobuffer; + char *cp = script->script_p; + + while (TokenAvailable( )) + { + GetToken( false ); + + if (token[0] == '\\' && token[1] == '\\') + { + break; + } + cp = script->script_p; + + pmacro->macroparam[pmacro->nummacroparams++] = mp; + + strcpy( mp, token ); + mp += strlen( token ) + 1; + + if (mp >= pmacro->macrobuffer + sizeof( pmacro->macrobuffer )) + Error("Macro buffer overflow\n"); + } + // roll back script_p to previous valid location + script->script_p = cp; + + // find end of macro def + while (*cp && *cp != '\n') + { + //Msg("%d ", *cp ); + if (*cp == '\\' && *(cp+1) == '\\') + { + // skip till end of line + while (*cp && *cp != '\n') + { + *cp = ' '; // replace with spaces + cp++; + } + + if (*cp) + { + cp++; + } + } + else + { + cp++; + } + } + + int size = (cp - script->script_p); + + pmacro->buffer = (char *)malloc( size + 1); + memcpy( pmacro->buffer, script->script_p, size ); + pmacro->buffer[size] = '\0'; + pmacro->end_p = &pmacro->buffer[size]; + + macrolist[nummacros++] = pmacro; + + script->script_p = cp; +} + + +void DefineVariable( char *variablename ) +{ + variable_t v; + + v.param = strdup( variablename ); + + GetToken( false ); + + v.value = strdup( token ); + + g_definevariable.AddToTail( v ); +} + + + +/* +============== +============== +*/ +bool AddMacroToStack( char *macroname ) +{ + // lookup macro + if (macroname[0] != '$') + return false; + + int i; + for (i = 0; i < nummacros; i++) + { + if (strcmpi( macrolist[i]->filename, ¯oname[1] ) == 0) + { + break; + } + } + if (i == nummacros) + return false; + + script_t *pmacro = macrolist[i]; + + // get tokens + script_t *pnext = script + 1; + + pnext++; + if (pnext == &scriptstack[MAX_INCLUDES]) + Error ("script file exceeded MAX_INCLUDES"); + + // get tokens + char *cp = pnext->macrobuffer; + + pnext->nummacroparams = pmacro->nummacroparams; + + for (i = 0; i < pnext->nummacroparams; i++) + { + GetToken(false); + + strcpy( cp, token ); + pnext->macroparam[i] = pmacro->macroparam[i]; + pnext->macrovalue[i] = cp; + + cp += strlen( token ) + 1; + + if (cp >= pnext->macrobuffer + sizeof( pnext->macrobuffer )) + Error("Macro buffer overflow\n"); + } + + script = pnext; + strcpy( script->filename, pmacro->filename ); + + int size = pmacro->end_p - pmacro->buffer; + script->buffer = (char *)malloc( size + 1 ); + memcpy( script->buffer, pmacro->buffer, size ); + pmacro->buffer[size] = '\0'; + script->script_p = script->buffer; + script->end_p = script->buffer + size; + script->line = pmacro->line; + + return true; +} + + + +bool ExpandMacroToken( char *&token_p ) +{ + if ( script->nummacroparams && *script->script_p == '$' ) + { + char *cp = script->script_p + 1; + + while ( *cp > 32 && *cp != '$' ) + { + cp++; + } + + // found a word with $'s on either end? + if (*cp != '$') + return false; + + // get token pointer + char *tp = script->script_p + 1; + int len = (cp - tp); + *(tp + len) = '\0'; + + // lookup macro parameter + int index = 0; + for (index = 0; index < script->nummacroparams; index++) + { + if (stricmp( script->macroparam[index], tp ) == 0) + break; + } + if (index >= script->nummacroparams) + { + Error("unknown macro token \"%s\" in %s\n", tp, script->filename ); + } + + // paste token into + len = strlen( script->macrovalue[index] ); + strcpy( token_p, script->macrovalue[index] ); + token_p += len; + + script->script_p = cp + 1; + + if (script->script_p >= script->end_p) + Error ("Macro expand overflow\n"); + + if (token_p >= &token[MAXTOKEN]) + Error ("Token too large on line %i\n",scriptline); + + return true; + } + return false; +} + + + +/* +============== +============== +*/ +// FIXME: this should create a new script context so the individual tokens in the variable can be parsed +bool ExpandVariableToken( char *&token_p ) +{ + if ( *script->script_p == '$' ) + { + char *cp = script->script_p + 1; + + while ( *cp > 32 && *cp != '$' ) + { + cp++; + } + + // found a word with $'s on either end? + if (*cp != '$') + return false; + + // get token pointer + char *tp = script->script_p + 1; + int len = (cp - tp); + *(tp + len) = '\0'; + + // lookup macro parameter + + int index; + for (index = 0; index < g_definevariable.Count(); index++) + { + if (Q_strnicmp( g_definevariable[index].param, tp, len ) == 0) + break; + } + + if (index >= g_definevariable.Count() ) + { + Error("unknown variable token \"%s\" in %s\n", tp, script->filename ); + } + + // paste token into + len = strlen( g_definevariable[index].value ); + strcpy( token_p, g_definevariable[index].value ); + token_p += len; + + script->script_p = cp + 1; + + if (script->script_p >= script->end_p) + Error ("Macro expand overflow\n"); + + if (token_p >= &token[MAXTOKEN]) + Error ("Token too large on line %i\n",scriptline); + + return true; + } + return false; +} + + + +/* +============== +ParseFromMemory +============== +*/ +void ParseFromMemory (char *buffer, int size) +{ + script = scriptstack; + script++; + if (script == &scriptstack[MAX_INCLUDES]) + Error ("script file exceeded MAX_INCLUDES"); + strcpy (script->filename, "memory buffer" ); + + script->buffer = buffer; + script->line = 1; + script->script_p = script->buffer; + script->end_p = script->buffer + size; + + endofscript = false; + tokenready = false; +} + + +//----------------------------------------------------------------------------- +// Used instead of ParseFromMemory to temporarily add a memory buffer +// to the script stack. ParseFromMemory just blows away the stack. +//----------------------------------------------------------------------------- +void PushMemoryScript( char *pszBuffer, const int nSize ) +{ + if ( script == NULL ) + { + script = scriptstack; + } + script++; + if ( script == &scriptstack[MAX_INCLUDES] ) + { + Error ( "script file exceeded MAX_INCLUDES" ); + } + strcpy (script->filename, "memory buffer" ); + + script->buffer = pszBuffer; + script->line = 1; + script->script_p = script->buffer; + script->end_p = script->buffer + nSize; + + endofscript = false; + tokenready = false; +} + + +//----------------------------------------------------------------------------- +// Used after calling PushMemoryScript to clean up the memory buffer +// added to the script stack. The normal end of script terminates +// all parsing at the end of a memory buffer even if there are more scripts +// remaining on the script stack +//----------------------------------------------------------------------------- +bool PopMemoryScript() +{ + if ( V_stricmp( script->filename, "memory buffer" ) ) + return false; + + if ( script == scriptstack ) + { + endofscript = true; + return false; + } + script--; + scriptline = script->line; + + endofscript = false; + + return true; +} + + +/* +============== +UnGetToken + +Signals that the current token was not used, and should be reported +for the next GetToken. Note that + +GetToken (true); +UnGetToken (); +GetToken (false); + +could cross a line boundary. +============== +*/ +void UnGetToken (void) +{ + tokenready = true; +} + + +qboolean EndOfScript (qboolean crossline) +{ + if (!crossline) + Error ("Line %i is incomplete\n",scriptline); + + if (!strcmp (script->filename, "memory buffer")) + { + endofscript = true; + return false; + } + + free (script->buffer); + script->buffer = NULL; + if (script == scriptstack+1) + { + endofscript = true; + return false; + } + script--; + scriptline = script->line; + // printf ("returning to %s\n", script->filename); + return GetToken (crossline); +} + + +//----------------------------------------------------------------------------- +// Purpose: Given an absolute path, do a find first find next on it and build +// a list of files. Physical file system only +//----------------------------------------------------------------------------- +static void FindFileAbsoluteList( CUtlVector< CUtlString > &outAbsolutePathNames, const char *pszFindName ) +{ + char szPath[MAX_PATH]; + V_strncpy( szPath, pszFindName, sizeof( szPath ) ); + V_StripFilename( szPath ); + + char szResult[MAX_PATH]; + FileFindHandle_t hFile = FILESYSTEM_INVALID_FIND_HANDLE; + + for ( const char *pszFoundFile = g_pFullFileSystem->FindFirst( pszFindName, &hFile ); pszFoundFile && hFile != FILESYSTEM_INVALID_FIND_HANDLE; pszFoundFile = g_pFullFileSystem->FindNext( hFile ) ) + { + V_ComposeFileName( szPath, pszFoundFile, szResult, sizeof( szResult ) ); + outAbsolutePathNames.AddToTail( szResult ); + } + + g_pFullFileSystem->FindClose( hFile ); +} + + +//----------------------------------------------------------------------------- +// Data for checking for single character tokens while parsing +//----------------------------------------------------------------------------- +bool g_bCheckSingleCharTokens = false; +CUtlString g_sSingleCharTokens; + + +//----------------------------------------------------------------------------- +// Sets whether the scriplib parser will do a special check for single +// character tokens. Returns previous state of whether single character +// tokens will be checked. +//----------------------------------------------------------------------------- +bool SetCheckSingleCharTokens( bool bCheck ) +{ + const bool bRetVal = g_bCheckSingleCharTokens; + + g_bCheckSingleCharTokens = bCheck; + + return bRetVal; +} + + +//----------------------------------------------------------------------------- +// Sets the list of single character tokens to check if SetCheckSingleCharTokens +// is turned on. +//----------------------------------------------------------------------------- +CUtlString SetSingleCharTokenList( const char *pszSingleCharTokenList ) +{ + const CUtlString sRetVal = g_sSingleCharTokens; + + if ( pszSingleCharTokenList ) + { + g_sSingleCharTokens = pszSingleCharTokenList; + } + + return sRetVal; +} + + +/* +============== +GetToken +============== +*/ +qboolean GetToken (qboolean crossline) +{ + char *token_p; + + if (tokenready) // is a token allready waiting? + { + tokenready = false; + return true; + } + + // printf("script_p %x (%x)\n", script->script_p, script->end_p ); fflush( stdout ); + + if (script->script_p >= script->end_p) + { + return EndOfScript (crossline); + } + + tokenready = false; + + // skip space, ctrl chars +skipspace: + while (*script->script_p <= 32) + { + if (script->script_p >= script->end_p) + { + return EndOfScript (crossline); + } + if (*(script->script_p++) == '\n') + { + if (!crossline) + { + Error ("Line %i is incomplete\n",scriptline); + } + scriptline = ++script->line; + } + } + + if (script->script_p >= script->end_p) + { + return EndOfScript (crossline); + } + + // strip single line comments + if (*script->script_p == ';' || *script->script_p == '#' || // semicolon and # is comment field + (*script->script_p == '/' && *((script->script_p)+1) == '/')) // also make // a comment field + { + if (!crossline) + Error ("Line %i is incomplete\n",scriptline); + while (*script->script_p++ != '\n') + { + if (script->script_p >= script->end_p) + { + return EndOfScript (crossline); + } + } + scriptline = ++script->line; + goto skipspace; + } + + // strip out matching /* */ comments + if (*script->script_p == '/' && *((script->script_p)+1) == '*') + { + script->script_p += 2; + while (*script->script_p != '*' || *((script->script_p)+1) != '/') + { + if (*script->script_p++ != '\n') + { + if (script->script_p >= script->end_p) + { + return EndOfScript (crossline); + } + + scriptline = ++script->line; + } + } + script->script_p += 2; + goto skipspace; + } + + // copy token to buffer + token_p = token; + + if (*script->script_p == '"') + { + // quoted token + script->script_p++; + while (*script->script_p != '"') + { + *token_p++ = *script->script_p++; + if (script->script_p == script->end_p) + break; + if (token_p == &token[MAXTOKEN]) + Error ("Token too large on line %i\n",scriptline); + } + script->script_p++; + } + else if ( g_bCheckSingleCharTokens && !g_sSingleCharTokens.IsEmpty() && strchr( g_sSingleCharTokens.String(), *script->script_p ) != NULL ) + { + *token_p++ = *script->script_p++; + } + else // regular token + while ( *script->script_p > 32 && *script->script_p != ';') + { + if ( !ExpandMacroToken( token_p ) ) + { + if ( !ExpandVariableToken( token_p ) ) + { + *token_p++ = *script->script_p++; + if (script->script_p == script->end_p) + break; + if (token_p == &token[MAXTOKEN]) + Error ("Token too large on line %i\n",scriptline); + + } + } + } + + // add null to end of token + *token_p = 0; + + // check for other commands + if ( !stricmp( token, "$include" ) ) + { + GetToken( false ); + + bool bFallbackToToken = true; + + CUtlVector< CUtlString > expandedPathList; + + if ( CmdLib_ExpandWithBasePaths( expandedPathList, token ) > 0 ) + { + for ( int i = 0; i < expandedPathList.Count(); ++i ) + { + CUtlVector< CUtlString > findFileList; + FindFileAbsoluteList( findFileList, expandedPathList[i].String() ); + + if ( findFileList.Count() > 0 ) + { + bFallbackToToken = false; + + // Only add the first set of glob matches from the first base path + for ( int j = 0; j < findFileList.Count(); ++j ) + { + AddScriptToStack( const_cast< char * >( findFileList[j].String() ) ); + } + + break; + } + } + } + + if ( bFallbackToToken ) + { + AddScriptToStack( token ); + } + + return GetToken( crossline ); + } + else if (!stricmp (token, "$definemacro")) + { + GetToken (false); + DefineMacro(token); + return GetToken (crossline); + } + else if (!stricmp (token, "$definevariable")) + { + GetToken (false); + DefineVariable(token); + return GetToken (crossline); + } + else if (AddMacroToStack( token )) + { + return GetToken (crossline); + } + + return true; +} + + +/* +============== +GetExprToken - use C mathematical operator parsing rules to split tokens instead of whitespace +============== +*/ +qboolean GetExprToken (qboolean crossline) +{ + char *token_p; + + if (tokenready) // is a token allready waiting? + { + tokenready = false; + return true; + } + + if (script->script_p >= script->end_p) + return EndOfScript (crossline); + + tokenready = false; + +// +// skip space +// +skipspace: + while (*script->script_p <= 32) + { + if (script->script_p >= script->end_p) + return EndOfScript (crossline); + if (*script->script_p++ == '\n') + { + if (!crossline) + Error ("Line %i is incomplete\n",scriptline); + scriptline = ++script->line; + } + } + + if (script->script_p >= script->end_p) + return EndOfScript (crossline); + + if (*script->script_p == ';' || *script->script_p == '#' || // semicolon and # is comment field + (*script->script_p == '/' && *((script->script_p)+1) == '/')) // also make // a comment field + { + if (!crossline) + Error ("Line %i is incomplete\n",scriptline); + while (*script->script_p++ != '\n') + if (script->script_p >= script->end_p) + return EndOfScript (crossline); + goto skipspace; + } + +// +// copy token +// + token_p = token; + + if (*script->script_p == '"') + { + // quoted token + script->script_p++; + while (*script->script_p != '"') + { + *token_p++ = *script->script_p++; + if (script->script_p == script->end_p) + break; + if (token_p == &token[MAXTOKEN]) + Error ("Token too large on line %i\n",scriptline); + } + script->script_p++; + } + else + { + if ( V_isalpha( *script->script_p ) || *script->script_p == '_' ) + { + // regular token + while ( V_isalnum( *script->script_p ) || *script->script_p == '_' ) + { + *token_p++ = *script->script_p++; + if (script->script_p == script->end_p) + break; + if (token_p == &token[MAXTOKEN]) + Error ("Token too large on line %i\n",scriptline); + } + } + else if ( V_isdigit( *script->script_p ) || *script->script_p == '.' ) + { + // regular token + while ( V_isdigit( *script->script_p ) || *script->script_p == '.' ) + { + *token_p++ = *script->script_p++; + if (script->script_p == script->end_p) + break; + if (token_p == &token[MAXTOKEN]) + Error ("Token too large on line %i\n",scriptline); + } + } + else + { + // single char + *token_p++ = *script->script_p++; + } + } + + *token_p = 0; + + if (!stricmp (token, "$include")) + { + GetToken (false); + AddScriptToStack (token); + return GetToken (crossline); + } + + return true; +} + + +/* +============== +TokenAvailable + +Returns true if there is another token on the line +============== +*/ +qboolean TokenAvailable (void) +{ + char *search_p; + + if (tokenready) // is a token allready waiting? + { + return true; + } + + search_p = script->script_p; + + if (search_p >= script->end_p) + return false; + + while ( *search_p <= 32) + { + if (*search_p == '\n') + return false; + search_p++; + if (search_p == script->end_p) + return false; + + } + + if (*search_p == ';' || *search_p == '#' || // semicolon and # is comment field + (*search_p == '/' && *((search_p)+1) == '/')) // also make // a comment field + return false; + + return true; +} + +qboolean GetTokenizerStatus( char **pFilename, int *pLine ) +{ + // is this the default state? + if (!script) + return false; + + if (script->script_p >= script->end_p) + return false; + + if (pFilename) + { + *pFilename = script->filename; + } + if (pLine) + { + *pLine = script->line; + } + return true; +} + + +#include +#include +#ifdef WIN32 +#include +#include +#include +#endif +#include +#include +#include +#include +#include "tier1/utlbuffer.h" + +class CScriptLib : public IScriptLib +{ +public: + virtual bool ReadFileToBuffer( const char *pSourceName, CUtlBuffer &buffer, bool bText = false, bool bNoOpenFailureWarning = false ); + virtual bool WriteBufferToFile( const char *pTargetName, CUtlBuffer &buffer, DiskWriteMode_t writeMode ); + virtual int FindFiles( char* pFileMask, bool bRecurse, CUtlVector &fileList ); + virtual char *MakeTemporaryFilename( char const *pchModPath, char *pPath, int pathSize ); + virtual void DeleteTemporaryFiles( const char *pFileMask ); + virtual int CompareFileTime( const char *pFilenameA, const char *pFilenameB ); + virtual bool DoesFileExist( const char *pFilename ); + +private: + + int GetFileList( const char* pDirPath, const char* pPattern, CUtlVector< fileList_t > &fileList ); + void RecurseFileTree_r( const char* pDirPath, int depth, CUtlVector< CUtlString > &dirList ); +}; + +static CScriptLib g_ScriptLib; +IScriptLib *scriptlib = &g_ScriptLib; +IScriptLib *g_pScriptLib = &g_ScriptLib; + +//----------------------------------------------------------------------------- +// Existence check +//----------------------------------------------------------------------------- +bool CScriptLib::DoesFileExist( const char *pFilename ) +{ + return g_pFullFileSystem->FileExists( pFilename ); +} + +//----------------------------------------------------------------------------- +// Purpose: Helper utility, read file into buffer +//----------------------------------------------------------------------------- +bool CScriptLib::ReadFileToBuffer( const char *pSourceName, CUtlBuffer &buffer, bool bText, bool bNoOpenFailureWarning ) +{ + bool bSuccess = true; + + if ( !g_pFullFileSystem->ReadFile( pSourceName, NULL, buffer ) ) + { + if ( !bNoOpenFailureWarning ) + { + Msg( "ReadFileToBuffer(): Error opening %s: %s\n", pSourceName, strerror( errno ) ); + } + return false; + } + + if ( bText ) + { + // force it into text mode + buffer.SetBufferType( true, true ); + } + else + { + buffer.SetBufferType( false, false ); + } + + return bSuccess; +} + +//----------------------------------------------------------------------------- +// Purpose: Helper utility, Write buffer to file +//----------------------------------------------------------------------------- +bool CScriptLib::WriteBufferToFile( const char *pTargetName, CUtlBuffer &buffer, DiskWriteMode_t writeMode ) +{ + char* ptr; + char dirPath[MAX_PATH]; + + bool bSuccess = true; + + // create path + // prime and skip to first seperator + strcpy( dirPath, pTargetName ); + ptr = strchr( dirPath, '\\' ); + while ( ptr ) + { + ptr = strchr( ptr+1, '\\' ); + if ( ptr ) + { + *ptr = '\0'; + _mkdir( dirPath ); + *ptr = '\\'; + } + } + + bool bDoWrite = false; + if ( writeMode == WRITE_TO_DISK_ALWAYS ) + { + bDoWrite = true; + } + else if ( writeMode == WRITE_TO_DISK_UPDATE ) + { + if ( DoesFileExist( pTargetName ) ) + { + bDoWrite = true; + } + } + + if ( bDoWrite ) + { + bSuccess = g_pFullFileSystem->WriteFile( pTargetName, NULL, buffer ); + } + + return bSuccess; +} + +//----------------------------------------------------------------------------- +// Returns -1, 0, or 1. +//----------------------------------------------------------------------------- +int CScriptLib::CompareFileTime( const char *pFilenameA, const char *pFilenameB ) +{ + int timeA = g_pFullFileSystem->GetFileTime( (char *)pFilenameA ); + int timeB = g_pFullFileSystem->GetFileTime( (char *)pFilenameB ); + + if ( timeA == -1) + { + // file a not exist + timeA = 0; + } + if ( timeB == -1 ) + { + // file b not exist + timeB = 0; + } + + if ( (unsigned int)timeA < (unsigned int)timeB ) + { + return -1; + } + else if ( (unsigned int)timeA > (unsigned int)timeB ) + { + return 1; + } + + return 0; +} + +//----------------------------------------------------------------------------- +// Make a temporary filename +//----------------------------------------------------------------------------- +char *CScriptLib::MakeTemporaryFilename( char const *pchModPath, char *pPath, int pathSize ) +{ + char *pBuffer = _tempnam( pchModPath, "mgd_" ); + if ( pBuffer[0] == '\\' ) + { + pBuffer++; + } + if ( pBuffer[strlen( pBuffer )-1] == '.' ) + { + pBuffer[strlen( pBuffer )-1] = '\0'; + } + V_snprintf( pPath, pathSize, "%s.tmp", pBuffer ); + + free( pBuffer ); + + return pPath; +} + +//----------------------------------------------------------------------------- +// Delete temporary files +//----------------------------------------------------------------------------- +void CScriptLib::DeleteTemporaryFiles( const char *pFileMask ) +{ +#if !defined( _X360 ) + const char *pEnv = getenv( "temp" ); + if ( !pEnv ) + { + pEnv = getenv( "tmp" ); + } + + if ( pEnv ) + { + char tempPath[MAX_PATH]; + strcpy( tempPath, pEnv ); + V_AppendSlash( tempPath, sizeof( tempPath ) ); + strcat( tempPath, pFileMask ); + + CUtlVector fileList; + FindFiles( tempPath, false, fileList ); + for ( int i=0; i &fileList ) +{ + char sourcePath[MAX_PATH]; + char fullPath[MAX_PATH]; + bool bFindDirs; + + fileList.Purge(); + + strcpy( sourcePath, pDirPath ); + int len = (int)strlen( sourcePath ); + if ( !len ) + { + strcpy( sourcePath, ".\\" ); + } + else if ( sourcePath[len-1] != '\\' ) + { + sourcePath[len] = '\\'; + sourcePath[len+1] = '\0'; + } + + strcpy( fullPath, sourcePath ); + if ( pPattern[0] == '\\' && pPattern[1] == '\0' ) + { + // find directories only + bFindDirs = true; + strcat( fullPath, "*" ); + } + else + { + // find files, use provided pattern + bFindDirs = false; + strcat( fullPath, pPattern ); + } + +#ifdef WIN32 + struct _finddata_t findData; + intptr_t h = _findfirst( fullPath, &findData ); + if ( h == -1 ) + { + return 0; + } + + do + { + // dos attribute complexities i.e. _A_NORMAL is 0 + if ( bFindDirs ) + { + // skip non dirs + if ( !( findData.attrib & _A_SUBDIR ) ) + continue; + } + else + { + // skip dirs + if ( findData.attrib & _A_SUBDIR ) + continue; + } + + if ( !stricmp( findData.name, "." ) ) + continue; + + if ( !stricmp( findData.name, ".." ) ) + continue; + + char fileName[MAX_PATH]; + strcpy( fileName, sourcePath ); + strcat( fileName, findData.name ); + + int j = fileList.AddToTail(); + fileList[j].fileName.Set( fileName ); + fileList[j].timeWrite = findData.time_write; + } + while ( !_findnext( h, &findData ) ); + + _findclose( h ); +#elif defined(POSIX) + FIND_DATA findData; + Q_FixSlashes( fullPath ); + void *h = FindFirstFile( fullPath, &findData ); + if ( (int)h == -1 ) + { + return 0; + } + + do + { + // dos attribute complexities i.e. _A_NORMAL is 0 + if ( bFindDirs ) + { + // skip non dirs + if ( !( findData.dwFileAttributes & S_IFDIR ) ) + continue; + } + else + { + // skip dirs + if ( findData.dwFileAttributes & S_IFDIR ) + continue; + } + + if ( !stricmp( findData.cFileName, "." ) ) + continue; + + if ( !stricmp( findData.cFileName, ".." ) ) + continue; + + char fileName[MAX_PATH]; + strcpy( fileName, sourcePath ); + strcat( fileName, findData.cFileName ); + + int j = fileList.AddToTail(); + fileList[j].fileName.Set( fileName ); + struct stat statbuf; + if ( stat( fileName, &statbuf ) ) +#ifdef OSX + fileList[j].timeWrite = statbuf.st_mtimespec.tv_sec; +#else + fileList[j].timeWrite = statbuf.st_mtime; +#endif + else + fileList[j].timeWrite = 0; + } + while ( !FindNextFile( h, &findData ) ); + + FindClose( h ); + +#else +#error +#endif + + + return fileList.Count(); +} + +//----------------------------------------------------------------------------- +// Purpose: Recursively determine directory tree +//----------------------------------------------------------------------------- +void CScriptLib::RecurseFileTree_r( const char* pDirPath, int depth, CUtlVector< CUtlString > &dirList ) +{ + // recurse from source directory, get directories only + CUtlVector< fileList_t > fileList; + int dirCount = GetFileList( pDirPath, "\\", fileList ); + if ( !dirCount ) + { + // add directory name to search tree + int j = dirList.AddToTail(); + dirList[j].Set( pDirPath ); + return; + } + + for ( int i=0; i &fileList ) +{ + char dirPath[MAX_PATH]; + char pattern[MAX_PATH]; + char extension[MAX_PATH]; + + // get path only + strcpy( dirPath, pFileMask ); + V_StripFilename( dirPath ); + + // get pattern only + V_FileBase( pFileMask, pattern, sizeof( pattern ) ); + V_ExtractFileExtension( pFileMask, extension, sizeof( extension ) ); + if ( extension[0] ) + { + strcat( pattern, "." ); + strcat( pattern, extension ); + } + + if ( !bRecurse ) + { + GetFileList( dirPath, pattern, fileList ); + } + else + { + // recurse and get the tree + CUtlVector< fileList_t > tempList; + CUtlVector< CUtlString > dirList; + RecurseFileTree_r( dirPath, 0, dirList ); + for ( int i=0; i &fileList ) = 0; - virtual char *MakeTemporaryFilename( char const *pchModPath, char *pPath, int pathSize ) = 0; - virtual void DeleteTemporaryFiles( const char *pFileMask ) = 0; - virtual int CompareFileTime( const char *pFilenameA, const char *pFilenameB ) = 0; - virtual bool DoesFileExist( const char *pFilename ) = 0; -}; - -extern IScriptLib *scriptlib; - - -#endif // SCRIPLIB_H +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//=============================================================================// + +#ifndef SCRIPLIB_H +#define SCRIPLIB_H + +#ifdef _WIN32 +#pragma once +#endif + + +enum ScriptPathMode_t +{ + SCRIPT_USE_ABSOLUTE_PATH, + SCRIPT_USE_RELATIVE_PATH +}; + + +// scriplib.h + +#define MAXTOKEN 1024 + +extern char token[MAXTOKEN]; +extern char *scriptbuffer,*script_p,*scriptend_p; +extern int grabbed; +extern int scriptline; +extern qboolean endofscript; + + +// If pathMode is SCRIPT_USE_ABSOLUTE_PATH, then it uses ExpandPath() on the filename before +// trying to open it. Otherwise, it passes the filename straight into the filesystem +// (so you can leave it as a relative path). +void LoadScriptFile (char *filename, ScriptPathMode_t pathMode=SCRIPT_USE_ABSOLUTE_PATH); +void ParseFromMemory (char *buffer, int size); + +qboolean GetToken (qboolean crossline); +qboolean GetExprToken (qboolean crossline); +void UnGetToken (void); +qboolean TokenAvailable (void); +qboolean GetTokenizerStatus( char **pFilename, int *pLine ); +bool SetCheckSingleCharTokens( bool bCheck ); + +// SCRIPT_LOADED_CALLBACK: +// Is called after the contents of a file is loaded. +// pFilenameLoaded is the path of a file that got loaded. +// pIncludedFromFileName is the name of the parent file or NULL if loaded because of "LoadScriptFile" toplevel call. +// nIncludeLineNumber is the number of the line in the parent file with $include statement or 0 in case of "LoadScriptFile" +typedef void ( * SCRIPT_LOADED_CALLBACK )( char const *pFilenameLoaded, char const *pIncludedFromFileName, int nIncludeLineNumber ); + +// SetScriptLoadedCallback: +// Sets the new callback for script loading. +// Returns the previous callback function. +SCRIPT_LOADED_CALLBACK SetScriptLoadedCallback( SCRIPT_LOADED_CALLBACK pfnNewScriptLoadedCallback ); + +#include "tier1/utlstring.h" +#include "tier1/utlvector.h" + +CUtlString SetSingleCharTokenList( const char *pszSingleCharTokenList ); + +class CUtlBuffer; + +enum DiskWriteMode_t +{ + WRITE_TO_DISK_NEVER, + WRITE_TO_DISK_ALWAYS, + WRITE_TO_DISK_UPDATE, // file must exist +}; + +struct fileList_t +{ + CUtlString fileName; + time_t timeWrite; +}; + +class IScriptLib +{ +public: + virtual bool ReadFileToBuffer( const char *pSourceName, CUtlBuffer &buffer, bool bText = false, bool bNoOpenFailureWarning = false ) = 0; + virtual bool WriteBufferToFile( const char *pTargetName, CUtlBuffer &buffer, DiskWriteMode_t writeMode ) = 0; + virtual int FindFiles( char* pFileMask, bool bRecurse, CUtlVector &fileList ) = 0; + virtual char *MakeTemporaryFilename( char const *pchModPath, char *pPath, int pathSize ) = 0; + virtual void DeleteTemporaryFiles( const char *pFileMask ) = 0; + virtual int CompareFileTime( const char *pFilenameA, const char *pFilenameB ) = 0; + virtual bool DoesFileExist( const char *pFilename ) = 0; +}; + +extern IScriptLib *scriptlib; + + +#endif // SCRIPLIB_H diff --git a/mp/src/utils/common/threads.cpp b/mp/src/utils/common/threads.cpp index 344943c9..74e457a9 100644 --- a/mp/src/utils/common/threads.cpp +++ b/mp/src/utils/common/threads.cpp @@ -1,257 +1,257 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// - -#define USED - -#include -#include "cmdlib.h" -#define NO_THREAD_NAMES -#include "threads.h" -#include "pacifier.h" - -#define MAX_THREADS 16 - - -class CRunThreadsData -{ -public: - int m_iThread; - void *m_pUserData; - RunThreadsFn m_Fn; -}; - -CRunThreadsData g_RunThreadsData[MAX_THREADS]; - - -int dispatch; -int workcount; -qboolean pacifier; - -qboolean threaded; -bool g_bLowPriorityThreads = false; - -HANDLE g_ThreadHandles[MAX_THREADS]; - - - -/* -============= -GetThreadWork - -============= -*/ -int GetThreadWork (void) -{ - int r; - - ThreadLock (); - - if (dispatch == workcount) - { - ThreadUnlock (); - return -1; - } - - UpdatePacifier( (float)dispatch / workcount ); - - r = dispatch; - dispatch++; - ThreadUnlock (); - - return r; -} - - -ThreadWorkerFn workfunction; - -void ThreadWorkerFunction( int iThread, void *pUserData ) -{ - int work; - - while (1) - { - work = GetThreadWork (); - if (work == -1) - break; - - workfunction( iThread, work ); - } -} - -void RunThreadsOnIndividual (int workcnt, qboolean showpacifier, ThreadWorkerFn func) -{ - if (numthreads == -1) - ThreadSetDefault (); - - workfunction = func; - RunThreadsOn (workcnt, showpacifier, ThreadWorkerFunction); -} - - -/* -=================================================================== - -WIN32 - -=================================================================== -*/ - -int numthreads = -1; -CRITICAL_SECTION crit; -static int enter; - - -class CCritInit -{ -public: - CCritInit() - { - InitializeCriticalSection (&crit); - } -} g_CritInit; - - - -void SetLowPriority() -{ - SetPriorityClass( GetCurrentProcess(), IDLE_PRIORITY_CLASS ); -} - - -void ThreadSetDefault (void) -{ - SYSTEM_INFO info; - - if (numthreads == -1) // not set manually - { - GetSystemInfo (&info); - numthreads = info.dwNumberOfProcessors; - if (numthreads < 1 || numthreads > 32) - numthreads = 1; - } - - Msg ("%i threads\n", numthreads); -} - - -void ThreadLock (void) -{ - if (!threaded) - return; - EnterCriticalSection (&crit); - if (enter) - Error ("Recursive ThreadLock\n"); - enter = 1; -} - -void ThreadUnlock (void) -{ - if (!threaded) - return; - if (!enter) - Error ("ThreadUnlock without lock\n"); - enter = 0; - LeaveCriticalSection (&crit); -} - - -// This runs in the thread and dispatches a RunThreadsFn call. -DWORD WINAPI InternalRunThreadsFn( LPVOID pParameter ) -{ - CRunThreadsData *pData = (CRunThreadsData*)pParameter; - pData->m_Fn( pData->m_iThread, pData->m_pUserData ); - return 0; -} - - -void RunThreads_Start( RunThreadsFn fn, void *pUserData, ERunThreadsPriority ePriority ) -{ - Assert( numthreads > 0 ); - threaded = true; - - if ( numthreads > MAX_TOOL_THREADS ) - numthreads = MAX_TOOL_THREADS; - - for ( int i=0; i < numthreads ;i++ ) - { - g_RunThreadsData[i].m_iThread = i; - g_RunThreadsData[i].m_pUserData = pUserData; - g_RunThreadsData[i].m_Fn = fn; - - DWORD dwDummy; - g_ThreadHandles[i] = CreateThread( - NULL, // LPSECURITY_ATTRIBUTES lpsa, - 0, // DWORD cbStack, - InternalRunThreadsFn, // LPTHREAD_START_ROUTINE lpStartAddr, - &g_RunThreadsData[i], // LPVOID lpvThreadParm, - 0, // DWORD fdwCreate, - &dwDummy ); - - if ( ePriority == k_eRunThreadsPriority_UseGlobalState ) - { - if( g_bLowPriorityThreads ) - SetThreadPriority( g_ThreadHandles[i], THREAD_PRIORITY_LOWEST ); - } - else if ( ePriority == k_eRunThreadsPriority_Idle ) - { - SetThreadPriority( g_ThreadHandles[i], THREAD_PRIORITY_IDLE ); - } - } -} - - -void RunThreads_End() -{ - WaitForMultipleObjects( numthreads, g_ThreadHandles, TRUE, INFINITE ); - for ( int i=0; i < numthreads; i++ ) - CloseHandle( g_ThreadHandles[i] ); - - threaded = false; -} - - -/* -============= -RunThreadsOn -============= -*/ -void RunThreadsOn( int workcnt, qboolean showpacifier, RunThreadsFn fn, void *pUserData ) -{ - int start, end; - - start = Plat_FloatTime(); - dispatch = 0; - workcount = workcnt; - StartPacifier(""); - pacifier = showpacifier; - -#ifdef _PROFILE - threaded = false; - (*func)( 0 ); - return; -#endif - - - RunThreads_Start( fn, pUserData ); - RunThreads_End(); - - - end = Plat_FloatTime(); - if (pacifier) - { - EndPacifier(false); - printf (" (%i)\n", end-start); - } -} - - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// + +#define USED + +#include +#include "cmdlib.h" +#define NO_THREAD_NAMES +#include "threads.h" +#include "pacifier.h" + +#define MAX_THREADS 16 + + +class CRunThreadsData +{ +public: + int m_iThread; + void *m_pUserData; + RunThreadsFn m_Fn; +}; + +CRunThreadsData g_RunThreadsData[MAX_THREADS]; + + +int dispatch; +int workcount; +qboolean pacifier; + +qboolean threaded; +bool g_bLowPriorityThreads = false; + +HANDLE g_ThreadHandles[MAX_THREADS]; + + + +/* +============= +GetThreadWork + +============= +*/ +int GetThreadWork (void) +{ + int r; + + ThreadLock (); + + if (dispatch == workcount) + { + ThreadUnlock (); + return -1; + } + + UpdatePacifier( (float)dispatch / workcount ); + + r = dispatch; + dispatch++; + ThreadUnlock (); + + return r; +} + + +ThreadWorkerFn workfunction; + +void ThreadWorkerFunction( int iThread, void *pUserData ) +{ + int work; + + while (1) + { + work = GetThreadWork (); + if (work == -1) + break; + + workfunction( iThread, work ); + } +} + +void RunThreadsOnIndividual (int workcnt, qboolean showpacifier, ThreadWorkerFn func) +{ + if (numthreads == -1) + ThreadSetDefault (); + + workfunction = func; + RunThreadsOn (workcnt, showpacifier, ThreadWorkerFunction); +} + + +/* +=================================================================== + +WIN32 + +=================================================================== +*/ + +int numthreads = -1; +CRITICAL_SECTION crit; +static int enter; + + +class CCritInit +{ +public: + CCritInit() + { + InitializeCriticalSection (&crit); + } +} g_CritInit; + + + +void SetLowPriority() +{ + SetPriorityClass( GetCurrentProcess(), IDLE_PRIORITY_CLASS ); +} + + +void ThreadSetDefault (void) +{ + SYSTEM_INFO info; + + if (numthreads == -1) // not set manually + { + GetSystemInfo (&info); + numthreads = info.dwNumberOfProcessors; + if (numthreads < 1 || numthreads > 32) + numthreads = 1; + } + + Msg ("%i threads\n", numthreads); +} + + +void ThreadLock (void) +{ + if (!threaded) + return; + EnterCriticalSection (&crit); + if (enter) + Error ("Recursive ThreadLock\n"); + enter = 1; +} + +void ThreadUnlock (void) +{ + if (!threaded) + return; + if (!enter) + Error ("ThreadUnlock without lock\n"); + enter = 0; + LeaveCriticalSection (&crit); +} + + +// This runs in the thread and dispatches a RunThreadsFn call. +DWORD WINAPI InternalRunThreadsFn( LPVOID pParameter ) +{ + CRunThreadsData *pData = (CRunThreadsData*)pParameter; + pData->m_Fn( pData->m_iThread, pData->m_pUserData ); + return 0; +} + + +void RunThreads_Start( RunThreadsFn fn, void *pUserData, ERunThreadsPriority ePriority ) +{ + Assert( numthreads > 0 ); + threaded = true; + + if ( numthreads > MAX_TOOL_THREADS ) + numthreads = MAX_TOOL_THREADS; + + for ( int i=0; i < numthreads ;i++ ) + { + g_RunThreadsData[i].m_iThread = i; + g_RunThreadsData[i].m_pUserData = pUserData; + g_RunThreadsData[i].m_Fn = fn; + + DWORD dwDummy; + g_ThreadHandles[i] = CreateThread( + NULL, // LPSECURITY_ATTRIBUTES lpsa, + 0, // DWORD cbStack, + InternalRunThreadsFn, // LPTHREAD_START_ROUTINE lpStartAddr, + &g_RunThreadsData[i], // LPVOID lpvThreadParm, + 0, // DWORD fdwCreate, + &dwDummy ); + + if ( ePriority == k_eRunThreadsPriority_UseGlobalState ) + { + if( g_bLowPriorityThreads ) + SetThreadPriority( g_ThreadHandles[i], THREAD_PRIORITY_LOWEST ); + } + else if ( ePriority == k_eRunThreadsPriority_Idle ) + { + SetThreadPriority( g_ThreadHandles[i], THREAD_PRIORITY_IDLE ); + } + } +} + + +void RunThreads_End() +{ + WaitForMultipleObjects( numthreads, g_ThreadHandles, TRUE, INFINITE ); + for ( int i=0; i < numthreads; i++ ) + CloseHandle( g_ThreadHandles[i] ); + + threaded = false; +} + + +/* +============= +RunThreadsOn +============= +*/ +void RunThreadsOn( int workcnt, qboolean showpacifier, RunThreadsFn fn, void *pUserData ) +{ + int start, end; + + start = Plat_FloatTime(); + dispatch = 0; + workcount = workcnt; + StartPacifier(""); + pacifier = showpacifier; + +#ifdef _PROFILE + threaded = false; + (*func)( 0 ); + return; +#endif + + + RunThreads_Start( fn, pUserData ); + RunThreads_End(); + + + end = Plat_FloatTime(); + if (pacifier) + { + EndPacifier(false); + printf (" (%i)\n", end-start); + } +} + + diff --git a/mp/src/utils/common/threads.h b/mp/src/utils/common/threads.h index e29e9aab..0908b67a 100644 --- a/mp/src/utils/common/threads.h +++ b/mp/src/utils/common/threads.h @@ -1,65 +1,65 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// - -#ifndef THREADS_H -#define THREADS_H -#pragma once - - -// Arrays that are indexed by thread should always be MAX_TOOL_THREADS+1 -// large so THREADINDEX_MAIN can be used from the main thread. -#define MAX_TOOL_THREADS 16 -#define THREADINDEX_MAIN (MAX_TOOL_THREADS) - - -extern int numthreads; - -// If set to true, then all the threads that are created are low priority. -extern bool g_bLowPriorityThreads; - -typedef void (*ThreadWorkerFn)( int iThread, int iWorkItem ); -typedef void (*RunThreadsFn)( int iThread, void *pUserData ); - - -enum ERunThreadsPriority -{ - k_eRunThreadsPriority_UseGlobalState=0, // Default.. uses g_bLowPriorityThreads to decide what to set the priority to. - k_eRunThreadsPriority_Normal, // Doesn't touch thread priorities. - k_eRunThreadsPriority_Idle // Sets threads to idle priority. -}; - - -// Put the process into an idle priority class so it doesn't hog the UI. -void SetLowPriority(); - -void ThreadSetDefault (void); -int GetThreadWork (void); - -void RunThreadsOnIndividual ( int workcnt, qboolean showpacifier, ThreadWorkerFn fn ); - -void RunThreadsOn ( int workcnt, qboolean showpacifier, RunThreadsFn fn, void *pUserData=NULL ); - -// This version doesn't track work items - it just runs your function and waits for it to finish. -void RunThreads_Start( RunThreadsFn fn, void *pUserData, ERunThreadsPriority ePriority=k_eRunThreadsPriority_UseGlobalState ); -void RunThreads_End(); - -void ThreadLock (void); -void ThreadUnlock (void); - - -#ifndef NO_THREAD_NAMES -#define RunThreadsOn(n,p,f) { if (p) printf("%-20s ", #f ":"); RunThreadsOn(n,p,f); } -#define RunThreadsOnIndividual(n,p,f) { if (p) printf("%-20s ", #f ":"); RunThreadsOnIndividual(n,p,f); } -#endif - -#endif // THREADS_H +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef THREADS_H +#define THREADS_H +#pragma once + + +// Arrays that are indexed by thread should always be MAX_TOOL_THREADS+1 +// large so THREADINDEX_MAIN can be used from the main thread. +#define MAX_TOOL_THREADS 16 +#define THREADINDEX_MAIN (MAX_TOOL_THREADS) + + +extern int numthreads; + +// If set to true, then all the threads that are created are low priority. +extern bool g_bLowPriorityThreads; + +typedef void (*ThreadWorkerFn)( int iThread, int iWorkItem ); +typedef void (*RunThreadsFn)( int iThread, void *pUserData ); + + +enum ERunThreadsPriority +{ + k_eRunThreadsPriority_UseGlobalState=0, // Default.. uses g_bLowPriorityThreads to decide what to set the priority to. + k_eRunThreadsPriority_Normal, // Doesn't touch thread priorities. + k_eRunThreadsPriority_Idle // Sets threads to idle priority. +}; + + +// Put the process into an idle priority class so it doesn't hog the UI. +void SetLowPriority(); + +void ThreadSetDefault (void); +int GetThreadWork (void); + +void RunThreadsOnIndividual ( int workcnt, qboolean showpacifier, ThreadWorkerFn fn ); + +void RunThreadsOn ( int workcnt, qboolean showpacifier, RunThreadsFn fn, void *pUserData=NULL ); + +// This version doesn't track work items - it just runs your function and waits for it to finish. +void RunThreads_Start( RunThreadsFn fn, void *pUserData, ERunThreadsPriority ePriority=k_eRunThreadsPriority_UseGlobalState ); +void RunThreads_End(); + +void ThreadLock (void); +void ThreadUnlock (void); + + +#ifndef NO_THREAD_NAMES +#define RunThreadsOn(n,p,f) { if (p) printf("%-20s ", #f ":"); RunThreadsOn(n,p,f); } +#define RunThreadsOnIndividual(n,p,f) { if (p) printf("%-20s ", #f ":"); RunThreadsOnIndividual(n,p,f); } +#endif + +#endif // THREADS_H diff --git a/mp/src/utils/common/tools_minidump.cpp b/mp/src/utils/common/tools_minidump.cpp index a0c84209..a7659200 100644 --- a/mp/src/utils/common/tools_minidump.cpp +++ b/mp/src/utils/common/tools_minidump.cpp @@ -1,61 +1,61 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include -#include -#include "tier0/minidump.h" -#include "tools_minidump.h" - -static bool g_bToolsWriteFullMinidumps = false; -static ToolsExceptionHandler g_pCustomExceptionHandler = NULL; - - -// --------------------------------------------------------------------------------- // -// Internal helpers. -// --------------------------------------------------------------------------------- // - -static LONG __stdcall ToolsExceptionFilter( struct _EXCEPTION_POINTERS *ExceptionInfo ) -{ - // Non VMPI workers write a minidump and show a crash dialog like normal. - int iType = MiniDumpNormal; - if ( g_bToolsWriteFullMinidumps ) - iType = MiniDumpWithDataSegs | MiniDumpWithIndirectlyReferencedMemory; - - WriteMiniDumpUsingExceptionInfo( ExceptionInfo->ExceptionRecord->ExceptionCode, ExceptionInfo, (MINIDUMP_TYPE)iType ); - return EXCEPTION_CONTINUE_SEARCH; -} - - -static LONG __stdcall ToolsExceptionFilter_Custom( struct _EXCEPTION_POINTERS *ExceptionInfo ) -{ - // Run their custom handler. - g_pCustomExceptionHandler( ExceptionInfo->ExceptionRecord->ExceptionCode, ExceptionInfo ); - return EXCEPTION_EXECUTE_HANDLER; // (never gets here anyway) -} - - -// --------------------------------------------------------------------------------- // -// Interface functions. -// --------------------------------------------------------------------------------- // - -void EnableFullMinidumps( bool bFull ) -{ - g_bToolsWriteFullMinidumps = bFull; -} - - -void SetupDefaultToolsMinidumpHandler() -{ - SetUnhandledExceptionFilter( ToolsExceptionFilter ); -} - - -void SetupToolsMinidumpHandler( ToolsExceptionHandler fn ) -{ - g_pCustomExceptionHandler = fn; - SetUnhandledExceptionFilter( ToolsExceptionFilter_Custom ); -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include +#include +#include "tier0/minidump.h" +#include "tools_minidump.h" + +static bool g_bToolsWriteFullMinidumps = false; +static ToolsExceptionHandler g_pCustomExceptionHandler = NULL; + + +// --------------------------------------------------------------------------------- // +// Internal helpers. +// --------------------------------------------------------------------------------- // + +static LONG __stdcall ToolsExceptionFilter( struct _EXCEPTION_POINTERS *ExceptionInfo ) +{ + // Non VMPI workers write a minidump and show a crash dialog like normal. + int iType = MiniDumpNormal; + if ( g_bToolsWriteFullMinidumps ) + iType = MiniDumpWithDataSegs | MiniDumpWithIndirectlyReferencedMemory; + + WriteMiniDumpUsingExceptionInfo( ExceptionInfo->ExceptionRecord->ExceptionCode, ExceptionInfo, (MINIDUMP_TYPE)iType ); + return EXCEPTION_CONTINUE_SEARCH; +} + + +static LONG __stdcall ToolsExceptionFilter_Custom( struct _EXCEPTION_POINTERS *ExceptionInfo ) +{ + // Run their custom handler. + g_pCustomExceptionHandler( ExceptionInfo->ExceptionRecord->ExceptionCode, ExceptionInfo ); + return EXCEPTION_EXECUTE_HANDLER; // (never gets here anyway) +} + + +// --------------------------------------------------------------------------------- // +// Interface functions. +// --------------------------------------------------------------------------------- // + +void EnableFullMinidumps( bool bFull ) +{ + g_bToolsWriteFullMinidumps = bFull; +} + + +void SetupDefaultToolsMinidumpHandler() +{ + SetUnhandledExceptionFilter( ToolsExceptionFilter ); +} + + +void SetupToolsMinidumpHandler( ToolsExceptionHandler fn ) +{ + g_pCustomExceptionHandler = fn; + SetUnhandledExceptionFilter( ToolsExceptionFilter_Custom ); +} diff --git a/mp/src/utils/common/tools_minidump.h b/mp/src/utils/common/tools_minidump.h index dfb44a9b..2e5f12cb 100644 --- a/mp/src/utils/common/tools_minidump.h +++ b/mp/src/utils/common/tools_minidump.h @@ -1,35 +1,35 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// - -#ifndef TOOLS_MINIDUMP_H -#define TOOLS_MINIDUMP_H -#ifdef _WIN32 -#pragma once -#endif - - - -// Defaults to false. If true, it'll write larger minidump files with the contents -// of global variables and following pointers from where the crash occurred. -void EnableFullMinidumps( bool bFull ); - - -// This handler catches any crash, writes a minidump, and runs the default system -// crash handler (which usually shows a dialog). -void SetupDefaultToolsMinidumpHandler(); - - -// (Used by VMPI) - you specify your own crash handler. -// Arguments passed to ToolsExceptionHandler -// exceptionCode - exception code -// pvExceptionInfo - on Win32 platform points to "struct _EXCEPTION_POINTERS" -// otherwise NULL -// -typedef void (*ToolsExceptionHandler)( unsigned long exceptionCode, void *pvExceptionInfo ); -void SetupToolsMinidumpHandler( ToolsExceptionHandler fn ); - - -#endif // MINIDUMP_H +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef TOOLS_MINIDUMP_H +#define TOOLS_MINIDUMP_H +#ifdef _WIN32 +#pragma once +#endif + + + +// Defaults to false. If true, it'll write larger minidump files with the contents +// of global variables and following pointers from where the crash occurred. +void EnableFullMinidumps( bool bFull ); + + +// This handler catches any crash, writes a minidump, and runs the default system +// crash handler (which usually shows a dialog). +void SetupDefaultToolsMinidumpHandler(); + + +// (Used by VMPI) - you specify your own crash handler. +// Arguments passed to ToolsExceptionHandler +// exceptionCode - exception code +// pvExceptionInfo - on Win32 platform points to "struct _EXCEPTION_POINTERS" +// otherwise NULL +// +typedef void (*ToolsExceptionHandler)( unsigned long exceptionCode, void *pvExceptionInfo ); +void SetupToolsMinidumpHandler( ToolsExceptionHandler fn ); + + +#endif // MINIDUMP_H diff --git a/mp/src/utils/common/utilmatlib.cpp b/mp/src/utils/common/utilmatlib.cpp index 962bb3f5..f2dc49fa 100644 --- a/mp/src/utils/common/utilmatlib.cpp +++ b/mp/src/utils/common/utilmatlib.cpp @@ -1,184 +1,184 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// $NoKeywords: $ -//=============================================================================// - -// C callable material system interface for the utils. - -#include "materialsystem/imaterialsystem.h" -#include "materialsystem/imaterial.h" -#include "materialsystem/imaterialvar.h" -#include -#include "utilmatlib.h" -#include "tier0/dbg.h" -#include -#include "filesystem.h" -#include "materialsystem/materialsystem_config.h" -#include "mathlib/Mathlib.h" - -void LoadMaterialSystemInterface( CreateInterfaceFn fileSystemFactory ) -{ - if( g_pMaterialSystem ) - return; - - // materialsystem.dll should be in the path, it's in bin along with vbsp. - const char *pDllName = "materialsystem.dll"; - CSysModule *materialSystemDLLHInst; - materialSystemDLLHInst = g_pFullFileSystem->LoadModule( pDllName ); - if( !materialSystemDLLHInst ) - { - Error( "Can't load MaterialSystem.dll\n" ); - } - - CreateInterfaceFn clientFactory = Sys_GetFactory( materialSystemDLLHInst ); - if ( clientFactory ) - { - g_pMaterialSystem = (IMaterialSystem *)clientFactory( MATERIAL_SYSTEM_INTERFACE_VERSION, NULL ); - if ( !g_pMaterialSystem ) - { - Error( "Could not get the material system interface from materialsystem.dll (" __FILE__ ")" ); - } - } - else - { - Error( "Could not find factory interface in library MaterialSystem.dll" ); - } - - if (!g_pMaterialSystem->Init( "shaderapiempty.dll", 0, fileSystemFactory )) - { - Error( "Could not start the empty shader (shaderapiempty.dll)!" ); - } -} - -void InitMaterialSystem( const char *materialBaseDirPath, CreateInterfaceFn fileSystemFactory ) -{ - LoadMaterialSystemInterface( fileSystemFactory ); - MaterialSystem_Config_t config; - g_pMaterialSystem->OverrideConfig( config, false ); -} - -void ShutdownMaterialSystem( ) -{ - if ( g_pMaterialSystem ) - { - g_pMaterialSystem->Shutdown(); - g_pMaterialSystem = NULL; - } -} - -MaterialSystemMaterial_t FindMaterial( const char *materialName, bool *pFound, bool bComplain ) -{ - IMaterial *pMat = g_pMaterialSystem->FindMaterial( materialName, TEXTURE_GROUP_OTHER, bComplain ); - MaterialSystemMaterial_t matHandle = pMat; - - if ( pFound ) - { - *pFound = true; - if ( IsErrorMaterial( pMat ) ) - *pFound = false; - } - - return matHandle; -} - -void GetMaterialDimensions( MaterialSystemMaterial_t materialHandle, int *width, int *height ) -{ - PreviewImageRetVal_t retVal; - ImageFormat dummyImageFormat; - IMaterial *material = ( IMaterial * )materialHandle; - bool translucent; - retVal = material->GetPreviewImageProperties( width, height, &dummyImageFormat, &translucent ); - if (retVal != MATERIAL_PREVIEW_IMAGE_OK ) - { -#if 0 - if (retVal == MATERIAL_PREVIEW_IMAGE_BAD ) - { - Error( "problem getting preview image for %s", - g_pMaterialSystem->GetMaterialName( materialInfo[matID].materialHandle ) ); - } -#else - *width = 128; - *height = 128; -#endif - } -} - -void GetMaterialReflectivity( MaterialSystemMaterial_t materialHandle, float *reflectivityVect ) -{ - IMaterial *material = ( IMaterial * )materialHandle; - const IMaterialVar *reflectivityVar; - - bool found; - reflectivityVar = material->FindVar( "$reflectivity", &found, false ); - if( !found ) - { - Vector tmp; - material->GetReflectivity( tmp ); - VectorCopy( tmp.Base(), reflectivityVect ); - } - else - { - reflectivityVar->GetVecValue( reflectivityVect, 3 ); - } -} - -int GetMaterialShaderPropertyBool( MaterialSystemMaterial_t materialHandle, int propID ) -{ - IMaterial *material = ( IMaterial * )materialHandle; - switch( propID ) - { - case UTILMATLIB_NEEDS_BUMPED_LIGHTMAPS: - return material->GetPropertyFlag( MATERIAL_PROPERTY_NEEDS_BUMPED_LIGHTMAPS ); - - case UTILMATLIB_NEEDS_LIGHTMAP: - return material->GetPropertyFlag( MATERIAL_PROPERTY_NEEDS_LIGHTMAP ); - - default: - Assert( 0 ); - return 0; - } -} - -int GetMaterialShaderPropertyInt( MaterialSystemMaterial_t materialHandle, int propID ) -{ - IMaterial *material = ( IMaterial * )materialHandle; - switch( propID ) - { - case UTILMATLIB_OPACITY: - if (material->IsTranslucent()) - return UTILMATLIB_TRANSLUCENT; - if (material->IsAlphaTested()) - return UTILMATLIB_ALPHATEST; - return UTILMATLIB_OPAQUE; - - default: - Assert( 0 ); - return 0; - } -} - -const char *GetMaterialVar( MaterialSystemMaterial_t materialHandle, const char *propertyName ) -{ - IMaterial *material = ( IMaterial * )materialHandle; - IMaterialVar *var; - bool found; - var = material->FindVar( propertyName, &found, false ); - if( found ) - { - return var->GetStringValue(); - } - else - { - return NULL; - } -} - -const char *GetMaterialShaderName( MaterialSystemMaterial_t materialHandle ) -{ - IMaterial *material = ( IMaterial * )materialHandle; - return material->GetShaderName(); -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//=============================================================================// + +// C callable material system interface for the utils. + +#include "materialsystem/imaterialsystem.h" +#include "materialsystem/imaterial.h" +#include "materialsystem/imaterialvar.h" +#include +#include "utilmatlib.h" +#include "tier0/dbg.h" +#include +#include "filesystem.h" +#include "materialsystem/materialsystem_config.h" +#include "mathlib/Mathlib.h" + +void LoadMaterialSystemInterface( CreateInterfaceFn fileSystemFactory ) +{ + if( g_pMaterialSystem ) + return; + + // materialsystem.dll should be in the path, it's in bin along with vbsp. + const char *pDllName = "materialsystem.dll"; + CSysModule *materialSystemDLLHInst; + materialSystemDLLHInst = g_pFullFileSystem->LoadModule( pDllName ); + if( !materialSystemDLLHInst ) + { + Error( "Can't load MaterialSystem.dll\n" ); + } + + CreateInterfaceFn clientFactory = Sys_GetFactory( materialSystemDLLHInst ); + if ( clientFactory ) + { + g_pMaterialSystem = (IMaterialSystem *)clientFactory( MATERIAL_SYSTEM_INTERFACE_VERSION, NULL ); + if ( !g_pMaterialSystem ) + { + Error( "Could not get the material system interface from materialsystem.dll (" __FILE__ ")" ); + } + } + else + { + Error( "Could not find factory interface in library MaterialSystem.dll" ); + } + + if (!g_pMaterialSystem->Init( "shaderapiempty.dll", 0, fileSystemFactory )) + { + Error( "Could not start the empty shader (shaderapiempty.dll)!" ); + } +} + +void InitMaterialSystem( const char *materialBaseDirPath, CreateInterfaceFn fileSystemFactory ) +{ + LoadMaterialSystemInterface( fileSystemFactory ); + MaterialSystem_Config_t config; + g_pMaterialSystem->OverrideConfig( config, false ); +} + +void ShutdownMaterialSystem( ) +{ + if ( g_pMaterialSystem ) + { + g_pMaterialSystem->Shutdown(); + g_pMaterialSystem = NULL; + } +} + +MaterialSystemMaterial_t FindMaterial( const char *materialName, bool *pFound, bool bComplain ) +{ + IMaterial *pMat = g_pMaterialSystem->FindMaterial( materialName, TEXTURE_GROUP_OTHER, bComplain ); + MaterialSystemMaterial_t matHandle = pMat; + + if ( pFound ) + { + *pFound = true; + if ( IsErrorMaterial( pMat ) ) + *pFound = false; + } + + return matHandle; +} + +void GetMaterialDimensions( MaterialSystemMaterial_t materialHandle, int *width, int *height ) +{ + PreviewImageRetVal_t retVal; + ImageFormat dummyImageFormat; + IMaterial *material = ( IMaterial * )materialHandle; + bool translucent; + retVal = material->GetPreviewImageProperties( width, height, &dummyImageFormat, &translucent ); + if (retVal != MATERIAL_PREVIEW_IMAGE_OK ) + { +#if 0 + if (retVal == MATERIAL_PREVIEW_IMAGE_BAD ) + { + Error( "problem getting preview image for %s", + g_pMaterialSystem->GetMaterialName( materialInfo[matID].materialHandle ) ); + } +#else + *width = 128; + *height = 128; +#endif + } +} + +void GetMaterialReflectivity( MaterialSystemMaterial_t materialHandle, float *reflectivityVect ) +{ + IMaterial *material = ( IMaterial * )materialHandle; + const IMaterialVar *reflectivityVar; + + bool found; + reflectivityVar = material->FindVar( "$reflectivity", &found, false ); + if( !found ) + { + Vector tmp; + material->GetReflectivity( tmp ); + VectorCopy( tmp.Base(), reflectivityVect ); + } + else + { + reflectivityVar->GetVecValue( reflectivityVect, 3 ); + } +} + +int GetMaterialShaderPropertyBool( MaterialSystemMaterial_t materialHandle, int propID ) +{ + IMaterial *material = ( IMaterial * )materialHandle; + switch( propID ) + { + case UTILMATLIB_NEEDS_BUMPED_LIGHTMAPS: + return material->GetPropertyFlag( MATERIAL_PROPERTY_NEEDS_BUMPED_LIGHTMAPS ); + + case UTILMATLIB_NEEDS_LIGHTMAP: + return material->GetPropertyFlag( MATERIAL_PROPERTY_NEEDS_LIGHTMAP ); + + default: + Assert( 0 ); + return 0; + } +} + +int GetMaterialShaderPropertyInt( MaterialSystemMaterial_t materialHandle, int propID ) +{ + IMaterial *material = ( IMaterial * )materialHandle; + switch( propID ) + { + case UTILMATLIB_OPACITY: + if (material->IsTranslucent()) + return UTILMATLIB_TRANSLUCENT; + if (material->IsAlphaTested()) + return UTILMATLIB_ALPHATEST; + return UTILMATLIB_OPAQUE; + + default: + Assert( 0 ); + return 0; + } +} + +const char *GetMaterialVar( MaterialSystemMaterial_t materialHandle, const char *propertyName ) +{ + IMaterial *material = ( IMaterial * )materialHandle; + IMaterialVar *var; + bool found; + var = material->FindVar( propertyName, &found, false ); + if( found ) + { + return var->GetStringValue(); + } + else + { + return NULL; + } +} + +const char *GetMaterialShaderName( MaterialSystemMaterial_t materialHandle ) +{ + IMaterial *material = ( IMaterial * )materialHandle; + return material->GetShaderName(); +} diff --git a/mp/src/utils/common/utilmatlib.h b/mp/src/utils/common/utilmatlib.h index f73a73d0..9d2e0b57 100644 --- a/mp/src/utils/common/utilmatlib.h +++ b/mp/src/utils/common/utilmatlib.h @@ -1,41 +1,41 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// $NoKeywords: $ -//=============================================================================// - -#ifndef UTILMATLIB_H -#define UTILMATLIB_H - -#ifdef _WIN32 -#pragma once -#endif - -#define MATERIAL_NOT_FOUND NULL - -class IMaterialSystem; -extern IMaterialSystem *g_pMaterialSystem; - -typedef void *MaterialSystemMaterial_t; - -#define UTILMATLIB_NEEDS_BUMPED_LIGHTMAPS 0 -#define UTILMATLIB_NEEDS_LIGHTMAP 1 -#define UTILMATLIB_OPACITY 2 - -enum { UTILMATLIB_ALPHATEST = 0, UTILMATLIB_OPAQUE, UTILMATLIB_TRANSLUCENT }; - -void InitMaterialSystem( const char *materialBaseDirPath, CreateInterfaceFn fileSystemFactory ); -void ShutdownMaterialSystem( ); -MaterialSystemMaterial_t FindMaterial( const char *materialName, bool *pFound, bool bComplain = true ); -void GetMaterialDimensions( MaterialSystemMaterial_t materialHandle, int *width, int *height ); -int GetMaterialShaderPropertyBool( MaterialSystemMaterial_t materialHandle, int propID ); -int GetMaterialShaderPropertyInt( MaterialSystemMaterial_t materialHandle, int propID ); -const char *GetMaterialVar( MaterialSystemMaterial_t materialHandle, const char *propertyName ); -void GetMaterialReflectivity( MaterialSystemMaterial_t materialHandle, float *reflectivityVect ); -const char *GetMaterialShaderName( MaterialSystemMaterial_t materialHandle ); - - -#endif // UTILMATLIB_H +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//=============================================================================// + +#ifndef UTILMATLIB_H +#define UTILMATLIB_H + +#ifdef _WIN32 +#pragma once +#endif + +#define MATERIAL_NOT_FOUND NULL + +class IMaterialSystem; +extern IMaterialSystem *g_pMaterialSystem; + +typedef void *MaterialSystemMaterial_t; + +#define UTILMATLIB_NEEDS_BUMPED_LIGHTMAPS 0 +#define UTILMATLIB_NEEDS_LIGHTMAP 1 +#define UTILMATLIB_OPACITY 2 + +enum { UTILMATLIB_ALPHATEST = 0, UTILMATLIB_OPAQUE, UTILMATLIB_TRANSLUCENT }; + +void InitMaterialSystem( const char *materialBaseDirPath, CreateInterfaceFn fileSystemFactory ); +void ShutdownMaterialSystem( ); +MaterialSystemMaterial_t FindMaterial( const char *materialName, bool *pFound, bool bComplain = true ); +void GetMaterialDimensions( MaterialSystemMaterial_t materialHandle, int *width, int *height ); +int GetMaterialShaderPropertyBool( MaterialSystemMaterial_t materialHandle, int propID ); +int GetMaterialShaderPropertyInt( MaterialSystemMaterial_t materialHandle, int propID ); +const char *GetMaterialVar( MaterialSystemMaterial_t materialHandle, const char *propertyName ); +void GetMaterialReflectivity( MaterialSystemMaterial_t materialHandle, float *reflectivityVect ); +const char *GetMaterialShaderName( MaterialSystemMaterial_t materialHandle ); + + +#endif // UTILMATLIB_H diff --git a/mp/src/utils/common/vmpi_tools_shared.cpp b/mp/src/utils/common/vmpi_tools_shared.cpp index c753ce11..247569f4 100644 --- a/mp/src/utils/common/vmpi_tools_shared.cpp +++ b/mp/src/utils/common/vmpi_tools_shared.cpp @@ -1,374 +1,374 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// - -#include -#include -#include "vmpi.h" -#include "cmdlib.h" -#include "vmpi_tools_shared.h" -#include "tier1/strtools.h" -#include "mpi_stats.h" -#include "iphelpers.h" -#include "tier0/minidump.h" - - -// ----------------------------------------------------------------------------- // -// Globals. -// ----------------------------------------------------------------------------- // - -static bool g_bReceivedDirectoryInfo = false; // Have we gotten the qdir info yet? - -static bool g_bReceivedDBInfo = false; -static CDBInfo g_DBInfo; -static unsigned long g_JobPrimaryID; - -static int g_nDisconnects = 0; // Tracks how many remote processes have disconnected ungracefully. - - -// ----------------------------------------------------------------------------- // -// Shared dispatch code. -// ----------------------------------------------------------------------------- // - -bool SharedDispatch( MessageBuffer *pBuf, int iSource, int iPacketID ) -{ - char *pInPos = &pBuf->data[2]; - - switch ( pBuf->data[1] ) - { - case VMPI_SUBPACKETID_DIRECTORIES: - { - Q_strncpy( gamedir, pInPos, sizeof( gamedir ) ); - pInPos += strlen( pInPos ) + 1; - - Q_strncpy( qdir, pInPos, sizeof( qdir ) ); - - g_bReceivedDirectoryInfo = true; - } - return true; - - case VMPI_SUBPACKETID_DBINFO: - { - g_DBInfo = *((CDBInfo*)pInPos); - pInPos += sizeof( CDBInfo ); - g_JobPrimaryID = *((unsigned long*)pInPos); - - g_bReceivedDBInfo = true; - } - return true; - - case VMPI_SUBPACKETID_CRASH: - { - char const chCrashInfoType = *pInPos; - pInPos += 2; - switch ( chCrashInfoType ) - { - case 't': - Warning( "\nWorker '%s' dead: %s\n", VMPI_GetMachineName( iSource ), pInPos ); - break; - case 'f': - { - int iFileSize = * reinterpret_cast< int const * >( pInPos ); - pInPos += sizeof( iFileSize ); - - // Temp folder - char const *szFolder = NULL; - if ( !szFolder ) szFolder = getenv( "TEMP" ); - if ( !szFolder ) szFolder = getenv( "TMP" ); - if ( !szFolder ) szFolder = "c:"; - - // Base module name - char chModuleName[_MAX_PATH], *pModuleName = chModuleName; - ::GetModuleFileName( NULL, chModuleName, sizeof( chModuleName ) / sizeof( chModuleName[0] ) ); - - if ( char *pch = strrchr( chModuleName, '.' ) ) - *pch = 0; - if ( char *pch = strrchr( chModuleName, '\\' ) ) - *pch = 0, pModuleName = pch + 1; - - // Current time - time_t currTime = ::time( NULL ); - struct tm * pTime = ::localtime( &currTime ); - - // Number of minidumps this run - static int s_numMiniDumps = 0; - ++ s_numMiniDumps; - - // Prepare the filename - char chSaveFileName[ 2 * _MAX_PATH ] = { 0 }; - sprintf( chSaveFileName, "%s\\vmpi_%s_on_%s_%d%.2d%2d%.2d%.2d%.2d_%d.mdmp", - szFolder, - pModuleName, - VMPI_GetMachineName( iSource ), - pTime->tm_year + 1900, /* Year less 2000 */ - pTime->tm_mon + 1, /* month (0 - 11 : 0 = January) */ - pTime->tm_mday, /* day of month (1 - 31) */ - pTime->tm_hour, /* hour (0 - 23) */ - pTime->tm_min, /* minutes (0 - 59) */ - pTime->tm_sec, /* seconds (0 - 59) */ - s_numMiniDumps - ); - - if ( FILE *fDump = fopen( chSaveFileName, "wb" ) ) - { - fwrite( pInPos, 1, iFileSize, fDump ); - fclose( fDump ); - - Warning( "\nSaved worker crash minidump '%s', size %d byte(s).\n", - chSaveFileName, iFileSize ); - } - else - { - Warning( "\nReceived worker crash minidump size %d byte(s), failed to save.\n", iFileSize ); - } - } - break; - } - } - return true; - } - - return false; -} - -CDispatchReg g_SharedDispatchReg( VMPI_SHARED_PACKET_ID, SharedDispatch ); - - - -// ----------------------------------------------------------------------------- // -// Module interfaces. -// ----------------------------------------------------------------------------- // - -void SendQDirInfo() -{ - char cPacketID[2] = { VMPI_SHARED_PACKET_ID, VMPI_SUBPACKETID_DIRECTORIES }; - - MessageBuffer mb; - mb.write( cPacketID, 2 ); - mb.write( gamedir, strlen( gamedir ) + 1 ); - mb.write( qdir, strlen( qdir ) + 1 ); - - VMPI_SendData( mb.data, mb.getLen(), VMPI_PERSISTENT ); -} - - -void RecvQDirInfo() -{ - while ( !g_bReceivedDirectoryInfo ) - VMPI_DispatchNextMessage(); -} - - -void SendDBInfo( const CDBInfo *pInfo, unsigned long jobPrimaryID ) -{ - char cPacketInfo[2] = { VMPI_SHARED_PACKET_ID, VMPI_SUBPACKETID_DBINFO }; - const void *pChunks[] = { cPacketInfo, pInfo, &jobPrimaryID }; - int chunkLengths[] = { 2, sizeof( CDBInfo ), sizeof( jobPrimaryID ) }; - - VMPI_SendChunks( pChunks, chunkLengths, ARRAYSIZE( pChunks ), VMPI_PERSISTENT ); -} - - -void RecvDBInfo( CDBInfo *pInfo, unsigned long *pJobPrimaryID ) -{ - while ( !g_bReceivedDBInfo ) - VMPI_DispatchNextMessage(); - - *pInfo = g_DBInfo; - *pJobPrimaryID = g_JobPrimaryID; -} - -// If the file is successfully opened, read and sent returns the size of the file in bytes -// otherwise returns 0 and nothing is sent -int VMPI_SendFileChunk( const void *pvChunkPrefix, int lenPrefix, tchar const *ptchFileName ) -{ - HANDLE hFile = NULL; - HANDLE hMapping = NULL; - void const *pvMappedData = NULL; - int iResult = 0; - - hFile = ::CreateFile( ptchFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); - if ( !hFile || ( hFile == INVALID_HANDLE_VALUE ) ) - goto done; - - hMapping = ::CreateFileMapping( hFile, NULL, PAGE_READONLY, 0, 0, NULL ); - if ( !hMapping || ( hMapping == INVALID_HANDLE_VALUE ) ) - goto done; - - pvMappedData = ::MapViewOfFile( hMapping, FILE_MAP_READ, 0, 0, 0 ); - if ( !pvMappedData ) - goto done; - - int iMappedFileSize = ::GetFileSize( hFile, NULL ); - if ( INVALID_FILE_SIZE == iMappedFileSize ) - goto done; - - // Send the data over VMPI - if ( VMPI_Send3Chunks( - pvChunkPrefix, lenPrefix, - &iMappedFileSize, sizeof( iMappedFileSize ), - pvMappedData, iMappedFileSize, - VMPI_MASTER_ID ) ) - iResult = iMappedFileSize; - - // Fall-through for cleanup code to execute -done: - if ( pvMappedData ) - ::UnmapViewOfFile( pvMappedData ); - - if ( hMapping && ( hMapping != INVALID_HANDLE_VALUE ) ) - ::CloseHandle( hMapping ); - - if ( hFile && ( hFile != INVALID_HANDLE_VALUE ) ) - ::CloseHandle( hFile ); - - return iResult; -} - -void VMPI_HandleCrash( const char *pMessage, void *pvExceptionInfo, bool bAssert ) -{ - static LONG crashHandlerCount = 0; - if ( InterlockedIncrement( &crashHandlerCount ) == 1 ) - { - Msg( "\nFAILURE: '%s' (assert: %d)\n", pMessage, bAssert ); - - // Send a message to the master. - char crashMsg[4] = { VMPI_SHARED_PACKET_ID, VMPI_SUBPACKETID_CRASH, 't', ':' }; - - VMPI_Send2Chunks( - crashMsg, - sizeof( crashMsg ), - pMessage, - strlen( pMessage ) + 1, - VMPI_MASTER_ID ); - - // Now attempt to create a minidump with the given exception information - if ( pvExceptionInfo ) - { - struct _EXCEPTION_POINTERS *pvExPointers = ( struct _EXCEPTION_POINTERS * ) pvExceptionInfo; - tchar tchMinidumpFileName[_MAX_PATH] = { 0 }; - bool bSucceededWritingMinidump = WriteMiniDumpUsingExceptionInfo( - pvExPointers->ExceptionRecord->ExceptionCode, - pvExPointers, - ( MINIDUMP_TYPE )( MiniDumpWithDataSegs | MiniDumpWithIndirectlyReferencedMemory | MiniDumpWithProcessThreadData ), - // ( MINIDUMP_TYPE )( MiniDumpWithDataSegs | MiniDumpWithFullMemory | MiniDumpWithHandleData | MiniDumpWithUnloadedModules | MiniDumpWithIndirectlyReferencedMemory | MiniDumpWithProcessThreadData | MiniDumpWithPrivateReadWriteMemory ), - // ( MINIDUMP_TYPE )( MiniDumpNormal ), - tchMinidumpFileName ); - if ( bSucceededWritingMinidump ) - { - crashMsg[2] = 'f'; - VMPI_SendFileChunk( crashMsg, sizeof( crashMsg ), tchMinidumpFileName ); - ::DeleteFile( tchMinidumpFileName ); - } - } - - // Let the messages go out. - Sleep( 500 ); - } - - InterlockedDecrement( &crashHandlerCount ); -} - - -// This is called if we crash inside our crash handler. It just terminates the process immediately. -LONG __stdcall VMPI_SecondExceptionFilter( struct _EXCEPTION_POINTERS *ExceptionInfo ) -{ - TerminateProcess( GetCurrentProcess(), 2 ); - return EXCEPTION_EXECUTE_HANDLER; // (never gets here anyway) -} - - -void VMPI_ExceptionFilter( unsigned long uCode, void *pvExceptionInfo ) -{ - // This is called if we crash inside our crash handler. It just terminates the process immediately. - SetUnhandledExceptionFilter( VMPI_SecondExceptionFilter ); - - //DWORD code = ExceptionInfo->ExceptionRecord->ExceptionCode; - - #define ERR_RECORD( name ) { name, #name } - struct - { - int code; - char *pReason; - } errors[] = - { - ERR_RECORD( EXCEPTION_ACCESS_VIOLATION ), - ERR_RECORD( EXCEPTION_ARRAY_BOUNDS_EXCEEDED ), - ERR_RECORD( EXCEPTION_BREAKPOINT ), - ERR_RECORD( EXCEPTION_DATATYPE_MISALIGNMENT ), - ERR_RECORD( EXCEPTION_FLT_DENORMAL_OPERAND ), - ERR_RECORD( EXCEPTION_FLT_DIVIDE_BY_ZERO ), - ERR_RECORD( EXCEPTION_FLT_INEXACT_RESULT ), - ERR_RECORD( EXCEPTION_FLT_INVALID_OPERATION ), - ERR_RECORD( EXCEPTION_FLT_OVERFLOW ), - ERR_RECORD( EXCEPTION_FLT_STACK_CHECK ), - ERR_RECORD( EXCEPTION_FLT_UNDERFLOW ), - ERR_RECORD( EXCEPTION_ILLEGAL_INSTRUCTION ), - ERR_RECORD( EXCEPTION_IN_PAGE_ERROR ), - ERR_RECORD( EXCEPTION_INT_DIVIDE_BY_ZERO ), - ERR_RECORD( EXCEPTION_INT_OVERFLOW ), - ERR_RECORD( EXCEPTION_INVALID_DISPOSITION ), - ERR_RECORD( EXCEPTION_NONCONTINUABLE_EXCEPTION ), - ERR_RECORD( EXCEPTION_PRIV_INSTRUCTION ), - ERR_RECORD( EXCEPTION_SINGLE_STEP ), - ERR_RECORD( EXCEPTION_STACK_OVERFLOW ), - ERR_RECORD( EXCEPTION_ACCESS_VIOLATION ), - }; - - int nErrors = sizeof( errors ) / sizeof( errors[0] ); - int i=0; - char *pchReason = NULL; - char chUnknownBuffer[32]; - for ( i; ( i < nErrors ) && !pchReason; i++ ) - { - if ( errors[i].code == uCode ) - pchReason = errors[i].pReason; - } - - if ( i == nErrors ) - { - sprintf( chUnknownBuffer, "Error code 0x%08X", uCode ); - pchReason = chUnknownBuffer; - } - - VMPI_HandleCrash( pchReason, pvExceptionInfo, true ); - - TerminateProcess( GetCurrentProcess(), 1 ); -} - - -void HandleMPIDisconnect( int procID, const char *pReason ) -{ - int nLiveWorkers = VMPI_GetCurrentNumberOfConnections() - g_nDisconnects - 1; - - // We ran into the size limit before and it wasn't readily apparent that the size limit had - // been breached, so make sure to show errors about invalid packet sizes.. - bool bOldSuppress = g_bSuppressPrintfOutput; - g_bSuppressPrintfOutput = ( Q_stristr( pReason, "invalid packet size" ) == 0 ); - - Warning( "\n\n--- WARNING: lost connection to '%s' (%s).\n", VMPI_GetMachineName( procID ), pReason ); - - if ( g_bMPIMaster ) - { - Warning( "%d workers remain.\n\n", nLiveWorkers ); - - ++g_nDisconnects; - /* - if ( VMPI_GetCurrentNumberOfConnections() - g_nDisconnects <= 1 ) - { - Error( "All machines disconnected!" ); - } - */ - } - else - { - VMPI_HandleAutoRestart(); - Error( "Worker quitting." ); - } - - g_bSuppressPrintfOutput = bOldSuppress; -} - - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include +#include +#include "vmpi.h" +#include "cmdlib.h" +#include "vmpi_tools_shared.h" +#include "tier1/strtools.h" +#include "mpi_stats.h" +#include "iphelpers.h" +#include "tier0/minidump.h" + + +// ----------------------------------------------------------------------------- // +// Globals. +// ----------------------------------------------------------------------------- // + +static bool g_bReceivedDirectoryInfo = false; // Have we gotten the qdir info yet? + +static bool g_bReceivedDBInfo = false; +static CDBInfo g_DBInfo; +static unsigned long g_JobPrimaryID; + +static int g_nDisconnects = 0; // Tracks how many remote processes have disconnected ungracefully. + + +// ----------------------------------------------------------------------------- // +// Shared dispatch code. +// ----------------------------------------------------------------------------- // + +bool SharedDispatch( MessageBuffer *pBuf, int iSource, int iPacketID ) +{ + char *pInPos = &pBuf->data[2]; + + switch ( pBuf->data[1] ) + { + case VMPI_SUBPACKETID_DIRECTORIES: + { + Q_strncpy( gamedir, pInPos, sizeof( gamedir ) ); + pInPos += strlen( pInPos ) + 1; + + Q_strncpy( qdir, pInPos, sizeof( qdir ) ); + + g_bReceivedDirectoryInfo = true; + } + return true; + + case VMPI_SUBPACKETID_DBINFO: + { + g_DBInfo = *((CDBInfo*)pInPos); + pInPos += sizeof( CDBInfo ); + g_JobPrimaryID = *((unsigned long*)pInPos); + + g_bReceivedDBInfo = true; + } + return true; + + case VMPI_SUBPACKETID_CRASH: + { + char const chCrashInfoType = *pInPos; + pInPos += 2; + switch ( chCrashInfoType ) + { + case 't': + Warning( "\nWorker '%s' dead: %s\n", VMPI_GetMachineName( iSource ), pInPos ); + break; + case 'f': + { + int iFileSize = * reinterpret_cast< int const * >( pInPos ); + pInPos += sizeof( iFileSize ); + + // Temp folder + char const *szFolder = NULL; + if ( !szFolder ) szFolder = getenv( "TEMP" ); + if ( !szFolder ) szFolder = getenv( "TMP" ); + if ( !szFolder ) szFolder = "c:"; + + // Base module name + char chModuleName[_MAX_PATH], *pModuleName = chModuleName; + ::GetModuleFileName( NULL, chModuleName, sizeof( chModuleName ) / sizeof( chModuleName[0] ) ); + + if ( char *pch = strrchr( chModuleName, '.' ) ) + *pch = 0; + if ( char *pch = strrchr( chModuleName, '\\' ) ) + *pch = 0, pModuleName = pch + 1; + + // Current time + time_t currTime = ::time( NULL ); + struct tm * pTime = ::localtime( &currTime ); + + // Number of minidumps this run + static int s_numMiniDumps = 0; + ++ s_numMiniDumps; + + // Prepare the filename + char chSaveFileName[ 2 * _MAX_PATH ] = { 0 }; + sprintf( chSaveFileName, "%s\\vmpi_%s_on_%s_%d%.2d%2d%.2d%.2d%.2d_%d.mdmp", + szFolder, + pModuleName, + VMPI_GetMachineName( iSource ), + pTime->tm_year + 1900, /* Year less 2000 */ + pTime->tm_mon + 1, /* month (0 - 11 : 0 = January) */ + pTime->tm_mday, /* day of month (1 - 31) */ + pTime->tm_hour, /* hour (0 - 23) */ + pTime->tm_min, /* minutes (0 - 59) */ + pTime->tm_sec, /* seconds (0 - 59) */ + s_numMiniDumps + ); + + if ( FILE *fDump = fopen( chSaveFileName, "wb" ) ) + { + fwrite( pInPos, 1, iFileSize, fDump ); + fclose( fDump ); + + Warning( "\nSaved worker crash minidump '%s', size %d byte(s).\n", + chSaveFileName, iFileSize ); + } + else + { + Warning( "\nReceived worker crash minidump size %d byte(s), failed to save.\n", iFileSize ); + } + } + break; + } + } + return true; + } + + return false; +} + +CDispatchReg g_SharedDispatchReg( VMPI_SHARED_PACKET_ID, SharedDispatch ); + + + +// ----------------------------------------------------------------------------- // +// Module interfaces. +// ----------------------------------------------------------------------------- // + +void SendQDirInfo() +{ + char cPacketID[2] = { VMPI_SHARED_PACKET_ID, VMPI_SUBPACKETID_DIRECTORIES }; + + MessageBuffer mb; + mb.write( cPacketID, 2 ); + mb.write( gamedir, strlen( gamedir ) + 1 ); + mb.write( qdir, strlen( qdir ) + 1 ); + + VMPI_SendData( mb.data, mb.getLen(), VMPI_PERSISTENT ); +} + + +void RecvQDirInfo() +{ + while ( !g_bReceivedDirectoryInfo ) + VMPI_DispatchNextMessage(); +} + + +void SendDBInfo( const CDBInfo *pInfo, unsigned long jobPrimaryID ) +{ + char cPacketInfo[2] = { VMPI_SHARED_PACKET_ID, VMPI_SUBPACKETID_DBINFO }; + const void *pChunks[] = { cPacketInfo, pInfo, &jobPrimaryID }; + int chunkLengths[] = { 2, sizeof( CDBInfo ), sizeof( jobPrimaryID ) }; + + VMPI_SendChunks( pChunks, chunkLengths, ARRAYSIZE( pChunks ), VMPI_PERSISTENT ); +} + + +void RecvDBInfo( CDBInfo *pInfo, unsigned long *pJobPrimaryID ) +{ + while ( !g_bReceivedDBInfo ) + VMPI_DispatchNextMessage(); + + *pInfo = g_DBInfo; + *pJobPrimaryID = g_JobPrimaryID; +} + +// If the file is successfully opened, read and sent returns the size of the file in bytes +// otherwise returns 0 and nothing is sent +int VMPI_SendFileChunk( const void *pvChunkPrefix, int lenPrefix, tchar const *ptchFileName ) +{ + HANDLE hFile = NULL; + HANDLE hMapping = NULL; + void const *pvMappedData = NULL; + int iResult = 0; + + hFile = ::CreateFile( ptchFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); + if ( !hFile || ( hFile == INVALID_HANDLE_VALUE ) ) + goto done; + + hMapping = ::CreateFileMapping( hFile, NULL, PAGE_READONLY, 0, 0, NULL ); + if ( !hMapping || ( hMapping == INVALID_HANDLE_VALUE ) ) + goto done; + + pvMappedData = ::MapViewOfFile( hMapping, FILE_MAP_READ, 0, 0, 0 ); + if ( !pvMappedData ) + goto done; + + int iMappedFileSize = ::GetFileSize( hFile, NULL ); + if ( INVALID_FILE_SIZE == iMappedFileSize ) + goto done; + + // Send the data over VMPI + if ( VMPI_Send3Chunks( + pvChunkPrefix, lenPrefix, + &iMappedFileSize, sizeof( iMappedFileSize ), + pvMappedData, iMappedFileSize, + VMPI_MASTER_ID ) ) + iResult = iMappedFileSize; + + // Fall-through for cleanup code to execute +done: + if ( pvMappedData ) + ::UnmapViewOfFile( pvMappedData ); + + if ( hMapping && ( hMapping != INVALID_HANDLE_VALUE ) ) + ::CloseHandle( hMapping ); + + if ( hFile && ( hFile != INVALID_HANDLE_VALUE ) ) + ::CloseHandle( hFile ); + + return iResult; +} + +void VMPI_HandleCrash( const char *pMessage, void *pvExceptionInfo, bool bAssert ) +{ + static LONG crashHandlerCount = 0; + if ( InterlockedIncrement( &crashHandlerCount ) == 1 ) + { + Msg( "\nFAILURE: '%s' (assert: %d)\n", pMessage, bAssert ); + + // Send a message to the master. + char crashMsg[4] = { VMPI_SHARED_PACKET_ID, VMPI_SUBPACKETID_CRASH, 't', ':' }; + + VMPI_Send2Chunks( + crashMsg, + sizeof( crashMsg ), + pMessage, + strlen( pMessage ) + 1, + VMPI_MASTER_ID ); + + // Now attempt to create a minidump with the given exception information + if ( pvExceptionInfo ) + { + struct _EXCEPTION_POINTERS *pvExPointers = ( struct _EXCEPTION_POINTERS * ) pvExceptionInfo; + tchar tchMinidumpFileName[_MAX_PATH] = { 0 }; + bool bSucceededWritingMinidump = WriteMiniDumpUsingExceptionInfo( + pvExPointers->ExceptionRecord->ExceptionCode, + pvExPointers, + ( MINIDUMP_TYPE )( MiniDumpWithDataSegs | MiniDumpWithIndirectlyReferencedMemory | MiniDumpWithProcessThreadData ), + // ( MINIDUMP_TYPE )( MiniDumpWithDataSegs | MiniDumpWithFullMemory | MiniDumpWithHandleData | MiniDumpWithUnloadedModules | MiniDumpWithIndirectlyReferencedMemory | MiniDumpWithProcessThreadData | MiniDumpWithPrivateReadWriteMemory ), + // ( MINIDUMP_TYPE )( MiniDumpNormal ), + tchMinidumpFileName ); + if ( bSucceededWritingMinidump ) + { + crashMsg[2] = 'f'; + VMPI_SendFileChunk( crashMsg, sizeof( crashMsg ), tchMinidumpFileName ); + ::DeleteFile( tchMinidumpFileName ); + } + } + + // Let the messages go out. + Sleep( 500 ); + } + + InterlockedDecrement( &crashHandlerCount ); +} + + +// This is called if we crash inside our crash handler. It just terminates the process immediately. +LONG __stdcall VMPI_SecondExceptionFilter( struct _EXCEPTION_POINTERS *ExceptionInfo ) +{ + TerminateProcess( GetCurrentProcess(), 2 ); + return EXCEPTION_EXECUTE_HANDLER; // (never gets here anyway) +} + + +void VMPI_ExceptionFilter( unsigned long uCode, void *pvExceptionInfo ) +{ + // This is called if we crash inside our crash handler. It just terminates the process immediately. + SetUnhandledExceptionFilter( VMPI_SecondExceptionFilter ); + + //DWORD code = ExceptionInfo->ExceptionRecord->ExceptionCode; + + #define ERR_RECORD( name ) { name, #name } + struct + { + int code; + char *pReason; + } errors[] = + { + ERR_RECORD( EXCEPTION_ACCESS_VIOLATION ), + ERR_RECORD( EXCEPTION_ARRAY_BOUNDS_EXCEEDED ), + ERR_RECORD( EXCEPTION_BREAKPOINT ), + ERR_RECORD( EXCEPTION_DATATYPE_MISALIGNMENT ), + ERR_RECORD( EXCEPTION_FLT_DENORMAL_OPERAND ), + ERR_RECORD( EXCEPTION_FLT_DIVIDE_BY_ZERO ), + ERR_RECORD( EXCEPTION_FLT_INEXACT_RESULT ), + ERR_RECORD( EXCEPTION_FLT_INVALID_OPERATION ), + ERR_RECORD( EXCEPTION_FLT_OVERFLOW ), + ERR_RECORD( EXCEPTION_FLT_STACK_CHECK ), + ERR_RECORD( EXCEPTION_FLT_UNDERFLOW ), + ERR_RECORD( EXCEPTION_ILLEGAL_INSTRUCTION ), + ERR_RECORD( EXCEPTION_IN_PAGE_ERROR ), + ERR_RECORD( EXCEPTION_INT_DIVIDE_BY_ZERO ), + ERR_RECORD( EXCEPTION_INT_OVERFLOW ), + ERR_RECORD( EXCEPTION_INVALID_DISPOSITION ), + ERR_RECORD( EXCEPTION_NONCONTINUABLE_EXCEPTION ), + ERR_RECORD( EXCEPTION_PRIV_INSTRUCTION ), + ERR_RECORD( EXCEPTION_SINGLE_STEP ), + ERR_RECORD( EXCEPTION_STACK_OVERFLOW ), + ERR_RECORD( EXCEPTION_ACCESS_VIOLATION ), + }; + + int nErrors = sizeof( errors ) / sizeof( errors[0] ); + int i=0; + char *pchReason = NULL; + char chUnknownBuffer[32]; + for ( i; ( i < nErrors ) && !pchReason; i++ ) + { + if ( errors[i].code == uCode ) + pchReason = errors[i].pReason; + } + + if ( i == nErrors ) + { + sprintf( chUnknownBuffer, "Error code 0x%08X", uCode ); + pchReason = chUnknownBuffer; + } + + VMPI_HandleCrash( pchReason, pvExceptionInfo, true ); + + TerminateProcess( GetCurrentProcess(), 1 ); +} + + +void HandleMPIDisconnect( int procID, const char *pReason ) +{ + int nLiveWorkers = VMPI_GetCurrentNumberOfConnections() - g_nDisconnects - 1; + + // We ran into the size limit before and it wasn't readily apparent that the size limit had + // been breached, so make sure to show errors about invalid packet sizes.. + bool bOldSuppress = g_bSuppressPrintfOutput; + g_bSuppressPrintfOutput = ( Q_stristr( pReason, "invalid packet size" ) == 0 ); + + Warning( "\n\n--- WARNING: lost connection to '%s' (%s).\n", VMPI_GetMachineName( procID ), pReason ); + + if ( g_bMPIMaster ) + { + Warning( "%d workers remain.\n\n", nLiveWorkers ); + + ++g_nDisconnects; + /* + if ( VMPI_GetCurrentNumberOfConnections() - g_nDisconnects <= 1 ) + { + Error( "All machines disconnected!" ); + } + */ + } + else + { + VMPI_HandleAutoRestart(); + Error( "Worker quitting." ); + } + + g_bSuppressPrintfOutput = bOldSuppress; +} + + diff --git a/mp/src/utils/common/vmpi_tools_shared.h b/mp/src/utils/common/vmpi_tools_shared.h index 7c22201f..980552e8 100644 --- a/mp/src/utils/common/vmpi_tools_shared.h +++ b/mp/src/utils/common/vmpi_tools_shared.h @@ -1,45 +1,45 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// - -#ifndef VMPI_TOOLS_SHARED_H -#define VMPI_TOOLS_SHARED_H -#ifdef _WIN32 -#pragma once -#endif - - -// Packet IDs. - #define VMPI_SUBPACKETID_DIRECTORIES 0 // qdir directories. - #define VMPI_SUBPACKETID_DBINFO 1 // MySQL database info. - #define VMPI_SUBPACKETID_CRASH 3 // A worker saying it crashed. - #define VMPI_SUBPACKETID_MULTICAST_ADDR 4 // Filesystem multicast address. - - -class CDBInfo; -class CIPAddr; - - -// Send/receive the qdir info. -void SendQDirInfo(); -void RecvQDirInfo(); - -void SendDBInfo( const CDBInfo *pInfo, unsigned long jobPrimaryID ); -void RecvDBInfo( CDBInfo *pInfo, unsigned long *pJobPrimaryID ); - -void SendMulticastIP( const CIPAddr *pAddr ); -void RecvMulticastIP( CIPAddr *pAddr ); - -void VMPI_HandleCrash( const char *pMessage, void *pvExceptionInfo, bool bAssert ); - -// Call this from an exception handler (set by SetUnhandledExceptionHandler). -// uCode = ExceptionInfo->ExceptionRecord->ExceptionCode. -// pvExceptionInfo = ExceptionInfo -void VMPI_ExceptionFilter( unsigned long uCode, void *pvExceptionInfo ); - -void HandleMPIDisconnect( int procID, const char *pReason ); - - -#endif // VMPI_TOOLS_SHARED_H +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef VMPI_TOOLS_SHARED_H +#define VMPI_TOOLS_SHARED_H +#ifdef _WIN32 +#pragma once +#endif + + +// Packet IDs. + #define VMPI_SUBPACKETID_DIRECTORIES 0 // qdir directories. + #define VMPI_SUBPACKETID_DBINFO 1 // MySQL database info. + #define VMPI_SUBPACKETID_CRASH 3 // A worker saying it crashed. + #define VMPI_SUBPACKETID_MULTICAST_ADDR 4 // Filesystem multicast address. + + +class CDBInfo; +class CIPAddr; + + +// Send/receive the qdir info. +void SendQDirInfo(); +void RecvQDirInfo(); + +void SendDBInfo( const CDBInfo *pInfo, unsigned long jobPrimaryID ); +void RecvDBInfo( CDBInfo *pInfo, unsigned long *pJobPrimaryID ); + +void SendMulticastIP( const CIPAddr *pAddr ); +void RecvMulticastIP( CIPAddr *pAddr ); + +void VMPI_HandleCrash( const char *pMessage, void *pvExceptionInfo, bool bAssert ); + +// Call this from an exception handler (set by SetUnhandledExceptionHandler). +// uCode = ExceptionInfo->ExceptionRecord->ExceptionCode. +// pvExceptionInfo = ExceptionInfo +void VMPI_ExceptionFilter( unsigned long uCode, void *pvExceptionInfo ); + +void HandleMPIDisconnect( int procID, const char *pReason ); + + +#endif // VMPI_TOOLS_SHARED_H diff --git a/mp/src/utils/common/wadlib.c b/mp/src/utils/common/wadlib.c index 2b5bb6b1..4aff972d 100644 --- a/mp/src/utils/common/wadlib.c +++ b/mp/src/utils/common/wadlib.c @@ -1,334 +1,334 @@ -//========= Copyright © 1996-2005, Valve LLC, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -// -//=============================================================================// -// wad2lib.c - -#include -#include -#include -#include -#include -#include -#include -//#include -#include - -#ifdef NeXT -#include -#endif -#include "cmdlib.h" -#include "wadlib.h" -#include "commonmacros.h" - -/* -============================================================================ - - WAD READING - -============================================================================ -*/ - - -lumpinfo_t *lumpinfo; // location of each lump on disk -int numlumps; - -wadinfo_t header; -FILE *wadhandle; - - -/* -==================== -W_OpenWad -==================== -*/ -void W_OpenWad (char *filename) -{ - lumpinfo_t *lump_p; - unsigned i; - int length; - -// -// open the file and add to directory -// - wadhandle = SafeOpenRead (filename); - SafeRead (wadhandle, &header, sizeof(header)); - - if (!STRING_MATCHES_ID(header.identification,WAD_ID)) - Error ("Wad file %s doesn't have %s identifier\n",filename, WAD_IDNAME); - - header.numlumps = LittleLong(header.numlumps); - header.infotableofs = LittleLong(header.infotableofs); - - numlumps = header.numlumps; - - length = numlumps*sizeof(lumpinfo_t); - lumpinfo = malloc (length); - lump_p = lumpinfo; - - fseek (wadhandle, header.infotableofs, SEEK_SET); - SafeRead (wadhandle, lumpinfo, length); - -// -// Fill in lumpinfo -// - - for (i=0 ; ifilepos = LittleLong(lump_p->filepos); - lump_p->size = LittleLong(lump_p->size); - } -} - - - -void CleanupName (char *in, char *out) -{ - int i; - - for (i=0 ; iname ) ; i++ ) - { - if (!in[i]) - break; - - out[i] = toupper(in[i]); - } - - for ( ; iname ); i++ ) - out[i] = 0; -} - - -/* -==================== -W_CheckNumForName - -Returns -1 if name not found -==================== -*/ -int W_CheckNumForName (char *name) -{ - char cleanname[TEXTURE_NAME_LENGTH]; - int v1,v2, v3, v4; - int i; - lumpinfo_t *lump_p; - - CleanupName (name, cleanname); - -// make the name into four integers for easy compares - - v1 = *(int *)cleanname; - v2 = *(int *)&cleanname[4]; - v3 = *(int *)&cleanname[8]; - v4 = *(int *)&cleanname[12]; - -// find it - - lump_p = lumpinfo; - for (i=0 ; iname == v1 - && *(int *)&lump_p->name[4] == v2 - && *(int *)&lump_p->name[8] == v3 - && *(int *)&lump_p->name[12] == v4 - && !strcmp( lump_p->name, cleanname ) ) - return i; - } - - return -1; -} - - -/* -==================== -W_GetNumForName - -Calls W_CheckNumForName, but bombs out if not found -==================== -*/ -int W_GetNumForName (char *name) -{ - int i; - - i = W_CheckNumForName (name); - if (i != -1) - return i; - - Error ("W_GetNumForName: %s not found!",name); - return -1; -} - - -/* -==================== -W_LumpLength - -Returns the buffer size needed to load the given lump -==================== -*/ -int W_LumpLength (int lump) -{ - if (lump >= numlumps) - Error ("W_LumpLength: %i >= numlumps",lump); - return lumpinfo[lump].size; -} - - -/* -==================== -W_ReadLumpNum - -Loads the lump into the given buffer, which must be >= W_LumpLength() -==================== -*/ -void W_ReadLumpNum (int lump, void *dest) -{ - lumpinfo_t *l; - - if (lump >= numlumps) - Error ("W_ReadLump: %i >= numlumps",lump); - l = lumpinfo+lump; - - fseek (wadhandle, l->filepos, SEEK_SET); - SafeRead (wadhandle, dest, l->size); -} - - - -/* -==================== -W_LoadLumpNum -==================== -*/ -void *W_LoadLumpNum (int lump) -{ - void *buf; - - if ((unsigned)lump >= numlumps) - Error ("W_CacheLumpNum: %i >= numlumps",lump); - - buf = malloc (W_LumpLength (lump)); - W_ReadLumpNum (lump, buf); - - return buf; -} - - -/* -==================== -W_LoadLumpName -==================== -*/ -void *W_LoadLumpName (char *name) -{ - return W_LoadLumpNum (W_GetNumForName(name)); -} - - -/* -=============================================================================== - - WAD CREATION - -=============================================================================== -*/ - -FILE *outwad; - -lumpinfo_t outinfo[4096]; -int outlumps; - -short (*wadshort) (short l); -int (*wadlong) (int l); - -/* -=============== -NewWad -=============== -*/ - -void NewWad (char *pathname, qboolean bigendien) -{ - outwad = SafeOpenWrite (pathname); - fseek (outwad, sizeof(wadinfo_t), SEEK_SET); - memset (outinfo, 0, sizeof(outinfo)); - - if (bigendien) - { - wadshort = BigShort; - wadlong = BigLong; - } - else - { - wadshort = LittleShort; - wadlong = LittleLong; - } - - outlumps = 0; -} - - -/* -=============== -AddLump -=============== -*/ - -void AddLump (char *name, void *buffer, int length, int type, int compress) -{ - lumpinfo_t *info; - int ofs; - - info = &outinfo[outlumps]; - outlumps++; - - memset (info,0,sizeof(info)); - - strcpy (info->name, name); - Q_strupr (info->name); - - ofs = ftell(outwad); - info->filepos = wadlong(ofs); - info->size = info->disksize = wadlong(length); - info->type = type; - info->compression = compress; - -// FIXME: do compression - - SafeWrite (outwad, buffer, length); -} - - -/* -=============== -WriteWad -=============== -*/ - -void WriteWad (int wad3) -{ - wadinfo_t header; - int ofs; - -// write the lumpingo - ofs = ftell(outwad); - - SafeWrite (outwad, outinfo, outlumps*sizeof(lumpinfo_t) ); - -// write the header - -// a program will be able to tell the ednieness of a wad by the id - ID_TO_STRING( WAD_ID, header.identification ); - - header.numlumps = wadlong(outlumps); - header.infotableofs = wadlong(ofs); - - fseek (outwad, 0, SEEK_SET); - SafeWrite (outwad, &header, sizeof(header)); - fclose (outwad); -} - - +//========= Copyright © 1996-2005, Valve LLC, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +// wad2lib.c + +#include +#include +#include +#include +#include +#include +#include +//#include +#include + +#ifdef NeXT +#include +#endif +#include "cmdlib.h" +#include "wadlib.h" +#include "commonmacros.h" + +/* +============================================================================ + + WAD READING + +============================================================================ +*/ + + +lumpinfo_t *lumpinfo; // location of each lump on disk +int numlumps; + +wadinfo_t header; +FILE *wadhandle; + + +/* +==================== +W_OpenWad +==================== +*/ +void W_OpenWad (char *filename) +{ + lumpinfo_t *lump_p; + unsigned i; + int length; + +// +// open the file and add to directory +// + wadhandle = SafeOpenRead (filename); + SafeRead (wadhandle, &header, sizeof(header)); + + if (!STRING_MATCHES_ID(header.identification,WAD_ID)) + Error ("Wad file %s doesn't have %s identifier\n",filename, WAD_IDNAME); + + header.numlumps = LittleLong(header.numlumps); + header.infotableofs = LittleLong(header.infotableofs); + + numlumps = header.numlumps; + + length = numlumps*sizeof(lumpinfo_t); + lumpinfo = malloc (length); + lump_p = lumpinfo; + + fseek (wadhandle, header.infotableofs, SEEK_SET); + SafeRead (wadhandle, lumpinfo, length); + +// +// Fill in lumpinfo +// + + for (i=0 ; ifilepos = LittleLong(lump_p->filepos); + lump_p->size = LittleLong(lump_p->size); + } +} + + + +void CleanupName (char *in, char *out) +{ + int i; + + for (i=0 ; iname ) ; i++ ) + { + if (!in[i]) + break; + + out[i] = toupper(in[i]); + } + + for ( ; iname ); i++ ) + out[i] = 0; +} + + +/* +==================== +W_CheckNumForName + +Returns -1 if name not found +==================== +*/ +int W_CheckNumForName (char *name) +{ + char cleanname[TEXTURE_NAME_LENGTH]; + int v1,v2, v3, v4; + int i; + lumpinfo_t *lump_p; + + CleanupName (name, cleanname); + +// make the name into four integers for easy compares + + v1 = *(int *)cleanname; + v2 = *(int *)&cleanname[4]; + v3 = *(int *)&cleanname[8]; + v4 = *(int *)&cleanname[12]; + +// find it + + lump_p = lumpinfo; + for (i=0 ; iname == v1 + && *(int *)&lump_p->name[4] == v2 + && *(int *)&lump_p->name[8] == v3 + && *(int *)&lump_p->name[12] == v4 + && !strcmp( lump_p->name, cleanname ) ) + return i; + } + + return -1; +} + + +/* +==================== +W_GetNumForName + +Calls W_CheckNumForName, but bombs out if not found +==================== +*/ +int W_GetNumForName (char *name) +{ + int i; + + i = W_CheckNumForName (name); + if (i != -1) + return i; + + Error ("W_GetNumForName: %s not found!",name); + return -1; +} + + +/* +==================== +W_LumpLength + +Returns the buffer size needed to load the given lump +==================== +*/ +int W_LumpLength (int lump) +{ + if (lump >= numlumps) + Error ("W_LumpLength: %i >= numlumps",lump); + return lumpinfo[lump].size; +} + + +/* +==================== +W_ReadLumpNum + +Loads the lump into the given buffer, which must be >= W_LumpLength() +==================== +*/ +void W_ReadLumpNum (int lump, void *dest) +{ + lumpinfo_t *l; + + if (lump >= numlumps) + Error ("W_ReadLump: %i >= numlumps",lump); + l = lumpinfo+lump; + + fseek (wadhandle, l->filepos, SEEK_SET); + SafeRead (wadhandle, dest, l->size); +} + + + +/* +==================== +W_LoadLumpNum +==================== +*/ +void *W_LoadLumpNum (int lump) +{ + void *buf; + + if ((unsigned)lump >= numlumps) + Error ("W_CacheLumpNum: %i >= numlumps",lump); + + buf = malloc (W_LumpLength (lump)); + W_ReadLumpNum (lump, buf); + + return buf; +} + + +/* +==================== +W_LoadLumpName +==================== +*/ +void *W_LoadLumpName (char *name) +{ + return W_LoadLumpNum (W_GetNumForName(name)); +} + + +/* +=============================================================================== + + WAD CREATION + +=============================================================================== +*/ + +FILE *outwad; + +lumpinfo_t outinfo[4096]; +int outlumps; + +short (*wadshort) (short l); +int (*wadlong) (int l); + +/* +=============== +NewWad +=============== +*/ + +void NewWad (char *pathname, qboolean bigendien) +{ + outwad = SafeOpenWrite (pathname); + fseek (outwad, sizeof(wadinfo_t), SEEK_SET); + memset (outinfo, 0, sizeof(outinfo)); + + if (bigendien) + { + wadshort = BigShort; + wadlong = BigLong; + } + else + { + wadshort = LittleShort; + wadlong = LittleLong; + } + + outlumps = 0; +} + + +/* +=============== +AddLump +=============== +*/ + +void AddLump (char *name, void *buffer, int length, int type, int compress) +{ + lumpinfo_t *info; + int ofs; + + info = &outinfo[outlumps]; + outlumps++; + + memset (info,0,sizeof(info)); + + strcpy (info->name, name); + Q_strupr (info->name); + + ofs = ftell(outwad); + info->filepos = wadlong(ofs); + info->size = info->disksize = wadlong(length); + info->type = type; + info->compression = compress; + +// FIXME: do compression + + SafeWrite (outwad, buffer, length); +} + + +/* +=============== +WriteWad +=============== +*/ + +void WriteWad (int wad3) +{ + wadinfo_t header; + int ofs; + +// write the lumpingo + ofs = ftell(outwad); + + SafeWrite (outwad, outinfo, outlumps*sizeof(lumpinfo_t) ); + +// write the header + +// a program will be able to tell the ednieness of a wad by the id + ID_TO_STRING( WAD_ID, header.identification ); + + header.numlumps = wadlong(outlumps); + header.infotableofs = wadlong(ofs); + + fseek (outwad, 0, SEEK_SET); + SafeWrite (outwad, &header, sizeof(header)); + fclose (outwad); +} + + diff --git a/mp/src/utils/common/wadlib.h b/mp/src/utils/common/wadlib.h index a8e4e09a..0fa12f50 100644 --- a/mp/src/utils/common/wadlib.h +++ b/mp/src/utils/common/wadlib.h @@ -1,46 +1,46 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -// -//=============================================================================// - -// wadlib.h - -// -// wad reading -// - -#define CMP_NONE 0 -#define CMP_LZSS 1 - -#define TYP_NONE 0 -#define TYP_LABEL 1 -#define TYP_LUMPY 64 // 64 + grab command number - -#ifndef WADTYPES_H -#include "wadtypes.h" -#endif - -extern lumpinfo_t *lumpinfo; // location of each lump on disk -extern int numlumps; -extern wadinfo_t header; - -void W_OpenWad (char *filename); -int W_CheckNumForName (char *name); -int W_GetNumForName (char *name); -int W_LumpLength (int lump); -void W_ReadLumpNum (int lump, void *dest); -void *W_LoadLumpNum (int lump); -void *W_LoadLumpName (char *name); - -void CleanupName (char *in, char *out); - -// -// wad creation -// -void NewWad (char *pathname, qboolean bigendien); -void AddLump (char *name, void *buffer, int length, int type, int compress); -void WriteWad (int wad3); - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +// wadlib.h + +// +// wad reading +// + +#define CMP_NONE 0 +#define CMP_LZSS 1 + +#define TYP_NONE 0 +#define TYP_LABEL 1 +#define TYP_LUMPY 64 // 64 + grab command number + +#ifndef WADTYPES_H +#include "wadtypes.h" +#endif + +extern lumpinfo_t *lumpinfo; // location of each lump on disk +extern int numlumps; +extern wadinfo_t header; + +void W_OpenWad (char *filename); +int W_CheckNumForName (char *name); +int W_GetNumForName (char *name); +int W_LumpLength (int lump); +void W_ReadLumpNum (int lump, void *dest); +void *W_LoadLumpNum (int lump); +void *W_LoadLumpName (char *name); + +void CleanupName (char *in, char *out); + +// +// wad creation +// +void NewWad (char *pathname, qboolean bigendien); +void AddLump (char *name, void *buffer, int length, int type, int compress); +void WriteWad (int wad3); + -- cgit v1.2.3