1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 */
20
21 // System functions for Android OS.
22 // Based on sys_linux.c
23
24 #include <unistd.h>
25 #include <signal.h>
26 #include <stdlib.h>
27 #include <limits.h>
28 #include <sys/time.h>
29 #include <sys/types.h>
30 #include <unistd.h>
31 #include <fcntl.h>
32 #include <stdarg.h>
33 #include <stdio.h>
34 // #include <sys/ipc.h>
35 // #include <sys/shm.h>
36 #include <sys/stat.h>
37 #include <string.h>
38 #include <ctype.h>
39 #include <sys/wait.h>
40 #include <sys/mman.h>
41 #include <errno.h>
42 #include <dirent.h>
43
44 #include <android/log.h>
45
46 #include "quakedef.h"
47
48 #define LOG_TAG "Quake sys_android"
49 #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
50 #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
51
52 qboolean isDedicated;
53
54 int noconinput = 0;
55 int nostdout = 0;
56
57 // Look for data on either the sdcard or the internal data store.
58 // (We look at the sdcard first
59
60 static const char *basedir1 = "/sdcard/data/quake";
61 static const char *basedir2 = "/data/quake";
62
63 static const char *cachedir = "/tmp";
64
65 cvar_t sys_linerefresh = CVAR2("sys_linerefresh","0");// set for entity display
66
67 // =======================================================================
68 // General routines
69 // =======================================================================
70
Sys_DebugNumber(int y,int val)71 void Sys_DebugNumber(int y, int val)
72 {
73 }
74
75 /*
76 void Sys_Printf (char *fmt, ...)
77 {
78 va_list argptr;
79 char text[1024];
80
81 va_start (argptr,fmt);
82 vsprintf (text,fmt,argptr);
83 va_end (argptr);
84 fprintf(stderr, "%s", text);
85
86 Con_Print (text);
87 }
88
89 void Sys_Printf (char *fmt, ...)
90 {
91
92 va_list argptr;
93 char text[1024], *t_p;
94 int l, r;
95
96 if (nostdout)
97 return;
98
99 va_start (argptr,fmt);
100 vsprintf (text,fmt,argptr);
101 va_end (argptr);
102
103 l = strlen(text);
104 t_p = text;
105
106 // make sure everything goes through, even though we are non-blocking
107 while (l)
108 {
109 r = write (1, text, l);
110 if (r != l)
111 sleep (0);
112 if (r > 0)
113 {
114 t_p += r;
115 l -= r;
116 }
117 }
118
119 }
120 */
121
122 #define USE_PMPEVENT
123
Sys_Printf(const char * fmt,...)124 void Sys_Printf (const char *fmt, ...)
125 {
126 va_list argptr;
127 char text[2048];
128 unsigned char *p;
129
130 va_start (argptr,fmt);
131 vsnprintf (text, sizeof(text), fmt,argptr);
132 va_end (argptr);
133
134 text[sizeof(text)-1] = 0;
135 LOGI("%s", text);
136
137 #ifdef USE_PMPEVENT
138 PMPEVENT(("%s", text));
139 #else
140 if (nostdout)
141 return;
142
143 for (p = (unsigned char *)text; *p; p++)
144 if ((*p > 128 || *p < 32) && *p != 10 && *p != 13 && *p != 9)
145 printf("[%02x]", *p);
146 else
147 putc(*p, stdout);
148 #endif
149 }
150
151 qboolean soft_quit;
152
Sys_Quit(void)153 void Sys_Quit (void)
154 {
155 Host_Shutdown();
156 #ifdef USE_PMPEVENT
157 PMPERROR(("Sys_Quit - exiting."));
158 #else
159 printf("Sys_Quit - exiting.\n");
160 #endif
161 // fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~FNDELAY);
162 if (soft_quit) {
163 return;
164 }
165 exit(0);
166 }
167
Sys_Init(void)168 void Sys_Init(void)
169 {
170
171 }
172
Sys_Error(const char * error,...)173 void Sys_Error (const char *error, ...)
174 {
175 va_list argptr;
176 char string[1024];
177
178 // change stdin to non blocking
179 // fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~FNDELAY);
180
181 va_start (argptr,error);
182 vsprintf (string,error,argptr);
183 va_end (argptr);
184 #ifdef USE_PMPEVENT
185 PMPERROR(("Error: %s\n", string));
186 #else
187 fprintf(stderr, "Error: %s\n", string);
188 #endif
189 Host_Shutdown ();
190 #ifdef USE_PMPEVENT
191 PMPERROR(("Sys_Error - exiting."));
192 #else
193 printf("Sys_Error - exiting.\n");
194 #endif
195 exit (1);
196
197 }
198
Sys_Warn(const char * warning,...)199 void Sys_Warn (const char *warning, ...)
200 {
201 va_list argptr;
202 char string[1024];
203
204 va_start (argptr,warning);
205 vsprintf (string,warning,argptr);
206 va_end (argptr);
207 #ifdef USE_PMPEVENT
208 PMPWARNING(("Warning: %s", string));
209 #else
210 fprintf(stderr, "Warning: %s\n", string);
211 #endif
212 }
213
214 /*
215 ============
216 Sys_FileTime
217
218 returns -1 if not present
219 ============
220 */
Sys_FileTime(const char * path)221 int Sys_FileTime (const char *path)
222 {
223 struct stat buf;
224
225 if (stat (path,&buf) == -1)
226 return -1;
227
228 return buf.st_mtime;
229 }
230
231
Sys_mkdir(const char * path)232 void Sys_mkdir (const char *path)
233 {
234 mkdir (path, 0777);
235 }
236
Sys_FileOpenRead(const char * path,int * handle)237 int Sys_FileOpenRead (const char *path, int *handle)
238 {
239 int h;
240 struct stat fileinfo;
241
242
243 h = open (path, O_RDONLY, 0666);
244 *handle = h;
245 if (h == -1)
246 return -1;
247
248 if (fstat (h,&fileinfo) == -1)
249 Sys_Error ("Error fstating %s", path);
250
251 return fileinfo.st_size;
252 }
253
Sys_FileOpenWrite(const char * path)254 int Sys_FileOpenWrite (const char *path)
255 {
256 int handle;
257
258 umask (0);
259
260 handle = open(path,O_RDWR | O_CREAT | O_TRUNC
261 , 0666);
262
263 if (handle == -1)
264 Sys_Error ("Error opening %s: %s", path,strerror(errno));
265
266 return handle;
267 }
268
Sys_FileWrite(int handle,const void * src,int count)269 int Sys_FileWrite (int handle, const void *src, int count)
270 {
271 return write (handle, src, count);
272 }
273
Sys_FileClose(int handle)274 void Sys_FileClose (int handle)
275 {
276 close (handle);
277 }
278
Sys_FileSeek(int handle,int position)279 void Sys_FileSeek (int handle, int position)
280 {
281 lseek (handle, position, SEEK_SET);
282 }
283
Sys_FileRead(int handle,void * dest,int count)284 int Sys_FileRead (int handle, void *dest, int count)
285 {
286 return read (handle, dest, count);
287 }
288
Sys_DebugLog(const char * file,char * fmt,...)289 void Sys_DebugLog(const char *file, char *fmt, ...)
290 {
291 va_list argptr;
292 static char data[1024];
293 int fd;
294
295 va_start(argptr, fmt);
296 vsprintf(data, fmt, argptr);
297 va_end(argptr);
298 // fd = open(file, O_WRONLY | O_BINARY | O_CREAT | O_APPEND, 0666);
299 fd = open(file, O_WRONLY | O_CREAT | O_APPEND, 0666);
300 write(fd, data, strlen(data));
301 close(fd);
302 }
303
Sys_EditFile(const char * filename)304 void Sys_EditFile(const char *filename)
305 {
306
307 char cmd[256];
308 char *term;
309 const char *editor;
310
311 term = getenv("TERM");
312 if (term && !strcmp(term, "xterm"))
313 {
314 editor = getenv("VISUAL");
315 if (!editor)
316 editor = getenv("EDITOR");
317 if (!editor)
318 editor = getenv("EDIT");
319 if (!editor)
320 editor = "vi";
321 sprintf(cmd, "xterm -e %s %s", editor, filename);
322 system(cmd);
323 }
324
325 }
326
Sys_FloatTime(void)327 double Sys_FloatTime (void)
328 {
329 struct timeval tp;
330 struct timezone tzp;
331 static int secbase;
332
333 gettimeofday(&tp, &tzp);
334
335 if (!secbase)
336 {
337 secbase = tp.tv_sec;
338 return tp.tv_usec/1000000.0;
339 }
340
341 return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0;
342 }
343
344 // =======================================================================
345 // Sleeps for microseconds
346 // =======================================================================
347
348 static volatile int oktogo;
349
alarm_handler(int x)350 void alarm_handler(int x)
351 {
352 oktogo=1;
353 }
354
Sys_LineRefresh(void)355 void Sys_LineRefresh(void)
356 {
357 }
358
floating_point_exception_handler(int whatever)359 void floating_point_exception_handler(int whatever)
360 {
361 // Sys_Warn("floating point exception\n");
362 signal(SIGFPE, floating_point_exception_handler);
363 }
364
Sys_ConsoleInput(void)365 char *Sys_ConsoleInput(void)
366 {
367 #if 0
368 static char text[256];
369 int len;
370
371 if (cls.state == ca_dedicated) {
372 len = read (0, text, sizeof(text));
373 if (len < 1)
374 return NULL;
375 text[len-1] = 0; // rip off the /n and terminate
376
377 return text;
378 }
379 #endif
380 return NULL;
381 }
382
383 #if !id386
Sys_HighFPPrecision(void)384 void Sys_HighFPPrecision (void)
385 {
386 }
387
Sys_LowFPPrecision(void)388 void Sys_LowFPPrecision (void)
389 {
390 }
391 #endif
392
393 int skipframes;
394
395 // The following APIs are called from the Java activity
396
397 double g_oldtime;
398
399 extern int scr_width;
400 extern int scr_height;
401
direxists(const char * path)402 qboolean direxists(const char* path)
403 {
404 struct stat sb;
405 if(stat(path, &sb))
406 {
407 return 0; // error
408 }
409 if(sb.st_mode & S_IFDIR)
410 {
411 return 1;
412 }
413 return 0;
414 }
415
416 // Remove all files in path. Recurses into subdirectories
417
rmDir(const char * path)418 void rmDir(const char* path) {
419 DIR* dir = opendir(path);
420 if(!dir) {
421 return;
422 }
423 struct dirent * dp;
424 while((dp = readdir(dir)) != NULL) {
425 const char* name = dp->d_name;
426 if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
427 continue;
428 }
429 char filePath[1024];
430 if ((int) (sizeof(filePath)-1) < snprintf(filePath, sizeof(filePath), "%s/%s", path, name)) {
431 continue; // buffer overflow
432 }
433 if(direxists(filePath)) {
434 rmDir(filePath);
435 }
436 else {
437 unlink(filePath);
438 }
439 }
440 closedir(dir);
441 rmdir(path);
442 }
443
444 // Increment this number whenever the data format of any of the files stored in glquake changes:
445
446 typedef unsigned long GLCacheVersion;
447
448 static const GLCacheVersion kCurrentCacheVersion = 0x3a914000; // The numbers mean nothing special
449
450 // #define FORCE_INVALIDATE_CACHE // Useful for testing
451
452 #define GLQUAKE_RELPATH "/id1/glquake"
CheckGLCacheVersion(const char * baseDir)453 void CheckGLCacheVersion(const char* baseDir)
454 {
455 char cachePath[1024];
456 if ((int) (sizeof(cachePath)-1) < snprintf(cachePath, sizeof(cachePath), "%s" GLQUAKE_RELPATH "/cacheversion", baseDir)) {
457 return; // buffer overflow
458 }
459 bool validCache = false;
460 {
461 GLCacheVersion vernum = 0;
462 FILE* f = fopen(cachePath, "rb");
463 if (f) {
464 if (1 == fread(&vernum, sizeof(vernum), 1, f)) {
465 if (vernum == kCurrentCacheVersion) {
466 validCache = true;
467 }
468 }
469 fclose(f);
470 }
471 }
472
473 #ifdef FORCE_INVALIDATE_CACHE
474 validCache = false;
475 #endif
476
477 if(!validCache) {
478 PMPLOG(("Invalidating glquake cache."));
479 char cacheDirPath[1024];
480 if ( (int)(sizeof(cacheDirPath)-1) < snprintf(cacheDirPath, sizeof(cacheDirPath), "%s" GLQUAKE_RELPATH, baseDir)) {
481 return; // Ran out ot memory
482 }
483 rmDir(cacheDirPath);
484 Sys_mkdir(cacheDirPath);
485 FILE* f = fopen(cachePath, "wb");
486 if (f) {
487 GLCacheVersion vernum = kCurrentCacheVersion;
488 fwrite(&vernum, sizeof(vernum), 1, f);
489 fclose(f);
490 } else {
491 PMPLOG(("Could not write %s %d.\n", cachePath, errno));
492 }
493 }
494 }
495
496 static int gArgc;
497 static char** gArgv;
498
AndroidInitArgs(int argc,char ** argv)499 void AndroidInitArgs(int argc, char** argv) {
500 gArgc = argc;
501 gArgv = argv;
502 }
503
504 static qboolean gDoubleInitializeGuard;
505 static qboolean gInitialized;
506 void GL_ReInit();
507
508 #if !defined(__clang__)
AndroidInit()509 bool AndroidInit()
510 #else
511 extern "C" bool AndroidInit_LLVM()
512 #endif
513 {
514 PMPLOG(("AndroidInit"));
515
516 PMPLOG(("This function was compiled on " __DATE__ " at " __TIME__));
517
518 if (gDoubleInitializeGuard && gInitialized)
519 {
520 GL_ReInit();
521 }
522
523 gDoubleInitializeGuard = true;
524 return true;
525 }
526
527
528 // Note: Needs a valid OpenGL context
529
AndroidInit2(int width,int height)530 void AndroidInit2(int width, int height)
531 {
532 PMPLOG(("AndroidInit2 %d,%d", width, height));
533
534 gInitialized = true;
535 PMPBEGIN(("AndroidInit2"));
536 quakeparms_t parms;
537 int j;
538 int c = 0;
539 const char* v[] = {"quake", (char*) 0};
540
541 scr_width = width;
542 scr_height = height;
543
544 // static char cwd[1024];
545
546 // signal(SIGFPE, floating_point_exception_handler);
547 // signal(SIGFPE, SIG_IGN);
548
549 memset(&parms, 0, sizeof(parms));
550
551 if (gArgc) {
552 COM_InitArgv(gArgc, (const char**) gArgv);
553 }
554 else {
555 COM_InitArgv(c, (const char**) v);
556 }
557
558 parms.argc = com_argc;
559 parms.argv = com_argv;
560
561 parms.memsize = 16*1024*1024;
562
563 j = COM_CheckParm("-mem");
564 if (j)
565 parms.memsize = (int) (Q_atof(com_argv[j+1]) * 1024 * 1024);
566 parms.membase = malloc (parms.memsize);
567
568 const char* basedir = basedir2;
569 if(direxists(basedir1))
570 {
571 basedir = basedir1;
572 }
573 else if(direxists(basedir2))
574 {
575 basedir = basedir2;
576 }
577 else
578 {
579 Sys_Error("Could not find data directories %s or %s", basedir1, basedir2);
580 }
581 parms.basedir = basedir;
582
583 CheckGLCacheVersion(basedir);
584
585 // caching is disabled by default, use -cachedir to enable
586 // parms.cachedir = cachedir;
587
588 #if 0 // FNDELAY not implemented
589 noconinput = COM_CheckParm("-noconinput");
590 if (!noconinput)
591 fcntl(0, F_SETFL, fcntl (0, F_GETFL, 0) | FNDELAY);
592 #endif
593
594 if (COM_CheckParm("-nostdout"))
595 nostdout = 1;
596
597 Sys_Init();
598
599 Host_Init(&parms);
600
601 g_oldtime = Sys_FloatTime ();
602 PMPEND(("AndroidInit2"));
603 }
604
605 static int currentFrame;
606 frameTime fastestFrame;
607 frameTime slowestFrame;
608
InitFrameTimes()609 void InitFrameTimes()
610 {
611 currentFrame = 0;
612 fastestFrame.time = 1e6;
613 fastestFrame.frame = 0;
614 slowestFrame.time = -1;
615 slowestFrame.frame = 0;
616 }
617
UpdateFrameTimes(float time)618 static void UpdateFrameTimes(float time)
619 {
620 if (currentFrame > 0) {
621
622 if (fastestFrame.time > time) {
623 fastestFrame.time = time;
624 fastestFrame.frame = currentFrame;
625 }
626 if (slowestFrame.time < time) {
627 slowestFrame.time = time;
628 slowestFrame.frame = currentFrame;
629 }
630 }
631 currentFrame++;
632 }
633
AndroidStepImp(int width,int height)634 int AndroidStepImp(int width, int height)
635 {
636 // PMPBEGIN(("AndroidStep"));
637 double time, newtime;
638 static int TotalCount = 0;
639 static double TotalFPS = 0.0;
640 if(!gInitialized)
641 AndroidInit2(width, height);
642
643 scr_width = width;
644 scr_height = height;
645
646 // find time spent rendering last frame
647 newtime = Sys_FloatTime ();
648 time = newtime - g_oldtime;
649
650 UpdateFrameTimes(time);
651 #if 0
652 // Disable the following because given a series of Ti representing time spent per frame
653 // 1/(sum(Ti)/n) isn't quite the same as sum(1/Ti)/n, especially when Ti has large variance.
654 // See LOGI in host.cpp::Host_Frame for better implementation
655 double fps = 1.0/time;
656 if (fps > 0.0 && fps < 200.0) { // Sometimes it
657 TotalCount += 1;
658 TotalFPS += fps;
659 LOGI("Quake fps: %3.2lf, Average: %3.2lf", fps, TotalFPS/TotalCount);
660 }
661 #endif
662 Host_Frame(time);
663 g_oldtime = newtime;
664 // PMPEND(("AndroidStep"));
665 return key_dest == key_game;
666 }
667
668 #if !defined(__clang__)
AndroidStep(int width,int height)669 int AndroidStep(int width, int height)
670 #else
671 extern "C" int AndroidStep_LLVM(int width, int height)
672 #endif
673 {
674 for(;;) {
675 host_framethrottled = false;
676 int result = AndroidStepImp(width, height);
677 if (!host_framethrottled) {
678 return result;
679 }
680 usleep(1000);
681 //LOGI("%s", "host_framethrottled");
682 }
683 }
684
685 extern void Host_Quit();
686
687
688 #if !defined(__clang__)
AndroidQuit()689 void AndroidQuit() {
690 #else
691 extern "C" void AndroidQuit_LLVM() {
692 #endif
693 soft_quit = true;
694 Host_Quit();
695 soft_quit = false; // In case we live on after returning.
696 }
697