You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openmw-tes3coop/apps/openmw-mp/amx/amxtime.c

481 lines
13 KiB
C

/* Date/time module for the Pawn Abstract Machine
*
* Copyright (c) ITB CompuPhase, 2001-2013
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Version: $Id: amxtime.c 4983 2013-10-21 07:32:57Z $
*/
#include <time.h>
#include <assert.h>
#include "amx.h"
#if defined __WIN32__ || defined _WIN32 || defined _Windows
#include <windows.h>
#include <mmsystem.h>
#endif
#define CELLMIN (-1 << (8*sizeof(cell) - 1))
#define SECONDS_PER_MINUTE 60
#define SECONDS_PER_HOUR 3600
#define SECONDS_PER_DAY 86400
#define SECONDS_PER_YEAR 31556952 /* based on 365.2425 days per year */
#if !defined CLOCKS_PER_SEC
#define CLOCKS_PER_SEC CLK_TCK
#endif
#if defined __WIN32__ || defined _WIN32 || defined WIN32
static int timerset = 0;
/* timeGetTime() is more accurate on WindowsNT if timeBeginPeriod(1) is set */
#define INIT_TIMER() \
if (!timerset) { \
timeBeginPeriod(1); \
timerset=1; \
}
#else
#define INIT_TIMER()
#endif
static unsigned long timestamp;
static unsigned long timelimit;
static int timerepeat;
static const unsigned char monthdays[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
static int wrap(int value, int min, int max)
{
if (value<min)
value=max;
else if (value>max)
value=min;
return value;
}
static unsigned long gettimestamp(void)
{
unsigned long value;
#if defined __WIN32__ || defined _WIN32 || defined WIN32
value=timeGetTime(); /* this value is already in milliseconds */
#elif defined __linux || defined __linux__ || defined __LINUX__ || defined __APPLE__
struct timeval tv;
gettimeofday(&tv, NULL);
value = ((tv.tv_sec * 1000) + (tv.tv_usec / 1000));
#else
value=clock();
#if CLOCKS_PER_SEC<1000
/* convert to milliseconds */
value=(cell)((1000L * value) / CLOCKS_PER_SEC);
#elif CLOCKS_PER_SEC>1000
/* convert to milliseconds */
value=(cell)(value/(CLOCKS_PER_SEC/1000));
#endif
#endif
return value;
}
void stamp2datetime(unsigned long sec1970,
int *year, int *month, int *day,
int *hour, int *minute, int *second)
{
int days, seconds;
/* find the year */
assert(year!=NULL);
for (*year = 1970; ; *year += 1) {
days = 365 + ((*year & 0x03) == 0); /* clumsy "leap-year" routine, fails for 2100 */
seconds = days * SECONDS_PER_DAY;
if ((unsigned long)seconds > sec1970)
break;
sec1970 -= seconds;
} /* if */
/* find the month */
assert(month!=NULL);
for (*month = 1; ; *month += 1) {
days = monthdays[*month - 1];
seconds = days * SECONDS_PER_DAY;
if ((unsigned long)seconds > sec1970)
break;
sec1970 -= seconds;
} /* if */
/* find the day */
assert(day!=NULL);
for (*day = 1; sec1970 >= SECONDS_PER_DAY; *day += 1)
sec1970 -= SECONDS_PER_DAY;
/* find the hour */
assert(hour!=NULL);
for (*hour = 0; sec1970 >= SECONDS_PER_HOUR; *hour += 1)
sec1970 -= SECONDS_PER_HOUR;
/* find the minute */
assert(minute!=NULL);
for (*minute = 0; sec1970 >= SECONDS_PER_MINUTE; *minute += 1)
sec1970 -= SECONDS_PER_MINUTE;
/* remainder is the number of seconds */
assert(second!=NULL);
*second = (int)sec1970;
}
static void settime(cell hour,cell minute,cell second)
{
#if defined __WIN32__ || defined _WIN32 || defined WIN32
SYSTEMTIME systim;
GetLocalTime(&systim);
if (hour!=CELLMIN)
systim.wHour=(WORD)wrap((int)hour,0,23);
if (minute!=CELLMIN)
systim.wMinute=(WORD)wrap((int)minute,0,59);
if (second!=CELLMIN)
systim.wSecond=(WORD)wrap((int)second,0,59);
SetLocalTime(&systim);
#else
/* Linux/Unix (and some DOS compilers) have stime(); on Linux/Unix, you
* must have "root" permission to call stime(); many POSIX systems will
* have settimeofday() instead
*/
time_t sec1970;
struct tm gtm;
#if defined __APPLE__ /* also valid for other POSIX systems */
struct timeval tv;
#endif
time(&sec1970);
gtm=*localtime(&sec1970);
if (hour!=CELLMIN)
gtm.tm_hour=wrap((int)hour,0,23);
if (minute!=CELLMIN)
gtm.tm_min=wrap((int)minute,0,59);
if (second!=CELLMIN)
gtm.tm_sec=wrap((int)second,0,59);
sec1970=mktime(&gtm);
#if defined __APPLE__ /* also valid for other POSIX systems */
tv.tv_sec = sec1970;
tv.tv_usec = 0;
settimeofday(&tv, 0);
#else
stime(&sec1970);
#endif
#endif
}
static void setdate(cell year,cell month,cell day)
{
int maxday;
#if defined __WIN32__ || defined _WIN32 || defined WIN32
SYSTEMTIME systim;
GetLocalTime(&systim);
if (year!=CELLMIN)
systim.wYear=(WORD)wrap((int)year,1970,2099);
if (month!=CELLMIN)
systim.wMonth=(WORD)wrap((int)month,1,12);
maxday=monthdays[systim.wMonth - 1];
if (systim.wMonth==2 && ((systim.wYear % 4)==0 && ((systim.wYear % 100)!=0 || (systim.wYear % 400)==0)))
maxday++;
if (day!=CELLMIN)
systim.wDay=(WORD)wrap((int)day,1,maxday);
SetLocalTime(&systim);
#else
/* Linux/Unix (and some DOS compilers) have stime(); on Linux/Unix, you
* must have "root" permission to call stime(); many POSIX systems will
* have settimeofday() instead
*/
time_t sec1970;
struct tm gtm;
#if defined __APPLE__ /* also valid for other POSIX systems */
struct timeval tv;
#endif
time(&sec1970);
gtm=*localtime(&sec1970);
if (year!=CELLMIN)
gtm.tm_year=year-1900;
if (month!=CELLMIN)
gtm.tm_mon=month-1;
if (day!=CELLMIN)
gtm.tm_mday=day;
sec1970=mktime(&gtm);
#if defined __APPLE__ /* also valid for other POSIX systems */
tv.tv_sec = sec1970;
tv.tv_usec = 0;
settimeofday(&tv, 0);
#else
stime(&sec1970);
#endif
#endif
}
/* settime(hour, minute, second)
* Always returns 0
*/
static cell AMX_NATIVE_CALL n_settime(AMX *amx, const cell *params)
{
(void)amx;
settime(params[1],params[2],params[3]);
return 0;
}
/* gettime(&hour, &minute, &second)
* The return value is the number of seconds since 1 January 1970 (Unix system
* time).
*/
static cell AMX_NATIVE_CALL n_gettime(AMX *amx, const cell *params)
{
time_t sec1970;
struct tm gtm;
cell *cptr;
assert(params[0]==(int)(3*sizeof(cell)));
time(&sec1970);
/* on DOS/Windows, the timezone is usually not set for the C run-time
* library; in that case gmtime() and localtime() return the same value
*/
gtm=*localtime(&sec1970);
cptr=amx_Address(amx,params[1]);
*cptr=gtm.tm_hour;
cptr=amx_Address(amx,params[2]);
*cptr=gtm.tm_min;
cptr=amx_Address(amx,params[3]);
*cptr=gtm.tm_sec;
/* the time() function returns the number of seconds since January 1 1970
* in Universal Coordinated Time (the successor to Greenwich Mean Time)
*/
return (cell)sec1970;
}
/* setdate(year, month, day)
* Always returns 0
*/
static cell AMX_NATIVE_CALL n_setdate(AMX *amx, const cell *params)
{
(void)amx;
setdate(params[1],params[2],params[3]);
return 0;
}
/* getdate(&year, &month, &day)
* The return value is the number of days since the start of the year. January
* 1 is day 1 of the year.
*/
static cell AMX_NATIVE_CALL n_getdate(AMX *amx, const cell *params)
{
time_t sec1970;
struct tm gtm;
cell *cptr;
assert(params[0]==(int)(3*sizeof(cell)));
time(&sec1970);
gtm=*localtime(&sec1970);
cptr=amx_Address(amx,params[1]);
*cptr=gtm.tm_year+1900;
cptr=amx_Address(amx,params[2]);
*cptr=gtm.tm_mon+1;
cptr=amx_Address(amx,params[3]);
*cptr=gtm.tm_mday;
return gtm.tm_yday+1;
}
/* tickcount(&granularity)
* Returns the number of milliseconds since start-up. For a 32-bit cell, this
* count overflows after approximately 24 days of continuous operation.
*/
static cell AMX_NATIVE_CALL n_tickcount(AMX *amx, const cell *params)
{
cell *cptr;
assert(params[0]==(int)sizeof(cell));
INIT_TIMER();
cptr=amx_Address(amx,params[1]);
#if defined __WIN32__ || defined _WIN32 || defined WIN32
*cptr=1000; /* granularity = 1 ms */
#else
*cptr=(cell)CLOCKS_PER_SEC; /* in Unix/Linux, this is often 100 */
#endif
return gettimestamp() & 0x7fffffff;
}
/* delay(milliseconds)
* Pauses for (at least) the requested number of milliseconds.
*/
static cell AMX_NATIVE_CALL n_delay(AMX *amx, const cell *params)
{
unsigned long stamp;
(void)amx;
assert(params[0]==(int)sizeof(cell));
INIT_TIMER();
stamp=gettimestamp();
while (gettimestamp()-stamp < (unsigned long)params[1])
/* nothing */;
return 0;
}
/* settimer(milliseconds, bool: singleshot = false)
* Sets the delay until the @timer() callback is called. The timer may either
* be single-shot or repetitive.
*/
static cell AMX_NATIVE_CALL n_settimer(AMX *amx, const cell *params)
{
(void)amx;
assert(params[0]==(int)(2*sizeof(cell)));
timestamp=gettimestamp();
timelimit=params[1];
timerepeat=(int)(params[2]==0);
return 0;
}
/* bool: gettimer(&milliseconds, bool: &singleshot = false)
* Retrieves the timer set with settimer(); returns true if a timer
* was set up, or false otherwise.
*/
static cell AMX_NATIVE_CALL n_gettimer(AMX *amx, const cell *params)
{
cell *cptr;
assert(params[0]==(int)(2*sizeof(cell)));
cptr=amx_Address(amx,params[1]);
*cptr=timelimit;
cptr=amx_Address(amx,params[2]);
*cptr=timerepeat;
return timelimit>0;
}
/* settimestamp(seconds1970) sets the date and time from a single parameter: the
* number of seconds since 1 January 1970.
*/
static cell AMX_NATIVE_CALL n_settimestamp(AMX *amx, const cell *params)
{
#if defined __WIN32__ || defined _WIN32 || defined WIN32
int year, month, day, hour, minute, second;
stamp2datetime(params[1],
&year, &month, &day,
&hour, &minute, &second);
setdate(year, month, day);
settime(hour, minute, second);
#else
/* Linux/Unix (and some DOS compilers) have stime(); on Linux/Unix, you
* must have "root" permission to call stime(); many POSIX systems will
* have settimeofday() instead
*/
#if defined __APPLE__ /* also valid for other POSIX systems */
struct timeval tv;
tv.tv_sec = params[1];
tv.tv_usec = 0;
settimeofday(&tv, 0);
#else
time_t sec1970=(time_t)params[1];
stime(&sec1970);
#endif
#endif
(void)amx;
return 0;
}
/* cvttimestamp(seconds1970, &year, &month, &day, &hour, &minute, &second)
*/
static cell AMX_NATIVE_CALL n_cvttimestamp(AMX *amx, const cell *params)
{
int year, month, day, hour, minute, second;
(void)amx;
stamp2datetime(params[1],
&year, &month, &day,
&hour, &minute, &second);
return 0;
}
#if !defined AMXTIME_NOIDLE
static AMX_IDLE PrevIdle = NULL;
static int idxTimer = -1;
static int AMXAPI amx_TimeIdle(AMX *amx, int AMXAPI Exec(AMX *, cell *, int))
{
int err=0;
assert(idxTimer >= 0);
if (PrevIdle != NULL)
PrevIdle(amx, Exec);
if (timelimit>0 && (gettimestamp()-timestamp)>=timelimit) {
if (timerepeat)
timestamp+=timelimit;
else
timelimit=0; /* do not repeat single-shot timer */
err = Exec(amx, NULL, idxTimer);
while (err == AMX_ERR_SLEEP)
err = Exec(amx, NULL, AMX_EXEC_CONT);
} /* if */
return err;
}
#endif
#if defined __cplusplus
extern "C"
#endif
const AMX_NATIVE_INFO time_Natives[] = {
{ "gettime", n_gettime },
{ "settime", n_settime },
{ "getdate", n_getdate },
{ "setdate", n_setdate },
{ "tickcount", n_tickcount },
{ "settimer", n_settimer },
{ "gettimer", n_gettimer },
{ "delay", n_delay },
{ "settimestamp", n_settimestamp },
{ "cvttimestamp", n_cvttimestamp },
{ NULL, NULL } /* terminator */
};
int AMXEXPORT AMXAPI amx_TimeInit(AMX *amx)
{
#if !defined AMXTIME_NOIDLE
/* see whether there is a @timer() function */
if (amx_FindPublic(amx,"@timer",&idxTimer) == AMX_ERR_NONE) {
if (amx_GetUserData(amx, AMX_USERTAG('I','d','l','e'), (void**)&PrevIdle) != AMX_ERR_NONE)
PrevIdle = NULL;
amx_SetUserData(amx, AMX_USERTAG('I','d','l','e'), amx_TimeIdle);
} /* if */
#endif
return amx_Register(amx, time_Natives, -1);
}
int AMXEXPORT AMXAPI amx_TimeCleanup(AMX *amx)
{
(void)amx;
#if !defined AMXTIME_NOIDLE
PrevIdle = NULL;
#endif
return AMX_ERR_NONE;
}