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( ui::Widget widget, gpointer data ){
64 auto parent = widget.window();
65 loop = (int*)gtk_object_get_data( GTK_OBJECT( parent ), "loop" );
66 ret = (int*)gtk_object_get_data( GTK_OBJECT( parent ), "ret" );
69 *ret = gpointer_to_int( data );
72 static gint dialog_delete_callback( GtkWidget *widget, GdkEvent* event, gpointer data ){
75 gtk_widget_hide( widget );
76 loop = (int*)gtk_object_get_data( GTK_OBJECT( widget ), "loop" );
82 int DoMessageBox( const char* lpText, const char* lpCaption, guint32 uType ){
84 int mode = ( uType & MB_TYPEMASK ), ret, loop = 1;
86 auto window = ui::Window( ui::window_type::TOP );
87 window.connect( "delete_event",
88 G_CALLBACK( dialog_delete_callback ), NULL );
89 window.connect( "destroy",
90 G_CALLBACK( gtk_widget_destroy ), NULL );
91 gtk_window_set_title( window, lpCaption );
92 gtk_container_set_border_width( GTK_CONTAINER( window ), 10 );
93 gtk_object_set_data( GTK_OBJECT( window ), "loop", &loop );
94 gtk_object_set_data( GTK_OBJECT( window ), "ret", &ret );
95 gtk_widget_realize( window );
97 auto vbox = ui::VBox( FALSE, 10 );
101 w = ui::Label( lpText );
102 vbox.pack_start( w, FALSE, FALSE, 2 );
103 gtk_label_set_justify( GTK_LABEL( w ), GTK_JUSTIFY_LEFT );
106 w = gtk_hseparator_new();
107 vbox.pack_start( w, FALSE, FALSE, 2 );
110 hbox = ui::HBox( FALSE, 10 );
111 vbox.pack_start( hbox, FALSE, FALSE, 2 );
114 if ( mode == MB_OK ) {
115 w = ui::Button( "Ok" );
116 hbox.pack_start( w, TRUE, TRUE, 0 );
117 w.connect( "clicked",
118 G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDOK ) );
119 gtk_widget_set_can_default( w, true );
120 gtk_widget_grab_default( w );
124 else if ( mode == MB_OKCANCEL ) {
125 w = ui::Button( "Ok" );
126 hbox.pack_start( w, TRUE, TRUE, 0 );
127 w.connect( "clicked",
128 G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDOK ) );
129 gtk_widget_set_can_default( w, true );
130 gtk_widget_grab_default( w );
133 w = ui::Button( "Cancel" );
134 hbox.pack_start( w, TRUE, TRUE, 0 );
135 w.connect( "clicked",
136 G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDCANCEL ) );
140 else if ( mode == MB_YESNOCANCEL ) {
141 w = ui::Button( "Yes" );
142 hbox.pack_start( w, TRUE, TRUE, 0 );
143 w.connect( "clicked",
144 G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDYES ) );
145 gtk_widget_set_can_default( w, true );
146 gtk_widget_grab_default( w );
149 w = ui::Button( "No" );
150 hbox.pack_start( w, TRUE, TRUE, 0 );
151 w.connect( "clicked",
152 G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDNO ) );
155 w = ui::Button( "Cancel" );
156 hbox.pack_start( w, TRUE, TRUE, 0 );
157 w.connect( "clicked",
158 G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDCANCEL ) );
162 else /* if (mode == MB_YESNO) */
164 w = ui::Button( "Yes" );
165 hbox.pack_start( w, TRUE, TRUE, 0 );
166 w.connect( "clicked",
167 G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDYES ) );
168 gtk_widget_set_can_default( w, true );
169 gtk_widget_grab_default( w );
172 w = ui::Button( "No" );
173 hbox.pack_start( w, TRUE, TRUE, 0 );
174 w.connect( "clicked",
175 G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDNO ) );
181 gtk_grab_add( window );
184 gtk_main_iteration();
186 gtk_grab_remove( window );
192 // End of rip from TexTool.cpp
194 // =============================================================================
195 // Ripped from cmdlib.cpp
202 void ExtractFilePath( const char *path, char *dest ){
205 src = path + strlen( path ) - 1;
208 // back up until a \ or the start
210 while ( src != path && *( src - 1 ) != '/' && *( src - 1 ) != '\\' )
213 memcpy( dest, path, src - path );
214 dest[src - path] = 0;
217 void ExtractFileName( const char *path, char *dest ){
220 src = path + strlen( path ) - 1;
223 // back up until a \ or the start
225 while ( src != path && *( src - 1 ) != '/'
226 && *( src - 1 ) != '\\' )
236 void ConvertDOSToUnixName( char *dst, const char *src ){
239 if ( *src == '\\' ) {
250 // End of rip from cmdlib.cpp
252 // =============================================================================
253 // Actual Plugin Code
255 // get the wad name from the shader name (or an actual wadname) and add to a list of wad names making
256 // sure we don't add duplicates.
258 GSList *AddToWadList( GSList *wadlist, const char *shadername, const char *wad ){
259 char tmpstr[QER_MAX_NAMELEN];
261 if ( !shadername && !wad ) {
266 if ( strcmp( shadername,"color" ) == 0 ) {
269 ExtractFilePath( shadername,tmpstr );
270 // Sys_Printf("checking: %s\n",shadername);
272 int l = strlen( tmpstr ) - 1;
274 if ( tmpstr[l] == '/' || tmpstr[l] == '\\' ) {
279 Sys_Printf( "WARNING: Unknown wad file for shader %s\n",shadername );
283 ExtractFileName( tmpstr,tmpstr );
285 wadname = (char *)malloc( strlen( tmpstr ) + 5 );
286 sprintf( wadname,"%s.wad",tmpstr );
290 wadname = strdup( wad );
293 for ( GSList *l = wadlist; l != NULL ; l = l->next )
295 if ( string_equal_nocase( (char *)l->data,wadname ) ) {
301 Sys_Printf( "Adding Wad File to WAD list: %s (reason: ",wadname );
303 Sys_Printf( "see shader \"%s\")\n", shadername );
306 Sys_Printf( "already in WAD key. )\n" );
308 return ( g_slist_append( wadlist, wadname ) );
311 void UpdateWadKeyPair( void ){
314 char wads[2048]; // change to CString usage ?
319 GSList *wadlist = NULL;
322 char cleanwadname[QER_MAX_NAMELEN];
323 const char *actualwad;
326 pEntity = (entity_t *)g_FuncTable.m_pfnGetEntityHandle( 0 ); // get the worldspawn ent
328 Sys_Printf( "Searching for in-use wad files...\n" );
329 for ( pEpair = pEntity->epairs; pEpair != NULL; pEpair = pEpair->next )
331 if ( string_equal_nocase( pEpair->key,"wad" ) ) {
332 strcpy( wads,pEpair->value );
333 ConvertDOSToUnixName( wads,wads );
335 // ok, we got the list of ; delimited wads, now split it into a GSList that contains
336 // just the wad names themselves.
342 p2 = strchr( p1,';' );
344 *p2 = 0; // swap the ; with a null terminator
347 if ( strchr( p1,'/' ) || strchr( p1,'\\' ) ) {
348 ExtractFileName( p1,cleanwadname );
349 wadlist = AddToWadList( wadlist, NULL, cleanwadname );
353 wadlist = AddToWadList( wadlist, NULL, p1 );
356 p1 = p2 + 1; // point back to the remainder of the string
359 p1 = NULL; // make it so we exit the loop.
364 // ok, now we have a list of wads in GSList.
365 // now we need to add any new wadfiles (with their paths) to this list
366 // so scan all brushes and see what wads are in use
367 // FIXME: scan brushes only in the region ?
369 break; // we don't need to process any more key/pairs.
373 nb = g_FuncTable.m_pfnAllocateActiveBrushHandles();
374 for ( i = 0; i < nb; i++ )
376 b = (brush_t *)g_FuncTable.m_pfnGetActiveBrushHandle( i );
377 if ( b->patchBrush ) { // patches in halflife ?
378 wadlist = AddToWadList( wadlist, b->pPatch->pShader->getName(),NULL );
382 for ( f = b->brush_faces ; f ; f = f->next )
384 wadlist = AddToWadList( wadlist, f->pShader->getName(),NULL );
388 g_FuncTable.m_pfnReleaseActiveBrushHandles();
390 nb = g_FuncTable.m_pfnAllocateSelectedBrushHandles();
391 for ( i = 0; i < nb; i++ )
393 b = (brush_t *)g_FuncTable.m_pfnGetSelectedBrushHandle( i );
394 if ( b->patchBrush ) { // patches in halflife ?
395 wadlist = AddToWadList( wadlist, b->pPatch->pShader->getName(),NULL );
399 for ( f = b->brush_faces ; f ; f = f->next )
401 wadlist = AddToWadList( wadlist, f->pShader->getName(),NULL );
405 g_FuncTable.m_pfnReleaseSelectedBrushHandles();
407 // Now we have a complete list of wadnames (without paths) so we just have to turn this
408 // back to a ; delimited list.
413 if ( string_equal_nocase( (char *)wadlist->data,"common-hydra.wad" ) ) {
414 Sys_Printf( "Skipping radiant-supplied wad file %s\n",(char *)wadlist->data );
422 actualwad = vfsGetFullPath( (char *)wadlist->data );
425 strcat( wads, actualwad );
429 Sys_Printf( "WARNING: could not locate wad file %s\n",(char *)wadlist->data );
430 strcat( wads, (char *)wadlist->data );
434 free( wadlist->data );
435 wadlist = g_slist_remove( wadlist, wadlist->data );
438 // store the wad list back in the worldspawn.
440 //free(pEpair->value);
441 //pEpair->value = strdup(wads);
442 SetKeyValue( pEntity, "wad", wads );
447 // =============================================================================
448 // PLUGIN INTERFACE STUFF
451 const char *PLUGIN_NAME = "Q3 Texture Tools";
453 // commands in the menu
454 const char *PLUGIN_COMMANDS = "About...;Create/Update WAD keypair";
456 const char *PLUGIN_ABOUT = "HydraToolz for GTKRadiant\n\n"
459 extern "C" void* WINAPI QERPlug_GetFuncTable(){
463 const char* QERPlug_Init( void* hApp, void *pWidget ){
464 GtkWidget* pMainWidget = static_cast<GtkWidget*>( pWidget );
466 g_pMainWnd = pMainWidget;
467 memset( &g_FuncTable, 0, sizeof( _QERFuncTable_1 ) );
468 g_FuncTable.m_nSize = sizeof( _QERFuncTable_1 );
469 return "HydraToolz for GTKRadiant"; // do we need this ? hmmm
472 const char* QERPlug_GetName(){
473 return (char*)PLUGIN_NAME;
476 const char* QERPlug_GetCommandList(){
477 return PLUGIN_COMMANDS;
480 extern "C" void QERPlug_Dispatch( const char* p, vec3_t vMin, vec3_t vMax, bool bSingleBrush ){
481 if ( !strcmp( p, "Create/Update WAD keypair" ) ) {
484 else if ( !strcmp( p, "About..." ) ) {
485 g_FuncTable.m_pfnMessageBox( (GtkWidget*)NULL, PLUGIN_ABOUT, "About", eMB_OK );
489 // =============================================================================
492 CSynapseServer* g_pSynapseServer = NULL;
493 CSynapseClientHydraToolz g_SynapseClient;
495 extern "C" CSynapseClient * SYNAPSE_DLL_EXPORT Synapse_EnumerateInterfaces( const char *version, CSynapseServer *pServer ){
496 if ( strcmp( version, SYNAPSE_VERSION ) ) {
497 Syn_Printf( "ERROR: synapse API version mismatch: should be '" SYNAPSE_VERSION "', got '%s'\n", version );
500 g_pSynapseServer = pServer;
501 g_pSynapseServer->IncRef();
502 Set_Syn_Printf( g_pSynapseServer->Get_Syn_Printf() );
504 g_SynapseClient.AddAPI( PLUGIN_MAJOR, "HydraToolz", sizeof( _QERPluginTable ) );
505 g_SynapseClient.AddAPI( RADIANT_MAJOR, NULL, sizeof( g_FuncTable ), SYN_REQUIRE, &g_FuncTable );
506 g_SynapseClient.AddAPI( VFS_MAJOR, "wad", sizeof( g_FileSystemTable ), SYN_REQUIRE, &g_FileSystemTable );
507 g_SynapseClient.AddAPI( ENTITY_MAJOR, NULL, sizeof( g_EntityTable ), SYN_REQUIRE, &g_EntityTable );
508 return &g_SynapseClient;
511 bool CSynapseClientHydraToolz::RequestAPI( APIDescriptor_t *pAPI ){
512 if ( !strcmp( pAPI->major_name, PLUGIN_MAJOR ) ) {
513 _QERPluginTable *pTable = static_cast<_QERPluginTable*>( pAPI->mpTable );
514 pTable->m_pfnQERPlug_Init = QERPlug_Init;
515 pTable->m_pfnQERPlug_GetName = QERPlug_GetName;
516 pTable->m_pfnQERPlug_GetCommandList = QERPlug_GetCommandList;
517 pTable->m_pfnQERPlug_Dispatch = QERPlug_Dispatch;
521 Syn_Printf( "ERROR: RequestAPI( '%s' ) not found in '%s'\n", pAPI->major_name, GetInfo() );
525 const char* CSynapseClientHydraToolz::GetInfo(){
526 return "HydraToolz plugin built " __DATE__ " " RADIANT_VERSION;