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