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