removed files that have since been renamed or moved
This was SVN commit r161.
This commit is contained in:
parent
c4d39513c7
commit
58ab55ab78
@ -1,235 +0,0 @@
|
||||
/*
|
||||
* 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 <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "font.h"
|
||||
#include "res.h"
|
||||
#include "vfs.h"
|
||||
#include "tex.h"
|
||||
#include "ogl.h"
|
||||
#include "posix.h"
|
||||
#include "misc.h"
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Handle tex;
|
||||
uint list_base;
|
||||
}
|
||||
FONT;
|
||||
|
||||
|
||||
#if 0
|
||||
|
||||
#include <ft2build.h>
|
||||
//#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(void* p)
|
||||
{
|
||||
FONT* font = (FONT*)p;
|
||||
glDeleteLists(font->list_base, 96);
|
||||
}
|
||||
|
||||
|
||||
Handle font_load(const char* fn)
|
||||
{
|
||||
const u32 fn_hash = fnv_hash(fn, strlen(fn));
|
||||
|
||||
FONT* font;
|
||||
Handle h = h_alloc(fn_hash, H_FONT, font_dtor, (void**)&font);
|
||||
if(!h)
|
||||
return 0;
|
||||
if(font->tex)
|
||||
return h;
|
||||
|
||||
void* p;
|
||||
size_t size;
|
||||
Handle hm = vfs_load(fn, p, size);
|
||||
if(!hm)
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
h_free(hm, H_MEM);
|
||||
|
||||
// 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->tex = tex;
|
||||
font->list_base = list_base;
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
|
||||
int font_bind(const Handle h)
|
||||
{
|
||||
FONT* font = (FONT*)h_user_data(h, H_FONT);
|
||||
if(!font)
|
||||
return -1;
|
||||
|
||||
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((GLsizei)strlen(buf), GL_UNSIGNED_BYTE, buf);
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
// 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"
|
||||
#include "res.h"
|
||||
|
||||
// load and return a handle to the font defined in <fn>
|
||||
extern Handle font_load(const char* fn);
|
||||
|
||||
// use the font referenced by h for all subsequent glprintf() calls
|
||||
extern int font_bind(Handle 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, g_xres, 0, g_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
|
||||
|
||||
*/
|
@ -1,49 +0,0 @@
|
||||
#include "ia32.h"
|
||||
#include "types.h"
|
||||
#include "misc.h"
|
||||
|
||||
|
||||
#ifndef _M_IX86
|
||||
#error "#define _M_IX86 to enable IA-32 code"
|
||||
#endif
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
UNUSED(new_cw)
|
||||
UNUSED(mask)
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
#ifndef _M_IX86
|
||||
#define __IA32_H__
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef __IA32_H__
|
||||
|
||||
#include "types.h"
|
||||
|
||||
|
||||
extern u64 rdtsc();
|
||||
|
||||
|
||||
#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);
|
||||
|
||||
#endif // #ifndef __IA32_H__
|
@ -1,167 +0,0 @@
|
||||
// malloc layer for less fragmentation, alignment, and automatic release
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cassert>
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "types.h"
|
||||
#include "mem.h"
|
||||
#include "res.h"
|
||||
#include "misc.h"
|
||||
#include "posix.h"
|
||||
|
||||
|
||||
static void heap_free(MEM* m)
|
||||
{
|
||||
free(m->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((uintptr_t)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_free(MEM* m)
|
||||
{
|
||||
// at end of pool? if so, 'free' it
|
||||
if(m->ofs + m->size == pool_pos)
|
||||
pool_pos -= m->size;
|
||||
}
|
||||
|
||||
|
||||
static void* pool_alloc(const size_t size, const uint align, MEM* mem)
|
||||
{
|
||||
if(!pool)
|
||||
{
|
||||
pool = (u8*)mem_alloc(size, align, MEM_HEAP);
|
||||
if(!pool)
|
||||
return 0;
|
||||
}
|
||||
|
||||
ptrdiff_t ofs = (u8*)round_up((uintptr_t)pool+pool_pos, align) - pool;
|
||||
if(ofs+size > POOL_CAP)
|
||||
return 0;
|
||||
|
||||
void* p = (u8*)pool + ofs;
|
||||
|
||||
mem->size = size;
|
||||
mem->ofs = ofs;
|
||||
|
||||
pool_pos = ofs+size;
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
static void mmap_free(MEM* m)
|
||||
{
|
||||
munmap(m->p, (uint)m->size);
|
||||
}
|
||||
|
||||
|
||||
static void* mmap_alloc(const size_t size, const int fd, MEM* mem)
|
||||
{
|
||||
mem->p = mmap(0, (uint)size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
mem->size = size;
|
||||
mem->fd = fd;
|
||||
|
||||
return mem->p;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
static void mem_dtor(void* p)
|
||||
{
|
||||
MEM* m = (MEM*)p;
|
||||
if(m->type == MEM_HEAP)
|
||||
heap_free(m);
|
||||
else if(m->type == MEM_POOL)
|
||||
pool_free(m);
|
||||
else if(m->type == MEM_MAPPED)
|
||||
mmap_free(m);
|
||||
else
|
||||
assert(0 && "mem_dtor: MEM.type invalid!");
|
||||
}
|
||||
|
||||
|
||||
int mem_free(void* p)
|
||||
{
|
||||
if(!p)
|
||||
return 1;
|
||||
|
||||
Handle h = h_find((uintptr_t)p, H_MEM, 0);
|
||||
if(h)
|
||||
return h_free(h, H_MEM);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int mem_free(Handle hm)
|
||||
{
|
||||
return h_free(hm, H_MEM);
|
||||
}
|
||||
|
||||
|
||||
void* mem_alloc(size_t size, const uint align, const MemType type, const int fd, Handle* ph)
|
||||
{
|
||||
assert(size != 0 && "mem_alloc: why is size = 0?");
|
||||
|
||||
// bit of a hack: the allocators require space for bookkeeping,
|
||||
// but we can't allocate a handle until we know the key
|
||||
// (the pointer address), which is used to find the corresponding
|
||||
// handle when freeing memory.
|
||||
// we fill a temp MEM, and then copy it into the handle's user data space
|
||||
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 if(type == MEM_MAPPED)
|
||||
p = mmap_alloc(size, fd, &mem);
|
||||
else
|
||||
{
|
||||
assert(0 && "mem_alloc: invalid type parameter");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(!p)
|
||||
return 0;
|
||||
|
||||
MEM* pmem;
|
||||
Handle h = h_alloc((uintptr_t)p, H_MEM, mem_dtor, (void**)&pmem);
|
||||
if(!h) // failed to allocate a handle
|
||||
{
|
||||
mem_dtor(&mem);
|
||||
return 0;
|
||||
}
|
||||
*pmem = mem; // copy our memory info into the handle's user data space
|
||||
|
||||
// caller is asking for the handle
|
||||
// (freeing the memory via handle is faster than mem_free, because
|
||||
// we wouldn't have to scan all handles looking for the pointer)
|
||||
if(ph)
|
||||
*ph = h;
|
||||
|
||||
return p;
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
#ifndef __MEM_H__
|
||||
#define __MEM_H__
|
||||
|
||||
#include "res.h"
|
||||
|
||||
// include memory mappings because the VFS may return either a chunk of
|
||||
// memory, or the mapped file, and a client doesn't know the difference.
|
||||
|
||||
enum MemType
|
||||
{
|
||||
MEM_POOL,
|
||||
MEM_HEAP,
|
||||
MEM_MAPPED
|
||||
};
|
||||
|
||||
|
||||
struct MEM
|
||||
{
|
||||
void* p;
|
||||
size_t size;
|
||||
|
||||
MemType type;
|
||||
|
||||
union
|
||||
{
|
||||
size_t ofs; // MEM_POOL only
|
||||
void* org_p; // MEM_HEAP only
|
||||
int fd; // MEM_MAPPED only
|
||||
};
|
||||
};
|
||||
|
||||
extern void* mem_alloc(size_t size, uint align = 1, MemType type = MEM_HEAP, int fd = -1, Handle* ph = 0);
|
||||
extern int mem_free(void* p);
|
||||
|
||||
// faster than mem_free(void*) - no scan of open handles for the pointer
|
||||
extern int mem_free(Handle hm);
|
||||
|
||||
#endif // #ifndef __MEM_H__
|
@ -1,523 +0,0 @@
|
||||
// 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 <cassert>
|
||||
#include <cstdlib>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
|
||||
#include <process.h>
|
||||
|
||||
#include "posix.h"
|
||||
#include "win.h"
|
||||
#include "time.h"
|
||||
#include "misc.h"
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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)((char*)0 + _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)(((char*)0) + 3), (HANDLE)(((char*)0) + 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)((char*)0 + 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);
|
||||
}
|
||||
|
||||
|
||||
pthread_mutex_t pthread_mutex_initializer()
|
||||
{
|
||||
return CreateMutex(0, 0, 0);
|
||||
}
|
||||
|
||||
int pthread_mutex_init(pthread_mutex_t* m, const pthread_mutexattr_t*)
|
||||
{
|
||||
if(!m)
|
||||
return -1;
|
||||
*m = pthread_mutex_initializer();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_mutex_lock(pthread_mutex_t* m)
|
||||
{
|
||||
return WaitForSingleObject(*m, INFINITE) == WAIT_OBJECT_0? 0 : -1;
|
||||
}
|
||||
|
||||
int pthread_mutex_trylock(pthread_mutex_t* m)
|
||||
{
|
||||
return WaitForSingleObject(*m, 0) == WAIT_OBJECT_0? 0 : -1;
|
||||
}
|
||||
|
||||
int pthread_mutex_unlock(pthread_mutex_t* m)
|
||||
{
|
||||
return ReleaseMutex(*m)? 0 : -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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)((char*)0 + _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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
u16_t htons(u16_t s)
|
||||
{
|
||||
return (s >> 8) | ((s & 0xff) << 8);
|
||||
}
|
||||
|
||||
/******************************************************************/
|
||||
/* socket dynamic functions */
|
||||
fp_getnameinfo_t getnameinfo;
|
||||
fp_getaddrinfo_t getaddrinfo;
|
||||
fp_freeaddrinfo_t freeaddrinfo;
|
||||
|
||||
/* IPv6 globals
|
||||
These are included in the linux C libraries, and in newer platform SDK's, so
|
||||
should only be needed in VC++6 or earlier.
|
||||
*/
|
||||
#if _MSC_VER <= 1200 /* VC++6 or earlier */
|
||||
const struct in6_addr in6addr_any=IN6ADDR_ANY_INIT; /* :: */
|
||||
const struct in6_addr in6addr_loopback=IN6ADDR_LOOPBACK_INIT; /* ::1 */
|
||||
#endif
|
||||
|
||||
void entry(void)
|
||||
{
|
||||
// note: winsock header is also removed by this define
|
||||
#ifndef NO_WINSOCK
|
||||
char d[1024];
|
||||
WSAStartup(0x0002, d); // want 2.0
|
||||
#endif
|
||||
|
||||
HMODULE h=LoadLibrary("ws2_32.dll");
|
||||
if (h)
|
||||
{
|
||||
getaddrinfo=(fp_getaddrinfo_t)GetProcAddress(h, "getaddrinfo");
|
||||
getnameinfo=(fp_getnameinfo_t)GetProcAddress(h, "getnameinfo");
|
||||
freeaddrinfo=(fp_freeaddrinfo_t)GetProcAddress(h, "freeaddrinfo");
|
||||
}
|
||||
else
|
||||
{
|
||||
getaddrinfo=NULL;
|
||||
getnameinfo=NULL;
|
||||
freeaddrinfo=NULL;
|
||||
}
|
||||
|
||||
mainCRTStartup();
|
||||
}
|
@ -1,465 +0,0 @@
|
||||
// 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 <cassert>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
|
||||
//#include <winsock2.h>
|
||||
#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 size_t 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)
|
||||
size_t pad; // offset from starting sector
|
||||
void* buf; // reused; resize if too small
|
||||
size_t buf_size;
|
||||
};
|
||||
|
||||
static const int MAX_REQS = 4;
|
||||
static Req reqs[MAX_REQS];
|
||||
|
||||
// TODO: use pthread mutex
|
||||
static HANDLE open_mutex = INVALID_HANDLE_VALUE;
|
||||
static HANDLE reqs_mutex = INVALID_HANDLE_VALUE;
|
||||
#define LOCK(what)\
|
||||
{\
|
||||
if(what##_mutex == INVALID_HANDLE_VALUE)\
|
||||
what##_mutex = CreateMutex(0,0,"aio_"#what);\
|
||||
WaitForSingleObject(what##_mutex, INFINITE);\
|
||||
}
|
||||
#define UNLOCK(what) ReleaseMutex(what##_mutex);
|
||||
|
||||
|
||||
// get async capable handle to file <fd>
|
||||
// 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)
|
||||
{
|
||||
int i;
|
||||
|
||||
// close files
|
||||
for(i = 0; i < (int)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);
|
||||
open_mutex = INVALID_HANDLE_VALUE;
|
||||
if(reqs_mutex != INVALID_HANDLE_VALUE) // happens if not initialized, i.e. aio_rw wasn't called
|
||||
CloseHandle(reqs_mutex);
|
||||
reqs_mutex = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
// called by aio_open and aio_open_winhandle
|
||||
static void init()
|
||||
{
|
||||
ONCE(
|
||||
for(int i = 0; i < MAX_REQS; i++)
|
||||
{
|
||||
reqs[i].ovl.hEvent = CreateEvent(0,1,0,0); // manual reset
|
||||
//printf("Req %p [%d]: hEvent %x\n", reqs+i, i, reqs[i].ovl.hEvent);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
//NOTE: Requires that the "open" lock is held
|
||||
int alloc_handle_entry(int fd)
|
||||
{
|
||||
// alloc aio_hs entry
|
||||
if((unsigned)fd >= hs_cap)
|
||||
{
|
||||
uint hs_cap2 = (uint)round_up(fd+8, 8);
|
||||
HANDLE* aio_hs2 = (HANDLE*)realloc(aio_hs, hs_cap2*sizeof(HANDLE));
|
||||
if(!aio_hs2)
|
||||
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;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// fd is already opened in async/overlapped mode; add to required internal structures
|
||||
int aio_open_winhandle(HANDLE fd)
|
||||
{
|
||||
init();
|
||||
|
||||
LOCK(open)
|
||||
|
||||
if (alloc_handle_entry(HANDLE2INT(fd)) == -1)
|
||||
{
|
||||
UNLOCK(open)
|
||||
return -1;
|
||||
}
|
||||
|
||||
aio_hs[HANDLE2INT(fd)]=fd;
|
||||
|
||||
UNLOCK(open)
|
||||
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)
|
||||
{
|
||||
init();
|
||||
|
||||
LOCK(open)
|
||||
|
||||
if (alloc_handle_entry(fd) == -1)
|
||||
return -1;
|
||||
|
||||
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)
|
||||
{
|
||||
printf("Invalid handle\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOCK(reqs)
|
||||
|
||||
// find free request slot
|
||||
Req* r = find_req(0);
|
||||
if(!r)
|
||||
{
|
||||
UNLOCK(reqs)
|
||||
printf("No Req\n");
|
||||
return -1;
|
||||
}
|
||||
r->cb = cb;
|
||||
|
||||
UNLOCK(reqs)
|
||||
|
||||
size_t ofs = 0;
|
||||
size_t size = cb->aio_nbytes;
|
||||
void* buf = cb->aio_buf;
|
||||
|
||||
#define SOL_SOCKET 0xffff
|
||||
#define SO_TYPE 0x1008
|
||||
|
||||
unsigned long opt = 0;
|
||||
socklen_t optlen = sizeof(opt);
|
||||
if (getsockopt((int)h, SOL_SOCKET, SO_TYPE, &opt, &optlen) != -1)
|
||||
// || (WSAGetLastError() != WSAENOTSOCK))
|
||||
cb->aio_offset = 0;
|
||||
else
|
||||
{
|
||||
// align
|
||||
r->pad = cb->aio_offset % sector_size; // offset to start of sector
|
||||
ofs = cb->aio_offset - r->pad;
|
||||
size += r->pad + sector_size-1;
|
||||
size &= sector_size-1; // align (sector_size = 2**n)
|
||||
|
||||
if(r->pad || (uintptr_t)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;
|
||||
}
|
||||
}
|
||||
|
||||
#if _MSC_VER >= 1300
|
||||
r->ovl.Pointer = (void*)ofs;
|
||||
#else
|
||||
r->ovl.Offset = ofs;
|
||||
#endif
|
||||
|
||||
DWORD size32 = (DWORD)(size & 0xffffffff);
|
||||
u32 status = (cb->aio_lio_opcode == LIO_READ)?
|
||||
ReadFile(h, buf, size32, 0, &r->ovl) : WriteFile(h, buf, size32, 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
|
||||
const size_t _buf = (char*)cb->aio_buf - (char*)0;
|
||||
if(r->pad || _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)
|
||||
{
|
||||
if(n <= 0 || n > MAXIMUM_WAIT_OBJECTS)
|
||||
return -1;
|
||||
|
||||
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)
|
||||
{
|
||||
if(r->ovl.Internal == STATUS_PENDING)
|
||||
hs[cnt++] = r->ovl.hEvent;
|
||||
}
|
||||
}
|
||||
|
||||
// no valid, pending transfers - done
|
||||
if(!cnt)
|
||||
return 0;
|
||||
|
||||
// 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;
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
// 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);
|
@ -1,326 +0,0 @@
|
||||
// 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 <cassert>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
|
||||
#include "types.h"
|
||||
#include "zip.h"
|
||||
#include "posix.h"
|
||||
#include "misc.h"
|
||||
#include "res.h"
|
||||
#include "mem.h"
|
||||
#include "vfs.h"
|
||||
|
||||
|
||||
// handle (32 bits)
|
||||
// .. make sure this is the same handle we opened
|
||||
const uint HTAG_BITS = 16;
|
||||
// .. index into array => = log2(max open handles)
|
||||
const uint HIDX_BITS = 12;
|
||||
|
||||
// together, <= 32-TAG_BITS bits
|
||||
const uint HTYPE_BITS = 4;
|
||||
const uint HREF_BITS = 8;
|
||||
|
||||
const int HDATA_USER_SIZE = 20;
|
||||
|
||||
// 32 bytes
|
||||
struct HDATA
|
||||
{
|
||||
uintptr_t key;
|
||||
u32 tag : HTAG_BITS;
|
||||
u32 type : HTYPE_BITS; // handle's type (e.g. texture, sound)
|
||||
u32 refs : HREF_BITS;
|
||||
|
||||
u32 unused; // TODO: type pointer?
|
||||
|
||||
u8 user[HDATA_USER_SIZE];
|
||||
};
|
||||
|
||||
static const ulong hdata_cap = 1ul << HIDX_BITS;
|
||||
static const uint type_cap = 1ul << HTYPE_BITS;
|
||||
|
||||
// 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 = hdata_cap / hdata_per_page;
|
||||
static HDATA* pages[num_pages];
|
||||
|
||||
static int first_free = -1; // don't want to scan array every h_alloc
|
||||
static int last_in_use = -1; // don't search unused entries
|
||||
|
||||
|
||||
static void(*dtors[type_cap])(void*);
|
||||
|
||||
|
||||
// get pointer to handle data (non-contiguous array)
|
||||
static HDATA* h_data(const int idx)
|
||||
{
|
||||
if(idx > hdata_cap)
|
||||
return 0;
|
||||
HDATA*& page = pages[idx / hdata_per_page];
|
||||
if(!page)
|
||||
{
|
||||
page = (HDATA*)calloc(PAGE_SIZE, 1);
|
||||
if(!page)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return &page[idx % hdata_per_page];
|
||||
}
|
||||
|
||||
|
||||
// get array index from handle
|
||||
static int h_idx(const Handle h, const uint type)
|
||||
{
|
||||
const int idx = h & ((1 << HIDX_BITS)-1);
|
||||
if(idx > last_in_use)
|
||||
return -1;
|
||||
|
||||
const HDATA* hd = h_data(idx);
|
||||
// cannot fail - all HDATA up to last_in_use are valid
|
||||
|
||||
const u32 tag = h >> HIDX_BITS;
|
||||
// note: tag = 0 marks unused entries => is invalid
|
||||
if(!tag || hd->tag != tag || hd->type != type)
|
||||
return -1;
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
|
||||
static Handle handle(const int idx)
|
||||
{
|
||||
const HDATA* hd = h_data(idx);
|
||||
if(!hd) // out of memory
|
||||
return 0;
|
||||
return (hd->tag) << HIDX_BITS | (u32)idx;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int h_free(const int idx)
|
||||
{
|
||||
HDATA* hd = h_data(idx);
|
||||
if(!hd)
|
||||
return -1;
|
||||
|
||||
// not the last reference
|
||||
if(--hd->refs)
|
||||
return 0;
|
||||
|
||||
// TODO: keep this handle open (cache)
|
||||
|
||||
// call its type's destructor
|
||||
if(dtors[hd->type])
|
||||
dtors[hd->type](hd);
|
||||
|
||||
memset(hd, 0, sizeof(HDATA));
|
||||
|
||||
if(first_free == -1 || idx < first_free)
|
||||
first_free = idx;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void cleanup(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
// close open handles
|
||||
for(i = 0; i < last_in_use; i++)
|
||||
h_free(i);
|
||||
|
||||
// free hdata array
|
||||
for(i = 0; i < (int)num_pages; i++)
|
||||
{
|
||||
free(pages[i]);
|
||||
pages[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Handle h_find(const uintptr_t key, uint type, void** puser)
|
||||
{
|
||||
int idx;
|
||||
HDATA* hd;
|
||||
|
||||
// already have first free entry cached - just search
|
||||
if(first_free != -1)
|
||||
{
|
||||
for(idx = 0; idx <= last_in_use; idx++)
|
||||
{
|
||||
hd = h_data(idx); // guaranteed valid
|
||||
if(hd->key == key && hd->type == type)
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
// search and remember first free entry (slower)
|
||||
else
|
||||
{
|
||||
for(idx = 0; idx <= last_in_use; idx++)
|
||||
{
|
||||
hd = h_data(idx); // guaranteed valid
|
||||
if(!hd->tag && first_free == -1)
|
||||
first_free = idx;
|
||||
else if(hd->key == key && hd->type == type)
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
|
||||
// not found
|
||||
return 0;
|
||||
|
||||
found:
|
||||
Handle h = handle(idx);
|
||||
if(puser)
|
||||
*puser = hd->user;
|
||||
return h;
|
||||
}
|
||||
|
||||
|
||||
Handle h_alloc(const uintptr_t key, const uint type, /*const size_t user_size,*/ H_DTOR dtor, void** puser)
|
||||
{
|
||||
ONCE(atexit(cleanup))
|
||||
/*
|
||||
if(user_size > HDATA_USER_SIZE)
|
||||
{
|
||||
assert(!"h_alloc: not enough space in entry for user data");
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
if(type >= type_cap)
|
||||
{
|
||||
assert(!"h_alloc: invalid type");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(dtor)
|
||||
{
|
||||
// registering a second dtor for type
|
||||
if(dtors[type] && dtors[type] != dtor)
|
||||
{
|
||||
assert(!"h_alloc: registering a second, different dtor for type");
|
||||
return 0;
|
||||
}
|
||||
dtors[type] = dtor;
|
||||
}
|
||||
|
||||
int idx;
|
||||
HDATA* hd;
|
||||
|
||||
if(key)
|
||||
{
|
||||
// object already loaded?
|
||||
Handle h = h_find(key, type, 0);
|
||||
if(h)
|
||||
{
|
||||
hd = h_data(h_idx(h, type));
|
||||
|
||||
if(hd->refs == (1ul << HREF_BITS))
|
||||
{
|
||||
assert(!"h_alloc: too many references to a handle - increase REF_BITS");
|
||||
return 0;
|
||||
}
|
||||
hd->refs++;
|
||||
|
||||
if(puser)
|
||||
*puser = hd;
|
||||
|
||||
return h;
|
||||
}
|
||||
}
|
||||
|
||||
// cached
|
||||
if(first_free != -1)
|
||||
{
|
||||
idx = first_free;
|
||||
hd = h_data(idx);
|
||||
}
|
||||
// search handle data for first free entry
|
||||
else
|
||||
for(idx = 0; idx < hdata_cap; idx++)
|
||||
{
|
||||
hd = h_data(idx);
|
||||
// not enough memory - abort (don't leave a hole in the array)
|
||||
if(!hd)
|
||||
return 0;
|
||||
// found an empty entry - done
|
||||
if(!hd->tag)
|
||||
break;
|
||||
}
|
||||
|
||||
if(idx >= hdata_cap)
|
||||
{
|
||||
assert(!"h_alloc: too many open handles (increase IDX_BITS)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// check if next entry is free
|
||||
HDATA* hd2 = h_data(idx+1);
|
||||
if(hd2 && hd2->tag == 0)
|
||||
first_free = idx+1;
|
||||
else
|
||||
first_free = -1;
|
||||
|
||||
if(idx > last_in_use)
|
||||
last_in_use = idx;
|
||||
|
||||
static u32 tag;
|
||||
if(++tag >= (1 << HTAG_BITS))
|
||||
{
|
||||
assert(!"h_alloc: tag overflow - may not notice stale handle reuse (increase TAG_BITS)");
|
||||
tag = 1;
|
||||
}
|
||||
|
||||
hd->key = key;
|
||||
hd->tag = tag;
|
||||
hd->type = type;
|
||||
|
||||
if(puser)
|
||||
*puser = hd->user;
|
||||
return handle(idx);
|
||||
}
|
||||
|
||||
|
||||
int h_free(Handle& h, const uint type)
|
||||
{
|
||||
int idx = h_idx(h, type);
|
||||
h = 0;
|
||||
if(idx >= 0)
|
||||
return h_free(idx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void* h_user_data(const Handle h, const uint type)
|
||||
{
|
||||
int idx = h_idx(h, type);
|
||||
if(idx >= 0)
|
||||
return h_data(idx)->user; // pointer is always valid if index is in range
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -1,79 +0,0 @@
|
||||
// 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"
|
||||
|
||||
|
||||
// handle type (for 'type safety' - can't use a texture handle as a sound)
|
||||
//
|
||||
// rationale: we could use the destructor passed to h_alloc to identify
|
||||
// the handle, but it's good to have a list of all types, and we avoid having
|
||||
// to create empty destructors for handle types that wouldn't need them.
|
||||
// finally, we save memory - this fits in a few bits, vs. needing a pointer.
|
||||
enum HType
|
||||
{
|
||||
H_TEX = 1,
|
||||
H_FONT = 2,
|
||||
H_SOUND = 3,
|
||||
H_ZFILE = 4,
|
||||
H_ZARCHIVE = 5,
|
||||
H_VFILE = 6,
|
||||
H_MEM = 7,
|
||||
|
||||
NUM_HANDLE_TYPES
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
// 0 = invalid handle value.
|
||||
typedef u32 Handle;
|
||||
|
||||
|
||||
// destructor, registered by h_alloc for a given handle type.
|
||||
// receives the user data associated with the handle.
|
||||
typedef void(*H_DTOR)(void*);
|
||||
|
||||
|
||||
// all functions check the passed tag (part of the handle) and type against
|
||||
// the internal values. if they differ, an error is returned.
|
||||
|
||||
|
||||
// allocate a new handle.
|
||||
// if key is 0, or a (key, type) handle doesn't exist,
|
||||
// the first free entry is used.
|
||||
// otherwise, a handle to the existing object is returned,
|
||||
// and HDATA.size != 0.
|
||||
//// user_size is checked to make sure the user data fits in the handle data space.
|
||||
// dtor is associated with type and called when the object is freed.
|
||||
// handle data is initialized to 0; optionally, a pointer to it is returned.
|
||||
extern Handle h_alloc(uintptr_t key, uint type,/* size_t user_size,*/ H_DTOR dtor = 0, void** puser = 0);
|
||||
extern int h_free(Handle& h, uint type);
|
||||
|
||||
// find and return a handle by type and key (typically filename hash)
|
||||
// currently O(n).
|
||||
extern Handle h_find(uintptr_t key, uint type, void** puser = 0);
|
||||
|
||||
// return a pointer to handle data
|
||||
extern void* h_user_data(Handle h, uint type);
|
||||
|
||||
|
||||
#endif // #ifndef __RES_H__
|
@ -80,14 +80,6 @@ static inline Handle handle(const i32 idx, const i32 tag)
|
||||
return _idx | _tag;
|
||||
}
|
||||
|
||||
inline __int64 __declspec(naked) GetCycleCount()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
RDTSC
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// internal per-resource-instance data
|
||||
|
@ -1,795 +0,0 @@
|
||||
// OpenGL texturing
|
||||
//
|
||||
// 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 <cassert>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#include "vfs.h"
|
||||
#include "tex.h"
|
||||
#include "mem.h"
|
||||
#include "ogl.h"
|
||||
#include "res.h"
|
||||
#include "misc.h"
|
||||
|
||||
#define NO_JP2
|
||||
#define NO_PNG
|
||||
|
||||
#ifndef NO_JP2
|
||||
#include <jasper/jasper.h>
|
||||
#endif
|
||||
|
||||
#ifndef NO_PNG
|
||||
#include <png.h>
|
||||
#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);
|
||||
|
||||
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->bpp = img_size / (w * h);
|
||||
tex->ofs = hdr_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("dds_load: %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));
|
||||
}
|
||||
|
||||
|
||||
// requirements: uncompressed, direct color, bottom up
|
||||
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 ulong img_size = (ulong)w * h * bpp / 8;
|
||||
|
||||
// determine format
|
||||
int fmt = -1;
|
||||
// .. grayscale
|
||||
if(type == 3)
|
||||
{
|
||||
if(bpp == 8)
|
||||
fmt = 0;
|
||||
else if(bpp == 16 && alpha_bits == 8)
|
||||
fmt = GL_LUMINANCE_ALPHA;
|
||||
}
|
||||
// .. true color
|
||||
else if(type == 2)
|
||||
{
|
||||
if(bpp == 24 && alpha_bits == 0)
|
||||
fmt = GL_BGR;
|
||||
else if(bpp == 32 && alpha_bits == 8)
|
||||
fmt = GL_BGRA;
|
||||
}
|
||||
|
||||
tex->width = w;
|
||||
tex->height = h;
|
||||
tex->fmt = fmt;
|
||||
tex->bpp = bpp;
|
||||
tex->ofs = hdr_size;
|
||||
|
||||
if(fmt == -1)
|
||||
err = "invalid format or 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("tga_load: %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: uncompressed, direct color, 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 u32 img_size = w * h * bpp/8;
|
||||
|
||||
tex->width = w;
|
||||
tex->height = h;
|
||||
tex->fmt = (bpp == 24)? GL_BGR : GL_BGRA;
|
||||
tex->bpp = bpp;
|
||||
tex->ofs = ofs;
|
||||
|
||||
if(h < 0)
|
||||
err = "top-down";
|
||||
if(compress != BI_RGB)
|
||||
err = "compressed";
|
||||
if(bpp < 24)
|
||||
err = "not direct color";
|
||||
if(size < ofs+img_size)
|
||||
err = "image not completely read";
|
||||
}
|
||||
|
||||
if(err)
|
||||
{
|
||||
printf("bmp_load: %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;
|
||||
}
|
||||
|
||||
|
||||
static int raw_load(const char* fn, const u8* ptr, size_t size, TEX* tex)
|
||||
{
|
||||
static GLenum fmts[5] = { 0, 0, GL_LUMINANCE_ALPHA, GL_RGB, GL_RGBA };
|
||||
for(int i = 1; i <= 4; i++)
|
||||
{
|
||||
u32 dim = (u32)sqrtf((float)size/i);
|
||||
// TODO: differentiate 8/32 bpp
|
||||
if(dim*dim*i != size)
|
||||
continue;
|
||||
|
||||
tex->width = dim;
|
||||
tex->height = dim;
|
||||
tex->fmt = fmts[i];
|
||||
tex->bpp = i * 8;
|
||||
tex->ofs = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("raw_load: %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;
|
||||
}
|
||||
|
||||
|
||||
// requirement: direct color
|
||||
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("png_load: %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 prec, 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(prec != 8)
|
||||
{
|
||||
err = "channel precision != 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;
|
||||
|
||||
int fmts[8] = { 0, -1, GL_RGB, -1, GL_LUMINANCE_ALPHA, -1, GL_RGBA, -1 };
|
||||
if(color_type >= 8)
|
||||
return -1;
|
||||
tex->fmt = fmts[color_type];
|
||||
if(tex->fmt == -1) // <==> palette image
|
||||
{
|
||||
printf("png_load: %s: %s\n", fn, "not direct color");
|
||||
return -1;
|
||||
}
|
||||
|
||||
tex->ptr = img;
|
||||
tex->bpp = pitch / width * 8;
|
||||
|
||||
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)
|
||||
{
|
||||
const char* err = 0;
|
||||
|
||||
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 prec = jas_image_cmptprec (image, 0);
|
||||
|
||||
if(depth != 8)
|
||||
{
|
||||
err = "channel precision != 8";
|
||||
printf("jp2_load: %s: %s\n", fn, err);
|
||||
// TODO: destroy image
|
||||
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->bpp = num_cmpts * 8;
|
||||
tex->ptr = img;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
static void tex_dtor(void* p)
|
||||
{
|
||||
TEX* tex = (TEX*)p;
|
||||
|
||||
mem_free(tex->hm);
|
||||
|
||||
glDeleteTextures(1, &tex->id);
|
||||
}
|
||||
|
||||
|
||||
// TEX output param is invalid if function fails
|
||||
Handle tex_load(const char* fn, TEX* ptex)
|
||||
{
|
||||
const u32 fn_hash = fnv_hash(fn, strlen(fn));
|
||||
|
||||
TEX* tex;
|
||||
Handle ht = h_alloc(fn_hash, H_TEX, tex_dtor, (void**)&tex);
|
||||
if(!ht)
|
||||
return 0;
|
||||
if(tex->id != 0)
|
||||
goto already_loaded;
|
||||
|
||||
{
|
||||
// load file
|
||||
const u8* p;
|
||||
size_t size;
|
||||
Handle hm = vfs_load(fn, (void*&)p, size);
|
||||
// .. note: xxx_valid routines assume 4 header bytes are available
|
||||
if(!hm || !p || size < 4)
|
||||
{
|
||||
h_free(ht, H_TEX);
|
||||
return 0;
|
||||
}
|
||||
tex->hm = hm;
|
||||
|
||||
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;
|
||||
|
||||
// loaders weren't able to determine type
|
||||
if(tex->fmt == 0)
|
||||
{
|
||||
assert(tex->bpp == 8);
|
||||
tex->fmt = GL_ALPHA;
|
||||
// TODO: check file name, go to 32 bit if wrong
|
||||
}
|
||||
|
||||
uint id;
|
||||
glGenTextures(1, &id);
|
||||
tex->id = id;
|
||||
// this can't realistically fail, just note that the already_loaded
|
||||
// check above assumes (id > 0) <==> texture is loaded and valid
|
||||
}
|
||||
|
||||
already_loaded:
|
||||
if(ptex)
|
||||
*ptex = *tex;
|
||||
|
||||
return ht;
|
||||
}
|
||||
|
||||
|
||||
int tex_bind(const Handle h)
|
||||
{
|
||||
TEX* tex = (TEX*)h_user_data(h, H_TEX);
|
||||
if(!tex)
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, tex->id);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int tex_filter = GL_LINEAR;
|
||||
uint tex_bpp = 32; // 16 or 32
|
||||
|
||||
int tex_upload(const Handle ht, int filter, int int_fmt)
|
||||
{
|
||||
TEX* tex = (TEX*)h_user_data(ht, H_TEX);
|
||||
if(!tex)
|
||||
return -1;
|
||||
|
||||
// greater than max supported tex dimension?
|
||||
// no-op if oglInit not yet called
|
||||
if(tex->width > (uint)max_tex_size || tex->height > (uint)max_tex_size)
|
||||
{
|
||||
assert(!"tex_upload: image dimensions exceed OpenGL implementation limit");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// both NV_texture_rectangle and subtexture require work for the client
|
||||
// (changing tex coords) => we'll just disallow non-power of 2 textures.
|
||||
// TODO: ARB_texture_non_power_of_two
|
||||
if(!is_pow2(tex->width) || !is_pow2(tex->height))
|
||||
{
|
||||
assert(!"tex_upload: image is not power-of-2");
|
||||
return 0;
|
||||
}
|
||||
|
||||
tex_bind(ht);
|
||||
|
||||
// get pointer to image data
|
||||
MEM* mem = (MEM*)h_user_data(tex->hm, H_MEM);
|
||||
if(!mem)
|
||||
return 0;
|
||||
void* p = mem->p;
|
||||
if(!p)
|
||||
{
|
||||
assert(0 && "tex_upload: mem object is a NULL pointer");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// set filter
|
||||
if(!filter)
|
||||
filter = tex_filter;
|
||||
const int mag = (filter == GL_NEAREST)? GL_NEAREST : GL_LINEAR;
|
||||
const bool mipmap = (filter == GL_NEAREST_MIPMAP_NEAREST || filter == GL_LINEAR_MIPMAP_NEAREST ||
|
||||
filter == GL_NEAREST_MIPMAP_LINEAR || filter == GL_LINEAR_MIPMAP_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag);
|
||||
|
||||
|
||||
const bool has_alpha = tex->fmt == GL_RGBA || tex->fmt == GL_BGRA || tex->fmt == GL_LUMINANCE_ALPHA || tex->fmt == GL_ALPHA;
|
||||
|
||||
// S3TC compressed
|
||||
if(tex->fmt >= GL_COMPRESSED_RGB_S3TC_DXT1_EXT &&
|
||||
tex->fmt <= GL_COMPRESSED_RGBA_S3TC_DXT5_EXT)
|
||||
{
|
||||
const int img_size = tex->width * tex->height * tex->bpp;
|
||||
assert(4+sizeof(DDSURFACEDESC2)+img_size == mem->size && "tex_upload: dds file size mismatch");
|
||||
glCompressedTexImage2DARB(GL_TEXTURE_2D, 0, tex->fmt, tex->width, tex->height, 0, img_size, p);
|
||||
}
|
||||
// normal
|
||||
else
|
||||
{
|
||||
// calc internal fmt from format and global bpp, if not passed as a param
|
||||
if(!int_fmt)
|
||||
{
|
||||
if(tex->bpp == 32)
|
||||
int_fmt = (tex_bpp == 32)? GL_RGBA8 : GL_RGBA4;
|
||||
else if(tex->bpp == 24)
|
||||
int_fmt = (tex_bpp == 32)? GL_RGB8 : GL_RGB5;
|
||||
else if(tex->bpp == 16)
|
||||
int_fmt = (tex_bpp == 32)? GL_LUMINANCE8_ALPHA8 : GL_LUMINANCE4_ALPHA4;
|
||||
else if(tex->fmt == GL_ALPHA)
|
||||
int_fmt = (tex_bpp == 32)? GL_ALPHA8 : GL_ALPHA4;
|
||||
else if(tex->fmt == GL_LUMINANCE)
|
||||
int_fmt = (tex_bpp == 32)? GL_LUMINANCE8 : GL_LUMINANCE4;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
// check if SGIS_generate_mipmap is available (once)
|
||||
static int sgm_avl = -1;
|
||||
if(sgm_avl == -1)
|
||||
sgm_avl = oglExtAvail("GL_SGIS_generate_mipmap");
|
||||
|
||||
// manual mipmap gen via GLU (box filter)
|
||||
if(mipmap && !sgm_avl)
|
||||
gluBuild2DMipmaps(GL_TEXTURE_2D, int_fmt, tex->width, tex->height, tex->fmt, GL_UNSIGNED_BYTE, p);
|
||||
// auto mipmap gen, or no mipmap
|
||||
else
|
||||
{
|
||||
if(mipmap)
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, int_fmt, tex->width, tex->height, 0, tex->fmt, GL_UNSIGNED_BYTE, p);
|
||||
}
|
||||
}
|
||||
|
||||
mem_free(tex->hm);
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
// OpenGL texturing
|
||||
//
|
||||
// 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 __TEX_H__
|
||||
#define __TEX_H__
|
||||
|
||||
#include "types.h"
|
||||
#include "res.h"
|
||||
#include "misc.h"
|
||||
|
||||
struct TEX
|
||||
{
|
||||
u32 width : 16;
|
||||
u32 height : 16;
|
||||
u32 fmt : 16;
|
||||
u32 bpp : 16;
|
||||
Handle hm; // H_MEM handle to loaded file
|
||||
size_t ofs; // WRT image data in file
|
||||
uint id;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
// load and return a handle to the texture given in <fn>.
|
||||
// supports RAW, BMP, JP2, PNG, TGA, DDS
|
||||
// optionally returns a copy of information about the texture.
|
||||
extern Handle tex_load(const char* fn, TEX* tex_info = 0);
|
||||
|
||||
extern int tex_bind(Handle h);
|
||||
|
||||
|
||||
extern int tex_filter; // GL values; default: GL_LINEAR
|
||||
extern uint tex_bpp; // 16 or 32; default: 32
|
||||
|
||||
// upload the specified texture to OpenGL. Texture filter and internal format
|
||||
// may be specified to override the global defaults.
|
||||
extern int tex_upload(Handle h, int filter_override = 0, int internal_fmt_override = 0);
|
||||
|
||||
#endif // __TEX_H__
|
@ -1,170 +0,0 @@
|
||||
// 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 <time.h>
|
||||
#include <cmath>
|
||||
|
||||
#include "win.h"
|
||||
|
||||
#include "ia32.h"
|
||||
#include "posix.h"
|
||||
#include "detect.h"
|
||||
#include "time.h"
|
||||
#include "types.h"
|
||||
#include "misc.h"
|
||||
|
||||
|
||||
|
||||
// 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)
|
||||
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) // decreasing
|
||||
trend = -1;
|
||||
else if(cur_fps < h1 && h1 < h2) // increasing
|
||||
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
|
||||
const float avg_fps = fps_sum / H;
|
||||
const float d_avg = avg_fps-fps;
|
||||
const float max_diff = fminf(5.f, 0.05f*fps);
|
||||
|
||||
if((trend > 0 && (avg_fps > fps || d_avg < -4.f)) || // going up, or large drop
|
||||
(trend < 0 && (avg_fps < fps || d_avg > 4.f)) || // going down, or large raise
|
||||
(fabs(d_avg) > max_diff)) // significant difference
|
||||
fps = (int)avg_fps;
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
// 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__
|
@ -1,567 +0,0 @@
|
||||
// virtual file system - transparent access to files in archives;
|
||||
// allows multiple search paths
|
||||
//
|
||||
// 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 <cstdio>
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#include "posix.h"
|
||||
#include "zip.h"
|
||||
#include "misc.h"
|
||||
#include "vfs.h"
|
||||
#include "mem.h"
|
||||
|
||||
|
||||
// H_VFILE handle
|
||||
struct VFILE
|
||||
{
|
||||
int fd;
|
||||
|
||||
size_t size; // compressed size, if a Zip file
|
||||
|
||||
// Zip only:
|
||||
size_t ucsize;
|
||||
size_t ofs;
|
||||
|
||||
Handle hm; // memory handle to the file or archive, if a Zip file
|
||||
};
|
||||
|
||||
|
||||
// rationale for n-archives per PATH entry:
|
||||
// We need to be able to unmount specific paths (e.g. when switching mods).
|
||||
// Don't want to remount everything (slow), or specify a mod tag when mounting
|
||||
// (not this module's job). Instead, we include all archives in one path entry;
|
||||
// the game keeps track of what path(s) it mounted for a mod,
|
||||
// and unmounts those when needed.
|
||||
|
||||
struct PATH
|
||||
{
|
||||
struct PATH* next; // linked list
|
||||
|
||||
char* dir; // relative to root dir;
|
||||
// points to space at end of this struct
|
||||
|
||||
size_t num_archives;
|
||||
Handle archives[1];
|
||||
|
||||
// space allocated here for archive Handles + dir string
|
||||
};
|
||||
static PATH* path_list;
|
||||
|
||||
|
||||
static void vfile_dtor(void* p)
|
||||
{
|
||||
VFILE* vf = (VFILE*)p;
|
||||
|
||||
if(vf->fd > 0)
|
||||
{
|
||||
close(vf->fd);
|
||||
vf->fd = -1;
|
||||
}
|
||||
|
||||
mem_free(vf->hm);
|
||||
}
|
||||
|
||||
|
||||
int vfs_set_root(const char* argv0, const char* root)
|
||||
{
|
||||
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;
|
||||
|
||||
chdir(path);
|
||||
chdir(root);
|
||||
|
||||
return vfs_mount(".");
|
||||
}
|
||||
|
||||
|
||||
int vfs_mount(const char* path)
|
||||
{
|
||||
const size_t path_len = strlen(path);
|
||||
if(path_len > VFS_MAX_PATH)
|
||||
{
|
||||
assert(!"vfs_mount_dir: path name is longer than VFS_MAX_PATH");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// enumerate all archives in <path>
|
||||
std::vector<std::string> archives;
|
||||
DIR* dir = opendir(path);
|
||||
struct dirent* ent;
|
||||
while((ent = readdir(dir)))
|
||||
{
|
||||
struct stat s;
|
||||
if(stat(ent->d_name, &s) < 0)
|
||||
continue;
|
||||
if(s.st_mode == S_IFREG) // regular file
|
||||
archives.push_back(ent->d_name);
|
||||
}
|
||||
closedir(dir);
|
||||
const size_t num_archives = archives.size();
|
||||
|
||||
// alloc search path entry (add to front)
|
||||
const size_t archives_size = num_archives*sizeof(Handle);
|
||||
const size_t entry_size = round_up((long)(sizeof(PATH)+archives_size+path_len+1), 32);
|
||||
PATH* entry = (PATH*)mem_alloc(entry_size, 32, MEM_HEAP);
|
||||
if(!entry)
|
||||
return -1;
|
||||
entry->next = path_list;
|
||||
path_list = entry;
|
||||
|
||||
entry->dir = (char*)&entry->archives[0] + archives_size;
|
||||
strcpy(entry->dir, path);
|
||||
|
||||
// add archives in alphabetical order
|
||||
std::sort(archives.begin(), archives.end());
|
||||
entry->num_archives = num_archives;
|
||||
for(size_t i = 0; i < num_archives; i++)
|
||||
entry->archives[i] = zip_open(archives[i].c_str());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int vfs_umount(const char* path)
|
||||
{
|
||||
PATH** prev = &path_list;
|
||||
PATH* entry = path_list;
|
||||
while(entry)
|
||||
{
|
||||
// found
|
||||
if(!strcmp(entry->dir, path))
|
||||
{
|
||||
// close all archives
|
||||
for(size_t i = 0; i < entry->num_archives; i++)
|
||||
h_free(entry->archives[i], H_ZARCHIVE);
|
||||
|
||||
// remove from list
|
||||
*prev = entry->next;
|
||||
mem_free(entry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
prev = &entry->next;
|
||||
entry = entry->next;
|
||||
}
|
||||
|
||||
// not found
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
// call func, passing the data argument, for each mounted path
|
||||
// fail if its return value is < 0, stop if it returns 0
|
||||
static int vfs_foreach_path(int (*func)(const char* path, Handle ha, void* data), const char* fn, void* data)
|
||||
{
|
||||
char buf[PATH_MAX+1]; buf[PATH_MAX] = 0;
|
||||
|
||||
for(PATH* entry = path_list; entry; entry = entry->next)
|
||||
{
|
||||
// dir
|
||||
const char* path = fn;
|
||||
if(entry->dir[0] != '.' || entry->dir[1] != '\0')
|
||||
{
|
||||
// only prepend dir if not "." (root) - "./" isn't portable
|
||||
snprintf(buf, PATH_MAX, "%s/%s", entry->dir, fn);
|
||||
path = buf;
|
||||
}
|
||||
|
||||
int err = func(path, 0, data);
|
||||
if(err <= 0)
|
||||
return err;
|
||||
|
||||
// archive
|
||||
for(size_t i = 0; i < entry->num_archives; i++)
|
||||
{
|
||||
err = func(path, entry->archives[i], data);
|
||||
if(err <= 0)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return -1; // func never returned 0
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int realpath_cb(const char* path, Handle ha, void* data)
|
||||
{
|
||||
char* full_path = (char*)data;
|
||||
struct stat s;
|
||||
|
||||
if(!path && !ha)
|
||||
{
|
||||
assert(0 && "realpath_cb: called with invalid path and archive handle");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(path)
|
||||
{
|
||||
if(!stat(path, &s))
|
||||
{
|
||||
strncpy(full_path, path, PATH_MAX);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if(ha)
|
||||
{
|
||||
if(!zip_stat(ha, path, &s))
|
||||
{
|
||||
zip_archive_info(ha, full_path, 0);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int vfs_realpath(const char* fn, char* full_path)
|
||||
{
|
||||
return vfs_foreach_path(realpath_cb, fn, full_path);
|
||||
}
|
||||
|
||||
|
||||
static int stat_cb(const char* path, Handle ha, void* data)
|
||||
{
|
||||
struct stat* s = (struct stat*)data;
|
||||
|
||||
if(path)
|
||||
return stat(path, s)? 1 : 0;
|
||||
else if(ha)
|
||||
return zip_stat(ha, path, s)? 1 : 0;
|
||||
|
||||
assert(0 && "stat_cb: called with invalid path and archive handle");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int vfs_stat(const char* fn, struct stat* s)
|
||||
{
|
||||
return vfs_foreach_path(stat_cb, fn, s);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int open_cb(const char* path, Handle ha, void* data)
|
||||
{
|
||||
struct stat s;
|
||||
VFILE* vf = (VFILE*)data;
|
||||
|
||||
// normal file
|
||||
if(path)
|
||||
{
|
||||
if(stat(path, &s) < 0)
|
||||
return 1;
|
||||
|
||||
int fd = open(path, O_RDONLY);
|
||||
if(fd < 0)
|
||||
return 1;
|
||||
|
||||
vf->fd = fd;
|
||||
vf->size = s.st_size;
|
||||
}
|
||||
// from archive
|
||||
else if(ha)
|
||||
{
|
||||
ZFILE* zf = zip_lookup(ha, path);
|
||||
if(!zf)
|
||||
return 1;
|
||||
|
||||
Handle hm;
|
||||
if(zip_archive_info(ha, 0, &hm) < 0)
|
||||
return 1;
|
||||
|
||||
vf->ofs = zf->ofs;
|
||||
vf->size = zf->csize;
|
||||
vf->ucsize = zf->ucsize;
|
||||
vf->fd = -1;
|
||||
vf->hm = hm;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(0 && "open_cb: called with invalid path and archive handle");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Handle vfs_open(const char* fn)
|
||||
{
|
||||
u32 fn_hash = fnv_hash(fn, strlen(fn));
|
||||
|
||||
VFILE* vf;
|
||||
Handle hv = h_alloc(fn_hash, H_VFILE, vfile_dtor, (void**)&vf);
|
||||
if(!hv)
|
||||
return 0;
|
||||
|
||||
// already open
|
||||
if(vf->size)
|
||||
return hv;
|
||||
|
||||
if(vfs_foreach_path(open_cb, fn, vf) < 0)
|
||||
{
|
||||
h_free(hv, H_VFILE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return hv;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const uint IDX_BITS = 4;
|
||||
const uint NUM_SLOTS = 1ul << IDX_BITS;
|
||||
const uint TAG_BITS = 32 - IDX_BITS;
|
||||
static struct Slot
|
||||
{
|
||||
u32 tag; // = 0 <==> slot available
|
||||
struct aiocb cb;
|
||||
}
|
||||
slots[NUM_SLOTS];
|
||||
|
||||
u32 vfs_start_read(const Handle hf, size_t& ofs, void** buf)
|
||||
{
|
||||
VFILE* vf = (VFILE*)h_user_data(hf, H_VFILE);
|
||||
if(!vf)
|
||||
return 0;
|
||||
|
||||
if(ofs >= vf->size)
|
||||
return 0;
|
||||
size_t bytes_left = vf->size - ofs;
|
||||
|
||||
// TODO: thread safety
|
||||
|
||||
// find a free slot
|
||||
int i = 0;
|
||||
Slot* s = slots;
|
||||
for(; i < NUM_SLOTS; i++, s++)
|
||||
if(!s->tag)
|
||||
break;
|
||||
if(i == NUM_SLOTS)
|
||||
{
|
||||
assert(!"vfs_start_read: too many active reads; increase NUM_SLOTS");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// mark it in use
|
||||
static u32 tag;
|
||||
if(++tag == 1ul << TAG_BITS)
|
||||
{
|
||||
assert(!"vfs_start_read: tag overflow!");
|
||||
tag = 1;
|
||||
}
|
||||
s->tag = tag;
|
||||
|
||||
struct aiocb* cb = &s->cb;
|
||||
|
||||
// use the buffer given (e.g. read directly into output buffer)
|
||||
if(buf)
|
||||
cb->aio_buf = *buf;
|
||||
// allocate our own (reused for subsequent requests)
|
||||
else
|
||||
if(!cb->aio_buf)
|
||||
{
|
||||
cb->aio_buf = mem_alloc(64*KB, 64*KB, MEM_HEAP);
|
||||
if(!cb->aio_buf)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// align to 64 KB for speed
|
||||
size_t rsize = 64*KB - (ofs & 0xffff); // min(~, bytes_left) - avoid warning
|
||||
if(rsize > bytes_left)
|
||||
rsize = bytes_left;
|
||||
|
||||
cb->aio_offset = (off_t)ofs;
|
||||
cb->aio_nbytes = rsize;
|
||||
aio_read(cb);
|
||||
|
||||
ofs += rsize;
|
||||
if(buf)
|
||||
(size_t&)*buf += rsize;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int vfs_finish_read(const u32 slot, void*& p, size_t& size)
|
||||
{
|
||||
p = 0;
|
||||
size = 0;
|
||||
|
||||
const uint idx = slot & (NUM_SLOTS-1);
|
||||
const u32 tag = slot >> IDX_BITS;
|
||||
Slot* const s = &slots[idx];
|
||||
if(s->tag != tag)
|
||||
{
|
||||
assert(!"vfs_finish_read: invalid slot");
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct aiocb* cb = &s->cb;
|
||||
|
||||
// wait for read to complete
|
||||
while(aio_error(cb) == -EINPROGRESS)
|
||||
aio_suspend(&cb, 1, 0);
|
||||
|
||||
ssize_t bytes_read = aio_return(cb);
|
||||
|
||||
s->tag = 0; // free this slot
|
||||
|
||||
p = (void *)cb->aio_buf;
|
||||
size = bytes_read;
|
||||
|
||||
return (bytes_read > 0)? 0 : -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Handle vfs_load(const char* fn, void*& _p, size_t& _size, bool dont_map)
|
||||
{
|
||||
_p = 0;
|
||||
_size = 0;
|
||||
|
||||
Handle hf = vfs_open(fn);
|
||||
if(!hf)
|
||||
return 0;
|
||||
|
||||
VFILE* vf = (VFILE*)h_user_data(hf, H_VFILE);
|
||||
|
||||
const bool deflated = vf->fd == -1 && vf->size != vf->ucsize;
|
||||
const size_t in_size = vf->size;
|
||||
const size_t out_size = deflated? vf->ucsize : vf->size;
|
||||
|
||||
// already mapped or read
|
||||
if(vf->hm)
|
||||
{
|
||||
MEM* m = (MEM*)h_user_data(vf->hm, H_MEM);
|
||||
if(m)
|
||||
{
|
||||
assert(out_size == m->size && "vfs_load: mismatch between VFILE and MEM size");
|
||||
|
||||
_p = m->p;
|
||||
_size = m->size;
|
||||
return vf->hm;
|
||||
}
|
||||
else
|
||||
assert(0 && "vfs_load: invalid MEM attached to VFILE");
|
||||
}
|
||||
|
||||
// decide whether to map the file, or read it
|
||||
MemType mt = MEM_MAPPED;
|
||||
if(deflated || dont_map)
|
||||
mt = MEM_POOL;
|
||||
|
||||
// allocate memory / map the file
|
||||
Handle hm;
|
||||
void* out = mem_alloc(out_size, 64*KB, mt, vf->fd, &hm);
|
||||
if(!out)
|
||||
{
|
||||
vfs_close(hf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(mt == MEM_MAPPED)
|
||||
{
|
||||
_p = out;
|
||||
_size = out_size;
|
||||
return vf->hm = hm;
|
||||
}
|
||||
|
||||
// now we read the file in 64 KB chunks (double buffered);
|
||||
// if in an archive, we inflate while waiting for the next chunk to finish
|
||||
u32 slots[2];
|
||||
int active_read = 0;
|
||||
|
||||
void* pos = out; // if not inflating, read directly into output buffer
|
||||
size_t ofs = vf->ofs;
|
||||
|
||||
void* ctx;
|
||||
if(deflated)
|
||||
{
|
||||
pos = 0; // read into separate buffer
|
||||
ctx = zip_inflate_start(out, out_size);
|
||||
}
|
||||
|
||||
bool first = true;
|
||||
bool done = false;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
// start reading next block
|
||||
if(!done)
|
||||
slots[active_read] = vfs_start_read(hf, ofs, &pos);
|
||||
|
||||
active_read ^= 1;
|
||||
|
||||
// process block read in previous iteration
|
||||
if(!first)
|
||||
{
|
||||
void* p;
|
||||
size_t bytes_read;
|
||||
vfs_finish_read(slots[active_read], p, bytes_read);
|
||||
|
||||
// inflate what we read
|
||||
if(deflated)
|
||||
zip_inflate_process(ctx, p, bytes_read);
|
||||
}
|
||||
|
||||
first = false;
|
||||
if(done)
|
||||
break;
|
||||
// one more iteration to process the last pending block
|
||||
if(ofs >= in_size)
|
||||
done = true;
|
||||
}
|
||||
|
||||
if(deflated)
|
||||
{
|
||||
if(zip_inflate_end(ctx) < 0)
|
||||
{
|
||||
mem_free(out);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
_p = out;
|
||||
_size = out_size;
|
||||
return vf->hm = hm;
|
||||
}
|
||||
|
||||
|
||||
int vfs_close(Handle h)
|
||||
{
|
||||
return h_free(h, H_VFILE);
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
// virtual file system - transparent access to files in archives;
|
||||
// allows multiple search paths
|
||||
//
|
||||
// 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 __VFS_H__
|
||||
#define __VFS_H__
|
||||
|
||||
#include "res.h"
|
||||
|
||||
#define VFS_MAX_PATH 40
|
||||
|
||||
extern int vfs_set_root(const char* argv0, const char* root);
|
||||
extern int vfs_mount(const char* path);
|
||||
extern int vfs_umount(const char* path);
|
||||
|
||||
extern int vfs_stat(const char* fn, struct stat *buffer);
|
||||
extern int vfs_realpath(const char* fn, char* realpath);
|
||||
|
||||
extern Handle vfs_load(const char* fn, void*& p, size_t& size, bool dont_map = false);
|
||||
|
||||
extern Handle vfs_open(const char* fn);
|
||||
extern int vfs_close(Handle h);
|
||||
extern u32 vfs_start_read(Handle hf, size_t& ofs, void** buf = 0);
|
||||
extern int vfs_finish_read(u32 slot, void*& p, size_t& size);
|
||||
|
||||
#endif // #ifndef __VFS_H__
|
112
source/lib/win.h
112
source/lib/win.h
@ -1,112 +0,0 @@
|
||||
#ifdef _WIN32
|
||||
|
||||
#ifndef __WIN_H__
|
||||
#define __WIN_H__
|
||||
|
||||
// Win32 socket decls aren't portable (e.g. problems with socklen_t)
|
||||
// => skip winsock.h; posix.h should be used instead
|
||||
|
||||
#define _WINSOCKAPI_
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define VC_EXTRALEAN
|
||||
|
||||
// set version; needed for EnumDisplayDevices
|
||||
#define _WIN32_WINNT 0x0500
|
||||
|
||||
|
||||
#define NOGDICAPMASKS // CC_*, LC_*, PC_*, CP_*, TC_*, RC_
|
||||
//#define NOVIRTUALKEYCODES // VK_*
|
||||
//#define NOWINMESSAGES // WM_*, EM_*, LB_*, CB_*
|
||||
//#define NOWINSTYLES // WS_*, CS_*, ES_*, LBS_*, SBS_*, CBS_*
|
||||
#define NOSYSMETRICS // SM_*
|
||||
#define NOMENUS // MF_*
|
||||
#define NOICONS // IDI_*
|
||||
#define NOKEYSTATES // MK_*
|
||||
//#define NOSYSCOMMANDS // SC_*
|
||||
#define NORASTEROPS // Binary and Tertiary raster ops
|
||||
//#define NOSHOWWINDOW // SW_*
|
||||
#define OEMRESOURCE // OEM Resource values
|
||||
#define NOATOM // Atom Manager routines
|
||||
//#define NOCLIPBOARD // Clipboard routines
|
||||
#define NOCOLOR // Screen colors
|
||||
#define NOCTLMGR // Control and Dialog routines
|
||||
#define NODRAWTEXT // DrawText() and DT_*
|
||||
//#define NOGDI // All GDI defines and routines
|
||||
//#define NOKERNEL // All KERNEL defines and routines
|
||||
//#define NOUSER // All USER defines and routines
|
||||
#define NONLS // All NLS defines and routines
|
||||
//#define NOMB // MB_* and MessageBox()
|
||||
#define NOMEMMGR // GMEM_*, LMEM_*, GHND, LHND, associated routines
|
||||
#define NOMETAFILE // typedef METAFILEPICT
|
||||
#define NOMINMAX // Macros min(a,b) and max(a,b)
|
||||
//#define NOMSG // typedef MSG and associated routines
|
||||
#define NOOPENFILE // OpenFile(), OemToAnsi, AnsiToOem, and OF_*
|
||||
#define NOSCROLL // SB_* and scrolling routines
|
||||
#define NOSERVICE // All Service Controller routines, SERVICE_ equates, etc.
|
||||
//#define NOSOUND // Sound driver routines
|
||||
#define NOTEXTMETRIC // typedef TEXTMETRIC and associated routines
|
||||
//#define NOWH // SetWindowsHook and WH_*
|
||||
#define NOWINOFFSETS // GWL_*, GCL_*, associated routines
|
||||
//#define NOCOMM // COMM driver routines
|
||||
#define NOKANJI // Kanji support stuff.
|
||||
#define NOHELP // Help engine interface.
|
||||
#define NOPROFILER // Profiler interface.
|
||||
#define NODEFERWINDOWPOS // DeferWindowPos routines
|
||||
#define NOMCX // Modem Configuration Extensions
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
// VC6 windows.h doesn't define these
|
||||
#ifndef INVALID_FILE_ATTRIBUTES
|
||||
#define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
|
||||
#endif
|
||||
|
||||
#ifndef PROCESSOR_ARCHITECTURE_AMD64
|
||||
#define PROCESSOR_ARCHITECTURE_AMD64 9
|
||||
#endif
|
||||
|
||||
// end VC6 fixes
|
||||
|
||||
|
||||
// HACK: warning-free definition for ICC (value is -1)
|
||||
#undef INVALID_HANDLE_VALUE
|
||||
const HANDLE INVALID_HANDLE_VALUE = (HANDLE)(((char*)0) + ~0);
|
||||
|
||||
#define HANDLE2INT(_h) ((char *)_h - (char *)0)
|
||||
|
||||
extern "C" {
|
||||
extern int _get_osfhandle(int);
|
||||
extern int _open(const char* fn, int mode, ...);
|
||||
extern int _close(int);
|
||||
extern int aio_open_winhandle(HANDLE);
|
||||
|
||||
#ifndef NO_WINSOCK
|
||||
#ifdef _MSC_VER
|
||||
#pragma comment(lib, "ws2_32.lib")
|
||||
#endif
|
||||
|
||||
extern __declspec(dllimport) int __stdcall WSAAsyncSelect(int s, HANDLE hWnd, unsigned int wMsg, long lEvent);
|
||||
|
||||
#define FD_READ_BIT 0
|
||||
#define FD_READ (1 << FD_READ_BIT)
|
||||
|
||||
#define FD_WRITE_BIT 1
|
||||
#define FD_WRITE (1 << FD_WRITE_BIT)
|
||||
|
||||
#define FD_ACCEPT_BIT 3
|
||||
#define FD_ACCEPT (1 << FD_ACCEPT_BIT)
|
||||
|
||||
#define FD_CONNECT_BIT 4
|
||||
#define FD_CONNECT (1 << FD_CONNECT_BIT)
|
||||
|
||||
#define FD_CLOSE_BIT 5
|
||||
#define FD_CLOSE (1 << FD_CLOSE_BIT)
|
||||
|
||||
extern __declspec(dllimport) int __stdcall WSAStartup(WORD, void*);
|
||||
extern __declspec(dllimport) int __stdcall WSAGetLastError();
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // #ifndef __WIN_H__
|
||||
#endif // #ifdef _WIN32
|
@ -1,560 +0,0 @@
|
||||
/*
|
||||
* emulation of a subset of SDL and GLUT 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/
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <process.h>
|
||||
|
||||
#include "wsdl.h"
|
||||
#include "win.h"
|
||||
#include "misc.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma comment(lib, "user32.lib")
|
||||
#pragma comment(lib, "gdi32.lib")
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* 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)
|
||||
{
|
||||
if(!ev)
|
||||
return -1;
|
||||
|
||||
// events that trigger messages (mouse done below)
|
||||
MSG msg;
|
||||
while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
|
||||
{
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
|
||||
int sdl_btn = -1;
|
||||
|
||||
switch(msg.message)
|
||||
{
|
||||
//
|
||||
case WM_KEYUP:
|
||||
case WM_KEYDOWN:
|
||||
ev->type = (u8)((msg.message == WM_KEYUP)? SDL_KEYUP : SDL_KEYDOWN);
|
||||
ev->key.keysym.sym = (SDLKey)msg.wParam;
|
||||
return 1;
|
||||
|
||||
//
|
||||
case WM_ACTIVATE:
|
||||
ev->type = SDL_ACTIVE;
|
||||
ev->active.gain = app_active;
|
||||
ev->active.state = 0;
|
||||
return 1;
|
||||
|
||||
//
|
||||
case WM_MOUSEWHEEL:
|
||||
sdl_btn = (msg.wParam & BIT(31))? SDL_BUTTON_WHEELUP : SDL_BUTTON_WHEELDOWN;
|
||||
break; // event filled in mouse code below
|
||||
}
|
||||
|
||||
// mouse button
|
||||
// map Win L(up,down,double),R(),M() to L,R,M with up flag
|
||||
uint btn = msg.message-0x201; // 0..8 if it's a valid button;
|
||||
if(btn < 9 && btn%3 != 2) // every third msg is dblclick
|
||||
sdl_btn = SDL_BUTTON_LEFT + btn/3; // assumes L,R,M
|
||||
if(sdl_btn != -1)
|
||||
{
|
||||
ev->type = SDL_MOUSEBUTTONDOWN + btn%3;
|
||||
ev->button.button = (u8)sdl_btn;
|
||||
ev->button.x = (u16)(msg.lParam & 0xffff);
|
||||
ev->button.y = (u16)((msg.lParam >> 16) & 0xffff);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// mouse motion
|
||||
//
|
||||
// don't use DirectInput, because we want to respect the user's mouse
|
||||
// sensitivity settings. 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;
|
||||
|
||||
dm.dmBitsPerPel = bpp;
|
||||
dm.dmFields = DM_BITSPERPEL;
|
||||
|
||||
/*
|
||||
* 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(/* 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.dmFields |= DM_PELSWIDTH|DM_PELSHEIGHT;
|
||||
}
|
||||
// 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, w, h, 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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void SDL_Quit()
|
||||
{
|
||||
DestroyWindow(hWnd);
|
||||
|
||||
wglMakeCurrent(0, 0);
|
||||
wglDeleteContext(hGLRC);
|
||||
ChangeDisplaySettings(0, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#define DDRAW
|
||||
|
||||
|
||||
#ifdef DDRAW
|
||||
#include <ddraw.h>
|
||||
#ifdef _MSC_VER
|
||||
#pragma comment(lib, "ddraw.lib")
|
||||
// for DirectDrawCreate. don't bother with dynamic linking -
|
||||
// DirectX is present in all Windows versions since Win95.
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
SDL_VideoInfo* SDL_GetVideoInfo()
|
||||
{
|
||||
static SDL_VideoInfo video_info;
|
||||
|
||||
#ifdef DDRAW
|
||||
|
||||
static bool init;
|
||||
if(!init)
|
||||
{
|
||||
IDirectDraw* dd = 0;
|
||||
HRESULT hr = DirectDrawCreate(0, &dd, 0);
|
||||
if(SUCCEEDED(hr) && dd != 0)
|
||||
{
|
||||
static DDCAPS caps;
|
||||
caps.dwSize = sizeof(caps);
|
||||
hr = dd->GetCaps(&caps, 0);
|
||||
if(SUCCEEDED(hr))
|
||||
video_info.video_mem = caps.dwVidMemTotal;
|
||||
dd->Release();
|
||||
}
|
||||
|
||||
init = true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return &video_info;
|
||||
}
|
||||
|
||||
|
||||
SDL_Surface* SDL_GetVideoSurface()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
__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, MAKEINTRESOURCE(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();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,309 +0,0 @@
|
||||
#ifndef _WIN32
|
||||
#include <SDL/SDL.h>
|
||||
#include <SDL/SDL_thread.h>
|
||||
#define __WSDL_H__ // => rest of header ignored
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef __WSDL_H__
|
||||
#define __WSDL_H__
|
||||
|
||||
|
||||
#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 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_Init
|
||||
|
||||
#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',
|
||||
|
||||
SDLK_KP_PLUS = 0x6b,
|
||||
SDLK_KP_MINUS = 0x6d,
|
||||
SDLK_LEFT = 0x25,
|
||||
SDLK_UP = 0x26,
|
||||
SDLK_RIGHT = 0x27,
|
||||
SDLK_DOWN = 0x28,
|
||||
|
||||
SDLK_KP0 = 0x60,
|
||||
SDLK_KP1 = 0x61,
|
||||
SDLK_KP2 = 0x62,
|
||||
SDLK_KP3 = 0x63,
|
||||
SDLK_KP4 = 0x64,
|
||||
SDLK_KP5 = 0x65,
|
||||
SDLK_KP6 = 0x66,
|
||||
SDLK_KP7 = 0x67,
|
||||
SDLK_KP8 = 0x68,
|
||||
SDLK_KP9 = 0x69,
|
||||
|
||||
__SDLK // hack to allow trailing comma
|
||||
}
|
||||
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
|
||||
{
|
||||
// do not change order
|
||||
SDL_BUTTON_LEFT,
|
||||
SDL_BUTTON_RIGHT,
|
||||
SDL_BUTTON_MIDDLE,
|
||||
|
||||
SDL_BUTTON_WHEELUP,
|
||||
SDL_BUTTON_WHEELDOWN
|
||||
};
|
||||
|
||||
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 // #ifndef __WSDL_H__
|
@ -1,285 +0,0 @@
|
||||
// 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 <cassert>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "zip.h"
|
||||
#include "posix.h"
|
||||
#include "misc.h"
|
||||
#include "res.h"
|
||||
#include "mem.h"
|
||||
#include "vfs.h"
|
||||
|
||||
// don't include windows.h from zconf!
|
||||
// not necessary, causes many conflicts.
|
||||
#define _WINDOWS_
|
||||
#define WINAPI __stdcall
|
||||
#define WINAPIV __cdecl
|
||||
|
||||
#include <zlib.h>
|
||||
#ifdef _MSC_VER
|
||||
#pragma comment(lib, "zlib.lib")
|
||||
#endif
|
||||
|
||||
|
||||
// H_ZARCHIVE
|
||||
// information about a Zip archive
|
||||
struct ZARCHIVE
|
||||
{
|
||||
Handle hm; // archive in memory (usually mapped)
|
||||
|
||||
const char* fn; // archive filename
|
||||
|
||||
// file lookup
|
||||
u16 num_files;
|
||||
u16 last_file; // index of last file we found (speed up lookups of sequential files)
|
||||
u32* fn_hashs; // split for more efficient search
|
||||
ZFILE* files;
|
||||
};
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
||||
static void zarchive_dtor(void* p)
|
||||
{
|
||||
ZARCHIVE* za = (ZARCHIVE*)p;
|
||||
|
||||
mem_free(za->hm);
|
||||
mem_free((void*)za->fn);
|
||||
mem_free(za->fn_hashs); // both fn_hashs[] and files[]
|
||||
}
|
||||
|
||||
|
||||
// open and return a handle to the zip archive indicated by <fn>
|
||||
Handle zip_open(const char* const fn)
|
||||
{
|
||||
const u8* ecdr; // declared here to avoid goto scope problems
|
||||
|
||||
const u32 fn_hash = fnv_hash(fn, strlen(fn));
|
||||
|
||||
// allocate a handle
|
||||
ZARCHIVE* za;
|
||||
Handle ha = h_alloc(fn_hash, H_ZARCHIVE, zarchive_dtor, (void**)&za);
|
||||
// .. failed
|
||||
if(!ha)
|
||||
return 0;
|
||||
// .. archive already loaded
|
||||
if(za->hm)
|
||||
return ha;
|
||||
|
||||
// map the Zip file
|
||||
const u8* zfile;
|
||||
size_t size;
|
||||
Handle hm = vfs_load(fn, (void*&)zfile, size);
|
||||
if(!hm)
|
||||
goto fail;
|
||||
|
||||
{
|
||||
// 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
|
||||
|
||||
size_t bytes_left = 66000; // min(66k, size) - avoid stupid warning
|
||||
if(bytes_left > size)
|
||||
bytes_left = size;
|
||||
ecdr = zfile + size - bytes_left;
|
||||
|
||||
while(bytes_left-3 > 0)
|
||||
{
|
||||
if(*(u32*)ecdr == *(u32*)&ecdr_id)
|
||||
goto found_ecdr;
|
||||
|
||||
// check next 4 bytes (non aligned!!)
|
||||
ecdr++;
|
||||
bytes_left--;
|
||||
}
|
||||
|
||||
// reached EOF and still haven't found the ECDR identifier
|
||||
}
|
||||
|
||||
fail:
|
||||
h_free(ha, H_ZARCHIVE);
|
||||
mem_free(hm);
|
||||
return 0;
|
||||
|
||||
found_ecdr:
|
||||
{
|
||||
// read ECDR
|
||||
const u16 num_files = read_le16(ecdr+10);
|
||||
const u32 cd_ofs = read_le32(ecdr+16);
|
||||
|
||||
// memory for fn_hash and File arrays
|
||||
void* mem = mem_alloc(num_files * (sizeof(u32) + sizeof(ZFILE)), 4*KB, MEM_HEAP);
|
||||
if(!mem)
|
||||
goto fail;
|
||||
|
||||
za->hm = hm;
|
||||
za->fn_hashs = (u32*)mem;
|
||||
za->files = (ZFILE*)((u8*)mem + sizeof(u32)*num_files);
|
||||
za->last_file = 0;
|
||||
|
||||
za->fn = (const char*)mem_alloc(strlen(fn)+1);
|
||||
strcpy((char*)za->fn, fn);
|
||||
|
||||
// cache file list for faster lookups
|
||||
// currently linear search, comparing filename hash.
|
||||
// TODO: if too slow, use hash table.
|
||||
const u8* cdfh = zfile+cd_ofs;
|
||||
u32* hs = za->fn_hashs;
|
||||
ZFILE* f = za->files;
|
||||
u16 i;
|
||||
for(i = 0; i < 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];
|
||||
|
||||
if(method & ~8) // neither deflated nor stored
|
||||
continue;
|
||||
|
||||
// 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;
|
||||
f->ucsize = ucsize;
|
||||
f++;
|
||||
|
||||
(uintptr_t&)cdfh += 46 + fn_len + e_len + c_len;
|
||||
}
|
||||
|
||||
za->num_files = i;
|
||||
}
|
||||
|
||||
return ha;
|
||||
}
|
||||
|
||||
|
||||
int zip_close(Handle ha)
|
||||
{
|
||||
return h_free(ha, H_ZARCHIVE);
|
||||
}
|
||||
|
||||
|
||||
ZFILE* zip_lookup(Handle ha, const char* fn)
|
||||
{
|
||||
ZARCHIVE* za = (ZARCHIVE*)h_user_data(ha, H_ZARCHIVE);
|
||||
if(!za)
|
||||
return 0;
|
||||
|
||||
// find its File descriptor
|
||||
const u32 fn_hash = fnv_hash(fn, strlen(fn));
|
||||
u16 i = za->last_file+1;
|
||||
if(i >= za->num_files || za->fn_hashs[i] != fn_hash)
|
||||
{
|
||||
for(i = 0; i < za->num_files; i++)
|
||||
if(za->fn_hashs[i] == fn_hash)
|
||||
break;
|
||||
if(i == za->num_files)
|
||||
return 0;
|
||||
|
||||
za->last_file = i;
|
||||
}
|
||||
|
||||
return &za->files[i];
|
||||
}
|
||||
|
||||
|
||||
int zip_stat(Handle ha, const char* fn, struct stat* s)
|
||||
{
|
||||
ZFILE* zf = zip_lookup(ha, fn);
|
||||
if(!zf)
|
||||
return -1;
|
||||
|
||||
s->st_size = (off_t)zf->ucsize;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int zip_archive_info(Handle ha, char* fn, Handle* hm)
|
||||
{
|
||||
ZARCHIVE* za = (ZARCHIVE*)h_user_data(ha, H_ZARCHIVE);
|
||||
if(!za)
|
||||
return -1;
|
||||
|
||||
if(fn)
|
||||
strcpy(fn, za->fn);
|
||||
|
||||
if(hm)
|
||||
*hm = za->hm;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void* zip_inflate_start(void* out, size_t out_size)
|
||||
{
|
||||
z_stream* stream = (z_stream*)mem_alloc(sizeof(z_stream), 4, MEM_POOL);
|
||||
memset(stream, 0, sizeof(stream));
|
||||
|
||||
if(inflateInit2(stream, -MAX_WBITS) != Z_OK)
|
||||
return 0;
|
||||
// -MAX_WBITS indicates no zlib header present
|
||||
|
||||
stream->next_out = (Bytef*)out;
|
||||
stream->avail_out = (uInt)out_size;
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
|
||||
int zip_inflate_process(void* ctx, void* p, size_t size)
|
||||
{
|
||||
z_stream* stream = (z_stream*)ctx;
|
||||
|
||||
stream->next_in = (Bytef*)p;
|
||||
stream->avail_in = (uInt)size;
|
||||
return inflate(stream, 0);
|
||||
}
|
||||
|
||||
|
||||
int zip_inflate_end(void* ctx)
|
||||
{
|
||||
z_stream* stream = (z_stream*)ctx;
|
||||
inflateEnd(stream);
|
||||
|
||||
// didn't read everything
|
||||
if(stream->avail_out || stream->avail_in)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,48 +0,0 @@
|
||||
// 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 __ZIP_H__
|
||||
#define __ZIP_H__
|
||||
|
||||
#include "res.h"
|
||||
|
||||
// open and return a handle to the zip archive indicated by <fn>
|
||||
extern Handle zip_open(const char* fn);
|
||||
extern int zip_close(Handle ha);
|
||||
|
||||
extern int zip_archive_info(Handle ha, char* fn, Handle* hm);
|
||||
|
||||
extern int zip_stat(Handle ha, const char* fn, struct stat* s);
|
||||
|
||||
|
||||
struct ZFILE
|
||||
{
|
||||
size_t ofs;
|
||||
size_t csize;
|
||||
size_t ucsize;
|
||||
};
|
||||
|
||||
// return information about file <fn> in archive <ha> (i.e. 'open' it)
|
||||
extern ZFILE* zip_lookup(Handle ha, const char* fn);
|
||||
|
||||
extern void* zip_inflate_start(void* out, size_t out_size);
|
||||
extern int zip_inflate_process(void* ctx, void* p, size_t size);
|
||||
extern int zip_inflate_end(void* ctx);
|
||||
|
||||
|
||||
#endif // #ifndef __ZIP_H__
|
Loading…
Reference in New Issue
Block a user