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