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