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