2 Copyright (C) 1999-2006 Id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
5 This file is part of GtkRadiant.
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.
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.
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
27 HydraToolz by Dominic Clifton - Hydra (Hydra@Hydras-World.com)
47 // =============================================================================
50 _QERFuncTable_1 g_FuncTable;
51 _QERFileSystemTable g_FileSystemTable;
52 _QEREntityTable g_EntityTable;
57 // =============================================================================
58 // Ripped from TexTool.cpp
60 static void dialog_button_callback( ui::Widget widget, gpointer data ){
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" );
68 *ret = gpointer_to_int( data );
71 static gint dialog_delete_callback( GtkWidget *widget, GdkEvent* event, gpointer data ){
74 gtk_widget_hide( widget );
75 loop = (int*)gtk_object_get_data( GTK_OBJECT( widget ), "loop" );
81 int DoMessageBox( const char* lpText, const char* lpCaption, guint32 uType ){
83 int mode = ( uType & MB_TYPEMASK ), ret, loop = 1;
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 );
96 auto vbox = ui::VBox( FALSE, 10 );
100 w = ui::Label( lpText );
101 vbox.pack_start( w, FALSE, FALSE, 2 );
102 gtk_label_set_justify( GTK_LABEL( w ), GTK_JUSTIFY_LEFT );
105 w = gtk_hseparator_new();
106 vbox.pack_start( w, FALSE, FALSE, 2 );
109 hbox = ui::HBox( FALSE, 10 );
110 vbox.pack_start( hbox, FALSE, FALSE, 2 );
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 );
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 );
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 ) );
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 );
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 ) );
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 ) );
161 else /* if (mode == MB_YESNO) */
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 );
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 ) );
180 gtk_grab_add( window );
183 gtk_main_iteration();
185 gtk_grab_remove( window );
191 // End of rip from TexTool.cpp
193 // =============================================================================
194 // Ripped from cmdlib.cpp
201 void ExtractFilePath( const char *path, char *dest ){
204 src = path + strlen( path ) - 1;
207 // back up until a \ or the start
209 while ( src != path && *( src - 1 ) != '/' && *( src - 1 ) != '\\' )
212 memcpy( dest, path, src - path );
213 dest[src - path] = 0;
216 void ExtractFileName( const char *path, char *dest ){
219 src = path + strlen( path ) - 1;
222 // back up until a \ or the start
224 while ( src != path && *( src - 1 ) != '/'
225 && *( src - 1 ) != '\\' )
235 void ConvertDOSToUnixName( char *dst, const char *src ){
238 if ( *src == '\\' ) {
249 // End of rip from cmdlib.cpp
251 // =============================================================================
252 // Actual Plugin Code
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.
257 GSList *AddToWadList( GSList *wadlist, const char *shadername, const char *wad ){
258 char tmpstr[QER_MAX_NAMELEN];
260 if ( !shadername && !wad ) {
265 if ( strcmp( shadername,"color" ) == 0 ) {
268 ExtractFilePath( shadername,tmpstr );
269 // Sys_Printf("checking: %s\n",shadername);
271 int l = strlen( tmpstr ) - 1;
273 if ( tmpstr[l] == '/' || tmpstr[l] == '\\' ) {
278 Sys_Printf( "WARNING: Unknown wad file for shader %s\n",shadername );
282 ExtractFileName( tmpstr,tmpstr );
284 wadname = (char *)malloc( strlen( tmpstr ) + 5 );
285 sprintf( wadname,"%s.wad",tmpstr );
289 wadname = strdup( wad );
292 for ( GSList *l = wadlist; l != NULL ; l = l->next )
294 if ( string_equal_nocase( (char *)l->data,wadname ) ) {
300 Sys_Printf( "Adding Wad File to WAD list: %s (reason: ",wadname );
302 Sys_Printf( "see shader \"%s\")\n", shadername );
305 Sys_Printf( "already in WAD key. )\n" );
307 return ( g_slist_append( wadlist, wadname ) );
310 void UpdateWadKeyPair( void ){
313 char wads[2048]; // change to CString usage ?
318 GSList *wadlist = NULL;
321 char cleanwadname[QER_MAX_NAMELEN];
322 const char *actualwad;
325 pEntity = (entity_t *)g_FuncTable.m_pfnGetEntityHandle( 0 ); // get the worldspawn ent
327 Sys_Printf( "Searching for in-use wad files...\n" );
328 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 );
334 // ok, we got the list of ; delimited wads, now split it into a GSList that contains
335 // just the wad names themselves.
341 p2 = strchr( p1,';' );
343 *p2 = 0; // swap the ; with a null terminator
346 if ( strchr( p1,'/' ) || strchr( p1,'\\' ) ) {
347 ExtractFileName( p1,cleanwadname );
348 wadlist = AddToWadList( wadlist, NULL, cleanwadname );
352 wadlist = AddToWadList( wadlist, NULL, p1 );
355 p1 = p2 + 1; // point back to the remainder of the string
358 p1 = NULL; // make it so we exit the loop.
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 ?
368 break; // we don't need to process any more key/pairs.
372 nb = g_FuncTable.m_pfnAllocateActiveBrushHandles();
373 for ( i = 0; i < nb; i++ )
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 );
381 for ( f = b->brush_faces ; f ; f = f->next )
383 wadlist = AddToWadList( wadlist, f->pShader->getName(),NULL );
387 g_FuncTable.m_pfnReleaseActiveBrushHandles();
389 nb = g_FuncTable.m_pfnAllocateSelectedBrushHandles();
390 for ( i = 0; i < nb; i++ )
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 );
398 for ( f = b->brush_faces ; f ; f = f->next )
400 wadlist = AddToWadList( wadlist, f->pShader->getName(),NULL );
404 g_FuncTable.m_pfnReleaseSelectedBrushHandles();
406 // Now we have a complete list of wadnames (without paths) so we just have to turn this
407 // back to a ; delimited list.
412 if ( string_equal_nocase( (char *)wadlist->data,"common-hydra.wad" ) ) {
413 Sys_Printf( "Skipping radiant-supplied wad file %s\n",(char *)wadlist->data );
421 actualwad = vfsGetFullPath( (char *)wadlist->data );
424 strcat( wads, actualwad );
428 Sys_Printf( "WARNING: could not locate wad file %s\n",(char *)wadlist->data );
429 strcat( wads, (char *)wadlist->data );
433 free( wadlist->data );
434 wadlist = g_slist_remove( wadlist, wadlist->data );
437 // store the wad list back in the worldspawn.
439 //free(pEpair->value);
440 //pEpair->value = strdup(wads);
441 SetKeyValue( pEntity, "wad", wads );
446 // =============================================================================
447 // PLUGIN INTERFACE STUFF
450 const char *PLUGIN_NAME = "Q3 Texture Tools";
452 // commands in the menu
453 const char *PLUGIN_COMMANDS = "About...;Create/Update WAD keypair";
455 const char *PLUGIN_ABOUT = "HydraToolz for GTKRadiant\n\n"
458 extern "C" void* WINAPI QERPlug_GetFuncTable(){
462 const char* QERPlug_Init( void* hApp, void *pWidget ){
463 GtkWidget* pMainWidget = static_cast<GtkWidget*>( pWidget );
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
471 const char* QERPlug_GetName(){
472 return (char*)PLUGIN_NAME;
475 const char* QERPlug_GetCommandList(){
476 return PLUGIN_COMMANDS;
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" ) ) {
483 else if ( !strcmp( p, "About..." ) ) {
484 g_FuncTable.m_pfnMessageBox( (GtkWidget*)NULL, PLUGIN_ABOUT, "About", eMB_OK );
488 // =============================================================================
491 CSynapseServer* g_pSynapseServer = NULL;
492 CSynapseClientHydraToolz g_SynapseClient;
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 );
499 g_pSynapseServer = pServer;
500 g_pSynapseServer->IncRef();
501 Set_Syn_Printf( g_pSynapseServer->Get_Syn_Printf() );
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;
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;
520 Syn_Printf( "ERROR: RequestAPI( '%s' ) not found in '%s'\n", pAPI->major_name, GetInfo() );
524 const char* CSynapseClientHydraToolz::GetInfo(){
525 return "HydraToolz plugin built " __DATE__ " " RADIANT_VERSION;