forked from 0ad/0ad
- massive overhaul of lib error code returning. int -> LibError everywhere.
- add translators from errno and GetLastError to LibError - clarified return values of callbacks (they must return INFO_CB_CONTINUE to continue) - this exposed a few bugs in error handling chains (returning incorrect values); also reduced say-nothing instances of return -1. - move CHECK_ERR etc. macros to lib_error This was SVN commit r3229.
This commit is contained in:
parent
78cc73481c
commit
1c1200a049
@ -42,7 +42,7 @@ static size_t round_up_to_page(size_t size)
|
|||||||
// stored in da->prot to reduce size; doesn't conflict with any PROT_* flags.
|
// stored in da->prot to reduce size; doesn't conflict with any PROT_* flags.
|
||||||
const int DA_NOT_OUR_MEM = 0x40000000;
|
const int DA_NOT_OUR_MEM = 0x40000000;
|
||||||
|
|
||||||
static int validate_da(DynArray* da)
|
static LibError validate_da(DynArray* da)
|
||||||
{
|
{
|
||||||
if(!da)
|
if(!da)
|
||||||
return ERR_INVALID_PARAM;
|
return ERR_INVALID_PARAM;
|
||||||
@ -53,19 +53,19 @@ static int validate_da(DynArray* da)
|
|||||||
const int prot = da->prot;
|
const int prot = da->prot;
|
||||||
|
|
||||||
if(debug_is_pointer_bogus(base))
|
if(debug_is_pointer_bogus(base))
|
||||||
return -1;
|
return ERR_1;
|
||||||
if(!is_page_multiple((uintptr_t)base))
|
if(!is_page_multiple((uintptr_t)base))
|
||||||
return -2;
|
return ERR_2;
|
||||||
if(!is_page_multiple(max_size_pa))
|
if(!is_page_multiple(max_size_pa))
|
||||||
return -3;
|
return ERR_3;
|
||||||
if(cur_size > max_size_pa)
|
if(cur_size > max_size_pa)
|
||||||
return -4;
|
return ERR_4;
|
||||||
if(pos > cur_size || pos > max_size_pa)
|
if(pos > cur_size || pos > max_size_pa)
|
||||||
return -5;
|
return ERR_5;
|
||||||
if(prot & ~(PROT_READ|PROT_WRITE|PROT_EXEC|DA_NOT_OUR_MEM))
|
if(prot & ~(PROT_READ|PROT_WRITE|PROT_EXEC|DA_NOT_OUR_MEM))
|
||||||
return -6;
|
return ERR_6;
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CHECK_DA(da) CHECK_ERR(validate_da(da))
|
#define CHECK_DA(da) CHECK_ERR(validate_da(da))
|
||||||
@ -75,42 +75,52 @@ static int validate_da(DynArray* da)
|
|||||||
// very thin wrapper on top of sys/mman.h that makes the intent more obvious
|
// very thin wrapper on top of sys/mman.h that makes the intent more obvious
|
||||||
// (its commit/decommit semantics are difficult to tell apart).
|
// (its commit/decommit semantics are difficult to tell apart).
|
||||||
|
|
||||||
|
static inline LibError LibError_from_mmap(void* ret)
|
||||||
|
{
|
||||||
|
if(ret != MAP_FAILED)
|
||||||
|
return ERR_OK;
|
||||||
|
return LibError_from_errno();
|
||||||
|
}
|
||||||
|
|
||||||
static const int mmap_flags = MAP_PRIVATE|MAP_ANONYMOUS;
|
static const int mmap_flags = MAP_PRIVATE|MAP_ANONYMOUS;
|
||||||
|
|
||||||
static int mem_reserve(size_t size, u8** pp)
|
static LibError mem_reserve(size_t size, u8** pp)
|
||||||
{
|
{
|
||||||
|
errno = 0;
|
||||||
void* ret = mmap(0, size, PROT_NONE, mmap_flags|MAP_NORESERVE, -1, 0);
|
void* ret = mmap(0, size, PROT_NONE, mmap_flags|MAP_NORESERVE, -1, 0);
|
||||||
if(ret == MAP_FAILED)
|
|
||||||
return ERR_NO_MEM;
|
|
||||||
*pp = (u8*)ret;
|
*pp = (u8*)ret;
|
||||||
return 0;
|
return LibError_from_mmap(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mem_release(u8* p, size_t size)
|
static LibError mem_release(u8* p, size_t size)
|
||||||
{
|
{
|
||||||
return munmap(p, size);
|
errno = 0;
|
||||||
|
return LibError_from_posix(munmap(p, size));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mem_commit(u8* p, size_t size, int prot)
|
static LibError mem_commit(u8* p, size_t size, int prot)
|
||||||
{
|
{
|
||||||
if(prot == PROT_NONE)
|
if(prot == PROT_NONE)
|
||||||
{
|
{
|
||||||
debug_warn("mem_commit: prot=PROT_NONE isn't allowed (misinterpreted by mmap)");
|
debug_warn("mem_commit: prot=PROT_NONE isn't allowed (misinterpreted by mmap)");
|
||||||
return ERR_INVALID_PARAM;
|
return ERR_INVALID_PARAM;
|
||||||
}
|
}
|
||||||
|
errno = 0;
|
||||||
void* ret = mmap(p, size, prot, mmap_flags|MAP_FIXED, -1, 0);
|
void* ret = mmap(p, size, prot, mmap_flags|MAP_FIXED, -1, 0);
|
||||||
return (ret == MAP_FAILED)? -1 : 0;
|
return LibError_from_mmap(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mem_decommit(u8* p, size_t size)
|
static LibError mem_decommit(u8* p, size_t size)
|
||||||
{
|
{
|
||||||
|
errno = 0;
|
||||||
void* ret = mmap(p, size, PROT_NONE, mmap_flags|MAP_NORESERVE|MAP_FIXED, -1, 0);
|
void* ret = mmap(p, size, PROT_NONE, mmap_flags|MAP_NORESERVE|MAP_FIXED, -1, 0);
|
||||||
return (ret == MAP_FAILED)? -1 : 0;
|
return LibError_from_mmap(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mem_protect(u8* p, size_t size, int prot)
|
static LibError mem_protect(u8* p, size_t size, int prot)
|
||||||
{
|
{
|
||||||
return mprotect(p, size, prot);
|
errno = 0;
|
||||||
|
return LibError_from_posix(mprotect(p, size, prot));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -121,7 +131,7 @@ static int mem_protect(u8* p, size_t size, int prot)
|
|||||||
// (rounded up to the next page size multiple) of address space for the
|
// (rounded up to the next page size multiple) of address space for the
|
||||||
// array; it can never grow beyond this.
|
// array; it can never grow beyond this.
|
||||||
// no virtual memory is actually committed until calls to da_set_size.
|
// no virtual memory is actually committed until calls to da_set_size.
|
||||||
int da_alloc(DynArray* da, size_t max_size)
|
LibError da_alloc(DynArray* da, size_t max_size)
|
||||||
{
|
{
|
||||||
const size_t max_size_pa = round_up_to_page(max_size);
|
const size_t max_size_pa = round_up_to_page(max_size);
|
||||||
|
|
||||||
@ -134,7 +144,7 @@ int da_alloc(DynArray* da, size_t max_size)
|
|||||||
da->prot = PROT_READ|PROT_WRITE;
|
da->prot = PROT_READ|PROT_WRITE;
|
||||||
da->pos = 0;
|
da->pos = 0;
|
||||||
CHECK_DA(da);
|
CHECK_DA(da);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -142,7 +152,7 @@ int da_alloc(DynArray* da, size_t max_size)
|
|||||||
// DynArray object, preparing it for use with da_read or da_append.
|
// DynArray object, preparing it for use with da_read or da_append.
|
||||||
// da_free should be called when the DynArray is no longer needed,
|
// da_free should be called when the DynArray is no longer needed,
|
||||||
// even though it doesn't free this memory (but does zero the DynArray).
|
// even though it doesn't free this memory (but does zero the DynArray).
|
||||||
int da_wrap_fixed(DynArray* da, u8* p, size_t size)
|
LibError da_wrap_fixed(DynArray* da, u8* p, size_t size)
|
||||||
{
|
{
|
||||||
da->base = p;
|
da->base = p;
|
||||||
da->max_size_pa = round_up_to_page(size);
|
da->max_size_pa = round_up_to_page(size);
|
||||||
@ -150,14 +160,14 @@ int da_wrap_fixed(DynArray* da, u8* p, size_t size)
|
|||||||
da->prot = PROT_READ|PROT_WRITE|DA_NOT_OUR_MEM;
|
da->prot = PROT_READ|PROT_WRITE|DA_NOT_OUR_MEM;
|
||||||
da->pos = 0;
|
da->pos = 0;
|
||||||
CHECK_DA(da);
|
CHECK_DA(da);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// free all memory (address space + physical) that constitutes the
|
// free all memory (address space + physical) that constitutes the
|
||||||
// given array. use-after-free is impossible because the memory is
|
// given array. use-after-free is impossible because the memory is
|
||||||
// marked not-present via MMU. also zeroes the contents of <da>.
|
// marked not-present via MMU. also zeroes the contents of <da>.
|
||||||
int da_free(DynArray* da)
|
LibError da_free(DynArray* da)
|
||||||
{
|
{
|
||||||
CHECK_DA(da);
|
CHECK_DA(da);
|
||||||
|
|
||||||
@ -174,21 +184,21 @@ int da_free(DynArray* da)
|
|||||||
// da_free is supposed to be called even in the above case.
|
// da_free is supposed to be called even in the above case.
|
||||||
if(!was_wrapped)
|
if(!was_wrapped)
|
||||||
CHECK_ERR(mem_release(p, size));
|
CHECK_ERR(mem_release(p, size));
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// expand or shrink the array: changes the amount of currently committed
|
// expand or shrink the array: changes the amount of currently committed
|
||||||
// (i.e. usable) memory pages. pages are added/removed until
|
// (i.e. usable) memory pages. pages are added/removed until
|
||||||
// new_size (rounded up to the next page size multiple) is met.
|
// new_size (rounded up to the next page size multiple) is met.
|
||||||
int da_set_size(DynArray* da, size_t new_size)
|
LibError da_set_size(DynArray* da, size_t new_size)
|
||||||
{
|
{
|
||||||
CHECK_DA(da);
|
CHECK_DA(da);
|
||||||
|
|
||||||
if(da->prot & DA_NOT_OUR_MEM)
|
if(da->prot & DA_NOT_OUR_MEM)
|
||||||
{
|
{
|
||||||
debug_warn("da is marked DA_NOT_OUR_MEM, must not be altered");
|
debug_warn("da is marked DA_NOT_OUR_MEM, must not be altered");
|
||||||
return -1;
|
return ERR_LOGIC;
|
||||||
}
|
}
|
||||||
|
|
||||||
// determine how much to add/remove
|
// determine how much to add/remove
|
||||||
@ -210,7 +220,7 @@ int da_set_size(DynArray* da, size_t new_size)
|
|||||||
|
|
||||||
da->cur_size = new_size;
|
da->cur_size = new_size;
|
||||||
CHECK_DA(da);
|
CHECK_DA(da);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -218,7 +228,7 @@ int da_set_size(DynArray* da, size_t new_size)
|
|||||||
// write-protection. affects the currently committed pages as well as
|
// write-protection. affects the currently committed pages as well as
|
||||||
// all subsequently added pages.
|
// all subsequently added pages.
|
||||||
// prot can be a combination of the PROT_* values used with mprotect.
|
// prot can be a combination of the PROT_* values used with mprotect.
|
||||||
int da_set_prot(DynArray* da, int prot)
|
LibError da_set_prot(DynArray* da, int prot)
|
||||||
{
|
{
|
||||||
CHECK_DA(da);
|
CHECK_DA(da);
|
||||||
|
|
||||||
@ -227,39 +237,39 @@ int da_set_prot(DynArray* da, int prot)
|
|||||||
if(da->prot & DA_NOT_OUR_MEM)
|
if(da->prot & DA_NOT_OUR_MEM)
|
||||||
{
|
{
|
||||||
debug_warn("da is marked DA_NOT_OUR_MEM, must not be altered");
|
debug_warn("da is marked DA_NOT_OUR_MEM, must not be altered");
|
||||||
return -1;
|
return ERR_LOGIC;
|
||||||
}
|
}
|
||||||
|
|
||||||
da->prot = prot;
|
da->prot = prot;
|
||||||
CHECK_ERR(mem_protect(da->base, da->cur_size, prot));
|
CHECK_ERR(mem_protect(da->base, da->cur_size, prot));
|
||||||
|
|
||||||
CHECK_DA(da);
|
CHECK_DA(da);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// "read" from array, i.e. copy into the given buffer.
|
// "read" from array, i.e. copy into the given buffer.
|
||||||
// starts at offset DynArray.pos and advances this.
|
// starts at offset DynArray.pos and advances this.
|
||||||
int da_read(DynArray* da, void* data, size_t size)
|
LibError da_read(DynArray* da, void* data, size_t size)
|
||||||
{
|
{
|
||||||
// make sure we have enough data to read
|
// make sure we have enough data to read
|
||||||
if(da->pos+size > da->cur_size)
|
if(da->pos+size > da->cur_size)
|
||||||
return -1;
|
return ERR_EOF;
|
||||||
|
|
||||||
memcpy2(data, da->base+da->pos, size);
|
memcpy2(data, da->base+da->pos, size);
|
||||||
da->pos += size;
|
da->pos += size;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// "write" to array, i.e. copy from the given buffer.
|
// "write" to array, i.e. copy from the given buffer.
|
||||||
// starts at offset DynArray.pos and advances this.
|
// starts at offset DynArray.pos and advances this.
|
||||||
int da_append(DynArray* da, const void* data, size_t size)
|
LibError da_append(DynArray* da, const void* data, size_t size)
|
||||||
{
|
{
|
||||||
RETURN_ERR(da_set_size(da, da->pos+size));
|
RETURN_ERR(da_set_size(da, da->pos+size));
|
||||||
memcpy2(da->base+da->pos, data, size);
|
memcpy2(da->base+da->pos, data, size);
|
||||||
da->pos += size;
|
da->pos += size;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -304,7 +314,7 @@ static const size_t POOL_CHUNK = 4*KiB;
|
|||||||
//
|
//
|
||||||
// note: el_size must at least be enough for a pointer (due to freelist
|
// note: el_size must at least be enough for a pointer (due to freelist
|
||||||
// implementation) but not exceed the expand-by amount.
|
// implementation) but not exceed the expand-by amount.
|
||||||
int pool_create(Pool* p, size_t max_size, size_t el_size)
|
LibError pool_create(Pool* p, size_t max_size, size_t el_size)
|
||||||
{
|
{
|
||||||
if(el_size < sizeof(void*) || el_size > POOL_CHUNK)
|
if(el_size < sizeof(void*) || el_size > POOL_CHUNK)
|
||||||
CHECK_ERR(ERR_INVALID_PARAM);
|
CHECK_ERR(ERR_INVALID_PARAM);
|
||||||
@ -312,14 +322,14 @@ int pool_create(Pool* p, size_t max_size, size_t el_size)
|
|||||||
RETURN_ERR(da_alloc(&p->da, max_size));
|
RETURN_ERR(da_alloc(&p->da, max_size));
|
||||||
p->pos = 0;
|
p->pos = 0;
|
||||||
p->el_size = el_size;
|
p->el_size = el_size;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// free all memory that ensued from <p>. all elements are made unusable
|
// free all memory that ensued from <p>. all elements are made unusable
|
||||||
// (it doesn't matter if they were "allocated" or in freelist or unused);
|
// (it doesn't matter if they were "allocated" or in freelist or unused);
|
||||||
// future alloc and free calls on this pool will fail.
|
// future alloc and free calls on this pool will fail.
|
||||||
int pool_destroy(Pool* p)
|
LibError pool_destroy(Pool* p)
|
||||||
{
|
{
|
||||||
// don't be picky and complain if the freelist isn't empty;
|
// don't be picky and complain if the freelist isn't empty;
|
||||||
// we don't care since it's all part of the da anyway.
|
// we don't care since it's all part of the da anyway.
|
||||||
@ -342,7 +352,8 @@ bool pool_contains(Pool* p, void* el)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// return an entry from the pool, or 0 if it cannot be expanded as necessary.
|
// return an entry from the pool, or 0 if it would have to be expanded and
|
||||||
|
// there isn't enough memory to do so.
|
||||||
// exhausts the freelist before returning new entries to improve locality.
|
// exhausts the freelist before returning new entries to improve locality.
|
||||||
void* pool_alloc(Pool* p)
|
void* pool_alloc(Pool* p)
|
||||||
{
|
{
|
||||||
|
@ -45,37 +45,37 @@ struct DynArray
|
|||||||
// (rounded up to the next page size multiple) of address space for the
|
// (rounded up to the next page size multiple) of address space for the
|
||||||
// array; it can never grow beyond this.
|
// array; it can never grow beyond this.
|
||||||
// no virtual memory is actually committed until calls to da_set_size.
|
// no virtual memory is actually committed until calls to da_set_size.
|
||||||
extern int da_alloc(DynArray* da, size_t max_size);
|
extern LibError da_alloc(DynArray* da, size_t max_size);
|
||||||
|
|
||||||
// free all memory (address space + physical) that constitutes the
|
// free all memory (address space + physical) that constitutes the
|
||||||
// given array. use-after-free is impossible because the memory is
|
// given array. use-after-free is impossible because the memory is
|
||||||
// marked not-present via MMU. also zeroes the contents of <da>.
|
// marked not-present via MMU. also zeroes the contents of <da>.
|
||||||
extern int da_free(DynArray* da);
|
extern LibError da_free(DynArray* da);
|
||||||
|
|
||||||
// expand or shrink the array: changes the amount of currently committed
|
// expand or shrink the array: changes the amount of currently committed
|
||||||
// (i.e. usable) memory pages. pages are added/removed until
|
// (i.e. usable) memory pages. pages are added/removed until
|
||||||
// new_size (rounded up to the next page size multiple) is met.
|
// new_size (rounded up to the next page size multiple) is met.
|
||||||
extern int da_set_size(DynArray* da, size_t new_size);
|
extern LibError da_set_size(DynArray* da, size_t new_size);
|
||||||
|
|
||||||
// change access rights of the array memory; used to implement
|
// change access rights of the array memory; used to implement
|
||||||
// write-protection. affects the currently committed pages as well as
|
// write-protection. affects the currently committed pages as well as
|
||||||
// all subsequently added pages.
|
// all subsequently added pages.
|
||||||
// prot can be a combination of the PROT_* values used with mprotect.
|
// prot can be a combination of the PROT_* values used with mprotect.
|
||||||
extern int da_set_prot(DynArray* da, int prot);
|
extern LibError da_set_prot(DynArray* da, int prot);
|
||||||
|
|
||||||
// "wrap" (i.e. store information about) the given buffer in a
|
// "wrap" (i.e. store information about) the given buffer in a
|
||||||
// DynArray object, preparing it for use with da_read or da_append.
|
// DynArray object, preparing it for use with da_read or da_append.
|
||||||
// da_free should be called when the DynArray is no longer needed,
|
// da_free should be called when the DynArray is no longer needed,
|
||||||
// even though it doesn't free this memory (but does zero the DynArray).
|
// even though it doesn't free this memory (but does zero the DynArray).
|
||||||
extern int da_wrap_fixed(DynArray* da, u8* p, size_t size);
|
extern LibError da_wrap_fixed(DynArray* da, u8* p, size_t size);
|
||||||
|
|
||||||
// "read" from array, i.e. copy into the given buffer.
|
// "read" from array, i.e. copy into the given buffer.
|
||||||
// starts at offset DynArray.pos and advances this.
|
// starts at offset DynArray.pos and advances this.
|
||||||
extern int da_read(DynArray* da, void* data_dst, size_t size);
|
extern LibError da_read(DynArray* da, void* data_dst, size_t size);
|
||||||
|
|
||||||
// "write" to array, i.e. copy from the given buffer.
|
// "write" to array, i.e. copy from the given buffer.
|
||||||
// starts at offset DynArray.pos and advances this.
|
// starts at offset DynArray.pos and advances this.
|
||||||
extern int da_append(DynArray* da, const void* data_src, size_t size);
|
extern LibError da_append(DynArray* da, const void* data_src, size_t size);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -108,18 +108,19 @@ struct Pool
|
|||||||
//
|
//
|
||||||
// note: el_size must at least be enough for a pointer (due to freelist
|
// note: el_size must at least be enough for a pointer (due to freelist
|
||||||
// implementation) but not exceed the expand-by amount.
|
// implementation) but not exceed the expand-by amount.
|
||||||
extern int pool_create(Pool* p, size_t max_size, size_t el_size);
|
extern LibError pool_create(Pool* p, size_t max_size, size_t el_size);
|
||||||
|
|
||||||
// free all memory that ensued from <p>. all elements are made unusable
|
// free all memory that ensued from <p>. all elements are made unusable
|
||||||
// (it doesn't matter if they were "allocated" or in freelist or unused);
|
// (it doesn't matter if they were "allocated" or in freelist or unused);
|
||||||
// future alloc and free calls on this pool will fail.
|
// future alloc and free calls on this pool will fail.
|
||||||
extern int pool_destroy(Pool* p);
|
extern LibError pool_destroy(Pool* p);
|
||||||
|
|
||||||
// indicate whether <el> was allocated from the given pool.
|
// indicate whether <el> was allocated from the given pool.
|
||||||
// this is useful for callers that use several types of allocators.
|
// this is useful for callers that use several types of allocators.
|
||||||
extern bool pool_contains(Pool* p, void* el);
|
extern bool pool_contains(Pool* p, void* el);
|
||||||
|
|
||||||
// return an entry from the pool, or 0 if it cannot be expanded as necessary.
|
// return an entry from the pool, or 0 if it would have to be expanded and
|
||||||
|
// there isn't enough memory to do so.
|
||||||
// exhausts the freelist before returning new entries to improve locality.
|
// exhausts the freelist before returning new entries to improve locality.
|
||||||
extern void* pool_alloc(Pool* p);
|
extern void* pool_alloc(Pool* p);
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@ FUNC(void, log, (const wchar_t* text), (text), (void))
|
|||||||
// holds a function pointer for each hook. passed to set_app_hooks.
|
// holds a function pointer for each hook. passed to set_app_hooks.
|
||||||
struct AppHooks
|
struct AppHooks
|
||||||
{
|
{
|
||||||
#define FUNC(ret, name, params, param_names, call_prefix) ret(*name) params;
|
#define FUNC(ret, name, params, param_names, call_prefix) ret (*name) params;
|
||||||
#include "app_hooks.h"
|
#include "app_hooks.h"
|
||||||
#undef FUNC
|
#undef FUNC
|
||||||
|
|
||||||
|
@ -207,13 +207,13 @@ void debug_wprintf(const wchar_t* fmt, ...)
|
|||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
int debug_write_crashlog(const wchar_t* text)
|
LibError debug_write_crashlog(const wchar_t* text)
|
||||||
{
|
{
|
||||||
FILE* f = fopen("crashlog.txt", "w");
|
FILE* f = fopen("crashlog.txt", "w");
|
||||||
if(!f)
|
if(!f)
|
||||||
{
|
{
|
||||||
DISPLAY_ERROR(L"debug_write_crashlog: unable to open file");
|
DISPLAY_ERROR(L"debug_write_crashlog: unable to open file");
|
||||||
return -1;
|
return ERR_FILE_ACCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
fputwc(0xfeff, f); // BOM
|
fputwc(0xfeff, f); // BOM
|
||||||
@ -226,7 +226,7 @@ int debug_write_crashlog(const wchar_t* text)
|
|||||||
fwprintf(f, L"Last known activity:\n\n %ls\n", debug_log);
|
fwprintf(f, L"Last known activity:\n\n %ls\n", debug_log);
|
||||||
|
|
||||||
fclose(f);
|
fclose(f);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -232,7 +232,7 @@ extern void debug_filter_clear();
|
|||||||
extern void debug_wprintf_mem(const wchar_t* fmt, ...);
|
extern void debug_wprintf_mem(const wchar_t* fmt, ...);
|
||||||
|
|
||||||
// write all logs and <text> out to crashlog.txt (unicode format).
|
// write all logs and <text> out to crashlog.txt (unicode format).
|
||||||
extern int debug_write_crashlog(const wchar_t* text);
|
extern LibError debug_write_crashlog(const wchar_t* text);
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@ -270,11 +270,11 @@ enum DbgBreakType
|
|||||||
// from addr's alignment, and is typically 1 machine word.
|
// from addr's alignment, and is typically 1 machine word.
|
||||||
// breakpoints are a limited resource (4 on IA-32); if none are
|
// breakpoints are a limited resource (4 on IA-32); if none are
|
||||||
// available, we return ERR_LIMIT.
|
// available, we return ERR_LIMIT.
|
||||||
extern int debug_set_break(void* addr, DbgBreakType type);
|
extern LibError debug_set_break(void* addr, DbgBreakType type);
|
||||||
|
|
||||||
// remove all breakpoints that were set by debug_set_break.
|
// remove all breakpoints that were set by debug_set_break.
|
||||||
// important, since these are a limited resource.
|
// important, since these are a limited resource.
|
||||||
extern int debug_remove_all_breaks();
|
extern LibError debug_remove_all_breaks();
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@ -293,7 +293,7 @@ const size_t DBG_FILE_LEN = 100;
|
|||||||
// sym_name and file must hold at least the number of chars above;
|
// sym_name and file must hold at least the number of chars above;
|
||||||
// file is the base name only, not path (see rationale in wdbg_sym).
|
// file is the base name only, not path (see rationale in wdbg_sym).
|
||||||
// the PDB implementation is rather slow (~500us).
|
// the PDB implementation is rather slow (~500us).
|
||||||
extern int debug_resolve_symbol(void* ptr_of_interest, char* sym_name, char* file, int* line);
|
extern LibError debug_resolve_symbol(void* ptr_of_interest, char* sym_name, char* file, int* line);
|
||||||
|
|
||||||
// write a complete stack trace (including values of local variables) into
|
// write a complete stack trace (including values of local variables) into
|
||||||
// the specified buffer. if <context> is nonzero, it is assumed to be a
|
// the specified buffer. if <context> is nonzero, it is assumed to be a
|
||||||
|
@ -636,14 +636,14 @@ template<class T> bool get_container_info(T* t, size_t size, size_t el_size,
|
|||||||
// return number of elements and an iterator (any data it needs is stored in
|
// return number of elements and an iterator (any data it needs is stored in
|
||||||
// it_mem, which must hold DEBUG_STL_MAX_ITERATOR_SIZE bytes).
|
// it_mem, which must hold DEBUG_STL_MAX_ITERATOR_SIZE bytes).
|
||||||
// returns 0 on success or an StlContainerError.
|
// returns 0 on success or an StlContainerError.
|
||||||
int stl_get_container_info(const char* type_name, const u8* p, size_t size,
|
LibError stl_get_container_info(const char* type_name, const u8* p, size_t size,
|
||||||
size_t el_size, size_t* el_count, DebugIterator* el_iterator, void* it_mem)
|
size_t el_size, size_t* el_count, DebugIterator* el_iterator, void* it_mem)
|
||||||
{
|
{
|
||||||
// HACK: The debug_stl code breaks VS2005's STL badly, causing crashes in
|
// HACK: The debug_stl code breaks VS2005's STL badly, causing crashes in
|
||||||
// later pieces of code that try to manipulate the STL containers. Presumably
|
// later pieces of code that try to manipulate the STL containers. Presumably
|
||||||
// it needs to be altered/rewritten to work happily with the new STL debug iterators.
|
// it needs to be altered/rewritten to work happily with the new STL debug iterators.
|
||||||
#if MSC_VERSION >= 1400
|
#if MSC_VERSION >= 1400
|
||||||
return -1;
|
return ERR_FAIL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool handled = false, valid = false;
|
bool handled = false, valid = false;
|
||||||
@ -690,10 +690,10 @@ int stl_get_container_info(const char* type_name, const u8* p, size_t size,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
if(!handled)
|
if(!handled)
|
||||||
return STL_CNT_UNKNOWN;
|
return ERR_STL_CNT_UNKNOWN;
|
||||||
if(!valid)
|
if(!valid)
|
||||||
return STL_CNT_INVALID;
|
return ERR_STL_CNT_INVALID;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -28,22 +28,12 @@ extern char* stl_simplify_name(char* name);
|
|||||||
// no STL iterator is larger than this; see below.
|
// no STL iterator is larger than this; see below.
|
||||||
const size_t DEBUG_STL_MAX_ITERATOR_SIZE = 64;
|
const size_t DEBUG_STL_MAX_ITERATOR_SIZE = 64;
|
||||||
|
|
||||||
enum StlContainerError
|
|
||||||
{
|
|
||||||
// type_name is not that of a known STL container.
|
|
||||||
STL_CNT_UNKNOWN = -100,
|
|
||||||
|
|
||||||
// the container is of a known type but its contents are invalid.
|
|
||||||
// likely causes: not yet initialized or memory corruption.
|
|
||||||
STL_CNT_INVALID = -101
|
|
||||||
};
|
|
||||||
|
|
||||||
// if <wtype_name> indicates the object <p, size> to be an STL container,
|
// if <wtype_name> indicates the object <p, size> to be an STL container,
|
||||||
// and given the size of its value_type (retrieved via debug information),
|
// and given the size of its value_type (retrieved via debug information),
|
||||||
// return number of elements and an iterator (any data it needs is stored in
|
// return number of elements and an iterator (any data it needs is stored in
|
||||||
// it_mem, which must hold DEBUG_STL_MAX_ITERATOR_SIZE bytes).
|
// it_mem, which must hold DEBUG_STL_MAX_ITERATOR_SIZE bytes).
|
||||||
// returns 0 on success or an StlContainerError.
|
// returns 0 on success or an StlContainerError.
|
||||||
extern int stl_get_container_info(const char* type_name, const u8* p, size_t size,
|
extern LibError stl_get_container_info(const char* type_name, const u8* p, size_t size,
|
||||||
size_t el_size, size_t* el_count, DebugIterator* el_iterator, void* it_mem);
|
size_t el_size, size_t* el_count, DebugIterator* el_iterator, void* it_mem);
|
||||||
|
|
||||||
#endif // #ifndef DEBUG_STL_H_INCLUDED
|
#endif // #ifndef DEBUG_STL_H_INCLUDED
|
||||||
|
@ -33,14 +33,14 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if OS_WIN
|
#if OS_WIN
|
||||||
extern int win_get_gfx_info();
|
extern LibError win_get_gfx_info();
|
||||||
extern int win_get_cpu_info();
|
extern LibError win_get_cpu_info();
|
||||||
extern int win_get_snd_info();
|
extern LibError win_get_snd_info();
|
||||||
#elif OS_UNIX
|
#elif OS_UNIX
|
||||||
extern int unix_get_cpu_info();
|
extern LibError unix_get_cpu_info();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern "C" int ogl_get_gfx_info();
|
extern "C" LibError ogl_get_gfx_info();
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -94,7 +94,7 @@ void in_stop()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int in_record(const char* fn)
|
LibError in_record(const char* fn)
|
||||||
{
|
{
|
||||||
if(state == INIT)
|
if(state == INIT)
|
||||||
atexit(in_stop);
|
atexit(in_stop);
|
||||||
@ -103,17 +103,17 @@ int in_record(const char* fn)
|
|||||||
|
|
||||||
f = fopen(fn, "wb");
|
f = fopen(fn, "wb");
|
||||||
if(!f)
|
if(!f)
|
||||||
return -1;
|
return ERR_FILE_ACCESS;
|
||||||
|
|
||||||
fwrite(&game_ticks, sizeof(u32), 1, f);
|
fwrite(&game_ticks, sizeof(u32), 1, f);
|
||||||
|
|
||||||
state = RECORD;
|
state = RECORD;
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int in_playback(const char* fn)
|
LibError in_playback(const char* fn)
|
||||||
{
|
{
|
||||||
if(state == INIT)
|
if(state == INIT)
|
||||||
atexit(in_stop);
|
atexit(in_stop);
|
||||||
@ -122,7 +122,7 @@ int in_playback(const char* fn)
|
|||||||
|
|
||||||
f = fopen(fn, "rb");
|
f = fopen(fn, "rb");
|
||||||
if(!f)
|
if(!f)
|
||||||
return -1;
|
return ERR_FILE_NOT_FOUND;
|
||||||
|
|
||||||
u32 rec_start_time;
|
u32 rec_start_time;
|
||||||
fread(&rec_start_time, sizeof(u32), 1, f);
|
fread(&rec_start_time, sizeof(u32), 1, f);
|
||||||
@ -133,7 +133,7 @@ int in_playback(const char* fn)
|
|||||||
|
|
||||||
state = PLAYBACK;
|
state = PLAYBACK;
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -51,8 +51,8 @@ extern void in_dispatch_event(const SDL_Event* event);
|
|||||||
|
|
||||||
extern void in_dispatch_recorded_events();
|
extern void in_dispatch_recorded_events();
|
||||||
|
|
||||||
extern int in_record(const char* fn);
|
extern LibError in_record(const char* fn);
|
||||||
extern int in_playback(const char* fn);
|
extern LibError in_playback(const char* fn);
|
||||||
extern void in_stop(void);
|
extern void in_stop(void);
|
||||||
|
|
||||||
|
|
||||||
|
109
source/lib/lib.h
109
source/lib/lib.h
@ -59,8 +59,6 @@ scope
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "lib/types.h"
|
#include "lib/types.h"
|
||||||
|
|
||||||
#include "lib/lib_errors.h"
|
|
||||||
|
|
||||||
#include "sysdep/sysdep.h"
|
#include "sysdep/sysdep.h"
|
||||||
#include "sysdep/cpu.h" // CAS
|
#include "sysdep/cpu.h" // CAS
|
||||||
|
|
||||||
@ -81,6 +79,9 @@ scope
|
|||||||
// squelch the warning (unfortunately non-portable).
|
// squelch the warning (unfortunately non-portable).
|
||||||
#define STMT(STMT_code__) do { STMT_code__; } while(false)
|
#define STMT(STMT_code__) do { STMT_code__; } while(false)
|
||||||
|
|
||||||
|
// must come after definition of STMT
|
||||||
|
#include "lib/lib_errors.h"
|
||||||
|
|
||||||
// execute the code passed as a parameter only the first time this is
|
// execute the code passed as a parameter only the first time this is
|
||||||
// reached.
|
// reached.
|
||||||
// may be called at any time (in particular before main), but is not
|
// may be called at any time (in particular before main), but is not
|
||||||
@ -109,110 +110,6 @@ STMT(\
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// be careful here. the given expression (e.g. variable or
|
|
||||||
// function return value) may be a Handle (=i64), so it needs to be
|
|
||||||
// stored and compared as such. (very large but legitimate Handle values
|
|
||||||
// casted to int can end up negative)
|
|
||||||
// all functions using this return int (instead of i64) for efficiency and
|
|
||||||
// simplicity. if the input was negative, it is an error code and is
|
|
||||||
// therefore known to fit; we still mask with UINT_MAX to avoid
|
|
||||||
// VC cast-to-smaller-type warnings.
|
|
||||||
|
|
||||||
// if expression evaluates to a negative i64, warn user and return the number.
|
|
||||||
#if OS_WIN
|
|
||||||
#define CHECK_ERR(expression)\
|
|
||||||
STMT(\
|
|
||||||
i64 err__ = (i64)(expression);\
|
|
||||||
if(err__ < 0)\
|
|
||||||
{\
|
|
||||||
DEBUG_WARN_ERR(err__);\
|
|
||||||
return (int)(err__ & UINT_MAX);\
|
|
||||||
}\
|
|
||||||
)
|
|
||||||
#else
|
|
||||||
#define CHECK_ERR(expression)\
|
|
||||||
STMT(\
|
|
||||||
i64 err__ = (i64)(expression);\
|
|
||||||
if(err__ < 0)\
|
|
||||||
{\
|
|
||||||
DEBUG_WARN_ERR(err__);\
|
|
||||||
return (int)(err__ & UINT_MAX);\
|
|
||||||
}\
|
|
||||||
)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// just pass on errors without any kind of annoying warning
|
|
||||||
// (useful for functions that can legitimately fail, e.g. vfs_exists).
|
|
||||||
#define RETURN_ERR(expression)\
|
|
||||||
STMT(\
|
|
||||||
i64 err__ = (i64)(expression);\
|
|
||||||
if(err__ < 0)\
|
|
||||||
return (int)(err__ & UINT_MAX);\
|
|
||||||
)
|
|
||||||
|
|
||||||
// if expression evaluates to a negative i64, warn user and throw the number.
|
|
||||||
#define THROW_ERR(expression)\
|
|
||||||
STMT(\
|
|
||||||
i64 err__ = (i64)(expression);\
|
|
||||||
if(err__ < 0)\
|
|
||||||
{\
|
|
||||||
DEBUG_WARN_ERR(err__);\
|
|
||||||
throw (int)(err__ & UINT_MAX);\
|
|
||||||
}\
|
|
||||||
)
|
|
||||||
|
|
||||||
// if expression evaluates to a negative i64, warn user and just return
|
|
||||||
// (useful for void functions that must bail and complain)
|
|
||||||
#define WARN_ERR_RETURN(expression)\
|
|
||||||
STMT(\
|
|
||||||
i64 err__ = (i64)(expression);\
|
|
||||||
if(err__ < 0)\
|
|
||||||
{\
|
|
||||||
DEBUG_WARN_ERR(err__);\
|
|
||||||
return;\
|
|
||||||
}\
|
|
||||||
)
|
|
||||||
|
|
||||||
// if expression evaluates to a negative i64, warn user
|
|
||||||
// (this is similar to debug_assert but also works in release mode)
|
|
||||||
#define WARN_ERR(expression)\
|
|
||||||
STMT(\
|
|
||||||
i64 err__ = (i64)(expression);\
|
|
||||||
if(err__ < 0)\
|
|
||||||
DEBUG_WARN_ERR(err__);\
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// if ok evaluates to false or FALSE, warn user and return -1.
|
|
||||||
#define WARN_RETURN_IF_FALSE(ok)\
|
|
||||||
STMT(\
|
|
||||||
if(!(ok))\
|
|
||||||
{\
|
|
||||||
debug_warn("FYI: WARN_RETURN_IF_FALSE reports that a function failed."\
|
|
||||||
"feel free to ignore or suppress this warning.");\
|
|
||||||
return -1;\
|
|
||||||
}\
|
|
||||||
)
|
|
||||||
|
|
||||||
// if ok evaluates to false or FALSE, return -1.
|
|
||||||
#define RETURN_IF_FALSE(ok)\
|
|
||||||
STMT(\
|
|
||||||
if(!(ok))\
|
|
||||||
return -1;\
|
|
||||||
)
|
|
||||||
|
|
||||||
// if ok evaluates to false or FALSE, warn user.
|
|
||||||
#define WARN_IF_FALSE(ok)\
|
|
||||||
STMT(\
|
|
||||||
if(!(ok))\
|
|
||||||
debug_warn("FYI: WARN_IF_FALSE reports that a function failed."\
|
|
||||||
"feel free to ignore or suppress this warning.");\
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// useful because VC6 may return 0 on failure, instead of throwing.
|
// useful because VC6 may return 0 on failure, instead of throwing.
|
||||||
// this wraps the exception handling, and creates a NULL pointer on failure.
|
// this wraps the exception handling, and creates a NULL pointer on failure.
|
||||||
#define SAFE_NEW(type, ptr)\
|
#define SAFE_NEW(type, ptr)\
|
||||||
|
@ -74,3 +74,45 @@ void error_description_r(int err, char* buf, size_t max_chars)
|
|||||||
if(!have_output)
|
if(!have_output)
|
||||||
snprintf(buf, max_chars, "Unknown error (%d, 0x%X)", err, err);
|
snprintf(buf, max_chars, "Unknown error (%d, 0x%X)", err, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// return the LibError equivalent of errno, or ERR_FAIL if
|
||||||
|
// there's no equal.
|
||||||
|
// only call after a POSIX function indicates failure.
|
||||||
|
LibError LibError_from_errno()
|
||||||
|
{
|
||||||
|
switch(errno)
|
||||||
|
{
|
||||||
|
case ENOMEM:
|
||||||
|
return ERR_NO_MEM;
|
||||||
|
|
||||||
|
case EINVAL:
|
||||||
|
return ERR_INVALID_PARAM;
|
||||||
|
case ENOSYS:
|
||||||
|
return ERR_NOT_IMPLEMENTED;
|
||||||
|
|
||||||
|
case ENOENT:
|
||||||
|
return ERR_PATH_NOT_FOUND;
|
||||||
|
case EACCES:
|
||||||
|
return ERR_FILE_ACCESS;
|
||||||
|
case EIO:
|
||||||
|
return ERR_IO;
|
||||||
|
case ENAMETOOLONG:
|
||||||
|
return ERR_PATH_LENGTH;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return ERR_FAIL;
|
||||||
|
}
|
||||||
|
UNREACHABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// translate the return value of any POSIX function into LibError.
|
||||||
|
// ret is typically to -1 to indicate error and 0 on success.
|
||||||
|
// you should set errno to 0 before calling the POSIX function to
|
||||||
|
// make sure we do not return any stale errors.
|
||||||
|
LibError LibError_from_posix(int ret)
|
||||||
|
{
|
||||||
|
debug_assert(ret == 0 || ret == -1);
|
||||||
|
return (ret == 0)? ERR_OK : LibError_from_errno();
|
||||||
|
}
|
||||||
|
@ -32,6 +32,127 @@ enum LibError {
|
|||||||
// "Unknown error (65536, 0x10000)".
|
// "Unknown error (65536, 0x10000)".
|
||||||
extern void error_description_r(int err, char* buf, size_t max_chars);
|
extern void error_description_r(int err, char* buf, size_t max_chars);
|
||||||
|
|
||||||
|
|
||||||
|
// return the LibError equivalent of errno, or ERR_FAIL if there's no equal.
|
||||||
|
// only call after a POSIX function indicates failure.
|
||||||
|
extern LibError LibError_from_errno();
|
||||||
|
|
||||||
|
// translate the return value of any POSIX function into LibError.
|
||||||
|
// ret is typically to -1 to indicate error and 0 on success.
|
||||||
|
// you should set errno to 0 before calling the POSIX function to
|
||||||
|
// make sure we do not return any stale errors.
|
||||||
|
extern LibError LibError_from_posix(int ret);
|
||||||
|
|
||||||
|
|
||||||
|
// be careful here. the given expression (e.g. variable or
|
||||||
|
// function return value) may be a Handle (=i64), so it needs to be
|
||||||
|
// stored and compared as such. (very large but legitimate Handle values
|
||||||
|
// casted to int can end up negative)
|
||||||
|
// all functions using this return int (instead of i64) for efficiency and
|
||||||
|
// simplicity. if the input was negative, it is an error code and is
|
||||||
|
// therefore known to fit; we still mask with UINT_MAX to avoid
|
||||||
|
// VC cast-to-smaller-type warnings.
|
||||||
|
|
||||||
|
// if expression evaluates to a negative i64, warn user and return the number.
|
||||||
|
#if OS_WIN
|
||||||
|
#define CHECK_ERR(expression)\
|
||||||
|
STMT(\
|
||||||
|
i64 err__ = (i64)(expression);\
|
||||||
|
if(err__ < 0)\
|
||||||
|
{\
|
||||||
|
DEBUG_WARN_ERR(err__);\
|
||||||
|
return (LibError)(err__ & UINT_MAX);\
|
||||||
|
}\
|
||||||
|
)
|
||||||
|
#else
|
||||||
|
#define CHECK_ERR(expression)\
|
||||||
|
STMT(\
|
||||||
|
i64 err__ = (i64)(expression);\
|
||||||
|
if(err__ < 0)\
|
||||||
|
{\
|
||||||
|
DEBUG_WARN_ERR(err__);\
|
||||||
|
return (LibError)(err__ & UINT_MAX);\
|
||||||
|
}\
|
||||||
|
)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// just pass on errors without any kind of annoying warning
|
||||||
|
// (useful for functions that can legitimately fail, e.g. vfs_exists).
|
||||||
|
#define RETURN_ERR(expression)\
|
||||||
|
STMT(\
|
||||||
|
i64 err__ = (i64)(expression);\
|
||||||
|
if(err__ < 0)\
|
||||||
|
return (LibError)(err__ & UINT_MAX);\
|
||||||
|
)
|
||||||
|
|
||||||
|
// return an error and warn about it (replaces debug_warn+return)
|
||||||
|
#define WARN_RETURN(err)\
|
||||||
|
STMT(\
|
||||||
|
DEBUG_WARN_ERR(err);\
|
||||||
|
return err;\
|
||||||
|
)
|
||||||
|
|
||||||
|
// if expression evaluates to a negative i64, warn user and throw the number.
|
||||||
|
#define THROW_ERR(expression)\
|
||||||
|
STMT(\
|
||||||
|
i64 err__ = (i64)(expression);\
|
||||||
|
if(err__ < 0)\
|
||||||
|
{\
|
||||||
|
DEBUG_WARN_ERR(err__);\
|
||||||
|
throw (LibError)(err__ & UINT_MAX);\
|
||||||
|
}\
|
||||||
|
)
|
||||||
|
|
||||||
|
// if expression evaluates to a negative i64, warn user and just return
|
||||||
|
// (useful for void functions that must bail and complain)
|
||||||
|
#define WARN_ERR_RETURN(expression)\
|
||||||
|
STMT(\
|
||||||
|
i64 err__ = (i64)(expression);\
|
||||||
|
if(err__ < 0)\
|
||||||
|
{\
|
||||||
|
DEBUG_WARN_ERR(err__);\
|
||||||
|
return;\
|
||||||
|
}\
|
||||||
|
)
|
||||||
|
|
||||||
|
// if expression evaluates to a negative i64, warn user
|
||||||
|
// (this is similar to debug_assert but also works in release mode)
|
||||||
|
#define WARN_ERR(expression)\
|
||||||
|
STMT(\
|
||||||
|
i64 err__ = (i64)(expression);\
|
||||||
|
if(err__ < 0)\
|
||||||
|
DEBUG_WARN_ERR(err__);\
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// if ok evaluates to false or FALSE, warn user and return -1.
|
||||||
|
#define WARN_RETURN_IF_FALSE(ok)\
|
||||||
|
STMT(\
|
||||||
|
if(!(ok))\
|
||||||
|
{\
|
||||||
|
debug_warn("FYI: WARN_RETURN_IF_FALSE reports that a function failed."\
|
||||||
|
"feel free to ignore or suppress this warning.");\
|
||||||
|
return -1;\
|
||||||
|
}\
|
||||||
|
)
|
||||||
|
|
||||||
|
// if ok evaluates to false or FALSE, return -1.
|
||||||
|
#define RETURN_IF_FALSE(ok)\
|
||||||
|
STMT(\
|
||||||
|
if(!(ok))\
|
||||||
|
return -1;\
|
||||||
|
)
|
||||||
|
|
||||||
|
// if ok evaluates to false or FALSE, warn user.
|
||||||
|
#define WARN_IF_FALSE(ok)\
|
||||||
|
STMT(\
|
||||||
|
if(!(ok))\
|
||||||
|
debug_warn("FYI: WARN_IF_FALSE reports that a function failed."\
|
||||||
|
"feel free to ignore or suppress this warning.");\
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
#endif // #ifndef ERRORS_H__
|
#endif // #ifndef ERRORS_H__
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@ -42,31 +163,85 @@ extern void error_description_r(int err, char* buf, size_t max_chars);
|
|||||||
// error code is usually negative; positive denotes warnings.
|
// error code is usually negative; positive denotes warnings.
|
||||||
// its absolute value must be within [ERR_MIN, ERR_MAX).
|
// its absolute value must be within [ERR_MIN, ERR_MAX).
|
||||||
|
|
||||||
|
// ERR_OK doesn't really need a string, but must be part of enum LibError
|
||||||
|
// due to compiler checks. (and calling error_description_r(0) should
|
||||||
|
// never happen, but we set the text accordingly..)
|
||||||
|
ERR(0, ERR_OK, "(but return value was 0 which indicates success)")
|
||||||
|
ERR(-1, ERR_FAIL, "Function failed (no details available)")
|
||||||
|
|
||||||
|
ERR(1, INFO_CB_CONTINUE , "1 (not an error)")
|
||||||
|
// these are all basically the same thing
|
||||||
|
ERR(2, INFO_CANNOT_HANDLE, "2 (not an error)")
|
||||||
|
ERR(3, INFO_NO_REPLACE , "3 (not an error)")
|
||||||
|
ERR(4, INFO_SKIPPED , "4 (not an error)")
|
||||||
|
|
||||||
|
ERR(-100000, ERR_LOGIC, "Logic error in code")
|
||||||
|
ERR(-100060, ERR_TIMED_OUT, "Timed out")
|
||||||
|
|
||||||
|
// these are for cases where we just want a distinct value to display and
|
||||||
|
// a symbolic name + string would be overkill (e.g. the various
|
||||||
|
// test cases in a validate() call). they are shared between multiple
|
||||||
|
// functions; when something fails, the stack trace will show in which
|
||||||
|
// one it was => these errors are unambiguous.
|
||||||
|
// there are 3 tiers - 1..9 are used in most functions, 11..19 are
|
||||||
|
// used in a function that calls another validator and 21..29 are
|
||||||
|
// for for functions that call 2 other validators (this avoids
|
||||||
|
// ambiguity as to which error actually happened where)
|
||||||
|
ERR(-100101, ERR_1, "Case 1")
|
||||||
|
ERR(-100102, ERR_2, "Case 2")
|
||||||
|
ERR(-100103, ERR_3, "Case 3")
|
||||||
|
ERR(-100104, ERR_4, "Case 4")
|
||||||
|
ERR(-100105, ERR_5, "Case 5")
|
||||||
|
ERR(-100106, ERR_6, "Case 6")
|
||||||
|
ERR(-100107, ERR_7, "Case 7")
|
||||||
|
ERR(-100108, ERR_8, "Case 8")
|
||||||
|
ERR(-100109, ERR_9, "Case 9")
|
||||||
|
ERR(-100111, ERR_11, "Case 11")
|
||||||
|
ERR(-100112, ERR_12, "Case 12")
|
||||||
|
ERR(-100113, ERR_13, "Case 13")
|
||||||
|
ERR(-100114, ERR_14, "Case 14")
|
||||||
|
ERR(-100115, ERR_15, "Case 15")
|
||||||
|
ERR(-100116, ERR_16, "Case 16")
|
||||||
|
ERR(-100117, ERR_17, "Case 17")
|
||||||
|
ERR(-100118, ERR_18, "Case 18")
|
||||||
|
ERR(-100119, ERR_19, "Case 19")
|
||||||
|
ERR(-100121, ERR_21, "Case 21")
|
||||||
|
ERR(-100122, ERR_22, "Case 22")
|
||||||
|
ERR(-100123, ERR_23, "Case 23")
|
||||||
|
ERR(-100124, ERR_24, "Case 24")
|
||||||
|
ERR(-100125, ERR_25, "Case 25")
|
||||||
|
ERR(-100126, ERR_26, "Case 26")
|
||||||
|
ERR(-100127, ERR_27, "Case 27")
|
||||||
|
ERR(-100128, ERR_28, "Case 28")
|
||||||
|
ERR(-100129, ERR_29, "Case 29")
|
||||||
|
|
||||||
// function arguments
|
// function arguments
|
||||||
ERR(-100000, ERR_INVALID_PARAM, "Invalid function argument")
|
ERR(-100220, ERR_INVALID_PARAM, "Invalid function argument")
|
||||||
ERR(-100001, ERR_INVALID_HANDLE, "Invalid Handle (argument)")
|
ERR(-100221, ERR_INVALID_HANDLE, "Invalid Handle (argument)")
|
||||||
ERR(-100002, ERR_BUF_SIZE, "Buffer argument too small")
|
ERR(-100222, ERR_BUF_SIZE, "Buffer argument too small")
|
||||||
|
|
||||||
// system limitations
|
// system limitations
|
||||||
ERR(-100020, ERR_NO_MEM, "Not enough memory")
|
ERR(-100240, ERR_NO_MEM, "Not enough memory")
|
||||||
ERR(-100021, ERR_AGAIN, "Try again later")
|
ERR(-100241, ERR_AGAIN, "Try again later")
|
||||||
ERR(-100022, ERR_LIMIT, "Fixed limit exceeded")
|
ERR(-100242, ERR_LIMIT, "Fixed limit exceeded")
|
||||||
ERR(-100023, ERR_NO_SYS, "OS doesn't provide a required API")
|
ERR(-100243, ERR_NO_SYS, "OS doesn't provide a required API")
|
||||||
ERR(-100024, ERR_NOT_IMPLEMENTED, "Feature currently not implemented")
|
ERR(-100244, ERR_NOT_IMPLEMENTED, "Feature currently not implemented")
|
||||||
ERR(-100025, ERR_NOT_SUPPORTED, "Feature isn't and won't be supported")
|
ERR(-100245, ERR_NOT_SUPPORTED, "Feature isn't and won't be supported")
|
||||||
|
|
||||||
ERR(-1060, ERR_TIMED_OUT, "Timed out")
|
|
||||||
|
|
||||||
// file + vfs
|
// file + vfs
|
||||||
ERR(-100200, ERR_FILE_NOT_FOUND, "VFile not found")
|
ERR(-100300, ERR_FILE_NOT_FOUND, "VFile not found")
|
||||||
ERR(-100201, ERR_PATH_NOT_FOUND, "VDir not found")
|
ERR(-100301, ERR_PATH_NOT_FOUND, "VDir not found")
|
||||||
ERR(-100202, ERR_PATH_LENGTH, "Path exceeds VFS_MAX_PATH characters")
|
ERR(-100302, ERR_PATH_LENGTH, "Path exceeds VFS_MAX_PATH characters")
|
||||||
ERR(-100203, ERR_PATH_INVALID, "Path is invalid")
|
ERR(-100303, ERR_PATH_INVALID, "Path is invalid")
|
||||||
ERR(-100210, ERR_DIR_END, "End of directory reached (no more files)")
|
ERR(-100310, ERR_DIR_END, "End of directory reached (no more files)")
|
||||||
ERR(-100220, ERR_NOT_FILE, "Not a file")
|
ERR(-100320, ERR_NOT_FILE, "Not a file")
|
||||||
ERR(-100230, ERR_FILE_ACCESS, "Insufficient access rights to open file")
|
ERR(-100321, ERR_NOT_DIR, "Not a directory")
|
||||||
ERR(-100231, ERR_IO, "Error during IO")
|
ERR(-100330, ERR_FILE_ACCESS, "Insufficient access rights to open file")
|
||||||
ERR(-100232, ERR_EOF, "Reading beyond end of file")
|
ERR(-100331, ERR_IO, "Error during IO")
|
||||||
|
ERR(-100332, ERR_EOF, "Reading beyond end of file")
|
||||||
|
ERR(-100333, ERR_IS_COMPRESSED, "Invalid operation for a compressed file")
|
||||||
|
ERR(-100334, ERR_ALREADY_MOUNTED, "Directory (tree) already mounted")
|
||||||
|
ERR(-100335, ERR_INVALID_MOUNT_TYPE, "Invalid mount type (memory corruption?)")
|
||||||
|
|
||||||
// file format
|
// file format
|
||||||
ERR(-100400, ERR_UNKNOWN_FORMAT, "Unknown file format")
|
ERR(-100400, ERR_UNKNOWN_FORMAT, "Unknown file format")
|
||||||
@ -81,7 +256,9 @@ ERR(-100503, ERR_TEX_INVALID_LAYOUT, "Unsupported texel layout, e.g. right-to-le
|
|||||||
ERR(-100504, ERR_TEX_COMPRESSED, "Unsupported texture compression")
|
ERR(-100504, ERR_TEX_COMPRESSED, "Unsupported texture compression")
|
||||||
ERR(+100505, WARN_TEX_INVALID_DATA, "Warning: invalid texel data encountered")
|
ERR(+100505, WARN_TEX_INVALID_DATA, "Warning: invalid texel data encountered")
|
||||||
ERR(-100506, ERR_TEX_INVALID_SIZE, "Texture size is incorrect")
|
ERR(-100506, ERR_TEX_INVALID_SIZE, "Texture size is incorrect")
|
||||||
|
ERR(-100507, ERR_TEX_CODEC_CANNOT_HANDLE, "Texture codec cannot handle the given format")
|
||||||
|
|
||||||
|
// CPU
|
||||||
ERR(-100600, ERR_CPU_FEATURE_MISSING, "This CPU doesn't support a required feature")
|
ERR(-100600, ERR_CPU_FEATURE_MISSING, "This CPU doesn't support a required feature")
|
||||||
|
|
||||||
// shaders
|
// shaders
|
||||||
@ -91,5 +268,28 @@ ERR(-100702, ERR_SHDR_NO_SHADER, "Invalid shader reference")
|
|||||||
ERR(-100703, ERR_SHDR_LINK, "Shader linking failed")
|
ERR(-100703, ERR_SHDR_LINK, "Shader linking failed")
|
||||||
ERR(-100704, ERR_SHDR_NO_PROGRAM, "Invalid shader program reference")
|
ERR(-100704, ERR_SHDR_NO_PROGRAM, "Invalid shader program reference")
|
||||||
|
|
||||||
|
// debug symbol engine
|
||||||
|
ERR(-100800, ERR_SYM_NO_STACK_FRAMES_FOUND, "No stack frames found")
|
||||||
|
ERR(-100801, ERR_SYM_UNRETRIEVABLE_STATIC, "Value unretrievable (stored in external module)")
|
||||||
|
ERR(-100802, ERR_SYM_UNRETRIEVABLE_REG, "Value unretrievable (stored in register)")
|
||||||
|
ERR(-100803, ERR_SYM_TYPE_INFO_UNAVAILABLE, "Error getting type_info")
|
||||||
|
// .. this limit is to prevent infinite recursion.
|
||||||
|
ERR(-100804, ERR_SYM_NESTING_LIMIT, "Symbol nesting too deep or infinite recursion")
|
||||||
|
// .. this limit is to prevent large symbols (e.g. arrays or linked lists) from
|
||||||
|
// hogging all stack trace buffer space.
|
||||||
|
ERR(-100805, ERR_SYM_SINGLE_SYMBOL_LIMIT, "Symbol has produced too much output")
|
||||||
|
ERR(-100806, ERR_SYM_INTERNAL_ERROR, "Exception raised while processing a symbol")
|
||||||
|
ERR(-100807, ERR_SYM_UNSUPPORTED, "Symbol type not (fully) supported")
|
||||||
|
// .. one of the dump_sym* functions decided not to output anything at
|
||||||
|
// all (e.g. for member functions in UDTs - we don't want those).
|
||||||
|
// therefore, skip any post-symbol formatting (e.g. ",") as well.
|
||||||
|
ERR(-100808, ERR_SYM_SUPPRESS_OUTPUT, "Symbol was suppressed")
|
||||||
|
ERR(-100809, ERR_SYM_CHILD_NOT_FOUND, "Symbol does not have the given child")
|
||||||
|
|
||||||
|
// STL debug
|
||||||
|
ERR(-100900, ERR_STL_CNT_UNKNOWN, "Unknown STL container type_name")
|
||||||
|
// .. likely causes: not yet initialized or memory corruption.
|
||||||
|
ERR(-100901, ERR_STL_CNT_INVALID, "Container type is known but contents are invalid")
|
||||||
|
|
||||||
#undef ERR
|
#undef ERR
|
||||||
#endif // #ifdef ERR
|
#endif // #ifdef ERR
|
||||||
|
@ -430,7 +430,7 @@ static inline Node* without_mark(Node* p)
|
|||||||
|
|
||||||
// make ready a previously unused(!) list object. if a negative error
|
// make ready a previously unused(!) list object. if a negative error
|
||||||
// code (currently only ERR_NO_MEM) is returned, the list can't be used.
|
// code (currently only ERR_NO_MEM) is returned, the list can't be used.
|
||||||
int lfl_init(LFList* list)
|
LibError lfl_init(LFList* list)
|
||||||
{
|
{
|
||||||
// make sure a TLS slot has been allocated for this thread.
|
// make sure a TLS slot has been allocated for this thread.
|
||||||
// if not (out of memory), the list object must not be used -
|
// if not (out of memory), the list object must not be used -
|
||||||
@ -447,7 +447,7 @@ int lfl_init(LFList* list)
|
|||||||
|
|
||||||
list->head = 0;
|
list->head = 0;
|
||||||
atomic_add(&active_data_structures, 1);
|
atomic_add(&active_data_structures, 1);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -611,7 +611,7 @@ have_node:
|
|||||||
|
|
||||||
|
|
||||||
// remove from list; return -1 if not found, or 0 on success.
|
// remove from list; return -1 if not found, or 0 on success.
|
||||||
int lfl_erase(LFList* list, uintptr_t key)
|
LibError lfl_erase(LFList* list, uintptr_t key)
|
||||||
{
|
{
|
||||||
TLS* tls = tls_get();
|
TLS* tls = tls_get();
|
||||||
debug_assert(tls != (void*)-1);
|
debug_assert(tls != (void*)-1);
|
||||||
@ -623,7 +623,7 @@ int lfl_erase(LFList* list, uintptr_t key)
|
|||||||
retry:
|
retry:
|
||||||
// not found in list - abort.
|
// not found in list - abort.
|
||||||
if(!list_lookup(list, key, pos))
|
if(!list_lookup(list, key, pos))
|
||||||
return -1;
|
return ERR_FAIL;
|
||||||
// mark as removed (avoids subsequent linking to it). failure implies
|
// mark as removed (avoids subsequent linking to it). failure implies
|
||||||
// at least of the following happened after list_lookup; we try again.
|
// at least of the following happened after list_lookup; we try again.
|
||||||
// - next was removed
|
// - next was removed
|
||||||
@ -639,7 +639,7 @@ retry:
|
|||||||
// call list_lookup to ensure # non-released nodes < # threads.
|
// call list_lookup to ensure # non-released nodes < # threads.
|
||||||
else
|
else
|
||||||
list_lookup(list, key, pos);
|
list_lookup(list, key, pos);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -669,7 +669,7 @@ static LFList* chain(LFHash* hash, uintptr_t key)
|
|||||||
// make ready a previously unused(!) hash object. table size will be
|
// make ready a previously unused(!) hash object. table size will be
|
||||||
// <num_entries>; this cannot currently be expanded. if a negative error
|
// <num_entries>; this cannot currently be expanded. if a negative error
|
||||||
// code (currently only ERR_NO_MEM) is returned, the hash can't be used.
|
// code (currently only ERR_NO_MEM) is returned, the hash can't be used.
|
||||||
int lfh_init(LFHash* hash, size_t num_entries)
|
LibError lfh_init(LFHash* hash, size_t num_entries)
|
||||||
{
|
{
|
||||||
hash->tbl = 0;
|
hash->tbl = 0;
|
||||||
hash->mask = ~0u;
|
hash->mask = ~0u;
|
||||||
@ -697,7 +697,7 @@ int lfh_init(LFHash* hash, size_t num_entries)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -734,7 +734,7 @@ void* lfh_insert(LFHash* hash, uintptr_t key, size_t additional_bytes, int* was_
|
|||||||
|
|
||||||
|
|
||||||
// remove from hash; return -1 if not found, or 0 on success.
|
// remove from hash; return -1 if not found, or 0 on success.
|
||||||
int lfh_erase(LFHash* hash, uintptr_t key)
|
LibError lfh_erase(LFHash* hash, uintptr_t key)
|
||||||
{
|
{
|
||||||
return lfl_erase(chain(hash,key), key);
|
return lfl_erase(chain(hash,key), key);
|
||||||
}
|
}
|
||||||
@ -893,10 +893,10 @@ static void* thread_func(void* arg)
|
|||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = lfl_erase(&list, key);
|
err = lfl_erase(&list, key);
|
||||||
TEST(was_in_set == (err == 0));
|
TEST(was_in_set == (err == ERR_OK));
|
||||||
|
|
||||||
err = lfh_erase(&hash, key);
|
err = lfh_erase(&hash, key);
|
||||||
TEST(was_in_set == (err == 0));
|
TEST(was_in_set == (err == ERR_OK));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@ struct LFList
|
|||||||
|
|
||||||
// make ready a previously unused(!) list object. if a negative error
|
// make ready a previously unused(!) list object. if a negative error
|
||||||
// code (currently only ERR_NO_MEM) is returned, the list can't be used.
|
// code (currently only ERR_NO_MEM) is returned, the list can't be used.
|
||||||
extern int lfl_init(LFList* list);
|
extern LibError lfl_init(LFList* list);
|
||||||
|
|
||||||
// call when list is no longer needed; should no longer hold any references.
|
// call when list is no longer needed; should no longer hold any references.
|
||||||
extern void lfl_free(LFList* list);
|
extern void lfl_free(LFList* list);
|
||||||
@ -125,7 +125,7 @@ extern void* lfl_find(LFList* list, void* key);
|
|||||||
extern void* lfl_insert(LFList* list, void* key, size_t additional_bytes, int* was_inserted);
|
extern void* lfl_insert(LFList* list, void* key, size_t additional_bytes, int* was_inserted);
|
||||||
|
|
||||||
// remove from list; return -1 if not found, or 0 on success.
|
// remove from list; return -1 if not found, or 0 on success.
|
||||||
extern int lfl_erase(LFList* list, void* key);
|
extern LibError lfl_erase(LFList* list, void* key);
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -141,7 +141,7 @@ struct LFHash
|
|||||||
// make ready a previously unused(!) hash object. table size will be
|
// make ready a previously unused(!) hash object. table size will be
|
||||||
// <num_entries>; this cannot currently be expanded. if a negative error
|
// <num_entries>; this cannot currently be expanded. if a negative error
|
||||||
// code (currently only ERR_NO_MEM) is returned, the hash can't be used.
|
// code (currently only ERR_NO_MEM) is returned, the hash can't be used.
|
||||||
extern int lfh_init(LFHash* hash, size_t num_entries);
|
extern LibError lfh_init(LFHash* hash, size_t num_entries);
|
||||||
|
|
||||||
// call when hash is no longer needed; should no longer hold any references.
|
// call when hash is no longer needed; should no longer hold any references.
|
||||||
extern void lfh_free(LFHash* hash);
|
extern void lfh_free(LFHash* hash);
|
||||||
@ -156,7 +156,7 @@ extern void* lfh_find(LFHash* hash, uintptr_t key);
|
|||||||
extern void* lfh_insert(LFHash* hash, uintptr_t key, size_t additional_bytes, int* was_inserted);
|
extern void* lfh_insert(LFHash* hash, uintptr_t key, size_t additional_bytes, int* was_inserted);
|
||||||
|
|
||||||
// remove from hash; return -1 if not found, or 0 on success.
|
// remove from hash; return -1 if not found, or 0 on success.
|
||||||
extern int lfh_erase(LFHash* hash, uintptr_t key);
|
extern LibError lfh_erase(LFHash* hash, uintptr_t key);
|
||||||
|
|
||||||
|
|
||||||
#endif // #ifndef LOCKFREE_H__
|
#endif // #ifndef LOCKFREE_H__
|
||||||
|
@ -350,7 +350,7 @@ static Alloc* allocs_find(const void* user_p)
|
|||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void allocs_foreach(void(*cb)(const Alloc*, void*), void* arg)
|
static void allocs_foreach(void (*cb)(const Alloc*, void*), void* arg)
|
||||||
{
|
{
|
||||||
for(uint i = 0; i < hash_entries; i++)
|
for(uint i = 0; i < hash_entries; i++)
|
||||||
{
|
{
|
||||||
|
@ -332,7 +332,7 @@ int ogl_max_tex_units = -1; // limit on GL_TEXTUREn
|
|||||||
|
|
||||||
|
|
||||||
// gfx_card and gfx_drv_ver are unchanged on failure.
|
// gfx_card and gfx_drv_ver are unchanged on failure.
|
||||||
int ogl_get_gfx_info()
|
LibError ogl_get_gfx_info()
|
||||||
{
|
{
|
||||||
const char* vendor = (const char*)glGetString(GL_VENDOR);
|
const char* vendor = (const char*)glGetString(GL_VENDOR);
|
||||||
const char* renderer = (const char*)glGetString(GL_RENDERER);
|
const char* renderer = (const char*)glGetString(GL_RENDERER);
|
||||||
@ -340,14 +340,14 @@ int ogl_get_gfx_info()
|
|||||||
// can fail if OpenGL not yet initialized,
|
// can fail if OpenGL not yet initialized,
|
||||||
// or if called between glBegin and glEnd.
|
// or if called between glBegin and glEnd.
|
||||||
if(!vendor || !renderer || !version)
|
if(!vendor || !renderer || !version)
|
||||||
return -1;
|
return ERR_AGAIN;
|
||||||
|
|
||||||
snprintf(gfx_card, ARRAY_SIZE(gfx_card), "%s %s", vendor, renderer);
|
snprintf(gfx_card, ARRAY_SIZE(gfx_card), "%s %s", vendor, renderer);
|
||||||
|
|
||||||
// add "OpenGL" to differentiate this from the real driver version
|
// add "OpenGL" to differentiate this from the real driver version
|
||||||
// (returned by platform-specific detect routines).
|
// (returned by platform-specific detect routines).
|
||||||
snprintf(gfx_drv_ver, ARRAY_SIZE(gfx_drv_ver), "OpenGL %s", version);
|
snprintf(gfx_drv_ver, ARRAY_SIZE(gfx_drv_ver), "OpenGL %s", version);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -102,7 +102,7 @@ extern int ogl_max_tex_units; // limit on GL_TEXTUREn
|
|||||||
//
|
//
|
||||||
// fails if OpenGL not ready for use.
|
// fails if OpenGL not ready for use.
|
||||||
// gfx_card and gfx_drv_ver are unchanged on failure.
|
// gfx_card and gfx_drv_ver are unchanged on failure.
|
||||||
extern int ogl_get_gfx_info(void);
|
extern LibError ogl_get_gfx_info(void);
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -70,7 +70,7 @@ const size_t SECTOR_SIZE = 4096;
|
|||||||
|
|
||||||
// write the given directory path into our buffer and set end/chars_left
|
// write the given directory path into our buffer and set end/chars_left
|
||||||
// accordingly. <dir> need and should not end with a directory separator.
|
// accordingly. <dir> need and should not end with a directory separator.
|
||||||
int pp_set_dir(PathPackage* pp, const char* dir)
|
LibError pp_set_dir(PathPackage* pp, const char* dir)
|
||||||
{
|
{
|
||||||
// note: use / instead of DIR_SEP because pp->path is portable.
|
// note: use / instead of DIR_SEP because pp->path is portable.
|
||||||
const int len = snprintf(pp->path, ARRAY_SIZE(pp->path), "%s/", dir);
|
const int len = snprintf(pp->path, ARRAY_SIZE(pp->path), "%s/", dir);
|
||||||
@ -84,16 +84,16 @@ int pp_set_dir(PathPackage* pp, const char* dir)
|
|||||||
// when attempting to vfs_open the file).
|
// when attempting to vfs_open the file).
|
||||||
if(len >= 2) // protect against underrun
|
if(len >= 2) // protect against underrun
|
||||||
debug_assert(pp->end[-2] != '/' && pp->end[-2] != DIR_SEP);
|
debug_assert(pp->end[-2] != '/' && pp->end[-2] != DIR_SEP);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// append the given filename to the directory established by the last
|
// append the given filename to the directory established by the last
|
||||||
// pp_set_dir on this package. the whole path is accessible at pp->path.
|
// pp_set_dir on this package. the whole path is accessible at pp->path.
|
||||||
int pp_append_file(PathPackage* pp, const char* fn)
|
LibError pp_append_file(PathPackage* pp, const char* fn)
|
||||||
{
|
{
|
||||||
CHECK_ERR(strcpy_s(pp->end, pp->chars_left, fn));
|
CHECK_ERR(strcpy_s(pp->end, pp->chars_left, fn));
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@ -138,7 +138,7 @@ enum Conversion
|
|||||||
TO_PORTABLE
|
TO_PORTABLE
|
||||||
};
|
};
|
||||||
|
|
||||||
static int convert_path(char* dst, const char* src, Conversion conv = TO_NATIVE)
|
static LibError convert_path(char* dst, const char* src, Conversion conv = TO_NATIVE)
|
||||||
{
|
{
|
||||||
// DIR_SEP is assumed to be a single character!
|
// DIR_SEP is assumed to be a single character!
|
||||||
|
|
||||||
@ -166,7 +166,7 @@ static int convert_path(char* dst, const char* src, Conversion conv = TO_NATIVE)
|
|||||||
|
|
||||||
// end of string - done
|
// end of string - done
|
||||||
if(c == '\0')
|
if(c == '\0')
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,7 +179,7 @@ static size_t n_root_dir_len;
|
|||||||
// return the native equivalent of the given relative portable path
|
// return the native equivalent of the given relative portable path
|
||||||
// (i.e. convert all '/' to the platform's directory separator)
|
// (i.e. convert all '/' to the platform's directory separator)
|
||||||
// makes sure length < PATH_MAX.
|
// makes sure length < PATH_MAX.
|
||||||
int file_make_native_path(const char* path, char* n_path)
|
LibError file_make_native_path(const char* path, char* n_path)
|
||||||
{
|
{
|
||||||
return convert_path(n_path, path, TO_NATIVE);
|
return convert_path(n_path, path, TO_NATIVE);
|
||||||
}
|
}
|
||||||
@ -187,7 +187,7 @@ int file_make_native_path(const char* path, char* n_path)
|
|||||||
// return the portable equivalent of the given relative native path
|
// return the portable equivalent of the given relative native path
|
||||||
// (i.e. convert the platform's directory separators to '/')
|
// (i.e. convert the platform's directory separators to '/')
|
||||||
// makes sure length < PATH_MAX.
|
// makes sure length < PATH_MAX.
|
||||||
int file_make_portable_path(const char* n_path, char* path)
|
LibError file_make_portable_path(const char* n_path, char* path)
|
||||||
{
|
{
|
||||||
return convert_path(path, n_path, TO_PORTABLE);
|
return convert_path(path, n_path, TO_PORTABLE);
|
||||||
}
|
}
|
||||||
@ -197,7 +197,7 @@ int file_make_portable_path(const char* n_path, char* path)
|
|||||||
// (i.e. convert all '/' to the platform's directory separator).
|
// (i.e. convert all '/' to the platform's directory separator).
|
||||||
// also prepends current directory => n_full_path is absolute.
|
// also prepends current directory => n_full_path is absolute.
|
||||||
// makes sure length < PATH_MAX.
|
// makes sure length < PATH_MAX.
|
||||||
int file_make_full_native_path(const char* path, char* n_full_path)
|
LibError file_make_full_native_path(const char* path, char* n_full_path)
|
||||||
{
|
{
|
||||||
debug_assert(path != n_full_path); // doesn't work in-place
|
debug_assert(path != n_full_path); // doesn't work in-place
|
||||||
|
|
||||||
@ -210,12 +210,12 @@ int file_make_full_native_path(const char* path, char* n_full_path)
|
|||||||
// n_full_path is absolute; if it doesn't match the current dir, fail.
|
// n_full_path is absolute; if it doesn't match the current dir, fail.
|
||||||
// (note: portable paths are always relative to the file root dir).
|
// (note: portable paths are always relative to the file root dir).
|
||||||
// makes sure length < PATH_MAX.
|
// makes sure length < PATH_MAX.
|
||||||
int file_make_full_portable_path(const char* n_full_path, char* path)
|
LibError file_make_full_portable_path(const char* n_full_path, char* path)
|
||||||
{
|
{
|
||||||
debug_assert(path != n_full_path); // doesn't work in-place
|
debug_assert(path != n_full_path); // doesn't work in-place
|
||||||
|
|
||||||
if(strncmp(n_full_path, n_root_dir, n_root_dir_len) != 0)
|
if(strncmp(n_full_path, n_root_dir, n_root_dir_len) != 0)
|
||||||
return -1;
|
return ERR_PATH_NOT_FOUND;
|
||||||
return convert_path(path, n_full_path+n_root_dir_len, TO_PORTABLE);
|
return convert_path(path, n_full_path+n_root_dir_len, TO_PORTABLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,7 +233,7 @@ int file_make_full_portable_path(const char* n_full_path, char* path)
|
|||||||
// easiest portable way to find our install directory.
|
// easiest portable way to find our install directory.
|
||||||
//
|
//
|
||||||
// can only be called once, by design (see below). rel_path is trusted.
|
// can only be called once, by design (see below). rel_path is trusted.
|
||||||
int file_set_root_dir(const char* argv0, const char* rel_path)
|
LibError file_set_root_dir(const char* argv0, const char* rel_path)
|
||||||
{
|
{
|
||||||
const char* msg = 0;
|
const char* msg = 0;
|
||||||
|
|
||||||
@ -259,12 +259,18 @@ int file_set_root_dir(const char* argv0, const char* rel_path)
|
|||||||
{
|
{
|
||||||
// .. failed; use argv[0]
|
// .. failed; use argv[0]
|
||||||
if(!realpath(argv0, n_path))
|
if(!realpath(argv0, n_path))
|
||||||
|
{
|
||||||
|
msg = "realpath(argv[0]) failed";
|
||||||
goto fail;
|
goto fail;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure it's valid
|
// make sure it's valid
|
||||||
if(access(n_path, X_OK) < 0)
|
if(access(n_path, X_OK) < 0)
|
||||||
|
{
|
||||||
|
msg = "ERR_FILE_ACCESS";
|
||||||
goto fail;
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
// strip executable name, append rel_path, convert to native
|
// strip executable name, append rel_path, convert to native
|
||||||
char* fn = strrchr(n_path, DIR_SEP);
|
char* fn = strrchr(n_path, DIR_SEP);
|
||||||
@ -283,7 +289,7 @@ int file_set_root_dir(const char* argv0, const char* rel_path)
|
|||||||
// (note: already 0-terminated, since it's static)
|
// (note: already 0-terminated, since it's static)
|
||||||
n_root_dir_len = strlen(n_root_dir)+1; // +1 for trailing DIR_SEP
|
n_root_dir_len = strlen(n_root_dir)+1; // +1 for trailing DIR_SEP
|
||||||
n_root_dir[n_root_dir_len-1] = DIR_SEP;
|
n_root_dir[n_root_dir_len-1] = DIR_SEP;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,10 +298,10 @@ fail:
|
|||||||
if(msg)
|
if(msg)
|
||||||
{
|
{
|
||||||
debug_printf("%s: %s\n", __func__, msg);
|
debug_printf("%s: %s\n", __func__, msg);
|
||||||
return -1;
|
return ERR_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return -errno;
|
return LibError_from_errno();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -304,8 +310,6 @@ fail:
|
|||||||
// layer on top of POSIX opendir/readdir/closedir that handles
|
// layer on top of POSIX opendir/readdir/closedir that handles
|
||||||
// portable -> native path conversion, ignores non-file/directory entries,
|
// portable -> native path conversion, ignores non-file/directory entries,
|
||||||
// and additionally returns the file status (size and mtime).
|
// and additionally returns the file status (size and mtime).
|
||||||
//
|
|
||||||
// all functions return an int error code to allow CHECK_ERR.
|
|
||||||
|
|
||||||
// rationale: see DirIterator definition in header.
|
// rationale: see DirIterator definition in header.
|
||||||
struct DirIterator_
|
struct DirIterator_
|
||||||
@ -326,7 +330,7 @@ cassert(sizeof(DirIterator_) <= sizeof(DirIterator));
|
|||||||
// prepare to iterate (once) over entries in the given directory.
|
// prepare to iterate (once) over entries in the given directory.
|
||||||
// returns a negative error code or 0 on success, in which case <d> is
|
// returns a negative error code or 0 on success, in which case <d> is
|
||||||
// ready for subsequent dir_next_ent calls and must be freed via dir_close.
|
// ready for subsequent dir_next_ent calls and must be freed via dir_close.
|
||||||
int dir_open(const char* P_path, DirIterator* d_)
|
LibError dir_open(const char* P_path, DirIterator* d_)
|
||||||
{
|
{
|
||||||
DirIterator_* d = (DirIterator_*)d_;
|
DirIterator_* d = (DirIterator_*)d_;
|
||||||
|
|
||||||
@ -346,32 +350,17 @@ int dir_open(const char* P_path, DirIterator* d_)
|
|||||||
|
|
||||||
d->os_dir = opendir(n_path);
|
d->os_dir = opendir(n_path);
|
||||||
if(!d->os_dir)
|
if(!d->os_dir)
|
||||||
{
|
CHECK_ERR(LibError_from_errno());
|
||||||
int err;
|
|
||||||
switch(errno)
|
|
||||||
{
|
|
||||||
case ENOMEM:
|
|
||||||
err = ERR_NO_MEM;
|
|
||||||
break;
|
|
||||||
case ENOENT:
|
|
||||||
err = ERR_PATH_NOT_FOUND;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
err = -1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
CHECK_ERR(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
RETURN_ERR(pp_set_dir(&d->pp, n_path));
|
RETURN_ERR(pp_set_dir(&d->pp, n_path));
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// return ERR_DIR_END if all entries have already been returned once,
|
// return ERR_DIR_END if all entries have already been returned once,
|
||||||
// another negative error code, or 0 on success, in which case <ent>
|
// another negative error code, or 0 on success, in which case <ent>
|
||||||
// describes the next (order is unspecified) directory entry.
|
// describes the next (order is unspecified) directory entry.
|
||||||
int dir_next_ent(DirIterator* d_, DirEnt* ent)
|
LibError dir_next_ent(DirIterator* d_, DirEnt* ent)
|
||||||
{
|
{
|
||||||
DirIterator_* d = (DirIterator_*)d_;
|
DirIterator_* d = (DirIterator_*)d_;
|
||||||
|
|
||||||
@ -420,17 +409,17 @@ get_another_entry:
|
|||||||
ent->size = s.st_size;
|
ent->size = s.st_size;
|
||||||
ent->mtime = s.st_mtime;
|
ent->mtime = s.st_mtime;
|
||||||
ent->name = name;
|
ent->name = name;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// indicate the directory iterator is no longer needed; all resources it
|
// indicate the directory iterator is no longer needed; all resources it
|
||||||
// held are freed.
|
// held are freed.
|
||||||
int dir_close(DirIterator* d_)
|
LibError dir_close(DirIterator* d_)
|
||||||
{
|
{
|
||||||
DirIterator_* d = (DirIterator_*)d_;
|
DirIterator_* d = (DirIterator_*)d_;
|
||||||
WARN_ERR(closedir(d->os_dir));
|
WARN_ERR(closedir(d->os_dir));
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -456,7 +445,7 @@ static bool dirent_less(const DirEnt* d1, const DirEnt* d2)
|
|||||||
// special-case Zip files anyway.
|
// special-case Zip files anyway.
|
||||||
// the advantage here is simplicity, and sparing callbacks the trouble
|
// the advantage here is simplicity, and sparing callbacks the trouble
|
||||||
// of converting from/to native path (we just give 'em the dirent name).
|
// of converting from/to native path (we just give 'em the dirent name).
|
||||||
int file_enum(const char* P_path, const FileCB cb, const uintptr_t user)
|
LibError file_enum(const char* P_path, const FileCB cb, const uintptr_t user)
|
||||||
{
|
{
|
||||||
// pointer to DirEnt: faster sorting, but more allocs.
|
// pointer to DirEnt: faster sorting, but more allocs.
|
||||||
typedef std::vector<const DirEnt*> DirEnts;
|
typedef std::vector<const DirEnt*> DirEnts;
|
||||||
@ -467,8 +456,8 @@ int file_enum(const char* P_path, const FileCB cb, const uintptr_t user)
|
|||||||
DirEnts dirents;
|
DirEnts dirents;
|
||||||
dirents.reserve(125); // preallocate for efficiency
|
dirents.reserve(125); // preallocate for efficiency
|
||||||
|
|
||||||
int stat_err = 0; // first error encountered by stat()
|
LibError stat_err = ERR_OK; // first error encountered by stat()
|
||||||
int cb_err = 0; // first error returned by cb
|
LibError cb_err = ERR_OK; // first error returned by cb
|
||||||
|
|
||||||
DirIterator d;
|
DirIterator d;
|
||||||
CHECK_ERR(dir_open(P_path, &d));
|
CHECK_ERR(dir_open(P_path, &d));
|
||||||
@ -476,7 +465,7 @@ int file_enum(const char* P_path, const FileCB cb, const uintptr_t user)
|
|||||||
DirEnt ent;
|
DirEnt ent;
|
||||||
for(;;) // instead of while() to avoid warnings
|
for(;;) // instead of while() to avoid warnings
|
||||||
{
|
{
|
||||||
int ret = dir_next_ent(&d, &ent);
|
LibError ret = dir_next_ent(&d, &ent);
|
||||||
if(ret == ERR_DIR_END)
|
if(ret == ERR_DIR_END)
|
||||||
break;
|
break;
|
||||||
if(!stat_err)
|
if(!stat_err)
|
||||||
@ -508,8 +497,8 @@ int file_enum(const char* P_path, const FileCB cb, const uintptr_t user)
|
|||||||
s.st_mode = (ent->size == -1)? S_IFDIR : S_IFREG;
|
s.st_mode = (ent->size == -1)? S_IFDIR : S_IFREG;
|
||||||
s.st_size = ent->size;
|
s.st_size = ent->size;
|
||||||
s.st_mtime = ent->mtime;
|
s.st_mtime = ent->mtime;
|
||||||
int ret = cb(ent->name, &s, user);
|
LibError ret = cb(ent->name, &s, user);
|
||||||
if(ret != 0)
|
if(ret != INFO_CB_CONTINUE)
|
||||||
{
|
{
|
||||||
cb_err = ret; // first error (since we now abort)
|
cb_err = ret; // first error (since we now abort)
|
||||||
break;
|
break;
|
||||||
@ -524,21 +513,22 @@ fail:
|
|||||||
for(DirEntRIt rit = dirents.rbegin(); rit != dirents.rend(); ++rit)
|
for(DirEntRIt rit = dirents.rbegin(); rit != dirents.rend(); ++rit)
|
||||||
free((void*)(*rit));
|
free((void*)(*rit));
|
||||||
|
|
||||||
if(cb_err != 0)
|
if(cb_err != ERR_OK)
|
||||||
return cb_err;
|
return cb_err;
|
||||||
return stat_err;
|
return stat_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// get file information. output param is zeroed on error.
|
// get file information. output param is zeroed on error.
|
||||||
int file_stat(const char* path, struct stat* s)
|
LibError file_stat(const char* path, struct stat* s)
|
||||||
{
|
{
|
||||||
memset(s, 0, sizeof(struct stat));
|
memset(s, 0, sizeof(struct stat));
|
||||||
|
|
||||||
char n_path[PATH_MAX+1];
|
char n_path[PATH_MAX+1];
|
||||||
CHECK_ERR(file_make_full_native_path(path, n_path));
|
CHECK_ERR(file_make_full_native_path(path, n_path));
|
||||||
|
|
||||||
return stat(n_path, s);
|
errno = 0;
|
||||||
|
return LibError_from_posix(stat(n_path, s));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -572,28 +562,28 @@ int file_stat(const char* path, struct stat* s)
|
|||||||
// close(our_fd_value) directly, either.
|
// close(our_fd_value) directly, either.
|
||||||
|
|
||||||
|
|
||||||
int file_validate(const File* f)
|
LibError file_validate(const File* f)
|
||||||
{
|
{
|
||||||
if(!f)
|
if(!f)
|
||||||
return ERR_INVALID_PARAM;
|
return ERR_INVALID_PARAM;
|
||||||
else if(f->fd < 0)
|
else if(f->fd < 0)
|
||||||
return -2;
|
return ERR_1;
|
||||||
// mapped but refcount is invalid
|
// mapped but refcount is invalid
|
||||||
else if((f->mapping != 0) ^ (f->map_refs != 0))
|
else if((f->mapping != 0) ^ (f->map_refs != 0))
|
||||||
return -3;
|
return ERR_2;
|
||||||
// fn_hash not set
|
// fn_hash not set
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
else if(!f->fn_hash)
|
else if(!f->fn_hash)
|
||||||
return -4;
|
return ERR_3;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CHECK_FILE(f) CHECK_ERR(file_validate(f))
|
#define CHECK_FILE(f) CHECK_ERR(file_validate(f))
|
||||||
|
|
||||||
|
|
||||||
int file_open(const char* p_fn, const uint flags, File* f)
|
LibError file_open(const char* p_fn, const uint flags, File* f)
|
||||||
{
|
{
|
||||||
// zero output param in case we fail below.
|
// zero output param in case we fail below.
|
||||||
memset(f, 0, sizeof(*f));
|
memset(f, 0, sizeof(*f));
|
||||||
@ -656,11 +646,11 @@ int file_open(const char* p_fn, const uint flags, File* f)
|
|||||||
f->map_refs = 0;
|
f->map_refs = 0;
|
||||||
f->fd = fd;
|
f->fd = fd;
|
||||||
CHECK_FILE(f);
|
CHECK_FILE(f);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int file_close(File* f)
|
LibError file_close(File* f)
|
||||||
{
|
{
|
||||||
CHECK_FILE(f);
|
CHECK_FILE(f);
|
||||||
|
|
||||||
@ -683,7 +673,7 @@ int file_close(File* f)
|
|||||||
f->fd = -1;
|
f->fd = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -743,7 +733,7 @@ static inline void aiocb_pool_free(void* cb)
|
|||||||
|
|
||||||
// starts transferring to/from the given buffer.
|
// starts transferring to/from the given buffer.
|
||||||
// no attempt is made at aligning or padding the transfer.
|
// no attempt is made at aligning or padding the transfer.
|
||||||
int file_io_issue(File* f, off_t ofs, size_t size, void* p, FileIo* io)
|
LibError file_io_issue(File* f, off_t ofs, size_t size, void* p, FileIo* io)
|
||||||
{
|
{
|
||||||
// zero output param in case we fail below.
|
// zero output param in case we fail below.
|
||||||
memset(io, 0, sizeof(FileIo));
|
memset(io, 0, sizeof(FileIo));
|
||||||
@ -798,10 +788,10 @@ debug_printf("FILE| issue2 io=%p nbytes=%d\n", io, cb->aio_nbytes);
|
|||||||
{
|
{
|
||||||
debug_printf("lio_listio: %d, %d[%s]\n", err, errno, strerror(errno));
|
debug_printf("lio_listio: %d, %d[%s]\n", err, errno, strerror(errno));
|
||||||
file_io_discard(io);
|
file_io_discard(io);
|
||||||
return err;
|
return LibError_from_errno();
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -821,7 +811,7 @@ int file_io_has_completed(FileIo* io)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int file_io_wait(FileIo* io, void*& p, size_t& size)
|
LibError file_io_wait(FileIo* io, void*& p, size_t& size)
|
||||||
{
|
{
|
||||||
debug_printf("FILE| wait io=%p\n", io);
|
debug_printf("FILE| wait io=%p\n", io);
|
||||||
|
|
||||||
@ -845,33 +835,33 @@ int file_io_wait(FileIo* io, void*& p, size_t& size)
|
|||||||
|
|
||||||
p = (void*)cb->aio_buf; // cast from volatile void*
|
p = (void*)cb->aio_buf; // cast from volatile void*
|
||||||
size = bytes_transferred;
|
size = bytes_transferred;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int file_io_discard(FileIo* io)
|
LibError file_io_discard(FileIo* io)
|
||||||
{
|
{
|
||||||
memset(io->cb, 0, sizeof(aiocb));
|
memset(io->cb, 0, sizeof(aiocb));
|
||||||
// discourage further use.
|
// discourage further use.
|
||||||
aiocb_pool_free(io->cb);
|
aiocb_pool_free(io->cb);
|
||||||
io->cb = 0;
|
io->cb = 0;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int file_io_validate(const FileIo* io)
|
LibError file_io_validate(const FileIo* io)
|
||||||
{
|
{
|
||||||
const aiocb* cb = (const aiocb*)io->cb;
|
const aiocb* cb = (const aiocb*)io->cb;
|
||||||
// >= 0x100 is not necessarily bogus, but suspicious.
|
// >= 0x100 is not necessarily bogus, but suspicious.
|
||||||
// this also catches negative values.
|
// this also catches negative values.
|
||||||
if((uint)cb->aio_fildes >= 0x100)
|
if((uint)cb->aio_fildes >= 0x100)
|
||||||
return -2;
|
return ERR_1;
|
||||||
if(debug_is_pointer_bogus((void*)cb->aio_buf))
|
if(debug_is_pointer_bogus((void*)cb->aio_buf))
|
||||||
return -3;
|
return ERR_2;
|
||||||
if(cb->aio_lio_opcode != LIO_WRITE && cb->aio_lio_opcode != LIO_READ && cb->aio_lio_opcode != LIO_NOP)
|
if(cb->aio_lio_opcode != LIO_WRITE && cb->aio_lio_opcode != LIO_READ && cb->aio_lio_opcode != LIO_NOP)
|
||||||
return -4;
|
return ERR_3;
|
||||||
// all other aiocb fields have no invariants we could check.
|
// all other aiocb fields have no invariants we could check.
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1026,7 +1016,7 @@ static void block_shutdown()
|
|||||||
|
|
||||||
|
|
||||||
// remove all blocks loaded from the file <fn>. used when reloading the file.
|
// remove all blocks loaded from the file <fn>. used when reloading the file.
|
||||||
int file_invalidate_cache(const char* fn)
|
LibError file_invalidate_cache(const char* fn)
|
||||||
{
|
{
|
||||||
// convert to native path to match fn_hash set by file_open
|
// convert to native path to match fn_hash set by file_open
|
||||||
char n_fn[PATH_MAX];
|
char n_fn[PATH_MAX];
|
||||||
@ -1041,7 +1031,7 @@ int file_invalidate_cache(const char* fn)
|
|||||||
if((it->first >> 32) == fn_hash)
|
if((it->first >> 32) == fn_hash)
|
||||||
block_cache.erase(it);
|
block_cache.erase(it);
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1300,7 +1290,7 @@ static const uint MAX_MAP_REFS = 255;
|
|||||||
// rationale: reference counting is required for zip_map: several
|
// rationale: reference counting is required for zip_map: several
|
||||||
// Zip "mappings" each reference one ZArchive's actual file mapping.
|
// Zip "mappings" each reference one ZArchive's actual file mapping.
|
||||||
// implement it here so that we also get refcounting for normal files.
|
// implement it here so that we also get refcounting for normal files.
|
||||||
int file_map(File* f, void*& p, size_t& size)
|
LibError file_map(File* f, void*& p, size_t& size)
|
||||||
{
|
{
|
||||||
p = 0;
|
p = 0;
|
||||||
size = 0;
|
size = 0;
|
||||||
@ -1314,10 +1304,7 @@ int file_map(File* f, void*& p, size_t& size)
|
|||||||
{
|
{
|
||||||
// prevent overflow; if we have this many refs, should find out why.
|
// prevent overflow; if we have this many refs, should find out why.
|
||||||
if(f->map_refs >= MAX_MAP_REFS)
|
if(f->map_refs >= MAX_MAP_REFS)
|
||||||
{
|
CHECK_ERR(ERR_LIMIT);
|
||||||
debug_warn("too many references to mapping");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
f->map_refs++;
|
f->map_refs++;
|
||||||
goto have_mapping;
|
goto have_mapping;
|
||||||
}
|
}
|
||||||
@ -1327,18 +1314,19 @@ int file_map(File* f, void*& p, size_t& size)
|
|||||||
// then again, don't complain, because this might happen when mounting
|
// then again, don't complain, because this might happen when mounting
|
||||||
// a dir containing empty files; each is opened as a Zip file.
|
// a dir containing empty files; each is opened as a Zip file.
|
||||||
if(f->size == 0)
|
if(f->size == 0)
|
||||||
return -1;
|
return ERR_FAIL;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
f->mapping = mmap((void*)0, f->size, prot, MAP_PRIVATE, f->fd, (off_t)0);
|
f->mapping = mmap((void*)0, f->size, prot, MAP_PRIVATE, f->fd, (off_t)0);
|
||||||
if(!f->mapping)
|
if(f->mapping == MAP_FAILED)
|
||||||
return ERR_NO_MEM;
|
return LibError_from_errno();
|
||||||
|
|
||||||
f->map_refs = 1;
|
f->map_refs = 1;
|
||||||
|
|
||||||
have_mapping:
|
have_mapping:
|
||||||
p = f->mapping;
|
p = f->mapping;
|
||||||
size = f->size;
|
size = f->size;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1348,7 +1336,7 @@ have_mapping:
|
|||||||
// the mapping will be removed (if still open) when its file is closed.
|
// the mapping will be removed (if still open) when its file is closed.
|
||||||
// however, map/unmap calls should still be paired so that the mapping
|
// however, map/unmap calls should still be paired so that the mapping
|
||||||
// may be removed when no longer needed.
|
// may be removed when no longer needed.
|
||||||
int file_unmap(File* f)
|
LibError file_unmap(File* f)
|
||||||
{
|
{
|
||||||
CHECK_FILE(f);
|
CHECK_FILE(f);
|
||||||
|
|
||||||
@ -1356,25 +1344,26 @@ int file_unmap(File* f)
|
|||||||
if(f->map_refs == 0)
|
if(f->map_refs == 0)
|
||||||
{
|
{
|
||||||
debug_warn("not currently mapped");
|
debug_warn("not currently mapped");
|
||||||
return -1;
|
return ERR_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// still more than one reference remaining - done.
|
// still more than one reference remaining - done.
|
||||||
if(--f->map_refs > 0)
|
if(--f->map_refs > 0)
|
||||||
return 0;
|
return ERR_OK;
|
||||||
|
|
||||||
// no more references: remove the mapping
|
// no more references: remove the mapping
|
||||||
void* p = f->mapping;
|
void* p = f->mapping;
|
||||||
f->mapping = 0;
|
f->mapping = 0;
|
||||||
// don't clear f->size - the file is still open.
|
// don't clear f->size - the file is still open.
|
||||||
|
|
||||||
return munmap(p, f->size);
|
errno = 0;
|
||||||
|
return LibError_from_posix(munmap(p, f->size));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int file_shutdown()
|
LibError file_shutdown()
|
||||||
{
|
{
|
||||||
aiocb_pool_shutdown();
|
aiocb_pool_shutdown();
|
||||||
block_shutdown();
|
block_shutdown();
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
@ -36,11 +36,11 @@ struct PathPackage
|
|||||||
|
|
||||||
// write the given directory path into our buffer and set end/chars_left
|
// write the given directory path into our buffer and set end/chars_left
|
||||||
// accordingly. <dir> need and should not end with a directory separator.
|
// accordingly. <dir> need and should not end with a directory separator.
|
||||||
extern int pp_set_dir(PathPackage* pp, const char* dir);
|
extern LibError pp_set_dir(PathPackage* pp, const char* dir);
|
||||||
|
|
||||||
// append the given filename to the directory established by the last
|
// append the given filename to the directory established by the last
|
||||||
// pp_set_dir on this package. the whole path is accessible at pp->path.
|
// pp_set_dir on this package. the whole path is accessible at pp->path.
|
||||||
extern int pp_append_file(PathPackage* pp, const char* file);
|
extern LibError pp_append_file(PathPackage* pp, const char* file);
|
||||||
|
|
||||||
|
|
||||||
// is s2 a subpath of s1, or vice versa? used by VFS and wdir_watch.
|
// is s2 a subpath of s1, or vice versa? used by VFS and wdir_watch.
|
||||||
@ -57,13 +57,13 @@ extern bool file_is_subpath(const char* s1, const char* s2);
|
|||||||
//
|
//
|
||||||
|
|
||||||
// relative paths (relative to root dir)
|
// relative paths (relative to root dir)
|
||||||
extern int file_make_native_path(const char* path, char* n_path);
|
extern LibError file_make_native_path(const char* path, char* n_path);
|
||||||
extern int file_make_portable_path(const char* n_path, char* path);
|
extern LibError file_make_portable_path(const char* n_path, char* path);
|
||||||
|
|
||||||
// as above, but with full native paths (portable paths are always relative).
|
// as above, but with full native paths (portable paths are always relative).
|
||||||
// prepends current directory, resp. makes sure it matches the given path.
|
// prepends current directory, resp. makes sure it matches the given path.
|
||||||
extern int file_make_full_native_path(const char* path, char* n_full_path);
|
extern LibError file_make_full_native_path(const char* path, char* n_full_path);
|
||||||
extern int file_make_full_portable_path(const char* n_full_path, char* path);
|
extern LibError file_make_full_portable_path(const char* n_full_path, char* path);
|
||||||
|
|
||||||
|
|
||||||
// establish the root directory from <rel_path>, which is treated as
|
// establish the root directory from <rel_path>, which is treated as
|
||||||
@ -79,7 +79,7 @@ extern int file_make_full_portable_path(const char* n_full_path, char* path);
|
|||||||
// easiest portable way to find our install directory.
|
// easiest portable way to find our install directory.
|
||||||
//
|
//
|
||||||
// can only be called once, by design (see below). rel_path is trusted.
|
// can only be called once, by design (see below). rel_path is trusted.
|
||||||
extern int file_set_root_dir(const char* argv0, const char* rel_path);
|
extern LibError file_set_root_dir(const char* argv0, const char* rel_path);
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -89,8 +89,6 @@ extern int file_set_root_dir(const char* argv0, const char* rel_path);
|
|||||||
// layer on top of POSIX opendir/readdir/closedir that handles
|
// layer on top of POSIX opendir/readdir/closedir that handles
|
||||||
// portable -> native path conversion, ignores non-file/directory entries,
|
// portable -> native path conversion, ignores non-file/directory entries,
|
||||||
// and additionally returns the file status (size and mtime).
|
// and additionally returns the file status (size and mtime).
|
||||||
//
|
|
||||||
// all functions return an int error code to allow CHECK_ERR.
|
|
||||||
|
|
||||||
// directory state initialized by dir_open.
|
// directory state initialized by dir_open.
|
||||||
// rationale: some private storage apart from opendir's DIR* is required
|
// rationale: some private storage apart from opendir's DIR* is required
|
||||||
@ -132,22 +130,23 @@ struct DirEnt
|
|||||||
// prepare to iterate (once) over entries in the given directory.
|
// prepare to iterate (once) over entries in the given directory.
|
||||||
// returns a negative error code or 0 on success, in which case <d> is
|
// returns a negative error code or 0 on success, in which case <d> is
|
||||||
// ready for subsequent dir_next_ent calls and must be freed via dir_close.
|
// ready for subsequent dir_next_ent calls and must be freed via dir_close.
|
||||||
extern int dir_open(const char* P_path, DirIterator* d);
|
extern LibError dir_open(const char* P_path, DirIterator* d);
|
||||||
|
|
||||||
// return ERR_DIR_END if all entries have already been returned once,
|
// return ERR_DIR_END if all entries have already been returned once,
|
||||||
// another negative error code, or 0 on success, in which case <ent>
|
// another negative error code, or 0 on success, in which case <ent>
|
||||||
// describes the next (order is unspecified) directory entry.
|
// describes the next (order is unspecified) directory entry.
|
||||||
extern int dir_next_ent(DirIterator* d, DirEnt* ent);
|
extern LibError dir_next_ent(DirIterator* d, DirEnt* ent);
|
||||||
|
|
||||||
// indicate the directory iterator is no longer needed; all resources it
|
// indicate the directory iterator is no longer needed; all resources it
|
||||||
// held are freed.
|
// held are freed.
|
||||||
extern int dir_close(DirIterator* d);
|
extern LibError dir_close(DirIterator* d);
|
||||||
|
|
||||||
|
|
||||||
// called by file_enum for each entry in the directory.
|
// called by file_enum for each entry in the directory.
|
||||||
// name doesn't include path!
|
// name doesn't include path!
|
||||||
// return non-zero to immediately abort; file_enum will return that value.
|
// return INFO_CB_CONTINUE to continue calling; anything else will cause
|
||||||
typedef int(*FileCB)(const char* name, const struct stat* s, const uintptr_t user);
|
// file_enum to abort and immediately return that value.
|
||||||
|
typedef LibError (*FileCB)(const char* name, const struct stat* s, const uintptr_t user);
|
||||||
|
|
||||||
// call <cb> for each file and subdirectory in <dir> (alphabetical order),
|
// call <cb> for each file and subdirectory in <dir> (alphabetical order),
|
||||||
// passing the entry name (not full path!), stat info, and <user>.
|
// passing the entry name (not full path!), stat info, and <user>.
|
||||||
@ -155,7 +154,7 @@ typedef int(*FileCB)(const char* name, const struct stat* s, const uintptr_t use
|
|||||||
// first builds a list of entries (sorted) and remembers if an error occurred.
|
// first builds a list of entries (sorted) and remembers if an error occurred.
|
||||||
// if <cb> returns non-zero, abort immediately and return that; otherwise,
|
// if <cb> returns non-zero, abort immediately and return that; otherwise,
|
||||||
// return first error encountered while listing files, or 0 on success.
|
// return first error encountered while listing files, or 0 on success.
|
||||||
extern int file_enum(const char* dir, FileCB cb, uintptr_t user);
|
extern LibError file_enum(const char* dir, FileCB cb, uintptr_t user);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -211,19 +210,19 @@ enum
|
|||||||
|
|
||||||
|
|
||||||
// get file information. output param is zeroed on error.
|
// get file information. output param is zeroed on error.
|
||||||
extern int file_stat(const char* path, struct stat*);
|
extern LibError file_stat(const char* path, struct stat*);
|
||||||
|
|
||||||
extern int file_open(const char* fn, uint flags, File* f);
|
extern LibError file_open(const char* fn, uint flags, File* f);
|
||||||
|
|
||||||
// note: final file size is calculated and returned in f->size.
|
// note: final file size is calculated and returned in f->size.
|
||||||
// see implementation for rationale.
|
// see implementation for rationale.
|
||||||
extern int file_close(File* f);
|
extern LibError file_close(File* f);
|
||||||
|
|
||||||
extern int file_validate(const File* f);
|
extern LibError file_validate(const File* f);
|
||||||
|
|
||||||
|
|
||||||
// remove all blocks loaded from the file <fn>. used when reloading the file.
|
// remove all blocks loaded from the file <fn>. used when reloading the file.
|
||||||
extern int file_invalidate_cache(const char* fn);
|
extern LibError file_invalidate_cache(const char* fn);
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -237,17 +236,17 @@ struct FileIo
|
|||||||
|
|
||||||
// rationale: this interface is more convenient than implicitly advancing a
|
// rationale: this interface is more convenient than implicitly advancing a
|
||||||
// file pointer because zip.cpp often accesses random offsets.
|
// file pointer because zip.cpp often accesses random offsets.
|
||||||
extern int file_io_issue(File* f, off_t ofs, size_t size, void* buf, FileIo* io);
|
extern LibError file_io_issue(File* f, off_t ofs, size_t size, void* buf, FileIo* io);
|
||||||
|
|
||||||
// indicates if the given IO has completed.
|
// indicates if the given IO has completed.
|
||||||
// return value: 0 if pending, 1 if complete, < 0 on error.
|
// return value: 0 if pending, 1 if complete, < 0 on error.
|
||||||
extern int file_io_has_completed(FileIo* io);
|
extern int file_io_has_completed(FileIo* io);
|
||||||
|
|
||||||
extern int file_io_wait(FileIo* io, void*& p, size_t& size);
|
extern LibError file_io_wait(FileIo* io, void*& p, size_t& size);
|
||||||
|
|
||||||
extern int file_io_discard(FileIo* io);
|
extern LibError file_io_discard(FileIo* io);
|
||||||
|
|
||||||
extern int file_io_validate(const FileIo* io);
|
extern LibError file_io_validate(const FileIo* io);
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -261,7 +260,7 @@ const size_t FILE_BLOCK_SIZE = 64*KiB;
|
|||||||
// return value:
|
// return value:
|
||||||
// < 0: failed; abort transfer.
|
// < 0: failed; abort transfer.
|
||||||
// >= 0: bytes output; continue.
|
// >= 0: bytes output; continue.
|
||||||
typedef ssize_t(*FileIOCB)(uintptr_t ctx, void* p, size_t size);
|
typedef ssize_t (*FileIOCB)(uintptr_t ctx, void* p, size_t size);
|
||||||
|
|
||||||
// transfer <size> bytes, starting at <ofs>, to/from the given file.
|
// transfer <size> bytes, starting at <ofs>, to/from the given file.
|
||||||
// (read or write access was chosen at file-open time).
|
// (read or write access was chosen at file-open time).
|
||||||
@ -296,7 +295,7 @@ extern ssize_t file_io(File* f, off_t ofs, size_t size, void* buf, FileIOCB cb =
|
|||||||
// rationale: reference counting is required for zip_map: several
|
// rationale: reference counting is required for zip_map: several
|
||||||
// Zip "mappings" each reference one ZArchive's actual file mapping.
|
// Zip "mappings" each reference one ZArchive's actual file mapping.
|
||||||
// implement it here so that we also get refcounting for normal files.
|
// implement it here so that we also get refcounting for normal files.
|
||||||
extern int file_map(File* f, void*& p, size_t& size);
|
extern LibError file_map(File* f, void*& p, size_t& size);
|
||||||
|
|
||||||
// decrement the reference count for the mapping belonging to file <f>.
|
// decrement the reference count for the mapping belonging to file <f>.
|
||||||
// fail if there are no references; remove the mapping if the count reaches 0.
|
// fail if there are no references; remove the mapping if the count reaches 0.
|
||||||
@ -304,9 +303,9 @@ extern int file_map(File* f, void*& p, size_t& size);
|
|||||||
// the mapping will be removed (if still open) when its file is closed.
|
// the mapping will be removed (if still open) when its file is closed.
|
||||||
// however, map/unmap calls should still be paired so that the mapping
|
// however, map/unmap calls should still be paired so that the mapping
|
||||||
// may be removed when no longer needed.
|
// may be removed when no longer needed.
|
||||||
extern int file_unmap(File* f);
|
extern LibError file_unmap(File* f);
|
||||||
|
|
||||||
|
|
||||||
extern int file_shutdown();
|
extern LibError file_shutdown();
|
||||||
|
|
||||||
#endif // #ifndef FILE_H
|
#endif // #ifndef FILE_H
|
||||||
|
@ -122,7 +122,7 @@ static void VDir_dtor(VDir* vd)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int VDir_reload(VDir* vd, const char* path, Handle UNUSED(hvd))
|
static LibError VDir_reload(VDir* vd, const char* path, Handle UNUSED(hvd))
|
||||||
{
|
{
|
||||||
// add required trailing slash if not already present to make
|
// add required trailing slash if not already present to make
|
||||||
// caller's life easier.
|
// caller's life easier.
|
||||||
@ -131,18 +131,18 @@ static int VDir_reload(VDir* vd, const char* path, Handle UNUSED(hvd))
|
|||||||
|
|
||||||
RETURN_ERR(tree_dir_open(V_path_slash, &vd->it));
|
RETURN_ERR(tree_dir_open(V_path_slash, &vd->it));
|
||||||
vd->it_valid = 1;
|
vd->it_valid = 1;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int VDir_validate(const VDir* vd)
|
static LibError VDir_validate(const VDir* vd)
|
||||||
{
|
{
|
||||||
// note: <it> is opaque and cannot be validated.
|
// note: <it> is opaque and cannot be validated.
|
||||||
if(vd->filter && !isprint(vd->filter[0]))
|
if(vd->filter && !isprint(vd->filter[0]))
|
||||||
return -2;
|
return ERR_1;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int VDir_to_string(const VDir* d, char* buf)
|
static LibError VDir_to_string(const VDir* d, char* buf)
|
||||||
{
|
{
|
||||||
const char* filter = d->filter;
|
const char* filter = d->filter;
|
||||||
if(!d->filter_latched)
|
if(!d->filter_latched)
|
||||||
@ -150,7 +150,7 @@ static int VDir_to_string(const VDir* d, char* buf)
|
|||||||
if(!filter)
|
if(!filter)
|
||||||
filter = "*";
|
filter = "*";
|
||||||
snprintf(buf, H_STRING_LEN, "(\"%s\")", filter);
|
snprintf(buf, H_STRING_LEN, "(\"%s\")", filter);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -165,7 +165,7 @@ Handle vfs_dir_open(const char* v_dir)
|
|||||||
|
|
||||||
|
|
||||||
// close the handle to a directory.
|
// close the handle to a directory.
|
||||||
int vfs_dir_close(Handle& hd)
|
LibError vfs_dir_close(Handle& hd)
|
||||||
{
|
{
|
||||||
return h_free(hd, H_VDir);
|
return h_free(hd, H_VDir);
|
||||||
}
|
}
|
||||||
@ -188,7 +188,7 @@ int vfs_dir_close(Handle& hd)
|
|||||||
// most callers don't need it and the overhead is considerable
|
// most callers don't need it and the overhead is considerable
|
||||||
// (we'd have to store all entries in a vector). it is left up to
|
// (we'd have to store all entries in a vector). it is left up to
|
||||||
// higher-level code such as VfsUtil.
|
// higher-level code such as VfsUtil.
|
||||||
int vfs_dir_next_ent(const Handle hd, DirEnt* ent, const char* filter)
|
LibError vfs_dir_next_ent(const Handle hd, DirEnt* ent, const char* filter)
|
||||||
{
|
{
|
||||||
H_DEREF(hd, VDir, vd);
|
H_DEREF(hd, VDir, vd);
|
||||||
|
|
||||||
@ -238,7 +238,7 @@ int vfs_dir_next_ent(const Handle hd, DirEnt* ent, const char* filter)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0; // success
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -317,7 +317,7 @@ void vfs_enable_file_listing(bool want_enabled)
|
|||||||
|
|
||||||
// return actual path to the specified file:
|
// return actual path to the specified file:
|
||||||
// "<real_directory>/fn" or "<archive_name>/fn".
|
// "<real_directory>/fn" or "<archive_name>/fn".
|
||||||
int vfs_realpath(const char* v_path, char* realpath)
|
LibError vfs_realpath(const char* v_path, char* realpath)
|
||||||
{
|
{
|
||||||
TFile* tf;
|
TFile* tf;
|
||||||
char V_exact_path[VFS_MAX_PATH];
|
char V_exact_path[VFS_MAX_PATH];
|
||||||
@ -339,7 +339,7 @@ bool vfs_exists(const char* v_fn)
|
|||||||
|
|
||||||
|
|
||||||
// get file status (mode, size, mtime). output param is zeroed on error.
|
// get file status (mode, size, mtime). output param is zeroed on error.
|
||||||
int vfs_stat(const char* v_path, struct stat* s)
|
LibError vfs_stat(const char* v_path, struct stat* s)
|
||||||
{
|
{
|
||||||
memset(s, 0, sizeof(*s));
|
memset(s, 0, sizeof(*s));
|
||||||
|
|
||||||
@ -388,21 +388,21 @@ static void VFile_dtor(VFile* vf)
|
|||||||
(void)mem_free_h(vf->hm);
|
(void)mem_free_h(vf->hm);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int VFile_reload(VFile* vf, const char* V_path, Handle)
|
static LibError VFile_reload(VFile* vf, const char* V_path, Handle)
|
||||||
{
|
{
|
||||||
const uint flags = x_flags(&vf->xf);
|
const uint flags = x_flags(&vf->xf);
|
||||||
|
|
||||||
// we're done if file is already open. need to check this because
|
// we're done if file is already open. need to check this because
|
||||||
// reload order (e.g. if resource opens a file) is unspecified.
|
// reload order (e.g. if resource opens a file) is unspecified.
|
||||||
if(x_is_open(&vf->xf))
|
if(x_is_open(&vf->xf))
|
||||||
return 0;
|
return ERR_OK;
|
||||||
|
|
||||||
file_listing_add(V_path);
|
file_listing_add(V_path);
|
||||||
|
|
||||||
TFile* tf;
|
TFile* tf;
|
||||||
char V_exact_path[VFS_MAX_PATH];
|
char V_exact_path[VFS_MAX_PATH];
|
||||||
uint lf = (flags & FILE_WRITE)? LF_CREATE_MISSING : 0;
|
uint lf = (flags & FILE_WRITE)? LF_CREATE_MISSING : 0;
|
||||||
int err = tree_lookup(V_path, &tf, lf, V_exact_path);
|
LibError err = tree_lookup(V_path, &tf, lf, V_exact_path);
|
||||||
if(err < 0)
|
if(err < 0)
|
||||||
{
|
{
|
||||||
// don't CHECK_ERR - this happens often and the dialog is annoying
|
// don't CHECK_ERR - this happens often and the dialog is annoying
|
||||||
@ -414,17 +414,17 @@ static int VFile_reload(VFile* vf, const char* V_path, Handle)
|
|||||||
return x_open(m, V_exact_path, flags, tf, &vf->xf);
|
return x_open(m, V_exact_path, flags, tf, &vf->xf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int VFile_validate(const VFile* vf)
|
static LibError VFile_validate(const VFile* vf)
|
||||||
{
|
{
|
||||||
// <ofs> doesn't have any invariant we can check.
|
// <ofs> doesn't have any invariant we can check.
|
||||||
RETURN_ERR(x_validate(&vf->xf));
|
RETURN_ERR(x_validate(&vf->xf));
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int VFile_to_string(const VFile* UNUSED(vf), char* buf)
|
static LibError VFile_to_string(const VFile* UNUSED(vf), char* buf)
|
||||||
{
|
{
|
||||||
strcpy(buf, ""); // safe
|
strcpy(buf, ""); // safe
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -456,7 +456,7 @@ Handle vfs_open(const char* v_fn, uint file_flags)
|
|||||||
|
|
||||||
|
|
||||||
// close the handle to a file.
|
// close the handle to a file.
|
||||||
int vfs_close(Handle& hf)
|
LibError vfs_close(Handle& hf)
|
||||||
{
|
{
|
||||||
// h_free already complains on error.
|
// h_free already complains on error.
|
||||||
return h_free(hf, H_VFile);
|
return h_free(hf, H_VFile);
|
||||||
@ -681,7 +681,7 @@ static void VIo_dtor(VIo* vio)
|
|||||||
// doesn't look possible without controlling the AIO implementation:
|
// doesn't look possible without controlling the AIO implementation:
|
||||||
// when we cancel, we can't prevent the app from calling
|
// when we cancel, we can't prevent the app from calling
|
||||||
// aio_result, which would terminate the read.
|
// aio_result, which would terminate the read.
|
||||||
static int VIo_reload(VIo* vio, const char* UNUSED(fn), Handle UNUSED(h))
|
static LibError VIo_reload(VIo* vio, const char* UNUSED(fn), Handle UNUSED(h))
|
||||||
{
|
{
|
||||||
size_t size = vio->size;
|
size_t size = vio->size;
|
||||||
void* buf = vio->buf;
|
void* buf = vio->buf;
|
||||||
@ -693,20 +693,20 @@ static int VIo_reload(VIo* vio, const char* UNUSED(fn), Handle UNUSED(h))
|
|||||||
return x_io_issue(&vf->xf, ofs, size, buf, &vio->xio);
|
return x_io_issue(&vf->xf, ofs, size, buf, &vio->xio);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int VIo_validate(const VIo* vio)
|
static LibError VIo_validate(const VIo* vio)
|
||||||
{
|
{
|
||||||
if(vio->hf < 0)
|
if(vio->hf < 0)
|
||||||
return -200;
|
return ERR_21;
|
||||||
// <size> doesn't have any invariant we can check.
|
// <size> doesn't have any invariant we can check.
|
||||||
if(debug_is_pointer_bogus(vio->buf))
|
if(debug_is_pointer_bogus(vio->buf))
|
||||||
return -201;
|
return ERR_22;
|
||||||
return x_io_validate(&vio->xio);
|
return x_io_validate(&vio->xio);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int VIo_to_string(const VIo* vio, char* buf)
|
static LibError VIo_to_string(const VIo* vio, char* buf)
|
||||||
{
|
{
|
||||||
snprintf(buf, H_STRING_LEN, "buf=%p size=%d", vio->buf, vio->size);
|
snprintf(buf, H_STRING_LEN, "buf=%p size=%d", vio->buf, vio->size);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -722,7 +722,7 @@ Handle vfs_io_issue(Handle hf, size_t size, void* buf)
|
|||||||
|
|
||||||
|
|
||||||
// finished with transfer <hio> - free its buffer (returned by vfs_io_wait)
|
// finished with transfer <hio> - free its buffer (returned by vfs_io_wait)
|
||||||
int vfs_io_discard(Handle& hio)
|
LibError vfs_io_discard(Handle& hio)
|
||||||
{
|
{
|
||||||
return h_free(hio, H_VIo);
|
return h_free(hio, H_VIo);
|
||||||
}
|
}
|
||||||
@ -739,7 +739,7 @@ int vfs_io_has_completed(Handle hio)
|
|||||||
|
|
||||||
// wait until the transfer <hio> completes, and return its buffer.
|
// wait until the transfer <hio> completes, and return its buffer.
|
||||||
// output parameters are zeroed on error.
|
// output parameters are zeroed on error.
|
||||||
int vfs_io_wait(Handle hio, void*& p, size_t& size)
|
LibError vfs_io_wait(Handle hio, void*& p, size_t& size)
|
||||||
{
|
{
|
||||||
H_DEREF(hio, VIo, vio);
|
H_DEREF(hio, VIo, vio);
|
||||||
return x_io_wait(&vio->xio, p, size);
|
return x_io_wait(&vio->xio, p, size);
|
||||||
@ -760,7 +760,7 @@ int vfs_io_wait(Handle hio, void*& p, size_t& size)
|
|||||||
// the mapping will be removed (if still open) when its file is closed.
|
// the mapping will be removed (if still open) when its file is closed.
|
||||||
// however, map/unmap calls should still be paired so that the mapping
|
// however, map/unmap calls should still be paired so that the mapping
|
||||||
// may be removed when no longer needed.
|
// may be removed when no longer needed.
|
||||||
int vfs_map(const Handle hf, const uint UNUSED(flags), void*& p, size_t& size)
|
LibError vfs_map(const Handle hf, const uint UNUSED(flags), void*& p, size_t& size)
|
||||||
{
|
{
|
||||||
p = 0;
|
p = 0;
|
||||||
size = 0;
|
size = 0;
|
||||||
@ -777,7 +777,7 @@ int vfs_map(const Handle hf, const uint UNUSED(flags), void*& p, size_t& size)
|
|||||||
// the mapping will be removed (if still open) when its file is closed.
|
// the mapping will be removed (if still open) when its file is closed.
|
||||||
// however, map/unmap calls should still be paired so that the mapping
|
// however, map/unmap calls should still be paired so that the mapping
|
||||||
// may be removed when no longer needed.
|
// may be removed when no longer needed.
|
||||||
int vfs_unmap(const Handle hf)
|
LibError vfs_unmap(const Handle hf)
|
||||||
{
|
{
|
||||||
H_DEREF(hf, VFile, vf);
|
H_DEREF(hf, VFile, vf);
|
||||||
return x_unmap(&vf->xf);
|
return x_unmap(&vf->xf);
|
||||||
@ -790,7 +790,7 @@ int vfs_unmap(const Handle hf)
|
|||||||
|
|
||||||
// called by vfs_reload and vfs_reload_changed_files (which will already
|
// called by vfs_reload and vfs_reload_changed_files (which will already
|
||||||
// have rebuilt the VFS - doing so more than once a frame is unnecessary).
|
// have rebuilt the VFS - doing so more than once a frame is unnecessary).
|
||||||
static int reload_without_rebuild(const char* fn)
|
static LibError reload_without_rebuild(const char* fn)
|
||||||
{
|
{
|
||||||
// invalidate this file's cached blocks to make sure its contents are
|
// invalidate this file's cached blocks to make sure its contents are
|
||||||
// loaded anew.
|
// loaded anew.
|
||||||
@ -798,12 +798,12 @@ static int reload_without_rebuild(const char* fn)
|
|||||||
|
|
||||||
RETURN_ERR(h_reload(fn));
|
RETURN_ERR(h_reload(fn));
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// called via console command.
|
// called via console command.
|
||||||
int vfs_reload(const char* fn)
|
LibError vfs_reload(const char* fn)
|
||||||
{
|
{
|
||||||
// if <fn> currently maps to an archive, the VFS must switch
|
// if <fn> currently maps to an archive, the VFS must switch
|
||||||
// over to using the loose file (that was presumably changed).
|
// over to using the loose file (that was presumably changed).
|
||||||
@ -816,7 +816,7 @@ int vfs_reload(const char* fn)
|
|||||||
// get directory change notifications, and reload all affected files.
|
// get directory change notifications, and reload all affected files.
|
||||||
// must be called regularly (e.g. once a frame). this is much simpler
|
// must be called regularly (e.g. once a frame). this is much simpler
|
||||||
// than asynchronous notifications: everything would need to be thread-safe.
|
// than asynchronous notifications: everything would need to be thread-safe.
|
||||||
int vfs_reload_changed_files()
|
LibError vfs_reload_changed_files()
|
||||||
{
|
{
|
||||||
// array of reloads requested this frame (see 'do we really need to
|
// array of reloads requested this frame (see 'do we really need to
|
||||||
// reload' below). go through gyrations to avoid heap allocs.
|
// reload' below). go through gyrations to avoid heap allocs.
|
||||||
@ -832,10 +832,10 @@ int vfs_reload_changed_files()
|
|||||||
{
|
{
|
||||||
// get next notification
|
// get next notification
|
||||||
char N_path[PATH_MAX];
|
char N_path[PATH_MAX];
|
||||||
int ret = dir_get_changed_file(N_path);
|
LibError ret = dir_get_changed_file(N_path);
|
||||||
CHECK_ERR(ret); // error? (doesn't cover 'none available')
|
if(ret == ERR_AGAIN) // none available; done.
|
||||||
if(ret != 0) // none available; done.
|
|
||||||
break;
|
break;
|
||||||
|
CHECK_ERR(ret);
|
||||||
|
|
||||||
// convert to VFS path
|
// convert to VFS path
|
||||||
char P_path[PATH_MAX];
|
char P_path[PATH_MAX];
|
||||||
@ -893,7 +893,7 @@ int vfs_reload_changed_files()
|
|||||||
for(uint i = 0; i < num_pending; i++)
|
for(uint i = 0; i < num_pending; i++)
|
||||||
CHECK_ERR(reload_without_rebuild(pending_reloads[i]));
|
CHECK_ERR(reload_without_rebuild(pending_reloads[i]));
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -227,7 +227,7 @@ extern void vfs_path_copy(char* dst, const char* src);
|
|||||||
// if necessary, a directory separator is added between the paths.
|
// if necessary, a directory separator is added between the paths.
|
||||||
// each may be empty, filenames, or full paths.
|
// each may be empty, filenames, or full paths.
|
||||||
// total path length (including '\0') must not exceed VFS_MAX_PATH.
|
// total path length (including '\0') must not exceed VFS_MAX_PATH.
|
||||||
extern int vfs_path_append(char* dst, const char* path1, const char* path2);
|
extern LibError vfs_path_append(char* dst, const char* path1, const char* path2);
|
||||||
|
|
||||||
// VFS paths are of the form: "(dir/)*file?"
|
// VFS paths are of the form: "(dir/)*file?"
|
||||||
// in English: '/' as path separator; trailing '/' required for dir names;
|
// in English: '/' as path separator; trailing '/' required for dir names;
|
||||||
@ -265,10 +265,10 @@ enum VfsMountFlags
|
|||||||
// flags determines extra actions to perform; see VfsMountFlags.
|
// flags determines extra actions to perform; see VfsMountFlags.
|
||||||
//
|
//
|
||||||
// P_real_dir = "." or "./" isn't allowed - see implementation for rationale.
|
// P_real_dir = "." or "./" isn't allowed - see implementation for rationale.
|
||||||
extern int vfs_mount(const char* V_mount_point, const char* P_real_dir, int flags = 0, uint pri = 0);
|
extern LibError vfs_mount(const char* V_mount_point, const char* P_real_dir, int flags = 0, uint pri = 0);
|
||||||
|
|
||||||
// unmount a previously mounted item, and rebuild the VFS afterwards.
|
// unmount a previously mounted item, and rebuild the VFS afterwards.
|
||||||
extern int vfs_unmount(const char* name);
|
extern LibError vfs_unmount(const char* name);
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -281,7 +281,7 @@ extern Handle vfs_dir_open(const char* dir);
|
|||||||
|
|
||||||
// close the handle to a directory.
|
// close the handle to a directory.
|
||||||
// all DirEnt.name strings are now invalid.
|
// all DirEnt.name strings are now invalid.
|
||||||
extern int vfs_dir_close(Handle& hd);
|
extern LibError vfs_dir_close(Handle& hd);
|
||||||
|
|
||||||
// retrieve the next (order is unspecified) dir entry matching <filter>.
|
// retrieve the next (order is unspecified) dir entry matching <filter>.
|
||||||
// return 0 on success, ERR_DIR_END if no matching entry was found,
|
// return 0 on success, ERR_DIR_END if no matching entry was found,
|
||||||
@ -302,7 +302,7 @@ extern int vfs_dir_close(Handle& hd);
|
|||||||
// most callers don't need it and the overhead is considerable
|
// most callers don't need it and the overhead is considerable
|
||||||
// (we'd have to store all entries in a vector). it is left up to
|
// (we'd have to store all entries in a vector). it is left up to
|
||||||
// higher-level code such as VfsUtil.
|
// higher-level code such as VfsUtil.
|
||||||
extern int vfs_dir_next_ent(Handle hd, DirEnt* ent, const char* filter = 0);
|
extern LibError vfs_dir_next_ent(Handle hd, DirEnt* ent, const char* filter = 0);
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -311,14 +311,14 @@ extern int vfs_dir_next_ent(Handle hd, DirEnt* ent, const char* filter = 0);
|
|||||||
|
|
||||||
// return actual path to the specified file:
|
// return actual path to the specified file:
|
||||||
// "<real_directory>/fn" or "<archive_name>/fn".
|
// "<real_directory>/fn" or "<archive_name>/fn".
|
||||||
extern int vfs_realpath(const char* fn, char* realpath);
|
extern LibError vfs_realpath(const char* fn, char* realpath);
|
||||||
|
|
||||||
// does the specified file exist? return false on error.
|
// does the specified file exist? return false on error.
|
||||||
// useful because a "file not found" warning is not raised, unlike vfs_stat.
|
// useful because a "file not found" warning is not raised, unlike vfs_stat.
|
||||||
extern bool vfs_exists(const char* fn);
|
extern bool vfs_exists(const char* fn);
|
||||||
|
|
||||||
// get file status (size, mtime). output param is zeroed on error.
|
// get file status (size, mtime). output param is zeroed on error.
|
||||||
extern int vfs_stat(const char* fn, struct stat*);
|
extern LibError vfs_stat(const char* fn, struct stat*);
|
||||||
|
|
||||||
// return the size of an already opened file, or a negative error code.
|
// return the size of an already opened file, or a negative error code.
|
||||||
extern ssize_t vfs_size(Handle hf);
|
extern ssize_t vfs_size(Handle hf);
|
||||||
@ -329,7 +329,7 @@ extern ssize_t vfs_size(Handle hf);
|
|||||||
extern Handle vfs_open(const char* fn, uint flags = 0);
|
extern Handle vfs_open(const char* fn, uint flags = 0);
|
||||||
|
|
||||||
// close the handle to a file.
|
// close the handle to a file.
|
||||||
extern int vfs_close(Handle& h);
|
extern LibError vfs_close(Handle& h);
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -348,10 +348,10 @@ extern int vfs_io_has_completed(Handle hio);
|
|||||||
|
|
||||||
// wait until the transfer <hio> completes, and return its buffer.
|
// wait until the transfer <hio> completes, and return its buffer.
|
||||||
// output parameters are zeroed on error.
|
// output parameters are zeroed on error.
|
||||||
extern int vfs_io_wait(Handle hio, void*& p, size_t& size);
|
extern LibError vfs_io_wait(Handle hio, void*& p, size_t& size);
|
||||||
|
|
||||||
// finished with transfer <hio> - free its buffer (returned by vfs_wait_read).
|
// finished with transfer <hio> - free its buffer (returned by vfs_wait_read).
|
||||||
extern int vfs_io_discard(Handle& hio);
|
extern LibError vfs_io_discard(Handle& hio);
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -414,7 +414,7 @@ extern ssize_t vfs_store(const char* fn, void* p, size_t size, uint flags = 0);
|
|||||||
// the mapping will be removed (if still open) when its file is closed.
|
// the mapping will be removed (if still open) when its file is closed.
|
||||||
// however, map/unmap calls should still be paired so that the mapping
|
// however, map/unmap calls should still be paired so that the mapping
|
||||||
// may be removed when no longer needed.
|
// may be removed when no longer needed.
|
||||||
extern int vfs_map(Handle hf, uint flags, void*& p, size_t& size);
|
extern LibError vfs_map(Handle hf, uint flags, void*& p, size_t& size);
|
||||||
|
|
||||||
// decrement the reference count for the mapping belonging to file <f>.
|
// decrement the reference count for the mapping belonging to file <f>.
|
||||||
// fail if there are no references; remove the mapping if the count reaches 0.
|
// fail if there are no references; remove the mapping if the count reaches 0.
|
||||||
@ -422,16 +422,16 @@ extern int vfs_map(Handle hf, uint flags, void*& p, size_t& size);
|
|||||||
// the mapping will be removed (if still open) when its file is closed.
|
// the mapping will be removed (if still open) when its file is closed.
|
||||||
// however, map/unmap calls should still be paired so that the mapping
|
// however, map/unmap calls should still be paired so that the mapping
|
||||||
// may be removed when no longer needed.
|
// may be removed when no longer needed.
|
||||||
extern int vfs_unmap(Handle hf);
|
extern LibError vfs_unmap(Handle hf);
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// hotloading
|
// hotloading
|
||||||
//
|
//
|
||||||
|
|
||||||
extern int vfs_reload(const char* fn);
|
extern LibError vfs_reload(const char* fn);
|
||||||
|
|
||||||
// this must be called from the main thread? (wdir_watch problem)
|
// this must be called from the main thread? (wdir_watch problem)
|
||||||
extern int vfs_reload_changed_files(void);
|
extern LibError vfs_reload_changed_files(void);
|
||||||
|
|
||||||
#endif // #ifndef __VFS_H__
|
#endif // #ifndef __VFS_H__
|
||||||
|
@ -184,7 +184,7 @@ private:
|
|||||||
// we get the full path, since that's what is stored in Zip archives.
|
// we get the full path, since that's what is stored in Zip archives.
|
||||||
//
|
//
|
||||||
// [total time 21ms, with ~2000 file's (includes add_file cost)]
|
// [total time 21ms, with ~2000 file's (includes add_file cost)]
|
||||||
static int zip_cb(const char* path, const struct stat* s, uintptr_t user)
|
static LibError zip_cb(const char* path, const struct stat* s, uintptr_t user)
|
||||||
{
|
{
|
||||||
CHECK_PATH(path);
|
CHECK_PATH(path);
|
||||||
|
|
||||||
@ -228,7 +228,8 @@ static int zip_cb(const char* path, const struct stat* s, uintptr_t user)
|
|||||||
last_td = td;
|
last_td = td;
|
||||||
}
|
}
|
||||||
|
|
||||||
return tree_add_file(td, fn, m, s->st_size, s->st_mtime);
|
WARN_ERR(tree_add_file(td, fn, m, s->st_size, s->st_mtime));
|
||||||
|
return INFO_CB_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -242,48 +243,46 @@ static bool archive_less(Handle hza1, Handle hza2)
|
|||||||
typedef std::vector<Handle> Archives;
|
typedef std::vector<Handle> Archives;
|
||||||
typedef Archives::const_iterator ArchiveCIt;
|
typedef Archives::const_iterator ArchiveCIt;
|
||||||
|
|
||||||
static int enqueue_archive(const char* name, const char* P_archive_dir, Archives* archives)
|
// return value is ERR_OK iff archives != 0 and name is a valid archive that
|
||||||
|
// was successfully added to the list. see comments below.
|
||||||
|
static LibError enqueue_archive(const char* name, const char* P_archive_dir, Archives* archives)
|
||||||
{
|
{
|
||||||
// caller wants us to check if this is a Zip file and if so, append it to
|
// caller doesn't want us to check if this is a Zip file. this is the
|
||||||
// a list. this is only done in the mounted directory itself, not its
|
// case in all subdirectories of the mount point, since checking for all
|
||||||
// subdirectories! see mount_dir_tree.
|
// mounted files would be slow. see mount_dir_tree.
|
||||||
// the archives will be mounted after regular directory mounts are done.
|
if(!archives)
|
||||||
if(archives)
|
return INFO_SKIPPED;
|
||||||
{
|
|
||||||
// get complete path for zip_archive_open.
|
|
||||||
// this doesn't (need to) work for subdirectories of the mounted td!
|
|
||||||
// we can't use mount_get_path because we don't have the VFS path.
|
|
||||||
char P_path[PATH_MAX];
|
|
||||||
vfs_path_append(P_path, P_archive_dir, name);
|
|
||||||
|
|
||||||
// just open the Zip file and see if it's valid. we don't bother
|
// get complete path for zip_archive_open.
|
||||||
// checking the extension because archives won't necessarily be
|
// this doesn't (need to) work for subdirectories of the mounted td!
|
||||||
// called .zip (e.g. Quake III .pk3).
|
// we can't use mount_get_path because we don't have the VFS path.
|
||||||
Handle archive = zip_archive_open(P_path);
|
char P_path[PATH_MAX];
|
||||||
if(archive > 0)
|
RETURN_ERR(vfs_path_append(P_path, P_archive_dir, name));
|
||||||
{
|
|
||||||
archives->push_back(archive);
|
|
||||||
|
|
||||||
// avoid also adding the Zip file itself to <td>.
|
// just open the Zip file and see if it's valid. we don't bother
|
||||||
return 0;
|
// checking the extension because archives won't necessarily be
|
||||||
}
|
// called .zip (e.g. Quake III .pk3).
|
||||||
}
|
Handle archive = zip_archive_open(P_path);
|
||||||
|
RETURN_ERR(archive);
|
||||||
|
archives->push_back(archive);
|
||||||
|
|
||||||
return 1;
|
// avoid also adding the Zip file itself to <td>.
|
||||||
|
// (when caller sees ERR_OK, they skip the file)
|
||||||
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mount_archive(TDir* td, const Mount& m)
|
static LibError mount_archive(TDir* td, const Mount& m)
|
||||||
{
|
{
|
||||||
ZipCBParams params(td, &m);
|
ZipCBParams params(td, &m);
|
||||||
zip_enum(m.archive, zip_cb, (uintptr_t)¶ms);
|
zip_enum(m.archive, zip_cb, (uintptr_t)¶ms);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mount_archives(TDir* td, Archives* archives, const Mount* mount)
|
static LibError mount_archives(TDir* td, Archives* archives, const Mount* mount)
|
||||||
{
|
{
|
||||||
// VFS_MOUNT_ARCHIVES flag wasn't set, or no archives present
|
// VFS_MOUNT_ARCHIVES flag wasn't set, or no archives present
|
||||||
if(archives->empty())
|
if(archives->empty())
|
||||||
return 0;
|
return ERR_OK;
|
||||||
|
|
||||||
std::sort(archives->begin(), archives->end(), archive_less);
|
std::sort(archives->begin(), archives->end(), archive_less);
|
||||||
|
|
||||||
@ -298,7 +297,7 @@ static int mount_archives(TDir* td, Archives* archives, const Mount* mount)
|
|||||||
mount_archive(td, m);
|
mount_archive(td, m);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -323,12 +322,12 @@ typedef std::deque<TDirAndPath> DirQueue;
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
static int enqueue_dir(TDir* parent_td, const char* name,
|
static LibError enqueue_dir(TDir* parent_td, const char* name,
|
||||||
const char* P_parent_path, DirQueue* dir_queue)
|
const char* P_parent_path, DirQueue* dir_queue)
|
||||||
{
|
{
|
||||||
// caller doesn't want us to enqueue subdirectories; bail.
|
// caller doesn't want us to enqueue subdirectories; bail.
|
||||||
if(!dir_queue)
|
if(!dir_queue)
|
||||||
return 0;
|
return ERR_OK;
|
||||||
|
|
||||||
// skip versioning system directories - this avoids cluttering the
|
// skip versioning system directories - this avoids cluttering the
|
||||||
// VFS with hundreds of irrelevant files.
|
// VFS with hundreds of irrelevant files.
|
||||||
@ -336,7 +335,7 @@ static int enqueue_dir(TDir* parent_td, const char* name,
|
|||||||
// strstr the entire path) and it is assumed the Zip file builder
|
// strstr the entire path) and it is assumed the Zip file builder
|
||||||
// will take care of it.
|
// will take care of it.
|
||||||
if(!strcmp(name, "CVS") || !strcmp(name, ".svn"))
|
if(!strcmp(name, "CVS") || !strcmp(name, ".svn"))
|
||||||
return 0;
|
return ERR_OK;
|
||||||
|
|
||||||
// prepend parent path to get complete pathname.
|
// prepend parent path to get complete pathname.
|
||||||
char P_path[PATH_MAX];
|
char P_path[PATH_MAX];
|
||||||
@ -347,7 +346,7 @@ static int enqueue_dir(TDir* parent_td, const char* name,
|
|||||||
CHECK_ERR(tree_add_dir(parent_td, name, &td));
|
CHECK_ERR(tree_add_dir(parent_td, name, &td));
|
||||||
// .. and add it to the list of directories to visit.
|
// .. and add it to the list of directories to visit.
|
||||||
dir_queue->push_back(TDirAndPath(td, const_cast<const char*>(P_path)));
|
dir_queue->push_back(TDirAndPath(td, const_cast<const char*>(P_path)));
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -370,7 +369,7 @@ static int enqueue_dir(TDir* parent_td, const char* name,
|
|||||||
// m - real td's location; assigned to all files added from this mounting
|
// m - real td's location; assigned to all files added from this mounting
|
||||||
// archives - if the dirent is an archive, its Mount is added here.
|
// archives - if the dirent is an archive, its Mount is added here.
|
||||||
|
|
||||||
static int add_ent(TDir* td, DirEnt* ent, const char* P_parent_path, const Mount* m,
|
static LibError add_ent(TDir* td, DirEnt* ent, const char* P_parent_path, const Mount* m,
|
||||||
DirQueue* dir_queue, Archives* archives)
|
DirQueue* dir_queue, Archives* archives)
|
||||||
{
|
{
|
||||||
const char* name = ent->name;
|
const char* name = ent->name;
|
||||||
@ -381,8 +380,8 @@ static int add_ent(TDir* td, DirEnt* ent, const char* P_parent_path, const Mount
|
|||||||
// else: it's a file (dir_next_ent discards everything except for
|
// else: it's a file (dir_next_ent discards everything except for
|
||||||
// file and subdirectory entries).
|
// file and subdirectory entries).
|
||||||
|
|
||||||
if(enqueue_archive(name, m->P_name.c_str(), archives) == 0)
|
if(enqueue_archive(name, m->P_name.c_str(), archives) == ERR_OK)
|
||||||
return 0;
|
return ERR_OK;
|
||||||
|
|
||||||
// it's a regular data file; add it to the directory.
|
// it's a regular data file; add it to the directory.
|
||||||
return tree_add_file(td, name, m, ent->size, ent->mtime);
|
return tree_add_file(td, name, m, ent->size, ent->mtime);
|
||||||
@ -390,10 +389,10 @@ static int add_ent(TDir* td, DirEnt* ent, const char* P_parent_path, const Mount
|
|||||||
|
|
||||||
|
|
||||||
// note: full path is needed for the dir watch.
|
// note: full path is needed for the dir watch.
|
||||||
static int populate_dir(TDir* td, const char* P_path, const Mount* m,
|
static LibError populate_dir(TDir* td, const char* P_path, const Mount* m,
|
||||||
DirQueue* dir_queue, Archives* archives, int flags)
|
DirQueue* dir_queue, Archives* archives, int flags)
|
||||||
{
|
{
|
||||||
int err;
|
LibError err;
|
||||||
|
|
||||||
RealDir* rd = tree_get_real_dir(td);
|
RealDir* rd = tree_get_real_dir(td);
|
||||||
WARN_ERR(mount_attach_real_dir(rd, P_path, m, flags));
|
WARN_ERR(mount_attach_real_dir(rd, P_path, m, flags));
|
||||||
@ -406,7 +405,7 @@ static int populate_dir(TDir* td, const char* P_path, const Mount* m,
|
|||||||
{
|
{
|
||||||
// don't RETURN_ERR since we need to close d.
|
// don't RETURN_ERR since we need to close d.
|
||||||
err = dir_next_ent(&d, &ent);
|
err = dir_next_ent(&d, &ent);
|
||||||
if(err != 0)
|
if(err != ERR_OK)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
err = add_ent(td, &ent, P_path, m, dir_queue, archives);
|
err = add_ent(td, &ent, P_path, m, dir_queue, archives);
|
||||||
@ -414,7 +413,7 @@ static int populate_dir(TDir* td, const char* P_path, const Mount* m,
|
|||||||
}
|
}
|
||||||
|
|
||||||
WARN_ERR(dir_close(&d));
|
WARN_ERR(dir_close(&d));
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -430,9 +429,9 @@ static int populate_dir(TDir* td, const char* P_path, const Mount* m,
|
|||||||
// note: we are only able to add archives found in the root directory,
|
// note: we are only able to add archives found in the root directory,
|
||||||
// due to dirent_cb implementation. that's ok - we don't want to check
|
// due to dirent_cb implementation. that's ok - we don't want to check
|
||||||
// every single file to see if it's an archive (slow!).
|
// every single file to see if it's an archive (slow!).
|
||||||
static int mount_dir_tree(TDir* td, const Mount& m)
|
static LibError mount_dir_tree(TDir* td, const Mount& m)
|
||||||
{
|
{
|
||||||
int err = 0;
|
LibError err = ERR_OK;
|
||||||
|
|
||||||
// add_ent fills these queues with dirs/archives if the corresponding
|
// add_ent fills these queues with dirs/archives if the corresponding
|
||||||
// flags are set.
|
// flags are set.
|
||||||
@ -454,8 +453,8 @@ static int mount_dir_tree(TDir* td, const Mount& m)
|
|||||||
TDir* const td = dir_queue.front().td;
|
TDir* const td = dir_queue.front().td;
|
||||||
const char* P_path = dir_queue.front().path.c_str();
|
const char* P_path = dir_queue.front().path.c_str();
|
||||||
|
|
||||||
int ret = populate_dir(td, P_path, &m, pdir_queue, parchives, m.flags);
|
LibError ret = populate_dir(td, P_path, &m, pdir_queue, parchives, m.flags);
|
||||||
if(!err)
|
if(err == ERR_OK)
|
||||||
err = ret;
|
err = ret;
|
||||||
|
|
||||||
// prevent searching for archives in subdirectories (slow!). this
|
// prevent searching for archives in subdirectories (slow!). this
|
||||||
@ -470,7 +469,7 @@ static int mount_dir_tree(TDir* td, const Mount& m)
|
|||||||
// do not pass parchives because that has been set to 0!
|
// do not pass parchives because that has been set to 0!
|
||||||
mount_archives(td, &archives, &m);
|
mount_archives(td, &archives, &m);
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -531,7 +530,7 @@ static const Mount& add_mount(const char* V_mount_point, const char* P_real_path
|
|||||||
|
|
||||||
// note: this is not a member function of Mount to avoid having to
|
// note: this is not a member function of Mount to avoid having to
|
||||||
// forward-declare mount_archive, mount_dir_tree.
|
// forward-declare mount_archive, mount_dir_tree.
|
||||||
static int remount(const Mount& m)
|
static LibError remount(const Mount& m)
|
||||||
{
|
{
|
||||||
TDir* td;
|
TDir* td;
|
||||||
CHECK_ERR(tree_lookup_dir(m.V_mount_point.c_str(), &td, LF_CREATE_MISSING));
|
CHECK_ERR(tree_lookup_dir(m.V_mount_point.c_str(), &td, LF_CREATE_MISSING));
|
||||||
@ -571,7 +570,7 @@ static inline void remount_all()
|
|||||||
// flags determines extra actions to perform; see VfsMountFlags.
|
// flags determines extra actions to perform; see VfsMountFlags.
|
||||||
//
|
//
|
||||||
// P_real_path = "." or "./" isn't allowed - see implementation for rationale.
|
// P_real_path = "." or "./" isn't allowed - see implementation for rationale.
|
||||||
int vfs_mount(const char* V_mount_point, const char* P_real_path, int flags, uint pri)
|
LibError vfs_mount(const char* V_mount_point, const char* P_real_path, int flags, uint pri)
|
||||||
{
|
{
|
||||||
// callers have a tendency to forget required trailing '/';
|
// callers have a tendency to forget required trailing '/';
|
||||||
// complain if it's not there, unless path = "" (root td).
|
// complain if it's not there, unless path = "" (root td).
|
||||||
@ -590,20 +589,14 @@ int vfs_mount(const char* V_mount_point, const char* P_real_path, int flags, uin
|
|||||||
for(MountIt it = mounts.begin(); it != mounts.end(); ++it)
|
for(MountIt it = mounts.begin(); it != mounts.end(); ++it)
|
||||||
{
|
{
|
||||||
if(file_is_subpath(P_real_path, it->P_name.c_str()))
|
if(file_is_subpath(P_real_path, it->P_name.c_str()))
|
||||||
{
|
CHECK_ERR(ERR_ALREADY_MOUNTED);
|
||||||
debug_warn("already mounted");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// disallow "." because "./" isn't supported on Windows.
|
// disallow "." because "./" isn't supported on Windows.
|
||||||
// it would also create a loophole for the parent td check above.
|
// it would also create a loophole for the parent td check above.
|
||||||
// "./" and "/." are caught by CHECK_PATH.
|
// "./" and "/." are caught by CHECK_PATH.
|
||||||
if(!strcmp(P_real_path, "."))
|
if(!strcmp(P_real_path, "."))
|
||||||
{
|
CHECK_ERR(ERR_PATH_INVALID);
|
||||||
debug_warn("mounting . not allowed");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Mount& m = add_mount(V_mount_point, P_real_path, 0, flags, pri);
|
const Mount& m = add_mount(V_mount_point, P_real_path, 0, flags, pri);
|
||||||
return remount(m);
|
return remount(m);
|
||||||
@ -616,17 +609,17 @@ int vfs_mount(const char* V_mount_point, const char* P_real_path, int flags, uin
|
|||||||
// dir_watch reports changes; can also be called from the console after a
|
// dir_watch reports changes; can also be called from the console after a
|
||||||
// rebuild command. there is no provision for updating single VFS dirs -
|
// rebuild command. there is no provision for updating single VFS dirs -
|
||||||
// it's not worth the trouble.
|
// it's not worth the trouble.
|
||||||
int mount_rebuild()
|
LibError mount_rebuild()
|
||||||
{
|
{
|
||||||
tree_clear();
|
tree_clear();
|
||||||
tree_init();
|
tree_init();
|
||||||
remount_all();
|
remount_all();
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// unmount a previously mounted item, and rebuild the VFS afterwards.
|
// unmount a previously mounted item, and rebuild the VFS afterwards.
|
||||||
int vfs_unmount(const char* P_name)
|
LibError vfs_unmount(const char* P_name)
|
||||||
{
|
{
|
||||||
// this removes all Mounts ensuing from the given mounting. their dtors
|
// this removes all Mounts ensuing from the given mounting. their dtors
|
||||||
// free all resources and there's no need to remove the files from
|
// free all resources and there's no need to remove the files from
|
||||||
@ -652,7 +645,7 @@ int vfs_unmount(const char* P_name)
|
|||||||
// if <path> or its ancestors are mounted,
|
// if <path> or its ancestors are mounted,
|
||||||
// return a VFS path that accesses it.
|
// return a VFS path that accesses it.
|
||||||
// used when receiving paths from external code.
|
// used when receiving paths from external code.
|
||||||
int mount_make_vfs_path(const char* P_path, char* V_path)
|
LibError mount_make_vfs_path(const char* P_path, char* V_path)
|
||||||
{
|
{
|
||||||
for(MountIt it = mounts.begin(); it != mounts.end(); ++it)
|
for(MountIt it = mounts.begin(); it != mounts.end(); ++it)
|
||||||
{
|
{
|
||||||
@ -664,10 +657,10 @@ int mount_make_vfs_path(const char* P_path, char* V_path)
|
|||||||
const char* replace = m.V_mount_point.c_str();
|
const char* replace = m.V_mount_point.c_str();
|
||||||
|
|
||||||
if(path_replace(V_path, P_path, remove, replace) == 0)
|
if(path_replace(V_path, P_path, remove, replace) == 0)
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return ERR_PATH_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -688,7 +681,7 @@ void mount_shutdown()
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
int mount_attach_real_dir(RealDir* rd, const char* P_path, const Mount* m, int flags)
|
LibError mount_attach_real_dir(RealDir* rd, const char* P_path, const Mount* m, int flags)
|
||||||
{
|
{
|
||||||
// more than one real dir mounted into VFS dir
|
// more than one real dir mounted into VFS dir
|
||||||
// (=> can't create files for writing here)
|
// (=> can't create files for writing here)
|
||||||
@ -706,7 +699,7 @@ int mount_attach_real_dir(RealDir* rd, const char* P_path, const Mount* m, int f
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -722,11 +715,11 @@ void mount_detach_real_dir(RealDir* rd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int mount_populate(TDir* td, RealDir* rd)
|
LibError mount_populate(TDir* td, RealDir* rd)
|
||||||
{
|
{
|
||||||
UNUSED2(td);
|
UNUSED2(td);
|
||||||
UNUSED2(rd);
|
UNUSED2(rd);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -745,7 +738,7 @@ int mount_populate(TDir* td, RealDir* rd)
|
|||||||
|
|
||||||
// given a Mount, return the actual location (portable path) of
|
// given a Mount, return the actual location (portable path) of
|
||||||
// <V_path>. used by vfs_realpath and VFile_reopen.
|
// <V_path>. used by vfs_realpath and VFile_reopen.
|
||||||
int x_realpath(const Mount* m, const char* V_exact_path, char* P_real_path)
|
LibError x_realpath(const Mount* m, const char* V_exact_path, char* P_real_path)
|
||||||
{
|
{
|
||||||
const char* P_parent_path = 0;
|
const char* P_parent_path = 0;
|
||||||
|
|
||||||
@ -758,8 +751,7 @@ int x_realpath(const Mount* m, const char* V_exact_path, char* P_real_path)
|
|||||||
P_parent_path = m->P_name.c_str();
|
P_parent_path = m->P_name.c_str();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
debug_warn("invalid type");
|
WARN_RETURN(ERR_INVALID_MOUNT_TYPE);
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* remove = m->V_mount_point.c_str();
|
const char* remove = m->V_mount_point.c_str();
|
||||||
@ -769,7 +761,7 @@ int x_realpath(const Mount* m, const char* V_exact_path, char* P_real_path)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
int x_open(const Mount* m, const char* V_exact_path, int flags, TFile* tf, XFile* xf)
|
LibError x_open(const Mount* m, const char* V_exact_path, int flags, TFile* tf, XFile* xf)
|
||||||
{
|
{
|
||||||
// declare variables used in the switch below to avoid needing {}.
|
// declare variables used in the switch below to avoid needing {}.
|
||||||
char P_path[PATH_MAX];
|
char P_path[PATH_MAX];
|
||||||
@ -780,7 +772,7 @@ int x_open(const Mount* m, const char* V_exact_path, int flags, TFile* tf, XFile
|
|||||||
if(flags & FILE_WRITE)
|
if(flags & FILE_WRITE)
|
||||||
{
|
{
|
||||||
debug_warn("requesting write access to file in archive");
|
debug_warn("requesting write access to file in archive");
|
||||||
return -1;
|
return ERR_NOT_IMPLEMENTED;
|
||||||
}
|
}
|
||||||
RETURN_ERR(zip_open(m->archive, V_exact_path, flags, &xf->u.zf));
|
RETURN_ERR(zip_open(m->archive, V_exact_path, flags, &xf->u.zf));
|
||||||
break;
|
break;
|
||||||
@ -789,8 +781,7 @@ int x_open(const Mount* m, const char* V_exact_path, int flags, TFile* tf, XFile
|
|||||||
RETURN_ERR(file_open(P_path, flags, &xf->u.f));
|
RETURN_ERR(file_open(P_path, flags, &xf->u.f));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
debug_warn("invalid type");
|
WARN_RETURN(ERR_INVALID_MOUNT_TYPE);
|
||||||
return ERR_CORRUPTED;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// success
|
// success
|
||||||
@ -798,17 +789,17 @@ int x_open(const Mount* m, const char* V_exact_path, int flags, TFile* tf, XFile
|
|||||||
// false impression that all is well.
|
// false impression that all is well.
|
||||||
xf->type = m->type;
|
xf->type = m->type;
|
||||||
xf->tf = tf;
|
xf->tf = tf;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int x_close(XFile* xf)
|
LibError x_close(XFile* xf)
|
||||||
{
|
{
|
||||||
switch(xf->type)
|
switch(xf->type)
|
||||||
{
|
{
|
||||||
// no file open (e.g. because x_open failed) -> nothing to do.
|
// no file open (e.g. because x_open failed) -> nothing to do.
|
||||||
case MT_NONE:
|
case MT_NONE:
|
||||||
return 0;
|
return ERR_OK;
|
||||||
|
|
||||||
case MT_ARCHIVE:
|
case MT_ARCHIVE:
|
||||||
(void)zip_close(&xf->u.zf);
|
(void)zip_close(&xf->u.zf);
|
||||||
@ -817,8 +808,7 @@ int x_close(XFile* xf)
|
|||||||
(void)file_close(&xf->u.f);
|
(void)file_close(&xf->u.f);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
debug_warn("invalid type");
|
WARN_RETURN(ERR_INVALID_MOUNT_TYPE);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// update file state in VFS tree
|
// update file state in VFS tree
|
||||||
@ -827,22 +817,22 @@ int x_close(XFile* xf)
|
|||||||
tree_update_file(xf->tf, xf->u.f.size, time(0)); // can't fail
|
tree_update_file(xf->tf, xf->u.f.size, time(0)); // can't fail
|
||||||
|
|
||||||
xf->type = MT_NONE;
|
xf->type = MT_NONE;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int x_validate(const XFile* xf)
|
LibError x_validate(const XFile* xf)
|
||||||
{
|
{
|
||||||
switch(xf->type)
|
switch(xf->type)
|
||||||
{
|
{
|
||||||
case MT_NONE:
|
case MT_NONE:
|
||||||
if(xf->tf != 0)
|
if(xf->tf != 0)
|
||||||
return -100;
|
return ERR_11;
|
||||||
return 0; // ok, nothing else to check
|
return ERR_OK; // ok, nothing else to check
|
||||||
|
|
||||||
case MT_FILE:
|
case MT_FILE:
|
||||||
if(xf->tf == 0)
|
if(xf->tf == 0)
|
||||||
return -101;
|
return ERR_12;
|
||||||
return file_validate(&xf->u.f);
|
return file_validate(&xf->u.f);
|
||||||
|
|
||||||
case MT_ARCHIVE:
|
case MT_ARCHIVE:
|
||||||
@ -850,11 +840,11 @@ int x_validate(const XFile* xf)
|
|||||||
// VFS after newly written files are closed, but archive files
|
// VFS after newly written files are closed, but archive files
|
||||||
// cannot be modified), but it's not ATM.
|
// cannot be modified), but it's not ATM.
|
||||||
if(xf->tf == 0)
|
if(xf->tf == 0)
|
||||||
return -102;
|
return ERR_13;
|
||||||
return zip_validate(&xf->u.zf);
|
return zip_validate(&xf->u.zf);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return -103; // invalid type
|
return ERR_INVALID_MOUNT_TYPE;
|
||||||
}
|
}
|
||||||
UNREACHABLE;
|
UNREACHABLE;
|
||||||
}
|
}
|
||||||
@ -891,7 +881,7 @@ uint x_flags(const XFile* xf)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
int x_io(XFile* xf, off_t ofs, size_t size, void* buf, FileIOCB cb, uintptr_t ctx)
|
ssize_t x_io(XFile* xf, off_t ofs, size_t size, void* buf, FileIOCB cb, uintptr_t ctx)
|
||||||
{
|
{
|
||||||
switch(xf->type)
|
switch(xf->type)
|
||||||
{
|
{
|
||||||
@ -906,13 +896,12 @@ int x_io(XFile* xf, off_t ofs, size_t size, void* buf, FileIOCB cb, uintptr_t ct
|
|||||||
return file_io(&xf->u.f, ofs, size, buf, cb, ctx);
|
return file_io(&xf->u.f, ofs, size, buf, cb, ctx);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
debug_warn("invalid file type");
|
WARN_RETURN(ERR_INVALID_MOUNT_TYPE);
|
||||||
return ERR_CORRUPTED;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int x_map(XFile* xf, void*& p, size_t& size)
|
LibError x_map(XFile* xf, void*& p, size_t& size)
|
||||||
{
|
{
|
||||||
switch(xf->type)
|
switch(xf->type)
|
||||||
{
|
{
|
||||||
@ -921,13 +910,12 @@ int x_map(XFile* xf, void*& p, size_t& size)
|
|||||||
case MT_FILE:
|
case MT_FILE:
|
||||||
return file_map(&xf->u.f, p, size);
|
return file_map(&xf->u.f, p, size);
|
||||||
default:
|
default:
|
||||||
debug_warn("invalid file type");
|
WARN_RETURN(ERR_INVALID_MOUNT_TYPE);
|
||||||
return ERR_CORRUPTED;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int x_unmap(XFile* xf)
|
LibError x_unmap(XFile* xf)
|
||||||
{
|
{
|
||||||
switch(xf->type)
|
switch(xf->type)
|
||||||
{
|
{
|
||||||
@ -936,13 +924,12 @@ int x_unmap(XFile* xf)
|
|||||||
case MT_FILE:
|
case MT_FILE:
|
||||||
return file_unmap(&xf->u.f);
|
return file_unmap(&xf->u.f);
|
||||||
default:
|
default:
|
||||||
debug_warn("invalid file type");
|
WARN_RETURN(ERR_INVALID_MOUNT_TYPE);
|
||||||
return ERR_CORRUPTED;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int x_io_issue(XFile* xf, off_t ofs, size_t size, void* buf, XIo* xio)
|
LibError x_io_issue(XFile* xf, off_t ofs, size_t size, void* buf, XIo* xio)
|
||||||
{
|
{
|
||||||
xio->type = xf->type;
|
xio->type = xf->type;
|
||||||
switch(xio->type)
|
switch(xio->type)
|
||||||
@ -952,8 +939,7 @@ int x_io_issue(XFile* xf, off_t ofs, size_t size, void* buf, XIo* xio)
|
|||||||
case MT_FILE:
|
case MT_FILE:
|
||||||
return file_io_issue(&xf->u.f, ofs, size, buf, &xio->u.fio);
|
return file_io_issue(&xf->u.f, ofs, size, buf, &xio->u.fio);
|
||||||
default:
|
default:
|
||||||
debug_warn("invalid file type");
|
WARN_RETURN(ERR_INVALID_MOUNT_TYPE);
|
||||||
return ERR_CORRUPTED;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -967,13 +953,12 @@ int x_io_has_completed(XIo* xio)
|
|||||||
case MT_FILE:
|
case MT_FILE:
|
||||||
return file_io_has_completed(&xio->u.fio);
|
return file_io_has_completed(&xio->u.fio);
|
||||||
default:
|
default:
|
||||||
debug_warn("invalid file type");
|
WARN_RETURN(ERR_INVALID_MOUNT_TYPE);
|
||||||
return ERR_CORRUPTED;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int x_io_wait(XIo* xio, void*& p, size_t& size)
|
LibError x_io_wait(XIo* xio, void*& p, size_t& size)
|
||||||
{
|
{
|
||||||
switch(xio->type)
|
switch(xio->type)
|
||||||
{
|
{
|
||||||
@ -982,13 +967,12 @@ int x_io_wait(XIo* xio, void*& p, size_t& size)
|
|||||||
case MT_FILE:
|
case MT_FILE:
|
||||||
return file_io_wait(&xio->u.fio, p, size);
|
return file_io_wait(&xio->u.fio, p, size);
|
||||||
default:
|
default:
|
||||||
debug_warn("invalid file type");
|
WARN_RETURN(ERR_INVALID_MOUNT_TYPE);
|
||||||
return ERR_CORRUPTED;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int x_io_discard(XIo* xio)
|
LibError x_io_discard(XIo* xio)
|
||||||
{
|
{
|
||||||
switch(xio->type)
|
switch(xio->type)
|
||||||
{
|
{
|
||||||
@ -997,13 +981,12 @@ int x_io_discard(XIo* xio)
|
|||||||
case MT_FILE:
|
case MT_FILE:
|
||||||
return file_io_discard(&xio->u.fio);
|
return file_io_discard(&xio->u.fio);
|
||||||
default:
|
default:
|
||||||
debug_warn("invalid file type");
|
WARN_RETURN(ERR_INVALID_MOUNT_TYPE);
|
||||||
return ERR_CORRUPTED;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int x_io_validate(const XIo* xio)
|
LibError x_io_validate(const XIo* xio)
|
||||||
{
|
{
|
||||||
switch(xio->type)
|
switch(xio->type)
|
||||||
{
|
{
|
||||||
@ -1012,7 +995,7 @@ int x_io_validate(const XIo* xio)
|
|||||||
case MT_FILE:
|
case MT_FILE:
|
||||||
return file_io_validate(&xio->u.fio);
|
return file_io_validate(&xio->u.fio);
|
||||||
default:
|
default:
|
||||||
return -100; // invalid type
|
return ERR_INVALID_MOUNT_TYPE;
|
||||||
}
|
}
|
||||||
UNREACHABLE;
|
UNREACHABLE;
|
||||||
}
|
}
|
@ -57,28 +57,28 @@ struct XFile
|
|||||||
|
|
||||||
// given a Mount, return the actual location (portable path) of
|
// given a Mount, return the actual location (portable path) of
|
||||||
// <V_path>. used by vfs_realpath and VFile_reopen.
|
// <V_path>. used by vfs_realpath and VFile_reopen.
|
||||||
extern int x_realpath(const Mount* m, const char* V_exact_path, char* P_real_path);
|
extern LibError x_realpath(const Mount* m, const char* V_exact_path, char* P_real_path);
|
||||||
|
|
||||||
extern int x_open(const Mount* m, const char* V_exact_path, int flags, TFile* tf, XFile* xf);
|
extern LibError x_open(const Mount* m, const char* V_exact_path, int flags, TFile* tf, XFile* xf);
|
||||||
extern int x_close(XFile* xf);
|
extern LibError x_close(XFile* xf);
|
||||||
|
|
||||||
extern int x_validate(const XFile* xf);
|
extern LibError x_validate(const XFile* xf);
|
||||||
|
|
||||||
extern bool x_is_open(const XFile* xf);
|
extern bool x_is_open(const XFile* xf);
|
||||||
extern off_t x_size(const XFile* xf);
|
extern off_t x_size(const XFile* xf);
|
||||||
extern uint x_flags(const XFile* xf);
|
extern uint x_flags(const XFile* xf);
|
||||||
extern void x_set_flags(XFile* xf, uint flags);
|
extern void x_set_flags(XFile* xf, uint flags);
|
||||||
|
|
||||||
extern int x_io(XFile* xf, off_t ofs, size_t size, void* buf, FileIOCB cb, uintptr_t ctx);;
|
extern ssize_t x_io(XFile* xf, off_t ofs, size_t size, void* buf, FileIOCB cb, uintptr_t ctx);;
|
||||||
|
|
||||||
extern int x_map(XFile* xf, void*& p, size_t& size);
|
extern LibError x_map(XFile* xf, void*& p, size_t& size);
|
||||||
extern int x_unmap(XFile* xf);
|
extern LibError x_unmap(XFile* xf);
|
||||||
|
|
||||||
extern int x_io_issue(XFile* xf, off_t ofs, size_t size, void* buf, XIo* xio);
|
extern LibError x_io_issue(XFile* xf, off_t ofs, size_t size, void* buf, XIo* xio);
|
||||||
extern int x_io_has_completed(XIo* xio);
|
extern int x_io_has_completed(XIo* xio);
|
||||||
extern int x_io_wait(XIo* xio, void*& p, size_t& size);
|
extern LibError x_io_wait(XIo* xio, void*& p, size_t& size);
|
||||||
extern int x_io_discard(XIo* xio);
|
extern LibError x_io_discard(XIo* xio);
|
||||||
extern int x_io_validate(const XIo* xio);
|
extern LibError x_io_validate(const XIo* xio);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -119,11 +119,11 @@ struct RealDir
|
|||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
extern int mount_attach_real_dir(RealDir* rd, const char* P_path, const Mount* m, int flags);
|
extern LibError mount_attach_real_dir(RealDir* rd, const char* P_path, const Mount* m, int flags);
|
||||||
extern void mount_detach_real_dir(RealDir* rd);
|
extern void mount_detach_real_dir(RealDir* rd);
|
||||||
|
|
||||||
struct TDir;
|
struct TDir;
|
||||||
extern int mount_populate(TDir* td, RealDir* rd);
|
extern LibError mount_populate(TDir* td, RealDir* rd);
|
||||||
|
|
||||||
|
|
||||||
// rebuild the VFS, i.e. re-mount everything. open files are not affected.
|
// rebuild the VFS, i.e. re-mount everything. open files are not affected.
|
||||||
@ -132,11 +132,11 @@ extern int mount_populate(TDir* td, RealDir* rd);
|
|||||||
// dir_watch reports changes; can also be called from the console after a
|
// dir_watch reports changes; can also be called from the console after a
|
||||||
// rebuild command. there is no provision for updating single VFS dirs -
|
// rebuild command. there is no provision for updating single VFS dirs -
|
||||||
// it's not worth the trouble.
|
// it's not worth the trouble.
|
||||||
extern int mount_rebuild();
|
extern LibError mount_rebuild();
|
||||||
|
|
||||||
// if <path> or its ancestors are mounted,
|
// if <path> or its ancestors are mounted,
|
||||||
// return a VFS path that accesses it.
|
// return a VFS path that accesses it.
|
||||||
// used when receiving paths from external code.
|
// used when receiving paths from external code.
|
||||||
extern int mount_make_vfs_path(const char* P_path, char* V_path);
|
extern LibError mount_make_vfs_path(const char* P_path, char* V_path);
|
||||||
|
|
||||||
#endif // #ifndef VFS_MOUNT_H__
|
#endif // #ifndef VFS_MOUNT_H__
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
// if path is invalid (see source for criteria), print a diagnostic message
|
// if path is invalid (see source for criteria), print a diagnostic message
|
||||||
// (indicating line number of the call that failed) and
|
// (indicating line number of the call that failed) and
|
||||||
// return a negative error code. used by CHECK_PATH.
|
// return a negative error code. used by CHECK_PATH.
|
||||||
int path_validate(const uint line, const char* path)
|
LibError path_validate(const uint line, const char* path)
|
||||||
{
|
{
|
||||||
size_t path_len = 0; // counted as we go; checked against max.
|
size_t path_len = 0; // counted as we go; checked against max.
|
||||||
|
|
||||||
@ -80,10 +80,10 @@ int path_validate(const uint line, const char* path)
|
|||||||
fail:
|
fail:
|
||||||
debug_printf("%s called from line %d failed: %s\n", __func__, line, msg);
|
debug_printf("%s called from line %d failed: %s\n", __func__, line, msg);
|
||||||
debug_warn("failed");
|
debug_warn("failed");
|
||||||
return -1;
|
return ERR_FAIL;
|
||||||
|
|
||||||
ok:
|
ok:
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -124,7 +124,7 @@ void vfs_path_copy(char* dst, const char* src)
|
|||||||
// if necessary, a directory separator is added between the paths.
|
// if necessary, a directory separator is added between the paths.
|
||||||
// each may be empty, filenames, or full paths.
|
// each may be empty, filenames, or full paths.
|
||||||
// total path length (including '\0') must not exceed VFS_MAX_PATH.
|
// total path length (including '\0') must not exceed VFS_MAX_PATH.
|
||||||
int vfs_path_append(char* dst, const char* path1, const char* path2)
|
LibError vfs_path_append(char* dst, const char* path1, const char* path2)
|
||||||
{
|
{
|
||||||
const size_t len1 = strlen(path1);
|
const size_t len1 = strlen(path1);
|
||||||
const size_t len2 = strlen(path2);
|
const size_t len2 = strlen(path2);
|
||||||
@ -147,19 +147,19 @@ int vfs_path_append(char* dst, const char* path1, const char* path2)
|
|||||||
if(need_separator)
|
if(need_separator)
|
||||||
*dst++ = '/';
|
*dst++ = '/';
|
||||||
strcpy(dst, path2); // safe
|
strcpy(dst, path2); // safe
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// strip <remove> from the start of <src>, prepend <replace>,
|
// strip <remove> from the start of <src>, prepend <replace>,
|
||||||
// and write to <dst>.
|
// and write to <dst>.
|
||||||
// used when converting VFS <--> real paths.
|
// used when converting VFS <--> real paths.
|
||||||
int path_replace(char* dst, const char* src, const char* remove, const char* replace)
|
LibError path_replace(char* dst, const char* src, const char* remove, const char* replace)
|
||||||
{
|
{
|
||||||
// remove doesn't match start of <src>
|
// remove doesn't match start of <src>
|
||||||
const size_t remove_len = strlen(remove);
|
const size_t remove_len = strlen(remove);
|
||||||
if(strncmp(src, remove, remove_len) != 0)
|
if(strncmp(src, remove, remove_len) != 0)
|
||||||
return -1;
|
return ERR_FAIL;
|
||||||
|
|
||||||
// get rid of trailing / in src (must not be included in remove)
|
// get rid of trailing / in src (must not be included in remove)
|
||||||
const char* start = src+remove_len;
|
const char* start = src+remove_len;
|
||||||
@ -168,5 +168,5 @@ int path_replace(char* dst, const char* src, const char* remove, const char* rep
|
|||||||
|
|
||||||
// prepend replace.
|
// prepend replace.
|
||||||
CHECK_ERR(vfs_path_append(dst, replace, start));
|
CHECK_ERR(vfs_path_append(dst, replace, start));
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
// if path is invalid (see source for criteria), print a diagnostic message
|
// if path is invalid (see source for criteria), print a diagnostic message
|
||||||
// (indicating line number of the call that failed) and
|
// (indicating line number of the call that failed) and
|
||||||
// return a negative error code. used by CHECK_PATH.
|
// return a negative error code. used by CHECK_PATH.
|
||||||
extern int path_validate(const uint line, const char* path);
|
extern LibError path_validate(const uint line, const char* path);
|
||||||
#define CHECK_PATH(path) CHECK_ERR(path_validate(__LINE__, path))
|
#define CHECK_PATH(path) CHECK_ERR(path_validate(__LINE__, path))
|
||||||
|
|
||||||
extern bool path_component_valid(const char* name);
|
extern bool path_component_valid(const char* name);
|
||||||
@ -16,6 +16,6 @@ extern bool path_component_valid(const char* name);
|
|||||||
// strip <remove> from the start of <src>, prepend <replace>,
|
// strip <remove> from the start of <src>, prepend <replace>,
|
||||||
// and write to <dst>.
|
// and write to <dst>.
|
||||||
// used when converting VFS <--> real paths.
|
// used when converting VFS <--> real paths.
|
||||||
extern int path_replace(char* dst, const char* src, const char* remove, const char* replace);
|
extern LibError path_replace(char* dst, const char* src, const char* remove, const char* replace);
|
||||||
|
|
||||||
#endif // #ifndef VFS_PATH_H__
|
#endif // #ifndef VFS_PATH_H__
|
||||||
|
@ -306,9 +306,9 @@ struct TDir
|
|||||||
|
|
||||||
void init();
|
void init();
|
||||||
TNode* find(const char* name, TNodeType desired_type);
|
TNode* find(const char* name, TNodeType desired_type);
|
||||||
int add(const char* name, TNodeType new_type, TNode** pnode);
|
LibError add(const char* name, TNodeType new_type, TNode** pnode);
|
||||||
int attach_real_dir(const char* path, int flags, const Mount* new_m);
|
LibError attach_real_dir(const char* path, int flags, const Mount* new_m);
|
||||||
int lookup(const char* path, uint flags, TNode** pnode, char* exact_path);
|
LibError lookup(const char* path, uint flags, TNode** pnode, char* exact_path);
|
||||||
void clearR();
|
void clearR();
|
||||||
void displayR(int indent_level);
|
void displayR(int indent_level);
|
||||||
};
|
};
|
||||||
@ -381,7 +381,7 @@ TNode* TDir::find(const char* name, TNodeType desired_type)
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
int TDir::add(const char* name, TNodeType new_type, TNode** pnode)
|
LibError TDir::add(const char* name, TNodeType new_type, TNode** pnode)
|
||||||
{
|
{
|
||||||
if(!path_component_valid(name))
|
if(!path_component_valid(name))
|
||||||
return ERR_PATH_INVALID;
|
return ERR_PATH_INVALID;
|
||||||
@ -396,7 +396,7 @@ int TDir::add(const char* name, TNodeType new_type, TNode** pnode)
|
|||||||
const size_t size = sizeof(TNode)+strnlen(name, VFS_MAX_PATH)+1;
|
const size_t size = sizeof(TNode)+strnlen(name, VFS_MAX_PATH)+1;
|
||||||
node = (TNode*)bucket_alloc(&node_buckets, size);
|
node = (TNode*)bucket_alloc(&node_buckets, size);
|
||||||
if(!node)
|
if(!node)
|
||||||
return 0;
|
return ERR_OK;
|
||||||
strcpy(node->exact_name, name); // safe
|
strcpy(node->exact_name, name); // safe
|
||||||
node->type = new_type;
|
node->type = new_type;
|
||||||
|
|
||||||
@ -404,7 +404,7 @@ int TDir::add(const char* name, TNodeType new_type, TNode** pnode)
|
|||||||
{
|
{
|
||||||
debug_warn("failed to expand table");
|
debug_warn("failed to expand table");
|
||||||
// node will be freed by node_free_all
|
// node will be freed by node_free_all
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// note: this is called from lookup, which needs to create nodes.
|
// note: this is called from lookup, which needs to create nodes.
|
||||||
@ -417,10 +417,10 @@ int TDir::add(const char* name, TNodeType new_type, TNode** pnode)
|
|||||||
|
|
||||||
done:
|
done:
|
||||||
*pnode = node;
|
*pnode = node;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int TDir::lookup(const char* path, uint flags, TNode** pnode, char* exact_path)
|
LibError TDir::lookup(const char* path, uint flags, TNode** pnode, char* exact_path)
|
||||||
{
|
{
|
||||||
// cleared on failure / if returning root dir node (= "")
|
// cleared on failure / if returning root dir node (= "")
|
||||||
if(exact_path)
|
if(exact_path)
|
||||||
@ -430,7 +430,7 @@ int TDir::lookup(const char* path, uint flags, TNode** pnode, char* exact_path)
|
|||||||
if(path[0] == '\0')
|
if(path[0] == '\0')
|
||||||
{
|
{
|
||||||
*pnode = (TNode*)this; // HACK: TDir is at start of TNode
|
*pnode = (TNode*)this; // HACK: TDir is at start of TNode
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
CHECK_PATH(path);
|
CHECK_PATH(path);
|
||||||
@ -522,7 +522,7 @@ int TDir::lookup(const char* path, uint flags, TNode** pnode, char* exact_path)
|
|||||||
|
|
||||||
// success.
|
// success.
|
||||||
*pnode = node;
|
*pnode = node;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// empty this directory and all subdirectories; used when rebuilding VFS.
|
// empty this directory and all subdirectories; used when rebuilding VFS.
|
||||||
@ -638,7 +638,7 @@ void tree_display()
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
int tree_add_file(TDir* td, const char* name, const Mount* m,
|
LibError tree_add_file(TDir* td, const char* name, const Mount* m,
|
||||||
off_t size, time_t mtime)
|
off_t size, time_t mtime)
|
||||||
{
|
{
|
||||||
TNode* node;
|
TNode* node;
|
||||||
@ -650,53 +650,53 @@ int tree_add_file(TDir* td, const char* name, const Mount* m,
|
|||||||
const bool is_same = (tf->size == size) &&
|
const bool is_same = (tf->size == size) &&
|
||||||
fabs(difftime(tf->mtime, mtime)) <= 2.0;
|
fabs(difftime(tf->mtime, mtime)) <= 2.0;
|
||||||
if(!mount_should_replace(tf->m, m, is_same))
|
if(!mount_should_replace(tf->m, m, is_same))
|
||||||
return 1;
|
return INFO_NO_REPLACE;
|
||||||
|
|
||||||
tf->m = m;
|
tf->m = m;
|
||||||
tf->mtime = mtime;
|
tf->mtime = mtime;
|
||||||
tf->size = size;
|
tf->size = size;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int tree_add_dir(TDir* td, const char* name, TDir** ptd)
|
LibError tree_add_dir(TDir* td, const char* name, TDir** ptd)
|
||||||
{
|
{
|
||||||
TNode* node;
|
TNode* node;
|
||||||
RETURN_ERR(td->add(name, N_DIR, &node));
|
RETURN_ERR(td->add(name, N_DIR, &node));
|
||||||
*ptd = &node->u.dir;
|
*ptd = &node->u.dir;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int tree_lookup_dir(const char* path, TDir** ptd, uint flags, char* exact_path)
|
LibError tree_lookup_dir(const char* path, TDir** ptd, uint flags, char* exact_path)
|
||||||
{
|
{
|
||||||
// TDir::lookup would return a file node
|
// path is not a directory; TDir::lookup might return a file node
|
||||||
if(path[0] != '\0' && path[strlen(path)-1] != '/')
|
if(path[0] != '\0' && path[strlen(path)-1] != '/')
|
||||||
return -1;
|
return ERR_NOT_DIR;
|
||||||
|
|
||||||
TDir* td = (flags & LF_START_DIR)? *ptd : tree_root_dir;
|
TDir* td = (flags & LF_START_DIR)? *ptd : tree_root_dir;
|
||||||
TNode* node;
|
TNode* node;
|
||||||
CHECK_ERR(td->lookup(path, flags, &node, exact_path));
|
CHECK_ERR(td->lookup(path, flags, &node, exact_path));
|
||||||
// directories should exist, so warn if this fails
|
// directories should exist, so warn if this fails
|
||||||
*ptd = &node->u.dir;
|
*ptd = &node->u.dir;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int tree_lookup(const char* path, TFile** pfile, uint flags, char* exact_path)
|
LibError tree_lookup(const char* path, TFile** pfile, uint flags, char* exact_path)
|
||||||
{
|
{
|
||||||
// TDir::lookup would return a directory node
|
// path is not a file; TDir::lookup might return a directory node
|
||||||
if(path[0] == '\0' || path[strlen(path)-1] == '/')
|
if(path[0] == '\0' || path[strlen(path)-1] == '/')
|
||||||
return -1;
|
return ERR_NOT_FILE;
|
||||||
|
|
||||||
TNode* node;
|
TNode* node;
|
||||||
int ret = tree_root_dir->lookup(path, flags, &node, exact_path);
|
LibError ret = tree_root_dir->lookup(path, flags, &node, exact_path);
|
||||||
RETURN_ERR(ret);
|
RETURN_ERR(ret);
|
||||||
*pfile = &node->u.file;
|
*pfile = &node->u.file;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -721,7 +721,7 @@ struct TreeDirIterator_
|
|||||||
cassert(sizeof(TreeDirIterator_) <= sizeof(TreeDirIterator));
|
cassert(sizeof(TreeDirIterator_) <= sizeof(TreeDirIterator));
|
||||||
|
|
||||||
|
|
||||||
int tree_dir_open(const char* path_slash, TreeDirIterator* d_)
|
LibError tree_dir_open(const char* path_slash, TreeDirIterator* d_)
|
||||||
{
|
{
|
||||||
TreeDirIterator_* d = (TreeDirIterator_*)d_;
|
TreeDirIterator_* d = (TreeDirIterator_*)d_;
|
||||||
|
|
||||||
@ -740,11 +740,11 @@ int tree_dir_open(const char* path_slash, TreeDirIterator* d_)
|
|||||||
d->it = td->children.begin();
|
d->it = td->children.begin();
|
||||||
d->end = td->children.end();
|
d->end = td->children.end();
|
||||||
d->td = td;
|
d->td = td;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int tree_dir_next_ent(TreeDirIterator* d_, DirEnt* ent)
|
LibError tree_dir_next_ent(TreeDirIterator* d_, DirEnt* ent)
|
||||||
{
|
{
|
||||||
TreeDirIterator_* d = (TreeDirIterator_*)d_;
|
TreeDirIterator_* d = (TreeDirIterator_*)d_;
|
||||||
|
|
||||||
@ -769,17 +769,17 @@ int tree_dir_next_ent(TreeDirIterator* d_, DirEnt* ent)
|
|||||||
debug_warn("invalid TNode type");
|
debug_warn("invalid TNode type");
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0; // success
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int tree_dir_close(TreeDirIterator* UNUSED(d))
|
LibError tree_dir_close(TreeDirIterator* UNUSED(d))
|
||||||
{
|
{
|
||||||
tree_unlock();
|
tree_unlock();
|
||||||
|
|
||||||
// no further cleanup needed. we could zero out d but that might
|
// no further cleanup needed. we could zero out d but that might
|
||||||
// hide bugs; the iterator is safe (will not go beyond end) anyway.
|
// hide bugs; the iterator is safe (will not go beyond end) anyway.
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -800,7 +800,7 @@ void tree_update_file(TFile* tf, off_t size, time_t mtime)
|
|||||||
|
|
||||||
|
|
||||||
// get file status (mode, size, mtime). output param is undefined on error.
|
// get file status (mode, size, mtime). output param is undefined on error.
|
||||||
int tree_stat(const TFile* tf, struct stat* s)
|
LibError tree_stat(const TFile* tf, struct stat* s)
|
||||||
{
|
{
|
||||||
// all stat members currently supported are stored in TFile, so we
|
// all stat members currently supported are stored in TFile, so we
|
||||||
// can return them directly without having to call file|zip_stat.
|
// can return them directly without having to call file|zip_stat.
|
||||||
@ -808,7 +808,7 @@ int tree_stat(const TFile* tf, struct stat* s)
|
|||||||
s->st_size = tf->size;
|
s->st_size = tf->size;
|
||||||
s->st_mtime = tf->mtime;
|
s->st_mtime = tf->mtime;
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -41,10 +41,10 @@ extern void tree_clear();
|
|||||||
//
|
//
|
||||||
// note: if "priority" is the same, replace!
|
// note: if "priority" is the same, replace!
|
||||||
// this makes sure mods/patches etc. actually replace files.
|
// this makes sure mods/patches etc. actually replace files.
|
||||||
extern int tree_add_file(TDir* td, const char* fn, const Mount* m,
|
extern LibError tree_add_file(TDir* td, const char* fn, const Mount* m,
|
||||||
off_t size, time_t mtime);
|
off_t size, time_t mtime);
|
||||||
|
|
||||||
extern int tree_add_dir(TDir* dir, const char* name, TDir** ptd);
|
extern LibError tree_add_dir(TDir* dir, const char* name, TDir** ptd);
|
||||||
|
|
||||||
enum TreeLookupFlags
|
enum TreeLookupFlags
|
||||||
{
|
{
|
||||||
@ -63,7 +63,7 @@ enum TreeLookupFlags
|
|||||||
//
|
//
|
||||||
// return 0 on success, or a negative error code
|
// return 0 on success, or a negative error code
|
||||||
// (in which case output params are undefined).
|
// (in which case output params are undefined).
|
||||||
extern int tree_lookup(const char* path, TFile** ptf, uint flags = 0, char* exact_path = 0);
|
extern LibError tree_lookup(const char* path, TFile** ptf, uint flags = 0, char* exact_path = 0);
|
||||||
|
|
||||||
// starting at VFS root, traverse <path> and pass back information
|
// starting at VFS root, traverse <path> and pass back information
|
||||||
// for its last directory component.
|
// for its last directory component.
|
||||||
@ -81,7 +81,7 @@ extern int tree_lookup(const char* path, TFile** ptf, uint flags = 0, char* exac
|
|||||||
//
|
//
|
||||||
// return 0 on success, or a negative error code
|
// return 0 on success, or a negative error code
|
||||||
// (in which case output params are undefined).
|
// (in which case output params are undefined).
|
||||||
extern int tree_lookup_dir(const char* path, TDir** ptd, uint flags = 0, char* exact_path = 0);
|
extern LibError tree_lookup_dir(const char* path, TDir** ptd, uint flags = 0, char* exact_path = 0);
|
||||||
|
|
||||||
|
|
||||||
// documentation and rationale: see file.h's dir_next_ent interface
|
// documentation and rationale: see file.h's dir_next_ent interface
|
||||||
@ -90,17 +90,17 @@ struct TreeDirIterator
|
|||||||
char opaque[32];
|
char opaque[32];
|
||||||
};
|
};
|
||||||
|
|
||||||
extern int tree_dir_open(const char* path_slash, TreeDirIterator* d);
|
extern LibError tree_dir_open(const char* path_slash, TreeDirIterator* d);
|
||||||
extern int tree_dir_next_ent(TreeDirIterator* d, DirEnt* ent);
|
extern LibError tree_dir_next_ent(TreeDirIterator* d, DirEnt* ent);
|
||||||
extern int tree_dir_close(TreeDirIterator* d);
|
extern LibError tree_dir_close(TreeDirIterator* d);
|
||||||
|
|
||||||
|
|
||||||
// given a file that is stored on disk and its VFS path,
|
// given a file that is stored on disk and its VFS path,
|
||||||
// return its OS path (for use with file.cpp).
|
// return its OS path (for use with file.cpp).
|
||||||
// used by vfs_realpath and VFile_reopen.
|
// used by vfs_realpath and VFile_reopen.
|
||||||
extern int tree_realpath(TFile* tf, const char* V_path, char* P_real_path);
|
extern LibError tree_realpath(TFile* tf, const char* V_path, char* P_real_path);
|
||||||
|
|
||||||
extern int tree_stat(const TFile* tf, struct stat* s);
|
extern LibError tree_stat(const TFile* tf, struct stat* s);
|
||||||
|
|
||||||
extern const Mount* tree_get_mount(const TFile* tf);
|
extern const Mount* tree_get_mount(const TFile* tf);
|
||||||
|
|
||||||
|
@ -158,7 +158,7 @@ static const u8* z_find_id(const u8* file, size_t size, const u8* start, const c
|
|||||||
// find "End of Central Dir Record" in file.
|
// find "End of Central Dir Record" in file.
|
||||||
// z_is_header has made sure size >= ECDR_SIZE.
|
// z_is_header has made sure size >= ECDR_SIZE.
|
||||||
// return -1 on failure (output param invalid), otherwise 0.
|
// return -1 on failure (output param invalid), otherwise 0.
|
||||||
static int z_find_ecdr(const u8* file, size_t size, const u8*& ecdr_)
|
static LibError z_find_ecdr(const u8* file, size_t size, const u8*& ecdr_)
|
||||||
{
|
{
|
||||||
// early out: check expected case (ECDR at EOF; no file comment)
|
// early out: check expected case (ECDR at EOF; no file comment)
|
||||||
const u8* ecdr = file + size - ECDR_SIZE;
|
const u8* ecdr = file + size - ECDR_SIZE;
|
||||||
@ -178,7 +178,7 @@ static int z_find_ecdr(const u8* file, size_t size, const u8*& ecdr_)
|
|||||||
|
|
||||||
found_ecdr:
|
found_ecdr:
|
||||||
ecdr_ = ecdr;
|
ecdr_ = ecdr;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -211,6 +211,24 @@ static time_t convert_dos_date(u16 fatdate, u16 fattime)
|
|||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
static bool z_cdfh_is_valid_file(u16 method, size_t csize, size_t ucsize,
|
||||||
|
const u8* lfh)
|
||||||
|
{
|
||||||
|
// compression method is unknown (neither deflated nor stored)
|
||||||
|
if(method & ~8)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// it's a directory entry (we only want files).
|
||||||
|
if(!csize && !ucsize)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// LFH signature not found
|
||||||
|
if(!lfh)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
enum z_extract_cdfh_ret
|
enum z_extract_cdfh_ret
|
||||||
{
|
{
|
||||||
Z_CDFH_ABORT = -1, // next CDFH not found; abort.
|
Z_CDFH_ABORT = -1, // next CDFH not found; abort.
|
||||||
@ -224,8 +242,8 @@ enum z_extract_cdfh_ret
|
|||||||
// called by z_enum_files, which passes the output to lookup.
|
// called by z_enum_files, which passes the output to lookup.
|
||||||
//
|
//
|
||||||
// [30ms]
|
// [30ms]
|
||||||
static int z_extract_cdfh(const u8* file, size_t size, // in
|
static z_extract_cdfh_ret z_extract_cdfh(const u8* file, size_t size, // in
|
||||||
const u8*& cdfh, const char*& fn, size_t& fn_len, ZLoc* loc) // out
|
const u8*& cdfh, const char*& fn, size_t& fn_len, ZLoc* loc) // out
|
||||||
{
|
{
|
||||||
// scan for next CDFH (at or beyond current cdfh position)
|
// scan for next CDFH (at or beyond current cdfh position)
|
||||||
cdfh = z_find_id(file, size, cdfh, cdfh_id, CDFH_SIZE);
|
cdfh = z_find_id(file, size, cdfh, cdfh_id, CDFH_SIZE);
|
||||||
@ -255,15 +273,7 @@ static int z_extract_cdfh(const u8* file, size_t size, // in
|
|||||||
// but will still scan ahead for its id on next call.
|
// but will still scan ahead for its id on next call.
|
||||||
cdfh += CDFH_SIZE + fn_len_ + e_len + c_len;
|
cdfh += CDFH_SIZE + fn_len_ + e_len + c_len;
|
||||||
|
|
||||||
// is this entry not a valid file?
|
if(!z_cdfh_is_valid_file(method, csize, ucsize, lfh))
|
||||||
if(
|
|
||||||
// compression method is unknown (neither deflated nor stored)
|
|
||||||
(method & ~8) ||
|
|
||||||
// it's a directory entry (we only want files).
|
|
||||||
(!csize && !ucsize) ||
|
|
||||||
// LFH signature not found
|
|
||||||
(!lfh)
|
|
||||||
)
|
|
||||||
return Z_CDFH_SKIPPED;
|
return Z_CDFH_SKIPPED;
|
||||||
|
|
||||||
// get actual file ofs (see above)
|
// get actual file ofs (see above)
|
||||||
@ -285,9 +295,10 @@ static int z_extract_cdfh(const u8* file, size_t size, // in
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// successively call <cb> for each valid file in the archive,
|
// successively called for each valid file in the archive,
|
||||||
// passing the complete path and <user>.
|
// passing the complete path and <user>.
|
||||||
// if it returns a nonzero value, abort and return that, otherwise 0.
|
// return INFO_CB_CONTINUE to continue calling; anything else will cause
|
||||||
|
// the caller to abort and immediately return that value.
|
||||||
//
|
//
|
||||||
// HACK: call back with negative index the first time; its abs. value is
|
// HACK: call back with negative index the first time; its abs. value is
|
||||||
// the number of entries in the archive. lookup needs to know this so it can
|
// the number of entries in the archive. lookup needs to know this so it can
|
||||||
@ -295,13 +306,13 @@ static int z_extract_cdfh(const u8* file, size_t size, // in
|
|||||||
// z_enum_files would require passing around a ZipInfo struct, or searching
|
// z_enum_files would require passing around a ZipInfo struct, or searching
|
||||||
// for the ECDR twice - both ways aren't nice. nor is expanding on demand -
|
// for the ECDR twice - both ways aren't nice. nor is expanding on demand -
|
||||||
// we try to minimize allocations (faster, less fragmentation).
|
// we try to minimize allocations (faster, less fragmentation).
|
||||||
|
//
|
||||||
// fn (filename) is not necessarily 0-terminated!
|
// fn (filename) is not necessarily 0-terminated!
|
||||||
// loc is only valid during the callback! must be copied or saved.
|
// loc is only valid during the callback! must be copied or saved.
|
||||||
typedef int(*CDFH_CB)(uintptr_t user, i32 idx, const char* fn, size_t fn_len, const ZLoc* loc);
|
typedef LibError (*CDFH_CB)(uintptr_t user, i32 idx, const char* fn, size_t fn_len, const ZLoc* loc);
|
||||||
|
|
||||||
|
|
||||||
static int z_enum_files(const u8* file, const size_t size, const CDFH_CB cb, const uintptr_t user)
|
static LibError z_enum_files(const u8* file, const size_t size, const CDFH_CB cb, const uintptr_t user)
|
||||||
{
|
{
|
||||||
// find "End of Central Directory Record"
|
// find "End of Central Directory Record"
|
||||||
const u8* ecdr;
|
const u8* ecdr;
|
||||||
@ -314,8 +325,9 @@ static int z_enum_files(const u8* file, const size_t size, const CDFH_CB cb, con
|
|||||||
const i32 num_entries = read_le16(ecdr+10);
|
const i32 num_entries = read_le16(ecdr+10);
|
||||||
// .. callback expects -num_entries < 0.
|
// .. callback expects -num_entries < 0.
|
||||||
// if it's 0, the callback would treat it as an index => crash.
|
// if it's 0, the callback would treat it as an index => crash.
|
||||||
|
// ERR_FAIL means we'll no longer be called.
|
||||||
if(!num_entries)
|
if(!num_entries)
|
||||||
return -1;
|
return ERR_FAIL;
|
||||||
CHECK_ERR(cb(user, -num_entries, 0, 0, 0));
|
CHECK_ERR(cb(user, -num_entries, 0, 0, 0));
|
||||||
|
|
||||||
// iterate through CDFH
|
// iterate through CDFH
|
||||||
@ -332,22 +344,24 @@ static int z_enum_files(const u8* file, const size_t size, const CDFH_CB cb, con
|
|||||||
size_t fn_len;
|
size_t fn_len;
|
||||||
ZLoc loc;
|
ZLoc loc;
|
||||||
|
|
||||||
int ret = z_extract_cdfh(file, size, cdfh, fn, fn_len, &loc);
|
z_extract_cdfh_ret ret = z_extract_cdfh(file, size, cdfh, fn, fn_len, &loc);
|
||||||
// valid file
|
// valid file
|
||||||
if(ret == Z_CDFH_FILE_OK)
|
if(ret == Z_CDFH_FILE_OK)
|
||||||
{
|
{
|
||||||
cb(user, idx, fn, fn_len, &loc);
|
LibError cb_ret = cb(user, idx, fn, fn_len, &loc);
|
||||||
|
if(cb_ret != INFO_CB_CONTINUE)
|
||||||
|
return cb_ret;
|
||||||
idx++; // see rationale above
|
idx++; // see rationale above
|
||||||
}
|
}
|
||||||
// next CDFH not found (Zip archive corrupted)
|
// next CDFH not found (Zip archive corrupted)
|
||||||
else if(ret == Z_CDFH_ABORT)
|
else if(ret == Z_CDFH_ABORT)
|
||||||
return -1;
|
return ERR_CORRUPTED;
|
||||||
// skipping this CDFH (e.g. if directory)
|
// skipping this CDFH (e.g. if directory)
|
||||||
else
|
else
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -422,7 +436,7 @@ struct LookupInfo
|
|||||||
// - loc is only valid during the callback! must be copied or saved.
|
// - loc is only valid during the callback! must be copied or saved.
|
||||||
//
|
//
|
||||||
// [40ms]
|
// [40ms]
|
||||||
static int lookup_add_file_cb(uintptr_t user, i32 idx,
|
static LibError lookup_add_file_cb(uintptr_t user, i32 idx,
|
||||||
const char* fn, size_t fn_len, const ZLoc* loc)
|
const char* fn, size_t fn_len, const ZLoc* loc)
|
||||||
{
|
{
|
||||||
LookupInfo* li = (LookupInfo*)user;
|
LookupInfo* li = (LookupInfo*)user;
|
||||||
@ -446,7 +460,7 @@ static int lookup_add_file_cb(uintptr_t user, i32 idx,
|
|||||||
// will count below, since some entries aren't files.
|
// will count below, since some entries aren't files.
|
||||||
li->ents = (ZEnt*)p;
|
li->ents = (ZEnt*)p;
|
||||||
li->fn_hashes = (FnHash*)((char*)p + ents_size);
|
li->fn_hashes = (FnHash*)((char*)p + ents_size);
|
||||||
return 0;
|
return INFO_CB_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// adding a regular file.
|
// adding a regular file.
|
||||||
@ -470,15 +484,15 @@ static int lookup_add_file_cb(uintptr_t user, i32 idx,
|
|||||||
li->fn_hashes[idx] = fn_hash;
|
li->fn_hashes[idx] = fn_hash;
|
||||||
(*li->idx)[fn_hash] = idx;
|
(*li->idx)[fn_hash] = idx;
|
||||||
|
|
||||||
return 0;
|
return INFO_CB_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// initialize lookup data structure for the given Zip archive:
|
// initialize lookup data structure for the given Zip archive:
|
||||||
// adds all files to the index.
|
// adds all files to the index.
|
||||||
static int lookup_init(LookupInfo* li, const u8* file, const size_t size)
|
static LibError lookup_init(LookupInfo* li, const u8* file, const size_t size)
|
||||||
{
|
{
|
||||||
int err;
|
LibError err;
|
||||||
|
|
||||||
// check if it's even a Zip file.
|
// check if it's even a Zip file.
|
||||||
// the VFS blindly opens files when mounting; it needs to open
|
// the VFS blindly opens files when mounting; it needs to open
|
||||||
@ -497,21 +511,21 @@ static int lookup_init(LookupInfo* li, const u8* file, const size_t size)
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int lookup_validate(const LookupInfo* li)
|
static LibError lookup_validate(const LookupInfo* li)
|
||||||
{
|
{
|
||||||
if(debug_is_pointer_bogus(li->ents) || debug_is_pointer_bogus(li->fn_hashes))
|
if(debug_is_pointer_bogus(li->ents) || debug_is_pointer_bogus(li->fn_hashes))
|
||||||
return -2;
|
return ERR_1;
|
||||||
if(li->num_files > li->num_entries || li->next_file > li->num_entries)
|
if(li->num_files > li->num_entries || li->next_file > li->num_entries)
|
||||||
return -3;
|
return ERR_2;
|
||||||
if(li->num_entries < 0 || li->num_files < 0 || li->next_file < 0)
|
if(li->num_entries < 0 || li->num_files < 0 || li->next_file < 0)
|
||||||
return -4;
|
return ERR_3;
|
||||||
if(debug_is_pointer_bogus(li->idx))
|
if(debug_is_pointer_bogus(li->idx))
|
||||||
return -5;
|
return ERR_4;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -536,7 +550,7 @@ static void lookup_free(LookupInfo* li)
|
|||||||
|
|
||||||
|
|
||||||
// look up ZLoc, given filename (untrusted!).
|
// look up ZLoc, given filename (untrusted!).
|
||||||
static int lookup_get_file_info(LookupInfo* li, const char* fn, ZLoc* loc)
|
static LibError lookup_get_file_info(LookupInfo* li, const char* fn, ZLoc* loc)
|
||||||
{
|
{
|
||||||
const FnHash fn_hash = fnv_lc_hash(fn);
|
const FnHash fn_hash = fnv_lc_hash(fn);
|
||||||
|
|
||||||
@ -565,14 +579,14 @@ have_idx:
|
|||||||
li->next_file = i+1;
|
li->next_file = i+1;
|
||||||
|
|
||||||
*loc = li->ents[i].loc;
|
*loc = li->ents[i].loc;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// successively call <cb> for each valid file in the index,
|
// successively call <cb> for each valid file in the index,
|
||||||
// passing the complete path and <user>.
|
// passing the complete path and <user>.
|
||||||
// if it returns a nonzero value, abort and return that, otherwise 0.
|
// if it returns a nonzero value, abort and return that, otherwise 0.
|
||||||
static int lookup_enum_files(LookupInfo* li, FileCB cb, uintptr_t user)
|
static LibError lookup_enum_files(LookupInfo* li, FileCB cb, uintptr_t user)
|
||||||
{
|
{
|
||||||
struct stat s;
|
struct stat s;
|
||||||
memset(&s, 0, sizeof(s));
|
memset(&s, 0, sizeof(s));
|
||||||
@ -584,12 +598,12 @@ static int lookup_enum_files(LookupInfo* li, FileCB cb, uintptr_t user)
|
|||||||
s.st_size = (off_t)ent->loc.ucsize;
|
s.st_size = (off_t)ent->loc.ucsize;
|
||||||
s.st_mtime = ent->loc.mtime;
|
s.st_mtime = ent->loc.mtime;
|
||||||
|
|
||||||
int ret = cb(ent->fn, &s, user);
|
LibError ret = cb(ent->fn, &s, user);
|
||||||
if(ret != 0)
|
if(ret != INFO_CB_CONTINUE)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -646,7 +660,7 @@ static void ZArchive_dtor(ZArchive* za)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ZArchive_reload(ZArchive* za, const char* fn, Handle)
|
static LibError ZArchive_reload(ZArchive* za, const char* fn, Handle)
|
||||||
{
|
{
|
||||||
// (note: don't warn on failure - this happens when
|
// (note: don't warn on failure - this happens when
|
||||||
// vfs_mount blindly zip_archive_opens a dir)
|
// vfs_mount blindly zip_archive_opens a dir)
|
||||||
@ -665,20 +679,20 @@ static int ZArchive_reload(ZArchive* za, const char* fn, Handle)
|
|||||||
(void)file_unmap(&za->f);
|
(void)file_unmap(&za->f);
|
||||||
za->is_mapped = 0;
|
za->is_mapped = 0;
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ZArchive_validate(const ZArchive* za)
|
static LibError ZArchive_validate(const ZArchive* za)
|
||||||
{
|
{
|
||||||
RETURN_ERR(file_validate(&za->f));
|
RETURN_ERR(file_validate(&za->f));
|
||||||
RETURN_ERR(lookup_validate(&za->li));
|
RETURN_ERR(lookup_validate(&za->li));
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ZArchive_to_string(const ZArchive* za, char* buf)
|
static LibError ZArchive_to_string(const ZArchive* za, char* buf)
|
||||||
{
|
{
|
||||||
snprintf(buf, H_STRING_LEN, "(%d files)", lookup_get_num_files(&za->li));
|
snprintf(buf, H_STRING_LEN, "(%d files)", lookup_get_num_files(&za->li));
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -693,7 +707,7 @@ TIMER("zip_archive_open");
|
|||||||
|
|
||||||
|
|
||||||
// close the archive <ha> and set ha to 0
|
// close the archive <ha> and set ha to 0
|
||||||
int zip_archive_close(Handle& ha)
|
LibError zip_archive_close(Handle& ha)
|
||||||
{
|
{
|
||||||
return h_free(ha, H_ZArchive);
|
return h_free(ha, H_ZArchive);
|
||||||
}
|
}
|
||||||
@ -702,7 +716,7 @@ int zip_archive_close(Handle& ha)
|
|||||||
// successively call <cb> for each valid file in the archive <ha>,
|
// successively call <cb> for each valid file in the archive <ha>,
|
||||||
// passing the complete path and <user>.
|
// passing the complete path and <user>.
|
||||||
// if it returns a nonzero value, abort and return that, otherwise 0.
|
// if it returns a nonzero value, abort and return that, otherwise 0.
|
||||||
int zip_enum(const Handle ha, const FileCB cb, const uintptr_t user)
|
LibError zip_enum(const Handle ha, const FileCB cb, const uintptr_t user)
|
||||||
{
|
{
|
||||||
H_DEREF(ha, ZArchive, za);
|
H_DEREF(ha, ZArchive, za);
|
||||||
|
|
||||||
@ -717,6 +731,27 @@ int zip_enum(const Handle ha, const FileCB cb, const uintptr_t user)
|
|||||||
//
|
//
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
static LibError LibError_from_zlib(int err)
|
||||||
|
{
|
||||||
|
switch(err)
|
||||||
|
{
|
||||||
|
case Z_OK:
|
||||||
|
return ERR_OK;
|
||||||
|
case Z_STREAM_END:
|
||||||
|
return ERR_EOF;
|
||||||
|
case Z_MEM_ERROR:
|
||||||
|
return ERR_NO_MEM;
|
||||||
|
case Z_DATA_ERROR:
|
||||||
|
return ERR_CORRUPTED;
|
||||||
|
case Z_STREAM_ERROR:
|
||||||
|
return ERR_INVALID_PARAM;
|
||||||
|
default:
|
||||||
|
return ERR_FAIL;
|
||||||
|
}
|
||||||
|
UNREACHABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// must be dynamically allocated - need one for every open ZFile,
|
// must be dynamically allocated - need one for every open ZFile,
|
||||||
// and z_stream is large.
|
// and z_stream is large.
|
||||||
struct InfCtx
|
struct InfCtx
|
||||||
@ -767,7 +802,7 @@ static void free_in_buf(InfCtx* ctx)
|
|||||||
|
|
||||||
|
|
||||||
// subsequent calls to inf_inflate will unzip into <out>.
|
// subsequent calls to inf_inflate will unzip into <out>.
|
||||||
int inf_set_dest(uintptr_t _ctx, void* out, size_t out_size)
|
LibError inf_set_dest(uintptr_t _ctx, void* out, size_t out_size)
|
||||||
{
|
{
|
||||||
#ifdef NO_ZLIB
|
#ifdef NO_ZLIB
|
||||||
return -1;
|
return -1;
|
||||||
@ -778,11 +813,11 @@ int inf_set_dest(uintptr_t _ctx, void* out, size_t out_size)
|
|||||||
if(zs->next_out || zs->avail_out)
|
if(zs->next_out || zs->avail_out)
|
||||||
{
|
{
|
||||||
debug_warn("ctx already in use!");
|
debug_warn("ctx already in use!");
|
||||||
return -1;
|
return ERR_FAIL;
|
||||||
}
|
}
|
||||||
zs->next_out = (Byte*)out;
|
zs->next_out = (Byte*)out;
|
||||||
zs->avail_out = (uInt)out_size;
|
zs->avail_out = (uInt)out_size;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -815,12 +850,20 @@ ssize_t inf_inflate(uintptr_t _ctx, void* in, size_t in_size, bool free_in_buf =
|
|||||||
ctx->in_buf = in;
|
ctx->in_buf = in;
|
||||||
}
|
}
|
||||||
|
|
||||||
int err = 0;
|
LibError err = ERR_OK;
|
||||||
|
|
||||||
if(ctx->compressed)
|
if(ctx->compressed)
|
||||||
{
|
{
|
||||||
TIMER_ACCRUE(tc_zip_inflate);
|
TIMER_ACCRUE(tc_zip_inflate);
|
||||||
err = inflate(zs, Z_SYNC_FLUSH);
|
int ret = inflate(zs, Z_SYNC_FLUSH);
|
||||||
|
err = LibError_from_zlib(ret);
|
||||||
|
// sanity check: if ZLib reports end of stream, all input data
|
||||||
|
// must have been consumed.
|
||||||
|
if(err == ERR_EOF)
|
||||||
|
{
|
||||||
|
debug_assert(zs->avail_in == 0);
|
||||||
|
err = ERR_OK;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -856,7 +899,7 @@ ssize_t inf_inflate(uintptr_t _ctx, void* in, size_t in_size, bool free_in_buf =
|
|||||||
|
|
||||||
|
|
||||||
// free the given context.
|
// free the given context.
|
||||||
int inf_free_ctx(uintptr_t _ctx)
|
LibError inf_free_ctx(uintptr_t _ctx)
|
||||||
{
|
{
|
||||||
#ifdef NO_ZLIB
|
#ifdef NO_ZLIB
|
||||||
return -1;
|
return -1;
|
||||||
@ -871,7 +914,7 @@ int inf_free_ctx(uintptr_t _ctx)
|
|||||||
|
|
||||||
inflateEnd(zs);
|
inflateEnd(zs);
|
||||||
free(ctx);
|
free(ctx);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -896,7 +939,7 @@ static inline bool zfile_compressed(ZFile* zf)
|
|||||||
|
|
||||||
|
|
||||||
// get file status (size, mtime). output param is zeroed on error.
|
// get file status (size, mtime). output param is zeroed on error.
|
||||||
int zip_stat(Handle ha, const char* fn, struct stat* s)
|
LibError zip_stat(Handle ha, const char* fn, struct stat* s)
|
||||||
{
|
{
|
||||||
// zero output param in case we fail below.
|
// zero output param in case we fail below.
|
||||||
memset(s, 0, sizeof(struct stat));
|
memset(s, 0, sizeof(struct stat));
|
||||||
@ -909,24 +952,24 @@ int zip_stat(Handle ha, const char* fn, struct stat* s)
|
|||||||
|
|
||||||
s->st_size = loc.ucsize;
|
s->st_size = loc.ucsize;
|
||||||
s->st_mtime = loc.mtime;
|
s->st_mtime = loc.mtime;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int zip_validate(const ZFile* zf)
|
LibError zip_validate(const ZFile* zf)
|
||||||
{
|
{
|
||||||
if(!zf)
|
if(!zf)
|
||||||
return ERR_INVALID_PARAM;
|
return ERR_INVALID_PARAM;
|
||||||
// note: don't check zf->ha - it may be freed at shutdown before
|
// note: don't check zf->ha - it may be freed at shutdown before
|
||||||
// its files. TODO: revisit once dependency support is added.
|
// its files. TODO: revisit once dependency support is added.
|
||||||
if(!zf->ucsize)
|
if(!zf->ucsize)
|
||||||
return -2;
|
return ERR_1;
|
||||||
else if(!zf->inf_ctx)
|
else if(!zf->inf_ctx)
|
||||||
return -3;
|
return ERR_2;
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CHECK_ZFILE(zf) CHECK_ERR(zip_validate(zf))
|
#define CHECK_ZFILE(zf) CHECK_ERR(zip_validate(zf))
|
||||||
@ -934,7 +977,7 @@ int zip_validate(const ZFile* zf)
|
|||||||
|
|
||||||
// open file, and fill *zf with information about it.
|
// open file, and fill *zf with information about it.
|
||||||
// return < 0 on error (output param zeroed).
|
// return < 0 on error (output param zeroed).
|
||||||
int zip_open(const Handle ha, const char* fn, int flags, ZFile* zf)
|
LibError zip_open(const Handle ha, const char* fn, int flags, ZFile* zf)
|
||||||
{
|
{
|
||||||
// zero output param in case we fail below.
|
// zero output param in case we fail below.
|
||||||
memset(zf, 0, sizeof(ZFile));
|
memset(zf, 0, sizeof(ZFile));
|
||||||
@ -956,12 +999,12 @@ int zip_open(const Handle ha, const char* fn, int flags, ZFile* zf)
|
|||||||
zf->inf_ctx = inf_init_ctx(zfile_compressed(zf));
|
zf->inf_ctx = inf_init_ctx(zfile_compressed(zf));
|
||||||
zf->is_mapped = 0;
|
zf->is_mapped = 0;
|
||||||
CHECK_ZFILE(zf);
|
CHECK_ZFILE(zf);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// close file.
|
// close file.
|
||||||
int zip_close(ZFile* zf)
|
LibError zip_close(ZFile* zf)
|
||||||
{
|
{
|
||||||
CHECK_ZFILE(zf);
|
CHECK_ZFILE(zf);
|
||||||
// other ZFile fields don't need to be freed/cleared
|
// other ZFile fields don't need to be freed/cleared
|
||||||
@ -989,7 +1032,7 @@ static const size_t CHUNK_SIZE = 16*KiB;
|
|||||||
|
|
||||||
// begin transferring <size> bytes, starting at <ofs>. get result
|
// begin transferring <size> bytes, starting at <ofs>. get result
|
||||||
// with zip_io_wait; when no longer needed, free via zip_io_discard.
|
// with zip_io_wait; when no longer needed, free via zip_io_discard.
|
||||||
int zip_io_issue(ZFile* zf, off_t user_ofs, size_t max_output_size, void* user_buf, ZipIo* io)
|
LibError zip_io_issue(ZFile* zf, off_t user_ofs, size_t max_output_size, void* user_buf, ZipIo* io)
|
||||||
{
|
{
|
||||||
// not needed, since ZFile tells us the last read offset in the file.
|
// not needed, since ZFile tells us the last read offset in the file.
|
||||||
UNUSED2(user_ofs);
|
UNUSED2(user_ofs);
|
||||||
@ -1023,7 +1066,7 @@ int zip_io_issue(ZFile* zf, off_t user_ofs, size_t max_output_size, void* user_b
|
|||||||
{
|
{
|
||||||
io->already_inflated = true;
|
io->already_inflated = true;
|
||||||
io->max_output_size = bytes_inflated;
|
io->max_output_size = bytes_inflated;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// read up to next chunk (so that the next read is aligned -
|
// read up to next chunk (so that the next read is aligned -
|
||||||
@ -1043,7 +1086,7 @@ int zip_io_issue(ZFile* zf, off_t user_ofs, size_t max_output_size, void* user_b
|
|||||||
|
|
||||||
CHECK_ERR(file_io_issue(&za->f, ofs, size, buf, &io->io));
|
CHECK_ERR(file_io_issue(&za->f, ofs, size, buf, &io->io));
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1059,12 +1102,12 @@ int zip_io_has_completed(ZipIo* io)
|
|||||||
|
|
||||||
// wait until the transfer <io> completes, and return its buffer.
|
// wait until the transfer <io> completes, and return its buffer.
|
||||||
// output parameters are zeroed on error.
|
// output parameters are zeroed on error.
|
||||||
int zip_io_wait(ZipIo* io, void*& buf, size_t& size)
|
LibError zip_io_wait(ZipIo* io, void*& buf, size_t& size)
|
||||||
{
|
{
|
||||||
buf = io->user_buf;
|
buf = io->user_buf;
|
||||||
size = io->max_output_size;
|
size = io->max_output_size;
|
||||||
if(io->already_inflated)
|
if(io->already_inflated)
|
||||||
return 0;
|
return ERR_OK;
|
||||||
|
|
||||||
void* raw_buf;
|
void* raw_buf;
|
||||||
size_t raw_size;
|
size_t raw_size;
|
||||||
@ -1085,28 +1128,28 @@ int zip_io_wait(ZipIo* io, void*& buf, size_t& size)
|
|||||||
size = raw_size;
|
size = raw_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// finished with transfer <io> - free its buffer (returned by zip_io_wait)
|
// finished with transfer <io> - free its buffer (returned by zip_io_wait)
|
||||||
int zip_io_discard(ZipIo* io)
|
LibError zip_io_discard(ZipIo* io)
|
||||||
{
|
{
|
||||||
if(io->already_inflated)
|
if(io->already_inflated)
|
||||||
return 0;
|
return ERR_OK;
|
||||||
return file_io_discard(&io->io);
|
return file_io_discard(&io->io);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int zip_io_validate(const ZipIo* io)
|
LibError zip_io_validate(const ZipIo* io)
|
||||||
{
|
{
|
||||||
if(debug_is_pointer_bogus(io->user_buf))
|
if(debug_is_pointer_bogus(io->user_buf))
|
||||||
return -2;
|
return ERR_1;
|
||||||
if(*(u8*)&io->already_inflated > 1)
|
if(*(u8*)&io->already_inflated > 1)
|
||||||
return -3;
|
return ERR_2;
|
||||||
// <inf_ctx> and <max_output_size> have no invariants we could check.
|
// <inf_ctx> and <max_output_size> have no invariants we could check.
|
||||||
RETURN_ERR(file_io_validate(&io->io));
|
RETURN_ERR(file_io_validate(&io->io));
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1229,7 +1272,7 @@ ssize_t zip_read(ZFile* zf, off_t ofs, size_t size, void* p, FileIOCB cb, uintpt
|
|||||||
// the mapping will be removed (if still open) when its file is closed.
|
// the mapping will be removed (if still open) when its file is closed.
|
||||||
// however, map/unmap calls should still be paired so that the mapping
|
// however, map/unmap calls should still be paired so that the mapping
|
||||||
// may be removed when no longer needed.
|
// may be removed when no longer needed.
|
||||||
int zip_map(ZFile* zf, void*& p, size_t& size)
|
LibError zip_map(ZFile* zf, void*& p, size_t& size)
|
||||||
{
|
{
|
||||||
p = 0;
|
p = 0;
|
||||||
size = 0;
|
size = 0;
|
||||||
@ -1239,10 +1282,7 @@ int zip_map(ZFile* zf, void*& p, size_t& size)
|
|||||||
// mapping compressed files doesn't make sense because the
|
// mapping compressed files doesn't make sense because the
|
||||||
// compression algorithm is unspecified - disallow it.
|
// compression algorithm is unspecified - disallow it.
|
||||||
if(zfile_compressed(zf))
|
if(zfile_compressed(zf))
|
||||||
{
|
CHECK_ERR(ERR_IS_COMPRESSED);
|
||||||
debug_warn("file is compressed");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// note: we mapped the archive in zip_archive_open, but unmapped it
|
// note: we mapped the archive in zip_archive_open, but unmapped it
|
||||||
// in the meantime to save memory in case it wasn't going to be mapped.
|
// in the meantime to save memory in case it wasn't going to be mapped.
|
||||||
@ -1256,7 +1296,7 @@ int zip_map(ZFile* zf, void*& p, size_t& size)
|
|||||||
size = zf->ucsize;
|
size = zf->ucsize;
|
||||||
|
|
||||||
zf->is_mapped = 1;
|
zf->is_mapped = 1;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1265,14 +1305,14 @@ int zip_map(ZFile* zf, void*& p, size_t& size)
|
|||||||
// the mapping will be removed (if still open) when its archive is closed.
|
// the mapping will be removed (if still open) when its archive is closed.
|
||||||
// however, map/unmap calls should be paired so that the archive mapping
|
// however, map/unmap calls should be paired so that the archive mapping
|
||||||
// may be removed when no longer needed.
|
// may be removed when no longer needed.
|
||||||
int zip_unmap(ZFile* zf)
|
LibError zip_unmap(ZFile* zf)
|
||||||
{
|
{
|
||||||
CHECK_ZFILE(zf);
|
CHECK_ZFILE(zf);
|
||||||
|
|
||||||
// make sure archive mapping refcount remains balanced:
|
// make sure archive mapping refcount remains balanced:
|
||||||
// don't allow multiple|"false" unmaps.
|
// don't allow multiple|"false" unmaps.
|
||||||
if(!zf->is_mapped)
|
if(!zf->is_mapped)
|
||||||
return -1;
|
return ERR_FAIL;
|
||||||
zf->is_mapped = 0;
|
zf->is_mapped = 0;
|
||||||
|
|
||||||
H_DEREF(zf->ha, ZArchive, za);
|
H_DEREF(zf->ha, ZArchive, za);
|
||||||
|
@ -35,12 +35,12 @@
|
|||||||
extern Handle zip_archive_open(const char* fn);
|
extern Handle zip_archive_open(const char* fn);
|
||||||
|
|
||||||
// close the archive <ha> and set ha to 0
|
// close the archive <ha> and set ha to 0
|
||||||
extern int zip_archive_close(Handle& ha);
|
extern LibError zip_archive_close(Handle& ha);
|
||||||
|
|
||||||
// successively call <cb> for each valid file in the archive <ha>,
|
// successively call <cb> for each valid file in the archive <ha>,
|
||||||
// passing the complete path and <user>.
|
// passing the complete path and <user>.
|
||||||
// if it returns a nonzero value, abort and return that, otherwise 0.
|
// if it returns a nonzero value, abort and return that, otherwise 0.
|
||||||
extern int zip_enum(const Handle ha, const FileCB cb, const uintptr_t user);
|
extern LibError zip_enum(const Handle ha, const FileCB cb, const uintptr_t user);
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -69,16 +69,16 @@ struct ZFile
|
|||||||
};
|
};
|
||||||
|
|
||||||
// get file status (size, mtime). output param is zeroed on error.
|
// get file status (size, mtime). output param is zeroed on error.
|
||||||
extern int zip_stat(Handle ha, const char* fn, struct stat* s);
|
extern LibError zip_stat(Handle ha, const char* fn, struct stat* s);
|
||||||
|
|
||||||
// open file, and fill *zf with information about it.
|
// open file, and fill *zf with information about it.
|
||||||
// return < 0 on error (output param zeroed).
|
// return < 0 on error (output param zeroed).
|
||||||
extern int zip_open(Handle ha, const char* fn, int flags, ZFile* zf);
|
extern LibError zip_open(Handle ha, const char* fn, int flags, ZFile* zf);
|
||||||
|
|
||||||
// close file.
|
// close file.
|
||||||
extern int zip_close(ZFile* zf);
|
extern LibError zip_close(ZFile* zf);
|
||||||
|
|
||||||
extern int zip_validate(const ZFile* zf);
|
extern LibError zip_validate(const ZFile* zf);
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -99,7 +99,7 @@ struct ZipIo
|
|||||||
|
|
||||||
// begin transferring <size> bytes, starting at <ofs>. get result
|
// begin transferring <size> bytes, starting at <ofs>. get result
|
||||||
// with zip_io_wait; when no longer needed, free via zip_io_discard.
|
// with zip_io_wait; when no longer needed, free via zip_io_discard.
|
||||||
extern int zip_io_issue(ZFile* zf, off_t ofs, size_t size, void* buf, ZipIo* io);
|
extern LibError zip_io_issue(ZFile* zf, off_t ofs, size_t size, void* buf, ZipIo* io);
|
||||||
|
|
||||||
// indicates if the IO referenced by <io> has completed.
|
// indicates if the IO referenced by <io> has completed.
|
||||||
// return value: 0 if pending, 1 if complete, < 0 on error.
|
// return value: 0 if pending, 1 if complete, < 0 on error.
|
||||||
@ -107,12 +107,12 @@ extern int zip_io_has_completed(ZipIo* io);
|
|||||||
|
|
||||||
// wait until the transfer <io> completes, and return its buffer.
|
// wait until the transfer <io> completes, and return its buffer.
|
||||||
// output parameters are zeroed on error.
|
// output parameters are zeroed on error.
|
||||||
extern int zip_io_wait(ZipIo* io, void*& p, size_t& size);
|
extern LibError zip_io_wait(ZipIo* io, void*& p, size_t& size);
|
||||||
|
|
||||||
// finished with transfer <io> - free its buffer (returned by zip_io_wait)
|
// finished with transfer <io> - free its buffer (returned by zip_io_wait)
|
||||||
extern int zip_io_discard(ZipIo* io);
|
extern LibError zip_io_discard(ZipIo* io);
|
||||||
|
|
||||||
extern int zip_io_validate(const ZipIo* io);
|
extern LibError zip_io_validate(const ZipIo* io);
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -152,14 +152,14 @@ extern ssize_t zip_read(ZFile* zf, off_t ofs, size_t size, void* buf, FileIOCB c
|
|||||||
// the mapping will be removed (if still open) when its archive is closed.
|
// the mapping will be removed (if still open) when its archive is closed.
|
||||||
// however, map/unmap calls should still be paired so that the archive mapping
|
// however, map/unmap calls should still be paired so that the archive mapping
|
||||||
// may be removed when no longer needed.
|
// may be removed when no longer needed.
|
||||||
extern int zip_map(ZFile* zf, void*& p, size_t& size);
|
extern LibError zip_map(ZFile* zf, void*& p, size_t& size);
|
||||||
|
|
||||||
// remove the mapping of file <zf>; fail if not mapped.
|
// remove the mapping of file <zf>; fail if not mapped.
|
||||||
//
|
//
|
||||||
// the mapping will be removed (if still open) when its archive is closed.
|
// the mapping will be removed (if still open) when its archive is closed.
|
||||||
// however, map/unmap calls should be paired so that the archive mapping
|
// however, map/unmap calls should be paired so that the archive mapping
|
||||||
// may be removed when no longer needed.
|
// may be removed when no longer needed.
|
||||||
extern int zip_unmap(ZFile* zf);
|
extern LibError zip_unmap(ZFile* zf);
|
||||||
|
|
||||||
|
|
||||||
#endif // #ifndef ZIP_H__
|
#endif // #ifndef ZIP_H__
|
||||||
|
@ -64,7 +64,7 @@ class GLCursor
|
|||||||
uint hotspotx, hotspoty;
|
uint hotspotx, hotspoty;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
int create(const char* filename, uint hotspotx_, uint hotspoty_)
|
LibError create(const char* filename, uint hotspotx_, uint hotspoty_)
|
||||||
{
|
{
|
||||||
ht = ogl_tex_load(filename);
|
ht = ogl_tex_load(filename);
|
||||||
RETURN_ERR(ht);
|
RETURN_ERR(ht);
|
||||||
@ -75,7 +75,7 @@ public:
|
|||||||
|
|
||||||
(void)ogl_tex_set_filter(ht, GL_NEAREST);
|
(void)ogl_tex_set_filter(ht, GL_NEAREST);
|
||||||
(void)ogl_tex_upload(ht);
|
(void)ogl_tex_upload(ht);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void destroy()
|
void destroy()
|
||||||
@ -109,10 +109,10 @@ public:
|
|||||||
{
|
{
|
||||||
const uint A = 128; // no cursor is expected to get this big
|
const uint A = 128; // no cursor is expected to get this big
|
||||||
if(w > A || h > A || hotspotx > A || hotspoty > A)
|
if(w > A || h > A || hotspotx > A || hotspoty > A)
|
||||||
return -2;
|
return ERR_1;
|
||||||
if(ht < 0)
|
if(ht < 0)
|
||||||
return -3;
|
return ERR_2;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -141,7 +141,7 @@ static void Cursor_dtor(Cursor* c)
|
|||||||
c->gl_cursor.destroy();
|
c->gl_cursor.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
static int Cursor_reload(Cursor* c, const char* name, Handle)
|
static LibError Cursor_reload(Cursor* c, const char* name, Handle)
|
||||||
{
|
{
|
||||||
char filename[VFS_MAX_PATH];
|
char filename[VFS_MAX_PATH];
|
||||||
|
|
||||||
@ -166,24 +166,24 @@ static int Cursor_reload(Cursor* c, const char* name, Handle)
|
|||||||
if(!c->sys_cursor)
|
if(!c->sys_cursor)
|
||||||
RETURN_ERR(c->gl_cursor.create(filename, hotspotx, hotspoty));
|
RETURN_ERR(c->gl_cursor.create(filename, hotspotx, hotspoty));
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int Cursor_validate(const Cursor* c)
|
static LibError Cursor_validate(const Cursor* c)
|
||||||
{
|
{
|
||||||
// note: system cursors have no state to speak of, so we don't need to
|
// note: system cursors have no state to speak of, so we don't need to
|
||||||
// validate them.
|
// validate them.
|
||||||
|
|
||||||
if(!c->sys_cursor)
|
if(!c->sys_cursor)
|
||||||
RETURN_ERR(c->gl_cursor.validate());
|
RETURN_ERR(c->gl_cursor.validate());
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int Cursor_to_string(const Cursor* c, char* buf)
|
static LibError Cursor_to_string(const Cursor* c, char* buf)
|
||||||
{
|
{
|
||||||
const char* type = c->sys_cursor? "sys" : "gl";
|
const char* type = c->sys_cursor? "sys" : "gl";
|
||||||
snprintf(buf, H_STRING_LEN, "(%s)", type);
|
snprintf(buf, H_STRING_LEN, "(%s)", type);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -198,7 +198,7 @@ static Handle cursor_load(const char* name)
|
|||||||
return h_alloc(H_Cursor, name, 0);
|
return h_alloc(H_Cursor, name, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cursor_free(Handle& h)
|
static LibError cursor_free(Handle& h)
|
||||||
{
|
{
|
||||||
return h_free(h, H_Cursor);
|
return h_free(h, H_Cursor);
|
||||||
}
|
}
|
||||||
@ -208,13 +208,13 @@ static int cursor_free(Handle& h)
|
|||||||
// (origin is top-left to match the windowing system).
|
// (origin is top-left to match the windowing system).
|
||||||
// uses a hardware mouse cursor where available, otherwise a
|
// uses a hardware mouse cursor where available, otherwise a
|
||||||
// portable OpenGL implementation.
|
// portable OpenGL implementation.
|
||||||
int cursor_draw(const char* name, int x, int y)
|
LibError cursor_draw(const char* name, int x, int y)
|
||||||
{
|
{
|
||||||
// Use 'null' to disable the cursor
|
// Use 'null' to disable the cursor
|
||||||
if(!name)
|
if(!name)
|
||||||
{
|
{
|
||||||
WARN_ERR(sys_cursor_set(0));
|
WARN_ERR(sys_cursor_set(0));
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle hc = cursor_load(name);
|
Handle hc = cursor_load(name);
|
||||||
@ -227,5 +227,5 @@ int cursor_draw(const char* name, int x, int y)
|
|||||||
c->gl_cursor.draw(x, y);
|
c->gl_cursor.draw(x, y);
|
||||||
|
|
||||||
(void)cursor_free(hc);
|
(void)cursor_free(hc);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
// (origin is top-left to match the windowing system).
|
// (origin is top-left to match the windowing system).
|
||||||
// uses a hardware mouse cursor where available, otherwise a
|
// uses a hardware mouse cursor where available, otherwise a
|
||||||
// portable OpenGL implementation.
|
// portable OpenGL implementation.
|
||||||
extern int cursor_draw(const char* name, int x, int y);
|
extern LibError cursor_draw(const char* name, int x, int y);
|
||||||
|
|
||||||
// internal use only:
|
// internal use only:
|
||||||
extern int g_yres;
|
extern int g_yres;
|
||||||
|
@ -72,12 +72,12 @@ static void Ogl_Shader_init(Ogl_Shader* shdr, va_list args)
|
|||||||
// have absolutely no effect on a program object that contains these shaders
|
// have absolutely no effect on a program object that contains these shaders
|
||||||
// when the program object is already linked.
|
// when the program object is already linked.
|
||||||
// So, how can we inform the "parent object" (i.e. the program object) of our change?
|
// So, how can we inform the "parent object" (i.e. the program object) of our change?
|
||||||
static int Ogl_Shader_reload(Ogl_Shader* shdr, const char* filename, Handle UNUSED(h))
|
static LibError Ogl_Shader_reload(Ogl_Shader* shdr, const char* filename, Handle UNUSED(h))
|
||||||
{
|
{
|
||||||
int err = -666;
|
LibError err = ERR_FAIL;
|
||||||
|
|
||||||
if (shdr->id)
|
if (shdr->id)
|
||||||
return 0;
|
return ERR_OK;
|
||||||
|
|
||||||
void* file;
|
void* file;
|
||||||
size_t file_size;
|
size_t file_size;
|
||||||
@ -140,7 +140,7 @@ static int Ogl_Shader_reload(Ogl_Shader* shdr, const char* filename, Handle UNUS
|
|||||||
}
|
}
|
||||||
|
|
||||||
mem_free_h(hm);
|
mem_free_h(hm);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
|
|
||||||
fail_shadercreated:
|
fail_shadercreated:
|
||||||
pglDeleteObjectARB(shdr->id);
|
pglDeleteObjectARB(shdr->id);
|
||||||
@ -162,16 +162,16 @@ static void Ogl_Shader_dtor(Ogl_Shader* shdr)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int Ogl_Shader_validate(const Ogl_Shader* UNUSED(shdr))
|
static LibError Ogl_Shader_validate(const Ogl_Shader* UNUSED(shdr))
|
||||||
{
|
{
|
||||||
// TODO
|
// TODO
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int Ogl_Shader_to_string(const Ogl_Shader* UNUSED(shdr), char* buf)
|
static LibError Ogl_Shader_to_string(const Ogl_Shader* UNUSED(shdr), char* buf)
|
||||||
{
|
{
|
||||||
snprintf(buf, H_STRING_LEN, "");
|
snprintf(buf, H_STRING_LEN, "");
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -195,7 +195,7 @@ void ogl_shader_free(Handle& h)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Attach a shader to the given OpenGL program.
|
// Attach a shader to the given OpenGL program.
|
||||||
int ogl_shader_attach(GLhandleARB program, Handle& h)
|
LibError ogl_shader_attach(GLhandleARB program, Handle& h)
|
||||||
{
|
{
|
||||||
H_DEREF(h, Ogl_Shader, shdr);
|
H_DEREF(h, Ogl_Shader, shdr);
|
||||||
|
|
||||||
@ -204,7 +204,7 @@ int ogl_shader_attach(GLhandleARB program, Handle& h)
|
|||||||
|
|
||||||
pglAttachObjectARB(program, shdr->id);
|
pglAttachObjectARB(program, shdr->id);
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -230,7 +230,7 @@ static void Ogl_Program_init(Ogl_Program* UNUSED(p), va_list UNUSED(args))
|
|||||||
|
|
||||||
// Load the shader associated with one Shader element,
|
// Load the shader associated with one Shader element,
|
||||||
// and attach it to our program object.
|
// and attach it to our program object.
|
||||||
static int do_load_shader(
|
static LibError do_load_shader(
|
||||||
Ogl_Program* p, const char* filename, Handle UNUSED(h),
|
Ogl_Program* p, const char* filename, Handle UNUSED(h),
|
||||||
const CXeromyces& XeroFile, const XMBElement& Shader)
|
const CXeromyces& XeroFile, const XMBElement& Shader)
|
||||||
{
|
{
|
||||||
@ -275,15 +275,15 @@ static int do_load_shader(
|
|||||||
// TODO: How will this work with automatic reload?
|
// TODO: How will this work with automatic reload?
|
||||||
ogl_shader_free(hshader);
|
ogl_shader_free(hshader);
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Reload the program object from the source file.
|
// Reload the program object from the source file.
|
||||||
static int Ogl_Program_reload(Ogl_Program* p, const char* filename, Handle h)
|
static LibError Ogl_Program_reload(Ogl_Program* p, const char* filename, Handle h)
|
||||||
{
|
{
|
||||||
if (p->id)
|
if (p->id)
|
||||||
return 0;
|
return ERR_OK;
|
||||||
|
|
||||||
oglCheck();
|
oglCheck();
|
||||||
|
|
||||||
@ -338,9 +338,7 @@ static int Ogl_Program_reload(Ogl_Program* p, const char* filename, Handle h)
|
|||||||
return ERR_CORRUPTED;
|
return ERR_CORRUPTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ret = do_load_shader(p, filename, h, XeroFile, Shader);
|
RETURN_ERR(do_load_shader(p, filename, h, XeroFile, Shader));
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -371,7 +369,7 @@ static int Ogl_Program_reload(Ogl_Program* p, const char* filename, Handle h)
|
|||||||
return ERR_SHDR_LINK;
|
return ERR_SHDR_LINK;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -385,16 +383,16 @@ static void Ogl_Program_dtor(Ogl_Program* p)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int Ogl_Program_validate(const Ogl_Program* UNUSED(p))
|
static LibError Ogl_Program_validate(const Ogl_Program* UNUSED(p))
|
||||||
{
|
{
|
||||||
// TODO
|
// TODO
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int Ogl_Program_to_string(const Ogl_Program* UNUSED(p), char* buf)
|
static LibError Ogl_Program_to_string(const Ogl_Program* UNUSED(p), char* buf)
|
||||||
{
|
{
|
||||||
snprintf(buf, H_STRING_LEN, "");
|
snprintf(buf, H_STRING_LEN, "");
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -417,12 +415,12 @@ void ogl_program_free(Handle& h)
|
|||||||
|
|
||||||
// Activate the program (glUseProgramObjectARB).
|
// Activate the program (glUseProgramObjectARB).
|
||||||
// h may be 0, in which case program objects are disabled.
|
// h may be 0, in which case program objects are disabled.
|
||||||
int ogl_program_use(Handle h)
|
LibError ogl_program_use(Handle h)
|
||||||
{
|
{
|
||||||
if (!h)
|
if (!h)
|
||||||
{
|
{
|
||||||
pglUseProgramObjectARB(0);
|
pglUseProgramObjectARB(0);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ogl_Program* p = H_USER_DATA(h, Ogl_Program);
|
Ogl_Program* p = H_USER_DATA(h, Ogl_Program);
|
||||||
@ -434,7 +432,7 @@ int ogl_program_use(Handle h)
|
|||||||
}
|
}
|
||||||
|
|
||||||
pglUseProgramObjectARB(p->id);
|
pglUseProgramObjectARB(p->id);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ void ogl_shader_free(Handle& h);
|
|||||||
|
|
||||||
// Attach a shader to the given OpenGL program.
|
// Attach a shader to the given OpenGL program.
|
||||||
// Returns 0 on success and a negative error code otherwise.
|
// Returns 0 on success and a negative error code otherwise.
|
||||||
int ogl_shader_attach(GLhandleARB program, Handle& h);
|
LibError ogl_shader_attach(GLhandleARB program, Handle& h);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -42,7 +42,7 @@ void ogl_program_free(Handle& h);
|
|||||||
|
|
||||||
// Activate the program (glUseProgramObjectARB).
|
// Activate the program (glUseProgramObjectARB).
|
||||||
// h may be 0, in which case program objects are disabled.
|
// h may be 0, in which case program objects are disabled.
|
||||||
int ogl_program_use(Handle h);
|
LibError ogl_program_use(Handle h);
|
||||||
|
|
||||||
// Query uniform information
|
// Query uniform information
|
||||||
GLint ogl_program_get_uniform_location(Handle h, const char* name);
|
GLint ogl_program_get_uniform_location(Handle h, const char* name);
|
||||||
|
@ -404,11 +404,11 @@ static void OglTex_dtor(OglTex* ot)
|
|||||||
ot->flags &= ~OT_IS_UPLOADED;
|
ot->flags &= ~OT_IS_UPLOADED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int OglTex_reload(OglTex* ot, const char* fn, Handle h)
|
static LibError OglTex_reload(OglTex* ot, const char* fn, Handle h)
|
||||||
{
|
{
|
||||||
// we're reusing a freed but still in-memory OglTex object
|
// we're reusing a freed but still in-memory OglTex object
|
||||||
if(ot->flags & OT_IS_UPLOADED)
|
if(ot->flags & OT_IS_UPLOADED)
|
||||||
return 0;
|
return ERR_OK;
|
||||||
|
|
||||||
// if we don't already have the texture in memory (*), load from file.
|
// if we don't already have the texture in memory (*), load from file.
|
||||||
// * this happens if the texture is "wrapped".
|
// * this happens if the texture is "wrapped".
|
||||||
@ -423,10 +423,10 @@ static int OglTex_reload(OglTex* ot, const char* fn, Handle h)
|
|||||||
if(ot->flags & OT_NEED_AUTO_UPLOAD)
|
if(ot->flags & OT_NEED_AUTO_UPLOAD)
|
||||||
(void)ogl_tex_upload(h);
|
(void)ogl_tex_upload(h);
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int OglTex_validate(const OglTex* ot)
|
static LibError OglTex_validate(const OglTex* ot)
|
||||||
{
|
{
|
||||||
RETURN_ERR(tex_validate(&ot->t));
|
RETURN_ERR(tex_validate(&ot->t));
|
||||||
|
|
||||||
@ -437,41 +437,41 @@ static int OglTex_validate(const OglTex* ot)
|
|||||||
GLsizei h = (GLsizei)ot->t.h;
|
GLsizei h = (GLsizei)ot->t.h;
|
||||||
// .. == 0; texture file probably not loaded successfully.
|
// .. == 0; texture file probably not loaded successfully.
|
||||||
if(w == 0 || h == 0)
|
if(w == 0 || h == 0)
|
||||||
return -100;
|
return ERR_11;
|
||||||
// .. greater than max supported tex dimension.
|
// .. greater than max supported tex dimension.
|
||||||
// no-op if oglInit not yet called
|
// no-op if oglInit not yet called
|
||||||
if(w > (GLsizei)ogl_max_tex_size || h > (GLsizei)ogl_max_tex_size)
|
if(w > (GLsizei)ogl_max_tex_size || h > (GLsizei)ogl_max_tex_size)
|
||||||
return -101;
|
return ERR_12;
|
||||||
// .. not power-of-2.
|
// .. not power-of-2.
|
||||||
// note: we can't work around this because both NV_texture_rectangle
|
// note: we can't work around this because both NV_texture_rectangle
|
||||||
// and subtexture require work for the client (changing tex coords).
|
// and subtexture require work for the client (changing tex coords).
|
||||||
// TODO: ARB_texture_non_power_of_two
|
// TODO: ARB_texture_non_power_of_two
|
||||||
if(!is_pow2(w) || !is_pow2(h))
|
if(!is_pow2(w) || !is_pow2(h))
|
||||||
return -102;
|
return ERR_13;
|
||||||
|
|
||||||
// texture state
|
// texture state
|
||||||
if(!filter_valid(ot->state.filter))
|
if(!filter_valid(ot->state.filter))
|
||||||
return -103;
|
return ERR_14;
|
||||||
if(!wrap_valid(ot->state.wrap))
|
if(!wrap_valid(ot->state.wrap))
|
||||||
return -104;
|
return ERR_15;
|
||||||
|
|
||||||
// misc
|
// misc
|
||||||
if(!q_flags_valid(ot->q_flags))
|
if(!q_flags_valid(ot->q_flags))
|
||||||
return -105;
|
return ERR_16;
|
||||||
if(ot->tmu >= 128) // unexpected that there will ever be this many
|
if(ot->tmu >= 128) // unexpected that there will ever be this many
|
||||||
return -106;
|
return ERR_17;
|
||||||
if(ot->flags > OT_ALL_FLAGS)
|
if(ot->flags > OT_ALL_FLAGS)
|
||||||
return -107;
|
return ERR_18;
|
||||||
// .. note: don't check ot->fmt and ot->int_fmt - they aren't set
|
// .. note: don't check ot->fmt and ot->int_fmt - they aren't set
|
||||||
// until during ogl_tex_upload.
|
// until during ogl_tex_upload.
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int OglTex_to_string(const OglTex* ot, char* buf)
|
static LibError OglTex_to_string(const OglTex* ot, char* buf)
|
||||||
{
|
{
|
||||||
snprintf(buf, H_STRING_LEN, "id=%d", ot->id);
|
snprintf(buf, H_STRING_LEN, "id=%d", ot->id);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -520,7 +520,7 @@ Handle ogl_tex_wrap(Tex* t, const char* fn, uint flags)
|
|||||||
|
|
||||||
// free all resources associated with the texture and make further
|
// free all resources associated with the texture and make further
|
||||||
// use of it impossible. (subject to refcount)
|
// use of it impossible. (subject to refcount)
|
||||||
int ogl_tex_free(Handle& ht)
|
LibError ogl_tex_free(Handle& ht)
|
||||||
{
|
{
|
||||||
return h_free(ht, H_OglTex);
|
return h_free(ht, H_OglTex);
|
||||||
}
|
}
|
||||||
@ -572,7 +572,7 @@ static void warn_if_uploaded(Handle ht, const OglTex* ot)
|
|||||||
// must be called before uploading (raises a warning if called afterwards).
|
// must be called before uploading (raises a warning if called afterwards).
|
||||||
// filter is as defined by OpenGL; it is applied for both minification and
|
// filter is as defined by OpenGL; it is applied for both minification and
|
||||||
// magnification (for rationale and details, see OglTexState)
|
// magnification (for rationale and details, see OglTexState)
|
||||||
int ogl_tex_set_filter(Handle ht, GLint filter)
|
LibError ogl_tex_set_filter(Handle ht, GLint filter)
|
||||||
{
|
{
|
||||||
H_DEREF(ht, OglTex, ot);
|
H_DEREF(ht, OglTex, ot);
|
||||||
|
|
||||||
@ -584,7 +584,7 @@ int ogl_tex_set_filter(Handle ht, GLint filter)
|
|||||||
warn_if_uploaded(ht, ot);
|
warn_if_uploaded(ht, ot);
|
||||||
ot->state.filter = filter;
|
ot->state.filter = filter;
|
||||||
}
|
}
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -592,7 +592,7 @@ int ogl_tex_set_filter(Handle ht, GLint filter)
|
|||||||
// must be called before uploading (raises a warning if called afterwards).
|
// must be called before uploading (raises a warning if called afterwards).
|
||||||
// wrap is as defined by OpenGL and applies to both S and T coordinates
|
// wrap is as defined by OpenGL and applies to both S and T coordinates
|
||||||
// (rationale: see OglTexState).
|
// (rationale: see OglTexState).
|
||||||
int ogl_tex_set_wrap(Handle ht, GLint wrap)
|
LibError ogl_tex_set_wrap(Handle ht, GLint wrap)
|
||||||
{
|
{
|
||||||
H_DEREF(ht, OglTex, ot);
|
H_DEREF(ht, OglTex, ot);
|
||||||
|
|
||||||
@ -604,7 +604,7 @@ int ogl_tex_set_wrap(Handle ht, GLint wrap)
|
|||||||
warn_if_uploaded(ht, ot);
|
warn_if_uploaded(ht, ot);
|
||||||
ot->state.wrap = wrap;
|
ot->state.wrap = wrap;
|
||||||
}
|
}
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -676,7 +676,7 @@ static void detect_gl_upload_caps()
|
|||||||
// whether mipmaps are needed and the quality settings).
|
// whether mipmaps are needed and the quality settings).
|
||||||
// returns 0 to indicate success; otherwise, caller must disable
|
// returns 0 to indicate success; otherwise, caller must disable
|
||||||
// mipmapping by switching filter to e.g. GL_LINEAR.
|
// mipmapping by switching filter to e.g. GL_LINEAR.
|
||||||
static int get_mipmaps(Tex* t, GLint filter, uint q_flags, int* plevels_to_skip)
|
static LibError get_mipmaps(Tex* t, GLint filter, uint q_flags, int* plevels_to_skip)
|
||||||
{
|
{
|
||||||
// decisions:
|
// decisions:
|
||||||
// .. does filter call for uploading mipmaps?
|
// .. does filter call for uploading mipmaps?
|
||||||
@ -689,7 +689,7 @@ static int get_mipmaps(Tex* t, GLint filter, uint q_flags, int* plevels_to_skip)
|
|||||||
|
|
||||||
*plevels_to_skip = TEX_BASE_LEVEL_ONLY;
|
*plevels_to_skip = TEX_BASE_LEVEL_ONLY;
|
||||||
if(!need_mipmaps)
|
if(!need_mipmaps)
|
||||||
return 0;
|
return ERR_OK;
|
||||||
|
|
||||||
// image already contains pregenerated mipmaps; we need do nothing.
|
// image already contains pregenerated mipmaps; we need do nothing.
|
||||||
// this is the nicest case, because they are fastest to load
|
// this is the nicest case, because they are fastest to load
|
||||||
@ -712,7 +712,7 @@ static int get_mipmaps(Tex* t, GLint filter, uint q_flags, int* plevels_to_skip)
|
|||||||
// all<->all transforms aren't implemented, it'd have to decompress
|
// all<->all transforms aren't implemented, it'd have to decompress
|
||||||
// from S3TC first), and DDS images ought to include mipmaps!
|
// from S3TC first), and DDS images ought to include mipmaps!
|
||||||
else if(is_s3tc)
|
else if(is_s3tc)
|
||||||
return -1;
|
return ERR_FAIL;
|
||||||
// image is uncompressed and we're on an old OpenGL implementation;
|
// image is uncompressed and we're on an old OpenGL implementation;
|
||||||
// we will generate mipmaps in software.
|
// we will generate mipmaps in software.
|
||||||
else
|
else
|
||||||
@ -734,7 +734,7 @@ static int get_mipmaps(Tex* t, GLint filter, uint q_flags, int* plevels_to_skip)
|
|||||||
*plevels_to_skip = log2(reduce);
|
*plevels_to_skip = log2(reduce);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -790,7 +790,7 @@ static void upload_impl(Tex* t, GLenum fmt, GLint int_fmt, int levels_to_skip)
|
|||||||
// side effects:
|
// side effects:
|
||||||
// - enables texturing on TMU 0 and binds the texture to it;
|
// - enables texturing on TMU 0 and binds the texture to it;
|
||||||
// - frees the texel data! see ogl_tex_get_data.
|
// - frees the texel data! see ogl_tex_get_data.
|
||||||
int ogl_tex_upload(const Handle ht, GLenum fmt_ovr, uint q_flags_ovr, GLint int_fmt_ovr)
|
LibError ogl_tex_upload(const Handle ht, GLenum fmt_ovr, uint q_flags_ovr, GLint int_fmt_ovr)
|
||||||
{
|
{
|
||||||
ONCE(detect_gl_upload_caps());
|
ONCE(detect_gl_upload_caps());
|
||||||
|
|
||||||
@ -805,7 +805,7 @@ int ogl_tex_upload(const Handle ht, GLenum fmt_ovr, uint q_flags_ovr, GLint int_
|
|||||||
// upload already happened; no work to do.
|
// upload already happened; no work to do.
|
||||||
// (this also happens if a cached texture is "loaded")
|
// (this also happens if a cached texture is "loaded")
|
||||||
if(ot->flags & OT_IS_UPLOADED)
|
if(ot->flags & OT_IS_UPLOADED)
|
||||||
return 0;
|
return ERR_OK;
|
||||||
|
|
||||||
debug_assert(ot->flags & OT_TEX_VALID);
|
debug_assert(ot->flags & OT_TEX_VALID);
|
||||||
|
|
||||||
@ -850,7 +850,7 @@ int ogl_tex_upload(const Handle ht, GLenum fmt_ovr, uint q_flags_ovr, GLint int_
|
|||||||
ot->flags &= ~OT_TEX_VALID;
|
ot->flags &= ~OT_TEX_VALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -860,7 +860,7 @@ int ogl_tex_upload(const Handle ht, GLenum fmt_ovr, uint q_flags_ovr, GLint int_
|
|||||||
|
|
||||||
// retrieve texture dimensions and bits per pixel.
|
// retrieve texture dimensions and bits per pixel.
|
||||||
// all params are optional and filled if non-NULL.
|
// all params are optional and filled if non-NULL.
|
||||||
int ogl_tex_get_size(Handle ht, uint* w, uint* h, uint* bpp)
|
LibError ogl_tex_get_size(Handle ht, uint* w, uint* h, uint* bpp)
|
||||||
{
|
{
|
||||||
H_DEREF(ht, OglTex, ot);
|
H_DEREF(ht, OglTex, ot);
|
||||||
|
|
||||||
@ -870,14 +870,14 @@ int ogl_tex_get_size(Handle ht, uint* w, uint* h, uint* bpp)
|
|||||||
*h = ot->t.h;
|
*h = ot->t.h;
|
||||||
if(bpp)
|
if(bpp)
|
||||||
*bpp = ot->t.bpp;
|
*bpp = ot->t.bpp;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// retrieve Tex.flags and the corresponding OpenGL format.
|
// retrieve Tex.flags and the corresponding OpenGL format.
|
||||||
// the latter is determined during ogl_tex_upload and is 0 before that.
|
// the latter is determined during ogl_tex_upload and is 0 before that.
|
||||||
// all params are optional and filled if non-NULL.
|
// all params are optional and filled if non-NULL.
|
||||||
int ogl_tex_get_format(Handle ht, uint* flags, GLenum* fmt)
|
LibError ogl_tex_get_format(Handle ht, uint* flags, GLenum* fmt)
|
||||||
{
|
{
|
||||||
H_DEREF(ht, OglTex, ot);
|
H_DEREF(ht, OglTex, ot);
|
||||||
|
|
||||||
@ -889,7 +889,7 @@ int ogl_tex_get_format(Handle ht, uint* flags, GLenum* fmt)
|
|||||||
debug_warn("hasn't been defined yet!");
|
debug_warn("hasn't been defined yet!");
|
||||||
*fmt = ot->fmt;
|
*fmt = ot->fmt;
|
||||||
}
|
}
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -900,12 +900,12 @@ int ogl_tex_get_format(Handle ht, uint* flags, GLenum* fmt)
|
|||||||
// the function doesn't fail (negative return value) by design.
|
// the function doesn't fail (negative return value) by design.
|
||||||
// if you still need to get at the data, add a reference before
|
// if you still need to get at the data, add a reference before
|
||||||
// uploading it or read directly from OpenGL (discouraged).
|
// uploading it or read directly from OpenGL (discouraged).
|
||||||
int ogl_tex_get_data(Handle ht, void** p)
|
LibError ogl_tex_get_data(Handle ht, void** p)
|
||||||
{
|
{
|
||||||
H_DEREF(ht, OglTex, ot);
|
H_DEREF(ht, OglTex, ot);
|
||||||
|
|
||||||
*p = tex_get_data(&ot->t);
|
*p = tex_get_data(&ot->t);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -924,7 +924,7 @@ int ogl_tex_get_data(Handle ht, void** p)
|
|||||||
// - assumes multitexturing is available.
|
// - assumes multitexturing is available.
|
||||||
// - not necessary before calling ogl_tex_upload!
|
// - not necessary before calling ogl_tex_upload!
|
||||||
// - on error, the unit's texture state is unchanged; see implementation.
|
// - on error, the unit's texture state is unchanged; see implementation.
|
||||||
int ogl_tex_bind(Handle ht, uint unit)
|
LibError ogl_tex_bind(Handle ht, uint unit)
|
||||||
{
|
{
|
||||||
// note: there are many call sites of glActiveTextureARB, so caching
|
// note: there are many call sites of glActiveTextureARB, so caching
|
||||||
// those and ignoring redundant sets isn't feasible.
|
// those and ignoring redundant sets isn't feasible.
|
||||||
@ -934,7 +934,7 @@ int ogl_tex_bind(Handle ht, uint unit)
|
|||||||
if(ht == 0)
|
if(ht == 0)
|
||||||
{
|
{
|
||||||
glDisable(GL_TEXTURE_2D);
|
glDisable(GL_TEXTURE_2D);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if this fails, the texture unit's state remains unchanged.
|
// if this fails, the texture unit's state remains unchanged.
|
||||||
@ -950,25 +950,25 @@ int ogl_tex_bind(Handle ht, uint unit)
|
|||||||
|
|
||||||
glEnable(GL_TEXTURE_2D);
|
glEnable(GL_TEXTURE_2D);
|
||||||
glBindTexture(GL_TEXTURE_2D, ot->id);
|
glBindTexture(GL_TEXTURE_2D, ot->id);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// apply the specified transforms (as in tex_transform) to the image.
|
// apply the specified transforms (as in tex_transform) to the image.
|
||||||
// must be called before uploading (raises a warning if called afterwards).
|
// must be called before uploading (raises a warning if called afterwards).
|
||||||
int ogl_tex_transform(Handle ht, uint transforms)
|
LibError ogl_tex_transform(Handle ht, uint transforms)
|
||||||
{
|
{
|
||||||
H_DEREF(ht, OglTex, ot);
|
H_DEREF(ht, OglTex, ot);
|
||||||
int ret = tex_transform(&ot->t, transforms);
|
LibError ret = tex_transform(&ot->t, transforms);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// change the pixel format to that specified by <new_flags>.
|
// change the pixel format to that specified by <new_flags>.
|
||||||
// (note: this is equivalent to ogl_tex_transform(ht, ht_flags^new_flags).
|
// (note: this is equivalent to ogl_tex_transform(ht, ht_flags^new_flags).
|
||||||
int ogl_tex_transform_to(Handle ht, uint new_flags)
|
LibError ogl_tex_transform_to(Handle ht, uint new_flags)
|
||||||
{
|
{
|
||||||
H_DEREF(ht, OglTex, ot);
|
H_DEREF(ht, OglTex, ot);
|
||||||
int ret = tex_transform_to(&ot->t, new_flags);
|
LibError ret = tex_transform_to(&ot->t, new_flags);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -214,7 +214,7 @@ extern Handle ogl_tex_wrap(Tex* t, const char* fn = 0, uint flags = 0);
|
|||||||
|
|
||||||
// free all resources associated with the texture and make further
|
// free all resources associated with the texture and make further
|
||||||
// use of it impossible. (subject to refcount)
|
// use of it impossible. (subject to refcount)
|
||||||
extern int ogl_tex_free(Handle& ht);
|
extern LibError ogl_tex_free(Handle& ht);
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -228,13 +228,13 @@ extern int ogl_tex_free(Handle& ht);
|
|||||||
// must be called before uploading (raises a warning if called afterwards).
|
// must be called before uploading (raises a warning if called afterwards).
|
||||||
// filter is as defined by OpenGL; it is applied for both minification and
|
// filter is as defined by OpenGL; it is applied for both minification and
|
||||||
// magnification (for rationale and details, see OglTexState)
|
// magnification (for rationale and details, see OglTexState)
|
||||||
extern int ogl_tex_set_filter(Handle ht, GLint filter);
|
extern LibError ogl_tex_set_filter(Handle ht, GLint filter);
|
||||||
|
|
||||||
// override default wrap mode (GL_REPEAT) for this texture.
|
// override default wrap mode (GL_REPEAT) for this texture.
|
||||||
// must be called before uploading (raises a warning if called afterwards).
|
// must be called before uploading (raises a warning if called afterwards).
|
||||||
// wrap is as defined by OpenGL and applies to both S and T coordinates
|
// wrap is as defined by OpenGL and applies to both S and T coordinates
|
||||||
// (rationale: see OglTexState).
|
// (rationale: see OglTexState).
|
||||||
extern int ogl_tex_set_wrap(Handle ht, GLint wrap);
|
extern LibError ogl_tex_set_wrap(Handle ht, GLint wrap);
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -266,7 +266,7 @@ extern void ogl_tex_override(OglTexOverrides what, OglTexAllow allow);
|
|||||||
// side effects:
|
// side effects:
|
||||||
// - enables texturing on TMU 0 and binds the texture to it;
|
// - enables texturing on TMU 0 and binds the texture to it;
|
||||||
// - frees the texel data! see ogl_tex_get_data.
|
// - frees the texel data! see ogl_tex_get_data.
|
||||||
extern int ogl_tex_upload(const Handle ht, GLenum fmt_ovr = 0, uint q_flags_ovr = 0, GLint int_fmt_ovr = 0);
|
extern LibError ogl_tex_upload(const Handle ht, GLenum fmt_ovr = 0, uint q_flags_ovr = 0, GLint int_fmt_ovr = 0);
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -275,12 +275,12 @@ extern int ogl_tex_upload(const Handle ht, GLenum fmt_ovr = 0, uint q_flags_ovr
|
|||||||
|
|
||||||
// retrieve texture dimensions and bits per pixel.
|
// retrieve texture dimensions and bits per pixel.
|
||||||
// all params are optional and filled if non-NULL.
|
// all params are optional and filled if non-NULL.
|
||||||
extern int ogl_tex_get_size(Handle ht, uint* w, uint* h, uint* bpp);
|
extern LibError ogl_tex_get_size(Handle ht, uint* w, uint* h, uint* bpp);
|
||||||
|
|
||||||
// retrieve Tex.flags and the corresponding OpenGL format.
|
// retrieve Tex.flags and the corresponding OpenGL format.
|
||||||
// the latter is determined during ogl_tex_upload and is 0 before that.
|
// the latter is determined during ogl_tex_upload and is 0 before that.
|
||||||
// all params are optional and filled if non-NULL.
|
// all params are optional and filled if non-NULL.
|
||||||
extern int ogl_tex_get_format(Handle ht, uint* flags, GLenum* fmt);
|
extern LibError ogl_tex_get_format(Handle ht, uint* flags, GLenum* fmt);
|
||||||
|
|
||||||
// retrieve pointer to texel data.
|
// retrieve pointer to texel data.
|
||||||
//
|
//
|
||||||
@ -289,7 +289,7 @@ extern int ogl_tex_get_format(Handle ht, uint* flags, GLenum* fmt);
|
|||||||
// the function doesn't fail (negative return value) by design.
|
// the function doesn't fail (negative return value) by design.
|
||||||
// if you still need to get at the data, add a reference before
|
// if you still need to get at the data, add a reference before
|
||||||
// uploading it or read directly from OpenGL (discouraged).
|
// uploading it or read directly from OpenGL (discouraged).
|
||||||
extern int ogl_tex_get_data(Handle ht, void** p);
|
extern LibError ogl_tex_get_data(Handle ht, void** p);
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -307,14 +307,14 @@ extern int ogl_tex_get_data(Handle ht, void** p);
|
|||||||
// - assumes multitexturing is available.
|
// - assumes multitexturing is available.
|
||||||
// - not necessary before calling ogl_tex_upload!
|
// - not necessary before calling ogl_tex_upload!
|
||||||
// - on error, the unit's texture state is unchanged; see implementation.
|
// - on error, the unit's texture state is unchanged; see implementation.
|
||||||
extern int ogl_tex_bind(Handle ht, uint unit = 0);
|
extern LibError ogl_tex_bind(Handle ht, uint unit = 0);
|
||||||
|
|
||||||
// apply the specified transforms (as in tex_transform) to the image.
|
// apply the specified transforms (as in tex_transform) to the image.
|
||||||
// must be called before uploading (raises a warning if called afterwards).
|
// must be called before uploading (raises a warning if called afterwards).
|
||||||
extern int ogl_tex_transform(Handle ht, uint flags);
|
extern LibError ogl_tex_transform(Handle ht, uint flags);
|
||||||
|
|
||||||
// change the pixel format to that specified by <new_flags>.
|
// change the pixel format to that specified by <new_flags>.
|
||||||
// (note: this is equivalent to ogl_tex_transform(ht, ht_flags^new_flags).
|
// (note: this is equivalent to ogl_tex_transform(ht, ht_flags^new_flags).
|
||||||
extern int ogl_tex_transform_to(Handle ht, uint new_flags);
|
extern LibError ogl_tex_transform_to(Handle ht, uint new_flags);
|
||||||
|
|
||||||
#endif // #ifndef OGL_TEX_H__
|
#endif // #ifndef OGL_TEX_H__
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
|
|
||||||
|
|
||||||
// be careful not to use other tex_* APIs here because they call us.
|
// be careful not to use other tex_* APIs here because they call us.
|
||||||
int tex_validate(const Tex* t)
|
LibError tex_validate(const Tex* t)
|
||||||
{
|
{
|
||||||
// pixel data
|
// pixel data
|
||||||
size_t tex_file_size;
|
size_t tex_file_size;
|
||||||
@ -44,25 +44,25 @@ int tex_validate(const Tex* t)
|
|||||||
// possible causes: texture file header is invalid,
|
// possible causes: texture file header is invalid,
|
||||||
// or file wasn't loaded completely.
|
// or file wasn't loaded completely.
|
||||||
if(tex_file_size < t->ofs + t->w*t->h*t->bpp/8)
|
if(tex_file_size < t->ofs + t->w*t->h*t->bpp/8)
|
||||||
return -2;
|
return ERR_1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// bits per pixel
|
// bits per pixel
|
||||||
// (we don't bother checking all values; a sanity check is enough)
|
// (we don't bother checking all values; a sanity check is enough)
|
||||||
if(t->bpp % 4 || t->bpp > 32)
|
if(t->bpp % 4 || t->bpp > 32)
|
||||||
return -3;
|
return ERR_2;
|
||||||
|
|
||||||
// flags
|
// flags
|
||||||
// .. DXT value
|
// .. DXT value
|
||||||
const uint dxt = t->flags & TEX_DXT;
|
const uint dxt = t->flags & TEX_DXT;
|
||||||
if(dxt != 0 && dxt != 1 && dxt != DXT1A && dxt != 3 && dxt != 5)
|
if(dxt != 0 && dxt != 1 && dxt != DXT1A && dxt != 3 && dxt != 5)
|
||||||
return -4;
|
return ERR_3;
|
||||||
// .. orientation
|
// .. orientation
|
||||||
const uint orientation = t->flags & TEX_ORIENTATION;
|
const uint orientation = t->flags & TEX_ORIENTATION;
|
||||||
if(orientation == (TEX_BOTTOM_UP|TEX_TOP_DOWN))
|
if(orientation == (TEX_BOTTOM_UP|TEX_TOP_DOWN))
|
||||||
return -5;
|
return ERR_4;
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CHECK_TEX(t) CHECK_ERR(tex_validate(t))
|
#define CHECK_TEX(t) CHECK_ERR(tex_validate(t))
|
||||||
@ -74,7 +74,7 @@ int tex_validate(const Tex* t)
|
|||||||
// tex_codec_plain_transform.
|
// tex_codec_plain_transform.
|
||||||
// return 0 if ok, otherwise negative error code (but doesn't warn;
|
// return 0 if ok, otherwise negative error code (but doesn't warn;
|
||||||
// caller is responsible for using CHECK_ERR et al.)
|
// caller is responsible for using CHECK_ERR et al.)
|
||||||
int tex_validate_plain_format(uint bpp, uint flags)
|
LibError tex_validate_plain_format(uint bpp, uint flags)
|
||||||
{
|
{
|
||||||
const bool alpha = (flags & TEX_ALPHA ) != 0;
|
const bool alpha = (flags & TEX_ALPHA ) != 0;
|
||||||
const bool grey = (flags & TEX_GREY ) != 0;
|
const bool grey = (flags & TEX_GREY ) != 0;
|
||||||
@ -88,14 +88,14 @@ int tex_validate_plain_format(uint bpp, uint flags)
|
|||||||
if(grey)
|
if(grey)
|
||||||
{
|
{
|
||||||
if(bpp == 8 && !alpha)
|
if(bpp == 8 && !alpha)
|
||||||
return 0;
|
return ERR_OK;
|
||||||
return ERR_TEX_FMT_INVALID;
|
return ERR_TEX_FMT_INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(bpp == 24 && !alpha)
|
if(bpp == 24 && !alpha)
|
||||||
return 0;
|
return ERR_OK;
|
||||||
if(bpp == 32 && alpha)
|
if(bpp == 32 && alpha)
|
||||||
return 0;
|
return ERR_OK;
|
||||||
|
|
||||||
return ERR_TEX_FMT_INVALID;
|
return ERR_TEX_FMT_INVALID;
|
||||||
}
|
}
|
||||||
@ -181,7 +181,7 @@ TIMER_ADD_CLIENT(tc_plain_transform);
|
|||||||
// but is much easier to maintain than providing all<->all conversion paths.
|
// but is much easier to maintain than providing all<->all conversion paths.
|
||||||
//
|
//
|
||||||
// somewhat optimized (loops are hoisted, cache associativity accounted for)
|
// somewhat optimized (loops are hoisted, cache associativity accounted for)
|
||||||
static int plain_transform(Tex* t, uint transforms)
|
static LibError plain_transform(Tex* t, uint transforms)
|
||||||
{
|
{
|
||||||
TIMER_ACCRUE(tc_plain_transform);
|
TIMER_ACCRUE(tc_plain_transform);
|
||||||
|
|
||||||
@ -197,13 +197,13 @@ TIMER_ACCRUE(tc_plain_transform);
|
|||||||
// sanity checks (not errors, we just can't handle these cases)
|
// sanity checks (not errors, we just can't handle these cases)
|
||||||
// .. unknown transform
|
// .. unknown transform
|
||||||
if(transforms & ~(TEX_BGR|TEX_ORIENTATION|TEX_MIPMAPS))
|
if(transforms & ~(TEX_BGR|TEX_ORIENTATION|TEX_MIPMAPS))
|
||||||
return TEX_CODEC_CANNOT_HANDLE;
|
return ERR_TEX_CODEC_CANNOT_HANDLE;
|
||||||
// .. data is not in "plain" format
|
// .. data is not in "plain" format
|
||||||
if(tex_validate_plain_format(bpp, flags) != 0)
|
if(tex_validate_plain_format(bpp, flags) != 0)
|
||||||
return TEX_CODEC_CANNOT_HANDLE;
|
return ERR_TEX_CODEC_CANNOT_HANDLE;
|
||||||
// .. nothing to do
|
// .. nothing to do
|
||||||
if(!transforms)
|
if(!transforms)
|
||||||
return 0;
|
return ERR_OK;
|
||||||
|
|
||||||
// setup row source/destination pointers (simplifies outer loop)
|
// setup row source/destination pointers (simplifies outer loop)
|
||||||
u8* dst = data;
|
u8* dst = data;
|
||||||
@ -295,7 +295,7 @@ TIMER_ACCRUE(tc_plain_transform);
|
|||||||
}
|
}
|
||||||
|
|
||||||
CHECK_TEX(t);
|
CHECK_TEX(t);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -407,7 +407,7 @@ void tex_util_foreach_mipmap(uint w, uint h, uint bpp, const u8* restrict data,
|
|||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
// split out of tex_load to ease resource cleanup
|
// split out of tex_load to ease resource cleanup
|
||||||
static int tex_load_impl(void* file_, size_t file_size, Tex* t)
|
static LibError tex_load_impl(void* file_, size_t file_size, Tex* t)
|
||||||
{
|
{
|
||||||
u8* file = (u8*)file_;
|
u8* file = (u8*)file_;
|
||||||
const TexCodecVTbl* c;
|
const TexCodecVTbl* c;
|
||||||
@ -438,20 +438,20 @@ static int tex_load_impl(void* file_, size_t file_size, Tex* t)
|
|||||||
|
|
||||||
flip_to_global_orientation(t);
|
flip_to_global_orientation(t);
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// load the specified image from file into the given Tex object.
|
// load the specified image from file into the given Tex object.
|
||||||
// currently supports BMP, TGA, JPG, JP2, PNG, DDS.
|
// currently supports BMP, TGA, JPG, JP2, PNG, DDS.
|
||||||
int tex_load(const char* fn, Tex* t)
|
LibError tex_load(const char* fn, Tex* t)
|
||||||
{
|
{
|
||||||
// load file
|
// load file
|
||||||
void* file; size_t file_size;
|
void* file; size_t file_size;
|
||||||
Handle hm = vfs_load(fn, file, file_size);
|
Handle hm = vfs_load(fn, file, file_size);
|
||||||
RETURN_ERR(hm); // (need handle below; can't test return value directly)
|
RETURN_ERR(hm); // (need handle below; can't test return value directly)
|
||||||
t->hm = hm;
|
t->hm = hm;
|
||||||
int ret = tex_load_impl(file, file_size, t);
|
LibError ret = tex_load_impl(file, file_size, t);
|
||||||
if(ret < 0)
|
if(ret < 0)
|
||||||
{
|
{
|
||||||
(void)tex_free(t);
|
(void)tex_free(t);
|
||||||
@ -463,7 +463,7 @@ int tex_load(const char* fn, Tex* t)
|
|||||||
// wasn't compressed) or was replaced by a new buffer for the image data.
|
// wasn't compressed) or was replaced by a new buffer for the image data.
|
||||||
|
|
||||||
CHECK_TEX(t);
|
CHECK_TEX(t);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -480,7 +480,7 @@ int tex_load(const char* fn, Tex* t)
|
|||||||
//
|
//
|
||||||
// we need only add bookkeeping information and "wrap" it in
|
// we need only add bookkeeping information and "wrap" it in
|
||||||
// our Tex struct, hence the name.
|
// our Tex struct, hence the name.
|
||||||
int tex_wrap(uint w, uint h, uint bpp, uint flags, void* img, Tex* t)
|
LibError tex_wrap(uint w, uint h, uint bpp, uint flags, void* img, Tex* t)
|
||||||
{
|
{
|
||||||
t->w = w;
|
t->w = w;
|
||||||
t->h = h;
|
t->h = h;
|
||||||
@ -500,18 +500,18 @@ int tex_wrap(uint w, uint h, uint bpp, uint flags, void* img, Tex* t)
|
|||||||
t->ofs = (u8*)img - (u8*)reported_ptr;
|
t->ofs = (u8*)img - (u8*)reported_ptr;
|
||||||
|
|
||||||
CHECK_TEX(t);
|
CHECK_TEX(t);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// free all resources associated with the image and make further
|
// free all resources associated with the image and make further
|
||||||
// use of it impossible.
|
// use of it impossible.
|
||||||
int tex_free(Tex* t)
|
LibError tex_free(Tex* t)
|
||||||
{
|
{
|
||||||
// do not validate <t> - this is called from tex_load if loading
|
// do not validate <t> - this is called from tex_load if loading
|
||||||
// failed, so not all fields may be valid.
|
// failed, so not all fields may be valid.
|
||||||
|
|
||||||
int ret = mem_free_h(t->hm);
|
LibError ret = mem_free_h(t->hm);
|
||||||
|
|
||||||
// do not zero out the fields! that could lead to trouble since
|
// do not zero out the fields! that could lead to trouble since
|
||||||
// ogl_tex_upload followed by ogl_tex_free is legit, but would
|
// ogl_tex_upload followed by ogl_tex_free is legit, but would
|
||||||
@ -526,7 +526,7 @@ TIMER_ADD_CLIENT(tc_transform);
|
|||||||
|
|
||||||
// change <t>'s pixel format by flipping the state of all TEX_* flags
|
// change <t>'s pixel format by flipping the state of all TEX_* flags
|
||||||
// that are set in transforms.
|
// that are set in transforms.
|
||||||
int tex_transform(Tex* t, uint transforms)
|
LibError tex_transform(Tex* t, uint transforms)
|
||||||
{
|
{
|
||||||
TIMER_ACCRUE(tc_transform);
|
TIMER_ACCRUE(tc_transform);
|
||||||
CHECK_TEX(t);
|
CHECK_TEX(t);
|
||||||
@ -538,22 +538,22 @@ TIMER_ACCRUE(tc_transform);
|
|||||||
remaining_transforms = target_flags ^ t->flags;
|
remaining_transforms = target_flags ^ t->flags;
|
||||||
// we're finished (all required transforms have been done)
|
// we're finished (all required transforms have been done)
|
||||||
if(remaining_transforms == 0)
|
if(remaining_transforms == 0)
|
||||||
return 0;
|
return ERR_OK;
|
||||||
|
|
||||||
int ret = tex_codec_transform(t, remaining_transforms);
|
LibError ret = tex_codec_transform(t, remaining_transforms);
|
||||||
if(ret != 0)
|
if(ret != 0)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// last chance
|
// last chance
|
||||||
CHECK_ERR(plain_transform(t, remaining_transforms));
|
CHECK_ERR(plain_transform(t, remaining_transforms));
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// change <t>'s pixel format to the new format specified by <new_flags>.
|
// change <t>'s pixel format to the new format specified by <new_flags>.
|
||||||
// (note: this is equivalent to tex_transform(t, t->flags^new_flags).
|
// (note: this is equivalent to tex_transform(t, t->flags^new_flags).
|
||||||
int tex_transform_to(Tex* t, uint new_flags)
|
LibError tex_transform_to(Tex* t, uint new_flags)
|
||||||
{
|
{
|
||||||
// tex_transform takes care of validating <t>
|
// tex_transform takes care of validating <t>
|
||||||
const uint transforms = t->flags ^ new_flags;
|
const uint transforms = t->flags ^ new_flags;
|
||||||
@ -621,7 +621,7 @@ size_t tex_hdr_size(const char* fn)
|
|||||||
// write the specified texture to disk.
|
// write the specified texture to disk.
|
||||||
// note: <t> cannot be made const because the image may have to be
|
// note: <t> cannot be made const because the image may have to be
|
||||||
// transformed to write it out in the format determined by <fn>'s extension.
|
// transformed to write it out in the format determined by <fn>'s extension.
|
||||||
int tex_write(Tex* t, const char* fn)
|
LibError tex_write(Tex* t, const char* fn)
|
||||||
{
|
{
|
||||||
CHECK_TEX(t);
|
CHECK_TEX(t);
|
||||||
CHECK_ERR(tex_validate_plain_format(t->bpp, t->flags));
|
CHECK_ERR(tex_validate_plain_format(t->bpp, t->flags));
|
||||||
@ -639,7 +639,7 @@ int tex_write(Tex* t, const char* fn)
|
|||||||
CHECK_ERR(tex_codec_for_filename(fn, &c));
|
CHECK_ERR(tex_codec_for_filename(fn, &c));
|
||||||
|
|
||||||
// encode into <da>
|
// encode into <da>
|
||||||
int err;
|
LibError err;
|
||||||
size_t rounded_size;
|
size_t rounded_size;
|
||||||
ssize_t bytes_written;
|
ssize_t bytes_written;
|
||||||
err = c->encode(t, &da);
|
err = c->encode(t, &da);
|
||||||
|
@ -185,7 +185,7 @@ extern void tex_set_global_orientation(int orientation);
|
|||||||
|
|
||||||
// load the specified image from file into the given Tex object.
|
// load the specified image from file into the given Tex object.
|
||||||
// currently supports BMP, TGA, JPG, JP2, PNG, DDS.
|
// currently supports BMP, TGA, JPG, JP2, PNG, DDS.
|
||||||
extern int tex_load(const char* fn, Tex* t);
|
extern LibError tex_load(const char* fn, Tex* t);
|
||||||
|
|
||||||
// store the given image data into a Tex object; this will be as if
|
// store the given image data into a Tex object; this will be as if
|
||||||
// it had been loaded via tex_load.
|
// it had been loaded via tex_load.
|
||||||
@ -200,11 +200,11 @@ extern int tex_load(const char* fn, Tex* t);
|
|||||||
//
|
//
|
||||||
// we need only add bookkeeping information and "wrap" it in
|
// we need only add bookkeeping information and "wrap" it in
|
||||||
// our Tex struct, hence the name.
|
// our Tex struct, hence the name.
|
||||||
extern int tex_wrap(uint w, uint h, uint bpp, uint flags, void* img, Tex* t);
|
extern LibError tex_wrap(uint w, uint h, uint bpp, uint flags, void* img, Tex* t);
|
||||||
|
|
||||||
// free all resources associated with the image and make further
|
// free all resources associated with the image and make further
|
||||||
// use of it impossible.
|
// use of it impossible.
|
||||||
extern int tex_free(Tex* t);
|
extern LibError tex_free(Tex* t);
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -213,11 +213,11 @@ extern int tex_free(Tex* t);
|
|||||||
|
|
||||||
// change <t>'s pixel format by flipping the state of all TEX_* flags
|
// change <t>'s pixel format by flipping the state of all TEX_* flags
|
||||||
// that are set in transforms.
|
// that are set in transforms.
|
||||||
extern int tex_transform(Tex* t, uint transforms);
|
extern LibError tex_transform(Tex* t, uint transforms);
|
||||||
|
|
||||||
// change <t>'s pixel format to the new format specified by <new_flags>.
|
// change <t>'s pixel format to the new format specified by <new_flags>.
|
||||||
// (note: this is equivalent to tex_transform(t, t->flags^new_flags).
|
// (note: this is equivalent to tex_transform(t, t->flags^new_flags).
|
||||||
extern int tex_transform_to(Tex* t, uint new_flags);
|
extern LibError tex_transform_to(Tex* t, uint new_flags);
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -255,11 +255,11 @@ extern size_t tex_hdr_size(const char* fn);
|
|||||||
// write the specified texture to disk.
|
// write the specified texture to disk.
|
||||||
// note: <t> cannot be made const because the image may have to be
|
// note: <t> cannot be made const because the image may have to be
|
||||||
// transformed to write it out in the format determined by <fn>'s extension.
|
// transformed to write it out in the format determined by <fn>'s extension.
|
||||||
extern int tex_write(Tex* t, const char* fn);
|
extern LibError tex_write(Tex* t, const char* fn);
|
||||||
|
|
||||||
|
|
||||||
// internal use only:
|
// internal use only:
|
||||||
extern int tex_validate(const Tex* t);
|
extern LibError tex_validate(const Tex* t);
|
||||||
|
|
||||||
// check if the given texture format is acceptable: 8bpp grey,
|
// check if the given texture format is acceptable: 8bpp grey,
|
||||||
// 24bpp color or 32bpp color+alpha (BGR / upside down are permitted).
|
// 24bpp color or 32bpp color+alpha (BGR / upside down are permitted).
|
||||||
@ -267,7 +267,7 @@ extern int tex_validate(const Tex* t);
|
|||||||
// tex_codec_plain_transform.
|
// tex_codec_plain_transform.
|
||||||
// return 0 if ok, otherwise negative error code (but doesn't warn;
|
// return 0 if ok, otherwise negative error code (but doesn't warn;
|
||||||
// caller is responsible for using CHECK_ERR et al.)
|
// caller is responsible for using CHECK_ERR et al.)
|
||||||
extern int tex_validate_plain_format(uint bpp, uint flags);
|
extern LibError tex_validate_plain_format(uint bpp, uint flags);
|
||||||
|
|
||||||
|
|
||||||
// indicate if the orientation specified by <src_flags> matches
|
// indicate if the orientation specified by <src_flags> matches
|
||||||
@ -276,7 +276,7 @@ extern int tex_validate_plain_format(uint bpp, uint flags);
|
|||||||
// have to mask off TEX_ORIENTATION)
|
// have to mask off TEX_ORIENTATION)
|
||||||
extern bool tex_orientations_match(uint src_flags, uint dst_orientation);
|
extern bool tex_orientations_match(uint src_flags, uint dst_orientation);
|
||||||
|
|
||||||
typedef void(*MipmapCB)(uint level, uint level_w, uint level_h,
|
typedef void (*MipmapCB)(uint level, uint level_w, uint level_h,
|
||||||
const u8* level_data, size_t level_data_size, void* ctx);
|
const u8* level_data, size_t level_data_size, void* ctx);
|
||||||
|
|
||||||
// special value for levels_to_skip: the callback will only be called
|
// special value for levels_to_skip: the callback will only be called
|
||||||
|
@ -34,9 +34,9 @@ struct BmpHeader
|
|||||||
#define BI_RGB 0 // biCompression
|
#define BI_RGB 0 // biCompression
|
||||||
|
|
||||||
|
|
||||||
static int bmp_transform(Tex* UNUSED(t), uint UNUSED(transforms))
|
static LibError bmp_transform(Tex* UNUSED(t), uint UNUSED(transforms))
|
||||||
{
|
{
|
||||||
return TEX_CODEC_CANNOT_HANDLE;
|
return ERR_TEX_CODEC_CANNOT_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -69,7 +69,7 @@ static size_t bmp_hdr_size(const u8* file)
|
|||||||
|
|
||||||
|
|
||||||
// requirements: uncompressed, direct colour, bottom up
|
// requirements: uncompressed, direct colour, bottom up
|
||||||
static int bmp_decode(DynArray* restrict da, Tex* restrict t)
|
static LibError bmp_decode(DynArray* restrict da, Tex* restrict t)
|
||||||
{
|
{
|
||||||
u8* file = da->base;
|
u8* file = da->base;
|
||||||
|
|
||||||
@ -96,11 +96,11 @@ static int bmp_decode(DynArray* restrict da, Tex* restrict t)
|
|||||||
t->h = h;
|
t->h = h;
|
||||||
t->bpp = bpp;
|
t->bpp = bpp;
|
||||||
t->flags = flags;
|
t->flags = flags;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int bmp_encode(Tex* restrict t, DynArray* restrict da)
|
static LibError bmp_encode(Tex* restrict t, DynArray* restrict da)
|
||||||
{
|
{
|
||||||
const size_t hdr_size = sizeof(BmpHeader); // needed for BITMAPFILEHEADER
|
const size_t hdr_size = sizeof(BmpHeader); // needed for BITMAPFILEHEADER
|
||||||
const size_t img_size = tex_img_size(t);
|
const size_t img_size = tex_img_size(t);
|
||||||
|
@ -12,6 +12,8 @@ static const TexCodecVTbl* codecs;
|
|||||||
// TEX_CODEC_REGISTER in each codec file. note that call order and therefore
|
// TEX_CODEC_REGISTER in each codec file. note that call order and therefore
|
||||||
// order in the list is undefined, but since each codec only steps up if it
|
// order in the list is undefined, but since each codec only steps up if it
|
||||||
// can handle the given format, this is not a problem.
|
// can handle the given format, this is not a problem.
|
||||||
|
//
|
||||||
|
// returns int to alloc calling from a macro at file scope.
|
||||||
int tex_codec_register(TexCodecVTbl* c)
|
int tex_codec_register(TexCodecVTbl* c)
|
||||||
{
|
{
|
||||||
debug_assert(c);
|
debug_assert(c);
|
||||||
@ -25,7 +27,7 @@ int tex_codec_register(TexCodecVTbl* c)
|
|||||||
|
|
||||||
|
|
||||||
// find codec that recognizes the desired output file extension
|
// find codec that recognizes the desired output file extension
|
||||||
int tex_codec_for_filename(const char* fn, const TexCodecVTbl** c)
|
LibError tex_codec_for_filename(const char* fn, const TexCodecVTbl** c)
|
||||||
{
|
{
|
||||||
const char* ext = strrchr(fn, '.');
|
const char* ext = strrchr(fn, '.');
|
||||||
if(!ext)
|
if(!ext)
|
||||||
@ -36,7 +38,7 @@ int tex_codec_for_filename(const char* fn, const TexCodecVTbl** c)
|
|||||||
{
|
{
|
||||||
// we found it
|
// we found it
|
||||||
if((*c)->is_ext(ext))
|
if((*c)->is_ext(ext))
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ERR_UNKNOWN_FORMAT;
|
return ERR_UNKNOWN_FORMAT;
|
||||||
@ -44,7 +46,7 @@ int tex_codec_for_filename(const char* fn, const TexCodecVTbl** c)
|
|||||||
|
|
||||||
|
|
||||||
// find codec that recognizes the header's magic field
|
// find codec that recognizes the header's magic field
|
||||||
int tex_codec_for_header(const u8* file, size_t file_size, const TexCodecVTbl** c)
|
LibError tex_codec_for_header(const u8* file, size_t file_size, const TexCodecVTbl** c)
|
||||||
{
|
{
|
||||||
// we guarantee at least 4 bytes for is_hdr to look at
|
// we guarantee at least 4 bytes for is_hdr to look at
|
||||||
if(file_size < 4)
|
if(file_size < 4)
|
||||||
@ -54,26 +56,26 @@ int tex_codec_for_header(const u8* file, size_t file_size, const TexCodecVTbl**
|
|||||||
{
|
{
|
||||||
// we found it
|
// we found it
|
||||||
if((*c)->is_hdr(file))
|
if((*c)->is_hdr(file))
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ERR_UNKNOWN_FORMAT;
|
return ERR_UNKNOWN_FORMAT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int tex_codec_transform(Tex* t, uint transforms)
|
LibError tex_codec_transform(Tex* t, uint transforms)
|
||||||
{
|
{
|
||||||
int ret = TEX_CODEC_CANNOT_HANDLE;
|
LibError ret = ERR_TEX_CODEC_CANNOT_HANDLE;
|
||||||
|
|
||||||
// find codec that understands the data, and transform
|
// find codec that understands the data, and transform
|
||||||
for(const TexCodecVTbl* c = codecs; c; c = c->next)
|
for(const TexCodecVTbl* c = codecs; c; c = c->next)
|
||||||
{
|
{
|
||||||
int err = c->transform(t, transforms);
|
LibError err = c->transform(t, transforms);
|
||||||
// success
|
// success
|
||||||
if(err == 0)
|
if(err == ERR_OK)
|
||||||
return 0;
|
return ERR_OK;
|
||||||
// something went wrong
|
// something went wrong
|
||||||
else if(err != TEX_CODEC_CANNOT_HANDLE)
|
else if(err != ERR_TEX_CODEC_CANNOT_HANDLE)
|
||||||
{
|
{
|
||||||
ret = err;
|
ret = err;
|
||||||
debug_warn("codec indicates error");
|
debug_warn("codec indicates error");
|
||||||
@ -97,7 +99,7 @@ int tex_codec_transform(Tex* t, uint transforms)
|
|||||||
//
|
//
|
||||||
// note: we don't allocate the data param ourselves because this function is
|
// note: we don't allocate the data param ourselves because this function is
|
||||||
// needed for encoding, too (where data is already present).
|
// needed for encoding, too (where data is already present).
|
||||||
int tex_codec_alloc_rows(const u8* data, size_t h, size_t pitch,
|
LibError tex_codec_alloc_rows(const u8* data, size_t h, size_t pitch,
|
||||||
uint src_flags, uint dst_orientation, RowArray& rows)
|
uint src_flags, uint dst_orientation, RowArray& rows)
|
||||||
{
|
{
|
||||||
const bool flip = !tex_orientations_match(src_flags, dst_orientation);
|
const bool flip = !tex_orientations_match(src_flags, dst_orientation);
|
||||||
@ -118,16 +120,16 @@ int tex_codec_alloc_rows(const u8* data, size_t h, size_t pitch,
|
|||||||
}
|
}
|
||||||
|
|
||||||
debug_assert(pos == end);
|
debug_assert(pos == end);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int tex_codec_write(Tex* t, uint transforms, const void* hdr, size_t hdr_size, DynArray* da)
|
LibError tex_codec_write(Tex* t, uint transforms, const void* hdr, size_t hdr_size, DynArray* da)
|
||||||
{
|
{
|
||||||
RETURN_ERR(tex_transform(t, transforms));
|
RETURN_ERR(tex_transform(t, transforms));
|
||||||
|
|
||||||
void* img_data = tex_get_data(t); const size_t img_size = tex_img_size(t);
|
void* img_data = tex_get_data(t); const size_t img_size = tex_img_size(t);
|
||||||
RETURN_ERR(da_append(da, hdr, hdr_size));
|
RETURN_ERR(da_append(da, hdr, hdr_size));
|
||||||
RETURN_ERR(da_append(da, img_data, img_size));
|
RETURN_ERR(da_append(da, img_data, img_size));
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ struct TexCodecVTbl
|
|||||||
// size is guaranteed to be >= 4.
|
// size is guaranteed to be >= 4.
|
||||||
// (usually enough to compare the header's "magic" field;
|
// (usually enough to compare the header's "magic" field;
|
||||||
// anyway, no legitimate file will be smaller)
|
// anyway, no legitimate file will be smaller)
|
||||||
int (*decode)(DynArray* restrict da, Tex* restrict t);
|
LibError (*decode)(DynArray* restrict da, Tex* restrict t);
|
||||||
|
|
||||||
// rationale: some codecs cannot calculate the output size beforehand
|
// rationale: some codecs cannot calculate the output size beforehand
|
||||||
// (e.g. PNG output via libpng); we therefore require each one to
|
// (e.g. PNG output via libpng); we therefore require each one to
|
||||||
@ -23,9 +23,9 @@ struct TexCodecVTbl
|
|||||||
//
|
//
|
||||||
// note: <t> cannot be made const because encoding may require a
|
// note: <t> cannot be made const because encoding may require a
|
||||||
// tex_transform.
|
// tex_transform.
|
||||||
int (*encode)(Tex* restrict t, DynArray* restrict da);
|
LibError (*encode)(Tex* restrict t, DynArray* restrict da);
|
||||||
|
|
||||||
int (*transform)(Tex* t, uint transforms);
|
LibError (*transform)(Tex* t, uint transforms);
|
||||||
|
|
||||||
// only guaranteed 4 bytes!
|
// only guaranteed 4 bytes!
|
||||||
bool (*is_hdr)(const u8* file);
|
bool (*is_hdr)(const u8* file);
|
||||||
@ -49,24 +49,22 @@ struct TexCodecVTbl
|
|||||||
static int dummy = tex_codec_register(&vtbl);
|
static int dummy = tex_codec_register(&vtbl);
|
||||||
|
|
||||||
|
|
||||||
// the given texture cannot be handled by this codec; pass the buck on to the next one
|
|
||||||
const int TEX_CODEC_CANNOT_HANDLE = 1;
|
|
||||||
|
|
||||||
|
|
||||||
// add this vtbl to the codec list. called at NLSO init time by the
|
// add this vtbl to the codec list. called at NLSO init time by the
|
||||||
// TEX_CODEC_REGISTER in each codec file. note that call order and therefore
|
// TEX_CODEC_REGISTER in each codec file. note that call order and therefore
|
||||||
// order in the list is undefined, but since each codec only steps up if it
|
// order in the list is undefined, but since each codec only steps up if it
|
||||||
// can handle the given format, this is not a problem.
|
// can handle the given format, this is not a problem.
|
||||||
|
//
|
||||||
|
// returns int to alloc calling from a macro at file scope.
|
||||||
extern int tex_codec_register(TexCodecVTbl* c);
|
extern int tex_codec_register(TexCodecVTbl* c);
|
||||||
|
|
||||||
|
|
||||||
// find codec that recognizes the desired output file extension
|
// find codec that recognizes the desired output file extension
|
||||||
extern int tex_codec_for_filename(const char* fn, const TexCodecVTbl** c);
|
extern LibError tex_codec_for_filename(const char* fn, const TexCodecVTbl** c);
|
||||||
|
|
||||||
// find codec that recognizes the header's magic field
|
// find codec that recognizes the header's magic field
|
||||||
extern int tex_codec_for_header(const u8* file, size_t file_size, const TexCodecVTbl** c);
|
extern LibError tex_codec_for_header(const u8* file, size_t file_size, const TexCodecVTbl** c);
|
||||||
|
|
||||||
extern int tex_codec_transform(Tex* t, uint transforms);
|
extern LibError tex_codec_transform(Tex* t, uint transforms);
|
||||||
|
|
||||||
|
|
||||||
// allocate an array of row pointers that point into the given texture data.
|
// allocate an array of row pointers that point into the given texture data.
|
||||||
@ -80,9 +78,9 @@ extern int tex_codec_transform(Tex* t, uint transforms);
|
|||||||
// needed for encoding, too (where data is already present).
|
// needed for encoding, too (where data is already present).
|
||||||
typedef const u8* RowPtr;
|
typedef const u8* RowPtr;
|
||||||
typedef RowPtr* RowArray;
|
typedef RowPtr* RowArray;
|
||||||
extern int tex_codec_alloc_rows(const u8* data, size_t h, size_t pitch,
|
extern LibError tex_codec_alloc_rows(const u8* data, size_t h, size_t pitch,
|
||||||
uint src_flags, uint dst_orientation, RowArray& rows);
|
uint src_flags, uint dst_orientation, RowArray& rows);
|
||||||
|
|
||||||
extern int tex_codec_write(Tex* t, uint transforms, const void* hdr, size_t hdr_size, DynArray* da);
|
extern LibError tex_codec_write(Tex* t, uint transforms, const void* hdr, size_t hdr_size, DynArray* da);
|
||||||
|
|
||||||
#endif // #ifndef TEX_CODEC_H__
|
#endif // #ifndef TEX_CODEC_H__
|
||||||
|
@ -253,7 +253,7 @@ static void s3tc_decompress_level(uint UNUSED(level), uint level_w, uint level_h
|
|||||||
|
|
||||||
// decompress the given image (which is known to be stored as DXTn)
|
// decompress the given image (which is known to be stored as DXTn)
|
||||||
// effectively in-place. updates Tex fields.
|
// effectively in-place. updates Tex fields.
|
||||||
static int s3tc_decompress(Tex* t)
|
static LibError s3tc_decompress(Tex* t)
|
||||||
{
|
{
|
||||||
// alloc new image memory
|
// alloc new image memory
|
||||||
// notes:
|
// notes:
|
||||||
@ -279,7 +279,7 @@ static int s3tc_decompress(Tex* t)
|
|||||||
t->ofs = 0;
|
t->ofs = 0;
|
||||||
t->bpp = out_bpp;
|
t->bpp = out_bpp;
|
||||||
t->flags &= ~TEX_DXT;
|
t->flags &= ~TEX_DXT;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -388,7 +388,7 @@ static bool is_valid_dxt(uint dxt)
|
|||||||
// pf points to the DDS file's header; all fields must be endian-converted
|
// pf points to the DDS file's header; all fields must be endian-converted
|
||||||
// before use.
|
// before use.
|
||||||
// output parameters invalid on failure.
|
// output parameters invalid on failure.
|
||||||
static int decode_pf(const DDPIXELFORMAT* pf, uint* bpp_, uint* flags_)
|
static LibError decode_pf(const DDPIXELFORMAT* pf, uint* bpp_, uint* flags_)
|
||||||
{
|
{
|
||||||
uint bpp = 0;
|
uint bpp = 0;
|
||||||
uint flags = 0;
|
uint flags = 0;
|
||||||
@ -449,7 +449,7 @@ static int decode_pf(const DDPIXELFORMAT* pf, uint* bpp_, uint* flags_)
|
|||||||
|
|
||||||
*bpp_ = bpp;
|
*bpp_ = bpp;
|
||||||
*flags_ = flags;
|
*flags_ = flags;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -457,7 +457,7 @@ static int decode_pf(const DDPIXELFORMAT* pf, uint* bpp_, uint* flags_)
|
|||||||
// sd points to the DDS file's header; all fields must be endian-converted
|
// sd points to the DDS file's header; all fields must be endian-converted
|
||||||
// before use.
|
// before use.
|
||||||
// output parameters invalid on failure.
|
// output parameters invalid on failure.
|
||||||
static int decode_sd(const DDSURFACEDESC2* sd, uint* w_, uint* h_,
|
static LibError decode_sd(const DDSURFACEDESC2* sd, uint* w_, uint* h_,
|
||||||
uint* bpp_, uint* flags_)
|
uint* bpp_, uint* flags_)
|
||||||
{
|
{
|
||||||
// check header size
|
// check header size
|
||||||
@ -537,7 +537,7 @@ static int decode_sd(const DDSURFACEDESC2* sd, uint* w_, uint* h_,
|
|||||||
*h_ = h;
|
*h_ = h;
|
||||||
*bpp_ = bpp;
|
*bpp_ = bpp;
|
||||||
*flags_ = flags;
|
*flags_ = flags;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -561,7 +561,7 @@ static size_t dds_hdr_size(const u8* UNUSED(file))
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int dds_decode(DynArray* restrict da, Tex* restrict t)
|
static LibError dds_decode(DynArray* restrict da, Tex* restrict t)
|
||||||
{
|
{
|
||||||
u8* file = da->base;
|
u8* file = da->base;
|
||||||
const DDSURFACEDESC2* sd = (const DDSURFACEDESC2*)(file+4);
|
const DDSURFACEDESC2* sd = (const DDSURFACEDESC2*)(file+4);
|
||||||
@ -575,19 +575,19 @@ static int dds_decode(DynArray* restrict da, Tex* restrict t)
|
|||||||
t->h = h;
|
t->h = h;
|
||||||
t->bpp = bpp;
|
t->bpp = bpp;
|
||||||
t->flags = flags;
|
t->flags = flags;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int dds_encode(Tex* restrict UNUSED(t), DynArray* restrict UNUSED(da))
|
static LibError dds_encode(Tex* restrict UNUSED(t), DynArray* restrict UNUSED(da))
|
||||||
{
|
{
|
||||||
// note: do not return ERR_NOT_IMPLEMENTED et al. because that would
|
// note: do not return ERR_NOT_IMPLEMENTED et al. because that would
|
||||||
// break tex_write (which assumes either this, 0 or errors are returned).
|
// break tex_write (which assumes either this, 0 or errors are returned).
|
||||||
return TEX_CODEC_CANNOT_HANDLE;
|
return ERR_TEX_CODEC_CANNOT_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int dds_transform(Tex* t, uint transforms)
|
static LibError dds_transform(Tex* t, uint transforms)
|
||||||
{
|
{
|
||||||
uint dxt = t->flags & TEX_DXT;
|
uint dxt = t->flags & TEX_DXT;
|
||||||
debug_assert(is_valid_dxt(dxt));
|
debug_assert(is_valid_dxt(dxt));
|
||||||
@ -597,13 +597,13 @@ static int dds_transform(Tex* t, uint transforms)
|
|||||||
if(dxt && transform_dxt)
|
if(dxt && transform_dxt)
|
||||||
{
|
{
|
||||||
RETURN_ERR(s3tc_decompress(t));
|
RETURN_ERR(s3tc_decompress(t));
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
// both are DXT (unsupported; there are no flags we can change while
|
// both are DXT (unsupported; there are no flags we can change while
|
||||||
// compressed) or requesting compression (not implemented) or
|
// compressed) or requesting compression (not implemented) or
|
||||||
// both not DXT (nothing we can do) - bail.
|
// both not DXT (nothing we can do) - bail.
|
||||||
else
|
else
|
||||||
return TEX_CODEC_CANNOT_HANDLE;
|
return ERR_TEX_CODEC_CANNOT_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -398,9 +398,9 @@ JpgErrorMgr::JpgErrorMgr(j_common_ptr cinfo)
|
|||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
static int jpg_transform(Tex* UNUSED(t), uint UNUSED(transforms))
|
static LibError jpg_transform(Tex* UNUSED(t), uint UNUSED(transforms))
|
||||||
{
|
{
|
||||||
return TEX_CODEC_CANNOT_HANDLE;
|
return ERR_TEX_CODEC_CANNOT_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -415,7 +415,7 @@ static int jpg_transform(Tex* UNUSED(t), uint UNUSED(transforms))
|
|||||||
// due to less copying.
|
// due to less copying.
|
||||||
|
|
||||||
|
|
||||||
static int jpg_decode_impl(DynArray* da,
|
static LibError jpg_decode_impl(DynArray* da,
|
||||||
jpeg_decompress_struct* cinfo,
|
jpeg_decompress_struct* cinfo,
|
||||||
Handle& img_hm, RowArray& rows, Tex* t)
|
Handle& img_hm, RowArray& rows, Tex* t)
|
||||||
{
|
{
|
||||||
@ -477,7 +477,7 @@ static int jpg_decode_impl(DynArray* da,
|
|||||||
// mem data source.
|
// mem data source.
|
||||||
(void)jpeg_finish_decompress(cinfo);
|
(void)jpeg_finish_decompress(cinfo);
|
||||||
|
|
||||||
int ret = 0;
|
LibError ret = ERR_OK;
|
||||||
if(cinfo->err->num_warnings != 0)
|
if(cinfo->err->num_warnings != 0)
|
||||||
ret = WARN_TEX_INVALID_DATA;
|
ret = WARN_TEX_INVALID_DATA;
|
||||||
|
|
||||||
@ -495,7 +495,7 @@ static int jpg_decode_impl(DynArray* da,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int jpg_encode_impl(Tex* t,
|
static LibError jpg_encode_impl(Tex* t,
|
||||||
jpeg_compress_struct* cinfo,
|
jpeg_compress_struct* cinfo,
|
||||||
RowArray& rows, DynArray* da)
|
RowArray& rows, DynArray* da)
|
||||||
{
|
{
|
||||||
@ -538,7 +538,7 @@ static int jpg_encode_impl(Tex* t,
|
|||||||
|
|
||||||
jpeg_finish_compress(cinfo);
|
jpeg_finish_compress(cinfo);
|
||||||
|
|
||||||
int ret = 0;
|
LibError ret = ERR_OK;
|
||||||
if(cinfo->err->num_warnings != 0)
|
if(cinfo->err->num_warnings != 0)
|
||||||
ret = WARN_TEX_INVALID_DATA;
|
ret = WARN_TEX_INVALID_DATA;
|
||||||
|
|
||||||
@ -567,9 +567,9 @@ static size_t jpg_hdr_size(const u8* UNUSED(file))
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int jpg_decode(DynArray* restrict da, Tex* restrict t)
|
static LibError jpg_decode(DynArray* restrict da, Tex* restrict t)
|
||||||
{
|
{
|
||||||
int err;
|
LibError err;
|
||||||
|
|
||||||
// freed when ret is reached:
|
// freed when ret is reached:
|
||||||
// .. contains the JPEG decompression parameters and pointers to
|
// .. contains the JPEG decompression parameters and pointers to
|
||||||
@ -584,7 +584,7 @@ static int jpg_decode(DynArray* restrict da, Tex* restrict t)
|
|||||||
JpgErrorMgr jerr((j_common_ptr)&cinfo);
|
JpgErrorMgr jerr((j_common_ptr)&cinfo);
|
||||||
if(setjmp(jerr.call_site))
|
if(setjmp(jerr.call_site))
|
||||||
{
|
{
|
||||||
err = -1;
|
err = ERR_FAIL;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -606,9 +606,9 @@ fail:
|
|||||||
|
|
||||||
|
|
||||||
// limitation: palette images aren't supported
|
// limitation: palette images aren't supported
|
||||||
static int jpg_encode(Tex* restrict t, DynArray* restrict da)
|
static LibError jpg_encode(Tex* restrict t, DynArray* restrict da)
|
||||||
{
|
{
|
||||||
int err;
|
LibError err;
|
||||||
|
|
||||||
// freed when ret is reached:
|
// freed when ret is reached:
|
||||||
// .. contains the JPEG compression parameters and pointers to
|
// .. contains the JPEG compression parameters and pointers to
|
||||||
@ -620,7 +620,7 @@ static int jpg_encode(Tex* restrict t, DynArray* restrict da)
|
|||||||
JpgErrorMgr jerr((j_common_ptr)&cinfo);
|
JpgErrorMgr jerr((j_common_ptr)&cinfo);
|
||||||
if(setjmp(jerr.call_site))
|
if(setjmp(jerr.call_site))
|
||||||
{
|
{
|
||||||
err = -1;
|
err = ERR_FAIL;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,9 +64,9 @@ static void io_flush(png_structp UNUSED(png_ptr))
|
|||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
static int png_transform(Tex* UNUSED(t), uint UNUSED(transforms))
|
static LibError png_transform(Tex* UNUSED(t), uint UNUSED(transforms))
|
||||||
{
|
{
|
||||||
return TEX_CODEC_CANNOT_HANDLE;
|
return ERR_TEX_CODEC_CANNOT_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -75,7 +75,7 @@ static int png_transform(Tex* UNUSED(t), uint UNUSED(transforms))
|
|||||||
|
|
||||||
// split out of png_decode to simplify resource cleanup and avoid
|
// split out of png_decode to simplify resource cleanup and avoid
|
||||||
// "dtor / setjmp interaction" warning.
|
// "dtor / setjmp interaction" warning.
|
||||||
static int png_decode_impl(DynArray* da,
|
static LibError png_decode_impl(DynArray* da,
|
||||||
png_structp png_ptr, png_infop info_ptr,
|
png_structp png_ptr, png_infop info_ptr,
|
||||||
Handle& img_hm, RowArray& rows, Tex* t)
|
Handle& img_hm, RowArray& rows, Tex* t)
|
||||||
{
|
{
|
||||||
@ -124,13 +124,13 @@ static int png_decode_impl(DynArray* da,
|
|||||||
t->bpp = bpp;
|
t->bpp = bpp;
|
||||||
t->flags = flags;
|
t->flags = flags;
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// split out of png_encode to simplify resource cleanup and avoid
|
// split out of png_encode to simplify resource cleanup and avoid
|
||||||
// "dtor / setjmp interaction" warning.
|
// "dtor / setjmp interaction" warning.
|
||||||
static int png_encode_impl(Tex* t,
|
static LibError png_encode_impl(Tex* t,
|
||||||
png_structp png_ptr, png_infop info_ptr,
|
png_structp png_ptr, png_infop info_ptr,
|
||||||
RowArray& rows, DynArray* da)
|
RowArray& rows, DynArray* da)
|
||||||
{
|
{
|
||||||
@ -167,7 +167,7 @@ static int png_encode_impl(Tex* t,
|
|||||||
png_set_rows(png_ptr, info_ptr, (png_bytepp)rows);
|
png_set_rows(png_ptr, info_ptr, (png_bytepp)rows);
|
||||||
png_write_png(png_ptr, info_ptr, png_transforms, 0);
|
png_write_png(png_ptr, info_ptr, png_transforms, 0);
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -195,11 +195,11 @@ static size_t png_hdr_size(const u8* UNUSED(file))
|
|||||||
TIMER_ADD_CLIENT(tc_png_decode);
|
TIMER_ADD_CLIENT(tc_png_decode);
|
||||||
|
|
||||||
// limitation: palette images aren't supported
|
// limitation: palette images aren't supported
|
||||||
static int png_decode(DynArray* restrict da, Tex* restrict t)
|
static LibError png_decode(DynArray* restrict da, Tex* restrict t)
|
||||||
{
|
{
|
||||||
TIMER_ACCRUE(tc_png_decode);
|
TIMER_ACCRUE(tc_png_decode);
|
||||||
|
|
||||||
int err = -1;
|
LibError err = ERR_FAIL;
|
||||||
// freed when ret is reached:
|
// freed when ret is reached:
|
||||||
png_structp png_ptr = 0;
|
png_structp png_ptr = 0;
|
||||||
png_infop info_ptr = 0;
|
png_infop info_ptr = 0;
|
||||||
@ -238,9 +238,9 @@ ret:
|
|||||||
|
|
||||||
|
|
||||||
// limitation: palette images aren't supported
|
// limitation: palette images aren't supported
|
||||||
static int png_encode(Tex* restrict t, DynArray* restrict da)
|
static LibError png_encode(Tex* restrict t, DynArray* restrict da)
|
||||||
{
|
{
|
||||||
int err = -1;
|
LibError err = ERR_FAIL;
|
||||||
// freed when ret is reached:
|
// freed when ret is reached:
|
||||||
png_structp png_ptr = 0;
|
png_structp png_ptr = 0;
|
||||||
png_infop info_ptr = 0;
|
png_infop info_ptr = 0;
|
||||||
|
@ -41,9 +41,9 @@ TgaHeader;
|
|||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
|
||||||
static int tga_transform(Tex* UNUSED(t), uint UNUSED(transforms))
|
static LibError tga_transform(Tex* UNUSED(t), uint UNUSED(transforms))
|
||||||
{
|
{
|
||||||
return TEX_CODEC_CANNOT_HANDLE;
|
return ERR_TEX_CODEC_CANNOT_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -86,7 +86,7 @@ static size_t tga_hdr_size(const u8* file)
|
|||||||
|
|
||||||
|
|
||||||
// requirements: uncompressed, direct colour, bottom up
|
// requirements: uncompressed, direct colour, bottom up
|
||||||
static int tga_decode(DynArray* restrict da, Tex* restrict t)
|
static LibError tga_decode(DynArray* restrict da, Tex* restrict t)
|
||||||
{
|
{
|
||||||
u8* file = da->base;
|
u8* file = da->base;
|
||||||
|
|
||||||
@ -116,11 +116,11 @@ static int tga_decode(DynArray* restrict da, Tex* restrict t)
|
|||||||
t->h = h;
|
t->h = h;
|
||||||
t->bpp = bpp;
|
t->bpp = bpp;
|
||||||
t->flags = flags;
|
t->flags = flags;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int tga_encode(Tex* restrict t, DynArray* restrict da)
|
static LibError tga_encode(Tex* restrict t, DynArray* restrict da)
|
||||||
{
|
{
|
||||||
u8 img_desc = 0;
|
u8 img_desc = 0;
|
||||||
if(t->flags & TEX_TOP_DOWN)
|
if(t->flags & TEX_TOP_DOWN)
|
||||||
|
@ -59,11 +59,11 @@ static void UniFont_dtor(UniFont* f)
|
|||||||
SAFE_DELETE(f->glyphs_size);
|
SAFE_DELETE(f->glyphs_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int UniFont_reload(UniFont* f, const char* fn, Handle UNUSED(h))
|
static LibError UniFont_reload(UniFont* f, const char* fn, Handle UNUSED(h))
|
||||||
{
|
{
|
||||||
// already loaded
|
// already loaded
|
||||||
if(f->ht > 0)
|
if(f->ht > 0)
|
||||||
return 0;
|
return ERR_OK;
|
||||||
|
|
||||||
f->glyphs_id = new glyphmap_id;
|
f->glyphs_id = new glyphmap_id;
|
||||||
f->glyphs_size = new glyphmap_size;
|
f->glyphs_size = new glyphmap_size;
|
||||||
@ -107,7 +107,7 @@ static int UniFont_reload(UniFont* f, const char* fn, Handle UNUSED(h))
|
|||||||
if (f->ListBase == 0) // My Voodoo2 drivers didn't support display lists (although I'd be surprised if they got this far)
|
if (f->ListBase == 0) // My Voodoo2 drivers didn't support display lists (although I'd be surprised if they got this far)
|
||||||
{
|
{
|
||||||
debug_warn("Display list creation failed");
|
debug_warn("Display list creation failed");
|
||||||
return -1;
|
return ERR_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < NumGlyphs; ++i)
|
for (int i = 0; i < NumGlyphs; ++i)
|
||||||
@ -162,7 +162,7 @@ static int UniFont_reload(UniFont* f, const char* fn, Handle UNUSED(h))
|
|||||||
// but we want ALPHA. there is no way of knowing what format
|
// but we want ALPHA. there is no way of knowing what format
|
||||||
// 8bpp textures are in - we could adopt a naming convention and
|
// 8bpp textures are in - we could adopt a naming convention and
|
||||||
// add some TEX_ flags, but that's overkill.
|
// add some TEX_ flags, but that's overkill.
|
||||||
int err = ogl_tex_upload(ht, GL_ALPHA);
|
LibError err = ogl_tex_upload(ht, GL_ALPHA);
|
||||||
if(err < 0)
|
if(err < 0)
|
||||||
{
|
{
|
||||||
(void)ogl_tex_free(ht);
|
(void)ogl_tex_free(ht);
|
||||||
@ -170,44 +170,44 @@ static int UniFont_reload(UniFont* f, const char* fn, Handle UNUSED(h))
|
|||||||
}
|
}
|
||||||
|
|
||||||
f->ht = ht;
|
f->ht = ht;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int UniFont_validate(const UniFont* f)
|
static LibError UniFont_validate(const UniFont* f)
|
||||||
{
|
{
|
||||||
if(f->ht < 0)
|
if(f->ht < 0)
|
||||||
return -2;
|
return ERR_1;
|
||||||
if(debug_is_pointer_bogus(f->glyphs_id) || debug_is_pointer_bogus(f->glyphs_size))
|
if(debug_is_pointer_bogus(f->glyphs_id) || debug_is_pointer_bogus(f->glyphs_size))
|
||||||
return -3;
|
return ERR_2;
|
||||||
// <LineSpacing> and <Height> are read directly from font file.
|
// <LineSpacing> and <Height> are read directly from font file.
|
||||||
// negative values don't make sense, but that's all we can check.
|
// negative values don't make sense, but that's all we can check.
|
||||||
if(f->LineSpacing < 0 || f->Height < 0)
|
if(f->LineSpacing < 0 || f->Height < 0)
|
||||||
return -4;
|
return ERR_3;
|
||||||
if(f->ListBase == 0 || f->ListBase > 1000000) // suspicious
|
if(f->ListBase == 0 || f->ListBase > 1000000) // suspicious
|
||||||
return -5;
|
return ERR_4;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int UniFont_to_string(const UniFont* UNUSED(f), char* buf)
|
static LibError UniFont_to_string(const UniFont* UNUSED(f), char* buf)
|
||||||
{
|
{
|
||||||
snprintf(buf, H_STRING_LEN, "");
|
snprintf(buf, H_STRING_LEN, "");
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Handle unifont_load(const char* fn, int scope)
|
Handle unifont_load(const char* fn, int flags)
|
||||||
{
|
{
|
||||||
return h_alloc(H_UniFont, fn, scope);
|
return h_alloc(H_UniFont, fn, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int unifont_unload(Handle& h)
|
LibError unifont_unload(Handle& h)
|
||||||
{
|
{
|
||||||
return h_free(h, H_UniFont);
|
return h_free(h, H_UniFont);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int unifont_bind(const Handle h)
|
LibError unifont_bind(const Handle h)
|
||||||
{
|
{
|
||||||
H_DEREF(h, UniFont, f);
|
H_DEREF(h, UniFont, f);
|
||||||
|
|
||||||
@ -215,7 +215,7 @@ int unifont_bind(const Handle h)
|
|||||||
glListBase(f->ListBase);
|
glListBase(f->ListBase);
|
||||||
BoundGlyphs = f->glyphs_id;
|
BoundGlyphs = f->glyphs_id;
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -233,7 +233,7 @@ int unifont_height(const Handle h)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int unifont_character_width(const Handle h, const wchar_t& c)
|
int unifont_character_width(const Handle h, wchar_t c)
|
||||||
{
|
{
|
||||||
H_DEREF(h, UniFont, f);
|
H_DEREF(h, UniFont, f);
|
||||||
glyphmap_size::iterator it = f->glyphs_size->find(c);
|
glyphmap_size::iterator it = f->glyphs_size->find(c);
|
||||||
@ -296,7 +296,7 @@ void glwprintf(const wchar_t* fmt, ...)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int unifont_stringsize(const Handle h, const wchar_t* text, int& width, int& height)
|
LibError unifont_stringsize(const Handle h, const wchar_t* text, int& width, int& height)
|
||||||
{
|
{
|
||||||
H_DEREF(h, UniFont, f);
|
H_DEREF(h, UniFont, f);
|
||||||
|
|
||||||
@ -315,11 +315,11 @@ int unifont_stringsize(const Handle h, const wchar_t* text, int& width, int& hei
|
|||||||
if (it == f->glyphs_size->end()) // Missing the missing glyph symbol - give up
|
if (it == f->glyphs_size->end()) // Missing the missing glyph symbol - give up
|
||||||
{
|
{
|
||||||
debug_warn("Missing the missing glyph in a unifont!\n");
|
debug_warn("Missing the missing glyph in a unifont!\n");
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
width += it->second; // Add the character's advance distance
|
width += it->second; // Add the character's advance distance
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
@ -9,14 +9,14 @@
|
|||||||
|
|
||||||
// Load and return a handle to the font defined
|
// Load and return a handle to the font defined
|
||||||
// in fn+".fnt" with texture fn+".tga"
|
// in fn+".fnt" with texture fn+".tga"
|
||||||
extern Handle unifont_load(const char* fn, int scope = 0);
|
extern Handle unifont_load(const char* fn, int flags = 0);
|
||||||
|
|
||||||
// Release a handle to a previously loaded font
|
// Release a handle to a previously loaded font
|
||||||
extern int unifont_unload(Handle& h);
|
extern LibError unifont_unload(Handle& h);
|
||||||
|
|
||||||
// Use the font referenced by h for all subsequent glwprintf() calls.
|
// Use the font referenced by h for all subsequent glwprintf() calls.
|
||||||
// Must be called before any glwprintf().
|
// Must be called before any glwprintf().
|
||||||
extern int unifont_bind(Handle h);
|
extern LibError unifont_bind(Handle h);
|
||||||
|
|
||||||
// Output text at current OpenGL modelview pos.
|
// Output text at current OpenGL modelview pos.
|
||||||
extern void glvwprintf(const wchar_t* fmt, va_list args);
|
extern void glvwprintf(const wchar_t* fmt, va_list args);
|
||||||
@ -37,13 +37,13 @@ extern void glwprintf(const wchar_t* fmt, ...);
|
|||||||
|
|
||||||
// Intended for the GUI (hence Unicode). 'height' is roughly the height of
|
// Intended for the GUI (hence Unicode). 'height' is roughly the height of
|
||||||
// a capital letter, for use when aligning text in an aesthetically pleasing way.
|
// a capital letter, for use when aligning text in an aesthetically pleasing way.
|
||||||
int unifont_stringsize(const Handle h, const wchar_t* text, int& width, int& height);
|
LibError unifont_stringsize(const Handle h, const wchar_t* text, int& width, int& height);
|
||||||
|
|
||||||
// Get only the height
|
// Get only the height
|
||||||
int unifont_height(const Handle h);
|
int unifont_height(const Handle h);
|
||||||
|
|
||||||
// Get only the width of one character
|
// Get only the width of one character
|
||||||
int unifont_character_width(const Handle h, const wchar_t& c);
|
int unifont_character_width(const Handle h, wchar_t c);
|
||||||
|
|
||||||
// Return spacing in pixels from one line of text to the next
|
// Return spacing in pixels from one line of text to the next
|
||||||
int unifont_linespacing(const Handle h);
|
int unifont_linespacing(const Handle h);
|
||||||
|
@ -260,7 +260,7 @@ static HDATA* h_data_tag_type(const Handle h, const H_Type type)
|
|||||||
|
|
||||||
// idx and hd are undefined if we fail.
|
// idx and hd are undefined if we fail.
|
||||||
// called by h_alloc only.
|
// called by h_alloc only.
|
||||||
static int alloc_idx(i32& idx, HDATA*& hd)
|
static LibError alloc_idx(i32& idx, HDATA*& hd)
|
||||||
{
|
{
|
||||||
// we already know the first free entry
|
// we already know the first free entry
|
||||||
if(first_free != -1)
|
if(first_free != -1)
|
||||||
@ -311,15 +311,15 @@ have_idx:;
|
|||||||
if(idx > last_in_use)
|
if(idx > last_in_use)
|
||||||
last_in_use = idx;
|
last_in_use = idx;
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int free_idx(i32 idx)
|
static LibError free_idx(i32 idx)
|
||||||
{
|
{
|
||||||
if(first_free == -1 || idx < first_free)
|
if(first_free == -1 || idx < first_free)
|
||||||
first_free = idx;
|
first_free = idx;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -488,8 +488,8 @@ static void warn_if_invalid(HDATA* hd)
|
|||||||
// the others have no invariants we could check.
|
// the others have no invariants we could check.
|
||||||
|
|
||||||
// have the resource validate its user_data
|
// have the resource validate its user_data
|
||||||
int err = vtbl->validate(hd->user);
|
LibError err = vtbl->validate(hd->user);
|
||||||
debug_assert(err == 0);
|
debug_assert(err == ERR_OK);
|
||||||
|
|
||||||
// make sure empty space in control block isn't touched
|
// make sure empty space in control block isn't touched
|
||||||
// .. but only if we're not storing a filename there
|
// .. but only if we're not storing a filename there
|
||||||
@ -507,31 +507,25 @@ static void warn_if_invalid(HDATA* hd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int type_validate(H_Type type)
|
static LibError type_validate(H_Type type)
|
||||||
{
|
{
|
||||||
int err = ERR_INVALID_PARAM;
|
|
||||||
|
|
||||||
if(!type)
|
if(!type)
|
||||||
{
|
{
|
||||||
debug_warn("type is 0");
|
debug_warn("type is 0");
|
||||||
goto fail;
|
return ERR_INVALID_PARAM;
|
||||||
}
|
}
|
||||||
if(type->user_size > HDATA_USER_SIZE)
|
if(type->user_size > HDATA_USER_SIZE)
|
||||||
{
|
{
|
||||||
debug_warn("type's user data is too large for HDATA");
|
debug_warn("type's user data is too large for HDATA");
|
||||||
goto fail;
|
return ERR_LIMIT;
|
||||||
}
|
}
|
||||||
if(type->name == 0)
|
if(type->name == 0)
|
||||||
{
|
{
|
||||||
debug_warn("type's name field is 0");
|
debug_warn("type's name field is 0");
|
||||||
goto fail;
|
return ERR_INVALID_PARAM;
|
||||||
}
|
}
|
||||||
|
|
||||||
// success
|
return ERR_OK;
|
||||||
err = 0;
|
|
||||||
|
|
||||||
fail:
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -583,9 +577,9 @@ static Handle reuse_existing_handle(uintptr_t key, H_Type type, uint flags)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int call_init_and_reload(Handle h, H_Type type, HDATA* hd, const char* fn, va_list* init_args)
|
static LibError call_init_and_reload(Handle h, H_Type type, HDATA* hd, const char* fn, va_list* init_args)
|
||||||
{
|
{
|
||||||
int err = 0;
|
LibError err = ERR_OK;
|
||||||
H_VTbl* vtbl = type; // exact same thing but for clarity
|
H_VTbl* vtbl = type; // exact same thing but for clarity
|
||||||
|
|
||||||
// init
|
// init
|
||||||
@ -599,7 +593,7 @@ static int call_init_and_reload(Handle h, H_Type type, HDATA* hd, const char* fn
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
err = vtbl->reload(hd->user, fn, h);
|
err = vtbl->reload(hd->user, fn, h);
|
||||||
if(err == 0)
|
if(err == ERR_OK)
|
||||||
warn_if_invalid(hd);
|
warn_if_invalid(hd);
|
||||||
}
|
}
|
||||||
catch(std::bad_alloc)
|
catch(std::bad_alloc)
|
||||||
@ -642,7 +636,7 @@ static Handle alloc_new_handle(H_Type type, const char* fn, uintptr_t key,
|
|||||||
if(key && !hd->unique)
|
if(key && !hd->unique)
|
||||||
key_add(key, h);
|
key_add(key, h);
|
||||||
|
|
||||||
int err = call_init_and_reload(h, type, hd, fn, init_args);
|
LibError err = call_init_and_reload(h, type, hd, fn, init_args);
|
||||||
if(err < 0)
|
if(err < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
@ -703,7 +697,7 @@ Handle h_alloc(H_Type type, const char* fn, uint flags, ...)
|
|||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
// currently cannot fail.
|
// currently cannot fail.
|
||||||
static int h_free_idx(i32 idx, HDATA* hd)
|
static LibError h_free_idx(i32 idx, HDATA* hd)
|
||||||
{
|
{
|
||||||
// debug_printf("free %s %s\n", type->name, hd->fn);
|
// debug_printf("free %s %s\n", type->name, hd->fn);
|
||||||
|
|
||||||
@ -713,7 +707,7 @@ static int h_free_idx(i32 idx, HDATA* hd)
|
|||||||
|
|
||||||
// still references open or caching requests it stays - do not release.
|
// still references open or caching requests it stays - do not release.
|
||||||
if(hd->refs > 0 || hd->keep_open)
|
if(hd->refs > 0 || hd->keep_open)
|
||||||
return 0;
|
return ERR_OK;
|
||||||
|
|
||||||
// actually release the resource (call dtor, free control block).
|
// actually release the resource (call dtor, free control block).
|
||||||
|
|
||||||
@ -751,11 +745,11 @@ static int h_free_idx(i32 idx, HDATA* hd)
|
|||||||
|
|
||||||
free_idx(idx);
|
free_idx(idx);
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int h_free(Handle& h, H_Type type)
|
LibError h_free(Handle& h, H_Type type)
|
||||||
{
|
{
|
||||||
i32 idx = h_idx(h);
|
i32 idx = h_idx(h);
|
||||||
HDATA* hd = h_data_tag_type(h, type);
|
HDATA* hd = h_data_tag_type(h, type);
|
||||||
@ -770,7 +764,7 @@ int h_free(Handle& h, H_Type type)
|
|||||||
// 0-initialized or an error code; don't complain because this
|
// 0-initialized or an error code; don't complain because this
|
||||||
// happens often and is harmless.
|
// happens often and is harmless.
|
||||||
if(h_copy <= 0)
|
if(h_copy <= 0)
|
||||||
return 0;
|
return ERR_OK;
|
||||||
// this was a valid handle but was probably freed in the meantime.
|
// this was a valid handle but was probably freed in the meantime.
|
||||||
// complain because this probably indicates a bug somewhere.
|
// complain because this probably indicates a bug somewhere.
|
||||||
CHECK_ERR(ERR_INVALID_HANDLE);
|
CHECK_ERR(ERR_INVALID_HANDLE);
|
||||||
@ -816,7 +810,7 @@ const char* h_filename(const Handle h)
|
|||||||
|
|
||||||
|
|
||||||
// TODO: what if iterating through all handles is too slow?
|
// TODO: what if iterating through all handles is too slow?
|
||||||
int h_reload(const char* fn)
|
LibError h_reload(const char* fn)
|
||||||
{
|
{
|
||||||
if(!fn)
|
if(!fn)
|
||||||
{
|
{
|
||||||
@ -840,7 +834,7 @@ int h_reload(const char* fn)
|
|||||||
hd->type->dtor(hd->user);
|
hd->type->dtor(hd->user);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ret = 0;
|
LibError ret = ERR_OK;
|
||||||
|
|
||||||
// now reload all affected handles
|
// now reload all affected handles
|
||||||
for(i32 i = 0; i <= last_in_use; i++)
|
for(i32 i = 0; i <= last_in_use; i++)
|
||||||
@ -851,7 +845,7 @@ int h_reload(const char* fn)
|
|||||||
|
|
||||||
Handle h = handle(i, hd->tag);
|
Handle h = handle(i, hd->tag);
|
||||||
|
|
||||||
int err = hd->type->reload(hd->user, hd->fn, h);
|
LibError err = hd->type->reload(hd->user, hd->fn, h);
|
||||||
// don't stop if an error is encountered - try to reload them all.
|
// don't stop if an error is encountered - try to reload them all.
|
||||||
if(err < 0)
|
if(err < 0)
|
||||||
{
|
{
|
||||||
@ -880,7 +874,7 @@ Handle h_find(H_Type type, uintptr_t key)
|
|||||||
// to later close the object.
|
// to later close the object.
|
||||||
// this is used when reinitializing the sound engine -
|
// this is used when reinitializing the sound engine -
|
||||||
// at that point, all (cached) OpenAL resources must be freed.
|
// at that point, all (cached) OpenAL resources must be freed.
|
||||||
int h_force_free(Handle h, H_Type type)
|
LibError h_force_free(Handle h, H_Type type)
|
||||||
{
|
{
|
||||||
// require valid index; ignore tag; type checked below.
|
// require valid index; ignore tag; type checked below.
|
||||||
HDATA* hd = h_data_no_tag(h);
|
HDATA* hd = h_data_no_tag(h);
|
||||||
|
@ -125,7 +125,7 @@ reload:
|
|||||||
does all initialization of the resource that requires its source file.
|
does all initialization of the resource that requires its source file.
|
||||||
called after init; also after dtor every time the file is reloaded.
|
called after init; also after dtor every time the file is reloaded.
|
||||||
|
|
||||||
static int Type_reload(Res1* r, const char* filename, Handle);
|
static LibError Type_reload(Res1* r, const char* filename, Handle);
|
||||||
{
|
{
|
||||||
// already loaded; done
|
// already loaded; done
|
||||||
if(r->data)
|
if(r->data)
|
||||||
@ -180,16 +180,16 @@ or "compact/free extraneous resources" may be added.
|
|||||||
|
|
||||||
validate:
|
validate:
|
||||||
makes sure the resource control block is in a valid state. returns 0 if
|
makes sure the resource control block is in a valid state. returns 0 if
|
||||||
all is well, or a distinct negative value (error code, if appropriate).
|
all is well, or a negative error code.
|
||||||
called automatically when the Handle is dereferenced or freed.
|
called automatically when the Handle is dereferenced or freed.
|
||||||
|
|
||||||
static int Type_validate(const Res1* r);
|
static LibError Type_validate(const Res1* r);
|
||||||
{
|
{
|
||||||
const int permissible_flags = 0x01;
|
const int permissible_flags = 0x01;
|
||||||
if(debug_is_pointer_bogus(r->data))
|
if(debug_is_pointer_bogus(r->data))
|
||||||
return -2;
|
return ERR_1;
|
||||||
if(r->flags & ~permissible_flags)
|
if(r->flags & ~permissible_flags)
|
||||||
return -3;
|
return ERR_2;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,7 +201,7 @@ Handle res1_load(const char* filename, int my_flags)
|
|||||||
return h_alloc(H_Res1, filename, 0, my_flags);
|
return h_alloc(H_Res1, filename, 0, my_flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
int res1_free(Handle& h)
|
LibError res1_free(Handle& h)
|
||||||
{
|
{
|
||||||
// control block is automatically zeroed after this.
|
// control block is automatically zeroed after this.
|
||||||
return h_free(h, H_Res1);
|
return h_free(h, H_Res1);
|
||||||
@ -308,11 +308,11 @@ but- has to handle variable params, a bit ugly
|
|||||||
// dependencies.
|
// dependencies.
|
||||||
struct H_VTbl
|
struct H_VTbl
|
||||||
{
|
{
|
||||||
void(*init)(void* user, va_list);
|
void (*init)(void* user, va_list);
|
||||||
int(*reload)(void* user, const char* fn, Handle);
|
LibError (*reload)(void* user, const char* fn, Handle);
|
||||||
void(*dtor)(void* user);
|
void (*dtor)(void* user);
|
||||||
int(*validate)(const void* user);
|
LibError (*validate)(const void* user);
|
||||||
int(*to_string)(const void* user, char* buf);
|
LibError (*to_string)(const void* user, char* buf);
|
||||||
size_t user_size;
|
size_t user_size;
|
||||||
const char* name;
|
const char* name;
|
||||||
};
|
};
|
||||||
@ -322,17 +322,17 @@ typedef H_VTbl* H_Type;
|
|||||||
#define H_TYPE_DEFINE(type)\
|
#define H_TYPE_DEFINE(type)\
|
||||||
/* forward decls */\
|
/* forward decls */\
|
||||||
static void type##_init(type*, va_list);\
|
static void type##_init(type*, va_list);\
|
||||||
static int type##_reload(type*, const char*, Handle);\
|
static LibError type##_reload(type*, const char*, Handle);\
|
||||||
static void type##_dtor(type*);\
|
static void type##_dtor(type*);\
|
||||||
static int type##_validate(const type*);\
|
static LibError type##_validate(const type*);\
|
||||||
static int type##_to_string(const type*, char* buf);\
|
static LibError type##_to_string(const type*, char* buf);\
|
||||||
static H_VTbl V_##type =\
|
static H_VTbl V_##type =\
|
||||||
{\
|
{\
|
||||||
(void(*)(void*, va_list))type##_init,\
|
(void (*)(void*, va_list))type##_init,\
|
||||||
(int(*)(void*, const char*, Handle))type##_reload,\
|
(LibError (*)(void*, const char*, Handle))type##_reload,\
|
||||||
(void(*)(void*))type##_dtor,\
|
(void (*)(void*))type##_dtor,\
|
||||||
(int(*)(const void*))type##_validate,\
|
(LibError (*)(const void*))type##_validate,\
|
||||||
(int(*)(const void*, char*))type##_to_string,\
|
(LibError (*)(const void*, char*))type##_to_string,\
|
||||||
sizeof(type), /* control block size */\
|
sizeof(type), /* control block size */\
|
||||||
#type /* name */\
|
#type /* name */\
|
||||||
};\
|
};\
|
||||||
@ -410,7 +410,7 @@ const size_t H_STRING_LEN = 256;
|
|||||||
// dtor is associated with type and called when the object is freed.
|
// 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.
|
// handle data is initialized to 0; optionally, a pointer to it is returned.
|
||||||
extern Handle h_alloc(H_Type type, const char* fn, uint flags = 0, ...);
|
extern Handle h_alloc(H_Type type, const char* fn, uint flags = 0, ...);
|
||||||
extern int h_free(Handle& h, H_Type type);
|
extern LibError h_free(Handle& h, H_Type type);
|
||||||
|
|
||||||
|
|
||||||
// find and return a handle by key (typically filename hash)
|
// find and return a handle by key (typically filename hash)
|
||||||
@ -428,7 +428,7 @@ extern void* h_user_data(Handle h, H_Type type);
|
|||||||
extern const char* h_filename(Handle h);
|
extern const char* h_filename(Handle h);
|
||||||
|
|
||||||
|
|
||||||
extern int h_reload(const char* fn);
|
extern LibError h_reload(const char* fn);
|
||||||
|
|
||||||
// force the resource to be freed immediately, even if cached.
|
// force the resource to be freed immediately, even if cached.
|
||||||
// tag is not checked - this allows the first Handle returned
|
// tag is not checked - this allows the first Handle returned
|
||||||
@ -436,7 +436,7 @@ extern int h_reload(const char* fn);
|
|||||||
// to later close the object.
|
// to later close the object.
|
||||||
// this is used when reinitializing the sound engine -
|
// this is used when reinitializing the sound engine -
|
||||||
// at that point, all (cached) OpenAL resources must be freed.
|
// at that point, all (cached) OpenAL resources must be freed.
|
||||||
extern int h_force_free(Handle h, H_Type type);
|
extern LibError h_force_free(Handle h, H_Type type);
|
||||||
|
|
||||||
// increment Handle <h>'s reference count.
|
// increment Handle <h>'s reference count.
|
||||||
// only meant to be used for objects that free a Handle in their dtor,
|
// only meant to be used for objects that free a Handle in their dtor,
|
||||||
|
@ -158,26 +158,26 @@ static void Mem_dtor(Mem* m)
|
|||||||
|
|
||||||
// can't alloc here, because h_alloc needs the key when called
|
// can't alloc here, because h_alloc needs the key when called
|
||||||
// (key == pointer we allocate)
|
// (key == pointer we allocate)
|
||||||
static int Mem_reload(Mem* m, const char* UNUSED(fn), Handle hm)
|
static LibError Mem_reload(Mem* m, const char* UNUSED(fn), Handle hm)
|
||||||
{
|
{
|
||||||
set_alloc(m->raw_p, hm);
|
set_alloc(m->raw_p, hm);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int Mem_validate(const Mem* m)
|
static LibError Mem_validate(const Mem* m)
|
||||||
{
|
{
|
||||||
if(debug_is_pointer_bogus(m->p))
|
if(debug_is_pointer_bogus(m->p))
|
||||||
return -2;
|
return ERR_1;
|
||||||
if(!m->size)
|
if(!m->size)
|
||||||
return -3;
|
return ERR_2;
|
||||||
if(m->raw_p && m->raw_p > m->p)
|
if(m->raw_p && m->raw_p > m->p)
|
||||||
return -4;
|
return ERR_3;
|
||||||
if(m->raw_size && m->raw_size < m->size)
|
if(m->raw_size && m->raw_size < m->size)
|
||||||
return -5;
|
return ERR_4;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int Mem_to_string(const Mem* m, char* buf)
|
static LibError Mem_to_string(const Mem* m, char* buf)
|
||||||
{
|
{
|
||||||
char owner_sym[DBG_SYMBOL_LEN];
|
char owner_sym[DBG_SYMBOL_LEN];
|
||||||
if(debug_resolve_symbol(m->owner, owner_sym, 0, 0) < 0)
|
if(debug_resolve_symbol(m->owner, owner_sym, 0, 0) < 0)
|
||||||
@ -189,7 +189,7 @@ static int Mem_to_string(const Mem* m, char* buf)
|
|||||||
}
|
}
|
||||||
|
|
||||||
snprintf(buf, H_STRING_LEN, "p=%p size=%d owner=%s", m->p, m->size, owner_sym);
|
snprintf(buf, H_STRING_LEN, "p=%p size=%d owner=%s", m->p, m->size, owner_sym);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -222,17 +222,17 @@ static void* heap_alloc(size_t raw_size, uintptr_t& ctx)
|
|||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
int mem_free_h(Handle& hm)
|
LibError mem_free_h(Handle& hm)
|
||||||
{
|
{
|
||||||
SCOPED_LOCK;
|
SCOPED_LOCK;
|
||||||
return h_free(hm, H_Mem);
|
return h_free(hm, H_Mem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int mem_free_p(void*& p)
|
LibError mem_free_p(void*& p)
|
||||||
{
|
{
|
||||||
if(!p)
|
if(!p)
|
||||||
return 0;
|
return ERR_OK;
|
||||||
|
|
||||||
Handle hm;
|
Handle hm;
|
||||||
{
|
{
|
||||||
@ -244,7 +244,7 @@ int mem_free_p(void*& p)
|
|||||||
if(hm <= 0)
|
if(hm <= 0)
|
||||||
{
|
{
|
||||||
debug_warn("mem_free_p: not found in map");
|
debug_warn("mem_free_p: not found in map");
|
||||||
return -1;
|
return ERR_FAIL;
|
||||||
}
|
}
|
||||||
return mem_free_h(hm);
|
return mem_free_h(hm);
|
||||||
}
|
}
|
||||||
@ -284,7 +284,7 @@ Handle mem_wrap(void* p, size_t size, uint flags, void* raw_p, size_t raw_size,
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
int mem_assign_user(Handle hm, void* user_p, size_t user_size)
|
LibError mem_assign_user(Handle hm, void* user_p, size_t user_size)
|
||||||
{
|
{
|
||||||
H_DEREF(hm, Mem, m);
|
H_DEREF(hm, Mem, m);
|
||||||
|
|
||||||
@ -295,12 +295,12 @@ int mem_assign_user(Handle hm, void* user_p, size_t user_size)
|
|||||||
if(user_p < m->raw_p || user_end > raw_end)
|
if(user_p < m->raw_p || user_end > raw_end)
|
||||||
{
|
{
|
||||||
debug_warn("mem_assign_user: user buffer not contained in real buffer");
|
debug_warn("mem_assign_user: user buffer not contained in real buffer");
|
||||||
return -EINVAL;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
m->p = user_p;
|
m->p = user_p;
|
||||||
m->size = user_size;
|
m->size = user_size;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -392,7 +392,7 @@ void* mem_get_ptr(Handle hm, size_t* user_size /* = 0 */)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int mem_get(Handle hm, u8** pp, size_t* psize)
|
LibError mem_get(Handle hm, u8** pp, size_t* psize)
|
||||||
{
|
{
|
||||||
SCOPED_LOCK;
|
SCOPED_LOCK;
|
||||||
|
|
||||||
@ -402,7 +402,7 @@ int mem_get(Handle hm, u8** pp, size_t* psize)
|
|||||||
if(psize)
|
if(psize)
|
||||||
*psize = m->size;
|
*psize = m->size;
|
||||||
// leave hm locked
|
// leave hm locked
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef void(*MEM_DTOR)(void* p, size_t size, uintptr_t ctx);
|
typedef void (*MEM_DTOR)(void* p, size_t size, uintptr_t ctx);
|
||||||
|
|
||||||
// mem_alloc flags
|
// mem_alloc flags
|
||||||
enum
|
enum
|
||||||
@ -21,14 +21,14 @@ enum
|
|||||||
extern void* mem_alloc(size_t size, size_t align = 1, uint flags = 0, Handle* ph = 0);
|
extern void* mem_alloc(size_t size, size_t align = 1, uint flags = 0, Handle* ph = 0);
|
||||||
|
|
||||||
#define mem_free(p) mem_free_p((void*&)p)
|
#define mem_free(p) mem_free_p((void*&)p)
|
||||||
extern int mem_free_p(void*& p);
|
extern LibError mem_free_p(void*& p);
|
||||||
|
|
||||||
extern int mem_free_h(Handle& hm);
|
extern LibError mem_free_h(Handle& hm);
|
||||||
|
|
||||||
// returns 0 if the handle is invalid
|
// returns 0 if the handle is invalid
|
||||||
extern void* mem_get_ptr(Handle h, size_t* size = 0);
|
extern void* mem_get_ptr(Handle h, size_t* size = 0);
|
||||||
|
|
||||||
extern int mem_get(Handle hm, u8** pp, size_t* psize);
|
extern LibError mem_get(Handle hm, u8** pp, size_t* psize);
|
||||||
|
|
||||||
|
|
||||||
extern Handle mem_wrap(void* p, size_t size, uint flags, void* raw_p, size_t raw_size, MEM_DTOR dtor, uintptr_t ctx, void* owner);
|
extern Handle mem_wrap(void* p, size_t size, uint flags, void* raw_p, size_t raw_size, MEM_DTOR dtor, uintptr_t ctx, void* owner);
|
||||||
|
@ -91,14 +91,14 @@ static bool al_initialized = false;
|
|||||||
|
|
||||||
|
|
||||||
// used by snd_dev_set to reset OpenAL after device has been changed.
|
// used by snd_dev_set to reset OpenAL after device has been changed.
|
||||||
static int al_reinit();
|
static LibError al_reinit();
|
||||||
|
|
||||||
// used by VSrc_reload to init on demand.
|
// used by VSrc_reload to init on demand.
|
||||||
static int snd_init();
|
static LibError snd_init();
|
||||||
|
|
||||||
// used by al_shutdown to free all VSrc and SndData objects, respectively,
|
// used by al_shutdown to free all VSrc and SndData objects, respectively,
|
||||||
// so that they release their OpenAL sources and buffers.
|
// so that they release their OpenAL sources and buffers.
|
||||||
static int list_free_all();
|
static LibError list_free_all();
|
||||||
static void hsd_list_free_all();
|
static void hsd_list_free_all();
|
||||||
|
|
||||||
|
|
||||||
@ -146,14 +146,14 @@ static const char* alc_dev_name = 0;
|
|||||||
// so preferably call this routine before sounds are loaded.
|
// so preferably call this routine before sounds are loaded.
|
||||||
//
|
//
|
||||||
// return 0 on success, or the status returned by OpenAL re-init.
|
// return 0 on success, or the status returned by OpenAL re-init.
|
||||||
int snd_dev_set(const char* alc_new_dev_name)
|
LibError snd_dev_set(const char* alc_new_dev_name)
|
||||||
{
|
{
|
||||||
// requesting a specific device
|
// requesting a specific device
|
||||||
if(alc_new_dev_name)
|
if(alc_new_dev_name)
|
||||||
{
|
{
|
||||||
// already using that device - done. (don't re-init)
|
// already using that device - done. (don't re-init)
|
||||||
if(alc_dev_name && !strcmp(alc_dev_name, alc_new_dev_name))
|
if(alc_dev_name && !strcmp(alc_dev_name, alc_new_dev_name))
|
||||||
return 0;
|
return ERR_OK;
|
||||||
|
|
||||||
// store name (need to copy it, since alc_init is called later,
|
// store name (need to copy it, since alc_init is called later,
|
||||||
// and it must then still be valid)
|
// and it must then still be valid)
|
||||||
@ -166,7 +166,7 @@ int snd_dev_set(const char* alc_new_dev_name)
|
|||||||
{
|
{
|
||||||
// already using default device - done. (don't re-init)
|
// already using default device - done. (don't re-init)
|
||||||
if(alc_dev_name == 0)
|
if(alc_dev_name == 0)
|
||||||
return 0;
|
return ERR_OK;
|
||||||
|
|
||||||
alc_dev_name = 0;
|
alc_dev_name = 0;
|
||||||
}
|
}
|
||||||
@ -193,9 +193,9 @@ static void alc_shutdown()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int alc_init()
|
static LibError alc_init()
|
||||||
{
|
{
|
||||||
int ret = 0;
|
LibError ret = ERR_OK;
|
||||||
|
|
||||||
// HACK: OpenAL loads and unloads these DLLs several times on Windows.
|
// HACK: OpenAL loads and unloads these DLLs several times on Windows.
|
||||||
// we hold a reference to prevent the actual unload,
|
// we hold a reference to prevent the actual unload,
|
||||||
@ -242,7 +242,7 @@ static int alc_init()
|
|||||||
if(err != ALC_NO_ERROR || !alc_dev || !alc_ctx)
|
if(err != ALC_NO_ERROR || !alc_dev || !alc_ctx)
|
||||||
{
|
{
|
||||||
debug_printf("alc_init failed. alc_dev=%p alc_ctx=%p alc_dev_name=%s err=%d\n", alc_dev, alc_ctx, alc_dev_name, err);
|
debug_printf("alc_init failed. alc_dev=%p alc_ctx=%p alc_dev_name=%s err=%d\n", alc_dev, alc_ctx, alc_dev_name, err);
|
||||||
ret = -1;
|
ret = ERR_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// release DLL references, so BoundsChecker doesn't complain at exit.
|
// release DLL references, so BoundsChecker doesn't complain at exit.
|
||||||
@ -287,7 +287,7 @@ static void al_listener_latch()
|
|||||||
|
|
||||||
// set amplitude modifier, which is effectively applied to all sounds.
|
// set amplitude modifier, which is effectively applied to all sounds.
|
||||||
// must be non-negative; 1 -> unattenuated, 0.5 -> -6 dB, 0 -> silence.
|
// must be non-negative; 1 -> unattenuated, 0.5 -> -6 dB, 0 -> silence.
|
||||||
int snd_set_master_gain(float gain)
|
LibError snd_set_master_gain(float gain)
|
||||||
{
|
{
|
||||||
if(gain < 0)
|
if(gain < 0)
|
||||||
{
|
{
|
||||||
@ -301,7 +301,7 @@ int snd_set_master_gain(float gain)
|
|||||||
// position will get sent too.
|
// position will get sent too.
|
||||||
// this isn't called often, so we don't care.
|
// this isn't called often, so we don't care.
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -459,7 +459,7 @@ static void al_src_free(ALuint al_src)
|
|||||||
// to reduce mixing cost on low-end systems.
|
// to reduce mixing cost on low-end systems.
|
||||||
// return 0 on success, or 1 if limit was ignored
|
// return 0 on success, or 1 if limit was ignored
|
||||||
// (e.g. if higher than an implementation-defined limit anyway).
|
// (e.g. if higher than an implementation-defined limit anyway).
|
||||||
int snd_set_max_voices(uint limit)
|
LibError snd_set_max_voices(uint limit)
|
||||||
{
|
{
|
||||||
// valid if cap is legit (less than what we allocated in al_src_init),
|
// valid if cap is legit (less than what we allocated in al_src_init),
|
||||||
// or if al_src_init hasn't been called yet. note: we accept anything
|
// or if al_src_init hasn't been called yet. note: we accept anything
|
||||||
@ -467,13 +467,15 @@ int snd_set_max_voices(uint limit)
|
|||||||
if(!al_src_allocated || limit < al_src_allocated)
|
if(!al_src_allocated || limit < al_src_allocated)
|
||||||
{
|
{
|
||||||
al_src_cap = limit;
|
al_src_cap = limit;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
// user is requesting a cap higher than what we actually allocated.
|
// user is requesting a cap higher than what we actually allocated.
|
||||||
// that's fine (not an error), but we won't set the cap, since it
|
// that's fine (not an error), but we won't set the cap, since it
|
||||||
// determines how many sources may be returned.
|
// determines how many sources may be returned.
|
||||||
|
// there's no return value to indicate this because the cap is
|
||||||
|
// precisely that - an upper limit only, we don't care if it can't be met.
|
||||||
else
|
else
|
||||||
return 1;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -486,11 +488,11 @@ int snd_set_max_voices(uint limit)
|
|||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// called from each snd_open; no harm if called more than once.
|
// called from each snd_open; no harm if called more than once.
|
||||||
static int al_init()
|
static LibError al_init()
|
||||||
{
|
{
|
||||||
// only take action on first call, OR when re-initializing.
|
// only take action on first call, OR when re-initializing.
|
||||||
if(al_initialized)
|
if(al_initialized)
|
||||||
return 0;
|
return ERR_OK;
|
||||||
|
|
||||||
CHECK_ERR(alc_init());
|
CHECK_ERR(alc_init());
|
||||||
|
|
||||||
@ -500,7 +502,7 @@ static int al_init()
|
|||||||
al_src_init();
|
al_src_init();
|
||||||
al_listener_latch();
|
al_listener_latch();
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -532,12 +534,12 @@ static void al_shutdown()
|
|||||||
|
|
||||||
|
|
||||||
// called from snd_dev_set (no other settings require re-init ATM).
|
// called from snd_dev_set (no other settings require re-init ATM).
|
||||||
static int al_reinit()
|
static LibError al_reinit()
|
||||||
{
|
{
|
||||||
// not yet initialized. settings have been saved, and will be
|
// not yet initialized. settings have been saved, and will be
|
||||||
// applied by the component init routines called from al_init.
|
// applied by the component init routines called from al_init.
|
||||||
if(!al_initialized)
|
if(!al_initialized)
|
||||||
return 0;
|
return ERR_OK;
|
||||||
|
|
||||||
// re-init (stops all currently playing sounds)
|
// re-init (stops all currently playing sounds)
|
||||||
al_shutdown();
|
al_shutdown();
|
||||||
@ -563,13 +565,13 @@ static const char* devs;
|
|||||||
// message should be presented to the user, and snd_dev_set need not be
|
// message should be presented to the user, and snd_dev_set need not be
|
||||||
// called; OpenAL will use its default device.
|
// called; OpenAL will use its default device.
|
||||||
// may be called each time the device list is needed.
|
// may be called each time the device list is needed.
|
||||||
int snd_dev_prepare_enum()
|
LibError snd_dev_prepare_enum()
|
||||||
{
|
{
|
||||||
if(alcIsExtensionPresent(0, (ALubyte*)"ALC_ENUMERATION_EXT") != AL_TRUE)
|
if(alcIsExtensionPresent(0, (ALubyte*)"ALC_ENUMERATION_EXT") != AL_TRUE)
|
||||||
return ERR_NO_SYS;
|
return ERR_NO_SYS;
|
||||||
|
|
||||||
devs = (const char*)alcGetString(0, ALC_DEVICE_SPECIFIER);
|
devs = (const char*)alcGetString(0, ALC_DEVICE_SPECIFIER);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -694,10 +696,10 @@ struct Stream
|
|||||||
};
|
};
|
||||||
|
|
||||||
// called from SndData_reload and snd_data_buf_get.
|
// called from SndData_reload and snd_data_buf_get.
|
||||||
static int stream_issue(Stream* s)
|
static LibError stream_issue(Stream* s)
|
||||||
{
|
{
|
||||||
if(s->active_ios >= MAX_IOS)
|
if(s->active_ios >= MAX_IOS)
|
||||||
return 0;
|
return ERR_OK;
|
||||||
|
|
||||||
void* buf = io_buf_alloc();
|
void* buf = io_buf_alloc();
|
||||||
if(!buf)
|
if(!buf)
|
||||||
@ -706,14 +708,14 @@ static int stream_issue(Stream* s)
|
|||||||
Handle h = vfs_io_issue(s->hf, STREAM_BUF_SIZE, buf);
|
Handle h = vfs_io_issue(s->hf, STREAM_BUF_SIZE, buf);
|
||||||
CHECK_ERR(h);
|
CHECK_ERR(h);
|
||||||
s->ios[s->active_ios++] = h;
|
s->ios[s->active_ios++] = h;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// if the first pending IO hasn't completed, return ERR_AGAIN;
|
// if the first pending IO hasn't completed, return ERR_AGAIN;
|
||||||
// otherwise, return a negative error code or 0 on success,
|
// otherwise, return a negative error code or 0 on success,
|
||||||
// if the pending IO's buffer is returned.
|
// if the pending IO's buffer is returned.
|
||||||
static int stream_buf_get(Stream* s, void*& data, size_t& size)
|
static LibError stream_buf_get(Stream* s, void*& data, size_t& size)
|
||||||
{
|
{
|
||||||
if(s->active_ios == 0)
|
if(s->active_ios == 0)
|
||||||
return ERR_EOF;
|
return ERR_EOF;
|
||||||
@ -721,17 +723,17 @@ static int stream_buf_get(Stream* s, void*& data, size_t& size)
|
|||||||
|
|
||||||
// has it finished? if not, bail.
|
// has it finished? if not, bail.
|
||||||
int is_complete = vfs_io_has_completed(hio);
|
int is_complete = vfs_io_has_completed(hio);
|
||||||
CHECK_ERR(is_complete);
|
RETURN_ERR(is_complete);
|
||||||
if(is_complete == 0)
|
if(is_complete == 0)
|
||||||
return ERR_AGAIN;
|
return ERR_AGAIN;
|
||||||
|
|
||||||
// get its buffer.
|
// get its buffer.
|
||||||
CHECK_ERR(vfs_io_wait(hio, data, size));
|
RETURN_ERR(vfs_io_wait(hio, data, size));
|
||||||
// no delay, since vfs_io_has_completed == 1
|
// no delay, since vfs_io_has_completed == 1
|
||||||
|
|
||||||
s->last_buf = data;
|
s->last_buf = data;
|
||||||
// (next stream_buf_discard will free this buffer)
|
// (next stream_buf_discard will free this buffer)
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -739,11 +741,11 @@ static int stream_buf_get(Stream* s, void*& data, size_t& size)
|
|||||||
// and remove its IO slot from our queue.
|
// and remove its IO slot from our queue.
|
||||||
// must be called exactly once after every successful stream_buf_get;
|
// must be called exactly once after every successful stream_buf_get;
|
||||||
// call before calling any other stream_* functions!
|
// call before calling any other stream_* functions!
|
||||||
static int stream_buf_discard(Stream* s)
|
static LibError stream_buf_discard(Stream* s)
|
||||||
{
|
{
|
||||||
Handle hio = s->ios[0];
|
Handle hio = s->ios[0];
|
||||||
|
|
||||||
int ret = vfs_io_discard(hio);
|
LibError ret = vfs_io_discard(hio);
|
||||||
|
|
||||||
// we implement the required 'circular queue' as a stack;
|
// we implement the required 'circular queue' as a stack;
|
||||||
// have to shift all items after this one down.
|
// have to shift all items after this one down.
|
||||||
@ -762,13 +764,13 @@ static uint active_streams;
|
|||||||
|
|
||||||
|
|
||||||
// open a stream and begin reading from disk.
|
// open a stream and begin reading from disk.
|
||||||
static int stream_open(Stream* s, const char* fn)
|
static LibError stream_open(Stream* s, const char* fn)
|
||||||
{
|
{
|
||||||
if(active_streams >= MAX_STREAMS)
|
if(active_streams >= MAX_STREAMS)
|
||||||
{
|
{
|
||||||
debug_warn("MAX_STREAMS exceeded - why?");
|
debug_warn("MAX_STREAMS exceeded - why?");
|
||||||
return -1;
|
// bail because we wouldn't have enough IO buffers for all
|
||||||
// fail, because we wouldn't have enough IO buffers for all
|
return ERR_LIMIT;
|
||||||
}
|
}
|
||||||
active_streams++;
|
active_streams++;
|
||||||
|
|
||||||
@ -778,16 +780,16 @@ static int stream_open(Stream* s, const char* fn)
|
|||||||
for(int i = 0; i < MAX_IOS; i++)
|
for(int i = 0; i < MAX_IOS; i++)
|
||||||
CHECK_ERR(stream_issue(s));
|
CHECK_ERR(stream_issue(s));
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// close a stream, which may currently be active.
|
// close a stream, which may currently be active.
|
||||||
// returns the first error that occurred while waiting for IOs / closing file.
|
// returns the first error that occurred while waiting for IOs / closing file.
|
||||||
static int stream_close(Stream* s)
|
static LibError stream_close(Stream* s)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
LibError ret = ERR_OK;
|
||||||
int err;
|
LibError err;
|
||||||
|
|
||||||
// for each pending IO:
|
// for each pending IO:
|
||||||
for(uint i = 0; i < s->active_ios; i++)
|
for(uint i = 0; i < s->active_ios; i++)
|
||||||
@ -816,12 +818,12 @@ static int stream_close(Stream* s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int stream_validate(const Stream* s)
|
static LibError stream_validate(const Stream* s)
|
||||||
{
|
{
|
||||||
if(s->active_ios > MAX_IOS)
|
if(s->active_ios > MAX_IOS)
|
||||||
return -2;
|
return ERR_1;
|
||||||
// <last_buf> has no invariant we could check.
|
// <last_buf> has no invariant we could check.
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -894,7 +896,7 @@ if(sd->o) ogg_release(sd->o);
|
|||||||
|
|
||||||
static void hsd_list_add(Handle hsd);
|
static void hsd_list_add(Handle hsd);
|
||||||
|
|
||||||
static int SndData_reload(SndData* sd, const char* fn, Handle hsd)
|
static LibError SndData_reload(SndData* sd, const char* fn, Handle hsd)
|
||||||
{
|
{
|
||||||
hsd_list_add(hsd);
|
hsd_list_add(hsd);
|
||||||
|
|
||||||
@ -942,11 +944,11 @@ static int SndData_reload(SndData* sd, const char* fn, Handle hsd)
|
|||||||
// refuse to stream anything that cannot be passed directly to OpenAL -
|
// refuse to stream anything that cannot be passed directly to OpenAL -
|
||||||
// we'd have to extract the audio data ourselves (not worth it).
|
// we'd have to extract the audio data ourselves (not worth it).
|
||||||
if(file_type != FT_OGG)
|
if(file_type != FT_OGG)
|
||||||
return -1;
|
return ERR_NOT_SUPPORTED;
|
||||||
|
|
||||||
RETURN_ERR(stream_open(&sd->s, fn));
|
RETURN_ERR(stream_open(&sd->s, fn));
|
||||||
sd->is_valid = 1;
|
sd->is_valid = 1;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// else: clip
|
// else: clip
|
||||||
@ -998,29 +1000,29 @@ else
|
|||||||
|
|
||||||
(void)mem_free(file);
|
(void)mem_free(file);
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int SndData_validate(const SndData* sd)
|
static LibError SndData_validate(const SndData* sd)
|
||||||
{
|
{
|
||||||
if(sd->al_fmt == 0)
|
if(sd->al_fmt == 0)
|
||||||
return -100;
|
return ERR_11;
|
||||||
if((uint)sd->al_freq > 100000) // suspicious
|
if((uint)sd->al_freq > 100000) // suspicious
|
||||||
return -101;
|
return ERR_12;
|
||||||
if(sd->al_buf == 0)
|
if(sd->al_buf == 0)
|
||||||
return -102;
|
return ERR_13;
|
||||||
|
|
||||||
if(sd->is_stream)
|
if(sd->is_stream)
|
||||||
RETURN_ERR(stream_validate(&sd->s));
|
RETURN_ERR(stream_validate(&sd->s));
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int SndData_to_string(const SndData* sd, char* buf)
|
static LibError SndData_to_string(const SndData* sd, char* buf)
|
||||||
{
|
{
|
||||||
const char* type = sd->is_stream? "stream" : "clip";
|
const char* type = sd->is_stream? "stream" : "clip";
|
||||||
snprintf(buf, H_STRING_LEN, "%s; al_buf=%d", type, sd->al_buf);
|
snprintf(buf, H_STRING_LEN, "%s; al_buf=%d", type, sd->al_buf);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1037,7 +1039,7 @@ static Handle snd_data_load(const char* fn, bool is_stream)
|
|||||||
|
|
||||||
|
|
||||||
// close the sound file data <hsd> and set hsd to 0.
|
// close the sound file data <hsd> and set hsd to 0.
|
||||||
static int snd_data_free(Handle& hsd)
|
static LibError snd_data_free(Handle& hsd)
|
||||||
{
|
{
|
||||||
return h_free(hsd, H_SndData);
|
return h_free(hsd, H_SndData);
|
||||||
}
|
}
|
||||||
@ -1097,26 +1099,16 @@ static void hsd_list_free_all()
|
|||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// returns:
|
||||||
// (need to convert ERR_EOF and ERR_AGAIN to legitimate return values -
|
// ERR_OK = buffer has been returned; more are expected to be available.
|
||||||
// for the caller, those aren't errors.)
|
// ERR_EOF = buffer has been returned but is the last one
|
||||||
enum BufRet
|
// (end of file reached)
|
||||||
|
// ERR_AGAIN = no buffer returned yet; still streaming in ATM.
|
||||||
|
// call back later.
|
||||||
|
// or negative error code.
|
||||||
|
static LibError snd_data_buf_get(Handle hsd, ALuint& al_buf)
|
||||||
{
|
{
|
||||||
// buffer has been returned; barring errors, more will be available.
|
LibError err = ERR_OK;
|
||||||
BUF_OK = 0,
|
|
||||||
|
|
||||||
// this was the last buffer we will return (end of file reached).
|
|
||||||
BUF_EOF = 1,
|
|
||||||
|
|
||||||
// no buffer returned - still streaming in ATM. call again later.
|
|
||||||
BUF_AGAIN = 2,
|
|
||||||
|
|
||||||
// anything else: negative error code
|
|
||||||
};
|
|
||||||
|
|
||||||
static int snd_data_buf_get(Handle hsd, ALuint& al_buf)
|
|
||||||
{
|
|
||||||
int err = 0;
|
|
||||||
|
|
||||||
// in case H_DEREF fails
|
// in case H_DEREF fails
|
||||||
al_buf = 0;
|
al_buf = 0;
|
||||||
@ -1127,7 +1119,7 @@ static int snd_data_buf_get(Handle hsd, ALuint& al_buf)
|
|||||||
if(!sd->is_stream)
|
if(!sd->is_stream)
|
||||||
{
|
{
|
||||||
al_buf = sd->al_buf;
|
al_buf = sd->al_buf;
|
||||||
return BUF_EOF;
|
return ERR_EOF;
|
||||||
}
|
}
|
||||||
|
|
||||||
// stream:
|
// stream:
|
||||||
@ -1137,7 +1129,7 @@ static int snd_data_buf_get(Handle hsd, ALuint& al_buf)
|
|||||||
size_t size;
|
size_t size;
|
||||||
err = stream_buf_get(&sd->s, data, size);
|
err = stream_buf_get(&sd->s, data, size);
|
||||||
if(err == ERR_AGAIN)
|
if(err == ERR_AGAIN)
|
||||||
return BUF_AGAIN;
|
return ERR_AGAIN;
|
||||||
CHECK_ERR(err);
|
CHECK_ERR(err);
|
||||||
|
|
||||||
// .. yes: pass to OpenAL and discard IO buffer.
|
// .. yes: pass to OpenAL and discard IO buffer.
|
||||||
@ -1148,26 +1140,26 @@ static int snd_data_buf_get(Handle hsd, ALuint& al_buf)
|
|||||||
// if EOF reached, indicate al_buf is the last that will be returned.
|
// if EOF reached, indicate al_buf is the last that will be returned.
|
||||||
err = stream_issue(&sd->s);
|
err = stream_issue(&sd->s);
|
||||||
if(err == ERR_EOF)
|
if(err == ERR_EOF)
|
||||||
return BUF_EOF;
|
return ERR_EOF;
|
||||||
CHECK_ERR(err);
|
CHECK_ERR(err);
|
||||||
|
|
||||||
// al_buf valid and next IO issued successfully.
|
// al_buf valid and next IO issued successfully.
|
||||||
return BUF_OK;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int snd_data_buf_free(Handle hsd, ALuint al_buf)
|
static LibError snd_data_buf_free(Handle hsd, ALuint al_buf)
|
||||||
{
|
{
|
||||||
H_DEREF(hsd, SndData, sd);
|
H_DEREF(hsd, SndData, sd);
|
||||||
|
|
||||||
// clip: no-op (caller will later release hsd reference;
|
// clip: no-op (caller will later release hsd reference;
|
||||||
// when hsd actually unloads, sd->al_buf will be freed).
|
// when hsd actually unloads, sd->al_buf will be freed).
|
||||||
if(!sd->is_stream)
|
if(!sd->is_stream)
|
||||||
return 0;
|
return ERR_OK;
|
||||||
|
|
||||||
// stream: we had allocated an additional buffer, so free it now.
|
// stream: we had allocated an additional buffer, so free it now.
|
||||||
al_buf_free(al_buf);
|
al_buf_free(al_buf);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1232,7 +1224,7 @@ static void VSrc_init(VSrc* vs, va_list args)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void list_remove(VSrc* vs);
|
static void list_remove(VSrc* vs);
|
||||||
static int vsrc_reclaim(VSrc* vs);
|
static LibError vsrc_reclaim(VSrc* vs);
|
||||||
|
|
||||||
static void VSrc_dtor(VSrc* vs)
|
static void VSrc_dtor(VSrc* vs)
|
||||||
{
|
{
|
||||||
@ -1248,11 +1240,11 @@ static void VSrc_dtor(VSrc* vs)
|
|||||||
(void)snd_data_free(vs->hsd);
|
(void)snd_data_free(vs->hsd);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int VSrc_reload(VSrc* vs, const char* fn, Handle hvs)
|
static LibError VSrc_reload(VSrc* vs, const char* fn, Handle hvs)
|
||||||
{
|
{
|
||||||
// cannot wait till play(), need to init here:
|
// cannot wait till play(), need to init here:
|
||||||
// must load OpenAL so that snd_data_load can check for OGG extension.
|
// must load OpenAL so that snd_data_load can check for OGG extension.
|
||||||
int err = snd_init();
|
LibError err = snd_init();
|
||||||
// .. don't complain if sound is disabled; fail silently.
|
// .. don't complain if sound is disabled; fail silently.
|
||||||
if(err == ERR_AGAIN)
|
if(err == ERR_AGAIN)
|
||||||
return err;
|
return err;
|
||||||
@ -1303,29 +1295,29 @@ static int VSrc_reload(VSrc* vs, const char* fn, Handle hvs)
|
|||||||
vs->hsd = snd_data_load(snd_fn, is_stream);
|
vs->hsd = snd_data_load(snd_fn, is_stream);
|
||||||
RETURN_ERR(vs->hsd);
|
RETURN_ERR(vs->hsd);
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int VSrc_validate(const VSrc* vs)
|
static LibError VSrc_validate(const VSrc* vs)
|
||||||
{
|
{
|
||||||
// al_src can legitimately be 0 (if vs is low-pri)
|
// al_src can legitimately be 0 (if vs is low-pri)
|
||||||
if(vs->flags & ~VS_ALL_FLAGS)
|
if(vs->flags & ~VS_ALL_FLAGS)
|
||||||
return -2;
|
return ERR_1;
|
||||||
// no limitations on <pos>
|
// no limitations on <pos>
|
||||||
if(!(0.0f <= vs->gain && vs->gain <= 1.0f))
|
if(!(0.0f <= vs->gain && vs->gain <= 1.0f))
|
||||||
return -3;
|
return ERR_2;
|
||||||
if(!(0.0f < vs->pitch && vs->pitch <= 1.0f))
|
if(!(0.0f < vs->pitch && vs->pitch <= 1.0f))
|
||||||
return -4;
|
return ERR_3;
|
||||||
if(*(u8*)&vs->loop > 1 || *(u8*)&vs->relative > 1)
|
if(*(u8*)&vs->loop > 1 || *(u8*)&vs->relative > 1)
|
||||||
return -5;
|
return ERR_4;
|
||||||
// <static_pri> and <cur_pri> have no invariant we could check.
|
// <static_pri> and <cur_pri> have no invariant we could check.
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int VSrc_to_string(const VSrc* vs, char* buf)
|
static LibError VSrc_to_string(const VSrc* vs, char* buf)
|
||||||
{
|
{
|
||||||
snprintf(buf, H_STRING_LEN, "al_src = %d", vs->al_src);
|
snprintf(buf, H_STRING_LEN, "al_src = %d", vs->al_src);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1355,7 +1347,7 @@ Handle snd_open(const char* snd_fn, bool is_stream)
|
|||||||
// close the sound <hvs> and set hvs to 0. if it was playing,
|
// close the sound <hvs> and set hvs to 0. if it was playing,
|
||||||
// it will be stopped. sounds are closed automatically when done
|
// it will be stopped. sounds are closed automatically when done
|
||||||
// playing; this is provided for completeness only.
|
// playing; this is provided for completeness only.
|
||||||
int snd_free(Handle& hvs)
|
LibError snd_free(Handle& hvs)
|
||||||
{
|
{
|
||||||
return h_free(hvs, H_VSrc);
|
return h_free(hvs, H_VSrc);
|
||||||
}
|
}
|
||||||
@ -1386,7 +1378,7 @@ static void list_add(VSrc* vs)
|
|||||||
|
|
||||||
|
|
||||||
// skip past <skip> entries; if end_idx != 0, stop before that entry.
|
// skip past <skip> entries; if end_idx != 0, stop before that entry.
|
||||||
static void list_foreach(void(*cb)(VSrc*), uint skip = 0, uint end_idx = 0)
|
static void list_foreach(void (*cb)(VSrc*), uint skip = 0, uint end_idx = 0)
|
||||||
{
|
{
|
||||||
It begin = vsrcs.begin() + skip;
|
It begin = vsrcs.begin() + skip;
|
||||||
It end = vsrcs.end();
|
It end = vsrcs.end();
|
||||||
@ -1453,10 +1445,10 @@ static void free_vs(VSrc* vs)
|
|||||||
snd_free(vs->hvs);
|
snd_free(vs->hvs);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int list_free_all()
|
static LibError list_free_all()
|
||||||
{
|
{
|
||||||
list_foreach(free_vs);
|
list_foreach(free_vs);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1497,10 +1489,10 @@ static int vsrc_deque_finished_bufs(VSrc* vs)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int vsrc_update(VSrc* vs)
|
static LibError vsrc_update(VSrc* vs)
|
||||||
{
|
{
|
||||||
if(!vs->al_src)
|
if(!vs->al_src)
|
||||||
return 0;
|
return ERR_OK;
|
||||||
|
|
||||||
int num_queued;
|
int num_queued;
|
||||||
alGetSourcei(vs->al_src, AL_BUFFERS_QUEUED, &num_queued);
|
alGetSourcei(vs->al_src, AL_BUFFERS_QUEUED, &num_queued);
|
||||||
@ -1514,7 +1506,7 @@ static int vsrc_update(VSrc* vs)
|
|||||||
if(num_queued == 0)
|
if(num_queued == 0)
|
||||||
{
|
{
|
||||||
snd_free(vs->hvs);
|
snd_free(vs->hvs);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// can still read from SndData
|
// can still read from SndData
|
||||||
@ -1532,35 +1524,37 @@ static int vsrc_update(VSrc* vs)
|
|||||||
{
|
{
|
||||||
ALuint al_buf;
|
ALuint al_buf;
|
||||||
ret = snd_data_buf_get(vs->hsd, al_buf);
|
ret = snd_data_buf_get(vs->hsd, al_buf);
|
||||||
CHECK_ERR(ret);
|
// these 2 are legit (see above); otherwise, bail.
|
||||||
|
if(ret != ERR_AGAIN && ret != ERR_EOF)
|
||||||
|
CHECK_ERR(ret);
|
||||||
|
|
||||||
alSourceQueueBuffers(vs->al_src, 1, &al_buf);
|
alSourceQueueBuffers(vs->al_src, 1, &al_buf);
|
||||||
al_check("vsrc_update alSourceQueueBuffers");
|
al_check("vsrc_update alSourceQueueBuffers");
|
||||||
}
|
}
|
||||||
while(to_fill-- && ret == BUF_OK);
|
while(to_fill-- && ret == ERR_OK);
|
||||||
|
|
||||||
// SndData has reported that no further buffers are available.
|
// SndData has reported that no further buffers are available.
|
||||||
if(ret == BUF_EOF)
|
if(ret == ERR_EOF)
|
||||||
vs->flags |= VS_EOF;
|
vs->flags |= VS_EOF;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// attempt to (re)start playing. fails if a source cannot be allocated
|
// attempt to (re)start playing. fails if a source cannot be allocated
|
||||||
// (see below). called by snd_play and voice management.
|
// (see below). called by snd_play and voice management.
|
||||||
static int vsrc_grant(VSrc* vs)
|
static LibError vsrc_grant(VSrc* vs)
|
||||||
{
|
{
|
||||||
// already playing - bail
|
// already playing - bail
|
||||||
if(vs->al_src)
|
if(vs->al_src)
|
||||||
return 0;
|
return ERR_OK;
|
||||||
|
|
||||||
// try to alloc source. if none are available, bail -
|
// try to alloc source. if none are available, bail -
|
||||||
// we get called in that hope that one is available by snd_play.
|
// we get called in that hope that one is available by snd_play.
|
||||||
vs->al_src = al_src_alloc();
|
vs->al_src = al_src_alloc();
|
||||||
if(!vs->al_src)
|
if(!vs->al_src)
|
||||||
return -1;
|
return ERR_FAIL;
|
||||||
|
|
||||||
// OpenAL docs don't specify default values, so initialize everything
|
// OpenAL docs don't specify default values, so initialize everything
|
||||||
// ourselves to be sure. note: alSourcefv param is not const.
|
// ourselves to be sure. note: alSourcefv param is not const.
|
||||||
@ -1579,18 +1573,18 @@ static int vsrc_grant(VSrc* vs)
|
|||||||
|
|
||||||
alSourcePlay(vs->al_src);
|
alSourcePlay(vs->al_src);
|
||||||
al_check("vsrc_grant alSourcePlay");
|
al_check("vsrc_grant alSourcePlay");
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// stop playback, and reclaim the OpenAL source.
|
// stop playback, and reclaim the OpenAL source.
|
||||||
// called when closing the VSrc, or when voice management decides
|
// called when closing the VSrc, or when voice management decides
|
||||||
// this VSrc must yield to others of higher priority.
|
// this VSrc must yield to others of higher priority.
|
||||||
static int vsrc_reclaim(VSrc* vs)
|
static LibError vsrc_reclaim(VSrc* vs)
|
||||||
{
|
{
|
||||||
// don't own a source - bail.
|
// don't own a source - bail.
|
||||||
if(!vs->al_src)
|
if(!vs->al_src)
|
||||||
return -1;
|
return ERR_FAIL;
|
||||||
|
|
||||||
alSourceStop(vs->al_src);
|
alSourceStop(vs->al_src);
|
||||||
al_check("src_stop");
|
al_check("src_stop");
|
||||||
@ -1600,7 +1594,7 @@ static int vsrc_reclaim(VSrc* vs)
|
|||||||
vsrc_deque_finished_bufs(vs);
|
vsrc_deque_finished_bufs(vs);
|
||||||
|
|
||||||
al_src_free(vs->al_src);
|
al_src_free(vs->al_src);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1613,7 +1607,7 @@ static int vsrc_reclaim(VSrc* vs)
|
|||||||
// priority (min 0 .. max 1, default 0) indicates which sounds are
|
// priority (min 0 .. max 1, default 0) indicates which sounds are
|
||||||
// considered more important; this is attenuated by distance to the
|
// considered more important; this is attenuated by distance to the
|
||||||
// listener (see snd_update).
|
// listener (see snd_update).
|
||||||
int snd_play(Handle hs, float static_pri)
|
LibError snd_play(Handle hs, float static_pri)
|
||||||
{
|
{
|
||||||
H_DEREF(hs, VSrc, vs);
|
H_DEREF(hs, VSrc, vs);
|
||||||
|
|
||||||
@ -1628,7 +1622,7 @@ int snd_play(Handle hs, float static_pri)
|
|||||||
// either we get a source and playing begins immediately,
|
// either we get a source and playing begins immediately,
|
||||||
// or it'll be taken care of on next update.
|
// or it'll be taken care of on next update.
|
||||||
vsrc_grant(vs);
|
vsrc_grant(vs);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1637,7 +1631,7 @@ int snd_play(Handle hs, float static_pri)
|
|||||||
// listener; otherwise, it is the position in world coordinates.
|
// listener; otherwise, it is the position in world coordinates.
|
||||||
// may be called at any time; fails with invalid handle return if
|
// may be called at any time; fails with invalid handle return if
|
||||||
// the sound has already been closed (e.g. it never played).
|
// the sound has already been closed (e.g. it never played).
|
||||||
int snd_set_pos(Handle hvs, float x, float y, float z, bool relative)
|
LibError snd_set_pos(Handle hvs, float x, float y, float z, bool relative)
|
||||||
{
|
{
|
||||||
H_DEREF(hvs, VSrc, vs);
|
H_DEREF(hvs, VSrc, vs);
|
||||||
|
|
||||||
@ -1645,7 +1639,7 @@ int snd_set_pos(Handle hvs, float x, float y, float z, bool relative)
|
|||||||
vs->relative = relative;
|
vs->relative = relative;
|
||||||
|
|
||||||
vsrc_latch(vs);
|
vsrc_latch(vs);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1653,17 +1647,17 @@ int snd_set_pos(Handle hvs, float x, float y, float z, bool relative)
|
|||||||
// must be non-negative; 1 -> unattenuated, 0.5 -> -6 dB, 0 -> silence.
|
// must be non-negative; 1 -> unattenuated, 0.5 -> -6 dB, 0 -> silence.
|
||||||
// may be called at any time; fails with invalid handle return if
|
// may be called at any time; fails with invalid handle return if
|
||||||
// the sound has already been closed (e.g. it never played).
|
// the sound has already been closed (e.g. it never played).
|
||||||
int snd_set_gain(Handle hvs, float gain)
|
LibError snd_set_gain(Handle hvs, float gain)
|
||||||
{
|
{
|
||||||
H_DEREF(hvs, VSrc, vs);
|
H_DEREF(hvs, VSrc, vs);
|
||||||
|
|
||||||
if(!(0.0f <= gain && gain <= 1.0f))
|
if(!(0.0f <= gain && gain <= 1.0f))
|
||||||
return -1;
|
return ERR_INVALID_PARAM;
|
||||||
|
|
||||||
vs->gain = gain;
|
vs->gain = gain;
|
||||||
|
|
||||||
vsrc_latch(vs);
|
vsrc_latch(vs);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1672,17 +1666,17 @@ int snd_set_gain(Handle hvs, float gain)
|
|||||||
// -12 semitones (one octave). zero is invalid.
|
// -12 semitones (one octave). zero is invalid.
|
||||||
// may be called at any time; fails with invalid handle return if
|
// may be called at any time; fails with invalid handle return if
|
||||||
// the sound has already been closed (e.g. it never played).
|
// the sound has already been closed (e.g. it never played).
|
||||||
int snd_set_pitch(Handle hvs, float pitch)
|
LibError snd_set_pitch(Handle hvs, float pitch)
|
||||||
{
|
{
|
||||||
H_DEREF(hvs, VSrc, vs);
|
H_DEREF(hvs, VSrc, vs);
|
||||||
|
|
||||||
if(!(0.0f < pitch && pitch <= 1.0f))
|
if(!(0.0f < pitch && pitch <= 1.0f))
|
||||||
return -1;
|
return ERR_INVALID_PARAM;
|
||||||
|
|
||||||
vs->pitch = pitch;
|
vs->pitch = pitch;
|
||||||
|
|
||||||
vsrc_latch(vs);
|
vsrc_latch(vs);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1696,14 +1690,14 @@ int snd_set_pitch(Handle hvs, float pitch)
|
|||||||
// a hardware voice at the moment play was requested.
|
// a hardware voice at the moment play was requested.
|
||||||
// - once looping is again disabled and the sound has reached its end,
|
// - once looping is again disabled and the sound has reached its end,
|
||||||
// the sound instance is freed automatically (as if never looped).
|
// the sound instance is freed automatically (as if never looped).
|
||||||
int snd_set_loop(Handle hvs, bool loop)
|
LibError snd_set_loop(Handle hvs, bool loop)
|
||||||
{
|
{
|
||||||
H_DEREF(hvs, VSrc, vs);
|
H_DEREF(hvs, VSrc, vs);
|
||||||
|
|
||||||
vs->loop = loop;
|
vs->loop = loop;
|
||||||
|
|
||||||
vsrc_latch(vs);
|
vsrc_latch(vs);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1760,7 +1754,7 @@ static void grant(VSrc* vs)
|
|||||||
|
|
||||||
|
|
||||||
// no-op if OpenAL not yet initialized.
|
// no-op if OpenAL not yet initialized.
|
||||||
static int vm_update()
|
static LibError vm_update()
|
||||||
{
|
{
|
||||||
list_prune_removed();
|
list_prune_removed();
|
||||||
|
|
||||||
@ -1778,9 +1772,9 @@ static int vm_update()
|
|||||||
list_foreach(grant, 0, first_unimportant);
|
list_foreach(grant, 0, first_unimportant);
|
||||||
|
|
||||||
// add / remove buffers for each source.
|
// add / remove buffers for each source.
|
||||||
list_foreach((void(*)(VSrc*))vsrc_update);
|
list_foreach((void (*)(VSrc*))vsrc_update);
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1792,7 +1786,7 @@ static int vm_update()
|
|||||||
// additionally, if any parameter is non-NULL, we set the listener
|
// additionally, if any parameter is non-NULL, we set the listener
|
||||||
// position, look direction, and up vector (in world coordinates).
|
// position, look direction, and up vector (in world coordinates).
|
||||||
// (allow any and all of them to be 0 in case world isn't initialized yet).
|
// (allow any and all of them to be 0 in case world isn't initialized yet).
|
||||||
int snd_update(const float* pos, const float* dir, const float* up)
|
LibError snd_update(const float* pos, const float* dir, const float* up)
|
||||||
{
|
{
|
||||||
// there's no sense in updating anything if we weren't initialized
|
// there's no sense in updating anything if we weren't initialized
|
||||||
// yet (most notably, if sound is disabled). we check for this to
|
// yet (most notably, if sound is disabled). we check for this to
|
||||||
@ -1800,14 +1794,14 @@ int snd_update(const float* pos, const float* dir, const float* up)
|
|||||||
// this fails, so report success here (everything will work once
|
// this fails, so report success here (everything will work once
|
||||||
// sound is re-enabled).
|
// sound is re-enabled).
|
||||||
if(!al_initialized)
|
if(!al_initialized)
|
||||||
return 0;
|
return ERR_OK;
|
||||||
|
|
||||||
if(pos || dir || up)
|
if(pos || dir || up)
|
||||||
al_listener_set_pos(pos, dir, up);
|
al_listener_set_pos(pos, dir, up);
|
||||||
|
|
||||||
vm_update();
|
vm_update();
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1817,7 +1811,7 @@ static bool snd_disabled = false;
|
|||||||
// extra layer on top of al_init that allows 'disabling' sound.
|
// extra layer on top of al_init that allows 'disabling' sound.
|
||||||
// called from each snd_open. returns ERR_AGAIN if sound disabled,
|
// called from each snd_open. returns ERR_AGAIN if sound disabled,
|
||||||
// otherwise the status returned by al_init.
|
// otherwise the status returned by al_init.
|
||||||
static inline int snd_init()
|
static inline LibError snd_init()
|
||||||
{
|
{
|
||||||
// (note: each VSrc_reload and therefore snd_open will fail)
|
// (note: each VSrc_reload and therefore snd_open will fail)
|
||||||
if(snd_disabled)
|
if(snd_disabled)
|
||||||
@ -1838,7 +1832,7 @@ static inline int snd_init()
|
|||||||
//
|
//
|
||||||
// can later be called to reactivate sound; all settings ever changed
|
// can later be called to reactivate sound; all settings ever changed
|
||||||
// will be applied and subsequent sound load / play requests will work.
|
// will be applied and subsequent sound load / play requests will work.
|
||||||
int snd_disable(bool disabled)
|
LibError snd_disable(bool disabled)
|
||||||
{
|
{
|
||||||
snd_disabled = disabled;
|
snd_disabled = disabled;
|
||||||
|
|
||||||
@ -1846,7 +1840,7 @@ int snd_disable(bool disabled)
|
|||||||
{
|
{
|
||||||
if(al_initialized)
|
if(al_initialized)
|
||||||
debug_warn("already initialized => disable is pointless");
|
debug_warn("already initialized => disable is pointless");
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return snd_init();
|
return snd_init();
|
||||||
|
@ -92,7 +92,7 @@ terminology
|
|||||||
// message should be presented to the user, and snd_dev_set need not be
|
// message should be presented to the user, and snd_dev_set need not be
|
||||||
// called; OpenAL will use its default device.
|
// called; OpenAL will use its default device.
|
||||||
// may be called each time the device list is needed.
|
// may be called each time the device list is needed.
|
||||||
extern int snd_dev_prepare_enum();
|
extern LibError snd_dev_prepare_enum();
|
||||||
|
|
||||||
// return the next device name, or 0 if all have been returned.
|
// return the next device name, or 0 if all have been returned.
|
||||||
// do not call unless snd_dev_prepare_enum succeeded!
|
// do not call unless snd_dev_prepare_enum succeeded!
|
||||||
@ -120,17 +120,17 @@ extern const char* snd_dev_next();
|
|||||||
// so preferably call this routine before sounds are loaded.
|
// so preferably call this routine before sounds are loaded.
|
||||||
//
|
//
|
||||||
// return 0 on success, or the status returned by OpenAL re-init.
|
// return 0 on success, or the status returned by OpenAL re-init.
|
||||||
extern int snd_dev_set(const char* alc_new_dev_name);
|
extern LibError snd_dev_set(const char* alc_new_dev_name);
|
||||||
|
|
||||||
// set maximum number of voices to play simultaneously,
|
// set maximum number of voices to play simultaneously,
|
||||||
// to reduce mixing cost on low-end systems.
|
// to reduce mixing cost on low-end systems.
|
||||||
// return 0 on success, or 1 if limit was ignored
|
// return 0 on success, or 1 if limit was ignored
|
||||||
// (e.g. if higher than an implementation-defined limit anyway).
|
// (e.g. if higher than an implementation-defined limit anyway).
|
||||||
extern int snd_set_max_voices(uint cap);
|
extern LibError snd_set_max_voices(uint cap);
|
||||||
|
|
||||||
// set amplitude modifier, which is effectively applied to all sounds.
|
// set amplitude modifier, which is effectively applied to all sounds.
|
||||||
// must be non-negative; 1 -> unattenuated, 0.5 -> -6 dB, 0 -> silence.
|
// must be non-negative; 1 -> unattenuated, 0.5 -> -6 dB, 0 -> silence.
|
||||||
extern int snd_set_master_gain(float gain);
|
extern LibError snd_set_master_gain(float gain);
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -153,7 +153,7 @@ extern Handle snd_open(const char* snd_fn, bool stream = false);
|
|||||||
// close the sound <hs> and set hs to 0. if it was playing,
|
// close the sound <hs> and set hs to 0. if it was playing,
|
||||||
// it will be stopped. sounds are closed automatically when done
|
// it will be stopped. sounds are closed automatically when done
|
||||||
// playing; this is provided for completeness only.
|
// playing; this is provided for completeness only.
|
||||||
extern int snd_free(Handle& hs);
|
extern LibError snd_free(Handle& hs);
|
||||||
|
|
||||||
// request the sound <hs> be played. once done playing, the sound is
|
// request the sound <hs> be played. once done playing, the sound is
|
||||||
// automatically closed (allows fire-and-forget play code).
|
// automatically closed (allows fire-and-forget play code).
|
||||||
@ -162,27 +162,27 @@ extern int snd_free(Handle& hs);
|
|||||||
// priority (min 0 .. max 1, default 0) indicates which sounds are
|
// priority (min 0 .. max 1, default 0) indicates which sounds are
|
||||||
// considered more important; this is attenuated by distance to the
|
// considered more important; this is attenuated by distance to the
|
||||||
// listener (see snd_update).
|
// listener (see snd_update).
|
||||||
extern int snd_play(Handle hs, float priority = 0.0f);
|
extern LibError snd_play(Handle hs, float priority = 0.0f);
|
||||||
|
|
||||||
// change 3d position of the sound source.
|
// change 3d position of the sound source.
|
||||||
// if relative (default false), (x,y,z) is treated as relative to the
|
// if relative (default false), (x,y,z) is treated as relative to the
|
||||||
// listener; otherwise, it is the position in world coordinates.
|
// listener; otherwise, it is the position in world coordinates.
|
||||||
// may be called at any time; fails with invalid handle return if
|
// may be called at any time; fails with invalid handle return if
|
||||||
// the sound has already been closed (e.g. it never played).
|
// the sound has already been closed (e.g. it never played).
|
||||||
extern int snd_set_pos(Handle hs, float x, float y, float z, bool relative = false);
|
extern LibError snd_set_pos(Handle hs, float x, float y, float z, bool relative = false);
|
||||||
|
|
||||||
// change gain (amplitude modifier) of the sound source.
|
// change gain (amplitude modifier) of the sound source.
|
||||||
// must be non-negative; 1 -> unattenuated, 0.5 -> -6 dB, 0 -> silence.
|
// must be non-negative; 1 -> unattenuated, 0.5 -> -6 dB, 0 -> silence.
|
||||||
// may be called at any time; fails with invalid handle return if
|
// may be called at any time; fails with invalid handle return if
|
||||||
// the sound has already been closed (e.g. it never played).
|
// the sound has already been closed (e.g. it never played).
|
||||||
extern int snd_set_gain(Handle hs, float gain);
|
extern LibError snd_set_gain(Handle hs, float gain);
|
||||||
|
|
||||||
// change pitch shift of the sound source.
|
// change pitch shift of the sound source.
|
||||||
// 1.0 means no change; each reduction by 50% equals a pitch shift of
|
// 1.0 means no change; each reduction by 50% equals a pitch shift of
|
||||||
// -12 semitones (one octave). zero is invalid.
|
// -12 semitones (one octave). zero is invalid.
|
||||||
// may be called at any time; fails with invalid handle return if
|
// may be called at any time; fails with invalid handle return if
|
||||||
// the sound has already been closed (e.g. it never played).
|
// the sound has already been closed (e.g. it never played).
|
||||||
extern int snd_set_pitch(Handle hs, float pitch);
|
extern LibError snd_set_pitch(Handle hs, float pitch);
|
||||||
|
|
||||||
// enable/disable looping on the sound source.
|
// enable/disable looping on the sound source.
|
||||||
// used to implement variable-length sounds (e.g. while building).
|
// used to implement variable-length sounds (e.g. while building).
|
||||||
@ -194,7 +194,7 @@ extern int snd_set_pitch(Handle hs, float pitch);
|
|||||||
// a hardware voice at the moment play was requested.
|
// a hardware voice at the moment play was requested.
|
||||||
// - once looping is again disabled and the sound has reached its end,
|
// - once looping is again disabled and the sound has reached its end,
|
||||||
// the sound instance is freed automatically (as if never looped).
|
// the sound instance is freed automatically (as if never looped).
|
||||||
extern int snd_set_loop(Handle hs, bool loop);
|
extern LibError snd_set_loop(Handle hs, bool loop);
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -212,13 +212,13 @@ extern int snd_set_loop(Handle hs, bool loop);
|
|||||||
//
|
//
|
||||||
// can later be called to reactivate sound; all settings ever changed
|
// can later be called to reactivate sound; all settings ever changed
|
||||||
// will be applied and subsequent sound load / play requests will work.
|
// will be applied and subsequent sound load / play requests will work.
|
||||||
extern int snd_disable(bool disabled);
|
extern LibError snd_disable(bool disabled);
|
||||||
|
|
||||||
// perform housekeeping (e.g. streaming); call once a frame.
|
// perform housekeeping (e.g. streaming); call once a frame.
|
||||||
//
|
//
|
||||||
// additionally, if any parameter is non-NULL, we set the listener
|
// additionally, if any parameter is non-NULL, we set the listener
|
||||||
// position, look direction, and up vector (in world coordinates).
|
// position, look direction, and up vector (in world coordinates).
|
||||||
extern int snd_update(const float* pos, const float* dir, const float* up);
|
extern LibError snd_update(const float* pos, const float* dir, const float* up);
|
||||||
|
|
||||||
// free all resources and shut down the sound system.
|
// free all resources and shut down the sound system.
|
||||||
// call before h_mgr_shutdown.
|
// call before h_mgr_shutdown.
|
||||||
|
@ -27,7 +27,7 @@ bool self_test_active = false;
|
|||||||
|
|
||||||
// trampoline that sets self_test_active and returns a dummy value;
|
// trampoline that sets self_test_active and returns a dummy value;
|
||||||
// used by SELF_TEST_RUN.
|
// used by SELF_TEST_RUN.
|
||||||
int self_test_run(void(*func)())
|
int self_test_run(void (*func)())
|
||||||
{
|
{
|
||||||
self_test_active = true;
|
self_test_active = true;
|
||||||
func();
|
func();
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
|
|
||||||
// path: portable and relative, must add current directory and convert to native
|
// path: portable and relative, must add current directory and convert to native
|
||||||
// better to use a cached string from rel_chdir - secure
|
// better to use a cached string from rel_chdir - secure
|
||||||
extern int dir_add_watch(const char* path, intptr_t* watch);
|
extern LibError dir_add_watch(const char* path, intptr_t* watch);
|
||||||
|
|
||||||
extern int dir_cancel_watch(intptr_t watch);
|
extern LibError dir_cancel_watch(intptr_t watch);
|
||||||
|
|
||||||
extern int dir_get_changed_file(char* fn);
|
extern LibError dir_get_changed_file(char* fn);
|
||||||
|
@ -7,11 +7,11 @@ extern "C" {
|
|||||||
|
|
||||||
// useful for choosing a video mode.
|
// useful for choosing a video mode.
|
||||||
// if we fail, outputs are unchanged (assumed initialized to defaults)
|
// if we fail, outputs are unchanged (assumed initialized to defaults)
|
||||||
extern int get_cur_vmode(int* xres, int* yres, int* bpp, int* freq);
|
extern LibError get_cur_vmode(int* xres, int* yres, int* bpp, int* freq);
|
||||||
|
|
||||||
// useful for determining aspect ratio.
|
// useful for determining aspect ratio.
|
||||||
// if we fail, outputs are unchanged (assumed initialized to defaults)
|
// if we fail, outputs are unchanged (assumed initialized to defaults)
|
||||||
extern int get_monitor_size(int& width_mm, int& height_mm);
|
extern LibError get_monitor_size(int& width_mm, int& height_mm);
|
||||||
|
|
||||||
|
|
||||||
const size_t GFX_CARD_LEN = 128;
|
const size_t GFX_CARD_LEN = 128;
|
||||||
|
@ -587,8 +587,14 @@ void ia32_get_cpu_info()
|
|||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
// checks if there is an IA-32 CALL instruction right before ret_addr.
|
||||||
int ia32_get_call_target(void* ret_addr, void** target)
|
// returns ERR_OK if so and ERR_FAIL if not.
|
||||||
|
// also attempts to determine the call target. if that is possible
|
||||||
|
// (directly addressed relative or indirect jumps), it is stored in
|
||||||
|
// target, which is otherwise 0.
|
||||||
|
//
|
||||||
|
// this is useful for walking the stack manually.
|
||||||
|
LibError ia32_get_call_target(void* ret_addr, void** target)
|
||||||
{
|
{
|
||||||
*target = 0;
|
*target = 0;
|
||||||
|
|
||||||
@ -603,13 +609,13 @@ int ia32_get_call_target(void* ret_addr, void** target)
|
|||||||
if(len >= 5 && c[-5] == 0xE8)
|
if(len >= 5 && c[-5] == 0xE8)
|
||||||
{
|
{
|
||||||
*target = (u8*)ret_addr + *(i32*)(c-4);
|
*target = (u8*)ret_addr + *(i32*)(c-4);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// CALL r/m32 (FF /2)
|
// CALL r/m32 (FF /2)
|
||||||
// .. CALL [r32 + r32*s] => FF 14 SIB
|
// .. CALL [r32 + r32*s] => FF 14 SIB
|
||||||
if(len >= 3 && c[-3] == 0xFF && c[-2] == 0x14)
|
if(len >= 3 && c[-3] == 0xFF && c[-2] == 0x14)
|
||||||
return 1;
|
return ERR_OK;
|
||||||
// .. CALL [disp32] => FF 15 disp32
|
// .. CALL [disp32] => FF 15 disp32
|
||||||
if(len >= 6 && c[6] == 0xFF && c[-5] == 0x15)
|
if(len >= 6 && c[6] == 0xFF && c[-5] == 0x15)
|
||||||
{
|
{
|
||||||
@ -617,29 +623,29 @@ int ia32_get_call_target(void* ret_addr, void** target)
|
|||||||
if(!debug_is_pointer_bogus(addr_of_target))
|
if(!debug_is_pointer_bogus(addr_of_target))
|
||||||
{
|
{
|
||||||
*target = *(void**)addr_of_target;
|
*target = *(void**)addr_of_target;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// .. CALL [r32] => FF 00-3F(!14/15)
|
// .. CALL [r32] => FF 00-3F(!14/15)
|
||||||
if(len >= 2 && c[-2] == 0xFF && c[-1] < 0x40 && c[-1] != 0x14 && c[-1] != 0x15)
|
if(len >= 2 && c[-2] == 0xFF && c[-1] < 0x40 && c[-1] != 0x14 && c[-1] != 0x15)
|
||||||
return 1;
|
return ERR_OK;
|
||||||
// .. CALL [r32 + r32*s + disp8] => FF 54 SIB disp8
|
// .. CALL [r32 + r32*s + disp8] => FF 54 SIB disp8
|
||||||
if(len >= 4 && c[-4] == 0xFF && c[-3] == 0x54)
|
if(len >= 4 && c[-4] == 0xFF && c[-3] == 0x54)
|
||||||
return 1;
|
return ERR_OK;
|
||||||
// .. CALL [r32 + disp8] => FF 50-57(!54) disp8
|
// .. CALL [r32 + disp8] => FF 50-57(!54) disp8
|
||||||
if(len >= 3 && c[-3] == 0xFF && (c[-2] & 0xF8) == 0x50 && c[-2] != 0x54)
|
if(len >= 3 && c[-3] == 0xFF && (c[-2] & 0xF8) == 0x50 && c[-2] != 0x54)
|
||||||
return 1;
|
return ERR_OK;
|
||||||
// .. CALL [r32 + r32*s + disp32] => FF 94 SIB disp32
|
// .. CALL [r32 + r32*s + disp32] => FF 94 SIB disp32
|
||||||
if(len >= 7 && c[-7] == 0xFF && c[-6] == 0x94)
|
if(len >= 7 && c[-7] == 0xFF && c[-6] == 0x94)
|
||||||
return 1;
|
return ERR_OK;
|
||||||
// .. CALL [r32 + disp32] => FF 90-97(!94) disp32
|
// .. CALL [r32 + disp32] => FF 90-97(!94) disp32
|
||||||
if(len >= 6 && c[-6] == 0xFF && (c[-5] & 0xF8) == 0x90 && c[-5] != 0x94)
|
if(len >= 6 && c[-6] == 0xFF && (c[-5] & 0xF8) == 0x90 && c[-5] != 0x94)
|
||||||
return 1;
|
return ERR_OK;
|
||||||
// .. CALL r32 => FF D0-D7
|
// .. CALL r32 => FF D0-D7
|
||||||
if(len >= 2 && c[-2] == 0xFF && (c[-1] & 0xF8) == 0xD0)
|
if(len >= 2 && c[-2] == 0xFF && (c[-1] & 0xF8) == 0xD0)
|
||||||
return 1;
|
return ERR_OK;
|
||||||
|
|
||||||
return -3;
|
return ERR_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -686,10 +692,10 @@ namespace test {
|
|||||||
TEST(i32_from_double(1.01) == 1);
|
TEST(i32_from_double(1.01) == 1);
|
||||||
TEST(i32_from_double(5.6) == 5);
|
TEST(i32_from_double(5.6) == 5);
|
||||||
|
|
||||||
TEST(i64_from_double(0.99999) == 0i64);
|
TEST(i64_from_double(0.99999) == 0ll);
|
||||||
TEST(i64_from_double(1.0) == 1i64);
|
TEST(i64_from_double(1.0) == 1ll);
|
||||||
TEST(i64_from_double(1.01) == 1i64);
|
TEST(i64_from_double(1.01) == 1ll);
|
||||||
TEST(i64_from_double(5.6) == 5i64);
|
TEST(i64_from_double(5.6) == 5ll);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void self_test()
|
static void self_test()
|
||||||
|
@ -117,7 +117,14 @@ extern void ia32_get_current_context(void* pcontext);
|
|||||||
|
|
||||||
extern void ia32_asm_init();
|
extern void ia32_asm_init();
|
||||||
|
|
||||||
extern int ia32_get_call_target(void* ret_addr, void** target);
|
// checks if there is an IA-32 CALL instruction right before ret_addr.
|
||||||
|
// returns ERR_OK if so and ERR_FAIL if not.
|
||||||
|
// also attempts to determine the call target. if that is possible
|
||||||
|
// (directly addressed relative or indirect jumps), it is stored in
|
||||||
|
// target, which is otherwise 0.
|
||||||
|
//
|
||||||
|
// this is useful for walking the stack manually.
|
||||||
|
extern LibError ia32_get_call_target(void* ret_addr, void** target);
|
||||||
|
|
||||||
// order in which registers are stored in regs array
|
// order in which registers are stored in regs array
|
||||||
// (do not change! brand string relies on this ordering)
|
// (do not change! brand string relies on this ordering)
|
||||||
|
@ -171,7 +171,7 @@ extern ErrorReaction sys_display_error(const wchar_t* text, int flags);
|
|||||||
//
|
//
|
||||||
|
|
||||||
// "copy" text into the clipboard. replaces previous contents.
|
// "copy" text into the clipboard. replaces previous contents.
|
||||||
extern int sys_clipboard_set(const wchar_t* text);
|
extern LibError sys_clipboard_set(const wchar_t* text);
|
||||||
|
|
||||||
// allow "pasting" from clipboard. returns the current contents if they
|
// allow "pasting" from clipboard. returns the current contents if they
|
||||||
// can be represented as text, otherwise 0.
|
// can be represented as text, otherwise 0.
|
||||||
@ -181,7 +181,7 @@ extern wchar_t* sys_clipboard_get(void);
|
|||||||
|
|
||||||
// frees memory used by <copy>, which must have been returned by
|
// frees memory used by <copy>, which must have been returned by
|
||||||
// sys_clipboard_get. see note above.
|
// sys_clipboard_get. see note above.
|
||||||
extern int sys_clipboard_free(wchar_t* copy);
|
extern LibError sys_clipboard_free(wchar_t* copy);
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -200,16 +200,16 @@ extern int sys_clipboard_free(wchar_t* copy);
|
|||||||
// return: negative error code, or 0 on success. cursor is filled with
|
// return: negative error code, or 0 on success. cursor is filled with
|
||||||
// a pointer and undefined on failure. it must be sys_cursor_free-ed
|
// a pointer and undefined on failure. it must be sys_cursor_free-ed
|
||||||
// when no longer needed.
|
// when no longer needed.
|
||||||
extern int sys_cursor_create(uint w, uint h, void* bgra_img,
|
extern LibError sys_cursor_create(uint w, uint h, void* bgra_img,
|
||||||
uint hx, uint hy, void** cursor);
|
uint hx, uint hy, void** cursor);
|
||||||
|
|
||||||
// replaces the current system cursor with the one indicated. need only be
|
// replaces the current system cursor with the one indicated. need only be
|
||||||
// called once per cursor; pass 0 to restore the default.
|
// called once per cursor; pass 0 to restore the default.
|
||||||
extern int sys_cursor_set(void* cursor);
|
extern LibError sys_cursor_set(void* cursor);
|
||||||
|
|
||||||
// destroys the indicated cursor and frees its resources. if it is
|
// destroys the indicated cursor and frees its resources. if it is
|
||||||
// currently the system cursor, the default cursor is restored first.
|
// currently the system cursor, the default cursor is restored first.
|
||||||
extern int sys_cursor_free(void* cursor);
|
extern LibError sys_cursor_free(void* cursor);
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -219,7 +219,7 @@ extern int sys_cursor_free(void* cursor);
|
|||||||
// OS-specific backend for error_description_r.
|
// OS-specific backend for error_description_r.
|
||||||
// NB: it is expected to be rare that OS return/error codes are actually
|
// NB: it is expected to be rare that OS return/error codes are actually
|
||||||
// seen by user code, but we still translate them for completeness.
|
// seen by user code, but we still translate them for completeness.
|
||||||
extern int sys_error_description_r(int err, char* buf, size_t max_chars);
|
extern LibError sys_error_description_r(int err, char* buf, size_t max_chars);
|
||||||
|
|
||||||
// determine filename of the module to whom the given address belongs.
|
// determine filename of the module to whom the given address belongs.
|
||||||
// useful for handling exceptions in other modules.
|
// useful for handling exceptions in other modules.
|
||||||
@ -231,13 +231,13 @@ wchar_t* sys_get_module_filename(void* addr, wchar_t* path);
|
|||||||
// store full path to the current executable.
|
// store full path to the current executable.
|
||||||
// returns 0 or a negative error code.
|
// returns 0 or a negative error code.
|
||||||
// useful for determining installation directory, e.g. for VFS.
|
// useful for determining installation directory, e.g. for VFS.
|
||||||
extern int sys_get_executable_name(char* n_path, size_t buf_size);
|
extern LibError sys_get_executable_name(char* n_path, size_t buf_size);
|
||||||
|
|
||||||
// have the user specify a directory via OS dialog.
|
// have the user specify a directory via OS dialog.
|
||||||
// stores its full path in the given buffer, which must hold at least
|
// stores its full path in the given buffer, which must hold at least
|
||||||
// PATH_MAX chars.
|
// PATH_MAX chars.
|
||||||
// returns 0 on success or a negative error code.
|
// returns 0 on success or a negative error code.
|
||||||
extern int sys_pick_directory(char* n_path, size_t buf_size);
|
extern LibError sys_pick_directory(char* n_path, size_t buf_size);
|
||||||
|
|
||||||
// execute the specified function once on each CPU.
|
// execute the specified function once on each CPU.
|
||||||
// this includes logical HT units and proceeds serially (function
|
// this includes logical HT units and proceeds serially (function
|
||||||
@ -247,7 +247,7 @@ extern int sys_pick_directory(char* n_path, size_t buf_size);
|
|||||||
// return 0 on success or a negative error code on failure
|
// return 0 on success or a negative error code on failure
|
||||||
// (e.g. if OS is preventing us from running on some CPUs).
|
// (e.g. if OS is preventing us from running on some CPUs).
|
||||||
// called from ia32.cpp get_cpu_count
|
// called from ia32.cpp get_cpu_count
|
||||||
extern int sys_on_each_cpu(void(*cb)());
|
extern LibError sys_on_each_cpu(void (*cb)());
|
||||||
|
|
||||||
|
|
||||||
// drop-in replacement for libc memcpy(). only requires CPU support for
|
// drop-in replacement for libc memcpy(). only requires CPU support for
|
||||||
|
@ -138,7 +138,7 @@ static HANDLE aio_h_get(const int fd)
|
|||||||
// associate h (an async-capable file handle) with fd;
|
// associate h (an async-capable file handle) with fd;
|
||||||
// returned by subsequent aio_h_get(fd) calls.
|
// returned by subsequent aio_h_get(fd) calls.
|
||||||
// setting h = INVALID_HANDLE_VALUE removes the association.
|
// setting h = INVALID_HANDLE_VALUE removes the association.
|
||||||
static int aio_h_set(const int fd, const HANDLE h)
|
static LibError aio_h_set(const int fd, const HANDLE h)
|
||||||
{
|
{
|
||||||
lock();
|
lock();
|
||||||
|
|
||||||
@ -177,12 +177,12 @@ static int aio_h_set(const int fd, const HANDLE h)
|
|||||||
aio_hs[fd] = h;
|
aio_hs[fd] = h;
|
||||||
|
|
||||||
unlock();
|
unlock();
|
||||||
return 0;
|
return ERR_OK;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
unlock();
|
unlock();
|
||||||
debug_warn("failed");
|
debug_warn("failed");
|
||||||
return -1;
|
return ERR_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -353,11 +353,11 @@ static Req* req_alloc(aiocb* cb)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int req_free(Req* r)
|
static LibError req_free(Req* r)
|
||||||
{
|
{
|
||||||
debug_assert(r->cb != 0 && "req_free: not currently in use");
|
debug_assert(r->cb != 0 && "req_free: not currently in use");
|
||||||
r->cb = 0;
|
r->cb = 0;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -707,7 +707,7 @@ int aio_fsync(int, struct aiocb*)
|
|||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
static int waio_init()
|
static LibError waio_init()
|
||||||
{
|
{
|
||||||
req_init();
|
req_init();
|
||||||
|
|
||||||
@ -747,13 +747,13 @@ static int waio_init()
|
|||||||
SetErrorMode(old_err_mode);
|
SetErrorMode(old_err_mode);
|
||||||
|
|
||||||
debug_assert(is_pow2((long)sector_size));
|
debug_assert(is_pow2((long)sector_size));
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int waio_shutdown()
|
static LibError waio_shutdown()
|
||||||
{
|
{
|
||||||
req_cleanup();
|
req_cleanup();
|
||||||
aio_h_cleanup();
|
aio_h_cleanup();
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ struct sigevent
|
|||||||
int sigev_notify; // notification mode
|
int sigev_notify; // notification mode
|
||||||
int sigev_signo; // signal number
|
int sigev_signo; // signal number
|
||||||
union sigval sigev_value; // signal value
|
union sigval sigev_value; // signal value
|
||||||
void(*sigev_notify_function)(union sigval);
|
void (*sigev_notify_function)(union sigval);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -92,7 +92,7 @@ static void check_speedstep()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int win_get_cpu_info()
|
LibError win_get_cpu_info()
|
||||||
{
|
{
|
||||||
// get number of CPUs (can't fail)
|
// get number of CPUs (can't fail)
|
||||||
SYSTEM_INFO si;
|
SYSTEM_INFO si;
|
||||||
@ -114,7 +114,7 @@ int win_get_cpu_info()
|
|||||||
|
|
||||||
check_speedstep();
|
check_speedstep();
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -251,7 +251,7 @@ static void* prof_thread_func(void* UNUSED(data))
|
|||||||
|
|
||||||
|
|
||||||
// call from thread that is to be profiled
|
// call from thread that is to be profiled
|
||||||
int prof_start()
|
LibError prof_start()
|
||||||
{
|
{
|
||||||
// we need a real HANDLE to the target thread for use with
|
// we need a real HANDLE to the target thread for use with
|
||||||
// Suspend|ResumeThread and GetThreadContext.
|
// Suspend|ResumeThread and GetThreadContext.
|
||||||
@ -262,20 +262,20 @@ int prof_start()
|
|||||||
if(hThread == INVALID_HANDLE_VALUE)
|
if(hThread == INVALID_HANDLE_VALUE)
|
||||||
{
|
{
|
||||||
debug_warn("OpenThread failed");
|
debug_warn("OpenThread failed");
|
||||||
return -1;
|
return ERR_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
prof_target_thread = hThread;
|
prof_target_thread = hThread;
|
||||||
|
|
||||||
sem_init(&exit_flag, 0, 0);
|
sem_init(&exit_flag, 0, 0);
|
||||||
pthread_create(&thread, 0, prof_thread_func, 0);
|
pthread_create(&thread, 0, prof_thread_func, 0);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int prof_shutdown()
|
LibError prof_shutdown()
|
||||||
{
|
{
|
||||||
CloseHandle(prof_target_thread);
|
WARN_IF_FALSE(CloseHandle(prof_target_thread));
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -159,7 +159,7 @@ void debug_heap_enable(DebugHeapChecks what)
|
|||||||
|
|
||||||
// to avoid deadlock, be VERY CAREFUL to avoid anything that may block,
|
// to avoid deadlock, be VERY CAREFUL to avoid anything that may block,
|
||||||
// including locks taken by the OS (e.g. malloc, GetProcAddress).
|
// including locks taken by the OS (e.g. malloc, GetProcAddress).
|
||||||
typedef int(*WhileSuspendedFunc)(HANDLE hThread, void* user_arg);
|
typedef LibError (*WhileSuspendedFunc)(HANDLE hThread, void* user_arg);
|
||||||
|
|
||||||
struct WhileSuspendedParam
|
struct WhileSuspendedParam
|
||||||
{
|
{
|
||||||
@ -173,10 +173,9 @@ static void* while_suspended_thread_func(void* user_arg)
|
|||||||
{
|
{
|
||||||
debug_set_thread_name("suspender");
|
debug_set_thread_name("suspender");
|
||||||
|
|
||||||
DWORD err;
|
|
||||||
WhileSuspendedParam* param = (WhileSuspendedParam*)user_arg;
|
WhileSuspendedParam* param = (WhileSuspendedParam*)user_arg;
|
||||||
|
|
||||||
err = SuspendThread(param->hThread);
|
DWORD err = SuspendThread(param->hThread);
|
||||||
// abort, since GetThreadContext only works if the target is suspended.
|
// abort, since GetThreadContext only works if the target is suspended.
|
||||||
if(err == (DWORD)-1)
|
if(err == (DWORD)-1)
|
||||||
{
|
{
|
||||||
@ -186,16 +185,15 @@ static void* while_suspended_thread_func(void* user_arg)
|
|||||||
// target is now guaranteed to be suspended,
|
// target is now guaranteed to be suspended,
|
||||||
// since the Windows counter never goes negative.
|
// since the Windows counter never goes negative.
|
||||||
|
|
||||||
int ret = param->func(param->hThread, param->user_arg);
|
LibError ret = param->func(param->hThread, param->user_arg);
|
||||||
|
|
||||||
err = ResumeThread(param->hThread);
|
WARN_IF_FALSE(ResumeThread(param->hThread));
|
||||||
debug_assert(err != 0);
|
|
||||||
|
|
||||||
return (void*)(intptr_t)ret;
|
return (void*)(intptr_t)ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int call_while_suspended(WhileSuspendedFunc func, void* user_arg)
|
static LibError call_while_suspended(WhileSuspendedFunc func, void* user_arg)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
@ -208,7 +206,7 @@ static int call_while_suspended(WhileSuspendedFunc func, void* user_arg)
|
|||||||
if(hThread == INVALID_HANDLE_VALUE)
|
if(hThread == INVALID_HANDLE_VALUE)
|
||||||
{
|
{
|
||||||
debug_warn("OpenThread failed");
|
debug_warn("OpenThread failed");
|
||||||
return -1;
|
return ERR_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
WhileSuspendedParam param = { hThread, func, user_arg };
|
WhileSuspendedParam param = { hThread, func, user_arg };
|
||||||
@ -220,7 +218,7 @@ static int call_while_suspended(WhileSuspendedFunc func, void* user_arg)
|
|||||||
err = pthread_join(thread, &ret);
|
err = pthread_join(thread, &ret);
|
||||||
debug_assert(err == 0 && ret == 0);
|
debug_assert(err == 0 && ret == 0);
|
||||||
|
|
||||||
return (int)(intptr_t)ret;
|
return (LibError)(intptr_t)ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -260,17 +258,17 @@ static const uint MAX_BREAKPOINTS = 4;
|
|||||||
|
|
||||||
// remove all breakpoints enabled by debug_set_break from <context>.
|
// remove all breakpoints enabled by debug_set_break from <context>.
|
||||||
// called while target is suspended.
|
// called while target is suspended.
|
||||||
static int brk_disable_all_in_ctx(BreakInfo* UNUSED(bi), CONTEXT* context)
|
static LibError brk_disable_all_in_ctx(BreakInfo* UNUSED(bi), CONTEXT* context)
|
||||||
{
|
{
|
||||||
context->Dr7 &= ~brk_all_local_enables;
|
context->Dr7 &= ~brk_all_local_enables;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// find a free register, set type according to <bi> and
|
// find a free register, set type according to <bi> and
|
||||||
// mark it as enabled in <context>.
|
// mark it as enabled in <context>.
|
||||||
// called while target is suspended.
|
// called while target is suspended.
|
||||||
static int brk_enable_in_ctx(BreakInfo* bi, CONTEXT* context)
|
static LibError brk_enable_in_ctx(BreakInfo* bi, CONTEXT* context)
|
||||||
{
|
{
|
||||||
uint reg; // index (0..3) of first free reg
|
uint reg; // index (0..3) of first free reg
|
||||||
uint LE; // local enable bit for <reg>
|
uint LE; // local enable bit for <reg>
|
||||||
@ -340,15 +338,15 @@ have_reg:
|
|||||||
context->Dr7 |= LE;
|
context->Dr7 |= LE;
|
||||||
|
|
||||||
brk_all_local_enables |= LE;
|
brk_all_local_enables |= LE;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// carry out the request stored in the BreakInfo* parameter.
|
// carry out the request stored in the BreakInfo* parameter.
|
||||||
// called while target is suspended.
|
// called while target is suspended.
|
||||||
static int brk_do_request(HANDLE hThread, void* arg)
|
static LibError brk_do_request(HANDLE hThread, void* arg)
|
||||||
{
|
{
|
||||||
int ret;
|
LibError ret;
|
||||||
BreakInfo* bi = (BreakInfo*)arg;
|
BreakInfo* bi = (BreakInfo*)arg;
|
||||||
|
|
||||||
CONTEXT context;
|
CONTEXT context;
|
||||||
@ -374,9 +372,9 @@ static int brk_do_request(HANDLE hThread, void* arg)
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return ret;
|
||||||
fail:
|
fail:
|
||||||
return -1;
|
return ERR_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -386,13 +384,13 @@ fail:
|
|||||||
// derived from addr's alignment, and is typically 1 machine word.
|
// derived from addr's alignment, and is typically 1 machine word.
|
||||||
// breakpoints are a limited resource (4 on IA-32); abort and
|
// breakpoints are a limited resource (4 on IA-32); abort and
|
||||||
// return ERR_LIMIT if none are available.
|
// return ERR_LIMIT if none are available.
|
||||||
int debug_set_break(void* p, DbgBreakType type)
|
LibError debug_set_break(void* p, DbgBreakType type)
|
||||||
{
|
{
|
||||||
lock();
|
lock();
|
||||||
|
|
||||||
brk_info.addr = (uintptr_t)p;
|
brk_info.addr = (uintptr_t)p;
|
||||||
brk_info.type = type;
|
brk_info.type = type;
|
||||||
int ret = call_while_suspended(brk_do_request, &brk_info);
|
LibError ret = call_while_suspended(brk_do_request, &brk_info);
|
||||||
|
|
||||||
unlock();
|
unlock();
|
||||||
return ret;
|
return ret;
|
||||||
@ -401,12 +399,12 @@ int debug_set_break(void* p, DbgBreakType type)
|
|||||||
|
|
||||||
// remove all breakpoints that were set by debug_set_break.
|
// remove all breakpoints that were set by debug_set_break.
|
||||||
// important, since these are a limited resource.
|
// important, since these are a limited resource.
|
||||||
int debug_remove_all_breaks()
|
LibError debug_remove_all_breaks()
|
||||||
{
|
{
|
||||||
lock();
|
lock();
|
||||||
|
|
||||||
brk_info.want_all_disabled = true;
|
brk_info.want_all_disabled = true;
|
||||||
int ret = call_while_suspended(brk_do_request, &brk_info);
|
LibError ret = call_while_suspended(brk_do_request, &brk_info);
|
||||||
brk_info.want_all_disabled = false;
|
brk_info.want_all_disabled = false;
|
||||||
|
|
||||||
unlock();
|
unlock();
|
||||||
@ -734,7 +732,7 @@ static LONG WINAPI vectored_exception_handler(EXCEPTION_POINTERS* ep)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int wdbg_init(void)
|
static LibError wdbg_init(void)
|
||||||
{
|
{
|
||||||
// see decl
|
// see decl
|
||||||
main_thread_id = GetCurrentThreadId();
|
main_thread_id = GetCurrentThreadId();
|
||||||
@ -753,7 +751,7 @@ static int wdbg_init(void)
|
|||||||
pAddVectoredExceptionHandler(TRUE, vectored_exception_handler);
|
pAddVectoredExceptionHandler(TRUE, vectored_exception_handler);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -71,7 +71,7 @@
|
|||||||
// EnumDisplayDevices is not available on Win95 or NT.
|
// EnumDisplayDevices is not available on Win95 or NT.
|
||||||
// try to import it manually here; return -1 if not available.
|
// try to import it manually here; return -1 if not available.
|
||||||
static BOOL (WINAPI *pEnumDisplayDevicesA)(LPCSTR, DWORD, LPDISPLAY_DEVICEA, DWORD);
|
static BOOL (WINAPI *pEnumDisplayDevicesA)(LPCSTR, DWORD, LPDISPLAY_DEVICEA, DWORD);
|
||||||
static int import_EnumDisplayDevices()
|
static LibError import_EnumDisplayDevices()
|
||||||
{
|
{
|
||||||
if(!pEnumDisplayDevicesA)
|
if(!pEnumDisplayDevicesA)
|
||||||
{
|
{
|
||||||
@ -86,13 +86,13 @@ static int import_EnumDisplayDevices()
|
|||||||
// so this resource leak is unavoidable.
|
// so this resource leak is unavoidable.
|
||||||
}
|
}
|
||||||
|
|
||||||
return pEnumDisplayDevicesA? 0 : -1;
|
return pEnumDisplayDevicesA? ERR_OK : ERR_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// useful for choosing a video mode.
|
// useful for choosing a video mode.
|
||||||
// if we fail, outputs are unchanged (assumed initialized to defaults)
|
// if we fail, outputs are unchanged (assumed initialized to defaults)
|
||||||
int get_cur_vmode(int* xres, int* yres, int* bpp, int* freq)
|
LibError get_cur_vmode(int* xres, int* yres, int* bpp, int* freq)
|
||||||
{
|
{
|
||||||
// don't use EnumDisplaySettingsW - BoundsChecker reports it causes
|
// don't use EnumDisplaySettingsW - BoundsChecker reports it causes
|
||||||
// a memory overrun (even if called as the very first thing, before
|
// a memory overrun (even if called as the very first thing, before
|
||||||
@ -104,7 +104,7 @@ int get_cur_vmode(int* xres, int* yres, int* bpp, int* freq)
|
|||||||
// dm.dmDriverExtra already set to 0 by memset
|
// dm.dmDriverExtra already set to 0 by memset
|
||||||
|
|
||||||
if(!EnumDisplaySettingsA(0, ENUM_CURRENT_SETTINGS, &dm))
|
if(!EnumDisplaySettingsA(0, ENUM_CURRENT_SETTINGS, &dm))
|
||||||
return -1;
|
return ERR_FAIL;
|
||||||
|
|
||||||
if(dm.dmFields & (DWORD)DM_PELSWIDTH && xres)
|
if(dm.dmFields & (DWORD)DM_PELSWIDTH && xres)
|
||||||
*xres = (int)dm.dmPelsWidth;
|
*xres = (int)dm.dmPelsWidth;
|
||||||
@ -115,20 +115,20 @@ int get_cur_vmode(int* xres, int* yres, int* bpp, int* freq)
|
|||||||
if(dm.dmFields & (DWORD)DM_DISPLAYFREQUENCY && freq)
|
if(dm.dmFields & (DWORD)DM_DISPLAYFREQUENCY && freq)
|
||||||
*freq = (int)dm.dmDisplayFrequency;
|
*freq = (int)dm.dmDisplayFrequency;
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// useful for determining aspect ratio.
|
// useful for determining aspect ratio.
|
||||||
// if we fail, outputs are unchanged (assumed initialized to defaults)
|
// if we fail, outputs are unchanged (assumed initialized to defaults)
|
||||||
int get_monitor_size(int& width_mm, int& height_mm)
|
LibError get_monitor_size(int& width_mm, int& height_mm)
|
||||||
{
|
{
|
||||||
// (DC for the primary monitor's entire screen)
|
// (DC for the primary monitor's entire screen)
|
||||||
HDC dc = GetDC(0);
|
HDC dc = GetDC(0);
|
||||||
width_mm = GetDeviceCaps(dc, HORZSIZE);
|
width_mm = GetDeviceCaps(dc, HORZSIZE);
|
||||||
height_mm = GetDeviceCaps(dc, VERTSIZE);
|
height_mm = GetDeviceCaps(dc, VERTSIZE);
|
||||||
ReleaseDC(0, dc);
|
ReleaseDC(0, dc);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -136,7 +136,7 @@ int get_monitor_size(int& width_mm, int& height_mm)
|
|||||||
// support routines for getting DLL version
|
// support routines for getting DLL version
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
|
|
||||||
static int get_ver(const char* module_path, char* out_ver, size_t out_ver_len)
|
static LibError get_ver(const char* module_path, char* out_ver, size_t out_ver_len)
|
||||||
{
|
{
|
||||||
WIN_SAVE_LAST_ERROR; // GetFileVersion*, Ver*
|
WIN_SAVE_LAST_ERROR; // GetFileVersion*, Ver*
|
||||||
|
|
||||||
@ -144,12 +144,12 @@ static int get_ver(const char* module_path, char* out_ver, size_t out_ver_len)
|
|||||||
DWORD unused;
|
DWORD unused;
|
||||||
const DWORD ver_size = GetFileVersionInfoSize(module_path, &unused);
|
const DWORD ver_size = GetFileVersionInfoSize(module_path, &unused);
|
||||||
if(!ver_size)
|
if(!ver_size)
|
||||||
return -1;
|
return ERR_FAIL;
|
||||||
void* buf = malloc(ver_size);
|
void* buf = malloc(ver_size);
|
||||||
if(!buf)
|
if(!buf)
|
||||||
return ERR_NO_MEM;
|
return ERR_NO_MEM;
|
||||||
|
|
||||||
int ret = -1; // single point of exit (for free())
|
LibError ret = ERR_FAIL; // single point of exit (for free())
|
||||||
|
|
||||||
if(GetFileVersionInfo(module_path, 0, ver_size, buf))
|
if(GetFileVersionInfo(module_path, 0, ver_size, buf))
|
||||||
{
|
{
|
||||||
@ -165,7 +165,7 @@ static int get_ver(const char* module_path, char* out_ver, size_t out_ver_len)
|
|||||||
if(VerQueryValue(buf, subblock, (void**)&in_ver, &in_ver_len))
|
if(VerQueryValue(buf, subblock, (void**)&in_ver, &in_ver_len))
|
||||||
{
|
{
|
||||||
strcpy_s(out_ver, out_ver_len, in_ver);
|
strcpy_s(out_ver, out_ver_len, in_ver);
|
||||||
ret = 0; // success
|
ret = ERR_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -198,13 +198,13 @@ static void dll_list_init(char* buf, size_t chars)
|
|||||||
// name should preferably be the complete path to DLL, to make sure
|
// name should preferably be the complete path to DLL, to make sure
|
||||||
// we don't inadvertently load another one on the library search path.
|
// we don't inadvertently load another one on the library search path.
|
||||||
// we add the .dll extension if necessary.
|
// we add the .dll extension if necessary.
|
||||||
static int dll_list_add(const char* name)
|
static LibError dll_list_add(const char* name)
|
||||||
{
|
{
|
||||||
// make sure we're allowed to be called.
|
// make sure we're allowed to be called.
|
||||||
if(!dll_list_pos)
|
if(!dll_list_pos)
|
||||||
{
|
{
|
||||||
debug_warn("called before dll_list_init or after failure");
|
debug_warn("called before dll_list_init or after failure");
|
||||||
return -1;
|
return ERR_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// some driver names are stored in the registry without .dll extension.
|
// some driver names are stored in the registry without .dll extension.
|
||||||
@ -241,7 +241,7 @@ static int dll_list_add(const char* name)
|
|||||||
if(len > 0)
|
if(len > 0)
|
||||||
{
|
{
|
||||||
dll_list_pos += len;
|
dll_list_pos += len;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// didn't fit; complain
|
// didn't fit; complain
|
||||||
@ -259,7 +259,7 @@ static int dll_list_add(const char* name)
|
|||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
static int win_get_gfx_card()
|
static LibError win_get_gfx_card()
|
||||||
{
|
{
|
||||||
// make sure EnumDisplayDevices is available (as pEnumDisplayDevicesA)
|
// make sure EnumDisplayDevices is available (as pEnumDisplayDevicesA)
|
||||||
if(import_EnumDisplayDevices() >= 0)
|
if(import_EnumDisplayDevices() >= 0)
|
||||||
@ -282,20 +282,21 @@ static int win_get_gfx_card()
|
|||||||
if(dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
|
if(dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
|
||||||
{
|
{
|
||||||
strcpy_s(gfx_card, ARRAY_SIZE(gfx_card), (const char*)dd.DeviceString);
|
strcpy_s(gfx_card, ARRAY_SIZE(gfx_card), (const char*)dd.DeviceString);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return ERR_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// note: this implementation doesn't require OpenGL to be initialized.
|
// note: this implementation doesn't require OpenGL to be initialized.
|
||||||
static int win_get_gfx_drv_ver()
|
static LibError win_get_gfx_drv_ver()
|
||||||
{
|
{
|
||||||
|
// don't overwrite existing information
|
||||||
if(gfx_drv_ver[0] != '\0')
|
if(gfx_drv_ver[0] != '\0')
|
||||||
return -1;
|
return ERR_FAIL;
|
||||||
|
|
||||||
// rationale:
|
// rationale:
|
||||||
// - we could easily determine the 2d driver via EnumDisplaySettings,
|
// - we could easily determine the 2d driver via EnumDisplaySettings,
|
||||||
@ -314,7 +315,7 @@ static int win_get_gfx_drv_ver()
|
|||||||
// gfx_card which one is correct; we thus avoid driver-specific
|
// gfx_card which one is correct; we thus avoid driver-specific
|
||||||
// name checks and reporting incorrectly.
|
// name checks and reporting incorrectly.
|
||||||
|
|
||||||
int ret = -1; // single point of exit (for RegCloseKey)
|
LibError ret = ERR_FAIL; // single point of exit (for RegCloseKey)
|
||||||
DWORD i;
|
DWORD i;
|
||||||
char drv_name[MAX_PATH+1];
|
char drv_name[MAX_PATH+1];
|
||||||
|
|
||||||
@ -323,7 +324,7 @@ static int win_get_gfx_drv_ver()
|
|||||||
HKEY hkOglDrivers;
|
HKEY hkOglDrivers;
|
||||||
const char* key = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\OpenGLDrivers";
|
const char* key = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\OpenGLDrivers";
|
||||||
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, key, 0, KEY_READ, &hkOglDrivers) != 0)
|
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, key, 0, KEY_READ, &hkOglDrivers) != 0)
|
||||||
return -1;
|
return ERR_FAIL;
|
||||||
|
|
||||||
// for each subkey (i.e. set of installed OpenGL drivers):
|
// for each subkey (i.e. set of installed OpenGL drivers):
|
||||||
for(i = 0; ; i++)
|
for(i = 0; ; i++)
|
||||||
@ -367,15 +368,15 @@ static int win_get_gfx_drv_ver()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int win_get_gfx_info()
|
LibError win_get_gfx_info()
|
||||||
{
|
{
|
||||||
int err1 = win_get_gfx_card();
|
LibError err1 = win_get_gfx_card();
|
||||||
int err2 = win_get_gfx_drv_ver();
|
LibError err2 = win_get_gfx_drv_ver();
|
||||||
|
|
||||||
// don't exit before trying both
|
// don't exit before trying both
|
||||||
CHECK_ERR(err1);
|
CHECK_ERR(err1);
|
||||||
CHECK_ERR(err2);
|
CHECK_ERR(err2);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -399,25 +400,25 @@ typedef std::set<std::string> StringSet;
|
|||||||
// note: we need the full DLL path for dll_list_add but DirEnt only gives us
|
// note: we need the full DLL path for dll_list_add but DirEnt only gives us
|
||||||
// the name. for efficiency, we append this in a PathPackage allocated by
|
// the name. for efficiency, we append this in a PathPackage allocated by
|
||||||
// add_oal_dlls_in_dir.
|
// add_oal_dlls_in_dir.
|
||||||
static int add_if_oal_dll(const DirEnt* ent, PathPackage* pp, StringSet* dlls)
|
static LibError add_if_oal_dll(const DirEnt* ent, PathPackage* pp, StringSet* dlls)
|
||||||
{
|
{
|
||||||
const char* fn = ent->name;
|
const char* fn = ent->name;
|
||||||
|
|
||||||
// skip non-files.
|
// skip non-files.
|
||||||
if(!DIRENT_IS_DIR(ent))
|
if(!DIRENT_IS_DIR(ent))
|
||||||
return 0;
|
return ERR_OK;
|
||||||
|
|
||||||
// skip if not an OpenAL DLL.
|
// skip if not an OpenAL DLL.
|
||||||
const size_t len = strlen(fn);
|
const size_t len = strlen(fn);
|
||||||
const bool oal = len >= 7 && !stricmp(fn+len-7, "oal.dll");
|
const bool oal = len >= 7 && !stricmp(fn+len-7, "oal.dll");
|
||||||
const bool openal = strstr(fn, "OpenAL") != 0;
|
const bool openal = strstr(fn, "OpenAL") != 0;
|
||||||
if(!oal && !openal)
|
if(!oal && !openal)
|
||||||
return 0;
|
return ERR_OK;
|
||||||
|
|
||||||
// skip if already in StringSet (i.e. has already been dll_list_add-ed)
|
// skip if already in StringSet (i.e. has already been dll_list_add-ed)
|
||||||
std::pair<StringSet::iterator, bool> ret = dlls->insert(fn);
|
std::pair<StringSet::iterator, bool> ret = dlls->insert(fn);
|
||||||
if(!ret.second) // insert failed - element already there
|
if(!ret.second) // insert failed - element already there
|
||||||
return 0;
|
return ERR_OK;
|
||||||
|
|
||||||
RETURN_ERR(pp_append_file(pp, fn));
|
RETURN_ERR(pp_append_file(pp, fn));
|
||||||
return dll_list_add(pp->path);
|
return dll_list_add(pp->path);
|
||||||
@ -430,7 +431,7 @@ static int add_if_oal_dll(const DirEnt* ent, PathPackage* pp, StringSet* dlls)
|
|||||||
// same name in the system directory.
|
// same name in the system directory.
|
||||||
//
|
//
|
||||||
// <dir>: no trailing.
|
// <dir>: no trailing.
|
||||||
static int add_oal_dlls_in_dir(const char* dir, StringSet* dlls)
|
static LibError add_oal_dlls_in_dir(const char* dir, StringSet* dlls)
|
||||||
{
|
{
|
||||||
PathPackage pp;
|
PathPackage pp;
|
||||||
RETURN_ERR(pp_set_dir(&pp, dir));
|
RETURN_ERR(pp_set_dir(&pp, dir));
|
||||||
@ -441,14 +442,14 @@ static int add_oal_dlls_in_dir(const char* dir, StringSet* dlls)
|
|||||||
DirEnt ent;
|
DirEnt ent;
|
||||||
for(;;) // instead of while to avoid warning
|
for(;;) // instead of while to avoid warning
|
||||||
{
|
{
|
||||||
int err = dir_next_ent(&d, &ent);
|
LibError err = dir_next_ent(&d, &ent);
|
||||||
if(err != 0)
|
if(err != ERR_OK)
|
||||||
break;
|
break;
|
||||||
(void)add_if_oal_dll(&ent, &pp, dlls);
|
(void)add_if_oal_dll(&ent, &pp, dlls);
|
||||||
}
|
}
|
||||||
|
|
||||||
(void)dir_close(&d);
|
(void)dir_close(&d);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -476,7 +477,7 @@ static BOOL CALLBACK ds_enum(void* UNUSED(guid), const char* description,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int win_get_snd_info()
|
LibError win_get_snd_info()
|
||||||
{
|
{
|
||||||
// get sound card name and DS driver path.
|
// get sound card name and DS driver path.
|
||||||
if(DirectSoundEnumerateA((LPDSENUMCALLBACKA)ds_enum, (void*)0) != DS_OK)
|
if(DirectSoundEnumerateA((LPDSENUMCALLBACKA)ds_enum, (void*)0) != DS_OK)
|
||||||
@ -488,7 +489,7 @@ int win_get_snd_info()
|
|||||||
{
|
{
|
||||||
strcpy_s(snd_card, SND_CARD_LEN, "(none)");
|
strcpy_s(snd_card, SND_CARD_LEN, "(none)");
|
||||||
strcpy_s(snd_drv_ver, SND_DRV_VER_LEN, "(none)");
|
strcpy_s(snd_drv_ver, SND_DRV_VER_LEN, "(none)");
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// find all DLLs related to OpenAL, retrieve their versions,
|
// find all DLLs related to OpenAL, retrieve their versions,
|
||||||
@ -498,5 +499,5 @@ int win_get_snd_info()
|
|||||||
StringSet dlls; // ensures uniqueness
|
StringSet dlls; // ensures uniqueness
|
||||||
(void)add_oal_dlls_in_dir(win_exe_dir, &dlls);
|
(void)add_oal_dlls_in_dir(win_exe_dir, &dlls);
|
||||||
(void)add_oal_dlls_in_dir(win_sys_dir, &dlls);
|
(void)add_oal_dlls_in_dir(win_sys_dir, &dlls);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
@ -153,7 +153,7 @@ static Events pending_events;
|
|||||||
// but that's more complicated, and this way is cleaner anyway.
|
// but that's more complicated, and this way is cleaner anyway.
|
||||||
|
|
||||||
|
|
||||||
static int wdir_watch_shutdown()
|
static LibError wdir_watch_shutdown()
|
||||||
{
|
{
|
||||||
CloseHandle(hIOCP);
|
CloseHandle(hIOCP);
|
||||||
hIOCP = INVALID_HANDLE_VALUE;
|
hIOCP = INVALID_HANDLE_VALUE;
|
||||||
@ -163,12 +163,12 @@ static int wdir_watch_shutdown()
|
|||||||
delete it->second;
|
delete it->second;
|
||||||
watches.clear();
|
watches.clear();
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// HACK - see call site
|
// HACK - see call site
|
||||||
static int get_packet();
|
static void get_packet();
|
||||||
|
|
||||||
|
|
||||||
static Watch* find_watch(intptr_t reqnum)
|
static Watch* find_watch(intptr_t reqnum)
|
||||||
@ -178,9 +178,9 @@ static Watch* find_watch(intptr_t reqnum)
|
|||||||
|
|
||||||
// path: portable and relative, must add current directory and convert to native
|
// path: portable and relative, must add current directory and convert to native
|
||||||
// better to use a cached string from rel_chdir - secure
|
// better to use a cached string from rel_chdir - secure
|
||||||
int dir_add_watch(const char* dir, intptr_t* _reqnum)
|
LibError dir_add_watch(const char* dir, intptr_t* _reqnum)
|
||||||
{
|
{
|
||||||
int err = -1;
|
LibError err = ERR_FAIL;
|
||||||
WIN_SAVE_LAST_ERROR; // Create*
|
WIN_SAVE_LAST_ERROR; // Create*
|
||||||
|
|
||||||
intptr_t reqnum;
|
intptr_t reqnum;
|
||||||
@ -237,10 +237,10 @@ int dir_add_watch(const char* dir, intptr_t* _reqnum)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// allocate watch, add to list, associate with reqnum
|
// allocate watch, add to list, associate with reqnum
|
||||||
|
// note: can't use SAFE_NEW due to ctor params.
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Watch* w = new Watch(reqnum, dir_s, hDir);
|
Watch* w = new Watch(reqnum, dir_s, hDir);
|
||||||
debug_assert(w != 0); // happened once; heap corruption?
|
|
||||||
|
|
||||||
// add trailing \ if not already there
|
// add trailing \ if not already there
|
||||||
if(dir_s[dir_s.length()-1] != '\\')
|
if(dir_s[dir_s.length()-1] != '\\')
|
||||||
@ -263,7 +263,7 @@ int dir_add_watch(const char* dir, intptr_t* _reqnum)
|
|||||||
}
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
err = 0;
|
err = ERR_OK;
|
||||||
*_reqnum = reqnum;
|
*_reqnum = reqnum;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
@ -272,7 +272,7 @@ fail:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int dir_cancel_watch(const intptr_t reqnum)
|
LibError dir_cancel_watch(const intptr_t reqnum)
|
||||||
{
|
{
|
||||||
if(reqnum <= 0)
|
if(reqnum <= 0)
|
||||||
return ERR_INVALID_PARAM;
|
return ERR_INVALID_PARAM;
|
||||||
@ -281,28 +281,28 @@ int dir_cancel_watch(const intptr_t reqnum)
|
|||||||
if(!w)
|
if(!w)
|
||||||
{
|
{
|
||||||
debug_warn("watches[reqnum] invalid");
|
debug_warn("watches[reqnum] invalid");
|
||||||
return -1;
|
return ERR_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// we're freeing a reference - done.
|
// we're freeing a reference - done.
|
||||||
debug_assert(w->refs >= 1);
|
debug_assert(w->refs >= 1);
|
||||||
if(--w->refs != 0)
|
if(--w->refs != 0)
|
||||||
return 0;
|
return ERR_OK;
|
||||||
|
|
||||||
// contrary to dox, the RDC IOs do not issue a completion notification.
|
// contrary to dox, the RDC IOs do not issue a completion notification.
|
||||||
// no packet was received on the IOCP while or after cancelling in a test.
|
// no packet was received on the IOCP while or after cancelling in a test.
|
||||||
//
|
//
|
||||||
// if cancel somehow fails though, no matter - the Watch is freed, and
|
// if cancel somehow fails though, no matter - the Watch is freed, and
|
||||||
// its reqnum isn't reused; if we receive a packet, it's ignored.
|
// its reqnum isn't reused; if we receive a packet, it's ignored.
|
||||||
BOOL ret = CancelIo(w->hDir);
|
BOOL ok = CancelIo(w->hDir);
|
||||||
|
|
||||||
delete w;
|
delete w;
|
||||||
watches[reqnum] = 0;
|
watches[reqnum] = 0;
|
||||||
return ret? 0 : -1;
|
return LibError_from_win32(ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int extract_events(Watch* w)
|
static void extract_events(Watch* w)
|
||||||
{
|
{
|
||||||
debug_assert(w);
|
debug_assert(w);
|
||||||
|
|
||||||
@ -330,13 +330,12 @@ static int extract_events(Watch* w)
|
|||||||
break;
|
break;
|
||||||
pos += ofs;
|
pos += ofs;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// if a packet is pending, extract its events and re-issue its watch.
|
// if a packet is pending, extract its events, post them in the queue and
|
||||||
static int get_packet()
|
// re-issue its watch.
|
||||||
|
static void get_packet()
|
||||||
{
|
{
|
||||||
// poll for change notifications from all pending watches
|
// poll for change notifications from all pending watches
|
||||||
DWORD bytes_transferred;
|
DWORD bytes_transferred;
|
||||||
@ -345,13 +344,13 @@ static int get_packet()
|
|||||||
OVERLAPPED* povl;
|
OVERLAPPED* povl;
|
||||||
BOOL got_packet = GetQueuedCompletionStatus(hIOCP, &bytes_transferred, &key, &povl, 0);
|
BOOL got_packet = GetQueuedCompletionStatus(hIOCP, &bytes_transferred, &key, &povl, 0);
|
||||||
if(!got_packet) // no new packet - done
|
if(!got_packet) // no new packet - done
|
||||||
return 1;
|
return;
|
||||||
|
|
||||||
const intptr_t reqnum = (intptr_t)key;
|
const intptr_t reqnum = (intptr_t)key;
|
||||||
Watch* const w = find_watch(reqnum);
|
Watch* const w = find_watch(reqnum);
|
||||||
// watch was subsequently removed - ignore the error.
|
// watch was subsequently removed - ignore the error.
|
||||||
if(!w)
|
if(!w)
|
||||||
return 1;
|
return;
|
||||||
|
|
||||||
// this is an actual packet, not just a kickoff for issuing the watch.
|
// this is an actual packet, not just a kickoff for issuing the watch.
|
||||||
// extract the events and push them onto AppState's queue.
|
// extract the events and push them onto AppState's queue.
|
||||||
@ -367,29 +366,27 @@ static int get_packet()
|
|||||||
memset(&w->ovl, 0, sizeof(w->ovl));
|
memset(&w->ovl, 0, sizeof(w->ovl));
|
||||||
BOOL watch_subtree = TRUE;
|
BOOL watch_subtree = TRUE;
|
||||||
// much faster than watching every dir separately. see dir_add_watch.
|
// much faster than watching every dir separately. see dir_add_watch.
|
||||||
BOOL ret = ReadDirectoryChangesW(w->hDir, w->change_buf, buf_size, watch_subtree, filter, &w->dummy_nbytes, &w->ovl, 0);
|
BOOL ok = ReadDirectoryChangesW(w->hDir, w->change_buf, buf_size, watch_subtree, filter, &w->dummy_nbytes, &w->ovl, 0);
|
||||||
if(!ret)
|
WARN_IF_FALSE(ok);
|
||||||
debug_warn("ReadDirectoryChangesW failed");
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// if a file change notification is pending, store its filename in <fn> and
|
// if a file change notification is pending, store its filename in <fn> and
|
||||||
// return 0; otherwise, return 1 ('none currently pending') or an error code.
|
// return ERR_OK; otherwise, return ERR_AGAIN ('none currently pending') or
|
||||||
|
// a negative error code.
|
||||||
// <fn> must hold at least PATH_MAX chars.
|
// <fn> must hold at least PATH_MAX chars.
|
||||||
int dir_get_changed_file(char* fn)
|
LibError dir_get_changed_file(char* fn)
|
||||||
{
|
{
|
||||||
// queue one or more events, or return 1 if none pending.
|
// may or may not queue event(s).
|
||||||
CHECK_ERR(get_packet());
|
get_packet();
|
||||||
|
|
||||||
// nothing to return; call again later.
|
// nothing to return; call again later.
|
||||||
if(pending_events.empty())
|
if(pending_events.empty())
|
||||||
return 1;
|
return ERR_AGAIN;
|
||||||
|
|
||||||
const std::string& fn_s = pending_events.front();
|
const std::string& fn_s = pending_events.front();
|
||||||
strcpy_s(fn, PATH_MAX, fn_s.c_str());
|
strcpy_s(fn, PATH_MAX, fn_s.c_str());
|
||||||
pending_events.pop_front();
|
pending_events.pop_front();
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
@ -556,7 +556,7 @@ __HrLoadAllImportsForDll(LPCSTR szDll) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
static int wdll_shutdown()
|
static LibError wdll_shutdown()
|
||||||
{
|
{
|
||||||
PUnloadInfo pui;
|
PUnloadInfo pui;
|
||||||
|
|
||||||
@ -579,7 +579,7 @@ static int wdll_shutdown()
|
|||||||
// changes __puiHead!
|
// changes __puiHead!
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -5,10 +5,10 @@ extern void wdll_add_notify(DllLoadNotify*);
|
|||||||
struct DllLoadNotify
|
struct DllLoadNotify
|
||||||
{
|
{
|
||||||
const char* dll_name;
|
const char* dll_name;
|
||||||
int(*func)(void);
|
LibError (*func)(void);
|
||||||
DllLoadNotify* next;
|
DllLoadNotify* next;
|
||||||
|
|
||||||
DllLoadNotify(const char* _dll_name, int(*_func)(void))
|
DllLoadNotify(const char* _dll_name, LibError (*_func)(void))
|
||||||
{
|
{
|
||||||
dll_name = _dll_name;
|
dll_name = _dll_name;
|
||||||
func = _func;
|
func = _func;
|
||||||
|
@ -31,7 +31,7 @@ typedef __int64 INT64;
|
|||||||
typedef float FLOAT;
|
typedef float FLOAT;
|
||||||
typedef const char* LPCSTR;
|
typedef const char* LPCSTR;
|
||||||
typedef void* HANDLE;
|
typedef void* HANDLE;
|
||||||
typedef int(*PROC)(void);
|
typedef int (*PROC)(void);
|
||||||
#define DECLARE_HANDLE(name) typedef HANDLE name
|
#define DECLARE_HANDLE(name) typedef HANDLE name
|
||||||
DECLARE_HANDLE(HDC);
|
DECLARE_HANDLE(HDC);
|
||||||
DECLARE_HANDLE(HGLRC);
|
DECLARE_HANDLE(HGLRC);
|
||||||
|
@ -26,6 +26,44 @@
|
|||||||
char win_sys_dir[MAX_PATH+1];
|
char win_sys_dir[MAX_PATH+1];
|
||||||
char win_exe_dir[MAX_PATH+1];
|
char win_exe_dir[MAX_PATH+1];
|
||||||
|
|
||||||
|
|
||||||
|
// only call after a Win32 function indicates failure.
|
||||||
|
static LibError LibError_from_GLE()
|
||||||
|
{
|
||||||
|
switch(GetLastError())
|
||||||
|
{
|
||||||
|
case ERROR_OUTOFMEMORY:
|
||||||
|
return ERR_NO_MEM;
|
||||||
|
|
||||||
|
case ERROR_INVALID_PARAMETER:
|
||||||
|
return ERR_INVALID_PARAM;
|
||||||
|
case ERROR_INSUFFICIENT_BUFFER:
|
||||||
|
return ERR_BUF_SIZE;
|
||||||
|
|
||||||
|
case ERROR_ACCESS_DENIED:
|
||||||
|
return ERR_FILE_ACCESS;
|
||||||
|
case ERROR_FILE_NOT_FOUND:
|
||||||
|
return ERR_FILE_NOT_FOUND;
|
||||||
|
case ERROR_PATH_NOT_FOUND:
|
||||||
|
return ERR_PATH_NOT_FOUND;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return ERR_FAIL;
|
||||||
|
}
|
||||||
|
UNREACHABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// return the LibError equivalent of GetLastError(), or ERR_FAIL if
|
||||||
|
// there's no equal.
|
||||||
|
// you should SetLastError(0) before calling whatever will set ret
|
||||||
|
// to make sure we do not return any stale errors.
|
||||||
|
LibError LibError_from_win32(DWORD ret)
|
||||||
|
{
|
||||||
|
return (ret != FALSE)? ERR_OK : LibError_from_GLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -86,7 +124,7 @@ void win_free(void* p)
|
|||||||
// be placed beyond the table start/end by the linker, since the linker's
|
// be placed beyond the table start/end by the linker, since the linker's
|
||||||
// ordering WRT other source files' data is undefined within a segment.
|
// ordering WRT other source files' data is undefined within a segment.
|
||||||
|
|
||||||
typedef int(*_PIFV)(void);
|
typedef LibError (*_PIFV)(void);
|
||||||
|
|
||||||
// pointers to start and end of function tables.
|
// pointers to start and end of function tables.
|
||||||
// note: COFF tosses out empty segments, so we have to put in one value
|
// note: COFF tosses out empty segments, so we have to put in one value
|
||||||
|
@ -438,7 +438,7 @@ extern void win_free(void* p);
|
|||||||
// to be called via WIN_REGISTER_FUNC, and then restore the previous segment
|
// to be called via WIN_REGISTER_FUNC, and then restore the previous segment
|
||||||
// with #pragma data_seg() .
|
// with #pragma data_seg() .
|
||||||
|
|
||||||
#define WIN_REGISTER_FUNC(func) static int func(void); static int(*p##func)(void) = func
|
#define WIN_REGISTER_FUNC(func) static LibError func(void); static LibError (*p##func)(void) = func
|
||||||
|
|
||||||
#define WIN_CALLBACK_PRE_LIBC(group) ".LIB$WC" #group
|
#define WIN_CALLBACK_PRE_LIBC(group) ".LIB$WC" #group
|
||||||
#define WIN_CALLBACK_PRE_MAIN(group) ".LIB$WI" #group
|
#define WIN_CALLBACK_PRE_MAIN(group) ".LIB$WI" #group
|
||||||
@ -450,6 +450,13 @@ extern void win_free(void* p);
|
|||||||
#define WIN_RESTORE_LAST_ERROR STMT(if(last_err__ != 0 && GetLastError() == 0) SetLastError(last_err__););
|
#define WIN_RESTORE_LAST_ERROR STMT(if(last_err__ != 0 && GetLastError() == 0) SetLastError(last_err__););
|
||||||
|
|
||||||
|
|
||||||
|
// return the LibError equivalent of GetLastError(), or ERR_FAIL if
|
||||||
|
// there's no equal.
|
||||||
|
// you should SetLastError(0) before calling whatever will set ret
|
||||||
|
// to make sure we do not return any stale errors.
|
||||||
|
extern LibError LibError_from_win32(DWORD ret);
|
||||||
|
|
||||||
|
|
||||||
extern char win_sys_dir[MAX_PATH+1];
|
extern char win_sys_dir[MAX_PATH+1];
|
||||||
extern char win_exe_dir[MAX_PATH+1];
|
extern char win_exe_dir[MAX_PATH+1];
|
||||||
|
|
||||||
|
@ -624,7 +624,7 @@ int mprotect(void* addr, size_t len, int prot)
|
|||||||
|
|
||||||
|
|
||||||
// called when flags & MAP_ANONYMOUS
|
// called when flags & MAP_ANONYMOUS
|
||||||
static int mmap_mem(void* start, size_t len, int prot, int flags, int fd, void** pp)
|
static LibError mmap_mem(void* start, size_t len, int prot, int flags, int fd, void** pp)
|
||||||
{
|
{
|
||||||
// sanity checks. we don't care about these but enforce them to
|
// sanity checks. we don't care about these but enforce them to
|
||||||
// ensure callers are compatible with mmap.
|
// ensure callers are compatible with mmap.
|
||||||
@ -648,7 +648,7 @@ debug_assert(ok); // todo4
|
|||||||
ok = VirtualFree(start, len, MEM_DECOMMIT);
|
ok = VirtualFree(start, len, MEM_DECOMMIT);
|
||||||
debug_assert(ok);
|
debug_assert(ok);
|
||||||
*pp = 0;
|
*pp = 0;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -659,7 +659,7 @@ debug_assert(ok);
|
|||||||
if(!p)
|
if(!p)
|
||||||
return ERR_NO_MEM;
|
return ERR_NO_MEM;
|
||||||
*pp = p;
|
*pp = p;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -667,7 +667,7 @@ debug_assert(ok);
|
|||||||
// CreateFileMapping / MapViewOfFile. they only support read-only,
|
// CreateFileMapping / MapViewOfFile. they only support read-only,
|
||||||
// read/write and copy-on-write, so we dumb it down to that and later
|
// read/write and copy-on-write, so we dumb it down to that and later
|
||||||
// set the correct (and more restrictive) permission via mprotect.
|
// set the correct (and more restrictive) permission via mprotect.
|
||||||
static int mmap_file_access(int prot, int flags, DWORD& flProtect, DWORD& dwAccess)
|
static LibError mmap_file_access(int prot, int flags, DWORD& flProtect, DWORD& dwAccess)
|
||||||
{
|
{
|
||||||
// assume read-only; other cases handled below.
|
// assume read-only; other cases handled below.
|
||||||
flProtect = PAGE_READONLY;
|
flProtect = PAGE_READONLY;
|
||||||
@ -696,11 +696,11 @@ static int mmap_file_access(int prot, int flags, DWORD& flProtect, DWORD& dwAcce
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int mmap_file(void* start, size_t len, int prot, int flags,
|
static LibError mmap_file(void* start, size_t len, int prot, int flags,
|
||||||
int fd, off_t ofs, void** pp)
|
int fd, off_t ofs, void** pp)
|
||||||
{
|
{
|
||||||
debug_assert(fd != -1); // handled by mmap_mem
|
debug_assert(fd != -1); // handled by mmap_mem
|
||||||
@ -744,14 +744,14 @@ static int mmap_file(void* start, size_t len, int prot, int flags,
|
|||||||
|
|
||||||
WIN_RESTORE_LAST_ERROR;
|
WIN_RESTORE_LAST_ERROR;
|
||||||
*pp = p;
|
*pp = p;
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void* mmap(void* start, size_t len, int prot, int flags, int fd, off_t ofs)
|
void* mmap(void* start, size_t len, int prot, int flags, int fd, off_t ofs)
|
||||||
{
|
{
|
||||||
void* p;
|
void* p;
|
||||||
int err;
|
LibError err;
|
||||||
if(flags & MAP_ANONYMOUS)
|
if(flags & MAP_ANONYMOUS)
|
||||||
err = mmap_mem(start, len, prot, flags, fd, &p);
|
err = mmap_mem(start, len, prot, flags, fd, &p);
|
||||||
else
|
else
|
||||||
|
@ -114,7 +114,7 @@ static const uint MAX_DTORS = 4;
|
|||||||
static struct
|
static struct
|
||||||
{
|
{
|
||||||
pthread_key_t key;
|
pthread_key_t key;
|
||||||
void(*dtor)(void*);
|
void (*dtor)(void*);
|
||||||
}
|
}
|
||||||
dtors[MAX_DTORS];
|
dtors[MAX_DTORS];
|
||||||
|
|
||||||
@ -203,7 +203,7 @@ again:
|
|||||||
for(uint i = 0; i < MAX_DTORS; i++)
|
for(uint i = 0; i < MAX_DTORS; i++)
|
||||||
{
|
{
|
||||||
// is slot #i in use?
|
// is slot #i in use?
|
||||||
void(*dtor)(void*) = dtors[i].dtor;
|
void (*dtor)(void*) = dtors[i].dtor;
|
||||||
if(!dtor)
|
if(!dtor)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -242,7 +242,7 @@ again:
|
|||||||
// c) we therefore use static data protected by a critical section.
|
// c) we therefore use static data protected by a critical section.
|
||||||
static struct FuncAndArg
|
static struct FuncAndArg
|
||||||
{
|
{
|
||||||
void*(*func)(void*);
|
void* (*func)(void*);
|
||||||
void* arg;
|
void* arg;
|
||||||
}
|
}
|
||||||
func_and_arg;
|
func_and_arg;
|
||||||
@ -251,7 +251,7 @@ func_and_arg;
|
|||||||
// bridge calling conventions required by _beginthreadex and POSIX.
|
// bridge calling conventions required by _beginthreadex and POSIX.
|
||||||
static unsigned __stdcall thread_start(void* UNUSED(param))
|
static unsigned __stdcall thread_start(void* UNUSED(param))
|
||||||
{
|
{
|
||||||
void*(*func)(void*) = func_and_arg.func;
|
void* (*func)(void*) = func_and_arg.func;
|
||||||
void* arg = func_and_arg.arg;
|
void* arg = func_and_arg.arg;
|
||||||
win_unlock(WPTHREAD_CS);
|
win_unlock(WPTHREAD_CS);
|
||||||
|
|
||||||
@ -270,7 +270,7 @@ static unsigned __stdcall thread_start(void* UNUSED(param))
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int pthread_create(pthread_t* thread_id, const void* UNUSED(attr), void*(*func)(void*), void* arg)
|
int pthread_create(pthread_t* thread_id, const void* UNUSED(attr), void* (*func)(void*), void* arg)
|
||||||
{
|
{
|
||||||
win_lock(WPTHREAD_CS);
|
win_lock(WPTHREAD_CS);
|
||||||
func_and_arg.func = func;
|
func_and_arg.func = func;
|
||||||
|
@ -61,7 +61,7 @@ typedef unsigned int pthread_t;
|
|||||||
extern pthread_t pthread_self(void);
|
extern pthread_t pthread_self(void);
|
||||||
extern int pthread_getschedparam(pthread_t thread, int* policy, struct sched_param* param);
|
extern int pthread_getschedparam(pthread_t thread, int* policy, struct sched_param* param);
|
||||||
extern int pthread_setschedparam(pthread_t thread, int policy, const struct sched_param* param);
|
extern int pthread_setschedparam(pthread_t thread, int policy, const struct sched_param* param);
|
||||||
extern int pthread_create(pthread_t* thread, const void* attr, void*(*func)(void*), void* arg);
|
extern int pthread_create(pthread_t* thread, const void* attr, void* (*func)(void*), void* arg);
|
||||||
extern int pthread_cancel(pthread_t thread);
|
extern int pthread_cancel(pthread_t thread);
|
||||||
extern int pthread_join(pthread_t thread, void** value_ptr);
|
extern int pthread_join(pthread_t thread, void** value_ptr);
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ static u16 cur_ramp[3][256];
|
|||||||
|
|
||||||
|
|
||||||
// ramp: 8.8 fixed point
|
// ramp: 8.8 fixed point
|
||||||
static int calc_gamma_ramp(float gamma, u16* ramp)
|
static LibError calc_gamma_ramp(float gamma, u16* ramp)
|
||||||
{
|
{
|
||||||
// assume identity if invalid
|
// assume identity if invalid
|
||||||
if(gamma <= 0.0f)
|
if(gamma <= 0.0f)
|
||||||
@ -93,7 +93,7 @@ static int calc_gamma_ramp(float gamma, u16* ramp)
|
|||||||
{
|
{
|
||||||
for(u16 i = 0; i < 256; i++)
|
for(u16 i = 0; i < 256; i++)
|
||||||
ramp[i] = (i << 8);
|
ramp[i] = (i << 8);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
const double inv_gamma = 1.0 / gamma;
|
const double inv_gamma = 1.0 / gamma;
|
||||||
@ -106,7 +106,7 @@ static int calc_gamma_ramp(float gamma, u16* ramp)
|
|||||||
ramp[i] = fp_to_u16(pow(frac, inv_gamma));
|
ramp[i] = fp_to_u16(pow(frac, inv_gamma));
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1106,10 +1106,10 @@ int SDL_SemWait(SDL_sem* sem)
|
|||||||
// warnings in VC2005, so we coerce values directly.
|
// warnings in VC2005, so we coerce values directly.
|
||||||
cassert(sizeof(pthread_t) == sizeof(SDL_Thread*));
|
cassert(sizeof(pthread_t) == sizeof(SDL_Thread*));
|
||||||
|
|
||||||
SDL_Thread* SDL_CreateThread(int(*func)(void*), void* param)
|
SDL_Thread* SDL_CreateThread(int (*func)(void*), void* param)
|
||||||
{
|
{
|
||||||
pthread_t thread = 0;
|
pthread_t thread = 0;
|
||||||
if(pthread_create(&thread, 0, (void*(*)(void*))func, param) < 0)
|
if(pthread_create(&thread, 0, (void* (*)(void*))func, param) < 0)
|
||||||
return 0;
|
return 0;
|
||||||
return *(SDL_Thread**)&thread;
|
return *(SDL_Thread**)&thread;
|
||||||
}
|
}
|
||||||
@ -1154,7 +1154,7 @@ inline void* SDL_GL_GetProcAddress(const char* name)
|
|||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// init/shutdown
|
// init/shutdown
|
||||||
|
|
||||||
static int wsdl_init()
|
static LibError wsdl_init()
|
||||||
{
|
{
|
||||||
hInst = GetModuleHandle(0);
|
hInst = GetModuleHandle(0);
|
||||||
|
|
||||||
@ -1170,8 +1170,8 @@ static int wsdl_init()
|
|||||||
// to avoid the OS opening a console on startup (ugly). that means
|
// to avoid the OS opening a console on startup (ugly). that means
|
||||||
// stdout isn't associated with a lowio handle; _close ends up
|
// stdout isn't associated with a lowio handle; _close ends up
|
||||||
// getting called with fd = -1. oh well, nothing we can do.
|
// getting called with fd = -1. oh well, nothing we can do.
|
||||||
FILE* ret = freopen(path, "wt", stdout);
|
FILE* f = freopen(path, "wt", stdout);
|
||||||
if(!ret)
|
if(!f)
|
||||||
debug_warn("stdout freopen failed");
|
debug_warn("stdout freopen failed");
|
||||||
|
|
||||||
#if CONFIG_PARANOIA
|
#if CONFIG_PARANOIA
|
||||||
@ -1182,11 +1182,11 @@ static int wsdl_init()
|
|||||||
|
|
||||||
enable_kbd_hook(true);
|
enable_kbd_hook(true);
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int wsdl_shutdown()
|
static LibError wsdl_shutdown()
|
||||||
{
|
{
|
||||||
is_shutdown = true;
|
is_shutdown = true;
|
||||||
|
|
||||||
@ -1200,7 +1200,7 @@ static int wsdl_shutdown()
|
|||||||
|
|
||||||
enable_kbd_hook(false);
|
enable_kbd_hook(false);
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -97,7 +97,7 @@ extern void SDL_DestroySemaphore(SDL_sem*);
|
|||||||
extern int SDL_SemPost(SDL_sem*);
|
extern int SDL_SemPost(SDL_sem*);
|
||||||
extern int SDL_SemWait(SDL_sem* sem);
|
extern int SDL_SemWait(SDL_sem* sem);
|
||||||
|
|
||||||
extern SDL_Thread* SDL_CreateThread(int(*)(void*), void*);
|
extern SDL_Thread* SDL_CreateThread(int (*)(void*), void*);
|
||||||
extern int SDL_KillThread(SDL_Thread*);
|
extern int SDL_KillThread(SDL_Thread*);
|
||||||
|
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ static int dll_refs;
|
|||||||
|
|
||||||
// called from delay loader the first time a wsock function is called
|
// called from delay loader the first time a wsock function is called
|
||||||
// (shortly before the actual wsock function is called).
|
// (shortly before the actual wsock function is called).
|
||||||
static int wsock_init()
|
static LibError wsock_init()
|
||||||
{
|
{
|
||||||
hWs2_32Dll = LoadLibrary("ws2_32.dll");
|
hWs2_32Dll = LoadLibrary("ws2_32.dll");
|
||||||
|
|
||||||
@ -58,14 +58,14 @@ static int wsock_init()
|
|||||||
debug_warn("WSAStartup failed");
|
debug_warn("WSAStartup failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
WDLL_LOAD_NOTIFY("ws2_32", wsock_init);
|
WDLL_LOAD_NOTIFY("ws2_32", wsock_init);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static int wsock_shutdown()
|
static LibError wsock_shutdown()
|
||||||
{
|
{
|
||||||
// call WSACleanup if DLL was used
|
// call WSACleanup if DLL was used
|
||||||
// (this way is easier to understand than ONCE in loop below)
|
// (this way is easier to understand than ONCE in loop below)
|
||||||
@ -77,7 +77,7 @@ static int wsock_shutdown()
|
|||||||
while(dll_refs-- > 0)
|
while(dll_refs-- > 0)
|
||||||
FreeLibrary(hWs2_32Dll);
|
FreeLibrary(hWs2_32Dll);
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -111,13 +111,13 @@ static HWND get_app_main_window()
|
|||||||
static POINTS dlg_client_origin;
|
static POINTS dlg_client_origin;
|
||||||
static POINTS dlg_prev_client_size;
|
static POINTS dlg_prev_client_size;
|
||||||
|
|
||||||
const int ANCHOR_LEFT = 0x01;
|
static const uint ANCHOR_LEFT = 0x01;
|
||||||
const int ANCHOR_RIGHT = 0x02;
|
static const uint ANCHOR_RIGHT = 0x02;
|
||||||
const int ANCHOR_TOP = 0x04;
|
static const uint ANCHOR_TOP = 0x04;
|
||||||
const int ANCHOR_BOTTOM = 0x08;
|
static const uint ANCHOR_BOTTOM = 0x08;
|
||||||
const int ANCHOR_ALL = 0x0f;
|
static const uint ANCHOR_ALL = 0x0f;
|
||||||
|
|
||||||
static void dlg_resize_control(HWND hDlg, int dlg_item, int dx,int dy, int anchors)
|
static void dlg_resize_control(HWND hDlg, int dlg_item, int dx,int dy, uint anchors)
|
||||||
{
|
{
|
||||||
HWND hControl = GetDlgItem(hDlg, dlg_item);
|
HWND hControl = GetDlgItem(hDlg, dlg_item);
|
||||||
RECT r;
|
RECT r;
|
||||||
@ -189,7 +189,7 @@ struct DialogParams
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
static int CALLBACK error_dialog_proc(HWND hDlg, unsigned int msg, WPARAM wParam, LPARAM lParam)
|
static INT_PTR CALLBACK error_dialog_proc(HWND hDlg, unsigned int msg, WPARAM wParam, LPARAM lParam)
|
||||||
{
|
{
|
||||||
switch(msg)
|
switch(msg)
|
||||||
{
|
{
|
||||||
@ -345,24 +345,25 @@ ErrorReaction sys_display_error(const wchar_t* text, int flags)
|
|||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
// "copy" text into the clipboard. replaces previous contents.
|
// "copy" text into the clipboard. replaces previous contents.
|
||||||
int sys_clipboard_set(const wchar_t* text)
|
LibError sys_clipboard_set(const wchar_t* text)
|
||||||
{
|
{
|
||||||
int err = -1;
|
|
||||||
|
|
||||||
const HWND new_owner = 0;
|
const HWND new_owner = 0;
|
||||||
// MSDN: passing 0 requests the current task be granted ownership;
|
// MSDN: passing 0 requests the current task be granted ownership;
|
||||||
// there's no need to pass our window handle.
|
// there's no need to pass our window handle.
|
||||||
if(!OpenClipboard(new_owner))
|
if(!OpenClipboard(new_owner))
|
||||||
return err;
|
return ERR_FAIL;
|
||||||
EmptyClipboard();
|
EmptyClipboard();
|
||||||
|
|
||||||
err = 0;
|
LibError err = ERR_FAIL;
|
||||||
|
|
||||||
|
{
|
||||||
const size_t len = wcslen(text);
|
const size_t len = wcslen(text);
|
||||||
|
|
||||||
HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, (len+1) * sizeof(wchar_t));
|
HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, (len+1) * sizeof(wchar_t));
|
||||||
if(!hMem)
|
if(!hMem)
|
||||||
|
{
|
||||||
|
err = ERR_NO_MEM;
|
||||||
goto fail;
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
wchar_t* copy = (wchar_t*)GlobalLock(hMem);
|
wchar_t* copy = (wchar_t*)GlobalLock(hMem);
|
||||||
if(copy)
|
if(copy)
|
||||||
@ -372,7 +373,8 @@ int sys_clipboard_set(const wchar_t* text)
|
|||||||
GlobalUnlock(hMem);
|
GlobalUnlock(hMem);
|
||||||
|
|
||||||
if(SetClipboardData(CF_UNICODETEXT, hMem) != 0)
|
if(SetClipboardData(CF_UNICODETEXT, hMem) != 0)
|
||||||
err = 0; // success
|
err = ERR_OK;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
@ -422,10 +424,10 @@ wchar_t* sys_clipboard_get()
|
|||||||
|
|
||||||
// frees memory used by <copy>, which must have been returned by
|
// frees memory used by <copy>, which must have been returned by
|
||||||
// sys_clipboard_get. see note above.
|
// sys_clipboard_get. see note above.
|
||||||
int sys_clipboard_free(wchar_t* copy)
|
LibError sys_clipboard_free(wchar_t* copy)
|
||||||
{
|
{
|
||||||
free(copy);
|
free(copy);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -464,7 +466,7 @@ static HCURSOR HCURSOR_from_ptr(void* p)
|
|||||||
// return: negative error code, or 0 on success. cursor is filled with
|
// return: negative error code, or 0 on success. cursor is filled with
|
||||||
// a pointer and undefined on failure. it must be sys_cursor_free-ed
|
// a pointer and undefined on failure. it must be sys_cursor_free-ed
|
||||||
// when no longer needed.
|
// when no longer needed.
|
||||||
int sys_cursor_create(uint w, uint h, void* bgra_img,
|
LibError sys_cursor_create(uint w, uint h, void* bgra_img,
|
||||||
uint hx, uint hy, void** cursor)
|
uint hx, uint hy, void** cursor)
|
||||||
{
|
{
|
||||||
// MSDN says selecting this HBITMAP into a DC is slower since we use
|
// MSDN says selecting this HBITMAP into a DC is slower since we use
|
||||||
@ -492,17 +494,17 @@ int sys_cursor_create(uint w, uint h, void* bgra_img,
|
|||||||
DeleteObject(hbmColour);
|
DeleteObject(hbmColour);
|
||||||
|
|
||||||
if(!hIcon) // not INVALID_HANDLE_VALUE
|
if(!hIcon) // not INVALID_HANDLE_VALUE
|
||||||
return -1;
|
return ERR_FAIL;
|
||||||
|
|
||||||
*cursor = ptr_from_HICON(hIcon);
|
*cursor = ptr_from_HICON(hIcon);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// replaces the current system cursor with the one indicated. need only be
|
// replaces the current system cursor with the one indicated. need only be
|
||||||
// called once per cursor; pass 0 to restore the default.
|
// called once per cursor; pass 0 to restore the default.
|
||||||
int sys_cursor_set(void* cursor)
|
LibError sys_cursor_set(void* cursor)
|
||||||
{
|
{
|
||||||
// restore default cursor.
|
// restore default cursor.
|
||||||
if(!cursor)
|
if(!cursor)
|
||||||
@ -511,17 +513,17 @@ int sys_cursor_set(void* cursor)
|
|||||||
(void)SetCursor(HCURSOR_from_ptr(cursor));
|
(void)SetCursor(HCURSOR_from_ptr(cursor));
|
||||||
// return value (previous cursor) is useless.
|
// return value (previous cursor) is useless.
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// destroys the indicated cursor and frees its resources. if it is
|
// destroys the indicated cursor and frees its resources. if it is
|
||||||
// currently the system cursor, the default cursor is restored first.
|
// currently the system cursor, the default cursor is restored first.
|
||||||
int sys_cursor_free(void* cursor)
|
LibError sys_cursor_free(void* cursor)
|
||||||
{
|
{
|
||||||
// bail now to prevent potential confusion below; there's nothing to do.
|
// bail now to prevent potential confusion below; there's nothing to do.
|
||||||
if(!cursor)
|
if(!cursor)
|
||||||
return 0;
|
return ERR_OK;
|
||||||
|
|
||||||
// if the cursor being freed is active, restore the default arrow
|
// if the cursor being freed is active, restore the default arrow
|
||||||
// (just for safety).
|
// (just for safety).
|
||||||
@ -529,7 +531,7 @@ int sys_cursor_free(void* cursor)
|
|||||||
WARN_ERR(sys_cursor_set(0));
|
WARN_ERR(sys_cursor_set(0));
|
||||||
|
|
||||||
BOOL ok = DestroyIcon(HICON_from_ptr(cursor));
|
BOOL ok = DestroyIcon(HICON_from_ptr(cursor));
|
||||||
return ok? 0 : -1;
|
return LibError_from_win32(ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -540,21 +542,21 @@ int sys_cursor_free(void* cursor)
|
|||||||
// OS-specific backend for error_description_r.
|
// OS-specific backend for error_description_r.
|
||||||
// NB: it is expected to be rare that OS return/error codes are actually
|
// NB: it is expected to be rare that OS return/error codes are actually
|
||||||
// seen by user code, but we still translate them for completeness.
|
// seen by user code, but we still translate them for completeness.
|
||||||
int sys_error_description_r(int err, char* buf, size_t max_chars)
|
LibError sys_error_description_r(int err, char* buf, size_t max_chars)
|
||||||
{
|
{
|
||||||
// not in our range (Win32 error numbers are positive)
|
// not in our range (Win32 error numbers are positive)
|
||||||
if(err < 0)
|
if(err < 0)
|
||||||
return -1;
|
return ERR_FAIL;
|
||||||
|
|
||||||
const LPCVOID source = 0; // ignored (we're not using FROM_HMODULE etc.)
|
const LPCVOID source = 0; // ignored (we're not using FROM_HMODULE etc.)
|
||||||
const DWORD lang_id = 0; // look for neutral, then current locale
|
const DWORD lang_id = 0; // look for neutral, then current locale
|
||||||
va_list* args = 0; // we don't care about "inserts"
|
va_list* args = 0; // we don't care about "inserts"
|
||||||
DWORD ret = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, source, (DWORD)err,
|
DWORD chars_output = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, source, (DWORD)err,
|
||||||
lang_id, buf, (DWORD)max_chars, args);
|
lang_id, buf, (DWORD)max_chars, args);
|
||||||
if(!ret)
|
if(!chars_output)
|
||||||
return -1;
|
return ERR_FAIL;
|
||||||
debug_assert(ret < max_chars); // ret = #chars output
|
debug_assert(chars_output < max_chars);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -584,10 +586,10 @@ wchar_t* sys_get_module_filename(void* addr, wchar_t* path)
|
|||||||
// store full path to the current executable.
|
// store full path to the current executable.
|
||||||
// returns 0 or a negative error code.
|
// returns 0 or a negative error code.
|
||||||
// useful for determining installation directory, e.g. for VFS.
|
// useful for determining installation directory, e.g. for VFS.
|
||||||
inline int sys_get_executable_name(char* n_path, size_t buf_size)
|
inline LibError sys_get_executable_name(char* n_path, size_t buf_size)
|
||||||
{
|
{
|
||||||
DWORD nbytes = GetModuleFileName(0, n_path, (DWORD)buf_size);
|
DWORD nbytes = GetModuleFileName(0, n_path, (DWORD)buf_size);
|
||||||
return nbytes? 0 : -1;
|
return nbytes? ERR_OK : ERR_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -609,7 +611,7 @@ static int CALLBACK browse_cb(HWND hWnd, unsigned int msg, LPARAM UNUSED(lParam)
|
|||||||
// stores its full path in the given buffer, which must hold at least
|
// stores its full path in the given buffer, which must hold at least
|
||||||
// PATH_MAX chars.
|
// PATH_MAX chars.
|
||||||
// returns 0 on success or a negative error code.
|
// returns 0 on success or a negative error code.
|
||||||
int sys_pick_directory(char* path, size_t buf_size)
|
LibError sys_pick_directory(char* path, size_t buf_size)
|
||||||
{
|
{
|
||||||
// bring up dialog; set starting directory to current working dir.
|
// bring up dialog; set starting directory to current working dir.
|
||||||
WARN_IF_FALSE(GetCurrentDirectory((DWORD)buf_size, path));
|
WARN_IF_FALSE(GetCurrentDirectory((DWORD)buf_size, path));
|
||||||
@ -631,7 +633,7 @@ int sys_pick_directory(char* path, size_t buf_size)
|
|||||||
p_malloc->Free(pidl);
|
p_malloc->Free(pidl);
|
||||||
p_malloc->Release();
|
p_malloc->Release();
|
||||||
|
|
||||||
return ok? 0 : -1;
|
return LibError_from_win32(ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -643,16 +645,16 @@ int sys_pick_directory(char* path, size_t buf_size)
|
|||||||
// return 0 on success or a negative error code on failure
|
// return 0 on success or a negative error code on failure
|
||||||
// (e.g. if OS is preventing us from running on some CPUs).
|
// (e.g. if OS is preventing us from running on some CPUs).
|
||||||
// called from ia32.cpp get_cpu_count
|
// called from ia32.cpp get_cpu_count
|
||||||
int sys_on_each_cpu(void(*cb)())
|
LibError sys_on_each_cpu(void (*cb)())
|
||||||
{
|
{
|
||||||
const HANDLE hProcess = GetCurrentProcess();
|
const HANDLE hProcess = GetCurrentProcess();
|
||||||
DWORD process_affinity, system_affinity;
|
DWORD process_affinity, system_affinity;
|
||||||
if(!GetProcessAffinityMask(hProcess, &process_affinity, &system_affinity))
|
if(!GetProcessAffinityMask(hProcess, &process_affinity, &system_affinity))
|
||||||
return -1;
|
return ERR_FAIL;
|
||||||
// our affinity != system affinity: OS is limiting the CPUs that
|
// our affinity != system affinity: OS is limiting the CPUs that
|
||||||
// this process can run on. fail (cannot call back for each CPU).
|
// this process can run on. fail (cannot call back for each CPU).
|
||||||
if(process_affinity != system_affinity)
|
if(process_affinity != system_affinity)
|
||||||
return -1;
|
return ERR_FAIL;
|
||||||
|
|
||||||
for(DWORD cpu_bit = 1; cpu_bit != 0 && cpu_bit <= process_affinity; cpu_bit *= 2)
|
for(DWORD cpu_bit = 1; cpu_bit != 0 && cpu_bit <= process_affinity; cpu_bit *= 2)
|
||||||
{
|
{
|
||||||
@ -675,5 +677,5 @@ int sys_on_each_cpu(void(*cb)())
|
|||||||
// restore to original value
|
// restore to original value
|
||||||
SetProcessAffinityMask(hProcess, process_affinity);
|
SetProcessAffinityMask(hProcess, process_affinity);
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
@ -156,7 +156,7 @@ static inline void unlock(void)
|
|||||||
// in case there are unforeseen problems with one of them.
|
// in case there are unforeseen problems with one of them.
|
||||||
// order of preference (due to resolution and speed): TSC, QPC, GTC.
|
// order of preference (due to resolution and speed): TSC, QPC, GTC.
|
||||||
// split out of reset_impl so we can just return when impl is chosen.
|
// split out of reset_impl so we can just return when impl is chosen.
|
||||||
static int choose_impl()
|
static LibError choose_impl()
|
||||||
{
|
{
|
||||||
bool safe;
|
bool safe;
|
||||||
#define SAFETY_OVERRIDE(impl)\
|
#define SAFETY_OVERRIDE(impl)\
|
||||||
@ -195,7 +195,7 @@ static int choose_impl()
|
|||||||
hrt_impl = HRT_TSC;
|
hrt_impl = HRT_TSC;
|
||||||
hrt_nominal_freq = cpu_freq;
|
hrt_nominal_freq = cpu_freq;
|
||||||
hrt_res = (1.0 / hrt_nominal_freq);
|
hrt_res = (1.0 / hrt_nominal_freq);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif // TSC
|
#endif // TSC
|
||||||
@ -254,7 +254,7 @@ static int choose_impl()
|
|||||||
hrt_impl = HRT_QPC;
|
hrt_impl = HRT_QPC;
|
||||||
hrt_nominal_freq = (double)qpc_freq;
|
hrt_nominal_freq = (double)qpc_freq;
|
||||||
hrt_res = (1.0 / hrt_nominal_freq);
|
hrt_res = (1.0 / hrt_nominal_freq);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif // QPC
|
#endif // QPC
|
||||||
@ -275,13 +275,13 @@ static int choose_impl()
|
|||||||
DWORD timer_period; // [hectonanoseconds]
|
DWORD timer_period; // [hectonanoseconds]
|
||||||
if(GetSystemTimeAdjustment(&adj, &timer_period, &adj_disabled))
|
if(GetSystemTimeAdjustment(&adj, &timer_period, &adj_disabled))
|
||||||
hrt_res = (timer_period / 1e7);
|
hrt_res = (timer_period / 1e7);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_warn("no safe timer found!");
|
debug_warn("no safe timer found!");
|
||||||
hrt_impl = HRT_NONE;
|
hrt_impl = HRT_NONE;
|
||||||
hrt_nominal_freq = -1.0;
|
hrt_nominal_freq = -1.0;
|
||||||
return -1;
|
return ERR_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -364,7 +364,7 @@ static double time_lk()
|
|||||||
//
|
//
|
||||||
// don't want to saddle timer module with the problem of initializing
|
// don't want to saddle timer module with the problem of initializing
|
||||||
// us on first call - it wouldn't otherwise need to be thread-safe.
|
// us on first call - it wouldn't otherwise need to be thread-safe.
|
||||||
static int reset_impl_lk()
|
static LibError reset_impl_lk()
|
||||||
{
|
{
|
||||||
HRTImpl old_impl = hrt_impl;
|
HRTImpl old_impl = hrt_impl;
|
||||||
|
|
||||||
@ -390,7 +390,7 @@ static int reset_impl_lk()
|
|||||||
hrt_cal_ticks = ticks_lk();
|
hrt_cal_ticks = ticks_lk();
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -455,23 +455,20 @@ unlock();
|
|||||||
// the timer may jump after doing so.
|
// the timer may jump after doing so.
|
||||||
// call with HRT_DEFAULT, HRT_NONE to re-evaluate implementation choice
|
// call with HRT_DEFAULT, HRT_NONE to re-evaluate implementation choice
|
||||||
// after system info becomes available.
|
// after system info becomes available.
|
||||||
static int hrt_override_impl(HRTOverride ovr, HRTImpl impl)
|
static LibError hrt_override_impl(HRTOverride ovr, HRTImpl impl)
|
||||||
{
|
{
|
||||||
if((ovr != HRT_DISABLE && ovr != HRT_FORCE && ovr != HRT_DEFAULT) ||
|
if((ovr != HRT_DISABLE && ovr != HRT_FORCE && ovr != HRT_DEFAULT) ||
|
||||||
(impl != HRT_TSC && impl != HRT_QPC && impl != HRT_GTC && impl != HRT_NONE))
|
(impl != HRT_TSC && impl != HRT_QPC && impl != HRT_GTC && impl != HRT_NONE))
|
||||||
{
|
CHECK_ERR(ERR_INVALID_PARAM);
|
||||||
debug_warn("invalid ovr or impl param");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
lock();
|
lock();
|
||||||
|
|
||||||
overrides[impl] = ovr;
|
overrides[impl] = ovr;
|
||||||
reset_impl_lk();
|
LibError ret = reset_impl_lk();
|
||||||
|
|
||||||
unlock();
|
unlock();
|
||||||
|
|
||||||
return 0;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -594,34 +591,34 @@ static void* calibration_thread(void* UNUSED(data))
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static inline int init_calibration_thread()
|
static inline LibError init_calibration_thread()
|
||||||
{
|
{
|
||||||
sem_init(&exit_flag, 0, 0);
|
sem_init(&exit_flag, 0, 0);
|
||||||
pthread_create(&thread, 0, calibration_thread, 0);
|
pthread_create(&thread, 0, calibration_thread, 0);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static inline int shutdown_calibration_thread()
|
static inline LibError shutdown_calibration_thread()
|
||||||
{
|
{
|
||||||
sem_post(&exit_flag);
|
sem_post(&exit_flag);
|
||||||
pthread_join(thread, 0);
|
pthread_join(thread, 0);
|
||||||
sem_destroy(&exit_flag);
|
sem_destroy(&exit_flag);
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static int hrt_init()
|
static LibError hrt_init()
|
||||||
{
|
{
|
||||||
// no lock needed - calibration thread hasn't yet been created
|
// no lock needed - calibration thread hasn't yet been created
|
||||||
reset_impl_lk();
|
RETURN_ERR(reset_impl_lk());
|
||||||
return init_calibration_thread();
|
return init_calibration_thread();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int hrt_shutdown()
|
static LibError hrt_shutdown()
|
||||||
{
|
{
|
||||||
// don't take a lock here! race condition:
|
// don't take a lock here! race condition:
|
||||||
// 1) calibration_thread is about to call clock_gettime
|
// 1) calibration_thread is about to call clock_gettime
|
||||||
@ -735,18 +732,18 @@ static i64 time_ns()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int wtime_init()
|
static LibError wtime_init()
|
||||||
{
|
{
|
||||||
hrt_init();
|
hrt_init();
|
||||||
|
|
||||||
// first call latches start times
|
// first call latches start times
|
||||||
time_ns();
|
time_ns();
|
||||||
|
|
||||||
return 0;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int wtime_shutdown()
|
static LibError wtime_shutdown()
|
||||||
{
|
{
|
||||||
return hrt_shutdown();
|
return hrt_shutdown();
|
||||||
}
|
}
|
||||||
|
@ -31,4 +31,6 @@ typedef unsigned int PS_uint;
|
|||||||
# error "check size_t and SIZE_MAX - too small?"
|
# error "check size_t and SIZE_MAX - too small?"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
enum LibError;
|
||||||
|
|
||||||
#endif // #ifndef __TYPES_H__
|
#endif // #ifndef __TYPES_H__
|
||||||
|
Loading…
Reference in New Issue
Block a user