diff --git a/Makefile b/Makefile index 6ee0d92..87fba7c 100644 --- a/Makefile +++ b/Makefile @@ -106,20 +106,24 @@ clean: check: hTable.o regionParser.o chunkParser.o cNBT.o model.o #hTable tests checkmk tests/hTable.check > tests/hTableCheck.c - gcc tests/hTableCheck.c hTable.o -lcheck -lm $(SUBUNIT) -Wall -o tests/hTableCheck + gcc tests/hTableCheck.c hTable.o -lcheck -g -lm $(SUBUNIT) -Wall -o tests/hTableCheck ./tests/hTableCheck #regionParser tests checkmk tests/regionParser.check > tests/regionParserCheck.c - gcc tests/regionParserCheck.c regionParser.o -lcheck -lm $(ZLIB) $(SUBUNIT) -o tests/regionParserCheck + gcc tests/regionParserCheck.c regionParser.o -lcheck -g -lm $(ZLIB) $(SUBUNIT) -o tests/regionParserCheck ./tests/regionParserCheck #chunkParser tests checkmk tests/chunkParser.check > tests/chunkParserCheck.c - gcc tests/chunkParserCheck.c chunkParser.o cNBT.o -lcheck -lm $(SUBUNIT) -o tests/chunkParserCheck + gcc tests/chunkParserCheck.c chunkParser.o cNBT.o -lcheck -g -lm $(SUBUNIT) -o tests/chunkParserCheck ./tests/chunkParserCheck #model tests checkmk tests/model.check > tests/modelCheck.c - gcc tests/modelCheck.c model.o hTable.o -lcheck -lm $(SUBUNIT) -o tests/modelCheck + gcc tests/modelCheck.c model.o hTable.o -lcheck -g -lm $(SUBUNIT) -o tests/modelCheck ./tests/modelCheck + #wavefront tests + checkmk tests/wavefront.check > tests/wavefrontCheck.c + gcc tests/wavefrontCheck.c model.o hTable.o -lcheck -g -lm $(SUBUNIT) -o tests/wavefrontCheck + ./tests/wavefrontCheck doc: src/lib/*.h src/*.c src/lib/*.c doc/Doxyfile.conf doxygen ./doc/Doxyfile.conf diff --git a/README.md b/README.md index 90f3173..70988e4 100644 --- a/README.md +++ b/README.md @@ -148,7 +148,7 @@ These are compiled automatically using make, and then linked together into cNBT. Most of the code has been organized into four "libraries". These may prove to be useful should you want to parse Minecraft save files or generate wavefront 3D models. Feel free to use these as libraries in your projects just make sure to read the license before you do so. -The documentation for functions in these libraries should be mainly in header files and on [GitHub Pages](https://tca166.github.io/mcSavefileParsers/), and I will gladly expand it should there be a need so just let me know. +The documentation for functions in these libraries should be mainly in header files and on GitHub Pages, and I will gladly expand it should there be a need so just let me know. - regionParser This library provides three functions for parsing region files. diff --git a/src/lib/chunkParser.h b/src/lib/chunkParser.h index d40bc25..dcc08e0 100644 --- a/src/lib/chunkParser.h +++ b/src/lib/chunkParser.h @@ -15,14 +15,42 @@ /*! @struct section @brief 16x16x16 section of a chunk + @details The section structure contains the block data, the block palette, the y coordinate of the section and the lengths of the block data and palette + @note This struct is mainly used for holding the raw nbt data of a section + @todo Add biomes + @see getBlockStates @ingroup chunkParser */ -typedef struct section{ +typedef struct{ //we ignore the biomes because we don't need that data - uint64_t* blockData; //raw nbt file block data + /*! + @var blockData + @brief raw nbt section block data + @details Packed array with padding, so that the individual block data isn't split. + @details The length of one element in this packed array is 4 or log2(paletteLen) whichever is higher. + @details the individual values stored here point to the identifiers in blockPalette + @warning can be NULL, in which case the section is full of the same block type: blockPalette[0] + */ + uint64_t* blockData; + /*! + @var blockPalette + @brief A string array of block identifiers + */ char** blockPalette; - size_t blockDataLen; //the length of blockData in bytes + /*! + @var blockDataLen + @brief Length of the blockData long array + */ + size_t blockDataLen; + /*! + @var paletteLen + @brief Length of the blockPalette array + */ size_t paletteLen; + /*! + @var y + @brief the y index of this section. They go from -4 to whatever the height limit of the world is but divided by 16 + */ short y; } section; @@ -31,10 +59,27 @@ typedef struct section{ @brief A block structure with it's own coordinates and a string containing it's type @ingroup chunkParser */ -typedef struct block{ +typedef struct{ + /*! + @var x + @brief The x position of the block + */ int x; + /*! + @var y + @brief The y position of the block + */ int y; + /*! + @var z + @brief The z position of the block + */ int z; + /*! + @var type + @brief A string block type identifier + @note Shouldn't be NULL + */ char* type; } block; diff --git a/src/lib/generator.c b/src/lib/generator.c index 1cd49fd..15f65aa 100644 --- a/src/lib/generator.c +++ b/src/lib/generator.c @@ -11,7 +11,7 @@ model generateFromNbt(unsigned char* data, int dataSize, hashTable* materials, hashTable* objects, bool yLim, int upLim, int downLim, bool b, bool f, unsigned int side, int chunkX, int chunkZ){ //Array of sections in this chunk - struct section sections[maxSections] = {0}; + section sections[maxSections] = {0}; int n = getSections(data, dataSize, sections); free(data); /*It is possible to not have to iterate over each block again, and do everything in a single loop. @@ -54,7 +54,7 @@ model generateFromNbt(unsigned char* data, int dataSize, hashTable* materials, h return newModel; } -cubeModel createCubeModel(struct section* sections, int sectionLen, hashTable* materials, bool yLim, int upLim, int downLim, unsigned int side, bool matCheck, int xOff, int zOff){ +cubeModel createCubeModel(section* sections, int sectionLen, hashTable* materials, bool yLim, int upLim, int downLim, unsigned int side, bool matCheck, int xOff, int zOff){ cubeModel cubeModel = initCubeModel(16,16 * sectionLen, 16); for(int i = 0; i < sectionLen; i++){ //create the block state array @@ -64,7 +64,7 @@ cubeModel createCubeModel(struct section* sections, int sectionLen, hashTable* m for(int x = 0; x < 16; x++){ for(int y = 0; y < 16; y++){ for(int z = 0; z < 16; z++){ - struct block newBlock = createBlock(x, y, z, states, sections[i]); + block newBlock = createBlock(x, y, z, states, sections[i]); if((newBlock.y > upLim || newBlock.y < downLim) && yLim){ newBlock.type = mcAir; } @@ -100,7 +100,7 @@ cubeModel createCubeModel(struct section* sections, int sectionLen, hashTable* m return cubeModel; } -cube cubeFromBlock(struct block block, const unsigned int side, material* material){ +cube cubeFromBlock(block block, const unsigned int side, material* material){ cube newCube = createGenericCube(side); newCube.x = block.x; diff --git a/src/lib/generator.h b/src/lib/generator.h index 1f74162..8a02924 100644 --- a/src/lib/generator.h +++ b/src/lib/generator.h @@ -23,7 +23,7 @@ @return The created cube object @ingroup generator */ -cube cubeFromBlock(struct block block, const unsigned int side, struct material* material); +cube cubeFromBlock(block block, const unsigned int side, material* material); /*! @brief Creates a new cube based model object from a section array. @@ -40,7 +40,7 @@ cube cubeFromBlock(struct block block, const unsigned int side, struct material* @return The created cube model object @ingroup generator */ -cubeModel createCubeModel(struct section* sections, int sectionLen, hashTable* materials, bool yLim, int upLim, int downLim, unsigned int side, bool matCheck, int xOff, int zOff); +cubeModel createCubeModel(section* sections, int sectionLen, hashTable* materials, bool yLim, int upLim, int downLim, unsigned int side, bool matCheck, int xOff, int zOff); //Generally speaking i could just pass a whole chunk object from regionParser here, but I like the design of having that be completely separate diff --git a/src/lib/hTable.c b/src/lib/hTable.c index 4ad4924..7ed591e 100644 --- a/src/lib/hTable.c +++ b/src/lib/hTable.c @@ -12,9 +12,12 @@ //taken from https://benhoyt.com/writings/hash-table-in-c/ //i am not smart enough to come up with my own hash function -//Generates a hash from a string -uint64_t hash(const char* key) -{ +/*! + @brief Generates a hash from a string + @param key the key to hash + @return a numeric hash +*/ +static uint64_t hash(const char* key){ uint64_t hash = FNV_OFFSET; for (const char* p = key; *p; p++) { hash ^= (uint64_t)(unsigned char)(*p); @@ -23,7 +26,13 @@ uint64_t hash(const char* key) return hash; } -struct hTableItem* initHashItem(const char* key, const void* value){ +/*! + @brief Initializes a hash item with the given values + @param key the key under which the value is held + @param value the value of the item + @return a newly allocated object +*/ +static struct hTableItem* initHashItem(const char* key, const void* value){ struct hTableItem* item = malloc(sizeof(struct hTableItem)); item->key = malloc(strlen(key) + 1); strcpy(item->key, key); @@ -40,7 +49,11 @@ hashTable* initHashTable(size_t size){ return table; } -void freeHashItem(struct hTableItem* item){ +/*! + @brief Frees a given hash item + @param item the item to free +*/ +static void freeHashItem(struct hTableItem* item){ if(item->next != NULL){ freeHashItem(item->next); } diff --git a/src/lib/hTable.h b/src/lib/hTable.h index b1f4a91..1b56137 100644 --- a/src/lib/hTable.h +++ b/src/lib/hTable.h @@ -14,8 +14,21 @@ @ingroup hTable */ struct hTableItem{ + /*! + @var key + @brief the item key, used to identify the value in hash collisions + */ char* key; + /*! + @var value + @brief whatever value the key is holding + */ void* value; + /*! + @var next + @brief the next item in the linked list + @details can be NULL + */ struct hTableItem* next; }; @@ -24,10 +37,23 @@ struct hTableItem{ @brief A hash table storing keys and void* as values. For collision handling a linked list approach has been chosen @ingroup hTable */ -typedef struct hashTable{ +typedef struct{ + /*! + @var items + @brief Nullable array of hash table items + @details Key hashes point into this array, and if the value under the hash isn't null then the item is inserted at the end of the underlying linked list + */ struct hTableItem** items; + /*! + @var size + @brief The size of the array, not meant to be changed + */ size_t size; //the size of the array - int count; //how many elements we have in the array + /*! + @var count + @brief how many elements we have in the array + */ + int count; } hashTable; /*! diff --git a/src/lib/model.c b/src/lib/model.c index 15ebb83..fc263db 100644 --- a/src/lib/model.c +++ b/src/lib/model.c @@ -21,7 +21,12 @@ #define reallocMult 1.5 -int digits(int i){ +/*! + @brief Calculates the number of digits in a number + @param i the number + @return the number of digits +*/ +static int digits(int i){ if(i == 0){ return 1; } @@ -33,12 +38,12 @@ int digits(int i){ return result; } -double distanceBetweenVectors(struct vertex a, struct vertex b){ - return sqrt(sq(a.x - b.x) + sq(a.y - b.y) + sq(a.z - b.z)); +double distanceBetweenVectors(vertex a, vertex b){ + return sqrt(sq(a.coordinates.x - b.coordinates.x) + sq(a.coordinates.y - b.coordinates.y) + sq(a.coordinates.z - b.coordinates.z)); } -bool verticesEqual(struct vertex a, struct vertex b){ - return (a.x == b.x) && (a.y == b.y) && (a.z == b.z); +bool verticesEqual(vertex a, vertex b){ + return (a.coordinates.x == b.coordinates.x) && (a.coordinates.y == b.coordinates.y) && (a.coordinates.z == b.coordinates.z); } model initModel(int objectCount){ @@ -96,11 +101,11 @@ cube createGenericCube(unsigned int side){ return newCube; } -struct vertex newVertex(int x, int y, int z){ - struct vertex new; - new.x = x; - new.y = y; - new.z = z; +vertex newVertex(int x, int y, int z){ + vertex new; + new.coordinates.x = x; + new.coordinates.y = y; + new.coordinates.z = z; return new; } @@ -108,26 +113,29 @@ struct vertex newVertex(int x, int y, int z){ //a > b && c < d cubeFace* newCubeFace(int a, int b, int c, int d){ cubeFace* face = malloc(sizeof(cubeFace)); - face->v1 = a; - face->v2 = b; - face->v3 = c; - face->v4 = d; + face->vertices.v1 = a; + face->vertices.v2 = b; + face->vertices.v3 = c; + face->vertices.v4 = d; return face; } -struct objFace deCube(cubeFace face){ - struct objFace new; +objFace deCube(cubeFace face){ + objFace new; new.vertices = malloc(sizeof(int) * 4); - new.vertices[0] = face.v1; - new.vertices[1] = face.v2; - new.vertices[2] = face.v3; - new.vertices[3] = face.v4; + memcpy(new.vertices, face.arr, sizeof(int) * 4); new.vertexCount = 4; new.m = NULL; return new; } -bool isPresent(char* string, hashTable* objects){ +/*! + @brief Checks if a key is present within a hashTable + @param string the key to check + @param objects the hashTable + @return true if string is a valid key +*/ +static inline bool isPresent(char* string, hashTable* objects){ if(objects == NULL || string == NULL){ return false; } @@ -216,7 +224,7 @@ object deCubeObject(cube* c){ //here we deCube faces, and check which vertices are needed for(int i = 0; i < 6; i++){ if(c->faces[i] != NULL){ - result.faces = realloc(result.faces, (result.faceCount + 1) * sizeof(struct objFace)); + result.faces = realloc(result.faces, (result.faceCount + 1) * sizeof(objFace)); result.faces[result.faceCount] = deCube(*(c->faces[i])); for(int n = 0; n < result.faces[result.faceCount].vertexCount; n++){ if(vertexNeeded[result.faces[result.faceCount].vertices[n]] == -1){ @@ -228,10 +236,10 @@ object deCubeObject(cube* c){ } } result.vertexCount = neededVertexCount; - result.vertices = calloc(neededVertexCount, sizeof(struct vertex)); + result.vertices = calloc(neededVertexCount, sizeof(vertex)); //If all vertices are used we can just copy stuff over if(neededVertexCount == 8){ - memcpy(result.vertices, c->vertices, 8 * sizeof(struct vertex)); + memcpy(result.vertices, c->vertices, 8 * sizeof(vertex)); } else if(neededVertexCount > 0){ //Else we need to create a transformation for face vertices indexes @@ -295,10 +303,10 @@ model cubeModelToModel(const cubeModel* m, hashTable* specialObjects){ newObject->y = m->cubes[x][y][z]->y * m->cubes[x][y][z]->side; newObject->z = m->cubes[x][y][z]->z * m->cubes[x][y][z]->side; //we need to copy everything so that we can free the entire hash table earlier - newObject->vertices = calloc(newObject->vertexCount, sizeof(struct vertex)); - memcpy(newObject->vertices, prot->vertices, sizeof(struct vertex) * newObject->vertexCount); - newObject->faces = calloc(newObject->faceCount, sizeof(struct objFace)); - memcpy(newObject->faces, prot->faces, sizeof(struct objFace) * newObject->faceCount); + newObject->vertices = calloc(newObject->vertexCount, sizeof(vertex)); + memcpy(newObject->vertices, prot->vertices, sizeof(vertex) * newObject->vertexCount); + newObject->faces = calloc(newObject->faceCount, sizeof(objFace)); + memcpy(newObject->faces, prot->faces, sizeof(objFace) * newObject->faceCount); for(int i = 0; i < newObject->faceCount; i++){ newObject->faces[i].vertices = calloc(prot->faces[i].vertexCount, sizeof(int)); memcpy(newObject->faces[i].vertices, prot->faces[i].vertices, sizeof(int) * prot->faces[i].vertexCount); @@ -321,7 +329,12 @@ model cubeModelToModel(const cubeModel* m, hashTable* specialObjects){ return result; } -bool isNotEmpty(object* c){ +/*! + @brief Checks if object is emtpy + @param c + @return true if the object is valid +*/ +static inline bool isNotEmpty(object* c){ if(c == NULL){ return false; } @@ -331,8 +344,15 @@ bool isNotEmpty(object* c){ return false; } -//Function for allocating the buffer in increasingly large sizes. This is faster than repeatedly reallocating -void* reallocBy(void* ptr, size_t* curSize, size_t* sizeTaken, float mult){ +/*! + @brief Function for allocating the buffer in increasingly large sizes. This is faster than repeatedly reallocating + @param ptr the pointer to realloc + @param curSize the current size of the buffer + @param sizeTaken the size that is taken up in the buffer + @param mult the multiplier to realloc by + @return a valid buffer pointer +*/ +static void* reallocBy(void* ptr, size_t* curSize, size_t* sizeTaken, float mult){ if(*sizeTaken > *curSize){ *curSize = *sizeTaken * mult; return realloc(ptr, *sizeTaken * mult); @@ -342,8 +362,15 @@ void* reallocBy(void* ptr, size_t* curSize, size_t* sizeTaken, float mult){ } } -//Simple procedure taken out of generateModel to prevent duplicate code -char* appendMtlLine(const char* mtlName, char* appendTo, size_t* outSize, size_t* alloc){ +/*! + @brief Simple procedure taken out of generateModel to prevent duplicate code + @param mtlName the name of the mtl file + @param appendTo the string to append to + @param outSize output pointer for size + @param alloc pointer to current size + @return pointer to a new buffer with appended mtl line +*/ +static char* appendMtlLine(const char* mtlName, char* appendTo, size_t* outSize, size_t* alloc){ size_t mtlLineSize = 9 + strlen(mtlName); char* mtlLine = malloc(mtlLineSize); if(snprintf(mtlLine, mtlLineSize, "usemtl %s\n", mtlName) < 0){ @@ -413,18 +440,18 @@ char* generateModel(const model* thisModel, size_t* outSize, const char* materia free(objectLine); //foreach vertex for(int i = 0; i < thisObject->vertexCount; i++){ - struct vertex v = thisObject->vertices[i]; - v.x += thisObject->x; - v.y += thisObject->y; - v.z += thisObject->z; - size_t size = 27 + digits((int)v.x) + digits((int)v.y) + digits((int)v.z); + vertex v = thisObject->vertices[i]; + v.coordinates.x += thisObject->x; + v.coordinates.y += thisObject->y; + v.coordinates.z += thisObject->z; + size_t size = 27 + digits((int)v.coordinates.x) + digits((int)v.coordinates.y) + digits((int)v.coordinates.z); //printf("%d %d %2f\n", size, digits(v.x), v.x); char* vertexLine = NULL; vertexLine = malloc(size); if(vertexLine == NULL){ mallocError("vertexLine", size); } - if(snprintf(vertexLine, size, "v %.6f %.6f %.6f\n", v.x, v.y , v.z) < 0){ + if(snprintf(vertexLine, size, "v %.6f %.6f %.6f\n", v.coordinates.x, v.coordinates.y , v.coordinates.z) < 0){ stringError("snprintf") } *outSize += size - 1; @@ -434,7 +461,7 @@ char* generateModel(const model* thisModel, size_t* outSize, const char* materia } //foreach face for(int i = 0; i < thisObject->faceCount; i++){ - struct objFace face = thisObject->faces[i]; + objFace face = thisObject->faces[i]; if(face.m != NULL){ fileContents = appendMtlLine(face.m->name, fileContents, outSize, &alloc); } @@ -551,14 +578,21 @@ hashTable* getMaterials(char* filename){ if(f != NULL){ f++; newMaterial.d = atof(f); + //we alloc a new buffer material* ptr = malloc(sizeof(material)); + //write the stack value to it *ptr = newMaterial; + //and store it in a hashtable insertHashItem(result, newMaterial.name, ptr); + newMaterial.name = NULL; //if we properly stored everything we indicate that } } } token = strtok(NULL, "\n"); } + if(newMaterial.name != NULL){ //if the file ended before there was a d line this will happen + free(newMaterial.name); + } free(bytes); return result; } @@ -645,10 +679,10 @@ hashTable* readWavefront(char* filename, hashTable* materials, unsigned int side parsingError(filename, "vertex definition parsing") } if(newObject.vertices == NULL){ - newObject.vertices = malloc(sizeof(struct vertex)); + newObject.vertices = malloc(sizeof(vertex)); } else{ - newObject.vertices = realloc(newObject.vertices, (newObject.vertexCount + 1) * sizeof(struct vertex)); + newObject.vertices = realloc(newObject.vertices, (newObject.vertexCount + 1) * sizeof(vertex)); } newObject.vertices[newObject.vertexCount] = newVertex(x * side, y * side, z * side); newObject.vertexCount++; @@ -688,7 +722,7 @@ hashTable* readWavefront(char* filename, hashTable* materials, unsigned int side f++; } free(num); - struct objFace newFace; + objFace newFace; newFace.vertexCount = f; newFace.vertices = vertices; if(nextM != NULL){ @@ -699,10 +733,10 @@ hashTable* readWavefront(char* filename, hashTable* materials, unsigned int side newFace.m = NULL; } if(newObject.faces == NULL){ - newObject.faces = malloc(sizeof(struct objFace)); + newObject.faces = malloc(sizeof(objFace)); } else{ - newObject.faces = realloc(newObject.faces, (newObject.faceCount + 1) * sizeof(struct objFace)); + newObject.faces = realloc(newObject.faces, (newObject.faceCount + 1) * sizeof(objFace)); } newObject.faces[newObject.faceCount] = newFace; newObject.faceCount++; @@ -745,12 +779,12 @@ object modelToObject(const model* m, const char* type){ object* thisObject = m->objects[o]; int vertexCount = 0; //the amount of local vertices that was appended to the result array int* localV = calloc(thisObject->vertexCount, sizeof(int)); //local transformation of face vertex - result.vertices = realloc(result.vertices, (result.vertexCount + thisObject->vertexCount) * sizeof(struct vertex)); + result.vertices = realloc(result.vertices, (result.vertexCount + thisObject->vertexCount) * sizeof(vertex)); for(int i = 0; i < thisObject->vertexCount; i++){ - struct vertex evalV = thisObject->vertices[i]; - evalV.x += thisObject->x; - evalV.y += thisObject->y; - evalV.z += thisObject->z; + vertex evalV = thisObject->vertices[i]; + evalV.coordinates.x += thisObject->x; + evalV.coordinates.y += thisObject->y; + evalV.coordinates.z += thisObject->z; bool done = false; //if we found the equivalent already stored for(int n = 0; n < result.vertexCount; n++){ if(verticesEqual(evalV, result.vertices[n])){ @@ -766,7 +800,7 @@ object modelToObject(const model* m, const char* type){ } result.vertexCount += vertexCount; //now just append faces and apply the localV transformation - result.faces = realloc(result.faces, (result.faceCount + thisObject->faceCount) * sizeof(struct objFace)); + result.faces = realloc(result.faces, (result.faceCount + thisObject->faceCount) * sizeof(objFace)); for(int i = 0; i < thisObject->faceCount; i++){ result.faces[result.faceCount + i] = thisObject->faces[i]; bool prevMatOverlap = false; diff --git a/src/lib/model.h b/src/lib/model.h index 9f63b4f..e960267 100644 --- a/src/lib/model.h +++ b/src/lib/model.h @@ -14,78 +14,190 @@ /*! @struct material @brief A material struct that can be used in a model + @todo add all the wavefront material properties + @see model @ingroup model */ -typedef struct material{ +typedef struct{ + /*! + @var name + @brief the material name + */ char* name; //here i could add more fields... but field d is the only one we need + /*! + @var d + @brief the material density + */ float d; } material; /*! - @struct vertex - @brief A 3D vertex struct + @union vertex + @brief A 3D vertex struct holding three floats + @see cubeFace + @see object @ingroup model */ -typedef struct vertex{ - float x; - float y; - float z; +typedef union { + /*! + @var coordinates + @brief a look into the vertex coordinates + */ + struct{ + /*! + @var x + @brief the x position of the vertices + */ + float x; + /*! + @var y + @brief the y position of the vertices + */ + float y; + /*! + @var z + @brief the z position of the vertices + */ + float z; + } coordinates; + /*! + @var arr + @brief a look into the vertex as an array of floats + */ + float arr[3]; } vertex; /*! - @struct cubeFace - @brief A face of a cube + @union cubeFace + @brief A face of a cube containing pointers to vertices @details A face with only 4 vertices. V variables point to vertices in the associated cube object + @see cube @ingroup model */ -typedef struct cubeFace{ - unsigned int v1; - unsigned int v2; - unsigned int v3; - unsigned int v4; +typedef union{ + /*! + @var vertices + @brief a look into the cubeFace as individual integers + */ + struct{ + unsigned int v1; + unsigned int v2; + unsigned int v3; + unsigned int v4; + } vertices; + /*! + @var arr + @brief a look at a cubeFace as an array of pointers + */ + unsigned int arr[4]; } cubeFace; /*! @struct cube @brief obj file cube with 6 cubeFaces and 8 defining vertices - @details A cube with 6 cubeFaces and 8 defining vertices. The cube is defined by it's x, y, z and side. + @details A static array of vertices with set positions define the shape, while a static array of faces point to these vertices giving information on how walls look like + @see cubeModel @ingroup model */ -typedef struct cube{ +typedef struct{ + /*! + @var x + @brief the x position of the cube + */ float x; + /*! + @var y + @brief the y position + */ float y; + /*! + @var z + @brief the z position + */ float z; + /*! + @var side + @brief length of the cube side + */ float side; //Cannot be lower than 0 for very obvious reasons - vertex vertices[8]; //array of relative to x,y,z vertices - cubeFace* faces[6]; //array of pointers so that it may be nullable - char* type; //text representing a cube name or a broad type. Feel free to make it NULL + /*! + @var vertices + @brief array of vertices relative to the cube + */ + vertex vertices[8]; + /*! + @var faces + @brief nullable array of faces + */ + cubeFace* faces[6]; + /*! + @var type + @brief the type of the cube, usually Minecraft material + @note may be NULL + */ + char* type; + /*! + @var m + @brief the material associated with the cube + @note may be NULL + */ material* m; } cube; /*! @struct cubeModel @brief A strict 3D model struct that is made up of cubes in a strict 3D arrangement. + @details A cubeModel consists of a nullable 3d array of cubes that transforms a 3d position aligned to 1 into a cube* @note Can be transformed into a normal loose model. @note Ideally if you plan on generating a model of something that can be represented by cubes you first generate this. @ingroup model */ -typedef struct cubeModel{ - unsigned int x; //Max x index - unsigned int y; //Max y index - unsigned int z; //Max z index - struct cube**** cubes; //nullable 3d array of cubes +typedef struct{ + /*! + @var x + @brief the maximum x index, or the size of the allocated x dimension + */ + unsigned int x; + /*! + @var y + @brief the maximum y index, or the size of the allocated y dimension + */ + unsigned int y; + /*! + @var z + @brief the maximum z index, or the size of the allocated z dimension + */ + unsigned int z; + /*! + @var cubes + @brief nullable 3d array of cubes + */ + cube**** cubes; } cubeModel; /*! @struct objFace @brief A dynamic array of vertices - an object face @details A dynamic array of vertices - an object face. The vertices are relative to the object's x, y, z coordinates. + @see object @ingroup model */ -typedef struct objFace{ +typedef struct{ + /*! + @var vertexCount + @brief the number of vertices + */ size_t vertexCount; + /*! + @var vertices + @brief an allocated array of vertices + */ int* vertices; + /*! + @var m + @brief NULL or pointer to the material + */ material* m; } objFace; @@ -93,17 +205,54 @@ typedef struct objFace{ @struct object @brief A 3D object struct that is made up of vertices and faces. @details A 3D object struct that is made up of vertices and faces. The vertices are relative to the object's x, y, z coordinates. + @see model @ingroup model */ -typedef struct object{ +typedef struct{ + /*! + @var x + @brief the x position of the object + */ float x; + /*! + @var y + @brief the y position of the object + */ float y; + /*! + @var z + @brief the z position + */ float z; + /*! + @var vertexCount + @brief the number of vertices this object uses + */ size_t vertexCount; - vertex* vertices; //array of relative to x, y, z vertices + /*! + @var vertices + @brief an array of vertices this object uses + */ + vertex* vertices; + /*! + @var faceCount + @brief the number of faces in this model + */ size_t faceCount; - objFace* faces; //doesn't need to be nullable + /*! + @var faces + @brief an array of faces + */ + objFace* faces; + /*! + @var m + @brief the material pointer + */ material* m; + /*! + @var type + @brief the type of the object, generally a Minecraft identifier + */ char* type; } object; @@ -112,14 +261,31 @@ typedef struct object{ /*! @struct model @brief A loose array of even looser objects with an associated array of materials that objects can point to. - @details A loose array of even looser objects with an associated array of materials that objects can point to. + @details A loose set of objects located in an undefined space, with materials pointing to materialArr. + @warning Having objects not using materialArr might lead to nasty memory leaks @ingroup model */ -typedef struct model{ +typedef struct{ + /*! + @var objects + @brief a nullable list of objects + */ object** objects; //Nullable array of objects - size_t objectCount; //Size of objects - material** materialArr; //Optional array of materials, that objects can point to - size_t materialCount; //Size of materialArr + /*! + @var objectCount + @brief the size of the objects array + */ + size_t objectCount; + /*! + @var materialArr + @brief Optional array of materials, that objects can point to + */ + material** materialArr; + /*! + @var materialCount + @brief Size of materialArr + */ + size_t materialCount; } model; /*! diff --git a/src/lib/regionParser.c b/src/lib/regionParser.c index 27dba6d..9a4f846 100644 --- a/src/lib/regionParser.c +++ b/src/lib/regionParser.c @@ -11,7 +11,22 @@ #define getRegion(coord) coord>>5 -int handleFirstSegment(chunk* output, FILE* regionFile, char* regionFileName){ +/*! + @def segmentLength + @brief Length in bytes of savefile segment + @ingroup regionParser +*/ +#define segmentLength 4096 + +/*! + @brief handles the first segment of the region file + @param output the chunk to write to + @param regionFile the file to read from + @param reigonFileName the filename for debug purposes + @warning this function is fpointer sensitive + @return 0 on success +*/ +static int handleFirstSegment(chunk* output, FILE* regionFile, char* regionFileName){ //so the numbers are stored as big endian AND as int24 byte bytes[3]; size_t s1 = fread(&bytes, 1, 3, regionFile); @@ -30,14 +45,27 @@ int handleFirstSegment(chunk* output, FILE* regionFile, char* regionFileName){ return (s1 + s2 == 4) - 1 || (output->offset == 0 && output->sectorCount == 0); } -int handleSecondSegment(chunk* output, FILE* regionFile){ +/*! + @brief handles the second segment of the region file + @param output the output to write to + @param regionFile the file to read from + @return 0 on success +*/ +static int handleSecondSegment(chunk* output, FILE* regionFile){ byte bytes[4]; size_t s1 = fread(&bytes, 1, 4, regionFile); output->timestamp = (bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3]; return (s1 == 4) - 1 && output->timestamp > 0; } -int getChunkData(chunk* thisChunk, FILE* regionFile, char* regionFileName){ +/*! + @brief gets the chunk's data from region file + @param thisChunk the chunk to read from and write data to + @param regionFile the file containing the raw chunk data we are looking for + @param regionFileName for debug + @return 0 on success +*/ +static int getChunkData(chunk* thisChunk, FILE* regionFile, char* regionFileName){ //find the corresponding section if(fseek(regionFile, segmentLength * thisChunk->offset, SEEK_SET) != 0){ fileError(regionFileName, "seek"); @@ -49,9 +77,12 @@ int getChunkData(chunk* thisChunk, FILE* regionFile, char* regionFileName){ } thisChunk->byteLength = (bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3]; //get the compression type - if(fread(&thisChunk->compression, 1, 1, regionFile) != 1){ + byte comp; + if(fread(&comp, 1, 1, regionFile) != 1){ fileError(regionFileName, "parsed:2"); } + //without this cast nasty things happen + thisChunk->compression = (compression_t)comp; //fseek(regionFile, 2, SEEK_CUR); //Then get the data thisChunk->byteLength += 5; @@ -64,7 +95,7 @@ int getChunkData(chunk* thisChunk, FILE* regionFile, char* regionFileName){ } //fseek(regionFile, (chunks[i].sectorCount * segmentLength) - chunks[i].byteLength, SEEK_CUR); //handle different compression types - if(thisChunk->compression == Uncompressed){ + if(thisChunk->compression == None){ thisChunk->data = data; } else{ diff --git a/src/lib/regionParser.h b/src/lib/regionParser.h index 3d3ad5e..b6845d5 100644 --- a/src/lib/regionParser.h +++ b/src/lib/regionParser.h @@ -15,18 +15,19 @@ */ #define chunkN 1024 -/*! - @def segmentLength - @brief Length in bytes of savefile segment - @ingroup regionParser -*/ -#define segmentLength 4096 //How to translate the compression byte -#define GZip 1 -#define Zlib 2 -#define Uncompressed 3 +/*! + @enum compression_t + @brief Minecraft indicator for how the chunk is compressed + @ingroup regionParser +*/ +typedef enum{ + GZip = 1, + Zlib = 2, + None = 3 +} compression_t; /*! @def chunkIsNull @@ -67,14 +68,49 @@ typedef unsigned char byte; @brief A chunk structure @ingroup regionParser */ -typedef struct chunk{ - int x; //x coordinate in chunk coordinates - int z; //z coordinate in chunk coordinates +typedef struct{ + /*! + @var x + @brief the x chunk coordinate + */ + int x; + /*! + @var z + @brief the y chunk coordinate + */ + int z; + /*! + @var offset + @brief the offset within the region file + @note this isn't particularly useful + */ unsigned int offset; //3 bytes of offset + /*! + @var sectorCount + @brief a single byte indicating how many region file sectors this chunk takes up + @note this isn't particularly useful + */ byte sectorCount; //1 byte indicating chunk data length + /*! + @var timestamp + @brief the minecraft timestamp + */ unsigned int timestamp; + /*! + @var byteLength + @brief length of region in bytes + */ int byteLength; //it actually is signed in the files - byte compression; + /*! + @var compression + @brief enum value indicating compression type + @note this isn't particularly useful + */ + compression_t compression; + /*! + @var data + @brief decompressed data + */ byte* data; //pointer to decompressed bytes } chunk; diff --git a/src/radiusGenerator.c b/src/radiusGenerator.c index b70922a..318fb04 100644 --- a/src/radiusGenerator.c +++ b/src/radiusGenerator.c @@ -296,7 +296,7 @@ int main(int argc, char** argv){ sharedFree(sem, sizeof(sem_t)); if(materials != NULL){ forHashTableItem(materials){ - struct material* mat = (struct material*)item->value; + material* mat = (material*)item->value; free(mat->name); free(mat); } diff --git a/tests/chunkParser.check b/tests/chunkParser.check index a945a74..95b8d91 100644 --- a/tests/chunkParser.check +++ b/tests/chunkParser.check @@ -20,7 +20,7 @@ #test getSectionsTest getTestChunk - struct section sections[maxSections]; + section sections[maxSections]; int numSections = getSections(data, sz, sections); ck_assert_msg(numSections > 0, "No sections were extracted"); for(int i = 0; i < numSections; i++){ @@ -47,7 +47,7 @@ #test statesTest getTestChunk - struct section sections[maxSections]; + section sections[maxSections]; int numSections = getSections(data, sz, sections); for(int i = 0; i < numSections; i++){ int sz = 0; @@ -72,9 +72,9 @@ #test dataTest getTestChunk - struct section sections[maxSections]; + section sections[maxSections]; int numSections = getSections(data, sz, sections); - struct section first = sections[0]; + section first = sections[0]; ck_assert_msg(first.y == -4, "Wrong section"); for(int i = 0; i < 16; i++){ ck_assert_msg(first.blockData[i] == 0, "0 section isn't 0"); @@ -86,7 +86,7 @@ for(int i = 16 + 64; i < 16 + 64 + 18; i++){ ck_assert_msg(first.blockData[i] == 2459565876494606882, "Later longs"); } - struct section third = sections[2]; + section third = sections[2]; ck_assert_msg(third.y == -2, "Wrong section"); for(int i = 0; i < 8; i++){ ck_assert_msg(third.blockData[i] == 0, "First 8 longs aren't 0"); diff --git a/tests/model.check b/tests/model.check index d7687f8..7cdb6e3 100644 --- a/tests/model.check +++ b/tests/model.check @@ -26,11 +26,11 @@ void verifyModelStr(char* modelStr, unsigned long vertexCount){ #suite modelTests #test createCubeModel - struct cubeModel model = initCubeModel(3, 3, 3); + cubeModel model = initCubeModel(3, 3, 3); int i = 0; dimensionalFor(3, 3, 3){ i++; - model.cubes[x][y][z] = malloc(sizeof(struct cube)); + model.cubes[x][y][z] = malloc(sizeof(cube)); *(model.cubes[x][y][z]) = createGenericCube(2); ck_assert_msg(model.cubes[x][y][z]->side == 2, "Wrong cube side"); for(int i = 0; i < 6; i++){ @@ -41,11 +41,11 @@ void verifyModelStr(char* modelStr, unsigned long vertexCount){ freeCubeModel(&model); #test cullTest - struct cubeModel model = initCubeModel(3, 3, 3); + cubeModel model = initCubeModel(3, 3, 3); int i = 0; dimensionalFor(3, 3, 3){ i++; - model.cubes[x][y][z] = malloc(sizeof(struct cube)); + model.cubes[x][y][z] = malloc(sizeof(cube)); *(model.cubes[x][y][z]) = createGenericCube(2); } ck_assert_msg(i == 27, "test model creation failed"); @@ -60,8 +60,8 @@ void verifyModelStr(char* modelStr, unsigned long vertexCount){ freeCubeModel(&model); #test deCubeObjectTest - struct cube c = createGenericCube(2); - struct object o = deCubeObject(&c); + cube c = createGenericCube(2); + object o = deCubeObject(&c); ck_assert_msg(o.faceCount == 6, "Less than 6 faces"); for(int i = 0; i < o.faceCount; i++){ for(int n = 0; n < o.faces[i].vertexCount; n++){ @@ -71,11 +71,11 @@ void verifyModelStr(char* modelStr, unsigned long vertexCount){ ck_assert_msg(o.vertexCount == 8, "Less than 8 vertices"); #test cubeModelToModelTest - struct cubeModel cModel = initCubeModel(3, 3, 3); + cubeModel cModel = initCubeModel(3, 3, 3); int i = 0; dimensionalFor(3, 3, 3){ i++; - cModel.cubes[x][y][z] = malloc(sizeof(struct cube)); + cModel.cubes[x][y][z] = malloc(sizeof(cube)); *(cModel.cubes[x][y][z]) = createGenericCube(2); } model preCull = cubeModelToModel(&cModel, NULL); @@ -88,11 +88,11 @@ void verifyModelStr(char* modelStr, unsigned long vertexCount){ ck_assert_msg(preVertex == 216 && postVertex == 152, "Vertex counts aren't valid"); #test vertexCountTest - struct cubeModel cModel = initCubeModel(3, 3, 3); + cubeModel cModel = initCubeModel(3, 3, 3); int i = 0; dimensionalFor(3, 3, 3){ i++; - cModel.cubes[x][y][z] = malloc(sizeof(struct cube)); + cModel.cubes[x][y][z] = malloc(sizeof(cube)); *(cModel.cubes[x][y][z]) = createGenericCube(2); } model nModel = cubeModelToModel(&cModel, NULL); @@ -105,11 +105,11 @@ void verifyModelStr(char* modelStr, unsigned long vertexCount){ freeModel(&nModel); #test generateTest - struct cubeModel cModel = initCubeModel(3, 3, 3); + cubeModel cModel = initCubeModel(3, 3, 3); int i = 0; dimensionalFor(3, 3, 3){ i++; - cModel.cubes[x][y][z] = malloc(sizeof(struct cube)); + cModel.cubes[x][y][z] = malloc(sizeof(cube)); *(cModel.cubes[x][y][z]) = createGenericCube(2); } model nModel = cubeModelToModel(&cModel, NULL); @@ -120,11 +120,11 @@ void verifyModelStr(char* modelStr, unsigned long vertexCount){ verifyModelStr(modelStr, vertexCount); #test simplifyTest - struct cubeModel cModel = initCubeModel(3, 3, 3); + cubeModel cModel = initCubeModel(3, 3, 3); int i = 0; dimensionalFor(3, 3, 3){ i++; - cModel.cubes[x][y][z] = malloc(sizeof(struct cube)); + cModel.cubes[x][y][z] = malloc(sizeof(cube)); *(cModel.cubes[x][y][z]) = createGenericCube(2); cModel.cubes[x][y][z]->x = x; cModel.cubes[x][y][z]->y = y; @@ -133,7 +133,7 @@ void verifyModelStr(char* modelStr, unsigned long vertexCount){ model nModel = cubeModelToModel(&cModel, NULL); freeCubeModel(&cModel); unsigned long oldVertexCount = getTotalVertexCount(nModel); - struct object nObj = modelToObject(&nModel, NULL); + object nObj = modelToObject(&nModel, NULL); freeModel(&nModel); //fprintf(stderr, "%d %ld\n", nObj.vertexCount, oldVertexCount); ck_assert_msg(nObj.vertexCount > 0 && nObj.vertexCount + 152 == oldVertexCount, "Invalid vertexCount"); diff --git a/tests/regionParser.check b/tests/regionParser.check index cf2fe59..bb594de 100644 --- a/tests/regionParser.check +++ b/tests/regionParser.check @@ -33,7 +33,7 @@ ck_assert_msg(!chunkIsNull(hopefullyChunk), "Chunk that shouldn't be NULL actually is"); //We check if any chunk has had it's data extracted short b = 0; - for(int i = 0; i < chunkN; i++){ + for(int i = 0; i < 1024; i++){ if(mockChunks[i].byteLength > 0){ b = !b; } diff --git a/tests/wavefront.check b/tests/wavefront.check new file mode 100644 index 0000000..a22564d --- /dev/null +++ b/tests/wavefront.check @@ -0,0 +1,11 @@ +#include "../src/lib/model.h" +#include +#include + +#suite wavefrontTests + +#test getMaterialsTest + + hashTable* ht = getMaterials("out.mtl"); + ck_assert_msg(ht != NULL, "getMaterials failed"); + ck_assert_msg(ht->count == 895, "getMaterials failed");