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