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