aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/client/bsp_utils.cpp
blob: c21c2adafb4d923e133335ad65e467d75e05a97b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Exposes bsp tools to game for e.g. workshop use
//
// $NoKeywords: $
//===========================================================================//

#include "cbase.h"
#include <tier2/tier2.h>
#include "filesystem.h"
#include "bsp_utils.h"
#include "utlbuffer.h"
#include "igamesystem.h"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

bool BSP_SyncRepack( const char *pszInputMapFile,
                     const char *pszOutputMapFile,
                     IBSPPack::eRepackBSPFlags eRepackFlags )
{
	// load the bsppack dll
	IBSPPack *libBSPPack = NULL;
	CSysModule *pModule = g_pFullFileSystem->LoadModule( "bsppack" );
	if ( pModule )
	{
		CreateInterfaceFn BSPPackFactory = Sys_GetFactory( pModule );
		if ( BSPPackFactory )
		{
			libBSPPack = ( IBSPPack * )BSPPackFactory( IBSPPACK_VERSION_STRING, NULL );
		}
	}
	if( !libBSPPack )
	{
		Warning( "Can't load bsppack library - unable to compress bsp\n" );
		return false;
	}

	Msg( "Repacking %s -> %s\n", pszInputMapFile, pszOutputMapFile );

	if ( !g_pFullFileSystem->FileExists( pszInputMapFile ) )
	{
		Warning( "Couldn't open input file %s - BSP recompress failed\n", pszInputMapFile );
		return false;
	}

	CUtlBuffer inputBuffer;
	if ( !g_pFullFileSystem->ReadFile( pszInputMapFile, NULL, inputBuffer ) )
	{
		Warning( "Couldn't read file %s - BSP compression failed\n", pszInputMapFile );
		return false;
	}

	CUtlBuffer outputBuffer;

	if ( !libBSPPack->RepackBSP( inputBuffer, outputBuffer, eRepackFlags ) )
	{
		Warning( "Internal error compressing BSP\n" );
		return false;
	}

	g_pFullFileSystem->WriteFile( pszOutputMapFile, NULL, outputBuffer );

	Msg( "Successfully repacked %s as %s -- %u -> %u bytes\n",
	     pszInputMapFile, pszOutputMapFile, inputBuffer.TellPut(), outputBuffer.TellPut() );

	return true;
}

// Helper to create a thread that calls SyncCompressMap, and clean it up when it exists
void BSP_BackgroundRepack( const char *pszInputMapFile,
                           const char *pszOutputMapFile,
                           IBSPPack::eRepackBSPFlags eRepackFlags )
{
	// Make this a gamesystem and thread, so it can check for completion each frame and clean itself up. Run() is the
	// background thread, Update() is the main thread tick.
	class BackgroundBSPRepackThread : public CThread, public CAutoGameSystemPerFrame
	{
	public:
		BackgroundBSPRepackThread( const char *pszInputFile, const char *pszOutputFile, IBSPPack::eRepackBSPFlags eRepackFlags )
			: m_strInput( pszInputFile )
			, m_strOutput( pszOutputFile )
			, m_eRepackFlags( eRepackFlags )
		{
			Start();
		}

		// CThread job - returns 0 for success
		virtual int Run() OVERRIDE
		{
			return BSP_SyncRepack( m_strInput.Get(), m_strOutput.Get(), m_eRepackFlags ) ? 0 : 1;
		}

		// GameSystem
		virtual const char* Name( void ) OVERRIDE { return "BackgroundBSPRepackThread"; }

		// Runs on main thread
		void CheckFinished()
		{
			if ( !IsAlive() )
			{
				// Thread finished
				if ( GetResult() != 0 )
				{
					Warning( "Map compression thread failed :(\n" );
				}

				// AutoGameSystem deregisters itself on destruction, we're done
				delete this;
			}
		}

		#ifdef CLIENT_DLL
		virtual void Update( float frametime ) OVERRIDE { CheckFinished(); }
        #else // GAME DLL
		virtual void FrameUpdatePostEntityThink() OVERRIDE { CheckFinished(); }
		#endif
	private:
		CUtlString                m_strInput;
		CUtlString                m_strOutput;
		IBSPPack::eRepackBSPFlags m_eRepackFlags;
	};

	Msg( "Starting BSP repack job %s -> %s\n", pszInputMapFile, pszOutputMapFile );

	// Deletes itself up when done
	new BackgroundBSPRepackThread( pszInputMapFile, pszOutputMapFile, eRepackFlags );
}

CON_COMMAND( bsp_repack, "Repack and output a (re)compressed version of a bsp file" )
{
#ifdef GAME_DLL
	if ( !UTIL_IsCommandIssuedByServerAdmin() )
		return;
#endif

	// Handle -nocompress
	bool bCompress = true;
	const char *szInFilename = NULL;
	const char *szOutFilename = NULL;

	if ( args.ArgC() == 4 && V_strcasecmp( args.Arg( 1 ), "-nocompress" ) == 0 )
	{
		bCompress = false;
		szInFilename = args.Arg( 2 );
		szOutFilename = args.Arg( 3 );
	}
	else if ( args.ArgC() == 3 )
	{
		szInFilename = args.Arg( 1 );
		szOutFilename = args.Arg( 2 );
	}

	if ( !szInFilename || !szOutFilename || !strlen( szInFilename ) || !strlen( szOutFilename ) )
	{
		Msg( "Usage: bsp_repack [-nocompress] map.bsp output_map.bsp\n" );
		return;
	}

	if ( bCompress )
	{
		// Use default compress flags
		BSP_BackgroundRepack( szInFilename, szOutFilename );
	}
	else
	{
		// No compression
		BSP_BackgroundRepack( szInFilename, szOutFilename, (IBSPPack::eRepackBSPFlags)0 );
	}
}