1 /**
2  * @file
3  */
4 
5 /*
6 Copyright (C) 1997-2001 Id Software, Inc.
7 
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
12 
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 
17 See the GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22 
23 */
24 
25 #include "common.h"
26 
27 const vec3_t bytedirs[] = {
28 #include "../shared/vertex_normals.h"
29 };
30 
31 static const float POSSCALE = 32.0f;
32 
NET_WriteChar(dbuffer * buf,char c)33 void NET_WriteChar (dbuffer* buf, char c)
34 {
35 	buf->add(&c, 1);
36 	Com_DPrintf(DEBUG_EVENTSYS, "char event data: %s (%i)\n", Com_ByteToBinary(c), c);
37 }
38 
NET_WriteByte(dbuffer * buf,byte c)39 void NET_WriteByte (dbuffer* buf, byte c)
40 {
41 	buf->add((const char*)&c, 1);
42 	Com_DPrintf(DEBUG_EVENTSYS, "byte event data: %s (%i)\n", Com_ByteToBinary(c), c);
43 }
44 
NET_WriteShort(dbuffer * buf,int c)45 void NET_WriteShort (dbuffer* buf, int c)
46 {
47 	const unsigned short v = LittleShort(c);
48 	buf->add((const char*)&v, 2);
49 	Com_DPrintf(DEBUG_EVENTSYS, "short event data: %i\n", c);
50 }
51 
NET_WriteLong(dbuffer * buf,int c)52 void NET_WriteLong (dbuffer* buf, int c)
53 {
54 	const int v = LittleLong(c);
55 	buf->add((const char*)&v, 4);
56 	Com_DPrintf(DEBUG_EVENTSYS, "long event data: %i\n", c);
57 }
58 
NET_WriteString(dbuffer * buf,const char * str)59 void NET_WriteString (dbuffer* buf, const char* str)
60 {
61 	if (!str)
62 		buf->add("", 1);
63 	else
64 		buf->add(str, strlen(str) + 1);
65 	Com_DPrintf(DEBUG_EVENTSYS, "string event data: %s\n", str);
66 }
67 
68 /**
69  * @brief Skip the zero string terminal character. If you need it, use @c NET_WriteString.
70  */
NET_WriteRawString(dbuffer * buf,const char * str)71 void NET_WriteRawString (dbuffer* buf, const char* str)
72 {
73 	if (!str)
74 		return;
75 	buf->add(str, strlen(str));
76 	Com_DPrintf(DEBUG_EVENTSYS, "string raw event data: %s\n", str);
77 }
78 
NET_WriteCoord(dbuffer * buf,float f)79 void NET_WriteCoord (dbuffer* buf, float f)
80 {
81 	NET_WriteLong(buf, (int) (f * 32));
82 }
83 
84 /**
85  * @sa NET_Read2Pos
86  */
NET_Write2Pos(dbuffer * buf,const vec2_t pos)87 void NET_Write2Pos (dbuffer* buf, const vec2_t pos)
88 {
89 	NET_WriteLong(buf, (long) (pos[0] * POSSCALE));
90 	NET_WriteLong(buf, (long) (pos[1] * POSSCALE));
91 }
92 
93 /**
94  * @sa NET_ReadPos
95  */
NET_WritePos(dbuffer * buf,const vec3_t pos)96 void NET_WritePos (dbuffer* buf, const vec3_t pos)
97 {
98 	NET_WriteLong(buf, (long) (pos[0] * POSSCALE));
99 	NET_WriteLong(buf, (long) (pos[1] * POSSCALE));
100 	NET_WriteLong(buf, (long) (pos[2] * POSSCALE));
101 }
102 
NET_WriteGPos(dbuffer * buf,const pos3_t pos)103 void NET_WriteGPos (dbuffer* buf, const pos3_t pos)
104 {
105 	NET_WriteByte(buf, pos[0]);
106 	NET_WriteByte(buf, pos[1]);
107 	NET_WriteByte(buf, pos[2]);
108 }
109 
NET_WriteAngle(dbuffer * buf,float f)110 void NET_WriteAngle (dbuffer* buf, float f)
111 {
112 	NET_WriteByte(buf, (int) (f * 256 / 360) & 255);
113 }
114 
NET_WriteAngle16(dbuffer * buf,float f)115 void NET_WriteAngle16 (dbuffer* buf, float f)
116 {
117 	NET_WriteShort(buf, ANGLE2SHORT(f));
118 }
119 
120 /**
121  * @note EV_ACTOR_SHOOT is using WriteDir for writing the normal, but ReadByte
122  * for reading it - keep that in mind when you change something here
123  */
NET_WriteDir(dbuffer * buf,const vec3_t dir)124 void NET_WriteDir (dbuffer* buf, const vec3_t dir)
125 {
126 	if (!dir) {
127 		NET_WriteByte(buf, 0);
128 		return;
129 	}
130 
131 	float bestd = 0.0f;
132 	int best = 0;
133 	const size_t bytedirsLength = lengthof(bytedirs);
134 	for (int i = 0; i < bytedirsLength; i++) {
135 		const float d = DotProduct(dir, bytedirs[i]);
136 		if (d > bestd) {
137 			bestd = d;
138 			best = i;
139 		}
140 	}
141 	NET_WriteByte(buf, best);
142 }
143 
144 /**
145  * @brief Writes to buffer according to format; version without syntactic sugar
146  * for variable arguments, to call it from other functions with variable arguments
147  * @note short and char are promoted to int when passed to variadic functions!
148  */
NET_vWriteFormat(dbuffer * buf,const char * format,va_list ap)149 void NET_vWriteFormat (dbuffer* buf, const char* format, va_list ap)
150 {
151 	while (*format) {
152 		const char typeID = *format++;
153 
154 		switch (typeID) {
155 		case 'c':
156 			NET_WriteChar(buf, va_arg(ap, int));
157 			break;
158 		case 'b':
159 			NET_WriteByte(buf, va_arg(ap, int));
160 			break;
161 		case 's':
162 			NET_WriteShort(buf, va_arg(ap, int));
163 			break;
164 		case 'l':
165 			NET_WriteLong(buf, va_arg(ap, int));
166 			break;
167 		case 'p':
168 			NET_WritePos(buf, va_arg(ap, float*));
169 			break;
170 		case 'g':
171 			NET_WriteGPos(buf, va_arg(ap, byte*));
172 			break;
173 		case 'd':
174 			NET_WriteDir(buf, va_arg(ap, float*));
175 			break;
176 		case 'a':
177 			/* NOTE: float is promoted to double through ... */
178 			NET_WriteAngle(buf, va_arg(ap, double));
179 			break;
180 		case '!':
181 			break;
182 		case '&':
183 			NET_WriteString(buf, va_arg(ap, char*));
184 			break;
185 		case '*': {
186 			const int n = va_arg(ap, int);
187 			const byte* p = va_arg(ap, byte*);
188 			NET_WriteShort(buf, n);
189 			for (int i = 0; i < n; i++)
190 				NET_WriteByte(buf, *p++);
191 			break;
192 		}
193 		default:
194 			Com_Error(ERR_DROP, "WriteFormat: Unknown type!");
195 		}
196 	}
197 	/* Too many arguments for the given format; too few cause crash above */
198 #ifdef PARANOID
199 	if (!ap)
200 		Com_Error(ERR_DROP, "WriteFormat: Too many arguments!");
201 #endif
202 }
203 
204 /**
205  * @brief The user-friendly version of NET_WriteFormat that writes variable arguments to buffer according to format
206  */
NET_WriteFormat(dbuffer * buf,const char * format,...)207 void NET_WriteFormat (dbuffer* buf, const char* format, ...)
208 {
209 	va_list ap;
210 	va_start(ap, format);
211 	NET_vWriteFormat(buf, format, ap);
212 	va_end(ap);
213 }
214 
215 
216 /* reading functions */
217 
218 /**
219  * returns -1 if no more characters are available
220  */
NET_ReadChar(dbuffer * buf)221 int NET_ReadChar (dbuffer* buf)
222 {
223 	char c;
224 	if (buf->extract(&c, 1) == 0)
225 		return -1;
226 	return c;
227 }
228 
229 /**
230  * @brief Reads a byte from the netchannel
231  * @note Beware that you don't put this into a byte or short - this will overflow
232  * use an int value to store the return value when you read this via the net format strings!!!
233  */
NET_ReadByte(dbuffer * buf)234 int NET_ReadByte (dbuffer* buf)
235 {
236 	unsigned char c;
237 	if (buf->extract((char*)&c, 1) == 0)
238 		return -1;
239 	return c;
240 }
241 
NET_ReadShort(dbuffer * buf)242 int NET_ReadShort (dbuffer* buf)
243 {
244 	unsigned short v;
245 	if (buf->extract((char*)&v, 2) < 2)
246 		return -1;
247 
248 	return LittleShort(v);
249 }
250 
NET_PeekByte(const dbuffer * buf)251 int NET_PeekByte (const dbuffer* buf)
252 {
253 	unsigned char c;
254 	if (buf->get((char*)&c, 1) == 0)
255 		return -1;
256 	return c;
257 }
258 
259 /**
260  * @brief Peeks into a buffer without changing it to get a short int.
261  * @param buf The buffer, returned unchanged, no need to be copied before.
262  * @return The short at the beginning of the buffer, -1 if it couldn't be read.
263  */
NET_PeekShort(const dbuffer * buf)264 int NET_PeekShort (const dbuffer* buf)
265 {
266 	uint16_t v;
267 	if (buf->get((char*)&v, 2) < 2)
268 		return -1;
269 
270 	return LittleShort(v);
271 }
272 
NET_PeekLong(const dbuffer * buf)273 int NET_PeekLong (const dbuffer* buf)
274 {
275 	uint32_t v;
276 	if (buf->get((char*)&v, 4) < 4)
277 		return -1;
278 
279 	return LittleLong(v);
280 }
281 
NET_ReadLong(dbuffer * buf)282 int NET_ReadLong (dbuffer* buf)
283 {
284 	unsigned int v;
285 	if (buf->extract((char*)&v, 4) < 4)
286 		return -1;
287 
288 	return LittleLong(v);
289 }
290 
291 /**
292  * @note Don't use this function in a way like
293  * <code> char* s = NET_ReadString(sb);
294  * char* t = NET_ReadString(sb);</code>
295  * The second reading uses the same data buffer for the string - so
296  * s is no longer the first - but the second string
297  * @sa NET_ReadStringLine
298  * @param[in,out] buf The input buffer to read the string data from
299  * @param[out] string The output buffer to read the string into
300  * @param[in] length The size of the output buffer
301  */
NET_ReadString(dbuffer * buf,char * string,size_t length)302 int NET_ReadString (dbuffer* buf, char* string, size_t length)
303 {
304 	unsigned int l = 0;
305 
306 	for (;;) {
307 		int c = NET_ReadByte(buf);
308 		if (c == -1 || c == 0)
309 			break;
310 		if (string && l < length - 1) {
311 			/* translate all format specs to avoid crash bugs */
312 			if (c == '%')
313 				c = '.';
314 			string[l] = c;
315 		}
316 		l++;
317 	}
318 
319 	if (string)
320 		string[l] = '\0';
321 
322 	return l;
323 }
324 
325 /**
326  * @sa NET_ReadString
327  */
NET_ReadStringLine(dbuffer * buf,char * string,size_t length)328 int NET_ReadStringLine (dbuffer* buf, char* string, size_t length)
329 {
330 	unsigned int l;
331 
332 	l = 0;
333 	do {
334 		int c = NET_ReadByte(buf);
335 		if (c == -1 || c == 0 || c == '\n')
336 			break;
337 		/* translate all format specs to avoid crash bugs */
338 		if (c == '%')
339 			c = '.';
340 		string[l] = c;
341 		l++;
342 	} while (l < length - 1);
343 
344 	string[l] = 0;
345 
346 	return l;
347 }
348 
NET_ReadCoord(dbuffer * buf)349 float NET_ReadCoord (dbuffer* buf)
350 {
351 	return (float) NET_ReadLong(buf) * (1.0 / 32);
352 }
353 
354 /**
355  * @sa NET_Write2Pos
356  */
NET_Read2Pos(dbuffer * buf,vec2_t pos)357 void NET_Read2Pos (dbuffer* buf, vec2_t pos)
358 {
359 	pos[0] = NET_ReadLong(buf) / POSSCALE;
360 	pos[1] = NET_ReadLong(buf) / POSSCALE;
361 }
362 
363 /**
364  * @sa NET_WritePos
365  */
NET_ReadPos(dbuffer * buf,vec3_t pos)366 void NET_ReadPos (dbuffer* buf, vec3_t pos)
367 {
368 	pos[0] = NET_ReadLong(buf) / POSSCALE;
369 	pos[1] = NET_ReadLong(buf) / POSSCALE;
370 	pos[2] = NET_ReadLong(buf) / POSSCALE;
371 }
372 
373 /**
374  * @sa NET_WriteGPos
375  * @sa NET_ReadByte
376  * @note pos3_t are byte values
377  */
NET_ReadGPos(dbuffer * buf,pos3_t pos)378 void NET_ReadGPos (dbuffer* buf, pos3_t pos)
379 {
380 	pos[0] = NET_ReadByte(buf);
381 	pos[1] = NET_ReadByte(buf);
382 	pos[2] = NET_ReadByte(buf);
383 }
384 
NET_ReadAngle(dbuffer * buf)385 float NET_ReadAngle (dbuffer* buf)
386 {
387 	return (float) NET_ReadChar(buf) * (360.0 / 256);
388 }
389 
NET_ReadAngle16(dbuffer * buf)390 float NET_ReadAngle16 (dbuffer* buf)
391 {
392 	const short s = NET_ReadShort(buf);
393 	return (float) SHORT2ANGLE(s);
394 }
395 
NET_ReadData(dbuffer * buf,void * data,int len)396 void NET_ReadData (dbuffer* buf, void* data, int len)
397 {
398 	int i;
399 
400 	for (i = 0; i < len; i++)
401 		((byte*) data)[i] = NET_ReadByte(buf);
402 }
403 
NET_ReadDir(dbuffer * buf,vec3_t dir)404 void NET_ReadDir (dbuffer* buf, vec3_t dir)
405 {
406 	const int b = NET_ReadByte(buf);
407 	if (b >= lengthof(bytedirs))
408 		Com_Error(ERR_DROP, "NET_ReadDir: out of range");
409 	VectorCopy(bytedirs[b], dir);
410 }
411 
412 /**
413  * @brief Reads from a buffer according to format; version without syntactic sugar for variable arguments, to call it from other functions with variable arguments
414  * @sa SV_ReadFormat
415  * @param[in] buf The buffer we read the data from
416  * @param[in] format The format string may not be nullptr
417  */
NET_vReadFormat(dbuffer * buf,const char * format,va_list ap)418 void NET_vReadFormat (dbuffer* buf, const char* format, va_list ap)
419 {
420 	while (*format) {
421 		const char typeID = *format++;
422 
423 		switch (typeID) {
424 		case 'c':
425 			*va_arg(ap, int*) = NET_ReadChar(buf);
426 			break;
427 		case 'b':
428 			*va_arg(ap, int*) = NET_ReadByte(buf);
429 			break;
430 		case 's':
431 			*va_arg(ap, int*) = NET_ReadShort(buf);
432 			break;
433 		case 'l':
434 			*va_arg(ap, int*) = NET_ReadLong(buf);
435 			break;
436 		case 'p':
437 			NET_ReadPos(buf, *va_arg(ap, vec3_t*));
438 			break;
439 		case 'g':
440 			NET_ReadGPos(buf, *va_arg(ap, pos3_t*));
441 			break;
442 		case 'd':
443 			NET_ReadDir(buf, *va_arg(ap, vec3_t*));
444 			break;
445 		case 'a':
446 			*va_arg(ap, float*) = NET_ReadAngle(buf);
447 			break;
448 		case '!':
449 			format++;
450 			break;
451 		case '&': {
452 			char* str = va_arg(ap, char*);
453 			const size_t length = va_arg(ap, size_t);
454 			NET_ReadString(buf, str, length);
455 			break;
456 		}
457 		case '*':
458 			{
459 				int i;
460 				byte* p;
461 				const int n = NET_ReadShort(buf);
462 
463 				*va_arg(ap, int*) = n;
464 				p = va_arg(ap, byte*);
465 
466 				for (i = 0; i < n; i++)
467 					*p++ = NET_ReadByte(buf);
468 			}
469 			break;
470 		default:
471 			Com_Error(ERR_DROP, "ReadFormat: Unknown type!");
472 		}
473 	}
474 	/* Too many arguments for the given format; too few cause crash above */
475 #ifdef PARANOID
476 	if (!ap)
477 		Com_Error(ERR_DROP, "ReadFormat: Too many arguments!");
478 #endif
479 }
480 
NET_SkipFormat(dbuffer * buf,const char * format)481 void NET_SkipFormat (dbuffer* buf, const char* format)
482 {
483 	while (*format) {
484 		const char typeID = *format++;
485 
486 		switch (typeID) {
487 		case 'c':
488 			NET_ReadChar(buf);
489 			break;
490 		case 'b':
491 			NET_ReadByte(buf);
492 			break;
493 		case 's':
494 			NET_ReadShort(buf);
495 			break;
496 		case 'l':
497 			NET_ReadLong(buf);
498 			break;
499 		case 'p': {
500 			vec3_t v;
501 			NET_ReadPos(buf, v);
502 			break;
503 		}
504 		case 'g': {
505 			pos3_t p;
506 			NET_ReadGPos(buf, p);
507 			break;
508 		}
509 		case 'd': {
510 			vec3_t v;
511 			NET_ReadDir(buf, v);
512 			break;
513 		}
514 		case 'a':
515 			NET_ReadAngle(buf);
516 			break;
517 		case '!':
518 			format++;
519 			break;
520 		case '&':
521 			NET_ReadString(buf, nullptr, 0);
522 			break;
523 		case '*': {
524 			const int n = NET_ReadShort(buf);
525 			for (int i = 0; i < n; i++)
526 				NET_ReadByte(buf);
527 			break;
528 		}
529 		default:
530 			Com_Error(ERR_DROP, "ReadFormat: Unknown type!");
531 		}
532 	}
533 }
534 
535 /**
536  * @brief The user-friendly version of NET_ReadFormat that reads variable arguments from a buffer according to format
537  */
NET_ReadFormat(dbuffer * buf,const char * format,...)538 void NET_ReadFormat (dbuffer* buf, const char* format, ...)
539 {
540 	va_list ap;
541 
542 	va_start(ap, format);
543 	NET_vReadFormat(buf, format, ap);
544 	va_end(ap);
545 }
546 
547 /**
548  * @brief Out of band print
549  * @sa clc_oob
550  * @sa CL_ReadPackets
551  * @sa SV_ConnectionlessPacket
552  */
NET_OOB_Printf(struct net_stream * s,const char * format,...)553 void NET_OOB_Printf (struct net_stream *s, const char* format, ...)
554 {
555 	va_list argptr;
556 	char string[256];
557 	const char cmd = (const char)clc_oob;
558 
559 	va_start(argptr, format);
560 	Q_vsnprintf(string, sizeof(string), format, argptr);
561 	va_end(argptr);
562 
563 	const int len = LittleLong(strlen(string) + 1);
564 	NET_StreamEnqueue(s, (const char*)&len, 4);
565 	NET_StreamEnqueue(s, &cmd, 1);
566 	NET_StreamEnqueue(s, string, strlen(string));
567 }
568 
569 /**
570  * @brief Enqueue the buffer in the net stream for ONE client
571  * @note Frees the msg buffer
572  * @sa NET_WriteConstMsg
573  */
NET_WriteMsg(struct net_stream * s,dbuffer & buf)574 void NET_WriteMsg (struct net_stream *s, dbuffer &buf)
575 {
576 	char tmp[256];
577 	int len = LittleLong(buf.length());
578 	NET_StreamEnqueue(s, (char*)&len, 4);
579 
580 	while (buf.length()) {
581 		len = buf.extract(tmp, sizeof(tmp));
582 		NET_StreamEnqueue(s, tmp, len);
583 	}
584 }
585 
586 /**
587  * @brief Enqueue the buffer in the net stream for MULTIPLE clients
588  * @note Same as NET_WriteMsg but doesn't free the buffer, use this if you send
589  * the same buffer to more than one connected clients
590  * @note Make sure that you free the msg buffer after you called this
591  * @sa NET_WriteMsg
592  */
NET_WriteConstMsg(struct net_stream * s,const dbuffer & buf)593 void NET_WriteConstMsg (struct net_stream *s, const dbuffer &buf)
594 {
595 	char tmp[256];
596 	const int len = LittleLong(buf.length());
597 	int pos = 0;
598 	NET_StreamEnqueue(s, (const char*)&len, 4);
599 
600 	while (pos < buf.length()) {
601 		const int x = buf.getAt(pos, tmp, sizeof(tmp));
602 		assert(x > 0);
603 		NET_StreamEnqueue(s, tmp, x);
604 		pos += x;
605 	}
606 }
607 
NET_VPrintf(dbuffer * buf,const char * format,va_list ap,char * str,size_t length)608 void NET_VPrintf (dbuffer* buf, const char* format, va_list ap, char* str, size_t length)
609 {
610 	const int len = Q_vsnprintf(str, length, format, ap);
611 	buf->add(str, len);
612 }
613