From 494d27bdd4ff9ef246c68e84738c6471f91635a2 Mon Sep 17 00:00:00 2001 From: janwas Date: Thu, 8 Sep 2005 01:47:45 +0000 Subject: [PATCH] moved SAFE_DELETE to lib.h dyn_array supports "wrapping" other types of memory (stopgap measure) texture codecs now load via DynArray (intermediate step to all mem stuff going through res/mem.cpp) simplified tex codecs by doing as many checks as possible in tex_load_mem ("template method"-esque) This was SVN commit r2690. --- source/graphics/Material.cpp | 3 - source/graphics/MaterialManager.cpp | 3 - source/lib/dyn_array.cpp | 38 +++++++++++- source/lib/dyn_array.h | 2 +- source/lib/lib.h | 7 +++ source/lib/res/graphics/tex.cpp | 91 ++++++++++++++++++----------- source/lib/res/graphics/tex.h | 1 - source/lib/res/graphics/tex_bmp.cpp | 47 ++++++++------- source/lib/res/graphics/tex_codec.h | 11 +++- source/lib/res/graphics/tex_dds.cpp | 52 ++++++++--------- source/lib/res/graphics/tex_jpg.cpp | 78 +++++++++++++++---------- source/lib/res/graphics/tex_png.cpp | 37 +++++++----- source/lib/res/graphics/tex_tga.cpp | 53 +++++++++-------- source/lib/res/mem.cpp | 12 ++++ source/lib/res/mem.h | 1 + 15 files changed, 272 insertions(+), 164 deletions(-) diff --git a/source/graphics/Material.cpp b/source/graphics/Material.cpp index 6eb710031e..1b048e694a 100755 --- a/source/graphics/Material.cpp +++ b/source/graphics/Material.cpp @@ -7,9 +7,6 @@ #include "Game.h" #include "Overlay.h" // for CColor -#define SAFE_DELETE(x) \ - if((x)) { delete (x); (x) = NULL; } - CMaterial NullMaterial; CMaterial IdentityMaterial; diff --git a/source/graphics/MaterialManager.cpp b/source/graphics/MaterialManager.cpp index e685266f1d..7703516fdd 100755 --- a/source/graphics/MaterialManager.cpp +++ b/source/graphics/MaterialManager.cpp @@ -3,9 +3,6 @@ #include "XML/Xeromyces.h" #include "MaterialManager.h" -#define SAFE_DELETE(x) \ - if((x)) { delete (x); (x) = NULL; } - static float ClampFloat(float value, float min, float max) { if(value < min) diff --git a/source/lib/dyn_array.cpp b/source/lib/dyn_array.cpp index 596b523103..026b8ef66f 100644 --- a/source/lib/dyn_array.cpp +++ b/source/lib/dyn_array.cpp @@ -16,6 +16,10 @@ static size_t round_up_to_page(size_t size) return round_up(size, page_size); } +// indicates that this DynArray must not be resized or freed +// (e.g. because it merely wraps an existing memory range). +// stored in da->prot to reduce size; doesn't conflict with any PROT_* flags. +const int DA_NOT_OUR_MEM = 0x40000000; static int validate_da(DynArray* da) { @@ -37,7 +41,7 @@ static int validate_da(DynArray* da) return -4; if(pos > cur_size || pos > max_size_pa) return -5; - if(prot & ~(PROT_READ|PROT_WRITE|PROT_EXEC)) + if(prot & ~(PROT_READ|PROT_WRITE|PROT_EXEC|DA_NOT_OUR_MEM)) return -6; return 0; @@ -108,10 +112,28 @@ int da_alloc(DynArray* da, size_t max_size) } +int da_wrap_fixed(DynArray* da, u8* p, size_t size) +{ + da->base = p; + da->max_size_pa = round_up_to_page(size); + da->cur_size = size; + da->pos = 0; + da->prot = PROT_READ|PROT_WRITE|DA_NOT_OUR_MEM; + CHECK_DA(da); + return 0; +} + + int da_free(DynArray* da) { CHECK_DA(da); + if(da->prot & DA_NOT_OUR_MEM) + { + debug_warn("da_free: da is marked DA_NOT_OUR_MEM, must not be altered"); + return -1; + } + // latch pointer; wipe out the DynArray for safety // (must be done here because mem_release may fail) u8* p = da->base; @@ -127,6 +149,12 @@ int da_set_size(DynArray* da, size_t new_size) { CHECK_DA(da); + if(da->prot & DA_NOT_OUR_MEM) + { + debug_warn("da_set_size: da is marked DA_NOT_OUR_MEM, must not be altered"); + return -1; + } + // determine how much to add/remove const size_t cur_size_pa = round_up_to_page(da->cur_size); const size_t new_size_pa = round_up_to_page(new_size); @@ -154,6 +182,14 @@ int da_set_prot(DynArray* da, int prot) { CHECK_DA(da); + // somewhat more subtle: POSIX mprotect requires the memory have been + // mmap-ed, which it probably wasn't here. + if(da->prot & DA_NOT_OUR_MEM) + { + debug_warn("da_set_prot: da is marked DA_NOT_OUR_MEM, must not be altered"); + return -1; + } + da->prot = prot; CHECK_ERR(mem_protect(da->base, da->cur_size, prot)); diff --git a/source/lib/dyn_array.h b/source/lib/dyn_array.h index faf917590a..698f5f1255 100644 --- a/source/lib/dyn_array.h +++ b/source/lib/dyn_array.h @@ -18,7 +18,7 @@ extern int da_set_size(DynArray* da, size_t new_size); extern int da_set_prot(DynArray* da, int prot); - +extern int da_wrap_fixed(DynArray* da, u8* p, size_t size); extern int da_read(DynArray* da, void* data_dst, size_t size); diff --git a/source/lib/lib.h b/source/lib/lib.h index 210b6e0530..4f85351a55 100755 --- a/source/lib/lib.h +++ b/source/lib/lib.h @@ -183,6 +183,13 @@ STMT(\ ptr = 0;\ } +#define SAFE_DELETE(p) STMT(\ + if((p))\ + {\ + delete (p);\ + (p) = 0;\ + }\ +) enum LibError diff --git a/source/lib/res/graphics/tex.cpp b/source/lib/res/graphics/tex.cpp index 30172d5f2e..36d8752d12 100755 --- a/source/lib/res/graphics/tex.cpp +++ b/source/lib/res/graphics/tex.cpp @@ -179,6 +179,8 @@ static const TexCodecVTbl* codecs[MAX_CODECS]; // can handle the given format, this is not a problem. int tex_codec_register(const TexCodecVTbl* c) { + debug_assert(c != 0 && "tex_codec_register(0) - why?"); + for(int i = 0; i < MAX_CODECS; i++) { // slot available @@ -233,13 +235,6 @@ int tex_codec_alloc_rows(const u8* data, size_t h, size_t pitch, } -int tex_codec_set_orientation(Tex* t, uint file_orientation) -{ - uint transforms = file_orientation ^ global_orientation; - return plain_transform(t, transforms); -} - - int tex_codec_write(Tex* t, uint transforms, const void* hdr, size_t hdr_size, DynArray* da) { RETURN_ERR(tex_transform(t, transforms)); @@ -274,37 +269,67 @@ void tex_set_global_orientation(int o) } -int tex_load_mem(Handle hm, const char* fn, Tex* t) +int tex_load_mem(Handle hm, Tex* t) { - size_t size; - void* _p = mem_get_ptr(hm, &size); +#define ERR_TOO_SHORT -4 - // guarantee is_fmt routines 4 header bytes - if(size < 4) - return ERR_CORRUPTED; - t->hm = hm; + u8* file; size_t file_size; + CHECK_ERR(mem_get(hm, &file, &file_size)); - // more convenient to pass loaders u8 - less casting. - // not const, because image may have to be flipped (in-place). - u8* p = (u8*)_p; - - // find codec that understands the data, and decode - for(int i = 0; i < MAX_CODECS; i++) + // find codec that recognizes the header's magic field + const TexCodecVTbl* c = 0; + // .. we guarantee at least 4 bytes for is_hdr to look at + if(file_size < 4) + return ERR_TOO_SHORT; + for(uint i = 0; i < MAX_CODECS; i++) { - // MAX_CODECS isn't a tight bound and we have hit a 0 entry - if(!codecs[i]) - continue; - const char* err_msg = 0; - int err = codecs[i]->decode(p, size, t, &err_msg); - if(err == TEX_CODEC_CANNOT_HANDLE) - continue; - if(err == 0) - return 0; - debug_printf("tex_load_mem (%s): %s: %s", codecs[i]->name, fn, err_msg); - CHECK_ERR(err); + c = codecs[i]; // may be 0 (e.g. if MAX_CODECS != num codecs) + if(c && c->is_hdr(file)) + goto found_codec; + } + return ERR_UNKNOWN_FORMAT; +found_codec: + + // make sure enough of the file has been read + const size_t min_hdr_size = c->hdr_size(0); + if(file_size < min_hdr_size) + return ERR_TOO_SHORT; + const size_t hdr_size = c->hdr_size(file); + if(file_size < hdr_size) + return ERR_TOO_SHORT; + + + DynArray da; + CHECK_ERR(da_wrap_fixed(&da, file, file_size)); + t->hm = hm; + t->ofs = hdr_size; + + const char* err_msg = 0; + int err = c->decode(&da, t, &err_msg); + if(err < 0) + { + debug_printf("tex_load_mem (%s): %s", c->name, err_msg); + debug_warn("tex_load_mem failed"); + return err; } - return ERR_UNKNOWN_FORMAT; + // sanity checks + if(!t->w || !t->h || t->bpp > 32) + return ERR_TEX_FMT_INVALID; + // TODO: need to compare against the new t->hm (file may be compressed, cannot use file_size) + //if(mem_size < t->ofs + tex_img_size(t)) + // return ERR_TOO_SHORT; + + // flip image to global orientation + uint orientation = t->flags & TEX_ORIENTATION; + // .. but only if it knows which way around it is (DDS doesn't) + if(orientation) + { + uint transforms = orientation ^ global_orientation; + WARN_ERR(plain_transform(t, transforms)); + } + + return 0; } @@ -314,7 +339,7 @@ int tex_load(const char* fn, Tex* t) void* p; size_t size; // unused Handle hm = vfs_load(fn, p, size); RETURN_ERR(hm); // (need handle below; can't test return value directly) - int ret = tex_load_mem(hm, fn, t); + int ret = tex_load_mem(hm, t); // do not free hm! it either still holds the image data (i.e. texture // wasn't compressed) or was replaced by a new buffer for the image data. if(ret < 0) diff --git a/source/lib/res/graphics/tex.h b/source/lib/res/graphics/tex.h index 181436d5a2..7c2367a8db 100755 --- a/source/lib/res/graphics/tex.h +++ b/source/lib/res/graphics/tex.h @@ -68,7 +68,6 @@ struct Tex extern int tex_load(const char* fn, Tex* t); -extern int tex_load_mem(Handle hm, const char* fn, Tex* t); extern int tex_free(Tex* t); extern u8* tex_get_data(const Tex* t); diff --git a/source/lib/res/graphics/tex_bmp.cpp b/source/lib/res/graphics/tex_bmp.cpp index 99185c68a0..ce664d601d 100644 --- a/source/lib/res/graphics/tex_bmp.cpp +++ b/source/lib/res/graphics/tex_bmp.cpp @@ -40,23 +40,33 @@ static int bmp_transform(Tex* UNUSED(t), uint UNUSED(transforms)) } -// requirements: uncompressed, direct colour, bottom up -static int bmp_decode(u8* file, size_t file_size, Tex* t, const char** perr_msg) +static bool bmp_is_hdr(const u8* file) { // check header signature (bfType == "BM"?). // we compare single bytes to be endian-safe. - if(file[0] != 'B' || file[1] != 'M') - return TEX_CODEC_CANNOT_HANDLE; + return (file[0] == 'B' && file[1] == 'M'); +} + +static size_t bmp_hdr_size(const u8* file) +{ const size_t hdr_size = sizeof(BmpHeader); - - // make sure we can access all header fields - if(file_size < hdr_size) + if(file) { - *perr_msg = "header not completely read"; -fail: - return ERR_CORRUPTED; + BmpHeader* hdr = (BmpHeader*)file; + const u32 ofs = read_le32(&hdr->bfOffBits); + debug_assert(ofs >= hdr_size && "bmp_hdr_size invalid"); + return ofs; } + return hdr_size; +} + + +// requirements: uncompressed, direct colour, bottom up +static int bmp_decode(DynArray* da, Tex* t, const char** perr_msg) +{ + u8* file = da->base; + size_t file_size = da->cur_size; const BmpHeader* hdr = (const BmpHeader*)file; const long w = (long)read_le32(&hdr->biWidth); @@ -65,11 +75,12 @@ fail: const u32 compress = read_le32(&hdr->biCompression); const u32 ofs = read_le32(&hdr->bfOffBits); - const int orientation = (h_ < 0)? TEX_TOP_DOWN : TEX_BOTTOM_UP; const long h = abs(h_); - const size_t img_size = tex_img_size(t); - int flags = TEX_BGR; + int flags = 0; + flags |= (h_ < 0)? TEX_TOP_DOWN : TEX_BOTTOM_UP; + if(bpp > 16) + flags |= TEX_BGR; if(bpp == 32) flags |= TEX_ALPHA; @@ -77,24 +88,16 @@ fail: const char* err = 0; if(compress != BI_RGB) err = "compressed"; - if(bpp != 24 && bpp != 32) - err = "invalid bpp (not direct colour)"; - if(file_size < ofs+img_size) - err = "image not completely read"; if(err) { *perr_msg = err; - goto fail; + return ERR_CORRUPTED; } - t->ofs = ofs; t->w = w; t->h = h; t->bpp = bpp; t->flags = flags; - - tex_codec_set_orientation(t, orientation); - return 0; } diff --git a/source/lib/res/graphics/tex_codec.h b/source/lib/res/graphics/tex_codec.h index 92acf37e01..749bfbfae6 100644 --- a/source/lib/res/graphics/tex_codec.h +++ b/source/lib/res/graphics/tex_codec.h @@ -8,12 +8,14 @@ struct TexCodecVTbl { + // 'template method' to increase code reuse and simplify writing new codecs + // pointers aren't const, because the textures // may have to be flipped in-place - see "texture orientation". // size is guaranteed to be >= 4. // (usually enough to compare the header's "magic" field; // anyway, no legitimate file will be smaller) - int (*decode)(u8* data, size_t data_size, Tex* t, const char** perr_msg); + int (*decode)(DynArray* da, Tex* t, const char** perr_msg); // rationale: some codecs cannot calculate the output size beforehand // (e.g. PNG output via libpng); we therefore require each one to @@ -22,12 +24,17 @@ struct TexCodecVTbl int (*transform)(Tex* t, uint transforms); + // only guaranteed 4 bytes! + bool (*is_hdr)(const u8* file); + + size_t (*hdr_size)(const u8* file); + const char* name; }; #define TEX_CODEC_REGISTER(name)\ - static const TexCodecVTbl vtbl = { name##_decode, name##_encode, name##_transform, #name};\ + static const TexCodecVTbl vtbl = { name##_decode, name##_encode, name##_transform, name##_is_hdr, name##_hdr_size, #name};\ static int dummy = tex_codec_register(&vtbl); diff --git a/source/lib/res/graphics/tex_dds.cpp b/source/lib/res/graphics/tex_dds.cpp index 15b9d4d3e3..da1d69e8f7 100644 --- a/source/lib/res/graphics/tex_dds.cpp +++ b/source/lib/res/graphics/tex_dds.cpp @@ -205,31 +205,32 @@ static int dds_transform(Tex* t, uint transforms) } -static int dds_decode(u8* file, size_t file_size, Tex* t, const char** perr_msg) +static bool dds_is_hdr(const u8* file) { - if(*(u32*)file != FOURCC('D','D','S',' ')) - return TEX_CODEC_CANNOT_HANDLE; + return *(u32*)file == FOURCC('D','D','S',' '); +} - const DDSURFACEDESC2* surf = (const DDSURFACEDESC2*)(file+4); - const size_t hdr_size = 4+sizeof(DDSURFACEDESC2); - // make sure we can access all header fields - if(file_size < hdr_size) - { - *perr_msg = "header not completely read"; -fail: - return ERR_CORRUPTED; - } +static size_t dds_hdr_size(const u8* UNUSED(file)) +{ + return 4+sizeof(DDSURFACEDESC2); +} - const u32 sd_size = read_le32(&surf->dwSize); - const u32 sd_flags = read_le32(&surf->dwFlags); - const u32 h = read_le32(&surf->dwHeight); - const u32 w = read_le32(&surf->dwWidth); - const u32 img_size = read_le32(&surf->dwLinearSize); - u32 mipmaps = read_le32(&surf->dwMipMapCount); - const u32 pf_size = read_le32(&surf->ddpfPixelFormat.dwSize); - const u32 pf_flags = read_le32(&surf->ddpfPixelFormat.dwFlags); - const u32 fourcc = surf->ddpfPixelFormat.dwFourCC; + +static int dds_decode(DynArray* da, Tex* t, const char** perr_msg) +{ + u8* file = da->base; + size_t file_size = da->cur_size; + + const DDSURFACEDESC2* hdr = (const DDSURFACEDESC2*)(file+4); + const u32 sd_size = read_le32(&hdr->dwSize); + const u32 sd_flags = read_le32(&hdr->dwFlags); + const u32 h = read_le32(&hdr->dwHeight); + const u32 w = read_le32(&hdr->dwWidth); + u32 mipmaps = read_le32(&hdr->dwMipMapCount); + const u32 pf_size = read_le32(&hdr->ddpfPixelFormat.dwSize); + const u32 pf_flags = read_le32(&hdr->ddpfPixelFormat.dwFlags); + const u32 fourcc = hdr->ddpfPixelFormat.dwFourCC; // compared against FOURCC, which takes care of endian conversion. // we'll use these fields; make sure they're present below. @@ -279,13 +280,9 @@ fail: // sanity checks const char* err = 0; - if(file_size < hdr_size + img_size) - err = "file size too small"; if(w % 4 || h % 4) err = "image dimensions not padded to S3TC block size"; - if(!w || !h) - err = "width or height = 0"; - if(bpp == 0) + if(flags & TEX_DXT == 0) err = "invalid pixel format (not DXT{1,3,5})"; if((sd_flags & sd_req_flags) != sd_req_flags) err = "missing one or more required fields (w, h, pixel format)"; @@ -296,10 +293,9 @@ fail: if(err) { *perr_msg = err; - goto fail; + return ERR_CORRUPTED; } - t->ofs = hdr_size; t->w = w; t->h = h; t->bpp = bpp; diff --git a/source/lib/res/graphics/tex_jpg.cpp b/source/lib/res/graphics/tex_jpg.cpp index 4fb8a688f0..9f1c88d4ab 100644 --- a/source/lib/res/graphics/tex_jpg.cpp +++ b/source/lib/res/graphics/tex_jpg.cpp @@ -32,13 +32,9 @@ cassert(sizeof(JOCTET) == 1 && CHAR_BIT == 8); typedef struct { struct jpeg_source_mgr pub; /* public fields */ - - JOCTET* buf; - size_t size; /* total size (bytes) */ - size_t pos; /* offset (bytes) to new data */ + DynArray* da; } SrcMgr; - typedef SrcMgr* SrcPtr; @@ -83,7 +79,6 @@ METHODDEF(boolean) src_fill_buffer(j_decompress_ptr cinfo) WARNMS(cinfo, JWRN_JPEG_EOF); - src->pub.next_input_byte = eoi; src->pub.bytes_in_buffer = 2; return TRUE; @@ -150,10 +145,13 @@ METHODDEF(void) src_term(j_decompress_ptr UNUSED(cinfo)) * The caller is responsible for freeing it after finishing decompression. */ -GLOBAL(void) src_prepare(j_decompress_ptr cinfo, void* p, size_t size) +GLOBAL(void) src_prepare(j_decompress_ptr cinfo, DynArray* da) { SrcPtr src; + const u8* p = da->base; + const size_t size = da->cur_size; + /* Treat 0-length buffer as fatal error */ if(size == 0) ERREXIT(cinfo, JERR_INPUT_EMPTY); @@ -196,12 +194,14 @@ GLOBAL(void) src_prepare(j_decompress_ptr cinfo, void* p, size_t size) /* Expanded data destination object for memory output */ typedef struct { struct jpeg_destination_mgr pub; /* public fields */ - DynArray* da; } DstMgr; typedef DstMgr* DstPtr; +// this affects how often dst_empty_output_buffer is called (which +// efficiently expands the DynArray) and how much tail memory we waste +// (not an issue because it is freed immediately after compression). #define OUTPUT_BUF_SIZE 64*KiB /* choose an efficiently writeable size */ // note: can't call dst_empty_output_buffer from dst_init or vice versa @@ -275,15 +275,15 @@ METHODDEF(void) dst_term(j_compress_ptr cinfo) /* -* Prepare for output to a stdio stream. -* The caller must have already opened the stream, and is responsible -* for closing it after finishing compression. +* Prepare for output to a buffer. +* The caller is responsible for allocating and writing out to disk after +* compression is complete. */ GLOBAL(void) dst_prepare(j_compress_ptr cinfo, DynArray* da) { /* The destination object is made permanent so that multiple JPEG images - * can be written to the same file without re-executing jpeg_stdio_dest. + * can be written to the same file without re-executing dst_prepare. * This makes it dangerous to use this manager and a different destination * manager serially with the same JPEG object, because their private object * sizes may be different. Caveat programmer. @@ -403,11 +403,11 @@ static int jpg_transform(Tex* UNUSED(t), uint UNUSED(transforms)) // due to less copying. -static int jpg_decode_impl(Tex* t, u8* file, size_t file_size, +static int jpg_decode_impl(DynArray* da, jpeg_decompress_struct* cinfo, - Handle& img_hm, RowArray& rows, const char** perr_msg) + Handle& img_hm, RowArray& rows, Tex* t, const char** perr_msg) { - src_prepare(cinfo, file, file_size); + src_prepare(cinfo, da); // ignore return value since: // - suspension is not possible with the mem data source @@ -473,12 +473,10 @@ static int jpg_decode_impl(Tex* t, u8* file, size_t file_size, // buffer and replace it with the decoded-image memory handle. mem_free_h(t->hm); // must come after jpeg_finish_decompress t->hm = img_hm; - t->ofs = 0; // jpeg returns decoded image data; no header t->w = w; t->h = h; t->bpp = bpp; t->flags = flags; - return 0; } @@ -534,14 +532,28 @@ static int jpg_encode_impl(Tex* t, } -static int jpg_decode(u8* file, size_t file_size, Tex* t, const char** perr_msg) + +static bool jpg_is_hdr(const u8* file) { // JFIF requires SOI marker at start of stream. // we compare single bytes to be endian-safe. - if(file[0] != 0xff || file[1] == 0xd8) - return TEX_CODEC_CANNOT_HANDLE; + return (file[0] == 0xff && file[1] == 0xd8); +} + + +static size_t jpg_hdr_size(const u8* UNUSED(file)) +{ + return 0; // libjpg returns decoded image data; no header +} + + +static int jpg_decode(DynArray* da, Tex* t, const char** perr_msg) +{ + u8* const file = da->base; + const size_t file_size = da->cur_size; + + int err; - int err = -1; // freed when ret is reached: // .. contains the JPEG decompression parameters and pointers to // working space (allocated as needed by the JPEG library). @@ -555,15 +567,13 @@ static int jpg_decode(u8* file, size_t file_size, Tex* t, const char** perr_msg) JpgErrorMgr jerr((j_common_ptr)&cinfo); if(setjmp(jerr.call_site)) { -fail: - // libjpg longjmp-ed here after an error, or code below failed. - mem_free_h(img_hm); - goto ret; + err = -1; + goto fail; } jpeg_create_decompress(&cinfo); - err = jpg_decode_impl(t, file, file_size, &cinfo, img_hm, rows, perr_msg); + err = jpg_decode_impl(da, &cinfo, img_hm, rows, t, perr_msg); if(err < 0) goto fail; @@ -571,6 +581,10 @@ ret: jpeg_destroy_decompress(&cinfo); // releases a "good deal" of memory free(rows); return err; + +fail: + mem_free_h(img_hm); + goto ret; } @@ -580,7 +594,8 @@ static int jpg_encode(const char* ext, Tex* t, DynArray* da, const char** perr_m if(stricmp(ext, "jpg") && stricmp(ext, "jpeg")) return TEX_CODEC_CANNOT_HANDLE; - int err = -1; + int err; + // freed when ret is reached: // .. contains the JPEG compression parameters and pointers to // working space (allocated as needed by the JPEG library). @@ -591,9 +606,8 @@ static int jpg_encode(const char* ext, Tex* t, DynArray* da, const char** perr_m JpgErrorMgr jerr((j_common_ptr)&cinfo); if(setjmp(jerr.call_site)) { -fail: - // either JPEG has raised an error, or code below failed. - goto ret; + err = -1; + goto fail; } jpeg_create_compress(&cinfo); @@ -606,6 +620,10 @@ ret: jpeg_destroy_compress(&cinfo); // releases a "good deal" of memory free(rows); return err; + +fail: + // currently no extra cleanup needed + goto ret; } TEX_CODEC_REGISTER(jpg); diff --git a/source/lib/res/graphics/tex_png.cpp b/source/lib/res/graphics/tex_png.cpp index 6c4c5469ab..dcbeb19c8f 100644 --- a/source/lib/res/graphics/tex_png.cpp +++ b/source/lib/res/graphics/tex_png.cpp @@ -76,17 +76,11 @@ static int png_transform(Tex* UNUSED(t), uint UNUSED(transforms)) // split out of png_decode to simplify resource cleanup and avoid // "dtor / setjmp interaction" warning. -static int png_decode_impl(Tex* t, u8* file, size_t file_size, +static int png_decode_impl(DynArray* da, png_structp png_ptr, png_infop info_ptr, - Handle& img_hm, RowArray& rows, const char** perr_msg) + Handle& img_hm, RowArray& rows, Tex* t, const char** perr_msg) { -DynArray da; -da.base = file; -da.max_size_pa = round_up(file_size, 4096); -da.cur_size = file_size; -da.pos = 0; -da.prot = PROT_READ|PROT_WRITE; - png_set_read_fn(png_ptr, &da, io_read); + png_set_read_fn(png_ptr, da, io_read); // read header and determine format png_read_info(png_ptr, info_ptr); @@ -125,14 +119,13 @@ da.prot = PROT_READ|PROT_WRITE; png_read_end(png_ptr, info_ptr); // success; make sure all data was consumed. - debug_assert(da.base == file && da.cur_size == file_size && da.pos == da.cur_size); + debug_assert(da->pos == da->cur_size); // store image info // .. transparently switch handles - free the old (compressed) // buffer and replace it with the decoded-image memory handle. mem_free_h(t->hm); // must come after png_read_end t->hm = img_hm; - t->ofs = 0; // libpng returns decoded image data; no header t->w = w; t->h = h; t->bpp = bpp; @@ -188,13 +181,25 @@ static int png_encode_impl(Tex* t, -// limitation: palette images aren't supported -static int png_decode(u8* file, size_t file_size, Tex* t, const char** perr_msg) +static bool png_is_hdr(const u8* file) { // don't use png_sig_cmp, so we don't pull in libpng for // this check alone (it might not actually be used). - if(*(u32*)file != FOURCC('\x89','P','N','G')) - return TEX_CODEC_CANNOT_HANDLE; + return *(u32*)file == FOURCC('\x89','P','N','G'); +} + + +static size_t png_hdr_size(const u8* UNUSED(file)) +{ + return 0; // libpng returns decoded image data; no header +} + + +// limitation: palette images aren't supported +static int png_decode(DynArray* da, Tex* t, const char** perr_msg) +{ + u8* const file = da->base; + const size_t file_size = da->cur_size; int err = -1; // freed when ret is reached: @@ -222,7 +227,7 @@ fail: goto ret; } - err = png_decode_impl(t, file, file_size, png_ptr, info_ptr, img_hm, rows, perr_msg); + err = png_decode_impl(da, png_ptr, info_ptr, img_hm, rows, t, perr_msg); if(err < 0) goto fail; diff --git a/source/lib/res/graphics/tex_tga.cpp b/source/lib/res/graphics/tex_tga.cpp index ba11a2ab13..bd7a7bc9d1 100644 --- a/source/lib/res/graphics/tex_tga.cpp +++ b/source/lib/res/graphics/tex_tga.cpp @@ -47,8 +47,7 @@ static int tga_transform(Tex* UNUSED(t), uint UNUSED(transforms)) } -// requirements: uncompressed, direct colour, bottom up -static int tga_decode(u8* file, size_t file_size, Tex* t, const char** perr_msg) +static bool tga_is_hdr(const u8* file) { TgaHeader* hdr = (TgaHeader*)file; @@ -56,32 +55,46 @@ static int tga_decode(u8* file, size_t file_size, Tex* t, const char** perr_msg) // we can only check if the first 4 bytes are valid // .. not direct colour if(hdr->colour_map_type != 0) - return TEX_CODEC_CANNOT_HANDLE; + return false; // .. wrong colour type (not uncompressed greyscale or RGB) if(hdr->img_type != TGA_TRUE_COLOUR && hdr->img_type != TGA_GREY) - return TEX_CODEC_CANNOT_HANDLE; + return false; - // make sure we can access all header fields - const size_t hdr_size = sizeof(TgaHeader) + hdr->img_id_len; - if(file_size < hdr_size) + // note: we can't check img_id_len or colour_map[0] - they are + // undefined and may assume any value. + + return true; +} + + +static size_t tga_hdr_size(const u8* file) +{ + size_t hdr_size = sizeof(TgaHeader); + if(file) { - *perr_msg = "header not completely read"; -fail: - return ERR_CORRUPTED; + TgaHeader* hdr = (TgaHeader*)file; + hdr_size += hdr->img_id_len; } + return hdr_size; +} + +// requirements: uncompressed, direct colour, bottom up +static int tga_decode(DynArray* da, Tex* t, const char** perr_msg) +{ + u8* file = da->base; + size_t file_size = da->cur_size; + + TgaHeader* hdr = (TgaHeader*)file; const u8 type = hdr->img_type; const uint w = read_le16(&hdr->w); const uint h = read_le16(&hdr->h); const uint bpp = hdr->bpp; const u8 desc = hdr->img_desc; - const u8 alpha_bits = desc & 0x0f; - const int orientation = (desc & TGA_TOP_DOWN)? TEX_TOP_DOWN : TEX_BOTTOM_UP; - const size_t img_size = tex_img_size(t); - int flags = 0; - if(alpha_bits != 0) + flags |= (desc & TGA_TOP_DOWN)? TEX_TOP_DOWN : TEX_BOTTOM_UP; + if(desc & 0x0f != 0) // alpha bits flags |= TEX_ALPHA; if(bpp == 8) flags |= TEX_GREY; @@ -94,24 +107,16 @@ fail: // we're not going to bother converting it. if(desc & TGA_RIGHT_TO_LEFT) err = "image is stored right-to-left"; - if(bpp != 8 && bpp != 16 && bpp != 24 && bpp != 32) - err = "invalid bpp"; - if(file_size < hdr_size + img_size) - err = "size < image size"; if(err) { *perr_msg = err; - goto fail; + return ERR_CORRUPTED; } - t->ofs = hdr_size; t->w = w; t->h = h; t->bpp = bpp; t->flags = flags; - - tex_codec_set_orientation(t, orientation); - return 0; } diff --git a/source/lib/res/mem.cpp b/source/lib/res/mem.cpp index 464f4262ae..fbaf43d520 100755 --- a/source/lib/res/mem.cpp +++ b/source/lib/res/mem.cpp @@ -390,6 +390,18 @@ void* mem_get_ptr(Handle hm, size_t* user_size /* = 0 */) } +int mem_get(Handle hm, u8** pp, size_t* psize) +{ + H_DEREF(hm, Mem, m); + if(pp) + *pp = (u8*)m->p; + if(psize) + *psize = m->size; + // leave hm locked + return 0; +} + + /* ssize_t mem_size(void* p) { diff --git a/source/lib/res/mem.h b/source/lib/res/mem.h index 9fd64ed8ef..6c5f97ac3a 100755 --- a/source/lib/res/mem.h +++ b/source/lib/res/mem.h @@ -28,6 +28,7 @@ extern int mem_free_h(Handle& hm); // returns 0 if the handle is invalid extern void* mem_get_ptr(Handle h, size_t* size = 0); +extern int mem_get(Handle hm, u8** pp, size_t* psize); extern void mem_shutdown(void);