commit 2ebc9e2cb6c27b2334709cb10df35216f6cd3338 Author: janwas Date: Sun Sep 21 21:24:53 2003 +0000 Initial revision This was SVN commit r5. diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..8c90e76c84 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,11 @@ +*.dae filter=lfs diff=lfs merge=lfs -text +*.dds filter=lfs diff=lfs merge=lfs -text +*.hmap filter=lfs diff=lfs merge=lfs -text +*.icns filter=lfs diff=lfs merge=lfs -text +*.ogg filter=lfs diff=lfs merge=lfs -text +*.pmd filter=lfs diff=lfs merge=lfs -text +*.pmp filter=lfs diff=lfs merge=lfs -text +*.png filter=lfs diff=lfs merge=lfs -text +*.psa filter=lfs diff=lfs merge=lfs -text +*.ttf filter=lfs diff=lfs merge=lfs -text +binaries/data/mods/official/maps/**/*.xml filter=lfs diff=lfs merge=lfs -text diff --git a/binaries/data/verdana.fnt b/binaries/data/verdana.fnt new file mode 100755 index 0000000000..4b36eb6bfa --- /dev/null +++ b/binaries/data/verdana.fnt @@ -0,0 +1,3 @@ +verdana.raw +24 24 +6 6 7 14 10 18 11 4 7 7 9 14 5 7 6 8 10 10 10 10 10 9 11 10 11 11 8 7 13 13 13 9 16 12 12 12 13 11 11 13 13 7 7 12 9 15 13 13 11 13 12 11 10 11 12 16 12 10 11 7 8 7 12 10 10 10 10 9 10 10 6 10 10 4 6 11 4 15 10 11 10 10 7 9 7 10 10 14 10 10 9 10 8 9 13 17 \ No newline at end of file diff --git a/binaries/data/verdana.raw b/binaries/data/verdana.raw new file mode 100755 index 0000000000..d403393d08 Binary files /dev/null and b/binaries/data/verdana.raw differ diff --git a/source/detect.cpp b/source/detect.cpp new file mode 100755 index 0000000000..feeea953de --- /dev/null +++ b/source/detect.cpp @@ -0,0 +1,301 @@ +// system detect +// +// Copyright (c) 2003 Jan Wassenberg +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// Contact info: +// Jan.Wassenberg@stud.uni-karlsruhe.de +// http://www.stud.uni-karlsruhe.de/~urkt/ + +// things missing in POSIX and SDL :P + +#include +#include + +#ifdef _WIN32 +#include "win.h" +#endif + +#include "detect.h" +#include "time.h" +#include "ogl.h" +#include "wsdl.h" + + +// useful for choosing a video mode. not called by detect(). +// currently not implemented for non-Win32 systems (returns 800x600). +void get_cur_resolution(int& xres, int& yres) +{ + // guess + xres = 800; yres = 600; + +#ifdef _WIN32 + static DEVMODE dm; + dm.dmSize = sizeof(dm); + EnumDisplaySettings(0, ENUM_CURRENT_SETTINGS, &dm); + xres = dm.dmPelsWidth; + yres = dm.dmPelsHeight; +#endif +} + + +unsigned long tot_mem = 0; +unsigned long avl_mem = 0; + +void get_mem_status() +{ +// Win32 +#ifdef _WIN32 + + MEMORYSTATUS ms; + GlobalMemoryStatus(&ms); + tot_mem = round_up(ms.dwTotalPhys, 1*MB); + // fixes results for my machine - off by 528 KB. why?! + avl_mem = ms.dwAvailPhys; + +// Sys V derived (GNU/Linux, Solaris) +#elif defined(_SC_PAGESIZE) && defined(_SC_AVPHYS_PAGES) + + long page_size = sysconf(_SC_PAGESIZE); + tot_mem = sysconf(_SC_PHYS_PAGES ) * page_size; + avl_mem = sysconf(_SC_AVPHYS_PAGES) * page_size; + +// BSD / Mac OS X +#elif HAVE_SYSCTL && defined(HW_PHYSMEM) + + size_t len = sizeof(tot_mem); + int mib[2] = { CTL_HW, HW_PHYSMEM }; + sysctl(mib, 2, &tot_mem, &len, 0, 0); + mib[1] = HW_USERMEM; + sysctl(mib, 2, &avl_mem, &len, 0, 0); + +#endif +} + + +char gfx_card[512] = ""; // = D3D8 MAX_DEVICE_IDENTIFIER_STRING + +#ifdef D3D8 +#include +#ifdef _MSC_VER +#pragma comment(lib, "d3d8.lib") +#endif +#endif + + +// no-op on non-Win32 systems until OpenGL is initialized. +void get_gfx_card() +{ + // already successfully detected + if(gfx_card[0] != 0) + return; + +#ifdef D3D8 + + IDirect3D8* d3d = Direct3DCreate8(D3D_SDK_VERSION); + D3DADAPTER_IDENTIFIER8 id; + d3d->GetAdapterIdentifier(D3DADAPTER_DEFAULT, D3DENUM_NO_WHQL_LEVEL, &id); + d3d->Release(); + + strcpy(gfx_card, id.Description); + +#else + + char* v = (char*)glGetString(GL_VENDOR); + if(!v) // OpenGL probably not initialized yet + return; + strncpy(gfx_card, v, sizeof(gfx_card)); + if(!strcmp(gfx_card, "ATI Technologies Inc.")) + gfx_card[3] = 0; + if(!strcmp(gfx_card, "NVIDIA Corporation")) + gfx_card[6] = 0; + strcat(gfx_card, (char*)glGetString(GL_RENDERER)); + +#endif +} + + +// +// CPU +// + +char cpu_type[49] = "unknown CPU"; // processor brand string is 48 chars +double cpu_freq = 0.f; + +long cpu_caps = 0; +long cpu_ext_caps = 0; + +// -1 if detect not yet called, or cannot be determined +int cpus = -1; +int is_notebook = -1; + + +#ifdef _M_IX86 + +int has_tsc = -1; + +inline u64 rdtsc() +{ + u64 c; +__asm +{ + cpuid + rdtsc + mov dword ptr [c], eax + mov dword ptr [c+4], edx +} + // 64 bit values are returned in edx:eax, but we do it the safe way + return c; +} + +#endif + + +static void get_cpu_info() +{ +#ifdef _M_IX86 + static char cpu_vendor[13]; + int family, model; + + __asm + { + ; make sure CPUID is supported (size opt.) + pushfd + or byte ptr [esp+2], 32 + popfd + pushfd + pop eax + shr eax, 22 ; bit 21 toggled? + jnc no_cpuid + + ; get vendor string + xor eax, eax + cpuid + mov dword ptr [cpu_vendor], ebx + mov dword ptr [cpu_vendor+4], edx + mov dword ptr [cpu_vendor+8], ecx + ; (already 0 terminated) + + ; get CPU signature and std feature bits + mov eax, 1 + cpuid + mov [cpu_caps], edx + mov edx, eax + shr edx, 4 + and edx, 0x0f + mov [model], edx + shr eax, 8 + and eax, 0x0f + mov [family], eax + + ; make sure CPUID ext functions are supported + mov eax, 0x80000000 + cpuid + cmp eax, 0x80000000 + jbe no_brand_str + + ; get CPU brand string (>= Athlon XP, P4) + mov edi, offset cpu_type + mov esi, -2 ; loop counter: -2 to 0 + $1: lea eax, [0x80000004+esi] + cpuid + stosd + xchg eax, ebx + stosd + xchg eax, ecx + stosd + xchg eax, edx + stosd + inc esi + jle $1 + ; already 0 terminated + + ; get extended feature flags + mov eax, 0x80000001 + cpuid + mov [cpu_ext_caps], edx + } + + // strip (tm) from Athlon string + if(!strncmp(cpu_type, "AMD Athlon(tm)", 14)) + memmove(cpu_type+10, cpu_type+14, 34); + + // fixup Intel's as well + int a, b; // not needed, but sscanf returns # fields actually stored + if(sscanf(cpu_type, "Intel ® Pentium 4 CPU %d.%d GHz", &a, &b) == 2) + strcpy(cpu_type, "Intel Pentium 4"); + + goto have_brand_str; + +no_brand_str: + + // AMD + if(!strcmp(cpu_vendor, "AuthenticAMD")) + { + if(family == 6) + strcpy(cpu_type, (model == 3)? "AMD Duron" : "AMD Athlon"); + } + // Intel + else if(!strcmp(cpu_vendor, "GenuineIntel")) + { + if(family == 6 && model >= 7) + strcpy(cpu_type, "Intel Pentium III / Celeron"); + } + +have_brand_str: + + // calc CPU freq (count clocks in 50 ms) + if(cpu_caps & TSC) + { + u64 clocks1 = rdtsc(); + + // .. wait at at least 50 ms + double t1 = get_time(); + double t2; + do + t2 = get_time(); + while(t2 < t1 + 50e-3); + + u64 clocks2 = rdtsc(); + + // .. freq = (clocks / 50 [ms]) / 50 [ms] * 1000 + // cpuid/rdtsc overhead is negligible + cpu_freq = (__int64)(clocks2-clocks1) / (t2-t1); + // VC6 can't convert u64 -> double, and we don't need full range + } + // don't bother with a WAG timing loop + +no_cpuid: + +#endif // #ifdef _M_IX86 + +#ifdef _WIN32 + HW_PROFILE_INFO hi; + GetCurrentHwProfile(&hi); + is_notebook = !(hi.dwDockInfo & DOCKINFO_DOCKED) ^ + !(hi.dwDockInfo & DOCKINFO_UNDOCKED); + // both flags set <==> this is a desktop machine. + // both clear is unspecified; we assume it's not a notebook. + + SYSTEM_INFO si; + GetSystemInfo(&si); + cpus = si.dwNumberOfProcessors; +#endif +} + + +void detect() +{ + get_mem_status(); + get_gfx_card(); + get_cpu_info(); +} diff --git a/source/detect.h b/source/detect.h new file mode 100755 index 0000000000..b99c432eda --- /dev/null +++ b/source/detect.h @@ -0,0 +1,88 @@ +// system detect +// +// Copyright (c) 2003 Jan Wassenberg +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// Contact info: +// Jan.Wassenberg@stud.uni-karlsruhe.de +// http://www.stud.uni-karlsruhe.de/~urkt/ + +#ifndef __DETECT_H__ +#define __DETECT_H__ + +#include "misc.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +// useful for choosing a video mode. not called by detect(). +// currently not implemented for non-Win32 systems (returns 800x600). +extern void get_cur_resolution(int& xres, int& yres); + + +extern char gfx_card[]; + +// no-op on non-Win32 systems until OpenGL is initialized. +extern void get_gfx_card(); + + +// +// mem +// + +extern unsigned long tot_mem; +extern unsigned long avl_mem; + +// updates *_mem above +extern void get_mem_status(); + + +// +// CPU +// + +extern char cpu_type[]; +extern double cpu_freq; + +enum +{ + TSC = BIT(4), + CMOV = BIT(15), + MMX = BIT(23), + SSE = BIT(25), + SSE2 = BIT(26) +}; + +extern long cpu_caps; + +// define instead of enum to avoid stupid sign conversion warning +#define EXT_3DNOW_PRO BIT(30) +#define EXT_3DNOW BIT(31) + +extern long cpu_ext_caps; + +// -1 if detect not yet called, or cannot be determined +extern int cpus; +extern int is_notebook; + + + +extern void detect(); + + +#ifdef __cplusplus +} +#endif + +#endif // #ifndef __DETECT_H__ diff --git a/source/font.cpp b/source/font.cpp new file mode 100755 index 0000000000..cc5b55d2bf --- /dev/null +++ b/source/font.cpp @@ -0,0 +1,226 @@ +/* + * OpenGL texture font + * + * Copyright (c) 2002 Jan Wassenberg + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * Contact info: + * Jan.Wassenberg@stud.uni-karlsruhe.de + * http://www.stud.uni-karlsruhe.de/~urkt/ + */ + +#include +#include +#include +#include + +#include "font.h" +#include "res.h" +#include "tex.h" +#include "ogl.h" +#include "posix.h" +#include "misc.h" + + +typedef struct +{ + Handle tex; + uint list_base; +} +FONT; + + +#if 0 + +#include +//#include FT_FREETYPE_H + + +static FT_Library lib; + + +static void cleanup(void) +{ + FT_Done_FreeType(lib); +} + + +int build_font(const char* in_ttf, const char* out_fnt, const char* out_raw, int height) +{ + if(!lib) + { + FT_Init_FreeType(&lib); + atexit(cleanup); + } + + FT_Face face; + if(FT_New_Face(lib, in_ttf, 0, &face)) + return -1; + + FT_Set_Pixel_Sizes(face, 0, height); + const int tex_dim = 256; + const int w = 24, h = 24; + + FILE* f = fopen(out_fnt, "w"); + if(!f) + return -1; + fprintf(f, "%s\n%d %d\n", out_raw, w, h); /* header */ + + u8* tex = (u8*)calloc(tex_dim*tex_dim, 2); /* GL_LUMINANCE_ALPHA fmt */ + + int x = 0, y = 0; + + for(int c = 32; c < 128; c++) /* for each (printable) char */ + { + FT_Load_Char(face, c, FT_LOAD_RENDER); + const u8* bmp = face->glyph->bitmap.buffer; + + /* copy glyph's bitmap into texture */ + for(int j = 0; j < face->glyph->bitmap.rows; j++) + { + u8* pos = &tex[(y+h-8-face->glyph->bitmap_top+j)*tex_dim*2 + (x+face->glyph->bitmap_left)*2]; + for(int i = 0; i < face->glyph->bitmap.width; i++) + { + *pos++ = *bmp; /* luminance */ + *pos++ = (*bmp)? 0xff : 0x00; /* alpha */ + bmp++; + } + } + + x += w; + if(x + w >= tex_dim) + x = 0, y += h; + + fprintf(f, "%d ", face->glyph->advance.x / 64); + } + + fclose(f); + + /* write texture */ + f = fopen(out_raw, "wb"); + fwrite(tex, 2, tex_dim*tex_dim, f); + fclose(f); + + free(tex); + + return 0; +} + + +#endif + + +static void font_dtor(HDATA* hd) +{ + FONT* font = (FONT*)hd->internal; + glDeleteLists(font->list_base, 96); +} + + +u32 font_load(const char* fn) +{ + void* p; + size_t size; + HDATA* hd; + u32 h = res_load(fn, RES_FONT, font_dtor, p, size, hd); + if(!h || !hd || !p) + return 0; + + int pos; // current position in the file + const char* file = (const char*)p; + + // read header + char tex_filename[PATH_MAX]; + int x_stride, y_stride; // glyph spacing in texture + if(sscanf(file, "%s\n%d %d\n%n", tex_filename, &x_stride, &y_stride, &pos) != 3) + { + printf("Problem loading \"%s\": header is invalid", fn); + return 0; + } + + // read glyph widths + int adv[128]; + for(int i = 32; i < 128; i++) + { + file += pos; + if(sscanf(file, "%d %n", &adv[i], &pos) != 1) + { + printf("Problem loading \"%s\": glyph width array is invalid", fn); + return 0; + } + } + + // load glyph texture + const Handle tex = tex_load(tex_filename); + if(!tex) + return 0; + tex_upload(tex); + + const int tex_dim = 256; + const float du = (float)x_stride / (float)tex_dim; + float u = 0, v = 0; + + // create a display list for each glyph + const uint list_base = glGenLists(128); + for(int c = 32; c < 128; c++) + { + const float w = (float)adv[c], h = (float)y_stride; // glyph quad width/height + const float tw = w / tex_dim, th = h / tex_dim; // texture space width/height + + glNewList(list_base+c, GL_COMPILE); + glBegin(GL_QUADS); + glTexCoord2f(u, v+th); glVertex2f(0, 0); + glTexCoord2f(u+tw, v+th); glVertex2f(w, 0); + glTexCoord2f(u+tw, v); glVertex2f(w, h); + glTexCoord2f(u, v); glVertex2f(0, h); + glEnd(); + glTranslatef(w, 0, 0); + glEndList(); + + u += du; + if(u + du > 1.f) + u = 0.f, v += th; + } + + FONT* font = (FONT*)hd->internal; + font->tex = tex; + font->list_base = list_base; + + return h; +} + + +int font_bind(const u32 h) +{ + HDATA* hd = h_data(h, RES_FONT); + if(!hd) + return -1; + FONT* font = (FONT*)hd->internal; + + tex_bind(font->tex); + glListBase(font->list_base); + + return 0; +} + + +void glprintf(const char* fmt, ...) +{ + va_list args; + char buf[1024]; buf[1023] = 0; + + va_start(args, fmt); + vsnprintf(buf, sizeof(buf)-1, fmt, args); + va_end(args); + + glCallLists(strlen(buf), GL_UNSIGNED_BYTE, buf); +} diff --git a/source/font.h b/source/font.h new file mode 100755 index 0000000000..17564e977c --- /dev/null +++ b/source/font.h @@ -0,0 +1,79 @@ +// OpenGL texture font +// Copyright (c) 2003 Jan Wassenberg +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// Contact info: +// Jan.Wassenberg@stud.uni-karlsruhe.de +// http://www.stud.uni-karlsruhe.de/~urkt/ + +#ifndef __FONT_H__ +#define __FONT_H__ + +#include "types.h" + +// load and return a handle to the font defined in +extern u32 font_load(const char* fn); + +// use the font referenced by h for all subsequent glprintf() calls +extern int font_bind(u32 h); + +// output text at current OpenGL modelview pos. +// assumes ortho projection with texturing, alpha test, and blending enabled. +// must bind a font before calling! +extern void glprintf(const char* fmt, ...); + + +#endif // #ifndef __FONT_H__ + + + + +/* +EXAMPLE: + +#include "font.h" + + +u32 h; + +void init() +{ + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, xres, 0, yres, -1, 1); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glBlendFunc(GL_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.5); + + h = font_load("font.fnt"); + if(!h) + abort(); +} + + +void render() +{ + font_bind(h); + glprintf("string"); +} + +// FONT FILE FORMAT: +%s // texture file name +%d %d // width/height of glyphs in the texture +%d [...] %d // advance width for chars 32..127 + +*/ diff --git a/source/glext_funcs.h b/source/glext_funcs.h new file mode 100755 index 0000000000..3f7a0b6ad3 --- /dev/null +++ b/source/glext_funcs.h @@ -0,0 +1,25 @@ +/* GL 1.2 */ +FUNC(void, glActiveTexture, (int)) + +/* EXT_swap_control */ +FUNC(int, wglSwapIntervalEXT, (int)) + +/* NV_vertex_array */ +FUNC(void, glVertexArrayRangeNV, (int, void*)) +FUNC(void, glFlushVertexArrayRangeNV, ()) +FUNC(void*, wglAllocateMemoryNV, (int, float, float, float)) +FUNC(void, wglFreeMemoryNV, (void*)) + +/* NV_fence */ +FUNC(void, glGenFencesNV, (int, unsigned int*)) +FUNC(void, glDeleteFencesNV, (int, const unsigned int*)) +FUNC(void, glSetFenceNV, (unsigned int, int)) +FUNC(int, glTestFenceNV, (unsigned int)) +FUNC(void, glFinishFenceNV, (unsigned int)) +FUNC(int, glIsFenceNV, (unsigned int)) +FUNC(void, glGetFenceivNV, (unsigned int, int, int*)) + + + +FUNC(void, glCompressedTexImage2DARB, (int, int, int, unsigned int, unsigned int, int, unsigned int, const void*)) +FUNC(void, glCompressedTexSubImage2DARB, (int, int, int, int, unsigned int, int, int, unsigned int, const void*)) diff --git a/source/input.cpp b/source/input.cpp new file mode 100755 index 0000000000..0e66d24fca --- /dev/null +++ b/source/input.cpp @@ -0,0 +1,164 @@ +/* + * input layer (dispatch events to multiple handlers; record/playback events) + * + * Copyright (c) 2002 Jan Wassenberg + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * Contact info: + * Jan.Wassenberg@stud.uni-karlsruhe.de + * http://www.stud.uni-karlsruhe.de/~urkt/ + */ + +#include +#include + +#include "input.h" + + +#define MAX_HANDLERS 4 + +static IN_HANDLER handler_stack[MAX_HANDLERS]; +static int handler_stack_top = 0; + + +int in_add_handler(IN_HANDLER handler) +{ + if(handler_stack_top >= MAX_HANDLERS || !handler) + return -1; + + handler_stack[handler_stack_top++] = handler; + + return 0; +} + + +/* send event to each handler (newest first) until one returns true */ +static void dispatch_event(const SDL_Event& event) +{ + for(int i = handler_stack_top-1; i >= 0; i--) + if(handler_stack[i](event)) + return; +} + + + +static enum +{ + INIT, /* first call to in_record() or in_playback(): register cleanup routine */ + IDLE, + RECORD, + PLAYBACK +} +state = INIT; + +static FILE* f; + +extern u32 game_ticks; + +static u32 time_adjust = 0; +static u32 next_event_time; + + +void in_stop() +{ + if(f) + { + fclose(f); + f = 0; + } + + state = IDLE; +} + + +int in_record(const char* fn) +{ + if(state == INIT) + atexit(in_stop); + + in_stop(); + + f = fopen(fn, "wb"); + if(!f) + return -1; + + fwrite(&game_ticks, sizeof(u32), 1, f); + + state = RECORD; + + return 0; +} + + +int in_playback(const char* fn) +{ + if(state == INIT) + atexit(in_stop); + + in_stop(); + + f = fopen(fn, "rb"); + if(!f) + return -1; + + u32 rec_start_time; + fread(&rec_start_time, sizeof(u32), 1, f); + time_adjust = game_ticks-rec_start_time; + + fread(&next_event_time, sizeof(u32), 1, f); + next_event_time += time_adjust; + + state = PLAYBACK; + + return 0; +} + + +void in_get_events() +{ + SDL_Event event; + + while(state == PLAYBACK && next_event_time <= game_ticks) + { + fread(&event, sizeof(SDL_Event), 1, f); + + /* + * do this before dispatch_event(), + * in case a handler calls in_stop() (setting f to 0) + */ + if(!fread(&next_event_time, sizeof(u32), 1, f)) +{ + in_stop(); +exit(0x73c07d); +// TODO: 'disconnect'? +} + next_event_time += time_adjust; + + dispatch_event(event); + } + + /* get new events */ + while(SDL_PollEvent(&event)) + { + if(state == RECORD) + { + fwrite(&game_ticks, sizeof(u32), 1, f); + fwrite(&event, sizeof(SDL_Event), 1, f); + } + + if(state == PLAYBACK) + if(event.type == SDL_KEYDOWN) + in_stop(); + + dispatch_event(event); + } +} diff --git a/source/input.h b/source/input.h new file mode 100755 index 0000000000..17daf3e07b --- /dev/null +++ b/source/input.h @@ -0,0 +1,56 @@ +/* + * input layer (dispatch events to multiple handlers; record/playback events) + * + * Copyright (c) 2002 Jan Wassenberg + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * Contact info: + * Jan.Wassenberg@stud.uni-karlsruhe.de + * http://www.stud.uni-karlsruhe.de/~urkt/ + */ + +#ifndef __INPUT_H__ +#define __INPUT_H__ + + +#include "wsdl.h" +#include "types.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +extern u32 game_ticks; + + +typedef bool (*IN_HANDLER)(const SDL_Event& event); + +/* + * register an input handler, which will receive all subsequent events first. + * events are passed to other handlers if handler returns false. + */ +extern int in_add_handler(IN_HANDLER handler); + +extern void in_get_events(); + +extern int in_record(const char* fn); +extern int in_playback(const char* fn); +extern void in_stop(); + + +#ifdef __cplusplus +} +#endif + +#endif /* #ifndef __INPUT_H__ */ diff --git a/source/main.cpp b/source/main.cpp new file mode 100755 index 0000000000..f5f18ccd38 --- /dev/null +++ b/source/main.cpp @@ -0,0 +1,260 @@ +#include +#include +#include + +#include "wsdl.h" +#include "ogl.h" +#include "detect.h" +#include "time.h" +#include "input.h" +#include "misc.h" +#include "posix.h" +#include "font.h" +#include "res.h" +#include "tex.h" +#include "vfs.h" + +u32 game_ticks; + +int xres = 800, yres = 600; + +u32 font; +u32 tex; + + +// error in SDL before OpenGL initialized; +// display error message, and quit +// TODO: localization + +enum SDLError +{ + INIT, + VMODE +}; + +#ifdef _WIN32 +extern "C" { +#define MB_ICONEXCLAMATION 0x30 +__declspec(dllimport) unsigned long __stdcall MessageBoxA(void*, const char*, const char*, unsigned int); +} +#endif + +static void sdl_error(SDLError err) +{ + char msg[1000] = "Problem while setting up OpenGL.\n"\ + "Details: "; + char* pos = msg + strlen(msg); + int rem = sizeof(msg)-1 - (pos-msg); // space remaining in buffer + + if(err == INIT) + snprintf(pos, rem, "SDL library initialization failed (%s)\n", SDL_GetError()); + else if(err == VMODE) + snprintf(pos, rem, "could not set %dx%d graphics mode (%s)\n", xres, yres, SDL_GetError()); + + fprintf(stderr, msg); + +#ifdef _WIN32 + MessageBoxA(0, msg, "0ad", MB_ICONEXCLAMATION); +#endif + + exit(1); +} + + +static int set_vmode(int w, int h, int bpp) +{ + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + + if(!SDL_SetVideoMode(w, h, bpp, SDL_OPENGL|SDL_FULLSCREEN)) + return -1; + + glViewport(0, 0, w, h); + + oglInit(); // required after each mode change + + return 0; +} + + +static bool keys[256]; + +static bool handler(const SDL_Event& ev) +{ + int c; + + switch(ev.type) + { + case SDL_KEYDOWN: + c = ev.key.keysym.sym; + keys[c] = true; + switch(c) + { + case SDLK_ESCAPE: + exit(0); + } + break; + + case SDL_KEYUP: + c = ev.key.keysym.sym; + keys[c] = false; + break; + } + + return 0; +} + + +static void render() +{ +// TODO: not needed with 100% draw coverage + glClear(GL_COLOR_BUFFER_BIT); + + // overlay mode + glPushAttrib(GL_ENABLE_BIT); + glDisable(GL_CULL_FACE); + glBlendFunc(GL_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + glAlphaFunc(GL_GREATER, 0.5); + glEnable(GL_ALPHA_TEST); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho(0., xres, 0., yres, -1., 1.); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + + // logo + glLoadIdentity(); + glTranslatef(50, 100, 0); + tex_bind(tex); + glBegin(GL_QUADS); + +const float u = 0.585f, v = 1.0f; +const float x = 600.0f, y = 512.0f; + glTexCoord2f(0, 0); glVertex2f(0, 0); + glTexCoord2f(u, 0); glVertex2f(x, 0); + glTexCoord2f(u, v); glVertex2f(x, y); + glTexCoord2f(0, v); glVertex2f(0, y); + glEnd(); + + // FPS counter + glLoadIdentity(); + glTranslatef(10, 30, 0); + font_bind(font); + glprintf("%d FPS", fps); + + // restore + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + glPopAttrib(); +} + + +static void do_tick() +{ +} + + +void main(int argc, char* argv[]) +{ + // set 24 bit (float) FPU precision for faster divides / sqrts +#ifdef _M_IX86 + _control87(_PC_24, _MCW_PC); +#endif + + detect(); + + // output system information + struct utsname un; + uname(&un); + freopen("stdout.txt", "w", stdout); + // .. OS + printf("%s %s (%s)\n", un.sysname, un.release, un.version); + // .. CPU + printf("%s, %s", un.machine, cpu_type); + if(cpu_freq != 0.0f) + { + if(cpu_freq < 1e9) + printf(", %.2f MHz\n", cpu_freq*1e-6); + else + printf(", %.2f GHz\n", cpu_freq*1e-9); + } + else + printf("\n"); + // .. memory + printf("%lu MB RAM; %lu MB free\n", tot_mem/MB, avl_mem/MB); + // .. graphics card + printf("%s\n", gfx_card); + // .. network name / ips + printf("%s\n", un.nodename); + char hostname[100]; // possibly nodename != hostname + gethostname(hostname, sizeof(hostname)); + hostent* h = gethostbyname(hostname); + if(h) + { + struct in_addr** ips = (struct in_addr**)h->h_addr_list; + for(int i = 0; ips && ips[i]; i++) + printf("%s ", inet_ntoa(*ips[i])); + printf("\n"); + } + fflush(stdout); + + // init SDL + if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_NOPARACHUTE) < 0) + sdl_error(INIT); + atexit(SDL_Quit); + + // preferred video mode = current desktop settings + // (command line params may override these) + get_cur_resolution(xres, yres); + + for(int a = 1; a < argc; a++) + if(!strncmp(argv[a], "xres", 4)) + xres = atoi(argv[a]+4); + else if(!strncmp(argv[a], "yres", 4)) + yres = atoi(argv[a]+4); + // TODO: other command line options + + if(set_vmode(xres, yres, 32) < 0) + sdl_error(VMODE); + + // call again - needs OpenGL to be initialized on non-Win32 systems +#ifndef _WIN32 + get_gfx_card(); +#endif + + glEnable(GL_TEXTURE_2D); + + vfs_add_search_path("data"); + tex = tex_load("0adlogo1.bmp"); + tex_upload(tex); + font = font_load("verdana.fnt"); + + +in_add_handler(handler); + + +// fixed timestep main loop + const double TICK_TIME = 30e-3; // [s] + double time0 = get_time(); + for(;;) + { +// TODO: limiter in case simulation can't keep up? + double time1 = get_time(); + while((time1-time0) > TICK_TIME) + { + game_ticks++; + in_get_events(); + do_tick(); + time0 += TICK_TIME; + } + + render(); + SDL_GL_SwapBuffers(); + + calc_fps(); + } +} diff --git a/source/mem.cpp b/source/mem.cpp new file mode 100755 index 0000000000..48164d44fb --- /dev/null +++ b/source/mem.cpp @@ -0,0 +1,123 @@ +// malloc layer for less fragmentation, alignment, and automatic release + +#include + +#include + +#include "types.h" +#include "mem.h" +#include "res.h" +#include "misc.h" + + +struct MEM +{ + uint type; + void* org_p; // MEM_HEAP only + size_t size; // MEM_POOL only +}; + + + +static void heap_dtor(HDATA* hd) +{ + MEM* mem = (MEM*)hd->internal; + free(mem->org_p); +} + + +static void* heap_alloc(const size_t size, const int align, MEM& mem) +{ + u8* org_p = (u8*)malloc(size+align-1); + u8* p = (u8*)round_up((long)org_p, align); + + mem.org_p = org_p; + return p; +} + + + +static u8* pool; +static size_t pool_pos; +static const size_t POOL_CAP = 64*MB; // TODO: user editable + + +static void pool_dtor(HDATA* hd) +{ + MEM* mem = (MEM*)hd->internal; + + long pool_ofs = (u8*)hd->p - (u8*)pool; + // not at end of pool + if(pool_ofs + mem->size == pool_pos) + pool_pos -= mem->size; +} + + +static void* pool_alloc(const size_t size, const uint align, MEM& mem) +{ + if(!pool) + pool = (u8*)mem_alloc(size, MEM_HEAP, align); + if(!pool) + return 0; + + size_t ofs = round_up((ulong)pool+pool_pos, align) - (ulong)pool; + if(ofs+size > POOL_CAP) + return 0; + + void* p = (u8*)pool + ofs; + + mem.size = size; + + pool_pos = ofs+size; + return p; +} + + + +static void mem_dtor(HDATA* hd) +{ + MEM* mem = (MEM*)hd->internal; + if(mem->type == MEM_HEAP) + heap_dtor(hd); + else if(mem->type == MEM_POOL) + pool_dtor(hd); +} + + +void* mem_alloc(size_t size, const MemType type, const uint align) +{ + if(size == 0) + size = 1; + + MEM mem; + + void* p; + if(type == MEM_HEAP) + p = heap_alloc(size, align, mem); + else if(type == MEM_POOL) + p = pool_alloc(size, align, mem); + else + return 0; + + HDATA* hd; + Handle h = h_alloc((u32)p, RES_MEM, mem_dtor, hd); + if(!h || !hd) + return 0; + *(MEM*)hd->internal = mem; + + return p; +} + + +int mem_free(void* p) +{ + if(!p) + return 1; + + HDATA* hd; + Handle h = h_find((u32)p, RES_MEM, hd); + if(!h) + return -1; + h_free(h, RES_MEM); + return 0; +} \ No newline at end of file diff --git a/source/mem.h b/source/mem.h new file mode 100755 index 0000000000..e9e3d3a033 --- /dev/null +++ b/source/mem.h @@ -0,0 +1,13 @@ +#ifndef __MEM_H__ +#define __MEM_H__ + +enum MemType +{ + MEM_POOL, + MEM_HEAP +}; + +extern void* mem_alloc(size_t size, MemType type = MEM_HEAP, uint align = 1); +extern int mem_free(void* p); + +#endif // #ifndef __MEM_H__ diff --git a/source/memcpy.cpp b/source/memcpy.cpp new file mode 100755 index 0000000000..cb044c13a8 --- /dev/null +++ b/source/memcpy.cpp @@ -0,0 +1,62 @@ +/* + * block prefetch memcpy for large, uncached arrays + * + * src and len must be multiples of CHUNK_SIZE. + */ +void memcpy_nt(void* dst, void* src, int len) +{ +__asm +{ + push esi + + mov edx, [dst] + mov esi, [src] + mov ecx, [len] + shr ecx, 12 ; # chunks + ; smaller than sub ecx, CHUNK_SIZE below + +main_loop: + +; prefetch: touch each cache line in chunk +; (backwards to prevent hardware prefetches) +; add esi, CHUNK_SIZE +prefetch_loop: + mov eax, [esi-64] + mov eax, [esi-128] + sub esi, 128 + test esi, 4095 ; CHUNK_SIZE-1 (icc doesn't preprocess asm) + jnz prefetch_loop + + +; copy the chunk 64 bytes at a time +write_loop: + movq mm0, [esi] + movq mm1, [esi+8] + movq mm2, [esi+16] + movq mm3, [esi+24] + movq mm4, [esi+32] + movq mm5, [esi+40] + movq mm6, [esi+48] + movq mm7, [esi+56] + add esi, 64 + test esi, 4095 ; CHUNK_SIZE-1 + movntq [edx], mm0 + movntq [edx+8], mm1 + movntq [edx+16], mm2 + movntq [edx+24], mm3 + movntq [edx+32], mm4 + movntq [edx+40], mm5 + movntq [edx+48], mm6 + movntq [edx+56], mm7 + lea edx, [edx+64] ; leave flags intact + jnz write_loop + + dec ecx + jnz main_loop + + sfence + emms + + pop esi +} +} \ No newline at end of file diff --git a/source/misc.cpp b/source/misc.cpp new file mode 100755 index 0000000000..b3b8f64c47 --- /dev/null +++ b/source/misc.cpp @@ -0,0 +1,223 @@ +// miscellany +// +// Copyright (c) 2003 Jan Wassenberg +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// Contact info: +// Jan.Wassenberg@stud.uni-karlsruhe.de +// http://www.stud.uni-karlsruhe.de/~urkt/ + + +#include +#include +#include + +#include "time.h" +#include "misc.h" + + +#include + + + +// FNV1-A +u32 fnv_hash(const char* str, int len) +{ + u32 h = 0; + + while(len--) + { + h ^= *(const u8*)str++; + h *= 0x01000193; + } + + return h; +} + + +#if defined(_M_IX86) && !defined(_WIN32) + +// change FPU control word (used to set precision) +uint _control87(uint new_cw, uint mask) +{ +__asm +{ + push eax + fnstcw [esp] + pop eax ; old_cw + mov ecx, [new_cw] + mov edx, [mask] + and ecx, edx ; new_cw & mask + not edx ; ~mask + and eax, edx ; old_cw & ~mask + or eax, ecx ; (old_cw & ~mask) | (new_cw & mask) + push eax + fldcw [esp] + pop eax +} + + return 0; +} + +#endif + + +u16 bswap16(u16 x) +{ + return (u16)(((x & 0xff) << 8) | (x >> 8)); +} + + +u32 bswap32(u32 x) +{ +#ifdef _M_IX86 + +__asm +{ + mov eax, [x] + bswap eax + mov [x], eax +} + +#else + + u32 t = x; + + for(int i = 0; i < 4; i++) + { + x <<= 8; + x |= t & 0xff; + } + +#endif + return x; +} + + +void bswap32(const u8* data, int cnt) +{ + __asm + { + mov edx, [data] + mov ecx, [cnt] +$loop: dec ecx + js $ret + mov eax, [edx+ecx*4] + bswap eax + mov [edx+ecx*4], eax + jmp $loop +$ret: + } +} + + +bool is_pow2(const int n) +{ + return (n > 0) && !(n & (n-1)); +} + + +// return -1 if not an integral power of 2, +// otherwise the base2 logarithm + +int log2(const int n) +{ +#ifdef _M_IX86 + + __asm + { + mov ecx, [n] + or eax, -1 // return value + lea edx, [ecx-1] + test ecx, edx // power of 2? + jnz $ret + bsf eax, ecx + $ret: + mov [n], eax + } + + return n; + +#else + + if(n || n & (n-1)) + return -1; + + int i = 1, j = 0; + for(; i != n; i += i, j++) + ; + return j; + +#endif +} + + +static int log2(const float x) +{ + u32 i = (u32&)x; + u32 exp = (i >> 23) & 0xff; + return (int)exp - 127; +} + + +long round_up(long val, int multiple) +{ + val += multiple-1; + val -= val % multiple; + return val; +} + + +// replace pathetic libc implementation +#if defined(_M_IX86) && defined(_WIN32) + +double ceil(double f) +{ + double r; + + const float _49 = 0.499999f; + __asm + { + fld [f] + fadd [_49] + frndint + fstp [r] + } + + return r; +} + +#endif + + +// big endian! +void base32(const int len, const u8* in, u8* out) +{ + int bits = 0; + u32 pool = 0; + + static u8 tbl[33] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + + for(int i = 0; i < len; i++) + { + if(bits < 5) + { + pool <<= 8; + pool |= *in++; + bits += 8; + } + + bits -= 5; + int c = (pool >> bits) & 31; + *out++ = tbl[c]; + } +} \ No newline at end of file diff --git a/source/misc.h b/source/misc.h new file mode 100755 index 0000000000..0aa7313ab6 --- /dev/null +++ b/source/misc.h @@ -0,0 +1,121 @@ +// miscellany +// +// Copyright (c) 2003 Jan Wassenberg +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// Contact info: +// Jan.Wassenberg@stud.uni-karlsruhe.de +// http://www.stud.uni-karlsruhe.de/~urkt/ + +#ifndef __MISC_H__ +#define __MISC_H__ + +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +#define UNUSED(param) (void)param; + +#define ONCE(code) { static bool done; if(!done) { code; }; done = true; } + + +const u32 KB = 1 << 10; +const u32 MB = 1 << 20; + + +#ifdef _WIN32 +#define DIR_SEP '\\' +#else +#define DIR_SEP '/' +#endif + + +#ifndef _MCW_PC +#define _MCW_PC 0x0300 // Precision Control +#endif +#ifndef _PC_24 +#define _PC_24 0x0000 // 24 bits +#endif + +extern uint _control87(uint new_cw, uint mask); + + +extern u32 fnv_hash(const char* str, int len); + +#define snprintf _snprintf +#define vsnprintf _vsnprintf + +#define BIT(n) (1ul << (n)) + +#ifndef min +inline int min(int a, int b) +{ + return (a < b)? a : b; +} + +inline int max(int a, int b) +{ + return (a > b)? a : b; +} +#endif + + +extern u16 bswap16(u16); +extern u32 bswap32(u32); + + +static inline u16 read_le16(const void* p) +{ +#ifdef BIG_ENDIAN + const u8* _p = p; + return (u16)_p[0] | (u16)_p[1] << 8; +#else + return *(u16*)p; +#endif +} + + +static inline u32 read_le32(const void* p) +{ +#ifdef BIG_ENDIAN + u32 t = 0; + for(int i = 0; i < 32; i += 8) + t |= (*(const u8*)p++) << i; + + return t; +#else + return *(u32*)p; +#endif +} + + +extern bool is_pow2(int n); + + +// return -1 if not an integral power of 2, +// otherwise the base2 logarithm +extern int log2(const int n); + + +extern long round_up(long val, int multiple); + +extern double ceil(double f); + + +#ifdef __cplusplus +} +#endif + +#endif // #ifndef __MISC_H__ diff --git a/source/ogl.cpp b/source/ogl.cpp new file mode 100755 index 0000000000..652037729e --- /dev/null +++ b/source/ogl.cpp @@ -0,0 +1,103 @@ +#include +#include +#include + +#include "wsdl.h" +#include "ogl.h" + +#ifdef _MSC_VER +#pragma comment(lib, "opengl32.lib") +#endif + + +// define extension function pointers +extern "C" +{ +#define FUNC(ret, name, params) ret (__stdcall *name) params; +#include "glext_funcs.h" +#undef FUNC +} + + +static const char* exts; + + +// check if the extension is supported by the OpenGL implementation +bool oglExtAvail(const char* ext) +{ + assert(exts && "call oglInit before using this function"); + + const char *p = exts, *end; + + // make sure ext is valid & doesn't contain spaces + if(!ext || ext == '\0' || strchr(ext, ' ')) + return false; + + for(;;) + { + p = strstr(p, ext); + if(!p) + return false; // string not found - extension not supported + end = p + strlen(ext); // end of current substring + + // make sure the substring found is an entire extension string, + // i.e. it starts and ends with ' ' + if(p == exts || *(p-1) == ' ') // valid start? + if(*end == ' ' || *end == '\0') // valid end? + return true; + p = end; + } +} + + +void oglPrintErrors() +{ +#define E(e) case e: printf("%s\n", #e); break; + + for(;;) + switch(glGetError()) + { + case GL_NO_ERROR: + return; +E(GL_INVALID_ENUM) +E(GL_INVALID_VALUE) +E(GL_INVALID_OPERATION) +E(GL_STACK_OVERFLOW) +E(GL_STACK_UNDERFLOW) +E(GL_OUT_OF_MEMORY) + } +} + + +int max_tex_size; // [pixels] +int tex_units; +int max_VAR_elements = -1; // GF2: 64K; GF3: 1M +bool tex_compression_avail; // S3TC / DXT{1,3,5} +int video_mem; // [MB]; approximate + +#include "time.h" + +// call after each video mode change +void oglInit() +{ + exts = (const char*)glGetString(GL_EXTENSIONS); + + // import functions +#define FUNC(ret, name, params) *(void**)&name = SDL_GL_GetProcAddress(#name); +#include "glext_funcs.h" +#undef FUNC + + // detect OpenGL / graphics card caps + + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size); + glGetIntegerv(GL_MAX_TEXTURE_UNITS, &tex_units); + // make sure value is -1 if not supported + if(oglExtAvail("GL_NV_vertex_array_range")) + glGetIntegerv(GL_MAX_VERTEX_ARRAY_RANGE_ELEMENT_NV, &max_VAR_elements); + + tex_compression_avail = oglExtAvail("GL_ARB_texture_compression") && + (oglExtAvail("GL_EXT_texture_compression_s3tc") || oglExtAvail("GL_S3_s3tc")); + + video_mem = (SDL_GetVideoInfo()->video_mem) / 1048576; // [MB] + // TODO: add sizeof(FB)? +} diff --git a/source/ogl.h b/source/ogl.h new file mode 100755 index 0000000000..451c0d90a7 --- /dev/null +++ b/source/ogl.h @@ -0,0 +1,65 @@ +#ifndef __OGL_H__ +#define __OGL_H__ + +#ifdef __cplusplus +extern "C" { +#endif + + +// function pointer declarations +#define FUNC(ret, name, params) extern ret (__stdcall *name) params; +#include "glext_funcs.h" +#undef FUNC + + +#ifdef _WIN32 +#ifndef WINGDIAPI +#define WINGDIAPI __declspec(dllimport) +#endif +#ifndef CALLBACK +#define CALLBACK __stdcall +#endif +#ifndef APIENTRY +#define APIENTRY __stdcall +#endif +typedef unsigned short wchar_t; // for glu.h +#endif // #ifndef _WIN32 + + +#ifdef __APPLE__ +#include +#include +#include +#else +#include +#include +#include +#endif + + + +#define GL_TEXTURE_IMAGE_SIZE_ARB 0x86A0 + + +extern int max_tex_size; // [pixels] +extern int tex_units; +extern int max_VAR_elements; // GF2: 64K; GF3: 1M +extern bool tex_compression_avail; // S3TC / DXT{1,3,5} +extern int video_mem; // [MB]; approximate + + +// check if the extension is supported by the OpenGL implementation +extern bool oglExtAvail(const char* ext); + +// print all OpenGL errors +extern void oglPrintErrors(); + +// call before using any of the above, and after each mode change +extern void oglInit(); + + +#ifdef __cplusplus +} +#endif + +#endif // #ifndef __OGL_H__ diff --git a/source/posix.cpp b/source/posix.cpp new file mode 100755 index 0000000000..c3148a9e11 --- /dev/null +++ b/source/posix.cpp @@ -0,0 +1,494 @@ +// POSIX emulation for Win32 +// +// Copyright (c) 2003 Jan Wassenberg +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// Contact info: +// Jan.Wassenberg@stud.uni-karlsruhe.de +// http://www.stud.uni-karlsruhe.de/~urkt/ + +// collection of hacks :P + +#include +#include +#include +#include + +#include + +#include "posix.h" +#include "win.h" +#include "time.h" +#include "misc.h" + + +// VC6 windows.h may not have defined these +#ifndef INVALID_FILE_ATTRIBUTES +#define INVALID_FILE_ATTRIBUTES ((DWORD)-1) +#endif +#ifndef PROCESSOR_ARCHITECTURE_AMD64 +#define PROCESSOR_ARCHITECTURE_AMD64 9 +#endif + +extern "C" +{ +// both hooked to support aio +extern int _open(const char* fn, int mode, ...); +extern int _close(int); + +extern int _get_osfhandle(int); +extern void mainCRTStartup(); +} + + +////////////////////////////////////////////////////////////////////////////// +// +// file +// +////////////////////////////////////////////////////////////////////////////// + + +extern int aio_open(const char*, int, int); +extern int aio_close(int); + + +int open(const char* fn, int mode, ...) +{ + // /dev/tty? => COM? + if(!strncmp(fn, "/dev/tty", 8)) + { + static char port[] = "COM "; + port[3] = (char)(fn[8]+1); + fn = port; + } + + int fd = _open(fn, mode); + + // open it for async I/O as well (_open defaults to deny_none sharing) + if(fd > 2) + aio_open(fn, mode, fd); + + return fd; +} + + +int close(int fd) +{ + aio_close(fd); + return _close(fd); +} + + +int ioctl(int fd, int op, int* data) +{ + HANDLE h = (HANDLE)_get_osfhandle(fd); + + switch(op) + { + case TIOCMGET: + /* TIOCM_* mapped directly to MS_*_ON */ + GetCommModemStatus(h, (DWORD*)data); + break; + + case TIOCMBIS: + /* only RTS supported */ + if(*data & TIOCM_RTS) + EscapeCommFunction(h, SETRTS); + else + EscapeCommFunction(h, CLRRTS); + break; + + case TIOCMIWAIT: + static DWORD mask; + DWORD new_mask = 0; + if(*data & TIOCM_CD) + new_mask |= EV_RLSD; + if(*data & TIOCM_CTS) + new_mask |= EV_CTS; + if(new_mask != mask) + SetCommMask(h, mask = new_mask); + WaitCommEvent(h, &mask, 0); + break; + } + + return 0; +} + + +////////////////////////////////////////////////////////////////////////////// +// +// dir +// +////////////////////////////////////////////////////////////////////////////// + + +char* realpath(const char* fn, char* path) +{ + if(!GetFullPathName(fn, PATH_MAX, path, 0)) + return 0; + return path; +} + + +int mkdir(const char* path, mode_t) +{ + return CreateDirectory(path, 0)? 0 : -1; +} + + +struct _DIR +{ + WIN32_FIND_DATA fd; + HANDLE handle; + struct dirent ent; // must not be overwritten by calls for different dirs + bool not_first; +}; + + +DIR* opendir(const char* name) +{ + DWORD fa = GetFileAttributes(name); + if(fa == INVALID_FILE_ATTRIBUTES || !(fa & FILE_ATTRIBUTE_DIRECTORY)) + return 0; + + _DIR* d = (_DIR*)calloc(sizeof(_DIR), 1); + + char path[MAX_PATH+1]; + strncpy(path, name, MAX_PATH-2); + strcat(path, "\\*"); + d->handle = FindFirstFile(path, &d->fd); + + return d; +} + + +struct dirent* readdir(DIR* dir) +{ + _DIR* d = (_DIR*)dir; + + if(d->not_first) + if(!FindNextFile(d->handle, &d->fd)) + return 0; + d->not_first = true; + + d->ent.d_ino = 0; + d->ent.d_name = &d->fd.cFileName[0]; + return &d->ent; +} + + +int closedir(DIR* dir) +{ + _DIR* d = (_DIR*)dir; + + FindClose(d->handle); + + free(dir); + return 0; +} + + +////////////////////////////////////////////////////////////////////////////// +// +// terminal +// +////////////////////////////////////////////////////////////////////////////// + + +static HANDLE std_h[2] = { (HANDLE)3, (HANDLE)7 }; + + +__declspec(naked) void _get_console() +{ __asm jmp dword ptr [AllocConsole] } + +__declspec(naked) void _hide_console() +{ __asm jmp dword ptr [FreeConsole] } + + +int tcgetattr(int fd, struct termios* termios_p) +{ + if(fd > 2) + return -1; + HANDLE h = std_h[fd]; + + DWORD mode; + GetConsoleMode(h, &mode); + termios_p->c_lflag = mode & (ENABLE_ECHO_INPUT|ENABLE_LINE_INPUT); + + return 0; +} + + +int tcsetattr(int fd, int /* optional_actions */, const struct termios* termios_p) +{ + if(fd > 2) + return -1; + HANDLE h = std_h[fd]; + SetConsoleMode(h, (DWORD)termios_p->c_lflag); + FlushConsoleInputBuffer(h); + + return 0; +} + + +int poll(struct pollfd /* fds */[], int /* nfds */, int /* timeout */) +{ + return -1; +} + + +////////////////////////////////////////////////////////////////////////////// +// +// thread +// +////////////////////////////////////////////////////////////////////////////// + + +__declspec(naked) pthread_t pthread_self(void) +{ __asm jmp dword ptr [GetCurrentThread] } + + +int pthread_setschedparam(pthread_t thread, int policy, const struct sched_param* param) +{ + if(policy == SCHED_FIFO) + SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS); + + HANDLE hThread = (HANDLE)thread; + + SetThreadPriority(hThread, param->sched_priority); + return 0; +} + + +int pthread_create(pthread_t* /* thread */, const void* /* attr */, void*(* func)(void*), void* arg) +{ + /* can't use asm 'cause _beginthread might be a func ptr (with libc) */ + return (int)_beginthread((void(*)(void*))func, 0, arg); +} + + +////////////////////////////////////////////////////////////////////////////// +// +// time +// +////////////////////////////////////////////////////////////////////////////// + + +int clock_gettime(clockid_t clock_id, struct timespec* tp) +{ + static double start_t = -1.0; + static time_t start_s; + + if(start_t < 0.0) + { + start_s = time(0); + start_t = get_time(); + } + + if(clock_id != CLOCK_REALTIME) + { +// errno = EINVAL; + return -1; + } + + double t = get_time(); + double dt = t-start_t; + start_t = t; + + int ds = (int)floor(dt); + int dn = (int)floor((dt-ds) * 1.0e9); + + tp->tv_sec = start_s + ds; + tp->tv_nsec = 0 + dn; + + return 0; +} + + +int nanosleep(const struct timespec* rqtp, struct timespec* /* rmtp */) +{ + int ms = (int)rqtp->tv_sec * 1000 + rqtp->tv_nsec / 1000000; + if(ms > 0) + Sleep(ms); + else + { + struct timespec t1, t2; + clock_gettime(CLOCK_REALTIME, &t1); + int d_ns; + do + { + clock_gettime(CLOCK_REALTIME, &t2); + d_ns = (int)(t2.tv_sec-t1.tv_sec)*1000000000 + (t2.tv_nsec-t1.tv_nsec); + } + while(d_ns < rqtp->tv_nsec); + } + + return 0; +} + + +uint sleep(uint sec) +{ + Sleep(sec * 1000); + + return sec; +} + + +////////////////////////////////////////////////////////////////////////////// +// +// memory mapping +// +////////////////////////////////////////////////////////////////////////////// + + +void* mmap(void* start, unsigned int len, int prot, int flags, int fd, long offset) +{ + if(!(flags & MAP_FIXED)) + start = 0; + + /* interpret protection/mapping flags. */ + SECURITY_ATTRIBUTES sec = { sizeof(SECURITY_ATTRIBUTES), 0, 0 }; + DWORD flProtect = PAGE_READONLY; /* mapping object permission */ + DWORD dwAccess = FILE_MAP_READ; /* file map access permission */ + if(prot & PROT_WRITE) + { + flProtect = PAGE_READWRITE; + + /* changes are shared & written to file */ + if(flags & MAP_SHARED) + { + sec.bInheritHandle = 1; + dwAccess = FILE_MAP_ALL_ACCESS; + } + /* private copy on write mapping */ + else if(flags & MAP_PRIVATE) + { + flProtect = PAGE_WRITECOPY; + dwAccess = FILE_MAP_COPY; + } + } + + DWORD len_hi = (u32)((u64)len >> 32), len_lo = (u32)len & 0xffffffff; + + HANDLE hFile = (HANDLE)_get_osfhandle(fd); + HANDLE hMap = CreateFileMapping(hFile, &sec, flProtect, len_hi, len_lo, 0); + + void* ptr = MapViewOfFileEx(hMap, dwAccess, len_hi, offset, len_lo, start); + + /* file mapping object will be freed when ptr is unmapped */ + CloseHandle(hMap); + + if(!ptr || (flags & MAP_FIXED && ptr != start)) + return MAP_FAILED; + + return ptr; +} + + +int munmap(void* start, unsigned int /* len */) +{ + return UnmapViewOfFile(start) - 1; /* 0: success; -1: fail */ +} + + +int uname(struct utsname* un) +{ + if(!un) + return -1; + + static OSVERSIONINFO vi; + vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&vi); + + // OS implementation name + const char* family = "??"; + int ver = (vi.dwMajorVersion << 8) | vi.dwMinorVersion; + if(vi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) + family = (ver == 0x045a)? "ME" : "9x"; + if(vi.dwPlatformId == VER_PLATFORM_WIN32_NT) + { + if(ver == 0x0500) + family = "2k"; + else if(ver == 0x0501) + family = "XP"; + else + family = "NT"; + } + sprintf(un->sysname, "Win%s", family); + + // release info + const char* vs = vi.szCSDVersion; + int sp; + if(sscanf(vs, "Service Pack %d", &sp) == 1) + sprintf(un->release, "SP %d", sp); + else + { + const char* release = ""; + if(vi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) + { + if(!strcmp(vs, " C")) + release = "OSR2"; + else if(!strcmp(vs, " A")) + release = "SE"; + } + strcpy(un->release, release); + } + + // version + sprintf(un->version, "%lu.%02lu.%lu", vi.dwMajorVersion, vi.dwMinorVersion, vi.dwBuildNumber & 0xffff); + + // node name + u32 buf_size = sizeof(un->nodename); + GetComputerName(un->nodename, &buf_size); + + // hardware type + static SYSTEM_INFO si; + GetSystemInfo(&si); + + if(si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) + strcpy(un->machine, "AMD64"); + else + strcpy(un->machine, "x86"); + + return 0; +} + + + + +#ifndef NO_WINSOCK +#ifdef _MSC_VER +#pragma comment(lib, "wsock32.lib") +#endif +extern "C" { +IMP(int, WSAStartup, (WORD, char*)) +} +#endif + + +extern void entry(void); + +void entry(void) +{ +// alloc __pio, __iob? +// replace CRT init? + +// header also removed to prevent calling Winsock functions + +#ifndef NO_WINSOCK + char d[1024]; + WSAStartup(0x0101, d); +#endif + + mainCRTStartup(); +} diff --git a/source/posix.h b/source/posix.h new file mode 100755 index 0000000000..70218e0cc4 --- /dev/null +++ b/source/posix.h @@ -0,0 +1,437 @@ +// POSIX emulation for Win32 +// +// Copyright (c) 2003 Jan Wassenberg +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// Contact info: +// Jan.Wassenberg@stud.uni-karlsruhe.de +// http://www.stud.uni-karlsruhe.de/~urkt/ + +#ifndef __POSIX_H__ +#define __POSIX_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +#define IMP(ret, name, param) extern __declspec(dllimport) ret __stdcall name param; + + + +// +// +// + +typedef unsigned short u16_t; + + +// +// +// + +typedef int ssize_t; +typedef unsigned int size_t; + + +// +// +// + +#define PATH_MAX 260 + + +// +// +// + +enum +{ +EINPROGRESS = 1000, // Operation in progress. +ENOMEM // Not enough space. +/* +EACCES, // Permission denied. +EADDRINUSE, // Address in use. +EADDRNOTAVAIL, // Address not available. +EAGAIN, // Resource unavailable, try again (may be the same value as EWOULDBLOCK]). +EALREADY, // Connection already in progress. +EBADF, // Bad file descriptor. +ECANCELED, // Operation canceled. +ECONNABORTED, // Connection aborted. +ECONNREFUSED, // Connection refused. +ECONNRESET, // Connection reset. +EDOM, // Mathematics argument out of domain of IMPtion. +EEXIST, // File exists. +EFAULT, // Bad address. +EHOSTUNREACH, // Host is unreachable. +EINTR, // Interrupted IMPtion. +EINVAL, // Invalid argument. +EISCONN, // Socket is connected. +EISDIR, // Is a directory. +ENAMETOOLONG, // Filename too long. +ENETDOWN, // Network is down. +ENETRESET, // Connection aborted by network. +ENETUNREACH, // Network unreachable. +ENOENT, // No such file or directory. +ENOEXEC, // Executable file format error. + +ENOSPC, // No space left on device. +ENOSYS, // IMPtion not supported. +ENOTCONN, // The socket is not connected. +ENOTDIR, // Not a directory. +ENOTEMPTY, // Directory not empty. +ENOTSOCK, // Not a socket. +ENOTSUP, // Not supported. +EOVERFLOW, // Value too large to be stored in data type. +EPERM, // Operation not permitted. +EPIPE, // Broken pipe. +EPROTO, // Protocol error. +ERANGE, // Result too large. +ETIMEDOUT, // Connection timed out. +EWOULDBLOCK // Operation would block (may be the same value as EAGAIN]). +*/ +}; + + +// +// +// + +typedef long time_t; + +typedef enum +{ + CLOCK_REALTIME +} +clockid_t; + +struct timespec +{ + time_t tv_sec; + long tv_nsec; +}; + +extern time_t time(time_t*); +extern int clock_gettime(clockid_t clock_id, struct timespec* tp); +extern int nanosleep(const struct timespec* rqtp, struct timespec* rmtp); + + +// +// sys/stat.h +// + +typedef unsigned short ino_t; +typedef unsigned int mode_t; +typedef long off_t; +typedef unsigned int dev_t; + +// struct stat defined in VC sys/stat.h + +#define stat _stat + +extern int mkdir(const char*, mode_t); + + +// +// dirent.h +// + +typedef void DIR; + +struct dirent +{ + ino_t d_ino; + char* d_name; +}; + +extern DIR* opendir(const char* name); +extern struct dirent* readdir(DIR*); +extern int closedir(DIR*); + + +// +// +// + +#define PROT_READ 0x01 // page can be read +#define PROT_WRITE 0x02 // page can be written + +#define MAP_SHARED 0x01 // share changes across processes +#define MAP_PRIVATE 0x02 // private copy on write mapping +#define MAP_FIXED 0x04 + +#define MAP_FAILED 0 + +extern void* mmap(void* start, unsigned int len, int prot, int flags, int fd, long offset); +extern int munmap(void* start, unsigned int len); + + +// +// +// + +// values from MS _open - do not change! +#define O_RDONLY 0x0000 // open for reading only +#define O_WRONLY 0x0001 // open for writing only +#define O_RDWR 0x0002 // open for reading and writing +#define O_APPEND 0x0008 // writes done at eof +#define O_CREAT 0x0100 // create and open file +#define O_TRUNC 0x0200 // open and truncate +#define O_EXCL 0x0400 // open only if file doesn't already exist +#define O_BINARY 0x8000 // file mode is binary (untranslated) + +#define O_NONBLOCK 0x1000000 + +extern int open(const char* fn, int mode, ...); + + +// +// +// + +#define F_OK 0 +#define R_OK 1 +#define W_OK 2 +#define X_OK 4 + +#define read _read +#define write _write + +extern int close(int); +extern int access(const char*, int); +extern int chdir(const char*); + +extern unsigned int sleep(unsigned int sec); +IMP(int, gethostname, (char* name, size_t namelen)) + + + +// +// +// + +extern char* realpath(const char*, char*); + + +// +// +// + +union sigval +{ + int sival_int; // Integer signal value. + void* sival_ptr; // Pointer signal value. +}; + +struct sigevent +{ + int sigev_notify; // notification mode + int sigev_signo; // signal number + union sigval sigev_value; // signal value +}; + + +// +// +// + +#define TCSANOW 0 + +struct termios +{ + long c_lflag; +}; + +#define ICANON 2 // do not change - correspond to ENABLE_LINE_INPUT / ENABLE_ECHO_INPUT +#define ECHO 4 + +extern int tcgetattr(int fd, struct termios* termios_p); +extern int tcsetattr(int fd, int optional_actions, const struct termios* termios_p); + + +// +// +// + +struct sched_param +{ + int sched_priority; +}; + +enum +{ + SCHED_RR, + SCHED_FIFO, + SCHED_OTHER +}; + +#define sched_get_priority_max(policy) 15 // TIME_CRITICAL +#define sched_get_priority_min(policy) -15 // IDLE + + +// +// +// + +typedef unsigned int pthread_t; + +extern pthread_t pthread_self(); +extern int pthread_setschedparam(pthread_t thread, int policy, const struct sched_param* param); +extern int pthread_create(pthread_t* thread, const void* attr, void*(*IMP)(void*), void* arg); + + +// +// +// + +typedef unsigned long socklen_t; +typedef unsigned short sa_family_t; + +// Win32 values - do not change +#define SOCK_STREAM 1 +#define SOCK_DGRAM 2 +#define AF_INET 2 + +struct sockaddr; + + +// +// +// + +typedef unsigned long in_addr_t; +typedef unsigned short in_port_t; + +struct in_addr +{ + in_addr_t s_addr; +}; + +struct sockaddr_in +{ + sa_family_t sin_family; + in_port_t sin_port; + struct in_addr sin_addr; + unsigned char sin_zero[8]; +}; + +#define INADDR_ANY 0 + + +// +// +// + +struct hostent +{ + char* h_name; // Official name of the host. + char** h_aliases; // A pointer to an array of pointers to + // alternative host names, terminated by a + // null pointer. + short h_addrtype; // Address type. + short h_length; // The length, in bytes, of the address. + char** h_addr_list; // A pointer to an array of pointers to network + // addresses (in network byte order) for the host, + // terminated by a null pointer. +}; + +IMP(struct hostent*, gethostbyname, (const char *name)) + + +// +// +// + +extern u16_t htons(u16_t hostshort); +#define ntohs htons + +IMP(in_addr_t, inet_addr, (const char*)) +IMP(char*, inet_ntoa, (in_addr)) +IMP(int, accept, (int, struct sockaddr*, socklen_t*)) +IMP(int, bind, (int, const struct sockaddr*, socklen_t)) +IMP(int, connect, (int, const struct sockaddr*, socklen_t)) +IMP(int, listen, (int, int)) +IMP(ssize_t, recv, (int, void*, size_t, int)) +IMP(ssize_t, send, (int, const void*, size_t, int)) + +IMP(int, socket, (int, int, int)) + + +// +// +// + +struct pollfd +{ + int fd; + short int events, revents; +}; + +#define POLLIN 1 + +extern int poll(struct pollfd[], int, int); + + +// +// +// + +struct utsname +{ + char sysname[9]; // Name of this implementation of the operating system. + char nodename[16]; // Name of this node within an implementation-defined + // communications network. + // Win9x requires this minimum buffer size. + char release[9]; // Current release level of this implementation. + char version[16]; // Current version level of this release. + char machine[9]; // Name of the hardware type on which the system is running. +}; + +extern int uname(struct utsname*); + + +// +// serial port IOCTL +// + +// use with TIOCMBIS +#define TIOCM_RTS 1 + +// use with TIOCMGET or TIOCMIWAIT +#define TIOCM_CD 0x80 // MS_RLSD_ON +#define TIOCM_CTS 0x10 // MS_CTS_ON + +enum +{ + TIOCMBIS, // set control line + TIOCMGET, // get line state + TIOCMIWAIT // wait for status change +}; + +extern int ioctl(int fd, int op, int* data); + + +extern void _get_console(); +extern void _hide_console(); + + +#include "posix/aio.h" + + +#ifdef __cplusplus +} +#endif + + +#endif // #ifndef __POSIX_H__ diff --git a/source/posix/aio.cpp b/source/posix/aio.cpp new file mode 100755 index 0000000000..9e489ffdbf --- /dev/null +++ b/source/posix/aio.cpp @@ -0,0 +1,392 @@ +// POSIX asynchronous I/O +// +// Copyright (c) 2003 Jan Wassenberg +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// Contact info: +// Jan.Wassenberg@stud.uni-karlsruhe.de +// http://www.stud.uni-karlsruhe.de/~urkt/ + +#include +#include + +#include "posix.h" +#include "win.h" +#include "misc.h" +#include "types.h" + + +// Win32 functions require sector aligned transfers. +// updated by aio_open; changes don't affect aio_return +static u32 sector_size = 4096; + +// async-capable handles to each lowio file +static HANDLE* aio_hs; +static uint hs_cap; + +// information about active transfers (reused) +struct Req +{ + // used to identify this request; != 0 <==> request valid + aiocb* cb; + + OVERLAPPED ovl; + // hEvent signals when transfer complete + + // read into a separate align buffer if necessary + // (note: unaligned writes aren't supported. see aio_rw) + u32 pad; // offset from starting sector + void* buf; // reused; resize if too small + u32 buf_size; +}; + +static const int MAX_REQS = 4; +static Req reqs[MAX_REQS]; + +static HANDLE open_mutex, reqs_mutex; +#define LOCK(what) WaitForSingleObject(what##_mutex, INFINITE); +#define UNLOCK(what) ReleaseMutex(what##_mutex); + + +// get async capable handle to file +// current implementation: open both versions of the file on open() +// wastes 1 handle/file, but we don't have to remember the filename/mode +static HANDLE aio_h(int fd) +{ + if((unsigned)fd >= hs_cap) + return INVALID_HANDLE_VALUE; + + return aio_hs[fd]; +} + + +// find request slot currently in use by cb +// cb = 0 => search for empty slot +static Req* find_req(const aiocb* cb) +{ + Req* r = reqs; + for(int i = 0; i < MAX_REQS; i++, r++) + if(r->cb == cb) + return r; + + return 0; +} + + +static void cleanup(void) +{ + uint i; + + // close files + for(i = 0; i < hs_cap; i++) + { + HANDLE h = aio_h(i); + if(h != INVALID_HANDLE_VALUE) + CloseHandle(h); + } + free(aio_hs); + aio_hs = 0; + hs_cap = 0; + + // free requests + Req* r = reqs; + for(i = 0; i < MAX_REQS; i++, r++) + { + r->cb = 0; + + CloseHandle(r->ovl.hEvent); + r->ovl.hEvent = INVALID_HANDLE_VALUE; + + free(r->buf); + r->buf = 0; + } + + CloseHandle(open_mutex); + CloseHandle(reqs_mutex); +} + + +// called by first aio_open +static void init() +{ + for(int i = 0; i < MAX_REQS; i++) + reqs[i].ovl.hEvent = CreateEvent(0,1,0,0); // manual reset + + open_mutex = CreateMutex(0,0,0); + reqs_mutex = CreateMutex(0,0,0); + + atexit(cleanup); +} + + +int aio_close(int fd) +{ + HANDLE h = aio_h(fd); + if(h == INVALID_HANDLE_VALUE) // out of bounds or already closed + return -1; + + CloseHandle(h); + aio_hs[fd] = INVALID_HANDLE_VALUE; + return 0; +} + + +// open fn in async mode; associate with fd (retrieve via aio_h(fd)) +int aio_open(const char* fn, int mode, int fd) +{ + +LOCK(open) + + ONCE(init()) + + // alloc aio_hs entry + if((unsigned)fd >= hs_cap) + { + uint hs_cap2 = fd+4; + HANDLE* aio_hs2 = (HANDLE*)realloc(aio_hs, hs_cap2*sizeof(HANDLE)); + if(!aio_hs2) + { + UNLOCK(open) + return -1; + } + + for(uint i = hs_cap; i < hs_cap2; i++) + aio_hs2[i] = INVALID_HANDLE_VALUE; + aio_hs = aio_hs2; + hs_cap = hs_cap2; + } + +UNLOCK(open) + + // interpret mode + u32 access = GENERIC_READ; // assume O_RDONLY + u32 share = 0; + if(mode & O_WRONLY) + access = GENERIC_WRITE; + else if(mode & O_RDWR) + access = GENERIC_READ|GENERIC_WRITE; + else + share = FILE_SHARE_READ; + u32 create = OPEN_EXISTING; + if(mode & O_CREAT) + create = (mode & O_EXCL)? CREATE_NEW : CREATE_ALWAYS; + u32 flags = FILE_FLAG_OVERLAPPED|FILE_FLAG_NO_BUFFERING|FILE_FLAG_SEQUENTIAL_SCAN; + + // open file, store in aio_hs array + aio_hs[fd] = CreateFile(fn, access, share, 0, create, flags, 0); + if(aio_hs[fd] == INVALID_HANDLE_VALUE) + return -1; + + // check drive's sector size (Win32 requires alignment) + char path[PATH_MAX]; + realpath(fn, path); + path[3] = 0; // cut off after ?:\\ + u32 spc, nfc, tnc; // don't need these + u32 sector_size2; + GetDiskFreeSpace(path, &spc, §or_size2, &nfc, &tnc); + +LOCK(open) + + if(sector_size < sector_size2) + sector_size = sector_size2; + +UNLOCK(open) + + return 0; +} + + +// called by aio_read, aio_write, and lio_listio +// cb->aio_lio_opcode specifies desired operation +static int aio_rw(struct aiocb* cb) +{ + if(!cb) + return -1; + if(cb->aio_lio_opcode == LIO_NOP) + return 0; + + HANDLE h = aio_h(cb->aio_fildes); + if(h == INVALID_HANDLE_VALUE) + return -1; + +LOCK(reqs) + + // find free request slot + Req* r = find_req(0); + if(!r) + { + UNLOCK(reqs) + return -1; + } + r->cb = cb; + +UNLOCK(reqs) + + // align + r->pad = cb->aio_offset % sector_size; // offset to start of sector + const u32 ofs = cb->aio_offset - r->pad; + const u32 size = round_up((long)cb->aio_nbytes+r->pad, sector_size); + void* buf = cb->aio_buf; + if(r->pad || (long)buf % sector_size) + { + // current align buffer is too small - resize + if(r->buf_size < size) + { + void* buf2 = realloc(r->buf, size); + if(!buf2) + return -1; + r->buf = buf2; + r->buf_size = size; + } + + // unaligned writes are not supported - + // we'd have to read padding, then write our data. ugh. + if(cb->aio_lio_opcode == LIO_WRITE) + return -1; + + buf = r->buf; + } + + r->ovl.Offset = ofs; + u32 status = (cb->aio_lio_opcode == LIO_READ)? + ReadFile(h, buf, size, 0, &r->ovl) : WriteFile(h, buf, size, 0, &r->ovl); + + if(status || GetLastError() == ERROR_IO_PENDING) + return 0; + return -1; +} + + +int aio_read(struct aiocb* cb) +{ + cb->aio_lio_opcode = LIO_READ; + return aio_rw(cb); +} + + +int aio_write(struct aiocb* cb) +{ + cb->aio_lio_opcode = LIO_WRITE; + return aio_rw(cb); +} + + +int lio_listio(int mode, struct aiocb* const cbs[], int n, struct sigevent* se) +{ + UNUSED(se) + + for(int i = 0; i < n; i++) + aio_rw(cbs[i]); // aio_rw checks for 0 param + + if(mode == LIO_WAIT) + aio_suspend(cbs, n, 0); + + return 0; +} + + +// return status of transfer +int aio_error(const struct aiocb* cb) +{ + Req* const r = find_req(cb); + if(!r) + return -1; + + switch(r->ovl.Internal) // I/O status + { + case 0: + return 0; + case STATUS_PENDING: + return -EINPROGRESS; + + // TODO: errors + default: + return -1; + } +} + + +// get bytes transferred. call exactly once for each op. +ssize_t aio_return(struct aiocb* cb) +{ + Req* const r = find_req(cb); + if(!r) + return -1; + + assert(r->ovl.Internal == 0 && "aio_return with transfer in progress"); + + // read wasn't aligned - need to copy to user's buffer + if(r->pad || (long)cb->aio_buf % sector_size) + memcpy(cb->aio_buf, (u8*)r->buf + r->pad, cb->aio_nbytes); + + // free this request slot + r->cb = 0; + + return (ssize_t)cb->aio_nbytes; +} + + +int aio_cancel(int fd, struct aiocb* cb) +{ + UNUSED(cb) + + const HANDLE h = aio_h(fd); + if(h == INVALID_HANDLE_VALUE) + return -1; + + // Win32 limitation: can't cancel single transfers + CancelIo(h); + return AIO_CANCELED; +} + + +int aio_fsync(int, struct aiocb*) +{ + return -1; +} + + +int aio_suspend(const struct aiocb* const cbs[], int n, const struct timespec* ts) +{ + int cnt = 0; // actual number of valid cbs + HANDLE* hs = (HANDLE*)malloc(n*sizeof(HANDLE)); + if(!hs) + return -1; + for(int i = 0; i < n; i++) + { + // ignore NULL list entries + if(!cbs[i]) + continue; + + Req* r = find_req(cbs[i]); + if(r) + hs[cnt++] = r->ovl.hEvent; + } + + // timeout: convert timespec to ms (NULL ptr -> no timeout) + u32 timeout = INFINITE; + if(ts) + timeout = ts->tv_sec*1000 + ts->tv_nsec/1000000; + + u32 status = WaitForMultipleObjects(cnt, hs, 0, timeout); + + free(hs); + + if(status == WAIT_TIMEOUT) + { + //errno = -EAGAIN; + return -1; + } + else if(status == WAIT_FAILED) + return -1; + return 0; +} diff --git a/source/posix/aio.h b/source/posix/aio.h new file mode 100755 index 0000000000..7de3a39c93 --- /dev/null +++ b/source/posix/aio.h @@ -0,0 +1,63 @@ +// POSIX asynchronous I/O +// +// Copyright (c) 2003 Jan Wassenberg +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// Contact info: +// Jan.Wassenberg@stud.uni-karlsruhe.de +// http://www.stud.uni-karlsruhe.de/~urkt/ + +// included by posix.h + +// Note: for maximum efficiency, transfer buffers, offsets, and lengths +// should be sector aligned (otherwise, buffer is copied). + + +struct aiocb +{ + int aio_fildes; // File descriptor. + off_t aio_offset; // File offset. + void* aio_buf; // Location of buffer. + size_t aio_nbytes; // Length of transfer. + int aio_reqprio; // Request priority offset. + struct sigevent aio_sigevent; // Signal number and value. + int aio_lio_opcode; // Operation to be performed. +}; + +enum +{ + // aio_cancel return + AIO_ALLDONE, // None of the requested operations could be canceled since they are already complete. + AIO_CANCELED, // All requested operations have been canceled. + AIO_NOTCANCELED, // Some of the requested operations could not be canceled since they are in progress. + + // lio_listio mode + LIO_WAIT, // wait until all I/O is complete + LIO_NOWAIT, + + // lio_listio ops + LIO_NOP, + LIO_READ, + LIO_WRITE +}; + +extern int aio_cancel(int, struct aiocb*); +extern int aio_error(const struct aiocb*); +extern int aio_fsync(int, struct aiocb*); +extern int aio_read(struct aiocb*); +extern ssize_t aio_return(struct aiocb*); +extern int aio_suspend(const struct aiocb* const[], int, const struct timespec*); +extern int aio_write(struct aiocb*); +extern int lio_listio(int, struct aiocb* const[], int, struct sigevent*); + +extern int aio_close(int fd); +extern int aio_open(const char* fn, int mode, int fd); diff --git a/source/ps/BaseEntity.h b/source/ps/BaseEntity.h new file mode 100755 index 0000000000..18ca59523c --- /dev/null +++ b/source/ps/BaseEntity.h @@ -0,0 +1,211 @@ +// base entity for all the Units and Buildings in the game 0AD + +// written by Jacob Ricketts + +#if !defined( __0AD_BASE_ENTITY_H_ ) +#define __0AD_BASE_ENTITY_H_ + +#include "ai_common.h" +#include +#include + +class DLLExport CBaseEntity +{ +public: + CBaseEntity( void ) { Clear(); }; + virtual ~CBaseEntity( void ) { }; + + virtual void Clear( void ) { ClearBaseEntity(); }; + //virtual bool Update( void ) = 0; + //virtual bool HandleMessage( const Message &msg ) = 0; + + // === ID FUNCTIONS === + inline + USHORT GetID( void ) { return( m_nID ); }; + + inline + USHORT GetTeamID( void ) { return( m_byteTeamID ); }; + + inline + BYTE AddAllies( BYTE bitflagTeams ) { return( m_bitflagAllies |= bitflagTeams ); }; + + inline + BYTE RemoveAllies( BYTE bitflagTeams ) { return( m_bitflagAllies &= ~bitflagTeams ); }; + + + // === HIT POINT FUNCTIONS === + inline + virtual USHORT GetHitPoints( void ) { std::cout << "CBaseEntity.GetHitPoints() called..." << endl; return( m_nHitPoints ); }; + + inline + virtual USHORT GetBaseHitPoints( void ) { return( m_nBaseHitPoints ); }; + + inline + virtual USHORT GetMaxHitPoints( void ) { return( m_nMaxHitPoints ); }; + + inline + virtual short GetRegenRate( void ) { return( m_nBaseRegenRate ); }; + + + // === ATTACK STAT FUNCTIONS === + inline + virtual USHORT GetAttackStrength( void ) { return( m_nBaseAttackStrength ); }; + + inline + virtual USHORT GetAttackRate( void ) { return( m_nBaseAttackRate ); }; + + inline + virtual USHORT GetAreaOfEffect( void ) { return( m_nBaseAreaOfEffect ); }; + + inline + virtual USHORT GetMaxRange( void ) { return( m_nBaseMaxRange ); }; + + inline + virtual USHORT GetMinRange( void ) { return( m_nBaseMinRange ); }; + + inline + virtual USHORT GetMeleeArmor( void ) { return( m_nBaseMeleeArmor ); } ; + + inline + virtual USHORT GetPierceArmor( void ) { return( m_nBasePierceArmor ); }; + + + // === MISC ATTRIBUTES === + inline + virtual const std::vector& AvailableToCivs( void ) { return( m_vAvailableToCivs ); }; + + inline + virtual const Vector3D& GetPosition( void ) { return( m_vPos ); }; + + inline + virtual bool BoostsMorale( void ) { return( m_bBoostsMorale ); }; + + inline + virtual bool DemoralizesEnemies( void ) { return( m_bDemoralizesEnemies ); }; + + inline + virtual USHORT GetActionRate( void ) { return( m_nBaseActionRate ); }; + + inline + virtual USHORT GetAreaOfInfluence( void ) { return( m_nBaseAreaOfInfluence ); }; + + inline + virtual USHORT GetLOS( void ) { return( m_nBaseLOS ); }; + + + // === BUILD STATS === + inline + virtual USHORT GetBuildTime( void ) { return( m_nBuildTime ); }; + + inline + virtual USHORT GetReqFoodToBuild( void ) { return( m_nFoodReqToBuild ); }; + + inline + virtual USHORT GetReqGoldToBuild( void ) { return( m_nGoldReqToBuild ); }; + + inline + virtual USHORT GetReqMetalToBuild( void ) { return( m_nMetalReqToBuild ); }; + + inline + virtual USHORT GetReqStoneToBuild( void ) { return( m_nStoneReqToBuild ); }; + + inline + virtual USHORT GetReqWoodToBuild( void ) { return( m_nWoodReqToBuild ); }; + + inline + virtual const std::vector& GetBuildingsReqToBuild( void ) { return( m_vBuildingsReqToBuild ); }; + + inline + virtual const std::vector& GetTechsReqToBuild( void ) { return( m_vTechsReqToBuild ); }; + + + // === UPGRADE STATS === + inline + virtual USHORT UpgradesTo( void ) { return( m_nUpgradesTo ); }; + + inline + virtual USHORT GetUpgradeTime( void ) { return( m_nUpgradeTime ); }; + + inline + virtual USHORT GetReqFoodToUpgrade( void ) { return( m_nFoodReqToUpgrade ); }; + + inline + virtual USHORT GetReqGoldToUpgrade( void ) { return( m_nGoldReqToUpgrade ); }; + + inline + virtual USHORT GetReqMetalToUpgrade( void ) { return( m_nMetalReqToUpgrade ); }; + + inline + virtual USHORT GetReqStoneToUpgrade( void ) { return( m_nStoneReqToUpgrade ); }; + + inline + virtual USHORT GetReqWoodToUpgrade( void ) { return( m_nWoodReqToUpgrade ); }; + + inline + virtual const std::vector& GetBuildingsReqToUpgrade( void ) { return( m_vBuildingsReqToUpgrade ); }; + + inline + virtual const std::vector& GetTechsReqToUpgrade( void ) { return( m_vTechsReqToUpgrade ); }; + + BYTE m_nCurrentFrame; + +protected: + void ClearBaseEntity( void ); + + USHORT m_nID; // ID for this entity + BYTE m_byteTeamID; // ID of the team this entity is on + BYTE m_bitflagAllies; // bitflag to contain the list of allies + + + std::vector m_vAvailableToCivs; // a list of Civs that can create this entity + + // === ATTRIBUTES === + USHORT m_nHitPoints; // current amount of hit points + USHORT m_nBaseHitPoints; // initial hit points + USHORT m_nMaxHitPoints; // maximum hit points + short m_nBaseRegenRate; // rate of health regeneation + + USHORT m_nBaseAttackStrength; // base strength + USHORT m_nBaseAttackRate; // rate of attack + USHORT m_nBaseMinRange; // minimum range of attack + USHORT m_nBaseMaxRange; // maximum range of attack + USHORT m_nBaseActionRate; // rate of actions + USHORT m_nBaseAreaOfEffect; // area of effect + + USHORT m_nBaseAreaOfInfluence; // area of influence + + USHORT m_nBaseMeleeArmor; // armor rating against melee attacks + USHORT m_nBasePierceArmor; // armor rating against projectile attacks + + USHORT m_nBaseLOS; // line of sight distance + + USHORT m_nBuildTime; // time required to build + USHORT m_nUpgradeTime; // time required to upgrade + + USHORT m_nFoodReqToBuild; // amount of food required to build + USHORT m_nGoldReqToBuild; // amount of gold required to build + USHORT m_nMetalReqToBuild; // amount of metal required to build + USHORT m_nStoneReqToBuild; // amount of stone required to build + USHORT m_nWoodReqToBuild; // amount of wood required to build + std::vector m_vBuildingsReqToBuild; // buildings required to build this entity + std::vector m_vTechsReqToBuild; // techs required to build this entity + + USHORT m_nFoodReqToUpgrade; // cost to upgrade (food) + USHORT m_nGoldReqToUpgrade; // cost to upgrade (gold) + USHORT m_nMetalReqToUpgrade; // cost to upgrade (metal) + USHORT m_nStoneReqToUpgrade; // cost to upgrade (stone) + USHORT m_nWoodReqToUpgrade; // cost to upgrade (wood) + std::vector m_vBuildingsReqToUpgrade; // buildings required to upgrade this entity + std::vector m_vTechsReqToUpgrade; // techs required to upgrade this entity + + USHORT m_nUpgradesTo; // ID of entity this unit will upgrade to if possible + + bool m_bBoostsMorale; // does this entity boost Morale? + bool m_bDemoralizesEnemies; // does this entity demoralize enemy units? + + Vector3D m_vPos; // position on the map + +private: +}; + +#endif \ No newline at end of file diff --git a/source/ps/CStr.h b/source/ps/CStr.h new file mode 100755 index 0000000000..ffa3229fea --- /dev/null +++ b/source/ps/CStr.h @@ -0,0 +1,155 @@ +/* +CStr.h +by Caecus +Caecus@0ad.wildfiregames.com + +Overview: + + Contains CStr class which is a versatile class for making string use easy. + Basic functionality implemented via STL string, so performance is limited + based on the STL implementation we use. + +Example: + + CStr MyString = _T("Hello, World."); + _int MyNum = 126; + MyString += _T(" I'm the number ") += CStr(MyNumber); + + // Prints "Hello, World. I'm the number 126" + _tcout << (LPCTSTR)MyString << endl; + + MyString = _("2341"); + MyNum = MyString.ToInt(); + + // Prints "2341" + _tcout << MyNum << endl; + +More Info: + http://wildfiregames.com/0ad/codepit/TDD/CStr.html + +*/ + +#ifndef CSTR_H +#define CSTR_H + +#include "Prometheus.h" +#include // Used for basic string functionality +#include +#include +#include // TCHAR of course +#include +using namespace std; + +// DEFINES/ENUMS +#define CONVERSION_BUFFER_SIZE 32 +#define FLOAT_CONVERSION _T("%.6f") + +#ifdef _UNICODE +typedef wstring tstring; +#define _tcout wcout +#define _tstod wcstod +#else +typedef string tstring; +#define _tcout cout +#define _tstod strtod +#endif + +enum PS_TRIM_MODE {PS_TRIM_LEFT, PS_TRIM_RIGHT, PS_TRIM_BOTH}; + + +// CStr class, the mother of all strings +class CStr +{ +public: + + // CONSTRUCTORS + CStr(); // Default constructor + CStr(const CStr &Str); // Copy Constructor + + CStr(tstring String); // Creates CStr from C++ string + CStr(LPCTSTR String); // Creates CStr from C-Style TCHAR string + CStr(TCHAR Char); // Creates CStr from a TCHAR + CStr(_int Number); // Creates CStr from a int + CStr(_uint Number); // Creates CStr from a uint + CStr(_long Number); // Creates CStr from a long + CStr(_ulong Number); // Creates CStr from a ulong + CStr(_float Number); // Creates CStr from a float + CStr(_double Number); // Creates CStr from a double + ~CStr(); // Destructor + + // Conversions + _int ToInt(); + _uint ToUInt(); + _long ToLong(); + _ulong ToULong(); + _float ToFloat(); + _double ToDouble(); + + _long Length(){return m_String.length();} + // Retrieves the substring within the string + CStr GetSubstring(_long start, _long len); + + //Search the string for another string + _long Find(const CStr &Str); + + //You can also do a "ReverseFind"- i.e. search starting from the end + _long ReverseFind(const CStr &Str); + + // Lowercase and uppercase + CStr LowerCase(); + CStr UpperCase(); + + // Lazy funcs + CStr LCase(); + CStr UCase(); + + // Retreive the substring of the first n characters + CStr Left(_long len); + + // Retreive the substring of the last n characters + CStr Right(_long len); + + //Remove all occurences of some character or substring + void Remove(const CStr &Str); + + //Replace all occurences of some substring by another + void Replace(const CStr &StrToReplace, const CStr &ReplaceWith); + + // Returns a trimed string, removes whitespace from the left/right/both + CStr Trim(PS_TRIM_MODE Mode); + + // Overload operations + CStr &operator=(const CStr &Str); + CStr &operator=(LPCTSTR String); + CStr &operator=(TCHAR Char); + CStr &operator=(_int Number); + CStr &operator=(_long Number); + CStr &operator=(_uint Number); + CStr &operator=(_ulong Number); + CStr &operator=(_float Number); + CStr &operator=(_double Number); + + _bool operator==(const CStr &Str) const; + _bool operator!=(const CStr &Str) const; + _bool operator<(const CStr &Str) const; + _bool operator<=(const CStr &Str) const; + _bool operator>(const CStr &Str) const; + _bool operator>=(const CStr &Str) const; + CStr &operator+=(CStr &Str); + CStr operator+(CStr &Str); + operator LPCTSTR(); + TCHAR &operator[](_int n); + TCHAR &operator[](_uint n); + TCHAR &operator[](_long n); + TCHAR &operator[](_ulong n); + + +private: + tstring m_String; + TCHAR m_ConversionBuffer[CONVERSION_BUFFER_SIZE]; +}; + +// overloaded operator for ostreams +ostream &operator<<(ostream &os, CStr &Str); + +#endif diff --git a/source/ps/Encryption.cpp b/source/ps/Encryption.cpp new file mode 100755 index 0000000000..ceaab105b7 --- /dev/null +++ b/source/ps/Encryption.cpp @@ -0,0 +1,86 @@ +// last modified Friday, May 09, 2003 + +#include "Encryption.h" + +//-------------------------------------------------------------------- +// EncryptData - Takes A pointer to data in memory, the length of that data, +// Along with a key in memory and its length. It will allocated space +// for the encrypted copy of the data via new[], It is the responsiblity of the user +// to call delete[]. +//-------------------------------------------------------------------- +_byte *EncryptData(_byte *Data, _long DataLength, _byte *Key, _long KeyLength) +{ + // Allocate space for new Encrypted data + _byte *NewData = new _byte[DataLength]; + + // A counter to hold our absolute position in data + _long DataOffset = 0; + + // Loop through Data until end is reached + while (DataOffset < DataLength) + { + // Loop through the key + for (_long KeyOffset = 0; KeyOffset < KeyLength; KeyOffset++) + { + // If were at end of data break and the the while loop should end as well. + if (DataOffset >= DataLength) + break; + + // Otherwise Add the previous element of the key from the newdata + // (just something a little extra to confuse the hackers) + if (KeyOffset > 0) // Don't mess with the first byte + NewData[DataOffset] += Key[KeyOffset - 1]; + + // Xor the Data byte with the key byte and get new data + NewData[DataOffset] = Data[DataOffset] ^ Key[KeyOffset]; + + // Increase position in data + DataOffset++; + } + } + + // return the new data + return NewData; +} + + +//-------------------------------------------------------------------- +// DecryptData - Takes A pointer to data in memory, the length of that data, +// Along with a key in memory and its length. It will allocated space +// for the decrypted copy of the data via new[], It is the responsiblity of the user +// to call delete[]. +//-------------------------------------------------------------------- +_byte *DecryptData(_byte *Data, _long DataLength, _byte *Key, _long KeyLength) +{ + // Allocate space for new Decrypted data + _byte *NewData = new _byte[DataLength]; + + // A counter to hold our absolute position in data + _long DataOffset = 0; + + // Loop through Data until end is reached + while (DataOffset < DataLength) + { + // Loop through the key + for (_long KeyOffset = 0; KeyOffset < KeyLength; KeyOffset++) + { + // If were at end of data break and the the while loop should end as well. + if (DataOffset >= DataLength) + break; + + // Otherwise Xor the Data byte with the key byte and get new data + NewData[DataOffset] = Data[DataOffset] ^ Key[KeyOffset]; + + // Subtract the previous element of the key from the newdata + // (just something a little extra to confuse the hackers) + if (KeyOffset > 0) // Don't mess with the first byte + NewData[DataOffset] -= Key[KeyOffset - 1]; + + // Increase position in data + DataOffset++; + } + } + + // return the new data + return NewData; +} diff --git a/source/ps/Encryption.h b/source/ps/Encryption.h new file mode 100755 index 0000000000..80916a1902 --- /dev/null +++ b/source/ps/Encryption.h @@ -0,0 +1,30 @@ +/* +Encryption.h +by Caecus +caecus18@hotmail.com + +Two simple functions for encrypting and decrypting data. The KeyLength is +specified in bytes, therefore Keys must be in multiples of 8 bits. I'd +advice using 128bit keys which you can define as such. + +_byte MyKey[16] = { _byte(0xAF), _byte(0x2B), _byte(0x80), _byte(0x7E), + _byte(0x09), _byte(0x23), _byte(0xCC), _byte(0x95), + _byte(0xB4), _byte(0x2D), _byte(0xF4), _byte(0x90), + _byte(0xB3), _byte(0xC4), _byte(0x2A), _byte(0x3B) }; + +There may be a better way to do this but this looks alright to me. +*/ + + +#include "Prometheus.h" + +#ifndef ENCRYPTION_H +#define ENCRYPTION_H + +// Simple Encryption function +_byte *EncryptData(_byte *Data, _long DataLength, _byte *Key, _long KeyLength); + +// Simple Decryption function +_byte *DecryptData(_byte *Data, _long DataLength, _byte *Key, _long KeyLength); + +#endif \ No newline at end of file diff --git a/source/ps/Error.h b/source/ps/Error.h new file mode 100755 index 0000000000..1ccd6ec066 --- /dev/null +++ b/source/ps/Error.h @@ -0,0 +1,157 @@ +////////////////////////////////////////////////////////////////////////////// +// AUTHOR: Michael Reiland +// FILENAME: Error.h +// PURPOSE: Provides a simple Error Mechanism +// +// USEAGE: +// SetError_Long(PS_FAIL,"A Sample Error",__FILE__,__LINE__); +// SetError_Short(PS_FAIL,"A Sample Error"); +// TestError(PS_FAIL); +// +// TODO: Figure out how we're going to clean up and exit upon failure of +// SetError() +// +// MODIFIED: 07.20.2003 mreiland + +#ifndef _ERROR_H_ +#define _ERROR_H_ + + +#include "Singleton.h" +#include "Prometheus.h" + +#include + + +// Used to specify Null pointers. +const int NullPtr = 0; + + +/****************************************************************************/ +// USER DEFINED TYPES // +/****************************************************************************/ + + +////////////////////////////////////////////////////////////////////////////// +// +// NAME: CError +// PURPOSE: Encapsulation of the data for our error framework. +// +class CError +{ +public: + + PS_RESULT ErrorName; // Actual name of the error + std::string Description; // Description of the error + std::string FileName; // File where the error occured + unsigned int LineNumber; // Line where the file occured + + CError() + :ErrorName(NullPtr), LineNumber(-1){;} +}; +////////////////////////////////////////////////////////////////////////////// + + + +////////////////////////////////////////////////////////////////////////////// +// +// NAME: ErrorMechanism +// PURPOSE: Provides the interface for the Error mechanism we are using. It's +// implemented as a singleton so that only one framework is in +// existence at one time. +// +class ErrorMechanism : Singleton +{ + public: + + inline bool ErrorMechanism_Error(); + inline void ErrorMechanism_ClearError(); + inline bool ErrorMechanism_TestError( PS_RESULT); + + inline void ErrorMechanism_SetError( + PS_RESULT, + std::string, + std::string, + unsigned int); + + inline CError ErrorMechanism_GetError(); + +private: + CError ErrorVar; + +}; +////////////////////////////////////////////////////////////////////////////// + + + + +/******************************************************************************/ +// FUNCTION DEFINITIONS // +/******************************************************************************/ + + + + +////////////////////////////////////////////////////////////////////////////// +// +// Tests if an error exists. If so, returns true +// +bool ErrorMechanism::ErrorMechanism_Error() +{ + return ( ErrorVar.ErrorName != NullPtr ); +} + + +////////////////////////////////////////////////////////////////////////////// +// +// The error has been dealt with, and needs to be cleared. +// +void ErrorMechanism::ErrorMechanism_ClearError() +{ + ErrorVar.ErrorName = NullPtr; +} + + +////////////////////////////////////////////////////////////////////////////// +// +// Checks if the error passed in is the error that's occurred +// +bool ErrorMechanism::ErrorMechanism_TestError( PS_RESULT TErrorName) +{ + return ( ErrorVar.ErrorName == TErrorName ); +} + + +////////////////////////////////////////////////////////////////////////////// +// +// Set's an error that's just occured. +// +void ErrorMechanism::ErrorMechanism_SetError( PS_RESULT TErrorName, std::string TDescription, + std::string TFileName, unsigned int TLineNumber) +{ + + if( ErrorMechanism::ErrorVar.ErrorName == NullPtr ) + { + ErrorVar.ErrorName = TErrorName; + ErrorVar.Description = TDescription; + ErrorVar.FileName = TFileName; + ErrorVar.LineNumber = TLineNumber; + } + else + { + // We need to clean up properly and exit. + exit(EXIT_FAILURE); + } +} + + +////////////////////////////////////////////////////////////////////////////// +// +// Getter for the error +// +CError ErrorMechanism::ErrorMechanism_GetError() +{ + return ErrorVar; +} + +#endif diff --git a/source/ps/LogFile.cpp b/source/ps/LogFile.cpp new file mode 100755 index 0000000000..ed3df1733b --- /dev/null +++ b/source/ps/LogFile.cpp @@ -0,0 +1,355 @@ +// last modified Thursday, May 08, 2003 + +#include "LogFile.h" + +//------------------------------------------------- +//Add a hyperlink to Link. +//------------------------------------------------- +PS_RESULT CLogFile::AddLink(string LinkText,string Link, string Colour) +{ + if(m_IsFileOpen) + { + m_TheFile << "" << LinkText << "
"; + } + else + { + return PS_FAIL; + } + + return PS_OK; +} + +//------------------------------------------------- +//Standard destructor. +//------------------------------------------------- +CLogFile::~CLogFile() +{ + if(m_IsFileOpen) + { + m_TheFile << ""; + m_TheFile.close(); + if(m_HasFrame) + { + m_ErrorFile.close(); + } + m_IsFileOpen = false; + } +} + +//------------------------------------------------- +//Standard constructor. +//------------------------------------------------- +CLogFile::CLogFile() +{ + m_CurrentColour = "Black"; + m_IsFileOpen = false; +} + +//------------------------------------------------- +// Constructor that opens a file. +// 3rd parameter determines whether the error frame is shown. +//------------------------------------------------- +CLogFile::CLogFile(string FileName, string PageTitle, _bool WithFrame) +{ + + Open(FileName,PageTitle,WithFrame); + m_IsFileOpen = true; +} + + +//------------------------------------------------- +//Closes the open files, if open. +//------------------------------------------------- +PS_RESULT CLogFile::Close() +{ + if(m_IsFileOpen) + { + m_CurrentColour = "Black"; + m_TheFile << ""; + m_TheFile.close(); + if(m_HasFrame) + { + m_ErrorFile.close(); + } + m_IsFileOpen = false; + } + else + { + return PS_FAIL; + } + return PS_OK; +} + + +//------------------------------------------------- +//Inserts a horzontal divide in the page. +//------------------------------------------------- +PS_RESULT CLogFile::InsertDivide() +{ + if(m_IsFileOpen) + { + m_TheFile << "
"; + } + else + { + return PS_FAIL; + } + return PS_OK; +} + + +//------------------------------------------------- +//Opens an error file. If WithFrame is true then it constructs a framed page. +//------------------------------------------------- +PS_RESULT CLogFile::Open(string FileName, string PageTitle, _bool WithFrame) +{ + if(m_IsFileOpen) + return PS_FAIL; + + m_HasFrame=false; + m_CurrentColour = "Black"; + //If there are no frames then create a normal page. + if(!WithFrame) + { + FileName = FileName + ".html"; + m_TheFile.open(FileName.c_str()); + m_TheFile << "" << PageTitle << ""; + } + else + { + //Open a temporary file to write the frameset. + ofstream TempStream; + string TempString; + TempString = FileName; + TempString = FileName + ".html"; + TempStream.open(TempString.c_str()); + FileName = FileName + "b.html"; + + //Write the proper details to the frame file. + TempStream << "" << PageTitle << ""; + TempStream << ""; + TempStream << ""; + TempStream << ""; + TempStream.close(); + + //Open the two pages to be displayed within the frames. + + m_TheFile.open(FileName.c_str()); + m_ErrorFile.open("ErrorLinks.html"); + m_FileName = FileName.c_str(); + + //Start writing the two pages that will be displayed. + m_TheFile << "" << PageTitle << ""; + m_ErrorFile << "" << PageTitle << ""; + m_HasFrame = true; + m_ErrNo = 0; + } + + m_IsFileOpen = true; + + return PS_OK; +} + + +//------------------------------------------------- +//Sets the current alignment. +//------------------------------------------------- +PS_RESULT CLogFile::SetAlignment(PS_TEXT_ALIGN Align) +{ + if(m_IsFileOpen) + { + switch(Align){ + + case PS_ALIGN_LEFT: + m_TheFile << "

"; + break; + case PS_ALIGN_CENTER: + m_TheFile << "

"; + break; + case PS_ALIGN_RIGHT: + m_TheFile << "

"; + break; + } + } + else + { + return PS_FAIL; + } + return PS_OK; +} + +//------------------------------------------------- +//Changes the current colour. +//------------------------------------------------- +PS_RESULT CLogFile::SetColour(string ColourName) +{ + if(m_IsFileOpen) + { + m_CurrentColour = ColourName; + m_TheFile << ""; + } + else + { + return PS_FAIL; + } + return PS_OK; +} + +//------------------------------------------------- +//Writes an error, always in red. +//------------------------------------------------- +PS_RESULT CLogFile::WriteError(string Text, PS_DISPLAY_SETTINGS displayOptions) +{ + if(m_IsFileOpen) + { + //If there is a frame then add an anchor and link to the appropriate places. + if(m_HasFrame) + { + m_TheFile << ""; + m_ErrorFile << "Error: " << m_ErrNo << "
"; + m_ErrNo++; + } + m_TheFile << "
" << Line(displayOptions) << Text << Date(displayOptions) << "
"; + } + else + { + return PS_FAIL; + } + return PS_OK; +} + + +PS_RESULT CLogFile::WriteError(string Text, PS_TEXT_ALIGN Align, PS_DISPLAY_SETTINGS displayOptions) +{ + if(m_IsFileOpen) + { + SetAlignment(Align); + //If there is a frame then add an anchor and link to the appropriate places. + if(m_HasFrame) + { + m_TheFile << ""; + m_ErrorFile << "Error: " << m_ErrNo << "
"; + m_ErrNo++; + } + m_TheFile << "
" << Line(displayOptions) << Text << Date(displayOptions) << "


"; + + } + else + { + return PS_FAIL; + } + return PS_OK; +} + + +//------------------------------------------------- +//Writes a header in larger text. +//------------------------------------------------- +PS_RESULT CLogFile::WriteHeader(string Text, PS_DISPLAY_SETTINGS displayOptions) +{ + + if(m_IsFileOpen) + { + m_TheFile << "
" + << Line(displayOptions) << Text << Date(displayOptions) << "
"; + } + else + { + return PS_FAIL; + } + return PS_OK; +} + + +//------------------------------------------------- +//Writes a header in larger text, with alignment. +//------------------------------------------------- +PS_RESULT CLogFile::WriteHeader(string Text, PS_TEXT_ALIGN Align, PS_DISPLAY_SETTINGS displayOptions) +{ + if(m_IsFileOpen) + { + SetAlignment(Align); + m_TheFile << "
" + << Line(displayOptions) << Text << Date(displayOptions) << "


"; + } + else + { + return PS_FAIL; + } + return PS_OK; +} + + +//------------------------------------------------- +//Write a normal string. +//------------------------------------------------- +PS_RESULT CLogFile::WriteText(string Text, PS_DISPLAY_SETTINGS displayOptions) +{ + if(m_IsFileOpen) + { + m_TheFile << Line(displayOptions) << Text << Date(displayOptions) << "
"; + } + else + { + return PS_FAIL; + } + return PS_OK; +} + + +//------------------------------------------------- +//Write a normal string, with alignment. +//------------------------------------------------- +PS_RESULT CLogFile::WriteText(string Text, PS_TEXT_ALIGN Align, PS_DISPLAY_SETTINGS displayOptions) +{ + if(m_IsFileOpen) + { + SetAlignment(Align); + m_TheFile << Line(displayOptions) << Text << Date(displayOptions) << "


"; + } + else + { + return PS_FAIL; + } + return PS_OK; +} + + +//------------------------------------------------- +//Retrieve a string to display the line number +//------------------------------------------------- +string CLogFile::Line(const PS_DISPLAY_SETTINGS &options) +{ + string lineText; + + if (options.displayMode == PS_DISPLAY_MODE_SHOW_LINE_NUMBER ) + { + lineText = options.file; + + char temp[8]; + itoa(options.line, temp, 10); + lineText += ", Line "; + lineText += temp; + lineText += ": "; + } + + return lineText; +} + +//------------------------------------------------- +//Retrieve a string to display the date +//------------------------------------------------- +string CLogFile::Date(const PS_DISPLAY_SETTINGS &options) +{ + string dateText; + + if (options.displayMode == PS_DISPLAY_MODE_SHOW_DATE ) + { + dateText = "   ("; + dateText += options.date; + dateText += ")"; + } + + return dateText; +} \ No newline at end of file diff --git a/source/ps/LogFile.h b/source/ps/LogFile.h new file mode 100755 index 0000000000..5daf089d17 --- /dev/null +++ b/source/ps/LogFile.h @@ -0,0 +1,155 @@ +/* +Log File Writer. +by Mark Ellis +mark@markdellis.co.uk + +--Overview-- + Writes specified output to a formatted html file. + +--Usage-- + First open a file for writing using either the constructor + or the Open() function. Then use the formatting functions to + write to the HTML file. You may use Close() but the destructor + will handle all cleanup of the class. + + You can enable a frame at the top of the page that will link to the errors, + this is enabled by passing true as the 3rd parameter when opening. + +--More Info-- + + http://wildfiregames.com/0ad/codepit/tdd/logfile.html + +*/ + +#ifndef LOGFILE_H +#define LOGFILE_H + +//-------------------------------------------------------- +// Includes / Compiler directives +//-------------------------------------------------------- + +#include "Prometheus.h" +#include +#include + +using std::string; +using std::ofstream; + + + +//-------------------------------------------------------- +// Macros +//-------------------------------------------------------- + +// Extra parameters for displaying text +#define PS_SHOW_LINE_NUMBER PS_DISPLAY_SETTINGS ( __LINE__, __FILE__, __DATE__, PS_DISPLAY_MODE_SHOW_LINE_NUMBER ) +#define PS_SHOW_DATE PS_DISPLAY_SETTINGS ( __LINE__, __FILE__, __DATE__, PS_DISPLAY_MODE_SHOW_DATE ) +#define PS_NONE PS_DISPLAY_SETTINGS ( __LINE__, __FILE__, __DATE__, PS_DISPLAY_MODE_NONE ) + + + +//------------------------------------------------- +// Types +//------------------------------------------------- + +enum PS_DISPLAY_MODE { PS_DISPLAY_MODE_SHOW_LINE_NUMBER, + PS_DISPLAY_MODE_SHOW_DATE, + PS_DISPLAY_MODE_NONE }; + + +enum PS_TEXT_ALIGN { PS_ALIGN_LEFT, PS_ALIGN_CENTER, PS_ALIGN_RIGHT }; + + + +//------------------------------------------------- +// Declarations +//------------------------------------------------- + +class PS_DISPLAY_SETTINGS +{ +public: + + PS_DISPLAY_SETTINGS(_int Line, _char *File, _char *Date, PS_DISPLAY_MODE DisplayMode) + { + line=Line; + file=File; + date=Date; + displayMode=DisplayMode; + } + + _int line; + _char *file; + _char *date; + PS_DISPLAY_MODE displayMode; +}; + + + + +class CLogFile +{ + +public: + + //Standard Constructor and destructor. + CLogFile(); + ~CLogFile(); + CLogFile(string FileName, string PageTitle, _bool WithFrame=false); + + //Opens a file for output the 3rd parameter can be set to true to enable the error frame. + PS_RESULT Open(string FileName, string PageTitle, _bool WithFrame=false); + + //Closes the file. + PS_RESULT Close(); + + + //The following functions edit the html file. + + //Writes a header to the file. + PS_RESULT WriteHeader(string Text, PS_DISPLAY_SETTINGS displayOptions=PS_NONE); + + //Writes a header to the file. Align can be 0=left, 1=centre, 2=left + PS_RESULT WriteHeader(string Text, PS_TEXT_ALIGN Align, PS_DISPLAY_SETTINGS displayOptions=PS_NONE); + + //Writes some text to the file. + PS_RESULT WriteText(string Text, PS_DISPLAY_SETTINGS displayOptions=PS_NONE); + + //Writes some text at a specified alignment. + PS_RESULT WriteText(string Text, PS_TEXT_ALIGN Align, PS_DISPLAY_SETTINGS displayOptions=PS_NONE); + + //Writes an error - in red. + PS_RESULT WriteError(string Text, PS_DISPLAY_SETTINGS displayOptions=PS_NONE); + + //Writes an aligned error. + PS_RESULT WriteError(string Text, PS_TEXT_ALIGN Align, PS_DISPLAY_SETTINGS displayOptions=PS_NONE); + + //Inserts a page break. + PS_RESULT InsertDivide(); + + //Sets the current colour to colourname. + PS_RESULT SetColour(string ColourName); + + //Adds a hyperlink. + PS_RESULT AddLink(string LinkText, string Link, string Colour); + +private: + _bool m_IsFileOpen; //Is the file open. + _bool m_HasFrame; //Have frames been enabled. + ofstream m_TheFile; //The main file. + ofstream m_ErrorFile; //The error link file, used only for frames. + string m_CurrentColour; //The current colour. + string m_FileName; //The name of the main file. + _int m_ErrNo; //The error number. + + //Sets the current alignment of the text. + PS_RESULT SetAlignment(PS_TEXT_ALIGN Align); + + //Writes the current line of text + string Line(const PS_DISPLAY_SETTINGS &options); + + //Writes today's date + string Date(const PS_DISPLAY_SETTINGS &options); +}; + + +#endif \ No newline at end of file diff --git a/source/ps/MathUtil.cpp b/source/ps/MathUtil.cpp new file mode 100755 index 0000000000..1e1208df80 --- /dev/null +++ b/source/ps/MathUtil.cpp @@ -0,0 +1,246 @@ +// last modified Thursday, May 08, 2003 + +#include +#include + +#include "MathUtil.h" + + +// MathUtil Errors +DEFINE_ERROR(ERRONEOUS_BOUND_ERROR, "Lower Bound is >= Upper Bound"); + + + +////////////////////////////////////////////////////////////////////// +// NAME: CompareFloat +// PURPOSE: Returns true if two floating point numbers are within +// FL_FP_TOLERANCE of each other. +// +_bool MathUtil::CompareFloat(const _double &num1, const _double &num2) +{ + if( Abs(num1 - num2) < FL_FP_TOLERANCE ) + return true; + else + return false; +} + + +////////////////////////////////////////////////////////////////////// +// NAME: RadiansToDegrees +// PURPOSE: Converts from Radians to Degrees +// +inline _double MathUtil::RadiansToDegrees(const _double &num) +{ + return num*(PI/180); +} + + +////////////////////////////////////////////////////////////////////// +// NAME: RadiansToDegrees +// PURPOSE: Converts from Degrees to Radians +// +inline _double MathUtil::DegreesToRadians(const _double &num) +{ + return (num*180)/PI; +} + +/* +////////////////////////////////////////////////////////////////////// +// NAME: Random +// PURPOSE: returns a random floating point number between lowerBound +// and upperBound +// NOTES: returns -1 if lowerBound >= upperBound +// +_float MathUtil::Random(const _float &lowerBound, const _float &upperBound) +{ + + if( lowerBound >= upperBound) + return -1; + else + { + // seed generator with current time + srand( static_cast( time(NULL) ) ); + + // finds a floating point number between 0 and 1.0 + _float randVar = ( static_cast<_float>( rand() )/RAND_MAX ); + + // maps the number onto the set from 0 to upperBound + randVar *= Abs(lowerBound - upperBound ) + 1; + + //translate to the proper range + randVar += lowerBound; + + return randVar; + } +} + + +////////////////////////////////////////////////////////////////////// +// NAME: Random +// PURPOSE: returns a random number between lowerBound and upperBound +// NOTES: returns -1 if lowerBound >= upperBound +// +_int MathUtil::Random(const _int &lowerBound,const _int &upperBound) +{ + if( lowerBound >= upperBound) + return -1; + else + { + // seed generator with current time + srand( static_cast( time(NULL) ) ); + + // find a random variable between 0 and range size + _int randVar = rand()%( Abs(upperBound - lowerBound) + 1); + + // translate to proper range + randVar += lowerBound; + + return randVar; + } +} +*/ + +////////////////////////////////////////////////////////// +// NAME: Round +// PURPOSE: Rounds a number. +// NOTES: Round rounds to the nearest representable number +// float version. +// +_int MathUtil::Round(const float &num) +{ + if( num > 0 ) + return static_cast<_int>(num + .5); + else if (num < 0 ) + return static_cast<_int>(num - .5); + else + return 0; +} + + +////////////////////////////////////////////////////////// +// NAME: Round +// PURPOSE: Rounds a number. +// NOTES: Round rounds to the nearest representable number +// double version. +// +_int MathUtil::Round(const double &num) +{ + if( num > 0 ) + return static_cast<_int>(num + .5); + else if (num < 0 ) + return static_cast<_int>(num - .5); + else + return 0; +} + + +////////////////////////////////////////////////////////////////////// +// NAME: SignedModulus +// PURPOSE: returns a mathematically correct modulus for int +// +_int MathUtil::SignedModulus(const _int &num, const _int &n) +{ + if( num >= 0 ) + return num%n; + else + { + // the % operator reflects the range if num < 0, so + // we have to multiply by -1 to reflect it back. This method + // is faster than calling Abs() and then doing the modulus. + _int Tnum = -1*(num%n); + + // if num%n equals 0, then n - Tnum will be n, which, logically + // speaking, is 0 in a different form, but we have to make sure it's + // in the form 0, and not n. Therefore, if Tnum = 0, simply leave + // it like that. + if( Tnum != 0) + Tnum = n - Tnum; + + return Tnum; + } +} + + +////////////////////////////////////////////////////////////////////// +// NAME: SignedModulus +// PURPOSE: returns a mathematically correct modulus for long +// +_long MathUtil::SignedModulus(const _long &num, const _long &n) +{ + if( num >= 0 ) + return num%n; + else + { + // the % operator reflects the range if num < 0, so + // we have to multiply by -1 to reflect it back. This method + // is faster than calling Abs() and then doing the modulus. + _long Tnum = -1*(num%n); + + // if num%n equals 0, then n - Tnum will be n, which, logically + // speaking, is 0 in a different form, but we have to make sure it's + // in the form 0, and not n. Therefore, if Tnum = 0, simply leave + // it like that. + if( Tnum != 0) + Tnum = n - Tnum; + + return Tnum; + } +} + + +////////////////////////////////////////////////////////////////////// +// NAME: SignedModulus +// PURPOSE: returns a mathematically correct modulus for float +// NOTES: uses fmod() in math.h, which returns the modulus of floats +// +_float MathUtil::SignedModulus(const _float &num, const _float &n) +{ + if( num >=0 ) + return static_cast<_float>( fmod(num,n) ); + else + { + // the % operator reflects the range if num < 0, so + // we have to multiply by -1 to reflect it back. This method + // is faster than calling Abs() and then doing the modulus. + _float Tnum = -1*( static_cast<_float>( fmod(num,n) ) ); + + // if num%n equals 0, then n - Tnum will be n, which, logically + // speaking, is 0 in a different form, but we have to make sure it's + // in the form 0, and not n. Therefore, if Tnum = 0, simply leave + // it like that. + if( Tnum != 0) + Tnum = n - Tnum; + + return Tnum; + } + +} + + +////////////////////////////////////////////////////////////////////// +// NAME: SignedModulus +// PURPOSE: returns a mathematically correct modulus for double +// NOTES: uses fmod() in math.h, which returns the modulus of floats +// +_double MathUtil::SignedModulus(const _double &num, const _double &n) +{ + if( num >=0 ) + return fmod(num,n); + else + { + // the % operator reflects the range if num < 0, so + // we have to multiply by -1 to reflect it back. This method + // is faster than calling Abs() and then doing the modulus. + _double Tnum = -1*( fmod(num,n) ); + + // if num%n equals 0, then n - Tnum will be n, which, logically + // speaking, is 0 in a different form, but we have to make sure it's + // in the form 0, and not n. Therefore, if Tnum = 0, simply leave + // it like that. + if( Tnum != 0) + Tnum = n - Tnum; + + return Tnum; + } + +} diff --git a/source/ps/MathUtil.h b/source/ps/MathUtil.h new file mode 100755 index 0000000000..b6b1a010ad --- /dev/null +++ b/source/ps/MathUtil.h @@ -0,0 +1,204 @@ +/* +Math utility functions +by Michael Reiland +recondite_phreak@yahool.com + +--Overview-- + + Contains common math functions like Abs, Sign, Max, Min, etc. + + +--More info-- + + TODO: actually write corresponding documentation + http://wildfiregames.com/0ad/codepit/TDD/math_utils.html + +*/ + + +#ifndef MATH_UTIL_H +#define MATH_UTIL_H + + +//-------------------------------------------------------- +// Includes / Compiler directives +//-------------------------------------------------------- + +#include "Prometheus.h" // Standard Engine Include +#include // Needed for fmod() + +//-------------------------------------------------------- +// Error declarations +//-------------------------------------------------------- + + +// MathUtil Errors +DECLARE_ERROR(ERRONEOUS_BOUND_ERROR); + + +//-------------------------------------------------------- +// Declarations +//-------------------------------------------------------- + +namespace MathUtil +{ + const _double PI = 3.14159265358932384; + const _double FL_FP_TOLERANCE = .000000001; + + + //-------------------------------------------------------- + // Template functions + //-------------------------------------------------------- + + + //-------------------------------------------------------- + // Declarations + //-------------------------------------------------------- + + ////////////////////////////////////////////////////////// + // NAME: Abs + // PURPOSE: Calculates the Absolute value + // + template + T Abs(const T &num) + { + if( num < 0) + return -1*num; + return num; + } + + + ////////////////////////////////////////////////////////// + // NAME: Clamp + // PURPOSE: Forces num to be between lowerBound and upperBound + // + template + T Clamp(T &num, const _int &lowerBound,const _int &upperBound) + { + if(num <= lowerBound) + num = static_cast(lowerBound); + else if( num >= upperBound) + num = static_cast(upperBound); + } + + + ////////////////////////////////////////////////////////// + // NAME: Max + // PURPOSE: Returns the largest number. + // + template + T Max(const T &num1, const T &num2) + { + if( num1 > num2) + return num1; + else + return num2; + } + + + ////////////////////////////////////////////////////////// + // NAME: Min + // PURPOSE: Returns the smallest number. + // + template + T Min(const T &num1, const T &num2) + { + if( num1 < num2) + return num1; + else + return num2; + } + + + ////////////////////////////////////////////////////////// + // NAME: Sign + // PURPOSE: Returns 1 if the number is > 0, -1 if it's < 0, + // otherwise returns 0. + // + template + _int Sign(const T &num) + { + if( num > 0 ) + return 1; + else if( num < 0 ) + return -1; + else + return 0; + } + + + ////////////////////////////////////////////////////////// + // NAME: Square + // PURPOSE: Returns the square of a number + // NOTES: Num should be less than the square root of the + // maximum representable number for the data type. + // + template + inline _double Square(const T &num) + { + return num*num; + } + + + ////////////////////////////////////////////////////////// + // NAME: Swap + // PURPOSE: Swaps two numbers + // + template + void Swap(T *num1, T *num2) + { + T temp = num1; + num1 = num2; + num2 = temp; + } + + + ////////////////////////////////////////////////////////// + // NAME: Wrap + // PURPOSE: Wraps num between lowerBound and upperBound. + // + template + PS_RESULT Wrap(T *num,const T &lowerBound, const T &upperBound) + { + if(lowerBound >= upperBound) + return ERRONEOUS_BOUND_ERROR; + else + { + // translate to range 0 to n-1, find the modulus, then + // translate back to range lowerBound to upperBound. + num -= lowerBound; + num = SignedModulus( num, Abs(upperBound - lowerBound) ); + num += lowerBound; + } + return PS_OK; + } + + + + //-------------------------------------------------------- + // Non-template functions + //-------------------------------------------------------- + + _int Ceiling(const float &num); + _int Ceiling(const double &num); + + _bool CompareFloat(const _double &, const _double &); + + _int Floor(const float &num); + _int Floor(const double &num); + + inline _double RadiansToDegrees(const _double &num); + inline _double DegreesToRadians(const _double &num); + + _float Random(const _float &, const _float &); + _int Random(const _int &,const _int &); + + _int Round(const float &num); + _int Round(const double &num); + + _int SignedModulus(const _int &num, const _int &n); + _long SignedModulus(const _long &num, const _long &n); + _float SignedModulus(const _float &num, const _float &n); + _double SignedModulus(const _double &num, const _double &n); +} +#endif \ No newline at end of file diff --git a/source/ps/Parser.cpp b/source/ps/Parser.cpp new file mode 100755 index 0000000000..daddd0116e --- /dev/null +++ b/source/ps/Parser.cpp @@ -0,0 +1,1015 @@ +// last modified Thursday, May 08, 2003 + +#include "parser.h" + +#pragma warning(disable:4786) +using namespace std; + +//------------------------------------------------- +// Macros +//------------------------------------------------- + +#if 1 +# define for if(false); else for +#endif // MSVC + +#define REGULAR_MAX_LENGTH 10 +#define START_DYNAMIC '<' +#define END_DYNAMIC '>' +#define START_OPTIONAL '[' +#define END_OPTIONAL ']' +#define REGULAR_EXPRESSION '$' + +// use GetDouble and type-cast it to <> +#define FUNC_IMPL_CAST_GETDOUBLE(func_name,type) \ +_bool CParserValue::func_name(type &ret) \ +{ \ + _double d; \ + if (GetDouble(d)) \ + return ret = (type)d, true; \ + else \ + return false; \ +} + +// Function-implementation creator for GetArg%type% that will call +// Get%type% from the CParserValue +// func_name must belong to CParserFile +#define FUNC_IMPL_GETARG(func_name, get_name, type) \ +_bool CParserLine::func_name(const _int & arg, type &ret) \ +{ \ + if (GetArgCount() <= arg) \ + return false; \ + return m_Arguments[arg].get_name(ret); \ +} + +//------------------------------------------------- +// Function definitions +//------------------------------------------------- + +static _bool _IsStrictNameChar(const _char& c); +static _bool _IsValueChar(const _char& c); + + +// Functions used for checking a character if it belongs to a value +// or not + +// Checks ident +static _bool _IsStrictNameChar(const _char& c) +{ + return ((c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9')); +} + +// Checks value +static _bool _IsValueChar(const _char& c) +{ + return ((c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || + c=='.' || c=='_'); +} + +// CParserValue +// ---------------------------------------------------------------------| Class + +CParserValue::CParserValue() +{ +} + +CParserValue::~CParserValue() +{ +} + +// Parse the string in Value to different types + +// bool +_bool CParserValue::GetBool(_bool &ret) +{ + // TODO Raj Add or remove some? I can make it all lowercase + // first so True and TRUE also works, or you could just + // add them too + + // true + if (m_String == "true" || + m_String == "on" || + m_String == "1" || + m_String == "yes") + { + ret = true; + return true; + } + else + // false + if (m_String == "false" || + m_String == "off" || + m_String == "0" || + m_String == "no") + { + ret = false; + return true; + } + + // point only erroneous runs reach + return false; +} + +// double +_bool CParserValue::GetDouble(_double &ret) +{ + // locals + _double TempRet = 0.0; + _int Size = m_String.size(); + _int i; + _bool AtLeastOne = false; // Checked if at least one of the loops + // run, otherwise "." would parse OK + _int DecimalPos; + _bool Negative = false; // "-" is found + + // Check if '-' is found + if (m_String[0]=='-') + { + Negative = true; + } + + // find decimal position + DecimalPos = m_String.find("."); + if (DecimalPos == string::npos) + DecimalPos = Size; + + // Iterate left of the decimal sign + // + for (i=(Negative?1:0); i < DecimalPos; ++i) + { + // Set AtLeastOne to true + AtLeastOne = true; + + // Check if a digit is found + if (m_String[i] >= '0' && m_String[i] <= '9') + { + TempRet += (m_String[i]-'0')*pow(10,(DecimalPos-i-1)); + } + else + { + // parse error! + return false; + } + } + + // Iterate right of the decimal sign + // + for (i=DecimalPos+1; i < Size; ++i) + { + // Set AtLeastOne to true + AtLeastOne = true; + + // Check if a digit is found + if (m_String[i] >= '0' && m_String[i] <= '9') + { + TempRet += (m_String[i]-'0')*pow(10,DecimalPos-i); + } + // It will accept and ending f, like 1.0f + else if (!(i==Size-1 && m_String[i] == 'f')) + { + // parse error! + return false; + } + } + + if (!AtLeastOne)return false; + + // Set the reference to the temp value and return success + ret = (Negative?-TempRet:TempRet); + return true; +} + +// string - only return m_String, can't fail +_bool CParserValue::GetString(std::string &ret) +{ + ret = m_String; + return true; +} + +// These macros include the IMPLEMENTATION of the +// the function in the macro argument for CParserValue +// They use GetDouble, and then type-cast it +FUNC_IMPL_CAST_GETDOUBLE(GetFloat, _float) +FUNC_IMPL_CAST_GETDOUBLE(GetChar, _char) +FUNC_IMPL_CAST_GETDOUBLE(GetShort, _short) +FUNC_IMPL_CAST_GETDOUBLE(GetInt, _int) +FUNC_IMPL_CAST_GETDOUBLE(GetLong, _long) +FUNC_IMPL_CAST_GETDOUBLE(GetUnsignedShort, _ushort) +FUNC_IMPL_CAST_GETDOUBLE(GetUnsignedInt, _uint) +FUNC_IMPL_CAST_GETDOUBLE(GetUnsignedLong, _ulong) + +// CParserTaskTypeNode +// ---------------------------------------------------------------------| Class + +CParserTaskTypeNode::CParserTaskTypeNode() : m_ParentNode(NULL), m_NextNode(NULL), m_AltNode(NULL) +{ +} + +CParserTaskTypeNode::~CParserTaskTypeNode() +{ +} + +// Delete all children +void CParserTaskTypeNode::DeleteChildren() +{ + // Delete nodes if applicable + if (m_NextNode) + { + m_NextNode->DeleteChildren(); + delete m_NextNode; + m_NextNode = NULL; + } + + if (m_AltNode) + { + m_AltNode->DeleteChildren(); + delete m_AltNode; + m_AltNode = NULL; + } +} + +// CParserTaskType +// ---------------------------------------------------------------------| Class + +CParserTaskType::CParserTaskType() : m_BaseNode(NULL) +{ +} + +CParserTaskType::~CParserTaskType() +{ +} + +// Delete m_BaseNode and all of its children +void CParserTaskType::DeleteTree() +{ + if (m_BaseNode) + { + m_BaseNode->DeleteChildren(); + delete m_BaseNode; + m_BaseNode = NULL; + } +} + +// CParserLine +// ---------------------------------------------------------------------| Class + +CParserLine::CParserLine() +{ +} + +CParserLine::~CParserLine() +{ + + ClearArguments(); +} + +// Clear arguments (deleting m_Memory +_bool CParserLine::ClearArguments() +{ + // Now we can actually clear it + m_Arguments.clear(); + return true; +} + +// Implementation of CParserFile::GetArg* +// it just checks if argument isn't out of range, and +// then it uses the the respective function in CParserValue +FUNC_IMPL_GETARG(GetArgString, GetString, string) +FUNC_IMPL_GETARG(GetArgBool, GetBool, _bool) +FUNC_IMPL_GETARG(GetArgChar, GetChar, _char) +FUNC_IMPL_GETARG(GetArgShort, GetShort, _short) +FUNC_IMPL_GETARG(GetArgInt, GetInt, _int) +FUNC_IMPL_GETARG(GetArgLong, GetLong, _long) +FUNC_IMPL_GETARG(GetArgUnsignedShort, GetUnsignedShort, _ushort) +FUNC_IMPL_GETARG(GetArgUnsignedInt, GetUnsignedInt, _uint) +FUNC_IMPL_GETARG(GetArgUnsignedLong, GetUnsignedLong, _ulong) +FUNC_IMPL_GETARG(GetArgFloat, GetFloat, _float) +FUNC_IMPL_GETARG(GetArgDouble, GetDouble, _double) + +// ParseString +// ------------------------------------------------------------------| Function +// Parses a line, dividing it into segments according to defined semantics +// each segment is called an argument and represents a value of some kind +// ex: +// variable = 5 => variable, =, 5 +// CallFunc(4,2) => CallFunc, 4, 2 +_bool CParserLine::ParseString(const CParser& Parser, string strLine) +{ + // Don't process empty string + if (strLine == string()) + { + m_ParseOK = false; // Empty lines should never be inputted by CParserFile + return m_ParseOK; + } + + // Locals + _bool Extract=false; + _int ExtractPos=0; + _char Buffer[256]; + _char Letter[] = {'\0','\0'}; // Letter as string + vector Segments; + string strSub; + + // Set result to false, then if a match is found, turn it true + m_ParseOK = false; + + /* + TODO Gee Remove this comment! + // Remove C++-styled comments! + // * * * * + int pos = strLine.find("//"); + if (pos != string::npos) + strLine = strLine.substr(0,pos); + */ + + // Divide string into smaller vectors, seperators are unusual signs + // * * * * + + for (_int i=0; i= 256) + { + Extract=false; + } + else + { + // Extract string after $ ! + // break whenever we reach a sign that's not A-Z a-z + if (_IsValueChar(strLine[i])) + { + Buffer[i-ExtractPos] = strLine[i]; + } + else + { + // Extraction is finished + Extract=false; + + // strLine[i] is now a non-regular character + // we'll jump back one step so that will + // be included next loop + --i; + } + + // Check if string is complete + if (i == strLine.size()-1) + Extract=false; + } + + // If extraction was finished! Input Buffer + if (Extract == false) + { + Segments.push_back( string(Buffer) ); + } + } + } + + // Try to find an appropriate CParserTaskType in parser + // * * * * + + // Locals + _int Progress; // progress in Segments index + _int Lane=0; // Have many alternative routes we are in + _bool Match; // If a task-type match has been found + // The vector of these three represents the different lanes + // LastValidProgress[1] takes you back to lane 1 and how + // the variables was set at that point + vector<_int> LastValidProgress; // When diving into a dynamic argument store store + // the last valid so you can go back to it + vector<_int> LastValidArgCount; // If an alternative route turns out to fail, we + // need to know the amount of arguments on the last + // valid position, so we can remove them. + vector<_bool> LastValidMatch; // Match at that point + _bool BlockAltNode = false; // If this turns true, the alternative route + // tested was not a success, and the settings + // should be set back in order to test the + // next node instead + _bool LookNoFurther = false; // If this turns true, it means a definite match has been + // found and no further looking is required + CParserTaskTypeNode *CurNode=NULL; // Current node on task type + CParserTaskTypeNode *PrevNode=NULL; // Last node + + // Iterate all different TaskType, and all TaskTypeElements... + // start from left and go to the right (prog), comparing + // the similarities. If enough + // similarities are found, then we can declare progress as + // that type and exit loop + vector::const_iterator cit_tt; + for (cit_tt = Parser.m_TaskTypes.begin(); + cit_tt != Parser.m_TaskTypes.end(); + ++cit_tt) + { + // Reset for this task-type + Match = true; + Progress = 0; + ClearArguments(); // Previous failed can have filled this + CurNode = cit_tt->m_BaseNode; // Start at base node + LookNoFurther = false; + BlockAltNode = false; + + // This loop will go through the whole tree until + // it reaches an empty node + while (!LookNoFurther) + { + // Check if node is valid + // otherwise try to jump back to parent + if (CurNode->m_NextNode == NULL && + (CurNode->m_AltNode == NULL || BlockAltNode)) + { + // Jump back to valid + //CurNode = PrevNode; + + // If the node has no children, it's the last, and we're + // on lane 0, i.e. with no + if (CurNode->m_NextNode == NULL && + (CurNode->m_AltNode == NULL || BlockAltNode) && + Lane == 0) + { + if (Progress != Segments.size()) + Match = false; + + break; + } + else + { + CParserTaskTypeNode *OldNode = NULL; + + // Go back to regular route! + while (1) + { + OldNode = CurNode; + CurNode = CurNode->m_ParentNode; + + if (CurNode->m_AltNode == OldNode) + { + break; + } + } + + // If the alternative route isn't repeatable, block alternative route for + // next loop cycle + if (!CurNode->m_AltNodeRepeatable) + BlockAltNode = true; + + // Decrease lane + --Lane; + } + } + + // Check alternative route + // * * * * + + // Check if alternative route is present + // note, if an alternative node has already failed + // we don't want to force usage of the next node + // therefore BlockAltNode has to be false + if (!BlockAltNode) + { + if (CurNode->m_AltNode) + { + // Alternative route found, we'll test this first! + CurNode = CurNode->m_AltNode; + + // --- New node is set! + + // Make sure they are large enough + if (LastValidProgress.size() < Lane+1) + { + LastValidProgress.resize(Lane+1); + LastValidMatch.resize(Lane+1); + LastValidArgCount.resize(Lane+1); + } + + // Store last valid progress + LastValidProgress[Lane] = Progress; + LastValidMatch[Lane] = Match; + LastValidArgCount[Lane] = m_Arguments.size(); + + ++Lane; + + continue; + } + } + else BlockAltNode = false; + + // Now check Regular Next Node + // * * * * + + if (CurNode->m_NextNode) + { + // Important! + // Change working node to the next node! + CurNode = CurNode->m_NextNode; + + // --- New node is set! + + // CHECK IF LETTER IS CORRECT + if (CurNode->m_Letter != '\0') + { + // OPTIONALLY SKIP BLANK SPACES + if (CurNode->m_Letter == '_') + { + // Find blank space if any! + // and jump to the next non-blankspace + if (Progress < Segments.size()) + { + // Skip blankspaces AND tabs! + while (Segments[Progress].size()==1 && + (Segments[Progress][0]==' ' || + Segments[Progress][0]=='\t')) + { + ++Progress; + + // Check length + if (Progress >= Segments.size()) + { + break; + } + } + } + } + else + // CHECK LETTER IF IT'S CORRECT + { + if (Progress < Segments.size()) + { + // This should be 1-Letter long + if (Segments[Progress].size() != 1) + Match = false; + + // Check Letter + if (CurNode->m_Letter != Segments[Progress][0]) + Match = false; + + // Update progress + ++Progress; + } + else Match = false; + } + } + // CHECK NAME + else + { + // Do this first, because we wan't to + // avoid the Progress and Segments.size() + // check for this + if (CurNode->m_Type == typeAddArg) + { + // Input argument + CParserValue value; + value.m_String = CurNode->m_String; + m_Arguments.push_back(value); + } + else + { + // Alright! An ident or const has been acquired, if we + // can't find any or if the string has run out + // that invalidates the match + + // String end? + if (Progress >= Segments.size()) + { + Match = false; + } + else + { + // Store argument in CParserValue! + CParserValue value; + _int i; + + switch(CurNode->m_Type) + { + case typeIdent: + // Check if this really is a string + if (!_IsStrictNameChar(Segments[Progress][0])) + { + Match = false; + break; + } + + // Same as at typeValue, but this time + // we won't allow strings like "this", just + // like this + if (Segments[Progress][0] == '\"') + Match = false; + else + value.m_String = Segments[Progress]; + + // Input argument! + m_Arguments.push_back(value); + + ++Progress; + break; + case typeValue: + // Check if this really is a string + if (!_IsValueChar(Segments[Progress][0]) && + Segments[Progress][0] != '\"') + { + Match = false; + break; + } + + // Check if initial is -> " <-, because that means it was + // stored from a "String like these with quotes" + // We don't want to store that prefix + if (Segments[Progress][0] == '\"') + value.m_String = Segments[Progress].substr(1, Segments[Progress].size()-1); + else + value.m_String = Segments[Progress]; + + // Input argument! + m_Arguments.push_back(value); + + ++Progress; + break; + case typeRest: + // Extract the whole of the string + + // Reset, probably is but still + value.m_String = string(); + + for (i=Progress; i " <=, add one to the end of it too + if (Segments[i][0] == '"') + value.m_String += "\""; + } + + m_Arguments.push_back(value); + + // Now BREAK EVERYTHING ! + // We're done, we found are match and let's get out + LookNoFurther = true; + //Match = true; + break; + default: + break; + } + } + } + } + } + + // Check if match is false! if it is, try returning to last valid state + if (!Match && Lane > 0) + { + // The alternative route failed + BlockAltNode = true; + + CParserTaskTypeNode *OldNode = NULL; + + // Go back to regular route! + while (1) + { + OldNode = CurNode; + CurNode = CurNode->m_ParentNode; + + if (CurNode->m_AltNode == OldNode) + { + break; + } + } + + // Decrease lane + --Lane; + + // Restore values as before + Progress = LastValidProgress[Lane]; + Match = LastValidMatch[Lane]; + m_Arguments.resize(LastValidArgCount[Lane]); + } + } + + // Check if it was a match! + if (Match) + { + // Before we celebrate the match, let's check if whole + // of Segments has been used, and if so we have to + // nullify the match + //if (Progress == Segments.size()) + { + // *** REPORT MATCH WAS FOUND *** + m_TaskTypeName = cit_tt->m_Name; + m_ParseOK = true; + break; + } + } + } + + // POST-PROCESSING OF ARGUMENTS! + + // if _minus is found as argument, remove it and add "-" to the one after that + // note, it's easier if std::iterator isn't used here + + for (_int i=1; i::iterator itTT; + for (itTT = m_TaskTypes.begin(); + itTT != m_TaskTypes.end(); + ++itTT) + { + itTT->DeleteTree(); + } +} + +// InputTaskType +// ------------------------------------------------------------------| Function +// A task-type is a string representing the acquired syntax when parsing +// This function converts that string into a binary tree, making it easier +// and faster to later parse. +_bool CParser::InputTaskType(const string& strName, const string& strSyntax) +{ + // Locals + CParserTaskType TaskType; // Object we acquire to create + _char Buffer[REGULAR_MAX_LENGTH]; + _int ExtractPos = 0; + _bool Extract = false; + _bool Error = false; + _bool ConstructNew = false; // If it's the first input, then don't + // construct a new node, because we + // we already have m_BaseNode + + // Construct base node + TaskType.m_BaseNode = new CParserTaskTypeNode(); + + // Working node + CParserTaskTypeNode *CurNode = TaskType.m_BaseNode; + + // Loop through the string and construct nodes in the binary tree + // when applicable + for (_int i=0; im_AltNode = new CParserTaskTypeNode(); + CurNode->m_AltNode->m_ParentNode = CurNode; + + // It's repeatable + CurNode->m_AltNodeRepeatable = _bool(strSyntax[i]==START_DYNAMIC); + + // Set to current + CurNode = CurNode->m_AltNode; + + ConstructNew = false; + + // We're done extracting for now + continue; + } + else + if (strSyntax[i] == END_DYNAMIC || strSyntax[i] == END_OPTIONAL) + { + CParserTaskTypeNode *OldNode = NULL; + + // Jump out of this alternative route + while (1) + { + OldNode = CurNode; + CurNode = CurNode->m_ParentNode; + + if (CurNode == NULL) + { + // Syntax error + Error = true; + break; + } + + if (CurNode->m_AltNode == OldNode) + { + break; + } + } + + if (Error)break; + } + else + { + // Check if this is the first input + // CONSTRUCT A CHILD NODE + CurNode->m_NextNode = new CParserTaskTypeNode(); + CurNode->m_NextNode->m_ParentNode = CurNode; + + // Jump into ! + CurNode = CurNode->m_NextNode; + + // Set CurNode + CurNode->m_Letter = strSyntax[i]; + } + } + + // Extact + if (Extract) + { + // No type names are longer than REGULAR_MAX_LENGTH characters + if (i-ExtractPos >= REGULAR_MAX_LENGTH) + { + Extract=false; + } + else + { + // Extract string after $ ! + // break whenever we reach a sign that's not A-Z a-z + if (_IsStrictNameChar(strSyntax[i])) + { + Buffer[i-ExtractPos] = strSyntax[i]; + } + else + { + // Extraction is finished + Extract=false; + + // strLine[i] is now a non-regular character + // we'll jump back one step so that will + // be included next loop + --i; + } + + // Check if string is complete + if (i == strSyntax.size()-1) + Extract=false; + } + + // If extraction was finished! Input Buffer + if (Extract == false) + { + // CONSTRUCT A CHILD NODE + CurNode->m_NextNode = new CParserTaskTypeNode(); + CurNode->m_NextNode->m_ParentNode = CurNode; + + // Jump into ! + CurNode = CurNode->m_NextNode; + + CurNode->m_Letter = '\0'; + + string str = string(Buffer); + + // Check value and set up CurNode accordingly + if (str == "value") CurNode->m_Type = typeValue; + else + if (str == "ident") CurNode->m_Type = typeIdent; + else + if (str == "rest") CurNode->m_Type = typeRest; + else + if (str == "rbracket") CurNode->m_Letter = '>'; + else + if (str == "lbracket") CurNode->m_Letter = '<'; + else + if (str == "rbrace") CurNode->m_Letter = ']'; + else + if (str == "lbrace") CurNode->m_Letter = '['; + else + if (str == "dollar") CurNode->m_Letter = '$'; + else + if (str == "arg") + { + // After $arg, you need a parenthesis, within that parenthesis is a string + // that will be added as an argument when it's passed through + + CurNode->m_Type = typeAddArg; + + // Check length, it has to have place for at least a '(' and ')' after $arg + if (ExtractPos+4 >= strSyntax.size()) + { + Error = true; + break; + } + + // We want to extract what's inside the parenthesis after $arg + // if it's not there at all, it's a syntactical error + if (strSyntax[ExtractPos+3] != '(') + { + Error = true; + break; + } + + // Now try finding the second ')' + _int Pos = strSyntax.find(")", ExtractPos+5); + + // Check if ')' exists at all + if (Pos == string::npos) + { + Error = true; + break; + } + + // Now extract string within ( and ) + CurNode->m_String = strSyntax.substr(ExtractPos+4, Pos-(ExtractPos+4)); + + // Now update position + i = Pos; + } + else + { + // TODO Gee report in log too + Error = true; + } + } + } + } + + // Input TaskType + if (!Error) + { + // Set name and input + TaskType.m_Name = strName; + m_TaskTypes.push_back(TaskType); + } + + return !Error; +} + +/* End of PARSER.CPP +******************************************************************************/ diff --git a/source/ps/Parser.h b/source/ps/Parser.h new file mode 100755 index 0000000000..8cc52a69df --- /dev/null +++ b/source/ps/Parser.h @@ -0,0 +1,217 @@ +/* +Customizeable Text Parser +by Gee +Gee@pyro.nu + +--Overview-- + +CParserValue Data! (an int, real, string etc), basically an argument +CParserTaskType Syntax description for a line (ex. "variable=value") +CParserLine Parse _one_ line +CParser Include all syntax (CParserTaskTypes) + +The whole CParser* class group is used to read in config files and +give instruction on how that should be made. The CParserTaskType +declares what in a line is arguments, of course different CParserTaskTypes +will exist, and it's up to the system to figure out which one acquired. + + +--More Info-- + + TODO: Write URL of documentation + +*/ + +#ifndef __PARSER_H +#define __PARSER_H + +#include "Prometheus.h" + +#pragma warning(disable:4786) + +//-------------------------------------------------------- +// Includes / Compiler directives +//-------------------------------------------------------- + +#include +#include +#include +#include +#include + +//------------------------------------------------- +// Types +//------------------------------------------------- + +enum _ParserValueType +{ + typeIdent, + typeValue, + typeRest, + typeAddArg +}; + +//------------------------------------------------- +// Declarations +//------------------------------------------------- + +class CParserValue; +class CParserTaskType; +class CParserLine; +class CParser; + + +// CParserValue +// --------------------------------------------------------------------- +// A parser value represents an argument +// color=r, g, b +// r, g and b will be CParserValues, or the arguments as they are called. +// This class can store only a string, but can try parsing it to different +// types +class CParserValue +{ +public: + CParserValue(); + ~CParserValue(); + + // return is error status + _bool GetString(std::string &ret); + _bool GetBool(_bool &ret); + _bool GetChar(_char &ret); // As number! otherwise use GetString make sure size=1 + _bool GetShort(_short &ret); + _bool GetInt(_int &ret); + _bool GetLong(_long &ret); + _bool GetUnsignedShort(_ushort &ret); + _bool GetUnsignedInt(_uint &ret); + _bool GetUnsignedLong(_ulong &ret); + _bool GetFloat(float &ret); + _bool GetDouble(double &ret); + + // Memory regardless if it's an int, real, string or whatever + std::string m_String; +}; + +// CParserTaskTypeNode +// ---------------------------------------------------------------------| Class +// A task type is basically a tree, this is because dynamic arguments +// requires alternative routes, so basically it's a binary tree with an +// obligatory next node (if it's not the end of the tree) and an alternative +// dynamic arguments node +// +// If we are at the beginning of this string, this will be the layout of the node +// "<$value_>:_" +// +// m_Element ":" +// m_AltNode => "$value" +// m_NextNode => "_" +// +class CParserTaskTypeNode +{ +public: + CParserTaskTypeNode(); + ~CParserTaskTypeNode(); + + // Free node pointers that are below this + void DeleteChildren(); + + // Either the node is a letter or a type, if m_Leter is '\0' + // then check m_Type what it is + _char m_Letter; + _ParserValueType m_Type; + std::string m_String; // Used for diverse storage + // mainly for the typeAddArg + + // Parent node + CParserTaskTypeNode *m_ParentNode; + + // Next node + CParserTaskTypeNode *m_NextNode; + + // true means AltNode can be looped <...> + // false means AltNode is just an optional part [...] + _bool m_AltNodeRepeatable; + + // Whenever a dynamic argument is used, it's first node is stored in this + // as an alternative node. The parser first checks if there is an + // alternative route, and if it applies to the next node. If not, proceed + // as usual with m_String and the next node + CParserTaskTypeNode *m_AltNode; + + // There are different kinds of alternative routes + //int m_AltRouteType; +}; + +// CParserTaskType +// ---------------------------------------------------------------------| Class +// A task type is basically different kinds of lines for the parser +// variable=value is one type... +class CParserTaskType +{ +public: + CParserTaskType(); + ~CParserTaskType(); + + // Delete the whole tree + void DeleteTree(); + + CParserTaskTypeNode *m_BaseNode; + + // Something to identify it with + std::string m_Name; +}; + +// CParserLine +// ---------------------------------------------------------------------| Class +// Representing one line, i.e. one task, in a config file +class CParserLine +{ +public: + CParserLine(); + ~CParserLine(); + + std::deque m_Arguments; + _bool m_ParseOK; // same as ParseString will return + std::string m_TaskTypeName; // Name of the task type found + +protected: + _bool ClearArguments(); + +public: + // Interface + _bool ParseString(const CParser& parser, std::string line); + + // Methods for getting arguments + // it returns success + _bool GetArgString (const _int& arg, std::string &ret); + _bool GetArgBool (const _int& arg, _bool &ret); + _bool GetArgChar (const _int& arg, _char &ret); + _bool GetArgShort (const _int& arg, _short &ret); + _bool GetArgInt (const _int& arg, _int &ret); + _bool GetArgLong (const _int& arg, _long &ret); + _bool GetArgUnsignedShort (const _int& arg, _ushort &ret); + _bool GetArgUnsignedInt (const _int& arg, _uint &ret); + _bool GetArgUnsignedLong (const _int& arg, _ulong &ret); + _bool GetArgFloat (const _int& arg, float &ret); + _bool GetArgDouble (const _int& arg, double &ret); + + // Get Argument count + _int GetArgCount() const { return m_Arguments.size(); } +}; + +// CParser +// ---------------------------------------------------------------------| Class +// Includes parsing instruction, i.e. task-types +class CParser +{ +public: + CParser::CParser(); + CParser::~CParser(); + + std::vector m_TaskTypes; + + // Interface + _bool InputTaskType(const std::string& strName, const std::string& strSyntax); +}; + + +#endif diff --git a/source/ps/Prometheus.cpp b/source/ps/Prometheus.cpp new file mode 100755 index 0000000000..88594a8ce8 --- /dev/null +++ b/source/ps/Prometheus.cpp @@ -0,0 +1,4 @@ +#include "Prometheus.h" + +DEFINE_ERROR(PS_OK, "OK"); +DEFINE_ERROR(PS_FAIL, "Fail"); diff --git a/source/ps/Prometheus.h b/source/ps/Prometheus.h new file mode 100755 index 0000000000..9a0a327c93 --- /dev/null +++ b/source/ps/Prometheus.h @@ -0,0 +1,80 @@ +/* +Prometheus.h +by Raj Sharma +rsharma@uiuc.edu + +Standard declarations which are included in all projects. +*/ + +#ifndef PROMETHEUS_H +#define PROMETHEUS_H + +#pragma warning (disable : 4786) +#pragma warning (disable : 4291) + +#include +#include +#include + +// Standard typedefs + +typedef int _int; +typedef unsigned int _unsigned_int; + +typedef long _long; +typedef unsigned long _unsigned_long; + +typedef bool _bool; + +typedef char _char; +typedef unsigned char _unsigned_char; + +typedef char _byte; +typedef unsigned char _unsigned_byte; + +typedef float _float; +typedef double _double; + +typedef unsigned short _unsigned_short; +typedef unsigned short _ushort; +typedef short _short; +typedef unsigned long _ulong; +typedef unsigned int _uint; + +// Error handling + +typedef const char * PS_RESULT; + +#define DEFINE_ERROR(x, y) PS_RESULT x=y; +#define DECLARE_ERROR(x) extern PS_RESULT x; + +DECLARE_ERROR(PS_OK); +DECLARE_ERROR(PS_FAIL); + + +/* +inline bool ErrorMechanism_Error(); + inline void ErrorMechanism_ClearError(); + inline bool ErrorMechanism_TestError( PS_RESULT); + + inline void ErrorMechanism_SetError( + PS_RESULT, + std::string, + std::string, + unsigned int); + + inline CError ErrorMechanism_GetError(); +*/ + + +// Setup error interface +#define IsError() GError.ErrorMechanism_Error() +#define ClearError() GError.ErrorMechanism_ClearError() +#define TestError(TError) GError.ErrorMechanism_TestError(TError) + +#define SetError_Short(w,x) GError.ErrorMechanism_SetError(w,x,__FILE__,__LINE__) +#define SetError_Long(w,x,y,z) GError.ErrorMechanism_SetError(w,x,y,z) + +#define GetError() GError.ErrorMechanism_GetError() + +#endif \ No newline at end of file diff --git a/source/ps/Singleton.h b/source/ps/Singleton.h new file mode 100755 index 0000000000..c817d44a01 --- /dev/null +++ b/source/ps/Singleton.h @@ -0,0 +1,63 @@ +////////////////////////////////////////////////////////////////////////////// +// AUTHOR: Michael Reiland +// FILENAME: Singleton.h +// PURPOSE: Provides a base template class for Singletons +// +// USEAGE: class myClass : Singleton{}; +// +// INFO: This implementation was copied from: +// +// Enginuity, Part II +// Memory Management, Error Logging, and Utility Classes; +// or, How To Forget To Explode Your Underwear +// by Richard "superpig" Fine +// +// hosted at Gamedev.net at +// http://gamedev.net/reference/articles/article1954.asp +// +// MODIFIED: 07.09.2003 mreiland +#ifndef _TEMPLATE_SINGLETON +#define _TEMPLATE_SINGLETON + +#include + + +template +class Singleton +{ + static T* ms_singleton; + + public: + Singleton() + { + assert( !ms_singleton ); + + //use a cunning trick to get the singleton pointing to the start of + //the whole, rather than the start of the Singleton part of the object + int offset = (int)(T*)1 - (int)(Singleton*)(T*)1; + ms_singleton = (T*)((int)this + offset); + } + + ~Singleton() + { + assert( ms_singleton ); + ms_singleton=0; + } + + static T& GetSingleton() + { + assert( ms_singleton ); + return *ms_singleton; + } + + static T* GetSingletonPtr() + { + assert( ms_singleton ); + return ms_singleton; + } +}; + +template +T* Singleton::ms_singleton = 0; + +#endif \ No newline at end of file diff --git a/source/ps/Sound.h b/source/ps/Sound.h new file mode 100755 index 0000000000..bc1b8b96e7 --- /dev/null +++ b/source/ps/Sound.h @@ -0,0 +1,163 @@ +/* +Sound.h +by Raj + +Classes to play sounds or music using FMOD + +Usage: Create a CWindow object, call Create, call Run. + + If you want to handle events like mouse clicks, you need to derive your + own class from CBaseWindow, and override the OnXXX() functions. For example, + OnActivate() or OnPaint(). +*/ + + +-----support pan, volume, and crossfading + + + +struct SSoundEffect +{ + Position + Radius + Volume + Actual sound effect + Layer +}; + + +struct SoundScheme +{ + include several different sounds + specify the looping time for this scheme + and, +} + + +Class methods + +-SetCrossFadeSpeed: how quickly it takes to crossfade between current music and new music + + + + + +#ifndef SOUND_H +#define SOUND_H + +#pragma warning (disable: 4786) + +//-------------------------------------------------------- +// Includes / Compiler directives +//-------------------------------------------------------- + +#include +#include +#include +#include +#include +#include + +using namespace std; + +DECLARE_ERROR(PS_SOUND_INIT); + +typedef bool STREAM_OPTION; +const int STREAMING = true; +const int NO_STREAMING = false; + +class CSample +{ +public: + + operator FSOUND_STREAM * (); + operator FSOUND_SAMPLE * (); + + operator = (FSOUND_STREAM *); + operator = (FSOUND_SAMPLE *); + +private: + union + { + FSOUND_STREAM *stream; + FSOUND_SAMPLE *sample; + }; + +}; + + +// CSound: Class which plays sounds or music +class CSound +{ +public: + CSound(); + ~CSound(); + + PS_RESULT Init(); + PS_RESULT Release(); + + PS_RESULT Load(string filename, string nickname, STREAM_OPTION useStreaming=false); + PS_RESULT Play(string nickname); + + PS_RESULT SetMasterVolume(float vol); + PS_RESULT SetSampleVolume(string sample, float vol); + + PS_RESULT Pause(); + PS_RESULT Resume(); + +private: + map soundList; + +}; + + +PS_RESULT CSound::Load(string filename, string nickname, STREAM_OPTION useStreaming) +{ + CSample newFile; + + if(useStreaming == STREAMING) + { + + } + else if(useStreaming == NO_STREAMING) + { + FSOUND_Sample_Load(FSOUND_FREE, //let FSOUND select an arbitrary sample slot + filename.c_str(), //name of the file to load + FSOUND_LOADMEMORY, + ); + } + + return PS_OK; +} + +PS_RESULT CSound::Play(string nickname) +{ + return PS_OK; +} + +PS_RESULT CSound::Release() +{ + return PS_OK; +} + +PS_RESULT CSound::SetMasterVolume(float vol) +{ + return PS_OK; +} + +PS_RESULT CSound::SetSampleVolume(string sample, float vol) +{ + return PS_OK; +} + +PS_RESULT CSound::Pause() +{ + return PS_OK; +} + +PS_RESULT CSound::Resume() +{ + return PS_OK; +} + +#endif \ No newline at end of file diff --git a/source/readme.txt b/source/readme.txt new file mode 100755 index 0000000000..4336b8a481 --- /dev/null +++ b/source/readme.txt @@ -0,0 +1,7 @@ +- download http://oss.sgi.com/projects/ogl-sample/ABI/glext.h , put in compiler's include\gl dir +- leave out debug* for now (requires dbghelp, included with vc7) +- leave out memcpy.cpp, unless processor pack installed (not currently used anyway) +- don't define D3D8 unless the SDK is installed (=> less accurate graphics card info) +- dito for DDRAW7 (=> video card memory size not available) +- set code generation to multithreaded +- set entry point to entry (=> diff --git a/source/res.cpp b/source/res.cpp new file mode 100755 index 0000000000..9ac9f337d8 --- /dev/null +++ b/source/res.cpp @@ -0,0 +1,289 @@ +// handle-based resource manager +// +// Copyright (c) 2003 Jan Wassenberg +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// Contact info: +// Jan.Wassenberg@stud.uni-karlsruhe.de +// http://www.stud.uni-karlsruhe.de/~urkt/ + +#include +#include +#include +#include + +#include "types.h" +#include "unzip.h" +#include "posix.h" +#include "misc.h" +#include "res.h" +#include "vfs.h" + +// handle (32 bits) +// .. detect use of already freed handles +static const uint TAG_BITS = 18; +// .. index into array => = log2(max open handles) +static const uint IDX_BITS = 14; + +static const uint TYPE_BITS = 5; +static const uint REF_BITS = 9; + +struct Res +{ + u32 key; + u32 tag : TAG_BITS; + u32 type : TYPE_BITS; + u32 refs : REF_BITS; +}; + +static const ulong res_array_cap = 1ul << IDX_BITS; +static const uint max_types = 1ul << TYPE_BITS; + +// static allocation for simplicity; mem usage isn't significant +static Res res_array[res_array_cap]; +static int first_free = -1; +static int max_idx = -1; // index of last in-use entry + +// array of pages for better locality, less fragmentation +static const uint PAGE_SIZE = 4096; +static const uint hdata_per_page = PAGE_SIZE / sizeof(HDATA); +static const uint num_pages = res_array_cap / hdata_per_page; +static HDATA* pages[num_pages]; + +static void(*dtors[max_types])(HDATA*); + + + + + + + + +static Handle handle(const uint idx) +{ + if(idx >= (1 << IDX_BITS)) + { + assert(!"invalid index passed to handle()"); + return 0; + } + return (res_array[idx].tag << IDX_BITS) | idx; +} + + +static int h_idx(Handle h, uint type) +{ + int idx = h & ((1 << IDX_BITS)-1); + const Res& r = res_array[idx]; + + u32 tag = h >> IDX_BITS; + if(!tag || r.tag != tag || r.type != type) + return -1; + + return idx; +} + + +static void cleanup(void) +{ + int i; + + // close open handles + for(i = 0; i < max_idx; i++) + h_free(handle(i)); + + // free internal data space + for(i = 0; i < (int)num_pages; i++) + { + free(pages[i]); + pages[i] = 0; + } +} + + +Handle h_alloc(const u32 key, const uint type, DTOR dtor, HDATA*& hd) +{ + ONCE(atexit(cleanup)) + + if(type >= max_types) + return 0; + + if(dtor) + { + // registering a second dtor for type + if(dtors[type] && dtors[type] != dtor) + return 0; + dtors[type] = dtor; + } + + Handle h; + int idx; + Res* r = res_array; + + h = h_find(key, type, hd); + if(h) + { + idx = h_idx(h, type); + r = &res_array[idx]; + if(r->refs == 1ul << REF_BITS) + { + assert(!"too many references to a handle - increase REF_BITS"); + return 0; + } + r->refs++; + return h; + } + + // cached + if(first_free != -1) + { + idx = first_free; + r = &res_array[idx]; + } + // search res_array for first entry + else + for(idx = 0; idx < res_array_cap; idx++, r++) + if(!r->tag) + break; + + // check if next entry is free + if(idx+1 < res_array_cap && !r[1].key) + first_free = idx+1; + else + first_free = -1; + + if(idx >= res_array_cap) + { + assert(!"too many open handles (increase IDX_BITS)"); + return 0; + } + + if(idx > max_idx) + max_idx = idx; + + static u32 tag; + if(++tag >= (1 << TAG_BITS)) + { + assert(!"tag overflow - may not notice stale handle reuse (increase TAG_BITS)"); + tag = 1; + } + + r->key = key; + r->tag = tag; + r->type = type; + + h = handle(idx); + hd = h_data(h, type); + return h; +} + + +Handle h_find(const u32 key, uint type, HDATA*& hd) +{ + int idx; + const Res* r = res_array; + + // already have first free entry cached - just search + if(first_free != -1) + { + for(idx = 0; idx <= max_idx; idx++, r++) + if(r->key == key && r->type == type) + goto found; + } + // search and remember first free entry (slower) + else + { + for(idx = 0; idx <= max_idx; idx++, r++) + if(!r->tag && first_free == -1) + first_free = idx; + else if(r->key == key && r->type == type) + goto found; + } + + // not found + return 0; + +found: + Handle h = handle(idx); + hd = h_data(h, type); + return h; +} + + +int h_free(const Handle h, const uint type) +{ + int idx = h_idx(h, type); + if(idx == -1) + return -1; + + Res& r = res_array[idx]; + if(--r.refs) + return 0; + + HDATA* hd = h_data(h, type); + if(hd && dtors[type]) + dtors[type](hd); + + r.key = 0; + r.tag = 0; + + if(first_free == -1 || idx < first_free) + first_free = idx; + + return 0; +} + + +HDATA* h_data(const Handle h, const uint type) +{ + int idx = h_idx(h, type); + if(idx == -1) + return 0; + + HDATA*& page = pages[idx / hdata_per_page]; + if(!page) + page = (HDATA*)malloc(PAGE_SIZE); + if(!page) + return 0; + + return &page[idx % hdata_per_page]; +} + + + +Handle res_load(const char* fn, uint type, DTOR dtor, void*& p, size_t& size, HDATA*& hd) +{ + p = 0; + size = 0; + hd = 0; + + u32 fn_hash = fnv_hash(fn, strlen(fn)); + // TODO: fn is usually a constant; pass fn len if too slow + + Handle h = h_alloc(fn_hash, type, dtor, hd); + if(!h || !hd) + return 0; + + Handle hf = vfs_open(fn); + int err = vfs_read(hf, p, size, 0); + vfs_close(hf); + if(err < 0) + return 0; + + hd->p = p; + hd->size = size; + + return h; +} + + + + diff --git a/source/res.h b/source/res.h new file mode 100755 index 0000000000..ec32363de0 --- /dev/null +++ b/source/res.h @@ -0,0 +1,60 @@ +// handle based caching resource manager +// +// Copyright (c) 2003 Jan Wassenberg +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// Contact info: +// Jan.Wassenberg@stud.uni-karlsruhe.de +// http://www.stud.uni-karlsruhe.de/~urkt/ + +#ifndef __RES_H__ +#define __RES_H__ + +#include "types.h" + + +enum +{ + RES_MEM = 1, + + RES_BIN = 2, + RES_TEX = 3, + RES_FONT = 4, + RES_ZIP = 5, + RES_VFILE = 6, + + NUM_RES_TYPES +}; + + +typedef unsigned long Handle; + +const int HDATA_INTERNAL_SIZE = 24; + +struct HDATA +{ + void* p; + size_t size; + u8 internal[HDATA_INTERNAL_SIZE]; +}; + +typedef void(*DTOR)(HDATA*); + +extern Handle h_alloc(u32 key, uint type, DTOR dtor, HDATA*& hd); +extern int h_free(Handle h, uint type = 0); +extern Handle h_find(u32 key, uint type, HDATA*& hd); +extern HDATA* h_data(Handle h, uint type); + +extern Handle res_load(const char* fn, uint type, DTOR dtor, void*& p, size_t& size, HDATA*& hd); + + +#endif // #ifndef __RES_H__ diff --git a/source/resource.h b/source/resource.h new file mode 100755 index 0000000000..cebbc0915b --- /dev/null +++ b/source/resource.h @@ -0,0 +1,22 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by assert_dlg.rc +// +#define IDD_DIALOG1 101 +#define IDC_EDIT1 1001 +#define IDC_COPY 1002 +#define IDC_CONTINUE 2000 +#define IDC_SUPPRESS 2001 +#define IDC_BREAK 2002 +#define IDC_EXIT 2003 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1003 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/source/tex.cpp b/source/tex.cpp new file mode 100755 index 0000000000..3946a5445a --- /dev/null +++ b/source/tex.cpp @@ -0,0 +1,694 @@ +// 2d texture format decoders +// +// Copyright (c) 2003 Jan Wassenberg +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// Contact info: +// Jan.Wassenberg@stud.uni-karlsruhe.de +// http://www.stud.uni-karlsruhe.de/~urkt/ + +// supported formats: DDS, TGA, PNG, JP2, BMP, RAW + +#include +#include +#include + +#include "tex.h" +#include "ogl.h" +#include "res.h" +#include "misc.h" + +#define NO_JP2 +#define NO_PNG + +#ifndef NO_JP2 +#include +#endif + +#ifndef NO_PNG +#include +#pragma comment(lib, "libpng.lib") +#endif + + +////////////////////////////////////////////////////////////////////////////// +// +// DDS +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef NO_DDS + +// converts 4 character string to u32 for easy comparison +// can't pass code as string, and use s[0]..s[3], because +// VC6/7 don't realize the macro is constant (and it's used in a switch{}) +#ifdef BIG_ENDIAN +#define FOURCC(a,b,c,d) ( ((u32)a << 24) | ((u32)b << 16) | \ + ((u32)c << 8 ) | ((u32)d << 0 ) ) +#else +#define FOURCC(a,b,c,d) ( ((u32)a << 0 ) | ((u32)b << 8 ) | \ + ((u32)c << 16) | ((u32)d << 24) ) +#endif + +// modified from ddraw header + +#pragma pack(push, 1) + +typedef struct { u32 lo, hi; } DDCOLORKEY; + +typedef struct +{ + u32 dwSize; // size of structure + u32 dwFlags; // pixel format flags + u32 dwFourCC; // (FOURCC code) + u32 dwRGBBitCount; // how many bits per pixel + u32 dwRBitMask; // mask for red bit + u32 dwGBitMask; // mask for green bits + u32 dwBBitMask; // mask for blue bits + u32 dwRGBAlphaBitMask; // mask for alpha channel +} +DDPIXELFORMAT; + +typedef struct +{ + u32 dwCaps; // capabilities of surface wanted + u32 dwCaps2; + u32 dwCaps3; + u32 dwCaps4; +} +DDSCAPS2; + +typedef struct +{ + u32 dwSize; // size of the DDSURFACEDESC structure + u32 dwFlags; // determines what fields are valid + u32 dwHeight; // height of surface to be created + u32 dwWidth; // width of input surface + u32 dwLinearSize; // surface size + u32 dwBackBufferCount; // number of back buffers requested + u32 dwMipMapCount; // number of mip-map levels requestde + u32 dwAlphaBitDepth; // depth of alpha buffer requested + u32 dwReserved; // reserved + void* lpSurface; // pointer to the associated surface memory + DDCOLORKEY unused[4]; // src/dst overlay, blt + DDPIXELFORMAT ddpfPixelFormat; // pixel format description of the surface + DDSCAPS2 ddsCaps; // direct draw surface capabilities + u32 dwTextureStage; // stage in multitexture cascade +} +DDSURFACEDESC2; + +#pragma pack(pop) + + +static inline bool dds_valid(const u8* ptr, size_t size) +{ + UNUSED(size) // only need first 4 chars + + return *(u32*)ptr == FOURCC('D','D','S',' '); +} + + +// TODO: DXT1a? +static int dds_load(const char* fn, const u8* ptr, size_t size, TEX* tex) +{ + const char* err = 0; + + const DDSURFACEDESC2* surf = (const DDSURFACEDESC2*)(ptr+4); + const u32 hdr_size = 4+sizeof(DDSURFACEDESC2); + if(size < hdr_size) + err = "header not completely read"; + else + { + const u32 w = read_le32(&surf->dwWidth); + const u32 h = read_le32(&surf->dwHeight); + const u32 ddsd_size = read_le32(&surf->dwSize); + const u32 img_size = read_le32(&surf->dwLinearSize); + const u32 mipmaps = read_le32(&surf->dwMipMapCount); + + const u8* img = ptr + hdr_size; + + uint fmt = 0; + switch(surf->ddpfPixelFormat.dwFourCC) // endian-independent + { + case FOURCC('D','X','T','1'): + fmt = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; + break; + case FOURCC('D','X','T','3'): + fmt = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + break; + case FOURCC('D','X','T','5'): + fmt = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + break; + } + + tex->width = w; + tex->height = h; + tex->fmt = fmt; + tex->ptr = img; + tex->img_size = img_size; + + if(sizeof(DDSURFACEDESC2) != ddsd_size) + err = "DDSURFACEDESC2 size mismatch"; + if(size < hdr_size + img_size) + err = "not completely loaded"; + if(mipmaps > 0) + err = "contains mipmaps"; + if(fmt == 0) + err = "invalid pixel format (not DXT{1,3,5})"; + } + + if(err) + { + printf("load_dds: %s: %s\n", fn, err); + return -1; + } + + return 0; +} + +#endif + + +////////////////////////////////////////////////////////////////////////////// +// +// TGA +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef NO_TGA + +static inline bool tga_valid(const u8* ptr, size_t size) +{ + UNUSED(size) + + // no color map; uncompressed grayscale or true color + return (ptr[1] == 0 && (ptr[2] == 2 || ptr[2] == 3)); +} + + +static int tga_load(const char* fn, const u8* ptr, size_t size, TEX* tex) +{ + const char* err = ""; + + const u8 img_id_len = ptr[0]; + const uint hdr_size = 18+img_id_len; + if(size < hdr_size) + err = "header not completely read"; + else + { + const u8 type = ptr[2]; + const u16 w = read_le16(ptr+12); + const u16 h = read_le16(ptr+14); + const u8 bpp = ptr[16]; + const u8 desc = ptr[17]; + + const u8 alpha_bits = desc & 0x0f; + + const u8* img = ptr + hdr_size; + const ulong img_size = (ulong)w * h * bpp / 8; + + // determine format + u32 fmt = 0; + // .. grayscale + if(type == 3) + { + if(bpp == 8) + fmt = GL_LUMINANCE; + else if(bpp == 16 && alpha_bits == 8) + fmt = GL_LUMINANCE_ALPHA; + } + // .. true color + else if(type == 2) + { + if(bpp == 16 || bpp == 24) + fmt = GL_BGR; + else if(bpp == 32 && alpha_bits == 8) + fmt = GL_BGRA; + } + + tex->width = w; + tex->height = h; + tex->fmt = fmt; + tex->ptr = img; + + if(!fmt) + err = "unknown format / invalid bpp"; + if(desc & 0x18) + err = "image is not bottom-up and left-to-right"; + if(size < hdr_size + img_size) + err = "size < image size"; + } + + if(err) + { + printf("load_tga: %s: %s\n", fn, err); + return -1; + } + + return 0; +} + +#endif + + +////////////////////////////////////////////////////////////////////////////// +// +// BMP +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef NO_BMP + +#pragma pack(push, 1) + +struct BITMAPFILEHEADER +{ + u16 bfType; // "BM" + u32 bfSize; // of file + u32 reserved; + u32 bfOffBits; // offset to image data +}; + +// BITMAPCOREHEADER + compression field +struct BITMAPCOREHEADER2 +{ + u32 biSize; + long biWidth; + long biHeight; + u16 biPlanes; // = 1 + u16 biBitCount; // bpp + u32 biCompression; +}; + +#pragma pack(pop) + +#define BI_RGB 0 // bch->biCompression + + +static inline bool bmp_valid(const u8* ptr, size_t size) +{ + UNUSED(size) + + // bfType == BM? (check single bytes => endian safe) + return ptr[0] == 'B' && ptr[1] == 'M'; +} + + +// requirements: bpp = 24, uncompressed, bottom up +static int bmp_load(const char* fn, const u8* ptr, size_t size, TEX* tex) +{ + const char* err = 0; + + BITMAPFILEHEADER* bfh = (BITMAPFILEHEADER*)ptr; + BITMAPCOREHEADER2* bch = (BITMAPCOREHEADER2*)(ptr+sizeof(BITMAPFILEHEADER)); + const int hdr_size = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPCOREHEADER2); + if(size < hdr_size) + err = "header not completely read"; + else + { + const long w = read_le32(&bch->biWidth); + const long h = read_le32(&bch->biHeight); + const u16 bpp = read_le16(&bch->biBitCount); + const u32 compress = read_le32(&bch->biCompression); + const u32 ofs = read_le32(&bfh->bfOffBits); + + const u8* img = ptr + ofs; + const u32 img_size = w * h * bpp/8; + + tex->width = w; + tex->height = h; + tex->ptr = img; + tex->fmt = GL_BGR; + + if(h < 0) + err = "top-down"; + if(compress != BI_RGB) + err = "compressed"; + if(bpp != 24) + err = "not 24 bpp"; + if(size < ofs+img_size) + err = "image not completely read"; + } + + if(err) + { + printf("load_bmp: %s: %s\n", fn, err); + return -1; + } + + return 0; +} +// TODO: no extra buffer needed here; dealloc? + +#endif + + +////////////////////////////////////////////////////////////////////////////// +// +// RAW +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef NO_RAW + +static inline bool raw_valid(const u8* p, size_t size) +{ + UNUSED(p) + UNUSED(size) + + return true; +} + + +// requirements: width = height = 2^n, bpp = {16|24|32} +static int raw_load(const char* fn, const u8* ptr, size_t size, TEX* tex) +{ + // 8bpp textures aren't supported, + // b/c it's impossible to differentiate 8bpp 2n*2n and 32bpp n*n + static GLenum fmts[5] = { 0, 0, GL_LUMINANCE_ALPHA, GL_RGB, GL_RGBA }; + for(int i = 2; i <= 4; i++) + { + u32 dim = (u32)sqrt(size/i); + if(size % i || // size is not a multiple of i components + !is_pow2(dim) || dim*dim*i != size) + continue; + + tex->width = tex->height = dim; + tex->fmt = fmts[i]; + tex->ptr = ptr; + + return 0; + } + + printf("load_raw: %s: %s\n", fn, "no matching format found"); + return -1; +} + +#endif + + +////////////////////////////////////////////////////////////////////////////// +// +// PNG +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef NO_PNG + +struct MemRange +{ + const u8* p; + size_t size; +}; + + +static void png_read_fn(png_struct* png_ptr, u8* data, png_size_t length) +{ + MemRange* const mr = (MemRange*)png_ptr->io_ptr; + mr->size -= length; + if(mr->size < 0) + png_error(png_ptr, "png_read_fn: no data remaining"); + + memcpy(data, mr->ptr, length); + mr->ptr += length; +} + + +static inline bool png_valid(const u8* ptr, size_t size) +{ + return png_sig_cmp((u8*)ptr, 0, min(size, 8)) == 0; +} + + +static int png_load(const char* fn, const u8* ptr, size_t size, TEX* tex) +{ + const char* err = 0; + + // allocate PNG structures + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); + if(!png_ptr) + return -1; + png_infop info_ptr = png_create_info_struct(png_ptr); + if(!info_ptr) + { + png_destroy_read_struct(&png_ptr, 0, 0); + return -1; + } + + // setup error handling + if(setjmp(png_jmpbuf(png_ptr))) + { +fail: + printf("load_png: %s: %s\n", fn, err? err : ""); + png_destroy_read_struct(&png_ptr, &info_ptr, 0); + return -1; + } + + MemRange mr = { ptr, size }; + png_set_read_fn(png_ptr, &mr, png_read_fn); + + png_read_info(png_ptr, info_ptr); + unsigned long width, height; + int bit_depth, color_type; + png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0); + + // can only handle 8 bits per channel + if(bit_depth != 8) + { + err = "bit depth != 8"; + goto fail; + } + + // allocate mem for image - rows point into buffer (sequential) + int pitch = png_get_rowbytes(png_ptr, info_ptr); + u8* img = (u8*)malloc(pitch * (height+1)); + u8** rows = (u8**)png_malloc(png_ptr, (height+1)*sizeof(void*)); + for(u32 i = 0; i < height+1; i++) + rows[i] = img + i*pitch; + + png_read_image(png_ptr, rows); + + png_read_end(png_ptr, 0); + png_destroy_read_struct(&png_ptr, &info_ptr, 0); + + // store info in ti + tex->width = width; tex->height = height; + tex->ptr = img; + + uint fmts[8] = { GL_LUMINANCE, 0, GL_RGB, 0, GL_LUMINANCE_ALPHA, 0, GL_RGBA, 0 }; + assert(color_type < 8); + tex->fmt = fmts[color_type]; + if(!tex->fmt) // <==> palette image + { + printf("load_png: %s: %s\n", fn, "palettes not supported"); + return -1; + } + + return 0; +} + +#endif + + +////////////////////////////////////////////////////////////////////////////// +// +// JP2 +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef NO_JP2 + +static inline bool jp2_valid(const u8* p, size_t size) +{ + static bool initialized; + if(!initialized) + { + jas_init(); + initialized = true; + } + + jas_stream_t* stream = jas_stream_memopen((char*)ptr, size); + return jp2_validate(stream) >= 0; +} + + +static int jp2_load(const char* fn, const u8* ptr, size_t size, TEX* tex) +{ + jas_stream_t* stream = jas_stream_memopen((char*)ptr, size); + jas_image_t* image = jas_image_decode(stream, -1, 0); + if(!image) + return -1; + + int num_cmpts = jas_image_numcmpts(image); + jas_matrix_t* matr[4] = {0}; + jas_seqent_t* rows[4] = {0}; + int width = jas_image_cmptwidth (image, 0); + int height = jas_image_cmptheight(image, 0); + int depth = jas_image_cmptprec (image, 0); + + if(depth != 8) + return -1; + + u8* img = (u8*)malloc(width*height*num_cmpts); + u8* out = img; + + int cmpt; + for(cmpt = 0; cmpt < num_cmpts; cmpt++) + matr[cmpt] = jas_matrix_create(1, width); + + for(int y = 0; y < height; y++) + { + for(cmpt = 0; cmpt < num_cmpts; cmpt++) + { + jas_image_readcmpt(image, cmpt, 0, y, width, 1, matr[cmpt]); + rows[cmpt] = jas_matrix_getref(matr[cmpt], 0, 0); + } + + for(int x = 0; x < width; x++) + for(cmpt = 0; cmpt < num_cmpts; cmpt++) + *out++ = *rows[cmpt]++; + } + + for(cmpt = 0; cmpt < num_cmpts; cmpt++) + jas_matrix_destroy(matr[cmpt]); + + tex->width = width; + tex->height = height; + tex->fmt = GL_RGB; + tex->ptr = img; + + return 0; +} + +#endif + + + + +static void tex_dtor(HDATA* hd) +{ + TEX* tex = (TEX*)hd->internal; + glDeleteTextures(1, &tex->id); +} + + +u32 tex_load(const char* fn, TEX* ptex) +{ + // load file + const u8* p; + size_t size; + HDATA* hd; + u32 h = res_load(fn, RES_TEX, tex_dtor, (void*&)p, size, hd); + if(h != 0 && !p) // already loaded + return h; + if(!p || size < 4) // is_* require a valid pointer, and >= 4 bytes + return 0; + + TEX* tex = (TEX*)hd->internal; + int err = -1; +#ifndef NO_DDS + if(dds_valid(p, size)) + err = dds_load(fn, p, size, tex); else +#endif +#ifndef NO_PNG + if(png_valid(p, size)) + err = png_load(fn, p, size, tex); else +#endif +#ifndef NO_JP2 + if(jp2_valid(p, size)) + err = jp2_load(fn, p, size, tex); else +#endif +#ifndef NO_BMP + if(bmp_valid(p, size)) + err = bmp_load(fn, p, size, tex); else +#endif +#ifndef NO_TGA + if(tga_valid(p, size)) + err = tga_load(fn, p, size, tex); else +#endif +#ifndef NO_RAW + if(raw_valid(p, size)) + err = raw_load(fn, p, size, tex); else +#endif + ; // make sure else chain is ended + + if(err < 0) + return err; + + uint id; + glGenTextures(1, &id); + tex->id = id; + + if(ptex) + *ptex = *tex; + + return h; +} + + +int tex_bind(u32 h) +{ + HDATA* hd = h_data(h, RES_TEX); + if(!hd) + { + glBindTexture(GL_TEXTURE_2D, 0); + return -1; + } + TEX* tex = (TEX*)hd->internal; + glBindTexture(GL_TEXTURE_2D, tex->id); + return 0; +} + + +uint tex_filter_quality = BILINEAR; // 1..6 +uint tex_bpp = 32; // 16 or 32 + +int tex_upload(const u32 h, const uint q_ovr, uint int_fmt) +{ + HDATA* hd = h_data(h, RES_TEX); + if(!hd) + return -1; + TEX* tex = (TEX*)hd->internal; + +// TODO: texture_rectangle or subtexture + if(!is_pow2(tex->width) || !is_pow2(tex->height)) + return 0; + + tex_bind(h); + + // filter quality + uint q = q_ovr? q_ovr : tex_filter_quality; + if(q > 6) + return 0; + const uint filters[7] = { 0, GL_NEAREST, GL_LINEAR, + GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_NEAREST, + GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_LINEAR }; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filters[q]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filters[min(q, 2)]); + + // internal format + if(!int_fmt) + { + if(tex->fmt == GL_RGBA || tex->fmt == GL_BGRA) + int_fmt = (tex_bpp == 32)? 4 : GL_RGBA4; + else + int_fmt = (tex_bpp == 32)? 3 : GL_RGB5; + } + + if(tex->fmt >= GL_COMPRESSED_RGB_S3TC_DXT1_EXT && + tex->fmt <= GL_COMPRESSED_RGBA_S3TC_DXT5_EXT) + glCompressedTexImage2DARB(GL_TEXTURE_2D, 0, tex->fmt, tex->width, tex->height, 0, tex->img_size, tex->ptr); + else + glTexImage2D(GL_TEXTURE_2D, 0, int_fmt, tex->width, tex->height, 0, tex->fmt, GL_UNSIGNED_BYTE, tex->ptr); + + return 0; +} diff --git a/source/tex.h b/source/tex.h new file mode 100755 index 0000000000..8c3d1ff34d --- /dev/null +++ b/source/tex.h @@ -0,0 +1,48 @@ +// +// Copyright (c) 2002 Jan Wassenberg +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// Contact info: +// Jan.Wassenberg@stud.uni-karlsruhe.de +// http://www.stud.uni-karlsruhe.de/~urkt/ + +#ifndef __TEX_H__ +#define __TEX_H__ + +#include "types.h" + + +struct TEX +{ + uint width; + uint height; + uint fmt; + const u8* ptr; + uint id; + uint img_size; // currently only used for S3TC +}; + + +extern u32 tex_load(const char* fn, TEX* ti = 0); + +extern int tex_bind(u32 h); + + +const int BILINEAR = 2; +const int TRINILEAR = 6; + +extern uint tex_filter_quality; // 1..6; default: BILINEAR +extern uint tex_bpp; // 16 or 32; default: 32 + +extern int tex_upload(u32 h, uint filter_quality = 0, uint internal_fmt = 0); + +#endif // __TEX_H__ diff --git a/source/time.cpp b/source/time.cpp new file mode 100755 index 0000000000..d4cd034c0c --- /dev/null +++ b/source/time.cpp @@ -0,0 +1,167 @@ +// platform indepentend high resolution timer +// +// Copyright (c) 2003 Jan Wassenberg +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// Contact info: +// Jan.Wassenberg@stud.uni-karlsruhe.de +// http://www.stud.uni-karlsruhe.de/~urkt/ + +#include +#include + +#include "detect.h" +#include "time.h" +#include "types.h" + +#ifdef _WIN32 +#include "win.h" +#endif + + +// high resolution (> 1 µs) timestamp [s], starting at or near 0 s. +// +// uses TSC on single processor x86 desktop systems unless NO_TSC is defined, +// otherwise platform specific timers (QueryPerformanceCounter, gettimeofday). +double get_time() +{ + static double to_s; + double t; + +#if defined(_M_IX86) && !defined(NO_TSC) +extern u64 rdtsc(); + static int use_tsc = -1; + static u64 tsc_start; + + // spaghetti code for minimum timing overhead +first_tsc: + if(use_tsc == 1) + return (__int64)(rdtsc() - tsc_start) * to_s; + // VC6 can't convert u64 -> double; we don't need full range anyway + // don't know yet + if(use_tsc == -1) + // don't use yet - need a time reference for CPU freq calculation. + if(cpu_freq != 0.0f) + // use only on single processor desktop systems + // (otherwise CPU freq may change, clocks may get out of sync) + if(cpus == 1 && !is_notebook && (cpu_caps & TSC)) + { + use_tsc = 1; + to_s = 1.0 / cpu_freq; + tsc_start = rdtsc(); + goto first_tsc; // using the other timers now would trash to_s + } + else + use_tsc = 0; +#endif + +#ifdef _WIN32 + + static LARGE_INTEGER start; + LARGE_INTEGER i; + + if(!to_s) + { + QueryPerformanceFrequency(&i); + to_s = 1.0 / i.QuadPart; + + QueryPerformanceCounter(&start); + } + + QueryPerformanceCounter(&i); + t = (i.QuadPart - start.QuadPart) * to_s; + +#else + + static struct timeval start; + struct timeval tv; + + if(!start.tv_sec) + gettimeofday(&start, 0); + + gettimeofday(&tv, 0); + t = (tv.tv_sec - start.tv_sec) + (tv.tv_usec - start.tv_usec)*1e-6; + +#endif + + return t; +} + + + + +// calculate fps (call once per frame) +// several smooth filters: +// - throw out single spikes / dips +// - average via small history buffer +// - update final value iff the difference (% or absolute) is too great, +// or if the change is consistent with the trend over the last few frames. +// +// => less fluctuation, but rapid tracking. +// filter values are tuned for 100 FPS. + +int fps = 0; + +void calc_fps() +{ + // history buffer - smooth out slight variations +#define H 10 // # buffer entries + static float fps_sum = 0; // sum of last H frames' cur_fps + static float fps_hist[H]; // last H frames' cur_fps + // => don't need to re-average every time + static uint head = 0; // oldest entry in fps_hist + // must be unsigned, b/c we do (head-1)%H + + // get elapsed time [s] since last frame; approximate current fps + static double last_t; + double t = get_time(); + float cur_fps = 30.0f; // start value => history converges faster + if(last_t != 0.0) + cur_fps = 1.0f / (float)(t-last_t); // = 1 / elapsed time + last_t = t; + + // calculate fps activity over 3 frames (used below to prevent fluctuation) + // -1: decreasing, +1: increasing, 0: neither or fluctuating + float h1 = fps_hist[(head-1)%H]; // last frame's cur_fps + float h2 = fps_hist[(head-2)%H]; // 2nd most recent frame's cur_fps + + int trend = 0; + if(h2 > h1 && h1 > cur_fps) // \ + trend = -1; + else if(cur_fps < h1 && h1 < h2) // / + trend = 1; + + // ignore onetime skips in fps (probably page faults or similar) + static int bad = 0; // bad > 0 <==> last value was skipped + if(fabs(h1-cur_fps) > .05f*h1) // > 5% difference + { + // first 'bad' value: don't update fps_hist/fps; otherwise, reset bad + if(!bad++) + return; + } + else + bad = 0; + + // remove oldest cur_fps value in fps_hist from the sum + // and add cur_fps; also insert cur_fps in fps_hist + fps_sum -= fps_hist[head]; + fps_sum += (fps_hist[head] = cur_fps); + head = (head+1)%H; + + // update fps counter if update threshold is exceeded + float avg_fps = fps_sum / H; + + if((trend > 0 && (avg_fps > fps || avg_fps-fps < -4.f)) || // going up, or large drop + (trend < 0 && (avg_fps < fps || avg_fps-fps > 4.f)) || // going down, or large raise + (fabs(fps-avg_fps) > min(5.f, 0.05f*fps))) // significant difference + fps = (int)avg_fps; +} diff --git a/source/time.h b/source/time.h new file mode 100755 index 0000000000..591fe7e654 --- /dev/null +++ b/source/time.h @@ -0,0 +1,44 @@ +// platform indepentend high resolution timer +// +// Copyright (c) 2003 Jan Wassenberg +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// Contact info: +// Jan.Wassenberg@stud.uni-karlsruhe.de +// http://www.stud.uni-karlsruhe.de/~urkt/ + +#ifndef __TIME_H__ +#define __TIME_H__ + +#ifdef __cplusplus +extern "C" { +#endif + + +// high resolution (> 1 µs) timestamp [s], starting at or near 0 s. +extern double get_time(); + + +// calculate fps (call once per frame) +// several smooth filters (tuned for ~100 FPS) +// => less fluctuation, but rapid tracking + +extern int fps; + +extern void calc_fps(); + + +#ifdef __cplusplus +} +#endif + +#endif // #ifndef __TIME_H__ diff --git a/source/types.h b/source/types.h new file mode 100755 index 0000000000..018a66e168 --- /dev/null +++ b/source/types.h @@ -0,0 +1,18 @@ +#ifndef __TYPES_H__ +#define __TYPES_H__ + +// defines instead of typedefs so we can #undef conflicting decls + +#define uint unsigned int +#define ulong unsigned long + +#define int8 signed char +#define int16 short +#define int32 long + +#define u8 unsigned char +#define u16 unsigned short +#define u32 unsigned long // compatibility with Win32 DWORD +#define u64 unsigned __int64 + +#endif // #ifndef __TYPES_H__ diff --git a/source/unzip.cpp b/source/unzip.cpp new file mode 100755 index 0000000000..a095f5cc78 --- /dev/null +++ b/source/unzip.cpp @@ -0,0 +1,351 @@ +// ZIP archiving (on top of ZLib) +// +// Copyright (c) 2003 Jan Wassenberg +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// Contact info: +// Jan.Wassenberg@stud.uni-karlsruhe.de +// http://www.stud.uni-karlsruhe.de/~urkt/ + +#include +#include +#include + +#include + +#include "unzip.h" +#include "posix.h" +#include "misc.h" +#include "res.h" +#include "mem.h" + +#ifdef _MSC_VER +#pragma comment(lib, "zlib.lib") +#endif + + +static const char ecdr_id[] = "PK\5\6"; // End of Central Directory Header identifier +static const char cdfh_id[] = "PK\1\2"; // Central File Header identifier +static const char lfh_id[] = "PK\3\4"; // Local File Header identifier + + +// Location and size of an archived file +struct File +{ + u32 ofs; + u32 csize; // bit 31 = compression method (1: deflate; 0: stored) + u32 ucsize; +}; + + +struct ZIP +{ + int fd; + + // file lookup + u32 num_files; + u32* fn_hashs; // split for more efficient search + File* files; + u32 last_file; // index of last file we found (speed up lookups of sequential files) +}; + + +static void zip_dtor(HDATA* hd) +{ + ZIP* const z = (ZIP*)hd->internal; + + close(z->fd); + z->fd = -1; +} + + +// open and return a handle to the zip archive indicated by +Handle zopen(const char* const fn) +{ + const u32 fn_hash = fnv_hash(fn, strlen(fn)); + + // already loaded? + HDATA* hd; + Handle h = h_find(fn_hash, RES_ZIP, hd); + if(h) + return h; + + // file size + struct stat stat_buf; + if(stat(fn, &stat_buf)) + return 0; + const size_t size = stat_buf.st_size; + if(size < 4) // ECDR scan below would overrun + return 0; + + // map zip file (easy access while getting list of files) + const int fd = open(fn, O_RDONLY); + if(fd < 0) + return 0; + u8* zfile = (u8*)mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0); + if(zfile == MAP_FAILED) + return 0; + + // find end of central dir record + // by scanning last 66000 bytes of file for ecdr_id magic + // (zip comment <= 65535 bytes, sizeof(ECDR) = 22, add some for safety) + // if the zip file is < 66000 bytes, scan the whole file + + u32 bytes_left = min(size, 66000); + u8* ecdr = zfile + size - bytes_left; + + while(bytes_left) + { + if(*(u32*)ecdr == *(u32*)&ecdr_id) + break; + + // check next 4 bytes (non aligned!!) + ecdr++; + bytes_left--; + } + + { + // reached EOF and still haven't found the ECDR identifier + if(bytes_left == 0) + goto fail; + + // read ECDR + u16 num_files = read_le16(ecdr+10); + u32 cd_ofs = read_le32(ecdr+16); + + h = h_alloc(fn_hash, RES_ZIP, zip_dtor, hd); + if(!h || !hd) + goto fail; + + ZIP* const zip = (ZIP*)hd->internal; + zip->fd = fd; + zip->fn_hashs = ( u32*)mem_alloc(num_files * sizeof(u32 ), MEM_HEAP, 64); + zip->files = (File*)mem_alloc(num_files * sizeof(File), MEM_HEAP, 64); + if(!zip->fn_hashs || !zip->files) + { + h_free(h, RES_ZIP); + mem_free(zip->fn_hashs); + mem_free(zip->files); + goto fail; + } + zip->num_files = num_files; + zip->last_file = 0; + + // cache file list for faster lookups + // currently linear search, comparing filename hash. + // if too slow, use hash table + const u8* cdfh = zfile+cd_ofs; + u32* hs = zip->fn_hashs; + File* f = zip->files; + for(uint i = 0; i < zip->num_files; i++) + { + // read CDFH + if(*(u32*)cdfh != *(u32*)cdfh_id) + continue; + const u32 csize = read_le32(cdfh+20); + const u32 ucsize = read_le32(cdfh+24); + const u16 fn_len = read_le16(cdfh+28); + const u16 e_len = read_le16(cdfh+30); + const u16 c_len = read_le16(cdfh+32); + const u32 lfh_ofs = read_le32(cdfh+42); + const u8 method = cdfh[10]; + + // read LFH + const u8* const lfh = zfile + lfh_ofs; + if(*(u32*)lfh != *(u32*)lfh_id) + continue; + const u16 lfh_fn_len = read_le16(lfh+26); + const u16 lfh_e_len = read_le16(lfh+28); + const char* lfh_fn = (const char*)lfh+30; + + *hs++ = fnv_hash(lfh_fn, lfh_fn_len); + f->ofs = lfh_ofs + 30 + lfh_fn_len + lfh_e_len; + f->csize = csize | ((method == 8)? BIT(31) : 0); + f->ucsize = ucsize; + f++; + + (int&)cdfh += 46 + fn_len + e_len + c_len; + } + } + +// unmap file and return 0 +fail: + + munmap(zfile, size); + // actual reading is done with aio + + return h; +} + + +// close the zip file zd +void zclose(const Handle h) +{ + h_free(h, RES_ZIP); +} + + + + + +// make file in zip available +// returns a pointer to the data, and optionally its size (0 on error) +// +// returns 0 on error. +int zread(Handle h, const char* fn, void*& p, size_t& size, size_t ofs) +{ + // size = 0 if we fail + size = 0; + + HDATA* hd = h_data(h, RES_ZIP); + if(!hd) + return 0; + ZIP* zip = (ZIP*)hd->internal; + + // find its File descriptor + u32 fn_hash = fnv_hash(fn, strlen(fn)); + uint i = zip->last_file+1; + if(i >= zip->num_files || zip->fn_hashs[i] != fn_hash) + { + for(i = 0; i < zip->num_files; i++) + if(zip->fn_hashs[i] == fn_hash) + break; + if(i == zip->num_files) + return 0; + + zip->last_file = i; + } + const File* const f = &zip->files[i]; + + const bool deflated = (f->csize & BIT(31)) != 0; + ofs += f->ofs; + const u32 csize = f->csize & ~BIT(31); + const u32 ucsize = f->ucsize; + + aiocb cbs[2]; + memset(cbs, 0, sizeof(cbs)); + cbs[0].aio_fildes = cbs[1].aio_fildes = zip->fd; + + // decompress only: + void* read_bufs = 0; + const int BLOCK_SIZE = 64*KB; + int active_read = 0; + z_stream stream; + + if(deflated) + { + memset(&stream, 0, sizeof(stream)); + if(inflateInit2(&stream, -MAX_WBITS) != Z_OK) + return -1; + // -MAX_WBITS indicates no zlib header present + + read_bufs = mem_alloc(BLOCK_SIZE*2, MEM_HEAP, 64*KB); + if(!read_bufs) + return -1; + + cbs[0].aio_buf = read_bufs; + cbs[1].aio_buf = (u8*)read_bufs + BLOCK_SIZE; + } + + void* out_mem = mem_alloc(ucsize, MEM_HEAP, 64*KB); + + // pos in output buffer when reading uncompressed data + u8* out_pos = (u8*)out_mem; + + stream.next_out = (u8*)out_mem; + stream.avail_out = ucsize; + + + long cbytes_left = csize; + + bool first = true; + bool done = false; + + for(;;) + { + aiocb* cb = &cbs[active_read]; + + // start reading next block + if(cbytes_left) + { + // align to 64 KB for speed + u32 rsize = min(64*KB - (ofs & 0xffff), cbytes_left); + + // if uncompressed, read directly into output buffer + if(!deflated) + { + cb->aio_buf = out_pos; + out_pos += rsize; + } + + cb->aio_offset = ofs; + cb->aio_nbytes = rsize; + aio_read(cb); + + ofs += rsize; + cbytes_left -= rsize; + assert(cbytes_left >= 0); // read size clamped => never negative + } + + active_read ^= 1; + + // process block read in previous iteration + if(first) + first = false; + else + { + // wait for read to complete + cb = &cbs[active_read]; + while(aio_error(cb) == -EINPROGRESS) + aio_suspend(&cb, 1, 0); + + ssize_t bytes_read = aio_return(cb); + + // inflate + if(deflated) + { + stream.next_in = (u8*)cb->aio_buf; + stream.avail_in = bytes_read; + inflate(&stream, 0); + } + } + + // one more iteration to process the last pending block + if(done) + break; + if(!cbytes_left) + done = true; + } + + if(deflated) + { + inflateEnd(&stream); + mem_free(read_bufs); + + if(stream.total_in != csize || stream.total_out != ucsize) + { +// read_bufs is not yet allocated, or already freed + mem_free(out_mem); + return 0; + } + } + + p = out_mem; + size = ucsize; + + return 0; +} + + +//1 cache per zip - res layer doesnt know our internals (cache entry=ofs, csize,ucsize) +//cache=array (allow finding next file quickly; need some mechanism for faster random access?) +//sync is not a problem - zip file won't be updated during gameplay because it's still open diff --git a/source/unzip.h b/source/unzip.h new file mode 100755 index 0000000000..3d1d3a582e --- /dev/null +++ b/source/unzip.h @@ -0,0 +1,33 @@ +// ZIP archiving (on top of ZLib) +// +// Copyright (c) 2003 Jan Wassenberg +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// Contact info: +// Jan.Wassenberg@stud.uni-karlsruhe.de +// http://www.stud.uni-karlsruhe.de/~urkt/ + +#ifndef __UNZIP_H__ +#define __UNZIP_H__ + + +#include "res.h" + +// open and return a handle to the zip archive indicated by +extern Handle zopen(const char* fn); + +extern void zclose(Handle h); + +extern int zread(Handle h, const char* fn, void*& p, size_t& size, size_t ofs); + + +#endif // #ifndef __UNZIP_H__ diff --git a/source/vfs.cpp b/source/vfs.cpp new file mode 100755 index 0000000000..56815fe578 --- /dev/null +++ b/source/vfs.cpp @@ -0,0 +1,169 @@ +#include +#include +#include + +#include "posix.h" +#include "unzip.h" +#include "misc.h" +#include "vfs.h" +#include "mem.h" + + +struct PATH +{ + Handle zip; + const char* path; + struct PATH* next; +}; +static PATH* path_list; + + + +int vfs_goto_app_dir(char* argv0) +{ + if(access(argv0, X_OK) != 0) + return -1; + + char path[PATH_MAX+1]; + path[PATH_MAX] = 0; + if(!realpath(argv0, path)) + return -1; + + // remove executable name + char* fn = strrchr(path, DIR_SEP); + if(!fn) + return -1; + *fn = 0; + + return chdir(path); +} + + +// TODO: check CD drives? +int vfs_add_search_path(const char* p) +{ + // copy path string - need to append .zip / save a copy if dir + const size_t path_len = strlen(p); + char* path = (char*)mem_alloc(path_len+4+1); + if(!p) + return -1; + strcpy(path, p); + + // ZIP file? + strcpy(path+path_len, ".zip"); // append zip extension + Handle zip = zopen(path); + + // dir? + path[path_len] = 0; // remove .zip extension + DIR* dir = opendir(path); + if(dir) + closedir(dir); + else + mem_free(path); + + // neither + if(!zip && !dir) + return -1; + + PATH* entry = (PATH*)mem_alloc(sizeof(PATH)); + if(!entry) + return -1; + entry->next = path_list; + path_list = entry; + entry->zip = zip; + entry->path = dir? path : 0; + + return 0; +} + + +int vfs_remove_first_path() +{ + PATH* const p = path_list; + if(!p) + return -1; + + mem_free((void*)p->path); + path_list = p->next; + zclose(p->zip); + mem_free(p); + + return 0; +} + + +static void vfile_dtor(HDATA* hd) +{ +} + + +Handle vfs_open(const char* fn) +{ + char path[PATH_MAX+1]; path[PATH_MAX] = 0; + + // for each search path: + for(PATH* entry = path_list; entry; entry = entry->next) + { + // dir - memory map the file + { + if(!entry->path) + goto not_dir; + struct stat stat_buf; + snprintf(path, PATH_MAX, "%s%c%s", entry->path, DIR_SEP, fn); + if(stat(path, &stat_buf) != 0) + goto not_dir; + int fd = open(path, O_RDONLY); + if(fd < 0) + goto not_dir; + size_t size = stat_buf.st_size; + void* p = mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0); + close(fd); + if(p != MAP_FAILED) + { + HDATA* hd; + Handle h = h_alloc(0, RES_VFILE, vfile_dtor, hd); + if(h && hd) + { + hd->p = p; + hd->size = size; + return h; + } + } + } +not_dir: + + // archive + if(entry->zip) + return entry->zip; + } + + // not found + return 0; +} + + +int vfs_read(Handle h, void*& p, size_t& size, size_t ofs) +{ + p = 0; + size = 0; + + HDATA* hd = h_data(h, RES_VFILE); + if(hd) + { + p = (u8*)hd->p + ofs; + if(!size || size > hd->size) + size = hd->size; + return 0; + } +// else +// return zread(h, fn, p, size, ofs); +return -1; +} + + +int vfs_close(Handle h) +{ + h_free(h, RES_ZIP); + h_free(h, RES_VFILE); + return 0; +} diff --git a/source/vfs.h b/source/vfs.h new file mode 100755 index 0000000000..928ac57fff --- /dev/null +++ b/source/vfs.h @@ -0,0 +1,8 @@ + +extern int vfs_add_search_path(const char* path); +extern int vfs_remove_first_path(); + +extern Handle vfs_open(const char* fn); +extern int vfs_close(Handle h); +extern int vfs_read(Handle h, void*& p, size_t& size, size_t ofs = 0); + diff --git a/source/win.h b/source/win.h new file mode 100755 index 0000000000..3dd43a192d --- /dev/null +++ b/source/win.h @@ -0,0 +1,5 @@ +#define _WINSOCKAPI_ // sockets already defined by posix.h +#define WIN32_LEAN_AND_MEAN +#define VC_EXTRALEAN +#define _WIN32_WINNT 0x0400 // needed for e.g. mousewheel support +#include diff --git a/source/wsdl.cpp b/source/wsdl.cpp new file mode 100755 index 0000000000..aad0e330a7 --- /dev/null +++ b/source/wsdl.cpp @@ -0,0 +1,547 @@ +/* + * emulation of a subset of SDL and GLUT for Win32 + * + * Copyright (c) 2002 Jan Wassenberg + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * Contact info: + * Jan.Wassenberg@stud.uni-karlsruhe.de + * http://www.stud.uni-karlsruhe.de/~urkt/ + */ + +#include +#include + +#include + +#include "wsdl.h" +#include "win.h" +#include "misc.h" + + +/* state */ +static bool app_active; /* is window active & on top? + if not, msg loop should yield */ +static bool fullscreen; /* in fullscreen mode? + if so, restore mode when app is deactivated */ + +HWND hWnd = 0; /* available to the app for ShowWindow calls, etc. */ + +static DEVMODE dm; /* current video mode */ +static HDC hDC; +static HGLRC hGLRC; + +static int z_depth = 24; /* depth buffer size; set via SDL_GL_SetAttribute */ + +static u16 mouse_x, mouse_y; + + + +/* + * shared msg handler + * SDL and GLUT have separate pumps; messages are handled there + */ +static LRESULT CALLBACK wndproc(HWND hWnd, unsigned int uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case WM_PAINT: + PAINTSTRUCT ps; + BeginPaint(hWnd, &ps); + EndPaint(hWnd, &ps); + return 0; + + case WM_ERASEBKGND: + return 0; + + // prevent screensaver / monitor power off + case WM_SYSCOMMAND: + if(wParam == SC_SCREENSAVE || wParam == SC_MONITORPOWER) + return 0; + break; + + case WM_ACTIVATE: + app_active = (wParam & 0xffff) != 0; + + if(fullscreen) + { + if(app_active) + ChangeDisplaySettings(&dm, CDS_FULLSCREEN); + else + ChangeDisplaySettings(0, 0); + } + break; + + case WM_CLOSE: + exit(0); + } + + return DefWindowProc(hWnd, uMsg, wParam, lParam); +} + + +int SDL_PollEvent(SDL_Event* ev) /* ev must be valid */ +{ + /* windows messages */ + MSG msg; + while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + + /* key */ + if(msg.message == WM_KEYUP || msg.message == WM_KEYDOWN) + { + ev->type = (u8)((msg.message == WM_KEYUP)? SDL_KEYUP : SDL_KEYDOWN); + ev->key.keysym.sym = (SDLKey)msg.wParam; + return 1; + } + + if(msg.message == WM_SYSCOMMAND) + ev->type = 0; + + /* mouse click */ + uint button; + for(button = 0; button < 3; button++) + { + if(msg.message == button+WM_LBUTTONDOWN) + ev->type = SDL_MOUSEBUTTONDOWN; + else if(msg.message == button+WM_LBUTTONUP) + ev->type = SDL_MOUSEBUTTONUP; + } + if(button < 3) + { + ev->button.button = (u8)button; + ev->button.x = (u16)(msg.lParam & 0xffff); + ev->button.y = (u16)((msg.lParam >> 16) & 0xffff); + return 1; + } + + /* active */ + if(msg.message == WM_ACTIVATE) + { + ev->type = SDL_ACTIVE; + ev->active.gain = app_active; + ev->active.state = 0; + return 1; + } + } + + /* + * mouse motion + * + * don't use DirectInput, because we want to respect the user's mouse sensitivity + * Windows messages are laggy, so poll instead. + */ + POINT p; + GetCursorPos(&p); + if(mouse_x != p.x || mouse_y != p.y) + { + ev->type = SDL_MOUSEMOTION; + ev->motion.x = mouse_x = (u16)p.x; + ev->motion.y = mouse_y = (u16)p.y; + return 1; + } + + return 0; +} + + + +int SDL_GL_SetAttribute(SDL_GLattr attr, int value) +{ + if(attr == SDL_GL_DEPTH_SIZE) + z_depth = value; + + return 0; +} + + +/* + * set video mode wxh:bpp if necessary. + * w = h = bpp = 0 => no change. + */ +int SDL_SetVideoMode(int w, int h, int bpp, u32 flags) +{ + fullscreen = (flags & SDL_FULLSCREEN); + + /* get current mode settings */ + dm.dmSize = sizeof(DEVMODE); + EnumDisplaySettings(0, ENUM_CURRENT_SETTINGS, &dm); + int cur_w = dm.dmPelsWidth, cur_h = dm.dmPelsHeight; + int cur_bpp = dm.dmBitsPerPel; + + /* + * check if mode needs to be changed + * (could split this out, but depends on fullscreen and dm) + */ + if(w != 0 && h != 0 && bpp != 0) + if(/* wrong bit depth */ + (bpp != cur_bpp) || + /* higher res mode needed */ + (w > cur_w || h > cur_h) || + /* fullscreen, and not exact mode */ + (fullscreen && (w != cur_w || h != cur_h))) + { + dm.dmPelsWidth = w; + dm.dmPelsHeight = h; + dm.dmBitsPerPel = bpp; + dm.dmFields = DM_PELSWIDTH|DM_PELSHEIGHT|DM_BITSPERPEL; + } + // mode set at first WM_ACTIVATE + + /* + * window init + * create new window every time (instead of once at startup), 'cause + * pixel format isn't supposed to be changed more than once + */ + + HINSTANCE hInst = GetModuleHandle(0); + + /* register window class */ + static WNDCLASS wc; + wc.style = CS_OWNDC; + wc.lpfnWndProc = wndproc; + wc.lpszClassName = "ogl"; + wc.hInstance = hInst; + RegisterClass(&wc); + + hWnd = CreateWindowEx(0, "ogl", APP_NAME, WS_POPUP|WS_VISIBLE, 0, 0, dm.dmPelsWidth, dm.dmPelsHeight, 0, 0, hInst, 0); + if(!hWnd) + return 0; + + hDC = GetDC(hWnd); + + /* set pixel format */ + static PIXELFORMATDESCRIPTOR pfd = + { + sizeof(PIXELFORMATDESCRIPTOR), + 1, + PFD_SUPPORT_OPENGL|PFD_DRAW_TO_WINDOW|PFD_DOUBLEBUFFER, + PFD_TYPE_RGBA, + (BYTE)bpp, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + (BYTE)z_depth, + 0, 0, + PFD_MAIN_PLANE, + 0, 0, 0, 0 + }; + + int pf = ChoosePixelFormat(hDC, &pfd); + if(!SetPixelFormat(hDC, pf, &pfd)) + return 0; + + hGLRC = wglCreateContext(hDC); + if(!hGLRC) + return 0; + + if(!wglMakeCurrent(hDC, hGLRC)) + return 0; + + return 1; +} + + +inline void SDL_GL_SwapBuffers() +{ + SwapBuffers(hDC); +} + + + +static SDL_VideoInfo video_info; + + +#ifdef DDRAW7 +#include +#ifdef _MSC_VER +#pragma comment(lib, "ddraw.lib") +#pragma comment(lib, "dxguid.lib") +#endif +#endif + + +int SDL_Init(u32 flags) +{ + UNUSED(flags) + +#ifdef DDRAW7 + LPDIRECTDRAW7 dd; + DirectDrawCreateEx(0, (void**)&dd, IID_IDirectDraw7, 0); + static DDSCAPS2 caps; + caps.dwCaps = DDSCAPS_TEXTURE | DDSCAPS_VIDEOMEMORY | DDSCAPS_LOCALVIDMEM; + dd->GetAvailableVidMem(&caps, (DWORD*)&video_info.video_mem, 0); + dd->Release(); +#endif + + dm.dmSize = sizeof(DEVMODE); + EnumDisplaySettings(0, ENUM_CURRENT_SETTINGS, &dm); + + return 0; +} + + +void SDL_Quit() +{ + DestroyWindow(hWnd); + + wglMakeCurrent(0, 0); + wglDeleteContext(hGLRC); + ChangeDisplaySettings(0, 0); +} + + +SDL_VideoInfo* SDL_GetVideoInfo() +{ + return &video_info; +} + + +SDL_Surface* SDL_GetVideoSurface() +{ + static SDL_Surface surf; + surf.w = dm.dmPelsWidth; + surf.h = dm.dmPelsHeight; + return &surf; +} + +__declspec(naked) u32 SDL_GetTicks() +{ +__asm jmp dword ptr [GetTickCount] +} + + +void* SDL_GL_GetProcAddress(const char* name) +{ + return wglGetProcAddress(name); +} + + +SDL_sem* SDL_CreateSemaphore(int cnt) +{ + return (SDL_sem*)CreateSemaphore(0, cnt, 0x7fffffff, 0); +} + +void __stdcall SDL_DestroySemaphore(SDL_sem*) +{ +__asm jmp dword ptr [CloseHandle] +} + +int SDL_SemPost(SDL_sem* sem) +{ + return ReleaseSemaphore(sem, 1, 0); +} + +int SDL_SemWait(SDL_sem* sem) +{ + return WaitForSingleObject(sem, INFINITE); +} + +SDL_Thread* SDL_CreateThread(int(*func)(void*), void* param) +{ + return (SDL_Thread*)_beginthread((void(*)(void*))func, 0, param); +} + + +int SDL_KillThread(SDL_Thread* thread) +{ + return TerminateThread(thread, 0); +} + + +__declspec(naked) int __stdcall SDL_WarpMouse(int, int) +{ +__asm jmp dword ptr [SetCursorPos] +} + + + + + + + + + + + + + + + + + + + + + + +static bool need_redisplay; /* display callback should be called in next main loop iteration */ + + + +/* glut callbacks */ +static void (*idle)(); +static void (*display)(); +static void (*key)(int, int, int); +static void (*special)(int, int, int); +static void (*mouse)(int, int, int, int); + +void glutIdleFunc(void (*func)()) +{ idle = func; } + +void glutDisplayFunc(void (*func)()) +{ display = func; } + +void glutKeyboardFunc(void (*func)(int, int, int)) +{ key = func; } + +void glutSpecialFunc(void (*func)(int, int, int)) +{ special = func; } + +void glutMouseFunc(void (*func)(int, int, int, int)) +{ mouse = func; } + + + + + + + + + +void glutInit(int* argc, char* argv[]) +{ + UNUSED(argc) + UNUSED(argv) + + SDL_Init(0); + atexit(SDL_Quit); +} + + +int glutGet(int arg) +{ + if(arg == GLUT_ELAPSED_TIME) + return GetTickCount(); + + dm.dmSize = sizeof(DEVMODE); + EnumDisplaySettings(0, ENUM_CURRENT_SETTINGS, &dm); + + if(arg == GLUT_SCREEN_WIDTH) + return dm.dmPelsWidth; + if(arg == GLUT_SCREEN_HEIGHT) + return dm.dmPelsHeight; + + return 0; +} + + +static int w, h, bpp, refresh; + +int glutGameModeString(const char* str) +{ + /* default = "don't care", in case string doesn't specify all values */ + w = 0, h = 0, bpp = 0, refresh = 0; + + sscanf(str, "%dx%d:%d@%d", &w, &h, &bpp, &refresh); + + return 1; +} + + +/* + */ +int glutEnterGameMode() +{ + return SDL_SetVideoMode(w, h, bpp, SDL_OPENGL|SDL_FULLSCREEN); +} + + + +inline void glutPostRedisplay() +{ + need_redisplay = true; +} + + +void glutSetCursor(int cursor) +{ + SetCursor(LoadCursor(NULL, (const char*)cursor)); +} + + + +/* + * GLUT message handler + * message also goes to the shared wndproc + * + * not done in wndproc to separate GLUT and SDL; + * split out of glutMainLoop for clarity. + */ +static void glut_process_msg(MSG* msg) +{ + switch(msg->message) + { + case WM_PAINT: + need_redisplay = true; + break; + + case WM_CHAR: + if(key) + key((int)msg->wParam, mouse_x, mouse_y); + break; + + case WM_KEYDOWN: + if(special) + special((int)msg->wParam, mouse_x, mouse_y); + break; + + case WM_LBUTTONDOWN: + case WM_RBUTTONDOWN: /* FIXME: only left/right clicks, assume GLUT_LEFT|RIGHT_BUTTON == 0, 1 */ + if(mouse) + mouse(msg->message == WM_RBUTTONDOWN, GLUT_DOWN, (int)(msg->lParam & 0xffff), (int)(msg->lParam >> 16)); + break; + + case WM_MOUSEWHEEL: + if(mouse) + mouse(GLUT_MIDDLE_BUTTON, ((short)(msg->wParam >> 16) > 0)? GLUT_UP : GLUT_DOWN, 0, 0); + break; + } +} + + +void glutMainLoop() +{ + for(;;) + { + if(!app_active) + WaitMessage(); + + MSG msg; + if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) + { + glut_process_msg(&msg); + + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + if(idle) + idle(); + + if(need_redisplay) + { + need_redisplay = false; + display(); + } + } +} diff --git a/source/wsdl.h b/source/wsdl.h new file mode 100755 index 0000000000..82bd6e5a21 --- /dev/null +++ b/source/wsdl.h @@ -0,0 +1,287 @@ +#ifndef __WSDL_H__ +#define __WSDL_H__ + +#ifndef _WIN32 +#include +#else + +#include "types.h" + + +/* allow apps to override window name */ +#ifndef APP_NAME +#define APP_NAME "ogl" +#endif + + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* SDL_Init flags */ +#define SDL_INIT_VIDEO 0 +#define SDL_INIT_AUDIO 0 +#define SDL_INIT_TIMER 0 +#define SDL_INIT_NOPARACHUTE 0 + +extern int SDL_Init(u32); +extern void SDL_Quit(); + + +typedef enum +{ + SDL_GL_DEPTH_SIZE, + SDL_GL_DOUBLEBUFFER /* ignored - always double buffered */ +} +SDL_GLattr; + +extern int SDL_GL_SetAttribute(SDL_GLattr attr, int value); + + +/* SDL_SetVideoMode() flags */ +#define SDL_OPENGL 0 +#define SDL_FULLSCREEN 1 + +extern int SDL_SetVideoMode(int w, int h, int bpp, u32 flags); + + +typedef struct +{ + int w, h; +} +SDL_Surface; + +extern SDL_Surface* SDL_GetVideoSurface(); + + +typedef struct +{ + int video_mem; +} +SDL_VideoInfo; + +extern SDL_VideoInfo* SDL_GetVideoInfo(); + + +/* + * threads / sync + */ + +typedef void SDL_sem; +typedef void SDL_Thread; + +extern void* SDL_GL_GetProcAddress(const char*); + +extern void SDL_GL_SwapBuffers(); + +extern u32 SDL_GetTicks(); + +extern SDL_sem* SDL_CreateSemaphore(int cnt); +extern void __stdcall SDL_DestroySemaphore(SDL_sem*); +extern int SDL_SemPost(SDL_sem*); +extern int SDL_SemWait(SDL_sem* sem); + +extern SDL_Thread* SDL_CreateThread(int(*)(void*), void*); +extern int SDL_KillThread(SDL_Thread*); + +extern int __stdcall SDL_WarpMouse(int, int); + + + + +/* macros */ + +#define SDL_GRAB_ON 0 +#define SDL_WM_GrabInput(a) +#define SDL_GetError() "" + + + + +/************************************************************************************************ + * events + ************************************************************************************************/ + +/* SDLKey (mapped to VK_* codes) */ +typedef enum +{ + SDLK_ESCAPE = 0x1b, + SDLK_8 = '8', + SDLK_9 = '9', + SDLK_0 = '0', + SDLK_p = 'P', + SDLK_r = 'R', + SDLK_s = 'S' +} +SDLKey; + +typedef struct +{ + SDLKey sym; +} +SDL_keysym; + +typedef struct +{ + SDL_keysym keysym; +} +SDL_KeyboardEvent; + +typedef struct +{ + u16 x, y; +} +SDL_MouseMotionEvent; + +/* SDL_MouseButtonEvent.button */ +enum +{ + SDL_BUTTON_LEFT, + SDL_BUTTON_MIDDLE, + SDL_BUTTON_RIGHT +}; + +typedef struct +{ + u8 button; + u8 state; + u16 x, y; +} +SDL_MouseButtonEvent; + +typedef struct +{ + u8 gain; + u8 state; +} +SDL_ActiveEvent; + +/* SDL_Event.type */ +enum +{ + SDL_KEYDOWN, + SDL_KEYUP, + SDL_MOUSEMOTION, + SDL_MOUSEBUTTONDOWN, + SDL_MOUSEBUTTONUP, + SDL_ACTIVE +}; + +typedef struct +{ + u8 type; + union + { + SDL_KeyboardEvent key; + SDL_MouseMotionEvent motion; + SDL_MouseButtonEvent button; + SDL_ActiveEvent active; + }; +} +SDL_Event; + +extern int SDL_PollEvent(SDL_Event* ev); + + + + + + + + + + + + + + + +/* glutInitDisplayMode */ +#define GLUT_RGB 0 +#define GLUT_DOUBLE 0 +#define GLUT_DEPTH 0 + +/* mouse buttons */ +enum +{ + GLUT_LEFT_BUTTON, + GLUT_RIGHT_BUTTON, + GLUT_MIDDLE_BUTTON /* also wheel, if avail */ +}; + +/* mouse button state */ +enum +{ + GLUT_DOWN, + GLUT_UP +}; + +/* keys */ +enum +{ + GLUT_KEY_LEFT = 0x25, /* VK_* */ + GLUT_KEY_RIGHT = 0x27, + GLUT_KEY_UP = 0x26, + GLUT_KEY_DOWN = 0x28 +}; + + +/* glutSetCursor */ +#define GLUT_CURSOR_INHERIT 32512 /* IDC_* */ +#define GLUT_CURSOR_WAIT 32514 +#define GLUT_CURSOR_DESTROY 32648 +#define GLUT_CURSOR_NONE 0 + +/* glutGet */ +enum +{ +GLUT_ELAPSED_TIME, +GLUT_SCREEN_WIDTH, +GLUT_SCREEN_HEIGHT, + +GLUT_GAME_MODE_WIDTH, +GLUT_GAME_MODE_HEIGHT, +GLUT_GAME_MODE_PIXEL_DEPTH, +GLUT_GAME_MODE_REFRESH_RATE +}; + + + +extern void glutIdleFunc(void(*)()); +extern void glutDisplayFunc(void(*)()); +extern void glutKeyboardFunc(void(*)(int, int, int)); +extern void glutSpecialFunc(void(*)(int, int, int)); +extern void glutMouseFunc(void(*)(int, int, int, int)); + + +#define glutInitDisplayMode(a) /* pixel format is hardwired */ + +extern int glutGameModeString(const char* str); + + + +extern void glutInit(int* argc, char* argv[]); +extern int glutGet(int arg); +extern int glutEnterGameMode(); +extern void glutMainLoop(); + +extern void glutPostRedisplay(); + +extern void glutSetCursor(int); + + + +#define glutSwapBuffers SDL_GL_SwapBuffers + + + +#ifdef __cplusplus +} +#endif + + +#endif /* #else / #ifndef WIN32 */ + +#endif /* #ifndef __WSDL_H__ */