/* \brief check if file is a MikuMikuDance PMD file */ int _glhckFormatPMD(const char *file) { FILE *f; mmd_data *mmd = NULL; CALL(0, "%s", file); if (!(f = fopen(file, "rb"))) goto read_fail; if (!(mmd = mmd_new(f))) goto fail; if (mmd_read_header(mmd) != 0) goto fail; /* close file */ NULLDO(mmd_free, mmd); NULLDO(fclose, f); return RETURN_OK; read_fail: DEBUG(GLHCK_DBG_ERROR, "Failed to open: %s", file); fail: IFDO(mmd_free, mmd); IFDO(fclose, f); return RETURN_FAIL; }
/* \brief free camera */ GLHCKAPI unsigned int glhckCameraFree(glhckCamera *object) { if (!glhckInitialized()) return 0; CALL(FREE_CALL_PRIO(object), "%p", object); assert(object); /* there is still references to this object alive */ if (--object->refCounter != 0) goto success; /* remove camera from global state */ if (GLHCKRD()->camera == object) GLHCKRD()->camera = NULL; /* free camera's object */ NULLDO(glhckObjectFree, object->object); /* remove camera from world */ _glhckWorldRemove(camera, object, glhckCamera*); /* free */ NULLDO(_glhckFree, object); success: RET(FREE_RET_PRIO(object), "%u", object?object->refCounter:0); return object?object->refCounter:0; }
static int processBonesAndAnimations(glhckObject *object, const struct aiScene *sc) { unsigned int i, b, numBones = 0, oldNumBones; const struct aiMesh *mesh; glhckObject *child; glhckBone **bones = NULL; glhckSkinBone **skinBones = NULL; /* import bones */ if (!(bones = _glhckCalloc(ASSIMP_BONES_MAX, sizeof(glhckBone*)))) goto fail; if (!(skinBones = _glhckCalloc(ASSIMP_BONES_MAX, sizeof(glhckSkinBone*)))) goto fail; for (i = 0; i != sc->mNumMeshes; ++i) { mesh = sc->mMeshes[i]; /* FIXME: UGLY */ char pointer[16]; snprintf(pointer, sizeof(pointer), "%p", mesh); if (!(child = findObject(object, pointer))) continue; if (mesh->mNumBones) { oldNumBones = numBones; processBones(sc->mRootNode, sc->mRootNode, mesh, bones, skinBones, &numBones); if (numBones) glhckObjectInsertSkinBones(child, skinBones+oldNumBones, numBones-oldNumBones); for (b = oldNumBones; b < numBones; ++b) glhckSkinBoneFree(skinBones[b]); } } /* we don't need skin bones anymore */ NULLDO(_glhckFree, skinBones); /* store all bones in root object */ if (numBones) glhckObjectInsertBones(object, bones, numBones); for (b = 0; b < numBones; ++b) glhckBoneFree(bones[b]); NULLDO(_glhckFree, bones); /* import animations */ if (sc->mNumAnimations && processAnimations(object, sc) != RETURN_OK) goto fail; return RETURN_OK; fail: if (bones) { for (i = 0; i < ASSIMP_BONES_MAX; ++i) if (bones[i]) glhckBoneFree(bones[i]); _glhckFree(bones); } if (skinBones) { for (i = 0; i < ASSIMP_BONES_MAX; ++i) if (skinBones[i]) glhckSkinBoneFree(skinBones[i]); _glhckFree(skinBones); } return RETURN_FAIL; }
/* \brief check if file is TGA */ int _glhckFormatTGA(const char *file) { FILE *f; void *data = NULL; char isTga = 0; size_t size; tga_header *header; tga_footer *footer; CALL(0, "%s", file); if (!(f = fopen(file, "rb"))) goto read_fail; fseek(f, 0L, SEEK_END); if ((size = ftell(f)) < sizeof(tga_header) + sizeof(tga_footer)) goto fail; if (!(data = _glhckMalloc(size))) goto out_of_memory; fseek(f, 0L, SEEK_SET); if (fread(data, 1, size, f) != size) goto fail; /* we don't need the file anymore */ NULLDO(fclose, f); header = (tga_header*)data; footer = (tga_footer*)((char*)data+size-sizeof(tga_footer)); /* is TGA v2.0? (stop storing the headers at EOF please) */ if (!memcmp(footer->signature, TGA_SIGNATURE, sizeof(footer->signature))) isTga = 1; else if ((header->bpp == 32) || (header->bpp == 24) || (header->bpp == 8)) isTga = 1; NULLDO(_glhckFree, data); RET(0, "%d", isTga?RETURN_OK:RETURN_FAIL); return isTga?RETURN_OK:RETURN_FAIL; read_fail: DEBUG(GLHCK_DBG_ERROR, "Failed to open: %s", file); goto fail; out_of_memory: DEBUG(GLHCK_DBG_ERROR, "Out of memory, won't load file: %s", file); fail: IFDO(fclose, f); IFDO(_glhckFree, data); return RETURN_FAIL; }
/* \brief save texture to file in TGA format */ GLHCKAPI int glhckTextureSave(glhckTexture *object, const char *path) { FILE *f = NULL; CALL(0, "%p, %s", object, path); assert(object); DEBUG(GLHCK_DBG_CRAP, "\2Save \3%d\5x\3%d\5 [\4%s\5]", object->width, object->height, path); /* TODO: Render to FBO to get the image * Or use glGetTexImage if it's available (not in GLES) */ #if 0 /* open for read */ if (!(f = fopen(path, "wb"))) goto fail; /* dump raw data */ fwrite(object->data, 1, object->size, f); NULLDO(fclose, f); #endif RET(0, "%d", RETURN_OK); return RETURN_OK; //fail: IFDO(fclose, f); RET(0, "%d", RETURN_FAIL); return RETURN_FAIL; }
/* \brief free texture */ GLHCKAPI unsigned int glhckTextureFree(glhckTexture *object) { unsigned int i; if (!glhckInitialized()) return 0; CALL(FREE_CALL_PRIO(object), "%p", object); assert(object); /* there is still references to this object alive */ if (--object->refCounter != 0) goto success; DEBUG(GLHCK_DBG_CRAP, "FREE(%p) %dx%dx%d", object, object->internalWidth, object->internalHeight, object->internalDepth); /* unbind from active slot */ for (i = 0; i != GLHCK_MAX_ACTIVE_TEXTURE; ++i) { if (GLHCKRD()->texture[i][object->target] == object) glhckTextureUnbind(object->target); } /* delete texture if there is one */ if (object->object) GLHCKRA()->textureDelete(1, &object->object); /* free */ IFDO(_glhckFree, object->file); /* remove from world */ _glhckWorldRemove(texture, object, glhckTexture*); /* free */ NULLDO(_glhckFree, object); success: RET(FREE_RET_PRIO(object), "%u", object?object->refCounter:0); return object?object->refCounter:0; }
/* \brief import Assimp file */ int _glhckImportAssimp(glhckObject *object, const char *file, const glhckImportModelParameters *params, glhckGeometryIndexType itype, glhckGeometryVertexType vtype) { const struct aiScene *scene; glhckObject *first = NULL; unsigned int aflags; CALL(0, "%p, %s, %p", object, file, params); /* import the model using assimp * TODO: make import hints tunable? * Needs changes to import protocol! */ aflags = aiProcessPreset_TargetRealtime_Fast | aiProcess_OptimizeGraph; if (!params->animated && params->flatten) aflags |= aiProcess_PreTransformVertices; scene = aiImportFile(file, aflags); if (!scene) goto assimp_fail; /* mark ourself as special root object. * this makes most functions called on root object echo to children */ object->flags |= GLHCK_OBJECT_ROOT; /* this is going to be the first object in mesh, * the object returned by this importer is just invisible root object. */ if (!(first = glhckObjectNew())) goto fail; glhckObjectAddChild(object, first); glhckObjectFree(first); /* process the model */ if (processModel(file, object, first, scene, scene->mRootNode, itype, vtype, params) != RETURN_OK) goto fail; /* process the animated model part */ if (params->animated && processBonesAndAnimations(object, scene) != RETURN_OK) goto fail; /* close file */ NULLDO(aiReleaseImport, scene); RET(0, "%d", RETURN_OK); return RETURN_OK; assimp_fail: DEBUG(GLHCK_DBG_ERROR, aiGetErrorString()); fail: IFDO(aiReleaseImport, scene); IFDO(glhckObjectFree, first); RET(0, "%d", RETURN_FAIL); return RETURN_FAIL; }
/* \brief free skin bone object */ GLHCKAPI unsigned int glhckSkinBoneFree(glhckSkinBone *object) { if (!glhckInitialized()) return 0; CALL(FREE_CALL_PRIO(object), "%p", object); assert(object); /* there is still references to this object alive */ if (--object->refCounter != 0) goto success; /* free the weights */ glhckSkinBoneInsertWeights(object, NULL, 0); /* remove from world */ _glhckWorldRemove(skinBone, object, glhckSkinBone*); /* free */ NULLDO(_glhckFree, object); success: RET(FREE_RET_PRIO(object), "%u", object?object->refCounter:0); return object?object->refCounter:0; }
/* \brief import MikuMikuDance PMD file */ int _glhckImportPMD(_glhckObject *object, const char *file, const glhckImportModelParameters *params, glhckGeometryIndexType itype, glhckGeometryVertexType vtype) { FILE *f; char *texturePath; mmd_data *mmd = NULL; glhckAtlas *atlas = NULL; glhckMaterial *material = NULL; glhckTexture *texture = NULL, **textureList = NULL; glhckImportVertexData *vertexData = NULL; glhckImportIndexData *indices = NULL, *stripIndices = NULL; unsigned int geometryType = GLHCK_TRIANGLE_STRIP; unsigned int i, i2, ix, start, numFaces, numIndices = 0; CALL(0, "%p, %s, %p", object, file, params); if (!(f = fopen(file, "rb"))) goto read_fail; if (!(mmd = mmd_new(f))) goto mmd_no_memory; if (mmd_read_header(mmd) != 0) goto mmd_import_fail; if (mmd_read_vertex_data(mmd) != 0) goto mmd_import_fail; if (mmd_read_index_data(mmd) != 0) goto mmd_import_fail; if (mmd_read_material_data(mmd) != 0) goto mmd_import_fail; /* close file */ NULLDO(fclose, f); if (mmd->header.name) { DEBUG(GLHCK_DBG_CRAP, "%s\n", mmd->header.name); } if (mmd->header.comment) printf("%s\n\n", mmd->header.comment); if (!(vertexData = _glhckCalloc(mmd->num_vertices, sizeof(glhckImportVertexData)))) goto mmd_no_memory; if (!(indices = _glhckMalloc(mmd->num_indices * sizeof(unsigned int)))) goto mmd_no_memory; if (!(textureList = _glhckCalloc(mmd->num_materials, sizeof(_glhckTexture*)))) goto mmd_no_memory; if (!(atlas = glhckAtlasNew())) goto mmd_no_memory; /* add all textures to atlas packer */ for (i = 0; i < mmd->num_materials; ++i) { if (!mmd->materials[i].texture) continue; if (!(texturePath = _glhckImportTexturePath(mmd->materials[i].texture, file))) continue; if ((texture = glhckTextureNewFromFile(texturePath, NULL, NULL))) { glhckAtlasInsertTexture(atlas, texture); glhckTextureFree(texture); textureList[i] = texture; } _glhckFree(texturePath); } for (i = 0; i < mmd->num_materials && !textureList[i]; ++i); if (i >= mmd->num_materials) { /* no textures found */ NULLDO(glhckAtlasFree, atlas); NULLDO(_glhckFree, textureList); } else { /* pack textures */ if (glhckAtlasPack(atlas, GLHCK_RGBA, 1, 0, glhckTextureDefaultParameters()) != RETURN_OK) goto fail; } /* assign data */ for (i = 0, start = 0; i < mmd->num_materials; ++i, start += numFaces) { numFaces = mmd->materials[i].face; for (i2 = start; i2 < start + numFaces; ++i2) { ix = mmd->indices[i2]; /* vertices */ vertexData[ix].vertex.x = mmd->vertices[ix*3+0]; vertexData[ix].vertex.y = mmd->vertices[ix*3+1]; vertexData[ix].vertex.z = mmd->vertices[ix*3+2]; /* normals */ vertexData[ix].normal.x = mmd->normals[ix*3+0]; vertexData[ix].normal.y = mmd->normals[ix*3+1]; vertexData[ix].normal.z = mmd->normals[ix*3+2]; /* texture coords */ vertexData[ix].coord.x = mmd->coords[ix*2+0]; vertexData[ix].coord.y = mmd->coords[ix*2+1] * -1; /* fix coords */ if (vertexData[ix].coord.x < 0.0f) vertexData[ix].coord.x += 1.0f; if (vertexData[ix].coord.y < 0.0f) vertexData[ix].coord.y += 1.0f; /* if there is packed texture */ if (atlas && textureList[i]) { kmVec2 coord; coord.x = vertexData[ix].coord.x; coord.y = vertexData[ix].coord.y; glhckAtlasTransformCoordinates(atlas, textureList[i], &coord, &coord); vertexData[ix].coord.x = coord.x; vertexData[ix].coord.y = coord.y; } indices[i2] = ix; } } if (atlas) { if (!(material = glhckMaterialNew(glhckAtlasGetTexture(atlas)))) goto mmd_no_memory; glhckObjectMaterial(object, material); NULLDO(glhckMaterialFree, material); NULLDO(glhckAtlasFree, atlas); NULLDO(_glhckFree, textureList); } /* triangle strip geometry */ if (!(stripIndices = _glhckTriStrip(indices, mmd->num_indices, &numIndices))) { /* failed, use non stripped geometry */ geometryType = GLHCK_TRIANGLES; numIndices = mmd->num_indices; stripIndices = indices; } else NULLDO(_glhckFree, indices); /* set geometry */ glhckObjectInsertIndices(object, itype, stripIndices, numIndices); glhckObjectInsertVertices(object, vtype, vertexData, mmd->num_vertices); object->geometry->type = geometryType; /* finish */ NULLDO(_glhckFree, vertexData); NULLDO(_glhckFree, stripIndices); NULLDO(mmd_free, mmd); RET(0, "%d", RETURN_OK); return RETURN_OK; read_fail: DEBUG(GLHCK_DBG_ERROR, "Failed to open: %s", file); goto fail; mmd_import_fail: DEBUG(GLHCK_DBG_ERROR, "MMD importing failed."); goto fail; mmd_no_memory: DEBUG(GLHCK_DBG_ERROR, "MMD not enough memory."); fail: IFDO(fclose, f); IFDO(glhckAtlasFree, atlas); IFDO(mmd_free, mmd); IFDO(_glhckFree, textureList); IFDO(_glhckFree, vertexData); IFDO(_glhckFree, indices); IFDO(_glhckFree, stripIndices); RET(0, "%d", RETURN_FAIL); return RETURN_FAIL; }
static int processModel(const char *file, glhckObject *object, glhckObject *current, const struct aiScene *sc, const struct aiNode *nd, glhckGeometryIndexType itype, glhckGeometryVertexType vtype, const glhckImportModelParameters *params) { unsigned int m, f; unsigned int numVertices = 0, numIndices = 0; unsigned int ioffset, voffset; glhckImportIndexData *indices = NULL; glhckImportVertexData *vertexData = NULL; glhckMaterial *material = NULL; glhckTexture **textureList = NULL, *texture = NULL; glhckAtlas *atlas = NULL; const struct aiMesh *mesh; const struct aiFace *face; int canFreeCurrent = 0; int hasTexture = 0; assert(file); assert(object && current); assert(sc && nd); /* combine && atlas loading path */ if (params->flatten) { /* prepare atlas for texture combining */ if (!(atlas = glhckAtlasNew())) goto assimp_no_memory; /* texturelist for offseting coordinates */ if (!(textureList = _glhckCalloc(nd->mNumMeshes, sizeof(_glhckTexture*)))) goto assimp_no_memory; /* gather statistics */ for (m = 0; m != nd->mNumMeshes; ++m) { mesh = sc->mMeshes[nd->mMeshes[m]]; if (!mesh->mVertices) continue; for (f = 0; f != mesh->mNumFaces; ++f) { face = &mesh->mFaces[f]; if (!face) goto fail; numIndices += face->mNumIndices; } numVertices += mesh->mNumVertices; if ((texture = textureFromMaterial(file, sc->mMaterials[mesh->mMaterialIndex]))) { glhckAtlasInsertTexture(atlas, texture); glhckTextureFree(texture); textureList[m] = texture; hasTexture = 1; } } /* allocate vertices */ if (!(vertexData = _glhckCalloc(numVertices, sizeof(glhckImportVertexData)))) goto assimp_no_memory; /* allocate indices */ if (!(indices = _glhckMalloc(numIndices * sizeof(glhckImportIndexData)))) goto assimp_no_memory; /* pack combined textures */ if (hasTexture) { if (glhckAtlasPack(atlas, GLHCK_RGBA, 1, 0, glhckTextureDefaultParameters()) != RETURN_OK) goto fail; } else { NULLDO(glhckAtlasFree, atlas); NULLDO(_glhckFree, textureList); } /* join vertex data */ for (m = 0, ioffset = 0, voffset = 0; m != nd->mNumMeshes; ++m) { mesh = sc->mMeshes[nd->mMeshes[m]]; if (!mesh->mVertices) continue; if (textureList) texture = textureList[m]; else texture = NULL; joinMesh(mesh, voffset, indices+ioffset, vertexData+voffset, atlas, texture); for (f = 0; f != mesh->mNumFaces; ++f) { face = &mesh->mFaces[f]; if (!face) goto fail; ioffset += face->mNumIndices; } voffset += mesh->mNumVertices; } /* create material */ if (hasTexture && !(material = glhckMaterialNew(texture))) goto assimp_no_memory; /* finally build the model */ if (buildModel(current, numIndices, numVertices, indices, vertexData, itype, vtype) == RETURN_OK) { _glhckObjectFile(current, nd->mName.data); if (material) glhckObjectMaterial(current, material); if (!(current = glhckObjectNew())) goto fail; glhckObjectAddChild(object, current); glhckObjectFree(current); canFreeCurrent = 1; } /* free stuff */ IFDO(glhckAtlasFree, atlas); IFDO(glhckMaterialFree, material); IFDO(_glhckFree, textureList); NULLDO(_glhckFree, vertexData); NULLDO(_glhckFree, indices); } else { /* default loading path */ for (m = 0, ioffset = 0, voffset = 0; m != nd->mNumMeshes; ++m) { mesh = sc->mMeshes[nd->mMeshes[m]]; if (!mesh->mVertices) continue; /* gather statistics */ numIndices = 0; for (f = 0; f != mesh->mNumFaces; ++f) { face = &mesh->mFaces[f]; if (!face) goto fail; numIndices += face->mNumIndices; } numVertices = mesh->mNumVertices; // FIXME: create materialFromAssimpMaterial // that returns glhckMaterial with correct stuff /* get texture */ hasTexture = 0; if ((texture = textureFromMaterial(file, sc->mMaterials[mesh->mMaterialIndex]))) hasTexture = 1; /* create material */ if (hasTexture && !(material = glhckMaterialNew(texture))) goto assimp_no_memory; /* allocate vertices */ if (!(vertexData = _glhckCalloc(numVertices, sizeof(glhckImportVertexData)))) goto assimp_no_memory; /* allocate indices */ if (!(indices = _glhckMalloc(numIndices * sizeof(glhckImportIndexData)))) goto assimp_no_memory; /* fill arrays */ joinMesh(mesh, 0, indices, vertexData, NULL, NULL); /* build model */ if (buildModel(current, numIndices, numVertices, indices, vertexData, itype, vtype) == RETURN_OK) { /* FIXME: UGLY */ char pointer[16]; snprintf(pointer, sizeof(pointer), "%p", mesh); _glhckObjectFile(current, pointer); if (material) glhckObjectMaterial(current, material); if (!(current = glhckObjectNew())) goto fail; glhckObjectAddChild(object, current); glhckObjectFree(current); canFreeCurrent = 1; } /* free stuff */ NULLDO(_glhckFree, vertexData); NULLDO(_glhckFree, indices); IFDO(glhckTextureFree, texture); IFDO(glhckMaterialFree, material); } } /* process childrens */ for (m = 0; m != nd->mNumChildren; ++m) { if (processModel(file, object, current, sc, nd->mChildren[m], itype, vtype, params) == RETURN_OK) { if (!(current = glhckObjectNew())) goto fail; glhckObjectAddChild(object, current); glhckObjectFree(current); canFreeCurrent = 1; } } /* we din't do anything to the next * allocated object, so free it */ if (canFreeCurrent) glhckObjectRemoveFromParent(current); return RETURN_OK; assimp_no_memory: DEBUG(GLHCK_DBG_ERROR, "Assimp not enough memory."); fail: IFDO(_glhckFree, vertexData); IFDO(_glhckFree, indices); IFDO(_glhckFree, textureList); IFDO(glhckTextureFree, texture); IFDO(glhckMaterialFree, material); IFDO(glhckAtlasFree, atlas); if (canFreeCurrent) glhckObjectRemoveFromParent(current); return RETURN_FAIL; }
/* \brief import TGA images */ int _glhckImportTGA(const char *file, _glhckImportImageStruct *import) { FILE *f; void *seg = NULL, *data; int bpp, vinverted = 0; int rle = 0, footer_present = 0; size_t datasize, size; unsigned short i, w, h; tga_header *header; tga_footer *footer; unsigned char *bufptr, *bufend, *importData = NULL; CALL(0, "%s, %p", file, import); if (!(f = fopen(file, "rb"))) goto read_fail; fseek(f, 0L, SEEK_END); if ((size = ftell(f)) < sizeof(tga_header) + sizeof(tga_footer)) goto not_possible; if (!(seg = _glhckMalloc(size))) goto out_of_memory; fseek(f, 0L, SEEK_SET); if (fread(seg, 1, size, f) != size) goto fail; /* we don't need the file anymore */ NULLDO(fclose, f); data = seg; header = (tga_header*)data; footer = (tga_footer*)((char*)data+size-sizeof(tga_footer)); /* is TGA v2.0? (stop storing the headers at EOF please) */ if (!memcmp(footer->signature, TGA_SIGNATURE, sizeof(footer->signature))) footer_present = 1; /* skip over header */ data = (char*)data+sizeof(tga_header); /* skip over ID filed */ if (header->idLength) data = (char*)data+header->idLength; /* inverted TGA? */ vinverted = !(header->descriptor & TGA_DESC_VERTICAL); switch (header->imageType) { case TGA_TYPE_COLOR_RLE: case TGA_TYPE_GRAY_RLE: rle = 1; break; case TGA_TYPE_COLOR: case TGA_TYPE_GRAY: rle = 0; break; default: goto unknown_type; } bpp = header->bpp; if (!((bpp == 32) || (bpp == 24) || (bpp == 8))) goto invalid_bpp; /* endian safe for 16-bit dimensions */ w = (header->widthHi << 8) | header->widthLo; h = (header->heightHi << 8) | header->heightLo; if (!IMAGE_DIMENSIONS_OK(w, h)) goto bad_dimensions; /* allocate destination buffer */ if (!(importData = _glhckMalloc(w*h*4))) goto out_of_memory; /* find out how much data to be read from file * (this is NOT simply width*height*4, due to compression) */ datasize = size - sizeof(tga_header) - header->idLength - (footer_present ? sizeof(tga_footer) : 0); /* bufptr is the next byte to be read from the buffer */ bufptr = data; bufend = data + datasize; /* non RLE compressed data */ if (!rle) { for (i = 0; i < h*w && bufptr+bpp/8 <= bufend; ++i) { switch (bpp) { /* 32-bit BGRA */ case 32: importData[i*4+0] = bufptr[2]; importData[i*4+1] = bufptr[1]; importData[i*4+2] = bufptr[0]; importData[i*4+3] = bufptr[3]; bufptr += 4; break; /* 24-bit BGR */ case 24: importData[i*4+0] = bufptr[2]; importData[i*4+1] = bufptr[1]; importData[i*4+2] = bufptr[0]; importData[i*4+3] = 255; bufptr += 3; break; /* 8-bit grayscale */ case 8: importData[i*4+0] = bufptr[0]; importData[i*4+1] = bufptr[0]; importData[i*4+2] = bufptr[0]; importData[i*4+3] = 255; bufptr += 1; break; } } } /* RLE compressed data */ if (rle) { DEBUG(GLHCK_DBG_ERROR, "RLE compressed import not yet implemented."); goto fail; } /* some TGA's are upside-down */ if (vinverted) _glhckInvertPixels(importData, w, h, 4); /* free */ NULLDO(_glhckFree, seg); /* fill import struct */ import->width = w; import->height = h; import->data = importData; import->format = GLHCK_RGBA; import->type = GLHCK_UNSIGNED_BYTE; import->flags |= (bpp==32?GLHCK_TEXTURE_IMPORT_ALPHA:0); RET(0, "%d", RETURN_OK); return RETURN_OK; read_fail: DEBUG(GLHCK_DBG_ERROR, "Failed to open: %s", file); goto fail; not_possible: DEBUG(GLHCK_DBG_ERROR, "Assumed TGA file '%s' has too small filesize", file); goto fail; out_of_memory: DEBUG(GLHCK_DBG_ERROR, "Out of memory, won't load file: %s", file); goto fail; unknown_type: DEBUG(GLHCK_DBG_ERROR, "Unknown TGA image type"); goto fail; invalid_bpp: DEBUG(GLHCK_DBG_ERROR, "TGA image is not either 32, 24 or 8 bpp"); goto fail; bad_dimensions: DEBUG(GLHCK_DBG_ERROR, "TGA image has invalid dimension %dx%d", w, h); fail: IFDO(fclose, f); IFDO(_glhckFree, importData); IFDO(_glhckFree, seg); RET(0, "%d", RETURN_FAIL); return RETURN_FAIL; }