]> git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/qe3.cpp
Merge commit '93a3d833ff57ef8304cb547dd1e3664b6b276ec4' into master-merge
[xonotic/netradiant.git] / radiant / qe3.cpp
1 /*
2    Copyright (C) 1999-2006 Id Software, Inc. and contributors.
3    For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5    This file is part of GtkRadiant.
6
7    GtkRadiant is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    GtkRadiant is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with GtkRadiant; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21
22 /*
23    The following source code is licensed by Id Software and subject to the terms of
24    its LIMITED USE SOFTWARE LICENSE AGREEMENT, a copy of which is included with
25    GtkRadiant. If you did not receive a LIMITED USE SOFTWARE LICENSE AGREEMENT,
26    please contact Id Software immediately at info@idsoftware.com.
27  */
28
29 //
30 // Linux stuff
31 //
32 // Leonardo Zide (leo@lokigames.com)
33 //
34
35 #include "defaults.h"
36 #include "qe3.h"
37 #include "globaldefs.h"
38
39 #include <gtk/gtk.h>
40
41 #include "debugging/debugging.h"
42
43 #include "ifilesystem.h"
44 //#include "imap.h"
45
46 #include <map>
47
48 #include <uilib/uilib.h>
49
50 #include "stream/textfilestream.h"
51 #include "cmdlib.h"
52 #include "stream/stringstream.h"
53 #include "os/path.h"
54 #include "scenelib.h"
55
56 #include "gtkutil/messagebox.h"
57 #include "error.h"
58 #include "map.h"
59 #include "build.h"
60 #include "points.h"
61 #include "camwindow.h"
62 #include "mainframe.h"
63 #include "preferences.h"
64 #include "watchbsp.h"
65 #include "autosave.h"
66 #include "convert.h"
67
68 QEGlobals_t g_qeglobals;
69
70
71 #if GDEF_OS_WINDOWS
72 #define PATH_MAX 260
73 #endif
74
75
76 void QE_InitVFS(){
77         // VFS initialization -----------------------
78         // we will call GlobalFileSystem().initDirectory, giving the directories to look in (for files in pk3's and for standalone files)
79         // we need to call in order, the mod ones first, then the base ones .. they will be searched in this order
80         // *nix systems have a dual filesystem in ~/.q3a, which is searched first .. so we need to add that too
81
82         const char* enginepath = EnginePath_get();
83         const char* homepath = g_qeglobals.m_userEnginePath.c_str(); // returns enginepath if not homepath is not set
84
85         const char* basegame = basegame_get();
86         const char* gamename = gamename_get(); // returns basegame if gamename is not set
87
88         // editor builtin VFS
89         StringOutputStream editorGamePath( 256 );
90         editorGamePath << GlobalRadiant().getDataPath() << DEFAULT_EDITORVFS_DIRNAME;
91         GlobalFileSystem().initDirectory( editorGamePath.c_str() );
92
93         globalOutputStream() << "engine path: " << enginepath << "\n";
94         globalOutputStream() << "home path: " << homepath << "\n";
95         globalOutputStream() << "base game: " << basegame << "\n";
96         globalOutputStream() << "game name: " << gamename << "\n";
97
98         // if we have a mod dir
99         if ( !string_equal( gamename, basegame ) ) {
100                 // if we have a home dir
101                 if ( !string_equal( homepath, enginepath ) )
102                 {
103                 // ~/.<gameprefix>/<fs_game>
104                 if ( homepath && !string_equal( enginepath, homepath ) && !g_disableHomePath ) {
105                         StringOutputStream userGamePath( 256 );
106                                 userGamePath << homepath << gamename << '/';
107                         GlobalFileSystem().initDirectory( userGamePath.c_str() );
108                 }
109                 }
110
111                 // <fs_basepath>/<fs_game>
112                 if ( !g_disableEnginePath ) {
113                         StringOutputStream globalGamePath( 256 );
114                         globalGamePath << enginepath << gamename << '/';
115                         GlobalFileSystem().initDirectory( globalGamePath.c_str() );
116                 }
117         }
118
119         // if we have a home dir
120         if ( !string_equal( homepath, enginepath ) )
121         {
122         // ~/.<gameprefix>/<fs_main>
123         if ( homepath && !string_equal( enginepath, homepath ) && !g_disableHomePath ) {
124                 StringOutputStream userBasePath( 256 );
125                         userBasePath << homepath << basegame << '/';
126                 GlobalFileSystem().initDirectory( userBasePath.c_str() );
127         }
128         }
129
130         // <fs_basepath>/<fs_main>
131         if ( !g_disableEnginePath ) {
132                 StringOutputStream globalBasePath( 256 );
133                 globalBasePath << enginepath << basegame << '/';
134                 GlobalFileSystem().initDirectory( globalBasePath.c_str() );
135         }
136
137         // extra pakpaths
138         for ( int i = 0; i < g_pakPathCount; i++ ) {
139                 if (g_strcmp0( g_strPakPath[i].c_str(), "")) {
140                         GlobalFileSystem().initDirectory( g_strPakPath[i].c_str() );
141                 }
142         }
143 }
144
145 int g_numbrushes = 0;
146 int g_numentities = 0;
147
148 void QE_UpdateStatusBar(){
149         char buffer[128];
150         sprintf( buffer, "Brushes: %d Entities: %d", g_numbrushes, g_numentities );
151         g_pParentWnd->SetStatusText( g_pParentWnd->m_brushcount_status, buffer );
152 }
153
154 SimpleCounter g_brushCount;
155
156 void QE_brushCountChanged(){
157         g_numbrushes = int(g_brushCount.get() );
158         QE_UpdateStatusBar();
159 }
160
161 SimpleCounter g_entityCount;
162
163 void QE_entityCountChanged(){
164         g_numentities = int(g_entityCount.get() );
165         QE_UpdateStatusBar();
166 }
167
168 bool ConfirmModified( const char* title ){
169         if ( !Map_Modified( g_map ) ) {
170                 return true;
171         }
172
173         auto result = ui::alert( MainFrame_getWindow(), "The current map has changed since it was last saved.\nDo you want to save the current map before continuing?", title, ui::alert_type::YESNOCANCEL, ui::alert_icon::Question );
174         if ( result == ui::alert_response::CANCEL ) {
175                 return false;
176         }
177         if ( result == ui::alert_response::YES ) {
178                 if ( Map_Unnamed( g_map ) ) {
179                         return Map_SaveAs();
180                 }
181                 else
182                 {
183                         return Map_Save();
184                 }
185         }
186         return true;
187 }
188
189 void bsp_init(){
190         // this is expected to not be used since
191         // ".[ExecutableType]" is replaced by "[ExecutableExt]"
192         const char *exe_ext = GDEF_OS_EXE_EXT;
193         build_set_variable( "ExecutableType", exe_ext[0] == '\0' ? exe_ext : exe_ext + 1 );
194
195         build_set_variable( "ExecutableExt", GDEF_OS_EXE_EXT );
196         build_set_variable( "RadiantPath", AppPath_get() );
197         build_set_variable( "EnginePath", EnginePath_get() );
198         build_set_variable( "UserEnginePath", g_qeglobals.m_userEnginePath.c_str() );
199
200         build_set_variable( "MonitorAddress", ( g_WatchBSP_Enabled ) ? "127.0.0.1:39000" : "" );
201
202         build_set_variable( "GameName", gamename_get() );
203
204         std::string ExtraQ3map2Args;
205         // extra pakpaths
206         for ( int i = 0; i < g_pakPathCount; i++ ) {
207                 if ( g_strcmp0( g_strPakPath[i].c_str(), "") ) {
208                         ExtraQ3map2Args += " -fs_pakpath \"";
209                         ExtraQ3map2Args += g_strPakPath[i].c_str();
210                         ExtraQ3map2Args += "\"";
211                 }
212         }
213
214         // extra switches
215         if ( g_disableEnginePath ) {
216                 ExtraQ3map2Args += " -fs_nobasepath ";
217         }
218
219         if ( g_disableHomePath ) {
220                 ExtraQ3map2Args += " -fs_nohomepath ";
221         }
222
223         build_set_variable( "ExtraQ3map2Args", ExtraQ3map2Args.c_str() );
224
225         const char* mapname = Map_Name( g_map );
226         std::string name;
227         name.append( mapname, path_get_filename_base_end( mapname ) - mapname );
228         name += ".bsp";
229
230         if( region_active ){
231                 StringOutputStream name( 256 );
232                 name << StringRange( mapname, path_get_filename_base_end( mapname ) ) << ".reg";
233                 build_set_variable( "MapFile", name.c_str() );
234         }
235         else{
236                 build_set_variable( "MapFile", mapname );
237         }
238
239         build_set_variable( "BspFile", name.c_str() );
240 }
241
242 void bsp_shutdown(){
243         build_clear_variables();
244 }
245
246 class ArrayCommandListener : public CommandListener
247 {
248 GPtrArray* m_array;
249 public:
250 ArrayCommandListener(){
251         m_array = g_ptr_array_new();
252 }
253
254 ~ArrayCommandListener(){
255         g_ptr_array_free( m_array, TRUE );
256 }
257
258 void execute( const char* command ){
259         g_ptr_array_add( m_array, g_strdup( command ) );
260 }
261
262 GPtrArray* array() const {
263         return m_array;
264 }
265 };
266
267 class BatchCommandListener : public CommandListener
268 {
269 TextOutputStream& m_file;
270 std::size_t m_commandCount;
271 const char* m_outputRedirect;
272 public:
273 BatchCommandListener( TextOutputStream& file, const char* outputRedirect ) : m_file( file ), m_commandCount( 0 ), m_outputRedirect( outputRedirect ){
274 }
275
276 void execute( const char* command ){
277         m_file << command;
278         if ( m_commandCount == 0 ) {
279                 m_file << " > ";
280         }
281         else
282         {
283                 m_file << " >> ";
284         }
285         m_file << "\"" << m_outputRedirect << "\"";
286         m_file << "\n";
287         ++m_commandCount;
288 }
289 };
290
291 bool Region_cameraValid(){
292         Vector3 vOrig( vector3_snapped( Camera_getOrigin( *g_pParentWnd->GetCamWnd() ) ) );
293
294         for ( int i = 0 ; i < 3 ; i++ )
295         {
296                 if ( vOrig[i] > region_maxs[i] || vOrig[i] < region_mins[i] ) {
297                         return false;
298                 }
299         }
300         return true;
301 }
302
303
304 void RunBSP( const char* name ){
305         // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=503
306         // make sure we don't attempt to region compile a map with the camera outside the region
307         if ( region_active && !Region_cameraValid() ) {
308                 globalErrorStream() << "The camera must be in the region to start a region compile.\n";
309                 return;
310         }
311
312         SaveMap();
313
314         if ( Map_Unnamed( g_map ) ) {
315                 globalOutputStream() << "build cancelled\n";
316                 return;
317         }
318
319         if ( g_SnapShots_Enabled && !Map_Unnamed( g_map ) && Map_Modified( g_map ) ) {
320                 Map_Snapshot();
321         }
322
323         if ( region_active ) {
324                 const char* mapname = Map_Name( g_map );
325                 StringOutputStream name( 256 );
326                 name << StringRange( mapname, path_get_filename_base_end( mapname ) ) << ".reg";
327                 Map_SaveRegion( name.c_str() );
328         }
329
330         Pointfile_Delete();
331
332         bsp_init();
333
334         if ( g_WatchBSP_Enabled ) {
335                 ArrayCommandListener listener;
336                 build_run( name, listener );
337                 // grab the file name for engine running
338                 const char* fullname = Map_Name( g_map );
339                 StringOutputStream bspname( 64 );
340                 bspname << StringRange( path_get_filename_start( fullname ), path_get_filename_base_end( fullname ) );
341                 BuildMonitor_Run( listener.array(), bspname.c_str() );
342         }
343         else
344         {
345                 char junkpath[PATH_MAX];
346                 strcpy( junkpath, SettingsPath_get() );
347                 strcat( junkpath, "junk.txt" );
348
349                 char batpath[PATH_MAX];
350 #if GDEF_OS_POSIX
351                 strcpy( batpath, SettingsPath_get() );
352                 strcat( batpath, "qe3bsp.sh" );
353 #elif GDEF_OS_WINDOWS
354                 strcpy( batpath, SettingsPath_get() );
355                 strcat( batpath, "qe3bsp.bat" );
356 #else
357 #error "unsupported platform"
358 #endif
359                 bool written = false;
360                 {
361                         TextFileOutputStream batchFile( batpath );
362                         if ( !batchFile.failed() ) {
363 #if GDEF_OS_POSIX
364                                 batchFile << "#!/bin/sh \n\n";
365 #endif
366                                 BatchCommandListener listener( batchFile, junkpath );
367                                 build_run( name, listener );
368                                 written = true;
369                         }
370                 }
371                 if ( written ) {
372 #if GDEF_OS_POSIX
373                         chmod( batpath, 0744 );
374 #endif
375                         globalOutputStream() << "Writing the compile script to '" << batpath << "'\n";
376                         globalOutputStream() << "The build output will be saved in '" << junkpath << "'\n";
377                         Q_Exec( batpath, NULL, NULL, true, false );
378                 }
379         }
380
381         bsp_shutdown();
382 }
383
384 // =============================================================================
385 // Sys_ functions
386
387 void Sys_SetTitle( const char *text, bool modified ){
388         StringOutputStream title;
389         title << text;
390
391         if ( modified ) {
392                 title << " *";
393         }
394
395         gtk_window_set_title(MainFrame_getWindow(), title.c_str() );
396 }
397
398 bool g_bWaitCursor = false;
399
400 void Sys_BeginWait( void ){
401         ScreenUpdates_Disable( "Processing...", "Please Wait" );
402         GdkCursor *cursor = gdk_cursor_new( GDK_WATCH );
403         gdk_window_set_cursor( gtk_widget_get_window(MainFrame_getWindow()), cursor );
404         gdk_cursor_unref( cursor );
405         g_bWaitCursor = true;
406 }
407
408 void Sys_EndWait( void ){
409         ScreenUpdates_Enable();
410         gdk_window_set_cursor(gtk_widget_get_window(MainFrame_getWindow()), 0 );
411         g_bWaitCursor = false;
412 }
413
414 void Sys_Beep( void ){
415         gdk_beep();
416 }