Merge commit '830125fad042fad35dc029b6eb57c8156ad7e176'
[xonotic/netradiant.git] / tools / quake3 / common / inout.c
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 //-----------------------------------------------------------------------------
23 //
24 //
25 // DESCRIPTION:
26 // deal with in/out tasks, for either stdin/stdout or network/XML stream
27 //
28
29 #include "cmdlib.h"
30 #include "mathlib.h"
31 #include "polylib.h"
32 #include "inout.h"
33 #include <sys/types.h>
34 #include <sys/stat.h>
35
36 #ifdef WIN32
37 #include <direct.h>
38 #include <windows.h>
39 #endif
40
41 // network broadcasting
42 #include "l_net/l_net.h"
43 #include "libxml/tree.h"
44
45 // utf8 conversion
46 #include <glib/gconvert.h>
47 #include <glib/gmem.h>
48
49 #ifdef WIN32
50 HWND hwndOut = NULL;
51 qboolean lookedForServer = qfalse;
52 UINT wm_BroadcastCommand = -1;
53 #endif
54
55 socket_t *brdcst_socket;
56 netmessage_t msg;
57
58 qboolean verbose = qfalse;
59
60 // our main document
61 // is streamed through the network to Radiant
62 // possibly written to disk at the end of the run
63 //++timo FIXME: need to be global, required when creating nodes?
64 xmlDocPtr doc;
65 xmlNodePtr tree;
66
67 // some useful stuff
68 xmlNodePtr xml_NodeForVec( vec3_t v ){
69         xmlNodePtr ret;
70         char buf[1024];
71
72         sprintf( buf, "%f %f %f", v[0], v[1], v[2] );
73         ret = xmlNewNode( NULL, (xmlChar*)"point" );
74         xmlNodeSetContent( ret, (xmlChar*)buf );
75         return ret;
76 }
77
78 // send a node down the stream, add it to the document
79 void xml_SendNode( xmlNodePtr node ){
80         xmlBufferPtr xml_buf;
81         char xmlbuf[MAX_NETMESSAGE]; // we have to copy content from the xmlBufferPtr into an aux buffer .. that sucks ..
82         // this index loops through the node buffer
83         int pos = 0;
84         int size;
85
86         xmlAddChild( doc->children, node );
87
88         if ( brdcst_socket ) {
89                 xml_buf = xmlBufferCreate();
90                 xmlNodeDump( xml_buf, doc, node, 0, 0 );
91
92                 // the XML node might be too big to fit in a single network message
93                 // l_net library defines an upper limit of MAX_NETMESSAGE
94                 // there are some size check errors, so we use MAX_NETMESSAGE-10 to be safe
95                 // if the size of the buffer exceeds MAX_NETMESSAGE-10 we'll send in several network messages
96                 while ( pos < (int)xml_buf->use )
97                 {
98                         // what size are we gonna send now?
99                         ( xml_buf->use - pos < MAX_NETMESSAGE - 10 ) ? ( size = xml_buf->use - pos ) : ( size = MAX_NETMESSAGE - 10 );
100                         //++timo just a debug thing
101                         if ( size == MAX_NETMESSAGE - 10 ) {
102                                 Sys_FPrintf( SYS_NOXML, "Got to split the buffer\n" );
103                         }
104                         memcpy( xmlbuf, xml_buf->content + pos, size );
105                         xmlbuf[size] = '\0';
106                         NMSG_Clear( &msg );
107                         NMSG_WriteString( &msg, xmlbuf );
108                         Net_Send( brdcst_socket, &msg );
109                         // now that the thing is sent prepare to loop again
110                         pos += size;
111                 }
112
113 #if 0
114                 // NOTE: the NMSG_WriteString is limited to MAX_NETMESSAGE
115                 // we will need to split into chunks
116                 // (we could also go lower level, in the end it's using send and receiv which are not size limited)
117                 //++timo FIXME: MAX_NETMESSAGE is not exactly the max size we can stick in the message
118                 //  there's some tweaking to do in l_net for that .. so let's give us a margin for now
119
120                 //++timo we need to handle the case of a buffer too big to fit in a single message
121                 // try without checks for now
122                 if ( xml_buf->use > MAX_NETMESSAGE - 10 ) {
123                         // if we send that we are probably gonna break the stream at the other end..
124                         // and Error will call right there
125                         //Error( "MAX_NETMESSAGE exceeded for XML feedback stream in FPrintf (%d)\n", xml_buf->use);
126                         Sys_FPrintf( SYS_NOXML, "MAX_NETMESSAGE exceeded for XML feedback stream in FPrintf (%d)\n", xml_buf->use );
127                         xml_buf->content[xml_buf->use] = '\0'; //++timo this corrupts the buffer but we don't care it's for printing
128                         Sys_FPrintf( SYS_NOXML, xml_buf->content );
129
130                 }
131
132                 size = xml_buf->use;
133                 memcpy( xmlbuf, xml_buf->content, size );
134                 xmlbuf[size] = '\0';
135                 NMSG_Clear( &msg );
136                 NMSG_WriteString( &msg, xmlbuf );
137                 Net_Send( brdcst_socket, &msg );
138 #endif
139
140                 xmlBufferFree( xml_buf );
141         }
142 }
143
144 void xml_Select( char *msg, int entitynum, int brushnum, qboolean bError ){
145         xmlNodePtr node, select;
146         char buf[1024];
147         char level[2];
148
149         // now build a proper "select" XML node
150         sprintf( buf, "Entity %i, Brush %i: %s", entitynum, brushnum, msg );
151         node = xmlNewNode( NULL, (xmlChar*)"select" );
152         xmlNodeSetContent( node, (xmlChar*)buf );
153         level[0] = (int)'0' + ( bError ? SYS_ERR : SYS_WRN )  ;
154         level[1] = 0;
155         xmlSetProp( node, (xmlChar*)"level", (xmlChar *)&level );
156         // a 'select' information
157         sprintf( buf, "%i %i", entitynum, brushnum );
158         select = xmlNewNode( NULL, (xmlChar*)"brush" );
159         xmlNodeSetContent( select, (xmlChar*)buf );
160         xmlAddChild( node, select );
161         xml_SendNode( node );
162
163         sprintf( buf, "Entity %i, Brush %i: %s", entitynum, brushnum, msg );
164         if ( bError ) {
165                 Error( buf );
166         }
167         else{
168                 Sys_FPrintf( SYS_NOXML, "%s\n", buf );
169         }
170
171 }
172
173 void xml_Point( char *msg, vec3_t pt ){
174         xmlNodePtr node, point;
175         char buf[1024];
176         char level[2];
177
178         node = xmlNewNode( NULL, (xmlChar*)"pointmsg" );
179         xmlNodeSetContent( node, (xmlChar*)msg );
180         level[0] = (int)'0' + SYS_ERR;
181         level[1] = 0;
182         xmlSetProp( node, (xmlChar*)"level", (xmlChar *)&level );
183         // a 'point' node
184         sprintf( buf, "%g %g %g", pt[0], pt[1], pt[2] );
185         point = xmlNewNode( NULL, (xmlChar*)"point" );
186         xmlNodeSetContent( point, (xmlChar*)buf );
187         xmlAddChild( node, point );
188         xml_SendNode( node );
189
190         sprintf( buf, "%s (%g %g %g)", msg, pt[0], pt[1], pt[2] );
191         Error( buf );
192 }
193
194 #define WINDING_BUFSIZE 2048
195 void xml_Winding( char *msg, vec3_t p[], int numpoints, qboolean die ){
196         xmlNodePtr node, winding;
197         char buf[WINDING_BUFSIZE];
198         char smlbuf[128];
199         char level[2];
200         int i;
201
202         node = xmlNewNode( NULL, (xmlChar*)"windingmsg" );
203         xmlNodeSetContent( node, (xmlChar*)msg );
204         level[0] = (int)'0' + SYS_ERR;
205         level[1] = 0;
206         xmlSetProp( node, (xmlChar*)"level", (xmlChar *)&level );
207         // a 'winding' node
208         sprintf( buf, "%i ", numpoints );
209         for ( i = 0; i < numpoints; i++ )
210         {
211                 sprintf( smlbuf, "(%g %g %g)", p[i][0], p[i][1], p[i][2] );
212                 // don't overflow
213                 if ( strlen( buf ) + strlen( smlbuf ) > WINDING_BUFSIZE ) {
214                         break;
215                 }
216                 strcat( buf, smlbuf );
217         }
218
219         winding = xmlNewNode( NULL, (xmlChar*)"winding" );
220         xmlNodeSetContent( winding, (xmlChar*)buf );
221         xmlAddChild( node, winding );
222         xml_SendNode( node );
223
224         if ( die ) {
225                 Error( msg );
226         }
227         else
228         {
229                 Sys_Printf( msg );
230                 Sys_Printf( "\n" );
231         }
232 }
233
234 // in include
235 #include "stream_version.h"
236
237 void Broadcast_Setup( const char *dest ){
238         address_t address;
239         char sMsg[1024];
240
241         Net_Setup();
242         Net_StringToAddress( dest, &address );
243         brdcst_socket = Net_Connect( &address, 0 );
244         if ( brdcst_socket ) {
245                 // send in a header
246                 sprintf( sMsg, "<?xml version=\"1.0\"?><q3map_feedback version=\"" Q3MAP_STREAM_VERSION "\">" );
247                 NMSG_Clear( &msg );
248                 NMSG_WriteString( &msg, sMsg );
249                 Net_Send( brdcst_socket, &msg );
250         }
251 }
252
253 void Broadcast_Shutdown(){
254         if ( brdcst_socket ) {
255                 Sys_Printf( "Disconnecting\n" );
256                 Net_Disconnect( brdcst_socket );
257                 brdcst_socket = NULL;
258         }
259 }
260
261 // all output ends up through here
262 void FPrintf( int flag, char *buf ){
263         xmlNodePtr node;
264         static qboolean bGotXML = qfalse;
265         char level[2];
266
267         printf( "%s", buf );
268
269         // the following part is XML stuff only.. but maybe we don't want that message to go down the XML pipe?
270         if ( flag == SYS_NOXML ) {
271                 return;
272         }
273
274         // ouput an XML file of the run
275         // use the DOM interface to build a tree
276         /*
277            <message level='flag'>
278            message string
279            .. various nodes to describe corresponding geometry ..
280            </message>
281          */
282         if ( !bGotXML ) {
283                 // initialize
284                 doc = xmlNewDoc( (xmlChar*)"1.0" );
285                 doc->children = xmlNewDocRawNode( doc, NULL, (xmlChar*)"q3map_feedback", NULL );
286                 bGotXML = qtrue;
287         }
288         node = xmlNewNode( NULL, (xmlChar*)"message" );
289         {
290                 gchar* utf8 = g_locale_to_utf8( buf, -1, NULL, NULL, NULL );
291                 xmlNodeSetContent( node, (xmlChar*)utf8 );
292                 g_free( utf8 );
293         }
294         level[0] = (int)'0' + flag;
295         level[1] = 0;
296         xmlSetProp( node, (xmlChar*)"level", (xmlChar *)&level );
297
298         xml_SendNode( node );
299 }
300
301 #ifdef DBG_XML
302 void DumpXML(){
303         xmlSaveFile( "XMLDump.xml", doc );
304 }
305 #endif
306
307 void Sys_FPrintf( int flag, const char *format, ... ){
308         char out_buffer[4096];
309         va_list argptr;
310
311         if ( ( flag == SYS_VRB ) && ( verbose == qfalse ) ) {
312                 return;
313         }
314
315         va_start( argptr, format );
316         vsprintf( out_buffer, format, argptr );
317         va_end( argptr );
318
319         FPrintf( flag, out_buffer );
320 }
321
322 void Sys_Printf( const char *format, ... ){
323         char out_buffer[4096];
324         va_list argptr;
325
326         va_start( argptr, format );
327         vsprintf( out_buffer, format, argptr );
328         va_end( argptr );
329
330         FPrintf( SYS_STD, out_buffer );
331 }
332
333 /*
334    =================
335    Error
336
337    For abnormal program terminations
338    =================
339  */
340 void Error( const char *error, ... ){
341         char out_buffer[4096];
342         char tmp[4096];
343         va_list argptr;
344
345         va_start( argptr,error );
346         vsprintf( tmp, error, argptr );
347         va_end( argptr );
348
349         sprintf( out_buffer, "************ ERROR ************\n%s\n", tmp );
350
351         FPrintf( SYS_ERR, out_buffer );
352
353 #ifdef DBG_XML
354         DumpXML();
355 #endif
356
357         //++timo HACK ALERT .. if we shut down too fast the xml stream won't reach the listener.
358         // a clean solution is to send a sync request node in the stream and wait for an answer before exiting
359         Sys_Sleep( 1000 );
360
361         Broadcast_Shutdown();
362
363         exit( 1 );
364 }