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