]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - contrib/hydratoolz/plugin.cpp
Merge branch 'fix-fast' into 'master'
[xonotic/netradiant.git] / contrib / hydratoolz / plugin.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 #include "plugin.h"
23 #include "version.h"
24
25 /*! \file plugin.cpp
26     \brief HydraToolz!
27
28     HydraToolz by Dominic Clifton - Hydra (Hydra@Hydras-World.com)
29
30     Overview
31     ========
32
33
34     Version History
35     ===============
36
37     v0.1 - 28/May/2002
38       - Initial version.
39
40
41     ToDo
42     ====
43
44  * Code it ? :)
45
46  */
47
48 // =============================================================================
49 // Globals
50
51 _QERFuncTable_1 g_FuncTable;
52 _QERFileSystemTable g_FileSystemTable;
53 _QEREntityTable g_EntityTable;
54
55 // cast to GtkWidget*
56 void *g_pMainWnd;
57
58 // =============================================================================
59 // Ripped from TexTool.cpp
60
61 static void dialog_button_callback(ui::Widget widget, gpointer data)
62 {
63     int *loop, *ret;
64
65     auto parent = widget.window();
66     loop = (int *) gtk_object_get_data(GTK_OBJECT(parent), "loop");
67     ret = (int *) gtk_object_get_data(GTK_OBJECT(parent), "ret");
68
69     *loop = 0;
70     *ret = gpointer_to_int(data);
71 }
72
73 static gint dialog_delete_callback(GtkWidget *widget, GdkEvent *event, gpointer data)
74 {
75     int *loop;
76
77     gtk_widget_hide(widget);
78     loop = (int *) gtk_object_get_data(GTK_OBJECT(widget), "loop");
79     *loop = 0;
80
81     return TRUE;
82 }
83
84 int DoMessageBox(const char *lpText, const char *lpCaption, guint32 uType)
85 {
86     GtkWidget *w, *hbox;
87     int mode = (uType & MB_TYPEMASK), ret, loop = 1;
88
89     auto window = ui::Window(ui::window_type::TOP);
90     window.connect("delete_event",
91                    G_CALLBACK(dialog_delete_callback), NULL);
92     window.connect("destroy",
93                    G_CALLBACK(gtk_widget_destroy), NULL);
94     gtk_window_set_title(window, lpCaption);
95     gtk_container_set_border_width(GTK_CONTAINER(window), 10);
96     gtk_object_set_data(GTK_OBJECT(window), "loop", &loop);
97     gtk_object_set_data(GTK_OBJECT(window), "ret", &ret);
98     gtk_widget_realize(window);
99
100     auto vbox = ui::VBox(FALSE, 10);
101     window.add(vbox);
102     vbox.show();
103
104     w = ui::Label(lpText);
105     vbox.pack_start(w, FALSE, FALSE, 2);
106     gtk_label_set_justify(GTK_LABEL(w), GTK_JUSTIFY_LEFT);
107     w.show();
108
109     w = gtk_hseparator_new();
110     vbox.pack_start(w, FALSE, FALSE, 2);
111     w.show();
112
113     hbox = ui::HBox(FALSE, 10);
114     vbox.pack_start(hbox, FALSE, FALSE, 2);
115     hbox.show();
116
117     if (mode == MB_OK) {
118         w = ui::Button("Ok");
119         hbox.pack_start(w, TRUE, TRUE, 0);
120         w.connect("clicked",
121                   G_CALLBACK(dialog_button_callback), GINT_TO_POINTER(IDOK));
122         gtk_widget_set_can_default(w, true);
123         gtk_widget_grab_default(w);
124         w.show();
125         ret = IDOK;
126     } else if (mode == MB_OKCANCEL) {
127         w = ui::Button("Ok");
128         hbox.pack_start(w, TRUE, TRUE, 0);
129         w.connect("clicked",
130                   G_CALLBACK(dialog_button_callback), GINT_TO_POINTER(IDOK));
131         gtk_widget_set_can_default(w, true);
132         gtk_widget_grab_default(w);
133         w.show();
134
135         w = ui::Button("Cancel");
136         hbox.pack_start(w, TRUE, TRUE, 0);
137         w.connect("clicked",
138                   G_CALLBACK(dialog_button_callback), GINT_TO_POINTER(IDCANCEL));
139         w.show();
140         ret = IDCANCEL;
141     } else if (mode == MB_YESNOCANCEL) {
142         w = ui::Button("Yes");
143         hbox.pack_start(w, TRUE, TRUE, 0);
144         w.connect("clicked",
145                   G_CALLBACK(dialog_button_callback), GINT_TO_POINTER(IDYES));
146         gtk_widget_set_can_default(w, true);
147         gtk_widget_grab_default(w);
148         w.show();
149
150         w = ui::Button("No");
151         hbox.pack_start(w, TRUE, TRUE, 0);
152         w.connect("clicked",
153                   G_CALLBACK(dialog_button_callback), GINT_TO_POINTER(IDNO));
154         w.show();
155
156         w = ui::Button("Cancel");
157         hbox.pack_start(w, TRUE, TRUE, 0);
158         w.connect("clicked",
159                   G_CALLBACK(dialog_button_callback), GINT_TO_POINTER(IDCANCEL));
160         w.show();
161         ret = IDCANCEL;
162     } else /* if (mode == MB_YESNO) */
163     {
164         w = ui::Button("Yes");
165         hbox.pack_start(w, TRUE, TRUE, 0);
166         w.connect("clicked",
167                   G_CALLBACK(dialog_button_callback), GINT_TO_POINTER(IDYES));
168         gtk_widget_set_can_default(w, true);
169         gtk_widget_grab_default(w);
170         w.show();
171
172         w = ui::Button("No");
173         hbox.pack_start(w, TRUE, TRUE, 0);
174         w.connect("clicked",
175                   G_CALLBACK(dialog_button_callback), GINT_TO_POINTER(IDNO));
176         w.show();
177         ret = IDNO;
178     }
179
180     window.show();
181     gtk_grab_add(window);
182
183     while (loop) {
184         gtk_main_iteration();
185     }
186
187     gtk_grab_remove(window);
188     window.destroy();
189
190     return ret;
191 }
192
193 // End of rip from TexTool.cpp
194
195 // =============================================================================
196 // Ripped from cmdlib.cpp
197
198 /*
199    ====================
200    Extract file parts
201    ====================
202  */
203 void ExtractFilePath(const char *path, char *dest)
204 {
205     const char *src;
206
207     src = path + strlen(path) - 1;
208
209 //
210 // back up until a \ or the start
211 //
212     while (src != path && *(src - 1) != '/' && *(src - 1) != '\\') {
213         src--;
214     }
215
216     memcpy(dest, path, src - path);
217     dest[src - path] = 0;
218 }
219
220 void ExtractFileName(const char *path, char *dest)
221 {
222     const char *src;
223
224     src = path + strlen(path) - 1;
225
226 //
227 // back up until a \ or the start
228 //
229     while (src != path && *(src - 1) != '/'
230            && *(src - 1) != '\\') {
231                src--;
232     }
233
234     while (*src) {
235         *dest++ = *src++;
236     }
237     *dest = 0;
238 }
239
240 void ConvertDOSToUnixName(char *dst, const char *src)
241 {
242     while (*src) {
243         if (*src == '\\') {
244             *dst = '/';
245         } else {
246             *dst = *src;
247         }
248         dst++;
249         src++;
250     }
251     *dst = 0;
252 }
253
254 // End of rip from cmdlib.cpp
255
256 // =============================================================================
257 // Actual Plugin Code
258
259 // get the wad name from the shader name (or an actual wadname) and add to a list of wad names making
260 // sure we don't add duplicates.
261
262 GSList *AddToWadList(GSList *wadlist, const char *shadername, const char *wad)
263 {
264     char tmpstr[QER_MAX_NAMELEN];
265     char *wadname;
266     if (!shadername && !wad) {
267         return wadlist;
268     }
269
270     if (shadername) {
271         if (strcmp(shadername, "color") == 0) {
272             return wadlist;
273         }
274         ExtractFilePath(shadername, tmpstr);
275         // Sys_Printf("checking: %s\n",shadername);
276
277         int l = strlen(tmpstr) - 1;
278
279         if (tmpstr[l] == '/' || tmpstr[l] == '\\') {
280             tmpstr[l] = 0;
281         } else {
282             Sys_Printf("WARNING: Unknown wad file for shader %s\n", shadername);
283             return wadlist;
284         }
285
286         ExtractFileName(tmpstr, tmpstr);
287
288         wadname = (char *) malloc(strlen(tmpstr) + 5);
289         sprintf(wadname, "%s.wad", tmpstr);
290     } else {
291         wadname = strdup(wad);
292     }
293
294     for (GSList *l = wadlist; l != NULL; l = l->next) {
295         if (string_equal_nocase((char *) l->data, wadname)) {
296             free(wadname);
297             return wadlist;
298         }
299     }
300
301     Sys_Printf("Adding Wad File to WAD list: %s (reason: ", wadname);
302     if (shadername) {
303         Sys_Printf("see shader \"%s\")\n", shadername);
304     } else {
305         Sys_Printf("already in WAD key. )\n");
306     }
307     return (g_slist_append(wadlist, wadname));
308 }
309
310 void UpdateWadKeyPair(void)
311 {
312     int i, nb;
313
314     char wads[2048]; // change to CString usage ?
315     wads[0] = 0;
316     char *p1, *p2;
317     entity_t *pEntity;
318     epair_t *pEpair;
319     GSList *wadlist = NULL;
320     face_t *f;
321     brush_t *b;
322     char cleanwadname[QER_MAX_NAMELEN];
323     const char *actualwad;
324
325
326     pEntity = (entity_t *) g_FuncTable.m_pfnGetEntityHandle(0); // get the worldspawn ent
327
328     Sys_Printf("Searching for in-use wad files...\n");
329     for (pEpair = pEntity->epairs; pEpair != NULL; pEpair = pEpair->next) {
330         if (string_equal_nocase(pEpair->key, "wad")) {
331             strcpy(wads, pEpair->value);
332             ConvertDOSToUnixName(wads, wads);
333
334             // ok, we got the list of ; delimited wads, now split it into a GSList that contains
335             // just the wad names themselves.
336
337             p1 = wads;
338
339             do {
340                 p2 = strchr(p1, ';');
341                 if (p2) {
342                     *p2 = 0; // swap the ; with a null terminator
343
344                 }
345                 if (strchr(p1, '/') || strchr(p1, '\\')) {
346                     ExtractFileName(p1, cleanwadname);
347                     wadlist = AddToWadList(wadlist, NULL, cleanwadname);
348                 } else {
349                     wadlist = AddToWadList(wadlist, NULL, p1);
350                 }
351                 if (p2) {
352                     p1 = p2 + 1; // point back to the remainder of the string
353                 } else {
354                     p1 = NULL; // make it so we exit the loop.
355
356                 }
357             } while (p1);
358
359             // ok, now we have a list of wads in GSList.
360             // now we need to add any new wadfiles (with their paths) to this list
361             // so scan all brushes and see what wads are in use
362             // FIXME: scan brushes only in the region ?
363
364             break; // we don't need to process any more key/pairs.
365         }
366     }
367
368     nb = g_FuncTable.m_pfnAllocateActiveBrushHandles();
369     for (i = 0; i < nb; i++) {
370         b = (brush_t *) g_FuncTable.m_pfnGetActiveBrushHandle(i);
371         if (b->patchBrush) { // patches in halflife ?
372             wadlist = AddToWadList(wadlist, b->pPatch->pShader->getName(), NULL);
373         } else {
374             for (f = b->brush_faces; f; f = f->next) {
375                 wadlist = AddToWadList(wadlist, f->pShader->getName(), NULL);
376             }
377         }
378     }
379     g_FuncTable.m_pfnReleaseActiveBrushHandles();
380
381     nb = g_FuncTable.m_pfnAllocateSelectedBrushHandles();
382     for (i = 0; i < nb; i++) {
383         b = (brush_t *) g_FuncTable.m_pfnGetSelectedBrushHandle(i);
384         if (b->patchBrush) { // patches in halflife ?
385             wadlist = AddToWadList(wadlist, b->pPatch->pShader->getName(), NULL);
386         } else {
387             for (f = b->brush_faces; f; f = f->next) {
388                 wadlist = AddToWadList(wadlist, f->pShader->getName(), NULL);
389             }
390         }
391     }
392     g_FuncTable.m_pfnReleaseSelectedBrushHandles();
393
394     // Now we have a complete list of wadnames (without paths) so we just have to turn this
395     // back to a ; delimited list.
396
397     wads[0] = 0;
398     while (wadlist) {
399         if (string_equal_nocase((char *) wadlist->data, "common-hydra.wad")) {
400             Sys_Printf("Skipping radiant-supplied wad file %s\n", (char *) wadlist->data);
401         } else {
402             if (wads[0]) {
403                 strcat(wads, ";");
404             }
405
406             actualwad = vfsGetFullPath((char *) wadlist->data);
407
408             if (actualwad) {
409                 strcat(wads, actualwad);
410             } else {
411                 Sys_Printf("WARNING: could not locate wad file %s\n", (char *) wadlist->data);
412                 strcat(wads, (char *) wadlist->data);
413             }
414         }
415
416         free(wadlist->data);
417         wadlist = g_slist_remove(wadlist, wadlist->data);
418     }
419
420     // store the wad list back in the worldspawn.
421     if (wads[0]) {
422         //free(pEpair->value);
423         //pEpair->value = strdup(wads);
424         SetKeyValue(pEntity, "wad", wads);
425     }
426
427 }
428
429 // =============================================================================
430 // PLUGIN INTERFACE STUFF
431
432 // plugin name
433 const char *PLUGIN_NAME = "Q3 Texture Tools";
434
435 // commands in the menu
436 const char *PLUGIN_COMMANDS = "About...;Create/Update WAD keypair";
437
438 const char *PLUGIN_ABOUT = "HydraToolz for GTKRadiant\n\n"
439         "By Hydra!";
440
441 extern "C" void *WINAPI
442
443 QERPlug_GetFuncTable()
444 {
445     return &g_FuncTable;
446 }
447
448 const char *QERPlug_Init(void *hApp, void *pWidget)
449 {
450     GtkWidget *pMainWidget = static_cast<GtkWidget *>( pWidget );
451
452     g_pMainWnd = pMainWidget;
453     memset(&g_FuncTable, 0, sizeof(_QERFuncTable_1));
454     g_FuncTable.m_nSize = sizeof(_QERFuncTable_1);
455     return "HydraToolz for GTKRadiant"; // do we need this ? hmmm
456 }
457
458 const char *QERPlug_GetName()
459 {
460     return (char *) PLUGIN_NAME;
461 }
462
463 const char *QERPlug_GetCommandList()
464 {
465     return PLUGIN_COMMANDS;
466 }
467
468 extern "C" void QERPlug_Dispatch(const char *p, vec3_t vMin, vec3_t vMax, bool bSingleBrush)
469 {
470     if (!strcmp(p, "Create/Update WAD keypair")) {
471         UpdateWadKeyPair();
472     } else if (!strcmp(p, "About...")) {
473         g_FuncTable.m_pfnMessageBox((GtkWidget *) NULL, PLUGIN_ABOUT, "About", eMB_OK);
474     }
475 }
476
477 // =============================================================================
478 // SYNAPSE
479
480 CSynapseServer *g_pSynapseServer = NULL;
481 CSynapseClientHydraToolz g_SynapseClient;
482
483 extern "C" CSynapseClient *SYNAPSE_DLL_EXPORT
484
485 Synapse_EnumerateInterfaces(const char *version, CSynapseServer *pServer)
486 {
487     if (strcmp(version, SYNAPSE_VERSION)) {
488         Syn_Printf("ERROR: synapse API version mismatch: should be '"
489         SYNAPSE_VERSION
490         "', got '%s'\n", version );
491         return NULL;
492     }
493     g_pSynapseServer = pServer;
494     g_pSynapseServer->IncRef();
495     Set_Syn_Printf(g_pSynapseServer->Get_Syn_Printf());
496
497     g_SynapseClient.AddAPI(PLUGIN_MAJOR, "HydraToolz", sizeof(_QERPluginTable));
498     g_SynapseClient.AddAPI(RADIANT_MAJOR, NULL, sizeof(g_FuncTable), SYN_REQUIRE, &g_FuncTable);
499     g_SynapseClient.AddAPI(VFS_MAJOR, "wad", sizeof(g_FileSystemTable), SYN_REQUIRE, &g_FileSystemTable);
500     g_SynapseClient.AddAPI(ENTITY_MAJOR, NULL, sizeof(g_EntityTable), SYN_REQUIRE, &g_EntityTable);
501     return &g_SynapseClient;
502 }
503
504 bool CSynapseClientHydraToolz::RequestAPI(APIDescriptor_t *pAPI)
505 {
506     if (!strcmp(pAPI->major_name, PLUGIN_MAJOR)) {
507         _QERPluginTable *pTable = static_cast<_QERPluginTable *>( pAPI->mpTable );
508         pTable->m_pfnQERPlug_Init = QERPlug_Init;
509         pTable->m_pfnQERPlug_GetName = QERPlug_GetName;
510         pTable->m_pfnQERPlug_GetCommandList = QERPlug_GetCommandList;
511         pTable->m_pfnQERPlug_Dispatch = QERPlug_Dispatch;
512         return true;
513     }
514
515     Syn_Printf("ERROR: RequestAPI( '%s' ) not found in '%s'\n", pAPI->major_name, GetInfo());
516     return false;
517 }
518
519 const char *CSynapseClientHydraToolz::GetInfo()
520 {
521     return "HydraToolz plugin built " __DATE__ " "
522     RADIANT_VERSION;
523 }