1 /*
2 * sys_amiga.c -- Amiga system interface code
3 * $Id: sys_amiga.c 6015 2018-02-21 17:30:51Z sezero $
4 *
5 * Copyright (C) 1996-1997 Id Software, Inc.
6 * Copyright (C) 2012 Szilard Biro <col.lawrence@gmail.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or (at
11 * your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * 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 along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24 #include "quakedef.h"
25 #include "debuglog.h"
26
27 #include <proto/exec.h>
28 #include <proto/dos.h>
29
30 #include <proto/timer.h>
31 #include <time.h>
32
33 #define MIN_STACK_SIZE 0x100000 /* 1 MB stack */
34 #ifdef __CLIB2__
35 int __stack_size = MIN_STACK_SIZE;
36 #else
37 int __stack = MIN_STACK_SIZE;
38 #endif
39 #ifdef __AROS__
40 #include "incstack.h"
41 /* The problem here is that our real main never returns: the exit
42 * point of program is either Sys_Quit() or Sys_Error(). One way
43 * of making the real main return to the incstack.h main wrapper
44 * is setjmp()'ing in real main and longjmp()'ing from the actual
45 * exit points, avoiding exit(). */
46 #include <setjmp.h>
47 static jmp_buf exit_buf;
48 static int my_rc = 0;
49 #define HAVE_AROS_MAIN_WRAPPER
50 #endif
51
52 // heapsize: minimum 8 mb, standart 16 mb, max is 32 mb.
53 // -heapsize argument will abide by these min/max settings
54 // unless the -forcemem argument is used
55 #define MIN_MEM_ALLOC 0x0800000
56 #define STD_MEM_ALLOC 0x1000000
57 #define MAX_MEM_ALLOC 0x2000000
58
59 cvar_t sys_nostdout = {"sys_nostdout", "0", CVAR_NONE};
60
61 qboolean isDedicated = true; /* compatibility */
62
63 static double starttime;
64 static qboolean first = true;
65
66 static BPTR amiga_stdin, amiga_stdout;
67 #define MODE_RAW 1
68 #define MODE_NORMAL 0
69
70 struct timerequest *timerio;
71 struct MsgPort *timerport;
72 #if defined(__MORPHOS__) || defined(__VBCC__)
73 struct Library *TimerBase;
74 #else
75 struct Device *TimerBase;
76 #endif
77
78
79 /*
80 ===============================================================================
81
82 FILE IO
83
84 ===============================================================================
85 */
86
Sys_mkdir(const char * path,qboolean crash)87 int Sys_mkdir(const char *path, qboolean crash)
88 {
89 BPTR lock = CreateDir((const STRPTR) path);
90
91 if (lock)
92 {
93 UnLock(lock);
94 return 0;
95 }
96
97 if (IoErr() == ERROR_OBJECT_EXISTS)
98 return 0;
99
100 if (crash)
101 Sys_Error("Unable to create directory %s", path);
102 return -1;
103 }
104
Sys_rmdir(const char * path)105 int Sys_rmdir (const char *path)
106 {
107 if (DeleteFile((const STRPTR) path) != 0)
108 return 0;
109 return -1;
110 }
111
Sys_unlink(const char * path)112 int Sys_unlink (const char *path)
113 {
114 if (DeleteFile((const STRPTR) path) != 0)
115 return 0;
116 return -1;
117 }
118
Sys_rename(const char * oldp,const char * newp)119 int Sys_rename (const char *oldp, const char *newp)
120 {
121 if (Rename((const STRPTR) oldp, (const STRPTR) newp) != 0)
122 return 0;
123 return -1;
124 }
125
Sys_filesize(const char * path)126 long Sys_filesize (const char *path)
127 {
128 long size = -1;
129 BPTR fh = Open((const STRPTR) path, MODE_OLDFILE);
130 if (fh)
131 {
132 struct FileInfoBlock *fib = (struct FileInfoBlock*)
133 AllocDosObject(DOS_FIB, NULL);
134 if (fib != NULL)
135 {
136 if (ExamineFH(fh, fib))
137 size = fib->fib_Size;
138 FreeDosObject(DOS_FIB, fib);
139 }
140 Close(fh);
141 }
142 return size;
143 }
144
Sys_FileType(const char * path)145 int Sys_FileType (const char *path)
146 {
147 int type = FS_ENT_NONE;
148 BPTR fh = Open((const STRPTR) path, MODE_OLDFILE);
149 if (fh)
150 {
151 struct FileInfoBlock *fib = (struct FileInfoBlock*)
152 AllocDosObject(DOS_FIB, NULL);
153 if (fib != NULL)
154 {
155 if (ExamineFH(fh, fib))
156 {
157 if (fib->fib_DirEntryType >= 0)
158 type = FS_ENT_DIRECTORY;
159 else type = FS_ENT_FILE;
160 }
161 FreeDosObject(DOS_FIB, fib);
162 }
163 Close(fh);
164 }
165 return type;
166 }
167
168 #define COPY_READ_BUFSIZE 8192 /* BUFSIZ */
Sys_CopyFile(const char * frompath,const char * topath)169 int Sys_CopyFile (const char *frompath, const char *topath)
170 {
171 char buf[COPY_READ_BUFSIZE];
172 BPTR in, out;
173 struct FileInfoBlock *fib;
174 struct DateStamp stamp;
175 LONG remaining, count;
176
177 in = Open((const STRPTR) frompath, MODE_OLDFILE);
178 if (!in)
179 {
180 Con_Printf ("%s: unable to open %s\n", __thisfunc__, frompath);
181 return 1;
182 }
183 fib = (struct FileInfoBlock*) AllocDosObject(DOS_FIB, NULL);
184 if (fib != NULL)
185 {
186 if (ExamineFH(in, fib) == 0)
187 remaining = -1;
188 else
189 {
190 remaining = fib->fib_Size;
191 stamp = fib->fib_Date;
192 }
193 FreeDosObject(DOS_FIB, fib);
194 if (remaining < 0)
195 {
196 Con_Printf ("%s: can't determine size for %s\n", __thisfunc__, frompath);
197 Close(in);
198 return 1;
199 }
200 }
201 else
202 {
203 Con_Printf ("%s: can't allocate FileInfoBlock for %s\n", __thisfunc__, frompath);
204 Close(in);
205 return 1;
206 }
207 out = Open((const STRPTR) topath, MODE_NEWFILE);
208 if (!out)
209 {
210 Con_Printf ("%s: unable to open %s\n", __thisfunc__, topath);
211 Close(in);
212 return 1;
213 }
214
215 while (remaining)
216 {
217 if (remaining < sizeof(buf))
218 count = remaining;
219 else count = sizeof(buf);
220
221 if (Read(in, buf, count) == -1)
222 break;
223 if (Write(out, buf, count) == -1)
224 break;
225
226 remaining -= count;
227 }
228
229 Close(in);
230 Close(out);
231
232 if (remaining != 0)
233 return 1;
234
235 SetFileDate(topath, &stamp);
236
237 return 0;
238 }
239
240 /*
241 =================================================
242 simplified findfirst/findnext implementation:
243 Sys_FindFirstFile and Sys_FindNextFile return
244 filenames only, not a dirent struct. this is
245 what we presently need in this engine.
246 =================================================
247 */
248 #define PATH_SIZE 1024
249 static struct AnchorPath *apath;
250 static BPTR oldcurrentdir;
251 static STRPTR pattern_str;
252
pattern_helper(const char * pat)253 static STRPTR pattern_helper (const char *pat)
254 {
255 char *pdup;
256 const char *p;
257 int n;
258
259 for (n = 0, p = pat; *p != '\0'; ++p, ++n)
260 {
261 if ((p = strchr (p, '*')) == NULL)
262 break;
263 }
264
265 if (n == 0)
266 pdup = Z_Strdup(pat);
267 else
268 {
269 /* replace each "*" by "#?" */
270 n += (int) strlen(pat) + 1;
271 pdup = (char *) Z_Malloc(n, Z_MAINZONE);
272
273 for (n = 0, p = pat; *p != '\0'; ++p, ++n)
274 {
275 if (*p != '*')
276 pdup[n] = *p;
277 else
278 {
279 pdup[n] = '#'; ++n;
280 pdup[n] = '?';
281 }
282 }
283 pdup[n] = '\0';
284 }
285
286 return (STRPTR) pdup;
287 }
288
Sys_FindFirstFile(const char * path,const char * pattern)289 const char *Sys_FindFirstFile (const char *path, const char *pattern)
290 {
291 BPTR newdir;
292
293 if (apath)
294 Sys_Error ("Sys_FindFirst without FindClose");
295
296 apath = (struct AnchorPath *) AllocVec (sizeof(struct AnchorPath) + PATH_SIZE, MEMF_CLEAR);
297 if (!apath)
298 return NULL;
299
300 apath->ap_Strlen = PATH_SIZE;
301 apath->ap_BreakBits = 0;
302 apath->ap_Flags = 0; /* APF_DOWILD */
303
304 newdir = Lock((const STRPTR) path, SHARED_LOCK);
305 if (newdir)
306 oldcurrentdir = CurrentDir(newdir);
307 else
308 {
309 FreeVec(apath);
310 apath = NULL;
311 return NULL;
312 }
313
314 pattern_str = pattern_helper (pattern);
315
316 if (MatchFirst((const STRPTR) pattern_str, apath) == 0)
317 {
318 if (apath->ap_Info.fib_DirEntryType < 0)
319 return (const char *) (apath->ap_Info.fib_FileName);
320 }
321
322 return Sys_FindNextFile();
323 }
324
Sys_FindNextFile(void)325 const char *Sys_FindNextFile (void)
326 {
327 if (!apath)
328 return NULL;
329
330 while (MatchNext(apath) == 0)
331 {
332 if (apath->ap_Info.fib_DirEntryType < 0)
333 return (const char *) (apath->ap_Info.fib_FileName);
334 }
335
336 return NULL;
337 }
338
Sys_FindClose(void)339 void Sys_FindClose (void)
340 {
341 if (!apath)
342 return;
343 MatchEnd(apath);
344 FreeVec(apath);
345 UnLock(CurrentDir(oldcurrentdir));
346 oldcurrentdir = 0;
347 apath = NULL;
348 Z_Free(pattern_str);
349 pattern_str = NULL;
350 }
351
352 /*
353 ===============================================================================
354
355 SYSTEM IO
356
357 ===============================================================================
358 */
359
360 /*
361 ================
362 Sys_Init
363 ================
364 */
Sys_Init(void)365 static void Sys_Init (void)
366 {
367 if ((timerport = CreateMsgPort()))
368 {
369 if ((timerio = (struct timerequest *)CreateIORequest(timerport, sizeof(struct timerequest))))
370 {
371 if (OpenDevice((STRPTR) TIMERNAME, UNIT_MICROHZ,
372 (struct IORequest *) timerio, 0) == 0)
373 {
374 #if defined(__MORPHOS__) || defined(__VBCC__)
375 TimerBase = (struct Library *)timerio->tr_node.io_Device;
376 #else
377 TimerBase = timerio->tr_node.io_Device;
378 #endif
379 }
380 else
381 {
382 DeleteIORequest((struct IORequest *)timerio);
383 DeleteMsgPort(timerport);
384 }
385 }
386 else
387 {
388 DeleteMsgPort(timerport);
389 }
390 }
391
392 if (!TimerBase)
393 Sys_Error("Can't open timer.device");
394
395 /* 1us wait, for timer cleanup success */
396 timerio->tr_node.io_Command = TR_ADDREQUEST;
397 timerio->tr_time.tv_secs = 0;
398 timerio->tr_time.tv_micro = 1;
399 SendIO((struct IORequest *) timerio);
400 WaitIO((struct IORequest *) timerio);
401
402 amiga_stdout = Output();
403 amiga_stdin = Input();
404 SetMode(amiga_stdin, MODE_RAW);
405 }
406
Sys_AtExit(void)407 static void Sys_AtExit (void)
408 {
409 if (amiga_stdin)
410 SetMode(amiga_stdin, MODE_NORMAL);
411 if (TimerBase)
412 {
413 /*
414 if (!CheckIO((struct IORequest *) timerio)
415 {
416 AbortIO((struct IORequest *) timerio);
417 WaitIO((struct IORequest *) timerio);
418 }
419 */
420 WaitIO((struct IORequest *) timerio);
421 CloseDevice((struct IORequest *) timerio);
422 DeleteIORequest((struct IORequest *) timerio);
423 DeleteMsgPort(timerport);
424 TimerBase = NULL;
425 }
426 }
427
428 #define ERROR_PREFIX "\nFATAL ERROR: "
Sys_Error(const char * error,...)429 void Sys_Error (const char *error, ...)
430 {
431 va_list argptr;
432 char text[MAX_PRINTMSG];
433 const char text2[] = ERROR_PREFIX;
434 const unsigned char *p;
435
436 host_parms->errstate++;
437
438 va_start (argptr, error);
439 q_vsnprintf (text, sizeof(text), error, argptr);
440 va_end (argptr);
441
442 if (con_debuglog)
443 {
444 LOG_Print (ERROR_PREFIX);
445 LOG_Print (text);
446 LOG_Print ("\n\n");
447 }
448
449 Host_Shutdown ();
450
451 for (p = (const unsigned char *) text2; *p; p++)
452 putc (*p, stderr);
453 for (p = (const unsigned char *) text ; *p; p++)
454 putc (*p, stderr);
455 putc ('\n', stderr);
456 putc ('\n', stderr);
457
458 #ifdef HAVE_AROS_MAIN_WRAPPER
459 Sys_AtExit();
460 my_rc = 1;
461 longjmp(exit_buf, 1);
462 #else
463 exit (1);
464 #endif
465 }
466
Sys_PrintTerm(const char * msgtxt)467 void Sys_PrintTerm (const char *msgtxt)
468 {
469 const unsigned char *p;
470
471 if (sys_nostdout.integer)
472 return;
473
474 for (p = (const unsigned char *) msgtxt; *p; p++)
475 putc (*p, stdout);
476 }
477
Sys_Quit(void)478 void Sys_Quit (void)
479 {
480 Host_Shutdown();
481
482 #ifdef HAVE_AROS_MAIN_WRAPPER
483 Sys_AtExit();
484 longjmp(exit_buf, 1);
485 #else
486 exit (0);
487 #endif
488 }
489
490
491 /*
492 ================
493 Sys_DoubleTime
494 ================
495 */
Sys_DoubleTime(void)496 double Sys_DoubleTime (void)
497 {
498 struct timeval tp;
499 double now;
500
501 GetSysTime(&tp);
502
503 now = tp.tv_secs + tp.tv_micro / 1e6;
504
505 if (first)
506 {
507 first = false;
508 starttime = now;
509 return 0.0;
510 }
511
512 return now - starttime;
513 }
514
Sys_DateTimeString(char * buf)515 char *Sys_DateTimeString (char *buf)
516 {
517 static char strbuf[24];
518 time_t t;
519 struct tm *l;
520 int val;
521
522 if (!buf) buf = strbuf;
523
524 t = time(NULL);
525 l = localtime(&t);
526
527 val = l->tm_mon + 1; /* tm_mon: months since January [0,11] */
528 buf[0] = val / 10 + '0';
529 buf[1] = val % 10 + '0';
530 buf[2] = '/';
531 val = l->tm_mday;
532 buf[3] = val / 10 + '0';
533 buf[4] = val % 10 + '0';
534 buf[5] = '/';
535 val = l->tm_year / 100 + 19; /* tm_year: #years since 1900. */
536 buf[6] = val / 10 + '0';
537 buf[7] = val % 10 + '0';
538 val = l->tm_year % 100;
539 buf[8] = val / 10 + '0';
540 buf[9] = val % 10 + '0';
541
542 buf[10] = ' ';
543
544 val = l->tm_hour;
545 buf[11] = val / 10 + '0';
546 buf[12] = val % 10 + '0';
547 buf[13] = ':';
548 val = l->tm_min;
549 buf[14] = val / 10 + '0';
550 buf[15] = val % 10 + '0';
551 buf[16] = ':';
552 val = l->tm_sec;
553 buf[17] = val / 10 + '0';
554 buf[18] = val % 10 + '0';
555
556 buf[19] = '\0';
557
558 return buf;
559 }
560
561
562 /*
563 ================
564 Sys_ConsoleInput
565 ================
566 */
Sys_ConsoleInput(void)567 const char *Sys_ConsoleInput (void)
568 {
569 static char con_text[256];
570 static int textlen;
571 char c;
572
573 while (WaitForChar(amiga_stdin,10))
574 {
575 Read (amiga_stdin, &c, 1);
576 if (c == '\n' || c == '\r')
577 {
578 Write(amiga_stdout, "\n", 1);
579 con_text[textlen] = '\0';
580 textlen = 0;
581 return con_text;
582 }
583 else if (c == 8)
584 {
585 if (textlen)
586 {
587 Write(amiga_stdout, "\b \b", 3);
588 textlen--;
589 con_text[textlen] = '\0';
590 }
591 continue;
592 }
593 con_text[textlen] = c;
594 textlen++;
595 if (textlen < (int) sizeof(con_text))
596 {
597 Write(amiga_stdout, &c, 1);
598 con_text[textlen] = '\0';
599 }
600 else
601 {
602 // buffer is full
603 textlen = 0;
604 con_text[0] = '\0';
605 Sys_PrintTerm("\nConsole input too long!\n");
606 break;
607 }
608 }
609
610 return NULL;
611 }
612
Sys_Sleep(unsigned long msecs)613 void Sys_Sleep (unsigned long msecs)
614 {
615 timerio->tr_node.io_Command = TR_ADDREQUEST;
616 timerio->tr_time.tv_secs = msecs / 1000;
617 timerio->tr_time.tv_micro = (msecs * 1000) % 1000000;
618 SendIO((struct IORequest *) timerio);
619 WaitIO((struct IORequest *) timerio);
620 }
621
Sys_GetBasedir(char * argv0,char * dst,size_t dstsize)622 static int Sys_GetBasedir (char *argv0, char *dst, size_t dstsize)
623 {
624 #if 1
625 int len = q_strlcpy(dst, "PROGDIR:", dstsize);
626 if (len < (int)dstsize)
627 return 0;
628 return -1;
629 #else
630 if (NameFromLock(GetProgramDir(), (STRPTR) dst, dstsize) != 0)
631 return 0;
632 return -1;
633 #endif
634 }
635
PrintVersion(void)636 static void PrintVersion (void)
637 {
638 Sys_Printf ("Hammer of Thyrion, release %s (%s)\n", HOT_VERSION_STR, HOT_VERSION_REL_DATE);
639 Sys_Printf ("Hexen II dedicated server %4.2f (%s)\n", ENGINE_VERSION, PLATFORM_STRING);
640 Sys_Printf ("More info / sending bug reports: http://uhexen2.sourceforge.net\n");
641 }
642
643 static const char *help_strings[] = {
644 " [-v | --version] Display version information",
645 #ifndef DEMOBUILD
646 # if defined(H2MP)
647 " [-noportals] Disable the mission pack support",
648 # else
649 " [-portals | -h2mp ] Run the Portal of Praevus mission pack",
650 # endif
651 #endif
652 " [-heapsize Bytes] Heapsize (memory to allocate)",
653 NULL
654 };
655
PrintHelp(const char * name)656 static void PrintHelp (const char *name)
657 {
658 int i = 0;
659
660 Sys_Printf ("Usage: %s [options]\n", name);
661 while (help_strings[i])
662 {
663 Sys_PrintTerm (help_strings[i]);
664 Sys_PrintTerm ("\n");
665 i++;
666 }
667 Sys_PrintTerm ("\n");
668 }
669
670 /*
671 ===============================================================================
672
673 MAIN
674
675 ===============================================================================
676 */
677 static quakeparms_t parms;
678 static char cwd[MAX_OSPATH];
679
main(int argc,char ** argv)680 int main (int argc, char **argv)
681 {
682 int i;
683 double time, oldtime;
684 ULONG availMem;
685
686 #ifdef HAVE_AROS_MAIN_WRAPPER
687 if (setjmp(exit_buf))
688 return my_rc;
689 #endif
690
691 PrintVersion();
692
693 if (argc > 1)
694 {
695 for (i = 1; i < argc; i++)
696 {
697 if ( !(strcmp(argv[i], "-v")) || !(strcmp(argv[i], "-version" )) ||
698 !(strcmp(argv[i], "--version")) )
699 {
700 return 0;
701 }
702 else if ( !(strcmp(argv[i], "-h")) || !(strcmp(argv[i], "-help" )) ||
703 !(strcmp(argv[i], "-?")) || !(strcmp(argv[i], "--help")) )
704 {
705 PrintHelp(argv[0]);
706 return 0;
707 }
708 }
709 }
710
711 /* initialize the host params */
712 memset (&parms, 0, sizeof(parms));
713 parms.basedir = cwd;
714 parms.userdir = cwd;
715 parms.argc = argc;
716 parms.argv = argv;
717 parms.errstate = 0;
718 host_parms = &parms;
719
720 memset (cwd, 0, sizeof(cwd));
721 if (Sys_GetBasedir(argv[0], cwd, sizeof(cwd)) != 0)
722 Sys_Error ("Couldn't determine current directory");
723
724 LOG_Init (&parms);
725
726 Sys_Printf("basedir is: %s\n", parms.basedir);
727 Sys_Printf("userdir is: %s\n", parms.userdir);
728
729 COM_ValidateByteorder ();
730
731 availMem = AvailMem(MEMF_ANY|MEMF_LARGEST);
732 if (availMem < STD_MEM_ALLOC)
733 parms.memsize = MIN_MEM_ALLOC;
734 else
735 parms.memsize = STD_MEM_ALLOC;
736
737 i = COM_CheckParm ("-heapsize");
738 if (i && i < com_argc-1)
739 {
740 parms.memsize = atoi (com_argv[i+1]) * 1024;
741
742 if ((parms.memsize > MAX_MEM_ALLOC) && !(COM_CheckParm ("-forcemem")))
743 {
744 Sys_Printf ("Requested memory (%d Mb) too large, using the default maximum.\n", parms.memsize/(1024*1024));
745 Sys_Printf ("If you are sure, use the -forcemem switch.\n");
746 parms.memsize = MAX_MEM_ALLOC;
747 }
748 else if ((parms.memsize < MIN_MEM_ALLOC) && !(COM_CheckParm ("-forcemem")))
749 {
750 Sys_Printf ("Requested memory (%d Mb) too little, using the default minimum.\n", parms.memsize/(1024*1024));
751 Sys_Printf ("If you are sure, use the -forcemem switch.\n");
752 parms.memsize = MIN_MEM_ALLOC;
753 }
754 }
755
756 parms.membase = malloc (parms.memsize);
757
758 if (!parms.membase)
759 Sys_Error ("Insufficient memory.\n");
760
761 #ifndef HAVE_AROS_MAIN_WRAPPER
762 atexit (Sys_AtExit);
763 #endif
764 Sys_Init ();
765
766 Host_Init();
767
768 oldtime = Sys_DoubleTime ();
769
770 /* main window message loop */
771 while (1)
772 {
773 time = Sys_DoubleTime ();
774
775 if (time - oldtime < sys_ticrate.value )
776 {
777 Sys_Sleep(1);
778 continue;
779 }
780
781 Host_Frame (time - oldtime);
782 oldtime = time;
783 }
784
785 return 0;
786 }
787
788