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