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 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 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 LOGI("%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 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 163 void Sys_Init(void) 164 { 165 166 } 167 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 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 */ 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 227 void Sys_mkdir (const char *path) 228 { 229 mkdir (path, 0777); 230 } 231 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 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 264 int Sys_FileWrite (int handle, const void *src, int count) 265 { 266 return write (handle, src, count); 267 } 268 269 void Sys_FileClose (int handle) 270 { 271 close (handle); 272 } 273 274 void Sys_FileSeek (int handle, int position) 275 { 276 lseek (handle, position, SEEK_SET); 277 } 278 279 int Sys_FileRead (int handle, void *dest, int count) 280 { 281 return read (handle, dest, count); 282 } 283 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 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 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 345 void alarm_handler(int x) 346 { 347 oktogo=1; 348 } 349 350 void Sys_LineRefresh(void) 351 { 352 } 353 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 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 379 void Sys_HighFPPrecision (void) 380 { 381 } 382 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 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 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" 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 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 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 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 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 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 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 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(); 661 void AndroidQuit() { 662 soft_quit = true; 663 Host_Quit(); 664 soft_quit = false; // In case we live on after returning. 665 } 666