UFO: Alien Invasion
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
test_generic.cpp
Go to the documentation of this file.
1 
6 /*
7 Copyright (C) 2002-2020 UFO: Alien Invasion.
8 
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13 
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 
18 See the GNU General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 
24 */
25 
26 #include "test_shared.h"
27 #include "../common/common.h"
28 #include "../common/sha1.h"
29 #include "../common/sha2.h"
30 #include "../common/http.h"
31 #include "../common/binaryexpressionparser.h"
32 #include "../shared/utf8.h"
33 #include "../shared/shared.h"
34 #include "../shared/parse.h"
35 #include "../shared/infostring.h"
36 #include "../shared/stringhunk.h"
37 #include "../shared/entitiesdef.h"
38 #include "../ports/system.h"
39 
40 class GenericTest: public ::testing::Test {
41 protected:
42  static void SetUpTestCase() {
43  TEST_Init();
44  }
45 
46  static void TearDownTestCase() {
47  TEST_Shutdown();
48  NET_Shutdown();
49  }
50 };
51 
52 static void STRHUNK_VisitorTestEntry (const char* string)
53 {
54  ASSERT_STREQ(string, "Test");
55 }
56 
57 static void STRHUNK_VisitorTestEntry2 (const char* string)
58 {
59  ASSERT_STREQ(string, "T");
60 }
61 
62 TEST_F(GenericTest, StringHunks)
63 {
64  stringHunk_t* hunk = STRHUNK_Create(20);
65  ASSERT_TRUE(STRHUNK_Add(hunk, "Test"));
66  ASSERT_EQ(STRHUNK_Size(hunk), 1);
67  ASSERT_EQ(STRHUNK_GetFreeSpace(hunk), 15);
69  STRHUNK_Delete(&hunk);
70  ASSERT_TRUE(nullptr == hunk);
71 
72  hunk = STRHUNK_Create(23);
73  ASSERT_TRUE(STRHUNK_Add(hunk, "Test"));
74  ASSERT_TRUE(STRHUNK_Add(hunk, "Test"));
75  ASSERT_TRUE(STRHUNK_Add(hunk, "Test"));
76  ASSERT_TRUE(STRHUNK_Add(hunk, "Test"));
77  ASSERT_EQ(STRHUNK_Size(hunk), 4);
78  ASSERT_EQ(STRHUNK_GetFreeSpace(hunk), 0);
80 
81  STRHUNK_Reset(hunk);
82  ASSERT_EQ(STRHUNK_Size(hunk), 0);
83 
84  STRHUNK_Delete(&hunk);
85  ASSERT_TRUE(nullptr == hunk);
86 
87  hunk = STRHUNK_Create(5);
88  ASSERT_TRUE(STRHUNK_Add(hunk, "T"));
89  ASSERT_FALSE(STRHUNK_Add(hunk, "Test"));
90  /* the second string is ignored */
91  ASSERT_FALSE(STRHUNK_Add(hunk, "Test"));
92  ASSERT_EQ(STRHUNK_Size(hunk), 2);
94  STRHUNK_Delete(&hunk);
95 }
96 
97 TEST_F(GenericTest, ConstInt)
98 {
99  const constListEntry_t list[] = {
100  {"namespace::power", 1},
101  {"namespace::speed", 2},
102  {"namespace::accuracy", 3},
103  {"namespace::mind", 4},
104  {"namespace::close", 5},
105  {"namespace::heavy", 6},
106  {"namespace::assault", 7},
107  {"namespace::sniper", 8},
108  {"namespace::explosive", 9},
109  {"namespace::hp", 10},
110 
111  {nullptr, -1}
112  };
113  const constListEntry_t list2[] = {
114  {"namespace2::soldier", 0},
115  {"namespace2::scientist", 1},
116  {"namespace2::worker", 2},
117  {"namespace2::pilot", 3},
118  {nullptr, -1}
119  };
120  int out;
121 
122  Com_RegisterConstInt("namespace::variable", 1);
123  ASSERT_TRUE(Com_UnregisterConstVariable("namespace::variable"));
124 
125  Com_RegisterConstInt("namespace::variable", 1);
126  ASSERT_TRUE(Com_UnregisterConstVariable("namespace::variable"));
127 
128  Com_RegisterConstInt("namespace::variable2", 2);
129  Com_RegisterConstInt("namespace::variable3", 3);
130  Com_RegisterConstInt("namespace::variable4", 4);
131  Com_RegisterConstInt("namespace::variable5", 5);
132  Com_RegisterConstInt("namespace::variable6", 6);
133 
134  Com_RegisterConstInt("namespace2::variable2", 10);
135 
136  out = 0;
137  ASSERT_TRUE(Com_GetConstInt("namespace2::variable2", &out));
138  ASSERT_EQ(out, 10);
139  out = 0;
140  ASSERT_TRUE(Com_GetConstInt("namespace::variable2", &out));
141  ASSERT_EQ(out, 2);
142  out = 0;
143  ASSERT_TRUE(Com_GetConstInt("variable2", &out));
144  ASSERT_EQ(out, 10);
145 
146  ASSERT_STREQ(Com_GetConstVariable("namespace", 2), "variable2");
147 
148  ASSERT_TRUE(Com_UnregisterConstVariable("namespace2::variable2"));
149  ASSERT_TRUE(Com_UnregisterConstVariable("namespace::variable2"));
150  ASSERT_TRUE(Com_UnregisterConstVariable("namespace::variable3"));
151  ASSERT_TRUE(Com_UnregisterConstVariable("namespace::variable4"));
152  ASSERT_TRUE(Com_UnregisterConstVariable("namespace::variable5"));
153  ASSERT_TRUE(Com_UnregisterConstVariable("namespace::variable6"));
154 
155  ASSERT_TRUE(!Com_UnregisterConstVariable("namespace::variable"));
156  ASSERT_TRUE(!Com_UnregisterConstVariable("namespace::variable2"));
157  ASSERT_TRUE(!Com_UnregisterConstVariable("namespace::variable3"));
158  ASSERT_TRUE(!Com_UnregisterConstVariable("namespace::variable4"));
159  ASSERT_TRUE(!Com_UnregisterConstVariable("namespace::variable5"));
160  ASSERT_TRUE(!Com_UnregisterConstVariable("namespace::variable6"));
161 
162  Com_RegisterConstList(list);
163  out = 0;
164  ASSERT_TRUE(Com_GetConstInt("sniper", &out));
165  ASSERT_EQ(out, 8);
166 
167  ASSERT_TRUE(Com_UnregisterConstList(list));
168  out = 0;
169  ASSERT_FALSE(Com_GetConstInt("sniper", &out));
170 
171  Com_RegisterConstList(list2);
172 
173  Com_RegisterConstList(list);
174  ASSERT_TRUE(Com_UnregisterConstList(list));
175 
176  out = 0;
177  ASSERT_TRUE(Com_GetConstInt("pilot", &out));
178  ASSERT_EQ(out, 3);
180 }
181 
182 static int testListSorter (linkedList_t* entry1, linkedList_t* entry2, const void* userData)
183 {
184  return strcmp((const char*)entry1->data, (const char*)entry2->data);
185 }
186 
187 TEST_F(GenericTest, LinkedList)
188 {
189  linkedList_t* list = nullptr, *list2;
190  const char* data = "SomeDataForTheLinkedList";
191  const size_t length = strlen(data);
192  linkedList_t* entry;
193  const linkedList_t* entry2;
194  const char* returnedData;
195 
196  entry = LIST_Add(&list, data, length);
197  ASSERT_EQ(LIST_Count(list), 1);
198  ASSERT_TRUE(entry != nullptr);
199  returnedData = (const char*)LIST_GetByIdx(list, 0);
200  ASSERT_TRUE(returnedData != nullptr);
201  entry2 = LIST_ContainsString(list, returnedData);
202  ASSERT_TRUE(entry2 != nullptr);
203  ASSERT_EQ((const void*)entry2->data, (const void*)returnedData);
204  ASSERT_STREQ(static_cast<const char*>(entry2->data), returnedData);
205  LIST_RemoveEntry(&list, entry);
206  ASSERT_EQ(LIST_Count(list), 0);
207  ASSERT_EQ(LIST_Count(LIST_CopyStructure(list)), 0);
208  LIST_Add(&list, data, length);
209  list2 = LIST_CopyStructure(list);
210  ASSERT_EQ(LIST_Count(list2), 1);
211  LIST_Delete(&list2);
212  ASSERT_EQ(LIST_Count(list2), 0);
213  ASSERT_EQ(LIST_Count(list), 1);
214  LIST_Delete(&list);
215  ASSERT_TRUE(nullptr == LIST_GetRandom(list2));
216 
217  LIST_AddString(&list, "test6");
218  LIST_AddString(&list, "test2");
219  LIST_AddString(&list, "test1");
220  LIST_AddString(&list, "test3");
221  LIST_AddString(&list, "test5");
222  LIST_AddString(&list, "test4");
223  LIST_AddString(&list, "test7");
224  ASSERT_EQ(LIST_Count(list), 7);
225  ASSERT_TRUE(nullptr != LIST_GetRandom(list));
226 
227  LIST_Sort(&list, testListSorter, nullptr);
228  ASSERT_STREQ(static_cast<const char*>(LIST_GetByIdx(list, 0)), "test1");
229  ASSERT_STREQ(static_cast<const char*>(LIST_GetByIdx(list, 1)), "test2");
230  ASSERT_STREQ(static_cast<const char*>(LIST_GetByIdx(list, 2)), "test3");
231  ASSERT_STREQ(static_cast<const char*>(LIST_GetByIdx(list, 3)), "test4");
232  ASSERT_STREQ(static_cast<const char*>(LIST_GetByIdx(list, 4)), "test5");
233  ASSERT_STREQ(static_cast<const char*>(LIST_GetByIdx(list, 5)), "test6");
234  ASSERT_STREQ(static_cast<const char*>(LIST_GetByIdx(list, 6)), "test7");
235 }
236 
237 TEST_F(GenericTest, LinkedListIterator)
238 {
239  linkedList_t* list = nullptr;
240  int cnt;
241 
242  LIST_AddString(&list, "test1");
243  LIST_AddString(&list, "test2");
244  LIST_AddString(&list, "test3");
245 
246  cnt = 0;
247  LIST_Foreach(list, char, string) {
248  ASSERT_TRUE(nullptr != string);
249  cnt++;
250  }
251 
252  LIST_Delete(&list);
253 
254  ASSERT_EQ(cnt, 3);
255 
256  list = nullptr;
257  LIST_Foreach(list, char, string) {
258  (void)string;
259  /* we should not be here, because the list is empty */
260  ASSERT_TRUE(false);
261  }
262 }
263 
264 TEST_F(GenericTest, LinkedListIteratorRemove)
265 {
266  linkedList_t* list = nullptr;
267 
268  LIST_AddString(&list, "test1");
269  LIST_AddString(&list, "test2");
270  LIST_AddString(&list, "test3");
271 
272  LIST_Foreach(list, char, string) {
273  Com_Printf("Found string: %s\n", string);
274  LIST_Remove(&list, string);
275  }
276 
277  ASSERT_TRUE(LIST_IsEmpty(list));
278 }
279 
280 TEST_F(GenericTest, PrependStringList)
281 {
282  linkedList_t* list = nullptr;
283 
284  LIST_PrependString(&list, "test2");
285  LIST_PrependString(&list, "test1");
286 
287  ASSERT_STREQ((const char*)LIST_GetByIdx(list, 0), "test1");
288  ASSERT_STREQ((const char*)LIST_GetByIdx(list, 1), "test2");
289 
290  LIST_Delete(&list);
291 }
292 
293 TEST_F(GenericTest, LinkedListStringSort)
294 {
295  linkedList_t* list = nullptr;
296 
297  LIST_AddStringSorted(&list, "test2");
298  LIST_AddStringSorted(&list, "test1");
299  LIST_AddStringSorted(&list, "test3");
300 
301  ASSERT_STREQ((const char*)LIST_GetByIdx(list, 0), "test1");
302 
303  LIST_Delete(&list);
304 }
305 
306 TEST_F(GenericTest, FileSystemBuildLists)
307 {
308  const char* filename, *prev;
309  const char* wildcard = "ufos/**.ufo";
310  const int ufosCnt = FS_BuildFileList(wildcard);
311 
312  ASSERT_TRUE(ufosCnt > 1);
313 
314  prev = nullptr;
315  while ((filename = FS_NextFileFromFileList(wildcard)) != nullptr) {
316  if (prev != nullptr) {
317  ASSERT_EQ(Q_StringSort(prev, filename), -1);
318  }
319  prev = filename;
320  }
321 
322  FS_NextFileFromFileList(nullptr);
323 }
324 
325 TEST_F(GenericTest, InfoStrings)
326 {
327  char info[MAX_INFO_STRING] = "";
328 
329  Info_SetValueForKey(info, sizeof(info), "name", "test");
330 
331  ASSERT_STREQ(Info_ValueForKey(info, "name"), "test");
332  ASSERT_STREQ(Info_ValueForKey(info, "name2"), "");
333  Info_RemoveKey(info, "name");
334  ASSERT_STREQ(Info_ValueForKey(info, "name"), "");
335 
336  Info_SetValueForKey(info, sizeof(info), "name", "\\invalid\\value");
337  ASSERT_STREQ(Info_ValueForKey(info, "name"), "");
338 }
339 
340 TEST_F(GenericTest, TokenizeInfoStrings)
341 {
342  Cvar_Get("password", "test", CVAR_USERINFO, nullptr);
343  char info[MAX_INFO_STRING];
344  const char* s = va(SV_CMD_CONNECT " %i \"%s\"\n", PROTOCOL_VERSION, Cvar_Userinfo(info, sizeof(info)));
345  Cmd_TokenizeString(s, false, false);
346  ASSERT_STREQ(Cmd_Argv(0), SV_CMD_CONNECT);
347  ASSERT_STREQ(Cmd_Argv(1), DOUBLEQUOTE(PROTOCOL_VERSION));
348  ASSERT_STREQ(Cmd_Argv(2), info);
349 }
350 
352 {
353  Cvar_Get("testGeneric_cvar", "testGeneric_cvarValue", CVAR_NOSET, "No set");
354  Cvar_Set("testGeneric_cvar", "test");
355  ASSERT_STREQ(Cvar_GetString("testGeneric_cvar"), "testGeneric_cvarValue");
356 }
357 
358 TEST_F(GenericTest, StringCopiers)
359 {
360  const char src[] = "Командующий, я чрезвычайно рад доложить, что наш проект ОПЭВ был завершён успешно. Я прикрепил к письму "
361  "изображения и схемы прототипа лазерного оружия, в котором реализовано непрерывное волновое излучение достаточной "
362  "мощности в портативном корпусе. Практическое решение лежало не в тщетных попытках увеличения ёмкости аккумуляторной "
363  "батареи, а в использовании радикальных технологий миниатюризации с целью облегчения и уменьшения размеров существующих "
364  "лазеров с химической накачкой. Они использовались довольно долгое время, будучи частью экспериментальных военных "
365  "программ с конца XX века, но их применение всегда сталкивалось с множеством проблем. Модели химических лазеров с "
366  "реальными военными перспективами были слишком большими и громоздкими для использования в бою пехотой. До сих пор их "
367  "расположение было ограничено выбором между лазерными орудиями на сухопутном шасси и батареями морского базирования. "
368  "Боевое применение заключалось в основном для целей противоракетной обороны (ПРО). Тем не менее, теперь мы в ФАЛАНКС "
369  "создали компактное лазерное орудие. Вопреки своему малому размеру, оно полностью способно управлять фтор-дейтериевой "
370  "реакцией и её токсическими продуктами распада без опасности поражения личного состава. Разрешите мне дать вам краткое "
371  "описание принципа его работы. Внутри камеры сгорания этилен окисляется в пламени трифторида азота. В ходе этой реакции "
372  "выделяются свободные радикалы фтора, которые затем приводятся в контакт с газообразной смесью гелия и дейтерия. Дейтерий "
373  "вступает в реакцию с фтором, что приводит к образованию энергетически возбуждённых молекул фторида дейтерия. Они "
374  "подвергаются стимулированной эмиссии в оптическом резонаторе оружия, генерируя лазерный луч. Серия интеллектуальных "
375  "линз фокусирует и направляет луч в точку прицеливания, и даже вносит коррекцию, с учётом небольших перемещений стрелка "
376  "и/или цели. Излишки газообразного дейтерия направляются через сконструированную нами систему фильтров высокого давления, "
377  "которые задерживают все токсичные и радиоактивные молекулы перед выбросом отработанных паров в атмосферу. Фильтр требует "
378  "замены после каждой боевой операции, но разработанная нами система делает этот процесс простым и безболезненным. Если ";
379 
380  long time;
381  const int copies = 10000;
382  char dest[8192];
383  int i;
384 
385  Com_Printf("\n");
386  time = Sys_Milliseconds();
387  for (i = 0; i < copies; ++i) {
388  Q_strncpyz(dest, src, sizeof(dest));
389  }
390  time = Sys_Milliseconds() - time;
391  Com_Printf("%d copies with Q_strncpyz: %ld ms\n", copies, time);
392 
393  time = Sys_Milliseconds();
394  for (i = 0; i < copies; ++i) {
395  UTF8_strncpyz(dest, src, sizeof(dest));
396  }
397  time = Sys_Milliseconds() - time;
398  Com_Printf("%d copies with UTF8_strncpyz: %ld ms\n", copies, time);
399 
400  time = Sys_Milliseconds();
401  for (i = 0; i < copies; ++i) {
402  Com_sprintf(dest, sizeof(dest), "%s", src);
403  }
404  time = Sys_Milliseconds() - time;
405  Com_Printf("%d copies with Com_sprintf: %ld ms\n", copies, time);
406 
407  const char* s = src;
408  int cnt = 0;
409  while (UTF8_next(&s) != -1) {
410  cnt++;
411  }
412  ASSERT_EQ(cnt, UTF8_strlen(src));
413 
414  /* Com_sprintf */
415 
416  /* empty string */
417  Com_sprintf(dest, 1, "aab%c%c", 0xd0, 0x80);
418  ASSERT_EQ(dest[0], '\0');
419 
420  /* trimmed non utf8 */
421  Com_sprintf(dest, 4, "aab%c%c", 0xd0, 0x80);
422  ASSERT_EQ(dest[2], 'b');
423  ASSERT_EQ(dest[3], '\0');
424 
425  /* trimmed utf8 char. */
426  Com_sprintf(dest, 5, "aab%c%c", 0xd0, 0x80);
427  ASSERT_EQ(dest[2], 'b');
428  ASSERT_EQ(dest[3], '\0');
429 
430  /* untrimmed utf8 char. */
431  Com_sprintf(dest, 6, "aab%c%c", 0xd0, 0x80);
432  ASSERT_EQ((unsigned char) dest[3], 0xd0);
433  ASSERT_EQ((unsigned char) dest[4], 0x80);
434  ASSERT_EQ(dest[5], '\0');
435 
436  /* 2 consecutive utf8 char. */
437  Com_sprintf(dest, 7, "aab\xD0\x80\xD0\x80");
438  ASSERT_NE(dest[3], '\0');
439  ASSERT_EQ(dest[5], '\0');
440 
441  /* UTF8_strncpyz */
442 
443  /* empty string */
444  UTF8_strncpyz(dest, "aab\xD0\x80", 1);
445  ASSERT_EQ(dest[0], '\0');
446 
447  /* trimmed non utf8 */
448  UTF8_strncpyz(dest, "aab\xD0\x80", 4);
449  ASSERT_EQ(dest[2], 'b');
450  ASSERT_EQ(dest[3], '\0');
451 
452  /* trimmed utf8 char. */
453  UTF8_strncpyz(dest, "aab\xD0\x80", 5);
454  ASSERT_EQ(dest[2], 'b');
455  ASSERT_EQ(dest[3], '\0');
456 
457  /* untrimmed utf8 char. */
458  UTF8_strncpyz(dest, "aab\xD0\x80", 6);
459  ASSERT_EQ((unsigned char) dest[3], 0xd0);
460  ASSERT_EQ((unsigned char) dest[4], 0x80);
461  ASSERT_EQ(dest[5], '\0');
462 
463  /* 2 consecutive utf8 char. */
464  UTF8_strncpyz(dest, "aab\xD0\x80\xD0\x80", 7);
465  ASSERT_NE(dest[3], '\0');
466  ASSERT_EQ(dest[5], '\0');
467 }
468 
469 TEST_F(GenericTest, StringFunctions)
470 {
471  char targetBuf[256];
472  char buf[16];
473  const size_t length = lengthof(targetBuf);
474 
475  ASSERT_FALSE(Q_strreplace("ReplaceNothing", "###", "foobar", targetBuf, length));
476  ASSERT_TRUE(Q_strreplace("Replace###Something", "###", "foobar", targetBuf, length));
477  ASSERT_STREQ(targetBuf, "ReplacefoobarSomething");
478 
479  ASSERT_TRUE(Q_strreplace("Replace#", "#", "foobar", targetBuf, length));
480  ASSERT_STREQ(targetBuf, "Replacefoobar");
481 
482  ASSERT_TRUE(Q_strreplace("#Replace", "#", "foobar", targetBuf, length));
483  ASSERT_STREQ(targetBuf, "foobarReplace");
484 
485  ASSERT_TRUE(Q_strreplace("#Replace#", "#", "foobar", targetBuf, length));
486  ASSERT_STREQ(targetBuf, "foobarReplace#");
487 
488  ASSERT_FALSE(Q_strreplace("#ReplaceNothing#", "##", "foobar", targetBuf, length));
489 
490  Q_strncpyz(buf, "foobar", sizeof(buf));
491  ASSERT_STREQ(Com_ConvertToASCII7(buf), "foobar");
492 
493  buf[0] = '\177';
494  ASSERT_STREQ(Com_ConvertToASCII7(buf), ".oobar");
495 
496  buf[5] = '\177';
497  ASSERT_STREQ(Com_ConvertToASCII7(buf), ".ooba.");
498 
499 
500  /* UTF8_char_offset_to_byte_offset */
501 
502  Q_strncpyz(buf, "mn\xD0\x80opq\xD0\x81r", sizeof(buf));
503 
504  ASSERT_EQ(UTF8_char_offset_to_byte_offset(buf, 4), 5);
505  ASSERT_EQ(UTF8_char_offset_to_byte_offset(buf, 0), 0);
506  ASSERT_EQ(UTF8_char_offset_to_byte_offset(buf, -1), 0);
507  ASSERT_EQ(UTF8_char_offset_to_byte_offset(buf, 999), 10);
508 
509  /* UTF8_delete_char_at */
510 
511  Q_strncpyz(buf, "mn\xD0\x80opq\xD0\x81r", sizeof(buf));
512 
513  /* single-byte char before any multi-byte chars */
514  ASSERT_EQ(UTF8_delete_char_at(buf, 4), 1);
515  ASSERT_STREQ(buf, "mn\xD0\x80oq\xD0\x81r");
516 
517  /* multi-byte char after a multi-byte char */
518  ASSERT_EQ(UTF8_delete_char_at(buf, 5), 2);
519  ASSERT_STREQ(buf, "mn\xD0\x80oqr");
520 
521  /* negative index deletes first char */
522  ASSERT_EQ(UTF8_delete_char_at(buf, -1), 1);
523  ASSERT_STREQ(buf, "n\xD0\x80oqr");
524 
525  /* too-high index deletes nothing */
526  ASSERT_EQ(UTF8_delete_char_at(buf, 999), 0);
527  ASSERT_STREQ(buf, "n\xD0\x80oqr");
528 
529  /* UTF8_insert_char_at */
530 
531  Q_strncpyz(buf, "m\xD0\x82opqr", sizeof(buf));
532 
533  /* single-byte char before any multi-byte chars */
534  ASSERT_EQ(UTF8_insert_char_at(buf, sizeof(buf), 1, (int)'n'), 1);
535  ASSERT_STREQ(buf, "mn\xD0\x82opqr");
536 
537  /* multi-byte char after a multi-byte char */
538  ASSERT_EQ(UTF8_insert_char_at(buf, sizeof(buf), 5, 0x0403), 2);
539  ASSERT_STREQ(buf, "mn\xD0\x82op\xD0\x83qr");
540 
541  /* negative index inserts as first char */
542  ASSERT_EQ(UTF8_insert_char_at(buf, sizeof(buf), -1, 0x0404), 2);
543  ASSERT_STREQ(buf, "\xD0\x84mn\xD0\x82op\xD0\x83qr");
544 
545  /* too-high index inserts as last char */
546  ASSERT_EQ(UTF8_insert_char_at(buf, sizeof(buf), 999, 0x0405), 2);
547  ASSERT_STREQ(buf, "\xD0\x84mn\xD0\x82op\xD0\x83qr\xD0\x85");
548 
549  Q_strncpyz(buf, "mnopqr", sizeof(buf));
550 
551  /* trigger buffer overrun protection using multi-byte char */
552  ASSERT_EQ(UTF8_insert_char_at(buf, 8, 0, 0x0405), 0);
553  ASSERT_STREQ(buf, "mnopqr");
554 
555  /* trigger buffer overrun protection using single-byte char */
556  ASSERT_EQ(UTF8_insert_char_at(buf, 7, 0, (int)'o'), 0);
557  ASSERT_STREQ(buf, "mnopqr");
558 
559  /* exactly fill the buffer using multi-byte char */
560  ASSERT_EQ(UTF8_insert_char_at(buf, 9, 1, 0x0406), 2);
561  ASSERT_STREQ(buf, "m\xD0\x86nopqr");
562 
563  /* exactly fill the buffer using single-byte char */
564  ASSERT_EQ(UTF8_insert_char_at(buf, 10, 1, (int)'s'), 1);
565  ASSERT_STREQ(buf, "ms\xD0\x86nopqr");
566 
567  char catBuf[32] = "";
568  Q_strcat(catBuf, sizeof(catBuf), "foo");
569  Q_strcat(catBuf, sizeof(catBuf), "bar");
570  ASSERT_STREQ(catBuf, "foobar");
571 }
572 
573 TEST_F(GenericTest, HttpHelperFunctions)
574 {
575  char scheme[6];
576  char server[512];
577  char uriPath[512];
578  int port;
579  const char* url;
580 
581  url = "http://www.test.domain.com:123/someScript.cgi?parameter";
582  ASSERT_TRUE(HTTP_ExtractComponents(url, scheme, sizeof(scheme), server, sizeof(server), uriPath, sizeof(uriPath), &port));
583  ASSERT_STREQ("http", scheme);
584  ASSERT_STREQ("www.test.domain.com", server);
585  ASSERT_EQ(port, 123);
586  ASSERT_STREQ("/someScript.cgi?parameter", uriPath);
587 
588  url = "hTTpS://Www.TeST.domain.coM:123/someScript.cgi?parameter";
589  ASSERT_TRUE(HTTP_ExtractComponents(url, scheme, sizeof(scheme), server, sizeof(server), uriPath, sizeof(uriPath), &port));
590  ASSERT_STREQ("https", scheme);
591  ASSERT_STREQ("www.test.domain.com", server);
592  ASSERT_EQ(port, 123);
593  ASSERT_STREQ("/someScript.cgi?parameter", uriPath);
594 
595  url = "http://www.test.domain.com/someScript.cgi?parameter";
596  ASSERT_TRUE(HTTP_ExtractComponents(url, scheme, sizeof(scheme), server, sizeof(server), uriPath, sizeof(uriPath), &port));
597  ASSERT_STREQ("http", scheme);
598  ASSERT_STREQ("www.test.domain.com", server);
599  ASSERT_EQ(80, port);
600  ASSERT_STREQ("/someScript.cgi?parameter", uriPath);
601 
602  url = "http://www.test.domain.com";
603  ASSERT_TRUE(HTTP_ExtractComponents(url, scheme, sizeof(scheme), server, sizeof(server), uriPath, sizeof(uriPath), &port));
604  ASSERT_STREQ("http", scheme);
605  ASSERT_STREQ("www.test.domain.com", server);
606  ASSERT_EQ(80, port);
607  ASSERT_STREQ("", uriPath);
608 
609  url = "http://www.test.domain.com/";
610  ASSERT_TRUE(HTTP_ExtractComponents(url, scheme, sizeof(scheme), server, sizeof(server), uriPath, sizeof(uriPath), &port));
611  ASSERT_STREQ("http", scheme);
612  ASSERT_STREQ("www.test.domain.com", server);
613  ASSERT_EQ(80, port);
614  ASSERT_STREQ("/", uriPath);
615 
616  url = "http://ufoai.org/ufo/masterserver.php?query";
617  ASSERT_TRUE(HTTP_ExtractComponents(url, scheme, sizeof(scheme), server, sizeof(server), uriPath, sizeof(uriPath), &port));
618  ASSERT_STREQ("http", scheme);
619  ASSERT_STREQ("ufoai.org", server);
620  ASSERT_EQ(80, port);
621  ASSERT_STREQ("/ufo/masterserver.php?query", uriPath);
622 
623  url = "https://ufoai.org/ufo/masterserver.php?query";
624  ASSERT_TRUE(HTTP_ExtractComponents(url, scheme, sizeof(scheme), server, sizeof(server), uriPath, sizeof(uriPath), &port));
625  ASSERT_STREQ("https", scheme);
626  ASSERT_STREQ("ufoai.org", server);
627  ASSERT_EQ(443, port);
628  ASSERT_STREQ("/ufo/masterserver.php?query", uriPath);
629 
630  url = "https://ufoai.org:0/ufo/masterserver.php?query";
631  ASSERT_FALSE(HTTP_ExtractComponents(url, scheme, sizeof(scheme), server, sizeof(server), uriPath, sizeof(uriPath), &port));
632  url = "https://ufoai.org:65536/ufo/masterserver.php?query";
633  ASSERT_FALSE(HTTP_ExtractComponents(url, scheme, sizeof(scheme), server, sizeof(server), uriPath, sizeof(uriPath), &port));
634  url = "https://ufoai.org:80sas/ufo/masterserver.php?query";
635  ASSERT_FALSE(HTTP_ExtractComponents(url, scheme, sizeof(scheme), server, sizeof(server), uriPath, sizeof(uriPath), &port));
636  url = "https://ufoai.org:000000080/ufo/masterserver.php?query";
637  ASSERT_FALSE(HTTP_ExtractComponents(url, scheme, sizeof(scheme), server, sizeof(server), uriPath, sizeof(uriPath), &port));
638  url = "ftp://ufoai.org:0/ufo/masterserver.php?query";
639  ASSERT_FALSE(HTTP_ExtractComponents(url, scheme, sizeof(scheme), server, sizeof(server), uriPath, sizeof(uriPath), &port));
640  url = "https:///ufo/masterserver.php?query";
641  ASSERT_FALSE(HTTP_ExtractComponents(url, scheme, sizeof(scheme), server, sizeof(server), uriPath, sizeof(uriPath), &port));
642 }
643 
644 TEST_F(GenericTest, NetResolv)
645 {
646  char ipServer[MAX_VAR];
647  ASSERT_TRUE(NET_ResolvNode("localhost", ipServer, sizeof(ipServer))) << "failed to resolve localhost";
648  ASSERT_STREQ("127.0.0.1", ipServer);
649 }
650 
651 TEST_F(GenericTest, UnsignedIntToBinary)
652 {
653  const char* buf = Com_UnsignedIntToBinary(3);
654  ASSERT_STREQ(buf, "00000000 00000000 00000000 00000011");
655 
656  buf = Com_UnsignedIntToBinary(255);
657  ASSERT_STREQ(buf, "00000000 00000000 00000000 11111111");
658 
659  buf = Com_UnsignedIntToBinary(65536);
660  ASSERT_STREQ(buf, "00000000 00000001 00000000 00000000");
661 
662  buf = Com_UnsignedIntToBinary(65535);
663  ASSERT_STREQ(buf, "00000000 00000000 11111111 11111111");
664 
665  buf = Com_ByteToBinary(2);
666  ASSERT_STREQ(buf, "00000010");
667 
668  buf = Com_ByteToBinary(255);
669  ASSERT_STREQ(buf, "11111111");
670 }
671 
672 TEST_F(GenericTest, StringCheckFunctions)
673 {
674  const char* strNull = nullptr;
675  const char* strEmpty = "";
676  const char* strValid = "someString";
677  ASSERT_TRUE(Q_strnull(strNull));
678  ASSERT_TRUE(Q_strnull(strEmpty));
679  ASSERT_FALSE(Q_strnull(strValid));
680  ASSERT_TRUE(Q_strvalid(strValid));
681  ASSERT_FALSE(Q_strvalid(strEmpty));
682  ASSERT_FALSE(Q_strvalid(strNull));
683 }
684 
685 TEST_F(GenericTest, EntitiesDef)
686 {
687  byte* fileBuffer;
688 
689  ASSERT_NE(-1, FS_LoadFile("ufos/entities.ufo", &fileBuffer)) << "Could not load ufos/entities.ufo.";
690  ASSERT_TRUE(fileBuffer != nullptr);
691 
692  const char* buf = (const char*) fileBuffer;
693  ASSERT_EQ(ED_Parse(buf), ED_OK);
694 
695  ASSERT_TRUE(numEntityDefs > 0);
696 
697  bool worldSpawnFound = false;
698  for (int i = 0; i < numEntityDefs; i++) {
699  const entityDef_t* e = &entityDefs[i];
700 
701  ASSERT_TRUE(nullptr != e);
702  if (Q_streq(e->classname, "worldspawn")) {
703  worldSpawnFound = true;
704 
705  ASSERT_TRUE(e->numKeyDefs > 10);
706  for (int j = 0; j < e->numKeyDefs; j++) {
707  const entityKeyDef_t* keyDef = &e->keyDefs[j];
708  ASSERT_TRUE(nullptr != keyDef);
709  }
710  }
711  }
712 
713  ASSERT_TRUE(worldSpawnFound);
714 
715  FS_FreeFile(fileBuffer);
716  ED_Free();
717 }
718 
719 TEST_F(GenericTest, GetBlock)
720 {
721  {
722  const char* test = "invalid block";
723  const int length = Com_GetBlock(&test, nullptr);
724  ASSERT_EQ(length, -1);
725  }
726  {
727  const char* test = "{the block length }";
728  const char* buf = test;
729  const size_t expected = strlen(test) - 2;
730  const char* start = nullptr;
731  const int length = Com_GetBlock(&buf, &start);
732  ASSERT_EQ(length, expected);
733  ASSERT_EQ(strncmp(start, test + 1, length), 0) << start << " and " << (test + 1) << " should match on the first " << length << " characters";
734  }
735 }
736 
737 TEST_F(GenericTest, ComFilePath)
738 {
739  char buf[32];
740  Com_FilePath("/foo/bar/file.txt", buf, sizeof(buf));
741  ASSERT_STREQ(buf, "/foo/bar");
742 
743  Com_FilePath("/foo/bar/a/little/bit/too/long/for/the/buffer/file.txt", buf, sizeof(buf));
744  ASSERT_STREQ(buf, "");
745 }
746 
748 {
749  const char* md5 = Com_MD5File("media/DejaVuSans.ttf");
750  const char* expected = "c4adcbdd6ec636e0b19cd6aabe85e8fb";
751  Com_Printf("got: '%s', expected '%s'\n", md5, expected);
752  ASSERT_STREQ(md5, expected);
753 }
754 
755 TEST_F(GenericTest, MD5Buffer)
756 {
757  const char* in = "Test";
758  const char* expected = "0cbc6611f5540bd0809a388dc95a615b";
759  const char* md5 = Com_MD5Buffer((const byte*)in, strlen(in));
760  Com_Printf("got: '%s', expected '%s'\n", md5, expected);
761  ASSERT_STREQ(md5, expected);
762 }
763 
764 TEST_F(GenericTest, SHA1Buffer)
765 {
766  const char* in = "Test";
767  const char* expected = "640ab2bae07bedc4c163f679a746f7ab7fb5d1fa";
768  char digest[41];
769  Com_SHA1Buffer((const byte*)in, strlen(in), digest);
770  Com_Printf("got: '%s', expected '%s'\n", digest, expected);
771  ASSERT_STREQ(digest, expected);
772 }
773 
774 TEST_F(GenericTest, SHA2Buffer)
775 {
776  const char* in = "Test";
777  const char* expected = "532eaabd9574880dbf76b9b8cc00832c20a6ec113d682299550d7a6e0f345e25";
778  byte digest[32];
779  Com_SHA2Csum((const byte*)in, strlen(in), digest);
780  char buf[65];
781  Com_SHA2ToHex(digest, buf);
782  Com_Printf("got: '%s', expected '%s'\n", buf, expected);
783  ASSERT_STREQ(buf, expected);
784 }
785 
789 static int TEST_BEP (const char* id, const void* userdata)
790 {
791  return Q_streq(id, "a") || Q_streq(id, "c");
792 }
793 
795 {
796  ASSERT_TRUE(BEP_Evaluate("", TEST_BEP));
797  ASSERT_TRUE(BEP_Evaluate("a", TEST_BEP));
798  ASSERT_TRUE(BEP_Evaluate("c", TEST_BEP));
799  ASSERT_FALSE(BEP_Evaluate("d", TEST_BEP));
800  ASSERT_TRUE(BEP_Evaluate("a|b", TEST_BEP));
801  ASSERT_FALSE(BEP_Evaluate("a&b", TEST_BEP));
802  ASSERT_TRUE(BEP_Evaluate("a&(b|c)", TEST_BEP));
803  ASSERT_TRUE(BEP_Evaluate("a|(b&c)", TEST_BEP));
804  ASSERT_FALSE(BEP_Evaluate("b|(d&c)", TEST_BEP));
805  ASSERT_TRUE(BEP_Evaluate("b|(a&c)", TEST_BEP));
806  ASSERT_FALSE(BEP_Evaluate("a^c", TEST_BEP));
807  ASSERT_TRUE(BEP_Evaluate("a^d", TEST_BEP));
808  ASSERT_TRUE(BEP_Evaluate("!z", TEST_BEP));
809  ASSERT_FALSE(BEP_Evaluate("!!z", TEST_BEP));
810  ASSERT_FALSE(BEP_Evaluate("!a", TEST_BEP));
811 }
list of script aliases to register
Definition: scripts.h:232
bool Q_strnull(const char *string)
Definition: shared.h:138
const char * Cmd_Argv(int arg)
Returns a given argument.
Definition: cmd.cpp:516
const char * FS_NextFileFromFileList(const char *files)
Returns the next file that is found in the virtual filesystem identified by the given file pattern...
Definition: files.cpp:1079
int UTF8_char_offset_to_byte_offset(char *str, int pos)
Convert UTF-8 character offset to a byte offset in the given string.
Definition: utf8.cpp:227
void Com_SHA2ToHex(const byte digest[32], char final[65])
Definition: sha2.cpp:301
#define CVAR_USERINFO
Definition: cvar.h:41
bool Com_UnregisterConstVariable(const char *name)
Removes a registered constant from the script mapping hash table.
Definition: scripts.cpp:147
size_t UTF8_strlen(const char *str)
Count the number of character (not the number of bytes) of a zero termination string.
Definition: utf8.cpp:207
void LIST_PrependString(linkedList_t **listDest, const char *data)
Adds a string as first entry to a linked list.
Definition: list.cpp:127
void NET_Shutdown(void)
Definition: net.cpp:337
bool Com_SHA1Buffer(const unsigned char *buf, unsigned int len, char digest[41])
Definition: sha1.cpp:358
const char * va(const char *format,...)
does a varargs printf into a temp buffer, so I don't need to have varargs versions of all text functi...
Definition: shared.cpp:410
#define SV_CMD_CONNECT
Definition: q_shared.h:588
void LIST_AddStringSorted(linkedList_t **listDest, const char *data)
Definition: list.cpp:107
void * LIST_GetByIdx(linkedList_t *list, int index)
Get an entry of a linked list by its index in the list.
Definition: list.cpp:362
Evaluates stuff like this expression.
void * data
Definition: list.h:31
char * UTF8_strncpyz(char *dest, const char *src, size_t limit)
UTF8 capable string copy function.
Definition: utf8.cpp:247
bool Com_sprintf(char *dest, size_t size, const char *fmt,...)
copies formatted string with buffer-size checking
Definition: shared.cpp:494
static void SetUpTestCase()
const char * filename
Definition: ioapi.h:41
entityDef_t entityDefs[ED_MAX_DEFS+1]
Definition: entitiesdef.cpp:46
void Com_FilePath(const char *in, char *out, size_t size)
Returns the path up to, but not including the last /.
Definition: shared.cpp:319
int UTF8_next(const char **str)
Get the next utf-8 character from the given string.
Definition: utf8.cpp:132
bool STRHUNK_Add(stringHunk_t *hunk, const char *string)
Definition: stringhunk.cpp:32
void Cmd_TokenizeString(const char *text, bool macroExpand, bool replaceWhitespaces)
Parses the given string into command line tokens.
Definition: cmd.cpp:565
int FS_LoadFile(const char *path, byte **buffer)
Filenames are relative to the quake search path.
Definition: files.cpp:384
void Com_Printf(const char *const fmt,...)
Definition: common.cpp:386
bool HTTP_ExtractComponents(const char *url, char *scheme, size_t schemeLength, char *host, size_t hostLength, char *path, size_t pathLength, int *port)
Extract the servername, the port and the path part of the given url.
Definition: http.cpp:38
int FS_BuildFileList(const char *fileList)
Build a filelist.
Definition: files.cpp:960
void LIST_Delete(linkedList_t **list)
Definition: list.cpp:195
void Com_RegisterConstList(const constListEntry_t constList[])
Registers a list of string aliases.
Definition: scripts.cpp:253
voidpf void * buf
Definition: ioapi.h:42
int LIST_Count(const linkedList_t *list)
Definition: list.cpp:344
#define Q_strvalid(string)
Definition: shared.h:141
static int testListSorter(linkedList_t *entry1, linkedList_t *entry2, const void *userData)
bool BEP_Evaluate(const char *expr, BEPEvaluteCallback_t varFuncParam, const void *userdata)
const char * Info_ValueForKey(const char *s, const char *key)
Searches the string for the given key and returns the associated value, or an empty string...
Definition: infostring.cpp:39
static void STRHUNK_VisitorTestEntry2(const char *string)
const char * Com_MD5File(const char *fn, int length=0)
Compute the md5sum of a given file.
Definition: md5.cpp:257
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
Definition: shared.cpp:457
bool LIST_Remove(linkedList_t **list, const void *data)
Definition: list.cpp:213
int UTF8_delete_char_at(char *s, int pos)
Delete a whole (possibly multibyte) character from a string.
Definition: utf8.cpp:35
char * Com_ConvertToASCII7(char *s)
Remove high character values and only keep ascii. This can be used to print utf-8 characters to the c...
Definition: shared.cpp:82
#define MAX_INFO_STRING
Definition: infostring.h:36
const char * Com_MD5Buffer(const byte *buf, size_t len)
Compute the md5sum of the given buffer.
Definition: md5.cpp:300
cvar_t * Cvar_Get(const char *var_name, const char *var_value, int flags, const char *desc)
Init or return a cvar.
Definition: cvar.cpp:342
void LIST_AddString(linkedList_t **listDest, const char *data)
Adds an string to a new or to an already existing linked list. The string is copied here...
Definition: list.cpp:139
#define MAX_VAR
Definition: shared.h:36
QGL_EXTERN GLuint GLsizei GLsizei * length
Definition: r_gl.h:110
stringHunk_t * STRHUNK_Create(size_t size)
Definition: stringhunk.cpp:88
void TEST_Shutdown(void)
Definition: test_shared.cpp:34
void LIST_Sort(linkedList_t **list, linkedListSort_t sorter, const void *userData)
int STRHUNK_Size(const stringHunk_t *hunk)
Definition: stringhunk.cpp:78
#define CVAR_NOSET
Definition: cvar.h:43
int UTF8_insert_char_at(char *s, int n, int pos, int c)
Insert a (possibly multibyte) UTF-8 character into a string.
Definition: utf8.cpp:63
static int TEST_BEP(const char *id, const void *userdata)
The string 'a' and 'c' evaluates to true - everything else to false.
void ED_Free(void)
static void STRHUNK_VisitorTestEntry(const char *string)
int numEntityDefs
Definition: entitiesdef.cpp:45
int numKeyDefs
Definition: entitiesdef.h:76
const linkedList_t * LIST_ContainsString(const linkedList_t *list, const char *string)
Searches for the first occurrence of a given string.
Definition: list.cpp:73
linkedList_t * LIST_CopyStructure(linkedList_t *src)
int ED_Parse(const char *data_p)
QGL_EXTERN GLenum GLuint * dest
Definition: r_gl.h:101
bool LIST_RemoveEntry(linkedList_t **list, linkedList_t *entry)
Removes one entry from the linked list.
Definition: list.cpp:172
void Com_RegisterConstInt(const char *name, int value)
Register mappings between script strings and enum values for values of the type V_INT.
Definition: scripts.cpp:198
const GLuint *typedef void(APIENTRY *GenRenderbuffersEXT_t)(GLsizei
Definition: r_gl.h:189
int Com_GetBlock(const char **text, const char **start)
Get the start and end point of a block in the given text.
Definition: parse.cpp:272
CGAME_HARD_LINKED_FUNCTIONS linkedList_t * LIST_Add(linkedList_t **listDest, void const *data, size_t length)
char * classname
Definition: entitiesdef.h:74
const char * Com_UnsignedIntToBinary(uint32_t x)
Definition: common.cpp:1021
const char * Com_GetConstVariable(const char *space, int value)
Searches the mapping variable for a given integer value and a namespace.
Definition: scripts.cpp:122
static void TearDownTestCase()
void Info_SetValueForKey(char *s, const size_t size, const char *key, const char *value)
Adds a new entry into string with given value.
Definition: infostring.cpp:169
const char * Com_ByteToBinary(byte x)
Definition: common.cpp:1005
QGL_EXTERN GLint i
Definition: r_gl.h:113
void TEST_Init(void)
Definition: test_shared.cpp:72
void STRHUNK_Reset(stringHunk_t *hunk)
Definition: stringhunk.cpp:55
TEST_F(GenericTest, StringHunks)
cvar_t * port
Definition: common.cpp:58
void Q_strcat(char *dest, size_t destsize, const char *format,...)
Safely (without overflowing the destination buffer) concatenates two strings.
Definition: shared.cpp:475
#define ED_OK
Definition: entitiesdef.h:33
const char * Cvar_Userinfo(char *info, size_t infoSize)
Returns an info string containing all the CVAR_USERINFO cvars.
Definition: cvar.cpp:967
void Info_RemoveKey(char *s, const char *key)
Searches through s for key and remove is.
Definition: infostring.cpp:95
bool Q_strreplace(const char *source, const char *pattern, const char *replace, char *dest, size_t destsize)
Replaces the first occurence of the given pattern in the source string with the given replace string...
Definition: shared.cpp:596
#define LIST_Foreach(list, type, var)
Iterates over a linked list, it's safe to delete the returned entry from the list while looping over ...
Definition: list.h:41
void Com_SHA2Csum(const byte *buf, uint32_t buflen, byte digest[32])
Output SHA-256(buf)
Definition: sha2.cpp:292
int Q_StringSort(const void *string1, const void *string2)
Compare two strings.
Definition: shared.cpp:385
#define lengthof(x)
Definition: shared.h:105
GLsizei const GLvoid * data
Definition: r_gl.h:152
cvar_t * Cvar_Set(const char *varName, const char *value,...)
Sets a cvar value.
Definition: cvar.cpp:615
const char * Cvar_GetString(const char *varName)
Returns the value of cvar as string.
Definition: cvar.cpp:210
#define Q_streq(a, b)
Definition: shared.h:136
bool LIST_IsEmpty(const linkedList_t *list)
Checks whether the given list is empty.
Definition: list.cpp:335
#define PROTOCOL_VERSION
Definition: common.h:134
size_t STRHUNK_GetFreeSpace(const stringHunk_t *hunk)
Definition: stringhunk.cpp:83
uint8_t byte
Definition: ufotypes.h:34
void STRHUNK_Delete(stringHunk_t **hunk)
Definition: stringhunk.cpp:97
void * LIST_GetRandom(linkedList_t *list)
Definition: list.cpp:381
#define DOUBLEQUOTE(x)
Definition: shared.h:90
void STRHUNK_Visit(stringHunk_t *hunk, stringHunkVisitor_t visitor)
Definition: stringhunk.cpp:62
bool Com_GetConstInt(const char *name, int *value)
Searches whether a given value was registered as a string to int mapping.
Definition: scripts.cpp:74
entityKeyDef_t * keyDefs
Definition: entitiesdef.h:75
bool Com_UnregisterConstList(const constListEntry_t constList[])
Unregisters a list of string aliases.
Definition: scripts.cpp:237
void FS_FreeFile(void *buffer)
Definition: files.cpp:411
bool NET_ResolvNode(const char *node, char *buf, size_t bufLength)
Definition: net.cpp:1229
int Sys_Milliseconds(void)
Definition: unix_shared.cpp:41