#define TABULA_ENTRY_POINT 1
#include "client/client.h"
#include <GLFW/glfw3.h>
#include <uv.h>
#include <alias/str.h>
refexport_t re;
cvar_t *vid_ref;
cvar_t *vid_xpos;
cvar_t *vid_ypos;
cvar_t *vid_fullscreen;
cvar_t *vid_gamma;
viddef_t viddef;
static bool vid__active = false;
static uv_lib_t vid__library;
static bool vid__library_loaded = false;
int ActiveApp;
void AppActivate(bool fActive, bool minimize) {
Key_ClearStates();
if(fActive && !minimize)
ActiveApp = true;
else
ActiveApp = false;
if(!ActiveApp) {
IN_Activate(false);
} else {
IN_Activate(true);
}
}
static void vid__Close(void) {
if(vid__library_loaded) {
uv_dlclose(&vid__library);
vid__library_loaded = false;
}
vid__active = false;
}
static void vid__API_Printf(int print_level, const char *fmt, ...) {
va_list ap;
const char * msg;
va_start(ap, fmt);
alias_str_stack_formatv(msg, fmt, ap);
va_end(ap);
if(print_level == PRINT_ALL) {
Com_Printf("%s", msg);
} else if(print_level == PRINT_DEVELOPER) {
Com_DPrintf("%s", msg);
} else if(print_level == PRINT_ALERT) {
}
}
static void vid__API_Error(int err_level, const char *fmt, ...) {
va_list ap;
const char * msg;
va_start(ap, fmt);
alias_str_stack_formatv(msg, fmt, ap);
va_end(ap);
Com_Error(err_level, "%s", msg);
}
typedef struct vidmode_s {
const char *description;
int width, height;
int mode;
} vidmode_t;
static vidmode_t vid_modes[] = {{"Mode 0: 320x240", 320, 240, 0}, {"Mode 1: 400x300", 400, 300, 1},
{"Mode 2: 512x384", 512, 384, 2}, {"Mode 3: 640x480", 640, 480, 3},
{"Mode 4: 800x600", 800, 600, 4}, {"Mode 5: 960x720", 960, 720, 5},
{"Mode 6: 1024x768", 1024, 768, 6}, {"Mode 7: 1152x864", 1152, 864, 7},
{"Mode 8: 1280x960", 1280, 960, 8}, {"Mode 9: 1600x1200", 1600, 1200, 9}};
#define VID_NUM_MODES (sizeof(vid_modes) / sizeof(vid_modes[0]))
static bool vid__API_GetModeInfo(int *width, int *height, int mode) {
if(mode < 0 || mode >= VID_NUM_MODES)
return false;
*width = vid_modes[mode].width;
*height = vid_modes[mode].height;
return true;
}
static void vid__API_NewWindow(int width, int height) {
viddef.width = width;
viddef.height = height;
cl.force_refdef = true; }
static bool vid__OpenFunction(GetRefAPI_t function, const char *name) {
refimport_t ri;
Com_Printf("------- Loading %s -------\n", name);
ri.Cmd_AddCommand = Cmd_AddCommand;
ri.Cmd_RemoveCommand = Cmd_RemoveCommand;
ri.Cmd_Argc = Cmd_Argc;
ri.Cmd_Argv = Cmd_Argv;
ri.Cmd_ExecuteText = Cbuf_ExecuteText;
ri.Con_Printf = vid__API_Printf;
ri.Sys_Error = vid__API_Error;
ri.FS_LoadFile = FS_LoadFile;
ri.FS_FreeFile = FS_FreeFile;
ri.FS_Gamedir = FS_Gamedir;
ri.Cvar_Get = Cvar_Get;
ri.Cvar_Set = Cvar_Set;
ri.Cvar_SetValue = Cvar_SetValue;
ri.Vid_GetModeInfo = vid__API_GetModeInfo;
ri.Vid_MenuInit = VID_MenuInit;
ri.Vid_NewWindow = vid__API_NewWindow;
re = function(ri);
if(re.Init(NULL, NULL) != 0) {
re.Shutdown();
vid__Close();
return false;
}
Com_Printf("------------------------------------\n");
vid__active = true;
return true;
}
static bool vid__OpenLibrary(const char *name) {
if(uv_dlopen(name, &vid__library) < 0) {
return false;
}
GetRefAPI_t function;
if(uv_dlsym(&vid__library, "GetRefAPI", (void **)&function) < 0) {
return false;
}
return vid__OpenFunction(function, name);
}
void VID_CheckChanges(void) {
char name[100];
if(vid_ref->modified) {
cl.force_refdef = true; S_StopAllSounds();
}
while(vid_ref->modified) {
vid_ref->modified = false;
vid_fullscreen->modified = true;
cl.refresh_prepped = false;
cls.disable_screen = true;
if(strcmp(vid_ref->string, "internal") == 0) {
extern refexport_t GetRefAPI(refimport_t rimp);
vid__OpenFunction(GetRefAPI, "internal");
} else {
Com_sprintf(name, sizeof(name), "ref_%s.dll", vid_ref->string);
if(!vid__OpenLibrary(name)) {
Cvar_Set("vid_ref", "internal");
if(cls.key_dest != key_console) {
Con_ToggleConsole_f();
}
}
}
cls.disable_screen = false;
}
if(vid_xpos->modified || vid_ypos->modified) {
vid_xpos->modified = false;
vid_ypos->modified = false;
}
}
static void vid__Restart(void) {
}
static void vid__Front(void) {
}
void VID_Init(void) {
vid_ref = Cvar_Get("vid_ref", "internal", CVAR_ARCHIVE);
vid_xpos = Cvar_Get("vid_xpos", "3", CVAR_ARCHIVE);
vid_ypos = Cvar_Get("vid_ypos", "22", CVAR_ARCHIVE);
vid_fullscreen = Cvar_Get("vid_fullscreen", "0", CVAR_ARCHIVE);
vid_gamma = Cvar_Get("vid_gamma", "1", CVAR_ARCHIVE);
Cmd_AddCommand("vid_restart", vid__Restart);
Cmd_AddCommand("vid_front", vid__Front);
VID_CheckChanges();
}
void VID_Shutdown(void) {
if(vid__active) {
re.Shutdown();
vid__Close();
}
}
bool NET_StringToSockaddr(const char *s, struct sockaddr *sadr) {
struct hostent *h;
char *colon;
char copy[128];
memset(sadr, 0, sizeof(*sadr));
((struct sockaddr_in *)sadr)->sin_family = AF_INET;
((struct sockaddr_in *)sadr)->sin_port = 0;
strcpy(copy, s);
for(colon = copy; *colon; colon++)
if(*colon == ':') {
*colon = 0;
((struct sockaddr_in *)sadr)->sin_port = htons((short)atoi(colon + 1));
}
if(copy[0] >= '0' && copy[0] <= '9') {
*(int *)&((struct sockaddr_in *)sadr)->sin_addr = inet_addr(copy);
} else {
if(!(h = gethostbyname(copy)))
return 0;
*(int *)&((struct sockaddr_in *)sadr)->sin_addr = *(int *)h->h_addr_list[0];
}
return true;
}
static void SockadrToNetadr(const struct sockaddr *s, netadr_t *a) {
const struct sockaddr_in * in = (const struct sockaddr_in *)s;
if(s->sa_family == AF_INET) {
a->type = NA_IP;
*(uint32_t *)&a->ip = in->sin_addr.s_addr;
a->port = in->sin_port;
}
}
static void NetadrToSockadr(const netadr_t *a, struct sockaddr *s) {
alias_memory_clear(s, sizeof(s));
struct sockaddr_in * in = (struct sockaddr_in *)s;
in->sin_family = AF_INET;
in->sin_port = a->port;
in->sin_addr.s_addr = (a->type == NA_BROADCAST) ? INADDR_BROADCAST : *(uint32_t *)&a->ip;
}
bool NET_StringToAdr(char *s, netadr_t *a) {
struct sockaddr sadr;
if(!strcmp(s, "localhost")) {
memset(a, 0, sizeof(*a));
a->type = NA_LOOPBACK;
return true;
}
if(!NET_StringToSockaddr(s, &sadr))
return false;
SockadrToNetadr(&sadr, a);
return true;
}
bool NET_IsLocalAddress(netadr_t adr) { return adr.type == NA_LOOPBACK; }
char *NET_AdrToString(netadr_t a) {
static char s[64];
if(a.type == NA_LOOPBACK)
Com_sprintf(s, sizeof(s), "loopback");
else if(a.type == NA_IP)
Com_sprintf(s, sizeof(s), "%i.%i.%i.%i:%i", a.ip[0], a.ip[1], a.ip[2], a.ip[3], ntohs(a.port));
return s;
}
bool NET_CompareAdr(netadr_t a, netadr_t b) {
if(a.type != b.type)
return false;
if(a.type == NA_LOOPBACK)
return true;
if(a.type == NA_IP) {
if(a.ip[0] == b.ip[0] && a.ip[1] == b.ip[1] && a.ip[2] == b.ip[2] && a.ip[3] == b.ip[3] && a.port == b.port)
return true;
return false;
}
return false;
}
bool NET_CompareBaseAdr(netadr_t a, netadr_t b) {
if(a.type != b.type)
return false;
if(a.type == NA_LOOPBACK)
return true;
if(a.type == NA_IP) {
if(a.ip[0] == b.ip[0] && a.ip[1] == b.ip[1] && a.ip[2] == b.ip[2] && a.ip[3] == b.ip[3])
return true;
return false;
}
return false;
}
#define NET__MAILBOX_SIZE__LOG2 4
#define NET__MAILBOX_SIZE (1 << NET__MAILBOX_SIZE__LOG2)
struct NET_MailboxMessage {
byte data[MAX_MSGLEN];
uint32_t length;
netadr_t from;
};
struct NET_Mailbox {
struct NET_MailboxMessage messages[NET__MAILBOX_SIZE];
uint32_t get;
uint32_t send;
};
static bool NET_Mailbox__get(struct NET_Mailbox *mailbox, netadr_t *from, sizebuf_t *message) {
if(mailbox->send - mailbox->get > NET__MAILBOX_SIZE) {
mailbox->get = mailbox->send - NET__MAILBOX_SIZE;
}
if(mailbox->get >= mailbox->send) {
return false;
}
uint32_t index = mailbox->get & (NET__MAILBOX_SIZE - 1);
mailbox->get++;
alias_memory_copy(message->data, message->maxsize, mailbox->messages[index].data, mailbox->messages[index].length);
message->cursize = mailbox->messages[index].length;
*from = mailbox->messages[index].from;
return true;
}
static void NET_Mailbox__send(struct NET_Mailbox *mailbox, const void * data, uint32_t length, netadr_t from) {
uint32_t index = mailbox->send & (NET__MAILBOX_SIZE - 1);
mailbox->send++;
alias_memory_copy(mailbox->messages[index].data, sizeof(mailbox->messages[index].data), data, length);
mailbox->messages[index].length = length;
mailbox->messages[index].from = from;
}
static struct NET_Mailbox net__mailboxes[NETSRC_COUNT];
static uv_udp_t net__udp_handles[NETSRC_COUNT];
bool NET_GetPacket(netsrc_t sock, netadr_t *from, sizebuf_t *message) {
struct NET_Mailbox *mailbox = &net__mailboxes[sock];
return NET_Mailbox__get(mailbox, from, message);
}
static void NET__udp_send_cb(uv_udp_send_t* req, int status) {
alias_free(NULL, req, sizeof(*req), alignof(*req));
}
static void net__udp_recv_alloc_cb(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) {
buf->base = alias_malloc(NULL, suggested_size, 1);
buf->len = suggested_size;
}
static void net__udp_recv_cb(uv_udp_t *handle, ssize_t nread, const uv_buf_t *buf, const struct sockaddr *addr, unsigned flags) {
struct NET_Mailbox *mailbox = uv_handle_get_data((uv_handle_t *)handle);
if(flags & UV_UDP_PARTIAL) {
}
if(nread > 0) {
netadr_t from;
SockadrToNetadr(addr, &from);
NET_Mailbox__send(mailbox, buf->base, nread, from);
}
if(buf->base != NULL && (nread < 0 || flags & UV_UDP_MMSG_FREE)) {
alias_free(NULL, (void *)buf->base, buf->len, 1);
}
}
void NET_SendPacket(netsrc_t sock, int length, void *data, netadr_t to) {
if(to.type == NA_LOOPBACK) {
NET_Mailbox__send(&net__mailboxes[!sock], data, length, to);
return;
}
uv_udp_t *handle = &net__udp_handles[sock];
struct sockaddr address;
NetadrToSockadr(&to, &address);
uv_udp_send_t *req = alias_malloc(NULL, sizeof(*req), alignof(*req));
uv_buf_t buf = uv_buf_init(data, length);
int err = uv_udp_send(req, handle, &buf, 1, &address, NET__udp_send_cb);
if(err != 0) {
}
}
void NET_Init(void) {
}
void NET_Sleep(int msec) {
}
static const char * net__get_ip(void) {
cvar_t *ip = Cvar_Get("ip", "localhost", CVAR_NOSET);
return ip->string;
}
#define TO_STRING(X) TO_STRING__0(X)
#define TO_STRING__0(X) TO_STRING__1(X)
#define TO_STRING__1(X) #X
static int net__get_server_port(void) {
int port = Cvar_Get("ip_hostport", "0", CVAR_NOSET)->value;
if(port != 0) {
return port;
}
port = Cvar_Get("hostport", "0", CVAR_NOSET)->value;
if(port != 0) {
return port;
}
return Cvar_Get("port", TO_STRING(PORT_SERVER), CVAR_NOSET)->value;
}
static int net__get_client_port(void) {
int port = Cvar_Get("ip_clientport", "0", CVAR_NOSET)->value;
if(port != 0) {
return port;
}
return Cvar_Get("clientport", TO_STRING(CLIENT_SERVER), CVAR_NOSET)->value;
}
static bool net__start_udp(uv_udp_t *handle, struct NET_Mailbox *mailbox, const char *ip, int port) {
int err;
if((err = uv_udp_init(global_uv_loop(), handle)) != 0) {
return false;
}
uv_handle_set_data((uv_handle_t *)handle, mailbox);
if((err = uv_udp_set_broadcast(handle, 1)) != 0) {
return false;
}
struct sockaddr_in address;
if(!ip || !ip[0] || !strcmp(ip, "localhost")) {
address.sin_addr.s_addr = INADDR_ANY;
} else {
NET_StringToSockaddr(ip, (struct sockaddr *)&address);
}
if(port == PORT_ANY) {
address.sin_port = 0;
} else {
address.sin_port = htons((short)port);
}
address.sin_family = AF_INET;
if((err = uv_udp_bind(handle, (const struct sockaddr *)&address, 0)) != 0) {
return false;
}
if((err = uv_udp_recv_start(handle, net__udp_recv_alloc_cb, net__udp_recv_cb)) != 0) {
return false;
}
return true;
}
static bool net__activate_udp(void) {
const char * ip = net__get_ip();
int port = net__get_server_port();
if(!net__start_udp(&net__udp_handles[NS_SERVER], &net__mailboxes[NS_SERVER], ip, port)) {
return false;
}
if(Cvar_VariableValue("dedicated")) {
return true;
}
port = net__get_client_port();
if(!net__start_udp(&net__udp_handles[NS_CLIENT], &net__mailboxes[NS_CLIENT], ip, port)) {
if(!net__start_udp(&net__udp_handles[NS_CLIENT], &net__mailboxes[NS_CLIENT], ip, PORT_ANY)) {
return false;
}
}
return true;
}
static void net__deactivate_udp(void) {
}
void NET_Config(bool active) {
static bool is_active = false;
if(active == is_active) {
return;
}
if(active) {
net__activate_udp();
} else {
net__deactivate_udp();
}
}
unsigned int sys_frame_time;
unsigned int sys_msg_time;
void Sys_Error(char *error, ...) {
va_list argptr;
CL_Shutdown();
Qcommon_Shutdown();
va_start(argptr, error);
vfprintf(stderr, error, argptr);
va_end(argptr);
exit(1);
}
void Sys_SendKeyEvents(void) {
extern GLFWwindow *glfw_window;
if(glfw_window != NULL && glfwWindowShouldClose(glfw_window)) {
Com_Quit();
}
glfwPollEvents();
sys_frame_time = Sys_Milliseconds();
}
static void windows_uv_idle_cb(uv_idle_t *handle) {
Sys_SendKeyEvents();
}
void Sys_Init(void) {
static uv_idle_t windows_uv_idle;
uv_idle_init(global_uv_loop(), &windows_uv_idle);
uv_idle_start(&windows_uv_idle, windows_uv_idle_cb);
}
void Sys_Quit(void) {
CL_Shutdown();
Qcommon_Shutdown();
exit(0);
}
char *Sys_ConsoleInput(void) {
return NULL;
}
void Sys_ConsoleOutput(char *string) {
}
char *Sys_GetClipboardData(void) {
return NULL;
}
void Sys_AppActivate(void) {
}
static struct {
char base[MAX_OSPATH];
char path[MAX_OSPATH + 2];
char pattern[MAX_OSPATH];
uv_dir_t *dir;
} find;
static int glob_match(const char *pattern, const char *text);
static int glob_match_after_star(const char *pattern, const char *text) {
const char *p = pattern;
const char *t = text;
char c, c1;
while ((c = *p++) == '?' || c == '*')
if (c == '?' && *t++ == '\0')
return 0;
if (c == '\0')
return 1;
if (c == '\\')
c1 = *p;
else
c1 = c;
while (1) {
if ((c == '[' || *t == c1) && glob_match(p - 1, t))
return 1;
if (*t++ == '\0')
return 0;
}
}
static int glob_pattern_p(const char *pattern)
{
const char *p = pattern;
char c;
int open = 0;
while ((c = *p++) != '\0')
switch (c) {
case '?':
case '*':
return 1;
case '[':
open++;
continue;
case ']':
if (open)
return 1;
continue;
case '\\':
if (*p++ == '\0')
return 0;
}
return 0;
}
static int glob_match(const char *pattern, const char *text) {
const char *p = pattern;
const char *t = text;
char c;
while ((c = *p++) != '\0')
switch (c) {
case '?':
if (*t == '\0')
return 0;
else
++t;
break;
case '\\':
if (*p++ != *t++)
return 0;
break;
case '*':
return glob_match_after_star(p, t);
case '[':
{
char c1 = *t++;
int invert;
if (!c1)
return (0);
invert = ((*p == '!') || (*p == '^'));
if (invert)
p++;
c = *p++;
while (1) {
register char cstart = c, cend = c;
if (c == '\\') {
cstart = *p++;
cend = cstart;
}
if (c == '\0')
return 0;
c = *p++;
if (c == '-' && *p != ']') {
cend = *p++;
if (cend == '\\')
cend = *p++;
if (cend == '\0')
return 0;
c = *p++;
}
if (c1 >= cstart && c1 <= cend)
goto match;
if (c == ']')
break;
}
if (!invert)
return 0;
break;
match:
while (c != ']') {
if (c == '\0')
return 0;
c = *p++;
if (c == '\0')
return 0;
else if (c == '\\')
++p;
}
if (invert)
return 0;
break;
}
default:
if (c != *t++)
return 0;
}
return *t == '\0';
}
static bool CompareAttributes(const char *path, const char *name, unsigned musthave, unsigned canthave) {
alias_str fn;
alias_str_stack_format(fn, "%s/%s", path, name);
uv_fs_t req;
uv_fs_stat(global_uv_loop(), &req, fn, NULL);
uv_fs_req_cleanup(&req);
if(req.result < 0) {
return false; }
if((req.statbuf.st_mode & S_IFDIR) && (canthave & SFF_SUBDIR)) {
return false;
}
if((musthave & SFF_SUBDIR) && !(req.statbuf.st_mode & S_IFDIR)) {
return false;
}
return true;
}
char *Sys_FindNext(unsigned musthave, unsigned canhave) {
if(find.dir == NULL)
return NULL;
uv_dirent_t dirent;
find.dir->nentries = 1;
find.dir->dirents = &dirent;
for(;;) {
uv_fs_t req;
int result = uv_fs_readdir(global_uv_loop(), &req, find.dir, NULL);
uv_fs_req_cleanup(&req);
if(result <= 0) {
Sys_FindClose();
return NULL;
}
if(dirent.type != UV_DIRENT_FILE || dirent.name == NULL) {
continue;
}
if(!*find.pattern || glob_match(find.pattern, dirent.name)) {
if(CompareAttributes(find.base, dirent.name, musthave, canhave)) {
sprintf(find.path, sizeof(find.path), "%s/%s", find.base, dirent.name);
return find.path;
}
}
}
}
char *Sys_FindFirst(const char *path, unsigned musthave, unsigned canhave) {
char *p;
if(find.dir != NULL) {
Sys_Error("Sys_BeginFind without close");
}
strcpy(find.base, path);
if((p = strrchr(find.base, '/')) != NULL) {
*p = 0;
strcpy(find.pattern, p + 1);
} else {
strcpy(find.pattern, "*");
}
if(strcmp(find.pattern, "*.*") == 0) {
strcpy(find.pattern, "*");
}
uv_fs_t req;
if(uv_fs_opendir(global_uv_loop(), &req, find.base, NULL) < 0) {
uv_fs_req_cleanup(&req);
return NULL;
}
find.dir = (uv_dir_t *)req.ptr;
uv_fs_req_cleanup(&req);
return Sys_FindNext(musthave, canhave);
}
void Sys_FindClose(void) {
if(find.dir != NULL) {
uv_fs_t req;
uv_fs_closedir(global_uv_loop(), &req, find.dir, NULL);
uv_fs_req_cleanup(&req);
}
find.dir = NULL;
}
void Sys_UnloadGame(void) {}
void *Sys_GetGameAPI(void *parms) {
extern void *GetGameAPI(void *);
return GetGameAPI(parms);
}
unsigned int curtime;
unsigned int sys_milliseconds_base;
static void sys_milliseconds_init(void) { sys_milliseconds_base = (unsigned int)(uv_hrtime() / (uint64_t)1000000); }
unsigned int Sys_Milliseconds(void) {
static uv_once_t init = UV_ONCE_INIT;
uv_once(&init, sys_milliseconds_init);
curtime = (unsigned int)(uv_hrtime() / (uint64_t)1000000) - sys_milliseconds_base;
return curtime;
}
void Sys_Mkdir(char *path) {
uv_fs_t req;
uv_fs_mkdir(global_uv_loop(), &req, path, 0777, NULL);
}
int tabula_entry_point(int argc, char **argv) {
Qcommon_Init(argc, argv);
return Qcommon_RunFrames();
}