]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/qe3.cpp
Merge branch 'optional_q3map2_type' into 'master'
[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 "qe3.h"
36 #include "globaldefs.h"
37
38 #include <gtk/gtk.h>
39
40 #include "debugging/debugging.h"
41
42 #include "ifilesystem.h"
43 //#include "imap.h"
44
45 #include <map>
46
47 #include <uilib/uilib.h>
48
49 #include "stream/textfilestream.h"
50 #include "cmdlib.h"
51 #include "stream/stringstream.h"
52 #include "os/path.h"
53 #include "scenelib.h"
54
55 #include "gtkutil/messagebox.h"
56 #include "error.h"
57 #include "map.h"
58 #include "build.h"
59 #include "points.h"
60 #include "camwindow.h"
61 #include "mainframe.h"
62 #include "preferences.h"
63 #include "watchbsp.h"
64 #include "autosave.h"
65 #include "convert.h"
66
67 QEGlobals_t g_qeglobals;
68
69
70 #if GDEF_OS_WINDOWS
71 #define PATH_MAX 260
72 #endif
73
74
75 void QE_InitVFS()
76 {
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     // if we have a mod dir
88     if (!string_equal(gamename, basegame)) {
89         // ~/.<gameprefix>/<fs_game>
90         if (userRoot && !g_disableHomePath) {
91             StringOutputStream userGamePath(256);
92             userGamePath << userRoot << gamename << '/';
93             GlobalFileSystem().initDirectory(userGamePath.c_str());
94         }
95
96         // <fs_basepath>/<fs_game>
97         if (!g_disableEnginePath) {
98             StringOutputStream globalGamePath(256);
99             globalGamePath << globalRoot << gamename << '/';
100             GlobalFileSystem().initDirectory(globalGamePath.c_str());
101         }
102     }
103
104     // ~/.<gameprefix>/<fs_main>
105     if (userRoot && !g_disableHomePath) {
106         StringOutputStream userBasePath(256);
107         userBasePath << userRoot << basegame << '/';
108         GlobalFileSystem().initDirectory(userBasePath.c_str());
109     }
110
111     // <fs_basepath>/<fs_main>
112     if (!g_disableEnginePath) {
113         StringOutputStream globalBasePath(256);
114         globalBasePath << globalRoot << basegame << '/';
115         GlobalFileSystem().initDirectory(globalBasePath.c_str());
116     }
117
118     // extra pakpaths
119     for (int i = 0; i < g_pakPathCount; i++) {
120         if (g_strcmp0(g_strPakPath[i].c_str(), "")) {
121             GlobalFileSystem().initDirectory(g_strPakPath[i].c_str());
122         }
123     }
124 }
125
126 int g_numbrushes = 0;
127 int g_numentities = 0;
128
129 void QE_UpdateStatusBar()
130 {
131     char buffer[128];
132     sprintf(buffer, "Brushes: %d Entities: %d", g_numbrushes, g_numentities);
133     g_pParentWnd->SetStatusText(g_pParentWnd->m_brushcount_status, buffer);
134 }
135
136 SimpleCounter g_brushCount;
137
138 void QE_brushCountChanged()
139 {
140     g_numbrushes = int(g_brushCount.get());
141     QE_UpdateStatusBar();
142 }
143
144 SimpleCounter g_entityCount;
145
146 void QE_entityCountChanged()
147 {
148     g_numentities = int(g_entityCount.get());
149     QE_UpdateStatusBar();
150 }
151
152 bool ConfirmModified(const char *title)
153 {
154     if (!Map_Modified(g_map)) {
155         return true;
156     }
157
158     auto result = ui::alert(MainFrame_getWindow(),
159                             "The current map has changed since it was last saved.\nDo you want to save the current map before continuing?",
160                             title, ui::alert_type::YESNOCANCEL, ui::alert_icon::Question);
161     if (result == ui::alert_response::CANCEL) {
162         return false;
163     }
164     if (result == ui::alert_response::YES) {
165         if (Map_Unnamed(g_map)) {
166             return Map_SaveAs();
167         } else {
168             return Map_Save();
169         }
170     }
171     return true;
172 }
173
174 void bsp_init()
175 {
176     build_set_variable("RadiantPath", AppPath_get());
177     build_set_variable("ExecutableType", RADIANT_EXECUTABLE);
178     build_set_variable("EnginePath", EnginePath_get());
179     build_set_variable("UserEnginePath", g_qeglobals.m_userEnginePath.c_str());
180     build_set_variable("MonitorAddress", (g_WatchBSP_Enabled) ? "127.0.0.1:39000" : "");
181     build_set_variable("GameName", gamename_get());
182
183     StringBuffer ExtraQ3map2Args;
184     // extra pakpaths
185     for (int i = 0; i < g_pakPathCount; i++) {
186         if (g_strcmp0(g_strPakPath[i].c_str(), "")) {
187             ExtraQ3map2Args.push_string(" -fs_pakpath \"");
188             ExtraQ3map2Args.push_string(g_strPakPath[i].c_str());
189             ExtraQ3map2Args.push_string("\"");
190         }
191     }
192
193     // extra switches
194     if (g_disableEnginePath) {
195         ExtraQ3map2Args.push_string(" -fs_nobasepath ");
196     }
197
198     if (g_disableHomePath) {
199         ExtraQ3map2Args.push_string(" -fs_nohomepath ");
200     }
201
202     build_set_variable("ExtraQ3map2Args", ExtraQ3map2Args.c_str());
203
204     const char *mapname = Map_Name(g_map);
205     StringOutputStream name(256);
206     name << StringRange(mapname, path_get_filename_base_end(mapname)) << ".bsp";
207
208     build_set_variable("MapFile", mapname);
209     build_set_variable("BspFile", name.c_str());
210 }
211
212 void bsp_shutdown()
213 {
214     build_clear_variables();
215 }
216
217 class ArrayCommandListener : public CommandListener {
218     GPtrArray *m_array;
219 public:
220     ArrayCommandListener()
221     {
222         m_array = g_ptr_array_new();
223     }
224
225     ~ArrayCommandListener()
226     {
227         g_ptr_array_free(m_array, TRUE);
228     }
229
230     void execute(const char *command)
231     {
232         g_ptr_array_add(m_array, g_strdup(command));
233     }
234
235     GPtrArray *array() const
236     {
237         return m_array;
238     }
239 };
240
241 class BatchCommandListener : public CommandListener {
242     TextOutputStream &m_file;
243     std::size_t m_commandCount;
244     const char *m_outputRedirect;
245 public:
246     BatchCommandListener(TextOutputStream &file, const char *outputRedirect) : m_file(file), m_commandCount(0),
247                                                                                m_outputRedirect(outputRedirect)
248     {
249     }
250
251     void execute(const char *command)
252     {
253         m_file << command;
254         if (m_commandCount == 0) {
255             m_file << " > ";
256         } else {
257             m_file << " >> ";
258         }
259         m_file << "\"" << m_outputRedirect << "\"";
260         m_file << "\n";
261         ++m_commandCount;
262     }
263 };
264
265 bool Region_cameraValid()
266 {
267     Vector3 vOrig(vector3_snapped(Camera_getOrigin(*g_pParentWnd->GetCamWnd())));
268
269     for (int i = 0; i < 3; i++) {
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 {
280     // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=503
281     // make sure we don't attempt to region compile a map with the camera outside the region
282     if (region_active && !Region_cameraValid()) {
283         globalErrorStream() << "The camera must be in the region to start a region compile.\n";
284         return;
285     }
286
287     SaveMap();
288
289     if (Map_Unnamed(g_map)) {
290         globalOutputStream() << "build cancelled\n";
291         return;
292     }
293
294     if (g_SnapShots_Enabled && !Map_Unnamed(g_map) && Map_Modified(g_map)) {
295         Map_Snapshot();
296     }
297
298     if (region_active) {
299         const char *mapname = Map_Name(g_map);
300         StringOutputStream name(256);
301         name << StringRange(mapname, path_get_filename_base_end(mapname)) << ".reg";
302         Map_SaveRegion(name.c_str());
303     }
304
305     Pointfile_Delete();
306
307     bsp_init();
308
309     if (g_WatchBSP_Enabled) {
310         ArrayCommandListener listener;
311         build_run(name, listener);
312         // grab the file name for engine running
313         const char *fullname = Map_Name(g_map);
314         StringOutputStream bspname(64);
315         bspname << StringRange(path_get_filename_start(fullname), path_get_filename_base_end(fullname));
316         BuildMonitor_Run(listener.array(), bspname.c_str());
317     } else {
318         char junkpath[PATH_MAX];
319         strcpy(junkpath, SettingsPath_get());
320         strcat(junkpath, "junk.txt");
321
322         char batpath[PATH_MAX];
323 #if GDEF_OS_POSIX
324         strcpy(batpath, SettingsPath_get());
325         strcat(batpath, "qe3bsp.sh");
326 #elif GDEF_OS_WINDOWS
327         strcpy( batpath, SettingsPath_get() );
328         strcat( batpath, "qe3bsp.bat" );
329 #else
330 #error "unsupported platform"
331 #endif
332         bool written = false;
333         {
334             TextFileOutputStream batchFile(batpath);
335             if (!batchFile.failed()) {
336 #if GDEF_OS_POSIX
337                 batchFile << "#!/bin/sh \n\n";
338 #endif
339                 BatchCommandListener listener(batchFile, junkpath);
340                 build_run(name, listener);
341                 written = true;
342             }
343         }
344         if (written) {
345 #if GDEF_OS_POSIX
346             chmod(batpath, 0744);
347 #endif
348             globalOutputStream() << "Writing the compile script to '" << batpath << "'\n";
349             globalOutputStream() << "The build output will be saved in '" << junkpath << "'\n";
350             Q_Exec(batpath, NULL, NULL, true, false);
351         }
352     }
353
354     bsp_shutdown();
355 }
356
357 // =============================================================================
358 // Sys_ functions
359
360 void Sys_SetTitle(const char *text, bool modified)
361 {
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 {
376     ScreenUpdates_Disable("Processing...", "Please Wait");
377     GdkCursor *cursor = gdk_cursor_new(GDK_WATCH);
378     gdk_window_set_cursor(gtk_widget_get_window(MainFrame_getWindow()), cursor);
379     gdk_cursor_unref(cursor);
380     g_bWaitCursor = true;
381 }
382
383 void Sys_EndWait(void)
384 {
385     ScreenUpdates_Enable();
386     gdk_window_set_cursor(gtk_widget_get_window(MainFrame_getWindow()), 0);
387     g_bWaitCursor = false;
388 }
389
390 void Sys_Beep(void)
391 {
392     gdk_beep();
393 }