1/* 2Copyright (C) 1996-1997 Id Software, Inc. 3 4This program is free software; you can redistribute it and/or 5modify it under the terms of the GNU General Public License 6as published by the Free Software Foundation; either version 2 7of the License, or (at your option) any later version. 8 9This program is distributed in the hope that it will be useful, 10but WITHOUT ANY WARRANTY; without even the implied warranty of 11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 13See the GNU General Public License for more details. 14 15You should have received a copy of the GNU General Public License 16along with this program; if not, write to the Free Software 17Foundation, 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 47qboolean isDedicated; 48 49int noconinput = 0; 50int nostdout = 0; 51 52// Look for data on either the sdcard or the internal data store. 53// (We look at the sdcard first 54 55static const char *basedir1 = "/sdcard/data/quake"; 56static const char *basedir2 = "/data/quake"; 57 58static const char *cachedir = "/tmp"; 59 60cvar_t sys_linerefresh = CVAR2("sys_linerefresh","0");// set for entity display 61 62// ======================================================================= 63// General routines 64// ======================================================================= 65 66void Sys_DebugNumber(int y, int val) 67{ 68} 69 70/* 71void 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 84void 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 119void 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 146qboolean soft_quit; 147 148void 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 163void Sys_Init(void) 164{ 165 166} 167 168void 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 194void 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============ 211Sys_FileTime 212 213returns -1 if not present 214============ 215*/ 216int 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 227void Sys_mkdir (const char *path) 228{ 229 mkdir (path, 0777); 230} 231 232int 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 249int 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 264int Sys_FileWrite (int handle, const void *src, int count) 265{ 266 return write (handle, src, count); 267} 268 269void Sys_FileClose (int handle) 270{ 271 close (handle); 272} 273 274void Sys_FileSeek (int handle, int position) 275{ 276 lseek (handle, position, SEEK_SET); 277} 278 279int Sys_FileRead (int handle, void *dest, int count) 280{ 281 return read (handle, dest, count); 282} 283 284void 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 299void 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 322double 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 343static volatile int oktogo; 344 345void alarm_handler(int x) 346{ 347 oktogo=1; 348} 349 350void Sys_LineRefresh(void) 351{ 352} 353 354void floating_point_exception_handler(int whatever) 355{ 356// Sys_Warn("floating point exception\n"); 357 signal(SIGFPE, floating_point_exception_handler); 358} 359 360char *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 379void Sys_HighFPPrecision (void) 380{ 381} 382 383void Sys_LowFPPrecision (void) 384{ 385} 386#endif 387 388int skipframes; 389 390// The following APIs are called from the Java activity 391 392double g_oldtime; 393 394extern int scr_width; 395extern int scr_height; 396 397qboolean 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 413void 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 441typedef unsigned long GLCacheVersion; 442 443static 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" 448void 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 491static int gArgc; 492static char** gArgv; 493 494void AndroidInitArgs(int argc, char** argv) { 495 gArgc = argc; 496 gArgv = argv; 497} 498 499static qboolean gDoubleInitializeGuard; 500static qboolean gInitialized; 501void GL_ReInit(); 502 503bool 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 521void 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 596static int currentFrame; 597frameTime fastestFrame; 598frameTime slowestFrame; 599 600void InitFrameTimes() 601{ 602 currentFrame = 0; 603 fastestFrame.time = 1e6; 604 fastestFrame.frame = 0; 605 slowestFrame.time = -1; 606 slowestFrame.frame = 0; 607} 608 609static 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 625int 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 648int 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 660extern void Host_Quit(); 661void AndroidQuit() { 662 soft_quit = true; 663 Host_Quit(); 664 soft_quit = false; // In case we live on after returning. 665} 666