diff --git a/source/lib/detect.cpp b/source/lib/detect.cpp index 12fcd89572..c5ad468411 100755 --- a/source/lib/detect.cpp +++ b/source/lib/detect.cpp @@ -57,6 +57,8 @@ void get_cur_resolution(int& xres, int& yres) size_t tot_mem = 0; size_t avl_mem = 0; +size_t page_size = 0; + void get_mem_status() { // Win32 @@ -68,10 +70,19 @@ void get_mem_status() // fixes results for my machine - off by 528 KB. why?! avl_mem = ms.dwAvailPhys; -// Sys V derived (GNU/Linux, Solaris) -#elif defined(_SC_PAGESIZE) && defined(_SC_AVPHYS_PAGES) + SYSTEM_INFO si; + GetSystemInfo(&si); + page_size = si.dwPageSize; + +#else // !_WIN32 + +#ifdef _SC_PAGESIZE + page_size = (size_t)sysconf(_SC_PAGESIZE); +#endif + +// Sys V derived (GNU/Linux, Solaris) +#ifdef _SC_AVPHYS_PAGES - long page_size = sysconf(_SC_PAGESIZE); tot_mem = sysconf(_SC_PHYS_PAGES ) * page_size; avl_mem = sysconf(_SC_AVPHYS_PAGES) * page_size; @@ -85,6 +96,8 @@ void get_mem_status() sysctl(mib, 2, &avl_mem, &len, 0, 0); #endif + +#endif // _WIN32 } diff --git a/source/lib/font.cpp b/source/lib/font.cpp index 1271edadd6..e782330143 100755 --- a/source/lib/font.cpp +++ b/source/lib/font.cpp @@ -25,6 +25,7 @@ #include "font.h" #include "res.h" +#include "vfs.h" #include "tex.h" #include "ogl.h" #include "posix.h" @@ -126,13 +127,21 @@ static void font_dtor(void* p) } -u32 font_load(const char* fn) +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; - HDATA* hd; - Handle h = res_load(fn, H_FONT, font_dtor, p, size, hd); - if(!h) + Handle hm = vfs_load(fn, p, size); + if(!hm) return 0; int pos; // current position in the file @@ -159,6 +168,8 @@ u32 font_load(const char* fn) } } + h_free(hm, H_MEM); + // load glyph texture const Handle tex = tex_load(tex_filename); if(!tex) @@ -191,7 +202,6 @@ u32 font_load(const char* fn) u = 0.f, v += th; } - FONT* font = (FONT*)hd->user; font->tex = tex; font->list_base = list_base; @@ -201,10 +211,9 @@ u32 font_load(const char* fn) int font_bind(const Handle h) { - HDATA* hd = h_data(h, H_FONT); - if(!hd) + FONT* font = (FONT*)h_user_data(h, H_FONT); + if(!font) return -1; - FONT* font = (FONT*)hd->user; tex_bind(font->tex); glListBase(font->list_base); diff --git a/source/lib/mem.cpp b/source/lib/mem.cpp index f6734e7eee..ae51259381 100755 --- a/source/lib/mem.cpp +++ b/source/lib/mem.cpp @@ -1,6 +1,7 @@ // malloc layer for less fragmentation, alignment, and automatic release #include +#include #include @@ -11,58 +12,43 @@ #include "posix.h" -struct MEM +static void heap_free(MEM* m) { - uint type; - - // MEM_HEAP only - void* org_p; - - // MEM_POOL only - size_t ofs; - size_t size; -}; - - - -static void heap_dtor(void* p) -{ - MEM* mem = (MEM*)p; - free(mem->org_p); + free(m->org_p); } -static void* heap_alloc(const size_t size, const int align, MEM& mem) +static void* heap_alloc(const size_t size, const int align, MEM* mem) { u8* org_p = (u8*)malloc(size+align-1); u8* p = (u8*)round_up((long)org_p, align); - mem.org_p = org_p; + mem->org_p = org_p; return p; } +////////////////////////////////////////////////////////////////////////////// + static u8* pool; static size_t pool_pos; static const size_t POOL_CAP = 64*MB; // TODO: user editable -static void pool_dtor(void* p) +static void pool_free(MEM* m) { - MEM* mem = (MEM*)p; - // at end of pool? if so, 'free' it - if(mem->ofs + mem->size == pool_pos) - pool_pos -= mem->size; + if(m->ofs + m->size == pool_pos) + pool_pos -= m->size; } -static void* pool_alloc(const size_t size, const uint align, MEM& mem) +static void* pool_alloc(const size_t size, const uint align, MEM* mem) { if(!pool) { - pool = (u8*)mem_alloc(size, MEM_HEAP, align); + pool = (u8*)mem_alloc(size, align, MEM_HEAP); if(!pool) return 0; } @@ -73,52 +59,47 @@ static void* pool_alloc(const size_t size, const uint align, MEM& mem) void* p = (u8*)pool + ofs; - mem.size = size; - mem.ofs = ofs; + mem->size = size; + mem->ofs = ofs; pool_pos = ofs+size; return p; } +////////////////////////////////////////////////////////////////////////////// -static void mem_dtor(void* p) + +static void mmap_free(MEM* m) { - MEM* mem = (MEM*)p; - if(mem->type == MEM_HEAP) - heap_dtor(p); - else if(mem->type == MEM_POOL) - pool_dtor(p); + munmap(m->p, m->size); } -void* mem_alloc(size_t size, const MemType type, const uint align, Handle* ph) +static void* mmap_alloc(const size_t size, const int fd, MEM* mem) { - if(size == 0) - size = 1; + mem->p = mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0); + mem->size = size; + mem->fd = fd; - HDATA* hd; - Handle h = h_alloc(0, H_MEM, mem_dtor, &hd); - if(!h) - return 0; + return mem->p; +} - MEM& mem = (MEM&)hd->user; - void* p; - if(type == MEM_HEAP) - p = heap_alloc(size, align, mem); - else if(type == MEM_POOL) - p = pool_alloc(size, align, mem); +////////////////////////////////////////////////////////////////////////////// + + +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 - { - h_free(h, H_MEM); - return 0; - } - - if(ph) - *ph = h; - - return p; + assert(0 && "mem_dtor: MEM.type invalid!"); } @@ -127,9 +108,60 @@ int mem_free(void* p) if(!p) return 1; - HDATA* hd; - Handle h = h_find(p, H_MEM, &hd); + Handle h = h_find((u32)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((u32)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; } \ No newline at end of file diff --git a/source/lib/mem.h b/source/lib/mem.h index 888053e340..5dbccb43de 100755 --- a/source/lib/mem.h +++ b/source/lib/mem.h @@ -3,13 +3,36 @@ #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_HEAP, + MEM_MAPPED }; -extern void* mem_alloc(size_t size, MemType type = MEM_HEAP, uint align = 1, Handle* ph = 0); + +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__ diff --git a/source/lib/posix.cpp b/source/lib/posix.cpp index 4e21c3c51b..66f7be2bb5 100755 --- a/source/lib/posix.cpp +++ b/source/lib/posix.cpp @@ -477,7 +477,6 @@ int uname(struct utsname* un) // hardware type static SYSTEM_INFO si; GetSystemInfo(&si); - if(si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) strcpy(un->machine, "AMD64"); else diff --git a/source/lib/res.cpp b/source/lib/res.cpp index 55b049289a..37a82a458d 100755 --- a/source/lib/res.cpp +++ b/source/lib/res.cpp @@ -22,13 +22,39 @@ #include #include "types.h" -#include "unzip.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 +{ + u32 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; @@ -70,7 +96,7 @@ static int h_idx(const Handle h, const uint type) return -1; const HDATA* hd = h_data(idx); - // cannot fail - it was successfully allocated + // 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 @@ -101,17 +127,7 @@ static int h_free(const int idx) if(--hd->refs) return 0; - // free its pointer - switch(hd->ptype) - { - case PT_MEM: - mem_free(hd->p); - break; - - case PT_MAP: - munmap(hd->p, hd->size); - break; - } + // TODO: keep this handle open (cache) // call its type's destructor if(dtors[hd->type]) @@ -143,25 +159,7 @@ static void cleanup(void) } -Handle h_find(const void* p, uint type, HDATA** puser) -{ - for(int idx = 0; idx <= last_in_use; idx++) - { - HDATA* hd = h_data(idx); // guaranteed valid - - if(hd->p == p && hd->type == type) - { - if(puser) - *puser = hd/*->user*/; - return handle(idx); - } - } - - return 0; -} - - -Handle h_find(const u32 key, uint type, HDATA** puser) +Handle h_find(const u32 key, uint type, void** puser) { int idx; HDATA* hd; @@ -195,32 +193,12 @@ Handle h_find(const u32 key, uint type, HDATA** puser) found: Handle h = handle(idx); if(puser) - *puser = hd/*->user*/; + *puser = hd->user; return h; } -int h_assign(Handle h, u8* p, size_t size, bool mem) -{ - HDATA* hd = h_data(h); - if(!hd) - return -1; - - if(hd->p || hd->size || hd->ptype != PT_NONE) - { - assert(!"h_assign: field(s) already assigned"); - return -1; - } - - hd->p = p; - hd->size = size; - hd->ptype = mem? PT_MEM : PT_MAP; - - return 0; -} - - -Handle h_alloc(const u32 key, const uint type, /*const size_t user_size,*/ DTOR dtor, HDATA** puser) +Handle h_alloc(const u32 key, const uint type, /*const size_t user_size,*/ H_DTOR dtor, void** puser) { ONCE(atexit(cleanup)) /* @@ -253,11 +231,10 @@ Handle h_alloc(const u32 key, const uint type, /*const size_t user_size,*/ DTOR if(key) { // object already loaded? - Handle h = h_find(key, type, &hd); + Handle h = h_find(key, type, 0); if(h) { - // only way to decide whether this handle is new, or a reference - assert(hd->size != 0); + hd = h_data(h_idx(h, type)); if(hd->refs == (1ul << HREF_BITS)) { @@ -320,14 +297,15 @@ Handle h_alloc(const u32 key, const uint type, /*const size_t user_size,*/ DTOR hd->type = type; if(puser) - *puser = hd/*->user*/; + *puser = hd->user; return handle(idx); } -int h_free(const Handle h, const uint type) +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; @@ -335,52 +313,14 @@ int h_free(const Handle h, const uint type) -HDATA* h_data(const Handle h, const uint type) +void* h_user_data(const Handle h, const uint type) { int idx = h_idx(h, type); if(idx >= 0) - return h_data(idx); + return h_data(idx)->user; // pointer is always valid if index is in range return 0; } -Handle res_load(const char* fn, uint type, DTOR dtor, void*& p, size_t& size, HDATA*& user) -{ - p = 0; - size = 0; - user = 0; - - u32 fn_hash = fnv_hash(fn, strlen(fn)); - // TODO: fn is usually a constant; pass fn len if too slow - - HDATA* hd; - Handle h = h_alloc(fn_hash, type, dtor, &hd); - if(!h) - return 0; - - // already open - return a reference - if(hd->size) - return h; - - Handle hf = vfs_open(fn); - int err = vfs_read(hf, p, size, 0); - vfs_close(hf); - if(err < 0) - return 0; - - if(!p) - { - h_free(h, type); - return 0; - } - - hd->p = p; - hd->size = size; -user=hd; - return h; -} - - - diff --git a/source/lib/res.h b/source/lib/res.h index ad68f3676a..af2e2fdcdd 100755 --- a/source/lib/res.h +++ b/source/lib/res.h @@ -21,16 +21,10 @@ #include "types.h" -// the following are internal to the resource manager, -// but are required for the HDATA definition -// (which is passed around to modules that create handles). -// we don't want to waste memory or fragment the handle data -// by splitting into internal/external - // handle type (for 'type safety' - can't use a texture handle as a sound) // -// rationale: we could use the destructor passed to res_load to identify +// 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. @@ -47,41 +41,7 @@ enum HType NUM_HANDLE_TYPES }; -// handle's pointer type -enum PType -{ - PT_NONE, - PT_MEM, // allocated by mem_alloc - PT_MAP // mapped by mmap -}; -// 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; - -// <= 32-TAG_BITS bits -const uint HTYPE_BITS = 4; -const uint PTYPE_BITS = 4; -const uint HREF_BITS = 8; - -const int HDATA_USER_SIZE = 16; - -// 32 bytes -struct HDATA -{ - u32 key; - u32 tag : HTAG_BITS; - u32 type : HTYPE_BITS; // handle's type (e.g. texture, sound) - u32 ptype : PTYPE_BITS; // what does p point to? - u32 refs : HREF_BITS; - - void* p; - size_t size; - - u8 user[HDATA_USER_SIZE]; -}; // 0 = invalid handle value. @@ -90,7 +50,7 @@ typedef u32 Handle; // destructor, registered by h_alloc for a given handle type. // receives the user data associated with the handle. -typedef void(*DTOR)(void*); +typedef void(*H_DTOR)(void*); // all functions check the passed tag (part of the handle) and type against @@ -105,20 +65,15 @@ typedef void(*DTOR)(void*); //// 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(u32 key, uint type,/* size_t user_size,*/ DTOR dtor = 0, HDATA** puser = 0); -extern int h_free(Handle h, uint type); +extern Handle h_alloc(u32 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(u32 key, uint type, HDATA** puser = 0); - -// same as above, but search for the handle's associated pointer -extern Handle h_find(const void* p, uint type, HDATA** puser = 0); +extern Handle h_find(u32 key, uint type, void** puser = 0); // return a pointer to handle data -extern HDATA* h_data(Handle h, uint type); - -extern Handle res_load(const char* fn, uint type, DTOR dtor, void*& p, size_t& size, HDATA*& user); +extern void* h_user_data(Handle h, uint type); #endif // #ifndef __RES_H__ diff --git a/source/lib/tex.cpp b/source/lib/tex.cpp index 093fb4fcda..240a507b29 100755 --- a/source/lib/tex.cpp +++ b/source/lib/tex.cpp @@ -21,8 +21,11 @@ #include #include #include +#include +#include "vfs.h" #include "tex.h" +#include "mem.h" #include "ogl.h" #include "res.h" #include "misc.h" @@ -134,8 +137,6 @@ static int dds_load(const char* fn, const u8* ptr, size_t size, TEX* tex) const u32 img_size = read_le32(&surf->dwLinearSize); const u32 mipmaps = read_le32(&surf->dwMipMapCount); - const u8* img = ptr + hdr_size; - uint fmt = 0; switch(surf->ddpfPixelFormat.dwFourCC) // endian-independent { @@ -154,7 +155,7 @@ static int dds_load(const char* fn, const u8* ptr, size_t size, TEX* tex) tex->height = h; tex->fmt = fmt; tex->bpp = img_size / (w * h); - tex->ptr = img; + tex->ofs = hdr_size; if(sizeof(DDSURFACEDESC2) != ddsd_size) err = "DDSURFACEDESC2 size mismatch"; @@ -214,7 +215,6 @@ static int tga_load(const char* fn, const u8* ptr, size_t size, TEX* tex) const u8 alpha_bits = desc & 0x0f; - const u8* img = ptr + hdr_size; const ulong img_size = (ulong)w * h * bpp / 8; // determine format @@ -240,7 +240,7 @@ static int tga_load(const char* fn, const u8* ptr, size_t size, TEX* tex) tex->height = h; tex->fmt = fmt; tex->bpp = bpp; - tex->ptr = img; + tex->ofs = hdr_size; if(fmt == -1) err = "invalid format or bpp"; @@ -323,14 +323,13 @@ static int bmp_load(const char* fn, const u8* ptr, size_t size, TEX* tex) const u32 compress = read_le32(&bch->biCompression); const u32 ofs = read_le32(&bfh->bfOffBits); - const u8* img = ptr + ofs; const u32 img_size = w * h * bpp/8; tex->width = w; tex->height = h; tex->fmt = (bpp == 24)? GL_BGR : GL_BGRA; tex->bpp = bpp; - tex->ptr = img; + tex->ofs = ofs; if(h < 0) err = "top-down"; @@ -386,7 +385,7 @@ static int raw_load(const char* fn, const u8* ptr, size_t size, TEX* tex) tex->height = dim; tex->fmt = fmts[i]; tex->bpp = i * 8; - tex->ptr = ptr; + tex->ofs = 0; return 0; } @@ -591,28 +590,38 @@ static int jp2_load(const char* fn, const u8* ptr, size_t size, TEX* tex) 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; - HDATA* hd; - Handle h = res_load(fn, H_TEX, tex_dtor, (void*&)p, size, hd); - if(!h) - return 0; - - TEX* tex = (TEX*)hd->user; - if(!p) - goto already_loaded; - if(size < 4) // guarantee xxx_valid routines 4 bytes - return 0; - + 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 @@ -643,7 +652,6 @@ Handle tex_load(const char* fn, TEX* ptex) if(err < 0) return err; - } // loaders weren't able to determine type if(tex->fmt == 0) @@ -653,43 +661,45 @@ Handle tex_load(const char* fn, TEX* ptex) // 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 h; + return ht; } int tex_bind(const Handle h) { - HDATA* hd = h_data(h, H_TEX); - if(!hd) + TEX* tex = (TEX*)h_user_data(h, H_TEX); + if(!tex) { glBindTexture(GL_TEXTURE_2D, 0); return -1; } - TEX* tex = (TEX*)hd->user; - glBindTexture(GL_TEXTURE_2D, tex->id); - return 0; + 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 h, int filter, int int_fmt) +int tex_upload(const Handle ht, int filter, int int_fmt) { - HDATA* hd = h_data(h, H_TEX); - if(!hd) + TEX* tex = (TEX*)h_user_data(ht, H_TEX); + if(!tex) return -1; - TEX* tex = (TEX*)hd->user; // greater than max supported tex dimension? // no-op if oglInit not yet called @@ -708,7 +718,18 @@ int tex_upload(const Handle h, int filter, int int_fmt) return 0; } - tex_bind(h); + 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) @@ -726,8 +747,9 @@ int tex_upload(const Handle h, int filter, int int_fmt) if(tex->fmt >= GL_COMPRESSED_RGB_S3TC_DXT1_EXT && tex->fmt <= GL_COMPRESSED_RGBA_S3TC_DXT5_EXT) { - int img_size = tex->width * tex->height * tex->bpp; - glCompressedTexImage2DARB(GL_TEXTURE_2D, 0, tex->fmt, tex->width, tex->height, 0, img_size, tex->ptr); + 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 @@ -756,16 +778,18 @@ int tex_upload(const Handle h, int filter, int int_fmt) // 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, tex->ptr); + 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, tex->ptr); + glTexImage2D(GL_TEXTURE_2D, 0, int_fmt, tex->width, tex->height, 0, tex->fmt, GL_UNSIGNED_BYTE, p); } } + mem_free(tex->hm); + return 0; } diff --git a/source/lib/tex.h b/source/lib/tex.h index 7954b79f8b..00b3bcbd30 100755 --- a/source/lib/tex.h +++ b/source/lib/tex.h @@ -29,7 +29,8 @@ struct TEX u32 height : 16; u32 fmt : 16; u32 bpp : 16; - const u8* ptr; + Handle hm; // H_MEM handle to loaded file + size_t ofs; // WRT image data in file uint id; }; diff --git a/source/lib/unzip.cpp b/source/lib/unzip.cpp deleted file mode 100755 index ce8f9fbb4e..0000000000 --- a/source/lib/unzip.cpp +++ /dev/null @@ -1,351 +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 -#include -#include - -#include - -#include "unzip.h" -#include "posix.h" -#include "misc.h" -#include "res.h" -#include "mem.h" -#include "vfs.h" - -#ifdef _MSC_VER -#pragma comment(lib, "zlib.lib") -#endif - - -static const char ecdr_id[] = "PK\5\6"; // End of Central Directory Header identifier -static const char cdfh_id[] = "PK\1\2"; // Central File Header identifier -static const char lfh_id[] = "PK\3\4"; // Local File Header identifier - - -// H_ZFILE handle -// location and size of an archived file -// no destructor -struct ZFILE -{ - u32 ofs; - u32 csize; // bit 31 = compression method (1: deflate; 0: stored) - u32 ucsize; -}; - - -// H_ZARCHIVE handle -// information about a ZIP archive -struct ZARCHIVE -{ - Handle hf; // actual ZIP file (H_VFILE) - - // file lookup - u32 num_files; - u32* fn_hashs; // split for more efficient search - ZFILE* files; - u32 last_file; // index of last file we found (speed up lookups of sequential files) -}; - - -static void zarchive_dtor(void* p) -{ - ZARCHIVE* za = (ZARCHIVE*)p; - - vfs_close(za->hf); - za->hf = 0; -} - - -// open and return a handle to the zip archive indicated by -Handle zopen(const char* const fn) -{ - const u32 fn_hash = fnv_hash(fn, strlen(fn)); - - // already loaded? - HDATA* hd; - Handle h = h_find(fn_hash, H_ZFILE, &hd); - if(h) - return h; - - // map the ZIP file - Handle hf = vfs_map(fn); - hd = h_data(hf, 0); - if(!hd) - return 0; - const size_t size = hd->size; - const u8* zfile = (const u8*)hd->p; - - // 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 = min(size, 66000); - const u8* 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: - vfs_close(hf); - 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 * (4 + sizeof(ZFILE)), MEM_HEAP, 4*KB); - if(!mem) - goto fail; - - h = h_alloc(fn_hash, H_ZFILE, zarchive_dtor, &hd); - if(!h) - goto fail; - - ZARCHIVE* const za = (ZARCHIVE*)hd->user; - za->hf = hf; - za->fn_hashs = (u32*)mem; - za->files = (ZFILE*)((u8*)mem + 4*num_files); - za->last_file = 0; - - // 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; - uint 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++; - - (int&)cdfh += 46 + fn_len + e_len + c_len; - } - - za->num_files = i; - } - - return h; -} - - -void zclose(const Handle h) -{ - if(h_data(h, H_ZFILE)) - h_free(h, H_ZFILE); - else - h_free(h, H_ZARCHIVE); -} - - -Handle zopen(Handle hz, const char* fn) -{ - HDATA* hzd = h_data(hz, H_ZFILE); - if(!hzd) - return 0; - ZARCHIVE* za = (ZARCHIVE*)hzd->user; - - // find its File descriptor - const u32 fn_hash = fnv_hash(fn, strlen(fn)); - uint 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; - } - const ZFILE* const f = &za->files[i]; - - // - HDATA* hfd; - Handle hf = h_alloc(fn_hash, H_ZFILE, 0, &hfd); - return hf; -} - -int zaccess(Handle hz, const char* fn) -{ - HDATA* hzd = h_data(hz, H_ZFILE); - if(!hzd) - return -1; - ZARCHIVE* za = (ZARCHIVE*)hzd->user; - - // find its File descriptor - const u32 fn_hash = fnv_hash(fn, strlen(fn)); - uint 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 -1; - } - za->last_file = i; - return( 0 ); -} - -int zread(Handle hf, void*& p, size_t& size, size_t ofs) -{ - HDATA* hfd = h_data(hf, 0); - if(!hfd) - return -1; - - ZFILE* zf = (ZFILE*)hfd->user; - const size_t ucsize = zf->ucsize; - const size_t csize = zf->csize; - - if(ofs > ucsize) - return -1; - -bool deflated = ucsize != csize; - - // TODO: async read for uncompressed files? - if(!deflated) - { - p = (u8*)hfd->p + zf->ofs + ofs; - size = hfd->size - ofs; - return 0; - } - - // double-buffered - u32 slots[2]; - int active_read = 0; - void* out_pos = 0; - - void* out_mem = mem_alloc(ucsize, MEM_HEAP, 64*KB); - if(!out_mem) - return -1; - - // inflating - z_stream stream; - if(deflated) - { - memset(&stream, 0, sizeof(stream)); - if(inflateInit2(&stream, -MAX_WBITS) != Z_OK) - return -1; - // -MAX_WBITS indicates no zlib header present - - stream.next_out = (u8*)out_mem; - stream.avail_out = ucsize; - } - // read directly into output buffer - else - out_pos = out_mem; - - bool first = true; - bool done = false; - - for(;;) - { - // start reading next block - if(!done) - slots[active_read] = vfs_start_read(hf, ofs, &out_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) - { - stream.next_in = (u8*)p; - stream.avail_in = bytes_read; - inflate(&stream, 0); - } - } - - first = false; - // one more iteration to process the last pending block - if(done) - break; - if(ofs >= csize) - done = true; - } - - if(deflated) - { - inflateEnd(&stream); - - if(stream.total_in != csize || stream.total_out != ucsize) - { - mem_free(out_mem); - return 0; - } - } - - p = out_mem; - size = ucsize; - - return 0; -} - - -//1 cache per zip - res layer doesnt know our internals (cache entry=ofs, csize,ucsize) -//cache=array (allow finding next file quickly; need some mechanism for faster random access?) -//sync is not a problem - zip file won't be updated during gameplay because it's still open diff --git a/source/lib/unzip.h b/source/lib/unzip.h deleted file mode 100755 index 04d543078d..0000000000 --- a/source/lib/unzip.h +++ /dev/null @@ -1,38 +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 __UNZIP_H__ -#define __UNZIP_H__ - - -#include "res.h" - -// open and return a handle to the zip archive indicated by -extern Handle zopen(const char* fn); - -// open and return a handle to file in the zip archive -extern Handle zopen(Handle hz, const char* fn); - -extern int zaccess( Handle hz, const char* fn ); - -extern void zclose(Handle hz); - -extern int zread(Handle hf, void*& p, size_t& size, size_t ofs); - - -#endif // #ifndef __UNZIP_H__ diff --git a/source/lib/vfs.cpp b/source/lib/vfs.cpp index 94e7b75e86..10334ce37a 100755 --- a/source/lib/vfs.cpp +++ b/source/lib/vfs.cpp @@ -1,3 +1,22 @@ +// 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 #include #include @@ -8,23 +27,44 @@ #include #include "posix.h" -#include "unzip.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 paths it mounted for a mod, and unmounts those -// when needed. +// the game keeps track of what path(s) it mounted for a mod, +// and unmounts those when needed. struct PATH { - char* dir; // relative to root; points to space at end of this struct - struct PATH* next; + struct PATH* next; // linked list + + char* dir; // relative to root dir; + // points to space at end of this struct + int num_archives; Handle archives[1]; + // space allocated here for archive Handles + dir string }; static PATH* path_list; @@ -34,19 +74,13 @@ static void vfile_dtor(void* p) { VFILE* vf = (VFILE*)p; - // in archive - if(vf->ha && vf->hz) - { - zclose(vf->hz); - zclose(vf->ha); - vf->ha = vf->hz = 0; - } - // normal file if(vf->fd > 0) { close(vf->fd); vf->fd = -1; } + + mem_free(vf->hm); } @@ -66,7 +100,7 @@ int vfs_set_root(const char* argv0, const char* root) return -1; *fn = 0; - chdir( fn + 1 ); + chdir(path); chdir(root); return vfs_mount("."); @@ -99,8 +133,8 @@ int vfs_mount(const char* path) // alloc search path entry (add to front) const int archives_size = num_archives*sizeof(Handle); - const int entry_size = round_up(sizeof(PATH)+archives_size+path_len+1, 8); - PATH* entry = (PATH*)mem_alloc(entry_size); + const int entry_size = round_up(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; @@ -113,7 +147,7 @@ int vfs_mount(const char* path) std::sort(archives.begin(), archives.end()); entry->num_archives = num_archives; for(int i = 0; i < num_archives; i++) - entry->archives[i] = zopen(archives[i].c_str()); + entry->archives[i] = zip_open(archives[i].c_str()); return 0; } @@ -130,7 +164,7 @@ int vfs_umount(const char* path) { // close all archives for(int i = 0; i < entry->num_archives; i++) - zclose(entry->archives[i]); + h_free(entry->archives[i], H_ZARCHIVE); // remove from list *prev = entry->next; @@ -148,107 +182,12 @@ int vfs_umount(const char* path) } -Handle vfs_map(const char* fn) +// 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) { - struct stat stat_buf; - if(stat(fn, &stat_buf) != 0) - return 0; - size_t size = stat_buf.st_size; - - int fd = open(fn, O_RDONLY); - if(fd < 0) - return 0; - - u32 fn_hash = fnv_hash(fn, strlen(fn)); - - void* p = mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0); - close(fd); - if(p != MAP_FAILED) - { - HDATA* hd; - Handle h = h_alloc(0, H_VFILE, vfile_dtor, &hd); - if(h) - { - hd->p = p; - hd->size = size; -// VFILE* vf = (VFILE*)hd->user; -// vf->fd = fd; - return h; - } - } - - return 0; -} - -int vfs_access( char* fn ) -{ - // Alters 'fn' to the path of the file, if we find it... - - // Mostly identical to vfs_stat, below. - - struct stat dy; - - // Strim out the path, if provided. - char* name = strrchr( fn, '/' ); - if( name ) { name++; } else name = fn; - char buf[PATH_MAX+1]; buf[PATH_MAX] = 0; - // for each search path: - for(PATH* entry = path_list; entry; entry = entry->next) - { - // dir - const char* path = name; - 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, name); - path = buf; - } - if( !_stat( path, &dy ) ) - { - strcpy( fn, path ); - return( 0 ); - } - - // archive - for(int i = 0; i < entry->num_archives; i++) - { - if( !zaccess(entry->archives[i], name) ) - return( 0 ); - } - - } - - // not found - return -1; -} - -int vfs_stat( const char* fn, struct stat *buffer ) -{ - // Mostly identical to vfs_open, below. - - // Note: vfs_stat redefines the nlink member of stat for its own - // nefarious ends. - - // Check the specific location 'fn' first. - // If the file's there, it's faster. - - if( !_stat( fn, buffer ) ) - { - buffer->st_nlink = 0; - return( 0 ); - } - - // Not there? Drop the old path. - - char* name = strrchr( fn, '/' ); - if( name ) - fn = name + 1; - - char buf[PATH_MAX+1]; buf[PATH_MAX] = 0; - - // for each search path: for(PATH* entry = path_list; entry; entry = entry->next) { // dir @@ -259,61 +198,150 @@ int vfs_stat( const char* fn, struct stat *buffer ) snprintf(buf, PATH_MAX, "%s/%s", entry->dir, fn); path = buf; } - if( !_stat( path, buffer ) ) - { - buffer->st_nlink = 1; - return( 0 ); - } - // Ignore files in an archive. + int err = func(path, 0, data); + if(err <= 0) + return err; + + // archive + for(int i = 0; i < entry->num_archives; i++) + { + err = func(path, entry->archives[i], data); + if(err <= 0) + return err; + } } - // not found - return -1; + 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) { - char buf[PATH_MAX+1]; buf[PATH_MAX] = 0; + u32 fn_hash = fnv_hash(fn, strlen(fn)); - // If a path to an existing file is given, use it. + VFILE* vf; + Handle hv = h_alloc(fn_hash, H_VFILE, vfile_dtor, (void**)&vf); + if(!hv) + return 0; - Handle h = vfs_map( fn ); - if( h ) - return( h ); + // already open + if(vf->size) + return hv; - char* name = strrchr( fn, '/' ); - if( name ) fn = name + 1; - - // for each search path: - for(PATH* entry = path_list; entry; entry = entry->next) + if(vfs_foreach_path(open_cb, fn, vf) < 0) { - // 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; - } - Handle h = vfs_map(path); - if(h) - return h; - - // archive - for(int i = 0; i < entry->num_archives; i++) - { - Handle h = zopen(entry->archives[i], fn); - if(h) - return h; - } + h_free(hv, H_VFILE); + return 0; } - // not found - return 0; + return hv; } + const uint IDX_BITS = 4; const uint NUM_SLOTS = 1ul << IDX_BITS; const uint TAG_BITS = 32 - IDX_BITS; @@ -326,12 +354,11 @@ slots[NUM_SLOTS]; u32 vfs_start_read(const Handle hf, size_t& ofs, void** buf) { - HDATA* hfd = h_data(hf, 0); - if(!hfd) + VFILE* vf = (VFILE*)h_user_data(hf, H_VFILE); + if(!vf) return 0; - VFILE* vf = (VFILE*)hfd->user; - ssize_t bytes_left = hfd->size - ofs; + ssize_t bytes_left = vf->size - ofs; if(bytes_left < 0) return 0; @@ -362,12 +389,12 @@ u32 vfs_start_read(const Handle hf, size_t& ofs, void** buf) // use the buffer given (e.g. read directly into output buffer) if(buf) - cb->aio_buf = buf; + cb->aio_buf = *buf; // allocate our own (reused for subsequent requests) else if(!cb->aio_buf) { - cb->aio_buf = mem_alloc(64*KB, MEM_HEAP, 64*KB); + cb->aio_buf = mem_alloc(64*KB, 64*KB, MEM_HEAP); if(!cb->aio_buf) return 0; } @@ -419,29 +446,120 @@ int vfs_finish_read(const u32 slot, void*& p, size_t& size) -int vfs_read(Handle h, void*& p, size_t& size, size_t ofs) +Handle vfs_load(const char* fn, void*& _p, size_t& _size, bool dont_map) { - p = 0; - size = 0; + _p = 0; + _size = 0; - HDATA* hd = h_data(h, H_VFILE); - if(hd) + 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) { - if(ofs+size > hd->size) - return -1; - p = (u8*)hd->p + ofs; - if(!size) - size = hd->size - ofs; + 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; } - // H_ZFILE - else - return zread(h, p, size, ofs); + + 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) { - h_free(h, 0); - return 0; -} + return h_free(h, H_VFILE); +} \ No newline at end of file diff --git a/source/lib/vfs.h b/source/lib/vfs.h index 84d6f098df..5ab74e2378 100755 --- a/source/lib/vfs.h +++ b/source/lib/vfs.h @@ -1,10 +1,26 @@ -struct VFILE -{ - int fd; +// 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/ - Handle hz; // position within archive (ZFILE) - Handle ha; // archive (ZARCHIVE) -}; +#ifndef __VFS_H__ +#define __VFS_H__ + +#include "res.h" #define VFS_MAX_PATH 40 @@ -12,17 +28,14 @@ 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 Handle vfs_map(const char* fn); // actual path in real FS +extern int vfs_stat(const char* fn, struct stat *buffer); +extern int vfs_realpath(const char* fn, char* realpath); -extern int vfs_access( char* fn ); -extern int vfs_stat( const char* fn, struct stat *buffer ); +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); - -// size: in - maximum byte count -// out - bytes actually read -extern int vfs_read(Handle h, void*& p, size_t& size, size_t ofs = 0); - 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); \ No newline at end of file +extern int vfs_finish_read(u32 slot, void*& p, size_t& size); + +#endif // #ifndef __VFS_H__ \ No newline at end of file diff --git a/source/main.cpp b/source/main.cpp index 9a1dfbb1ee..b9c2fd447c 100755 --- a/source/main.cpp +++ b/source/main.cpp @@ -14,7 +14,7 @@ #include "tex.h" #include "vfs.h" #include "ia32.h" -#include "Config.h" +#include "ps/Config.h" #ifndef NO_GUI #include "gui/GUI.h" diff --git a/source/ps/Config.cpp b/source/ps/Config.cpp index 10100a3daa..aca878364a 100755 --- a/source/ps/Config.cpp +++ b/source/ps/Config.cpp @@ -61,11 +61,10 @@ PS_RESULT CConfig::Register( CStr Filename, void* Data, LoaderFunction DynamicLo // Might as well check we can find the thing. char filepath[PATH_MAX]; - strcpy( filepath, Filename ); - if( vfs_access( filepath ) ) // This changes filepath to the disk location - // of the file, if we know it, to speed up - // checks later. + if( vfs_realpath( Filename, filepath ) ) // This changes filepath to the disk location + // of the file, if we know it, to speed up + // checks later. { if( m_LogFile ) { @@ -127,8 +126,7 @@ PS_RESULT CConfig::Update() // Find its new path and copy it back to the data we maintain // here to speed up future queries. char filepath[PATH_MAX]; - strcpy( filepath, i->Filename ); - vfs_access( filepath ); + vfs_realpath( i->Filename, filepath ); if( m_LogFile ) { CStr Report = _T( "File " ); @@ -212,8 +210,7 @@ PS_RESULT CConfig::ReloadAll() // We can't find the file. Seeing as this should reload everything, // check that it exists. char filepath[PATH_MAX]; - strcpy( filepath, i->Filename ); - if( vfs_access( filepath ) ) + if( vfs_realpath( i->Filename, filepath ) ) { // Oops. notfound++; @@ -237,8 +234,7 @@ PS_RESULT CConfig::ReloadAll() // Find its new path and copy it back to the data we maintain // here to speed up future queries. char filepath[PATH_MAX]; - strcpy( filepath, i->Filename ); - vfs_access( filepath ); + vfs_realpath( i->Filename, filepath ); if( m_LogFile ) { CStr Report = _T( "File " ); diff --git a/source/ps/Config.h b/source/ps/Config.h index 676b4c5ca6..cbeef2ff43 100755 --- a/source/ps/Config.h +++ b/source/ps/Config.h @@ -52,9 +52,9 @@ TDD at http://forums.wildfiregames.com/0ad #include "Singleton.h" #include "CStr.h" #include "LogFile.h" -#include "..\lib\posix.h" -#include "..\lib\unzip.h" -#include "..\lib\misc.h" +#include "posix.h" +#include "zip.h" +#include "misc.h" #include diff --git a/source/terrain/Terrain.cpp b/source/terrain/Terrain.cpp index ccd4927943..66bfe65c8c 100755 --- a/source/terrain/Terrain.cpp +++ b/source/terrain/Terrain.cpp @@ -16,6 +16,7 @@ #include "Terrain.H" #include "tex.h" +#include "mem.h" bool g_HillShading = true; @@ -46,7 +47,9 @@ TEX tex; Handle h = tex_load(filename, &tex); if(!h) return false; -const u8* data = tex.ptr; +Handle hm = tex.hm; +MEM* mem = (MEM*)h_user_data(hm, H_MEM); +const u8* data = (const u8*)mem->p; m_pVertices = new STerrainVertex[MAP_SIZE*MAP_SIZE]; if (m_pVertices == NULL) diff --git a/source/vc6/ps.dsp b/source/vc6/ps.dsp index fbcf4b0173..6f234b34d8 100755 --- a/source/vc6/ps.dsp +++ b/source/vc6/ps.dsp @@ -299,14 +299,6 @@ SOURCE=..\lib\types.h # End Source File # Begin Source File -SOURCE=..\lib\unzip.cpp -# End Source File -# Begin Source File - -SOURCE=..\lib\unzip.h -# End Source File -# Begin Source File - SOURCE=..\lib\vfs.cpp # End Source File # Begin Source File @@ -325,6 +317,14 @@ SOURCE=..\lib\wsdl.cpp SOURCE=..\lib\wsdl.h # End Source File +# Begin Source File + +SOURCE=..\lib\zip.cpp +# End Source File +# Begin Source File + +SOURCE=..\lib\zip.h +# End Source File # End Group # Begin Group "ps" @@ -335,6 +335,14 @@ SOURCE=..\ps\BaseEntity.h # End Source File # Begin Source File +SOURCE=..\ps\Config.cpp +# End Source File +# Begin Source File + +SOURCE=..\ps\Config.h +# End Source File +# Begin Source File + SOURCE=..\ps\CStr.cpp # End Source File # Begin Source File diff --git a/source/vc7/note.txt b/source/vc7/note.txt index 8b7d583fe6..dad0a6c6ec 100755 --- a/source/vc7/note.txt +++ b/source/vc7/note.txt @@ -8,4 +8,14 @@ Known problems: Ultimately the xml file reading's root dir should be binaries/ too. -/Gee \ No newline at end of file +/Gee + +Known solution :)) +- I put the GUI init code before VFS init (which changes the dir + as you mention to binaries/data), so that it'd work as you had it. + When you're ready, just move all data to binaries/data, move + the GUI init after vfs_set_root in main.cpp, and we're go. + +p.s. we only need .vsproj and .sln in CVS; .ncb is built by VC. + +janwas \ No newline at end of file diff --git a/source/vc7/ps.vcproj b/source/vc7/ps.vcproj index 58357022b0..15f143119a 100755 --- a/source/vc7/ps.vcproj +++ b/source/vc7/ps.vcproj @@ -225,6 +225,10 @@ Filter=""> + + - - + + diff --git a/source/vc7/vc7.ncb b/source/vc7/vc7.ncb deleted file mode 100755 index 9057ebacbf..0000000000 --- a/source/vc7/vc7.ncb +++ /dev/null @@ -1 +0,0 @@ -Microsoft C/C++ MSF 7.00