1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: i_system.cpp 4556 2014-02-10 02:48:10Z dr_sean $
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 // Copyright (C) 2006-2014 by The Odamex Team.
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. See the
17 // GNU General Public License for more details.
18 //
19 // DESCRIPTION:
20 // System specific interface stuff.
21 //
22 //-----------------------------------------------------------------------------
23
24 #include <sstream>
25 #include <limits>
26
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <cstring>
30 #include <stdarg.h>
31 #include <math.h>
32
33 #ifdef OSX
34 #include <mach/clock.h>
35 #include <mach/mach.h>
36 #endif
37
38 #include "win32inc.h"
39 #ifdef _WIN32
40 #include <conio.h>
41 #include <io.h>
42 #include <process.h>
43 #include <mmsystem.h>
44 #include <direct.h> // SoM: I don't know HOW this has been overlooked until now...
45 #ifndef _XBOX
46 #include <winsock2.h>
47 #endif // !_XBOX
48 #endif
49
50 #ifdef UNIX
51 #define HAVE_PWD_H
52
53 #include <fnmatch.h>
54 #include <sys/stat.h>
55 #include <sys/types.h>
56 #include <sys/time.h>
57 #include <pwd.h>
58 #include <unistd.h>
59 #include <limits.h>
60
61 #endif
62
63 #include "errors.h"
64
65 #include "doomtype.h"
66 #include "version.h"
67 #include "doomdef.h"
68 #include "cmdlib.h"
69 #include "m_argv.h"
70 #include "m_misc.h"
71
72 #include "d_main.h"
73 #include "d_net.h"
74 #include "g_game.h"
75 #include "i_system.h"
76 #include "i_net.h"
77 #include "c_dispatch.h"
78 #include "sv_main.h"
79
80 #ifdef _WIN32
81 UINT TimerPeriod;
82 #endif
83
84 ticcmd_t emptycmd;
I_BaseTiccmd(void)85 ticcmd_t *I_BaseTiccmd(void)
86 {
87 return &emptycmd;
88 }
89
90 /* [Russell] - Modified to accomodate a minimal allowable heap size */
91 // These values are in megabytes
92 size_t def_heapsize = 64;
93 const size_t min_heapsize = 8;
94
95 // The size we got back from I_ZoneBase in megabytes
96 size_t got_heapsize = 0;
97
98 DWORD LanguageIDs[4];
99
100 //
101 // I_MegabytesToBytes
102 //
103 // Returns the megabyte value of size in bytes
I_MegabytesToBytes(size_t Megabytes)104 size_t I_MegabytesToBytes (size_t Megabytes)
105 {
106 return (Megabytes*1024*1024);
107 }
108
109 //
110 // I_BytesToMegabytes
111 //
112 // Returns the byte value of size in megabytes
I_BytesToMegabytes(size_t Bytes)113 size_t I_BytesToMegabytes (size_t Bytes)
114 {
115 if (!Bytes)
116 return 0;
117
118 return (Bytes/1024/1024);
119 }
120
121 //
122 // I_ZoneBase
123 //
124 // Allocates a portion of system memory for the Zone Memory Allocator, returns
125 // the 'size' of what it could allocate in its parameter
I_ZoneBase(size_t * size)126 void *I_ZoneBase (size_t *size)
127 {
128 void *zone = NULL;
129
130 // User wanted a different default size
131 const char *p = Args.CheckValue ("-heapsize");
132
133 if (p)
134 def_heapsize = atoi(p);
135
136 if (def_heapsize < min_heapsize)
137 def_heapsize = min_heapsize;
138
139 // Set the size
140 *size = I_MegabytesToBytes(def_heapsize);
141
142 // Allocate the def_heapsize, otherwise try to allocate a smaller amount
143 while ((zone == NULL) && (*size >= I_MegabytesToBytes(min_heapsize)))
144 {
145 zone = malloc (*size);
146
147 if (zone != NULL)
148 break;
149
150 *size -= I_MegabytesToBytes(1);
151 }
152
153 // Our heap size we received
154 got_heapsize = I_BytesToMegabytes(*size);
155
156 // Die if the system has insufficient memory
157 if (got_heapsize < min_heapsize)
158 I_FatalError("I_ZoneBase: Insufficient memory available! Minimum size "
159 "is %lu MB but got %lu MB instead",
160 min_heapsize,
161 got_heapsize);
162
163 return zone;
164 }
165
I_BeginRead(void)166 void I_BeginRead(void)
167 {
168 }
169
I_EndRead(void)170 void I_EndRead(void)
171 {
172 }
173
174 //
175 // I_GetTime
176 //
177 // [SL] Retrieve an arbitrarily-based time from a high-resolution timer with
178 // nanosecond accuracy.
179 //
I_GetTime()180 dtime_t I_GetTime()
181 {
182 #if defined OSX
183 clock_serv_t cclock;
184 mach_timespec_t mts;
185
186 host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
187 clock_get_time(cclock, &mts);
188 mach_port_deallocate(mach_task_self(), cclock);
189 return mts.tv_sec * 1000LL * 1000LL * 1000LL + mts.tv_nsec;
190
191 #elif defined UNIX
192 timespec ts;
193 clock_gettime(CLOCK_MONOTONIC, &ts);
194 return ts.tv_sec * 1000LL * 1000LL * 1000LL + ts.tv_nsec;
195
196 #elif defined WIN32 && !defined _XBOX
197 static bool initialized = false;
198 static uint64_t initial_count;
199 static double nanoseconds_per_count;
200
201 if (!initialized)
202 {
203 QueryPerformanceCounter((LARGE_INTEGER*)&initial_count);
204
205 uint64_t temp;
206 QueryPerformanceFrequency((LARGE_INTEGER*)&temp);
207 nanoseconds_per_count = 1000.0 * 1000.0 * 1000.0 / double(temp);
208
209 initialized = true;
210 }
211
212 uint64_t current_count;
213 QueryPerformanceCounter((LARGE_INTEGER*)¤t_count);
214
215 return nanoseconds_per_count * (current_count - initial_count);
216
217 #endif
218 }
219
I_MSTime()220 dtime_t I_MSTime()
221 {
222 return I_ConvertTimeToMs(I_GetTime());
223 }
224
I_ConvertTimeToMs(dtime_t value)225 dtime_t I_ConvertTimeToMs(dtime_t value)
226 {
227 return value / 1000000LL;
228 }
229
I_ConvertTimeFromMs(dtime_t value)230 dtime_t I_ConvertTimeFromMs(dtime_t value)
231 {
232 return value * 1000000LL;
233 }
234
235 //
236 // I_Sleep
237 //
238 // Sleeps for the specified number of nanoseconds, yielding control to the
239 // operating system. In actuality, the highest resolution availible with
240 // the select() function is 1 microsecond, but the nanosecond parameter
241 // is used for consistency with I_GetTime().
242 //
I_Sleep(dtime_t sleep_time)243 void I_Sleep(dtime_t sleep_time)
244 {
245 #if defined UNIX
246 usleep(sleep_time / 1000LL);
247
248 #elif defined(WIN32) && !defined(_XBOX)
249 Sleep(sleep_time / 1000000LL);
250
251 #else
252 SDL_Delay(sleep_time / 1000000LL);
253
254 #endif
255 }
256
257
258 //
259 // I_Yield
260 //
261 // Sleeps for 1 millisecond
262 //
I_Yield()263 void I_Yield()
264 {
265 I_Sleep(1000LL * 1000LL); // sleep for 1ms
266 }
267
268 //
269 // I_WaitVBL
270 //
271 // I_WaitVBL is never used to actually synchronize to the
272 // vertical blank. Instead, it's used for delay purposes.
273 //
I_WaitVBL(int count)274 void I_WaitVBL(int count)
275 {
276 I_Sleep(1000000LL * 1000LL * count / 70);
277 }
278
279 //
280 // SubsetLanguageIDs
281 //
282 #ifdef _WIN32
SubsetLanguageIDs(LCID id,LCTYPE type,int idx)283 static void SubsetLanguageIDs (LCID id, LCTYPE type, int idx)
284 {
285 char buf[8];
286 LCID langid;
287 char *idp;
288
289 if (!GetLocaleInfo (id, type, buf, 8))
290 return;
291 langid = MAKELCID (strtoul(buf, NULL, 16), SORT_DEFAULT);
292 if (!GetLocaleInfo (langid, LOCALE_SABBREVLANGNAME, buf, 8))
293 return;
294 idp = (char *)(&LanguageIDs[idx]);
295 memset (idp, 0, 4);
296 idp[0] = tolower(buf[0]);
297 idp[1] = tolower(buf[1]);
298 idp[2] = tolower(buf[2]);
299 idp[3] = 0;
300 }
301 #endif
302
303 //
304 // SetLanguageIDs
305 //
306 static const char *langids[] = {
307 "auto",
308 "enu",
309 "fr",
310 "it"
311 };
312
EXTERN_CVAR(language)313 EXTERN_CVAR (language)
314
315 void SetLanguageIDs ()
316 {
317 unsigned int langid = language.asInt();
318
319 if (langid == 0 || langid > 3)
320 {
321 #ifdef _WIN32
322 memset (LanguageIDs, 0, sizeof(LanguageIDs));
323 SubsetLanguageIDs (LOCALE_USER_DEFAULT, LOCALE_ILANGUAGE, 0);
324 SubsetLanguageIDs (LOCALE_USER_DEFAULT, LOCALE_IDEFAULTLANGUAGE, 1);
325 SubsetLanguageIDs (LOCALE_SYSTEM_DEFAULT, LOCALE_ILANGUAGE, 2);
326 SubsetLanguageIDs (LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTLANGUAGE, 3);
327 #else
328 langid = 1; // Default to US English on non-windows systems
329 #endif
330 }
331 else
332 {
333 DWORD lang = 0;
334 const char *langtag = langids[langid];
335
336 ((BYTE *)&lang)[0] = (langtag)[0];
337 ((BYTE *)&lang)[1] = (langtag)[1];
338 ((BYTE *)&lang)[2] = (langtag)[2];
339 LanguageIDs[0] = lang;
340 LanguageIDs[1] = lang;
341 LanguageIDs[2] = lang;
342 LanguageIDs[3] = lang;
343 }
344 }
345
346 //
347 // I_Init
348 //
I_Init(void)349 void I_Init (void)
350 {
351 }
352
I_GetCWD()353 std::string I_GetCWD()
354 {
355 char tmp[4096] = {0};
356 std::string ret = "./";
357
358 const char *cwd = getcwd(tmp, sizeof(tmp));
359 if(cwd)
360 ret = cwd;
361
362 FixPathSeparator(ret);
363
364 return ret;
365 }
366
367 #ifdef UNIX
I_GetHomeDir(std::string user="")368 std::string I_GetHomeDir(std::string user = "")
369 {
370 const char *envhome = getenv("HOME");
371 std::string home = envhome ? envhome : "";
372
373 if (!home.length())
374 {
375 #ifdef HAVE_PWD_H
376 // try the uid way
377 passwd *p = user.length() ? getpwnam(user.c_str()) : getpwuid(getuid());
378 if(p && p->pw_dir)
379 home = p->pw_dir;
380 #endif
381
382 if (!home.length())
383 I_FatalError ("Please set your HOME variable");
384 }
385
386 if(home[home.length() - 1] != PATHSEPCHAR)
387 home += PATHSEP;
388
389 return home;
390 }
391 #endif
392
I_GetUserFileName(const char * file)393 std::string I_GetUserFileName (const char *file)
394 {
395 #ifdef UNIX
396 std::string path = I_GetHomeDir();
397
398 if(path[path.length() - 1] != PATHSEPCHAR)
399 path += PATHSEP;
400
401 path += ".odamex";
402
403 struct stat info;
404 if (stat (path.c_str(), &info) == -1)
405 {
406 if (mkdir (path.c_str(), S_IRUSR | S_IWUSR | S_IXUSR) == -1)
407 {
408 I_FatalError ("Failed to create %s directory:\n%s",
409 path.c_str(), strerror (errno));
410 }
411 }
412 else
413 {
414 if (!S_ISDIR(info.st_mode))
415 {
416 I_FatalError ("%s must be a directory", path.c_str());
417 }
418 }
419
420 path += PATHSEP;
421 path += file;
422 #endif
423
424 #ifdef _WIN32
425 std::string path = I_GetBinaryDir();
426
427 if(path[path.length() - 1] != PATHSEPCHAR)
428 path += PATHSEP;
429
430 path += file;
431 #endif
432
433 return path;
434 }
435
I_ExpandHomeDir(std::string & path)436 void I_ExpandHomeDir (std::string &path)
437 {
438 #ifdef UNIX
439 if(!path.length())
440 return;
441
442 if(path[0] != '~')
443 return;
444
445 std::string user;
446
447 size_t slash_pos = path.find_first_of(PATHSEPCHAR);
448 size_t end_pos = path.length();
449
450 if(slash_pos == std::string::npos)
451 slash_pos = end_pos;
452
453 if(path.length() != 1 && slash_pos != 1)
454 user = path.substr(1, slash_pos - 1);
455
456 if(slash_pos != end_pos)
457 slash_pos++;
458
459 path = I_GetHomeDir(user) + path.substr(slash_pos, end_pos - slash_pos);
460 #endif
461 }
462
I_GetBinaryDir()463 std::string I_GetBinaryDir()
464 {
465 std::string ret;
466
467 #ifdef _WIN32
468 char tmp[MAX_PATH]; // denis - todo - make separate function
469 GetModuleFileName (NULL, tmp, sizeof(tmp));
470 ret = tmp;
471 #else
472 if(!Args[0])
473 return "./";
474
475 char realp[PATH_MAX];
476 if(realpath(Args[0], realp))
477 ret = realp;
478 else
479 {
480 // search through $PATH
481 const char *path = getenv("PATH");
482 if(path)
483 {
484 std::stringstream ss(path);
485 std::string segment;
486
487 while(ss)
488 {
489 std::getline(ss, segment, ':');
490
491 if(!segment.length())
492 continue;
493
494 if(segment[segment.length() - 1] != PATHSEPCHAR)
495 segment += PATHSEP;
496 segment += Args[0];
497
498 if(realpath(segment.c_str(), realp))
499 {
500 ret = realp;
501 break;
502 }
503 }
504 }
505 }
506 #endif
507
508 FixPathSeparator(ret);
509
510 size_t slash = ret.find_last_of(PATHSEPCHAR);
511 if(slash == std::string::npos)
512 return "";
513 else
514 return ret.substr(0, slash);
515 }
516
I_FinishClockCalibration()517 void I_FinishClockCalibration ()
518 {
519 /// Printf (PRINT_HIGH, "CPU Frequency: ~%f MHz\n", CyclesPerSecond / 1e6);
520 }
521
522 //
523 // I_Quit
524 //
525 static int has_exited;
526
I_Quit(void)527 void STACK_ARGS I_Quit (void)
528 {
529 has_exited = 1; /* Prevent infinitely recursive exits -- killough */
530
531 #ifdef _WIN32
532 timeEndPeriod (TimerPeriod);
533 #endif
534
535 G_ClearSnapshots ();
536 SV_SendDisconnectSignal();
537
538 CloseNetwork ();
539
540 DConsoleAlias::DestroyAll();
541 }
542
543
544 //
545 // I_Error
546 //
547 BOOL gameisdead;
548
549 #define MAX_ERRORTEXT 1024
550
551 void STACK_ARGS call_terms (void);
552
I_FatalError(const char * error,...)553 void STACK_ARGS I_FatalError (const char *error, ...)
554 {
555 static BOOL alreadyThrown = false;
556 gameisdead = true;
557
558 if (!alreadyThrown) // ignore all but the first message -- killough
559 {
560 alreadyThrown = true;
561 char errortext[MAX_ERRORTEXT];
562 va_list argptr;
563 va_start (argptr, error);
564 #ifdef _WIN32
565 int index = vsprintf (errortext, error, argptr);
566 sprintf (errortext + index, "\nGetLastError = %ld", GetLastError());
567 #else
568 vsprintf (errortext, error, argptr);
569 #endif
570 va_end (argptr);
571
572 throw CFatalError (errortext);
573 }
574
575 if (!has_exited) // If it hasn't exited yet, exit now -- killough
576 {
577 has_exited = 1; // Prevent infinitely recursive exits -- killough
578
579 call_terms();
580
581 exit(EXIT_FAILURE);
582 }
583 }
584
I_Error(const char * error,...)585 void STACK_ARGS I_Error (const char *error, ...)
586 {
587 va_list argptr;
588 char errortext[MAX_ERRORTEXT];
589
590 va_start (argptr, error);
591 vsprintf (errortext, error, argptr);
592 va_end (argptr);
593
594 throw CRecoverableError (errortext);
595 }
596
597 char DoomStartupTitle[256] = { 0 };
598
I_SetTitleString(const char * title)599 void I_SetTitleString (const char *title)
600 {
601 int i;
602
603 for (i = 0; title[i]; i++)
604 DoomStartupTitle[i] = title[i] | 0x80;
605 }
606
I_PrintStr(int xp,const char * cp,int count,BOOL scroll)607 void I_PrintStr (int xp, const char *cp, int count, BOOL scroll)
608 {
609 char string[4096];
610
611 memcpy (string, cp, count);
612 if (scroll)
613 string[count++] = '\n';
614 string[count] = 0;
615
616 fputs (string, stdout);
617 fflush (stdout);
618 }
619
620 //static const char *pattern; // [DL] todo - remove
621 //static findstate_t *findstates[8]; // [DL] todo - remove
622
I_FindFirst(char * filespec,findstate_t * fileinfo)623 long I_FindFirst (char *filespec, findstate_t *fileinfo)
624 {
625 return 0;
626 }
627
I_FindNext(long handle,findstate_t * fileinfo)628 int I_FindNext (long handle, findstate_t *fileinfo)
629 {
630 return 0;
631 }
632
I_FindClose(long handle)633 int I_FindClose (long handle)
634 {
635 return 0;
636 }
637
I_FindAttr(findstate_t * fileinfo)638 int I_FindAttr (findstate_t *fileinfo)
639 {
640 return 0;
641 }
642
643 //
644 // I_ConsoleInput
645 //
646 #ifdef _WIN32
647 int ShutdownNow();
648
I_ConsoleInput(void)649 std::string I_ConsoleInput (void)
650 {
651 // denis - todo - implement this properly!!!
652 static char text[1024] = {0};
653 static char buffer[1024] = {0};
654 unsigned int len = strlen(buffer);
655
656 if (ShutdownNow())
657 return "quit";
658
659 while(kbhit() && len < sizeof(text))
660 {
661 char ch = (char)getch();
662
663 // input the character
664 if(ch == '\b' && len)
665 {
666 buffer[--len] = 0;
667 // john - backspace hack
668 fwrite(&ch, 1, 1, stdout);
669 ch = ' ';
670 fwrite(&ch, 1, 1, stdout);
671 ch = '\b';
672 }
673 else
674 buffer[len++] = ch;
675 buffer[len] = 0;
676
677 // recalculate length
678 len = strlen(buffer);
679
680 // echo character back to user
681 fwrite(&ch, 1, 1, stdout);
682 fflush(stdout);
683 }
684
685 if(len && (buffer[len - 1] == '\n' || buffer[len - 1] == '\r'))
686 {
687 // echo newline back to user
688 char ch = '\n';
689 fwrite(&ch, 1, 1, stdout);
690 fflush(stdout);
691
692 strcpy(text, buffer);
693 text[len-1] = 0; // rip off the /n and terminate
694 buffer[0] = 0;
695 len = 0;
696
697 return text;
698 }
699
700 return "";
701 }
702
703 #else
704
I_ConsoleInput(void)705 std::string I_ConsoleInput (void)
706 {
707 std::string ret;
708 static char text[1024] = {0};
709 int len;
710
711 fd_set fdr;
712 FD_ZERO(&fdr);
713 FD_SET(0, &fdr);
714 struct timeval tv;
715 tv.tv_sec = 0;
716 tv.tv_usec = 0;
717
718 if (select(1, &fdr, NULL, NULL, &tv) <= 0)
719 return "";
720
721 len = read (0, text + strlen(text), sizeof(text) - strlen(text)); // denis - fixme - make it read until the next linebreak instead
722
723 if (len < 1)
724 return "";
725
726 len = strlen(text);
727
728 if (strlen(text) >= sizeof(text))
729 {
730 if(text[len-1] == '\n' || text[len-1] == '\r')
731 text[len-1] = 0; // rip off the /n and terminate
732
733 ret = text;
734 memset(text, 0, sizeof(text));
735 return ret;
736 }
737
738 if(text[len-1] == '\n' || text[len-1] == '\r')
739 {
740 text[len-1] = 0;
741
742 ret = text;
743 memset(text, 0, sizeof(text));
744 return ret;
745 }
746
747 return "";
748 }
749 #endif
750
751 VERSION_CONTROL (i_system_cpp, "$Id: i_system.cpp 4556 2014-02-10 02:48:10Z dr_sean $")
752
753