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
28 HydraToolz by Dominic Clifton - Hydra (Hydra@Hydras-World.com)
48 // =============================================================================
51 _QERFuncTable_1 g_FuncTable;
52 _QERFileSystemTable g_FileSystemTable;
53 _QEREntityTable g_EntityTable;
58 // =============================================================================
59 // Ripped from TexTool.cpp
61 static void dialog_button_callback( GtkWidget *widget, gpointer data ){
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" );
70 *ret = gpointer_to_int( data );
73 static gint dialog_delete_callback( GtkWidget *widget, GdkEvent* event, gpointer data ){
76 gtk_widget_hide( widget );
77 loop = (int*)gtk_object_get_data( GTK_OBJECT( widget ), "loop" );
83 int DoMessageBox( const char* lpText, const char* lpCaption, guint32 uType ){
85 int mode = ( uType & MB_TYPEMASK ), ret, loop = 1;
87 auto window = ui::Window( ui::window_type::TOP );
88 window.connect( "delete_event",
89 G_CALLBACK( dialog_delete_callback ), NULL );
90 window.connect( "destroy",
91 G_CALLBACK( gtk_widget_destroy ), NULL );
92 gtk_window_set_title( GTK_WINDOW( window ), lpCaption );
93 gtk_container_set_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 );
98 auto vbox = ui::VBox( FALSE, 10 );
102 w = ui::Label( lpText );
103 vbox.pack_start( w, FALSE, FALSE, 2 );
104 gtk_label_set_justify( GTK_LABEL( w ), GTK_JUSTIFY_LEFT );
105 gtk_widget_show( w );
107 w = gtk_hseparator_new();
108 vbox.pack_start( w, FALSE, FALSE, 2 );
109 gtk_widget_show( w );
111 hbox = ui::HBox( FALSE, 10 );
112 vbox.pack_start( hbox, FALSE, FALSE, 2 );
113 gtk_widget_show( hbox );
115 if ( mode == MB_OK ) {
116 w = ui::Button( "Ok" );
117 hbox.pack_start( w, TRUE, TRUE, 0 );
118 w.connect( "clicked",
119 G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDOK ) );
120 gtk_widget_set_can_default( w, true );
121 gtk_widget_grab_default( w );
122 gtk_widget_show( w );
125 else if ( mode == MB_OKCANCEL ) {
126 w = ui::Button( "Ok" );
127 hbox.pack_start( w, TRUE, TRUE, 0 );
128 w.connect( "clicked",
129 G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDOK ) );
130 gtk_widget_set_can_default( w, true );
131 gtk_widget_grab_default( w );
132 gtk_widget_show( w );
134 w = ui::Button( "Cancel" );
135 hbox.pack_start( w, TRUE, TRUE, 0 );
136 w.connect( "clicked",
137 G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDCANCEL ) );
138 gtk_widget_show( w );
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 gtk_widget_show( w );
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 gtk_widget_show( w );
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 gtk_widget_show( w );
163 else /* if (mode == MB_YESNO) */
165 w = ui::Button( "Yes" );
166 hbox.pack_start( w, TRUE, TRUE, 0 );
167 w.connect( "clicked",
168 G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDYES ) );
169 gtk_widget_set_can_default( w, true );
170 gtk_widget_grab_default( w );
171 gtk_widget_show( w );
173 w = ui::Button( "No" );
174 hbox.pack_start( w, TRUE, TRUE, 0 );
175 w.connect( "clicked",
176 G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDNO ) );
177 gtk_widget_show( w );
181 gtk_widget_show( window );
182 gtk_grab_add( window );
185 gtk_main_iteration();
187 gtk_grab_remove( window );
193 // End of rip from TexTool.cpp
195 // =============================================================================
196 // Ripped from cmdlib.cpp
203 void ExtractFilePath( const char *path, char *dest ){
206 src = path + strlen( path ) - 1;
209 // back up until a \ or the start
211 while ( src != path && *( src - 1 ) != '/' && *( src - 1 ) != '\\' )
214 memcpy( dest, path, src - path );
215 dest[src - path] = 0;
218 void ExtractFileName( const char *path, char *dest ){
221 src = path + strlen( path ) - 1;
224 // back up until a \ or the start
226 while ( src != path && *( src - 1 ) != '/'
227 && *( src - 1 ) != '\\' )
237 void ConvertDOSToUnixName( char *dst, const char *src ){
240 if ( *src == '\\' ) {
251 // End of rip from cmdlib.cpp
253 // =============================================================================
254 // Actual Plugin Code
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.
259 GSList *AddToWadList( GSList *wadlist, const char *shadername, const char *wad ){
260 char tmpstr[QER_MAX_NAMELEN];
262 if ( !shadername && !wad ) {
267 if ( strcmp( shadername,"color" ) == 0 ) {
270 ExtractFilePath( shadername,tmpstr );
271 // Sys_Printf("checking: %s\n",shadername);
273 int l = strlen( tmpstr ) - 1;
275 if ( tmpstr[l] == '/' || tmpstr[l] == '\\' ) {
280 Sys_Printf( "WARNING: Unknown wad file for shader %s\n",shadername );
284 ExtractFileName( tmpstr,tmpstr );
286 wadname = (char *)malloc( strlen( tmpstr ) + 5 );
287 sprintf( wadname,"%s.wad",tmpstr );
291 wadname = strdup( wad );
294 for ( GSList *l = wadlist; l != NULL ; l = l->next )
296 if ( string_equal_nocase( (char *)l->data,wadname ) ) {
302 Sys_Printf( "Adding Wad File to WAD list: %s (reason: ",wadname );
304 Sys_Printf( "see shader \"%s\")\n", shadername );
307 Sys_Printf( "already in WAD key. )\n" );
309 return ( g_slist_append( wadlist, wadname ) );
312 void UpdateWadKeyPair( void ){
315 char wads[2048]; // change to CString usage ?
320 GSList *wadlist = NULL;
323 char cleanwadname[QER_MAX_NAMELEN];
324 const char *actualwad;
327 pEntity = (entity_t *)g_FuncTable.m_pfnGetEntityHandle( 0 ); // get the worldspawn ent
329 Sys_Printf( "Searching for in-use wad files...\n" );
330 for ( pEpair = pEntity->epairs; pEpair != NULL; pEpair = pEpair->next )
332 if ( string_equal_nocase( pEpair->key,"wad" ) ) {
333 strcpy( wads,pEpair->value );
334 ConvertDOSToUnixName( wads,wads );
336 // ok, we got the list of ; delimited wads, now split it into a GSList that contains
337 // just the wad names themselves.
343 p2 = strchr( p1,';' );
345 *p2 = 0; // swap the ; with a null terminator
348 if ( strchr( p1,'/' ) || strchr( p1,'\\' ) ) {
349 ExtractFileName( p1,cleanwadname );
350 wadlist = AddToWadList( wadlist, NULL, cleanwadname );
354 wadlist = AddToWadList( wadlist, NULL, p1 );
357 p1 = p2 + 1; // point back to the remainder of the string
360 p1 = NULL; // make it so we exit the loop.
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 ?
370 break; // we don't need to process any more key/pairs.
374 nb = g_FuncTable.m_pfnAllocateActiveBrushHandles();
375 for ( i = 0; i < nb; i++ )
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 );
383 for ( f = b->brush_faces ; f ; f = f->next )
385 wadlist = AddToWadList( wadlist, f->pShader->getName(),NULL );
389 g_FuncTable.m_pfnReleaseActiveBrushHandles();
391 nb = g_FuncTable.m_pfnAllocateSelectedBrushHandles();
392 for ( i = 0; i < nb; i++ )
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 );
400 for ( f = b->brush_faces ; f ; f = f->next )
402 wadlist = AddToWadList( wadlist, f->pShader->getName(),NULL );
406 g_FuncTable.m_pfnReleaseSelectedBrushHandles();
408 // Now we have a complete list of wadnames (without paths) so we just have to turn this
409 // back to a ; delimited list.
414 if ( string_equal_nocase( (char *)wadlist->data,"common-hydra.wad" ) ) {
415 Sys_Printf( "Skipping radiant-supplied wad file %s\n",(char *)wadlist->data );
423 actualwad = vfsGetFullPath( (char *)wadlist->data );
426 strcat( wads, actualwad );
430 Sys_Printf( "WARNING: could not locate wad file %s\n",(char *)wadlist->data );
431 strcat( wads, (char *)wadlist->data );
435 free( wadlist->data );
436 wadlist = g_slist_remove( wadlist, wadlist->data );
439 // store the wad list back in the worldspawn.
441 //free(pEpair->value);
442 //pEpair->value = strdup(wads);
443 SetKeyValue( pEntity, "wad", wads );
448 // =============================================================================
449 // PLUGIN INTERFACE STUFF
452 const char *PLUGIN_NAME = "Q3 Texture Tools";
454 // commands in the menu
455 const char *PLUGIN_COMMANDS = "About...;Create/Update WAD keypair";
457 const char *PLUGIN_ABOUT = "HydraToolz for GTKRadiant\n\n"
460 extern "C" void* WINAPI QERPlug_GetFuncTable(){
464 const char* QERPlug_Init( void* hApp, void *pWidget ){
465 GtkWidget* pMainWidget = static_cast<GtkWidget*>( pWidget );
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
473 const char* QERPlug_GetName(){
474 return (char*)PLUGIN_NAME;
477 const char* QERPlug_GetCommandList(){
478 return PLUGIN_COMMANDS;
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" ) ) {
485 else if ( !strcmp( p, "About..." ) ) {
486 g_FuncTable.m_pfnMessageBox( (GtkWidget*)NULL, PLUGIN_ABOUT, "About", eMB_OK );
490 // =============================================================================
493 CSynapseServer* g_pSynapseServer = NULL;
494 CSynapseClientHydraToolz g_SynapseClient;
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 );
501 g_pSynapseServer = pServer;
502 g_pSynapseServer->IncRef();
503 Set_Syn_Printf( g_pSynapseServer->Get_Syn_Printf() );
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;
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;
522 Syn_Printf( "ERROR: RequestAPI( '%s' ) not found in '%s'\n", pAPI->major_name, GetInfo() );
526 const char* CSynapseClientHydraToolz::GetInfo(){
527 return "HydraToolz plugin built " __DATE__ " " RADIANT_VERSION;