]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/qe3.cpp
radiant: strip .[ExecutableType] entirely (including preceding dot) outside of Windows
[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* gamename = gamename_get();
83         const char* basegame = basegame_get();
84         const char* userRoot = g_qeglobals.m_userEnginePath.c_str();
85         const char* globalRoot = EnginePath_get();
86
87         // editor builtin VFS
88         StringOutputStream editorGamePath( 256 );
89         editorGamePath << GlobalRadiant().getAppPath() << DEFAULT_EDITORVFS_DIRNAME;
90         GlobalFileSystem().initDirectory( editorGamePath.c_str() );
91
92         // if we have a mod dir
93         if ( !string_equal( gamename, basegame ) ) {
94                 // ~/.<gameprefix>/<fs_game>
95                 if ( userRoot && !g_disableHomePath ) {
96                         StringOutputStream userGamePath( 256 );
97                         userGamePath << userRoot << gamename << '/';
98                         GlobalFileSystem().initDirectory( userGamePath.c_str() );
99                 }
100
101                 // <fs_basepath>/<fs_game>
102                 if ( !g_disableEnginePath ) {
103                         StringOutputStream globalGamePath( 256 );
104                         globalGamePath << globalRoot << gamename << '/';
105                         GlobalFileSystem().initDirectory( globalGamePath.c_str() );
106                 }
107         }
108
109         // ~/.<gameprefix>/<fs_main>
110         if ( userRoot && !g_disableHomePath ) {
111                 StringOutputStream userBasePath( 256 );
112                 userBasePath << userRoot << basegame << '/';
113                 GlobalFileSystem().initDirectory( userBasePath.c_str() );
114         }
115
116         // <fs_basepath>/<fs_main>
117         if ( !g_disableEnginePath ) {
118                 StringOutputStream globalBasePath( 256 );
119                 globalBasePath << globalRoot << basegame << '/';
120                 GlobalFileSystem().initDirectory( globalBasePath.c_str() );
121         }
122
123         // extra pakpaths
124         for ( int i = 0; i < g_pakPathCount; i++ ) {
125                 if (g_strcmp0( g_strPakPath[i].c_str(), "")) {
126                         GlobalFileSystem().initDirectory( g_strPakPath[i].c_str() );
127                 }
128         }
129 }
130
131 int g_numbrushes = 0;
132 int g_numentities = 0;
133
134 void QE_UpdateStatusBar(){
135         char buffer[128];
136         sprintf( buffer, "Brushes: %d Entities: %d", g_numbrushes, g_numentities );
137         g_pParentWnd->SetStatusText( g_pParentWnd->m_brushcount_status, buffer );
138 }
139
140 SimpleCounter g_brushCount;
141
142 void QE_brushCountChanged(){
143         g_numbrushes = int(g_brushCount.get() );
144         QE_UpdateStatusBar();
145 }
146
147 SimpleCounter g_entityCount;
148
149 void QE_entityCountChanged(){
150         g_numentities = int(g_entityCount.get() );
151         QE_UpdateStatusBar();
152 }
153
154 bool ConfirmModified( const char* title ){
155         if ( !Map_Modified( g_map ) ) {
156                 return true;
157         }
158
159         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 );
160         if ( result == ui::alert_response::CANCEL ) {
161                 return false;
162         }
163         if ( result == ui::alert_response::YES ) {
164                 if ( Map_Unnamed( g_map ) ) {
165                         return Map_SaveAs();
166                 }
167                 else
168                 {
169                         return Map_Save();
170                 }
171         }
172         return true;
173 }
174
175 void bsp_init(){
176         build_set_variable( "RadiantPath", AppPath_get() );
177
178         #if GDEF_OS_WINDOWS
179         build_set_variable( "ExecutableType", RADIANT_EXECUTABLE );
180         #endif // GDEF_OS_WINDOWS
181
182         build_set_variable( "EnginePath", EnginePath_get() );
183         build_set_variable( "UserEnginePath", g_qeglobals.m_userEnginePath.c_str() );
184         build_set_variable( "MonitorAddress", ( g_WatchBSP_Enabled ) ? "127.0.0.1:39000" : "" );
185         build_set_variable( "GameName", gamename_get() );
186
187         StringBuffer ExtraQ3map2Args;
188         // extra pakpaths
189         for ( int i = 0; i < g_pakPathCount; i++ ) {
190                 if ( g_strcmp0( g_strPakPath[i].c_str(), "") ) {
191                         ExtraQ3map2Args.push_string( " -fs_pakpath \"" );
192                         ExtraQ3map2Args.push_string( g_strPakPath[i].c_str() );
193                         ExtraQ3map2Args.push_string( "\"" );
194                 }
195         }
196
197         // extra switches
198         if ( g_disableEnginePath ) {
199                 ExtraQ3map2Args.push_string( " -fs_nobasepath " );
200         }
201
202         if ( g_disableHomePath ) {
203                 ExtraQ3map2Args.push_string( " -fs_nohomepath " );
204         }
205
206         build_set_variable( "ExtraQ3map2Args", ExtraQ3map2Args.c_str() );
207
208         const char* mapname = Map_Name( g_map );
209         StringOutputStream name( 256 );
210         name << StringRange( mapname, path_get_filename_base_end( mapname ) ) << ".bsp";
211
212         build_set_variable( "MapFile", mapname );
213         build_set_variable( "BspFile", name.c_str() );
214 }
215
216 void bsp_shutdown(){
217         build_clear_variables();
218 }
219
220 class ArrayCommandListener : public CommandListener
221 {
222 GPtrArray* m_array;
223 public:
224 ArrayCommandListener(){
225         m_array = g_ptr_array_new();
226 }
227
228 ~ArrayCommandListener(){
229         g_ptr_array_free( m_array, TRUE );
230 }
231
232 void execute( const char* command ){
233         g_ptr_array_add( m_array, g_strdup( command ) );
234 }
235
236 GPtrArray* array() const {
237         return m_array;
238 }
239 };
240
241 class BatchCommandListener : public CommandListener
242 {
243 TextOutputStream& m_file;
244 std::size_t m_commandCount;
245 const char* m_outputRedirect;
246 public:
247 BatchCommandListener( TextOutputStream& file, const char* outputRedirect ) : m_file( file ), m_commandCount( 0 ), m_outputRedirect( outputRedirect ){
248 }
249
250 void execute( const char* command ){
251         m_file << command;
252         if ( m_commandCount == 0 ) {
253                 m_file << " > ";
254         }
255         else
256         {
257                 m_file << " >> ";
258         }
259         m_file << "\"" << m_outputRedirect << "\"";
260         m_file << "\n";
261         ++m_commandCount;
262 }
263 };
264
265 bool Region_cameraValid(){
266         Vector3 vOrig( vector3_snapped( Camera_getOrigin( *g_pParentWnd->GetCamWnd() ) ) );
267
268         for ( int i = 0 ; i < 3 ; i++ )
269         {
270                 if ( vOrig[i] > region_maxs[i] || vOrig[i] < region_mins[i] ) {
271                         return false;
272                 }
273         }
274         return true;
275 }
276
277
278 void RunBSP( const char* name ){
279         // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=503
280         // make sure we don't attempt to region compile a map with the camera outside the region
281         if ( region_active && !Region_cameraValid() ) {
282                 globalErrorStream() << "The camera must be in the region to start a region compile.\n";
283                 return;
284         }
285
286         SaveMap();
287
288         if ( Map_Unnamed( g_map ) ) {
289                 globalOutputStream() << "build cancelled\n";
290                 return;
291         }
292
293         if ( g_SnapShots_Enabled && !Map_Unnamed( g_map ) && Map_Modified( g_map ) ) {
294                 Map_Snapshot();
295         }
296
297         if ( region_active ) {
298                 const char* mapname = Map_Name( g_map );
299                 StringOutputStream name( 256 );
300                 name << StringRange( mapname, path_get_filename_base_end( mapname ) ) << ".reg";
301                 Map_SaveRegion( name.c_str() );
302         }
303
304         Pointfile_Delete();
305
306         bsp_init();
307
308         if ( g_WatchBSP_Enabled ) {
309                 ArrayCommandListener listener;
310                 build_run( name, listener );
311                 // grab the file name for engine running
312                 const char* fullname = Map_Name( g_map );
313                 StringOutputStream bspname( 64 );
314                 bspname << StringRange( path_get_filename_start( fullname ), path_get_filename_base_end( fullname ) );
315                 BuildMonitor_Run( listener.array(), bspname.c_str() );
316         }
317         else
318         {
319                 char junkpath[PATH_MAX];
320                 strcpy( junkpath, SettingsPath_get() );
321                 strcat( junkpath, "junk.txt" );
322
323                 char batpath[PATH_MAX];
324 #if GDEF_OS_POSIX
325                 strcpy( batpath, SettingsPath_get() );
326                 strcat( batpath, "qe3bsp.sh" );
327 #elif GDEF_OS_WINDOWS
328                 strcpy( batpath, SettingsPath_get() );
329                 strcat( batpath, "qe3bsp.bat" );
330 #else
331 #error "unsupported platform"
332 #endif
333                 bool written = false;
334                 {
335                         TextFileOutputStream batchFile( batpath );
336                         if ( !batchFile.failed() ) {
337 #if GDEF_OS_POSIX
338                                 batchFile << "#!/bin/sh \n\n";
339 #endif
340                                 BatchCommandListener listener( batchFile, junkpath );
341                                 build_run( name, listener );
342                                 written = true;
343                         }
344                 }
345                 if ( written ) {
346 #if GDEF_OS_POSIX
347                         chmod( batpath, 0744 );
348 #endif
349                         globalOutputStream() << "Writing the compile script to '" << batpath << "'\n";
350                         globalOutputStream() << "The build output will be saved in '" << junkpath << "'\n";
351                         Q_Exec( batpath, NULL, NULL, true, false );
352                 }
353         }
354
355         bsp_shutdown();
356 }
357
358 // =============================================================================
359 // Sys_ functions
360
361 void Sys_SetTitle( const char *text, bool modified ){
362         StringOutputStream title;
363         title << text;
364
365         if ( modified ) {
366                 title << " *";
367         }
368
369         gtk_window_set_title(MainFrame_getWindow(), title.c_str() );
370 }
371
372 bool g_bWaitCursor = false;
373
374 void Sys_BeginWait( void ){
375         ScreenUpdates_Disable( "Processing...", "Please Wait" );
376         GdkCursor *cursor = gdk_cursor_new( GDK_WATCH );
377         gdk_window_set_cursor( gtk_widget_get_window(MainFrame_getWindow()), cursor );
378         gdk_cursor_unref( cursor );
379         g_bWaitCursor = true;
380 }
381
382 void Sys_EndWait( void ){
383         ScreenUpdates_Enable();
384         gdk_window_set_cursor(gtk_widget_get_window(MainFrame_getWindow()), 0 );
385         g_bWaitCursor = false;
386 }
387
388 void Sys_Beep( void ){
389         gdk_beep();
390 }