/* Copyright (C) 1999-2006 Id Software, Inc. and contributors. For a list of contributors, see the accompanying CONTRIBUTORS file. This file is part of GtkRadiant. GtkRadiant is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GtkRadiant is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "plugin.h" /*! \file plugin.cpp \brief HydraToolz! HydraToolz by Dominic Clifton - Hydra (Hydra@Hydras-World.com) Overview ======== Version History =============== v0.1 - 28/May/2002 - Initial version. ToDo ==== * Code it ? :) */ // ============================================================================= // Globals _QERFuncTable_1 g_FuncTable; _QERFileSystemTable g_FileSystemTable; _QEREntityTable g_EntityTable; // cast to GtkWidget* void *g_pMainWnd; // ============================================================================= // Ripped from TexTool.cpp static void dialog_button_callback( ui::Widget widget, gpointer data ){ int *loop, *ret; auto parent = widget.window(); loop = (int*)gtk_object_get_data( GTK_OBJECT( parent ), "loop" ); ret = (int*)gtk_object_get_data( GTK_OBJECT( parent ), "ret" ); *loop = 0; *ret = gpointer_to_int( data ); } static gint dialog_delete_callback( GtkWidget *widget, GdkEvent* event, gpointer data ){ int *loop; gtk_widget_hide( widget ); loop = (int*)gtk_object_get_data( GTK_OBJECT( widget ), "loop" ); *loop = 0; return TRUE; } int DoMessageBox( const char* lpText, const char* lpCaption, guint32 uType ){ GtkWidget *w, *hbox; int mode = ( uType & MB_TYPEMASK ), ret, loop = 1; auto window = ui::Window( ui::window_type::TOP ); window.connect( "delete_event", G_CALLBACK( dialog_delete_callback ), NULL ); window.connect( "destroy", G_CALLBACK( gtk_widget_destroy ), NULL ); gtk_window_set_title( window, lpCaption ); gtk_container_set_border_width( GTK_CONTAINER( window ), 10 ); gtk_object_set_data( GTK_OBJECT( window ), "loop", &loop ); gtk_object_set_data( GTK_OBJECT( window ), "ret", &ret ); gtk_widget_realize( window ); auto vbox = ui::VBox( FALSE, 10 ); window.add(vbox); vbox.show(); w = ui::Label( lpText ); vbox.pack_start( w, FALSE, FALSE, 2 ); gtk_label_set_justify( GTK_LABEL( w ), GTK_JUSTIFY_LEFT ); w.show(); w = gtk_hseparator_new(); vbox.pack_start( w, FALSE, FALSE, 2 ); w.show(); hbox = ui::HBox( FALSE, 10 ); vbox.pack_start( hbox, FALSE, FALSE, 2 ); hbox.show(); if ( mode == MB_OK ) { w = ui::Button( "Ok" ); hbox.pack_start( w, TRUE, TRUE, 0 ); w.connect( "clicked", G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDOK ) ); gtk_widget_set_can_default( w, true ); gtk_widget_grab_default( w ); w.show(); ret = IDOK; } else if ( mode == MB_OKCANCEL ) { w = ui::Button( "Ok" ); hbox.pack_start( w, TRUE, TRUE, 0 ); w.connect( "clicked", G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDOK ) ); gtk_widget_set_can_default( w, true ); gtk_widget_grab_default( w ); w.show(); w = ui::Button( "Cancel" ); hbox.pack_start( w, TRUE, TRUE, 0 ); w.connect( "clicked", G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDCANCEL ) ); w.show(); ret = IDCANCEL; } else if ( mode == MB_YESNOCANCEL ) { w = ui::Button( "Yes" ); hbox.pack_start( w, TRUE, TRUE, 0 ); w.connect( "clicked", G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDYES ) ); gtk_widget_set_can_default( w, true ); gtk_widget_grab_default( w ); w.show(); w = ui::Button( "No" ); hbox.pack_start( w, TRUE, TRUE, 0 ); w.connect( "clicked", G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDNO ) ); w.show(); w = ui::Button( "Cancel" ); hbox.pack_start( w, TRUE, TRUE, 0 ); w.connect( "clicked", G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDCANCEL ) ); w.show(); ret = IDCANCEL; } else /* if (mode == MB_YESNO) */ { w = ui::Button( "Yes" ); hbox.pack_start( w, TRUE, TRUE, 0 ); w.connect( "clicked", G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDYES ) ); gtk_widget_set_can_default( w, true ); gtk_widget_grab_default( w ); w.show(); w = ui::Button( "No" ); hbox.pack_start( w, TRUE, TRUE, 0 ); w.connect( "clicked", G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDNO ) ); w.show(); ret = IDNO; } window.show(); gtk_grab_add( window ); while ( loop ) gtk_main_iteration(); gtk_grab_remove( window ); window.destroy(); return ret; } // End of rip from TexTool.cpp // ============================================================================= // Ripped from cmdlib.cpp /* ==================== Extract file parts ==================== */ void ExtractFilePath( const char *path, char *dest ){ const char *src; src = path + strlen( path ) - 1; // // back up until a \ or the start // while ( src != path && *( src - 1 ) != '/' && *( src - 1 ) != '\\' ) src--; memcpy( dest, path, src - path ); dest[src - path] = 0; } void ExtractFileName( const char *path, char *dest ){ const char *src; src = path + strlen( path ) - 1; // // back up until a \ or the start // while ( src != path && *( src - 1 ) != '/' && *( src - 1 ) != '\\' ) src--; while ( *src ) { *dest++ = *src++; } *dest = 0; } void ConvertDOSToUnixName( char *dst, const char *src ){ while ( *src ) { if ( *src == '\\' ) { *dst = '/'; } else{ *dst = *src; } dst++; src++; } *dst = 0; } // End of rip from cmdlib.cpp // ============================================================================= // Actual Plugin Code // get the wad name from the shader name (or an actual wadname) and add to a list of wad names making // sure we don't add duplicates. GSList *AddToWadList( GSList *wadlist, const char *shadername, const char *wad ){ char tmpstr[QER_MAX_NAMELEN]; char *wadname; if ( !shadername && !wad ) { return wadlist; } if ( shadername ) { if ( strcmp( shadername,"color" ) == 0 ) { return wadlist; } ExtractFilePath( shadername,tmpstr ); // Sys_Printf("checking: %s\n",shadername); int l = strlen( tmpstr ) - 1; if ( tmpstr[l] == '/' || tmpstr[l] == '\\' ) { tmpstr[l] = 0; } else { Sys_Printf( "WARNING: Unknown wad file for shader %s\n",shadername ); return wadlist; } ExtractFileName( tmpstr,tmpstr ); wadname = (char *)malloc( strlen( tmpstr ) + 5 ); sprintf( wadname,"%s.wad",tmpstr ); } else { wadname = strdup( wad ); } for ( GSList *l = wadlist; l != NULL ; l = l->next ) { if ( string_equal_nocase( (char *)l->data,wadname ) ) { free( wadname ); return wadlist; } } Sys_Printf( "Adding Wad File to WAD list: %s (reason: ",wadname ); if ( shadername ) { Sys_Printf( "see shader \"%s\")\n", shadername ); } else{ Sys_Printf( "already in WAD key. )\n" ); } return ( g_slist_append( wadlist, wadname ) ); } void UpdateWadKeyPair( void ){ int i,nb; char wads[2048]; // change to CString usage ? wads[0] = 0; char *p1,*p2; entity_t *pEntity; epair_t *pEpair; GSList *wadlist = NULL; face_t *f; brush_t *b; char cleanwadname[QER_MAX_NAMELEN]; const char *actualwad; pEntity = (entity_t *)g_FuncTable.m_pfnGetEntityHandle( 0 ); // get the worldspawn ent Sys_Printf( "Searching for in-use wad files...\n" ); for ( pEpair = pEntity->epairs; pEpair != NULL; pEpair = pEpair->next ) { if ( string_equal_nocase( pEpair->key,"wad" ) ) { strcpy( wads,pEpair->value ); ConvertDOSToUnixName( wads,wads ); // ok, we got the list of ; delimited wads, now split it into a GSList that contains // just the wad names themselves. p1 = wads; do { p2 = strchr( p1,';' ); if ( p2 ) { *p2 = 0; // swap the ; with a null terminator } if ( strchr( p1,'/' ) || strchr( p1,'\\' ) ) { ExtractFileName( p1,cleanwadname ); wadlist = AddToWadList( wadlist, NULL, cleanwadname ); } else { wadlist = AddToWadList( wadlist, NULL, p1 ); } if ( p2 ) { p1 = p2 + 1; // point back to the remainder of the string } else{ p1 = NULL; // make it so we exit the loop. } } while ( p1 ); // ok, now we have a list of wads in GSList. // now we need to add any new wadfiles (with their paths) to this list // so scan all brushes and see what wads are in use // FIXME: scan brushes only in the region ? break; // we don't need to process any more key/pairs. } } nb = g_FuncTable.m_pfnAllocateActiveBrushHandles(); for ( i = 0; i < nb; i++ ) { b = (brush_t *)g_FuncTable.m_pfnGetActiveBrushHandle( i ); if ( b->patchBrush ) { // patches in halflife ? wadlist = AddToWadList( wadlist, b->pPatch->pShader->getName(),NULL ); } else { for ( f = b->brush_faces ; f ; f = f->next ) { wadlist = AddToWadList( wadlist, f->pShader->getName(),NULL ); } } } g_FuncTable.m_pfnReleaseActiveBrushHandles(); nb = g_FuncTable.m_pfnAllocateSelectedBrushHandles(); for ( i = 0; i < nb; i++ ) { b = (brush_t *)g_FuncTable.m_pfnGetSelectedBrushHandle( i ); if ( b->patchBrush ) { // patches in halflife ? wadlist = AddToWadList( wadlist, b->pPatch->pShader->getName(),NULL ); } else { for ( f = b->brush_faces ; f ; f = f->next ) { wadlist = AddToWadList( wadlist, f->pShader->getName(),NULL ); } } } g_FuncTable.m_pfnReleaseSelectedBrushHandles(); // Now we have a complete list of wadnames (without paths) so we just have to turn this // back to a ; delimited list. wads[0] = 0; while ( wadlist ) { if ( string_equal_nocase( (char *)wadlist->data,"common-hydra.wad" ) ) { Sys_Printf( "Skipping radiant-supplied wad file %s\n",(char *)wadlist->data ); } else { if ( wads[0] ) { strcat( wads,";" ); } actualwad = vfsGetFullPath( (char *)wadlist->data ); if ( actualwad ) { strcat( wads, actualwad ); } else { Sys_Printf( "WARNING: could not locate wad file %s\n",(char *)wadlist->data ); strcat( wads, (char *)wadlist->data ); } } free( wadlist->data ); wadlist = g_slist_remove( wadlist, wadlist->data ); } // store the wad list back in the worldspawn. if ( wads[0] ) { //free(pEpair->value); //pEpair->value = strdup(wads); SetKeyValue( pEntity, "wad", wads ); } } // ============================================================================= // PLUGIN INTERFACE STUFF // plugin name const char *PLUGIN_NAME = "Q3 Texture Tools"; // commands in the menu const char *PLUGIN_COMMANDS = "About...;Create/Update WAD keypair"; const char *PLUGIN_ABOUT = "HydraToolz for GTKRadiant\n\n" "By Hydra!"; extern "C" void* WINAPI QERPlug_GetFuncTable(){ return &g_FuncTable; } const char* QERPlug_Init( void* hApp, void *pWidget ){ GtkWidget* pMainWidget = static_cast( pWidget ); g_pMainWnd = pMainWidget; memset( &g_FuncTable, 0, sizeof( _QERFuncTable_1 ) ); g_FuncTable.m_nSize = sizeof( _QERFuncTable_1 ); return "HydraToolz for GTKRadiant"; // do we need this ? hmmm } const char* QERPlug_GetName(){ return (char*)PLUGIN_NAME; } const char* QERPlug_GetCommandList(){ return PLUGIN_COMMANDS; } extern "C" void QERPlug_Dispatch( const char* p, vec3_t vMin, vec3_t vMax, bool bSingleBrush ){ if ( !strcmp( p, "Create/Update WAD keypair" ) ) { UpdateWadKeyPair(); } else if ( !strcmp( p, "About..." ) ) { g_FuncTable.m_pfnMessageBox( (GtkWidget*)NULL, PLUGIN_ABOUT, "About", eMB_OK ); } } // ============================================================================= // SYNAPSE CSynapseServer* g_pSynapseServer = NULL; CSynapseClientHydraToolz g_SynapseClient; extern "C" CSynapseClient * SYNAPSE_DLL_EXPORT Synapse_EnumerateInterfaces( const char *version, CSynapseServer *pServer ){ if ( strcmp( version, SYNAPSE_VERSION ) ) { Syn_Printf( "ERROR: synapse API version mismatch: should be '" SYNAPSE_VERSION "', got '%s'\n", version ); return NULL; } g_pSynapseServer = pServer; g_pSynapseServer->IncRef(); Set_Syn_Printf( g_pSynapseServer->Get_Syn_Printf() ); g_SynapseClient.AddAPI( PLUGIN_MAJOR, "HydraToolz", sizeof( _QERPluginTable ) ); g_SynapseClient.AddAPI( RADIANT_MAJOR, NULL, sizeof( g_FuncTable ), SYN_REQUIRE, &g_FuncTable ); g_SynapseClient.AddAPI( VFS_MAJOR, "wad", sizeof( g_FileSystemTable ), SYN_REQUIRE, &g_FileSystemTable ); g_SynapseClient.AddAPI( ENTITY_MAJOR, NULL, sizeof( g_EntityTable ), SYN_REQUIRE, &g_EntityTable ); return &g_SynapseClient; } bool CSynapseClientHydraToolz::RequestAPI( APIDescriptor_t *pAPI ){ if ( !strcmp( pAPI->major_name, PLUGIN_MAJOR ) ) { _QERPluginTable *pTable = static_cast<_QERPluginTable*>( pAPI->mpTable ); pTable->m_pfnQERPlug_Init = QERPlug_Init; pTable->m_pfnQERPlug_GetName = QERPlug_GetName; pTable->m_pfnQERPlug_GetCommandList = QERPlug_GetCommandList; pTable->m_pfnQERPlug_Dispatch = QERPlug_Dispatch; return true; } Syn_Printf( "ERROR: RequestAPI( '%s' ) not found in '%s'\n", pAPI->major_name, GetInfo() ); return false; } const char* CSynapseClientHydraToolz::GetInfo(){ return "HydraToolz plugin built " __DATE__ " " RADIANT_VERSION; }