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 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 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 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 168 void Sys_Init(void) 169 { 170 171 } 172 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 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 */ 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 232 void Sys_mkdir (const char *path) 233 { 234 mkdir (path, 0777); 235 } 236 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 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 269 int Sys_FileWrite (int handle, const void *src, int count) 270 { 271 return write (handle, src, count); 272 } 273 274 void Sys_FileClose (int handle) 275 { 276 close (handle); 277 } 278 279 void Sys_FileSeek (int handle, int position) 280 { 281 lseek (handle, position, SEEK_SET); 282 } 283 284 int Sys_FileRead (int handle, void *dest, int count) 285 { 286 return read (handle, dest, count); 287 } 288 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 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 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 350 void alarm_handler(int x) 351 { 352 oktogo=1; 353 } 354 355 void Sys_LineRefresh(void) 356 { 357 } 358 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 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 384 void Sys_HighFPPrecision (void) 385 { 386 } 387 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 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 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" 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 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__) 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 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 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 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 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__) 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__) 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